openimageio-1.7.17~dfsg0.orig/0000755000175000017500000000000013152525262014331 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/0000755000175000017500000000000013151711064015114 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/idiff/0000755000175000017500000000000013151711064016175 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/idiff/CMakeLists.txt0000644000175000017500000000034313151711064020735 0ustar mfvmfvset (idiff_srcs idiff.cpp) add_executable (idiff ${idiff_srcs}) set_target_properties (idiff PROPERTIES FOLDER "Tools") target_link_libraries (idiff OpenImageIO ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) oiio_install_targets (idiff) openimageio-1.7.17~dfsg0.orig/src/idiff/idiff.cpp0000644000175000017500000003375013151711064017772 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/argparse.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/imagecache.h" #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" OIIO_NAMESPACE_USING enum idiffErrors { ErrOK = 0, ///< No errors, the images match exactly ErrWarn, ///< Warning: the errors differ a little ErrFail, ///< Failure: the errors differ a lot ErrDifferentSize, ///< Images aren't even the same size ErrFile, ///< Could not find or open input files, etc. ErrLast }; static bool verbose = false; static bool quiet = false; static bool outdiffonly = false; static std::string diffimage; static float diffscale = 1.0; static bool diffabs = false; static float warnthresh = 1.0e-6f; static float warnpercent = 0; static float hardwarn = std::numeric_limits::max(); static float failthresh = 1.0e-6f; static float failpercent = 0; static bool perceptual = false; static float hardfail = std::numeric_limits::max(); static std::vector filenames; //static bool comparemeta = false; static bool compareall = false; static int parse_files (int argc, const char *argv[]) { for (int i = 0; i < argc; i++) filenames.push_back (argv[i]); return 0; } static void getargs (int argc, char *argv[]) { bool help = false; ArgParse ap; ap.options ("idiff -- compare two images\n" OIIO_INTRO_STRING "\n" "Usage: idiff [options] image1 image2", "%*", parse_files, "", "--help", &help, "Print help message", "-v", &verbose, "Verbose status messages", "-q", &quiet, "Quiet (minimal messages)", "-a", &compareall, "Compare all subimages/miplevels", "", "Thresholding and comparison options", "-fail %g", &failthresh, "Failure threshold difference (0.000001)", "-failpercent %g", &failpercent, "Allow this percentage of failures (0)", "-hardfail %g", &hardfail, "Fail if any one pixel exceeds this error (infinity)", "-warn %g", &warnthresh, "Warning threshold difference (0.00001)", "-warnpercent %g", &warnpercent, "Allow this percentage of warnings (0)", "-hardwarn %g", &hardwarn, "Warn if any one pixel exceeds this error (infinity)", "-p", &perceptual, "Perform perceptual (rather than numeric) comparison", "", "Difference image options", "-o %s", &diffimage, "Output difference image", "-od", &outdiffonly, "Output image only if nonzero difference", "-abs", &diffabs, "Output image of absolute value, not signed difference", "-scale %g", &diffscale, "Scale the output image by this factor", // "-meta", &comparemeta, "Compare metadata", NULL); if (ap.parse(argc, (const char**)argv) < 0) { std::cerr << ap.geterror() << std::endl; ap.usage (); exit (EXIT_FAILURE); } if (help) { ap.usage (); exit (EXIT_FAILURE); } if (filenames.size() != 2) { std::cerr << "idiff: Must have two input filenames.\n"; ap.usage(); exit (EXIT_FAILURE); } } static bool read_input (const std::string &filename, ImageBuf &img, ImageCache *cache, int subimage=0, int miplevel=0) { if (img.subimage() >= 0 && img.subimage() == subimage && img.miplevel() == miplevel) return true; img.reset (filename, cache); if (img.read (subimage, miplevel, false, TypeDesc::TypeFloat)) return true; std::cerr << "idiff ERROR: Could not read " << filename << ":\n\t" << img.geterror() << "\n"; return false; } // function that standarize printing NaN and Inf values on // Windows (where they are in 1.#INF, 1.#NAN format) and all // others platform inline void safe_double_print (double val) { if (OIIO::isnan (val)) std::cout << "nan"; else if (OIIO::isinf (val)) std::cout << "inf"; else std::cout << val; std::cout << '\n'; } inline void print_subimage (ImageBuf &img0, int subimage, int miplevel) { if (img0.nsubimages() > 1) std::cout << "Subimage " << subimage << ' '; if (img0.nmiplevels() > 1) std::cout << " MIP level " << miplevel << ' '; if (img0.nsubimages() > 1 || img0.nmiplevels() > 1) std::cout << ": "; std::cout << img0.spec().width << " x " << img0.spec().height; if (img0.spec().depth > 1) std::cout << " x " << img0.spec().depth; std::cout << ", " << img0.spec().nchannels << " channel\n"; } int main (int argc, char *argv[]) { Filesystem::convert_native_arguments (argc, (const char **)argv); getargs (argc, argv); if (! quiet) std::cout << "Comparing \"" << filenames[0] << "\" and \"" << filenames[1] << "\"\n"; // Create a private ImageCache so we can customize its cache size // and instruct it store everything internally as floats. ImageCache *imagecache = ImageCache::create (true); imagecache->attribute ("forcefloat", 1); if (sizeof(void *) == 4) // 32 bit or 64? imagecache->attribute ("max_memory_MB", 512.0); else imagecache->attribute ("max_memory_MB", 2048.0); imagecache->attribute ("autotile", 256); // force a full diff, even for files tagged with the same // fingerprint, just in case some mistake has been made. imagecache->attribute ("deduplicate", 0); ImageBuf img0, img1; if (! read_input (filenames[0], img0, imagecache) || ! read_input (filenames[1], img1, imagecache)) return ErrFile; // ImageSpec spec0 = img0.spec(); // stash it int ret = ErrOK; for (int subimage = 0; subimage < img0.nsubimages(); ++subimage) { if (subimage > 0 && !compareall) break; if (subimage >= img1.nsubimages()) break; if (! read_input (filenames[0], img0, imagecache, subimage) || ! read_input (filenames[1], img1, imagecache, subimage)) { std::cerr << "Failed to read subimage " << subimage << "\n"; return ErrFile; } if (img0.nmiplevels() != img1.nmiplevels()) { if (! quiet) std::cout << "Files do not match in their number of MIPmap levels\n"; } for (int m = 0; m < img0.nmiplevels(); ++m) { if (m > 0 && !compareall) break; if (m > 0 && img0.nmiplevels() != img1.nmiplevels()) { std::cerr << "Files do not match in their number of MIPmap levels\n"; ret = ErrDifferentSize; break; } if (! read_input (filenames[0], img0, imagecache, subimage, m) || ! read_input (filenames[1], img1, imagecache, subimage, m)) return ErrFile; if (img0.deep() != img1.deep()) { std::cerr << "One image contains deep data, the other does not\n"; ret = ErrDifferentSize; break; } int npels = img0.spec().width * img0.spec().height * img0.spec().depth; if (npels == 0) npels = 1; // Avoid divide by zero for 0x0 images ASSERT (img0.spec().format == TypeDesc::FLOAT); // Compare the two images. // ImageBufAlgo::CompareResults cr; ImageBufAlgo::compare (img0, img1, failthresh, warnthresh, cr); int yee_failures = 0; if (perceptual && ! img0.deep()) { ImageBufAlgo::CompareResults cr; yee_failures = ImageBufAlgo::compare_Yee (img0, img1, cr); } if (cr.nfail > (failpercent/100.0 * npels) || cr.maxerror > hardfail || yee_failures > (failpercent/100.0 * npels)) { ret = ErrFail; } else if (cr.nwarn > (warnpercent/100.0 * npels) || cr.maxerror > hardwarn) { if (ret != ErrFail) ret = ErrWarn; } // Print the report // if (verbose || (ret != ErrOK && !quiet)) { if (compareall) print_subimage (img0, subimage, m); std::cout << " Mean error = "; safe_double_print (cr.meanerror); std::cout << " RMS error = "; safe_double_print (cr.rms_error); std::cout << " Peak SNR = "; safe_double_print (cr.PSNR); std::cout << " Max error = " << cr.maxerror; if (cr.maxerror != 0) { std::cout << " @ (" << cr.maxx << ", " << cr.maxy; if (img0.spec().depth > 1) std::cout << ", " << cr.maxz; if (cr.maxc < (int)img0.spec().channelnames.size()) std::cout << ", " << img0.spec().channelnames[cr.maxc] << ')'; else if (cr.maxc < (int)img1.spec().channelnames.size()) std::cout << ", " << img1.spec().channelnames[cr.maxc] << ')'; else std::cout << ", channel " << cr.maxc << ')'; } std::cout << "\n"; #if OIIO_MSVS_BEFORE_2015 // When older Visual Studio is used, float values in // scientific foramt are printed with three digit exponent. // We change this behaviour to fit Linux way. _set_output_format(_TWO_DIGIT_EXPONENT); #endif std::streamsize precis = std::cout.precision(); std::cout << " " << cr.nwarn << " pixels (" << std::setprecision(3) << (100.0*cr.nwarn / npels) << std::setprecision(precis) << "%) over " << warnthresh << "\n"; std::cout << " " << cr.nfail << " pixels (" << std::setprecision(3) << (100.0*cr.nfail / npels) << std::setprecision(precis) << "%) over " << failthresh << "\n"; if (perceptual) std::cout << " " << yee_failures << " pixels (" << std::setprecision(3) << (100.0*yee_failures / npels) << std::setprecision(precis) << "%) failed the perceptual test\n"; } // If the user requested that a difference image be output, // do that. N.B. we only do this for the first subimage // right now, because ImageBuf doesn't really know how to // write subimages. if (diffimage.size() && (cr.maxerror != 0 || !outdiffonly)) { ImageBuf diff; if (diffabs) ImageBufAlgo::absdiff (diff, img0, img1); else ImageBufAlgo::sub (diff, img0, img1); if (diffscale != 1.0f) ImageBufAlgo::mul (diff, diff, diffscale); diff.write (diffimage); // Clear diff image name so we only save the first // non-matching subimage. diffimage = ""; } } } if (compareall && img0.nsubimages() != img1.nsubimages()) { if (! quiet) std::cerr << "Images had differing numbers of subimages (" << img0.nsubimages() << " vs " << img1.nsubimages() << ")\n"; ret = ErrFail; } if (!compareall && (img0.nsubimages() > 1 || img1.nsubimages() > 1)) { if (! quiet) std::cout << "Only compared the first subimage (of " << img0.nsubimages() << " and " << img1.nsubimages() << ", respectively)\n"; } if (ret == ErrOK) { if (! quiet) std::cout << "PASS\n"; } else if (ret == ErrWarn) { if (! quiet) std::cout << "WARNING\n"; } else if (ret) { if (quiet) std::cerr << "FAILURE\n"; else std::cout << "FAILURE\n"; } imagecache->invalidate_all (true); ImageCache::destroy (imagecache); return ret; } openimageio-1.7.17~dfsg0.orig/src/iconvert/0000755000175000017500000000000013151711064016745 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/iconvert/iconvert.cpp0000644000175000017500000004706713151711064021320 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include #include "OpenImageIO/argparse.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/sysutil.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/imagecache.h" OIIO_NAMESPACE_USING; static std::string uninitialized = "uninitialized \001 HHRU dfvAS: efjl"; static std::string dataformatname = ""; static float gammaval = 1.0f; //static bool depth = false; static bool verbose = false; static int nthreads = 0; // default: use #cores threads if available static std::vector filenames; static int tile[3] = { 0, 0, 1 }; static bool scanline = false; //static bool zfile = false; //static std::string channellist; static std::string compression; static bool no_copy_image = false; static int quality = -1; static bool adjust_time = false; static std::string caption = uninitialized; static std::vector keywords; static bool clear_keywords = false; static std::vector attribnames, attribvals; static bool inplace = false; static int orientation = 0; static bool rotcw = false, rotccw = false, rot180 = false; static bool sRGB = false; static bool separate = false, contig = false; static bool noclobber = false; static int parse_files (int argc, const char *argv[]) { for (int i = 0; i < argc; i++) filenames.push_back (argv[i]); return 0; } static void getargs (int argc, char *argv[]) { bool help = false; ArgParse ap; ap.options ("iconvert -- copy images with format conversions and other alterations\n" OIIO_INTRO_STRING "\n" "Usage: iconvert [options] inputfile outputfile\n" " or: iconvert --inplace [options] file...\n", "%*", parse_files, "", "--help", &help, "Print help message", "-v", &verbose, "Verbose status messages", "--threads %d", &nthreads, "Number of threads (default 0 = #cores)", "-d %s", &dataformatname, "Set the output data format to one of:" "uint8, sint8, uint10, uint12, uint16, sint16, half, float, double", "-g %f", &gammaval, "Set gamma correction (default = 1)", "--tile %d %d", &tile[0], &tile[1], "Output as a tiled image", "--scanline", &scanline, "Output as a scanline image", "--compression %s", &compression, "Set the compression method (default = same as input)", "--quality %d", &quality, "Set the compression quality, 1-100", "--no-copy-image", &no_copy_image, "Do not use ImageOutput copy_image functionality (dbg)", "--adjust-time", &adjust_time, "Adjust file times to match DateTime metadata", "--caption %s", &caption, "Set caption (ImageDescription)", "--keyword %L", &keywords, "Add a keyword", "--clear-keywords", &clear_keywords, "Clear keywords", "--attrib %L %L", &attribnames, &attribvals, "Set a string attribute (name, value)", "--orientation %d", &orientation, "Set the orientation", "--rotcw", &rotcw, "Rotate 90 deg clockwise", "--rotccw", &rotccw, "Rotate 90 deg counter-clockwise", "--rot180", &rot180, "Rotate 180 deg", "--inplace", &inplace, "Do operations in place on images", "--sRGB", &sRGB, "This file is in sRGB color space", "--separate", &separate, "Force planarconfig separate", "--contig", &contig, "Force planarconfig contig", "--no-clobber", &noclobber, "Do no overwrite existing files", //FIXME "-z", &zfile, "Treat input as a depth file", //FIXME "-c %s", &channellist, "Restrict/shuffle channels", NULL); if (ap.parse(argc, (const char**)argv) < 0) { std::cerr << ap.geterror() << std::endl; ap.usage (); exit (EXIT_FAILURE); } if (help) { ap.usage (); exit (EXIT_FAILURE); } if (filenames.size() != 2 && ! inplace) { std::cerr << "iconvert: Must have both an input and output filename specified.\n"; ap.usage(); exit (EXIT_FAILURE); } if (filenames.size() == 0 && inplace) { std::cerr << "iconvert: Must have at least one filename\n"; ap.usage(); exit (EXIT_FAILURE); } if (((int)rotcw + (int)rotccw + (int)rot180 + (orientation>0)) > 1) { std::cerr << "iconvert: more than one of --rotcw, --rotccw, --rot180, --orientation\n"; ap.usage(); exit (EXIT_FAILURE); } } static bool DateTime_to_time_t (const char *datetime, time_t &timet) { int year, month, day, hour, min, sec; int r = sscanf (datetime, "%d:%d:%d %d:%d:%d", &year, &month, &day, &hour, &min, &sec); // printf ("%d %d:%d:%d %d:%d:%d\n", r, year, month, day, hour, min, sec); if (r != 6) return false; struct tm tmtime; time_t now; Sysutil::get_local_time (&now, &tmtime); // fill in defaults tmtime.tm_sec = sec; tmtime.tm_min = min; tmtime.tm_hour = hour; tmtime.tm_mday = day; tmtime.tm_mon = month-1; tmtime.tm_year = year-1900; timet = mktime (&tmtime); return true; } // Adjust the output spec based on the command-line arguments. // Return whether the specifics preclude using copy_image. static bool adjust_spec (ImageInput *in, ImageOutput *out, const ImageSpec &inspec, ImageSpec &outspec) { bool nocopy = no_copy_image; // Copy the spec, with possible change in format outspec.format = inspec.format; if (inspec.channelformats.size()) { // Input file has mixed channels if (out->supports("channelformats")) { // Output supports mixed formats -- so request it outspec.format = TypeDesc::UNKNOWN; } else { // Input had mixed formats, output did not, so just use a fixed // format and forget the per-channel formats for output. outspec.channelformats.clear (); } } if (! dataformatname.empty()) { // make sure there isn't a stray BPS that will screw us up outspec.erase_attribute ("oiio:BitsPerSample"); if (dataformatname == "uint8") outspec.set_format (TypeDesc::UINT8); else if (dataformatname == "int8") outspec.set_format (TypeDesc::INT8); else if (dataformatname == "uint10") { outspec.attribute ("oiio:BitsPerSample", 10); outspec.set_format (TypeDesc::UINT16); } else if (dataformatname == "uint12") { outspec.attribute ("oiio:BitsPerSample", 12); outspec.set_format (TypeDesc::UINT16); } else if (dataformatname == "uint16") outspec.set_format (TypeDesc::UINT16); else if (dataformatname == "int16") outspec.set_format (TypeDesc::INT16); else if (dataformatname == "uint32" || dataformatname == "uint") outspec.set_format (TypeDesc::UINT32); else if (dataformatname == "int32" || dataformatname == "int") outspec.set_format (TypeDesc::INT32); else if (dataformatname == "half") outspec.set_format (TypeDesc::HALF); else if (dataformatname == "float") outspec.set_format (TypeDesc::FLOAT); else if (dataformatname == "double") outspec.set_format (TypeDesc::DOUBLE); outspec.channelformats.clear (); } if (outspec.format != inspec.format || inspec.channelformats.size()) nocopy = true; outspec.attribute ("oiio:Gamma", gammaval); if (sRGB) { outspec.attribute ("oiio:ColorSpace", "sRGB"); if (!strcmp (in->format_name(), "jpeg") || outspec.find_attribute ("Exif:ColorSpace")) outspec.attribute ("Exif:ColorSpace", 1); } if (tile[0]) { outspec.tile_width = tile[0]; outspec.tile_height = tile[1]; outspec.tile_depth = tile[2]; } if (scanline) { outspec.tile_width = 0; outspec.tile_height = 0; outspec.tile_depth = 0; } if (outspec.tile_width != inspec.tile_width || outspec.tile_height != inspec.tile_height || outspec.tile_depth != inspec.tile_depth) nocopy = true; if (! compression.empty()) { outspec.attribute ("compression", compression); if (compression != inspec.get_string_attribute ("compression")) nocopy = true; } if (quality > 0) { outspec.attribute ("CompressionQuality", quality); if (quality != inspec.get_int_attribute ("CompressionQuality")) nocopy = true; } if (contig) outspec.attribute ("planarconfig", "contig"); if (separate) outspec.attribute ("planarconfig", "separate"); if (orientation >= 1) outspec.attribute ("Orientation", orientation); else { orientation = outspec.get_int_attribute ("Orientation", 1); if (orientation >= 1 && orientation <= 8) { static int cw[] = { 0, 6, 7, 8, 5, 2, 3, 4, 1 }; if (rotcw || rotccw || rot180) orientation = cw[orientation]; if (rotccw || rot180) orientation = cw[orientation]; if (rotccw) orientation = cw[orientation]; outspec.attribute ("Orientation", orientation); } } if (caption != uninitialized) outspec.attribute ("ImageDescription", caption); if (clear_keywords) outspec.attribute ("Keywords", ""); if (keywords.size()) { std::string oldkw = outspec.get_string_attribute ("Keywords"); std::vector oldkwlist; if (! oldkw.empty()) { Strutil::split (oldkw, oldkwlist, ";"); for (size_t i = 0; i < oldkwlist.size(); ++i) oldkwlist[i] = Strutil::strip (oldkwlist[i]); } BOOST_FOREACH (const std::string &nk, keywords) { bool dup = false; BOOST_FOREACH (const std::string &ok, oldkwlist) dup |= (ok == nk); if (! dup) oldkwlist.push_back (nk); } outspec.attribute ("Keywords", Strutil::join (oldkwlist, "; ")); } for (size_t i = 0; i < attribnames.size(); ++i) { outspec.attribute (attribnames[i].c_str(), attribvals[i].c_str()); } return nocopy; } static bool convert_file (const std::string &in_filename, const std::string &out_filename) { if (noclobber && Filesystem::exists(out_filename)) { std::cerr << "iconvert ERROR: Output file already exists \"" << out_filename << "\"\n"; return false; } if (verbose) std::cout << "Converting " << in_filename << " to " << out_filename << "\n"; std::string tempname = out_filename; if (tempname == in_filename) { tempname = out_filename + ".tmp" + Filesystem::extension (out_filename); } // Find an ImageIO plugin that can open the input file, and open it. ImageInput *in = ImageInput::open (in_filename.c_str()); if (! in) { std::string err = geterror(); std::cerr << "iconvert ERROR: " << (err.length() ? err : Strutil::format("Could not open \"%s\"", in_filename)) << "\n"; delete in; return false; } ImageSpec inspec = in->spec(); std::string metadatatime = inspec.get_string_attribute ("DateTime"); // Find an ImageIO plugin that can open the output file, and open it ImageOutput *out = ImageOutput::create (tempname.c_str()); if (! out) { std::cerr << "iconvert ERROR: Could not find an ImageIO plugin to write \"" << out_filename << "\" :" << geterror() << "\n"; delete in; return false; } // In order to deal with formats that support subimages, but not // subimage appending, we gather them all first. std::vector subimagespecs; if (out->supports("multiimage") && !out->supports("appendsubimage")) { ImageCache *imagecache = ImageCache::create (); int nsubimages = 0; ustring ufilename (in_filename); imagecache->get_image_info (ufilename, 0, 0, ustring("subimages"), TypeDesc::TypeInt, &nsubimages); if (nsubimages > 1) { subimagespecs.resize (nsubimages); for (int i = 0; i < nsubimages; ++i) { ImageSpec inspec = *imagecache->imagespec (ufilename, i, 0, true /*native*/); subimagespecs[i] = inspec; adjust_spec (in, out, inspec, subimagespecs[i]); } } ImageCache::destroy (imagecache); } bool ok = true; bool mip_to_subimage_warning = false; for (int subimage = 0; ok && in->seek_subimage(subimage,0,inspec); ++subimage) { if (subimage > 0 && !out->supports ("multiimage")) { std::cerr << "iconvert WARNING: " << out->format_name() << " does not support multiple subimages.\n"; std::cerr << "\tOnly the first subimage has been copied.\n"; break; // we're done } int miplevel = 0; do { // Copy the spec, with possible change in format ImageSpec outspec = inspec; bool nocopy = adjust_spec (in, out, inspec, outspec); if (miplevel > 0) { // Moving to next MIP level ImageOutput::OpenMode mode; if (out->supports ("mipmap")) mode = ImageOutput::AppendMIPLevel; else if (out->supports ("multiimage") && out->supports ("appendsubimage")) { mode = ImageOutput::AppendSubimage; // use if we must if (! mip_to_subimage_warning && strcmp(out->format_name(),"tiff")) { std::cerr << "iconvert WARNING: " << out->format_name() << " does not support MIPmaps.\n"; std::cerr << "\tStoring the MIPmap levels in subimages.\n"; } mip_to_subimage_warning = true; } else { std::cerr << "iconvert WARNING: " << out->format_name() << " does not support MIPmaps.\n"; std::cerr << "\tOnly the first level has been copied.\n"; break; // on to the next subimage } ok = out->open (tempname.c_str(), outspec, mode); } else if (subimage > 0) { // Moving to next subimage ok = out->open (tempname.c_str(), outspec, ImageOutput::AppendSubimage); } else { // First time opening if (subimagespecs.size()) ok = out->open (tempname.c_str(), int(subimagespecs.size()), &subimagespecs[0]); else ok = out->open (tempname.c_str(), outspec, ImageOutput::Create); } if (! ok) { std::string err = out->geterror(); std::cerr << "iconvert ERROR: " << (err.length() ? err : Strutil::format("Could not open \"%s\"", out_filename)) << "\n"; ok = false; break; } if (! nocopy) { ok = out->copy_image (in); if (! ok) std::cerr << "iconvert ERROR copying \"" << in_filename << "\" to \"" << out_filename << "\" :\n\t" << out->geterror() << "\n"; } else { // Need to do it by hand for some reason. Future expansion in which // only a subset of channels are copied, or some such. std::vector pixels ((size_t)outspec.image_bytes(true)); ok = in->read_image (outspec.format, &pixels[0]); if (! ok) { std::cerr << "iconvert ERROR reading \"" << in_filename << "\" : " << in->geterror() << "\n"; } else { ok = out->write_image (outspec.format, &pixels[0]); if (! ok) std::cerr << "iconvert ERROR writing \"" << out_filename << "\" : " << out->geterror() << "\n"; } } ++miplevel; } while (ok && in->seek_subimage(subimage,miplevel,inspec)); } out->close (); delete out; in->close (); delete in; // Figure out a time for the input file -- either one supplied by // the metadata, or the actual time stamp of the input file. std::time_t in_time; if (metadatatime.empty() || ! DateTime_to_time_t (metadatatime.c_str(), in_time)) in_time = Filesystem::last_write_time (in_filename); if (out_filename != tempname) { if (ok) { Filesystem::remove (out_filename); Filesystem::rename (tempname, out_filename); } else Filesystem::remove (tempname); } // If user requested, try to adjust the file's modification time to // the creation time indicated by the file's DateTime metadata. if (ok && adjust_time) Filesystem::last_write_time (out_filename, in_time); return ok; } int main (int argc, char *argv[]) { Filesystem::convert_native_arguments (argc, (const char **)argv); getargs (argc, argv); OIIO::attribute ("threads", nthreads); bool ok = true; if (inplace) { BOOST_FOREACH (const std::string &s, filenames) ok &= convert_file (s, s); } else { ok = convert_file (filenames[0], filenames[1]); } return ok ? EXIT_SUCCESS : EXIT_FAILURE; } openimageio-1.7.17~dfsg0.orig/src/iconvert/CMakeLists.txt0000644000175000017500000000037013151711064021505 0ustar mfvmfvset (iconvert_srcs iconvert.cpp) add_executable (iconvert ${iconvert_srcs}) set_target_properties (iconvert PROPERTIES FOLDER "Tools") target_link_libraries (iconvert OpenImageIO ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) oiio_install_targets (iconvert) openimageio-1.7.17~dfsg0.orig/src/maketx/0000755000175000017500000000000013151711064016405 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/maketx/maketx.cpp0000644000175000017500000004702413151711064020411 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include #include "OpenImageIO/argparse.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/timer.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/filter.h" OIIO_NAMESPACE_USING // # FIXME: Refactor all statics into a struct // Basic runtime options static std::string full_command_line; static std::vector filenames; static std::string outputfilename; static bool verbose = false; static bool runstats = false; static int nthreads = 0; // default: use #cores threads if available // Conversion modes. If none are true, we just make an ordinary texture. static bool mipmapmode = false; static bool shadowmode = false; static bool envlatlmode = false; static bool envcubemode = false; static bool lightprobemode = false; static std::string filter_help_string () { std::string s ("Select filter for resizing (choices:"); for (int i = 0, e = Filter2D::num_filters(); i < e; ++i) { FilterDesc d; Filter2D::get_filterdesc (i, &d); s.append (" "); s.append (d.name); } s.append (", default=box)"); return s; } static std::string colortitle_help_string () { std::string s ("Color Management Options "); if(ColorConfig::supportsOpenColorIO()) { s += "(OpenColorIO enabled)"; } else { s += "(OpenColorIO DISABLED)"; } return s; } static std::string colorconvert_help_string () { std::string s = "Apply a color space conversion to the image. " "If the output color space is not the same bit depth " "as input color space, it is your responsibility to set the data format " "to the proper bit depth using the -d option. "; s += " (choices: "; ColorConfig colorconfig; if (colorconfig.error() || colorconfig.getNumColorSpaces()==0) { s += "NONE"; } else { for (int i=0; i < colorconfig.getNumColorSpaces(); ++i) { if (i!=0) s += ", "; s += colorconfig.getColorSpaceNameByIndex(i); } } s += ")"; return s; } static int parse_files (int argc, const char *argv[]) { for (int i = 0; i < argc; i++) filenames.push_back (argv[i]); return 0; } // Concatenate the command line into one string, optionally filtering out // verbose attribute commands. static std::string command_line_string (int argc, char * argv[], bool sansattrib) { std::string s; for (int i = 0; i < argc; ++i) { if (sansattrib) { // skip any filtered attributes if (!strcmp(argv[i], "--attrib") || !strcmp(argv[i], "-attrib") || !strcmp(argv[i], "--sattrib") || !strcmp(argv[i], "-sattrib")) { i += 2; // also skip the following arguments continue; } if (!strcmp(argv[i], "--sansattrib") || !strcmp(argv[i], "-sansattrib")) { continue; } } if (strchr (argv[i], ' ')) { // double quote args with spaces s += '\"'; s += argv[i]; s += '\"'; } else { s += argv[i]; } if (i < argc-1) s += ' '; } return s; } static void getargs (int argc, char *argv[], ImageSpec &configspec) { bool help = false; // Basic runtime options std::string dataformatname = ""; std::string fileformatname = ""; std::vector mipimages; int tile[3] = { 64, 64, 1 }; // FIXME if we ever support volume MIPmaps std::string compression = "zip"; bool updatemode = false; bool checknan = false; std::string fixnan; // none, black, box3 bool set_full_to_pixels = false; bool do_highlight_compensation = false; std::string filtername; // Options controlling file metadata or mipmap creation float fovcot = 0.0f; std::string wrap = "black"; std::string swrap; std::string twrap; bool doresize = false; Imath::M44f Mcam(0.0f), Mscr(0.0f); // Initialize to 0 bool separate = false; bool nomipmap = false; bool prman_metadata = false; bool constant_color_detect = false; bool monochrome_detect = false; bool opaque_detect = false; bool compute_average = true; int nchannels = -1; bool prman = false; bool oiio = false; bool ignore_unassoc = false; // ignore unassociated alpha tags bool unpremult = false; bool sansattrib = false; float sharpen = 0.0f; std::string incolorspace; std::string outcolorspace; std::string channelnames; std::vector string_attrib_names, string_attrib_values; std::vector any_attrib_names, any_attrib_values; filenames.clear(); ArgParse ap; ap.options ("maketx -- convert images to tiled, MIP-mapped textures\n" OIIO_INTRO_STRING "\n" "Usage: maketx [options] file...", "%*", parse_files, "", "--help", &help, "Print help message", "-v", &verbose, "Verbose status messages", "-o %s", &outputfilename, "Output filename", "--threads %d", &nthreads, "Number of threads (default: #cores)", "-u", &updatemode, "Update mode", "--format %s", &fileformatname, "Specify output file format (default: guess from extension)", "--nchannels %d", &nchannels, "Specify the number of output image channels.", "--chnames %s", &channelnames, "Rename channels (comma-separated)", "-d %s", &dataformatname, "Set the output data format to one of: " "uint8, sint8, uint16, sint16, half, float", "--tile %d %d", &tile[0], &tile[1], "Specify tile size", "--separate", &separate, "Use planarconfig separate (default: contiguous)", "--compression %s", &compression, "Set the compression method (default = zip, if possible)", "--fovcot %f", &fovcot, "Override the frame aspect ratio. Default is width/height.", "--wrap %s", &wrap, "Specify wrap mode (black, clamp, periodic, mirror)", "--swrap %s", &swrap, "Specific s wrap mode separately", "--twrap %s", &twrap, "Specific t wrap mode separately", "--resize", &doresize, "Resize textures to power of 2 (default: no)", "--noresize %!", &doresize, "Do not resize textures to power of 2 (deprecated)", "--filter %s", &filtername, filter_help_string().c_str(), "--hicomp", &do_highlight_compensation, "Compress HDR range before resize, expand after.", "--sharpen %f", &sharpen, "Sharpen MIP levels (default = 0.0 = no)", "--nomipmap", &nomipmap, "Do not make multiple MIP-map levels", "--checknan", &checknan, "Check for NaN/Inf values (abort if found)", "--fixnan %s", &fixnan, "Attempt to fix NaN/Inf values in the image (options: none, black, box3)", "--fullpixels", &set_full_to_pixels, "Set the 'full' image range to be the pixel data window", "--Mcamera %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f", &Mcam[0][0], &Mcam[0][1], &Mcam[0][2], &Mcam[0][3], &Mcam[1][0], &Mcam[1][1], &Mcam[1][2], &Mcam[1][3], &Mcam[2][0], &Mcam[2][1], &Mcam[2][2], &Mcam[2][3], &Mcam[3][0], &Mcam[3][1], &Mcam[3][2], &Mcam[3][3], "Set the camera matrix", "--Mscreen %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f", &Mscr[0][0], &Mscr[0][1], &Mscr[0][2], &Mscr[0][3], &Mscr[1][0], &Mscr[1][1], &Mscr[1][2], &Mscr[1][3], &Mscr[2][0], &Mscr[2][1], &Mscr[2][2], &Mscr[2][3], &Mscr[3][0], &Mscr[3][1], &Mscr[3][2], &Mscr[3][3], "Set the screen matrix", "--prman-metadata", &prman_metadata, "Add prman specific metadata", "--attrib %L %L", &any_attrib_names, &any_attrib_values, "Sets metadata attribute (name, value)", "--sattrib %L %L", &string_attrib_names, &string_attrib_values, "Sets string metadata attribute (name, value)", "--sansattrib", &sansattrib, "Write command line into Software & ImageHistory but remove --sattrib and --attrib options", "--constant-color-detect", &constant_color_detect, "Create 1-tile textures from constant color inputs", "--monochrome-detect", &monochrome_detect, "Create 1-channel textures from monochrome inputs", "--opaque-detect", &opaque_detect, "Drop alpha channel that is always 1.0", "--no-compute-average %!", &compute_average, "Don't compute and store average color", "--ignore-unassoc", &ignore_unassoc, "Ignore unassociated alpha tags in input (don't autoconvert)", "--runstats", &runstats, "Print runtime statistics", "--stats", &runstats, "", // DEPRECATED 1.6 "--mipimage %L", &mipimages, "Specify an individual MIP level", "", "Basic modes (default is plain texture):", "--shadow", &shadowmode, "Create shadow map", "--envlatl", &envlatlmode, "Create lat/long environment map", "--lightprobe", &lightprobemode, "Create lat/long environment map from a light probe", // "--envcube", &envcubemode, "Create cubic env map (file order: px, nx, py, ny, pz, nz) (UNIMP)", "", colortitle_help_string().c_str(), "--colorconvert %s %s", &incolorspace, &outcolorspace, colorconvert_help_string().c_str(), "--unpremult", &unpremult, "Unpremultiply before color conversion, then premultiply " "after the color conversion. You'll probably want to use this flag " "if your image contains an alpha channel.", "", "Configuration Presets", "--prman", &prman, "Use PRMan-safe settings for tile size, planarconfig, and metadata.", "--oiio", &oiio, "Use OIIO-optimized settings for tile size, planarconfig, metadata.", NULL); if (ap.parse (argc, (const char**)argv) < 0) { std::cerr << ap.geterror() << std::endl; ap.usage (); exit (EXIT_FAILURE); } if (help) { ap.usage (); exit (EXIT_FAILURE); } if (filenames.empty()) { ap.briefusage (); std::cout << "\nFor detailed help: maketx --help\n"; exit (EXIT_SUCCESS); } int optionsum = ((int)shadowmode + (int)envlatlmode + (int)envcubemode + (int)lightprobemode); if (optionsum > 1) { std::cerr << "maketx ERROR: At most one of the following options may be set:\n" << "\t--shadow --envlatl --envcube --lightprobe\n"; exit (EXIT_FAILURE); } if (optionsum == 0) mipmapmode = true; if (prman && oiio) { std::cerr << "maketx ERROR: '--prman' compatibility, and '--oiio' optimizations are mutually exclusive.\n"; std::cerr << "\tIf you'd like both prman and oiio compatibility, you should choose --prman\n"; std::cerr << "\t(at the expense of oiio-specific optimizations)\n"; exit (EXIT_FAILURE); } if (filenames.size() != 1) { std::cerr << "maketx ERROR: requires exactly one input filename\n"; exit (EXIT_FAILURE); } // std::cout << "Converting " << filenames[0] << " to " << outputfilename << "\n"; // Figure out which data format we want for output if (! dataformatname.empty()) { if (dataformatname == "uint8") configspec.format = TypeDesc::UINT8; else if (dataformatname == "int8" || dataformatname == "sint8") configspec.format = TypeDesc::INT8; else if (dataformatname == "uint16") configspec.format = TypeDesc::UINT16; else if (dataformatname == "int16" || dataformatname == "sint16") configspec.format = TypeDesc::INT16; else if (dataformatname == "half") configspec.format = TypeDesc::HALF; else if (dataformatname == "float") configspec.format = TypeDesc::FLOAT; else if (dataformatname == "double") configspec.format = TypeDesc::DOUBLE; else { std::cerr << "maketx ERROR: unknown data format \"" << dataformatname << "\"\n"; exit (EXIT_FAILURE); } } configspec.tile_width = tile[0]; configspec.tile_height = tile[1]; configspec.tile_depth = tile[2]; configspec.attribute ("compression", compression); if (fovcot != 0.0f) configspec.attribute ("fovcot", fovcot); configspec.attribute ("planarconfig", separate ? "separate" : "contig"); if (Mcam != Imath::M44f(0.0f)) configspec.attribute ("worldtocamera", TypeDesc::TypeMatrix, &Mcam); if (Mscr != Imath::M44f(0.0f)) configspec.attribute ("worldtoscreen", TypeDesc::TypeMatrix, &Mscr); std::string wrapmodes = (swrap.size() ? swrap : wrap) + ',' + (twrap.size() ? twrap : wrap); configspec.attribute ("wrapmodes", wrapmodes); configspec.attribute ("maketx:verbose", verbose); configspec.attribute ("maketx:runstats", runstats); configspec.attribute ("maketx:resize", doresize); configspec.attribute ("maketx:nomipmap", nomipmap); configspec.attribute ("maketx:updatemode", updatemode); configspec.attribute ("maketx:constant_color_detect", constant_color_detect); configspec.attribute ("maketx:monochrome_detect", monochrome_detect); configspec.attribute ("maketx:opaque_detect", opaque_detect); configspec.attribute ("maketx:compute_average", compute_average); configspec.attribute ("maketx:unpremult", unpremult); configspec.attribute ("maketx:incolorspace", incolorspace); configspec.attribute ("maketx:outcolorspace", outcolorspace); configspec.attribute ("maketx:checknan", checknan); configspec.attribute ("maketx:fixnan", fixnan); configspec.attribute ("maketx:set_full_to_pixels", set_full_to_pixels); configspec.attribute ("maketx:highlightcomp", (int)do_highlight_compensation); configspec.attribute ("maketx:sharpen", sharpen); if (filtername.size()) configspec.attribute ("maketx:filtername", filtername); configspec.attribute ("maketx:nchannels", nchannels); configspec.attribute ("maketx:channelnames", channelnames); if (fileformatname.size()) configspec.attribute ("maketx:fileformatname", fileformatname); configspec.attribute ("maketx:prman_metadata", prman_metadata); configspec.attribute ("maketx:oiio_options", oiio); configspec.attribute ("maketx:prman_options", prman); if (mipimages.size()) configspec.attribute ("maketx:mipimages", Strutil::join(mipimages,";")); std::string cmdline = Strutil::format ("OpenImageIO %s : %s", OIIO_VERSION_STRING, command_line_string (argc, argv, sansattrib)); configspec.attribute ("Software", cmdline); configspec.attribute ("maketx:full_command_line", cmdline); // Add user-specified string attributes for (size_t i = 0; i < string_attrib_names.size(); ++i) { configspec.attribute (string_attrib_names[i], string_attrib_values[i]); } // Add user-specified "any" attributes -- try to deduce the type for (size_t i = 0; i < any_attrib_names.size(); ++i) { string_view s = any_attrib_values[i]; // Does it parse as an int (and nothing more?) int ival; if (Strutil::parse_int(s,ival)) { Strutil::skip_whitespace(s); if (! s.size()) { configspec.attribute (any_attrib_names[i], ival); continue; } } s = any_attrib_values[i]; // Does it parse as a float (and nothing more?) float fval; if (Strutil::parse_float(s,fval)) { Strutil::skip_whitespace(s); if (! s.size()) { configspec.attribute (any_attrib_names[i], fval); continue; } } // OK, treat it like a string configspec.attribute (any_attrib_names[i], any_attrib_values[i]); } if (ignore_unassoc) { configspec.attribute ("maketx:ignore_unassoc", (int)ignore_unassoc); ImageCache *ic = ImageCache::create (); // get the shared one ic->attribute ("unassociatedalpha", (int)ignore_unassoc); } } int main (int argc, char *argv[]) { Timer alltimer; ImageSpec configspec; Filesystem::convert_native_arguments (argc, (const char **)argv); getargs (argc, argv, configspec); OIIO::attribute ("threads", nthreads); // N.B. This will apply to the default IC that any ImageBuf's get. ImageCache *ic = ImageCache::create (); // get the shared one ic->attribute ("forcefloat", 1); // Force float upon read ic->attribute ("max_memory_MB", 1024.0); // 1 GB cache ImageBufAlgo::MakeTextureMode mode = ImageBufAlgo::MakeTxTexture; if (shadowmode) mode = ImageBufAlgo::MakeTxShadow; if (envlatlmode) mode = ImageBufAlgo::MakeTxEnvLatl; if (lightprobemode) mode = ImageBufAlgo::MakeTxEnvLatlFromLightProbe; bool ok = ImageBufAlgo::make_texture (mode, filenames[0], outputfilename, configspec, &std::cout); if (runstats) std::cout << "\n" << ic->getstats(); return ok ? 0 : EXIT_FAILURE; } openimageio-1.7.17~dfsg0.orig/src/maketx/CMakeLists.txt0000644000175000017500000000035213151711064021145 0ustar mfvmfvset (maketx_srcs maketx.cpp) add_executable (maketx ${maketx_srcs}) set_target_properties (maketx PROPERTIES FOLDER "Tools") target_link_libraries (maketx OpenImageIO ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) oiio_install_targets (maketx) openimageio-1.7.17~dfsg0.orig/src/dds.imageio/0000755000175000017500000000000013151711064017277 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/dds.imageio/CMakeLists.txt0000644000175000017500000000043213151711064022036 0ustar mfvmfvadd_oiio_plugin (ddsinput.cpp ddsoutput.cpp squish/alpha.cpp squish/clusterfit.cpp squish/colourblock.cpp squish/colourfit.cpp squish/colourset.cpp squish/maths.cpp squish/rangefit.cpp squish/singlecolourfit.cpp squish/squish.cpp) openimageio-1.7.17~dfsg0.orig/src/dds.imageio/ddsinput.cpp0000644000175000017500000005567613151711064021660 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include "dds_pvt.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/fmath.h" #include "squish/squish.h" OIIO_PLUGIN_NAMESPACE_BEGIN using namespace DDS_pvt; // uncomment the following define to enable 3x2 cube map layout //#define DDS_3X2_CUBE_MAP_LAYOUT class DDSInput : public ImageInput { public: DDSInput () { init(); } virtual ~DDSInput () { close(); } virtual const char * format_name (void) const { return "dds"; } virtual bool open (const std::string &name, ImageSpec &newspec); virtual bool close (); virtual int current_subimage (void) const { return m_subimage; } virtual int current_miplevel (void) const { return m_miplevel; } virtual bool seek_subimage (int subimage, int miplevel, ImageSpec &newspec); virtual bool read_native_scanline (int y, int z, void *data); virtual bool read_native_tile (int x, int y, int z, void *data); private: std::string m_filename; ///< Stash the filename FILE *m_file; ///< Open image handle std::vector m_buf; ///< Buffer the image pixels int m_subimage; int m_miplevel; int m_nchans; ///< Number of colour channels in image int m_nfaces; ///< Number of cube map sides in image int m_Bpp; ///< Number of bytes per pixel int m_redL, m_redR; ///< Bit shifts to extract red channel int m_greenL, m_greenR; ///< Bit shifts to extract green channel int m_blueL, m_blueR; ///< Bit shifts to extract blue channel int m_alphaL, m_alphaR; ///< Bit shifts to extract alpha channel dds_header m_dds; ///< DDS header /// Reset everything to initial state /// void init () { m_file = NULL; m_subimage = -1; m_miplevel = -1; m_buf.clear (); } /// Helper function: read the image as scanlines (all but cubemaps). /// bool readimg_scanlines (); /// Helper function: read the image as tiles (cubemaps only). /// bool readimg_tiles (); /// Helper function: calculate bit shifts to properly extract channel data /// inline void calc_shifts (int mask, int& left, int& right); /// Helper function: performs the actual file seeking. /// void internal_seek_subimage (int cubeface, int miplevel, unsigned int& w, unsigned int& h, unsigned int& d); /// Helper function: performs the actual pixel decoding. bool internal_readimg (unsigned char *dst, int w, int h, int d); /// Helper: read, with error detection /// bool fread (void *buf, size_t itemsize, size_t nitems) { size_t n = ::fread (buf, itemsize, nitems, m_file); if (n != nitems) error ("Read error"); return n == nitems; } }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageInput *dds_input_imageio_create () { return new DDSInput; } OIIO_EXPORT int dds_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* dds_imageio_library_version () { return NULL; } OIIO_EXPORT const char * dds_input_extensions[] = { "dds", NULL }; OIIO_PLUGIN_EXPORTS_END bool DDSInput::open (const std::string &name, ImageSpec &newspec) { m_filename = name; m_file = Filesystem::fopen (name, "rb"); if (! m_file) { error ("Could not open file \"%s\"", name.c_str()); return false; } // due to struct packing, we may get a corrupt header if we just load the // struct from file; to adress that, read every member individually // save some typing #define RH(memb) if (! fread (&m_dds.memb, sizeof (m_dds.memb), 1)) \ return false RH(fourCC); RH(size); RH(flags); RH(height); RH(width); RH(pitch); RH(depth); RH(mipmaps); // advance the file pointer by 44 bytes (reserved fields) fseek (m_file, 44, SEEK_CUR); // pixel format struct RH(fmt.size); RH(fmt.flags); RH(fmt.fourCC); RH(fmt.bpp); RH(fmt.rmask); RH(fmt.gmask); RH(fmt.bmask); RH(fmt.amask); // caps RH(caps.flags1); RH(caps.flags2); // advance the file pointer by 8 bytes (reserved fields) fseek (m_file, 8, SEEK_CUR); #undef RH if (bigendian()) { // DDS files are little-endian // only swap values which are not flags or bitmasks swap_endian (&m_dds.size); swap_endian (&m_dds.height); swap_endian (&m_dds.width); swap_endian (&m_dds.pitch); swap_endian (&m_dds.depth); swap_endian (&m_dds.mipmaps); swap_endian (&m_dds.fmt.size); swap_endian (&m_dds.fmt.bpp); } /*std::cerr << "[dds] fourCC: " << ((char *)&m_dds.fourCC)[0] << ((char *)&m_dds.fourCC)[1] << ((char *)&m_dds.fourCC)[2] << ((char *)&m_dds.fourCC)[3] << " (" << m_dds.fourCC << ")\n"; std::cerr << "[dds] size: " << m_dds.size << "\n"; std::cerr << "[dds] flags: " << m_dds.flags << "\n"; std::cerr << "[dds] pitch: " << m_dds.pitch << "\n"; std::cerr << "[dds] width: " << m_dds.width << "\n"; std::cerr << "[dds] height: " << m_dds.height << "\n"; std::cerr << "[dds] depth: " << m_dds.depth << "\n"; std::cerr << "[dds] mipmaps: " << m_dds.mipmaps << "\n"; std::cerr << "[dds] fmt.size: " << m_dds.fmt.size << "\n"; std::cerr << "[dds] fmt.flags: " << m_dds.fmt.flags << "\n"; std::cerr << "[dds] fmt.fourCC: " << ((char *)&m_dds.fmt.fourCC)[0] << ((char *)&m_dds.fmt.fourCC)[1] << ((char *)&m_dds.fmt.fourCC)[2] << ((char *)&m_dds.fmt.fourCC)[3] << " (" << m_dds.fmt.fourCC << ")\n"; std::cerr << "[dds] fmt.bpp: " << m_dds.fmt.bpp << "\n"; std::cerr << "[dds] caps.flags1: " << m_dds.caps.flags1 << "\n"; std::cerr << "[dds] caps.flags2: " << m_dds.caps.flags2 << "\n";*/ // sanity checks - valid 4CC, correct struct sizes and flags which should // be always present, regardless of the image type, size etc., also check // for impossible flag combinations if (m_dds.fourCC != DDS_MAKE4CC('D', 'D', 'S', ' ') || m_dds.size != 124 || m_dds.fmt.size != 32 || !(m_dds.caps.flags1 & DDS_CAPS1_TEXTURE) || !(m_dds.flags & DDS_CAPS) || !(m_dds.flags & DDS_PIXELFORMAT) || (m_dds.caps.flags2 & DDS_CAPS2_VOLUME && !(m_dds.caps.flags1 & DDS_CAPS1_COMPLEX && m_dds.flags & DDS_DEPTH)) || (m_dds.caps.flags2 & DDS_CAPS2_CUBEMAP && !(m_dds.caps.flags1 & DDS_CAPS1_COMPLEX))){ error ("Invalid DDS header, possibly corrupt file"); return false; } // make sure all dimensions are > 0 and that we have at least one channel // (for uncompressed images) if (!(m_dds.flags & DDS_WIDTH) || !m_dds.width || !(m_dds.flags & DDS_HEIGHT) || !m_dds.height || ((m_dds.flags & DDS_DEPTH) && !m_dds.depth) || (!(m_dds.fmt.flags & DDS_PF_FOURCC) && !((m_dds.fmt.flags & DDS_PF_RGB) | (m_dds.fmt.flags & DDS_PF_LUMINANCE) | (m_dds.fmt.flags & DDS_PF_ALPHA)))) { error ("Image with no data"); return false; } // validate the pixel format // TODO: support DXGI and the "wackier" uncompressed formats if (m_dds.fmt.flags & DDS_PF_FOURCC && m_dds.fmt.fourCC != DDS_4CC_DXT1 && m_dds.fmt.fourCC != DDS_4CC_DXT2 && m_dds.fmt.fourCC != DDS_4CC_DXT3 && m_dds.fmt.fourCC != DDS_4CC_DXT4 && m_dds.fmt.fourCC != DDS_4CC_DXT5) { error ("Unsupported compression type"); return false; } // determine the number of channels we have if (m_dds.fmt.flags & DDS_PF_FOURCC) { // squish decompresses everything to RGBA anyway /*if (m_dds.fmt.fourCC == DDS_4CC_DXT1) m_nchans = 3; // no alpha in DXT1 else*/ m_nchans = 4; } else { m_nchans = ((m_dds.fmt.flags & DDS_PF_LUMINANCE) ? 1 : 3) + ((m_dds.fmt.flags & DDS_PF_ALPHA) ? 1 : 0); // also calculate bytes per pixel and the bit shifts m_Bpp = (m_dds.fmt.bpp + 7) >> 3; if (!(m_dds.fmt.flags & DDS_PF_LUMINANCE)) { calc_shifts (m_dds.fmt.rmask, m_redL, m_redR); calc_shifts (m_dds.fmt.gmask, m_greenL, m_greenR); calc_shifts (m_dds.fmt.bmask, m_blueL, m_blueR); calc_shifts (m_dds.fmt.amask, m_alphaL, m_alphaR); } } // fix depth, pitch and mipmaps for later use, if needed if (!(m_dds.fmt.flags & DDS_PF_FOURCC && m_dds.flags & DDS_PITCH)) m_dds.pitch = m_dds.width * m_Bpp; if (!(m_dds.caps.flags2 & DDS_CAPS2_VOLUME)) m_dds.depth = 1; if (!(m_dds.flags & DDS_MIPMAPCOUNT)) m_dds.mipmaps = 1; // count cube map faces if (m_dds.caps.flags2 & DDS_CAPS2_CUBEMAP) { m_nfaces = 0; for (int flag = DDS_CAPS2_CUBEMAP_POSITIVEX; flag <= DDS_CAPS2_CUBEMAP_NEGATIVEZ; flag <<= 1) { if (m_dds.caps.flags2 & flag) m_nfaces++; } } else m_nfaces = 1; seek_subimage(0, 0, m_spec); newspec = spec (); return true; } inline void DDSInput::calc_shifts (int mask, int& left, int& right) { if (mask == 0) { left = right = 0; return; } int i, tmp = mask; for (i = 0; i < 32; i++, tmp >>= 1) { if (tmp & 1) break; } right = i; for (i = 0; i < 8; i++, tmp >>= 1) { if (!(tmp & 1)) break; } left = 8 - i; } // NOTE: This function has no sanity checks! It's a private method and relies // on the input being correct and valid! void DDSInput::internal_seek_subimage (int cubeface, int miplevel, unsigned int& w, unsigned int& h, unsigned int& d) { // early out for cubemaps that don't contain the requested face if (m_dds.caps.flags2 & DDS_CAPS2_CUBEMAP && !(m_dds.caps.flags2 & (DDS_CAPS2_CUBEMAP_POSITIVEX << cubeface))) { w = h = d = 0; return; } // we can easily calculate the offsets because both compressed and // uncompressed images have predictable length // calculate the offset; start with after the header unsigned int ofs = 128; unsigned int len; // this loop is used to iterate over cube map sides, or run once in the // case of ordinary 2D or 3D images for (int j = 0; j <= cubeface; j++) { w = m_dds.width; h = m_dds.height; d = m_dds.depth; // skip subimages preceding the one we're seeking to // if we have no mipmaps, the modulo formula doesn't work and we // don't skip at all, so just add the offset and continue if (m_dds.mipmaps < 2) { if (j > 0) { if (m_dds.fmt.flags & DDS_PF_FOURCC) // only check for DXT1 - all other formats have same block // size len = squish::GetStorageRequirements(w, h, m_dds.fmt.fourCC == DDS_4CC_DXT1 ? squish::kDxt1 : squish::kDxt5); else len = w * h * d * m_Bpp; ofs += len; } continue; } for (int i = 0; i < miplevel; i++) { if (m_dds.fmt.flags & DDS_PF_FOURCC) // only check for DXT1 - all other formats have same block size len = squish::GetStorageRequirements(w, h, m_dds.fmt.fourCC == DDS_4CC_DXT1 ? squish::kDxt1 : squish::kDxt5); else len = w * h * d * m_Bpp; ofs += len; w >>= 1; if (!w) w = 1; h >>= 1; if (!h) h = 1; d >>= 1; if (!d) d = 1; } } // seek to the offset we've found fseek (m_file, ofs, SEEK_SET); } bool DDSInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec) { if (subimage != 0) return false; // early out if (subimage == current_subimage() && miplevel == current_miplevel()) { newspec = m_spec; return true; } // don't seek if the image doesn't contain mipmaps, isn't 3D or a cube map, // and don't seek out of bounds if (miplevel < 0 || (!(m_dds.caps.flags1 & DDS_CAPS1_COMPLEX) && miplevel != 0) || (unsigned int)miplevel >= m_dds.mipmaps) return false; // clear buffer so that readimage is called m_buf.clear(); // for cube maps, the seek will be performed when reading a tile instead unsigned int w = 0, h = 0, d = 0; if (m_dds.caps.flags2 & DDS_CAPS2_CUBEMAP) { // calc sizes separately for cube maps w = m_dds.width; h = m_dds.height; d = m_dds.depth; for (int i = 1; i < miplevel; i++) { w >>= 1; if (w < 1) w = 1; h >>= 1; if (h < 1) h = 1; d >>= 1; if (d < 1) d = 1; } // create imagespec for the 3x2 cube map layout #ifdef DDS_3X2_CUBE_MAP_LAYOUT m_spec = ImageSpec (w * 3, h * 2, m_nchans, TypeDesc::UINT8); #else // 1x6 layout m_spec = ImageSpec (w, h * 6, m_nchans, TypeDesc::UINT8); #endif // DDS_3X2_CUBE_MAP_LAYOUT m_spec.depth = d; m_spec.tile_width = m_spec.full_width = w; m_spec.tile_height = m_spec.full_height = h; m_spec.tile_depth = m_spec.full_depth = d; } else { internal_seek_subimage(0, miplevel, w, h, d); // create imagespec m_spec = ImageSpec (w, h, m_nchans, TypeDesc::UINT8); m_spec.depth = d; } // fill the imagespec if (m_dds.fmt.flags & DDS_PF_FOURCC) { std::string tempstr = ""; tempstr += ((char *)&m_dds.fmt.fourCC)[0]; tempstr += ((char *)&m_dds.fmt.fourCC)[1]; tempstr += ((char *)&m_dds.fmt.fourCC)[2]; tempstr += ((char *)&m_dds.fmt.fourCC)[3]; m_spec.attribute ("compression", tempstr); } m_spec.attribute ("oiio:BitsPerSample", m_dds.fmt.bpp); m_spec.default_channel_names (); // detect texture type if (m_dds.caps.flags2 & DDS_CAPS2_VOLUME) { m_spec.attribute ("texturetype", "Volume Texture"); m_spec.attribute ("textureformat", "Volume Texture"); } else if (m_dds.caps.flags2 & DDS_CAPS2_CUBEMAP) { m_spec.attribute ("texturetype", "Environment"); m_spec.attribute ("textureformat", "CubeFace Environment"); // check available cube map sides std::string sides = ""; if (m_dds.caps.flags2 & DDS_CAPS2_CUBEMAP_POSITIVEX) sides += "+x"; if (m_dds.caps.flags2 & DDS_CAPS2_CUBEMAP_NEGATIVEX) { if (sides.size()) sides += " "; sides += "-x"; } if (m_dds.caps.flags2 & DDS_CAPS2_CUBEMAP_POSITIVEY) { if (sides.size()) sides += " "; sides += "+y"; } if (m_dds.caps.flags2 & DDS_CAPS2_CUBEMAP_NEGATIVEY) { if (sides.size()) sides += " "; sides += "-y"; } if (m_dds.caps.flags2 & DDS_CAPS2_CUBEMAP_POSITIVEZ) { if (sides.size()) sides += " "; sides += "+z"; } if (m_dds.caps.flags2 & DDS_CAPS2_CUBEMAP_NEGATIVEZ) { if (sides.size()) sides += " "; sides += "-z"; } m_spec.attribute ("dds:CubeMapSides", sides); } else { m_spec.attribute ("texturetype", "Plain Texture"); m_spec.attribute ("textureformat", "Plain Texture"); } m_subimage = subimage; m_miplevel = miplevel; newspec = spec (); return true; } bool DDSInput::internal_readimg (unsigned char *dst, int w, int h, int d) { if (m_dds.fmt.flags & DDS_PF_FOURCC) { // compressed image int flags = 0; switch (m_dds.fmt.fourCC) { case DDS_4CC_DXT1: flags = squish::kDxt1; break; // DXT2 and 3 are the same, only 2 has pre-multiplied alpha case DDS_4CC_DXT2: case DDS_4CC_DXT3: flags = squish::kDxt3; break; // DXT4 and 5 are the same, only 4 has pre-multiplied alpha case DDS_4CC_DXT4: case DDS_4CC_DXT5: flags = squish::kDxt5; break; } // create source buffer std::vector tmp(squish::GetStorageRequirements (w, h, flags)); // load image into buffer if (! fread (&tmp[0], tmp.size(), 1)) return false; // decompress image squish::DecompressImage (dst, w, h, &tmp[0], flags); tmp.clear(); // correct pre-multiplied alpha, if necessary if (m_dds.fmt.fourCC == DDS_4CC_DXT2 || m_dds.fmt.fourCC == DDS_4CC_DXT4) { int k; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { k = (y * w + x) * 4; dst[k + 0] = (unsigned char)((int)dst[k + 0] * 255 / (int)dst[k + 3]); dst[k + 1] = (unsigned char)((int)dst[k + 1] * 255 / (int)dst[k + 3]); dst[k + 2] = (unsigned char)((int)dst[k + 2] * 255 / (int)dst[k + 3]); } } } } else { // uncompressed image // HACK: shortcut for luminance if (m_dds.fmt.flags & DDS_PF_LUMINANCE) { return fread (dst, w * m_Bpp, h); } int k, pixel = 0; for (int z = 0; z < d; z++) { for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { if (! fread (&pixel, 1, m_Bpp)) return false; k = (z * h * w + y * w + x) * m_spec.nchannels; dst[k + 0] = ((pixel & m_dds.fmt.rmask) >> m_redR) << m_redL; dst[k + 1] = ((pixel & m_dds.fmt.gmask) >> m_greenR) << m_greenL; dst[k + 2] = ((pixel & m_dds.fmt.bmask) >> m_blueR) << m_blueL; if (m_dds.fmt.flags & DDS_PF_ALPHA) dst[k + 3] = ((pixel & m_dds.fmt.amask) >> m_alphaR) << m_alphaL; } } } } return true; } bool DDSInput::readimg_scanlines () { //std::cerr << "[dds] readimg: " << ftell (m_file) << "\n"; // resize destination buffer m_buf.resize (m_spec.scanline_bytes() * m_spec.height * m_spec.depth /*/ (1 << m_miplevel)*/); return internal_readimg (&m_buf[0], m_spec.width, m_spec.height, m_spec.depth); } bool DDSInput::readimg_tiles () { // resize destination buffer m_buf.resize (m_spec.tile_bytes()); return internal_readimg (&m_buf[0], m_spec.tile_width, m_spec.tile_height, m_spec.tile_depth); } bool DDSInput::close () { if (m_file) { fclose (m_file); m_file = NULL; } init(); // Reset to initial state return true; } bool DDSInput::read_native_scanline (int y, int z, void *data) { // don't proceed if a cube map - use tiles then instead if (m_dds.caps.flags2 & DDS_CAPS2_CUBEMAP) return false; if (m_buf.empty ()) readimg_scanlines (); size_t size = spec().scanline_bytes(); memcpy (data, &m_buf[0] + z * m_spec.height * size + y * size, size); return true; } bool DDSInput::read_native_tile (int x, int y, int z, void *data) { // static ints to keep track of the current cube face and re-seek and // re-read face static int lastx = -1, lasty = -1, lastz = -1; // don't proceed if not a cube map - use scanlines then instead if (!(m_dds.caps.flags2 & DDS_CAPS2_CUBEMAP)) return false; // make sure we get the right dimensions if (x % m_spec.tile_width || y % m_spec.tile_height || z % m_spec.tile_width) return false; if (m_buf.empty() || x != lastx || y != lasty || z != lastz) { lastx = x; lasty = y; lastz = z; unsigned int w = 0, h = 0, d = 0; #ifdef DDS_3X2_CUBE_MAP_LAYOUT internal_seek_subimage (((x / m_spec.tile_width) << 1) + y / m_spec.tile_height, m_miplevel, w, h, d); #else // 1x6 layout internal_seek_subimage (y / m_spec.tile_height, m_miplevel, w, h, d); #endif // DDS_3X2_CUBE_MAP_LAYOUT if (!w && !h && !d) // face not present in file, black-pad the image memset (&m_buf[0], 0, m_spec.tile_bytes()); else readimg_tiles (); } memcpy (data, &m_buf[0], m_spec.tile_bytes()); return true; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/dds.imageio/ddsoutput.cpp0000644000175000017500000000676013151711064022047 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include "dds_pvt.h" #include "oiioversion.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/fmath.h" OIIO_PLUGIN_NAMESPACE_BEGIN using namespace DDS_pvt; class DDSOutput : public ImageOutput { public: DDSOutput (); virtual ~DDSOutput (); virtual const char * format_name (void) const { return "dds"; } virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode); virtual bool close (); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); private: std::string m_filename; ///< Stash the filename FILE *m_file; ///< Open image handle std::vector m_scratch; // Initialize private members to pre-opened state void init (void) { m_file = NULL; } }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *dds_output_imageio_create () { return new DDSOutput; } // OIIO_EXPORT int dds_imageio_version = OIIO_PLUGIN_VERSION; // it's in tgainput.cpp OIIO_EXPORT const char * dds_output_extensions[] = { "dds", NULL }; OIIO_PLUGIN_EXPORTS_END DDSOutput::DDSOutput () { init (); } DDSOutput::~DDSOutput () { // Close, if not already done. close (); } bool DDSOutput::open (const std::string &name, const ImageSpec &userspec, OpenMode mode) { error ("DDS writing is not supported yet, please poke Leszek in the " "mailing list"); return false; } bool DDSOutput::close () { return false; } bool DDSOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { return false; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/dds.imageio/dds_pvt.h0000644000175000017500000001221413151711064021113 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_DDS_PVT_H #define OPENIMAGEIO_DDS_PVT_H #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" OIIO_PLUGIN_NAMESPACE_BEGIN namespace DDS_pvt { // IneQuation was here #define DDS_MAKE4CC(a, b, c, d) (a | b << 8 | c << 16 | d << 24) #define DDS_4CC_DXT1 DDS_MAKE4CC('D', 'X', 'T', '1') #define DDS_4CC_DXT2 DDS_MAKE4CC('D', 'X', 'T', '2') #define DDS_4CC_DXT3 DDS_MAKE4CC('D', 'X', 'T', '3') #define DDS_4CC_DXT4 DDS_MAKE4CC('D', 'X', 'T', '4') #define DDS_4CC_DXT5 DDS_MAKE4CC('D', 'X', 'T', '5') /// DDS pixel format flags. Channel flags are only applicable for uncompressed /// images. /// enum { DDS_PF_ALPHA = 0x00000001, ///< image has alpha channel DDS_PF_FOURCC = 0x00000004, ///< image is compressed DDS_PF_LUMINANCE = 0x00020000, ///< image has luminance data DDS_PF_RGB = 0x00000040 ///< image has RGB data }; /// DDS pixel format structure. /// typedef struct { uint32_t size; ///< structure size, must be 32 uint32_t flags; ///< flags to indicate valid fields uint32_t fourCC; ///< compression four-character code uint32_t bpp; ///< bits per pixel uint32_t rmask; ///< bitmask for the red channel uint32_t gmask; ///< bitmask for the green channel uint32_t bmask; ///< bitmask for the blue channel uint32_t amask; ///< bitmask for the alpha channel } dds_pixformat; /// DDS caps flags, field 1. /// enum { DDS_CAPS1_COMPLEX = 0x00000008, ///< >2D image or cube map DDS_CAPS1_TEXTURE = 0x00001000, ///< should be set for all DDS files DDS_CAPS1_MIPMAP = 0x00400000 ///< image has mipmaps }; /// DDS caps flags, field 2. /// enum { DDS_CAPS2_CUBEMAP = 0x00000200, ///< image is a cube map DDS_CAPS2_CUBEMAP_POSITIVEX = 0x00000400, ///< +x side DDS_CAPS2_CUBEMAP_NEGATIVEX = 0x00000800, ///< -x side DDS_CAPS2_CUBEMAP_POSITIVEY = 0x00001000, ///< +y side DDS_CAPS2_CUBEMAP_NEGATIVEY = 0x00002000, ///< -y side DDS_CAPS2_CUBEMAP_POSITIVEZ = 0x00004000, ///< +z side DDS_CAPS2_CUBEMAP_NEGATIVEZ = 0x00008000, ///< -z side DDS_CAPS2_VOLUME = 0x00200000 ///< image is a 3D texture }; /// DDS caps structure. /// typedef struct { uint32_t flags1; ///< flags to indicate certain surface properties uint32_t flags2; ///< flags to indicate certain surface properties } dds_caps; /// DDS global flags - indicate valid header fields. /// enum { DDS_CAPS = 0x00000001, DDS_HEIGHT = 0x00000002, DDS_WIDTH = 0x00000004, DDS_PITCH = 0x00000008, DDS_PIXELFORMAT = 0x00001000, DDS_MIPMAPCOUNT = 0x00020000, DDS_LINEARSIZE = 0x00080000, DDS_DEPTH = 0x00800000 }; /// DDS file header. /// Please note that this layout is not identical to the one found in a file. /// typedef struct { uint32_t fourCC; ///< file four-character code uint32_t size; ///< structure size, must be 124 uint32_t flags; ///< flags to indicate valid fields uint32_t height; ///< image height uint32_t width; ///< image width uint32_t pitch; ///< bytes per scanline (uncmp.)/total byte size (cmp.) uint32_t depth; ///< image depth (for 3D textures) uint32_t mipmaps; ///< number of mipmaps // 11 reserved 4-byte fields come in here dds_pixformat fmt; ///< pixel format dds_caps caps; ///< DirectDraw Surface caps } dds_header; } // namespace DDS_pvt OIIO_PLUGIN_NAMESPACE_END #endif // OPENIMAGEIO_DDS_PVT_H openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/0000755000175000017500000000000013151711064020613 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/clusterfit.cpp0000644000175000017500000002756213151711064023517 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2007 Ignacio Castano icastano@nvidia.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #include "clusterfit.h" #include "colourset.h" #include "colourblock.h" #include namespace squish { ClusterFit::ClusterFit( ColourSet const* colours, int flags, float* metric ) : ColourFit( colours, flags ) { // set the iteration count m_iterationCount = ( m_flags & kColourIterativeClusterFit ) ? kMaxIterations : 1; // initialise the metric (old perceptual = 0.2126f, 0.7152f, 0.0722f) if( metric ) m_metric = Vec4( metric[0], metric[1], metric[2], 1.0f ); else m_metric = VEC4_CONST( 1.0f ); // initialise the best error m_besterror = VEC4_CONST( FLT_MAX ); // cache some values int const count = m_colours->GetCount(); Vec3 const* values = m_colours->GetPoints(); // get the covariance matrix Sym3x3 covariance = ComputeWeightedCovariance( count, values, m_colours->GetWeights() ); // compute the principle component m_principle = ComputePrincipleComponent( covariance ); } bool ClusterFit::ConstructOrdering( Vec3 const& axis, int iteration ) { // cache some values int const count = m_colours->GetCount(); Vec3 const* values = m_colours->GetPoints(); // build the list of dot products float dps[16]; u8* order = ( u8* )m_order + 16*iteration; for( int i = 0; i < count; ++i ) { dps[i] = Dot( values[i], axis ); order[i] = ( u8 )i; } // stable sort using them for( int i = 0; i < count; ++i ) { for( int j = i; j > 0 && dps[j] < dps[j - 1]; --j ) { std::swap( dps[j], dps[j - 1] ); std::swap( order[j], order[j - 1] ); } } // check this ordering is unique for( int it = 0; it < iteration; ++it ) { u8 const* prev = ( u8* )m_order + 16*it; bool same = true; for( int i = 0; i < count; ++i ) { if( order[i] != prev[i] ) { same = false; break; } } if( same ) return false; } // copy the ordering and weight all the points Vec3 const* unweighted = m_colours->GetPoints(); float const* weights = m_colours->GetWeights(); m_xsum_wsum = VEC4_CONST( 0.0f ); for( int i = 0; i < count; ++i ) { int j = order[i]; Vec4 p( unweighted[j].X(), unweighted[j].Y(), unweighted[j].Z(), 1.0f ); Vec4 w( weights[j] ); Vec4 x = p*w; m_points_weights[i] = x; m_xsum_wsum += x; } return true; } void ClusterFit::Compress3( void* block ) { // declare variables int const count = m_colours->GetCount(); Vec4 const two = VEC4_CONST( 2.0 ); Vec4 const one = VEC4_CONST( 1.0f ); Vec4 const half_half2( 0.5f, 0.5f, 0.5f, 0.25f ); Vec4 const zero = VEC4_CONST( 0.0f ); Vec4 const half = VEC4_CONST( 0.5f ); Vec4 const grid( 31.0f, 63.0f, 31.0f, 0.0f ); Vec4 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f, 0.0f ); // prepare an ordering using the principle axis ConstructOrdering( m_principle, 0 ); // check all possible clusters and iterate on the total order Vec4 beststart = VEC4_CONST( 0.0f ); Vec4 bestend = VEC4_CONST( 0.0f ); Vec4 besterror = m_besterror; u8 bestindices[16]; int bestiteration = 0; int besti = 0, bestj = 0; // loop over iterations (we avoid the case that all points in first or last cluster) for( int iterationIndex = 0;; ) { // first cluster [0,i) is at the start Vec4 part0 = VEC4_CONST( 0.0f ); for( int i = 0; i < count; ++i ) { // second cluster [i,j) is half along Vec4 part1 = ( i == 0 ) ? m_points_weights[0] : VEC4_CONST( 0.0f ); int jmin = ( i == 0 ) ? 1 : i; for( int j = jmin;; ) { // last cluster [j,count) is at the end Vec4 part2 = m_xsum_wsum - part1 - part0; // compute least squares terms directly Vec4 alphax_sum = MultiplyAdd( part1, half_half2, part0 ); Vec4 alpha2_sum = alphax_sum.SplatW(); Vec4 betax_sum = MultiplyAdd( part1, half_half2, part2 ); Vec4 beta2_sum = betax_sum.SplatW(); Vec4 alphabeta_sum = ( part1*half_half2 ).SplatW(); // compute the least-squares optimal points Vec4 factor = Reciprocal( NegativeMultiplySubtract( alphabeta_sum, alphabeta_sum, alpha2_sum*beta2_sum ) ); Vec4 a = NegativeMultiplySubtract( betax_sum, alphabeta_sum, alphax_sum*beta2_sum )*factor; Vec4 b = NegativeMultiplySubtract( alphax_sum, alphabeta_sum, betax_sum*alpha2_sum )*factor; // clamp to the grid a = Min( one, Max( zero, a ) ); b = Min( one, Max( zero, b ) ); a = Truncate( MultiplyAdd( grid, a, half ) )*gridrcp; b = Truncate( MultiplyAdd( grid, b, half ) )*gridrcp; // compute the error (we skip the constant xxsum) Vec4 e1 = MultiplyAdd( a*a, alpha2_sum, b*b*beta2_sum ); Vec4 e2 = NegativeMultiplySubtract( a, alphax_sum, a*b*alphabeta_sum ); Vec4 e3 = NegativeMultiplySubtract( b, betax_sum, e2 ); Vec4 e4 = MultiplyAdd( two, e3, e1 ); // apply the metric to the error term Vec4 e5 = e4*m_metric; Vec4 error = e5.SplatX() + e5.SplatY() + e5.SplatZ(); // keep the solution if it wins if( CompareAnyLessThan( error, besterror ) ) { beststart = a; bestend = b; besti = i; bestj = j; besterror = error; bestiteration = iterationIndex; } // advance if( j == count ) break; part1 += m_points_weights[j]; ++j; } // advance part0 += m_points_weights[i]; } // stop if we didn't improve in this iteration if( bestiteration != iterationIndex ) break; // advance if possible ++iterationIndex; if( iterationIndex == m_iterationCount ) break; // stop if a new iteration is an ordering that has already been tried Vec3 axis = ( bestend - beststart ).GetVec3(); if( !ConstructOrdering( axis, iterationIndex ) ) break; } // save the block if necessary if( CompareAnyLessThan( besterror, m_besterror ) ) { // remap the indices u8 const* order = ( u8* )m_order + 16*bestiteration; u8 unordered[16]; for( int m = 0; m < besti; ++m ) unordered[order[m]] = 0; for( int m = besti; m < bestj; ++m ) unordered[order[m]] = 2; for( int m = bestj; m < count; ++m ) unordered[order[m]] = 1; m_colours->RemapIndices( unordered, bestindices ); // save the block WriteColourBlock3( beststart.GetVec3(), bestend.GetVec3(), bestindices, block ); // save the error m_besterror = besterror; } } void ClusterFit::Compress4( void* block ) { // declare variables int const count = m_colours->GetCount(); Vec4 const two = VEC4_CONST( 2.0f ); Vec4 const one = VEC4_CONST( 1.0f ); Vec4 const onethird_onethird2( 1.0f/3.0f, 1.0f/3.0f, 1.0f/3.0f, 1.0f/9.0f ); Vec4 const twothirds_twothirds2( 2.0f/3.0f, 2.0f/3.0f, 2.0f/3.0f, 4.0f/9.0f ); Vec4 const twonineths = VEC4_CONST( 2.0f/9.0f ); Vec4 const zero = VEC4_CONST( 0.0f ); Vec4 const half = VEC4_CONST( 0.5f ); Vec4 const grid( 31.0f, 63.0f, 31.0f, 0.0f ); Vec4 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f, 0.0f ); // prepare an ordering using the principle axis ConstructOrdering( m_principle, 0 ); // check all possible clusters and iterate on the total order Vec4 beststart = VEC4_CONST( 0.0f ); Vec4 bestend = VEC4_CONST( 0.0f ); Vec4 besterror = m_besterror; u8 bestindices[16]; int bestiteration = 0; int besti = 0, bestj = 0, bestk = 0; // loop over iterations (we avoid the case that all points in first or last cluster) for( int iterationIndex = 0;; ) { // first cluster [0,i) is at the start Vec4 part0 = VEC4_CONST( 0.0f ); for( int i = 0; i < count; ++i ) { // second cluster [i,j) is one third along Vec4 part1 = VEC4_CONST( 0.0f ); for( int j = i;; ) { // third cluster [j,k) is two thirds along Vec4 part2 = ( j == 0 ) ? m_points_weights[0] : VEC4_CONST( 0.0f ); int kmin = ( j == 0 ) ? 1 : j; for( int k = kmin;; ) { // last cluster [k,count) is at the end Vec4 part3 = m_xsum_wsum - part2 - part1 - part0; // compute least squares terms directly Vec4 const alphax_sum = MultiplyAdd( part2, onethird_onethird2, MultiplyAdd( part1, twothirds_twothirds2, part0 ) ); Vec4 const alpha2_sum = alphax_sum.SplatW(); Vec4 const betax_sum = MultiplyAdd( part1, onethird_onethird2, MultiplyAdd( part2, twothirds_twothirds2, part3 ) ); Vec4 const beta2_sum = betax_sum.SplatW(); Vec4 const alphabeta_sum = twonineths*( part1 + part2 ).SplatW(); // compute the least-squares optimal points Vec4 factor = Reciprocal( NegativeMultiplySubtract( alphabeta_sum, alphabeta_sum, alpha2_sum*beta2_sum ) ); Vec4 a = NegativeMultiplySubtract( betax_sum, alphabeta_sum, alphax_sum*beta2_sum )*factor; Vec4 b = NegativeMultiplySubtract( alphax_sum, alphabeta_sum, betax_sum*alpha2_sum )*factor; // clamp to the grid a = Min( one, Max( zero, a ) ); b = Min( one, Max( zero, b ) ); a = Truncate( MultiplyAdd( grid, a, half ) )*gridrcp; b = Truncate( MultiplyAdd( grid, b, half ) )*gridrcp; // compute the error (we skip the constant xxsum) Vec4 e1 = MultiplyAdd( a*a, alpha2_sum, b*b*beta2_sum ); Vec4 e2 = NegativeMultiplySubtract( a, alphax_sum, a*b*alphabeta_sum ); Vec4 e3 = NegativeMultiplySubtract( b, betax_sum, e2 ); Vec4 e4 = MultiplyAdd( two, e3, e1 ); // apply the metric to the error term Vec4 e5 = e4*m_metric; Vec4 error = e5.SplatX() + e5.SplatY() + e5.SplatZ(); // keep the solution if it wins if( CompareAnyLessThan( error, besterror ) ) { beststart = a; bestend = b; besterror = error; besti = i; bestj = j; bestk = k; bestiteration = iterationIndex; } // advance if( k == count ) break; part2 += m_points_weights[k]; ++k; } // advance if( j == count ) break; part1 += m_points_weights[j]; ++j; } // advance part0 += m_points_weights[i]; } // stop if we didn't improve in this iteration if( bestiteration != iterationIndex ) break; // advance if possible ++iterationIndex; if( iterationIndex == m_iterationCount ) break; // stop if a new iteration is an ordering that has already been tried Vec3 axis = ( bestend - beststart ).GetVec3(); if( !ConstructOrdering( axis, iterationIndex ) ) break; } // save the block if necessary if( CompareAnyLessThan( besterror, m_besterror ) ) { // remap the indices u8 const* order = ( u8* )m_order + 16*bestiteration; u8 unordered[16]; for( int m = 0; m < besti; ++m ) unordered[order[m]] = 0; for( int m = besti; m < bestj; ++m ) unordered[order[m]] = 2; for( int m = bestj; m < bestk; ++m ) unordered[order[m]] = 3; for( int m = bestk; m < count; ++m ) unordered[order[m]] = 1; m_colours->RemapIndices( unordered, bestindices ); // save the block WriteColourBlock4( beststart.GetVec3(), bestend.GetVec3(), bestindices, block ); // save the error m_besterror = besterror; } } } // namespace squish openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/colourblock.h0000644000175000017500000000326513151711064023310 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #ifndef SQUISH_COLOURBLOCK_H #define SQUISH_COLOURBLOCK_H #include "squish.h" #include "maths.h" namespace squish { void WriteColourBlock3( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block ); void WriteColourBlock4( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block ); void DecompressColour( u8* rgba, void const* block, bool isDxt1 ); } // namespace squish #endif // ndef SQUISH_COLOURBLOCK_H openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/colourfit.cpp0000644000175000017500000000331213151711064023324 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #include "colourfit.h" #include "colourset.h" namespace squish { ColourFit::ColourFit( ColourSet const* colours, int flags ) : m_colours( colours ), m_flags( flags ) { } ColourFit::~ColourFit() { } void ColourFit::Compress( void* block ) { bool isDxt1 = ( ( m_flags & kDxt1 ) != 0 ); if( isDxt1 ) { Compress3( block ); if( !m_colours->IsTransparent() ) Compress4( block ); } else Compress4( block ); } } // namespace squish openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/singlecolourlookup.inl0000644000175000017500000011255513151711064025267 0ustar mfvmfv static SingleColourLookup const lookup_5_3[] = { { { { 0, 0, 0 }, { 0, 0, 0 } } }, { { { 0, 0, 1 }, { 0, 0, 1 } } }, { { { 0, 0, 2 }, { 0, 0, 2 } } }, { { { 0, 0, 3 }, { 0, 1, 1 } } }, { { { 0, 0, 4 }, { 0, 1, 0 } } }, { { { 1, 0, 3 }, { 0, 1, 1 } } }, { { { 1, 0, 2 }, { 0, 1, 2 } } }, { { { 1, 0, 1 }, { 0, 2, 1 } } }, { { { 1, 0, 0 }, { 0, 2, 0 } } }, { { { 1, 0, 1 }, { 0, 2, 1 } } }, { { { 1, 0, 2 }, { 0, 2, 2 } } }, { { { 1, 0, 3 }, { 0, 3, 1 } } }, { { { 1, 0, 4 }, { 0, 3, 0 } } }, { { { 2, 0, 3 }, { 0, 3, 1 } } }, { { { 2, 0, 2 }, { 0, 3, 2 } } }, { { { 2, 0, 1 }, { 0, 4, 1 } } }, { { { 2, 0, 0 }, { 0, 4, 0 } } }, { { { 2, 0, 1 }, { 0, 4, 1 } } }, { { { 2, 0, 2 }, { 0, 4, 2 } } }, { { { 2, 0, 3 }, { 0, 5, 1 } } }, { { { 2, 0, 4 }, { 0, 5, 0 } } }, { { { 3, 0, 3 }, { 0, 5, 1 } } }, { { { 3, 0, 2 }, { 0, 5, 2 } } }, { { { 3, 0, 1 }, { 0, 6, 1 } } }, { { { 3, 0, 0 }, { 0, 6, 0 } } }, { { { 3, 0, 1 }, { 0, 6, 1 } } }, { { { 3, 0, 2 }, { 0, 6, 2 } } }, { { { 3, 0, 3 }, { 0, 7, 1 } } }, { { { 3, 0, 4 }, { 0, 7, 0 } } }, { { { 4, 0, 4 }, { 0, 7, 1 } } }, { { { 4, 0, 3 }, { 0, 7, 2 } } }, { { { 4, 0, 2 }, { 1, 7, 1 } } }, { { { 4, 0, 1 }, { 1, 7, 0 } } }, { { { 4, 0, 0 }, { 0, 8, 0 } } }, { { { 4, 0, 1 }, { 0, 8, 1 } } }, { { { 4, 0, 2 }, { 2, 7, 1 } } }, { { { 4, 0, 3 }, { 2, 7, 0 } } }, { { { 4, 0, 4 }, { 0, 9, 0 } } }, { { { 5, 0, 3 }, { 0, 9, 1 } } }, { { { 5, 0, 2 }, { 3, 7, 1 } } }, { { { 5, 0, 1 }, { 3, 7, 0 } } }, { { { 5, 0, 0 }, { 0, 10, 0 } } }, { { { 5, 0, 1 }, { 0, 10, 1 } } }, { { { 5, 0, 2 }, { 0, 10, 2 } } }, { { { 5, 0, 3 }, { 0, 11, 1 } } }, { { { 5, 0, 4 }, { 0, 11, 0 } } }, { { { 6, 0, 3 }, { 0, 11, 1 } } }, { { { 6, 0, 2 }, { 0, 11, 2 } } }, { { { 6, 0, 1 }, { 0, 12, 1 } } }, { { { 6, 0, 0 }, { 0, 12, 0 } } }, { { { 6, 0, 1 }, { 0, 12, 1 } } }, { { { 6, 0, 2 }, { 0, 12, 2 } } }, { { { 6, 0, 3 }, { 0, 13, 1 } } }, { { { 6, 0, 4 }, { 0, 13, 0 } } }, { { { 7, 0, 3 }, { 0, 13, 1 } } }, { { { 7, 0, 2 }, { 0, 13, 2 } } }, { { { 7, 0, 1 }, { 0, 14, 1 } } }, { { { 7, 0, 0 }, { 0, 14, 0 } } }, { { { 7, 0, 1 }, { 0, 14, 1 } } }, { { { 7, 0, 2 }, { 0, 14, 2 } } }, { { { 7, 0, 3 }, { 0, 15, 1 } } }, { { { 7, 0, 4 }, { 0, 15, 0 } } }, { { { 8, 0, 4 }, { 0, 15, 1 } } }, { { { 8, 0, 3 }, { 0, 15, 2 } } }, { { { 8, 0, 2 }, { 1, 15, 1 } } }, { { { 8, 0, 1 }, { 1, 15, 0 } } }, { { { 8, 0, 0 }, { 0, 16, 0 } } }, { { { 8, 0, 1 }, { 0, 16, 1 } } }, { { { 8, 0, 2 }, { 2, 15, 1 } } }, { { { 8, 0, 3 }, { 2, 15, 0 } } }, { { { 8, 0, 4 }, { 0, 17, 0 } } }, { { { 9, 0, 3 }, { 0, 17, 1 } } }, { { { 9, 0, 2 }, { 3, 15, 1 } } }, { { { 9, 0, 1 }, { 3, 15, 0 } } }, { { { 9, 0, 0 }, { 0, 18, 0 } } }, { { { 9, 0, 1 }, { 0, 18, 1 } } }, { { { 9, 0, 2 }, { 0, 18, 2 } } }, { { { 9, 0, 3 }, { 0, 19, 1 } } }, { { { 9, 0, 4 }, { 0, 19, 0 } } }, { { { 10, 0, 3 }, { 0, 19, 1 } } }, { { { 10, 0, 2 }, { 0, 19, 2 } } }, { { { 10, 0, 1 }, { 0, 20, 1 } } }, { { { 10, 0, 0 }, { 0, 20, 0 } } }, { { { 10, 0, 1 }, { 0, 20, 1 } } }, { { { 10, 0, 2 }, { 0, 20, 2 } } }, { { { 10, 0, 3 }, { 0, 21, 1 } } }, { { { 10, 0, 4 }, { 0, 21, 0 } } }, { { { 11, 0, 3 }, { 0, 21, 1 } } }, { { { 11, 0, 2 }, { 0, 21, 2 } } }, { { { 11, 0, 1 }, { 0, 22, 1 } } }, { { { 11, 0, 0 }, { 0, 22, 0 } } }, { { { 11, 0, 1 }, { 0, 22, 1 } } }, { { { 11, 0, 2 }, { 0, 22, 2 } } }, { { { 11, 0, 3 }, { 0, 23, 1 } } }, { { { 11, 0, 4 }, { 0, 23, 0 } } }, { { { 12, 0, 4 }, { 0, 23, 1 } } }, { { { 12, 0, 3 }, { 0, 23, 2 } } }, { { { 12, 0, 2 }, { 1, 23, 1 } } }, { { { 12, 0, 1 }, { 1, 23, 0 } } }, { { { 12, 0, 0 }, { 0, 24, 0 } } }, { { { 12, 0, 1 }, { 0, 24, 1 } } }, { { { 12, 0, 2 }, { 2, 23, 1 } } }, { { { 12, 0, 3 }, { 2, 23, 0 } } }, { { { 12, 0, 4 }, { 0, 25, 0 } } }, { { { 13, 0, 3 }, { 0, 25, 1 } } }, { { { 13, 0, 2 }, { 3, 23, 1 } } }, { { { 13, 0, 1 }, { 3, 23, 0 } } }, { { { 13, 0, 0 }, { 0, 26, 0 } } }, { { { 13, 0, 1 }, { 0, 26, 1 } } }, { { { 13, 0, 2 }, { 0, 26, 2 } } }, { { { 13, 0, 3 }, { 0, 27, 1 } } }, { { { 13, 0, 4 }, { 0, 27, 0 } } }, { { { 14, 0, 3 }, { 0, 27, 1 } } }, { { { 14, 0, 2 }, { 0, 27, 2 } } }, { { { 14, 0, 1 }, { 0, 28, 1 } } }, { { { 14, 0, 0 }, { 0, 28, 0 } } }, { { { 14, 0, 1 }, { 0, 28, 1 } } }, { { { 14, 0, 2 }, { 0, 28, 2 } } }, { { { 14, 0, 3 }, { 0, 29, 1 } } }, { { { 14, 0, 4 }, { 0, 29, 0 } } }, { { { 15, 0, 3 }, { 0, 29, 1 } } }, { { { 15, 0, 2 }, { 0, 29, 2 } } }, { { { 15, 0, 1 }, { 0, 30, 1 } } }, { { { 15, 0, 0 }, { 0, 30, 0 } } }, { { { 15, 0, 1 }, { 0, 30, 1 } } }, { { { 15, 0, 2 }, { 0, 30, 2 } } }, { { { 15, 0, 3 }, { 0, 31, 1 } } }, { { { 15, 0, 4 }, { 0, 31, 0 } } }, { { { 16, 0, 4 }, { 0, 31, 1 } } }, { { { 16, 0, 3 }, { 0, 31, 2 } } }, { { { 16, 0, 2 }, { 1, 31, 1 } } }, { { { 16, 0, 1 }, { 1, 31, 0 } } }, { { { 16, 0, 0 }, { 4, 28, 0 } } }, { { { 16, 0, 1 }, { 4, 28, 1 } } }, { { { 16, 0, 2 }, { 2, 31, 1 } } }, { { { 16, 0, 3 }, { 2, 31, 0 } } }, { { { 16, 0, 4 }, { 4, 29, 0 } } }, { { { 17, 0, 3 }, { 4, 29, 1 } } }, { { { 17, 0, 2 }, { 3, 31, 1 } } }, { { { 17, 0, 1 }, { 3, 31, 0 } } }, { { { 17, 0, 0 }, { 4, 30, 0 } } }, { { { 17, 0, 1 }, { 4, 30, 1 } } }, { { { 17, 0, 2 }, { 4, 30, 2 } } }, { { { 17, 0, 3 }, { 4, 31, 1 } } }, { { { 17, 0, 4 }, { 4, 31, 0 } } }, { { { 18, 0, 3 }, { 4, 31, 1 } } }, { { { 18, 0, 2 }, { 4, 31, 2 } } }, { { { 18, 0, 1 }, { 5, 31, 1 } } }, { { { 18, 0, 0 }, { 5, 31, 0 } } }, { { { 18, 0, 1 }, { 5, 31, 1 } } }, { { { 18, 0, 2 }, { 5, 31, 2 } } }, { { { 18, 0, 3 }, { 6, 31, 1 } } }, { { { 18, 0, 4 }, { 6, 31, 0 } } }, { { { 19, 0, 3 }, { 6, 31, 1 } } }, { { { 19, 0, 2 }, { 6, 31, 2 } } }, { { { 19, 0, 1 }, { 7, 31, 1 } } }, { { { 19, 0, 0 }, { 7, 31, 0 } } }, { { { 19, 0, 1 }, { 7, 31, 1 } } }, { { { 19, 0, 2 }, { 7, 31, 2 } } }, { { { 19, 0, 3 }, { 8, 31, 1 } } }, { { { 19, 0, 4 }, { 8, 31, 0 } } }, { { { 20, 0, 4 }, { 8, 31, 1 } } }, { { { 20, 0, 3 }, { 8, 31, 2 } } }, { { { 20, 0, 2 }, { 9, 31, 1 } } }, { { { 20, 0, 1 }, { 9, 31, 0 } } }, { { { 20, 0, 0 }, { 12, 28, 0 } } }, { { { 20, 0, 1 }, { 12, 28, 1 } } }, { { { 20, 0, 2 }, { 10, 31, 1 } } }, { { { 20, 0, 3 }, { 10, 31, 0 } } }, { { { 20, 0, 4 }, { 12, 29, 0 } } }, { { { 21, 0, 3 }, { 12, 29, 1 } } }, { { { 21, 0, 2 }, { 11, 31, 1 } } }, { { { 21, 0, 1 }, { 11, 31, 0 } } }, { { { 21, 0, 0 }, { 12, 30, 0 } } }, { { { 21, 0, 1 }, { 12, 30, 1 } } }, { { { 21, 0, 2 }, { 12, 30, 2 } } }, { { { 21, 0, 3 }, { 12, 31, 1 } } }, { { { 21, 0, 4 }, { 12, 31, 0 } } }, { { { 22, 0, 3 }, { 12, 31, 1 } } }, { { { 22, 0, 2 }, { 12, 31, 2 } } }, { { { 22, 0, 1 }, { 13, 31, 1 } } }, { { { 22, 0, 0 }, { 13, 31, 0 } } }, { { { 22, 0, 1 }, { 13, 31, 1 } } }, { { { 22, 0, 2 }, { 13, 31, 2 } } }, { { { 22, 0, 3 }, { 14, 31, 1 } } }, { { { 22, 0, 4 }, { 14, 31, 0 } } }, { { { 23, 0, 3 }, { 14, 31, 1 } } }, { { { 23, 0, 2 }, { 14, 31, 2 } } }, { { { 23, 0, 1 }, { 15, 31, 1 } } }, { { { 23, 0, 0 }, { 15, 31, 0 } } }, { { { 23, 0, 1 }, { 15, 31, 1 } } }, { { { 23, 0, 2 }, { 15, 31, 2 } } }, { { { 23, 0, 3 }, { 16, 31, 1 } } }, { { { 23, 0, 4 }, { 16, 31, 0 } } }, { { { 24, 0, 4 }, { 16, 31, 1 } } }, { { { 24, 0, 3 }, { 16, 31, 2 } } }, { { { 24, 0, 2 }, { 17, 31, 1 } } }, { { { 24, 0, 1 }, { 17, 31, 0 } } }, { { { 24, 0, 0 }, { 20, 28, 0 } } }, { { { 24, 0, 1 }, { 20, 28, 1 } } }, { { { 24, 0, 2 }, { 18, 31, 1 } } }, { { { 24, 0, 3 }, { 18, 31, 0 } } }, { { { 24, 0, 4 }, { 20, 29, 0 } } }, { { { 25, 0, 3 }, { 20, 29, 1 } } }, { { { 25, 0, 2 }, { 19, 31, 1 } } }, { { { 25, 0, 1 }, { 19, 31, 0 } } }, { { { 25, 0, 0 }, { 20, 30, 0 } } }, { { { 25, 0, 1 }, { 20, 30, 1 } } }, { { { 25, 0, 2 }, { 20, 30, 2 } } }, { { { 25, 0, 3 }, { 20, 31, 1 } } }, { { { 25, 0, 4 }, { 20, 31, 0 } } }, { { { 26, 0, 3 }, { 20, 31, 1 } } }, { { { 26, 0, 2 }, { 20, 31, 2 } } }, { { { 26, 0, 1 }, { 21, 31, 1 } } }, { { { 26, 0, 0 }, { 21, 31, 0 } } }, { { { 26, 0, 1 }, { 21, 31, 1 } } }, { { { 26, 0, 2 }, { 21, 31, 2 } } }, { { { 26, 0, 3 }, { 22, 31, 1 } } }, { { { 26, 0, 4 }, { 22, 31, 0 } } }, { { { 27, 0, 3 }, { 22, 31, 1 } } }, { { { 27, 0, 2 }, { 22, 31, 2 } } }, { { { 27, 0, 1 }, { 23, 31, 1 } } }, { { { 27, 0, 0 }, { 23, 31, 0 } } }, { { { 27, 0, 1 }, { 23, 31, 1 } } }, { { { 27, 0, 2 }, { 23, 31, 2 } } }, { { { 27, 0, 3 }, { 24, 31, 1 } } }, { { { 27, 0, 4 }, { 24, 31, 0 } } }, { { { 28, 0, 4 }, { 24, 31, 1 } } }, { { { 28, 0, 3 }, { 24, 31, 2 } } }, { { { 28, 0, 2 }, { 25, 31, 1 } } }, { { { 28, 0, 1 }, { 25, 31, 0 } } }, { { { 28, 0, 0 }, { 28, 28, 0 } } }, { { { 28, 0, 1 }, { 28, 28, 1 } } }, { { { 28, 0, 2 }, { 26, 31, 1 } } }, { { { 28, 0, 3 }, { 26, 31, 0 } } }, { { { 28, 0, 4 }, { 28, 29, 0 } } }, { { { 29, 0, 3 }, { 28, 29, 1 } } }, { { { 29, 0, 2 }, { 27, 31, 1 } } }, { { { 29, 0, 1 }, { 27, 31, 0 } } }, { { { 29, 0, 0 }, { 28, 30, 0 } } }, { { { 29, 0, 1 }, { 28, 30, 1 } } }, { { { 29, 0, 2 }, { 28, 30, 2 } } }, { { { 29, 0, 3 }, { 28, 31, 1 } } }, { { { 29, 0, 4 }, { 28, 31, 0 } } }, { { { 30, 0, 3 }, { 28, 31, 1 } } }, { { { 30, 0, 2 }, { 28, 31, 2 } } }, { { { 30, 0, 1 }, { 29, 31, 1 } } }, { { { 30, 0, 0 }, { 29, 31, 0 } } }, { { { 30, 0, 1 }, { 29, 31, 1 } } }, { { { 30, 0, 2 }, { 29, 31, 2 } } }, { { { 30, 0, 3 }, { 30, 31, 1 } } }, { { { 30, 0, 4 }, { 30, 31, 0 } } }, { { { 31, 0, 3 }, { 30, 31, 1 } } }, { { { 31, 0, 2 }, { 30, 31, 2 } } }, { { { 31, 0, 1 }, { 31, 31, 1 } } }, { { { 31, 0, 0 }, { 31, 31, 0 } } } }; static SingleColourLookup const lookup_6_3[] = { { { { 0, 0, 0 }, { 0, 0, 0 } } }, { { { 0, 0, 1 }, { 0, 1, 1 } } }, { { { 0, 0, 2 }, { 0, 1, 0 } } }, { { { 1, 0, 1 }, { 0, 2, 1 } } }, { { { 1, 0, 0 }, { 0, 2, 0 } } }, { { { 1, 0, 1 }, { 0, 3, 1 } } }, { { { 1, 0, 2 }, { 0, 3, 0 } } }, { { { 2, 0, 1 }, { 0, 4, 1 } } }, { { { 2, 0, 0 }, { 0, 4, 0 } } }, { { { 2, 0, 1 }, { 0, 5, 1 } } }, { { { 2, 0, 2 }, { 0, 5, 0 } } }, { { { 3, 0, 1 }, { 0, 6, 1 } } }, { { { 3, 0, 0 }, { 0, 6, 0 } } }, { { { 3, 0, 1 }, { 0, 7, 1 } } }, { { { 3, 0, 2 }, { 0, 7, 0 } } }, { { { 4, 0, 1 }, { 0, 8, 1 } } }, { { { 4, 0, 0 }, { 0, 8, 0 } } }, { { { 4, 0, 1 }, { 0, 9, 1 } } }, { { { 4, 0, 2 }, { 0, 9, 0 } } }, { { { 5, 0, 1 }, { 0, 10, 1 } } }, { { { 5, 0, 0 }, { 0, 10, 0 } } }, { { { 5, 0, 1 }, { 0, 11, 1 } } }, { { { 5, 0, 2 }, { 0, 11, 0 } } }, { { { 6, 0, 1 }, { 0, 12, 1 } } }, { { { 6, 0, 0 }, { 0, 12, 0 } } }, { { { 6, 0, 1 }, { 0, 13, 1 } } }, { { { 6, 0, 2 }, { 0, 13, 0 } } }, { { { 7, 0, 1 }, { 0, 14, 1 } } }, { { { 7, 0, 0 }, { 0, 14, 0 } } }, { { { 7, 0, 1 }, { 0, 15, 1 } } }, { { { 7, 0, 2 }, { 0, 15, 0 } } }, { { { 8, 0, 1 }, { 0, 16, 1 } } }, { { { 8, 0, 0 }, { 0, 16, 0 } } }, { { { 8, 0, 1 }, { 0, 17, 1 } } }, { { { 8, 0, 2 }, { 0, 17, 0 } } }, { { { 9, 0, 1 }, { 0, 18, 1 } } }, { { { 9, 0, 0 }, { 0, 18, 0 } } }, { { { 9, 0, 1 }, { 0, 19, 1 } } }, { { { 9, 0, 2 }, { 0, 19, 0 } } }, { { { 10, 0, 1 }, { 0, 20, 1 } } }, { { { 10, 0, 0 }, { 0, 20, 0 } } }, { { { 10, 0, 1 }, { 0, 21, 1 } } }, { { { 10, 0, 2 }, { 0, 21, 0 } } }, { { { 11, 0, 1 }, { 0, 22, 1 } } }, { { { 11, 0, 0 }, { 0, 22, 0 } } }, { { { 11, 0, 1 }, { 0, 23, 1 } } }, { { { 11, 0, 2 }, { 0, 23, 0 } } }, { { { 12, 0, 1 }, { 0, 24, 1 } } }, { { { 12, 0, 0 }, { 0, 24, 0 } } }, { { { 12, 0, 1 }, { 0, 25, 1 } } }, { { { 12, 0, 2 }, { 0, 25, 0 } } }, { { { 13, 0, 1 }, { 0, 26, 1 } } }, { { { 13, 0, 0 }, { 0, 26, 0 } } }, { { { 13, 0, 1 }, { 0, 27, 1 } } }, { { { 13, 0, 2 }, { 0, 27, 0 } } }, { { { 14, 0, 1 }, { 0, 28, 1 } } }, { { { 14, 0, 0 }, { 0, 28, 0 } } }, { { { 14, 0, 1 }, { 0, 29, 1 } } }, { { { 14, 0, 2 }, { 0, 29, 0 } } }, { { { 15, 0, 1 }, { 0, 30, 1 } } }, { { { 15, 0, 0 }, { 0, 30, 0 } } }, { { { 15, 0, 1 }, { 0, 31, 1 } } }, { { { 15, 0, 2 }, { 0, 31, 0 } } }, { { { 16, 0, 2 }, { 1, 31, 1 } } }, { { { 16, 0, 1 }, { 1, 31, 0 } } }, { { { 16, 0, 0 }, { 0, 32, 0 } } }, { { { 16, 0, 1 }, { 2, 31, 0 } } }, { { { 16, 0, 2 }, { 0, 33, 0 } } }, { { { 17, 0, 1 }, { 3, 31, 0 } } }, { { { 17, 0, 0 }, { 0, 34, 0 } } }, { { { 17, 0, 1 }, { 4, 31, 0 } } }, { { { 17, 0, 2 }, { 0, 35, 0 } } }, { { { 18, 0, 1 }, { 5, 31, 0 } } }, { { { 18, 0, 0 }, { 0, 36, 0 } } }, { { { 18, 0, 1 }, { 6, 31, 0 } } }, { { { 18, 0, 2 }, { 0, 37, 0 } } }, { { { 19, 0, 1 }, { 7, 31, 0 } } }, { { { 19, 0, 0 }, { 0, 38, 0 } } }, { { { 19, 0, 1 }, { 8, 31, 0 } } }, { { { 19, 0, 2 }, { 0, 39, 0 } } }, { { { 20, 0, 1 }, { 9, 31, 0 } } }, { { { 20, 0, 0 }, { 0, 40, 0 } } }, { { { 20, 0, 1 }, { 10, 31, 0 } } }, { { { 20, 0, 2 }, { 0, 41, 0 } } }, { { { 21, 0, 1 }, { 11, 31, 0 } } }, { { { 21, 0, 0 }, { 0, 42, 0 } } }, { { { 21, 0, 1 }, { 12, 31, 0 } } }, { { { 21, 0, 2 }, { 0, 43, 0 } } }, { { { 22, 0, 1 }, { 13, 31, 0 } } }, { { { 22, 0, 0 }, { 0, 44, 0 } } }, { { { 22, 0, 1 }, { 14, 31, 0 } } }, { { { 22, 0, 2 }, { 0, 45, 0 } } }, { { { 23, 0, 1 }, { 15, 31, 0 } } }, { { { 23, 0, 0 }, { 0, 46, 0 } } }, { { { 23, 0, 1 }, { 0, 47, 1 } } }, { { { 23, 0, 2 }, { 0, 47, 0 } } }, { { { 24, 0, 1 }, { 0, 48, 1 } } }, { { { 24, 0, 0 }, { 0, 48, 0 } } }, { { { 24, 0, 1 }, { 0, 49, 1 } } }, { { { 24, 0, 2 }, { 0, 49, 0 } } }, { { { 25, 0, 1 }, { 0, 50, 1 } } }, { { { 25, 0, 0 }, { 0, 50, 0 } } }, { { { 25, 0, 1 }, { 0, 51, 1 } } }, { { { 25, 0, 2 }, { 0, 51, 0 } } }, { { { 26, 0, 1 }, { 0, 52, 1 } } }, { { { 26, 0, 0 }, { 0, 52, 0 } } }, { { { 26, 0, 1 }, { 0, 53, 1 } } }, { { { 26, 0, 2 }, { 0, 53, 0 } } }, { { { 27, 0, 1 }, { 0, 54, 1 } } }, { { { 27, 0, 0 }, { 0, 54, 0 } } }, { { { 27, 0, 1 }, { 0, 55, 1 } } }, { { { 27, 0, 2 }, { 0, 55, 0 } } }, { { { 28, 0, 1 }, { 0, 56, 1 } } }, { { { 28, 0, 0 }, { 0, 56, 0 } } }, { { { 28, 0, 1 }, { 0, 57, 1 } } }, { { { 28, 0, 2 }, { 0, 57, 0 } } }, { { { 29, 0, 1 }, { 0, 58, 1 } } }, { { { 29, 0, 0 }, { 0, 58, 0 } } }, { { { 29, 0, 1 }, { 0, 59, 1 } } }, { { { 29, 0, 2 }, { 0, 59, 0 } } }, { { { 30, 0, 1 }, { 0, 60, 1 } } }, { { { 30, 0, 0 }, { 0, 60, 0 } } }, { { { 30, 0, 1 }, { 0, 61, 1 } } }, { { { 30, 0, 2 }, { 0, 61, 0 } } }, { { { 31, 0, 1 }, { 0, 62, 1 } } }, { { { 31, 0, 0 }, { 0, 62, 0 } } }, { { { 31, 0, 1 }, { 0, 63, 1 } } }, { { { 31, 0, 2 }, { 0, 63, 0 } } }, { { { 32, 0, 2 }, { 1, 63, 1 } } }, { { { 32, 0, 1 }, { 1, 63, 0 } } }, { { { 32, 0, 0 }, { 16, 48, 0 } } }, { { { 32, 0, 1 }, { 2, 63, 0 } } }, { { { 32, 0, 2 }, { 16, 49, 0 } } }, { { { 33, 0, 1 }, { 3, 63, 0 } } }, { { { 33, 0, 0 }, { 16, 50, 0 } } }, { { { 33, 0, 1 }, { 4, 63, 0 } } }, { { { 33, 0, 2 }, { 16, 51, 0 } } }, { { { 34, 0, 1 }, { 5, 63, 0 } } }, { { { 34, 0, 0 }, { 16, 52, 0 } } }, { { { 34, 0, 1 }, { 6, 63, 0 } } }, { { { 34, 0, 2 }, { 16, 53, 0 } } }, { { { 35, 0, 1 }, { 7, 63, 0 } } }, { { { 35, 0, 0 }, { 16, 54, 0 } } }, { { { 35, 0, 1 }, { 8, 63, 0 } } }, { { { 35, 0, 2 }, { 16, 55, 0 } } }, { { { 36, 0, 1 }, { 9, 63, 0 } } }, { { { 36, 0, 0 }, { 16, 56, 0 } } }, { { { 36, 0, 1 }, { 10, 63, 0 } } }, { { { 36, 0, 2 }, { 16, 57, 0 } } }, { { { 37, 0, 1 }, { 11, 63, 0 } } }, { { { 37, 0, 0 }, { 16, 58, 0 } } }, { { { 37, 0, 1 }, { 12, 63, 0 } } }, { { { 37, 0, 2 }, { 16, 59, 0 } } }, { { { 38, 0, 1 }, { 13, 63, 0 } } }, { { { 38, 0, 0 }, { 16, 60, 0 } } }, { { { 38, 0, 1 }, { 14, 63, 0 } } }, { { { 38, 0, 2 }, { 16, 61, 0 } } }, { { { 39, 0, 1 }, { 15, 63, 0 } } }, { { { 39, 0, 0 }, { 16, 62, 0 } } }, { { { 39, 0, 1 }, { 16, 63, 1 } } }, { { { 39, 0, 2 }, { 16, 63, 0 } } }, { { { 40, 0, 1 }, { 17, 63, 1 } } }, { { { 40, 0, 0 }, { 17, 63, 0 } } }, { { { 40, 0, 1 }, { 18, 63, 1 } } }, { { { 40, 0, 2 }, { 18, 63, 0 } } }, { { { 41, 0, 1 }, { 19, 63, 1 } } }, { { { 41, 0, 0 }, { 19, 63, 0 } } }, { { { 41, 0, 1 }, { 20, 63, 1 } } }, { { { 41, 0, 2 }, { 20, 63, 0 } } }, { { { 42, 0, 1 }, { 21, 63, 1 } } }, { { { 42, 0, 0 }, { 21, 63, 0 } } }, { { { 42, 0, 1 }, { 22, 63, 1 } } }, { { { 42, 0, 2 }, { 22, 63, 0 } } }, { { { 43, 0, 1 }, { 23, 63, 1 } } }, { { { 43, 0, 0 }, { 23, 63, 0 } } }, { { { 43, 0, 1 }, { 24, 63, 1 } } }, { { { 43, 0, 2 }, { 24, 63, 0 } } }, { { { 44, 0, 1 }, { 25, 63, 1 } } }, { { { 44, 0, 0 }, { 25, 63, 0 } } }, { { { 44, 0, 1 }, { 26, 63, 1 } } }, { { { 44, 0, 2 }, { 26, 63, 0 } } }, { { { 45, 0, 1 }, { 27, 63, 1 } } }, { { { 45, 0, 0 }, { 27, 63, 0 } } }, { { { 45, 0, 1 }, { 28, 63, 1 } } }, { { { 45, 0, 2 }, { 28, 63, 0 } } }, { { { 46, 0, 1 }, { 29, 63, 1 } } }, { { { 46, 0, 0 }, { 29, 63, 0 } } }, { { { 46, 0, 1 }, { 30, 63, 1 } } }, { { { 46, 0, 2 }, { 30, 63, 0 } } }, { { { 47, 0, 1 }, { 31, 63, 1 } } }, { { { 47, 0, 0 }, { 31, 63, 0 } } }, { { { 47, 0, 1 }, { 32, 63, 1 } } }, { { { 47, 0, 2 }, { 32, 63, 0 } } }, { { { 48, 0, 2 }, { 33, 63, 1 } } }, { { { 48, 0, 1 }, { 33, 63, 0 } } }, { { { 48, 0, 0 }, { 48, 48, 0 } } }, { { { 48, 0, 1 }, { 34, 63, 0 } } }, { { { 48, 0, 2 }, { 48, 49, 0 } } }, { { { 49, 0, 1 }, { 35, 63, 0 } } }, { { { 49, 0, 0 }, { 48, 50, 0 } } }, { { { 49, 0, 1 }, { 36, 63, 0 } } }, { { { 49, 0, 2 }, { 48, 51, 0 } } }, { { { 50, 0, 1 }, { 37, 63, 0 } } }, { { { 50, 0, 0 }, { 48, 52, 0 } } }, { { { 50, 0, 1 }, { 38, 63, 0 } } }, { { { 50, 0, 2 }, { 48, 53, 0 } } }, { { { 51, 0, 1 }, { 39, 63, 0 } } }, { { { 51, 0, 0 }, { 48, 54, 0 } } }, { { { 51, 0, 1 }, { 40, 63, 0 } } }, { { { 51, 0, 2 }, { 48, 55, 0 } } }, { { { 52, 0, 1 }, { 41, 63, 0 } } }, { { { 52, 0, 0 }, { 48, 56, 0 } } }, { { { 52, 0, 1 }, { 42, 63, 0 } } }, { { { 52, 0, 2 }, { 48, 57, 0 } } }, { { { 53, 0, 1 }, { 43, 63, 0 } } }, { { { 53, 0, 0 }, { 48, 58, 0 } } }, { { { 53, 0, 1 }, { 44, 63, 0 } } }, { { { 53, 0, 2 }, { 48, 59, 0 } } }, { { { 54, 0, 1 }, { 45, 63, 0 } } }, { { { 54, 0, 0 }, { 48, 60, 0 } } }, { { { 54, 0, 1 }, { 46, 63, 0 } } }, { { { 54, 0, 2 }, { 48, 61, 0 } } }, { { { 55, 0, 1 }, { 47, 63, 0 } } }, { { { 55, 0, 0 }, { 48, 62, 0 } } }, { { { 55, 0, 1 }, { 48, 63, 1 } } }, { { { 55, 0, 2 }, { 48, 63, 0 } } }, { { { 56, 0, 1 }, { 49, 63, 1 } } }, { { { 56, 0, 0 }, { 49, 63, 0 } } }, { { { 56, 0, 1 }, { 50, 63, 1 } } }, { { { 56, 0, 2 }, { 50, 63, 0 } } }, { { { 57, 0, 1 }, { 51, 63, 1 } } }, { { { 57, 0, 0 }, { 51, 63, 0 } } }, { { { 57, 0, 1 }, { 52, 63, 1 } } }, { { { 57, 0, 2 }, { 52, 63, 0 } } }, { { { 58, 0, 1 }, { 53, 63, 1 } } }, { { { 58, 0, 0 }, { 53, 63, 0 } } }, { { { 58, 0, 1 }, { 54, 63, 1 } } }, { { { 58, 0, 2 }, { 54, 63, 0 } } }, { { { 59, 0, 1 }, { 55, 63, 1 } } }, { { { 59, 0, 0 }, { 55, 63, 0 } } }, { { { 59, 0, 1 }, { 56, 63, 1 } } }, { { { 59, 0, 2 }, { 56, 63, 0 } } }, { { { 60, 0, 1 }, { 57, 63, 1 } } }, { { { 60, 0, 0 }, { 57, 63, 0 } } }, { { { 60, 0, 1 }, { 58, 63, 1 } } }, { { { 60, 0, 2 }, { 58, 63, 0 } } }, { { { 61, 0, 1 }, { 59, 63, 1 } } }, { { { 61, 0, 0 }, { 59, 63, 0 } } }, { { { 61, 0, 1 }, { 60, 63, 1 } } }, { { { 61, 0, 2 }, { 60, 63, 0 } } }, { { { 62, 0, 1 }, { 61, 63, 1 } } }, { { { 62, 0, 0 }, { 61, 63, 0 } } }, { { { 62, 0, 1 }, { 62, 63, 1 } } }, { { { 62, 0, 2 }, { 62, 63, 0 } } }, { { { 63, 0, 1 }, { 63, 63, 1 } } }, { { { 63, 0, 0 }, { 63, 63, 0 } } } }; static SingleColourLookup const lookup_5_4[] = { { { { 0, 0, 0 }, { 0, 0, 0 } } }, { { { 0, 0, 1 }, { 0, 1, 1 } } }, { { { 0, 0, 2 }, { 0, 1, 0 } } }, { { { 0, 0, 3 }, { 0, 1, 1 } } }, { { { 0, 0, 4 }, { 0, 2, 1 } } }, { { { 1, 0, 3 }, { 0, 2, 0 } } }, { { { 1, 0, 2 }, { 0, 2, 1 } } }, { { { 1, 0, 1 }, { 0, 3, 1 } } }, { { { 1, 0, 0 }, { 0, 3, 0 } } }, { { { 1, 0, 1 }, { 1, 2, 1 } } }, { { { 1, 0, 2 }, { 1, 2, 0 } } }, { { { 1, 0, 3 }, { 0, 4, 0 } } }, { { { 1, 0, 4 }, { 0, 5, 1 } } }, { { { 2, 0, 3 }, { 0, 5, 0 } } }, { { { 2, 0, 2 }, { 0, 5, 1 } } }, { { { 2, 0, 1 }, { 0, 6, 1 } } }, { { { 2, 0, 0 }, { 0, 6, 0 } } }, { { { 2, 0, 1 }, { 2, 3, 1 } } }, { { { 2, 0, 2 }, { 2, 3, 0 } } }, { { { 2, 0, 3 }, { 0, 7, 0 } } }, { { { 2, 0, 4 }, { 1, 6, 1 } } }, { { { 3, 0, 3 }, { 1, 6, 0 } } }, { { { 3, 0, 2 }, { 0, 8, 0 } } }, { { { 3, 0, 1 }, { 0, 9, 1 } } }, { { { 3, 0, 0 }, { 0, 9, 0 } } }, { { { 3, 0, 1 }, { 0, 9, 1 } } }, { { { 3, 0, 2 }, { 0, 10, 1 } } }, { { { 3, 0, 3 }, { 0, 10, 0 } } }, { { { 3, 0, 4 }, { 2, 7, 1 } } }, { { { 4, 0, 4 }, { 2, 7, 0 } } }, { { { 4, 0, 3 }, { 0, 11, 0 } } }, { { { 4, 0, 2 }, { 1, 10, 1 } } }, { { { 4, 0, 1 }, { 1, 10, 0 } } }, { { { 4, 0, 0 }, { 0, 12, 0 } } }, { { { 4, 0, 1 }, { 0, 13, 1 } } }, { { { 4, 0, 2 }, { 0, 13, 0 } } }, { { { 4, 0, 3 }, { 0, 13, 1 } } }, { { { 4, 0, 4 }, { 0, 14, 1 } } }, { { { 5, 0, 3 }, { 0, 14, 0 } } }, { { { 5, 0, 2 }, { 2, 11, 1 } } }, { { { 5, 0, 1 }, { 2, 11, 0 } } }, { { { 5, 0, 0 }, { 0, 15, 0 } } }, { { { 5, 0, 1 }, { 1, 14, 1 } } }, { { { 5, 0, 2 }, { 1, 14, 0 } } }, { { { 5, 0, 3 }, { 0, 16, 0 } } }, { { { 5, 0, 4 }, { 0, 17, 1 } } }, { { { 6, 0, 3 }, { 0, 17, 0 } } }, { { { 6, 0, 2 }, { 0, 17, 1 } } }, { { { 6, 0, 1 }, { 0, 18, 1 } } }, { { { 6, 0, 0 }, { 0, 18, 0 } } }, { { { 6, 0, 1 }, { 2, 15, 1 } } }, { { { 6, 0, 2 }, { 2, 15, 0 } } }, { { { 6, 0, 3 }, { 0, 19, 0 } } }, { { { 6, 0, 4 }, { 1, 18, 1 } } }, { { { 7, 0, 3 }, { 1, 18, 0 } } }, { { { 7, 0, 2 }, { 0, 20, 0 } } }, { { { 7, 0, 1 }, { 0, 21, 1 } } }, { { { 7, 0, 0 }, { 0, 21, 0 } } }, { { { 7, 0, 1 }, { 0, 21, 1 } } }, { { { 7, 0, 2 }, { 0, 22, 1 } } }, { { { 7, 0, 3 }, { 0, 22, 0 } } }, { { { 7, 0, 4 }, { 2, 19, 1 } } }, { { { 8, 0, 4 }, { 2, 19, 0 } } }, { { { 8, 0, 3 }, { 0, 23, 0 } } }, { { { 8, 0, 2 }, { 1, 22, 1 } } }, { { { 8, 0, 1 }, { 1, 22, 0 } } }, { { { 8, 0, 0 }, { 0, 24, 0 } } }, { { { 8, 0, 1 }, { 0, 25, 1 } } }, { { { 8, 0, 2 }, { 0, 25, 0 } } }, { { { 8, 0, 3 }, { 0, 25, 1 } } }, { { { 8, 0, 4 }, { 0, 26, 1 } } }, { { { 9, 0, 3 }, { 0, 26, 0 } } }, { { { 9, 0, 2 }, { 2, 23, 1 } } }, { { { 9, 0, 1 }, { 2, 23, 0 } } }, { { { 9, 0, 0 }, { 0, 27, 0 } } }, { { { 9, 0, 1 }, { 1, 26, 1 } } }, { { { 9, 0, 2 }, { 1, 26, 0 } } }, { { { 9, 0, 3 }, { 0, 28, 0 } } }, { { { 9, 0, 4 }, { 0, 29, 1 } } }, { { { 10, 0, 3 }, { 0, 29, 0 } } }, { { { 10, 0, 2 }, { 0, 29, 1 } } }, { { { 10, 0, 1 }, { 0, 30, 1 } } }, { { { 10, 0, 0 }, { 0, 30, 0 } } }, { { { 10, 0, 1 }, { 2, 27, 1 } } }, { { { 10, 0, 2 }, { 2, 27, 0 } } }, { { { 10, 0, 3 }, { 0, 31, 0 } } }, { { { 10, 0, 4 }, { 1, 30, 1 } } }, { { { 11, 0, 3 }, { 1, 30, 0 } } }, { { { 11, 0, 2 }, { 4, 24, 0 } } }, { { { 11, 0, 1 }, { 1, 31, 1 } } }, { { { 11, 0, 0 }, { 1, 31, 0 } } }, { { { 11, 0, 1 }, { 1, 31, 1 } } }, { { { 11, 0, 2 }, { 2, 30, 1 } } }, { { { 11, 0, 3 }, { 2, 30, 0 } } }, { { { 11, 0, 4 }, { 2, 31, 1 } } }, { { { 12, 0, 4 }, { 2, 31, 0 } } }, { { { 12, 0, 3 }, { 4, 27, 0 } } }, { { { 12, 0, 2 }, { 3, 30, 1 } } }, { { { 12, 0, 1 }, { 3, 30, 0 } } }, { { { 12, 0, 0 }, { 4, 28, 0 } } }, { { { 12, 0, 1 }, { 3, 31, 1 } } }, { { { 12, 0, 2 }, { 3, 31, 0 } } }, { { { 12, 0, 3 }, { 3, 31, 1 } } }, { { { 12, 0, 4 }, { 4, 30, 1 } } }, { { { 13, 0, 3 }, { 4, 30, 0 } } }, { { { 13, 0, 2 }, { 6, 27, 1 } } }, { { { 13, 0, 1 }, { 6, 27, 0 } } }, { { { 13, 0, 0 }, { 4, 31, 0 } } }, { { { 13, 0, 1 }, { 5, 30, 1 } } }, { { { 13, 0, 2 }, { 5, 30, 0 } } }, { { { 13, 0, 3 }, { 8, 24, 0 } } }, { { { 13, 0, 4 }, { 5, 31, 1 } } }, { { { 14, 0, 3 }, { 5, 31, 0 } } }, { { { 14, 0, 2 }, { 5, 31, 1 } } }, { { { 14, 0, 1 }, { 6, 30, 1 } } }, { { { 14, 0, 0 }, { 6, 30, 0 } } }, { { { 14, 0, 1 }, { 6, 31, 1 } } }, { { { 14, 0, 2 }, { 6, 31, 0 } } }, { { { 14, 0, 3 }, { 8, 27, 0 } } }, { { { 14, 0, 4 }, { 7, 30, 1 } } }, { { { 15, 0, 3 }, { 7, 30, 0 } } }, { { { 15, 0, 2 }, { 8, 28, 0 } } }, { { { 15, 0, 1 }, { 7, 31, 1 } } }, { { { 15, 0, 0 }, { 7, 31, 0 } } }, { { { 15, 0, 1 }, { 7, 31, 1 } } }, { { { 15, 0, 2 }, { 8, 30, 1 } } }, { { { 15, 0, 3 }, { 8, 30, 0 } } }, { { { 15, 0, 4 }, { 10, 27, 1 } } }, { { { 16, 0, 4 }, { 10, 27, 0 } } }, { { { 16, 0, 3 }, { 8, 31, 0 } } }, { { { 16, 0, 2 }, { 9, 30, 1 } } }, { { { 16, 0, 1 }, { 9, 30, 0 } } }, { { { 16, 0, 0 }, { 12, 24, 0 } } }, { { { 16, 0, 1 }, { 9, 31, 1 } } }, { { { 16, 0, 2 }, { 9, 31, 0 } } }, { { { 16, 0, 3 }, { 9, 31, 1 } } }, { { { 16, 0, 4 }, { 10, 30, 1 } } }, { { { 17, 0, 3 }, { 10, 30, 0 } } }, { { { 17, 0, 2 }, { 10, 31, 1 } } }, { { { 17, 0, 1 }, { 10, 31, 0 } } }, { { { 17, 0, 0 }, { 12, 27, 0 } } }, { { { 17, 0, 1 }, { 11, 30, 1 } } }, { { { 17, 0, 2 }, { 11, 30, 0 } } }, { { { 17, 0, 3 }, { 12, 28, 0 } } }, { { { 17, 0, 4 }, { 11, 31, 1 } } }, { { { 18, 0, 3 }, { 11, 31, 0 } } }, { { { 18, 0, 2 }, { 11, 31, 1 } } }, { { { 18, 0, 1 }, { 12, 30, 1 } } }, { { { 18, 0, 0 }, { 12, 30, 0 } } }, { { { 18, 0, 1 }, { 14, 27, 1 } } }, { { { 18, 0, 2 }, { 14, 27, 0 } } }, { { { 18, 0, 3 }, { 12, 31, 0 } } }, { { { 18, 0, 4 }, { 13, 30, 1 } } }, { { { 19, 0, 3 }, { 13, 30, 0 } } }, { { { 19, 0, 2 }, { 16, 24, 0 } } }, { { { 19, 0, 1 }, { 13, 31, 1 } } }, { { { 19, 0, 0 }, { 13, 31, 0 } } }, { { { 19, 0, 1 }, { 13, 31, 1 } } }, { { { 19, 0, 2 }, { 14, 30, 1 } } }, { { { 19, 0, 3 }, { 14, 30, 0 } } }, { { { 19, 0, 4 }, { 14, 31, 1 } } }, { { { 20, 0, 4 }, { 14, 31, 0 } } }, { { { 20, 0, 3 }, { 16, 27, 0 } } }, { { { 20, 0, 2 }, { 15, 30, 1 } } }, { { { 20, 0, 1 }, { 15, 30, 0 } } }, { { { 20, 0, 0 }, { 16, 28, 0 } } }, { { { 20, 0, 1 }, { 15, 31, 1 } } }, { { { 20, 0, 2 }, { 15, 31, 0 } } }, { { { 20, 0, 3 }, { 15, 31, 1 } } }, { { { 20, 0, 4 }, { 16, 30, 1 } } }, { { { 21, 0, 3 }, { 16, 30, 0 } } }, { { { 21, 0, 2 }, { 18, 27, 1 } } }, { { { 21, 0, 1 }, { 18, 27, 0 } } }, { { { 21, 0, 0 }, { 16, 31, 0 } } }, { { { 21, 0, 1 }, { 17, 30, 1 } } }, { { { 21, 0, 2 }, { 17, 30, 0 } } }, { { { 21, 0, 3 }, { 20, 24, 0 } } }, { { { 21, 0, 4 }, { 17, 31, 1 } } }, { { { 22, 0, 3 }, { 17, 31, 0 } } }, { { { 22, 0, 2 }, { 17, 31, 1 } } }, { { { 22, 0, 1 }, { 18, 30, 1 } } }, { { { 22, 0, 0 }, { 18, 30, 0 } } }, { { { 22, 0, 1 }, { 18, 31, 1 } } }, { { { 22, 0, 2 }, { 18, 31, 0 } } }, { { { 22, 0, 3 }, { 20, 27, 0 } } }, { { { 22, 0, 4 }, { 19, 30, 1 } } }, { { { 23, 0, 3 }, { 19, 30, 0 } } }, { { { 23, 0, 2 }, { 20, 28, 0 } } }, { { { 23, 0, 1 }, { 19, 31, 1 } } }, { { { 23, 0, 0 }, { 19, 31, 0 } } }, { { { 23, 0, 1 }, { 19, 31, 1 } } }, { { { 23, 0, 2 }, { 20, 30, 1 } } }, { { { 23, 0, 3 }, { 20, 30, 0 } } }, { { { 23, 0, 4 }, { 22, 27, 1 } } }, { { { 24, 0, 4 }, { 22, 27, 0 } } }, { { { 24, 0, 3 }, { 20, 31, 0 } } }, { { { 24, 0, 2 }, { 21, 30, 1 } } }, { { { 24, 0, 1 }, { 21, 30, 0 } } }, { { { 24, 0, 0 }, { 24, 24, 0 } } }, { { { 24, 0, 1 }, { 21, 31, 1 } } }, { { { 24, 0, 2 }, { 21, 31, 0 } } }, { { { 24, 0, 3 }, { 21, 31, 1 } } }, { { { 24, 0, 4 }, { 22, 30, 1 } } }, { { { 25, 0, 3 }, { 22, 30, 0 } } }, { { { 25, 0, 2 }, { 22, 31, 1 } } }, { { { 25, 0, 1 }, { 22, 31, 0 } } }, { { { 25, 0, 0 }, { 24, 27, 0 } } }, { { { 25, 0, 1 }, { 23, 30, 1 } } }, { { { 25, 0, 2 }, { 23, 30, 0 } } }, { { { 25, 0, 3 }, { 24, 28, 0 } } }, { { { 25, 0, 4 }, { 23, 31, 1 } } }, { { { 26, 0, 3 }, { 23, 31, 0 } } }, { { { 26, 0, 2 }, { 23, 31, 1 } } }, { { { 26, 0, 1 }, { 24, 30, 1 } } }, { { { 26, 0, 0 }, { 24, 30, 0 } } }, { { { 26, 0, 1 }, { 26, 27, 1 } } }, { { { 26, 0, 2 }, { 26, 27, 0 } } }, { { { 26, 0, 3 }, { 24, 31, 0 } } }, { { { 26, 0, 4 }, { 25, 30, 1 } } }, { { { 27, 0, 3 }, { 25, 30, 0 } } }, { { { 27, 0, 2 }, { 28, 24, 0 } } }, { { { 27, 0, 1 }, { 25, 31, 1 } } }, { { { 27, 0, 0 }, { 25, 31, 0 } } }, { { { 27, 0, 1 }, { 25, 31, 1 } } }, { { { 27, 0, 2 }, { 26, 30, 1 } } }, { { { 27, 0, 3 }, { 26, 30, 0 } } }, { { { 27, 0, 4 }, { 26, 31, 1 } } }, { { { 28, 0, 4 }, { 26, 31, 0 } } }, { { { 28, 0, 3 }, { 28, 27, 0 } } }, { { { 28, 0, 2 }, { 27, 30, 1 } } }, { { { 28, 0, 1 }, { 27, 30, 0 } } }, { { { 28, 0, 0 }, { 28, 28, 0 } } }, { { { 28, 0, 1 }, { 27, 31, 1 } } }, { { { 28, 0, 2 }, { 27, 31, 0 } } }, { { { 28, 0, 3 }, { 27, 31, 1 } } }, { { { 28, 0, 4 }, { 28, 30, 1 } } }, { { { 29, 0, 3 }, { 28, 30, 0 } } }, { { { 29, 0, 2 }, { 30, 27, 1 } } }, { { { 29, 0, 1 }, { 30, 27, 0 } } }, { { { 29, 0, 0 }, { 28, 31, 0 } } }, { { { 29, 0, 1 }, { 29, 30, 1 } } }, { { { 29, 0, 2 }, { 29, 30, 0 } } }, { { { 29, 0, 3 }, { 29, 30, 1 } } }, { { { 29, 0, 4 }, { 29, 31, 1 } } }, { { { 30, 0, 3 }, { 29, 31, 0 } } }, { { { 30, 0, 2 }, { 29, 31, 1 } } }, { { { 30, 0, 1 }, { 30, 30, 1 } } }, { { { 30, 0, 0 }, { 30, 30, 0 } } }, { { { 30, 0, 1 }, { 30, 31, 1 } } }, { { { 30, 0, 2 }, { 30, 31, 0 } } }, { { { 30, 0, 3 }, { 30, 31, 1 } } }, { { { 30, 0, 4 }, { 31, 30, 1 } } }, { { { 31, 0, 3 }, { 31, 30, 0 } } }, { { { 31, 0, 2 }, { 31, 30, 1 } } }, { { { 31, 0, 1 }, { 31, 31, 1 } } }, { { { 31, 0, 0 }, { 31, 31, 0 } } } }; static SingleColourLookup const lookup_6_4[] = { { { { 0, 0, 0 }, { 0, 0, 0 } } }, { { { 0, 0, 1 }, { 0, 1, 0 } } }, { { { 0, 0, 2 }, { 0, 2, 0 } } }, { { { 1, 0, 1 }, { 0, 3, 1 } } }, { { { 1, 0, 0 }, { 0, 3, 0 } } }, { { { 1, 0, 1 }, { 0, 4, 0 } } }, { { { 1, 0, 2 }, { 0, 5, 0 } } }, { { { 2, 0, 1 }, { 0, 6, 1 } } }, { { { 2, 0, 0 }, { 0, 6, 0 } } }, { { { 2, 0, 1 }, { 0, 7, 0 } } }, { { { 2, 0, 2 }, { 0, 8, 0 } } }, { { { 3, 0, 1 }, { 0, 9, 1 } } }, { { { 3, 0, 0 }, { 0, 9, 0 } } }, { { { 3, 0, 1 }, { 0, 10, 0 } } }, { { { 3, 0, 2 }, { 0, 11, 0 } } }, { { { 4, 0, 1 }, { 0, 12, 1 } } }, { { { 4, 0, 0 }, { 0, 12, 0 } } }, { { { 4, 0, 1 }, { 0, 13, 0 } } }, { { { 4, 0, 2 }, { 0, 14, 0 } } }, { { { 5, 0, 1 }, { 0, 15, 1 } } }, { { { 5, 0, 0 }, { 0, 15, 0 } } }, { { { 5, 0, 1 }, { 0, 16, 0 } } }, { { { 5, 0, 2 }, { 1, 15, 0 } } }, { { { 6, 0, 1 }, { 0, 17, 0 } } }, { { { 6, 0, 0 }, { 0, 18, 0 } } }, { { { 6, 0, 1 }, { 0, 19, 0 } } }, { { { 6, 0, 2 }, { 3, 14, 0 } } }, { { { 7, 0, 1 }, { 0, 20, 0 } } }, { { { 7, 0, 0 }, { 0, 21, 0 } } }, { { { 7, 0, 1 }, { 0, 22, 0 } } }, { { { 7, 0, 2 }, { 4, 15, 0 } } }, { { { 8, 0, 1 }, { 0, 23, 0 } } }, { { { 8, 0, 0 }, { 0, 24, 0 } } }, { { { 8, 0, 1 }, { 0, 25, 0 } } }, { { { 8, 0, 2 }, { 6, 14, 0 } } }, { { { 9, 0, 1 }, { 0, 26, 0 } } }, { { { 9, 0, 0 }, { 0, 27, 0 } } }, { { { 9, 0, 1 }, { 0, 28, 0 } } }, { { { 9, 0, 2 }, { 7, 15, 0 } } }, { { { 10, 0, 1 }, { 0, 29, 0 } } }, { { { 10, 0, 0 }, { 0, 30, 0 } } }, { { { 10, 0, 1 }, { 0, 31, 0 } } }, { { { 10, 0, 2 }, { 9, 14, 0 } } }, { { { 11, 0, 1 }, { 0, 32, 0 } } }, { { { 11, 0, 0 }, { 0, 33, 0 } } }, { { { 11, 0, 1 }, { 2, 30, 0 } } }, { { { 11, 0, 2 }, { 0, 34, 0 } } }, { { { 12, 0, 1 }, { 0, 35, 0 } } }, { { { 12, 0, 0 }, { 0, 36, 0 } } }, { { { 12, 0, 1 }, { 3, 31, 0 } } }, { { { 12, 0, 2 }, { 0, 37, 0 } } }, { { { 13, 0, 1 }, { 0, 38, 0 } } }, { { { 13, 0, 0 }, { 0, 39, 0 } } }, { { { 13, 0, 1 }, { 5, 30, 0 } } }, { { { 13, 0, 2 }, { 0, 40, 0 } } }, { { { 14, 0, 1 }, { 0, 41, 0 } } }, { { { 14, 0, 0 }, { 0, 42, 0 } } }, { { { 14, 0, 1 }, { 6, 31, 0 } } }, { { { 14, 0, 2 }, { 0, 43, 0 } } }, { { { 15, 0, 1 }, { 0, 44, 0 } } }, { { { 15, 0, 0 }, { 0, 45, 0 } } }, { { { 15, 0, 1 }, { 8, 30, 0 } } }, { { { 15, 0, 2 }, { 0, 46, 0 } } }, { { { 16, 0, 2 }, { 0, 47, 0 } } }, { { { 16, 0, 1 }, { 1, 46, 0 } } }, { { { 16, 0, 0 }, { 0, 48, 0 } } }, { { { 16, 0, 1 }, { 0, 49, 0 } } }, { { { 16, 0, 2 }, { 0, 50, 0 } } }, { { { 17, 0, 1 }, { 2, 47, 0 } } }, { { { 17, 0, 0 }, { 0, 51, 0 } } }, { { { 17, 0, 1 }, { 0, 52, 0 } } }, { { { 17, 0, 2 }, { 0, 53, 0 } } }, { { { 18, 0, 1 }, { 4, 46, 0 } } }, { { { 18, 0, 0 }, { 0, 54, 0 } } }, { { { 18, 0, 1 }, { 0, 55, 0 } } }, { { { 18, 0, 2 }, { 0, 56, 0 } } }, { { { 19, 0, 1 }, { 5, 47, 0 } } }, { { { 19, 0, 0 }, { 0, 57, 0 } } }, { { { 19, 0, 1 }, { 0, 58, 0 } } }, { { { 19, 0, 2 }, { 0, 59, 0 } } }, { { { 20, 0, 1 }, { 7, 46, 0 } } }, { { { 20, 0, 0 }, { 0, 60, 0 } } }, { { { 20, 0, 1 }, { 0, 61, 0 } } }, { { { 20, 0, 2 }, { 0, 62, 0 } } }, { { { 21, 0, 1 }, { 8, 47, 0 } } }, { { { 21, 0, 0 }, { 0, 63, 0 } } }, { { { 21, 0, 1 }, { 1, 62, 0 } } }, { { { 21, 0, 2 }, { 1, 63, 0 } } }, { { { 22, 0, 1 }, { 10, 46, 0 } } }, { { { 22, 0, 0 }, { 2, 62, 0 } } }, { { { 22, 0, 1 }, { 2, 63, 0 } } }, { { { 22, 0, 2 }, { 3, 62, 0 } } }, { { { 23, 0, 1 }, { 11, 47, 0 } } }, { { { 23, 0, 0 }, { 3, 63, 0 } } }, { { { 23, 0, 1 }, { 4, 62, 0 } } }, { { { 23, 0, 2 }, { 4, 63, 0 } } }, { { { 24, 0, 1 }, { 13, 46, 0 } } }, { { { 24, 0, 0 }, { 5, 62, 0 } } }, { { { 24, 0, 1 }, { 5, 63, 0 } } }, { { { 24, 0, 2 }, { 6, 62, 0 } } }, { { { 25, 0, 1 }, { 14, 47, 0 } } }, { { { 25, 0, 0 }, { 6, 63, 0 } } }, { { { 25, 0, 1 }, { 7, 62, 0 } } }, { { { 25, 0, 2 }, { 7, 63, 0 } } }, { { { 26, 0, 1 }, { 16, 45, 0 } } }, { { { 26, 0, 0 }, { 8, 62, 0 } } }, { { { 26, 0, 1 }, { 8, 63, 0 } } }, { { { 26, 0, 2 }, { 9, 62, 0 } } }, { { { 27, 0, 1 }, { 16, 48, 0 } } }, { { { 27, 0, 0 }, { 9, 63, 0 } } }, { { { 27, 0, 1 }, { 10, 62, 0 } } }, { { { 27, 0, 2 }, { 10, 63, 0 } } }, { { { 28, 0, 1 }, { 16, 51, 0 } } }, { { { 28, 0, 0 }, { 11, 62, 0 } } }, { { { 28, 0, 1 }, { 11, 63, 0 } } }, { { { 28, 0, 2 }, { 12, 62, 0 } } }, { { { 29, 0, 1 }, { 16, 54, 0 } } }, { { { 29, 0, 0 }, { 12, 63, 0 } } }, { { { 29, 0, 1 }, { 13, 62, 0 } } }, { { { 29, 0, 2 }, { 13, 63, 0 } } }, { { { 30, 0, 1 }, { 16, 57, 0 } } }, { { { 30, 0, 0 }, { 14, 62, 0 } } }, { { { 30, 0, 1 }, { 14, 63, 0 } } }, { { { 30, 0, 2 }, { 15, 62, 0 } } }, { { { 31, 0, 1 }, { 16, 60, 0 } } }, { { { 31, 0, 0 }, { 15, 63, 0 } } }, { { { 31, 0, 1 }, { 24, 46, 0 } } }, { { { 31, 0, 2 }, { 16, 62, 0 } } }, { { { 32, 0, 2 }, { 16, 63, 0 } } }, { { { 32, 0, 1 }, { 17, 62, 0 } } }, { { { 32, 0, 0 }, { 25, 47, 0 } } }, { { { 32, 0, 1 }, { 17, 63, 0 } } }, { { { 32, 0, 2 }, { 18, 62, 0 } } }, { { { 33, 0, 1 }, { 18, 63, 0 } } }, { { { 33, 0, 0 }, { 27, 46, 0 } } }, { { { 33, 0, 1 }, { 19, 62, 0 } } }, { { { 33, 0, 2 }, { 19, 63, 0 } } }, { { { 34, 0, 1 }, { 20, 62, 0 } } }, { { { 34, 0, 0 }, { 28, 47, 0 } } }, { { { 34, 0, 1 }, { 20, 63, 0 } } }, { { { 34, 0, 2 }, { 21, 62, 0 } } }, { { { 35, 0, 1 }, { 21, 63, 0 } } }, { { { 35, 0, 0 }, { 30, 46, 0 } } }, { { { 35, 0, 1 }, { 22, 62, 0 } } }, { { { 35, 0, 2 }, { 22, 63, 0 } } }, { { { 36, 0, 1 }, { 23, 62, 0 } } }, { { { 36, 0, 0 }, { 31, 47, 0 } } }, { { { 36, 0, 1 }, { 23, 63, 0 } } }, { { { 36, 0, 2 }, { 24, 62, 0 } } }, { { { 37, 0, 1 }, { 24, 63, 0 } } }, { { { 37, 0, 0 }, { 32, 47, 0 } } }, { { { 37, 0, 1 }, { 25, 62, 0 } } }, { { { 37, 0, 2 }, { 25, 63, 0 } } }, { { { 38, 0, 1 }, { 26, 62, 0 } } }, { { { 38, 0, 0 }, { 32, 50, 0 } } }, { { { 38, 0, 1 }, { 26, 63, 0 } } }, { { { 38, 0, 2 }, { 27, 62, 0 } } }, { { { 39, 0, 1 }, { 27, 63, 0 } } }, { { { 39, 0, 0 }, { 32, 53, 0 } } }, { { { 39, 0, 1 }, { 28, 62, 0 } } }, { { { 39, 0, 2 }, { 28, 63, 0 } } }, { { { 40, 0, 1 }, { 29, 62, 0 } } }, { { { 40, 0, 0 }, { 32, 56, 0 } } }, { { { 40, 0, 1 }, { 29, 63, 0 } } }, { { { 40, 0, 2 }, { 30, 62, 0 } } }, { { { 41, 0, 1 }, { 30, 63, 0 } } }, { { { 41, 0, 0 }, { 32, 59, 0 } } }, { { { 41, 0, 1 }, { 31, 62, 0 } } }, { { { 41, 0, 2 }, { 31, 63, 0 } } }, { { { 42, 0, 1 }, { 32, 61, 0 } } }, { { { 42, 0, 0 }, { 32, 62, 0 } } }, { { { 42, 0, 1 }, { 32, 63, 0 } } }, { { { 42, 0, 2 }, { 41, 46, 0 } } }, { { { 43, 0, 1 }, { 33, 62, 0 } } }, { { { 43, 0, 0 }, { 33, 63, 0 } } }, { { { 43, 0, 1 }, { 34, 62, 0 } } }, { { { 43, 0, 2 }, { 42, 47, 0 } } }, { { { 44, 0, 1 }, { 34, 63, 0 } } }, { { { 44, 0, 0 }, { 35, 62, 0 } } }, { { { 44, 0, 1 }, { 35, 63, 0 } } }, { { { 44, 0, 2 }, { 44, 46, 0 } } }, { { { 45, 0, 1 }, { 36, 62, 0 } } }, { { { 45, 0, 0 }, { 36, 63, 0 } } }, { { { 45, 0, 1 }, { 37, 62, 0 } } }, { { { 45, 0, 2 }, { 45, 47, 0 } } }, { { { 46, 0, 1 }, { 37, 63, 0 } } }, { { { 46, 0, 0 }, { 38, 62, 0 } } }, { { { 46, 0, 1 }, { 38, 63, 0 } } }, { { { 46, 0, 2 }, { 47, 46, 0 } } }, { { { 47, 0, 1 }, { 39, 62, 0 } } }, { { { 47, 0, 0 }, { 39, 63, 0 } } }, { { { 47, 0, 1 }, { 40, 62, 0 } } }, { { { 47, 0, 2 }, { 48, 46, 0 } } }, { { { 48, 0, 2 }, { 40, 63, 0 } } }, { { { 48, 0, 1 }, { 41, 62, 0 } } }, { { { 48, 0, 0 }, { 41, 63, 0 } } }, { { { 48, 0, 1 }, { 48, 49, 0 } } }, { { { 48, 0, 2 }, { 42, 62, 0 } } }, { { { 49, 0, 1 }, { 42, 63, 0 } } }, { { { 49, 0, 0 }, { 43, 62, 0 } } }, { { { 49, 0, 1 }, { 48, 52, 0 } } }, { { { 49, 0, 2 }, { 43, 63, 0 } } }, { { { 50, 0, 1 }, { 44, 62, 0 } } }, { { { 50, 0, 0 }, { 44, 63, 0 } } }, { { { 50, 0, 1 }, { 48, 55, 0 } } }, { { { 50, 0, 2 }, { 45, 62, 0 } } }, { { { 51, 0, 1 }, { 45, 63, 0 } } }, { { { 51, 0, 0 }, { 46, 62, 0 } } }, { { { 51, 0, 1 }, { 48, 58, 0 } } }, { { { 51, 0, 2 }, { 46, 63, 0 } } }, { { { 52, 0, 1 }, { 47, 62, 0 } } }, { { { 52, 0, 0 }, { 47, 63, 0 } } }, { { { 52, 0, 1 }, { 48, 61, 0 } } }, { { { 52, 0, 2 }, { 48, 62, 0 } } }, { { { 53, 0, 1 }, { 56, 47, 0 } } }, { { { 53, 0, 0 }, { 48, 63, 0 } } }, { { { 53, 0, 1 }, { 49, 62, 0 } } }, { { { 53, 0, 2 }, { 49, 63, 0 } } }, { { { 54, 0, 1 }, { 58, 46, 0 } } }, { { { 54, 0, 0 }, { 50, 62, 0 } } }, { { { 54, 0, 1 }, { 50, 63, 0 } } }, { { { 54, 0, 2 }, { 51, 62, 0 } } }, { { { 55, 0, 1 }, { 59, 47, 0 } } }, { { { 55, 0, 0 }, { 51, 63, 0 } } }, { { { 55, 0, 1 }, { 52, 62, 0 } } }, { { { 55, 0, 2 }, { 52, 63, 0 } } }, { { { 56, 0, 1 }, { 61, 46, 0 } } }, { { { 56, 0, 0 }, { 53, 62, 0 } } }, { { { 56, 0, 1 }, { 53, 63, 0 } } }, { { { 56, 0, 2 }, { 54, 62, 0 } } }, { { { 57, 0, 1 }, { 62, 47, 0 } } }, { { { 57, 0, 0 }, { 54, 63, 0 } } }, { { { 57, 0, 1 }, { 55, 62, 0 } } }, { { { 57, 0, 2 }, { 55, 63, 0 } } }, { { { 58, 0, 1 }, { 56, 62, 1 } } }, { { { 58, 0, 0 }, { 56, 62, 0 } } }, { { { 58, 0, 1 }, { 56, 63, 0 } } }, { { { 58, 0, 2 }, { 57, 62, 0 } } }, { { { 59, 0, 1 }, { 57, 63, 1 } } }, { { { 59, 0, 0 }, { 57, 63, 0 } } }, { { { 59, 0, 1 }, { 58, 62, 0 } } }, { { { 59, 0, 2 }, { 58, 63, 0 } } }, { { { 60, 0, 1 }, { 59, 62, 1 } } }, { { { 60, 0, 0 }, { 59, 62, 0 } } }, { { { 60, 0, 1 }, { 59, 63, 0 } } }, { { { 60, 0, 2 }, { 60, 62, 0 } } }, { { { 61, 0, 1 }, { 60, 63, 1 } } }, { { { 61, 0, 0 }, { 60, 63, 0 } } }, { { { 61, 0, 1 }, { 61, 62, 0 } } }, { { { 61, 0, 2 }, { 61, 63, 0 } } }, { { { 62, 0, 1 }, { 62, 62, 1 } } }, { { { 62, 0, 0 }, { 62, 62, 0 } } }, { { { 62, 0, 1 }, { 62, 63, 0 } } }, { { { 62, 0, 2 }, { 63, 62, 0 } } }, { { { 63, 0, 1 }, { 63, 63, 1 } } }, { { { 63, 0, 0 }, { 63, 63, 0 } } } }; openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/simd_float.h0000644000175000017500000000771113151711064023113 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #ifndef SQUISH_SIMD_FLOAT_H #define SQUISH_SIMD_FLOAT_H #include namespace squish { #define VEC4_CONST( X ) Vec4( X ) class Vec4 { public: typedef Vec4 const& Arg; Vec4() {} explicit Vec4( float s ) : m_x( s ), m_y( s ), m_z( s ), m_w( s ) { } Vec4( float x, float y, float z, float w ) : m_x( x ), m_y( y ), m_z( z ), m_w( w ) { } Vec3 GetVec3() const { return Vec3( m_x, m_y, m_z ); } Vec4 SplatX() const { return Vec4( m_x ); } Vec4 SplatY() const { return Vec4( m_y ); } Vec4 SplatZ() const { return Vec4( m_z ); } Vec4 SplatW() const { return Vec4( m_w ); } Vec4& operator+=( Arg v ) { m_x += v.m_x; m_y += v.m_y; m_z += v.m_z; m_w += v.m_w; return *this; } Vec4& operator-=( Arg v ) { m_x -= v.m_x; m_y -= v.m_y; m_z -= v.m_z; m_w -= v.m_w; return *this; } Vec4& operator*=( Arg v ) { m_x *= v.m_x; m_y *= v.m_y; m_z *= v.m_z; m_w *= v.m_w; return *this; } friend Vec4 operator+( Vec4::Arg left, Vec4::Arg right ) { Vec4 copy( left ); return copy += right; } friend Vec4 operator-( Vec4::Arg left, Vec4::Arg right ) { Vec4 copy( left ); return copy -= right; } friend Vec4 operator*( Vec4::Arg left, Vec4::Arg right ) { Vec4 copy( left ); return copy *= right; } //! Returns a*b + c friend Vec4 MultiplyAdd( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c ) { return a*b + c; } //! Returns -( a*b - c ) friend Vec4 NegativeMultiplySubtract( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c ) { return c - a*b; } friend Vec4 Reciprocal( Vec4::Arg v ) { return Vec4( 1.0f/v.m_x, 1.0f/v.m_y, 1.0f/v.m_z, 1.0f/v.m_w ); } friend Vec4 Min( Vec4::Arg left, Vec4::Arg right ) { return Vec4( std::min( left.m_x, right.m_x ), std::min( left.m_y, right.m_y ), std::min( left.m_z, right.m_z ), std::min( left.m_w, right.m_w ) ); } friend Vec4 Max( Vec4::Arg left, Vec4::Arg right ) { return Vec4( std::max( left.m_x, right.m_x ), std::max( left.m_y, right.m_y ), std::max( left.m_z, right.m_z ), std::max( left.m_w, right.m_w ) ); } friend Vec4 Truncate( Vec4::Arg v ) { return Vec4( v.m_x > 0.0f ? std::floor( v.m_x ) : std::ceil( v.m_x ), v.m_y > 0.0f ? std::floor( v.m_y ) : std::ceil( v.m_y ), v.m_z > 0.0f ? std::floor( v.m_z ) : std::ceil( v.m_z ), v.m_w > 0.0f ? std::floor( v.m_w ) : std::ceil( v.m_w ) ); } friend bool CompareAnyLessThan( Vec4::Arg left, Vec4::Arg right ) { return left.m_x < right.m_x || left.m_y < right.m_y || left.m_z < right.m_z || left.m_w < right.m_w; } private: float m_x; float m_y; float m_z; float m_w; }; } // namespace squish #endif // ndef SQUISH_SIMD_FLOAT_H openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/colourset.h0000644000175000017500000000364613151711064023014 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #ifndef SQUISH_COLOURSET_H #define SQUISH_COLOURSET_H #include "squish.h" #include "maths.h" namespace squish { /*! @brief Represents a set of block colours */ class ColourSet { public: ColourSet( u8 const* rgba, int mask, int flags ); int GetCount() const { return m_count; } Vec3 const* GetPoints() const { return m_points; } float const* GetWeights() const { return m_weights; } bool IsTransparent() const { return m_transparent; } void RemapIndices( u8 const* source, u8* target ) const; private: int m_count; Vec3 m_points[16]; float m_weights[16]; int m_remap[16]; bool m_transparent; }; } // namespace sqish #endif // ndef SQUISH_COLOURSET_H openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/clusterfit.h0000644000175000017500000000376513151711064023163 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Copyright (c) 2007 Ignacio Castano icastano@nvidia.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #ifndef SQUISH_CLUSTERFIT_H #define SQUISH_CLUSTERFIT_H #include "squish.h" #include "maths.h" #include "simd.h" #include "colourfit.h" namespace squish { class ClusterFit : public ColourFit { public: ClusterFit( ColourSet const* colours, int flags, float* metric ); private: bool ConstructOrdering( Vec3 const& axis, int iteration ); virtual void Compress3( void* block ); virtual void Compress4( void* block ); enum { kMaxIterations = 8 }; int m_iterationCount; Vec3 m_principle; u8 m_order[16*kMaxIterations]; Vec4 m_points_weights[16]; Vec4 m_xsum_wsum; Vec4 m_metric; Vec4 m_besterror; }; } // namespace squish #endif // ndef SQUISH_CLUSTERFIT_H openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/README0000644000175000017500000000272213151711064021476 0ustar mfvmfvLICENSE ------- The squish library is distributed under the terms and conditions of the MIT license. This license is specified at the top of each source file and must be preserved in its entirety. BUILDING AND INSTALLING THE LIBRARY ----------------------------------- If you are using Visual Studio 2003 or above under Windows then load the Visual Studio 2003 project in the vs7 folder. By default, the library is built using SSE2 optimisations. To change this either change or remove the SQUISH_USE_SSE=2 from the preprocessor symbols. If you are using a Mac then load the Xcode 2.2 project in the distribution. By default, the library is built using Altivec optimisations. To change this either change or remove SQUISH_USE_ALTIVEC=1 from the preprocessor symbols. I guess I'll have to think about changing this for the new Intel Macs that are rolling out... If you are using unix then first edit the config file in the base directory of the distribution, enabling Altivec or SSE with the USE_ALTIVEC or USE_SSE variables, and editing the optimisation flags passed to the C++ compiler if necessary. Then make can be used to build the library, and make install (from the superuser account) can be used to install (into /usr/local by default). REPORTING BUGS OR FEATURE REQUESTS ---------------------------------- Feedback can be sent to Simon Brown (the developer) at si@sjbrown.co.uk New releases are announced on the squish library homepage at http://sjbrown.co.uk/?code=squish openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/singlecolourfit.h0000644000175000017500000000354613151711064024204 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #ifndef SQUISH_SINGLECOLOURFIT_H #define SQUISH_SINGLECOLOURFIT_H #include "squish.h" #include "colourfit.h" namespace squish { class ColourSet; struct SingleColourLookup; class SingleColourFit : public ColourFit { public: SingleColourFit( ColourSet const* colours, int flags ); private: virtual void Compress3( void* block ); virtual void Compress4( void* block ); void ComputeEndPoints( SingleColourLookup const* const* lookups ); u8 m_colour[3]; Vec3 m_start; Vec3 m_end; u8 m_index; int m_error; int m_besterror; }; } // namespace squish #endif // ndef SQUISH_SINGLECOLOURFIT_H openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/colourset.cpp0000644000175000017500000000664313151711064023347 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #include "colourset.h" namespace squish { ColourSet::ColourSet( u8 const* rgba, int mask, int flags ) : m_count( 0 ), m_transparent( false ) { // check the compression mode for dxt1 bool isDxt1 = ( ( flags & kDxt1 ) != 0 ); bool weightByAlpha = ( ( flags & kWeightColourByAlpha ) != 0 ); // create the minimal set for( int i = 0; i < 16; ++i ) { // check this pixel is enabled int bit = 1 << i; if( ( mask & bit ) == 0 ) { m_remap[i] = -1; continue; } // check for transparent pixels when using dxt1 if( isDxt1 && rgba[4*i + 3] < 128 ) { m_remap[i] = -1; m_transparent = true; continue; } // loop over previous points for a match for( int j = 0;; ++j ) { // allocate a new point if( j == i ) { // normalise coordinates to [0,1] float x = ( float )rgba[4*i] / 255.0f; float y = ( float )rgba[4*i + 1] / 255.0f; float z = ( float )rgba[4*i + 2] / 255.0f; // ensure there is always non-zero weight even for zero alpha float w = ( float )( rgba[4*i + 3] + 1 ) / 256.0f; // add the point m_points[m_count] = Vec3( x, y, z ); m_weights[m_count] = ( weightByAlpha ? w : 1.0f ); m_remap[i] = m_count; // advance ++m_count; break; } // check for a match int oldbit = 1 << j; bool match = ( ( mask & oldbit ) != 0 ) && ( rgba[4*i] == rgba[4*j] ) && ( rgba[4*i + 1] == rgba[4*j + 1] ) && ( rgba[4*i + 2] == rgba[4*j + 2] ) && ( rgba[4*j + 3] >= 128 || !isDxt1 ); if( match ) { // get the index of the match int index = m_remap[j]; // ensure there is always non-zero weight even for zero alpha float w = ( float )( rgba[4*i + 3] + 1 ) / 256.0f; // map to this point and increase the weight m_weights[index] += ( weightByAlpha ? w : 1.0f ); m_remap[i] = index; break; } } } // square root the weights for( int i = 0; i < m_count; ++i ) m_weights[i] = std::sqrt( m_weights[i] ); } void ColourSet::RemapIndices( u8 const* source, u8* target ) const { for( int i = 0; i < 16; ++i ) { int j = m_remap[i]; if( j == -1 ) target[i] = 3; else target[i] = source[j]; } } } // namespace squish openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/rangefit.cpp0000644000175000017500000001170213151711064023117 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #include "rangefit.h" #include "colourset.h" #include "colourblock.h" #include namespace squish { RangeFit::RangeFit( ColourSet const* colours, int flags, float* metric ) : ColourFit( colours, flags ) { // initialise the metric (old perceptual = 0.2126f, 0.7152f, 0.0722f) if( metric ) m_metric = Vec3( metric[0], metric[1], metric[2] ); else m_metric = Vec3( 1.0f ); // initialise the best error m_besterror = FLT_MAX; // cache some values int const count = m_colours->GetCount(); Vec3 const* values = m_colours->GetPoints(); float const* weights = m_colours->GetWeights(); // get the covariance matrix Sym3x3 covariance = ComputeWeightedCovariance( count, values, weights ); // compute the principle component Vec3 principle = ComputePrincipleComponent( covariance ); // get the min and max range as the codebook endpoints Vec3 start( 0.0f ); Vec3 end( 0.0f ); if( count > 0 ) { float min, max; // compute the range start = end = values[0]; min = max = Dot( values[0], principle ); for( int i = 1; i < count; ++i ) { float val = Dot( values[i], principle ); if( val < min ) { start = values[i]; min = val; } else if( val > max ) { end = values[i]; max = val; } } } // clamp the output to [0, 1] Vec3 const one( 1.0f ); Vec3 const zero( 0.0f ); start = Min( one, Max( zero, start ) ); end = Min( one, Max( zero, end ) ); // clamp to the grid and save Vec3 const grid( 31.0f, 63.0f, 31.0f ); Vec3 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f ); Vec3 const half( 0.5f ); m_start = Truncate( grid*start + half )*gridrcp; m_end = Truncate( grid*end + half )*gridrcp; } void RangeFit::Compress3( void* block ) { // cache some values int const count = m_colours->GetCount(); Vec3 const* values = m_colours->GetPoints(); // create a codebook Vec3 codes[3]; codes[0] = m_start; codes[1] = m_end; codes[2] = 0.5f*m_start + 0.5f*m_end; // match each point to the closest code u8 closest[16]; float error = 0.0f; for( int i = 0; i < count; ++i ) { // find the closest code float dist = FLT_MAX; int idx = 0; for( int j = 0; j < 3; ++j ) { float d = LengthSquared( m_metric*( values[i] - codes[j] ) ); if( d < dist ) { dist = d; idx = j; } } // save the index closest[i] = ( u8 )idx; // accumulate the error error += dist; } // save this scheme if it wins if( error < m_besterror ) { // remap the indices u8 indices[16]; m_colours->RemapIndices( closest, indices ); // save the block WriteColourBlock3( m_start, m_end, indices, block ); // save the error m_besterror = error; } } void RangeFit::Compress4( void* block ) { // cache some values int const count = m_colours->GetCount(); Vec3 const* values = m_colours->GetPoints(); // create a codebook Vec3 codes[4]; codes[0] = m_start; codes[1] = m_end; codes[2] = ( 2.0f/3.0f )*m_start + ( 1.0f/3.0f )*m_end; codes[3] = ( 1.0f/3.0f )*m_start + ( 2.0f/3.0f )*m_end; // match each point to the closest code u8 closest[16]; float error = 0.0f; for( int i = 0; i < count; ++i ) { // find the closest code float dist = FLT_MAX; int idx = 0; for( int j = 0; j < 4; ++j ) { float d = LengthSquared( m_metric*( values[i] - codes[j] ) ); if( d < dist ) { dist = d; idx = j; } } // save the index closest[i] = ( u8 )idx; // accumulate the error error += dist; } // save this scheme if it wins if( error < m_besterror ) { // remap the indices u8 indices[16]; m_colours->RemapIndices( closest, indices ); // save the block WriteColourBlock4( m_start, m_end, indices, block ); // save the error m_besterror = error; } } } // namespace squish openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/alpha.h0000644000175000017500000000321513151711064022052 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #ifndef SQUISH_ALPHA_H #define SQUISH_ALPHA_H #include "squish.h" namespace squish { void CompressAlphaDxt3( u8 const* rgba, int mask, void* block ); void CompressAlphaDxt5( u8 const* rgba, int mask, void* block ); void DecompressAlphaDxt3( u8* rgba, void const* block ); void DecompressAlphaDxt5( u8* rgba, void const* block ); } // namespace squish #endif // ndef SQUISH_ALPHA_H openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/colourfit.h0000644000175000017500000000336313151711064022777 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #ifndef SQUISH_COLOURFIT_H #define SQUISH_COLOURFIT_H #include "squish.h" #include #include "maths.h" namespace squish { class ColourSet; class ColourFit { public: ColourFit( ColourSet const* colours, int flags ); virtual ~ColourFit(); void Compress( void* block ); protected: virtual void Compress3( void* block ) = 0; virtual void Compress4( void* block ) = 0; ColourSet const* m_colours; int m_flags; }; } // namespace squish #endif // ndef SQUISH_COLOURFIT_H openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/squish.h0000644000175000017500000002573513151711064022314 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #ifndef SQUISH_H #define SQUISH_H //! All squish API functions live in this namespace. namespace squish { // ----------------------------------------------------------------------------- //! Typedef a quantity that is a single unsigned byte. typedef unsigned char u8; // ----------------------------------------------------------------------------- enum { //! Use DXT1 compression. kDxt1 = ( 1 << 0 ), //! Use DXT3 compression. kDxt3 = ( 1 << 1 ), //! Use DXT5 compression. kDxt5 = ( 1 << 2 ), //! Use a very slow but very high quality colour compressor. kColourIterativeClusterFit = ( 1 << 8 ), //! Use a slow but high quality colour compressor (the default). kColourClusterFit = ( 1 << 3 ), //! Use a fast but low quality colour compressor. kColourRangeFit = ( 1 << 4 ), //! Weight the colour by alpha during cluster fit (disabled by default). kWeightColourByAlpha = ( 1 << 7 ) }; // ----------------------------------------------------------------------------- /*! @brief Compresses a 4x4 block of pixels. @param rgba The rgba values of the 16 source pixels. @param mask The valid pixel mask. @param block Storage for the compressed DXT block. @param flags Compression flags. @param metric An optional perceptual metric. The source pixels should be presented as a contiguous array of 16 rgba values, with each component as 1 byte each. In memory this should be: { r1, g1, b1, a1, .... , r16, g16, b16, a16 } The mask parameter enables only certain pixels within the block. The lowest bit enables the first pixel and so on up to the 16th bit. Bits beyond the 16th bit are ignored. Pixels that are not enabled are allowed to take arbitrary colours in the output block. An example of how this can be used is in the CompressImage function to disable pixels outside the bounds of the image when the width or height is not divisible by 4. The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, however, DXT1 will be used by default if none is specified. When using DXT1 compression, 8 bytes of storage are required for the compressed DXT block. DXT3 and DXT5 compression require 16 bytes of storage per block. The flags parameter can also specify a preferred colour compressor to use when fitting the RGB components of the data. Possible colour compressors are: kColourClusterFit (the default), kColourRangeFit (very fast, low quality) or kColourIterativeClusterFit (slowest, best quality). When using kColourClusterFit or kColourIterativeClusterFit, an additional flag can be specified to weight the importance of each pixel by its alpha value. For images that are rendered using alpha blending, this can significantly increase the perceived quality. The metric parameter can be used to weight the relative importance of each colour channel, or pass NULL to use the default uniform weight of { 1.0f, 1.0f, 1.0f }. This replaces the previous flag-based control that allowed either uniform or "perceptual" weights with the fixed values { 0.2126f, 0.7152f, 0.0722f }. If non-NULL, the metric should point to a contiguous array of 3 floats. */ void CompressMasked( u8 const* rgba, int mask, void* block, int flags, float* metric = 0 ); // ----------------------------------------------------------------------------- /*! @brief Compresses a 4x4 block of pixels. @param rgba The rgba values of the 16 source pixels. @param block Storage for the compressed DXT block. @param flags Compression flags. @param metric An optional perceptual metric. The source pixels should be presented as a contiguous array of 16 rgba values, with each component as 1 byte each. In memory this should be: { r1, g1, b1, a1, .... , r16, g16, b16, a16 } The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, however, DXT1 will be used by default if none is specified. When using DXT1 compression, 8 bytes of storage are required for the compressed DXT block. DXT3 and DXT5 compression require 16 bytes of storage per block. The flags parameter can also specify a preferred colour compressor to use when fitting the RGB components of the data. Possible colour compressors are: kColourClusterFit (the default), kColourRangeFit (very fast, low quality) or kColourIterativeClusterFit (slowest, best quality). When using kColourClusterFit or kColourIterativeClusterFit, an additional flag can be specified to weight the importance of each pixel by its alpha value. For images that are rendered using alpha blending, this can significantly increase the perceived quality. The metric parameter can be used to weight the relative importance of each colour channel, or pass NULL to use the default uniform weight of { 1.0f, 1.0f, 1.0f }. This replaces the previous flag-based control that allowed either uniform or "perceptual" weights with the fixed values { 0.2126f, 0.7152f, 0.0722f }. If non-NULL, the metric should point to a contiguous array of 3 floats. This method is an inline that calls CompressMasked with a mask of 0xffff, provided for compatibility with older versions of squish. */ inline void Compress( u8 const* rgba, void* block, int flags, float* metric = 0 ) { CompressMasked( rgba, 0xffff, block, flags, metric ); } // ----------------------------------------------------------------------------- /*! @brief Decompresses a 4x4 block of pixels. @param rgba Storage for the 16 decompressed pixels. @param block The compressed DXT block. @param flags Compression flags. The decompressed pixels will be written as a contiguous array of 16 rgba values, with each component as 1 byte each. In memory this is: { r1, g1, b1, a1, .... , r16, g16, b16, a16 } The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, however, DXT1 will be used by default if none is specified. All other flags are ignored. */ void Decompress( u8* rgba, void const* block, int flags ); // ----------------------------------------------------------------------------- /*! @brief Computes the amount of compressed storage required. @param width The width of the image. @param height The height of the image. @param flags Compression flags. The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, however, DXT1 will be used by default if none is specified. All other flags are ignored. Most DXT images will be a multiple of 4 in each dimension, but this function supports arbitrary size images by allowing the outer blocks to be only partially used. */ int GetStorageRequirements( int width, int height, int flags ); // ----------------------------------------------------------------------------- /*! @brief Compresses an image in memory. @param rgba The pixels of the source. @param width The width of the source image. @param height The height of the source image. @param blocks Storage for the compressed output. @param flags Compression flags. @param metric An optional perceptual metric. The source pixels should be presented as a contiguous array of width*height rgba values, with each component as 1 byte each. In memory this should be: { r1, g1, b1, a1, .... , rn, gn, bn, an } for n = width*height The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, however, DXT1 will be used by default if none is specified. When using DXT1 compression, 8 bytes of storage are required for each compressed DXT block. DXT3 and DXT5 compression require 16 bytes of storage per block. The flags parameter can also specify a preferred colour compressor to use when fitting the RGB components of the data. Possible colour compressors are: kColourClusterFit (the default), kColourRangeFit (very fast, low quality) or kColourIterativeClusterFit (slowest, best quality). When using kColourClusterFit or kColourIterativeClusterFit, an additional flag can be specified to weight the importance of each pixel by its alpha value. For images that are rendered using alpha blending, this can significantly increase the perceived quality. The metric parameter can be used to weight the relative importance of each colour channel, or pass NULL to use the default uniform weight of { 1.0f, 1.0f, 1.0f }. This replaces the previous flag-based control that allowed either uniform or "perceptual" weights with the fixed values { 0.2126f, 0.7152f, 0.0722f }. If non-NULL, the metric should point to a contiguous array of 3 floats. Internally this function calls squish::CompressMasked for each block, which allows for pixels outside the image to take arbitrary values. The function squish::GetStorageRequirements can be called to compute the amount of memory to allocate for the compressed output. */ void CompressImage( u8 const* rgba, int width, int height, void* blocks, int flags, float* metric = 0 ); // ----------------------------------------------------------------------------- /*! @brief Decompresses an image in memory. @param rgba Storage for the decompressed pixels. @param width The width of the source image. @param height The height of the source image. @param blocks The compressed DXT blocks. @param flags Compression flags. The decompressed pixels will be written as a contiguous array of width*height 16 rgba values, with each component as 1 byte each. In memory this is: { r1, g1, b1, a1, .... , rn, gn, bn, an } for n = width*height The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, however, DXT1 will be used by default if none is specified. All other flags are ignored. Internally this function calls squish::Decompress for each block. */ void DecompressImage( u8* rgba, int width, int height, void const* blocks, int flags ); // ----------------------------------------------------------------------------- } // namespace squish #endif // ndef SQUISH_H openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/maths.h0000644000175000017500000001042213151711064022077 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #ifndef SQUISH_MATHS_H #define SQUISH_MATHS_H #include #include #include "config.h" namespace squish { class Vec3 { public: typedef Vec3 const& Arg; Vec3() { } explicit Vec3( float s ) { m_x = s; m_y = s; m_z = s; } Vec3( float x, float y, float z ) { m_x = x; m_y = y; m_z = z; } float X() const { return m_x; } float Y() const { return m_y; } float Z() const { return m_z; } Vec3 operator-() const { return Vec3( -m_x, -m_y, -m_z ); } Vec3& operator+=( Arg v ) { m_x += v.m_x; m_y += v.m_y; m_z += v.m_z; return *this; } Vec3& operator-=( Arg v ) { m_x -= v.m_x; m_y -= v.m_y; m_z -= v.m_z; return *this; } Vec3& operator*=( Arg v ) { m_x *= v.m_x; m_y *= v.m_y; m_z *= v.m_z; return *this; } Vec3& operator*=( float s ) { m_x *= s; m_y *= s; m_z *= s; return *this; } Vec3& operator/=( Arg v ) { m_x /= v.m_x; m_y /= v.m_y; m_z /= v.m_z; return *this; } Vec3& operator/=( float s ) { float t = 1.0f/s; m_x *= t; m_y *= t; m_z *= t; return *this; } friend Vec3 operator+( Arg left, Arg right ) { Vec3 copy( left ); return copy += right; } friend Vec3 operator-( Arg left, Arg right ) { Vec3 copy( left ); return copy -= right; } friend Vec3 operator*( Arg left, Arg right ) { Vec3 copy( left ); return copy *= right; } friend Vec3 operator*( Arg left, float right ) { Vec3 copy( left ); return copy *= right; } friend Vec3 operator*( float left, Arg right ) { Vec3 copy( right ); return copy *= left; } friend Vec3 operator/( Arg left, Arg right ) { Vec3 copy( left ); return copy /= right; } friend Vec3 operator/( Arg left, float right ) { Vec3 copy( left ); return copy /= right; } friend float Dot( Arg left, Arg right ) { return left.m_x*right.m_x + left.m_y*right.m_y + left.m_z*right.m_z; } friend Vec3 Min( Arg left, Arg right ) { return Vec3( std::min( left.m_x, right.m_x ), std::min( left.m_y, right.m_y ), std::min( left.m_z, right.m_z ) ); } friend Vec3 Max( Arg left, Arg right ) { return Vec3( std::max( left.m_x, right.m_x ), std::max( left.m_y, right.m_y ), std::max( left.m_z, right.m_z ) ); } friend Vec3 Truncate( Arg v ) { return Vec3( v.m_x > 0.0f ? std::floor( v.m_x ) : std::ceil( v.m_x ), v.m_y > 0.0f ? std::floor( v.m_y ) : std::ceil( v.m_y ), v.m_z > 0.0f ? std::floor( v.m_z ) : std::ceil( v.m_z ) ); } private: float m_x; float m_y; float m_z; }; inline float LengthSquared( Vec3::Arg v ) { return Dot( v, v ); } class Sym3x3 { public: Sym3x3() { } Sym3x3( float s ) { for( int i = 0; i < 6; ++i ) m_x[i] = s; } float operator[]( int index ) const { return m_x[index]; } float& operator[]( int index ) { return m_x[index]; } private: float m_x[6]; }; Sym3x3 ComputeWeightedCovariance( int n, Vec3 const* points, float const* weights ); Vec3 ComputePrincipleComponent( Sym3x3 const& matrix ); } // namespace squish #endif // ndef SQUISH_MATHS_H openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/alpha.cpp0000644000175000017500000002003613151711064022405 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #include "alpha.h" #include #include namespace squish { static int FloatToInt( float a, int limit ) { // use ANSI round-to-zero behaviour to get round-to-nearest int i = ( int )( a + 0.5f ); // clamp to the limit if( i < 0 ) i = 0; else if( i > limit ) i = limit; // done return i; } void CompressAlphaDxt3( u8 const* rgba, int mask, void* block ) { u8* bytes = reinterpret_cast< u8* >( block ); // quantise and pack the alpha values pairwise for( int i = 0; i < 8; ++i ) { // quantise down to 4 bits float alpha1 = ( float )rgba[8*i + 3] * ( 15.0f/255.0f ); float alpha2 = ( float )rgba[8*i + 7] * ( 15.0f/255.0f ); int quant1 = FloatToInt( alpha1, 15 ); int quant2 = FloatToInt( alpha2, 15 ); // set alpha to zero where masked int bit1 = 1 << ( 2*i ); int bit2 = 1 << ( 2*i + 1 ); if( ( mask & bit1 ) == 0 ) quant1 = 0; if( ( mask & bit2 ) == 0 ) quant2 = 0; // pack into the byte bytes[i] = ( u8 )( quant1 | ( quant2 << 4 ) ); } } void DecompressAlphaDxt3( u8* rgba, void const* block ) { u8 const* bytes = reinterpret_cast< u8 const* >( block ); // unpack the alpha values pairwise for( int i = 0; i < 8; ++i ) { // quantise down to 4 bits u8 quant = bytes[i]; // unpack the values u8 lo = quant & 0x0f; u8 hi = quant & 0xf0; // convert back up to bytes rgba[8*i + 3] = lo | ( lo << 4 ); rgba[8*i + 7] = hi | ( hi >> 4 ); } } static void FixRange( int& min, int& max, int steps ) { if( max - min < steps ) max = std::min( min + steps, 255 ); if( max - min < steps ) min = std::max( 0, max - steps ); } static int FitCodes( u8 const* rgba, int mask, u8 const* codes, u8* indices ) { // fit each alpha value to the codebook int err = 0; for( int i = 0; i < 16; ++i ) { // check this pixel is valid int bit = 1 << i; if( ( mask & bit ) == 0 ) { // use the first code indices[i] = 0; continue; } // find the least error and corresponding index int value = rgba[4*i + 3]; int least = INT_MAX; int index = 0; for( int j = 0; j < 8; ++j ) { // get the squared error from this code int dist = ( int )value - ( int )codes[j]; dist *= dist; // compare with the best so far if( dist < least ) { least = dist; index = j; } } // save this index and accumulate the error indices[i] = ( u8 )index; err += least; } // return the total error return err; } static void WriteAlphaBlock( int alpha0, int alpha1, u8 const* indices, void* block ) { u8* bytes = reinterpret_cast< u8* >( block ); // write the first two bytes bytes[0] = ( u8 )alpha0; bytes[1] = ( u8 )alpha1; // pack the indices with 3 bits each u8* dest = bytes + 2; u8 const* src = indices; for( int i = 0; i < 2; ++i ) { // pack 8 3-bit values int value = 0; for( int j = 0; j < 8; ++j ) { int index = *src++; value |= ( index << 3*j ); } // store in 3 bytes for( int j = 0; j < 3; ++j ) { int byte = ( value >> 8*j ) & 0xff; *dest++ = ( u8 )byte; } } } static void WriteAlphaBlock5( int alpha0, int alpha1, u8 const* indices, void* block ) { // check the relative values of the endpoints if( alpha0 > alpha1 ) { // swap the indices u8 swapped[16]; for( int i = 0; i < 16; ++i ) { u8 index = indices[i]; if( index == 0 ) swapped[i] = 1; else if( index == 1 ) swapped[i] = 0; else if( index <= 5 ) swapped[i] = 7 - index; else swapped[i] = index; } // write the block WriteAlphaBlock( alpha1, alpha0, swapped, block ); } else { // write the block WriteAlphaBlock( alpha0, alpha1, indices, block ); } } static void WriteAlphaBlock7( int alpha0, int alpha1, u8 const* indices, void* block ) { // check the relative values of the endpoints if( alpha0 < alpha1 ) { // swap the indices u8 swapped[16]; for( int i = 0; i < 16; ++i ) { u8 index = indices[i]; if( index == 0 ) swapped[i] = 1; else if( index == 1 ) swapped[i] = 0; else swapped[i] = 9 - index; } // write the block WriteAlphaBlock( alpha1, alpha0, swapped, block ); } else { // write the block WriteAlphaBlock( alpha0, alpha1, indices, block ); } } void CompressAlphaDxt5( u8 const* rgba, int mask, void* block ) { // get the range for 5-alpha and 7-alpha interpolation int min5 = 255; int max5 = 0; int min7 = 255; int max7 = 0; for( int i = 0; i < 16; ++i ) { // check this pixel is valid int bit = 1 << i; if( ( mask & bit ) == 0 ) continue; // incorporate into the min/max int value = rgba[4*i + 3]; if( value < min7 ) min7 = value; if( value > max7 ) max7 = value; if( value != 0 && value < min5 ) min5 = value; if( value != 255 && value > max5 ) max5 = value; } // handle the case that no valid range was found if( min5 > max5 ) min5 = max5; if( min7 > max7 ) min7 = max7; // fix the range to be the minimum in each case FixRange( min5, max5, 5 ); FixRange( min7, max7, 7 ); // set up the 5-alpha code book u8 codes5[8]; codes5[0] = ( u8 )min5; codes5[1] = ( u8 )max5; for( int i = 1; i < 5; ++i ) codes5[1 + i] = ( u8 )( ( ( 5 - i )*min5 + i*max5 )/5 ); codes5[6] = 0; codes5[7] = 255; // set up the 7-alpha code book u8 codes7[8]; codes7[0] = ( u8 )min7; codes7[1] = ( u8 )max7; for( int i = 1; i < 7; ++i ) codes7[1 + i] = ( u8 )( ( ( 7 - i )*min7 + i*max7 )/7 ); // fit the data to both code books u8 indices5[16]; u8 indices7[16]; int err5 = FitCodes( rgba, mask, codes5, indices5 ); int err7 = FitCodes( rgba, mask, codes7, indices7 ); // save the block with least error if( err5 <= err7 ) WriteAlphaBlock5( min5, max5, indices5, block ); else WriteAlphaBlock7( min7, max7, indices7, block ); } void DecompressAlphaDxt5( u8* rgba, void const* block ) { // get the two alpha values u8 const* bytes = reinterpret_cast< u8 const* >( block ); int alpha0 = bytes[0]; int alpha1 = bytes[1]; // compare the values to build the codebook u8 codes[8]; codes[0] = ( u8 )alpha0; codes[1] = ( u8 )alpha1; if( alpha0 <= alpha1 ) { // use 5-alpha codebook for( int i = 1; i < 5; ++i ) codes[1 + i] = ( u8 )( ( ( 5 - i )*alpha0 + i*alpha1 )/5 ); codes[6] = 0; codes[7] = 255; } else { // use 7-alpha codebook for( int i = 1; i < 7; ++i ) codes[1 + i] = ( u8 )( ( ( 7 - i )*alpha0 + i*alpha1 )/7 ); } // decode the indices u8 indices[16]; u8 const* src = bytes + 2; u8* dest = indices; for( int i = 0; i < 2; ++i ) { // grab 3 bytes int value = 0; for( int j = 0; j < 3; ++j ) { int byte = *src++; value |= ( byte << 8*j ); } // unpack 8 3-bit values from it for( int j = 0; j < 8; ++j ) { int index = ( value >> 3*j ) & 0x7; *dest++ = ( u8 )index; } } // write out the indexed codebook values for( int i = 0; i < 16; ++i ) rgba[4*i + 3] = codes[indices[i]]; } } // namespace squish openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/simd.h0000644000175000017500000000274613151711064021731 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #ifndef SQUISH_SIMD_H #define SQUISH_SIMD_H #include "maths.h" #if SQUISH_USE_ALTIVEC #include "simd_ve.h" #elif SQUISH_USE_SSE #include "simd_sse.h" #else #include "simd_float.h" #endif #endif // ndef SQUISH_SIMD_H openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/maths.cpp0000644000175000017500000001444313151711064022441 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ /*! @file The symmetric eigensystem solver algorithm is from http://www.geometrictools.com/Documentation/EigenSymmetric3x3.pdf */ #include "maths.h" #include "simd.h" #include namespace squish { Sym3x3 ComputeWeightedCovariance( int n, Vec3 const* points, float const* weights ) { // compute the centroid float total = 0.0f; Vec3 centroid( 0.0f ); for( int i = 0; i < n; ++i ) { total += weights[i]; centroid += weights[i]*points[i]; } if( total > FLT_EPSILON ) centroid /= total; // accumulate the covariance matrix Sym3x3 covariance( 0.0f ); for( int i = 0; i < n; ++i ) { Vec3 a = points[i] - centroid; Vec3 b = weights[i]*a; covariance[0] += a.X()*b.X(); covariance[1] += a.X()*b.Y(); covariance[2] += a.X()*b.Z(); covariance[3] += a.Y()*b.Y(); covariance[4] += a.Y()*b.Z(); covariance[5] += a.Z()*b.Z(); } // return it return covariance; } #if 0 static Vec3 GetMultiplicity1Evector( Sym3x3 const& matrix, float evalue ) { // compute M Sym3x3 m; m[0] = matrix[0] - evalue; m[1] = matrix[1]; m[2] = matrix[2]; m[3] = matrix[3] - evalue; m[4] = matrix[4]; m[5] = matrix[5] - evalue; // compute U Sym3x3 u; u[0] = m[3]*m[5] - m[4]*m[4]; u[1] = m[2]*m[4] - m[1]*m[5]; u[2] = m[1]*m[4] - m[2]*m[3]; u[3] = m[0]*m[5] - m[2]*m[2]; u[4] = m[1]*m[2] - m[4]*m[0]; u[5] = m[0]*m[3] - m[1]*m[1]; // find the largest component float mc = std::fabs( u[0] ); int mi = 0; for( int i = 1; i < 6; ++i ) { float c = std::fabs( u[i] ); if( c > mc ) { mc = c; mi = i; } } // pick the column with this component switch( mi ) { case 0: return Vec3( u[0], u[1], u[2] ); case 1: case 3: return Vec3( u[1], u[3], u[4] ); default: return Vec3( u[2], u[4], u[5] ); } } static Vec3 GetMultiplicity2Evector( Sym3x3 const& matrix, float evalue ) { // compute M Sym3x3 m; m[0] = matrix[0] - evalue; m[1] = matrix[1]; m[2] = matrix[2]; m[3] = matrix[3] - evalue; m[4] = matrix[4]; m[5] = matrix[5] - evalue; // find the largest component float mc = std::fabs( m[0] ); int mi = 0; for( int i = 1; i < 6; ++i ) { float c = std::fabs( m[i] ); if( c > mc ) { mc = c; mi = i; } } // pick the first eigenvector based on this index switch( mi ) { case 0: case 1: return Vec3( -m[1], m[0], 0.0f ); case 2: return Vec3( m[2], 0.0f, -m[0] ); case 3: case 4: return Vec3( 0.0f, -m[4], m[3] ); default: return Vec3( 0.0f, -m[5], m[4] ); } } Vec3 ComputePrincipleComponent( Sym3x3 const& matrix ) { // compute the cubic coefficients float c0 = matrix[0]*matrix[3]*matrix[5] + 2.0f*matrix[1]*matrix[2]*matrix[4] - matrix[0]*matrix[4]*matrix[4] - matrix[3]*matrix[2]*matrix[2] - matrix[5]*matrix[1]*matrix[1]; float c1 = matrix[0]*matrix[3] + matrix[0]*matrix[5] + matrix[3]*matrix[5] - matrix[1]*matrix[1] - matrix[2]*matrix[2] - matrix[4]*matrix[4]; float c2 = matrix[0] + matrix[3] + matrix[5]; // compute the quadratic coefficients float a = c1 - ( 1.0f/3.0f )*c2*c2; float b = ( -2.0f/27.0f )*c2*c2*c2 + ( 1.0f/3.0f )*c1*c2 - c0; // compute the root count check float Q = 0.25f*b*b + ( 1.0f/27.0f )*a*a*a; // test the multiplicity if( FLT_EPSILON < Q ) { // only one root, which implies we have a multiple of the identity return Vec3( 1.0f ); } else if( Q < -FLT_EPSILON ) { // three distinct roots float theta = std::atan2( std::sqrt( -Q ), -0.5f*b ); float rho = std::sqrt( 0.25f*b*b - Q ); float rt = std::pow( rho, 1.0f/3.0f ); float ct = std::cos( theta/3.0f ); float st = std::sin( theta/3.0f ); float l1 = ( 1.0f/3.0f )*c2 + 2.0f*rt*ct; float l2 = ( 1.0f/3.0f )*c2 - rt*( ct + ( float )sqrt( 3.0f )*st ); float l3 = ( 1.0f/3.0f )*c2 - rt*( ct - ( float )sqrt( 3.0f )*st ); // pick the larger if( std::fabs( l2 ) > std::fabs( l1 ) ) l1 = l2; if( std::fabs( l3 ) > std::fabs( l1 ) ) l1 = l3; // get the eigenvector return GetMultiplicity1Evector( matrix, l1 ); } else // if( -FLT_EPSILON <= Q && Q <= FLT_EPSILON ) { // two roots float rt; if( b < 0.0f ) rt = -std::pow( -0.5f*b, 1.0f/3.0f ); else rt = std::pow( 0.5f*b, 1.0f/3.0f ); float l1 = ( 1.0f/3.0f )*c2 + rt; // repeated float l2 = ( 1.0f/3.0f )*c2 - 2.0f*rt; // get the eigenvector if( std::fabs( l1 ) > std::fabs( l2 ) ) return GetMultiplicity2Evector( matrix, l1 ); else return GetMultiplicity1Evector( matrix, l2 ); } } #else #define POWER_ITERATION_COUNT 8 Vec3 ComputePrincipleComponent( Sym3x3 const& matrix ) { Vec4 const row0( matrix[0], matrix[1], matrix[2], 0.0f ); Vec4 const row1( matrix[1], matrix[3], matrix[4], 0.0f ); Vec4 const row2( matrix[2], matrix[4], matrix[5], 0.0f ); Vec4 v = VEC4_CONST( 1.0f ); for( int i = 0; i < POWER_ITERATION_COUNT; ++i ) { // matrix multiply Vec4 w = row0*v.SplatX(); w = MultiplyAdd(row1, v.SplatY(), w); w = MultiplyAdd(row2, v.SplatZ(), w); // get max component from xyz in all channels Vec4 a = Max(w.SplatX(), Max(w.SplatY(), w.SplatZ())); // divide through and advance v = w*Reciprocal(a); } return v.GetVec3(); } #endif } // namespace squish openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/singlecolourfit.cpp0000644000175000017500000001045313151711064024532 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #include "singlecolourfit.h" #include "colourset.h" #include "colourblock.h" namespace squish { struct SourceBlock { u8 start; u8 end; u8 error; }; struct SingleColourLookup { SourceBlock sources[2]; }; #include "singlecolourlookup.inl" static int FloatToInt( float a, int limit ) { // use ANSI round-to-zero behaviour to get round-to-nearest int i = ( int )( a + 0.5f ); // clamp to the limit if( i < 0 ) i = 0; else if( i > limit ) i = limit; // done return i; } SingleColourFit::SingleColourFit( ColourSet const* colours, int flags ) : ColourFit( colours, flags ) { // grab the single colour Vec3 const* values = m_colours->GetPoints(); m_colour[0] = ( u8 )FloatToInt( 255.0f*values->X(), 255 ); m_colour[1] = ( u8 )FloatToInt( 255.0f*values->Y(), 255 ); m_colour[2] = ( u8 )FloatToInt( 255.0f*values->Z(), 255 ); // initialise the best error m_besterror = INT_MAX; } void SingleColourFit::Compress3( void* block ) { // build the table of lookups SingleColourLookup const* const lookups[] = { lookup_5_3, lookup_6_3, lookup_5_3 }; // find the best end-points and index ComputeEndPoints( lookups ); // build the block if we win if( m_error < m_besterror ) { // remap the indices u8 indices[16]; m_colours->RemapIndices( &m_index, indices ); // save the block WriteColourBlock3( m_start, m_end, indices, block ); // save the error m_besterror = m_error; } } void SingleColourFit::Compress4( void* block ) { // build the table of lookups SingleColourLookup const* const lookups[] = { lookup_5_4, lookup_6_4, lookup_5_4 }; // find the best end-points and index ComputeEndPoints( lookups ); // build the block if we win if( m_error < m_besterror ) { // remap the indices u8 indices[16]; m_colours->RemapIndices( &m_index, indices ); // save the block WriteColourBlock4( m_start, m_end, indices, block ); // save the error m_besterror = m_error; } } void SingleColourFit::ComputeEndPoints( SingleColourLookup const* const* lookups ) { // check each index combination (endpoint or intermediate) m_error = INT_MAX; for( int index = 0; index < 2; ++index ) { // check the error for this codebook index SourceBlock const* sources[3]; int error = 0; for( int channel = 0; channel < 3; ++channel ) { // grab the lookup table and index for this channel SingleColourLookup const* lookup = lookups[channel]; int target = m_colour[channel]; // store a pointer to the source for this channel sources[channel] = lookup[target].sources + index; // accumulate the error int diff = sources[channel]->error; error += diff*diff; } // keep it if the error is lower if( error < m_error ) { m_start = Vec3( ( float )sources[0]->start/31.0f, ( float )sources[1]->start/63.0f, ( float )sources[2]->start/31.0f ); m_end = Vec3( ( float )sources[0]->end/31.0f, ( float )sources[1]->end/63.0f, ( float )sources[2]->end/31.0f ); m_index = ( u8 )( 2*index ); m_error = error; } } } } // namespace squish openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/colourblock.cpp0000644000175000017500000001216513151711064023642 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #include "colourblock.h" namespace squish { static int FloatToInt( float a, int limit ) { // use ANSI round-to-zero behaviour to get round-to-nearest int i = ( int )( a + 0.5f ); // clamp to the limit if( i < 0 ) i = 0; else if( i > limit ) i = limit; // done return i; } static int FloatTo565( Vec3::Arg colour ) { // get the components in the correct range int r = FloatToInt( 31.0f*colour.X(), 31 ); int g = FloatToInt( 63.0f*colour.Y(), 63 ); int b = FloatToInt( 31.0f*colour.Z(), 31 ); // pack into a single value return ( r << 11 ) | ( g << 5 ) | b; } static void WriteColourBlock( int a, int b, u8* indices, void* block ) { // get the block as bytes u8* bytes = ( u8* )block; // write the endpoints bytes[0] = ( u8 )( a & 0xff ); bytes[1] = ( u8 )( a >> 8 ); bytes[2] = ( u8 )( b & 0xff ); bytes[3] = ( u8 )( b >> 8 ); // write the indices for( int i = 0; i < 4; ++i ) { u8 const* ind = indices + 4*i; bytes[4 + i] = ind[0] | ( ind[1] << 2 ) | ( ind[2] << 4 ) | ( ind[3] << 6 ); } } void WriteColourBlock3( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block ) { // get the packed values int a = FloatTo565( start ); int b = FloatTo565( end ); // remap the indices u8 remapped[16]; if( a <= b ) { // use the indices directly for( int i = 0; i < 16; ++i ) remapped[i] = indices[i]; } else { // swap a and b std::swap( a, b ); for( int i = 0; i < 16; ++i ) { if( indices[i] == 0 ) remapped[i] = 1; else if( indices[i] == 1 ) remapped[i] = 0; else remapped[i] = indices[i]; } } // write the block WriteColourBlock( a, b, remapped, block ); } void WriteColourBlock4( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block ) { // get the packed values int a = FloatTo565( start ); int b = FloatTo565( end ); // remap the indices u8 remapped[16]; if( a < b ) { // swap a and b std::swap( a, b ); for( int i = 0; i < 16; ++i ) remapped[i] = ( indices[i] ^ 0x1 ) & 0x3; } else if( a == b ) { // use index 0 for( int i = 0; i < 16; ++i ) remapped[i] = 0; } else { // use the indices directly for( int i = 0; i < 16; ++i ) remapped[i] = indices[i]; } // write the block WriteColourBlock( a, b, remapped, block ); } static int Unpack565( u8 const* packed, u8* colour ) { // build the packed value int value = ( int )packed[0] | ( ( int )packed[1] << 8 ); // get the components in the stored range u8 red = ( u8 )( ( value >> 11 ) & 0x1f ); u8 green = ( u8 )( ( value >> 5 ) & 0x3f ); u8 blue = ( u8 )( value & 0x1f ); // scale up to 8 bits colour[0] = ( red << 3 ) | ( red >> 2 ); colour[1] = ( green << 2 ) | ( green >> 4 ); colour[2] = ( blue << 3 ) | ( blue >> 2 ); colour[3] = 255; // return the value return value; } void DecompressColour( u8* rgba, void const* block, bool isDxt1 ) { // get the block bytes u8 const* bytes = reinterpret_cast< u8 const* >( block ); // unpack the endpoints u8 codes[16]; int a = Unpack565( bytes, codes ); int b = Unpack565( bytes + 2, codes + 4 ); // generate the midpoints for( int i = 0; i < 3; ++i ) { int c = codes[i]; int d = codes[4 + i]; if( isDxt1 && a <= b ) { codes[8 + i] = ( u8 )( ( c + d )/2 ); codes[12 + i] = 0; } else { codes[8 + i] = ( u8 )( ( 2*c + d )/3 ); codes[12 + i] = ( u8 )( ( c + 2*d )/3 ); } } // fill in alpha for the intermediate values codes[8 + 3] = 255; codes[12 + 3] = ( isDxt1 && a <= b ) ? 0 : 255; // unpack the indices u8 indices[16]; for( int i = 0; i < 4; ++i ) { u8* ind = indices + 4*i; u8 packed = bytes[4 + i]; ind[0] = packed & 0x3; ind[1] = ( packed >> 2 ) & 0x3; ind[2] = ( packed >> 4 ) & 0x3; ind[3] = ( packed >> 6 ) & 0x3; } // store out the colours for( int i = 0; i < 16; ++i ) { u8 offset = 4*indices[i]; for( int j = 0; j < 4; ++j ) rgba[4*i + j] = codes[offset + j]; } } } // namespace squish openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/simd_ve.h0000644000175000017500000001004713151711064022414 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #ifndef SQUISH_SIMD_VE_H #define SQUISH_SIMD_VE_H #include #undef bool namespace squish { #define VEC4_CONST( X ) Vec4( ( vector float ){ X } ) class Vec4 { public: typedef Vec4 Arg; Vec4() {} explicit Vec4( vector float v ) : m_v( v ) {} Vec4( Vec4 const& arg ) : m_v( arg.m_v ) {} Vec4& operator=( Vec4 const& arg ) { m_v = arg.m_v; return *this; } explicit Vec4( float s ) { union { vector float v; float c[4]; } u; u.c[0] = s; u.c[1] = s; u.c[2] = s; u.c[3] = s; m_v = u.v; } Vec4( float x, float y, float z, float w ) { union { vector float v; float c[4]; } u; u.c[0] = x; u.c[1] = y; u.c[2] = z; u.c[3] = w; m_v = u.v; } Vec3 GetVec3() const { union { vector float v; float c[4]; } u; u.v = m_v; return Vec3( u.c[0], u.c[1], u.c[2] ); } Vec4 SplatX() const { return Vec4( vec_splat( m_v, 0 ) ); } Vec4 SplatY() const { return Vec4( vec_splat( m_v, 1 ) ); } Vec4 SplatZ() const { return Vec4( vec_splat( m_v, 2 ) ); } Vec4 SplatW() const { return Vec4( vec_splat( m_v, 3 ) ); } Vec4& operator+=( Arg v ) { m_v = vec_add( m_v, v.m_v ); return *this; } Vec4& operator-=( Arg v ) { m_v = vec_sub( m_v, v.m_v ); return *this; } Vec4& operator*=( Arg v ) { m_v = vec_madd( m_v, v.m_v, ( vector float ){ -0.0f } ); return *this; } friend Vec4 operator+( Vec4::Arg left, Vec4::Arg right ) { return Vec4( vec_add( left.m_v, right.m_v ) ); } friend Vec4 operator-( Vec4::Arg left, Vec4::Arg right ) { return Vec4( vec_sub( left.m_v, right.m_v ) ); } friend Vec4 operator*( Vec4::Arg left, Vec4::Arg right ) { return Vec4( vec_madd( left.m_v, right.m_v, ( vector float ){ -0.0f } ) ); } //! Returns a*b + c friend Vec4 MultiplyAdd( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c ) { return Vec4( vec_madd( a.m_v, b.m_v, c.m_v ) ); } //! Returns -( a*b - c ) friend Vec4 NegativeMultiplySubtract( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c ) { return Vec4( vec_nmsub( a.m_v, b.m_v, c.m_v ) ); } friend Vec4 Reciprocal( Vec4::Arg v ) { // get the reciprocal estimate vector float estimate = vec_re( v.m_v ); // one round of Newton-Rhaphson refinement vector float diff = vec_nmsub( estimate, v.m_v, ( vector float ){ 1.0f } ); return Vec4( vec_madd( diff, estimate, estimate ) ); } friend Vec4 Min( Vec4::Arg left, Vec4::Arg right ) { return Vec4( vec_min( left.m_v, right.m_v ) ); } friend Vec4 Max( Vec4::Arg left, Vec4::Arg right ) { return Vec4( vec_max( left.m_v, right.m_v ) ); } friend Vec4 Truncate( Vec4::Arg v ) { return Vec4( vec_trunc( v.m_v ) ); } friend bool CompareAnyLessThan( Vec4::Arg left, Vec4::Arg right ) { return vec_any_lt( left.m_v, right.m_v ) != 0; } private: vector float m_v; }; } // namespace squish #endif // ndef SQUISH_SIMD_VE_H openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/ChangeLog0000644000175000017500000000340213151711064022364 0ustar mfvmfv1.10 * Iterative cluster fit is now considered to be a new compression mode * The core cluster fit is now 4x faster using contributions by Ignacio Castano from NVIDIA * The single colour lookup table has been halved by exploiting symmetry 1.9 * Added contributed SSE1 truncate implementation * Changed use of SQUISH_USE_SSE to be 1 for SSE and 2 for SSE2 instructions * Cluster fit is now iterative to further reduce image error 1.8 * Switched from using floor to trunc for much better SSE performance (again) * Xcode build now expects libpng in /usr/local for extra/squishpng 1.7 * Fixed floating-point equality issue in clusterfit sort (x86 affected only) * Implemented proper SSE(2) floor function for 50% speedup on SSE builds * The range fit implementation now uses the correct colour metric 1.6 * Fixed bug in CompressImage where masked pixels were not skipped over * DXT3 and DXT5 alpha compression now properly use the mask to ignore pixels * Fixed major DXT1 bug that can generate unexpected transparent pixels 1.5 * Added CompressMasked function to handle incomplete DXT blocks more cleanly * Added kWeightColourByAlpha flag for better quality images when alpha blending 1.4 * Fixed stack overflow in rangefit 1.3 * Worked around SSE floor implementation bug, proper fix needed! * This release has visual studio and makefile builds that work 1.2 * Added provably optimal single colour compressor * Added extra/squishgen.cpp that generates single colour lookup tables 1.1 * Fixed a DXT1 colour output bug * Changed argument order for Decompress function to match Compress * Added GetStorageRequirements function * Added CompressImage function * Added DecompressImage function * Moved squishtool.cpp to extra/squishpng.cpp * Added extra/squishtest.cpp 1.0 * Initial release openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/rangefit.h0000644000175000017500000000334013151711064022563 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #ifndef SQUISH_RANGEFIT_H #define SQUISH_RANGEFIT_H #include "squish.h" #include "colourfit.h" #include "maths.h" namespace squish { class ColourSet; class RangeFit : public ColourFit { public: RangeFit( ColourSet const* colours, int flags, float* metric ); private: virtual void Compress3( void* block ); virtual void Compress4( void* block ); Vec3 m_metric; Vec3 m_start; Vec3 m_end; float m_besterror; }; } // squish #endif // ndef SQUISH_RANGEFIT_H openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/squish.cpp0000644000175000017500000001442113151711064022635 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #include "squish.h" #include "colourset.h" #include "maths.h" #include "rangefit.h" #include "clusterfit.h" #include "colourblock.h" #include "alpha.h" #include "singlecolourfit.h" namespace squish { static int FixFlags( int flags ) { // grab the flag bits int method = flags & ( kDxt1 | kDxt3 | kDxt5 ); int fit = flags & ( kColourIterativeClusterFit | kColourClusterFit | kColourRangeFit ); int extra = flags & kWeightColourByAlpha; // set defaults if( method != kDxt3 && method != kDxt5 ) method = kDxt1; if( fit != kColourRangeFit && fit != kColourIterativeClusterFit ) fit = kColourClusterFit; // done return method | fit | extra; } void CompressMasked( u8 const* rgba, int mask, void* block, int flags, float* metric ) { // fix any bad flags flags = FixFlags( flags ); // get the block locations void* colourBlock = block; void* alphaBock = block; if( ( flags & ( kDxt3 | kDxt5 ) ) != 0 ) colourBlock = reinterpret_cast< u8* >( block ) + 8; // create the minimal point set ColourSet colours( rgba, mask, flags ); // check the compression type and compress colour if( colours.GetCount() == 1 ) { // always do a single colour fit SingleColourFit fit( &colours, flags ); fit.Compress( colourBlock ); } else if( ( flags & kColourRangeFit ) != 0 || colours.GetCount() == 0 ) { // do a range fit RangeFit fit( &colours, flags, metric ); fit.Compress( colourBlock ); } else { // default to a cluster fit (could be iterative or not) ClusterFit fit( &colours, flags, metric ); fit.Compress( colourBlock ); } // compress alpha separately if necessary if( ( flags & kDxt3 ) != 0 ) CompressAlphaDxt3( rgba, mask, alphaBock ); else if( ( flags & kDxt5 ) != 0 ) CompressAlphaDxt5( rgba, mask, alphaBock ); } void Decompress( u8* rgba, void const* block, int flags ) { // fix any bad flags flags = FixFlags( flags ); // get the block locations void const* colourBlock = block; void const* alphaBock = block; if( ( flags & ( kDxt3 | kDxt5 ) ) != 0 ) colourBlock = reinterpret_cast< u8 const* >( block ) + 8; // decompress colour DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 ); // decompress alpha separately if necessary if( ( flags & kDxt3 ) != 0 ) DecompressAlphaDxt3( rgba, alphaBock ); else if( ( flags & kDxt5 ) != 0 ) DecompressAlphaDxt5( rgba, alphaBock ); } int GetStorageRequirements( int width, int height, int flags ) { // fix any bad flags flags = FixFlags( flags ); // compute the storage requirements int blockcount = ( ( width + 3 )/4 ) * ( ( height + 3 )/4 ); int blocksize = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16; return blockcount*blocksize; } void CompressImage( u8 const* rgba, int width, int height, void* blocks, int flags, float* metric ) { // fix any bad flags flags = FixFlags( flags ); // initialise the block output u8* targetBlock = reinterpret_cast< u8* >( blocks ); int bytesPerBlock = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16; // loop over blocks for( int y = 0; y < height; y += 4 ) { for( int x = 0; x < width; x += 4 ) { // build the 4x4 block of pixels u8 sourceRgba[16*4]; u8* targetPixel = sourceRgba; int mask = 0; for( int py = 0; py < 4; ++py ) { for( int px = 0; px < 4; ++px ) { // get the source pixel in the image int sx = x + px; int sy = y + py; // enable if we're in the image if( sx < width && sy < height ) { // copy the rgba value u8 const* sourcePixel = rgba + 4*( width*sy + sx ); for( int i = 0; i < 4; ++i ) *targetPixel++ = *sourcePixel++; // enable this pixel mask |= ( 1 << ( 4*py + px ) ); } else { // skip this pixel as its outside the image targetPixel += 4; } } } // compress it into the output CompressMasked( sourceRgba, mask, targetBlock, flags, metric ); // advance targetBlock += bytesPerBlock; } } } void DecompressImage( u8* rgba, int width, int height, void const* blocks, int flags ) { // fix any bad flags flags = FixFlags( flags ); // initialise the block input u8 const* sourceBlock = reinterpret_cast< u8 const* >( blocks ); int bytesPerBlock = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16; // loop over blocks for( int y = 0; y < height; y += 4 ) { for( int x = 0; x < width; x += 4 ) { // decompress the block u8 targetRgba[4*16]; Decompress( targetRgba, sourceBlock, flags ); // write the decompressed pixels to the correct image locations u8 const* sourcePixel = targetRgba; for( int py = 0; py < 4; ++py ) { for( int px = 0; px < 4; ++px ) { // get the target location int sx = x + px; int sy = y + py; if( sx < width && sy < height ) { u8* targetPixel = rgba + 4*( width*sy + sx ); // copy the rgba value for( int i = 0; i < 4; ++i ) *targetPixel++ = *sourcePixel++; } else { // skip this pixel as its outside the image sourcePixel += 4; } } } // advance sourceBlock += bytesPerBlock; } } } } // namespace squish openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/config.h0000644000175000017500000000355213151711064022236 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #ifndef SQUISH_CONFIG_H #define SQUISH_CONFIG_H // Set to 1 when building squish to use Altivec instructions. #ifndef SQUISH_USE_ALTIVEC #define SQUISH_USE_ALTIVEC 0 #endif // Set to 1 or 2 when building squish to use SSE or SSE2 instructions. #ifndef SQUISH_USE_SSE #define SQUISH_USE_SSE 0 #endif // Internally set SQUISH_USE_SIMD when either Altivec or SSE is available. #if SQUISH_USE_ALTIVEC && SQUISH_USE_SSE #error "Cannot enable both Altivec and SSE!" #endif #if SQUISH_USE_ALTIVEC || SQUISH_USE_SSE #define SQUISH_USE_SIMD 1 #else #define SQUISH_USE_SIMD 0 #endif #endif // ndef SQUISH_CONFIG_H openimageio-1.7.17~dfsg0.orig/src/dds.imageio/squish/simd_sse.h0000644000175000017500000001156013151711064022575 0ustar mfvmfv/* ----------------------------------------------------------------------------- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------- */ #ifndef SQUISH_SIMD_SSE_H #define SQUISH_SIMD_SSE_H #include #if ( SQUISH_USE_SSE > 1 ) #include #endif #define SQUISH_SSE_SPLAT( a ) \ ( ( a ) | ( ( a ) << 2 ) | ( ( a ) << 4 ) | ( ( a ) << 6 ) ) #define SQUISH_SSE_SHUF( x, y, z, w ) \ ( ( x ) | ( ( y ) << 2 ) | ( ( z ) << 4 ) | ( ( w ) << 6 ) ) namespace squish { #define VEC4_CONST( X ) Vec4( X ) class Vec4 { public: typedef Vec4 const& Arg; Vec4() {} explicit Vec4( __m128 v ) : m_v( v ) {} Vec4( Vec4 const& arg ) : m_v( arg.m_v ) {} Vec4& operator=( Vec4 const& arg ) { m_v = arg.m_v; return *this; } explicit Vec4( float s ) : m_v( _mm_set1_ps( s ) ) {} Vec4( float x, float y, float z, float w ) : m_v( _mm_setr_ps( x, y, z, w ) ) {} Vec3 GetVec3() const { #ifdef __GNUC__ __attribute__ ((__aligned__ (16))) float c[4]; #else __declspec(align(16)) float c[4]; #endif _mm_store_ps( c, m_v ); return Vec3( c[0], c[1], c[2] ); } Vec4 SplatX() const { return Vec4( _mm_shuffle_ps( m_v, m_v, SQUISH_SSE_SPLAT( 0 ) ) ); } Vec4 SplatY() const { return Vec4( _mm_shuffle_ps( m_v, m_v, SQUISH_SSE_SPLAT( 1 ) ) ); } Vec4 SplatZ() const { return Vec4( _mm_shuffle_ps( m_v, m_v, SQUISH_SSE_SPLAT( 2 ) ) ); } Vec4 SplatW() const { return Vec4( _mm_shuffle_ps( m_v, m_v, SQUISH_SSE_SPLAT( 3 ) ) ); } Vec4& operator+=( Arg v ) { m_v = _mm_add_ps( m_v, v.m_v ); return *this; } Vec4& operator-=( Arg v ) { m_v = _mm_sub_ps( m_v, v.m_v ); return *this; } Vec4& operator*=( Arg v ) { m_v = _mm_mul_ps( m_v, v.m_v ); return *this; } friend Vec4 operator+( Vec4::Arg left, Vec4::Arg right ) { return Vec4( _mm_add_ps( left.m_v, right.m_v ) ); } friend Vec4 operator-( Vec4::Arg left, Vec4::Arg right ) { return Vec4( _mm_sub_ps( left.m_v, right.m_v ) ); } friend Vec4 operator*( Vec4::Arg left, Vec4::Arg right ) { return Vec4( _mm_mul_ps( left.m_v, right.m_v ) ); } //! Returns a*b + c friend Vec4 MultiplyAdd( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c ) { return Vec4( _mm_add_ps( _mm_mul_ps( a.m_v, b.m_v ), c.m_v ) ); } //! Returns -( a*b - c ) friend Vec4 NegativeMultiplySubtract( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c ) { return Vec4( _mm_sub_ps( c.m_v, _mm_mul_ps( a.m_v, b.m_v ) ) ); } friend Vec4 Reciprocal( Vec4::Arg v ) { // get the reciprocal estimate __m128 estimate = _mm_rcp_ps( v.m_v ); // one round of Newton-Rhaphson refinement __m128 diff = _mm_sub_ps( _mm_set1_ps( 1.0f ), _mm_mul_ps( estimate, v.m_v ) ); return Vec4( _mm_add_ps( _mm_mul_ps( diff, estimate ), estimate ) ); } friend Vec4 Min( Vec4::Arg left, Vec4::Arg right ) { return Vec4( _mm_min_ps( left.m_v, right.m_v ) ); } friend Vec4 Max( Vec4::Arg left, Vec4::Arg right ) { return Vec4( _mm_max_ps( left.m_v, right.m_v ) ); } friend Vec4 Truncate( Vec4::Arg v ) { #if ( SQUISH_USE_SSE == 1 ) // convert to ints __m128 input = v.m_v; __m64 lo = _mm_cvttps_pi32( input ); __m64 hi = _mm_cvttps_pi32( _mm_movehl_ps( input, input ) ); // convert to floats __m128 part = _mm_movelh_ps( input, _mm_cvtpi32_ps( input, hi ) ); __m128 truncated = _mm_cvtpi32_ps( part, lo ); // clear out the MMX multimedia state to allow FP calls later _mm_empty(); return Vec4( truncated ); #else // use SSE2 instructions return Vec4( _mm_cvtepi32_ps( _mm_cvttps_epi32( v.m_v ) ) ); #endif } friend bool CompareAnyLessThan( Vec4::Arg left, Vec4::Arg right ) { __m128 bits = _mm_cmplt_ps( left.m_v, right.m_v ); int value = _mm_movemask_ps( bits ); return value != 0; } private: __m128 m_v; }; } // namespace squish #endif // ndef SQUISH_SIMD_SSE_H openimageio-1.7.17~dfsg0.orig/src/hdr.imageio/0000755000175000017500000000000013151711064017302 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/hdr.imageio/rgbe.h0000644000175000017500000000472413151711064020401 0ustar mfvmfv#ifndef _H_RGBE #define _H_RGBE /* THIS CODE CARRIES NO GUARANTEE OF USABILITY OR FITNESS FOR ANY PURPOSE. * WHILE THE AUTHORS HAVE TRIED TO ENSURE THE PROGRAM WORKS CORRECTLY, * IT IS STRICTLY USE AT YOUR OWN RISK. */ /* utility for reading and writing Ward's rgbe image format. See rgbe.txt file for more details. */ #include #include "OpenImageIO/imageio.h" OIIO_PLUGIN_NAMESPACE_BEGIN typedef struct { int valid; /* indicate which fields are valid */ char programtype[16]; /* listed at beginning of file to identify it * after "#?". defaults to "RGBE" */ float gamma; /* image has already been gamma corrected with * given gamma. defaults to 1.0 (no correction) */ float exposure; /* a value of 1.0 in an image corresponds to * watts/steradian/m^2. * defaults to 1.0 */ int orientation; /* Orientation of the image. Use the same coded * values as the TIFF and JPEG/JFIF/EXIF specs. * defaults to 1 (-Y +X) * (added by Larry Gritz, 7/2008) */ } rgbe_header_info; /* flags indicating which fields in an rgbe_header_info are valid */ #define RGBE_VALID_PROGRAMTYPE 0x01 #define RGBE_VALID_GAMMA 0x02 #define RGBE_VALID_EXPOSURE 0x04 #define RGBE_VALID_ORIENTATION 0x08 /* return codes for rgbe routines */ #define RGBE_RETURN_SUCCESS 0 #define RGBE_RETURN_FAILURE -1 /* read or write headers */ /* you may set rgbe_header_info to null if you want to */ int RGBE_WriteHeader(FILE *fp, int width, int height, rgbe_header_info *info, char *errbuf=NULL); int RGBE_ReadHeader(FILE *fp, int *width, int *height, rgbe_header_info *info, char *errbuf=NULL); /* read or write pixels */ /* can read or write pixels in chunks of any size including single pixels*/ int RGBE_WritePixels(FILE *fp, float *data, int numpixels, char *errbuf=NULL); int RGBE_ReadPixels(FILE *fp, float *data, int numpixels, char *errbuf=NULL); /* read or write run length encoded files */ /* must be called to read or write whole scanlines */ int RGBE_WritePixels_RLE(FILE *fp, float *data, int scanline_width, int num_scanlines, char *errbuf=NULL); int RGBE_ReadPixels_RLE(FILE *fp, float *data, int scanline_width, int num_scanlines, char *errbuf=NULL); OIIO_PLUGIN_NAMESPACE_END #endif /* _H_RGBE */ openimageio-1.7.17~dfsg0.orig/src/hdr.imageio/CMakeLists.txt0000644000175000017500000000006613151711064022044 0ustar mfvmfvadd_oiio_plugin (rgbe.cpp hdrinput.cpp hdroutput.cpp) openimageio-1.7.17~dfsg0.orig/src/hdr.imageio/hdrinput.cpp0000644000175000017500000001444313151711064021651 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include "OpenImageIO/imageio.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" #include "rgbe.h" OIIO_PLUGIN_NAMESPACE_BEGIN ///////////////////////////////////////////////////////////////////////////// // .hdr / .rgbe files - HDR files from Radiance // // General info on the hdr/rgbe format can be found at: // http://local.wasp.uwa.edu.au/~pbourke/dataformats/pic/ // The source code in rgbe.{h,cpp} originally came from: // http://www.graphics.cornell.edu/~bjw/rgbe.html // But it's been modified in several minor ways by LG. // Also see Greg Ward's "Real Pixels" chapter in Graphics Gems II for an // explanation of the encoding that's used in Radiance rgba files. ///////////////////////////////////////////////////////////////////////////// class HdrInput : public ImageInput { public: HdrInput () { init(); } virtual ~HdrInput () { close(); } virtual const char * format_name (void) const { return "hdr"; } virtual bool open (const std::string &name, ImageSpec &spec); virtual bool read_native_scanline (int y, int z, void *data); virtual bool close (); virtual int current_subimage (void) const { return m_subimage; } virtual bool seek_subimage (int subimage, int miplevel, ImageSpec &newspec); private: std::string m_filename; ///< File name FILE *m_fd; ///< The open file handle int m_subimage; ///< What subimage are we looking at? int m_next_scanline; ///< Next scanline to read char rgbe_error[1024]; ///< Buffer for RGBE library error msgs void init () { m_fd = NULL; m_subimage = -1; m_next_scanline = 0; } }; // Export version number and create function symbols OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT int hdr_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* hdr_imageio_library_version () { return NULL; } OIIO_EXPORT ImageInput *hdr_input_imageio_create () { return new HdrInput; } OIIO_EXPORT const char *hdr_input_extensions[] = { "hdr", "rgbe", NULL }; OIIO_PLUGIN_EXPORTS_END bool HdrInput::open (const std::string &name, ImageSpec &newspec) { m_filename = name; return seek_subimage (0, 0, newspec); } bool HdrInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec) { // HDR doesn't support multiple subimages or mipmaps if (subimage != 0 || miplevel != 0) return false; // Skip the hard work if we're already on the requested subimage if (subimage == current_subimage()) { newspec = spec(); return true; } close(); // Check that file exists and can be opened m_fd = Filesystem::fopen (m_filename, "rb"); if (m_fd == NULL) { error ("Could not open file \"%s\"", m_filename.c_str()); return false; } rgbe_header_info h; int width, height; int r = RGBE_ReadHeader (m_fd, &width, &height, &h, rgbe_error); if (r != RGBE_RETURN_SUCCESS) { error ("%s", rgbe_error); close (); return false; } m_spec = ImageSpec (width, height, 3, TypeDesc::FLOAT); if (h.valid & RGBE_VALID_GAMMA) m_spec.attribute ("oiio:Gamma", h.gamma); if (h.valid & RGBE_VALID_ORIENTATION) m_spec.attribute ("Orientation", h.orientation); // FIXME -- should we do anything about exposure, software, // pixaspect, primaries? (N.B. rgbe.c doesn't even handle most of them) m_subimage = subimage; m_next_scanline = 0; newspec = m_spec; return true; } bool HdrInput::read_native_scanline (int y, int z, void *data) { if (m_next_scanline > y) { // User is trying to read an earlier scanline than the one we're // up to. Easy fix: close the file and re-open. ImageSpec dummyspec; int subimage = current_subimage(); int miplevel = current_miplevel(); if (! close () || ! open (m_filename, dummyspec) || ! seek_subimage (subimage, miplevel, dummyspec)) return false; // Somehow, the re-open failed assert (m_next_scanline == 0 && current_subimage() == subimage && current_miplevel() == miplevel); } while (m_next_scanline <= y) { // Keep reading until we're read the scanline we really need int r = RGBE_ReadPixels_RLE (m_fd, (float *)data, m_spec.width, 1, rgbe_error); ++m_next_scanline; if (r != RGBE_RETURN_SUCCESS) { error ("%s", rgbe_error); return false; } } return true; } bool HdrInput::close () { if (m_fd) fclose (m_fd); init (); // Reset to initial state return true; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/hdr.imageio/hdroutput.cpp0000644000175000017500000001447113151711064022053 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include "OpenImageIO/imageio.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/strutil.h" #include "rgbe.h" OIIO_PLUGIN_NAMESPACE_BEGIN class HdrOutput : public ImageOutput { public: HdrOutput () { init(); } virtual ~HdrOutput () { close(); } virtual const char * format_name (void) const { return "hdr"; } virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); virtual bool close (); private: FILE *m_fd; std::vector scratch; char rgbe_error[1024]; ///< Buffer for RGBE library error msgs std::vector m_tilebuffer; void init (void) { m_fd = NULL; } }; OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *hdr_output_imageio_create () { return new HdrOutput; } OIIO_EXPORT const char *hdr_output_extensions[] = { "hdr", "rgbe", NULL }; OIIO_PLUGIN_EXPORTS_END bool HdrOutput::open (const std::string &name, const ImageSpec &newspec, OpenMode mode) { if (mode != Create) { error ("%s does not support subimages or MIP levels", format_name()); return false; } // Save spec for later use m_spec = newspec; // HDR always behaves like floating point m_spec.set_format (TypeDesc::FLOAT); // Check for things HDR can't support if (m_spec.nchannels != 3) { error ("HDR can only support 3-channel images"); return false; } if (m_spec.width < 1 || m_spec.height < 1) { error ("Image resolution must be at least 1x1, you asked for %d x %d", m_spec.width, m_spec.height); return false; } if (m_spec.depth < 1) m_spec.depth = 1; if (m_spec.depth > 1) { error ("%s does not support volume images (depth > 1)", format_name()); return false; } m_spec.set_format (TypeDesc::FLOAT); // Native rgbe is float32 only m_fd = Filesystem::fopen (name, "wb"); if (m_fd == NULL) { error ("Unable to open file"); return false; } rgbe_header_info h; h.valid = 0; // Most readers seem to think that rgbe files are valid only if they // identify themselves as from "RADIANCE". h.valid |= RGBE_VALID_PROGRAMTYPE; Strutil::safe_strcpy (h.programtype, "RADIANCE", sizeof(h.programtype)); ImageIOParameter *p; p = m_spec.find_attribute ("Orientation", TypeDesc::INT); if (p) { h.valid |= RGBE_VALID_ORIENTATION; h.orientation = * (int *)p->data(); } // FIXME -- should we do anything about gamma, exposure, software, // pixaspect, primaries? (N.B. rgbe.c doesn't even handle most of them) int r = RGBE_WriteHeader (m_fd, m_spec.width, m_spec.height, &h, rgbe_error); if (r != RGBE_RETURN_SUCCESS) error ("%s", rgbe_error); // If user asked for tiles -- which this format doesn't support, emulate // it by buffering the whole image. if (m_spec.tile_width && m_spec.tile_height) m_tilebuffer.resize (m_spec.image_bytes()); return true; } bool HdrOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { data = to_native_scanline (format, data, xstride, scratch); int r = RGBE_WritePixels_RLE (m_fd, (float *)data, m_spec.width, 1, rgbe_error); if (r != RGBE_RETURN_SUCCESS) error ("%s", rgbe_error); return (r == RGBE_RETURN_SUCCESS); } bool HdrOutput::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { // Emulate tiles by buffering the whole image return copy_tile_to_image_buffer (x, y, z, format, data, xstride, ystride, zstride, &m_tilebuffer[0]); } bool HdrOutput::close () { if (! m_fd) { // already closed init (); return true; } bool ok = true; if (m_spec.tile_width) { // We've been emulating tiles; now dump as scanlines. ASSERT (m_tilebuffer.size()); ok &= write_scanlines (m_spec.y, m_spec.y+m_spec.height, 0, m_spec.format, &m_tilebuffer[0]); std::vector().swap (m_tilebuffer); } fclose (m_fd); m_fd = NULL; init(); return ok; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/hdr.imageio/rgbe.cpp0000644000175000017500000004030613151711064020730 0ustar mfvmfv/* THIS CODE CARRIES NO GUARANTEE OF USABILITY OR FITNESS FOR ANY PURPOSE. * WHILE THE AUTHORS HAVE TRIED TO ENSURE THE PROGRAM WORKS CORRECTLY, * IT IS STRICTLY USE AT YOUR OWN RISK. */ #include "rgbe.h" #include #include #include #include /* This file contains code to read and write four byte rgbe file format developed by Greg Ward. It handles the conversions between rgbe and pixels consisting of floats. The data is assumed to be an array of floats. By default there are three floats per pixel in the order red, green, blue. (RGBE_DATA_??? values control this.) Only the mimimal header reading and writing is implemented. Each routine does error checking and will return a status value as defined below. This code is intended as a skeleton so feel free to modify it to suit your needs. (Place notice here if you modified the code.) posted to http://www.graphics.cornell.edu/~bjw/ written by Bruce Walter (bjw@graphics.cornell.edu) 5/26/95 based on code written by Greg Ward Various modifications by Larry Gritz (lg@larrygritz.com), July 1998: 1. Fix RGBE_ReadHeader to handle changes in the order of header fields that can be found in some .hdr files on the net. 2. Correctly read and write images of all 8 orientations (not just -Y+X) and specify the orientation in the rgbe_header_info structure. 3. Change the default programtype string from "RGBE" to "RADIANCE" since I noticed that some hdr/rgbe readers (including OS X's preivew util) will only accept "RADIANCE" as the programtype. */ #if defined(_CPLUSPLUS) || defined(__cplusplus) /* define if your compiler understands inline commands */ #define INLINE inline #else #define INLINE #endif /* offsets to red, green, and blue components in a data (float) pixel */ #define RGBE_DATA_RED 0 #define RGBE_DATA_GREEN 1 #define RGBE_DATA_BLUE 2 /* number of floats per pixel */ #define RGBE_DATA_SIZE 3 OIIO_PLUGIN_NAMESPACE_BEGIN enum rgbe_error_codes { rgbe_read_error, rgbe_write_error, rgbe_format_error, rgbe_memory_error, }; /* default error routine. change this to change error handling */ static int rgbe_error(int rgbe_error_code, const char *msg, char *errbuf) { switch (rgbe_error_code) { case rgbe_read_error: if (errbuf) strcpy (errbuf, "RGBE read error"); else perror("RGBE read error"); break; case rgbe_write_error: if (errbuf) strcpy (errbuf, "RGBE write error"); else perror("RGBE write error"); break; case rgbe_format_error: if (errbuf) sprintf(errbuf,"RGBE bad file format: %s\n", msg); else fprintf(stderr,"RGBE bad file format: %s\n",msg); break; default: case rgbe_memory_error: if (errbuf) sprintf(errbuf,"RGBE error: %s\n",msg); else fprintf(stderr,"RGBE error: %s\n",msg); break; } return RGBE_RETURN_FAILURE; } /* standard conversion from float pixels to rgbe pixels */ /* note: you can remove the "inline"s if your compiler complains about it */ static INLINE void float2rgbe(unsigned char rgbe[4], float red, float green, float blue) { float v; int e; v = red; if (green > v) v = green; if (blue > v) v = blue; if (v < 1e-32) { rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; } else { v = frexpf(v,&e) * 256.0f/v; rgbe[0] = (unsigned char) (red * v); rgbe[1] = (unsigned char) (green * v); rgbe[2] = (unsigned char) (blue * v); rgbe[3] = (unsigned char) (e + 128); } } /* standard conversion from rgbe to float pixels */ /* note: Ward uses ldexp(col+0.5,exp-(128+8)). However we wanted pixels */ /* in the range [0,1] to map back into the range [0,1]. */ static INLINE void rgbe2float(float *red, float *green, float *blue, unsigned char rgbe[4]) { float f; if (rgbe[3]) { /*nonzero pixel*/ f = ldexpf(1.0f,rgbe[3]-(int)(128+8)); *red = rgbe[0] * f; *green = rgbe[1] * f; *blue = rgbe[2] * f; } else *red = *green = *blue = 0.0; } /* default minimal header. modify if you want more information in header */ int RGBE_WriteHeader(FILE *fp, int width, int height, rgbe_header_info *info, char *errbuf) { const char *programtype = "RADIANCE"; /* N.B. from Larry Gritz: * Plenty of readers will refuse to read .rgbe/.hdr files if their * program type is not "RADIANCE". So I changed the default * programtype from Bruce Walter's original "RGBE", which many readers * refuse to accept. (Mac OS X's "preview" utility is one such reader!) */ if (info && (info->valid & RGBE_VALID_PROGRAMTYPE)) programtype = info->programtype; if (fprintf(fp,"#?%s\n",programtype) < 0) return rgbe_error(rgbe_write_error,NULL, errbuf); /* The #? is to identify file type, the programtype is optional. */ if (info && (info->valid & RGBE_VALID_GAMMA)) { if (fprintf(fp,"GAMMA=%g\n",info->gamma) < 0) return rgbe_error(rgbe_write_error,NULL, errbuf); } if (info && (info->valid & RGBE_VALID_EXPOSURE)) { if (fprintf(fp,"EXPOSURE=%g\n",info->exposure) < 0) return rgbe_error(rgbe_write_error,NULL, errbuf); } if (fprintf(fp,"FORMAT=32-bit_rle_rgbe\n\n") < 0) return rgbe_error(rgbe_write_error,NULL, errbuf); if (fprintf(fp, "-Y %d +X %d\n", height, width) < 0) return rgbe_error(rgbe_write_error,NULL, errbuf); return RGBE_RETURN_SUCCESS; } /* minimal header reading. modify if you want to parse more information */ int RGBE_ReadHeader(FILE *fp, int *width, int *height, rgbe_header_info *info, char *errbuf) { char buf[128]; float tempf; size_t i; if (info) { info->valid = 0; info->programtype[0] = 0; info->gamma = info->exposure = 1.0; } if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == NULL) return rgbe_error(rgbe_read_error,NULL, errbuf); if ((buf[0] != '#')||(buf[1] != '?')) { /* if you want to require the magic token then uncomment the next line */ /*return rgbe_error(rgbe_format_error,"bad initial token"); */ } else if (info) { info->valid |= RGBE_VALID_PROGRAMTYPE; for(i=0;i<(int)sizeof(info->programtype)-1;i++) { if ((buf[i+2] == 0) || isspace(buf[i+2])) break; info->programtype[i] = buf[i+2]; } info->programtype[i] = 0; if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0) return rgbe_error(rgbe_read_error,NULL, errbuf); } bool found_FORMAT_line = false; for(;;) { if ((buf[0] == 0)||(buf[0] == '\n')) { if (found_FORMAT_line) break; return rgbe_error(rgbe_format_error,"no FORMAT specifier found", errbuf); } else if (strcmp(buf,"FORMAT=32-bit_rle_rgbe\n") == 0) { found_FORMAT_line = true; /* LG says no: break; // format found so break out of loop */ } else if (info && (sscanf(buf,"GAMMA=%g",&tempf) == 1)) { info->gamma = tempf; info->valid |= RGBE_VALID_GAMMA; } else if (info && (sscanf(buf,"EXPOSURE=%g",&tempf) == 1)) { info->exposure = tempf; info->valid |= RGBE_VALID_EXPOSURE; } if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0) return rgbe_error(rgbe_read_error,NULL, errbuf); } if (strcmp(buf,"\n") != 0) { printf ("Found '%s'\n", buf); return rgbe_error(rgbe_format_error, "missing blank line after FORMAT specifier", errbuf); } if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0) return rgbe_error(rgbe_read_error,NULL, errbuf); if (sscanf(buf,"-Y %d +X %d",height,width) == 2) { if (info) { info->orientation = 1; info->valid |= RGBE_VALID_ORIENTATION; } } else if (sscanf(buf,"-Y %d -X %d",height,width) == 2) { if (info) { info->orientation = 2; info->valid |= RGBE_VALID_ORIENTATION; } } else if (sscanf(buf,"+Y %d -X %d",height,width) == 2) { if (info) { info->orientation = 3; info->valid |= RGBE_VALID_ORIENTATION; } } else if (sscanf(buf,"+Y %d +X %d",height,width) == 2) { if (info) { info->orientation = 4; info->valid |= RGBE_VALID_ORIENTATION; } } else if (sscanf(buf,"+X %d -Y %d",height,width) == 2) { if (info) { info->orientation = 5; info->valid |= RGBE_VALID_ORIENTATION; } } else if (sscanf(buf,"+X %d +Y %d",height,width) == 2) { if (info) { info->orientation = 6; info->valid |= RGBE_VALID_ORIENTATION; } } else if (sscanf(buf,"-X %d +Y %d",height,width) == 2) { if (info) { info->orientation = 7; info->valid |= RGBE_VALID_ORIENTATION; } } else if (sscanf(buf,"-X %d -Y %d",height,width) == 2) { if (info) { info->orientation = 8; info->valid |= RGBE_VALID_ORIENTATION; } } else { return rgbe_error(rgbe_format_error,"missing image size specifier", errbuf); } return RGBE_RETURN_SUCCESS; } /* simple write routine that does not use run length encoding */ /* These routines can be made faster by allocating a larger buffer and fread-ing and fwrite-ing the data in larger chunks */ int RGBE_WritePixels(FILE *fp, float *data, int numpixels, char *errbuf) { unsigned char rgbe[4]; while (numpixels-- > 0) { float2rgbe(rgbe,data[RGBE_DATA_RED], data[RGBE_DATA_GREEN],data[RGBE_DATA_BLUE]); data += RGBE_DATA_SIZE; if (fwrite(rgbe, sizeof(rgbe), 1, fp) < 1) return rgbe_error(rgbe_write_error,NULL, errbuf); } return RGBE_RETURN_SUCCESS; } /* simple read routine. will not correctly handle run length encoding */ int RGBE_ReadPixels(FILE *fp, float *data, int numpixels, char *errbuf) { unsigned char rgbe[4]; while(numpixels-- > 0) { if (fread(rgbe, sizeof(rgbe), 1, fp) < 1) return rgbe_error(rgbe_read_error,NULL, errbuf); rgbe2float(&data[RGBE_DATA_RED],&data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE],rgbe); data += RGBE_DATA_SIZE; } return RGBE_RETURN_SUCCESS; } /* The code below is only needed for the run-length encoded files. */ /* Run length encoding adds considerable complexity but does */ /* save some space. For each scanline, each channel (r,g,b,e) is */ /* encoded separately for better compression. */ static int RGBE_WriteBytes_RLE(FILE *fp, unsigned char *data, int numbytes, char *errbuf) { #define MINRUNLENGTH 4 int cur, beg_run, run_count, old_run_count, nonrun_count; unsigned char buf[2]; cur = 0; while(cur < numbytes) { beg_run = cur; /* find next run of length at least 4 if one exists */ run_count = old_run_count = 0; while((run_count < MINRUNLENGTH) && (beg_run < numbytes)) { beg_run += run_count; old_run_count = run_count; run_count = 1; while((data[beg_run] == data[beg_run + run_count]) && (beg_run + run_count < numbytes) && (run_count < 127)) run_count++; } /* if data before next big run is a short run then write it as such */ if ((old_run_count > 1)&&(old_run_count == beg_run - cur)) { buf[0] = 128 + old_run_count; /*write short run*/ buf[1] = data[cur]; if (fwrite(buf,sizeof(buf[0])*2,1,fp) < 1) return rgbe_error(rgbe_write_error,NULL, errbuf); cur = beg_run; } /* write out bytes until we reach the start of the next run */ while(cur < beg_run) { nonrun_count = beg_run - cur; if (nonrun_count > 128) nonrun_count = 128; buf[0] = nonrun_count; if (fwrite(buf,sizeof(buf[0]),1,fp) < 1) return rgbe_error(rgbe_write_error,NULL, errbuf); if (fwrite(&data[cur],sizeof(data[0])*nonrun_count,1,fp) < 1) return rgbe_error(rgbe_write_error,NULL, errbuf); cur += nonrun_count; } /* write out next run if one was found */ if (run_count >= MINRUNLENGTH) { buf[0] = 128 + run_count; buf[1] = data[beg_run]; if (fwrite(buf,sizeof(buf[0])*2,1,fp) < 1) return rgbe_error(rgbe_write_error,NULL, errbuf); cur += run_count; } } return RGBE_RETURN_SUCCESS; #undef MINRUNLENGTH } int RGBE_WritePixels_RLE(FILE *fp, float *data, int scanline_width, int num_scanlines, char *errbuf) { unsigned char rgbe[4]; unsigned char *buffer; int i, err; if ((scanline_width < 8)||(scanline_width > 0x7fff)) /* run length encoding is not allowed so write flat*/ return RGBE_WritePixels(fp,data,scanline_width*num_scanlines); buffer = (unsigned char *)malloc(sizeof(unsigned char)*4*scanline_width); if (buffer == NULL) /* no buffer space so write flat */ return RGBE_WritePixels(fp,data,scanline_width*num_scanlines); while(num_scanlines-- > 0) { rgbe[0] = 2; rgbe[1] = 2; rgbe[2] = scanline_width >> 8; rgbe[3] = scanline_width & 0xFF; if (fwrite(rgbe, sizeof(rgbe), 1, fp) < 1) { free(buffer); return rgbe_error(rgbe_write_error,NULL, errbuf); } for(i=0;i 0x7fff)) /* run length encoding is not allowed so read flat*/ return RGBE_ReadPixels(fp,data,scanline_width*num_scanlines); scanline_buffer = NULL; /* read in each successive scanline */ while(num_scanlines > 0) { if (fread(rgbe,sizeof(rgbe),1,fp) < 1) { free(scanline_buffer); return rgbe_error(rgbe_read_error,NULL, errbuf); } if ((rgbe[0] != 2)||(rgbe[1] != 2)||(rgbe[2] & 0x80)) { /* this file is not run length encoded */ rgbe2float(&data[0],&data[1],&data[2],rgbe); data += RGBE_DATA_SIZE; free(scanline_buffer); return RGBE_ReadPixels(fp,data,scanline_width*num_scanlines-1); } if ((((int)rgbe[2])<<8 | rgbe[3]) != scanline_width) { free(scanline_buffer); return rgbe_error(rgbe_format_error,"wrong scanline width", errbuf); } if (scanline_buffer == NULL) scanline_buffer = (unsigned char *) malloc(sizeof(unsigned char)*4*scanline_width); if (scanline_buffer == NULL) return rgbe_error(rgbe_memory_error,"unable to allocate buffer space", errbuf); ptr = &scanline_buffer[0]; /* read each of the four channels for the scanline into the buffer */ for(i=0;i<4;i++) { ptr_end = &scanline_buffer[(i+1)*scanline_width]; while(ptr < ptr_end) { if (fread(buf,sizeof(buf[0])*2,1,fp) < 1) { free(scanline_buffer); return rgbe_error(rgbe_read_error,NULL, errbuf); } if (buf[0] > 128) { /* a run of the same value */ count = buf[0]-128; if ((count == 0)||(count > ptr_end - ptr)) { free(scanline_buffer); return rgbe_error(rgbe_format_error,"bad scanline data", errbuf); } while(count-- > 0) *ptr++ = buf[1]; } else { /* a non-run */ count = buf[0]; if ((count == 0)||(count > ptr_end - ptr)) { free(scanline_buffer); return rgbe_error(rgbe_format_error,"bad scanline data", errbuf); } *ptr++ = buf[1]; if (--count > 0) { if (fread(ptr,sizeof(*ptr)*count,1,fp) < 1) { free(scanline_buffer); return rgbe_error(rgbe_read_error,NULL, errbuf); } ptr += count; } } } } /* now convert data from buffer into floats */ for(i=0;i #include "OpenImageIO/typedesc.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/fmath.h" OIIO_PLUGIN_NAMESPACE_BEGIN class PtexOutput : public ImageOutput { public: PtexOutput (); virtual ~PtexOutput (); virtual const char * format_name (void) const { return "ptex"; } virtual int supports (string_view feature) const; virtual bool open (const std::string &name, const ImageSpec &spec, ImageOutput::OpenMode mode); virtual bool close (); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); private: // Initialize private members to pre-opened state void init (void) { } }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *ptex_output_imageio_create () { return new PtexOutput; } // OIIO_EXPORT int ptex_imageio_version = OIIO_PLUGIN_VERSION; // it's in ptexinput.cpp OIIO_EXPORT const char * ptex_output_extensions[] = { "ptex", "ptx", NULL }; OIIO_PLUGIN_EXPORTS_END PtexOutput::PtexOutput () { init (); } PtexOutput::~PtexOutput () { // Close, if not already done. close (); } int PtexOutput::supports (string_view feature) const { return (feature == "tiles" || feature == "multiimage" || feature == "mipmap" || feature == "alpha" || feature == "nchannels" || feature == "arbitrary_metadata" || feature == "exif" // Because of arbitrary_metadata || feature == "iptc"); // Because of arbitrary_metadata } bool PtexOutput::open (const std::string &name, const ImageSpec &userspec, ImageOutput::OpenMode mode) { error ("Ptex writer is not implemented yet, please poke Larry."); return false; } bool PtexOutput::close () { init(); return true; } bool PtexOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { error ("Ptex writer is not implemented yet, please poke Larry."); return false; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/ptex.imageio/ptexinput.cpp0000644000175000017500000002255213151711064022257 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/fmath.h" OIIO_PLUGIN_NAMESPACE_BEGIN class PtexInput : public ImageInput { public: PtexInput () : m_ptex(NULL) { init(); } virtual ~PtexInput () { close(); } virtual const char * format_name (void) const { return "ptex"; } virtual int supports (string_view feature) const { return (feature == "arbitrary_metadata" || feature == "exif" // Because of arbitrary_metadata || feature == "iptc"); // Because of arbitrary_metadata } virtual bool open (const std::string &name, ImageSpec &newspec); virtual bool close (); virtual int current_subimage (void) const { return m_subimage; } virtual int current_miplevel (void) const { return m_miplevel; } virtual bool seek_subimage (int subimage, int miplevel, ImageSpec &newspec); virtual bool read_native_scanline (int y, int z, void *data); virtual bool read_native_tile (int x, int y, int z, void *data); private: PtexTexture *m_ptex; int m_subimage; int m_miplevel; int m_numFaces; Ptex::Res m_faceres; Ptex::Res m_mipfaceres; Ptex::Res m_tileres; bool m_isTiled; bool m_hasMipMaps; int m_ntilesu; /// Reset everything to initial state /// void init () { if (m_ptex) m_ptex->release(); m_ptex = NULL; m_subimage = -1; m_miplevel = -1; } }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageInput *ptex_input_imageio_create () { return new PtexInput; } OIIO_EXPORT int ptex_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* ptex_imageio_library_version () { return ustring::format("Ptex %d.%d", PtexLibraryMajorVersion, PtexLibraryMinorVersion).c_str(); } OIIO_EXPORT const char * ptex_input_extensions[] = { "ptex", "ptx", NULL }; OIIO_PLUGIN_EXPORTS_END bool PtexInput::open (const std::string &name, ImageSpec &newspec) { Ptex::String perr; m_ptex = PtexTexture::open (name.c_str(), perr, true /*premultiply*/); if (! perr.empty()) { if (m_ptex) { m_ptex->release (); m_ptex = NULL; } error ("%s", perr.c_str()); return false; } m_numFaces = m_ptex->numFaces(); m_hasMipMaps = m_ptex->hasMipMaps(); bool ok = seek_subimage (0, 0, newspec); newspec = spec (); return ok; } bool PtexInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec) { if (m_subimage == subimage && m_miplevel == miplevel) return true; // Already fine if (subimage < 0 || subimage >= m_numFaces) return false; m_subimage = subimage; const Ptex::FaceInfo &pface = m_ptex->getFaceInfo (subimage); m_faceres = pface.res; int nmiplevels = std::max(m_faceres.ulog2,m_faceres.vlog2) + 1; if (miplevel < 0 || miplevel > nmiplevels-1) return false; m_miplevel = miplevel; m_mipfaceres = Ptex::Res (std::max(0,m_faceres.ulog2-miplevel), std::max(0,m_faceres.vlog2-miplevel)); TypeDesc format = TypeDesc::UNKNOWN; switch (m_ptex->dataType()) { case Ptex::dt_uint8 : format = TypeDesc::UINT8; break; case Ptex::dt_uint16 : format = TypeDesc::UINT16; break; case Ptex::dt_half : format = TypeDesc::HALF; break; case Ptex::dt_float : format = TypeDesc::FLOAT; break; default: error ("Ptex with unknown data format"); return false; } m_spec = ImageSpec (std::max (1, m_faceres.u() >> miplevel), std::max (1, m_faceres.v() >> miplevel), m_ptex->numChannels(), format); m_spec.alpha_channel = m_ptex->alphaChannel(); if (m_ptex->meshType() == Ptex::mt_triangle) m_spec.attribute ("ptex:meshType", "triangle"); else m_spec.attribute ("ptex:meshType", "quad"); if (m_ptex->hasEdits()) m_spec.attribute ("ptex:hasEdits", (int)1); PtexFaceData *facedata = m_ptex->getData (m_subimage, m_faceres); m_isTiled = facedata->isTiled(); if (m_isTiled) { m_tileres = facedata->tileRes(); m_spec.tile_width = m_tileres.u(); m_spec.tile_height = m_tileres.v(); m_ntilesu = m_faceres.ntilesu (m_tileres); } else { // Always make it look tiled m_spec.tile_width = m_spec.width; m_spec.tile_height = m_spec.height; } std::string wrapmode; if (m_ptex->uBorderMode() == Ptex::m_clamp) wrapmode = "clamp"; else if (m_ptex->uBorderMode() == Ptex::m_black) wrapmode = "black"; else // if (m_ptex->uBorderMode() == Ptex::m_periodic) wrapmode = "periodic"; wrapmode += ","; if (m_ptex->uBorderMode() == Ptex::m_clamp) wrapmode += "clamp"; else if (m_ptex->uBorderMode() == Ptex::m_black) wrapmode += "black"; else // if (m_ptex->uBorderMode() == Ptex::m_periodic) wrapmode += "periodic"; m_spec.attribute ("wrapmode", wrapmode); #define GETMETA(pmeta,key,ptype,basetype,typedesc,value) \ { \ const ptype *v; \ int count; \ pmeta->getValue (key, v, count); \ typedesc = TypeDesc (basetype, count); \ value = (const void *) v; \ } PtexMetaData *pmeta = m_ptex->getMetaData(); if (pmeta) { int n = pmeta->numKeys(); for (int i = 0; i < n; ++i) { const char *key = NULL; Ptex::MetaDataType ptype; pmeta->getKey (i, key, ptype); ASSERT (key); const char *vchar; const void *value; TypeDesc typedesc; switch (ptype) { case Ptex::mdt_string: pmeta->getValue (key, vchar); value = &vchar; typedesc = TypeDesc::STRING; break; case Ptex::mdt_int8: GETMETA (pmeta, key, int8_t, TypeDesc::INT8, typedesc, value); break; case Ptex::mdt_int16: GETMETA (pmeta, key, int16_t, TypeDesc::INT16, typedesc, value); break; case Ptex::mdt_int32: GETMETA (pmeta, key, int32_t, TypeDesc::INT32, typedesc, value); break; case Ptex::mdt_float: GETMETA (pmeta, key, float, TypeDesc::FLOAT, typedesc, value); break; case Ptex::mdt_double: GETMETA (pmeta, key, double, TypeDesc::DOUBLE, typedesc, value); break; default: continue; } m_spec.attribute (key, typedesc, value); } pmeta->release(); } facedata->release(); newspec = m_spec; return true; } bool PtexInput::close () { init(); // Reset to initial state, including closing any open files return true; } bool PtexInput::read_native_scanline (int y, int z, void *data) { return false; // Not scanline oriented } bool PtexInput::read_native_tile (int x, int y, int z, void *data) { PtexFaceData *facedata = m_ptex->getData (m_subimage, m_mipfaceres); PtexFaceData *f = facedata; if (m_isTiled) { int tileno = y/m_spec.tile_height * m_ntilesu + x/m_spec.tile_width; f = facedata->getTile (tileno); } bool ok = true; void *tiledata = f->getData(); if (tiledata) { memcpy (data, tiledata, m_spec.tile_bytes()); } else { ok = false; } if (m_isTiled) f->release(); facedata->release(); return ok; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/nuke/0000755000175000017500000000000013151711064016056 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/nuke/CMakeLists.txt0000644000175000017500000000040313151711064020613 0ustar mfvmfvif (USE_NUKE) find_package (Nuke ${NUKE_VERSION}) if (NUKE_FOUND) add_subdirectory (txReader) add_subdirectory (txWriter) else () message (STATUS "Could not Find Nuke. Skipping build of Nuke plugins.") endif () endif() openimageio-1.7.17~dfsg0.orig/src/nuke/txWriter/0000755000175000017500000000000013151711064017706 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/nuke/txWriter/CMakeLists.txt0000644000175000017500000000203213151711064022443 0ustar mfvmfvinclude_directories (${NUKE_INCLUDE_DIRS}) link_directories (${NUKE_LIBRARY_DIRS}) add_library (txWriter SHARED txWriter.cpp) target_link_libraries (txWriter DDImage OpenImageIO) if (WIN32) add_definitions (/DFN_OS_WINDOWS /DUSE_GLEW) set_target_properties (txWriter PROPERTIES PREFIX "" COMPILE_FLAGS "/wd4251 /W3 /O2 /MD /GL /GR /GF" LINK_FLAGS "/machine:x64 /SUBSYSTEM:WINDOWS /dll") elseif (APPLE) target_link_libraries (txWriter GLEW) add_definitions (-DUSE_GLEW) set_target_properties (txWriter PROPERTIES PREFIX "" COMPILE_FLAGS "-arch x86_64" LINK_FLAGS "-arch x86_64 -bundle -framework QuartzCore -framework IOKit -framework CoreFoundation -framework Carbon -framework ApplicationServices -framework OpenGL -framework AGL") else () add_definitions (-DUSE_GLEW) set_target_properties (txWriter PROPERTIES PREFIX "" COMPILE_FLAGS "-fPIC -msse") endif () install (TARGETS txWriter LIBRARY DESTINATION "${LIB_INSTALL_DIR}/nuke") openimageio-1.7.17~dfsg0.orig/src/nuke/txWriter/txWriter.cpp0000644000175000017500000002603113151711064022244 0ustar mfvmfv#include #include "DDImage/Writer.h" #include "DDImage/Thread.h" #include "DDImage/Row.h" #include "OpenImageIO/filter.h" #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" /* * TODO: * - Look into using an ImageBuf iterator to fill the source buffer. * - Support for more than 4 output channels is easy, but we can't currently * set the output channel names in such a way that OIIO will store them in * the output file. * - Could throw Nuke script name and/or tree hash into the metadata * ( iop->getHashOfInputs() ) */ using namespace DD::Image; namespace TxWriterNS { OIIO_NAMESPACE_USING // Limit the available output datatypes (for now, at least). static const TypeDesc::BASETYPE oiioBitDepths[] = {TypeDesc::INT8, TypeDesc::INT16, TypeDesc::INT32, TypeDesc::FLOAT, TypeDesc::DOUBLE}; // Knob values for above bit depths (keep them synced!) static const char* const bitDepthValues[] = {"8-bit integer", "16-bit integer", "32-bit integer", "32-bit float", "64-bit double", NULL}; // Knob values for NaN fix modes static const char* const nanFixValues[] = {"black\tblack", "box3\tbox3 filter", NULL}; // Knob values for "preset" modes static const char* const presetValues[] = {"oiio", "prman", "custom", NULL}; // Knob values for planar configuration static const char* const planarValues[] = {"contig\tcontiguous", "separate", NULL}; // Knob values for texture mode configuration static const char* const txModeValues[] = {"Ordinary 2D texture", "Latitude-longitude environment map", "Latitude-longitude environment map (light probe)", "Shadow texture", NULL}; static const ImageBufAlgo::MakeTextureMode oiiotxMode[] = {ImageBufAlgo::MakeTxTexture, ImageBufAlgo::MakeTxEnvLatl, ImageBufAlgo::MakeTxEnvLatlFromLightProbe, ImageBufAlgo::MakeTxShadow}; bool gTxFiltersInitialized = false; static std::vector gFilterNames; class txWriter : public Writer { int preset_; int tileW_, tileH_; int planarMode_; int txMode_; int bitDepth_; int filter_; bool fixNan_; int nanFixType_; bool checkNan_; bool verbose_; bool stats_; void setChannelNames(ImageSpec& spec, const ChannelSet& channels) { if (channels == Mask_RGB || channels == Mask_RGBA) return; int index = 0; std::ostringstream buf; foreach (z, channels) { if (index > 0) buf << ","; switch (z) { case Chan_Red: buf << "R"; break; case Chan_Green: buf << "G"; break; case Chan_Blue: buf << "B"; break; case Chan_Alpha: buf << "A"; spec.alpha_channel = index; break; case Chan_Z: buf << "Z"; spec.z_channel = index; break; default: buf << getName(z); break; } index++; } spec.attribute("maketx:channelnames", buf.str()); } public: txWriter(Write* iop) : Writer(iop), preset_(0), tileW_(64), tileH_(64), planarMode_(0), txMode_(0), // ordinary 2d texture bitDepth_(3), // float filter_(0), fixNan_(false), nanFixType_(0), checkNan_(true), verbose_(false), stats_(false) { if (!gTxFiltersInitialized) { for (int i = 0, e = Filter2D::num_filters(); i < e; ++i) { FilterDesc d; Filter2D::get_filterdesc (i, &d); gFilterNames.push_back(d.name); }; gFilterNames.push_back(NULL); gTxFiltersInitialized = true; } } void knobs(Knob_Callback cb) { Enumeration_knob(cb, &preset_, &presetValues[0], "preset"); Tooltip(cb, "Choose a preset for various output parameters.\n" "oiio: Tile and planar settings optimized for OIIO.\n" "prman: Tile and planar ettings and metadata safe for " "use with prman."); Knob* k; k = Int_knob(cb, &tileW_, "tile_width", "tile size"); if (cb.makeKnobs()) k->disable(); else if (preset_ == 2) k->enable(); Tooltip(cb, "Tile width"); k = Int_knob(cb, &tileH_, "tile_height", "x"); if (cb.makeKnobs()) k->disable(); else if (preset_ == 2) k->enable(); Tooltip(cb, "Tile height"); ClearFlags(cb, Knob::STARTLINE); k = Enumeration_knob(cb, &planarMode_, &planarValues[0], "planar_config", "planar config"); if (cb.makeKnobs()) k->disable(); else if (preset_ == 2) k->enable(); Tooltip(cb, "Planar mode of the image channels."); SetFlags(cb, Knob::STARTLINE); Enumeration_knob(cb, &txMode_, &txModeValues[0], "tx_mode", "mode"); Tooltip(cb, "What type of texture file we are creating."); Enumeration_knob(cb, &bitDepth_, &bitDepthValues[0], "tx_datatype", "datatype"); Tooltip(cb, "The datatype of the output image."); Enumeration_knob(cb, &filter_, &gFilterNames[0], "tx_filter", "filter"); Tooltip(cb, "The filter used to resize the image when generating mip " "levels."); Bool_knob(cb, &fixNan_, "fix_nan", "fix NaN/Inf pixels"); Tooltip(cb, "Attempt to fix NaN/Inf pixel values in the image."); SetFlags(cb, Knob::STARTLINE); k = Enumeration_knob(cb, &nanFixType_, &nanFixValues[0], "nan_fix_type", ""); if (cb.makeKnobs()) k->disable(); else if (fixNan_) k->enable(); Tooltip(cb, "The method to use to fix NaN/Inf pixel values."); ClearFlags(cb, Knob::STARTLINE); Bool_knob(cb, &checkNan_, "check_nan", "error on NaN/Inf"); Tooltip(cb, "Check for NaN/Inf pixel values in the output image, and " "error if any are found. If this is enabled, the check will be " "run after the NaN fix process."); SetFlags(cb, Knob::STARTLINE); Bool_knob(cb, &verbose_, "verbose"); Tooltip(cb, "Toggle verbose OIIO output."); SetFlags(cb, Knob::STARTLINE); Bool_knob(cb, &stats_, "oiio_stats", "output stats"); Tooltip(cb, "Toggle output of OIIO runtime statistics."); ClearFlags(cb, Knob::STARTLINE); } int knob_changed(Knob* k) { if (k->is("fix_nan")) { iop->knob("nan_fix_type")->enable(fixNan_); return 1; } if (k->is("preset")) { const bool e = preset_ == 2; iop->knob("tile_width")->enable(e); iop->knob("tile_height")->enable(e); iop->knob("planar_config")->enable(e); return 1; } return Writer::knob_changed(k); } void execute() { const int chanCount = num_channels(); ChannelSet channels = channel_mask(chanCount); const bool doAlpha = channels.contains(Chan_Alpha); iop->progressMessage("Preparing image"); input0().request(0, 0, width(), height(), channels, 1); if (aborted()) return; ImageSpec srcSpec(width(), height(), chanCount, TypeDesc::FLOAT); ImageBuf srcBuffer(srcSpec); Row row(0, width()); // Buffer for a channel-interleaved row after output LUT processing std::vector lutBuffer(width() * chanCount); for (int y = 0; y < height(); y++) { iop->progressFraction(double(y) / height() * 0.85); get(height() - y - 1, 0, width(), channels, row); if (aborted()) return; const float* alpha = doAlpha ? row[Chan_Alpha] : NULL; for (int i = 0; i < chanCount; i++) to_float(i, &lutBuffer[i], row[channel(i)], alpha, width(), chanCount); for (int x = 0; x < width(); x++) srcBuffer.setpixel(x, y, &lutBuffer[x * chanCount]); } ImageSpec destSpec(width(), height(), chanCount, oiioBitDepths[bitDepth_]); setChannelNames(destSpec, channels); destSpec.attribute("maketx:filtername", gFilterNames[filter_]); switch (preset_) { case 0: destSpec.attribute("maketx:oiio_options", 1); break; case 1: destSpec.attribute("maketx:prman_options", 1); break; default: destSpec.tile_width = tileW_; destSpec.tile_height = tileH_; destSpec.attribute("planarconfig", planarMode_ ? "separate" : "contig"); break; } if (fixNan_) { if (nanFixType_) destSpec.attribute("maketx:fixnan", "box3"); else destSpec.attribute("maketx:fixnan", "black"); } else destSpec.attribute("maketx:fixnan", "none"); destSpec.attribute("maketx:checknan", checkNan_); destSpec.attribute("maketx:verbose", verbose_); destSpec.attribute("maketx:stats", stats_); OIIO::attribute("threads", (int)Thread::numCPUs); if (aborted()) return; iop->progressMessage("Writing %s", filename()); if (!ImageBufAlgo::make_texture(oiiotxMode[txMode_], srcBuffer, filename(), destSpec, &std::cout)) iop->critical("ImageBufAlgo::make_texture failed to write file %s", filename()); } const char* help() { return "Tiled, mipmapped texture format"; } static const Writer::Description d; }; } // ~TxWriterNS static Writer* build(Write* iop) { return new TxWriterNS::txWriter(iop); } const Writer::Description TxWriterNS::txWriter::d("tx\0TX\0", build); openimageio-1.7.17~dfsg0.orig/src/nuke/txReader/0000755000175000017500000000000013151711064017634 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/nuke/txReader/CMakeLists.txt0000644000175000017500000000203213151711064022371 0ustar mfvmfvinclude_directories (${NUKE_INCLUDE_DIRS}) link_directories (${NUKE_LIBRARY_DIRS}) add_library (txReader SHARED txReader.cpp) target_link_libraries (txReader DDImage OpenImageIO) if (WIN32) add_definitions (/DFN_OS_WINDOWS /DUSE_GLEW) set_target_properties (txReader PROPERTIES PREFIX "" COMPILE_FLAGS "/wd4251 /W3 /O2 /MD /GL /GR /GF" LINK_FLAGS "/machine:x64 /SUBSYSTEM:WINDOWS /dll") elseif (APPLE) target_link_libraries (txReader GLEW) add_definitions (-DUSE_GLEW) set_target_properties (txReader PROPERTIES PREFIX "" COMPILE_FLAGS "-arch x86_64" LINK_FLAGS "-arch x86_64 -bundle -framework QuartzCore -framework IOKit -framework CoreFoundation -framework Carbon -framework ApplicationServices -framework OpenGL -framework AGL") else () add_definitions (-DUSE_GLEW) set_target_properties (txReader PROPERTIES PREFIX "" COMPILE_FLAGS "-fPIC -msse") endif () install (TARGETS txReader LIBRARY DESTINATION "${LIB_INSTALL_DIR}/nuke") openimageio-1.7.17~dfsg0.orig/src/nuke/txReader/txReader.cpp0000644000175000017500000003074413151711064022126 0ustar mfvmfv#ifndef _WIN32 #include #endif #include "DDImage/Enumeration_KnobI.h" #include "DDImage/Reader.h" #include "DDImage/Row.h" #include "OpenImageIO/imageio.h" using namespace DD::Image; /* * TODO: * - Look into using the planar Reader API in Nuke 8, which may map better to * TIFF/OIIO. * - It would be nice to have a way to read in a region, rather than the whole * image, but this would require access to the Read's request region, * which isn't currently possible. A feature request for this is logged * with The Foundry as Bug 46237. */ namespace TxReaderNS { OIIO_NAMESPACE_USING static const char* const EMPTY[] = {NULL}; class TxReaderFormat : public ReaderFormat { int mipLevel_; int mipEnumIndex_; Knob* mipLevelKnob_; Knob* mipLevelEnumKnob_; public: TxReaderFormat() : mipLevel_(0), mipEnumIndex_(0), mipLevelKnob_(NULL), mipLevelEnumKnob_(NULL) { } void knobs(Knob_Callback cb) { // The "real" mip level knob that controls the level read by the Reader // class, and whose value is stored when the Read is serialized. mipLevelKnob_ = Int_knob(cb, &mipLevel_, "tx_mip_level", "mip index"); SetFlags(cb, Knob::INVISIBLE); // The user-facing mip level dropdown. This is populated lazily by the // Reader when it opens a file, and does not directly contribute to the // op hash or get stored when the Read is serialized. mipLevelEnumKnob_ = Enumeration_knob(cb, &mipEnumIndex_, EMPTY, "tx_user_mip_level", "mip level"); SetFlags(cb, Knob::EXPAND_TO_WIDTH | Knob::DO_NOT_WRITE | Knob::NO_RERENDER); Tooltip(cb, "The mip level to read from the file. Currently, this will " "be resampled to fill the same resolution as the base image."); } int knob_changed(Knob* k) { if (k == mipLevelEnumKnob_) mipLevelKnob_->set_value(mipEnumIndex_); return 1; } void append(Hash& hash) { hash.append(mipLevel_); } inline int mipLevel() { return mipLevel_; } void setMipLabels(std::vector items) { if (mipLevelEnumKnob_) { mipLevelEnumKnob_->set_flag(Knob::NO_KNOB_CHANGED); mipLevelEnumKnob_->enumerationKnob()->menu(items); mipLevelEnumKnob_->set_value( std::min((int)items.size() - 1, mipLevel_)); mipLevelEnumKnob_->clear_flag(Knob::NO_KNOB_CHANGED); } } const char* help() { return "Tiled, mipmapped texture format"; } }; class txReader : public Reader { ImageInput* oiioInput_; TxReaderFormat* txFmt_; int chanCount_, lastMipLevel_; bool haveImage_, flip_; std::vector imageBuf_; std::map chanMap_; MetaData::Bundle meta_; static const Description d; void fillMetadata(const ImageSpec& spec, bool isEXR) { switch (spec.format.basetype) { case TypeDesc::UINT8: case TypeDesc::INT8: meta_.setData(MetaData::DEPTH, MetaData::DEPTH_8); break; case TypeDesc::UINT16: case TypeDesc::INT16: meta_.setData(MetaData::DEPTH, MetaData::DEPTH_16); break; case TypeDesc::UINT32: case TypeDesc::INT32: meta_.setData(MetaData::DEPTH, MetaData::DEPTH_32); break; case TypeDesc::HALF: meta_.setData(MetaData::DEPTH, MetaData::DEPTH_HALF); break; case TypeDesc::FLOAT: meta_.setData(MetaData::DEPTH, MetaData::DEPTH_FLOAT); break; case TypeDesc::DOUBLE: meta_.setData(MetaData::DEPTH, MetaData::DEPTH_DOUBLE); break; default: meta_.setData(MetaData::DEPTH, "Unknown"); break; } meta_.setData("tx/tile_width", spec.tile_width); meta_.setData("tx/tile_height", spec.tile_height); string_view val; val = spec.get_string_attribute("ImageDescription"); if (!val.empty()) meta_.setData("tx/image_description", val); val = spec.get_string_attribute("DateTime"); if (!val.empty()) meta_.setData(MetaData::CREATED_TIME, val); val = spec.get_string_attribute("Software"); if (!val.empty()) meta_.setData(MetaData::CREATOR, val); val = spec.get_string_attribute("textureformat"); if (!val.empty()) meta_.setData("tx/texture_format", val); val = spec.get_string_attribute("wrapmodes"); if (!val.empty()) meta_.setData("tx/wrap_modes", val); val = spec.get_string_attribute("fovcot"); if (!val.empty()) meta_.setData("tx/fovcot", val); val = spec.get_string_attribute("compression"); if (!val.empty()) meta_.setData("tx/compression", val); if (isEXR) { val = spec.get_string_attribute("openexr:lineOrder"); if (!val.empty()) meta_.setData("exr/line_order", val); float cl = spec.get_float_attribute("openexr:dwaCompressionLevel", 0.0f); if (val > 0) meta_.setData("exr/dwa_compression_level", cl); } else { val = spec.get_string_attribute("tiff:planarconfig"); if (!val.empty()) meta_.setData("tiff/planar_config", val); } } void setChannels(const ImageSpec& spec) { ChannelSet mask; Channel chan; int chanIndex = 0; for (std::vector::const_iterator it = spec.channelnames.begin(); it != spec.channelnames.end(); it++) { chan = Reader::channel(it->c_str()); mask += chan; chanMap_[chan] = chanIndex++; } info_.channels(mask); } public: txReader(Read* iop) : Reader(iop), chanCount_(0), lastMipLevel_(-1), haveImage_(false), flip_(false) { txFmt_ = dynamic_cast(iop->handler()); OIIO::attribute("threads", (int)Thread::numThreads / 2); oiioInput_ = ImageInput::open(filename()); if (!oiioInput_) { iop->internalError("OIIO: Failed to open file %s: %s", filename(), geterror().c_str()); return; } const ImageSpec& baseSpec = oiioInput_->spec(); if (!(baseSpec.width * baseSpec.height)) { iop->internalError("tx file has one or more zero dimensions " "(%d x %d)", baseSpec.width, baseSpec.height); return; } chanCount_ = baseSpec.nchannels; const bool isEXR = strcmp(oiioInput_->format_name(), "openexr") == 0; if (isEXR) { float pixAspect = baseSpec.get_float_attribute("PixelAspectRatio", 0); set_info(baseSpec.width, baseSpec.height, 1, pixAspect); meta_.setData(MetaData::PIXEL_ASPECT, pixAspect > 0 ? pixAspect : 1.0f); setChannels(baseSpec); // Fills chanMap_ flip_ = true; } else { set_info(baseSpec.width, baseSpec.height, chanCount_); int orientation = baseSpec.get_int_attribute("Orientation", 1); meta_.setData("tiff/orientation", orientation); flip_ = !((orientation - 1) & 2); int chanIndex = 0; foreach(z, info_.channels()) chanMap_[z] = chanIndex++; } fillMetadata(baseSpec, isEXR); // Populate mip level pulldown with labels in the form: // "MIPLEVEL - WxH" (e.g. "0 - 1920x1080") std::vector mipLabels; std::ostringstream buf; ImageSpec mipSpec(baseSpec); int mipLevel = 0; while (true) { buf << mipLevel << " - " << mipSpec.width << 'x' << mipSpec.height; mipLabels.push_back(buf.str()); if (oiioInput_->seek_subimage(0, mipLevel + 1, mipSpec)) { buf.str(std::string()); buf.clear(); mipLevel++; } else break; } meta_.setData("tx/mip_levels", mipLevel + 1); txFmt_->setMipLabels(mipLabels); } virtual ~txReader() { if (oiioInput_) oiioInput_->close(); delete oiioInput_; oiioInput_ = NULL; } void open() { if (lastMipLevel_ != txFmt_->mipLevel()) { ImageSpec mipSpec; if (!oiioInput_->seek_subimage(0, txFmt_->mipLevel(), mipSpec)) { iop->internalError("Failed to seek to mip level %d: %s", txFmt_->mipLevel(), oiioInput_->geterror().c_str()); return; } if (txFmt_->mipLevel() && mipSpec.nchannels != chanCount_) { iop->internalError("txReader does not support mip levels with " "different channel counts"); return; } lastMipLevel_ = txFmt_->mipLevel(); haveImage_ = false; } if (!haveImage_) { const int needSize = oiioInput_->spec().width * oiioInput_->spec().height * oiioInput_->spec().nchannels; if (size_t(needSize) > imageBuf_.size()) imageBuf_.resize(needSize); oiioInput_->read_image(&imageBuf_[0]); haveImage_ = true; } } void engine(int y, int x, int r, ChannelMask channels, Row& row) { if (!haveImage_) iop->internalError("engine called, but haveImage_ is false"); if (aborted()) { row.erase(channels); return; } const bool doAlpha = channels.contains(Chan_Alpha); if (flip_) y = height() - y - 1; if (lastMipLevel_) { // Mip level other than 0 const int mipW = oiioInput_->spec().width; const int mipMult = width() / mipW; y = y * oiioInput_->spec().height / height(); const int bufX = x ? x / mipMult : 0; const int bufR = r / mipMult; const int bufW = bufR - bufX; std::vector chanBuf(bufW); float* chanStart = &chanBuf[0]; const int bufStart = y * mipW * chanCount_ + bufX * chanCount_; const float* alpha = doAlpha ? &imageBuf_[bufStart + chanMap_[Chan_Alpha]] : NULL; foreach (z, channels) { from_float(z, &chanBuf[0], &imageBuf_[bufStart + chanMap_[z]], alpha, bufW, chanCount_); float* OUT = row.writable(z); for (int stride = 0, c = 0; stride < bufW; stride++, c = 0) for (; c < mipMult; c++) *OUT++ = *(chanStart + stride); } } else { // Mip level 0 const int pixStart = y * width() * chanCount_ + x * chanCount_; const float* alpha = doAlpha ? &imageBuf_[pixStart + chanMap_[Chan_Alpha]] : NULL; foreach (z, channels) { from_float(z, row.writable(z) + x, &imageBuf_[pixStart + chanMap_[z]], alpha, r - x, chanCount_); } } } const MetaData::Bundle& fetchMetaData(const char* key) { return meta_; } }; } // ~TxReaderNS static Reader* buildReader(Read* iop, int fd, const unsigned char* b, int n) { // FIXME: I expect that this close() may be problematic on Windows. // For Linux/gcc, we needed to #include at the top of // this file. If this is a problem for Windows, a different #include // or a different close call here may be necessary. close(fd); return new TxReaderNS::txReader(iop); } static ReaderFormat* buildformat(Read* iop) { return new TxReaderNS::TxReaderFormat(); } static bool test(int fd, const unsigned char* block, int length) { // Big-endian TIFF if (block[0] == 'M' && block[1] == 'M' && block[2] == 0 && block[3] == 42) return true; // Little-endian TIFF if (block[0] == 'I' && block[1] == 'I' && block[2] == 42 && block[3] == 0) return true; // EXR return block[0] == 0x76 && block[1] == 0x2f && block[2] == 0x31 && block[3] == 0x01; } const Reader::Description TxReaderNS::txReader::d("tx\0TX\0", buildReader, test, buildformat); openimageio-1.7.17~dfsg0.orig/src/socket.imageio/0000755000175000017500000000000013151711064020015 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/socket.imageio/CMakeLists.txt0000644000175000017500000000016413151711064022556 0ustar mfvmfvadd_oiio_plugin (socketinput.cpp socketoutput.cpp socket_pvt.cpp DEFINITIONS "-DUSE_BOOST_ASIO=1") openimageio-1.7.17~dfsg0.orig/src/socket.imageio/socketinput.cpp0000644000175000017500000001343013151711064023072 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "OpenImageIO/imageio.h" #include "socket_pvt.h" OIIO_PLUGIN_NAMESPACE_BEGIN // Export version number and create function symbols OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT int socket_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* socket_imageio_library_version() { return NULL; } OIIO_EXPORT ImageInput *socket_input_imageio_create () { return new SocketInput; } OIIO_EXPORT const char *socket_input_extensions[] = { "socket", NULL }; OIIO_PLUGIN_EXPORTS_END SocketInput::SocketInput() : socket (io) { } bool SocketInput::valid_file (const std::string &filename) const { // Pass it a configuration request that includes a "nowait" option // so that it returns immediately rather than waiting for a socket // connection that doesn't yet exist. ImageSpec config; config.attribute ("nowait", (int)1); ImageSpec tmpspec; bool ok = const_cast(this)->open (filename, tmpspec, config); if (ok) const_cast(this)->close (); return ok; } bool SocketInput::open (const std::string &name, ImageSpec &newspec) { return open (name, newspec, ImageSpec()); } bool SocketInput::open (const std::string &name, ImageSpec &newspec, const ImageSpec &config) { // If there is a nonzero "nowait" request in the configuration, just // return immediately. if (config.get_int_attribute ("nowait", 0)) { return false; } if (! (accept_connection (name) && get_spec_from_client (newspec))) { return false; } // Also send information about endianess etc. m_spec = newspec; return true; } bool SocketInput::read_native_scanline (int y, int z, void *data) { try { boost::asio::read (socket, buffer (reinterpret_cast (data), m_spec.scanline_bytes ())); } catch (boost::system::system_error &err) { error ("Error while reading: %s", err.what ()); return false; } catch (...) { error ("Error while reading: unknown exception"); return false; } return true; } bool SocketInput::read_native_tile (int x, int y, int z, void *data) { try { boost::asio::read (socket, buffer (reinterpret_cast (data), m_spec.tile_bytes ())); } catch (boost::system::system_error &err) { error ("Error while reading: %s", err.what ()); return false; } catch (...) { error ("Error while reading: unknown exception"); return false; } return true; } bool SocketInput::close () { socket.close(); return true; } bool SocketInput::accept_connection(const std::string &name) { std::map rest_args; std::string baseurl; rest_args["port"] = socket_pvt::default_port; rest_args["host"] = socket_pvt::default_host; if (! Strutil::get_rest_arguments (name, baseurl, rest_args)) { error ("Invalid 'open ()' argument: %s", name.c_str ()); return false; } int port = atoi (rest_args["port"].c_str ()); try { acceptor = OIIO::shared_ptr (new ip::tcp::acceptor (io, ip::tcp::endpoint (ip::tcp::v4(), port))); acceptor->accept (socket); } catch (boost::system::system_error &err) { error ("Error while accepting: %s", err.what ()); return false; } catch (...) { error ("Error while accepting: unknown exception"); return false; } return true; } bool SocketInput::get_spec_from_client (ImageSpec &spec) { try { int spec_length; boost::asio::read (socket, buffer (reinterpret_cast (&spec_length), sizeof (boost::uint32_t))); char *spec_xml = new char[spec_length + 1]; boost::asio::read (socket, buffer (spec_xml, spec_length)); spec.from_xml (spec_xml); delete [] spec_xml; } catch (boost::system::system_error &err) { error ("Error while get_spec_from_client: %s", err.what ()); return false; } catch (...) { error ("Error while get_spec_from_client: unknown exception"); return false; } return true; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/socket.imageio/socket_pvt.cpp0000644000175000017500000000432513151711064022706 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ ///////////////////////////////////////////////////////////////////////////// // Private definitions internal to the socket.imageio plugin ///////////////////////////////////////////////////////////////////////////// #include "socket_pvt.h" OIIO_PLUGIN_NAMESPACE_BEGIN using namespace boost; using namespace boost::asio; namespace socket_pvt { std::size_t socket_write (ip::tcp::socket &s, TypeDesc &type, const void *data, int size) { std::size_t bytes; // TODO: Translate data to correct endianesss. bytes = write (s, buffer (reinterpret_cast (data), size)); return bytes; } } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/socket.imageio/socket_pvt.h0000644000175000017500000001113513151711064022350 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ ///////////////////////////////////////////////////////////////////////////// // Private definitions internal to the socket.imageio plugin ///////////////////////////////////////////////////////////////////////////// #ifndef OPENIMAGEIO_SOCKET_PVT_H #define OPENIMAGEIO_SOCKET_PVT_H #include "OpenImageIO/imageio.h" #include "OpenImageIO/refcnt.h" #include // The boost::asio library uses functionality only available since Windows XP, // thus _WIN32_WINNT must be set to _WIN32_WINNT_WINXP (0x0501) or greater. // If _WIN32_WINNT is not defined before including the asio headers, they issue // a message warning that _WIN32_WINNT was explicitly set to _WIN32_WINNT_WINXP. #if defined(_WIN32) && !defined(_WIN32_WINNT) # define _WIN32_WINNT 0x0501 #endif #include OIIO_PLUGIN_NAMESPACE_BEGIN using namespace boost::asio; class SocketOutput : public ImageOutput { public: SocketOutput (); virtual ~SocketOutput () { close(); } virtual const char * format_name (void) const { return "socket"; } virtual int supports (string_view property) const; virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode=Create); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); virtual bool close (); virtual bool copy_image (ImageInput *in); private: int m_next_scanline; // Which scanline is the next to write? io_service io; ip::tcp::socket socket; std::vector m_scratch; bool connect_to_server (const std::string &name); bool send_spec_to_server (const ImageSpec &spec); }; class SocketInput : public ImageInput { public: SocketInput (); virtual ~SocketInput () { close(); } virtual const char * format_name (void) const { return "socket"; } virtual bool valid_file (const std::string &filename) const; virtual bool open (const std::string &name, ImageSpec &spec); virtual bool open (const std::string &name, ImageSpec &spec, const ImageSpec &config); virtual bool read_native_scanline (int y, int z, void *data); virtual bool read_native_tile (int x, int y, int z, void *data); virtual bool close (); private: int m_next_scanline; // Which scanline is the next to read? io_service io; ip::tcp::socket socket; OIIO::shared_ptr acceptor; bool accept_connection (const std::string &name); bool get_spec_from_client (ImageSpec &spec); friend class SocketOutput; }; namespace socket_pvt { const char default_port[] = "10110"; const char default_host[] = "127.0.0.1"; std::size_t socket_write (ip::tcp::socket &s, TypeDesc &type, const void *data, int size); } OIIO_PLUGIN_NAMESPACE_END #endif /* OPENIMAGEIO_SOCKET_PVT_H */ openimageio-1.7.17~dfsg0.orig/src/socket.imageio/socketoutput.cpp0000644000175000017500000001340013151711064023270 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include "OpenImageIO/imageio.h" #include "socket_pvt.h" OIIO_PLUGIN_NAMESPACE_BEGIN OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *socket_output_imageio_create () { return new SocketOutput; } OIIO_EXPORT const char *socket_output_extensions[] = { "socket", NULL }; OIIO_PLUGIN_EXPORTS_END SocketOutput::SocketOutput() : socket (io) { } int SocketOutput::supports (string_view feature) const { return (feature == "alpha" || feature == "nchannels"); } bool SocketOutput::open (const std::string &name, const ImageSpec &newspec, OpenMode mode) { if (! (connect_to_server (name) && send_spec_to_server (newspec))) { return false; } m_next_scanline = 0; m_spec = newspec; if (m_spec.format == TypeDesc::UNKNOWN) m_spec.set_format (TypeDesc::UINT8); // Default to 8 bit channels return true; } bool SocketOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { data = to_native_scanline (format, data, xstride, m_scratch); try { socket_pvt::socket_write (socket, format, data, m_spec.scanline_bytes ()); } catch (boost::system::system_error &err) { error ("Error while writing: %s", err.what ()); return false; } catch (...) { error ("Error while writing: unknown exception"); return false; } ++m_next_scanline; return true; } bool SocketOutput::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { data = to_native_tile (format, data, xstride, ystride, zstride, m_scratch); try { socket_pvt::socket_write (socket, format, data, m_spec.tile_bytes ()); } catch (boost::system::system_error &err) { error ("Error while writing: %s", err.what ()); return false; } catch (...) { error ("Error while writing: unknown exception"); return false; } return true; } bool SocketOutput::close () { socket.close(); return true; } bool SocketOutput::copy_image (ImageInput *in) { return true; } bool SocketOutput::send_spec_to_server(const ImageSpec& spec) { std::string spec_xml = spec.to_xml(); int xml_length = spec_xml.length (); try { boost::asio::write (socket, buffer (reinterpret_cast (&xml_length), sizeof (boost::uint32_t))); boost::asio::write (socket, buffer (spec_xml.c_str (), spec_xml.length ())); } catch (boost::system::system_error &err) { error ("Error while send_spec_to_server: %s", err.what ()); return false; } catch (...) { error ("Error while send_spec_to_server: unknown exception"); return false; } return true; } bool SocketOutput::connect_to_server (const std::string &name) { std::map rest_args; std::string baseurl; rest_args["port"] = socket_pvt::default_port; rest_args["host"] = socket_pvt::default_host; if (! Strutil::get_rest_arguments (name, baseurl, rest_args)) { error ("Invalid 'open ()' argument: %s", name.c_str ()); return false; } try { ip::tcp::resolver resolver (io); ip::tcp::resolver::query query (rest_args["host"].c_str (), rest_args["port"].c_str ()); ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve (query); ip::tcp::resolver::iterator end; boost::system::error_code err = error::host_not_found; while (err && endpoint_iterator != end) { socket.close (); socket.connect (*endpoint_iterator++, err); } if (err) { error ("Host \"%s\" not found", rest_args["host"].c_str ()); return false; } } catch (boost::system::system_error &err) { error ("Error while connecting: %s", err.what ()); return false; } catch (...) { error ("Error while connecting: unknown exception"); return false; } return true; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/gif.imageio/0000755000175000017500000000000013151711064017272 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/gif.imageio/CMakeLists.txt0000644000175000017500000000044713151711064022037 0ustar mfvmfvif (USE_GIF AND GIF_FOUND) add_oiio_plugin (gifinput.cpp gifoutput.cpp INCLUDE_DIRS ${GIF_INCLUDE_DIR} LINK_LIBRARIES ${GIF_LIBRARIES} DEFINITIONS "-DUSE_GIF") else() message (WARNING "GIF plugin will not be built") endif() openimageio-1.7.17~dfsg0.orig/src/gif.imageio/gifoutput.cpp0000644000175000017500000001532313151711064022030 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #define USE_GIFH 1 #include #include #include #include #include "OpenImageIO/imageio.h" #include "OpenImageIO/platform.h" namespace { #define GIF_TEMP_MALLOC malloc #define GIF_TEMP_FREE free #define GIF_MALLOC malloc #define GIF_FREE free #include "gif.h" } OIIO_PLUGIN_NAMESPACE_BEGIN class GIFOutput : public ImageOutput { public: GIFOutput () { init(); } virtual ~GIFOutput () { close(); } virtual const char * format_name (void) const { return "gif"; } virtual int supports (string_view feature) const { return (feature == "alpha" || feature == "random_access" || feature == "multiimage" || feature == "appendsubimage"); } virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode=Create); virtual bool open (const std::string &name, int subimages, const ImageSpec *specs); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool close (); private: std::string m_filename; int m_subimage; // Current subimage index int m_nsubimages; bool m_pending_write; // Do we have an image buffered? std::vector m_subimagespecs; // Saved subimage specs GifWriter m_gifwriter; std::vector m_canvas; // Image canvas, accumulating output int m_delay; void init (void) { m_filename.clear (); m_subimage = 0; m_canvas.clear (); m_pending_write = false; } bool start_subimage (); bool finish_subimage (); }; OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *gif_output_imageio_create () { return new GIFOutput; } OIIO_EXPORT const char *gif_output_extensions[] = { "gif", NULL }; OIIO_PLUGIN_EXPORTS_END bool GIFOutput::open (const std::string &name, const ImageSpec &newspec, OpenMode mode) { if (mode == Create) { return open (name, 1, &newspec); } if (mode == AppendMIPLevel) { error ("%s does not support MIP levels", format_name()); return false; } if (mode == AppendSubimage) { if (m_pending_write) finish_subimage(); ++m_subimage; m_spec = newspec; return start_subimage (); } ASSERTMSG (0, "Unknown open mode %d", int(mode)); return false; } bool GIFOutput::open (const std::string &name, int subimages, const ImageSpec *specs) { if (subimages < 1) { error ("%s does not support %d subimages.", format_name(), subimages); return false; } m_filename = name; m_subimage = 0; m_nsubimages = subimages; m_subimagespecs.assign (specs, specs+subimages); m_spec = specs[0]; float fps = m_spec.get_float_attribute ("FramesPerSecond", 1.0f); m_delay = (fps == 0.0f ? 0 : (int)(100.0f/fps)); return start_subimage (); } bool GIFOutput::close () { if (m_pending_write) { finish_subimage (); GifEnd (&m_gifwriter); } init (); return true; } bool GIFOutput::start_subimage () { // Check for things this format doesn't support if (m_spec.width < 1 || m_spec.height < 1) { error ("Image resolution must be at least 1x1, you asked for %d x %d", m_spec.width, m_spec.height); return false; } if (m_spec.depth < 1) m_spec.depth = 1; if (m_spec.depth > 1) { error ("%s does not support volume images (depth > 1)", format_name()); return false; } if (m_spec.nchannels != 3 && m_spec.nchannels != 4) { error ("%s does not support %d-channel images", format_name(), m_spec.nchannels); return false; } m_spec.set_format (TypeDesc::UINT8); // GIF is only 8 bit if (m_subimage == 0) { bool ok = GifBegin (&m_gifwriter, m_filename.c_str(), m_spec.width, m_spec.height, m_delay, 8 /*bit depth*/, true /*dither*/); if (!ok) { error ("Could not open file %s", m_filename); return false; } } m_canvas.clear (); m_canvas.resize (size_t(m_spec.image_pixels()*4), 255); m_pending_write = true; return true; } bool GIFOutput::finish_subimage () { if (! m_pending_write) return true; bool ok = GifWriteFrame (&m_gifwriter, &m_canvas[0], spec().width, spec().height, m_delay, 8 /*bitdepth*/, true /*dither*/); m_pending_write = false; return ok; } bool GIFOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { return convert_image (spec().nchannels, spec().width, 1 /*1 scanline*/, 1, data, format, xstride, AutoStride, AutoStride, &m_canvas[y*spec().width*4], TypeDesc::UINT8, 4, AutoStride, AutoStride, spec().nchannels > 3 ? 3 : -1/*alpha channel*/); } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/gif.imageio/gifinput.cpp0000644000175000017500000003405113151711064021626 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include "OpenImageIO/imageio.h" #include "OpenImageIO/thread.h" // GIFLIB: // http://giflib.sourceforge.net/ // Format description: // http://giflib.sourceforge.net/whatsinagif/index.html // for older giflib versions #ifndef GIFLIB_MAJOR #define GIFLIB_MAJOR 4 #endif #ifndef DISPOSAL_UNSPECIFIED #define DISPOSAL_UNSPECIFIED 0 #endif #ifndef DISPOSE_BACKGROUND #define DISPOSE_BACKGROUND 2 #endif OIIO_PLUGIN_NAMESPACE_BEGIN class GIFInput : public ImageInput { public: GIFInput () { init (); } virtual ~GIFInput () { close (); } virtual const char *format_name (void) const { return "gif"; } virtual bool open (const std::string &name, ImageSpec &newspec); virtual bool close (void); virtual bool read_native_scanline (int y, int z, void *data); virtual bool seek_subimage (int subimage, int miplevel, ImageSpec &newspec); virtual int current_subimage (void) const { return m_subimage; } virtual int current_miplevel (void) const { // No mipmap support return 0; } private: std::string m_filename; ///< Stash the filename GifFileType *m_gif_file; ///< GIFLIB handle int m_transparent_color; ///< Transparent color index int m_subimage; ///< Current subimage index int m_disposal_method; ///< Disposal method of current subimage. /// Indicates what to do with canvas /// before drawing the _next_ subimage. int m_previous_disposal_method; ///< Disposal method of previous subimage. /// Indicates what to do with canvas /// before drawing _current_ subimage. std::vector m_canvas; ///< Image canvas in output format, on /// which subimages are sequentially /// drawn. /// Reset everything to initial state /// void init (void); /// Read current subimage metadata. /// bool read_subimage_metadata (ImageSpec &newspec); /// Read current subimage data (ie. draw it on canvas). /// bool read_subimage_data (void); /// Helper: read gif extension. /// void read_gif_extension (int ext_code, GifByteType *ext, ImageSpec &spec); /// Decode and return a real scanline index in the interlaced image. /// int decode_line_number (int line_number, int height); /// Print error message. /// void report_last_error (void); }; // Obligatory material to make this a recognizeable imageio plugin OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT int gif_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT ImageInput *gif_input_imageio_create () { return new GIFInput; } OIIO_EXPORT const char *gif_input_extensions[] = { "gif", NULL }; OIIO_EXPORT const char* gif_imageio_library_version () { #define STRINGIZE2(a) #a #define STRINGIZE(a) STRINGIZE2(a) #if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR) && defined(GIFLIB_RELEASE) return "gif_lib " STRINGIZE(GIFLIB_MAJOR) "." STRINGIZE(GIFLIB_MINOR) "." STRINGIZE(GIFLIB_RELEASE); #else return "gif_lib unknown version"; #endif } OIIO_PLUGIN_EXPORTS_END void GIFInput::init (void) { m_gif_file = NULL; } bool GIFInput::open (const std::string &name, ImageSpec &newspec) { m_filename = name; m_subimage = -1; m_canvas.clear (); return seek_subimage (0, 0, newspec); } inline int GIFInput::decode_line_number (int line_number, int height) { if (1 < height && (height + 1) / 2 <= line_number) // 4th tile 1/2 sized return 2 * (line_number - (height + 1) / 2) + 1; if (2 < height && (height + 3) / 4 <= line_number) // 3rd tile 1/4 sized return 4 * (line_number - (height + 3) / 4) + 2; if (4 < height && (height + 7) / 8 <= line_number) // 2nd tile 1/8 sized return 8 * (line_number - (height + 7) / 8) + 4; // 1st tile 1/8 sized return line_number * 8; } bool GIFInput::read_native_scanline (int y, int z, void *data) { if (y < 0 || y > m_spec.height || ! m_canvas.size()) return false; memcpy (data, &m_canvas[y * m_spec.width * m_spec.nchannels], m_spec.width * m_spec.nchannels); return true; } void GIFInput::read_gif_extension (int ext_code, GifByteType *ext, ImageSpec &newspec) { if (ext_code == GRAPHICS_EXT_FUNC_CODE) { // read background color index, disposal method and delay time between frames // http://giflib.sourceforge.net/whatsinagif/bits_and_bytes.html#graphics_control_extension_block if (ext[1] & 0x01) { m_transparent_color = (int) ext[4]; } m_disposal_method = (ext[1] & 0x1c) >> 2; int delay = (ext[3] << 8) | ext[2]; if (delay) { newspec.attribute ("FramesPerSecond", float(100.0f/delay)); newspec.attribute ("oiio:Movie", 1); } } else if (ext_code == COMMENT_EXT_FUNC_CODE) { // read comment data // http://giflib.sourceforge.net/whatsinagif/bits_and_bytes.html#comment_extension_block std::string comment = std::string ((const char *)&ext[1], int (ext[0])); newspec.attribute("ImageDescription", comment); } else if (ext_code == APPLICATION_EXT_FUNC_CODE) { // read loop count // http://giflib.sourceforge.net/whatsinagif/bits_and_bytes.html#application_extension_block if (ext[0] == 3) { newspec.attribute ("gif:LoopCount", (ext[3] << 8) | ext[2]); } } } bool GIFInput::read_subimage_metadata (ImageSpec &newspec) { newspec = ImageSpec (TypeDesc::UINT8); newspec.nchannels = 4; newspec.default_channel_names (); newspec.alpha_channel = 4; newspec.attribute ("oiio:ColorSpace", "sRGB"); m_previous_disposal_method = m_disposal_method; m_disposal_method = DISPOSAL_UNSPECIFIED; m_transparent_color = -1; GifRecordType m_gif_rec_type; do { if (DGifGetRecordType (m_gif_file, &m_gif_rec_type) == GIF_ERROR) { report_last_error (); return false; } switch (m_gif_rec_type) { case IMAGE_DESC_RECORD_TYPE: if (DGifGetImageDesc (m_gif_file) == GIF_ERROR) { report_last_error (); return false; } break; case EXTENSION_RECORD_TYPE: int ext_code; GifByteType *ext; if (DGifGetExtension(m_gif_file, &ext_code, &ext) == GIF_ERROR) { report_last_error (); return false; } read_gif_extension (ext_code, ext, newspec); while (ext != NULL) { if (DGifGetExtensionNext(m_gif_file, &ext) == GIF_ERROR) { report_last_error (); return false; } if (ext != NULL) { read_gif_extension (ext_code, ext, newspec); } } break; case TERMINATE_RECORD_TYPE: return false; break; default: break; } } while (m_gif_rec_type != IMAGE_DESC_RECORD_TYPE); newspec.attribute ("gif:Interlacing", m_gif_file->Image.Interlace ? 1 : 0); return true; } bool GIFInput::read_subimage_data() { GifColorType *colormap = NULL; if (m_gif_file->Image.ColorMap) { // local colormap colormap = m_gif_file->Image.ColorMap->Colors; } else if (m_gif_file->SColorMap) { // global colormap colormap = m_gif_file->SColorMap->Colors; } else { error ("Neither local nor global colormap present."); return false; } if (m_subimage == 0 || m_previous_disposal_method == DISPOSE_BACKGROUND) { // make whole canvas transparent std::fill (m_canvas.begin(), m_canvas.end(), 0x00); } // decode scanline index if image is interlaced bool interlacing = m_spec.get_int_attribute ("gif:Interlacing") != 0; // get subimage dimensions and draw it on canvas int window_height = m_gif_file->Image.Height; int window_width = m_gif_file->Image.Width; int window_top = m_gif_file->Image.Top; int window_left = m_gif_file->Image.Left; for (int wy = 0; wy < window_height; wy++) { boost::scoped_array fscanline (new unsigned char[window_width]); if (DGifGetLine (m_gif_file, fscanline.get(), window_width) == GIF_ERROR) { report_last_error (); return false; } int y = window_top + (interlacing ? decode_line_number(wy, window_height) : wy); if (0 <= y && y < m_spec.height) { for (int wx = 0; wx < window_width; wx++) { int x = window_left + wx; int idx = m_spec.nchannels * (y * m_spec.width + x); if (0 <= x && x < m_spec.width && fscanline[wx] != m_transparent_color) { m_canvas[idx] = colormap[fscanline[wx]].Red; m_canvas[idx + 1] = colormap[fscanline[wx]].Green; m_canvas[idx + 2] = colormap[fscanline[wx]].Blue; m_canvas[idx + 3] = 0xff; } } } } return true; } bool GIFInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec) { if (subimage < 0 || miplevel != 0) return false; if (m_subimage == subimage) { // We're already pointing to the right subimage newspec = m_spec; return true; } if (m_subimage > subimage) { // requested subimage is located before the current one // file needs to be reopened if (m_gif_file && ! close()) { return false; } } if (! m_gif_file) { #if GIFLIB_MAJOR >= 5 int giflib_error; if (! (m_gif_file = DGifOpenFileName (m_filename.c_str(), &giflib_error))) { error (GifErrorString (giflib_error)); return false; } #else if (! (m_gif_file = DGifOpenFileName (m_filename.c_str()))) { error ("Error trying to open the file."); return false; } #endif m_subimage = -1; m_canvas.resize (m_gif_file->SWidth * m_gif_file->SHeight * 4); } // skip subimages preceding the requested one if (m_subimage < subimage) { for (m_subimage += 1; m_subimage < subimage; m_subimage ++) { if (! read_subimage_metadata (newspec) || ! read_subimage_data ()) { return false; } } } // read metadata of current subimage if (! read_subimage_metadata (newspec)) { return false; } newspec.width = m_gif_file->SWidth; newspec.height = m_gif_file->SHeight; newspec.depth = 1; newspec.full_height = newspec.height; newspec.full_width = newspec.width; newspec.full_depth = newspec.depth; m_spec = newspec; m_subimage = subimage; // draw subimage on canvas if (! read_subimage_data ()) { return false; } return true; } static spin_mutex gif_error_mutex; void GIFInput::report_last_error (void) { // N.B. Only GIFLIB_MAJOR >= 5 looks properly thread-safe, in that the // error is guaranteed to be specific to this open file. We use a spin // mutex to prevent a thread clash for older versions, but it still // appears to be a global call, so we can't be absolutely sure that the // error was for *this* file. So if you're using giflib prior to // version 5, beware. #if GIFLIB_MAJOR >= 5 error ("%s", GifErrorString (m_gif_file->Error)); #elif GIFLIB_MAJOR == 4 && GIFLIB_MINOR >= 2 spin_lock lock (gif_error_mutex); error ("%s", GifErrorString()); #else spin_lock lock (gif_error_mutex); error ("GIF error %d", GifLastError()); #endif } inline bool GIFInput::close (void) { if (m_gif_file) { #if GIFLIB_MAJOR > 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1) if (DGifCloseFile (m_gif_file, NULL) == GIF_ERROR) { #else if (DGifCloseFile (m_gif_file) == GIF_ERROR) { #endif error ("Error trying to close the file."); return false; } m_gif_file = NULL; } m_canvas.clear(); return true; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/gif.imageio/gif.h0000644000175000017500000006743313151711064020225 0ustar mfvmfv// // gif.h // by Charlie Tangora // Public domain. // Email me : ctangora -at- gmail -dot- com // // This file offers a simple, very limited way to create animated GIFs directly in code. // // Those looking for particular cleverness are likely to be disappointed; it's pretty // much a straight-ahead implementation of the GIF format with optional Floyd-Steinberg // dithering. (It does at least use delta encoding - only the changed portions of each // frame are saved.) // // So resulting files are often quite large. The hope is that it will be handy nonetheless // as a quick and easily-integrated way for programs to spit out animations. // // Only RGBA8 is currently supported as an input format. (The alpha is ignored.) // // USAGE: // Create a GifWriter struct. Pass it to GifBegin() to initialize and write the header. // Pass subsequent frames to GifWriteFrame(). // Finally, call GifEnd() to close the file handle and free memory. // // GitHub source: https://github.com/ginsweater/gif-h /* This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to */ #ifndef gif_h #define gif_h #include // for FILE* #include // for memcpy and bzero #include // for integer typedefs // Define these macros to hook into a custom memory allocator. // TEMP_MALLOC and TEMP_FREE will only be called in stack fashion - frees in the reverse order of mallocs // and any temp memory allocated by a function will be freed before it exits. // MALLOC and FREE are used only by GifBegin and GifEnd respectively (to allocate a buffer the size of the image, which // is used to find changed pixels for delta-encoding.) #ifndef GIF_TEMP_MALLOC #include #define GIF_TEMP_MALLOC malloc #endif #ifndef GIF_TEMP_FREE #include #define GIF_TEMP_FREE free #endif #ifndef GIF_MALLOC #include #define GIF_MALLOC malloc #endif #ifndef GIF_FREE #include #define GIF_FREE free #endif const int kGifTransIndex = 0; struct GifPalette { int bitDepth; uint8_t r[256]; uint8_t g[256]; uint8_t b[256]; // k-d tree over RGB space, organized in heap fashion // i.e. left child of node i is node i*2, right child is node i*2+1 // nodes 256-511 are implicitly the leaves, containing a color uint8_t treeSplitElt[255]; uint8_t treeSplit[255]; }; // max, min, and abs functions int GifIMax(int l, int r) { return l>r?l:r; } int GifIMin(int l, int r) { return l (1<bitDepth)-1) { int ind = treeRoot-(1<bitDepth); if(ind == kGifTransIndex) return; // check whether this color is better than the current winner int r_err = r - ((int32_t)pPal->r[ind]); int g_err = g - ((int32_t)pPal->g[ind]); int b_err = b - ((int32_t)pPal->b[ind]); int diff = GifIAbs(r_err)+GifIAbs(g_err)+GifIAbs(b_err); if(diff < bestDiff) { bestInd = ind; bestDiff = diff; } return; } // take the appropriate color (r, g, or b) for this node of the k-d tree int comps[3]; comps[0] = r; comps[1] = g; comps[2] = b; int splitComp = comps[pPal->treeSplitElt[treeRoot]]; int splitPos = pPal->treeSplit[treeRoot]; if(splitPos > splitComp) { // check the left subtree GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2); if( bestDiff > splitPos - splitComp ) { // cannot prove there's not a better value in the right subtree, check that too GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2+1); } } else { GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2+1); if( bestDiff > splitComp - splitPos ) { GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2); } } } void GifSwapPixels(uint8_t* image, int pixA, int pixB) { uint8_t rA = image[pixA*4]; uint8_t gA = image[pixA*4+1]; uint8_t bA = image[pixA*4+2]; uint8_t aA = image[pixA*4+3]; uint8_t rB = image[pixB*4]; uint8_t gB = image[pixB*4+1]; uint8_t bB = image[pixB*4+2]; uint8_t aB = image[pixA*4+3]; image[pixA*4] = rB; image[pixA*4+1] = gB; image[pixA*4+2] = bB; image[pixA*4+3] = aB; image[pixB*4] = rA; image[pixB*4+1] = gA; image[pixB*4+2] = bA; image[pixB*4+3] = aA; } // just the partition operation from quicksort int GifPartition(uint8_t* image, const int left, const int right, const int elt, int pivotIndex) { const int pivotValue = image[(pivotIndex)*4+elt]; GifSwapPixels(image, pivotIndex, right-1); int storeIndex = left; bool split = 0; for(int ii=left; ii neededCenter) GifPartitionByMedian(image, left, pivotIndex, com, neededCenter); if(pivotIndex < neededCenter) GifPartitionByMedian(image, pivotIndex+1, right, com, neededCenter); } } // Builds a palette by creating a balanced k-d tree of all pixels in the image void GifSplitPalette(uint8_t* image, int numPixels, int firstElt, int lastElt, int splitElt, int splitDist, int treeNode, bool buildForDither, GifPalette* pal) { if(lastElt <= firstElt || numPixels == 0) return; // base case, bottom of the tree if(lastElt == firstElt+1) { if(buildForDither) { // Dithering needs at least one color as dark as anything // in the image and at least one brightest color - // otherwise it builds up error and produces strange artifacts if( firstElt == 1 ) { // special case: the darkest color in the image uint32_t r=255, g=255, b=255; for(int ii=0; iir[firstElt] = r; pal->g[firstElt] = g; pal->b[firstElt] = b; return; } if( firstElt == (1 << pal->bitDepth)-1 ) { // special case: the lightest color in the image uint32_t r=0, g=0, b=0; for(int ii=0; iir[firstElt] = r; pal->g[firstElt] = g; pal->b[firstElt] = b; return; } } // otherwise, take the average of all colors in this subcube uint64_t r=0, g=0, b=0; for(int ii=0; iir[firstElt] = (uint8_t)r; pal->g[firstElt] = (uint8_t)g; pal->b[firstElt] = (uint8_t)b; return; } // Find the axis with the largest range int minR = 255, maxR = 0; int minG = 255, maxG = 0; int minB = 255, maxB = 0; for(int ii=0; ii maxR) maxR = r; if(r < minR) minR = r; if(g > maxG) maxG = g; if(g < minG) minG = g; if(b > maxB) maxB = b; if(b < minB) minB = b; } int rRange = maxR - minR; int gRange = maxG - minG; int bRange = maxB - minB; // and split along that axis. (incidentally, this means this isn't a "proper" k-d tree but I don't know what else to call it) int splitCom = 1; if(bRange > gRange) splitCom = 2; if(rRange > bRange && rRange > gRange) splitCom = 0; int subPixelsA = numPixels * (splitElt - firstElt) / (lastElt - firstElt); int subPixelsB = numPixels-subPixelsA; GifPartitionByMedian(image, 0, numPixels, splitCom, subPixelsA); pal->treeSplitElt[treeNode] = splitCom; pal->treeSplit[treeNode] = image[subPixelsA*4+splitCom]; GifSplitPalette(image, subPixelsA, firstElt, splitElt, splitElt-splitDist, splitDist/2, treeNode*2, buildForDither, pal); GifSplitPalette(image+subPixelsA*4, subPixelsB, splitElt, lastElt, splitElt+splitDist, splitDist/2, treeNode*2+1, buildForDither, pal); } // Finds all pixels that have changed from the previous image and // moves them to the fromt of th buffer. // This allows us to build a palette optimized for the colors of the // changed pixels only. int GifPickChangedPixels( const uint8_t* lastFrame, uint8_t* frame, int numPixels ) { int numChanged = 0; uint8_t* writeIter = frame; for (int ii=0; iibitDepth = bitDepth; // SplitPalette is destructive (it sorts the pixels by color) so // we must create a copy of the image for it to destroy int imageSize = width*height*4*sizeof(uint8_t); uint8_t* destroyableImage = (uint8_t*)GIF_TEMP_MALLOC(imageSize); memcpy(destroyableImage, nextFrame, imageSize); int numPixels = width*height; if(lastFrame) numPixels = GifPickChangedPixels(lastFrame, destroyableImage, numPixels); const int lastElt = 1 << bitDepth; const int splitElt = lastElt/2; const int splitDist = splitElt/2; GifSplitPalette(destroyableImage, numPixels, 1, lastElt, splitElt, splitDist, 1, buildForDither, pPal); GIF_TEMP_FREE(destroyableImage); // add the bottom node for the transparency index pPal->treeSplit[1 << (bitDepth-1)] = 0; pPal->treeSplitElt[1 << (bitDepth-1)] = 0; pPal->r[0] = pPal->g[0] = pPal->b[0] = 0; } // Implements Floyd-Steinberg dithering, writes palette value to alpha void GifDitherImage( const uint8_t* lastFrame, const uint8_t* nextFrame, uint8_t* outFrame, uint32_t width, uint32_t height, GifPalette* pPal ) { int numPixels = width*height; // quantPixels initially holds color*256 for all pixels // The extra 8 bits of precision allow for sub-single-color error values // to be propagated int32_t* quantPixels = (int32_t*)GIF_TEMP_MALLOC(sizeof(int32_t)*numPixels*4); for( int ii=0; iir[bestInd]) * 256; int32_t g_err = nextPix[1] - int32_t(pPal->g[bestInd]) * 256; int32_t b_err = nextPix[2] - int32_t(pPal->b[bestInd]) * 256; nextPix[0] = pPal->r[bestInd]; nextPix[1] = pPal->g[bestInd]; nextPix[2] = pPal->b[bestInd]; nextPix[3] = bestInd; // Propagate the error to the four adjacent locations // that we haven't touched yet int quantloc_7 = (yy*width+xx+1); int quantloc_3 = (yy*width+width+xx-1); int quantloc_5 = (yy*width+width+xx); int quantloc_1 = (yy*width+width+xx+1); if(quantloc_7 < numPixels) { int32_t* pix7 = quantPixels+4*quantloc_7; pix7[0] += GifIMax( -pix7[0], r_err * 7 / 16 ); pix7[1] += GifIMax( -pix7[1], g_err * 7 / 16 ); pix7[2] += GifIMax( -pix7[2], b_err * 7 / 16 ); } if(quantloc_3 < numPixels) { int32_t* pix3 = quantPixels+4*quantloc_3; pix3[0] += GifIMax( -pix3[0], r_err * 3 / 16 ); pix3[1] += GifIMax( -pix3[1], g_err * 3 / 16 ); pix3[2] += GifIMax( -pix3[2], b_err * 3 / 16 ); } if(quantloc_5 < numPixels) { int32_t* pix5 = quantPixels+4*quantloc_5; pix5[0] += GifIMax( -pix5[0], r_err * 5 / 16 ); pix5[1] += GifIMax( -pix5[1], g_err * 5 / 16 ); pix5[2] += GifIMax( -pix5[2], b_err * 5 / 16 ); } if(quantloc_1 < numPixels) { int32_t* pix1 = quantPixels+4*quantloc_1; pix1[0] += GifIMax( -pix1[0], r_err / 16 ); pix1[1] += GifIMax( -pix1[1], g_err / 16 ); pix1[2] += GifIMax( -pix1[2], b_err / 16 ); } } } // Copy the palettized result to the output buffer for( int ii=0; iir[bestInd]; outFrame[1] = pPal->g[bestInd]; outFrame[2] = pPal->b[bestInd]; outFrame[3] = bestInd; } if(lastFrame) lastFrame += 4; outFrame += 4; nextFrame += 4; } } // Simple structure to write out the LZW-compressed portion of the image // one bit at a time struct GifBitStatus { uint8_t bitIndex; // how many bits in the partial byte written so far uint8_t byte; // current partial byte uint32_t chunkIndex; uint8_t chunk[256]; // bytes are written in here until we have 256 of them, then written to the file }; // insert a single bit void GifWriteBit( GifBitStatus& stat, uint32_t bit ) { bit = bit & 1; bit = bit << stat.bitIndex; stat.byte |= bit; ++stat.bitIndex; if( stat.bitIndex > 7 ) { // move the newly-finished byte to the chunk buffer stat.chunk[stat.chunkIndex++] = stat.byte; // and start a new byte stat.bitIndex = 0; stat.byte = 0; } } // write all bytes so far to the file void GifWriteChunk( FILE* f, GifBitStatus& stat ) { fputc(stat.chunkIndex, f); fwrite(stat.chunk, 1, stat.chunkIndex, f); stat.bitIndex = 0; stat.byte = 0; stat.chunkIndex = 0; } void GifWriteCode( FILE* f, GifBitStatus& stat, uint32_t code, uint32_t length ) { for( uint32_t ii=0; ii> 1; if( stat.chunkIndex == 255 ) { GifWriteChunk(f, stat); } } } // The LZW dictionary is a 256-ary tree constructed as the file is encoded, // this is one node struct GifLzwNode { uint16_t m_next[256]; }; // write a 256-color (8-bit) image palette to the file void GifWritePalette( const GifPalette* pPal, FILE* f ) { fputc(0, f); // first color: transparency fputc(0, f); fputc(0, f); for(int ii=1; ii<(1 << pPal->bitDepth); ++ii) { uint32_t r = pPal->r[ii]; uint32_t g = pPal->g[ii]; uint32_t b = pPal->b[ii]; fputc(r, f); fputc(g, f); fputc(b, f); } } // write the image header, LZW-compress and write out the image void GifWriteLzwImage(FILE* f, uint8_t* image, uint32_t left, uint32_t top, uint32_t width, uint32_t height, uint32_t delay, GifPalette* pPal) { // graphics control extension fputc(0x21, f); fputc(0xf9, f); fputc(0x04, f); fputc(0x05, f); // leave prev frame in place, this frame has transparency fputc(delay & 0xff, f); fputc((delay >> 8) & 0xff, f); fputc(kGifTransIndex, f); // transparent color index fputc(0, f); fputc(0x2c, f); // image descriptor block fputc(left & 0xff, f); // corner of image in canvas space fputc((left >> 8) & 0xff, f); fputc(top & 0xff, f); fputc((top >> 8) & 0xff, f); fputc(width & 0xff, f); // width and height of image fputc((width >> 8) & 0xff, f); fputc(height & 0xff, f); fputc((height >> 8) & 0xff, f); //fputc(0, f); // no local color table, no transparency //fputc(0x80, f); // no local color table, but transparency fputc(0x80 + pPal->bitDepth-1, f); // local color table present, 2 ^ bitDepth entries GifWritePalette(pPal, f); const int minCodeSize = pPal->bitDepth; const uint32_t clearCode = 1 << pPal->bitDepth; fputc(minCodeSize, f); // min code size 8 bits GifLzwNode* codetree = (GifLzwNode*)GIF_TEMP_MALLOC(sizeof(GifLzwNode)*4096); memset(codetree, 0, sizeof(GifLzwNode)*4096); int32_t curCode = -1; uint32_t codeSize = minCodeSize+1; uint32_t maxCode = clearCode+1; GifBitStatus stat; stat.byte = 0; stat.bitIndex = 0; stat.chunkIndex = 0; GifWriteCode(f, stat, clearCode, codeSize); // start with a fresh LZW dictionary for(uint32_t yy=0; yy= (1ul << codeSize) ) { // dictionary entry count has broken a size barrier, // we need more bits for codes codeSize++; } if( maxCode == 4095 ) { // the dictionary is full, clear it out and begin anew GifWriteCode(f, stat, clearCode, codeSize); // clear tree memset(codetree, 0, sizeof(GifLzwNode)*4096); curCode = -1; codeSize = minCodeSize+1; maxCode = clearCode+1; } curCode = nextValue; } } } // compression footer GifWriteCode( f, stat, curCode, codeSize ); GifWriteCode( f, stat, clearCode, codeSize ); GifWriteCode( f, stat, clearCode+1, minCodeSize+1 ); // write out the last partial chunk while( stat.bitIndex ) GifWriteBit(stat, 0); if( stat.chunkIndex ) GifWriteChunk(f, stat); fputc(0, f); // image block terminator GIF_TEMP_FREE(codetree); } struct GifWriter { FILE* f; uint8_t* oldImage; bool firstFrame; }; // Creates a gif file. // The input GIFWriter is assumed to be uninitialized. // The delay value is the time between frames in hundredths of a second - note that not all viewers pay much attention to this value. bool GifBegin( GifWriter* writer, const char* filename, uint32_t width, uint32_t height, uint32_t delay, int32_t bitDepth = 8, bool dither = false ) { #if _MSC_VER >= 1400 writer->f = 0; fopen_s(&writer->f, filename, "wb"); #else writer->f = fopen(filename, "wb"); #endif if(!writer->f) return false; writer->firstFrame = true; // allocate writer->oldImage = (uint8_t*)GIF_MALLOC(width*height*4); fputs("GIF89a", writer->f); // screen descriptor fputc(width & 0xff, writer->f); fputc((width >> 8) & 0xff, writer->f); fputc(height & 0xff, writer->f); fputc((height >> 8) & 0xff, writer->f); fputc(0xf0, writer->f); // there is an unsorted global color table of 2 entries fputc(0, writer->f); // background color fputc(0, writer->f); // pixels are square (we need to specify this because it's 1989) // now the "global" palette (really just a dummy palette) // color 0: black fputc(0, writer->f); fputc(0, writer->f); fputc(0, writer->f); // color 1: also black fputc(0, writer->f); fputc(0, writer->f); fputc(0, writer->f); if( delay != 0 ) { // animation header fputc(0x21, writer->f); // extension fputc(0xff, writer->f); // application specific fputc(11, writer->f); // length 11 fputs("NETSCAPE2.0", writer->f); // yes, really fputc(3, writer->f); // 3 bytes of NETSCAPE2.0 data fputc(1, writer->f); // JUST BECAUSE fputc(0, writer->f); // loop infinitely (byte 0) fputc(0, writer->f); // loop infinitely (byte 1) fputc(0, writer->f); // block terminator } return true; } // Writes out a new frame to a GIF in progress. // The GIFWriter should have been created by GIFBegin. // AFAIK, it is legal to use different bit depths for different frames of an image - // this may be handy to save bits in animations that don't change much. bool GifWriteFrame( GifWriter* writer, const uint8_t* image, uint32_t width, uint32_t height, uint32_t delay, int bitDepth = 8, bool dither = false ) { if(!writer->f) return false; const uint8_t* oldImage = writer->firstFrame? NULL : writer->oldImage; writer->firstFrame = false; GifPalette pal; GifMakePalette((dither? NULL : oldImage), image, width, height, bitDepth, dither, &pal); if(dither) GifDitherImage(oldImage, image, writer->oldImage, width, height, &pal); else GifThresholdImage(oldImage, image, writer->oldImage, width, height, &pal); GifWriteLzwImage(writer->f, writer->oldImage, 0, 0, width, height, delay, &pal); return true; } // Writes the EOF code, closes the file handle, and frees temp memory used by a GIF. // Many if not most viewers will still display a GIF properly if the EOF code is missing, // but it's still a good idea to write it out. bool GifEnd( GifWriter* writer ) { if(!writer->f) return false; fputc(0x3b, writer->f); // end of file fclose(writer->f); GIF_FREE(writer->oldImage); writer->f = NULL; writer->oldImage = NULL; return true; } #endif openimageio-1.7.17~dfsg0.orig/src/psd.imageio/0000755000175000017500000000000013151711064017313 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/psd.imageio/jpeg_memory_src.h0000644000175000017500000000436113151711064022654 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_PSD_JPEG_MEMORY_SRC_H #define OPENIMAGEIO_PSD_JPEG_MEMORY_SRC_H #ifdef WIN32 //#undef FAR #define XMD_H #endif extern "C" { #include "jpeglib.h" } OIIO_PLUGIN_NAMESPACE_BEGIN namespace psd_pvt { // This function allows you to read a JPEG from memory using libjpeg. // Newer versions of libjpeg have jpeg_mem_src which has the same functionality. // inbuffer is the buffer that holds the JPEG data // insize is the size of the buffer void jpeg_memory_src (j_decompress_ptr cinfo, unsigned char *inbuffer, unsigned long insize); } // end namespace psd_pvt OIIO_PLUGIN_NAMESPACE_END #endif /* OPENIMAGEIO_PSD_JPEG_MEMORY_SRC_H */ openimageio-1.7.17~dfsg0.orig/src/psd.imageio/CMakeLists.txt0000644000175000017500000000024613151711064022055 0ustar mfvmfvadd_oiio_plugin (psdinput.cpp psdoutput.cpp jpeg_memory_src.cpp INCLUDE_DIRS ${JPEG_INCLUDE_DIR} LINK_LIBRARIES ${JPEG_LIBRARIES}) openimageio-1.7.17~dfsg0.orig/src/psd.imageio/psdoutput.cpp0000644000175000017500000000625613151711064022077 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include "OpenImageIO/imageio.h" OIIO_PLUGIN_NAMESPACE_BEGIN class PSDOutput : public ImageOutput { public: PSDOutput (); virtual ~PSDOutput (); virtual const char * format_name (void) const { return "psd"; } virtual int supports (string_view feature) const { return (feature == "alpha"); } virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode=Create); virtual bool close (); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); private: std::string m_filename; ///< Stash the filename std::ofstream m_file; ///< Open image handle // Initialize private members to pre-opened state void init (void) { } }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *psd_output_imageio_create () { return new PSDOutput; } OIIO_EXPORT const char * psd_output_extensions[] = { "psd", NULL }; OIIO_PLUGIN_EXPORTS_END PSDOutput::PSDOutput () { init (); } PSDOutput::~PSDOutput () { // Close, if not already done. close (); } bool PSDOutput::open (const std::string &name, const ImageSpec &userspec, OpenMode mode) { return false; } bool PSDOutput::close () { init (); return false; } bool PSDOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { return false; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/psd.imageio/jpeg_memory_src.cpp0000644000175000017500000000657413151711064023217 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include "OpenImageIO/imageio.h" #include "jpeg_memory_src.h" #include "jerror.h" namespace { void init_memory_source (j_decompress_ptr cinfo) { } void term_memory_source (j_decompress_ptr cinfo) { } void skip_input (j_decompress_ptr cinfo, long num_bytes) { struct jpeg_source_mgr *src = cinfo->src; if (!num_bytes) return; while (num_bytes > (long)src->bytes_in_buffer) { num_bytes -= (long)src->bytes_in_buffer; (*src->fill_input_buffer)(cinfo); } src->next_input_byte += (size_t)num_bytes; src->bytes_in_buffer -= (size_t)num_bytes; } boolean fill_input (j_decompress_ptr cinfo) { static JOCTET mybuffer[4]; WARNMS(cinfo, JWRN_JPEG_EOF); /* Insert a fake EOI marker */ mybuffer[0] = (JOCTET) 0xFF; mybuffer[1] = (JOCTET) JPEG_EOI; cinfo->src->next_input_byte = mybuffer; cinfo->src->bytes_in_buffer = 2; return TRUE; } } // anon namespace OIIO_PLUGIN_NAMESPACE_BEGIN namespace psd_pvt { void jpeg_memory_src (j_decompress_ptr cinfo, unsigned char *inbuffer, unsigned long insize) { struct jpeg_source_mgr * src; if (inbuffer == NULL || insize == 0) ERREXIT(cinfo, JERR_INPUT_EMPTY); if (cinfo->src == NULL) { cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, (size_t)sizeof(struct jpeg_source_mgr)); } src = cinfo->src; src->init_source = init_memory_source; src->fill_input_buffer = fill_input; src->skip_input_data = skip_input; src->resync_to_restart = jpeg_resync_to_restart; src->term_source = term_memory_source; src->bytes_in_buffer = (size_t) insize; src->next_input_byte = (JOCTET *) inbuffer; } } // namespace psd_pvt OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/psd.imageio/psdinput.cpp0000644000175000017500000017150213151711064021673 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include #include "psd_pvt.h" #include "jpeg_memory_src.h" OIIO_PLUGIN_NAMESPACE_BEGIN using namespace psd_pvt; class PSDInput : public ImageInput { public: PSDInput (); virtual ~PSDInput () { close(); } virtual const char * format_name (void) const { return "psd"; } virtual int supports (string_view feature) const { return (feature == "exif" || feature == "iptc"); } virtual bool open (const std::string &name, ImageSpec &newspec); virtual bool open (const std::string &name, ImageSpec &newspec, const ImageSpec &config); virtual bool close (); virtual int current_subimage () const { return m_subimage; } virtual bool seek_subimage (int subimage, int miplevel, ImageSpec &newspec); virtual bool read_native_scanline (int y, int z, void *data); private: enum ColorMode { ColorMode_Bitmap = 0, ColorMode_Grayscale = 1, ColorMode_Indexed = 2, ColorMode_RGB = 3, ColorMode_CMYK = 4, ColorMode_Multichannel = 7, ColorMode_Duotone = 8, ColorMode_Lab = 9 }; enum Compression { Compression_Raw = 0, Compression_RLE = 1, Compression_ZIP = 2, Compression_ZIP_Predict = 3 }; enum ChannelID { ChannelID_Transparency = -1, ChannelID_LayerMask = -2, ChannelID_UserMask = -3 }; // Image resource loaders to handle loading certain image resources // into ImageSpec struct ResourceLoader { uint16_t resource_id; boost::function load; }; // Map image resource ID to image resource block typedef std::map ImageResourceMap; struct ResolutionInfo { float hRes; int16_t hResUnit; int16_t widthUnit; float vRes; int16_t vResUnit; int16_t heightUnit; enum ResolutionUnit { PixelsPerInch = 1, PixelsPerCentimeter = 2 }; enum Unit { Inches = 1, Centimeters = 2, Points = 3, Picas = 4, Columns = 5 }; }; struct LayerMaskInfo { uint64_t length; std::streampos begin; std::streampos end; struct LayerInfo { uint64_t length; int16_t layer_count; std::streampos begin; std::streampos end; }; LayerInfo layer_info; }; struct ChannelInfo { uint32_t row_length; int16_t channel_id; uint64_t data_length; std::streampos data_pos; uint16_t compression; std::vector rle_lengths; std::vector row_pos; }; struct Layer { uint32_t top, left, bottom, right; uint32_t width, height; uint16_t channel_count; std::vector channel_info; std::map channel_id_map; char bm_key[4]; uint8_t opacity; uint8_t clipping; uint8_t flags; uint32_t extra_length; struct MaskData { uint32_t top, left, bottom, right; uint8_t default_color; uint8_t flags; }; MaskData mask_data; //TODO: layer blending ranges? std::string name; struct AdditionalInfo { char key[4]; uint64_t length; std::streampos pos; }; std::vector additional_info; }; struct GlobalMaskInfo { uint16_t overlay_color_space; uint16_t color_components[4]; uint16_t opacity; int8_t kind; }; struct ImageDataSection { std::vector channel_info; //When the layer count is negative, this is true and indicates that //the first alpha channel should be used as transparency (for the //merged image) bool transparency; }; std::string m_filename; OIIO::ifstream m_file; //Current subimage int m_subimage; //Subimage count (1 + layer count) int m_subimage_count; std::vector m_specs; static const ResourceLoader resource_loaders[]; //This holds the attributes for the merged image (subimage 0) ImageSpec m_composite_attribs; //This holds common attributes that apply to all subimages ImageSpec m_common_attribs; //psd:RawData config option, indicates that the user wants the raw, //unconverted channel data bool m_WantRaw; TypeDesc m_type_desc; //This holds all the ChannelInfos for all subimages //Example: m_channels[subimg][channel] std::vector > m_channels; //Alpha Channel Names, not currently used std::vector m_alpha_names; //Buffers for channel data std::vector m_channel_buffers; //Buffer for RLE conversion std::string m_rle_buffer; //Index of the transparent color, if any (for Indexed color mode only) int16_t m_transparency_index; //Background color double m_background_color[4]; ///< Do not convert unassociated alpha bool m_keep_unassociated_alpha; FileHeader m_header; ColorModeData m_color_data; LayerMaskInfo m_layer_mask_info; std::vector m_layers; GlobalMaskInfo m_global_mask_info; ImageDataSection m_image_data; //Reset to initial state void init (); //File Header bool load_header (); bool read_header (); bool validate_header (); //Color Mode Data bool load_color_data (); bool validate_color_data (); //Image Resources bool load_resources (); bool read_resource (ImageResourceBlock &block); bool validate_resource (ImageResourceBlock &block); //Call the resource_loaders to load the resources into an ImageSpec //m_specs should be resized to m_subimage_count first bool handle_resources (ImageResourceMap &resources); //ResolutionInfo bool load_resource_1005 (uint32_t length); //Alpha Channel Names bool load_resource_1006 (uint32_t length); //Background Color bool load_resource_1010 (uint32_t length); //JPEG thumbnail (Photoshop 4.0) bool load_resource_1033 (uint32_t length); //JPEG thumbnail (Photoshop 5.0) bool load_resource_1036 (uint32_t length); //Transparency index (Indexed color mode) bool load_resource_1047 (uint32_t length); //Exif data 1 bool load_resource_1058 (uint32_t length); //Exif data 3 bool load_resource_1059 (uint32_t length); //XMP metadata bool load_resource_1060 (uint32_t length); //Pixel Aspect Ratio bool load_resource_1064 (uint32_t length); //Load thumbnail resource, used for resources 1033 and 1036 bool load_resource_thumbnail (uint32_t length, bool isBGR); //For thumbnail loading struct thumbnail_error_mgr { jpeg_error_mgr pub; jmp_buf setjmp_buffer; }; METHODDEF (void) thumbnail_error_exit (j_common_ptr cinfo); //Layers bool load_layers (); bool load_layer (Layer &layer); bool load_layer_channels (Layer &layer); bool load_layer_channel (Layer &layer, ChannelInfo &channel_info); bool read_rle_lengths (uint32_t height, std::vector &rle_lengths); //Global Mask Info bool load_global_mask_info (); //Global Additional Layer Info bool load_global_additional (); //Image Data Section bool load_image_data (); void set_type_desc (); //Setup m_specs and m_channels void setup (); void fill_channel_names (ImageSpec &spec, bool transparency); //Read a row of channel data bool read_channel_row (const ChannelInfo &channel_info, uint32_t row, char *data); // Interleave channels (RRRGGGBBB -> RGBRGBRGB) while copying from // m_channel_buffers[0..nchans-1] to dst. template void interleave_row (T *dst, size_t nchans); //Convert the channel data to RGB bool indexed_to_rgb (char *dst); bool bitmap_to_rgb (char *dst); // Convert from photoshop native alpha to // associated/premultiplied template void removeBackground (T *data, int size, int nchannels, int alpha_channel, double *background) { // RGB = CompRGB - (1 - alpha) * Background; double scale = std::numeric_limits::is_integer ? 1.0/std::numeric_limits::max() : 1.0; for ( ; size; --size, data += nchannels) for (int c = 0; c < nchannels; c++) if (c != alpha_channel) { double alpha = data[alpha_channel] * scale; double f = data[c]; data[c] = T (f - (((1.0 - alpha) * background[c]) / scale)); } } template void unassociateAlpha (T *data, int size, int nchannels, int alpha_channel, double *background) { // RGB = (CompRGB - (1 - alpha) * Background) / alpha double scale = std::numeric_limits::is_integer ? 1.0/std::numeric_limits::max() : 1.0; for ( ; size; --size, data += nchannels) for (int c = 0; c < nchannels; c++) if (c != alpha_channel) { double alpha = data[alpha_channel] * scale; double f = data[c]; if (alpha > 0.0) data[c] = T ((f - (((1.0 - alpha) * background[c]) / scale)) / alpha) ; else data[c] = 0; } } template void associateAlpha (T *data, int size, int nchannels, int alpha_channel) { double scale = std::numeric_limits::is_integer ? 1.0/std::numeric_limits::max() : 1.0; for ( ; size; --size, data += nchannels) for (int c = 0; c < nchannels; c++) if (c != alpha_channel) { double f = data[c]; data[c] = T (f * (data[alpha_channel] * scale)); } } void background_to_assocalpha (int n, void *data); void background_to_unassalpha (int n, void *data); void unassalpha_to_assocalpha (int n, void *data); template void cmyk_to_rgb (int n, const T *cmyk, size_t cmyk_stride, T *rgb, size_t rgb_stride) { for ( ; n; --n, cmyk += cmyk_stride, rgb += rgb_stride) { float C = convert_type(cmyk[0]); float M = convert_type(cmyk[1]); float Y = convert_type(cmyk[2]); float K = convert_type(cmyk[3]); #if 0 // WHY doesn't this work if it's cmyk? float R = (1.0f - C) * (1.0f - K); float G = (1.0f - M) * (1.0f - K); float B = (1.0f - Y) * (1.0f - K); #else // But this gives the right results????? WTF? // Is it because it's subtractive and PhotoShop records it // as MAX-val? float R = C * (K); float G = M * (K); float B = Y * (K); #endif rgb[0] = convert_type(R); rgb[1] = convert_type(G); rgb[2] = convert_type(B); } } //Check if m_file is good. If not, set error message and return false. bool check_io (); //This may be a bit inefficient but I think it's worth the convenience. //This takes care of things like reading a 32-bit BE into a 64-bit LE. template bool read_bige (TVariable &value) { TStorage buffer; m_file.read ((char *)&buffer, sizeof(buffer)); if (!bigendian ()) swap_endian (&buffer); // For debugging, numeric_cast will throw if precision is lost: // value = boost::numeric_cast(buffer); value = buffer; return m_file.good(); } int read_pascal_string (std::string &s, uint16_t mod_padding); bool decompress_packbits (const char *src, char *dst, uint16_t packed_length, uint16_t unpacked_length); // These are AdditionalInfo entries that, for PSBs, have an 8-byte length static const char *additional_info_psb[]; static const unsigned int additional_info_psb_count; bool is_additional_info_psb (const char *key); // Channel names and counts for each color mode static const char *mode_channel_names[][4]; static const unsigned int mode_channel_count[]; // Some attributes may apply to only the merged composite. // Others may apply to all subimages. // These functions are intended to be used by image resource loaders. // // Add an attribute to the composite image spec template void composite_attribute (const std::string &name, const T &value) { m_composite_attribs.attribute (name, value); } // Add an attribute to the composite image spec template void composite_attribute (const std::string &name, const TypeDesc &type, const T &value) { m_composite_attribs.attribute (name, type, value); } // Add an attribute to the composite image spec and common image spec template void common_attribute (const std::string &name, const T &value) { m_composite_attribs.attribute (name, value); m_common_attribs.attribute (name, value); } // Add an attribute to the composite image spec and common image spec template void common_attribute (const std::string &name, const TypeDesc &type, const T &value) { m_composite_attribs.attribute (name, type, value); m_common_attribs.attribute (name, type, value); } }; // Image resource loaders // To add an image resource loader, do the following: // 1) Add ADD_LOADER() below // 2) Add a method in PSDInput: // bool load_resource_ (uint32_t length); #define ADD_LOADER(id) {id, boost::bind (&PSDInput::load_resource_##id, _1, _2)} const PSDInput::ResourceLoader PSDInput::resource_loaders[] = { ADD_LOADER(1005), ADD_LOADER(1006), ADD_LOADER(1010), ADD_LOADER(1033), ADD_LOADER(1036), ADD_LOADER(1047), ADD_LOADER(1058), ADD_LOADER(1059), ADD_LOADER(1060), ADD_LOADER(1064) }; #undef ADD_LOADER const char * PSDInput::additional_info_psb[] = { "LMsk", "Lr16", "Lr32", "Layr", "Mt16", "Mt32", "Mtrn", "Alph", "FMsk", "Ink2", "FEid", "FXid", "PxSD" }; const unsigned int PSDInput::additional_info_psb_count = sizeof(additional_info_psb) / sizeof(additional_info_psb[0]); const char * PSDInput::mode_channel_names[][4] = { {"A"}, {"I"}, {"I"}, {"R", "G", "B"}, {"C", "M", "Y", "K"}, {}, {}, {}, {}, {"L", "a", "b"} }; const unsigned int PSDInput::mode_channel_count[] = { 1, 1, 1, 3, 4, 0, 0, 0, 0, 3 }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageInput *psd_input_imageio_create () { return new PSDInput; } OIIO_EXPORT int psd_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* psd_imageio_library_version () { return NULL; } OIIO_EXPORT const char * psd_input_extensions[] = { "psd", "pdd", "psb", NULL }; OIIO_PLUGIN_EXPORTS_END PSDInput::PSDInput () { init(); } bool PSDInput::open (const std::string &name, ImageSpec &newspec) { m_filename = name; Filesystem::open (m_file, name, std::ios::binary); if (!m_file) { error ("\"%s\": failed to open file", name); return false; } // File Header if (!load_header ()) { error ("failed to open \"%s\": failed load_header", name); return false; } // Color Mode Data if (!load_color_data ()) { error ("failed to open \"%s\": failed load_color_data", name); return false; } // Image Resources if (!load_resources ()) { error ("failed to open \"%s\": failed load_resources", name); return false; } // Layers if (!load_layers ()) { error ("failed to open \"%s\": failed load_layers", name); return false; } // Global Mask Info if (!load_global_mask_info ()) { error ("failed to open \"%s\": failed load_global_mask_info", name); return false; } // Global Additional Layer Info if (!load_global_additional ()) { error ("failed to open \"%s\": failed load_global_additional", name); return false; } // Image Data if (!load_image_data ()) { error ("failed to open \"%s\": failed load_image_data", name); return false; } // Layer count + 1 for merged composite (Image Data Section) m_subimage_count = m_layers.size () + 1; // Set m_type_desc to the appropriate TypeDesc set_type_desc (); // Setup ImageSpecs and m_channels setup (); if (!seek_subimage (0, 0, newspec)) return false; return true; } bool PSDInput::open (const std::string &name, ImageSpec &newspec, const ImageSpec &config) { m_WantRaw = config.get_int_attribute ("psd:RawData") || config.get_int_attribute ("oiio:RawColor"); if (config.get_int_attribute("oiio:UnassociatedAlpha", 0) == 1) m_keep_unassociated_alpha = true; return open (name, newspec); } bool PSDInput::close () { init(); return true; } bool PSDInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec) { if (miplevel != 0) return false; if (subimage < 0 || subimage >= m_subimage_count) return false; m_subimage = subimage; newspec = m_spec = m_specs[subimage]; return true; } void PSDInput::background_to_assocalpha (int n, void *data) { switch (m_spec.format.basetype) { case TypeDesc::UINT8: removeBackground ((unsigned char *)data, n, m_spec.nchannels, m_spec.alpha_channel, m_background_color); break; case TypeDesc::UINT16: removeBackground ((unsigned short *)data, n, m_spec.nchannels, m_spec.alpha_channel, m_background_color); break; case TypeDesc::UINT32: removeBackground ((unsigned long *)data, n, m_spec.nchannels, m_spec.alpha_channel, m_background_color); break; case TypeDesc::FLOAT: removeBackground ((float *)data, n, m_spec.nchannels, m_spec.alpha_channel, m_background_color); break; default: break; } } void PSDInput::background_to_unassalpha (int n, void *data) { switch (m_spec.format.basetype) { case TypeDesc::UINT8: unassociateAlpha ((unsigned char *)data, n, m_spec.nchannels, m_spec.alpha_channel, m_background_color); break; case TypeDesc::UINT16: unassociateAlpha ((unsigned short *)data, n, m_spec.nchannels, m_spec.alpha_channel, m_background_color); break; case TypeDesc::UINT32: unassociateAlpha ((unsigned long *)data, n, m_spec.nchannels, m_spec.alpha_channel, m_background_color); break; case TypeDesc::FLOAT: unassociateAlpha ((float *)data, n, m_spec.nchannels, m_spec.alpha_channel, m_background_color); break; default: break; } } void PSDInput::unassalpha_to_assocalpha (int n, void *data) { switch (m_spec.format.basetype) { case TypeDesc::UINT8: associateAlpha ((unsigned char *)data, n, m_spec.nchannels, m_spec.alpha_channel); break; case TypeDesc::UINT16: associateAlpha ((unsigned short *)data, n, m_spec.nchannels, m_spec.alpha_channel); break; case TypeDesc::UINT32: associateAlpha ((unsigned long *)data, n, m_spec.nchannels, m_spec.alpha_channel); break; case TypeDesc::FLOAT: associateAlpha ((float *)data, n, m_spec.nchannels, m_spec.alpha_channel); break; default: break; } } bool PSDInput::read_native_scanline (int y, int z, void *data) { if (y < 0 || y > m_spec.height) return false; if (m_channel_buffers.size () < m_channels[m_subimage].size ()) m_channel_buffers.resize (m_channels[m_subimage].size ()); int bps = (m_header.depth + 7) / 8; // bytes per sample ASSERT (bps == 1 || bps == 2 || bps == 4); std::vector &channels = m_channels[m_subimage]; int channel_count = (int)channels.size (); for (int c = 0; c < channel_count; ++c) { std::string &buffer = m_channel_buffers[c]; ChannelInfo &channel_info = *channels[c]; if (buffer.size () < channel_info.row_length) buffer.resize (channel_info.row_length); if (!read_channel_row (channel_info, y, &buffer[0])) return false; } char *dst = (char *)data; if (m_WantRaw || m_header.color_mode == ColorMode_RGB || m_header.color_mode == ColorMode_Multichannel || m_header.color_mode == ColorMode_Grayscale) { switch (bps) { case 4: interleave_row ((float *)dst, m_channels[m_subimage].size()); break; case 2: interleave_row ((unsigned short *)dst, m_channels[m_subimage].size()); break; default: interleave_row ((unsigned char *)dst, m_channels[m_subimage].size()); break; } } else if (m_header.color_mode == ColorMode_CMYK) { switch (bps) { case 4: { boost::scoped_array cmyk (new float [4*m_spec.width]); interleave_row (cmyk.get(), 4); cmyk_to_rgb (m_spec.width, cmyk.get(), 4, (float *)dst, m_spec.nchannels); break; } case 2: { boost::scoped_array cmyk (new unsigned short [4*m_spec.width]); interleave_row (cmyk.get(), 4); cmyk_to_rgb (m_spec.width, cmyk.get(), 4, (unsigned short *)dst, m_spec.nchannels); break; } default: { boost::scoped_array cmyk (new unsigned char [4*m_spec.width]); interleave_row (cmyk.get(), 4); cmyk_to_rgb (m_spec.width, cmyk.get(), 4, (unsigned char *)dst, m_spec.nchannels); break; } } } else if (m_header.color_mode == ColorMode_Indexed) { if (!indexed_to_rgb (dst)) return false; } else if (m_header.color_mode == ColorMode_Bitmap) { if (!bitmap_to_rgb (dst)) return false; } else { ASSERT (0 && "unknown color mode"); } // PSD specifically dictates unassociated (un-"premultiplied") alpha. // Convert to associated unless we were requested not to do so. // // Composite layer (subimage 0) is mixed with background, which // affects the alpha (aka white borders if background not removed). // // Composite: // m_keep_unassociated_alpha true: remove background and convert to unassociated // m_keep_unassociated_alpha false: remove background only // // Other Layers: // m_keep_unassociated_alpha true: do nothing // m_keep_unassociated_alpha false: convert to associated // // if (m_spec.alpha_channel != -1) { if (m_subimage == 0) { if (m_keep_unassociated_alpha) { background_to_unassalpha (m_spec.width, data); } else { background_to_assocalpha (m_spec.width, data); } } else { if (m_keep_unassociated_alpha) { // do nothing - leave as it is } else { unassalpha_to_assocalpha (m_spec.width, data); } } } return true; #undef DEB } void PSDInput::init () { m_filename.clear (); m_file.close(); m_subimage = -1; m_subimage_count = 0; m_specs.clear (); m_WantRaw = false; m_layers.clear (); m_image_data.channel_info.clear (); m_image_data.transparency = false; m_channels.clear (); m_alpha_names.clear (); m_channel_buffers.clear (); m_rle_buffer.clear (); m_transparency_index = -1; m_keep_unassociated_alpha = false; m_background_color[0] = 1.0; m_background_color[1] = 1.0; m_background_color[2] = 1.0; m_background_color[3] = 1.0; } bool PSDInput::load_header () { if (!read_header () || !validate_header ()) return false; return true; } bool PSDInput::read_header () { m_file.read (m_header.signature, 4); read_bige (m_header.version); m_file.seekg(6, std::ios::cur); read_bige (m_header.channel_count); read_bige (m_header.height); read_bige (m_header.width); read_bige (m_header.depth); read_bige (m_header.color_mode); return check_io (); } bool PSDInput::validate_header () { if (std::memcmp (m_header.signature, "8BPS", 4) != 0) { error ("[Header] invalid signature"); return false; } if (m_header.version != 1 && m_header.version != 2) { error ("[Header] invalid version"); return false; } if (m_header.channel_count < 1 || m_header.channel_count > 56) { error ("[Header] invalid channel count"); return false; } switch (m_header.version) { case 1: // PSD // width/height range: [1,30000] if (m_header.height < 1 || m_header.height > 30000) { error ("[Header] invalid image height"); return false; } if (m_header.width < 1 || m_header.width > 30000) { error ("[Header] invalid image width"); return false; } break; case 2: // PSB (Large Document Format) // width/height range: [1,300000] if (m_header.height < 1 || m_header.height > 300000) { error ("[Header] invalid image height"); return false; } if (m_header.width < 1 || m_header.width > 300000) { error ("[Header] invalid image width"); return false; } break; } // Valid depths are 1,8,16,32 if (m_header.depth != 1 && m_header.depth != 8 && m_header.depth != 16 && m_header.depth != 32) { error ("[Header] invalid depth"); return false; } if (m_WantRaw) return true; //There are other (undocumented) color modes not listed here switch (m_header.color_mode) { case ColorMode_Bitmap : case ColorMode_Indexed : case ColorMode_RGB : case ColorMode_Grayscale : case ColorMode_CMYK : case ColorMode_Multichannel : break; case ColorMode_Duotone : case ColorMode_Lab : error ("[Header] unsupported color mode"); return false; default: error ("[Header] unrecognized color mode"); return false; } return true; } bool PSDInput::load_color_data () { read_bige (m_color_data.length); if (!check_io ()) return false; if (!validate_color_data ()) return false; if (m_color_data.length) { m_color_data.data.resize (m_color_data.length); m_file.read (&m_color_data.data[0], m_color_data.length); } return check_io (); } bool PSDInput::validate_color_data () { if (m_header.color_mode == ColorMode_Duotone && m_color_data.length == 0) { error ("[Color Mode Data] color mode data should be present for duotone image"); return false; } if (m_header.color_mode == ColorMode_Indexed && m_color_data.length != 768) { error ("[Color Mode Data] length should be 768 for indexed color mode"); return false; } return true; } bool PSDInput::load_resources () { uint32_t length; read_bige (length); if (!check_io ()) return false; ImageResourceBlock block; ImageResourceMap resources; std::streampos begin = m_file.tellg (); std::streampos end = begin + (std::streampos)length; while (m_file && m_file.tellg () < end) { if (!read_resource (block) || !validate_resource (block)) return false; resources.insert (std::make_pair (block.id, block)); } if (!check_io ()) return false; if (!handle_resources (resources)) return false; m_file.seekg (end); return check_io (); } bool PSDInput::read_resource (ImageResourceBlock &block) { m_file.read (block.signature, 4); read_bige (block.id); read_pascal_string (block.name, 2); read_bige (block.length); // Save the file position of the image resource data block.pos = m_file.tellg(); // Skip the image resource data m_file.seekg (block.length, std::ios::cur); // Image resource blocks are supposed to be padded to an even size. // I'm not sure if the padding is included in the length field if (block.length % 2 != 0) m_file.seekg(1, std::ios::cur); return check_io (); } bool PSDInput::validate_resource (ImageResourceBlock &block) { if (std::memcmp (block.signature, "8BIM", 4) != 0) { error ("[Image Resource] invalid signature"); return false; } return true; } bool PSDInput::handle_resources (ImageResourceMap &resources) { // Loop through each of our resource loaders const ImageResourceMap::const_iterator end (resources.end ()); BOOST_FOREACH (const ResourceLoader &loader, resource_loaders) { ImageResourceMap::const_iterator it (resources.find (loader.resource_id)); // If a resource with that ID exists in the file, call the loader if (it != end) { m_file.seekg (it->second.pos); if (!check_io ()) return false; loader.load (this, it->second.length); if (!check_io ()) return false; } } return true; } bool PSDInput::load_resource_1005 (uint32_t length) { ResolutionInfo resinfo; // Fixed 16.16 read_bige (resinfo.hRes); resinfo.hRes /= 65536.0f; read_bige (resinfo.hResUnit); read_bige (resinfo.widthUnit); // Fixed 16.16 read_bige (resinfo.vRes); resinfo.vRes /= 65536.0f; read_bige (resinfo.vResUnit); read_bige (resinfo.heightUnit); if (!m_file) return false; // Make sure the same unit is used both horizontally and vertically // FIXME(dewyatt): I don't know for sure that the unit can differ. However, // if it can, perhaps we should be using ResolutionUnitH/ResolutionUnitV or // something similar. if (resinfo.hResUnit != resinfo.vResUnit) { error ("[Image Resource] [ResolutionInfo] Resolutions must have the same unit"); return false; } // Make sure the unit is supported // Note: This relies on the above check that the units are the same. if (resinfo.hResUnit != ResolutionInfo::PixelsPerInch && resinfo.hResUnit != ResolutionInfo::PixelsPerCentimeter) { error ("[Image Resource] [ResolutionInfo] Unrecognized resolution unit"); return false; } common_attribute ("XResolution", resinfo.hRes); common_attribute ("XResolution", resinfo.hRes); common_attribute ("YResolution", resinfo.vRes); switch (resinfo.hResUnit) { case ResolutionInfo::PixelsPerInch: common_attribute ("ResolutionUnit", "in"); break; case ResolutionInfo::PixelsPerCentimeter: common_attribute ("ResolutionUnit", "cm"); break; }; return true; } bool PSDInput::load_resource_1006 (uint32_t length) { int32_t bytes_remaining = length; std::string name; while (m_file && bytes_remaining >= 2) { bytes_remaining -= read_pascal_string (name, 1); m_alpha_names.push_back (name); } return check_io (); } bool PSDInput::load_resource_1010 (uint32_t length) { const double int8_to_dbl = 1.0 / 0xFF; int8_t color_id; int32_t color; read_bige (color_id); read_bige (color); m_background_color[0] = ((color) & 0xFF) * int8_to_dbl; m_background_color[1] = ((color >> 8) & 0xFF) * int8_to_dbl; m_background_color[2] = ((color >> 16) & 0xFF) * int8_to_dbl; m_background_color[3] = ((color >> 24) & 0xFF) * int8_to_dbl; return true; } bool PSDInput::load_resource_1033 (uint32_t length) { return load_resource_thumbnail (length, true); } bool PSDInput::load_resource_1036 (uint32_t length) { return load_resource_thumbnail (length, false); } bool PSDInput::load_resource_1047 (uint32_t length) { read_bige (m_transparency_index); if (m_transparency_index < 0 || m_transparency_index >= 768) { error ("[Image Resource] [Transparency Index] index is out of range"); return false; } return true; } bool PSDInput::load_resource_1058 (uint32_t length) { std::string data (length, 0); if (!m_file.read (&data[0], length)) return false; if (!decode_exif (data, m_composite_attribs) || !decode_exif (data, m_common_attribs)) { error ("Failed to decode Exif data"); return false; } return true; } bool PSDInput::load_resource_1059 (uint32_t length) { //FIXME(dewyatt): untested, I don't have any images with this resource return load_resource_1058 (length); } bool PSDInput::load_resource_1060 (uint32_t length) { std::string data (length, 0); if (!m_file.read (&data[0], length)) return false; // Store the XMP data for the composite and all other subimages if (!decode_xmp (data, m_composite_attribs) || !decode_xmp (data, m_common_attribs)) { error ("Failed to decode XMP data"); return false; } return true; } bool PSDInput::load_resource_1064 (uint32_t length) { uint32_t version; if (!read_bige (version)) return false; if (version != 1 && version != 2) { error ("[Image Resource] [Pixel Aspect Ratio] Unrecognized version"); return false; } double aspect_ratio; if (!read_bige (aspect_ratio)) return false; // FIXME(dewyatt): loss of precision? common_attribute ("PixelAspectRatio", (float)aspect_ratio); return true; } bool PSDInput::load_resource_thumbnail (uint32_t length, bool isBGR) { enum ThumbnailFormat { kJpegRGB = 1, kRawRGB = 0 }; uint32_t format; uint32_t width, height; uint32_t widthbytes; uint32_t total_size; uint32_t compressed_size; uint16_t bpp; uint16_t planes; int stride; jpeg_decompress_struct cinfo; thumbnail_error_mgr jerr; uint32_t jpeg_length = length - 28; read_bige (format); read_bige (width); read_bige (height); read_bige (widthbytes); read_bige (total_size); read_bige (compressed_size); read_bige (bpp); read_bige (planes); if (!m_file) return false; // We only support kJpegRGB since I don't have any test images with // kRawRGB if (format != kJpegRGB || bpp != 24 || planes != 1) { error ("[Image Resource] [JPEG Thumbnail] invalid or unsupported format"); return false; } cinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = thumbnail_error_exit; if (setjmp (jerr.setjmp_buffer)) { jpeg_destroy_decompress (&cinfo); error ("[Image Resource] [JPEG Thumbnail] libjpeg error"); return false; } std::string jpeg_data (jpeg_length, '\0'); if (!m_file.read (&jpeg_data[0], jpeg_length)) return false; jpeg_create_decompress (&cinfo); jpeg_memory_src (&cinfo, (unsigned char *)&jpeg_data[0], jpeg_length); jpeg_read_header (&cinfo, TRUE); jpeg_start_decompress (&cinfo); stride = cinfo.output_width * cinfo.output_components; unsigned int thumbnail_bytes = cinfo.output_width * cinfo.output_height * cinfo.output_components; std::string thumbnail_image (thumbnail_bytes, '\0'); // jpeg_destroy_decompress will deallocate this JSAMPLE **buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, stride, 1); while (cinfo.output_scanline < cinfo.output_height) { if (jpeg_read_scanlines (&cinfo, buffer, 1) != 1) { jpeg_finish_decompress (&cinfo); jpeg_destroy_decompress (&cinfo); error ("[Image Resource] [JPEG Thumbnail] libjpeg error"); return false; } std::memcpy (&thumbnail_image[(cinfo.output_scanline - 1) * stride], (char *)buffer[0], stride); } jpeg_finish_decompress (&cinfo); jpeg_destroy_decompress (&cinfo); // Set these attributes for the merged composite only (subimage 0) composite_attribute ("thumbnail_width", (int)width); composite_attribute ("thumbnail_height", (int)height); composite_attribute ("thumbnail_nchannels", 3); if (isBGR) { for (unsigned int i = 0; i < thumbnail_bytes - 2; i += 3) std::swap (thumbnail_image[i], thumbnail_image[i + 2]); } composite_attribute ("thumbnail_image", TypeDesc (TypeDesc::UINT8, thumbnail_image.size ()), &thumbnail_image[0]); return true; } void PSDInput::thumbnail_error_exit (j_common_ptr cinfo) { thumbnail_error_mgr *mgr = (thumbnail_error_mgr *)cinfo->err; longjmp (mgr->setjmp_buffer, 1); } bool PSDInput::load_layers () { if (m_header.version == 1) read_bige (m_layer_mask_info.length); else read_bige (m_layer_mask_info.length); m_layer_mask_info.begin = m_file.tellg (); m_layer_mask_info.end = m_layer_mask_info.begin + (std::streampos)m_layer_mask_info.length; if (!check_io ()) return false; if (!m_layer_mask_info.length) return true; LayerMaskInfo::LayerInfo &layer_info = m_layer_mask_info.layer_info; if (m_header.version == 1) read_bige (layer_info.length); else read_bige (layer_info.length); layer_info.begin = m_file.tellg (); layer_info.end = layer_info.begin + (std::streampos)layer_info.length; if (!check_io ()) return false; if (!layer_info.length) return true; read_bige (layer_info.layer_count); if (layer_info.layer_count < 0) { m_image_data.transparency = true; layer_info.layer_count = -layer_info.layer_count; } m_layers.resize (layer_info.layer_count); for (int16_t layer_nbr = 0; layer_nbr < layer_info.layer_count; ++layer_nbr) { Layer &layer = m_layers[layer_nbr]; if (!load_layer (layer)) return false; } for (int16_t layer_nbr = 0; layer_nbr < layer_info.layer_count; ++layer_nbr) { Layer &layer = m_layers[layer_nbr]; if (!load_layer_channels (layer)) return false; } return true; } bool PSDInput::load_layer (Layer &layer) { read_bige (layer.top); read_bige (layer.left); read_bige (layer.bottom); read_bige (layer.right); read_bige (layer.channel_count); if (!check_io ()) return false; layer.width = std::abs((int)layer.right - (int)layer.left); layer.height = std::abs((int)layer.bottom - (int)layer.top); layer.channel_info.resize (layer.channel_count); for(uint16_t channel = 0; channel < layer.channel_count; channel++) { ChannelInfo &channel_info = layer.channel_info[channel]; read_bige (channel_info.channel_id); if (m_header.version == 1) read_bige (channel_info.data_length); else read_bige (channel_info.data_length); layer.channel_id_map[channel_info.channel_id] = &channel_info; } char bm_signature[4]; m_file.read (bm_signature, 4); if (!check_io ()) return false; if (std::memcmp (bm_signature, "8BIM", 4) != 0) { error ("[Layer Record] Invalid blend mode signature"); return false; } m_file.read (layer.bm_key, 4); read_bige (layer.opacity); read_bige (layer.clipping); read_bige (layer.flags); // skip filler m_file.seekg(1, std::ios::cur); read_bige (layer.extra_length); uint32_t extra_remaining = layer.extra_length; // layer mask data length uint32_t lmd_length; read_bige (lmd_length); if (!check_io ()) return false; switch (lmd_length) { case 0: break; case 20: read_bige (layer.mask_data.top); read_bige (layer.mask_data.left); read_bige (layer.mask_data.bottom); read_bige (layer.mask_data.right); read_bige (layer.mask_data.default_color); read_bige (layer.mask_data.flags); // skip padding m_file.seekg(2, std::ios::cur); break; case 36: // In this case, we skip the above (lmd_length == 20) fields // to read the "real" fields. m_file.seekg (18, std::ios::cur); read_bige (layer.mask_data.flags); read_bige (layer.mask_data.default_color); read_bige (layer.mask_data.top); read_bige (layer.mask_data.left); read_bige (layer.mask_data.bottom); read_bige (layer.mask_data.right); break; default: // The size should always be 0,20, or 36 error ("[Layer Mask Data] invalid size"); return false; break; }; extra_remaining -= (lmd_length + 4); // layer blending ranges length uint32_t lbr_length; read_bige (lbr_length); // skip block m_file.seekg (lbr_length, std::ios::cur); extra_remaining -= (lbr_length + 4); if (!check_io ()) return false; extra_remaining -= read_pascal_string(layer.name, 4); while (m_file && extra_remaining >= 12) { layer.additional_info.push_back (Layer::AdditionalInfo()); Layer::AdditionalInfo &info = layer.additional_info.back(); char signature[4]; m_file.read (signature, 4); m_file.read (info.key, 4); if (std::memcmp (signature, "8BIM", 4) != 0 && std::memcmp (signature, "8B64", 4) != 0) { error ("[Additional Layer Info] invalid signature"); return false; } extra_remaining -= 8; if (m_header.version == 2 && is_additional_info_psb (info.key)) { read_bige (info.length); extra_remaining -= 8; } else { read_bige (info.length); extra_remaining -= 4; } m_file.seekg (info.length, std::ios::cur); extra_remaining -= info.length; } return check_io (); } bool PSDInput::load_layer_channels (Layer &layer) { for (uint16_t channel = 0; channel < layer.channel_count; ++channel) { ChannelInfo &channel_info = layer.channel_info[channel]; if (!load_layer_channel (layer, channel_info)) return false; } return true; } bool PSDInput::load_layer_channel (Layer &layer, ChannelInfo &channel_info) { std::streampos start_pos = m_file.tellg (); if (channel_info.data_length >= 2) { read_bige (channel_info.compression); if (!check_io ()) return false; } // No data at all or just compression if (channel_info.data_length <= 2) return true; channel_info.data_pos = m_file.tellg (); channel_info.row_pos.resize (layer.height); channel_info.row_length = (layer.width * m_header.depth + 7) / 8; switch (channel_info.compression) { case Compression_Raw: if (layer.height) { channel_info.row_pos[0] = channel_info.data_pos; for (uint32_t i = 1; i < layer.height; ++i) channel_info.row_pos[i] = channel_info.row_pos[i - 1] + (std::streampos)channel_info.row_length; } channel_info.data_length = channel_info.row_length * layer.height; break; case Compression_RLE: // RLE lengths are stored before the channel data if (!read_rle_lengths (layer.height, channel_info.rle_lengths)) return false; // channel data is located after the RLE lengths channel_info.data_pos = m_file.tellg (); // subtract the RLE lengths read above channel_info.data_length = channel_info.data_length - (channel_info.data_pos - start_pos); if (layer.height) { channel_info.row_pos[0] = channel_info.data_pos; for (uint32_t i = 1; i < layer.height; ++i) channel_info.row_pos[i] = channel_info.row_pos[i - 1] + (std::streampos)channel_info.rle_lengths[i - 1]; } break; // These two aren't currently supported. They would likely // require large changes in the code as they probably don't // support random access like the other modes. I doubt these are // used much and I haven't found any test images. case Compression_ZIP: case Compression_ZIP_Predict: default: error ("[Layer Channel] unsupported compression"); return false; ; } m_file.seekg (channel_info.data_length, std::ios::cur); return check_io (); } bool PSDInput::read_rle_lengths (uint32_t height, std::vector &rle_lengths) { rle_lengths.resize (height); for (uint32_t row = 0; row < height && m_file; ++row) { if (m_header.version == 1) read_bige (rle_lengths[row]); else read_bige (rle_lengths[row]); } return check_io (); } bool PSDInput::load_global_mask_info () { if (!m_layer_mask_info.length) return true; m_file.seekg (m_layer_mask_info.layer_info.end); uint64_t remaining = m_layer_mask_info.end - m_file.tellg(); uint32_t length; // This section should be at least 17 bytes, but some files lack // global mask info and additional layer info, not convered in the spec if (remaining < 17) { m_file.seekg(m_layer_mask_info.end); return true; } read_bige (length); std::streampos start = m_file.tellg (); std::streampos end = start + (std::streampos)length; if (!check_io ()) return false; // this can be empty if (!length) return true; read_bige (m_global_mask_info.overlay_color_space); for (int i = 0; i < 4; ++i) read_bige (m_global_mask_info.color_components[i]); read_bige (m_global_mask_info.opacity); read_bige (m_global_mask_info.kind); m_file.seekg (end); return check_io (); } bool PSDInput::load_global_additional () { if (!m_layer_mask_info.length) return true; char signature[4]; char key[4]; uint64_t length; uint64_t remaining = m_layer_mask_info.length - (m_file.tellg() - m_layer_mask_info.begin); while (m_file && remaining >= 12) { m_file.read (signature, 4); if (!check_io ()) return false; // the spec supports 8BIM, and 8B64 (presumably for psb support) if (std::memcmp (signature, "8BIM", 4) != 0 && std::memcmp (signature, "8B64", 4) != 0) { error ("[Global Additional Layer Info] invalid signature"); return false; } m_file.read (key, 4); if (!check_io ()) return false; remaining -= 8; if (m_header.version == 2 && is_additional_info_psb (key)) { read_bige (length); remaining -= 8; } else { read_bige (length); remaining -= 4; } // Long story short these are aligned to 4 bytes but that is not // included in the stored length and the specs do not mention it. // round up to multiple of 4 length = (length + 3) & ~3; remaining -= length; // skip it for now m_file.seekg (length, std::ios::cur); } // finished with the layer and mask information section, seek to the end m_file.seekg (m_layer_mask_info.end); return check_io (); } bool PSDInput::load_image_data () { uint16_t compression; uint32_t row_length = (m_header.width * m_header.depth + 7) / 8; int16_t id = 0; read_bige (compression); if (!check_io ()) return false; if (compression != Compression_Raw && compression != Compression_RLE) { error ("[Image Data Section] unsupported compression"); return false; } m_image_data.channel_info.resize (m_header.channel_count); // setup some generic properties and read any RLE lengths // Image Data Section has RLE lengths for all channels stored first BOOST_FOREACH (ChannelInfo &channel_info, m_image_data.channel_info) { channel_info.compression = compression; channel_info.channel_id = id++; channel_info.data_length = row_length * m_header.height; if (compression == Compression_RLE) { if (!read_rle_lengths (m_header.height, channel_info.rle_lengths)) return false; } } BOOST_FOREACH (ChannelInfo &channel_info, m_image_data.channel_info) { channel_info.row_pos.resize (m_header.height); channel_info.data_pos = m_file.tellg (); channel_info.row_length = (m_header.width * m_header.depth + 7) / 8; switch (compression) { case Compression_Raw: channel_info.row_pos[0] = channel_info.data_pos; for (uint32_t i = 1; i < m_header.height; ++i) channel_info.row_pos[i] = channel_info.row_pos[i - 1] + (std::streampos)row_length; m_file.seekg (channel_info.row_pos.back () + (std::streampos)row_length); break; case Compression_RLE: channel_info.row_pos[0] = channel_info.data_pos; for (uint32_t i = 1; i < m_header.height; ++i) channel_info.row_pos[i] = channel_info.row_pos[i - 1] + (std::streampos)channel_info.rle_lengths[i - 1]; m_file.seekg (channel_info.row_pos.back () + (std::streampos)channel_info.rle_lengths.back ()); break; } } return check_io (); } void PSDInput::setup () { // raw_channel_count is the number of channels in the file // spec_channel_count is what we will report to OIIO client int raw_channel_count, spec_channel_count; if (m_header.color_mode == ColorMode_Multichannel) { spec_channel_count = raw_channel_count = m_header.channel_count; } else { raw_channel_count = mode_channel_count[m_header.color_mode]; spec_channel_count = m_WantRaw ? raw_channel_count : (m_header.color_mode == ColorMode_Grayscale ? 1 : 3); if (m_image_data.transparency) { spec_channel_count++; raw_channel_count++; } else if (m_header.color_mode == ColorMode_Indexed && m_transparency_index) { spec_channel_count++; } } // Composite spec m_specs.push_back (ImageSpec (m_header.width, m_header.height, spec_channel_count, m_type_desc)); m_specs.back().extra_attribs = m_composite_attribs.extra_attribs; if (m_WantRaw) fill_channel_names (m_specs.back(), m_image_data.transparency); // Composite channels m_channels.reserve (m_subimage_count); m_channels.resize (1); m_channels[0].reserve (raw_channel_count); for (int i = 0; i < raw_channel_count; ++i) m_channels[0].push_back (&m_image_data.channel_info[i]); BOOST_FOREACH (Layer &layer, m_layers) { spec_channel_count = m_WantRaw ? mode_channel_count[m_header.color_mode] : 3; raw_channel_count = mode_channel_count[m_header.color_mode]; bool transparency = (bool)layer.channel_id_map.count (ChannelID_Transparency); if (transparency) { spec_channel_count++; raw_channel_count++; } m_specs.push_back (ImageSpec (layer.width, layer.height, spec_channel_count, m_type_desc)); ImageSpec &spec = m_specs.back (); spec.extra_attribs = m_common_attribs.extra_attribs; if (m_WantRaw) fill_channel_names (spec, transparency); m_channels.resize (m_channels.size () + 1); std::vector &channels = m_channels.back (); channels.reserve (raw_channel_count); for (unsigned int i = 0; i < mode_channel_count[m_header.color_mode]; ++i) channels.push_back (layer.channel_id_map[i]); if (transparency) channels.push_back (layer.channel_id_map[ChannelID_Transparency]); } if (m_specs.back().alpha_channel != -1) if (m_keep_unassociated_alpha) m_specs.back().attribute ("oiio:UnassociatedAlpha", 1); } void PSDInput::fill_channel_names (ImageSpec &spec, bool transparency) { spec.channelnames.clear (); if (m_header.color_mode == ColorMode_Multichannel) { spec.default_channel_names (); } else { for (unsigned int i = 0; i < mode_channel_count[m_header.color_mode]; ++i) spec.channelnames.push_back (mode_channel_names[m_header.color_mode][i]); if (transparency) spec.channelnames.push_back ("A"); } } bool PSDInput::read_channel_row (const ChannelInfo &channel_info, uint32_t row, char *data) { if (row >= channel_info.row_pos.size ()) return false; uint32_t rle_length; channel_info.row_pos[row]; m_file.seekg (channel_info.row_pos[row]); switch (channel_info.compression) { case Compression_Raw: m_file.read (data, channel_info.row_length); break; case Compression_RLE: rle_length = channel_info.rle_lengths[row]; if (m_rle_buffer.size () < rle_length) m_rle_buffer.resize (rle_length); m_file.read (&m_rle_buffer[0], rle_length); if (!check_io ()) return false; if (!decompress_packbits (&m_rle_buffer[0], data, rle_length, channel_info.row_length)) return false; break; } if (!check_io ()) return false; if (!bigendian ()) { switch (m_header.depth) { case 16: swap_endian ((uint16_t *)data, m_spec.width); break; case 32: swap_endian ((uint32_t *)data, m_spec.width); // if (row == 131) // printf ("%x %x %x %x\n", // ((uint32_t*)data)[0], ((uint32_t*)data)[1], // ((uint32_t*)data)[2], ((uint32_t*)data)[3]); // convert_type ((float *)&data[0], // (uint32_t*)&data[1], m_spec.width); break; } } return true; } template void PSDInput::interleave_row (T *dst, size_t nchans) { ASSERT (nchans <= m_channels[m_subimage].size()); for (size_t c = 0; c < nchans; ++c) { const T *cbuf = (const T*) &(m_channel_buffers[c][0]); for (int x = 0; x < m_spec.width; ++x) dst[nchans*x+c] = cbuf[x]; } } bool PSDInput::indexed_to_rgb (char *dst) { char *src = &m_channel_buffers[m_subimage][0]; // The color table is 768 bytes which is 256 * 3 channels (always RGB) char *table = &m_color_data.data[0]; if (m_transparency_index >= 0) { for (int i = 0; i < m_spec.width; ++i) { unsigned char index = *src++; if (index == m_transparency_index) { std::memset (dst, 0, 4); dst += 4; continue; } *dst++ = table[index]; // R *dst++ = table[index + 256]; // G *dst++ = table[index + 512]; // B *dst++ = 0xff; // A } } else { for (int i = 0; i < m_spec.width; ++i) { unsigned char index = *src++; *dst++ = table[index]; // R *dst++ = table[index + 256]; // G *dst++ = table[index + 512]; // B } } return true; } bool PSDInput::bitmap_to_rgb (char *dst) { for (int i = 0; i < m_spec.width; ++i) { int byte = i / 8; int bit = 7 - i % 8; char result; char *src = &m_channel_buffers[m_subimage][byte]; if (*src & (1 << bit)) result = 0; else result = 0xff; std::memset (dst, result, 3); dst += 3; } return true; } void PSDInput::set_type_desc () { switch (m_header.depth) { case 1: case 8: m_type_desc = TypeDesc::UINT8; break; case 16: m_type_desc = TypeDesc::UINT16; break; case 32: m_type_desc = TypeDesc::FLOAT; break; }; } bool PSDInput::check_io () { if (!m_file) { error ("\"%s\": I/O error", m_filename.c_str ()); return false; } return true; } int PSDInput::read_pascal_string (std::string &s, uint16_t mod_padding) { s.clear(); uint8_t length; int bytes = 0; if (m_file.read ((char *)&length, 1)) { bytes = 1; if (length == 0) { if (m_file.seekg (mod_padding - 1, std::ios::cur)) bytes += mod_padding - 1; } else { s.resize (length); if (m_file.read (&s[0], length)) { bytes += length; if (mod_padding > 0) { for (int padded_length = length + 1; padded_length % mod_padding != 0; padded_length++) { if (!m_file.seekg(1, std::ios::cur)) break; bytes++; } } } } } return bytes; } bool PSDInput::decompress_packbits (const char *src, char *dst, uint16_t packed_length, uint16_t unpacked_length) { int32_t src_remaining = packed_length; int32_t dst_remaining = unpacked_length; int16_t header; int length; while (src_remaining > 0 && dst_remaining > 0) { header = *src++; src_remaining--; if (header == 128) continue; else if (header >= 0) { // (1 + n) literal bytes length = 1 + header; src_remaining -= length; dst_remaining -= length; if (src_remaining < 0 || dst_remaining < 0) return false; std::memcpy (dst, src, length); src += length; dst += length; } else { // repeat byte (1 - n) times length = 1 - header; src_remaining--; dst_remaining -= length; if (src_remaining < 0 || dst_remaining < 0) return false; std::memset (dst, *src, length); src++; dst += length; } } return true; } bool PSDInput::is_additional_info_psb (const char *key) { for (unsigned int i = 0; i < additional_info_psb_count; ++i) if (std::memcmp (additional_info_psb[i], key, 4) == 0) return true; return false; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/psd.imageio/psd_pvt.h0000644000175000017500000000452313151711064021147 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_PSD_PVT_H #define OPENIMAGEIO_PSD_PVT_H #include "OpenImageIO/imageio.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" OIIO_PLUGIN_NAMESPACE_BEGIN namespace psd_pvt { struct FileHeader { char signature[4]; uint16_t version; uint16_t channel_count; uint32_t height; uint32_t width; uint16_t depth; uint16_t color_mode; }; struct ColorModeData { uint32_t length; std::string data; }; struct ImageResourceBlock { char signature[4]; uint16_t id; std::string name; uint32_t length; std::streampos pos; }; } // namespace psd_pvt OIIO_PLUGIN_NAMESPACE_END #endif // OPENIMAGEIO_PSD_PVT_H openimageio-1.7.17~dfsg0.orig/src/doc/0000755000175000017500000000000013152525262015665 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/doc/notes.txt0000644000175000017500000001074613151711064017562 0ustar mfvmfvProposed menus: File New window ^N New tab ^T x Open ^O Open Recent Save ^S Save As... Save Window As... Save Selection As... x Reload x Close ^W Delete image on disk Preferences ^, x Quit ^Q Edit Cut (??? is this the same as clear?) Copy Paste Clear Region of Interest Prioritize Region of Interest Crop Region of Interest Select all images ^A Annotate Image ? View x Fit image (zoom to fill current window) x Fit window (adjust window to fit current zoom level of image) x Full screen x Channel mode (color, red, green, blue, alpha, luminance) View as depth, false, random x Zoom in x Zoom out x Stop up/down x Gamma up/down x Toggle last image Foreground/background images x Prev/next image x Prev/next subimage x Prev/next channel Subtract (image difference against last) Stereo mode Tools Mouse mode (zoom/pan, select, wipe) [ note: In zoom/pan mode, L/R click is zoom in/out, middle drag is pan, shift-drag is select. ] x Pixel view Thumbnails Hide controls Disconnect Overwrite render Store render Show error log Help --- Standard tags: int oiio:bitspersample - for the *file* -- may differ from what format implies) string compression - name of compression scheme to use string planarconfig - "separate" or "contiguous" string datetime - "yyyy:mm:dd hh:mm:ss" (TIFF DateTime, OpenEXR capDate) string copyright (TIFF Copyright, OpenEXR owner) [ should it be owner? ] string artist (TIFF Artist) string name (TIFF DocumentName) string host (TIFF HostName) string description (TIFF ImageDescription) string software (TIFF Software) string make (TIFF Make) string model (TIFF Model) string textureformat string wrapmodes matrix worldtocamera matrix worldtoscreen float fovcot int[nchannels] bitspersample ? - allow per-channel description? int xresolution (pixels per resolutionunit) int yresolution string resolutionunit ("none", "in", "cm") float pixelaspectratio int orientation ??? float aperture float exposuretime float fstop float focallength tiff-specific: tiff_Predictor tiff_RowsPerStrip tiff_SubfileType tiff_PhotometricInterpretation tiff_Planarconfig openexr-specific openexr_foo - arbitrary float, int, matrix, string exif_ShutterSpeedValue --- Assumptions about in-core image representation (NOT file!): * Pixel data are 8- 16- or 32-bit int (signed or unsigned), 16- 32- or 16-bit float. NOTHING ELSE. No <8 bit images, or pixels boundaries that aren't byte boundaries. Files with <8 bits will appear to the client as 8-bit unsigned grayscale images. * Color space is grayscale or RGB. XYZ, CMYK, YUV, or other non-spectral pixel data are converted to RGB upon reading. (Though you could write a custom ImageIO plugin that would preserve some other color space upon read, or convert to some other color space upon write.) * All color channels have the same data format. Upon read, an ImageInput ought to convert all channels to the one with the highest precision in the file. * All image channels in a subimage are sampled at the same resolution. For file formats that allow some channels to be subsampled, they will be automatically up-resed to the highest resolution channel in the subimage. * Color information is always in the order R, G, B, and the alpha channel, if any, always follows RGB, and z channel (if any) always follows alpha. So if a file actually stores ABGR, the plugin is expected to rearrange it as RGBA. --- operations zoom (in + out) (ctrl-+/-, alt-left mouse) drag/pan (always middle mouse) select (a rectangle) (shift-left mouse) draw (lines) info/pick selectors none shift ctrl alt buttons left right middle middle should always pan shift-left should always select alt-mouse should always zoom operations zoom (in + out) (ctrl-+/-, alt-left mouse) drag/pan (always middle mouse) select (a rectangle) (shift-left mouse) draw (lines) info/pick selectors none shift ctrl alt buttons left right middle middle should always pan shift-left should always select alt-mouse should always zoom openimageio-1.7.17~dfsg0.orig/src/doc/imageoutput.tex0000644000175000017500000021407313151711064020755 0ustar mfvmfv\chapter{ImageOutput: Writing Images} \label{chap:imageoutput} \index{Image I/O API|(} \indexapi{ImageOutput} \section{Image Output Made Simple} \label{sec:imageoutput:simple} Here is the simplest sequence required to write the pixels of a 2D image to a file: \begin{code} #include OIIO_NAMESPACE_USING ... const char *filename = "foo.jpg"; const int xres = 640, yres = 480; const int channels = 3; // RGB unsigned char pixels[xres*yres*channels]; ImageOutput *out = ImageOutput::create (filename); if (! out) return; ImageSpec spec (xres, yres, channels, TypeDesc::UINT8); out->open (filename, spec); out->write_image (TypeDesc::UINT8, pixels); out->close (); ImageOutput::destroy (out); \end{code} \noindent This little bit of code does a surprising amount of useful work: \begin{itemize} \item Search for an ImageIO plugin that is capable of writing the file (\qkw{foo.jpg}), deducing the format from the file extension. When it finds such a plugin, it creates a subclass instance of \ImageOutput that writes the right kind of file format. \begin{code} ImageOutput *out = ImageOutput::create (filename); \end{code} \item Open the file, write the correct headers, and in all other important ways prepare a file with the given dimensions ($640 \times 480$), number of color channels (3), and data format (unsigned 8-bit integer). \begin{code} ImageSpec spec (xres, yres, channels, TypeDesc::UINT8); out->open (filename, spec); \end{code} \item Write the entire image, hiding all details of the encoding of image data in the file, whether the file is scanline- or tile-based, or what is the native format of data in the file (in this case, our in-memory data is unsigned 8-bit and we've requested the same format for disk storage, but if they had been different, {\kw write_image()} would do all the conversions for us). \begin{code} out->write_image (TypeDesc::UINT8, &pixels); \end{code} \item Close the file, destroy and free the \ImageOutput we had created, and perform all other cleanup and release of any resources needed by the plugin. \begin{code} out->close (); ImageOutput::destroy (out); \end{code} \end{itemize} \subsection*{What happens when the file format doesn't support the spec?} The {\cf open()} call will fail (return {\cf false} and set an appropriate error message) if the output format cannot accommodate what is requested by the \ImageSpec. This includes: \begin{itemize} \item Dimensions (width, height, or number of channels) exceeding the limits supported by the file format.\footnote{One exception to the rule about number of channels is that a file format that supports only RGB, but not alpha, is permitted to silently drop the alpha channel without considering that to be an error.} \item Volumetric (depth $> 1$) if the format does not support volumetric data. \item Tile size $>1$ if the format does not support tiles. \item Multiple subimages or MIP levels if not supported by the format. \end{itemize} However, several other mismatches between requested \ImageSpec and file format capabilities will be silently ignored, allowing {\cf open()} to succeed: \begin{itemize} \item If the pixel data format is not supported (for example, a request for {\cf half} pixels when writing a JPEG/JFIF file), the format writer may substitute another data format (generally, whichever commonly-used data format supported by the file type will result in the least reduction of precision or range). \item If the \ImageSpec requests different per-channel data formats, but the format supports only a single format for all channels, it may just choose the most precise format requested and use it for all channels. \item If the file format does not support arbitrarily-named channels, the channel names may be lost when saving the file. \item Any other metadata in the \ImageSpec may be summarily dropped if not supported by the file format. \end{itemize} \section{Advanced Image Output} \label{sec:imageoutput:advanced} Let's walk through many of the most common things you might want to do, but that are more complex than the simple example above. \subsection{Writing individual scanlines, tiles, and rectangles} \label{sec:imageoutput:scanlinestiles} The simple example of Section~\ref{sec:imageoutput:simple} wrote an entire image with one call. But sometimes you are generating output a little at a time and do not wish to retain the entire image in memory until it is time to write the file. \product allows you to write images one scanline at a time, one tile at a time, or by individual rectangles. \subsubsection{Writing individual scanlines} Individual scanlines may be written using the \writescanline API call: \begin{code} ... unsigned char scanline[xres*channels]; out->open (filename, spec); int z = 0; // Always zero for 2D images for (int y = 0; y < yres; ++y) { ... generate data in scanline[0..xres*channels-1] ... out->write_scanline (y, z, TypeDesc::UINT8, scanline); } out->close (); ... \end{code} The first two arguments to \writescanline specify which scanline is being written by its vertical ($y$) scanline number (beginning with 0) and, for volume images, its slice ($z$) number (the slice number should be 0 for 2D non-volume images). This is followed by a \TypeDesc describing the data you are supplying, and a pointer to the pixel data itself. Additional optional arguments describe the data stride, which can be ignored for contiguous data (use of strides is explained in Section~\ref{sec:imageoutput:strides}). All \ImageOutput implementations will accept scanlines in strict order (starting with scanline 0, then 1, up to {\kw yres-1}, without skipping any). See Section~\ref{sec:imageoutput:randomrewrite} for details on out-of-order or repeated scanlines. The full description of the \writescanline function may be found in Section~\ref{sec:imageoutput:reference}. \subsubsection{Writing individual tiles} Not all image formats (and therefore not all \ImageOutput implementations) support tiled images. If the format does not support tiles, then \writetile will fail. An application using \product should gracefully handle the case that tiled output is not available for the chosen format. Once you {\kw create()} an \ImageOutput, you can ask if it is capable of writing a tiled image by using the {\kw supports("tiles")} query: \begin{code} ... ImageOutput *out = ImageOutput::create (filename); if (! out->supports ("tiles")) { // Tiles are not supported } \end{code} Assuming that the \ImageOutput supports tiled images, you need to specifically request a tiled image when you {\kw open()} the file. This is done by setting the tile size in the \ImageSpec passed to {\kw open()}. If the tile dimensions are not set, they will default to zero, which indicates that scanline output should be used rather than tiled output. \begin{code} int tilesize = 64; ImageSpec spec (xres, yres, channels, TypeDesc::UINT8); spec.tile_width = tilesize; spec.tile_height = tilesize; out->open (filename, spec); ... \end{code} In this example, we have used square tiles (the same number of pixels horizontally and vertically), but this is not a requirement of \product. However, it is possible that some image formats may only support square tiles, or only certain tile sizes (such as restricting tile sizes to powers of two). Such restrictions should be documented by each individual plugin. \begin{code} unsigned char tile[tilesize*tilesize*channels]; int z = 0; // Always zero for 2D images for (int y = 0; y < yres; y += tilesize) { for (int x = 0; x < xres; x += tilesize) { ... generate data in tile[] .. out->write_tile (x, y, z, TypeDesc::UINT8, tile); } } out->close (); ... \end{code} The first three arguments to \writetile specify which tile is being written by the pixel coordinates of any pixel contained in the tile: $x$ (column), $y$ (scanline), and $z$ (slice, which should always be 0 for 2D non-volume images). This is followed by a \TypeDesc describing the data you are supplying, and a pointer to the tile's pixel data itself, which should be ordered by increasing slice, increasing scanline within each slice, and increasing column within each scanline. Additional optional arguments describe the data stride, which can be ignored for contiguous data (use of strides is explained in Section~\ref{sec:imageoutput:strides}). All \ImageOutput implementations that support tiles will accept tiles in strict order of increasing $y$ rows, and within each row, increasing $x$ column, without missing any tiles. See Section~\ref{sec:imageoutput:randomrewrite} for details on out-of-order or repeated tiles. The full description of the \writetile function may be found in Section~\ref{sec:imageoutput:reference}. \subsubsection{Writing arbitrary rectangles} Some \ImageOutput implementations --- such as those implementing an interactive image display, but probably not any that are outputting directly to a file --- may allow you to send arbitrary rectangular pixel regions. Once you {\kw create()} an \ImageOutput, you can ask if it is capable of accepting arbitrary rectangles by using the {\kw supports("rectangles")} query: \begin{code} ... ImageOutput *out = ImageOutput::create (filename); if (! out->supports ("rectangles")) { // Rectangles are not supported } \end{code} If rectangular regions are supported, they may be sent using the {\kw write_rectangle()} API call: \begin{code} unsigned int rect[...]; ... generate data in rect[] .. out->write_rectangle (xbegin, xend, ybegin, yend, zbegin, zend, TypeDesc::UINT8, rect); \end{code} The first six arguments to {\kw write_rectangle()} specify the region of pixels that is being transmitted by supplying the minimum and one-past-maximum pixel indices in $x$ (column), $y$ (scanline), and $z$ (slice, always 0 for 2D non-volume images).\footnote{\OpenImageIO nearly always follows the C++ STL convention of specifying ranges as {\cf [begin,end)}, that is, {\cf begin, begin+1, ..., end-1.}} The total number of pixels being transmitted is therefore: \begin{code} (xend-xbegin) * (yend-ybegin) * (zend-zbegin) \end{code} \noindent This is followed by a \TypeDesc describing the data you are supplying, and a pointer to the rectangle's pixel data itself, which should be ordered by increasing slice, increasing scanline within each slice, and increasing column within each scanline. Additional optional arguments describe the data stride, which can be ignored for contiguous data (use of strides is explained in Section~\ref{sec:imageoutput:strides}). \subsection{Converting data formats} \label{sec:imageoutput:convertingformats} The code examples of the previous sections all assumed that your internal pixel data is stored as unsigned 8-bit integers (i.e., 0-255 range). But \product is significantly more flexible. You may request that the output image be stored in any of several formats. This is done by setting the {\kw format} field of the \ImageSpec prior to calling {\kw open}. You can do this upon construction of the \ImageSpec, as in the following example that requests a spec that stores data as 16-bit unsigned integers: \begin{code} ImageSpec spec (xres, yres, channels, TypeDesc::UINT16); \end{code} \noindent Or, for an \ImageSpec that has already been constructed, you may reset its format using the {\kw set_format()} method. \begin{code} ImageSpec spec (...); spec.set_format (TypeDesc::UINT16); \end{code} Note that resetting the format must be done \emph{before} passing the spec to {\kw open()}, or it will have no effect on the file. Individual file formats, and therefore \ImageOutput implementations, may only support a subset of the formats understood by the \product library. Each \ImageOutput plugin implementation should document which data formats it supports. An individual \ImageOutput implementation may choose to simply fail to {\kw open()}, though the recommended behavior is for {\kw open()} to succeed but in fact choose a data format supported by the file format that best preserves the precision and range of the originally-requested data format. It is not required that the pixel data passed to \writeimage, \writescanline, \writetile, or {\kw write_rectangle()} actually be in the same data format as that requested as the native format of the file. You can fully mix and match data you pass to the various {\kw write} routines and \product will automatically convert from the internal format to the native file format. For example, the following code will open a TIFF file that stores pixel data as 16-bit unsigned integers (values ranging from 0 to 65535), compute internal pixel values as floating-point values, with \writeimage performing the conversion automatically: \begin{code} ImageOutput *out = ImageOutput::create ("myfile.tif"); ImageSpec spec (xres, yres, channels, TypeDesc::UINT16); out->open (filename, spec); ... float pixels [xres*yres*channels]; ... out->write_image (TypeDesc::FLOAT, pixels); \end{code} \noindent Note that \writescanline, \writetile, and {\cf write_rectangle} have a parameter that works in a corresponding manner. \subsection{Data Strides} \label{sec:imageoutput:strides} In the preceeding examples, we have assumed that the block of data being passed to the {\cf write} functions are \emph{contiguous}, that is: \begin{itemize} \item each pixel in memory consists of a number of data values equal to the declared number of channels that are being written to the file; \item successive column pixels within a row directly follow each other in memory, with the first channel of pixel $x$ immediately following last channel of pixel $x-1$ of the same row; \item for whole images, tiles or rectangles, the data for each row immediately follows the previous one in memory (the first pixel of row $y$ immediately follows the last column of row $y-1$); \item for 3D volumetric images, the first pixel of slice $z$ immediately follows the last pixel of of slice $z-1$. \end{itemize} Please note that this implies that data passed to \writetile be contiguous in the shape of a single tile (not just an offset into a whole image worth of pixels), and that data passed to {\cf write_rectangle()} be contiguous in the dimensions of the rectangle. The \writescanline function takes an optional {\cf xstride} argument, and the \writeimage, \writetile, and {\cf write_rectangle} functions take optional {\cf xstride}, {\cf ystride}, and {\cf zstride} values that describe the distance, in \emph{bytes}, between successive pixel columns, rows, and slices, respectively, of the data you are passing. For any of these values that are not supplied, or are given as the special constant {\cf AutoStride}, contiguity will be assumed. By passing different stride values, you can achieve some surprisingly flexible functionality. A few representative examples follow: \begin{itemize} \item Flip an image vertically upon writing, by using \emph{negative} $y$ stride: \begin{code} unsigned char pixels[xres*yres*channels]; int scanlinesize = xres * channels * sizeof(pixels[0]); ... out->write_image (TypeDesc::UINT8, (char *)pixels+(yres-1)*scanlinesize, // offset to last AutoStride, // default x stride -scanlinesize, // special y stride AutoStride); // default z stride \end{code} \item Write a tile that is embedded within a whole image of pixel data, rather than having a one-tile-only memory layout: \begin{code} unsigned char pixels[xres*yres*channels]; int pixelsize = channels * sizeof(pixels[0]); int scanlinesize = xres * pixelsize; ... out->write_tile (x, y, 0, TypeDesc::UINT8, (char *)pixels + y*scanlinesize + x*pixelsize, pixelsize, scanlinesize); \end{code} \item Write only a subset of channels to disk. In this example, our internal data layout consists of 4 channels, but we write just channel 3 to disk as a one-channel image: \begin{code} // In-memory representation is 4 channel const int xres = 640, yres = 480; const int channels = 4; // RGBA const int channelsize = sizeof(unsigned char); unsigned char pixels[xres*yres*channels]; // File representation is 1 channel ImageOutput *out = ImageOutput::create (filename); ImageSpec spec (xres, yres, 1, TypeDesc::UINT8); out->open (filename, spec); // Use strides to write out a one-channel "slice" of the image out->write_image (TypeDesc::UINT8, (char *)pixels+3*channelsize, // offset to chan 3 channels*channelsize, // 4 channel x stride AutoStride, // default y stride AutoStride); // default z stride ... \end{code} \end{itemize} Please consult Section~\ref{sec:imageoutput:reference} for detailed descriptions of the stride parameters to each {\cf write} function. \subsection{Writing a crop window or overscan region} \label{sec:imageoutput:cropwindows} \index{crop windows} \index{overscan} % FIXME -- Marcos suggests adding a figure here to illustrate % the w/h/d, xyz, full The \ImageSpec fields {\cf width}, {\cf height}, and {\cf depth} describe the dimensions of the actual pixel data. At times, it may be useful to also describe an abstract \emph{full} or \emph{display} image window, whose position and size may not correspond exactly to the data pixels. For example, a pixel data window that is a subset of the full display window might indicate a \emph{crop window}; a pixel data window that is a superset of the full display window might indicate \emph{overscan} regions (pixels defined outside the eventual viewport). The \ImageSpec fields {\cf full_width}, {\cf full_height}, and {\cf full_depth} describe the dimensions of the full display window, and {\cf full_x}, {\cf full_y}, {\cf full_z} describe its origin (upper left corner). The fields {\cf x}, {\cf y}, {\cf z} describe the origin (upper left corner) of the pixel data. These fields collectively describe an abstract full display image ranging from [{\cf full_x} ... {\cf full_x+full_width-1}] horizontally, [{\cf full_y} ... {\cf full_y+full_height-1}] vertically, and [{\cf full_z} ... {\cf full_z+full_depth-1}] in depth (if it is a 3D volume), and actual pixel data over the pixel coordinate range [{\cf x} ... {\cf x+width-1}] horizontally, [{\cf y} ... {\cf y+height-1}] vertically, and [{\cf z} ... {\cf z+depth-1}] in depth (if it is a volume). Not all image file formats have a way to describe display windows. An \ImageOutput implementation that cannot express display windows will always write out the {\cf width} $\times$ {\cf height} pixel data, may upon writing lose information about offsets or crop windows. Here is a code example that opens an image file that will contain a $32 \times 32$ pixel crop window within an abstract $640 \times 480$ full size image. Notice that the pixel indices (column, scanline, slice) passed to the {\cf write} functions are the coordinates relative to the full image, not relative to the crop widow, but the data pointer passed to the {\cf write} functions should point to the beginning of the actual pixel data being passed (not the the hypothetical start of the full data, if it was all present). \begin{code} int fullwidth = 640, fulllength = 480; // Full display image size int cropwidth = 16, croplength = 16; // Crop window size int xorigin = 32, yorigin = 128; // Crop window position unsigned char pixels [cropwidth * croplength * channels]; // Crop size! ... ImageOutput *out = ImageOutput::create (filename); ImageSpec spec (cropwidth, croplength, channels, TypeDesc::UINT8); spec.full_x = 0; spec.full_y = 0; spec.full_width = fullwidth; spec.full_length = fulllength; spec.x = xorigin; spec.y = yorigin; out->open (filename, spec); ... int z = 0; // Always zero for 2D images for (int y = yorigin; y < yorigin+croplength; ++y) { out->write_scanline (y, z, TypeDesc::UINT8, (y-yorigin)*cropwidth*channels); } out->close (); \end{code} \subsection{Writing metadata} \label{sec:imageoutput:metadata} The \ImageSpec passed to {\cf open()} can specify all the common required properties that describe an image: data format, dimensions, number of channels, tiling. However, there may be a variety of additional \emph{metadata}\footnote{\emph{Metadata} refers to data about data, in this case, data about the image that goes beyond the pixel values and description thereof.} that should be carried along with the image or saved in the file. The remainder of this section explains how to store additional metadata in the \ImageSpec. It is up to the \ImageOutput to store these in the file, if indeed the file format is able to accept the data. Individual \ImageOutput implementations should document which metadata they respect. \subsubsection{Channel names} In addition to specifying the number of color channels, it is also possible to name those channels. Only a few \ImageOutput implementations have a way of saving this in the file, but some do, so you may as well do it if you have information about what the channels represent. By convention, channel names for red, green, blue, and alpha (or a main image) should be named \qkw{R}, \qkw{G}, \qkw{B}, and \qkw{A}, respectively. Beyond this guideline, however, you can use any names you want. The \ImageSpec has a vector of strings called {\cf channelnames}. Upon construction, it starts out with reasonable default values. If you use it at all, you should make sure that it contains the same number of strings as the number of color channels in your image. Here is an example: \begin{code} int channels = 4; ImageSpec spec (width, length, channels, TypeDesc::UINT8); spec.channelnames.clear (); spec.channelnames.push_back ("R"); spec.channelnames.push_back ("G"); spec.channelnames.push_back ("B"); spec.channelnames.push_back ("A"); \end{code} Here is another example in which custom channel names are used to label the channels in an 8-channel image containing beauty pass RGB, per-channel opacity, and texture $s,t$ coordinates for each pixel. \begin{code} int channels = 8; ImageSpec spec (width, length, channels, TypeDesc::UINT8); spec.channelnames.clear (); spec.channelnames.push_back ("R"); spec.channelnames.push_back ("G"); spec.channelnames.push_back ("B"); spec.channelnames.push_back ("opacityR"); spec.channelnames.push_back ("opacityG"); spec.channelnames.push_back ("opacityB"); spec.channelnames.push_back ("texture_s"); spec.channelnames.push_back ("texture_t"); \end{code} The main advantage to naming color channels is that if you are saving to a file format that supports channel names, then any application that uses \product to read the image back has the option to retain those names and use them for helpful purposes. For example, the {\cf iv} image viewer will display the channel names when viewing individual channels or displaying numeric pixel values in ``pixel view'' mode. \subsubsection{Specially-designated channels} The \ImageSpec contains two fields, {\cf alpha_channel} and {\cf z_channel}, which can be used to designate which channel indices are used for alpha and $z$ depth, if any. Upon construction, these are both set to {\cf -1}, indicating that it is not known which channels are alpha or depth. Here is an example of setting up a 5-channel output that represents RGBAZ: \begin{code} int channels = 5; ImageSpec spec (width, length, channels, format); spec.channelnames.push_back ("R"); spec.channelnames.push_back ("G"); spec.channelnames.push_back ("B"); spec.channelnames.push_back ("A"); spec.channelnames.push_back ("Z"); spec.alpha_channel = 3; spec.z_channel = 4; \end{code} There are two advantages to designating the alpha and depth channels in this manner: \begin{itemize} \item Some file formats may require that these channels be stored in a particular order, with a particular precision, or the \ImageOutput may in some other way need to know about these special channels. \end{itemize} \subsubsection{Arbitrary metadata} For all other metadata that you wish to save in the file, you can attach the data to the \ImageSpec using the {\cf attribute()} methods. These come in polymorphic varieties that allow you to attach an attribute name and a value consisting of a single {\cf int}, {\cf unsigned int}, {\cf float}, {\cf char*}, or {\cf std::string}, as shown in the following examples: \begin{code} ImageSpec spec (...); ... unsigned int u = 1; spec.attribute ("Orientation", u); float x = 72.0; spec.attribute ("dotsize", f); std::string s = "Fabulous image writer 1.0"; spec.attribute ("Software", s); \end{code} These are convenience routines for metadata that consist of a single value of one of these common types. For other data types, or more complex arrangements, you can use the more general form of {\cf attribute()}, which takes arguments giving the name, type (as a \TypeDesc), number of values (1 for a single value, $>1$ for an array), and then a pointer to the data values. For example, \begin{code} ImageSpec spec (...); // Attach a 4x4 matrix to describe the camera coordinates float mymatrix[16] = { ... }; spec.attribute ("worldtocamera", TypeDesc::TypeMatrix, &mymatrix); // Attach an array of two floats giving the CIE neutral color float neutral[2] = { ... }; spec.attribute ("adoptedNeutral", TypeDesc(TypeDesc::FLOAT, 2), &neutral); \end{code} In general, most image file formats (and therefore most \ImageOutput implementations) are aware of only a small number of name/value pairs that they predefine and will recognize. Some file formats (OpenEXR, notably) do accept arbitrary user data and save it in the image file. If an \ImageOutput does not recognize your metadata and does not support arbitrary metadata, that metadatum will be silently ignored and will not be saved with the file. Each individual \ImageOutput implementation should document the names, types, and meanings of all metadata attributes that they understand. \subsubsection{Color space hints} We certainly hope that you are using only modern file formats that support high precision and extended range pixels (such as OpenEXR) and keeping all your images in a linear color space. But you may have to work with file formats that dictate the use of nonlinear color values. This is prevalent in formats that store pixels only as 8-bit values, since 256 values are not enough to linearly represent colors without banding artifacts in the dim values. Since this can (and probably will) happen, we have a convention for explaining what color space your image pixels are in. Each individual \ImageOutput should document how it uses this (or not). The {\cf ImageSpec::extra_attribs} field should store metadata that reveals the color space of the pixels you are sending the ImageOutput. The \qkw{oiio:ColorSpace} attribute may take on any of the following values: \begin{description} \item[\halfspc \rm \qkw{Linear}] indicates that the color pixel values are known to be linear. \item[\halfspc \rm \qkw{GammaCorrected}] indicates that the color pixel values (but not alpha or $z$) have already been gamma corrected (raised to the power $1/\gamma$), and that the gamma exponent may be found in the \qkw{oiio:Gamma} metadata. \item[\halfspc \rm \qkw{sRGB}] indicates that the color pixel values are in sRGB color space. \item[\halfspc \rm \qkw{AdobeRGB}] indicates that the color pixel values are in Adobe RGB color space. \item[\halfspc \rm \qkw{Rec709}] indicates that the color pixel values are in Rec709 color space. \item[\halfspc \rm \qkw{KodakLog}] indicates that the color pixel values are in Kodak logarithmic color space. \end{description} \noindent The color space hints only describe color channels. You should always pass alpha, depth, or other non-color channels with linear values. Here is a simple example of setting up the \ImageSpec when you know that the pixel values you are writing are linear: \begin{code} ImageSpec spec (width, length, channels, format); spec.attribute ("oiio:ColorSpace", "Linear"); ... \end{code} If a particular \ImageOutput implementation is required (by the rules of the file format it writes) to have pixels in a particular color space, then it should try to convert the color values of your image to the right color space if it is not already in that space. For example, JPEG images must be in sRGB space, so if you declare your pixels to be \qkw{Linear}, the JPEG \ImageOutput will convert to sRGB. If you leave the \qkw{oiio:ColorSpace} unset, the values will not be transformed, since the plugin can't be sure that it's not in the correct space to begin with. \subsection{Controlling quantization} \label{sec:imageoutput:quantization} It is possible that your internal data format (that in which you compute pixel values that you pass to the {\cf write} functions) is of greater precision or range than the native data format of the output file. This can occur either because you specified a lower-precision data format in the \ImageSpec that you passed to {\cf open()}, or else that the image file format dictates a particular data format that does not match your internal format. For example, you may compute {\cf float} pixels and pass those to {\cf write_image()}, but if you are writing a JPEG/JFIF file, the values must be stored in the file as 8-bit unsigned integers. The conversion from floating-point formats to integer formats (or from higher to lower integer, which is done by first converting to float) is always done by rescaling the value so that $0.0$ maps to integer $0$ and $1.0$ to the maximum value representable by the integer type, then rounded to an integer value for final output. Here is the code that implements this transformation ({\cf T} is the final output integer type): \begin{code} float value = quant_max * input; T output = (T) clamp ((int)(value + 0.5), quant_min, quant_max); \end{code} The values of the quantization parameters are set in one of three ways: (1) upon construction of the \ImageSpec, they are set to the default quantization values for the given data format; (2) upon call to {\cf ImageSpec::set_format()}, the quantization values are set to the defaults for the given data format; (3) or, after being first set up in this manner, you may manually change the quantization parameters in the \ImageSpec, if you want something other than the default quantization. \noindent Default quantization for each integer type is as follows:\\ \smallskip \begin{tabular}{|l|r|r|} \hline {\bf Data Format} & {\bf min} & {\bf max} \\ \hline {\cf UINT8} & 0 & 255 \\ {\cf INT8} & -128 & 127 \\ {\cf UINT16} & 0 & 65535 \\ {\cf INT16} & -32768 & 32767 \\ {\cf UINT} & 0 & 4294967295 \\ {\cf INT} & -2147483648 & 2147483647 \\ \hline \end{tabular} \\ \smallskip \noindent Note that the default is to use the entire positive range of each integer type to represent the floating-point (0..1) range. Floating-point types do not attempt to remap values, and do not clamp (except to their full floating-point range). The \qkw{oiio:dither} attribute in the \ImageSpec is used to signal the desire for dithering when writing {\cf float} data to a {\cf uint8} file. Dithering can reduce visible banding when converting continuous float data to low bit depth integer images. The attribute is an integer, and if 0 (the default), it means not to dither. If nonzero, it requests that the results are dithered prior to quantization, and the specific nonzero value acts as a ``seed'' to the hash function that determines the per-pixel dither values (two images using the same seed will have the same dither pattern, different seeds will yield different dither patterns). \subsection{Random access and repeated transmission of pixels} \label{sec:imageoutput:randomrewrite} All \ImageOutput implementations that support scanlines and tiles should write pixels in strict order of increasing $z$ slice, increasing $y$ scanlines/rows within each slice, and increasing $x$ column within each row. It is generally not safe to skip scanlines or tiles, or transmit them out of order, unless the plugin specifically advertises that it supports random access or rewrites, which may be queried using: \begin{code} ImageOutput *out = ImageOutput::create (filename); if (out->supports ("random_access")) ... \end{code} \noindent Similarly, you should assume the plugin will not correctly handle repeated transmissions of a scanline or tile that has already been sent, unless it advertises that it supports rewrites, which may be queried using: \begin{code} if (out->supports ("rewrite")) ... \end{code} \subsection{Multi-image files} \label{sec:imageoutput:multiimage} Some image file formats support storing multiple images within a single file. Given a created \ImageOutput, you can query whether multiple images may be stored in the file: \begin{code} ImageOutput *out = ImageOutput::create (filename); if (out->supports ("multiimage")) ... \end{code} Some image formats allow you to do the initial {\cf open()} call without declaring the specifics of the subimages, and simply append subimages as you go. You can detect this by checking \begin{code} if (out->supports ("appendsubimage")) ... \end{code} In this case, all you have to do is, after writing all the pixels of one image but before calling {\cf close()}, call {\cf open()} again for the next subimage and pass {\cf AppendSubimage} as the value for the \emph{mode} argument (see Section~\ref{sec:imageoutput:reference} for the full technical description of the arguments to {\cf open}). The {\cf close()} routine is called just once, after all subimages are completed. Here is an example: \begin{code} const char *filename = "foo.tif"; int nsubimages; // assume this is set ImageSpec specs[]; // assume these are set for each subimage unsigned char *pixels[]; // assume a buffer for each subimage // Create the ImageOutput ImageOutput *out = ImageOutput::create (filename); // Be sure we can support subimages if (subimages > 1 && (! out->supports("multiimage") || ! out->supports("appendsubimage"))) { std::cerr << "Does not support appending of subimages\n"; ImageOutput::destroy (out); return; } // Use Create mode for the first level. ImageOutput::OpenMode appendmode = ImageOutput::Create; // Write the individual subimages for (int s = 0; s < nsubimages; ++s) { out->open (filename, specs[s], appendmode); out->write_image (TypeDesc::UINT8, pixels[s]); // Use AppendSubimage mode for subsequent levels appendmode = ImageOutput::AppendSubimage; } out->close (); ImageOutput::destroy (out); \end{code} On the other hand, if {\cf out->supports("appendsubimage")} returns {\cf false}, then you must use a different {\cf open()} variety that allows you to declare the number of subimages and their specifications up front. Below is an example of how to write a multi-subimage file, assuming that you know all the image specifications ahead of time. This should be safe for any file format that supports multiple subimages, regardless of whether it supports appending, and thus is the preferred method for writing subimages, assuming that you are able to know the number and specification of the subimages at the time you first open the file. \begin{code} const char *filename = "foo.tif"; int nsubimages; // assume this is set ImageSpec specs[]; // assume these are set for each subimage unsigned char *pixels[]; // assume a buffer for each subimage // Create the ImageOutput ImageOutput *out = ImageOutput::create (filename); // Be sure we can support subimages if (subimages > 1 && ! out->supports ("multiimage")) { std::cerr << "Cannot write multiple subimages\n"; ImageOutput::destroy (out); return; } // Open and declare all subimages out->open (filename, nsubimages, specs); // Write the individual subimages for (int s = 0; s < nsubimages; ++s) { if (s > 0) // Not needed for the first, which is already open out->open (filename, specs[s], ImageInput::AppendSubimage); out->write_image (TypeDesc::UINT8, pixels[s]); } out->close (); ImageOutput::destroy (out); \end{code} In both of these examples, we have used \writeimage, but of course \writescanline, \writetile, and {\cf write_rectangle()} work as you would expect, on the current subimage. \subsection{MIP-maps} \label{sec:imageoutput:mipmap} Some image file formats support multiple copies of an image at successively lower resolutions (MIP-map levels, or an ``image pyramid''). Given a created \ImageOutput, you can query whether MIP-maps may be stored in the file: \begin{code} ImageOutput *out = ImageOutput::create (filename); if (out->supports ("mipmap")) ... \end{code} If you are working with an \ImageOutput that supports MIP-map levels, it is easy to write these levels. After writing all the pixels of one MIP-map level, call {\cf open()} again for the next MIP level and pass {\cf ImageInput::AppendMIPLevel} as the value for the \emph{mode} argument, and then write the pixels of the subsequent MIP level. (See Section~\ref{sec:imageoutput:reference} for the full technical description of the arguments to {\cf open()}.) The {\cf close()} routine is called just once, after all subimages and MIP levels are completed. Below is pseudocode for writing a MIP-map (a multi-resolution image used for texture mapping): \begin{code} const char *filename = "foo.tif"; const int xres = 512, yres = 512; const int channels = 3; // RGB unsigned char *pixels = new unsigned char [xres*yres*channels]; // Create the ImageOutput ImageOutput *out = ImageOutput::create (filename); // Be sure we can support either mipmaps or subimages if (! out->supports ("mipmap") && ! out->supports ("multiimage")) { std::cerr << "Cannot write a MIP-map\n"; ImageOutput::destroy (out); return; } // Set up spec for the highest resolution ImageSpec spec (xres, yres, channels, TypeDesc::UINT8); // Use Create mode for the first level. ImageOutput::OpenMode appendmode = ImageOutput::Create; // Write images, halving every time, until we're down to // 1 pixel in either dimension while (spec.width >= 1 && spec.height >= 1) { out->open (filename, spec, appendmode); out->write_image (TypeDesc::UINT8, pixels); // Assume halve() resamples the image to half resolution halve (pixels, spec.width, spec.height); // Don't forget to change spec for the next iteration spec.width /= 2; spec.height /= 2; // For subsequent levels, change the mode argument to // open(). If the format doesn't support MIPmaps directly, // try to emulate it with subimages. if (out->supports("mipmap")) appendmode = ImageOutput::AppendMIPLevel; else appendmode = ImageOutput::AppendSubimage; } out->close (); ImageOutput::destroy (out); \end{code} In this example, we have used \writeimage, but of course \writescanline, \writetile, and {\cf write_rectangle()} work as you would expect, on the current MIP level. \subsection{Per-channel formats} \label{sec:imageoutput:channelformats} Some image formats allow separate per-channel data formats (for example, {\cf half} data for colors and {\cf float} data for depth). When this is desired, the following steps are necessary: \begin{enumerate} \item Verify that the writer supports per-channel formats by checking \\ {\cf supports ("channelformats")}. \item The \ImageSpec passed to {\cf open()} should have its {\cf channelformats} vector filled with the types for each channel. \item The call to {\cf write_scanline}, {\cf read_scanlines}, {\cf write_tile}, {\cf write_tiles}, or {\cf write_image} should pass a {\cf data} pointer to the raw data, already in the native per-channel format of the file and contiguously packed, and specify that the data is of type {\cf TypeDesc::UNKNOWN}. \end{enumerate} For example, the following code fragment will write a 5-channel image to an OpenEXR file, consisting of R/G/B/A channels in {\cf half} and a Z channel in {\cf float}: \begin{code} // Mixed data type for the pixel struct Pixel { half r,g,b,a; float z; }; Pixel pixels[xres*yres]; ImageOutput *out = ImageOutput::create ("foo.exr"); // Double check that this format accepts per-channel formats if (! out->supports("channelformats")) { ImageOutput::destroy (out); return; } // Prepare an ImageSpec with per-channel formats ImageSpec spec (xres, yres, 5, TypeDesc::FLOAT); spec.channelformats.push_back (TypeDesc::HALF); spec.channelformats.push_back (TypeDesc::HALF); spec.channelformats.push_back (TypeDesc::HALF); spec.channelformats.push_back (TypeDesc::HALF); spec.channelformats.push_back (TypeDesc::FLOAT); spec.channelnames.clear (); spec.channelnames.push_back ("R"); spec.channelnames.push_back ("G"); spec.channelnames.push_back ("B"); spec.channelnames.push_back ("A"); spec.channelnames.push_back ("Z"); out->open (filename, spec); out->write_image (TypeDesc::UNKNOWN, /* use channel formats */ pixels, /* data buffer */ sizeof(Pixel)); /* pixel stride */ \end{code} \subsection{Writing ``deep'' data} \label{sec:imageoutput:deepdata} \index{deep data} Some image file formats (OpenEXR only, at this time) support the concept of ``deep'' pixels -- those containing multiple samples per pixel (and a potentially differing number of them in each pixel). You can tell if a format supports deep images by checking {\cf supports("deepdata")}, and you can specify a deep data in an \ImageSpec by setting its {\cf deep} field will be {\cf true}. Deep files cannot be written with the usual {\cf write_scanline}, {\cf write_scanlines}, {\cf write_tile}, {\cf write_tiles}, {\cf write_image} functions, due to the nature of their variable number of samples per pixel. Instead, \ImageOutput has three special member functions used only for writing deep data: \begin{code} bool write_deep_scanlines (int ybegin, int yend, int z, const DeepData &deepdata); bool write_deep_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, const DeepData &deepdata); bool write_deep_image (const DeepData &deepdata); \end{code} It is only possible to write ``native'' data types to deep files; that is, there is no automatic translation into arbitrary data types as there is for ordinary images. All three of these functions are passed deep data in a special {\cf DeepData} structure, described in detail in Section~\ref{sec:deepdata}. \noindent Here is an example of using these methods to write a deep image: \begin{code} // Prepare the spec for 'half' RGBA, 'float' z int nchannels = 5; ImageSpec spec (xres, yres, nchannels); TypeDesc channeltypes[] = { TypeDesc::HALF, TypeDesc::HALF, TypeDesc::HALF, TypeDesc::HALF, TypeDesc::FLOAT }; spec.z_channel = 4; spec.channelnames[spec.z_channel] = "Z"; spec.channeltypes.assign (channeltypes+0, channeltypes+nchannels); spec.deep = true; // Prepare the data (sorry, complicated, but need to show the gist) DeepData deepdata; deepdata.init (spec); for (int y = 0; y < yres; ++y) for (int x = 0; x < xres; ++x) deepdata.set_samples(y*xres+x, ...samples for that pixel...); deepdata.alloc (); // allocate pointers and data int pixel = 0; for (int y = 0; y < yres; ++y) for (int x = 0; x < xres; ++x, ++pixel) for (int chan = 0; chan < nchannels; ++chan) for (int samp = 0; samp < deepdata.samples(pixel); ++samp) deepdata.set_deep_value (pixel, chan, samp, ...value...); // Create the output ImageOutput *out = ImageOutput::create (filename); if (! out) return; // Make sure the format can handle deep data and per-channel formats if (! out->supports("deepdata") || ! out->supports("channelformats")) return; // Do the I/O (this is the easy part!) out->open (filename, spec); out->write_deep_image (deepdata); out->close (); ImageOutput::destroy (out); \end{code} \subsection{Copying an entire image} \label{sec:imageoutput:copyimage} Suppose you want to copy an image, perhaps with alterations to the metadata but not to the pixels. You could open an \ImageInput and perform a {\cf read_image()}, and open another \ImageOutput and call {\cf write_image()} to output the pixels from the input image. However, for compressed images, this may be inefficient due to the unnecessary decompression and subsequent re-compression. In addition, if the compression is \emph{lossy}, the output image may not contain pixel values identical to the original input. A special {\cf copy_image} method of \ImageOutput is available that attempts to copy an image from an open \ImageInput (of the same format) to the output as efficiently as possible with without altering pixel values, if at all possible. Not all format plugins will provide an implementation of {\cf copy_image} (in fact, most will not), but the default implemenatation simply copies pixels one scanline or tile at a time (with decompression/recompression) so it's still safe to call. Furthermore, even a provided {\cf copy_image} is expected to fall back on the default implementation if the input and output are not able to do an efficient copy. Nevertheless, this method is recommended for copying images so that maximal advantage will be taken in cases where savings can be had. The following is an example use of {\cf copy_image} to transfer pixels without alteration while modifying the image description metadata: \begin{code} // Open the input file const char *input = "input.jpg"; ImageInput *in = ImageInput::create (input); ImageSpec in_spec; in->open (input, in_spec); // Make an output spec, identical to the input except for metadata ImageSpec out_spec = in_spec; out_spec.attribute ("ImageDescription", "My Title"); // Create the output file and copy the image const char *output = "output.jpg"; ImageOutput *out = ImageOutput::create (output); out->open (output, out_spec); out->copy_image (in); // Clean up out->close (); ImageOutput::destroy (out); in->close (); ImageInput::destroy (in); \end{code} \subsection{Custom search paths for plugins} \label{sec:imageoutput:searchpaths} When you call {\cf ImageOutput::create()}, the \product library will try to find a plugin that is able to write the format implied by your filename. These plugins are alternately known as DLL's on Windows (with the {\cf .dll} extension), DSO's on Linux (with the {\cf .so} extension), and dynamic libraries on Mac OS X (with the {\cf .dylib} extension). \product will look for matching plugins according to \emph{search paths}, which are strings giving a list of directories to search, with each directory separated by a colon (`{\cf :}'). Within a search path, any substrings of the form {\cf \$\{FOO\}} will be replaced by the value of environment variable {\cf FOO}. For example, the searchpath \qkw{\$\{HOME\}/plugins:/shared/plugins} will first check the directory \qkw{/home/tom/plugins} (assuming the user's home directory is {\cf /home/tom}), and if not found there, will then check the directory \qkw{/shared/plugins}. The first search path it will check is that stored in the environment variable {\cf OIIO_LIBRARY_PATH}. It will check each directory in turn, in the order that they are listed in the variable. If no adequate plugin is found in any of the directories listed in this environment variable, then it will check the custom searchpath passed as the optional second argument to {\cf ImageOutput::create()}, searching in the order that the directories are listed. Here is an example: \begin{code} char *mysearch = "/usr/myapp/lib:${HOME}/plugins"; ImageOutput *out = ImageOutput::create (filename, mysearch); ... \end{code} % $ \subsection{Error checking} \label{sec:imageoutput:errors} \index{error checking} Nearly every \ImageOutput API function returns a {\cf bool} indicating whether the operation succeeded ({\cf true}) or failed ({\cf false}). In the case of a failure, the \ImageOutput will have saved an error message describing in more detail what went wrong, and the latest error message is accessible using the \ImageOutput method {\cf geterror()}, which returns the message as a {\cf std::string}. The exception to this rule is {\cf ImageOutput::create}, which returns {\cf NULL} if it could not create an appropriate \ImageOutput. And in this case, since no \ImageOutput exists for which you can call its {\cf geterror()} function, there exists a global {\cf geterror()} function (in the {\cf OpenImageIO} namespace) that retrieves the latest error message resulting from a call to {\cf create}. Here is another version of the simple image writing code from Section~\ref{sec:imageoutput:simple}, but this time it is fully elaborated with error checking and reporting: \begin{code} #include OIIO_NAMESPACE_USING ... const char *filename = "foo.jpg"; const int xres = 640, yres = 480; const int channels = 3; // RGB unsigned char pixels[xres*yres*channels]; ImageOutput *out = ImageOutput::create (filename); if (! out) { std::cerr << "Could not create an ImageOutput for " << filename << ", error = " << OpenImageIO::geterror() << "\n"; return; } ImageSpec spec (xres, yres, channels, TypeDesc::UINT8); if (! out->open (filename, spec)) { std::cerr << "Could not open " << filename << ", error = " << out->geterror() << "\n"; ImageOutput::destroy (out); return; } if (! out->write_image (TypeDesc::UINT8, pixels)) { std::cerr << "Could not write pixels to " << filename << ", error = " << out->geterror() << "\n"; ImageOutput::destroy (out); return; } if (! out->close ()) { std::cerr << "Error closing " << filename << ", error = " << out->geterror() << "\n"; ImageOutput::destroy (out); return; } ImageOutput::destroy (out); \end{code} \section{\ImageOutput Class Reference} \label{sec:imageoutput:reference} \apiitem{static ImageOutput * {\ce create} (const std::string \&filename, \\ \bigspc\bigspc\spc const std::string \&plugin_searchpath="")} Create an \ImageOutput that can be used to write an image file. The type of image file (and hence, the particular subclass of \ImageOutput returned, and the plugin that contains its methods) is inferred from the extension of the file name. The {\kw plugin_searchpath} parameter is a colon-separated list of directories to search for \product plugin DSO/DLL's. \apiend \apiitem{void {\ce destroy} (ImageOutput *Output)} Destroy an \ImageOutput that was created by {\cf create()}. The {\cf destroy()} method is just a wrapper around operator {\cf delete}, but by being implemented within the \product DLL, it can ensure that the memory deallocation is done in the same DLL arena as where it was originally allocated. This is considered safer than a bare {\cf delete} when used inside ``plug-ins,'' especially on Windows systems. \apiend \apiitem{const char * {\ce format_name} ()} Returns the canonical name of the format that this \ImageOutput instance is capable of writing. \apiend \apiitem{int {\ce supports} (string_view feature) const} \label{sec:supportsfeaturelist} Given the name of a \emph{feature}, tells if this \ImageOutput instance supports that feature. Most queries will simply return 0 for ``doesn't support the feature'' and nonzero for ``supports the feature,'' but it is acceptable to have queries return other nonzero integers to indicate varying degrees of support or limits (but those queries should be clearly documented as such). The following features are recognized by this query: \begin{description} \item[\spc] \spc \item[\rm \qkw{tiles}] Is this plugin able to write tiled images? \item[\rm \qkw{rectangles}] Can this plugin accept arbitrary rectangular pixel regions (via {\kw write_rectangle()})? False indicates that pixels must be transmitted via \writescanline (if scanline-oriented) or \writetile (if tile-oriented, and only if {\kw supports("tiles")} returns true). \item[\rm \qkw{random_access}] May tiles or scanlines be written in any order? False indicates that they must be in successive order. \item[\rm \qkw{multiimage}] Does this format support multiple subimages within a single file? \item[\rm \qkw{appendsubimage}] Does this format support multiple subimages that can be successively appended at will, without needing to pre-declare the number and specifications the subimages when the file is first opened? \item[\rm \qkw{mipmap}] Does this format support resolutions per image/subimage (MIP-map levels)? \item[\rm \qkw{volumes}] Does this format support ``3D'' pixel arrays (a.k.a.\ volume images)? \item[\rm \qkw{alpha}] Does this format support an alpha channel? \item[\rm \qkw{nchannels}] Does this format support an arbitrary number of channels (beyond RGBA)? \item[\rm \qkw{rewrite}] Does this plugin allow the same scanline or tile to be sent more than once? Generally this is true for plugins that implement some sort of interactive display, rather than a saved image file. \item[\rm \qkw{empty}] Does this plugin support passing a NULL data pointer to the various {\kw write} routines to indicate that the entire data block is composed of pixels with value zero. Plugins that support this achieve a speedup when passing blank scanlines or tiles (since no actual data needs to be transmitted or converted). \item[\rm \qkw{channelformats}] Does this format writer support per-channel data formats, respecting the \ImageSpec's {\cf channelformats} field? (If not, it only accepts a single data format for all channels and will ignore the {\cf channelformats} field of the spec.) \item[\rm \qkw{displaywindow}] Does the image format support specifying a display (``full'') window that is distinct from the pixel data window? \item[\rm \qkw{origin}] Does the image format support specifying a pixel window origin (i.e., nonzero \ImageSpec {\cf x}, {\cf y}, {\cf z})? \item[\rm \qkw{negativeorigin}] Does the image format allow pixel and data window origins (i.e., nonzero \ImageSpec {\cf x}, {\cf y}, {\cf z}, {\cf full_x}, {\cf full_y}, {\cf full_z}) to have negative values? \item[\rm \qkw{deepdata}] Does the image format allow ``deep'' data consisting of multiple values per pixel (and potentially a differing number of values from pixel to pixel)? \item[\rm \qkw{arbitrary_metadata}] Does the image file format allow metadata with arbitrary names (and either arbitrary, or a reasonable set of, data types)? (Versus the file format supporting only a fixed list of specific metadata names/values? \item[\rm \qkw{exif}] Does the image file format support Exif camera data (either specifically, or via arbitrary named metadata)? \item[\rm \qkw{iptc}] Does the image file format support IPTC data (either specifically, or via arbitrary named metadata)? \end{description} \noindent This list of queries may be extended in future releases. Since this can be done simply by recognizing new query strings, and does not require any new API entry points, addition of support for new queries does not break ``link compatibility'' with previously-compiled plugins. \apiend \apiitem{bool {\ce open} (const std::string \&name, const ImageSpec \&newspec,\\ \bigspc OpenMode mode=Create)} \label{sec:imageoutputopen} Open the file with given {\kw name}, with resolution and other format data as given in {\kw newspec}. This function returns {\kw true} for success, {\kw false} for failure. Note that it is legal to call {\kw open()} multiple times on the same file without a call to {\kw close()}, if it supports multiimage and {\kw mode} is {\kw AppendSubimage}, or if it supports MIP-maps and {\kw mode} is {\kw AppendMIPLevel} -- this is interpreted as appending a subimage, or a MIP level to the current subimage, respectively. \apiend \apiitem{bool {\ce open} (const std::string \&name, int subimages, const ImageSpec *specs)} Open the file with given {\cf name}, expecting to have a given total number of subimages, described by {\cf specs[0..subimages-1]}. Return {\cf true} for success, {\cf false} for failure. Upon success, the first subimage will be open and ready for transmission of pixels. Subsequent subimages will be denoted with the usual call of {\cf open(name,spec,AppendSubimage)} (and MIP levels by {\cf open(name,spec,AppendMIPLevel)}). The purpose of this call is to accommodate format-writing libraries that must know the number and specifications of the subimages upon first opening the file; such formats can be detected by \begin{code} supports("multiimage") && ! supports("appendsubimage") \end{code} The individual specs passed to the appending {\cf open()} calls for subsequent subimages must match the ones originally passed. \apiend \apiitem{const ImageSpec \& {\ce spec} ()} Returns the spec internally associated with this currently open \ImageOutput. \apiend \apiitem{bool {\ce close} ()} Closes the currently open file associated with this \ImageOutput and frees any memory or resources associated with it. \apiend \apiitem{bool {\ce write_scanline} (int y, int z, TypeDesc format, const void *data, \\ \bigspc stride_t xstride=AutoStride)} Write the scanline that includes pixels $(*,y,z)$ from {\cf data}. For 2D non-volume images, $z$ is ignored. The {\cf xstride} value gives the data spacing of adjacent pixels (in bytes). Strides set to the special value {\kw AutoStride} imply contiguous data, i.e., \\ \spc {\kw xstride} $=$ {\kw spec.nchannels * format.size()} \\ This method automatically converts the data from the specified {\kw format} to the actual output format of the file. If {\cf format} is {\cf TypeDesc::UNKNOWN}, the data is assumed to already be in the file's native format (including per-channel formats, as specified in the \ImageSpec's {\cf channelformats} field, if applicable). Return {\kw true} for success, {\kw false} for failure. It is a failure to call \writescanline with an out-of-order scanline if this format driver does not support random access. \apiend \apiitem{bool {\ce write_scanlines} (int ybegin, int yend, int z, \\ \bigspc TypeDesc format, const void *data, \\ \bigspc stride_t xstride=AutoStride, stride_t ystride=AutoStride)} Write a block of scanlines that include pixels $(*,y,z)$, where ${\mathit ybegin} \le y < {\mathit yend}$. This is essentially identical to {\cf write_scanline()}, except that it can write more than one scanline at a time, which may be more efficient for certain image format writers. For 2D non-volume images, $z$ is ignored. The {\kw xstride} value gives the distance between successive pixels (in bytes), and {\kw ystride} gives the distance between successive scanlines. Strides set to the special value {\kw AutoStride} imply contiguous data, i.e., \\ \spc {\kw xstride} $=$ {\kw spec.nchannels*format.size()} \\ \spc {\kw ystride} $=$ {\kw spec.width*xstride} This method automatically converts the data from the specified {\kw format} to the actual output format of the file. If {\cf format} is {\cf TypeDesc::UNKNOWN}, the data is assumed to already be in the file's native format (including per-channel formats, as specified in the \ImageSpec's {\cf channelformats} field, if applicable). Return {\kw true} for success, {\kw false} for failure. It is a failure to call \writescanline with an out-of-order scanline if this format driver does not support random access. \apiend \apiitem{bool {\ce write_tile} (int x, int y, int z, TypeDesc format, const void *data, \\ \bigspc stride_t xstride=AutoStride, stride_t ystride=AutoStride, \\ \bigspc stride_t zstride=AutoStride)} Write the tile with $(x,y,z)$ as the upper left corner. For 2D non-volume images, $z$ is ignored. The three stride values give the distance (in bytes) between successive pixels, scanlines, and volumetric slices, respectively. Strides set to the special value {\kw AutoStride} imply contiguous data in the shape of a full tile, i.e., \\ \spc {\kw xstride} $=$ {\kw spec.nchannels * format.size()} \\ \spc {\kw ystride} $=$ {\kw xstride * spec.tile_width} \\ \spc {\kw zstride} $=$ {\kw ystride * spec.tile_height} \\ This method automatically converts the data from the specified {\kw format} to the actual output format of the file. If {\cf format} is {\cf TypeDesc::UNKNOWN}, the data is assumed to already be in the file's native format (including per-channel formats, as specified in the \ImageSpec's {\cf channelformats} field, if applicable). Return {\kw true} for success, {\kw false} for failure. It is a failure to call \writetile with an out-of-order tile if this format driver does not support random access. This function returns {\cf true} if it successfully writes the tile, otherwise {\cf false} for a failure. The call will fail if the image is not tiled, or if $(x,y,z)$ is not actually a tile boundary. \apiend \apiitem{bool {\ce write_tiles} (int xbegin, int xend, int ybegin, int yend,\\ \bigspc int zbegin, int zend, TypeDesc format, const void *data, \\ \bigspc stride_t xstride=AutoStride, stride_t ystride=AutoStride, \\ \bigspc stride_t zstride=AutoStride)} Write the tiles that include pixels {\kw xbegin} $\le x <$ {\kw xend}, {\kw ybegin} $\le y <$ {\kw yend}, {\kw zbegin} $\le z <$ {\kw zend} from {\kw data}, converting if necessary from {\kw format} specified into the file's native data format. If {\cf format} is {\cf TypeDesc::UNKNOWN}, the data will be assumed to already be in the native format (including per-channel formats, if applicable). The stride values give the data spacing of adjacent pixels, scanlines, and volumetric slices, respectively (measured in bytes). Strides set to the special value of {\kw AutoStride} imply contiguous data in the shape of the region specified, i.e., \\ \spc {\kw xstride} $=$ {\kw spec.nchannels * spec.pixel_size()} \\ \spc {\kw ystride} $=$ {\kw xstride * (xend - xbegin)} \\ \spc {\kw zstride} $=$ {\kw ystride * (yend - ybegin)} \\ The data for those tiles is assumed to be in the usual image order, as if it were just one big tile, and not ``paded'' to a whole multiple of the tile size. This function returns {\cf true} if it successfully writes the tiles, otherwise {\cf false} for a failure. The call will fail if the image is not tiled, or if the pixel ranges do not fall along tile (or image) boundaries, or if it is not a valid tile range. \apiend \apiitem{bool {\ce write_rectangle} ({\small int xbegin, int xend, int ybegin, int yend, \\ \bigspc int zbegin, int zend,} TypeDesc format, const void *data, \\ \bigspc stride_t xstride=AutoStride, stride_t ystride=AutoStride, \\ \bigspc stride_t zstride=AutoStride)} Write pixels covering the range that includes pixels {\kw xbegin} $\le x <$ {\kw xend}, {\kw ybegin} $\le y <$ {\kw yend}, {\kw zbegin} $\le z <$ {\kw zend}. The three stride values give the distance (in bytes) between successive pixels, scanlines, and volumetric slices, respectively. Strides set to the special value {\kw AutoStride} imply contiguous data, i.e.,\\ \spc {\kw xstride} $=$ {\kw spec.nchannels*format.size()} \\ \spc {\kw ystride} $=$ {\kw xstride*(xend-xbegin)} \\ \spc {\kw zstride} $=$ {\kw ystride*(yend-ybegin)}\\ This method automatically converts the data from the specified {\kw format} to the actual output format of the file. If {\cf format} is {\cf TypeDesc::UNKNOWN}, the data is assumed to already be in the file's native format (including per-channel formats, as specified in the \ImageSpec's {\cf channelformats} field, if applicable). Return {\kw true} for success, {\kw false} for failure. It is a failure to call {\kw write_rectangle} for a format plugin that does not return true for {\kw supports("rectangles")}. \apiend \apiitem{bool {\ce write_image} (TypeDesc format, const void *data, \\ \bigspc stride_t xstride=AutoStride, stride_t ystride=AutoStride, \\ \bigspc stride_t zstride=AutoStride, \\ \bigspc ProgressCallback progress_callback=NULL,\\ \bigspc void *progress_callback_data=NULL)} Write the entire image of {\kw spec.width} $\times$ {\kw spec.height} $\times$ {\kw spec.depth} pixels, with the given strides and in the desired format. If {\cf format} is {\cf TypeDesc::UNKNOWN}, the data is assumed to already be in the file's native format (including per-channel formats, as specified in the \ImageSpec's {\cf channelformats} field, if applicable). Strides set to the special value {\kw AutoStride} imply contiguous data, i.e., \\ \spc {\kw xstride} $=$ {\kw spec.nchannels * format.size()} \\ \spc {\kw ystride} $=$ {\kw xstride * spec.width} \\ \spc {\kw zstride} $=$ {\kw ystride * spec.height}\\ The function will internally either call \writescanline or \writetile, depending on whether the file is scanline- or tile-oriented. Because this may be an expensive operation, a progress callback may be passed. Periodically, it will be called as follows: \begin{code} progress_callback (progress_callback_data, float done) \end{code} \noindent where \emph{done} gives the portion of the image (between 0.0 and 1.0) that has been written thus far. \apiend \apiitem{bool {\ce write_deep_scanlines} (int ybegin, int yend, int z, \\ \bigspc const DeepData \&deepdata) \\ bool {\ce write_deep_tiles} (int xbegin, int xend, int ybegin, int yend,\\ \bigspc int zbegin, int zend, const DeepData \&deepdata) \\ bool {\ce write_deep_image} (const DeepData \&deepdata)} Write deep data for a block of scanlines, a block of tiles, or an entire image (analogously to the usual {\cf write_scanlines}, {\cf write_tiles}, and {\cf write_image}, but with deep data). Return {\kw true} for success, {\kw false} for failure. \apiend \apiitem{bool {\ce copy_image} (ImageInput *in)} Read the current subimage of {\cf in}, and write it as the next subimage of {\cf *this}, in a way that is efficient and does not alter pixel values, if at all possible. Both {\cf in} and {\cf this} must be a properly-opened \ImageInput and \ImageOutput, respectively, and their current images must match in size and number of channels. Return {\cf true} if it works ok, {\cf false} if for some reason the operation wasn't possible. If a particular \ImageOutput implementation does not supply a {\cf copy_image} method, it will inherit the default implementation, which is to simply read scanlines or tiles from {\cf in} and write them to {\cf *this}. However, some file format implementations may have a special technique for directly copying raw pixel data from the input to the output, when both input and output are the same file type and the same data format. This can be more efficient than {\cf in->read_image} followed by {\cf out->write_image}, and avoids any unintended pixel alterations, especially for formats that use lossy compression. \apiend \apiitem{int {\ce send_to_output} (const char *format, ...)} General message passing between client and image output server. This is currently undefined and is reserved for future use. \apiend \apiitem{int {\ce send_to_client} (const char *format, ...)} General message passing between client and image output server. This is currently undefined and is reserved for future use. \apiend \apiitem{void {\ce threads} (int n) \\ int {\ce threads} () const} \index{threads} Get or set the threading policy for this \ImageOutput, controlling the maximum amount of parallelizing thread ``fan-out'' that might occur during large write operations. The default of 0 means that the global {\cf attribute("threads")} value should be used (which itself defaults to using as many threads as cores; see Section~\ref{sec:attribute:threads}). The main reason to change this value is to set it to 1 to indicate that the calling thread should do all the work rather than spawning new threads. That is probably the desired behavior in situations where the calling application has already spawned multiple worker threads. \apiend \apiitem{std::string {\ce geterror} ()} \index{error checking} Returns the current error string describing what went wrong if any of the public methods returned {\kw false} indicating an error. (Hopefully the implementation plugin called {\kw error()} with a helpful error message.) \apiend \index{Image I/O API|)} \chapwidthend openimageio-1.7.17~dfsg0.orig/src/doc/CMakeLists.txt0000644000175000017500000000261613151711064020426 0ustar mfvmfvproject(documentation) set (public_docs openimageio.pdf "${OpenImageIO_SOURCE_DIR}/LICENSE" "${OpenImageIO_SOURCE_DIR}/CHANGES.md" ) if (INSTALL_DOCS) install (FILES ${public_docs} DESTINATION ${DOC_INSTALL_DIR} COMPONENT documentation) endif () # generate man pages using txt2man and a tiny python script to munge the # result of "$tool --help" find_program(TXT2MAN txt2man) find_package(PythonInterp) if (UNIX AND TXT2MAN AND PYTHONINTERP_FOUND) message (STATUS "Unix man page documentation will be generated") set (cli_tools oiiotool iinfo maketx idiff igrep iconvert) find_program (IV_FOUND iv) if (IV_FOUND) list (APPEND cli_tools iv) endif() foreach (tool ${cli_tools}) set (outfile "${documentation_BINARY_DIR}/${tool}.1") list (APPEND manpage_files ${outfile}) add_custom_command (OUTPUT ${outfile} COMMAND ${tool} --help | ${PYTHON_EXECUTABLE} "${documentation_SOURCE_DIR}/help2man_preformat.py" | ${TXT2MAN} -v OpenImageIO -s 1 -t ${tool} > ${outfile} DEPENDS ${tool} help2man_preformat.py) endforeach() # force man page build before install add_custom_target (man_pages ALL DEPENDS ${manpage_files}) if (INSTALL_DOCS) install (FILES ${manpage_files} DESTINATION ${MAN_INSTALL_DIR} COMPONENT documentation) endif () endif() openimageio-1.7.17~dfsg0.orig/src/doc/techref.sty0000644000175000017500000001541013151711064020043 0ustar mfvmfv%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % renew the commands for \chapter, \section, etc., to have the % formatting that I want. These were taken from book.cls and % modified. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \renewcommand\part{% \if@openright \cleardoublepage \else \clearpage \fi \thispagestyle{plain}% \if@twocolumn \onecolumn \@tempswatrue \else \@tempswafalse \fi \null\vfil \secdef\@part\@spart} \def\@part[#1]#2{% \ifnum \c@secnumdepth >-2\relax \refstepcounter{part}% \addcontentsline{toc}{part}{\thepart\hspace{1em}#1}% \else \addcontentsline{toc}{part}{#1}% \fi \markboth{}{}% {\centering \interlinepenalty \@M \normalfont \ifnum \c@secnumdepth >-2\relax %lg \huge\bfseries \partname~\thepart \huge\sffamily\bfseries \partname~\thepart %lg \par \vskip 20\p@ \fi \Huge \bfseries #2\par}% \@endpart} \def\@spart#1{% {\centering \interlinepenalty \@M \normalfont %lg \Huge \bfseries #1\par}% \Huge \sffamily\bfseries #1\par}% %lg \@endpart} \def\@endpart{\vfil\newpage \if@twoside \null \thispagestyle{empty}% \newpage \fi \if@tempswa \twocolumn \fi} \renewcommand\chapter{\if@openright\cleardoublepage\else\clearpage\fi \thispagestyle{plain}% \global\@topnum\z@ \@afterindentfalse \secdef\@chapter\@schapter} \def\@chapter[#1]#2{\ifnum \c@secnumdepth >\m@ne \if@mainmatter \refstepcounter{chapter}% \typeout{\@chapapp\space\thechapter.}% \addcontentsline{toc}{chapter}% {\protect\numberline{\thechapter}#1}% \else \addcontentsline{toc}{chapter}{#1}% \fi \else \addcontentsline{toc}{chapter}{#1}% \fi \chaptermark{#1}% \addtocontents{lof}{\protect\addvspace{10\p@}}% \addtocontents{lot}{\protect\addvspace{10\p@}}% \if@twocolumn \@topnewpage[\@makechapterhead{#2}]% \else \@makechapterhead{#2}% \@afterheading \fi} \def\@makechapterhead#1{% \vspace*{50\p@}% {\parindent \z@ \raggedright \normalfont \ifnum \c@secnumdepth >\m@ne \if@mainmatter %lg \huge\bfseries \@chapapp\space \thechapter \Huge \sffamily \bfseries \thechapter \hspace{1em} %lg %lg \par\nobreak %lg \vskip 20\p@ \fi \fi \interlinepenalty\@M %lg \Huge \bfseries #1\par\nobreak \Huge \sffamily\bfseries #1\par\nobreak %lg \vskip 40\p@ }} \def\@schapter#1{\if@twocolumn \@topnewpage[\@makeschapterhead{#1}]% \else \@makeschapterhead{#1}% \@afterheading \fi} \def\@makeschapterhead#1{% %%% lg this is for chapter* \vspace*{50\p@}% {\parindent \z@ \raggedright \normalfont \interlinepenalty\@M %lg \Huge \bfseries #1\par\nobreak \Huge \sffamily \bfseries #1\par\nobreak %lg \vskip 40\p@ }} \renewcommand\section{\@startsection {section}{1}{\z@}% {-3.5ex \@plus -1ex \@minus -.2ex}% {2.3ex \@plus.2ex}% %lg {\normalfont\Large\bfseries}} {\normalfont\Large\sffamily\bfseries}} %lg \renewcommand\subsection{\@startsection{subsection}{2}{\z@}% {-3.25ex\@plus -1ex \@minus -.2ex}% {1.5ex \@plus .2ex}% %lg {\normalfont\large\bfseries}} {\normalfont\large\sffamily\bfseries}} %lg \renewcommand\subsubsection{\@startsection{subsubsection}{3}{\z@}% {-3.25ex\@plus -1ex \@minus -.2ex}% {1.5ex \@plus .2ex}% %lg {\normalfont\normalsize\bfseries}} {\normalfont\normalsize\sffamily\bfseries}} %lg \renewcommand\paragraph{\@startsection{paragraph}{4}{\z@}% {3.25ex \@plus1ex \@minus.2ex}% {-1em}% {\normalfont\normalsize\bfseries}} \renewcommand\subparagraph{\@startsection{subparagraph}{5}{\parindent}% {3.25ex \@plus1ex \@minus .2ex}% {-1em}% {\normalfont\normalsize\bfseries}} %%%% % lg - added new environment for descriptions specifically for the glossary % \newenvironment{glossarydescription} {\list{}{\small \setlength{\leftmargin}{0pt} \setlength{\itemsep}{0pt} \labelwidth\z@ \itemindent-\leftmargin \let\makelabel\glossarydescriptionlabel}} {\endlist} \newcommand*\glossarydescriptionlabel[1]{\hspace\labelsep \normalfont\bfseries #1} % %%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Modify the bibliography citation style % I borrowed this from apastyle, then modified it -- lg %%%%%%%% \def\@cite#1#2{(#1\if@tempswa , #2\fi)} \def\@biblabel#1{} \newlength{\bibhang} \setlength{\bibhang}{0em} \@ifundefined{chapter}{\def\thebibliography#1{\section*{\refname\@mkboth {\sl\uppercase{\refname}}{\sl\uppercase{\refname}}}\list {\relax}{ \setlength{\labelsep}{0em} %lg \setlength{\itemindent}{-\bibhang} \setlength{\leftmargin}{\bibhang}} \def\newblock{\hskip .11em plus .33em minus .07em} \sloppy\clubpenalty4000\widowpenalty4000 \sfcode`\.=1000\relax}}% {\def\thebibliography#1{\chapter*{\bibname\@mkboth {\sl\uppercase{\bibname}}{\sl\uppercase{\bibname}}}\list {\relax}{\setlength{\labelsep}{0em} \small \setlength{\itemsep}{0pt} %lg \setlength{\itemindent}{-\bibhang} \setlength{\leftmargin}{\bibhang}} \def\newblock{\hskip .11em plus .33em minus .07em} \sloppy\clubpenalty4000\widowpenalty4000 \sfcode`\.=1000\relax}} \def\@citex[#1]#2{\if@filesw\immediate\write\@auxout{\string\citation{#2}}\fi \def\@citea{}\@cite{\@for\@citeb:=#2\do {\@citea\def\@citea{; }\@ifundefined {b@\@citeb}{{\bf ?}\@warning {Citation `\@citeb' on page \thepage \space undefined}}% {\csname b@\@citeb\endcsname}}}{#1}} openimageio-1.7.17~dfsg0.orig/src/doc/figures/0000755000175000017500000000000013151711064017325 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/doc/figures/grid-small.jpg0000644000175000017500000014120513151711064022065 0ustar mfvmfvJFIFExifII*JR1ZiHHOpenImageIO 1.5.0dev : ../../../build/macosx/src/oiiotool/oiiotool ../../../../oiio-images/grid.tif --resize 256x256 --colorconvert linear sRGB -o grid-small.jpg../../../build/macosx/src/oiiotool/oiiotool ../../../../oiio-images/grid.tif --resize 256x256 --colorconvert linear sRGB -o grid-small.jpg02200100Photoshop 3.08BIMAOpenImageIO 1.5.0dev : ../../../build/macosx/src/oiiotool/oiiotool ../../../../oiio-images/grid.tif --resize 256x256 --colorconvert linear sRGB -o grid-small.jpgCC" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?~|#>xğ"3k<߇Ww3ڙpI[tg@ ىq'߶[Z?MPӬ4ɴ}ODV n#XXEoB f ԩkv9/vrk}R}vM^caXOl:6^<=W:ti蛍Fy RS#?d?0OW.|7׼SO>E4NHaىhKyUVU >> !|!xOԵsSKhaZ84oCI2"nvgCk ĿO]ŷ'Yn,U-ʆ@IeHXk߅_=u gMu2's1Xcn@cRT+ѕlf kIב_y^ӯs<5j҄BpZ\YP|ǕJC_o |2d( x~|o~::kb[tEoO1\$jc([)ɿc;|)׌~|BxûMs:zȌ-<-BmɊ= :/A.״,m;5M6P54] $Pi 8Zo T3蚴PcYǫi~Equ}$2o,˹wyr-9uZ*s\vtfjQiꤝaqO-hŽ3MԎ3)˖)Z2[Msr<<}o ? n-k ,}61+{};$*c?gobO$.vKoͧwUo2yXM>#?|-Ac!"v񟻌NF=<[c#æz;_Z+ĩ+N[/.*sah;~_fc 'v~?n#.?>oLּǟg- |Jז|5Vmm?ldD?,nk&O1ҷ?$Z>@eF.ucaH5)σ9 1ӵU h9m/.ަyUyp~:_v;~*u]~)Aycwõ Ї=1r7qޟw:+-OۛЃwï.gײj7[Ha8>?6iےnHo'G}x}? W{g|eT{?{5/֓?ſ4֟ |m+E?MxGj% ;7epc,ZΟOhO s`$_UCjDq`M6~F݉k@H*bMgNHl5@ϧ8o?z~AGMgw{tIm̈$:o;5Qoq ?>z\Hg|+3Ӟmd\-'S}z_[^gxt->|$fj1YXZEi)W!dfUS]A>aԬ-k6eG =|Ka$QO ywK-o.b;w*y:*#-YӂB|ʥ[S17&ޜ[N/y'}n>oR? ה>xq#=qFLqk>-#i? 5"W ៈ7^I}-c)y.Xiu?Zg zq_ /?!zu x~mV%[5=BKY ;6pϼ3p}$ ,WGFg:giƥI( MF.qݓO:.R}޻%?a~h| ~\j6O5Kv]jͬV;Y[=Wuه~ ӿf/Ə%gGXtWJm-_ Sqmq\8IHX>BMy_쥢蚗3umI_BC=>o:wBQTdg!%gĻ_jQ< R]F9 Oi291j0xe:L}(VuiR>έ? sJ2:9BpQݽfS4?C :oAOZ!h|9xGԴmE׶v2,+`Hji'mᆹ=OS?jrERHXE`?Ԉ@[i_-nbr2Ĝ+axB JIqԖ=O9,\|V𩄥FUdSF\ pFiʜӏ3]"xg>ߴ!s5^Mk_i ?ow\AyR{qO>+eOltĚD>=P mwk{y 9%e%DaH_b>%~ ƚRߴ"Fp #r\yï;_?h'1e!{Ҽj01D26] (%(II:|A.N[$]vUT˥Fr8XK)CQNs8Ivt>8_ǏnྋI<ki|E%J&drCnoa.Ҳu_<_ѿk}߄մ7_ ^};ź_4^j:[moTuI"2z_GO( +&'=sަ[0~%~$> 7j}/H䚊k)I+ǕZQx.X*'J[IլέI監vGGW4Ԁ!jO/˿Y+299Etk*1mc3k?E{N\Jw+c㲤."aA+ͷEb9׍BԷ|e2UGH▐ȝ̍c$TH'f`?> E&q>\m"}*/YVJqqCx/O # \1"Wʪ=:]%|LlVfQN=/}o-KTk$Chx>x4PyirW^/Ol~9~\|JF-K|B$mm4$~zd)_t_s{7+ۭY=rpO W:BKzSVe[/'[;tt݀݃ح51cY^n{P=־EJ.x˽<{} ~*hv@ DV-TIV_ s`?i_Y}ڽ}A#kISc78b?:֠j_s'zn?OM |`ɪ<}955-F:|Rg(]t`/>YZ!e~_:ijiOK >8㩯:I[vSܬ7_Wo,+jmBt%o?)aJ{Eu_?Qc1yA__7?go o 4[F6}8ֽ9?u=+ t˿?ǟ৆.㯌t>}>R[D|RV(D@~@+.{/M_i֍ϯ^A0W5aRis~??OTj4̌KυWi? u9ُ^ 5# Tjgq+|0'4V㿌AA!V{x^gA /Ȍaj_/|$˯M&d`?'xOF >zw t%?9Yᠼn Υv8$rMz1 s o | 콳gxK]A|<`"Q ~i|aƔH<U2C1uCFc$ӭ~]\?c0ڜqw-c^nWZ%O}sq=I65?vn?$nBOy?h3[υt01Z*Ҽ=hĐ?Lv t)|(|wE|vZi:{ r wU_QYfج࢝nz_oߛsort߆ǀ?i*Qּ9 j_59u-KÚeޡg [n{K=FDeVhI1[OSp#7,JeomyEY-YU>:?~8=` A^3S>ib[ k4ۦlyX+) NR|IՕ:u%ԭ)%+(MZV_ӯK|FX|~|ۏz^nxCeG[mJ'QyKT-Æ0Z?V[1;UmQխdh.-$7Y!Q䰮w?>7޷? >{x$. !Y#BwFگ,?3٩?^7S+HήMC:ZTRu {+sRqݔ_3@F0\kwt.|xPCⷈl~ѿ> [Busq-MZ+;>DŝLwBķp4I[:=S^iڶ{\ySn 4 r3Ֆ_ t]=IO 0NN:Owᦛa1yO|d{O:|a%|-#ockE/Jy [GܥRe'k krpJiͻi/WT7k$G0.!4-h7?k37Wۭ%[,.Ɲ2ǟ_ZKcS-}oHm >|-<'EC=v{:c? vjY BiH|Ez9 +<Nk7H}.N=kjs/ɞfUhSn~[ݿf`IOE1ڼk y#OXC7n;{XV?`XnwYd|V={g a~8Cёw3Vj%fcw5-^[xW^e4?Oj6>ێzq^iZAkc}?q +LQIծ}M #|~ cD-7ó>\W]/F.sq ?C;|ai++˜aj.(-88S+.d1M7Se[gn:խU-9ۮQu{fo~6mE8l`mm$3c6,q!Kw=y'ƋYlu9ѵ%~'hөjO: .l Kx?Zu{RO鲝b'o]4quwj72qP,N5-niDT9kڪEhF6s(_'#PW)-S9QVw2+VO޿]+{hf~ c6ԶV[j xOx{Jb*O-7/A=~?z?q?eBQb[f>R< GqM7䓢[-ޜr]|g8vj_6xд!Fk1׏_Z+גY|R这Uy&\?K^O|^GòBSfWp2W=sǔ&o鴯~;|DI56Ξ< q$m RmG Rd+]N϶]_`Z}/{g^k~YtY%Er>kxiS'FZ/y+3m[^O)~=}qax۟Ht~mO۟mu{໿ Q2}lH}~(C3CX[7FƠ?zV.~g:?'?|7|A>KI'O٤fUl)}y,a?3w'h9x 8ǏO>q^nN:w_ɰ?`٬O:GO{sןJڵis-ˢ7+27~;V?3 #z[g?בS(J֓'֑ۄJ[/FwDf/6l-ٸYS>^9 /\TX[IO=s>!-KMGH'OҹyYiv=jUi{7W~7=-~ xſı4Y_⮉._Ij푢W(mt+p[߁)Bow'Y}VSro?<+o_ge+ቬu;x[{GU2qe/sZƐ"xo 0Bgジ< ʹgv"[_Icin[(-4-0c<^wkK@s{4$2~޾+輗 .j/'>cHs|>/!o8?g"799<ᇅ~0ɦx}>4_ޙ&OlDoIj<2ņ'߯u Xgp` K$x^Y?쿈谜|s:^cݺc291y]\6/'_GNDŽ>5y9qll{m>5ye~"xu߳b3݁=sqXyFOH2/2 y? Ngx?ǥzʨz}~s|O |ao>>/x)n_wLʎ|m* Sd۟{yb?[xGOkm1.3z<6~_/ySqViN4?St9ϧy^WEїS??.sit0m9}ƩlYd:B$|JQxbzn-#͵#'? DJ Qc%-1iīJ\7D>rk-c ?6 Nix?W zԿ7ȶ[/j?~"x4eۑ 8F Nx_R4~2Z:E+mI8"f@%Ho-cmK ?.Nɨ|U[?eo *GpVw,ǮTLWQcSL=o+7맚>NZ?/yu<οyfkMZכ}G??p?Oa0=};{=ϋK{OzU?5~оd|aHmҚXf(NĂk~!|e4 ?~ gJV??*LW/^O|RKY dX4e!ur@/^zl4?5hX _n~ZR񕿵C|t ^?y?+S=miWK/_wߴ7<ߙ< Ia !dz J?iybl5տ^_*iQoT?A}-kes;v<=Nx_tlS'#?ag?zcKgƑ?%<VeZxzH#y׻=_I?#B»]?3l3iXcxd zj<~ -*Kdѿho DxMCGE2V~ d~FEЩ̵[/3\$?TSx_־mfosg1^5r!¿cso?~0m^? #?s޼Ǟ<'߄rJ+[/ ;xS93#ˍc`NJj m/fٶX8tRkLk\Vۗߥ:Lw/ͼZW̞lڼ_P񔶦ΐ7P'ެi|| u 1蟃:FZyp':՛~ΰl_=iiZ3˩x;JmO^?h?=~_|c6}kZ~?rΡxOS+>=?ջC yVU|#{DM^Zi*I2f\>VU*}jeo#幱+)삺/??դU1}* fU,j_4ğ-k4H}S=y^kX›Cȍzc{6*e3'ᝩlI~<>'"C? *_ZW+DJ3Z M%]v U.f'V\?g3|?xW;D"`]m>K,}?|_ J Ozm?|1 u֜/4I@;3!mw>8R`@U:OקPsy dX4O9ӗ33um1,|'Aw,T`[s@f?[}^/Q_x~i#PV&w`}sC^i|v|(nVO>;f?3fbOM%n6 AZbl5տ^_r{t}P<|qIhyY6묲h?oPi@?p^-{ק7$AMfGdAQ#RGSu>9[G.6>sմNwnz{ z˴Zn|_QO>?:lO5ᘖcoy.':iG7˔U?(n-.Nő>g6  iw*?I1ך8??u=X|Ͷ>B?s^Os-$|f|A.?Uqs,'?t}8/ @zewӯ52>((OlSŦ[[nbf]4,~Z6!m',6?!.omAHncA?5:Oý:mZKY?As2 §X_lxi8d /\[1_߯A^|4_9y}>9#OM:˚ck|n'{bW?Sx[O[m ~;xfmXغjP s#U*}BYZT^š~5+9${u<+mYg;Z<)ӗ"ۯ_CLy馌yuo_ǵ|UhL5?5}N9u^7O 3'jm_|~ Mmfac?-jU>ԑi$PyHͷh`ßf9>*3_Ÿ~eR_?Q+8 OP~x[P*N8ǽ{=LzN3mGVIˏ>9ܫ8Awr| i܅[r H8潞_/S&o忷ӻ$;vϯҿZ}fz}vfL)ujL?0.|ϥӏN{k>,4@ꪘ~% , IQ3 '{y.5^)۫uw={sy۩WHc1j0pe#מvN4{y/JڷYsX܌ msϵ6R&KM ':_quS-ܿiWs#1=>lo&l.7nH׵Ccʭz1/h~+@?~"p39,Ca\ [X^^G||Ho\ T5$n0;_EǵHyۿҶ2ݙU%e?T/ijG??ec+"Gغz^7 Pc L>4?>(g?&o#wn>uql{?? ur#N_kZbwkpu}{UaNy1\?Zn 8ϖNwjGͽݺ =҃k qnt1w`3ح5#ُ杀.|jyǾ:׏t;Jk5tp832l KKɼ0/.ӳ{]̟:8oÞd ԭ=u<:n ' Ӛ_ځo_jhupT]1϶zWiě2@׵gێ8^gZV50}o[q".O ouӯqcg'V___Bh/ƒ`-Ai>qc'2zױ_f;g0?,_Eu2/˜QjE85w2qw?jaۿ^U9ba}]_k j2?c}~~Y_<;O/?'2u9O^zu&O7Գ}\ז|(iҦQ G|n}!#=Nrpx1sy[Z8qSEL|#cCϧMĖ_2?1?z_=qw?#?7:$\k?3zcR^{{˿hE _X8ρ>2}zck{L<-?b`4/{וBA!T"8A;"98{k|?Ykj yUI{{:Z\ֿ$FljG=H۟쿇^M{Y@7ijº}czٺ?sDZ?պ`F?ϥy?.$?~W1WAa9YfbKWM{&zRıݧӯK]#H^=L}u'Lw?]K?70:Q-d/^IsK{oYy/ƛKLR >'h8(:3k-=5/y)y*s!9oumU;D9޽xq^mu/i?|ٌ[Ww*%[{R/yVt׸sq&N|U^hE,>҆KTa+5E̿-Kq5m_?޾z$? ͟ ꚣgZZ>B|KZ|դi?<~cio$eh02?zK[s ~  ?8| air ȧ?re@֓1;QwZM}+*?Y*bamY[_O쯌2uz:_9wc+'MVWE}3y"^.R Ha N&ıEY>ZSuh|WDZܾhc#Aw[?$jiʏkC3Ӡ˯Ozsx*t /)_<$WNn4BWc_<:=ZMDYӭ6s-W@ƛ|ǎkjocC3_x7Wf9/fS)9\ߥx+Y7-ģWt-];yMnzuJ~I}/CچdQenz_?<NJG}3t:s\㧃xҾ2|]g\o:_qѰ@`ԭ'α;Ʈ@{Z*KI,U#?{e/`Bɼz(f˯_5xNzdQiZ2VLU.UTy0f(֭~:x8Mgit5qrrKY;* 5?tsrsC`cϲud;~'Z'/ִ{<:3?:߾[R_zY:wOs](>.Ɨcog :7?"ZyeNlo?uM w!rv+0M:? i=sTﷷ^iZqga6J?PWU?B^:r1ϥ(N^_'C3έY_y} {?~ Xͧ|Mil xbg;њ?i1x4DܤFɆF1w_&Rona?&M^o90OAżvv)N\?P08d{q7@ڠe<ʿ߼gj.Bn~?("5?υeCŭ^I#HԎH"t$kL] N.O=?]ÑۯƏ %s Kq''eǮ>_YLq55+o=F=N@p[Lg_[tmStGM̱b{/j?ֽ}yd^|ON\x'(5˜t*)c8 &-MmB;A[-9%4*p1li_@mn?Dpp4Hp.3c>ko?iK`6sx?JrT_ߏ<}.%o0|V#_? [y/Zo_ .O;YD#C;yO.'ĮB\vU ~qğSG<8'?3kp_/ʺx3: 6,oY[|'MÛnK>?DiKp37`=8Ui%Kp#:s:s~b+oC?6ް>?zo6}8ѵAW޽z3Ni~n_4|/EAY~(x6-/4K**܄@e*m4LMlN_|sNƛ1Aho0Mt}Ms 7S zͽ}#6GT8<qСiڗׯnN}<#!7O'q7냎>-cQ$|'+[Kx* yS|.̮VY0`C7 Ա;ͽ|aAM.1x0s=_zURї.gyV/^4?N5k$ʝWOݏ =>%gIG.1 Ӧ;FOZԾ,at]Wf-|g|g?)o>egd[-ƞ[>1'Eկ)W8Gjѯo_tc~4;_ӳB!N>->[xY䟈;>yA}M&+!ͭC^> gԿ>Gk7q'| u!da7&Z\Eb1/˷EC:69zڽKݖˤ?#xFIՇ*}??ڙc}ȼ}K\C~*% |wU_ Yn/Cԯ#.Os4jH,gХS,MU%;hۓVI?$oDV>CLxLyӔӧEMΥGBm)[1m~ <3i^1͍4!_Gx]ߌGmc [̾K6buXn:aip8^Τ9!IQ ZZ3R 0MEԕ #5]yIh]jvOPi[RH Ce!6͵ݞzW+VO*qlc`1ۦOZ]u?߮(Ӽa<%k>|/k7RѴI-^|؅3Iퟂ>jF/VoNVG/ŦĽi!]IU86ܨ&8wg>ƯsOv:tmui L8Ħ_5Soˆ1@7f?衸KY^s2@FY<ط%͏p\PGioamόKS? sqܯ*2N?TT>e3e3b<8}cAG[H#F4BjHbFЊň@Xz IбjWl*~gW n[Z^ڞԾ?y~gȴ}{cbZ #Nur=UYhG66xs\zq6_MJ6<1/g&=Ϩl?#eOPZXxw72u$X+geP9bR̽>1NW=O2F~:=~Ee[T>꺯O/3_Nc:2Hb]3[b'?ጨ^0-S^3_\/toO8~4;ml%-K$P[86fu6 oFI8~^8DZIϊ4 R3A@⦆?3W^Ox6?4Ϭz/S垿oS2ͥ?UH+v?.kr?8>ON8o f_ٱ:P>M{RM#_\}}c[,:3t:%q..} *?vMgO¹l-oo(Yg% h,{6'ȭ[eٚNb[C5 -ou۞oCXQ @Jxn'#ߦ4ُ-00>-}H͏o9]~dF|u?xqW8}|߲l5J%߻<7c̙z׉|Je{_m?g?O~&em{|V7g \ٴb &i2̬˂ğT= #]~û9ǵxG-B#klZ4+}P c'>^y'iש_ez OS}H_;D+e]pA ac*$ ӯd'̿P {A+m =>qozΫogZ!_U&3c܁RjF{vuX]_>(s>zW q;!f^o_Sʺ[zO_o/x(x@MGִh:F4\Y픔eR|~z|_> jm_OBwM &|S֜ŬrScRO_%FxǍ+/'/.i_\x5kzնe6Mk _(*L _Ee 9el֭( 7)UjRr!RT)ޔ_YWm]BPa9FnsSBrQ:qNR)JOi|+V~_u_3 }.S3~.xn1mcZ~!ේ8orOoS_mg{wT~xs?iAeo|*]x/5O:cB؎kHsD¿MdW-Bn|]?~i"_oZsue5׶ 6M3q3Q8U>~wgUľ;6NUn+.΁ 卭e/&OاjeO(G_~f??{\ou˞:Stا}4M$ &Ж/OD:f @i8S]vx:kh;wx@wwϑTz6Gljۨxt?0yiy|xQ_r!ݟ c^Αiծ/sij-|ri&66;O y+X',Age'v݋Cծ&2y pIΡs[ W S? l%OƜ'oJnoXEעTw@ x#ZP9Tg%rCd?dKpw~ on.߷$ˑsy~v7>YmtKÿԬ,M烮hŬg c33 n$>DSXg%·Dٳ/LG;x+ߍ:-I~ϲY0Ԥ<3:cq:=9br6Hv_r ~RxO젆L-C|5OlM۹kqM?QaoD?O젫QtxIS7`pkvi?=CNGymvs6ŵ=woXS6|q1:^S$G$;/]wi~?eNO#~|%er/t˫vx %T'x9Aȧ4اM$ %44U (Y?H4YwJ9!}SBa'g1K,4>"7? ۽>Jzc=lxgah_Ѿ |&>vV {]E lpL$kú*+]9U?|J|1?{Fܟos_ F5emڑAcpdQ7PQDQ+͈ܰH e/ wX))ΑoGOQ q8az׾j 0k=_ؾ<;Ϡk٧ඍy03'/M~%iZe᎛tV!YH;06+?"ŭL-uBI)8Ǡ钍^p\ 0o2Wi?wO%̰{ͱ w?=kF2|Fԕ4<:r0>ׇ hCgdQ r7kD:iub7olF 9 2x h&~Vf`aQKӢm%;@$Ĵ!B\" ԪW}˿{aV{_hpjs OV3 ^s3ZѶհ[%6=q+fnB%?ᬾ)ssAo˞lnn$*eT^#H@v#Mr:.W׷)arGWycF*߃N`T gGsǷ>"eJ/u+|$ZߴX? _Pԟ e[|M2C%ү1Dd[2˟gӾ ߴEӜ~) 5 \/*#Hm W#.~}َ/uczp_ݗZΞ(7},^KW/knE(|L|l7^$NmT;D Kn+IYۏwuߗx_Fh%~%։=y|{n$XYvUUXjf4pzɪ.?}cZxuJ@Xft`{j]CZ>u>#){O4g:nJ[v[_&=mQgIF $A< }9KSv_oĚ|Cx$$O  _Ow/a˹c]yYςtp|D/i0<㯇fF7o7mlm>y2Fo*=[[0zWğ ~ >'~ѓʿ~&j(W̢9Sh4bc~)Q%yV_tG0[-Oe?I$rI0_S׼?uVez{3mo#Lt@r~\g3 u漃VdiF Ξh?fK6h8&|HL^&oL!`~,~H'p>`zmAe=BO=MMֆ_5.l$w.\qkkxEVOaf~5H'0_ERوnp2&ϣ|G2YcFLVr%*.;7_;~(1H%?1\(%Knp xO_f#~Ib$:# ǀ~6d?>wOM=7h#8VG2K4$&RHD)& bN.C/ɟG;P/U`ÀAJke<8o<δOw?uJ==Ɲ/q3(O6ƢoD*``t2YbU`H@+|* ڷⴣŐ3 rd ?ddLT;B#".kJsd?Lv_sP|PoߵOow7~mIZ[io!pl(]LPf{?ge:D Zb#sƲ T } 'D t9 ֓#?Ŝw5oƝygWĶӁᏍXo/r|09e3)0TlA'??kS'o,vxnݾ^';Nݻ~Z 'E_j+ jQ/^ O&UYP<,jU\Ȩ?GCB1И+ʬMUO/aq橵o0T`1SġaF?~NqqgE/\Fm,QH߶Ƣa$dDgRy,p0Gu77kh?>5fn~P.I` ?j i~┲n KxT8JQT6۵|HM֗|O5+|3$p9_~{'“r~jc 3_G1$Ql/@ %Cz'9*k<4HƤ.'[B  9 /D_چm;N3ۧ~Ֆf1zIƘ#V:~+oNe?Ayx5镳Kz#l̪([ٷcm?@x qpӯ$2?:T?dO-TG>NNGҼ3bܝAm.u5֙/T`iox,ߢ|}#w'^6i4_ KFWš.} ^I8QAUu[ۈ[o>Wg7kLӖIjx^l^4UcN^w5GVc8׶in7ĶvbEӸZ䔡[9:m_🇢Co G w|To7v󺽿L[#ILxӟ5,o߇ztκQ 1-}|#qml`?[vwYixxxcRbuvc}v!ekWd#%\ԊVh ^>gb#_wG_3@'a~+[M=6F]x(3ggO?c(_gL 3qWq̬?$+qSB {$ H/^b4>ls5nԈ7ڮLUo^qqj cLwq]˯_CEf}>^%7zFtuPQN?{J۱BPu];vG[Bt~5"}|6 ?^8}ϟm{ 첺n7/ GtӦ'Ӥя}~ '> LÃo7d;GIڤ1{7l[v/ImN7͟]5\~OmU;oSq_Մ78ԁ4bq9}6k>6-kxԶŒֹ1)m^axkm2]LO S?u|8%OQ^75蕿 }5<3)ٯ?YX9C_{:?Ab4.aORQZ'oh_MGFgk۳Č(CBmw?bM*yaoqK~| ⫴q 'DQ-Ealg>N!8?_gߋß ,Oܯ_?|Ol$wx[$xv~$a៽O| _9΋x|9c?z`Cl_6@n|GO Sߋ(2gs_:T{+g^ܼ'!UX3mPy8$>U??;u77~c_?6fkE|ݰH]n[O784yͿ 5_7狾_vOq׶i6p.4cf?<^U,Oې8lwſJ|+[nGm=v󺾇Q75ghb?D9; Nk_ZD:Mc9P3y_^f_a b~Ѵiͯ_v¼{x'YF5A$KYkg\|GoW)?_5l[i_H5;C@g/s}=o5dHI{<{rO}Vru xgQ7v.YXt^i&#~x uܳrݟ2D փ}¥C+ݟM'ޯ+.T~Ş*nmϙ<;-?vߌ 92uLA ΕkI??IbY dP|Uj-]Ցz// b RK {5okg`*O*0ZsԾ~Ѣ-`?߅[MT?ce66p)˺|-)ΌtFf| ķ<9+Z-J~x{tzJl#<;_=k'$1jZũ#?exo=u1Cf $ǵ|<AͣN6>)CuKmTsK!2#yBCyHZ?2R<E&p7^7JtDNk/eHxrcߊQd;p ڟSwc? y}gOz_b?>ks~yJ 弼`?s|;; u̟pN?Fcq% `6@G#>ї?8{t6.&ޏdž>"FfFq=9GoRj?v> h&#[Fh$?gts7j͞]Ǚ7S,`$^W}NyF4>4!J+{g>>7be0[p߆~屝}M`h4ٜ鄓 "Ƥ{snһ1ڊ~cUUb?/?h߰[l!y\cOOW?h(6?DS'۪xsZfC$MD""PӭEEqdž3"Zf1޼#⍄ j*A~Oxy^?q..ws5xmGi Լ1r\j}/ [^7n7A'psė?h>߅uGt3㏛Hn{fۆt k \ĚcyJPm(7 xK|FgqtYs'&d%#G|6?h҂O5@FG[B/tY><6 ddEHp,GXiY17FDž>#Շ>LSysꭘϤiSGx#Ch֌y É77x9>!Jk? &OPG%6hDl(cmpeYà.q:Nik3ZmDW-Nv_Otա/^ea3JG?cW)i_Zi?ρemnbFͭў(e73%Di|D*5>R5|*>*XkLNWM$; ǰ߼yi6 Ȯ ;ɾr_'Do050?1"s}Ih/ăNvCx!ADŽ4 Gjwɒw`ܾ&ϟC-/_;D>({ C)xsF8RL!fCd?~:v~2|O@u#S"b{x̳?趠>Ӕ,+> Cm"_ uew|7N+Ho|e-r0>h6i]fVm _`(,-_pQ6Y?.sS4_w+|w(clE`r?E.m#v`x3<uzg{Ѵ[S>ӛ7|l^\j9Ly@_ ~ON5$C6yasr?,Gtf2!&o{խmgkh2!y|̈́` ^lߵoT )dCc/Cgɂ4]NvI> I> X| jZ6}`W\cS/~91*+yCa9 W[xKW/=ϑ|9yj~#)&dI*Πe\y?;2fό5ƣ ?wހ>qŸڻ> {a7 Y|H`ޟem? YkJ-ޭcħ:ݬ |N(5OO>#@]}o&FYlk5?iƒ#PI5l}|+(moԑtKY?b8|!3 L$RqZSus ۤ*/|Rb7| @6~վ!Chzdr,7ȩq5kq?̼-ż.hZ,=|l/~?.7}cP3̓#pwwM{ݿ.* _LJ:ŭ5o˶e `rzxѾ%GmcDIh7nt|JC]ʨV;d*ĬUx˿Z'(K^Gן*6./jTKI:?N3n魧nZ>༏u\n6wxRT.wh?liq z1QR@%ΖcS]ԝ0F|َ+1?~$mo1u6;[Xi]% ErMh(6.zcq &n?i帥rS^GOg+n<n~\nb~}׳۵]^OTn7s?;W<;k[@Kf]s) ޝyKOxvLBżE'Bx8 MbU oB4mh7'/?tgzs^`l5Mwd qk~7x/H[߂y2̿$?s >JhIp>\JYQǔ ;A/4ew4/wWx:jn?:eOyv{~nk/:()^cw~n_wŒv$ Ļ3s5QC 7A>\:)ڻ@zǖIɕڼώ| ]oŽ~>_ DY/#l ֌6t/,Ŭu/z?nLf7|' +E"~շ_Sky>&~ g fͫw8~j~hk/ػUwR-?ejR4 d33X\xzK2Udsl36 ?Li&ĂO5Ī <3C61??_>F|]K>+l+xgfWwd/zw}/c?txw|ٷU?U}xG63+}*X^b~q'$W|DAVGoRw|~V\]òîb"K!+ Bꨲ"I4s%|9\w_S4w<$4#;nɾ?7y8\n<-_7/WBA?xgF ?leGWs/>\|3MIΝϾ c1C5b_ ~K+A[տwSro+oq63e,c37 O!:m^_ >0~~.~xrK;M&oP-k>0Jd]T/,?h _b`9/'/Np }d8 \&wيC_Y,5>\1 ǔglhlg lǟm/{#[8sڼNHiY.|$˨o?Q/*i2[_ ?qԊP> AJ'wJxWnι&'J}#zn,u-nHk.I۳WKu_ݚE|.nm?A'Kv@ѽOJyb#2<ڟ (Y,?{$򱑁7o f#ۿmQw۟L(ٙO*/oFwcgkt?o+}m|8mO> Fɲx?ů5k6z}_O _JPB ɟw7m@/g|I?𴓲ZH%@9"WN/_ֹaZ]_u]a y:{& 3לw3L$+CxL?TZ ķz*u%XJ JvS⏛+F?O=sZf?{z宴}xU< cW~N˱(.G_Voh5o˩?0gvH9Md|<{0O$E+G:/ml|,987UP-<ϛKctBqo^Yd7%>(^|/dY.qێCWZP.,Z9@OۏBٿ{ŜxG-UڎP4/o|&H2=O5M?{ټJ|NsZ:sWǟ//a|W'o&I֢O;wE}eg)YnN/mưDOuHhyftyu a~KWp?*Q}|nJ5Bm!">_xfǞ~\^)ӯeUOQFN|c;~\Z_\!X.~O_Gߎ;7o7mlm>,n4To6xM-c ᳀&3GOak@8|ʂ}^%^(JV< cWEXT>]hG:֧ENꦍ|8GF:e&SZ?ٙ@5?f?r9??o~:zbF'o:*NHS<7t^Ag-/گ,^p|KIŵ)'k9_\]"~z/^kE Ȗ@c|K(i.] LFakL~H'p>lbMF?_D^Q*~qj2/X(Zop2+wV0KSb7H|u d$/l'67Ğ!p|b2SDTbF|3x-(%d!ƫ|i.7 }MZ7:CY?;GFHӳd}?g]F|eUԭv66/Tm)(I&l ' ?B]N[.Crs3x"fґ/UAAvV&7w:1,s*=Vk CS}U`s~w>Jф%N_%(M;D$ՙ g?اiAҾ}UܟڻⴧPԤpO[Q|傕%'FծiW'-Ɲ~\ "D2~ye1?Ŝw5gƭrYi/Q8'r>xΔ\8|I⾘}V2 u<}0qo·FvJNu'+>FR|3./9>d"l&|mO|C .Д272~# Tڷd>-5=w ^Pqcؿ'8 sGuk'Hu8h$o1#Nɒ1O֥u[@u8Z?:Kчm;3A >-K&4~;ćU o4ldͩ{W)c\8oV{uKWc_4[4H G~JnEg ZVro4j^n~ߟtw`'ϏtKþ"r#a1hɇ\b/\}ߚ'kՈ&~ž(_ݙw!P PGwGծKx=~D2nf 7 ]q_ F;lhj8س nCbaa1H2I i;_鴏Me–Pgv n-"J4m$F1s Y?MT߳wE{JP m#=*c 9Y |[zO~M~V 4c)z$$_*|Fi?x5_ڻ| #6%<'Z>ϴ߶Zƭ55(/%fe To.j!9ɴ_WZO:kVeSTj0(C%"ƍ, oTt&gH<ݍ˟'UAΗ[2mSğ~7ho!vGĊ̫ Y h/~?o_= <xMľ-vd;h >9!Wt7zͧ|!SZ{XvkW|*bᡀpՄZ2Uo~Y){4~e.d\ZiYv' K,<ZqqS]8J.2StⒿ22N2RQ<hocG+noM+n}ӿh 4pnB~Ϳ Ƒg$\ |ߴ_?)C| g?hό}/J_ZA:|VkxTU}4_o{G4 13( |=ZqsN$UJa%(NINVT0ekҋF:Pzu# FpjRJ5)VTa߃b>#_>qB]c4^ҍ3]%>B%`ҿOmA#nAdžu<Г^Twcs 9>zk>9A_3ϳŶ>\4 L#6?ԸW:Go5kÇ-Cx#=69@~>lľ&{]|ogOIo=ypPͥ h1XQYk o^h˿?f~ݏ1ּ╥|z?C_g:L3/ 65g8W.?啰+R Oyhj?Kx+6u@wI`6tA݀&n\A oEai8;pCN}Y x%υWb>ߑ/JP3bj0ҿO|.U![._Oy^|{iQE|oV_/~?,#uҕK:I7E=I<7I!?fߎN&-$';p6~tUX:{Uj}/=N?htX%wW,zq#$6mo_<w+╚hKoq;|jW^"LOxL;՜}itg't(<#BGü |+5\'M;/ZAJ gfHO Wv5_Iq_'6< ~>DI>-wӿeUX>5Վ|BV{Vh 5ڃ/-tt)o?U𖆠e(z~xL$CAuL?sg/͟=>/#Bt>)߲w-mk󡂧%P{]?c|]GhYjh{H~1[D. c`A bbPxI'/S01|3 Ps_;~κuPik)ܭ^9  'Q%<?ELup?ic!?nwڇJ΅K>Ycii\c2Y啔}U?`q\;)<3FoƓ3S~ 5Yi6|aJxڏn&E~ me* e2[Ǣ1y lA>}iNKWNYOQz‘ޏ{QY*.᫾+DtF0~ cHoCG/1?CUٵ:fwi <N6j; |)?j+͓JdWhٷw;3篝1^~_gῌ):.vL.>x.(<.4Qw(We#QDJ:DX<~o >0nےunW_ti_1c<3Sᗂ)ohk_<Ťh_cKt|`IpT>^໓tC_~RDo-4y 80dڍ`?lg~4|?g)Ex"m&ueUZ(G/ʤ'ĩ0i~P&_?R ~~~W¹~)[Si $|_G閞Kok绖r+E~| t/W1Z<'sf?0#ʿϴ6?PO/:mt_ +O=&o.2Z'D W'Ŧ8>oWWR, ,t}KOv,p&`@d;:??:쥨~ߴ/~|#_^}OZ~j]LV92M@G1Jb17QV򥉌JQ|TM&z`c%bFnite87OʓJI6׫~ -~>k? >|b Gzszh}Ũmz(3WмLu7Iixkv!$k662hI:K"CLñ]o(0iڜhZfY][=voulbdeȀH|nUiSGJ/SI՗-'5'tj6cb  4p :4ak)VVuf6JRNJ@~MĿR,oC6[p< 0\{PNW8Ҥ?cOjm DSE̖3^ڞyp |_BOto<FkMc]WҼL ڵψ$QH&f'rfϳ`|Z/xƯhz]V |.?M|=m%' _^ #&C0^xFcdPQIMrU[mY]ǫP\ x(' 9^%QrSfۋM WwhߦұNlDž>@猓Ӑ+POv-=C½6SUv7wau8c/o\;ܹ(84's6+Gi,%Jxv %x0H&uuW_8{%V.8 C!.k+9.@G!K/q޼#⎒W1ADG(/ ;V< F?kٳIMm@{zoz^9 j?k:Q<F.WPּ2z{> ͞*\SqqoqY9uq ZܳUO^]G TjoF?v!Ð}J3B@phR3yzo#>pN3_ jQ'|k٭֖ I>a>Xr|CRG5Ok`nR5o +'!$qT~ot8sc oCx'G')3:pc?GN͟˝ܴ?@ &OyW(7KJѿjR/`-,gaB[p[u LLXɯ`;V{֡o4rڿ^甓,RF&ztuy˪[x)E`1Os_>ѴcȎbaz8rr:+5%~ZaA>$|ᯉܑN߼֗w?byqUyf0a|l;7zPV-3FjYԾFk>[˹ es#]O!@ϙ*.2@3ŊĿ__+ üH,"5pt'n?%}7(?Oy:o}7%c_TkuĪ|li *D( Gn Y_L ǿUf~:33+þ$um_gW/ZFyit݉-"'?0"૟J١pm L/kxB–)]_2K~?H7&Sd;UZ_*do/Y%@ \M_ϜsLJҌ쌁o7H¬>(n',mHeF+$V8>Sc)1$iG`w@?Cv!M¨Yր#'Cqۖ=>`+ .[g=>@o+dN_Zß j+nab\m;lVُ?d8ظMğg7g@ou |x!_o}5R]dV:tk6_(oØM Mj.WTH %D{ sxd7'|L'G ~Ş8kCoxJ|b/úwKr.-Qo9~˩BK3]jA3G2kQm'ʵoG%hڕ,uEKi5ɻHW<۔FZkjZ-:7W,9OҲ*.YۅQG/%obHfe~G=RmuY|#;8o7UK|\<7 EL{fGaxş _O jR֡A+mX$jrd"Cek[r ~𾙥C'NxS#jk~@r!Y ?Zaan|!k- &cWQ/ H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-gAMA|Q cHRMz%u0`:o_FIDATx_lǿGAS MKdc,{ٲ$"? 1HV-J.öX "-Ev") I5>ɵۇn$Oe;ci3N<(w3F!iD)%qx99p! Ŀ ((CPf۶ݣcö8RfXIH?~'pyw3wuG1D:44D"'2@vAͼ>"wth40<|mö-Xm e3o佪l4d$ Àx"(`+ `@6(Beb1 *64M k++2~5K~0,˂iY,eMll竟#3*dYFTB, ir4 H2|;BpEQD6E&([^PUUr9:O> 2a&SOyFt333陠TU8.]x9=,<>\IPב緅 tV fk![1tNmnф,{㍇OӐeFn&8|QCp+Jp.J ky9dwӤ}kTB6R{]*.^)2P333Uv=_ڃK<< .d;ȍFdG###lbdqp --dY½$QRq-`q!om"v6OUU7-ojT* N&at](HRHR( [MOO읯ʝJ@]@ȣ= BZX躎T*:t]G&(=uKKKl6eV+X ! t2Nw>B;z(kjZl;hsrUNOC<i=Vp5MC<K\-A1h|5! -uKǾ>įC$PfnPӈD"͌ood025,--|#GB:0z_k02JaPx.קw4 A$HQ:M?=l޾{.:…Y (6(trrv:-v::99IEQdN:I>6袱@_Gg?ˊ Apx =o.M²,ܽ[k?bw3ԍf"y"?*AFh4J˿/SA. ?7PJ-}i S ^+lBTqcƶgH&^ LMe*'a"N4 7"x'iqiy N|ck*-훷+iSJ%|Ł(Lz IENDB`openimageio-1.7.17~dfsg0.orig/src/doc/figures/tahoe-small.jpg0000644000175000017500000015205113151711064022241 0ustar mfvmfvJFIFExifII* HTC (1i%T-Mobile G1HHOpenImageIO 1.5.0dev : ../../../build/macosx/src/oiiotool/oiiotool ../../../../oiio-images/tahoe-gps.jpg --colorconvert sRGB linear --resize 320x240 --colorconvert linear sRGB -o tahoe-small.jpg2009:02:21 08:32:042009:02:21 08:32:04' xqd8!WGS-841915:08:08../../../build/macosx/src/oiiotool/oiiotool ../../../../oiio-images/tahoe-gps.jpg --colorconvert sRGB linear --resize 320x240 --colorconvert linear sRGB -o tahoe-small.jpg 0220ey0100 NW Photoshop 3.08BIMAOpenImageIO 1.5.0dev : ../../../build/macosx/src/oiiotool/oiiotool ../../../../oiio-images/tahoe-gps.jpg --colorconvert sRGB linear --resize 320x240 --colorconvert linear sRGB -o tahoe-small.jpgCC@" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?0FP6s\ Y㸻fƳ.4lfȓ 9EEd%umAO-e9Vdxr#63[9,\-ac<ϖ_qgWrJC}}[m$Z[_:˖;ǻikkwlg`g) g#18W|zkiuwph7v{9a7[$7T{(t@ڮ07av9oja 1N.QzI$oy>e;nmB3,M{ZU.Y>/]5%flƬ:3;A˫_D.-ռ6%8cڻ]6Rm[^>[X7Z=o{!Duh+ . x-_Z&Udm%m4BݒjI+:O:q~!̘;O ->TY<>lVYP݀+h.5o/X ysk,,1L!och凅}%ZwwunOF&XgםG~|4 XM9 Y-ooe+H kGÖ<'`n/%8=[VT23~҈ C*G9_k\rRv\:i5`֚kkbrq}ɴA8sPZ~c#GifN .CqסyB/Fuҥ$] nG6r >[icoŷ=?ƙki,~Q!xx1 ҏ=4a{/6^׫Fs:m6+FӜ`c%A<*o+Z;(p[~0 ޵I>ZEer3`um29&QiZ CGR khq߿Jod~" (s²)78JDgx[۱*X *kR p8? jk|Z=JZn3203ZpZCs^E|]tח9N6 \mnoL*h-#1ZZtc#b1I:Ď?lB3*ͽ8~mcʝ9=}+"NVUluioqŝ%~zWǷzlcSI'̃Eoٶ5q2XOVq)|'5R8/4R>M(im$N g9HZE$Fh ]F8WSzV-Vx~H^+My?r#%G,yۂk/º}= ݬuǸ8ڱ!r0]YAĕrPVJp|󊕣$]~QK䔳V45(ME4MZeqʎ@di[Ab cNxqﭣiV34S1${fkO׬mu E$7kn cmw]:hyq}] 7eAta|o'7 $h: Yo&u#$3Cߔl䁔:#U2*$11]f8ksnyJ[FU NQbRI+շ[>/L Bp&]_oíkE-/&.lV?5dIR"4 2Yy..*gLMusr`[y1fi bN dο#5o?t۸4I6XR&#0~izsA\c.d=EA_ڿGnQ3(B͵%Ri5k} c^]6nwe](ka jClϷSl 2@݆Sի u`Nw&Nc['o}b[8Xm󑍤 sX-l 3~{4 G?3`?:ո-S]{`m[ݴAL*#۾۷ё?Ì~?JoEN3{,BP޺[X"̐:`%@rx^u=X8&zt=v``7L*@NApX9Twi+_3„WiG5n䷸N>%o7Zm9`,gE'da׵FFTӥVCIVJu_&%|5gKv_֏~-d^qmXuV6:VVǨIs7{2*Cj@0>S+Z @%Xufscl[Z1^}lG*ѝCa¯p3Zpi[2^?n g'z?lAk&1Q^5|\ԧM46F;G8ZYIEHN)T}r;S;_ts,_S/Bi8ǚQx.i|)Z~4xFO.+OjL,M3%" bHH7"w|o?EHn\["J~|McaX4thmbA D/QIsBKroHԌRV[OI>H{a^EX푙ʛNLjʌI[TX,ĐP3q,HUZDL(8w{q7ÛO~,}9\YhV)]&х'HDm^c?5^~FwjwZarkE mJ?_ p*X X)׏49FQU.یcuܔe:n$[Z\e%'h6O}Y/[ yO`5{"Ud>4Xjm .#VR`0[2 ) ~|_\g9|y` Ӷ_{=mf{/Ÿۄq76⤥8Esn^vO'?_mitиJ} ?[vsg?5)O|M}[@ 8͵J=T0cR[6`##Zݷl(JJ)1rֿ-8#}*1$rO8۸ 1!ssU-7t dpqMmlI.8?,ۀ8}fv0iQ7L ;K|q*qy xjHtVMcyB(ZhۻiGP͟an|^cs⧄ZhE{~R4J9&>V:e68ͻ[N_5k;}&E[! 'RI_^4VՏ/m& ^VKCq^iMos#f'۹meۈ5⿳֑&"P _ۻvxZݜrr~**t崔&լwKtq*꺰qNo}5zR~oo'g_™GCzVżNq(d9-PCAk 6b+7}NI-b+~SxAXmq~lcѹ:b | dҼJs =X'dz~,:#4q$Vݽ;sN1VOSڹ <H/>ri< ySmQ}ⷭ9z~qݝ+ڞ:qߴ`@<2jͭq淭?tq]|+6m.dZP9>k[R:pvںk[C8<j|n3sҥ%vʁmٜYTl* QWk c#59$=%6v4Pg=s =:Xg5lXqzb Ne#7x癮6-KSrXn!$K){"3c8j?7\K-en:,m HG0*I5Oך RZm WpM˽@'nR@%uϸr|uZx,b߾d͢vn1mlge\UfXT"$*~]&%\1/[Qsm3M#3)b7pp=jaX 6NõúEK̅W} o[OH%Yn3o2>X9`@ϾeF^e vXuUVRnI+ݶފm%Mе$ﲲ_^/4-;ÿV7 3+O)Ȓo$,LPt\Y?g6?kQ诣jig=S5ݴ $a)෗`)|ii$R HmV:eye#A߱+Wo\YRj:5'mZ9ūeQ%DopxC"LxͪgKO$tջ?%{[2zNm}t)I>owJ]>Of戾6=RWu xt4Phu@ٱF #-ש_ʿ>|rCxCǺ.gOhvY4HSKP!WyZ42ϸG>%xamlȃJE-@-\[K)qӂWIS4UKǕ3w]߻.!rRx>*>Y:~2dvJok{qӎ9<Ҷ AiR[¼I8+#]xNS!NJ[_cŽOZن qNN}ho{~mng֯]p~ozX䍣lЮ8l8''+j a!ǿ9<ׇĮdI_1mRS81oncMݸg5mlGc51\TuLcN+v۞?Z- qd;[#ھwJ$V r1ܶ$uOmbp㞧t@=1_9E^pR[39kRx?Ju ?Ƿ[XMsqseekąO2R@PYg9c緄(tSu_xMu;_Hll⾸ҥF)FxTCf[O`L~dSZhK<3C]y4UK Їrŷ4eT)ne迩|* eNAܽe.R W䕓I72O{a֏_Ίɣ3,HR;q(8\o|/{Y-Ju`V ԌG!'zTM[C&V!(֙~Eœ2:Ɗ]~>t ݐo៊k:tB;?4 m$^Z6΄wǖb1qmB֕ZE5%vջE{9hZ:yp^yd4X<=ue4mb@m  *O RK;[$ziv_͸1:9=7o\%s-┥mۖ. -ƚgoWԼ5b.R[җKm82vJM_D_ j>I)kԳQYQ$I`WWx'J'jw,KK:OI,t{f8o6V eH!hW^+ס%{k+OOhI4; 2|59Drl^Y_Y{Zt8)rB/NdJVv2gSN;3g|/Z c丷I|DFbƾg Igׇm&[8)y)2P8`낡l2Y 6a\k8r,\Y.e{RcL2RfۊI9)-v֗1x:ϊnBYRE0"$:Yw12ȼ-7я3E׵?E^[x9X[Y[iFY)m`e~E;7q?jهO`'$v=M|7ع*F~Z=o%&&IY} ǎ=юԥgm9cVM+ވc(%|K˪ D~>^yu|ETTfr# LHR$ dZf[sUt E4x6[vgrzڇMTVfG>8o'MCᾯsuv2h-rJ"xI;{쉠^=F{{JmSi27#b#,G$a-| bjKGR+\񄹥5%/Gl5Ԛ_vip8sENq匦yw&|*NqO#FOx`;4`'kC@'wU3?)-yR>4SxSGkV^+|a 9] (㜏bB+DW)p$eOa;oÖk6:x.ᡞpUi*zw?j+AAgi62xSuI}d- f+9?.0'sK5&!u/Iha>MWö ĒB28PJ$ Lcd>tSMϫ$cj:@.,V̑Hf\n!db^.OxFiV2wSwc(^-EjgVg7- L -8)F\NV峍Dgt~H"ydbi,q"wv=}m]?kx[ჟx[O|Cc-.KMQVpl\(nOT!vY+Ə~+m̋Wd ߎ_>+x)ee~.x(? i4ۼipdtJP$_Ꮙu '.MCi4VE .`An˓{4qȿp~|oCoAu]jVIoe$ϦM;E$c x!Q_F5kBPKfiEBM4y […i97*o׵Kfܐ>V>8?ֶm9EKHahWpjzFc}jYLܤzGj`-/CGjG籢.Yٔ!s^}3[2}VJ[xRx8}p̙mhH͛j=* 9MoAL(b& V9RKvf[+_ c ISZB x8dSe ; x@sԞ+v @5@9{VmUҾ0Z4%gg-#U0[0zc_=7{ K`3joCJOxY60kh>˼v1P7 m 3 #U$cUf>dYОzTj("j]YM˨c"$S:HxlWB/Y+{N/TkTO{EWK>ݺ4yokmK/ci7Z[Yux[P,Vj !Yǝer2ل3F?Oď~)~(ou,ZzW47q\BĎZ51Tޮe!+5/ WZn\kP֛AqQϧ57[XyPѮnEg? k=O}VOV]Mf)J̾Dh1y`P(,QSΰ5pʜ}^Iji٫>)9][%WNCXеI ӎ81iӴ"zs_'_ѵ\~#=B_ "iZ97ʸqmAV<>%JFk$iZv-6Yc$d'#.uFڤ"ǂ{y?y tubXį?t[ȵ))fU\]_i;_]촒puOK=tڄk=p \20X Y Fy 'Ե@iZ6 b"'akן{ CJM ʿ1k>T_+cW>,u2W2DmQʅa''^+ִkIm5C]ܼw5ܳcTeyj'x"ASsJ3r ɧJM)'fzIw_8>tkN 2Λu9Z){:ĽM{4Ӵ%G^]>ZvH;fu`QJz ON5ۯ j0:OD`[_4𪖌+5:꺗lL6qa-%W;h3,(̙l_E̗WKo8gTn;cRA0<3ck''E9PҕiN*-^IŻ-,n[C F(EsNj)I.{E~~u_Y@tΫji:՛ƬsV,0r⾶JPޟW?_ό4mVJ,Z? jЇ;Uexi!C9 G oqG8b`kjʥRr=~vmE'(chƝH՚aymo|V,~ъ>Lc+b`.sy}hfpyXAԻ_º hpۓ-GOUooE|Mhﭷ̥4O־‹ @x_Ͷ ᫸>3ۆwCZ"TbT˨U8c?~;[_ZKړj>}ljqE omq, ?1.*)s&-q WzktiKo )q(.kIF2m-i[|+ /&MJWS4qsYO&k})+**22F̢C:<1᫭kY{854 CGy*& \ȠB2O$/>=ԮƓ.4OhͪxS֤vG{cq;sߖ<^ӧޭ|5kxQ4k Pu 7́BmR=S.[E曍H()FPWM- ^y\+,2'cwZCIBJPnN2\ݚSoUt6o]i,f[?+K #4f y޻m1=zW?[OڭdKuW6~ c$$E ).XBRbź<5j"τ9EO&S,M"2b+9濦p b a읒Z'ӫ~?~"9Ш'5WmiIۥ_^߈տ|4+ח6pɨ]I<٣q'Q),~' 2Z~*O4 Jn-1j_lH M K% )Vmy_gE~#J].,4 O\{sc>l,Vrc ^"u}sǁ~&Zhz:ھgqܮ$e- dnnI"z0c5Rr۔VBJJu嶭0pZ$橷;sYFOrhNz jqkHa/47vKi-m @zHULJ]PJ%sE_gֺT֐[]1P,Brzg֠!(.5Oay%2kYn4ѬO FYJRCz7{O/ h*MqB& *YP f%r:j2n)JN<1v[IZr p=Q)I.U>W)&$wz~8>:Qm^#׼-Ƈy;:xi9ArȲK,icn!Tb>zb~њ|=]G-<4kxuxK[H9a;M5DY__0-]Ji(=Ω(atYiW~Y68x[ˋ|2).+|yxJN^K iY2\y&ieȂi""7$Ed11}*Я7E+5uhQkiZ:ЩI'4!gy[F t泊oݽYam?a@$󤕦ei\ȱT_> C=bMcI6F;ϭD׶F%7]I?LÞ+no"ռ_ |1qkKK/yLl,n#gJY#:OVfNBi9BRJ>4&>ϔ 9U _\ "kx`p;x;ᆵxsR懥Ё(!x_QYogMW#UUI-)^4R6[KL%UBjZ-Ӿ}4?\5YxǺ5íC> \_GshfO&%UZMB@@j7M:ƙ5AnKʀUCni-n8_~}X.a=UJu h{I"!c\}𲨣 M*j7Wn҃vRI6l=ƆmotUU׋nO [)[]OYmeHk]~Li)u Ɇ2-3(0Cp7] 4Z=Mmyqq?C"yw L-dYR7)R9 p_3bdܥͻ)=啰 ӓ_*%JڦKhР4EèͩڡaN:LD!A€@\zGn|`ȋwhMͅk ;NG"H]jo~w-q1#* 5ϭi_>jzmYnWoa]R-8 `ץcFI?z朧$/|8:!gһ傎ڥ?)S3xS|1^\:g5 6K-nkxRvT`¬!ڻkE)W >qs|2AidUmxv -nc22u%=q|/z,͛]C*o'PBosj!"hJɐdv JmC࿃W\n4V5]B{+|ٶ=\iY,2ۻ^a!Ahh%xqwk$mEo/v y~?>'|U-t\[jZ4@-˒O2REF36+,fF#e4V{Џ2jOK/e5p B+Dی%I'˟ly?_xgEOhWq}QcF&6,loW_ƞK?h~(Ou;i~_xz]cC!Bh`\ߎ7kڅl.]Ztkpmӭe0>(EgUO|7æEeh%X.3n%>bB*1ΰyVӚj7*i/wf2R[-Hn๓8Z.M]+E2kEۯW|?ۋȑk>Rt:$6w)lƶJ~|L.ju߀TӔCxbT6)E+¹vV4f 濒]^2W(okxTNvmZ9]K1,Gbڋ<7SU־6?H|Gu[j+ug v;KHVXg3s,` ʏx-9(JRQ,/2Ӻn׈|%_xeQH6NJΝ)rY.)'eO㗅.|Sx?>_k[ZX^|J7(IM'C"[u-slđxh_Z7oa^մkYOi5Ģ+wrgG>$j0ԴúG|;d5x4yVK}2)7{!.R;9T '_w_þ y=neX6MPТ^ke!IEksظX5&)JZ7Grn)+}M<ХNTizk쬯yZ0WI(Rnxu|D[🆼 }JK>J%0Aocr ^x?d5LxrHoe6-YCmc ɀ匱V> ]כ:ϋg=k|FwzT0{1A4jkqKZh=ƹ^iqG\ү%'Ļ n`~tx'=Utʦ߽rQڊu>t(9+ F1Wwin[&ϻI~ &KYy?IKKWE,ۡG5_%Ʀ8W=kbc=\x~+|6J_!O*uO6K T>}2SID*cX_(M>ldN/G|H/AG6'мcxbgծ#/{˺۸)7G24pW<ʯc TgpX~l|M(l)V'1?,c 9Ǎ>"Cǥxi|#Zr) 1t+K+Fwm+__{Ŀ|+m :N3ztZA!XTI9 b2J~OviYw<_֖&꼺urKCWVP` yq֙Ӝgh'id7 yWB@|mJyJi*Z7&Ф.A|'S.lM5c.q 3\pJwkO]gxcO>N>?<"]i侞"d{inJȑ®VW ;%5F>%g4oY8TiCdFPco*źƗox_uoxY[>4[࿻mvSe xZ WMۙQ(WZ4_.U5m۫;gol>/&4Ϳ~!|-m#NJu>Ұ63Y4w}o)f6f .ϚP;^WWvo2| iQ &QP>݉_OK~u=Z@]:жj7),Zv5xEPٸW~мeP_xHt@Sˣ]E5Ňbo\AJ}?bs]{+nOmFSH⩤ZKmZzF}+tm+xw.xz-*oONz]Ow5i fo"9~dDuW;?_U[J7vz#7z7 l"mpO-? V>4Ft k{ wa#eہ/M?o]XxKz6xl世Xۃq8y#s$l=PMN\Ҵa&s!U*IBVj[Eo_[=W_~"Q|E˿w|9G |-x:WVi]UD|oL)~-֛u5`]Fl %BejW ;%n,[x.JKxNR-Z0o4VO}RvoiRX}bݭ/1b+{Œ|YD/LȧhcTd._Zv!".p1 |wuIf%REgv;}G{B/ meXO-& # r QfC]&IkѵTOz^ƍ&{{[౔L$OU#q9O1XS?? eЭuKŏkQGe0:8OVI<]&4k:ĿZu/$kj7SI} }P3\,131BIAJho?xJ[\ .匶ː  ڳ+*pk.(gQ8O.1QMKnjx:`p1˚ MINRRMݤuK""xZM/X`u+8mcd&?OMN% W}.~BK_H%^ysiXgkݩ((V4UWtmwB>65h}&; K[PjC FC!JZ)PdOnpxCG'~ըh񵿊ƜNic#G;N! U<'勊U.yw3zgT~τ'Q.wV8ZQn;.E$՜+Gc>w~5ƛiqP2 ꗿlZ0I2*6_"^%֏P-i>DPI.smD*dT8,/_7-]>]l/uWMyV=>{KhHn$o*eՌFQ3[ O~;{:DLHV,,idmg\isƦ&Qԇ'RqZ3N2ێR$O\~]/hYNouʜe(BV^viw*úI{ijhxRU$+~t\@gpw:8[hm1]Q2>vmy:Ao ìv&ooztuZL3HC6b SWEݞ_Qj6jhv7yIɍ~yH_7,ʤk{XR,MiiI''&7$GtpKMSJn鮩{%ҎU_]ii=ԗ&]MgLmuEIp1#xK?g_,~o<)Xx‰[ɬK Lv0ie-gݔHndt4xlk4hcV\]xʺj /wH#¿z?>'YQw<4=[P = ɍíTH1.>VoҼc2TP~8˖4aJi/)<1=&]kZ.NUS騞{ݛHڲE$G*xo_ ~\2 +t?k[jGu3q("HD*3HޫmHլ 4mzS%[)XNŚI 1S G6Qk~!ӼG.e Myn!~Z <"f=6Hrltg/a^NԯgFOv*)˛igj.20({;索Wj--/f+;oxBuZrZ>ͳ>;kci1$i-4hՏnUuc]DȗMjZ}C骱L"M<ʎߒ<6÷.дߍ/ oDbx@o[Y5M&..;=,aߏ?g_>ϊ<[OA;-f_)5r+ %B\‰!2~s~ʬ5{9)K6Z{))6|ea S,>ҩ+'$ەړMnҹ/–O_k_tx /<)>)!R)lfacI%gO~ |=O$To<{O-2-Nḃx̸K(owόWk}gW%T 7ST:*<kx 5&kZ+mx$KT+G)#χV T|K /5я~ xQC ,&Ɩ䔩ؐoo2?eOWXS\U!`-O1cC՛._6G}_⟏Vh~DzO1g[[̠ͨH!c]X *x*Ju몶}4o_ Kk[Ӿ?On|\,-[O5}fmbV] Zrl H7aѳyk%]A!?b~ߵB|'s:|L[t7#ҭe.[#aj<#x[=ORἋT|ww-4B5hnf%Y7D nr͎+~% [j_C4tWQx%hmtҬw29vv2Q}HՓQwֶ}B7,.Dj}U[h_6xYi_MrÍk-e"\Ȫi0YFT?S/:?~|)w%>%-C5Uy)KԦh-OE`Z\n  a~G^%Ο1`I}CFJ̗N_yp;]C8@oƟ|׬4GV<]—G-K ^psbG]e4r4,9EZXʘag SJjM,k]cϊρI\xZÊ[[L7{TKPL lVHS wYdZdžc5<a`Ӧtnݽ\|X6ht}Ey^Gkou'RQ JAK|knk6C˭EM*W-ۻ+y$17c㨼.WKf x&"d*!|RkoNhƚZ-]ݒͽ_Mݿz4(ait[k~ͣ}N.8alO-փ\[,Ogf $,͒6?ߌ +_Þ8_Gk)֕6vkzgf8Lw G'D$,oalɘakf=l.V4Z'okiڭU֊ncANjPI|Wo|I.m_T]"ǂ-o2ZN[q:1f9FW<]oAcxzi"_k|DI.!v6f6迵i;u_ <3a7,sx]$Fki3zDT$ i:WMm3PDjZG.Y#7I*_J*گ,\쮹h<¥zmOY'k{O x{šF c{og$gs6A8rZ-7XKoup3A` *[Gq6I0$Fpt_ k;?$/GwZO , hd]@ak1"3bL_?}yO:/55]>T{FY]dC$8A$ܫ*Sj _ՊT^IKH[~g rY"`Rc3FChNa1G0@@U~A1jXlAagL"/%ʯ#$:}1rRGEFzV2fX)4[ F0Kc; _ $AلQF*+j(1dn'Q$YplN`0}km)g5$zd4;k8M孬ddc+5uېp'Zo^$/eVZo,cl_H {aw*,q#t_,ŲN q2Mw,n_Ow>T7q%\a$dzyc\-s倆eCn߸9 z|~eFSI4JQMl+4ߖx(qm7fjͧӫ50V5mj5g+1zX@B@]c$$פV4lu MmNg`tH9&{U|F#GUn-G<'?#ll%PP6o(]+ҩf9VnT#v,Qvo[E:qppҔe&m^wKOzmt4ޓdh~- =́X:=/z 7Y7  A=*J'G[i=NC S4rhV.%ך3Q0m (ę|3!MxI'XgwĭrğٶVrh~M?VP\ZZ^+C%ۤO_ s^6.OrMikH8 a׵O_3 ;ǝTHw&ضRʩ ׸2gtxrքӚNͮdMGi+c0\+>jS$M٤]Zֿ߆<3ߎwZf_%; iY6aJY9Y%JB 5g[[5JGjڷ۵`걼w !cnq~5NTuVi3")OnH&[]7m|֋,Z{^KRN_-]|Es = \ wj*INMfӜVVm|2\*kWRnͥ*)%hi"T~nj<8c KRbG,0Ap;vȨ#֭m5K=sQ=֛% Nkvu6!, ۽'b;9.mn ͳ6ylKT1HFy k/n̘a ) wҪET[[Z르BNrGwukYf}5w=EԭR]X|UzZY^ۘ.dGymÐ pG5>፯ޟ+O]cQ{Quz;4N"@2Nx7.T/rAo$`4Y/ YgY]·v.$fYJ5h98 mn4ˆQMi[QG44}80 {T3U\D+FK݂j ;[[&ni{%naJ5NW5쬾VM^׹K{ψiRijkZhr`U@]gtk^6KeOϡh1Cj66-""rq&>zaG$Y#d D%"#cQk>C=aGF|RR쭧[knk x+TgONJJY蹾1-ȼZ$iaNs- ܕ 1 O_zω}~ƚG}k_ xt,c*"1p433[ ȘbE`a@V{X+CWm5]ݶk}~_X|hW-/%}?@ xk_!gbƗb[Gp0q2d+{7|uūқ(4 t<1OQ{]yT.|\xJ cxFo:Z$>",A $VAYs^-6z5ڔw߰ҭ[1frٙpvE-Qn)QQ(5z=>+tjwxh,Dj KYu-O>TZ%-Lm"p` RH 6,J,1k 7ŚaK&y5?@^ET`[}Tiy)V +!F dv8#Њ[uG4羞9dm-_'dm+98UvB-,,c )ݯVת꣇[zٽl2QА>}Pe#¼ekTX^_ewv4Ŋ3aia,H;x{_:Tr,v[$N( [s6mb^k)mu*)zm$B2l688TzPPwjޚo}M/m]ZNW:rk]\hϡm#Ww΢UHCH"ڑ U.7}k }?oGx()ҮUMM}k3Ao#+0 Qk::\G8P$y%f%|"MA8Ӥӥt6P^,"33mvM6N2Fkѥ x<9YtϵC |LӼ/OE-{{d:|3ڮ/nUh4#{He~'xxWG{+j-3Q#CY,&yi~0Eɹ%=3S{A9Kvtx)]r,8e#;w%Mu ;XY .Ҭ9aAODM>`1`RPXm9 WrViarpGsi)ƌ/^i(ϯSRIJ8I<<~UeoXMK4f8Ġc$Ubv vM̻ T79'Fe}H2{s5}SJlԕYTHy!C9#<@9J=DYiV%3(sa<\Đ<^/2'898>vW2pdbp.BGşԒnחU#MGD-ź,lF0ѻ3PeqBo6eL*9 9G=kRODhdHV$1,KrNz tioڻiRjLrWd6G]#g^ZmapVi++IJjUZ]}-{̍s̮n.d(cEs(ߥu /N=ݺizݹdxYѣ+YfMV1\{hS$Q(%e Qj3qȖ(J m 5uZz]{X󮓼v_O5 ~ ptWA-kKҩG9|>QmU^{uj!Г-ggDhD#l-k%[^-t7ڏ0cH-K/ZB|aVn]E5 [Kc}{ԣ"iS72 hѪ9'k_}V.}70 #vy>&(HY;kY-q$[ݨ7{ou.k}oQc⹴ˮ3 q_FjZrKhlbմ?XYtcqk #F6=WxPocvm-#fZXq"(tV-={jߕ]6g?h!Iˁ^kxٙȷPN7mgN5[yY"Tܬp%D`{I04pYص4sDw-$w(G/,~*NvI.]X϶URB쯟+-`zWO|>|+/=ͼ 7QY\Y:$rIɂ"H$HnKat"Fe:.'&-&ҦQ66Įr8:7_K )ӨZNY4VcӧU8)f_ѼU]scwfUw2|߁D}5КF7 w j{yZ? qUsA*ҌWz?#d&KkG=ͣ )l(-ԩVٵNOLk 2"C*Mko|dԱgڸ,Xs8Km&A*3ہҺSJMӹ”MN՗-.mY  `Dr>~f(rJ bJB`#ι ېmUDC$3\eR HsS ig*n?:"E+Fd*8 p֧e0Ve66pH?\Wb~f=mwQ%vXT̏,w w]TZ^w0f^Gisqo cs _iR#;E fkso]\ۮZ䰀ƗU>PJ9%\B_ySi ?q<1d-ȩyF uJz-/sQ;/"CIJ-PX)X~8se֭.H>UA"vo'O6sGn?( FA\uZҴjS~?o hg_vF fD+!SNTdA77WqvZӮ.,V+x5%-272?!ʤ(AkSǓym!繼g$̐ſl*m!9l?5 ;Pcў>Rjwfd ] 㦆+׊k=߭3&ݓ=:C{D?SoPHTXʌ; @K(H yXYROpTW$s]Ǎ[渴=9(,V-3Gml4afbQޅ'tXIྖ+;&K%m)+*+,/֗Pm!K;;kuZq%1>d Ђyet/ !6PCoo G#luv(ūk1ԝJJQ>- 6>'G;fPNDf`XX/t?j%"?6%vc9&܀r+_q$߱H0NϭiWȄ T2Ø9Ԝ I^jʰ!H$idW`9wu5Ɩ+=2TR̒\6@`xĞ?IU ['ޟ:je0VSag/gONGxT3JbXqYWR,AI61< :ԶҺAdd81?li)hzj=O9].4Z6w(T:afiddX큰.:٘懦ܪDnv,?R@Rɬ^O5bVFtK `1VEm€N8 u[7q\Ȗu2` |pc #o5mI|%kWǭ%m/܈~ΫfEA+_IשjE6|fܻhUqQI˯[mݾY^:m/z/R/o< ObIAsl5QkĨutiC"wߵGamGQ=&š+4 !{Fe>tnY B7m$z wOB𕖟 8@J[kY1;ȃ_. n\hʉ/% SRrh4'.&WRHf]; 9^sz˖gG*̟+eNqM{E lGi .3C׈4+ d23>G9ҭIJ6r.gsG,(Kl#8LqV>u.LI#*6N 8W,M ˰ EoN崿RVii!$M2>BW7>N`y1<21{Kf I' F0qIj^~7.hopw>cB89", BNm[Iw{%':Q^m}W : 7r,J Hn`i67I-j|JQ_%gV&UpJ g˄㣁9Jr劻*r{'{(}5[ІXJ ׽%~m/𶵼SڭV6.Z^PyѐK唑sp3yGīSv4, :mۭ,B:[¿YgH MioimJ]B3x3uQ `+ 9#? |m6=~"=akqx^xA1-H]2\1'iSQnRfn׺kUvNae+F\II?[^GjW&ּ0$)e.I1i2Ag*>%ެT _zN[Ouj%t, y",r%"<0`y2~) xƝo^w4Kc=խ K%Ò 6߰񖇨^/Gw֯AF0H6nC<1WI9 |'^B5`e'jM.w\M7xsO:'k^.YY;։Lo>o$7?U+?25{0p3 jMݔKySld]=]CJwܪX0]L+ŗ0g 0jZ勭GKo->oy&@o%͖5į:焯<;~1lq\aդ#jBtxۓx3uR]7\GFTgּ7K׿o#eˏ%h ì|_񥇂m{>"B W}=5k;nv13[>Zf5JZ^ڳӼ?sakhQlA󥶷Ydt;Ƨ<eH&O5FqΓ*M̎Wq9FxY]Λ2ZV|Hʒ<$Z ܃=6^DSG{{bVT(.9uhVћjQnWwTV3(Vܩ-/OI^:iVRAL21=ZI<;N2{χ/$1=?xVR5XPASȇE3C bRȿ0?5iڧ5BzwFGD,$H]piBJ0>qq޵ΥtAs I5u7K{uLw1+L>oR8I{rݝu\GNWquPi.[]-WnV[g_o)ѴFb{ųͅ@eO |E0Ms7Zv?{ekvjXZ< qRI(kycfs^7HJnmONnʲI<|RB_x[GFÏGԾ-i^,57W6G(㳍-w3LO3:E 1VaͱʝZmqVp|B.{y,|LOj#S{ƛ`>*oJ${{y-\K*R9~j~+,/ze16z;붖Vrl,mnl-=y 췗-дpum ;{zWxέ4&ʁz8~^Ke&x? i ٵH^`,Wm.L|;'zW Nƒ>275}׉t-:[I25I,enakۼQK7FC-Szh1wzˤ&limq#JD\ʌa`UF]8%$ʽTsrӤT\Riv좗]]mއ&<tJFP>{z%If\`W?6P.n!MSZ^tumnB4Ւ$b@(rz7׃<9yy^]-$tܦ,"sWaۏJ^Jͯmm0% R7=H8@}BIu ~QsWG#V p~Ke5s zzlkgm8ˈsl24('mO2f7hc'a~x/VPVMủuoc_×a,ys]&|1Kj-@=bK|3]15;1ҡp ۸ 1H_W U*7uwnZ]|8:I3K`ntin ҷs YIrʲdvm_䱛 qaZIm1#o$#8Asu [KgKD{- >#/"ڬmq `w-3w\,}$F7{w~oz&W+e\*D2SRV"S4l55M#-K9Zzhs˭_1TqY&.Ti|G{ng,⻟gK@Z-Qo4̌WR55<NN8*p<;%I]ٻv`pJX*{6;< ɥk Լ5=-Դ +k- ťNpDE,W/|W o SQ\''¶W"KZ<;N( W3)Oe7)^+2Q拒=q6I79-/e$n|nu~ oßu>yu+F ame5IhF։x'yx xcǂM/ \p"Hln|x/g{Fd + 0f.R]_ W0xwƺUǁmk_)tg{ ]S\& Lzxk\X4$jO-Â1]7pj_>!IFi~W{}ĉ泅b0[ʚLZOM*OFq}%-\K'OK[5u}?J~ך/~,>=^BHBKul)rAF3>^Vѧ<s,-  8nbUhTŶI`??mKKCw#< V ܎!&9E(m՚ \k!~kQXxO\WĞ-i|Aw66bCp;-m :iM|Kwem4Z]>L)F-{;,Ҿ 7:E}Fc^ |qk*eBe?"|U߈xj~~K]I,7k>,,LTwp &ŰV2H2!CcO㯌^ ?5K [S޽okU[ly|A@7?1|Wk@y}DjZe- +:[. 7/ʲ:Hc;j1nwכv}!hrKyg7`">3#\?>s r^:ۻA;Q/Gcj 70|>%zKu^N!<2DN NWW> ׯE^?"\KVVMrW|MH |wío:uDmn[x?X>gլqc r#X1$[]*I{r|Xz1M+{tvw Io(>->+IkkCGյ2B䴁b~ӚXvmU_D|9E ?\^:cnk{?!%ʘKy#eéa%|udhDh'nW?k1tGWm}gez +G,;[8FKA˝KF˷zc)xhƣm;%e>vૹC_]ourr\"If]\HWcŽ7$Ұ>_ČPsj hc8,ɀYAzk? ?cjNz;[od xL}{B '1xtBvK vĹV1hмK\ oFj:Rtྜྷ(5+_($j r&Z~t]6ivPB>C=&pcqZ5>\Myn5&EԼE㴷,Z)лAjsL?4⌡cS |zuJ-M6gdC mv?$4E~kgK'Gf2kS1L#3퍦ͶT);6!-%㈣s e۱쩮eyb @La[%ޓ^iyMn'O?) 0rq|. 4O%)kvjީ/{C)$ofu1-Z{.ImS1IbIo/y&QТ:_ںූ[+&]OS\am~5UT&I鴟a$1[Im<eȩqZ LJS~ ~$RZKi<0u}2ʦ0 cc0kѭ~X\],-n4lpQǖJ6ۧS<xK/IxZ C(i!2&@YÕB~%xc +/.OԤ62ȖK>-2ƢD܌YdmĄOZOk7kqxvZH6W0X1)[bH_)>=p [嘰P# Po{^dﵟ{?b0ѵկ?7YZkۍQKf0T29 I^K ts[饷&hܤvf+o  /L5]3F-$W$In-UPH}Q̰k)\,4GX# 0dQr@`}^7ilzWw83ZT*=-zwߡU|nS⮕Ή?~/y,g//Xxz8aXyhG4l$~) Mƾ t^$>Ԑҵh%d]?VAԞ7M]ͦShf%` +>,~پ?5? yPCFwbL'qS'haPӜwK_]:-־60VjVVm&{'k>8fo5m7:5th;7H{1d*cW`-S|Y}>_ ?|4O+]up7]"} W)Z^;1nD,c!ڤ_Gjׂ<-t/Noo7Q;L> `kyLi9ֳr85MDDƧ*A{W*x:/{)uiK;1 1n6k_k[~d5]oJ4xZ%ժ:@tϊ$M#Ou7w޵܁8#xy7PVv_x˥U ڕ :V}K]5ɹ*tivGߏOih :\.tbD>kg|Cm&K#[}' ;3wkvH/:W?ugZW:j~Mcs[b:|4M)D-8__C]{.YѼ+iKsMH3K(Yp0ѯ_c|m'/χ,f7P .qy,^ȭ=?hHMT*ʳX,mL.61tngdӲך6vnVTJ 󧪿[kz??j^沂[-|=5eemD2 g%a)uќ!~8:ĝ=_zu}W?ĦeGZDof#l /Ǟƣ?:t5;G}od1JFѲ&oToEFc'ResKO; ok ڀS2>Yq;(v2TNrJvZw緩"~|O>.Gß ᯵$oS7u.&SwokVֆ<3gox:̘~xe|OOazD,m&Іl禹ncxwTyonCC[s#]>LGyŮC!Q`[ ÷iW $|7?.jmif,~J2˹wO 䌹oKi~|αuRPVU-+^#šQVى}"0n1$ȯH_įx{ž!|c mf>P42Yy?y.b 6ˑ//N1[]|%6l%SgB2ۣܙ!jhx.t$Y]:@Ęu%vJd n&.֌VMWc0B4!~h};=tfv_Cmu#ZWË+ynUO.R[jeC%Glr26}/R_"js\0(%u`x{-PE""W—u=Ԭ_eFɐ%Ԛt ڇϑ_?%.:x7%.y}3kM>*v5NyBsI|LDgi<߇eZ헋ᶯ{sZe$X=3-撮qo?+iou;XBƖ5iʡ##{]ıߴ[W>8 |Wžծ!GN.FE3s.\P\J֡Jy2q]6ǵBZQJ>dZ?neosask~ k x"iaѭV[q .cRF`ѳ|˥NisMZ^]iZe*_/B۷2|ٮSXmj=Bwb/ ^%2+F#i# Yrv"B<w,|E$ipNxoyERR{4ݛM>+=l|cb\pջ_S5S\/[QMR J79̒ǫJ.q\,LdI 8}X38޽D:tHZ_S3_\T{a731- qb{m/׶cΣFWwgkīP~f$>RRx侀%6teB0_5 ?\4[] 1/4ڕܮR[I%ʤqIjڷ?jϪ|DP dy`."<ky2!pׅ'N̺;pEFh;;?O[k붧 xc[~xw_?kx?%D[X-^=ΗwlcKs98ѣl3nczZw R?b;hv`n"U.r~~'9|]5 lRUet|5jVj,hL2|y#?W!ß/Kmsmhѽq3Afa[L1!hMYbo޳립7QtqKoM/ߍC@mou5KZAʟR1;Vn`xEN+ |?}ixHӴ"Qq_fIΧwI IxCk(5w_75ok^\ #include \end{code} Please refer to the {\cf Ilmbase} and {\cf OpenEXR} documentation and header files for more complete information about use of these types in your own application. However, note that you are not strictly required to use these classes in your application --- {\cf Imath::V3f} has a memory layout identical to {\cf float[3]} and {\cf Imath::M44f} has a memory layout identical to {\cf float[16]}, so as long as your own internal vectors and matrices have the same memory layout, it's ok to just cast pointers to them when passing as arguments to \TextureSystem methods. \subsection{\TextureOpt} \indexapi{TextureOpt} \TextureOpt is a structure that holds many options controlling single-point texture lookups. Because each texture lookup API call takes a reference to a \TextureOpt, the call signatures remain uncluttered rather than having an ever-growing list of parameters, most of which will never vary from their defaults. Here is a brief description of the data members of a \TextureOpt structure: \apiitem{int firstchannel} The beginning channel for the lookup. For example, to retrieve just the blue channel, you should have {\cf firstchannel} = 2 while passing {\cf nchannels} = 1 to the appropriate texture function. \apiend \apiitem{int subimage \\ ustring subimagename} Specifies the subimage or face within the file to use for the texture lookup. If {\cf subimagename} is set (it defaults to the empty string), it will try to use the subimage that had a matching metadata \qkw{oiio:subimagename}, otherwise the integer {\cf subimage} will be used (which defaults to 0, i.e., the first/default subimage). Nonzero subimage indices only make sense for a texture file that supports subimages or separate images per face (such as Ptex). This will be ignored if the file does not have multiple subimages or separate per-face textures. \apiend \apiitem{Wrap swrap, twrap} Specify the \emph{wrap mode} for 2D texture lookups (and 3D volume texture lookups, using the additional {\cf rwrap} field). These fields are ignored for shadow and environment lookups. These specify what happens when texture coordinates are found to be outside the usual $[0,1]$ range over which the texture is defined. {\cf Wrap} is an enumerated type that may take on any of the following values: \begin{description} \item[\spc] \spc \item[\rm \kw{WrapBlack}] The texture is black outside the [0,1] range. \item[\rm \kw{WrapClamp}] The texture coordinates will be clamped to [0,1], i.e., the value outside [0,1] will be the same as the color at the nearest point on the border. \item[\rm \kw{WrapPeriodic}] The texture is periodic, i.e., wraps back to 0 after going past 1. \item[\rm \kw{WrapMirror}] The texture presents a mirror image at the edges, i.e., the coordinates go from 0 to 1, then back down to 0, then back up to 1, etc. \item[\rm \kw{WrapDefault}] Use whatever wrap might be specified in the texture file itself, or some other suitable default (caveat emptor). \end{description} The wrap mode does not need to be identical in the $s$ and $t$ directions. \apiend \apiitem{float swidth, twidth} For each direction, gives a multiplier for the derivatives. Note that a width of 0 indicates a point sampled lookup (assuming that blur is also zero). The default width is 1, indicating that the derivatives should guide the amount of blur applied to the texture filtering (not counting any additional \emph{blur} specified). \apiend \apiitem{float sblur, tblur} For each direction, specifies an additional amount of pre-blur to apply to the texture (\emph{after} derivatives are taken into account), expressed as a portion of the width of the texture. In other words, blur = 0.1 means that the texture lookup should act as if the texture was pre-blurred with a filter kernel with a width 1/10 the size of the full image. The default blur amount is 0, indicating a sharp texture lookup. \apiend \apiitem{float fill} Specifies the value that will be used for any color channels that are requested but not found in the file. For example, if you perform a 4-channel lookup on a 3-channel texture, the last channel will get the fill value. (Note: this behavior is affected by the \qkw{gray_to_rgb} attribute described in Section~\ref{sec:texturesys:attributes}.) \apiend \apiitem{const float* missingcolor} If not NULL, indicates that a missing or broken texture should \emph{not} be treated as an error, but rather will simply return the supplied color as the texture lookup color and {\cf texture()} will return {\cf true}. If the {\cf missingcolor} field is left at its default (a NULL pointer), a missing or broken texture will be treated as an error and {\cf texture()} will return {\cf false}. Note: When not NULL, the data must point to \emph{nchannels} contiguous floats. \apiend \apiitem{float bias} For shadow map lookups only, this gives the ``shadow bias'' amount. \apiend \apiitem{int samples} For shadow map lookups only, the number of samples to use for the lookup. \apiend \apiitem{Wrap rwrap \\ float rblur, rwidth} Specifies wrap, blur, and width for the third component of 3D volume texture lookups. These are not used for 2D texture lookups. \apiend \subsection{\TextureOptions} \TextureOptions is a structure that holds many options controlling batched texture lookups. Because each texture lookup API call takes a reference to a \TextureOptions, the call signatures remain uncluttered rather than having an ever-growing list of parameters, most of which will never vary from their defaults. Here is a brief description of the data members of a \TextureOptions structure: \apiitem{int firstchannel} The beginning channel for the lookup. For example, to retrieve just the blue channel, you should have {\cf firstchannel} = 2 while passing {\cf nchannels} = 1 to the appropriate texture function. \apiend \apiitem{int subimage} The subimage or face within the file to use for the texture lookup. The default is 0, and larger values only make sense for a texture file that supports subimages or separate images per face (such as Ptex). This will be ignored if the file does not have multiple subimages or separate per-face textures. \apiend \apiitem{Wrap swrap, twrap} Specify the \emph{wrap mode} for 2D texture lookups (and 3D volume texture lookups, using the additional {\cf rwrap} field). These fields are ignored for shadow and environment lookups. These specify what happens when texture coordinates are found to be outside the usual $[0,1]$ range over which the texture is defined. {\cf Wrap} is an enumerated type that may take on any of the following values: \begin{description} \item[\spc] \spc \item[\rm \kw{WrapBlack}] The texture is black outside the [0,1] range. \item[\rm \kw{WrapClamp}] The texture coordinates will be clamped to [0,1], i.e., the value outside [0,1] will be the same as the color at the nearest point on the border. \item[\rm \kw{WrapPeriodic}] The texture is periodic, i.e., wraps back to 0 after going past 1. \item[\rm \kw{WrapMirror}] The texture presents a mirror image at the edges, i.e., the coordinates go from 0 to 1, then back down to 0, then back up to 1, etc. \item[\rm \kw{WrapDefault}] Use whatever wrap might be specified in the texture file itself, or some other suitable default (caveat emptor). \end{description} The wrap mode does not need to be identical in the $s$ and $t$ directions. \apiend \apiitem{VaryingRef swidth, twidth} For each direction, gives a multiplier for the derivatives. Note that a width of 0 indicates a point sampled lookup (assuming that blur is also zero). The default width is 1, indicating that the derivatives should guide the amount of blur applied to the texture filtering (not counting any additional \emph{blur} specified). \apiend \apiitem{VaryingRef sblur, tblur} For each direction, specifies an additional amount of pre-blur to apply to the texture (\emph{after} derivatives are taken into account), expressed as a portion of the width of the texture. In other words, blur = 0.1 means that the texture lookup should act as if the texture was pre-blurred with a filter kernel with a width 1/10 the size of the full image. The default blur amount is 0, indicating a sharp texture lookup. \apiend \apiitem{VaryingRef fill} Specifies the value that will be used for any color channels that are requested but not found in the file. For example, if you perform a 4-channel lookup on a 3-channel texture, the lsat channel will get the fill value. (Note: this behavior is affected by the \qkw{gray_to_rgb} attribute described in Section~\ref{sec:texturesys:attributes}.) \apiend \apiitem{VaryingRef missingcolor} If supplied, indicates that a missing or broken texture should \emph{not} be treated as an error, but rather will simply return the supplied color as the texture lookup color and {\cf texture()} will return {\cf true}. If the {\cf missingcolor} field is left at its default (a NULL pointer), a missing or broken texture will be treated as an error and {\cf texture()} will return {\cf false}. Although this is a {\cf VaryingRef}, the data must point to \emph{nchannels} contiguous floats, and if ``varying,'' the step size must be set to {\cf nchannels*sizeof(float)}, not {\cf sizeof(float)}. \apiend \apiitem{VaryingRef bias} For shadow map lookups only, this gives the ``shadow bias'' amount. \apiend \apiitem{VaryingRef samples} For shadow map lookups only, the number of samples to use for each lookup. \apiend \apiitem{Wrap rwrap \\ VaryingRef rblur, rwidth} Specifies wrap, blur, and width for the third component of 3D volume texture lookups. These are not used for 2D texture lookups. \apiend \subsection{{\cf VaryingRef}: encapsulate uniform and varying} Many texture access API routines are designed to look up texture efficiently at many points at once. Therefore, many of the parameters to the API routines, and many of the fields in \TextureOptions need to accommodate both uniform and varying values. \emph{Uniform} means that a single value may be used for each of the many simultaneous texture lookups, whereas \emph{varying} means that a different value is provided for each of the positions where you are sampling the texture. Please read the comments in \qkw{varyingref.h} for the full gory details, but here's all you really need to know about it to use the texture functionality. Let's suppose that we have a routine whose prototype looks like this: \begin{code} void API (int n, VaryingRef x); \end{code} \noindent This means that parameter $x$ may either be a single value for use at each of the $n$ texture lookups, or it may have $n$ different values of $x$. If you want to pass a uniform value, you may do any of the following: \begin{code} float x; // just one value API (n, x); // automatically knows what to do! API (n, &x); // Also ok to pass the pointer to x API (n, VaryingRef(x)); // Wordy but correct API (n, Uniform(x)); // Shorthand \end{code} If you want to pass a varying value, i.e., an array of values, \begin{code} float x[n]; // One value for each of n points API (n, VaryingRef(x), sizeof(x)); // Wordy but correct API (n, Varying(x)); // Shorthand if stride is sizeof(x) \end{code} You can also initialize a VaryingRef directly: \begin{code} float x; // just one value float y[n]; // array of values VaryingRef r; r.init (&x); // Initialize to uniform r.init (&x, 0); // Initialize to uniform the wordy way r.init (&y, sizeof(float)); // Initialize to varying ... API (n, r); \end{code} \subsection{SIMD Run Flags} Many of the texture lookup API routines are written to accommodate queries about many points at once. Furthermore, only a subset of points may need to compute. This is all expressed using three parameters: {\cf Runflag *runflags, int beginactive, int endactive}. There are also {\cf VaryingRef} parameters such as {\cf s} and {\cf t} that act as if they are arrays. The {\cf beginactive} and {\cf endactive} indices are the first (inclusive) and last (exclusive) points that should be computed, and for each point {\cf runflags[i]} is nonzero if the point should be computed. To illustrate, here is how a routine might be written that would copy values in {\cf arg} to {\cf result} using runflags: \begin{code} void copy (Runflag *runflags, int beginactive, int endactive, VaryingRef arg, VaryingRef result) { for (int i = beginactive; i < endactive; ++i) if (runflags[i]) result[i] = arg[i]; } \end{code} \newpage \section{TextureSystem API} \label{sec:texturesys:api} \subsection{Creating and destroying texture systems} \label{sec:texturesys:api:createdestroy} \TextureSystem is an abstract API described as a pure virtual class. The actual internal implementation is not exposed through the external API of \product. Because of this, you cannot construct or destroy the concrete implementation, so two static methods of \TextureSystem are provided: \apiitem{static TextureSystem *TextureSystem::{\ce create} (bool share=true)} Creates a new \TextureSystem and returns a pointer to it. If {\cf shared} is {\cf true}, the \TextureSystem created will share its underlying \ImageCache with any other \TextureSystem's or \ImageCache's that requested shared caches. If {\cf shared} is {\cf false}, a completely unique \ImageCache will be created that is private to this particular \TextureSystem. \apiend \apiitem{static void TextureSystem::{\ce destroy} (TextureSystem *x, \\ \bigspc\bigspc bool teardown_imagecache=false)} Destroys an allocated \TextureSystem, including freeing all system resources that it holds (such as its underlying \ImageCache). This is necessary to ensure that the memory is freed in a way that matches the way it was allocated within the library. Note that simply using {\cf delete} on the pointer will not always work (at least, not on some platforms in which a DSO/DLL can end up using a different allocator than the main program). If {\cf teardown_imagecache} is {\cf true}, and the \TextureSystem's underlying \ImageCache is the \emph{shared} one, then that \ImageCache will be thoroughly destroyed, not merely releasing the reference. \apiend \subsection{Setting options and limits for the texture system} \label{sec:texturesys:api:options} The following member functions of \TextureSystem allow you to set (and in some cases retrieve) options that control the overall behavior of the texture system: \apiitem{bool {\ce attribute} (string_view name, TypeDesc type, const void *val)} \indexapi{attribute} Sets an attribute (i.e., a property or option) of the \TextureSystem. The {\cf name} designates the name of the attribute, {\cf type} describes the type of data, and {\cf val} is a pointer to memory containing the new value for the attribute. If the \TextureSystem recognizes a valid attribute name that matches the type specified, the attribute will be set to the new value and {\cf attribute()} will return {\cf true}. If {\cf name} is not recognized as a valid attribute name, or if the types do not match (e.g., {\cf type} is {\cf TypeDesc::FLOAT} but the named attribute is a string), the attribute will not be modified, and {\cf attribute()} will return {\cf false}. Here are examples: \begin{code} TextureSystem *ts; ... int maxfiles = 50; ts->attribute ("max_open_files", TypeDesc::INT, &maxfiles); const char *path = "/my/path"; ts->attribute ("searchpath", TypeDesc::STRING, &path); \end{code} Note that when passing a string, you need to pass a pointer to the {\cf char*}, not a pointer to the first character. (Rationale: for an {\cf int} attribute, you pass the address of the {\cf int}. So for a string, which is a {\cf char*}, you need to pass the address of the string, i.e., a {\cf char**}). The complete list of attributes can be found at the end of this section. \apiend \apiitem{bool {\ce attribute} (string_view name, int val) \\ bool {\ce attribute} (string_view name, float val) \\ bool {\ce attribute} (string_view name, double val) \\ bool {\ce attribute} (string_view name, string_view val)} Specialized versions of {\cf attribute()} in which the data type is implied by the type of the argument. For example, the following are equivalent to the example above for the general (pointer) form of {\cf attribute()}: \begin{code} ts->attribute ("max_open_files", 50); ts->attribute ("searchpath", "/my/path"); \end{code} \apiend \apiitem{bool {\ce getattribute} (string_view name, TypeDesc type, void *val)} \indexapi{getattribute} Gets the current value of an attribute of the \TextureSystem. The {\cf name} designates the name of the attribute, {\cf type} describes the type of data, and {\cf val} is a pointer to memory where the user would like the value placed. If the \TextureSystem recognizes a valid attribute name that matches the type specified, the attribute value will be stored at address {\cf val} and {\cf attribute()} will return {\cf true}. If {\cf name} is not recognized as a valid attribute name, or if the types do not match (e.g., {\cf type} is {\cf TypeDesc::FLOAT} but the named attribute is a string), no data will be written to {\cf val}, and {\cf attribute()} will return {\cf false}. Here are examples: \begin{code} TextureSystem *ts; ... int maxfiles; ts->getattribute ("max_open_files", TypeDesc::INT, &maxfiles); const char *path; ts->getattribute ("searchpath", TypeDesc::STRING, &path); \end{code} Note that when passing a string, you need to pass a pointer to the {\cf char*}, not a pointer to the first character. Also, the {\cf char*} will end up pointing to characters owned by the \TextureSystem; the caller does not need to ever free the memory that contains the characters. The complete list of attributes can be found at the end of this section. \apiend \apiitem{bool {\ce getattribute} (string_view name, int \&val) \\ bool {\ce getattribute} (string_view name, float \&val) \\ bool {\ce getattribute} (string_view name, double \&val) \\ bool {\ce getattribute} (string_view name, char **val) \\ bool {\ce getattribute} (string_view name, std::string \& val)} Specialized versions of {\cf getattribute()} in which the data type is implied by the type of the argument. For example, the following are equivalent to the example above for the general (pointer) form of {\cf getattribute()}: \begin{code} int maxfiles; ts->getattribute ("max_open_files", &maxfiles); const char *path; ts->getattribute ("searchpath", &path); \end{code} \apiend \subsubsection*{Texture system attributes} \label{sec:texturesys:attributes} Recognized attributes include the following: \apiitem{int max_open_files \\ float max_memory_MB \\ string searchpath \\ string plugin_searchpath \\ int autotile \\ int autoscanline \\ int automip \\ int accept_untiled \\ int accept_unmipped \\ int failure_retries \\ int deduplicate \\ string substitute_image \\ int max_errors_per_file} These attributes are all passed along to the underlying \ImageCache that is used internally by the \TextureSystem. Please consult the \ImageCache attribute list in Section~\ref{sec:imagecache:api:attribute} for explanations of these attributes. \apiend \apiitem{matrix worldtocommon} The $4 \times 4$ matrix that provides the spatial transformation from ``world'' to a ``common'' coordinate system. This is used for shadow map lookups, in which the shadow map itself encodes the world coordinate system, but positions passed to {\cf shadow()} are expressed in ``common'' coordinates. \apiend \apiitem{matrix commontoworld} The $4 \times 4$ matrix that is the inverse of {\cf worldtocommon} --- that is, it transforms points from ``common'' to ``world'' coordinates. You do not need to set {\cf commontoworld} and {\cf worldtocommon} separately; just setting either one will implicitly set the other, since each is the inverse of the other. \apiend \apiitem{int gray_to_rgb} If set to nonzero, texture lookups of single-channel (grayscale) images will replicate the sole channel's values into the next two channels, making it behave like an RGB image that happens to have all three channels with identical pixel values. (Channels beyond the third will get the ``fill'' value.) The default value of zero means that all missing channels will get the ``fill'' color. \apiend \apiitem{int max_tile_channels} Sets the maximum number of color channels in a texture file for which all channels will be loaded as cached tiles. Files with more than this number of color channels will have only the requested subset loaded, in order to save cache space (but at the possible wasted expense of separate tiles that overlap their channel ranges). The default is 5. \apiend \apiitem{string latlong_up} Sets the default ``up'' direction for latlong environment maps (only applies if the map itself doesn't specify a format or is in a format that explicitly requires a particular orientation). The default is \qkw{y}. (Currently any other value will result in $z$ being ``up.'') \apiend \apiitem{int flip_t} \NEW % 1.7 If nonzero, $t$ coordinates will be flipped ($1-t$) for texture lookups. The default is 0. \apiend \apiitem{string options} This catch-all is simply a comma-separated list of {\cf name=value} settings of named options. For example, \begin{code} ic->attribute ("options", "max_memory_MB=512.0,autotile=1"); \end{code} \apiend \subsection{Opaque data for performance lookups} \label{sec:texturesys:api:opaque} \apiitem{Perthread * {\ce get_perthread_info} (Perthread *thread_info=NULL) \\ Perthread * {\ce create_perthread_info} () \\ void {\ce destroy_perthread_info} (Perthread *thread_info)} \indexapi{get_perthread_info} \indexapi{create_perthread_info} \indexapi{destroy_perthread_info} The \TextureSystem implementation needs to maintain certain per-thread state, and some \TextureSystem methods take an opaque {\cf Perthread} pointer to this record. There are three options for how to deal with it: 1. Don't worry about it at all: don't use the methods that want {\cf Perthread} pointers, or always pass {\cf NULL} for any {\cf Perthread*} arguments, and \TextureSystem will do thread-specific-pointer retrieval as necessary (though at some small cost). 2. If your app already stores per-thread information of its own, you may call {\cf get_perthread_info(NULL)} to retrieve it for that thread, and then pass it into the functions that allow it (thus sparing them the need and expense of retrieving the thread-specific pointer). However, it is crucial that this pointer not be shared between multiple threads. In this case, the \TextureSystem manages the storage, which will automatically be released when the thread terminates. 3. If your app also wants to manage the storage of the {\cf Perthread}, it can explicitly create one with {\cf create_perthread_info}, pass it around, and eventually be responsible for destroying it with {\cf destroy_perthread_info}. When managing the storage, the app may reuse the {\cf Perthread} for another thread after the first is terminated, but still may not use the same {\cf Perthread} for two threads running concurrently. \apiend \apiitem{TextureHandle * {\ce get_texture_handle} (ustring filename,\\ \bigspc\bigspc\bigspc Perthread *thread_info=NULL)} \indexapi{get_texture_handle} Retrieve an opaque handle for fast texture lookups. The optional opaque pointer {\cf thread_info} is thread-specific information returned by {\cf get_perthread_info()}. Return {\cf NULL} if something has gone horribly wrong. \apiend \apiitem{bool {\ce good} (TextureHandle *texture_handle)} \indexapi{good} Return true if the texture handle (previously returned by {\cf get_texture_handle()}) is a valid image that can be subsequently read or sampled. \apiend %\newpage \subsection{Texture Lookups} \label{sec:texturesys:api:texture} \apiitem{bool {\ce texture} (ustring filename, TextureOpt \&options,\\ \bigspc\spc float s, float t, float dsdx, float dtdx,\\ \bigspc\spc float dsdy, float dtdy, int nchannels, float *result),\\ \bigspc\spc float *dresultds=NULL, float *dresultdt=NULL)} \indexapi{texture} Perform a filtered 2D texture lookup on a position centered at 2D coordinates ({\cf s}, {\cf t}) from the texture identified by {\cf filename}, and using relevant texture {\cf options}. The {\cf nchannels} parameter determines the number of channels to retrieve (e.g., 1 for a single value, 3 for an RGB triple, etc.). The filtered results will be stored in {\cf result[0..nchannels-1]}. We assume that this lookup will be part of an image that has pixel coordinates {\cf x} and {\cf y}. By knowing how {\cf s} and {\cf t} change from pixel to pixel in the final image, we can properly \emph{filter} or antialias the texture lookups. This information is given via derivatives {\cf dsdx} and {\cf dtdx} that define the change in {\cf s} and {\cf t} per unit of {\cf x}, and {\cf dsdy} and {\cf dtdy} that define the change in {\cf s} and {\cf t} per unit of {\cf y}. If it is impossible to know the derivatives, you may pass 0 for them, but in that case you will not receive an antialiased texture lookup. If the {\cf dresultds} and {\cf dresultdt} parameters are not {\cf NULL} (the default), these specify locations in which to store the \emph{derivatives} of the texture lookup, i.e., the change of the filtered texture per unit of $s$ and $t$, respectively. Each must point to at least {\cf nchannels} contiguous floats. If they are {\cf NULL}, the derivative computations will not be performed. Fields within {\cf options} that are honored for 2D texture lookups include the following: \vspace{-12pt} \apiitem{int firstchannel} \vspace{10pt} The index of the first channel to look up from the texture. \apiend \vspace{-24pt} \apiitem{int subimage} \vspace{10pt} The subimage or face within the file. This will be ignored if the file does not have multiple subimages or separate per-face textures. \apiend \vspace{-24pt} \apiitem{Wrap swrap, twrap} \vspace{10pt} Specify the \emph{wrap mode} for each direction, one of: {\cf WrapBlack}, {\cf WrapClamp}, {\cf WrapPeriodic}, {\cf WrapMirror}, or {\cf WrapDefault}. \apiend \vspace{-24pt} \apiitem{float swidth, twidth} \vspace{10pt} For each direction, gives a multiplier for the derivatives. \apiend \vspace{-24pt} \apiitem{float sblur, tblur} \vspace{10pt} For each direction, specifies an additional amount of pre-blur to apply to the texture (\emph{after} derivatives are taken into account), expressed as a portion of the width of the texture. \apiend \vspace{-24pt} \apiitem{float fill} \vspace{10pt} Specifies the value that will be used for any color channels that are requested but not found in the file. For example, if you perform a 4-channel lookup on a 3-channel texture, the last channel will get the fill value. (Note: this behavior is affected by the \qkw{gray_to_rgb} attribute described in Section~\ref{sec:texturesys:attributes}.) \apiend \vspace{-24pt} \apiitem{const float *missingcolor} \vspace{10pt} If not NULL, specifies the color that will be returned for missing or broken textures (rather than being an error). \apiend This function returns {\cf true} upon success, or {\cf false} if the file was not found or could not be opened by any available ImageIO plugin. \apiend \apiitem{bool {\ce texture} (TextureHandle *texture_handle, Perthread *thread_info, \\ \bigspc TextureOpt \&options, float s, float t, float dsdx, float dtdx,\\ \bigspc float dsdy, float dtdy, int nchannels, float *result,\\ \bigspc float *dresultds=NULL, float *dresultdt=NULL)} A slightly faster {\cf texture} call for applications that are willing to do the extra housekeeping of knowing the handle of the texture they are accessing and the per-thread info for the curent thread. These may be retrieved by the {\cf get_texture_handle()} and {\cf get_perthread_info()} methods, respectively. \apiend \apiitem{bool {\ce texture} (ustring filename, TextureOptions \&options,\\ \bigspc Runflag *runflags, int beginactive, int endactive,\\ \bigspc VaryingRef s, VaryingRef t,\\ \bigspc VaryingRef dsdx, VaryingRef dtdx,\\ \bigspc VaryingRef dsdy, VaryingRef dtdy,\\ \bigspc int nchannels, float *result,\\ \bigspc float *dresultds=NULL, float *dresultdt=NULL) \\[2ex] bool {\ce texture} (TextureHandle *texture_handle, Perthread *thread_info, \\ \bigspc TextureOptions \&options,\\ \bigspc Runflag *runflags, int beginactive, int endactive,\\ \bigspc VaryingRef s, VaryingRef t,\\ \bigspc VaryingRef dsdx, VaryingRef dtdx,\\ \bigspc VaryingRef dsdy, VaryingRef dtdy,\\ \bigspc int nchannels, float *result,\\ \bigspc float *dresultds=NULL, float *dresultdt=NULL) \\ } Perform filtered 2D texture lookups on a collection of positions all at once, which may be much more efficient than repeatedly calling the single-point version of {\cf texture()}. The parameters {\cf s}, {\cf t}, {\cf dsdx}, {\cf dtdx}, and {\cf dsdy}, {\cf dtdy} are now {\cf VaryingRef}'s that may refer to either a single or an array of values, as are many of the fields in the {\cf options}. Texture will be computed at indices {\cf beginactive} through {\cf endactive} (exclusive of the end), but only at indices where {\cf runflags[i]} is nonzero. Results will be stored at corresponding positions of {\cf result}, that is, {\cf result[i*nchannels ... (i+1)*nchannels-1]} (and similarly for {\cf dresultds} and {\cf dresultdt}, if they are not {\cf NULL}). This function returns {\cf true} upon success, or {\cf false} if the file was not found or could not be opened by any available ImageIO plugin. \apiend %\newpage \subsection{Volume Texture Lookups} \label{sec:texturesys:api:texture3d} \apiitem{bool {\ce texture3d} (ustring filename, TextureOpt \&options,\\ \bigspc\spc const Imath::V3f \&P, const Imath::V3f \&dPdx,\\ \bigspc\spc const Imath::V3f \&dPdy, const Imath::V3f \&dPdz,\\ \bigspc\spc int nchannels, float *result,\\ \bigspc\spc float *dresultds=NULL, float *dresultdt=NULL,\\ \bigspc\spc float *dresultdr=NULL)} \indexapi{texture3d} Perform a filtered 3D volumetric texture lookup on a position centered at 3D position {\cf P} from the texture identified by {\cf filename}, and using relevant texture {\cf options}. The filtered results will be stored in {\cf result[0..nchannels-1]}. We assume that this lookup will be part of an image that has pixel coordinates {\cf x} and {\cf y} and depth {\cf z}. By knowing how {\cf P} changes from pixel to pixel in the final image, and as we step in $z$ depth, we can properly \emph{filter} or antialias the texture lookups. This information is given via derivatives {\cf dPdx}, {\cf dPdy}, and {\cf dPdz} that define the changes in {\cf P} per unit of {\cf x}, {\cf y}, and {\cf z}, respectively. If it is impossible to know the derivatives, you may pass 0 for them, but in that case you will not receive an antialiased texture lookup. The {\cf P} coordinate and {\cf dPdx}, {\cf dPdy}, and {\cf dPdz} derivatives are assumed to be in some kind of common global coordinate system (usually \qkw{world} space) and will be automatically transformed into volume local coordinates, if such a transormation is specified in the volume file itself. If the {\cf dresultds}, {\cf dresultdt}, and {\cf dresultdr} parameters are not {\cf NULL} (the default), these specify locations in which to store the \emph{derivatives} of the texture lookup, i.e., the change of the filtered texture per unit of $s$, $t$ and $r$, respectively. Each must point to at least {\cf nchannels} contiguous floats. If they are {\cf NULL}, the derivative computations will not be performed. Fields within {\cf options} that are honored for 3D texture lookups include the following: \vspace{-12pt} \apiitem{int firstchannel} \vspace{10pt} The index of the first channel to look up from the texture. \apiend \vspace{-24pt} \apiitem{Wrap swrap, twrap, rwrap} \vspace{10pt} Specify the wrap modes for each direction, one of: {\cf WrapBlack}, {\cf WrapClamp}, {\cf WrapPeriodic}, {\cf WrapMirror}, or {\cf WrapDefault}. \apiend \vspace{-24pt} \apiitem{float swidth, twidth, rwidth} \vspace{10pt} For each direction, gives a multiplier for the derivatives. \apiend \vspace{-24pt} \apiitem{float sblur, tblur, rblur} \vspace{10pt} For each direction, specifies an additional amount of pre-blur to apply to the texture (\emph{after} derivatives are taken into account), expressed as a portion of the width of the texture. \apiend \vspace{-24pt} \apiitem{float fill} \vspace{10pt} Specifies the value that will be used for any color channels that are requested but not found in the file. For example, if you perform a 4-channel lookup on a 3-channel texture, the last channel will get the fill value. (Note: this behavior is affected by the \qkw{gray_to_rgb} attribute described in Section~\ref{sec:texturesys:attributes}.) \apiend \vspace{-24pt} \apiitem{const float *missingcolor} \vspace{10pt} If not NULL, specifies the color that will be returned for missing or broken textures (rather than being an error). \apiend \vspace{-24pt} \apiitem{float time} \vspace{10pt} A time value to use if the volume texture specifies a time-varying local transformation (default: 0). \apiend This function returns {\cf true} upon success, or {\cf false} if the file was not found or could not be opened by any available ImageIO plugin. \apiend \apiitem{bool {\ce texture3d} (TextureHandle *texture_handle, Perthread *thread_info, \\ \bigspc\spc TextureOpt \&opt, const Imath::V3f \&P, const Imath::V3f \&dPdx,\\ \bigspc\spc const Imath::V3f \&dPdy, const Imath::V3f \&dPdz,\\ \bigspc\spc int nchannels, float *result, float *dresultds=NULL,\\ \bigspc\spc float *dresultdt=NULL, float *dresultdr=NULL)} A slightly faster {\cf texture3d} call for applications that are willing to do the extra housekeeping of knowing the handle of the texture they are accessing and the per-thread info for the curent thread. These may be retrieved by the {\cf get_texture_handle()} and {\cf get_perthread_info()} methods, respectively. \apiend \apiitem{bool {\ce texture3d} (ustring filename, TextureOptions \&options,\\ \bigspc Runflag *runflags, int beginactive, int endactive,\\ \bigspc VaryingRef P, VaryingRef dPdx,\\ \bigspc VaryingRef dPdy, VaryingRef dPdz,\\ \bigspc int nchannels, float *result, float *dresultds=NULL,\\ \bigspc float *dresultdt=NULL,float *dresultdr=NULL)\\[2ex] bool {\ce texture3d} (TextureHandle *texture_handle, Perthread *thread_info, \\ \bigspc TextureOptions \&options,\\ \bigspc Runflag *runflags, int beginactive, int endactive,\\ \bigspc VaryingRef P, VaryingRef dPdx,\\ \bigspc VaryingRef dPdy, VaryingRef dPdz,\\ \bigspc int nchannels, float *result, float *dresultds=NULL,\\ \bigspc float *dresultdt=NULL, float *dresultdr=NULL)} Perform filtered 3D volumetric texture lookups on a collection of positions all at once, which may be much more efficient than repeatedly calling the single-point version of {\cf texture()}. The parameters {\cf P}, {\cf dPdx}, {\cf dPdy}, and {\cf dPdz} are now {\cf VaryingRef}'s that may refer to either a single or an array of values, as are all the fields in the {\cf options}. Texture will be computed at indices {\cf beginactive} through {\cf endactive} (exclusive of the end), but only at indices where {\cf runflags[i]} is nonzero. Results will be stored at corresponding positions of {\cf result}, that is, {\cf result[i*n ... (i+1)*n-1]} where $n$ is the number of channels requested by {\cf options.nchannels}. This function returns {\cf true} upon success, or {\cf false} if the file was not found or could not be opened by any available ImageIO plugin. \apiend %\newpage \subsection{Shadow Lookups} \label{sec:texturesys:api:shadow} \apiitem{bool {\ce shadow} (ustring filename, TextureOpt \&opt,\\ \bigspc const Imath::V3f \&P, const Imath::V3f \&dPdx,\\ \bigspc const Imath::V3f \&dPdy, int nchannels, float *result,\\ \bigspc float *dresultds=NULL, float *dresultdt=NULL)} \indexapi{shadow} Perform a shadow map lookup on a position centered at 3D coordinate {\cf P} (in a designated ``common'' space) from the shadow map identified by {\cf filename}, and using relevant texture {\cf options}. The filtered results will be stored in {\cf result[]}. We assume that this lookup will be part of an image that has pixel coordinates {\cf x} and {\cf y}. By knowing how {\cf P} changes from pixel to pixel in the final image, we can properly \emph{filter} or antialias the texture lookups. This information is given via derivatives {\cf dPdx} and {\cf dPdy} that define the changes in {\cf P} per unit of {\cf x} and {\cf y}, respectively. If it is impossible to know the derivatives, you may pass 0 for them, but in that case you will not receive an antialiased texture lookup. Fields within {\cf options} that are honored for 2D texture lookups include the following: \vspace{-12pt} \apiitem{float swidth, twidth} \vspace{10pt} For each direction, gives a multiplier for the derivatives. \apiend \vspace{-24pt} \apiitem{float sblur, tblur} \vspace{10pt} For each direction, specifies an additional amount of pre-blur to apply to the texture (\emph{after} derivatives are taken into account), expressed as a portion of the width of the texture. \apiend \vspace{-24pt} \apiitem{float bias} \vspace{10pt} Specifies the amount of \emph{shadow bias} to use --- this effectively ignores shadow occlusion that is closer than the bias amount to the surface, helping to eliminate self-shadowing artifacts. \apiend \vspace{-24pt} \apiitem{int samples} \vspace{10pt} Specifies the number of samples to use when evaluating the shadow map. More samples will give a smoother, less noisy, appearance to the shadows, but may also take longer to compute. \apiend This function returns {\cf true} upon success, or {\cf false} if the file was not found or could not be opened by any available ImageIO plugin. \apiend \apiitem{bool {\ce shadow} (TextureHandle *texture_handle, Perthread *thread_info, \\ \bigspc TextureOpt \&opt, const Imath::V3f \&P,\\ \bigspc const Imath::V3f \&dPdx, const Imath::V3f \&dPdy,\\ \bigspc int nchannels, float *result,\\ \bigspc float *dresultds=NULL, float *dresultdt=NULL)} A slightly faster {\cf shadow} call for applications that are willing to do the extra housekeeping of knowing the handle of the texture they are accessing and the per-thread info for the curent thread. These may be retrieved by the {\cf get_texture_handle()} and {\cf get_perthread_info()} methods, respectively. \apiend \apiitem{bool {\ce shadow} (ustring filename, TextureOptions \&options,\\ \bigspc Runflag *runflags, int beginactive, int endactive,\\ \bigspc VaryingRef P, VaryingRef dPdx,\\ \bigspc VaryingRef dPdy, int nchannels, float *result,\\ \bigspc float *dresultds=NULL, float *dresultdt=NULL)\\[2ex] bool {\ce shadow} (TextureHandle *texture_handle, Perthread *thread_info, \\ \bigspc TextureOptions \&options,\\ \bigspc Runflag *runflags, int beginactive, int endactive,\\ \bigspc VaryingRef P, VaryingRef dPdx,\\ \bigspc VaryingRef dPdy, int nchannels, float *result,\\ \bigspc float *dresultds=NULL, float *dresultdt=NULL)} Perform filtered shadow map lookups on a collection of positions all at once, which may be much more efficient than repeatedly calling the single-point version of {\cf shadow()}. The parameters {\cf P}, {\cf dPdx}, and {\cf dPdy} are now {\cf VaryingRef}'s that may refer to either a single or an array of values, as are many the fields in the {\cf options}. Shadow lookups will be computed at indices {\cf beginactive} through {\cf endactive} (exclusive of the end), but only at indices where {\cf runflags[i]} is nonzero. Results will be stored at corresponding positions of {\cf result}, that is, {\cf result[i*n ... (i+1)*n-1]} where $n$ is the number of channels requested by {\cf options.nchannels}. This function returns {\cf true} upon success, or {\cf false} if the file was not found or could not be opened by any available ImageIO plugin. \apiend %\newpage \subsection{Environment Lookups} \label{sec:texturesys:api:environment} \apiitem{bool {\ce environment} (ustring filename, TextureOpt \&options,\\ \bigspc\spc const Imath::V3f \&R, const Imath::V3f \&dRdx,\\ \bigspc\spc const Imath::V3f \&dRdy, int nchannels, float *result,\\ \bigspc\spc float *dresultds=NULL, float *dresultdt=NULL)} \indexapi{environment} Perform a filtered directional environment map lookup in the direction of vector {\cf R}, from the texture identified by {\cf filename}, and using relevant texture {\cf options}. The filtered results will be stored in {\cf result[]}. We assume that this lookup will be part of an image that has pixel coordinates {\cf x} and {\cf y}. By knowing how {\cf R} changes from pixel to pixel in the final image, we can properly \emph{filter} or antialias the texture lookups. This information is given via derivatives {\cf dRdx} and {\cf dRdy} that define the changes in {\cf R} per unit of {\cf x} and {\cf y}, respectively. If it is impossible to know the derivatives, you may pass 0 for them, but in that case you will not receive an antialiased texture lookup. Fields within {\cf options} that are honored for 3D texture lookups include the following: \vspace{-12pt} \apiitem{int firstchannel} \vspace{10pt} The index of the first channel to look up from the texture. \apiend \vspace{-24pt} \apiitem{float swidth, twidth} \vspace{10pt} For each direction, gives a multiplier for the derivatives. \apiend \vspace{-24pt} \apiitem{float sblur, tblur} \vspace{10pt} For each direction, specifies an additional amount of pre-blur to apply to the texture (\emph{after} derivatives are taken into account), expressed as a portion of the width of the texture. \apiend \vspace{-24pt} \apiitem{float fill} \vspace{10pt} Specifies the value that will be used for any color channels that are requested but not found in the file. For example, if you perform a 4-channel lookup on a 3-channel texture, the last channel will get the fill value. (Note: this behavior is affected by the \qkw{gray_to_rgb} attribute described in Section~\ref{sec:texturesys:attributes}.) \apiend This function returns {\cf true} upon success, or {\cf false} if the file was not found or could not be opened by any available ImageIO plugin. \apiend \apiitem{bool {\ce environment} (TextureHandle *texture_handle, Perthread *thread_info, \\ \bigspc\spc TextureOpt \&opt, const Imath::V3f \&R,\\ \bigspc\spc const Imath::V3f \&dRdx, onst Imath::V3f \&dRdy,\\ \bigspc\spc int nchannels, float *result,\\ \bigspc\spc float *dresultds=NULL, float *dresultdt=NULL)} A slightly faster {\cf environment} call for applications that are willing to do the extra housekeeping of knowing the handle of the texture they are accessing and the per-thread info for the curent thread. These may be retrieved by the {\cf get_texture_handle()} and {\cf get_perthread_info()} methods, respectively. \apiend \apiitem{bool {\ce environment} (ustring filename, TextureOptions \&options,\\ \bigspc\spc Runflag *runflags, int beginactive, int endactive,\\ \bigspc\spc VaryingRef R, VaryingRef dRdx,\\ \bigspc\spc VaryingRef dRdy, int nchannels, float *result,\\ \bigspc\spc float *dresultds=NULL, float *dresultdt=NULL)\\[2ex] bool {\ce environment} (TextureHandle *texture_handle, Perthread *thread_info, \\ \bigspc\spc TextureOptions \&options,\\ \bigspc\spc Runflag *runflags, int beginactive, int endactive,\\ \bigspc\spc VaryingRef R, VaryingRef dRdx,\\ \bigspc\spc VaryingRef dRdy, int nchannels, float *result,\\ \bigspc\spc float *dresultds=NULL, float *dresultdt=NULL)} Perform filtered directional environment map lookups on a collection of directions all at once, which may be much more efficient than repeatedly calling the single-point version of {\cf environment()}. The parameters {\cf R}, {\cf dRdx}, and {\cf dRdy} are now {\cf VaryingRef}'s that may refer to either a single or an array of values, as are many the fields in the {\cf options}. Results will be computed at indices {\cf beginactive} through {\cf endactive} (exclusive of the end), but only at indices where {\cf runflags[i]} is nonzero. Results will be stored at corresponding positions of {\cf result}, that is, {\cf result[i*n ... (i+1)*n-1]} where $n$ is the number of channels requested by {\cf options.nchannels}. This function returns {\cf true} upon success, or {\cf false} if the file was not found or could not be opened by any available ImageIO plugin. \apiend %\newpage \subsection{Texture Metadata and Raw Texels} \label{sec:texturesys:api:gettextureinfo} \label{sec:texturesys:api:getimagespec} \apiitem{bool {\ce get_texture_info} (ustring filename, int subimage, \\ \bigspc\spc\spc ustring dataname, TypeDesc datatype, void *data) \\ bool {\ce get_texture_info} (TextureHandle *texture_handle, \\ \bigspc\spc\spc Perthread *thread_info, int subimage, \\ \bigspc\spc\spc ustring dataname, TypeDesc datatype, void *data)} Retrieves information about the texture, either named by {\cf filename} or specified by an opaque handle returned by {\cf get_texture_handle()}. The {\cf dataname} is a keyword indcating what information should be retrieved, {\cf datatype} is the type of data expected, and {\cf data} points to caller-owned memory where the results should be placed. It is up to the caller to ensure that {\cf data} contains enough space to hold an item of the requested {\cf datatype}. The return value is {\cf true} if {\cf get_texture_info()} is able to answer the query -- that is, find the requested {\cf dataname} for the texture and it matched the requested {\cf datatype}. If the requested data was not found, or was not of the right data type, {\cf get_texture_info()} will return {\cf false}. Except for the \qkw{exists} and \qkw{udim} queries, file that does not exist or could not be read properly as an image also constitutes a query failure that will return {\cf false}. Supported {\cf dataname} values include: \begin{description} \item[\spc] \spc \vspace{-12pt} \item[\rm \kw{exists}] Stores the value 1 (as an {\cf int} if the file exists and is an image format that \product can read, or 0 if the file does not exist, or could not be properly read as a texture. Note that unlike all other queries, this query will ``succeed'' (return {\cf true}) even if the file does not exist. \item[\rm \kw{udim}] Stores the value 1 (as an {\cf int}) if the file is a ``virtual UDIM'' or texture atlas file (as described in Section~\ref{sec:texturesys:udim}) or 0 otherwise. \item[\rm \kw{subimages}] The number of subimages/faces in the file, as an integer. \item[\rm \kw{resolution}] The resolution of the texture file, which is an array of 2 integers (described as {\cf TypeDesc(INT,2)}). \item[\rm \kw{resolution} (int[3])] The 3D resolution of the texture file, which is an array of 3 integers (described as {\cf TypeDesc(INT,3)}) The third value will e 1 unless it's a volumetric (3D) image. \item[\rm \kw{miplevels}] The number of MIPmap levels for the specified subimage (an integer). \item[\rm \kw{texturetype}] A string describing the type of texture of the given file, which describes how the texture may be used (also which texture API call is probably the right one for it). This currently may return one of: \qkw{unknown}, \qkw{Plain Texture}, \qkw{Volume Texture}, \qkw{Shadow}, or \qkw{Environment}. \item[\rm \kw{textureformat}] A string describing the format of the given file, which describes the kind of texture stored in the file. This currently may return one of: \qkw{unknown}, \qkw{Plain Texture}, \qkw{Volume Texture}, \qkw{Shadow}, \qkw{CubeFace Shadow}, \qkw{Volume Shadow}, \qkw{LatLong Environment}, or \qkw{CubeFace Environment}. Note that there are several kinds of shadows and environment maps, all accessible through the same API calls. \item[\rm \kw{channels}] The number of color channels in the file (an integer). \item[\rm \kw{format}] The native data format of the pixels in the file (an integer, giving the {\cf TypeDesc::BASETYPE} of the data). Note that this is not necessarily the same as the data format stored in the image cache. \item[\rm \kw{cachedformat}] The native data format of the pixels as stored in the image cache (an integer, giving the {\cf TypeDesc::BASETYPE} of the data). Note that this is not necessarily the same as the native data format of the file. \item[\rm \kw{datawindow}] Returns the pixel data window of the image, which is either an array of 4 integers (returning xmin, ymin, xmax, ymax) or an array of 6 integers (returning xmin, ymin, zmin, xmax, ymax, zmax). The $z$ values may be useful for 3D/volumetric images; for 2D images they will be 0). \item[\rm \kw{displaywindow}] Returns the display (a.k.a.\ full) window of the image, which is either an array of 4 integers (returning xmin, ymin, xmax, ymax) or an array of 6 integers (returning xmin, ymin, zmin, xmax, ymax, zmax). The $z$ values may be useful for 3D/volumetric images; for 2D images they will be 0). \item[\rm \kw{worldtocamera}] The viewing matrix, which is a $4 \times 4$ matrix (an {\cf Imath::M44f}, described as {\cf TypeDesc(FLOAT,MATRIX)}), giving the world-to-camera 3D transformation matrix that was used when the image was created. Generally, only rendered images will have this. \item[\rm \kw{worldtoscreen}] The projection matrix, which is a $4 \times 4$ matrix (an {\cf Imath::M44f}, described as {\cf TypeDesc(FLOAT,MATRIX)}), giving the matrix that projected points from world space into a 2D screen coordinate system where $x$ and $y$ range from $-1$ to $+1$. Generally, only rendered images will have this. \item[\rm \kw{averagecolor}] If available in the metadata (generally only for files that have been processed by {\cf maketx}), this will return the average color of the texture (into an array of floats). \item[\rm \kw{averagealpha}] If available in the metadata (generally only for files that have been processed by {\cf maketx}), this will return the average alpha value of the texture (into a float). \item[\rm \kw{constantcolor}] If the metadata (generally only for files that have been processed by {\cf maketx}) indicates that the texture has the same values for all pixels in the texture, this will retrieve the constant color of the texture (into an array of floats). A non-constant image (or one that does not have the special metadata tag identifying it as a constant texture) will fail this query (return false). \item[\rm \kw{constantalpha}] If the metadata indicates that the texture has the same values for all pixels in the texture, this will retrieve the constant alpha value of the texture (into a float). A non-constant image (or one that does not have the special metadata tag identifying it as a constant texture) will fail this query (return false). \item[\rm \kw{stat:tilesread}] Number of tiles read from this file ({\cf int64}). \item[\rm \kw{stat:bytesread}] Number of bytes of uncompressed pixel data read from this file ({cf int64}). \item[\rm \kw{stat:redundant_tiles}] Number of times a tile was read, where the same tile had been rad before. ({\cf int64}). \item[\rm \kw{stat:redundant_bytesread}] Number of bytes (of uncompressed pixel data) in tiles that were read redundantly. ({\cf int64}). \item[\rm \kw{stat:redundant_bytesread}] Number of tiles read from this file ({\cf int}). \item[\rm \kw{stat:timesopened}] Number of times this file was opened ({\cf int}). \item[\rm \kw{stat:iotime}] Time (in seconds) spent on all I/O for this file ({\cf float}). \item[\rm \kw{stat:mipsused}] Stores 1 if any MIP levels beyond the highest resolution were accesed, otherwise 0. ({\cf int}) \item[\rm \kw{stat:is_duplicate}] Stores 1 if this file was a duplicate of another image, otherwise 0. ({\cf int}) \item[Anything else] -- For all other data names, the the metadata of the image file will be searched for an item that matches both the name and data type. \end{description} \apiend \apiitem{bool {\ce get_imagespec} (ustring filename, int subimage, ImageSpec \&spec) \\ bool {\ce get_imagespec} (TextureHandle *texture_handle, Perthread *thread_info,\\ \bigspc\bigspc int subimage, ImageSpec \&spec)} If the image (specified by either name or handle) is found and able to be opened by an available image format plugin, this function copies its image specification into {\cf spec} and returns {\cf true}. Otherwise, if the file is not found, could not be opened, or is not of a format readable by any plugin that could be found, the return value is {\cf false}. \apiend \apiitem{const ImageSpec * {\ce imagespec} (ustring filename, int subimage) \\ const ImageSpec * {\ce imagespec} (TextureHandle *texture_handle, \\ \bigspc\bigspc Perthread *thread_info, int subimage)} If the named image is found and able to be opened by an available image format plugin, and the designated subimage exists, this function returns a pointer to an \ImageSpec that describes it. Otherwise, if the file is not found, could not be opened, is not of a format readable by any plugin that could be find, or the designated subimage did not exist in the file, the return value is NULL. This method is much more efficient than {\cf get_imagespec()}, since it just returns a pointer to the spec held internally by the underlying \ImageCache (rather than copying the spec to the user's memory). However, the caller must beware that the pointer is only valid as long as nobody (even other threads) calls {\cf invalidate()} on the file, or {\cf invalidate_all()}, or destroys the \TextureSystem. \apiend \apiitem{bool {\ce get_texels} (ustring filename, TextureOpt \&options, int miplevel, \\ \bigspc int xbegin, int xend, int ybegin, int yend,\\ \bigspc int zbegin, int zend, int chbegin, int chend,\\ \bigspc TypeDesc format, void *result) \\ bool {\ce get_texels} (TextureHandle *texture_handle, PerThread *thread_info, \\ \bigspc Perthread *thread_info, TextureOpt \&options, int miplevel, \\ \bigspc int xbegin, int xend, int ybegin, int yend,\\ \bigspc int zbegin, int zend, int chbegin, int chend,\\ \bigspc TypeDesc format, void *result)} For a texture identified by either name or handle, retrieve a rectangle of raw unfiltered texels at the named MIP-map level, storing the texel values beginning at the address specified by result. Note that the face/subimage is communicated through {\kw options.subimage}. The texel values will be converted to the type specified by format. It is up to the caller to ensure that result points to an area of memory big enough to accommodate the requested rectangle (taking into consideration its dimensions, number of channels, and data format). The rectangular region to be retrieved includes {\cf begin} but does not include {\cf end} (much like STL begin/end usage). Requested pixels that are not part of the valid pixel data region of the image file will be filled with zero values. Fields within {\cf options} that are honored for raw texel retieval include the following: \vspace{-12pt} \apiitem{int subimage} \vspace{10pt} The subimage to retrieve. \apiend % FIXME -- we should support this %\vspace{-24pt} %\apiitem{Wrap swrap, twrap} %\vspace{10pt} %Specify the \emph{wrap mode} for each direction, one of: %{\cf WrapBlack}, {\cf WrapClamp}, {\cf WrapPeriodic}, {\cf WrapMirror}, %or {\cf WrapDefault}. %\apiend \vspace{-24pt} \apiitem{float fill} \vspace{10pt} Specifies the value that will be used for any color channels that are requested but not found in the file. For example, if you perform a 4-channel lookup on a 3-channel texture, the last channel will get the fill value. (Note: this behavior is affected by the \qkw{gray_to_rgb} attribute described in Section~\ref{sec:texturesys:attributes}.) \apiend Return true if the file is found and could be opened by an available ImageIO plugin, otherwise return false. \apiend \apiitem{std::string {\ce resolve_filename} (const std::string \&filename)} Returns the true path to the given file name, with searchpath logic applied. \apiend \subsection{Miscellaneous -- Statistics, errors, flushing the cache} \label{sec:texturesys:api:geterror} \label{sec:texturesys:api:getstats} \label{sec:texturesys:api:resetstats} \label{sec:texturesys:api:invalidate} \apiitem{std::string {\ce geterror} ()} \index{error checking} If any other API routines return {\cf false}, indicating that an error has occurred, this routine will retrieve the error and clear the error status. If no error has occurred since the last time {\cf geterror()} was called, it will return an empty string. \apiend \apiitem{std::string {\ce getstats} (int level=1, bool icstats=true)} Returns a big string containing useful statistics about the \ImageCache operations, suitable for saving to a file or outputting to the terminal. The {\cf level} indicates the amount of detail in the statistics, with higher numbers (up to a maximum of 5) yielding more and more esoteric information. If {\cf icstats} is true, the returned string will also contain all the statistics of the underlying \ImageCache, but if false will only contain texture-specific statistics. \apiend \apiitem{void {\ce reset_stats} ()} Reset most statistics to be as they were with a fresh \ImageCache. Caveat emptor: this does not flush the cache itelf, so the resulting statistics from the next set of texture requests will not match the number of tile reads, etc., that would have resulted from a new \ImageCache. \apiend \apiitem{void {\ce invalidate} (ustring filename)} Invalidate any loaded tiles or open file handles associated with the filename, so that any subsequent queries will be forced to re-open the file or re-load any tiles (even those that were previously loaded and would ordinarily be reused). A client might do this if, for example, they are aware that an image being held in the cache has been updated on disk. This is safe to do even if other procedures are currently holding reference-counted tile pointers from the named image, but those procedures will not get updated pixels until they release the tiles they are holding. \apiend \apiitem{void {\ce invalidate_all} (bool force=false)} Invalidate all loaded tiles and open file handles, so that any subsequent queries will be forced to re-open the file or re-load any tiles (even those that were previously loaded and would ordinarily be reused). A client might do this if, for example, they are aware that an image being held in the cache has been updated on disk. This is safe to do even if other procedures are currently holding reference-counted tile pointers from the named image, but those procedures will not get updated pixels until they release the tiles they are holding. If force is true, everything will be invalidated, no matter how wasteful it is, but if force is false, in actuality files will only be invalidated if their modification times have been changed since they were first opened. \apiend \subsection{UDIM and texture atlases} \label{sec:texturesys:udim} \NEW % 1.7 The {\cf texture()} call supports virtual filenames that expand per lookup for UDIM and other tiled texture atlas techniques. The substitutions will occur if the texture filename initially passed to {\cf texture()} does not exist as a concrete file and contains one or more of the following substrings: \medskip %\noindent \begin{tabular}{p{0.75in} p{4.75in}} {\cf } & 1001 + \emph{utile} + \emph{vtile}*10 \\ {\cf } & \emph{utile} \\ {\cf } & \emph{vtile} \\ {\cf } & \emph{utile} + 1 \\ {\cf } & \emph{vtile} + 1 \\ \end{tabular} \medskip \noindent where the tile numbers are derived from the input $u,v$ texture coordinates as follows: \begin{code} // Each unit square of texture is a different tile utile = max (0, int(u)); vtile = max (0, int(v)); // Re-adjust the texture coordinates to the offsets within the tile u = u - utile; v = v - vtile; \end{code} \smallskip \noindent Example: \begin{code} ustring filename ("paint..tif"); float s = 1.4, t = 3.8; texsys->texture (filename, s, t, ...); \end{code} \noindent will retrieve from file \qkw{paint.1032.tif} at coordinates $(0.4,0.8)$. \smallskip Please note that most other calls, including most queries for {\cf get_texture_info()}, will fail with one of these special filenames, since it's not a real file and the system doesn't know which concrete file you it corresponds to in the absence of specific texture coordinates. \index{Texture System|)} \chapwidthend openimageio-1.7.17~dfsg0.orig/src/doc/CLA-CORPORATE0000644000175000017500000001656313151711064017552 0ustar mfvmfvSoftware Grant and Corporate Contributor License Agreement ("Agreement") (modeled after http://www.apache.org/licenses/ v r190612) Thank you for your interest in OpenImageIO (the "Project", which collectively includes its authors, contributors, and any encompassing organization). In order to clarify the intellectual property license granted with Contributions from any person or entity, the Project must have a Contributor License Agreement (CLA) on file that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of the Project and its users; it does not change your rights to use your own Contributions for any other purpose. This version of the Agreement allows an entity (the "Corporation") to submit Contributions to the Project, to authorize Contributions submitted by its designated employees to the Project, and to grant copyright and patent licenses thereto. If you have not already done so, sign this agreement, scan it, and email to info@openimageio.org. Or contact that email to request a FAX number or US postal address to mail an original. Please read this document carefully before signing and keep a copy for your records. Corporation name: ________________________________________________ Corporation address: ________________________________________________ ________________________________________________ ________________________________________________ Point of Contact: ________________________________________________ E-Mail: ________________________________________________ Telephone: _____________________ Fax: _____________________ You accept and agree to the following terms and conditions for Your present and future Contributions submitted to the Project. In return, the Project shall not use Your Contributions in a way that is contrary to the public benefit or inconsistent with its nonprofit status and bylaws in effect at the time of the Contribution. Except for the license granted herein to the Project and recipients of software distributed by the Project, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with the Project. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "Contribution" shall mean the code, documentation or other original works of authorship expressly identified in Schedule B, as well as any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to the Project for inclusion in, or documentation of, any of the products owned or managed by the Project (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Project or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Project for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to the Project and to recipients of software distributed by the Project a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to the Project and to recipients of software distributed by the Project a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) were submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent that You are legally entitled to grant the above license. You represent further that each employee of the Corporation designated on Schedule A below (or in a subsequent written modification to that Schedule) is authorized to submit Contributions on behalf of the Corporation. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 7. Should You wish to submit work that is not Your original creation, You may submit it to the Project separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. It is your responsibility to notify the Project when any change is required to the list of designated employees authorized to submit Contributions on behalf of the Corporation, or to the Corporation's Point of Contact with the Project. Please sign: __________________________________ Date: _______________ Title: __________________________________ Corporation: __________________________________ Schedule A [Initial list of designated employees. NB: authorization is not tied to particular Contributions.] Schedule B [Identification of optional concurrent software grant. Would be left blank or omitted if there is no concurrent software grant.] openimageio-1.7.17~dfsg0.orig/src/doc/imagecache.tex0000644000175000017500000011760313151711064020461 0ustar mfvmfv\chapter{Cached Images} \label{chap:imagecache} \index{Image Cache|(} \section{Image Cache Introduction and Theory of Operation} \label{sec:imagecache:intro} \ImageCache is a utility class that allows an application to read pixels from a large number of image files while using a remarkably small amount of memory and other resources. Of course it is possible for an application to do this directly using \ImageInput objects. But \ImageCache offers the following advantages: \begin{itemize} \item \ImageCache presents an even simpler user interface than \ImageInput --- the only supported operations are asking for an \ImageSpec describing a subimage in the file, retrieving for a block of pixels, and locking/reading/releasing individual tiles. You refer to images by filename only; you don't need to keep track of individual file handles or \ImageInput objects. You don't need to explicitly open or close files. \item The \ImageCache is completely thread-safe; if multiple threads are accessing the same file, the \ImageCache internals will handle all the locking and resource sharing. \item No matter how many image files you are accessing, the \ImageCache will maintain a reasonable number of simultaneously-open files, automatically closing files that have not been needed recently. \item No matter how large the total pixels in all the image files you are dealing with are, the \ImageCache will use only a small amount of memory. It does this by loading only the individual tiles requested, and as memory allotments are approached, automatically releasing the memory from tiles that have not been used recently. \end{itemize} In short, if you have an application that will need to read pixels from many large image files, you can rely on \ImageCache to manage all the resources for you. It is reasonable to access thousands of image files totalling hundreds of GB of pixels, efficiently and using a memory footprint on the order of 50 MB. \newpage Below are some simple code fragments that shows \ImageCache in action: \medskip \begin{code} #include OIIO_NAMESPACE_USING // Create an image cache and set some options ImageCache *cache = ImageCache::create (); cache->attribute ("max_memory_MB", 500.0); cache->attribute ("autotile", 64); // Get a block of pixels from a file. // (for brevity of this example, let's assume that 'size' is the // number of channels times the number of pixels in the requested region) float pixels[size]; cache->get_pixels ("file1.jpg", 0, 0, xbegin, xend, ybegin, yend, zbegin, zend, TypeDesc::FLOAT, pixels); // Get information about a file ImageSpec spec; bool ok = cache->get_imagespec ("file2.exr", spec); if (ok) std::cout << "resolution is " << spec.width << "x" << "spec.height << "\n"; // Request and hold a tile, do some work with its pixels, then release ImageCache::Tile *tile; tile = cache->get_tile ("file2.exr", 0, 0, x, y, z); // The tile won't be freed until we release it, so this is safe: TypeDesc format; void *p = cache->tile_pixels (tile, format); // Now p points to the raw pixels of the tile, whose data format // is given by 'format'. cache->release_tile (tile); // Now cache is permitted to free the tile when needed // Note that all files were referenced by name, we never had to open // or close any files, and all the resource and memory management // was automatic. ImageCache::destroy (cache); \end{code} \newpage \section{ImageCache API} \label{sec:imagecache:api} \subsection{Creating and destroying an image cache} \label{sec:imagecache:api:createdestroy} \ImageCache is an abstract API described as a pure virtual class. The actual internal implementation is not exposed through the external API of \product. Because of this, you cannot construct or destroy the concrete implementation, so two static methods of \ImageCache are provided: \apiitem{static ImageCache *ImageCache::{\ce create} (bool shared=true)} Creates a new \ImageCache and returns a pointer to it. If {\cf shared} is {\cf true}, {\cf create()} will return a pointer to a shared \ImageCache (so that multiple parts of an application that request an \ImageCache will all end up with the same one). If {\cf shared} is {\cf false}, a completely unique \ImageCache will be created and returned. \apiend \apiitem{static void ImageCache::{\ce destroy} (ImageCache *x, bool teardown=false)} Destroys an allocated \ImageCache, including freeing all system resources that it holds. This is necessary to ensure that the memory is freed in a way that matches the way it was allocated within the library. Note that simply using {\cf delete} on the pointer will not always work (at least, not on some platforms in which a DSO/DLL can end up using a different allocator than the main program). It is safe to destroy even a shared \ImageCache, as the implementation of {\cf destroy()} will recognize a shared one and only truly release its resources if it has been requested to be destroyed as many times as shared \ImageCache's were created. For a shared \ImageCache, if the {\cf teardown} parameter is {\cf true}, it will try to truly destroy the shared cache if nobody else is still holding a reference (otherwise, it will leave it intact). \apiend \subsection{Setting options and limits for the image cache} \label{sec:imagecache:api:attribute} The following member functions of \ImageCache allow you to set (and in some cases retrieve) options that control the overall behavior of the image cache: \apiitem{bool {\ce attribute} (string_view name, TypeDesc type, const void *val)} \indexapi{attribute} Sets an attribute (i.e., a property or option) of the \ImageCache. The {\cf name} designates the name of the attribute, {\cf type} describes the type of data, and {\cf val} is a pointer to memory containing the new value for the attribute. If the \ImageCache recognizes a valid attribute name that matches the type specified, the attribute will be set to the new value and {\cf attribute()} will return {\cf true}. If {\cf name} is not recognized as a valid attribute name, or if the types do not match (e.g., {\cf type} is {\cf TypeDesc::FLOAT} but the named attribute is a string), the attribute will not be modified, and {\cf attribute()} will return {\cf false}. Here are examples: \begin{code} ImageCache *ts; ... int maxfiles = 50; ts->attribute ("max_open_files", TypeDesc::INT, &maxfiles); const char *path = "/my/path"; ts->attribute ("searchpath", TypeDesc::STRING, &path); \end{code} Note that when passing a string, you need to pass a pointer to the {\cf char*}, not a pointer to the first character. (Rationale: for an {\cf int} attribute, you pass the address of the {\cf int}. So for a string, which is a {\cf char*}, you need to pass the address of the string, i.e., a {\cf char**}). The complete list of attributes can be found at the end of this section. \apiend \apiitem{bool {\ce attribute} (string_view name, int val) \\ bool {\ce attribute} (string_view name, float val) \\ bool {\ce attribute} (string_view name, double val) \\ bool {\ce attribute} (string_view name, string_view val)} Specialized versions of {\cf attribute()} in which the data type is implied by the type of the argument. For example, the following are equivalent to the example above for the general (pointer) form of {\cf attribute()}: \begin{code} ts->attribute ("max_open_files", 50); ts->attribute ("searchpath", "/my/path"); \end{code} \apiend \apiitem{bool {\ce getattribute} (string_view name, TypeDesc type, void *val)} \indexapi{getattribute} Gets the current value of an attribute of the \ImageCache. The {\cf name} designates the name of the attribute, {\cf type} describes the type of data, and {\cf val} is a pointer to memory where the user would like the value placed. If the \ImageCache recognizes a valid attribute name that matches the type specified, the attribute value will be stored at address {\cf val} and {\cf attribute()} will return {\cf true}. If {\cf name} is not recognized as a valid attribute name, or if the types do not match (e.g., {\cf type} is {\cf TypeDesc::FLOAT} but the named attribute is a string), no data will be written to {\cf val}, and {\cf attribute()} will return {\cf false}. Here are examples: \begin{code} ImageCache *ts; ... int maxfiles; ts->getattribute ("max_open_files", TypeDesc::INT, &maxfiles); const char *path; ts->getattribute ("searchpath", TypeDesc::STRING, &path); \end{code} Note that when passing a string, you need to pass a pointer to the {\cf char*}, not a pointer to the first character. Also, the {\cf char*} will end up pointing to characters owned by the \ImageCache; the caller does not need to ever free the memory that contains the characters. The complete list of attributes can be found at the end of this section. \apiend \apiitem{bool {\ce getattribute} (string_view name, int \&val) \\ bool {\ce getattribute} (string_view name, float \&val) \\ bool {\ce getattribute} (string_view name, double \&val) \\ bool {\ce getattribute} (string_view name, char **val) \\ bool {\ce getattribute} (string_view name, std::string \& val)} Specialized versions of {\cf getattribute()} in which the data type is implied by the type of the argument. For example, the following are equivalent to the example above for the general (pointer) form of {\cf getattribute()}: \begin{code} int maxfiles; ts->getattribute ("max_open_files", &maxfiles); const char *path; ts->getattribute ("searchpath", &path); \end{code} \apiend \subsubsection*{Image cache attributes} Recognized attributes include the following: \apiitem{int max_open_files} The maximum number of file handles that the image cache will hold open simultaneously. (Default = 100) \apiend \apiitem{float max_memory_MB} The maximum amount of memory (measured in MB) that the image cache will use for its ``tile cache.'' (Default: 256.0 MB) \apiend \apiitem{string searchpath} The search path for images: a colon-separated list of directories that will be searched in order for any image name that is not specified as an absolute path. (Default: no search path.) \apiend \apiitem{string plugin_searchpath} The search path for plugins: a colon-separated list of directories that will be searched in order for any OIIO plugins, if not found in OIIO's ``lib'' directory.) (Default: no additional search path.) \apiend \apiitem{int autotile \\ int autoscanline} \label{imagecacheattr:autotile} These attributes control how the image cache deals with images that are not ``tiled'' (i.e., are stored as scanlines). If {\cf autotile} is set to 0 (the default), an untiled image will be treated as if it were a single tile of the resolution of the whole image. This is simple and fast, but can lead to poor cache behavior if you are simultaneously accessing many large untiled images. If {\cf autotile} is nonzero (e.g., 64 is a good recommended value), any untiled images will be read and cached as if they were constructed in tiles of size: \begin{tabular}{p{2in} p{3in}} {\cf autotile} $\times$ {\cf autotile} & if {\cf autoscanline} is 0 \\ {\cf width} $\times$ {\cf autotile} & if {\cf autoscanline} is nonzero. \\ \end{tabular} In both cases, this should lead more efficient caching. The {\cf autoscanline} determines whether the ``virtual tiles'' in the cache are square (if {\cf autoscanline} is 0, the default) or if they will be as wide as the image (but only {\cf autotile} scanlines high). You should try in your application to see which leads to higher performance. \apiend \apiitem{int automip} If {\cf automip} is set to 0 (the default), an untiled single-subimage file will only be able to utilize that single subimage. If {\cf automip} is nonzero, any untiled, single-subimage (un-MIP-mapped) images will have lower-resolution MIP-map levels generated on-demand if pixels are requested from the lower-res subimages (that don't really exist). Essentially this makes the \ImageCache pretend that the file is MIP-mapped even if it isn't. \apiend \apiitem{int forcefloat} If set to nonzero, all image tiles will be converted to {\cf float} type when stored in the image cache. This can be helpful especially for users of \ImageBuf who want to simplify their image manipulations to only need to consider {\cf float} data. The default is zero, meaning that image pixels are not forced to be {\cf float} when in cache. \apiend \apiitem{int accept_untiled} When nonzero (the default), \ImageCache accepts untiled images as usual. When set to zero, \ImageCache will reject untiled images with an error condition, as if the file could not be properly read. This is sometimes helpful for applications that want to enforce use of tiled images only. \apiend \apiitem{int accept_unmipped} When nonzero (the default), \ImageCache accepts un-MIPmapped images as usual. When set to zero, \ImageCache will reject un-MIPmapped images with an error condition, as if the file could not be properly read. This is sometimes helpful for applications that want to enforce use of MIP-mapped images only. \apiend \apiitem{int failure_retries} When an {\cf open()} or {\cf read_tile()} calls fails, pause and try again, up to {\cf failure_retries} times before truly returning a failure. This is meant to address spooky disk or network failures. The default is zero, meaning that failures of open or tile reading will immediately return as a failure. \apiend \apiitem{int deduplicate} When nonzero, the \ImageCache will notice duplicate images under different names if their headers contain a SHA-1 fingerprint (as is done with \maketx-produced textures) and handle them more efficiently by avoiding redundant 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. \apiend \apiitem{string substitute_image} When set to anything other than the empty string, the \ImageCache will use the named image in place of \emph{all} other images. This allows you to run an app using OIIO and (if you can manage to get this option set) automagically substitute a grid, zone plate, or other special debugging image for all image/texture use. \apiend \apiitem{int unassociatedalpha} When nonzero, will request that image format readers try to leave input images with unassociated alpha as they are, rather than automatically converting to associated alpha upon reading the pixels. The default is 0, meaning that the automatic conversion will take place. \apiend \apiitem{int max_errors_per_file} \NEW % 1.7 The maximum number of errors that will be printed for each file. The default is 100. If your output is cluttered with error messages and after the first few for each file you aren't getting any helpful additional information, this can cut down on the clutter and the runtime. \apiend \apiitem{string options} This catch-all is simply a comma-separated list of {\cf name=value} settings of named options. For example, \begin{code} ic->attribute ("options", "max_memory_MB=512.0,autotile=1"); \end{code} \apiend \apiitem{int max_open_files} The maximum number of file handles that the image cache will hold open simultaneously. (Default = 100) \apiend \apiitem{int total_files {\rm ~(read only)}} The total number of unique file names referenced by calls to the \ImageCache. \apiend \apiitem{string[] all_filenames {\rm ~(read only)}} An array that will be filled with the list of the names of all files referenced by calls to the \ImageCache. (The array is of {\cf ustring}s or {\cf char*}'s.) \apiend \apiitem{int64 stat:cache_memory_used {\rm ~(read only)}} Total bytes used by tile cache. \apiend \apiitem{int stat:tiles_created {\rm ~(read only)} \\ int stat:tiles_current {\rm ~(read only)} \\ int stat:tiles_peak {\rm ~(read only)}} Total times created, still allocated (at the time of the query), and the peak number of tiles in memory at any time. \apiend \apiitem{int stat:open_files_created {\rm ~(read only)} \\ int stat:open_files_current {\rm ~(read only)} \\ int stat:open_files_peak {\rm ~(read only)}} Total number of times a file was opened, number still opened (at the time of the query), and the peak number of files opened at any time. \apiend \apiitem{int stat:find_tile_calls {\rm ~(read only)}} Number of times a filename was looked up in the file cache. \apiend \apiitem{int64 stat:image_size {\rm ~(read only)}} Total size (uncompressed bytes of pixel data) of all images referenced by the \ImageCache. (Note: Prior to 1.7, this was called \qkw{stat:files_totalsize}.) \apiend \apiitem{int64 stat:file_size {\rm ~(read only)}} \NEW % 1.7 Total size of all files (as on disk, possibly compressed) of all images referenced by the \ImageCache. \apiend \apiitem{int64 stat:bytes_read {\rm ~(read only)}} Total size (uncompressed bytes of pixel data) read. \apiend \apiitem{int stat:unique_files {\rm ~(read only)}} Number of unique files opened. \apiend \apiitem{float stat:fileio_time {\rm ~(read only)}} Total I/O-related time (seconds). \apiend \apiitem{float stat:fileopen_time {\rm ~(read only)}} I/O time related to opening and reading headers (but not pixel I/O). \apiend \apiitem{float stat:file_locking_time {\rm ~(read only)}} Total time (across all threads) that threads blocked waiting for access to the file data structures. \apiend \apiitem{float stat:tile_locking_time {\rm ~(read only)}} Total time (across all threads) that threads blocked waiting for access to the tile cache data structures. \apiend \apiitem{float stat:find_file_time {\rm ~(read only)}} Total time (across all threads) that threads spent looking up files by name. \apiend \apiitem{float stat:find_tile_time {\rm ~(read only)}} Total time (across all threads) that threads spent looking up individual tiles. \apiend \bigskip \subsection{Opaque data for performance lookups} \label{sec:imagecache:api:opaque} \apiitem{Perthread * {\ce get_perthread_info} (Perthread *thread_info=NULL) \\ Perthread * {\ce create_perthread_info} () \\ void {\ce destroy_perthread_info} (Perthread *thread_info)} \indexapi{get_perthread_info} \indexapi{create_perthread_info} \indexapi{destroy_perthread_info} The \ImageCache implementation needs to maintain certain per-thread state, and some \ImageCache methods take an opaque {\cf Perthread} pointer to this record. There are three options for how to deal with it: 1. Don't worry about it at all: don't use the methods that want {\cf Perthread} pointers, or always pass {\cf NULL} for any {\cf Perthread*} arguments, and \ImageCache will do thread-specific-pointer retrieval as necessary (though at some small cost). 2. If your app already stores per-thread information of its own, you may call {\cf get_perthread_info(NULL)} to retrieve it for that thread, and then pass it into the functions that allow it (thus sparing them the need and expense of retrieving the thread-specific pointer). However, it is crucial that this pointer not be shared between multiple threads. In this case, the \ImageCache manages the storage, which will automatically be released when the thread terminates. 3. If your app also wants to manage the storage of the {\cf Perthread}, it can explicitly create one with {\cf create_perthread_info}, pass it around, and eventually be responsible for destroying it with {\cf destroy_perthread_info}. When managing the storage, the app may reuse the {\cf Perthread} for another thread after the first is terminated, but still may not use the same {\cf Perthread} for two threads running concurrently. \apiend \apiitem{ImageHandle * {\ce get_image_handle} (ustring filename,\\ \bigspc\bigspc\spc\spc Perthread *thread_info=NULL)} \indexapi{get_image_handle} Retrieve an opaque handle for fast \ImageCache lookups. The optional opaque pointer {\cf thread_info} is thread-specific information returned by {\cf get_perthread_info()}. Return {\cf NULL} if something has gone horribly wrong. \apiend \apiitem{bool {\ce good} (ImageHandle *file)} \indexapi{good} Return true if the image handle (previously returned by {\cf get_image_handle()}) is a valid image that can be subsequently read. \apiend \subsection{Getting information about images} \label{sec:imagecache:api:getimageinfo} \label{sec:imagecache:api:getimagespec} \apiitem{bool {\ce get_image_info} (ustring filename, int subimage, int miplevel, \\ \bigspc\bigspc ustring dataname, TypeDesc datatype, void *data) \\ bool {\ce get_image_info} (ImageHandle *file, Perthread *thread_info,\\ \bigspc\bigspc int subimage, int miplevel, \\ \bigspc\bigspc ustring dataname, TypeDesc datatype, void *data)} Retrieves information about the image named by {\cf filename} (or specified by the opaque image handle). The {\cf dataname} is a keyword indcating what information should be retrieved, {\cf datatype} is the type of data expected, and {\cf data} points to caller-owned memory where the results should be placed. It is up to the caller to ensure that {\cf data} contains enough space to hold an item of the requested {\cf datatype}. The return value is {\cf true} if {\cf get_image_info()} is able to answer the query -- that is, find the requested {\cf dataname} for the texture and it matched the requested {\cf datatype}. If the requested data was not found, or was not of the right data type, {\cf get_texture_info()} will return {\cf false}. Except for the \qkw{exists} query, file that does not exist or could not be read properly as an image also constitutes a query failure that will return {\cf false}. Supported {\cf dataname} values include: \begin{description} \item[\spc] \spc \vspace{-12pt} \item[\rm \kw{exists}] Stores the value 1 (as an {\cf int}) if the file exists and is an image format that \product can read, or 0 if the file does not exist, or could not be properly read as an image. Note that unlike all other queries, this query will ``succeed'' (return {\cf true}) even if the file does not exist. \item[\rm \kw{udim}] Stores the value 1 (as an {\cf int}) if the file is a ``virtual UDIM'' or texture atlas file (as described in Section~\ref{sec:texturesys:udim}) or 0 otherwise. \item[\rm \kw{subimages}] The number of subimages in the file, as an integer. \item[\rm \kw{resolution}] The resolution of the image file, which is an array of 2 integers (described as {\cf TypeDesc(INT,2)}). \item[\rm \kw{miplevels}] The number of MIPmap levels for the specified subimage (an integer). \item[\rm \kw{texturetype}] A string describing the type of texture of the given file, which describes how the texture may be used (also which texture API call is probably the right one for it). This currently may return one of: \qkw{unknown}, \qkw{Plain Texture}, \qkw{Volume Texture}, \qkw{Shadow}, or \qkw{Environment}. \item[\rm \kw{textureformat}] A string describing the format of the given file, which describes the kind of texture stored in the file. This currently may return one of: \qkw{unknown}, \qkw{Plain Texture}, \qkw{Volume Texture}, \qkw{Shadow}, \qkw{CubeFace Shadow}, \qkw{Volume Shadow}, \qkw{LatLong Environment}, or \qkw{CubeFace Environment}. Note that there are several kinds of shadows and environment maps, all accessible through the same API calls. \item[\rm \kw{channels}] The number of color channels in the file (an integer). \item[\rm \kw{format}] The native data format of the pixels in the file (an integer, giving the {\cf TypeDesc::BASETYPE} of the data). Note that this is not necessarily the same as the data format stored in the image cache. \item[\rm \kw{cachedformat}] The native data format of the pixels as stored in the image cache (an integer, giving the {\cf TypeDesc::BASETYPE} of the data). Note that this is not necessarily the same as the native data format of the file. \item[\rm \kw{datawindow}] Returns the pixel data window of the image, which is either an array of 4 integers (returning xmin, ymin, xmax, ymax) or an array of 6 integers (returning xmin, ymin, zmin, xmax, ymax, zmax). The $z$ values may be useful for 3D/volumetric images; for 2D images they will be 0). \item[\rm \kw{displaywindow}] Returns the display (a.k.a.\ full) window of the image, which is either an array of 4 integers (returning xmin, ymin, xmax, ymax) or an array of 6 integers (returning xmin, ymin, zmin, xmax, ymax, zmax). The $z$ values may be useful for 3D/volumetric images; for 2D images they will be 0). \item[\rm \kw{worldtocamera}] The viewing matrix, which is a $4 \times 4$ matrix (an {\cf Imath::M44f}, described as {\cf TypeDesc(FLOAT,MATRIX)}), giving the world-to-camera 3D transformation matrix that was used when the image was created. Generally, only rendered images will have this. \item[\rm \kw{worldtoscreen}] The projection matrix, which is a $4 \times 4$ matrix (an {\cf Imath::M44f}, described as {\cf TypeDesc(FLOAT,MATRIX)}), giving the matrix that projected points from world space into a 2D screen coordinate system where $x$ and $y$ range from $-1$ to $+1$. Generally, only rendered images will have this. \item[\rm \kw{averagecolor}] If available in the metadata (generally only for files that have been processed by {\cf maketx}), this will return the average color of the texture (into an array of floats). \item[\rm \kw{averagealpha}] If available in the metadata (generally only for files that have been processed by {\cf maketx}), this will return the average alpha value of the texture (into a float). \item[\rm \kw{constantcolor}] If the metadata (generally only for files that have been processed by {\cf maketx}) indicates that the texture has the same values for all pixels in the texture, this will retrieve the constant color of the texture (into an array of floats). A non-constant image (or one that does not have the special metadata tag identifying it as a constant texture) will fail this query (return false). \item[\rm \kw{constantalpha}] If the metadata indicates that the texture has the same values for all pixels in the texture, this will retrieve the constant alpha value of the texture (into a float). A non-constant image (or one that does not have the special metadata tag identifying it as a constant texture) will fail this query (return false). \item[\rm \kw{stat:tilesread}] Number of tiles read from this file ({\cf int64}). \item[\rm \kw{stat:bytesread}] Number of bytes of uncompressed pixel data read from this file ({cf int64}). \item[\rm \kw{stat:redundant_tiles}] Number of times a tile was read, where the same tile had been rad before. ({\cf int64}). \item[\rm \kw{stat:redundant_bytesread}] Number of bytes (of uncompressed pixel data) in tiles that were read redundantly. ({\cf int64}). \item[\rm \kw{stat:redundant_bytesread}] Number of tiles read from this file ({\cf int}). \item[\rm \kw{stat:image_size}] Size of the uncompressed image pixel data of this image, in bytes ({\cf int64}). \item[\rm \kw{stat:file_size}] Size of the disk file (possibly compressed) for this image, in bytes ({\cf int64}). \item[\rm \kw{stat:timesopened}] Number of times this file was opened ({\cf int}). \item[\rm \kw{stat:iotime}] Time (in seconds) spent on all I/O for this file ({\cf float}). \item[\rm \kw{stat:mipsused}] Stores 1 if any MIP levels beyond the highest resolution were accesed, otherwise 0. ({\cf int}) \item[\rm \kw{stat:is_duplicate}] Stores 1 if this file was a duplicate of another image, otherwise 0. ({\cf int}) \item[Anything else] -- For all other data names, the the metadata of the image file will be searched for an item that matches both the name and data type. \end{description} \apiend \apiitem{bool {\ce get_imagespec} (ustring filename, ImageSpec \&spec,\\ \bigspc\spc\spc int subimage=0, int miplevel=0, bool native=false)\\ bool {\ce get_imagespec} (ImageHandle *file, Perthread *thread_info,\\ \bigspc\bigspc ImageSpec \&spec, int subimage=0, \\ \bigspc\bigspc int miplevel=0, bool native=false)} If the image (and the specific subimage and MIP level) is found and able to be opened by an available image format plugin, and the designated subimage exists, this function copies its image specification for that subimage into {\cf spec} and returns {\cf true}. Otherwise, if the file is not found, could not be opened, is not of a format readable by any plugin that could be found, or the designated subimage did not exist in the file, the return value is {\cf false} and {\cf spec} will not be modified. The image may be specified either by name, or by the opaque handle returned by {\cf get_image_handle()}. If {\cf native} is {\cf false} (the default), then the spec retrieved will accurately describe the image stored internally in the cache, whereas if {\cf native} is {\cf true}, the spec retrieved will reflect the original file. These may differ due to use of certain \ImageCache settings such as \qkw{forcefloat} or \qkw{autotile}. \apiend \apiitem{const ImageSpec * {\ce imagespec} (ustring filename, int subimage=0, \\ \bigspc\bigspc\spc int miplevel=0, bool native=false) \\ const ImageSpec * {\ce imagespec} (ImageHandle *file, Perthread *thread_info,\\ \bigspc\bigspc int subimage=0, int miplevel=0, bool native=false)} If the image is found and able to be opened by an available image format plugin, and the designated subimage exists, this function returns a pointer to an \ImageSpec that describes it. Otherwise, if the file is not found, could not be opened, is not of a format readable by any plugin that could be find, or the designated subimage did not exist in the file, the return value is NULL. The image may be specified either by name, or by the opaque handle returned by {\cf get_image_handle()}. If {\cf native} is {\cf false} (the default), then the spec retrieved will accurately describe the image stored internally in the cache, whereas if {\cf native} is {\cf true}, the spec retrieved will reflect the original file. These may differ due to use of certain \ImageCache settings such as \qkw{forcefloat} or \qkw{autotile}. This method is much more efficient than {\cf get_imagespec()}, since it just returns a pointer to the spec held internally by the \ImageCache (rather than copying the spec to the user's memory). However, the caller must beware that the pointer is only valid as long as nobody (even other threads) calls {\cf invalidate()} on the file, or {\cf invalidate_all()}, or destroys the \ImageCache. \apiend \apiitem{std::string {\ce resolve_filename} (const std::string \&filename)} Returns the true path to the given file name, with searchpath logic applied. \apiend \subsection{Getting pixels} \label{sec:imagecache:api:getpixels} \apiitem{bool {\ce get\_pixels} (ustring filename, int subimage, int miplevel, \\ \bigspc\spc int xbegin, int xend, int ybegin, int yend, \\ \bigspc\spc int zbegin, int zend, \\ \bigspc\spc TypeDesc format, void *result) \\ bool {\ce get\_pixels} (ImageHandle *file, Perthread *thread_info, \\ \bigspc\spc int subimage, int miplevel, \\ \bigspc\spc int xbegin, int xend, int ybegin, int yend, \\ \bigspc\spc int zbegin, int zend, \\ \bigspc\spc TypeDesc format, void *result)} For an image specified by either name or handle, retrieve the rectangle of pixels of the designated {\cf subimage} and {\cf miplevel}, storing the pixel values beginning at the address specified by result. The pixel values will be converted to the type specified by {\cf format}. It is up to the caller to ensure that result points to an area of memory big enough to accommodate the requested rectangle (taking into consideration its dimensions, number of channels, and data format). The rectangular region to be retrieved includes {\cf begin} but does not include {\cf end} (much like STL begin/end usage). Requested pixels that are not part of the valid pixel data region of the image file will be filled with zero values. \apiend \apiitem{bool {\ce get\_pixels} (ustring filename, int subimage, int miplevel, \\ \bigspc\spc int xbegin, int xend, int ybegin, int yend, \\ \bigspc\spc int zbegin, int zend, int chbegin, int chend,\\ \bigspc\spc TypeDesc format, void *result, \\ \bigspc\spc stride_t xstride, stride_t ystride, stride_t zstride,\\ \bigspc\spc int cache_chbegin=0, int cache_chend=-1) \\ bool {\ce get\_pixels} (ImageHandle *file, Perthread *thread_info, \\ \bigspc\spc int subimage, int miplevel, \\ \bigspc\spc int xbegin, int xend, int ybegin, int yend, \\ \bigspc\spc int zbegin, int zend, int chbegin, int chend,\\ \bigspc\spc TypeDesc format, void *result, \\ \bigspc\spc stride_t xstride, stride_t ystride, stride_t zstride,\\ \bigspc\spc int cache_chbegin=0, int cache_chend=-1)} For an image specified by either name or handle, retrieve the rectangle of pixels and subset of channels of the designated {\cf subimage} and {\cf miplevel}, storing the pixel values beginning at the address specified by result and with the given strides. The pixel values will be converted to the type specified by {\cf format}. It is up to the caller to ensure that result points to an area of memory big enough to accommodate the requested rectangle (taking into consideration its dimensions, number of channels, data format, and strides). Any stride values set to {\cf AutoStride} will be assumed to indicate a contiguous data layout. The rectangular region and channel set to be retrieved includes {\cf begin} but does not include {\cf end} (much like STL begin/end usage). Requested pixels that are not part of the valid pixel data region of the image file will be filled with zero values. The optional parameters {\cf cache_chbegin} and {\cf cache_chend} can be used to tell the \ImageCache to read and cache a subset of channels (if not specified, all the channels of the file will be stored in the cached tile). \apiend \subsection{Dealing with tiles} \label{sec:imagecache:api:tiles} \apiitem{ImageCache::Tile * {\ce get_tile} (ustring filename, int subimage, int miplevel, \\ \bigspc \bigspc int x, int y, int z, int chbegin=0, int chend=-1) \\ ImageCache::Tile * {\ce get_tile} (ImageHandle *file, Perthread *thread_info, \\ \bigspc\bigspc int subimage, int miplevel, \\ \bigspc \bigspc int x, int y, int z, int chbegin=0, int chend=-1)} Find a tile of an image (identified by either name or handle) for the requested {\cf subimage}, pixel coordinates, and channel range (if {\cf chend < chbegin}, the full range of channels in the file will be used). An opaque pointer to the tile will be returned, or {\cf NULL} if no such file (or tile within the file) exists or can be read. The tile will not be purged from the cache until after {\cf release_tile()} is called on the tile pointer. This is thread-safe. \apiend \apiitem{void {\ce release_tile} (ImageCache::Tile *tile)} After finishing with a tile, {\cf release_tile()} will allow it to once again be purged from the tile cache if required. \apiend \apiitem{const void * {\ce tile_pixels} (ImageCache::Tile *tile, TypeDesc \&format)} For a tile retrived by {\cf get_tile()}, return a pointer to the pixel data itself, and also store in {\cf format} the data type that the pixels are internally stored in (which may be different than the data type of the pixels in the disk file). This method should only be called on a tile that has been requested by {\cf get_tile()} but has not yet been released with {\cf release_tile()}. \apiend \apiitem{void {\ce invalidate} (ustring filename)} Invalidate any loaded tiles or open file handles associated with the filename, so that any subsequent queries will be forced to re-open the file or re-load any tiles (even those that were previously loaded and would ordinarily be reused). A client might do this if, for example, they are aware that an image being held in the cache has been updated on disk. This is safe to do even if other procedures are currently holding reference-counted tile pointers from the named image, but those procedures will not get updated pixels until they release the tiles they are holding. \apiend \apiitem{void {\ce invalidate_all} (bool force=false)} Invalidate all loaded tiles and open file handles, so that any subsequent queries will be forced to re-open the file or re-load any tiles (even those that were previously loaded and would ordinarily be reused). A client might do this if, for example, they are aware that an image being held in the cache has been updated on disk. This is safe to do even if other procedures are currently holding reference-counted tile pointers from the named image, but those procedures will not get updated pixels until they release the tiles they are holding. If force is true, everything will be invalidated, no matter how wasteful it is, but if force is false, in actuality files will only be invalidated if their modification times have been changed since they were first opened. \apiend \subsection{Seeding the cache} \label{sec:imagecache:api:seeding} \apiitem{bool {\ce add_file} (ustring filename, \\ \bigspc ImageInput::Creator creator = NULL,\\ \bigspc const ImageSpec *config = NULL)} This method causes a file to be opened or added to the cache. There is no reason to use this method unless you are supplying a custom creator, or configuration, or both. If {\cf creator} is not NULL, it points to an {\cf ImageInput::Creator} that will be used rather than the default {\cf ImageInput::create()}, thus instead of reading from disk, creates and uses a custom ImageInput to generate the image. The {\cf creator} is a factory that creates the custom ImageInput and will be called like this: \begin{code} ImageInput *in = creator(); \end{code} Once created, the \ImageCache owns the \ImageInput and is responsible for destroying it when done. Custom \ImageInput's allow ``procedural'' images, among other things. Also, this is the method you use to set up a ``writeable'' \ImageCache images (perhaps with a type of \ImageInput that's just a stub that does as little as possible). If {\cf config} is not NULL, it points to an \ImageSpec with configuration options/hints that will be passed to the underlying {\cf ImageInput::open()} call. Thus, this can be used to ensure that the \ImageCache opens a call with special configuration options. This call (including any custom creator or configuration hints) will have no effect if there's already an image by the same name in the cache. Custom creators or configurations only ``work'' the \emph{first} time a particular filename is referenced in the lifetime of the \ImageCache. \apiend \apiitem{bool {\ce add_tile} (ustring filename, int subimage, int miplevel,\\ \bigspc int x, int y, int z, int chbegin, int chend, \\ \bigspc TypeDesc format, const void *buffer,\\ \bigspc stride_t xstride=AutoStride, stride_t ystride=AutoStride, \\ \bigspc stride_t zstride=AutoStride} Preemptively add a tile corresponding to the named image, at the given subimage and MIP level. The tile added is the one whose corner is (x,y,z), with the given channel range, and buffer points to the pixels (in the given format, with supplied strides) which will be copied and inserted into the cache and made available for future lookups. \apiend \subsection{Errors and statistics} \label{sec:imagecache:api:geterror} \label{sec:imagecache:api:getstats} \label{sec:imagecache:api:resetstats} \apiitem{std::string {\ce geterror} ()} \index{error checking} If any other API routines return {\cf false}, indicating that an error has occurred, this routine will retrieve the error and clear the error status. If no error has occurred since the last time {\cf geterror()} was called, it will return an empty string. \apiend \apiitem{std::string {\ce getstats} (int level=1)} Returns a big string containing useful statistics about the \ImageCache operations, suitable for saving to a file or outputting to the terminal. The {\cf level} indicates the amount of detail in the statistics, with higher numbers (up to a maximum of 5) yielding more and more esoteric information. \apiend \apiitem{void {\ce reset_stats} ()} Reset most statistics to be as they were with a fresh \ImageCache. Caveat emptor: this does not flush the cache itelf, so the resulting statistics from the next set of texture requests will not match the number of tile reads, etc., that would have resulted from a new \ImageCache. \apiend \index{Image Cache|)} \chapwidthend openimageio-1.7.17~dfsg0.orig/src/doc/Makefile0000644000175000017500000000172513151711064017326 0ustar mfvmfv.PHONY: figures include ../make/detectplatform.mk ifeq (${OIIOTOOL},) ifneq (${shell ls ./../../build/${platform}/src/oiiotool/oiiotool},) OIIOTOOL := "../../../build/${platform}/src/oiiotool/oiiotool" else OIIOTOOL := "../../../build/src/oiiotool/oiiotool" endif endif PDFLATEX := pdflatex -halt-on-error -interaction=errorstopmode # by default, just make the document all: openimageio.pdf # document making rule: use pdflatex openimageio.pdf: *.tex *.aux figures.turd ${PDFLATEX} openimageio.tex # special command 'make index' to regenerate the index index: openimageio_index openimageio_index: figures ${PDFLATEX} openimageio.tex ${PDFLATEX} openimageio.tex makeindex openimageio ${PDFLATEX} openimageio.tex figures: (cmake -E make_directory figures ; \ cd figures ; \ OIIOTOOL="${OIIOTOOL}" bash ../makefigures.bash ; \ ) figures.turd: makefigures.bash ${MAKE} figures touch figures.turd cleanfigures: cmake -E remove_directory figures openimageio-1.7.17~dfsg0.orig/src/doc/iconvert.tex0000644000175000017500000002726213151711064020245 0ustar mfvmfv\chapter{Converting Image Formats With {\kw iconvert}} \label{chap:iconvert} \indexapi{iconvert} \section{Overview} The {\cf iconvert} program will read an image (from any file format for which an \ImageInput plugin can be found) and then write the image to a new file (in any format for which an \ImageOutput plugin can be found). In the process, {\cf iconvert} can optionally change the file format or data format (for example, converting floating-point data to 8-bit integers), apply gamma correction, switch between tiled and scanline orientation, or alter or add certain metadata to the image. The {\cf iconvert} utility is invoked as follows: \medskip \hspace{0.25in} {\cf iconvert} [\emph{options}] \emph{input} \emph{output} \medskip Where \emph{input} and \emph{output} name the input image and desired output filename. The image files may be of any format recognized by \product (i.e., for which \ImageInput plugins are available). The file format of the output image will be inferred from the file extension of the output filename (e.g., \qkw{foo.tif} will write a TIFF file). Alternately, any number of files may be specified as follows: \medskip \hspace{0.25in} {\cf iconvert --inplace} [\emph{options}] \emph{file1 file2 ..} \medskip When the {\cf --inplace} option is used, any number of file names $\ge 1$ may be specified, and the image conversion commands are applied to each file in turn, with the output being saved under the original file name. This is useful for applying the same conversion to many files, or simply if you want to replace the input with the output rather than create a new file with a different name. \section{{\cf iconvert} Recipes} This section will give quick examples of common uses of {\cf iconvert}. \subsection*{Converting between file formats} It's a snap to converting among image formats supported by \product (i.e., for which \ImageInput and \ImageOutput plugins can be found). The {\cf iconvert} utility will simply infer the file format from the file extension. The following example converts a PNG image to JPEG: \begin{code} iconvert lena.png lena.jpg \end{code} \subsection*{Changing the data format or bit depth} Just use the {\cf -d} option to specify a pixel data format. For example, assuming that {\cf in.tif} uses 16-bit unsigned integer pixels, the following will convert it to an 8-bit unsigned pixels: \begin{code} iconvert -d uint8 in.tif out.tif \end{code} \subsection*{Changing the compression} The following command converts writes a TIFF file, specifically using LZW compression: \begin{code} iconvert --compression lzw in.tif out.tif \end{code} The following command writes its results as a JPEG file at a compression quality of 50 (pretty severe compression): \begin{code} iconvert --quality 50 big.jpg small.jpg \end{code} \subsection*{Gamma-correcting an image} The following gamma-corrects the pixels, raising all pixel values to $x^{1/2.2}$ upon writing: \begin{code} iconvert -g 2.2 in.tif out.tif \end{code} \subsection*{Converting between scanline and tiled images} Convert a scanline file to a tiled file with $16 \times 16$ tiles: \begin{code} iconvert --tile 16 16 s.tif t.tif \end{code} \noindent Convert a tiled file to scanline: \begin{code} iconvert --scanline t.tif s.tif \end{code} \subsection*{Converting images in place} You can use the {\cf --inplace} flag to cause the output to \emph{replace} the input file, rather than create a new file with a different name. For example, this will re-compress all of your TIFF files to use ZIP compression (rather than whatever they currently are using): \begin{code} iconvert --inplace --compression zip *.tif \end{code} \subsection*{Change the file modification time to the image capture time} Many image formats (including JPEGs from digital cameras) contain an internal time stamp indicating when the image was captured. But the time stamp on the file itself (what you'd see in a directory listing from your OS) most likely shows when the file was last copied, not when it was created or captured. You can use the following command to re-stamp your files so that the file system modification time matches the time that the digital image was originally captured: \begin{code} iconvert --inplace --adjust-time *.jpg \end{code} \subsection*{Add captions, keywords, IPTC tags} For formats that support it, you can add a caption/image description, keywords, or arbitrary string metadata: \begin{code} iconvert --inplace --adjust-time --caption "Hawaii vacation" *.jpg iconvert --inplace --adjust-time --keyword "John" img18.jpg img21.jpg iconvert --inplace --adjust-time --attrib IPTC:State "HI" \ --attrib IPTC:City "Honolulu" *.jpg \end{code} \medskip \section{{\cf iconvert} command-line options} \apiitem{--help} Prints usage information to the terminal. \apiend \apiitem{-v} Verbose status messages. \apiend \apiitem{--threads \emph{n}} Use \emph{n} execution threads if it helps to speed up image operations. The default (also if $n=0$) is to use as many threads as there are cores present in the hardware. \apiend \apiitem{--inplace} Causes the output to \emph{replace} the input file, rather than create a new file with a different name. Without this flag, {\cf iconvert} expects two file names, which will be used to specify the input and output files, respectively. But when {\cf --inplace} option is used, any number of file names $\ge 1$ may be specified, and the image conversion commands are applied to each file in turn, with the output being saved under the original file name. This is useful for applying the same conversion to many files. For example, the following example will add the caption ``Hawaii vacation'' to all JPEG files in the current directory: \begin{code} iconvert --inplace --adjust-time --caption "Hawaii vacation" *.jpg \end{code} \apiend \apiitem{-d {\rm \emph{datatype}}} Attempt to sets the output pixel data type to one of: {\cf uint8}, {\cf sint8}, {\cf uint16}, {\cf sint16}, {\cf half}, {\cf float}, {\cf double}. The types {\cf uint10} and {\cf uint12} may be used to request 10- or 12-bit unsigned integers. If the output file format does not support them, {\cf uint16} will be substituted. If the {\cf -d} option is not supplied, the output data type will be the same as the data format of the input file, if possible. In any case, if the output file type does not support the requested data type, it will instead use whichever supported data type results in the least amount of precision lost. \apiend % FIXME -- no it doesn't! \apiitem{-g {\rm \emph{gamma}}} Applies a gamma correction of $1/\mathrm{gamma}$ to the pixels as they are output. \apiend \apiitem{--sRGB} Explicitly tags the image as being in sRGB color space. Note that this does not alter pixel values, it only marks which color space those values refer to (and only works for file formats that understand such things). An example use of this command is if you have an image that is not explicitly marked as being in any particular color space, but you know that the values are sRGB. \apiend \apiitem{--tile {\rm \emph{x}} {\rm \emph{y}}} Requests that the output file be tiled, with the given $x \times y$ tile size, if tiled images are supported by the output format. By default, the output file will take on the tiledness and tile size of the input file. \apiend \apiitem{--scanline} Requests that the output file be scanline-oriented (even if the input file was tile-oriented), if scanline orientation is supported by the output file format. By default, the output file will be scanline if the input is scanline, or tiled if the input is tiled. \apiend \apiitem{--separate \\ --contig} Forces either ``separate'' (e.g., RRR...GGG...BBB) or ``contiguous'' (e.g., RGBRGBRGB...) packing of channels in the file. If neither of these options are present, the output file will have the same kind of channel packing as the input file. Of course, this is ignored if the output file format does not support a choice or does not support the particular choice requested. \apiend \apiitem{--compression {\rm \emph{method}}} Sets the compression method for the output image. Each \ImageOutput plugin will have its own set of methods that it supports. By default, the output image will use the same compression technique as the input image (assuming it is supported by the output format, otherwise it will use the default compression method of the output plugin). \apiend \apiitem{--quality {\rm \emph{q}}} Sets the compression quality, on a 1--100 floating-point scale. This only has an effect if the particular compression method supports a quality metric (as JPEG does). \apiend \apiitem{--no-copy-image} Ordinarily, {\cf iconvert} will attempt to use {\cf ImageOutput::copy_image} underneath to avoid de/recompression or alteration of pixel values, unless other settings clearly contradict this (such as any settings that must alter pixel values). The use of {\cf --no-copy-image} will force all pixels to be decompressed, read, and compressed/written, rather than copied in compressed form. We're not exactly sure when you would need to do this, but we put it in just in case. \apiend \apiitem{--adjust-time} When this flag is present, after writing the output, the resulting file's modification time will be adjusted to match any \qkw{DateTime} metadata in the image. After doing this, a directory listing will show file times that match when the original image was created or captured, rather than simply when {\cf iconvert} was run. This has no effect on image files that don't contain any \qkw{DateTime} metadata. \apiend \apiitem{--caption {\rm \emph{text}}} Sets the image metadata \qkw{ImageDescription}. This has no effect if the output image format does not support some kind of title, caption, or description metadata field. Be careful to enclose \emph{text} in quotes if you want your caption to include spaces or certain punctuation! \apiend \apiitem{--keyword {\rm \emph{text}}} Adds a keyword to the image metadata \qkw{Keywords}. Any existing keywords will be preserved, not replaced, and the new keyword will not be added if it is an exact duplicate of existing keywords. This has no effect if the output image format does not support some kind of keyword field. Be careful to enclose \emph{text} in quotes if you want your keyword to include spaces or certain punctuation. For image formats that have only a single field for keywords, \OpenImageIO will concatenate the keywords, separated by semicolon (`;'), so don't use semicolons within your keywords. \apiend \apiitem{--clear-keywords} Clears all existing keywords in the image. \apiend \apiitem{--attrib {\rm \emph{name text}}} Sets the named image metadata attribute to a string given by \emph{text}. For example, you could explicitly set the IPTC location metadata fields with: \begin{code} iconvert --attrib "IPTC:City" "Berkeley" in.jpg out.jpg \end{code} \apiend \apiitem{--orientation {\rm \emph{orient}}} Explicitly sets the image's \qkw{Orientation} metadata to a numeric value (see Section~\ref{metadata:orientation} for the numeric codes). This only changes the metadata field that specifies how the image should be displayed, it does NOT alter the pixels themselves, and so has no effect for image formats that don't support some kind of orientation metadata. \apiend \apiitem{--rotcw \\ --rotccw \\ --rot180} Adjusts the image's \qkw{Orientation} metadata by rotating it $90^\circ$ clockwise, $90^\circ$ degrees counter-clockwise, or $180^\circ$, respectively, compared to its current setting. This only changes the metadata field that specifies how the image should be displayed, it does NOT alter the pixels themselves, and so has no effect for image formats that don't support some kind of orientation metadata. \apiend \chapwidthend openimageio-1.7.17~dfsg0.orig/src/doc/Welcome.txt0000644000175000017500000000114413151711064020015 0ustar mfvmfvThank you for selecting OpenImageIO. You must be very wise, as well as irresistably attractive! OpenImageIO consists of: * Simple APIs for reading and writing image files in a format-agnostic manner, and plugins to read many common image formats including TIFF, JPEG, OpenEXR, PNG, HDR, ICO, and BMP. * Image utilities, including an image viewer, printing image information, format conversion, image comparison, and others. * A library for image texture lookups, and a managed image cache that allows an application to access hundreds of GB of image data using little runtime RAM. openimageio-1.7.17~dfsg0.orig/src/doc/igrep.tex0000644000175000017500000000443613151711064017520 0ustar mfvmfv\chapter{Searching Image Metadata With {\kw igrep}} \label{chap:igrep} \indexapi{igrep} %\section{Overview} The {\cf igrep} program search one or more image files for metadata that match a string or regular expression. \section{Using {\cf igrep}} The {\cf igrep} utility is invoked as follows: \bigskip \hspace{0.25in} {\cf igrep} [\emph{options}] \emph{pattern} \emph{filename} ... \medskip Where \emph{pattern} is a POSIX.2 regular expression (just like the Unix/Linux {\cf grep(1)} command), and \emph{filename} (and any following names) specify images or directories that should be searched. An image file will ``match'' if any of its metadata contains values contain substring that are recognized regular expression. The image files may be of any format recognized by \product (i.e., for which \ImageInput plugins are available). Example: \begin{code} $ igrep Jack *.jpg bar.jpg: Keywords = Carly; Jack foo.jpg: Keywords = Jack test7.jpg: ImageDescription = Jack on vacation \end{code} % $ \section{{\cf igrep} command-line options} \apiitem{--help} Prints usage information to the terminal. \apiend \apiitem{-d} Print directory names as it recurses. This only happens if the {\cf -r} option is also used. \apiend \apiitem{-E} Interpret the pattern as an extended regular expression (just like {\cf egrep} or {\cf grep -E}). \apiend \apiitem{-f} Match the expression against the filename, as well as the metadata within the file. \apiend \apiitem{-i} Ignore upper/lower case distinctions. Without this flag, the expression matching will be case-sensitive. \apiend \apiitem{-l} Simply list the matching files by name, surpressing the normal output that would include the metadata name and values that matched. For example: \begin{code} $ igrep Jack *.jpg bar.jpg: Keywords = Carly; Jack foo.jpg: Keywords = Jack test7.jpg: ImageDescription = Jack on vacation $ igrep -l Jack *.jpg bar.jpg foo.jpg test7.jpg \end{code} \apiend \apiitem{-r} Recurse into directories. If this flag is present, any files specified that are directories will have any image file contained therein to be searched for a match (an so on, recursively). \apiend \apiitem{-v} Invert the sense of matching, to select image files that \emph{do not} match the expression. \apiend openimageio-1.7.17~dfsg0.orig/src/doc/CLA-INDIVIDUAL0000644000175000017500000001610713151711064017636 0ustar mfvmfv Software Grant and Corporate Contributor License Agreement ("Agreement") (modeled after http://www.apache.org/licenses/) Thank you for your interest in OpenImageIO (the "Project", which collectively includes its authors, contributors, and any encompassing organization). In order to clarify the intellectual property license granted with Contributions from any person or entity, the Project must have a Contributor License Agreement ("CLA") on file that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of the Project and its users; it does not change your rights to use your own Contributions for any other purpose. If you have not already done so, sign this agreement, scan it, and email to info@openimageio.org. Or contact that email to request a FAX number or US postal address to mail an original. Please read this document carefully before signing and keep a copy for your records. Full name: ______________________________________________________ Mailing Address: ________________________________________________ _________________________________________________________________ _________________________________________________________________ Country: ______________________________________________________ Telephone: ______________________________________________________ Facsimile: ______________________________________________________ E-Mail: ______________________________________________________ You accept and agree to the following terms and conditions for Your present and future Contributions submitted to the Project. In return, the Project shall not use Your Contributions in a way that is contrary to the public benefit or inconsistent with its nonprofit status and bylaws in effect at the time of the Contribution. Except for the license granted herein to the Project and recipients of software distributed by the Project, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with the Project. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to the Project for inclusion in, or documentation of, any of the products owned or managed by the Project (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Project or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Project for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to the Project and to recipients of software distributed by the Project a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to the Project and to recipients of software distributed by the Project a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to the Project, or that your employer has executed a separate Corporate CLA with the Project. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 7. Should You wish to submit work that is not Your original creation, You may submit it to the Project separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. You agree to notify the Project of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. Please sign: __________________________________ Date: ________________ openimageio-1.7.17~dfsg0.orig/src/doc/moreverb.sty0000644000175000017500000001330513151711064020245 0ustar mfvmfv%% %% This is file `moreverb.sty', %% generated with the docstrip utility. %% %% The original source files were: %% %% moreverb.dtx (with options: `moreverb') %% %% Copyright Robin Fairbairns, 1997 2002 %% %% This file is distributed under the terms of the latex project public %% licence (LPPL: see CTAN macros/latex/base/lppl.txt), version 1.2 or %% (at your convenience) any later version. %% \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{moreverb} [2008/06/03 v2.3 `more' verbatim facilities% ] \@ifundefined{verbatim@processline}{\RequirePackage{verbatim}}{} \newwrite \verbatim@out \def\verbatimwrite#1{% \@bsphack \immediate\openout \verbatim@out #1 \let\do\@makeother\dospecials \catcode`\^^M\active \catcode`\^^I=12 \def\verbatim@processline{% \immediate\write\verbatim@out {\the\verbatim@line}}% \verbatim@start} \def\endverbatimwrite{% \immediate\closeout\verbatim@out \@esphack} \newcount\tab@position \newcount\tab@size \def\verbatimtabsize{8\relax} \def\@xobeytab{% \loop \toks@\expandafter{\the\toks@\@xobeysp}% \advance\tab@position-1 \ifnum\tab@position>0 \repeat } \begingroup \catcode`\^^I=\active \gdef\@vobeytabs{\catcode`\^^I\active\let^^I\@xobeytab}% \endgroup \def\verbatim@tabexpand#1{% \ifx#1\@nil \the\toks@ \expandafter\par \else \ifx#1\@xobeytab \@xobeytab \else \toks@\expandafter{\the\toks@#1}% \advance\tab@position\m@ne \fi \ifnum\tab@position=0 \tab@position\tab@size \fi \expandafter\verbatim@tabexpand \fi } \newcount\listing@line \listing@line=1 \def\listing@step{1\relax} \def\listinglabel#1{\llap{\small\rmfamily\the#1}\hskip\listingoffset\relax} \def\thelisting@line{% \setbox0\hbox{\listinglabel\listing@line}% \@tempcnta=\listing@line \divide\@tempcnta\listing@step \multiply\@tempcnta\listing@step \ifnum\listing@line=\@ne \unhbox0 \else \ifnum\@tempcnta=\listing@line \unhbox0 \else \hskip\wd0 \fi \fi} \providecommand\listingoffset{1.5em} \newcommand\listing[2][1]{% \global\listing@line=#2\relax \gdef\listing@step{#1\relax} \listingcont} \def\listingcont{% \tab@size=\verbatimtabsize \def\verbatim@processline{\tab@position\tab@size \thelisting@line \global\advance\listing@line1 \toks@{}% \expandafter\verbatim@tabexpand\the\verbatim@line\@nil}% \@verbatim\frenchspacing\@vobeyspaces\@vobeytabs\verbatim@start} \let\endlisting=\endtrivlist \let\endlistingcont=\endtrivlist \expandafter\newcommand\csname listing*\endcsname[2][1]{% \global\listing@line=#2\relax \gdef\listing@step{#1\relax} \csname listingcont*\endcsname} \@namedef{listingcont*}{% \def\verbatim@processline{% \thelisting@line \global\advance\listing@line1 \the\verbatim@line\par}% \@verbatim\verbatim@start} \expandafter\let\csname endlisting*\endcsname\endtrivlist \expandafter\let\csname endlistingcont*\endcsname\endtrivlist \def\listinginput{% \@ifnextchar[%] {\@listinginput}% {\@listinginput[1]}} \begingroup \catcode`\~=\active \lccode`\~=`\^^M \lccode`\N=`\N \lowercase{\endgroup \def\@listinginput[#1]#2#3{\begingroup \global\listing@line=#2 \gdef\listing@step{#1\relax} \tab@size=\verbatimtabsize \def\verbatim@processline{\tab@position\tab@size \thelisting@line \global\advance\listing@line1 \toks@{}% \expandafter\verbatim@tabexpand\the\verbatim@line\@nil}% \@verbatim\frenchspacing\@vobeyspaces\@vobeytabs \def\verbatim@addtoline##1~{% \verbatim@line\expandafter{\the\verbatim@line##1}}% \openin\verbatim@in@stream=#3 \ifeof\verbatim@in@stream \PackageWarning{moreverb}{No file #3.}% \else \do@verbatimtabinput \closein\verbatim@in@stream \fi \endtrivlist\endgroup \@doendpe }% } \def\verbatimcmd{% \PackageError{moreverb}{The verbatimcmd environment is obsolete}% {Use alltt (from the LaTeX required package alltt) in place of verbatimcmd}% } \let\endverbatimcmd\relax \def\boxedverbatim{% \def\verbatim@processline{% {\setbox0=\hbox{\the\verbatim@line}% \hsize=\wd0 \the\verbatim@line\par}}% \@minipagetrue % DPC \@tempswatrue % DPC \@totalleftmargin\z@ % MH \setbox0=\vbox\bgroup \verbatim } \def\endboxedverbatim{% \endverbatim \unskip\setbox0=\lastbox % DPC \egroup \fbox{\box0}% } \newenvironment{verbatimtab}{\obeylines\@verbatimtab}{\endtrivlist} \newcommand\@verbatimtab[1][\verbatimtabsize]{% \do@verbatimtab{#1}{% \@verbatim\frenchspacing\@vobeyspaces\@vobeytabs\verbatim@start}% } \def\do@verbatimtab#1#2{% \tab@size=#1 \def\verbatim@processline{\tab@position\tab@size \toks@{}% \expandafter\verbatim@tabexpand\the\verbatim@line\@nil}% #2% } \def\verbatimtabinput{% \@ifnextchar[%] {\@verbatimtabinput}% {\@verbatimtabinput[\verbatimtabsize]}} \begingroup \catcode`\~=\active \lccode`\~=`\^^M \lccode`\N=`\N \lowercase{\endgroup \def\@verbatimtabinput[#1]#2{\begingroup \do@verbatimtab{#1}{% \@verbatim\frenchspacing\@vobeyspaces\@vobeytabs}% \def\verbatim@addtoline##1~{% \verbatim@line\expandafter{\the\verbatim@line##1}}% \openin\verbatim@in@stream=#2 \ifeof\verbatim@in@stream \PackageWarning{moreverb}{No file #2.} \else \@addtofilelist{#2}% \do@verbatimtabinput \closein\verbatim@in@stream \fi \endtrivlist\endgroup\@doendpe}% } \def\do@verbatimtabinput{% \read\verbatim@in@stream to \verbtab@line \ifeof\verbatim@in@stream \else \expandafter\verbatim@addtoline\verbtab@line \verbatim@processline \verbatim@startline \expandafter\do@verbatimtabinput \fi } \endinput %% %% End of file `moreverb.sty'. openimageio-1.7.17~dfsg0.orig/src/doc/maketx.tex0000644000175000017500000005707513151711064017712 0ustar mfvmfv\chapter{Making Tiled MIP-Map Texture Files With \maketx or \oiiotool} \label{chap:maketx} \indexapi{maketx} \index{Texture System!making textures with maketx} \section{Overview} The \TextureSystem (Chapter~\ref{chap:texturesystem}) will exhibit much higher performance if the image files it uses as textures are tiled (versus scanline) orientation, have multiple subimages at different resolutions (MIP-mapped), and include a variety of header or metadata fields appropriately set for texture maps. Any image that you intend to use as input to \TextureSystem, therefore, should first be converted to have these properties. An ordinary image will work as a texture, but without this additional step, it will be drastically less efficient in terms of memory, disk or network I/O, and time. This can be accomplished programmatically using the \ImageBufAlgo\ {\cf make_texture()} function (see Section~\ref{sec:iba:importexport} for C++ and Section~\ref{sec:iba:py:importexport} for Python). \product includes two command-line utilities capable of converting ordinary images into texture files: \maketx and \oiiotool.\footnote{Why are there two programs? Historical artifact -- \maketx existed first, and much later \oiiotool was extended to be capable of directly writing texture files. If you are simply converting an image into a texture, \maketx is more straightforward and foolproof, in that you can't accidentally forget to turn it into a texture, as you might do with \oiiotool. On the other hand, \oiiotool is the way to go if you have a complex series of image processing operations and want the end result to be a texture, without having to write an intermediate file to feed separately to \maketx.} \section{\maketx} \label{sec:maketx} The \maketx program will convert ordinary images to efficient textures. The \maketx utility is invoked as follows: \medskip \hspace{0.25in} {\cf maketx} [\emph{options}] \emph{input}... {\cf -o} \emph{output} \medskip Where \emph{input} and \emph{output} name the input image and desired output filename. The input files may be of any image format recognized by \product (i.e., for which \ImageInput plugins are available). The file format of the output image will be inferred from the file extension of the output filename (e.g., \qkw{foo.tif} will write a TIFF file). \medskip \newpage \noindent Command-line arguments are: \apiitem{--help} Prints usage information to the terminal. \apiend \apiitem{-v} Verbose status messages, including runtime statistics and timing. \apiend \apiitem{--runstats} Print runtime statistics and timing. \apiend \apiitem{-o {\rm \emph{outputname}}} Sets the name of the output texture. \apiend \apiitem{--threads \emph{n}} Use \emph{n} execution threads if it helps to speed up image operations. The default (also if $n=0$) is to use as many threads as there are cores present in the hardware. \apiend \apiitem{--format {\rm \emph{formatname}}} Specifies the image format of the output file (e.g., ``tiff'', ``OpenEXR'', etc.). If {\cf --format} is not used, \maketx will guess based on the file extension of the output filename; if it is not a recognized format extension, TIFF will be used by default. \apiend \apiitem{-d {\rm \emph{datatype}}} Attempt to sets the output pixel data type to one of: {\cf uint8}, {\cf sint8}, {\cf uint16}, {\cf sint16}, {\cf half}, {\cf float}, {\cf double}. If the {\cf -d} option is not supplied, the output data type will be the same as the data format of the input file. In either case, the output file format itself (implied by the file extension of the output filename) may trump the request if the file format simply does not support the requested data type. \apiend \apiitem{--tile {\rm \emph{x}} {\rm \emph{y}}} Specifies the tile size of the output texture. If not specified, \maketx will make $64 \times 64$ tiles. \apiend \apiitem{--separate} Forces ``separate'' (e.g., RRR...GGG...BBB) packing of channels in the output file. Without this option specified, ``contiguous'' (e.g., RGBRGBRGB...) packing of channels will be used for those file formats that support it. \apiend \apiitem{--compression {\rm \emph{method}}} Sets the compression method for the output image (the default is to try to use \qkw{zip} compression, if it is available). \apiend \apiitem{-u} Ordinarily, textures are created unconditionally (which could take several seconds for large input files if read over a network) and will be stamped with the current time. The {\cf -u} option enables \emph{update mode}: if the output file already exists, and has the same time stamp as the input file, and the command-lie arguments that created it are identical to the current ones, then the texture will be left alone and not be recreated. If the output file does not exist, or has a different time stamp than the input file, or was created using different command line arguments, then the texture will be created and given the time stamp of the input file. \apiend \apiitem{--wrap {\rm \emph{wrapmode}} \\ --swrap {\rm \emph{wrapmode}} --twrap {\rm \emph{wrapmode}}} Sets the default \emph{wrap mode} for the texture, which determines the behavior when the texture is sampled outside the $[0,1]$ range. Valid wrap modes are: {\cf black}, {\cf clamp}, {\cf periodic}, {\cf mirror}. The default, if none is set, is {\cf black}. The {\cf --wrap} option sets the wrap mode in both directions simultaneously, while the {\cf --swrap} and {\cf --twrap} may be used to set them individually in the $s$ (horizontal) and $t$ (vertical) diretions. Although this sets the default wrap mode for a texture, note that the wrap mode may have an override specified in the texture lookup at runtime. \apiend \apiitem{--resize} Causes the highest-resolution level of the MIP-map to be a power-of-two resolution in each dimension (by rounding up the resolution of the input image). There is no good reason to do this for the sake of OIIO's texture system, but some users may require it in order to create MIP-map images that are compatible with both OIIO and other texturing systems that require power-of-2 textures. \apiend \apiitem{--filter {\rm \emph{name}}} By default, the resizing step that generates successive MIP levels uses a triangle filter to bilinearly combine pixels (for MIP levels with even number of pixels, this is also equivalent to a box filter, which merely averages groups of 4 adjacent pixels). This is fast, but for source images with high frequency content, can result in aliasing or other artifacts in the lower-resolution MIP levels. The {\cf --filter} option selects a high-quality filter to use when resizing to generate successive MIP levels. Choices include {\cf lanczos3} (our best recommendation for highest-quality filtering, a 3-lobe Lanczos filter), {\cf box}, {\cf triangle}, {\cf catrom}, {\cf blackman-harris}, {\cf gaussian}, {\cf mitchell}, {\cf bspline}, \qkw{cubic}, \qkw{keys}, \qkw{simon}, \qkw{rifman}, {\cf radial-lanczos3}, {\cf disk}, {\cf sinc}. If you select a filter with negative lobes (including {\cf lanczos3}, {\cf sinc}, {\cf lanczos3}, {\cf keys}, {\cf simon}, {\cf rifman}, or {\cf catrom}), and your source image is an HDR image with very high contrast regions that include pixels with values $>1$, you may also wish to use the {\cf --rangecompress} option to avoid ringing artifacts. \apiend \apiitem{--hicomp} Perform highlight compensation. When HDR input data with high-contrast highlights is turned into a MIP-mapped texture using a high-quality filter with negative lobes (such as {\cf lanczos3}), objectionable ringing could appear near very high-contrast regions with pixel values $>1$. This option improves those areas by using range compression (transforming values from a linear to a logarithmic scale that greatly compresses the values $> 1$) prior to each image filtered-resize step, and then expanded back to a linear format after the resize, and also clamping resulting pixel values to be non-negative. This can result in some loss of energy, but often this is a preferable alternative to ringing artifacts in your upper MIP levels. \apiend \apiitem{--sharpen {\rm \emph{contrast}}} EXPERIMENTAL: USE AT YOUR OWN RISK! This option will run an additional sharpening filter when creating the successive MIP-map levels. It uses an unsharp mask (much like in Section~\ref{sec:iba:unsharpmask}) to emphasize high-frequency details to make features ``pop'' visually even at high MIP-map levels. The \emph{contrast} controls the degree to which it does this. Probably a good value to enhance detail but not go overboard is 0.5 or even 0.25. A value of 1.0 may make strage artifacts at high MIP-map levels. Also, if you simultaneously use {\cf --filter unsharp-median}, a slightly different variant of unsharp masking will be used that employs a median filter to separate out the low-frequencies, this may tend to help emphasize small features while not over-emphasizing large edges. \apiend \apiitem{--nomipmap} Causes the output to \emph{not} be MIP-mapped, i.e., only will have the highest-resolution level. \apiend \apiitem{--nchannels {\rm \emph{n}}} Sets the number of output channels. If \emph{n} is less than the number of channels in the input image, the extra channels will simply be ignored. If \emph{n} is greater than the number of channels in the input image, the additional channels will be filled with 0 values. \apiend \apiitem{--chnames {\rm \emph{a,b,...}}} Renames the channels of the output image. All the channel names are concatenated together, separated by commas. A ``blank'' entry will cause the channel to retain its previous value (for example, {\cf --chnames ,,,A} will rename channel 3 to be \qkw{A} and leave channels 0--2 as they were. \apiend \apiitem{--checknan} Checks every pixel of the input image to ensure that no NaN or Inf values are present. If such non-finite pixel values are found, an error message will be printed and {\cf maketx} will terminate without writing the output image (returning an error code). \apiend \apiitem{--fixnan {\rm \emph{streategy}}} Repairs any pixels in the input image that contained {\cf NaN} or {\cf Inf} values (hereafter referred to collectively as ``nonfinite''). If \emph{strategy} is {\cf black}, nonfinite values will be replaced with {\cf 0}. If \emph{strategy} is {\cf box3}, nonfinite values will be replaced by the average of all the finite values within a $3 \times 3$ region surrounding the pixel. \apiend \apiitem{--fullpixels} Resets the ``full'' (or ``display'') pixel range to be the ``data'' range. This is used to deal with input images that appear, in their headers, to be crop windows or overscanned images, but you want to treat them as full 0--1 range images over just their defined pixel data. \apiend %\apiitem{--ingamma {\rm \emph{value}} \\ %--outgamma {\rm \emph{value}}} %Not currently implemented %\apiend \apiitem{--Mcamera {\rm \emph{...16 floats...}} \\ --Mscreen {\rm \emph{...16 floats...}}} Sets the camera and screen matrices (sometimes called {\cf Nl} and {\cf NP}, respectively, by some renderers) in the texture file, overriding any such matrices that may be in the input image (and would ordinarily be copied to the output texture). \apiend \apiitem{--prman-metadata} Causes metadata \qkw{PixarTextureFormat} to be set, which is useful if you intend to create an OpenEXR texture or environment map that can be used with PRMan as well as OIIO. \apiend \apiitem{--attrib {\rm \emph{name value}}} Adds or replaces metadata with the given \emph{name} to have the specified \emph{value}. It will try to infer the type of the metadata from the value: if the value contains only numerals (with optional leading minus sign), it will be saved as {\cf int} metadata; if it also contains a decimal point, it will be saved as {\cf float} metadata; otherwise, it will be saved as a {\cf string} metadata. For example, you could explicitly set the IPTC location metadata fields with: \begin{code} oiiotool --attrib "IPTC:City" "Berkeley" in.jpg out.jpg \end{code} \apiend \apiitem{--sattrib {\rm \emph{name value}}} Adds or replaces metadata with the given \emph{name} to have the specified \emph{value}, forcing it to be interpreted as a {\cf string}. This is helpful if you want to set a {\cf string} metadata to a value that the {\cf --attrib} command would normally interpret as a number. \apiend \apiitem{--sansattrib} When set, this edits the command line inserted in the \qkw{Software} and \qkw{ImageHistory} metadata to omit any verbose {\cf --attrib} and {\cf --sattrib} commands. \apiend \apiitem{--constant-color-detect} Detects images in which all pixels are identical, and outputs the texture at a reduced resolution equal to the tile size, rather than filling endless tiles with the same constant color. That is, by substituting a low-res texture for a high-res texture if it's a constant color, you could save a lot of save disk space, I/O, and texture cache size. It also sets the \qkw{ImageDescription} to contain a special message of the form \qkw{ConstantColor=[r,g,...]}. \apiend \apiitem{--monochrome-detect} Detects multi-channel images in which all color components are identical, and outputs the texture as a single-channel image instead. That is, it changes RGB images that are gray into single-channel gray scale images. Use with caution! This is a great optimization if such textures will only have their first channel accessed, but may cause unexpected behavior if the ``client'' application will attempt to access those other channels that will no longer exist. \apiend \apiitem{--opaque-detect} Detects images that have a designated alpha channel for which the alpha value for all pixels is 1.0 (fully opaque), and omits the alpha channel from the output texture. So, for example, an RGBA input texture where A=1 for all pixels will be output just as RGB. The purpose is to save disk space, texture I/O bandwidth, and texturing time for those textures where alpha was present in the input, but clearly not necessary. Use with caution! This is a great optimization only if your use of such textures will assume that missing alpha channels are equivalent to textures whose alpha is 1.0 everywhere. \apiend \apiitem{--ignore-unassoc} Ignore any header tags in the input images that indicate that the input has ``unassociated'' alpha. When this option is used, color channels with unassociated alpha will not be automatically multiplied by alpha to turn them into associated alpha. This is also a good way to fix input images that really are associated alpha, but whose headers incorrectly indicate that they are unassociated alpha. \apiend \apiitem{--prman} PRMan is will crash in strange ways if given textures that don't have its quirky set of tile sizes and other specific metadata. If you want \maketx to generate textures that may be used with either \OpenImageIO or PRMan, you should use the {\cf --prman} option, which will set several options to make PRMan happy, overriding any contradictory settings on the command line or in the input texture. Specifically, this option sets the tile size (to 64x64 for 8 bit, 64x32 for 16 bit integer, and 32x32 for float or {\cf half} images), uses ``separate'' planar configuration ({\cf --separate}), and sets PRMan-specific metadata ({\cf --prman-metadata}). It also outputs sint16 textures if uint16 is requested (because PRMan for some reason does not accept true uint16 textures), and in the case of TIFF files will omit the Exif directory block which will not be recognized by the older version of libtiff used by PRMan. \OpenImageIO will happily accept textures that conform to PRMan's expectations, but not vice versa. But \OpenImageIO's \TextureSystem has better performance with textures that use \maketx's default settings rather than these oddball choices. You have been warned! \apiend \apiitem{--oiio} This sets several options that we have determined are the optimal values for \OpenImageIO's \TextureSystem, overriding any contradictory settings on the command line or in the input texture. Specifically, this is the equivalent to using \\ {\cf --separate --tile 64 64}. \apiend \apiitem{--colorconvert {\rm \emph{inspace outspace}}} Convert the color space of the input image from \emph{inspace} to \emph{tospace}. If OpenColorIO is installed and finds a valid configuration, it will be used for the color conversion. If OCIO is not enabled (or cannot find a valid configuration, OIIO will at least be able to convert among linear, sRGB, and Rec709. \apiend \apiitem{--unpremult} When undergoing some color conversions, it is helpful to ``un-premultiply'' the alpha before converting color channels, and then re-multiplying by alpha. Caveat emptor -- if you don't know exactly when to use this, you probably shouldn't be using it at all. \apiend \apiitem{--mipimage {\rm \emph{filename}}} Specifies the name of an image file to use as a custom MIP-map level, instead of simply downsizing the last one. This option may be used multiple times to specify multiple levels. For example: \begin{code} maketx 256.tif --mipimage 128.tif --mipimage 64.tif -o out.tx \end{code} This will make a texture with the first MIP level taken from {\cf 256.tif}, the second level from {\cf 128.tif}, the third from {\cf 64.tif}, and then subsequent levels will be the usual downsizings of {\cf 64.tif}. \apiend \apiitem{--envlatl} Creates a latitude-longitude environment map, rather than an ordinary texture map. \apiend \apiitem{--lightprobe} Creates a latitude-longitude environment map, but in contrast to {\cf --envlatl}, the original input image is assumed to be formatted as a \emph{light probe} image\footnote{See {\cf http://www.pauldebevec.com/Probes/} for examples and an explanation of the geometric layout.}. \apiend % --shadow --shadcube % --volshad --envlatl --envcube --lightprobe --latl2envcube --vertcross % --fov % --opaquewidth \begin{comment} \subsection{{\cf maketx} Recipes} % FIXME This section will give quick examples of common uses of {\cf maketx}. \subsection*{Converting between file formats} It's a snap to converting among image formats supported by \product (i.e., for which \ImageInput and \ImageOutput plugins can be found). The {\cf maketx} utility will simply infer the file format from the file extension. The following example converts a PNG image to JPEG: \begin{code} maketx lena.png lena.jpg \end{code} \end{comment} \section{\oiiotool} \label{sec:oiiotooltex} The \oiiotool utility (Chapter~\ref{chap:oiiotool}) is capable of writing textures using the {\cf -otex} option, and lat-long environment maps using the {\cf -oenv} option. Roughly speaking, \medskip \hspace{0.25in} {\cf maketx} [\emph{maketx-options}] \emph{input} {\cf -o} \emph{output} \medskip \noindent is equivalent to \medskip \hspace{0.25in} {\cf oiiotool} \emph{input} [\emph{oiiotool-options}] {\cf -otex} \emph{output} \medskip \noindent and \medskip \hspace{0.25in} {\cf maketx -envlatl} [\emph{maketx-options}] \emph{input} {\cf -o} \emph{output} \medskip \noindent is equivalent to \medskip \hspace{0.25in} {\cf oiiotool} \emph{input} [\emph{oiiotool-options}] {\cf -oenv} \emph{output} \medskip \noindent However, the notation for the various options are not identical between the two programs, as will be explained by the remainder of this section. The most important difference between \oiiotool and \maketx is that \oiiotool can do so much more than convert an existing input image to a texture -- literally any image creation or manipulation you can do via \oiiotool may be output directly as a full texture file using {\cf -otex} (or as a lat-long environment map using {\cf -oenv}). Note that it is vitally important that you use one of the texture output commands ({\cf -otex} or {\cf -oenv}) when creating textures with \oiiotool --- if you inadvertently forget and use an ordinary {\cf -o}, you will end up with an output image that is much less efficient to use as a texture. \subsubsection*{Command line arguments useful when creating textures} As with any use of \oiiotool, you may use the following to control the run generally: \apiitem{{--help \\ -v \\ --runstats \\ --threads} \emph{n}} \apiend \noindent and as with any use of \oiiotool, you may use the following command-line options to control aspects of the any output files (including those from {\cf -otex} and {\cf -oenv}, as well as {\cf -o}). Only brief descriptions are given below, please consult Chapter~\ref{chap:oiiotool} for detailed explanations. \apiitem{{-d} {\rm \emph{datatype}}} Specify the pixel data type ({\cf uint8}, {\cf uint16}, {\cf half}, {\cf float}, etc.) if you wish to override the default of writing the same data type as the first input file read. \apiend \apiitem{{--tile} {\rm \emph{x}} {\rm \emph{y}}} Explicitly override the tile size (though you are strongly urged to use the default, and not use this command). \apiend \apiitem{{--compression} {\rm \emph{method}}} Explicitly override the default compression methods when writing the texture file. \apiend \apiitem{--ch {\rm \emph{channellist}}} Shuffle, add, delete, or rename channels (see \ref{sec:oiiotool:ch}). \apiend \apiitem{--chnames {\rm \emph{a,b,...}}} Renames the channels of the output image. \apiend \apiitem{--fixnan {\rm \emph{stretegy}}} Repairs any pixels in the input image that contained {\cf NaN} or {\cf Inf} values (if the \emph{strategy} is {\cf box3} or {\cf black}), or to simply abort with an error message (if the \emph{strategy} is {\cf error}). \apiend \apiitem{--fullpixels} Resets the ``full'' (or ``display'') pixel range to be the ``data'' range. \apiend \apiitem{--planarconfig separate} Forces ``separate'' (e.g., RRR...GGG...BBB) packing of channels in the output texture. This is almost always a bad choice, unless you know that the texture file must be readable by PRMan, in which case it is required. \apiend \apiitem{--attrib {\rm \emph{name ~ value}}} \oiiotool's {\cf --attrib} command may be used to set attributes in the metadata of the output texture. \apiend \apiitem{--attrib:type=matrix worldtocam {\rm \emph{...16 comma-separated floats...}} \\ --attrib:type=matrix screentocam {\rm \emph{...16 comma-separated floats...}}} Set/override the camera and screen matrices. \apiend \subsubsection*{Optional arguments to {\cf -otex} and {\cf -oenv}} As with many \oiiotool commands, the {\cf -otex} and {\cf -oenv} may have various optional arguments appended, in the form {\cf :name=value} (see Section~\ref{sec:oiiotooloptionalargs}). Optional arguments supported by {\cf -otex} and {\cf -oenv} include all the same options as {\cf -o} (Section~\ref{sec:oiiotool:o}) and also the following (explanations are brief, please consult Section~\ref{sec:maketx} for more detailed explanations of each, for the corresponding \maketx option): \medskip \noindent \begin{tabular}{p{2in} p{3in}} Appended Option & \maketx equivalent \\ \hline {\cf wrap=}\emph{string} & {\cf --wrap} \\ {\cf swrap=}\emph{string} & {\cf --swrap} \\ {\cf twrap=}\emph{string} & {\cf --twrap} \\[.5ex] {\cf resize=1} & {\cf --resize} \\ {\cf nomipmap=1} & {\cf --nomipmap} \\ {\cf updatemode=1} & {\cf -u} \\ {\cf monochrome_detect=1} & {\cf --monochrome-detect} \\ {\cf opaque_detect=1} & {\cf --opaque-detect} \\ {\cf unpremult=1} & {\cf --unpremult} \\ {\cf incolorspace=}\emph{name} & {\cf --incolorspace} \\ {\cf outcolorspace=}\emph{name} & {\cf --outcolorspace} \\ {\cf hilightcomp=1} & {\cf --hicomp} \\ {\cf sharpen=}\emph{float} & {\cf --sharpen} \\ {\cf filter=}\emph{string} & {\cf --filter} \\ %{\cf oiio_options=1} & {\cf --oiio} \\ {\cf prman_metadata=1} & {\cf --prman} \\ {\cf prman_options=1} & {\cf --prman-metadata} \\ \end{tabular} \subsubsection*{Examples} \begin{code} oiiotool in.tif -otex out.tx oiiotool in.jpg --colorconvert sRGB linear -d uint16 -otex out.tx oiiotool --pattern:checker 512x512 3 -d uint8 -otex:wrap=periodic checker.tx oiiotool in.exr -otex:hilightcomp=1:sharpen=0.5 out.exr \end{code} openimageio-1.7.17~dfsg0.orig/src/doc/builtinplugins.tex0000644000175000017500000013547113151711064021466 0ustar mfvmfv\chapter{Bundled ImageIO Plugins} \label{chap:bundledplugins} \index{Plugins!bundled|(} This chapter lists all the image format plugins that are bundled with \product. For each plugin, we delineate any limitations, custom attributes, etc. The plugins are listed alphabetically by format name. \vspace{.25in} \section{BMP} \label{sec:bundledplugins:bmp} \index{BMP} BMP is a bitmap image file format used mostly on Windows systems. BMP files use the file extension {\cf .bmp}. BMP is not a nice format for high-quality or high-performance images. It only supports unsigned integer 1-, 2-, 4-, and 8- bits per channel; only grayscale, RGB, and RGBA; does not support MIPmaps, multiimage, or tiles. %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.5in}|p{0.5in}|p{3.25in}} \ImageSpec Attribute & Type & BMP header data or explanation \\ \hline \qkw{XResolution} & float & hres \\ \qkw{YResolution} & float & vres \\ \qkw{ResolutionUnit} & string & always \qkw{m} (pixels per meter) \end{tabular} \vspace{.25in} \section{Cineon} \label{sec:bundledplugins:cineon} \index{Cineon} Cineon is an image file format developed by Kodak that is commonly used for scanned motion picture film and digital intermediates. Cineon files use the file extension {\cf .cin}. %FIXME \vspace{.25in} \section{DDS} \label{sec:bundledplugins:dds} \index{DDS} DDS (Direct Draw Surface) is an image file format designed by Microsoft for use in Direct3D graphics. DDS files use the extension {\cf .dds}. DDS is an awful format, with several compression modes that are all so lossy as to be completely useless for high-end graphics. Nevertheless, they are widely used in games and graphics hardware directly supports these compression modes. Alas. \product currently only supports reading DDS files, not writing them. %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.5in}|p{0.5in}|p{3.5in}} \ImageSpec Attribute & Type & DDS header data or explanation \\ \hline \qkw{compression} & string & compression type \\ \qkw{oiio:BitsPerSample} & int & bits per sample \\ \qkw{textureformat} & string & Set correctly to one of \qkws{Plain Texture}, \qkws{Volume Texture}, or \qkws{CubeFace Environment}. \\ \qkw{texturetype} & string & Set correctly to one of \qkws{Plain Texture}, \qkws{Volume Texture}, or \qkws{Environment}. \\ \qkw{dds:CubeMapSides} & string & For environment maps, which cube faces are present (e.g., \qkw{+x -x +y -y} if $x$ \& $y$ faces are present, but not $z$). \\ \end{tabular} %\subsubsection*{Limitations} %\begin{itemize} %\item blah %\end{itemize} \vspace{.25in} \section{DPX} \label{sec:bundledplugins:dpx} \index{DPX} DPX (Digital Picture Exchange) is an image file format used for motion picture film scanning, output, and digital intermediates. DPX files use the file extension {\cf .dpx}. %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.8in}|p{0.65in}|p{2.75in}} OIIO Attribute & Type & DPX header data or explanation \\ \hline \qkw{ImageDescription} & string & Description of image element \\ \qkw{Copyright} & string & Copyright statement \\ \qkw{Software} & string & Creator \\ \qkw{DocumentName} & string & Project name \\ \qkw{DateTime} & string & Creation date/time \\ \qkw{Orientation} & int & the orientation of the DPX image data (see \ref{metadata:orientation}) \\ \qkw{compression} & string & The compression type \\ \qkw{PixelAspectRatio} & float & pixel aspect ratio \\ \qkw{oiio:BitsPerSample} & int & the true bits per sample of the DPX file. \\ \qkw{oiio:Endian} & string & When writing, force a particular endianness for the output file (\qkw{little} or \qkw{big}) \\ \qkw{smpte:TimeCode} & int[2] & SMPTE time code (vecsemantics will be marked as TIMECODE) \\ \qkw{smpte:KeyCode} & int[7] & SMPTE key code (vecsemantics will be marked as KEYCODE) \\ \end{tabular} \noindent\begin{tabular}{p{1.8in}|p{0.65in}|p{2.75in}} OIIO Attribute & Type & DPX header data or explanation \\ \hline \qkw{dpx:Transfer} & string & Transfer characteristic \\ \qkw{dpx:Colorimetric} & string & Colorimetric specification \\ \qkw{dpx:ImageDescriptor} & string & ImageDescriptor \\ \qkw{dpx:Packing} & string & Image packing method \\ \qkw{dpx:TimeCode} & int & SMPTE time code \\ \qkw{dpx:UserBits} & int & SMPTE user bits \\ \qkw{dpx:SourceDateTime} & string & source time and date \\ \qkw{dpx:FilmEdgeCode} & string & FilmEdgeCode \\ \qkw{dpx:Signal} & string & Signal (\qkw{Undefined}, \qkw{NTSC}, \qkw{PAL}, etc.) \\ \qkw{dpx:UserData} & UCHAR[*] & User data (stored in an array whose length is whatever it was in the DPX file) \\ \qkw{dpx:EncryptKey} & int & Encryption key (-1 is not encrypted) \\ \qkw{dpx:DittoKey} & int & Ditto (0 = same as previous frame, 1 = new) \\ \qkw{dpx:LowData} & int & reference low data code value \\ \qkw{dpx:LowQuantity} & float & reference low quantity \\ \qkw{dpx:HighData} & int & reference high data code value \\ \qkw{dpx:HighQuantity} & float & reference high quantity \\ \qkw{dpx:XScannedSize} & float & X scanned size \\ \qkw{dpx:YScannedSize} & float & Y scanned size \\ \qkw{dpx:FramePosition} & int & frame position in sequence \\ \qkw{dpx:SequenceLength} & int & sequence length (frames) \\ \qkw{dpx:HeldCount} & int & held count (1 = default) \\ \qkw{dpx:FrameRate} & float & frame rate of original (frames/s) \\ \qkw{dpx:ShutterAngle} & float & shutter angle of camera (deg) \\ \qkw{dpx:Version} & string & version of header format \\ \qkw{dpx:Format} & string & format (e.g., \qkw{Academy}) \\ \qkw{dpx:FrameId} & string & frame identification \\ \qkw{dpx:SlateInfo} & string & slate information \\ \qkws{dpx:SourceImageFileName} & string & source image filename \\ \qkw{dpx:InputDevice} & string & input device name \\ \qkwf{dpx:InputDeviceSerialNumber} & string & input device serial number \\ \qkw{dpx:Interlace} & int & interlace (0 = noninterlace, 1 = 2:1 interlace)\\ \qkw{dpx:FieldNumber} & int & field number \\ \qkws{dpx:HorizontalSampleRate} & float & horizontal sampling rate (Hz) \\ \qkws{dpx:VerticalSampleRate} & float & vertical sampling rate (Hz) \\ \qkws{dpx:TemporalFrameRate} & float & temporal sampling rate (Hz) \\ \qkw{dpx:TimeOffset} & float & time offset from sync to first pixel (ms) \\ \qkw{dpx:BlackLevel} & float & black level code value \\ \qkw{dpx:BlackGain} & float & black gain \\ \qkw{dpx:BreakPoint} & float & breakpoint \\ \qkw{dpx:WhiteLevel} & float & reference white level code value \\ \qkw{dpx:IntegrationTimes} & float & integration time (s) \\ \qkw{dpx:EndOfLinePadding} & int & Padded bytes at the end of each line \\ \qkw{dpx:EndOfImagePadding} & int & Padded bytes at the end of each image \\ \end{tabular} %\subsubsection*{Limitations} %\begin{itemize} %\item blah %\end{itemize} \vspace{.25in} \section{Field3D} \label{sec:bundledplugins:field3d} \index{Field3D} Field3d is an open-source volume data file format. Field3d files commonly use the extension {\cf .f3d}. The official Field3D site is: \url{http://sites.google.com/site/field3d/} Currently, \product only reads Field3d files, and does not write them. Fields are comprised of multiple \emph{layers} (which appear to \product as subimages). Each layer/subimage may have a different name, resolution, and coordinate mapping. Layers may be scalar (1 channel) or vector (3 channel) fields, and the data may be {\cf half}, {\cf float}, or {\cf double}. \product always reports Field3D files as tiled. If the Field3d file has a ``block size'', the block size will be reported as the tile size. Otherwise, the tile size will be the size of the entire volume. %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.6in}|p{0.6in}|p{3.0in}} \ImageSpec Attribute & Type & Field3d header data or explanation \\ \hline \qkw{ImageDescription} & string & unique layer name \\ \qkw{oiio:subimagename} & string & unique layer name \\ \qkw{field3d:partition} & string & the partition name \\ \qkw{field3d:layer} & string & the layer (a.k.a.\ attribute) name \\ \qkw{field3d:fieldtype} & string & field type, one of: \qkw{dense}, \qkw{sparse}, or \qkw{MAC} \\ \qkw{field3d:mapping} & string & the coordinate mapping type \\ \qkws{field3d:localtoworld} & matrix of doubles & if a matrixMapping, the local-to-world transformation matrix \\ \qkw{worldtocamera} & matrix & if a matrixMapping, the world-to-local coordinate mapping \\ \end{tabular} \vspace{10pt} The ``unique layer name'' is generally the partition name + ``:'' + attribute name (example: \qkw{defaultfield:density}), with the following exceptions: (1) if the partition and attribute names are identical, just one is used rather than it being pointlessly concatenated (e.g., \qkw{density}, not \qkw{density:density}); (2) if there are mutiple partitions + attribute combinations with identical names in the same file, ``.\emph{number}'' will be added after the partition name for subsequent layers (e.g., \qkw{default:density}, \qkw{default.2:density}, \qkw{default.3:density}). \vspace{.25in} \section{FITS} \label{sec:bundledplugins:fits} \index{FITS} FITS (Flexible Image Transport System) is an image file format used for scientific applications, particularly professional astronomy. FITS files use the file extension {\cf .fits}. Official FITS specs and other info may be found at: \url{http://fits.gsfc.nasa.gov/} \product supports multiple images in FITS files, and supports the following pixel data types: UINT8, UINT16, UINT32, FLOAT, DOUBLE. FITS files can store various kinds of arbitrary data arrays, but \product's support of FITS is mostly limited using FITS for image storage. Currently, \product only supports 2D FITS data (images), not 3D (volume) data, nor 1-D or higher-dimensional arrays. %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.5in}|p{0.5in}|p{3.5in}} \ImageSpec Attribute & Type & FITS header data or explanation \\ \hline \qkw{Orientation} & int & derived from FITS ``ORIENTAT'' field. \\ \qkw{DateTime} & string & derived from the FITS ``DATE'' field. \\ \qkw{Comment} & string & FITS ``COMMENT'' (*) \\ \qkw{History} & string & FITS ``HISTORY'' (*) \\ \qkw{Hierarch} & string & FITS ``HIERARCH'' (*) \\[1.5ex] \emph{other} & & all other FITS keywords will be added to the \ImageSpec as arbitrary named metadata. \end{tabular} \noindent (*) Note: If the file contains multiple COMMENT, HISTORY, or HIERARCH fields, their text will be appended to form a single attribute (of each) in \product's \ImageSpec. \vspace{.25in} \section{GIF} \label{sec:bundledplugins:gif} \index{GIF} GIF (Graphics Interchange Format) is an image file format developed by CompuServe in 1987. Nowadays it is widely used to display basic animations despite its technical limitations. %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.5in}|p{0.5in}|p{3.25in}} \ImageSpec Attribute & Type & GIF header data or explanation \\ \hline \qkw{gif:Interlacing} & int & Specifies if image is interlaced (0 or 1). \\ \qkw{FramesPerSecond} & float & Frames per second \\ \qkw{oiio:Movie} & int & If nonzero, indicates that it's an animated GIF. \\ \qkw{gif:LoopCount} & int & Number of times the animation should be played (0--65535, 0 stands for infinity). \\ \qkw{ImageDescription} & string & The GIF comment field. \end{tabular} \subsubsection*{Limitations} \begin{itemize} \item GIF only supports 3-channel (RGB) images and at most 8 bits per channel. \item Each subimage can include its own palette or use global palette. Palettes contain up to 256 colors of which one can be used as background color. It is then emulated with additional Alpha channel by \product's reader. \end{itemize} \vspace{.25in} \section{HDR/RGBE} \label{sec:bundledplugins:hdr} \index{HDR} \index{RGBE} HDR (High Dynamic Range), also known as RGBE (rgb with extended range), is a simple format developed for the Radiance renderer to store high dynamic range images. HDR/RGBE files commonly use the file extensions {\cf .hdr}. The format is described in this section of the Radiance documentation: \url{http://radsite.lbl.gov/radiance/refer/filefmts.pdf} RGBE does not support tiles, multiple subimages, mipmapping, true half or float pixel values, or arbitrary metadata. Only RGB (3 channel) files are supported. RGBE became important because it was developed at a time when no standard file formats supported high dynamic range, and is still used for many legacy applications and to distribute HDR environment maps. But newer formats with native HDR support, such as OpenEXR, are vastly superior and should be preferred except when legacy file access is required. %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.5in}|p{0.5in}|p{3.25in}} \ImageSpec Attribute & Type & RGBE header data or explanation \\ \hline \qkw{Orientation} & int & encodes the orientation (see \ref{metadata:orientation}) \\ {\cf ImageSpec.gamma} & float & the gamma correction specified in the RGBE header. \end{tabular} \vspace{.25in} \section{ICO} \label{sec:bundledplugins:ico} \index{ICO} ICO is an image file format used for small images (usually icons) on Windows. ICO files use the file extension {\cf .ico}. %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.5in}|p{0.5in}|p{3.25in}} \ImageSpec Attribute & Type & ICO header data or explanation \\ \hline \qkw{oiio:BitsPerSample} & int & the true bits per sample in the ICO file. \\ \qkw{ico:PNG} & int & if nonzero, will cause the ICO to be written out using PNG format. \end{tabular} \subsubsection*{Limitations} \begin{itemize} \item ICO only supports UINT8 and UINT16 formats; all output images will be silently converted to one of these. \item ICO only supports \emph{small} images, up to $256 \times 256$. Requests to write larger images will fail their {\cf open()} call. \end{itemize} \vspace{.25in} \vspace{.25in} \section{IFF} \label{sec:bundledplugins:iff} \index{IFF} IFF files are used by Autodesk Maya and use the file extension {\cf .iff}. %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.8in}|p{0.65in}|p{2.75in}} OIIO Attribute & Type & DPX header data or explanation \\ \hline \qkw{Artist} & string & The IFF ``author'' \\ \qkw{DateTime} & string & Creation date/time \\ \qkw{compression} & string & The compression type \\ \qkw{oiio:BitsPerSample} & int & the true bits per sample of the IFF file. \\ \end{tabular} %\subsubsection*{Limitations} %\begin{itemize} %\item blah %\end{itemize} \vspace{.25in} \section{JPEG} \label{sec:bundledplugins:jpeg} \index{JPEG} JPEG (Joint Photographic Experts Group), or more properly the JFIF file format containing JPEG-compressed pixel data, is one of the most popular file formats on the Internet, with applications, and from digital cameras, scanners, and other image acquisition devices. JPEG/JFIF files usually have the file extension {\cf .jpg}, {\cf .jpe}, {\cf .jpeg}, {\cf .jif}, {\cf .jfif}, or {\cf .jfi}. The JFIF file format is described by \url{http://www.w3.org/Graphics/JPEG/jfif3.pdf}. Although we strive to support JPEG/JFIF because it is so widely used, we acknowledge that it is a poor format for high-end work: it supports only 1- and 3-channel images, has no support for alpha channels, no support for high dynamic range or even 16 bit integer pixel data, by convention stores sRGB data and is ill-suited to linear color spaces, and does not support multiple subimages or MIPmap levels. There are newer formats also blessed by the Joint Photographic Experts Group that attempt to address some of these issues, such as JPEG-2000, but these do not have anywhere near the acceptance of the original JPEG/JFIF format. %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.5in}|p{0.5in}|p{3.25in}} \ImageSpec Attribute & Type & JPEG header data or explanation \\ \hline \qkw{ImageDescription} & string & the JPEG Comment field \\ \qkw{Orientation} & int & the image orientation \\[2ex] \qkw{XResolution}, \qkw{YResolution}, \qkw{ResolutionUnit} & & The resolution and units from the Exif header \\[2ex] \qkw{CompressionQuality} & int & Quality of compression (1-100) \\[2ex] \qkw{ICCProfile} & uint8[] & The ICC color profile \\[2ex] \qkw{jpeg:subsampling} & string & Describes the chroma subsampling, e.g., \qkw{4:2:0} (the default), \qkw{4:4:4}, \qkw{4:2:2}, \qkw{4:2:1}. \\[2ex] & & \\ Exif, IPTC, XMP, GPS & & Extensive Exif, IPTC, XMP, and GPS data are supported by the reader/writer, and you should assume that nearly everything described Appendix~\ref{chap:stdmetadata} is properly translated when using JPEG files. \end{tabular} \subsubsection*{Limitations} \begin{itemize} \item JPEG/JFIF only supports 1- (grayscale) and 3-channel (RGB) images. As a special case, \product's JPEG writer will accept 4-channel image data and silently drop the alpha channel while outputting. Other channel count requests (i.e., anything other than 1, 3, and 4) will cause {\cf open()} to fail, since it is not possible to write a JFIF file with other than 1 or 3 channels. \item Since JPEG/JFIF only supports 8 bits per channel, \product's JPEG/JFIF writer will silently convert to UINT8 upon output, regardless of requests to the contrary from the calling program. \item \product's JPEG/JFIF reader and writer always operate in scanline mode and do not support tiled image input or output. \end{itemize} \vspace{.25in} \section{JPEG-2000} \label{sec:bundledplugins:jpeg2000} \index{Jpeg 2000} JPEG-2000 is a successor to the popular JPEG/JFIF format, that supports better (wavelet) compression and a number of other extensions. It's geared toward photography. JPEG-2000 files use the file extensions {\cf .jp2} or {\cf .j2k}. The official JPEG-2000 format specification and other helpful info may be found at \url{http://www.jpeg.org/JPEG2000.htm}. JPEG-2000 is not yet widely used, so \product's support of it is preliminary. In particular, we are not yet very good at handling the metadata robustly. %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.75in}|p{0.5in}|p{3.0in}} \ImageSpec Attribute & Type & JPEG-2000 header data or explanation \\ \hline \qkws{jpeg2000:streamformat} & string & specifies the JPEG-2000 stream format (\qkw{none} or \qkw{jpc}) \end{tabular} \vspace{.25in} \section{Movie formats (using ffmpeg)} \label{sec:bundledplugins:ffmpeg} \index{ffmpeg}\index{movie files} The {\cf ffmpeg}-based reader is capable of reading the individual frames from a variety of movie file formats, including: \smallskip \noindent\begin{tabular}{p{0.5in} p{1.5in} p{2.5in}} & Format & Extensions \\[0.75ex] %\hline & AVI & {\cf .avi} \\ & QuickTime & {\cf .qt}, {\cf .mov} \\ & MPEG-4 & {\cf .mp4}, {\cf .m4a}, {\cf .m4v} \\ & 3GPP files & {\cf .3gp}, {\cf .3g2} \\ & Motion JPEG-2000 & {\cf .mj2} \\ & Apple M4V & {\cf .m4v} \\ & MPEG-1/MPEG-2 & {\cf .mpg} \\ \end{tabular} \medskip Currently, these files may only be read. Write support may be added in a future release. Also, currently, these files simply look to OIIO like simple multi-image files and not much support is given to the fact that they are technically \emph{movies} (for example, there is no support for reading audio information). \medskip Some special attributes are used for movie files: \medskip \noindent\begin{tabular}{p{1.8in}|p{0.65in}|p{2.75in}} OIIO Attribute & Type & Explanation \\ \hline \qkw{oiio:Movie} & int & Nonzero value for movie files \\ \qkw{FramesPerSecond} & float & Frames per second \\ \end{tabular} \vspace{.25in} \section{OpenEXR} \label{sec:bundledplugins:openexr} \index{OpenEXR} OpenEXR is an image file format developed by Industrial Light \& Magic, and subsequently open-sourced. OpenEXR's strengths include support of high dynamic range imagery ({\cf half} and {\cf float} pixels), tiled images, explicit support of MIPmaps and cubic environment maps, arbitrary metadata, and arbitrary numbers of color channels. OpenEXR files use the file extension {\cf .exr}. The official OpenEXR site is \url{http://www.openexr.com/}. %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.95in}|p{0.5in}|p{2.8in}} \ImageSpec Attribute & Type & OpenEXR header data or explanation \\ \hline {\cf width}, {\cf height}, {\cf x}, {\cf y} & & {\cf dataWindow} \\[1ex] {\cf\small full_width}, {\cf\small full_height}, {\cf\small full_x}, {\cf\small full_y} & & {\cf displayWindow}. \\[4ex] \qkw{worldtocamera} & matrix & worldToCamera \\ \qkw{worldtoscreen} & matrix & worldToNDC \\ \qkw{ImageDescription} & string & comments \\ \qkw{Copyright} & string & owner \\ \qkw{DateTime} & string & capDate \\ \qkw{PixelAspectRatio} & float & pixelAspectRatio \\ \qkw{ExposureTime} & float & expTime \\ \qkw{FNumber} & float & aperture \\ \qkw{compression} & string & one of: \qkw{none}, \qkw{rle}, \qkw{zip}, \qkw{piz}, \qkw{pxr24}, \qkw{b44}, \qkw{b44a}, \qkw{dwaa}, or \qkw{dwab}. If the writer receives a request for a compression type it does not recognize or is not supported by the version of OpenEXR on the system, it will use \qkw{zip} by default. For \qkw{dwaa} and \qkw{dwab}, the dwaCompressionLevel may be optionally appended to the compression name after a colon, like this: \qkw{dwaa:200}. \\ \qkw{textureformat} & string & set to \qkw{Plain Texture} for MIP-mapped OpenEXR files, \qkw{CubeFace Environment} or \qkw{Latlong Environment} for OpenEXR environment maps. Non-environment non-MIP-mapped OpenEXR files will not set this attribute. \\ \qkw{wrapmodes} & string & wrapmodes \\ \qkw{smpte:TimeCode} & int[2] & SMPTE time code (vecsemantics will be marked as TIMECODE) \\ \qkw{smpte:KeyCode} & int[7] & SMPTE key code (vecsemantics will be marked as KEYCODE) \\ %\qkw{oiio:updirection} & string & Will be set to \qkw{y} for OpenEXR % latlong environment maps to indicate that OpenEXR dictates a % right-handed, ``$y$ is up'' coordinate system. \\ %\qkw{oiio:sampleborder} & int & Will be set to 1 for OpenEXR environment % maps to indicate that OpenEXR dictates that boundary texels sample exactly % on the texture border (pole, meridian, or cube edge).\\[2ex] \qkw{openexr:lineOrder} & string & the OpenEXR lineOrder attribute (set to \qkws{increasingY}, \qkws{randomY}, or \qkws{decreasingY}). \\ \qkws{openexr:roundingmode} & int & the MIPmap rounding mode of the file. \\ \qkws{\small openexr:dwaCompressionLevel} & float & compression level for dwaa or dwab compression (default: 45.0). \\[2ex] \emph{other} & & All other attributes will be added to the \ImageSpec by their name and apparent type. \end{tabular} \subsubsection*{A note on channel names} The underlying OpenEXR library (libIlmImf) always saves channels into lexicographic order, so the channel order on disk (and thus when read!) will NOT match the order when the image was created. But in order to adhere to OIIO's convention that RGBAZ will always be the first channels (if they exist), OIIO's OpenEXR reader will automatically reorder just those channels to appear at the front and in that order. All other channel names will remain in their relative order as presented to OIIO by libIlmImf. \subsubsection*{Limitations} \begin{itemize} \item The OpenEXR format only supports HALF, FLOAT, and UINT32 pixel data. \product's OpenEXR writer will silently convert data in formats (including the common UINT8 and UINT16 cases) to HALF data for output. \end{itemize} \vspace{.25in} \section{PNG} \label{sec:bundledplugins:png} \index{PNG} PNG (Portable Network Graphics) is an image file format developed by the open source community as an alternative to the GIF, after Unisys started enforcing patents allegedly covering techniques necessary to use GIF. PNG files use the file extension {\cf .png}. %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.75in}|p{0.5in}|p{3.0in}} \ImageSpec Attribute & Type & PNG header data or explanation \\ \hline \qkw{ImageDescription} & string & Description \\ \qkw{Artist} & string & Author \\ \qkw{DocumentName} & string & Title \\ \qkw{DateTime} & string & the timestamp in the PNG header \\ \qkw{PixelAspectRatio} & float & pixel aspect ratio \\ \qkw{XResolution} \qkw{YResolution} \qkw{ResolutionUnit} & & resolution and units from the PNG header. \\ \qkw{ICCProfile} & uint8[] & The ICC color profile \\ \end{tabular} \subsubsection*{Limitations} \begin{itemize} \item PNG stupidly specifies that any alpha channel is ``unassociated'' (i.e., that the color channels are not ``premultiplied'' by alpha). This is a disaster, since it results in bad loss of precision for alpha image compositing, and even makes it impossible to properly represent certain additive glows and other desirable pixel values. \product automatically associates alpha (i.e., multiplies colors by alpha) upon input and deassociates alpha (divides colors by alpha) upon output in order to properly conform to the OIIO convention (and common sense) that all pixel values passed through the OIIO APIs should use associated alpha. \item PNG only supports UINT8 and UINT16 output; other requested formats will be automatically converted to one of these. \end{itemize} \vspace{.25in} \section{PNM / Netpbm} \label{sec:bundledplugins:pnm} \index{PNM} The Netpbm project, a.k.a.\ PNM (portable ``any'' map) defines PBM, PGM, and PPM (portable bitmap, portable graymap, portable pixmap) files. Without loss of generality, we will refer to these all collectively as ``PNM.'' These files have extensions {\cf .pbm}, {\cf .pgm}, and {\cf .ppm} and customarily correspond to bi-level bitmaps, 1-channel grayscale, and 3-channel RGB files, respectively, or {\cf .pnm} for those who reject the nonsense about naming the files depending on the number of channels and bitdepth. PNM files are not much good for anything, but because of their historical significance and extreme simplicity (that causes many ``amateur'' programs to write images in these formats), \product supports them. PNM files do not support floating point images, anything other than 1 or 3 channels, no tiles, no multi-image, no MIPmapping. It's not a smart choice unless you are sending your images back to the 1980's via a time machine. %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.3in}|p{0.5in}|p{3.50in}} \ImageSpec Attribute & Type & PNG header data or explanation \\ \hline \qkws{oiio:BitsPerSample} & int & the true bits per sample of the file (1 for true PBM files, even though OIIO will report the {\cf format} as UINT8). \\ \qkws{pnm:binary} & int & nonzero if the file itself used the PNM binary format, 0 if it used ASCII. The PNM writer honors this attribute in the \ImageSpec to determine whether to write an ASCII or binary file. \end{tabular} \vspace{.25in} \section{PSD} \label{sec:bundledplugins:psd} \index{PSD} \index{PhotoShop images} PSD is the file format used for storing Adobe PhotoShop images. \product provides limited read abilities for PDF, but not currently the ability to write PSD files. \subsubsection*{Configuration settings for PSD input} When opening an \ImageInput with a \emph{configuration} (see Section~\ref{sec:inputwithconfig}), the following special configuration options are supported: \vspace{.125in} \noindent\begin{tabular}{p{1.8in}|p{0.5in}|p{2.95in}} Configuration attribute & Type & Meaning \\ \hline \qkws{oiio:RawColor} & int & If nonzero, reading images with non-RGB color models (such as CMYK or YCbCr) will return unaltered raw pixel values (versus the default OIIO behavior of automatically converting to RGB). \\ \end{tabular} Currently, the PSD format reader supports color modes RGB, CMYK, multichannel, grayscale, indexed, and bitmap. It does NOT currenty support Lab or duotone modes. \vspace{.25in} \section{Ptex} \label{sec:bundledplugins:ptex} \index{Ptex} Ptex is a special per-face texture format developed by Walt Disney Feature Animation. The format and software to read/write it are open source, and available from \url{http://ptex.us/}. Ptex files commonly use the file extension {\cf .ptex}. \product's support of Ptex is still incomplete. We can read pixels from Ptex files, but the \TextureSystem doesn't properly filter across face boundaries when using it as a texture. \product currently does not write Ptex files at all. %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.75in}|p{0.5in}|p{3.0in}} \ImageSpec Attribute & Type & Ptex header data or explanation \\ \hline \qkw{ptex:meshType} & string & the mesh type, either \qkw{triangle} or \qkw{quad}. \\ \qkw{ptex:hasEdits} & int & nonzero if the Ptex file has edits. \\ \qkw{wrapmode} & string & the wrap mode as specified by the Ptex file. \\ \emph{other} & & Any other arbitrary metadata in the Ptex file will be stored directly as attributes in the \ImageSpec. \end{tabular} \vspace{.25in} \section{RAW digital camera files} \label{sec:bundledplugins:raw} \index{RAW digital camera files} A variety of digital camera ``raw'' formats are supported via this plugin that is based on the LibRaw library ({\cf http://www.libraw.org/}). % FIXME - fill in more docs here \begin{comment} %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.75in}|p{0.5in}|p{3.0in}} \ImageSpec Attribute & Type & RAW header data or explanation \\ \hline \qkw{ImageDescription} & string & comment \\ \qkw{oiio:BitsPerSample} & int & the true bits per sample in the RAW file. \end{tabular} \end{comment} \subsubsection*{Configuration settings for RAW input} When opening an \ImageInput with a \emph{configuration} (see Section~\ref{sec:inputwithconfig}), the following special configuration options are supported: \vspace{.125in} \noindent\begin{tabular}{p{1.8in}|p{0.5in}|p{2.95in}} Configuration attribute & Type & Meaning \\ \hline \qkws{raw:auto_bright} & int & If nonzero, will use libraw's exposure correction. (Default: 0) \\ \qkws{raw:use_camera_wb} & int & If 1, use libraw's camera white balance adjustment. (Default: 1) \\ \qkws{raw:use_camera_matrix} & int & Whether to use the embedded color profile, if it's present: 0 = never, 1 (default) = only for DNG files, 3 = always. \\ \qkws{raw:adjust_maximum_thr} & float & If nonzero, auto-adjusting maximum value. (Default:0.0) \\ \qkws{raw:user_sat} & int & If nonzero, sets the camera maximum value that will be normalized to appear saturated. (Default: 0) \\ \qkws{raw:ColorSpace} & string & Which color primaries to use: \qkw{raw}, \qkw{sRGB}, \qkw{Adobe}, \qkw{Wide}, \qkw{ProPhoto}, \qkw{XYZ}. (Default: \qkw{sRGB}) \\ \qkws{raw:Exposure} & float & Amount of exposure before de-mosaicing, from 0.25 (2 stop darken) to 8 (3 stop brighten). (Default: 0, meaning no correction.) \\ \qkws{raw:Demosaic} & string & Force a demosaicing algorithm: \qkw{linear}, \qkw{VNG}, \qkw{PPG}, \qkw{AHD} (default), \qkw{DCB}, \qkw{AHD-Mod}, \qkw{AFD}, \qkw{VCD}, \qkw{Mixed}, \qkw{LMMSE}, \qkw{AMaZE}, \qkw{DHT}, \qkw{AAHD}. \\ \end{tabular} \vspace{.25in} \newpage \section{RLA} \label{sec:bundledplugins:rla} \index{RLA} RLA (Run-Length encoded, version A) is an early CGI renderer output format, originating from Wavefront Advanced Visualizer and used primarily by software developed at Wavefront. RLA files commonly use the file extension {\cf .rla}. %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.65in}|p{0.85in}|p{2.8in}} \ImageSpec Attribute & Type & RLA header data or explanation \\ \hline {\cf width}, {\cf height}, {\cf x}, {\cf y} & {\kw int} & RLA ``active/viewable'' window. \\ & & \\ {\cf\small full_width}, {\cf\small full_height}, {\cf\small full_x}, {\cf\small full_y} & {\kw int} & RLA ``full'' window. \\ & & \\ \qkws{rla:FrameNumber} & int & frame sequence number. \\ \qkws{rla:Revision} & int & file format revision number, currently \qkw{0xFFFE}. \\ \qkws{rla:JobNumber} & int & job number ID of the file. \\ \qkws{rla:FieldRendered} & int & whether the image is a field-rendered (interlaced) one (\qkw{0} for false, non-zero for true). \\ \qkws{rla:FileName} & string & name under which the file was orignally saved. \\ \qkw{ImageDescription} & string & RLA ``Description'' of the image. \\ \qkw{Software} & string & name of software used to save the image. \\ \qkw{HostComputer} & string & name of machine used to save the image. \\ \qkw{Artist} & string & RLA ``UserName'': logon name of user who saved the image. \\ \qkws{rla:Aspect} & string & aspect format description string. \\ \qkws{rla:ColorChannel} & string & textual description of color channel data format (usually \qkw{rgb}). \\ \qkws{rla:Time} & string & description (format not standardized) of amount of time spent on creating the image. \\ \qkws{rla:Filter} & string & name of post-processing filter applied to the image. \\ \qkws{rla:AuxData} & string & textual description of auxiliary channel data format. \\ \qkws{rla:AspectRatio} & float & image aspect ratio. \\ \qkws{rla:RedChroma} & vec2 or vec3 of floats & red point XY (vec2) or XYZ (vec3) coordinates. \\ \qkws{rla:GreenChroma} & vec2 or vec3 of floats & green point XY (vec2) or XYZ (vec3) coordinates. \\ \qkws{rla:BlueChroma} & vec2 or vec3 of floats & blue point XY (vec2) or XYZ (vec3) coordinates. \\ \qkws{rla:WhitePoint} & vec2 or vec3 of floats & white point XY (vec2) or XYZ (vec3) coordinates. \\ \end{tabular} \subsubsection*{Limitations} \begin{itemize} \item \product will only write a single image to each file, multiple subimages are not supported by the writer (but are supported by the reader). \end{itemize} \vspace{.25in} \section{SGI} \label{sec:bundledplugins:sgi} \index{SGI files} The SGI image format was a simple raster format used long ago on SGI machines. SGI files use the file extensions {\cf sgi}, {\cf rgb}, {\cf rgba}, \qkw{bw}, \qkw{int}, and \qkw{inta}. The SGI format is sometimes used for legacy apps, but has little merit otherwise: no support for tiles, no MIPmaps, no multi-subimage, only 8- and 16-bit integer pixels (no floating point), only 1-4 channels. %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.75in}|p{0.5in}|p{3.0in}} \ImageSpec Attribute & Type & SGI header data or explanation \\ \hline \qkw{ImageDescription} & string & image name \\ \qkw{Compression} & string & thee compression of the SGI file (\qkw{rle}, if RLE compression is used). \end{tabular} \vspace{.25in} \section{Softimage PIC} \label{sec:bundledplugins:pic} \index{Softimage PIC} Softimage PIC is an image file format used by the SoftImage 3D application, and some other programs that needed to be compatible with it. Softimage files use the file extension {\cf .pic}. The Softimage PIC format is sometimes used for legacy apps, but has little merit otherwise, so currently \product only reads Softimage files and is unable to write them. %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.75in}|p{0.5in}|p{3.0in}} \ImageSpec Attribute & Type & PIC header data or explanation \\ \hline \qkw{ImageDescription} & string & comment \\ \qkw{oiio:BitsPerSample} & int & the true bits per sample in the PIC file. \end{tabular} \vspace{.25in} \section{Targa} \label{sec:bundledplugins:targa} \index{Targa} Targa (a.k.a.\ Truevision TGA) is an image file format with little merit except that it is very simple and is used by many legacy applications. Targa files use the file extension {\cf .tga}, or, much more rarely, {\cf .tpic}. The official Targa format specification may be found at\\ \url{http://www.dca.fee.unicamp.br/~martino/disciplinas/ea978/tgaffs.pdf}. %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.75in}|p{0.5in}|p{3.0in}} \ImageSpec Attribute & Type & TGA header data or explanation \\ \hline \qkw{ImageDescription} & string & comment \\ \qkw{Artist} & string & author \\ \qkw{DocumentName} & string & job name/ID \\ \qkw{Software} & string & software name \\ \qkw{DateTime} & string & TGA time stamp \\ \qkw{targa:JobTime} & string & TGA ``job time.'' \\ \qkw{Compression} & string & values of \qkw{none} and \qkw{rle} are supported. The writer will use RLE compression if any unknown compression methods are requested. \\ \qkw{targa:ImageID} & string & Image ID \\ \qkw{PixelAspectRatio} & float & pixel aspect ratio \\ \qkw{oiio:BitsPerSample} & int & the true (in the file) bits per sample. \\ \end{tabular} \\ \vspace{.25in} If the TGA file contains a thumbnail, its dimensions will be stored in the attributes \qkw{thumbnail_width}, \qkw{thumbnail_height}, and \qkw{thumbnail_nchannels}, and the thumbnail pixels themselves will be stored in \qkw{thumbnail_image} (as an array of UINT8 values, whose length is the total number of channel samples in the thumbnail). \subsubsection*{Limitations} \begin{itemize} \item The Targa reader reserves enough memory for the entire image. Therefore it is not a good choice for high-performance image use such as would be used for \ImageCache or \TextureSystem. \item Targa files only support 8- and 16-bit unsigned integers (no signed, floating point, or HDR capabilities); the \product TGA writer will silently convert all output images to UINT8 (except if UINT16 is explicitly requested). \item Targa only supports grayscale, RGB, and RGBA; the \product TGA writer will fail its call to {\cf open()} if it is asked create a file with more than 4 color channels. \end{itemize} \vspace{.25in} \section{TIFF} \label{sec:bundledplugins:tiff} \index{TIFF} TIFF (Tagged Image File Format) is a flexible file format created by Aldus, now controlled by Adobe. TIFF supports nearly everything anybody could want in an image format (and has extactly the complexity you would expect from such a requirement). TIFF files commonly use the file extensions {\cf .tif} or, {\cf .tiff}. Additionally, \product associates the following extensions with TIFF files by default: {\cf .tx}, {\cf .env}, {\cf .sm}, {\cf .vsm}. The official TIFF format specification may be found here: \url{http://partners.adobe.com/public/developer/tiff/index.html} ~ The most popular library for reading TIFF directly is {\cf libtiff}, available here: \url{http://www.remotesensing.org/libtiff/} ~ \product uses {\cf libtiff} for its TIFF reading/writing. We like TIFF a lot, especially since its complexity can be nicely hidden behind OIIO's simple APIs. It supports a wide variety of data formats (though unfortunately not {\cf half}), an arbitrary number of channels, tiles and multiple subimages (which makes it our preferred texture format), and a rich set of metadata. \product supports the vast majority of TIFF features, including: tiled images (\qkw{tiled}) as well as scanline images; multiple subimages per file (\qkw{multiimage}); MIPmapping (using multi-subimage; that means you can't use multiimage and MIPmaps simultaneously); data formats 8- 16, and 32 bit integer (both signed and unsigned), and 32- and 64-bit floating point; palette images (will convert to RGB); ``miniswhite'' photometric mode (will convert to ``minisblack''). The TIFF plugin attempts to support all the standard Exif, IPTC, and XMP metadata if present. \subsubsection*{Configuration settings for TIFF input} When opening an \ImageInput with a \emph{configuration} (see Section~\ref{sec:inputwithconfig}), the following special configuration options are supported: \vspace{.125in} \noindent\begin{tabular}{p{1.8in}|p{0.5in}|p{2.95in}} Configuration attribute & Type & Meaning \\ \hline \qkws{oiio:UnassociatedAlpha} & int & If nonzero, will leave alpha unassociated (versus the default of premultiplying color channels by alpha if the alpha channel is unassociated). \\ \qkws{oiio:RawColor} & int & If nonzero, reading images with non-RGB color models (such as CMYK or YCbCr) will return unaltered raw pixel values (versus the default OIIO behavior of automatically converting to RGB). \\ \end{tabular} \subsubsection*{Configuration settings for TIFF output} When opening an \ImageOutput, the following special metadata tokens control aspects of the writing itself: \vspace{.125in} \noindent\begin{tabular}{p{1.8in}|p{0.5in}|p{2.95in}} Output attribute & Type & Meaning \\ \hline \qkws{oiio:UnassociatedAlpha} & int & If nonzero, any alpha channel is understood do be unassociated, and the EXTRASAMPLES tag in the TIFF file will be set to reflect this). \\ \qkw{tiff:write_exif} & int & If zero, will not write any Exif data to the TIFF file. (The default is 1.) \\ \qkw{tiff:half} & int & If nonzero, allow writing TIFF files with `half' (16 bit float) pixels. The default of 0 will automatically translate to float pixels, since most non-OIIO applications will not properly read half TIFF files despite their being legal. \\ \qkws{tiff:ColorSpace} & string & Requests that the file be saved with a non-RGB color spaces. Choices are \qkw{RGB}, \qkw{CMYK}. % , \qkw{YCbCr}, \qkw{CIELAB}, \qkw{ICCLAB}, \qkw{ITULAB} \\ \qkws{tiff:zipquality} & int & A time-vs-quality knob for \qkw{zip} compression, ranging from 1--9 (default is 6). Higher means compress to less space, but taking longer to do so. It is strictly a time vs space tradeoff, the quality is identical (lossless) no matter what the setting. \end{tabular} \subsubsection*{TIFF compression modes} \noindent The full list of possible TIFF compression mode values are as follows ($ ^*$ indicates that \product can write that format, and is not part of the format name): \\ {\kw none}$ ^*$ ~ {\kw lzw}$ ^*$ ~ {\kw zip}$ ^*$ ~ \\ {\kw ccitt_t4} ~ {\kw ccitt_t6} ~ {\kw ccittfax3} ~ {\kw ccittfax4} ~ {\kw ccittrle2} ~ {\kw ccittrle}$ ^*$ ~ {\kw dcs} ~ {\kw isojbig} ~ {\kw IT8BL} ~ {\kw IT8CTPAD} ~ {\kw IT8LW} ~ {\kw IT8MP} ~ {\kw jp2000} ~ {\kw jpeg}$ ^*$ ~ {\kw lzma} ~ {\kw next} ~ {\kw ojpeg} ~ {\kw packbits}$ ^*$ ~ {\kw pixarfilm} ~ {\kw pixarlog} ~ {\kw sgilog24} ~ {\kw sgilog} ~ {\kw T43} ~ {\kw T85} ~ {\kw thunderscan} ~ \subsubsection*{Limitations} \product's TIFF reader and writer have some limitations you should be aware of: \begin{itemize} \item No separate per-channel data formats (not supported by {\cf libtiff}). \item Only multiples of 8 bits per pixel may be passed through \product's APIs, e.g., 1-, 2-, and 4-bits per pixel will be passed by OIIO as 8 bit images; 12 bits per pixel will be passed as 16, etc. But the \qkw{oiio:BitsPerSample} attribute in the \ImageSpec will correctly report the original bit depth of the file. Similarly for output, you must pass 8 or 16 bit output, but \qkw{oiio:BitsPerSample} gives a hint about how you want it to be when written to the file, and it will try to accommodate the request (for signed integers, TIFF output can accommodate 2, 4, 8, 10, 12, and 16 bits). \item JPEG compression is limited to 8-bit per channel, 3-channel files. \end{itemize} \newpage \subsubsection*{TIFF Attributes} \noindent\begin{tabular}{p{2.0in}|p{0.5in}|p{2.75in}} \ImageSpec Attribute & Type & TIFF header data or explanation \\ \hline {\cf ImageSpec::x} & int & XPosition \\ {\cf ImageSpec::y} & int & YPosition \\ {\cf ImageSpec::full_width} & int & PIXAR\_IMAGEFULLWIDTH \\ {\cf ImageSpec::full_length} & int & PIXAR\_IMAGEFULLLENGTH \\ \qkw{ImageDescription} & string & ImageDescription \\ \qkw{DateTime} & string & DateTime \\ \qkw{Software} & string & Software \\ \qkw{Artist} & string & Artist \\ \qkw{Copyright} & string & Copyright \\ \qkw{Make} & string & Make \\ \qkw{Model} & string & Model \\ \qkw{DocumentName} & string & DocumentName \\ \qkw{HostComputer} & string & HostComputer \\ \qkws{XResultion} \qkws{YResolution} & float & XResolution, YResolution \\ \qkws{ResolutionUnit} & string & ResolutionUnit (\qkw{in} or \qkw{cm}). \\ \qkw{Orientation} & int & Orientation \\ \qkw{ICCProfile} & uint8[] & The ICC color profile \\ \qkw{textureformat} & string & {\cf PIXAR_TEXTUREFORMAT} \\ \qkw{wrapmodes} & string & {\cf PIXAR_WRAPMODES} \\ \qkw{fovcot} & float & {\cf PIXAR_FOVCOT} \\ \qkw{worldtocamera} & matrix & PIXAR\_MATRIX\_WORLDTOCAMERA \\ \qkw{worldtoscreen} & matrix & PIXAR\_MATRIX\_WORLDTOSCREEN\\ \qkw{compression} & string & based on TIFF Compression (one of \qkw{none}, \qkw{lzw}, \qkw{zip}, or others listed above.).\\ \qkw{tiff:compression} & int & the original integer code from the TIFF Compression tag.\\ \qkw{tiff:planarconfig} & string & PlanarConfiguration (\qkw{separate} or \qkw{contig}). The \product TIFF writer will honor such a request in the \ImageSpec.\\ \qkwf{tiff:PhotometricInterpretation} & int & Photometric \\ \qkw{tiff:PageName} & string & PageName \\ \qkw{tiff:PageNumber} & int & PageNumber \\ \qkw{tiff:RowsPerStrip} & int & RowsPerStrip \\ \qkw{tiff:subfiletype} & 1 & SubfileType \\ \qkw{Exif:*} & & A wide variety of EXIF data are honored, and are all prefixed with \qkw{Exif:}.\\ \qkw{oiio:BitsPerSample} & int & The actual bits per sample in the file (may differ from {\cf ImageSpec::format}).\\ \qkw{oiio:UnassociatedAlpha} & int & Nonzero if the alpha channel contained ``unassociated'' alpha. \\ \end{tabular} \vspace{.25in} \vspace{.25in} \section{Webp} \label{sec:bundledplugins:webp} \index{WebP} % FIXME \vspace{.25in} \section{Zfile} \label{sec:bundledplugins:zfile} \index{Zfile} Zfile is a very simple format for writing a depth ($z$) image, originally from Pixar's PhotoRealistic RenderMan but now supported by many other renderers. It's extremely minimal, holding only a width, height, world-to-screen and camera-to-screen matrices, and uncompressed float pixels of the z-buffer. Zfile files use the file extension {\cf .zfile}. %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.75in}|p{0.5in}|p{3.0in}} \ImageSpec Attribute & Type & Zfile header data or explanation \\ \hline \qkw{worldtocamera} & matrix & NP \\ \qkw{worldtoscreen} & matrix & Nl \\ \end{tabular} \index{Plugins!bundled|)} \chapwidthend %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \begin{comment} FOO () is an image file format. Strengths. FOO files use the file extension {\cf .foo}. The official FOO format specification may be found at \url{} . %\subsubsection*{Attributes} \vspace{.125in} \noindent\begin{tabular}{p{1.5in}|p{0.5in}|p{3.5in}} \ImageSpec Attribute & Type & FOO header data or explanation \\ \hline \end{tabular} \subsubsection*{Limitations} \begin{itemize} \item blah \end{itemize} \end{comment} openimageio-1.7.17~dfsg0.orig/src/doc/imageinput.tex0000644000175000017500000014564713151711064020566 0ustar mfvmfv\chapter{Image I/O: Reading Images} \label{chap:imageinput} \index{Image I/O API|(} \section{Image Input Made Simple} \label{sec:imageinput:simple} Here is the simplest sequence required to open an image file, find out its resolution, and read the pixels (converting them into 8-bit values in memory, even if that's not the way they're stored in the file): \begin{code} #include OIIO_NAMESPACE_USING ... ImageInput *in = ImageInput::open (filename); if (! in) return; const ImageSpec &spec = in->spec(); int xres = spec.width; int yres = spec.height; int channels = spec.nchannels; std::vector pixels (xres*yres*channels); in->read_image (TypeDesc::UINT8, &pixels[0]); in->close (); ImageInput::destroy (in); \end{code} \noindent Here is a breakdown of what work this code is doing: \begin{itemize} \item Search for an ImageIO plugin that is capable of reading the file (\qkw{foo.jpg}), first by trying to deduce the correct plugin from the file extension, but if that fails, by opening every ImageIO plugin it can find until one will open the file without error. When it finds the right plugin, it creates a subclass instance of \ImageInput that reads the right kind of file format, and tries to fully open the file. \begin{code} ImageInput *in = ImageInput::open (filename); \end{code} \item The specification, accessible as {\cf in->spec()}, contains vital information such as the dimensions of the image, number of color channels, and data type of the pixel values. This is enough to allow us to allocate enough space for the image. \begin{code} const ImageSpec &spec = in->spec(); int xres = spec.width; int yres = spec.height; int channels = spec.nchannels; std::vector pixels (xres*yres*channels); \end{code} Note that in this example, we don't care what data format is used for the pixel data in the file --- we allocate enough space for unsigned 8-bit integer pixel values, and will rely on \product's ability to convert to our requested format from the native data format of the file. \item Read the entire image, hiding all details of the encoding of image data in the file, whether the file is scanline- or tile-based, or what is the native format of the data in the file (in this case, we request that it be automatically converted to unsigned 8-bit integers). \begin{code} in->read_image (TypeDesc::UINT8, &pixels[0]); \end{code} \item Close the file, destroy and free the \ImageInput we had created, and perform all other cleanup and release of any resources used by the plugin. \begin{code} in->close (); ImageInput::destroy (in); \end{code} \end{itemize} \section{Advanced Image Input} \label{sec:advancedimageinput} Let's walk through some of the most common things you might want to do, but that are more complex than the simple example above. \subsection{Reading individual scanlines and tiles} \label{sec:imageinput:scanlinestiles} The simple example of Section~\ref{sec:imageinput:simple} read an entire image with one call. But sometimes you want to read a large image a little at a time and do not wish to retain the entire image in memory as you process it. \product allows you to read images one scanline at a time or one tile at a time. Examining the \ImageSpec reveals whether the file is scanline or tile-oriented: a scanline image will have {\cf spec.tile_width} and {\cf spec.tile_height} set to 0, whereas a tiled images will have nonzero values for the tile dimensions. \subsubsection{Reading scanlines} Individual scanlines may be read using the \readscanline API call: \begin{code} ... in = ImageInput::open (filename); const ImageSpec &spec = in->spec(); if (spec.tile_width == 0) { std::vector scanline (spec.width*spec.channels); for (int y = 0; y < yres; ++y) { in->read_scanline (y, 0, TypeDesc::UINT8, &scanline[0]); ... process data in scanline[0..width*channels-1] ... } } else { ... handle tiles, or reject the file ... } in->close (); ... \end{code} The first two arguments to \readscanline specify which scanline is being read by its vertical ($y$) scanline number (beginning with 0) and, for volume images, its slice ($z$) number (the slice number should be 0 for 2D non-volume images). This is followed by a \TypeDesc describing the data type of the pixel buffer you are supplying, and a pointer to the pixel buffer itself. Additional optional arguments describe the data stride, which can be ignored for contiguous data (use of strides is explained in Section~\ref{sec:imageinput:strides}). Nearly all \ImageInput implementations will be most efficient reading scanlines in strict order (starting with scanline 0, then 1, up to {\kw yres-1}, without skipping any). An \ImageInput is required to accept \readscanline requests in arbitrary order, but depending on the file format and reader implementation, out-of-order scanline reads may be inefficient. There is also a {\cf read_scanlines()} function that operates similarly, except that it takes a {\cf ybegin} and {\cf yend} that specify a range, reading all scanlines {\cf ybegin} $\le y <$ {\cf yend}. For most image format readers, this is implemented as a loop over individual scanlines, but some image format readers may be able to read a contiguous block of scanlines more efficiently than reading each one individually. The full descriptions of the \readscanline and {\cf read_scanlines()} functions may be found in Section~\ref{sec:imageinput:reference}. \subsubsection{Reading tiles} Once you {\kw open()} an image file, you can find out if it is a tiled image (and the tile size) by examining the \ImageSpec's {\cf tile_width}, {\cf tile_height}, and {\cf tile_depth} fields. If they are zero, it's a scanline image and you should read pixels using \readscanline, not \readtile. \begin{code} ... in = ImageInput::open (filename); const ImageSpec &spec = in->spec(); if (spec.tile_width == 0) { ... read by scanline ... } else { // Tiles int tilesize = spec.tile_width * spec.tile_height; std::vector tile (tilesize * spec.channels); for (int y = 0; y < yres; y += spec.tile_height) { for (int x = 0; x < xres; x += spec.tile_width) { in->read_tile (x, y, 0, TypeDesc::UINT8, &tile[0]); ... process the pixels in tile[] .. } } } in->close (); ... \end{code} The first three arguments to \readtile specify which tile is being read by the pixel coordinates of any pixel contained in the tile: $x$ (column), $y$ (scanline), and $z$ (slice, which should always be 0 for 2D non-volume images). This is followed by a \TypeDesc describing the data format of the pixel buffer you are supplying, and a pointer to the pixel buffer. Pixel data will be written to your buffer in order of increasing slice, increasing scanline within each slice, and increasing column within each scanline. Additional optional arguments describe the data stride, which can be ignored for contiguous data (use of strides is explained in Section~\ref{sec:imageinput:strides}). All \ImageInput implementations are required to support reading tiles in arbitrary order (i.e., not in strict order of increasing $y$ rows, and within each row, increasing $x$ column, without missing any tiles). The full description of the \readtile function may be found in Section~\ref{sec:imageinput:reference}. \subsection{Converting formats} \label{sec:imageinput:convertingformat} The code examples of the previous sections all assumed that your internal pixel data is stored as unsigned 8-bit integers (i.e., 0-255 range). But \product is significantly more flexible. You may request that the pixels be stored in any of several formats. This is done merely by passing the {\cf read} function the data type of your pixel buffer, as one of the enumerated type \TypeDesc. %FIXME %Individual file formats, and therefore \ImageInput implementations, may %only support a subset of the formats understood by the \product library. %Each \ImageInput plugin implementation should document which data %formats it supports. An individual \ImageInput implementation may %choose to simply fail open {\kw open()}, though the recommended behavior %is for {\kw open()} to succeed but in fact choose a data format %supported by the file format that best preserves the precision and range %of the originally-requested data format. It is not required that the pixel data buffer passed to \readimage, \readscanline, or \readtile actually be in the same data format as the data in the file being read. \product will automatically convert from native data type of the file to the internal data format of your choice. For example, the following code will open a TIFF and read pixels into your internal buffer represented as {\cf float} values. This will work regardless of whether the TIFF file itself is using 8-bit, 16-bit, or float values. \begin{code} ImageInput *in = ImageInput::open ("myfile.tif"); const ImageSpec &spec = in->spec(); ... int numpixels = spec.width * spec.height; float pixels = new float [numpixels * channels]; ... in->read_image (TypeDesc::FLOAT, pixels); \end{code} \noindent Note that \readscanline and \readtile have a parameter that works in a corresponding manner. You can, of course, find out the native type of the file simply by examining {\cf spec.format}. If you wish, you may then allocate a buffer big enough for an image of that type and request the native type when reading, therefore eliminating any translation among types and seeing the actual numerical values in the file. %FIXME %Please refer to Section~\ref{sec:imageinput:quantization} for more %information on how values are translated among the supported data %formats by default, and how to change the formulas by specifying %quantization in the \ImageSpec. \subsection{Data Strides} \label{sec:imageinput:strides} In the preceeding examples, we have assumed that the buffer passed to the {\cf read} functions (i.e., the place where you want your pixels to be stored) is \emph{contiguous}, that is: \begin{itemize} \item each pixel in memory consists of a number of data values equal to the number of channels in the file; \item successive column pixels within a row directly follow each other in memory, with the first channel of pixel $x$ immediately following last channel of pixel $x-1$ of the same row; \item for whole images or tiles, the data for each row immediately follows the previous one in memory (the first pixel of row $y$ immediately follows the last column of row $y-1$); \item for 3D volumetric images, the first pixel of slice $z$ immediately follows the last pixel of of slice $z-1$. \end{itemize} Please note that this implies that \readtile will write pixel data into your buffer so that it is contiguous in the shape of a single tile, not just an offset into a whole image worth of pixels. The \readscanline function takes an optional {\cf xstride} argument, and the \readimage and \readtile functions take optional {\cf xstride}, {\cf ystride}, and {\cf zstride} values that describe the distance, in \emph{bytes}, between successive pixel columns, rows, and slices, respectively, of your pixel buffer. For any of these values that are not supplied, or are given as the special constant {\cf AutoStride}, contiguity will be assumed. By passing different stride values, you can achieve some surprisingly flexible functionality. A few representative examples follow: \begin{itemize} \item Flip an image vertically upon reading, by using \emph{negative} $y$ stride: \begin{code} unsigned char pixels[spec.width * spec.height * spec.nchannels]; int scanlinesize = spec.width * spec.nchannels * sizeof(pixels[0]); ... in->read_image (TypeDesc::UINT8, (char *)pixels+(yres-1)*scanlinesize, // offset to last AutoStride, // default x stride -scanlinesize, // special y stride AutoStride); // default z stride \end{code} \item Read a tile into its spot in a buffer whose layout matches a whole image of pixel data, rather than having a one-tile-only memory layout: \begin{code} unsigned char pixels[spec.width * spec.height * spec.nchannels]; int pixelsize = spec.nchannels * sizeof(pixels[0]); int scanlinesize = xpec.width * pixelsize; ... in->read_tile (x, y, 0, TypeDesc::UINT8, (char *)pixels + y*scanlinesize + x*pixelsize, pixelsize, scanlinesize); \end{code} \end{itemize} Please consult Section~\ref{sec:imageinput:reference} for detailed descriptions of the stride parameters to each {\cf read} function. \subsection{Reading metadata} \label{sec:imageinput:metadata} The \ImageSpec that is filled in by {\cf ImageInput::open()} specifies all the common properties that describe an image: data format, dimensions, number of channels, tiling. However, there may be a variety of additional \emph{metadata} that are present in the image file and could be queried by your application. The remainder of this section explains how to query additional metadata in the \ImageSpec. It is up to the \ImageInput to read these from the file, if indeed the file format is able to carry additional data. Individual \ImageInput implementations should document which metadata they read. \subsubsection{Channel names} In addition to specifying the number of color channels, the \ImageSpec also stores the names of those channels in its {\cf channelnames} field, which is a {\cf vector}. Its length should always be equal to the number of channels (it's the responsibility of the \ImageInput to ensure this). Only a few file formats (and thus \ImageInput implementations) have a way of specifying custom channel names, so most of the time you will see that the channel names follow the default convention of being named \qkw{R}, \qkw{G}, \qkw{B}, and \qkw{A}, for red, green, blue, and alpha, respectively. Here is example code that prints the names of the channels in an image: \begin{code} ImageInput *in = ImageInput::open (filename); const ImageSpec &spec = in->spec(); for (int i = 0; i < spec.nchannels; ++i) std::cout << "Channel " << i << " is " << spec.channelnames[i] << "\n"; \end{code} \subsubsection{Specially-designated channels} The \ImageSpec contains two fields, {\cf alpha_channel} and {\cf z_channel}, which designate which channel numbers represent alpha and $z$ depth, if any. If either is set to {\cf -1}, it indicates that it is not known which channel is used for that data. If you are doing something special with alpha or depth, it is probably safer to respect the {\cf alpha_channel} and {\cf z_channel} designations (if not set to {\cf -1}) rather than merely assuming that, for example, channel 3 is always the alpha channel. \subsubsection{Arbitrary metadata} All other metadata found in the file will be stored in the \ImageSpec's {\cf extra_attribs} field, which is a \ParamValueList, which is itself essentially a vector of \ParamValue instances. Each \ParamValue stores one meta-datum consisting of a name, type (specified by a \TypeDesc), number of values, and data pointer. If you know the name of a specific piece of metadata you want to use, you can find it using the {\cf ImageSpec::find_attribute()} method, which returns a pointer to the matching \ParamValue, or {\cf NULL} if no match was found. An optional \TypeDesc argument can narrow the search to only parameters that match the specified type as well as the name. Below is an example that looks for orientation information, expecting it to consist of a single integer: \begin{code} ImageInput *in = ImageInput::open (filename); const ImageSpec &spec = in->spec(); ... ParamValue *p = spec.find_attribute ("Orientation", TypeDesc::TypeInt); if (p) { int orientation = * (int *) p->data(); } else { std::cout << "No integer orientation in the file\n"; } \end{code} By convention, \ImageInput plugins will save all integer metadata as 32-bit integers ({\cf TypeDesc::INT} or {\cf TypeDesc::UINT}), even if the file format dictates that a particular item is stored in the file as a 8- or 16-bit integer. This is just to keep client applications from having to deal with all the types. Since there is relatively little metadata compared to pixel data, there's no real memory waste of promoting all integer types to int32 metadata. Floating-point metadata and string metadata may also exist, of course. For certain common types, there is an even simpler method for retrieving the metadata: \begin{code} int i = spec.get_int_attribute ("Orientation", 0); float f = spec.get_float_attribute ("PixelAspectRatio", 1.0f); std::string s = spec.get_string_attribute ("ImageDescription", ""); \end{code} This method simply returns the value. The second argument is the default value to use if the attribute named is not found. These versions will do automatic type conversion as well --- for example, if you ask for a float and the attribute is really an int, it will return the proper float for it; or if the attribute is a UINT16 and you call {\cf get_int_attribute}, it will succeed, promoting to an int. It is also possible to step through all the metadata, item by item. This can be accomplished using the technique of the following example: \begin{code} for (size_t i = 0; i < spec.extra_attribs.size(); ++i) { const ParamValue &p (spec.extra_attribs[i]); printf (" \%s: ", p.name.c_str()); if (p.type() == TypeDesc::TypeString) printf ("\"\%s\"", *(const char **)p.data()); else if (p.type() == TypeDesc::TypeFloat) printf ("\%g", *(const float *)p.data()); else if (p.type() == TypeDesc::TypeInt) printf ("\%d", *(const int *)p.data()); else if (p.type() == TypeDesc::UINT) printf ("\%u", *(const unsigned int *)p.data()); else if (p.type() == TypeDesc::TypeMatrix) { const float *f = (const float *)p.data(); printf ("\%f \%f \%f \%f \%f \%f \%f \%f " "\%f \%f \%f \%f \%f \%f \%f \%f", f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9], f[10], f[11], f[12], f[13], f[14], f[15]); } else printf (" "); printf ("\n"); } \end{code} Each individual \ImageInput implementation should document the names, types, and meanings of all metadata attributes that they understand. \subsubsection{Color space hints} We certainly hope that you are using only modern file formats that support high precision and extended range pixels (such as OpenEXR) and keeping all your images in a linear color space. But you may have to work with file formats that dictate the use of nonlinear color values. This is prevalent in formats that store pixels only as 8-bit values, since 256 values are not enough to linearly represent colors without banding artifacts in the dim values. The {\cf ImageSpec::extra_attribs} field may store metadata that reveals the color space the image file in the \qkw{oiio:ColorSpace} attribute, which may take on any of the following values: \begin{description} \item[\halfspc \rm \qkw{Linear}] indicates that the color pixel values are known to be linear. \item[\halfspc \rm \qkw{GammaCorrected}] indicates that the color pixel values (but not alpha or $z$) have already been gamma corrected (raised to the power $1/\gamma$), and that the gamma exponent may be found in the \qkw{oiio:Gamma} metadata. \item[\halfspc \rm \qkw{sRGB}] indicates that the color pixel values are in sRGB color space. \item[\halfspc \rm \qkw{AdobeRGB}] indicates that the color pixel values are in Adobe RGB color space. \item[\halfspc \rm \qkw{Rec709}] indicates that the color pixel values are in Rec709 color space. \item[\halfspc \rm \qkw{KodakLog}] indicates that the color pixel values are in Kodak logarithmic color space. \end{description} The \ImageInput sets the \qkw{oiio:ColorSpace} metadata in a purely advisory capacity --- the {\cf read} will not convert pixel values among color spaces. Many image file formats only support nonlinear color spaces (for example, JPEG/JFIF dictates use of sRGB). So your application should intelligently deal with gamma-corrected and sRGB input, at the very least. The color space hints only describe color channels. You should assume that alpha or depth ($z$) channels (designated by the {\cf alpha_channel} and {\cf z_channel} fields, respectively) always represent linear values and should never be transformed by your application. %\subsection{Controlling quantization and encoding} %\label{sec:imageinput:quantization} % %FIXME %\subsection{Random access and repeated transmission of pixels} %\label{sec:imageinput:randomrepeated} % %FIXME \subsection{Multi-image files and MIP-maps} \label{sec:imageinput:multiimage} \label{sec:imageinput:mipmap} Some image file formats support multiple discrete subimages to be stored in one file, and/or miltiple resolutions for each image to form a MIPmap. When you {\cf open()} an \ImageInput, it will by default point to the first (i.e., number 0) subimage in the file, and the highest resolution (level 0) MIP-map level. You can switch to viewing another subimage or MIP-map level using the {\cf seek_subimage()} function: \begin{code} ImageInput *in = ImageInput::open (filename); const ImageSpec &spec = in->spec(); ... int subimage = 1; int miplevel = 0; if (in->seek_subimage (subimage, miplevel, spec)) { ... } else { ... no such subimage/miplevel ... } \end{code} The {\cf seek_subimage()} function takes three arguments: the index of the subimage to switch to (starting with 0), the MIPmap level (starting with 0 for the highest-resolution level), and a reference to an \ImageSpec, into which will be stored the spec of the new subimage/miplevel. The {\cf seek_subimage()} function returns {\cf true} upon success, and {\cf false} if no such subimage or MIP level existed. It is legal to visit subimages and MIP levels out of order; the \ImageInput is responsible for making it work properly. It is also possible to find out which subimage and MIP level is currently being viewed, using the {\cf current_subimage()} and {\cf current_miplevel()} functions, which return the index of the current subimage and MIP levels, respectively. Below is pseudocode for reading all the levels of a MIP-map (a multi-resolution image used for texture mapping) that shows how to read multi-image files: \begin{code} ImageInput *in = ImageInput::open (filename); const ImageSpec &spec = in->spec(); int num_miplevels = 0; while (in->seek_subimage (0, num_miplevels, spec)) { // Note: spec has the format of the current subimage/miplevel int npixels = spec.width * spec.height; int nchannels = spec.nchannels; unsigned char *pixels = new unsigned char [npixels * nchannels]; in->read_image (TypeDesc::UINT8, pixels); ... do whatever you want with this level, in pixels ... delete [] pixels; ++num_miplevels; } // Note: we break out of the while loop when seek_subimage fails // to find a next MIP level. in->close (); ImageInput::destroy (in); \end{code} In this example, we have used \readimage, but of course \readscanline and \readtile work as you would expect, on the current subimage and MIP level. \subsection{Per-channel formats} \label{sec:imageinput:channelformats} Some image formats allow separate per-channel data formats (for example, {\cf half} data for colors and {\cf float} data for depth). If you want to read the pixels in their true native per-channel formats, the following steps are necessary: \begin{enumerate} \item Check the \ImageSpec's {\cf channelformats} vector. If non-empty, the channels in the file do not all have the same format. \item When calling {\cf read_scanline}, {\cf read_scanlines}, {\cf read_tile}, {\cf read_tiles}, or {\cf read_image}, pass a format of {\cf TypeDesc::UNKNOWN} to indicate that you would like the raw data in native per-channel format of the file written to your {\cf data} buffer. \end{enumerate} For example, the following code fragment will read a 5-channel image to an OpenEXR file, consisting of R/G/B/A channels in {\cf half} and a Z channel in {\cf float}: \begin{code} ImageInput *in = ImageInput::open (filename); const ImageSpec &spec = in->spec(); // Allocate enough space unsigned char *pixels = new unsigned char [spec.image_bytes(true)]; in->read_image (TypeDesc::UNKNOWN, /* use native channel formats */ pixels); /* data buffer */ if (spec.channelformats.size() > 0) { ... the buffer contains packed data in the native per-channel formats ... } else { ... the buffer contains all data per spec.format ... } \end{code} \subsection{Reading ``deep'' data} \label{sec:imageinput:deepdata} \index{deep data} Some image file formats (OpenEXR only, at this time) support the concept of ``deep'' pixels -- those containing multiple samples per pixel (and a potentially differing number of them in each pixel). You can tell an image is ``deep'' from its \ImageSpec: the {\cf deep} field will be {\cf true}. Deep files cannot be read with the usual {\cf read_scanline}, {\cf read_scanlines}, {\cf read_tile}, {\cf read_tiles}, {\cf read_image} functions, due to the nature of their variable number of samples per pixel. Instead, \ImageInput has three special member functions used only for reading deep data: \begin{code} bool read_native_deep_scanlines (int ybegin, int yend, int z, int chbegin, int chend, DeepData &deepdata); bool read_native_deep_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, DeepData &deepdata); bool read_native_deep_image (DeepData &deepdata); \end{code} It is only possible to read ``native'' data types from deep files; that is, there is no automatic translation into arbitrary data types as there is for ordinary images. All three of these functions store the resulting deep data in a special {\cf DeepData} structure, described in detail in Section~\ref{sec:deepdata}. Here is an example of using these methods to read a deep image from a file and print all its values: \begin{code} ImageInput *in = ImageInput::open (filename); if (! in) return; const ImageSpec &spec = in->spec(); if (spec.deep) { DeepData deepdata; in->read_native_deep_image (deepdata); int p = 0; // absolute pixel number for (int y = 0; y < spec.height; ++y) { for (int x = 0; x < spec.width; ++x, ++p) { std::cout << "Pixel " << x << "," << y << ":\n"; if (deepdata.samples(p) == 0) std::cout << " no samples\n"; else for (int c = 0; c < spec.nchannels; ++c) { TypeDesc type = deepdata.channeltype(c); std::cout << " " << spec.channelnames[c] << ": "; void *ptr = deepdata.pointers[p*spec.nchannels+c] for (int s = 0; s < deepdata.samples(p); ++s) { if (type.basetype == TypeDesc::FLOAT || type.basetype == TypeDesc::HALF) std::cout << deepdata.deep_value(p, c, s) << ' '; else if (type.basetype == TypeDesc::UINT32) std::cout << deepdata.deep_value_uint(p, c, s) << ' '; } std::cout << "\n"; } } } } in->close (); ImageInput::destroy (in); \end{code} \subsection{Custom search paths for plugins} \label{sec:imageinput:searchpaths} Please see Section~\ref{sec:miscapi} for discussion about setting the plugin search path via the {\cf attribute()} function. For example: \begin{code} std::string mysearch = "/usr/myapp/lib:${HOME}/plugins"; OpenImageIO::attribute ("plugin_searchpath", mysearch); ImageInput *in = ImageInput::open (filename); ... \end{code} %$ \subsection{Error checking} \label{sec:imageinput:errors} \index{error checking} Nearly every \ImageInput API function returns a {\cf bool} indicating whether the operation succeeded ({\cf true}) or failed ({\cf false}). In the case of a failure, the \ImageInput will have saved an error message describing in more detail what went wrong, and the latest error message is accessible using the \ImageInput method {\cf geterror()}, which returns the message as a {\cf std::string}. The exceptions to this rule are static methods such as the static {\cf ImageInput::open} and {\cf ImageInput::create}, which return {\cf NULL} if it could not create an appropriate \ImageInput (and open it, in the case of {\cf open()}. In such a case, since no \ImageInput is returned for which you can call its {\cf geterror()} function, there exists a global {\cf geterror()} function (in the {\cf OpenImageIO} namespace) that retrieves the latest error message resulting from a call to static {\cf open()} or {\cf create()}. Here is another version of the simple image reading code from Section~\ref{sec:imageinput:simple}, but this time it is fully elaborated with error checking and reporting: \begin{code} #include OIIO_NAMESPACE_USING ... const char *filename = "foo.jpg"; int xres, yres, channels; std::vector pixels; ImageInput *in = ImageInput::open (filename); if (! in) { std::cerr << "Could not open " << filename << ", error = " << OpenImageIO::geterror() << "\n"; return; } const ImageSpec &spec = in->spec(); xres = spec.width; yres = spec.height; channels = spec.nchannels; pixels.resize (xres*yres*channels); if (! in->read_image (TypeDesc::UINT8, &pixels[0])) { std::cerr << "Could not read pixels from " << filename << ", error = " << in->geterror() << "\n"; ImageInput::destroy (in); return; } if (! in->close ()) { std::cerr << "Error closing " << filename << ", error = " << in->geterror() << "\n"; ImageInput::destroy (in); return; } ImageInput::destroy (in); \end{code} \newpage \section{\ImageInput Class Reference} \label{sec:imageinput:reference} \apiitem{ImageInput * {\ce open} (const std::string \&filename, \\ \bigspc\bigspc const ImageSpec *config=NULL)} Create an \ImageInput subclass instance that is able to read the given file and open it, returning the opened \ImageInput if successful. If it fails, return {\cf NULL} and set an error that can be retrieved by {\cf OpenImageIO::geterror()}. The {\cf config}, if not {\cf NULL}, points to an \ImageSpec giving requests or special instructions. \ImageInput implementations are free to not respond to any such requests, so the default implementation is just to ignore config. The {\cf open()} function will first try to make an \ImageInput corresponding to the format implied by the file extension (for example, \qkw{foo.tif} will try the TIFF plugin), but if one is not found or if the inferred one does not open the file, every known \ImageInput type will be tried until one is found that will open the file. \apiend \apiitem{ImageInput * {\ce create} (const std::string \&filename, \\ \bigspc\bigspc const std::string \&plugin_searchpath="")} Create and return an \ImageInput implementation that is able to read the given file. The {\kw plugin_searchpath} parameter is a colon-separated list of directories to search for \product plugin DSO/DLL's (not a searchpath for the image itself!). This will actually just try every ImageIO plugin it can locate, until it finds one that's able to open the file without error. This just creates the \ImageInput, it does not open the file. \apiend \apiitem{void {\ce destroy} (ImageInput *input)} Destroy an \ImageInput that was created by {\cf create()} or {\cf open()}. The {\cf destroy()} method is just a wrapper around operator {\cf delete}, but by being implemented within the \product DLL, it can ensure that the memory deallocation is done in the same DLL arena as where it was originally allocated. This is considered safer than a bare {\cf delete} when used inside ``plug-ins,'' especially on Windows systems. \apiend \apiitem{const char * {\ce format_name} (void) const} Return the name of the format implemented by this class. \apiend \apiitem{int {\ce supports} (string_view feature)} \label{sec:inputsupportsfeaturelist} Given the name of a \emph{feature}, tells if this \ImageInput instance supports that feature. Most queries will simply return 0 for ``doesn't support the feature'' and nonzero for ``supports the feature,'' but it is acceptable to have queries return other nonzero integers to indicate varying degrees of support or limits (but those queries should be clearly documented as such). The following features are recognized by this query: \begin{description} \item[\spc] \spc \item[\rm \qkw{arbitrary_metadata}] Does the image file format allow metadata with arbitrary names (and either arbitrary, or a reasonable set of, data types)? (Versus the file format supporting only a fixed list of specific metadata names/values? \item[\rm \qkw{exif}] Does the image file format support Exif camera data (either specifically, or via arbitrary named metadata)? \item[\rm \qkw{iptc}] Does the image file format support IPTC data (either specifically, or via arbitrary named metadata)? \item[\rm \qkw{procedural}] Might the image ``file format'' generate pixels procedurally, without the need for any disk file to be present? \end{description} \apiend \apiitem{bool {\ce valid_file} (const std::string \&filename) const} Return {\cf true} if the named file is a file of the type for this \ImageInput. The implementation will try to determine this as efficiently as possible, in most cases much less expensively than doing a full {\cf open()}. Note that a file can appear to be of the right type (i.e., {\cf valid_file()} returning {\cf true}) but still fail a subsequent call to {\cf open()}, such as if the contents of the file are truncated, nonsensical, or otherwise corrupted. \apiend \apiitem{bool {\ce open} (const std::string \&name, ImageSpec \&newspec)} Opens the file with given name and seek to the first subimage in the file. Various file attributes are put in {\kw newspec} and a copy is also saved internally to the \ImageInput (retrievable via {\kw spec()}. From examining {\kw newspec} or {\kw spec()}, you can discern the resolution, if it's tiled, number of channels, native data format, and other metadata about the image. Return {\kw true} if the file was found and opened okay, otherwise {\kw false}. \apiend \apiitem{bool {\ce open} (const std::string \&name, ImageSpec \&newspec,\\ \bigspc const ImageSpec \&config)} \label{sec:inputwithconfig} Opens the file with given name, similarly to {\cf open(name, newspec)}. However, in this version, any non-default fields of {\cf config}, including metadata, will be taken to be configuration requests, preferences, or hints. The default implementation of {\cf open (name, newspec, config)} will simply ignore {\cf config} and calls the usual {\cf open (name, newspec)}. But a plugin may choose to implement this version of {\cf open} and respond in some way to the configuration requests. Supported configuration requests should be documented by each plugin. \apiend \apiitem {const ImageSpec \& {\ce spec} (void) const} Returns a reference to the image format specification of the current subimage. Note that the contents of the spec are invalid before {\kw open()} or after {\kw close()}. \apiend \apiitem{bool {\ce close} ()} Closes an open image. \apiend \apiitem{int {\ce current_subimage} (void) const} Returns the index of the subimage that is currently being read. The first subimage (or the only subimage, if there is just one) is number 0. \apiend \apiitem{bool {\ce seek_subimage} (int subimage, int miplevel, ImageSpec \&newspec)} Seek to the given subimage and MIP-map level within the open image file. The first subimage in the file has index 0, and for each subimage, the highest-resolution MIP level has index 0. Return {\kw true} on success, {\kw false} on failure (including that there is not a subimage or MIP level with those indices). The new subimage's vital statistics are put in {\kw newspec} (and also saved internally in a way that can be retrieved via {\kw spec()}). The \ImageInput is expected to give the appearance of random access to subimages and MIP levels --- in other words, if it can't randomly seek to the given subimage or MIP level, it should transparently close, reopen, and sequentially read through prior subimages and levels. \apiend \apiitem{bool {\ce read_scanline} (int y, int z, TypeDesc format, void *data,\\ \bigspc\spc\spc stride_t xstride=AutoStride)} Read the scanline that includes pixels $(*,y,z)$ into {\kw data} ($z=0$ for non-volume images), converting if necessary from the native data format of the file into the {\kw format} specified. If {\cf format} is {\cf TypeDesc::UNKNOWN}, the data will be preserved in its native format (including per-channel formats, if applicable). The {\kw xstride} value gives the data spacing of adjacent pixels (in bytes). Strides set to the special value {\kw AutoStride} imply contiguous data, i.e., \\ \spc {\kw xstride} $=$ {\kw spec.nchannels * spec.pixel_size()} \\ The \ImageInput is expected to give the appearance of random access --- in other words, if it can't randomly seek to the given scanline, it should transparently close, reopen, and sequentially read through prior scanlines. The base \ImageInput class has a default implementation that calls {\kw read_native_scanline()} and then does appropriate format conversion, so there's no reason for each format plugin to override this method. \apiend \apiitem{bool {\ce read_scanline} (int y, int z, float *data)} This simplified version of {\kw read_scanline()} reads to contiguous float pixels. \apiend \apiitem{bool {\ce read_scanlines} (int ybegin, int yend, int z,\\ \bigspc TypeDesc format, void *data,\\ \bigspc stride_t xstride=AutoStride, stride_t ystride=AutoStride) \\ bool {\ce read_scanlines} (int ybegin, int yend, int z,\\ \bigspc int chbegin, int chend, TypeDesc format, void *data,\\ \bigspc stride_t xstride=AutoStride, stride_t ystride=AutoStride)} Read all the scanlines that include pixels $(*,y,z)$, where $\mathit{ybegin} \le y < \mathit{yend}$, into {\kw data}. This is essentially identical to \readscanline, except that can read more than one scanline at a time, which may be more efficient for certain image format readers. The version that specifies a channel range will read only channels $[${\cf chbegin},{\cf chend}$)$ into the buffer. \apiend \apiitem{bool {\ce read_tile} (int x, int y, int z, TypeDesc format, void *data, \\ \bigspc stride_t xstride=AutoStride, stride_t ystride=AutoStride, \\ \bigspc stride_t zstride=AutoStride)} Read the tile whose upper-left origin is $(x,y,z)$ into {\kw data} ($z=0$ for non-volume images), converting if necessary from the native data format of the file into the {\kw format} specified. If {\cf format} is {\cf TypeDesc::UNKNOWN}, the data will be preserved in its native format (including per-channel formats, if applicable). The stride values give the data spacing of adjacent pixels, scanlines, and volumetric slices, respectively (measured in bytes). Strides set to the special value of {\kw AutoStride} imply contiguous data in the shape of a full tile, i.e., \\ \spc {\kw xstride} $=$ {\kw spec.nchannels * spec.pixel_size()} \\ \spc {\kw ystride} $=$ {\kw xstride * spec.tile_width} \\ \spc {\kw zstride} $=$ {\kw ystride * spec.tile_height} \\ The \ImageInput is expected to give the appearance of random access --- in other words, if it can't randomly seek to the given tile, it should transparently close, reopen, and sequentially read through prior tiles. The base \ImageInput class has a default implementation that calls {\cf read_native_tile()} and then does appropriate format conversion, so there's no reason for each format plugin to override this method. This function returns {\cf true} if it successfully reads the tile, otherwise {\cf false} for a failure. The call will fail if the image is not tiled, or if $(x,y,z)$ is not actually a tile boundary. \apiend \apiitem{bool {\ce read_tile} (int x, int y, int z, float *data)} Simple version of {\kw read_tile} that reads to contiguous float pixels. \apiend \apiitem{bool {\ce read_tiles} (int xbegin, int xend, int ybegin, int yend, \\ \bigspc int zbegin, int zend, TypeDesc format, void *data, \\ \bigspc stride_t xstride=AutoStride, stride_t ystride=AutoStride, \\ \bigspc stride_t zstride=AutoStride) \\ bool {\ce read_tiles} (int xbegin, int xend, int ybegin, int yend, \\ \bigspc int zbegin, int zend, int chbegin, int chend,\\ \bigspc TypeDesc format, void *data, \\ \bigspc stride_t xstride=AutoStride, stride_t ystride=AutoStride, \\ \bigspc stride_t zstride=AutoStride)} Read the tiles bounded by {\kw xbegin} $\le x <$ {\kw xend}, {\kw ybegin} $\le y <$ {\kw yend}, {\kw zbegin} $\le z <$ {\kw zend} into {\kw data} converting if necessary from the file's native data format into the specified buffer {\kw format}. If {\cf format} is {\cf TypeDesc::UNKNOWN}, the data will be preserved in its native format (including per-channel formats, if applicable). The stride values give the data spacing of adjacent pixels, scanlines, and volumetric slices, respectively (measured in bytes). Strides set to the special value of {\kw AutoStride} imply contiguous data in the shape of the region specified, i.e., \\ \spc {\kw xstride} $=$ {\kw spec.nchannels * spec.pixel_size()} \\ \spc {\kw ystride} $=$ {\kw xstride * (xend - xbegin)} \\ \spc {\kw zstride} $=$ {\kw ystride * (yend - ybegin)} \\ The \ImageInput is expected to give the appearance of random access --- in other words, if it can't randomly seek to the given tile, it should transparently close, reopen, and sequentially read through prior tiles. The base \ImageInput class has a default implementation that calls {\cf read_native_tiles()} and then does appropriate format conversion, so there's no reason for each format plugin to override this method. This function returns {\cf true} if it successfully reads the tiles, otherwise {\cf false} for a failure. The call will fail if the image is not tiled, or if the pixel ranges do not fall along tile (or image) boundaries, or if it is not a valid tile range. The version that specifies a channel range will read only channels $[${\cf chbegin},{\cf chend}$)$ into the buffer. \apiend \apiitem{bool {\ce read_image} (TypeDesc format, void *data, \\ \bigspc stride_t xstride=AutoStride, stride_t ystride=AutoStride, \\ \bigspc stride_t zstride=AutoStride, \\ \bigspc ProgressCallback progress_callback=NULL,\\ \bigspc void *progress_callback_data=NULL) \\ bool {\ce read_image} (int chbegin, int chend, \\ \bigspc TypeDesc format, void *data, \\ \bigspc stride_t xstride=AutoStride, stride_t ystride=AutoStride, \\ \bigspc stride_t zstride=AutoStride, \\ \bigspc ProgressCallback progress_callback=NULL,\\ \bigspc void *progress_callback_data=NULL)} Read the entire image of {\kw spec.width * spec.height * spec.depth} pixels into data (which must already be sized large enough for the entire image) with the given strides, converting into the desired data format. If {\cf format} is {\cf TypeDesc::UNKNOWN}, the data will be preserved in its native format (including per-channel formats, if applicable). This function will automatically handle either tiles or scanlines in the file. Strides set to the special value of {\kw AutoStride} imply contiguous data, i.e., \\ \spc {\kw xstride} $=$ {\kw spec.nchannels * pixel_size()} \\ \spc {\kw ystride} $=$ {\kw xstride * spec.width} \\ \spc {\kw zstride} $=$ {\kw ystride * spec.height} \\ The function will internally either call {\kw read_scanlines} or {\kw read_tiles}, depending on whether the file is scanline- or tile-oriented. The version that specifies a channel range will read only channels $[${\cf chbegin},{\cf chend}$)$ into the buffer. Because this may be an expensive operation, a progress callback may be passed. Periodically, it will be called as follows:\\ \begin{code} progress_callback (progress_callback_data, float done) \end{code} \noindent where \emph{done} gives the portion of the image (between 0.0 and 1.0) that has been read thus far. \apiend \apiitem{bool {\ce read_image} (float *data)} Simple version of {\kw read_image()} reads to contiguous float pixels. \apiend \apiitem{bool {\ce read_native_scanline} (int y, int z, void *data)} The {\kw read_native_scanline()} function is just like {\kw read_scanline()}, except that it keeps the data in the native format of the disk file and always reads into contiguous memory (no strides). It's up to the user to have enough space allocated and know what to do with the data. IT IS EXPECTED THAT EACH FORMAT PLUGIN WILL OVERRIDE THIS METHOD. \apiend \apiitem{bool {\ce read_native_scanlines} (int ybegin, int yend, int z, void *data)} The {\kw read_native_scanlines()} function is just like {\cf read_native_scanline}, except that it reads a range of scanlines rather than only one scanline. It is not necessary for format plugins to override this method --- a default implementation in the \ImageInput base class simply calls {\cf read_native_scanline} for each scanline in the range. But format plugins may optionally override this method if there is a way to achieve higher performance by reading multiple scanlines at once. \apiend \apiitem{bool {\ce read_native_scanlines} (int ybegin, int yend, int z, \\ \bigspc int chbegin, int chend, void *data)} A variant of {\cf read_native_scanlines} that reads only a subset of channels \\ $[${\cf chbegin},{\cf chend}$)$. If a format reader subclass does not override this method, the default implementation will simply call the all-channel version of {\cf read_native_scanlines} into a temporary buffer and copy the subset of channels. \apiend \apiitem{bool {\ce read_native_tile} (int x, int y, int z, void *data)} The {\kw read_native_tile()} function is just like {\kw read_tile()}, except that it keeps the data in the native format of the disk file and always read into contiguous memory (no strides). It's up to the user to have enough space allocated and know what to do with the data. IT IS EXPECTED THAT EACH FORMAT PLUGIN WILL OVERRIDE THIS METHOD IF IT SUPPORTS TILED IMAGES. \apiend \apiitem{bool {\ce read_native_tiles} (int xbegin, int xend, int ybegin, int yend, \\ \bigspc int zbegin, int zend, void *data)} The {\kw read_native_tiles()} function is just like {\kw read_tiles()}, except that it keeps the data in the native format of the disk file and always read into contiguous memory (no strides). If a format reader does not override this method, the default implementation it will simply be a loop calling read_native_tile for each tile in the block. \apiend \apiitem{bool {\ce read_native_tiles} (int xbegin, int xend, int ybegin, int yend, \\ \bigspc int zbegin, int zend, int chbegin, int chend, void *data)} A variant of {\kw read_native_tiles()} that reads only a subset of channels \\ $[${\cf chbegin},{\cf chend}$)$. If a format reader subclass does not override this method, the default implementation will simply call the all-channel version of {\cf read_native_tiles} into a temporary buffer and copy the subset of channels. \apiend \apiitem{bool {\ce read_native_deep_scanlines} (int ybegin, int yend, int z, \\ \bigspc int chbegin, int chend, DeepData \&deepdata) \\ bool {\ce read_native_deep_tiles} (int xbegin, int xend, int ybegin, int yend, \\ \bigspc int zbegin, int zend, int chbegin, int chend, DeepData \&deepdata) \\ bool {\ce read_native_deep_image} (DeepData \&deepdata)} Read native deep data from scanlines, tiles, or an entire image, storing the results in {\cf deepdata} (analogously to the usual {\cf read_scanlines}, {\cf read_tiles}, and {\cf read_image}, but with deep data). Only channels $[${\cf chbegin},{\cf chend}$)$ will be read. \apiend \apiitem{int {\ce send_to_input} (const char *format, ...)} General message passing between client and image input server. This is currently undefined and is reserved for future use. \apiend \apiitem{int {\ce send_to_client} (const char *format, ...)} General message passing between client and image input server. This is currently undefined and is reserved for future use. \apiend \apiitem{void {\ce threads} (int n) \\ int {\ce threads} () const} \index{threads} Get or set the threading policy for this \ImageInput, controlling the maximum amount of parallelizing thread ``fan-out'' that might occur during large read operations. The default of 0 means that the global {\cf attribute("threads")} value should be used (which itself defaults to using as many threads as cores; see Section~\ref{sec:attribute:threads}). The main reason to change this value is to set it to 1 to indicate that the calling thread should do all the work rather than spawning new threads. That is probably the desired behavior in situations where the calling application has already spawned multiple worker threads. \apiend \apiitem{std::string {\ce geterror} () const} \index{error checking} Returns the current error string describing what went wrong if any of the public methods returned {\kw false} indicating an error. (Hopefully the implementation plugin called {\kw error()} with a helpful error message.) \apiend \index{Image I/O API|)} \chapwidthend openimageio-1.7.17~dfsg0.orig/src/doc/imagebuf.tex0000644000175000017500000012324613151711064020172 0ustar mfvmfv\chapter{Image Buffers} \label{chap:imagebuf} \index{Image Buffers|(} \index{ImageBuf|(} \section{ImageBuf Introduction and Theory of Operation} \label{sec:imagebuf:intro} \ImageBuf is a utility class that stores an entire image. It provides a nice API for reading, writing, and manipulating images as a single unit, without needing to worry about any of the details of storage or I/O. \smallskip \noindent An \ImageBuf can store its pixels in one of several ways: \begin{itemize} \item Allocate ``local storage'' to hold the image pixels internal to the \ImageBuf. This storage will be freed when the \ImageBuf is destroyed. \item ``Wrap'' pixel memory already allocated by the calling application, which will continue to own that memory and be responsible for freeing it after the \ImageBuf is destroyed. \item Be ``backed'' by an \ImageCache, which will automatically be used to retreive pixels when requested, but the \ImageBuf will not allocate separate storage for it. This brings all the advantages of the \ImageCache, but can only be used for read-only \ImageBuf's that reference a stored image file. \end{itemize} All I/O involving \ImageBuf (that is, calls to {\cf read} or {\cf write}) are implemented in terms of \ImageCache, \ImageInput, and \ImageOutput underneath, and so support all of the image file formats supported by OIIO. \smallskip \noindent The \ImageBuf class definition requires that you \begin{code} #include \end{code} \subsection{Helper: \ROI} \label{sec:ROI} \indexapi{ROI} \index{region of interest} \ROI is a small helper struct that describes a rectangular region of pixels of an image (and a channel range). An \ROI holds the following data members: \apiitem{int xbegin, xend, ybegin, yend, zbegin, zend; \\ int chbegin, chend;} Describes the $x, y, z$ range of the region. The {\cf end} values are \emph{exclusive}; that is, ({\cf xbegin, ybegin, zbegin}) is the first pixel included in the range, ({\cf xend-1, yend-1, zend-1}) is the last pixel included in the range, and ({\cf xend, yend, zend}) is \emph{one past the last pixel} in each dimension. Similarly, {\cf chbegin} and {\cf chend} describe a range of channels: {\cf chbegin} is the first channel, {\cf chend} is \emph{one past the last channel}. \apiend \smallskip \noindent \ROI has the following member functions and friends: \smallskip \apiitem{{\ce ROI} ()} A default-constructed \ROI has an \emph{undefined} region, which is interpreted to mean all valid pixels and all valid channels of an image. \apiend \apiitem{bool ROI::{\ce defined} () const} Returns {\cf true} if the \ROI is defined, having a specified region, or {\cf false} if the \ROI is undefined. \apiend \apiitem{static ROI ROI::{\ce All} ()} Returns an undefined \ROI, which is interpreted to mean all valid pixels and all valid channels of an image. \apiend \apiitem{int ROI::{\ce width} () const \\ int ROI::{\ce height} () const \\ int ROI::{\ce depth} () const} Returns the width, height, and depth, respectively, of a defined region. These do not return sensible values for an \ROI that is not {\cf defined()}. \apiend \apiitem{imagesize_t ROI::{\ce npixels} () const} For an \ROI that is {\cf defined()}, returns the total number of pixels included in the region, or {\cf 0} for an undefined \ROI. \apiend \apiitem{imagesize_t ROI::{\ce nchannels} () const} For an \ROI that is {\cf defined()}, returns the number of channels in the channel range. \apiend \apiitem{ROI {\ce roi_union} (const ROI \&A, const ROI \&B) \\ ROI {\ce roi_intersection} (const ROI \&A, const ROI \&B)} Returns the union of two \ROI's (an \ROI that is exactly big enough to include all the pixels of both individual \ROI's) or intersection of two \ROI's (an \ROI that contains only the pixels that are contained in \emph{both} \ROI's). \apiend \apiitem{ROI {\ce get_roi} (const ImageSpec \&spec) \\ ROI {\ce get_roi_full} (const ImageSpec \&spec)} Return the ROI describing {\cf spec}'s pixel data window (the {\cf x, y, z, width, height, depth} fields) or the full (display) window (the {\cf full_x, full_y, full_z, full_width, full_height, full_depth} fields), respectively. \apiend \apiitem{void {\ce set_roi} (const ImageSpec \&spec, const ROI \&newroi) \\ void {\ce set_roi_full} (const ImageSpec \&spec, const ROI \&newroi)} Alters the {\cf spec} so to make its pixel data window or the full (display) window match {\cf newroi}. \apiend \section{Constructing, reading, and writing an \ImageBuf} \subsection*{Default constructor of an empty \ImageBuf} \apiitem{{\ce ImageBuf} ()} The default constructor makes an uninitialized \ImageBuf. There isn't much you can do with an uninitialized buffer until you call {\cf reset()}. \apiend \apiitem{void {\ce clear} ()} Resets the \ImageBuf to a pristine state identical to that of a freshly constructed \ImageBuf using the default constructor. \apiend \subsection*{Constructing and initializing a writeable \ImageBuf} \apiitem{{\ce ImageBuf} (const ImageSpec \&spec) \\ {\ce ImageBuf} (string_view name, const ImageSpec \&spec)} Constructs a writeable \ImageBuf with the given specification (including resolution, data type, metadata, etc.), initially set to all black pixels. Optionally, you may name the \ImageBuf. \apiend \apiitem{void {\ce reset} (const ImageSpec \&spec) \\ void {\ce reset} (string_view name, const ImageSpec \&spec)} Destroys any previous contents of the \ImageBuf and re-initializes it as a writeable all-black \ImageBuf with the given specification (including resolution, data type, metadata, etc.). Optionally, you may name the \ImageBuf. \apiend \apiitem{bool make_writeable (bool keep_cache_type = false)} Force the \ImageBuf to be writeable. That means that if it was previously backed by an \ImageCache (storage was {\cf IMAGECACHE}), it will force a full read so that the whole image is in local memory. This will invalidate any current iterators on the image. It has no effect if the image storage not {\cf IMAGECACHE}. Return {\cf true} if it works (including if no read was necessary), {\cf false} if something went horribly wrong. If {\cf keep_cache_type} is true, it preserves any \ImageCache-forced data types (you might want to do this if it is critical that the apparent data type doesn't change, for example if you are calling make_writeable from within a type-specialized function). \apiend \subsection*{Constructing a readable \ImageBuf and reading from a file} Constructing a readable \ImageBuf that will hold an image to be read from disk. \apiitem{{\ce ImageBuf} (string_view name, int subimage=0, int miplevel=0, \\ \bigspc\bigspc ImageCache *imagecache = NULL, \\ \bigspc\bigspc const ImageSpec *config = NULL)} Construct an \ImageBuf that will be used to read the named file (at the given subimage and MIP-level, defaulting to the first in the file). But don't read it yet! The image will actually be read when other methods need to access the spec and/or pixels, or when an explicit call to {\cf init_spec()} or {\cf read()} is made, whichever comes first. If {\cf imagecache} is non-NULL, the custom \ImageCache will be used (if applicable); otherwise, a NULL imagecache indicates that the global/shared \ImageCache should be used. If {\cf config} is not NULL, it points to an \ImageSpec giving requests or special instructions to be passed on to the eventual {\cf ImageInput::open()} call. \apiend \apiitem{void {\ce reset} (string_view name, int subimage=0, int miplevel=0, \\ \bigspc\bigspc ImageCache *imagecache = NULL \\ \bigspc\bigspc const ImageSpec *config = NULL)} Destroys any previous contents of the \ImageBuf and re-initializes it to read the named file (but doesn't actually read yet). If {\cf config} is not NULL, it points to an \ImageSpec giving requests or special instructions to be passed on to the eventual {\cf ImageInput::open()} call. \apiend \apiitem{bool {\ce read} (int subimage=0, int miplevel=0, bool force=false, \\ \bigspc TypeDesc convert=TypeDesc::UNKNOWN, \\ \bigspc ProgressCallback progress_callback=NULL, \\ \bigspc void *progress_callback_data=NULL)} Explicitly reads the particular subimage and MIP level of the image. Generally, this will skip the expensive read if the file has already been read into the \ImageBuf (at the specified subimage and MIP level). It will clear and re-allocate memory if the previously allocated space was not appropriate for the size or data type of the image being read. If {\cf convert} is set to a specific type (not {\cf UNKNOWN}), the \ImageBuf memory will be allocated for that type specifically and converted upon read. In general, {\cf read()} will try not to do any I/O at the time of the {\cf read()} call, but rather to have the \ImageBuf ``backed'' by an \ImageCache, which will do the file I/O on demand, as pixel values are needed, and in that case the \ImageBuf doesn't actually allocate memory for the pixels (the data lives in the \ImageCache). However, there are several conditions for which the \ImageCache will be bypassed, the \ImageBuf will allocate ``local'' memory, and the disk file will be read directly into allocated buffer at the time of the {\cf read()} call: (a) if the {\cf force} parameter is {\cf true}; (b) if the {\cf convert} parameter requests a data format conversion to a type that is not the native file type and also is not one of the internal types supported by the {\cf ImageCache} (specifically, {\cf FLOAT} and {\cf UINT8}); (c) if the \ImageBuf already has local pixel memory allocated, or ``wraps'' an application buffer. If {\cf progress_callback} is non-NULL, the underlying read, if expensive, may make several calls to \begin{code} progress_callback(progress_callback_data, portion_done); \end{code} \noindent which allows you to implement some sort or progress meter. Note that if the \ImageBuf is backed by an \ImageCache, the progress callback will never be called, since no actual file I/O will occur at this time (\ImageCache will load tiles or scanlines on demand, as individual pixel values are needed). Note that {\cf read()} is not strictly necessary. If you are happy with the filename, subimage and MIP level specified by the \ImageBuf constructor (or the last call to {\cf reset()}), and you want the storage to be backed by the {\cf ImageCache} (including storing the pixels in whatever data format that implies), then the file contents will be automatically read the first time you make any other \ImageBuf API call that requires the spec or pixel values. The only reason to call {\cf read()} yourself is if you are changing the filename, subimage, or MIP level, or if you want to use {\cf force=true} or a specific {\cf convert} value to force data format conversion. \apiend \apiitem{bool {\ce init_spec} (string_view filename, int subimage, int miplevel)} This call will read the \ImageSpec for the given file, subimage, and MIP level into the \ImageBuf, but will not read the pixels or allocate any local storage (until a subsequent call to {\cf read()}). This is helpful if you have an \ImageBuf and you need to know information about the image, but don't want to do a full read yet, and maybe won't need to do the full read, depending on what's found in the spec. Note that {\cf init_spec()} is not strictly necessary. If you are happy with the filename, subimage and MIP level specified by the \ImageBuf constructor (or the last call to {\cf reset()}), then the spec will be automatically read the first time you make any other \ImageBuf API call that requires it. The only reason to call {\cf read()} yourself is if you are changing the filename, subimage, or MIP level, or if you want to use {\cf force=true} or a specific {\cf convert} value to force data format conversion. \apiend \subsection*{Constructing an \ImageBuf that ``wraps'' an application buffer} \apiitem{{\ce ImageBuf} (const ImageSpec \&spec, void *buffer) \\ {\ce ImageBuf} (string_view name, const ImageSpec \&spec, void *buffer)} Constructs an ImageBuf that "wraps" a memory buffer owned by the calling application. It can write pixels to this buffer, but can't change its resolution or data type. Optionally, it names the \ImageBuf. \apiend \subsection*{Writing an \ImageBuf to a file} \apiitem{bool {\ce write} (string_view filename, \\ \bigspc string_view fileformat = "", \\ \bigspc ProgressCallback progress_callback=NULL, \\ \bigspc void *progress_callback_data=NULL) const} Write the image to the named file in the named format (an empty format means to infer the type from the filename extension). Return {\cf true} if all went ok, {\cf false} if there were errors writing. By default, it will always write a scanline-oriented file, unless the {\cf set_write_tiles()} method has been used to override this. Also, it will use the data format of the buffer itself, unless the {\cf set_write_format()} method has been used to override the data format. \apiend \apiitem{bool {\ce write} (ImageOutput *out, \\ \bigspc ProgressCallback progress_callback=NULL, \\ \bigspc void *progress_callback_data=NULL) const} Write the image to the open \ImageOutput {\cf out}. Return {\cf true} if all went ok, {\cf false} if there were errors writing. It does NOT close the file when it's done (and so may be called in a loop to write a multi-image file). \apiend \apiitem{void {\ce set_write_format} (TypeDesc format=TypeDesc::UNKNOWN) \\ void {\ce set_write_tiles} (int width=0, int height=0, int depth=0)} These methods allow the caller to override the data format and tile sizing when using the {\cf write()} function (the variety that does not take an open {\cf ImageOutput*}). \apiend \section{Getting and setting basic information about an \ImageBuf} \apiitem{bool {\ce initialized} () const} Returns {\cf true} if the \ImageBuf is initialized, {\cf false} if not yet initialized. \apiend \apiitem{IBStorage {\ce storage} () const} Returns an enumerated type describing the type of storage currently employed by the \ImageBuf: {\cf UNINITIALIZED} (no storage), {\cf LOCALBUFFER} (the \ImageBuf has allocated and owns the pixel memory), {\cf APPBUFFER} (the \ImageBuf ``wraps'' memory owned by the calling application), or {\cf IMAGECACHE} (the image is backed by an \ImageCache). \apiend \apiitem{const ImageSpec \& {\ce spec} () const \\ const ImageSpec \& {\ce nativespec} () const} The {\cf spec()} function returns a {\cf const} reference to an \ImageSpec that describes the image data held by the \ImageBuf. The {\cf nativespec()} function returns a {\cf const} reference to an \ImageSpec that describes the actual data in the file that was read. These may differ --- for example, if a data format conversion was requested, if the buffer is backed by an \ImageCache which stores the pixels internally in a different data format than that of the file, or if the file had differing per-channel data formats (\ImageBuf must contain a single data format for all channels). \apiend \apiitem{string_view {\ce name} () const} Returns the name of the buffer (name of the file, for an \ImageBuf read from disk). \apiend \apiitem{string_view {\ce file_format_name} () const} Returns the name of the file format, for an \ImageBuf read from disk (for example, \qkw{openexr}). \apiend \apiitem{int {\ce subimage} () const \\ int {\ce nsubimages} () const \\ int {\ce miplevel} () const \\ int {\ce nmiplevels} () const} The {\cf subimage()} and {\cf miplevel()} methods return the subimage and MIP level of the image held by the \ImageBuf (the file it came from may hold multiple subimages and/or MIP levels, but the \ImageBuf can only store one of those at any given time). The {\cf nsubimages()} method returns the total number of subimages in the file, and the {\cf nmiplevels()} method returns the total number of MIP levels in the currently-loaded subimage. \apiend \apiitem{int {\ce nchannels} () const} Returns the number of channels stored in the buffer (this is equivalent to {\cf spec().nchannels}). \apiend \apiitem{int {\ce xbegin} () const \\ int {\ce xend} () const \\ int {\ce ybegin} () const \\ int {\ce yend} () const \\ int {\ce zbegin} () const \\ int {\ce zend} () const} Returns the {\cf [begin,end)} range of the pixel data window of the buffer. These are equivalent to {\cf spec().x}, {\cf spec().x+spec().width}, {\cf spec().y}, {\cf spec().y+spec().height}, {\cf spec().z}, and {\cf spec().z+spec().depth}, respectively. \apiend \apiitem{int {\ce orientation} () const \\ int {\ce oriented_width} () const \\ int {\ce oriented_height} () const \\ int {\ce oriented_x} () const \\ int {\ce oriented_y} () const \\ int {\ce oriented_full_width} () const \\ int {\ce oriented_full_height} () const \\ int {\ce oriented_full_x} () const \\ int {\ce oriented_full_y} () const } The {\cf orientation()} returns the interpretation of the layout (top/bottom, left/right) of the image, per the table in Section~\ref{metadata:orientation}. The oriented width, height, x, and y describe the pixel data window after taking the display orientation into consideration. The \emph{full} versions the ``full'' (a.k.a.\ display) window after taking the display orientation into consideration. \apiend \apiitem{void {\ce set_orientation} (int orient)} Sets the \qkw{Orientation} metadata value. \apiend \apiitem{TypeDesc {\ce pixeltype} () const} The data type of the pixels stored in the buffer (equivalent to {\cf spec().format}). \apiend \apiitem{void {\ce set_full} (int xbegin, int xend, int ybegin, int yend, \\ \bigspc\spc int zbegin, int zend)} Alters the metadata of the spec in the \ImageBuf to reset the ``full'' image size (a.k.a.\ ``display window''). This does not affect the size of the pixel data window. \apiend \apiitem{ImageSpec \& {\ce specmod} ()} This returns a \emph{writeable} reference to the \ImageSpec describing the buffer. It's ok to modify most of the metadata, but if you modify the spec's {\cf format}, {\cf width}, {\cf height}, or {\cf depth} fields, you get the pain you deserve, as the \ImageBuf will no longer have correct knowledge of its pixel memory layout. USE WITH EXTREME CAUTION. \apiend \apiitem{void {\ce threads} (int n) \\ int {\ce threads} () const} \index{threads} Get or set the threading policy for this \ImageBuf, controlling the maximum amount of parallelizing thread ``fan-out'' that might occur during expensive operations. The default of 0 means that the global {\cf attribute("threads")} value should be used (which itself defaults to using as many threads as cores; see Section~\ref{sec:attribute:threads}). The main reason to change this value is to set it to 1 to indicate that the calling thread should do all the work rather than spawning new threads. That is probably the desired behavior in situations where the calling application has already spawned multiple worker threads. \apiend \section{Copying \ImageBuf's and blocks of pixels} \apiitem{bool {\ce copy} (const ImageBuf \&src) \\ bool {\ce copy} (const ImageBuf \&src, TypeDesc format)} Copies {\cf src} to {\cf this} -- both pixel values and all metadata. If a {\cf format} is provided, {\cf this} will get the specified pixel data type rather than using the same pixel format as {\cf src}. \apiend \apiitem{void {\ce copy_metadata} (const ImageBuf \&src)} Copies all metadata (except for {\cf format}, {\cf width}, {\cf height}, {\cf depth} from {\cf src} to {\cf this}. \apiend \apiitem{bool {\ce copy_pixels} (const ImageBuf \&src)} Copies the pixels of {\cf src} to {\cf this}, but does not change the metadata (other than format and resolution) of {\cf this}. \apiend \apiitem{void {\ce swap} (ImageBuf \&other)} Swaps the entire contents of {\cf other} and {\cf this}. \apiend \apiitem{bool {\ce get_pixels} (ROI roi, TypeDesc format, \\ \bigspc\bigspc void *result, stride_t xstride=AutoStride,\\ \bigspc\bigspc stride_t ystride=AutoStride, \\ \bigspc\bigspc stride_t zstride=AutoStride) const} Retrieve the rectangle of pixels specified by ROI, at the current subimage and MIP-map level, storing the pixel values beginning at the address specified by {\cf result} and with the given strides (by default, {\cf AutoStride} means the usual contiguous packing of pixels) and converting into the data type described by {\cf format}. It is up to the caller to ensure that {\cf result} points to an area of memory big enough to accommodate the requested rectangle. Return {\cf true} if the operation could be completed, otherwise return {\cf false}. \apiend \apiitem{bool {\ce get_pixel_channels} (int xbegin, int xend, int ybegin, int yend, \\ \bigspc\bigspc int zbegin, int zend, int chbegin, int chend,\\ \bigspc\bigspc TypeDesc format, void *result,\\ \bigspc\bigspc stride_t xstride=AutoStride,\\ \bigspc\bigspc stride_t ystride=AutoStride,\\ \bigspc\bigspc stride_t zstride=AutoStride) const} Retrieve the rectangle of pixels spanning {\cf [xbegin..xend)} $\times$ {\cf [ybegin..yend)} $\times$ {\cf [zbegin..zend)}, channels {\cf [chbegin,chend)} (all with exclusive end), specified as integer pixel coordinates, at the current subimage and MIP-map level, storing the pixel values beginning at the address specified by {\cf result} and with the given strides (by default, {\cf AutoStride} means the usual contiguous packing of pixels) and converting into the data type described by {\cf format}. It is up to the caller to ensure that {\cf result} points to an area of memory big enough to accommodate the requested rectangle. Return {\cf true} if the operation could be completed, otherwise return {\cf false}. \apiend \apiitem{bool {\ce get_pixels} (int xbegin, int xend, int ybegin, int yend, \\ \bigspc\bigspc int zbegin, int zend, TypeDesc format, \\ \bigspc\bigspc void *result, stride_t xstride=AutoStride,\\ \bigspc\bigspc stride_t ystride=AutoStride, \\ \bigspc\bigspc stride_t zstride=AutoStride) const} Retrieve the rectangle of pixels spanning {\cf [xbegin..xend)} $\times$ {\cf [ybegin..yend)} $\times$ {\cf [zbegin..zend)} (all with exclusive end), specified as integer pixel coordinates, at the current subimage and MIP-map level, storing the pixel values beginning at the address specified by {\cf result} and with the given strides (by default, {\cf AutoStride} means the usual contiguous packing of pixels) and converting into the data type described by {\cf format}. It is up to the caller to ensure that {\cf result} points to an area of memory big enough to accommodate the requested rectangle. Return {\cf true} if the operation could be completed, otherwise return {\cf false}. \apiend \apiitem{bool {\ce set_pixels} (ROI roi, TypeDesc format, \\ \bigspc\bigspc const void *result, stride_t xstride=AutoStride,\\ \bigspc\bigspc stride_t ystride=AutoStride, \\ \bigspc\bigspc stride_t zstride=AutoStride)} Copy the rectangle of data into the specified ROI of the ImageBuf. The data points to values specified by {\cf format}, with layout detailed by the stride values (in bytes, with {\cf AutoStride} indicating ``contiguous'' layout). It is up to the caller to ensure that data points to an area of memory big enough to account for the ROI. Return {\cf true} if the operation could be completed, otherwise return {\cf false}. \apiend \section{Getting and setting individual pixel values -- simple but slow} \apiitem{float {\ce getchannel} (int x, int y, int z, int c, \\ \bigspc\spc WrapMode wrap=WrapBlack) const} Returns the value of pixel {\cf x, y, z}, channel {\cf c}. The {\cf wrap} describes what value should be returned if the {\cf x, y, z} coordinates are outside the pixel data window, and may be one of: {\cf WrapBlack}, {\cf WrapClamp}, {\cf WrapPeriodic}, or {\cf WrapMirror}. \apiend \apiitem{void {\ce getpixel} (int x, int y, int z, float *pixel, \\ \bigspc\spc int maxchannels=1000, WrapMode wrap=WrapBlack) const} Retrieves pixel ({\cf x, y, z}), placing its contents in {\cf pixel[0..n-1]}, where $n$ is the smaller of {\cf maxchannels} or the actual number of channels stored in the buffer. It is up to the application to ensure that {\cf pixel} points to enough memory to hold the required number of channels. The {\cf wrap} describes what value should be returned if the {\cf x, y, z} coordinates are outside the pixel data window, and may be one of: {\cf WrapBlack}, {\cf WrapClamp}, {\cf WrapPeriodic}, or {\cf WrapMirror}. \apiend \apiitem{void {\ce interppixel} (float x, float y, float *pixel, \\ \bigspc\bigspc WrapMode wrap=WrapBlack) const \\ void {\ce interppixel_bicubic} (float x, float y, float *pixel, \\ \bigspc\bigspc WrapMode wrap=WrapBlack) const} Sample the image plane at coordinates $(x,y)$, using linear interpolation between pixels by default, or B-spline bicubic interpolation for the {\cf _bicubic} varieties, placing the result in {\cf pixel[0..n-1]}, where $n$ is the smaller of {\cf maxchannels} or the actual number of channels stored in the buffer. It is up to the application to ensure that {\cf pixel} points to enough memory to hold the required number of channels. The sampling location is specified in the floating-point pixel coordinate system, where pixel $(i,j)$ is centered at image plane coordinate $(i+0.5, j+0.5)$. The {\cf wrap} describes how the image function should be computed outside the boundaries of the pixel data window, and may be one of: {\cf WrapBlack}, {\cf WrapClamp}, {\cf WrapPeriodic}, or {\cf WrapMirror}. \apiend \apiitem{void {\ce interppixel_NDC} (float s, float t, float *pixel, \\ \bigspc\bigspc WrapMode wrap=WrapBlack) const \\ void {\ce interppixel_bicubic_NDC} (float s, float t, float *pixel, \\ \bigspc\bigspc WrapMode wrap=WrapBlack) const} Sample the image plane at coordinates $(s,t)$, similar to the {\cf\small interppixel} and {\cf\small interppixel_bicubic} methods, but specifying location using the ``NDC'' (normalized device coordinate) system where (0,0) is the upper left corner of the full (a.k.a. ``display'') window and (1,1) is the lower right corner of the full/display window. \apiend \apiitem{void {\ce setpixel} (int x, int y, int z, const float *pixel, int maxchannels=1000)} Set the pixel with coordinates {\cf (x, y, z)} to have the values {\cf pixel[0..n-1]}. The number of channels, {\cf n}, is the minimum of {\cf minchannels} and the actual number of channels in the image. \apiend \subsection*{Deep data in an \ImageBuf} \apiitem{bool {\ce deep} () const} Returns {\cf true} if the \ImageBuf holds a ``deep'' image, {\cf false} if the \ImageBuf holds an ordinary pixel-based image. \apiend \apiitem{int {\ce deep_samples} (int x, int y, int z=0) const} Returns the number of deep samples for the given pixel, or 0 if there are no deep samples for that pixel (including if the pixel coordinates are outside the data area). For non-deep images, it will always return 0. \apiend \apiitem{void {\ce set_deep_samples} (int x, int y, int z, int nsamples)} Set the number of samples for pixel {\cf (x,y,z)}. If data has already been allocated, this is equivalent to inserting or erasing samples. \apiend \apiitem{void {\ce deep_insert_samples} (int x, int y, int z, int samplepos, int nsamples)} \NEW % 1.7 Insert {\cf nsamples} new samples, starting at position {\cf samplepos} of pixel {\cf (x,y,z)}. \apiend \apiitem{void {\ce deep_erase_samples} (int x, int y, int z, int samplepos, int nsamples)} \NEW % 1.7 Remove {\cf nsamples} new samples, starting at position {\cf samplepos} of pixel {\cf (x,y,z)}. \apiend \apiitem{float {\ce deep_value} (int x, int y, int z, int c, int s) const \\ uint32_t {\ce deep_value_uint} (int x, int y, int z, int c, int s) const} Return the value of sample {\cf s} of channel {\cf c} of pixel {\cf (x,y,z)}. Return {\cf 0} if not a deep image or if the pixel coordinates, channel number, or sample number are out of range, or if it has no deep samples. You are expected to call the {\cf float} or {\cf uint32_t} version based on the data type of channel {\cf c}. \apiend \apiitem{void {\ce set_deep_value} (int x, int y, int z, int c, int s, float value) const \\ void {\ce set_deep_value} (int x, int y, int z, int c, int s, uint32_t value) const} Set the value of sample {\cf s} of channel {\cf c} of pixel {\cf (x,y,z)}. It is expected that you choose the {\cf float} or {\cf uint32_t} variety of the call based on the data type of channel {\cf c}. \apiend \apiitem{const void *{\ce deep_pixel_ptr} (int x, int y, int z, int c, int s=0) const} Returns a pointer to the raw pixel data pixel {\cf (x,y,z)}, channel {\cf c}, sample {\cf s}. This will return {\cf NULL} if the pixel coordinates or channel number are out of range, if the pixel/channel has no deep samples, or if the image is not deep. Use with caution --- these pointers may be invalidated by calls that adjust the number of samples in any pixel. \apiend \apiitem{DeepData\& {\ce deepdata} () \\ const DeepData\& {\ce deepdata} () const} Returns a reference to the underlying {\cf DeepData} for a deep image. \apiend \section{Miscellaneous} \apiitem{void {\ce error} (const char *format, ...) const} This can be used to register an error associated with this \ImageBuf. \apiend \apiitem{bool {\ce has_error} (void) const} Returns {\cf true} if the \ImageBuf has had an error and has an error message to retrieve via {\cf geterror()}. \apiend \apiitem{std::string {\ce geterror} (void) const} Return the text of all error messages issued since {\cf geterror()} was called (or an empty string if no errors are pending). This also clears the error message for next time. \apiend \apiitem{void *{\ce localpixels} (); \\ const void *{\ce localpixels} () const;} Returns a raw pointer to the ``local'' pixel memory, if they are fully in RAM and not backed by an \ImageCache (in which case, {\cf NULL} will be returned). You can also test it like a {\cf bool} to find out if pixels are local. \apiend \apiitem{const void *{\ce pixeladdr} (int x, int y, int z=0) const \\ void *{\ce pixeladdr} (int x, int y, int z)} Return the address where pixel (x,y,z) is stored in the image buffer. Use with extreme caution! Will return NULL if the pixel values aren't local (for example, if backed by an \ImageCache). \apiend \apiitem{int {\ce pixelindex} (int x, int y, int z, bool check_range=false) const} \NEW %1.7 Return the index of pixel (x,y,z). If {\cf check_range} is {\cf true}, return -1 for an invalid coordinate that is not within the data window. \apiend \section{Iterators -- the fast way of accessing individual pixels} Sometimes you need to visit every pixel in an \ImageBuf (or at least, every pixel in a large region). Using the {\cf getpixel} and {\cf setpixel} for this purpose is simple but very slow. But \ImageBuf provides templated {\cf Iterator} and {\cf ConstIterator} types that are very inexpensive and hide all the details of local versus cached storage. An {\cf Iterator} is associated with a particular \ImageBuf. The {\cf Iterator} has a \emph{current pixel} coordinate that it is visiting, and an \emph{iteration range} that describes a rectangular region of pixels that it will visits as it advances. It always starts at the upper left corner of the iteration region. We say that the iterator is \emph{done} after it has visited every pixel in its iteration range. We say that a pixel coordinate \emph{exists} if it is within the pixel data window of the \ImageBuf. We say that a pixel coordinate is \emph{valid} if it is within the iteration range of the iterator. The {\cf ImageBuf::ConstIterator} is identical to the {\cf Iterator}, except that {\cf ConstIterator} may be used on a {\cf const ImageBuf} and may not be used to alter the contents of the \ImageBuf. For simplicity, the remainder of this section will only discuss the {\cf Iterator}. The {\cf Iterator} is templated based on two types: {\cf BUFT} the type of the data stored in the \ImageBuf, and {\cf USERT} type type of the data that you want to manipulate with your code. {\cf USERT} defaults to {\cf float}, since usually you will want to do all your pixel math with {\cf float}. We will thus use {\cf Iterator} synonymously with {\cf Iterator}. For the remainder of this section, we will assume that you have a {\cf float}-based \ImageBuf, for example, if it were set up like this: \begin{code} ImageBuf buf ("myfile.exr"); buf.read (0, 0, true, TypeDesc::FLOAT); \end{code} \apiitem{Iterator (ImageBuf \&buf, WrapMode wrap=WrapDefault)} Initialize an iterator that will visit every pixel in the data window of {\cf buf}, and start it out pointing to the upper left corner of the data window. The {\cf wrap} describes what values will be retrieved if the iterator is positioned outside the data window of the buffer. \apiend \apiitem{Iterator (ImageBuf \&buf, const ROI \&roi, WrapMode wrap=WrapDefault)} Initialize an iterator that will visit every pixel of {\cf buf} within the region described by {\cf roi}, and start it out pointing to pixel ({\cf roi.xbegin, roi.ybegin, roi.zbegin}). The {\cf wrap} describes what values will be retrieved if the iterator is positioned outside the data window of the buffer. \apiend \apiitem{Iterator (ImageBuf \&buf, int x, int y, int z, WrapMode wrap=WrapDefault)} Initialize an iterator that will visit every pixel in the data window of {\cf buf}, and start it out pointing to pixel ({\cf x, y, z}). The {\cf wrap} describes what values will be retrieved if the iterator is positioned outside the data window of the buffer. \apiend \apiitem{Iterator::operator++ ()} The {\cf ++} operator advances the iterator to the next pixel in its iteration range. (Both prefix and postfix increment operator are supported.) \apiend \apiitem{bool Iterator::done () const} Returns {\cf true} if the iterator has completed its visit of all pixels in its iteration range. \apiend \apiitem{ROI Iterator::range () const} Returns the iteration range of the iterator, expressed as an \ROI. \apiend \apiitem{int Iterator::x () const \\ int Iterator::y () const \\ int Iterator::z () const} Returns the $x$, $y$, and $z$ pixel coordinates, respectively, of the pixel that the iterator is currently visiting. \apiend \apiitem{bool Iterator::valid () const} Returns {\cf true} if the iterator's current pixel coordinates are within its iteration range. \apiend \apiitem{bool Iterator::valid (int x, int y, int z=0) const} Returns {\cf true} if pixel coordinate ({\cf x, y, z}) are within the iterator's iteration range (regardless of where the iterator itself is currently pointing). \apiend \apiitem{bool Iterator::exists () const} Returns {\cf true} if the iterator's current pixel coordinates are within the data window of the \ImageBuf. \apiend \apiitem{bool {\ce Iterator}::exists (int x, int y, int z=0) const} Returns {\cf true} if pixel coordinate ({\cf x, y, z}) are within the pixel data window of the \ImageBuf (regardless of where the iterator itself is currently pointing). \apiend \apiitem{USERT\& Iterator::operator[] (int i)} The value of channel {\cf i} of the current pixel. (The wrap mode, set up when the iterator was constructed, determines what value is returned if the iterator points outside the pixel data window of its buffer.) \apiend \apiitem{int Iterator::deep_samples () const} For deep images only, retrieves the number of deep samples for the current pixel. \apiend \apiitem{void Iterator::set_deep_samples ()} For deep images only (and non-const \ImageBuf), set the number of deep samples for the current pixel. This only is useful if the \ImageBuf has not yet had the {\cf deep_alloc()} method called. \apiend \apiitem{USERT Iterator::deep_value (int c, int s) const \\ uint32_t Iterator::deep_value_int (int c, int s) const} For deep images only, returns the value of channel {\cf c}, sample number {\cf s}, at the current pixel. \apiend \apiitem{void Iterator::set_deep_value (int c, int s, float value) \\ void Iterator::set_deep_value (int c, int s, uint32_t value)} For deep images only (and non-cconst \ImageBuf, sets the value of channel {\cf c}, sample number {\cf s}, at the current pixel. This only is useful if the \ImageBuf has already had the {\cf deep_alloc()} method called. \apiend \subsection*{Example: Visiting all pixels to compute an average color} \begin{code} void print_channel_averages (const std::string &filename) { // Set up the ImageBuf and read the file ImageBuf buf (filename); bool ok = buf.read (0, 0, true, TypeDesc::FLOAT); // Force a float buffer if (! ok) return; // Initialize a vector to contain the running total int nc = buf.nchannels(); std::vector total (n, 0.0f); // Iterate over all pixels of the image, summing channels separately for (ImageBuf::ConstIterator it (buf); ! it.done(); ++it) for (int c = 0; c < nc; ++c) total[c] += it[c]; // Print the averages imagesize_t npixels = buf.spec().image_pixels(); for (int c = 0; c < nc; ++c) std::cout << "Channel " << c << " avg = " (total[c] / npixels) << "\n"; } \end{code} \subsection*{Example: Set all pixels in a region to black} \label{makeblackexample} \begin{code} bool make_black (ImageBuf &buf, ROI region) { if (buf.spec().format != TypeDesc::FLOAT) return false; // Assume it's a float buffer // Clamp the region's channel range to the channels in the image roi.chend = std::min (roi.chend, buf.nchannels); // Iterate over all pixels in the region... for (ImageBuf::Iterator it (buf, region); ! it.done(); ++it) { if (! it.exists()) // Make sure the iterator is pointing continue; // to a pixel in the data window for (int c = roi.chbegin; c < roi.chend; ++c) it[c] = 0.0f; // clear the value } return true; } \end{code} \section{Dealing with buffer data types} The previous section on iterators presented examples and discussion based on the assumption that the \ImageBuf was guaranteed to store {\cf float} data and that you wanted all math to also be done as {\cf float} computations. Here we will explain how to deal with buffers and files that contain different data types. \subsection*{Strategy 1: Only have {\cf float} data in your \ImageBuf} \noindent When creating your own buffers, make sure they are {\cf float}: \begin{code} ImageSpec spec (640, 480, 3, TypeDesc::FLOAT); // <-- float buffer ImageBuf buf ("mybuf", spec); \end{code} \noindent When using \ImageCache-backed buffers, force the \ImageCache to convert everything to {\cf float}: \begin{code} // Just do this once, to set up the cache: ImageCache *cache = ImageCache::create (true /* shared cache */); cache->attribute ("forcefloat", 1); ... ImageBuf buf ("myfile.exr"); // Backed by the shared cache \end{code} \noindent Or force the read to convert to {\cf float} in the buffer if it's not a native type that would automatically stored as a {\cf float} internally to the \ImageCache:\footnote{\ImageCache only supports a limited set of types internally, currently only FLOAT and UINT8, and all other data types are converted to these automatically as they are read into the cache.} \begin{code} ImageBuf buf ("myfile.exr"); // Backed by the shared cache buf.read (0, 0, false /* don't force read to local mem */, TypeDesc::FLOAT /* but do force conversion to float*/); \end{code} \noindent Or force a read into local memory unconditionally (rather than relying on the \ImageCache), and convert to {\cf float}: \begin{code} ImageBuf buf ("myfile.exr"); buf.read (0, 0, true /*force read*/, TypeDesc::FLOAT /* force conversion */); \end{code} \subsection*{Strategy 2: Template your iterating functions based on buffer type} Consider the following alternate version of the {\cf make_black} function from Section~\ref{makeblackexample}: \begin{code} template static bool make_black_impl (ImageBuf &buf, ROI region) { // Clamp the region's channel range to the channels in the image roi.chend = std::min (roi.chend, buf.nchannels); // Iterate over all pixels in the region... for (ImageBuf::Iterator it (buf, region); ! it.done(); ++it) { if (! it.exists()) // Make sure the iterator is pointing continue; // to a pixel in the data window for (int c = roi.chbegin; c < roi.chend; ++c) it[c] = 0.0f; // clear the value } return true; } bool make_black (ImageBuf &buf, ROI region) { if (buf.spec().format == TypeDesc::FLOAT) return make_black_impl (buf, region); else if (buf.spec().format == TypeDesc::HALF) return make_black_impl (buf, region); else if (buf.spec().format == TypeDesc::UINT8) return make_black_impl (buf, region); else if (buf.spec().format == TypeDesc::UINT16) return make_black_impl (buf, region); else { buf.error ("Unsupported pixel data format %s", buf.spec().format); retrn false; } } \end{code} In this example, we make an implementation that is templated on the buffer type, and then a wrapper that calls the appropriate template specialization for each of 4 common types (and logs an error in the buffer for any other types it encounters). In fact, {\cf imagebufalgo_util.h} provides a macro to do this (and several variants, which will be discussed in more detail in the next chapter). You could rewrite the example even more simply: \begin{code} #include template static bool make_black_impl (ImageBuf &buf, ROI region) { ... same as before ... } bool make_black (ImageBuf &buf, ROI region) { bool ok; OIIO_DISPATCH_COMMON_TYPES (ok, "make_black", make_black_impl, buf.spec().format, buf, region); return ok; } \end{code} \noindent This other type-dispatching helper macros will be discussed in more detail in Chapter~\ref{chap:imagebufalgo}. \index{ImageBuf|)} \index{Image Buffers|)} \chapwidthend openimageio-1.7.17~dfsg0.orig/src/doc/oiiointro.tex0000644000175000017500000003257613151711064020433 0ustar mfvmfv\chapter{Introduction} \label{chap:oiiointro} Welcome to \product! \bigskip \section{Overview} \product provides simple but powerful \ImageInput and \ImageOutput APIs that abstract the reading and writing of 2D image file formats. They don't support every possible way of encoding images in memory, but for a reasonable and common set of desired functionality, they provide an exceptionally easy way for an application using the APIs support a wide --- and extensible --- selection of image formats without knowing the details of any of these formats. Concrete instances of these APIs, each of which implements the ability to read and/or write a different image file format, are stored as plugins (i.e., dynamic libraries, DLL's, or DSO's) that are loaded at runtime. The \product distribution contains such plugins for several popular formats. Any user may create conforming plugins that implement reading and writing capabilities for other image formats, and any application that uses \product would be able to use those plugins. The library also implements the helper class {\kw ImageBuf}, which is a handy way to store and manipulate images in memory. {\kw ImageBuf} itself uses \ImageInput and \ImageOutput for its file I/O, and therefore is also agnostic as to image file formats. A variety of functions in the {\cf ImageBufAlgo} namespace are available to perform image processing operations on {\cf ImageBuf}'s. The {\kw ImageCache} class transparently manages a cache so that it can access truly vast amounts of image data (thousands of image files totaling hundreds of GB) very efficiently using only a tiny amount (tens of megabytes at most) of runtime memory. Additionally, a {\kw TextureSystem} class provides filtered MIP-map texture lookups, atop the nice caching behavior of {\kw ImageCache}. Finally, the \product distribution contains several utility programs that operate on images, each of which is built atop \ImageInput and \ImageOutput, and therefore may read or write any image file type for which an appropriate plugin is found at runtime. Paramount among these utilities {\cf oiiotool}, a command-line image processing engine, and {\fn iv}, an image viewing application. Additionally, there are programs for converting images among different formats, comparing image data between two images, and examining image metadata. All of this is released as ``open source'' software using the very permissive ``New BSD'' license. So you should feel free to use any or all of \product in your own software, whether it is private or public, open source or proprietary, free or commercial. You may also modify it on your own. You are encouraged to contribute to the continued development of \product and to share any improvements that you make on your own, though you are by no means required to do so. \section{Simplifying Assumptions} \product is not the only image library in the world. Certainly there are many fine libraries that implement a single image format (including the excellent {\fn libtiff}, {\fn libjpeg}, and {\fn OpenEXR} that \product itself relies on). Many libraries attempt to present a uniform API for reading and writing multiple image file formats. Most of these support a fixed set of image formats, though a few of these also attempt to provide an extensible set by using the plugin approach. But in our experience, these libraries are all flawed in one or more ways: (1) They either support only a few formats, or many formats but with the majority of them somehow incomplete or incorrect. (2) Their APIs are not sufficiently expressive as to handle all the image features we need (such as tiled images, which is critical for our texture library). (3) Their APIs are \emph{too complete}, trying to handle every possible permutation of image format features, and as a result are horribly complicated. The third sin is the most severe, and is almost always the main problem at the end of the day. Even among the many open source image libraries that rely on extensible plugins, we have not found one that is both sufficiently flexible and has APIs anywhere near as simple to understand and use as those of \product. Good design is usually a matter of deciding what \emph{not} to do, and \product is no exception. We achieve power and elegance only by making simplifying assumptions. Among them: \begin{itemize} \item \product only deals with ordinary 2D images, and to a limited extent 3D volumes, and image files that contain multiple (but finite) independent images within them. \product's support of ``movie'' files is limited to viewing them as a sequence of separate frames within the file, but not as movies per se (for example, no support for dealing with audio or synchronization). \item Pixel data are presented as 8- 16- or 32-bit int (signed or unsigned), 16- 32- or 64-bit float. NOTHING ELSE. No $<8$ bit images, or pixels boundaries that aren't byte boundaries. Files with $<8$ bits will appear to the client application as 8-bit unsigned grayscale images. \item Only fully elaborated, non-compressed data are accepted and returned by the API. Compression or special encodings are handled entirely within an \product plugin. \item Color space is by default converted to grayscale or RGB. Non-spectral color models, such as XYZ, CMYK, or YUV, are converted to RGB upon reading. (There is a way to override this and ask for raw pixel values.) \item All color channels can be treated (by apps or readers/writers) as having the same data format (though there is a way to deal with per-channel formats for apps and readers/writers that truly need it). \item All image channels in a subimage are sampled at the same resolution. For file formats that allow some channels to be subsampled, they will be automatically up-sampled to the highest resolution channel in the subimage. \item Color information is always in the order R, G, B, and the alpha channel, if any, always follows RGB, and Z channel (if any) always follows alpha. So if a file actually stores ABGR, the plugin is expected to rearrange it as RGBA. \end{itemize} It's important to remember that these restrictions apply to data passed through the APIs, not to the files themselves. It's perfectly fine to have an \product plugin that supports YUV data, or 4 bits per channel, or any other exotic feature. You could even write a movie-reading \ImageInput (despite \product's claims of not supporting movies) and make it look to the client like it's just a series of images within the file. It's just that all the nonconforming details are handled entirely within the \product plugin and are not exposed through the main \product APIs. \subsection*{Historical Origins} \product is the evolution of concepts and tools I've been working on for two decades. In the 1980's, every program I wrote that output images would have a simple, custom format and viewer. I soon graduated to using a standard image file format (TIFF) with my own library implementation. Then I switched to Sam Leffler's stable and complete {\fn libtiff}. In the mid-to-late-1990's, I worked at Pixar as one of the main implementors of PhotoRealistic RenderMan, which had \emph{display drivers} that consisted of an API for opening files and outputting pixels, and a set of DSO/DLL plugins that each implement image output for each of a dozen or so different file format. The plugins all responded to the same API, so the renderer itself did not need to know how to the details of the image file formats, and users could (in theory, but rarely in practice) extend the set of output image formats the renderer could use by writing their own plugins. This was the seed of a good idea, but PRMan's display driver plugin API was abstruse and hard to use. So when I started Exluna in 2000, Matt Pharr, Craig Kolb, and I designed a new API for image output for our own renderer, Entropy. This API, called ``ExDisplay,'' was C++, and much simpler, clearer, and easier to use than PRMan's display drivers. NVIDIA's Gelato (circa 2002), whose early work was done by myself, Dan Wexler, Jonathan Rice, and Eric Enderton, had an API called ``ImageIO.'' ImageIO was \emph{much} more powerful and descriptive than ExDisplay, and had an API for \emph{reading} as well as writing images. Gelato was not only ``format agnostic'' for its image output, but also for its image input (textures, image viewer, and other image utilities). We released the API specification and headers (though not the library implementation) using the BSD open source license, firmly repudiating any notion that the API should be specific to NVIDIA or Gelato. For Gelato 3.0 (circa 2007), we refined ImageIO again (by this time, Philip Nemec was also a major influence, in addition to Dan, Eric, and myself\footnote{Gelato as a whole had many other contributors; those I've named here are the ones I recall contributing to the design or implementation of the ImageIO APIs}). This revision was not a major overhaul but more of a fine tuning. Our ideas were clearly approaching stability. But, alas, the Gelato project was canceled before Gelato 3.0 was released, and despite our prodding, NVIDIA executives would not open source the full ImageIO code and related tools. After I left NVIDIA, I was determined to recreate this work once again -- and ONLY once more -- and release it as open source from the start. Thus, \product was born. I started with the existing Gelato ImageIO specification and headers (which were BSD licensed all along), and made some more refinements since I had to rewrite the entire implementation from scratch anyway. I think the additional changes are all improvements. This is the software you have in your hands today. \subsection*{Acknowledgments} \begin{comment} The direct precursor to \product was Gelato's ImageIO, which was co-designed and implemented by Larry Gritz, Dan Wexler, Jonathan Rice, Eric Enderton, and Philip Nemec. Big thanks to our bosses at NVIDIA for allowing us to share the API spec and headers under the BSD license. And thanks to their inability to open source their own implementation in a timely manner, I was forced to create this clearly superior descendant. \end{comment} \product incorporates, depends upon, or dynamically links against several other open source packages, detailed below. These other packages are all distributed under licenses that allow them to be used by \product. Where not specifically noted, they are all using the same BSD license that \product uses. Any omissions or inaccuracies in this list are inadvertent and will be fixed if pointed out. The full original licenses can be found in the relevant parts of the source code. \smallskip \noindent \product incorporates or distributes: \begin{itemize} \item The SHA-1 implemenation we use is public domain by Dominik Reichl \\ \url{http://www.dominik-reichl.de/} \item Squish \copyright\ 2006 Simon Brown, MIT license. \url{http://sjbrown.co.uk/?code=squish} \item PugiXML \copyright\ 2006-2009 by Arseny Kapoulkine (based on work \copyright\ 2003 Kristen Wegner). \url{http://pugixml.org/} \item DPX reader/writer \copyright\ 2009 Patrick A.\ Palmer. \url{http://code.google.com/p/dpx} \item {\cf tinyformat.h} \copyright\ 2011 Chris Foster, Boost license. \url{http://github.com/c42f/tinyformat} \item {\cf lookup3} code by Bob Jenkins, Public Domain. \url{http://burtleburtle.net/bob/c/lookup3.c} \item {\cf xxhash} \copyright\ 2014 Yann Collet, BSD license. \url{http://code.google.com/p/xxhash/} \item {\cf farmhash} \copyright\ 2014 Google, Inc., MIT license. \url{http://code.google.com/p/farmhash/} \item {\cf KissFFT} \copyright\ 2003--2010 Mark Borgerding. \url{http://sourceforge.net/projects/kissfft} \item Droid fonts from the Android SDK are distributed under the Apache license. \\ \url{http://www.droidfonts.com} \end{itemize} \noindent \product dynamically links against: \begin{itemize} \item {\cf libtiff} \copyright\ 1988-1997 Sam Leffler and 1991-1997 Silicon Graphics, Inc. \\ \url{http://www.remotesensing.org/libtiff} \item {\cf IJG libjpeg} \copyright\ 1991-1998, Thomas G. Lane. \url{http://www.ijg.org} \item OpenEXR, Ilmbase, and Half \copyright\ 2006, Industrial Light \& Magic.\\ \url{http://www.openexr.com} \item {\cf zlib} \copyright\ 1995-2005 Jean-loup Gailly and Mark Adler. \url{http://www.zlib.net} \item {\cf libpng} \copyright\ 1998-2008 Glenn Randers-Pehrson, et al. \url{http://www.libpng.org} \item Boost \copyright\ various authors. \url{http://www.boost.org} \item GLEW \copyright\ 2002-2007 Milan Ikits, et al. \url{http://glew.sourceforge.net} \item Jasper \copyright\ 2001-2006 Michael David Adams, et al. \\ \url{http://www.ece.uvic.ca/~mdadams/jasper/} \item Ptex \copyright\ 2009 Disney Enterprises, Inc. \url{http://ptex.us} \item Field3D \copyright\ 2009 Sony Pictures Imageworks. \url{http://sites.google.com/site/field3d/} \item {\cf GIFLIB} \copyright\ 1997 Eric S. Raymond (MIT Licensed). \url{http://giflib.sourceforge.net/} \item {\cf LibRaw} \copyright\ 2008-2013 LibRaw LLC (LGPL, CDDL, and LibRaw licenses). \\ \url{http://www.libraw.org/} \item {\cf FFmpeg} \copyright\ various authors and distributed under LGPL. \url{https://www.ffmpeg.org} \item {\cf FreeType} \copyright\ 1996-2002, 2006 by David Turner, Robert Wilhelm, and Werner Lemberg. Distributed under the FreeType license (BSD compatible). \item {\cf JPEG-Turbo} \copyright\ 2009--2015 D.\ R.\ Commander. Distributed under the BSD license. \end{itemize} \chapwidthend openimageio-1.7.17~dfsg0.orig/src/doc/openimageio.tex0000644000175000017500000001154613151711064020706 0ustar mfvmfv\documentclass[11pt,letterpaper]{book} \setlength{\oddsidemargin}{0.5in} \setlength{\topmargin}{0in} \setlength{\evensidemargin}{0.3in} \setlength{\textwidth}{5.75in} \setlength{\textheight}{8.5in} %\setlength{\oddsidemargin}{1.25in} %\setlength{\evensidemargin}{0.5in} % don't do this \usepackage{times} % Better fonts than Computer Modern \renewcommand{\sfdefault}{phv} \renewcommand{\rmdefault}{ptm} % don't replace tt -- old is better \renewcommand{\ttdefault}{pcr} %\usepackage{apalike} \usepackage{pslatex} \usepackage{techref} %\usepackage{epsfig} \usepackage{verbatim} \usepackage{moreverb} \usepackage{graphicx} \usepackage{xspace} %\usepackage{multicol} %\usepackage{color} %\usepackage{html} \usepackage{hyperref} \usepackage{array} \usepackage{multirow} \usepackage{longtable} \usepackage{tabularx} %\usepackage{version} \usepackage{makeidx} %\usepackage{showidx} \usepackage[chapter]{algorithm} \floatname{algorithm}{Listing} \usepackage{syntax} \usepackage{fancyhdr} \pagestyle{fancy} \fancyhead[LE,RO]{\bfseries\thepage} \fancyhead[LO]{\bfseries\rightmark} \fancyhead[RE]{\bfseries\leftmark} \fancyfoot[C]{\bfseries OpenImageIO Programmer's Documentation} \renewcommand{\footrulewidth}{1pt} \def\product{{\sffamily OpenImageIO}\xspace} \def\OpenImageIO{{\sffamily OpenImageIO}\xspace} \def\versionnumber{1.7} \def\productver{\product\ {\sffamily \versionnumber}\xspace} \def\producthome{{\codefont \$IMAGEIOHOME}\xspace} \def\ivbinary{{\codefont iv}\xspace} \def\maketx{{\codefont maketx}\xspace} \def\iconvert{{\codefont iconvert}\xspace} \def\idiff{{\codefont idiff}\xspace} \def\iinfo{{\codefont iinfo}\xspace} \def\igrep{{\codefont igrep}\xspace} \def\oiiotool{{\codefont oiiotool}\xspace} \def\ImageSpec{{\codefont ImageSpec}\xspace} \def\ImageInput{{\codefont ImageInput}\xspace} \def\ImageOutput{{\codefont ImageOutput}\xspace} \def\ParamBaseType{{\codefont ParamBaseType}\xspace} \def\TypeDesc{{\codefont TypeDesc}\xspace} \def\ParamValue{{\codefont ParamValue}\xspace} \def\ParamValueList{{\codefont ParamValueList}\xspace} \def\ImageBuf{{\codefont ImageBuf}\xspace} \def\ImageBufAlgo{{\codefont ImageBufAlgo}\xspace} \def\ImageCache{{\codefont ImageCache}\xspace} \def\TextureSystem{{\codefont TextureSystem}\xspace} \def\ROI{{\codefont ROI}\xspace} \def\IBA{{\codefont ImageBufAlgo}\xspace} \def\NULL{{\codefont NULL}\xspace} \def\DeepData{{\codefont DeepData}\xspace} %\def\opencall{{\codefont open()}\xspace} \def\writeimage{{\codefont write\_image()}\xspace} \def\writescanline{{\codefont write\_scanline()}\xspace} \def\writetile{{\codefont write\_tile()}\xspace} \def\readimage{{\codefont read\_image()}\xspace} \def\readscanline{{\codefont read\_scanline()}\xspace} \def\readtile{{\codefont read\_tile()}\xspace} \def\AutoStride{{\codefont AutoStride}\xspace} \def\arrayview{{\codefont array\_view}\xspace} \def\stringview{{\codefont string\_view}\xspace} \def\ustring{{\codefont ustring}\xspace} \title{ {\Huge{\bf \product} %\textregistered\ {\bf\sffamily \versionnumber} \medskip \\ \huge Programmer Documentation % \large (in progress) } \bigskip } \author{Editor: Larry Gritz \\ \emph{lg@openimageio.org} \bigskip \\ } \date{{\large Date: 1 Oct 2016 \\ (with corrections, 30 May 2017) }} \include{macros} \makeindex \begin{document} \frontmatter \maketitle \include{copyr} \vspace*{2in} \begin{centering} \emph{I kinda like ``Oy-e-oh'' with a bit of a groaning Yiddish accent, as in\\ ``OIIO, did you really write yet another file I/O library?''} \\ \end{centering} \medskip \begin{centering} \center Dan Wexler \\ \end{centering} \setcounter{tocdepth}{1} \tableofcontents \mainmatter \include{oiiointro} \part{The OpenImageIO Library APIs} \include{imageioapi} \include{imageoutput} \include{imageinput} \include{writingplugins} \include{builtinplugins} \include{imagecache} \include{texturesys} \include{imagebuf} \include{imagebufalgo} \include{pythonbindings} \part{Image Utilities} \include{oiiotool} \chapter{The {\kw iv} Image Viewer} \label{chap:iv} \indexapi{iv} The {\cf iv} program is a great interactive image viewer. Because {\cf iv} is built on top on \product, it can display images of any formats readable by \ImageInput plugins on hand. \medskip More documentation on this later. \include{iinfo} \include{iconvert} \include{igrep} \include{idiff} \include{maketx} \part{Appendices} \begin{appendix} %\include{typedesc} \chapter{Building OpenImageIO} \include{stdmetadata} %\include{header} \include{glossary} \end{appendix} \backmatter %\bibliographystyle{alpha} %% Select for [GH95] \bibliographystyle{apalike} %% Select for (Gritz and Hahn, 1995) %\addcontentsline{toc}{chapter}{Bibliography} %\bibliography{bmrtbib} \addcontentsline{toc}{chapter}{Index} \printindex \end{document} % Canonical figure \begin{comment} \begin{figure}[ht] \noindent \includegraphics[width=5in]{figures/bredow/foo} \caption{Caption \label{fig:foo}} \end{figure} \end{comment} openimageio-1.7.17~dfsg0.orig/src/doc/copyr.tex0000644000175000017500000000412513151711064017541 0ustar mfvmfv\newpage \label{speccopyr} \vspace*{0.2in} \noindent The OpenImageIO source code and documentation are: \vspace*{0.2in} \noindent Copyright (c) 2008-2016 Larry Gritz, et al. All Rights Reserved. \vspace{0.5in} The code that implements OpenImageIO is licensed under the BSD 3-clause (also sometimes known as ``new BSD'' or ``modified BSD'') license: \vspace{0.25in} Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \begin{itemize} \item Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \item Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \item Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. \end{itemize} THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \vspace{0.5in} This manual and other text documentation about OpenImageIO are licensed under the Creative Commons Attribution 3.0 Unported License. \\ \smallskip \spc \includegraphics[width=0.85in]{figures/CC-30BY.png} \spc http://creativecommons.org/licenses/by/3.0/ \bigskip openimageio-1.7.17~dfsg0.orig/src/doc/pythonbindings.tex0000644000175000017500000032534113151711064021452 0ustar mfvmfv\chapter{Python Bindings} \label{chap:pythonbindings} \indexapi{Python|()} \section{Overview} \OpenImageIO provides Python language bindings for much of its functionality. \smallskip You must ensure that the environment variable {\cf PYTHONPATH} includes the {\cf python} subdirectory of the \OpenImageIO installation. \smallskip A Python program must import the {\cf OpenImageIO} package: \begin{code} import OpenImageIO \end{code} \noindent In most of our examples below, we assume that for the sake of brevity, we will alias the package name as follows: \begin{code} import OpenImageIO as oiio from OpenImageIO import ImageInput, ImageOutput from OpenImageIO import ImageBuf, ImageSpec, ImageBufAlgo \end{code} \section{TypeDesc} \label{sec:pythontypedesc} The \TypeDesc class that describes data types of pixels and metadata, described in detail in Section~\ref{sec:TypeDesc}, is replicated for Python. \apiitem{BASETYPE} The {\cf BASETYPE} enum corresponds to the C++ {\cf TypeDesc::BASETYPE} and contains the following values: \\ {\cf UNKNOWN NONE UINT8 INT8 UINT16 INT16 UINT32 INT32 UINT64 INT64 \\ HALF FLOAT DOUBLE STRING PTR} \\ These names are also exported to the {\cf OpenImageIO} namespace. \apiend \apiitem{AGGREGATE} The {\cf AGGREGATE} enum corresponds to the C++ {\cf TypeDesc::AGGREGATE} and contains the following values: \\ {\cf SCALAR VEC2 VEC3 VEC4 MATRIX33 MATRIX44} \\ These names are also exported to the {\cf OpenImageIO} namespace. \apiend \apiitem{VECSEMANTICS} The {\cf VECSEMANTICS} enum corresponds to the C++ {\cf TypeDesc::VECSEMANTICS} and contains the following values: \\ {\cf NOSEMANTICS COLOR POINT VECTOR NORMAL TIMECODE KEYCODE} \\ These names are also exported to the {\cf OpenImageIO} namespace. \apiend \apiitem{TypeDesc () \\ TypeDesc (basetype) \\ TypeDesc (basetype, aggregate) \\ TypeDesc (basetype, aggregate, vecsemantics) \\ TypeDesc (basetype, aggregate, vecsemantics, arraylen) \\ TypeDesc (str)} Construct a {\cf TypeDesc} object. \noindent Examples: \begin{code} import OpenImageIO as oiio # make a default (UNKNOWN) TypeDesc t = TypeDesc() # make a TypeDesc describing an unsigned 8 bit int t = TypeDesc(oiio.UINT8) # make a TypeDesc describing an array of 14 'half' values t = TypeDesc(oiio.HALF, oiio.SCALAR, oiio.NOSEMANTICS, 14) # make a TypeDesc describing a float point t = TypeDesc(oiio.FLOAT, oiio.VEC3, oiio.POINT) # Some constructors from a string description t = TypeDesc("uint8") t = TypeDesc("half[14]") t = TypeDesc("point") # equiv to FLOAT, VEC3, POINT \end{code} \apiend \apiitem{TypeDesc.TypeFloat() \\ TypeDesc.TypeInt() \\ TypeDesc.TypeString() \\ TypeDesc.TypeHalf() \\ TypeDesc.TypeColor() \\ TypeDesc.TypePoint() \\ TypeDesc.TypeVector() \\ TypeDesc.TypeNormal() \\ TypeDesc.TypeMatrix() \\ TypeDesc.TypeMatrix33() \\ TypeDesc.TypeTimeCode() \\ TypeDesc.TypeKeyCode() \\ TypeDesc.TypeFloat4()} Pre-constructed \TypeDesc objects for some common types. \noindent Example: \begin{code} t = TypeDesc.TypeFloat() \end{code} \apiend \apiitem{string {\ce str} (TypeDesc)} Returns a string that describes the \TypeDesc. \noindent Example: \begin{code} print str(TypeDesc(oiio.UINT16)) > int16 \end{code} \apiend \apiitem{TypeDesc.{\ce basetype} \\ TypeDesc.{\ce aggregate} \\ TypeDesc.{\ce vecsemantics} \\ TypeDesc.{\ce arraylen}} Access to the raw fields in the \TypeDesc. \noindent Example: \begin{code} t = TypeDesc(...) if t.basetype == oiio.FLOAT : print "It's made of floats" \end{code} \apiend \apiitem{int TypeDesc.{\ce size} () \\ int TypeDesc.{\ce basesize} () \\ TypeDesc TypeDesc.{\ce elementtype} () \\ int TypeDesc.{\ce numelements} () \\ int TypeDesc.{\ce elementsize} ()} The {\cf size()} is the size in bytes, of the type described. The {\cf basesize()} is the size in bytes of the {\cf basetype}. The {\cf elementtype()} is the type of each array element, if it is an array, or just the full type if it is not an array. The {\cf elementsize()} is the size, in bytes, of the {\cf elementtype} (thus, returning the same value as {\cf size()} if the type is not an array). The {\cf numelements()} method returns {\cf arraylen} if it is an array, or {\cf 1} if it is not an array. \noindent Example: \begin{code} t = TypeDesc("point[2]") print "size =", t.size() print "elementtype =", t.elementtype() print "elementsize =", t.elementsize() > size = 24 > elementtype = point > elementsize = 12 \end{code} \apiend \apiitem{bool typedesc {\ce ==} typedesc \\ bool typedesc {\ce !=} typedesc \\ bool TypeDesc.{\ce equivalent} (typedesc) \\} Test for equality or inequality. The {\cf equivalent()} method is more forgiving than {\cf ==}, in that it considers {\cf POINT}, {\cf VECTOR}, and {\cf NORMAL} vector semantics to not constitute a difference from one another. \noindent Example: \begin{code} f = TypeDesc("float") p = TypeDesc("point") v = TypeDesc("vector") print "float==point?", (f == p) print "vector==point?", (v == p) print "float.equivalent(point)?", f.equivalent(p) print "vector.equivalent(point)?", v.equivalent(p) > float==point? False > vector==point? False > float.equivalent(point)? False > vector.equivalent(point)? True \end{code} \apiend \section{ROI} \label{sec:pythonroi} The \ROI class that describes an image extent or region of interest, explained in deail in Section~\ref{sec:ROI}, is replicated for Python. \apiitem{{\ce ROI} () \\ {\ce ROI} (xbegin, xend, ybegin, yend) \\ {\ce ROI} (xbegin, xend, ybegin, yend, zbegin, zend) \\ {\ce ROI} (xbegin, xend, ybegin, yend, zbegin, zend, chbegin, chend)} Construct an \ROI with the given bounds. The constructor with no arguments makes an \ROI that is ``undefined.'' \noindent Example: \begin{code} import OpenImageIO as oiio ... roi = ROI (0, 640, 0, 480, 0, 1, 0, 4) # video res RGBA \end{code} \apiend \apiitem{int ROI.{\ce xbegin} \\ int ROI.{\ce xend} \\ int ROI.{\ce ybegin} \\ int ROI.{\ce yend} \\ int ROI.{\ce zbegin} \\ int ROI.{\ce zend} \\ int ROI.{\ce chbegin} \\ int ROI.{\ce chend}} The basic fields of the \ROI. \apiend \apiitem{ROI ROI.{\ce All}} A pre-constructed undefined \ROI. \apiend \apiitem{bool ROI.{\ce defined}} {\cf True} if the \ROI is defined, {\cf False} if the \ROI is undefined. \apiend \apiitem{int ROI.{\ce width} \\ int ROI.{\ce height} \\ int ROI.{\ce depth} \\ int ROI.{\ce nchannels}} The number of pixels in each dimension, and the number of channels, as described by the \ROI. \apiend \apiitem{int ROI.{\ce npixels}} The total number of pixels in the region described by the \ROI. \apiend \apiitem{ROI {\ce get_roi} (imagespec) \\ ROI {\ce get_roi_full} (imagespec)} Returns the \ROI corresponding to the pixel data window of the given \ImageSpec, or the display/full window, respectively. \noindent Example: \begin{code} spec = ImageSpec(...) roi = oiio.get_roi(spec) \end{code} \apiend \apiitem{{\ce set_roi} (imagespec, roi) \\ {\ce set_roi_full} (imagespec, roi)} Alter the \ImageSpec's resolution and offset to match the passed \ROI. \noindent Example: \begin{code} # spec is an ImageSpec # The following sets the full (display) window to be equal to the # pixel data window: oiio.set_roi_full (spec, oiio.get_roi(spec)) \end{code} \apiend \section{ImageSpec} \label{sec:pythonimagespec} The \ImageSpec class that describes an image, explained in deail in Section~\ref{sec:ImageSpec}, is replicated for Python. \apiitem{{\ce ImageSpec} ()\\ {\ce ImageSpec} (basetype) \\ {\ce ImageSpec} (typedesc) \\ {\ce ImageSpec} (xres, yres, nchannels, basetype) \\ {\ce ImageSpec} (xres, yres, nchannels, typespec)} Constructors of an \ImageSpec. These correspond directly to the constructors in the C++ bindings. \noindent Example: \begin{code} import OpenImageIO as oiio ... # default ctr s = ImageSpec() # construct with known pixel type, unknown resolution s = ImageSpec(oiio.UINT8) # construct with known resolution, channels, pixel data type s = ImageSpec(640, 480, 4, oiio.HALF) \end{code} \apiend \apiitem{ImageSpec.{\ce width}, ImageSpec.{\ce height}, ImageSpec.{\ce depth} \\ ImageSpec.{\ce x}, ImageSpec.{\ce y}, ImageSpec.{\ce z}} Resolution and offset of the image data ({\cf int} values). \noindent Example: \begin{code} s = ImageSpec (...) print "Data window is ({},{})-({},{})".format (s.x, s.x+s.width-1, s.y, s.y+s.height-1) \end{code} \apiend \apiitem{ImageSpec.{\ce full_width}, ImageSpec.{\ce full_height}, ImageSpec.{\ce full_depth} \\ ImageSpec.{\ce full_x}, ImageSpec.{\ce full_y}, ImageSpec.{\ce full_z}} Resolution and offset of the ``full'' display window ({\cf int} values). \apiend \apiitem{ImageSpec.{\ce tile_width}, ImageSpec.{\ce tile_height}, ImageSpec.{\ce tile_depth}} For tiled images, the resolution of the tiles ({\cf int} values). Will be {\cf 0} for untiled images. \apiend \apiitem{typedesc ImageSpec.{\ce format}} A \TypeDesc describing the pixel data. \apiend \apiitem{int ImageSpec.{\ce nchannels}} An {\cf int} giving the number of color channels in the image. \apiend \apiitem{ImageSpec.{\ce channelnames}} A tuple of strings containing the names of each color channel. \apiend \apiitem{ImageSpec.{\ce channelformats}} If all color channels have the same format, that will be {\cf ImageSpec.format}, and {\cf channelformats} will be {\cf None}. However, if there are different formats per channel, they will be stored in {\cf channelformats} as a tuple of BASETYPE values, and {\cf format} will contain the ``widest'' of them. \noindent Example: \begin{code} if spec.channelformats == None: print "All color channels are", str(spec.format) else: print "Channel formats: " for i in range(len(spec.channelformats)): print "\t", str(TypeDesc(spec.channelformats[i])) \end{code} \apiend \apiitem{ImageSpec.{\ce alpha_channel} \\ ImageSpec.{\ce z_channel}} The channel index containing the alpha or depth channel, respectively, or -1 if either one does not exist or cannot be identified. \apiend \apiitem{ImageSpec.{\ce deep}} Hold {\cf True} if the image is a \emph{deep} (multiple samples per pixel) image, of {\cf False} if it is an ordinary image. \apiend \apiitem{ImageSpec.{\ce set_format} (basetype) \\ ImageSpec.{\ce set_format} (typedesc)} Given a {\cf BASETYPE} or a \TypeDesc, sets the {\cf format} field and clear any per-channel formats in {\cf channelformats}. \noindent Example: \begin{code} s = ImageSpec () s.set_format (oiio.UINT8) \end{code} \apiend \apiitem{ImageSpec.{\ce default_channel_names} ()} Sets {\cf channel_names} to the default names given the value of the {\cf nchannels} field. \apiend \apiitem{ImageSpec.{\ce channel_bytes} () \\ ImageSpec.{\ce channel_bytes} (channel, native=False)} Returns the size of a single channel value, in bytes (as an {\cf int}). (Analogous to the C++ member functions, see Section~\ref{sec:ImageSpecMemberFuncs} for details.) \apiend \apiitem{ImageSpec.{\ce pixel_bytes} () \\ ImageSpec.{\ce pixel_bytes} (native) \\ ImageSpec.{\ce pixel_bytes} (chbegin, chend) \\ ImageSpec.{\ce pixel_bytes} (chbegin, chend, native=False)} Returns the size of a pixel, in bytes (as an {\cf int}). (Analogous to the C++ member functions, see Section~\ref{sec:ImageSpecMemberFuncs} for details.) \apiend \apiitem{ImageSpec.{\ce scanline_bytes} (native=False) \\ ImageSpec.{\ce tile_bytes} (native=False) \\ ImageSpec.{\ce image_bytes} (native=False)} Returns the size of a scanline, tile, or the full image, in bytes (as an {\cf int}). (Analogous to the C++ member functions, see Section~\ref{sec:ImageSpecMemberFuncs} for details.) \apiend \apiitem{ImageSpec.{\ce tile_pixels} () \\ ImageSpec.{\ce image_pixels} ()} Returns the number of pixels in a tile or the full image, respectively (as an {\cf int}). (Analogous to the C++ member functions, see Section~\ref{sec:ImageSpecMemberFuncs} for details.) \apiend \apiitem{ImageSpec.{\ce erase_attribute} (name, searchtype=TypeDesc(TypeDesc.UNKNOWN),\\ \bigspc\bigspc\spc casesensitive=False)} Remove the specified attribute from the list of extra_attribs. If not found, do nothing. \apiend \apiitem{ImageSpec.{\ce attribute} (name, int) \\ ImageSpec.{\ce attribute} (name, float) \\ ImageSpec.{\ce attribute} (name, string) \\ ImageSpec.{\ce attribute} (name, typedesc, data) \\} Sets a metadata value in the {\cf extra_attribs}. If the metadata item is a single {\cf int}, {\cf float}, or {\cf string}, you can pass it directly. For other types, you must pass the \TypeDesc and then the data (for aggregate types or arrays, pass multiple values as a tuple). \noindent Example: \begin{code} s = ImageSpec (...) s.attribute ("foo_str", "blah") s.attribute ("foo_int", 14) s.attribute ("foo_float", 3.14) s.attribute ("foo_vector", TypeDesc.TypeVector, (1, 0, 11)) s.attribute ("foo_matrix", TypeDesc.TypeMatrix, (1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 1, 2, 3, 1)) \end{code} \apiend \apiitem{ImageSpec.{\ce getattribute} (name) \\ ImageSpec.{\ce getattribute} (name, typedesc)} Retrieves a named metadata value from {\cf extra_attribs}. The generic {\cf getattribute()} function returns it regardless of type, or {\cf None} if the attribute does not exist. The typed variety will only succeed if the attribute is actually of that type specified. \noindent Example: \begin{code} foo = s.getattribute ("foo") # None if not found foo = s.getattribute ("foo", oiio.FLOAT) # None if not found AND float \end{code} \apiend \apiitem{ImageSpec.{\ce get_int_attribute} (name, defaultval=0) \\ ImageSpec.{\ce get_float_attribute} (name, defaultval=0.0) \\ ImageSpec.{\ce get_string_attribute} (name, defaultval="")} Retrieves a named metadata value from {\cf extra_attribs}, if it is found and is of the given type; returns the default value (or a passed value) if not found. \noindent Example: \begin{code} # If "foo" is not found, or if it's not an int, return 0 foo = s.get_int_attribute ("foo") # If "foo" is not found, or if it's not a string, return "blah" foo = s.get_string_attribute ("foo", "blah") \end{code} \apiend \apiitem{ImageSpec.{\ce extra_attribs}} Direct access to the {\cf extra_attribs} named metadata, appropriate for iterating over the entire list rather than searching for a particular named value. \vspace{-10pt} \apiitem{len(extra_attribs)} \vspace{10pt} Returns the number of extra attributes. \apiend \vspace{-24pt} \apiitem{extra_attribs[i].name} \vspace{10pt} The name of the indexed attribute. \apiend \vspace{-24pt} \apiitem{extra_attribs[i].type} \vspace{10pt} The type of the indexed attribute, as a \TypeDesc. \apiend \vspace{-24pt} \apiitem{extra_attribs[i].value} \vspace{10pt} The value of the indexed attribute. \apiend \noindent Example: \begin{code} s = ImageSpec(...) ... print "extra_attribs size is", len(s.extra_attribs) for i in range(len(s.extra_attribs)) : print i, s.extra_attribs[i].name, s.extra_attribs[i].type, " :" print "\t", s.extra_attribs[i].value print \end{code} \apiend \newpage \subsection*{Example: Header info} Here is an illustrative example of the use of \ImageSpec, a working Python function that opens a file and prints all the relevant header information: \begin{tinycode} #!/usr/bin/env python import OpenImageIO as oiio # Print the contents of an ImageSpec def print_imagespec (spec, subimage=0, mip=0) : if spec.depth <= 1 : print (" resolution %dx%d%+d%+d" % (spec.width, spec.height, spec.x, spec.y)) else : print (" resolution %dx%d%x%d+d%+d%+d" % (spec.width, spec.height, spec.depth, spec.x, spec.y, spec.z)) if (spec.width != spec.full_width or spec.height != spec.full_height or spec.depth != spec.full_depth) : if spec.full_depth <= 1 : print (" full res %dx%d%+d%+d" % (spec.full_width, spec.full_height, spec.full_x, spec.full_y)) else : print (" full res %dx%d%x%d+d%+d%+d" % (spec.full_width, spec.full_height, spec.full_depth, spec.full_x, spec.full_y, spec.full_z)) if spec.tile_width : print (" tile size %dx%dx%d" % (spec.tile_width, spec.tile_height, spec.tile_depth)) else : print " untiled" if mip >= 1 : return print " " + str(spec.nchannels), "channels:", spec.channelnames print " format = ", str(spec.format) if spec.channelformats : print " channelformats = ", spec.channelformats print " alpha channel = ", spec.alpha_channel print " z channel = ", spec.z_channel print " deep = ", spec.deep for i in range(len(spec.extra_attribs)) : if type(spec.extra_attribs[i].value) == str : print " ", spec.extra_attribs[i].name, "= \"" + spec.extra_attribs[i].value + "\"" else : print " ", spec.extra_attribs[i].name, "=", spec.extra_attribs[i].value def poor_mans_iinfo (filename) : input = ImageInput.open (filename) if not input : print 'Could not open "' + filename + '"' print "\tError: ", oiio.geterror() return print 'Opened "' + filename + '" as a ' + input.format_name() sub = 0 mip = 0 while True : if sub > 0 or mip > 0 : print "Subimage", sub, "MIP level", mip, ":" print_imagespec (input.spec(), mip=mip) mip = mip + 1 if input.seek_subimage (sub, mip) : continue # proceed to next MIP level else : sub = sub + 1 mip = 0 if input.seek_subimage (sub, mip) : continue # proceed to next subimage break # no more MIP levels or subimages input.close () \end{tinycode} \section{DeepData} \label{sec:pythondeepdata} The \DeepData class describing ``deep'' image data (multiple depth sample per pixel), which is explained in deail in Section~\ref{sec:imageinput:deepdata}, is replicated for Python. \apiitem{{\ce DeepData} ()} Constructs a \DeepData object. It needs to have its {\cf init()} and {\cf alloc()} methods called before it can hold any meaningful data. \apiend \apiitem{DeepData.{\ce init} (npixels, nchannels, channeltypes, channelnames)} Initializes this \DeepData to hold {\cf npixels} total pixels, with {\cf nchannels} color channels. The data types of the channels are described by {\cf channeltypes}, a tuple of \TypeDesc values (one per channel), and the names are provided in a tuple of {\cf string}s {\cf channelnames}. After calling {\cf init}, you still need to set the number of samples for each pixel (using {\cf set_nsamples}) and then call {\cf alloc()} to actually allocate the sample memory. \apiend \apiitem{DeepData.{\ce pixels}} This {\cf int} field constains the total number of pixels in this collection of deep data. \apiend \apiitem{DeepData.{\ce channels}} This {\cf int} field constains the number of channels. \apiend \apiitem{string DeepData.{\ce channelname} (c)} \NEW % 1.7 Retrieve the name of channel {\cf c}. \apiend \apiitem{TypeDesc DeepData.{\ce channeltype} (c)} Retrieve the data type of channel {\cf c}. \apiend \apiitem{int DeepData.{\ce channelsize} (c)} Retrieve the size (in bytes) of one datum of channel {\cf c}. \apiend \apiitem{int DeepData.{\ce samplesize} ()} Retrieve the packed size (in bytes) of all channels of one sample. \apiend \apiitem{DeepData.{\ce set_samples} (pixel, nsamples) \\ int DeepData.{\ce samples} (pixel)} Set or get the number of samples for a given pixel (specified by integer index). \apiend \apiitem{DeepData.{\ce insert_samples} (pixel, samplepos, n) \\ int DeepData.{\ce erase_samples} (pixel, samplepos, n)} \NEW % 1.7 Insert or erase \emph{n} samples starting at the given position of an indexed pixel. \apiend \apiitem{DeepData.{\ce set_deep_value} (pixel, channel, sample, value) \\ DeepData.{\ce set_deep_value_uint} (pixel, channel, sample, value)} Set specific float or unsigned int value of a given pixel, channel, and sample index. \apiend \apiitem{DeepData.{\ce deep_value} (pixel, channel, sample, value) \\ int DeepData.{\ce deep_value_uint} (pixel, channel, sample)} Retrieve the specific value of a given pixel, channel, and sample index (for float or uint channels, respectively). \apiend \apiitem{DeepData.{\ce copy_deep_sample} (pixel, sample, src, srcpixel, srcsample)} \NEW % 1.7 Copy a deep sample from \DeepData {\cf src} into this \DeepData. \apiend \apiitem{DeepData.{\ce copy_deep_pixel} (pixel, src, srcpixel)} \NEW % 1.7 Copy a deep pixel from \DeepData {\cf src} into this \DeepData. \apiend \apiitem{DeepData.{\ce split} (pixel, depth)} \NEW % 1.7 Split any samples of the pixel that cross {\cf depth}. \apiend \apiitem{DeepData.{\ce sort} (pixel)} \NEW % 1.7 Sort the samples of the pixel by their Z depth. \apiend \apiitem{DeepData.{\ce merge_overlaps} (pixel)} \NEW % 1.7 Merge any adjacent samples in the pixel that exactly overlap in $z$ range. This is only useful if the pixel has previously been split at all sample starts and ends, and sorted by depth. \apiend \apiitem{DeepData.{\ce merge_deep_pixels} (pixel, src, srcpixel)} \NEW % 1.7 Merge the samples of {\cf src}'s pixel into this \DeepData's pixel. \apiend \apiitem{DeepData.{\ce occlusion_cull} (pixel)} \NEW % 1.7 Eliminate any samples beyond an opaque sample. \apiend \section{ImageInput} \label{sec:pythonimageinput} See Chapter~\ref{chap:imageinput} for detailed explanations of the C++ \ImageInput class APIs. The Python APIs are very similar. The biggest difference is that in C++, the various {\cf read_*} functions write the pixel values into an already-allocated array that belongs to the caller, whereas the Python versions allocate and return an array holding the pixel values (or {\cf None} if the read failed). \apiitem{ImageInput.{\ce open} (filename) \\ ImageInput.{\ce open} (filename, config_imagespec)} Creates an \ImageInput object and opens the named file. Returns the open \ImageInput upon success, or {\cf None} if it failed to open the file (after which, {\cf OpenImageIO.geterror()} will contain an error message). In the second form, the optional \ImageSpec argument {\cf config} contains attributes that may set certain options when opening the file. \noindent Example: \begin{code} input = ImageInput.open ("tahoe.jpg") if input == None : print "Error:", oiio.geterror() return \end{code} \apiend \apiitem{bool ImageInput.{\ce close} ()} Closes an open image file, returning {\cf True} if successful, {\cf False} otherwise. \noindent Example: \begin{code} input = ImageInput.open (filename) ... input.close () \end{code} \apiend \apiitem{str ImageInput.{\ce format_name} ()} Returns the format name of the open file. \noindent Example: \begin{code} input = ImageInput.open (filename) if input : print filename, "was a", input.format_name(), "file." input.close () \end{code} \apiend \apiitem{ImageSpec ImageInput.{\ce spec} ()} Returns the \ImageSpec corresponding to the currently open subimage and MIP level of the file. \noindent Example: \begin{code} input = ImageInput.open (filename) spec = input.spec() print "resolution ", spec.width, "x", spec.height \end{code} \apiend \apiitem{int ImageInput.{\ce current_subimage} () \\ int ImageInput.{\ce current_miplevel} ()} Returns the current subimage and/or MIP level of the file. \apiend \apiitem{bool ImageInput.{\ce seek_subimage} (subimage, miplevel)} Repositions the file pointer to the given subimage and MIP level within the file (starting with {\cf 0}). This function returns {\cf True} upon success, {\cf False} upon failure (which may include the file not having the specified subimage or MIP level). \noindent Example: \begin{code} input = ImageInput.open (filename) mip = 0 while True : ok = input.seek_subimage (0, mip) if not ok : break spec = input.spec() print "MIP level", mip, "is", spec.width, "x", spec.height \end{code} \apiend \apiitem{array ImageInput.{\ce read_image} (type=OpenImageIO.FLOAT) \\ array ImageInput.{\ce read_image} (chbegin, chend, type=OpenImageIO.FLOAT)} Read the entire image and return the pixels as an array of $\mathit{width} \times \mathit{height} \times \mathit{depth} \times \mathit{nchannels}$ values (or {\cf None} if an error occurred). The array will be of the type specified by the {\cf TypeDesc} or {\cf BASETYPE} argument (by default, {\cf FLOAT}). If {\cf type} is {\cf OpenImageIO.UNKNOWN}, an array of {\cf unsigned char} will be returned containing the raw data in its native format. The call may optionally specify a subset of channels. \noindent Example: \begin{code} input = ImageInput.open (filename) spec = input.spec () pixels = input.read_image (oiio.FLOAT) print "The first pixel is", pixels[0:spec.nchannels] print "The second pixel is", pixels[spec.nchannels:(2*spec.nchannels)] input.close () \end{code} \apiend \apiitem{array ImageInput.{\ce read_scanline} (y, z, type=OpenImageIO.FLOAT)} Read scanline number {\cf y} from depth plane {\cf z} from the open file, returning it as an array of $\mathit{width} \times \mathit{nchannels}$ values (or {\cf None} if an error occurred). The array will be of the type specified by the {\cf TypeDesc} or {\cf BASETYPE} argument (by default, {\cf FLOAT}). If {\cf type} is {\cf OpenImageIO.UNKNOWN}, an array of {\cf unsigned char} will be returned containing the raw data in its native format. \noindent Example: \begin{code} input = ImageInput.open (filename) spec = input.spec () if spec.tile_width == 0 : for y in range(spec.y, spec.y+spec.height) : pixels = input.read_scanline (y, spec.z, oiio.FLOAT) # process the scanline else : print "It's a tiled file" input.close () \end{code} \apiend \apiitem{array ImageInput.{\ce read_tile} (x, y, z, type=OpenImageIO.FLOAT)} Read the tile whose upper left corner is pixel {\cf (x,y,z)} from the open file, returning it as an array of $\mathit{width} \times \mathit{height} \times \mathit{depth} \times \mathit{nchannels}$ values (or {\cf None} if an error occurred). The array will be of the type specified by the {\cf TypeDesc} or {\cf BASETYPE} argument (by default, {\cf FLOAT}). If {\cf type} is {\cf OpenImageIO.UNKNOWN}, an array of {\cf unsigned char} will be returned containing the raw data in its native format. \noindent Example: \begin{code} input = ImageInput.open (filename) spec = input.spec () if spec.tile_width > 0 : for z in range(spec.z, spec.z+spec.depth, spec.tile_depth) : for y in range(spec.y, spec.y+spec.height, spec.tile_height) : for x in range(spec.x, spec.x+spec.width, spec.tile_width) : pixels = input.read_tile (x, y, z, oiio.FLOAT) # process the tile else : print "It's a scanline file" input.close () \end{code} \apiend \apiitem{array ImageInput.{\ce read_scanlines} (ybegin, yend, z, chbegin, chend, \\ \bigspc\bigspc\spc type=OpenImageIO.FLOAT) \\ array ImageInput.{\ce read_tiles} (xbegin, xend, ybegin, yend, zbegin, zend, \\ \bigspc\bigspc\spc chbegin, chend, type=OpenImageIO.FLOAT)} Similar to the C++ routines, these functions read multiple scanlines or tiles at once, which in some cases may be more efficient than reading each scanline or tile separately. Additionally, they allow you to read only a subset of channels. \noindent Example: \begin{code} input = ImageInput.open (filename) spec = input.spec () # Read the whole image, the equivalent of # pixels = input.read_image (type) # but do it using read_scanlines or read_tiles: if spec.tile_width == 0 : pixels = input.read_scanlines (spec.y, spec.y+spec.height, 0, 0, spec.nchannels, oiio.FLOAT) else : pixels = input.read_tiles (spec.x, spec.x+spec.width, spec.y, spec.y+spec.height, spec.z, spec.z+spec.depth, 0, spec.nchannels, oiio.FLOAT) \end{code} \apiend \apiitem{DeepData ImageInput.{\ce read_native_deep_scanlines} (ybegin, yend, z,\\ \bigspc\bigspc chbegin, chend) \\ DeepData ImageInput.{\ce read_native_deep_tiles} (xbegin, xend, ybegin, yend,\\ \bigspc\bigspc zbegin, zend, chbegin, chend) \\ DeepData ImageInput.{\ce read_native_deep_image} (chbegin, chend)} Read a collection of scanlines, tiles, or an entire image of ``deep'' pixel data. The begin/end coordinates are all integer values. The value returned will be a \DeepData if the read succeeds, or {\cf None} if the read fails. \apiend \apiitem{str ImageInput.{\ce geterror} ()} Retrieves the error message from the latest failed operation on an ImageInput. \noindent Example: \begin{code} input = ImageInput.open (filename) if not input : print "Open error:", oiio.geterror() # N.B. error on open must be retrieved with the global geterror(), # since there is no ImageInput object! else : pixels = input.read_image (oiio.FLOAT) if not pixels : print "Read_image error:", input.geterror() input.close () \end{code} \apiend \newpage \subsection*{Example: Reading pixel values from a file to find min/max} \begin{code} #!/usr/bin/env python import OpenImageIO as oiio def find_min_max (filename) : input = ImageInput.open (filename) if not input : print 'Could not open "' + filename + '"' print "\tError: ", oiio.geterror() return spec = input.spec() nchans = spec.nchannels pixels = input.read_image(oiio.FLOAT) if not pixels : print "Could not read:", input.geterror() return input.close() # we're done with the file at this point minval = pixels[0:nchans] # initialize to the first pixel value maxval = pixels[0:nchans] i = 0 # absolute index for z in range(spec.depth) : for y in range(spec.height) : for x in range(spec.width) : for c in range(nchans) : if pixels[i+c] < minval[c] : minval[c] = pixels[i+c] if pixels[i+c] > maxval[c] : maxval[c] = pixels[i+c] i = i + nchans # advance the index print "Min values per channel were", minval print "Max values per channel were", maxval \end{code} \newpage \section{ImageOutput} \label{sec:pythonimageoutput} See Chapter~\ref{chap:imageoutput} for detailed explanations of the C++ \ImageOutput class APIs. The Python APIs are very similar. \apiitem{ImageOutput ImageOutput.{\ce create} (fileformat, plugin_searchpath="")} Create a new \ImageOutput capable of writing the named file format (which may also be a file name, with the type deduced from the extension). There is an optional parameter giving an colon-separated search path for finding \ImageOutput plugins. The function returns an \ImageOutput object, or {\cf None} upon error (in which case, {OpenImageIO.geterror()} may be used to retrieve the error message). \noindent Example: \begin{code} import OpenImageIO as oiio output = ImageOutput.create ("myfile.tif") if not output : print "Error:", oiio.geterror() \end{code} \apiend \apiitem{str ImageOutput.{\ce format_name} ()} The file format name of a created \ImageOutput. \noindent Example: \begin{code} output = ImageOutput.create (filename) if output : print "Created output", filename, "as a", output.format_name() \end{code} \apiend \apiitem{int ImageOutput.{\ce supports} (feature)} For a created \ImageOutput, returns {\cf True} if the file format supports the named feature (such as \qkw{tiles}, \qkw{mipmap}, etc., see Section~\ref{sec:supportsfeaturelist} for the full list), or {\cf False} if this file format does not support the feature. \noindent Example: \begin{code} output = ImageOutput.create (filename) if output : print output.format_name(), "supports..." print "tiles?", output.supports("tiles") print "multi-image?", output.supports("multiimage") print "MIP maps?", output.supports("mipmap") print "per-channel formats?", output.supports("channelformats") \end{code} \apiend \apiitem{bool ImageOutput.{\ce open} (filename, imagespec, mode)} Opens the named output file, with an \ImageSpec describing the image to be output. The {\cf mode} may be one of {\cf OpenImageIO.Create}, {\cf OpenImageIO.AppendSubimage}, or {\cf OpenImageIO.AppendMIPLevel}. See Section~\ref{sec:imageoutputopen} for details. Returns {\cf True} upon success, {\cf False} upon failure (error messages retrieved via {\cf ImageOutput.geterror()}.) \noindent Example: \begin{code} output = ImageOutput.create (filename) if not output : print "Error:", oiio.geterror() spec = ImageSpec (640, 480, 3, oiio.UINT8) ok = output.open (filename, spec, oiio.Create) if not ok : print "Could not open", filename, ":", output.geterror() \end{code} \apiend \apiitem{bool ImageOutput.{\ce open} (filename, (imagespec, ...))} This variety of {\cf open()} is used specifically for multi-subimage files. A \emph{tuple} of \ImageSpec objects is passed, one for each subimage that will be written to the file. After each subimage is written, then a regular call to {\cf open(name, newspec, {\ce AppendSubimage})} moves on to the next subimage. \apiend \apiitem{bool ImageOutput.{\ce close} ()} Closes an open output. \apiend \apiitem{ImageSpec ImageOutput.{\ce spec} ()} Retrieves the \ImageSpec of the currently-open output image. \apiend \apiitem{bool ImageOutput.{\ce write_image} (typedesc, pixels)} Write the currently opened image all at once. The {\cf pixels} parameter should be an {\cf array} containing data elements. The data type is deduced from the contents of the array itself. Returns {\cf True} upon success, {\cf False} upon failure. \noindent Example: \begin{code} # This example reads a scanline file, then converts it to tiled # and writes to the same name. input = ImageInput.open (filename) spec = input.spec () pixels = input.read_image (oiio.FLOAT) input.close () output = ImageOutput.create (filename) if output.supports("tiles") : spec.tile_width = 64 spec.tile_height = 64 output.open (filename, spec, oiio.Create) output.write_image (pixels) output.close () \end{code} \apiend \apiitem{bool ImageOutput.{\ce write_scanline} (y, z, pixels) \\ bool ImageOutput.{\ce write_scanlines} (ybegin, yend, z, pixels)} Write one or many scanlines to the currently open file. \noindent Example: \begin{code} # Copy a TIFF image to JPEG by copying scanline by scanline. input = ImageInput.open ("in.tif") spec = input.spec () output = ImageOutput.create ("out.jpg") output.open (filename, spec, oiio.Create) for z in range(spec.z, spec.z+spec.depth) : for y in range(spec.y, spec.y+spec.height) : pixels = input.read_scanline (y, z, oiio.FLOAT) output.write_scanline (y, z, pixels) output.close () input.close () # The same example, but copying a whole "plane" of scanlines at a time: ... for z in range(spec.z, spec.z+spec.depth) : pixels = input.read_scanlines (spec.y, spec.y+spec.height, z, oiio.FLOAT) output.write_scanlines (spec.y, spec.y+spec.height, z, pixels) ... \end{code} \apiend \apiitem{bool ImageOutput.{\ce write_tile} (x, y, z, pixels) \\ bool ImageOutput.{\ce write_tiles} (xbegin, xend, ybegin, yend, zbegin, zend)} Write one or many tiles to the currently open file. \noindent Example: \begin{code} input = ImageInput.open (in_filename) spec = input.spec () output = ImageOutput.create (out_filename) output.open (out_filename, spec, oiio.Create) for z in range(spec.z, spec.z+spec.depth, spec.tile_depth) : for y in range(spec.y, spec.y+spec.height, spec.tile_height) : for x in range(spec.x, spec.x+spec.width, spec.tile_width) : pixels = input.read_tile (x, y, z, oiio.FLOAT) output.write_tile (x, y, z, pixels) output.close () input.close () # The same example, but copying a whole row of of tiles at a time: ... for z in range(spec.z, spec.z+spec.depth, spec.tile_depth) : for y in range(spec.y, spec.y+spec.height, spec.tile_height) : pixels = input.read_tiles (spec.x, spec.x+spec.width, y, y+tile_width, z, z+tile_width, oiio.FLOAT) output.write_tiles (spec.x, spec.x+spec.width, y, y+tile_width, z, z+tile_width, pixels) ... \end{code} \apiend \apiitem{bool ImageOutput.{\ce write_deep_scanlines} (ybegin, yend, z, deepdata) \\ bool ImageOutput.{\ce write_deep_tiles} (xbegin, xend, ybegin, yend, \\ \bigspc\bigspc\bigspc zbegin, zend, deepdata) \\ bool ImageOutput.{\ce write_deep_image} (deepdata)} Write a collection of scanlines, tiles, or an entire image of ``deep'' pixel data. The begin/end coordinates are all integer values, and {\cf deepdata} should be a \DeepData. \apiend \apiitem{bool ImageOutput.{\ce copy_image} (imageinput)} Copy the current image of the open input to the open output. (The reason this may be preferred in some circumstances is that, if input and output were the same kind of input file format, they may have a special efficient technique to copy pixels unaltered, for example by avoiding the decompression/recompression round trip.) \noindent Example: \begin{code} input = ImageInput.open (in_filename) spec = input.spec () output = ImageOutput.create (out_filename) output.open (filename, spec, oiio.Create) output.copy_image (input) output.close () input.close () \end{code} \apiend \apiitem{str ImageOuput.{\ce geterror} ()} Retrieves the error message from the latest failed operation on an open file. \noindent Example: \begin{code} output = ImageOutput.create (filename) if not output : print "Create error:", oiio.geterror() # N.B. error on create must be retrieved with the global geterror(), # since there is no ImageOutput object! else : ok = output.open (filename, spec, oiio.Create) if not ok : print "Open error:", output.geterror() ok = output.write_image (pixels) if not ok : print "Write error:", output.geterror() output.close () \end{code} \apiend \section{ImageBuf} \label{sec:pythonimagebuf} See Chapter~\ref{chap:imagebuf} for detailed explanations of the C++ \ImageBuf class APIs. The Python APIs are very similar. \apiitem{ImageBuf {\ce ImageBuf} ()} Construct a new, empty \ImageBuf. The \ImageBuf is uninitialized and is awaiting a call to {\cf reset()} or {\cf copy()} before it is useful. \apiend \apiitem{ImageBuf {\ce ImageBuf} (filename) \\ ImageBuf {\ce ImageBuf} (filename, subimage, miplevel)} Construct a read-only \ImageBuf that will read from the named file. Optionally, a specific subimage or MIP level may be specified (defaulting to 0). \noindent Example: \begin{code} import OpenImageIO as oiio ... buf = ImageBuf ("grid.tif") \end{code} \apiend \apiitem{ImageBuf {\ce ImageBuf} (imagespec)} Construct a writeable \ImageBuf of the dimensions and data format specified by an \ImageSpec. \noindent Example: \begin{code} spec = ImageSpec (640, 480, 3, oiio.FLOAT) buf = ImageBuf (spec) \end{code} \apiend \apiitem{ImageBuf.{\ce clear} ()} Return the \ImageBuf to its pristine, uninitialized state. \noindent Example: \begin{code} buf = ImageBuf (...) # The following two commands are equivalent: buf = ImageBuf() # 1 - assign a new blank ImageBuf buf.clear() # 2 - clear the existing ImageBuf \end{code} \apiend \apiitem{ImageBuf.{\ce reset} (filename, subimage=0, miplevel=0, config=ImageSpec())} Restore the \ImageBuf to a newly-constructed state, to read from a filename (optionally specifying a subimage, MIP level, and/or a ``configuration'' \ImageSpec). \apiend \apiitem{ImageBuf.{\ce reset} (imagespec)} Restore the \ImageBuf to the newly-constructed state of a blank, writeable \ImageBuf specified by an \ImageSpec. \apiend \apiitem{bool ImageBuf.{\ce read} (subimage=0, miplevel=0, force=False, convert=oiio.UNKNOWN)} Explicitly read the image from the file (of a file-reading \ImageBuf), optionally specifying a particular subimage and MIP level. If {\cf force} is {\cf True}, will force an allocation of memory and a full read (versus the default of relying on an underlying \ImageCache). If {\cf convert} is not the default of {\cf UNKNOWN}, it will force the \ImageBuf to convert the image to the specified data format (versus keeping it in the native format or relying on the \ImageCache to make a data formatting decision). Note that a call to {\cf read()} is not necessary --- any \ImageBuf API call that accesses pixel values will trigger a file read if it has not yet been done. The {\cf read()} method will return {\cf True} for success, or {\cf False} if the read could not be performed (in which case, a {\cf geterror()} call will retrieve the specific error message). \noindent Example: \begin{code} buf = ImageBuf ("mytexture.exr") buf.read (0, 2, True) # That forces an allocation and read of MIP level 2 \end{code} \apiend \apiitem{bool ImageBuf.{\ce init_spec} (filename, subimage=0, miplevel=0)} Explicitly read just the header from a file-reading \ImageBuf (if the header has not yet been read), optionally specifying a particular subimage and MIP level. The {\cf init_spec()} method will return {\cf True} for success, or {\cf False} if the read could not be performed (in which case, a {\cf geterror()} call will retrieve the specific error message). Note that a call to {\cf init_spec()} is not necessary --- any \ImageBuf API call that accesses the spec will read it automatically it has not yet been done. \apiend \apiitem{bool ImageBuf.{\ce write} (filename, fileformat="")} Write the contents of the \ImageBuf to the named file. Optionally, {\cf fileformat} can specify a particular file format to use (by default, it will infer it from the extension of the file name). \noindent Example: \begin{code} # No-frills conversion of a TIFF file to JPEG buf = ImageBuf ("in.tif") buf.write ("out.jpg") \end{code} \apiend \apiitem{bool ImageBuf.{\ce make_writeable} (keep_cache_type = false)} Force the \ImageBuf to be writeable. That means that if it was previously backed by an \ImageCache (storage was {\cf IMAGECACHE}), it will force a full read so that the whole image is in local memory. \apiend \apiitem{bool ImageBuf.{\ce set_write_format} (format=oiio.UNKNOWN) \\ bool ImageBuf.{\ce set_write_tiles} (width=0, height=0, depth=0)} Override the data format or tile size in a subsequent call to {\cf write()}. \noindent Example: \begin{code} # Conversion to a tiled float file buf = ImageBuf ("in.tif") buf.set_write_format (oiio.FLOAT) buf.set_write_tiles (64, 64) buf.write ("out.tif") \end{code} \apiend \apiitem{ImageSpec ImageBuf.{\ce spec}() \\ ImageSpec ImageBuf.{\ce nativespec}()} {\cf ImageBuf.spec()} returns the \ImageSpec that describes the contents of the \ImageBuf. {\cf ImageBuf.nativespec()} returns an \ImageSpec that describes the contents of the file that the \ImageBuf was read from (this may differ from {\cf ImageBuf.spec()} due to format conversions, or any changes made to the \ImageBuf after the file was read, such as adding metadata). Handy rule of thumb: {\cf spec()} describes the buffer, {\cf nativespec()} describes the original file it came from. \noindent Example: \begin{code} buf = ImageBuf ("in.tif") print "Resolution is", buf.spec().width, "x", buf.spec().height \end{code} \apiend \apiitem{ImageSpec ImageBuf.{\ce specmod}()} {\cf ImageBuf.specmod()} provides writeable access to the \ImageSpec that describes the contents of the \ImageBuf. Be very careful! It is safe to modify certain metadata, but if you change the data format or resolution fields, you will get the chaos you deserve. \noindent Example: \begin{code} # Read an image, add a caption metadata, write it back out in place buf = ImageBuf ("file.tif") buf.specmod().attribute ("ImageDescription", "my photo") buf.write ("file.tif") \end{code} \apiend \apiitem{str ImageBuf.{\ce name} \\ str ImageBuf.{\ce file_format_name}} The file name and name of the file format of the image. \apiend \apiitem{int ImageBuf.{\ce subimage} \\ int ImageBuf.{\ce miplevel} \\ int ImageBuf.{\ce nsubimages} \\ int ImageBuf.{\ce nmiplevels}} Several fields giving information about the current subimage and MIP level, and the total numbers thereof in the file. \apiend \apiitem{int ImageBuf.{\ce xbegin} \\ int ImageBuf.{\ce xend} \\ int ImageBuf.{\ce ybegin} \\ int ImageBuf.{\ce yend} \\ int ImageBuf.{\ce zbegin} \\ int ImageBuf.{\ce zend}} The range of valid pixel data window. Remember that the {\cf end} is \emph{one past} the last pixel. \apiend \apiitem{int ImageBuf.{\ce xmin} \\ int ImageBuf.{\ce xmax} \\ int ImageBuf.{\ce ymin} \\ int ImageBuf.{\ce ymax} \\ int ImageBuf.{\ce zmin} \\ int ImageBuf.{\ce zmax}} The minimum and maximum (inclusive) coordinates of the pixel data window. \apiend \apiitem{int ImageBuf.{\ce orientation} \\ int ImageBuf.{\ce oriented_width} \\ int ImageBuf.{\ce oriented_height} \\ int ImageBuf.{\ce oriented_x} \\ int ImageBuf.{\ce oriented_y} \\ int ImageBuf.{\ce oriented_full_width} \\ int ImageBuf.{\ce oriented_full_height} \\ int ImageBuf.{\ce oriented_full_x} \\ int ImageBuf.{\ce oriented_full_y}} The {\cf orientation} field gives the suggested display oriententation of the image (see Section~\ref{metadata:orientation}). The other fields are helpers that give the width, height, and origin (as well as ``full'' or ``display'' resolution and origin), taking the intended orientation into consideration. \apiend \apiitem{ROI ImageBuf.{\ce roi} \\ ROI ImageBuf.{\ce roi_full}} These fields return an \ROI description of the pixel data window ({\cf roi}) and the full (a.k.a.\ ``display'') window ({\cf roi_full}). \noindent Example: \begin{code} buf = ImageBuf ("tahoe.jpg") print "Resolution is", buf.roi.width, "x", buf.roi.height \end{code} \apiend \apiitem{ImageBuf.{\ce set_full} (roi)} Changes the ``full'' (a.k.a. ``display'') window to the specified ROI. \noindent Example: \begin{code} newroi = ROI (0, 1024, 0, 768) buf.set_full (newroi) \end{code} \apiend \apiitem{bool ImageBuf.{\ce pixels_valid}} Will be {\cf True} if the file has already been read and the pixels are valid. (It is always {\cf True} for writeable \ImageBuf's.) There should be few good reasons to access these, since the spec and pixels will be automatically be read when they are needed. \apiend \apiitem{TypeDesc ImageBuf.{\ce pixeltype}} Returns the description of the data type of the pixels stored within the \ImageBuf. \apiend \apiitem{ImageBuf.{\ce copy_metadata} (other_imagebuf)} Replaces the metadata (all \ImageSpec items, except for the data format and pixel data window size) with the corresponding metadata from the other \ImageBuf. \apiend \apiitem{ImageBuf.{\ce copy_pixels} (other_imagebuf)} Replace the pixels in this \ImageBuf with the values from the other \ImageBuf. \apiend \apiitem{ImageBuf.{\ce copy} (other_imagebuf) \\ ImageBuf.{\ce copy} (other_imagebuf, format)} Make this \ImageBuf a complete copy of the other \ImageBuf. If a {\cf format} is provided, {\cf this} will get the specified pixel data type rather than using the same pixel format as the source \ImageBuf. \noindent Example: \begin{code} A = ImageBuf("A.tif") # Make a separate, duplicate copy of A B = ImageBuf() B.copy (A) # Make another copy of A, but converting to float pixels C = ImageBuf() C.copy (A, OIIO.FLOAT) \end{code} \apiend \apiitem{ImageBuf.{\ce swap} (other_imagebuf)} Swaps the content of this \ImageBuf and the other \ImageBuf. \noindent Example: \begin{code} A = ImageBuf("A.tif") B = ImageBuf("B.tif") A.swap (B) # Now B contains the "A.tif" image and A contains the "B.tif" image \end{code} \apiend \apiitem{tuple ImageBuf.{\ce getpixel} (x, y, z=0, wrap=oiio.WrapBlack)} Retrieves pixel $(x,y,z)$ from the buffer and return it as a tuple of {\cf float} values, one for each color channel. The {\cf x, y, z} values are {\cf int} pixel coordinates. The optional {\cf wrap} parameter describes what should happen if the coordinates are outside the pixel data window (and may be: {\cf WrapBlack, WrapClamp, WrapPeriodic, WrapMirror}). \noindent Example: \begin{code} buf = ImageBuf ("tahoe.jpg") p = buf.getpixel (50, 50) print p > (0.37, 0.615, 0.97) \end{code} \apiend \apiitem{float ImageBuf.{\ce getchannel} (x, y, z, channel, wrap=oiio.WrapBlack)} Retrieves just a single channel value from pixel $(x,y,z)$ from the buffer and returns it as a {\cf float} value. The optional {\cf wrap} parameter describes what should happen if the coordinates are outside the pixel data window (and may be: {\cf WrapBlack, WrapClamp, WrapPeriodic, WrapMirror}). \noindent Example: \begin{code} buf = ImageBuf ("tahoe.jpg") green = buf.getchannel (50, 50, 0, 1) \end{code} \apiend \apiitem{tuple ImageBuf.{\ce interppixel} (x, y, wrap=oiio.WrapBlack) \\ tuple ImageBuf.{\ce interppixel_bicubic} (x, y, wrap=oiio.WrapBlack)} Interpolates the image value (bilinearly or bicubically) at coordinates $(x,y)$ and return it as a tuple of {\cf float} values, one for each color channel. The {\cf x, y} values are continuous {\cf float} coordinates in ``pixel space.'' The optional {\cf wrap} parameter describes what should happen if the coordinates are outside the pixel data window (and may be: {\cf WrapBlack, WrapClamp, WrapPeriodic, WrapMirror}). \noindent Example: \begin{code} buf = ImageBuf ("tahoe.jpg") midx = float(buf.xbegin + buf.xend) / 2.0 midy = float(buf.ybegin + buf.yend) / 2.0 p = buf.interpixel (midx, midy) # Now p is the interpolated value from right in the center of # the data window \end{code} \apiend \apiitem{tuple ImageBuf.{\ce interppixel_NDC} (x, y, wrap=oiio.WrapBlack) \\ tuple ImageBuf.{\ce interppixel_bicubic_NDC} (x, y, wrap=oiio.WrapBlack)} Interpolates the image value (bilinearly or bicubically) at coordinates $(x,y)$ and return it as a tuple of {\cf float} values, one for each color channel. The {\cf x, y} values are continuous, normalized {\cf float} coordinates in ``NDC space,'' where {\cf (0,0)} is the upper left corner of the full (a.k.a.\ ``display'') window, and {\cf (1,1)} is the lower right corner of the full/display window. The {\cf wrap} parameter describes what should happen if the coordinates are outside the pixel data window (and may be: {\cf WrapBlack, WrapClamp, WrapPeriodic, WrapMirror}). \noindent Example: \begin{code} buf = ImageBuf ("tahoe.jpg") p = buf.interpixel_NDC (0.5, 0.5) # Now p is the interpolated value from right in the center of # the display window \end{code} \apiend \apiitem{ImageBuf.{\ce setpixel} (x, y, pixel_value) \\ ImageBuf.{\ce setpixel} (x, y, z, pixel_value)} Sets pixel $(x,y,z)$ to be the {\cf pixel_value}, expressed as a tuple of {\cf float}s (one for each color channel). \noindent Example: \begin{code} buf = ImageBuf (ImageSpec (640, 480, 3, oiio.UINT8)) # Set the whole image to red (the dumb slow way, but it works): for y in range(buf.ybegin, buf.yend) : for x in range(buf.xbegin, buf.xend) : buf.setpixel (x, y, (1.0, 0.0, 0.0)) \end{code} \apiend \apiitem{array ImageBuf.{\ce get_pixels} (format=OpenImageIO.UNKNOWN, roi=ROI.All)} Retrieves the rectangle of pixels (and channels) specified by {\cf roi} from the image and returns them as an array of values with type specified by {\cf format}. \noindent Example: \begin{code} buf = ImageBuf ("tahoe.jpg") pixels = buf.get_pixels (oiio.FLOAT) # no ROI means the whole image \end{code} \apiend \apiitem{ImageBuf.{\ce set_pixels} (roi, data)} Sets the rectangle of pixels (and channels) specified by {\cf roi} with values in the {\cf data}, which may be either a tuple of floats or an array (if it's an array, the type of array --- float, unsigned char, etc. --- will be correctly discerned). \noindent Example: \begin{code} buf = ImageBuf (...) pixels = (....) buf.set_pixels (ROI(), pixels) \end{code} \apiend \apiitem{bool ImageBuf.{\ce has_error} \\ str ImageBuf.{\ce geterror} ()} The {\cf ImageBuf.has_error} field will be {\cf True} if an error has occurred in the \ImageBuf, in which case the {\cf geterror()} method will retrieve the error message (and clear it afterwards). \noindent Example: \begin{code} buf = ImageBuf ("in.tif") buf.read () # force a read if buf.has_error : print "Error reading the file:", buf.geterror() buf.write ("out.jpg") if buf.has_error : print "Could not convert the file:", buf.geterror() \end{code} \apiend \apiitem{int ImageBuf.{\ce pixelindex} (x, y, z, check_range=False)} \NEW %1.7 Return the index of pixel (x,y,z). \apiend \apiitem{bool ImageBuf.{\ce deep}} Will be {\cf True} if the file contains ``deep'' pixel data, or {\cf False} for an ordinary images. \apiend \apiitem{int ImageBuf.{\ce deep_samples} (x, y, z=0)} Return the number of deep samples for pixel (x,y,z). \apiend \apiitem{ImageBuf.{\ce set_deep_samples} (x, y, z, nsamples)} Set the number of deep samples for pixel (x,y,z). \apiend \apiitem{ImageBuf.{\ce deep_insert_samples} (x, y, z, samplepos, nsamples) \\ int ImageBuf.{\ce deep_erase_samples} (x, y, z, samplepos, nsamples)} \NEW % 1.7 Insert or erase \emph{nsamples} samples starting at the given position of pixel {\cf (x,y,z)}. \apiend \apiitem{float ImageBuf.{\ce deep_value} (x, y, z, channel, sample) \\ uint ImageBuf.{\ce deep_value_uint} (x, y, z, channel, sample)} Return the value of the given deep sample (particular pixel, channel, and sample number) for a channel that is a float or an unsigned integer type, respectively. \apiend \apiitem{ImageBuf.{\ce set_deep_value} (x, y, z, channel, sample, value) \\ ImageBuf.{\ce set_deep_value_uint} (x, y, z, channel, sample, value)} Set the value of the given deep sample (particular pixel, channel, and sample number) for a channel that is a float or an unsigned integer type, respectively. \apiend \apiitem{DeepData ImageBuf.{\ce deepdata}} Returns a reference to the underlying {\cf DeepData} of the image. \apiend \newpage \section{ImageBufAlgo} \label{sec:pythonimagebufalgo} The C++ \IBA functions are described in detail in Chapter~\ref{chap:imagebufalgo}. They are also exposed to Python. For the majority of \IBA functions, their use in Python is identical to C++; in those cases, we will keep our descriptions of the Python bindings minimal and refer you to Chapter~\ref{chap:imagebufalgo}, saving the extended descriptions for those functions that differ from the C++ counterparts. A few things about the paramters of the \IBA function calls are identical among the functions, so we will explain once here rather than separately for each function: \begin{itemize} \item {\cf dst} is an existing \ImageBuf, which will be modified (it may be an uninitialized \ImageBuf, but it must be an \ImageBuf). \item {\cf src} parameter is an initialized \ImageBuf, which will not be modified (unless it happens to refer to the same image as {\cf dst}. \item {\cf roi}, if supplied, is an {\cf ROI} specifying a region of interst over which to operate. If omitted, the region will be the entire size of the source image(s). \item {\cf nthreads} is the maximum number of threads to use. If not supplied, it defaults to 0, meaning to use as many threads as hardware cores available. \end{itemize} Just as with the C++ \IBA functions, if {\cf dst} is an uninitialized \ImageBuf, it will be sized to reflect the {\cf roi} (which, in turn, if undefined, will be sized to be the union of the ROI's of the source images). \subsection{Pattern generation} \label{sec:iba:py:patterns} \apiitem{bool ImageBufAlgo.{\ce zero} (dst, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!zero} \indexapi{zero} Zero out the destination buffer (or a specific region of it). \smallskip \noindent Example: \begin{code} # Initialize buf to a 640x480 3-channel FLOAT buffer, zero it out buf = ImageBuf (ImageSpec (640, 480, 3, oiio.FLOAT)) ImageBufAlgo.zero (buf) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce fill} (dst, values, roi=ROI.All, nthreads=0) \\ bool ImageBufAlgo.{\ce fill} (dst, top, bottom, roi=ROI.All, nthreads=0) \\ bool ImageBufAlgo.{\ce fill} (dst, topleft, topright, \\ \bigspc bottomleft, bottomright, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!fill} \indexapi{fill} Sets the the pixels in {\cf dst} within the ROI to a color or gradient. Three options are available: (a) if one color tuple is supplied, the whole ROI will be filled with that constant value, (b) if two color tuples are supplied, a linear gradient will be applied from top to bottom, (c) if four color cuples are supplied, the ROI will be be filled with values bilinearly interpolated from the four corner colors supplied. \smallskip \noindent Examples: \begin{code} # Draw a red rectangle into buf buf = ImageBuf (ImageSpec(640, 480, 3, TypeDesc.FLOAT) ImageBufAlgo.fill (buf, (1,0,0), ROI(50, 100, 75, 85)) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce checker} (dst, width, height, depth, color1, color2, \\ \bigspc xoffset=0, yoffset=0, zoffset=0, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!checker} \indexapi{checker} Fill {\cf dst} with a checkerboard pattern. The colors are specified as tuples giving the values for each color channel. \smallskip \noindent Examples: \begin{code} buf = ImageBuf(ImageSpec(640, 480, 3, oiio.UINT8)) ImageBufAlgo.checker (buf, 64, 64, 1, (0.1,0.1,0.1), (0.4,0.4,0.4)) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce noise_uniform} (dst, min=0.0, max=1.0, \\ \bigspc\bigspc seed=0, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!noise_uniform} \indexapi{noise_uniform} Add noise to {\cf dst} that is uniformly-distributed over the range {\cf [min,max)}. Choosing different seed values will result in a different pattern. \smallskip \noindent Examples: \begin{code} buf = ImageBuf(ImageSpec(640, 480, 3, oiio.UINT8)) ImageBufAlgo.noise_uniform (buf, 0.25, 0.75) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce noise_gaussian} (dst, mean=0.0, stddev=0.1, \\ \bigspc\bigspc seed=0, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!noise_gaussian} \indexapi{noise_gaussian} Fill {\cf dst} with Gaussian noise (normal distribution) with given mean and standard deviation. Choosing different seed values will result in a different pattern. \smallskip \noindent Examples: \begin{code} buf = ImageBuf(ImageSpec(640, 480, 3, oiio.UINT8)) ImageBufAlgo.noise_gaussian (buf, (0.5,0.5,0.5), (0.1,0.1,0.1)) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce render_point} (dst, x, y, color=(1,1,1,1))} \index{ImageBufAlgo!render_point} \indexapi{render_point} \NEW % 1.7 Render a point at pixel $(x,y)$ of {\cf dst}. The {\cf color} (if supplied) is a tuple giving the per-channel colors. \smallskip \noindent Examples: \begin{code} buf = ImageBuf(ImageSpec (640, 480, 4, oiio.FLOAT)) ImageBufAlgo.render_point (buf, 10, 20, (1,0,0,1)) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce render_line} (dst, x1, y1, x2, y2, \\ \bigspc\bigspc color=(1,1,1,1), skip_first_point=False)} \index{ImageBufAlgo!render_line} \indexapi{render_line} \NEW % 1.7 Render a line from pixel $(x_1,y_1)$ to $(x_2,y_2)$ into {\cf dst}. The {\cf color} (if supplied) is a tuple giving the per-channel colors. \smallskip \noindent Examples: \begin{code} buf = ImageBuf(ImageSpec (640, 480, 4, oiio.FLOAT)) ImageBufAlgo.render_line (buf, 10, 10, 500, 20, (1,0,0,1)) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce render_box} (dst, x1, y1, x2, y2, \\ \bigspc\bigspc color=(1,1,1,1), filled=False)} \index{ImageBufAlgo!render_box} \indexapi{render_box} \NEW % 1.7 Render a filled or unfilled box with corners at pixels $(x_1,y_1)$ and $(x_2,y_2)$ into {\cf dst}. The {\cf color} (if supplied) is a tuple giving the per-channel colors. \smallskip \noindent Examples: \begin{code} buf = ImageBuf(ImageSpec (640, 480, 4, oiio.FLOAT)) ImageBufAlgo.render_box (buf, 150, 100, 240, 180, (0,1,1,1)) ImageBufAlgo.render_box (buf, 100, 50, 180, 140, (0.5, 0.5, 0, 0.5), True) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce render_text} (dst, x, y, text, fontsize=16, \\ \bigspc\bigspc fontname="", textcolor=(1,1,1,1))} \index{ImageBufAlgo!render_text} \indexapi{render_text} Render antialiased text into {\cf dst}. The {\cf textcolor} (if supplied) is a tuple giving the per-channel colors. \smallskip \noindent Examples: \begin{code} buf = ImageBuf(ImageSpec (640, 480, 4, oiio.FLOAT)) ImageBufAlgo.render_text (buf, 50, 100, "Hello, world") ImageBufAlgo.render_text (buf, 100, 200, "Go Big Red!", 60, "Arial Bold", (1,0,0,1)) \end{code} % \spc \includegraphics[width=2.5in]{figures/text.jpg} \apiend \subsection{Image transformations and data movement} \label{sec:iba:py:transforms} \apiitem{bool ImageBufAlgo.{\ce channels} (dst, src, channelorder, newchannelnames=(), \\ \bigspc\bigspc shuffle_channel_names=False, nthreads=0)} \index{ImageBufAlgo!channels} \indexapi{channels} Copy {\cf src} to {\cf dst}, but with channels in the order specified by the tuple {\cf channelorder}. The length of {\cf channelorder} specifies the number of channels to copy. Each element in the tuple {\cf channelorder} may be one of the following: \begin{itemize} \item {} \item {\cf int} : specifies the index (beginning at 0) of the channel to copy. \item {\cf str} : specifies the name of the channel to copy. \item {\cf float} : specifies a constant value to use for that channel. \end{itemize} Does not support in-place operation -- that is, {\cf dst} and {\cf src} must be different \ImageBuf's. If {\cf newchannelnames} is supplied, it is a tuple of new channel names. (See the C++ version for more full explanation.) \smallskip \noindent Examples: \begin{code} # Copy the first 3 channels of an RGBA, drop the alpha RGBA = ImageBuf("rgba.tif") RGB = ImageBuf() ImageBufAlgo.channels (RGB, RGBA, (0,1,2)) # Copy just the alpha channel, making a 1-channel image Alpha = ImageBuf() ImageBufAlgo.channels (Alpha, RGBA, ("A",)) # Swap the R and B channels BGRA = ImageBuf() ImageBufAlgo.channels (BRGA, RGBA, (2, 1, 0, 3)) # Add an alpha channel with value 1.0 everywhere to an RGB image ImageBufAlgo.channels (RGBA, RGB, ("R", "G", "B", 1.0), ("R", "G", "B", "A")) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce channel_append} (dst, A, B, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!channel_append} \indexapi{channel_append} Append the channels of images {\cf A} and {\cf B} together into {\cf dst} over the region of interest. \smallskip \noindent Examples: \begin{code} RGBA = ImageBuf ("rgba.exr") Z = ImageBuf ("z.exr") RGBAZ = ImageBuf() ImageBufAlgo.channel_append (RGBAZ, RGBA, Z) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce copy} (dst, src, convert=TypeDesc.UNKNOWN, \\ \bigspc roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!copy} \indexapi{copy} Copy the specified region of pixels of {\cf src} into {\cf dst} at the same locations, without changing any existing pixels of {\cf dst} outside the region. If {\cf dst} is not already initialized, it will be set to the same size as {\cf roi} (defaulting to all of {\cf src}), optionally with the pixel type overridden by {\cf convert} (if it is not {\cf UNKNOWN}). \smallskip \noindent Examples: \begin{code} # Copy A's upper left 200x100 region into B ImageBufAlgo.copy (B, A, ROI(0,200,0,100)) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce crop} (dst, src, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!crop} \indexapi{crop} Reset {\cf dst} to be the specified region of {\cf src}. \smallskip \noindent Examples: \begin{code} # Set B to be the upper left 200x100 region of A A = ImageBuf ("a.tif") B = ImageBuf() ImageBufAlgo.crop (B, A, ROI(0,200,0,100)) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce cut} (dst, src, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!cut} \indexapi{cut} Reset {\cf dst} to be the specified region of {\cf src}, but moved so that the resulting new image has its pixel data at the image plane origin. \smallskip \noindent Examples: \begin{code} # Set B to be the lower left 200x100 region of A, moved to the origin A = ImageBuf ("a.tif") B = ImageBuf() ImageBufAlgo.cut (B, A, ROI(0,200,380,480)) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce paste} (dst, xbegin, ybegin, zbegin, chbegin, \\ \bigspc\bigspc src, ROI srcroi=ROI.All, nthreads=0)} \index{ImageBufAlgo!paste} \indexapi{paste} Copy the specified region of {\cf src} into {\cf dst} beginning at offset {\cf (xbegin, ybegin, zbegin)}. \smallskip \noindent Examples: \begin{code} # Paste small.exr on top of big.exr at offset (100,100) Big = ImageBuf ("big.exr") Small = ImageBuf ("small.exr") ImageBufAlgo.paste (Big, 100, 100, 0, 0, Small) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce rotate90} (dst, src, roi=ROI.All, nthreads=0) \\ bool ImageBufAlgo.{\ce rotate180} (dst, src, roi=ROI.All, nthreads=0) \\ bool ImageBufAlgo.{\ce rotate270} (dst, src, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!roate90} \indexapi{roate90} \index{ImageBufAlgo!rotate180} \indexapi{rotate180} \index{ImageBufAlgo!rotate270} \indexapi{rotate270} Copy while rotating the image by a multiple of 90 degrees. \smallskip \noindent Examples: \begin{code} A = ImageBuf ("tahoe.exr") B = ImageBuf() ImageBufAlgo.rotate90 (B, A) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce flip} (dst, src, roi=ROI.All, nthreads=0) \\ bool ImageBufAlgo.{\ce flop} (dst, src, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!flip} \indexapi{flip} \index{ImageBufAlgo!flop} \indexapi{flop} Copy while reversing orientation vertically (flip) or horizontally (flop). \smallskip \noindent Examples: \begin{code} A = ImageBuf ("tahoe.exr") B = ImageBuf() ImageBufAlgo.flip (B, A) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce reorient} (dst, src, nthreads=0)} \index{ImageBufAlgo!reorient} \indexapi{reorient} Copy {\cf src} to {\cf dst}, but with whatever seties of rotations, flips, or flops are necessary to transform the pixels into the configuration suggested by the \qkw{Orientation} metadata of the image (and the \qkw{Orientation} metadata is then set to 1, ordinary orientation). \smallskip \noindent Examples: \begin{code} A = ImageBuf ("tahoe.jpg") ImageBufAlgo.reorient (A, A) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce transpose} (dst, src, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!transpose} \indexapi{transpose} Copy while transposing ($x \leftrightarrow y$) pixels. \smallskip \noindent Examples: \begin{code} A = ImageBuf ("tahoe.exr") B = ImageBuf() ImageBufAlgo.transpose (B, A) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce circular_shift} (dst, src, xshift, yshift, zshift=0, \\ \bigspc roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!circular_shift} \indexapi{circular_shift} Copy while circularly shifting by the given amount. \smallskip \noindent Examples: \begin{code} A = ImageBuf ("tahoe.exr") B = ImageBuf() ImageBufAlgo.circular_shift (B, A, 200, 100) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce rotate} (dst, src, angle, \\ \bigspc\bigspc filtername="", filtersize=0.0, recompute_roi=False, \\ \bigspc\bigspc roi=ROI.All, nthreads=0) \\ bool ImageBufAlgo.{\ce rotate} (dst, src, angle, \\ \bigspc\bigspc center_x, center_y, filtername="", filtersize=0.0, \\ \bigspc\bigspc recompute_roi=False, roi=ROI.All, nthreads=0) } \index{ImageBufAlgo!rotate} \indexapi{rotate} Set {\cf dst}, over the ROI, to be a rotated version of the corresponding portion of {\cf src}. The angle is in radians, with positive values indicating clockwise rotation. If the filter and size are not specified, an appropriate default will be chosen. \smallskip \noindent Examples: \begin{code} Src = ImageBuf ("tahoe.exr") Dst = ImageBuf () ImageBufAlgo.rotate (Dst, Src, math.radians(45.0)) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce warp} (dst, src, M, filtername="", filtersize=0.0, \\ \bigspc\bigspc wrap=oiio.WrapDefault, recompute_roi=False, \\ \bigspc\bigspc roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!warp} \indexapi{warp} Set {\cf dst}, over the ROI, to be a warped (transformed) copy of {\cf src}, with the warp specified by {\cf M} consisting of 9 floating-point numbers representing a $3 \times 3$ transformation matrix. If the filter and size are not specified, an appropriate default will be chosen. \smallskip \noindent Examples: \begin{code} M = (0.7071068, 0.7071068, 0, -0.7071068, 0.7071068, 0, 20, -8.284271, 1) Src = ImageBuf ("tahoe.exr") Dst = ImageBuf () ImageBufAlgo.warp (Dst, Src, M) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce resize} (dst, src, filtername="", filtersize=0.0, \\ \bigspc\bigspc roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!resize} \indexapi{resize} Set {\cf dst}, over the ROI, to be a high-quality resized version of the corresponding portion of {\cf src}. If the filter and size are not specified, an appropriate default will be chosen. \smallskip \noindent Examples: \begin{code} # Resize the image to 640x480, using the default filter Src = ImageBuf ("tahoe.exr") Dst = ImageBuf (ImageSpec (640, 480, 3, OpenImageIO.FLOAT)) ImageBufAlgo.resize (Dst, Src) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce resample} (dst, src, interpolate=True, \\ \bigspc\bigspc roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!resample} \indexapi{resample} Set {\cf dst}, over the ROI, to be a low-quality (but fast) resized version of the corresponding portion of {\cf src}, either using a simple ``closest pixel'' choice or by bilinaerly interpolating (depending on {\cf interpolate}). \smallskip \noindent Examples: \begin{code} # Resample quickly to 320x240 to make a low-quality thumbnail Src = ImageBuf ("tahoe.exr") Dst = ImageBuf (ImageSpec (320, 240, 3, OpenImageIO.UINT8)) ImageBufAlgo.resample (Dst, Src) \end{code} \apiend \subsection{Image arithmetic} \label{sec:iba:py:arith} \apiitem{bool ImageBufAlgo.{\ce add} (dst, A, B, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!add} \indexapi{add} Compute {\cf dst = A + B}. {\cf A} is an \ImageBuf, and {\cf B} may be an \ImageBuf, a {\cf float} (added to all channels) or a tuple giving a {\cf float} for each color channel. \smallskip \noindent Examples: \begin{code} # Add two images buf = ImageBuf () ImageBufAlgo.add (buf, ImageBuf("a.exr"), ImageBuf("b.exr")) # Add 0.2 to channels 0-2 ImageBufAlgo.add (buf, buf, (0.2,0.2,0.2,0)) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce sub} (dst, A, B, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!sub} \indexapi{sub} Compute {\cf dst = A - B}. {\cf A} is an \ImageBuf, and {\cf B} may be an \ImageBuf, a {\cf float} (added to all channels) or a tuple giving a {\cf float} for each color channel. \smallskip \noindent Examples: \begin{code} buf = ImageBuf () ImageBufAlgo.sub (buf, ImageBuf("a.exr"), ImageBuf("b.exr")) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce absdiff} (dst, A, B, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!absdiff} \indexapi{absdiff} Compute {\cf dst = abs(A - B)}. {\cf A} is an \ImageBuf, and {\cf B} may be an \ImageBuf, a {\cf float} (added to all channels) or a tuple giving a {\cf float} for each color channel. \smallskip \noindent Examples: \begin{code} buf = ImageBuf () ImageBufAlgo.absdiff (buf, ImageBuf("a.exr"), ImageBuf("b.exr")) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce abs} (dst, A, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!abs} \indexapi{abs} Compute {\cf dst = abs(A)}. {\cf A} is an \ImageBuf. \smallskip \noindent Examples: \begin{code} buf = ImageBuf () ImageBufAlgo.abs (buf, ImageBuf("a.exr")) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce mul} (dst, A, B, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!mul} \indexapi{mul} Compute {\cf dst} = {\cf A * B} (channel-by-channel multiplication). {\cf A} is an \ImageBuf, and {\cf B} may be an \ImageBuf, a {\cf float} (scaling all channels) or a tuple giving a {\cf float} for each color channel. \smallskip \noindent Examples: \begin{code} # Multiply the two images buf = ImageBuf () ImageBufAlgo.mul (buf, ImageBuf("a.exr"), ImageBuf("b.exr")) # Reduce intensity of buf's channels 0-2 by 50%, in place ImageBufAlgo.mul (buf, buf, (0.5, 0.5, 0.5, 1.0)) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce div} (dst, A, B, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!div} \indexapi{div} Compute {\cf dst} = {\cf A / B} (channel-by-channel division), where $x/0$ is defined to be $0$. {\cf A} is an \ImageBuf, and {\cf B} may be an \ImageBuf, a {\cf float} (scaling all channels) or a tuple giving a {\cf float} for each color channel. \smallskip \noindent Examples: \begin{code} # Divide a.exr by b.exr buf = ImageBuf () ImageBufAlgo.div (buf, ImageBuf("a.exr"), ImageBuf("b.exr")) # Reduce intensity of buf's channels 0-2 by 50%, in place ImageBufAlgo.div (buf, buf, (2.0, 2.0, 2.0, 1.0)) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce mad} (dst, A, B, C, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!mad} \indexapi{mad} Compute {\cf dst} = {\cf A * B + C} (channel-by-channel multiplication and addition). {\cf A} is an \ImageBuf, and {\cf B} and {\cf C} may be both \ImageBuf, both {\cf float} (scaling all channels) or a both tuples giving a {\cf float} for each color channel. \smallskip \noindent Examples: \begin{code} # Multiply a and b, then add c buf = ImageBuf () ImageBufAlgo.mad (buf, ImageBuf("a.exr"), ImageBuf("b.exr"), ImageBuf("c.exr")) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce invert} (dst, A, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!invert} \indexapi{invert} Compute {\cf dst = 1-A} (channel by channel color inverse). {\cf A} is an \ImageBuf. \smallskip \noindent Examples: \begin{code} buf = ImageBuf () ImageBufAlgo.invert (buf, ImageBuf("a.exr")) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce pow} (dst, A, B, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!pow} \indexapi{pow} Compute {\cf dst} = {\cf pow (A, B} (channel-by-channel exponentiation). {\cf A} is an \ImageBuf, and {\cf B} may be a {\cf float} (a single power for all channels) or a tuple giving a {\cf float} for each color channel. \smallskip \noindent Examples: \begin{code} # Linearize a 2.2 gamma-corrected image (channels 0-2 only) img = ImageBuf ("a.exr") buf = ImageBuf () ImageBufAlgo.pow (buf, img, (2.2, 2.2, 2.2, 1.0)) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce channel_sum} (dst, src, weights=(), \\ \bigspc\bigspc roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!channel_sum} \indexapi{channel_sum} Converts a multi-channel image into a 1-channel image via a weighted sum of channels. The {\cf weights} is a tuple providing the weight for each channel (if not supplied, all channels will have weight 1.0). \smallskip \noindent Examples: \begin{code} # Compute luminance via a weighted sum of R,G,B # (assuming Rec709 primaries and a linear scale) luma_weights = (.2126, .7152, .0722) ImageBufAlgo.channel_sum (luma, ImageBuf("a.exr"), luma_weights) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce clamp} (dst, src, min, max, bool clampalpha01=False, \\ \bigspc\bigspc roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!clamp} \indexapi{clamp} Copy pixels while clamping between the {\cf min} and {\cf max} values. The {\cf min} and {\cf max} may either be tuples (one min and max value per channel), or single {\cf floats} (same value for all channels). Additionally, if {\cf clampalpha01} is {\cf True}, then any alpha channel is clamped to the 0--1 range. \smallskip \noindent Examples: \begin{code} # Clamp image buffer A in-place to the [0,1] range for all channels. ImageBufAlgo.clamp (A, A, 0.0, 1.0) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce rangecompress} (dst, src, useluma=False, \\ \bigspc\bigspc roi=ROI.All, nthreads=0) \\ bool ImageBufAlgo.{\ce rangeexpand} (dst, src, useluma=False, \\ \bigspc\bigspc roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!rangecompress} \indexapi{rangecompress} \index{ImageBufAlgo!rangeexpand} \indexapi{rangeexpand} Copy from {\cf src} to {\cf dst}, compressing (logarithmically) or expanding (by the inverse of the compressive transformation) the range of pixel values. Alpha and z channels are copied but not transformed. If {\cf useluma} is {\cf True}, the luma of the first three channels (presumed to be R, G, and B) are used to compute a single scale factor for all color channels, rather than scaling all channels individually (which could result in a big color shift when performing {\cf rangecompress} and {\cf rangeexpand}). \smallskip \noindent Examples: \begin{code} # Resize the image to 640x480, using a Lanczos3 filter, which # has negative lobes. To prevent those negative lobes from # producing ringing or negative pixel values for HDR data, # do range compression, then resize, then re-expand the range. # 1. Read the original image Src = ImageBuf ("tahoeHDR.exr") # 2. Range compress to a logarithmic scale Compressed = ImageBuf () ImageBufAlgo.rangecompress (Compressed, Src) # 3. Now do the resize Dst = ImageBuf () roi = ROI (0, 640, 0, 480, 0, 1, 0, Compressed.nchannels) ImageBufAlgo.resize (Dst, Compressed, "lanczos3", 6.0, roi) # 4. Expand range to be linear again (operate in-place) ImageBufAlgo.rangeexpand (Dst, Dst) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce over} (dst, A, B, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!over} \indexapi{over} Composite \ImageBuf\ {\cf A} \emph{over} \ImageBuf\ {\cf B}. \smallskip \noindent Examples: \begin{code} Composite = ImageBuf() ImageBufAlgo.over (Composite, ImageBuf("fg.exr"), ImageBuf("bg.exr")) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce zover} (dst, A, B, bool z_zeroisinf=False,\\ \bigspc\bigspc roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!zover} \indexapi{zover} Composite \ImageBuf\ {\cf A} and \ImageBuf\ {\cf B} using their respective $Z$ channels to decide which is in front on a pixel-by-pixel basis. \smallskip \noindent Examples: \begin{code} Composite = ImageBuf() ImageBufAlgo.zover (Composite, ImageBuf("fg.exr"), ImageBuf("bg.exr")) \end{code} \apiend \subsection{Image comparison and statistics} \label{sec:iba:py:stats} \apiitem{bool ImageBufAlgo.{\ce computePixelStats} (src, stats, \\ \bigspc\bigspc roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!computePixelStats} \indexapi{computePixelStats} Compute statistics about the ROI of the image {\cf src}, storing results in {\cf stats} (which must refer to an {\cf PixelStats} object). A return value of {\cf True} indicates success, {\cf False} indicates that it was not possible to complete the operation. The {\cf PixelStats} structure is defined as contining the following data fields: {\cf min}, {\cf max}, {\cf avg}, {\cf stddev}, {\cf nancount}, {\cf infcount}, {\cf finitecount}, {\cf sum}, {\cf sum2}, each of which is a \emph{tuple} with one value for each channel of the image. \smallskip \noindent Examples: \begin{code} A = ImageBuf("a.exr") stats = OpenImageIO.PixelStats() computePixelStats (A, stats) print " min = ", stats.min print " max = ", stats.max print " average = ", stats.avg print " standard deviation = ", stats.stddev print " # NaN values = ", stats.nancount print " # Inf values = ", stats.infcount print " # finite values = ", stats.finitecount \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce compare} (A, B, failthresh, warnthresh, result, \\ \bigspc\bigspc roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!compare} \indexapi{compare} Numerically compare two \ImageBuf's, {\cf A} and {\cf B}. The {\cf failthresh} and {\cf warnthresh} supply failure and warning difference thresholds. The {\cf result} parameter must refer to a {\cf CompareResults} object, which is defined as a class having the following members: \begin{code} meanerror, rms_error, PSNR, maxerror # error statistics maxx, maxy, maxz, maxc # pixel of biggest difference nwarn, nfail # number of warnings and failures \end{code} \smallskip \noindent Examples: \begin{code} A = ImageBuf ("a.exr") B = ImageBuf ("b.exr") comp = OpenImageIO.CompareResults() ImageBufAlgo.compare (A, B, 1.0/255.0, 0.0, comp) if comp.nwarn == 0 and comp.nfail == 0 : print "Images match within tolerance" else : print comp.nfail, "failures,", comp.nwarn, " warnings." print "Average error was " , comp.meanerror print "RMS error was" , comp.rms_error print "PSNR was" , comp.PSNR print "largest error was ", comp.maxerror print " on pixel", (comp.maxx, comp.maxy, comp.maxz) print " channel", comp.maxc \end{code} \apiend \apiitem{tuple ImageBufAlgo.{\ce isConstantColor} (src, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!isConstantColor} \indexapi{isConstantColor} If all pixels of {\cf src} within the ROI have the same values (for the subset of channels described by {\cf roi}), return a tuple giving that color (one {\cf float} for each channel), otherwise return {\cf None}. \smallskip \noindent Examples: \begin{code} A = ImageBuf ("a.exr") color = ImageBufAlgo.isConstantColor (A) if color != None : print "The image has the same value in all pixels:", color else : print "The image is not a solid color." \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce isConstantChannel} (src, channel, val, \\ \bigspc\bigspc roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!isConstantChannel} \indexapi{isConstantChannel} Returns {\cf True} if all pixels of {\cf src} within the ROI have the given {\cf channel} value {\cf val}. \smallskip \noindent Examples: \begin{code} A = ImageBuf ("a.exr") alpha = A.spec.alpha_channel if alpha < 0 : print "The image does not have an alpha channel" elif ImageBufAlgo.isConstantChannel (A, alpha, 1.0) : print "The image has alpha = 1.0 everywhere" else : print "The image has alpha < 1 in at least one pixel" \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce isMonochrome} (src, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!isMonochrome} \indexapi{isMonochrome} Returns {\cf True} if the image is monochrome within the ROI. \smallskip \noindent Examples: \begin{code} A = ImageBuf ("a.exr") roi = A.roi roi.chend = min (3, roi.chend) # only test RGB, not alpha if ImageBufAlgo.isMonochrome (A, roi) : print "a.exr is really grayscale" \end{code} \apiend \begin{comment} \apiitem{bool ImageBufAlgo.{\ce color_count} (src, imagesize_t *count,\\ \bigspc int ncolors, const float *color, const float *eps=NULL, \\ \bigspc roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!color_count} \indexapi{color_count} Count how many pixels in the image (within the ROI) match a list of colors. The colors to match are in: \begin{code} colors[0 ... nchans-1] colors[nchans ... 2*nchans-1] ... colors[(ncolors-1)*nchans ... (ncolors*nchans)-1] \end{code} \noindent and so on, a total of {\cf ncolors} consecutively stored colors of {\cf nchans} channels each ({\cf nchans} is the number of channels in the image, itself, it is not passed as a parameter). The values in {\cf eps[0..nchans-1]} are the error tolerances for a match, for each channel. Setting {\cf eps[c]} to {\cf numeric_limits::max()} will effectively make it ignore the channel. Passing {\cf eps == NULL} will be interpreted as a tolerance of 0.001 for all channels (requires exact matches for 8 bit images, but allows a wee bit of imprecision for {\cf float} images. \smallskip \noindent Examples: \begin{code} A = ImageBuf ("a.exr") n = A.nchannels # Try to match two colors: pure red and green std::vector colors (2*n, numeric_limits::max()); colors[0] = 1.0; colors[1] = 0.0; colors[2] = 0.0; colors[n+0] = 0.0; colors[n+1] = 1.0; colors[n+2] = 0.0; const int ncolors = 2; imagesize_t count[ncolors]; ImageBufAlgo.color_count (A, count, ncolors); print "Number of red pixels : ", count[0] print "Number of green pixels : ", count[1] \end{code} \apiend \apiitem{bool {\ce color_range_check} (src, imagesize_t *lowcount, \\ \bigspc imagesize_t *highcount, imagesize_t *inrangecount, \\ \bigspc const float *low, const float *high, \\ \bigspc roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!color_range_check} \indexapi{color_range_check} Count how many pixels in the image (within the ROI) are outside the value range described by {\cf low[roi.chbegin..roi.chend-1]} and {\cf high[roi.chbegin..roi.chend-1]} as the low and high acceptable values for each color channel. The number of pixels containing values that fall below the lower bound will be stored in {\cf *lowcount}, the number of pixels containing values that fall above the upper bound will be stored in {\cf *highcount}, and the number of pixels for which all channels fell within the bounds will be stored in {\cf *inrangecount}. Any of these may be NULL, which simply means that the counts need not be collected or stored. \smallskip \noindent Examples: \begin{code} A = ImageBuf ("a.exr") ROI roi = get_roi (A.spec()) roi.chend = std::min (roi.chend, 4); # only compare RGBA float low[] = {0, 0, 0, 0}; float high[] = {1, 1, 1, 1}; imagesize_t lowcount, highcount, inrangecount; ImageBufAlgo.color_range_check (A, &lowcount, &highcount, &inrangecount, low, high, roi); print lowcount, " pixels had components < 0" print highcount, " pixels had components > 1" print inrangecount, " pixels were fully within [0,1] range" \end{code} \apiend \end{comment} \apiitem{std::string ImageBufAlgo.{\ce computePixelHashSHA1} (src, extrainfo = "", \\ \bigspc\bigspc roi=ROI.All, blocksize=0, nthreads=0)} \index{ImageBufAlgo!computePixelHashSHA1} \indexapi{computePixelHashSHA1} Compute the SHA-1 byte hash for all the pixels in the ROI of {\cf src}. \smallskip \noindent Examples: \begin{code} A = ImageBuf ("a.exr") hash = ImageBufAlgo.computePixelHashSHA1 (A, blocksize=64) \end{code} \apiend \begin{comment} \apiitem{bool {\ce histogram} (src, int channel, \\ \bigspc std::vector \&histogram, int bins=256, \\ \bigspc float min=0, float max=1, imagesize_t *submin=NULL, \\ \bigspc imagesize_t *supermax=NULL, roi=ROI.All)} \index{ImageBufAlgo!histogram} \indexapi{histogram} \apiend \end{comment} \subsection{Convolutions} \label{sec:iba:py:convolutions} \apiitem{bool ImageBufAlgo.{\ce make_kernel} (dst, name, width, height, \\ \bigspc\bigspc depth=1.0, normalize=True)} \index{ImageBufAlgo!make_kernel} \indexapi{make_kernel} Initialize {\cf dst} to be a 1-channel {\cf float} image of the named kernel and dimensions. If {\cf normalize} is {\cf True}, the values will be normalized so that they sum to $1.0$. If {\cf depth} $> 1$, a volumetric kernel will be created. Use with caution! Kernel names can be: \qkw{gaussian}, \qkw{sharp-gaussian}, \qkw{box}, \qkw{triangle}, \qkw{mitchell}, \qkw{blackman-harris}, \qkw{b-spline}, \qkw{catmull-rom}, \qkw{lanczos3}, \qkw{cubic}, \qkw{keys}, \qkw{simon}, \qkw{rifman}, \qkw{disk}, \qkw{binomial}, \qkw{laplacian}. Note that \qkw{catmull-rom} and \qkw{lanczos3} are fixed-size kernels that don't scale with the width, and are therefore probably less useful in most cases. \smallskip \noindent Examples: \begin{code} K = ImageBuf() ImageBufAlgo.make_kernel (K, "gaussian", 5.0, 5.0) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce convolve} (dst, src, kernel, normalize=True, \\ \bigspc\bigspc roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!convolve} \indexapi{convolve} Replace the given ROI of {\cf dst} with the convolution of {\cf src} and a kernel (also an \ImageBuf). \smallskip \noindent Examples: \begin{code} # Blur an image with a 5x5 Gaussian kernel Src = ImageBuf ("tahoe.exr") K = ImageBuf () ImageBufAlgo.make_kernel (K, "gaussian", 5.0, 5.0) Blurred = ImageBuf () ImageBufAlgo.convolve (Blurred, Src, K) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce laplacian} (dst, src, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!laplacian} \indexapi{laplacian} \NEW % 1.7 Replace the given ROI of {\cf dst} with the Laplacian of the corresponding part of {\cf src}. \smallskip \noindent Examples: \begin{code} Src = ImageBuf ("tahoe.exr") L = ImageBuf () ImageBufAlgo.laplacian (L, Src) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce fft} (dst, src, roi=ROI.All, nthreads=0) \\ bool ImageBufAlgo.{\ce ifft} (dst, src, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!fft} \indexapi{fft} \index{ImageBufAlgo!ifft} \indexapi{ifft} Compute the forward or inverse discrete Fourier Transform. \smallskip \noindent Examples: \begin{code} Src = ImageBuf ("tahoe.exr") # Take the DFT of the first channel of Src Freq = ImageBuf () ImageBufAlgo.fft (Freq, Src) # At this point, Freq is a 2-channel float image (real, imag) # Convert it back from frequency domain to a spatial iamge Spatial = ImageBuf () ImageBufAlgo.ifft (Spatial, Freq) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce complex_to_polar} (dst, src, roi=ROI.All, nthreads=0) \\ bool ImageBufAlgo.{\ce polar_to_complex} (dst, src, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!polar_to_complex} \indexapi{polar_to_complex} \index{ImageBufAlgo!complex_to_polar} \indexapi{complex_to_polar} Transform a 2-channel image from complex (real, imaginary) representation to polar (amplitude, phase), or vice versa. \smallskip \noindent Examples: \begin{code} Polar = ImageBuf ("polar.exr") Complex = ImageBuf () ImageBufAlgo.polar_to_complex (Complex, Polar) # At this point, Complex is a 2-channel complex image (real, imag) # Convert it back from frequency domain to a spatial iamge Spatial = ImageBuf () ImageBufAlgo.ifft (Spatial, Complex) \end{code} \apiend \subsection{Image Enhancement / Restoration} \label{sec:iba:py:enhance} \apiitem{bool ImageBufAlgo.{\ce fixNonFinite} (dst, src, mode=NONFINITE_BOX3, \\ \bigspc\spc roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!fixNonFinite} \indexapi{fixNonFinite} Copy pixel values from {\cf src} to {\cf dst} (within the pixel and channel range designated by {\cf roi}), and repair any non-finite ({\cf NaN} or {\cf Inf}) pixels. How the non-finite values are repaired is specified by one of the following modes: \\ {\cf OpenImageIO.NONFINITE_NONE}, \\ {\cf OpenImageIO.NONFINITE_BLACK} \\ {\cf OpenImageIO.NONFINITE_BOX3} \smallskip \noindent Examples: \begin{code} Src = ImageBuf ("tahoe.exr") ImageBufAlgo.fixNonFinite (Src, Src, OpenImageIO.NONFINITE_BOX3) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce fillholes_pushpull} (dst, src, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!fillholes_pushpull} \indexapi{fillholes_pushpull} Copy the specified ROI of {\cf src} to {\cf dst} and fill any holes (pixels where alpha $< 1$) with plausible values using a push-pull technique. The {\cf src} image must have an alpha channel. The dst image will end up with a copy of src, but will have an alpha of 1.0 everywhere, and any place where the alpha of src was < 1, dst will have a pixel color that is a plausible ``filling'' of the original alpha hole. \smallskip \noindent Examples: \begin{code} Src = ImageBuf ("holes.exr") Filled = ImageBuf () ImageBufAlgo.fillholes_pushpull (Filled, Src) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce median_filter} (dst, src, width=3, height=-1, \\ \bigspc\spc roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!median_filter} \indexapi{median_filter} Replace the given ROI of {\cf dst} with the ${\mathit width} \times {\mathit height}$ median filter of the corresponding region of {\cf src} using the ``unsharp mask'' technique. \smallskip \noindent Examples: \begin{code} Noisy = ImageBuf ("tahoe.exr") Clean = ImageBuf () ImageBufAlgo.median_filter (Clean, Noisy, 3, 3) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce dilate} (dst, src, width=3, height=-1, \\ \bigspc\spc roi=ROI.All, nthreads=0) \\ bool ImageBufAlgo.{\ce erode} (dst, src, width=3, height=-1, \\ \bigspc\spc roi=ROI.All, nthreads=0) } \index{ImageBufAlgo!dilate} \indexapi{dilate} \index{ImageBufAlgo!erode} \indexapi{erode} \NEW % 1.7 Replace the given ROI of {\cf dst} with a dilated or eroded version of the corresponding region of {\cf src}. \smallskip \noindent Examples: \begin{code} Source = ImageBuf ("source.tif") Dilated = ImageBuf () ImageBufAlgo.dilate (Dilated, Source, 3, 3) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce unsharp_mask} (dst, src, kernel="gaussian", \\ \bigspc\spc width=3.0, contrast=1.0, threshold=0.0, \\ \bigspc\spc roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!unsharp_mask} \indexapi{unsharp_mask} Replace the given ROI of {\cf dst} with a sharpened version of the corresponding region of {\cf src} using the ``unsharp mask'' technique. \smallskip \noindent Examples: \begin{code} Blurry = ImageBuf ("tahoe.exr") Sharp = ImageBuf () ImageBufAlgo.unsharp_mask (Sharp, Blurry, "gaussian", 5.0) \end{code} \apiend \subsection{Color manipulation} \label{sec:iba:py:color} \apiitem{bool ImageBufAlgo.{\ce colorconvert} (dst, src, from, to, unpremult=False, \\ \bigspc\bigspc context_key="", context_value="", \\ \bigspc\bigspc colorconfig="", roi=ROI.All, nthreads=0) \\ } \index{ImageBufAlgo!colorconvert} \indexapi{colorconvert} Copy pixels from {\cf src} to {\cf dst} (within the ROI), while applying a color transform to the pixel values. In-place operations ({\cf dst} and {\cf src} being the same image) are supported. \smallskip \noindent Examples: \begin{code} Src = ImageBuf ("tahoe.jpg") Dst = ImageBuf () ImageBufAlgo.colorconvert (Dst, Src, "vd8", "lnf") \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce ociolook} (dst, src, looks, from, to, \\ \bigspc\bigspc inverse=False, unpremult=False, \\ \bigspc\bigspc context_key="", context_value="", colorconfig="", \\ \bigspc\bigspc roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!ociolook} \indexapi{ociolook} Copy pixels from {\cf src} to {\cf dst} (within the ROI), while applying an OpenColorIO ``look'' transform to the pixel values. In-place operations ({\cf dst} and {\cf src} being the same image) are supported. \smallskip \noindent Examples: \begin{code} Src = ImageBuf ("tahoe.jpg") Dst = ImageBuf () ImageBufAlgo.ociolook (Dst, Src, "look", "vd8", "lnf", False, False, context_key="SHOT", context_value="pe0012") \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce ociodisplay} (dst, src, display, view, \\ \bigspc\bigspc from=None, looks=None, unpremult=False, \\ \bigspc\bigspc context_key="", context_value="", colorconfig="", \\ \bigspc\bigspc roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!ociodisplay} \indexapi{ociodisplay} Copy pixels from {\cf src} to {\cf dst} (within the ROI), while applying an OpenColorIO ``display'' transform to the pixel values. In-place operations ({\cf dst} and {\cf src} being the same image) are supported. \smallskip \noindent Examples: \begin{code} Src = ImageBuf ("tahoe.exr") Dst = ImageBuf () ImageBufAlgo.ociodisplay (Dst, Src, "sRGB", "Film", "lnf", False, context_key="SHOT", context_value="pe0012") \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce unpremult} (dst, src, roi=ROI.All, nthreads=0) \\ bool ImageBufAlgo.{\ce premult} (dst, src, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!unpremult} \indexapi{unpremult} \index{ImageBufAlgo!premult} \indexapi{premult} Copy pixels from {\cf src} to {\cf dst}, and un-premultiply (or premultiply) the colors by alpha. \smallskip \noindent Examples: \begin{code} # Convert in-place from associated alpha to unassociated alpha A = ImageBuf ("a.exr") ImageBufAlgo.unpremult (A, A) \end{code} \apiend \subsection{Import / export} \label{sec:iba:py:importexport} \apiitem{bool ImageBufAlgo.{\ce make_texture} (mode, input,\\ \bigspc\bigspc outputfilename, config=ImageSpec())} \index{ImageBufAlgo!make_texture} \indexapi{make_texture} Turn an input image (either an \ImageBuf or a string giving a filename) into a tiled, MIP-mapped, texture file and write to the file named by ({\cf outputfilename}). The {\cf mode} describes what type of texture file we are creating and may be one of the following: \noindent \begin{tabular}{p{4in}} {\cf OpenImageIO.MakeTxTexture} \\ {\cf OpenImageIO.MakeTxEnvLatl} \\ {\cf OpenImageIO.MakeTxEnvLatlFromLightProbe} \\ \end{tabular} The {\cf config}, if supplied, is an \ImageSpec that contains all the information and special instructions for making the texture. The full list of supported configuration options is given in Section~\ref{sec:iba:importexport}. \smallskip \noindent Examples: \begin{code} # This command line: # maketx in.exr --hicomp --filter lanczos3 --opaque-detect \ # -o texture.exr # is equivalent to: Input = ImageBuf ("in.exr") config = ImageSpec() config.attribute ("maketx:highlightcomp", 1) config.attribute ("maketx:filtername", "lanczos3") config.attribute ("maketx:opaquedetect", 1) ImageBufAlgo.make_texture (oiio.MakeTxTexture, Input, "texture.exr", config) \end{code} \apiend \apiitem{bool ImageBufAlgo::{\ce capture_image} (dst, cameranum, \\ \bigspc\bigspc convert = OpenImageIO.UNKNOWN)} \index{ImageBufAlgo!capture_image} \indexapi{capture_image} Capture a still image from a designated camera. \smallskip \noindent Examples: \begin{code} WebcamImage = ImageBuf() ImageBufAlgo.capture_image (WebcamImage, 0, OpenImageIO.UINT8) WebcamImage.save ("webcam.jpg") \end{code} \apiend \subsection{Functions specific to deep images} \apiitem{bool ImageBufAlgo.{\ce deepen} (dst, src, zvalue=1.0, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!deepen} \indexapi{deepen} \index{deep images} Convert a flat image to a deep one that has one depth sample per pixel (but no depth samples for the pixels corresponding to those in the source image that have infinite \qkw{Z} or that had 0 for all color channels and no \qkw{Z} channel). \smallskip \noindent Examples: \begin{code} Deep = ImageBuf() ImageBufAlgo.deepen (Deep, ImageBuf("az.exr")) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce flatten} (dst, src, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!flatten} \indexapi{flatten} \index{deep images} Composite the depth samples within each pixel of ``deep'' \ImageBuf\ {\cf src} and store the single resulting value per pixel in ``flat'' \ImageBuf {\cf dst}. \smallskip \noindent Examples: \begin{code} Flat = ImageBuf() ImageBufAlgo.flatten (Flat, ImageBuf("deepalpha.exr")) \end{code} \apiend \apiitem{bool ImageBufAlgo.{\ce deep_merge} (dst, A, B, occlusion_cull, roi=ROI.All, nthreads=0)} \index{ImageBufAlgo!deep_merge} \indexapi{deep_merge} \index{deep images} \NEW % 1.7 Merge the samples of two \emph{deep} images {\cf A} and {\cf B} into deep result {\cf dst}. If {\cf occlusion_cull} is {\cf True}, samples beyond the first opaque sample will be discarded, otherwise they will be kept. \smallskip \noindent Examples: \begin{code} DeepA = ImageBuf("hardsurf.exr") DeepB = ImageBuf("volume.exr") Merged = ImageBuf() ImageBufAlgo.deep_merge (Merged, DeepA, DeepB) \end{code} \apiend \subsection*{Other ImageBufAlgo methods that understand deep images} In addition to the previously described methods that are specific to deep images, the following \ImageBufAlgo methods (described in their respective sections) work with deep inputs: \medskip \noindent {\cf ImageBufAlgo.add} \\ {\cf ImageBufAlgo.channels} \\ {\cf ImageBufAlgo.compare} \\ {\cf ImageBufAlgo.computePixelStats} \\ {\cf ImageBufAlgo.crop} \\ {\cf ImageBufAlgo.div} \\ {\cf ImageBufAlgo.fixNonFinite} \\ {\cf ImageBufAlgo.mul} \\ {\cf ImageBufAlgo.nonzero_region} \\ {\cf ImageBufAlgo.resample} \\ {\cf ImageBufAlgo.sub} \\ \newpage \section{Miscellaneous Utilities} \label{sec:pythonmiscapi} In the main {\cf OpenImageIO} module, there are a number of values and functions that are useful. These correspond to the C++ API functions explained in Section~\ref{sec:miscapi}, please refer there for details. \apiitem{int {\ce openimageio_version}} The \product version number, 10000 for each major version, 100 for each minor version, 1 for each patch. For example, \product 1.2.3 would return a value of 10203. \apiend \apiitem{str {\ce geterror} ()} Retrieves the latest global error. \apiend \apiitem{bool {\ce attribute} (name, typedesc, value) \\ bool {\ce attribute} (name, int_value) \\ bool {\ce attribute} (name, float_value) \\ bool {\ce attribute} (name, str_value)} Sets a global attribute (see Section~\ref{sec:miscapi} for details), returning {\cf True} upon success, or {\cf False} if it was not a recognized attribute. \noindent Example: \begin{code} oiio.attribute ("threads", 0) \end{code} \apiend \apiitem{{\ce getattribute} (name, typedesc) \\ {\ce get_int_attribute} (name, defaultval=0) \\ {\ce get_float_attribute} (name, defaultval=0.0) \\ {\ce get_string_attribute} (name, defaultval="")} \NEW % 1.7 Retrieves an attribute value from the named set of global OIIO options. (See Section~\ref{sec:globalattribute}.) The {\cf getattribute()} function returns the value regardless of type, or {\cf None} if the attribute does not exist. The typed variety will only succeed if the attribute is actually of that type specified. Type varity with the type in the name also takes a default value. \noindent Example: \begin{code} formats = oiio.get_string_attribute ("format_list") \end{code} \apiend \section{Python Recipes} \label{sec:pythonrecipes} This section illustrates the Python syntax for doing many common image operations from Python scripts, but that aren't already given as examples in the earlier function descriptions. All example code fragments assume the following boilerplate: \begin{code} #!/usr/bin/env python import OpenImageIO as oiio from OpenImageIO import ImageBuf, ImageSpec, ImageBufAlgo \end{code} \subsubsection*{Subroutine to create a constant-colored image} \begin{code} # Create an ImageBuf holding a n image of constant color, given the # resolution, data format (defaulting to UINT8), fill value, and image # origin. def make_constimage (xres, yres, chans=3, format=oiio.UINT8, value=(0,0,0), xoffset=0, yoffset=0) : spec = ImageSpec (xres,yres,chans,format) spec.x = xoffset spec.y = yoffset b = ImageBuf (spec) oiio.ImageBufAlgo.fill (b, value) return b \end{code} \noindent The image is returned as an \ImageBuf, then up to the caller what to do with it next. \subsubsection*{Subroutine to save an image to disk, printing errors} \begin{code} # Save an ImageBuf to a given file name, with optional forced image format # and error handling. def write_image (image, filename, format=oiio.UNKNOWN) : if not image.has_error : image.set_write_format (format) image.write (filename) if image.has_error : print "Error writing", filename, ":", image.geterror() \end{code} \subsubsection*{Converting between file formats} \begin{code} img = ImageBuf ("input.png") write_image (img, "output.tif") \end{code} \subsubsection*{Comparing two images and writing a difference image} \begin{code} A = ImageBuf ("A.tif") B = ImageBuf ("B.tif") compresults = oiio.CompareResults() ImageBufAlgo.compare (A, B, 1.0e-6, 1.0e-6, compresults) if compresults.nfail > 0 : print "Images did not match, writing difference image diff.tif" diff = ImageBuf() ImageBufAlgo.sub (diff, A, B) ImageBufAlgo.abs (diff, diff) image_write (diff, "diff.tif") \end{code} \subsubsection*{Changing the data format or bit depth} \begin{code} img = ImageBuf ("input.exr") # presume that it's a "half" OpenEXR file # write it back out as a "float" file: write_image (img, "output.exr", oiio.FLOAT) \end{code} \subsubsection*{Changing the compression} The following command converts writes a TIFF file, specifically using LZW compression: \begin{code} img = ImageBuf ("in.tif") img.specmod().attribute ("compression", "lzw") write_image (img, "compressed.tif") \end{code} The following command writes its results as a JPEG file at a compression quality of 50 (pretty severe compression): \begin{code} img = ImageBuf ("big.jpg") img.specmod().attribute ("quality", 50) write_image (img, "small.jpg") \end{code} \subsubsection*{Converting between scanline and tiled images} \begin{code} img = ImageBuf ("scan.tif") img.set_write_tiles (16, 16) write_image (img, "tile.tif") img = ImageBuf ("tile.tif") img.set_write_tiles (0, 0) write_image (img, "scan.tif") \end{code} \subsubsection*{Adding captions or metadata} \begin{code} img = ImageBuf ("foo.jpg") # Add a caption: img.specmod().attribute ("ImageDescription", "Hawaii vacation") # Add keywords: img.specmod().attribute ("keywords", "volcano,lava") write_image (img, "foo.jpg") \end{code} \subsubsection*{Changing image boundaries} \noindent Change the origin of the pixel data window: \begin{code} img = ImageBuf ("in.exr") img.specmod().x = 256 img.specmod().y = 80 write_image (img, "offset.exr") \end{code} \noindent Change the display window: \begin{code} img = ImageBuf ("in.exr") img.set_full (16, 1040, 16, 784) write_image (img, "out.exr") \end{code} \noindent Change the display window to match the data window: \begin{code} img = ImageBuf ("in.exr") img.set_full (img.roi()) write_image (img, "out.exr") \end{code} \noindent Cut (trim and extract) a 128x128 region whose upper left corner is at location (900,300), moving the result to the origin (0,0) of the image plane and setting the display window to the new pixel data window: \begin{code} img = ImageBuf ("in.exr") b = ImageBuf () ImageBufAlgo.cut (b, img, oiio.ROI(900,1028,300,428)) write_image (b, "out.exr") \end{code} \subsubsection*{Extract just the named channels from a complicted many-channel image, and add an alpha channel that is 1 everywhere} \begin{code} img = ImageBuf ("allmyaovs.exr") b = ImageBuf () ImageBufAlgo.channels (b, img, ("spec.R", "spec.G", "spec.B", 1.0)) write_image (b, "spec.tif") \end{code} \subsubsection*{Fade 30\% of the way between two images} \begin{code} a = ImageBuf () ImageBufAlgo.mul (a, ImageBuf("A.exr"), 0.7) b = ImageBuf () ImageBufAlgo.mul (b, ImageBuf("B.exr"), 0.3) fade = ImageBuf () ImageBufAlgo.add (fade, a, b) write_image (fade, "fade.exr") \end{code} \subsubsection*{Composite of small foreground over background, with offset} \begin{code} fg = ImageBuf ("fg.exr") fg.specmod().x = 512 fg.specmod().y = 89 bg = ImageBuf ("bg.exr") comp = ImageBuf () ImageBufAlgo.over (comp, fg, bg) write_image (fade, "composite.exr") \end{code} \index{Python|)} \chapwidthend openimageio-1.7.17~dfsg0.orig/src/doc/imageioapi.tex0000644000175000017500000012254513151711064020520 0ustar mfvmfv\chapter{Image I/O API Helper Classes} \label{chap:imageioapi} \index{Image I/O API Helper Classes|(} \section{Data Type Descriptions: {\cf TypeDesc}} \label{sec:dataformats} \label{sec:TypeDesc} \index{data formats} There are two kinds of data that are important to \product: \begin{itemize} \item \emph{Internal data} is in the memory of the computer, used by an application program. \item \emph{Native file data} is what is stored in an image file itself (i.e., on the ``other side'' of the abstraction layer that \product provides). \end{itemize} Both internal and file data is stored in a particular \emph{data format} that describes the numerical encoding of the values. \product understands several types of data encodings, and there is a special class, \TypeDesc, that allows their enumeration and is described in the header file {\cf OpenImageIO/typedesc.h}. A \TypeDesc describes a base data format type, aggregation into simple vector and matrix types, and an array length (if it's an array). The remainder of this section describes the C++ API for \TypeDesc. See Section~\ref{sec:pythontypedesc} for the corresponding Python bindings. \TypeDesc supports the following base data format types, given by the enumerated type {\cf BASETYPE}: \medskip \begin{tabular}{l p{4.75in}} {\cf UINT8} & 8-bit integer values ranging from 0..255, corresponding to the C/C++ {\cf unsigned char}. \\ {\cf INT8} & 8-bit integer values ranging from -128..127, corresponding to the C/C++ {\cf char}. \\ {\cf UINT16} & 16-bit integer values ranging from 0..65535, corresponding to the C/C++ {\cf unsigned short}. \\ {\cf INT16} & 16-bit integer values ranging from -32768..32767, corresponding to the C/C++ {\cf short}. \\ {\cf UINT} & 32-bit integer values, corresponding to the C/C++ {\cf unsigned int}. \\ {\cf INT} & signed 32-bit integer values, corresponding to the C/C++ {\cf int}. \\ {\cf UINT64} & 64-bit integer values, corresponding to the C/C++ {\cf unsigned long long} (on most architectures). \\ {\cf INT64} & signed 64-bit integer values, corresponding to the C/C++ {\cf long long} (on most architectures). \\ {\cf FLOAT} & 32-bit IEEE floating point values, corresponding to the C/C++ {\cf float}. \\ {\cf DOUBLE} & 64-bit IEEE floating point values, corresponding to the C/C++ {\cf double}. \\ {\cf HALF} & 16-bit floating point values in the format supported by OpenEXR and OpenGL. \end{tabular} \medskip \noindent A \TypeDesc can be constructed using just this information, either as a single scalar value, or an array of scalar values: \apiitem{{\ce TypeDesc} (BASETYPE btype) \\ {\ce TypeDesc} (BASETYPE btype, int arraylength)} Construct a type description of a single scalar value of the given base type, or an array of such scalars if an array length is supplied. For example, {\cf TypeDesc(UINT8)} describes an unsigned 8-bit integer, and {\cf TypeDesc(FLOAT,7)} describes an array of 7 32-bit float values. Note also that a non-array \TypeDesc may be implicitly constructed from just the {\cf BASETYPE}, so it's okay to pass a {\cf BASETYPE} to any function parameter that takes a full \TypeDesc. \apiend \medskip \noindent In addition, \TypeDesc supports certain aggregate types, described by the enumerated type {\cf AGGREGATE}: \medskip \begin{tabular}{l p{4.75in}} {\cf SCALAR} & a single scalar value (such as a raw {\cf int} or {\cf float} in C). This is the default. \\ {\cf VEC2} & two values representing a 2D vector. \\ {\cf VEC3} & three values representing a 3D vector. \\ {\cf VEC4} & four values representing a 4D vector. \\ {\cf MATRIX33} & nine values representing a $3 \times 3$ matrix. \\ {\cf MATRIX44} & sixteen values representing a $4 \times 4$ matrix. \end{tabular} \medskip \noindent And optionally, a hint about the semantics of the data, described by the enumerated type {\cf VECSEMANTICS}:\footnote{It's unfortunately called {\cf VECSEMANTICS} because it used to be used strictly for 3-vectors. If we were to do it over again, it would just be {\cf SEMANTICS}.} \medskip \begin{tabular}{p{1in} p{4.25in}} {\cf NOSEMANTICS} & nothing special known. \\ {\cf COLOR} & indicates a vector that is intended to represent a ``color,'' not a spatial quantity (and of course therefore does not undergo a transformation). \\ {\cf POINT} & indicates a vector that represents a spatial position and should be transformed by a $4 \times 4$ matrix as if it had a 4th component of 1. \\ {\cf VECTOR} & indicates a vector that represents a spatial direction and should be transformed by a $4 \times 4$ matrix as if it had a 4th component of 0. \\ {\cf NORMAL} & indicates a vector that represents a surface normal and should be transformed like a vector, but using the inverse-transpose of a $4 \times 4$ matrix. \\ {\cf TIMECODE} & indicates an {\cf int[2]} representing the standard 4-byte encoding of an SMPTE timecode. \\ {\cf KEYCODE} & indicates an {\cf int[7]} representing the standard 28-byte encoding of an SMPTE keycode. \\ \end{tabular} \medskip \noindent These can be combined to fully describe a complex type: \apiitem{{\ce TypeDesc} (BASETYPE btype, AGGREGATE agg, VECSEMANTICS xform=NOSEMANTICS) \\ {\ce TypeDesc} (BASETYPE btype, AGGREGATE agg, int arraylen) \\ {\ce TypeDesc} (BASETYPE btype, AGGREGATE agg, VECSEMANTICS xform, int arraylen) } Construct a type description of an aggregate (or array of aggregates), with optional vector transformation semantics. For example, {\cf TypeDesc(HALF,COLOR)} describes an aggregate of 3 16-bit floats comprising a color, and {\cf TypeDesc(FLOAT,VEC3,POINT)} describes an aggregate of 3 32-bit floats comprising a 3D position. Note that aggregates and arrays are different. A {\cf TypeDesc(FLOAT,3)} is an array of three floats, a {\cf TypeDesc(FLOAT,COLOR)} is a single 3-channel color comprised of floats, and {\cf TypeDesc(FLOAT,3,COLOR)} is an array of 3 color values, each of which is comprised of 3 floats. \apiend \bigskip Of these, the only ones commonly used to store pixel values in image files are scalars of {\cf UINT8}, {\cf UINT16}, {\cf FLOAT}, and {\cf HALF} (the last only used by OpenEXR, to the best of our knowledge). Note that the \TypeDesc (which is also used for applications other than images) can describe many types not used by \product. Please ignore this extra complexity; only the above simple types are understood by \product as pixel storage data types, though a few others, including {\cf STRING} and {\cf MATRIX44} aggregates, are occasionally used for \emph{metadata} for certain image file formats (see Sections~\ref{sec:imageoutput:metadata}, \ref{sec:imageinput:metadata}, and the documentation of individual ImageIO plugins for details). \section{Efficient unique strings: {\cf ustring}} \label{sec:ustring} \indexapi{ustring} A \ustring is an alternative to {\cf char*} or {\cf std::string} for storing strings, in which the character sequence is stored uniquely. If there are many copies of the same \ustring, they all point to the same canonical characters, which themselves are stored only once. This allows \ustring's to be assigned to one another with only the copy of a pointer assignment (not allocation or copying of characters), and for {\cf ==} and {\cf !=} comparisons of two \ustring values for only the cost of comparing the pointers (not examining the characters). \OpenImageIO uses \ustring in a few select places, where string assignment/copy and equality/inequality are the dominant string operation and we want them to be the same low cost as a simple pointer assignment or comparison. (This primarily comes into play for the \ImageCache and \TextureSystem, where we refer to images by their filenames and want very fast lookups.) Please consult the public header {\cf ustring.h} for details, especially if you want to use \ustring extensively in your own code. But here are the most important details to know if you are calling the \OpenImageIO functions that take \ustring parameters: \apiitem{{\ce ustring} (const char *chars) \\ {\ce ustring} (const char *chars, size_t length) \\ {\ce ustring} (const std::string \&str) \\ {\ce ustring} (string\_view sref)} Constructs a \ustring from a C string ({\cf char*}), C++ {\cf std::string}, or a \stringview. \apiend \apiitem{const char* ustring::{\ce c_str} () \\ const std::string\& ustring::{\ce string()} \\ string\_view ustring::{\ce operator string\_view}()} Convert a \ustring to a 0-terminated C string, a C++ {\cf std::string}, or a {\cf string_view}. All of these are extremely inexpensive. \apiend \section{Non-owning string views: {\cf string_view}} \label{sec:stringview} \indexapi{string_view} A \stringview a non-owning, non-copying, non-allocating reference to a sequence of characters. It encapsulates both a character pointer and a length. A function that takes a string input (but does not need to alter the string in place) may use a \stringview parameter and accept input that is any of {\cf char*} (C string), string literal (constant char array), a {\cf std::string} (C++ string), or OIIO \ustring. For all of these cases, no extra allocations are performed, and no extra copies of the string contents are performed (as they would be, for example, if the function took a const {\cf std::string\&} argument but was passed a {\cf char*} or string literal). Furthermore, a function that returns a copy or a substring of one of its inputs (for example, a {\cf substr()}-like function) may return a \stringview rather than a {\cf std::string}, and thus generate its return value without any allocation or copying. Upon assignment to a {\cf std::string} or \ustring, it will properly auto-convert. There are two important caveats to using this class: \begin{enumerate} \item The \stringview merely refers to characters owned by another string, so the \stringview may not be used outside the lifetime of the string it refers to. Thus, \stringview is great for parameter passing, but it's not a good idea to use a \stringview to store strings in a data structure (unless you are really sure you know what you're doing). \item Because the run of characters that the \stringview refers to may not be 0-terminated, it is important to distinguish between the {\cf data()} method, which returns the pointer to the characters, and the {\cf c_str()} method, which is guaranteed to return a valid C string that is 0-terminated. Thus, if you want to pass the contents of a \stringview to a function that expects a 0-terminated string (say, fopen), you must call {\cf fopen(my_string_view.c_str()}). Note that the usual case is that the \stringview does refer to a 0-terminated string, and in that case c_str() returns the same thing as data() without any extra expense; but in the rare case that it is not 0-terminated, c_str() will incur extra expense to internally allocate a valid C string. \end{enumerate} \apiitem{{\ce string_view} (const char *chars) \\ {\ce string_view} (const char *chars, size_t length) \\ {\ce string_view} (const std::string \&str) \\ {\ce string_view} (ustring ustr)} Constructs a \stringview. The \stringview doesn't have its own copy of the characters, so don't use the \stringview after the original string has been destroyed or altered. Note that the version that takes a {\cf const char*} but not a length will automatically take the {\cf strlen(chars)} to determine the length. (All the other constructors can deduce the length without walking through all of the characters.) \apiend \apiitem{const char* string_view::{\ce data} () \\ size_t string_view::{\ce size} ()} The raw pointer to the characters (not necessarily 0-terminated!) and the length of the \stringview. \apiend \apiitem{const char* string_view::{\ce c_str} ()} Return a 0-terminated {\cf char*} version of the \stringview (a proper C string). \apiend \apiitem{std::string (string_view sr) \\ ustring (string_view sr)} Automatic constructions of C++ {\cf std::string} or OIIO \ustring from a \stringview. \apiend \smallskip \noindent Additionally, a large portion of the usual API for {\cf std::string} is mimicked by \stringview. Please consult the public {\cf string_view.h} header file for full details, if you wish to use \stringview in your own code. \section{Non-owning array views: {\cf array_view}} \label{sec:arrayview} \indexapi{array_view} A {\cf template array\_view} is a non-owning, non-copying, non- allocating reference to an array. It encapsulates both a pointer and a length, and thus is a safer way of passing pointers around (because the function called knows how long the array is). A function that might ordinarily take a {\cf T*} and a length could instead just take an {\cf array\_view}. An \arrayview may be initialized explicitly from a pointer and length, by initializing with a {\cf std::vector}, or by initalizing with a constant (treated as an array of length 1). For all of these cases, no extra allocations are performed, and no extra copies of the array contents are made. Important caveat: The \arrayview merely refers to items owned by another array, so the \arrayview should not be used outside the lifetime of the array it refers to. Thus, \arrayview is great for parameter passing, but it's not a good idea to use a \arrayview to store strings in a data structure (unless you are really sure you know what you're doing). \noindent Commonly used \arrayview methods include: \apiitem{{\ce array_view} (T *data, size_t len) \\ {\ce array_view} (T \&data) \\ {\ce array_view} (T *begin, T *end) \\ {\ce array_view} (std::vector \&vec)} Constructs a \arrayview. The \arrayview doesn't have its own copy of the array elements, so don't use the \arrayview after the original array has been destroyed or altered. \apiend \apiitem{T* array_view::{\ce data} () \\ size_t array_view::{\ce size} ()} The raw pointer to the array, and its length. \apiend \apiitem{T\& array_view::operator{\ce []} (size_t pos)} References a single element of the \arrayview. \apiend \smallskip \noindent Please consult the public {\cf array_view.h} header file for full details, if you wish to use \arrayview in your own code. \section{Image Specification: {\cf ImageSpec}} \label{sec:ImageSpec} \indexapi{ImageSpec} An \ImageSpec is a structure that describes the complete format specification of a single image. It contains: \begin{itemize} \item The image resolution (number of pixels) and origin. This specifies what is often called the ``pixel data window.'' \item The full size and offset of an abstract ``full'' or ``display'' window. Differing full and data windows can indicate that the pixels are a crop region or a larger image, or contain overscan pixels. \item Whether the image is organized into \emph{tiles}, and if so, the tile size. \item The \emph{native data format} of the pixel values (e.g., float, 8-bit integer, etc.). \item The number of color channels in the image (e.g., 3 for RGB images), names of the channels, and whether any particular channels represent \emph{alpha} and \emph{depth}. \item A user-extensible (and format-extensible) list of any other arbitrarily-named and -typed data that may help describe the image or its disk representation. \end{itemize} The remainder of this section describes the C++ API for \ImageSpec. See Section~\ref{sec:pythonimagespec} for the corresponding Python bindings. \subsection{\ImageSpec Data Members} The \ImageSpec contains data fields for the values that are required to describe nearly any image, and an extensible list of arbitrary attributes that can hold metadata that may be user-defined or specific to individual file formats. Here are the hard-coded data fields: \apiitem{int width, height, depth \\ int x, y, z} {\cf width, height, depth} are the size of the data of this image, i.e., the number of pixels in each dimension. A {\cf depth} greater than 1 indicates a 3D ``volumetric'' image. {\cf x, y, z} indicate the \emph{origin} of the pixel data of the image. These default to (0,0,0), but setting them differently may indicate that this image is offset from the usual origin. Therefore the pixel data are defined over pixel coordinates [{\cf x} ... {\cf x+width-1}] horizontally, [{\cf y} ... {\cf y+height-1}] vertically, and [{\cf z} ... {\cf z+depth-1}] in depth. \apiend \apiitem{int full_width, full_height, full_depth \\ int full_x, full_y, full_z} These fields define a ``full'' or ``display'' image window over the region [{\cf full_x} ... {\cf full_x+full_width-1}] horizontally, [{\cf full_y} ... {\cf full_y+full_height-1}] vertically, and [{\cf full_z} ... {\cf full_z+full_depth-1}] in depth. Having the full display window different from the pixel data window can be helpful in cases where you want to indicate that your image is a \emph{crop window} of a larger image (if the pixel data window is a subset of the full display window), or that the pixels include \emph{overscan} (if the pixel data is a superset of the full display window), or may simply indicate how different non-overlapping images piece together. \apiend \apiitem{int tile_width, tile_height, tile_depth} If nonzero, indicates that the image is stored on disk organized into rectangular \emph{tiles} of the given dimension. The default of (0,0,0) indicates that the image is stored in scanline order, rather than as tiles. \apiend \apiitem{int nchannels} The number of \emph{channels} (color values) present in each pixel of the image. For example, an RGB image has 3 channels. \apiend %\newpage %\vspace{24pt} % make some space \apiitem{TypeDesc format \\[0.5ex] std::vector channelformats} Describes the native format of the pixel data values themselves, as a \TypeDesc (see \ref{sec:TypeDesc}). Typical values would be {\cf TypeDesc::UINT8} for 8-bit unsigned values, {\cf TypeDesc::FLOAT} for 32-bit floating-point values, etc. If all channels of the image have the same data format, that will be described by {\cf format} and {\cf channelformats} will be empty (zero length). If there are different data formats for each channel, they will be described in the {\cf channelformats} vector, and the {\cf format} field will indicate a single default data format for applications that don't wish to support per-channel formats (usually this will be the format of the channel that has the most precision). \apiend \apiitem{std::vector channelnames} The names of each channel, in order. Typically this will be \qkw{R}, \qkw{G},\qkw{B}, \qkw{A} (alpha), \qkw{Z} (depth), or other arbitrary names. \apiend \apiitem{int alpha_channel} The index of the channel that represents \emph{alpha} (pixel coverage and/or transparency). It defaults to -1 if no alpha channel is present, or if it is not known which channel represents alpha. \apiend \apiitem{int z_channel} The index of the channel that respresents \emph{z} or \emph{depth} (from the camera). It defaults to -1 if no depth channel is present, or if it is not know which channel represents depth. \apiend \apiitem{bool deep} If {\cf true}, this indicates that the image describes contains ``deep'' data consisting of multiple samples per pixel. If {\cf false}, it's an ordinary image with one data value (per channel) per pixel. \apiend \apiitem{ParamValueList extra_attribs} A list of arbitrarily-named and arbitrarily-typed additional attributes of the image, for any metadata not described by the hard-coded fields described above. This list may be manipulated with the {\cf attribute()} and {\cf find_attribute()} methods. \apiend \subsection{\ImageSpec member functions} \label{sec:ImageSpecMemberFuncs} \noindent \ImageSpec contains the following methods that manipulate format specs or compute useful information about images given their format spec: \apiitem{{\ce ImageSpec} (int xres, int yres, int nchans, TypeDesc fmt = UINT8)} Constructs an \ImageSpec with the given $x$ and $y$ resolution, number of channels, and pixel data format. All other fields are set to the obvious defaults -- the image is an ordinary 2D image (not a volume), the image is not offset or a crop of a bigger image, the image is scanline-oriented (not tiled), channel names are ``R'', ``G'', ``B,'' and ``A'' (up to and including 4 channels, beyond that they are named ``channel \emph{n}''), the fourth channel (if it exists) is assumed to be alpha, and values are assumed to be linear. \apiend \apiitem{void {\ce set_format} (TypeDesc fmt)} Sets the format as described, and clears any per-channel format information in {\cf channelformats}. \apiend \apiitem{void {\ce default_channel_names} ()} Sets the {\cf channelnames} to reasonable defaults for the number of channels. Specifically, channel names are set to ``R'', ``G'', ``B,'' and ``A'' (up to and including 4 channels, beyond that they are named ``channel\emph{n}''. \apiend \apiitem{size_t {\ce channel_bytes} () const} Returns the number of bytes comprising each channel of each pixel (i.e., the size of a single value of the type described by the {\cf format} field). \apiend \apiitem{size_t {\ce channel_bytes} (int chan, bool native=false) const} Returns the number of bytes needed for the single specified channel. If native is {\cf false} (default), compute the size of one channel of {\cf this->format}, but if native is {\cf true}, compute the size of the channel in terms of the ``native'' data format of that channel as stored in the file. \apiend \apiitem{size_t {\ce pixel_bytes} (bool native=false) const} Returns the number of bytes comprising each pixel (i.e. the number of channels multiplied by the channel size). If {\cf native} is true, this will be the sum of all the per-channel formats in {\cf channelformats}. If {\cf native} is false (the default), or if all channels use the same format, this will simply be the number of channels multiplied by the width (in bytes) of the {\cf format}. \apiend \apiitem{size_t {\ce pixel_bytes} (int chbegin, int chend, bool native=false) const} Returns the number of bytes comprising the range of channels $[${\cf chbegin}, {\cf chend}$)$ for each pixel. If {\cf native} is true, this will be the sum of the per-channel formats in {\cf channelformats} (for the given range of channels). If {\cf native} is false (the default), or if all channels use the same format, this will simply be the number of channels multiplied by the width (in bytes) of the {\cf format}. \apiend \apiitem{imagesize_t {\ce scanline_bytes} (bool native=false) const} Returns the number of bytes comprising each scanline, i.e., \\ {\cf pixel_bytes(native) * width} \\ This will return {\cf std::numeric_limits::max()} in the event of an overflow where it's not representable in an {\cf imagesize_t}. \apiend \apiitem{imagesize_t {\ce tile_pixels} () const} Returns the number of tiles comprising an image tile (if it's a tiled image). This will return {\cf std::numeric_limits::max()} in the event of an overflow where it's not representable in an {\cf imagesize_t}. \apiend \apiitem{imagesize_t {\ce tile_bytes} (bool native=false) const} Returns the number of bytes comprising an image tile (if it's a tiled image), i.e., \\ {\cf pixel_bytes(native) * tile_width * tile_height * tile_depth } \\ This will return {\cf std::numeric_limits::max()} in the event of an overflow where it's not representable in an {\cf imagesize_t}. \apiend \apiitem{imagesize_t {\ce image_pixels} () const} Returns the number of pixels comprising an entire image image of these dimensions. This will return {\cf std::numeric_limits::max()} in the event of an overflow where it's not representable in an {\cf imagesize_t}. \apiend \apiitem{imagesize_t {\ce image_bytes} (bool native=false) const} Returns the number of bytes comprising an entire image of these dimensions, i.e., \\ {\cf pixel_bytes(native) * width * height * depth } \\ This will return {\cf std::numeric_limits::max()} in the event of an overflow where it's not representable in an {\cf imagesize_t}. \apiend \apiitem{bool {\ce size_t_safe} () const} Return {\cf true} if an image described by this spec can the sizes (in pixels or bytes) of its scanlines, tiles, and the entire image can be represented by a {\cf size_t} on that platform. If this returns {\cf false}, the client application should be very careful allocating storage! \apiend % FIXME - document auto_stride() ? \apiitem{void {\ce attribute} (string_view name, TypeDesc type, \\ \bigspc const void *value)} Add a metadata attribute to {\cf extra_attribs}, with the given name and data type. The {\cf value} pointer specifies the address of the data to be copied. \apiend \apiitem{void {\ce attribute} (string_view name, unsigned int value)\\ void {\ce attribute} (string_view name, int value)\\ void {\ce attribute} (string_view name, float value)\\ void {\ce attribute} (string_view name, string_view value)} Shortcuts for passing attributes comprised of a single integer, floating-point value, or string. \apiend \apiitem{void {\ce erase_attribute} (string_view name,\\ \bigspc\bigspc TypeDesc searchtype=UNKNOWN,\\ \bigspc\bigspc bool casesensitive=false)} Searches {\cf extra_attribs} for an attribute matching {\cf name}, and if it exists, remove it entirely from {\cf extra_attribs}. If {\cf searchtype} is anything other than {\cf TypeDesc::UNKNOWN}, matches will be restricted only to attributes with the given type. The name comparison will be exact if {\cf casesensitive} is true, otherwise in a case-insensitive manner if {\cf caseinsensitive} is false. \apiend \apiitem{ImageIOParameter * {\ce find_attribute} (string_view name,\\ \bigspc\bigspc\bigspc TypeDesc searchtype=UNKNOWN,\\ \bigspc\bigspc\bigspc bool casesensitive=false)\\ const ImageIOParameter * {\ce find_attribute} (string_view name,\\ \bigspc\bigspc\bigspc\spc TypeDesc searchtype=UNKNOWN,\\ \bigspc\bigspc\bigspc\spc bool casesensitive=false) const} Searches {\cf extra_attribs} for an attribute matching {\cf name}, returning a pointer to the attribute record, or NULL if there was no match. If {\cf searchtype} is anything other than {\cf TypeDesc::UNKNOWN}, matches will be restricted only to attributes with the given type. The name comparison will be exact if {\cf casesensitive} is true, otherwise in a case-insensitive manner if {\cf caseinsensitive} is false. \apiend \apiitem{const ImageIOParameter * {\ce find_attribute} (string_view name,\\ \bigspc\bigspc\bigspc\spc ImageIOParameter \&tmpparam,\\ \bigspc\bigspc\bigspc\spc TypeDesc searchtype=UNKNOWN,\\ \bigspc\bigspc\bigspc\spc bool casesensitive=false) const} Search for the named attribute and return the pointer to its {\cf ImageIOParameter} record, or {\cf NULL} if not found. This variety of {\cf find_attribute(}) can retrieve items such as \qkw{width}, which are part of the \ImageSpec, but not in {\cf extra_attribs}. The {\cf tmpparam} is a temporary storage area owned by the caller, which is used as temporary buffer in cases where the information does not correspond to an actual {\cf extra_attribs} (in this case, the return value will be {\cf \&tmpparam}). \apiend \apiitem{int {\ce get_int_attribute} (string_view name, int defaultval=0) const} Gets an integer metadata attribute (silently converting to {\cf int} even if if the data is really int8, uint8, int16, uint16, or uint32), and simply substituting the supplied default value if no such metadata exists. This is a convenience function for when you know you are just looking for a simple integer value. \apiend \apiitem{float {\ce get_float_attribute} (string_view name, float defaultval=0) const} Gets a float metadata attribute (silently converting to {\cf float} even if the data is really half or double), simply substituting the supplied default value if no such metadata exists. This is a convenience function for when you know you are just looking for a simple float value. \apiend \apiitem{string_view {\ce get_string_attribute} (string_view name, \\ \bigspc\bigspc\bigspc string_view defaultval = "") const} Gets a string metadata attribute, simply substituting the supplied default value if no such metadata exists. This is a convenience function for when you know you are just looking for a simple string value. \apiend \apiitem{static std::string {\ce metadata_val} (const ImageIOParamaeter \&p, bool human=true) const} For a given parameter {\cf p}, format the value nicely as a string. If {\cf human} is true, use especially human-readable explanations (units, or decoding of values) for certain known metadata. \apiend \apiitem{std::string {\ce to_xml} () const} Saves the contents of the \ImageSpec as XML, returning it as a string. \apiend \apiitem{void {\ce from_xml} (const char *xml) const} Populates the fields of the \ImageSpec based on the XML passed in. \apiend \apiitem{TypeDesc {\ce channelformat} (int chan) const} Returns a \TypeDesc describing the format of the requested channel. \apiend \apiitem{void {\ce get_channelformats} (std::vector \&formats) const} Fill in an array of channel formats describing all channels in the image. (Note that this differs slightly from the member data {\cf channelformats}, which is empty if there are not separate per-channel formats.) \apiend \section{``Deep'' pixel data: {\cf DeepData}} \label{sec:deepdata} \indexapi{DeepData} A \DeepData holds the contents of an image of ``deep'' pixels (multiple depth samples per pixel). \noindent Commonly used \DeepData fields and methods include: \apiitem{void {\ce init} (const ImageSpec \&spec)} Initialize the \DeepData based on the \ImageSpec's total number of pixels, number and types of channels. At this stage, all pixels are assumed to have 0 samples, and no sample data is allocated. \apiend \apiitem {void {\ce init} (int npix, int nchan,\\ \bigspc array_view channeltypes),\\ \bigspc array_view channelnames)} Initialize the \DeepData with a number of pixels, channels, channel types, and channel names. \apiend \apiitem{void {\ce clear} ()} Reset the \DeepData to be equivalent to its empty initial state. \apiend \apiitem{void {\ce free} ()} Not only {\cf clear()}, but also ensure that all allocated memory has been truly freed. \apiend \apiitem{int {\ce npixels} () const} Retrieve the total number of pixels. \apiend \apiitem{int {\ce nchannels} () const} Retrieve the number of channels. \apiend \apiitem{string_view {\ce channelname} (int c) const} \NEW % 1.7 Retrieve the name of channel {\cf c}. \apiend \apiitem{TypeDesc {\ce channeltype} (int c) const} Retrieve the data type of channel {\cf c}. \apiend \apiitem{size_t {\ce channelsize} (int c) const} Retrieve the size (in bytes) of one datum of channel {\cf c}. \apiend \apiitem{size_t {\ce samplesize} () const} Retrieve the packed size (in bytes) of all channels of one sample. \apiend \apiitem{int {\ce samples} (int pixel) const} Retrieve the number of samples for the given pixel index. \apiend \apiitem{void {\ce set_samples} (int pixel, int samps)} Set the number of samples for the given pixel index. \apiend \apiitem{void {\ce insert_samples} (int pixel, int samplepos, int n=1)} \NEW % 1.7 Insert {\cf n} samples of the specified pixel, betinning at the sample position index. After insertion, the new samples will have uninitialized values. \apiend \apiitem{void {\ce erase_samples} (int pixel, int samplepos, int n=1)} \NEW % 1.7 Remove {\cf n} samples of the specified pixel, betinning at the sample position index. \apiend \apiitem{float {\ce deep_value} (int pixel, int channel, int sample) const \\ uint32_t {\ce deep_value_uint} (int pixel, int channel, int sample) const} Retrieve the value of the given pixel, channel, and sample indices, for floating point or unsigned integer channel types, respectively. \apiend \apiitem{void {\ce set_deep_value} (int pixel, int channel, int sample, float value) \\ void {\ce set_deep_value} (int pixel, int channel, int sample, uint32_t value)} Set the value of the given pixel, channel, and sample indices, for floating point or unsigned integer channel types, respectively. \apiend \apiitem{bool {\ce copy_deep_sample} (int pixel, int sample, \\ \bigspc const DeepData \&src, int srcpixel, int srcsample)} \NEW % 1.7 Copy a deep sample from {\cf src} to this \DeepData. They must have the same channel layout. Return {\cf true} if ok, {\cf false} if the operation could not be performed. \apiend \apiitem{bool {\ce copy_deep_pixel} (int pixel, const DeepData \&src, int srcpixel)} \NEW % 1.7 Copy an entire deep pixel from {\cf src} to this \DeepData, completely replacing any pixel data for that pixel. They must have the same channel layout. Return {\cf true} if ok, {\cf false} if the operation could not be performed. \apiend \apiitem{void {\ce split} (int pixel, float depth)} \NEW % 1.7 Split any samples of the pixel that cross {\cf depth}. \apiend \apiitem{void {\ce sort} (int pixel)} \NEW % 1.7 Sort the samples of the pixel by their Z depth. \apiend \apiitem{void {\ce merge_overlaps} (int pixel)} \NEW % 1.7 Merge any adjacent samples in the pixel that exactly overlap in $z$ range. This is only useful if the pixel has previously been split at all sample starts and ends, and sorted by depth. Note that this may change the number of samples in the pixel. \apiend \apiitem{bool {\ce merge_deep_pixels} (int pixel, const DeepData \&src, int srcpixel)} \NEW % 1.7 Merge the samples of {\cf src}'s pixel into this \DeepData's pixel. Return {\cf true} if ok, {\cf false} if the operation could not be performed. \apiend \apiitem{bool {\ce occlusion_cull} (int pixel)} \NEW % 1.7 Eliminate any samples beyond an opaque sample. \apiend \section{Miscellaneous Utilities} \label{sec:miscapi} These helper functions are not part of any other \OpenImageIO class, they just exist in the {\cf OpenImageIO} namespace as general utilities. (See Section~\ref{sec:pythonmiscapi} for the corresponding Python bindings.) \apiitem{int {\ce openimageio_version} ()} \index{version} \indexapi{openimageio_version} Returns a numeric value for the version of \product, 10000 for each major version, 100 for each minor version, 1 for each patch. For example, \product 1.2.3 would return a value of 10203. \apiend \apiitem{std::string {\ce geterror} ()} \index{error checking} \indexapi{geterror} Returns any error string describing what went wrong if {\cf ImageInput::create()} or \\ {\cf ImageOutput::create()} failed (since in such cases, the \ImageInput or \ImageOutput itself does not exist to have its own {\cf geterror()} function called). This function returns the last error for this particular thread; separate threads will not clobber each other's global error messages. \apiend \apiitem{bool {\ce attribute} (string_view name, TypeDesc type, const void *val)} \index{globalattribute} \label{sec:globalattribute} \indexapi{attribute} Sets an global attribute (i.e., a property or option) of \product. The {\cf name} designates the name of the attribute, {\cf type} describes the type of data, and {\cf val} is a pointer to memory containing the new value for the attribute. If the name is known, valid attribute that matches the type specified, the attribute will be set to the new value and {\cf attribute()} will return {\cf true}. If {\cf name} is not recognized, or if the types do not match (e.g., {\cf type} is {\cf TypeDesc::TypeFloat} but the named attribute is a string), the attribute will not be modified, and {\cf attribute()} will return {\cf false}. \noindent The following are the recognized attributes: \apiitem{int threads} \vspace{10pt} \index{threads} \label{sec:attribute:threads} Some \product operations can be accelerated if allowed to spawn multiple threads to parallelize the task. (Examples: simultaneous format conversions of multiple scanlines read together, or many \ImageBufAlgo operations.) This attribute sets the default number of threads that will be spawned for these operations (the ``fan out''). The default is 0, which means that it should spawn as many threads as there are hardware cores present on the system. Situations where the main application logic is essentially single threaded (i.e., one top-level call into OIIO at a time) should leave this at the default value, or some reasonable number of cores, thus allowing lots of threads to fill the cores when OIIO has big tasks to complete. But situations where you have many threads at the application level, each of which is expected to be making separate OIIO calls simultaneously, should set this to 1, thus having each calling thread do its own work inside of OIIO rather than spawning new threads with a high overall ``fan out.'' \apiend \apiitem{int exr_threads} \vspace{10pt} \index{exr_threads} Sets the internal OpenEXR thread pool size. The default is to use as many threads as the amount of hardware concurrency detected. Note that this is separate from the OIIO \qkw{threads} attribute. \apiend \apiitem{string plugin_searchpath} \vspace{10pt} \index{plugin_searchpath} A colon-separated list of directories to search for dynamically-loaded format plugins. \apiend \apiitem{string format_list} \vspace{10pt} \index{format_list} A comma-separated list of all the names of all supported image format readers and writers. (Note: can only be retrieved by {\cf getattribute()}, cannot be set by {\cf attribute()}.) \apiend \apiitem{string extension_list} \vspace{10pt} \index{extension_list} For each format, the format name, followed by a colon, followed by a comma-separated list of all extensions that are presumed to be used for that format. Semicolons separate the lists for formats. For example, \begin{code} "tiff:tif;jpeg:jpg,jpeg;openexr:exr" \end{code} (Note: can only be retrieved by {\cf getattribute()}, cannot be set by {\cf attribute()}.) \apiend \apiitem{string library_list} \vspace{10pt} \index{library_list} \NEW % 1.7 For each format that uses a dependent library, the format name, followed by a colon, followed by the name and version of the dependency. Semicolons separate the lists for formats. For example, \begin{code} "tiff:LIBTIFF 4.0.4;gif:gif_lib 4.2.3;openexr:OpenEXR 2.2.0" \end{code} (Note: can only be retrieved by {\cf getattribute()}, cannot be set by {\cf attribute()}.) \apiend \apiitem{int read_chunk} \vspace{10pt} \index{read_chunk} When performing a {\cf read_image()}, this is the number of scanlines it will attempt to read at a time (some formats are more efficient when reading and decoding multiple scanlines). The default is 256. The special value of 0 indicates that it should try to read the whole image if possible. \apiend \apiend \apiitem{bool {\ce attribute} (string_view name, int val) \\ bool {\ce attribute} (string_view name, float val) \\ bool {\ce attribute} (string_view name, const char *val) \\ bool {\ce attribute} (string_view name, const std::string \& val)} Specialized versions of {\cf attribute()} in which the data type is implied by the type of the argument. \apiend \apiitem{bool {\ce getattribute} (string_view name, TypeDesc type, void *val)} \indexapi{getattribute} Gets the current value of a global attribute. The {\cf name} designates the name of the attribute, {\cf type} describes the type of data, and {\cf val} is a pointer to memory where the user would like the value placed. If the attribute name is valid and matches the type specified, the attribute value will be stored at address {\cf val} and {\cf attribute()} will return {\cf true}. If {\cf name} is not recognized as a valid attribute name, or if the types do not match (e.g., {\cf type} is {\cf TypeDesc::TypeFloat} but the named attribute is a string), no data will be written to {\cf val}, and {\cf attribute()} will return {\cf false}. The complete list of attributes can be found above, in the description of the {\cf attribute()} function. \apiend \apiitem{bool {\ce getattribute} (string_view name, int \&val) \\ bool {\ce getattribute} (string_view name, float \&val) \\ bool {\ce getattribute} (string_view name, char **val) \\ bool {\ce getattribute} (string_view name, std::string \& val)} Specialized versions of {\cf getattribute()} in which the data type is implied by the type of the argument. For example, the following are equivalent to the example above for the general (pointer) form of {\cf getattribute()}: \begin{code} int threads; OIIO::getattribute ("threads", &threads); std::string path; OIIO::getattribute ("plugin_searchpath", &path); \end{code} \apiend \apiitem{int {\ce get_int_attribute} (string_view name, int defaultvalue=0) \\ float {\ce get_float_attribute} (string_view name, float defaultvalue=0) \\ string_view {\ce get_string_attribute} (string_view name, \\ \bigspc\bigspc\bigspc string_view defaultvalue="")} \indexapi{get_int_attribute} \indexapi{get_float_attribute} \indexapi{get_string_attribute} \NEW % 1.7 Specialized versions of {\cf getattribute()} for common types, in which the data is returned directly, and a supplied default value is returned if the attribute was not found. For example, the following are equivalent to the example above for the general (pointer) form of {\cf getattribute()}: \begin{code} int threads = OIIO::getattribute ("threads", 0); string_view path = OIIO::getattribute ("plugin_searchpath"); \end{code} \apiend \apiitem{void {\ce declare_imageio_format} (const std::string \&format_name,\\ \bigspc\bigspc\spc ImageInput::Creator input_creator, \\ \bigspc\bigspc\spc const char **input_extensions,\\ \bigspc\bigspc\spc ImageOutput::Creator output_creator, \\ \bigspc\bigspc\spc const char **output_extensions, \\ \bigspc\bigspc\spc const char *lib_version)} Register the input and output `create' routines and list of file extensions for a particular format. \apiend \index{Image I/O API Helper Classes|)} \chapwidthend openimageio-1.7.17~dfsg0.orig/src/doc/oiiotool.tex0000644000175000017500000032640413151711064020251 0ustar mfvmfv\chapter{{\kw oiiotool}: the OIIO Swiss Army Knife} \label{chap:oiiotool} \indexapi{oiiotool} \section{Overview} The \oiiotool program will read images (from any file format for which an \ImageInput plugin can be found), perform various operations on them, and write images (in any format for which an \ImageOutput plugin can be found). The \oiiotool utility is invoked as follows: \medskip \hspace{0.25in} \oiiotool \emph{args} \medskip \oiiotool maintains an \emph{image stack}, with the top image in the stack also called the \emph{current image}. The stack begins containing no images. \oiiotool arguments consist of image names, or commands. When an image name is encountered, that image is pushed on the stack and becomes the new \emph{current image}. Most other commands either alter the current image (replacing it with the alteration), or in some cases will pull more than one image off the stack (such as the current image and the next item on the stack) and then push a new image. \subsubsection*{Argument order matters!} \oiiotool processes operations \emph{in order}. Thus, the order of operations on the command line is extremely important. For example, \begin{code} oiiotool in.tif -resize 640x480 -o out.tif \end{code} \noindent has the effect of reading \qkw{in.tif} (thus making it the \emph{current image}), resizing it (as the current image, and making the resized version the new current image), and then writing the new current image to the file \qkw{out.tif}. Contrast that with the following subtly-incorrect command: \begin{code} oiiotool in.tif -o out.tif -resize 640x480 \end{code} \noindent has the effect of reading \qkw{in.tif} (thus making it the \emph{current image}), saving the current image to the file \qkw{out.tif} (note that it will be an exact copy of \qkw{in.tif}), resizing the current image, and then... exiting. Thus, the resized image is never saved, and \qkw{out.tif} will be an unaltered copy of \qkw{in.tif}. \subsubsection*{Optional arguments} \label{sec:oiiotooloptionalargs} Some commands stand completely on their own (like {\cf --flip}), others take one or more arguments (like {\cf --resize} or {\cf -o}): \smallskip \hspace{0.25in} {\cf oiiotool foo.jpg --flip --resize 640x480 -o out.tif} \smallskip A few commands take optional modifiers for options that are so rarely-used or confusing that they should not be required arguments. In these cases, they are appended to the command name, after a colon (``{\cf :}''), and with a \emph{name}{\cf =}\emph{value} format. As an example: \smallskip \hspace{0.25in} {\cf oiiotool --capture:camera=1 -o out.tif} \smallskip \subsubsection*{Frame sequences} \index{frame sequences}\index{wildcard} \index{numeric frame sequence wildcards} It is also possible to have \oiiotool operate on numbered sequences of images. In effect, this will execute the \oiiotool command several times, making substitutions to the sequence arguments in turn. Image sequences are specified by having filename arguments to oiiotool use either a numeric range wildcard (designated such as ``{\cf 1-10\#}'' or a {\cf printf}-like notation ``{\cf 1-10\%d}''), or spelling out a more complex pattern with {\cf --frames}. For example: \begin{code} oiiotool big.1-3#.tif --resize 100x100 -o small.1-3#.tif oiiotool big.1-3%04d.tif --resize 100x100 -o small.1-3%04d.tif oiiotool --frames 1-3 big.#.tif --resize 100x100 -o small.#.tif oiiotool --frames 1-3 big.%04d.tif --resize 100x100 -o small.%04d.tif \end{code} \noindent Any of those will be the equivalent of having issued the following sequence of commands: \begin{code} oiiotool big.0001.tif --resize 100x100 -o small.0001.tif oiiotool big.0002.tif --resize 100x100 -o small.0002.tif oiiotool big.0003.tif --resize 100x100 -o small.0003.tif \end{code} The frame range may be forwards ({\cf 1-5}) or backwards ({\cf 5-1}), and may give a step size to skip frames ({\cf 1-5x2} means 1, 3, 5) or take the complement of the step size set ({\cf 1-5y2} means 2, 4) and may combine subsequences with a comma. If you are using the {\cf \#} or {\cf @} wildcards, then the wildcard characters themselves specify how many digits to pad with leading zeroes, with {\cf \#} indicating 4 digits and {\cf @} indicating one digit (these may be combined: {\cf \#@@} means 6 digits). An optional {\cf --framepadding} can also be used to override the number of padding digits. For example, \begin{code} oiiotool --framepadding 3 --frames 3,4,10-20x2 blah.#.tif \end{code} \noindent would match {\cf blah.003.tif}, {\cf blah.004.tif}, {\cf blah.010.tif}, {\cf blah.012.tif}, {\cf blah.014.tif}, {\cf blah.016.tif}, {\cf blah.018.tif}, {\cf blah.020.tif}. Alternately, you can use the {\cf printf} notation, such as \begin{code} oiiotool --frames 3,4,10-20x2 blah.%03d.tif \end{code} Two special command line arguments can be used to disable numeric wildcard expansion: {\cf --wildcardoff} disables numeric wildcard expansion for subsequent command line arguments, until {\cf --wildcardon} re-enables it for subsequent command line arguments. Turning wildcard expansion off for selected arguments can be helpful if you have arguments that must contain the wildcard characters themselves. For example: \begin{code} oiiotool input.@@@.tif --wildcardoff --sattrib Caption "lg@openimageio.org" \ --wildcardon -o output.@@@.tif \end{code} \noindent In this example, the `{\cf @}' characters in the filenames should be expanded into numeric file sequence wildcards, but the `{\cf @}' in the caption (denoting an email address) should not. \subsubsection*{Stereo wildcards} \oiiotool can also handle image sequences with separate left and right images per frame using {\cf views}. The {\cf \%V} wildcard will match the full name of all views and {\cf \%v} will match the first character of each view. View names default to ``left'' and ``right'', but may be overridden using the {\cf --views} option. For example, \begin{code} oiiotool --frames 1-5 blah_%V.#.tif \end{code} \noindent would match {\cf blah_left.0001.tif}, {\cf blah_right.0001.tif}, {\cf blah_left.0002.tif}, {\cf blah_right.0002.tif}, {\cf blah_left.0003.tif}, {\cf blah_right.0003.tif}, {\cf blah_left.0004.tif}, {\cf blah_right.0004.tif}, {\cf blah_left.0005.tif}, {\cf blah_right.0005.tif}, and \begin{code} oiiotool --frames 1-5 blah_%v.#.tif \end{code} \noindent would match {\cf blah_l.0001.tif}, {\cf blah_r.0001.tif}, {\cf blah_l.0002.tif}, {\cf blah_r.0002.tif}, {\cf blah_l.0003.tif}, {\cf blah_r.0003.tif}, {\cf blah_l.0004.tif}, {\cf blah_r.0004.tif}, {\cf blah_l.0005.tif}, {\cf blah_r.0005.tif}, but \begin{code} oiiotool --views left --frames 1-5 blah_%v.#.tif \end{code} \noindent would only match {\cf blah_l.0001.tif}, {\cf blah_l.0002.tif}, {\cf blah_l.0003.tif}, {\cf blah_l.0004.tif}, {\cf blah_l.0005.tif}. \subsubsection*{Expression evaluation and substitution} \label{oiiotoolexpr} \oiiotool can perform \emph{expression evaluation and substitution} on command-line arguments. As command-line arguments are needed, they are scanned for containing braces {\cf \{ \}}. If found, the braces and any text they enclose will be evaluated as an expression and replaced by its result. The contents of an expression may be any of: \begin{description} \item[{\emph{number}}] A numerical value (e.g., {\cf 1} or {\cf 3.14159}). \item[{\emph{imagename.metadata}}] The named metadata of an image. The \emph{imagename} may be one of: {\cf TOP} (the top or current image), {\cf IMG[}\emph{i}{\cf ]} describing the \emph{i}{\textsuperscript{th}} image on the stack (thus {\cf TOP} is a synonym for {\cf IMG[0]} , the next image on the stack is {\cf IMG[1]}, etc.), or {\cf IMG["}\emph{name}{\cf "]} to denote an image named by filename or by label name. The \emph{metadata} may be the name of any standard metadata of the specified image (e.g., \qkw{ImageDescription}, or \qkw{width}), or one of the following special names: \qkw{filename}, \qkw{file_extension}, \qkw{file_noextension}, \qkw{geom} (giving the pixel data size in the form \qkw{640x480+0+0}), or \qkw{full_geom} (similar, for the ``full'' or ``display'' size). \item[{Arithmetic}] Sub-expressions may be joined by {\cf +}, {\cf -}, {\cf *}, or {\cf /} for arithmetic operations. Parentheses are supported, and standard operator precedence applies. \item[{Special variables}] \spc \begin{itemize} \item {\cf FRAME_NUMBER} : the number of the frame in this iteration of wildcard expansion. \item {\cf FRAME_NUMBER_PAD} : like {\cf FRAME_NUMBER}, but 0-padded based on the value set on the command line by {\cf --framepadding}. \end{itemize} \end{description} To illustrate how this works, consider the following command, which trims a four-pixel border from all sides and outputs a new image prefixed with "cropped_", without needing to know the resolution or filename of the original image: \begin{smallcode} oiiotool input.exr -cut "{TOP.width-2*4}x{TOP.height-2*4}+{TOP.x+4}+{TOP.y+4}" \ -o cropped_{TOP.filename} \end{smallcode} \section{\oiiotool Tutorial / Recipes} This section will give quick examples of common uses of \oiiotool to get you started. They should be fairly intuitive, but you can read the subsequent sections of this chapter for all the details on every command. \subsection*{Printing information about images} To print the name, format, resolution, and data type of an image (or many images): \begin{code} oiiotool --info *.tif \end{code} \noindent To also print the full metadata about each input image, use both {\cf --info} and {\cf -v}: \begin{code} oiiotool --info -v *.tif \end{code} \noindent To print info about all subimages and/or MIP-map levels of each input image, use the {\cf -a} flag: \begin{code} oiiotool --info -v -a mipmap.exr \end{code} \noindent To print statistics giving the minimum, maximum, average, and standard deviation of each channel of an image, as well as other information about the pixels: \begin{code} oiiotool --stats img_2012.jpg \end{code} \noindent The {\cf --info}, {\cf --stats}, {\cf -v}, and {\cf -a} flags may be used in any combination. \subsection*{Converting between file formats} It's a snap to convert among image formats supported by \product (i.e., for which \ImageInput and \ImageOutput plugins can be found). The \oiiotool utility will simply infer the file format from the file extension. The following example converts a PNG image to JPEG: \begin{code} oiiotool lena.png -o lena.jpg \end{code} The first argument ({\cf lena.png}) is a filename, causing \oiiotool to read the file and makes it the current image. The {\cf -o} command outputs the current image to the filename specified by the next argument. Thus, the above command should be read to mean, ``Read {\cf lena.png} into the current image, then output the current image as {\cf lena.jpg} (using whatever file format is traditionally associated with the {\cf .jpg} extension).'' \subsection*{Comparing two images} To print a report of the differences between two images of the same resolution: \index{image comparison} \begin{code} oiiotool old.tif new.tif --diff \end{code} \noindent If you also want to save an image showing just the differences: \begin{code} oiiotool old.tif new.tif --diff --absdiff -o diff.tif \end{code} This looks complicated, but it's really simple: read {\cf old.tif}, read {\cf new.tif} (pushing {\cf old.tif} down on the image stack), report the differences between them, subtract {\cf new.tif} from {\cf old.tif} and replace them both with the difference image, replace that with its absolute value, then save that image to {\cf diff.tif}. Sometimes you want to compare images but allow a certain number of small difference, say allowing the comparison to pass as long as no more than 1\% of pixels differs by more than 1/255, and as long as no single pixel differs by more than 2/255.. You can do this with thresholds: \begin{code} oiiotool old.tif new.tif --fail 0.004 -failpercent 1 --hardfail 0.008 --diff \end{code} \subsection*{Changing the data format or bit depth} Just use the {\cf -d} option to specify a pixel data format for all subsequent outputs. For example, assuming that {\cf in.tif} uses 16-bit unsigned integer pixels, the following will convert it to an 8-bit unsigned pixels: \begin{code} oiiotool in.tif -d uint8 -o out.tif \end{code} For formats that support per-channel data formats, you can override the format for one particular channel using {\cf -d CHNAME=TYPE}. For example, assuming {\cf rgbaz.exr} is a {\cf float} RGBAZ file, and we wish to convert it to be {\cf half} for RGBA, and {\cf float} for Z. That can be accomplished with the following command: \begin{code} oiiotool rgbaz.tif -d half -d Z=float -o rgbaz2.exr \end{code} \subsection*{Changing the compression} The following command converts writes a TIFF file, specifically using LZW compression: \begin{code} oiiotool in.tif --compression lzw -o compressed.tif \end{code} The following command writes its results as a JPEG file at a compression quality of 50 (pretty severe compression): \begin{code} oiiotool big.jpg --quality 50 -o small.jpg \end{code} \subsection*{Converting between scanline and tiled images} Convert a scanline file to a tiled file with $16 \times 16$ tiles: \begin{code} oiiotool s.tif --tile 16 16 -o t.tif \end{code} \noindent Convert a tiled file to scanline: \begin{code} oiiotool t.tif --scanline -o s.tif \end{code} \subsection*{Adding captions or metadata} \noindent Add a caption to the metadata: \begin{code} oiiotool foo.jpg --caption "Hawaii vacation" -o bar.jpg \end{code} \noindent Add keywords to the metadata: \begin{code} oiiotool foo.jpg --keyword "volcano,lava" -o bar.jpg \end{code} \noindent Add other arbitrary metadata: \begin{code} oiiotool in.exr --attrib "FStop" 22.0 \ --attrib "IPTC:City" "Berkeley" -o out.exr oiiotool in.exr --attrib:type=timecode smpte:TimeCode "11:34:04:00" \ -o out.exr oiiotool in.exr --attrib:type=int[4] FaceBBox "140,300,219,460" \ -o out.exr \end{code} \subsection*{Changing image boundaries} \noindent Change the origin of the pixel data window: \begin{code} oiiotool in.exr --origin +256+80 -o offset.exr \end{code} \noindent Change the display window: \begin{code} oiiotool in.exr --fullsize 1024x768+16+16 -o out.exr \end{code} \noindent Change the display window to match the data window: \begin{code} oiiotool in.exr --fullpixels -o out.exr \end{code} \noindent Crop (trim) an image to a 128x128 region whose upper left corner is at location (900,300), leaving the remaining pixels in their original positions on the image plane (i.e., the resulting image will have origin at 900,300), and retaining its original display window: \begin{code} oiiotool in.exr --crop 128x128+900+300 -o out.exr \end{code} \noindent Cut (trim and extract) a 128x128 region whose upper left corner is at location (900,300), moving the result to the origin (0,0) of the image plane and setting the display window to the new pixel data window: \begin{code} oiiotool in.exr --cut 128x128+900+300 -o out.exr \end{code} \subsection*{Scale the values in an image} Reduce the brightness of the R, G, and B channels by 10\%, but leave the A channel at its original value: \begin{code} oiiotool original.exr --mulc 0.9,0.9,0.9,1.0 -o out.exr \end{code} \subsection*{Remove gamma-correction from an image} Convert a gamma-corrected image (with gamma = 2.2) to linear values. \begin{code} oiiotool corrected.exr --powc 2.2,2.2,2.2,1.0 -o linear.exr \end{code} \subsection*{Resize an image} \noindent Resize to a specific resolution: \begin{code} oiiotool original.tif --resize 1024x768 -o specific.tif \end{code} \noindent Resize both dimensions by a known scale factor: \begin{code} oiiotool original.tif --resize 200% -o big.tif oiiotool original.tif --resize 25% -o small.tif \end{code} \noindent Resize each dimension, independently, by known scale factors: \begin{code} oiiotool original.tif --resize 300%x200% -o big.tif oiiotool original.tif --resize 100%x25% -o small.tif \end{code} \noindent Resize to a known resolution in one dimension, with the other dimension automatically computed to preserve aspect ratio (just specify 0 as the resolution in the dimension to be automatically computed): \begin{code} oiiotool original.tif --resize 200x0 -o out.tif oiiotool original.tif --resize 0x1024 -o out.tif \end{code} \noindent Resize to fit into a given resolution, keeping the original aspect ratio and padding with black where necessary to fit into the specified resolution: \begin{code} oiiotool original.tif --fit 640x480 -o fit.tif \end{code} \subsection*{Color convert an image} This command linearizes a JPEG assumed to be in sRGB, saving as an HDRI OpenEXR file: \begin{code} oiiotool photo.jpg --colorconvert sRGB linear -o output.exr \end{code} \noindent And the other direction: \begin{code} oiiotool render.exr --colorconvert linear sRGB -o fortheweb.png \end{code} \noindent This converts between two named color spaces (presumably defined by your facility's OpenColorIO configuration): \begin{code} oiiotool in.dpx --colorconvert lg10 lnf -o out.exr \end{code} \subsection*{Grayscale and RGB} \noindent Turn a single channel image into a 3-channel gray RGB: \begin{code} oiiotool gray.tif --ch 0,0,0 -o rgb.tif \end{code} \noindent Convert a color image to luminance grayscale: \begin{code} oiiotool RGB.tif --chsum:weight=.2126,.7152,.0722 -o luma.tif \end{code} \subsection*{Channel reordering and padding} \noindent Copy just the color from an RGBA file, truncating the A, yielding RGB only: \begin{code} oiiotool rgba.tif --ch R,G,B -o rgb.tif \end{code} \noindent Zero out the red and green channels: \begin{code} oiiotool rgb.tif --ch R=0,G=0,B -o justblue.tif \end{code} \noindent Swap the red and blue channels from an RGBA image: \begin{code} oiiotool rgba.tif --ch R=B,G,B=R,A -o bgra.tif \end{code} \noindent Extract just the named channels from a complicted many-channel image, and add an alpha channel that is 1 everywhere: \begin{code} oiiotool allmyaovs.exr --ch spec.R,spec.G,spec.B,A=1 -o spec.exr \end{code} \noindent Add an alpha channel to an RGB image, setting it to 1.0 everywhere, and naming it ``A'' so it will be recognized as an alpha channel: \begin{code} oiiotool rgb.tif --ch R,G,B,A=1.0 -o rgba.tif \end{code} \noindent Add an alpha channel to an RGB image, setting it to be the same as the R channel and naming it ``A'' so it will be recognized as an alpha channel: \begin{code} oiiotool rgb.tif --ch R,G,B,A=R -o rgba.tif \end{code} \noindent Add a $z$ channel to an RGBA image, setting it to 3.0 everywhere, and naming it ``Z'' so it will be recognized as a depth channel: \begin{code} oiiotool rgba.exr --ch R,G,B,A,Z=3.0 -o rgbaz.exr \end{code} \subsection*{Fade between two images} \noindent Fade 30\% of the way from A to B: \begin{code} oiiotool A.exr --mulc 0.7 B.exr --mulc 0.3 --add -o fade.exr \end{code} \subsection*{Simple compositing} \noindent Simple ``over'' composite of aligned foreground and background: \begin{code} oiiotool fg.exr bg.exr --over -o composite.exr \end{code} \noindent Composite of small foreground over background, with offset: \begin{code} oiiotool fg.exr --origin +512+89 bg.exr --over -o composite.exr \end{code} \subsection*{Creating an animated GIF from still images} \index{GIF} \index{animated GIF} \noindent Combine several separate JPEG images into an animated GIF with a frame rate of 8 frames per second: \begin{code} oiiotool foo??.jpg --siappendall --attrib FramesPerSecond 10.0 -o anim.gif \end{code} \subsection*{Frame sequences: composite a sequence of images} \noindent Composite foreground images over background images for a series of files with frame numbers in their names: \begin{code} oiiotool fg.1-50%04d.exr bg.1-50%04d.exr --over -o comp.1-50%04d.exr \end{code} \noindent Or, \begin{code} oiiotool --frames 1-50 fg.%04d.exr bg.%04d.exr --over -o comp.%04d.exr \end{code} \subsection*{Expression example: annotate the image with its caption} \noindent This command reads a file, and draws any text in the \qkw{ImageDescription} metadata, 30 pixels from the bottom of the image. \begin{code} oiiotool input.exr --text:x=30:y={TOP.height-30} {TOP.ImageDescription} -o out.exr \end{code} \noindent Note that this works without needing to know the caption ahead of time, and will always put the text 30 pixels from the bottom of the image without requiring you to know the resolution. \subsection{Split a multi-image file into separate files} \noindent Take a multi-image TIFF file, split into its constituent subimages and output each one to a different file, with names \qkw{sub0001.tif}, \qkw{sub0002.tif}, etc. \begin{code} oiiotool multi.tif -sisplit -o:all=1 sub%04d.tif \end{code} \newpage \section{\oiiotool commands: general and image information} \apiitem{\ce --help} Prints full usage information to the terminal, as well as information about image formats supported, known color spaces, OIIO build options and library dependencies. \apiend \apiitem{\ce -v} Verbose status messages --- print out more information about what \oiiotool is doing at every step. \apiend \apiitem{\ce -q} Quet mode --- print out less information about what \oiiotool is doing (only errors). \apiend \apiitem{\ce -n} No saved output --- do not save any image files. This is helpful for certain kinds of tests, or in combination with {\cf --runstats} or {\cf --debug}, for getting detailed information about what a command sequence will do and what it costs, but without producing any saved output files. \apiend \apiitem{\ce --debug} Debug mode --- print lots of information about what operations are being performed. \apiend \apiitem{\ce --runstats} Print timing and memory statistics about the work done by \oiiotool. \apiend \apiitem{\ce -a} Performs all operations on all subimages and/or MIPmap levels of each input image. Without {\cf -a}, generally each input image will really only read the top-level MIPmap of the first subimage of the file. \apiend \apiitem{\ce --info} Prints information about each input image as it is read. If verbose mode is turned on ({\cf -v}), all the metadata for the image is printed. If verbose mode is not turned on, only the resolution and data format are printed. \apiend \apiitem{{\ce --echo} {\rm \emph{message}}} \NEW % 1.8 Prints the message to the console, at that point in the left-to-right execution of command line arguments. The message may contain expressions for substitution. \noindent Optional appended arguments include: \noindent \begin{tabular}{p{10pt} p{0.75in} p{4.0in}} & {\cf newline=}\emph{n} & The number of newlines to print after the message (default is 1, but 0 will suppress the newline, and a larger number will make more vertical space. \\ \end{tabular} \noindent Examples: \begin{code} oiiotool test.tif --resize 256x0 --echo "result is {TOP.width}x{TOP.height}" \end{code} This will resize the input to be 256 pixels wide and automatically size it vertically to preserve the original aspect ratio, and then print a message to the console revealing the resolution of the resulting image. \apiend \apiitem{{\ce --metamatch} {\rm \emph{regex}} \\ {\ce --no-metamatch} {\rm \emph{regex}}} Regular expressions to restrict which metadata are output when using {\cf oiiotool --info -v}. The {\cf --metamatch} expression causes only metadata whose name matches to print; non-matches are not output. The {\cf --no-metamatch} expression causes metadata whose name matches to be suppressed; others (non-matches) are printed. It is not advised to use both of these options at the same time (probably nothing bad will happen, but it's hard to reason about the behavior in that case). \apiend \apiitem{\ce --stats} Prints detailed statistical information about each input image as it is read. \apiend \apiitem{\ce --hash} Print the SHA-1 hash of the pixels of each input image. \apiend \apiitem{\ce --dumpdata} Print to the console detailed information about the values in every pixel. \noindent Optional appended arguments include: \begin{tabular}{p{10pt} p{0.75in} p{3.75in}} & {\cf empty=}{\verb&0|1&} & If 0, will cause deep images to skip printing of information about pixels with no samples. \end{tabular} \apiend \apiitem{\ce --diff \\ --fail {\rm \emph{A}} --failpercent {\rm \emph{B}} --hardfail {\rm \emph{C}} \\ --warn {\rm \emph{A}} --warnpercent {\rm \emph{B}} --hardwarn {\rm \emph{C}}} \index{image comparison} This command computes the difference of the current image and the next image on the stack, and prints a report of those differences (how many pixels differed, the maximum amount, etc.). This command does not alter the image stack. The {\cf --fail}, {\cf --failpercent}, and {\cf hardfail} options set thresholds for {\cf FAILURE}: if more than \emph{B}\% of pixels (on a 0-100 floating point scale) are greater than \emph{A} different, or if \emph{any} pixels are more than \emph{C} different. The defaults are to fail if more than 0\% (any) pixels differ by more than 0.00001 (1e-6), and \emph{C} is infinite. The {\cf --warn}, {\cf --warnpercent}, and {\cf hardwarn} options set thresholds for {\cf WARNING}: if more than \emph{B}\% of pixels (on a 0-100 floating point scale) are greater than \emph{A} different, or if \emph{any} pixels are more than \emph{C} different. The defaults are to warn if more than 0\% (any) pixels differ by more than 0.00001 (1e-6), and \emph{C} is infinite. \apiend \apiitem{\ce --pdiff} This command computes the difference of the current image and the next image on the stack using a perceptual metric, and prints whether or not they match according to that metric. This command does not alter the image stack. \apiend \apiitem{{\ce --colorcount} {\rm\emph{r1,g1,b1,...}}:{\rm\emph{r2,g2,b2,...}}:{\rm\emph{...}}} Given a list of colors separated by colons or semicolons, where each color is a list of comma-separated values (for each channel), examine all pixels of the current image and print a short report of how many pixels matched each of the colors. \noindent Optional appended arguments include: \begin{tabular}{p{10pt} p{0.75in} p{3.75in}} & {\cf eps=}\emph{r,g,b,...} & Tolerance for matching colors (default: 0.001 for all channels). \end{tabular} \noindent Examples: \begin{code} oiiotool test.tif --colorcount "0.792,0,0,1;0.722,0,0,1" \end{code} \noindent might produce the following output: \begin{code} 10290 0.792,0,0,1 11281 0.722,0,0,1 \end{code} \noindent Notice that use of double quotes (\qkw{ }) around the list of color arguments, in order to make sure that the command shell does not interpret the semicolon ({\cf ;}) as a statement separator. An alternate way to specify multiple colors is to separate them with a colon ({\cf :}), for example: \begin{code} oiiotool test.tif --colorcount 0.792,0,0,1:0.722,0,0,1 \end{code} \noindent Another example: \begin{code} oiiotool test.tif --colorcount:eps=.01,.01,.01,1000 "0.792,0,0,1" \end{code} \noindent This example sets a larger epsilon for the R, G, and B channels (so that, for example, a pixel with value [0.795,0,0] would also match), and by setting the epsilon to 1000 for the alpha channel, it effectively ensures that alpha will not be considered in the matching of pixels to the color value. \apiend \apiitem{{\ce --rangecheck} \rm \emph{Rlow,Glow,Blow,...} \emph{Rhi,Bhi,Ghi,...}} Given a two colors (each a comma-separated list of values for each channel), print a count of the number of pixels in the image that has channel values outside the [low,hi] range. Any channels not specified will assume a low of 0.0 and high of 1.0. \noindent Example: \begin{code} oiiotool test.exr --rangecheck 0,0,0 1,1,1 \end{code} \noindent might produce the following output: \begin{code} 0 < 0,0,0 221 > 1,1,1 65315 within range \end{code} \apiend \apiitem{\ce --no-clobber} Sets ``no clobber'' mode, in which existing images on disk will never be overridden, even if the {\cf -o} command specifies that file. \apiend \apiitem{\ce --threads \emph{n}} Use \emph{n} execution threads if it helps to speed up image operations. The default (also if $n=0$) is to use as many threads as there are cores present in the hardware. \apiend \apiitem{{\ce --frames} \rm\emph{seq}\\ {\ce --framepadding} \rm\emph{n}} Describes the frame range to substitute for the {\cf \#} or {\cf \%0Nd} numeric wildcards. The sequence is a comma-separated list of subsequences; each subsequence is a single frame (e.g., {\cf 100}), a range of frames ({\cf 100-150}), or a frame range with step ({\cf 100-150x4} means {\cf 100,104,108,...}). The frame padding is the number of digits (with leading zeroes applied) that the frame numbers should have. It defaults to 4. For example, \begin{code} oiiotool --framepadding 3 --frames 3,4,10-20x2 blah.#.tif \end{code} \noindent would match {\cf blah.003.tif}, {\cf blah.004.tif}, {\cf blah.010.tif}, {\cf blah.012.tif}, {\cf blah.014.tif}, {\cf blah.016.tif}, {\cf blah.018.tif}, {\cf blah.020.tif}. \apiend \apiitem{{\ce --views} \rm\emph{name1,name2,...}} Supplies a comma-separated list of view names (substituted for {\cf \%V} and {\cf \%v}). If not supplied, the view list will be {\cf left,right}. \apiend \apiitem{\ce --wildcardoff \\ --wildcardon} Turns off (or on) numeric wildcard expansion for subsequent command line arguments. This can be useful in selectively disabling numeric wildcard expansion for a subset of the command line. \apiend \begin{comment} \apiitem{\ce --inplace} Causes the output to \emph{replace} the input file, rather than create a new file with a different name. Without this flag, \oiiotool expects two file names, which will be used to specify the input and output files, respectively. But when {\cf --inplace} option is used, any number of file names $\ge 1$ may be specified, and the image conversion commands are applied to each file in turn, with the output being saved under the original file name. This is useful for applying the same conversion to many files. For example, the following example will add the caption ``Hawaii vacation'' to all JPEG files in the current directory: \begin{code} oiiotool --inplace --adjust-time --caption "Hawaii vacation" *.jpg \end{code} \apiend \end{comment} \section{\oiiotool commands: reading and writing images} The commands described in this section read images, write images, or control the way that subsequent images will be written upon output. \subsection*{Reading images} \apiitem{\ce \rm \emph{filename} \\ \ce -i \rm \emph{filename}} \label{sec:oiiotool:i} If a command-line option is the name of an image file, that file will be read and will become the new \emph{current image}, with the previous current image pushed onto the image stack. The {\cf -i} command may be used, which allows additional options that control the reading of just that one file. \noindent Optional appended arguments include: \noindent \begin{tabular}{p{1.25in} p{3.75in}} {\cf now=}\emph{int} & If 1, read the image now, before proceding to the next command. \\ {\cf autocc=}\emph{int} & Enable or disable {\cf --autocc} for this input image. \\ {\cf info=}\emph{int} & Print info about this file (even if the global {\cf --info} was not used) if nonzero. If the value is 2, print full verbose info (like {\cf --info -v}). \\ \end{tabular} \apiend \apiitem{\ce --no-autopremult \\ --autopremult} By default, \product's format readers convert any ``unassociated alpha'' (color values that are not ``premultiplied'' by alpha) to the usual associated/premultiplied convention. If the {\cf --no-autopremult} flag is found, subsequent inputs will not do this premultiplication. It can be turned on again via {\cf --autopremult}. \apiend \apiitem{\ce --autoorient} Automatically do the equivalent of {\cf --reorient} on every image as it is read in, if it has a nonstandard orientation. This is generally a good idea to use if you are using oiiotool to combine images that may have different orientations. \apiend \apiitem{\ce --autocc} Turns on automatic color conversion: Every input image file will be immediately converted to a scene-referred linear color space, and every file written will be first transformed to an appropriate output color space based on the filename or type. Additionally, if the name of an output file contains a color space and that color space is associated with a particular data format, it will output that data format (akin to {\cf -d}). The rules for deducing color spaces are as follows, in order of priority: \begin{enumerate} \item[] \item If the filename (input or output) contains as a substring the name of a color space from the current OpenColorIO configuration, that will be assumed to be the color space of input data (or be the requested color space for output). \item For input files, if the \ImageInput set the \qkw{oiio:ColorSpace} metadata, it will be honored if the filename did not override it. \item When outputting to JPEG files, assume that sRGB is the desired output color space (since JPEG requires sRGB), but still this only occurs if the filename does not specify something different. \end{enumerate} \noindent Example: If the input file \qkw{in_lg10.dpx} is in the \qkw{lg10} color space, and you want to read it in, brighten the RGB uniformly by 10\% (in a linear space, of course), and then save it as a 16 bit integer TIFF file encoded in the {\cf vd16} color space, you could specifiy the conversions explicitly: \begin{code} oiiotool in_lg10.dpx --colorconvert lg10 linear \ --mulc 1.1,1.1,1.1,1.0 -colorconvert linear vd16 \ -d uint16 -o out_vd16.tif \end{code} \noindent or rely on the naming convention matching the OCIO color space names and use automatic conversion: \begin{code} oiiotool --autocc in_lg10.dpx --mulc 1.1 -o out_vd16.tif \end{code} \apiend \apiitem{\ce --native} Normally, all images read by \oiiotool are read into an \ImageBuf backed by an underlying \ImageCache, and are automatically converted to {\cf float} pixels for internal storage (because any subsequent image processing is usually much faster and more accurate when done on floating-point values). This option causes (1) input images to be stored internally in their native pixel data type rather than converted to float, and (2) to bypass the \ImageCache (reading directly into an \ImageBuf) if the pixel data type is not one of the types that is supported internally to \ImageCache ({\cf uint8}, {\cf uint16}, {\cf half}, and {\cf float}). images whose pixels are comprised of data types that are not natively representable exactly in the \ImageCache to bypass the \ImageCache and be read directly into an \ImageBuf. The typical use case for this is when you know you are dealing with unusual pixel data types that might lose precision if converted to {\cf float} (for example, if you have images with {\cf uint32} or {\cf double} pixels). Another use case is if you are using \oiiotool merely for file format or data format conversion, with no actual image processing math performed on the pixel values -- in that case, you might save time and memory by bypassing the conversion to {\cf float}. \apiend \apiitem{\ce --cache {\rm \emph{size}}} \NEW % 1.7 Set the underlying \ImageCache size (in MB). See Section~\ref{imagecacheattr:autotile}. \apiend \apiitem{\ce --autotile {\rm \emph{tilesize}}} \NEW % 1.7 For the underlying \ImageCache, turn on auto-tiling with the given tile size. Setting \emph{tilesize} to 0 turns off auto-tiling. If auto-tile is turned on, The \ImageCache \qkw{autoscanline} feature will also be enabled. See Section~\ref{imagecacheattr:autotile} for details. \apiend \apiitem{\ce --iconfig {\rm \emph{name value}}} \NEW % 1.7 Sets configuration metadata that will apply to the next input file read. \noindent Optional appended arguments include: \begin{tabular}{p{10pt} p{1in} p{3.5in}} & {\cf type=}\emph{typename} & Specify the metadata type. \end{tabular} If the optional {\cf type=} specifier is used, that provides an explicit type for the metadata. If not provided, it will try to infer the type of the metadata from the value: if the value contains only numerals (with optional leading minus sign), it will be saved as {\cf int} metadata; if it also contains a decimal point, it will be saved as {\cf float} metadata; otherwise, it will be saved as a {\cf string} metadata. \noindent Examples: \begin{code} oiiotool --iconfig "oiio:UnassociatedAlpha" 1 in.png -o out.tif \end{code} \apiend \newpage \subsection*{Writing images} \apiitem{\ce -o \rm \emph{filename}} \label{sec:oiiotool:o} Outputs the current image to the named file. This does not remove the current image from the image stack, it merely saves a copy of it. \noindent Optional appended arguments include: \noindent \begin{tabular}{p{1.25in} p{3.75in}} {\cf datatype=}\emph{name} & Set the pixel data type (like {\cf -d}) for this output image (e.g., {\cf uint8, uint16, half, float}, etc.). \\ {\cf bits=}\emph{int} & Set the bits per pixel (if nonstandard for the datatype) for this output image. \\ {\cf dither=}\emph{int} & Turn dither on or off for this output. (default: 0) \\[0.5ex] {\cf autocc=}\emph{int} & Enable or disable {\cf --autocc} for this output image. \\ {\cf autocrop=}\emph{int} & Enable or disable autocrop for this output image. \\ {\cf autotrim=}\emph{int} & Enable or disable {\cf --autotrim} for this output image. \index{autotrim} \\ {\cf separate=}\emph{int} & \\ {\cf contig=}\emph{int} & Set separate or contiguous planar configuration for this output. \\ {\cf\small fileformatname=}\emph{string} & Specify the desired output file format, overriding any guess based on file name extension. \\ {\cf all=}\emph{n} & Output all images currently on the stack using a pattern. See further explanation below. \end{tabular} The {\cf all=}\emph{n} option causes \emph{all} images on the image stack to be output, with the filename argument used as a pattern assumed to contain a {\cf \%d}, which will be substituted with the index of the image (beginning with \emph{n}). For example, to take a multi-image TIFF and extract all the subimages and save them as separate files, \NEW % 1.7 \begin{code} oiiotool multi.tif -sisplit -o:all=1 sub%04d.tif \end{code} \vspace{-12pt} \noindent This will output the subimges as separate files \qkw{sub0001.tif}, \qkw{sub0002.tif}, and so on. \apiend \apiitem{\ce -otex \rm \emph{filename} \\ \ce -oenv \rm \emph{filename} } \index{Texture System!making textures with oiiotool} \label{sec:oiiotool:otex} Outputs the current image to the named file, as a MIP-mapped texture or environment map, identical to that which would be output by \maketx (Chapter~\ref{chap:maketx}). The advantage of using \oiiotool rather than \maketx is simply that you can have a complex \oiiotool command line sequence of image operations, culminating in a direct saving of the results as a texture map, rather than saving to a temporary file and then separately invoking \maketx. \noindent In addition to all the optional arguments of {cf -o}, optional appended arguments for {\cf -otex} and {\cf -oenv} also include: \smallskip \noindent \begin{longtable}{p{10pt} p{1.5in} p{3.75in}} & {\cf wrap=}\emph{string} & Set the default $s$ and $t$ wrap modes of the texture, to one of: {\cf black, clamp, periodic, mirror}. \\ & {\cf swrap=}\emph{string} & Set the default $s$ wrap mode of the texture. \\ & {\cf twrap=}\emph{string} & Set the default $t$ wrap mode of the texture. \\[.5ex] & {\cf resize=}\emph{int} & If nonzero, resize to a power of 2 before starting to create the MIPpmap levels. (default: 0) \\ & {\cf nomipmap=}\emph{int} & If nonzero, do not create MIP-map levels at all. (default: 0) \\ & {\cf updatemode=}\emph{int} & If nonzero, do not create and overwrite the existing texture if it appears to already match the source pixels. (default: 0) \\ & {\cf\small monochrome_detect=}\emph{int} & Detect monochrome (R=G=B) images and turn them into 1-channel textures. (default: 0) \\ & {\cf\small opaque_detect=}\emph{int} & Detect opaque (A=1) images and drop the alpha channel from the texture. (default: 0) \\ & {\cf unpremult=}\emph{int} & Unpremultiply colors before any per-MIP-level color conversions, and re-premultiply after. (default: 0) \\[0.5ex] & {\cf\small incolorspace=}\emph{string} & Specify color space conversion. \\ & {\cf\small outcolorspace=}\emph{string} & ... \\[0.5ex] & {\cf highlightcomp=}\emph{int} & Use highlight compensation for HDR images when resizing for MIP-map levels. (default: 0) \\ & {\cf sharpen=}\emph{float} & Additional sharpening factor when resizing for MIP-map levels. (default: 0.0) \\ & {\cf filter=}\emph{string} & Specify the filter for MIP-map level resizing. (default: box) \\ %& {\cf oiio_options=}\emph{int} & {\cf --oiio} (default: 0) \\ & {\cf\small prman_metadata=}\emph{int} & Turn all all options required to make the resulting texture file compatible with PRMan (particular tile sizes, formats, options, and metadata). (default: 0) \\ & {\cf\small prman_options=}\emph{int} & Include the metadata that PRMan's texture system wants. (default: 0) \\ \end{longtable} \noindent Examples: \begin{smallcode} oiiotool in.tif -otex out.tx oiiotool in.jpg --colorconvert sRGB linear -d uint16 -otex out.tx oiiotool --pattern:checker 512x512 3 -d uint8 -otex:wrap=periodic checker.tx oiiotool in.exr -otex:hilightcomp=1:sharpen=0.5 out.exr \end{smallcode} \apiend \apiitem{\ce -d {\rm \emph{datatype}} \\ -d {\rm \emph{channelname}{\cf =}\emph{datatype}}} Attempts to set the pixel data type of all subsequent outputs. If no channel is named, sets \emph{all} channels to be the specified data type. If a specific channel is named, then the data type will be overridden for just that channel (multiple {\cf -d} commands may be used). Valid types are: {\cf uint8}, {\cf sint8}, {\cf uint16}, {\cf sint16}, {\cf half}, {\cf float}, {\cf double}. The types {\cf uint10} and {\cf uint12} may be used to request 10- or 12-bit unsigned integers. If the output file format does not support them, {\cf uint16} will be substituted. If the {\cf -d} option is not supplied, the output data type will be the same as the data format of the input files, if possible. In any case, if the output file type does not support the requested data type, it will instead use whichever supported data type results in the least amount of precision lost. \apiend % FIXME -- no it doesn't! %\apiitem{\ce -g {\rm \emph{gamma}}} %Applies a gamma correction of $1/\mathrm{gamma}$ to the pixels as they %are output. %\apiend %\apiitem{\ce --sRGB} %Explicitly tags the image as being in sRGB color space. Note that this %does not alter pixel values, it only marks which color space those %values refer to (and only works for file formats that understand such %things). An example use of this command is if you have an image %that is not explicitly marked as being in any particular color space, %but you know that the values are sRGB. %\apiend \apiitem{\ce --scanline} \label{sec:oiiotool:scanline} Requests that subsequent output files be scanline-oriented, if scanline orientation is supported by the output file format. By default, the output file will be scanline if the input is scanline, or tiled if the input is tiled. \apiend \apiitem{\ce --tile {\rm \emph{x}} {\rm \emph{y}}} \label{sec:oiiotool:tile} Requests that subsequent output files be tiled, with the given $x \times y$ tile size, if tiled images are supported by the output format. By default, the output file will take on the tiledness and tile size of the input file. \apiend \apiitem{\ce --compression {\rm \emph{method}}} Sets the compression method for subsequent output images. Each \ImageOutput plugin will have its own set of methods that it supports. By default, the output image will use the same compression technique as the input image (assuming it is supported by the output format, otherwise it will use the default compression method of the output plugin). \apiend \apiitem{\ce --quality {\rm \emph{q}}} Sets the compression quality, on a 1--100 floating-point scale. This only has an effect if the particular compression method supports a quality metric (as JPEG does). \apiend \apiitem{\ce --dither} Turns on \emph{dither} when outputting to 8-bit image files (does not affect other data types). This adds just a bit of noise that reduces visible banding artifacts. \apiend \apiitem{\ce --planarconfig {\rm \emph{config}}} Sets the planar configuration of subsequent outputs (if supported by their formats). Valid choices are: {\cf config} for contiguous (or interleaved) packing of channels in the file (e.g., RGBRGBRGB...), {\cf separate} for separate channel planes (e.g., RRRR...GGGG...BBBB...), or {\cf default} for the default choice for the given format. This command will be ignored for output files whose file format does not support the given choice. \apiend \apiitem{\ce --adjust-time} When this flag is present, after writing each output, the resulting file's modification time will be adjusted to match any \qkw{DateTime} metadata in the image. After doing this, a directory listing will show file times that match when the original image was created or captured, rather than simply when \oiiotool was run. This has no effect on image files that don't contain any \qkw{DateTime} metadata. \apiend \apiitem{\ce --noautocrop} For subsequent outputs, do \emph{not} automatically crop images whose formats don't support separate pixel data and full/display windows. Without this, the default is that outputs will be cropped or padded with black as necessary when written to formats that don't support the concepts of pixel data windows and full/display windows. This is a non-issue for file formats that support these concepts, such as OpenEXR. \apiend \apiitem{\ce --autotrim} \index{autotrim} For subsequent outputs, if the output format supports separate pixel data and full/display windows, automatically trim the output so that it writes the minimal data window that contains all the non-zero valued pixels. In other words, trim off any all-black border rows and columns before writing the file. \apiend \section{\oiiotool commands that change the current image metadata} This section describes \oiiotool commands that alter the metadata of the current image, but do not alter its pixel values. Only the current (i.e., top of stack) image is affected, not any images further down the stack. If the {\cf -a} flag has previously been set, these commands apply to all subimages or MIPmap levels of the current top image. Otherwise, they only apply to the highest-resolution MIPmap level of the first subimage of the current top image. \apiitem{\ce --attrib {\rm \emph{name value}}} Adds or replaces metadata with the given \emph{name} to have the specified \emph{value}. Optional appended arguments include: \begin{tabular}{p{10pt} p{1in} p{3.5in}} & {\cf type=}\emph{typename} & Specify the metadata type. \end{tabular} If the optional {\cf type=} specifier is used, that provides an explicit type for the metadata. If not provided, it will try to infer the type of the metadata from the value: if the value contains only numerals (with optional leading minus sign), it will be saved as {\cf int} metadata; if it also contains a decimal point, it will be saved as {\cf float} metadata; otherwise, it will be saved as a {\cf string} metadata. \noindent Examples: \begin{code} oiiotool in.jpg --attrib "IPTC:City" "Berkeley" out.jpg oiiotool in.jpg --attrib:type=string "Name" "0" out.jpg oiiotool in.exr --attrib:type=matrix worldtocam \ "1,0,0,0,0,1,0,0,0,0,1,0,2.3,2.1,0,1" -o out.exr oiiotool in.exr --attrib:type=timecode smpte:TimeCode "11:34:04:00" \ -o out.exr \end{code} \apiend \apiitem{\ce --sattrib {\rm \emph{name value}}} Adds or replaces metadata with the given \emph{name} to have the specified \emph{value}, forcing it to be interpreted as a {\cf string}. This is helpful if you want to set a {\cf string} metadata to a value that the {\cf --attrib} command would normally interpret as a number. \noindent Note also that {\cf --sattrib} is equivalent to {\cf --attrib:type=string}. \apiend \apiitem{\ce --caption {\rm \emph{text}}} Sets the image metadata \qkw{ImageDescription}. This has no effect if the output image format does not support some kind of title, caption, or description metadata field. Be careful to enclose \emph{text} in quotes if you want your caption to include spaces or certain punctuation! \apiend \apiitem{\ce --keyword {\rm \emph{text}}} Adds a keyword to the image metadata \qkw{Keywords}. Any existing keywords will be preserved, not replaced, and the new keyword will not be added if it is an exact duplicate of existing keywords. This has no effect if the output image format does not support some kind of keyword field. Be careful to enclose \emph{text} in quotes if you want your keyword to include spaces or certain punctuation. For image formats that have only a single field for keywords, \OpenImageIO will concatenate the keywords, separated by semicolon (`;'), so don't use semicolons within your keywords. \apiend \apiitem{\ce --clear-keywords} Clears all existing keywords in the current image. \apiend \apiitem{\ce --nosoftwareattrib} When set, this prevents the normal adjustment of \qkw{Software} and \qkw{ImageHistory} metadata to reflect what \oiiotool is doing. \apiend \apiitem{\ce --sansattrib} When set, this edits the command line inserted in the \qkw{Software} and \qkw{ImageHistory} metadata to omit any verbose {\cf --attrib} and {\cf --sattrib} commands. \apiend \apiitem{\ce --orientation {\rm \emph{orient}}} Explicitly sets the image's \qkw{Orientation} metadata to a numeric value (see Section~\ref{metadata:orientation} for the numeric codes). This only changes the metadata field that specifies how the image should be displayed, it does NOT alter the pixels themselves, and so has no effect for image formats that don't support some kind of orientation metadata. \apiend \apiitem{\ce --orientcw \\ --orientccw \\ --orient180} Adjusts the image's \qkw{Orientation} metadata by rotating the suggested viewing orientation $90^\circ$ clockwise, $90^\circ$ degrees counter-clockwise, or $180^\circ$, respectively, compared to its current setting. This only changes the metadata field that specifies how the image should be displayed, it does NOT alter the pixels themselves, and so has no effect for image formats that don't support some kind of orientation metadata. See the {\cf --rotate90}, {\cf --rotate180}, {\cf --rotate270}, and {\cf --reorient} commands for true rotation of the pixels (not just the metadata). \apiend \apiitem{\ce --origin {\rm \emph{offset}}} Set the pixel data window origin, essentially translating the existing pixel data window to a different position on the image plane. The offset is in the form \begin{code} [+-]x[+-]y \end{code} \noindent Examples: \begin{code} --origin +20+10 x=20, y=10 --origin +0-40 x=0, y=-40 \end{code} \apiend \apiitem{\ce --fullsize {\rm \emph{size}}} Set the display/full window size and/or offset. The size is in the form \\ \emph{width}\,{\cf x}\,\emph{height}{\cf [+-]}\emph{xoffset}{\cf [+-]}\emph{yoffset} \\ If either the offset or resolution is omitted, it will remain unchanged. \noindent Examples: \begin{tabular}{p{2in} p{4in}} {\cf --fullsize 1920x1080} & resolution w=1920, h=1080, offset unchanged \\ {\cf --fullsize -20-30} & resolution unchanged, x=-20, y=-30 \\ {\cf --fullsize 1024x768+100+0} & resolution w=1024, h=768, offset x=100, y=0 \end{tabular} \apiend \apiitem{\ce --fullpixels} Set the full/display window range to exactly cover the pixel data window. \apiend \apiitem{\ce --chnames {\rm \emph{name-list}}} Rename some or all of the channels of the top image to the given comma-separated list. Any completely empty channel names in the list will not be changed. For example, \begin{code} oiiotool in.exr --chnames ",,,A,Z" -o out.exr \end{code} \noindent will rename channel 3 to be \qkw{A} and channel 4 to be \qkw{Z}, but will leave channels 0--3 with their old names. \apiend \section{\oiiotool commands that shuffle channels or subimages} \apiitem{\ce --selectmip {\rm \emph{level}}} If the current image is MIP-mapped, replace the current image with a new image consisting of only the given \emph{level} of the MIPmap. Level 0 is the highest resolution version, level 1 is the next-lower resolution version, etc. \apiend \apiitem{\ce --unmip} If the current image is MIP-mapped, discard all but the top level (i.e., replacing the current image with a new image consisting of only the highest-resolution level). Note that this is equivalent to {\cf --selectmip 0}. \apiend \apiitem{\ce --subimage {\rm \emph{n}}} If the current image has multiple subimages, extract the specified subimage. The subimage specifier \emph{n} is either an integer giving the index of the subimage to extract (starting with 0), or the \emph{name} of the subimage to extract (comparing to the \qkw{oiio:subimagename} metadata). \apiend \apiitem{\ce --sisplit} Remove the top image from the stack, split it into its constituent subimages, and push them all onto the stack (first to last). \apiend \apiitem{\ce --siappend} Replaces the top two images on the stack with a single new image comprised of the subimages of both images appended together. \apiend \apiitem{\ce --siappendall} Replace \emph{all} of the individual images on the stack with a single new image comprised of the subimages of all original images appended together. \apiend \apiitem{\ce --ch {\rm \emph{channellist}}} \label{sec:oiiotool:ch} Replaces the top image with a new image whose channels have been reordered as given by the \emph{channellist}. The {\cf channellist} is a comma-separated list of channel designations, each of which may be (a) an integer channel index of the channel to copy, (b) the name of a channel to copy, (c) \emph{newname}{\cf =}\emph{oldname}, which copies a named channel and also renames it, (d) {\cf =}\emph{float}, which will set the channel to a constant value, or (e) \emph{newname}{\cf =}\emph{float}, which sets the channel to a constant value as well as names the new channel. Examples include: \qkw{R,G,B}, \qkw{R=0.0,G,B,A=1.0}, \qkw{R=B,G,B=R}, \qkw{4,5,6,A}. Channel numbers outside the valid range of input channels, or unknown names, will be replaced by black channels. If the \emph{channellist} is shorter than the number of channels in the source image, unspecified channels will be omitted. \apiend \apiitem{\ce --chappend} Replaces the top two images on the stack with a new image comprised of the channels of both images appended together. \apiend \section{\oiiotool commands that adjust the image stack} \apiitem{\ce --pop} Pop the image stack, discarding the current image and thereby making the next image on the stack into the new current image. \apiend \apiitem{\ce --dup} Duplicate the current image and push the duplicate on the stack. Note that this results in both the current and the next image on the stack being identical copies. \apiend \apiitem{\ce --swap} Swap the current image and the next one on the stack. \apiend \apiitem{\ce --label {\rm \emph{name}}} Gives a name to (and saves) the current image at the top of the stack. Thereafter, the label name may be used to refer to that saved image, in the usual manner that an ordinary input image would be specified by filename. \apiend \section{\oiiotool commands that make entirely new images} \apiitem{\ce --create {\rm \emph{size channels}}} Create new black image with the given size and number of channels, pushing it onto the image stack and making it the new current image. The \emph{size} is in the form \\ \spc \emph{width}\,{\cf x}\,\emph{height}{\cf [+-]}\emph{xoffset}{\cf [+-]}\emph{yoffset} \\ If the offset is omitted, it will be $x=0,y=0$. \noindent Examples: \begin{tabular}{p{2in} p{4in}} {\cf --create 1920x1080 3} & RGB with w=1920, h=1080, x=0, y=0 \\ {\cf --create 1024x768+100+0 4} & RGBA with w=1024, h=768, x=100, y=0 \end{tabular} \apiend \apiitem{\ce --pattern {\rm \emph{patternname size channels}}} Create new image with the given size and number of channels, initialize its pixels to the named pattern, and push it onto the image stack to make it the new current image. The \emph{size} is in the form \\ \spc \emph{width}\,{\cf x}\,\emph{height}{\cf [+-]}\emph{xoffset}{\cf [+-]}\emph{yoffset} \\ If the offset is omitted, it will be $x=0,y=0$. The patterns recognized include the following: \begin{tabular}{p{1in} p{4in}} {\cf black} & A black image (all pixels 0.0) \\ {\cf constant} & A constant color image, defaulting to white, but the color can be set with the optional {\cf :color=r,g,b,...} arguments giving a numerical value for each channel. \\ {\cf checker} & A black and white checkerboard pattern. The optional argument {\cf :width=} sets with width of the checkers (defaulting to 8 pixels). \\ {\cf fill} & A constant color or gradient, depending on the optional colors. Argument {\cf :color=r,g,b,...} results in a constant color. Argument {\cf :top=r,g,b,...:bottom=...} results in a top-to-bottom gradient. Argument {\cf :left=r,g,b,...:right=...} results in a left-to-right gradient. Argument {\small\cf :topleft=r,g,b,...:topright=...:bottomleft=...:bottomright=...} results in a 4-corner bilinear gradient. \\ {\cf noise} & Create a noise image, with the option {\cf :type=} specifying the kind of noise: (1) {\cf gaussian} (default) for normal distribution noise with mean and standard deviation given by {\cf :mean=} and {\cf :stddev=}, respectively (defaulting to 0 and 0.1); (2) {\cf uniform} for uniformly-distributed noise over the range of values given by options {\cf :min=} and {\cf :max=} (defaults: 0 and 0.1); (3) {\cf salt} for ``salt and pepper'' noise where a portion of pixels given by option {\cf portion=} (default: 0.1) is replaced with value given by option {\cf value=} (default: 0). For any of these noise types, the option {\cf seed=} can be used to change the random number seed and {\cf mono=1} can be used to make monochromatic noise (same value in all channels). \\ \end{tabular} \noindent Examples: \apiitem{--pattern constant:color=0.3,0.5,0.1,1.0 640x480 4} \vspace{10pt} A constant 4-channel, $640\times 480$ image with all pixels (0.5, 0.5, 0.1, 1). \apiend \apiitem{--pattern checker:width=16:height=16 256x256 3} \vspace{10pt} An $256 \times 256$ RGB image with a 16-pixel-wide checker pattern. \spc \includegraphics[width=1.25in]{figures/checker.jpg} \apiend \vspace{10pt} \begin{tinycode} --pattern fill:top=0.1,0.1,0.1:bottom=0,0,0.5 640x480 3 --pattern fill:left=0.1,0.1,0.1:right=0,0.75,0 640x480 3 --pattern fill:topleft=.1,.1,.1:topright=1,0,0:bottomleft=0,1,0:botromright=0,0,1 640x480 3 \end{tinycode} \noindent Horizontal, vertical, or 4-corner gradients. \noindent \begin{tabular}{lll} \includegraphics[width=1.5in]{figures/gradient.jpg} & \includegraphics[width=1.5in]{figures/gradienth.jpg} & \includegraphics[width=1.5in]{figures/gradient4.jpg} \end{tabular} \apiitem{\small oiiotool --pattern noise:type=uniform:min=1:max=1 256x256 3 -o colornoise.jpg \\ oiiotool --pattern noise:type=uniform:min=0:max=1:mono=1 256x256 3 -o greynoise.jpg} \vspace{10pt} The first example puts uniform noise independently in 3 channels, while the second generates a single greyscale noise and replicates it in all channels. \spc \includegraphics[width=1.25in]{figures/unifnoise3.jpg} \spc \includegraphics[width=1.25in]{figures/unifnoise1.jpg} \apiend \apiitem{\small oiiotool --pattern noise:type=gaussian:mean=0.5:stddev=0.2 256x256 3 -o gaussnoise.jpg} \vspace{10pt} Generate Gaussian noise with mean 0.5 and standard deviation 0.2 for each channel. \spc \includegraphics[width=1.25in]{figures/gaussnoise.jpg} \apiend \apiend \apiitem{\ce --kernel {\rm \emph{name size}}} Create new 1-channel {\cf float} image big enough to hold the named kernel and size (size is expressed as \emph{width}{\cf x}\emph{height}, e.g. {\cf 5x5}). The \emph{width} and \emph{height} are allowed to be floating-point numbers. The kernel image will have its origin offset so that the kernel center is at (0,0), and and will be normalized (the sum of all pixel values will be 1.0). Kernel names can be: {\cf gaussian}, {\cf sharp-gaussian}, {\cf box}, {\cf triangle}, {\cf blackman-harris}, {\cf mitchell}, {\cf b-spline}, \qkw{cubic}, \qkw{keys}, \qkw{simon}, \qkw{rifman}, {\cf disk}. There are also {\cf catmull-rom} and {\cf lanczos3}, but they are fixed-size kernels that don't scale with the width, and are therefore probably less useful in most cases. \noindent Examples: \begin{code} oiiotool --kernel gaussian 11x11 -o gaussian.exr \end{code} \apiend \apiitem{\ce --capture} Capture a frame from a camera device, pushing it onto the image stack and making it the new current image. Optional appended arguments include: \begin{tabular}{p{10pt} p{1in} p{3.5in}} & {\cf camera=}\emph{num} & Select which camera number to capture (default: 0). \end{tabular} \noindent Examples: \begin{tabular}{p{2in} p{4in}} {\cf --capture} & Capture from the default camera. \\ {\cf --capture:camera=1} & Capture from camera \#1. \\ \end{tabular} \apiend \section{\oiiotool commands that do image processing} \apiitem{{\ce --add} \\ {\ce --addc} {\rm \emph{value}} \\ {\ce --addc} {\rm \emph{value0,value1,value2...}}} Replace the \emph{two} top images with a new image that is the pixel-by- pixel sum of those images ({\cf --add}), or add a constant color value to all pixels in the top image ({\cf --addc}). For {\cf --addc}, if a single constant value is given, it will be added to all color channels. Alternatively, a series of comma-separated constant values (with no spaces!) may be used to specifiy a different value to add to each channel in the image. % \noindent Examples: % \begin{tabular}{m{3in} m{2.5in}} % {\cf \footnotesize oiiotool tahoe.jpg --addc 0.5 -o addc.jpg} % & \includegraphics[width=1in]{figures/tahoe-small.jpg} % \raisebox{40pt}{\large $\rightarrow$} % \includegraphics[width=1in]{figures/addc.jpg} \\ % \end{tabular} \noindent Examples: \begin{code} oiiotool tahoe.jpg --addc 0.5 -o addc.jpg \end{code} \spc \includegraphics[width=1.5in]{figures/tahoe-small.jpg} \raisebox{40pt}{\large $\rightarrow$} \includegraphics[width=1.5in]{figures/addc.jpg} \\ \vspace{12pt} \begin{code} oiiotool imageA.tif imageB.tif --add -o sum.jpg \end{code} \apiend \apiitem{{\ce --sub} \\ {\ce --subc} {\rm \emph{value}} \\ {\ce --subc} {\rm \emph{value0,value1,value2...}}} Replace the \emph{two} top images with a new image that is the pixel-by- pixel difference between the first and second images ({\cf --sub}), or subtract a constant color value from all pixels in the top image ({\cf --subc}). For {\cf --subc}, if a single constant value is given, it will be subtracted from all color channels. Alternatively, a series of comma-separated constant values (with no spaces!) may be used to specifiy a different value to subtract from each channel in the image. \apiend \apiitem{{\ce --mul} \\ {\ce --mulc} {\rm \emph{value}} \\ {\ce --mulc} {\rm \emph{value0,value1,value2...}}} Replace the \emph{two} top images with a new image that is the pixel-by- pixel multiplicative product of those images ({\cf --mul}), or multiply all pixels in the top image by a constant value ({\cf --mulc}). For {\cf --mulc}, if a single constant value is given, it will be multiplied to all color channels. Alternatively, a series of comma-separated constant values (with no spaces!) may be used to specifiy a different value to multiply with each channel in the image. \noindent Example: \begin{code} oiiotool tahoe.jpg --mulc 0.2 -o mulc.jpg \end{code} \spc \includegraphics[width=1.5in]{figures/tahoe-small.jpg} \raisebox{40pt}{\large $\rightarrow$} \includegraphics[width=1.5in]{figures/mulc.jpg} \\ \apiend \apiitem{{\ce --div} \\ {\ce --divc} {\rm \emph{value}} \\ {\ce --divc} {\rm \emph{value0,value1,value2...}}} Replace the \emph{two} top images with a new image that is the pixel-by-pixel, channel-by-channel result of the first image divided by the second image ({\cf --div}), or divide all pixels in the top image by a constant value ({\cf --divc}). Division by zero is defined as resulting in 0. For {\cf --divc}, if a single constant value is given, all color channels will have their values divided by the same value. Alternatively, a series of comma-separated constant values (with no spaces!) may be used to specifiy a different multiplier for each channel in the image, respectively. \apiend \apiitem{{\ce --mad}} Replace the \emph{three} top images A, B, and C (C being the top of stack, B below it, and A below B), and compute A*B+C, placing the result on the stack. Note that \qkw{A B C --mad} is equivalent to \qkw{A B --mul C --add}, though using {\cf --mad} may be somewhat faster and preserve more precision. %\noindent Example: %\begin{code} % oiiotool tahoe.jpg --mulc 0.2 -o mulc.jpg %\end{code} %\spc \includegraphics[width=1.5in]{figures/tahoe-small.jpg} %\raisebox{40pt}{\large $\rightarrow$} %\includegraphics[width=1.5in]{figures/mulc.jpg} \\ \apiend \apiitem{{\ce --invert}} Replace the top images with its color inverse. It only inverts the first three channels, in order to preserve alpha. \noindent Example: \begin{code} oiiotool tahoe.jpg --inverse -o inverse.jpg \end{code} \spc \includegraphics[width=1.5in]{figures/tahoe-small.jpg} \raisebox{40pt}{\large $\rightarrow$} \includegraphics[width=1.5in]{figures/invert.jpg} \\ \apiend \apiitem{{\ce --absdiff} \\ {\ce --absdiffc} {\rm \emph{value}} \\ {\ce --absdiffc} {\rm \emph{value0,value1,value2...}}} Replace the \emph{two} top images with a new image that is the absolute value of the difference between the first and second images ({\cf --absdiff}), or replace the top image by the absolute value of the difference between each pixel and a constant color ({\cf --absdiffc}). \apiend \apiitem{\ce --abs} Replace the current image with a new image that has each pixel consisting of the \emph{absolute value} of he old pixel value. \apiend \apiitem{\ce --powc {\rm \emph{value}} \\ --powc {\rm \emph{value0,value1,value2...}}} Raise all the pixel values in the top image to a constant power value. If a single constant value is given, all color channels will have their values raised to this power. Alternatively, a series of comma-separated constant values (with no spaces!) may be used to specifiy a different exponent for each channel in the image, respectively. \apiend \apiitem{\ce --noise} Alter the top image to introduce noise, with the option {\cf :type=} specifying the kind of noise: (1) {\cf gaussian} (default) for normal distribution noise with mean and standard deviation given by {\cf :mean=} and {\cf :stddev=}, respectively (defaulting to 0 and 0.1); (2) {\cf uniform} for uniformly-distributed noise over the range of values given by options {\cf :min=} and {\cf :max=} (defaults: 0 and 0.1); (3) {\cf salt} for ``salt and pepper'' noise where a portion of pixels given by option {\cf portion=} (default: 0.1) is replaced with value given by option {\cf value=} (default: 0). For any of these noise types, the option {\cf seed=} can be used to change the random number seed, {\cf mono=1} can be used to make monochromatic noise (same value in all channels), and {\cf nchannels=} can be used to limit which channels are affected by the noise \noindent Example: \begin{code} # Add color gaussian noise to an image oiiotool tahoe.jpg --noise:type=gaussian:stddev=0.1 -o noisy.jpg # Simulate bad pixels by turning 1% of pixels black, but only in RGB # channels (leave A alone) oiiotool tahoe-rgba.tif --noise:type=salt:value=0:portion=0.01:mono=1:nchannels=3 \ -o dropouts.tif \end{code} \spc \begin{tabular}{lll} \includegraphics[width=1.5in]{figures/tahoe-small.jpg} & \includegraphics[width=1.5in]{figures/tahoe-gauss.jpg} & \includegraphics[width=1.5in]{figures/tahoe-pepper.jpg} \\ original & gaussian noise & salt \& pepper dropouts \\ \end{tabular} \apiend \apiitem{\ce --chsum} Replaces the top image by a copy that contains only 1 color channel, whose value at each pixel is the sum of all channels of the original image. Using the optional {\cf weight} allows you to customize the weight of each channel in the sum. \begin{tabular}{p{10pt} p{1in} p{3.5in}} & {\cf weight=}\emph{r,g,...} & Specify the weight of each channel (default: 1). \end{tabular} \noindent Example: \begin{code} oiiotool RGB.tif --chsum:weight=.2126,.7152,.0722 -o luma.tif \end{code} \spc \includegraphics[width=1.5in]{figures/tahoe-small.jpg} \raisebox{40pt}{\large $\rightarrow$} \includegraphics[width=1.5in]{figures/luma.jpg} \\ \apiend \apiitem{\ce --paste {\rm \emph{location}}} Takes two images -- the first is the ``foreground'' and the second is the ``background'' -- and uses the pixels of the foreground to replace those of the backgroud beginning at the upper left \emph{location} (expressed as {\cf +}\emph{xpos}{\cf +}\emph{ypos}, e.g., {\cf +100+50}, or of course using {\cf -} for negative offsets). \apiend \apiitem{\ce --mosaic {\rm \emph{size}}} Removes \emph{w}{\cf x}\emph{h} images, dictated by the \emph{size}, and turns them into a single image mosaic. Optional appended arguments include: \begin{tabular}{p{10pt} p{1in} p{3.5in}} & {\cf pad=}\emph{num} & Select the number of pixels of black padding to add between images (default: 0). \end{tabular} \noindent Examples: \begin{code} oiiotool left.tif right.tif --mosaic:pad=16 2x1 -o out.tif oiiotool 0.tif 1.tif 2.tif 3.tif 4.tif --mosaic:pad=16 2x2 -o out.tif \end{code} \apiend \apiitem{\ce --over} \index{composite} Replace the \emph{two} top images with a new image that is the Porter/Duff ``over'' composite with the first image as the foreground and the second image as the background. Both input images must have the same number and order of channels and must contain an alpha channel. \apiend \apiitem{\ce --zover} \index{depth composite} Replace the \emph{two} top images with a new image that is a \emph{depth composite} of the two images -- the operation is the Porter/Duff ``over'' composite, but each pixel individually will choose which of the two images is the foreground and which background, depending on the ``Z'' channel values for that pixel (larger Z means farther away). Both input images must have the same number and order of channels and must contain both depth/Z and alpha channels. Optional appended arguments include: \begin{tabular}{p{10pt} p{1in} p{3.5in}} & {\cf zeroisinf=}\emph{num} & If nonzero, indicates that $z=0$ pixels should be treated as if they were infinitely far away. (The default is 0, meaning that ``zero means zero.''). \end{tabular} \apiend \apiitem{\ce --rotate90} Replace the current image with a new image that is rotated 90 degrees clockwise. \noindent Example: \begin{code} oiiotool grid.jpg --rotate90 -o rotate90.jpg \end{code} \spc \includegraphics[width=1.25in]{figures/grid-small.jpg} \raisebox{40pt}{\large $\rightarrow$} \includegraphics[width=1.25in]{figures/rotate90.jpg} \\ \apiend \apiitem{\ce --rotate180} Replace the current image with a new image that is rotated by 180 degrees. \noindent Example: \begin{code} oiiotool grid.jpg --rotate180 -o rotate180.jpg \end{code} \spc \includegraphics[width=1.25in]{figures/grid-small.jpg} \raisebox{40pt}{\large $\rightarrow$} \includegraphics[width=1.25in]{figures/rotate180.jpg} \\ \apiend \apiitem{\ce --rotate270} Replace the current image with a new image that is rotated 270 degrees clockwise (or 90 degrees counter-clockwise). \noindent Example: \begin{code} oiiotool grid.jpg --rotate270 -o rotate270.jpg \end{code} \spc \includegraphics[width=1.25in]{figures/grid-small.jpg} \raisebox{40pt}{\large $\rightarrow$} \includegraphics[width=1.25in]{figures/rotate270.jpg} \\ \apiend \apiitem{\ce --flip} Replace the current image with a new image that is flipped vertically, with the top scanline becoming the bottom, and vice versa. \noindent Example: \begin{code} oiiotool grid.jpg --flip -o flip.jpg \end{code} \spc \includegraphics[width=1.25in]{figures/grid-small.jpg} \raisebox{40pt}{\large $\rightarrow$} \includegraphics[width=1.25in]{figures/flip.jpg} \\ \apiend \apiitem{\ce --flop} Replace the current image with a new image that is flopped horizontally, with the leftmost column becoming the rightmost, and vice versa. \noindent Example: \begin{code} oiiotool grid.jpg --flop -o flop.jpg \end{code} \spc \includegraphics[width=1.25in]{figures/grid-small.jpg} \raisebox{40pt}{\large $\rightarrow$} \includegraphics[width=1.25in]{figures/flop.jpg} \\ \apiend \apiitem{\ce --reorient} Replace the current image with a new image that is rotated and/or flipped as necessary to move the pixels to match the Orientation metadata that describes the desired display orientation. \noindent Example: \begin{code} oiiotool tahoe.jpg --reorient -o oriented.jpg \end{code} %\spc \includegraphics[width=1.25in]{figures/grid-small.jpg} %\raisebox{40pt}{\large $\rightarrow$} %\includegraphics[width=1.25in]{figures/flop.jpg} \\ \apiend \apiitem{\ce --transpose} Replace the current image with a new image that is trasposed about the $xy$ axis (x and coordinates and size are flipped). \noindent Example: \begin{code} oiiotool grid.jpg --transpose -o transpose.jpg \end{code} \spc \includegraphics[width=1.25in]{figures/grid-small.jpg} \raisebox{40pt}{\large $\rightarrow$} \includegraphics[width=1.25in]{figures/transpose.jpg} \\ \apiend \apiitem{\ce --cshift {\rm \emph{offset}}} Circularly shift the pixels of the image by the given offset (expressed as {\cf +10+100} to move by 10 pixels horizontally and 100 pixels vertically, or {\cf +50-30} to move by 50 pixels horizontally and $-30$ pixels vertically. \emph{Circular} shifting means that the pixels wrap to the other side as they shift. \noindent Example: \begin{code} oiiotool grid.jpg --cshift +70+30 -o cshift.jpg \end{code} \spc \includegraphics[width=1.25in]{figures/grid-small.jpg} \raisebox{40pt}{\large $\rightarrow$} \includegraphics[width=1.25in]{figures/cshift.jpg} \\ \apiend \apiitem{\ce --crop {\rm \emph{size}}} Replace the current image with a new copy with the given \emph{size}, cropping old pixels no longer needed, padding black pixels where they previously did not exist in the old image, and adjusting the offsets if requested. The size is in the form \\ \spc\spc \emph{width}\,{\cf x}\,\emph{height}{\cf [+-]}\emph{xoffset}{\cf [+-]}\emph{yoffset} \\ or~~~~ \spc \emph{xmin,ymin,xmax,ymax} \\ Note that {\cf crop} does not \emph{reposition} pixels, it only trims or pads to reset the image's pixel data window to the specified region. If \oiiotool's global {\cf -a} flag is used ({\bf a}ll subimages)), or if the optional {\cf --crop:allsubimages=1} is employed, the crop will be applied identically to all subimages. \noindent Examples: \begin{code} # Both of these crop to a 100x120 region that begins at x=35,y=40 oiiotool tahoe.exr --crop 100x120+35+40 -o crop.exr oiiotool tahoe.exr --crop 35,40,134,159 -o crop.exr \end{code} \hspace{0.4in} \includegraphics[width=1.5in]{figures/tahoe-small.jpg} \raisebox{40pt}{\large $\rightarrow$} \includegraphics[width=1.5in]{figures/crop.jpg} \apiend \apiitem{\ce --croptofull} Replace the current image with a new image that is cropped or padded as necessary to make the pixel data window exactly cover the full/display window. \apiend \apiitem{\ce --trim} Replace the current image with a new image that is cropped to contain the minimal rectangular ROI that contains all of the nonzero-valued pixels of the original image. \noindent Examples: \begin{code} oiiotool greenrect.exr -trim -o trimmed.jpg \end{code} \hspace{0.4in} \includegraphics[width=1.5in]{figures/pretrim.jpg} \raisebox{40pt}{\large $\rightarrow$} \includegraphics[width=1.5in]{figures/trim.jpg} \apiend \apiitem{\ce --cut {\rm \emph{size}}} Replace the current image with a new copy with the given \emph{size}, cropping old pixels no longer needed, padding black pixels where they previously did not exist in the old image, repositioning the cut region at the image origin (0,0) and resetting the full/display window to be identical to the new pixel data window. (In other words, {\cf --cut} is equavalent to {\cf --crop} followed by {\cf --origin +0+0 --fullpixels}.) The size is in the form \\ \spc\spc \emph{width}\,{\cf x}\,\emph{height}{\cf [+-]}\emph{xoffset}{\cf [+-]}\emph{yoffset} \\ or~~~~ \spc \emph{xmin,ymin,xmax,ymax} \\ \noindent Examples: \begin{code} # Both of these crop to a 100x120 region that begins at x=35,y=40 oiiotool tahoe.exr --cut 100x120+35+40 -o cut.exr oiiotool tahoe.exr --cut 35,40,134,159 -o cut.exr \end{code} \hspace{0.4in} \includegraphics[width=1.5in]{figures/tahoe-small.jpg} \raisebox{40pt}{\large $\rightarrow$} \raisebox{0.55in}{\includegraphics[width=0.476in]{figures/cut.jpg}} \apiend \apiitem{\ce --resample {\rm \emph{size}}} Replace the current image with a new image that is resampled to the given pixel data resolution rapidly, but at a low quality, by simply copying the ``closest'' pixel. The \emph{size} is in the form \\ \spc\spc \emph{width}\,{\cf x}\,\emph{height}{\cf [+-]}\emph{xoffset}{\cf [+-]}\emph{yoffset} \\ or~~~~ \spc \emph{xmin,ymin,xmax,ymax} \\ \\ or~~~~ \spc \emph{scale}{\verb|%|} \\ \\ or~~~~ \spc \emph{wscale}{\verb|%|}\,{\cf x}\,\emph{hscale}{\verb|%|} \\ \noindent if {\cf width} or {\cf height} is 0, that dimension will be automatically computed so as to preserve the original aspect ratio. \noindent Examples (suppose that the original image is 640x480): \begin{tabular}{p{2in} p{4in}} {\cf --resample 1024x768} & new resolution w=1024, h=768 \\ {\cf --resample 50{\verb|%|}} & reduce resolution to 320x240 \\ {\cf --resample 300{\verb|%|}} & increase resolution to 1920x1440 \\ {\cf --resample 400x0} & new resolution will be 400x300 \end{tabular} \apiend \apiitem{\ce --resize {\rm \emph{size}}} Replace the current image with a new image whose display (full) size is the given pixel data resolution and offset. The \emph{size} is in the form \\ \spc\spc \emph{width}\,{\cf x}\,\emph{height}{\cf [+-]}\emph{xoffset}{\cf [+-]}\emph{yoffset} \\ or~~~~ \spc \emph{xmin,ymin,xmax,ymax} \\ \\ or~~~~ \spc \emph{scale}{\verb|%|} \\ \\ or~~~~ \spc \emph{wscale}{\verb|%|}\,{\cf x}\,\emph{hscale}{\verb|%|} \\ \noindent if {\cf width} or {\cf height} is 0, that dimension will be automatically computed so as to preserve the original aspect ratio. Optional appended arguments include: \begin{tabular}{p{10pt} p{1in} p{3.75in}} & {\cf filter=}\emph{name} & Filter name. The default is {\cf blackman-harris} when increasing resolution, {\cf lanczos3} when decreasing resolution. \\ \end{tabular} \noindent Examples (suppose that the original image is 640x480): \begin{tabular}{p{2in} p{4in}} {\cf --resize 1024x768} & new resolution w=1024, h=768 \\ {\cf --resize 50{\verb|%|}} & reduce resolution to 320x240 \\ {\cf --resize 300{\verb|%|}} & increase resolution to 1920x1440 \\ {\cf --resize 400x0} & new resolution will be 400x300 \end{tabular} \apiend \apiitem{\ce --fit {\rm \emph{size}}} Replace the current image with a new image that is resized to fit into the given pixel data resolution, keeping the original aspect ratio and padding with black pixels if the requested image size does not have the same aspect ratio. The \emph{size} is in the form \\ \spc\spc \emph{width}\,{\cf x}\,\emph{height} \\ or~~~~ \spc \emph{width}\,{\cf x}\,\emph{height}{\cf [+-]}\emph{xorigin}{\cf [+-]}\emph{yorigin} \\ Optional appended arguments include: \begin{tabular}{p{10pt} p{1in} p{3.75in}} & {\cf filter=}\emph{name} & Filter name. The default is {\cf blackman-harris} when increasing resolution, {\cf lanczos3} when decreasing resolution. \\ & {\cf pad=}\emph{p} & If the argument is nonzero, will pad with black pixels to make the resulting image exactly the size specified, if the source and desired size are not the same aspect ratio. \end{tabular} \noindent Examples: \begin{code} oiiotool in.exr --fit:pad=1 640x480 -o out.exr oiiotool in.exr --fit 1024x1024 -o out.exr \end{code} \apiend \apiitem{\ce --pixelaspect {\rm \emph{aspect}}} Replace the current image with a new image that scales up the width or height in order to match the requested pixel aspect ratio. If displayed in a manner that honors the PixelAspectRatio, it should look the same, but it will have different pixel dimensions than the original. It will always be the same or higher resolution, so it does not lose any detail present in the original. As an example, if you have a $512 \times 512$ image with pixel aspect ratio 1.0, {\cf --pixelaspect 2.0} will result in a $512 \times 1024$ image that has \qkw{PixelAspectRatio} metadata set to 2.0. Optional appended arguments include: \begin{tabular}{p{10pt} p{1.25in} p{3.5in}} & {\cf filter=}\emph{name} & Filter name. The default is \qkw{lanczos3}. \\ \end{tabular} \noindent Examples: \begin{tinycode} oiiotool mandrill.tif --pixelaspect 2.0 -o widepixels.tif \end{tinycode} %\spc \includegraphics[width=1.25in]{figures/grid-small.jpg} %\raisebox{40pt}{\large $\rightarrow$} %\includegraphics[width=1.25in]{figures/rotate45.jpg} \\ \apiend \apiitem{\ce --rotate {\rm \emph{angle}}} Replace the current image with a new image that is rotated by the given angle (in degrees). Positive angles mean to rotate counter-clockwise, negative angles mean clockwise. By default, the center of rotation is at the exact center of the display window (a.k.a.\ ``full'' image), but can be explicitly set with the optional {\cf center=\emph{x,y}} option. Optional appended arguments include: \begin{tabular}{p{10pt} p{1.25in} p{3.5in}} & {\cf center=}\emph{x,y} & Alternate center of rotation. \\ & {\cf filter=}\emph{name} & Filter name. The default is \qkw{lanczos3}. \\ & {\small \cf recompute_roi=}\emph{val} & If nonzero, recompute the pixel data window to exactly hold the transformed image (default=0). \\ \end{tabular} \noindent Examples: \begin{tinycode} oiiotool mandrill.tif --rotate 45 -o rotated.tif oiiotool mandrill.tif --rotate:center=80,91.5:filter=lanczos3 45 -o rotated.tif \end{tinycode} \spc \includegraphics[width=1.25in]{figures/grid-small.jpg} \raisebox{40pt}{\large $\rightarrow$} \includegraphics[width=1.25in]{figures/rotate45.jpg} \\ \apiend \apiitem{\ce --warp {\rm \emph{M33}}} Replace the current image with a new image that is warped by the given $3 \times 3$ matrix (presented as a comma-separated list of values, without any spaces). Optional appended arguments include: \begin{tabular}{p{10pt} p{1.25in} p{3.5in}} & {\cf filter=}\emph{name} & Filter name. The default is \qkw{lanczos3}. \\ & {\small\cf recompute_roi=}\emph{val} & If nonzero, recompute the pixel data window to exactly hold the transformed image (default=0). \\ \end{tabular} \noindent Examples: \begin{tinycode} oiiotool mandrill.tif --warp "0.707,0.707,0,-0.707,0.707,0,128,-53.02,1" -o warped.tif \end{tinycode} \apiend \apiitem{\ce --convolve} Use the top image as a kernel to convolve the next image farther down the stack, replacing both with the result. \noindent Examples: \begin{code} # Use a kernel image already prepared oiiotool image.exr kernel.exr --convolve -o output.exr # Construct a kernel image on the fly with --kernel oiiotool image.exr --kernel gaussian 5x5 --convolve -o blurred.exr \end{code} \apiend \apiitem{\ce --blur {\rm \emph{size}}} Blur the top image with a blur kernel of the given size expressed as \emph{width}{\cf x}\emph{height}. (The sizes may be floating point numbers.) Optional appended arguments include: \begin{tabular}{p{10pt} p{1in} p{3.75in}} & {\cf kernel=}\emph{name} & Kernel name. The default is {\cf gaussian}. \end{tabular} \noindent Examples: \begin{code} oiiotool image.jpg --blur 5x5 -o blurred.jpg oiiotool image.jpg --blur:kernel=bspline 7x7 -o blurred.jpg \end{code} \spc \begin{tabular}{ll} \includegraphics[width=1.5in]{figures/tahoe-small.jpg} & \includegraphics[width=1.5in]{figures/tahoe-blur.jpg} \\ original & blurred \\ \end{tabular} \apiend \apiitem{\ce --median {\rm \emph{size}}} Perform a median filter on the top image with a window of the given size expressed as \emph{width}{\cf x}\emph{height}. (The sizes are integers.) This helps to eliminate noise and other unwanted high-frequency detail, but without blurring long edges the way a {\cf --blur} command would. \noindent Examples: \begin{code} oiiotool noisy.jpg --median 3x3 -o smoothed.jpg \end{code} \spc \begin{tabular}{lll} \includegraphics[width=1.5in]{figures/tahoe-small.jpg} & \includegraphics[width=1.5in]{figures/tahoe-pepper.jpg} & \includegraphics[width=1.5in]{figures/tahoe-pepper-median.jpg} \\ original & with dropouts & median filtered \\ \end{tabular} \apiend \apiitem{\ce --dilate {\rm \emph{size}}\\ \ce --erode {\rm \emph{size}}} \indexapi{dilate} \indexapi{erode} \index{morphological filtering} \NEW % 1.7 Perform dilation or erosion on the top image with a window of the given size expressed as \emph{width}{\cf x}\emph{height}. (The sizes are integers.) Dilation takes the maximum of pixel values inside the window, and makes bright features wider and more prominent, dark features thinner, and removes small isolated dark spots. Erosion takes the minimum of pixel values inside the window, and makes dark features wider, bright features thinner, and removes small isolated bright spots. \noindent Examples: \begin{code} oiiotool orig.tif --dilate 3x3 -o dilate.tif oiiotool orig.tif --erode 3x3 -o erode.tif oiiotool orig.tif --erode 3x3 --dilate 3x3 -o open.tif oiiotool orig.tif --dilate 3x3 --erode 3x3 -o close.tif oiiotool orig.tif --dilate 3x3 --erode 3x3 -sub -o gradient.tif oiiotool orig.tif open.tif -o tophat.tif oiiotool close.tif orig.tif -o bottomhat.tif \end{code} \spc \begin{tabular}{llll} \includegraphics[width=0.75in]{figures/morphsource.jpg} & \includegraphics[width=0.75in]{figures/dilate.jpg} & \includegraphics[width=0.75in]{figures/erode.jpg} & \includegraphics[width=0.75in]{figures/morphopen.jpg} \\ original & dilate & erode & open \\ \includegraphics[width=0.75in]{figures/morphclose.jpg} & \includegraphics[width=0.75in]{figures/morphgradient.jpg} & \includegraphics[width=0.75in]{figures/tophat.jpg} & \includegraphics[width=0.75in]{figures/bottomhat.jpg} \\ close & gradient & tophat & bottomhat \\ \end{tabular} \apiend \apiitem{\ce --laplacian} Calculates the Laplacian of the top image. \noindent Examples: \begin{code} oiiotool tahoe.jpg --laplacian tahoe-laplacian.exr \end{code} \spc \begin{tabular}{ll} \includegraphics[width=1.5in]{figures/tahoe-small.jpg} & \includegraphics[width=1.5in]{figures/tahoe-laplacian.jpg} \\ original & Laplacian image \\ \end{tabular} \apiend \apiitem{\ce --unsharp} Unblur the top image using an ``unsharp mask.'' Optional appended arguments include: \begin{tabular}{p{10pt} p{1in} p{3.75in}} & {\cf kernel=}\emph{name} & Name of the blur kernel (default: {\cf gaussian}). If the kernel name is {\cf median}, the unsarp mask algorithm will use a median filter rather than a blurring filter in order to compute the low-frequency image. \\ & {\cf width=}\emph{w} & Width of the blur kernel (default: 3). \\ & {\cf contrast=}\emph{c} & Contrast scale (default: 1.0) \\ & {\cf threshold=}\emph{t} & Threshold for applying the difference (default: 0) \end{tabular} \noindent Examples: \begin{code} oiiotool image.jpg --unsharp -o sharper.jpg oiiotool image.jpg --unsharp:width=5:contrast=1.5 -o sharper.jpg oiiotool image.jpg --unsharp:kernel=median -o sharper.jpg # Note: median filter helps emphasize compact high-frequency details # without over-sharpening long edges as the default unsharp filter # sometimes does. \end{code} % FIXME - should have a visual example \apiend \apiitem{\ce --fft \\ --ifft} Performs forward and inverse unitized discrete Fourier transform. The forward FFT always transforms only the first channel of the top image on the stack, and results in a 2-channel image (with real and imaginary channels). The inverse FFT transforms the first two channels of the top image on the stack (assuming they are real and imaginary, respectively) and results in a single channel result (with the real component only of the spatial domain result). \noindent Examples: \begin{code} # Select the blue channel and take its DCT oiiotool image.jpg --ch 2 --fft -o fft.exr # Reconstruct from the FFT oiiotool fft.exr --ifft -o reconstructed.exr # Output the power spectrum: real^2 + imag^2 oiiotool fft.exr --dup --mul --chsum -o powerspectrum.exr \end{code} \apiend \apiitem{\ce --polar \\ --unpolar} The {\cf --polar} transforms a 2-channel image whose channels are interpreted as complex values (real and imaginary components) into the equivalent values expressed in polar form of amplitude and phase (with phase between $0$ and $2\pi$). The {\cf unpolar} performs the reverse transformation, converting from polar values (amplitude and phase) to complex (real and imaginary). \noindent Examples: \begin{code} oiiotool complex.exr --polar -o polar.exr oiiotool polar.exr --unpolar -o complex.exr \end{code} \apiend \apiitem{\ce --fixnan {\rm \emph{streategy}}} Replace the top image with a copy in which any pixels that contained {\cf NaN} or {\cf Inf} values (hereafter referred to collectively as ``nonfinite'') are repaired. If \emph{strategy} is {\cf black}, nonfinite values will be replaced with {\cf 0}. If \emph{strategy} is {\cf box3}, nonfinite values will be replaced by the average of all the finite values within a $3 \times 3$ region surrounding the pixel. If \emph{strategy} is {\cf error}, nonfinite values will be left alone, but it will result in an error that will terminate \oiiotool. \apiend \apiitem{\ce --clamp} Replace the top image with a copy in which pixel values have been clamped. Optional arguments include: Optional appended arguments include: \begin{tabular}{p{10pt} p{1in} p{3.75in}} & {\cf min=}\emph{val} & Specify a minimum value for all channels. \\ & {\cf min=}\emph{val0,val1,...} & Specify minimum value for each channel individually. \\ & {\cf max=}\emph{val} & Specify a maximum value for all channels. \\ & {\cf max=}\emph{val0,val1,...} & Specify maximum value for each channel individually. \\ & {\cf clampalpha=}\emph{val} & If \emph{val} is nonzero, will additionally clamp the alpha channel to [0,1]. (Default: 0, no additional alpha clamp.) \end{tabular} If no value is given for either the minimum or maximum, it will NOT clamp in that direction. For the variety of minimum and maximum that specify per-channel values, a missing value indicates that the corresponding channel should not be clamped. \noindent Examples: \begin{tabular}{p{2in} p{4in}} {\cf --clamp:min=0} & Clamp all channels to a mimimum of 0 (all \\ & negative values are changed to 0). \\ {\cf --clamp:min=0:max=1} & Clamp all channels to [0,1]. \\ {\cf --clamp:clampalpha=1} & Clamp the designated alpha channel to [0,1]. \\ {\cf --clamp:min=,,0:max=,,3.0} & Clamp the third channel to [0,3], do not clamp \\ & other channels. \end{tabular} \apiend \apiitem{\ce --rangecompress \\ --rangeexpand} Range compression re-maps input values to a logarithmic scale. Range expansion is the inverse mapping back to a linear scale. Range compression and expansion only applies to color channels; alpha or z channels will not be modified. By default, this transformation will happen to each color channel independently. But if the optional {\cf luma} argument is nonzero and the image has at least 3 channels and the first three channels are not alpha or depth, they will be assumed to be RGB and the pixel scaling will be done using the luminance and applied equally to all color channels. This can help to preserve color even when remapping intensity. Optional appended arguments include: \begin{tabular}{p{10pt} p{1in} p{3.75in}} & {\cf luma=}\emph{val} & If \emph{val} is 0, turns off the luma behavior. \end{tabular} Range compression and expansion can be useful in cases where high contrast super-white ($> 1$) pixels (such as very bright highlights in HDR captured or rendered images) can produce undesirable artifacts, such as if you resize an HDR image using a filter with negative lobes -- which could result in objectionable ringing or even negative result pixel values. For example, \begin{smallcode} oiiotool hdr.exr --rangecompress --resize 512x512 --rangeexpand -o resized.exr \end{smallcode} \apiend \apiitem{\ce --fillholes} Replace the top image with a copy in which any pixels that had $\alpha < 1$ are ``filled'' in a smooth way using data from surrounding $\alpha > 0$ pixels, resulting in an image that is $\alpha = 1$ and plausible color everywhere. This can be used both to fill internal ``holes'' as well as to extend an image out. \apiend \apiitem{\ce --line {\rm \emph{x1,y1,x2,y2,...}}} \NEW % 1.7 Draw (rasterize) an open polyline connecting the list of pixel positions, as a comma-separated list of alternating $x$ and $y$ values. Additional optional arguments include: \begin{tabular}{p{10pt} p{1in} p{3.75in}} & {\cf color=}\emph{r,g,b,...} & specify the color of the line \\ \end{tabular} The default color, if not supplied, is opaque white. \noindent Examples: \begin{code} oiiotool checker.exr --line:color=1,0,0 10,60,250,20,100,190 -o out.exr \end{code} \spc \includegraphics[width=2in]{figures/lines.png} \\ \apiend \apiitem{\ce --box {\rm \emph{x1,y1,x2,y2}}} \NEW % 1.7 Draw (rasterize) a filled or unfilled a box with opposite corners {\cf (x1,y1)} and {\cf (x2,y2)}. Additional optional arguments include: \begin{tabular}{p{10pt} p{1in} p{3.75in}} & {\cf color=}\emph{r,g,b,...} & specify the color of the lines \\ & {\cf fill=}\emph{bool} & if nonzero, fill in the box \\ \end{tabular} The default color, if not supplied, is opaque white. \noindent Examples: \begin{code} oiiotool checker.exr --box:color=0,1,1,1 150,100,240,180 \ --box:color=0.5,0.5,0,0.5:fill=1 100,50,180,140 -o out.exr \end{code} \spc \includegraphics[width=2in]{figures/box.png} \\ \apiend \apiitem{\ce --fill {\rm \emph{size}}} Alter the top image by filling the ROI specified by \emph{size}. The fill can be a constant color, vertical gradient, horizontal gradient, or four-corner gradient. Optional arguments for constant color: \\ \begin{tabular}{p{10pt} p{1.5in} p{3.75in}} & {\cf color=}\emph{r,g,b,...} & the color of the constant \\ \end{tabular} Optional arguments for vertical gradient: \\ \begin{tabular}{p{10pt} p{1.5in} p{3.75in}} & {\cf top=}\emph{r,g,b,...} & the color for the top edge of the region \\ & {\cf bottom=}\emph{r,g,b,...} & the color for the bottom edge of the region \\ \end{tabular} Optional arguments for horizontal gradient: \\ \begin{tabular}{p{10pt} p{1.5in} p{3.75in}} & {\cf left=}\emph{r,g,b,...} & the color for the left edge of the region \\ & {\cf right=}\emph{r,g,b,...} & the color for the right edge of the region \\ \end{tabular} Optional arguments for 4-corner gradient: \\ \begin{tabular}{p{10pt} p{1.5in} p{3.75in}} & {\cf topleft=}\emph{r,g,b,...} & the color for the top left corner of the region \\ & {\cf topright=}\emph{r,g,b,...} & the color for the top right corner of the region \\ & {\cf bottomleft=}\emph{r,g,b,...} & the color for the bottom left corner of the region \\ & {\cf bottomright=}\emph{r,g,b,...} & the color for the bottom right corner of the region \\ \end{tabular} \noindent Examples: \begin{smallcode} # make a grey-to-blue vertical gradient oiiotool --create 640x480 3 \ --fill:top=0.1,0.1,0.1:bottom=0,0,0.5 640x480 -o gradient.tif # make a grey-to-green horizontal gradient oiiotool --create 640x480 3 \ --fill:left=0.1,0.1,0.1:right=0,0.75,0 640x480 -o gradient.tif \end{smallcode} \begin{tinycode} # four-corner interpolated gradient oiiotool --create 640x480 3 \ -fill:topleft=.1,.1,.1:topright=1,0,0:bottomleft=0,1,0:botromright=0,0,1 \ 640x480 -o gradient.tif \end{tinycode} \noindent \begin{tabular}{lll} \includegraphics[width=1.5in]{figures/gradient.jpg} & \includegraphics[width=1.5in]{figures/gradienth.jpg} & \includegraphics[width=1.5in]{figures/gradient4.jpg} \end{tabular} \apiend \apiitem{\ce --text {\rm \emph{words}}} Draw (rasterize) text overtop of the current image. \begin{tabular}{p{10pt} p{1in} p{3.75in}} & {\cf x=}\emph{xpos} & $x$ position (in pixel coordinates) of the text \\ & {\cf y=}\emph{ypos} & $y$ position (in pixel coordinates) of the text \\ & {\cf size=}\emph{size} & font size (height, in pixels) \\ & {\cf font=}\emph{name} & font name, full path to the font file on disk (use double quotes {\cf "name"} if the path name includes spaces) \\ & {\cf color=}\emph{r,g,b,...} & specify the color of the text \\ \end{tabular} The default positions the text starting at the center of the image, drawn 16 pixels high in opaque white in all channels (1,1,1,...), and using a default font (which may be system dependent). \noindent Examples: \begin{smallcode} oiiotool in.exr --text:x=10:y=400:size=40 "Hello world" \ --text:x=100:y=200:font="Arial Bold":color=1,0,0:size=60 "Go Big Red!" \ --tocolorspace sRGB -o text.jpg \end{smallcode} \spc \includegraphics[width=2.5in]{figures/text.jpg} \\ \noindent Note that because of slightly differing fonts and versions of Freetype available, we do not expect drawn text to be pixel-for-pixel identical on different platforms supported by \product. \apiend \section{\oiiotool commands for color management} Many of the color management commands depend on an installation of OpenColorIO ({\cf http://opencolorio.org/}). If OIIO has been compiled with OpenColorIO support and the environment variable {\cf \$OCIO} is set to point to a valid OpenColorIO configuration file, you will have access to all the color spaces that are known by that OCIO configuration. Alternately, you can use the {\cf --colorconfig} option to explicitly point to a configuration file. If no valid configuration file is found (either in {\cf \$OCIO} or specified by {\cf --colorconfig}) or OIIO was not compiled with OCIO support, then the only color space transformats available are {\cf linear} to {\cf Rec709} (and vice versa) and {\cf linear} to {\cf sRGB} (and vice versa). If you ask for \oiiotool help ({\cf oiiotool --help}), at the very bottom you will see the list of all color spaces, looks, and displays that \oiiotool knows about. \apiitem{\ce --iscolorspace {\rm \emph{colorspace}}} Alter the metadata of the current image so that it thinks its pixels are in the named color space. This does not alter the pixels of the image, it only changes \oiiotool's understanding of what color space those those pixels are in. \apiend \apiitem{\ce --colorconfig {\rm \emph{filename}}} Instruct \oiiotool to read an OCIO configuration from a custom location. Without this, the default is to use the {\cf \$OCIO} environment variable as a guide for the location of the configuration file. \apiend \apiitem{\ce --colorconvert {\rm \emph{fromspace tospace}}} Replace the current image with a new image whose pixels are transformed from the named \emph{fromspace} color space into the named \emph{tospace} (disregarding any notion it may have previously had about the color space of the current image). Optional appended arguments include: \begin{tabular}{p{10pt} p{1in} p{3.75in}} & {\cf key=}\emph{name} & \\ & {\cf value=}\emph{str} & Adds a key/value pair to the ``context'' that OpenColorIO will used when applying the look. Multiple key/value pairs may be specified by making each one a comma-separated list. \\ \end{tabular} \apiend \apiitem{\ce --tocolorspace {\rm \emph{tospace}}} Replace the current image with a new image whose pixels are transformed from their existing color space (as best understood or guessed by OIIO) into the named \emph{tospace}. This is equivalent to a use of {\cf oiiotool --colorconvert} where the \emph{fromspace} is automatically deduced. \apiend \apiitem{\ce --ociolook {\rm \emph{lookname}}} Replace the current image with a new image whose pixels are transformed using the named OpenColorIO look description. Optional appended arguments include: \begin{tabular}{p{10pt} p{1in} p{3.75in}} & {\cf from=}\emph{val} & Assume the image is in the named color space. If no {\cf from=} is supplied, it will try to deduce it from the image's metadata or previous {\cf --iscolorspace} directives. \\ & {\cf to=}\emph{val} & Convert to the named space after applying the look. \\ & {\cf inverse=}\emph{val} & If \emph{val} is nonzero, inverts the color transformation and look application. \\ & {\cf key=}\emph{name} & \\ & {\cf value=}\emph{str} & Adds a key/value pair to the ``context'' that OpenColorIO will used when applying the look. Multiple key/value pairs may be specified by making each one a comma-separated list. \\ \end{tabular} This command is only meaningful if OIIO was compiled with OCIO support and the environment variable {\cf \$OCIO} is set to point to a valid OpenColorIO configuration file. If you ask for \oiiotool help ({\cf oiiotool --help}), at the very bottom you will see the list of all looks that \oiiotool knows about. \noindent Examples: \begin{tinycode} oiiotool in.jpg --ociolook:from=vd8:to=vd8:key=SHOT:value=pe0012 match -o cc.jpg \end{tinycode} \apiend \apiitem{\ce --ociodisplay {\rm \emph{displayname viewname}}} Replace the current image with a new image whose pixels are transformed using the named OpenColorIO ``display'' transformation given by the \emph{displayname} and \emph{viewname}. Optional appended arguments include: \begin{tabular}{p{10pt} p{1in} p{3.75in}} & {\cf from=}\emph{val} & Assume the image is in the named color space. If no {\cf from=} is supplied, it will try to deduce it from the image's metadata or previous {\cf --iscolorspace} directives. \\ & {\cf key=}\emph{name} & \\ & {\cf value=}\emph{str} & Adds a key/value pair to the ``context'' that OpenColorIO will used when applying the display transform. Multiple key/value pairs may be specified by making each one a comma-separated list. \\ \end{tabular} This command is only meaningful if OIIO was compiled with OCIO support and the environment variable {\cf \$OCIO} is set to point to a valid OpenColorIO configuration file. If you ask for \oiiotool help ({\cf oiiotool --help}), at the very bottom you will see the list of all looks that \oiiotool knows about. \noindent Examples: \begin{tinycode} oiiotool in.exr --ociodisplay:from=lnf:key=SHOT:value=pe0012 sRGB Film -o cc.jpg \end{tinycode} \apiend \apiitem{\ce --ociofiletransform {\rm \emph{name}}} Replace the current image with a new image whose pixels are transformed using the named OpenColorIO file transform. Optional appended arguments include: \begin{tabular}{p{10pt} p{1in} p{3.75in}} & {\cf inverse=}\emph{val} & If \emph{val} is nonzero, inverts the color transformation. \\ \end{tabular} This command is only meaningful if OIIO was compiled with OCIO support and the environment variable {\cf \$OCIO} is set to point to a valid OpenColorIO configuration file. If you ask for \oiiotool help ({\cf oiiotool --help}), at the very bottom you will see the list of all looks that \oiiotool knows about. \noindent Examples: \begin{tinycode} oiiotool in.jpg --ociofiletransform footransform.csp -o out.jpg \end{tinycode} \apiend \apiitem{\ce --unpremult} Divide all color channels (those not alpha or z) of the current image by the alpha value, to ``un-premultiply'' them. This presumes that the image starts of as ``associated alpha,'' a.k.a.\ ``premultipled.'' Pixels in which the alpha channel is 0 will not be modified (since the operation is undefined in that case). This is a no-op if there is no identified alpha channel. \apiend \apiitem{\ce --premult} Multiply all color channels (those not alpha or z) of the current image by the alpha value, to ``premultiply'' them. This presumes that the image starts of as ``unassociated alpha,'' a.k.a.\ ``non-premultipled.'' \apiend \section{\oiiotool commands for deep images} A number of \oiiotool operations are designed to work with ``deep'' images. These are detailed below. In general, operations not listed in this section should not be expected to work with deep images. \subsection{Commands specific to deep images} \apiitem{\ce --deepen} \index{deep images} If the top image is not deep, then turn it into the equivalent ``deep'' image. Pixels with non-infinite $z$ or with any non-zero color channels will get a single depth sample in the resulting deep image. Pixels in the source that have 0 in all channels, and either no \qkw{Z} channel or a $z$ value indicating an infinite distance, will result in a pixel with no depth samples. \noindent Optional appended arguments include: \begin{tabular}{p{10pt} p{0.75in} p{3.75in}} & {\cf z=}\emph{val} & The depth to use for deep samples if the source image did not have a \qkw{Z} channel. (The default is {\cf 1.0}.) \end{tabular} \apiend \apiitem{\ce --flatten} \index{deep images} If the top image is ``deep,'' then ``flatten'' it by compositing the depth samples in each pixel. \apiend \apiitem{\ce --deepmerge} \index{composite} \NEW % 1.7 Replace the \emph{two} top images with a new deep image that is the ``deep merge'' of the inputs. Both input images must be deep images, have the same number and order of channels and must contain an alpha channel and depth channel. \apiend \subsection{General commands that also work for deep images} \apiitem{\ce --addc \\ --subc \\ --mulc \\ --divc} Adding, subtracting, multiplying, or dividing a per-channel constant will work for deep images, performing the operation for every sample in the image. \apiend \apiitem{\ce --autotrim} For subsequent outputs, automatically {\cf --trim} before writing the file. \apiend \apiitem{\ce --ch {\rm \emph{channellist}}} Reorder, rename, remove, or add channels to a deep image. See Section~\ref{sec:oiiotool:ch} \apiend \apiitem{\ce --crop {\rm \emph{size}}} Crop (adjust the pixel data window), removing pixels or adding empty pixels as needed. \apiend \apiitem{\ce --resample {\rm \emph{size}}} Resampling (resize without filtering or interpolation, just choosing the closest deep pixel to copy for each output pixel). \apiend \apiitem{\ce --diff} Report on the difference of the top two images. \apiend \apiitem{\ce --dumpdata} Print to the console detailed information about the values in every pixel. \noindent Optional appended arguments include: \begin{tabular}{p{10pt} p{0.75in} p{3.75in}} & {\cf empty=}{\verb&0|1&} & If 0, will cause deep images to skip printing of information about pixels with no samples, and cause non-deep images to skip printing information about pixels that are entirely 0.0 value in all channels. \end{tabular} \apiend \apiitem{\ce --info} Prints information about each input image as it is read. \apiend \apiitem{\ce --trim} Replace the current image with a new image that is cropped to contain the minimal rectangular ROI that contains all of the non-empty pixels of the original image. \apiend \apiitem{\ce --scanline \\ \ce --tile {\rm \emph{x}} {\rm \emph{y}}} Convert to scanline or to tiled representations upon output. \apiend \apiitem{\ce --stats} Prints detailed statistical information about each input image as it is read. \apiend \apiitem{\ce --fixnan {\rm \emph{streategy}}} \NEW % 1.7 Replace the top image with a copy in which any pixels that contained {\cf NaN} or {\cf Inf} values (hereafter referred to collectively as ``nonfinite'') are repaired. The \emph{strategy} may be either {\cf black} or {\cf error}. \apiend \chapwidthend openimageio-1.7.17~dfsg0.orig/src/doc/idiff.tex0000644000175000017500000002127513151711064017473 0ustar mfvmfv\chapter{Comparing Images With {\kw idiff}} \label{chap:idiff} \indexapi{idiff} \section{Overview} The {\cf idiff} program compares two images, printing a report about how different they are and optionally producing a third image that records the pixel-by-pixel differences between them. There are a variety of options and ways to compare (absolute pixel difference, various thresholds for warnings and errors, and also an optional perceptual difference metric). Because {\cf idiff} is built on top on \product, it can compare two images of any formats readable by \ImageInput plugins on hand. They may have any (or different) file formats, data formats, etc. \section{Using {\cf idiff}} The {\cf idiff} utility is invoked as follows: \bigskip \hspace{0.25in} {\cf idiff} [\emph{options}] \emph{image1} \emph{image2} \medskip Where \emph{input1} and \emph{input2} are the names of two image files that should be compared. They may be of any format recognized by \product (i.e., for which image-reading plugins are available). If the two input images are not the same resolutions, or do not have the same number of channels, the comparison will return FAILURE immediately and will not attempt to compare the pixels of the two images. If they are the same dimensions, the pixels of the two images will be compared, and a report will be printed including the mean and maximum error, how many pixels were above the warning and failure thresholds, and whether the result is {\cf PASS}, {\cf WARNING}, or {\cf FAILURE}. For example: \begin{code} $ idiff a.jpg b.jpg Comparing "a.jpg" and "b.jpg" Mean error = 0.00450079 RMS error = 0.00764215 Peak SNR = 42.3357 Max error = 0.254902 @ (700, 222, B) 574062 pixels (82.1%) over 1e-06 574062 pixels (82.1%) over 1e-06 FAILURE \end{code} % $ The ``mean error'' is the average difference (per channel, per pixel). The ``max error'' is the largest difference in any pixel channel, and will point out on which pixel and channel it was found. It will also give a count of how many pixels were above the warning and failure thresholds. The metadata of the two images (e.g., the comments) are not currently compared; only differences in pixel values are taken into consideration. \subsection*{Raising the thresholds} By default, if any pixels differ between the images, the comparison will fail. You can allow \emph{some} differences to still pass by raising the failure thresholds. The following example will allow images to pass the comparison test, as long as no more than 10\% of the pixels differ by 0.004 (just above a 1/255 threshold): \begin{code} idiff -fail 0.004 -failpercent 10 a.jpg b.jpg \end{code} But what happens if a just a few pixels are very different? Maybe you want that to fail, also. The following adjustment will fail if at least 10\% of pixels differ by 0.004, or if \emph{any} pixel differs by more than 0.25: \begin{code} idiff -fail 0.004 -failpercent 10 -hardfail 0.25 a.jpg b.jpg \end{code} If none of the failure criteria are met, and yet some pixels are still different, it will still give a WARNING. But you can also raise the warning threshold in a similar way: \begin{code} idiff -fail 0.004 -failpercent 10 -hardfail 0.25 \ -warn 0.004 -warnpercent 3 a.jpg b.jpg \end{code} \noindent The above example will PASS as long as fewer than 3\% of pixels differ by more than 0.004. If it does, it will be a WARNING as long as no more than 10\% of pixels differ by 0.004 and no pixel differs by more than 0.25, otherwise it is a FAILURE. %\subsection*{Perceptual differences} \subsection*{Output a difference image} Ordinary text output will tell you how many pixels failed or were warnings, and which pixel had the biggest difference. But sometimes you need to see visually where the images differ. You can get {\cf idiff} to save an image of the differences between the two input images: \begin{code} idiff -o diff.tif -abs a.jpg b.jpg \end{code} The {\cf -abs} flag saves the absolute value of the differences (i.e., all positive values or zero). If you omit the {\cf -abs}, pixels in which {\cf a.jpg} have smaller values than {\cf b.jpg} will be negative in the difference image (be careful in this case of using a file format that doesn't support negative values). You can also scale the difference image with the {\cf -scale}, making them easier to see. And the {\cf -od} flag can be used to output a difference image only if the comparison fails, but not if the images pass within the designated threshold (thus saving you the trouble and space of saving a black image). \section{{\cf idiff} Reference} The various command-line options are discussed below: \subsection*{General options} \apiitem{--help} Prints usage information to the terminal. \apiend \apiitem{-v} Verbose output --- more detail about what it finds when comparing images, even when the comparison does not fail. \apiend \apiitem{-q} Quiet mode -- output nothing for successful match), output only minimal error messages to stderr for failure / no match. The shell return code also indicates success or failure (successful match returns 0, failure returns nonzero). \apiend \apiitem{-a} Compare all subimages. Without this flag, only the first subimage of each file will be compared. \apiend \subsection*{Thresholds and comparison options} \apiitem{-fail {\rm \emph{A}} \\ -failpercent {\rm \emph{B}} \\ -hardfail {\rm \emph{C}}} Sets the threshold for {\cf FAILURE}: if more than \emph{B}\% of pixels (on a 0-100 floating point scale) are greater than \emph{A} different, or if \emph{any} pixels are more than \emph{C} different. The defaults are to fail if more than 0\% (any) pixels differ by more than 0.00001 (1e-6), and \emph{C} is infinite. \apiend \apiitem{-warn {\rm \emph{A}} \\ -warnpercent {\rm \emph{B}} \\ -hardwarn {\rm \emph{C}}} Sets the threshold for {\cf WARNING}: if more than \emph{B}\% of pixels (on a 0-100 floating point scale) are greater than \emph{A} different, or if \emph{any} pixels are more than \emph{C} different. The defaults are to warn if more than 0\% (any) pixels differ by more than 0.00001 (1e-6), and \emph{C} is infinite. \apiend \apiitem{-p} Does an additional test on the images to attempt to see if they are \emph{perceptually} different (whether you are likely to discern a difference visually), using Hector Yee's metric. If this option is enabled, the statistics will additionally show a report on how many pixels failed the perceptual test, and the test overall will fail if more than the ``fail percentage'' failed the perceptual test. \apiend \subsection*{Difference image output} \apiitem{-o {\rm \emph{outputfile}}} Outputs a \emph{difference image} to the designated file. This difference image pixels consist are each of the value of the corresponding pixel from \emph{image1} minus the value of the pixel \emph{image2}. The file extension of the output file is used to determine the file format to write (e.g., \qkw{out.tif} will write a TIFF file, \qkw{out.jpg} will write a JPEG, etc.). The data format of the output file will be format of whichever of the two input images has higher precision (or the maximum precision that the designated output format is capable of, if that is less than either of the input imges). Note that pixels whose value is lower in \emph{image1} than in \emph{image2}, this will result in negative pixels (which may be clamped to zero if the image format does not support negative values)), unless the {\cf -abs} option is also used. \apiend \apiitem{-abs} Will cause the output image to consist of the \emph{absolute value} of the difference between the two input images (so all values in the difference image $\ge 0$). \apiend \apiitem{-scale {\rm \emph{factor}}} Scales the values in the difference image by the given (floating point) factor. The main use for this is to make small actual differences more visible in the resulting difference image by giving a large scale factor. \apiend \apiitem{-od} Causes a difference image to be produce \emph{only} if the image comparison fails. That is, even if the {\cf -o} option is used, images that are within the comparison threshold will not write out a useless black (or nearly black) difference image. \apiend \subsection*{Process return codes} The {\cf idiff} program will return a code that can be used by scripts to indicate the results: \medskip \begin{tabular}{p{0.3in} p{5in}} 0 & OK: the images match within the warning and error thresholds. \\ 1 & Warning: the errors differ a little, but within error thresholds. \\ 2 & Failure: the errors differ a lot, outside error thresholds. \\ 3 & The images weren't the same size and couldn't be compared. \\ 4 & File error: could not find or open input files, etc. \end{tabular} \begin{code} \end{code} openimageio-1.7.17~dfsg0.orig/src/doc/help2man_preformat.py0000755000175000017500000000170213151711064022023 0ustar mfvmfv#!/usr/bin/python # Format the output from various oiio command line "$tool --help" invocations, # and munge such that txt2man generates a simple man page with not-too-horrible # formatting. from __future__ import print_function import sys lines = [l.rstrip().replace('\t', ' '*8) for l in sys.stdin.readlines()] print('TITLE') print(lines[0]) print() print('SYNOPSIS') for i,line in enumerate(lines[2:]): if line.lstrip().startswith('-') or line.lstrip().startswith('Options'): optStart = i+2 break print(line) print('''DESCRIPTION This program is part of the OpenImageIO (http://www.openimageio.org) tool suite. Detailed documentation is avaliable in pdf format with the OpenImageIO distribution. ''') print('OPTIONS') for line in lines[optStart:]: if not line.startswith(' '): print() print(line) elif not line.lstrip().startswith('-'): print(line.lstrip()) else: print(line) print() openimageio-1.7.17~dfsg0.orig/src/doc/algorithm.sty0000644000175000017500000000561013151711064020412 0ustar mfvmfv% ALGORITHM STYLE -- Released 8 April 1996 % for LaTeX-2e % Copyright -- 1994 Peter Williams % % E-mail pwil3058@bigpond.net.au % % This style file is free software; you can redistribute it and/or % modify it under the terms of the GNU Lesser General Public % License as published by the Free Software Foundation; either % version 2 of the License, or (at your option) any later version. % % This style file is distributed in the hope that it will be useful, % but WITHOUT ANY WARRANTY; without even the implied warranty of % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU % Lesser General Public License for more details. % % You should have received a copy of the GNU Lesser General Public % License along with this style file; if not, write to the % Free Software Foundation, Inc., 59 Temple Place - Suite 330, % Boston, MA 02111-1307, USA. % \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{algorithm} \typeout{Document Style `algorithm' - floating environment} \RequirePackage{float} \RequirePackage{ifthen} \newcommand{\ALG@within}{nothing} \newboolean{ALG@within} \setboolean{ALG@within}{false} \newcommand{\ALG@floatstyle}{ruled} \newcommand{\ALG@name}{Algorithm} \newcommand{\listalgorithmname}{List of \ALG@name s} % Declare Options % first appearance \DeclareOption{plain}{ \renewcommand{\ALG@floatstyle}{plain} } \DeclareOption{ruled}{ \renewcommand{\ALG@floatstyle}{ruled} } \DeclareOption{boxed}{ \renewcommand{\ALG@floatstyle}{boxed} } % then numbering convention \DeclareOption{part}{ \renewcommand{\ALG@within}{part} \setboolean{ALG@within}{true} } \DeclareOption{chapter}{ \renewcommand{\ALG@within}{chapter} \setboolean{ALG@within}{true} } \DeclareOption{section}{ \renewcommand{\ALG@within}{section} \setboolean{ALG@within}{true} } \DeclareOption{subsection}{ \renewcommand{\ALG@within}{subsection} \setboolean{ALG@within}{true} } \DeclareOption{subsubsection}{ \renewcommand{\ALG@within}{subsubsection} \setboolean{ALG@within}{true} } \DeclareOption{nothing}{ \renewcommand{\ALG@within}{nothing} \setboolean{ALG@within}{true} } \DeclareOption*{\edef\ALG@name{\CurrentOption}} % ALGORITHM % \ProcessOptions \floatstyle{\ALG@floatstyle} \ifthenelse{\boolean{ALG@within}}{ \ifthenelse{\equal{\ALG@within}{part}} {\newfloat{algorithm}{htbp}{loa}[part]}{} \ifthenelse{\equal{\ALG@within}{chapter}} {\newfloat{algorithm}{htbp}{loa}[chapter]}{} \ifthenelse{\equal{\ALG@within}{section}} {\newfloat{algorithm}{htbp}{loa}[section]}{} \ifthenelse{\equal{\ALG@within}{subsection}} {\newfloat{algorithm}{htbp}{loa}[subsection]}{} \ifthenelse{\equal{\ALG@within}{subsubsection}} {\newfloat{algorithm}{htbp}{loa}[subsubsection]}{} \ifthenelse{\equal{\ALG@within}{nothing}} {\newfloat{algorithm}{htbp}{loa}}{} }{ \newfloat{algorithm}{htbp}{loa} } \floatname{algorithm}{\ALG@name} \newcommand{\listofalgorithms}{\listof{algorithm}{\listalgorithmname}} openimageio-1.7.17~dfsg0.orig/src/doc/glossary.tex0000644000175000017500000000544613151711064020257 0ustar mfvmfv\chapter{Glossary} \begin{description} \item[Channel] One of several data values persent in each pixel. Examples include red, green, blue, alpha, etc. The data in one channel of a pixel may be represented by a single number, whereas the pixel as a whole requires one number for each channel. \item[Client] A client (as in ``client application'') is a program or library that uses \product or any of its constituent libraries. \item[Data format] The type of numerical representation used to store a piece of data. Examples include 8-bit unsigned integers, 32-bit floating-point numbers, etc. \item[Image File Format] The specification and data layout of an image on disk. For example, TIFF, JPEG/JFIF, OpenEXR, etc. \item[Image Format Plugin] A DSO/DLL that implements the \ImageInput and \ImageOutput classes for a particular image file format. \item[Format Plugin] See \emph{image format plugin}. \item[Metadata] Data about data. As used in \product, this means Information about an image, beyond describing the values of the pixels themselves. Examples include the name of the artist that created the image, the date that an image was scanned, the camera settings used when a photograph was taken, etc. \item[Native data format] The \emph{data format} used in the disk file representing an image. Note that with \product, this may be different than the data format used by an application to store the image in the computer's RAM. \item[Pixel] One pixel element of an image, consisting of one number describing each \emph{channel} of data at a particular location in an image. \item[Plugin] See \emph{image format plugin}. \item[Scanline] A single horizontal row of pixels of an image. See also \emph{tile}. \item[Scanline Image] An image whose data layout on disk is organized by breaking the image up into horizontal scanlines, typically with the ability to read or write an entire scanline at once. See also \emph{tiled image}. \item[Tile] A rectangular region of pixels of an image. A rectangular tile is more spatially coherent than a scanline that stretches across the entire image --- that is, a pixel's neighbors are most likely in the same tile, whereas a pixel in a scanline image will typically have most of its immediate neighbors on different scanlines (requiring additional scanline reads in order to access them). \item[Tiled Image] An image whose data layout on disk is organized by breaking the image up into rectangular regions of pixels called \emph{tiles}. All the pixels in a tile can be read or written at once, and individual tiles may be read or written separately from other tiles. \item[Volume Image] A 3-D set of pixels that has not only horizontal and vertical dimensions, but also a "depth" dimension. \end{description} \chapwidthend openimageio-1.7.17~dfsg0.orig/src/doc/mainpage.h0000644000175000017500000000611213151711064017613 0ustar mfvmfv// Comments that don't go anywhere in the source code, but help to // generate the "main page" or other docs for doxygen. namespace OpenImageIO { /** @mainpage OpenImageIO Programmer Documentation @section imageio_sec Basic Image Input/Output and Helper Classes The core of OpenImageIO consists of simple classes for format-agnostic reading and writing of image files: \li ImageInput is the public API that allows you to read images. \li ImageOutput is the public API that allows you to write images. \li ImageSpec is a helper class that describes an image (resolution, data format, metadata, etc.). @section helper_sec Helper Classes used by the main APIs \li TypeDesc describes data types and is used to describe channel formats and many other things throughout %OpenImageIO. (\ref typedesc.h) \li ParamValueList and ParamValue are used for storing arbitrary name/data pairs and are used to store metadata. (\ref paramlist.h) \li \ref ustring is string class that's especially suitable for super fast string copying and comparison (== and !=) and that stores the character strings uniquely. (\ref ustring.h) @section ic_and_ts_sec Cached Images and Filtered Texture \li ImageCache provides a way to access an unlimited number of images and amount of pixel data (thousands of files, hundreds of GB of pixels) using a read-on-demand cache system that uses as little as several tens of MB of RAM. \li ImageBuf is a handy method for manipulating image pixels in memory, completely hiding even the details of reading, writing, and memory management (by being based internally upon ImageInput, ImageOutput, and ImageCache). \li TextureSystem is an API for performing filtered anisotropic texture lookups (backed by ImageCache, so it can easily scale to essentially unlimited number of texture files and/or pixel data). @section gifts_sec Gifts for developers These classes and utilities are not exposed through any of the public OIIO APIs and are not necessary to understand even for people who are integrating OIIO into their applications. But we like them, feel that they are pretty useful, and so distribute them so that OIIO users may rely on them in their own apps. \li \ref argparse.h : The ArgParse class that privides a really simple way to parse command-line arguments. \li \ref dassert.h : Handy assertion macros. \li errorhandler.h : An ErrorHandler class. \li \ref filesystem.h : Platform-independent utilties for handling file names, etc. \li \ref fmath.h : Lots of numeric utilities. \li \ref hash.h : Definitions helpful for using hash maps and hash functions. \li \ref paramlist.h : The ParamValue and ParamValueList classes. \li \ref refcnt.h : A "mix-in" class for intrusive reference counting. \li \ref strutil.h : String utilities. \li \ref sysutil.h : Platform-independent OS, hardware, and system utilities. \li \ref thread.h : Threading, mutexes, atomics, etc. \li \ref timer.h : A simple \ref Timer class. \li \ref typedesc.h : The TypeDesc class. \li \ref ustring.h : The ustring class. \li \ref varyingref.h : The VaryingRef template. */ }; openimageio-1.7.17~dfsg0.orig/src/doc/stdmetadata.tex0000644000175000017500000007137613151711064020714 0ustar mfvmfv\chapter{Metadata conventions} \label{chap:stdmetadata} The \ImageSpec class, described thoroughly in Section~\ref{sec:ImageSpec}, provides the basic description of an image that are essential across all formats --- resolution, number of channels, pixel data format, etc. Individual images may have additional data, stored as name/value pairs in the {\cf extra_attribs} field. Though literally \emph{anything} can be stored in {\cf extra_attribs} --- it's specifically designed for format- and user-extensibility --- this chapter establishes some guidelines and lays out all of the field names that \product understands. \section{Description of the image} \apiitem{"ImageDescription" : string} The image description, title, caption, or comments. \apiend \apiitem{"Keywords" : string} Semicolon-separated keywords describing the contents of the image. (Semicolons are used rather than commas because of the common case of a comma being part of a keyword itself, e.g., ``Kurt Vonnegut, Jr.'' or ``Washington, DC.'') \apiend \apiitem{"Artist" : string} The artist, creator, or owner of the image. \apiend \apiitem{"Copyright" : string} Any copyright notice or owner of the image. \apiend \apiitem{"DateTime" : string} The creation date of the image, in the following format: {\cf YYYY:MM:DD HH:MM:SS} (exactly 19 characters long, not including a terminating NULL). For example, 7:30am on Dec 31, 2008 is encoded as \qkw{2008:12:31 07:30:00}. Usually this is simply the time that the image data was last modified. It may be wise to also store the \qkw{Exif:DateTimeOriginal} and \qkw{Exif:DateTimeDigitized} (see Section~\ref{sec:metadata:exif}) to further distinguish the original image and its conversion to digital media. \apiend \apiitem{"DocumentName" : string} The name of an overall document that this image is a part of. \apiend \apiitem{"Software" : string} The software that was used to create the image. \apiend \apiitem{"HostComputer" : string} The name or identity of the computer that created the image. \apiend \section{Display hints} \label{metadata:displayhints} \apiitem{"oiio:ColorSpace" : string} The name of the color space of the color channels. Values incude: \qkw{Linear}, \qkw{sRGB}, \qkw{GammaCorrected}, \qkw{AdobeRGB}, \qkw{Rec709}, and \qkw{KodakLog}. \apiend \apiitem{"oiio:Gamma" : float} If the color space is \qkw{GammaCorrected}, this value is the gamma exponent. \apiend \apiitem{"oiio:BorderColor" : float[nchannels]} The color presumed to be filling any parts of the display/full image window that are not overlapping the pixel data window. If not supplied, the default is black (0 in all channels). \apiend \apiitem{"Orientation" : int} \index{Orientation} \label{metadata:orientation} By default, image pixels are ordered from the top of the display to the bottom, and within each scanline, from left to right (i.e., the same ordering as English text and scan progression on a CRT). But the \qkw{Orientation} field can suggest that it should be displayed with a different orientation, according to the TIFF/EXIF conventions: \begin{tabular}{p{0.3in} p{4in}} 1 & normal (top to bottom, left to right) \\ 2 & flipped horizontally (top to botom, right to left) \\ 3 & rotated $180^\circ$ (bottom to top, right to left) \\ 4 & flipped vertically (bottom to top, left to right) \\ 5 & transposed (left to right, top to bottom) \\ 6 & rotated $90^\circ$ clockwise (right to left, top to bottom) \\ 7 & transverse (right to left, bottom to top) \\ 8 & rotated $90^\circ$ counter-clockwise (left to right, bottom to top) \\ \end{tabular} \apiend \apiitem{"PixelAspectRatio" : float} The aspect ratio ($x/y$) of the size of individual pixels, with square pixels being 1.0 (the default). \apiend \apiitem{"XResolution" : float \\ "YResolution" : float \\ "ResolutionUnit" : string} The number of horizontal ($x$) and vertical ($y$) pixels per resolution unit. This ties the image to a physical size (where applicable, such as with a scanned image, or an image that will eventually be printed). Different file formats may dictate different resolution units. For example, the TIFF ImageIO plugin supports \qkw{none}, \qkw{in}, and \qkw{cm}. \apiend \apiitem{"ICCProfile" : uint8[]} The ICC color profile takes the form of an array of bytes (unsigned 8 bit chars). The length of the array is the length of the profile blob. \apiend \apiitem{"oiio:Movie" : int} If nonzero, a hint that a multi-image file is meant to be interpreted as an animation (i.e., that the subimages are a time sequence). \apiend \apiitem{"FramesPerSecond" : float} For a multi-image file intended to be played back as an animation, the frame refresh rate. \apiend \section{Disk file format info/hints} \apiitem{"oiio:BitsPerSample" : int} Number of bits per sample \emph{in the file}. Note that this may not match the reported {\cf ImageSpec::format}, if the plugin is translating from an unsupported format. For example, if a file stores 4 bit grayscale per channel, the {\cf "oiio:BitsPerSample"} may be 4 but the {\cf format} field may be {\cf TypeDesc::UINT8} (because the \product APIs do not support fewer than 8 bits per sample). \apiend \apiitem{"oiio:UnassociatedAlpha" : int} Whether the data in the file stored alpha channels (if any) that were unassociated with the color (i.e., color not ``premultiplied'' by the alpha coverage value). \apiend \apiitem{"planarconfig" : string} \qkw{contig} indicates that the file has contiguous pixels (RGB RGB RGB...), whereas \qkw{separate} indicate that the file stores each channel separately (RRR...GGG...BBB...). Note that only contiguous pixels are transmitted through the \product APIs, but this metadata indicates how it is (or should be) stored in the file, if possible. \apiend \apiitem{"compression" : string} Indicates the type of compression the file uses. Supported compression modes will vary from \ImageInput plugin to plugin, and each plugin should document the modes it supports. If {\cf ImageInput::open} is called with an \ImageSpec that specifies an compression mode not supported by that \ImageInput, it will choose a reasonable default. As an example, the TIFF \ImageInput plugin supports \qkw{none}, \qkw{lzw}, \qkw{ccittrle}, \qkw{zip} (the default), \qkw{packbits}. \apiend \apiitem{"CompressionQuality" : int} Indicates the quality of compression to use (0--100), for those plugins and compression methods that allow a variable amount of compression, with higher numbers indicating higher image fidelity. \apiend \section{Photographs or scanned images} The following metadata items are specific to photos or captured images. \apiitem{"Make" : string} For captured or scanned image, the make of the camera or scanner. \apiend \apiitem{"Model" : string} For captured or scanned image, the model of the camera or scanner. \apiend \apiitem{"ExposureTime" : float} The exposure time (in seconds) of the captured image. \apiend \apiitem{"FNumber" : float} The f/stop of the camera when it captured the image. \apiend \section{Texture Information} Several standard metadata are very helpful for images that are intended to be used as textures (especially for \product's \TextureSystem). \apiitem{"textureformat" : string} The kind of texture that this image is intended to be. We suggest the following names: \noindent \begin{tabular}{p{1.75in} p{3.25in}} \qkw{Plain Texture} & Ordinary 2D texture \\ \qkw{Volume Texture} & 3D volumetric texture \\ \qkw{Shadow} & Ordinary $z$-depth shadow map \\ \qkw{CubeFace Shadow} & Cube-face shadow map \\ \qkw{Volume Shadow} & Volumetric (``deep'') shadow map \\ \qkw{LatLong Environment} & Latitude-longitude (rectangular) environment map \\ \qkw{CubeFace Environment} & Cube-face environment map \\ \end{tabular} \apiend \apiitem{"wrapmodes" : string} Give the intended texture \emph{wrap mode} indicating what happens with texture coordinates outside the $[0...1]$ range. We suggest the following names: \qkw{black}, \qkw{periodic}, \qkw{clamp}, \qkw{mirror}. If the wrap mode is different in each direction, they should simply be separated by a comma. For example, \qkw{black} means black wrap in both directions, whereas \qkw{clamp,periodic} means to clamp in $u$ and be periodic in $v$. \apiend \apiitem{"fovcot" : float} The cotangent ($x/y$) of the field of view of the original image (which may not be the same as the aspect ratio of the pixels of the texture, which may have been resized). \apiend \apiitem{"worldtocamera" : matrix44} For shadow maps or rendered images this item (of type {\cf TypeDesc::PT_MATRIX}) is the world-to-camera matrix describing the camera position. \apiend \apiitem{"worldtoscreen" : matrix44} For shadow maps or rendered images this item (of type {\cf TypeDesc::PT_MATRIX}) is the world-to-screen matrix describing the full projection of the 3D view onto a $[-1...1] \times [-1...1]$ 2D domain. \apiend \apiitem{"oiio:updirection" : string} For environment maps, indicates which direction is ``up'' (valid values are \qkw{y} or \qkw{z}), to disambiguate conventions for environment map orientation. \apiend \apiitem{"oiio:sampleborder" : int} If not present or 0 (the default), the conversion from pixel integer coordinates $(i,j)$ to texture coordinates $(s,t)$ follows the usual convention of $s = (i+0.5)/\mathit{xres}$ and $t = (j+0.5)/\mathit{yres}$. However, if this attribute is present and nonzero, the first and last row/column of pixels lie exactly at the $s$ or $t = 0$ or $1$ boundaries, i.e., $s = i/(\mathit{xres}-1)$ and $t = j/(\mathit{yres}-1)$. \apiend \apiitem{"oiio:ConstantColor" : string} If present, is a hint that the texture has the same value in all pixels, and the metadata value is a string containing the channel values as a comma-separated list (no spaces, for example: \qkw{0.73,0.9,0.11,1.0}). \apiend \apiitem{"oiio:AverageColor" : string} If present, is a hint giving the \emph{average} value of all pixels in the texture, as a string containing a comma-separated list of the channel values (no spaces, for example: \qkw{0.73,0.9,0.11,1.0}). \apiend \apiitem{"oiio:SHA-1" : string} If present, is a 40-byte SHA-1 hash of the input image (possibly salted with various maketx options) that can serve to quickly compare two separate textures to know if they contain the same pixels. While it's not, technically, 100\% guaranteed that no separate textures will match, it's so astronomically unlikely that we discount the possibility (you'd be rendering movies for centuries before finding a single match). \apiend \section{Exif metadata} \label{sec:metadata:exif} \index{Exif metadata} % FIXME -- unsupported/undocumented: ExifVersion, FlashpixVersion, % ComponentsConfiguration, MakerNote, UserComment, RelatedSoundFile, % OECF, SubjectArea, SpatialFrequencyResponse, % CFAPattern, DeviceSettingDescription % % SubjectLocation -- unsupported, but we could do it The following Exif metadata tags correspond to items in the ``standard'' set of metadata. \medskip \begin{tabular}{p{1.5in} p{3.5in}} {\bf Exif tag} & {\bf \product metadata convention} \\ \hline ColorSpace & (reflected in \qkw{oiio:ColorSpace}) \\ ExposureTime & \qkw{ExposureTime} \\ FNumber & \qkw{FNumber} \\ \end{tabular} \medskip The other remaining Exif metadata tags all include the ``Exif:'' prefix to keep it from clashing with other names that may be used for other purposes. \apiitem{"Exif:ExposureProgram" : int} The exposure program used to set exposure when the picture was taken: \smallskip \begin{tabular}{p{0.3in} p{4in}} 0 & unknown \\ 1 & manual \\ 2 & normal program \\ 3 & aperture priority \\ 4 & shutter priority \\ 5 & Creative program (biased toward depth of field) \\ 6 & Action program (biased toward fast shutter speed) \\ 7 & Portrait mode (closeup photo with background out of focus) \\ 8 & Landscape mode (background in focus) \end{tabular} \apiend \apiitem{"Exif:SpectralSensitivity" : string} The camera's spectral sensitivity, using the ASTM conventions. \apiend \apiitem{"Exif:ISOSpeedRatings" : int} The ISO speed and ISO latitude of the camera as specified in ISO 12232. \apiend %\apiitem{"Exif:OECF", TIFF_NOTYPE } // skip it %\apiitem{"Exif:ExifVersion", TIFF_NOTYPE } // skip it \apiitem{"Exif:DateTimeOriginal" : string \\ "Exif:DateTimeDigitized" : string} Date and time that the original image data was generated or captured, and the time/time that the image was stored as digital data. Both are in \qkw{YYYY:MM:DD HH:MM:SS} format. To clarify the role of these (and also with respect to the standard \qkw{DateTime} metadata), consider an analog photograph taken in 1960 ({\cf Exif:DateTimeOriginal}), which was scanned to a digital image in 2010 ({\cf Exif:DateTimeDigitized}), and had color corrections or other alterations performed in 2015 ({\cf DateTime}). \apiend %\apiitem{"Exif:ComponentsConfiguration",TIFF_UNDEFINED } \apiitem{"Exif:CompressedBitsPerPixel" : float } The compression mode used, measured in compressed bits per pixel. \apiend \apiitem{"Exif:ShutterSpeedValue" : float } Shutter speed, in APEX units: $-\log_2 (\mathit{exposure time})$ \apiend \apiitem{"Exif:ApertureValue" : float } Aperture, in APEX units: $2 \log_2 (\mathit{fnumber})$ \apiend \apiitem{"Exif:BrightnessValue" : float } Brightness value, assumed to be in the range of $-99.99$ -- $99.99$. \apiend \apiitem{"Exif:ExposureBiasValue" : float } Exposure bias, assumed to be in the range of $-99.99$ -- $99.99$. \apiend \apiitem{"Exif:MaxApertureValue" : float } Smallest F number of the lens, in APEX units: $2 \log_2 (\mathit{fnumber})$ \apiend \apiitem{"Exif:SubjectDistance" : float } Distance to the subject, in meters. \apiend \apiitem{"Exif:MeteringMode" : int} The metering mode: \smallskip \begin{tabular}{p{0.3in} p{4in}} 0 & unknown \\ 1 & average \\ 2 & center-weighted average \\ 3 & spot \\ 4 & multi-spot \\ 5 & pattern \\ 6 & partial \\ 255 & other \end{tabular} \apiend \apiitem{"Exif:LightSource" : int} The kind of light source: \smallskip \begin{tabular}{p{0.3in} p{4in}} 0 & unknown \\ 1 & daylight \\ 2 & tungsten (incandescent light) \\ 4 & flash \\ 9 & fine weather \\ 10 & cloudy weather \\ 11 & shade \\ 12 & daylight fluorescent (D 5700-7100K) \\ 13 & day white fluorescent (N 4600-5400K) \\ 14 & cool white fuorescent (W 3900 - 4500K) \\ 15 & white fluorescent (WW 3200 - 3700K) \\ 17 & standard light A \\ 18 & standard light B \\ 19 & standard light C \\ 20 & D55 \\ 21 & D65 \\ 22 & D75 \\ 23 & D50 \\ 24 & ISO studio tungsten \\ 255 & other light source \end{tabular} \apiend \apiitem{"Exif:Flash" int} A sum of: \smallskip \begin{tabular}{p{0.3in} p{4in}} 1 & if the flash fired \\ \hline 0 & no strobe return detection function \\ 4 & strobe return light was not detected \\ 6 & strobe return light was detected \\ \hline 8 & compulsary flash firing \\ 16 & compulsary flash supression \\ 24 & auto-flash mode \\ \hline 32 & no flash function (0 if flash function present) \\ \hline 64 & red-eye reduction supported (0 if no red-eye reduction mode) \end{tabular} \apiend \apiitem{"Exif:FocalLength" : float } Actual focal length of the lens, in mm. \apiend \apiitem{"Exif:SecurityClassification" : string} Security classification of the image: `C' = confidential, `R' = restricted, `S' = secret, `T' = top secret, `U' = unclassified. \apiend \apiitem{"Exif:ImageHistory" : string} Image history. \apiend %\apiitem{"Exif:SubjectArea",TIFF_NOTYPE } // skip %\apiitem{"Exif:MakerNote",TIFF_NOTYPE } // skip it %\apiitem{"Exif:UserComment",TIFF_NOTYPE }// skip it \apiitem{"Exif:SubsecTime" : string} Fractions of a second to augment the \qkw{DateTime} (expressed as text of the digits to the right of the decimal). \apiend \apiitem{"Exif:SubsecTimeOriginal" : string} Fractions of a second to augment the \qkw{Exif:DateTimeOriginal} (expressed as text of the digits to the right of the decimal). \apiend \apiitem{"Exif:SubsecTimeDigitized" : string} Fractions of a second to augment the \qkw{Exif:DateTimeDigital} (expressed as text of the digits to the right of the decimal). \apiend %\apiitem{"Exif:FlashPixVersion",TIFF_NOTYPE } \apiitem{"Exif:PixelXDimension" : int \\ "Exif:PixelYDimension" : int } The $x$ and $y$ dimensions of the valid pixel area. FIXME -- better explanation? \apiend %\apiitem{"Exif:RelatedSoundFile", TIFF_NOTYPE }// skip \apiitem{"Exif:FlashEnergy" : float } Strobe energy when the image was captures, measured in Beam Candle Power Seconds (BCPS). \apiend %\apiitem{"Exif:SpatialFrequencyResponse",TIFF_NOTYPE } \apiitem{"Exif:FocalPlaneXResolution" : float \\ "Exif:FocalPlaneYResolution" : float \\ "Exif:FocalPlaneResolutionUnit" : int} The number of pixels in the $x$ and $y$ dimension, per resolution unit. The codes for resolution units are: \smallskip \begin{tabular}{p{0.3in} p{4in}} 1 & none \\ 2 & inches \\ 3 & cm \\ 4 & mm \\ 5 & $\mu$m \\ \end{tabular} \apiend %\apiitem{"Exif:SubjectLocation" : int} // FIXME: short[2] \apiitem{"Exif:ExposureIndex" : float } The exposure index selected on the camera. \apiend \apiitem{"Exif:SensingMethod" : int} The image sensor type on the camra: \smallskip \begin{tabular}{p{0.3in} p{4in}} 1 & undefined \\ 2 & one-chip color area sensor \\ 3 & two-chip color area sensor \\ 4 & three-chip color area sensor \\ 5 & color sequential area sensor \\ 7 & trilinear sensor \\ 8 & color trilinear sensor \end{tabular} \apiend \apiitem{"Exif:FileSource" : int} The source type of the scanned image, if known: \smallskip \begin{tabular}{p{0.3in} p{4in}} 1 & film scanner \\ 2 & reflection print scanner \\ 3 & digital camera \\ \end{tabular} \apiend \apiitem{"Exif:SceneType" : int} Set to 1, if a directly-photographed image, otherwise it should not be present. \apiend %\apiitem{"Exif:CFAPattern",TIFF_NOTYPE } \apiitem{"Exif:CustomRendered" : int} Set to 0 for a normal process, 1 if some custom processing has been performed on the image data. \apiend \apiitem{"Exif:ExposureMode" : int} The exposure mode: \smallskip \begin{tabular}{p{0.3in} p{4in}} 0 & auto \\ 1 & manual \\ 2 & auto-bracket \end{tabular} \apiend \apiitem{"Exif:WhiteBalance" : int} Set to 0 for auto white balance, 1 for manual white balance. \apiend \apiitem{"Exif:DigitalZoomRatio" : float } The digital zoom ratio used when the image was shot. \apiend \apiitem{"Exif:FocalLengthIn35mmFilm" : int} The equivalent focal length of a 35mm camera, in mm. \apiend \apiitem{"Exif:SceneCaptureType" : int} The type of scene that was shot: \smallskip \begin{tabular}{p{0.3in} p{4in}} 0 & standard \\ 1 & landscape \\ 2 & portrait \\ 3 & night scene \end{tabular} \apiend \apiitem{"Exif:GainControl" : float } The degree of overall gain adjustment: \smallskip \begin{tabular}{p{0.3in} p{4in}} 0 & none \\ 1 & low gain up \\ 2 & high gain up \\ 3 & low gain down \\ 4 & high gain down \end{tabular} \apiend \apiitem{"Exif:Contrast" : int} The direction of contrast processing applied by the camera: \smallskip \begin{tabular}{p{0.3in} p{4in}} 0 & normal \\ 1 & soft \\ 2 & hard \end{tabular} \apiend \apiitem{"Exif:Saturation" : int} The direction of saturation processing applied by the camera: \smallskip \begin{tabular}{p{0.3in} p{4in}} 0 & normal \\ 1 & low saturation \\ 2 & high saturation \end{tabular} \apiend \apiitem{"Exif:Sharpness" : int} The direction of sharpness processing applied by the camera: \smallskip \begin{tabular}{p{0.3in} p{4in}} 0 & normal \\ 1 & soft \\ 2 & hard \end{tabular} \apiend %\apiitem{"Exif:DeviceSettingDescription",TIFF_NOTYPE } \apiitem{"Exif:SubjectDistanceRange" : int} The distance to the subject: \smallskip \begin{tabular}{p{0.3in} p{4in}} 0 & unknown \\ 1 & macro \\ 2 & close \\ 3 & distant \end{tabular} \apiend \apiitem{"Exif:ImageUniqueID" : string} A unique identifier for the image, as 16 ASCII hexidecimal digits representing a 128-bit number. \apiend \section{GPS Exif metadata} \label{sec:metadata:GPS} \index{GPS metadata} The following GPS-related Exif metadata tags correspond to items in the ``standard'' set of metadata. %\apiitem{"GPS:VersionID" : int} %\apiend \apiitem{"GPS:LatitudeRef" : string} Whether the \qkw{GPS:Latitude} tag refers to north or south: \qkw{N} or \qkw{S}. \apiend \apiitem{"GPS:Latitude" : float[3]} The degrees, minutes, and seconds of latitude (see also \qkw{GPS:LatitudeRef}). \apiend \apiitem{"GPS:LongitudeRef" : string} Whether the \qkw{GPS:Longitude} tag refers to east or west: \qkw{E} or \qkw{W}. \apiend \apiitem{"GPS:Longitude" : float[3]} The degrees, minutes, and seconds of longitude (see also \qkw{GPS:LongitudeRef}). \apiend \apiitem{"GPS:AltitudeRef" : string} A value of 0 indicates that the altitude is above sea level, 1 indicates below sea level. \apiend \apiitem{"GPS:Altitude" : float} Absolute value of the altitude, in meters, relative to sea level (see \qkw{GPS:AltitudeRef} for whether it's above or below sea level). \apiend \apiitem{"GPS:TimeStamp" : float[3]} Gives the hours, minutes, and seconds, in UTC. \apiend \apiitem{"GPS:Satellites" : string} Information about what satellites were visible. \apiend \apiitem{"GPS:Status" : string} \qkw{A} indicates a measurement in progress, \qkw{V} indicates measurement interoperability. \apiend \apiitem{"GPS:MeasureMode" : string} \qkw{2} indicates a 2D measurement, \qkw{3} indicates a 3D measurement. \apiend \apiitem{"GPS:DOP" : float} Data degree of precision. \apiend \apiitem{"GPS:SpeedRef" : string} Indicates the units of the related \qkw{GPS:Speed} tag: \qkw{K} for km/h, \qkw{M} for miles/h, \qkw{N} for knots. \apiend \apiitem{"GPS:Speed" : float} Speed of the GPS receiver (see \qkw{GPS:SpeedRef} for the units). \apiend \apiitem{"GPS:TrackRef" : string} Describes the meaning of the \qkw{GPS:Track} field: \qkw{T} for true direction, \qkw{M} for magnetic direction. \apiend \apiitem{"GPS:Track" : float} Direction of the GPS receiver movement (from 0--359.99). The related \qkw{GPS:TrackRef} indicate whether it's true or magnetic. \apiend \apiitem{"GPS:ImgDirectionRef" : string} Describes the meaning of the \qkw{GPS:ImgDirection} field: \qkw{T} for true direction, \qkw{M} for magnetic direction. \apiend \apiitem{"GPS:ImgDirection" : float} Direction of the image when captured (from 0--359.99). The related \qkw{GPS:ImgDirectionRef} indicate whether it's true or magnetic. \apiend \apiitem{"GPS:MapDatum" : string} The geodetic survey data used by the GPS receiver. \apiend \apiitem{"GPS:DestLatitudeRef" : string} Whether the \qkw{GPS:DestLatitude} tag refers to north or south: \qkw{N} or \qkw{S}. \apiend \apiitem{"GPS:DestLatitude" : float[3]} The degrees, minutes, and seconds of latitude of the destination (see also \qkw{GPS:DestLatitudeRef}). \apiend \apiitem{"GPS:DestLongitudeRef" : string} Whether the \qkw{GPS:DestLongitude} tag refers to east or west: \qkw{E} or \qkw{W}. \apiend \apiitem{"GPS:DestLongitude" : float[3]} The degrees, minutes, and seconds of longitude of the destination (see also \qkw{GPS:DestLongitudeRef}). \apiend \apiitem{"GPS:DestBearingRef" : string} Describes the meaning of the \qkw{GPS:DestBearing} field: \qkw{T} for true direction, \qkw{M} for magnetic direction. \apiend \apiitem{"GPS:DestBearing" : float} Bearing to the destination point (from 0--359.99). The related \qkw{GPS:DestBearingRef} indicate whether it's true or magnetic. \apiend \apiitem{"GPS:DestDistanceRef" : string} Indicates the units of the related \qkw{GPS:DestDistance} tag: \qkw{K} for km, \qkw{M} for miles, \qkw{N} for knots. \apiend \apiitem{"GPS:DestDistance" : float} Distance to the destination (see \qkw{GPS:DestDistanceRef} for the units). \apiend \apiitem{"GPS:ProcessingMethod" : string} Processing method information. \apiend \apiitem{"GPS:AreaInformation" : string} Name of the GPS area. \apiend \apiitem{"GPS:DateStamp" : string} Date according to the GPS device, in format \qkw{YYYY:MM:DD}. \apiend \apiitem{"GPS:Differential" : int} If 1, indicates that differential correction was applied. \apiend \apiitem{"GPS:HPositioningError" : float} Positioning error. \apiend \section{IPTC metadata} \label{sec:metadata:iptc} \index{IPTC metadata} The IPTC (International Press Telecommunications Council) publishes conventions for storing image metadata, and this standard is growing in popularity and is commonly used in photo-browsing programs to record captions and keywords. The following IPTC metadata items correspond exactly to metadata in the \product conventions, so it is recommended that you use the standards and that plugins supporting IPTC metadata respond likewise: \medskip \begin{tabular}{p{1.5in} p{3.5in}} {\bf IPTC tag} & {\bf \product metadata convention} \\ \hline Caption & \qkw{ImageDescription} \\[0.5ex] Keyword & IPTC keywords should be concatenated, separated by semicolons ({\cf ;}), and stored as the \qkw{Keywords} attribute. \\[0.5ex] ExposureTime & \qkw{ExposureTime} \\[0.5ex] CopyrightNotice & \qkw{Copyright} \\[0.5ex] Creator & \qkw{Artist} \\ \end{tabular} \bigskip The remainder of IPTC metadata fields should use the following names, prefixed with ``IPTC:'' to avoid conflicts with other plugins or standards. \apiitem{"IPTC:ObjecTypeReference" : string} Object type reference. \apiend \apiitem{"IPTC:ObjectAttributeReference" : string} Object attribute reference. \apiend \apiitem{"IPTC:ObjectName" : string} The name of the object in the picture. \apiend \apiitem{"IPTC:EditStatus" : string} Edit status. \apiend \apiitem{"IPTC:SubjectReference" : string} Subject reference. \apiend \apiitem{"IPTC:Category" : string} Category. \apiend % \apiitem{ 25, "Keywords" : string} %\apiend % FIXME \apiitem{"IPTC:ContentLocationCode" : string} Code for content location. \apiend \apiitem{"IPTC:ContentLocationName" : string} Name of content location. \apiend \apiitem{"IPTC:ReleaseDate" : string \\ "IPTC:ReleaseTime" : string} Release date and time. \apiend \apiitem{"IPTC:ExpirationDate" : string \\ "IPTC:ExpirationTime" : string} Expiration date and time. \apiend \apiitem{"IPTC:Instructions" : string} Special instructions for handling the image. \apiend \apiitem{"IPTC:ReferenceService" : string \\ "IPTC:ReferenceDate" : string \\ "IPTC:ReferenceNumber" : string} Reference date, service, and number. \apiend \apiitem{"IPTC:DateCreated" : string \\ "IPTC:TimeCreated" : string} Date and time that the image was created. \apiend \apiitem{"IPTC:DigitalCreationDate" : string \\ "IPTC:DigitalCreationTime" : string} Date and time that the image was digitized. \apiend %\apiitem{"IPTC:Creator" : string} %The creator of the image. This is optinal and, If present, %is expected to be the same as the data in the standard \qkw{Artist} field. %\apiend \apiitem{"IPTC:ProgramVersion" : string} The version number of the creation software. \apiend \apiitem{"IPTC:AuthorsPosition" : string} The job title or position of the creator of the image. \apiend \apiitem{"IPTC:City" : string \\ "IPTC:Sublocation" : string \\ "IPTC:State" : string \\ "IPTC:Country" : string \\ "IPTC:CountryCode" : string} The city, sublocation within the city, state, country, and country code of the location of the image. \apiend \apiitem{"IPTC:Headline" : string} Any headline that is meant to accompany the image. \apiend \apiitem{"IPTC:Provider" : string} The provider of the image, or credit line. \apiend \apiitem{"IPTC:Source" : string} The source of the image. \apiend %\apiitem{"IPTC:CopyrightNotice" : string} %The copyright notice for the image. This is optinal and, If present, %is expected to be the same as the data in the standard \qkw{Copyright} field. %\apiend \apiitem{"IPTC:Contact" : string} The contact information for the image (possibly including name, address, email, etc.). \apiend %\apiitem{"IPTC:Caption", string } %The caption, abstract, or description of the image. %This is optional and, if present, is expected to be the same as the %data in the standard \qkw{ImageDescription} field. %\apiend \apiitem{"IPTC:CaptionWriter" : string} The name of the person who wrote the caption or description of the image. \apiend \apiitem{"IPTC:JobID" : string \\ "IPTC:MasterDocumentID" : string \\ "IPTC:ShortDocumentID" : string \\ "IPTC:UniqueDocumentID" : string \\ "IPTC:OwnerID" : string } Various identification tags. \apiend \apiitem{"IPTC:Prefs" : string \\ "IPTC:ClassifyState" : string \\ "IPTC:SimilarityIndex" : string} Who knows what the heck these are? \apiend \apiitem{"IPTC:DocumentNotes" : string} Notes about the image or document. \apiend \apiitem{"IPTC:DocumentHistory" : string} The history of the image or document. \apiend \section{SMPTE metadata} \label{sec:metadata:smpte} \index{SMPTE metadata} \apiitem{"smpte:TimeCode" : int[2]} SMPTE time code, encoded as an array of 2 32-bit integers (as a \TypeDesc, it will be tagged with vecsemantics {\cf TIMECODE}). \apiend \apiitem{"smpte:KeyCode" : int[7]} SMPTE key code, encoded as an array of 7 32-bit integers (as a \TypeDesc, it will be tagged with vecsemantics {\cf KEYCODE}). \apiend \section{Extension conventions} To avoid conflicts with other plugins, or with any additional standard metadata names that may be added in future verions of \product, it is strongly advised that writers of new plugins should prefix their metadata with the name of the format, much like the \qkw{Exif:} and \qkw{IPTC:} metadata. \chapwidthend openimageio-1.7.17~dfsg0.orig/src/doc/Doxyfile0000644000175000017500000017427013151711064017402 0ustar mfvmfv# Doxyfile 1.6.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = OpenImageIO # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = src/doc/doxygen # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = YES # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it parses. # With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this tag. # The format is ext=language, where ext is a file extension, and language is one of # the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, # Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat # .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by # doxygen. The layout file controls the global structure of the generated output files # in an output format independent way. The create the layout file that represents # doxygen's defaults, run doxygen with the -l option. You can optionally specify a # file name after the option, if omitted DoxygenLayout.xml will be used as the name # of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = src/include/pystring.h src/include/tbb \ src/include/export.h src/include/filter.h \ src/include/osdep.h src/include/plugin.h \ src/include/SHA1.h \ src/libOpenImageIO src/libtexture src/libutil src/iv # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = *.imageio # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER # are set, an additional index file will be generated that can be used as input for # Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated # HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. # For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's # filter section matches. # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # When the SEARCHENGINE tag is enable doxygen will generate a search box for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP) or Qt help (GENERATE_QHP) # there is already a search function so this one should typically # be disabled. SEARCHENGINE = YES #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = src/doc # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = *.h # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES openimageio-1.7.17~dfsg0.orig/src/doc/.gitignore0000644000175000017500000000010013151711064017640 0ustar mfvmfv*.aux *.idx *.log *.out *.toc /openimageio.ilg /openimageio.ind openimageio-1.7.17~dfsg0.orig/src/doc/writingplugins.tex0000644000175000017500000005570313151711064021502 0ustar mfvmfv\chapter{Writing ImageIO Plugins} \label{chap:writingplugins} \section{Plugin Introduction} \label{sec:pluginintro} As explained in Chapters~\ref{chap:imageinput} and \ref{chap:imageoutput}, the ImageIO library does not know how to read or write any particular image formats, but rather relies on plugins located and loaded dynamically at run-time. This set of plugins, and therefore the set of image file formats that \product or its clients can read and write, is extensible without needing to modify \product itself. This chapter explains how to write your own \product plugins. We will first explain separately how to write image file readers and writers, then tie up the loose ends of how to build the plugins themselves. \section{Image Readers} \label{sec:pluginreaders} A plugin that reads a particular image file format must implement a \emph{subclass} of \ImageInput (described in Chapter~\ref{chap:imageinput}). This is actually very straightforward and consists of the following steps, which we will illustrate with a real-world example of writing a JPEG/JFIF plug-in. \begin{enumerate} \item Read the base class definition from {\fn imageio.h}. It may also be helpful to enclose the contents of your plugin in the same namespace that the \product library uses: \begin{code} #include OIIO_PLUGIN_NAMESPACE_BEGIN ... everything else ... OIIO_PLUGIN_NAMESPACE_END \end{code} \item Declare these public items: \begin{enumerate} \item An integer called \emph{name}{\cf _imageio_version} that identifies the version of the ImageIO protocol implemented by the plugin, defined in {\fn imageio.h} as the constant {\cf OIIO_PLUGIN_VERSION}. This allows the library to be sure it is not loading a plugin that was compiled against an incompatible version of \product. \item An function named \emph{name}{\cf _imageio_library_version} that identifies the underlying dependent library that is responsible for reading or writing the format (it may return {\cf NULL} to indicate that there is no dependent library being used for this format). \item A function named \emph{name}{\cf _input_imageio_create} that takes no arguments and returns a new instance of your \ImageInput subclass. (Note that \emph{name} is the name of your format, and must match the name of the plugin itself.) \item An array of {\cf char *} called \emph{name}{\cf _input_extensions} that contains the list of file extensions that are likely to indicate a file of the right format. The list is terminated by a {\cf NULL} pointer. \end{enumerate} All of these items must be inside an `{\cf extern "C"}' block in order to avoid name mangling by the C++ compiler, and we provide handy macros {\cf OIIO_PLUGIN_EXPORTS_BEGIN} and {\cf OIIO_PLUGIN_EXPORTS_END} to make this easy. Depending on your compiler, you may need to use special commands to dictate that the symbols will be exported in the DSO; we provide a special {\cf OIIO_EXPORT} macro for this purpose, defined in {\fn export.h}. Putting this all together, we get the following for our JPEG example: \begin{code} OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT int jpeg_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT JpgInput *jpeg_input_imageio_create () { return new JpgInput; } OIIO_EXPORT const char *jpeg_input_extensions[] = { "jpg", "jpe", "jpeg", "jif", "jfif", "jfi", NULL }; OIIO_EXPORT const char* jpeg_imageio_library_version () { #define STRINGIZE2(a) #a #define STRINGIZE(a) STRINGIZE2(a) #ifdef LIBJPEG_TURBO_VERSION return "jpeg-turbo " STRINGIZE(LIBJPEG_TURBO_VERSION); #else return "jpeglib " STRINGIZE(JPEG_LIB_VERSION_MAJOR) "." \\ STRINGIZE(JPEG_LIB_VERSION_MINOR); #endif } OIIO_PLUGIN_EXPORTS_END \end{code} \item The definition and implementation of an \ImageInput subclass for this file format. It must publicly inherit \ImageInput, and must overload the following methods which are ``pure virtual'' in the \ImageInput base class: \begin{enumerate} \item {\cf format_name()} should return the name of the format, which ought to match the name of the plugin and by convention is strictly lower-case and contains no whitespace. \item {\cf open()} should open the file and return true, or should return false if unable to do so (including if the file was found but turned out not to be in the format that your plugin is trying to implement). \item {\cf close()} should close the file, if open. \item {\cf read_native_scanline} should read a single scanline from the file into the address provided, uncompressing it but keeping it in its native data format without any translation. \item The virtual destructor, which should {\cf close()} if the file is still open, addition to performing any other tear-down activities. \end{enumerate} Additionally, your \ImageInput subclass may optionally choose to overload any of the following methods, which are defined in the \ImageInput base class and only need to be overloaded if the default behavior is not appropriate for your plugin: \begin{enumerate} \item[(f)] {\cf supports()}, only if your format supports any of the optional features described in Section~\ref{sec:inputsupportsfeaturelist}. \item[(g)] {\cf valid_file()}, if your format has a way to determine if a file is of the given format in a way that is less expensive than a full {\cf open()}. \item[(h)] {\cf seek_subimage()}, only if your format supports reading multiple subimages within a single file. \item[(i)] {\cf read_native_scanlines()}, only if your format has a speed advantage when reading multiple scanlines at once. If you do not supply this function, the default implementation will simply call {\cf read_scanline()} for each scanline in the range. \item[(j)] {\cf read_native_tile()}, only if your format supports reading tiled images. \item[(k)] {\cf read_native_tiles()}, only if your format supports reading tiled images and there is a speed advantage when reading multiple tiles at once. If you do not supply this function, the default implementation will simply call {\cf read_native_tile()} for each tile in the range. \item[(l)] ``Channel subset'' versions of {\cf read_native_scanlines()} and/or {\cf read_native_tiles()}, only if your format has a more efficient means of reading a subset of channels. If you do not supply these methods, the default implementation will simply use {\cf read_native_scanlines()} or {\cf read_native_tiles()} to read into a temporary all-channel buffer and then copy the channel subset into the user's buffer. \item[(m)] {\cf read_native_deep_scanlines()} and/or {\cf read_native_deep_tiles()}, only if your format supports ``deep'' data images. \end{enumerate} Here is how the class definition looks for our JPEG example. Note that the JPEG/JFIF file format does not support multiple subimages or tiled images. \begin{code} class JpgInput : public ImageInput { public: JpgInput () { init(); } virtual ~JpgInput () { close(); } virtual const char * format_name (void) const { return "jpeg"; } virtual bool open (const std::string &name, ImageSpec &spec); virtual bool read_native_scanline (int y, int z, void *data); virtual bool close (); private: FILE *m_fd; bool m_first_scanline; struct jpeg_decompress_struct m_cinfo; struct jpeg_error_mgr m_jerr; void init () { m_fd = NULL; } }; \end{code} \end{enumerate} Your subclass implementation of {\cf open()}, {\cf close()}, and {\cf read_native_scanline()} are the heart of an \ImageInput implementation. (Also {\cf read_native_tile()} and {\cf seek_subimage()}, for those image formats that support them.) The remainder of this section simply lists the full implementation of our JPEG reader, which relies heavily on the open source {\fn jpeg-6b} library to perform the actual JPEG decoding. \tinyincludedcode{../jpeg.imageio/jpeginput.cpp} \section{Image Writers} \label{sec:pluginwriters} A plugin that writes a particular image file format must implement a \emph{subclass} of \ImageOutput (described in Chapter~\ref{chap:imageoutput}). This is actually very straightforward and consists of the following steps, which we will illustrate with a real-world example of writing a JPEG/JFIF plug-in. \begin{enumerate} \item Read the base class definition from {\fn imageio.h}, just as with an image reader (see Section~\ref{sec:pluginreaders}). \item Declare three public items: \begin{enumerate} \item An integer called \emph{name}{\cf _imageio_version} that identifies the version of the ImageIO protocol implemented by the plugin, defined in {\fn imageio.h} as the constant {\cf OIIO_PLUGIN_VERSION}. This allows the library to be sure it is not loading a plugin that was compiled against an incompatible version of \product. Note that if your plugin has both a reader and writer and they are compiled as separate modules (C++ source files), you don't want to declare this in \emph{both} modules; either one is fine. \item A function named \emph{name}{\cf _output_imageio_create} that takes no arguments and returns a new instance of your \ImageOutput subclass. (Note that \emph{name} is the name of your format, and must match the name of the plugin itself.) \item An array of {\cf char *} called \emph{name}{\cf _output_extensions} that contains the list of file extensions that are likely to indicate a file of the right format. The list is terminated by a {\cf NULL} pointer. \end{enumerate} All of these items must be inside an `{\cf extern "C"}' block in order to avoid name mangling by the C++ compiler, and we provide handy macros {\cf OIIO_PLUGIN_EXPORTS_BEGIN} and {\cf OIIO_PLUGIN_EXPORTS_END} to mamke this easy. Depending on your compiler, you may need to use special commands to dictate that the symbols will be exported in the DSO; we provide a special {\cf OIIO_EXPORT} macro for this purpose, defined in {\fn export.h}. Putting this all together, we get the following for our JPEG example: \begin{code} OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT int jpeg_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT JpgOutput *jpeg_output_imageio_create () { return new JpgOutput; } OIIO_EXPORT const char *jpeg_input_extensions[] = { "jpg", "jpe", "jpeg", NULL }; OIIO_PLUGIN_EXPORTS_END \end{code} \item The definition and implementation of an \ImageOutput subclass for this file format. It must publicly inherit \ImageOutput, and must overload the following methods which are ``pure virtual'' in the \ImageOutput base class: \begin{enumerate} \item {\cf format_name()} should return the name of the format, which ought to match the name of the plugin and by convention is strictly lower-case and contains no whitespace. \item {\cf supports()} should return {\cf true} if its argument names a feature supported by your format plugin, {\cf false} if it names a feature not supported by your plugin. See Section~\ref{sec:supportsfeaturelist} for the list of feature names. \item {\cf open()} should open the file and return true, or should return false if unable to do so (including if the file was found but turned out not to be in the format that your plugin is trying to implement). \item {\cf close()} should close the file, if open. \item {\cf write_scanline} should write a single scanline to the file, translating from internal to native data format and handling strides properly. \item The virtual destructor, which should {\cf close()} if the file is still open, addition to performing any other tear-down activities. \end{enumerate} Additionally, your \ImageOutput subclass may optionally choose to overload any of the following methods, which are defined in the \ImageOutput base class and only need to be overloaded if the default behavior is not appropriate for your plugin: \begin{enumerate} \item[(g)] {\cf write_scanlines()}, only if your format supports writing scanlines and you can get a performance improvement when outputting multiple scanlines at once. If you don't supply {\cf write_scanlines()}, the default implementation will simply call {\cf write_scanline()} separately for each scanline in the range. \item[(h)] {\cf write_tile()}, only if your format supports writing tiled images. \item[(i)] {\cf write_tiles()}, only if your format supports writing tiled images and you can get a performance improvement when outputting multiple tiles at once. If you don't supply {\cf write_tiles()}, the default implementation will simply call {\cf write_tile()} separately for each tile in the range. \item[(j)] {\cf write_rectangle()}, only if your format supports writing arbitrary rectangles. \item[(k)] {\cf write_image()}, only if you have a more clever method of doing so than the default implementation that calls {\cf write_scanline()} or {\cf write_tile()} repeatedly. \item[(l)] {\cf write_deep_scanlines()} and/or {\cf write_deep_tiles()}, only if your format supports ``deep'' data images. \end{enumerate} It is not strictly required, but certainly appreciated, if a file format does not support tiles, to nonetheless accept an \ImageSpec that specifies tile sizes by allocating a full-image buffer in {\cf open()}, providing an implementation of {\cf write_tile()} that copies the tile of data to the right spots in the buffer, and having {\cf close()} then call {\cf write_scanlines} to process the buffer now that the image has been fully sent. Here is how the class definition looks for our JPEG example. Note that the JPEG/JFIF file format does not support multiple subimages or tiled images. \begin{code} class JpgOutput : public ImageOutput { public: JpgOutput () { init(); } virtual ~JpgOutput () { close(); } virtual const char * format_name (void) const { return "jpeg"; } virtual int supports (string_view property) const { return false; } virtual bool open (const std::string &name, const ImageSpec &spec, bool append=false); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); bool close (); private: FILE *m_fd; std::vector m_scratch; struct jpeg_compress_struct m_cinfo; struct jpeg_error_mgr m_jerr; void init () { m_fd = NULL; } }; \end{code} \end{enumerate} Your subclass implementation of {\cf open()}, {\cf close()}, and {\cf write_scanline()} are the heart of an \ImageOutput implementation. (Also {\cf write_tile()}, for those image formats that support tiled output.) An \ImageOutput implementation must properly handle all data formats and strides passed to {\cf write_scanline()} or {\cf write_tile()}, unlike an \ImageInput implementation, which only needs to read scanlines or tiles in their native format and then have the super-class handle the translation. But don't worry, all the heavy lifting can be accomplished with the following helper functions provided as protected member functions of \ImageOutput that convert a scanline, tile, or rectangular array of values from one format to the native format(s) of the file. \apiitem{const void * {\ce to_native_scanline} (TypeDesc format, const void *data, \\ \bigspc stride_t xstride, std::vector \&scratch, \\ \bigspc unsigned int dither=0, int yorigin=0, int zorigin=0)} Convert a full scanline of pixels (pointed to by \emph{data}) with the given \emph{format} and strides into contiguous pixels in the native format (described by the \ImageSpec returned by the {\cf spec()} member function). The location of the newly converted data is returned, which may either be the original \emph{data} itself if no data conversion was necessary and the requested layout was contiguous (thereby avoiding unnecessary memory copies), or may point into memory allocated within the \emph{scratch} vector passed by the user. In either case, the caller doesn't need to worry about thread safety or freeing any allocated memory (other than eventually destroying the scratch vector). \apiend \apiitem{const void * {\ce to_native_tile} (TypeDesc format, const void *data,\\ \bigspc stride_t xstride, stride_t ystride, stride_t zstride,\\ \bigspc std::vector \&scratch, unsigned int dither=0, \\ \bigspc int xorigin=0, int yorigin=0, int zorigin=0)} Convert a full tile of pixels (pointed to by \emph{data}) with the given \emph{format} and strides into contiguous pixels in the native format (described by the \ImageSpec returned by the {\cf spec()} member function). The location of the newly converted data is returned, which may either be the original \emph{data} itself if no data conversion was necessary and the requested layout was contiguous (thereby avoiding unnecessary memory copies), or may point into memory allocated within the \emph{scratch} vector passed by the user. In either case, the caller doesn't need to worry about thread safety or freeing any allocated memory (other than eventually destroying the scratch vector). \apiend \apiitem{const void * {\ce to_native_rectangle} (int xbegin, int xend, \\ \bigspc int ybegin, int yend, int zbegin, int zend, \\ \bigspc TypeDesc format, const void *data, \\ \bigspc stride_t xstride, stride_t ystride, stride_t zstride, \\ \bigspc std::vector \&scratch, unsigned int dither=0, \\ \bigspc int xorigin=0, int yorigin=0, int zorigin=0)} Convert a rectangle of pixels (pointed to by \emph{data}) with the given \emph{format}, dimensions, and strides into contiguous pixels in the native format (described by the \ImageSpec returned by the {\cf spec()} member function). The location of the newly converted data is returned, which may either be the original \emph{data} itself if no data conversion was necessary and the requested layout was contiguous (thereby avoiding unnecessary memory copies), or may point into memory allocated within the \emph{scratch} vector passed by the user. In either case, the caller doesn't need to worry about thread safety or freeing any allocated memory (other than eventually destroying the scratch vector). \apiend For {\cf float} to 8 bit integer conversions only, if {\cf dither} parameter is nonzero, random dither will be added to reduce quantization banding artifacts; in this case, the specific nonzero {\cf dither} value is used as a seed for the hash function that produces the per-pixel dither amounts, and the optional {\cf origin} parameters help it to align the pixels to the right position in the dither pattern. \bigskip \bigskip \noindent The remainder of this section simply lists the full implementation of our JPEG writer, which relies heavily on the open source {\fn jpeg-6b} library to perform the actual JPEG encoding. \tinyincludedcode{../jpeg.imageio/jpegoutput.cpp} \section{Tips and Conventions} \label{sec:plugintipsconventions} \product's main goal is to hide all the pesky details of individual file formats from the client application. This inevitably leads to various mismatches between a file format's true capabilities and requests that may be made through the \product APIs. This section outlines conventions, tips, and rules of thumb that we recommend for image file support. \subsection*{Readers} \begin{itemize} \item If the file format stores images in a non-spectral color space (for example, YUV), the reader should automatically convert to RGB to pass through the OIIO APIs. In such a case, the reader should signal the file's true color space via a \qkw{Foo:colorspace} attribute in the \ImageSpec. \item ``Palette'' images should be automatically converted by the reader to RGB. \item If the file supports thumbnail images in its header, the reader should store the thumbnail dimensions in attributes \qkw{thumbnail_width}, \qkw{thumbnail_height}, and \qkw{thumbnail_nchannels} (all of which should be {\cf int}), and the thumbnail pixels themselves in \qkw{thumbnail_image} as an array of channel values (the array length is the total number of channel samples in the thumbnail). \end{itemize} \subsection*{Writers} The overall rule of thumb is: try to always ``succeed'' at writing the file, outputting the closest approximation of the user's data as possible. But it is permissible to fail the {\cf open()} call if it is clearly nonsensical or there is no possible way to output a decent approximation of the user's data. Some tips: \begin{itemize} \item If the client application requests a data format not directly supported by the file type, silently write the supported data format that will result in the least precision or range loss. \item It is customary to fail a call to {\cf open()} if the \ImageSpec requested a number of color channels plainly not supported by the file format. As an exception to this rule, it is permissible for a file format that does not support alpha channels to silently drop the fourth (alpha) channel of a 4-channel output request. \item If the app requests a \qkw{Compression} not supported by the file format, you may choose as a default any lossless compression supported. Do not use a lossy compression unless you are fairly certain that the app wanted a lossy compression. \item If the file format is able to store images in a non-spectral color space (for example, YUV), the writer may accept a \qkw{Foo:colorspace} attribute in the \ImageSpec as a request to automatically convert and store the data in that format (but it will always be passed as RGB through the OIIO APIs). \item If the file format can support thumbnail images in its header, and the \ImageSpec contain attributes \qkw{thumbnail_width}, \qkw{thumbnail_height}, \qkw{thumbnail_nchannels}, and \qkw{thumbnail_image}, the writer should attempt to store the thumbnail if possible. \end{itemize} \section{Building ImageIO Plugins} \label{sec:buildingplugins} FIXME -- spell out how to compile and link plugins on each of the major platforms. \chapwidthend openimageio-1.7.17~dfsg0.orig/src/doc/Description.txt0000644000175000017500000000100213151711064020676 0ustar mfvmfvOpenImageIO consists of: * Simple APIs for reading and writing image files in a format-agnostic manner, and plugins to read many common image formats including TIFF, JPEG, OpenEXR, PNG, HDR, ICO, and BMP. * Image utilities, including an image viewer, printing image information, format conversion, image comparison, and others. * A library for image texture lookups, and a managed image cache that allows an application to access hundreds of GB of image data using little runtime RAM. openimageio-1.7.17~dfsg0.orig/src/doc/makefigures.bash0000755000175000017500000001114013151711064021022 0ustar mfvmfv#!/bin/bash if [ "${OIIOTOOL}" == "" ] ; then OIIOTOOL=oiiotool fi echo "Using OIIOTOOL=${OIIOTOOL}" # Uncomment to make these files. But we check them in so that the figures # can be generated without oiio-images. #${OIIOTOOL} ../../../../oiio-images/tahoe-gps.jpg --colorconvert sRGB linear --resize 320x240 --colorconvert linear sRGB -o tahoe-small.jpg #${OIIOTOOL} ../../../../oiio-images/grid.tif --resize 256x256 --colorconvert linear sRGB -o grid-small.jpg ${OIIOTOOL} tahoe-small.jpg --tocolorspace linear --addc 0.2 --tocolorspace sRGB -o addc.jpg ${OIIOTOOL} tahoe-small.jpg --tocolorspace linear --mulc 0.5 --tocolorspace sRGB -o mulc.jpg ${OIIOTOOL} tahoe-small.jpg --tocolorspace linear --chsum:weight=.2126,.7152,.0722 --ch 0,0,0 --tocolorspace sRGB -o luma.jpg ${OIIOTOOL} grid-small.jpg --flip -o flip.jpg ${OIIOTOOL} grid-small.jpg --flop -o flop.jpg ${OIIOTOOL} grid-small.jpg --flipflop -o flipflop.jpg ${OIIOTOOL} grid-small.jpg --rotate90 -o rotate90.jpg ${OIIOTOOL} grid-small.jpg --rotate180 -o rotate180.jpg ${OIIOTOOL} grid-small.jpg --rotate270 -o rotate270.jpg ${OIIOTOOL} grid-small.jpg --transpose -o transpose.jpg ${OIIOTOOL} grid-small.jpg --rotate 45 -o rotate45.jpg ${OIIOTOOL} grid-small.jpg --cshift +70+30 -o cshift.jpg ${OIIOTOOL} --pattern constant:color=1,0.7,0.7 320x240 3 --fill:color=1,0,0 50x100+50+75 --tocolorspace sRGB -o fill.jpg ${OIIOTOOL} --create 320x240 3 -fill:bottom=1,0.7,0.7:top=1,0,0 320x240 --tocolorspace sRGB -o fill.jpg ${OIIOTOOL} --pattern checker:color1=0.1,0.1,0.1:color2=0.4,0.4,0.4:width=32:height=32 320x240 3 --tocolorspace sRGB -o checker.jpg ${OIIOTOOL} --pattern fill:top=0.1,0.1,0.1:bottom=0,0,0.75 320x240 3 --tocolorspace sRGB -o gradient.jpg ${OIIOTOOL} --pattern fill:left=0.1,0.1,0.1:right=0,0.75,0 320x240 3 --tocolorspace sRGB -o gradienth.jpg ${OIIOTOOL} --pattern fill:topleft=.1,.1,.1:topright=1,0,0:bottomleft=0,1,0:bottomright=0,0,1 320x240 3 --tocolorspace sRGB -o gradient4.jpg ${OIIOTOOL} --pattern checker:width=16:height=16 256x256 3 --tocolorspace sRGB -o checker.jpg ${OIIOTOOL} --pattern noise:type=uniform:min=0:max=1:mono=1 256x256 3 --tocolorspace sRGB -o unifnoise1.jpg ${OIIOTOOL} --pattern noise:type=uniform:min=0:max=1 256x256 3 --tocolorspace sRGB -o unifnoise3.jpg ${OIIOTOOL} --pattern noise:type=gaussian:mean=0.5:stddev=0.2 256x256 3 --tocolorspace sRGB -o gaussnoise.jpg ${OIIOTOOL} tahoe-small.jpg --noise:type=gaussian:mean=0:stddev=0.1 -o tahoe-gauss.jpg ${OIIOTOOL} tahoe-small.jpg --noise:type=salt:portion=0.01:value=0:mono=1 -o tahoe-pepper.jpg ${OIIOTOOL} tahoe-small.jpg --blur 7x7 -o tahoe-blur.jpg ${OIIOTOOL} tahoe-pepper.jpg --median 3x3 -o tahoe-pepper-median.jpg ${OIIOTOOL} tahoe-small.jpg --laplacian -mulc 2 -o tahoe-laplacian.jpg ${OIIOTOOL} --create 320x240 3 --text:x=25:y=50 "Hello, world" \ --text:x=50:y=100:font="Arial Bold":color=1,0,0:size=30 "Go Big Red!" --tocolorspace sRGB -o text.jpg ${OIIOTOOL} tahoe-small.jpg --crop 100x120+35+40 \ -ch R,G,B,A=1.0 -create 320x240 4 -fill:color=0.75,0.75,0.75,1 320x240 \ -fill:color=1,1,1,1 318x238+1+1 -over -tocolorspace sRGB -o crop.jpg ${OIIOTOOL} tahoe-small.jpg --cut 100x120+35+40 -tocolorspace sRGB -o cut.jpg ${OIIOTOOL} -create 320x240 4 -fill:color=.1,.5,.1 120x80+50+70 -rotate 30 -o pretrim.jpg \ -trim -ch R,G,B,A=1.0 -create 320x240 4 -fill:color=0.75,0.75,0.75,1 320x240 \ -fill:color=1,1,1,1 318x238+1+1 -over -tocolorspace sRGB -o trim.jpg ${OIIOTOOL} --autocc tahoe-small.jpg --invert -o invert.jpg ${OIIOTOOL} --pattern checker:color1=.1,.2,.1:color2=.2,.2,.2 320x240 3 \ --line:color=1,0,0 10,60,250,20,100,190 -d uint8 -o lines.png ${OIIOTOOL} --pattern checker:color1=.1,.2,.1:color2=.2,.2,.2 320x240 3 \ --box:color=0,1,1,1 150,100,240,180 \ --box:color=0.5,0.5,0,0.5:fill=1 100,50,180,140 \ --colorconvert linear sRGB -d uint8 -o box.png ${OIIOTOOL} --autocc --pattern constant:color=0.1,0.1,0.1 80x64 3 --text:x=8:y=54:size=40:font=DroidSerif Aai -o morphsource.jpg ${OIIOTOOL} --autocc morphsource.jpg --dilate 3x3 -d uint8 -o dilate.jpg ${OIIOTOOL} --autocc morphsource.jpg --erode 3x3 -d uint8 -o erode.jpg ${OIIOTOOL} --autocc morphsource.jpg --erode 3x3 --dilate 3x3 -d uint8 -o morphopen.jpg ${OIIOTOOL} --autocc morphsource.jpg --dilate 3x3 --erode 3x3 -d uint8 -o morphclose.jpg ${OIIOTOOL} --autocc dilate.jpg erode.jpg -sub -d uint8 -o morphgradient.jpg ${OIIOTOOL} --autocc morphsource.jpg morphopen.jpg -sub -d uint8 -o tophat.jpg ${OIIOTOOL} --autocc morphclose.jpg morphsource.jpg -sub -d uint8 -o bottomhat.jpg #${OIIOTOOL} ../../../testsuite/oiiotool/tahoe-small.tif openimageio-1.7.17~dfsg0.orig/src/doc/imagebufalgo.tex0000644000175000017500000031564313151711064021041 0ustar mfvmfv\chapter{Image Processing} \label{chap:imagebufalgo} \index{Image Processing|(} \index{ImageBufAlgo|(} \section{ImageBufAlgo general principles} \label{sec:iba:intro} \IBA is a set of image processing functions that operate on \ImageBuf's. The functions are declared in the header file {\cf OpenImageIO/imagebufalgo.h} and are declared in the {\cf namespace ImageBufAlgo}. \subsubsection*{Return values and error messages} All \IBA functions return a {\cf bool} that is {\cf true} if the function succeeds, {\cf false} if the function fails. Upon failure, the \emph{destination} \ImageBuf (the one that is being altered) will have an error message set. Below is an example: \begin{code} ImageBuf src ("input.exr"); ImageBuf dst; // will be the output image ... bool ok = ImageBufAlgo::crop (dst, src); if (! ok) { std::string err = dst.gas_error() ? dst.geterror() : "unknown"; std::cout << "crop error: " << err << "\n"; } \end{code} For a small minority of \IBA functions, there are only input images, and no image outputs (e.g., {\cf isMonochrome()}). In such cases, the error message should be retrieved from the first input image. \subsubsection*{Region of interest} Most \IBA functions take a destination (output) \ImageBuf and one or more source (input) \ImageBuf's. The destination \ImageBuf may or may not already be initialized (allocated and having existing pixel values). A few \IBA functions take only a single \ImageBuf parameter, which is altered in-place (and must already be initialized prior to the function call). All \IBA functions take an optional \ROI parameter describing which subset of the image should be altered. The default value of the \ROI parameter is an ``undefined'' \ROI If the destination \ImageBuf is already initialized, then the operation will be performed on the pixels in the destination that overlap the \ROI, leaving pixels in the destination which are outside the \ROI unaltered. If the \ROI is also undefined, the operation will be performed on the entire destination image. If the destination \ImageBuf is uninitialized, it will be initialized/allocated to be the size of the \ROI. If the \ROI itself is undefined, it will be set to the union of the defined pixel regions of all the input images. The usual way to use \IBA functions is to have an uninitialized destination image, and pass {\cf ROI::All()} (which is a synonym for an undefined \ROI) for the region of interest. Most \IBA functions also respect the {\cf chbegin} and {\cf chend} members of the \ROI, thus restricting the channel range on which the operation is performed. The default \ROI constructor sets up the \ROI to specify that the operation should be performed on all channels of the input image(s). \subsubsection*{Multithreading} All \IBA functions take an optional {\cf nthreads} parameter that signifies the maximum number of threads to use to parallelize the operation. The default value for {\cf nthreads} is 0, which signifies that the number of thread should be the OIIO global default set by {\cf OIIO::attribute()} (see Section~\ref{sec:attribute:threads}), which itself defaults to be the detected level of hardware concurrency (number of cores available). Generally you can ignore this parameter (or pass 0), meaning to use all the cores available in order to perform the computation as quickly as possible. The main reason to explicitly pass a different number (generally 1) is if the application is multithreaded at a high level, and the thread calling the \IBA function just wants to continue doing the computation without spawning additional threads, which might tend to crowd out the other application threads. \section{Pattern generation} \label{sec:iba:patterns} \apiitem{bool {\ce zero} (ImageBuf \&dst, ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!zero} \indexapi{zero} Set the destination image pixels to 0 within the specified region. This operation is performed in-place. The {\cf dst} buffer needs to already be an initialized \ImageBuf, otherwise there's no way to know how big to make it or what data type it should have. \smallskip \noindent Examples: \begin{code} ImageBuf dst ("myfile.exr"); ... // Zero out whole buffer, keeping it the same size ImageBufAlgo::zero (dst); // Zero out just a rectangle in the upper left corner ImageBufAlgo::zero (dst, ROI (0, 100, 0, 100)); // Zero out just the green channel, leave everything else the same ROI roi = dst.roi (); roi.chbegin = 1; // green roi.chend = 2; // one past the end of the channel region ImageBufAlgo::zero (dst, roi); \end{code} \apiend \apiitem{bool {\ce fill} (ImageBuf \&dst, const float *values, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce fill} (ImageBuf \&dst, const float *top, const float *bottom, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce fill} (ImageBuf \&dst, const float *topleft, const float *topright, \\ \bigspc const float *bottomleft, const float *bottomright, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!fill} \indexapi{fill} Set the pixels in the destination image within the specified region to the values in {\cf values[]}. The {\cf values} array must point to at least {\cf chend} values, or the number of channels in the image, whichever is smaller. Three varieties of {\cf fill()} exist: (a) a single set of channel values that will apply to the ROI, (b) two sets of values that will create a linearly interpolated gradient from top to bottom of the ROI, (c) four sets of values that will be bilnearly interpolated across all four corners of the ROI. \smallskip \noindent Examples: \begin{code} // Create a new 640x480 RGB image, fill it with pink, then draw a filled // red rectangle. ImageBuf A ("myimage", ImageSpec(640, 480, 3, TypeDesc::FLOAT); float pink[3] = { 1, 0.7, 0.7 }; float red[3] = { 1, 0, 0 }; ImageBufAlgo::fill (A, pink); ImageBufAlgo::fill (A, red, ROI(50,100, 75, 175)); // Draw a top-to-bottom gradient from red to pink ImageBufAlgo::fill (A, red, pink); \end{code} \spc \includegraphics[width=1.25in]{figures/fill.jpg} \\ \apiend \apiitem{bool {\ce checker} (ImageBuf \&dst, float width, float height, float depth, \\ \bigspc const float *color1, const float *color2, \\ \bigspc int xoffset=0, int yoffset=0, int zoffset=0, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!checker} \indexapi{checker} Set the pixels in the destination image within the specified region to a checkerboard pattern with origin given by the {\cf offset} values, checker size given by the {\cf width, height, depth} values, and alternting between {\cf color1[]} and {\cf color2[]}. The colors must point to arrays long enough to contain values for all channels in the image. \smallskip \noindent Examples: \begin{code} // Create a new 640x480 RGB image, fill it with a two-toned gray // checkerboard, the checkers being 64x64 pixels each. ImageBuf A (ImageSpec(640, 480, 3, TypeDesc::FLOAT); float dark[3] = { 0.1, 0.1, 0.1 }; float light[3] = { 0.4, 0.4, 0.4 }; ImageBufAlgo::checker (A, 64, 64, 1, dark, light, 0, 0, 0); \end{code} \spc \includegraphics[width=1.25in]{figures/checker.jpg} \\ \apiend \apiitem{bool {\ce noise} (ImageBuf \&dst, string_view noisetype, \\ \bigspc\bigspc float A = 0.0f, float B = 0.1f, bool mono = false, \\ \bigspc\bigspc int seed = 0, ROI roi=ROI::All(), int nthreads=0)} \indexapi{noise} Add pseudorandom noise to the specified region of {\cf dst}. For noise type \qkw{uniform}, the noise is uniformly distributed on the range {\cf [A,B)]}. For noise \qkw{gaussian}, the noise will have a normal distribution with mean A and standard deviation B. For noise \qkw{salt}, the value A will be stored in a random set of pixels whose proportion (of the overall image) is B. For all noise types, choosing different {\cf seed} values will result in a different pattern. If the {\cf mono} flag is {\cf true}, a single noise value will be applied to all channels specified by {\cf roi}, but if {\cf mono} is {\cf false}, a separate noise value will be computed for each channel in the region. If {\cf dst} is uninitialized, it will be resized to be a {\cf float} \ImageBuf large enough to hold the region specified by {\cf roi}. It is an error to pass both an uninitialized {\cf dst} and an undefined {\cf roi}. \smallskip \noindent Examples: \begin{code} // Create a new 256x256 field of grayscale uniformly distributed noise on [0,1) ImageBuf A (ImageSpec(256, 256, 3, TypeDesc::FLOAT)); ImageBufAlgo::zero (A); ImageBufAlgo::noise (A, "uniform", 0.0f /*min*/, 1.0f /*max*/, true /*mono*/, 1 /*seed*/); // Add color Gaussian noise to an existing image ImageBuf B ("tahoe.jpg"); ImageBufAlgo::noise (B, "gaussian", 0.0f /*mean*/, 0.1f /*stddev*/, false /*mono*/, 1 /*seed*/); // Use salt and pepper noise to make occasional random dropouts ImageBuf C ("tahoe.jpg"); ImageBufAlgo::noise (C, "salt", 0.0f /*value*/, 0.01f /*portion*/, true /*mono*/, 1 /*seed*/); \end{code} \noindent \includegraphics[width=1.25in]{figures/unifnoise1.jpg} \spc \includegraphics[width=1.65in]{figures/tahoe-gauss.jpg} \spc \includegraphics[width=1.65in]{figures/tahoe-pepper.jpg} \apiend \apiitem{bool {\ce render_point} (ImageBuf \&dst, int x, int y, \\ \bigspc\spc\spc array_view color, \\ \bigspc\spc\spc ROI roi=ROI.All(), int nthreads=0)} \index{ImageBufAlgo!render_point} \indexapi{render_point} \NEW % 1.7 Render a point into the destination image, doing an ``over'' of color (if it includes an alpha channel). The {\cf color} value should have at least as many entires as the ROI (which will default to being the entirety of {\cf dst}). No pixels or channels outside the ROI will be modified. \smallskip \noindent Examples: \begin{code} ImageBuf A (ImageSpec (640, 480, 4, TypeDesc::FLOAT)); float red[4] = { 1, 0, 0, 1 }; ImageBufAlgo::render_point (A, 50, 100, red); \end{code} \apiend \apiitem{bool {\ce render_line} (ImageBuf \&dst, int x1, int y1, int x2, int y2, \\ \bigspc\spc\spc array_view color, \\ \bigspc\spc\spc bool skip_first_point=false,\\ \bigspc\spc\spc ROI roi=ROI.All(), int nthreads=0)} \index{ImageBufAlgo!render_line} \indexapi{render_line} \NEW % 1.7 Render a line from pixel $(x_1,y_1)$ to $(x_2,y_2)$ into {\cf dst}, doing an ``over'' of the color (if it includes an alpha channel) onto the existing data in {\cf dst}. The {\cf color} must include as many values as {\cf roi.chend-1}. The ROI can be used to limit the pixel area or channels that are modified, and default to the entirety of {\cf dst}. If {\cf skip_first_point} is {\cf true}, the first point {\cf (x1, y1)} will not be drawn (this can be helpful when drawing poly-lines, to avoid double-rendering of the vertex positions). \smallskip \noindent Examples: \begin{code} ImageBuf A (ImageSpec (640, 480, 4, TypeDesc::FLOAT)); float red[4] = { 1, 0, 0, 1 }; ImageBufAlgo::render_line (A, 10, 60, 250, 20, red); ImageBufAlgo::render_line (A, 250, 20, 100, 190, red, true); \end{code} \spc \includegraphics[width=1.65in]{figures/lines.png} \\ \apiend \apiitem{bool {\ce render_box} (ImageBuf \&dst, int x1, int y1, int x2, int y2, \\ \bigspc\spc\spc array_view color, bool fill=false,\\ \bigspc\spc\spc roi=ROI.All(), int nthreads=0)} \index{ImageBufAlgo!render_box} \indexapi{render_box} \NEW % 1.7 Render a filled or unfilled box with corners at pixels $(x_1,y_1)$ and $(x_2,y_2)$ into {\cf dst}, doing an ``over'' of the color (if it includes an alpha channel) onto the existing data in {\cf dst}. The {\cf color} must include as many values as {\cf roi.chend-1}. The ROI can be used to limit the pixel area or channels that are modified, and default to the entirety of {\cf dst}. If {\cf fill} is {\cf true}, the box will be completely filled in, otherwise only its outlien will be drawn. \smallskip \noindent Examples: \begin{code} ImageBuf A (ImageSpec (640, 480, 4, TypeDesc::FLOAT)); float cyan[4] = { 1, 0, 0, 1 }; ImageBufAlgo::render_box (A, 150, 100, 240, 180, cyan); float yellow_transparent[4] = { 0.5, 0.5, 0, 0.5 }; ImageBufAlgo::render_box (A, 100, 50, 180, 140, yellow_transparent, true); \end{code} \spc \includegraphics[width=1.65in]{figures/box.png} \\ \apiend \apiitem{bool {\ce render_text} (ImageBuf \&dst, int x, int y, string_view text,\\ \bigspc\spc\spc int fontsize=16, string_view fontname="", \\ \bigspc\spc\spc const float *textcolor = NULL)} \index{ImageBufAlgo!render_text} \indexapi{render_text} Render a text string (encode as UTF-8) into the destination, essentially doing an ``over'' of the antialiased character into the existing pixel data. The baseline of the first character will start at position ({\cf x, y}). The font is given by {\cf fontname} as a full pathname to the font file (defaulting to some reasonable system font if not supplied at all), and with a nominal height of {\cf fontsize} (in pixels). The characters will be drawn in opaque white (1.0 in all channels), unless {\cf textcolor} is supplied (and is expected to point to a {\cf float} array of length at least equal to the number of channels in {\cf dst}). \smallskip \noindent Examples: \begin{code} ImageBuf A (ImageSpec (640, 480, 4, TypeDesc::FLOAT)); ImageBufAlgo::render_text (A, 50, 100, "Hello, world"); float red[] = { 1, 0, 0, 1 }; ImageBufAlgo::render_text (A, 100, 200, "Go Big Red!", 60, "Arial Bold", red); \end{code} \spc \includegraphics[width=2.5in]{figures/text.jpg} \\ \noindent Note that because of slightly differing fonts and versions of Freetype available, we do not expect drawn text to be pixel-for-pixel identical on different platforms supported by \product. \apiend \section{Image transformations and data movement} \label{sec:iba:transforms} \apiitem{bool {\ce channels} (ImageBuf \&dst, const ImageBuf \&src, int nchannels, \\ \bigspc const int *channelorder, const float *channelvalues=NULL, \\ \bigspc const std::string *newchannelnames=NULL, \\ \bigspc bool shuffle_channel_names=false, int nthreads=0)} \index{ImageBufAlgo!channels} \indexapi{channels} \label{sec:iba:channels} Generic channel shuffling: copy {\cf src} to {\cf dst}, but with channels in the order specified by {\cf channelorder[0..nchannels-1]}. Does not support in-place operation. For any channel in which {\cf channelorder[i]} $< 0$, it will just make {\cf dst} channel {\cf i} be a constant value set to {\cf channelvalues[i]} (if {\cf channelvalues} is not \NULL) or {\cf 0.0} (if {\cf channelvalues} is \NULL). If {\cf channelorder} is \NULL, it will be interpreted as {\cf \{0, 1, ..., nchannels-1\}}, meaning that it's only renaming channels, not reordering them. If {\cf newchannelnames} is not \NULL, it points to an array of new channel names. Channels for which {\cf newchannelnames[i]} is the empty string (or all channels, if {\cf newchannelnames == NULL}) will be named as follows: If {\cf shuffle_channel_names} is {\cf false}, the resulting dst image will have default channel names in the usual order (\qkw{R}, \qkw{G}, etc.), but if {\cf shuffle_channel_names} is {\cf true}, the names will be taken from the corresponding channels of the source image -- be careful with this, shuffling both channel ordering and their names could result in no semantic change at all, if you catch the drift. \smallskip \noindent Examples: \begin{code} // Copy the first 3 channels of an RGBA, drop the alpha ImageBuf RGBA (...); // assume it's initialized, 4 chans ImageBuf RGB; ImageBufAlgo::channels (RGB, RGBA, 3, NULL /*default ordering*/); // Copy just the alpha channel, making a 1-channel image ImageBuf Alpha; int alpha_channel[] = { 3 /* alpha channel */ }; ImageBufAlgo::channels (Alpha, RGBA, 1, &alpha_channel); // Swap the R and B channels ImageBuf BRGA; int channelorder[] = { 2 /*B*/, 1 /*G*/, 0 /*R*/, 3 /*A*/ }; ImageBufAlgo::channels (BRGA, RGBA, 4, &channelorder); // Add an alpha channel with value 1.0 everywhere to an RGB image, // keep the other channels with their old ordering, values, and // names. int channelorder[] = { 0, 1, 2, -1 /*use a float value*/ }; float channelvalues[] = { 0 /*ignore*/, 0 /*ignore*/, 0 /*ignore*/, 1.0 }; std::string channelnames[] = { "", "", "", "A" }; ImageBufAlgo::channels (RGBA, RGB, 4, channelorder, channelvalues, channelnames); \end{code} \apiend \apiitem{bool {\ce channel_append} (ImageBuf \&dst, const ImageBuf \&A, const ImageBuf \&B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!channel_append} \indexapi{channel_append} Append the channels of {\cf A} and {\cf B} together into {\cf dst} over the region of interest. If the region passed is uninitialized (the default), it will be interpreted as being the union of the pixel windows of {\cf A} and {\cf B} (and all channels of both images). If {\cf dst} is not already initialized, it will be resized to be big enough for the region. \smallskip \noindent Examples: \begin{code} ImageBuf RGBA (...); // assume initialized, 4 channels ImageBuf Z (...); // assume initialized, 1 channel ImageBuf RGBAZ; ImageBufAlgo::channel_append (RGBAZ, RGBA, Z); \end{code} \apiend \apiitem{bool {\ce copy} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc TypeDesc convert=TypeDesc::UNKNOWN, ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!copy} \indexapi{copy} Copy the specified region of pixels of {\cf src} into {\cf dst} at the same locations, without changing any existing pixels of {\cf dst} outside the region. If {\cf dst} is not already initialized, it will be set to the same size as {\cf roi} (by default all of {\cf src}, optionally with the pixel type overridden by {\cf convert} (if it is not {\cf UNKNOWN}). \smallskip \noindent Examples: \begin{code} // Set B to be A, but converted to float ImageBuf A (...); // Assume initialized ImageBuf B; ImageBufAlgo::copy (B, A, TypeDesc::FLOAT); \end{code} \apiend \apiitem{bool {\ce crop} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!crop} \indexapi{crop} Reset {\cf dst} to be the specified region of {\cf src}. Pixels from {\cf src} which are outside {\cf roi} will not be copied, and new black pixels will be added for regions of {\cf roi} which were outside the data window of {\cf src}. Note that the {\cf crop} operation does not actually move the pixels on the image plane or adjust the full/display window; it merely restricts which pixels are copied from {\cf src} to {\cf dst}. (Note the difference compared to {\cf cut()}). \smallskip \noindent Examples: \begin{code} // Set B to be the upper left 200x100 region of A ImageBuf A (...); // Assume initialized ImageBuf B; ImageBufAlgo::crop (B, A, ROI(0,200,0,100)); \end{code} \apiend \apiitem{bool {\ce cut} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!cut} \indexapi{cut} Reset {\cf dst} to be the specified region of {\cf src}, but repositioned at the image plane origin and with the full/display window set to exactly cover the new pixel window. (Note the difference compared to {\cf crop()}). \smallskip \noindent Examples: \begin{code} // Set B to be the 100x100 region of A with origin (50,200). ImageBuf A (...); // Assume initialized ImageBuf B; ImageBufAlgo::cut (B, A, ROI(50,250,200,300)); // Note: B has origin at 0,0, NOT at (50,200). \end{code} \apiend \apiitem{bool {\ce paste} (ImageBuf \&dst, int xbegin, int ybegin, int zbegin, int chbegin, \\ \bigspc const ImageBuf \&src, ROI srcroi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!paste} \indexapi{paste} Copy into {\cf dst}, beginning at {\cf (xbegin, ybegin, zbegin)}, the pixels of {\cf src} described by {\cf srcroi}. If {\cf srcroi} is {\cf ROI::All()}, the entirety of src will be used. It will copy into channels {\cf [chbegin...]}, as many channels as are described by {\cf srcroi}. \smallskip \noindent Examples: \begin{code} // Paste small.exr on top of big.exr at offset (100,100) ImageBuf Big ("big.exr"); ImageBuf Small ("small.exr"); ImageBufAlgo::paste (Big, 100, 100, 0, 0, Small); \end{code} \apiend \apiitem{bool {\ce rotate90} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!rotate90} \indexapi{rotate90} Copy {\cf src} (or a subregion of {\cf src} to the corresponding pixels of {\cf dst}, but rotated 90 degrees clockwise. \smallskip \noindent Examples: \begin{code} ImageBuf A ("grid.jpg"); ImageBuf B; ImageBufAlgo::rotate90 (B, A); \end{code} \spc \includegraphics[width=1.25in]{figures/grid-small.jpg} ~ {\Huge $\rightarrow$} ~ \includegraphics[width=1.25in]{figures/rotate90.jpg} \\ \apiend \apiitem{bool {\ce rotate180} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!rotate180} \indexapi{rotate180} Copy {\cf src} (or a subregion of {\cf src} to the corresponding pixels of {\cf dst}, but rotated 180 degrees. \smallskip \noindent Examples: \begin{code} ImageBuf A ("grid.jpg"); ImageBuf B; ImageBufAlgo::rotate180 (B, A); \end{code} \spc \includegraphics[width=1.25in]{figures/grid-small.jpg} ~ {\Huge $\rightarrow$} ~ \includegraphics[width=1.25in]{figures/rotate180.jpg} \\ \apiend \apiitem{bool {\ce rotate270} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!rotate270} \indexapi{rotate270} Copy {\cf src} (or a subregion of {\cf src} to the corresponding pixels of {\cf dst}, but rotated 270 degrees clockwise (or 90 degrees counter-clockwise). \smallskip \noindent Examples: \begin{code} ImageBuf A ("grid.jpg"); ImageBuf B; ImageBufAlgo::rotate270 (B, A); \end{code} \spc \includegraphics[width=1.25in]{figures/grid-small.jpg} ~ {\Huge $\rightarrow$} ~ \includegraphics[width=1.25in]{figures/rotate270.jpg} \\ \apiend \apiitem{bool {\ce flip} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!flip} \indexapi{flip} Copy {\cf src} (or a subregion of {\cf src}) to the corresponding pixels of {\cf dst}, but with the scanlines exchanged vertically. \smallskip \noindent Examples: \begin{code} ImageBuf A ("grid.jpg"); ImageBuf B; ImageBufAlgo::flip (B, A); \end{code} \spc \includegraphics[width=1.25in]{figures/grid-small.jpg} ~ {\Huge $\rightarrow$} ~ \includegraphics[width=1.25in]{figures/flip.jpg} \\ \apiend \apiitem{bool {\ce flop} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!flop} \indexapi{flop} Copy {\cf src} (or a subregion of {\cf src}) to the corresponding pixels of {\cf dst}, but with the columns exchanged horizontally. \smallskip \noindent Examples: \begin{code} ImageBuf A ("grid.jpg"); ImageBuf B; ImageBufAlgo::flop (B, A); \end{code} \spc \includegraphics[width=1.25in]{figures/grid-small.jpg} ~ {\Huge $\rightarrow$} ~ \includegraphics[width=1.25in]{figures/flop.jpg} \\ \apiend \apiitem{bool {\ce reorient} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!reorient} \indexapi{reorient} Copy {\cf src} to {\cf dst}, but with whatever seties of rotations, flips, or flops are necessary to transform the pixels into the configuration suggested by the \qkw{Orientation} metadata of the image (and the \qkw{Orientation} metadata is then set to 1, ordinary orientation). It is allowed for {\cf src} and {\cf dst} to refer to the same image, in which case the operation will be done ``in place'' (though not in a way that is safe for another thread to be accessing the image simultaneously). \smallskip \noindent Examples: \begin{code} ImageBuf A ("tahoe.jpg"); ImageBufAlgo::reorient (A, A); \end{code} %\spc \includegraphics[width=1.25in]{figures/grid-small.jpg} %~ {\Huge $\rightarrow$} ~ %\includegraphics[width=1.25in]{figures/flop.jpg} \\ \apiend \apiitem{bool {\ce transpose} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!transpose} \indexapi{transpose} Copy {\cf src} (or a subregion of {\cf src} to the corresponding transposed ($x \leftrightarrow y$) pixels of {\cf dst}. In other words, for all $(x,y)$ within the region, set {\cf dst[y,x] = src[x,y]}. \smallskip \noindent Examples: \begin{code} ImageBuf A ("grid.jpg"); ImageBuf B; ImageBufAlgo::transpose (B, A); \end{code} \spc \includegraphics[width=1.25in]{figures/grid-small.jpg} ~ {\Huge $\rightarrow$} ~ \includegraphics[width=1.25in]{figures/transpose.jpg} \\ \apiend \apiitem{bool {\ce circular_shift} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc int xshift, int yshift, int zshift=0, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!circular_shift} \indexapi{circular_shift} Copy {\cf src} (or a subregion of {\cf src} to the pixels of {\cf dst}, but circularly shifting by the given amount. To clarify, the circular shift of $[0,1,2,3,4,5]$ by $+2$ is $[4,5,0,1,2,3]$. \smallskip \noindent Examples: \begin{code} ImageBuf A ("grid.jpg"); ImageBuf B; ImageBufAlgo::circular_shift (B, A, 70, 30); \end{code} \spc \includegraphics[width=1.25in]{figures/grid-small.jpg} ~ {\Huge $\rightarrow$} ~ \includegraphics[width=1.25in]{figures/cshift.jpg} \\ \apiend \apiitem{bool {\ce rotate} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc float angle, \\ \bigspc string_view filtername="", float filtersize=0, \\ \bigspc bool recompute_roi = false, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce rotate} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc float angle, \\ \bigspc Filter2D *filter, \\ \bigspc bool recompute_roi = false, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce rotate} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc float angle, float center_x, float center_y, \\ \bigspc string_view filtername="", float filtersize=0, \\ \bigspc bool recompute_roi = false, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce rotate} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc float angle, float center_x, float center_y, \\ \bigspc Filter2D *filter, \\ \bigspc bool recompute_roi = false, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!rotate} \indexapi{rotate} Rotate the {\cf src} image by the {\cf angle} (in radians, with positive angles clockwise). When {\cf center_x} and {\cf center_y} are supplied, they denote the center of rotation; in their absence, the rotation will be about the center of the image's display window. Only the pixels (and channels) of {\cf dst} that are specified by {\cf roi} will be copied from the rotated {\cf src}; the default {\cf roi} is to alter all the pixels in {\cf dst}. If {\cf dst} is uninitialized, it will be resized to be an ImageBuf large enough to hold the rotated image if {\cf recompute_roi} is {\cf true}, or will have the same ROI as {\cf src} if {\cf recompute_roi} is false. It is an error to pass both an uninitialied {\cf dst} and an undefined {\cf roi}. The caller may explicitly pass a reconstruction filter, or specify one by name and size, or if the name is the empty string {\cf rotate()} will choose a reasonable high-quality default if \NULL is passed. The filter is used to weight the {\cf src} pixels falling underneath it for each {\cf dst} pixel; the filter's size is expressed in pixel units of the {\cf dst} image. \smallskip \noindent Examples: \begin{code} ImageBuf Src ("tahoe.exr"); ImageBuf Dst; ImageBufAlgo::rotate (Dst, Src, 45.0); \end{code} \spc \includegraphics[width=1.25in]{figures/grid-small.jpg} ~ {\Huge $\rightarrow$} ~ \includegraphics[width=1.25in]{figures/rotate45.jpg} \\ \apiend \apiitem{bool {\ce warp} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc const Imath::M33f \&M, \\ \bigspc string_view filtername="", float filtersize=0, \\ \bigspc bool recompute_roi = false, \\ \bigspc ImageBuf::WrapMode wrap = ImageBuf::WrapDefault, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce warp} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc const Imath::M33f \&M, \\ \bigspc Filter2D *filter, \\ \bigspc bool recompute_roi = false, \\ \bigspc ImageBuf::WrapMode wrap = ImageBuf::WrapDefault, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!warp} \indexapi{warp} Warp the {\cf src} image using the supplied 3x3 transformation matrix {\cf M}. Only the pixels (and channels) of {\cf dst} that are specified by {\cf roi} will be copied from the warped {\cf src}; the default {\cf roi} is to alter all the pixels in {\cf dst}. If {\cf dst} is uninitialized, it will be resized to be an \ImageBuf large enough to hold the warped image if {\cf recompute_roi} is {\cf true}, or will have the same ROI as {\cf src} if {\cf recompute_roi} is false. It is an error to pass both an uninitialied {\cf dst} and an undefined {\cf roi}. The caller may explicitly pass a reconstruction filter, or specify one by name and size, or if the name is the empty string {\cf resize()} will choose a reasonable high-quality default if \NULL is passed. The filter is used to weight the {\cf src} pixels falling underneath it for each {\cf dst} pixel; the filter's size is expressed in pixel units of the {\cf dst} image. \smallskip \noindent Examples: \begin{code} Imath::M33f M ( 0.7071068, 0.7071068, 0, -0.7071068, 0.7071068, 0, 20, -8.284271, 1); ImageBuf Src ("tahoe.exr"); ImageBuf Dst; ImageBufAlgo::warp (dst, src, M, "lanczos3"); \end{code} \apiend \apiitem{bool {\ce resize} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc string_view filtername="", float filtersize=0, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce resize} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc Filter2D *filter, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!resize} \indexapi{resize} Set {\cf dst}, over the region of interest, to be a resized version of the corresponding portion of {\cf src} (mapping such that the ``full'' image window of each correspond to each other, regardless of resolution). If {\cf dst} is not yet initialized, it will be sized according to {\cf roi}. The caller may explicitly pass a reconstruction filter, or specify one by name and size, or if the name is the empty string {\cf resize()} will choose a reasonable high-quality default if \NULL is passed. The filter is used to weight the {\cf src} pixels falling underneath it for each {\cf dst} pixel; the filter's size is expressed in pixel units of the {\cf dst} image. \smallskip \noindent Examples: \begin{code} // Resize the image to 640x480, using the default filter ImageBuf Src ("tahoe.exr"); ImageBuf Dst; ROI roi (0, 640, 0, 480, 0, 1, /*chans:*/ 0, Src.nchannels()); ImageBufAlgo::resize (Dst, Src, "", 0, roi); \end{code} \apiend \apiitem{bool {\ce resample} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc bool interpolate = true, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!resample} \indexapi{resample} Set {\cf dst}, over the region of interest, to be a resized version of the corresponding portion of {\cf src} (mapping such that the ``full'' image window of each correspond to each other, regardless of resolution). If {\cf dst} is not yet initialized, it will be sized according to {\cf roi}. Unlike {\cf ImageBufAlgo::resize()}, {\cf resample()} does not take a filter; it just samples either with a bilinear interpolation (if {\cf interpolate} is {\cf true}, the default) or uses the single ``closest'' pixel (if {\cf interpolate} is {\cf false}). This makes it a lot faster than a proper {\cf resize()}, though obviously with lower quality (aliasing when downsizing, pixel replication when upsizing). For ``deep'' images, this function returns copies the closest source pixel needed, rather than attempting to interpolate deep pixels (regardless of the value of {\cf interpolate}). \smallskip \noindent Examples: \begin{code} // Resample quickly to 320x240, using the default filter ImageBuf Src ("tahoe.exr"); ImageBuf Dst; ROI roi (0, 320, 0, 240, 0, 1, /*chans:*/ 0, Src.nchannels()); ImageBufAlgo::resample (Dst, Src, NULL, roi); \end{code} \apiend \section{Image arithmetic} \label{sec:iba:arith} \apiitem{bool {\ce add} (ImageBuf \&dst, const ImageBuf \&A, const ImageBuf \&B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce add} (ImageBuf \&dst, const ImageBuf \&A, float B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce add} (ImageBuf \&dst, const ImageBuf \&A, const float *B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!add} \indexapi{add} For all pixels and channels within the designated region, set {\cf dst} to the sum of image {\cf A} and {\cf B}. {\cf B} is either an image, a float (added to all channels) or a per-channel float array. All of the images must have the same number of channels. \smallskip \noindent Examples: \begin{code} // Add images A and B, assign to Sum ImageBuf A ("a.exr"); ImageBuf B ("b.exr"); ImageBuf Sum; ImageBufAlgo::add (Sum, A, B); // Add 0.2 to channels 0-2 of A ImageBuf A ("a.exr"), Sum; ROI roi = get_roi (A.spec()); roi.chbegin = 0; roi.chend = 3; ImageBufAlgo::add (Sum, A, 0.2, roi); \end{code} \apiend \apiitem{bool {\ce sub} (ImageBuf \&dst, const ImageBuf \&A, const ImageBuf \&B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce sub} (ImageBuf \&dst, const ImageBuf \&A, float B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce sub} (ImageBuf \&dst, const ImageBuf \&A, const float *B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!sub} \indexapi{sub} For all pixels within the designated region, subtract {\cf B} from {\cf A}, putting the results into {\cf dst}. {\cf B} is either an image, a float (added to all channels) or a per-channel float array. All of the images must have the same number of channels. \smallskip \noindent Examples: \begin{code} ImageBuf A ("a.exr"); ImageBuf B ("b.exr"); ImageBuf Difference; ImageBufAlgo::sub (Difference, A, B); \end{code} \apiend \apiitem{bool {\ce absdiff} (ImageBuf \&dst, const ImageBuf \&A, const ImageBuf \&B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce absdiff} (ImageBuf \&dst, const ImageBuf \&A, float B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce absdiff} (ImageBuf \&dst, const ImageBuf \&A, const float *B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!absdiff} \indexapi{absdiff} For all pixels within the designated region, compute the absolute value of the difference between the corresponding pixels of {\cf A} from {\cf B}, putting the results into {\cf dst}. {\cf A} is an \ImageBuf. {\cf B} may be an \ImageBuf (with the same number of channels as {\cf A}), or an array of floats (one per channel, for each channel of {\cf A}), or a single float (same value for all channels). \smallskip \noindent Examples: \begin{code} ImageBuf A ("a.exr"); ImageBuf B ("b.exr"); ImageBuf Difference; ImageBufAlgo::absdiff (Difference, A, B); \end{code} \apiend \apiitem{bool {\ce abs} (ImageBuf \&dst, const ImageBuf \&A, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!abs} \indexapi{abs} For all pixels within the designated region, compute the absolute value of the corresponding pixels of {\cf A}, putting the results into {\cf dst}. \smallskip \noindent Examples: \begin{code} ImageBuf A ("a.exr"); ImageBuf Abs; ImageBufAlgo::abs (Abs, A); \end{code} \apiend \apiitem{bool {\ce mul} (ImageBuf \&dst, const ImageBuf \&A, const ImageBuf \&B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce mul} (ImageBuf \&dst, const ImageBuf \&A, float B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce mul} (ImageBuf \&dst, const ImageBuf \&A, const float *B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!mul} \indexapi{mul} For all pixels within the designated region, multiply the pixel values of image {\cf A} by {\cf B} (channel by channel), putting the product in {\cf dst}. {\cf B} is either an image, a float (added to all channels) or a per-channel float array. All of the images must have the same number of channels. \smallskip \noindent Examples: \begin{code} ImageBuf A ("a.exr"); ImageBuf B ("b.exr"); ImageBuf Product; ImageBufAlgo::mul (Product, A, B); // Reduce intensity of A's channels 0-2 by 50% ROI roi = get_roi (A.spec()); roi.chbegin = 0; roi.chend = 3; ImageBufAlgo::mul (A, A, 0.5, roi); \end{code} \apiend \apiitem{bool {\ce div} (ImageBuf \&dst, const ImageBuf \&A, const ImageBuf \&B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce div} (ImageBuf \&dst, const ImageBuf \&A, float B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce div} (ImageBuf \&dst, const ImageBuf \&A, const float *B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!div} \indexapi{div} For all pixels within the designated region, divide the pixel values of image {\cf A} by {\cf B} (channel by channel), putting the result in {\cf dst}. {\cf B} is either an image, a float (added to all channels) or a per-channel float array. All of the images must have the same number of channels. Division by zero is definied to result in zero. \smallskip \noindent Examples: \begin{code} ImageBuf A ("a.exr"); ImageBuf B ("b.exr"); ImageBuf Result; ImageBufAlgo::div (Result, A, B); // Reduce intensity of A's channels 0-2 by 50% ROI roi = get_roi (A.spec()); roi.chbegin = 0; roi.chend = 3; ImageBufAlgo::div (A, A, 2.0f, roi); \end{code} \apiend \apiitem{bool {\ce mad} (ImageBuf \&dst, const ImageBuf \&A, const ImageBuf \&B, \\ \bigspc const ImageBuf \&C, ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce mad} (ImageBuf \&dst, const ImageBuf \&A, float B, \\ \bigspc float C, ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce mad} (ImageBuf \&dst, const ImageBuf \&A, const float *B, \\ \bigspc const float *C, ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!mad} \indexapi{mad} For all pixels within the designated region, compute {\cf dst = A * B + C} (pixel by pixel, channel by channel). {\cf A} must be an image. {\cf B} and {\cf C} are either both images, both single floats (applied to all channels), or are both per-channel float arrays. All of the images must have the same number of channels. \smallskip \noindent Examples: \begin{code} ImageBuf A ("a.exr"); ImageBuf B ("b.exr"); ImageBuf C ("c.exr"); ImageBuf Result; ImageBufAlgo::mad (Result, A, B, C); // Compute the "inverse" A, which is 1.0-A, as A*(-1) + 1 // Do this in-place, and only for the first 3 channels (leave any // alpha channel, if present, as it is). ROI roi = get_roi (A.spec()); roi.chbegin = 0; roi.chend = 3; ImageBufAlgo::mad (A, A, -1.0, 1.0, roi); \end{code} \apiend \apiitem{bool {\ce invert} (ImageBuf \&dst, const ImageBuf \&A, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!invert} \indexapi{invert} For all pixels and channels within the designated region, set {\cf dst} to be the color inverse of the corresponding pixels of {\cf A}, that is, 1.0 - A. \smallskip \noindent Examples: \begin{code} ImageBuf A ("a.exr"); ImageBuf Inverse; ImageBufAlgo::invert (Inverse, A); // In this example, we are careful to deal with alpha in an RGBA // image. First we copy A to Inverse, and then in-place, we will: // un-premultiply the color values by alpha, invert just the color // channels, and then re-premultiply the colors by alpha. Inverse.copy (A); // First copy A to inverse roi = A.roi(); roi.chend = 3; // Restrict roi to only R,G,B ImageBufAlgo::unpremult (Inverse, Inverse); ImageBufAlgo::invert (Inverse, Inverse, roi); ImageBufAlgo::premult (Inverse, Inverse); \end{code} %\spc \includegraphics[width=1.5in]{figures/tahoe-small.jpg} %\raisebox{40pt}{\large $\rightarrow$} %\includegraphics[width=1.5in]{figures/invert.jpg} \\ \apiend \apiitem{bool {\ce pow} (ImageBuf \&dst, const ImageBuf \&A, float b, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce pow} (ImageBuf \&dst, const ImageBuf \&A, const float *b, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!pow} \indexapi{pow} For all pixels within the designated region, raise the pixel value to the power {\cf b} (channel by channel), putting the result in {\cf dst}. The value {\cf b} is either a single float (applied to all channels) or a per-channel float array. \smallskip \noindent Examples: \begin{code} // Gamma-correct by 2.2 channels 0-2 of the image ROI roi = get_roi (A.spec()); roi.chbegin = 0; roi.chend = 3; ImageBufAlgo::mul (A, A, 1.0f/2.2f, roi); \end{code} \apiend \apiitem{bool {\ce channel_sum} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc\spc const float *weights=NULL, \\ \bigspc\spc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!channel_sum} \indexapi{channel_sum} Converts a multi-channel image into a 1-channel image via a weighted sum of channels. For each pixel of {\cf src} within the designated ROI (defaulting to all of {\cf src}, if not defined), sum the channels designated by {\cf roi} and store the result in channel 0 of {\cf dst}. If {\cf weights} is not \NULL, {\cf weight[i]} will provide a per-channel weight (rather than defaulting to 1.0 for each channel). \smallskip \noindent Examples: \begin{code} // Compute luminance via a weighted sum of R,G,B // (assuming Rec709 primaries and a linear scale) float luma_weights[3] = { .2126, .7152, .0722 }; ImageBuf A ("a.exr"); ImageBuf B; ROI roi = A.roi(); roi.chbegin = 0; roi.chend = 3; ImageBufAlgo::channel_sum (B, A, luma_weights, roi); \end{code} \apiend \apiitem{bool {\ce clamp} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc float min = -std::numeric_limits::max(), \\ \bigspc float max = std::numeric_limits::max(), \\ \bigspc bool clampalpha01 = false, ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce clamp} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc const float *min = NULL, const float *max = NULL, \\ \bigspc bool clampalpha01 = false, ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!clamp} \indexapi{clamp} Copy pixels from {\cf src} to {\cf src} (within the {\cf roi}), clamping between the {\cf min} and {\cf max} values. Additionally, if {\cf clampalpha01} is {\cf true}, then any alpha channel is clamped to the 0--1 range. For the variety of {\cf clamp()} in which the {\cf min} and {\cf max} values are {\cf float}, the minimum and maximum will be applied to all color channels (or, at least, the subset of channels specified by {\cf roi}). For the variety of {\cf clamp()} in which the {\cf min} and {\cf max} parameters are pointers, they point to arrays giving per-channel minimum and maximum clamp values. If {\cf min} is \NULL, no minimum clamping is performed, and if {\cf max} is \NULL, no maximum clamping is performed. \smallskip \noindent Examples: \begin{code} // Clamp image buffer A in-place to the [0,1] range for all pixels. ImageBufAlgo::clamp (A, A, 0.0f, 1.0f); // Just clamp alpha to [0,1] ImageBufAlgo::clamp (A, A, -std::numeric_limits::max(), std::numeric_limits::max(), true); // Clamp R & G to [0,0.5], leave other channels alone std::vector min (A.nchannels(), -std::numeric_limits::max()); std::vector max (A.nchannels(), std::numeric_limits::max()); min[0] = 0.0f; max[0] = 0.5f; min[1] = 0.0f; max[1] = 0.5f; ImageBufAlgo::clamp (A, A, &min[0], &max[0], false); \end{code} \apiend \apiitem{bool {\ce rangecompress} (ImageBuf \&dst, const ImageBuf \&src, bool useluma = false, \\ \bigspc\bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce rangeexpand} (ImageBuf \&dst, const ImageBuf \&src, bool useluma = false, \\ \bigspc\bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!rangecompress} \indexapi{rangecompress} \index{ImageBufAlgo!rangeexpand} \indexapi{rangeexpand} Some image operations (such as resizing with a ``good'' filter that contains negative lobes) can have objectionable artifacts when applied to images with very high-contrast regions involving extra bright pixels (such as highlights in HDR captured or rendered images). One way to address this is by compressing the range of pixel values, then performing the operation (such as a resize), then re-expanding the range of the result again. This approach can yield results that are much more pleasing (even if not exactly mathematically correct). The {\cf rangecompress} operation does the following: For all pixels and color channels of {\cf src} within region {\cf roi} (defaulting to all the defined pixels of {\cf src}), copy the pixels to {\cf dst} while applying a logarithmic transformation on their values. Alpha and z channels are copied but not transformed. The {\cf rangeexpand} operation is the opposite of {\cf rangecompress}: it copies while rescaling the logarithmic color channel values back to a linear response. If {\cf useluma} is true, the luma of the first three channels (presumed to be R, G, and B) are used to compute a single scale factor for all color channels, rather than scaling all channels individually (which could result in a big color shift when performing {\cf rangecompress} and {\cf rangeexpand}). \smallskip \noindent Examples: \begin{code} // Resize the image to 640x480, using a Lanczos3 filter, which // has negative lobes. To prevent those negative lobes from // producing ringing or negative pixel values for HDR data, // do range compression, then resize, then re-expand the range. // 1. Read the original image ImageBuf Src ("tahoeHDR.exr"); // 2. Range compress to a logarithmic scale ImageBuf Compressed; ImageBufAlgo::rangecompress (Compressed, Src); // 3. Now do the resize ImageBuf Dst; ROI roi (0, 640, 0, 480, 0, 1, /*chans:*/ 0, Compressed.nchannels()); ImageBufAlgo::resize (Dst, Comrpessed, "lanczos3", 6.0, roi); // 4. Expand range to be linear again (operate in-place) ImageBufAlgo::rangeexpand (Dst, Dst); \end{code} \apiend \apiitem{bool {\ce over} (ImageBuf \&dst, const ImageBuf \&A, const ImageBuf \&B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!over} \indexapi{over} For all pixels within the designated region, combine the pixels of images {\cf A} and {\cf B} using the Porter-Duff ``over'' compositing operation, putting the result in {\cf dst}. Image {\cf A} is the ``foreground,'' and {\cf B} is the ``background.'' Images {\cf A} and {\cf B} must have the same number of channels and must both have an alpha channel. \smallskip \noindent Examples: \begin{code} ImageBuf A ("fg.exr"); ImageBuf B ("bg.exr"); ImageBuf Composite; ImageBufAlgo::over (Composite, A, B); \end{code} \apiend \apiitem{bool {\ce zover} (ImageBuf \&dst, const ImageBuf \&A, const ImageBuf \&B, \\ \bigspc bool z_zeroisinf = false, ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!zover} \indexapi{zover} For all pixels within the designated region, combine the pixels of images {\cf A} and {\cf B} using the Porter-Duff ``over'' compositing operation, putting the result in {\cf dst}. {\cf A} and {\cf B} must have the same number of channels and must both alpha and $z$ (depth) channels. Rather than {\cf A} always being the foreground (as it would be for the {\cf over()} function, the $z$ channel is used to select which image is foreground and which is background for each pixel separately, with a lower $z$ value being the foreground for that pixel. If {\cf z_zeroisinf} is {\cf true}, then $z=0$ values will be treated as if they are infinitely far away. \smallskip \noindent Examples: \begin{code} ImageBuf A ("a.exr"); ImageBuf B ("b.exr"); ImageBuf Composite; ImageBufAlgo::zover (Composite, A, B); \end{code} \apiend \section{Image comparison and statistics} \label{sec:iba:stats} \apiitem{bool {\ce computePixelStats} (PixelStats \&stats, const ImageBuf \&src, \\ \bigspc\bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!computePixelStats} \indexapi{computePixelStats} \label{sec:iba:computePixelStats} Compute statistics about the ROI of the image {\cf src}, storing results in {\cf stats} (each of the vectors within {\cf stats} will be automatically resized to the number of channels in the image). A return value of {\cf true} indicates success, {\cf false} indicates that it was not possible to complete the operation. The {\cf PixelStats} structure is defined as follows: \begin{code} struct PixelStats { std::vector min; std::vector max; std::vector avg; std::vector stddev; std::vector nancount; std::vector infcount; std::vector finitecount; std::vector sum, sum2; // for intermediate calculation }; \end{code} \smallskip \noindent Examples: \begin{code} ImageBuf A ("a.exr"); ImageBufAlgo::PixelStats stats; ImageBufAlgo::computePixelStats (stats, A); for (int c = 0; c < A.nchannels(); ++c) { std::cout << "Channel " << c << ":\n"; std::cout << " min = " << stats.min[c] << "\n"; std::cout << " max = " << stats.max[c] << "\n"; std::cout << " average = " << stats.avg[c] << "\n"; std::cout << " standard deviation = " << stats.stddev[c] << "\n"; std::cout << " # NaN values = " << stats.nancount[c] << "\n"; std::cout << " # Inf values = " << stats.infcount[c] << "\n"; std::cout << " # finite values = " << stats.finitecount[c] << "\n"; } \end{code} \apiend \apiitem{bool {\ce compare} (const ImageBuf \&A, const ImageBuf \&B, \\ \bigspc float failthresh, float warnthresh, CompareResults \&result,\\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!compare} \indexapi{compare} Numerically compare two images. The difference threshold (for any individual color channel in any pixel) for a ``failure'' is {\cf failthresh}, and for a ``warning'' is {\cf warnthresh}. The results are stored in {\cf result}. If {\cf roi} is defined, pixels will be compared for the pixel and channel range that is specified. If {\cf roi} is not defined, the comparison will be for all channels, on the union of the defined pixel windows of the two images (for either image, undefined pixels will be assumed to be black). The {\cf CompareResults} structure is defined as follows: \begin{code} struct CompareResults { double meanerror, rms_error, PSNR, maxerror; int maxx, maxy, maxz, maxc; imagesize_t nwarn, nfail; }; \end{code} \smallskip \noindent Examples: \begin{code} ImageBuf A ("a.exr"); ImageBuf B ("b.exr"); ImageBufAlgo::CompareResults comp; ImageBufAlgo::compare (A, B, 1.0f/255.0f, 0.0f, comp); if (comp.nwarn == 0 && comp.nfail == 0) { std::cout << "Images match within tolerance\n"; } else { std::cout << "Image differed: " << comp.nfail << " failures, " << comp.nwarn << " warnings.\n"; std::cout << "Average error was " << comp.meanerror << "\n"; std::cout << "RMS error was " << comp.rms_error << "\n"; std::cout << "PSNR was " << comp.PSNR << "\n"; std::cout << "largest error was " << comp.maxerror << " on pixel (" << comp.maxx << "," << comp.maxy << "," << comp.maxz << "), channel " << comp.maxc << "\n"; } \end{code} \apiend \begin{comment} compare_Yee is a bit half-baked. Leave it out of the docs for now. FIXME \apiitem{int {\ce compare_Yee} (const ImageBuf \&A, const ImageBuf \&B, \\ \bigspc CompareResults \&result, float luminance, float fov, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!compare_Yee} \indexapi{compare_Yee} \apiend \end{comment} \apiitem{bool {\ce isConstantColor} (const ImageBuf \&src, float *color, \\ \bigspc\bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!isConstantColor} \indexapi{isConstantColor} If all pixels of {\cf src} within the ROI have the same values (for the subset of channels described by {\cf roi}), return {\cf true} and store the values in {\cf color[roi.chbegin...roi.chend-1]}. Otherwise, return {\cf false}. \smallskip \noindent Examples: \begin{code} ImageBuf A ("a.exr"); std::vector color (A.nchannels()); if (ImageBufAlgo::isConstantColor (A, &color[0])) { std::cout << "The image has the same value in all pixels: "; for (int c = 0; c < A.nchannels(); ++c) std::cout << (c ? " " : "") << color[c]; std::cout << "\n"; } else { std::cout << "The image is not a solid color.\n"; } \end{code} \apiend \apiitem{bool {\ce isConstantChannel} (const ImageBuf \&src, int channel, float val, \\ \bigspc\bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!isConstantChannel} \indexapi{isConstantChannel} Returns {\cf true} if all pixels of {\cf src} within the ROI have the given {\cf channel} value {\cf val}. \smallskip \noindent Examples: \begin{code} ImageBuf A ("a.exr"); int alpha = A.spec().alpha_channel; if (alpha < 0) std::cout << "The image does not have an alpha channel\n"; else if (ImageBufAlgo::isConstantChannel (A, alpha, 1.0f)) std::cout << "The image has alpha = 1.0 everywhere\n"; else std::cout << "The image has alpha < 1 in at least one pixel\n"; \end{code} \apiend \apiitem{bool {\ce isMonochrome} (const ImageBuf \&src, ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!isMonochrome} \indexapi{isMonochrome} Returns {\cf true} if the image is monochrome within the ROI, that is, for all pixels within the region, do all channels {\cf [roi.chbegin, roi.chend)} have the same value? If roi is not defined (the default), it will be understood to be all of the defined pixels and channels of source. \smallskip \noindent Examples: \begin{code} ImageBuf A ("a.exr"); ROI roi = get_roi (A.spec()); roi.chend = std::min (3, roi.chend); // only test RGB, not alpha if (ImageBufAlgo::isMonochrome (A, roi)) std::cout << "a.exr is really grayscale\n"; \end{code} \apiend \apiitem{bool {\ce color_count} (const ImageBuf \&src, imagesize_t *count,\\ \bigspc int ncolors, const float *color, const float *eps=NULL, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!color_count} \indexapi{color_count} Count how many pixels in the image (within the ROI) match a list of colors. The colors to match are in: \begin{code} colors[0 ... nchans-1] colors[nchans ... 2*nchans-1] ... colors[(ncolors-1)*nchans ... (ncolors*nchans)-1] \end{code} \noindent and so on, a total of {\cf ncolors} consecutively stored colors of {\cf nchans} channels each ({\cf nchans} is the number of channels in the image, itself, it is not passed as a parameter). The values in {\cf eps[0..nchans-1]} are the error tolerances for a match, for each channel. Setting {\cf eps[c]} to {\cf numeric_limits::max()} will effectively make it ignore the channel. Passing {\cf eps == NULL} will be interpreted as a tolerance of 0.001 for all channels (requires exact matches for 8 bit images, but allows a wee bit of imprecision for {\cf float} images. \smallskip \noindent Examples: \begin{code} ImageBuf A ("a.exr"); int n = A.nchannels(); // Try to match two colors: pure red and green std::vector colors (2*n, numeric_limits::max()); colors[0] = 1.0f; colors[1] = 0.0f; colors[2] = 0.0f; colors[n+0] = 0.0f; colors[n+1] = 1.0f; colors[n+2] = 0.0f; const int ncolors = 2; imagesize_t count[ncolors]; ImageBufAlgo::color_count (A, count, ncolors); std::cout << "Number of red pixels : " << count[0] << "\n"; std::cout << "Number of green pixels : " << count[1] << "\n"; \end{code} \apiend \apiitem{bool {\ce color_range_check} (const ImageBuf \&src, imagesize_t *lowcount, \\ \bigspc imagesize_t *highcount, imagesize_t *inrangecount, \\ \bigspc const float *low, const float *high, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!color_range_check} \indexapi{color_range_check} Count how many pixels in the image (within the ROI) are outside the value range described by {\cf low[roi.chbegin..roi.chend-1]} and {\cf high[roi.chbegin..roi.chend-1]} as the low and high acceptable values for each color channel. The number of pixels containing values that fall below the lower bound will be stored in {\cf *lowcount}, the number of pixels containing values that fall above the upper bound will be stored in {\cf *highcount}, and the number of pixels for which all channels fell within the bounds will be stored in {\cf *inrangecount}. Any of these may be NULL, which simply means that the counts need not be collected or stored. \smallskip \noindent Examples: \begin{code} ImageBuf A ("a.exr"); ROI roi = get_roi (A.spec()); roi.chend = std::min (roi.chend, 4); // only compare RGBA float low[] = {0, 0, 0, 0}; float high[] = {1, 1, 1, 1}; imagesize_t lowcount, highcount, inrangecount; ImageBufAlgo::color_range_check (A, &lowcount, &highcount, &inrangecount, low, high, roi); std::cout << lowcount << " pixels had components < 0\n"; std::cout << highcount << " pixels had components > 1\n"; std::cout << inrangecount << " pixels were fully within [0,1] range\n"; \end{code} \apiend \apiitem{ROI {\ce nonzero_region} (const ImageBuf \&src, ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!nonzero_region} \indexapi{nonzero_region} Find the minimal rectangular region within {\cf roi} (which defaults to the entire pixel data window of {\cf src}) that consists of nonzero pixel values. In other words, gives the region that is a ``shrink-wraps'' of {\cf src} to exclude black border pixels. Note that if the entire image was black, the ROI returned will contain no pixels. For ``deep'' images, this function returns the smallest ROI that contains all pixels that contain depth samples, and excludes the border pixels that contain no depth samples at all. \smallskip \noindent Examples: \begin{code} ImageBuf A ("a.exr"); ROI shrunk = ImageBufAlgo::nonzero_region (A); if (shrunk.undefined()) std::cout << "All pixels were empty\n"; else std::cout << "Non-empty region was " << shrunk << "\n"; \end{code} \apiend \apiitem{std::string {\ce computePixelHashSHA1} (const ImageBuf \&src, \\ \bigspc\bigspc string_view extrainfo = "", \\ \bigspc\bigspc ROI roi=ROI::All(), int blocksize=0, int nthreads=0)} \index{ImageBufAlgo!computePixelHashSHA1} \indexapi{computePixelHashSHA1} Compute the SHA-1 byte hash for all the pixels in the specifed region of the image. If {\cf blocksize} $> 0$, the function will compute separate SHA-1 hashes of each {\cf blocksize} batch of scanlines, then return a hash of the individual hashes. This is just as strong a hash, but will NOT match a single hash of the entire image ({\cf blocksize == 0}). But by breaking up the hash into independent blocks, we can parallelize across multiple threads, given by {\cf nthreads}. The {\cf extrainfo} provides additional text that will be incorporated into the hash. \smallskip \noindent Examples: \begin{code} ImageBuf A ("a.exr"); std::string hash; hash = ImageBufAlgo::computePixelHashSHA1 (A, "", ROI::All(), 64); \end{code} \apiend \apiitem{bool {\ce histogram} (const ImageBuf \&src, int channel, \\ \bigspc std::vector \&histogram, int bins=256, \\ \bigspc float min=0, float max=1, imagesize_t *submin=NULL, \\ \bigspc imagesize_t *supermax=NULL, ROI roi=ROI::All())} \index{ImageBufAlgo!histogram} \indexapi{histogram} Computes a histogram of the given {\cf channel} of image {\cf src}, within the ROI, as follows: The vector {\cf histogram[0..bins-1]} will contain the count of pixels whose value was in each of the equally-sized range bins between {\cf min} and {\cf max}; if {\cf submin} is not \NULL, it specifies storage of the number of pixels whose value was $<$ {\cf min}; if {\cf supermax} is not \NULL, it specifies storage of the number of pixels whose value was $>$ {\cf max}. \smallskip \noindent Examples: \begin{code} ImageBuf Src ("tahoe.exr"); const int bins = 4; std::vector hist (bins, 0); imagesize_t submin=0, supermax=0; ImageBufAlgo::histogram (Src, 0, hist, bins, 0.0f, 1.0f, &submin, &supermax); std::cout << "Channel 0 of the image had:\n"; float binsize = (max-min)/nbins; for (int i = 0; i < nbins; ++i) hist[i] << " pixels that are >= " << (min+i*binsize) << " and " << (i == nbins-1 ? " <= " : " < ") << (min+(i+1)*binsize) << "\n"; std::cout << submin << " pixels < " << min << "\n"; std::cout << supermax << " pixels > " << max << "\n"; \end{code} \apiend \begin{comment} % I'm not documenting histogram_draw at this point because as I started % writing this section, I realized I really don't like this function % as it is written and would like to rewrite its specification when I % get the chance. \apiitem{bool {\ce histogram_draw} (ImageBuf \&dst, \bigspc const std::vector \&histogram)} \index{ImageBufAlgo!histogram_draw} \indexapi{histogram_draw} \smallskip \noindent Examples: \begin{code} \end{code} \apiend \end{comment} \section{Convolutions} \label{sec:iba:convolutions} \apiitem{bool {\ce make_kernel} (ImageBuf \&dst, string_view name, \\ \bigspc\spc float width, float height, float depth = 1.0f, \\ \bigspc\spc bool normalize = true)} \index{ImageBufAlgo!make_kernel} \indexapi{make_kernel} Initialize {\cf dst} to be a 1-channel {\cf float} image of the named kernel. The size of the {\cf dst} image will be big enough to contain the kernel given its size ({\cf width} $\times$ {\cf height}) and rounded up to odd resolution so that the center of the kernel can be at the center of the middle pixel. The kernel image will be offset so that its center is at the {\cf (0,0)} coordinate. If {\cf normalize} is true, the values will be normalized so that they sum to $1.0$. If {\cf depth} $> 1$, a volumetric kernel will be created. Use with caution! Kernel names can be: \qkw{gaussian}, \qkw{sharp-gaussian}, \qkw{box}, \qkw{triangle}, \qkw{mitchell}, \qkw{blackman-harris}, \qkw{b-spline}, \qkw{catmull-rom}, \qkw{lanczos3}, \qkw{cubic}, \qkw{keys}, \qkw{simon}, \qkw{rifman}, \qkw{disk}, \qkw{binomial}, \qkw{laplacian}. Note that \qkw{catmull-rom} and \qkw{lanczos3} are fixed-size kernels that don't scale with the width, and are therefore probably less useful in most cases. \smallskip \noindent Examples: \begin{code} ImageBuf K; ImageBufAlgo::make_kernel (K, "gaussian", 5.0f, 5.0f); \end{code} \apiend \apiitem{bool {\ce convolve} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc const ImageBuf \&kernel, bool normalize = true, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!convolve} \indexapi{convolve} Replace the given ROI of {\cf dst} with the convolution of {\cf src} and a kernel. If {\cf roi} is not defined, it defaults to the full size of {\cf dst} (or {\cf src}, if {\cf dst} was uninitialized). If {\cf dst} is uninitialized, it will be allocated to be the size specified by {\cf roi}. If {\cf normalized} is {\cf true}, the kernel will be normalized for the convolution, otherwise the original values will be used. \smallskip \noindent Examples: \begin{code} // Blur an image with a 5x5 Gaussian kernel ImageBuf Src ("tahoe.exr"); ImageBuf K; ImageBufAlgo::make_kernel (K, "gaussian", 5.0f, 5.0f); ImageBuf Blurred; ImageBufAlgo::convolve (Blurred, Src, K); \end{code} \spc \begin{tabular}{lll} \includegraphics[width=1.5in]{figures/tahoe-small.jpg} & \includegraphics[width=1.5in]{figures/tahoe-blur.jpg} \\ original & blurred \\ \end{tabular} \apiend \apiitem{bool {\ce laplacian} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc\spc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!laplacian} \indexapi{laplacian} \NEW % 1.7 Replace the given ROI of {\cf dst} with the Laplacian of the corresponding region of {\cf src}. The Laplacian is the generalized second derivative of the image, $$\frac{\partial^2 s}{\partial x^2} + \frac{\partial^2 s}{\partial y^2}$$ which is approximated by convolving the image with a discrete $3 \times 3$ Laplacian kernel, \[ \left( \begin{array}{ccc} 0 & 1 & 0 \\ 1 & -4 & 1 \\ 0 & 1 & 0 \end{array} \right)\] \smallskip \noindent Example: \begin{code} ImageBuf src ("tahoe.exr"); ImageBuf dst; ImageBufAlgo::laplacian (dst, src); \end{code} \spc \begin{tabular}{ll} \includegraphics[width=1.5in]{figures/tahoe-small.jpg} & \includegraphics[width=1.5in]{figures/tahoe-laplacian.jpg} \\ original & Laplacian image \\ \end{tabular} \apiend \apiitem{bool {\ce fft} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce ifft} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!fft} \indexapi{fft} \index{ImageBufAlgo!ifft} \indexapi{ifft} The {\cf fft()} function takes the discrete Fourier transform (DFT) of the section of {\cf src} denoted by {\cf roi}, storing it in {\cf dst}. If {\cf roi} is not defined, it will be all of {\cf src}'s pixels. Only one channel of {\cf src} may be transformed at a time, so it will be the first channel described by {\cf roi} (or, again, channel 0 if {\cf roi} is undefined). If not already in the correct format, {\cf dst} will be re-allocated to be a 2-channel {\cf float} buffer of size {\cf roi.width()} $\times$ {\cf roi.height}, with channel 0 being the ``real'' part and channel 1 being the the ``imaginary'' part. The values returned are actually the unitary DFT, meaning that it is scaled by $1/\sqrt{\mathrm{npixels}}$. \smallskip \noindent Examples: \begin{code} ImageBuf Src ("tahoe.exr"); // Take the DFT of the first channel of Src ImageBuf Freq; ImageBufAlgo::fft (Freq, Src); // At this point, Freq is a 2-channel float image (real, imag) // Convert it back from frequency domain to a spatial image ImageBuf Spatial; ImageBufAlgo::ifft (Spatial, Freq); \end{code} \apiend \apiitem{bool {\ce complex_to_polar} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce polar_to_complex} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!complex_to_polar} \indexapi{complex_to_polar} \index{ImageBufAlgo!polar_to_complex} \indexapi{polar_to_complex} The {\cf polar_to_complex()} function transforms a 2-channel image whose channels are interpreted as complex values (real and imaginary components) into the equivalent values expressed in polar form of amplitude and phase (with phase between $0$ and $2\pi$). The {\cf complex_to_polar()} function performs the reverse transformation, converting from polar values (amplitude and phase) to complex (real and imaginary). In either case, the section of {\cf src} denoted by {\cf roi} is transformed, storing the result in {\cf dst}. If {\cf roi} is not defined, it will be all of {\cf src}'s pixels. Only the first two channels of {\cf src} will be transformed. \smallskip \noindent Examples: \begin{code} // Suppose we have a set of frequency space values expressed as // amplitudes and phase... ImageBuf Polar ("polar.exr"); // Convert to complex representation ImageBuf Complex; ImageBufAlgo::complex_to_polar (Complex, Polar); // Now, it's safe to take an IFFT of the complex image. // Convert it back from frequency domain to a spatial image. ImageBuf Spatial; ImageBufAlgo::ifft (Spatial, Complex); \end{code} \apiend \section{Image Enhancement / Restoration} \label{sec:iba:enhance} \apiitem{bool {\ce fixNonFinite} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc\spc NonFiniteFixMode mode = NONFINITE_BOX3, \\ \bigspc\spc int *pixelsFixed = NULL, \\ \bigspc\spc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!fixNonFinite} \indexapi{fixNonFinite} Copy pixel values from {\cf src} to {\cf dst} (within the pixel and channel range designated by {\cf roi}), and repair any non-finite ({\cf NaN} or {\cf Inf}) pixels. If {\cf pixelsFound} is not \NULL, store in it the number of pixels that contained non-finite value. How the non-finite values are repaired is specified by one of the following modes: \begin{description} \item[\spc] \spc \item[\rm \kw{NONFINITE_NONE}] do not alter the pixels (but do count the number of nonfinite pixels in {\cf *pixelsFixed}, if non-\NULL). \item[\rm \kw{NONFINITE_BLACK}] change non-finite values to 0. \item[\rm \kw{NONFINITE_BOX3}] replace non-finite values by the average of any finite pixels within a 3x3 window. \item[\rm \kw{NONFINITE_ERROR}] do not alter non-finite values when copying, but return {\cf false} and set an error if any non-finite values are found. \end{description} This works on all pixel data types, though it's just a copy for images with pixel data types that cannot represent {\cf NaN} or {\cf Inf} values. \smallskip \noindent Examples: \begin{code} ImageBuf Src ("tahoe.exr"); int pixelsFixed = 0; ImageBufAlgo::fixNonFinite (Src, Src, ImageBufAlgo::NONFINITE_BOX3, &pixelsFixed); std::cout << "Repaired " << pixelsFixed << " non-finite pixels\n"; \end{code} \apiend \apiitem{bool {\ce fillholes_pushpull} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!fillholes_pushpull} \indexapi{fillholes_pushpull} Copy the specified ROI of {\cf src} to {\cf dst} and fill any holes (pixels where alpha $< 1$) with plausible values using a push-pull technique. The {\cf src} image must have an alpha channel. The dst image will end up with a copy of src, but will have an alpha of 1.0 everywhere, and any place where the alpha of src was < 1, dst will have a pixel color that is a plausible ``filling'' of the original alpha hole. \smallskip \noindent Examples: \begin{code} ImageBuf Src ("holes.exr"); ImageBuf Filled; ImageBufAlgo::fillholes_pushpull (Filled, Src); \end{code} \apiend \apiitem{bool {\ce median_filter} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc\spc int width = 3, int height = -1, \\ \bigspc\spc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!median_filter} \indexapi{median_filter} Replace the given ROI of {\cf dst} with a median-filtered version of the corresponding region of {\cf src}. The median filter replaces each pixel with the median value underneath the $\mathit{width} \times \mathit{height}$ window surrounding it. If the height is $< 1$, it will be set to width, making a square window. The median filter tends to smooth out noise and small high frequency details that are smaller than the window size, while preserving the sharpness of long edges. \smallskip \noindent Examples: \begin{code} ImageBuf Noisy ("tahoe.exr"); ImageBuf Clean; ImageBufAlgo::median_filter (Clean, Noisy, 3, 3); \end{code} \spc \begin{tabular}{lll} \includegraphics[width=1.5in]{figures/tahoe-small.jpg} & \includegraphics[width=1.5in]{figures/tahoe-pepper.jpg} & \includegraphics[width=1.5in]{figures/tahoe-pepper-median.jpg} \\ original & with dropouts & median filtered \\ \end{tabular} \apiend \apiitem{bool {\ce dilate} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc\spc int width = 3, int height = -1, \\ \bigspc\spc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce erode} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc\spc int width = 3, int height = -1, \\ \bigspc\spc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!dilate} \indexapi{dilate} \index{ImageBufAlgo!erode} \indexapi{erode} \index{morphological filtering} \NEW % 1.7 Replace the given ROI of {\cf dst} with the dilated or eroded version of the corresponding region of {\cf src}. The dilate operation replaces each pixel with the maximum value underneath the $\mathit{width} \times \mathit{height}$ window surrounding it, and the erode operation does the same for the minimum value under the window. If the height is $< 1$, it will be set to width, making a square window. Dilation makes bright features wider and more prominent, dark features thinner, and removes small isolated dark spots. Erosion makes dark features wider, bright features thinner, and removes small isolated bright spots. Dilation and erosion are basic morphological filters, and more complex ones are often constructed from them: \begin{itemize} \item \item ``open'' is erode followed by dilate, and it keeps the overall shape while removing small bright regions; \item ``close'' is dilate followed by erode, and it keeps the overall shape while removing small dark regions; \item ``morphological gradient'' is dilate minus erode, which gives a bright perimeter edge; \item ``tophat'' is the original source minus the ``open'', which isolates local peaks; \item ``bottomhat'' is the ``close'' minus the original source, which isolates dark holes. \end{itemize} \smallskip \noindent Examples: \begin{code} ImageBuf Source ("source.tif"); ImageBuf Dilate, Erode, Opene, Close, Gradient, Tophat, Bottomhat; ImageBufAlgo::dilate (Dilated, Source, 3, 3); ImageBufAlgo::erode (Eroded, Source, 3, 3); // Morphological "open" is dilate(erode((source)) ImageBufAlgo::dilate (Opene, Erode, 3, 3); // Morphological "close" is erode(dilate(source)) ImageBufAlgo::erode (Close, Dilate, 3, 3); // Morphological "gradient" is dilate minus erode ImageBufAlgo::sub (Gradient, Dilate, Erode); // Tophat filter is source minus open ImageBufAlgo::sub (Tophat, Source, Opene); // Bottomhat filter is close minus source ImageBufAlgo::sub (Bottomhat, Close, Source); \end{code} \spc \begin{tabular}{llll} \includegraphics[width=0.75in]{figures/morphsource.jpg} & \includegraphics[width=0.75in]{figures/dilate.jpg} & \includegraphics[width=0.75in]{figures/erode.jpg} & \includegraphics[width=0.75in]{figures/morphopen.jpg} \\ original & dilate & erode & open \\ \includegraphics[width=0.75in]{figures/morphclose.jpg} & \includegraphics[width=0.75in]{figures/morphgradient.jpg} & \includegraphics[width=0.75in]{figures/tophat.jpg} & \includegraphics[width=0.75in]{figures/bottomhat.jpg} \\ close & gradient & tophat & bottomhat \\ \end{tabular} \apiend \apiitem{bool {\ce unsharp_mask} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc\spc string_view kernel = "gaussian", float width = 3.0f, \\ \bigspc\spc float contrast = 1.0f, float threshold = 0.0f, \\ \bigspc\spc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!unsharp_mask} \indexapi{unsharp_mask} \label{sec:iba:unsharpmask} Replace the given ROI of {\cf dst} with a sharpened version of the corresponding region of {\cf src} using the ``unsharp mask'' technique. Unsharp masking basically works by first blurring the image (low pass filter), subtracting this from the original image, then adding the residual back to the original to emphasize the edges. Roughly speaking, \begin{code} dst = src + contrast * thresh(src - blur(src)) \end{code} The specific blur can be selected by kernel name and width (for example, \qkw{gaussian} is typical). As a special case, \qkw{median} is also accepted as the kernel name, in which case a median filter is performed rather than a blurring convolution (Gaussian and other blurs sometimes over-sharpen edges, whereas using the median filter will sharpen compact high-frequency details while not over-sharpening long edges). The {\cf contrast} is a multiplier on the overall sharpening effect. The thresholding step causes all differences less than {\cf threshold} to be squashed to zero, which can be useful for suppressing sharpening of low-contrast details (like noise) but allow sharpening of higher-contrast edges. \smallskip \noindent Examples: \begin{code} ImageBuf Blurry ("tahoe.exr"); ImageBuf Sharp; ImageBufAlgo::unsharp_mask (Sharp, Blurry, "gaussian", 5.0f); \end{code} \apiend \section{Color manipulation} \label{sec:iba:color} \apiitem{bool {\ce colorconvert} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc\spc string_view from, string_view to, bool unpremult=false, \\ \bigspc\spc string_view context_key="", string_view context_value="", \\ \bigspc\spc ColorConfig *colorconfig=NULL, \\ \bigspc\spc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce colorconvert} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc\spc const ColorProcessor *processor, bool unpremult=false, \\ \bigspc\spc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!colorconvert} \indexapi{colorconvert} Copy pixels from {\cf src} to {\cf dst} (within the ROI), while applying a color transform to the pixel values. In-place operations ({\cf dst} and {\cf src} being the same image) are supported. If {\cf unpremult} is {\cf true}, unpremultiply before color conversion, then premultiply again after the color conversion. You may want to use this flag if your image contains an alpha channel. The first form of this function specifies the ``from'' and ``to'' color spaces by name. An optional {\cf ColorConfig} is specified, but {\cf NULL} is passed, the default OCIO color configuration found by examining the {\cf \$OCIO} environment variable will be used instead. The second form is directly passed a {\cf ColorProcessor}, which is is a special object created by a {\cf ColorConfig} (see {\cf OpenImageIO/color.h} for details). The {\cf context_key} and {\cf context_value} may optionally be used to establish a context (for example, a shot-specific transform). If OIIO was built with OpenColorIO support enabled, then the transformation may be between any two spaces supported by the active OCIO configuration, or may be a ``look'' transformation created by {\cf ColorConfig::createLookTransform}. If OIIO was not built with OpenColorIO support enabled, then the only transformations available are from \qkw{sRGB} to \qkw{linear} and vice versa. \smallskip \noindent Examples: \begin{code} #include #include using namespace OIIO; ImageBuf Src ("tahoe.jpg"); ImageBuf Dst; ColorConfig cc; ColorProcessor *processor = cc.createColorProcessor ("vd8", "lnf"); ImageBufAlgo::colorconvert (Dst, Src, processor, true); ColorProcessor::deleteColorProcessor (processor); // Equivalent, though possibly less efficient if you will be // converting many images using the same transformation: ImageBuf Src ("tahoe.jpg"); ImageBuf Dst; ImageBufAlgo::colorconvert (Dst, Src, "vd8", "lnf", true); \end{code} \apiend \apiitem{bool {\ce ociolook} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc\spc string_view looks, string_view from, string_view to, \\ \bigspc\spc bool inverse=false, bool unpremult=false, \\ \bigspc\spc string_view context_key="", string_view context_value="", \\ \bigspc\spc ColorConfig *colorconfig=NULL, \\ \bigspc\spc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!ociolook} \indexapi{ociolook} Copy pixels from {\cf src} to {\cf dst} (within the ROI), while applying an OpenColorIO ``look'' transform to the pixel values. In-place operations ({\cf dst} and {\cf src} being the same image) are supported. The {\cf context_key} and {\cf context_value} may optionally be used to establish a context (for example, a shot-specific transform). If {\cf inverse} is {\cf true}, it will reverse the color transformation and look application. If {\cf unpremult} is {\cf true}, unpremultiply before color conversion, then premultiply again after the color conversion. You may want to use this flag if your image contains an alpha channel. An optional {\cf ColorConfig} is specified, but {\cf NULL} is passed, the default OCIO color configuration found by examining the {\cf \$OCIO} environment variable will be used instead. \smallskip \noindent Examples: \begin{code} ImageBuf Src ("tahoe.jpg"); ImageBuf Dst; ImageBufAlgo::ociolook (Dst, Src, "look", "vd8", "lnf", false, false, "SHOT", "pe0012"); \end{code} \apiend \apiitem{bool {\ce ociodisplay} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc\spc string_view display, string_view view, \\ \bigspc\spc string_view from="", string_view looks="",\\ \bigspc\spc bool unpremult=false, \\ \bigspc\spc string_view key="", string_view value="", \\ \bigspc\spc ColorConfig *colorconfig=NULL, \\ \bigspc\spc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!ociodisplay} \indexapi{ociodisplay} Copy pixels from {\cf src} to {\cf dst} (within the ROI), while applying an OpenColorIO ``display'' transform to the pixel values. In-place operations ({\cf dst} and {\cf src} being the same image) are supported. If {\cf from} or {\cf looks} are empty, it will not override the look or source color space (subtly different than passing \qkw{}, the empty string, which means to use no look or source space). The {\cf key} and {\cf value} may optionally be used to establish a context (for example, a shot-specific transform). If {\cf inverse} is {\cf true}, it will reverse the color transformation and look application. If {\cf unpremult} is {\cf true}, unpremultiply before color conversion, then premultiply again after the color conversion. You may want to use this flag if your image contains an alpha channel. An optional {\cf ColorConfig} is specified, but {\cf NULL} is passed, the default OCIO color configuration found by examining the {\cf \$OCIO} environment variable will be used instead. \smallskip \noindent Examples: \begin{code} ImageBuf Src ("tahoe.exr"); ImageBuf Dst; ImageBufAlgo::ociodisplay (Dst, Src, "sRGB", "Film", "lnf", NULL, false, "SHOT", "pe0012"); \end{code} \apiend \apiitem{bool {\ce ociofiletransform} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc\spc string_view name, bool inverse=false, bool unpremult=false, \\ \bigspc\spc ColorConfig *colorconfig=NULL, \\ \bigspc\spc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!ociofiletransform} \indexapi{ociofiletransform} Copy pixels from {\cf src} to {\cf dst} (within the ROI), while applying an OpenColorIO ``file'' transform to the pixel values. If {\cf inverse} is {\cf true}, it will reverse the color transformation and look application. In- place operations ({\cf dst} and {\cf src} being the same image) are supported. If {\cf unpremult} is {\cf true}, unpremultiply before color conversion, then premultiply again after the color conversion. You may want to use this flag if your image contains an alpha channel. An optional {\cf ColorConfig} is specified, but {\cf NULL} is passed, the default OCIO color configuration found by examining the {\cf \$OCIO} environment variable will be used instead. \smallskip \noindent Examples: \begin{code} ImageBuf Src ("tahoe.jpg"); ImageBuf Dst; ImageBufAlgo::ociofiletransform (Dst, Src, "footransform.csp"); \end{code} \apiend \apiitem{bool {\ce unpremult} (ImageBuf \&dst, const ImageBuf \&src,\\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!unpremult} \indexapi{unpremult} Copy pixels from {\cf src} to {\cf dst}, and in the process divide all color channels (those not alpha or z) by the alpha value, to ``un-premultiply'' them. This presumes that the image starts of as ``associated alpha'' a.k.a.\ ``premultipled.'' The alterations are restricted to the pixels and channels of the supplied ROI (which defaults to all of {\cf src}). Pixels in which the alpha channel is 0 will not be modified (since the operation is undefined in that case). This is just a copy if there is no identified alpha channel (and a no-op if {\cf dst} and {\cf src} are the same image). \smallskip \noindent Examples: \begin{code} // Convert in-place from associated alpha to unassociated alpha ImageBuf A ("a.exr"); ImageBufAlgo::unpremult (A, A); \end{code} \apiend \apiitem{bool {\ce premult} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!premult} \indexapi{premult} Copy pixels from {\cf src} to {\cf dst}, and in the process multiply all color channels (those not alpha or z) by the alpha value, to ``premultiply'' them. This presumes that the image starts of as ``unassociated alpha'' a.k.a.\ ``non-premultipled.'' The alterations are restricted to the pixels and channels of the supplied ROI (which defaults to all of {\cf src}). This is just a copy if there is no identified alpha channel (and a no-op if {\cf dst} and {\cf src} are the same image). \smallskip \noindent Examples: \begin{code} // Convert in-place from unassociated alpha to associated alpha ImageBuf A ("a.exr"); ImageBufAlgo::premult (A, A); \end{code} \apiend \section{Import / export} \label{sec:iba:importexport} \apiitem{bool {\ce make_texture} (MakeTextureMode mode, const ImageBuf \&input, \\ \bigspc string_view outputfilename, const ImageSpec \&config,\\ \bigspc std::ostream *outstream = NULL) \\ bool {\ce make_texture} (MakeTextureMode mode, string_view filename, \\ \bigspc string_view outputfilename, const ImageSpec \&config,\\ \bigspc std::ostream *outstream = NULL)} \index{ImageBufAlgo!make_texture} \indexapi{make_texture} Turn an image file (either an existing \ImageBuf or specified by {\cf filename}) into a tiled, MIP-mapped, texture file and write to the file named by ({\cf outputfilename}). The {\cf mode} describes what type of texture file we are creating and may be one of the following: \noindent \begin{tabular}{p{2in}p{3in}} {\cf MakeTxTexture} & Ordinary 2D texture\\ %MakeTxShadow & \\ {\cf MakeTxEnvLatl} & Latitude-longitude environment map\\ {\cf \small MakeTxEnvLatlFromLightProbe} & Latitude-longitude environment map constructed from a ``light probe'' image.\\ \end{tabular} If the {\cf outstream} pointer is not \NULL, it should point to a stream (for example, {\cf \&std::out}, or a pointer to a local {\cf std::stringstream} to capture output), which is where console output and error messages will be deposited. The {\cf config} is an \ImageSpec that contains all the information and special instructions for making the texture. Anything set in {\cf config} (format, tile size, or named metadata) will take precedence over whatever is specified by the input file itself. Additionally, named metadata that starts with \qkw{maketx:} will not be output to the file itself, but may contain instructions controlling how the texture is created. The full list of supported configuration options is: \noindent Named fields: \begin{tabular}{ >{\cf}l p{4in}} format & Data format of the texture file (default: UNKNOWN = same format as the input) \\ tile_width & \multirow{3}{*}{Preferred tile size (default: 64x64x1)} \\ tile_height & \\ tile_depth & \\ \end{tabular} \medskip \noindent Metadata in {\cf config.extra_attribs}: \begin{longtable}{ >{\spc \cf\small}p{1.8in} >{\cf\small}l p{3in}} compression & string & Default: "zip" \\ fovcot & float & Default: aspect ratio of the image resolution \\ planarconfig & string & Default: "separate" \\ worldtocamera & matrix & World-to-camera matrix of the view. \\ worldtoscreen & matrix & World-to-screen space matrix of the view. \\ wrapmodes & string & Default: "black,black" \\ maketx:verbose & int & How much detail should go to outstream (0). \\ maketx:stats & int & If nonzero, print stats to outstream (0). \\ maketx:resize & int & If nonzero, resize to power of 2. (0) \\ maketx:nomipmap & int & If nonzero, only output the top MIP level (0). \\ maketx:updatemode & int & If nonzero, write new output only if the output file doesn't already exist, or is older than the input file, or was created with different command-line arguments. (0) \\ \multicolumn{2}{l}{\spc \cf\small maketx:constant_color_detect} \\ & int & If nonzero, detect images that are entirely one color, and change them to be low resolution (default: 0). \\ \multicolumn{2}{l}{\spc \cf\small maketx:monochrome_detect} \\ & int & If nonzero, change RGB images which have R==G==B everywhere to single-channel grayscale (default: 0). \\ maketx:opaque_detect & int & If nonzero, drop the alpha channel if alpha is 1.0 in all pixels (default: 0). \\ maketx:unpremult & int & If nonzero, unpremultiply color by alpha before color conversion, then multiply by alpha after color conversion (default: 0). \\ {\small maketx:incolorspace} & string & \\ {\small maketx:outcolorspace} & string & These two together will apply a color conversion (with OpenColorIO, if compiled). Default: "" \\ maketx:checknan & int & If nonzero, will consider it an error if the input image has any NaN pixels. (0) \\ maketx:fixnan & string & If set to "black" or "box3", will attempt to repair any NaN pixels found in the input image (default: "none"). \\ \multicolumn{2}{l}{\spc \cf\small maketx:set_full_to_pixels} \\ & int & If nonzero, doctors the full/display window of the texture to be identical to the pixel/data window and reset the origin to 0,0 (default: 0). \\ maketx:filtername & string & If set, will specify the name of a high-quality filter to use when resampling for MIPmap levels. Default: "", use bilinear resampling. \\ maketx:highlightcomp & int & If nonzero, performs highlight compensation -- range compression and expansion around the resize, plus clamping negative plxel values to zero. This reduces ringing when using filters with negative lobes. \\ maketx:nchannels & int & If nonzero, will specify how many channels the output texture should have, padding with 0 values or dropping channels, if it doesn't the number of channels in the input. (default: 0, meaning keep all input channels) \\ maketx:channelnames & string & If set, overrides the channel names of the output image (comma-separated). \\ {\small maketx:fileformatname} & string & If set, will specify the output file format. (default: "", meaning infer the format from the output filename) \\ \multicolumn{2}{l}{\spc \cf\small maketx:prman_metadata} \\ & int & If set, output some metadata that PRMan will need for its textures. (0) \\ {\small maketx:oiio_options} & int & (Deprecated; all are handled by default) \\ \multicolumn{2}{l}{\spc \cf\small maketx:prman_options} \\ & int & If nonzero, override a whole bunch of settings as needed to make textures that are compatible with PRMan. (0) \\ maketx:mipimages & string & Semicolon-separated list of alternate images to be used for individual MIPmap levels, rather than simply downsizing. (default: "") \\ \multicolumn{2}{l}{\spc \cf\small maketx:full_command_line} \\ & string & The command or program used to generate this call, will be embedded in the metadata. (default: "") \\ \multicolumn{2}{l}{\spc \cf\small maketx:ignore_unassoc} \\ & int & If nonzero, will disbelieve any evidence that the input image is unassociated alpha. (0) \\ \multicolumn{2}{l}{\spc \cf\small maketx:read_local_MB} \\ & int & If nonzero, will read the full input file locally if it is smaller than this threshold. Zero causes the system to make a good guess at a reasonable threshold (e.g. 1 GB). (0) \\ maketx:forcefloat & int & Forces a conversion through float data for the sake of ImageBuf math. (1) \\ maketx:hash & int & Compute the sha1 hash of the file in parallel. (1) \\ \multicolumn{2}{l}{\spc \cf\small maketx:allow_pixel_shift} \\ & int & Allow up to a half pixel shift per mipmap level. The fastest path may result in a slight shift in the image, accumulated for each mip level with an odd resolution. (0) \\ \end{longtable} \smallskip \noindent Examples: \begin{code} // This command line: // maketx in.exr --hicomp --filter lanczos3 --opaque-detect \ // -o texture.exr // is equivalent to: ImageBuf Input ("in.exr"); ImageSpec config; config.attribute ("maketx:highlightcomp", 1); config.attribute ("maketx:filtername", "lanczos3"); config.attribute ("maketx:opaquedetect", 1); stringstream s; bool ok = ImageBufAlgo::make_texture (ImageBufAlgo::MakeTxTexture, Input, "texture.exr", config, &s); if (! ok) std::cout << "make_texture error: " << s.str() << "\n"; \end{code} \apiend \apiitem{bool {\ce from_IplImage} (ImageBuf \&dst, const IplImage *ipl, \\ \bigspc\bigspc TypeDesc convert = TypeDesc::UNKNOWN)} \index{ImageBufAlgo!from_IplImage} \indexapi{from_IplImage} \index{OpenCV}\indexapi{IplImage}\index{Intel Image Library} Convert an {\cf IplImage}, used by OpenCV and Intel's Image Libray, and set {\cf dst} to be the same image (copying the pixels). If {\cf convert} is not set to {\cf UNKNOWN}, try to establish {\cf dst} as holding that data type and convert the {\cf IplImage} data. Return {\cf true} if ok, {\cf false} if it couldn't figure out how to make the conversion from {\cf IplImage} to an \ImageBuf. If OpenImageIO was compiled without OpenCV support, this function will return false without modifying {\cf dst}. \begin{comment} \smallskip \noindent Examples: \begin{code} \end{code} \end{comment} \apiend \apiitem{IplImage* {\ce to_IplImage} (const ImageBuf \&src)} \index{ImageBufAlgo!to_IplImage} \indexapi{to_IplImage} \index{OpenCV}\indexapi{IplImage}\index{Intel Image Library} Construct an {\cf IplImage*}, used by OpenCV and Intel's Image Library, that is equivalent to the \ImageBuf {\cf src}. If it is not possible, or if OpenImageIO was compiled without OpenCV support, then return \NULL. The ownership of the {\cf IplImage} is fully transferred to the calling application. \begin{comment} \smallskip \noindent Examples: \begin{code} \end{code} \end{comment} \apiend \apiitem{bool {\ce capture_image} (ImageBuf \&dst, int cameranum, \\ \bigspc\bigspc TypeDesc convert = TypeDesc::UNKNOWN)} \index{ImageBufAlgo!capture_image} \indexapi{capture_image} Capture a still image from a designated camera. If able to do so, store the image in {\cf dst} and return {\cf true}. If there is no such device, or support for camera capture is not available (such as if OpenCV support was not enabled at compile time), return {\cf false} and do not alter {\cf dst}. \smallskip \noindent Examples: \begin{code} ImageBuf WebcamImage; ImageBufAlgo::capture_image (WebcamImage, 0, TypeDesc::UINT8); WebcamImage.save ("webcam.jpg"); \end{code} \apiend \section{Deep images} \label{sec:iba:deep} A number of {\cf ImageBufAlgo} functions are designed to work with ``deep'' images. These are detailed below. In general, {\cf ImageBufAlgo} functions not listed in this section should not be expected to work with deep images. \subsection{Functions specific to deep images} \apiitem{bool {\ce deepen} (ImageBuf \&dst, const ImageBuf \&src, float zvalue, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!deepen} \indexapi{deepen} \index{deep images} Copy pixels from regular (not ``deep'') image {\cf src} into deep {\cf dst}. If the {\cf src} image has a \qkw{Z} channel: if the source pixel's {\cf Z} channel value is not infinite, the corresponding pixel of {\cf dst} will get a single depth sample that copies the data from the soruce pixel; otherwise, {\cf dst} will get an empty pixel. In other words, infinitely far pixels will not turn into deep samples. If the {\cf src} image lacks a \qkw{Z} channel: if any of the source pixel's channel values are nonzero, the corresponding pixel of {\cf dst} will get a single depth sample that copies the data from the source pixel and uses the {\cf zvalue} parameter for the depth; otherwise, if all source channels in that pixel are zero, the destination pixel will get no depth samples. If {\cf src} is already a deep image, it will just copy pixel values from {\cf src} to {\cf dst}. If {\cf dst} is not already an initialized \ImageBuf, it will be sized to match {\cf src} (but made deep). \smallskip \noindent Examples: \begin{code} ImageBuf Flat ("RGBAZ.exr"); ImageBuf Deep; ImageBufAlgo::deepen (Deep, Flat); \end{code} \apiend \apiitem{bool {\ce flatten} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!flatten} \indexapi{flatten} \index{deep images} Copy pixels from \emph{deep} image {\cf src} into non-deep {\cf dst}, compositing the depth samples within each pixel to yield a single ``flat'' value per pixel. If {\cf src} is not deep, it just copies the pixels without alteration. \smallskip \noindent Examples: \begin{code} ImageBuf Deep ("deepalpha.exr"); ImageBuf Flat; ImageBufAlgo::flatten (Flat, Deep); \end{code} \apiend \apiitem{bool {\ce deep_merge} (ImageBuf \&dst, const ImageBuf \&A, const ImageBuf \&B, \\ \bigspc\spc bool occlusion_cull=true, ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!deep_merge} \indexapi{deep_merge} \index{deep images} \NEW % 1.7 Merge the samples of two \emph{deep} images {\cf A} and {\cf B} into deep result {\cf dst}. If {\cf occlusion_cull} is {\cf true}, samples beyond the first opaque sample will be discarded, otherwise they will be kept. \smallskip \noindent Examples: \begin{code} ImageBuf DeepA ("hardsurf.exr"); ImageBuf DeepB ("volume.exr"); ImageBuf Merged; ImageBufAlgo::deep_merge (Merged, DeepA, DeepB); \end{code} \apiend \subsection{General functions that also work for deep images} \apiitem{bool {\ce channels} (ImageBuf \&dst, const ImageBuf \&src, int nchannels, \\ \bigspc const int *channelorder, const float *channelvalues=NULL, \\ \bigspc const std::string *newchannelnames=NULL, \\ \bigspc bool shuffle_channel_names=false)} Reorder, rename, remove, or add channels to a deep image. See Section~\ref{sec:iba:channels} \apiend \apiitem{bool {\ce compare} (const ImageBuf \&A, const ImageBuf \&B, \\ \bigspc float failthresh, float warnthresh, CompareResults \&result,\\ \bigspc ROI roi=ROI::All(), int nthreads=0)} Numerically compare two images. \apiend \apiitem{bool {\ce computePixelStats} (PixelStats \&stats, const ImageBuf \&src, \\ \bigspc\bigspc ROI roi=ROI::All(), int nthreads=0)} Compute per-channel statistics about the image. See Section~\ref{sec:iba:computePixelStats} \apiend \apiitem{bool {\ce crop} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!crop} \indexapi{crop} Reset the data region of {\cf dst} to be the specified region of {\cf src}. \apiend \apiitem{ROI {\ce nonzero_region} (const ImageBuf \&src, ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!nonzero_region} \indexapi{nonzero_region} For ``deep'' images, this function returns the smallest ROI that contains all pixels that contain depth samples, and excludes the border pixels that contain no depth samples at all. \apiend \apiitem{bool {\ce add} (ImageBuf \&dst, const ImageBuf \&A, const float *B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce sub} (ImageBuf \&dst, const ImageBuf \&A, const float *B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce mul} (ImageBuf \&dst, const ImageBuf \&A, const float *B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0) \\ bool {\ce div} (ImageBuf \&dst, const ImageBuf \&A, const float *B, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!add} \indexapi{add} \index{ImageBufAlgo!sub} \indexapi{sub} \index{ImageBufAlgo!mul} \indexapi{mul} \index{ImageBufAlgo!div} \indexapi{div} Add, subtract, multiply, or divide a deep image {\cf A} by per-channel values {\cf B[]}, storing the result in deep image {\cf dst}. \apiend \apiitem{bool {\ce fixNonFinite} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc\spc NonFiniteFixMode mode = NONFINITE_BOX3, \\ \bigspc\spc int *pixelsFixed = NULL, \\ \bigspc\spc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!fixNonFinite} \indexapi{fixNonFinite} \NEW % 1.7 Repair nonfinite ({\cf NaN} or {\cf Inf}) values, setting them to 0.0. \apiend \apiitem{bool {\ce resample} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc bool interpolate = true, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!resample} \indexapi{resample} \NEW % 1.8 Set {\cf dst}, over the region of interest, to be a resized version of the corresponding portion of {\cf src} (mapping such that the ``full'' image window of each correspond to each other, regardless of resolution), for each pixel merely copying the closest deep pixel of the source image (no true interpolation is done for deep images). \apiend \begin{comment} \apiitem{bool {\ce blah} (ImageBuf \&dst, const ImageBuf \&src, \\ \bigspc ROI roi=ROI::All(), int nthreads=0)} \index{ImageBufAlgo!blah} \indexapi{blah} Blah. \smallskip \noindent Examples: \begin{code} \end{code} \apiend \end{comment} \index{ImageBufAlgo|)} \index{Image Processing|)} \chapwidthend openimageio-1.7.17~dfsg0.orig/src/doc/iinfo.tex0000644000175000017500000001426113151711064017513 0ustar mfvmfv\chapter{Getting Image information With {\kw iinfo}} \label{chap:iinfo} \indexapi{iinfo} %\section{Overview} The {\cf iinfo} program will print either basic information (name, resolution, format) or detailed information (including all metadata) found in images. Because {\cf iinfo} is built on top on \product, it will print information about images of any formats readable by \ImageInput plugins on hand. \section{Using {\cf iinfo}} The {\cf iinfo} utility is invoked as follows: \bigskip \hspace{0.25in} {\cf iinfo} [\emph{options}] \emph{filename} ... \medskip Where \emph{filename} (and any following strings) names the image file(s) whose information should be printed. The image files may be of any format recognized by \product (i.e., for which \ImageInput plugins are available). In its most basic usage, it simply prints the resolution, number of channels, pixel data type, and file format type of each of the files listed: \begin{code} $ iinfo img_6019m.jpg grid.tif lenna.png img_6019m.jpg : 1024 x 683, 3 channel, uint8 jpeg grid.tif : 512 x 512, 3 channel, uint8 tiff lenna.png : 120 x 120, 4 channel, uint8 png \end{code} % $ The {\cf -s} flag also prints the uncompressed sizes of each image file, plus a sum for all of the images: \begin{code} $ iinfo -s img_6019m.jpg grid.tif lenna.png img_6019m.jpg : 1024 x 683, 3 channel, uint8 jpeg (2.00 MB) grid.tif : 512 x 512, 3 channel, uint8 tiff (0.75 MB) lenna.png : 120 x 120, 4 channel, uint8 png (0.05 MB) Total size: 2.81 MB \end{code} % $ The {\cf -v} option turns on \emph{verbose mode}, which exhaustively prints all metadata about each image: \begin{code} $ iinfo -v img_6019m.jpg img_6019m.jpg : 1024 x 683, 3 channel, uint8 jpeg channel list: R, G, B Color space: sRGB ImageDescription: "Family photo" Make: "Canon" Model: "Canon EOS DIGITAL REBEL XT" Orientation: 1 (normal) XResolution: 72 YResolution: 72 ResolutionUnit: 2 (inches) DateTime: "2008:05:04 19:51:19" Exif:YCbCrPositioning: 2 ExposureTime: 0.004 FNumber: 11 Exif:ExposureProgram: 2 (normal program) Exif:ISOSpeedRatings: 400 Exif:DateTimeOriginal: "2008:05:04 19:51:19" Exif:DateTimeDigitized: "2008:05:04 19:51:19" Exif:ShutterSpeedValue: 7.96579 (1/250 s) Exif:ApertureValue: 6.91887 (f/11) Exif:ExposureBiasValue: 0 Exif:MeteringMode: 5 (pattern) Exif:Flash: 16 (no flash, flash supression) Exif:FocalLength: 27 (27 mm) Exif:ColorSpace: 1 Exif:PixelXDimension: 2496 Exif:PixelYDimension: 1664 Exif:FocalPlaneXResolution: 2855.84 Exif:FocalPlaneYResolution: 2859.11 Exif:FocalPlaneResolutionUnit: 2 (inches) Exif:CustomRendered: 0 (no) Exif:ExposureMode: 0 (auto) Exif:WhiteBalance: 0 (auto) Exif:SceneCaptureType: 0 (standard) Keywords: "Carly; Jack" \end{code} % $ If the input file has multiple subimages, extra information summarizing the subimages will be printed: \begin{code} $ iinfo img_6019m.tx img_6019m.tx : 1024 x 1024, 3 channel, uint8 tiff (11 subimages) $ iinfo -v img_6019m.tx img_6019m.tx : 1024 x 1024, 3 channel, uint8 tiff 11 subimages: 1024x1024 512x512 256x256 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 channel list: R, G, B tile size: 64 x 64 ... \end{code} Furthermore, the {\cf -a} option will print information about all individual subimages: \begin{code} $ iinfo -a ../sample-images/img_6019m.tx img_6019m.tx : 1024 x 1024, 3 channel, uint8 tiff (11 subimages) subimage 0: 1024 x 1024, 3 channel, uint8 tiff subimage 1: 512 x 512, 3 channel, uint8 tiff subimage 2: 256 x 256, 3 channel, uint8 tiff subimage 3: 128 x 128, 3 channel, uint8 tiff subimage 4: 64 x 64, 3 channel, uint8 tiff subimage 5: 32 x 32, 3 channel, uint8 tiff subimage 6: 16 x 16, 3 channel, uint8 tiff subimage 7: 8 x 8, 3 channel, uint8 tiff subimage 8: 4 x 4, 3 channel, uint8 tiff subimage 9: 2 x 2, 3 channel, uint8 tiff subimage 10: 1 x 1, 3 channel, uint8 tiff $ iinfo -v -a img_6019m.tx img_6019m.tx : 1024 x 1024, 3 channel, uint8 tiff 11 subimages: 1024x1024 512x512 256x256 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 subimage 0: 1024 x 1024, 3 channel, uint8 tiff channel list: R, G, B tile size: 64 x 64 ... subimage 1: 512 x 512, 3 channel, uint8 tiff channel list: R, G, B ... ... \end{code} \section{{\cf iinfo} command-line options} \apiitem{--help} Prints usage information to the terminal. \apiend \apiitem{-v} Verbose output --- prints all metadata of the image files. \apiend \apiitem{-a} Print information about all subimages in the file(s). \apiend \apiitem{-f} Print the filename as a prefix to every line. For example, \begin{code} $ iinfo -v -f img_6019m.jpg img_6019m.jpg : 1024 x 683, 3 channel, uint8 jpeg img_6019m.jpg : channel list: R, G, B img_6019m.jpg : Color space: sRGB img_6019m.jpg : ImageDescription: "Family photo" img_6019m.jpg : Make: "Canon" ... \end{code} %$ \apiend \apiitem{-m {\rm \emph{pattern}}} Match the \emph{pattern} (specified as an extended regular expression) against data metadata field names and print only data fields whose names match. The default is to print all data fields found in the file (if {\cf -v} is given). For example, \begin{code} $ iinfo -v -f -m ImageDescription test*.jpg test3.jpg : ImageDescription: "Birthday party" test4.jpg : ImageDescription: "Hawaii vacation" test5.jpg : ImageDescription: "Bob's graduation" test6.jpg : ImageDescription: \end{code} %$ \apiend Note: the {\cf -m} option is probably not very useful without also using the {\cf -v} and {\cf -f} options. \apiitem{--hash} Displays a SHA-1 hash of the pixel data of the image (and of each subimage if combined with the {\cf -a} flag). \apiend \apiitem{-s} Show the image sizes, including a sum of all the listed images. \apiend openimageio-1.7.17~dfsg0.orig/src/doc/macros.tex0000644000175000017500000001214413151711064017671 0ustar mfvmfv%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Larry's favorite LaTeX macros for making technical books. These have % been refined for years, starting with SIGGRAPH course notes in the % '90's, further refined for _Advanced RenderMan_. % % Please use or modify this at will. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Define typesetting commands for filenames and code % % Just like Advanced RenderMan -- all code in courier, keywords in text % courier but not bold. \def\codefont{\ttfamily} % font to use for code \def\ce{\codefont\bfseries} % emphasize something in code % % Define typesetting commands for filenames and code % \def\cf{\codefont} % abbreviation for \codefont \def\fn{\codefont} % in-line filenames & unix commands \def\kw{\codefont} % in-line keyword \newcommand{\var}[1]{{\kw \emph{#1}}} % variable \newcommand{\qkw}[1]{{\kw "#1"}} % quoted keyword \newcommand{\qkws}[1]{{\small \kw "#1"}} % quoted keyword, small \newcommand{\qkwf}[1]{{\footnotesize \kw "#1"}} % quoted keyword, tiny % Define some environments for easy typesetting of small amounts of % code. These are mostly just wrappers around verbatim, but the % different varieties also change font sizes. \newenvironment{code}{\small \verbatimtab}{\endverbatimtab} \newenvironment{smallcode}{\small \renewcommand{\baselinestretch}{0.8} \verbatimtab}{\endverbatimtab \renewcommand{\baselinestretch}{1}} \newenvironment{tinycode}{\footnotesize \renewcommand{\baselinestretch}{0.75} \verbatimtab}{\endverbatimtab \renewcommand{\baselinestretch}{1}} % \begin{htmlonly} % \renewenvironment{code}{\begin{verbatim}}{\end{verbatim}} % \newenvironment{smallcode}{\begin{verbatim}}{\end{verbatim}} % \newenvironment{tinycode}{\begin{verbatim}}{\end{verbatim}} % \end{htmlonly} \newcommand{\includedcode}[1]{{\small \verbatimtabinput{#1}}} \newcommand{\smallincludedcode}[1]{{\small \renewcommand{\baselinestretch}{0.8} \verbatimtabinput{#1} \renewcommand{\baselinestretch}{1}}} \newcommand{\tinyincludedcode}[1]{{\footnotesize \renewcommand{\baselinestretch}{0.75} \verbatimtabinput{#1} \renewcommand{\baselinestretch}{1}}} % Also create a hyphenation list, essentially just to guarantee that % type names aren't hyphenated %\hyphenation{Attribute} % Handy for parameter lists \def\pl{{\rm\emph{...params...}\xspace}} \def\dotdotdot{{\rm\emph{...}\xspace}} \hyphenation{parameterlist} %begin{latexonly} \newenvironment{apilist}{\begin{list}{}{\medskip \item[]}}{\end{list}} \newcommand{\apiitem}[1]{\vspace{12pt} \noindent {\bf\tt #1} \vspace{-10pt}\begin{apilist}\nopagebreak[4]} \newcommand{\apiend}{\end{apilist}\medskip\pagebreak[2]} \def\bigspc{\makebox[72pt]{}} \def\spc{\makebox[24pt]{}} \def\halfspc{\makebox[12pt]{}} \def\neghalfspc{\hspace{-12pt}} \def\negspc{\hspace{-24pt}} \def\chapwidthbegin{} \def\chapwidthend{} %end{latexonly} %\begin{htmlonly} %\newcommand{\apiitem}[1]{\medskip \noindent {\bf #1} \begin{quote}} %\newcommand{\apiend}{\end{quote}} %\def\halfspc{\begin{rawhtml}     \end{rawhtml}} %\def\spc{\halfspc\halfspc} %\pagecolor[named]{White} %\def\chapwidthbegin{\begin{rawhtml}

\end{rawhtml}} %\def\chapwidthend{\begin{rawhtml}
\end{rawhtml}} %\end{htmlonly} \newcommand{\apibinding}[3]{\apiitem{#1\\[1ex]#2\\[1ex]#3}} \newcommand{\CPPBINDING}[1]{\par {\small C++ BINDING:}\par {\spc \codefont #1}} \newcommand{\PARAMETERS}{\par {\small PARAMETERS:} \par} \newcommand{\EXAMPLE}{\par {\small EXAMPLE:} \par} \newcommand{\EXAMPLES}{\par {\small EXAMPLES:} \par} \newcommand{\SEEALSO}{\par \hspace{-20pt} See Also: \par} % The \begin{algorithm} \end{algorithm} macros (in algorithm.sty) are % great for code that can fit all on one page. But when it can't, use % these macros. The first parameter is the caption, the second is the % label name. \newcommand{\longalgorithmbegin}[2]{\noindent\hrulefill \\ \refstepcounter{algorithm} \noindent {\bf Listing \arabic{chapter}.\arabic{algorithm}}: #1 \label{#2} \\ \addcontentsline{loa}{algorithm}{\numberline {\arabic{algorithm}} #1} \noindent\hrulefill } \newcommand{\longalgorithmend}{\noindent\hrulefill \\} \def\NEW{\marginpar[\medskip\hfill~\fbox{\sffamily \Huge NEW!}~]{\medskip~\fbox{\sffamily \Huge NEW!}~}} \newcommand{\NEWdown}[1]{\marginpar[\vspace{#1}\hfill\fbox{\sffamily \Huge NEW!}]{\vspace{#1}\fbox{\sffamily \Huge NEW!}}} \def\DEPRECATED{\marginpar[\medskip\hfill~\fbox{\sffamily \Large Deprecated}]{\medskip~\fbox{\sffamily \Large Deprecated}}} \newcommand{\DEPRECATEDdown}[1]{\marginpar{\vspace{#1}\fbox{\sffamily \Large Deprecated}}} \def\CHANGED{\marginpar[\medskip\hfill~\fbox{\sffamily \huge CHANGED!}~]{\medskip~\fbox{\sffamily \huge CHANGED!}~}} \def\ENHANCED{\marginpar[\medskip\hfill~\fbox{\sffamily \huge ENHANCED}~]{\medskip~\fbox{\sffamily \huge ENHANCED}~}} \def\QUESTION{\marginpar[\medskip\hfill~\fbox{\sffamily \Huge ?}~~~~]{\medskip~\fbox{\sffamily \Huge ?}~~~~}} \newcommand{\indexapi}[1]{\index{#1@\tt#1\rm}} \newenvironment{annotate}{\medskip\sffamily\em\noindent}{\medskip} %\newenvironment{annotate}{\begin{comment}}{\end{comment}} openimageio-1.7.17~dfsg0.orig/src/build-scripts/0000755000175000017500000000000013151711064017700 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/build-scripts/dwaLookups.h.zip0000644000175000017500000265612213151711064023020 0ustar mfvmfvPK›Hk[[ _ dwaLookups.hUT +RW1RWux Lֽ ZvѼSZ l' 9e 侢o jL ?˿}~_?ׯ㯿۟߿寿}׿|?~鷿߿۰i˶oo:PJBI( %$nI( %$PJ▤TJRI*I%$oI*I%$TJ򖔒RRJJI))%nI))%RRJꖴVJZI+i%oI+i%VJQ2JF(%dn(%dQ2J斬UJV*Y%do*Y%dUJ<%OS<%OSnS<%OS<%|SM7%ߔ|SM7%n7%ߔ|SM7%ߔ|S|W]w%ߕ|W]w%ow%ߕ|W]w%ߕ|WPC%?PC%?n%?PC%?PTSO%?TSO%?oO%?TSO%?T|(PCɇ%J>nɇ%J>|(P|*TSɧO%J>oɧO%J>|*T|)RKɗ/%_Jnɗ/%_J|)RRK/%RK/%n/%RK/%RIIIIIIII\IIIIIIII\IIIIIIII\IIIIIIII\IIIIIIII\IIIIIIII\IIIIIIII\IIIIIIII\IIIIIIII\IIIIIIII\IIIIIIII\IIIIIIII\IIIIIIII\IIIIIIII\IIIIIIII\IIIIIIII\$$$$$$$IIIIIIII^$$$$$$$IIIIIIII^$$$$$$$IIIIIIII^$$$$$$$IIIIIIII^$$$$$$$IIIIIIII^$$$$$$$IIIIIIII^$$$$$$$IIIIIIII^$$$$$$$IIIIIIII^$$$$$$$IIIIIIII^$$$$$$$IIIIIIII^$$$$$$$IIIIIIII^$$$$$$$IIIIIIII^$$$$$$$IIIIIIII^$$$$$$$IIIIIIII^$$$$$$$IIIIIIII^$$$$$$$IIIIIIII^IIIIIIII]IIIIIIII]IIIIIIII]IIIIIIII]IIIIIIII]IIIIIIII]IIIIIIII]IIIIIIII]IIIIIIII]IIIIIIII]IIIIIIII]IIIIIIII]IIIIIIII]IIIIIIII]IIIIIIII]IIIIIIII]4444444IIIIIIII_4444444IIIIIIII_4444444IIIIIIII_4444444IIIIIIII_4444444IIIIIIII_4444444IIIIIIII_4444444IIIIIIII_4444444IIIIIIII_4444444IIIIIIII_4444444IIIIIIII_4444444IIIIIIII_4444444IIIIIIII_4444444IIIIIIII_4444444IIIIIIII_4444444IIIIIIII_4444444IIIIIIII_ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \,,,,,,,^,,,,,,,^,,,,,,,^,,,,,,,^,,,,,,,^,,,,,,,^,,,,,,,^,,,,,,,^,,,,,,,^,,,,,,,^,,,,,,,^,,,,,,,^,,,,,,,^,,,,,,,^,,,,,,,^,,,,,,,^<>y|'O'O<>y|]<>y|'O'O<>y|]<>y|'O'O<>y|]<>y|'O'O<>y|]<>y|'O'O<>y|]<>y|'O'O<>y|]<>y|'O'O<>y|]<>y|'O'O<>y|]<>y|'O'O<>y|]<>y|'O'O<>y|]<>y|'O'O<>y|]<>y|'O'O<>y|]<>y|'O'O<>y|]<>y|'O'O:  CAk5mV'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{|~}FlV'qikߒ$( J$( J[%AIP%AIxK$*J$*oITD%QITD%-IJ$)IJ$%IIR$%IIR$+J$+Jd%YIVd%YIVߒ()J()J[R%EIQR%EIyK*J*oIURT%UIURT%-iJ)iJ%MISҔ4%MISҔ+J+Jt%]IWҕt%]IWߒd(Jd(J[2 %CP2 %CxKd*Jd*oT2L%ST2L%-YJd)YJd%KR,%KRd+Jd+Jl%[Vl%[Vߒ(9J(9J[r%GQr%GyK*J*oUr\%WUr\%-|J>%OɧS%OɧS)|J^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>ğm&mնIwve=ڷ$( J$( J%AIP%AIPޒ$*J$*J[D%QITD%QI|K$)IJ$)IoIR$%IIR$%-J$+J$%YIVd%YIV䷤()J()J%EIQR%EIQRޒ*J*J[RT%UIURT%UI}K)iJ)ioISҔ4%MISҔ4%-J+J%]IWҕt%]IWҕd(Jd(J %CP2 %CP2ޒd*Jd*J[2L%ST2L%S|Kd)YJd)YoR,%KR,%-Jd+Jd%[Vl%[V(9J(9J%GQr%GQrޒ*J*J[r\%WUr\%W}K>%OɧS)|oɧS)|J>%'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O''lf[l}vm- J$( J$%AIP%AIP$*J$*JD%QITD%QITߒ$)IJ$)IJ[$%IIR$%IIzK$+J$+oIVd%YIVd%-)J()J%EIQR%EIQR*J*JT%UIURT%UIURߒ)iJ)iJ[Ҕ4%MISҔ4%MI{K+J+oIWҕt%]IWҕt%-Jd(Jd%CP2 %CP2d*Jd*JL%ST2L%ST2ߒd)YJd)YJ[,%KR,%KzKd+Jd+oVl%[Vl%-9J(9J%GQr%GQr*J*J\%WUr\%WUrߒOɧS)|J>%[)|J>%OɧI'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>Im[m{ta]vc}K$( J$( oIP%AIP%-J$*J$%QITD%QITķ$)IJ$)IJ$%IIR$%IIRޒ$+J$+J[d%YIVd%YI~K()J()oIQR%EIQR%-J*J%UIURT%UIURԷ)iJ)iJ4%MISҔ4%MISޒ+J+J[ҕt%]IWҕt%]IKd(Jd(oP2 %CP2 %-Jd*Jd%ST2L%ST2̷d)YJd)YJ,%KR,%KRޒd+Jd+J[l%[Vl%[~K(9J(9oQr%GQr%-J*J%WUr\%WUrܷS)|J>%O|J>%OɧS)y}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|2Y@S2~2h Ƣ'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|Id'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|d'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||o6lO.׾%AIP%AIP$( J$( JD%QITD%QITߒ$*J$*J[$%IIR$%IIzK$)IJ$)IoIVd%YIVd%-J$+J$%EIQR%EIQR()J()JT%UIURT%UIURߒ*J*J[Ҕ4%MISҔ4%MI{K)iJ)ioIWҕt%]IWҕt%-J+J%CP2 %CP2d(Jd(JL%ST2L%ST2ߒd*Jd*J[,%KR,%KzKd)YJd)YoVl%[Vl%-Jd+Jd%GQr%GQr(9J(9J\%WUr\%WUrߒ*J*J[)|J>%Oɧ{K>%OɧS)> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||=>?>`M6bm8n{oIP%AIP%- J$( J$%QITD%QITķ$*J$*J$%IIR$%IIRޒ$)IJ$)IJ[d%YIVd%YI~K$+J$+oIQR%EIQR%-)J()J%UIURT%UIURԷ*J*J4%MISҔ4%MISޒ)iJ)iJ[ҕt%]IWҕt%]IK+J+oP2 %CP2 %-Jd(Jd%ST2L%ST2̷d*Jd*J,%KR,%KRޒd)YJd)YJ[l%[Vl%[~Kd+Jd+oQr%GQr%-9J(9J%WUr\%WUrܷ*J*J|J>%OɧS)ޒOɧS)|J>%O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|OϿO6hͶj;;{[%AIP%AIxK$( J$( oITD%QITD%-J$*J$%IIR$%IIR$)IJ$)IJd%YIVd%YIVߒ$+J$+J[R%EIQR%EIyK()J()oIURT%UIURT%-J*J%MISҔ4%MISҔ)iJ)iJt%]IWҕt%]IWߒ+J+J[2 %CP2 %CxKd(Jd(oT2L%ST2L%-Jd*Jd%KR,%KRd)YJd)YJl%[Vl%[Vߒd+Jd+J[r%GQr%GyK(9J(9oUr\%WUr\%-J*J%OɧS)|JS)|J>%O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||' 6d->N^%AIP%AIPޒ$( J$( J[D%QITD%QI|K$*J$*oIR$%IIR$%-IJ$)IJ$%YIVd%YIV$+J$+J%EIQR%EIQRޒ()J()J[RT%UIURT%UI}K*J*oISҔ4%MISҔ4%-iJ)iJ%]IWҕt%]IWҕ+J+J %CP2 %CP2ޒd(Jd(J[2L%ST2L%S|Kd*Jd*oR,%KR,%-YJd)YJd%[Vl%[Vd+Jd+J%GQr%GQrޒ(9J(9J[r\%WUr\%W}K*J*oɧS)|J>%-|J>%OɧS$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$II'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}/un@ A0%'w *k,ڬ'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>m[m{ta]vc}K$( J$( oIP%AIP%-J$*J$%QITD%QITķ$)IJ$)IJ$%IIR$%IIRޒ$+J$+J[d%YIVd%YI~K()J()oIQR%EIQR%-J*J%UIURT%UIURԷ)iJ)iJ4%MISҔ4%MISޒ+J+J[ҕt%]IWҕt%]IKd(Jd(oP2 %CP2 %-Jd*Jd%ST2L%ST2̷d)YJd)YJ,%KR,%KRޒd+Jd+J[l%[Vl%[~K(9J(9oQr%GQr%-J*J%WUr\%WUrܷS)|J>%O|J>%OɧS)y}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{|2}FlV'qikߒ$( J$( J[%AIP%AIxK$*J$*oITD%QITD%-IJ$)IJ$%IIR$%IIR$+J$+Jd%YIVd%YIVߒ()J()J[R%EIQR%EIyK*J*oIURT%UIURT%-iJ)iJ%MISҔ4%MISҔ+J+Jt%]IWҕt%]IWߒd(Jd(J[2 %CP2 %CxKd*Jd*oT2L%ST2L%-YJd)YJd%KR,%KRd+Jd+Jl%[Vl%[Vߒ(9J(9J[r%GQr%GyK*J*oUr\%WUr\%-|J>%OɧS%OɧS)|J^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>m&mնIwve=ڷ$( J$( J%AIP%AIPޒ$*J$*J[D%QITD%QI|K$)IJ$)IoIR$%IIR$%-J$+J$%YIVd%YIV䷤()J()J%EIQR%EIQRޒ*J*J[RT%UIURT%UI}K)iJ)ioISҔ4%MISҔ4%-J+J%]IWҕt%]IWҕd(Jd(J %CP2 %CP2ޒd*Jd*J[2L%ST2L%S|Kd)YJd)YoR,%KR,%-Jd+Jd%[Vl%[V(9J(9J%GQr%GQrޒ*J*J[r\%WUr\%W}K>%OɧS)|oɧS)|J>%'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O:t>|II'O:t>|_t>|I'O'O:t>|I}I'O:t>O:t>|I'I'O:t>|>|I'O:'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O >|2dd'O >|2^ >|2d'O'O >|2dx}2d'O >O >|2d''O >|2d>|2d'O'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O&L>|2dd'O&L>|2_L>|2d'O'O&L>|2d|}2d'O&L>O&L>|2d''O&L>|2d>|2d'O&'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O,>Y|dd'O,>Y|^,>Y|d'O'O,>Y|dz}d'O,>YO,>Y|d''O,>Y|d>Y|d'O'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O6l>|dd'O6l>|_l>|d'O'O6l>|d~}d'O6l>ٯO6l>|d''O6l>|d>|d'O6'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O>9|r'O>9|r^>9|r'O'O>9|ry}r'O>9O>9|r''O>9|r>9|r'O'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'O.\>|r'O.\>|r_\>|r'O'O.\>|r}}r'O.\>O.\>|r''O.\>|r>|r'O.'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O'||'||㓏O>>㓏O>>^||'||'듏O>>㓏O>>{}'||'|O>>㓏O>>'||'||>㓏O>>㓏O''lf[l}vm- J$( J$%AIP%AIP$*J$*JD%QITD%QITߒ$)IJ$)IJ[$%IIR$%IIzK$+J$+oIVd%YIVd%-)J()J%EIQR%EIQR*J*JT%UIURT%UIURߒ)iJ)iJ[Ҕ4%MISҔ4%MI{K+J+oIWҕt%]IWҕt%-Jd(Jd%CP2 %CP2d*Jd*JL%ST2L%ST2ߒd)YJd)YJ[,%KR,%KzKd+Jd+oVl%[Vl%-9J(9J%GQr%GQr*J*J\%WUr\%WUrߒOɧS)|J>%[)|J>%OɧI'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O> |$> |$I'O'O> |$I$I'O> |^> |$I'O'O> |$Ix}$I'O> O> |$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O"D>|$>|$I'O"'O"D>|$I$I'O"D>|_D>|$I'O'O"D>|$I|}$I'O"D>O"D>|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O$>I|$>I|$I'O'O$>I|$I$I'O$>I|^$>I|$I'O'O$>I|$Iz}$I'O$>IO$>I|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O2d>|$>|$I'O2'O2d>|$I$I'O2d>|_d>|$I'O'O2d>|$I~}$I'O2d>ɯO2d>|$I'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O >)|R>)|RI'O 'O >)|RII'O >)|R^>)|RI'O'O >)|RIy}RI'O >)O >)|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O*T>|R>|RI'O*'O*T>|RII'O*T>|R_T>|RI'O'O*T>|RI}}RI'O*T>O*T>|RI'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|II'O4>i|^4>i|I'O'O4>i|I{}I'O4>iO4>i|I'I'O4>i|>i|I'O'O4>i|IIedIPOOha#hRFI hQ |=w{`eZU>Ȫ擗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O\/5Y->T6CR\\\\\\\\1%%%%%%%$I$I$I$I$I1I$I$I$I$IdI$Y,I$K%1I$K%ɒdI$YI$E"IHR$)1IHR$)I$Eܒܒܒܒܒܒܒ1-------<<<<<<<<1#######1+++++++TI$U*IJR%1IJR%TI$U4I$M&II$i1II$i4I$MtI$].IK%1IK%tI$] I$C!ɐdH2$1ɐdH2$ I$CLI$S)ɔdJ2%1ɔdJ2%LI$S,I$K%ɒdI$Y1ɒdI$Y,I$KlI$[-ɖdK%1ɖdK%ْlI$[I$G#ɑHr$91ɑHr$9I$G蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O\/5Y->T6CR\\\\\\\\1%%%%%%%$I$I$I$I$I1I$I$I$I$IdI$Y,I$K%1I$K%ɒdI$YI$E"IHR$)1IHR$)I$Eܒܒܒܒܒܒܒ1-------<<<<<<<<1#######1+++++++TI$U*IJR%1IJR%TI$U4I$M&II$i1II$i4I$MtI$].IK%1IK%tI$] I$C!ɐdH2$1ɐdH2$ I$CLI$S)ɔdJ2%1ɔdJ2%LI$S,I$K%ɒdI$Y1ɒdI$Y,I$KlI$[-ɖdK%1ɖdK%ْlI$[I$G#ɑHr$91ɑHr$9I$G蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO蓋O.>ⓋO.>>ⓋO.>ⓋO'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O$>I|$I>I|$I'O'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O2d>|$I>|$I'O2'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 'O >)|RI>)|RI'O 蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓛On>擛On>>擛On>擛O蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓇O>yᓇO>y>yᓇO>yᓇO蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O蓗O^>y哗O^>y>y哗O^>y哗O'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O*T>|RI>|RI'O*'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O4>i|I>i|I'O'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O:t>|I>|I'O:'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O >|2dɈ>|2d'O'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O&L>|2dɌ>|2d'O&'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O,>Y|dɊ>Y|d'O'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O6l>|dɎ>|d'O6'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O'O>9|rɉ>9|r'O̓I&5EG}cvuS]VcKKKKKKK+&$$$$$$$b$I$I$I$I$)&I$I$I$I$Ib,I$K%ɒdI$9&ɒdI$Y,I$Kc"IHR$)I$%&)I$E"IHRb[[[[[[[;&%%%%%%%cGGGGGGG'&y$y$y$y$y$y$y$ybWWWWWWW7&y%y%y%y%y%y%y%yc*IJR%TI$5&TI$U*IJRc&II$i4I$-&i4I$M&IIb.IK%tI$=&tI$].IKc!ɐdH2$ I$#& I$C!ɐdH2b)ɔdJ2%LI$3&LI$S)ɔdJ2c%ɒdI$Y,I$+&Y,I$K%ɒdIb-ɖdK%ْlI$;&ْlI$[-ɖdKc#ɑHr$9I$'&9I$G#ɑH}r'\|r'\'\|r'\|r}r'\|r'\'\|r'\|r}r'\|r'\'\|r'\|r}r'\|r'\'\|r'\|r}r'\|r'\'\|r'\|r}r'\|r'\'\|r'\|r}r'\|r'\'\|r'\|r}r'\|r'\'\|r'\|r}r'\|r'\'\|r'\|r}r'\|r'\'\|r'\|r}r'\|r'\'\|r'\|r}r'\|r'\'\|r'\|r}r'\|r'\'\|r'\|r}r'\|r'\'\|r'\|r}r'\|r'\'\|r'\|r}r'\|r'\'\|r'\|r}$I'O$>I'O$>I|$I}$I'O$>I'O$>I|$I}$I'O$>I'O$>I|$I}$I'O$>I'O$>I|$I}$I'O$>I'O$>I|$I}$I'O$>I'O$>I|$I}$I'O$>I'O$>I|$I}$I'O$>I'O$>I|$I}$I'O$>I'O$>I|$I}$I'O$>I'O$>I|$I}$I'O$>I'O$>I|$I}$I'O$>I'O$>I|$I}$I'O$>I'O$>I|$I}$I'O$>I'O$>I|$I}$I'O$>I'O$>I|$I}$I'O$>I'O$>I|$I}$I'O2d>'O2d>|$I}$I'O2d>'O2d>|$I}$I'O2d>'O2d>|$I}$I'O2d>'O2d>|$I}$I'O2d>'O2d>|$I}$I'O2d>'O2d>|$I}$I'O2d>'O2d>|$I}$I'O2d>'O2d>|$I}$I'O2d>'O2d>|$I}$I'O2d>'O2d>|$I}$I'O2d>'O2d>|$I}$I'O2d>'O2d>|$I}$I'O2d>'O2d>|$I}$I'O2d>'O2d>|$I}$I'O2d>'O2d>|$I}$I'O2d>'O2d>|$I}RI'O >)'O >)|RI}RI'O >)'O >)|RI}RI'O >)'O >)|RI}RI'O >)'O >)|RI}RI'O >)'O >)|RI}RI'O >)'O >)|RI}RI'O >)'O >)|RI}RI'O >)'O >)|RI}RI'O >)'O >)|RI}RI'O >)'O >)|RI}RI'O >)'O >)|RI}RI'O >)'O >)|RI}RI'O >)'O >)|RI}RI'O >)'O >)|RI}RI'O >)'O >)|RI}RI'O >)'O >)|RI}r'7|r'7'7|r'7|r}r'7|r'7'7|r'7|r}r'7|r'7'7|r'7|r}r'7|r'7'7|r'7|r}r'7|r'7'7|r'7|r}r'7|r'7'7|r'7|r}r'7|r'7'7|r'7|r}r'7|r'7'7|r'7|r}r'7|r'7'7|r'7|r}r'7|r'7'7|r'7|r}r'7|r'7'7|r'7|r}r'7|r'7'7|r'7|r}r'7|r'7'7|r'7|r}r'7|r'7'7|r'7|r}r'7|r'7'7|r'7|r}r'7|r'7'7|r'7|r}'<|'<'<|'<|}'<|'<'<|'<|}'<|'<'<|'<|}'<|'<'<|'<|}'<|'<'<|'<|}'<|'<'<|'<|}'<|'<'<|'<|}'<|'<'<|'<|}'<|'<'<|'<|}'<|'<'<|'<|}'<|'<'<|'<|}'<|'<'<|'<|}'<|'<'<|'<|}'<|'<'<|'<|}'<|'<'<|'<|}'<|'<'<|'<|}'/|'/'/|'/|}'/|'/'/|'/|}'/|'/'/|'/|}'/|'/'/|'/|}'/|'/'/|'/|}'/|'/'/|'/|}'/|'/'/|'/|}'/|'/'/|'/|}'/|'/'/|'/|}'/|'/'/|'/|}'/|'/'/|'/|}'/|'/'/|'/|}'/|'/'/|'/|}'/|'/'/|'/|}'/|'/'/|'/|}'/|'/'/|'/|}RI'O*T>'O*T>|RI}RI'O*T>'O*T>|RI}RI'O*T>'O*T>|RI}RI'O*T>'O*T>|RI}RI'O*T>'O*T>|RI}RI'O*T>'O*T>|RI}RI'O*T>'O*T>|RI}RI'O*T>'O*T>|RI}RI'O*T>'O*T>|RI}RI'O*T>'O*T>|RI}RI'O*T>'O*T>|RI}RI'O*T>'O*T>|RI}RI'O*T>'O*T>|RI}RI'O*T>'O*T>|RI}RI'O*T>'O*T>|RI}RI'O*T>'O*T>|RI}I'O4>i'O4>i|I}I'O4>i'O4>i|I}I'O4>i'O4>i|I}I'O4>i'O4>i|I}I'O4>i'O4>i|I}I'O4>i'O4>i|I}I'O4>i'O4>i|I}I'O4>i'O4>i|I}I'O4>i'O4>i|I}I'O4>i'O4>i|I}I'O4>i'O4>i|I}I'O4>i'O4>i|I}I'O4>i'O4>i|I}I'O4>i'O4>i|I}I'O4>i'O4>i|I}I'O4>i'O4>i|I}I'O:t>'O:t>|I}I'O:t>'O:t>|I}I'O:t>'O:t>|I}I'O:t>'O:t>|I}I'O:t>'O:t>|I}I'O:t>'O:t>|I}I'O:t>'O:t>|I}I'O:t>'O:t>|I}I'O:t>'O:t>|I}I'O:t>'O:t>|I}I'O:t>'O:t>|I}I'O:t>'O:t>|I}I'O:t>'O:t>|I}I'O:t>'O:t>|I}I'O:t>'O:t>|I}I'O:t>'O:t>|I}2d'O >'O >|2d}2d'O >'O >|2d}2d'O >'O >|2d}2d'O >'O >|2d}2d'O >'O >|2d}2d'O >'O >|2d}2d'O >'O >|2d}2d'O >'O >|2d}2d'O >'O >|2d}2d'O >'O >|2d}2d'O >'O >|2d}2d'O >'O >|2d}2d'O >'O >|2d}2d'O >'O >|2d}2d'O >'O >|2d}2d'O >'O >|2d}2d'O&L>'O&L>|2d}2d'O&L>'O&L>|2d}2d'O&L>'O&L>|2d}2d'O&L>'O&L>|2d}2d'O&L>'O&L>|2d}2d'O&L>'O&L>|2d}2d'O&L>'O&L>|2d}2d'O&L>'O&L>|2d}2d'O&L>'O&L>|2d}2d'O&L>'O&L>|2d}2d'O&L>'O&L>|2d}2d'O&L>'O&L>|2d}2d'O&L>'O&L>|2d}2d'O&L>'O&L>|2d}2d'O&L>'O&L>|2d}2d'O&L>'O&L>|2d}d'O,>Y'O,>Y|d}d'O,>Y'O,>Y|d}d'O,>Y'O,>Y|d}d'O,>Y'O,>Y|d}d'O,>Y'O,>Y|d}d'O,>Y'O,>Y|d}d'O,>Y'O,>Y|d}d'O,>Y'O,>Y|d}d'O,>Y'O,>Y|d}d'O,>Y'O,>Y|d}d'O,>Y'O,>Y|d}d'O,>Y'O,>Y|d}d'O,>Y'O,>Y|d}d'O,>Y'O,>Y|d}d'O,>Y'O,>Y|d}d'O,>Y'O,>Y|d}d'O6l>'O6l>|d}d'O6l>'O6l>|d}d'O6l>'O6l>|d}d'O6l>'O6l>|d}d'O6l>'O6l>|d}d'O6l>'O6l>|d}d'O6l>'O6l>|d}d'O6l>'O6l>|d}d'O6l>'O6l>|d}d'O6l>'O6l>|d}d'O6l>'O6l>|d}d'O6l>'O6l>|d}d'O6l>'O6l>|d}d'O6l>'O6l>|d}d'O6l>'O6l>|d}d'O6l>'O6l>|d}r'O>9'O>9|r}r'O>9'O>9|r}r'O>9'O>9|r}r'O>9'O>9|r}r'O>9'O>9|r}r'O>9'O>9|r}r'O>9'O>9|r}r'O>9'O>9|r}r'O>9'O>9|r}r'O>9'O>9|r}r'O>9'O>9|r}r'O>9'O>9|r}r'O>9'O>9|r}r'O>9'O>9|r}r'O>9'O>9|r}r'O>9'O>9|r/>o??ǿO[??O&M|7nw&M|7nw&M|7nw&M|7nw&M|7nw&M|7nw&M|7nw&M|7nw&M|7nw&M|7nw&M|7nw&M|7nw&M|7nw&M|7nw&M|7nw&m\w&M|7nw&M|7nw&M|7n&鷿\|7nw&M|7n$w&M|7nMo~7nw5iw&y~wћ?O?yWw&5?&&MmO_xw&O4?>wu4W^dwdwɟ<MNlr~*\_Mm~&^ߢI~+/I~+/c4'u4W^I^ߨIiO|MA~Qc4'7^?M/_O㓿Z_&/d_MίOXϞ_$>]o$z4'7_OEvMʧ~_$1ݿo_7y~_MO&^כO_x_o2~^?M~y4&^כO_x?z4'M&l7鷿\z&'zu4W^_y4z矯&Ɓrɟ?Yxڿ&?8{u4|o}&Ś>$izX?M5c֯_S?MX;u}]?M~pb=iroXj^5}]?MZˏO?ϧ~/y4fc4'u}'5۟׿7T_u4Z~&?x>?O?>O4xb+[y?qe?'5׿7i<_i}e_O/u4_O/k~x_|Y˺&_u{eq=?'5'II~~Q~?<~R~?<9~R~I?O<^߾o2ܿZ~RR0>?~x~Qۏu|]?McyԿ7KiZ&~?'5?>u|]?M('5c{?x}Q5^+|QO4'~POj\W/YoP~RO{?/]~z>i~QoXuX?M~p~Q5^{y=x?E&~:?Z?QO/~Pϟoߛ|鋚c?E&o^/jOl_E귿+?E͟izQE?O?jC?OO4o|o5QKlR~z>_76yZi:iC&>cS?M|z꧉O4ڿS?M~p/O:Vn\n\\\n\n\m\e\^vN r9_?5Hrj~9_?5JAr6}93_?5^;/'×!Prn{9i"yHrz9%^r9ȼX^&/gW<{1^/džIJO.'{aprw9o]+͹S%su9n]trju9H].gM3ѵ8S%rs9ٹ\$rRs9\[.*su\rr9q\N3.S$FDr0$g Ag@rlm~OvɎ=z#Ovf;d'dS?~8&dklil[MOR$d: fL6^1_N60%{d[l[lϒmYJvZN+L%7M&)%dߓs-MIqlMHHv"H$?*lAH6'O3É',O80<w"?9r ~&Mӆc&^Mh4!h?2cD &K~ %K-X¯?p+UªDP)%J=|$I 'AN&AM5 j$IS f$Iܒ%qKfl"O4$NI8%qJ}$I%G{ ERx$qHvB =R霸#qGB 5j$H31"EB );i1R$HH"O!CB$tH܅KԐh!BZqEWD $:HNSrG)AD $pR5( Q@D d'?xO~:/f6lgw lg=9lg= y6ȳ lg<;Vfv6sDv6ClVg3:9]lPgGlfs6;;fq6YlgLJ9lg38 fovX+fnvTlfc6ӭlf36;k6^ϔٚe48hvԳ1lfc4̎Jrt66#llfc3iE6);c?2c2;Pe6! M\?xc&c6Mٶ>ٶ>$&a6sv&`6ɗ}(.ϱ#&_6񲉗MleLJgǡcY|<XcY|Dzp>O>rXG!#QG OIw+c{,>VX1|,z>N-+c{xO<,{j>X+@=O8y,wZ>+c{8<4O8< =VǙCO\z|qX+ce{@+ce{h)cE{h >=h=>LJ +c%{d>ήmc{x=vOc{`=ع?Ǣczh=l89~+c'X^k1z޼?Zd^z'/VT_Kkx_(}ץB*]ݯ7n$_W}]3\ 7^܋/B5v^ooM2x_o)kk@5wzkek2]&ӱxMIMeP}5j^WeЪO9հU5aU:O@WjHUL^sMY#+WȽzVzVsOVè:P裚?;adToۼ;ܩ]?TzWePt5bj 괢:jTJ 5ޥJ5WR]C(jxP͑ FH յVGXc+WV`52 :3užqn_VhB+c]hFC5*VWq5 jtlʯ_)P]4 ͥ\͹ksoFEC᥹ԛ\݌ԴE6vsT\U\ͦfK׹D ŅCRl7ܰ3wDfNc& TZjj଱_YjWG**k[*OVoz k|S(ZVmVά'U[jVkV rr5~jR*<_U Z5P*#V1]0Y{U WeUZjAV[ k٪UYeUV-JFg[X-ʽV _y,&al`5 q{m_c\Yxk^wڢf5nYpMz׬\7 jf6٬fêŇBkZVdfMf,fGl2%6ɴOجfM5kE7zo<_mf-45!er,5i\]'xf 5kqjjZ&iƙ5kY;M(ڬNg||iW0IeGO5ӭnt!if}OM҅ZJt;= wksN]=Q/HhɃO1A@NHj{G|i x!ՁF,40f{JcD#,!rA)T v?zaj!:Qz3 .@C=5 9{JC1C(1pzzts~>9mĭ'n=q)_ƅ.u .م.du!+>v]HBR+'\˶eBR]b+n_r 9]BNqypER҅.t!KveW,|! ]BF2卙+֏х.dt .KuŽ ]HBB%\1ܺ[p벏r!Ku \t.s!KXu \o\©K8u ./ ^\x3W|QB6d%rDNW=4EL1/Hun;4!"[tQtnrx+7>#'7>ɍOn|r;ɍOn|rx#YnQ'D?Q3=}ڿܾ|"LOpON>~-D8VjZLӥ'zD>쏆kС':DSԡnaZ "O'2jѝeaTX%<ћ'zDo qYЂ\bFg>ѵIy"1.aʬΊVFw"Np;Ev"ܞhDoF$[]mD'xP{ PQ0j'BFV:mvN/#@;hO4"Ehӧ&ˈgXkcYb'ZD`*.^ӊh vZcxZū:^'D{3\'Dsh'2HJ։:Z'Z뉌 7huZa+قV%V'D[= HFMuHbDAFnR'Z!M lS>eDx ȧDFDTٖhِMOa5c\:KO\!#eo"M2bLz+i!QF-H &2DF>CHi;@RI7{ҔЉ:QBF#"d:Q@' D`"N|Nω9Q>' gP<'D(sxstNΉ9Q:'JD霶P;Q8' D(sphq9Q6'D<(bC OP8]Kv.>'Grȟ..QÎ`.y%(aG ;J vv. `T vnnG;*`I:F; Qh'ZB] )F{b,AO=ICn|Ovt(':Y=YC!?ʹh@F)k>OtFqˁ vHh!90}:0ZΈmO7VuȰD7V0D?1Xx':#RI)tT貅_o,>k>]LtolW\;@̪LtmCuĎ4UZ@R-`|D0Ah٩bjQ *F*R }2*HڜE!TI "A*j@@05٪ZńˎTZ# &CUTkoD#bi~p "ȸbJVa"O(tܞ!O<]`˔?fY4YGP'''D2,7~j7yt8fP"8&{h4>I7۸׶Ƒpo$)jmD#ĚiLf7Ӿq$Oeӝ?5ӝ ?,Iؾqhb&sofxٚiLf7z Z4n7}"OÂ&n6oX~-}7iM^y}X {(=?LV'2;BqcurDN9^:?-u2DF?Qw)m܏|>'+O5R>8cSt{]stz|L>u{JLfj7SIJW>_Fl/SOq/E|3v3>Q§!'A('nvx׍qgx('pI}0)'Și?8V͐'" vF|OFi5c} F̼W@ԞQ{"jȘjYh2'2&"I'2&/戏DJ>1yψz"ZODӈ@'b>=ѧOd,z"RhDby @F3? 1ېУOdLCF(>?FA'2OSԡiD<џ'4jvy;OtiC 8")ѝO4"Do"5GMg)"1O$|b`s\剴|"#3IH<9T<'RDJ>qON<'DB>tFR"Ot5_FP .TpKAh {r`'2huN,?3KLshu`BWJN։:ZOt~YW|u !.!eK:XKpYWL/KimuKp;K:T꾭&Nw h-uedW`S>eҾ,(ܢD6=1DԄl:MO_FVݞV-qNЉzbVqmR@' Ngj%ω9|4ĐyD<ѽSu~p첲3fg,NR\Vq9\OtY*.8Yqr#8LZ\$,5qřgZ\$.k:Vg\IV:I(3Dg\$lNR9L\$ڝ$dRL*>љv7pwIv't8L9pIx93t8r'NR˩T:Su9IT'r35gj\N4'iNdkԜ4'@w/;Sw2֝$=^pÙ3 j8pIF23 g.tӽ:9]L8]$'{uF*\L˸\$˸$+\L˸]2n'#Q.ve.f]+ v`?'~'26F~uoC~ DƳȌ0cU e/ĵ$%'s*$~Es)Llш3'J ĕ$D5q \YiD#l}ڜ 'D6vDRY)po¹Me%9˄&zLH0`jq$h/5#iF&K+!H %&0kgm6Sfӈ^YL^3y}&C{&dYLV'./w>k39DsK&d>g2s{ֹ=/1%|>E SD#Zg3{^3D#Zhč:g3|l^(b-L?_FE"X">SgsTgLB3zF#"<=k)~=/-5Kϋ\=돞3{^bK=뇞3{^P2N=3{&rZFWgLܞ3q{|b4"Z]D홨=gL̞=gbL̞'n #D왈=g"LĞu)ty bL3ܚns+g]ȳ.]f Lճ`1>=ӿDϥKt.=q&NϫP#xG= `qx8#b2L&ɣ'v$Z)ބљ<:EgN"<8aK:J>I %t>BLɡa/:S@g LP=/AC@2ϔϙ9S<#sxϙ9S陲93g')'y"YsT=CB )&ũdҙLz"㲑ǟXp{։}"2ꉌTОuh֩=#5uڷOdd"㊤ 3av=]{L=1bԁyvYL}ϟB/#\τ,0L% TYL=5UrUF >q^I ,f':'@F BLB>ѰF|"XSZeDGgM\zc{aПO4l,;}֝~ rF#+v]"_FVbuhWEZO4Zгz]xV^8ٰx V;qWiEܔd;~Zv/īEqqI+&Ȩx"j2"ꊨ*\\%5TtEAWM?Qpp\sE4Z*HIS,#dN+.8 qEX\qp F/3֋>U]qoŽ;ң (U&v[=?9W=JL\Xa' i~~7@VZ +-l貅 3wJ kض0Y`J &mȩ!fV K%6;ã2Z(qC  h~ڛM Z@n¬f6 bJ@V%`"cfM5kgZ\;Ldhf4Ӿ 9ZL$ТMl43V!@,ӈj_:9ΑwiQ`bws?F${ڹngNp]9ygB./>&Y7':#Wҹew. 3a&wOZgLПw?1&!DvI7yu!(&X wXa 3~ֵ>g[KuϴV?N?{#>D4F?3vЀ>ϴ6?k?ZLOC<<ch i󐡓gLv?e_F^y>a[qĘm`!&d>g21 TLVyڼOCėwڻOt!,e:v@X5!չ=ܞGLi3|&@D1Gh3|&E>uNTqƷNY'{։=(B>qL>DֳfSe^yi>QE|Q5U`JL OTpF*8W\PL)~}bg)ף|gN=Sg3D[g|wrtML䞉3{&rBR?-uLܞ۳YyoOK=g&ꌾ=gby&fY|=g".j>f]'_GRD'LOh=g]ȳ._#ދBLuϺ˪8cOWkuD\\Хg_!NϗzYg3=zGϱx@|ѳu:NGG5 gLWyi 5ϗ͖KDw>-kĪ7zO4BnAe!^^^QΗey 2 y&!$dȝ/򊛺trj]oeqNL՝okEF$-آhQ].WVآ'_=.E+ۅp{3\!.[/B]'2ZE뉌XGqbxa0Vq!.څ0{" 3,xPZc5)؅|l7y1{9f؛yn;{'3솽Iwߍxgpð0ÈƏxOg8 'հ+Ո+5ޓ հ{R 1ڌ1=i,7n6n6#nF=݋n؝FwFva#'ׅ\K tPIӈ'aӨ{q1z`b2܋ы˽Fnofv/nFoFoF8n_DIlP@ B](K / 2袅2)'2 uB/hf4DJҹP:OQ΅¹P8 B>ѵZ2.{.:O4BS{IF. &ܚpk­4_fOt#qj©ڻ퉁 '&.%.!.$|!. }xO,蚇_m?CZL|"c'ch:383]]Od ] d($%ݮMj4?14d μdq-npϋN?1c)'2u&^eje_'2]5B`F f!VؽĖ}"c~"c/c?2&+c/c?ma$%,$;cx02~Efˢ,zɟ0[lY#e̖EYH?ш0Z =FnD݈=fQ!D#dY !<QD#eOFb1*H/#BZLF0W)%0шVۈc~po#k%_f|@)=e(EУYdz(j-r`٣ %XK*N9F#՗xbӿ'V (U&[½nYd,-DB_"h^('_q"/#|&e.C* ze|b't}BEWBZHУz 'PSB^Ћ^%!B^/{|p۴|k$ѝOdܳ=>ڭݮЛy-=TvFy뻐w[`9jWHˋvE;Mx]vh=HmKl]v9)y!%/:>;t܆.Ș#w9'/t]Ha9]I!/$d]H D `EB*^Hċۅ:h]-<#B^{G iD/fjB ^bIv9lLRM x/TV؅PuB]4.T \rXG|4C] w.!ÏrH$ w.Z Lu.ZWuu!.-6ZU AwѢtآPs.9bBrAp&_} )ۅp{a<EbG3l퉌r=%rW[ v!.aBn/#"iIv${"&{bu ]]j]h -v.!ADN.4؅`b uq^?T ceRCBk].օֺZZR)5nXO4LJc=1Y/5&״ՅVꉌ*S4Յ].ԥJ?e_FD@ԥɊ&/dE/Rۆz"c"$7x!.z ߕ* .qx!.%6/҅\h$^ȤÄ54)2B&=1oRGj3ڌ#棈.fST+y.EwɡVzjz3?a;B\( s|.3^sxxJB\=/΅ҹP:O4|¹P8 sp.O0#esl.ͅP6ҋ~&pk& lȫwrȟ PsB ]4Y/->ij?F$t}-jK'Fj*vfmAR:mf۰!&}ˊڹOtYI[Ҥu͖h)i>hE24dԐaDFl3 #'70= D78=6$ԐPCB QOdIVn5ᖆd|"#x6Ӑ)yh~{o4/- |3Ȉ' hD?1A6:;DFDNMfАH} wXaiCuB>q<`2Q+tDFI|DO:>~"#O:>/t.>2&0%]*D#@ =>LAQCtQ]҅ =T*J:J(Gqw qǰA@QZ T@ (J %~C RE) z@}6%&?,aPT &ې jW^EJ@Du.S 3B:@*@Q QG2PTRa5Y2jU틆)ϸvN5/Ec[3d医~d崱W|9CB>_im _( D#nXzS{rb"ڼۻlS B_tp/i~^EB^(ދN>1,@ O|bzԯ4B>ї'ppE } /Х} \\f.zC/h}^8c=/ܗz$EkBZ^>_FP̋慔 yReģ:-ʋBк*NBp!dr.oROkyw[P ':IwtIwŇB=шL6='n0=^߅p]h aDFD=1ARw.7q{b4t؞KuDOO1mܘLrrmC <'z'w'YO4D\.q{[TF ؽ_yB-/K)/Dʋ qB&!/tG(/3tpvpvpv#~+^4~⤡B/ݼ/6 IB|{tRمw`d3 .ԼnF_g7=0';⍭X:cuyUW,O.4 B;h=.̢B".GlANu:t^Tt92AFvǟv? B|.tKl.ĥvKzE cS;. B'Ї.$ ).N4ň%υs"AӴr.iuk'Y;i3WN8RBPc.i7qkr!\Rg8|×- ɉa(̻H.Kj吚{Ҍ2;~}OθP0.{0b'>|Lw"ĉk0r˦9qOODR%+Mu2BNB-~ _#ԟvbto![K7' DE_څn[2'[:.t]Phh'2v{rf&BC-lKȖ Vr6 )&gAKM|:#%&dcqkƭvy8UE٥7f'װIDOaƖK&W1ɥ좵DIRLbR!KY_P̣F#&Sٌӕ?=v){V01آuBЁ-t`˧2v2v)KAZ dU[V s >Yw؉Fpa$%"l)Ȩ܌F;4?lxO^!Zt]]_%;|ZP{uuZzFbKź. FuZ:k:kuu_آ`%<][.ssfW/A!u-t]֛uћu!GzkыuыuI؃uZW':C'ZH{ Ypbl-ګ.$[g[/#8v."ZHmT'ybaih-$Z[օ4k&u!ZI:HgԅkYCE[ԉ:HBZp:.:.PH$I&.p 5d;?hDUHm5ш5Z,DqVkjȸnRU-N4ͽ5X__҉q$r@*eF!h$r?©HU(rgt!ZGb-H-R}YzD#er; jYB!"t!Z&Pim]>'iis!4L+tf3libMm0l2,m0//'+rp]djC[lBNWXSkѩrkD@=>DFK.k4b2'9/a<:~g/r'rvv;wvLK|2s=nLiLt W@w#0?Hk&2~2gjfhqYd#hP,1މ A̢grGGxҴPLt%e!tYuF2|hY FЬ,4+ tdGhaЦ,4) Mʢix9a\p!7YM2F \P`~pFRd Y>c!XbWxc9kt Br78OLc bzXtȎPcjnm5X &uCEE_K'{EūWReFdկQ9'/1_tZtZj/_-^ɽT/c/5Ydxz\VAڽ<1bo-O|n8;_7lzwZMEq$2/.`gU 㒦ϷT>]ll6Х3衪뛋4gV\xrA;?#UƉ}8S8)?}vvY{mob'^[YQtAu-u&8a!pf&`وq.7li` qF543-_DEoJ/GH1$K,B ^&:Y"eB [ï29,#<9?4~,ïEDƂXl~OdIWEiTa=8mH-~%=D& LBASDQ/OiR3טD3_b60;-3T|]׵bѥbѕbkOL .%BK}qf?,Wܬ˴8tסa_ o/F"DaHӺl\]{DÕkY\^]1h 05%j/j///MEޣMEV-bݓ[xؾ=e[Ԛ~+Lx󅯽# Zh VEWE'^/|cc׏ *X]+}[U__?ޫ{xc|yU~Uꉌ25(`_#KcUuU!牯{^q _LY{0rٯ67w0O!^_#UF8Rጯbw{r˗/߾f^]]shZ*.**8`Uunl6̛3BUӉ#yokUcfd`x^ k>]i]NZ,?W65|N>ZKyL|Ur-(BYU9ˉF*(?t4_ZBUUɉ{T^|W)Voy쵄7␫,VJ&5%DJzJ֥88*Vq,^&ufįj]Bu3QAU!UUÉFޏ_A‰FҍI#iɫ0.n*.A_.7c^ֺ[[PuUorVT4 Wo]_{ `R׈V}}eyc'\Cuݝefݺ8f$WPnn˱,V7~ uY/_vW7|W.nުۺY#[7mJMܡ_yD' ϋ Bf_^Umus?ˏ}J%X[H/enÿ ywk/aUkc̶;fJhɷspJ^\a$^}PUjW4uOv|wh2PO ON_?bMW޵XaZW"4"㵗01&:kSfUhUhrNЗpѝѝQ?ꖯLt~ ==DxutaE {Ɏ?wQa&ZZ_yD&PB9_K"˗1J+*Y.Z:5N{Mֺ;_EI|U ȪǪD',jə|Y`O OI^4+J.JR8O| hy%V^W-+:^zx/^u%_Ghٶën+J"CR5{+ JRԽ+U:DR#nԻ+]JȽ:oőHe n!]ijWZuK~@+Af09J2^]W24)ZWJ֕u=%1}T])TW Wbԕu%>]v^ĥHWѕntiyb4b-Ѕ Еs%\Or Uzs\5 ?nJRcNF\N|@\AT_g\i%WU; /O+ 䪭J9%Qη+⪩ 7ȸ˽!~(W-eW"Õp%*\o/;ă+zˏWp9{F(z~$}+)z[wLiIVR^lp Fi}Ѹmm6FmVn /#j#K4(6ʳlrnmeEq8cuW m¶Fmz%nt_Fl}v67~!H6Mjڨ"&KEQOm镊lRFICԍjӞnK3oUn)T *nE#,kz Fһwr6+)WH:u:[RZx5B9a_gVipknp{EH[Z(I)uBw[(;w=l8lmQBeb2cJa[E#¯!­ Ո?N a"a=!نHhqő\^W=5S|r:Xl;t9+\ΐel'q nvNQ3Kpq6q ](⓯ݮvY`(pWg7D W~=TH-Tw(J۽䶺 oS?^X&hxE +J_OSv '?n,׮?錼FvƑ\y;%:le^ZX);r{i-S3WׯD# ]=~{~mlNl9f;5O|0Fk%3sr󥸡%WMtf{ƕʨHjq/'.j/Ɗ&ru*Wծ_/^X~؉ޮD's9K6?埯(ainDn__Q¾2ok65uu+JWSa?w1Ҍr,l l_/Ʈ+y}{=muu57?-!ms۷Hc5l/ooDҾ]`d|y}ϰ#~͇;KξǑj/ߏ2]̮~&,* w]MvJO}=^!_w6#hXZbŮuŮŮĮW^_Qė1jokY0(a`q×/EwwzI{}ݾ7CE]q]mBU]ケm36))f׾_QO|L彇{_шrRwA+{\+N?c1yF#c1yF#c1y_nd1ym?c 6_o##?5>F6rF#\O1;k|HRRcX#QҏmFm0RǷÌ,F_moc#6_6_ds|?o$nt_&#W|)o#猌7_?ls|?gz?o#e#W2wS_7c~k6FAHF˗,o|Y~=ǿ!#?Ƿqo#?7r9{|/{#6_Fm|9o#o=m[#㗑sF׍ТƷ?=/Yo#?7s|bte9~ŷ|FHH72~o#6 {#F_{#m[# oύm|Fo#)~ods|F絬?5{|/-F?5{|/-FoF,ǿǷ?b;_F?Ƿp'o|/F֏_F~=/|?py?5!no|~=1Zwl?=xm_>~_FF{|1c6nqn6m1mx~o#?x!o|bd=Ƿ3o#6Oo#&۷XǷl+7xmxdaR}{|\~ŷ\ۯ6o#_Y#c Ff;Ə7xT[Lۈy-m<mm6b^|{|1/{|1/{2̇o1}OZX~Oq F>iۏ<6r76o|7xo#?x=HTˏ7?Ʒ/<+c6'o0x=7~ _m7|o#~_p Ɵx?`}?oo#~_ Ɵx\?W2 o0{\o#?_mO `$OF~o0x}|1 Z~'o#?7U'oo#?8 ~'ǯe7]~'.+u~<6l`u?q|6WƟFL`K4R~'.+l?ox?o}=?7xo#׏;E /Ʒ\ܢ ۈ_l+ ^o|"_F .?q n?oo#opo#/x_ˈWU 7ۿ o|q<6/ؾƷ6~/#_ .o|/}F~ omč7xmčDx+`F/\W|qc_0Xz7S)x=ꯘo#?op{ۈ +_ؿ77^q77X6F_pF/<3ӵٯ8F~ y+y `XRR|HJȱE-d#Hv3݋d"q$?w#`m8d ^Bw^A7digi}g{zgxlgIvTgs/X'ˇ7{6KiT6Vs,IeYeY%Y9 0/.K-GfeiYyeVieT<4eIReOdyNdLJdHudF]dD@| dAd@g}pg1{gxgwaf1grgpfnafqkfif!h]R\Rfc-fQbf`e^e]e1ZeW/;eV^eaUAeS)eP/dOdNdANdANANdAMdAMdqLGdALdqKdqK:#FCdqJdqJdIdqIdH= Er|#Y,B,swdqGjdFjdFjkZdEZdERdO0?" )" !YE9>B,tB)\5dB-3rP!{E YEYtE9>iʞ(eA d@ d@'G3YEYEg?qϼ~u/|O /_<({ ^8± ^8¡p#/6K.x ^8b[م.Q ]8bSՅ.|tu- G]lͅo.|rKT/.|q \bD_\>-6 [Ϋ-|nUVnݭ>ؽ*k^ Z Zp;-hS -hF 7Z}[%%]bˣp,f[QxʲhOM ,,X} V_ΟF r`/| E ,. q/厓MV&Vۅ+^%+^a{E+^% +^ߊWD"ފWjo4W"A16V$Vļ"1yb̊W"!b }E+"^X~UU-x[񮶾V8w1,_+]qkW\ڰZqo+]=[qcWUĽF]mDuŭ+n]q*^EkN]E֫zũ+N]E+:]BdũHzť+.]E+] +.]m 8t}4#+]=IZq*8^:~33DJ+\q*^ëmΊ3WA.WtڜY*]31;مY;>مU-U(bJw; -]г[սۛc"[U[\pmnnB][K5[;F#v~ v7w`gALݹם[;2vnrw.vp7;:gbæ!?8#::8CLN0Glqaa?U3a!L?LþaR1,?zr?ԃ?:؃:[gt9C}8Ì>2n!>bՑ9LFa"C$}p1aⰁ}X#rX+4)qGA\!>Çv ea{Wqq_!=,3֪=k8C<{Xop8Cz}U^r RzZeB/s*S\G]j״ZViU]*鮢Vfo\*68 KU*Vq+TjZjVʋ@7u(U[9jVojRU[jV`P+'WcW*RU_@Z**,f5 q-ʑj\FפwZkuݬm6nqk6Z|,f5l5 Mfdf-n7 qkx&j{Qb L͚jTZ>|5kjRISR6o/7R隝5eqpkP֬Ʃ(o&iYvӄiq4O|g͗y5T[3ݚLvkGTnߤ[+]ڭntNCpV:ԅSuˣ[=+HF5.ngݦm%IݲNg^v%:n Џwkh蒨n9tˡs-kOAYu~[2A z|߾'?u^ йn4<7߹K tmts螩w1[81L!vװLmwn2<3ҷ ݈(3~}]wtnHF|z{!9tpRC78Y 9ആׯFt^6&U:䀃3!z 1!'VhfX䈏f=C8xa{j̰q= adODrH$tI9U9NՁGG4C:;C9ld j5M 3⁄<AY4ꁤwė x]!![HlĢL }`!H481F42" 8H"asZ6z7C0t9DmQ@#4C=aG7G؉[Oz㧱8ĩ'N=qiK/Zhqġġ8D;Oy2;)>)>ę9'<)!8)>2N)A8&1MQ_|="=)yOĉOx-hv\Sq«Ӿۉ Xz֟hvbSrwAO4xڌ;mƝS8vb-N6r:';mΝb1ۉNwzt';rN Şӝb3V;pM&ll|wNvb)I:1څ.dKxI.ӅĮwɤ.Qe`^\`^2+4bDI%0ׅ.߅.+l\X]BVK`w .݅.$u! I]r%l^ /$u.i%v%Ӆ.tI.[W$ )]HBJRh-KXv2х.dt! ]ޘb ]BF0^>_W{Ѕ.$t! ]®íKu .(Y޹,^HB:ҹUͅo&%Spg.#'7ȍGn'LopKnq-e^F w|xۋ7j![r A+~%(F 7JQ-C;&fV `$989EW~T?.^2vWUcCn-eNUwϪe/joXz؁VuUNUw7Qedu7oF9iRu3TCNUF͢.UCCաjP=ZTGx_*WUP|eQp{܌WUuU*5] =X\M*WE:U p8= r/gܛH/'㍽vog]iJU\ T>UͪfU*sYUdUI* YuJ5VU kV U_Uׯ*WU۫z^U*lWլU qU†CU*VUI U̪ZdUTUoURYUիBX^jMU5ZRULTU ~V~2R N%,8EbLUɚ,4IU4RU vTSFUjQMA֖DUY[A)倪?5YS銗s9r׹t;ZRl Wtj~U>N<7UKT%m6UJMU*@S9eB2UᘪFLUS9]xʹTe\-U"D# m쪊+UJU<*RGԼkwR7JTKj>0&FY#TE&2)RshR T?UGUƣ~2ҝ;7JUG3(Qè_T.g/#3/XLd2\{0x V%UZL"j {J# AL55U-ZFwUӾ1L4b$bdU9D_6݋BU *vP9L4WV- /{:}Pb^F|[U}<3bX;ExE_ +~՟^`TJs_'{&r%Q . ²%.t޼қWzXjc6:]HH+y]%t SfpV啴|"cV_^vH+Iy%)hU0-}RJJ^I'2 NN%!$䕄OdL%'5OdLԖ SI'2m.POt=tH'2V2'#r yc% _+5x]8O8bbL\]av7crBJ]+w5ՎwTߕ껮p m*D wɶQyW*2&(^sQwWb:J=0c+UD#t][CJ]W!w%䮄u-b52ۊ+w%un(ܮHM=шd5^ +D#jO4"* VBJ=ш0*\ͮSeshWFh+Mvdkm +ZJ]iڣLE=ekZ vkVOx1^WJ{]izF#Js]i+DFIֺZWZJk=dژZu4AB0l?[ЪTJ[]i'I򡩮4I>Ȩ-ZJK]7 6| |ʧ6t%Ȉj3 ;flKWr+dl2MĴ>_FAIOtxWJ\lJ\)+sxߡtΕҹR:WJJ\)jtW J\)+sp'dAs%;jQÞ,ۅ"{x=o~0%(aG . vT=R.m`G4,C(`G; Q DC]hKv!ž<=hO 3`{1z|vt(':Y=YC!?ʹh@F)kOtFqˁ vHh!90}=0ZΈmO7VuȰD7V0D?1Xx':#lL!aA1?8\@J T&MիdD@UJ0U&lqځaEb>d[f2#t>!PU x_'Q%`b;Ltvv*:bwrTԊ*" *ҩ6g*~QV|RHy~p"**P*Pk,6? |M*"V1%+ժ`':Ⱦ nU$کBJ?ш95X\5C_ '2.!oRJ_ +aD# gF+AD-jע:932%䯄YMф"-ɩ&hf94ˡ J_Go4Y-G2n6q$h3x#6m~J?H8f7ӾʹoG 4}tůt'ï->h&o\D"ܛz`f7ӼMxJv?1+hw3Mn_kŰɸM'{ftlMnZWJ^_;kl:W"{"XWrJN?qjlKOdqwcJ=#|~ {|%d|M.衔=Xr5ݞuv1݃?!ݞR7S.Oe˔+4BK_)'_ fhǹ u GPkVJ<`uGr>+!c::B:Ls2fN>qD#Up3kD}"cSQ~sX}"c3WJ^'2&`x1DƸ bŤ}"c%"ɋ9*J>ѯ`OdL3*J^+D!:fOt0>}"cAU+D# k:Zaن<}"cz:B5:J>1|B^}~pO#/ҟׁoFPCn+y;I|!'z:D=j:Jg^OIy%1$۟\'"-AVg|NW^OHDR:䕔WR |rr$䕄W 3x%t|"#)T=dWz9ů픾1%T|f$HNqi3SeDK"^I|+Ux s"PWjJ ^C O1Lɜ^ߕRW#SxzE=gOđڻR{WjzB+wTxWшg\ݕ6_+aD?D ˩+Uw=ݕ{bA]/A] +!w%%!ܕ{5 +v%ܞ`q E\(/lW퉆o_F~jB^lK|rFQ….@]i+MvdA*P . -vŮW_BKA]i ו^Otϼ.2u4וzm ]q+ֺZWZJk=Ve_%-UJc]/Qe_1,R/Q%J8O,qJS]oo1*QO\OKܢ[pȦ+=RJ6=ɥkK'2[q[w\ŷt!u[7~s7N]dp~0\zoz)+EtTD[u{zr[āRBWJZ7}sܷUK])':Y|ZoP>WzєCuK(+DFO[tÝ3Ñ7ZONƄYMFnJxp4o7=^̾iߴo2Foz7F^y}#2t=Y}#oڼ7FV?uy{#oM{FN',X}#od􍌾7ۛm)-|#o|-RX'l7bP7'zF#nV׉(|o׿`EB! mP72,o"iޢ"Q7p7%|?-5-VMFKomiшYz[zMFވ#ENXM?Fވۂ pJMFވ{-g4"7Fވۛ""joD퍨7b썘7bF>ql0!boD썈7"Ky[ok5Fވ'pp+^хBt!26ke4bFthpSe%Rh|.]zKotm qz[E7=zGom e ÛMF *(i4 o W{I7FwΛ^伭N[mUZQy[Qg/#μљ7y޻i=1~%TFZH':iۊ HIy#)o$Mv7mۊH)y#%oMlmOBH y#!o:rO|tKa7F:HǛlſ%Hxq6xTun:l7uxoj-nt76Q&ڍ*Q-mlUS7Mٍfٍ$|bxmGul#k4nߍQ**n`Otpu:'i(q3?ջYލڻo6ơo:wnUK=FV-uwnZW7NfnWVfn6ADgWT[T7jYUJ'.paF='ۍpnOa:YxlO4L r n^OtP"jO4Lvn [|d7ͲMvdw pi',=;?8=Y`7F=1i{6iDߏneYp4׍\OPe~p7Ikhuۭg[hFchuӾ{䚶zhgG; wkkiMunz}7rFFȨ'hd _7ɨu#nG(sh|O7NboOIM7F.ȥ':IK7rF.4oҍ\I/>/&ndҍqlOr>1󉆍uoO4ba]hİ{Z?QٽvʾU^n 26~"\F\ky?ѽzVO+r'rjG"*4R~blOx|~"!5N6cEdVDMf?1XD]uPEUK~ߪƷ*"芠i'2*1+bf_K6Z5>ieL?QiG2"+ezѧ+zGzJD׎sg^'Ju[|G +!ochJ 4ZC ]Pac~pMI0^pMۖ0&kL)ք 95c*!~if'yxPF %nyHPACoV{ؾT hMլf6T [T hLdLӬf-5LkGuYM\,f7!GdZ4Ӻ !͙f*h~Q `X;9.S`bX;L5nunh$u@;7M6F?wKQ:'9/BF?'$&DgJ:ҹcEt1f?18.%o \+ :~#o7Ftxpc .V7'2b`ķ܇Fa{767'2" oK4bak04o726bGh'0b9 OeX#76dd쾑ݷODW|b!#omV1fX|H}#od̾X˓7F^ߴ}oھO D=b*7mޛ6elݛ]w0K'6BD#V cHmuno:SZF>D#9Q̷n4F6@:O|ݻU r{Ӊtb:|;9) F 7ۧ2ˈēd}Fߢ"i>© ?Tp NQ70c5KoSGGoD4%l/zxo7b>OȽ7"F>1~Z퍸7-Λ߼8Q{#joDMJI}11{#foM Fވٛ 1{#boD]|ZͺOxOtVɡVW1z#Zo7]'FzYG9X:toU{qNޮPע2ވ%< <Ko+NC>fgZ5o7zFb :gt:mk4 o ޮi .-$|ZUIo>={hg/#B,D':٫ۻ./iy#-,+ThzyKERH':;նŶMFJHɛvݍ|5FBH':Ȃӑ]<7uIx#o:p7'OtMD'?ltn$8-vGH"tn`Xv oT펻:hOt]OM5x4Q,{~Ax%*F>1mRSS'@YC5tLJD`7MwnTߍݜ-NjFݨmB;rK$oNʻQyۚ!nZY;tGjX7uZWEwܬӷQuZѪz*o$ZTw-;!nphd &_;vʺGv'ܞ  ֝pd `lwB퉌fz"vBQ~a0^/U ;v'̞xh8?8 {0000*v쮣vX e|e|e|e<(D:!voflolΰfl1 a~ݰww#?=90=9 `0#ޓîI5x5x:C5Tn6#nxOͰͰ͈xOawvgѝn=nððÈu']#N&5d43iا{q2z2z4^\^9X bbr/.F/7۽݋ћћ;NW6+НS@w ‹ ka>"j牌eD5 2 2t;d'GspΝ¹S8O< S62BlP\_zOaák)' Žie4bɟ=}5BK 5Y) ssCqn¹Odt7=:kǵ:Otz㯃ciaci{­ &ܚp+ D#HpF{b j퉆K {FF}ǵK  {BK0C'WAzO4;ڤ?1LkF$Cb,: yOkFOd M&4B|3Y\Ko>qaG+}COd -X 8E"E]'2 ?AZw"e)DͅP;h~0<4`FYH}'v}|˘/*⏌ =R~bldjOtyc.IE. -'2Θh25a2#hDٲ1i'<̖1[(leb-O4"i2F#Q7"!@YTH?ш0YaDH,vk0ш~y哑e JD#ˈ̕xJ L4"v6 ȩ %&6a Y()kibt/z"0{Vb"Y^%zZ\lXhĂ+B"(RSH%޾X/}i/U*]d3p[-g4}YF& X.7,O#(<[y1bSF!@_R 051]@_+G#j;3pa"L'bEXbf*'.`@~Ol׮bz/4';A'Ot0]ohm?ͺ%yOt0W{}׽/qÁpw];~׭V?e}w':B\,q`*h;~_N[6Gm~ װp װgi,1h~ů]ӹw 廆]C;2X&d}!jiWܾ;}l eD7|'d̾w2 VV1y}'d1z$d]NV':@1wڻw;9}'H}urܿw;}',T;|'>ڻN} .jw3{י}YJ/#9r{׉}(c626u^^Fh!R5+ߺZO4B "Sw;lpem5Ow ~edmܨ-Ե͢x nsƈZu}':huF޵Xawvjw~DwQwN?gٝkݩ;5xͲ&1I5T S~o#,[bwNݩ=;wS}Ot/ra wޝڻS{<<?!Tޝʻk '270>֝k] ՝2"䚶VwN[=1Y%۟r/#8Tw^ߝWˈW4YѤkx۰POdLD>d]^C^ed]N65ҝ\\Kw;tP|"c5]wN&ɤ'2fMNkt^mUqd|ѽ8lʑCt%НN>1=9tת|"cPS@O4"\ ]o7L`G);s|Ͻq+xp]^|C)ҹS:wJFP8w zp]_zl͝S6w_z/dR`M@ yNsT7`j)&'MMG=ш|OtY>%X c>шUCV Y-ӡ}"#HJސTۢ\ml6$p:OtYq_;. 9iޛў %m'2k>1AFz2"ȈmVd7DFG|ǽdžjH!!?i1j­&POd4F4t!%OtO7att`'F#FWNuȈȩZO\+5{NӉ'.plp@Fr4"2x㕎W>O2I'POd$I'2~G('z$2H#].9]c9tyDF{=nuA]P5]P5]WoO4\G]kpݳ.}7QEuJuA#O4QDG=D]|']|']&DU&˼ThLJ?;jn# AjW] yF %tQBr(!6J T#R]I@WR`σ_=bh@WJ+%0T#ϦDö?,}UdaSWZ@W-` ݫP\U R0eJaFU UCT0 t&:dXjVo,?xa/G?wBN?|w>,wN ':H0$4wN?,D]k=~buh;m_M~׭VPw'5uZS]#~F7J{?OrDy3C ;y}X~" Y}?.(9}'h< }?ml3wI,({bMOAɉh){l>?O );|}z Sw'2*$ՙ}bX !j]_i}/s!O<.C_zK+>A<9\:N;y_q{>3Jb޵6SZe ʼkaI;)y׭._F<ӢJ>#i2ROt !Ktyc|_Ӹ+ߝk >MˤL+>t SwwFd>qa]NoϾh'@k '2"ĸ'm\w;u1tD_2Ȩ&ڝ|zb'&ύnddSF;nEO\_?><|ٲ¼FrF!5e)w2}reӟqP0ʼn=1iN_>}tO;5|eӜpS;V _ɯm {z;9o zA7.o \꼉F8ix 2nPۍ AȡS lG;4omC;9ȡkE!g"m{#ghq-1D'/׈[3nͷéZfG of'װIDOaƖK&W1ɥZviTEH(yThd*`?ǎl cؠt`Sei;|)HtJx}h;0uhFR"D` r3~v<_{I kk:辆c6*c^nk{ zFbKźi:נt^#uh: lh:Gkh:&e _Ƞt]֛u:H&葞ks X^]GN5bA5踆ޫġ]ם/}} 0Mș1ɪ1`w`0>ngqj4 O!gKU$\r.OkZᒭ۫\VuۮuKVn?k5\rԕKVn r5ڙկ< e ;uDK8+v3_}c!-bt1ajRgkD5ꪕk; WUܬ)F g}KԶ9 WQC0©lKxmt %:|\0vʅRk7ۖZ 1;n%ۡE[F܎ V.Z;:U5POp[\rS6f5OON:o\=CqZin]>1iZi#.k8kN_岥3ᚥeJ+)=]?j>̑eIkYgH+\{;>2ːƑs)DS G$=3W=bn7uWn̰);_XfVr9r˧io6Z,v8! 3;U>}5VnJJjCcC8-Ѹ)! ):tl\gBN:|q嫌O eLϷl|p?O?ЅȚ=5}Vóua̗VnrK]yծ| R n8JceFq,wyʃ9ȸͺ2nF5+YW 7yOʽei0X,q;uս2N/OcDƝ\ !VJ+Oi.3qnYFFZRϢ&y<}(qIU?5fhoL +p +?ī߽_#d.X}(& ۉO܏_՚_L/_#Ac~(OypÃQsw>~<#OWdž?=Wuc.*| }M!g⏙X?ř /j;ve;bǮ<<|/;O {ԟG|Xn> sz3>g?̳S8G3j"d%mL,T ) O"G6lUf8mGhCmnZq~'6S~ kW\CmnUPӱ5}{khӧ yM?_ꉰy{W!ԁ 4"b5{jASsi^4:?L<[Z~)T ,K#ofSh̭4ɧ>j^oO_Jëyg&㐁 -}m_ZҚRׄ90 U̯1>LH˃[鈯m{ZA31[D~1C> SZj+'V,sOiBKZؖ%/ 6r\~40/6xk:=~/~3?8|O_OUKxLKMO_-8ܻ)'wlrJ;`72ˀ߬ d[2^?7e^#]dc:;ܺ\9z7G,?z^#N[_=}u|OfsiOySWO!Þ* 6YßC xxG)3P} SR~iZ~)@3b_O=/2_1 j;H0-S_Y9BǙ`T0.% ƅǕ,g0r OYXΨaHcd<)+yJs.O_~~ea /~T)Gai/%a a8?pBp~Y8p}X5a<=W\3¼w[x۟'O;?}=\:pu2a1_ \\X9pra5.F\V^\o8p}qa5l \G~8pKIj8po^|W +#TW ܮ;pn܁ rwl[T nLkk +f \+ȃ86plXy\5kh \5j N߸R5pjЁQ.> 9pi85piu;-?!.4 \3pgم . ܿ8pf՚!TWe|:ૣ髥 D4׾. \Hf2pdxC4_ \ 2p[厁7D!w \mWB1pebE{oV\yl"Ņ C)/;. \4c \0$SgC( . \d\T/$&O\/p^HI2?.p]V "E.^}TBr9k"E}F31ok"׶En-rZ䲵M+j{kf,-rh+"WEn nE,\QʼnF.f)F. \ Wb亯^"w6̔6r=W:ȝ#l=eybybnE.Ŋ\_jUVbE<5UTbD.\ 0*VVjb{yV;`ők",<)rYRr]bY:W*ă^u=^N' 5\=TBX\k5*~G.75O=W\$6l5 ihZD{IE҉ȭr"WA5\qMڄhA\kL䲁`"WĖ/Gȗ#W|>Yi+}B#~[CH]YҲHK#_t#Mؽ6#_ʎk~uߣ|_:vнfo1ǎ ~8q\]^3 hFn/5R|O6sF_P5}䋥r$F׀/ϲG%ȗ*dKq5x䫋qx.PDmȷ #?`䗰##-k{~=/5ȯ@GQȗ*=%|3Ƒ)p9Yq8M.v^#/F~6N u /I,YF e Ux^R`x }/ "G-"101rʈ'Gs-m@"..n%D۱Qaz};S@1\j)& i@F P-I1כҳ!-_-zNƗQS=O˃[4%qˁ^\@L$޼GOBB^,ؖDV&%$Yv׉[XwwN|sO_Y YoiJܷ8U3-xRz6R=%0s]Fmկv~RMKksL:NR=BGs+@Sz3K$ՑСMQBjhuMfAt\3S޼NR8>kf|iύ?nij7w*L-v΄BBVB<8jcZy=Kꋥ~m4gei_K ]zӒex`h/΄̄,<;jhI|K''4Zbe ]k,Yn:$+]vkMǮ}Bݿuh zIH\'By33- Xs$W5  ivّ [# O)^!B$8᫣P-Ԇj6;d=OqhjMH3Z6j{$9 vwxM$^tyĉ:qN}vxmtٙx'Ĉ)HyI3}1k%-oi1c9'^3L,8˧?Eⵓx$^;Nb-dԐFΉ__dx7HR2[C!1ZL/̇VXBX#θl1}"MH_| v+r_~ ?&/嚿\kvf7k>_5f7krC7Ѧ6! /7s_n Gņ-/nmS[--h-G[_f;Zтtv)c;Zтtvm-iA_=ӂ6hAO zZӂgdBZ0Ђ `-a!`-h@ Z0=Gd%#-iHKF{؏`#-iH FZ0SeB&Z0тL`-x"&ےLd%-hDKfZ2̴ۖd%3-iLKfZ2Ӓٶd%3-iLKfZ2Ӓ,% -YhBKZВ,d-YhBKZВ,d%mJKVZҒd]9bWZҢhE+- ZhQEZlK- $В@K- i_ő(ҢH"-(Ңh',ҢH"-(ҢBD-J(ѢD-JD-J(-NZ~BuPb(Q1^-jO8:θjC #Ңꏓ1LǸhQEzT =lCh㐊qHU2#UMT c\R1.jZb|R1>i㔊qJ8i㕊J՘`bR5qKŸbR5ld0S1T -cS1hChfC3T n*7m bS1ȩT--cS1ةLL=AOŠG_h&#띴P٧QňbD$iaG U}{KG2R)U*FJ?*f2b1U*FL̞f2rTHSpJ\U@ QU~TdxU1^IZ:ѾfU $pbU1ܪFiHs~U *_Xb,&Pcñ؏{cYجbl&i)ójh2VUդXlc1*n<;\pc1yk.2:1]ŘNڎeW1IZRx?*f3U $-eWف_l`P+ݥ +TZ@V }CgX1J4bF=2j5JbX$FNJѣEV"T͎4ѤUV*TfY1Ĕ4e=fPb)i~Ō6Tf)i>Cϊ CjbPf(Z38TdsɆ̏ qR3N4l_3~ g8f+i>][g֌g%g\+i)?Cwi>ۚjC 5^I֌wC ~%g\3 M!IG k?!kFʒ`,yц]vd74׶lX3A׌%APW1#in0#ڎkj5#뚑d7YhC@j5#ln0Ү{3*v kݒ`-0on0{; k`8.;,v k5rn02icB]J&Q2:0d2% a0kLa%'$3)e%+=by9L>&31H332v0=q=b5Ld75v0m36цLorraN0wڧ9I9^OF'=.>;=0yn4ٹ8It$ZF/ՉuB͉mB݉}Be2=~w.kYƂɆ[)45ǖjDn}bcC{;=۟8ؐɆLŭ\O R!'&t\Il%F*q+;έOBrL{QY9kg#BGv{=R2m)9*]B(/TY9IjK)U;:jwpI*d[8;/@2:QJǶ_skg/@-9HaSiߋ)ڶme(BClu[-IaCi6*-_\BX0]F%[;:,sN{Q.srR!Ź:CBtrr}RNMV*읃ӾRssv.R! FZtn崯bj-Il {sB}SЖI*eU{9cA{zR ]Tȶ08c$*X;l]A۱[J鹣s Υ*BGfFgB:J*X;<ή`oCzG$-KU*$<)wp[9\Сmi;cA۱\ ڎlQ΍$B*{Ⱦq;:4s(8JСsU1W![:)*w4*mBݾz{w*8;mRkKARRsc1ʺ`KQv{Xjr,89g2?.Wg!q4LG'; *X;; JrBo\ۻs%W!vfp\l)=jgnS 3TȶptNgmRpuma,kPQB.h{ꫲ-9 3g璫z=3R!t:ln%}a])h{ܾ=)ʱ䴇=E\\sBgƂI*T|eU{Ⱦ)g{*waOEXN\k -^*?!ª`]О('˶`9*Ϝ .R!µ`(hOJԠez/e{ܺ`#Bgv{mPp,h\pUBf(Bs[nYlr³BC}vwTs- cBgI<{Ⱦ.oeWU㩚K{S%t$qI_Q=P拓KBdlQ(dKvCZ;Ԥe:jtAKvrdw(/wJrG9Y;T,kd = pOW6;ұ9mWg4Vvgݢ*GdC؝]v hCx6~T=e4gd(d9]vB\[e4ڢlC9Q a)T$GOJ_Oe'['g(S)Iv✜BGpݣ&Cdd(qݣ4&G}Rݣ% WOU%+' Ud(HA*}婤$=b)d7)I{R;dBW>rtTvzͺ\&iX*7=C(]dd7uwvM=R/驏c:J)DVrSꐭsړ 9:''iSJdw$ r=)QP)$HZjr a_wg'˴_ڧ4_Le%}f.;}%Τ[R!dw?K34Kyl:e崣Gfuv^*}eߙil|g6*3:a7d嬝T9fL]{rv.N{3L]e:80{v`*+kglBlٹ8Wg!Q{^8[g'BNoن̇ hC*e~,+9 L$ɲ awG䜥BQ*dd2-#ͩ``R=0w6`r-'\l}$[gt&B_0󖕳v>a&>0o9Hsv.R!npFg{3tY9kg#Bv9Hsv.R!npFg{yY9kg#B_v9Hsv.R!npFg{sY9kg#B_v9Hsv.R!npFg{5Y9낶O( d읶O($ 䜝O(( dpFO( rN' s OvG䜥BQ*d +g--9mPJ99mPNOR sz/ Y=jl Ov蜤BsqN;1PҐ( i v{Bٹ8WBGQљ@=d[;:jsA*ʜӞ Idp\lKdBDή=)QT99I@%wu:j7k_[,qN{R2PlT;;Hf#3mf2#+glBѮ`l9BQ*d[RӼG*8qN*s!tŹJaљ`{F< 2;geO䜥B`F$QB{Ⱦv66W!tn 6v$炋T Fg!t Y=d_8[g:pwpH)wqR!t\ft=uA*Z5R휽T9:\ЁKlHxtho=.HЁU;HK.BљKlWWrkg:r+;u39:crׂcrQJi?!YlB* N cwv,8׮FsrmR)v^*d[8Tȶpv.WmapƂI*TdU{ܶ`'BfG:6sv. c33*dX'eBY! sgz;w 3'眫z;s-B^jH1.Hۙ]^*d[88ǂTȶp.8Wma(I*2s|Bus/88 ι wׂ!W!zf:JUB4.Hl [gW ǂ*gRYe(s>Roͭr꯹MV*d[9Tȶp,8=[\+cLssTȶ):;ma_p(8ڐqt*8*dyw-B`:J=d_X 66ym v Y R!©\p!Z0*ga72=Ztn]  GcBx2炋Tȶp- FG{)tC(tma[+hr(8*g#s.Hl ׂ}v(RE*X%vr³9BS}v˵`Uȶ0L?6QU:W!<+mN*d[ g~Tp .ׂ١/cP/[ Ȧ`̾ 2c)W!<;KU*d[N )qCn%}a}b̮`/-NsS9\Bg#$o?bI򱃤2&>fxA| 'O $Ň2JedO?Q(QM%G^N{z/QJ%wil'Ju9ٽٞ(O%G\.R!#UqxD[{eG,l98=jҾRMKvR!+gY9M˓-/Oed7K{R6OT\BW*2;QԝoLMued7J{h;Q9K]epFP_v-(NJedwuwv8=)Q在nJZ {K8]j>&'j}(gywu}%Δ e[L9PvhCFNٹ*G3.7bLQn'glN}m&G$>.NҘ6}QJarl aww Trv.6}%3:a?Shv6R!n( I{S#)ٹ8aOg#w*8mf[ə8[}>"G=)W\K$}@IDVr"gB9:'*\iOT%. +=Ph),9BL@,BR![ yY9k0msR!蜜T}K_0iO̸g&ٲrN{zd=3󖝳w6`.'ogf28 3 윽 黜 }t~f:/+g}~fZ/;g}4fz/'}f/3:mqZC9{dwtNY*>]B@jrr69mPP)$99mPX)(O(0k:J!{8[g'B9I'KilT%f2 !mPJ*흃s vg\BLG/cϔ4fJv6N'6d읃T;9g"-\DMc!+g,Cd윽T8: Y8WgpFGQ%{8ۂ è9:'2=ŹJQ`:jbPdY(*휽s 2'\lFEQ*d(#HfB#[g/@*5rpI*\3:Mn:J%g 6ֆqTrd_p v'\l{<28cBC>rr6*s Ź*ۍtb`Y;\qwR!99\q3OG)Vr$l Nr(8Jqsi6 Q Lr[9k=(L- Q>PsZ> M %nRSZ%-{BH\D 塅B9hWk.@ 埅BGBBEgPb}RPB1f"BfPtY(,U)Ҿv(,O& I ),=쩋HK)dPX(},PX(u,8 U IKW*jPt(UBAbpXJ,T#  BBAAҡj 5   BM` ira/X L 3vIK/v0#4{a-m2^\/L%-eR0^X& yxa^,ĩeUv66ym]ކ, I*d[8;Tȶ08c$&͹U:\бC1W!t\pqڎe-C;(e(s-L殺k.*g%+Kl \dmZ0E^#V١2,;ma_p(hjr*8*d[\ l \wnUw˦`maW/h߼(I*d[8\ #_(-LkO|"[lB]Bx2TVN\e' tX0*0HnUм6[mawbޱ$2s%W!wChC|%ZXX*.=!h_pUȶp,8UOaRp YP0*d{ 9O9>ɭ 6R![rS;8G'ώIC;BD> |d#k> |4HFNv^&𑋜&{l&H{InB]>O1x>;a(3E Y;Sg#$r awg>Q>|A]:ʇ~e1 .ib.`*P̗suOGi߼J}.{ywtNNvV%@\gt&BP elB[vnF;PsqÞu&-ݦ 5gY;g+¾p\Ź:aO8P(]iqelT;8G$B.;2aNG)iOL%R:;=P T?蜜jg) 2Jl}(eBARKsrR! (R{>:;g/B_9K38TH}MVZ!d윽T};:',B_R![_ +Y9kPsR!蜜T}:3Jl H9HVN'O#[gmH9:'lCf.цtBQE6I'stNR!ٹ8Wg!t% RORD)JQspR!\ 6*!3?RDv6 v;gB9\lEICgt&BGQȭC(q9{:*stNN{R)yȵ` vQJ!{:I:I>"T;;*- LGm$RTOrkg#BGv{}RdYsBR!t⋬HF6IQCiD*4r)Ja`:j;RTnd] v;g_lw"99\a38}dP-h6@ R!t\ ڃQ,$c$jD*X;F(R3휽T9:'=ب%Ź:Tȶ0:QjK Mv68S$T;vF!*R (-LG)L*wZ%g[оZEVr(8JqsqW1U,}SՊTdU{U9cA^L+RKU*̌t[3R .hO"%KЁsU\H,R?MG)ElZX9낍T9\Бs*hw(El3Hl 35Qwq Irkgq+;2<ΩlCf.Wg F)%s:}B.Q;D-QRKΖ%i:Zt&i)%dZ-Q"4X$~ކ\J_*Td[jV^% VRJ0V% ST(DIZl,QhJԖ$ͦ%I6%jFfS&Jed %@򏤃$*=K'QITt]*9JN?ޕ$*6JM8(HZl0⋤]Iϰnk.ROIOuI-I)$J!JRT5RK>DBlj/@j i ‚5i S+H$-H$gڟKZniܚL]bĴ=1SO3rI3 O=N'fމvb-)=Iub,i>ĔW.634).Sd8KL]%g* fFSl41L:%3Ą2-(RÞ cb(i>sBI014d7%Ĝ.1KL$g&(ӳĴL|f`'μ3-IaIvIT+H̤dI|EҾ IvyOb#yWļ&1SC$(`j"y"e&d7eHv El"1M^6uƆ4m.W!CQ*dzw.Hl ׂ`!q4T< uF*d[ 6 Ȝ .R!µ`(h*>rg#.Hl ۂ]APpUF\p CAq44<EiѶ`̡(- ,A*d[ i=d_X*.-Itne_pUȶp,8/q~w)Jl É1W!SWpSоNh}b#-lOrS9hϱ8:8Kl \f9vJ+ \ s?;BFr*8*d[\*43h9=.N/OlrSY+hw(8JnTpUȶp)UOiX>KzuS{]c]Uȶ=UOiѡ}Dr*- '\l {ß{HͬOOqhm vR!98G  R!tn:X94Qqil}s awW'}ӞO"8+gIRdSB9;}78!DU|($kglmH;hCٹ8W8(TbdlT}*rpɆ|j" 6A4oT|!gl}grpN'|vQY/8B`7cY=dlT};8G$B.BtzB1_6Is,hz|E].i{EI]TϥJUr8[Up98G m8W tuek5iY;g+B9J's)hw(WTet*_Y;E9HQsv.R!npƂɆQTe嬝fVQ:(ʮ`/BG9KQk6Q r}Ea:;:jwpI*d[8;'%ʄ@K= v98I"]EQ.wv.R!tnpƂH}&+gP7sR!tT蜜Hu:TKaJQlmB95ZKB$BEkW5jj{POՔjJeDV/;ՔjJb50Iz!=WMuK|Z5,ilT$ͧPUSj S5TMJԘd{ M5ZԒ=Ԋ$ͧ,$i-Քj@5: Ώ[HCyPθ8W 6$0b:Jѥ"ͫP?9hC # d7(}4= %JG)q4T5$OHPhZ4T+$Aa7T%ݠ [ph(84 ݠP;hU Zd7 Hv~&P hKvilLa´a.fݱ߄iyKvId7\7vr0nDKvdw% yq|XL}滒aj+6ݶ;a*fFa0Ovwu62&a)n Ɔdw 0l0Q!s;0}쎽 a0MkIvta0Ӓ*iwdw4iOL$G[wNFvNv)̏ )c"-&r )c "-fݚ0iHvYdf;LI$ S9;-{9Raj05(S^I0Ui4LQ$TE6}d0eSyLE[K:!h|a8={Küa^0ݳת4o7 ^Atyd5⎆IQdHvNvPc?oIvɑ\$IF'dI ISdI{L}{a$''ghI5L$$J&̨dub--l e=Br,8*d~Rp!㽡` eBypavY.ؐq= ' )̜Nm8Jl É1W!SXIv9)̼e}b#-lOrSXt(8ڐީ,2K5W!P0UT:)8O1U YH4ne[Uȶ/8UOit}U*du+Cmaz/E!TjrSLy'-Itne_pUȶphd@|8 mIA'q v{Bٹ8Wd7LGE-3| kg}-7Ⱦ BGNٹHQ ڡU|F!+g>hBv{:jwtNY*\i|Ѧt㳍Jn!e:;9:'2pv.W8( t-q6}휽s vs!t{ FÐ [G=.[g:lwpI*d[8; 6$3ib&Ð2' Z!L %̜PN3Q(J9\B] LG/L &}g([ywL }nDR.Nv `/G]tdw$fL:;goC99gy}g)Ӿ3 JYllTvnu`V*gg6SUɾ/(TUn+SV98G=La%V}7eJ++Vi{n3\:3Jl{%}WÞyWvi̇rr|>y|Xgt&Y [iOL&rv.N{*`=0ꋑho4ՉuB͉mB݉}Be2=~w.kYƂɆ[)45ǑjDn}bcC{;=۟8ؐɆLŭ\O R!'&t\Il%F*q+;έOBrPέ 6R!~fW ǂvJK[\,dQ*d[Ka;׾ePu&W!<]^*d[8 gD5D| .w ݒݳDa{-j5jP騽y-͒ݡ<+'dP9Tƒݢ2,_3/cTg*3_8[lH爓s!3 \ f꯲r[ZekC؝]vR awv-28 )ۤ9SӜgݢ)hCxv- ruSnQs gju{`Cxv''GEP6}8S>i2f*}3lLoX'=sr a)Iv:Lr6SOݣd6;$GiLN&'GK %L5KVNvsj5S)9H<ԚG,5%&"iXjG3lTJGNvlY7SޑݤX#KfR3S{E&LQENvX%3yLG)J!>fJuvN{R!Gd7f3U BAVNvZ'% 3SK@R!dwK3Ki]eO13dߙtY*}].gib&3fɾ3/C2ŕ)f;3sq5SV(ltf)g'jf3̙ T8y`J^ls(NY9Yl awwfrv3_N{M)Y9kg#¾vNY=Sי)Lag3SW.^;]8[q9:'2pv.lH`at¼W6I>Cr!a:3JGiN oIyB99g`wug LKHs*XT/Le읃 /\9;B_0ɖɆ̼e嬝O/%}[R!䜝T}љ ]VHwR!䜝T}љt^VHwR!䜝T}љ_VHwR!䜝T}љB@Vκ  59{ 99g y: ‚}B99gdwug )!kglB98G$sv.ՆQ>dtPY(F*휽s 2''j$ u1W![G!Y9kP7+hOJU'rtNN{RPG]A*MG Ŗ"lӞ(,[r(BGΥ}RY(LW[YY;q+;2ptNgqtuцDTndP-:J R!t\ 6̓I!{r+g:;g/-cI*] `Cht6ULhv$낍T9\Бci_\ ڎRpMG97r:2s C3G甫:tw):mRŒ`:jZjYKKήXJ_rpmR s)hϱdpƂۥAJe*hF+3l͋z  wsBrBogR[u^+Eܺ`#-l vaO%o;==9\rBggUN*Cu`kCѮ`9*\ R!P0'1=R{̭r MV*d[ ړK9r- .CS1W!۳ [=d_Xl gRinW ǂ١*mZ0XK=/ [ٞIl CA;;BKBx2c$+)V{eMA,{mPp,Dp-ׂ!W!X0C(tV'a9UpٜJl }YTf's,%u9Hl CB`*PϭNO1|ٜJneWUȶp(8UOmѹ} w=>| I*$W1Cn \*8s8>|;hߋ#w)Jne81JD25-D \l \l }Y *dz\V!wײ ߍeN>*kNOr<۞ِ 'N6dzw>q!{ َ|=eqQ-}Yp\:*d[88*d[*d[.B[~8CD{anwB8xeqRt*m|⒫mz xb Iǝe*g-9L9*pwq -t=X>qR!;=Ys  :8SVǙ/e[ 2[ٹ8WmapƂI!|L[9kl]A*'s Υ*BGfFgCQVkX>a+h;O"I*d[8;Tȶ08c$>,̭ R!thfrBf1W!tl\rBfgUs?Y!|ޗ8\ۙ}A*Μ R!Ź R!X0Ӻ`?ª`]ОONή=Q wKBP0Jl {MVrS7sBg'm\p)h|&3g5=YnYnS ver,8*d[8\ gd(s=+N>ʭ/ 6g]; ǂ-9\r³R4e^>:qUn}}3,r³St(hwxJN\l kA;*c(OLR!Or[H䓦ܦ`+Һ]>W!wǂ GmzbUH|7ne^Odub--l eSٟ8ؐqt|~;B&r>q '\l cTv#UAQLns}(Fv'R![9s-Ns-\ eS\4UϻOOb>=ɭOllHxo{bgCA*dp+'ɭO\Br-r-'\V{Ⱦ>Uȶ=Uȶ?qUȶpqUȶp=1*d[OL a!qr}a}bma{bmaA*dp+'ɭO\Br=1H|D{V>+k>˭UIѶB8Ne-Ns-NsW.'R![.j'|l~wS>,[UOѦB]Bsp 8؝s gt}v,TVHqw5}F>̝T:|)Q>֔Cq v{Pl|R:.38ӾqO(eUоe1eIB9*ܝ .N*HX>y|(C-IБsU;\cQg,h;vM|x(c0aan[v,F>$Tȶp*8;mZ08Tȶ0d%}a]qR!®`Bc}s{kA;As=#z*X;d[U98\ۙsE*d[ (-LoM| kGr.*}  NR!¹Rp!h(s= N>H˭/ 6[>v\ld'm\p)h> `Ul8 ,{Ⱦ.ɮ`cAY . Y THt+{gXuF*d[؞*,s(8JnTpUOYzb!q4L{? 6~mNlB]`C{ǂTV\  Od]>4JW:UO]f{}!OBSBx .; 冂Q*$|spV )lOO1A*d[88*d[8\*43hb>M壙d4Z*d[؜*d[*X>|;hb>r] VTH:Ӕ*w'6  y/:U{ѩBx3ٞfrÉT7w;urrdwb/һÉTVN'R![Jne81JD2.[؅?V'R![\'|۝h> N N'Orq§m\p|Me>+[UǩMB]BCBI*dr+%_z=N17|B=+ϭs-lNlsWv'R![9\ԞTNٹHggtS&> ͭC T;X>M|)ǂT\g7ez/o𱦬 6N{fc9HasU:CA{>Q>̭ǔU;UǔO"sg"-\` :_$!Džs<*į.ېCYE'2=~w.mrU*dWUȶ0^4I|忬 R!u\l ۋvR!]`_V!ᢣTȘ<8Uȶpņ,k +u|jUțs/>uݦB l/+e-?末7+\UȶpP{q͘7+L*͟z\Uț +*e{>_!W!oV8m̏\B|BJs-sC_rf +*1[=>  )m4rB*eW5_֐7+Ʋ )mU[/>W!>־TH|k7+*ʃCY6U_9]W!s-\>W!J?־N*3THWfG!vV yuBެ<ؔU/ؖU/ؕUHi\6UHi\uWU_\W!~z] +uW*ĭ\wVVU_Y_W!~es]uWvU__W!sRhkfB8/KʃCYu~ꜫ7+\UȶpP{دq͘7+L*χu׬sfB 5q^*W*xBӇN\rfBp]l NHb*F 6^PB|[\Yyp(rB*`\\sfPV! +us c 0ՏlB|[\Yy+F +*d[8~B*WWqZJ_ R!!_֘7+ߚrfn!卪sRڨ>W!~es]uWvUȶ\l Uȶp\LUZ,U`C; L6$=^@WqSW}=pSW}=pS}B8|B8|B8?|B8?|c2B*d[~Bp] )mUHa#X=t㪬B~ǺB~`7+ iItK T~B8ɜ:*d[88*į\N\B|DȲBXȵe=$[YH4sX/mpQ*dW8h68,Uȶph `kW{Ⱦh-R+lUG]Bvť{eDz NB|˹  R!!_cW!*?μj꺳TȜE*dWuUțCYяBJsVr%leu]l Uȶ\FX*Jϵb0(2+u :K_]B|[\Y֐7+sf[SBެ|'Cޭ|k7+Z*ʷ6 ymBެ|k7+*ʷ ycBެ|딫7+u 󕿺H,_]B|%+ ?מO+/pįCUȶ\uWUHi\mܗU/8U/sfӹ ^@WqU]٪BE&_y-maw^*W*d[8^t  .j'\V6U_/jGەe'\Dvp y\ԏlB| vېq!W!~x]LϜB|B όR!1_c:W!,䪩ÞέsfB p^*W*į^k_;\t7+\Uȶ0|Bx\6I|;(nչ ֟^3mB|[\Yy/rBN6dz|,2+ߺ*ʃkY Uƾ躕TH|k7+6eWU_]W!s c; B]w osfBJ*Q8W!~eBsB.=>׾sJ_B|[\Y!W!oV*䗍~*䗍~*rBJ*Q8W! )mU[ɵoCuWU_\W!~e{]uWU_9\W!sRh:W!sRh9W!srq(_61U/*䗍\txpyUBެZ*Wj#+/i'\ < q Y?'SReJOȃ{Ⱦh-REa5ݹ =uUȶp$2+UWѵBQ*$+Lk/*xlUV!TH=W! +#*d[8_t Y?*#Ԙ7+L!\Uwn=bP6W!~ewQ{r۹CB嚶s\ua|jUț?sa|U.*gs-/H4ʃmYEa_ y\l R!sRV!~z]p_6J|TV!n%WS]rR!u`SV!~e{]Zl/+eWUR!sKBެq=W! 5r$V.zRܨ:W!CkJʃ]Y6U_9\W!>֎ٸ9W!oV\*zBs c pMJW\Yy.B* 0]w osfB~ǹBJ-*į\ma\Flh\uJ*Wj-R+*ʃmYя]Y6UHi\uWNU_9_W!s-\?W! ^_pW*7*7*7*7*7*7*S7*S7*S7*S7*S7!*d[>W! UqeEBuRڨ>W!srq[V!lcWV!ltUțorf寎R!c _ Y\s- R!ѭL״kp2:W!6e-lOrW=5*#9W!墫TȚ1BXńVr *6W!THq8W!N +.6dyZV!~e==ri^TV!,*s+؅syV*W؝R!C\p^v ?.*#A*$+*d[gUYl $;U_]W!ΗB| Nj{:wU_^W!旍R!1_y0U[UVC꺵TH<ؔU_^W!.KʃCYukᲳTȜ_!W!oV*tB .^ߨ^k_\Mu$j{ȻorfBJ5*Q{B +*d[8|B*W7*WW1W}}s" ]7J|&M.cq /YITʷֹ yMBެ|k7+*ʷ yCBެ|똫7+:*ʷι yW%_TȚ Bb|2Wڋ ."\{>¯ToToCUȶ\uWUHi\6UsY%W!oV*+ *CTV!,ʲUOlB|õe ' +g9_rB8N Ƌ&\\{*d[\ i?v*!W!~xQ{zsYl R!kpB8OM q+\{zjuYEUue jX.+;UȶpP{r[٥B#kƲ +5ek [*įS\YcwB8/;H ʃcYl =y*s- R!1_y0U[%XC꺵TH<ؔU_^W!.KʃCYukᲳTȜxP1KC;  ~!uBެB~CsraC*/6p!\C{1~߇7tʿo\ bй+>ľsEW} == CxsŃC*}Ҟ }HWV!l_Kʫ}Z_rBJ*l㥬B~7+5H|%; ?yH\V%Ӎ euBެ=Q+_Fݹ }Bެ=;_Fӹ sBެB8}c9dB>(*ʫ*7CurA=ʫ*s7*&_yrH{]\}P7+Nx=d(_6U{\$^W!<;7uݭC\Yy!uY!uCUCxƿo*W~2H ʫ~2Jʫ~2*ʫ~2*ʫ^Y*䗍B~ eF\H,_6CRBެ!8=ʫ~R*ʫ~RKʫ}_0TsŃC*}2U/}xB6*䗍ι yW%_yI^c~Bx<$~BJsr1?=䷍V y-_<\urqWV!l^*W^U!ɾ2^W!gOeFjOXW!*zB.l|0H|U )mUȅRikSsrq]V!lo~e9B>UțWCUCYNx2 ՞Iׯ<YUȅj.Wsraڳ=?*CRY~+TeFԟOH4ʫ*ooW~*ʫC*;W^*䗍tоy_yȚ7+zUȅU_q?|pyC eC*4/}粿N*W^>W!oV^zPV!l! !ӹ !u,U^W!xx]|! AC9K!<\\\\C e>UțW> W;_0 A?NR!SvT2R!c~Bp]6*lTV!lNnb~}^!_ԟB6n*䗍Itʫ*s7C*l㱬B~譓TȔB8}c9dB>hUțGnx]m*䗍~ް4_YsfU¹CsraポTHjB>hUțWU'o2U/=Qsϡ<YUȅj+C¹ [ٞ[)}yH:W!6> M_y=*_6<\\}P7+ZitоepcҗU/}2|B>}(2+z=d*_6xP97o>$I|Utm[ȯ}R]W!<<\|W!~e}]mܔU/ i󕗴'jnyH )m4 8*K3xAϾ-B|UCsragE*dW^W!_Ď -W! 8dB>x! ߇LeF2 ekBެ 򕗴+M~a]>r)nT e6R!M-;}e}BJ*l㡬B~譣TȘB8}c9dB>hUțGnyH\m*䗍ޚBRY7=t㪬B~譵THj|B6n*䗍~՞ccWC*8H ʫ!*ꃦ\YyUp"!KYNxUȅj܎+Cҹ i'!UYyH7+OItʫ*!e|!cBެj9dB>xAÍѾ%W!oV^^W!<›7~Bb!\\rwW=h߼W~RKʫ^i*䗍>=W!6}Bxp7}}>n_}(2+yȔ7+z=d._6\\\\p]|! ACu.pӸK!-mWL/|2_W!!cY!*hw1W^Y*䗍>Y?W!_ WRU/}HsB.l|=\|; fCz>_yC\Yy!cY߇L*ƟU:Y>W!_<~Bxp{7!?7!=78z|?R?i__~Bxp9\| A *2U/!SBެ߇6d~|?wr!wr!wr!wr!wr!wr!wr!wr!wZ8yݢz^NkXSu}]%>u3q+z_Կ~w_WNCU4Ꝇ/O\uY4V+|lhnX3_ΟNGqey u49M7MSxzݧx^׭riߣ&aiz4`__Ufdin^:>ͯN3-_$A{l~{ygGxpk|׳TXK _xV}uSi17}v6_ Y#Ky5 u]$dtBs++VVеfyڌ&ykg}ݶ)_w}:kVsWzݒu_d%LVsHH\'By33-5?8ƒ`W;_xJ͎H+S$^V}:᫣P-Ԇj6;d=OqhjMH3Z6zݕ)!1n.zÙKbח.jQ4{aa<;8694+5-Z1J -}}xﵩA&Ĺ7lonDx/N /4g7k"qN#bГ8Q'Ni4N<;ĝ1%^?L_p6O ['+9'^3L,8˧?f~2"k'IRR}91KLR2>>֐-וe3֨:83.b!_L/+b_|@zR_势\_ 5/ݬ͚?jkr_͚ MH_n r6f!}nhA_n-G[}oiAK Zr>t-};r_tm-iA_=ӂ6hAO zZӂg`?-h@ Z0Ђ 쾹?`-h@ Z0=Gd%#-iHKF{؏`#-iH FZ0UЂL`-h1ٖLd%-hDK&Z2Ӓٶd%3-iLKfZ2Ӓ̶%3-iLKfZ2Ӓ,d-YhBKZВ,d%mBKZВ,d%+-YmKVZҒd%GҢhE+-ZiQGl (Т@- (В`[hI%ZhIN*tTEEZiQEE{>Ō*FD#"I ;ZȠSӼ]:RHbT1RQ!4SňbT1b6f4*FR`bS2ZȈbDU UbxU1^IZ:ѾfU $pbU1ܪFiHs~U *_Xb,&PcñX5َelV16U$-exVMM\jc˘bV1f[ح2zGb W1t;oEvB2I۱ *xˊeqPbY1Ԕ4?bFU Cϊ =+' Am5CњhPTf\ZS='2?.bpF4I8U|ƫAZn>|Gf*'Bh>ؚqgwmfp[34qߥokƷ׫ a7 z%g[3qa[34Ap X6 kÒ7 &ep\380R)KvA hGBw FΒ`\۲af]3Ci_Ō%HZk;5֌kF֒`d- vA֌%Hͨf]3vKv[ %0Kvdw찼Y3, %G %0];=2fTE%p]a.a^RjXf,/0׌%^al/rta/a_O`cP3cvwLj&a 0!ab  dw ԋ=Q (n1a{fP3{,vwEK:lf!٭YElBNv˖kf5مd]HC fQ(نoԤeduH;*`!''CASi9LI$DvN;fc"G'TE.6l78٭&;e"k'E598-0Ҏkj3bJ#ٞM2֦ϫ8Ɔ@e?/T 0Pҟs{/{˜X`۝GcCl10#ƈəq!Gw,0q}dx0 Ɇc\$6u<\iހAS`,K dvz ~TxȦd%c#*8YMS#J&'H+0Ғd+0lRF\q=윽p,pB$ºΌjoljved5;| $̰Mņ 78[y0 dN9:YhJc<8Y]w?!dduI~MOR!!PNʘ00duiŌBPɺ3 A!FeNVێFCTil UCUɺO뫌R Ue NVnl ]CViw{!dY*u?\_eH+yp7-0wݞqn.NV!nn8Wޝs J!=WG{Áqrv.U!ߓu}2: <rtNN{(`DŹOn!؜ؖ*dNJOO Iu2~w oT\mz{+Q(mO 6$۝ېxb!|`C{NJT'R!k\N\BF*5uy؉?'{5THrA*dp㉓T'R!k\N\BF\uy؉/'YiwO~B4huv?Wz?'Ks)X;T>19spګ IywN۱(n}ȦP)sR!arf BNλT5; r}}BuYTuəB99#UPǸ8#Ҩl3HQ}XJR98RQw\B(/T8AjG)Uv{:09s 'yw7 W8W)vQ-mi߀i;걥љBábE[9W\BClu;KabvڝoGWNR!tRq}u;Jpi R!tXaNQ.98G};Rq뫔KirG9YTWiOԜT>+.R!{"iԨ;jӥ 'FgrRЁci\T)l˦b+ ݥwo=qv.WyP%Mie+ڎQJ/A*d8:w:pq{HO]6[g!j+F*f OSŻT|{\K ץ-UzU읶c9mRNsh;qOetqR!L[8[yKСb C GT:pv. ;IT6GQ*v cTzp8:mRv,IKQv,5FWN۱e88G2^*ޝ o.W'{ :B* S\zp8*.KBvRMB4TBX1Is8Jw,O\*()V=UULsB9*^/+.R!U갥SfP^=rduQ*ecjQDaHE8RT~epvې3m _ed(Ά:T auY-rv.6MK3R#MjQȔəmբP)نY-jqHmQvNVL6Mv88Y=*nCXGʁʟdIsʈT"HEOvNV~P0R$GqNR!#E8QgepvN{fo>LBXɦf)fWuiu;T~Ⱥ3 8Ud4*gⴻ=uwRFrtNN3 a#CW&;M ee N*19s!G;g23qUG^ >Cr!aywņ*c8͡ 1XN|*He aspR!9;̎4Ġ:13: /\9:'B_0Ȗsq6`-g}H<1oB9:'pv.U!elA*>8љB9:'pv.U! elA*8љB9:'pv.U!elA*8љB9:'pv.U!dl+>O($$ rpN'9;KQdlO(0$ wR'98GT'HA6V!T!dWw>:JON$ORq}/ #j K'əT>qtNλ^SH4* ilIQљY*>)nC(JrqG( BGLR!tT@M$Q's)UQHD6V!Md+ڃEDDf%*:J9KQUJbK"sڃŖD49Tu8: H&Qs}1"L2#g R!վbt&CQ*dzwE*dJF6NTpdpv^**LlFEtrޥBŹj+SɔyJ[g {gtڝ2PS:8G:vgF$W) F!]BL ɱ$BjC@jIG118;g_:09sE.)W[LsBcI*dx8W[z^EMl*֡.}B*b 'NJvPsBKؖ*RUBbhҡ(O*+ڭC_.#ݦ:bWNaBkRu w}|R6 vSK)[g{H9PΈəmHfޥB۫s}hu#E" ֝H=Mf :NNҘ\lެR ӜG[s(ºlN!JivBl3H>,$nOh $G䴻=e2dݩRm%gb#[gpvR!!}@Ef)W;gBP/%#z KəT}@Bޝ J*8BHaAviFj>6 GTp^6V! eQ*>89Jk8 [6iGF޲wFg!#p98G=e0"ٹ8m0B:m0lO OƏ \OΏ elOޏ eN' GrpN'Grv.N'z{Zl)}L'jFťL4Q( M&D9h 4Q(LTz$Kh?1QޙLTr&*8%h3ٯaLTh$KHQf3Qlf3Q|(L[&*i;U&'uzd )LHRD d1Qlc1Q(qLT3&%w1UjCRL&& DUb1Q(qv.Wy0h.mmBٗ*=LsBñˈ[KǮl˦XF28cLm2"s8ِ9W\lݮe^jwwFꥡbgC۫3VL6$^ΡhC۫^q!ե˦bjؗ*>LsB9*.+.R!{o;69&P 'cESXSP(*ݞv kBq T>1:S,O*nOB+NS{ZxP(m+}b+F}b+ګGJr8*/KBwR)mrLl+DDcB BNR!ĹRz{O6GP 'cE{ʠ$sšT!l©]*d8W\*ڭ3n6GP 'cEu\q(U[px 'vE*zMrLl+vP&}XJa8HNJSEu(PʹR}^ vPmPJa_1JӉT!l±$OW+ڭCt}/ݻEMl*֡2,{}b*ڭCYRvC[.R0wiSюwP+U>+ړvOBcũ=;/U0UR!5R '7 7:Rq-U6qHiSѼgT>?1*$ޛ+R!k+N ~wؐu#؜ؖ*MW؝ېjJO aNR!w8W\Jo)5ؙ>m \cW/U09cdIGs?G.R!{"ofni53ffnd5c3`fnjrWeM䆉 wMfHV" a]Ygn|i717d 'V Y~}:Y]nb3E.~L1_Nλs a*y8ɋzL]e aspڶZ>SBٹ8WBP )|B[d=Q)`99nOz&-'My69T~Lx",'iw{*3_JqWڣ=E\ a3;prޝE9SŻ JŹj~JP3*:љB9VlEICŹ:FilBCvBDc!g:p}RlB=48;=(Q'YT1KQsrޥBsqBQe~R:TuWNȲPds,UvxwE*dJE6N#sR!ta HfB#w:pj{Pm BXlw*:rp ι(,y}lJ>28vg Pb GTlԈBr#l*NQ;Z(Bf;$99Y*d8W-xPh3Tױ N2VLR!tPqtk6 Q ٹHW)LɦZ%hTV2UR!t\蜜]LKΥ}SZfɦb+Zww1%/=SZ(}ɩ]*,\5JaqBIwF:0;RБSŻ-JW)-4qTX;cB*ŷ:[9K֕ۏ{JuRtWљlHCцL*ޝT>qA:Jqn'mP[)I֝zJm:J]mRG[)IjZmD&Y\Jb+%ꗌ6ťR,)UBj[+լ*Jj`%YR U-LTV S+BJ!JV M+%bSSZ%Is]Ԍ$Mhq2t,>Tj0*]hte2]}:%sy2W봾@re)nπqe(Y|Ƅ%c1dIVaj?2[ƭ $ϰM(óadI6y$K Vʠie$Y|EҾ IVqxGGV9+㚕d5**+Cj04lC2$b(B }91m`Cծb_x{oB{NJT>^qؐOҰ ۊA*dU+FoT!l±$OW+ڭ,WJQV R!Įb_n6^MsBcI*dx8W[]n J֨V['۫]žT!lT1KcEut{~,O\*^m* av'ڭWcTNPq 'N'K)\*R!LTl!=0TB}XW!l\q ' a}Odm ONK&+LTHvCűT!l۫KŵT!LL*61T>*MW+R!k+N aU+7`Zn#Tl*MWUBz+Rsš}ީ]*d8*dt?phn'}bwb_6aa:c|{u8q 'N'K&,\Na89G1=1*MXhc7UR!5R a.'M<?'69&'R)&b ɮq8*d8UURnib#;Ƕb(U>;/Ujh7=;h7}:x ''. '=Bf6ma{sK39^ l3Oλs cW:rit윽0;(ºޝ8da?:~kdLR!T~8;6&QMBuggC:&LlC۫srޥB۫s}[35p+FB5m} ʍiN 7@nx윽 O!3;mp^/!gp}Gdpv^*>8LBywR!*|#b Kə+>Pz}B.WK'28; UpiBwCa[Nλ Zu*5֨jҲug'BFgrfptNyrqCeX.d pCW}BQ[I*:s OgRq!t%U8[fk(6Je_1JQ98G:^qv.6*e4 ɆdiBA*d8:'iJ ʁr}/*~ug'OљD(W::':pv.ZCM6ζn&;gR!tTavNP*;g:p}/)i (H5dpv 3U;5ZSBٹ8W&R摍uڣ=9{g '&gvNQBs}΋G: 'L Xl?rHJ=Q#j( v^*+L촧QjGrtNλT>qv.εti)ɦb 6$0;TH7;hC۫^q!3^ S-LJ[gpv6;cdCHVr8JЁw\lr{u}jV9TZZIЁљJBGGdCHJ`s 'R+mBi48;goC۫љ*fBR?+T>+.R!{G:ɦ&Ci;⛌Tv,ŷz[蜤Bw\q ;IQN6G9*vN۱dtcJG$O;犋TޑN*{)=BgW:0VLN۱+ڎ (ιXʂm\KEP69&P 'X1IСCQ*-;Rб{)'xP[mKQv,5G+N۱$WKAR.X qBeKh.[ `Cm SŻ )d.ȋfKu)H -0[ -ʖE [AdK(Y|ʋ-ev ŖbKP [ʆ- [ʃ-eASll-edTZ*yŧhREqNZo%jb#uZK]MOZJf-2IS"k퇝ZJc-%Rdz*J^--Sj)`I{FJZ T%TKAR5v=RK-IԒԊ$OYHRj)u}1P Tv~C@E'P=Fg!#N;6df]uiŁJ~"Y %2a!@}$P`?(yf}G!!@I#PT- jP@U"P؂C(4 ՠToHVڀdui5@- 0여|iW'쫁 y0baKVdu'a#oj0Ȗ`ׁt`-Y ˒ձ|ˁqq`<,Y Ү]0C`Wat3!j`4*YFՉvuuFdu@J:q_ dJVGcOF'G:$M:)-0L $La8yFZaP%0&yw5#j1`?7wZ a~T!dȻT!d}HVkP$0jvu$!})0D MCP%'CP%0DCmȐ%0dC9֑qL`"Yi}n.A8&}%0 k}V%0 o9;Y=8'0 sddx'0 w$xGZv=c#gq^_e$Y=A>CwzIV I.NVo@MAS`$Y=KҾ<MrpzH*0x(=cD%[}bUH{oBkRߝ*m޹"O\G˺m`C^*dOL a'6d:x ''. 7aE٥1A*d؝ؗ*MX5U6$;TBF8U*d8W\*dߔN u&*!THp]žT!X1Up8nb&r:.rwsťT!ezP)mONiW ]cJO' aV*M[8h71*.;Ǧb[}b8+Uj6qa>nb>gR:VB&x?q ]Rq-UmJʁuC]ݫ&XN't CQ*dtӉwk+. 'Ku#Tl*^{X)֖nx=Pg5.'R!k#e庍1W*~89Y*MwJ\6.sR!tarf B_Nλs /W)F!epv^*8LBywR!*uz#z KəT}q89Y*8\_/yP9{0UN' 'i;w*7t!ٕ*:R!蜜wpj*qA}m 2VLR!tTu8;҂{(dl{,d_1JQ98G:ݺso{HϽ  &gv6d`*ޥB۫s};uggC۫3:Tu8T è˹"wRME3|eKB&gvR!99ن̷W*&6vg-{gt&}bviw6jT>q}2lJ28vg"SəKBΩ=Q)ʥ*갲: W;gR!ΡT!tWb=U ޭfi N.P){gh${jr8JЁwbʆr}`}S)P  3*<cE{xHS+T>q}lJh2THLFghzJ_s  gR#RXi9dpvmR7)&g 'α$BGΥT!{"i; ήXo=uR!9VB¹"wlJt2T윶cLmRɓsh;^OEtv.R!Td#cb ;:0VLR!thKBޝsB _q8VlOtؐ^ie[1*^/읱T!za8HۅSŻT>qT=Hqhw{*2Tv,cTzާ)JB)ưJQ7R̍mH6RYzl+Y ꮒՠODꬑ:k*Y JҾw(JVd5F[F#UHS:=j UH!S0-`F e@)Y jҮIjPshkc(Y ʈձeH-1R. #AjP 3~2`'Y *}ձT"HEO]tա('Yo~-R$CM:ٮH=-RG%3iWdu(촫CK:T$Cu+VbIVduCEUU %YjOҮ'99YjJҮθRCԎ$Cy(_oԈ" hAO#Y*:ձH%'NTn"Ej_*Th"HFF'ec*Rt["EjQGCQ%R?U%҈Q!sZ<*!'EB-rqZ$ !Y- wKP 2;Y-j^S#ZWUEuz 諒c8/b.G:2B%[ڣ=#;1Ȗ39<&ՉA20qtNxwR! WJVkCVzRet&:NVpqz y&cb(;g/:21K1N)i0M6NV{,ñ+1ҒٯJ#-99YMYɎ#.&e%cy;YMdb8d5I*u=dUdbbx&[gpõ0M&'H-[b&Ygmrv.6uf#1]b'{gtM̘NcO.NV]O%zu Čc?ɦe'uf (g SZ_eL V! %̘PF Eɺ3FT|:3`Q e':F'RMg+I99Y]>]UյH2L9{g!avQ*u>duiŌBPɺ3 A!FeNVێFCĨTil UCUɺO뫌RPUdDkb*3;nP61;CX9K ie#n'sN3M͗j3Mn~b+ٹH뎌{%}Wݞqpb+{gtڣ=8XN{Ɏa9;*:T}oi f9:'=0N "3~59-U>1ؕ*d؟Kn*dXW!wu2~wڐV u1S(mO 6$۝ېxb!|`C{NJT'R!k\N\BF*5uy؉?'{5THrA*dp㉓T'R!k\N\BF\uy؉/'YiwO~B4huv?Wz?'Ks)X;T>19spګ ¤;gX [_#)=B(T윽T}r9H'{,UL!SRДOnfIQљY**^=RT?\KJq BGc)fJrp ٹHQ^JqڃL)Uv{:09s 'yw7 W8W)f[_K[gp7 EL=4:Tv8T\L6SsE*d0x[8FQ7S̕]^*;LlT|3_9UKaKU3vJp\:TV;levQ*d89ιT!t*R{)'P 39is 'ޝsE*d@]8u6]9{}bt&g.Ux8V]LA[ŹJ0¶l*]9{ Pə+#L5trޥBs#*l*N{x.;g_v,%L)4;}*ޥBŹ!%wTl۫mHdbT>qtNR!s-=B(\6ζT!taWwڎ,3WK9YΩXŹJ0oilrL ήb/BY*-SBٹ*dX'Sٔ!Feؕ*>SBáKR+N۱$^jv,5FV N۱e_1:mRkmx{uxw6dT\_(Pʦbg@ ؗ*>LsBñTzpv. {IS6GP c$OΡ(O*ޝT>q~t*g[WeWw2U̥ GTzpHWÖ69&CES(֖Ɗvx+siw{rx/U^T\BHW6[yCŮ=Sљ*ݞ* g}\K#Tl+ڃdUKV(L\zp8ID Z B-B¾b 'RcũT!l¹"O\KaԞ2(t˶b(U[BbhUjxTў2˹R}+869&CE{B.T!l㩚KS%t$qI_Q Tu-Y=[(p ْաf-P5ijtAKVrdu(/I@9Y:T({d muFa쑊Hx+ކLhCF&Nxwθ8ͅHU6NVRlsjQJ9Vբ:*gbCdT?GuHuSZ2erf&;d(Tʻs!lCVl|(;'GP&&;Ay!#ʟdIsTF*zsz#bd(Q*u'Y=lrGzl%HLzdv7 %099Y=\Ҿ)yTFYqzW>GV#U+QI*u2;YM*;dHyGޝ&iX*7#lA!aF.2:YM)V&uiKF#c^_"yP)u;ARjϳT)Frv* q=(QP)$H6-BX2v3K֝al]ΌeﴛȈ\ (:ޝ.gi71Ad w qČsGrtNN{f**~htd)s5͑QȀRf :NN65LiŌ6y}QlQ윽3J;P9:푍SF\vRq ºNQ=Cב! aG#CW81zvb(+[gpvR!Ոə 8:'9ې 2ԕ8;g"2ّP01LɆ  /dٹ8WB_0򖍳u>a$>1oB9:'pv.U!elA*>8љB9:'pv.U! elA*8љB9:'pv.U!elA*8љB9:'pv.U!dl+>0Q33:mPH($9:mPP($ٹ8_ g}Ba {g,U}rs OٹH}dlBBv{U Չꄼ;gӸjlBBgBA*d8:'i)mL4* ilIQљY*>)nC(JrqD=dR:Tu;3IQst5u:KBB%qn";g_(LOdvN{P2QG);g:p}J}Rl(DeRY*:SE3Qs}n0w*3q ^+Fg!9TB۫w\q! WȦ#:JLTv8:wBR)]u(WV R!Įb_TTJsESٓcũT!~\J `i#cb[1TlHw{KV(ΡT!~T.O+.A)=6GP 'cE{Pp)sšT!ıTnr*d*N.mJuCE,T>1Uu¦+Oo@ ^q.U>q=B(t6'r2IBؔD{.'}ĹT!ĥZ>_ڜh71|NB:W 'BشUNJvs~VˉTmҦ9&'Riu&.L'M D{.GT.rwT׷0yqĶT!pbW}b_1UHnc]L߽U|ݥB͡P0s稴=1ؐp{owboC{Ɇ{ nOlt{ن̷.*d/*rG7u~QB/Nͥ '' 'N'KO/H,q]n>Dp?W!qqrSڎlI{gt&=rS9Iٹ*d8vl*ڝۙ3/eW 3;mK9:']*d8;q8:'}\B{'yۊIљBPўϷp^'^{ oᾞl*N{U;UKB&g.U]8VBs 'o] Wjv[rJ^+R{sA*d8V*m\q)UȾH+mrLl+ ncB O+N+. ٷ{`*M\5W[/vs˥^q ]r*6r7)=B퉡T!Įb_W!lBU aN'ڣ=fJ7;+uJ?D{(ঐOR!5THvÉTR!w8H,q]n[x؉?6'R!k  7J~ tOqV:hN'm a=BUWSCB݉}BTߘOB88ILe~\[nWۖ*dNJTHt S98G$v/ 'r/q*N۱])sA*;*ޝ=ϕۛr}/7q*O?!梭TH[6U>h/җ/ƺ 'f\68U>qɆLkK*ԵT!o=B͇ݾmo*7vUeTH,_LuOjwv]s,UțsOvͥT!o\Uß9]-UțC] =q(ƷRi1}gNR!S⽮Bj3*d|BE?)Uțsuc5[>3JIJTW! uO?W!.{ oKťBj3*5{Uoo>> Ʒ yX7/ ͔Uo} 'Nޥk|N.gR!k߮*M aCܮٖ*M㋡B|cw]l e㋩Bj3sO>ԾwvͩT!o_Uo}3\#N|kSW!sO PBT!o_LuuO>W!>־v{B48UHm\ as77>3H]B4UHmxB|cBsO>W!+?־w{T\6u)UțƷ yhlx{H}\fjUo Uo쮫_W! 'U>1B*d}B*~ن̷\lrՆ5<@Puk~Y*d.\UȾg3r/!lU{詡T!oU{ꩱT!oL*=TP68ޥBes]~*)*P7?v*d_4JIJTW!1_W!BƲũB=OU*M a"F]<un[7/ u{Q*$/ 'j;q#Ǐ>׾yF麭TH[65*M]]fUoU>1}BxW~yuGlI*d*z/Uț e ʹs\@T]ݦBETH(_*d_4JIJTW!|A*d(_*d8} 8ZݹB|rQ{U?YvbCd/!olUH{ sO?4ڐxf*UoUpQ*d,_*7ޯTR6&unR綥 yb]W!.Bbbߘk;q\^*䡠w?6*d~Bx\ Ʒ ybߘ}b BƲSB4x8_W!s>47 ] iƷ yb]W!Bsc;H e[Ri|qLsRi>W!qB*h eݹ a<5*7#*d8^t *=ԥT!o\=BxܦT!cyPܮT!cyTߘ/jXi;w,UovSRiq9W!W塲>q2\ۋP6Uo/jw{:7*M\R!cTW!~]_v Y׺ q־#y#NզT!o_l*7*d}Bx}} uTH*ߚKšB~DZBj3M*7ޯ} ] ߝjjy$뺍THS6j+Җo ybWW!c_W! ͔UoUo8^W!sOB*7*dk3n_ߨߨߨߨߨߨߨߨߨߨX?W!sO\>W!sx{H\W!sR) e ecB45*Mf\6^r!k2޾V!R.R!k\i.disBxU uONKEnc\:*d8].r/U;YյBÄ6_lU;۩]BETH,L*=ԡT!qdC*7GͫUyF!v al'ҕ?*=I*$?sžzQ*d,U{eglq9W!P{Udv u|$ 'jw{:w*Uo_W!]BŵB\#OY]ᩪR!mb]W!.Bbbߘ BƲũBj3U>qPᱣR!khI!Ecs] :+Uț '۹ 2oK\uO?Ծy2身TZ6^q\FX.iR!kNzy֦T!o_l*6S8W!s*7*d>W! ]]fk<ci]BWWlMc/H4e[Ri|k(UțƷv y־T!oKT!oߚKCB4u,Uț_BWR!WglE*d)/i'zJPa!ϵ*JpFp2\UoUoLUHm|Bj3 *l汮B~ũT!o.r/ULJk]0(6GH؞BBcw}cXW!tb.UoNBƲ\s:*d\t YƧ<\Gm*d.Ite ag;5*7c,*d8].r/U{kB\#Oݝk1~cy@n}vYS]jXeUo\i[ xы^*䗙>?W!8JIJ!\\zH\|A*d(z=d_f>dB>xыwr}!sB4^zRW!}z]|j}߇3  ~!mB4^zH_f\\ E!sŋNpƿbsʿbʿ'tʿ'tʿ't:ʿ'thʿy9gCx +~BxyHB>x!ݹ 0߇uL}!Q*$W{i|e?W!qBj3*l橮B~{B4,2g@f+ːsK!37uL/ yxI{[" >W!sracB4^,C*6pB.X7WUoܯ/K "@#]U1CsraTH(Z鮫/K CR]2ӯ5_!ù 0[큚/\\hW|y|B.V{pzZW!t9oCRij~B>}q eUCsra#yH,UțƫCu!gT!oz2 3p2CR!C eC*C8}_<)Uțƫ^i*䗙 U/:>W!_ 4bO|+W~BRxCTH.yP7WW!f~=_y=d_f7~B~s鋃TH(zҝ 3՞2.ݯ<$*MU! E/C eC*hO^|W~r/Uțƫ*CU/޾#]U4uL߇U/: /^=u_}H/җW<$*MU e*߇ *̟U/2]W!z EC*]Y^t=dB>xG'/<9W!f>=W!fPW!χt yxCzl߇K ~+.%9$_W!A*d(/i T'o,C8_W!sr6ZW!N~#NH{B i?W!1\W!g3wuLoB t]͜*䗙:H eU!urERi=W!8KC eCUȧ/BCuL߇U/pCVlՐ_[{L߇4U/:i?W!_`CkBC e*̟*U/ /^\C}݆o_! o_! Yn_! Yo_!z~Q+Np !u>Uȅ>$U/3!T!o}H eU7 ?W!qBj3Us]2ӋKB4*W=B,CUolC]2[;l=Pc__YUHmtB.c.UțK#e 4 3x WUo<YUȅ\KG?!͹ 0mB4^U!;<;W!f~=a_YURij?W!g3uLC80ytB.V{ǾW!f~=d_y 3e_y=_f<\\}Q(UțƫC*hOWsraN}H eUCsra3}$2W= 3_?W!_G{WվT!oz¯re!*hπW~BBx!]]2߇*̟*C8=~?i>ĞF>d ƫ~2*MU?*MU?K񪟇, yxCRi!܋GȻƫ~Ҕ*MU?iK߇P6^CU?Kb YCTt]|C 0߇ uL2 e yT\6^Ҟ]T~"_U#:SsBfn*䗙BBxI{ž ?W! 99U/35K H T!o/iW!*l楮B~魫TZ6^ǿ>9_fzk+ҖW-C*l殮B~W1_ zHB>xяI*$W-C*ꋆRiB8B~zo<UȅjW*̧!W! ƫsraNyH.UțƫC*h;0W~2*MU!*C8}!TR6^$Ib/-ٺo >W!_xяTT6^^s_NX,߸^W!յ_=BcS7Je%y02\fU̱B~I*$WUo Uc]2['lB8}c=dBh)UțK}}m5r:sSW!ˏ}e=$\W!W_ԕ*MU±C 0I*$W*ꋆRiB8B~zo2U/3]Cֺ e!,W^i*䗙pCBB4^ >W! ƫCu^ ~+ eCU/ўwa2U/3}r\|y_OCRij=dB>xQ5_UClB~C¹ 0sŋg@~+R!l!T!oz=$U/3}pB.yx]|sŋ!U/.C8~?9ND~4+pH{8$ܾ)\|zH ! EsrauL|P7Wц'Cَoȿ!oȿ!oȿ!oȿ!oȿ!oȿ!oȿ!oȿ!o?5?2>.ly 37aZlBL:;س=/82=2=B of?-i6aM&}tLj{AX|v ĺ6=l!-۲-1[eۦC^ѶLo4[e[jڞvMǶmb̙%aSv; ŸۋiC0v3IXXV': Nӱ:{hǻkӵ[hw:똩o0vƮgHy?И /fxv1I7%y? vzֹg#{ֽgg;c;?8⏼yl.7Ϸw6_dlDV':ՉNdu"DV'r,u"Mw{'Iwd;Nb{'3&;NVHw[!Hw}dV'ϼw2yd8Nfq3,stxf3o쒰,nm0>Y@K:X!n=$^̻u̼A'Fٳ&n 2sŁ?v={ogȇɄ$fN%mwm>i*xpB%iZc.L!ovl:L 93Z l[qCAYa-euZV-Ӳ:NUoN eL M;0}`v| ۻ ,~ '&3˱لv-B`+"tl`u8q?da24hXN%3^Dw,13Gcq;cq;nߙ}917K{^̻iiݾ,~G"Hrlbt6}dGsYPN,qdcoNyFހF2@op:m8>/K,q f$7&Kc!3f4=6=í2ȏ.=3cbN LJ9pFlhh~2qvp뛁8h ZColGS߉L(a:af-&~L&cy볖cMvn9 i{nhoLCևg枙14dܙm>[?Ӷ[Ӄ.-<0M8L9p{c1!-mD^xq`z`v}b{X7-盇Lߎ7mHvu23g9 .vYg7!S9=L[J-ɇjq^j9/9(Z-ŖT-ӳ:=o7Zoa%$`xmN-û[xf 4ZNZ#] {crjM33Ӂ ZN^ΦumePy:Ёy 8Xf;pr{onKP['0 g#cmx;)2SdߙLblA'oа  ^3sdPX\F^3_`y΀W`+0 #10 #`G^Wdp[MXRΐUvs :f8=NLلlSCBގ*)t!a23;YGu=Mq^6ah?mpޛG`P8<¨5>Ɠgzϯ/ޗd;'ןl1AQ`Pbq$|,i`(80SCKV`0fԁSJ`8dԏ?MK0P> JR@$ ÐXĻSJ`v~fI(L0䖿YRN)SJ9!=1Gfy[pJyts*P|p:2sq`08umw:A7si6f:!3w̼CL7SBGe!!ۑc1(=t:N ]٠kzL7G[sͭo:چfcڭTo=/8ўyqۭ;i㡎RЬG:ZG ;;BCE0'!7Kʽ[XGx]n;u 83Ks:nW=omuzs((uƺ~;v6~;>d>䟳:FsSq6so#Oq"KѽcuEJdύ1@;KN@]]jk3=ݞtclX; T]6.oe{T114kkZ?lnt>tv!DHv1C\q\i:C{"3'fvǵ6_]v\ O@|oL%/fIs7f̉=7D2};?ާ y;\\|oY5fy1I!{BބDf,[CIf봬G>:Tz[=tx4CsM:9BXܰSGAB8hBCυ]\z}g.rz< H[O8LIUݓC 7CÀx·zN}ojԏ? z{vԨ{]oSIOͩFSp)8W7(5SJMtZ4! d>Sp9 z =睞BOͩOSxz0ހIO Xyex%Φszc3@f)L=YF='c<,@32E"#ZOO(8zp=wQz=5Ǿ٠g븇"+x|!]4!=3)w=O`B&"]]2rW%ڻ*3`9ED{W%rW%r{ih^l鳟+{5"rzy2_bzcBM]!E;1l+S]@p%Hd\G 3BnKڙőZRG3oǑȇ")8DFa,>h#2xM&FG|[PN%7H*rqf=KGdeH)LŞw- IA$ˈ#͒rJRb4y%Rpz̙y[pJ%JfWZ9ud>5u$q!q!q!q!qOAHBm$ $ $ ${!1H.56N%.jRptO Ry1[3uԱUmM{.w D(Q.J?=Q_OܔN5VvuٜOeyq Oq'ȏy!18l61k}Z2;e2MT"Rf 'D-+ĕvʽ  q7 "s+7sl}6wk-e>;-L>s\ig35ܑ3ۙqn;sŝζ03L!*!L?Mņ>2a37K-ܰ9gęqv9gE+ܚ20P,S,ؑ 1e>D) 尽3d.3r< d@6q`I9@g><1--L63m~L)"@{{^Lu0#F mfnqν9e<6ahNMVCL7uVoY3]SC(?ݚv@ 3L?)̕tp s2j,Q6L%s圹r~'P_ (d+9'fN̜Yy0fSAө:JlyN#s6vUhb\1g?/8?7Z Lց#d; kSßċ3gfޖ61#zhmHzl |f37䁒 ց:1xh0n?P=p[-7E˭`1v!g$nSJg:|uPe:P%(m 3'/oj OK6Vx>ؘ{^̩EcI>|p۴[E!ޱ; ޙ.1nSt,V}ʋ}4=v?Z=w17{lζ{JXfۑac:G9Α,ߞ}z>giCό?fN=:msf$߇x/%vأ}gv̻89.μ3lřtlc3j`;v?zۓFφA>|f7cKnfEO;Vd}t&XXXMgWcu8Á:={jZDVCf MǎP8f,.Gq`mx ֆ'X`m O6<k O6<kc`mxq7KѿI,n=t9s\ɼAtxet~>--n,YȖMO2?ӡݞd}:d>8f?P'p 8бZ]`oୠoxo%6~{bmq/cz6ln SBL-mnhoV7XgIf6qU+_,)gȶe [[΀-gٟyhp Nnm`n5<% tVxJ{ 7<e>ިٜہv@d :nZts>{7[s1υ6<\h=4yg9N SB(+c=(lρ6 2_be;=e Z}>0Юꟃfiv@&0 cG=4>,1i%ϑ}t3oڇ[YR4"M/|Zܦwzv(Km?tYg::l+t?<QϞ7fe]M7 B3_txAl߃% *8<7 r>:0Ǝ{YwBfg>̼833 a [syJuIbS \Ļ9%ӱۇ?%ȼ2}18`61kyecp֡2~0pD= dXg>c.Z>B)xw>r@ܵt,WWW]ۛ%᲻Q(Du-KJ2%@Ә Oc6r|S-l@q 8wԎx)DZQ-pB1[ێ]ζq|~_3uwO;{Qf{ <痟m,)g {{:{G٧۾}|,Q=Z%@әOfmu)LuTrxJ!;+.GhN<4Q"KhGf{@sp J@u)w}!u4e$ZOk>e&RÁ@eS+8wcĞ7KʕtY 7o4Os6<4g=yp(ʹʹ8pȋ z+=ŕ~{lvoxc>Ec{ xY1 ے-naD2/i7|lx6Z>o^~~"VES 7>8ўXg1['?Q#W7`unun;3covB=>f:C{.z1 Cb )S^mΞBw=1ߞxox3['_P)H\ 5CUt>3vz|"{ =@{.{.nq3D:z49r&-ϯ }[}dB:fxq̑%wN=s8MM'EnȽӇ=퉐,Hy9n?vs /RX-3-}})uL7'/kxޯy&rʣ~Pۋ7CXg*őKɆ?rXܶ\(C3HΌ2xDy_7^)D2@٭3oGس=;Uf٭Ⱥs=uݳL;q:ܧ[M{}Ο9,FbSٷvP|7Yn> ;F3/μx:ٖRROiv}-)%i̇'3W푉#b.rӟM]oxȒ2 6;^1s۹8suyKL_{Pf9g 2Gm'2G}[{Hm?|$ %h16O.qw᷸qtEp9f Es!sdgy?cp2w34Cހ&3l4fbvzs9onxታ'8kxታ'Yg O5pi9ػ<gf̒p7n`ۗؾ' 씽9y \=7_́M)'Ξ$ulz.Q HCt69p :P| R08pC~Ht05!o!{^w>˓eOCA%א&.5FƇl\bDYeaɞH5pXI?WϏ67 LU㏟͞'^hUۏ>f"{n!3OiLH}!cZ{~w[V?mbfBO>LO' ކ_ipk7? tlcjV3؎ [a]g۝燙MtOb>MC[ нBz:gln=?J5nyaa'?q[ ʄOYD%Vc+>¶O$fbʆOMيY_VŤmt[[N_d|Kd{!;Uo2zt-?mqhxUCG6aۑɲ2O5P GӦ8ln}ï7>e麃64DXfzaVlTCknK]vkɄDf81sffdfkJ&bllOfeIkv{\i4]<6-KвxҬ \a>V+>edN`+~l~x{mBfۘfs^Ou[MmMa{Ǟ?nMߛ?mŧe{V#;{ OG mO1KȺG6udI";[bgKvgKl۷<8lTO==ݞba>TO=˼skΖyq-8D{2{lf3<;G<~q>afGl}i!mv6Z>c?*5̬_-|OB]/=du>\C>Ӷ}ֱpia|ڞ۞J;v~I̜xqf'۽ؘ5YA{{aےlcć抺lʘXxܞngfZMASdl;߾omv=Mُ2Q6OY:gޘtxfʃ ï= 8<44p>L= 16lۡ l{G^h7'l*K˃. vӱ8>ۃ-?2 9@ e_ܖp{v#$1 'VNؾJ?Sڞ@l屇j>خ [ClHC»dVN8ɏ|=$\‡/l8b7[[yY=H@.\rt\jt;X3-tb-2ӭO"KЭV%LEsq}m_t!u #h L}SfN9uT:FVuOy1߬z$Sp෫nKU[~!PAk-#Ió#mINÑzʹ}_ӭa[Yll]fqux);4_:.s\0qeP9vvm0_cWg6幋Do!n5C4{,l'~Et-qO=I~r)3w}ux.ё爄L=9=χۻ>n30KC,-?{fb(]"IʙL%FYz~oȶ{sY?bt[67K-(d=$qMܾ!}D#H4:J>ng")!rJ[y|S3-?ltJ} RZݞyjFvY%h{ӛ!-MFv§g,Io.Ayj7Z~Y1o=ln?ODxG[~!9FS7uظ}RS5-Oմf~~dzkXF޴ C$GLO]>rmʚ{_I| ឧjtl<2=O-%<\Cׇq@bnpO8 ' RBĔ( %B<|Sge !(bWR_]+ǎ$=E|׳i@@zl,z:GBf vO"}&hxAb\.z~=jN T` L)"#vdp`RbIxL 31d`rvE[&gV]<8q|9QOy/{Sus〉U\;ϝL [A"9jqF⚚I碆k @e7cIIl.'5ZVTgS-MX<{$N2)%vdGõC yJdˤ{e􁦥$56'B]\LF}LjǥKۿ;#B+c7ic74sώݔ)(Qܮ&n=(IߙP y Xg!p?t$$$;0~EF|͡NC.Ҝ(C2%-U`(g_&U,F;cEёcv:,`wHk}4G{[$\3:tOj<"OnNjTMȏؤIXwCuՕHM/ψPZT_ $F 9~jsTc ?|?AEbՁgF2DI\F|Kڍ'ƓhI$xm<6D{zm<6DO'ƓhI$ӓhIEF?XAr~isl,HϺ9jsf~DoT65e#?dzZ\o3uaJ'CMG^OFQhq;j$d5ǘ?j \5ڮj䚚:}пUĨQc'1%r넨Fj4y6'BmN:}v(x*G >ĒML>9f s0A5]}o1ҾF whes$d&3W76JIj r~FaNDQ_c#43GhjRȗm.[?Y9ɌP\o6>MOƧiӴi4Ӵi4m|6>MOƧiOOƧiӴi4m|6>M{4m|6>MOƧiӴiӧiԨ[s"{ע+g&E"ڮ\k.'O9PNGڜ9i9is:Ҟ#mNGڜqڜ9is:t=OGڜԨ1ˍ>3 {ɱa/0| 3C㴗7NMIwyvڋD!69hs^9cѴ\I Lc7\!qcf.n^:v7U揄wƻj]xW]xWm6U揄wƻjO揄w^I;A(z&LFXׅx\2l^ct6m.\ =*ro1f[} 58VcwUCbx[76^V{&٨Loko4cj}Q[G\4򐶣jrP٣t"C5bnEHoTfMrF,nVf 8.^ڲٸhmNX<Z6lhs\Fƙ %#1%"6^5F)I[1$һV#-dSt0mHG{A"6 F㐟Eyf"l}l%jeuQ=tO*I5j29.[zkLc!dN;iF(l(( E!7g2㰞-F M&5?8P 6̃/42S$DE"j |vh#~^% ds=e v6DH(x/0'cbU:<ǺF~, Ө氷μRօy(b6.9 Y("v L4ӼᘘƯO`?;j=ߍKu15*#4"\4Qp>G^ %LYc]|>G 1%qiTsǑc``^T"Ԫ>CrLMɬ~< |ѱFDz{X vkcc>G닔O䗎1M>^aS?e4Ɇ9"|`?ȝs/+('\$WGǒ;%Hn'_rQ`ͤבL#Gs:')$zuqIrFMЀ /a:(AGn$ʄS4șpc̄ Q6"[.~|#Դ Q޲9esx- O=`PDe]pxK/sdBI(1rs}҉vq>8>hŠ] 0ɔo4yȋ@^phЖE`nNpkyEastO8-zh tUK8A4W8*(9q!r|6Gl":i*xA`/b,vP[zTqd<"n6\OP5<MI?KPF50 EM0H:I!cXqx<蠟tDWj.0T@֋ü 9 8Bu+:^H:y^dɘoԄo-oFj)QrX}7?>$'IGG UË؜Xbs5`x;4I\2 tN3x=l6lN3ٜf9dL6lN3ٜf9ds4i&{f9ͤ8y?q,t_&SxA4H$3H.BoFJ¥7p18d'&w9@eJ {:HLyȴFJ Syه5.n".20rIBF9DP'kŜ ^e=~ն 2 צDBlr.9u1: >[gT`φ7D#yiQqIq|Vjي+G4 o]φD>[-l8Q j|6Fr>B|݂KEg+fM>\Z ɑ)y1ČBLbRqgg+Ŝr,D >G|boR.꫏kƵ&q >[g+lȔlߋm5Ki kE#ǫ/R7yЛd/X|֗qS}'C&PbGTE5*lUƽ$,L9]V^@~k40xtK]n "k4G*@ef@۞hƹ*$ 5[{ęl=֨Dhifg{pZzqL%j"%J)JR FMbheH;2ءrďuG\pzE)Pz^G!.&?hx Fy>ÔИB5fL|FsШ(ʢqE"LhDDd\$4ӄB>˰q=kjM ^'$o`(> &Bg5rͨ 8q40Ð\b(4.c\%oHdsD _@۞3:#Z` >` >6} A0| A` >` >MA0| A0|l >y)jVɻtL1:($ 0Li~,C=ÀiOE/[tl#SeBG{D=n~&$֋~ /MH\1Ֆ̉g/b`ҵjQ5yt1ٖjWkTf%-Ei4|l!=Ú|0Bzbpaؒ%ojkM7mRohW#D-aGbJD-0sgkjJBM&"6[+SsF-1[ќC"֡cD[c ulٱH%&rtXOޘQ/j2-<1قM0BJJ:B/$mJ Z 7lK&)DΧ>+rgg -2mE1L͚pk𑉓!!F#RemkEP5aY#(h(a~jmkõQШ9W`7Y> tsu?ֶN͂qk{ 1k=Na+Yd~ۛ625L(Q}@#|8o Vގ1JOqmԪ-Y#$cEęl.QvJf-6kCpJSɄuZpZpٱ_1_o[,2ۃEfMY&S0D0H</_1_1_o[ívRRRRRFlިmK-Fe"GөAGyhBg%۲ۮB=f4^<  !ol0{#hFrK2AΘf#ևk@S7{(LylQfCW E3gi!0cYg1F>FE%J鼦& F0}6%cfFsL,3Cejd>C^@7(򍪉3:V^挎3:8He~ޠ›‚H:}rt,"QiE SM28Ť TR0䘃8PwýA Xhp,8C Ca31GuFs0@P1 D  gdR$Ru$ +XATotsMMYpŏbdȏ֎ &Q5Zk 6#N0N0GPF~L:lG/q v{҉.ч&} r7X`,wnn r7X`,w,wnѨ(vo$}f}w/ɏ19(Ӹ(A/:G/wF䘅g\ 2snFv@XL4h!f[GdeJ 䘁_&:` 9hs0(9t8+aq$o"%&%& kWb(;xkP/LQ8O<:ф+0NcHGMFLihɆ9ʼAL?&TAb ,s=ŃX<y2Ha+   ;88x64ܧhJMLM)4tД8;QДԔauă5(e8+bpŇQs㌈F1wGsEV`1:hL:Su8%IA8ᢖuBF=osauXqauШBݷ J J8ᢑQItD2Nf049x0>'8s3խ0$;ؠ+îA:bT9FݷO߇=jRAѪ!&U#ǒ6>qC>Vf,Xb ˳;*3P` 1< $QrB >! ᰅC ="ῨDU!OFm}B1hD2dm!! FDz| ?pfbv Oo+'hJ85xvy>>^| !'pEq=6%J:t@JPİr4ErZ6N3(iڎJĞW`V=`+0/ttt _y&iJ̞W=/OϜP"g+WOs >EMD@ibf^,iȓܓRs_T=) %&1#Â(qIX&˽UٶJ(FvU$S2Q%B^ߨod۹`!N,X~=$B (Q@> DHh}~JFH [Iz_Q/2Sjr$FgEb!~[t|Q\r.Gʧ3ۍ$b㤸2atrӁ6XBՄxJmm0X@nA1" .T*FT4i*=NSTkRi*D-ڼh3BTF)^;3D9::rv=RmJKG ^B(vmevIC{,vXX:0@ns<)9o$VjG8%g۽:ҨLu]<&bSsEfa9.,AXRș‚ E=ߨ!mW&ڈ*oD7Q卨FTy#ͨFTy#A}6FTy#UfTy#Ef$*LAW1lɽxKI~ WoX‡wEߴw(١8(΍<#18l2Y(iQ";>;tַx/J͐QU2d}4gaO31F%c1:cstQBh*}taQHs$\?TNR)zE;/~U}bL.w ted>tG%j'7OTL"(m4'}Ytj}RdVTWVjn1V7#Ec xdhx"H~II?Z]\D^y)LƓL(I'5gT޵/kI|HLiߧF񦤭}"Sa MW4GtߋqB_Ì1Fh ct9:NI8㌎3:Na$$$6zO?>L:E hcF#jpC_(B :L[/!K5 2bO$K'зCh4<{$bOJw;UbE!3v't~;BiQ/>K6ɋI6m\|z(L >jPCI%vpBá^B8/Y!%YJmzHOڟ^hH&̊NDdncl-^t_7[տLޗࢁTor2/؆+ ZvnԷzj,ևqjơڇjKjW`}tt91_+$ 5K꺯d2N;D چpBI]C-UTWF..^ *LM#P5ݼ[;So-}ڡ-†oԛuoL0}*o[9gt+7S8N FQ ?N'S5]CmTfZ;DF?mQ%AĚQ7Cl\ ?0b.6j㐱R8;o,VO>D ?ěS>!P5;FK@nX]_& ";5*C|Dv mm׼Sb#v4; aiL1l&P"tx |R,p9wŰZnT.55b|RltݻQYn$5J-8ƧtXZEI'y#D|2t mV[l|*79ʰb fHbctlb@tȵC9|\Ay)(sNdtw̆0Q7VM5oXe|Q)`h 8z~};; } ԑQM:y^d*Qѵmkj6oj8Nv\s!15=w'n;&t/PwϦӍ3:L4FKk rX-U CDwk {psdBdF X 8NGTTMz!.L?3_oI'Ah9vPt}ѱr#gă@{>czjtp"2dyu,/${`$bq;WLTazbE]7ItDawbz7PبAoiTb4#nWՂ%[ Hf/:{֓(H\'ϓL=As7 jA  RC2L4y '̚$`Jxz.j2R   >R'*BQ T&#DDk93}ccp0oA)59N9(LK+OF$N'?| 708q/Gs'=Xl;G$уFMEX'>ǽ`T&蠚>XOd {]0/2JpAa+0wad;0w'a7D4S)SS`\9W" %[{J(jX"7PCQAt(x/1s e~ '_& xf@,DoJc.RH?V^&L:VpKX8!oLmqhԏ28Xv^Ѩf!Jt Jp]P$A' /ڌL La \sR%<0mS"b3^Vx9;@}81/qdq@|@Jo#CT5 ^ 9ѨC lT+.EjC H  (Y 77%nޤCDX[R)}u2?Q/TfMp{^0*@Ldn׸p>914ppE#jrtШ$jCMѮe8υ9Q%=9 b|l%jTnW|q|yξ8qct߅m޴yf,ńoT&P/JD,&ۖxG\d#l\D X DcLɏD<&ɾbN$aF1|N6T9bp==b1~`©HSH:.,3cmEGw"_<ZAb͠A&/?]F&Irꅺ%& 9Αl7t%`O-Ƅǿq(~Y-.:IV(|Gn-$W)LC gDz%g.[hǫ+|>M"I!$H© O.يZ}n?Nij>M "2I\,.׊alG:Y.Qp>?VK~N|Q5Ew+w:сU#jt:4.&MB+ʙH.Qu7\A2ao gF"Gt,r:x8;IDutvN}GMDԿ, +hM+ha-xt QiCA b]0 D`iq#i$<4(d,i Si%Z"n&N~'EjsAӂV$긟Oe"E}> SjXCk'LXd׈o+GCD> {t%t~8xR*z(jc*CMq^lQ#_v; {$~eiD-hQ21c9b_4F{F uM >T7&Mbs!CMMD=?rLP0*ɖJ2j`@$ܣ}>ij'Q8&X}+/NM_pi3*cڇ҉vqVIjpx}qxN!o0H;Aģt=F'`@F~8y^ct6'mNڜ }ܤ";:_&(q!1%&(L+JMb^L\Eۋ_5"uSp|qqx'.p;?>$>$d|ɶ.tF8J\/xdh9Rv/:?vKh=y:Bp3vwt$Ag7MG}8}Q%-j| 7cFg'ᔁ)\oS0kd$>8HLM;{?vB0F8l5y4}t8Ç  qp1Ċ}8UԨfHN:&㌂3y p=rYctm>{p1ztfW">K)1lAaI^1:Gd\A39Ԩ'5H&M:jߞ׽`% 1_F1jNb"5@l>ڨtsh"}PtV}SPbpipELJC+X=6ڼSQQ<(jd[(K{;ZF%B\15θ?Mjة'GL6$[(| om:vM3X$ג"%%j"ig>A[wQ3.bst3"t@PZcq}8p0`;x_]`F|!)f ֡92v?$S2t :,h̃WÙiV0OP V#I\s9:0» (i!V>\SӤ/ps>T).Q'59 nBFKXFLéI|@[Ey1#pȌ,jivd+&'Ͽ)\lmmt=uζ6:FgHζpKkCtζ6:FgSgluhK2H8<.J쩳mt)yڛ>E#qlFA'B5Yr-Cfchfc.d휙P"bo%:%:UE$bsk:i&p+8Q1DPYnIGM+8xW]d~CM0ΜldStߋTG7Տ͹8app:;x7~ƌM&[!qj~3flIYrA2A.Jd 9ߥl(L!)ήWt$7ȯBM>#xqCjL!~zq =C80qattL(Qh=PBS[vɢ$)?&F10G886`4rs=p285{8slSXJg +iB'c Qq3mC 6Q>te:45!~<52q~|H$fho3;cN4jC7%tQ EH p9E~,Fm~a jVsu15*#'CÛkD! cI6'/&&4cma 2g&%{ccá +ST100Go|oXN\hL:48kA?vQ d$R3u_&8>rli0m'84f)O|Y>,}F&BG81sBjsoJz@<)(Ar;זq T0墔4e<9kH3˝ )G r G޸u 7 ըD&#r=gVqi;\"}cr~L`ƜמqxY\l=Ʊ557%Qb%}ű-сdæ,t4AY1{yh‘8ⱝ?9h$>$? ǒdQF58_MEGQPA2:9qQeH:#s~rD9k$Qpü}DmgQG4ȹ,pxh5 :8xtѡQX}Ab2BO\dr\$5o1yxtE:av9z[D;E"َ`-?hHeBDZLpEj5bʛ.bFxWǻ9U`C#ϓt_&SxA\{@%0&XH CIPԈI%Ƕf Mը0H&p~${qH|xi]a*j4],65B' {hTr8X!*\2tVU{#>b$.~,38xK%%2,$CQ.:c}6ήt^ C I Tt,4c]u8eBo֮`쪾$QE+W qXcy?3ϩb<{ؘlHsWtj| jühsh3u\':\&os ;.}YRacxcR@{ v$##`G2ܑ v$rE O^$d㘇Z䈽6Jҋْg]&|ϖφ7D#DƵPq6D@5 >[gKl -'-YrpMMbMF%'' FJL2Iz-myĽ j$|684H:%݄KEgKfN>O4$l)&phT&r8Q?ϖdO4qZEcLl ׊kEҡ %|Qw~L_ggKqyJφkEұngK؄Qy{ -%Ƹϖْ=DE"bc\"c\&v8hTs'-V4*3ERh9͔X>28'I,QPNs8ԈNs/48wqQC]6J\<P1 ZW+&Ga\t/|$&1%c+9Kb Xb  ufv8 p(02ٔAsPhd+ڒá6%)lX#.Ѩt\[\X բ;<oqA5+$.2WQG J))B+ZOPH=Vq8 (б!d^ VkhG aX?aQᢄPI%S9r|. EYlTf Z/Bx!|v'5OEFqDY<h@w/஀LP> BG_&*8TbIp𧛇toTj"Q׷&nh38'3/>B >B!A|!A|>B >B A|!A|!BHz@5KѰ:㌂3y=Ï9FiCChep#C{)Pa $ $`cLxE | .2dNXJ$m&-i{2IMɖL-l]I d6u^5i/f3m5胢"o{Řd+^86Zz/js2 2ID-a%"ٞg$]DIfE&xDF5GM}Ώ[{@#бfn:vM3X$#BbJTQ5(I'^}uP ]t{ACh !qPzɧAkqQ z!#ӋFeB!|C)Hb!|8O% d>sUɄ4/ș%"Go@L䬤D;f$ֈq/()a1 IXs#O>[:08墾G!AN""Q"^5Fgr .,sXeBI%‚Q{>:vaP D G`WB$TLrN1iTI vbwýлC8-G%g\d?AE٣9A6*Fؼ;1ق}« A1:wvЬ$Lqs_(1"a+~55e TG&B:C"迍ܯ'E#78#6r4E|C/2d_d"OvG} `,{rX1Y=`,{rrX\oSGY{>p~|H$&/s42A1QK(-0 q~E0LAoTI LTʆgbp^F#٦Ās1:.D@|~l1:F6m6%njgqe3SS3ٰv:=c{1x&Z' ghBv+0N萗&#n94Ɇ9|@o$-d &bv\> ; 1u!1%P|%o{[PPPaV`hJahJă:By>4%CS24%CS24%ΎCS̈)9r`|@ΊWx+ypFDSqXo! +0r: "'hH.Gr)_&uQQтµ:qخ·k-|쾅cqV\ΊNΊѨc-|SҦMI_'64IQo(\tdQK u>#_&N!|84cn9ˆ±A;+0w ꗋQ8FV||aeցͅaPAcIJFWslY9+3QeBGRDcr18qa #M8_$QYdA:#C>G1:f;?>< |b888L +gp9Aa/?8!Έgu4{@P~;zu>os>KG4P5{6%mJڔ,=sPX&#gGN|~m#(au:ttt8yAo:y{^sa >~Ï3{^3tlkuOߨG@oT IL˜yr0x'%˺C89o)?$ȰIǨoL6ܛmuD1v)}NyqG *B~;|`ۉBX ##C8 ?ڒNx" "G8AshQ%aىE梍AF_&X~=$B oj0(el2c;BD!b4}E}NQ9U#x"Q䚎B0I,:&X~0@T G "c;`l9#XD0a vNSs)0;MNS " k_dh1w9I6 DEj5L`ȍ`9RPJEZ ūDz#Nvs0<&%BҾ(1=&;MӕXw29d< %!4EJd𫛆?dQ{݋% MT1vwF|<dC(>r;^Pd]9XH d#E boe'>CH+DQ僨AT |U>fT |U>> |U>*3|UQCX$r&E(8td{;%%E8`97Ӽ ŹP#WMU#Is#O8ŹfdIdX2 ]&ˤ"v !Vc08/ 7>`!/ yQȋ$R ķbIR0a%I$)oTM&`CI%=zgX KbGt.DCA+h{ȑvjaJQ.DNL@h|r5 5NR L{%n p(쬅ipAd[^g' _wXMV-/-i(^jQJQX- Eai BzLBP> LcfB.Wx7*NedCudr9Tl 8~wP աP r\5=T_$qB.ߝvs/$s1R?w^,%Vo侫& &ocL(IZq~J, &ިt .~s/(iQҢ$6ګ)J/JôڸHsb侨L7()azT3#s}9UWm;!ٶS2u2o^joT y4SHH< >!SͮIu_‹ꛠÃ> 2W̏rm$qM K&רIRRRRRlȏt4'7IFRќZ> "w>Djmn|kG{3Q$BE #gdJGF؋y rGWWo/H6}*d}lÄNVV7>jb}\#I\v%P_ImOP ǽ$E$ 5ݨXC:mCq4)/3{Aue3]zxA^ЇTӿLxK2myԔюe>é^0hZ0@j*zܪ$\> y=2_d hq:3z&SO!.|I$(znhs1cyOp|w 6hhsm<72Uۮį0"E~dK Q>T55?O*& ԘBX}S3Kr-h!gb/E0٨Yt Z,F2t @<%֋ {ĿFLHs?lCD!(%fi}0l;^&dۄlH:jCg#2v;DN"c'9#c'L$2v;a&Id윑ع\ a7| "b7x54RB[c AI$L݋[$v#UL L8&[؆IdF=&6&6+3~ɌbcC{oEJݷ-j2xmi.~PuŦ\ct6mFnoЀE:\G5L(QNEJDln08VpLnwhpH8ÍJ$O#LBF|q*_ 1 p55EN I'FF9̋kqx-}6*"%9;DNE$`A5&}q;1a:2ClBLȋ`$ TMҹ><Ɍ4hq !w)xQk QUlձZԤ9:xwJ JT.ng}/Dz 'F%c/Dwk EoH|$HO2Q+[!mQ"*ƍcbX/UfʖeU EjW#5ߙByjXV:mGsP1ͤ&0Cl Ͽ3Ģ!!_W ͋C+0g)6PχV@$C١}83Dm]DDC-0Jjn1:AI7w)dfn1b &xK:f[D3?'$3>C᳤cY[̜XSFOC3rr>A3EH5K"_܋Mb wk7gI'sql"ǬuٿL$7I0_C-3:*ިI85O| %F{#Dd\ŁL$Fwo\/P% czm̆)7}"OB@n$1Cmu߆aʱ9kkFaF@}Ab: )g 1uEMw~'/:gL6Ot]5Ю~~|qݽ`T`[i<l>u6'F3_ll>u6 {9a/'儽rNr^N {9a/'儽sr#x>`3HǤB2y 't^2}0hs6$sJB'!œ⩐⍚lUa rR ܨLgn&mԏK׃9O`Rg GG49Ϣ& AG`y֘lP࢒5@l< I$O$Id-Oެ*R!2,x(bx :;#l$qfw{ %=FFHÞa3F) ]숧P*t#[|X8D#^y$O))%qɡc11(M[X$!ǓPyքDo蓠$35찭 %:O R-TA2B~<$xB4B*|4֮fB:)`I< >qCDz;`-gv,'䏑Hb@1;# 4c}}ۀN1`> ;|j<S :!['dٺB; #-@%70LE1?kl'B'发Dn Iy7rDJywT\2 '?N~DnT&l:SFe"q[&k@)CTLLH|s2'vD$drj&,ǴOI癬Ug#.uj<"$3Qb%^u&Kb[JcƹoMU3X=18YJHE:@T3:V^錎3:`-CaA?i=KHIP #OVHBEg"Bѡ3 :E 9L!gϗIȓ:2Qrͺ@5t“dKtaD% ܠA/UWI!sa~^,-QFHMğ)P08~Z`kj«y DHIFï(&5Zc#bIp^NѼ_$1T/R>6X %QA8f,T+uaPfJ'&tȏ UC*hnT`@T]16E0nBKwwAb:7*WSHФĤDv O+W㈥{AI(;Ux:<"+(kUTB%}]Jwcm{!oI&[BbPRYrcy]|L$[N!K!_$g+*6P;(3cU/."TtpH)5IIF&)]MlE4F5whJE" tA@/@Q(EQ(E" tAH/@Q(5@QɄutL>;)QEJtJ&U z)_&v{?]>7:}LC0NO$.~*轠$ѯZA:F-M~Qۓ'V6*$<9II4WhN1?p[bxDzA_43O~?ct"@"sȹؾH|akW}LJϿ%mb$+ՄnVfJY)_&Zv}Sf2 |{<A<6/,@2UD̄ud[ BbJUܨrSO%)}#$c5pX;796Jֲ)яsqktLcfʹٱGTy+xjKx= }}+ӣw9 |"||F҅j|>{콠AB;O0jcQ3,9_, Ls tH!j~$3R{^E"psAoԏ7/Ō,fdѼb2"g;ҞWc U"/+^#Nm=OxPSI[kDFb}x},b47#bO\h.hnY/jh>kQPߣ|hb!/bmX ѧ1J2Fct1 3F蠟,$$ EM:8A<:0${K& p/Wuq NyJ: !s/|c`r[4|00FX"LV y{4'}LMʱP5ogL26 SDA5w8qwD:ϫ&[FjmT b._t%v.۵Y@־(Eiotv vcckbpi UW;M{ۈ5Nӽ͋6/:g#*6Ɩ[}dS":g#76m授H"pckgqh0/J{F΁]/χ5:X9XT4g"O6:g;%٨۞5PjdbC6~= ϘleYlձ T0Y/~ΧE狔:g6~cy^E;iV2?.٨bHs! sA}nq6D{5#M.~)1 /Xm5]Lk˹CGܙPD)*wd e*$\87,,&>`[NUҢEI/%.M >`] $i|/?TFlaU0P5ÒEX2$DD.c5b$$$(""Dș!EhC_ h'Q="Cdc`䓱&Kz[!_ 0ɔۆ(D%j'颴7h "36*jo a0Vy˝xwyR8*cp§Ղ`%"EWu_/rmF\+tGuE0>).w`E0"q)E}F7%mJڔBbQqVΫQ{>3dmq{I"F  `6A+pG+pG+ EFgj.w)Ն aGK jÃh+}QhQE<Ãcxѓw'\\`B_zA Rmoq9%S*G5LhyzXMrc5 I×>9$픢e: ^ОX g,$CI%-JvpL#ycFJ$mjb}\栃~qL#'ᥒQ C:maTmSd zZ~b#ooC2L[2?]K Bi/hcé7ߚ|saB> مuv3 4#nLg4DO)1u g2ߦs_K %BO~@%Vt@D y,qj|ܯ1OC>E ̉n| UDmTb[x95@W߿L'L{AMŨ`"b| QQF'Ve|$5Hj6E$};y+P#xJQ#g? RV T(1:E6+p(jlS$D_&^E#ćĚllS3aXJ,*|#כjf]F&NÏzHH #9M}+O%1uNSi4Fb318(h*Q9iD,&bj0sԁ$U]ԇԓ>P[H45݈Ĵh3 n$yLJ8•& kVs:JBI(u"IN٨#h{mTsDZP_ k/:IeB"pBB/$WIvM2(d•QOXǤW{hH.B!K!eBbdNU#ј5Mc+8͢B4`L Iְ)%&d lBT鈥*9ܩ߰)(UB*JN)hs6ɓB¨&]ds**P2_Xx1Uf >H X-6L ]0t8q SjDׂZ'.8x>DA!| XZIx%XqB+B§A8FbeUG \%OFKDF5*4 U^f C3!lԡ̎=%J§>6RXŐPW5ق`ƥ` |**jJJJ*J¤ kQPH;h1tM&Qx]H!W_ll2{uW4SH?DA&A&,J\uQפHa/_ _Ӿ}~/*oNb)݈m|~x~l*1c^Ta_8jƻLk9igoxF2"挎3:N3f8㌎9tF0:sJ:t(]a] &ݻҸG4ҜW44WA_*}(Qao0U$zcM5 N24?TEoD 4J~U^z|3uNc:б tl̠1;yZOP{vc>IJz62so/ɖtl2a.* {?EIEIEIѥB PZ'QxԜ4S#$JX" <_&fzǹ#"f%7Ҽɸ37dQ⢭%.} "7>PRP^_T Q5><~!ODmNFef7cQQQKjK1j&r d,9opF H枣9ȋXȋuhbj(pCI9ats(,zVPH0B9 9UWqG2C":,%cK5K2/ޡj}2VDF&W>V QP9VyAWtF ~FigG {d$|@Fؼɣb [yߝͫ^JQ)Dlt~LsxJ',Vy6#gl^bo^b㻳QirmQ_mdۜ㻳Q6Fu&Dl>;HINIr5l1j>;U߇}w6>ɛ} : `lV 6ZZF+h`lV 6Z6R E}tZ\=qtla6D|z}UUyhN9谒xm1< ()~RI Cߑ(}!e| pY*9AXUأj&-2-edHVp_}b/ľX4^q}c`|Gc!) ہĤL 09&S!7>3u}x|tA&yF& 1oaC|!V`vّt7b|m\F`,HV[Kj\c'6`)`)`|-q2)s J!Cmҗϊ?玾`fy{>D68DD&(TQ_Eem<(@25-u2*jcIn5|OP;X߲I8puh%t,/ Z^uw7p9N0}8oBwV7ѸѸ}Y;+tНBBw^?|>'2ܟ-zh qqŰ|Q{2IsO:*ÓSQOJd|cQ, ϩT9阱/l%,Iޜlx~}AB#s!yLJGK4FJD?|'eyd=IKPGKݳ/|_c2,#1Z|;Ϣ$̃a QS0ҿLv Φ9;UM!g1J@?;96wiFd;qMpA8 v;9sA8s v;eaᰃpA8si+eAWF&ai<9A?'h:6IJJJB?8LA?IsR?E5w /BAGiF1:,O ,~N"6jt`,7F'~*)~] t`SQEN/fb DmL2%5wmUF2g xC-E/0ڸP;₾T`3 0}M,ÌRmT@Nu]ߝp7Z-ߝU J/LJ 8ġYE2 D}wW>x# vlߝ@u6#$e}@6q|w!P!ZC$u(g#%%/$ZC;5;QLg^(SAOD+HD+HD+HD+ȩ$N$ZA$ZA  1'|dJ,~"Kul.uX9Uu@Bz6hΖJ5*CFC<&dXrK%My H6>'"5&aHFs'd&{:Gd{d{Luyp>yȜe~hsHzBAO!c3( ;}% P21!fAle|ȓyL;}6'osbKL!mb`,OC\͢&6Joq *˯9EAl|M][mivDE1)_&^ѧPEpVduًJ 8Db6J'+v \LyAk1L_r}+UH_/Xe[Z,˩3/и>}ͭ 5VTa"+")4ūkQbcgߠjtl2rXr M hg]kqU4,>ay3ė'|K|Yow2t2;__&/J7_&/KzQٗeoKmIl$v0-_ȄJZH>fp+xG!wTd{q_&{OCzQ54cӌwE71FctŒѱ9:FIFIFIZo_bZ12շ-TRiMoTiܨtZ/_Kzŗb6j U23A-4?v/xثdܛūʊu1ɤTC>r2,{a$|rvQC% 9Y< >!ӣI(V_&Au! .2KYxf&$A}u[̿,iPULzF6 R[؃^}E"{`=Xdd=Xd,E"{&Eߢ&( _&|v/JE y6_>Qm~c|HÏLt/bO_HPpl4PO7FLDT[ыJv:VE×oZ^Z^$&G3vCG@ ˋtqX^ y6_<8[9>[\T)0ި'1Iftpqp6ģy/0@Ek$Sm< XC4tƂ.MW<|k|lmP&M.\5LϤ] ڼiͬ%mAmh:Q3XFXBzgE7jze ǧZ'Q2Ccò9< LdBMeɹ:0(~#G AhZHp[A O>΂!KܧO8f,o)e`aԽ:*ދܗ12a(El$1.i"cl>՜9IsM69Es[R˭ՙ/wnIA6Yό33--؊ByUL1H*c"sٰT? t}{a$29CA!£EMDz!uJJ_pѾ,]y 5jk_X)$)2cYF2F:j_$1V2lFXG {[\ ѱ){AAĿwO:ydJ7FǨ.Bg ! >1DUQ]cbVtÍksƢ*|`)%IhT>L1i8ctHG|O8 CNh29c5 ۰DԳ6ӡCeBa'B1/so7؈Bs,|Ӄ>a08)|M:㾍ϵ[@Y|$d2M(hG6btj|нNE 9RԄOcj/{Abɑ <`=,4؋ߌ8L/&)*)$gM#WGg¿v*s>)goߕE;Z>)uYcbç㈋Ѩt[/[|nAlbmF_Eq! f75ee~659ĬЏq+j㘑> S1:N4F~LlOQF&Gn8C臇cSy]g2ΡO,[~ 蜱5i/9493P*AV7l pF/bNH֞=4ǘw> jÔ#GAE_Td2k$5a:,ANR#6N" 06NQ`?S֢۟Xc7?08v бh܁h܊/@ R!*0&b輍8Qy.U#A@@@H:p`(ꋤ32$Edk&P#8&f@=j`ft59EDZ}XzBFÔr7z:D"﷌s}aJ,9"ćĚd %'#_#&j,mhQ#ހɏ}Гdk7aJ)zkFEj }E [A"cLBȭih҉& IJHhAE[AbdI4DsG*c_$lH,ٚSH7GaX7i!C%3Eih̉ƜHDYȏ; 5DfM0sϏk}P6d>s/\9 W"r,Um ɗ$(rBJB '&M ǼC1>g`ٍV'[twQ@~ k?"ke,i=a%,d /ssXF"+fa(5YdRl2}$H$Jך,g=c )6ޡ7*HqO6-ߒsGHXl&OA:A?عSȏASi2]K!<₵Zc)- C!6EM)6BLBLoxN L G1Zܧ@es! \a`P$Um8;d"Xy=dH yYl_0,)B!=ߠ!W2C-ca1.F+@E1_< B,ۖJZdC-v$+*PJ^<,0f*lEXx Ə\Xl$ѫ,.⮟hf .*Qbp_?5,dfao\9ݛ82Yu(qQ$"Bp~B4'R5,IFsvԂ~ q;,$|>7ļP.Ѩ&̣l/H}R uIxW蓢9ZbH_1:t/jTWmp#Xcec $Z$Z\o)52qiIIf#u*QGVFjysU]K|p 2ܑDE" eGBַI!~%. /ɜ1Ϣ8\ϦO65ڟ"5B>y>g4l:L.$f}Evd3PS%oȥb=bLKJF ^X40PLkxqfS?9N85rF Xq^<&sMM}t şËwČH\rhxwQ((R ,(@?Aa>ILߘ Ldk&q[R'Ir׃ j|FbT[\ϩ cȏ̗&ovې^o5pɍ!*JJ! /|R֒s-`->*8㫲Cr\O^On$g"[K)Q`)B~, z/h32u-ڬEw#$kd ]wJ)IF5w%/Lzצ7W5Jd]g\/k.2A.%r~wTF2viEXrb9AVzvi^3*g^ȏ/HLżܨtul;P85y_0EtFI+"RLQAL.n5Lc7fD2s+vѠ;ɨ$ ?Dn[;%P/c(x>(ޝ*iRxm؋)<el5Ԗ$2Ii7*D~BrB߈P"*m"&yq26bpkCQsT121?X`d}{^ w+5p;ެ෢wyɶQ1p/R&HϿ=E<%ܠe+xܓ.VY_T-R yɷ}hif$Cmq1f&(jEJDK$NE ^zGs(I[1 ߦLÌ>T^ݨD,7 ƍ$(|n$43g"pHIuHa FmXfn1Xo[nbYo.J6׆f?dڙ^xQ>N:>כVL7%L' o>)rǞfAU]7exP27D,*d}ÐX 55]Ţs*\nǚT2m:_']HQZQAX٦vl: #ah?èkH.S4΋fQt8o_1ٌ>Q4E,QQcgtF4{ ͦΆ ͐lfl6u6Cg3t6y7lfl6u6Cg- Y<:=7fMZB2yE "i"mNڜ }\lMĔXL6VDUYǵ:b% zQAbFb'DE?^»W&bǢH2w:#i0ٱH.W|ዔ+E /}r(IgW 5 '>Hptl0"$@8CV$/RkEOFm!r/$Op uY z!//:x&ďqAsQaq0XJ>f4.?4BJz75oc<5qI$MgqHYc]kMJdx؞8cxӴC7fv&`B;{ArH/b.j5zɧmQIhnp({lns؃iw6Jf-xjmr%7oT&NDz{X vkc>7_7_C\OEC CK&kæz˲a8>^7_7_7_C\RMO + J Jzu䁸kިt(Ar;Dp3H%#ɼ~9SZwux=¸$&h@GNdƋt/ynFrQ32H%y[H&13I Ol1\")Դ Q/fߌ IH tȄPb%&}B-qq>RLMsY=0=9ȋ@^"f *pV^a쏀sG~sQpJrJb_"zΠP4TWEFeʢ*3j oÒC#5֔(9>Vj_8q8ImjQ0X!1{Hgd2`x;\l)J剆r%fEب)4jhs!*l& ?Ԉ1*[MԮ0j{4],e8̟`")5|B`cJuVIc1b% % o%o XHf" 'Y̟j[$6#[>b5(F .!hR߻?U!KaA^$5s pF=gIVHbYt(k0/i: FCYl&w\&│5K_؟{!yR >@;ŎdB{$[A(d!& >.dŒ|6(h\e'ZqDxG߸Vl\+Il}d/X|֗ɳdmn\+6;>^M;QugXLk~,>Ϩ{/h͋6kOtsbgۿgۿMsIJut\ҙ;l)IƸF2KM34,2@~l@tdDٜCn Qc9ds&j̩=ď_4X?dC˷va/dSZN9hs0ɂG&A9ؿĠ`FPgs}$%%oq'R2ɒoq2ْ8-.-[tlѱE3-Y4f%w^[ 4t9l!FznNbϣ!|o$<Pzz?%=PNdϢƦƉ %o?gmG# w l(L~^_/BcLDd\$4O(}ԱAF I1Z> -LDhT 5z>sq:0$tKb$#F5Ppܷ1}%69@۞%saヰA l|A l|6>aヰaヰA l|6>=}6>aヰA 郰Ah$PŻ3Z|byPa4PZ̽DmLȹIt@3`%`JD rƽ`/YB-{m6lٙdHt]C^NZ6yĔd{@U{JB["nl^$39՜C#B-mlIбHtlLԱ:PV̎Er-9F57蓤$uW2sQÖvUN;Yc[ a1U4 k!V}9csdžԿupNS ɔ̟j6<6|4hsm0ԬMl:|;Ǻxoz_GRoi'sQmLp.uFl(J"nlHjt;%'DIfE&JdLHCJf-{ؠwLւ;f"|Xd)ᳵ{!7E9>^NEޝ"|6gBl;.~V͑')(Ia.RrgF{b0m8Pʯa(~=I~g;5jdqQ/aԿ!_ uިLzɓhC62a h(}@4/'8W57"]<<}jHc [ WASasqQa0lJ2͜測YL}H9 C^@P/ҌFae9ats( sa* ,L:ѱ؊D}N/O 7BP18gآ}QqC7IaT!k\"7D)NQ6'w4!x='slN؎>¡>t َ^F3}aoXar߰7, }r߰7, }O9\㢄+ІH&̺<{E1kTbQHo/Ԍè cV? SPURPژ~ᙸ9/DCAi1t눌LXYy209f y~$mNڜ9z/:ŕ08sɆǵ+wQ_T%#ZcLƏćݹ1u&ۑ[cc5PI?&T\Ԓ<yx1q|,ib (2H:p )`Wő;{ AS }tДԔAS:hJM#.RҙAS:x29+W.oau|sFEJqvì˟cdsuyh:SuJD:ߨ推h.jP8Wi!+ۙosauXqauШBݷIJJJjsbC;}Q˱ItDڜ̰ţo`~:LpOaSt\aunˆv`V`[~U92Q[!K2H:}u_&U]8@!XL^c|,i%6`ê(<1f@ Co$ U 0?T*H{A`$DᰅC ="ῨDU!OF >m 3Jf۹jDAu:[`0 ٱ @8xڼ2M'l{?Gx‡ pQF {l%%%ȝ@ X&ov!dJ#뽠 Wy{^.9]`s 7P}7IĚɞWbnNoxmrp@ǾPbր'ʹ57bfv, =_c'$1$k\FbCȑ#JBIČ QqIroζUB1J=J#M$btH3B6GJbt'CDA?!&NVXp;rTa ;i&~M|)oTIXv2\M_{w'C/>2!8#^jGw37Ԣڨ9Έo[7pqxBJ.9xKGmۍ$,Y,%dQĻhJ c{:ǚٵňvQNSTs0;MNS!" kBD"NS!'+MYP5JQg&:XrX@=5׆P*$94(,$I+_&loH΁!iw!i62cYFHi<)9Hs U~u>9m&뜒E}+xwdSsu/,,Dž;^PbS] 9SXHw52 .- 9(NA$,T¾2)ЛFTyQ卨FTylPFTy#ͨFTF2)%_2]-CrH%E`97 sߪToglPM`77*ĸ Jڔ)I v ]6!;E#Wha4i3:$gtqF0gt|ΡCIV&.ѱӎRoy*UWAa9IjT5EϻȐ+0]u/xwɥѸ]La~Pk'#dZ .EQќegJu#-~y _{<ȄWwILZ]4.}w:JIb%|TCqy}wtT{:4fm Ӿ=1J2J2J2Qy.2j}w:8ݷ7AdB>t3:8pq9:sFPatCIAIAIZmL:E hؠôڰ'hbLi`^UMmؓ yy`}R֗IK#зAG~[M @Gp) 60@U{a_/|MCx>Y+懭sR6X [|ע0-l3j(iQҢ$lm Q1#?FҜ}T>P]mm2HLCD1kfFK{쳖R#B-g;CۼkKͲ boXz_6 Rm됩|$m64XFj#h)hu+q0)Ötw#.b}ؒnġn>l%3u #|jPLi#چ0BIS-ߊcmk2M,\Lci{82޶Mn[;G1!PU4͋eoV2ѽi4FgE'!mREy~fʹDfo6[joh$݁8H"Rq,hЍdJF8Օ"EQ#22NusMMqքy"VCMygJ - c!J{FgC0٨9t Z,Fe2h}A":PGv]7y1^C@5'⠦59IǡDq5(0Qژ,EZH&u<1}5EM<rg7, )G)M ] VrIl$&-b_{lFE4=| 5ҡ ѱ˺(Í 6}!1Ģ˴`#!QIU3_7kΡ͇6|zTTma< %"P"*Oaj1:Ay2 -[h|詯ez~g& &[2IlLdlIIY\{1Xx|>(:^4/ l dnLсng29$:2I2)kac1v nE }?J]: ӨtoZ~X7rC,HǢ?X/$bXl9Fq7(A,n%u%ap/~vZ!1C gݍH"?h8N!ggp\9gp>roTf̓)6IIDM*-8ǥ,P%"}0)$}"A@n$1CF:j0uFIl %f Nwt}C::"ӽNb@NuK7 t`=`W? t"Hl/ϩ:[Lhkě19{`/el ^^6{`/el M^n$ޡX;e((l̓{{9rÏÀ6ofa@&dLXR)n )ިZa"loTff"cn&b6cj50'^F59c!Xaڎ1"Nc$j$$In I b1!"Na ̂pA^HA ny2IR6x)!GLK(J,JD{G)JB-fU MG\s˹!FR`H$ٗd%y2AKԱyJɆG"|8EU4bw6<'ݨ˟K79m$TItD\+.2_X#&kd#ʢ(q%&<EYQD⿒ %{ )Cd"cݨY03X#f QLڜ/2}fBM"|Ea*h%"@`%cY;r261]TsVm/Jk QPR|؋NC&A&ѱ)cd*1.R"fyz92$SZ(SmAɘ*O*O!| ?|RMk2 [dklm"[7RrP/c\,- Tҁm$F|!t(^a/6^KQ4G§0HJP#"}7<׍drHD Mre5}xh+J>*Qa08#N$\) QگIL˜yRyk}tUޯ7:.R%|G&;:t72yKjǔE Ѧb {>W{vℊS0U:m:\_TI:_ 2؆}Φ+=QHȋ tOI;q ־H|x(,ԛm $q3L݋xAfavPG{QA<>FMA1|N6g"5@l>8hy-inEJVlC'd+dJ'vgdSl@y{h͇6 .MG&AҔ:b)F4F5whJNh' C@w@;Q(NhQ(Nh' CHw@;Q(>@;QIѱE3-Y4f"SUR+3,{zބK+x)sc}SKWbҊtJk*顤Djƀ~,O׋5tDtBrύdJ70tDBCmUL|'B,ִ%F,Q΍j8!WPB7(ɬD2*+_&dKxi>a|Ac-Ytl̢5;DILZ9F5%ʳop?ǫϋ:k#|=>Qpc}?PCIPrg)_&-Χ;ȻP.2_g4%,=/ߛB5jnv7;F~Lߍ=/jT&jk;<821J4ڊC&L9Fa=>}zQ53ӷc}=nx[sYQ5{Nf'@+@s#Ȃ(UXd7˴4GV {#Gsy;ib'Na奀̍N2:$SstOvQRQ+,羋I'ιѱE$8J41w"~;jG梍)`Gd,CL_(hE!&DоDqST5ۊQ/J@6*jok%dmTul!kBpsb+7ǘ{&j1:rqC*10y^cal;m79X瘈?M&ݨfb*ic/TCkΏZD&7u!* UV82csu!7u즀#[R\I avnn#Lm7i~)Qr1Gs-:X9[̚<19:uónLL">j砱} >rxͥU XEAf9crr7clEs."ʤ@7W6%lqT b1o3Άp:¡>;F#o| ^nT&ghKGθlwDNN尜i^|Qޑ+t?Lp;gP]FŒe{()()(I&6|5,4GU?TF|;aU18Q5<3˲薞MXv|i&aK[d-[<"_:EIEIEIraxD6:clE||QqDgx:6&p|Q2./&$Hd2\ FUlh|r"5! oI/i.IqaYӆo,' E;n±Z8ܙ#8g;V GpT jX-OΈMDF>cwẐ83Dv8#6g%nT:Ի8Gupgpq8bK|%XwpFl8#67Ҭ0}<ƺAIAIAI ZwmA߿Le8S"hqOF2GxDhVhVh0yA+p@+@+pKa}1c6`2r)~[%Ts1r_T?DhK(E2b \l{Q*)$ۋRq|q"2^^Ԝ-S* 5 S*$F^nj!Sfy w!rѨ1:=I혼I̧@o}G}!|l$)spJvN!`P_/~'fCpUze`r7<ĸdx5} kxkx5} ニw_ûMニwFilE lPV } ܼ?N Vj#/%dNPGNG'{'DmOX88XΞ%3Qq{1! `6ꥈ_<'S&hN<7޾p?b&4"e,My"rbRa_iSM`s$knrB6RV*>#pA;(9h×E\F3h3(t/j9ŧAl$q蜬p?I<LQcv\S49rcpByR>бI_pRD-QEj9=xʤp0Fbw/tvVyHJŏ(x P5o# яxqeݷ1Z.xO &.Fq QX t/`6țX"NNa+m|-j y0P2F%Bu(lԋt߇VE€YBX*Vz29C1̄u#QB()ʆ2 +Ěx&;LLp߻}@%F)vbԈvS#l`9OJq.Rfap((h%XE\7YB DaɄC D {#Y5:Q`>P(ዔdzQ2F6JtI-!|M,vx)t2 ~O8QMvqDWs\.gB\ćuvztvڗG,J_Q=vdӱn5s%pH6I(]6M?bǔ$P j)݇fH9<ًL%ha>8kdb$vI$fqq*!ԨsƋSc.^lǘ|JEܾH "F΋ FTu z_W8'vd>isLtx22g&%:((Ea2\u  H#%^ϸx4nL:JE͙G7/(x>$IfʳDD6L>NQ.R"GF ՜~}uх./;ΌgƎ3c38 z'i&>ylJڔ)I&UH:}gǓNcgbshB9pho  Z50-NѶ)A.j#}gxLNyt5/DNN2v@I=9 {th0A ˜yu1I,I$J Al.#6)))))ID/C0u<xxq!:l](s%_S?˰C$t8!E2BXH+XKlќCEeTcj,fx/j,YzM35q cb6"mxsPn|TRPAjs=9\P pA.u':\P pA.u>:\P pA.u:\P pA.O. P̍;>G470,=c-con~ٲDձUcŘP">GG")vh}(I~@;(&FՀ+ X9x{ȲJ>go >P9;}tv1 }hsˍdJ1:(hwNJt4džnediT>[]|֨?Fl*f?.q웭Flq]gW"m J JG7}Q\qjAs#O :Qafe: c&%%%%pEju`!oIj yi>r㌸1:AT?gDo+I_%9lIgԏNqjV"SѮGTEu RGZ]Vwtw{&Id>.w8 Lwf+Kw]pG]wu+i&'KҝL>r:^Y.ԙgԄYlݩ3.и UտLƻCU$^y C!*A&T ФqzQ“!0Q/ڌ΋6Q[W3#1}R k4kggEsv0}3ء;tܝ:NIw}hJЃzCӋCS fopRBȔ>[p2R8M!W:JaX6*}"p 8>e48 v'ӋG1Z8nL QYIN#6qgڨ氍?6CwppLXDDk4=lv,Ct45wϥNrh? CW..ޏ@6>HXc8pv<^$̃y0Z`N "iN|'8l L$8 N")E 3/q8b94΋Lb+ ENp ' Brș! B $' BON޵` lL}|q>r>? DOt8m߈M( ?!mT<^Ԓq2=ыd\Ћ:tQ5`g~,~I#H/u8ah; WMl~id$O`WN\8IR3.D"!.yO,5yT/xJQl䏍cBl$$4.?O+gc ))_ `b:.?7``6Pq~6e ^A++EJWP @l>2p.~+(xvPlqyq;VPhVϫ B+ZAZAZ$ʋ|Z։r ΠmCu: ȋ}pC* |rv*2!=^dpa+(Gt\y-ma/VB Jf/8*^_*m~i7ћ#|w%+~4Ҽ>MݙXS4(6 e1y훋>jPCI%up S\91L|ꍕNq$dH<]JL_C F2?'{p$ (jRɚōFn>P]ţ>ZL҅93)5#vPm}:&iy|֭A"ƋN:JNAqMƉIwjE2-P;DjxMKE_#Cx .G&l\Ń#[_,g" {y5d=J~QC`y}5u+5؞YrھZ2ӁEDj#?#m 1Y@ h{}ɡDf'Fk0>´s>GUmQIըԁZGY22ϸ#ѱ#8Gw$1挧qG2$fId< Ww$琁sx ؀.h Nuf0$_'IJJNu|:EIEIEI0gvC hz}tla5{J-P}=wJ{?&PZ|Q/fb;i;Kg%GZZ^@˻H#}^ҬK(AQmN5KO62/%&%ɴ|W*ãLq\5^$֧]}sbPҡCI:+H!b40C\F-b y}S]N/l^fwtA&c-^x[lT&0.Y\a]QwQ}1kb-^s-^ŋEZXkkb-^'|Q"aiH:eDg&CuR\_~;&HeDLj8D6Id#a{PMd !^4A2e_&RQ 5en=BT cs[RSu0uNIEɑmԐE6̄ڲ6;CAAmju v#;>PRPRP|]r)A4GW*S=|Q4ŷW5wy2=X}ΡʊܔXȧ-2,F"(g99 i,ӷjUm_$SE 2љEǚˁ }|j/㫋,?G$x9bvbe[A Rm$*i/Cu" Xd,`,EEY" Xd,`,,EG1!.J d(Jd:lG}xO?QX֊#qtM-'4i'vA2rZl/< = zva7z؍3wg|n؍ c7jFmF 䑧](ѣep8/Y~PafJm) [c cXluPG}9p 9NsA`v3pnVCsj5mA6}b*&^ah}uf)AvfG1iWaA6mf/>k%-6Gr_dD3%_m+A펿 2^m >AX(9ڡ#XvTGGUXj"A1TgθLŸaqiTMjXznIH닶cu9X:pQE ὑ)5^Vw@%D.j/|Bj$♚*\w5a7]3 .ɜbJQ]4pO#bg-&w6c\3,26jgo_8 +\8+G޸B,LÊ'6>1[^(hAlp8㌎Spri}8 Gr/8qLؾ943tN1|B\ 6yAMtcCFJA 8lfz?n7kě kLf|TOjúEp6|L?Az'.&u.`̺YY0f] u.`̺̺Yo y~31gb$6I$l߇b*ިOH@F٤;Q"mɌE A._& $ިkQ#XCg~d$/46:rTѱf8D hy-/08F@bΆ x[ {#t~uZ$3I4(63ކ;6#S=/p8P 9P %pJDlj&"DPIGs}5Pt˷A&@FX/:Nytj8:lB N@Kj4UP.Ʉq.PM#A 芐;VHZB`_r zl#]+h.}싣蓒51$b3u&f6?'AyvhP"b19#}xl$dpH n*灻FL/dߛ xu($ 6c!&UE3|6T; QV0^LD,.'7աIw}hȄC bKe ^e2UgB>#1%"?^Ğ|,.f1`SbD#,|~Ÿ V65 O Ůtg ª>sq: JH!x }$+^M3%/~ץI H6jE~NUCӊڶsԇ5#>[_Y2;@fËᅪ}(%6JYFQ3 T`3 q#if stg&-lLHL _'(IXB]M½(2aD#:rI0؂YtpRr2Y✳8ؤcMtl̤9;(Q$z#E&H:Ss7޵$9i`P_/$r{ۣ{ 4XQBnϗCI%=$E ^|VЄ۷~ jd/x$I< UʼnF"&.lŸM)_&"A=̞DfO"'ٓ9#'ٓh|bchDfO"chA 6Ý4A |A3Ij`㔤J$o?@m~2 [HOұI&L:6if̜ytm+$DH|e^dՐ0q/jz[1R_X/8 <)ܤ{UTv2Y U0sW)nPֈ|AI~,]2P]4@!4޷[7LdH!h6ڨ EUCDđE yPިLtDdoT&șkcfB9dl%.y`뉣͋I1dV%vEdkjL( %o6˒m"_l)]ԒCͶHdB^,AU_' J Nu|:'8L)G :nVѱE\[J a}U"aUeVs$c]T~D3~1DFl.36CoJD:һ֣Sk>.87HQ96fYA;~jͦT)lAMa4=qFqoa5>9>rcBr|ha%[$z\t 1OtΗ8Lj6*3U$zJrj@$zt=t&|5&Mj vsxmQ,XuQ5A!B{cL*WA8m t.B_&(3 H V[λJ}OW2sbșWBag%BC@ν.&¢yхj3 ~f&NbLl`OXF|PI(;G>.Lx?v}L ؁Υ/$싴;qy} #kx}Q5@l3qLxլMl:^g\HwL8EbFEڌtД)435tДAS:HtДԔĈOb'1IhI$F|#>'1I1I$F|#>ĈOb8M&C$F|#>g;~Qx^dv}Q;̝q8M&C zJgJ*J*J*D)NӇd3ѭX%0?L b:M}Ǧe#CA@o$Ɖ~U=񍼔2lKؙAP}cVo8E 5uh#ǖ>)]ͰI;3cgыV`llK߃?Nx?D/fl&E{pQg&@I 4,F)\#_s)t#$cMqI>d,2Eζ$`81cپAOs:av61Q^}4m!w,C1d)\'<ĭilR u: 46\!| s!| CTq:(((Iw>~%_mܹn!g#HŽi4]w5:7m,μpsHqh@Ι#|p{ y?'i%$v2 %'HOg^Ιc{&}:g^б8g^>ϼ7}'t~.ffB( Oss9J ?t~ tGqȰkjI$,gj35]t\hҍGȶvEF"f *DO˄;_ c!Ɨj:bѧ:' y҉s5$0$9rDtNFs-O8?e mݵM"˯8_hu&Jc;CDzd03) 4!{T`+_&Xrn{ { ѕc}d00X@~5r~6XBj9( L?$j>vXğQh S;@y{)3DϘ'MCmf@DINKN 9N`9xj FIT@Nm2x ʌ}d턤='Mtlбs# $FώE`As;蓤$9pn`bEՈddt,`3>[5BnP5q8T/LHg$$ƖT؎'B5>ʤ@wq%z(A2>)`dO{ g U]13)Bām](QsTWsysn4N18'8XM͙lYR7bLBI2)1 9S<&l.%,Feb:L31ٖHE"/H"*U#9ɄI$ &ْ$[ĨF&LN&L„ɠ$]o60̠ӵd+(%Ήh<L !9IʤDϻ'*)Y*a$8^MƉ&3+ b߽ġj^b {2ewLTjQ& E1ˋY^VbVB(TjQX- EMEa(t8T;}t!Y#|(|1'>UԺSg;DO'hISpsFO'h$Z}>V3Z}?$ӄ,✇)_&2_NsD j$]>UgBZAZAY1ZAh|B+ZAAv+vH!6Jn?:60vU41r"Wo!_n}x,y_m|WAb)*ETX­V~R KE^H_$S*W|"{^ ΋UD.]})l7FM%J:$E QPz3|\І"y9>-GHO+Yp^P[/&Jg-/y_m;'v>O|b滬]lV fX/Av'I#yqaT7:H#ՍcPGj#.-E]G_y=G "OӾUGɔ &5(ɔ *_&RL"IJqo&bs\5QG .h!s!5'^QfBx8V-76χL5(A\Ћ:-yǨFsŏ:)h$VG;F~.^Y h=N;TF>d୵ 6*XIw# FԣQ j"s=c$} œ 'HFd(L*:fGFȑG:c} Ob"㥨׋c|R|N/jx䍎%:X ?N}_Ш,Eb(\a װ%LI"!lԋlXRU ؋;B,cɢ͢]:(»_<#Ex%6ytLqS Vjp2{qLoҍ12q;L Cv%CM>h-Nh;>%"`z1( 6 Sھ߰_ŅX\2\^pR{1ƈ5%%J .5 jk;#if C-eX#DQ{өAH6jn OE"F|Ѷ_Ĉ/b׌_^E"F|A}-fZĈ/b1E"F|E"F|#_Ĉ/b1kƈ/b_buzZc;8 cWbY5 [R8LJz($dٔjԤ<ً[5ME2_>#J|mF:+ ͑6^<'O6h#I<§nߞ&󻏯i+Bh2f:&^lǘt$}diBM >/~ǂZ_ k"82Ƒ Q@ FeⴙM{ ǽ#]ί$_!|AqmTsG{E-H}X79츠6uxmT&I&L:6if̜:EHQX)C5d,վׯVbjBu:Xjz~@I%=$jZKW;gowG?J/J%|FHk t0߾ P^6%cuЀܘ./j _P{#GD4OF2. OJmE9eRm d;}$ ue  O_Nbst6og#kQb^)c3>lӎV/grRJ/A^ }/v^':INu’렟$vX(t:UGSvA& I "iG+Seh,>逵v. D2EobeC0CGZΊ/j=a64ybhtjr1[/Mu1ڦ:+-hF5wt$8I$4q159|]ѻAFDC1&y%wȍs#r#.h-\Ђ Z ZpA .h-\К\Ђ ZpA .h-\Ђ ZpA .h-&^L(IĄC0ݚ~>GKn;}aqkp\">;;FCհ5;ybs}a#H:D7.')_}c 7kع^>ORHa ~{Jb#sbN_G1s mT&{Ƈc_}^+f |pܨt;Fx.q*q3K.%7*䌋c^?J֝@ugpq8B~;P[cnEPRP"V5V2a_9pZ$ lVq"YUb-9h4nP;l2u\Fi3ƺSAiXG&hZTY; tkQDb^qwZ;mT:4;B.v(y:nN'fV8(dʋLK{½iA,ȕk~bXnsnncwxLI {-~ŋ$:KH:;jλ_ݚ?^56ќEeiKkfٱ_f#%nJ|F# {>ٔj"j4>X_&=|'|4Pػ7߾%%%F y-|5f'4J5*S's_9N9%8xO )u"稉3"ߘC_`}w`D +gK~ĤII B#'Oa Ca_ bEǾ F#f FSIO%=?:~ y^Q_LcDp)Ӈ̟5A^<ȋy!jEy:Ϣ#:r>ڨuEI%m%qE5\qt{? Dm 缫:Hyh#']I掜L^^252;$SK%nٸGsw㥃T'SflAuCIimF" v67~B*IT?o6To1I"|ټ!sf~t|#v?ěټ }˅g#$5Q}30ڬ{hVظ@Z7FL'FZ5>r Fwr()`#FgV% 6F]A9Q n'S&jvZwhߡ rc T+n2.и7tN̿LZ7ki@deFL;LOCeۼ2z9G&*❟dyg~,gP{Q_EV42{;@n)|rh$Y|꫏*{rH!琎vƂ~rHsci:8=1,>1ͯ}8_:h:NININI0gv6ۢ5ѱAd rn髠\J'Il?gdIWI\H5ʴ N">uIP>h6~D:OP\.2a:@ğ='㼏gd$NJPnmQR[Z[(iQ{VH!+IGsl5j+ƣl%{mbc-6f1ؘZls-6bc6kkk͵XlZlbc-6bkkkk\؂Ol!`-1e1dcM:d/*S`ce싍j)Q`)Q`(l`! MД6HxOLPUu\rبLPg:Tg$J^@ԙ2_~`G# '5ߓF_Ԑ'ZߐL8geԙx;4cذ]:XPҡ#!L@g9uS||6Jw Wn266wWDtlt W홇,d XD9{Q iN|lhNHRm SwģտKDtv^e+f&ZC_54F"I)|VxT2y6/ t0xOD_&./b5j.fETwS]fXdԔ *Yl J%@jz _&q, V\4`@M֊0'QGI&d4;ܯ꺌w(\OsV@_ vn4؍`7s7|Fh vn4n4O#ɈMiF1jX, eHuh4L1od%,`Ɋ̭9(1&j5j&s*h&*YQ#9Y㭸Y ԧWq {sObITғL9w?h&*f%D5ѱly(ٚhy$;lr;hW@9zI^`xjuԉ܍ɞ/;|yН̙|yэd['U#}G&ɢh6.1&CLq\{Q.% b<;YT9bʢ93m_# 1$kwfqaAjaD^!zL⽦AaLS[، e#˜[oY^ki=R$LeC !5[l4fє7$(#LaSM XfkM؅rB69{L*ȏJnasRƇX4zݍPzλ1x?dl$_ɚC8)~6v&]8`K[`/KyRPCC\X苅8%f&*VrJHhd$r~2I2)7\#%>()`+c %鰹D̤4*{ŏ<6n5 "q>Fs~t즙4s=;vSDĔmHRs@IU-K5;}IOxNhQlGFJ2JnG$_(!!ָUϜdy:L§Q$!ICCbI8c|B|GA_TM^߿3G> YRma)|9D%KW{ZᯎָS~! QPG5D|OmOF5W63qM2$J|zziTGtHdLF2FGg"m7j<^ܼA<2>9Fl 2fٱGGs){9!cMI]Ksj(sbޟot"|shr䉼(6RQLe7;8{Q1Wad{4)QqB^_4k$A(Hcb/w[|H&Ƞy`Dr;بIQ5Y,þ)I;G_T:l.ѱqC;o%C_!NU8~WucǺQ*F(u#_v`n3f?Ս FcqfBI[oT:/~%-=.aaix]=ݨuDATd1}9Χ袴#Skj#vGv֋ԠڨU0ϸzs.V wɒ|9,?3~ZT X-V3UD.Ng. >rd”?.S0 c1T;,~`k5C7uP P6{(L>Ǻc%=Pң ih slucL}[4GWqC;0z u >;~|C+0؄$1C+&HI6݆ aaظׁ vf4GG1+-$t*cx0 C<8\`^ 0ɔ̿` e)XzqÔj]%#݄[)`K+2m1ݰI.=΋c[l]I؆!TIu3JN-İXe qrg!w|G9ٙY33=,yYQ|O %}D^twf|6;^#fCG'ber.Ap?tw;irvŹG?X>QQuC3Nt]#Tc2eTa> ؆lmLm6|`> ؆lg ؆mTN(Xd(G~5vaG jL? hQ2{3CWp.؆!!xt`Ѓ57и;иcЃ58ءCUlД eQ؋t` NB\G_?.ޏ=`Tr9kqlfBGhp2؃5:fi9 %I>wQr,r() Ç;8Ac1"Bi& zXEJ JD`a;6>tQ)? #Սd8 t`lf%Q_&aV61;2Wq N hN|'9lLL8wDON RܮF%.KNRq/ʞț|Nr $' Brș! B $' BON! B$ D@l/ !ȓ9 !'!a8$tDODW;ÔX?昬>˻^8IE"/y.&"6?}~,~Ic:_:0$d> tJ Jҍ<C2!u荚.hȠ'"A^2!C䓳OI&C-O6E/%d8\7p}x= vЦzQc2)ɩ0A|qvEPqR ^A+05t#}WPJQ ^A++,x0FbbS|ǺPNA( ^AM^A:C:Bk(x&WP @|L4_ ^AbZAZAZdׇ48ZAZAZ$p~HZP'z= ?9u*?$S2դ~t($b_}_zW%} _%xo)α2yݡS/;^/\Eu$4=j^xL}@em7$JQXP_BfQMI6%oAXJwhl.N;Q Wd~r4ie_x[x[x[x[x[{{{{{{{{ޚ5z( 0UK2 z_:'Ab9L&T ;­f&5NeՌIEY? a/bdE/[ɣ{/V:{Zce *S&$IRS&#NLKn 3-{PrԐP-KA_=`cu=T.0BDk~_~9:8[t,y72qJdv{-B )LkPzuxv!n}ǣIL‚Džccc%vkjc0{qs^E Pd^g4+GvUT4Y_GNw|ؑɒڽnpX_gI~p|E ~?$ݢzP;/LĹyG$TxqzFsXF3}i)pY]$/o?8ȗv~y)_&ޗRC2e/nͬZpOeroIO\:9}ŚJrJrJYy;䏥=9_~w݂wPfsiYy1aޭlqwqŋu$kb-^k0 Ykb/ZZsj-s-ެkRC: Dg&CuRG.2_ΫmHeDFYެ[ًC@zyy@3Ġm++E vH_0A7t5a*m +w E2ED'-ca[#:Pannn']& L-k :~g0vPn]i`K[RR2tp$8WRf9ݵGg]:h] 2z~cpDa)~dj,2wE' YwQ˽?Ce`wxi@9(Hcc\+DG$FƉ5vxkT ؍܍)Z(ܮdnnOM~[#cڦb2&+M w&1%&//ZY5`NMCu;C_pܬ7k !a1=P5 Khf *b+adf+ƅsĮIK2J\h3{Ac2y]\ ZԎ"O^Ԉt|q漘d{//Q&E|0.~Nz,JvlS3=Ĕj΃Lt06.kǒhqE!I>4j}Q/uzҨj3Z|J:;vWg1vpvQ#ml%ˡj :X7"w1cXwir +@1NPQpgxL(QG.Jbg`a-9̂LtC9z3Ejd6`^ќcM:󐂖LfآDlE^XXL0.ӇֺwQp7wq "j\t,b sŁ>$3.R.C{ ^(nnڴWJX459x5p~[1>l1}?G $$æș)a^ x-^KFtq(=)±] JleCN%5ޏaȓr|1ILg)_&h"t5R8}c4j8\iZ@}yO`9fZpܭwkZpܭ5[ ւn-[ ւnZpܭwkZpܭwkMւn-[ ւn-[krܭanP[^Ѵ2ѽ\jD2)lХL5)]\%|ܭF W~«M/vrרY#NjRF2_cFW{bcW3URmv],L8/.\aS_T !j} e>bAǷ]Ɓ~u>1rͺXȋUX1\ذ\:ȄOl'_#'N2Ol.y]c<,έl 62aaQVWȕ7R dؿa?٨i/ ڲQ"SMjm#݋DGM jdSCuaۆA"rlDFuMz:>1աT jmR f16N:ΕƍSScu;F~O\I7q9r)EjY]L5;?)1Ia`/*?Ѩ,攖٭/3tw,0j΍$YEGZ/]m?v=oA?lOܸ@M\B"}G~7.X5Qc?K_GE2Y[|?rn݂ظZhd,Jڔ)IV0jxt,{4G*Cgh-ݏJvX79a]ȟc>o\;tޏr1>ԏ4FG>㨋T; Sf{ :jTI΋3΋NL'}'IaIߌ$%%%%%x?E!:Я3΋7qD72y~dro*5΋7^6^6^6^6^6^6^񊶡[mV{Q%/,^ŋx͵x/ZXkb-^kx=kDy^7$iPq^΋bM^Kf 8/x=x=x=zִ:/k%MRm_$S&ʵ%Gʔ y^T7n0.2Aql8/+l:/ދ5wxqY xØjU*jă)僬q,h3ni˺aJ2x[}6nkڸ7Q>7Q>7Q>7 MMM=|n|m|n|n|n|n܊m|sQՔjo&"n:n'L>+:TDD[HMvDPil#iE֭[>29q^H}bd*&/'G "5FYV#ٞ7>7>IJ $bJqE-;qFD/HQwi۟(y#GE)+-,Ÿ@υAuQKp`;bslv0 P8!`L],lGUă)Ցf/ي.֊E"V閚}wjKHuW25)ƹaL5Dva7z؍vgFaJmR6n=s7߭FD<:]8F,6.R]&aWzLe8Fa&a82zd݊΁ jayPAO07IfV#ƅE>}:w .nޓf:ljjҷu#l&NEVC uQtۢxjc\$3/[?PԆwլ󢾊ͳ jڢ39ǸH/h;Tb%NM%15bzwE^27 lLqkTl'Fub*SjJ&1}"f&E 頤ƙ_Xܲ0Zj'et$ ڥ egXt 1 {C̼=%1(OW;sժ9d"t`rf3šͪ1:Ю..Ć~D=2+I_"uf]$VDgcQyalvp.|q67U{Bbo^/J8@^;aquqdr3Qu5_cI${sCzڐ[e]!=]:BYok1䦋8ځ5n.Je]HuHxK3WV4+]$S&|Pe,Ek  GMtwo0ܐ{ XEGx@ɑ 颦EC`.L۹NeEj>0ؒTH\s%%%E a9M})e,r?ĂXp{tĂ%8=bC=P68̆)AJD%)1lMŐ3hqtqrTdn2 RiC1Zc3- yںz7?P<#y4^R!x1ݳݕ }l5rTe8L7l|^xdh.% 2eeK&T7.4qLTxɌ$M| J(%gw~ B)YSWȷw6RC&^AQO?X]t($IfoyBnbln~c/]#8ij8μL}O%MjTDLXqs"*qUřa//Huƈ-vuL4>h1btQ B RS3Pbl,IqeDA(/28RStV W) ZXA U8E5YJWlqtQnaS*V6Y rhkm?bo;x@㽃 ꃫRI]|?ES].Ue9Dg5$:D;D#R0*"keœY {mfJ;x9+\%Y,JKDYeXt{ C~K:>e?[.nx'2Pmݖ8;[G2aL========gz9x9[DwNGy=K@-(eJ6a"""Ek\?A<#>Ic[vLe8{00{.f̡!QqXNF(f!܁:rvƈ-\h3F~uĜ]c:?#y"N J(eC\s C(e2YA@gJ,?:F 8,j/lǴ8L 3"4D5vs`3")JH3DJE_D7.P5dI9aߔp~s6;mv2s2% Qptu5sQKa:.9vfÒi38:BEjTH'QͺQളG ܼ?JobڍoO'ʔ%ÞhH}Q1 L>85;MsI75Ԉo#q1@ d1枡Z؆5L:lupaspaspasaa"7/'p]LєLJDvZ4d֨eqt,[eF8c:8ZlUl0A;&p[KIuf}lFghwfgiȤFYL@uQ5`O=@iiNDȑlNTMrs\o;GI<:ru5g&ćFb F|5pp0tL+ct\CDDD{BS ӸAdBub͚P]ġ/cFrtq ݨ/:XtM3x\)IfEE!\{Z~4Ĉ|ͧ6 o:tS]QX\FQROqLNU@ɑ6.7g㽓)5.=N #0bC|N܍겮1be65LԔ5D C ȠF7;6j<7]d9ۍK25Dt`0RuHtQ'Z:/P<=)+5Y!1'F#S>cqӑ秿LE̓ócl%STb֥J'G䧿LO("=Euu| C]6%30!LOzűDq$>Ɠ I,9oc]eR Cɋ#P}?pItHd<;L35ܭFjR$ ȑ_&@j$晚.:\wlGM8AEy ],)"WްQA }? `$]Xq[D{I` }8T'>|CMP3?18tܡ+@M)q7cl\ >, Lgk?߇dV#/'jrcN si21zV(CPc?Ĭ Hgi '^'SYH#Y`3w?&^2M髣/P83ANFۢز((:0XFGN>a*E!/Du?~ JJ2K~,w2"R1nyO 8g\k䏓Ibj_[aD@/=j ~&AIII3bJ@g^nPd0DZN|ȋ΃Sv^d{QRX>RMHlXjTI$ ?fv^_."sG~6u El‚AFy?z 6G!SKuƷ%\*]I$~e#]:~*IV #EwFe!H<;LBr4G2gGm䙚*Ej8.⋇?>$6th}*h?--Pe0}d:M頽ĦlHoȄO'W:͡&:Z~gP'/9ɵ TM䳻MO!x1j8|(xՠ@_\ =:֙;r}drH d^6(5>q0[9A0w( H7|` ŸQ7NIO47DTL(Qb"%&%(L'f|B$OtpL.~~- ߅X> /J|ݸ)lj~/J>;ٔF[!E ,,~dBYLP[n8Hl䏓c?F"cfk4h͎Er=>HbJw+$)L.'NIq7f 6cO|B4+( }$ŌH!?8m 7ŋ4G1.g@(~_&E8Q%a(*A`Lhk1;O)}kxa E>1)>u1^9Mo IDh,j,L%іζ#'']PH̚$DE"I:q=kp.%+AMu#}VP["m Hb͝%+h~VP#V(Mv#|wm.ҭM[" Ҧ/Mǡ%.ĵĈϵě7MYM)bֈe0 }-1LJ>K1M&FGd7t<ըdQ[0B R[F hsm? *LyqFSxz$FWr*τ^:}GA c2[.`15yƭFJD ?Hy) +01 םDN0=nd$gLd8v$Y :kb*- [Uc]WC$> Z9tL8uWFbdBm(2:8#/s?6薻mۢ~2e49 m5`ɸё OhT"ԍ˶[ Ma?_[qÙh{^㏓?֖W<2ٴ7mfӽ7E%Ź3JZ_850ۇt#Qp:ч枘h/9ύ=md(J,9曀І8?f<تQ5ȿSZҨLqL gZ8}6%F'5;~c., &\4gEm['KdF P7;||FLrMm\q$FN$#q4h*l߷ 4>:;D 8dm &;$: 4oķXj}Tfx0lv#U ߃LLc#yءsoQQIcxw,)$(ݹ6c_I}N*Flҏ#`e*xL4ظԨjvG v֗%!;hG&H"i?pl$jT#;6lhfg!r`gDvvăd;(83" JQ7*`#:1fbxDRI"Ɉmxtl̤9;6J =EE:u1<ɶV b7j#?p7q,fnYxm,\:($QQJǾن/w3r7C'w!V"wlr7,ȭFUw:.l %|IDM801]mT!2{;Ub!L݌DwgBR#:7/P?(v۽fUVxFs͑^$0tLT :8'e݌jtcCg3$7ϛͦΆ `3t6CgEjfl6u6CgZZ.ޠflfSg3t655@qvMٌ\w>M.5é% :l!P^ 6 H6S6i$253Q27PٵB0qAfs>TПWJȴfܲ2 7<]K1#CM0Q1؜Ce9nM.N5t" gEt<.9@51Ũxƅg+Hsq싹 qX$8Lg sy):͸cMnQEoj\1(_Ԃkhԁ goT:<}싹Zӫ&%J:mv! $6on`B -{$o7n1 1}\=C ru 1Id&rҨmX4_( <^.Q0: ] {ٱĔ(q"s(BK{A;_]K_ 'D‡@)L.+L2IJJJzxB;k'/shDLFd^1!|wo&\'1_7IL$рR Khqnq,PDH6зL(#Do$v^ijgi?05S^8/:ǰOIr^/Rc$Z}#LI}B-q/lJ6-Q )Tb׸,fAh< ,IVwJ^&.9+`ՄqAQ1qG"$_=CQՔ3F2*DF)CLhWF٢kkQ] 0tKa.ȸGM:2S-6UŦ &3ⶃqpm\t0j7*=X5toHF~DBbu:S+lNuŅDD{#SA)4%n=yX˨O, QG3;F6Č2j#g>̆:- 6ha>rf$!y) WN(9` ǫ0H^ N mA-ԭBjWaUR栋B'+M6nE4*DT0V2J9BP{Ś*&' fp[¸-aܖ0ݖ d;~qX)(uRCژ.t?Q?~4^7ߚ4.]9UC j ŭ? hIFD敏|<4ƗN$m^\ȧsk1G&fH@7cuuuuEҕ~߿ѱ"õ'qܒhP]Ĕ]gM?f<ܖpnK8%7lΕFJ"ɴHltl+@!">Ҥo}O^E QQL >㿾1\h F3l_?>$6&l6F&*rz۱\pV4*>+:'*G|'?>[vSE:@uV8W+'-^./D?N~m޴yf:N+>3D)qSp.J1QɓE:L]kI8cj` :#4}= 1ڨ1،fsM|>3z@cO}{ .Nnߟj `!9 Ts~hh$ 2$t"^|F&I:9踜$D$>PR2Y\̥b#5gq1؊Y\t0+UxjBu,Sf.9\'g$ ]os".~^N-9tnA\d̻A$ 2x,tPZcsݧ*iQҢ$VEh SEv$oÀv: yYuhy9wPZ:!pB4; YCXi!BBw[F_5)/OtTe|jQe|3|bWAX-q:0$A; ѓt>>1kqSt x` Da뜻=cN|esAp 8w;> 8w;sAyAp 8w;;sAp 8w|Ap 4PKA v@&[5q=-y+(X uEe"EP"bo?P0>V]>Pl͚x@4 ?8cju˜بtf?~ܼ2#^&H>_b%W,WF;$yӉRѨ[DzAɷ^fle7o.oW}()-vJtJDm0Qo$t-sMuMxo;`$FH1f1IұI3Q6F9;ɵuqwRbR\;73(I|#5lT;|S->XۛY oGl?qN_M52ѩIsP9|G"Y"w3GLdFi{Bq4~f[a{G8I>*D::`u"xax#g uaAWШFtsCMc_|8~0O8FqdC1lYd~?{à 팰Su* iZO*qJҝ|?"n4*3LdGa}XjNAⵓpQ@F2cfTFc>G,2?!C̡"bsEFs>hrg\ooIkFDB)&_ Ww6D3qӑ* Ôɓ.n!tz ߉v6|} w@>)H yck[};Y?hn9dׇ{@۴%0Do6'$lp4+ټ}j*nl =ư5pc(1c&qWJ: ӇaS2i4e2LݜU ↼"PU:켈HbrNpWPRPRR;,XnɠCV${#A4vwQFujt~OÉ>BP&x1d;zcv6G}t rwXa;,w,wrwXad;,w'F χ@AQ?3^J/ J_䘅3}7R5@XhcNF1>H推i"&|d`r@PNJvqShsӣxl&l{;%T8[ҏA$xf(kx _ C ؁ŢtdԏKʼCoT:NbuT4E =cb1!Cw(= [Q((N @pOlR)`Y _ܼR)R)#.RRLM)ДWPʼ+,v^!N0vc cQ ?tL cD@D4)t~h`C"CjȎ+8}y{W S 4@(<} N")( XzBqz.jVNdQ[ u{ʇC8F$:<5>KONu+FDbSDJU {,.n^JN]bs~؄V%+eU&Ub5EjM:ٙ%;dg&L%Q3P)CɄ6CH U D\DOBH.41X$M݄(hT9|8|8\ٶ%۶Խ! FDz}K?ShT&g"|*@"|l;.KF `'xZx%‡C(F8=6RJqbINe {, .J&ԥe'EGpz0μ rqUyg^yNt/μ 7ng^řWqU3/L(3.R"{Sy w^_dЉs~FEhcxؿH9;LNQu7ބ'ᅮިć6ڬ}Nȏ}#./{qfB\CF5מutDGIh${>1ctSXI7ߣ9N:tLcf:ٱA{\0;蓠7]##7?Ѧ-&PIq~\%%%F q!V|[69 q5|U>*x^1^9>^W^d$ ~O2I2)_ɢE[%.J|QMgBIҁM߯l:T?B^ǰPsH6)$N*Pҡ$y6͍㫝>HPbDW/gb-q <9ȋy / _:NIqu|_))))4`Z[F4}"u9:IHjU$d>0wUVjneZd>Q|j$]VK|q߽юK"㥃&ɔY<p/jYMt(^.fbaKV bjqQb-3fiwH&A&,"%2waiY{; a)E áZ \OP/TPqi֡0x|CIFIFI yb-vYm7#LǺ Z./ V ZB+Xh o) `M`HIm1*\\cKPńUfz%B X:z'/2/ mL;!pl/J ޺Fg*HO#\Z)5 ^<4=2phvk@x'{2~Ť"6 }o c5ٔ)iSl)D>?k\I6|o_9[5`!zu$ʷ͛ Qtq>1y'v>O|bfiEI,k4'}HYwH^؆+5pZ89nZ'r>~hڨHq֭K@k]%o]7DӋ_w|v$tCD$҈=cTuE>7!6O)?7F2O\r"TdB GMS]xNE8jy`DLq `^:ȗыt( :NFMЁh Td(:'b!OGݨ#:&8aOtI:N׀ڨtEj[V7*4c|4w+xqvPcaK)1x39&0ÔjMClQϓm5RtzgݐiÐ؍Tw(OlhJ& IDyO-j>Y#CPVȶ6LLFΆg3v;x!;@8g~+`$NS`z??Asteg2ED pU9Fd =N+I\ q>*zI@8n$sjÔI-˻a ,~bևnGn5L G.\>>J96Jr#/5q8s~n܇$j~89Oɩ~tX#3`+=5@g˩ <`-7*1:["-rlr^{9`/倽r^{9`/d/Fi8AȝK~u_0@؛v@1̜*NVՊ#URPt„VŨe>8)]8(NE:-c_FȆnj>9i3$(o7*LhUc_\PO!| ^$n/j7ZW¾%%d!|eM`kLY{?([hJ,6L3J}}u)`09}ұ:󼸔XބIa5U1bīH!-5>5C۴sD&}&S{_?.hE:Q_A^<"U^T3蛟_GF}\I_O_瑵7`J'suQ7ǔE ѡb9Uawag$;Ɠj BG`Ցr ((DE2Gmx4wK_L2)QjE2?!I'<'I7f7cF2/N0oB6whSFm体)CcXS̐s2jMkLbM,=_<$HGGx|`%#5鳜0鰤dn$11d 霯QKjR >)DAQ-&hujΣs|l}XB-k\fO\Eh/X5Y/ e־!ӏK %P^i#U)nBO?v8x]EbJQZ ;N]Kfv9%޴yf]:MhD63HbJ\ۯNr:Pv|5L#(k52 'L K\I7*QA w"h;q511l6cYGSfͥg.sPq,7Pr:^F~1bL8(v`c$ ; i3BSZHДԔBSZhJ M y d욚^/ =xN@'^szN@'^/ !=xN@xn|3?ձ[nѱɵQ6Ԗz_&%Ffǵe͎k-8}Ib}͎k#6;FPAEIDH!zQ_M2ꢄ/jkwl:  Q5'T6h#l'kQu19!eN9'lg|Ja/R y8y?᳝ն6;LgB,O#g᳃6mU" :<O#B}DlH-@йO#!$h$ݺ֎XH>d,ޝ՜ٶQ\᳋e>q8xN<< |ă9l_:!cBܣ;)uu>sA|&'gQҢEIKJs;%Hg"8rγ&}Fs:gS¦:X*Dg^^&h@Gg^qsBo#o&i8$HDta9FF[Qrp܇lj3ߙW§oT#W_԰? tI;0m>O5{͉AsAs#Ȃܨ"`4GX#h9ȋ8ȋ4QqS|_2HbSV|_J2^L|1'tp/~7:L 8J" <f6~1^eX~!*E#}HSCPZLgU2@a,q-ŨkjNBnT#$qZܨf*WY,f|DbstCɁ0v%MğutҔ0,T C04]I]HY:i];CD"I}A61Djէ MPb9>ǐ>rv;< vr .Jaơ=nTsfY;!i69ir]Ms# $tqvڍI,)s\u:ϫ*I:j&j|5$IOs\8cr#W9}q.sBdqs:4Gʤ@c>C f |_̍>t{ Psn4݈ ^nT&gh[Gθ#6DQVDDyJX>Pw:T?G 2}{ ✢87,,ppؠ$O͍J|q7?]szO k9ӈ[FpL"pT /NͯÖȉ[t$xDnK !&Lc?\#rBDn, [<7I BqB#'N$ZAog8Em$vL:X&S~?)bmT_A\>K$G/MwTCALP(C:ABmwdJg"2$LD@[ 2 S*$Lv)M#,r<Өr4ogAOl)g;)op^L@o,!}l$3(s JvA!P_3sן/d"SHCud:R7xM&Ļo7wĻo7xM&}9&}oƚ`x(^ RbIa#xィjkuA+ݑ Vj#/?H( `}d}{{(cދQl-]jʰJF]!E(uz1y)8(!)Z^b &h1AE0˄YZl˨ "rbYRa!6G'aDPPA )4bK"C`.e_wTk9ŧnBlT&95g#χF 1NPK).l'6*s8]1}cठͅZަa6h#I`eU&@zI&GHm;.}ӘR:k6旉t/P=@E㏍ˬ»o uP`-` 4]A dph˫5b#լeh#6lɄ y#g}{آE6hn2H\NWP(*AAuK5[)~A%?u%i= N٨߇sтzQ2CA}-D}ID,…pBB/$ף}L#)Qg)LgMIKL@e9%3ceݼ$pz:Pzηi*CIbu(8IW;Mjd ŭ}fc1ct1:Ngw9⧳%].xF9:N[B(꣠&RJƏ1u}5ЁI#''(YZtxjs'}_ k᜸6njm.\L̢ElfBQ5N/~*h_[Q"`>XB#'x4(Q^ԜYܗx?DBG){II~tB,4*1GF ՜CB]mLF™q̸pf\rf jA][CI5]]uG%J:$jZ[㫝_x2.ةi1(}f#sۣ9e`4C۷FbA:3HZlܗ2]_,y?ԭHgD:#i33KWsJ΍2 J ڊeR9$V| j%M}2As#O_$v,Z5VPbx\x<.<qMDžqx\x<.<Қ u6bI`A(u53fEjX_g?'a)JSwu֩E/{DD҂Hzwd>NUPDE2mA&e 5cUW+XmD\ыd~aluKhzAʱQ56|r/<7YNuVZ<Ki:?NklYz.賘5 Ncb6"mx+P~t,=\Ђ ZpA .h-5\Ђ ZpA .h-\Ђ ZpA .hM.h-\Ђ ZpAkrA .h#T$҄'EiRGfڮ S>sf,%E9O)7*E3؃<9s9ы}ɦ$m~=pE/~|@Dzs{Pb9P<_m%VIsp\8p.77f Bᘹ^9:)|/Zb_99F%8@NqC?Y5{w>T~氟]r. T&☗<$τ%%#ZUm<_dأjdaDmYlYNq/EIEIEI"z\'8+o.j0ݾ+_tP/4bP8##j~ 1$&bW#:pFD5%-JZ+)݈--rr [vL M_!I!Qj=t[xu(%&x^$s! %hv&Gib0쬆iÜjrf|eE1ˍYnrV cV y g#Ls°ZvL]@,hͳD˄)o.)#ދZwnvq;`PAlT AV:@YBo݋cE㏝?քT[#) Detq7Hh =nsg;̽zvQ\4Κp"z7܂٨   Y;>n w6t[9 : i0N9/*g"ph ͋ڋcyY5%D^ COj{Pl039ex?*<&rq# /I܃gCwl.:V<˙΅sPp6tȖs@3af"'l^d"oEuFrl01ˋ(fb_a {X00 f,`` ̚ ̂Y00 f,``d` fspK;._ ٱΨ6_k)!{ulvU1§Vp"5ث;T1//Hxomo/㎂Yly3z\IHB~B6 e5D8B1r^Y %4=1?;8\-X1>($v2{Rr~јU4iܠV`bm4:cz ^9m,~- rdsV6HCbm)kQ9Z4?&_JSi;Ѹs;9''d<ȏ&2Q $\ ܛM reA,ȕïEd'jyBH.ˁr _JnRILx2"ïEƴQ?bNn DʙW#;3iVBH̆'^+c|rOƚH6 1<`|3B'7T'u\6&Lk$7YmQșW%~ָInK1 g7'@{@; BqP 7 B75o mA(nc}qP |j P+ű磅hd8 C:~p(t|VMCpMN_h /h0yQuTGD8:?~.hqѪCp/ůii#ql 2r2r'+Zť"E۫hXJ3AFڊ%yh{5_ 2 Koܣa{T}H[mߨCjKUDx2^:x?0wZ5(wN"]S9g\_ukl<|i j??cqojA{owhI$յb ("EN޷׍dRc?@Hu^Sn>CTLfl~™K ho>G]07]lt!f]l5Q,~LM(8/7H6qFn .{2AO-o?p*z55>!\ZL]LP'$H| m띘B h_u_@$Al-E xkiZ!66uއ/^} )vBN[;:Zcסm¶uq9Z'R|_C#s.#G`\Ffl܇rz`4ͩ6fH֥i$ZNfxHBrb.fD6I0^/fC+id:$¿7Kz'靤N;9{ȩ蝢w)zȬ蝚STTT-^kSc_Mc!Bl⍺-JQlO kBf?8{iK F[ 8_H7m, ?!}69MaCHȁ7mZ#3m쌒999wdbSM\KNu;FnG{f/lśټً܋7{f6o^ً7{f/s/śټً7{fo^^ً7{f/śxo=^|~҃|~Z;Ӡ`RR&A>_vvÞ{^/$Mt PT7j9F x526z:3G4(a!l` r C08c "uwP?ɠKO`=5)~Ja"Bx1PNxf/˽յz%Y–P]lV{p}լ# OȢkT c"U[eVL! Cm^(؞F$xOFoR&r6_HQөzφ@,U$\쌃E(S\fXd *Y%s-J? 1%cfQ%d;a.@S QDBj{9k%a.Ҋ`gtU4FӨsuN>O|i9:Q4F}FXDɏjLju, eHqhΩ*RF1b L0g9;3|eFhsWQ#ՙSAQAEj=Pgv:x+nV {scqTMP럣먘N2beg0PkMzhs찞)?PI9 :gZBh Ȉ/zc0ts&^^tK-:1fOG0ؔ08`z)j}~ 4P?Ɣ̙$0Z$֊y` nU.$9'ƢcMNtap*c?|^|9a+wEdѠ\1KBq7E lLM*\$FGLFJʽN80@p qXBS \ a/ RaT`9 Ta!Fl"$i8Xty >0?.)sΝĴ(3`ry 9(z'-84G0ظq Oa- Cq:&y'#HJNΕŰd\W*HpmVڄ@TgO1~Xkbtq18 YY̺KfĚxb5k{!DKAr c~'JOa'Y$V%|rHV&t>|4Umݠ#_ >(jQ=9$jB.?!IbDʭ}(ܔ_%w7?>$>$$I}:Y7KI.u,C?It39D|Q% jgl&/Znܾ J;wZu!3,q0tSg*/1#&r B/1m#f{Jkl5c3F1)$HDݱ=f$,9LdI/EI s3Ĭ2 ,Q&D͢wPIc]ffX9J8H̢E Y>![/5K Ka&\\?I’'?ĬGS-]FLd=χ)Fz,' qnsoVrJ :GQ_Cl,)b#-l1*fh%eX-~!1YeBZAme 9ld± W50\+_ U%uT\+^qZ\*NHhV(%}Ğ'{IΥIg!w?r["/X+Z;{HkGH}L!([Q?JzlٞG:[ aߓ-Vgl\Fiɍe#w#3GyaHۨːy3`yQEw+9QG,?FlrmagxSMVg׷(:6gV~`{ckTu]}(.2l6c颽> ~l|$'19C~60uTN4ED 6ׄ{X3E~l>ƈu٩s0XuAq!A:¶3V#+¿FIrJfq2 dD'#:,N[2fE{Y̊,.)fѰE5jlXV.lTO) &|m~id5rj׋q5zԅK*8-$Xχrz!'qoxQg}^"b!B:cYۛTGgv18'gBE5|_H:CL{1ȉ-͸1mˆFX|EUhEy;?"9j)Pdcg9RX|Sgb=l{Ai^Db%fYb]$35AY9E Q"5vQ7某d4ps2gX J6iآE,YaY|Vi],>gź9Tc F֥px~l^g ,>gߨtT|VrzR2a(A|z?ۣTgI2L5JB?$ȁgvx2k 袊⣈Ώv{["Qq !>yyuڇdlr](*ins~ly^.D*z̖}Qb6Jv6˖mKeeSjG{P8*Af2_Dxfج H5Iz'gpI$N;IfE9l$'NXȠѰETp~ T[H v9=k|`6jp]Ga)8"A]D3+~pj?:ʻ:{HQ9aYAiٴ q_fc>wΡZϔhq 9$x8G9gJJx>sX'6rL'1lQ"ߣMA@1AHH٘h0#Cs8֓J ctCrcЍ|"%yQFz'RpXt.j9zۯXC,NĞ!(r`F+,NF%2slT (YOFb"bmP̝י׋]&l80@~Bā$PrQ+{#n s3=GjC!ǔvt?hAnTuX!c?rٸl5ܶ)˦\~6&?.GZcY4ŋ֡  /Vi1#ˠa2N`l `t7J)+)MMД MД MX MДljJd# sF3}b40+b7j+F|r"BhX?H5jlXV.C2)q|ňoJ:q2lƉˊ3nc8'.C2%o5E_=~q\Vq\1? ;(|Xz|[b,F/ܺ9ꗋQ՘CuerlѾ4^}{Z}SM Fqu䌮ؤs2sNf>ZR3,QďR,> ڱAb41l Gɑ|G"lnCu=Z\oGrIwXc/m$gm.W&<6Y|oL!9RW0w^17YW7bf?cI` xqzLpg쀁(1a7*k6hܟ\[(Fi3/d[X/" ow|D`!,ā'+}r ro~2g4l`"I^:, p/V, 5#nQ9aّFPI50wC/v p7о/J+~e$<_u`t _@{2[OĨ{Q]ϋDOr.G2#I$y;tgIV'K@b$*FbJ'c$kd vMSrӔ)1$7MMSD@1KDΛdHF9$KEjBssNn$(E)DDHC{}Qc 9I 6hX9EaYOsNr$G9/G$9_`✓Eb NNrI97OrI,Dž~߃ܧ0S]u C~}OR^ (+~HDO=hEu #KcjA}nƏ ZgLqM8LA(=tz6uQX΍4/Cq~J/@b])h4Oj1|#Kqd)B^6 LA)2%1)2s,槜 /0{b(֋b(L"RQetM&Lq$)0ő8F4R0aj2a &L%9%9%sgOYS9QQ*.=OոA(nh0yUt9z$v%/ IX?+O8BJt~Ijru%^?']8T]?YO_,_'dQ\Y-UY-OtEP[jѱnoXVTgX-9!TK۴NC!G.!G.>UO^C9_,ߗJ(NKF@JuXV"}ֈV H&Y H;K;JY0a:ANPI$wIz'靤w̒;INENEN:m_I16(-CEut}y$Rpu= C1I%鬒\mף m$AW?$K# G G͙Ϩ | ޴F(^TۈE&0şC렰mɚtQCN9vc2 uE'bcT`"9zlpu=fa6+{g-_H]F?@x.6v=zhnBbG{kOH'I6Iz7}QܢKu]vqU(q1(cj%ZRc)zXK:Z֥G@KA֛>X `6&# Z~?P\Y#J(T?!RL) BEr~`[bs܂"7V >Ȃ z1@#(!"^B6:.yY_ g#BKcQZhqq4\S4:n|cTYMASPmD+0ڈTˏi[ZK$lUpFHOr.~Dw9`I$%Fc$m ֋U'l%c}`WtͰl|f,k$f U-.ڿnkorӎĄ*ֵD#89=yɩC JkFX&~>N8H"͢͡um9kpJ]Ubˀ6fڨ{7B΋:kBhp]S\|MzHbrm=NNC6 6>TSϥ5v($6k)bk9Z݉.dyt`9O\],[SڋA]p+1QeX8Wc:5X{1{ZOHPg&wO"osD2H\)EHAlP]/j9?<ٮ9v)F|##ʈl,. #?аdH5-9kĈpQMb-ߨcĈ(E܃t8p[iF@~}BbU K,rZIlVO~ kg}6~ߢj:]w04A:+Huba1:z&.|6n3zPW#|?Qq:c6~ѕ%0J ~yF#gtcVӆ=. m1gX qԙC{{{"rDuX|b7C{o~}/tncU@UN\PZ?N~RPs)`(!E,fQ͚ strY|DamTf9k.5=^nLa 4~l^PWuuՋd ͞u@N9=Ijҡ}Q5NHua)|c"7âTaJ5Yr 8N^drcp|1{cz#WB8y7ꉰшBPW3rĤjT!^A$~ŸpN%v{$1B1]ysXC1ѫ1BD-Â`ZeA(^녱^XR owr'/Kz'面w)2+zfXS',+2VŹUyܪ8*έ4@$]I/8nU[ç<` z3,I/jp9+Y GpG%g8 V|.oبrŖr(.&U?[w ‰qGyB?6;|i;F"%8vX%w/wsG . . .krA\t]pA\t] . . . .\t]pA\t]pA.%pW3bL09Ռsq>^,4/DiX9Q5B.ڂ ֓s 0ыN:D7!')_ wlp6~ /[%ruQՊ)ñ>}[9/8/7_4ҩbDX8f^pBWl}%J .hrTwtT >0] =thS?vgy&8.y.?PB1_'ȑ+0r4rG"šQϺFU#AcC]F%h#Fs/jgQEȽPB@wwApo VPhVPhVPhxm,..ݮⴁsA}7돚C9/*ƒE5KW/6/"Nj/悮^Z$dK%- 8]04$RWㄶ(g6|8ex?*JFMelHxHռcza,XeAb1ŋ PlE$c/r27ƋT'>o]́L!F^l丆X:I态``. 悁&s\00 s\00 5 E&UZqp~.=7 .4>፞ E&&VHf_ x<{E .U.oTa w$O[ 6`B)!pR&C-xexexeʺ7YHzsP\FqEz -Bti K7p ުaS%pJw`m ?KA|! N!I$ZuGW_M47=w/ }v7*E UT_gC [\ dې)_΍kѮ7M7M7 rCkaXeI"uˁF Hޗ4=z(эFHjP9șOxFN#g_lBMgFu6 iMòD)je{ †>2-}153V%} 4i!_$eg \_'D$,tJy}q!|=:b^ttt6 GENix K &$3_'d&MNኾ,Ebx C}ˇjiqtB]o{)<bZ^\K-pD$!pATϥˀP/~;Bɩm FXY"`K+^kY05_BW)Q2-iT,x1T;m&#^W`ȔlvO WQj:o\~n\~n\~=xn!Ծe{U!W-C9=x*w鏚{Q $L"| rꝍVjl=V 6ZF+h(/;S+h&۷(?vU됉|4*Sa`CU8]bꈇ7Z nqʀM!!>Gx\kTPƼ+tqݲ7cU9%V9o8IZSNF*ƾ=ŕF }uo-v P>(6gjbuc 5 &lp 8»{r8$w͇|3={*ܹ\ToY_|8ljEY| j-Q^}Brg(.6m^ԒpثQbk Aw?Gw YkDV(g.`X Q:mE-O,M齩t1It=J,}E 4=_|FI8E+p.HIewmxH6ܽ :bhij]ìz=5SIw]ɏkD.6 mw{Bȑ QeEHfl?GUG)'=jTBv@c5Yo$z#qyQ;Too$rb'w$c(`ijFrrhK..fQf9nyw|j9 ܜ zм-f999Ü MoCkaògIu5pm:ņmzJΘlnM[ .]]%Zކwy19뎼A-h3D_brw]]<|Pf\8BqߤP oPLWXLq\5^ kӻcz=i&'ݕ_$1f.z.qŸ給 jc:^fg6üf3\x[lbvbb{1̺`0ͽrf;{܋ًaε|6a/vb{{Хḫg*eqrtHٓ]7V'/\ Q [Z pqx8eoHo)c!/h]Tw'?JE%xm?!Rv00{ Qm RRBOsh {l覺 2]Lm4ufGPl)K'[`7TF3xI. itω!F*6wME?D l!j @/` / lr4աm(g9E}j֨/x~; ^o_K4x*r9 HgO/)~E)jE4E%?o^FR&WHQir%OP\6, l"۰6,=YdنEamXdنEa"۰LoQ.HIG-؟vf:%c"_ԴHEv?Pgh$N`Pc g~lîqHPZ/hrMNp.h%;_rMNp."hhf1b[ .TS8]Z#7x@ w&X1 jgl=0\Ŝ"&uqܡp6ܬ-nV~ˍq F4{r5S[xHIm{jSaJͲJ6YzuQ㧸\@ڄ8РP#aw)&wV ^W &v/hűE,?XR8e_Du~\Wco|5J(̩ (4wSΡF>/_n]y[͖@ww?K?jI^#V7a!|8T'Gux~\J{^҉ ;#BW;UEI^F):h6ǛߑԀ"~59hm!X!\Eˡ$z~ '+^؇f|^a17*'F t{}.D񀪖.!\wrtƁṵȏ}SgN4(i䨕"9-L;$dIH&^>|jj;OPn~.Rd%KKo$cQJ[1؊]]l`9؊&Uy?fK}o=%qq̈ۇۇۇۇۇgF>Dn?.%DԴ3<1"˝"GV6I͍$rBt,c`i`VR),~t@8ۍa74즚jٰ\xpCNU-#!yt}GA`3B YZFf,Jwgz"DU:A)H ʱQ56|GW*7=e߿BT?[VFkl*gI)%`blT l5]4hB09a9l]S0f9"cwH:??ru;,%ƾ㋜9-rCN:"sJX7xg݁]xQHI'_u*IB6zv`6"$|XJP>~a~,5 Hw0}$39DN4DyBOn(LJ}ӏ9'''dBYlT6m/΀lA4.ƋƏ0uf'j9%ˁr m2>t٣qרF`:# ;)H)ç+^1x'f>u 0Y"pwpwpw2fò^i]$$GtdcO6>u.ʈ=n/*_dIt z CQ?! "1&_X C ˃N)Q:<^.c3>w``6,>x;1MqF A(nuAyP/G A; BqP C@lT d,t b=)]"pp(u  5T\\S:^= a( \],;mR 9;0*)I/:~̥bAbѰ\3^]:, bI?! T`Ut9 Pvq=Fk_~`_ 4OS?!:_|y@Y~Uߢ4p)fy#FUG*~4F TKFF |S9Wp9W`01U+0M '^bDX|;3VťIJ]ZFf;FNFNFNtX\ywjh_:+hw T#蝠wbNS;A$NY;9{'))57I2OH`*j*pCH -EuS[ cc !J`h39"v|ѕ6|E#9"{ŗ,~ ֨cÎ&no'D$l{^  E2;yFI9r:$9DԀT|vj@(!zqq[B f:FNox3 y< y<{=gx3gx35VY<`Gw;iDX} I5E- HtŠ=HwoTNEE`"TEDȒn;6}j)Aq,K2l?-L5\ZL]LP9,].RC3A8&vBt)4AJ*-E蛽ogK~p?Jp(!_u&=rܨ/FHl g>L6a[]r`e:83h{m!1# `0ͮ rԅ u5%N)|@IL̒񔟿GñplgrlzwяPZ öͱUTGi61Ϡuط:+Gv/wUc;[NwL> IwN;ۺ@sJ潟;D nWT )@qnSHu^T[AϳrTxnoynT6™k>!z_l3zi ZH]uGnyP /_uermhC(|jƈۨDviGLGW]rpZ_~sl;]d͈5cHNAN+d"L Tf#֌]Πٖ)Jh"|ĚiH$^͛ k0lϽx㻮}-I{1 V T܋aa6èk_MKh^|^:W^|؋{a/>s/>kPXKt,Jyq:`=r\ =N^s1$8; RpğR魹*Rcv֝X;{ 9WV.;3l)LcgYJpfˀ RR&A>F5{H;iC!'IgIרE}|IFORg&ze=L4?[5 ˉP 6NmlENENENc xNi[ g]Ӓ~` +ew]#!$I)nXh^5l u!}ۧ-+(gL1Vcxk;/#wU6+gFXUfD5;e''DFt(D-13rd6llb~AIvXSdBq"87HQө9V`,2Ef ""3Xd`,2Ef M"EIQ^ M(;fV{+J3v>\4 `?tpC.)7- qi.ܠV<|1 F(JiiOq/؜FӨsyuSDΏh TS~ƈu,~(.f!g9R?gX[wtk9l]5SO-.vsa.BDTR$9 AnW#鴲弥űE {^iP949{QzBDIl$ZNfrc r'MZcv 2)\ecH4j8\IqߓXs1[wn-ep M2[wn-eplr 2[wn-eep 2[wn--Bq[ދ(FNEͪX J(/"|}a{=gx3 y6 y<{A2lz35G6A-/fy1 n8ڄ^GP) ۠a5{ȧFS#A2XFS#M|FS#2\Oȧ-fS#Ek5A2hY-DPI$ZAfzyq;pcz>cP9J3SIEڬͺHZAwq{~{]?` Z{{;ÛSqWMH&zl5~.sQB!-nyS3.nl!\3z.?!}Œ/zp1wjoLT2.>:ש)MB5]AR n!\d~ӵFrpHfN%qrrrrr*ǧCrW?Z.ց>"iQ9%lv§^LPTmE%a>"ٛàQ L{nG)@fC1uZGL?а/zSlmvͮan+rꏯwHj-"/j<~ 'f/v6Y=cC_^]LJ֨wqQ˵FDDw&'h˹EU=zgmrIV;CD6+FLTG~ Va/&h(' lM͝rFBt?Ts\]DuE-K=k^8Pun@I Y7{uT&iM/5 1]}][dV͸/ȩȩIDV_+qDh%|}[/v9^hW4++xEs9t+nxEַO?Pa/^ً܋7{f/śxo^^׳F}Ƽu_[t:/žu_ܨؓ}|Y5"|;^g3׳lwئѰhMyطSlv2A/P*\.ogK 0|bx'"))f}d7{}qYG q_0|뾸=PE x0\>6zt_ܘCbw{0vr[:=B21?lDt|:Q>ӉD1Dt|:Q>([1'ʧ(NϾ}@Dč'7 g~tDt}ѣd?l G+?zkG.(w~^OhCT.&՟NaT5N qB%ӉGW.zUf ǰ4h_)YUM%zr$O2N/A2|8?EqNn$shB(;Esc.'^cȕ{ &6.7QEWX@cQ1&n؎Lc7&L^'!o?!؊@/t?J {]DxGHqQ1,m0:Tq5Ni8<”rRN'4jFӨ(~ˣDh TSs-V; PAc̘`!ܲpPˤbmͩ7Tq4TPK@'{`F5EE.܊/^껆pbj:150cq9sՌf⎊騘NkZla &!Ccu]ĺ]W/oGa+@-ԾBhxr?]o9 Ij8ڢss9p?!;s#e{"w]՜%pG \qk]M*pQ#hPL.Ez?vQN8@ EEBQF#s˓ǿ80z~ĢI]|/38g:;\ۡU913/6ؙWngJg .LB= oV;thWt*GU\I$V"3=z8zؚ7~̎bbl^Ԟc`1D1 6V;IЍ:n97{`>J K87@ѭа."ܾkUrꢬ];Ҏ O//Wbc ,10^$ \5X|?Lx`GܞgD^5Mb\FhmCڥ$ɡQØjz)NITГQGѵXpfa]lNn'{߈M,80.SN]A2Gx*9Ld'Ȕًㄞa;,.t Mr!V17,K(ғCzr9]!=]>٩QB7GPkdBnjV<19.2KMM g= Q#ڂ LP,E ΈMƟ% ޟ_)iP/:GZGS* &k2Rn<1&(FlENx\to޼]TNMc'}AS eitE]D3Mb"QFtqPXpJoPK#C==̆ e*3Z[K1$=> b":=' RlcMD(F)9_y6*'zھFD({#y?hd3^*gLDlk(DwnjM-.<%."~A01Nϋ_ Jͥ”ZPvFVR\fi%=v'(%7;BΚ$~x }3 tx&B>b{|Ub@^\J$_{"BߝCBR%& . ]_ߝ`$2j?@Ovۧ@-+E*=|w^՝EG8}]46(ɡFqj>tLGI4LP~ %}=4n51N;t1U:hA(ۏ$s LVЋ.>zYA/~6a)QRLYA/|PYA7[1bNw./J)7bvOq?][M ߕϣhk?Rɹ:?a.}]/~ u(\Ԉ|tk/*K_2KN5^nm.j(R6b>*DLG؋*RtQO`>Dȟ +G121E!^bӨY6v?8CNE-{<'\dp1=y/j#mPq^R e b%l8I4:Yʹ'.]QuF bm"Wu~uPC,6^Ԉ^iy/j!DZc𢊽n44 aj!$HQ(bx1(bx1 ^/FQL/Fs.:?-6Bk "^=f0cZLƻAm@J*{JdL%sT2ލYҋI"턋kBjDF*~0'p@to1a.Rl]x4l2!C+ >`^yiQ-@ Kt .!KJ 8|`*~*HȗП&fm&~ OP^Cw jگMG(=K(ߟM7t򺨽Eu=.~'!ID1u򊭓W/AN^+N^ NQQR[Ƒ*B mPnbF?F|mՏ:;OjĎPCRx@HuD0Qc1g5RM?<\s]d%fS2m\4S"ǖ;?X07\d F%GqDk"4*G]a-JW!=Bdx'mQ)^Ka;Os=ǞcO'x'x <{=ǞcOL=ǞF5!N@9uRqF_K~I'ł:C2L \a.a^8#>I#T}$ qC9tAQhX#'PG:LJ&t- *Lab~cF_l_ۼ9-pAhqqu C0(#A(2g 9]G'+$l4ΰM%lS=1-6"t 4A f#K{PID dzAT2'D/J Lw]aN RL|] 7쁟hB Xt.?N[1 [M-&[[ a_1]O)R%T\7Q}mm ]6**Pm%7B/"AFBBnc(־QN#T?gQbM }p_mJ9`wcWs>U%`ػlBյZ"\_4PMjBj@; =oDx"죎D`yHгBPHB^n}S |oDE" 0 x'dOUwoEByC>^ 9%!1xf[~(lB_)cC4F7~L KFBdF3m$g'D&%ҘCm"/%p<+w,56@FUad8 ` #!t#\b!ϵ9,|Nqϙx6X &רШ[^=|f{Bˇ|EpqIHq'"9Bt}"5q!L]HJ,{klQJ1rAFh߉1ДBQBOӐHLI{a,vQwr5ѧCa! DXw͡ӭC9w@%~șc|bJDK^"^Hy99$k,..`yQg0HI!54QcLY6s^kg;sRF5qt4SCݏ*qa|L)XaJ ErK)|[ jyYt"c )~TIJw/Xj_@cT?!N9#TNU=Uplx.If,'M$2,MaF ȝWrK.h|cKA+I^u1tNq.N'r)fKEz>Kb9sĞniPol^[??5a]4A@HH]QBٝCC,p.AZ Y0jrVkHjǔm<ps`+%kRQ/n>R Il(ߏbY,Rv0jqnvQ9 &VB _<\QB. !9O9+M |Nu@\UŰ" ORF9d\̗3} [hWhE}_ŘlD W0aK_BXJ.c#jTu3FUJ*.[d^r~{|(h툒Abl8ߜڋ$߸UI^|f{KnU.?VGU\=hT %Ao)%ԨD,+J@2yB5P$a#)n$/Wx6Q_|nS=nYݔH8.-VΎQMmL+W"+Šktg" ʕ̺x6J"l69EN(.Xcwm$fdJgk=%v;)t$OgKn5=D:[#ѰΜ`%,(A0w ǣI ;j5z'd$@ dʐasRI'M\6ɋRX,FEE`+2Qb)rmc? ^ߢYPFb-I󰑰 /_ZN7#})`eb yZE܄5ZzWqIgL3&n.:‚'?N~%}+Λ:({n\@5&Ml!31.;DKDml/ʤ >f1UYc<.Y/FаF55iTgòr=#HNz#/jx X9bámF"wT?_EKi Q9=] f5nn !NbrFW[HF}}Ω٨u`lsW/=h|?s!YY,qxrx'g)TErdYP9:šQ?:Zc~vQk,*Xcz6:>䬳`򢖂ec)+Z kThXF5m6,ϒҋ;H5Pz?^d:G.)R,X|`,i gQ% z?SS3J2aY(A+ąZB"I)+id|$7âj~LD(m6JJb`z/CC8-"Z&-ID˄pDL\hDL"Z&-ID$e2gD$ec 5 Q5췞_ k zuyQ;ڪ1[c(1PV/"\&ՄVS?!)]Ԗ7١lL6߃ϨfجP *wN^PZw̌ޱ;'ȉ62ѥb/[3i0yTa;mKO)w`5dp:rˇj_y|k,D%d)؊E;〰?6Fغһ#"]#oTh}yObrf ڏߒBMuċt"2;P;GE#dB$d>z?l'xK}DCb5yt&،ftG`уFUg x ^rdn$&Y)rhsL%9L%3T`Əp?&[KtĽ\_cKa9H'gMP/~@bG!/2^X .~ g/83f{ }Ed<ȏst1+1rvܑFGp:8 "C@bkNPAG9As:wPp q^$`a93|u0儲s'}TSBhB')'0͍JWXV 6Ap2;`+[V 6=Nl\p7AGZcY4E#$Hi/ >l&,e{ Q?fO))+)MM uc MX MДljJkGE"%̎s' C7v#??~ 0ءa {0*rM| !GyCK|v2%U tM_CȏDZcm!H}D{mTu6 ̱9fa1@O옝̆eqQY|ITtJXcM$]^|Ӎ#碉t=OyE1ٷ;%;+\wrrrrrS)@kûo<7%t.}y0>ûo:!eBti?;/ y9sQ?f?EbF$w^ ?;/(c{v9:|Ʒu }g?f($F2`= =/䍦k2 nPOJLJL`F9 &cDKX7$m(F\We,t@ ` mأw!$>$VP0C9mr: yNi?`"I&An3Xgp, 1<'S~Qk,l'ᢍ+6~! sBڄR@ G^#G0tpqFuWHf&#P5Y9xQl< YQB$1s$bQ`nfI*.*Fb`)J턱039@䜓QDB\36sK>nsOrӄLM ՏY"r4%Dx6!i7"D zH9&gbI99'Y7sFUwE zҨDl'7"i z;P; jyL Ib(!圓i ag3$9$&$,>CO,8B&7L *zBUb3y&qI֙0dr,Z䨘!)s2+=|Fj4㏋18'✢87Ző伫CBNe->Slb}\4%>X#PRSR/j)Kű:|Y¤غ(s^LZ~?P\Y.LЇ *fXEIn,[3JtE~;~u^|@#Kt;BM9R$.I?` FH@"rf]!sQpAUgߍ|޵)0צ-tSpqX pG a!{vfgl-IqHOr.~DA#u]EM06$Nךcm7Սf'l%cR~WtШhgYE&1)}x(mK)7K.R]K4񌆕E JkF~܉],$b(Fs%r0 VjִQXM>G$u.\ԹEB""99 Uc뚢_JmL& vjc !c[rI$RŸ$u*Ru':EJ錿(Q?WAH"]DnӁTF$  n5 jk~ʷ ~Mz;刲eXC!GQ#+T׋Zc^[D0iDo* 1S6J+ׂZP_KBhXVm4,GB7JSM ʵ9l'G'Gݝ7jm9 hH{v0w`CSA]ⴓj1mдk;?Nkٔj֤.#7i39ugW QJp9rz~QvXg l t?*ZmKG5:HIPċmSEFP¤\I.I'HLjDLn+ _ׂZDP.8~,DRH7uu0MzѦrDuX|n E 4z'}VaXc ~~9iN~wRpX|ѰF4֨QM stRνHrUW/jk,F 6g:GOs%4;16/uaZֽ(M))I&Ղz^_l.ةP!R~Of(a9EE58" QAڿ4"MKw P_ng<9BD!2 !9Hb\N/~ r% ӂpZ30t:틃$jǘ.b;7b %Pb %Dc /~A qLMu0ڡޥ  a0 c5pA(Hzfp2w1z#3wl99q2'gV 4nU"iA$-cIqr?h"]kaHNjT\ݮ\fe,pE(?eaRazW8̆V|^dF0T g>urب q$cqq1.>}[FQ1pfܗq}(9x6n$'J91XpA .h-\К\Ђ ZpA .h-\Ђ ZpA .h-&\Ђ ZpA .h-54jэW3Fsqfscj]"4scqT |4lX6sq,pDV䶿v!<s+GՀ+Z O 99If)eX$9qspH:z  NhyQs\W \7л"vN:߇nYB=qq]K.%EE"vq 5#nSa %T^9"O%h#hM'PN69UpE+Yqp!3U $qW` y4FuX/"X/HT?|qG0z#Ip$!"w7|W{^HNzn1NK.p!GUS) ņ*AU&'lD6ٟ7 jp MuL!F^l丆:kTG ̂Y00 f,&`` ̂Y00 f,5\Ɩkuk}gT*W+^]a)Eja(.RTbB(!r_^zѻs0/Q uXc)bFz#ډG/ؽO-]ƞPNG "Rx@X6({oݾq ުd>*k<howhw6>@bkxHl $ ]5U|$")z#%0J_9hX#gg8mZl@@1>^uE~| ؾhsPA <ϯE "ݣ/&%k)ɰo S3{6IwE +r~/Ŵ=S4lѰE,fQ ^b tWFy^hz I$ZNf^LHGEZЖy߉6f=IaxX/ 9mI$Sw)z蝢ẘީ;ENENdQ6aO ~_AFbIXmTCyh#'] $}ܑFVF7x$t!| ڨ^Fik@Gy6Q}Hd| FXY:hY69,_uƾh.TJ!/FulL@4(|w!cYLwS~1kYvlBuXr-˵ V}g:,THLB qSjB.tZ}g%Ùvl_5V ZB+Xh `lV 6ZF+h`O`4٘jL װQGsHFW _QkJ)CFGX{lگEQ*\y(ߑ?8՛%`Tlep7>9>} l Y?2vFhp6mд-ILAakjT '''''LS.@> F9TkC;f6ofN3wjhgl;7..^.f6ب}3wi1s~j}Q7x@{c !* o/P{y64(XOAw?rymJBZ9*`~PER /7H=BgPܣh.&a&XBq9CqRR&1m睘B8nsIO9NI _ AvKj9h҇w5 FOP`p=AI [ KA>ؓ44a[=Ɉ=I2>ꤓ;Ds'k)rdvڃf}f}fO!>zQ XS6kl6kI,ON#_$%^Ɠ}o$OטrH&!z͞W!_DVØg c זzGN!_Tcwůw ۶z8VVyc69mr6ۢ5Ѱòg꼜C"~:ņmEɌ.hgvc_;3k1%XF']Z͵Pj|UQ%#5(6f 2O{^T$ I"<Æ$1]Jqn"mjiȩȩȩd25VV6q^L#z^Z{`ubg6;ً>bg/vf;{;{܋ؙ^,vbg/;{;{;{;{ϽًKُs^cSgOvd?ڇN^ms3[ݴi)pegov`tK););# _vNqrb3A LP>'E)d\$%e:ʰ!-;{rΑ 5n=@#^'Z@m4~s0#k` `/>:0dVC`N-ZCN9=Z lv)KN> XPq@[S<^1a69B4z!b,lq~4^r֨GSش=} _H1L!4*V8]R)V)1rxL!CT*bb{#3/ODjNb II4b &J-/b520+f`g,E |@m)E4E)Z*i>PAO;`r3؎q2/1 X0Z+H(M?=Az5R-]b.ʭO'i49&4FsF/hrMNi49<&iFlZ(mj1b˯B7R\&Zr*MG(栌1b Ld%;^2221X\FF*s*9h&*Y=1$;]b7/ '1$B,,=[}B,vXh@zװVZ[ m|:W/Kq,6vP B]{,ԅ܍ř;+10ts^^tK-:1ijQ XlJvX Sj1=ĔjU0W]#Bٗ \|XˋSNCشihL$+]kmx3`fqa"1 jaMHGbf5R`rp,vYk(Ozj1{ Qq,Q1ؒ1c+ify.ꫴ)N% #oq,AS WJa]gK{~ N< xHt?^ȗ/>et?ܨJP]> |Y!19U-PӞ>:rz^Fhv}IE@QVE(=}Q9ȣS A"$ZQ 6m cM|::}\|>z$>HqrtsΡ,Bn$[߿;G> _TNF Mac}6ay5ZN:ڋSE~Q9s1;]#gX 5JHPg[~QBy(^Erdytz֝ӸG$9pH5V>t+/:E9sX R [4lQ͢aj~aEX|0C7؅tхrb y-/k,bX|N`Ls[OE9(XS]=rبt-#Elk|}FuXt ]2u_Ue:|( =G@ /4רެ3-6l lb!Ѩ+Jc7cd3`BNaXWQ\o|?b=Y^tz̒mw1bQbJBYH&4*[BtLJBdOH޾b^,+面wjNS;ESNY;5{D-^9a`}?jٿ@-##O>YI6dE{ ](W!{ij9`#8 l?VQ=9l<:{ߺ1@ʱQ56|0t{D*?dr?!I15f6Kb,cb\$]lAߐIMsӰs)G"9$lTş}$^ePv:mnkY"v3PA9lSJgIr䜳9l֍9^cQINlT"TKhyC{`=uc䜳+~h秆=?5a眃j)IUp9#ouEs8sy4Ay$p9KJs_?a6ȉss0Ż[~lsr6ѭJBPBqkTjTs6Ȣ8֍ɏv6Typ9gQ1vBF]ǿHf" ^~r\A]jg'rרNlFő梍^ߋJZ1tT:zD(7zqX/$QQ%(zfp$9Ep$9I ^Tf&F;.bk.tHOGkTuA #?~j0{ƚ.."|iْh]=ݨez{Q*mJ'C7J+0LWm:2 ;aڰCQ9 UØ68D{brcܦ˜P) 6TgZ- E qœ6tQ_M!˜Ɣ7NǾcWRrk7`XC `Z toG$9%9%9&5yC[,ǘVTGOqC;b$3ۭƾhVh;VS+pG+Mx1IqG+&HN6ӆs56\%i*uܖ2 ](` 6I8{,.NpvaD(¿` A+;KR _TǠ*\miC((L~ ;6IGqs! ]iI؆) ہMmfQfqË28;g=tHN3u8ٹ]lFz5"fc+fsp?,р;$ h_u$^ LPxA?!\"a/ Xj{#w~ fk|3z$8AKuDp LИj9lm6|`> 6|`> ؆lmLv` n(a^t X؅mW Ф#s"wz8QG&ɰFNIG0}38'` 4@4'@톢'`NP/&M)P;PKD66-H "Qǡ46c;3`l'r H.)mdrbO"Xsm41 :H4K ;1>EaaNnrӰ%B^D0n2){~d!AHn!Yg76 $7ɱ>AHn7V0uS%cd8Ip(t|Op(tPP0!'tB/ByaJ-,~_,YNj}qwqSzQbTRڣw _qXЫ zUqP~;á69rҋKłQ9>!\3^=eb4ԡ8K C(9gDPˋ@a+,z#{qQFe@}_\(ɩ0A|q}LЇ 8PܤEqqQ SOH1^Ս zZ?tOCa>ق%?8͕;BM<|] /ѥR _ut?Kgr v].9?dwlA[.B4w !pI6ͮ=ubPjk.6n~a= T됙߷> v v vK Fw%Di8tw0{Eȩx9N r z'蝠w 2 z'f999%Ü MoA[ '$i0Y*AVI[a/^EUty!پhg֑7 ZނwϼYn` Po4ꭷLtBJ`eYvm]_Tp/~O*=XJtbrIw"b4*]&.q]nA0^mQ^͛ nmf3^B37{f/aw.f/s/Y׫rd/śYً7{{1̹xo=^KzAE1(ӧ->=r\j+/ 5N^֭k^3fo>ϮKuД.:=|zO?!Im&a'FI n{+ zP;1vÞ{<"%E OOO%t?WE 3s3pd=e=Ls(n%Hjuz_PԪc#'#'#'ù:{|]Ԡ;Nutb!Bp^mDg]~Q >I$b$E_r OFu/F_E,/R*[i~N:-wdL! C_rQjZrQܰ=3llb_g ۨ8M'Dz/_:HQө&q|#قE`-Xd قE`"[,l"[,lMقEvQ6> %U؇ " a4L_joN xg&l) i:X 4v1NtCwQ;\WӨqcEQQ<]-8Q]iiOň- .G5uqc:_嘅JPA w&3L۟/Z5R94z}Zpܬ%nVCXYw !I"Q5Kjf'Q190jѰl}S &Դ]j.bפ]٩Sg΂q,Ԏ"O^Ԉt|W8xB~?hˋnd-::T m? lJvS3=ĔjUep1Xca/oKN-s'.L%⍾/= 'oTIDx =+GlfЮNPOvXhX 'p+ ccGF:Fllir 'T$b` T47N!ȕKrDo fqpV =8p.:?r!I:zl0Ezk].]:FrFbRJM5jlX'GlE(u#G׆L"0nAcnDЅA~z.۹0f1np$'3. .~)(nn.""9`OD~8^\t&^E#?6~|9X(ljM)Yg[ڜe^ ׂ൒[Z]Ɍ =--.jCjrth|?}$ZrSMfrO]LDʾ^lF b9fZpܭwkZpܭ5[ ւn-[ ւnZpܭwkZpܭwkMւn-[ ւn-[krܭFS~za9~'D4?!IE6d)Hy8 y y y y y y ykz[x[x[x[x[ЯwڤPˋY^b4LSMB(T ߰^$7Iбtȧw ULyE>m$G|a^.f;5"rqn-kAZe Au(T5jm2Ӄ̋c߁E2 Ph uƾS}/5Jd]"m;p:rB+(B+¿_B+(B+(B+(ZAg5Z6*t O6UAuoUPBn5z7*VJ搼-ѴpʐKFϊ9^¥6"1y¡s?-jo\X ~yO2jc8?-_wBt_#H曰U#BᅮJ–F2a,rZI6>6 j11TG6F7fNd?MՎ6%͛𩻃 !b7lmvŚCՑ,no*5㴾s<^N8BEܿvNnNnNn˝\AA9l'}o7%D6ھEjDQ1^d1ݿJP a޿bFݱ@-F&3F]v 1??,aiQ_c+?!lE k ̆T%ZGM x0h!e<`)aj[J"g&V#W93ȹA#BDQ-ٮy592;#G#GQ0mv+''4^:7Nj0tw9JcT+,O~Ю:Ii$`(9 ?lI'n͒Oa>I$TYN)r*z蝢w)2+zF,K~U|/fZ/cdKhR`ɘףµ^grp6Ezl_vѴZJ6J7.6.Z_ߥ^2%ͺ}1:[ Fy7먽t b Ygދv:j/;mr}^:<_Fx/ǩ{999龸06}&;^Nut_LE/xExExExExExExE+++++چn[mmm/fb/^ŋY؋{{b/^ŋx/^ً܋zK%aHA}޺/P?PBt_[4F׳׳׳-g j) 㾸 Q$2tb뾸| {7J%kY#ڂ #0K([GgƱ~`#h/}1u1A#)!ԖƱnL#)7f0Č` s99%9AgύMMMMύ[M=|n|^߲^5dbD#z\5-.>7>ѣFXB?+>^5P(>r۷N(&6$B B1,_J#>VϢ(g"&&gO\,?I"#uQ(n_ YhE8Q4jFӨ(L Sj#Ө(~ȗG{ԏPM |jn-eJm#s1b fL0c﵍[65R9e4 n's̨֠kMLML͋L{b*wY}(}oXm4,;|QkMBE3FlRI9 ?P X VyQ;NhαL/QՆtH9w]u? arLtyH}t1a//JSrs ZPt?l&bFma^m.J:BX\$C-?d&>aL]Ԗzwuv3@rs,:EǙEwUmbf^ed'|lxHufͪ TP= Awb5;ZOz',靜nV#?f0yQ{ %ǙYXEڪ^s޼( 8{tZ7p.jp.P //>x|7oWЮ.#%@tgTGw/W})6^U&f"m)oW;G=kzu9j&0Fhg'~:gh0کzɨITГ>Ck9xú UݧoQ7.6%8HF.E,! =vX\b]I$BnsQZ!=mHO[vWHOAv'])894k$xI8ډ5nX 3n}r7xLP zިDKs R?uP&|P a"ŕ ?IsQ?>sS.R5|ё68/PHbJtQ" bF5p)s;1&(~~Hk38#&#4NNNNNNz~'N65KNej*&TEyLĂLDC61s $()цzt5̆)E p4Ĕ5Cboqȗ<}' RlF!z| іPb6"D'J(|ZH.xxD%r- ͗(õ FM~pQGs0A(;XBPCqwQ3AK.^A(ܰO]> CǢT쀥'[;)! \(ukS#? 4v;SM-ON =:w^H ͍S-R};_ɟ&*:hPv {~a^|?Dz^lJb z18:2V$FCj %UȔ73V; )B3c UF8s ,ohva$#2ɈLz%Wt{O!IJ36olb!m6B1d^GD iO^؏/`7pXXX(9T| e YiGFhszCGo$PB7pr;PN;T(-?PB7H/R:i /F؈oVκ QuЎN6qsOS\K5~_ J'DZ!ÝFsd$#3/L@L/F/FDaHsiy-=0d^x't0{і:ƻuW$N%dy=w2R T{zbC%E)DO0֋uxx}2/Z7RlY?'B eC+q+kW؍'A7A HVTcL۔Dy\k76g%ԃ)LLLd& ww,0L #եF2Ehġ΄V:1! 59a'JZfc-~;6g2vHbLZ8yǕ?n0H}'wH5{'}n{BU>L6cm @α,;Y +%Ln6odk7؎e HĨlH~?g6\p۞sKyQo}%|# -@jDP`$ס-5Cڻ >tCqzB<wC8柼qh}eClt^ 1y' LbcNJ={+8VqdNJ={+8VqXDZb{+7o#=bpQ111ıRcj{f Ul20"+!! |[H5s|w " &P@Kb3gcʈCHG{5ܬNxi,G, 3 n9 / z-DmlusA7ZL)!FDHMQTұ^k;|)T#rIʷ΋2ur dXmdEBLމ]V5XB%H> Q4a豨N "G)NYup0eV 5F]cCdDw9)8:IX.+!4!*pcxbVHG>[-Gd3+aȴNHyC]OO^L)k./FsL)^9Ez)JSI8\F&dķ mcjI06a3q,S3^Ax~B.oC r$DĿN@{61z@J}ސx=DmcY730#gDQ1IBS.܈b[бOJGMGIr3e+oHfI,8s2bZ#>Kr,X1j `Bcpi0r'$$$$ #81́m42x,T! %]E (396Fq.pHOh@9[H9,cY Dz@ev&˙,&vlb3Llf˙+-}LDƷcL-ډfSocJ&nr.o)p"jM3]!Ѭĸ,Փͱ޷ APR]kQ xHQܱ&P 99qVG⼃+v&)%N>oMx[tkr@LTOqhS$:Tq`LݱfPxQ*ab;@:+}'f^-9OaO(EPShgSAB]Z{,[`6{h0)ΣLe %x8$N> y5 Ķ=`˴KIj/s, U& /3B'10 8DTObLHڙpIt XAJl&_$;OB)Ob-qI>ʋ UH,'lj0#:ak_HF} IHM1 ļE ̷/J@|Y`"S2__9tJ peqR4 4)E&2K;̟EDm8@G$6;(r,P䲨mX@(ios3jxd9V)*f9d/#QB.>x)X3xyYd|PÄdB2/`yJՊ\f|$#SR9K eV)*"G6SEf03䞣r$N$CpªEROXcrcrcrYT?(Cd6SCf(>39%dqG| 9V #{-=+キ+{,o)m\&2y/Vez.`YŨ@![K ̀K4*!Ɩ@2{7L6^d w A//^T''r r\ >ۤ&o !FQ)?697UI8jر8x^.qs=["e{Et39FȰ t́mv8%J9B GJ9@ ۹HIȌ{i+߳Dck ,Qr&Z2zќĎMĎMlfb&63YvlDs*$2%S !?$$SjoQ= U7~c˃'2p9Hd4kA%-P.RήՌgvXeesPԳ(Se%sq/myD䳬B b Md)p] +I̾ێ-h#ȉL<e <|`(ѱ%[z0-"EV ~ἲxv(^6zm,QÄW.9 M `偒%J},#8j! cCŦ D2` $G&0 k,T_-=I8:8:(,qtLI2%eRLCD_oLaHL05Bey\ 7b"ØWlWc+$^ȵKƅMbZ9=.d!wio+XjW@|IW%_qTa"rjU|I׍"jQ߷nDЮrOEWD-<-ŊE vlZ7DdftQ ֝_U|YɩB"S"Nymu2++q_rȯH(LQͯA;T!4aE6|Zxxk}IHIPɳge'N^VvYɳge'N<+;yYɳ_+X+`R\°B V+X+X+X+X+X+X+X+X`宀sCia\7Li$Fxy32=Gwj$ٜwU gg' @ {)(BP2߃$"X\`"Ŀ K\uGZdEFZdd" %JlkD= TrbkN^sya00^y̹v`2Z^b-,XV!L$# ƑF7WIjs!S2Oɫ<2 thX #aB;NjXd9z#$S]Dj7҂  Ӆ{Ʉlj0BӅ`3\Y}y-{R*0DSX By-xS]CV#jdr,VW #PUo/BoTԣ.0e$d#X ;XԣD0K.BVauXP@|W#},E=gA@^!H0Y* /B2gH"=~=$G1+.0fќOD|G\ NDF(QH*4IR|UV 1Scl t bρ $ZRv'XAp_dHG]J:NxU+" " " " " " " 2,0,0,0,0,0,0,0,,,1)DHMQ^^Ii ty_ǂfA&]BG$O"ԳH9Y]g5Oo d/ 3$Gvl D8Wg]]` hyhƕLM IT7SLuhkL3-6Kʾ6x٪-1M>280W@XFp L rC /0-+V[/V[o /V[o V[-ƕnÁ^#!hit>V/6gaC6lxdU}d6{ٓzI &͋zqCd}IBGǼOշQ" fP^x=C^DȔx]U/ᏴX^1X,l\ 67ΖٱT!hOYy^ظhmCio!%ǵᓉ  4m|iW!E:8H7~` 8l`VB",U!#?J![6 [T8JW^R"^xVt"툖n^@9\W;Į )I]U4DZc;ֱcر c3XKyJ<:n `fL:$hW3V & a X!4B\&Us$>]$$f)]&-m}qs˄",>'{dW .Qr1sahVB0tLǧG!2-2"9vy-mT` xK<t/VfZ% %)r_{''/"YBL wH誐BpCnUL0Q3+Dz@>72%nsssssss 2w+0w+0w+0w+0w+0w+0w+0w+ܭܭܭܭܭܭܭܭ ssB͓<9rF)F㻋Z% rk#+^wX!F2Gp"^978@x%Wќ'Z*ϿL%^q 1Ӯ*$2^^x7bh[ӰB! #2=I&D; .;C3-޷w[! A#Te5wL L HڙpuŋF?"Y+V07fx$|U^n!lba C|UӖqFuv& xb X/_rW/V X1 xb  cϬSf_!,,c>#vh*ܭZo!$Y]@G>(p!\'S$WxXN.\`9;+ޙxgbw&ޙxgbw&ޙxgbAޙxgbw&ޙxgbw&ygbw&ޙxgbw&ޙ䝉w&n4AIUKyWbFa,AU $n*0bHs$7-"lȌ!ĝt1S^ &rM|KXĭ@XnZ)r1 9R`zJHf5Ua#2CC&.KB$Jw&;:JU5u |'hTėbL\fYyb d|\!,Oa<|'sM$z/Ӯ Ă轉AĀwDeOL&ط K(6Éޛ"G'rtxN9:p۝G'8:Ýw;%9:8%JbT<ѻycvXN0:K}unV. gz{gqqyd|+YK Z`I /3r;Q1Qg_Ge+9dJ/@u:*3ޖiW)g8♇gVINU|_ř3)~U3[bVG+̐{b]ՌzŬX-*Z`UhUтX-*Z`UhVVUтX@8XZgzqZggřkqZgřkqZZ̪grHc|_yn|_y^}qbxcX2iV.+YdճȪgU* 褐n _@Np#tL%/( \*\@12@/4YJuWő"3" &x_\0&BjM!Ȍ"3"jUx,m2yc"yEfL3@IvdTdc+()P2ꙊxXN \DX0l/2Xd./eɁe.G,#V -:ѢŋZ"U`" |#!f2G$Kˣ9,V!aF[ .BQU=a$y_ddކGWKJ{"#'2w]x pJ-8Hɂ tTo" \ d_82qqHT^%td bƶ #hW jk )#3g]jHcT4qq-d_\!"idŵ:?4HDuqq4\P #3 8@rff@ hdTdTj B*Nup0)()>Vd' L7M`+l1C@j 4{Yqqԕ>+ ̕nE6VAY3cf[?FB++"=BB+ jXql+r ܺjdz5El\őծ*qd#qZqqWqqADLQ﬉c^I}főIO8@ 3L_\(1٩@tBB 1P@L(PU T ǹd#?jp(yd`OUTɄcQo 2%ҷ_̘W8@lrNMTƮY"7Dt (QݪBLVU %9J#>aOyg۸±Ul5`C0dŞլ*sn8:AXrt[D (G'RRHI\7zitxwZ`a-9=2g4y 9ݪj"S [-ӇU#lܬ .^!#Ӱ s_ ]O 1<4:NH!Ud唑U-dJ/w7:mcV0^DM2v&h=:8o̠gDV#qFI%m2̩5u&ȌLvܢ:|r#sqr䍖!ћY BaWYK=L<1b=-{\頨A3U0FN ~&;|+_@=1#Ŭdi%FӐii\ܚR &4pi\+p!{12:_bYOVY`+(Ÿ&"ш#'dh@0Rmhp, AK~b*`͠$[v&XlC^$i p R 8*3 > W1ĈZ$ vd_D2v&bObŞĊ={+$VIbObŞĊ={+$VIؓX'Ɋ={j4: ě >[xTM/I/I/IԓZRH)U,c s7%0p\C Yai 'S$8&Vht.R-Ž(#xI\-I@G \x\bH ֺ&2fOfP@L9^lHXl@ >6x[”īSFRF*<9@r @NԈ_&L5J-(xo,C9 HJ-nNb6HZ /j2%D>%ܾ3BB݊(zW|[벂(ÞX'{l%b*XW1xU`"ѱ>NXRVÊXB#!lul&K%Lx=U 2&S#O&~aq,S!4KQFW+C>+0@BbLdn.־BLFkd_ú+ \D"V\>@dj 'fwԺ/̾ٯ7ɆlZwlxXxM\2X&Mb ̙Q#U2YɄP)mx;[ Fi!\Zώg6$;%\"aBIIirCo ߾UbBKDIQ|^ ,ulb5*D<ߏ#mbP<:1}nHSbW9>LHBӆr D.R@UH<8HIUH$Z&r+KLH8H!)1Bݓ88w1ot-c[f33;6̲c3%f:\L)2}¬ 3ƿctB~B*B۩Ȉ^р1;f6e-PKfQs둕XT'N`“CH@EsE|6 Ԁ$^R?#wۑϖXLB0A@6[QyEdBmu5utonS#`}Csb1 wCffbHY0ab: t*d!~(,g@()P`'IZ^!6~Eyt!v: Br/ )%)%%N$9:G'qtGa),st PL0IBݳPU/y|[`B(^?%2%2<A;|qkQ,~  W'/J[ _:x=w >RXDv&x5xd%݂_C$ht}ssw-~ "qt+$/w,y K'UA+$dT[[(:9. H;xnxQ!bzdK xQ|П^"" W) 0>|O$)GX*3^RQgg.ͯ}\Y'N :8)ŃgOa< m~bĻW FfL B#o0|חw9<dr@o:+KS-pq0a*ЛC'Fr;DIqӛC;k%Kkv"2{?ӱ2͞A:^'"CL"歐?bJ()S_F_"7)rwt0En#_D0(E#52U?\w ?΄oOPHH?ōLQnyXG|[_L(Laѽ&R(xX&E:h} 4;F"PX[J:^TMkl1EyiXL& fF#QI1RȎ4x|"w5 slh#Ո$R7&6]b7%$,b997%,xXb7%yXb3G%ӧF.lGSbNbNbNBNhVb<.^G:Mb轉˛sv,$uޛbJHzK_$dx"6Éޛ)pÀF ̀j{'S|Qgg⩮琢jk }j)E截o IjUfQfQd.fysXa7Uv "1ѱvjfD.LG/ya; 3k`iydnv),S%%/ZZI@G 0uLJeბ-(%P e@h@)E%j-T qwXV2E^]m`r. ^ u&i8V pב`嫐?nTOuWøv@azKG[5 dpPFU mj23!&䅾TiLf@RԂfLM Nۜ N{a՚mH/}tߚd.J+vUj+j2:M d9 8csbOuk%E(m!GUyDf@bfS^I&L࠲MyDXH[kxk}LHXV|Y~5i'g[;6)D\!,]b϶fJK #B2)sԲس\X/޲f!Z?z oH$,P6-)S`;!}"N=C[  WJZ(i$xûӌ*<8kUS4޼l2s L쫍̷E4[)Cq 2ӳ$A0l@iok1V"avU`d#Uh,7"B'LB$ b > d8T&ۓ Ⱦ^h))RR$|NRkӂBX;jfL1JAm!&-y;j/Do2ӛy=YAXlӛ!ude";$K 1+~!&#o' pt ZAH x+D˨I jlnLc4LI3Y[LIP~.{:r깜בyOWѭhǷ+K)/|Pa鈞I xP0 &د80(3F ayDxnB2BBԠ*P=*䏞j 72E)nj8;GutDHr*R2=THX#`\@$ *GPjXyVFɛIGjs3\N@u_P#'")WF/gMOT^\OZًjAWle_ !5ŗhT 1RS ܂ ?)!1ǀ\WzLq{+v  zBΫ~W $WCg@G"$}290r% n0y34Ei4Dx :KGcOL J@M`LɁ+^o 1M.IjI@ q_q|z4Hg=g82̷sb)gTY$eVi'h}#סx{X-֢>TǫHuh+ #a"S2?ևHf =4x5Q'U2~|+%*$ ?X.)hc_ld~W&4٨Fu7WHM72}p:t{wXa (E FpSK*`N).mdNpÐ}п~N!j7|.+~dHT腱ם ّ/+'jWH;JNtU!-TH ɕf{OW&,6͙mls&Lf9!TY^"QB ¨, <^ҰB RF* 2R!lY>QZT 7 TH Y?$mec b[,ⅾlŽB#S>s‹RB*Ĥ +l-GBahv69Yؙa*X"%` ȡ}/)rh߱rX`K`ZS!|ky/)bd}TioyW'25(c\_7LPzW:MyXNSPB\2Du־BLF늾ZW,n+`}'W]Ddxb ƺQ-C 1_W4{ET!:|uE"V;X=,tlgA'O&LhHبL$ {pT!Q:Bd}iDȌ=&Va'j&Κ.~Mđđ(`I@f{xCL\*L3*C%c{-Ǥ i{a^X7nfK^G/$"őQE.F`zw e[(qA.FmŨmr=adsV VOt%5 *d366cl 1: ANF}qqrt%9Jn,0xTa3MWˡN^4"ø<ƽnA7 sB$LdJQv]MQlύޖhj'I"SzĮw!;`q\sBO LIT`ފ;*T 1N T;*1R)wUыЄ;*Ad*$b_A72nv-72[j i4`#< 1x<`* xW"2w!ex6<~%nUjTHI+%<s9\N`Gm76gXy\F3~鈸@ }x3f;?`* Zc*y=vN޻B 5_ސ?b>O?2 I GĝD;*Ą-$Xp'K !,($/3$ӟ bǝDHgw_TaIT!G/H$ωH%$nT+cx+ps %%,"[X0)z#!LYv&\úbGFp/L1ۣK8i ca;$fo6;P ^#s'+`~58˘c/EW/xGH}dag1:ܟci40d10{ =:Z`10{ 2``+^ocAccO4 c.dl!;´CcҊ3W|q#tGBcq3{y3rE#wq񄰣 `e ,4[#wqWO-0ǕmY0rFRv&=Ρ agH߉y̋Džz9^"wǺȠ T(†E)rwy=Q'>I`@G2!Sba8 vGǸc\<*J)H߉\"ץds8EPLlNC2%,>ǘi\i9IL HMzTAFPU+ vwT pp=G$=a LLAi ]cb3qDbwlp'ɆwUewU(*BK&FEo•P p'Q?B w9z^ ,ZГ}ɔ؂. .@)`zLAL!Z/…21LQ>RDR[D8JzP)ɔ”>61~2-7S݌}l0eL)y7ҙo2$̍a0sc_bW;/E^^ !UH* E 2wq$"Q!WaDET!եeJ MXle:XenEBLى},7*\ a>HL>6y ·,_W΂gu,/8 p3 ==׻V; Wd W}Y^l2`yzU!yO$Tad^J//^^w^ac"迼v G <T=D@LyU&z. jTHxC&ChLxH+ᆎ?n "5|U&p|UDfH*ġOz$W^wWwUHH,|FN_F'-LŲ,/P4\ɷ~ <^::!DFEHf+=UEsVX$;*GgU["GB˲سմ;;9@;۽dO_2ȳ=GX b ‹8gL, /^KĤ;*t4ˆf#F]2]B]XR `wQ׫6g9Y&b*X pPF;*ĒV ד>y'LlG0B [nmK&H\T!~bMA@ELJ[ڼR!44$>N.p#)w2Mfu3L_n`i9M2T첣xBSGu/x{gzˡ:gPǢ@İ \a n9΀FOC|@86H 8gjz"E2!SnǜH-!ITЂ |;B5wHTapnd Uwސ?FB=d0@Ouwc:DDidJbqGKGC ɔG::9:ѷ\&]NxqB>->\S!M a'mq2#zİwTگHT3GD 3 AT=a L73 |[y8Z3F๗Ul-PAɌ_!&j\Ì3&n* $ p !U&*y}#]B=V:xv}s]4R3 [ d*TCG4y&Tmjf1ĈE/&QJH")D}hBLN7͂)>{ c>SLuyarB !2Ē / ]0 1UYp}:Buԓk!,0B`, db +.wTHHQMnPy(<|XlؑERSŽ3a-071b@ c1f3{BL!ŋzBXd.wBDlqk}τi/-i,gW <,K^^qk} L2{@t@YxqoD$n{BY| /¤yXBM#%S3Z۩$B]d rI+BxY`$q"$̐ZAs61D|T'R DCd,:{<%%rI\Ru *DDFL(%]\ Ԉ}/{?82`*5S܇3oQdh pUJIt,ҾUߐiVJ ? $M0IEB&w?W[p›X(/ငtS!=Q 񉡯D[%1h ӄ&y_pIogŹ8qxHqOI%yH/QwI Dd娑FWGŞ: Yd>]_Lv}yvQ>@[nTȾBw} Vc__Nּ$UY_x"5([$%~}%vxb,F'3 2-4YەEȄ& Lׂ]2YȄZ_ =e=L_dŐ/! fuY#aL2L}kInL]&[0כ 1ۯ+Q4G+* T qb)fuw=+=̓ٻ V|7.w+ߝKxi1uA^`GT/j1R L$f8ƺ$G%+KFK>E㻨[!5(iK>1 D_&2 :~}M0@Ld=o$XѺW+ڢߏ.N"~eу+B1:+οV Al^x$U-#x2LFIAXZTp/#_ ^"^}6> tkIQml>TMo۽gv*SNiѭɗ?Bw& l3T?^g_: Y$`A|%ZO cv=jp$Y7][.Ļke8bN[1;ή3*>\~8-ynP]1I뗋No bYEVp )"͖mx7e? N7Wb7dD 40 ɝQI_2'-vyCuAv㌞ }ӎ#!*`HoZ5C Z.'(x&>oiWԤ |,i|:KKnQzpDrH>];DWnnS.FP[;n b%hSg *ZaZ@i ]h?pIRKU:{-tJu6+s]&Z6#i4i0i ~߃,|3qS+IIs -ZgS7mofJcCmwδo bؔvHYkguNE0"F8(>7Y~t}ĺVOa8#z *ywI>#]xFhۧX#O*(;szKWh4Ļ{j7${Tx 0KM}&Qϙ.}Tw#881q"c;<^=/I#(z߿G>m*\fL|t_O nP9bxW|Trh۶?},,`X-4_Zymf1ኇZ^T$E2MV_IN7k6+zqv|\N?!>TgtvmW'5GBT;vz5"4C Z.jwXCUcz }Y Cǀd,)Q#ZN'  GãfHyUo{E2{x4 j siFxWpOGd|cDo?yZX󍨂c`CX.ӿ%> z2ڱT&M{R%S앣+u}xSQ| {I#]+:A-_wVgBT~wmgm .ycĜoy~t2*WY-x4ݠvvQ|.4Ļke8F{(>³Wk6;Y<]AOK͗CtPCq =joM푐- ; *XNU%B\l?!n$ttvr#lϨ=PbM!7]lԢm*tSgб=ni xBKjTc1Hu13b ٮ^>jwhIC FS8zA'o󪡐q!g cwT$x՝K꿉])t\b':Hrb躑qNCG'6u.݉4\oDS'JPk*>Novw>MЁ܉BKѽ"IT#P!y/ u\r(7ۂ>؟u෈(rc^0KṼY9^x((x>j"KZ}Y2Y n\9/V 6WM'%ypӒ󭼝2Tnd%Tms+2F}BJEU{$? ; *XNU%m6(0m* oZrnY#AH$oFPw.9r/>J CE ީ<@c:KpDJQB{*TG'mnS]LLVbCcbKeWV?*L8jcE'31nq}tPK+ lCs9-93W1k)8э'VKBnE%+q :٢oݘaJIi ]hwɱ:]%1nsJ7 G~% CP`UŽKlgR`EnwqnE~UJLϫq^^PG zn.6NqZ^/6o0k؉!-I m)41U0gL#IP[!wi28DL7` :<3%=fdV1q3-7<:^Qu;{*^vzsXHf5`c%VrxjGM ijS[xkæ\*[m)gEr!僕< l1=},*RH+<ճ1(!nInNCl')ay Oxyxtڼ9 LmiHQ ֶď{ ʧA/ZƹOs'pѪdX@'t 1wb#zīŭ3pRP.,M*Ʌ1P]1vj'cw:x0]lRUwaoyՈǧ Fb*N`IN  /_d/:gp+X3sl]Cm\14tP?Tg0D-˓q QIPUw L.ˮM%x*ٶOxeb誑\c.FQ{Ŷ; ։(T$虧Nqj~>K>:و.PիBCu63C}^0"6=Eܲ|`0vi ~Gkfu(oEPYyFvZ^uÓ]@\^xw,8-q(:̊^hy8kqMaw }?+ch薡Bӂ,Ʃ5,CW5bm*XNU%m6(`]AJ. -Fdt .yGP[tl)_|@JSEuhzxͅlۊ.7Mut: "6w.FrܯņFĦ.T$mAUqU 4NgD\~DžSQζ}+=$z 2DWCX}Bm-T#_LZ\MEy6pl~w3~ b훁ݫv!ܡj 0h'FĖbHm*{[RU0X;f&G g^嵬3GqO+hmOTc l'ΎqB*׾5{`'&5shT&}xl*dJNb!~Ly6ce]D:7϶bn3L `F9#S`L\~-7<^Q@c:^O< H}V RH1zT&$7~/SDvRk>#EK>xdw6f7 摢1<U ZƺK]P dcClC1 O=V= 1,ͱcv<`R"VӌS^@@os *"4f .8:Hl^B L^5is6M[%#M%,c+UD><1(8m%ʒ-CS[XC06Nca1]%zj(;ވuNS'K6~3*(ۖxt6n$gtn3G'6cvGȝ1Ρ%PH5O*M; 0B't 1?mcutyH.T@F担uB?M`4 wJi,f0-Wfb$%;V_D H̝؈^w y;qd8'@L[ dT0 4ѻjKd{($H`;U&٠wEW%,MtD/KNmt;F eUb/4s"ݶw*N.8^F`m6 md[իBCu6 ^W^lEl5 !H0ۂԫ!6\q? 7|b^Qζ}+qz<:jNtm\s'b'Pg *0]v;f6Ѕ]ȃ6wt#!;~sb'P`UŽClgROw~&n֫P#vp$NlD0sSݫ.BIc\8FX#6k]4i7!6 7VGL# ]S& 0s"/Yd'X:a_kxMI&-7F]j(aVfALb鰻7Y7/!G *1@ErWdRx@9sUj%Z=F}xX 8&̎sRxd8Ce Gch薡BPZC06+eD/Uc!7cwT$x՝KZlP`ۻ\@c:F rWLڭ`}FmKBed34^Pj)j,R0˓ZrwB{*TGmP]B_,lHlEoW AET8*E'3_".Kޤxzm[GJ[ oRrs?7,IuH0ᒤBťԊaJ^Vo0 M.QUmPFB*d=%%5$6t8T-*[%ݟw*̴ۭ?D\qg̣pȌ~4XQ*ĶYRJ1R{`qHKQn&&}xlgʉ6<3όb7+SsHZ؉ۊ1)<\EN$$FW6fKn_".0XyXJQi0'Lݔb VrO *jwwtӨH1UPkO6Ѕ}s򪆐fn$;~!D܎jT$mAUA#3PP*xM\^$ʈ.`^ /Tc*i0k^Ռ0fڤV*xڶ$fy"$aM!*`H%==3 y;ﲌ^@֋$'V 넗Z}Y2ޗo3܉Ь0;νcl]Cm\14tP?S43>`[BlT$0xBU%ߜx\mW@I.`pmFrEg7lO>+jw\'JPg 0VR0|b:i ]h?pɡ:W4C6w.Fry;q'qGT u=ա *+)u 2%Ej1/UCVČaZpQ소dW͎'$[j_EDrhr 2TH7pZ8ufeU[`򪱐؝ ,^u60f%Zn#kh}R3_g6Hȍli6 &lT5;C; GTk.gVtql{Eȃ6w.FrܯņFĦQxT$mAUSBlg]q28%~asOE9 ?fb]+rڃɠ@?/iSg:*:w7CvfR8Ft%#A;T ^+=1"6(R-*,b?T`n26qMCҲfgE? ,u^u2جORJ~Ӿ5{`'MIII_# 7{L3lʺىusom؉C4LH0o3ɜcl$WyИVnuJúPo;>5yHܒ<8?;8L:"G!@7/ oΣNh͝6wB3QTWоrKn 0G!!/KF߉QIOowԟ zz]DG<>ֱ]h^xiЋqUhVFy*9 M<*!V 21߂:ᝀӣrH.FWC06jf}!zWm^@@C<҃j=lSUwaoxՐǧ Fb*ZYQcw˒: +X3s'$0λy*ch薡B fi,WW;zWm^kHȍxNSU`roI )\l['0:Ŷu#'j\>@mw" GqB53 R4Kh~_5KP#tBs'6 G.9RGmP]Lt{CܙTy(7ۂ&6.8q]o#8؍we 6e]@%;VߕH;Ib+zUq8Ix?&@L[ dT8 4ѻjKl{ϡx{1; ^PU`rIk l{WԶq5K.(#Ydt .yGPQl":eUb/4s"mTqmĥpDxن.mut: }"5Iu1 ^lEl슝-*0N uLkB@\^W$XJ\w4ڃHr mtի.;jKm ;!wb2u*̳Aܶ~ bG:]څja'l+Ƥdp95|8 ֠hpUGFn&SˍGϫ:< ^q=+3V@c&1;L \eb7\fm\͒ 0.7wt8>]Үm}BNFNQH0a;U0n\#q0QNFb~lC?w^u\0wbCMbSeɄPH0ۂԫY9 ,!~z7_;(W@{I.0BW]aCG6u.9]AB53 zS%0GUW{U.{:n0**>λ\14tP!}h!PѕV̌U[`򪱐pl~NSU`rIk l{Wt} n\# |2FO\l),=LۡUvw[;hFtql{YHzH.Tџ纹-+"Do SBlg]q28g%]o؃ҵl\ r2іadlV[v:gn ?RQeb)V +-e2{Gȃ6wFB03A|V"Do ݟIRVg 0n^ptqpjp]46muT m֭TgǸP!mX#6N<i)$ۤmB/wˉc(l2;+sD[*bLLs37` }R#K鑑U LsθN>eɛ%ԙա@|FQ{ŶBnd׉)T$虧NjLVcl%f#~Cu^ yn$B36al6iHnM%m{Q=l! k9R-DUԊay "fo#ecf .0\x[ūac=Ru`Z< ]6 Q;*6 YFrdClOoE4i!gV]IG_$s8I5ϽyѤHm2~ی`MqĨ@(qRW)<Ӄ9d{SS- *ȋ]D9ynF;ɌH>1*G6`( =yqGg;ޒ);PMՏǴcxXCm3tʵ Pm(wYzOmş Wsǧw3jlxs͊H% P5Z}Wr@WbAԡWkEg9()84L* 2(ާj#n_^ѻjKd򪱐% ;I*J60f%o,:F jt .yGQ#!7Gd2{'C+٧B)Њ{R0":z(> md[ݫ.BCu132zeA<^1 &z[PU`zb?"/_".KޠDU=춭lHr8-^AT0d=M!7fZPB_*LNfaJ쭷bх&]ȃ6wt#!A{HlQ0(Q &z[PU0Kb? Z?K\qgȣJp{E#lVGfJuv+ȶWCZriR+e@< V@o =3ψbt+SsHZ؉ۊ1)<Eq׈` }R!qOĪUGfmaNP˒ m+tP!#j Pȝqƈ"}Pe=m2>mIvĶ:]څL*]0o0s@[=*#hăq{.dnqQA*dDcq/^Ռ0g֤I_O61t (<['cfXS7na(2\D$$z|kmFQ8 x2qyzd$Fin9U f氱ډ^wᘎWft4VGflHTf{oʪr}4ɶшU*dEcQs3#?3Ock{lGIdp|0|8!)/#hsB+J\#*'-C4<̒J%cJjKƔIg rcگyj:q?21A'Fd2z'yl vnqSs 6?:%S<>y $_&zL;ƈ18Cv,IG K*(qnA"xck}CD|Sv}Bv|[Q#_ JFEy`aT6t}%Ꜽ!dҍd[CmbSLS;Q`U=IϤB1TׯxU;QW*#hăq{.>RAħDO{U;&hRꘪI_O6RL \meyfiyT ;Qm<\EN$_%z׎2eCS;PG xb$Vij9ȯa*G3sؤړCeP>m{2 FWȹ(n{`vtѤ 64'kNlcz]=gQyHL={J#Y] qI#]J:p&ǘ1?q9J;XM6Y%6׼jmU{l\{Ŷ5Sn@du&sM17)X}7mW9pBzO4& ޢ/fmCvC:;]~k9pm4ڬRiޅ4舘EKtwȷ<5#FhbqZO v]rc J Sے,jJ0- Ɠ}4Hn6[_r0Qbq tIudq]em6y<]r]4ߐ\fgAP;f%V["3O ik ;"Y;KnEm)\f]rU;gjwA)O_GAlC1 O=V= NW==汷ae5I;%D1]/KƋU;^8l&h&cU(qxtFDׇꌎw&Vd) jѻjK|BTTKl6!U}q| =Z^ewv jyM> KTcšxw+yۻM;9nGQ*3zM;?kp 09n*$ܴCqۚ!zWm^ɫBn#fξm*Jo.m(g>G hb誑\{%jk!7g-JЦ"A<Teô6t%5*WhF\0k=k~GDo`EǮ?+T'_".lzh[XÚUg+WdVi,Y |(u%k0޴ɟGx uGvmsghf}58W}@E+8nZ4T# J_1ΈcП zzzO;j2%E6 qZ}Wk:;rF8;jub')<'}HrhCuEO!U>(F-@@C<҃j=lSUwaoxՐa#.k3;17X,^jex_:gpmz`fα$0λy*ch薡s?T13wՖת;,$H`;U&l&^v9-@(ۖxi?\8NmtH.#lϨ=Pb۝HÅ"u  SyѶizf HOmBKѽ"A;T#9S!ci=wB}mAUhmYi ~G3CA+5TV SL*UW<>)]@\AFx7%7.EY+dg-NI6g%M14tPyx-LmSoY=Oj,9^y-Ѧ"drIk mf%Zn#Yds _'"4n ?${-Ҧ"mTqڥa8z-^s(>۶7;*W]Q"Xl:;ƕ }k4"6۝VI6#i46kc۾P!7?Tub `W̳+g'1Ͻya&DULE$ RC3pg 2IؖI=(1l-JҧN*4 $j&H `OxfǣNh͝>m)\x WNw &(5Ļeɨ\:1*wAU´ǧ:M;haxgwӪYd$τ^E`F "1QA+O$\hCQ[$4{ѻjK|BT;q/BsXb&U}d㰷ꌼj#1z^]r_01»i5J ^B`F pF+ɼ9I3~"p49n*"bsjGѫS^m //$H`*KEx$Dsm hw# NmtH.iSG'6uζr-T!=Sg:JUC; 0b't 1wb#~CutyH.T@ =}GTi0v 4ND\@눡A/`a.뎢{mFˮHKvb-W,$.޶W0Ν8Iaw Ur&-C ̪SwY]%zƽHȝcwT$x՝KZlP`TT' GrsG ^CG6}FmK*QH0a;N.KG i^uEN`ڃ]}BN`vGȍ(=:q=lPE?a0ن.PWQ(eJ7 :ߔ5M= N I"Do {ϤB4 2qylf 0R'GbFt87ѽ"dY8ƙqFlқ4ʡiSU`bm_[gL# ]BS&\|6"Os37uxb<2?-7F]j(`&/$[a#E5_#"/R 2MV_tԮwV `oҦY 㩒 k8YCC ҇R&kU['W($H`;U&٠wE7+V1H2rWLFϨmtɑ#T͔_JRKUcilje˳Zp̢]ho[%^u\†ĦX -*M؟"mx[D\vYIR۶5ۏ$WNctWQV 4T0d=M!7&KVa*LNfaJ^Vo0 M.QUmPFB*d=e%5$6t8T-*[%ݟIJ m^KVPWY=qn{Emn8ƅcduū!-E4`g*}VN ƙyMntQ& 0s"/Y8k@0bg%Q1qsˍū另9f_d`J.kCuF/$UvdlUnV:';B?Kl`HT$g*(w`OӐ~@Cȃ6wFB03vxQ-*o{v&ջTs Pz*#hăq{.RA OcxګV#5inҗӶ`ԃ/yfypJ0IU܊ay ."focSZ%(GFbT`L\斛3<,^(`fT{rL4TPG35c{6NFrd| <$6rI&#i5ɴQUW#.8⏃ws8K5ϽyѬH\]8alG\GFbTOi8ٰW)< x0=O{9$~japۼAOT 2Iz;ZVʣU t؀ag) _ .љLJ-OxG ۣ1lYǷy&@+&QU߶V؝ zv9IXEr3'$wţcAODcxN<0^Zoxp^дU dGx,c2槷m;fyO=V= k'pz&Ou,q5:/iЋqFx7a܉dc4~Kbqo/OEKg Ǒc4T;A;zWm^@@C<҃*amUSUwaoyՈǧ Fb*,Yz͖*+B3+yEb S̝؈^ uRҤ84fCC7 Պ|Q8 uc<֪% ;I*JoK6wTm hw#~b誑\c.FQ{Ŷ; #ѪP LEyTrq m;](B]>BKѽ"A;T#9S!hm&6=E_QnUClg]q2%^G %{q'؍s2z^4J5E@%;V_.;`*8w$'Ɇq54NVCC ZNeVmQU[`{ډq(T$x՝KZlP`ۻƍUrn1'KNmt;ڝf۝хQH0a;N.xDk$6tmlzY6J:ɕy+8^vmLޖTئ^x B/caXڰ:)(g>G 8P6Ut= ,'jwɠQH3Us.6aE?tj Ħ8*Q-*,b?aw~&n֫ޅu(11qn{E`>Iuq3R1·MbsĝޤQM㤯lL̛mPpE L# ]bS& 0s"/ELMbw0ޯyX&q##II1qrc6n,-*LbNHk5~0tbB@ 7ĻkeHG:15mzA28wx'Ɇq54 0.wtP9m&vQ6zOz%v(? ;I*J6h'KѶqU-Fg ͨmsI}FmKNn˒T$؎F푸tnZsG2]ho[%^u>:5M=n7;S`UƩW!6.8%uv|HrWNmtW᭑Tz}U}Bmce mtu.HCG6u.n}'~Pg:1mvX'(16х]ȃ6wt#٩?(V;B-`!3PPʏxL\Ы:}WE#ݫ(dtcS'$NVYI3Frkܤ/m)i~=bSfTIV dp97|)SS|mF. x Q1qs͙G\<,^(`fPw2$iG[FAӡ:j]ئ!+HTl;7=訬*7GlZuB&.!Q+9J!Vslza[+G 퍫dSݫ.BCu1 ~%t9$6@+"Do dzb?"8/]aT۶#>ctWEJ j9&Sr/jE%ա"%w*TDcv{Vt%;#A;T |=$6DQ &z[PU0Se7qFurȌ~4XQ*ĶYRJ1R/=CZr}46kc۾P*=m&yh(F2Jb? e1[ib0%{UFtшl]>T'[b^FkH.MJo ij( fy&DR3JOJ<\EN$_%SgE(<2c4\xU f氱 oR.jь.bvbTg#PResHlxtTVI FpP!.C@qyT]ܛg;M&C䃙Kh16#ؕA_%.OĨ@<1qrū/RM`QyHL=cjkuVً6fj}?h:XYz[aU j5r3^ѷ9\87tLʷ?U?owgs ڱ ,Jv|["A<[T?tazن.XW}UCcCOz]48۽j_gtة45ijDŽ[M1k=iFik81fk#Rx`{&Yv;{H†!#Sb45~d$3F8}cŸwt qa˿m~t!fKxވXm/ƣ2OK=c ~2i9z{7M業j%A6Le2G;PَA56aom>QQ#/c@ْgy<(cu}{La /es<.;夭xAdvvl1N< 8/Gw]quz.~0ZYMdU^z&Yl F\S-YLXUr<$s1?;1uWIeiW8jv<=VdqoV {wdIaOacgՇxry͸X@cRe-ڀUzm)\yǃ+˪H aR,9bNE~؆cП zz֋2z$#{{!C&xxkYƹjhe ; Oo+~P;KI:+ v?\1PWd.jdl^5.p,%Dw˒v&s'6W08wb2'Ɇq5fCC NcVmQU[`[BnZ;~SU`roGy;r(ۖxt6n$tvsg=r&ڝɻ!7%PH5O*M;]BB+6t%G^u2J_Ʌ mbsĝPDž*r-*Mmb?"/?wJi,f0-W-<_H nZ> K$NlDQ<Ν8Iea'e,sjo@?wNc^LBEyTʗ m넙$f#~Cu^ yn$BrIlpN I"Do {;Qx痉 l&ʽCV36읡:W] 6TgǸRa:*{j4f$&&}xl*dlH~Q0+9:S֧R-DUmŘy "fo#g~͝D?A\#qhR`L\rcQDhi& o(K` rݴ%ZJF:$NlD  qN kh%'G%n*N1Y]q|m;rySH`;U}2 +j\謒 0n\#q|2]mc0MAPL ?N7n˒T$*F$r79QWʊ~ho ѽ"A;T#Pa|$6@8QLET^ϪHcHB◈ ֮un۝I.`ʩn*5rRoeigԞQ!&H0ᒤB5g5GBNbхƐVSWBCNҚD!%H0ۂ>-ϤB07Le8Wy]q3NlDs[իB|s촹Cuv+m&U~RII_# y_~{(gQǏr&T ;Q`[1&E'ȩ07Io˗ xd$FL-7VRx`߂twH eɅ`[FWꊎNڃ{}F:d] ɾSH3PvN8Qb~lC\rN׫v!ܡҍTHoDĦŊ|'VP`UcϤBA)? 2qC$i^eDx0vڅ I*1v*> &bxګ:Ye&ɵITm#%͏BH$vDHď{-LR0bE^&N@u}(;?A\##1*0&.`n9ȾU 43(J;t4Tct4VG4d:ə*$IlxtTVIa!gV] t yQ$U<ѼW#`&1:ׯ Q.1хQn#Rx`{&m*OTL U®"Ȃ p7IfHF t?I=y,`ag) vaq͏<>lX(Sۖ~DL=cjklLMzloL=uY=AMs<ǡVE09N D!y ҸV_D09QƊ^e6s'$0λmUr&-C`ZF-nwՖa#!wbX% ,^u606|T `j7Q0$ &z[Pէ;.&k 9-lx*#hăq{̝%TcTxFU,8Ey؛47KiF\.O“iTqg扐H2~0n-LR0bE^&ȹJOտfuLGFbT`L\rsW)< jg.|Z4m1MvbTg#RaTl{`QYUn&64?õBLd ]~f<@+U< ,C<9"$!FR^2F\f8h<2]bLIRx`3K( W+'*laWSUb6OMC_'m혠=c<^u@y~ ;MY/05)<@8Z*O썌ܩڶmeTSY08F?~0ybm3:oFB'GAT-.Nr3^!x&4Az$ڃK:jۊ!w֨H1,}e|g# D=VUmP5T!6qe-*Izv&M\@?6ԫqeݫUo"$ /dvIoLG\#1+'-CثT<ì˽6>SWja73]b TKmO\dp%^@S^3W<<̱_C.5Iq3s 6?:%S<>q =۾Cb1c(< 8Q;MzZf{ 9NJq, _ bDŽ?<-߱`a5;<@\љNdnao{!0i=fc~0oVq8uZ/ ܼ}bU8. f*yNDDUX Z, _m0e$ tW؛-{Gϫt+8(cu}{La /C̖L8 ><.'mĈ.rmp ɣl(x)뗁Hd˒ 1 6WT E]C&* ;e@̪2Z:A}B/6 \/'Ƿ% ^PU`rIk mm4K.vV Fdsm.Om QՑ˿u1M7`Gaôs`~lCVɶ:WT#P9?usGv:J*L8*E'3D\fv `{;x*J׶e}oH.z61u‹ur]m\)_Hp2OU.6ˊaD? TvڅR`UNlgR۷x؟%.ຉW貆pqn{EHf;mP]L5&b;qloM2N TH|]%O. ]xgg!n#*$UcNe2II 4X~R!K鑑U Ls͆9aB/K&@n kf&}=gԞ Bnŷ"}٩ 0P#w24dEOm+=yn$B"L)LETLmOvީ2dmf(v [mSg3 q ^ Z܇TcTxFUcISk&}I'<&< 4 vPrqAb+()X%͗e~f:v >HW ĖRx Y%8HNTP-ZB&"RQf+Ih`,twGƤ ^5YHѨ+?U|t%^KaA B@/xV[@><|<:w{z w - =a7G5- (4|!JL.jJ ȝ 0z7yYuSX8AYR߁ao,j'8[dzv/:I G 18ܙgvn2E'Z&QAVo3V Tp{>My Z`75&b<A"%tx DǰyHW *_tQżУ*REdG%Ro]q$&6_px Y%8HG %oU흊6( BEֈۉOiCA6pA )b^U'j9~wQ+k) AB5L1|vK9- 8{cmܫpH¬tj=0AL0yԖ=*@>+gIwmX[~8 ]a}GgKb< mb/YFGa!gUޠ ȻF1fƶ$?F3ޘg DJҘ x;1coP*۟8&8ܸJzQNdNoϞ u+wXύ;E_tXŏiW+S;P9s<#߈O*̲؋eNݱ?;jdO*SeEѧsdЃ`I9oHᆧ( OeD<7*2" ]`Լ)ς$6'%*A:jX>{}iFۖLW6wa EӇ;vƒZ&l?툝 51|QgKB<oc#>?1DgbU}EXw v Ѓ;@q苴 {,}Xq[1`/A 鸭8/^Dy `/Z|V~w֊- <&"83M G 18ܙy]>(;CN;xQ?ne]D)qbK^ ړڈha€WlQT&!ҷFKM/DTE0bһ A$O!_-9*g\3 J`]&K0g) }oh(_Ե͖y*1FRggowd#Քgy 8mp3jJV뗕4kWOH>خ9l}ط"bjk"UY)=( )& VOJ6sQKo@URK owa,=u @6v)* i*IG[ ұ*W}KϒTy!-^z@i9J/#b~t# m{[6Qf򵲴DŽvyJ:*guU?#=?߃oD~1\;.qC.NU3L}'{jOtp |CIm> :'"/`0VxDY$;ܬ (gI@nbdY1FV>ғe86d% 3[xX6Kƿ'Fy;tjU2d>sf5U޸fAK)aƆǕ] ,VVIOvlN3}7w$PS :(}ک6j_jq9 vhUźDlkz0@hY8&J+K˪f\6]:e*)N wU'z{zk; ygI?ϧ%4^Nw46pSm>rZM' T"PJ6M{c1NRv5 $oNno>{uաla,.Z ;m43ГԭJV$55C2X285IO Ǎ-t2 v1XY&Z=8fDbl+Njѧ Lg^An] ? v xoPSG+;%-ury˪ų3>I 0hmL(,`px";> g㱿3]I[H>8B! GޞҞNilVM`k+O'$DT욥|8m> z$yՇAR7γޫĶih/fK 2 OEG9n\ϪguG08i磻U T$ Hx`]T.mJW^@>"b ]o?vmP%HF8sJvD ɵ*oU_fpdC'Bwj%:RD s}TAĐSvYjS6Y@Y M' T"PJ6 ]vuܑ|pԖw8$z>6XJB.gۋHQ킚h7n\GE4gp=JY@5AR"<0k?:*fD+|EŠOAܸ]fq:q'KA p1TM':wx@mmg_^/R_GGe j%ä6δwRU̪,Dx6(9H   yH=$o3T"n|(:bQ;>άvWv!:sܬQub4-)@ҒMRиD/nQJσk OWfa“̓>A:D*P0wF1QABAT-7AVV<0J]m)2d͑wg@W^ld -BLs}С%v>1eZ;l6ST$FqgMki!`trj /#baVAQlZY* 'HyV],6ϬV< OCJ ؉ޡGaH3|A H>py'e5TY=|AuN#D)VZbJNIm[O(ً,0*rjn4Z LTVwF08<zp<;o*< J>pdgS{OcR[n] ?VBZj=*_QdJJI6fl>WRv5 $yՇAnWm'%;W !A)Q`Z8z\06uG6cÙNJYU Q[ E]<06Z 3P8,Cw#\ɎB" -0&}ul^.ZgJ0%|dى@Y̳'$#L{h$Đ#SE36U7ɢ7{zB.tb @JJIf@ +* rs ޤd~ 'Ÿ(:f;z\G gڃU T$ jRy_1*ft^ ,,5~t 7zw֖ߌҞW'iA|7EŬjԫDS.Ÿ=}Vx(:gWqtf# δwRUʪSHJM <ٍʀ*gq7x>N$V Fj0vcy36k{UG̪VsDՉѢhÊ<06ZZIX(&aE_ݠhͮHW,+>HlyY8Hd_wYKK AJ8)* !e(HvD^󬍻hmLۑvrd߹x>H ϳ%\4t|Ypdh.ԫ6}7e/3؅N$t$j3EQ}QQn\~?? LITU}0%=!WgxA cډ(F~\J~˳#$;"{OoUi˱ Od&%o2[c8g> {$K u\ f;YN'w}x@m_Pr}L]Vx*s[ǥ߃43_e:rV5B:l6PM4(ahPz*6C%^ (R;z-G/BSDZ`VыƏp P4z-V%Ӿ.# ȋCY̳'$\4 * PbHÑ -U?^En͇Wཧ']O'$DDmb( ޸dч#AB/>j)yQLIOȅ?њPkq):lʏF,`p8[%uuĬjtl Q!x`]18*posT"X~>+"bЀ_AܸX[~2KORv5PT.oӇY'FcWmNQQg]ȅ?;ci2:*TP3'4ffl8IIW3 $E;&5 y\6=$o3T"n|(:,m;E֙}8|dÙ8Ku82U}h2f =wr>R~lu矙t J@%$j3ۧBK>v* d+3L 6ӄt\ӝfQ팢cf0=.ecfx8[%uujtl iP0cnNdeW ʀ*]W,A t;[*=6f뒼F+}8Uh6NUy_gN:~Ge@ %3N֚~>Ì^Ҋgc--]3 $Efts}>Ge@ Hlq`< 궷3aA80bl#yƆpK:bVB5l6Pub(#A {xoi E5 +؏EfoO܃`zi}1;'.#B[1"bЊ U`>m6; |$> d P me,FqHaHv5 %ĹЇSWz%Uyrׇ6W !LbTX᩠V"Ìv"yڌ g;)cfU}DARPH>G+Z^ʀ*QtWJgV3#:Ϫ"Y܃(912jWN!2v&M iMplӖk]>މtT<<%x!DQb|bЈU`ZdžLGt-F/ P|\ŖDz*<x$R|W:b1vTÙ-X A'a[` {x{TzkvC#U v-^90ݶx! ,aFAR1$TpٵMӿW]A*`0E`@Jq'yl uT"E~;1D$T*C$&O`7* zH ;AB5,X?cˣ Ζx1so\-p#. 1dxky:<z b UV^O>U8v ,V:fz$*uUiz9_8shP)]<_ʒy.:J>pdhzvM]Un͇*_ȅVkBeUWT"PJ6 ]u| 0 %_)PL׾۪ -׾ʀ@lJDRvV׭δWIU9j!ojAQ.}0{$z7Z xKY σϠce `8U\=joIp\ƳYro֗<%ON$%[1_faR$"|D] o3T"ŽQt_āYѣvm%<ͳUfEՉѠh;v=}(`%($K`?ۦ`:qiImjLO%GG[1"bЈU`.x Uly Y%8TPP{}S>#SE96Q{rGԎUȅ?pw&|MTKa~;*Cp}̪"f%IFѾz<1ܼ^aʀ*#\PT6SDP4 5k YUw%'FA} *è{mL}Nvux&QIܩ%mM1<]a YVdNs6ӡ||  -7 Gd 櫽_׵Ex8+C:Z!XBQu(bu=e8AO}"Zՠh'.DG{ DQfxGH"6c`2]4}| 0tn @Jq\u0\2 W%{*PdaXb /yc5lfG$64{ApV}yD~=;A0x[2<-gˋX!ϒV  c ;p"&NcyT8ْ /8i4{o̾m<<1se0ϧN;eU"J6?>6 wō+Ƀԣdbpc<╤cm_xؐ 泤ô_@llu,78JX.Y@Ʒ[WIdje#OocteaVI<ÌH,?`JLImO'\À C>\#a7`Ui? OE0x sۨD#SE?F#6=}n>T OWZ CeUWT"HJjl8qHl\":${ՇA '7:\̪fxa4:KFc颶%ʟ#2 OqYKa7+8Xr1l (be|T1,! xqw猢&',8&.UtDd# ΌYUGΪFsD5Qh+dWqҒMD#]h}Ffڐ 5}Qr1T"Fdzg_,$R[n@fV<;yuJx{6ZZY0DtL{ftxV%v񑧷| g%#\4Qɉ`K0*>4 Fզʧ7fXWȕ~:1T YuE%$j3YQcVq#AB{WfQL`JzB.8*]P3HaX>+gڃU Eۇf0 y`Yʀ*^H>"bPp"z-^\@l_HNyGQoj;~+Ÿ00ʀ@JJ$;δZZ:fVe!`K EARPp-l>Ge@ Hlqg@>g6{;ӞYGU, gF`#fU+YGQubԨ'vtsU z4i@lOw"X% υY=qAaucCƋA+2TIijXJ:HW tZnPHV]y$0Vb4Wzjqs[N*ة@ԇGvibg'2 OEg=-K@\3ډ|dδwRU̪"$n߈j`찢 xҕsQt'ρBg JN+Ye*SL դXgxlӖk]>ü{I- J0J$b"bЈU`2 Z PHly/UWjJR,N:>O0k4u EQ5GeQI kQ+[\}AyأQPP x%Myqn35ViDTatqRmUWkXdQ9*T"kEc(89T*C{J` 2O`7*J{c3wDB5}-xp$㌰{6_Fl3VYxbx<Đ1lJms֩ao OQe;n>{6#5 OqZb8">xX2Es^94D(.32S.g#9qDCL\^/6uVk}6x *X9*ARP(^Ñ sڬ6M&{2TౝQ}OF:Gf0:i:bVB6[:HZF砛Y2 mJWX@>+"bЀ_p"z7c$#Ɩp$#jc;cwHvf8*)Qt"杁ǥ0 dbÙX3MW̪"$n$hf|17Q6C%[8EuYg`7qpVm?,5ܲ#gU#DYf $F92N_ oi LvwXI“ۼƃ(9*pňlA#2VIi;Fo"g 8 pĖUW 5 c%'V\CSEl Fܯj DWȅ?pw妠}ǥ0X!p8>v[qof ٽ_ )Hz~126G%}`< ]63tDVEUpfD|:w%&FAح*\<1I7:8۴QnG[w"JOj(J0:1()X% T}t$8 p*}6w{[߿큻#o>+Čl*C Co@gav.<\xп=}=;wnm܇qo,j'8[Q"NkϭN=6Y76EA|/+$*J{,} -tclV| (`i?aeƒs*YP߁ 0}g]QْASg'm;VH #;=ڰ A;d6c~']=y܍0+nEXGSI2<\?J*뫝>p#\y$5όUQ{"h晶¿W"M%isXEYDJoFodzJ~N 1NW*HlӦwq5-meuJWͱw7c"fDAy@viztN],ѿsN@"s%'G6`jSm 7fXj)yQLIOȅtfćg~{A} 8C헏>Ù*#fU+$nAцx>I͉ o3T"X@>+"bЀ?1D~0Čm|Tr=| ÑnG`axDX>}2rb`QHɂ90Xp=1l (';qA7 _?~?2 mJ$3p 3j 'YE.G6<γUfE5Ѡ7q177Y巴!خQ)x%V?@xz(9*pňlA-o3=`ob)y 8 pĖUW"5 c%'V\S5f@՞0W{+38*DDu&a7=Il XYu l ɨHzN 9*fDB猢É' 8J mUVÙydUZ]E&ssUr(&Ŋ>cmB[tHԶJ0J$b"bЈU`ZLOF'+H@\p-75yWx YuHjywt't8+C:!EQu(*A7ޣ\$FgV5(I7tVv\'EY"bD+ 0mʛ5M't 8dՕG"Kp 츢(upfPdaPb ڡ6hp"{L3ퟜ^oUL{c3g 8P x $< ԰ J%V/4!]eH︚ABV8*8 L&9[ p kUFіM?DBF4(3J "K& 9r[1ȭ V`6<ЬbK ;p"&NcyT8ْ ؉~d۝O̪o 0xQHU6CAh RYޮX%U%9߁騞7xqFX4+}G4}~%QB؊!DJҚ xZtY~ z3dHǍĪ{juB&ۉD vXǍ;E_v؝9~x:mSlA3?e(asTz#s5<fUMUh^vY&ook$}R!2tAQP;G= sػe'G47qE$O?}0e:%;p"]A*`Z/QHV]y$a}lvTuGۖ eKG KQaQ|<67wT ˩'ux#$ȝyo؅si'r"J$$߁M0x'-ƚGa]PvyDǰyg=wHsz1LS{;l,߁{kTƗvWU Hg P8P xvI< 8[܇cGvXG 1D3ۅsi|#@UیU T鮞@V7<<\feLU΍YBH90iua9FTKF֍ WOթذ}!63t[Aܨ{n A2nSJwxo5,y$2j7{<ӧ-ox7 x;hH5[gu * Mc*^DE)i&h:z-o\*kF1؝2>ޛ2w#*ex_u*li8e}1|%k=sC,Yzl #1ʌ0Mb=q^ŬjÌR>4i`Q{JYz'2w1xq=^z$'ü ˌ#jv(u!qTRf"|8%p=1l (-NZ>Ge@ Hhq@>gӶx;Ӟ'YE.M, gF#gU#YGQMb4ɪ+D5 P{C)Vc'ɧ lʐF/EQ5GeQI kjPnZρ Oo`eg1{4 $)o5:mfj4MHW `Zrl{<Geܜτ0X!p8>t[!7l (W/j`Q6C%}@>gmPo_ Όs (|u+nn'(Olͷ6mH“X(J0J$b"bPri?Ub3? N+H@\p" $JvJ$2U*."#1 ڵCmv73> NNx 4zQhV}ymz $yN$R$o,j'8[qg ZTؾJ8O ?ݵ æ <'FLtX!޾JӁ$':]2d }NR[mn#j;4@亮W*TP+E6tյYl31l ɨޢV?z_YBe@ }Ob\PTŝO'bzJ+Y4}u̬*B9J?MUdzJ k&jR3p8_[Lғc3x%EmmW O%FG%Z1Pu1hE*H0 x]6J8 p@#@ *ʐrXW;fP*mtm5gVԇ}OW"GtzQżУ*RY{L~$xN+H@LLӮUbdՕGװdz`}9M(J6C%*PdaXb /yc5l&ۿ'r'H<l0%P׋@؉@ 2jO .>8iQͣ%!gbޠVl_g'F汭 $1<3I\ʛ(?u_zP6ÙIX( Z!MATs\U0m.*63 {`5M_`z8QgKB<;̃6SSg'm;VH #;=ڰ Fkߤ7 *m*+ !p"ՓVx4W=絷zcTA/tVTW__#ABi?hW]+ '{:3gf^Pp`OvY#8i:rV5B6[&H ' 9y_1*ft+gY$X j'2豂O}|uyd`w >_GԎ3#w:q ̨:"gB gڃmnnoi LŠbA鍥VI:Ó>Pa bE6Š$gtx#m3HW Db ɪ+D LNDn+TQ'wax@m gO~2 J{@gBvN8i7* (9H j?xEa'Oo3T"GsF{(QVYgF9UEHlqPrbdԖnEMVG>#ئ`jZ@["j[S O%FG<"(.P% 3w8P nc}6{{# l>J?1DI@~h|6xybۂ#8}p;\E?{>J5w7;ܰW=ݩۙ㶶ƫixe p ڜ9&(C39d60Y Ed3ꂢ{cetz۫arAoM>~jQ;հG?Ƅ'FỸz"bp|yg05_G }J5Orsݳ} (8;A} )(@>;h~g ;{)A7(G5.xp ZSYO'=@#;#$㑮<}.dN+f{+;)]xHO<ozOK:7h ;ʟ>p#.(6C睒|%RWw:GPTSP~V >g)^z$2t; r8P0捝aec&1}AN$To8ْ>,2`ԁcGEyH<+° DK12F. DB=!D'#,ģ$GJz/ J$G;ӧ>p#\\@> <gΪx~y4L n5avfE{ͦ9춷Wv8d`n4U} ^=Va5?#w5cl:a/8ݺ-[=3wk~S9&2 ݬa5㰛5tQjLJ~='}zGb_Ev ޭafO\3ߟ9oe3Ɣ_F+e$*u0X*(:6'B`0AX|XїNl6.4US{cϩҸW;31,ɮ͞HF" ~ ;G~] 0_jdBbIA321ᯆ}(9:*惾rԦ}ktn͆'d ʟV_*ARP(v,9qp$Hhg;|,6\o3TbErOl 6:i:bVB6[:HNݜe9* ` X1g 8AweX=cVÁk#jo1CŠ 6M]!o]rTRV"|8EδGRR̪,l(9H j"دLoqT x -猢Ƙ+&*ztĶxÙv[enmPr~7n>^Fe@ b 猢#' 8J UVÙyd ϒJN݊묚-^r8M}L; gu9LI}9SKdOGQbqT"YVdN N+H@L0#?'ΖxYy6_x !<;gRN]B}e'  `ed7qTP&nMR3x. ^++yx>HDgtW{Ece)$nR$"ȋW x ݓpp 3^ޱb07VYgF9UEHh}vQrbdm\nn%*(ObR3p;YRV- `O_qQŒnAkGa1\{6 oQ2vyDǰyމ:3(vQvxxMP\x%2(g35vy8 0tdՕG"L;x:PĦ DX#2wk=ȝ }!Ѐi<Ьw=;wPjؕ}pr'8[q&F J8OcHb\y;lJ-sSWXY%x Jv#,[EYE;vJخgU1),X1v Xz$NBcܻ2óu扔 `$Cރ6"S'$6x',JHI9Af;#t?24ȭ x8oJ e#υLY`~N8}pˣ Ζxp籓x qȝy^_o;ݥynt*yf6cvm/껡r9l.g"2xD4X7(xbݨƊWp\1`G|&@>G_j8 ̞ 6US;xQW;$1yv @4H O;bjo=j:m>kB.Y @eOEq߾7) goafVe![f %IAQٜd xYz|Ψjzo瑬ڱPΌs -fu-~Sp8m.H3p8'ffI1H9QobE O%FGDƋA+2TI"4fa唐gt NW^HV]y$aA3[2d$9BB{4$b\qovG rC3Zl}1oL<+fx(3H-SڲGYc%,9`(j`p"=a}7Wyp$ƣko&9B@8~cV;fbx<ĸH<6Xѩͳ*m*]OՒo?bo\HYOU+{,}LF̱ Cl NNx `J ;ƶ*c'*B 볠$6Aq@wup\, h vzV`[$1T`<3_Ɇ] ۊ %*@,;T< N$^jyA+YXJRgvu>#1H<7ty.:OPfϟU+{KTO>8gmtost¬vyDv4Tx7-<Ӈ+J$y3HOU̼|rMgy4YxDtH OduTOƪ ?pXрw7:Ƶa5aw>vn_9vW+S;PݍhH ۨS(fJ$tVdw$ծFCl NNx `s4<w|b(H <3q%!}XA~``cU`<՝yfۅs։~b_d6cHg0{D'0#,ƣ$]^9]IwHؾWa|g:7"k앓l*̊~cu{.6{տ~qGUG"7n\]ujչQ=9ctE5 msFTKF֍ WOթذ}C8lf.C ap@O<ˣ s0aVYp syc<W iӖ |U 5y.f"=݊YkxnV<&FMՀ~e߯Mll9^#ll:b3pS-u[4zfVӋ<2ǀ͌c"Cfy45H~[`՝:w=wVFZw)}a]/\c5f5[ZJ>r9l.W"Ӎ2P`%߸Qvns{X2VQ_[lrmsbKraxlΌ3cUԎԔbj@3{:4TMIY*j $F0%jǦ;^;v m j,-Momb*O"( SaiS'#k5[Td45B 0K}(8 A;&Ƕ*G"'~owOUo7eۏ]?1V$'R Iڱ]Q|{<0janRW=ÈsXݖ8vVOislUDea}osǝ/Xnq;[ =o`L9h]<eXt Wp"wn^WQU%1}X1.Gwb<x$rg?7ep#\\@>+5ѷsV}65U7c"_Ƨj'I N_>wGvcTink}p"}D R(ewA6VHI<-sgKB<[c4f%7-/͡0#1 ܙYZ]oO3avfӭUoYE }gyTWX*~wc{N|a`u:~%r@0zfVﯷ;:ߟP9lfR:o~'9M] qܸvRUƽ'guzƊѨF_CwXCa+n}p kX}`{n l3ߟh;w(CDWU 1DF혱fm4P Gۜ x>3VE;/51`3㘮n4 df^j'ҝlwDwJwoY.EaY1lVN7,3Q;Ȑ}WfÙ;;/r[uTO}]l}N:uuTΪ|uZ"whlw,vf#69&S_.Zp |]RSRJIEmUqĊBz`S]c&0pnt96 "2 .+<18DVm5_ m>}z Nw8 7\#߯Dz:4TRry3p'2$1FLՈTBgNTB_j"|@]tXOrظ;찮WȮ+Ƈd-a3}r \8Ss e ЯaF"1~Bm_z{sAo]j$I$[HJ"Ddo_ljӭVOrs}\_A"&޸J>SM?ydE?EO!ړJ7Ls[¿t%y oM+;a"Xf:Lz,MӕB^-YTb:>u5޸Im6yf0* KDq(*)z}1IG]$k觪&95rCy@r"RlcD%vc_5!s;QFh_E(_$:el)h$5 Ʊ!33<-cbo͎␦q7x/t;ITy! LG3IOt ǍUpxY 9;u#0@DFy"=&㲧/^${"5zXQdn9IO7 G DW}y~}d}VfaH=|\"{$WӍ;\v8 n p(͇'s!"uTXᩣSRd;LRrv7TPoh%}ҳodY1Fۇfɲ Ooz[ԇow6U+t J@%omƉC"Ƚ!9qԞbbғMԇGvib[rP\.+<LDpt:h8Nf36i襤B Q2xr XjuP?DۀFG3Db 8l"A:L?[m+Vstt# 퉮,:a{U>{uաla,.Z+6Kߍ,[ю#ԭH"5C,q[:pxb`2#Lj?zxuq:̈"K{/ٌEd~B3FߡJ}a+"h Or5+{=5?I]D:KI }8UpF!J-^An͇'s!"LZ OG۞:NVԭ6Y|x"HVBZ5*8tTY%ARPSQ}Aɉhˍ;'_a)QKIax@mCǶ.Ÿ}I.vƥ0zDݤ~MÑ}gcUl iP4N 9`ˮM xK,,53w۾lԱ%O~D p1I{j%wx@mm g@ SAD|v3zWK:*UEHf IF yI=$o3T"⮃Qt`ŦwsיUҮ#;p Ό癡j87AT5blҷF@--$uJ,W05vlJ鰐k OWfa3³>ȺpT"J2L{f|$ N.71VV<kaVڍծ~uhnvdgծn:*h5q!thɮOblalN;mvԉQ>]"Y@ki!LUQ'G&baF7(6SSzqtevd2긫V% Od&$oČh( i晍K}DPSU=)QtȮ7fq9uhtT"fJZ]o{xY8  UB`UHDa47 8QɴMe7z|dYxa<;odd`%D=L ؍ATmz٣u6VyB.xTPdma'b.ɉOp)HOv:}jcXj8Au6\o_z@Bej'bVq)9 )H#O_U!q-T$ tq Xouz*6C%b^"R=Ow#\ɞ" -0>Tx/.TU']R,`px"/d2zs,D#S5Q֛,ad}n͆+r? CeUWT"HJ6 ]n\aq,p$H6\U9PpFηF 87L4۶Sqcwp=JYl Q~71l;+Ce@ Xz`$e`1k 8w]ϳ5NR6'Grh1Y'^':}xDmG&{35*TP3'2L|i36i襤UJ q@>=>d xqwsAsC`48&.U0HwdÙ^`^x:Z<(9*llA-h3= h=fX> N+HR'[n@fV<4kX?g;KD ["J%ysdp3n#B4󬎻hmL(vD(޴,$<ӕMɉ#pdVnᘤ^/lx"HVB.tb @JIIf@$Qn\HޕLLGSB3M^;Rz[3Zk8gÙ*#fU+$nEmD|9y]18*ft^ "+ '2-lĶ56ZÑFoဦjO1cŠgUȅ~ʀ@JfK sa¾,k3푔1l (ZZ#2 mJ$3p 궶3o`7qpVm?,h5 UfE5Ѡho=0[ZrAo3#]h[XIO%GC%1M1hD*@0zm6;}ƀ PHly YuRPq}Q% %Oߟ%'VũjS4j;OF%Ÿ{;Vx*( (g,qi:h'p:fVmPrD$},9*Q\PJP1 NWJ_V}}FVÙyƳ,AWN!R;I@\LҶxlӖ׺|W@xRK䣶5Pr1AO[Ap4"cd$;l=$;p"]A*UlyLɪ+D5,vpRlz)AѓG(6e=q:*FgV5(IZρ^uHxz(3F%>˱ Od%$oؾ80g6.m"ԇS V?,6.7iMDuN#D_VFjteU@YE/K& 05Sk}TL(MF08<z&N ϳ%I普=qG$1T~6ƴ|x"Z i?3QdJIIRlԱ%Gr"S>\ f8횙'n'w}xDmG:u! SFqqvN蠬h g;)#gU#$njAX {^g? xҕSzu0ϲH<NļJLanm:Q\JD)iZZK}=.#OocA gO%#\4 (9$1TцhDզkMD{OOȕ~:1T YuE%$j3YQcO\nÑ sd`J^GSry6Z3*]PHwGT_G6cÙ*#fU+$nA i`elP{JY|ƀq׻b4Z!ulq#9v>\̪v80Kj;OtFc颶%Ÿ}Vx*z\3L|i36i襤UY%q ;+Ge@ Vz0ϊH4k 8AwŋmkɉId5ZSzvW#j}GVQHɂ90cbÙXKKW̪"$n$h n>}\>Ge@ Hlqg@>gez;Ӟ'YE.M, gF#gU#DYf $Fsd zkFbFѾ x%VcvVoO\%GC%1M1Em4l _,>'$.8r@#O)OgVZm>.qn+T÷|W>7ތEmKȕWfTX᩠v"Ü :@tձB)AQ @>+ڌ^ʀ*Q9@k= Ny@*ccUpfD|YU8{%'FFў>jxVd> + mڒyt}z/<%y!DPV PdS J2Lqp鈮hx8]u NW1 $< 0@،t|$*`8+C:!V EX(Ef&a748ANk]>6:$<}A<((R(Ld Ӧ]4p"]A*`iuR;fl{v[qofUBAQ+~Ey`c`WX2 mJ$v|Ψ7{ H%aA+̈<-JNWP"&jR)6Ic{Zi89$<_%5tQbqT"YZm=1n=ԑ$;p"=A* -7 ׬J5 PEK^p| k`eEAҠ2+M{GeQ&AEpXkU[Ow"أѽ(;<<#y,Ld`uzM't hu6NFy YuH$ G6{( Hl@M5"c1<ڑ6'r'H<LB 4<~6=Xaz wWuN$Vîp#$1D`kVۇMVy>N]B}e < !H'b:oԕ 5iXYu (JLEWi8UzOX3)$WBEM[LNIL$^IImmWO%GC%Z1PM1hD*@0{]6J8 pĖQx YuHNVP-2䴨5!]eH`ABVo`ᵕ)$~ZQw} =u덢,Pr"`Z,# Lͮx p"]A*`WZv$a|x"MAѮp=PEf&aϒ8Akآ~{+CS1` bvM,"%LM͏T{Y2ɪ+D5Or[J?19(p"w|! ic,EG 3c{=K13l?N>NcyT8ْAY? wVuIxbx<ĸH\ v88ϠWm'%u6: ;ϳ%P`8z\#4guG6cÙNJYl QAm֏U hu4s*6C%^,,π1z-G=/"SEZhVQ1>JD)/Y+#da5>ቬDBp< J>w>*0M={M}6x ߿/~f!2ɪ+*]!) (vQq%'.7@,? LIX7^ۺ+o'n1Z3*>Q;->kY l iQ ,nNk1 xJY5d{N]XZhuԱ%O~D p1ᙇXTk,#j;4l<5IP`/K=.Mm&>L{'%]u̬*B6[(9H Xj`¼3*ft,:8EGM.^{Yp"M\4舮؇G08γU IEsdWqRLD6 ,W05v,g$ρk OoP_6J0J\1b"bЈU`ijml668 gHHlyYHg_z.sz8?F+qWJ^i;j F'2Ay^6 !vJN[bT8L06uWVT>}6ĺB.}щPdJIIf@$BJNq#AW`JS:<6Ls\O7 vADG#}&cVv:i:rVB' iPT40xf5*fD{#,A 1t5w5xq=m|#9畓>*zkGu҇a+a,H #Kbÿڃ--]3J li|f8*fDb;9ۙ,8&̪-?}zdgp83g]1Z!:AfvP([ZrAo3/خQZ=q7:ki}?'.#VȺ"Cd(m6~c+H@\p -7($<Ajo;tf3;=D CjDJ>N.NU;bΓ>+I <%y?DPV PdS J2L[66]p"]A*-75@#@ PIG3[2#:!UIbk| qvk\_}:8ANk]>q> bh)Sn:杚]4p"=A*`08)ƶ*Gװ>ɸ;ޭDosT"BE֊ %H;9TÆllfp"wSS +;_@P߁vqQŒf$_Xg=@Cǰy*=Ϥ* h7)vgRgfA <Ri7@JvD-j}Uiwv :< ;ˌ̳ŔKFhVACL^/6uvk}60OȅVIQd̀HlE|o> ? uxDm0%=!; =*Qt=OnE[G3UPGʪ u\QZQ7'3bT ]JY z'2殗~cPZaɉ#pYu _uXfQ;R jGrf`V2 5 saHbÙP3P̪"$n$>p|k`ߓIʀ*^Qmg='YOYΌYUGΪFI*%)$l˦FbFW"`% Hd <=a bD6Š鶙7f<HW Db ɪ+? LNDo+Ñڨ^b ĈXQPk B3ۿPXn :ާz\n2N{=@41l (뇁w*posT"G9^.$T F <r/VYgFϪSHhqwQbbԋcuV]pr8M }m] މ+I < Vc蟪{@1hD*H0 лm=6$;p"]A*Uly,ɪ+D5,vpRl!E *Cz z*@]$~H솆3Y2}7d8 OEY̋="]D+(0-mf[o8]u iurڵJl{/DżUjyz b**hPqf`$9Q@B8UgU2㪂qAIسkhPZD2t/<fx H0J2L{?`g}'t ^hR;fl{_#ABi'PL`JzBZ3;y xl'~m#gp=JY l iPw' 9rTP`WĊA~ƀt;=Va$Doa8UbtOV{ vRw8*)YP+@>δx:fVe!`EARPp2՞~Ge@ Hlqg@>g= ]fG5cΌYUGΪFvĨQK#&.&\_:0 zSN$}IA(J0:8()X% `ob)u 8 p*܀ρBbϒ.JN+ssUr(&Ŋ>cmA[tHYԶJ0J$b"bЈU`)G+^O PHly/UWj_WNwBm:Z!XBQu(*A7ޣ\$FgV5(I'3^xQ<((R(Ld Ӧ]4}HW `:y7Ny YuHW:\r;(J6G%*TdaPb  հ16hA^$c @x.@1DB5Lq|/[*(kYq'J/WwVYpb{z w{nm{:G5.xlAk8QgKB<; 'n0ufw-*@b0)Sd *,yfjjDTDz%]HV]y$a}lz| `g}$ 'r+Hz1 xR[H><\xDz%y   c<ْ1|!JN=@#;󼼾\Uk*m*'D#6EQQT"'F&9t[1|ۄ&xlyYxDBP`}wDB5Lq|uE-h v7:/-*@b0ܙse2Y`I/@UیUJ~ϧHOp#.xǾY 7mDJҚ簊x;ډYM~yYyD=H W`ZunTOΪ ?pX-DkܸZQݨۅSP` p ڜ9&7Y`ݎ9 )|uy'،T]PK L*ֈLB=E[.(4m$xлeg1 ̀&B 0UoEʳt$&x zdՕG"'~owOUwymtAlsWNN@_h`P >y9XMR-pg@m7tcD7G5- (4 Jx qȝyvmRUk*m*x^($<Ê"ͩZiPtAn+F$]g%H^O3}R8x y<WӒΠ >{ `V`~H<x$rg_uy:_Ӎ xJ$$jS|]xHO< cͣngfǶ$?y<ӇH]IH쭑>8i>?s\, h Zǡ#Yi:,#G"wIB9R *m*P`H%a8Ƀno. h*IA?WNtW0x%)51k'F{$½YM?x{"&{~=Q8ȣ*#7:Tܨmv"csƘcVn}AtunƆTFlؾDЭ wDno[7-HxuZ.t0+᩾ ԰XOȰybЧ-JBYYLlYug<o+| ONOX 4o>bٞsFbtVE2pS-WOl@fw\R05㰻5,(qA)ui$ Oc{a~Eq؀5n [|LH6ooeWAQ6nƾ[4W 6>vq}KM?cg!sTӡj`J2}/7*j'"'(ROIUCìX\*md5W諡}ScCrDYe0mW6K% ig7qg=SCy=sC,~e6^L𑧷yd2ʊxp.az$2/"6y6Vg$aF痏,`px";$1czg*9pGQu6zBwӉ2ɪ+*$%QΊz$'u8$wei 'Ÿ8*]Pǥ-?gcUEۇV0 y`^YVz0ϊH3Ƞc{3f둜svz߈uXfQ;T `?r>a~Ge@ % j%Ì^Šgc--]3 )H C~c'Zm~2 mJ$sp 3iۼiςجG&Gy3#yVՑݬ&1jԔ*%($A v=މ/@>g(ymg8(Yel쓲 Όs H=KQrbD)8UL1)$WbEñӖk]>üv[SKdWۚbx(18*ЊA+2TIFin>6f:k7N+H@\p"y)U{Lr{AfVrO'$DDm@b( -D/p$H>te|ܩw><6 sdB1Qxb}{h6/lȘ^-Ɩp$ZX0S;V ڱ|v;ȕ[ʀ@Jf saz3푔1l ɨo y`cn/Be@ Hlqg@>-V ozG+E#yƆlY nQT-8}Q(v[ZrAo3/خQ߭R L$}=DD#(.P% ~7fw~ƀ PHhAy YuRPɉ=Vr"m0*`16.  6]! Ψ P2gڇn+fVe!ޯTQr~E y`f`w]ʀ*#9f*`!py$uVÙydU[=+[tsU.8Ixbdoup< jux'$ `FPv FE6Š$#ayOHW t[nj۪@#@ ~wsB+S g`eHWGtX++C(lqo{ʢrAo3ZU޴.ǃ?; DQVxaFAB1`"dx#!6S7M 0GyD 43=HV]y$ap$EDBS#ڡ6hp"wS7 @hӃDY@P _cj>8QgKB< m|}>p#$1DkeVl*<\UN+WV?H"qjqI_u $'v2K޾ B߭]ub[ m Fxj(tmĥ0}k5δSW̪"$n$h[oQ`LWP6C%bߓ@>gFUTap"vo7ԾUpfD|YUs%'FA+ xV=aͤ@\LrxlӷӮ}L|/8QgKB< mgt3l;V$ƕG"wynBѩMUیU" :oP;gDSQ8 mPC&6 `= ;p"&> < 8[t' v7:sXG 1D3;Z 4=MzfϟWpCD'wŭ h*I嫞Ur~Yg=±*#y權7-"fi++Ik*̊7(n{}v*7 8):=S`U?@b6Ow7;rX[Ec)y\$]A3D2dtR=!di ~qD:'Z Lg=;ȋ#O<Cy.7qD#SEg4-lxk'I6CeUWT"HJ6 ]WIPAGp$Hh?j)yQLIOȅ?;y (:{v6Rv_f|oL{pTՑB5ARhC Ots";+Ce@ Xz`$e`1k 8Qw@ܿ]'A%'u8UbtOV|gGn t*)YPԉ6ў>p~-?LmP{ -sF-tÞ'āYУv#:ͳU-fu-T-*vtsU zkxVbA入VI:Ó]>Pa bE6Š鷙7fwy 8 pĖUW"5 c%'VÑ @ 6+;ʀrSP;E aMi3|O:fV!q-$E<"惰|aʀ*#9pl=N$0eUpfD|YUw%'FF>d䃠\<1Iئ[`:$y Oj,j[SO%GC%[1@M1hD*H0wClc#t$.8]ŖǾ+cj>8QgKB}.dҩ2Vo*XB7q+mZ#jv>ۮ v9*\P`[ aV*LPoHofUQ$Eua}eʀ*= 93t7{(3,*R(Ld 묏HW `:,vN̈@#x gs;ܖs|UUF 10m,ޱ¢<\xDp>13܎'sy}eGgKb< mgݱt3$ƕG"wyy*<ύN]6omVo3V'0x j()<+T";@Xd5Uc`jۄ6/'r'H<l0%;*c')R 볤{`NZ`yT(bIGsgPx v?+q0ufw8(@b0ܙd.dNt6 xJ$SI|} 8YN]Ga!gUޠ ;v?+}G4}~%1j=<h+*Im vfujBޠJ8'Fx$1"z9qFWq>.g5/82񔮬ߘugf|l0jY5i/S$Zwj3 / jnz$$7_ $`QkE]p뒯$zVlE8>,T "U\vādՙC_znCa`U>:( 62 T\GAsXuY[wb,?,ef97y_u7{P |]ҼqQU:1B 1M{8:̼ۭNExPF>wpC%VR3@P]RKCj0fjZ1,+HiE(UcRA@'볣EP5X9vp8 6}^>2fރ#-ra8Dv.dNO~o*UcUh,c#GaaGohJG c;@* 8W gQ;%UyjVTzGgnyȲphnBj?-T:nwXĵⰞ;n'y͍jiF((Cdz@scVz- fu OٮIG9W*0Prհ+?Ƅ'w敏 d10]dXKYm~x#J A*xRb;װlnvPF#^ Ol?6Ǜ WE & 9q|>McHH"1 q_ M'{ R1P[+3-S]nB,Z Os^nIz hpTI:C1l+7΋&+%r$yJ!vPFdq؂[D41,C:T:R&K~ }ѴwsWgjTϯ϶98n;ӡm(a6gLzܠ.AzT+b*HH`} ErԲ1 EP5ˣX96paSEs< 'xA$3[eqN: f_A*ۆJ~}x%yPs`Io;8J8WU2$D* xWx8;"On˩`^@x;Q*yRYVO.+USLi60o k\00<,SϪEcy)Gڗ!Fe+H~,n亮n/A2n%𨷣NcG kX!e-mxgrd͟q6QNT='VrXtV|rPP& */A9˪xIQ1 + ^c.*^bZcx/wGAdY1Ⱥ*H6^,=˯ ܀HV9D 5 F=92T;w96W #ČW~gQ @\nJ/O)x9PF{nZ>˜w\CAP~CtpVxD%mM \ƻ,A!",~U`'Tj넻G(?16WI7gY?('fW Y,+ 9 䏢(X .J2O ,ClփY+"T .7Q@!aڳ^|i6؂:N2A I7KWgحQ_xjxnaFdN1t/]V#!6b/^ $U#?NHV9_È\qEQ J7T`Ye]r1Jհ63^ $YjhVUv_dfpV/[e~hёcoP{xxlpTI>1"$1|z3Ujכ)^* B}1.+q&&qPeCR3x6xTӜQ$ӪbImJ<jуgp}<ÖQWhs=6ײjrdG-*2ot%^W mP򋚓X0J^nT1 $uY67FLdUꤸYcC/mYVO:񉱙}c[1]ڑ`= {Ve)#̠DpW ˬd]rd X\U>[( $!Xw9rLUgD #& +(nG-j-~ۼ #mL&4ȵ*| 𤂻5j On7DUs35%_Ii<``^/h P*X^Zɪ3=醕[4 Q-J5Th˻c(8 0UL`zi`}wzQ4 .c$Ԫk.EP5Zj9kZBra+xzD8*mcM 1M3Vquj)JT/aqϏ/]%XGhnL `E/N@8uȪuOk欒O{ʑ=r84%K u*0jvۥS L|%+>f]\K"T bW ɪ3fs9%劝1K"z({- 9q豽+_bfKp+,XDw@of曆N Ce8DTOC{v}Tjzaz|c$QЪEp5R˱%ǞovH{<*mc88sfUj3 o^vA܈=6SަQլ'n=J>}TϪ$^Ҁb,PQq+4"yMKn]dX+=6Ӣ&8>,%HPj^+յ9r8D56۽o{IKpwrY~0XDD>o l @;E4 G?Xȱ%>zCVvm 16otlimI~pg"_|rߋ QnYꊁvy,yT% ^TPa}6]^E`$!X~Q8:s jX]C47P9މ\CUDx9kmge=pD!Hb„&'ZUGK`ͫՙTYDUs3/2,K  /̴lŮ`MA*PYb;@!aD3asmPo{˺c$XYf  @*Xax9˰`l8Ь2m? #[~,ag~ёc'K(=1g_αg@lG #q,g=|v!T}JTF왴6_"EzViw>* ]øՊI"bpW }UZQVևET5<7aQ~.ggOܽdͳ.jE @8C װlnvP5G>|OZjXђ`=ABnd`M=E"7UE˽\zi%\ ~ rM-|ta7OnG< 51"$1"z9q*?G ׼\"mD\y#( 8Tym`5bvm H*Xal8ЬpWQ҉VpgGj<:rd ő 6ew>v3XWf.E"1 qN:GNfp/ mC%cdg= `-rë#0c7\8NzбraL^1qGgpKXI2"*ۉJ?2s;(Wenv8f2quZ*WzZsmp{7W;l1[3#[=npMttv; C 06'FD"#g|yoi6cY*\n3J)ѥS#XJ&ՃQ{V2T@˽^îkF24<U ͼ r`.J2/aE٬Ky#'"T ,K!#$"sn+/_NjXMeh-;(H TLX yUT#f츔y.W86pGocsQM3RMI3qfJsSS<7Ou*ۆJ~čh ¢lF]և -ϓ,Η9`=A)P`EnVlĩ_W*.EsE ,+f ő 6 1nie6x2CD8eqN: @ qJ2jv ~,zZ~M&ݒ gTI:C1jdE9V̼vҏUM;(C񉰵 m=Ȳph:TCS= mmڠiBL\3jS aM?rDqՉN{㰞}flg< Ό?2氶li$V4mEQ[ӮQa`A*P/*J2^vUEӚeEEӮ)%qsT4`]coDb=|qoo:}.-z HExPh^3hg-" w.ִz>~or:Js%Aa9Jc8{cq7Wm[NG"*ۉcRo!m];&92"z&:PeTOC{;{"t^Dxþ?Z:lzY5SLS`AFeGa ,n[~M# #3o(rV<*m]B 6g}n@gCyDhYUF%of曖jP V{t֗`Ml8LMlf39̨&n6Wύj56(`dl0R82[}f9ޢ,5%su:枖j6Oz[4.tXGwQ (ecvz|TȈۼ3N*Zַ,CICʝ&QRTC қk_ 6ۙϖjStb1}ƙϱCu!}4"/7Ļ%jMea֫vtf<l߂$rԲmj:10Gj~ )Z ,8ơۗ, ֝GqI"CD8v!t_BT J2QK" _GőUJzF?*X$p/Uͬ7ݪ$bVCJo ،~6'92oDUSLi6=Bnk:-ӮUÖ8USLSp/V5"PWXDOރn P;X^C9|1j}8?ek^H{;Q={i ig ݙUozn"uz>XYO};ɝ8ytɽW_jfZ=7ۧ'G۬P  !e4?7=?<DZYP %3uz枖jsOz[4=Q]u:~XeT[NOuf}K/d$lN'm*C'wʦolDE$1nsVKobpfmplvD;A bo@}py~z.W[Dsbt{`qĐ+Fox/[VRKWľJs1NE9c8VcbOpZ "*դnO=Nx*MLߡNKtvo %oNlNa]lNu6TaF5-u;4>bfwD+6gBFf(bHavja5lsQC5(%ېL( ( (G5ņְլycԥ܋Д6rƆTxbX*6il56B6 r4MsnFn9'T^ȈlEД O\dߦ}چX~ IC5"<ǔ;15l"eSMH-ִ\[q;i 94a45wkmɝe(i8ns_yDִ\rһi8|G, >Iws>M HlVZHlEAA%R21!M}":5DGE[mFJPܜVςW2hB_ƫC Ff̎`[kwRp8gjG=m-M;!9vxbp_0e#f(:6~ufгa+{cvn)) * IW59Lʫ Vk$ld[n`YE0ǹd{Fy`o|#C*+4z7:ͫj"kUɉ܈**:9]򶝈ZzŖA5_רvt^nN]yfv3M8gW)^t6g7^rkѾfU os u R5JkłAJo "ޚ#ž#/"CEZ`VB\9csebù\b[n`#Yse!XE 6GJ1+,z=G,UMs=NNـt2@$j3ۛ-Ers$Htsn|Um0%:9sҌJ"J?0G;seƶ`s=x,gU os semW2~3se]A%@-< (Ζ/eae&rknR2TU['5$ w̕SJ`9z!9mQ"{sB-z-\ LȬZХmlYwLI11rT;`kIsCe0JYnXJ^U`/<2c3(2+.+LW*k6+"B `fUCgՌ +I4TH% 䩉0}F`~zv'5*kjx^ǹg{Y5/ҳ0ssdr.,ѻUMs%6 8N'J$(HJ6 ) ^Ess$Hٕ\L#Ӝ`JFKeQ @vBѦKti9.}m/-EJ*넷ʂ$CiVcXgaP @x۠Xz`$.bpQ#^V"+o|уV@VkVlUɩM]m*˝+fP Wt=,:`l푔S˪ҧVtIsnW`ʠA.LZUp`0X8j}}}WscDt6^Uy'dE#GU,qlxIQ1 + }3q'X%i O=h#Q~AvK ^+7cb1,)H@Brr Yu0ߦ/Z^@vnڇv",>4<*VfwijڵNN&JJxP J%.+/'Z6c͍թfU$x͓EA-1ÊZJ%>}|N(=Pj .˯ JkVs lnǙȪŝE񍚾ү;`6/ `#\7`6ߨEECJ*J$];ޢjkKkcm>K{= dՙCQbbݻ2jK;T@sc T*x;u!ub[~=[eW9$hBrQO `Bmo֨3<YT5<7[#K.J2 +m^{Ͽ_MA*POzu ,vādՙC_z&l^oum ea.flv\ $U լr9| 趿Ep5,q<:rd ű'd5y- U8'Fā$Z|xd7 o^vQs`3g5#l_3MQl(Z6Z[~C71@R[C8b gHb͑~~-Dm]Eo^WW6E`󰰓skTYD ) ((vqG{$8<SPLssBmp3|=-*P l]aq+?:zhی57cWɲ:~V;.QT$Jk?tIY-*oH>]1`5h{+?#yЃ#M=HV \$N"4'NYT)VD>3/6h.թf os U G"f G%m] Zn,āYuO.͍8q*cbd(}V>}=y;^r#mLŠb룇/}vgr#̢D`W c1Ȼ*P6^,=m_MA*uA9:s@jj308c{~Jsdq;Ȩݹ;b?xFaP _n6oݠGwXx(C`s=5pYu\CAP\ݽ|byiw J5}`"7op"CV?F@ 5uYu]#Jsc3+Ƕ>(w.O*JB.ܱaa%b]f 뒫${U}߇:jϿ$!Xw9r,UgD ~oxcZěVuX VP1Hr|f{`vX;HoLs PVe(}.]^% pŵ/<QT5<7[#K]>eOXaC`-A*P(Z-C 0p3nsrmQ"BbkP2w3-u3DfQ"+]Rd TVGcCh T,d[*CQHɡ fKP8,Z(&Q -ge=y[0&Q NZg\IukZ/fukvK ӪʑmŮge"T փSrUg| #zghJ|on.˺c$8 0WÆ̕`zi`}ɭhVYœs 3ң&ׂEp5~ Z ,88xC}VNyTI>1<88sy2Uj3 oSRF\g|}ݎ6͍IX( NI:fUҧʞs\K`upW9G]1r5D}Xר,>1P O9߼ʡ,K\Ya5+"ZUةR]*C_znCaS{X*'f\es~-XDO>X,ªX 9q豽_|f0._2`ˣ#NP8ƢGStpձE$1g*RT=:mC%nD'p7EAI__PY[Kԗ& VT 'oU>; XU~ёc'K(lf8ar4`]㤫-ra8D9l2ΛNz +bJ>~-݄ Hz# ^DǑUCU)לJ8AvTCD8[}+7G6΋&+%r$yJ!z`66UߴL\QCR=s\aV޺\= n<("ee$ S8/*oG]@AQw9aNCU w8ǯ8Mq4'q`on#C*jj+:ܼi.aQ''HVQ IIf@$7E$س g$6tӜP]:9f/`]٠ Iн]~yŏ?ڣ Eu:m P7GJ7F_SO q3js {gP @dB/t]7lnTj넷dC]QX3 mP򋻸0 GAk"IUsuרfs#sY7ŋe9đn>f/i9ʑ6&aE K z0 .A(ʍ0]1. $# .㍴9z?nc"T .7(UgH CmP<Yi*}Xb;w96W D]9^/7;jRݠGwXx(C`s=5pYu\CAP zA-Is|E%mAM-Égp"1CV͍8Yu]#*V+H7gY('F2 |6KWg|%!SDⲦʏ0]1.bp2^ ;h!61sy-XDS `ȱN$aKO.t'+Ceu\`eEZ H78Uv#g88]rԢ[nsDQT3.b`+(KVج]Khz%XDS Tz>8Ir Yu0E\WzۢDPCvyK*1F)\is0`=A\%9Ьp x.s91`T s87GG,8^UPFɭjxnQFh~N]Ə_֋%x$uk^/h P*X]Zɪ3kѳްܢimP"",KW`9~J 5^*ñxQ|dfPz >|-Uf,ZcoP{b #Qi'=H\@!gٌ3Vq>.g5<񔮬zV h-@scT+xvCV(}leݜUi`O9$^'@qD5D=FwvɆgU Ϗy<)K/*{16/jjϿ_MA*Pн$ˁdՙC_znCau`124 ,'H  r8}/:1@ ˮE4`ˣ#NP荧^ xTx麪g=\JsSS<7T oD+o,ƭ;1 i [ ~ nM}<[Bq$p sQi׃NH CD8SUjw:= ׼\½mD\y#ǃMQnxZjvyKjz$8&  $U>F9Ьp| Q"po,;8YBqg; c+ȠcDb3Ώ*tC"_A*ۆJ*ȪE4UO~ [,8=ԊyTI>#6g}ndh^DEx;Q}-J謂M=92"&g驞j6zx[E5 K; !mSn,_=T=L 06'FD"#OǺ 4XnX0y'(8T9J/0OٮI{5Q1 {:` eE:82J༡1 ݠjxna^K.J2?ʟ>n3-^)WE @ kBrYpkX[m6{yZ#/_NZjX-eh-B`7(>X_xGcVˎO읋"Za;>7c?`=Y&Fā$ƙCD8?fJsSSF8*  Rzk8m.jo;TRiYIIpi2HB_j a^mĨm(€f>o.tSlmZ YPzW8iH># 썁 0g92T rUktn^U4_HV'3_ *:D ) ((xSVĹ9$d <|QLssFm0%NN+Dl'~n X7`͍UUY'5T$G>BW97X 8bT6(`=|ƀE[I_} ga87GJu_r-S+\>r6džҥA%;jRA3X4W Iɚ:;m PJŹ|`aQ @x۠DPw yC1fhk"IU޺lB#q.fUI)f6PQiI,%-G90Jc*Jz >3(2+.e\jsx4,)H@Brr Yu0f`p,V8͑\@$bəcwfC*HM2v9I\@,Sͪ$ X 8Z+<AএLsB3wXqBDDGU`sc?DVmp'FB 3H9˪/G9>16otl/d .]/z .,I(ʍ0A]1. $C f=8~%,T /rqUgD ^O-6+Ceu\e'c('ݜZnVU1 8]2>IK\q O9@U ͼ r`.J2-ukMh P:XN$Ug| #zcjpE6$",K``pv\ $m'p@pL*r 36p҃OI =|/ۅ2tj*F' JĠ_\I}I8Td&qMmrja''~sLo[T4Hu=3hϭ =vK( Tݔ~[klQ {ۢDPs yCU FYڬB#qƳj>(716=nHmގu#>GLWnv(xUW" Z=[0.G?X@WK-,88zC`ǣNz8^sI3q~fJQ'p<+yؑAteά=6s `CqU*}ú9$GLzbCwnz$x5*ne1<# BT5<7asy|%+e.j`$UJu5#$"sn{+#OM524 ,#HB`}Q\́}[4KbP]{"vX9vpGoCrC8*<wPFo\ $ >X_x6L"~> ~7@;E4 G?Xȱ%μxԟpTxľr q_=3x*p*L>;1}Gj~pld Ǒ6Gƒu~#90"z|+l2I'n"mf0Ls4XDsAaE=($Ws6 JEr$|i'gpo:m9 o'*7 3ΪSuȲpxrYٮ:PeTOC y[t^~Pr1&e2)gD|+>3˵lN. $+Xa^ͪ~fRdfP|E`\ ;so,;8YBqq6=+J8'Fg3j ]9өQ;e!'0gf|#;,DUj2~DtsVIupW9>6{IN⊡H~dVÓ{!DUs3iK/LؼB.h T:xUQ Zl@V9"sGF>REtI=V D+cRr/p/l(][e`-0|taGoC !P_XQ$1"zyl2ΙNMhQ"mD\y#( 8TyA:]k0fjZ1n@C/(UcVA@'볣EP5Y8Yqd3CٹG+ra8D>F.dN\ HExPɯ$ph,c#Gaao,):ZΨtl@%!Pg8 %r$yDUYx{9Ye9ނ;WhW0T:RmކOw -T:nwXĵ氎_pXGNKWOjW:#isb1e{#(3Zں 47KoaK6P >%7~`˻5n Dp Pjzװ 5ɘ nXT5<7)b`+(Q>hͺx7R~,T K!#$"sn+/__ln*C"z$R ֆ#AsO7AT#fg\ 43`ˣ#NP }L~-S`|"g=ZJsSS<7T  6AXÊ"pP/5elnV iE/+d( qg1 `| ;ro,+f ő e1n/_ie6x T;(8'B彅TT"$2{`  #0cgHpF `c;+IP8sW w_xcn^DEx{U41Y%Go+nxz#*!aQ5Q\)j?-TڠeJ5L\sdS qXO>36W3nsOmUgF|^_px(J3Ӈ<\nN]2ޡg`=A)PkxUbƁfXU0k^ ESoAy`>pl#mz/v3hN"CD8Q K/f_A*ۆJf0m_h6Hz# ܹGőU1/j˩2*͕D$?F37VEr*QNTx3)-ߪS?_`9]Pl:Ln6[jsVsQMDovmjlP -a1 0tsqa-2Bu{Zfp(Pih5]谎:Q5,z~nbSKM]YQ{{&];qEQ{͌kD (g6Ťsaxl糡f75>>f̖lzSw5[b"1Ȕ26*ΪS'PQ8ߠ]5voG:O9+8rl4XS Z OlFUs3]:eUwX%7fݤnE4 @ \+vādՙC~56۽3R=*`ˣ#,86p捗 "A'F$!ggR|)C `f}S6T"~F^ nϖ`e} jss)XDO`ov; `ћ&}>}p1X_Î`#YBqd3C'ؑ~`]c+,C~{7qN:Mz k"mD<,ˮE4UO~&ͳ̛%M+7%{Jbp6p҃$?3{pY-͋o'*mroAU籶[ϑe4q=S5UNO4T炷6hڻafkaS aM?rDt;}Pr'y[TxbX*v!^zs 8f.;gKnj:>3ޣ Ka&.W[mٖFk.޶(/U8fjf4X$U65QZV'P~,aG?XF̖c'K(l*85'/Y;-!E"1 q6GByI^_A**$Ojc4-RF]58l BZ<*͕BWƹ犑tw[DT7*7nkqdV9luȲp虸F CS= mt![h'xc]OnkL o-VfTSLSD͆QDs {|-Hm}G=<{ n+L^͋ooT}!ꬂ7of曞ۄHz& \=X&x,I'/sؤ_jwXѷ8ƶVύ06@K 06F5 5'8lU.X,v:sOK{㊑vO8{0vG8}f,]rQZ91*^2ZS6}c#*"1 [o-f㌭L8fpl6{653HlEt,{CKQ_M2v}"rώۄݒW+oe:56p҃.!P=-lbw9qg&pX 2"*ۉJ?65mmtV|2wF UKtvo 馦 ]NM۩0{3ɯ޺\=y~{bwD+6gBFf(bHavja5lsQC5(%vD+) 碿Qw؀p؀9l@?njVlwCFĨ⛚6r/*Cд{46RTIck7װ8m_:kFDZE͖QD MyP}ܼ̗6-g!1$1F ՈS^ٵea/M8؍J1ХWkY9l`wذ+\|QWlWCFQp_< }5l(rn[z v2hB_^|q$|FЋĶn͆QDǴ*P_"%~X"xEL6Ads n,R=7 ]1 )L:R=z6`Z#.06TcːN }o=?S zvˌD}W3Wמ _"{;_nJtU^%k Ai $6̼r$6t }Y#5{@H\%qbb`N͑W_s055ms؄&$矿&ɢ*%IIf@ wEK,ڳ89$3[N஑ӜQKɰNoXTP`y$22Uhs=v,gU os IZT̹Yl_1 *oK%X :,bЬwÝ&OD{~ )9U:ߣcn\>GMn-ׅxҢHɄ/ky$|K+hթf os IB3HY?sgQ @x۠Dpw&yCi.'q`V-%^_Z# K:nV`uuLcG9˪xIQ1 + hP/s\xp1-<\\+s3DfQ"+]Rd$X>e6;1,%HPBrrYp@jj308c{M+P-*Jݹ; İ9a,*͎U`7(/'hOM+jVN\ek(?H6SW=q, J5}`"J?txqBDXt!~:.Oz_j,H&:`>\+ $5P~[$]f 뒫$#z?3ftt%XDS `]ndՙCQt_\1hsc V;:IJnO!ݜZnގn]Ho Nڵ*Ct tyXM+} >ṙ]:tWQ`5\qb.v-`$U~c$9:sk^/+rmQ"Bnc"po,;8YBql7<$6p҃NH ӵ>c26ctЩZ*g*ȍ($8gu= #W=͵P]`68Uiխ9?l׷-*|Pvy$|ŏlnֆjVm6P~$>KԤœ J7'1`"J_׶/ ֆ=d3U['9B/ֈmZe#>[ڡY_Ieu.Ŋʏ0]1.bp[ gr ţ"T .7Gu*p Yu_D #& +(-\▨NPZt y%9)X"ۛIoMh 8kՆZT< nZSdó>LvO|%~h(6/viϿ$UoQ jlGHV9D5 y^{ϰ1%*Cdc(,9~J eOM j@}T񛊹b =U`j<:rd ű'<="6p҃O1$1g sSS+QNT"Ѹ< 47&Aay'#0U'ŽeݜUi`O9$^'@劑~aKWb} u U qld Ǒ 6m' ֕v4A$3Γ9sikpH_+ mC%~`eT= G?X&G<(=xQiJG c;@* `gq~i["Y% o'UESU J}SkAp:=Pmj3t^\ﰦka=EwXO.W3sOtmnBE =63&&렷s=o 6B96WzFdBݿ43k#:p͍e os ɎZTP7߄>'k_8*o%- }[P`4Xq/ID67FLdUQ~bl(m~ۼ c3+Ƕ>~7oW"Xv9ש̣vB*č(3i3m6D5 Qլ'=J>}TϪdvN⊡u?%U_$QT.uWQ`q+}6[ $US/RŕjlGHV9D56۽'#TMws.=e`M0<:rd ű3o5( >]#@!zyP;{8g:5yZ"mD ͋ooT;Xzv{ `$mN"ƾ#O+/X޲:86PǡQz7~H7u;ar?&T8` e(}5 tXs b3H,)H@`P~!9:s/fs9eKTúl7A MXapqR ݄r 3)-;g\~ p-<:rl#sohԟpTh'?o!@b8":jgR|i7&x4oT-Mg 0>Tm`}d?0x;J 5 !98+  R5xe(lF{Zײj$ş)#v~լ3~ERLҚWx]U>p%IFp?5NJ1[cC/6YV.V9>16o|l;3)%'|%!Sfn$DfP"+eV .J2^&Haܘ,)H@Brsx= HV9D5T*lpe{f朗 q(-ܚ>(Gzcwf'rJYq+C ֨-<+ַs[#Kn.%`Xrg٬]sq]G"T ϼWgH$axKa^` Q-J5Th˻c xJLհ6/e":$T^TPS)*ñx103HE?ok$G?Xȱ7K(=1ܱgQi'=8sH CD8g3XT~,y_O¬=6s `CQU*}k?B9$qcGL%-8+>cpK#[Yo OņU ͼyXإ_.J2 W8nef]\`$U~xdhB9:skX[m6{y#[Y _qXcln*C"zhr8s( 13%[ 43`ˣ#NP }Hs6ʣƒ  9ʁ$ƙCD8/6Vqtj*G[՚nBrӢJjvyK 0fjZ1,+Hi+7ʁfTPЉ({`T s87T838aJyTx̮ܹR(CD8cB99YEzT j z=>,zZ~MyQ{*J8AvTq&8+߼F5h)ó ~74sdYe8DL\:ýPlOw -T:nwXĵ氞;n'i͍jiF((Cdus_zƹ-S]or*ĽRa|WWS FMǝtvO.fCjwXQͺx)E4 @Xɪ3fs9š净iMa`=A½ b/}Q`m>hV%}ĉtfRװ#G?XnNkgo,* oJ$=8p9W8f3T-p6ΙN|P^#/sV  H*6A&Z{b4X_Î`#YBqd3 B>;*U N"aϯۮUþ6lìj":vXz:TH`=HQ( A e`-uron?qһ%-u;C_F kX7~ L^1qW[q6Qިf-ó}רެ|rP *\=[,_olK0Ϧm Ϧm6jw؀oT[]cgz<  RZOv0ɑ0!eP5dNPbU{ kz0pKpX_h,_2*d6hڣ++*bU!@O\kSaޢiksX!Qz3 3hVrp}{Pmj^lMhqf Me/Q`}"h OaErd5poG )34T{j)z}a=EuX_Xvo=uL 06o翨 鷖f"2q0T"Fmj=jȽܳ;XYhw8(9|>MM͌vA bg@}pYFx6'FͿrŘcuo6#Oɖky| 6[ͮ.p҃.!P=]-\ >7 7/bV-u{tVowg;ӴACePmfb+ӽS/pnSaEߪ&n6WDuy9nVb[?26F5v@}p1_ا5g!oXz[ȥTkNTø75c7~*@_M[I>w;r$Zfvu Dt\1Wbw+6k󐑰91_ܩ2hZQ*>16upc[p\10m~ }y}"|6{'"[i!#aa1 ԗHɍQRM}"F&hJ״i8a/HI}ŝLٳy]`̎`Y_KV{R1(14Xog1pGl۟eF7c!UGtljKsφox;Zvϣ/teKMR%:*G~U*@cdl"Ȕ$ffT,L_f7VČo!۩ Ÿ>>Ԉ67sW]  PZxSs ;3;%[30`"J?,9/⶟0rt3g"NA#^m;eF9>1IܙǶ7;e# KsqGA#eV .J2;L.~sqG"T /rsXUgD #& +(-\:Ck*Cъ$}?h#mL&4ȵJQ5Gt/-\o υ|!-Bs75t%_IF-Ef]Z%`$U~n!P9:sk\qCmA 1².+:qclh κ­hV}8t w`C-`T s87GG,88xC_YOʕG #qLg=<݌JsSS+QNT"QgfK;?aBT5;}l 9DKZpW }aᖨ'|óH53X,?,ef9f9VKH7V}Hs6ʣBW=-rpo}JsSK̻Mu*ۆJ~}Çj&(7<-J5D<U%v[z8&  $u&7ʁfXUPЉ({v~hёcoPǙΡܙiQ2r߯B90"z|l2I'D{Q6T"?WIS +hi87*n98;xC@^OPG c;@*Q ?c_g}~DJr4/"cR+rUV&xޞx, 뙪y2uzڽ?nz5M\+)ø_W3ծvO<͉QQ#60mP-z6~HmwkתͤTՀ4H~rh历j On&DUs3i M.fۗ>r{` A+P*Vc8<| kfosc]V#eY~XPƁEtI k5L"7IE!+{` G?Xnc'Kso<ϝSՃOI3q~>&wT=u*ۆJ7@4ltݖ^k167M+FߢeV W&HAcV\>sTh8p+Fe#K'-ިќE"1 q\ Ӭ`5߼𶡒__?VIniXDK`0988ƒ*J8AvTg=* ʤ2΋&+%r$yD-;(C5owD41,[p8v*WzZYZJ,-{7W:ezzqXGѷ|aualծvGtmI uF(J3Ӈ<\nN]2ޡvLm  ꍟFsYÛY3b .0,,8yƱh.Q90g?ߊ؅sCoSA*ۆJf0m_h6Hz# =KaGVI6Em9UFPx|%1l{IIr*QNTx!y[ujYV=Hd2uzڽ Gޖ_:>(rþ?Z:{Y5=uh,_=T=H;uZ[]Q( Ar3Yh*vљ|>MmvSӋm6lY7%}}\n)%j˿A$67|oȪ4z__z}Iy0U՝:o,XSakϦ 2j6ܥ_ö.J2 ifݤS h P*,5ɪ3fs9¨wʆgEo7fp" {fWAsћ:rGRpy써yc7߶_uλ|bDHb9W8*vԎsSS06FeNǯEmɺ4IrVeTI@>1jAoՖ'嬂yDz6m *]{jMmC5jTO'GWm} ٴvtٴPPjn6WD5}};e^!#aa1 82!9f8D̷<54T%o V݅)zﰞ_4aG$ El@?lz2TKa1rތ%j-<9š} z=1`4is M7XDWt&}kj 9sL*gk Pqqһwǔyc}{׬}IgTxϩs$FH !gM*ۅsI?B65cQ6T"׷XD$&FőU#J6m:ztvZo揦qb$ tVF5Rz[e}m]/'92":&:Pi:Сֳ/󚞭őӏuM=[jwXO?&nji'i:}W0ʯ2$S[{wKm}lz6r5F G7ܳ/뤴M/TժgbXVlYJ5P * \=X,_ol:)j:q6dv#_Ìj" 6WD|zZ0R/{U.X,v:sOK{㊑oM{t8Êo(ecvzo9M+e_^Ȉۜ_l~ktؐ O K%bP&\b0Y6h:n:-զvϦfF{[A ;TDJf_nwKќ]6XĐ+FoxF;jHJr~ ^MfsF4,rsǰq0y5},3/"#jqYnͪ73: C5z_={j:ݫg;utճj6{SDmQFCPo Nְ䢞75CuR5\5t PP3 (gc{kthFqRq!gxlTDb*6glf(g6Ŭs]p|NTWFC 06F.9}p1_ؗG>HmC,_6ԳlTήu0Q3&ܠli':w6wظAdcl|Q5`.7{eJM_ymlߚ]3hbll|FoOb{֭2{ꋧƨ߆^nՖ_#c` yL%75î񂔼_ÊR=Gl^M[=3{jlR瞓+>XU1>3^a؞C\a9a u:bG=쾪L|^js^oPIN _DvrMMvVh~l柖6Q =>AlHCW9ƤV ]&>Mw]^ ?q$T2UGvgGd$΂Ht9#qB/1~}1\Tu%Uof73K_%UJWI*u#aQ=рma0G3mNgS['uI:篆eĕ4?hʿCֈ?dJz?rVϯim﹡Kh2]_b&2UGUJ]IGբV~rUo6WfCDwq [7{n识a5jĵa =7/.xno=?8Òӊ-Ywkqo=쯆a5o=df࿢zw /9f /UϿW=gr_+bp]]˽}mяPI`x_naMӊ6“i]Y{A?bW=s /1~}ww*ɿ#*_%$Y#Q?&ln͆fC!͉78o6_ Wjoհk{_{^zؿro[faiſ5s؏8oWU簿_2q[[zea3 _Q=wH; oϜ3@_39zE1;Ѫa;?+= \{=LxߒU=WU簿˷׿^_>_3T1r23 /oW=_ǫ_1{ɴ?o=?ߺ2Iܟf\&=Y_>_3T1r|JSUϿW=W_1[_XߺuϿY߅_r߬<߬ߞPIWawc=WUtvs{=gZb:_^*uQ$^j=Ts&.['%j8^*Jk(`lauv߾v&"s型}VsU8*y {.EXԤKEo8GLYE)%}wsDm96n {ܗ\jxTnݱ z1ocSK]b9j,[ G%Ql?5epgo՞|K@= ڙ&jx86W-)0!6my uddke WUܩC;61&ۦƚ"?u&MGعo" r6{UMbsХww$bV{^4q1!uIvnlM;7,"JbsTx86_׼yUm\~Cz֪X#?%]Ê=TP= ৊Cp 1˱ݤvvAx>MnPqdGf2SóncݿWྪz#vv&"FĶi橥j1QU#̽?^I{}+wNlM;7,"Jbs'SeH_Qܼi."PgؽuQ/NlQM>R57o{ Q*cyTN}N&yڝ*6wsӛ3&1"fy=32Λɞߢ7GJ7 WUm7qڦ$NNv}+@ꌒ_HJZ;!DԳ]l cWrsqVbXCsX9v3ggJJxP´"˺Â1K TUN a!HN (+vLRG(HJ&Kodz:VCU`KXJWW+CQh!sis%1J{pݧSGQQY8)\@QIV=v vV+=L:TQc5I߹'s"ŠFU0gGT#8unEN/"\\OƹpjpϳF@yr6\C[P $gND =cE`yV{YwbDsڦ@t`H?U/cjJCok1-Um X'9P!V^^>i5~[Qz"&< [ !VўE 6G{-! ֻ[9yUm\v ;9opJ $% vlv){۶ZzŖA7On._AJjokۄ[eA%@-< YE0(ǥw0Nc3hdUNKek,H26p^/6pe#CMӮm2gK\Y7S^D # RVe^SO=]zk(ws_;iU|0goq#g4!UE)qIqV "zzF$1PZEFV\d W6E Ytr_&ɠɪ3J̀Hlo#{Q܄͑ mJEڥ7`JF/ӳRF% u[Nzzkkps=v,fU os u y6oW J%뀁|.t |[ǀEtz8εvnclv"ʃ[Y7ת-,u] 3jd\x:eTG}A>0oa`s}:լ:m I(}=1<R JDpq/|PZ;?L{j&c\f# KeUY'dDeoλF,iL7 ,g0G>ys/t=xRV,UóV < uU`}^YƻݏEpA)EUE~ s5,]tJa2Tpf~=[ϕ{񡣭>"tv~|&͕]ڿ%ԗ$KSEྚ]xDKZac{>Da =橥U-Chȱ;{_Aӯ8G;aVQ{ؖl.bU$F>'f(m$g!I0gaA]FQbDsڦ@t`H?g%5ۑ:QVEÀ J7α*瞃.Y=c*څm"nqj۫Ua\{qҁm 67)fU os u #꩏<]0\.GvەýmQ"[\+ETg5G=^ 9 }Y{OͮSQ`JVZr<+%T Yse!XE 6GJxVYX-W6E`sϰq8@$j3ۛYx97G~)I 9veƋQoUyVfTfĵ8YY-wF{*YTϪc'+R$* 6Tc?]3 *oH>]1`~Ř̲4S,")7w*kVYXjT;ZX*7'Ԯ,,Ԯur7Š` `+}Sͪ$ TwJ`9>YYCrP {ۢD2焚)Z޹,"đY5qS9r͍8q*$cbd(}c+ 6peI7)z} v'oa)ZLsCQ~AvK/*k6+?? Qͪ"yn%q&TIPaBJWq 0o[<u$lvg#MޚZhsGx+Ǝa{gHb͑LJ&FN7UMsHVWɠɪ3J̀Hl'E:z5l>2yij)ynΨ dɉ1w7J"7FX u+ݖ{KkGƗ9{mnǮeuܬ;mARﵰ yu2*po[:` ]b~}=ƀE G=~you,6ꥻF/NNmlj{a|.wr7g2*P " yehs=5uY:VMk(?H6SϔIG/oJ%[0 +OX~a88CemnǹUY'dEeqD=8YVKZr?& Hlg( 3q׃W`%i O=U\+3BfPo=PbuU`m. 9X:<,)H@B.7 UgH +`Uk69mۉ۳`ZQnU\xUՎu.vr7{tJZx(=U8c ˪*ˉf3hdN5Nxk(?H6~yV: mP+>D>'(nP{U`sc?DVi'st/IPOA!vtVƢIDM⊾ӱ=#istyXoYDⲦ!ʍ0A]1. $#>vzD\Y "M8:s jvq^P?D pX VP1Hr[]{`GʮrI؄'@ZI9Z3Zqs$yح٥S L|%ևʊpuk,XDS Tz=8vādՙC_znP+ea%Kd:WÆ̕z`}~rzIjV 춿Ep5~:8Yql7{Tl 6qS~$1Jf\[*/.z4ٴQ}C"(6*kuƪ-SUMsN\ 5%x*_QMcH("a%聺eys%\=aKe¶ܽ5Pq{Vދ-qlRG?vo@>ĊE`Yoaϑ" -4xzDS2plZEJ=s=keĶ`s $q؞q4 [D#CEVm^Erڦlvr>{tkeA%UgBRPMQl,ܜ#AJՎv ++4^lWr8DloU8q-D0E]+5n3}gU os I_Y*1,ͮ$J%,g0n’^1`Y_ KmrC;oYRuXUyRR9vN廱#˜ux>UU FE7(mYugo" 5 m>T*o9=3=]:$bV{=Κs9n.yMd"p#CO>VuQz:n^U4_HV*,qVP d%IIf@$7E=EaѸ9$yWm0%:9O{VP @vB}v_Σ9mJ*넷ʂ'e>sO1:nV`uuLđn>f3^r#1f b;C=K(xVUŵ2?<#a%b]fetXxCm/1h P,܀HV9"5 F=Nsdq'H . : İ3gE%qP <afxNTj넷*IBiwN_1hJ%>0 .>e""œkY67FLd wqP~b$>WI7gYU\a,4Ar6+Ƕ>(./҉cSDeM?<#a%b]f 뒫$#zs^Uf=-x^ _MA*w9rkɪ3jO<^1N"؂:ÎLŵ51HrR9lF`8Uv#mL&48jת OM`=ssW\|EUs35"t钯$PYxJ W@vādՙC_'=nW\Qmz Mc]R1 .*C"MhVIv_`f(_1`T s87GG,8J8'Fā$q~}/ۅk>TN3F BEipٶQd"2Tnz;oyWnjWַOj:9#JZxP?r\VVyնmƚ탔 թfU$Z&Q~l(-J%yt+; mP"9ILsBaSU˯K򠮗U`sc?DVmg'^_WP0Uʚkҫtj}c eW]78:s j69TPAmʐ9 UV0+A[ȯBq {`veu7)zAMh,8kՆgW_sׁlW4աjxnQFh~޺+(^,+G Y^˯ W\vādՙC_Èe9B6(P]fv\N,׎+C"z$R Gx_Z/ʁfՇCs}q$5G,ag~ёc'K(=1't_#*}6p҃Oı Hb9Dz3*?ΙNM7) D7dϤ͔q#6NI:fU2qG9$^'@qDͺ-?KW'X Ϲ@-<˨Zxa08K,/3>f]Ԝ+"T bJu5#$"sn|/_M524 |!H{ ka\,rq*{r 3]e"Za;^Wv86pGo3! ucHb9D0Uj3 ѼiS6TnDgp7EA4v[zXfp7!ټ 6[T" !uIZV8VTtb>; -݄2G?Xȱ7K(lfqr4`]㤫-ra8D9l2I'=hz+"mDPkC* E`Io8J8r V z+ rdǨqyGgpKVIB*ۉjgn%1.[{VI'6UV㛖CEUAPFoP0wsZ&5E?vW2& `:2ːW9ŠT~õ^}r)bQ%z=*l!4yPK86@lns$N^*Ǫ, иOGMDYkBm״XRDԨV[eNzkfsmp+ooWm6BƹԚi5g}x-;l.bR$F/g%Z;I6Φz%F?TR;T[^'ͫj"uvpЉ˿kaGԢ%5ۑ:QVE Jǟ.Ĩ-, q%{1ڪi"#\Aǹ&czFy`Ϗ>,Zl ժ"NUpؼm.VovF%UJ~!)YoVW:Mmۉ(g]l$Y{rsyՎ +NԮurZY%pP P jUL+q;L\+Kn3}P?Nxk,H26?[ woH>]F,:9"9"2TPfr?=]FąZ YZ+%)?U8WIl8oYnsdZYdaֻ5Zd)7m/purgɠɪ3J̀HloW緈Y͑ ͹r8h`J3j)ur7/Dl'F;6}[zmF{*YQ˪S'5!H/[]1}@J%T r6,};}}嵓Z;!D7,MJs/N͕ԬvҜQ;PϕNNwS :**YBM*\˪LXs}=%cuYu\CAP*T[cx~}5$ JD^PO`IUGti_pYuR,12T;`kY9*`$I-xJ$]<7橎#̠DW ˬ,/3]]y:`MA2!Xw9YUYa5cJ RI oyj;lS^4=]xqVV|5C 6;sy34Ϊa Ğ#C*G0AV[Z] N4m.a'9k20,*rP"Dmb{WС=&Ns Hn7.jiNsFm,%NN’kxjS#`֨</>J͍UUY'5T$jQ3fv|0 mP ,g0~`18A[Dlfk-*͑][fU\>GkoaDJ& joA3^lnǖTj넷$^zH7 vCYT6(݀|PZ'.'q`VӭKqrgLjX7N::&F1*;Xؼ/i9ʑ6&aE +z.wgr#̢D`W c1Ȼ*H>uF\~L=ƀE @A]nP H +Uk69mۉ۳`ZQnU<*WV;rsBIZ''~~Ԭ"A)eaDrknR2TU5OɆz ÔZJ%">D>'Ro0W WpҘU#q&j$(?1~YUy7IɤQ2m}$-]^^UO]{`Q~[_mD1Ⱥ*H^ʇ."T /rsx$"{uv qw!u 6oGʮrI؄'@ZI{tyx%huHxFjxnaFdN10]dX+_euͺu,X~5 @)zpҏɪ3x(%*c|ˉ*6f P*X|k$G-|c/03p=%c97GG,8Ę7Gmrl[.p҃NH ӵ8G+8o:yN)a>6CHxKN?9G Ds%8V{=ÜC6a2#\y!8{$*}~"j*#zܼi.E_*ZT YuF@RPMQ,~q#AJ$SܜP]:9Ϧg`Ӡ 5C"GGmfJ t PZѥK">G)4Yuׯh P,dGɪ3jOz}konlPYay'XBQ QJU9e={;r?&;]2>I7KWgحQ_xjxnSewXW\ج]Shz%XDS Tz>8Ir Yu_| #z#&pE6(P]fu%@+ 0WF?x%XDOD !KrYe8Vm9Yco29GG YBqy^+Vb<*m?zyq*?/{P!>Sxe1*.ϯ&qDgA*7w5ׇʻjGE*7gɊAe-2GMmtQ/=tַqYsثmhs}:;m Q ")_ue J~s2 Wկֆg3Ums'Ɔ_TK`>*k 9}c[1}E˵ׁPW *>g#̠DpW ˬd]rd X*mcC_h P,9[ɪ3jCdKP8,Z(&Q -We=y[0e&qNZP+zB𤂻5j hDUs35%_IiWH6b׫`MA*Ph֏ɪ3=5Z4%Q-J5Th˻c(8 0UL`zi`}ŭhV]"q S $ԬGMǑj~/xrl*zc'tvF #q̷$!cqFWquj)JT/aduy; 47&aТ;&q(UJ*+縖6oGr?&E` Pb( jp +C}Y|bsyC/0Y L|%+>f]Ԝ+"T bJu5#$"sn|sČlԯ pCsZX5D>'=w1@ t n/h~ёc'K( yC@8*; XU~ёc'K(lf>L=Wb']slO !csu.`wVbJ_~ p7!W+q+Ρ*GɔQi'=9JphgK3yY_-c%̋ooT{rXw0oYUMա5T-ӡڽ?DZ/an0ۮP6+̓(Zj]Ay@ždYY~DZ&=s aNCN9~i!8{~ tC*}|êKtwyUm\XâNNHVQ IIf@$7E$س %SW|iN.QGLXW6 b{CrItv9.]i%~|2GG{pUy'51Hr6'!,tZT޶(Xu@>].xǀE Ӥ+x> ڳidժkFSBT>;9񛓂x%΢HɄғWp<5@{@e:լ:m P ")=^uF,2J~Qw &9(\,YZOl͍8IbY#C}nβ*^r#mLO0%ǃ`\Lk O=v@Q~uK 3+7S<ƀE4 @]n@$" 6xڳ0ӊssda\@db?%4E%qPQ\˪CiřU['5$ "/sR/lQ {L`8-|x׀E7(pi_pV͍8Y鄹(/1> \|*6$'.0M)9<+ K"%ʏ0\1.bpX Tct} {`MA*Ep#c*p Yu_D ~}*C47@eS'PQ Q ;usj[eW93a 8 Phx嵽1I$v"CӳxPǖoIyz.9vα;9z!= :(8.su7FBJVPxV6P~$c_D>S~| YQ @x۠D$1 UtπE.*g]U` ȪR̖mPy<Y  lW ui @7ZxF(? JẘA%WI'9̗}0{`MA*Ep#Q@!aPae+C%ꠓ2TDՃDPqކF9J@U 5s?UpFmYDUs35vY)^,# .YnE4 @Yb;@!|O7΢izۢDPCvyK*1_i6{qG":$T^TPS)*ñnf u>R=[0.G?X@WK-,88zC8GıJ8A'q @b8"z>qFWq>. o^vA܈=?t<1~6͍IX,FX 6CQǬPT96ؼʑ6&a8jW .Q_>ẃ|bET5<7y٥S L|%+>f]ԼNjWWS T^+؎8:skX[m6{y#,^ 3.  pC=E"oE1.e"a,cytc pTxzq q3jUj3 ѼV"mD\y#! O+ q.+zwLM+FEt 58<|@pL*p}v=9`ˣ#NPǙơS?㤹n죸K$FH !gx2IT"mD,?^Iޏ4h,c#GaaGoQiJ:Nzбr0g}n+If^DEx{UYy76ߴL\7i x WOC Ty wsZ&5}a=uh,_=T=зyPVߐ2{ۇv"-ɞE+ͽʛUG 헳ڵN6e%|8. q? :լ:m APZxCl"0J%~/YPQ2?QlͪnU`sc?DVmX  ?E0UUIc0O}KJ ϧ.}=5}FEhbwIUEEp i,/X~5 @_&㸭$"FMTPv[2# gQ GQt$BDMh,8kՆGƟ5^ֳϟ YT5<7[#Kno]dXW6b׳`MA*P)8Ug| #z^ #mzsK.1FuQ6seh0XDWp G{rk(UcRA;1 J~%_jؙ,cytcO71sH;Qi'=Hˣ$ƙCD8/=]8g:5@|OZjXђ`=ABnd`M=E"7UE˽|f0D.}d-86p ƓQz,zZ~oxUrdqv>kPxrI0pKpX_hlo {0٬="o\JTljkQভŒpc@4X >mZˁ ^fY%g`C50%?'◛xEvYNɢ%V+(>2?K=Oo=W!}kh wʢv&KjV866k(6ɪNR]P^cϢv&kpm1aZ`yj)Z jTlzDW#ټ֖u/Ia[8E8۝ܳxV~MXsHbm5uUWF&qD}ҳ 1ݡʚCkeU[0F+UMs:;8~P?ejJCoDk!UVV>=G]QY˵ܳ>Vl 61a8WVrl8ouYnsd ݵrލSUMs*,ﬕmHVQ",yen:udmډg]l$Y5d9v'k͉keA%@5<}Ԭ"VwVlf>JH?Dϸ,H26R Nr JDTz`$.bp Xgu[sDsEdB ʪKQ韞]*UvVo1EEcRps $qVZghUʉ-= #nsdz`ﺕQܼi.{ƝdP d%IIf@$7E+ܳr5G䡿*!4SܜQLɨ-͜Q @vB}ICHd^{'WJ*넷ʂ$Ci x;bT6(3gK:,};_qsbNDqz~Js/N͵jծU3jd\gS :**Y@}X|eaeCfѾ:լ:m IE >j_8*oŽ&yCiw.Oجo{_u8c?FOYwRcb([E0%IXLŠb}^ T`+[V#CTںr3+lUͫj"Klɉz|8HVQ IIf@$7E سpzn+SW\Lsjia'g-^˲ b{C*[!½ޖ⥵b`bZY?Nxk,H2kaE0 |͎ mP"{#v .iee1j[~ٚzp'] N_:mnԲjrdG-*|9JQ @x۠uq`<71moA`$̪s3?@͍8q*x,\C#G,qlxIQ1 + }݃g.`X%i K"<#a%b]f 뒫$\is1xh P,܀HV9D 5oumrT۶},, ܹ3jd]y-7=+w*jY@M*r\VVYN-wF %CuYu\CAPؽhysXAe0 hnP`U`sc?DVmw'ƆJB6]E @?huWy$vlݨuaUHB1`,ddUC~5 P{(6fvp+CuuJ$C.p{ Gr&yhݫr7uy#P=DSͫb`+,^ИqnNv,+H@ENrl{HV]9D5eh6(\WMf5%D{ӵ3mnlf`#A)8Kr4{, {22*w`\ X'8XBqDQFo7bP |bxHb~:uyJ)n>{6#5 OpI9tӫ~ik yF-Fϗ?2Раf~nl1-#5`"(^tբ'_\/hur8mN@VP"Dmb;*xyeaĖ_ #AϽjc)YQKI {ZTQ l]a:4+Oˍ]N9Fx[,HR[K"G,|Em+r`1HkXĤQ#i bee؄Cr$tupwNmi< 93m,*Mτ-hdKfVF׶IT9H"Jowk`$ş˲ mP"Pzdq`VşZ`rcuuY7 QTj} H7gYOi#5 ܲY4o<8Fx/bYG46wx1ț*TV>poͲ^9`=A*uA9*p@jj39s[ + #]9VpP@,\ΨbҼxw&|{UMӉI/7SÊ3b#-T9H"jWUOogQ@x۠5|`"J]\i""IV#~&*4=KrbDgI7gY+('F4+Ƕn[z89:%/r6ILN|U}z꿸~'XDW `&$"7߷ZJU_[IӺ:E`eEA͍S6k`ޣrQ9$l@S@^t' N]) pu,<}(<шlP L|%My+8:w'M TGyIȪ-H78㊢޶(TWM7I%Tp`ͱ*CV{L(u%9Ь2 챿Ƞ~s"VgK ,8L/ߍT}>pЃO I?Fu~*u_GSW4:TTȃhӁ}+_ND zZᔡEog&wN"/'no_n5r7v4ҽz\tp_)jWfVFx[Ju yn+P@x۠D8c:LޫE8sCYUW.gx1g"b#D#tqj gUcNlwj%+Ƕn1}{ӵPշ7<: n-*GA`Y1Ț*L^ИennL P,Mq$"FMvVP-\򦨋lD1jɦK[c=[cvbwL4Ƚ*tgniSӛ%QqF`ź}6WI`ݭ5$nNvm$mh8rk9[mvQEo[G4CpxMR1Lβ*C"zLwzQ4 Ǫz/02(=jr# Aհ8X(8LCwkшk|b$ƕCH?u~t*e2JĎ;l͸Gj3p1 +DX6CQiV(-s\k`SP$o v;Fe XxVw 4gP1MI6)6O $MUgq Yu_| k~owwUwS7𫫆ؼ{SE @zr΁q%1 vi9G)GGK(μVS ƒt;f*#\M?cϩN@f=6mC%=\9Dt:c륟u+.d$5ϣ"[A9l *kzU]Juz}~c[7a.~Pc:[W͓*e,C& q< 6K ܡIX:Zm}􇿎qSE*X%~>5qh"(^tn ?jl]\VF.-**D ) (vTK- #A $Sz968E6r7#EI=9L_>F{.YWUi#sIOBYwwZT6(`齂|.7 skҨX 4c<mY]*]=`w RP}t?gQ@dD*|f,=8WWU$7p&tGqП mP"Pz·Ⱜ£;GW~/7F@PUFj1[J#G;usUVI9$~lG7dZ_x~v6 *EX1&bpi$sz67ffY?9`$.X~1ɪ+6è=0S.~L.G@ԆK3js%z^}28ݜ?z@iDatbZˍ̰̪os UZc^n~vNP@x۠D0LsDiƦ PsQY^5PUʉQ }dݜe{CI9>1I\7`>-U_I&P3(l2+Y\%~4ڙC'jYf2pЃO$1"Fy[M?cϙN]l^v$D0Ko \nL `]b`8fU/^E9$nr&[ h1r58E}Xk *jxd&J2 w8pf}ͯ0O $-؏%I "r{Swu <1c;"F\(uc |- 9p|Nct N3`ˣK(μ*^|bxHb\9H?6UX.sSWL{m*ۆJĝTWEӢDP#@ɴ&)zWL +fE@_oͪ/)AB Aհ8X(8#LŽ{ ;N po ~:=؟o@J58?\MsE tVrdqzC{pЃm$LgQg DJQގT: ɭܕU G*gUC@Toy;SM7Ϳ; \a6Pճ@uS~3'/5*odLs@as`~ͪjB1ˍ|?Yi?ѱrb~go\ův@ٗ+%|%!SqqYT9 #ox`dMrd&Xwd۬z/ $.X~qU8r j6UXApe9 g\[A[QU0MWLڈ h 8{U@P`=|q焧3Q̢&u;6WY`Y'۬]7u/X~u @/ 88rkXsYT%*4&Wp:𢂪asll qhUÕE9Ьrqs|j V= zuu,aqΖZ,8H۸!.J8A'Fx=*@b8DaUr?g:y5*ہJ JLLy/#gQͬlk*$IԳqM+7E=Α,Ѽqd&J2 Wnf}f\`]A*kuUW| k~owwUwSwï6fsWnZT =ku*ϑcS,|) 53r#So&͕[ WgGq۱U$1"Fx~*u3 `jޱo*C; w~p .%M]7IMQOCp 3l~s "U UP@p*X\P&8G)GGK(tdprUtWx̾#ڎra8Ds1>9Iro*VIz,>,zZq S<(3owbP>pЃm$gx}u+\I+IfGEx;PG ]YPzsdYe8D \ T]3R= mpk7;grH/:[)k~Ju=R#is`A!2ڌmrcVz- pE].j#mLƞ0P2~c5~74'< h6%,$_I&u6_H,+H@`W/$UW| knocM6#̟ 6wY`CA=(GcUcl:g3`ˣK(Nm?n`=!b}u~t)yWD7pEA3tlwXzX.gsϰb#EJ +"X_CE(cS]?Tװc_h1%G62:@1^:3Db#lӯۅsI?k R6TU؜. U= 8X&W6s^iIzwWA>= oMVpI$$y@u*3j_'wm=1Ȳp|Lݮ:HWeTOCm\o ^еvsú)վ}g6RJu=R6w "2簾췙j2Xj;j7nhl*HHZ]wl)^$G+~ZasР‹~AjXc,ϖK(tvr* `+C 24$m?H8r<-xyt s9|.pgKu3yY/t4QގTيF .ަ⛞DŽլ꩞A#%[fasluh3pQMh.]=06O(`$l6"/9aE=դ{6R\'36'PSu-ƎְLiKzHuOxns/Q!@UT@bD pc|t:6 Q}{@[f5I3{fhF߁ۍ%>Ko}re31Z-6{j#u0YBN;e_ cP@n"jS=4z0w:1-CF{nXe¨rD~?b3`o0Em  ڴ|cxwBD5*rcDVF*'F@4[9*wWI9>1Iؙ,%0ۻ56uy X'-;DaT1,#-li1ț*T3YYϖ,'HP\n7)gpYUA0@|n fP";dV &˩E\5f߁Et nRZ@!FMvVP-\⦨F7QQ&IEor&Aܫ"Jz*Gz]T;<)l\c|%֛%v$u N$M[Nt-ʁdՕC_ÈM JU`YeMr1,t$MwE@[x\/ʁf՗N5A@۩߁Ep51T`ϒ$UW| kno&O_Sm*C"{(uc |- 9r*gz #{7Et 8X(8#Sov =]W@!b߭u~t*yWq袇U OA&ӮʛtF#Մ6SÊW*AB +2~I4 Ǧ*,aqoz#%G62:@{ ;N pY ~:z'"m?$<9`=rotVrFqf^Kzc*I u~^t+.$7ϡb=wvV- J}Sky=]e9DiA۹joNk \[( t,P-ڍh`!ef`65Qp ?n\Lm죅2|9oL*d@A tx'_sH$F{nd-6P (-+$^C6d N$Ml'HV]9D5 oypЃO$1"Fy]M?cϙN]l^v$DɖI9o#¨Ajf`[{H7gܱ*gU0 <{֠榨|U vxynyrMdXp:r"T #Lu3=$"rfgu ?f;"F[HoV|s8zbu MYqQq%GgUB`=(F9(pȯ~~lJ]L8T vUo~U OA&Ӯʚ䦨n5a԰b6XHx 4mF9ЬropЃm$LgQg}cDJQTᆭkeFV?/Oo.uozb<ɑf3p-PvU@T皷Ynns:Ӣc@ѷ|EY:(dz(/C}C[ꨢ%R#}v7Iz* c3Vo juKEr± 1Aհ8XF- P8,pk;x+vCV߻K:wLrdYe8D \=uJ֫Tf!B";L?%nm/㰑UMHݮW+iHLi7} .CCAw`=ux'8X&/G$=u*a. RgrQ]51%o7Uj@g@Q&b\Zf;t~Q{gTa9W<+CfpF^nUy#-T9HJ_9ȕ2o_- J72`"#Jk:^5,$" \C:$r̪H-f6PĈ(F9*|('F4+Ƕ3s])9r OcAT!R⎁7iI2d߁EA+ECīfUCQKdzW*8k+-T;Hv57No_jrޫCUfxSQ~4יj$1{p q13Vt*G `Ѽ*@%x%[&mqUGT\0_I#*;YҀcԕ)u2>72x܃ 3`|zT4糽vC]O:Bd10Mdvf~VR XDW 4UE-Кq YuװlvwWuGXuZv~VWPO"FS ᣓU"P j@wXD8po,R,8"8㯧 }n|bxHb\9H?*R9өGDEgkU)z~~"FS E'%D KEw ~n`ˣ#ZBqd#o<hlwuf~qlH !bou~:=@JUW0j1-Մa]374:GCŠ+}.PI*/}.$5ϥ½}R*zJz=خnxHrz*WgzZsoVnsX]6K=M=_vzN ` Yue!aU#U٢DP#}v7Iz/*H،Û 4)^nqYe86ԜA!_P"VV,8qXx04:j8A$1ϫë\~:z+c"m?$+5 ]=mFrю0#$~%EBTאؾr$X▜*9TաbJ߯m;&92Q= I O2uF6{+-J5jaj6RUcy:))?#aa] YW"zdȃngsjգZV>pЃa.gxeϋPjK{;P-=vSV-FMcBjVWW|e^%[fapluaEߪ&Fv߭ R۬q )ehl-?#aCDx@՛:#cOKm׉\o Vݍ(E ~XeTynhFY<0  #mn'[Wf5T"&+lU$bL> EjDޣj4%Nݬ :|EA$6ТWEBVK#HQ61(5]:΍mc vҗ jhxB |8=1\*IjGEx;Pk ʔھַMo]H, 땪2uF:>?W-B/Za]הj;F:k᧠aQ9z^NG ` Yue>g]#}k)bXN]F6,L uXY0Hre(p7V$V Ey4J]9XfmяP8L9 }Q 7 ́'1ϿT]@?:* JxK%{Π7[N?cը$\4#H+ɡ]IwPhDL;FI\y-9yT#ժbJ/ֹׅc#*!b`ꪃtUH4TG? DRz*L/fT1RJu~uXDOݶ`ۛ9Nn[,WA@>O;FՖ72<*ۑjS3[1=wjͪ蛞DŽդ2)|ƶZfaZf+PPj"FvyB4#aa_SPxf٬6ɑ0!g]M\:=pTCh z0pIpXk,]r?.Q6UVo3M viuUEYcbk*Qu-0].ڻS TȢ_rQMDOvūgmfWD6g\Ff(bJarja5,gz@5)%۔Էv@M(aB. Ejh 궾;26F޵{Wuk!73֣"PU9c7pB12ζYV9P_oBn#Sy.#aa_?s ԗHL %~9$ )1f2ĝ][wvpojv ?bpѵ|zk[6286QPq/**pf}e$lŃ;5᜺]#mh5(amGˋH|FOEb[\Ff(b /P1vSe8ǜK*k3JTGt-![ u6 N &fI3ÜIuz^'t-bе2aѱ)asUE֗T6w2푭RR2j?PT(%-m*Zvͪ>\m-C I=Mջ\yjq4mnfF[-Z_ mF:M9g˵ tpKDLnYHx~̕ÕY ZTk-D=qq:-ouJsL qR"HbQ$m]W$亏ղ0qy.UTVcJ}\ v)`K_4³LmOvVÀ _Ԙiiҙj5EQ/P8 TR2QCn-@x9U >76/^5r_7ɠɪ+J=g][/b\'ᾎ?ym\^rusn^Ψv}!~Pr4E@36ˍX?)fUވ3X'Qibd(݊X^nLiMr4P"_q`)9xo2mFx>}C8ڨbYA6IL*{67;]A|A `Gvr!c_* nLiŶ9:?9o^4{U-vgcN^yz\ƛ'ύi<2QCl,^/GJ=GjywqySms5r? O' $(HJ6 miYr$HtLcSz96^#LSmM:},Sk݀ˍ]N9Fx[,HRnz_ 1U7oAKsI5,b֨AvmE8KNˑUgnwNm0 a|Aϙ|skhdKfVi#/tJ D$D>3~{}ӶpT޶(݂|C>`˯YGWc23x1:Ŭ(*M } H7gYOi#59'b;GiP{{n*JiDxzrG469`dMrd&X/,Ke$.X~qɪ+Ub^y5&},\m1eΰ~yuSū1ة7XQQ'ߴjP 2y@eaD{eh襤N3b#-T9H"JkfJoc %cLs@mZi{"άjl+ex1g"B#޳$*'FDc gUcWY$"H&qE߀-i/o>۩CṄ)f1<]T1,Ju@LA$UI&2>6EZ,'HP\vq< "Kc'vpoU谼 4Hr[VH7+#{T.*Gzۘ h hݫ2[ h,<}$<шlP L|%MyKcu:ٵ4&MXDW 4zm,ǶǁdՕC_Z&l6mudaY\b7s5l\ p$cB'phfUcS=c\;89GK83 |46oĶ=8(cZ?cAX6FEz;Pi9ϞH xR{1&)u59ŜC^.BeFԶ jӲ0"Q*?skE޼_R^.jn:YTYU@@RPSQ掶,bgdd]pkN33jc)6r~[^yO "JB\yzOdөu^m/7cwɺ:mn P 097،;Am_`齂|.7 :EL=V"[6{Zp¡p9U:s\>3JsfϽYT)Pz"ꝁX˦-hMifUl*I@kfm Gm Һ}^pf;1I;u0ۺQ N]>TOy/rG;&b5U`߁OYu|/ $.X~qU8r jn\Z/7`eNay#XBQid)50mQmc6)uP N]37XxQT3Tadb`+$n)6O $mޤ38rkX_}6w{wG?{X5N̸24 ,b(Hg"ЍŇZX+x> Y) ,gf97G) PzC+A ^s"zoDlc- #]ɪUV^G\>ovgT)Q " shM5ifUl*I@mzfX7a *onD>Ԯu ,āY.^s]bV`u&F } H7gYNi#5 ;b;CwL u6*FEdi1H|LpoͲ~<9`=A*EPȪ#RPaԞ۲lXQ_D%' jn#W3ßo7'P={0lqڌIb]fVFx[J~ {p %>Z0hn PsQY^nȪwsPUr|()'F4+[{H򇃯$Dxȣ:YOU01&b5U`aG+ϱ`m+H@\n7)vT8r j3|]z*rc V5}DAÕޣrQ9$l@S@^ jpSXxQT3o{0kF{jn̪8EAQJNQn﷐- J7&1`"j*d%IV#~&*6ݟ=T91jG#o-('F0 ܚ[[LtTNpdx4vR0*FE}vR0dZ &J2;nyzw`=A*uI9^?[Ȫj] !:$&IEorIԀƂWE]}zo:ÓuQQFh+,𮢫٬]/p& P&w'n@!aDlCmP"",mNLβ+C"FS -/n@{GP|ddP\հ+8XFgK- Pgb$xn|bQ@!b6VUըHo*ă(f Jk3p$prN1FAJ*G/XY%w#mLž% h12 sTXxX1@Qa0B10MdX6/aRӟ,'HPZGoR$U| k~owwUo-m𫧆 S? Q|- 9rl*gz #EpZ&q ,8"8ng]Wx Hb\9De~t*yMExPɯaxW-zH&bxZj6vU$XzXKp5a`#A)kI4 ǡ*jBc,Z`yrDpFC/v{'Mra8D9nA'=hz'"mDPsS* =i876`L98K(WA>=1=~?V\*IjGEx;P rWV&`ͪ蛮R?:@WYRF Uq vsú øXzΖ `IY2@P~?g[n]W$vrjt,ivRV]._TT[v k\ΨFX 9o/YP J"_3qtX5h3xѾ::mnAP}bSc0'5*odLsDi1w,,ٛUՄbVp|uYi?ѱrbD/T|Vgk&Ar5+Ƕnd_͌*JBN}Μ>*GA`Y1Ț*D^G̷Y^,+H@\vr_$"FMvVP-\C*Cfh^8 [r&9[@U[WCw7ܣQ_x:,<'r`|O֛e}x:ٵzsYEt @ 88r/-s:*zۢDP]6FX$T a_E @R(UPA=b[~;: f1-RLzAFlz#r< #L?3q~NuX692άuef|0j碚Y;{`xE&QϒC7u)`= ͬws$jg4g6Y.IYY'5l P&XTWgq YuװlvwWuG8{XՏ24 ,b$H {){$D>=bbܛ 53`}2=OK(N4܂@Wxvv[@!bx~t*yǣMExPAt n5EӢJj6vU$5E=í&̱V jX7qQ4 Ǫ*,aqQq%G:2zv7Xgf7ȠcDb#|nnمsIIbDo*?$qgETO1YqˑGqFX=wBtl@%){CϺ~DJQގT;=wJ^x, kRuwTfpP鸆[a=זF~a#u;0Gn,_=T#J 06FG d#2&(/@6èr[o0OîIjMI9_W*J` ]PΑAw'vՏ d10MdXKYm]H,+H@`P-HV]9D5>UN2fwV )w}Q`Æ,ρc'lwTGN;;wE,8X(8#3o*z `WxWWFZrQ_#LWaϙN=03qo*AtA`=#@fWc{D 31$Mc ,Q~EaA1ߟW*kX1<*1%G62cƒufv^#a90"Fٜ_ 稓ރݵVoDsݕd9;G` qhA&ӒP~|l{H%)cZ?wB?;$Utc9İ"0ߊsI?q1R6T_W=EtWO$\#H+Ix_w[$D* vWf?;"T3ϡbSJol>ܷ_Zֹc#*e Z ]3R= Utmq A8c|a#E]5WS.#aa]"F22u}i u[쨷TX^=s\ U*te/=.@%A 9`32oED}3زpor$v@kq褩ȕ߀K "%#Jmo4z0fi_ Ī:ͬ6P (qRqn^^8~#AmA-z~#b0,āYG?n/7F`\WUy#BG1X7Y ʑ6&aEb;C=5cn~ÁU=^$C#̠D`w ɬdMrd&X/,K1,T/vr Yu0fsGJ#]h''\18W~O  n7}Ĥ a9o*I@߀ J7|4`"#J78wXBDۉ,˿F`pn̪wsPĈ(>nβUPOhW ]I} S"I(a%cM o$3S2l"zUn7)U!aO>xim ):,o+C(* TyÕQP$|8Z ;Mh4>@ h9u0Y Mxpu:'M P&G-Кq Yu_| #Z=V[&lPeo&˚c"x հ6  X5zI4 ǡ{22*GuqhQ zϖw{@쏑~J]auKP!޾Rߚ[ * q8$ J/\ΨUj{L\Ƞ5zOouMh mnAQJNQn %1PV+"!"IV#~&JyU36PĈEh-_.тrtb&qE߀~9];{GϗNϟbYꎁ6I7ISUA l2I.1fCs5l\ fV -/n@pl*B #:^հ+87<[j9"8XqxCw#^=Z@!b3:WLX6FEz;P?Z^,Jk3p1 +uu7 PTU)j {洒,nrOp v;FU`SwX +恇^Mi$oسuRs' P&X]3H$"r|<1c;"F\(ucFr8ӯ12) ,kf87G) PyC+_)|yܣdnMdxWS;mɮ78y{X~u @pH$"^efUE m2Ip:v03];f   \/ʁf՗C[cDu ,aqΖZ,8H۸!n|bQ@!bׇ6VUQ+e.Al6OD[E5Jw{H7gܱ*gU4iqMonz*xU!><# \T32uq޹ ,gf97G) PzC/رS^z} @!b>]H?g:uԼզ"mDO ?ͮK=\-ԂkXŽ-D{.D,8A ֙{ pgs~.NQ][h@𶡒_?a]`=ror50̬gCt% zбrh(w?~1vpI$$y@]+(3Yb['ƓiVY^iR\iBw3pM8l[>㰁]`쭞SXe$m"208dr4Ɍzu[AͱZ1$?n9SXFVU8 ݄V E߁E051K(l9v03vA$_#lQ 砓~c"mD0ss*ɋ{N讞6HF# \hFǑUȡ%FtW/$ytwDjɩbGEx;P:;ϪSuȲpz ]3R= mq A8̒Vއy_U1RJu=RSM E 24$tXOqhmȂ8dM6쨷]C[|?kj.@% 옡jbJ}'nՖK@Pm"X!'ǹһO1jS{֓ A|]r1`|l_)^=8KI2v$\)^`|꽜6(Pʀ|trj2X1ID_rFLdUlu[rbԦX7gYĈ&qE߀5·$DxI"<=T9 JẘA$WIfrdc;[; P,Mq$"FMvVP-\޻<ȹ^ N~3F9$n@cܫ%x+7h>' hh6Yg|%}wڬ]1Et @| HV]9D5 o9[mvQEo[GPǞM7I%Tv03];f  1!/@\/ʁfXUPFU"VX'8XBqxc cz8ޯ W#.B9өͣV\*;0ef|0jYe 漒5I[cӠ榨7|Ó{E535T `h ;7>fNj>`]A*zh9r/gs;¸OUFl~R21$mk(q|r*%vSpOnZ~6#98X ߏ.|-8"8[EO, A'$FCH??W^SyϭMExPAÏ'ࢊiQ" dUyN:Fp SlfE@oͪ/WQ>TT,p.(w`T +potW8#De>3O\)H !F}]H?VR6T"9JU= 8updqxC7 ?szq i1 [HVIR<*ہC,UzggnB?#*!kzLiNoώ踆[afifЏ~kn,_=T#J 06Fs]F2cr_˜_k6PP*%/TְI iDǞ p Q/}5eVF5xU~r/gE4ω:,ILvW6/"T LJ=Xmɪ+gs;ï6fsW $M(>D>GME{O5bdӎ/읛"zac>` 7t~O+ƒ)zq q13VUSֵMExPɯa}]էvxv&]=~=el/g hN*Q#ǡ6 gX#D 1y*CH?!а ӮPA*ۆJU%LSU?詞c,cƔ#0#3oWhF1(J8AǶT?f/b%T򣓐]+(3j#cl_7Y$GUCD@U:R&kt谞kF8:k᧠aQ9zN ` Yue!a-ܪ*lQ">dzɚ"j4fjg6XHx 4sW#[hV}9 ?Հ_P"V8#}-cv7XgcDb#lQ 稓ފŘro9),ܪQ-HF# \hFǑUȡ%ztWWDL;FIY%yHRz{fRYV#WO2uF:>;}`P:L?8ǞTfx0]5W16FeU]7~nۖc,u#{ComV>pЃa.epocY_mYt4QގT: -ݕUFG*1@5V]:U^_sc{՗`Vn6rluaEߪ&bn26@K 06F5 ְ^f+8l]xY=UoJ =-;F3@x05aᰮ>XTynhFxx_e(hP(UGbߺK[kD 8fձ\0A h4pe,$_If^b |slE]t,T  ΁k8rkX_}6{#}>ow]ehXH`HUNҊ-vD>ъ-v5bT"zac,cyr` 7̊WBE7$FC@?R*usz `fѦ"mDwuU;<&]ο~ 31$=Êة|,Q#Ǫ}~;>,a)8X|LjP r ;3olƕ pg󽛺]H?EowNUA*ۆJ*NS"je>-YϽ.y>pЃm$E`/g!1 VopI$$yH*3jnk{X-{[BrdYe8Dt \T]cim nntX5V㰑oFz^NG ` YueP³殑Xvӵ1[]Fc6w-L uX *Q"΂b'6b/fѵjd9l~aC^[~a9İ"vi%mbLT-9|ް讞6H#lW.5zCo}KN^$ AW\k-9yT~Ljþ"uGV/lB-o1ɑe10puA*SgzjO)mfIa8l_U_#uj,_=T#0}!͆QDwd0WwEm1SvȺ㤣%GgP r+}]\ +j.@̶}#rqi "ۮ:@J#cOKE@xᰁ_t@ѯ8l׍kXFet+6[z.#as`OطM:[KufX O K%bR&m\bP`m ARjHz06Fť%R2wvKծW[Dwb w`qĔ;hx/[R(^}͖_yl9Y ܰB9~=qИa“\mŶ~*e$l6":fhZ]Kdۍ^d?֜}چX~MI]5#<'!چ'0MM$&<%ZsX7a1QRڍͳJ 026F:57%^ǔ2DuD=KUƖJoMb`m*D$p>T詢bw)g$l6"Ů%R20~#o7 ql>_m3cbMyR%yEgp0ސ/EvI.^ճvEgχy]5 - N ƫ}ڿߐswLyDm,=WUj)PWh%[ Ait$zh譞V&6 ,n&EXDH?k{QCplY8^tծ~#j_/om.hF[HV]Q"Dm@b;(/ ,H ]r"Sz96^#9hF~:SWd]bV6PidW>g"_JEmJX1Iއ?^h"'HV=jߣc jsn#DJFԦgaer/7csuuYgQT9H"Jo"~QIQ@x۠Dp7w&9V|M`37/=v3/7F\UUY#`EeޡA,)r&gHlg( je; 羞MfPRA6I4Teos"T/vr Yu0fs,V\/GJW"68XQ+fp{ ep@~ DaO:і!r=5(l*ɉUPՓk$ %Q Pw&Y^nȪ,ʉQ }ܜe$0ۺN]~|#JBN<ӚP3(l2+Y\%֝BO1SlԈw`]A*Epc}T8r j+|틯1_[IӺ:E`eEA؂nWFo{EHoM{UҝtOp{4 Op jg4{4",$_I&uSqbNv=I;"T n$9rk:[mvQEo[&˛c*8t0ULz+A=& hV"Q`dPP8gK ^gxZot <>pЃN H?8L?cϿDFhuoAX31,C ^Gs+ jn#W˟߶ 94zOT3xW{pN3b#-T%HJ)j=[Q@x۠Dpc&9k7EsCDu/7FLdUl?{rbn|nβ%ZPOhW mo/nv&_Ii׻+P3(l2+Y\% 7鷂k&]243[=QvC2&/7s#nl*I@<Ćt)hk0Q|P,%z`<ꭇQ27EϒxrcxVPOt.'*DIFUV9:1Ii0ۺJ)M_Iԩ77fP";dV &Yn/*1X78~XDW `&x$adWa%•! 2͸FnxGnWFosV9_Ƚ*; Ѩ3 (7xM`\ roxp` q&F <>pЃOq$1ktVUQ+e.Al6_۸.U`{H7dwlYu=KpwM7uoD3}I࢚M $_I&u?Fά٬99>,+H@CEϳˁdՕC_Z9d#ggx}0f-r+X~u @*رɪ+gs;i [AksW1$؛ (w "~>aǾЈbdӎ;v]`]01}`G`]ksװbXPt +|krXUtJ]`| K97G;i ő "ep`~`=HX"1 ~6B9E8ܵFDo*?$NSG詞c, 9gf=FdWA:] 3y(qK"i%oGMEO青UӇKzb<ɑe3p-PuwUvOm\o ^еvszպ}g6RJu1;F_T2o#v[Vyͪ蛎DŽլꨞ%ܜA+Zfa~j5Ō eÌj"FvchhF/)ehlFkXprVۓRgJIp pyݩᾍ;pfNj"F~XT2RӳFPTĪc~;!RT+H'FH[.e 8t,&{C>@>S/gA}1CvĔ4NT-I E&" rr,fD;6Y=UmvuY ;ҸgktF{^5 WPmxf*Gzۘ h 8{U@7> hՙfxr۹yԣdn&J2 7K̴AuQW'`$Mw'3,UW| [~VUE m2Ip:v03];f   |hqs(U_Co4b8;87G) PgbXoº9$qcr|VEgI ⎡{T/C|xr/6fxFzan+$n r3c6擛 $M7)&ɪ+gs;¸𫫆Uf?'hU'wAsTt=Kr/DN=i`]01 +,8"8n|r ^_P=8ĸre]+sSWL6mC%~h] n5EӢDP#@ɴ&sՄ)6s "FS 6hV$N*p.(wV `a9"8ZBqd#ȡGTB_af;W r ~:~?r½mDPsC{w`=rolrFqxC7 szбrY onQ_`QTb0YPȲp*Sgzm 'z-ø+7WSxI[}qT3㢚]D_mxwUWEƞ[NZj 9F}7< h^ר2GdpFp٬/p!& h^-HV]9D5>ͽ6ٌi9`յ2hsW1$](/ :ϑCob,aڑ;{g6XD8roqp1fm/ԟzqA'$FC@?RŮbWl?:Լզ½mDѥk³g6*jTF7hsϰb#AP`} aZq9960½R1,a)871%]7-k+pЃm$E0GC5FE{bCx;P*J#OoOzV=ɑeՙ]-u* x[􂮵5pMޛqH7|a#u]=T#hsM](/CV)ZU#U٢DP#}v7Izgu[­M[ 1$?XqNB9Ьr3~AjXc,ϖK(l9>V3 wA$_#lQ 砓~bc"mDPsS*Yid0V,zY%HT; +ilE|emX;Lw4 UT],6g`]5e}'n3Xe7"1‡WwEt{3ˍPoSʪK#PQIbdOhbusZzbI9ۧIo<Ϫ jW o*6olFSfc,$_If*u. $M~Hr YuװlvwWuGNm`}o7fwV ^7r8Fol@o#*wEt 8X(8#3o=[{y?,W5f ~t*yѦ"mDwuU? ,a)8X|LjP p<;3WH !bnv!u{;* /?$["jf2̬J8AǶT?3y(lHZIR\*'ժȪֶo95]H4,kru:M>?׼ֵvsLÞM t8ln{gyVlUWF|sX_x3wln9`;u ڌÛѵj*9[u)U EMEϜ?J ?/E~aC^:aoDb#o+eI?sIS. mC%~y%r ,z 0pa=37]VWkHDl_9J36&kגSnCx;P*V]Bs$GU#wvA*Sgzhmq vO)mfIaj6RUcy:))?zQDwd0WwEm1ɺOKzvХؗ^=r\Ǭ~fW -R\*ہjhm]ljg5y{0N<&fux[,`l޵vtaZf+P/V55ZVHnٕQ/0R:rCf]1Br5rozS2Sgdi:mZaE谁_q@ѯKװ|h)90vXUqGH C%bV4$Jobpema_HgCuz{Bѿ>3ޣktպB)iELݬv}" O8?bmJX&<&o_d\uL fZԄ9YU5K1װ԰8X PP*pkV;.+-{uI*CH?#v!tҏZaT W`hݵ#*IFBnW-P/5ytwvpѭYI<*ۑJ'řYu9vm0_qYe9D \]uJ9Ta蚢mZT09Џubt@/&bn׍gyf?0ʯ24$1Pwn|3Hju+IsAgt]2AW3ϡb ]=UzI]P=Xt m};NapP5NM(F5FWcgZqkt 50RzfWtrY 3"F۞:HJfꌌ= ;F]t0Ca㰑_5a)ڑ} ~ʐ~k.&"WH C%bVmL 8fzu8(|>ȢfF!͆Q~*P_"%3}'nͨ]c":6lpd)qAYo{m]C[}z1aEu.4& 7ϣ"#jGrBZ=U7#լꨞ%cB=}YNﰏ:՘Ô*C.&bn_<\Z(bJa հEԛ oOJɞVJy_ԁaE?spѿ8l藌aP\lRC+*bU1s[Z~N է"PU9c7pB12ζ#^:Y@1a[Ѱggm@D$*:)#aa?(v/qӡ{>_m531&FD'U辙XzF 1 )_a~wO`̞w[v?IxOV[Ro?Ǭb`Š8ca}#엱a=0wׁsUxotuoL(U@hW$s[A$w}#M|Wjs_=~+⃂'H.Ÿ?>_n$&q U~䊨Aɯ^0N:,x'"\_0FLdUh\PĈ(Eq~`|('F4+AMM~#JBNq>+OP3(l2+Y\%~AU>pЃm$LgQg DJQގT;=wJ~92"z6x#t[aW|ہJuM49 ڍjg4k  ByCSj1um $M*n@!r뚖^~¯6bҵ2 ,b H\]EP8TjiDž;{g6#VP,8X 5Ӓ9o=+=1;FE\*IjGEx;P:)Y^KSI, 땪2uF:>?W-􂮵ƽ:S:摢oZ)hXTWSXyBV]Eew;qF.hEN&oN/nhX9 ,b(HA\ ^W/*ñ 1Aհ8XF- P8,rk+wXWx`(CH?oEB9XŘbDo*ԜJb,z 0,VQY%][$D* wWV?1N-9oGMŔ6W헒uȲp ]íWթ3o k00{V:6RUcy:m))?imME .Ctڕ{g6XDOݶ`ۿy:rG{ks?~E[;|bCx;P* Y.* w\͓{רN/2}e[fSPj"Fv^)mҸ͆QĔ24Ze b#aCDx@՛:cOKuz{~]kt9{w8l׍kXJ16-Oxv TŁ+JE'鷁m~c[R/6Uvݙt>[6Ym:{;~%>>_mDb)Iu7_fsQНoF`otϮMzҗܑ~3ɡw"] |uT3͝6ٌ0`o724 ,b(HnXIz-vyD>GUE#0weDTlq샨y㴄̻o+߲kt֗`e{2[*.fT1ZvW@<  )e}ea#aCDx@՝:#cOCuz{䎑Bk$R%(LxM&޾4`&SG7αz9k:XHPo]ZԄ9Ь2Ԣf)VRAK88dN8"8ZBqdCȡj]+4+-ՕC 1xbX|[l.N~젎1,R޶T")0~]=m F؁&،0#$C_9/5z`jf\tkVvԬ2/slEo|9ɑe10puA*Sgzm 'c]ؠ:6Rɯ]5WSҾFf( ߁EmtZ$Gn沃ZlJwxR/|>GL w }^«QTBmY.yͪ蛑OP=X,hluPD8yt0+vaF5FWcg: "!m6VrI K8>:dWK?%/=mpPih_vXѯ97ekXNet+6[z.#asdinlxT+L'FB%bNfmދŠ87pP|Nbl,jamNEO Nɋ÷%>ծW[Dwb ̂?qH87yJvދ7vSZ^=آsճef?sÊb?nQގT=?з=ofUMHD5̺YwnNFcuVja/3"S3Ecgz  RFkZuh .)QMJɞVJyṄ_s̢ zb}cGkزEK uo= rjP5#zrmL 8fձ\*0|>U\m|Ff(bUDJ,!O6"'%jxN*Cٵ]ףP,?,L0ٳ|zk[# 1wj7)Il5X}EE?R斈gu#m_[, 8f:>vww>?NHlUDJCRM}"f&hJ7vTOR8?%Ie}LzZ-Hl90fHɌ/qZ= sGLJzŠ`1(0aѱ)a=0c6 z:ZV#d~VIF `/"Kmmx/JmxUkyJ~>z][Ĭ|(yG{p}Ԣ^UW9|4N/?S-XD8p?X,8"8Ʈ]+@E԰$٬tq߾  5P[H C]K%y7pB12ζYhu,M3|6TQmvQW'l3CKapٿ'F_xsA{e :xXɿ'Vn], ̀-/xEMUME?F51Шa ,a qQ{ZBqCcW]^ҲTWĨra8H?oB9褟[ܵFDo*?$I,z 0; 8DKb ^}+IǴ~c]t*GEx;PGli2/s,bb:_NrdYe8D \]u z[~[Za9[ZNGU5#ujl"T{?0.CCAw`H>KXP9re :Jwxb/|>GL w }lqWZ!vZUVL*whVEߌ<&xttՄiKdxlaK0]FX|h0 #j'@C 06FSPx5wm5rqi "ۮ:@J =-;FZaE谁_vX_@װ|j-=924w=:'Uo6(JĤM*+#a::XYhpPX|TQE͔v}!͖QDqP}j-؝o\75cXqM>&͌iIOͼ z z9ZEg˖@?~ i1soCEOݞUR`gnUfݬzWv-:ڵzthZN-PPj"Fv@W@|ʮ6lƹ͆QĔ24pQaE=;I)ٵ"HI5O<}9kers UDJfNݘ/E'mE{y]oɑ!!b ]uSEo ?5o ޑottma#uc~ȨVva26F⊝P*mO跁qn,Rzamˋ+HX96{'±֥429SdIL\V = /os -#5wV&Xߟ}WWK s4ơI}G~/)w`L +q?˿ GK8G-~`]Q{S0CH?KB9 }Sk<*ۆJ~1;s* <,z 0taGVI~%/{zG+Iv.s7ullV=pww.A0ފm]Js (DJFѳܺT-f&X?a^ sf $ 9Fh8|(1ζ'|XÙlL#istG̙c%SRwO7JTd7$Ɣbb0γ9aLU ; r}[NYjly`PuĔM&8WjozΖG`O}'8fC+b^b\=0%2}ݫ7#8՞+Mjwߐe cǣHw F~34Gyd0;7dUx(9Qc9 ԗ<~_3[n7j:{+M1x*3Ώӱ1" j?c`oſ>VTߤOt O}{t"nKW&o$GO) >yk|_7{{]J|vuߗ/u=ukdnG~ucƇ@x[Sq{xޒUos+  lRhL8蘽[,C;Y24pݡ'_9L]g̙p>VCST> X)1jZ ~ ڤnxk{יKB4SѶAw6އm].n7:M{.=ܷͽKDLnyjZ5W`pf1hQ5#n7is#c[ JN_~{>[vt׫ZmoR>Z /]iVm]W$g{{Z$FbW*]Y%FԾ^T\.uNH;]E="ufJ]o{8YrD5f b[ uIӧB"1k5e}٘W.R~neٖ~ulYˑw:cIYi}7~ySms^5r? O' $(HJ۹'.+^Ŷ#g],SlrdjUKڎ:Yl7f2G>;]eԪVTri#06c)gUos II50nLud({z[(^魂&bpXDsr۲EB *-<}[DSҿql˺٘.WF0x}&Y~n̓ؖ~ly9^6@WtSޫ5©ҧ;R\6t?+e@VP IIf@ OE+ڲvS g$c~9vkۭ);jM\Q@vD*k'2#5_c@QG# j`ۍ)IXTLŠ~ vҝ7/}&Ӧo(E-GU01&b7IUE7x7ټ46 Y+ AnPVV9D]]ZZWys.~@zsuoH1tZMnY#-95{exN1g[F9jіEHW$ή,A˛jEX 1W@UW$%Q؎7:ez9$K$6 4 9t,*؎Mhm"cO-dUrVxߒ,H2ka50 xf)*o^r`1H:,fz[Dl7fiYTTi)ޞ8U01&b5U`ƛj{,_ P, ȁdՕCR*m^Yo},\m16//vU~jL uKM]I ԡ|)A$a%cM o$3ejm>6ѵ6&<XDO `&x*@V8D5 PR;앶ˍ-XStXVPT$9jW!n((Gzkֽ*CN7uy#xS=D53=M4`1:fz7&MXDW 4z8Ymɪ+kX_˄zzxUA l2I.1&fͰE@ ͬ*s*z #p9'8X(h F{|mFl!pЃO I?3V:SX4KxJgn>{6#5 OqJ>w oNuV[֍omΡA/a2#bj[F9j+E Q*?zuy]\~0 aEUW$%Q؎7wؼ͢ˁ txWm0%3j)5r|[ހyO <#JByOpA`өu^m/7cwɺ:Ŭmn Po.I78 ؽcXT6(X-ϕ&b_9`F[cՃƖ͞p(\^Y*]9@FmlCrFm.x93m,*Uτ-hMifUl*I@[9p,*ons@ᇛY{"AUR#~SΪlPA,)r&[6 `$Sn=ܼoo*Jz0NfP";dV &iz87ffY p" ȁdՕCa@0jmY8^tծ96pW{' jȕwD* B#i3:&ڃuuZYu6BU$*Ic,*ojhs@鱋;:wXBD}1*rcͬP7wUN]>;:ȹ9*\A9>1I\7`>u܎N]wXx0fP";dV &J2;U)6롎`$.X~qc=*HV]9D5 P7_{q.-W谬p7J$GimYʑ6&a8Z ;vp3c DQaFdb`+$n)6dO $mޝ$UW| [>HqEQEo[G[6&˛c*X;j3UfE  PzI4 Ǫ{02((@kXU `ˣK(3<oˣqj= zЉrQH??v!z B}0<[suKl?׉uA_"euy˵PVWY[jW j#v4ۭl:5hLI@fVFS4$E:E]c0+Q@x۠D4$-p Pz~Un%/7F^̪HLʉP)0 LjB[LtC_IimaW]0Κ*L^Pef=6N pݤV@!adW[2Ԟ6UfQ(qDpuvfRJ71@UZ~t{XdOSI2<[zG#B10MdxWne+w`$M*h8rk}ϖQT)JUx6.MӉwY6eh>XHx 4/M f@!ǹ&A԰"8X(8L^P==^=$ƕCH?mR9ө#V*ă(rQĖ \5 OQn]Aq8fezk{ O#mLB%1.54E}xUh'Ϡj2,$_I&7A3lΖ,+H@]oRՙfl{HV]9D5>UMjؘ]eh#ij 5 ~-*ϑC4ߊ|dSpдm`=017\䈱EEO, ~<^tb@b8D /X.sSO{m*ۆJ~F퀚LʢD`7u&̵[, R6X Ms¡>VE߁?ZMhqE ,RPI ֙G8iDZU"1 ~>7B9=؛o@~XDwA2ax+(py(f1_y|l{H%rgCVZY?5Y%yT]7wبwPoYUMJ56[56}ajl⫧:[W͓*e,C& QL6ILk, 6,'HP\n7(UH Cm:Q{n"aErt @$' 3js%4; vQχzOO'&ehO + ͬ6P ]W=q ؛+LQ@x۠5|@9^ L PQ&Y^n9UYE#8H9*o^Q9>1I\7`>K_I)WuZa%cMf k$S1<$1a~~N]P!޾R?"hV2L "5[tLpuF'ş߶ 94zOT3xW{pN3b#-T%HJ)jqn m mP"1G/17EpЃO~U8ĸrwnϙN]l^vA<-u``^im.7&aN1FAJ*G_ؽts^ISP$p v;F\);  h6#,otm`Á3SlI՟,+H@`=,vO 9r/gs;)ߛ𫫆7 1$mk(q|r*gz #Ni9G)GGK(μׁ n`=] 3LrQ13V/ `fskS6T"|}!eop5EӢDP#@ɴ&:]Wf/ 1$M~I4zJ/"Tn7)ǺV8r/j]\nlЕrXWϸD lҫ`Io"H.&9[@U5 >TpF}Lvx!6pDVc|%֛e}x:ٵysYEt @o 88rkXsYT%*4&Ę ֝3];1$-n\/ʁfɱqs|j v= zwu,aqΖZ,8HiovWA:1"pQ_#8LW3q~t)jTAl6_sv˳fV~pO{ zۛ/*gU4z {禨g$Y OH h^8m\c|%;7>fNjl h&UnƶǁdՕC_Zstfp j:΁'1ǹeQ'='"m?$|pgETO1YqˑGqFXgdWA:]!gx}u+.$7ϡb=wzV&lYVV$U:#PEBn~ \[)e>rcQ=T#J 06FD"#nˠқ~4 Q`]-՚rIسFQjt#X?ip8 OlF53z& 4WY`-eO̷Y'w!V P&X?Tp`/,UW| k~owwUw224 ,b$H7A*`}Q,ρC?p8MS <~´#v]`㰄c,cyr` ",S^aqzsQJuzԥ~Qs6mC%QUO+blv>}්ݨÊy`CA18O-DY;`ˣ#ZBq$#lzt'FH !b7B9=][h@vWyw`]pon5A9Yx#DG]+}RIBcydDJQގT;#a.-ordYe8Dt \T]+SEB鰮kJa#E+cJET+iH, ~x0O !b?掮r9G @ L$f9,z rю0#$㡿%F}+Y 5J-9Uot|wm;&92":HWeTOCuz|-bW<cV{ma#EUcQ=T#0}!͆QDw ۯK;@p庭K쨷LϖJ8Qh0~Tg?w_2<*ہ{,3a\oYUMcBjVWTmrΠoQl7:L2[*ڌ.fT1Z*zF<Z(bJf8 (װ"嬆'dس:Za~ E?s؄56PuװLiKzͻ5"tOX甡_TC8pE(PKoEÁbPgmGN0AX|.PEEM'Ff̞&wvc[cfbZvmF6a@AjWLxM5 VzxX' _."!H?kc1_ qGf1^tADuV]/om.Nz\_fX,T YuE/$%Q؎e`"r$H LI\mjȕ;2x3DlGԡ|L@ߣ)'0o`Gꔳ*kʂ$CiGg97Am+8\lNmXĬQ/0ˌ`"+Y˺OtWq^Ψ30k 4 R2v}ž 色ÀH{/7#)RU$7P*qA[ *onH>ԡu=pf+<K/ s]bV7˺-T9jQ H7Y ʑ6&aEb;Cx ߏU|VI{6*FEdi1ț*Ln,~x3m7 p" ʁdՕCRP!o- #6(\t. DFmɽp96W #Č?gQnNԪ{@DatbRˍ԰̪os U ^7H7ۀ J5|@9Zi""IV#~NlfUl{rbDTVnβUPO`x&KǶ,|Sw7|%!SD^I(a%cMf mҟ ;L)6 P,ݤZ@!o1W谼 4HR O9\ޣrQ9ۧI x>I{UU`SwأPx.>@O5B8D),t%C|ExqJed$J|XSybɪ3 lOl=l{2R곉Ix: PWma  }*}S,2 ]+2 *~e[GG]9ya3/S?I[W٧8iH A,_vN6A"}P@ h 0I/ʯLžïLkW%F Z2JVZŦC$x(w(1j̯?%#hg(^*(Wexo.ެʱL\r(Jb>~=%T6[Y?F4mb1?0ĵ07 JϞg6|ʪ#"? i60YWFZ(^rOҙul~3aQLVRFɬ~?#_[A*ݬ}i5:IbQb%ߋBmE-)&6(H[aX5Auov*?E7Ҫ'F/,`A[VV#P6Ⱦ̦PCiK_ ?Ҫ0E1,Ƞx$$<) ^ )'S'Q0TOpsX&$Ju FCCok_XGaNiam6IA3V%-~e|Q^x2}KԶ .34R>8( BVE$,Dk@_ychPdUa'dCjK`kya#UFk|h(Z@? e~|zڬ3;ϕ>d-> zU|2 *~e[Z*1v '!<}*6d cz404q i]soi&݃*eC(QɠXq40h7gӼ0%V.)ީ('`Wl:Tya=# Z3ðGb˵yQsǣI:Ҁ26W}{ɪ3}9dhðH5}2q 8W*t P*s559c_eh+~eBGG=0r 㱂@U`c_7Ĩ`p7^6mXAіP$pMcê CjZ$Ǧ* -QG*''jBXA Z *CUo >]Sb)8WouH]9yag;cG[W ;2H$Ov_idӿ)-H@k>&c7he&~UqQ0 cah țaؔxahvFGJ;7{#( /1ذ UY5q7Qd aMUaOG[ym?t_{+t̩!=CSx~0=Ԍ-\R=kICCo tG]9;o=uV{fT^2ę$H_tWD_O (U 1Ϊ lOl=%UN @Cq/:JC{KRc 3B{ّ =eyï{xnN #+'O0|^DVOg;ahAΏhgS۹DGs O_plMe3=M3aT(OcLΞ#4qB%{#( jfBl9ڼv bY%0Hx&#s4MUaOG[험y^P}_Vy}֖zvjCG'ѝ֦2T 0Ӑ2 oˑz]7e'4m:~U 1\Qku hg(GvYViͪτ TPSA{:P9`o<)l7ց@4^ޮ:pLPC@V,S ic9,2߮@iIIeH=o]&1UրIlW X4o;VBgoW` qcaYߖ4TPUj q3[;[ކdF`,b5 mҵQ eFT)Ww!0ܴ&I m;+ E6Ô}\Ǩ_V&B̆Y?|10}]rJoN*/y)L ).*ʧ>$k12}槈z2;EPN |NZ3s:X V&{=LYC/9e'v>~U!.V=1ҝֆ*}E'Fv #}m3sR2B螼w #zZ$CȂ GbL[/`O=eN4`Lnۭɪ3 @R| Xݼb!h[\▕+80%lB#fbox+3 집{>tΨzswi|dGLЙiNy18ReN4} {U@ɪ3 O~eU"5V>JP PK k]ezڬ g(fY6I@V+VJ<08D@ǞuZŦ#asCg vgڕZ;6T)B?~>6NMZݬKT0L+2 *~e[Vؕ'F13H2]i\Wf'\)-m4m1XH_'@%7 h $|{ 2Y01a= عRV)';=0I=a팎vW'{#( jfaRME{}1$L3 s<)hk?n.(̈́L{ 0o'u)MP{v=xU >'D1{DyG0xmׅg}shCp{ZIT< ?Me}h79Rt^bj4+9tAvޠ紕(&qȹĢ9<.2 K'U?A޷`'lOl=,=}pv'Ix:Ȁ2'a9cl,kT f^vg\ ïLV舑'FV.c|ԟ^> wJ̃;AmbhA,Yv>.L޴h (?CtAP-U4G7g39.] eN22AM ߖ1 TIv+ߺ<d ʾmVy&>yY!K&bn-HL?$ϴL!Lžï&8f aVMU`O G܊6}T,` v>;H_{ ? ٳ iHWXhUYjL_pS>*'j3TV&$$P|N6Z3W =eU0ؕ'F1K->LٷʼlH A⮈_vN6EOe1NJ( f$ $);)+aL1'(9*jYCB g=#F{ϟ"@Of;‡>;~.V&amWA(;h5QoVir!"vtY]3omy* PP veXvUV *M9 1,?婮6oybe2VfkC4&(=~5wQWʆ>/|-QD$BC.d5`cc9Lb̷+P֔,q=%mLjQ{?SaHS9HlsXu>;x@|N|2ghJ*W-9n>lz vW@ 3b$V}L<T6-j@AMMɺMB7`Jv_b;pPEqVfSkqkkTG`O@W3pINRSgU5sV+]i=u>V{fTW2ę$H+-_\XEKI:Ҁ2FahA0ۓm>>ola=e)vl(eNuP=I؅b1xSW`MKKT.V&aI 2ˣ#Ʈ<0r %UVWgA1Q hoTJYK,xtozіP$~ڬ ͶwO p~S~^ tN-ӊ0nZ2ʼg՝ʇb1fL >XaG a7b$O0`ǘX *2̮WH A⾛_vN6EpMGJ( >}2Ū+E\LĞïLՄU˼{bYY/1a%ϭbSNv}{ajj7mC" luOϦpXo1"BUT5l*)tv2YU 61J|Ѫ;p_P&J_7Y~(aqݍJVy~~A1Λ6\)lVe1NJ( yϺ]NW)0$)+bL1&QRwVU6f=a1Mseh'(Lj"uAVmm]׎AcVI ES8SBv/L7IRDI&|v~4n;Yv` > Df;ɶws~O"`mW&+@qһ'.躛ƭbSNvT1z6Ps#a v-u hge"<нFh{*3T)ox޾=CLe eTf@e2[-`4^ޮ:pLPϛ9Y !ޑeC0A2߮@S03P{=#FaZZyA<_ 3&(ÎPjMY[qaaY߾ 5Ams H E"G{дm3\NwC^o> l6ZԬ>`ZVh:Mm*{$mfͦ=mlaH|ꑇhH 7э>OIP+ ǷU-ž>0氚P]W&+SFVΞ@LV+-l91@b v~KɦK^CJ( Ǚ,u+0$vaF$ t7[Ŧm ic QfydUݺL2/J{!PEm2G{~1$<ב9*񰧀ڣ\pEkl^D<6>yxl !#?F\Ixx{L$ok{è6:is61|kcaXh^ý}_KxV+²J[EۛU96 #s MD+O'/ غVyg0Dl3Cm D7"2(ޯiր*q0Y5z(R41P9:0]^&`үA9Hls+ D;wh}[N\PĐP$xtG~2 F7t8(p>KlY4cTJ{,7-xI)13a$zyPv%UlɎ*Fcaٲ76`B JFP@3yݵ86Uf5u35{֠rZloOU > DBC.dZDu!2,ԠRRv{>ӧ0Q4 XUR;\qdNSJbcaX޹prKgGP@brÜRoaa}&XY9A{^^DgH°Bk*@Jk֥D2[IA4] z0h@I5K €Yk{KȚōg#K$X|-CD } 1bܟ:ZJ(?Llz:Ƞ3 ւD[=64os)m,cZQk*@J&!_1H&z$"#$9|ÍY {& -jLj$oC'ž`Z 6Jt *x[:)XϞgX/| # <6=,6<1Lf¬alޤz#H= .%q?J&Q=3wU{H|[QV?7eݧgI%5jf 'PN_4o!kUՏ(4v>k: f*PQZIAUg-0@ӷ!Vdw_t5}R$`2%_~F9){ovǴxYL{GZfS7^7Jͩf!e)Bu$B`Ig.іZ$t[QN |n{oVЇ>:l^kÇxRj% %g$`RگW.]fU~sOID0/QJ @ hpIAIkbV]LB7Sgtyח _N(۹iN=&Z"1ZT>0EV*Ɓ.)IG-~qZ/ %غ6H{=LhP# 2(1I2Ax(/[2}2uCe 7J MV1Hh8L볢qcW+_4WDl/12)axVR vK6^N<АzlZ||sOIXkW]S0[yJ,>R4}IAϻ"he}oʬ~9 CjbZIȪ[e.A//=Nڥ g8ݓDf{=LhF # 2(1I2)4)/ ']LI I`cz404Yu p9 gq ͩ|ZNRj1*OÜG{h]RSQN 7& O#_Tv 9^ƻLP̋]xJeN4̗pL'A MV1a'|\qjU-H@M}䱇#RyM qXT&$#ʜ@ʵ^CUcbfP1VP21d.Y9{a  [ŦcHF<cheynzi{"GdP<c@e~rV}>3PLI I`c504Yu ph6T}+ #n' .cqݣq`K؄F('XY^ʼ<=ޑV{fFGy;?g e~[uVI3-v)^/R&a$ZzO>yk`9L'`}bCj D7jК'X`h]R]QN N\Byc!_tdwOvOy GL#re=g^|ǯT&a$J|XSybɪ3 lOl=l{2`T+Ix: PWma  }*}S,2 bP' g>{U._LI 4ɪ3 @R|]P_0 a+Y_XGaNVMKh TN *k+ [6vOlx=觑uΏę$JKl3/vۋ铩( *ks5۷G:c9 xO|iգ-H`M|dÎT.'^ ,FP2 O'vj1YW9>$-> zU|2 ~e[Z*1v '!<}*6d c504q i&cz;6T)B~O}_0mV7 UypIoló*IA?blR-Q*,pI0u=DWQkafmjYSN *Iwd1yCݨ3x$$As| )*0u}etCUg 8ٞlm~ 6\N?*(>h(J~4@1|` =ey+ߺ<:b#+;g*l# Mb1Hxy=ڹDGG "?DuE{fOt?KP1BwBϦiEK NbV$? (@tENgݖIv+mX Q 2LVo2{m#A1!1H8>O㗢Mߗ"-׏1ɍ)nKR&aaOWj}^z{Q*6dܷ &Cw4{?|dUDLR7B6 ɪi}b8ܔf3NX(gLIFt/Uu^PКA(Hb)8W%Ʈ<0yXq\wظUpe^C1΋UZ;'Ř:*h (КA,3Z=e'0e0I2JNV)';}3IGX;{FdɊh' .^ wt}Ok1$ uhJs)hkMx^Pݿ/`}v~4n;Yv`{ > DfruSXxGu) oc;|ƓI(r!"kgpĨ3om+ PP vlWez ެʱL@E5=+P{5NM0wv]plml 0&(=t>U > D!4RNELQ"|g'%u"UL_O=ed$J nɪ3 @R|]&?[ju; hW֥q`K؄F('XfG*AO#clTw']4>8)vfg^z /T( +褻uda'O+eUz 3#=|$rm E+pugB{ZCUtwtbfn)8Wuytؕ'ƞhسvؔpb$P$FrSwTڹB JC|23m = Y*}hV.a"rRF ~~–C8 0ve6|d#P`$5'lqT#u?K/PNUB| $,\LIt+h14Yu s667c'|6PK*(ZʼQ`6L@>ge P`fˎ3vN2 dNC-RĪFCR4>8J  ~Ix: P|bxi1Y5> L _~r(a e<#ڭ+ ;L$ igqWD/M;gx(VcB"0(K`UV'0YZaL1VrjM9g Ir<3bM YSս!>;"z ʋx{Q~D{cUc:4GT9P{?~ e`\0c=kCe%Ni$v6Aٳ A|$4w]?$,-17e'v ڰڭbSNvT1z6Ps=؎^;#Fyk˂>VwoD;C-,,Yk47rl,?*PAM5jCeqM0 Vf.`\FX2[jX Hxyl3As X"@O!4RN.p |eNS@GӞkyg|m$)Kz@L j>{x @D|N d>qUBf*-g+28#FL<gF}v=gL )Y; So =l@bSrN/Ê|g,i.T~ǜZV^!1J-~}kaMfmj<ΪBkMWH Wrhѕ}sZx$$Q+ @3)aRed$J|Ъ;p da'|GT6Sv2o볉Ix:nTC$bsV]OW%*W)$_cWN@Y %UVFU$MD[v.l2ѽև-HѵYmY#M5\N]'gӴ"JI7SP 3LIuW\L#_Q}Ȟ@ a:;o*2̮W}7m4lK~P@ǘdKRU$,)17XMĻ̻'2wXFJؔpb(,M;#F $rd½ ŋ JDVmkmpS'bJbL\+P֦:~=%TYY?F ͅL\P9H_≀9xxҳgjA YUAi0[ln6S)"D_%LU#ϦO2 O'mj)s50mb4165p_P~ SHb 'F1 @D|~#!\%n"`ؔY`0^²9eLɓ9* MPܵ,5+f'z ;&(5Pwʒ;RJbfqc4,T@&;C!HDBz K#+4/5Q3'TTct5k}`a5~Oa5 omM񀑕'F1̹P]*r0'FH A#:~i9ٴ P D[@ѧgQefÔ= ($ag VݐbLm뵳k畓UYt2 vfU/X[Z/0Y%1^:WES8SBe5Q 5Piݾ*g9Hb oMPz@f;{Zi>KDfruS&7o?oU޾}\&<59QKΰ&{#( c".QYc04TyӖ}{wK8Vh"e,i)`fpv6A왠ĸikt  DJS)R7`y_;oǛMœ.Oa1va.eQ$w Tuwur)';=b(سvvnB FP@3ʩYݵ87rl,3 TTSYسGۼf*Erj*S+vg݄~ DD icT9leywSEQ-SP ~0S-vl2sMLТfg;8D|ΈS!*:T-A!HD6lz |Ch vbз9A=ޛh!ӷ#> DNS)R7`y߄?=lL-!g Agv^9 کa?Pix7;`#Ye f%di$#F1J(d :61b|GPBTPoJȭ靭Ro@2x#FLuHF \J"> DiE)N` ~7CPY?a5:0n9J4OFBۼ3UBg8ݓ8S{=LhF # 2(1IvɿLI I`h`hA0l_`4tWDCF'a.-DG[= h]vI夠z`Gt*/,FmHjugvi|d#=ey-QG+0u]G'E-& 0[jU-H@M}䱇#PyJ qXϓrE$dh@_yeMڬ g(fIk&lSK U 2Yˣ#FVΞ@{b_봊M9'FƸ/ EbT0Hxv֮hrHuJC(QɠXq40h7f3a/qV8)ó*I1xÄ-QG*Mbn]"9[R=:Ҁ2_55W}{ɪ3 lOl= [YUO؅_1>h(J`B_Z _5ak> x n–壕IaïLe9'FV>F_Ҝ@UpeiޖbhA&cz;6vCP$!:VMjU"/Ǧ*Yz27VMVD+puhZqT}{[*1sŀS&qXïL舱+'O c3vfZUpe^@b vwҴsch (1W5 $,)1vU\b= {1wXFJؔpb(l@*,poD;C-,,U h?*A2q@h7 }V66A\0ĵ08o `3ϰ DHkhg[V'%:c0n9qX\¶THo B9ǰ ݓh>>ˏ4C ϼx=c/G+0u]eTg/Z MV1Hf{g{Meaee~c{6UP|> a$< rT~F0}T`9a<ڛ3'/;>wIXaïL?6`++%feC EbT0蓧oh*' ,XwP@!4AZim*l?F2-1>[a$<PmOSd`[Q"mV&acH 2ˣ=0A]OS*2Ķh1$<,|nil6{($o(ϟck_PW&+t#F0\D{v4TUlo04LRGX;#Fy#RwoD;C,LU0h) FUiz27Ua=ms#/;+uöԡwqψG(=PUgD8 "j4Ԫ,H@3}#MHh>A$g^X4NbhJ`*ބ^P~ŀS&qXïL`h?KL@b1&f< ' hEK&bQc-f$(2 3{Na곴z= 82IƸOQr 5L$۴ig v=" Vrj7B6!kQ///h1Qdx 䲊_=s4MUaO>3mmy vu1س60M# `3nO,Ii$+Z`;~IXx[bo1Nb"`G[Ŧb8lF<ڹμe7>+]UFh{*3T)n`YqM0 Vf.`\FX2[jX 蓗&(=~5wQW>4CDgH"\4kV8r aoW)Y{Jm uhw-S€9H07 Ja&%=ϼEjMCBTu⪅#2`mFQ"' ba u泄ڣ'>\}tPS9Sa4D7`JfCm(jYfjut_{퉁t7{fV2UjŎ UYwOS=T3I2WZM>\XSI:Ҁ2_$bi14Yu s667a{B'|6PK؊]Jʼ'iF@>g'0veD%Zi&0ʯz؏=ի]2^un\oY?Z Mb1HxyyvFMe;62h (?Dib,@ZmϦM;a$<ĴkWOة|Z 3/aU>>cWD+"0d.#F(fnXyK+"C7qM/E;'6hB-HI6^L0e~e[7a_9|kzA=Տ0氚L_VNr(R.i\Y9$FH A<,h헦M1 FP@ d};IDISvgi0`1P!*745${<3b$|qUB!HD6lzB[U>{- 67*)jO,Ix+MHހf{[f?3bIXF W eJZ;+AwuZŦbH`j DTJ{, n7V-I;ZBnu ή-z}s&1^کفF ?2ڥi<"J̲\ cfch;#FDy9!.Щ5X O7wJ @ Fy]69象A}KiCDgH15^ %3"Oᦉo6a>1I k1T͆l#osjN/i٦RiV=[;Dya iHviyٲ|| QBpeN4<լRvCUg s667A縆*?'| W&$#ʜ@ʡ&@>giW`f0嗰U_#+gO \D`:~*:v1$Y3P,h^uto]P@ OjtFB{6o7U3V淞-/󹽁'Ix:ȀNahjǘǠj4N<6I9lWuyT1vO `XY *2Nl61_vN6S2&@-mE$Ӵv2 {J xaFV>FLs\;dPirC 4Qkgމ6K"&)ANP|4\U㩽Om (J`L\+P*񰧀ڣ={m] nyTA7᳇;`>`j7CuH![lsב"]\Gv:utx2 W'Lx48(O!M 2²f Nl+L\7@% 34Um2Oma61$<,kid_*81 TBP$k|\Zx2 3{NaEYV\hOQ]$3`e-+x W1MTr*AP:8󺬚YS֎1cVI ES͙M TبҢ6AM,?6{`j+pvg  O,铙N2lX%_y{XxFBoaV)';=b(};Z;֖YY:ΆJPH²ZU0ش3aN{[fFۼ f6.fS,i$]u6A왠3Z)W""(/8e>o+1 $, 9% sqMPևC@푅1gLPI.doSckgLjm=_aM$#( jfBe9,& FUc:4GT9P{G?~ ԕ_ԕPaiQ0a o7MP{v=x4\Ixx{ ᲼6\Uǭb6!9v1x3sX͆P@3±ZU2ެʱL7&<5TذDۼ;>qgenjX H &(=>U > DBCEv:[*q0A1HSBvikZyAtS |=gJ(,|f=Gih[ÝJ%c@g{[Z' b#pP|Po{GkA,I8+MWG{Nq v[f0'뙋nU$,#+τUX+Xu*6dGGC {6heH{#( R[LRPfUe_j>/h-ӷy;b*^0.-rjjX 蓗&(=VD;>qCDgH"\ɴ1CdYީ@"I6O}@ԟ X@EHl2s,Fg~4U$, qa1r gGP@b(Q9ۥ28#FLx.gk܄/`=)Y0܈2r+у$~kmh}%wC.[pF7Ddb0cCDBnٳ?bT}&۱6ln=%Ԟ]q=w0M{uMd蓅7pg`$F,x*m zXl\b<2!=LzaM+̞X'q?mFxh tgȔ[QVO='QM0{'AV^bQ=U-P)1>+)@: j@_Ym5F'Z{D;}`L5~O%w ïLV(Y9{a1ug[W @b vKҴs/ ]ZlýmEYމaL̞x{v Io[*pUlo`: syg poD;AE+Jj hs(J`L\Gh0Þjn}bmi" Bq)Y0쓅owu/O!@|$b.Q{<)!KlF/%zDHb׺=8os+=(i!s,V(!f1:&(QBM=U9!~ߘPPUm59nv?Lstzqd }&ςNFۜ@Р=?i"}VR2!<֐ᦉo6u <D@n0gyб3r.FgOv[ փi_5U`JOԋn'<ǚ}1K\d|mGYK  {ƪ&f<u% <mǫh; yǿ %:egɃtO72"x{¨wGKy̷Ul4Yo P{;c ql^ݗ0~Jjfa  ,7fU+6TTSسG/a\0. ޠC;P9`ji$]u6A왡7s X"@$BC.dZDu!2LkPA) pHIp=ӧ?,&(3N}"sF\Xf9tv$"ۘ]=Zh|}&XYuV77 j~n}}Y 0Ji/Í}K 0} IA4d9U9̲~$MtlL{ >`e1"%wgd|w %1}V@ix۲a7K7ZXHoh '⵺ \J"> D?P)WJYJȽᦉo6p{Zna<3"4܀س?bT}~ ynn=%T`zԷ"Aa`J>e{ ȞPߗ1/H2tƐD Y’KJ"zXioXlB@g]U{JH:} *daGY??y3 2JFDƨL+_kw^Jvx.@hGWʀ 7g3xP헰 D}]K8ayo R$FO;[ކ2h#F n9]|.&1Ă'6Lc%TJ{̈c{S&=, &I*z.{f F xAJͻ=asUFD36" ǏAS׾{^R=D\׺7_7@ F,`I%9!=7nj_`6o̽[. hIfF |$3)YXL{b&'9J -.a>Э ti-rͳelh`V iۯl$CvpsF6KIw+~ < /a9yd# z=)+feϻ?Wiw+V/2D/:C~V=J'S(X(ޖPlg+>ѵ#`MozJ*?v̾OQGm@m+!h{Z]JyJ&Q+h^YOAh݊QUOE^:5digE^?[*LBNrxC[VU|zlu*CNu_4Yu"II 4};>*mk: |hOVʔ=/.}(LB+)};im)SZZ&qz(6UCp{ZNrԚx#CYLbCj%} VB/ԑA|(NJ@+=)7[jLw(`5OKfC~ _ܐ6וVRR2k,|6^;#zݢiۜnV>$iIyj.ןNDh -.&_:=02V&ĩLeiy+k.PsӜzVMֵZEbZ8,EV*Ɓ~ܨTQo>feJ^P2{wNMV -{dAG.S/(B}e^+0uCepdQaZdi뚦Ѥ\@V. V\@>/~cVR+-n$ msYjtܯ  i|J hK-Q*'E>'x(~{<~[f ehpjU#k v 1l]H_*}_ŜK|Qڬ!=yIdV^~ժ0E1,Ƞx$$+ 7gjy2}2uCe 7Gda iK _t46!kZNRj1*oÜ=\5pI7('XUhK(Rr=g<%{Z4Y!HIY3/vE+I:Ҁ2N'A MV1HΟ+ZjK(_ya#UV.׆ P2 W'L+rf%"!6>T _eU0.rػ!嬼ig*6dC Eb4h=_v6YPڤRDE"hsjn+v&qʦi=%ly\>/Uy׷VR9k-,26~ʯ 롵 ~7CKZz'Z \⺇x]Yj% B6'IAI/x*$H>}]UuYpo7Y#zb$)}~aqD+ቑ\R[Q6bĖkC78ݓ;Z&^BU~dA#1&T[eǷ?heN4`L\:c8 4l*ׅ/ aKCL4.'-.!?b,nYY{4p 夠T{2}TvZH={O##뼝3I2# B|Ůr"ed$J|\UbhA0[4jU-H@M}䱇(PX@3DC$dh@W_]^-6|<<3𑢋u䋔I@V+R+'O =17tV)';[C v~DuL]o& 6kANP$'J_YK9V>D7jOʳk0L2~bmdE9)FBqQ.Q_<ѐ{N:2jugvO7Y!HI{re=g^\ƋW*0u]e>,vQbhA0ۓm>ۣmn*s/^>8糉_B,$ue~xQn 1| ovf(/R&a r#+'O0|κ( VtEy0I3 O;o:sams OW]EYMjU"<6UHx5-( >$dh@_yeCUcc`(jBïLV舑'qfcI[WUy4$ igqu/M;'uH!@%mEL|]Q~e~e[*.1a= ~yU%3lrC 4w̯¯ʒȑI FP@bj7AmoVX&蛣i*AÞjs-څi|SbhAp?XiV=BTG=x$ELI2'֫fX@?+}@33HZ|<"@V~eBGG=08F<}*6d c]8cv֮hr {#4 YY E'@C&q#nVl0(>gUv -)zX [S~ ^VMݓvώV{3Ə8D),ߒ.5ɿLLDi@WH0chA0ۓm>ۣmn*sW#xPM4LIQ \s}K~K03݀S&a071v јx Vcg;ahA&cz;6|CP$.!&jE^"3@#Muzg )*0u]*]da'|GT6\LyO J"ȥ$*0|N'Tk> x;{*ey+ߺ<:b#+/N }?ہ'Cg ieMcz;6޲h (?CTWQki8,j:e#t.lVD)t]? (@ c2]Y*e81.#F(fq]7]c*Wfm$(>1$ G;wRnAZ($o(In)+ a2;tGC[ŦC$x38bڙ7bܕ%#hgzCj6ҋ(FUeZ27Ua=ms+/;+umCM]Ylz3 `3>dNC>K-L_pS>*Ub|E/U@JϽYF/ h͠_1I@V+U e uG[W0H A":~i9ٴP, P D[@'h IDISvoSV.= ($ag 5L; $CiGXy+95@!NPt]U}GZ;0Y%1^BwrYǯ9*q>7)MP<](ry_n3`P{_1&(={Vvx}|-4z%" 3 :)cץLQv oKc^ײ:~U 1܈GT;+{v-e8*ANPwGvUV ͪτ TTSYسG\3mlֆ@4 gxPW>?x;pMmhB$BC.dZDu!̷+PA)i{vRRY'tuS.[ d9`jϦ~pϰ D}K|9hhhl>qUBቑ2`zg[0 b=T{A>Eq>c4m*0%M=lжPoN"T?ql7ۃ_V&Ɩl,ꇾRۧSNZ0FtG+Ӫw`LKШUS*` h*%"ŜիI6)kH_N|f/lV51v - mv]v9IeofJ$@ + =LhF # 28>k<|>.0q2 S'Q0T& 7Gd> 6}7CL4mZ&ĄqVKh]LIA3Ve-*o,FuF }d#q&R;33/vMËc.U&a$J|ˤudam`jգ-H`M|dÊGbOrme$<RQ( [bhWL_:}@33HZ|IĤ*e1.r vYUl<1ƶ504qƠOv6TꕸJ;6^ v" E'@eC^V׬D IfqcG]~K 夠1f~~\YuOlcP={yU3I2@>+.yQsǃI:Ҁ[rɪ3 lOl6G*'8M4LI,12[8c'Zi1|3 o۴x n僕IaïLfq јy VM&1$<\۹DG>mE4 CjZ$Ǧ*Yz2X5!ghZX54CZ mV D>+2 *~w%FVN` c0:vJUpe^s@b vcҴs/9uTBPǘ $,)1vU\b= {+(YirC Tv۹헾* vXX7: h??U%s~ Q4Ui=%TY} d1?0ĵ07&(={VvXU >'D =L(y Nve{[O-G[v4K'v}Kχ"'v{bV{fL^f`2;VKIt'V~bhA0ۓm>ۣmn*s3-('S鳉Ix: POQsA؝ > x񉝽L4~e9(=1?Sڭ+ON-&1$#{s@Vql啢o;sUYjfGЬ7R>A$gLI?2'vCUccT+K`U _~Y9{a8vXUpe^_#@b vwEҴs/XŘ:*h (КA2/(0$>K I\`%vؔ޾}$#wy=" Vr7G;A|Aǂ>Ux{U~D{cUc:4GT9P{?&..`|1Ɗ{ֆ img=+P,`o? "@$a!|hV&amW~޴#1Na!bG[Ŧb8lF<1j[[V{#( cY2UYk47rl,?*PQMU]_mbee+2['`4^ޮ:pP9H"\4s?0¯9,=TyiܵL5 !Z<_&(ÎPW}cy9#,Z4,T@'J(<1r<-.]P$Kf;Lph7T6-j@E0-+4dހf MxI1N-]\+CpPEsjYuzzZ8iޡZXB\RhF<8J-tDSMWZtvO]O=}d GnRu @EmXtTh *EB5CU s667 ]|O,yXTH9t.猱P` D*ի:~eBGG=0r uq|#ϦbhA,Yv>4!U ޴h (w8`mm* g#tPŘKIx:Ȁ2ot& hk*e81.#F(f'#ƭ+fXqU`!0蓧}7m4lK~P@駘dJRU$ y+ Q0z DnTM9ف1ƘIZkglĨ3aSDLR7bRAȪxֶ w9@0!$ NG܌6}R,`l/`&|/%]C3ܼi)?Y;ɿLI쓸 ԬhPƘY|>EIxRk.e|$ QƃqݍJVsb41$<v9]L̞x{raS4'(vk*_ IR ]P7o_ICBTu⚡[ H E"G[X@ghI,v3x> l6ZԬ>iGk顲HIH0s<1mk7ՊFfUgD̞70mLѷyw:q€qm@ }agz<(`>АEYv~W@ |{d&% s lY>DgH³P\i*@Jvjvw[f0'S&a1|a.eQz1/ :~U1وGT;UZ;wweH{#( juvS95<GfUe_j* {Vh?:}M 墻Z@4 g߾юOaFD ic9,ȲSIɇ"NǨ5D!`_,&(+=nb*5;>!"sBWL܋hu{tvO E"Ag{[Z' b?v;p>g&U > DئR dayo=lL/ =r{tvm'U à~:d S 9Y6w$F0 13F1bMPOFסXdž[ *okqs)`zg۰0 b/1ס+"6=64os)m,I8R dB|TjnfLsE7zg5-1)LaaOOѸ1!ޖN ֳ=EPbL_KHd *>kF0桽G+feOW]ՈcOm@׶D雚QRT {_IbLLIhĘx)̞,l̞u!gUdvdݩϻV j+V{Y?$IZ&n{Z'.LXETj rRs%'lkk2 g?ð fU~43j#i сqVWʤqxbd0xyV)T'G{Ԭ<ջHÄ lP> D*xiI 61歁ɪ3 @R|]_01v~Mc.-D㖣:`m6IA3V%-5aGtF*]o؆V{fFGy;?g e3a>yk–I:Ҁ2NZ MV1H~b˵CjK`T#%H8X5!g LIF>fՎ۬fJP~Oa 2ˣ#Ʈ<03cea&cʼ2b!0蓧?m4l+رguFP@ǘW5<ޣIXSb_W%F0/ ?+ڭbSNv}{`F<1j̯?%#h'(LUP*ބZ?*A2q@0Þj x^n.v8` ?u t6Ay}VI\Gm~Vk+ Mo>6ƪixk(Ts ;=P={Yr_H1ˏ4#u'vAg^~b/G+0u]eT牕_:c9dhL1铉|>h(J%L]e(4L@>g ^ `Iˎ;$,0W~6{b'k vOU`o-Y^b 1Q hoK,XwP@}RuO p~SA/F*$ v}K~heNR*'Sd`X 0)mV~ SCb ]G qzQH_6bY%0H&g(sSxS@F?7M`\0l"`x ? y`瀬:#iH-aUVgEkynGbPeUb|E$<S(?.)^Z mV16EuoB(Hb)8W%Ʈ<0ye,`VW^_#Đ$"~)yic @-7 h I/hXըI=L}VaG&1VrjM9g Ir<1bdxȊhgEZ߫zK-ڼv bY%0H8&Cs4MUaOG[]+vu1Vس6TY]"LFMggga? "@$a!l|hV&amWA(;hІnr!"v1j[[Ve8*AP /dz ެʱL@5ר 6oY2u2ŠP{H_FUgΞ jyP*"} !r2 1&1HX(kJ8jc(L[Srt m#LYf* MPz+ĒTkJ;~*4,T@U'Z(CbpLJ qG,A1gv^g>KܷѢfoW\YӲBk*gJVݰ;&$Xso*fCkmO tdUƳ7]mؑ~|f(-Tkkugv4q55GL0 t6HaE+'S'QU拄65-&$p==20lOY2ŽDCQ$' h./n $L0Wb?^uls|#φbhAMD]4\d"{˭D[@ls6gim*`Pe#lϦiE2 W'1M+2oٰSf ^25||Ʈ>V&a 2刑=1"H_k J+"CG vNP9M휀Hl[6)>B01 Lz2-S$;~OΞG 201ad"wdUlo+0LҊasV!3'FP@3ޠz<v9& *AVMu4G){&(vsi 1yH_yxx !YuFi0[7RDģjP T&$BBYPo }'ȭïLVNe DZڭ+?sb41$<ƪ\E;'<+1uTBP$?$]N$);a= _q=EcOSUlo>cLƆz;ɽEIbZ7.TD{{1$M9*񰧀ڣ={mIp%ۖ*oECg蓇&(=Pc(MPܵL5  j&(=PH eIO_0چY % PՉkz>P@b(Q=28#F3Y@eѢfo]Ysm碲HIH"`y>-xI\wkwnHQSUaG-@?RFHyAnXW~ U^RchJ`pv#EjrXՀS&pXï[YSVyvE_1&(={VvXޗDgHLCN{$^2F;R쵊{"x>xD.mgLvG6ANPӍqY, ͪτ9 `ILY"mvGN2`\F#zP{H_FfggΞ j~9~WV4>'к*W%Docv0 Έ>'r8(x> OY 6^ % {y=lp +O|ƛ_a1$_&|c)a TzbSNvT1z?Pg#a N+*%g PhP3 oGgCY@FۙU{l,3 TTS!^~NtS9qa*Vrrj-`4^ޮ:`P9Y !r2-"`z: 6A)ivRRS=ӧ?,3b&(3=މ@y9#.,,;WBn H E"1g{T"FL<3U͍sߛh!ӷ? "@$aY4^ % {.fy.у/ =r{rvmt(8Lbx{ڣRKS k;CK〙JCg~(DJUϜ\Q CI?"V#hbZ(CbpL%VVaa }&X"WDmdhPRY pL+jMHɄTj nfLsE7zg5-1)_ '7F0aPlO@}tɦUqm=+fO_\H[vA^'T{ĘL60z0{elv,㩣0%e-&Q~S3bM['ޭ3jx'+ߦ?K j?ilesBn4wOFV{fL;͖G&ʼasBŨ 3^( +լRvCUg 8ٞlmӦ#=L:v25}>h(J~%e85+|nb1fw8}dڝL4~e}P\f{b*&VNVahAmlsh΅M&<>P@!\RuO Vˏ4- (liZҀ2﷛ K 3٪Fʷ0IM_5F (fcba&c3ζl# }bH v|mm+g[ O_?$3/Ș]a$,)17XMXV֋hSﰌV)';=P0I=XknT3D%# h'zCjE_h]o FU%L{+P*񰧀>[&(vsa,jZD;ܘJ!3NgS'L *NtPwȽ>i7[2 TL=0yXqtiUpe^M†@b igq]/M;'Rt,i "a^300efAk=ta ~?u6*bSNvxiM#a1MTr*ANP|l:3"欹YRD֎1cVI ES8SBv?gu{{O6 +pvg n Y 0I}7)ޖ~e8g}e4tgUlɎ*FÆ|n#μ,@gC{#( jfq Y5jEۛU96  `Oq+u6oylOS}Fl Hxyl3AM7s X"@$BC彅ۊ@ |eNS@iky~-`үT:;Bٞʒg"5D|Έ PPU&eC!HDBzv }&X˙N67*5k}z"NS-^ %"f-xv-s Y%{XU{L hx:e~M.1QyZeNTR5a mV nUQ3_BUa ]Շ QL2ꒊVTiQ;cCbp"fkhdW-ZaPhK(fEue$);-꒪AG&YfQ&hr۷Uw)0HD󨚩ig󵌊n&ANP| *-^Ub32*&v FUc:4GT9P{y6f-ҢjϦ'o7MP{v=x}l4\Ixx{^Ѳ`uޞxhSUbag Kܷ=E#A,I8+MWx7o6 sb>ovj^L2bH 2 LĊzhO%JؔU1YjiEB FP@3Y,ͪLTnffOb;}ӭrrj*V0&(=!| @@|)\ɴ1CdYީ@"IQ-S=&!C 〙jCg&a6Z!">'čey*:j*ΎP/a*9ۥ28#FL<3U͍sz7gh!ӷ!"@$aY4^ %JlF.%zo mTm Ƒ$ G+=ڎ(iiTB.`Z"`lҏQiEy9!͋ܟdZ(<1r Y f @ FLGBz{Y\F}KiCDgH"z{L|77MxID&F`s=n}(=H(AB={-Lj>±6ln=%T6SnD?lӱ6h|}m`E YA5/yFI V1O{8xUgH6 : vZXPEYi hs(J`L\Ghh)R( Ɨin2k4n:{7T '3 :)N.e;1o߾%MuUݓb9sKĴs/u;0VH vXxV+m;86ŝ05T@hkO V%컌Vyg0{fPs6A왠gZW"•o_K4c8p}=6g'ssmQe]0ݩ@耀IlRsX %R_M {CDy} -T@&j( 1(ƜRoaa}&_P]כ*(jO,WlS)R7`av =lp{$BF oKI NOTs;b&hgvV,Bc FP@/hkͪ{2'='}{uݭl>;ʩ5Hxyl3Cm D7"r)'"ǨsX t:r'A)騈VSR{{n @~?;v{_;azwęEN ݸpsrPĐP$xtG~2 F[^߷9CooY ZR?7Gd&[kCu:6{x. biHbp9 +͙=LBv@)MxJW̱\[ cfPv6AG!S*31b\BCBTPxb9H0Mzmh 69%C6҆DBo/ q<4ͦOay0"5`vٳ0`8#F/aS@e=zDL;$a LIB'f\{уX\d|#F,`_:Zmod+WWOWm2>q]n;))~G  ($LI^q$.ecr*aY}B7KRNvY5z[jg=Z;y :NJub^*`mgV1ՠ5=ں_}w: e;tsjiUg̞pk=,#> D!4RNELQ"dNJk{ݓ)-e+@~?;;ۼxD~?DC\[[NG.A!HD6lz 3}⹜PgusM@V,IVhzMHހ}>J 06tm7g Y΢AU1p՚doޡ `5nf9`q#8) V{>N!"sFYD>+4m0>[lrx82>[6^`64os)m,I@Q dB\y*e)!&z$"i]J$Dp:cBQADv0aP{Jm";Md(e{ `3z\H*Ǔ!賄%De\W_(l>ns hD'6US3Ռ1 2$)`zg۰0 ވ>,O,'ۜ^" J\)'QzpD7Ddb@$/r;ala܈!/Hə?y36w{z^GtgG٠bik?}]Uns}7;ݬʩ#zb$5}~|cq]8<1Kqm< QW.u9-bG=LjvlG2( 1I2wX̗;OX-Zh *'h9b_KCU fM *`jku; 0c0n9Wq`KЄF*'Xx7U"N=P={O##뼝3ID6yk6[F+'S'QU+glTɪ3 À'?ɀ#z ,z+}c|h(X +'[bhW>$-XrKʿL3_֮J]9yaqzۭbSNv0[Cg v^n":sah'(QɠP*aZݬ] m(ay.a%rRF |2cKԡʼF'vP={yi|$$AʼEaW=>?Ӣ&8LI|PdUa'|GTW#' 9}PK]e~u&9c,,,%欬KpOpY>Xe&1ʯ/K<01,4[W[nb04q iC\d"{D[@ѧ/8`zUZ)H@3@#MU>ZTNN`Մ@2 O'WjvCUcc`(X5W&+tytd FǮl +f@M 1 O; ~i9=uTBP$?$|{=Z=_֮KA{;D_@~b̵[ŦC$xDN;O쨷{#( /{bJ7mcY%0H&g(sSxS@~Dk3M\;~5`Xj(MP{v=xU >'D =L(y Np;/Xgk]ce)m[찢HeBln 3vOv3ve;|du?2 Qt'ϼx`/G+0u}eCUg 8ٞlml?fZQO g E)pu.1> xq_P&a 2 \_B9ybY,1n\Ynb04q imjth&{>P@!j*T44~G~^cvOϦiEK Zʼ a1Af>>c[*IXïcF䉩QDw,:FUpe^]m$Z 1 O;wҴsWYm$uFP@}1&Y2vF2 {J 2 VVq"ee+(YirC T"GCڹ헾WވbX h'z#j"/w1Y%1^:SgJs)곴&(vsa,꾈9H_≀9xxҳgj7AuM NCmRbUVgEkynGBHe~vŪF!>cheN)W/-6|ª7_1I@V+cWN@<,c8n\WW0->yYK&bUc-H@k!L2CshefgiuzQ0IOXɩ*6do1p&kgψl="+Vr7;|,2߫zK-ڼv bY%0Hx&#s4MUaOG[mBcرgmgM# `3nNk*Ii$Zb;~)ޖ~eo`(ymQoV)';=b(vGJ;oe7G{Y@VmFh;jgB*칉X6u}mܷy̆ٮ 6ֆS:vg߄(`D iրm|1q0A2߮@YS41P{#FiZZyE_ 3&(=P֦}3o">gądohJ*W-9n>lg+^Y|>#FL<gF}v=gL )Y7`7A-x$h68F3}F$'2UjÎ UT3i*ݓviYHI/M>\ذSI:Ҁ2_$ai14Yu s667a{"ydžo3v5Q2 O'H_'iF|N/˟bv%kݿZـS&a07C1v h, Xk\FM181jηE4hzl&ݶ>mEll{4Ǐ6v^2gô"NXvvʧ0a|rfL ]}L#_#{b(Eјxn\yZw;̉C`_v6lY?7B"acXd=嗰l:` ݄Q0 #+ށݨnr# Qi''+v\ݽ 50QoDVw]NhbY%0H&g(sSxS@F?7 R0`4cx n l}3 `3>dNCٺgL_I>XJA<:YF^ 8嗰Tt7NfFCM 2b'VN科[g*~M}*_e+xf2' holuI/(jh (LrrS&afOI= ܨTac4can[Ųe3Iv_ vsJN{($ jcBwcc*F5(sSxS@倁&(XN""`W)UހMϦvj7CGiA|$4w{/(+ o߿s'v Fa7g[Ŧb8lF<ڹONV欺{#( e"=u>j=E"SCODH?VY Y W&$0氚L_VNr(R YݡdUpe{rI& Ҵsi:ư "o02 3{Na꒪Ad3JBV= IR/@͍糀f{cA|+)Y0<av =lp +O|ƛ_eĐ~ߣZHI c$hL'h!ݷaz%")S?L&>}=J%064%V :kz.}s&1HL&h;~,aW̲\ cfPs1j. WON4gNЩj MV1Hf{g{Mea3tka>M4LItyܬ*򹍡WkН>̼mE~uc-M4?;|dLŮe#-|L+”I:eZ'dXXR`1ffL l*),J 2 \v1'/E4f /VyevnS4C`𴳸헦M|l*h (ꠑIxb6Z S&abOWu#F \D#(ynr# Ta:Qigމ6+K"AP Fd>xFwEV TGs[;Pgta+_0n. izf0 <0oP{v=X9 Έ8 fvM3}&Yo_ZMU#OS'L +,ZMAm =  Ӛ\fi)j$1dY qOf< +OӶ!}tm4lKcI FP@ 9M)0$Zac4ԧtnr۷MnjgZ;ѽ>M{d9,hik FUg:2GT9P{y6 טK `|ӴCg蓇&(=P3d:qMPч'"C [Z' b/)YBeѢfoW\YsmHIH?|f&'),f[Km黔y1]ٱBW~ SQSk@W7x- m*j>s0etI7Z< S]覺dUpe,ʡ&1@b vid_,APǙDT~AgG=̲3}򌒦Bno6Wi^=&NVeѭ$#($ jcBecAw FU%LUMUaO}%Kx^)-F[ OY p,ךJi/~o6 sb>ovj)S ,#+τMX2]Vir#!=kgpZQkg?x$½ 5vxV) h{*2ӯ@E5U{ Wۼn5S/VS9}F˩ Hxyl3A7s X"@$BC.dZDu!,TRT$ͳ=ӧ?,3b&(3ml3"/ Ж B!>1$ QT"FL=SXp>'ޜv!gH²Bn*,7 /=lLBà-˱s@# I =4Ga9W6+P{?P xu3 g0< D @J&Dr֐ᦉo6a^0"5܌,س0`8#F37`ms)ٞu=#Dg鲦 Lɗm pgXRS62TۈظXeBz%7V50篞D;wU=ڎeG??yNG7;ӊǴꀍjEYS?U#u+A W&$vRnj mV1r GƤ_3I V+ʅƮ<0 x|> :*ˀuØ ub0Hۙ ۦPHP$&a;~O%*px{v01TM9  al1v! FP@3"z-/m!m? FU}:6GT9P{Q_ }&[ t{M 0&(=PАAMV0#I EJ8jc(L[Srt ݝv>`_J,ii7j|fqZ |1 ݾBTu⪅'z {i0ɠ3H]67*mGQ}-4z%" BqShM7nv =lp{$BF wtL= - /1h|_RgGT;{٦;'+t iý} 1nmEۛU96~CSdo?|iED\tS%'/oWMP8{&6S*"r)'"ǨsXymZ='] CHX韝MPfۣMq=3@G %`h 7O!W $"ۘ]-, 3br*|NPM@V,ID-]b)Y0ܼo0ї[d&[kCwBSB,q!A!r9 +͹{~JӸ#xJW̱\[ cfch;#F T+ D ?ACBTPxb9H0Ƒ@%x :mnk>W1}Ji͖mD L/JmM=la;PC(=H(A l=K #F1g_:Æ S2o+A4U`JDʞ{у^X\d|yGYK Îz0=_a?hW5XG;nKmǿ %: ~~!864N*LI~ecr*adpnZŦ@j16a<]mg=j;^ݻ {($ :N hc sPS=W}ޡ/ С=`/L# g|ol5OXjC"r)'"ǨsXa^ JIcEJ˵{暀eݩ/8CD}% PǸr\:;C@m.1g<nn<ޛpo-4>}6^ % {;Af `bmܶA4d9Uŀ9ayaD'_"rY69n! -#Kއxnns>fC6҆D@JfčEpD7Ddb8{yg r-`=,-h ous)>`)[񾏮l"N 2> Xqq@H+= ?meQG-I tg R:<,wE`14$ =i3^Hm_vehp0  2 NCWB,=AW|7.~|[xu7FrJ<9[n/he?|d=GZBylz3gӫa$LDi@W/M 14Yu s6671铉|>h(J%L%d]ed=v1| /rc%T S&aI FVN`YxS9*2=7ahAC3+΅M&<7}( >}5l{4FSYVCTl*)t922d=0cl,ٴ 9L%LU#W&+tyT1'F13nm~- 1$ ĵem{J=VIL fPMA0Sba8={Vv9 Έ$p=>[fG̣jYG)t]e޸4Zb40Pgp_P&J@<)gO b0 OӽVy٬i$ igqwj/M;gx(VcB"a^30i7[KFNaEad d*9UZŦmxgH6VT7f!kQo?Ssc*A3qi{ =ڞG ׵>M[*PgV 0&(=P<®t;oPHLCp'YL~A%_y{s'v nRV)';=b(vGZ;֖½ ˰2[xV{ZfUgB*^ Tѵ›`S->` SO4^ޮ:`POV,S }d 8r aoW)Y{J=ڎ0ힺ{^06NЛj3&(=P"1>L5$6+i(YN\3ԴP/a*-628#FL<3mn<T6-j@E0-+4o*{~o6 <1lӖfhHߣU > D!4dQX=`b&0Hx#s4)Y{ =ڞ0-u-O `үCՀ |=gaG(qǪ,i|]zCD猸 RDocvhi1g/67*)jO,Ix+MHހfy[f?yJ뙖q$ #ïLc?LIX'ÒVUlɎ*Fclòٲ`Μvz{($ -RPvgri *D̞b^tzAT.SkP{/M# f}h獯"}$BhȥLa{ ;5,NJnZ=yf9€iHB&`~ү8v>{ ,h [:;C@m.1g+ & ޜ^򆈀>}6^ % {q^Fl%z/ T@0Ɓ$}{lΘJsDamӌ6Al)!_0rm=`A+`lR%,|j,Gv %`a1dj(<1rY f @ Fyq\nns>k6 ͥ!"@$9 dF3xiǛM"21{xLrz K j,7`usy}QnE=lӾ6j| ޖYN yF6 )AF%K,ezDXlޤ9 mʪh;^5ReGY ?!(v oj;y%ΞU(.yFI 5;}3Ij;&?lIFPH"z-&s?*5(sSxS@eRh;IW2M,!i$1$ Qoi *>'Uus,vaDgH±P\k*@Jolv=lp{=)aԩ\Z_gGX;L+&NVeK#( jfh+ h{*?och|}gTrjd\tS%TSNFUgΞ y>U > D!4RNELQ"N<%=ZJG+wg=@1`ZleEzX"sF\XD-DnB H E"1g{Sk#FL'čcĸ *ok qO;@2x#FLO};(WA}KiCDgH"rZHɄ}K7MxID&ݿcC>L"H 7=,-h_- fO l0m#p *0%_b'V.k/ # <qvH,=7nj_a6_=_wU#ڎe{mWL#3l3Cm D7"r)'"ǨsX g<%=R2b6Ɵ CHX韝MPV{N} <: uv܅\U > DAu ^ % {}~Af `amٶ- !YCsp켮04)_01`q3we1"%*V#F ( o{67<]0x]A`y"PnӜ{A}KiCDgH"^ %SaiǛM"x],w}AL"HD 7],, 1Έ>3KzX;8{ lv@z`zna4U`J"g.l"u "鳀} &aBzXaoXlB@ghﺔf tq?y3 2JFDƨL+繶4'Ii@d^8,U2`q8Lf?V9!N|]p4,T@)6{biUPnaHhI,bvP>kn' X"@O_MHɄw7Oᦍo6a>1I_~LFψ$oќ"3h3=#^8fO`8 ~)% {cK'׵8H,8G ƍUM ̞x$GwRO">_d6AJ(9`3&'>t ۱Mz,qo8H 5yN6y+.O'eJ{ᆷO5lĈ%S*~7v_`6o:ޭ8\ޭh_d6_tL|乀m}!f<;AQ2싢}M' EpD'%?Wp&Y/%5} %/v(y.QouTKI>\` 8Oۣxy;snf| GOt$.d~r?PL$ĐDC*-{'`q9}NACЇ?l3Ü>g6W fCD>a鿿AC7&Ro-]gSqÍk̞6g=o 7eWfDd'`??=?pg=dY|]~&4`|Fߠ 33gȪ3{~ÞL "_î5'Ѿ0:DG|5vWzʺ=U'`?Ϛ= ؇~'gY|#-23b|fg+j~Þ==?dyʹO?ϾY,EO>??ߞ4ON/37D a{~=?dY_XDb3 tdg{MSpsٟY'#g~f|ƈ=?a{~C7eD8$l)g{LI3Y>|MS2?IP=WCuO?'=?@G|f4O7}2ffz{o/0'۟_7?s燆isgDJywص>~vm>kzR과OW=?a{~C >ٟJD?KԿD~^Tc?9\fg3kOD3Y?$hxo~|N>곾inP?۟ ~ >fI'7~ě}Q}|{uYog?44YߎHɫַ?3Lagv#yٟgL3o5eg3bՀ}F~(`_.^"2HiOS op_1j|ܗ')PK›Hk[[ _ dwaLookups.hUT+RWux PKR[ openimageio-1.7.17~dfsg0.orig/src/build-scripts/OpenEXR-IlmImf-CMakeLists.txt0000644000175000017500000001331613151711064025075 0ustar mfvmfv# yue.nicholas@gmail.com SET(CMAKE_INCLUDE_CURRENT_DIR 1) if (NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/b44ExpLogTable.h") ADD_EXECUTABLE ( b44ExpLogTable b44ExpLogTable.cpp ) TARGET_LINK_LIBRARIES ( b44ExpLogTable Half Iex${ILMBASE_LIBSUFFIX} IlmThread${ILMBASE_LIBSUFFIX} ${PTHREAD_LIB} ) ADD_CUSTOM_COMMAND ( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/b44ExpLogTable.h COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/b44ExpLogTable > ${CMAKE_CURRENT_BINARY_DIR}/b44ExpLogTable.h DEPENDS b44ExpLogTable ) else () message (STATUS "Skipping build of b44ExpLogTable.h") endif () if (NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/dwaLookups.h") ADD_EXECUTABLE ( dwaLookups dwaLookups.cpp ) TARGET_LINK_LIBRARIES ( dwaLookups Half Iex${ILMBASE_LIBSUFFIX} IlmThread${ILMBASE_LIBSUFFIX} ${PTHREAD_LIB} ) ADD_CUSTOM_COMMAND ( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/dwaLookups.h COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/dwaLookups > ${CMAKE_CURRENT_BINARY_DIR}/dwaLookups.h DEPENDS dwaLookups ) else () message (STATUS "Skipping build of dwaLookups.h") endif () SET ( ILMIMF_SRCS ImfAttribute.cpp ImfBoxAttribute.cpp ImfCRgbaFile.cpp ImfChannelList.cpp ImfChannelListAttribute.cpp ImfFloatAttribute.cpp ImfFrameBuffer.cpp ImfHeader.cpp ImfIO.cpp ImfInputFile.cpp ImfIntAttribute.cpp ImfLineOrderAttribute.cpp ImfMatrixAttribute.cpp ImfOpaqueAttribute.cpp ImfOutputFile.cpp ImfRgbaFile.cpp ImfStringAttribute.cpp ImfVecAttribute.cpp ImfHuf.cpp ImfThreading.cpp ImfWav.cpp ImfLut.cpp ImfCompressor.cpp ImfRleCompressor.cpp ImfZipCompressor.cpp ImfPizCompressor.cpp ImfB44Compressor.cpp ImfDwaCompressor.cpp ImfMisc.cpp ImfCompressionAttribute.cpp ImfDoubleAttribute.cpp ImfConvert.cpp ImfPreviewImage.cpp ImfPreviewImageAttribute.cpp ImfVersion.cpp ImfChromaticities.cpp ImfChromaticitiesAttribute.cpp ImfKeyCode.cpp ImfKeyCodeAttribute.cpp ImfTimeCode.cpp ImfTimeCodeAttribute.cpp ImfRational.cpp ImfRationalAttribute.cpp ImfFramesPerSecond.cpp ImfStandardAttributes.cpp ImfStdIO.cpp ImfEnvmap.cpp ImfEnvmapAttribute.cpp ImfScanLineInputFile.cpp ImfTiledInputFile.cpp ImfTiledMisc.cpp ImfTiledOutputFile.cpp ImfTiledRgbaFile.cpp ImfTileDescriptionAttribute.cpp ImfTileOffsets.cpp ImfRgbaYca.cpp ImfPxr24Compressor.cpp ImfTestFile.cpp ImfStringVectorAttribute.cpp ImfMultiView.cpp ImfAcesFile.cpp ImfMultiPartOutputFile.cpp ImfGenericOutputFile.cpp ImfOutputPartData.cpp ImfMultiPartInputFile.cpp ImfGenericInputFile.cpp ImfPartType.cpp ImfInputPartData.cpp ImfOutputPart.cpp ImfTiledOutputPart.cpp ImfInputPart.cpp ImfTiledInputPart.cpp ImfDeepScanLineInputPart.cpp ImfDeepScanLineOutputPart.cpp ImfDeepScanLineInputFile.cpp ImfDeepScanLineOutputFile.cpp ImfDeepTiledInputPart.cpp ImfDeepTiledOutputPart.cpp ImfDeepTiledInputFile.cpp ImfDeepTiledOutputFile.cpp ImfDeepFrameBuffer.cpp ImfDeepCompositing.cpp ImfCompositeDeepScanLine.cpp ImfDeepImageStateAttribute.cpp ImfFastHuf.cpp ImfFloatVectorAttribute.cpp ImfRle.cpp ImfSystemSpecific.cpp ImfZip.cpp ) IF (BUILD_SHARED_LIBS) ADD_DEFINITIONS(-DILMIMF_EXPORTS) ENDIF() ADD_LIBRARY ( IlmImf ${LIB_TYPE} ${ILMIMF_SRCS} ) TARGET_LINK_LIBRARIES ( IlmImf Half Iex${ILMBASE_LIBSUFFIX} Imath${ILMBASE_LIBSUFFIX} IlmThread${ILMBASE_LIBSUFFIX} ${PTHREAD_LIB} ${ZLIB_LIBRARIES} ) ADD_DEPENDENCIES ( IlmImf b44ExpLogTable ) SET_SOURCE_FILES_PROPERTIES ( ImfB44Compressor.cpp PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/b44ExpLogTable.h ) ADD_DEPENDENCIES ( IlmImf dwaLookups ) SET_SOURCE_FILES_PROPERTIES ( ImfDwaCompressor.cpp PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/dwaLookups.h ) # Libraries INSTALL ( TARGETS IlmImf DESTINATION ${CMAKE_INSTALL_PREFIX}/lib ) # Headers INSTALL ( FILES ImfForward.h ImfExport.h ImfAttribute.h ImfBoxAttribute.h ImfCRgbaFile.h ImfChannelList.h ImfChannelListAttribute.h ImfCompressionAttribute.h ImfDoubleAttribute.h ImfFloatAttribute.h ImfFrameBuffer.h ImfHeader.h ImfIO.h ImfInputFile.h ImfIntAttribute.h ImfLineOrderAttribute.h ImfMatrixAttribute.h ImfOpaqueAttribute.h ImfOutputFile.h ImfRgbaFile.h ImfStringAttribute.h ImfVecAttribute.h ImfHuf.h ImfWav.h ImfLut.h ImfArray.h ImfCompression.h ImfLineOrder.h ImfName.h ImfPixelType.h ImfVersion.h ImfXdr.h ImfConvert.h ImfPreviewImage.h ImfPreviewImageAttribute.h ImfChromaticities.h ImfChromaticitiesAttribute.h ImfKeyCode.h ImfKeyCodeAttribute.h ImfTimeCode.h ImfTimeCodeAttribute.h ImfRational.h ImfRationalAttribute.h ImfFramesPerSecond.h ImfStandardAttributes.h ImfEnvmap.h ImfEnvmapAttribute.h ImfInt64.h ImfRgba.h ImfTileDescription.h ImfTileDescriptionAttribute.h ImfTiledInputFile.h ImfTiledOutputFile.h ImfTiledRgbaFile.h ImfRgbaYca.h ImfTestFile.h ImfThreading.h ImfB44Compressor.h ImfStringVectorAttribute.h ImfMultiView.h ImfAcesFile.h ImfMultiPartOutputFile.h ImfGenericOutputFile.h ImfMultiPartInputFile.h ImfGenericInputFile.h ImfPartType.h ImfPartHelper.h ImfOutputPart.h ImfTiledOutputPart.h ImfInputPart.h ImfTiledInputPart.h ImfDeepScanLineOutputFile.h ImfDeepScanLineOutputPart.h ImfDeepScanLineInputFile.h ImfDeepScanLineInputPart.h ImfDeepTiledInputFile.h ImfDeepTiledInputPart.h ImfDeepTiledOutputFile.h ImfDeepTiledOutputPart.h ImfDeepFrameBuffer.h ImfDeepCompositing.h ImfCompositeDeepScanLine.h ImfNamespace.h ImfMisc.h ImfDeepImageState.h ImfDeepImageStateAttribute.h ImfFloatVectorAttribute.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/OpenEXR ) openimageio-1.7.17~dfsg0.orig/src/build-scripts/install_test_images.bash0000755000175000017500000000060013151711064024570 0ustar mfvmfv#!/bin/bash if [ ! -e ../oiio-images ] ; then git clone https://github.com/OpenImageIO/oiio-images.git ../oiio-images fi if [ ! -e ../libtiffpic ] ; then wget ftp://ftp.remotesensing.org/pub/libtiff/pics-3.8.0.tar.gz tar xf pics-3.8.0.tar.gz -C .. fi if [ ! -e ../openexr-images ] ; then git clone https://github.com/openexr/openexr-images.git ../openexr-images fi openimageio-1.7.17~dfsg0.orig/src/build-scripts/b44ExpLogTable.h.zip0000644000175000017500000021537213151711064023344 0ustar mfvmfvPKHbD@b44ExpLogTable.hUT !RW#RWux ` S0ERHIK|M5%P$C]x&kIƚ"ySx&sKs&kH)/~l?תIU(.J5$["e%%%]SɏLt$u?_ "«3KbM)?$)_V+I9Era?\H>џIN|Fÿ_A2 E2օWgL5E*M_A\H>џI~N|̒h H:'|JOg"ySx&I8H.gt)~g%wk%H^QR/+$u$_I{5̒H^I2S$V3J)/~YB$P$[]xBItm>%W?3HT$/^"uU~.I5E*Kr"e?.)Ʌ?)~t.>Hq-!\8|%P$s]xK"ySx%k"e?.) +,I:O~Ԗ?. z ?_{I'IW^XS$o $)_V^S$\R>Hq--\4|%cM)뒩MɅK)/~\Kh{8m?_cIv5wWy]/+qI8H.|]}N|ZB $HR]xKrM)ҦH~Y{I"Gsߨ<|%P$S]x%s"e?.Y) +_S$_Fk~-)N腟ķ)_VpN\X"ǵ6~/IB*Kr"e?.)ɅK)/~|n5BuU^Lm䗕)~ԖЌ~?.mqY璭H^婤w|J|]?Hq-K:H*Kb"e%:EoER«\ɛ«lKc9EB1u?qd r)7/Ic) u0|NP}L]) 3&HR]H)J9Er,S$ ww0|E(.|˵H޸S$]S\?H+?O3FHb]H0)J"9)noqP$S]sM)|ҦHZ+ɱpΧdg B-"ySئH:EX?H>3B u 6EZi:Hup֟ϸ EՅ8Oɻ6Er, )scpN?טkM K"iԟS$ 77<\\S$o _)J9EX}ΧdF\5"yS®MVca"F"I ߲)7/ɱpN<NR߀H-ך"ySxvN WwO 1 kjM c"i)9E vP$}]CM)qC8P$.|aצHZ+s0}NX")u 6EZi8H 9igHK"9)3z}xsH0)caw"s5ErN  eBu 6EZi>H QuέHj򕛷|JB"ឹN(.16"y, KXS$\\HCV)!pM`䱰k06);RS$CP$pjp)JŵM(oPr cMpV*P$}CD858 g& I5Q7]H8sE(¾A}vo.kQqmpLg®XS$"caР>;LN 5E٨6 EBuS\5}RS$CP$Ss:*`vpmp{~i}o0 gbl0 EX, ꘍Ác¹H8eE+ N(`jPw$yUXkTS$KP${r `נn2xQJ }CMp+N Bp+ nB7j)y. JWфҠy;L848 GɹH8%E1S`'ca<05"y,, ˞}^ة95/{޽. ~~^ț.5ea`)RS$ÁcMp+ .Bp+n~aፅN(x`j0 EXX"y,"y,\\܄"a!Ok)y. ڞ ρkum#݅5uSׅ5uóxǘ5Ur dͮ%`ђ¾QKFN .B< nժyB`806B<H gH 7S3  b<0 b9)cSMp`\\|9 yP,O݁A}`yr こP$sˁP$+O 5cMp@+KMp`}|Ҡ> q{Ok`ʸ}(ca:07X"Vkr> W2^SXWkvFH Ӂ" pXS$'e(]Q22j^;0 EX H8ǚ">97,kޓp>%υ( b> A]22]s9pp{%c5)a[5Q|`iPG \QIE 7 96zxyeyexeXn2"2+#B6ܕAܕqڕqUO^]]w]b]Rz(uetete4T22йi+B6˕˕1uڄO +c+c+É+È+ÆBaxpe8pepeO2 t+#x+u+sBqqU BVVEbHFVFVFVV=fejeje`je jeieieliLVƔVƐVƎVVVu>% 쬌䬌ܬ )fdfe0feeeee|ee&c@}L<ė9~8pc|r*S8OӁY(08i ".nZ±܁p`$Ǣt|`}r8pQ}F`8;0j "9 E¾?988 E¾$q R8;NI(r`  $uU8i{ev9>?LY_}Á̾MIe_dK9}~}.Ӂ󁋖e=}݁^sLھgr BOrz>=>σ?0 }ҁr>xt ;S>-ˁ]'ؕ}]5]ǕSN{-Vqn@ :vcx ٽMױ {t pcy:B \{:Ӂ-),xg-YqFv~0n~_{Bvp~+dJ~ay2n&-awd -^+`wd ٭ ݡ*dw:;t]=]VOUnuzwz^ݡW*ԻCoT ;t:jcS$?8udd8lGQyuT^;ǎꨴE HvDcUGeQYuTVݪ%uuTRTG%vu::*ʩrTNH%u"`抌zEHM+d "af XE*Hth1REH\j02#QdZDQW1ZFXR%EPFH 9#!gd8(RF=[#ukd'RFBH] Izv Ft"#:4RGBUW#sd&RAG*ȠLjf[@c#wij <l+Z=FjHyjw"|$qH1Z¿LSÑ ;*D~[cHpi*"C&#dD5Yl-`#1GG)ϱU#K}/đ:2{}3.#NdH$DFڡ8˞p;(ESH#ST/LexIъpP1GZHi)bCH#"gQ_ht"i#iV#j)]\e%FH 2 D:Qw"O"mp DH!&M_""ȠL D꨾Ȱ׊1`'DhmtN*] pK͉69 ɉ8'D[x%7&DshnSPIM61ahf$DhVjYM44 hFhM4f4E;43l&DIIh6eL42B&ZȔ<&D˘h-bEL-pPSh -a%L00%:I/%ZPKE-^Kx.yJ4nqݡKpNTQKtZQKhcG,ј%D#hĒnЈ%Dq% ?IV⅞DhU]ĘR$D#h=`]JKIe_QJtݴD{hPRߑ_ۖhOۖhONb)$ڝD{O7\Zu`I3n]ItMK/l$ڗDhWR&%Ѥ${I}hSK{eLy!ӄd=} 2MG4#ӝLeLSi*2MEU=gi"2MD4XuLӐi2MBi 2A5LSaezV{H9gL)Sg:MZ?SgG9{'SgjLmy蜩3|Ӣ%3L-3LŞL힩3zVte2z+>=FmhϽ -zBAޞۣ':=DϽso=Em5pO=QBO% {^G=QAOTz.>=~\dlO3Ӎi{4 P4==Aw?1LbOs=P?Тi{S=u3p=mpύ{n6{ٞyUfswgzҞ洧9Q{^wUzڞ泧i6{FzWe?i{^e w@K8-< |w]^]@<01p CCrnws{j600į@:0f0p >ħc3 /t=cgZ@T9M&æq$jGFo=ȍȽ1҉ Gna-H5 F#H6r9H4f#e?_~{y9 7#aH5Čuߑe8Ү\#Mȕ=rezRHoG0l龍>tF` y 3FFj+wG}Ŏ\#WHhvF=#G~ 1QOk&(LTWD=Upk"̚'̉^D=夒,D9)Mt&: gO\D;OԵu7:v"'։n⢛&bI8LO ɉjsPgN\dD59jLTM\\hD(1O pN\Ox 6QMt'zfRL?S \d3Q6Bzgəqbuxʜ gBjs&柉gY_3L?xʝgJf֙z&gb&ĘUL1j\31g;fěT3m1S'Ϻ_<<3LE=`;L<2SqK31=5.Kfj|Fg*{m/< g┙pgbY3j3L?sc3Ch3S: 3Ce3wL2!x&🹛g^­#q|d=)#ۑYG2sˑG[q\kW|8A7z#׍|7i㢲G>koF>j#ѧOF@gۚbFhaLFc䫍.Fa\t6 @x`什OE^yqѝk>S\#qE>ZiͳϑE(X{^OqEozJ<"_|)'E=|P @DoyJx4l"|S&Yȗ]"gR"<|$ȷC"|#E>.FSD>4VDq/ G"=| ȇ"/{G2GGGRGGU$G{$z$cz$y$_y$x${x\uޑܑtڑؑ$בd֑q t$t$et$;t$s$s%t~JIɽɱIIɈW=KHFHHrHHH H IIIɹ7=t̶ $qC$$䏍䉍~du^F$[k$k$j$j$wj$Gj$jk5/d(d"$$ NCHHjHHvHH͸鋍ę\̔qSdd$$ytʌIIɶɦII}V"a"ga"-a"`"`"`"Y`r^K<#H<ܟ%=/%//.....NK:$-؂؍{̟nyڞnDv#-lAb Wvrjz"qY"QY"Y"X"X"X"3Xrj*_"W"W"W"IW"W"V"V))ٮY 91)HHH֔zpH”HȧțHHrOYى9i-S2sgϜ/l)^)^܅S +[lʿƖncئ,6VITRPyOMW}K,I$IdIdI$I$ II^9\4t$$)Qnq"""u"y5!="-""!!!!1<%e~ HD2D_p yn<#n8d} `'?8;[H:?"{@"k@"[Nu  y!&'&'&v@q0?|E"W''''Ͻt4'''ϧ@KL g'f'f'f,pl ̞ĞBd?ѓwoݣaJyb*ySSwr, +0+|'jwbNV=+ӄ&p'=q{Gȁ`.vbv \zubNV \LNLn/zw6sa7ݙٜ:эļTlĬ#ԩ>`b"nbm?;麳( Qaj_O7~LIBӎ[ٙ S s#s 33STY SSRT)ssRTϋ33RTA]y\;|f]%fW%&PT=R$bbSbRb:NxDŽB@;"N-wStRTANLILI̞I̒ILILxIiIID\tĴ̑ -L}Ĥ7ˉ5n¿Tгh ;4C gk9=נhdO@j 2m"WY8+#H%5n ꍄz/#5 6rFޑPu^VK2rDL#qej+s=t|fFH:ǎJGQؑ9'b'X{L3!<ٜʙ|&柉*gyջC%333 pE=`M }Sj.4 B̿p,A_9q tYb{L B%p BЅ{ikB̿P)-*Ep gg!_V5p- '/Jj[ Vz.YV«2JrO4V}o'>y|'wN|9eǐ1N|8WM|7yrM|6>9p`ko&>jciS^M|4U:L|3˴M&c⫍0& bt|—L|0iK|/mz%8h\{oOepZ} Ifϐe$(X^iۇ{tFUsS/De>^SK٩'יe>`yPK@dǓye>M5od>Kc-٩R3M|$H2|$%|S2͞c2sLvwAY1tC!٫QLZLfLrL^LjLvLի홴뙌除٫{';;V;;:::{):g:؝-<ܔ^e3gY4F9.99{3y_^9rWR; T6p6g#2Y|3z3 ywPm 9t3rsHS66u$t6\6i[0EPln{V9pɝɑISU>p`{9}zTv/w?33s'2adgr`DK{Uǒir'"G;ق-e;~,䀑e1Mq'`c9drfrfff33-)Sr$J4/z/]/G52ɖɊ#W, 29v 2i2ivFn;1-w-Z-B-%--,=%?fl6=2-S`^ٝ)A\nHiࣚIIv=ߙ#UN곘RQ;2=e2:|,Wu,I2ɖv4yv UL֢LvlV3(壺NH uf2d [ӔɋIqIeV9Tm1d2 c29a2_2]2i\v޷$W&J&/r{sS2G=)]d'RnwIv~Hn>_wvgdf!b( [EHLʁS `f?= 5 - ,l; YO g&g&Xgd ̜`f~in9UG03w5=333we>4=﹟lIYOfLb>Ӡ?OKGzzfyfyfyhZRJSJg gxhЙzwf^Mȏb~&rdTA!!/qb~`dz4kc@4^ LLztqEvvL^LRC+DuvW?"G }3z3wf&9y\]fjfjcKyg>i87दdzftffm>':Q̥̓#@f9f3ff,ff&f&YB⏙f_f_f*_ 1f`ifB s9q{ŏufZWfefe&e&eEe[ŻZ)Yya7]@>.T B!1)3(f=%T B[߇+J&)753Y20 /y%Y{uLVd䕋#{p?Lɫԑ7F4̬-aCfBC8 9s1\̴ ›wwo5C}}+W .^x8U^L/^xǼ.yur o~J-])[W+vwSANޫ E(|ڠUS‡ 9 i I )KT Y wޏUTR|rtm2fx.KR@ܯRH?8r1(2"I ) {dդݩ2זtK ycwBBՒĒujɁn{\HYn92%H'|G)YձD\CBFBRKܣRcBkw{nr2{8V 2a z`;hhy?dIy/$67T]'*=.=GρgիOjْ{N@OӫY3eew޷U;[:pj~)' T!S!S!iSݹ2y{G~ D/xa2xjw!d[Ǟ{V2Ge9lH3QN2q'IMto-ӽ{V{4Y&-& Li_^SJ&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$U&1ILb$&1ILb$&1ILb$&1ILb$&Gt}w4ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb|,s>$_$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&GS$_$&1ILb$&1ILb$&1ILb$L|N|c$&1ILb$&1ILb?dN|#$&1ILb$&dN|#$&1ILbO%u$&1ILb|+) +$&1ILb$}"e%$&1IH9Erae$|N\X$&1IR"e%$&92ɛ«4IL'jMU$&ǕHB]x&1IL|M$&yi%V)yWx&kI֚"ySx&kJH*MהBuU-щ kHoh I%?6ɟ[RS$$-I>H.l$)_V2?$)_V2?)7Wi$E«4_[_X4?4ɲ}Χ$?3I?3J?3Ks%ئH~Y(/+%}M)?ɛ«+HP$.ʿD.k$zp?_/+a%s$:Ew"Gbɼ)_I)_V+K"ySx,\^_A2 E2ՅWWQyY //?HIRS$,ɅJԦH~Y,5E* N(.ʿDZke%V|9EŏiO7?>)~t]\H>""m䗕~M_ɛ«^R")uU~/B*DS\)|ͿI?nK?>?,|J>",m䗕~M_2ɛ«^2 E2օW%:>%:>ww| I"mI"x"GKs\/+$M_jMU~-6S~?.ɰ|Ϳ8%?H2}N|sx"GKs\ҷ)_VKJM)%Y(\^$I Kt|2ğw| I""GK9Era?,6VK֚"ySxKH*D'5o$?HdN|%9Era?.)m䗕\S$o {I$ՅW%:>׼./+0ɛ·5EW:H\]&|JѲ|Gk&֒sp"9)J"i)S"ec"ev5EWHB]+P$.D])5EB% k9EX8N ɱpNV)6EJ76EJ/5EW&HR]+P$.D])kJ9Er(Lu>%Uz"iS$g+m䗕n/+p)7rd e/I_^R"$}N<)caw"i)}"e]"e%͸|JU(.D]);J)?_S$T:߰S$:Er,S$9ErrnS$TS$o e$օ /QWJ1|MP|Z>HvOIs"iS$g+m䗕^8ɛ_9 E2օ+n"|N<)caNV]"e"yS+P$.D])uk>%J֒sp"9)J9ErئH~YCM)P$}]xR k֒9Er,)J9ErrhS$B_S$o ՅWu䷟)*oXK)c|"i4S$g+m䗕^8ɛ_ Eׅ+%_~Ha-IS$x"iԝS$g+6EJ/5EW:H\]xNWJnk"y,\N8ɱpH]=sҦH SM ;HpNp%6}m'\k>$urY(.q:HƟ9)H"I;C"y,"qvb[ן\).SN5EX8^ P؟P_l|aܦHNkX؝0E9Eho<E RjslS$\\kXXN"u""kkS$"P$PQ'IڦH 5Er,.R_lR9S$\d,5Er,'LBxNp5cH' $lS_lksiS$ENBL"F66EXXNkX.R_ldjkS$ᄾHufLNmmsM #i| )KM E4.m6EX؝0ɱ_G+ɨOszP$ˡpH]=ͱM< Eׅ7ꑉm6EX/ o  W)]M *|J. W)"9G&ٷ)rY(|(LGf ٵ)p^(_0kM :SڜcxP$áHݠ3׭ܦH EFHb]xcwNp%ɱ]52͵M<.9 E2ׅ7N W)"9X&>ca(I o)S"9kԓF&)"gH"%&Æ%n A)N;04 ^1USoJ '-~^984kIr`n0iIyen͵S£ۚsP$cCRԚA])Tj_7"a!/k JǔO kN J'¡H8 sI(`נn2x' O kKMp*N B< &d`)BŮ coн~\5XJ6 g(c`ߠ>;< SMp6*v H }=uG/µH8GH A07jQk0EXt <^ .5EhrjP"T &H c]At5 \OsSMpV* B<sAO EX BG\k>%<9PU"T, fH cMpv*P$=yQSp\"y,g5P$Ԡ>;<v' B<[jz.B< N Z2r8a/cai07$gl04S)y.\N8 p(,5E)N$k0 IzszE(¹A}3DzF{F|@.Bό ]8lϸ}ghU E>?T3J*5C=C@3),fH'(-fOE eπdϘcp͌eBR![Lh[`*A P:*PY!S[#:g?*j@1~ T^+b@,x25Y lTbaR8PjR L-0<@5[f f ߁;0v5\ DAO3 "@D5] GW::Z@`%t:ZB'n;EG":=2:ZF_s419ZHG 9=]\::GNet:=1>h-s 99Gt 뽋SwV.jG ;#UM>YX`vtVd'e.ތ_;YOl&Ŷl7ޝV.g傴;ԣVάŷfVH3%Kp%+GVlidz*BX"׎;A18V\k`l`ʞ=gsrDH#H=zw6 {Yg F*葘,zwF"o;H4R*F>zS灯>ߵ|]:C|8 0׀ |7yr |6>5p\` |F5%Lè^|W4i= |c3.#O ?ߋ |1L |0ܾ\Qw |/a7 F |q.l |m-pSeg_ |,]'_ b=%\|*w |)ɥpZSe |>(E| C>HL0/>5!7O_- |$ g?_CwPb>e:EoE>n_fxJHBO>p@[aP2RŇ^䐳=f=9=<% pnHHȁuHkH_Ht@@F@@r@@@*-R3R0,)&#r2rr2 2 {$ $ d E߀d d $ $ E$ d d $} $w q E= c ] X P dI dC $< E$0 $, "    d EM3 d d $ $ EFIHvnH\HTAn2)HHH^F S R R R 5R R Qi~ kQ ;Q Q P P wP -P4?t>=<;d;<:!닍877T6543!zwm0&&%%%%d , +$*d)|(''!:T%$#"D"!\ )%G MG G F F F:!)F E E E EE E DexJHHHJ#HHȘȐ91"nII B"^!A A oNuv? y B"'A @H':@ @ @ @Hrh%  $1ؿq'X=73?$b~=>0>$*bχD̟!L 4ڃ6уwo8SSSɃB:2*" 彳D'n&l&j^g.v`v`NU2:0:0:D%QOM=]:0M:0:09090y90Iy'A؞}_Tlum7pS;aj`j`j`i`>i`hĻQ?eg`Jg`f`vf`f`e`.eNKθ; C&& oSvwz;U,Dl!P.BK:ffDNw`SYSSBP'vc0)#EO υ@I?y=;):!u)7!qdL`L`"L`K`NK\t-9!jR5J%L\MAL3^ LL鿓:{d;^x=NoB %rͼJx{Wޣ*x9s-ʁ/-N% +s{o݉Te\x6l}+W[oT5o: Xx4h];D@ qx2N-2GP'=t;ЇS`[@|pbGGI^IfO!N+`S39ݝjG/`;ӝ׭v;'`gNIG}'[قtlA? ~ sL^I~;V5k㾹+sx3?鰳(ɽRk=~́[*;hGH JR +sʙ-JXO]́\;c7[z}L8J_3~7;x,.Jf]ԝHf$\ xQ3?z/v"G~@/^Iz{_ 3[4P(/\KdydzMbŽ,p<=˽Ǿ:|w~NU^̜o|&Y`${z'Rnzdrf5aS#3gm=?ޝ9sf|̱Hl1gqVϑ!e9t枚;JzgnS;msy_e ff:6q 8z~Z[=$Bni9{}(O#7utvx?5ttd U*O6wN)['~ƚ=N݉bjw{|G>{̿~THs9R)#?29f㽳D͚_*Kx4+wT?X92ر\cT7cx twvr B@{bC;x}KA 8/L 'ÈD΁s=2Wгc\CV@1%'s*K: 9 ga ˩c_ nȺ{>=|zg=A| b߫)䞯{>63/z>r^}j)Wχ[=\|>SGLO 7|mMg2=_磓Ez>z烋o'z>o_2|JhI?<3=z':ϗ<|SMχ|QCoy>C`zy>} )UMy{MO/{[Dy<˧q<_|b<_z|ϧO<_/|-@<_Y_|T} ϧ-<_|hg|V/OzxOx>>g;d$d$Y*IɘIz[I=zIIɈIrIf%Iމ; TDŽ;O#iwE%N isIJxRT )==}w$IPɡɕ~JUB-N@0n>\HM6VOUOb՝H'Q=85/qDF$$ Q=dd$O \~OJOfʝiғQғ4ғғғѓG6ٓѓѓeѓMѓ0ѓѓwj:2:nSǒaГIp}wI9$$yI;vcBI;O;QiΓn;Vy2DNq:BcBME޵om~][bfDe;1;i|R gpy*#typymd=i TBd 8aPnq@1PO?O 1=1ɓ߲=$d-+sI4%9W?J:OƞDf<:ޫ|8;)dSoƓf'UEO"Đœe' }DŽ +$*d)|('&!!U'%''''Q'xJIIɼɬIz;^xZ[AzlީW=#Y`wDzvEeqxbW;^ w|e~Mnێ:> 0㳘R&:^}vxk|o9Dz4`k.de\T ˵h:އuxtK]>(r|f = xKo8>Jz,8>c{J|8؆c4 +pQ=p|Ǘ\G\7w}\w%ݽW H8>,:]t'8>t}xБߑ;2d"[B-Dw{ )v'E`eT=wS5$w$w>h; ,PmIwQ 8abr/6^Ut/w+wb8^YrxPsC.pMF50@;ԃx#W#7N-v ]{.Pڑz>;Ԏ|Ԏ!GiGiգ邐Bڑ2ڑ1^s !˳;,-{I?!)+N66)wO,))X6s}#cf'zwdH^3Cv72zKh=N ;E~aD1|DŽ[#EuNxO1N%NvDΫ{{' PG^G^ǰ*c i'?TFucp8΋;j}r:G ;)葓&1PHx;ttQ,G\G\Ǩ#NdHwxՑ֑#í#sqJL;Hoxi֑֑֑1w/IkS@;2_&c#I[Ǹsь#S\m:iSO:2:GW䴎{#Ntdu: Ii#s twj"9&c`{tgix1&VˎLQ 9S\81dudu~:GH;^HllF+ejs H"[HdMf;E2娠l)6/g*V&cWQf?3 >=vt0wPS88E}0 ӁNKWvb#&x@])|^p`_S$'%(5Á^(BDU<. KWE868 Gb9 jXS$ CMpvtm>%bmmp9\S$鄣P$c]xpNp]\fԅ7s0}H}A_g'IW0)J"i)JʧtM "ySxd+֒sƚ"ySxmp"Gm 7;Kɾ$_H>g)/~gtS$_$0ɛ«4D~oh4"quU$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&1ILb$&9O_y]ۿ?_o?_c|҇Ʋ99Kݙ;Sr_wNB~歈$ze= w3)t lF%0ԳR?B$YIBd +"HsR$EG%MbwH$1g-G~qwvtc:$IǖwzK[ؒp, -zgl޼Ȗl̖̓P$lfRs%X![:s6I6w@Nl*7= SͧMS<͟^KGvc\d7YP ٝq8ݡQ$Γgg<^9{7B}0 ぃP$@vsHF-ap?Plo.Ss ^K84|`:PcB/ "yXSR8&uB|",fH ӁH8pO+A}ܦ<586lr`056s"k] Ek+U878 G`/cai07mal)BEB}vya\k>%ܠ>;qyPS$}Mp*>ƟWv' B<OI)lrmS$ʊ )Ws<>t P$PO"IxN(PN"B;&|J.B,u uO:aL;Mp}4ca:a)caw I o'Ip?q|J .5Er,/R_l~c"y,Nɱ\|M<k6 m_ՅN~S\^"R8S$EBu 6EXH;s"9 evBtu"y,R_\adsp?8ɛmX8\HX")u["ySxc:H:E|Ǡ%3zH|]BצH+W>%Uz"y,\>H.sd r)7o9ɛmR9H:EX>H  w7 ww1 ?Wy2yS^*ZE(.P$s]+'H-ǚ"ySCM)P$}]+P$.Y(\$I e$օN+QO|J2o) Ik"i4S$g+Om䗕^8ɛ_9E2ԅW)*oXKɱ0_HZ+s7ƚ"yS+;H*EBM "i) +;yoOɻK5oB}Z|NV) +8ɛK#o}ŶdNܷ)_VzMXKʩM[FHb]x"9[9)_VlKux3Oɻ« u7.Y/+J]QWlK?qd -S$Wɛ«%srM)Z>H(ɛ«%ɧ?B*"G/M~橄WSres M7\ɛ«lK?znwK6EJK)O EׅWy]R/+}.ɟS$_Fx1 +~.5E*ے:Eo E«\jMUJe+++_S$_}%P$s]xK"ySx%c"e?.) +.S$\R>Hq-mg m73o+qnD7̕\tf~EэA(P^e-S$WjMUV2|ħ֚"ySx"K"ySxd"H^e->H>q/I_^e[RS$腺 W.Im䗕Kh*璮H^e[S$腺!W\jMU6% ħP;d9K"ySx"~/"«./+}/S$\R>H$?W\jMU^6EJK?:)/~\Kh&fs?_k y!NS~.YkMU~.YjMU^m䗕~MɅ Ʌ?)caN uXS$x"iԝS$9EroS$tkS$Iv>% r)7rd /QW$"&"d"9)K"e"yS+P$.D])h+֒sXS$g+6EJ/t5E uHXK)J9Era"ySxf#W%Ik"}M)D]=c+%:ErrjS$+uHvؖt)C"e_XKŶ]H^ ;Q_CґKM)q>H8 E2օ/ca!k5Er,L);HM E:-q?H>% o\)C˞jM9EX8|^Kk,5E|N<$\cWS$o o ɱ_HE _ڦHZ+-c!Cn"mX_H ˇ鐯3 E6EZ;H  7e:H\]!~Sk"9.)scjM 6EZS$|"y,LS$pS߀d I ɛ6EXܹ) c9E]1u3Jz"_9E2ԅoɛ6EZ)S$9Er,)caw"y, S$ܭP$CꄫYX?I9]L뜅"_9 E2Յo9ɛ·jM[5E-KM)|anS$tcjS$tclS$tcצH~YЦH~YFߦH~YFצH$ls>%z gHCtQ(P8\wHXP_NX).&}"y\ -KM<'jX8pd oOXh OM<v' 5Er,'tBIʣ6uDj66EtʩH H"j2)tQ(x(.R_l$iӷ)Bwdt7/6δ9)"GHƺ" H/sd _8)C˞i5Er,L)."9EX?DI)yWxzNp_,\pjS$:EeP$}]ҦH:E½pQ(XkS$p"y,R;Df()yWxzN<.S$#9 E2Յ/Ik"y,?wvY(\0)J"y,>Hw. E·t5E$_TupbuNBLu[5EC"9)9E1 ȔBĺ-"ySЦH:EX>HXܦ0s9K9 E2ׅo9ɛmpN ɱ\H  w7 ww1t3E_"u["yS)7/|HpmS$tҦHZ+Ik"9)cp"9)9EX?H ca"y,>H"y_)aW |K"mrjS$xj䱰?a)|TS$]M<BCL z׋ Soj' Sj I}"y,,'5EX.2 EEꚍ ,59Ed[>%υ "9'"sɾM<"95 )}M 5)5ssM caE(Rޘ)Sƚ"9vGcFz W)"ySxtNper)ca" =e)caH}J9"9 W)]M yn?RS$o o)."m䱰6s"9 )"ySxc8H ԗ~~%SR).˜"NmX8^HOɛ9EX?&pQ(XkS$p"^L' 5sOIU^H'>wpΩH8S$:E½rE(Re)77s0~Np\f$ԅokM"92C꘍9 E2ׅ/Ik"9)CIי"u["ySئHZ+uɱ0\H> z_ɛ.m|NV)cx"y,>H+?rY(\$I 2ɛvm䗕n m?H:Er(dj%>%υc9E] E1]<ϩb|NeRuHJ]+P$.I(Te)7oɛ· 5E-}M)|KWS$o ߑF/|JZS$o r)77' :Ӑj'w1ӌj'7 3j{B5 e䟚"lҵ0_͵Mp]r)cM BrfvLtB]2ͮMpcF=WeEJ'5sɱM<'kXX.R_l).Sv5EX. E9Ys)"gH"b;PS$"u3D" %υMssd _8)cx"a"9KM)1S$t"^a9CM)џS$BwO S(>nwgqP$K]¹MV)scIY")u s"i)caN<vS$p"u["ySɌ|J+ɱpN<ΟS$܀S߀EA(.P$}]H0)J"9ɱN<)o(!T\"Y_Eԅo9ɛ·jM 6EJ7m䗕nIkrNVIktN uX]H:Er,)B9EBf}ħp"y,\>H )cM<'k䱰0ca:a)HN(Z<6WS޸\NSs:NrSs8HN,m䔩H "9zH|]xzgJk"9\S$EB"uĴ"b:e)xP$]]xc8ؘѦkS$,d9WS޸\ؘBԦH Nj"ꋍYm6EX.2 Es+𔾦H66EX|H}2ͩM<9E2ԅ7 WefHr]xc:HBOɛ9E•zJWS$o +bE(.q>H "s䱰6s"95ϟ)77s2P$.|J /{^?\S$o o)"H}"9 e&HR]ئHu{2P$.|kS$B\Sœϩs)77Nc!D"6Er,)!lsv5EM u}F ϔ\"Y.m|N<NS$P9E2ԅoɛ6EZ)S$sc" u }"i)B̐K|Hd9E|YK8 E2Յo9ɛmRN uXS$s$|NK" uB-]M)d[>%tڦHZ+-Ik"9N)cx"y,>H )o(⏩b&|Ψ%;ꪀI?SWLN/ ՅBN$zYxP$k]+HWBu[N5E-ǚ"ySxpB}3f9n^T3P߀ٝP; jӷ) {)aROK"b:TS$ "y,/ERI(TO+jf  OM,m5p9,\8PWJ=ishS$\D,5EX/2 E ƌ6C"y,'t5Er(d^9|9EuʩHEꋍmm픹H"u366EX. E9s)crzdz@S"y,?byÿ;M<BtN<Ƌ"9EzN(W;yW>%~P$K]x|N<N9 E2օ/ca!uTm6Er,L+j^?gWS$p"rL' _+C˞9ɱpNpy_ PoS$r"r$I oca!eϴs"ySx;HLħ9׹Eԅ/ɱpNpO\ Pe_S$o o,caa"uFHb]®M uH}d.^ڦHZ+-c9E uP$c]CM)|aߦH:EX?H.3 E·jM C"i)B9f|N}2C:H-"yS©MV)cp"y,?H?rI(T(I ߲)7/ m?H:Er(dV%>%υ w7 ww1Sj>jsP$C]+{H-KM)|\S$o _/+/+ص)J"i)J"i_TuX\HuX8]HuX8\H "y,'L5EXOcaH/?$ ',q="r(/R_lߩ9P_'˩ٟS$\'M6EudlS$ CM P$-#[j. OS"y,O8ɱ?aԅ7Ji#m6EX؝0ɱП Eti( urQ(=ˁYK+ӁN(po Xq=p9p֒Ӂcp ׾r`0"@VWpmp9Peytؠ> v,fH ӁN(dEߠ>%3zRS$'ǚ">7Xv?L W/p\vZ~^9784rh?hIyen0 GɮPS$'{[ͧdhW\k]qlp䱰?4"y,L e?rРoP_y5/ayTS$CP$`065/!7j>%=G"ct(c`ߠ`)BŮ coнgѕR~^6 g(cp¾H8+sI(`נ>;%7j>%Rqmpd9 N5E٩84 EXXmS16 EXmSvd\OsܦH8eH "UL FH⡰k0 IQWqyzE(¹Mp* B`)N]A}^(B;Sq\7`7r:(cp¾H8Mu إ vBtР)Bak) ʊ )+B<'k䱰4 b<>šyР {S\p)NmS"WNO޳4"ɇt(I<v "y,'t5EB58{s"WO-7gBn$I:vBtpB/?\yh‡D'\jp>TS$ "y,OXj00I]" ?ӷ)u )p9p>pSp`ߠ>;<[y%LAʳ.?нR?ZyֵlK8 ϶Ve 1Y0BpL )pmp9pÁpx U3"@=B<WIX0$ c5'HCPS${P$P5EРJ +)y.\\dyϩQ(}Mp*&-I? v5EX :-q?oxU7. 5E©krlP߀ ,BCan0calkS$ʊ/煌߷ sMp*ԧ'5P$PL5EXO Iߠ>ųyK?O1& gH 5EX84ط)NmA}yR3Pb zHN,,\Oc|©H8MmdiS$:NAK{P$Am§D/q9,|(N8 E2 "y,,'5EXNk䱰;a)BBWS$,Wkͧp9\S$ӁÁ`lၘ0gb Ͼ@><[x%\\DŽg] ϶ÁlkYV|`cdw`8 E8&UE<N&S \S$'݁A(ԓ=y$7 4 u&p``U1ԑO }NH4o@-< Bp, à@®  I<ꋍ> wsP$Áby0B<v}M<M@]fLQ(" `$3.jN5E2p`_S$=󁩦H{v"?^ɓ>%; CMp6*d`|֣p=RS$SP$C}0- "y, v 7j>% `\O¹H8;v1jai0 gؠ }M<dx^.'"y, b` sH8eMv l? p\|J9P߀ "y,,mSX1P CM<]O 5:q{|I(CMp*,mSZ1PbH ]M 56;ԜOO1&j B E(|TS$&6E)n7Oqr?o6)p9,|(N8 EX8)r\S$&c"7 罢:C umC uU#xA(P؟ERI(t('"A_S$wOsz¥H N5EX8p)r`>Pb<vA+y%\g^3ϺӁkٖ4"< BpLt䱕p>%< NvU3@}Lx<58J"@=t/x4'MO&(M1J>1*>1>1=1=1=1v=w''ƨ'Ƥ'Ƣ''EKL'F'F'F'{*ԝ NՉԉS![Ȩ(GA'F9H2','&'"'=81 91818181l81\(LZf27171'd ` emb\mblblүqN M M MnMf ^ LM D 91M%M&UXYn$F ś{q텿dsb#6AH0lRb(1,J $FvDԎ$Fj1"_(.qɝO &PHbCHHv"1J!r @$D.+H 6i"Odɦ='2D-r$ɮHIs"N俩6ٔ66g"L$=cI a%96k,y|$S)ٵR")J="_4L"eIv(T$m|$)\ħ!$b}'|O>ϖx" 9|"D}$6v"9|"DD}SBD4'dWMDʉH9>h\^I~qh['Mı"GnD*rī"_ t31L.RuGfb^:?3HuE[bzj㋶) e-HS-R-bپi& Hf`&1Tafn$ bj )!{%#" ֙,b&njTDbf UT;2f'2f qhCzdqdꑅPWRdƚhGRe&ETϦ*3fRꑲQ:rJl2R=Rig%#$H37␑j[33L#RM% fqhX!qH5ɇD{ƒ$G"$I( u}:W4 iSFioCdT38d$S]l&CFIr%6Lk&iɴīqsFK%ڟtlNH JB{F'23iD&^a8L3YIƘ1dla#JΉkE|J&r%bw@*+ڦv"]FN܆یMƋ]k%'#H8.Vav#avHD=I!H$=4H<1D#HP< p#ȑĹc2WHp+rĵ#H+vVī"GJ:2FR% AGBO~#H)rD#h]5RD#H(r}i$:9\‘@p$K7z"GJl7؍t#1H 'rr}i$ F#1۩|#az"Gl F>& Dpc$9l@P4HT86H`8D#QH8%|ܦ G;Ek$jGF/qhǑq$z9|ȑ(R !m'*E{b啄#D 5Es$9r;kf$9|BP/Pt/KGBёPt$ IG;d$>OG>!hՑ8Uc rvJH*rį" m;ǎı#qϊ'9J#H;2/R #AH5~GE)T`x$AX#hb"dj$T`y#H,>1,`"!x (f.w v܁v {Ʀ؁8v ~_ׁPuī@h:`CЁt 9B΁s . @90:\įC8=D]e ZÁ΁p @48 ~@708 vn bn v j1@6 ca`W®k ¬j©pj r"Hi@4! DF@04 vn@3 9A@3 3Coړ`f b▁e NS '@<2 j !`7Bb )!B(a J F@?@?聁}#:1zw:ꁎzo>x`licJ}@_;t@w:Н6́ns.zEk[ zoکmg:=XO'3Ӊtb=#<=YOg3uzFpz:Nl=#6=#6=[O38==\O3(tn=1=\oazzz]zngtv}9KOx=*==_xJOgG[{zzz`o{g{zzz[t==aOO ]2gȣgG{:zzĞς it=zzƞg{;j3jMt===dOCNSϨDOw] @ @==eo_ sgL`6{n&}xg`>{͞g젷IF{ўgF{ۍt=c=iOwӍ=i٫ܿ;I{՞n;{{oI{՞D=kOӽA>{מ̼f{xO_=IwO7ۓtt=}nOrt=mo={{ޞɗ{<=yqO=}pO ۾/{➾'{=)mO'=}rOܓ={R֞o{枾hOݓd=}tOG{mttvGݑuv}uG_+laԛl;:#+軻 yGсwtiGGޑvvLGGёwtwW::ClѡwU;:c;RG*q;:#lJw-;:IGG٧:z޾HGGߑvt~got~Gߑv;:>wG>t@g(# :h#ȏ;dG4 tD4:#*lTtDQAGTq#: ItGTБO;v}nGۑvtjGwg-:F;юJ;ƪ;z>&-v=`GuwtvKulYGgщut^㵝}BGgIu$QG?to:QЎ~#R:Zێt.#UDH:D.!DZHi#|av1ҺGZHki# w46ԑ:0GHi#movQHFik#mlmf4ҌFd$If2LFHi ]g8 FHi"-\l>i"-YEH#iqi"R$U*%2i]4DH*>"YG$4REZHvd"HDho&FZH$ D!hEhHI " A$ErH$H i"-X$֏>҈E{,ҒEZȍH i"a{$\vHi"-Z$@/b`Wf$Ў«@MEK"@jMA ^ '>$z@!FM_-!E \ 2=]32.tZ Łk'pz@jHe#Ɓ>)Z,4p@G8b 7 vAi8s9^K:'m-}qkk{[2>e4%yj^[֮rˮmF[ϖ3mFZ䖱Ŗ좥GlIZ%hN-qKLr-ZK8ҙΫj[Z~ool9[NQ3ioZvuߖ_ixjJZ%mmU-uݕ3n9[Ƕı-7FZZVuo U[QZ;~Ҋħ-wP- ABφQa an.>p7 yip74 gxc׵h8Ć̼!0lh6 {p7 {`72a̺!kΆ5 ZCp7 }iӾom®ܰ!jh2vCkК7 p7M4ANCЪ7DJ RCԐ5 l6順Huoh`2h ~\ |C+7/ KC\̵Ӑ5 Bkrp9\luaBտp9\W.+╋"Y]Ȩ.v]h/\Z >{ڸ\H.. cK☋}M̅͡ A q#9 ͅ5r!+\.6ȹ\r. \ra\Bs2 Zp\HQ.= ]ŅKgC*r!0nr!0^rᚹe\O/\3 # ̅Bq/輐=\:.$ " ΅ C{\v.\; C/\;. m\O._1~==l\՞lPR"6}qoa0Daۣ8#~8 [~~vaFМ'f076r#l9#i4IÑ4Ʌ#p$_-o9q!G6ΰ#Y#YTm33q?>M:Fsq?ǞSRyOZ^zgO-[ysJ#8>c$pٶ?X\l +++R3]:- #ڈ p\.<r-'x{ofOL=īoO <˵OI?;oXN 9ⴘoN7:nM56B״G[GM4>>/L3t5E0&މxcaW%^f(5fK -ڲKJW38Bk/wJr)/IW3jxM37$ޯxI"7w$ī+H!ރ['OuVVO,X=zb:j2SzD+d'N,=]X9%.4MNtWb*)H)fnJ|,BX'6kbr)HZH}&L3g2A&Tcbچe&KX.f=[bĪh5ˆ%J,f3Xc*SbѥG%IyP0Mbm,uQ$Iȑ`H,Xv!Abi 4物މ ۉ։i2o3ILJN#NLM-slb3Z|/♘FNw;n||L*LYe11e/1.1-1]-1,1+1E+kbSbZRbQ#29,i4 &f$&m$PV}!g@:4g𸇕GƕÓۉqZd5.DaMa9u&L=_toNH<x,/p\?ij['RoNu#KNSg礎32>oBS$ۈE ĭ#7c[N$ Q>O6,ٚG}n*f?G♂Y-N2ƠSvˑ2ZLy,\ +4t }uCppPjqQt$Oc(]lD"9|{ηͼ>:Bӡ2fyyhg^<jẙsC9"3sѴ3ӑfyZl`|4s /NWم$=ڍ6Rٖt[1.7\;Ǝ^h8Bc<>FBBh7HuBx<ԲbCxHC|'!>GȣF|==Z==sK3<x/\cqAЛq90 n5 ,$ 6 `9 fx.L`^`a`d vlSd`uI`R{`}1,X"p,X`$OZkV X'P`0 ukT X=/p5@ y5e` a~7bd`0WсEksVC2cԁkLO^xH(^xM:#$_mH3xWUY(Ұ`_x)\ /z[/ 3ީxUl WxrEj ^xx%w^jZeyG|XuB\8C O nn&_$\a$\9 "h>_6Qkh} 1 sa1ZXXcIGq2Ybcb#;?F68dN5q|+_OJ5]ڰrf4#C`yNn-;Lsy#J\7N[ g(FkodLt 7|7؛boFv撿='h Ώ2mp3T87p[k LEnEnm=6vpiXqMl#;Gv~ұ_~f#w"w6R#9b3/=pG2p$G2r$#G2#9#8#8#8̑̑̑$$q$IHGrHɕ#r$WjdHdHdHVdHV{$+Gr$+ ~F##7pyÙpf7f=폑 >i[-rF'"4!C bޘC?<6>C!DNӽto8Nwؘk: |#{y&DWM:_'++i2Ze\3- vC\3-]GKњU?ʇVNq7kh7>v#8>`3"x)y$ёFFgcc qJtK=$EMfl䟙oPWDl4 埶dLfrPdFϴ+L;LCH5igb4-6i|b?ӡG#&Fm_,/CTUV{_YX1?9Ғ1+2k#<&fXdY.\=fpLs 1ζj8?!q?ǁjDhٶ?ϤƋ&Əq?w$#"7i77H$?őL~̌čT;N'&B֜鎑.YFOFyJW{=2y#GrxHW{ĭ1ҭտ.ّ "#7RmhO- 0Ӿfydp+4}MGGY3}#G@ݑgKa3~#G@_W3 ?ڹAq3?r62#oG+;:pV ȑwݝyBdz9Ɩ::r/Hd%yGb#໠ClѡBudم {G,"Dt{G!̲IGj9:$ }@GG1Fz9 >`lH{:X4c#G#gȣE#IXc#Gb:ȑ tD#,# Xe$pBd&}ciFQD,YL%Jd,lu;8劬 Y+&vt,0 <%t6KD,};dˉ]ގy"EV*j%t!;O,DY(vUEB= 'EY)E"+6EVr;:3#8"?y+R-I #;AIݦH5!sJ#?#}H!R 1ꐊ*CA,Roˊ=+V-CA*#С/CA/R;ě"oY̐j :$M" I}QZ-jɅTB:v`Eyϛ!!muKDC'R;%:1␑6zRrD /d#T{c/Lt1: :W"!)@b2fR-1OǰH6żGҐj1'.$dҰER-Gq%"bXQPGBPjދ2k0fzTףF^j8ez xwFPR=DGP<#o5z D1و!##mopHlX>rkTp] SB,ϋ T#übؐ1"^3 @# 1lB䈽( ueG2RMƲ՞&⒑jWK32{ C(JB]w!v$EbH5IDe05n/E [q0s0.cq#2( \3^٘׸G^Y0d$ԙL2'+i,ax͸P'3id?1Å,aHI^:?9fuX0pɸf4 D'6یA?I!h%ԝ\P3Rmrɬx'Dqx{%9iO(RwF1f쬄R2iOv9PN$5"JBݟSlħls"c.=>'Oq8elT; WQTl2SB*ƌ]^1#YhRىV\2mSɉVl2mS@;]>{'^q8g yD,.׌;!/ȋ'aħX2Q's$Jw@y0|&fȍ!FQD3I@F).ALp<p3L+rĹ$0pkgٙ8v&m;ĭ3L*rs?Y $K(:΄b 9gB͙SH*gy!L9=9#G:/p&*b gAA})! Xΐ#%c% Xxu5#GLL793a9ɞz3amS@76L7pc& E`@p2f™pM`8γmcgę(Q]e>%D3QL(rD3lƞgǙQ"tBJTf*E{b΄"m(S L)rOra^yB)r3!ȑ/I+6D$R1dzdbo%1## PGRDJ"y;TE$ԑE2R=R;%OR=Rqh/i#$)h/@D~o>#՛HL T'gIO9^I#3l cFiJ$GH3RMdɒHI52ȤS"yیA|JȤbqhb2+qΘ2Rݫmcɴx{%[?q!#%OL:1#%S5#%3OLH&#%SKvD&vTצohqΘ2ګN\3^IZ`Zɐ1c9HI:?2^3Rm&ôJNXw; CII]II}C9)W+rJֿW>j{xgsV (ɾ;`_R}p$sIIkW)WȀ[>%R) NI8딄ʱNI83WQ~'׌W2r aEl3Q/ɾ!N2B#SF0r? ׌WF6c() _ԓ]> JwqΘDI׌K5Jmɧ1"1f2$|QnjSF2te,IVE=y{%cLb#>%ύmƐ1fAQbq8E3dJ\2oSU&c^j1c%7njSIILړ+Cc=").V2֞q4gDIcƩY/59 ‰W2W^ ]+2FQ}c8/ɩ92 ˓ _, C(Jo2$|sIIB ^3.%%}9)aQl+ Ug_,X~jkF2)^ig^ یbhTvmP8T8oɹTRKƵ$ג0[2TEI]}Ad8V8UhO{FzT5= o%FK6$Ɛ1V؉ i8U8oEd߸f'cW;FMmQb [v%%W(8T8 mĨxp)) F[./ufcSa[aNdW8T8Zʩ¹$=.$KVVx+0]%7 c+DIcSad*Jo\*\딄,'c%)ynlJJOV%¡±$dU&+IVd߸Vx{O;F0p]%iPa,) ?]}d[8\h/@g2!wmlؖX/W8 YXTR}\aS~҂OX-) ?eNII ?1(ɔ\JJO[Χ%ڟ %CQ$f{Q}p$sII) {k/?-ES S= #v$]Vp%(ɾq>`*)ɾz$Pu6%$UvuJ_P$UNuJyPeS΃'kƛp#Ll2( B.c~[%N;^=.qɸf|N%6ی;^3v{Q'njSYkKF=rjo䶔dQnC;Q'cId8gLm;J%ofb4-IیB)q'igJJqΘDI׌KƵ$$6[)yn cƮ$|qO M;b*JZ핫=ٸpd12%% |r8Uh[6,ܒ)y͸//|J#*-}Ad8f*EI ^3.%%|+7ND==یh%]}Ad8f*EI)Ed{+§%ی$|Ov%%}rp*) _쓩kII\+|JG!6%%~2Vؕoɡ±$|猩$|kIIsr'd#>%9(*$Ʈ> Jo+* wĔZmO# J>% l2%%[/+DI}Cgp_2Ӟ|ZRRd~TVh[{XaWR~C(ɾqp: k'}ɠ66_t1Vؕ_P(Jo*+¥$Ƶ{a[l*lKJ¯R0V؉tYa_PR~S(ɾ1ZR~k7Q62]gS)a d0Ĭ/) ?Y6fY+L%%7^+\*1c3^"*T) ?]x@{2-%%7N «(5+\*\KJt2z^^ l+ $!+v%%7S(ɜ %%7.uJ2_(VDIBV+JJo+ꔄʩBsۡd*Jr $grOscSa[$U O O]q<$J2e(ɾz$O `lꔄ߽P S; Sr8mSrp%t(5+\Y3;M-)ɾ10d/)ɾq8XR}t$tkIIגod?17Vn1c:[%Fʽ.qhu-xޖd4ɍ{[7eN{q8LQ}JnS˸q{m)1dw(8dEI3 VƝ&qx{]&f $|QOv+ 8U8E=y͸TZ _\{{%7ĦSM!1dړA7nC( _ؓsT=ٸsVxMmF{q?G(ɾq8fJJdѶlܯqK܅[3dl3>UgW1\1TrJkEd߸f'/|J{">Ug_,wK1TbKrHkEd߸f'%V(*$Ʈ> Jo+* w n!(yж'! o%MmII ;Q}cqp%7NӞ}ZRRW2^§qmAd+*=PXR~sIdxp6J dTO C% (ɾ1VUhš±$ (ɾZR$*o;5X +JJ¯RPu 璒: `kO c%~1j1TKJOVe_   S6fxY\o[>%9l+ $!+v%%'r^ dgQ}cZ$t psS*uJXZ$\P5KOIZS}c8`,)ɾ;`_R}p(ɘNEI0=O=$y)3)ɾ1d'Jҕwǔ3cII<%3)I^؝=c3:% Ǔg0\R0$Ƈ\JJI)߲S}c%i?s%% 4Q,|X$K~1McJo\=o%%ySNc>%ύ͗Ac()ɛ;1%7v_vSwCII9S}=%8$J^$/\S}%3Nq|JS=OIKkgؕM :%4S}=%:Yd. 2M uJ.)ɾqI؍ٔM :%)I^S}c=%;Ad( r,)ɛNuJRi>$1% )ɾqpU~MۍW^ħnȦ$o ߲-)ɛ:%)ImyJo${JU %*h;.$KY(Z~MVr)+|˦$o ߲-)ɛ:%ӝNI>tgW$vS;9)ɇNwNuJa;:% $׬p9*Jf\/Sb7%i;iƿhص{d[ǿϜꔄT$\JJo\x+)IVx$m{ ) 'O}%% ΢$sYxg:mn׿\ꔄ鐷<668f#/l$}fS}cw(I'9ZSL$)+b%guJo#;e cJxPR0dg%guJoNr%;cJ™xTRzpfr-)I^x;G}ClJJ'iX3)I^؝=cyΡNI$ǜKJKII޹S4>Io/_S}c%iu:% CII9S}%iL%% ) i$kY[d<O Og+JҖ/ uJk4{Q,|P$yxp-|M{0ᘩ$o S}=%9͛(ɭ,d"[>%՝cJolvpQ$/ꔤSLI 4'Q,|˹$o Lǔ$/$K΋y%ȼ>% l)I^؞$=%BdFy$}Y¡NIj;ǔ$/S}=%dy.$KY(Z孤$o -OIu昒yJo S}cpu~M{3;{ګ29eGN$SYsII0)ɇ)Im嘒vZ)Im1% sOIQ؜$ya{d${Jo쿧$\P._( /KJ% %үrS*ou>%$S}c{PR}c<`WR}c$$'Q)+DIRVx=I{0z̚ȧ2uuJyrXR}cw(I'i[6&) 'QNI.%% DIne̐:mL::%7ƒIړI%cJuȩ$y|dc*E1%$;ZRvv6Dl::%7'DIBYxg<$KJ'iG9)ɾq>$J;ǔ3kIIy;SClJJ'iX?f,)I^؝=cy$CII$ǜKJKII޹SNi>*1%7_Ҟ$ya:OIq;$kY[Tvb~G>%Easpa}M{2<(I, ߲+)ɛuJ)ɾqp}M{2<(I* ZR7/\ꔤzLIyJ27{JU5UĖ$,N+ ߲/)ɛuJRi<$cJ)I^S}{Jo\$\PvN|ibg+JҖDIBYQ$ىte[%%ySCIIXR7o9M[%%yS$o _xS;ݹ)I^&Jr+ Ciblh/@f3J=Jǔ3ʹNI8) !גol>%ύI$mVN6̎)жLxsSNCN%%7'DIRVx=I(1zLIBfS-)I^NҞl(SNC%%7'9Le1%$Z$$WQ,v̧lJJ $,3SC%% (s d8I<2Mk\EIֲ1?fSR7wǔ3$yawpr()I^8$KJ3KII޹SN־te cJolO2NI$mCJJpZ,J2/LuJ^OҞ<̵$o S6DyӞ'ٖMᘒ㗴=ٗM1%7_9)8\R7wcJo~I{0%ύJU %**fR5|m s(X~$J29eG&QT~UZRR7oM[JJ§]Uံ/fzQdPafP阒p:Wꔄ3ʥNI8y+) SgS)ynlO20%;چY1%cJS\$tkII倫(ZyL;$0ΦNI8JJo'ىtYadcGcpRr.)I^NҞl(SNCJJ2udcDp2$/NvL`sS}x(T9SC^KJ.'iGyS61)߲S}c8(J;cJ™y(P9SӜEItLI8SOs%Y;cJ'>1%7'DIBYX$yawQc%% 4gQ,3S}KӞ\$/.<KI=%9$o R3Kb|鼤%}|{=%9$o ϲLSo?$$_|..= ~$o V$vg ?)9<%Cǒ{JÇK }/w틙7]bbf%(I_Ad( w(J2gd%S}1󅿧FG=m7( =/NJ3?0+>%ύ$/lS0$1%9ڹS;)ɇ^8MG$cYxQbvOF%<%t=$G;/uJa%%ySxQ 0y'%yJrs[$vza()ɛS-b)龧$cJrb;)%gv~a#JҔgYJ)ΡNI>ex,)ɉ_؋eY{JrCcII$4ȿdS;Pk|鼄VuJau%,OɧKh$o ϲ)ɷ$,/$?|=n~Yd. {I*)ɛ³w혒k +S͇KK)ɾ1|OI{Jv)I^؟$ypv)Im阒v)ΩNI>kIIRR7e)ږm̧U~b)i$ya{v ǔhX$vzaWR7h%[ºd7Q[YxO +ĺ9OIvnNiG$~OIvNoًeY{JrX$vzId* ϲSS|#m?.YS/>t*JgYJnSY-O; NI>fɟx,ǔoi[{ּǒ-mk?X2S;mY'uJa_ZR7gYJ)ɷ^EIֲ,%cJrbC K%MIIe]Ҟ$_|腶a%۟$MY%yJŇ:_d()ɛ³<ǔ%i;/V:KRIIzLINd9OIP]B ]~~S,K:%ӿKcJrb%<%Cǒ=%uI=%åŬ]bbh.: yIS;yS;ݹ)ɇ\NwN!Si6%%ySmII~d% eGFQXmcY'JB %dyJRi>$G;:% %%ySxQ?Q_XJnSN#Ý:%[$mYxe ?)Imξ$o OѶl'{JRi:$'vs.)ɛS#ou<%9yS;}myOO,%);ާNXI3KI=%99)ɇ>2ZIuIw|;{Q,?.SS|#mK4~b]$_|Ud- ϲܾ$;~|J>}?XS;0,x|;;Q,<˺?OIН(Pe]2$_|Id* ϲ.S/>tg%IeYKǔoi NK:%y5%xOɻ³K;u':%=%w~JNI>d8OI m #~$o X2S;)'~/YJJ,%1%9yJŇ^]bQ^$mYx%NI>xLIN;$'v^ҟ$_|X2|OI~p)e }//KYB ]ryy.qK\%.qK\%.qK\%.qK\%..]9%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK\%.qK7HPKHbD@b44ExpLogTable.hUT!RWux PKVopenimageio-1.7.17~dfsg0.orig/src/build-scripts/OpenEXR-CMakeLists.txt0000644000175000017500000002006313151711064023717 0ustar mfvmfvCMAKE_MINIMUM_REQUIRED(VERSION 2.8) PROJECT (openexr) SET(OPENEXR_VERSION_MAJOR "2") SET(OPENEXR_VERSION_MINOR "2") SET(OPENEXR_VERSION_PATCH "0") SET(OPENEXR_VERSION ${OPENEXR_VERSION_MAJOR}.${OPENEXR_VERSION_MINOR}.${OPENEXR_VERSION_PATCH}) SET(OPENEXR_VERSION_API ${OPENEXR_VERSION_MAJOR}_${OPENEXR_VERSION_MINOR}) # enable the tests ENABLE_TESTING() # distro building SET(CPACK_PACKAGE_VERSION_MAJOR "${OPENEXR_VERSION_MAJOR}") SET(CPACK_PACKAGE_VERSION_MINOR "${OPENEXR_VERSION_MINOR}") SET(CPACK_PACKAGE_VERSION_PATCH "${OPENEXR_VERSION_PATCH}") SET(CPACK_SOURCE_GENERATOR "TGZ") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${OPENEXR_VERSION}" ) set(CPACK_SOURCE_IGNORE_FILES "/.git*;/.cvs*;${CPACK_SOURCE_IGNORE_FILES}") INCLUDE ( CPack ) # Allow the developer to select if Dynamic or Static libraries are built OPTION (BUILD_SHARED_LIBS "Build Shared Libraries" ON) OPTION (USE_ZLIB_WINAPI "Use ZLib Win API" OFF) OPTION (NAMESPACE_VERSIONING "Use Namespace Versioning" ON) OPTION (BUILD_UTILS "Build the binary/utility programs" ON) OPTION (BUILD_TESTS "Build the tests" ON) # Setup osx rpathing SET (CMAKE_MACOSX_RPATH 1) SET (BUILD_WITH_INSTALL_RPATH 1) ADD_DEFINITIONS ( -DHAVE_CONFIG_H -DILM_IMF_TEST_IMAGEDIR="${CMAKE_SOURCE_DIR}/IlmImfTest/" ) INCLUDE_DIRECTORIES ( ${CMAKE_CURRENT_BINARY_DIR}/config IlmImf IlmImfUtil exrmaketiled exrenvmap exrmakepreview exrmultiview IlmImfFuzzTest ) FIND_PACKAGE(ZLIB REQUIRED) INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR}) IF (NOT WIN32) SET ( PTHREAD_LIB pthread ) ENDIF() INCLUDE_DIRECTORIES ( ${ILMBASE_PACKAGE_PREFIX}/include/OpenEXR ) LINK_DIRECTORIES ( ${ILMBASE_PACKAGE_PREFIX}/lib ) MESSAGE ( "ILMBASE_PACKAGE_PREFIX = " ${ILMBASE_PACKAGE_PREFIX}) SET (LIB_TYPE STATIC) IF (BUILD_SHARED_LIBS) # User wants to build Dynamic Libraries, so change the LIB_TYPE variable to CMake keyword 'SHARED' SET (LIB_TYPE SHARED) IF (WIN32) ADD_DEFINITIONS(-DOPENEXR_DLL) ENDIF () ENDIF () IF (USE_ZLIB_WINAPI) ADD_DEFINITIONS(-DZLIB_WINAPI) ENDIF () # Test for GCC-style inline asm support with AVX instructions INCLUDE (CheckCXXSourceCompiles) CHECK_CXX_SOURCE_COMPILES ( " int main() { #if defined(__GNUC__) && defined(__SSE2__) int n = 0; int eax = 0; int edx = 0; __asm__( \"xgetbv ;\" \"vzeroupper \" : \"=a\"(eax), \"=d\"(edx) : \"c\"(n) : ); #else #error No GCC style inline asm supported for AVX instructions #endif } " HAVE_GCC_INLINE_ASM_AVX) # Check if sysconf(_SC_NPROCESSORS_ONLN) can be used for CPU count CHECK_CXX_SOURCE_COMPILES ( " #include int main() { sysconf(_SC_NPROCESSORS_ONLN); } " HAVE_SYSCONF_NPROCESSORS_ONLN) ########################## # OpenEXRConfig.h generation ########################## IF (WIN32) FILE ( WRITE ${CMAKE_CURRENT_BINARY_DIR}/config/OpenEXRConfig.h "#define OPENEXR_IMF_HAVE_COMPLETE_IOMANIP 1\n" ) ELSEIF (APPLE) FILE ( WRITE ${CMAKE_CURRENT_BINARY_DIR}/config/OpenEXRConfig.h "#define OPENEXR_IMF_HAVE_DARWIN 1\n" ) FILE ( APPEND ${CMAKE_CURRENT_BINARY_DIR}/config/OpenEXRConfig.h "#define OPENEXR_IMF_HAVE_COMPLETE_IOMANIP 1\n" ) FILE ( APPEND ${CMAKE_CURRENT_BINARY_DIR}/config/OpenEXRConfig.h "#include \n" ) ELSE () # Linux FILE ( WRITE ${CMAKE_CURRENT_BINARY_DIR}/config/OpenEXRConfig.h "#define OPENEXR_IMF_HAVE_LINUX_PROCFS 1\n" ) FILE ( APPEND ${CMAKE_CURRENT_BINARY_DIR}/config/OpenEXRConfig.h "#define OPENEXR_IMF_HAVE_COMPLETE_IOMANIP 1\n" ) FILE ( APPEND ${CMAKE_CURRENT_BINARY_DIR}/config/OpenEXRConfig.h "#define OPENEXR_IMF_HAVE_LARGE_STACK 1\n" ) ENDIF() IF (NAMESPACE_VERSIONING) FILE ( APPEND ${CMAKE_CURRENT_BINARY_DIR}/config/OpenEXRConfig.h "#define OPENEXR_IMF_INTERNAL_NAMESPACE_CUSTOM 1\n") FILE ( APPEND ${CMAKE_CURRENT_BINARY_DIR}/config/OpenEXRConfig.h "#define OPENEXR_IMF_NAMESPACE Imf\n" ) FILE ( APPEND ${CMAKE_CURRENT_BINARY_DIR}/config/OpenEXRConfig.h "#define OPENEXR_IMF_INTERNAL_NAMESPACE Imf_${OPENEXR_VERSION_API}\n\n" ) ELSE () FILE ( APPEND ${CMAKE_CURRENT_BINARY_DIR}/config/OpenEXRConfig.h "#define OPENEXR_IMF_INTERNAL_NAMESPACE_CUSTOM 0\n") FILE ( APPEND ${CMAKE_CURRENT_BINARY_DIR}/config/OpenEXRConfig.h "#define OPENEXR_IMF_NAMESPACE Imf\n" ) FILE ( APPEND ${CMAKE_CURRENT_BINARY_DIR}/config/OpenEXRConfig.h "#define OPENEXR_IMF_INTERNAL_NAMESPACE Imf\n\n" ) ENDIF () FILE ( APPEND ${CMAKE_CURRENT_BINARY_DIR}/config/OpenEXRConfig.h "#define OPENEXR_VERSION_STRING \"${OPENEXR_VERSION}\"\n" ) FILE ( APPEND ${CMAKE_CURRENT_BINARY_DIR}/config/OpenEXRConfig.h "#define OPENEXR_PACKAGE_STRING \"OpenEXR ${OPENEXR_VERSION}\"\n" ) FILE ( APPEND ${CMAKE_CURRENT_BINARY_DIR}/config/OpenEXRConfig.h " #define OPENEXR_VERSION_MAJOR ${OPENEXR_VERSION_MAJOR} #define OPENEXR_VERSION_MINOR ${OPENEXR_VERSION_MINOR} #define OPENEXR_VERSION_PATCH ${OPENEXR_VERSION_PATCH} ") FILE ( APPEND ${CMAKE_CURRENT_BINARY_DIR}/config/OpenEXRConfig.h " // Version as a single hex number, e.g. 0x01000300 == 1.0.3 #define OPENEXR_VERSION_HEX ((OPENEXR_VERSION_MAJOR << 24) | \\ (OPENEXR_VERSION_MINOR << 16) | \\ (OPENEXR_VERSION_PATCH << 8))\n ") IF (HAVE_GCC_INLINE_ASM_AVX) FILE ( APPEND ${CMAKE_CURRENT_BINARY_DIR}/config/OpenEXRConfig.h "#define OPENEXR_IMF_HAVE_GCC_INLINE_ASM_AVX 1\n" ) ENDIF() IF (HAVE_SYSCONF_NPROCESSORS_ONLN) FILE ( APPEND ${CMAKE_CURRENT_BINARY_DIR}/config/OpenEXRConfig.h "#define OPENEXR_IMF_HAVE_SYSCONF_NPROCESSORS_ONLN 1\n" ) ENDIF() SET (OPENEXR_LIBSUFFIX "") SET (ILMBASE_LIBSUFFIX "") IF (NAMESPACE_VERSIONING) SET ( OPENEXR_LIBSUFFIX "-${OPENEXR_VERSION_API}" ) # assume same NAMESPACE_VERSION setting for IlmBase for now SET ( ILMBASE_LIBSUFFIX "-${OPENEXR_VERSION_API}" ) ENDIF () ########################## # IlmImf library ########################## ADD_SUBDIRECTORY ( IlmImf ) SET_TARGET_PROPERTIES ( IlmImf PROPERTIES VERSION 22.0.0 SOVERSION 22 OUTPUT_NAME "IlmImf${OPENEXR_LIBSUFFIX}" ) ########################## # IlmImfUtil library ########################## ADD_SUBDIRECTORY ( IlmImfUtil ) SET_TARGET_PROPERTIES ( IlmImfUtil PROPERTIES VERSION 22.0.0 SOVERSION 22 OUTPUT_NAME "IlmImfUtil${OPENEXR_LIBSUFFIX}" ) ########################## # Example Code ########################## ADD_SUBDIRECTORY ( IlmImfExamples ) ########################## # Tests ########################## if (BUILD_TESTS) ADD_SUBDIRECTORY ( IlmImfTest ) ADD_SUBDIRECTORY ( IlmImfUtilTest ) ADD_SUBDIRECTORY ( IlmImfFuzzTest ) endif () ########################## # Binaries / Utilities ########################## if (BUILD_UTILS) ADD_SUBDIRECTORY ( exrheader ) ADD_SUBDIRECTORY ( exrmaketiled ) ADD_SUBDIRECTORY ( exrstdattr ) ADD_SUBDIRECTORY ( exrmakepreview ) ADD_SUBDIRECTORY ( exrenvmap ) ADD_SUBDIRECTORY ( exrmultiview ) ADD_SUBDIRECTORY ( exrmultipart ) endif () ########################## # Installation ########################## INSTALL ( FILES ${CMAKE_CURRENT_BINARY_DIR}/config/OpenEXRConfig.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/OpenEXR ) # Documentation INSTALL ( FILES doc/TechnicalIntroduction.pdf doc/ReadingAndWritingImageFiles.pdf doc/OpenEXRFileLayout.pdf doc/MultiViewOpenEXR.pdf doc/InterpretingDeepPixels.pdf doc/TheoryDeepPixels.pdf DESTINATION ${CMAKE_INSTALL_PREFIX}/share/doc/OpenEXR-${OPENEXR_VERSION} ) # Examples INSTALL ( FILES IlmImfExamples/main.cpp IlmImfExamples/drawImage.cpp IlmImfExamples/rgbaInterfaceExamples.cpp IlmImfExamples/rgbaInterfaceTiledExamples.cpp IlmImfExamples/generalInterfaceExamples.cpp IlmImfExamples/lowLevelIoExamples.cpp IlmImfExamples/previewImageExamples.cpp IlmImfExamples/generalInterfaceTiledExamples.cpp IlmImfExamples/generalInterfaceTiledExamples.h IlmImfExamples/drawImage.h IlmImfExamples/rgbaInterfaceExamples.h IlmImfExamples/generalInterfaceExamples.h IlmImfExamples/rgbaInterfaceTiledExamples.h IlmImfExamples/lowLevelIoExamples.h IlmImfExamples/previewImageExamples.h IlmImfExamples/namespaceAlias.h DESTINATION ${CMAKE_INSTALL_PREFIX}/share/doc/OpenEXR-${OPENEXR_VERSION}/examples ) openimageio-1.7.17~dfsg0.orig/src/build-scripts/install_homebrew_deps.bash0000755000175000017500000000177413151711064025124 0ustar mfvmfv#!/bin/bash # This script, which assumes it is runnign on a Mac OSX with Homebrew # installed, does a "brew install" in all packages reasonably needed by # OIIO. if [ `uname` != "Darwin" ] ; then echo "Don't run this script unless you are on Mac OSX" exit 1 fi if [ `which brew` == "" ] ; then echo "You need to install Homebrew before running this script." echo "See http://brew.sh" exit 1 fi brew update >/dev/null echo "" echo "Before my brew installs:" brew list --versions brew install ccache cmake brew install ilmbase openexr brew install boost-python brew install opencolorio brew install freetype brew install libraw libpng webp jpeg-turbo brew install openjpeg20 if [ "$LINKSTATIC" == "1" ] ; then brew install little-cms2 tinyxml szip brew install homebrew/dupes/bzip2 brew install yaml-cpp --with-static-lib fi #brew install homebrew/science/hdf5 --with-threadsafe #brew install field3d webp ffmpeg openjpeg opencv echo "" echo "After brew installs:" brew list --versions openimageio-1.7.17~dfsg0.orig/src/build-scripts/build_openexr.bash0000755000175000017500000000253313151711064023404 0ustar mfvmfv#!/bin/bash # The Linux VM used by Travis has OpenEXR 1.x. We really want 2.x. EXRINSTALLDIR=${EXRINSTALLDIR:=${PWD}/openexr-install} EXRVERSION=${EXRVERSION:=2.2.0} BASEDIR=`pwd` pwd echo "EXR install dir will be: ${EXRINSTALLDIR}" if [ ! -e ${EXRINSTALLDIR} ] ; then mkdir ${EXRINSTALLDIR} fi # Clone OpenEXR project (including IlmBase) from GitHub and build if [ ! -e ./openexr ] ; then git clone -b v${EXRVERSION} https://github.com/openexr/openexr.git ./openexr fi flags= if [ ${LINKSTATIC:=0} == 1 ] ; then flags=${flags} --enable-static --enable-shared=no --with-pic fi pushd ./openexr git checkout v${EXRVERSION} --force cd IlmBase mkdir build cd build cmake --config Release -DCMAKE_INSTALL_PREFIX=${EXRINSTALLDIR} .. && make clean && make -j 4 && make install cd .. cd ../OpenEXR cp ${BASEDIR}/src/build-scripts/OpenEXR-CMakeLists.txt CMakeLists.txt cp ${BASEDIR}/src/build-scripts/OpenEXR-IlmImf-CMakeLists.txt IlmImf/CMakeLists.txt mkdir build mkdir build/IlmImf cd build unzip -d IlmImf ${BASEDIR}/src/build-scripts/b44ExpLogTable.h.zip unzip -d IlmImf ${BASEDIR}/src/build-scripts/dwaLookups.h.zip cmake --config Release -DCMAKE_INSTALL_PREFIX=${EXRINSTALLDIR} -DILMBASE_PACKAGE_PREFIX=${EXRINSTALLDIR} -DBUILD_UTILS=0 -DBUILD_TESTS=0 .. && make clean && make -j 4 && make install popd ls -R ${EXRINSTALLDIR} #echo "listing .." #ls .. openimageio-1.7.17~dfsg0.orig/src/webp.imageio/0000755000175000017500000000000013151711064017462 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/webp.imageio/CMakeLists.txt0000644000175000017500000000044413151711064022224 0ustar mfvmfvif (WEBP_FOUND) add_oiio_plugin (webpinput.cpp webpoutput.cpp INCLUDE_DIRS ${WEBP_INCLUDE_DIR} LINK_LIBRARIES ${WEBP_LIBRARY} DEFINITIONS "-DUSE_WEBP=1") else () message (STATUS "WebP plugin will not be built") endif() openimageio-1.7.17~dfsg0.orig/src/webp.imageio/webpinput.cpp0000644000175000017500000001356313151711064022213 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include "OpenImageIO/imageio.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" OIIO_PLUGIN_NAMESPACE_BEGIN namespace webp_pvt { class WebpInput : public ImageInput { public: WebpInput() { init(); } virtual ~WebpInput() { close(); } virtual const char* format_name() const { return "webp"; } virtual bool open (const std::string &name, ImageSpec &spec); virtual bool read_native_scanline (int y, int z, void *data); virtual bool close (); private: std::string m_filename; uint8_t *m_decoded_image; uint64_t m_image_size; long int m_scanline_size; FILE *m_file; void init() { m_image_size = 0; m_scanline_size = 0; m_decoded_image = NULL; m_file = NULL; } }; bool WebpInput::open (const std::string &name, ImageSpec &spec) { m_filename = name; // Perform preliminary test on file type. if (!Filesystem::is_regular(m_filename)) { error ("Not a regular file \"%s\"", m_filename.c_str()); return false; } // Get file size and check we've got enough data to decode WebP. m_image_size = Filesystem::file_size(name); if (m_image_size == uint64_t(-1)) { error ("Failed to get size for \"%s\"", m_filename); return false; } if (m_image_size < 12) { error ("File size is less than WebP header for file \"%s\"", m_filename); return false; } m_file = Filesystem::fopen(m_filename, "rb"); if (!m_file) { error ("Could not open file \"%s\"", m_filename.c_str()); return false; } // Read header and verify we've got WebP image. std::vector image_header; image_header.resize(std::min(m_image_size, (uint64_t)64), 0); size_t numRead = fread(&image_header[0], sizeof(uint8_t), image_header.size(), m_file); if (numRead != image_header.size()) { error ("Read failure for header of \"%s\" (expected %d bytes, read %d)", m_filename, image_header.size(), numRead); close (); return false; } int width = 0, height = 0; if(!WebPGetInfo(&image_header[0], image_header.size(), &width, &height)) { error ("%s is not a WebP image file", m_filename.c_str()); close(); return false; } // Read actual data and decode. std::vector encoded_image; encoded_image.resize(m_image_size, 0); fseek (m_file, 0, SEEK_SET); numRead = fread(&encoded_image[0], sizeof(uint8_t), encoded_image.size(), m_file); if (numRead != encoded_image.size()) { error ("Read failure for \"%s\" (expected %d bytes, read %d)", m_filename, encoded_image.size(), numRead); close (); return false; } const int CHANNEL_NUM = 4; m_scanline_size = width * CHANNEL_NUM; m_spec = ImageSpec(width, height, CHANNEL_NUM, TypeDesc::UINT8); spec = m_spec; if (!(m_decoded_image = WebPDecodeRGBA(&encoded_image[0], encoded_image.size(), &m_spec.width, &m_spec.height))) { error ("Couldn't decode %s", m_filename.c_str()); close(); return false; } return true; } bool WebpInput::read_native_scanline (int y, int z, void *data) { if (y < 0 || y >= m_spec.width) // out of range scanline return false; memcpy(data, &m_decoded_image[y*m_scanline_size], m_scanline_size); return true; } bool WebpInput::close() { if (m_file) { fclose(m_file); m_file = NULL; } if (m_decoded_image) { free(m_decoded_image); // this was allocated by WebPDecodeRGB and should be fread by free m_decoded_image = NULL; } return true; } } // namespace webp_pvt // Obligatory material to make this a recognizeable imageio plugin OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT int webp_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* webp_imageio_library_version () { int v = WebPGetDecoderVersion(); return ustring::format("Webp %d.%d.%d", v>>16, (v>>8)&255, v&255).c_str(); } OIIO_EXPORT ImageInput *webp_input_imageio_create () { return new webp_pvt::WebpInput; } OIIO_EXPORT const char *webp_input_extensions[] = { "webp", NULL }; OIIO_PLUGIN_EXPORTS_END OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/webp.imageio/webpoutput.cpp0000644000175000017500000001645613151711064022420 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include "OpenImageIO/filesystem.h" #include "OpenImageIO/imageio.h" OIIO_PLUGIN_NAMESPACE_BEGIN namespace webp_pvt { class WebpOutput : public ImageOutput { public: WebpOutput(){ init(); } virtual ~WebpOutput(){ close(); } virtual const char* format_name () const { return "webp"; } virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode=Create); virtual int supports (string_view feature) const; virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); virtual bool close(); private: WebPPicture m_webp_picture; WebPConfig m_webp_config; std::string m_filename; FILE *m_file; int m_scanline_size; unsigned int m_dither; std::vector m_uncompressed_image; void init() { m_scanline_size = 0; m_file = NULL; } }; int WebpOutput::supports (string_view feature) const { return feature == "tiles" || feature == "alpha" || feature == "random_access" || feature == "rewrite"; } static int WebpImageWriter(const uint8_t* img_data, size_t data_size, const WebPPicture* const webp_img) { FILE *out_file = (FILE*)webp_img->custom_ptr; size_t wb = fwrite (img_data, data_size, sizeof(uint8_t), out_file); if (wb != sizeof(uint8_t)) { //FIXME Bad write occurred } return 1; } bool WebpOutput::open (const std::string &name, const ImageSpec &spec, OpenMode mode) { if (mode != Create) { error ("%s does not support subimages or MIP levels", format_name()); return false; } // saving 'name' and 'spec' for later use m_filename = name; m_spec = spec; if (m_spec.nchannels != 3 && m_spec.nchannels != 4) { error ("%s does not support %d-channel images\n", format_name(), m_spec.nchannels); return false; } m_file = Filesystem::fopen (m_filename, "wb"); if (!m_file) { error ("Unable to open file \"%s\"", m_filename.c_str ()); return false; } if (!WebPPictureInit(&m_webp_picture)) { error("Couldn't initialize WebPPicture\n"); close(); return false; } m_webp_picture.width = m_spec.width; m_webp_picture.height = m_spec.height; m_webp_picture.writer = WebpImageWriter; m_webp_picture.custom_ptr = (void*)m_file; if (!WebPConfigInit(&m_webp_config)) { error("Couldn't initialize WebPPicture\n"); close(); return false; } m_webp_config.method = 6; int compression_quality = 100; const ImageIOParameter *qual = m_spec.find_attribute ("CompressionQuality", TypeDesc::INT); if (qual) { compression_quality = *static_cast(qual->data()); } m_webp_config.quality = compression_quality; // forcing UINT8 format m_spec.set_format (TypeDesc::UINT8); m_dither = m_spec.get_int_attribute ("oiio:dither", 0); m_scanline_size = m_spec.width * m_spec.nchannels; const int image_buffer = m_spec.height * m_scanline_size; m_uncompressed_image.resize(image_buffer, 0); return true; } bool WebpOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { if (y > m_spec.height) { error ("Attempt to write too many scanlines to %s", m_filename.c_str()); close (); return false; } std::vector scratch; data = to_native_scanline (format, data, xstride, scratch, m_dither, y, z); memcpy(&m_uncompressed_image[y*m_scanline_size], data, m_scanline_size); if (y == m_spec.height - 1) { if (m_spec.nchannels == 4) { WebPPictureImportRGBA(&m_webp_picture, &m_uncompressed_image[0], m_scanline_size); } else { WebPPictureImportRGB(&m_webp_picture, &m_uncompressed_image[0], m_scanline_size); } if (!WebPEncode(&m_webp_config, &m_webp_picture)) { error ("Failed to encode %s as WebP image", m_filename.c_str()); close(); return false; } } return true; } bool WebpOutput::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { // Emulate tiles by buffering the whole image return copy_tile_to_image_buffer (x, y, z, format, data, xstride, ystride, zstride, &m_uncompressed_image[0]); } bool WebpOutput::close() { if (! m_file) return true; // already closed bool ok = true; if (m_spec.tile_width) { // We've been emulating tiles; now dump as scanlines. ASSERT (m_uncompressed_image.size()); ok &= write_scanlines (m_spec.y, m_spec.y+m_spec.height, 0, m_spec.format, &m_uncompressed_image[0]); std::vector().swap (m_uncompressed_image); } WebPPictureFree(&m_webp_picture); fclose(m_file); m_file = NULL; return true; } } // namespace webp_pvt OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *webp_output_imageio_create () { return new webp_pvt::WebpOutput; } OIIO_EXPORT const char *webp_output_extensions[] = { "webp", NULL }; OIIO_PLUGIN_EXPORTS_END OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/field3d.imageio/0000755000175000017500000000000013151711064020037 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/field3d.imageio/CMakeLists.txt0000644000175000017500000000056513151711064022605 0ustar mfvmfvif (FIELD3D_FOUND) find_library (SZIP_LIBRARY NAMES sz) if (NOT SZIP_LIBRARY) set (SZIP_LIBRARY "") endif () add_oiio_plugin (field3dinput.cpp field3doutput.cpp INCLUDE_DIRS ${FIELD3D_INCLUDES} LINK_LIBRARIES ${FIELD3D_LIBRARY} ${HDF5_LIBRARIES} ${SZIP_LIBRARY}) endif() openimageio-1.7.17~dfsg0.orig/src/field3d.imageio/field3d_backdoor.h0000644000175000017500000000403513151711064023370 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ OIIO_NAMESPACE_BEGIN namespace f3dpvt { // Define an abstract interface that allows us to get special information // from the Field3DInput. class Field3DInput_Interface : public ImageInput { public: Field3DInput_Interface () { } // Transform world space P to local space P. virtual void worldToLocal (const Imath::V3f &wsP, Imath::V3f &lsP, float time) const = 0; }; } // end namespace f3dpvt OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/field3d.imageio/field3doutput.cpp0000644000175000017500000004714213151711064023346 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/thread.h" #include "field3d_pvt.h" using namespace OIIO_NAMESPACE::f3dpvt; OIIO_PLUGIN_NAMESPACE_BEGIN class Field3DOutput : public ImageOutput { public: Field3DOutput (); virtual ~Field3DOutput (); virtual const char * format_name (void) const { return "field3d"; } virtual int supports (string_view feature) const; virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode); virtual bool open (const std::string &name, int subimages, const ImageSpec *specs); virtual bool close (); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); private: std::string m_name; Field3DOutputFile *m_output; int m_subimage; ///< What subimage/field are we writing now int m_nsubimages; ///< How many subimages will be in the file? bool m_writepending; ///< Is there an unwritten current layer? std::vector m_specs; std::vector m_scratch; ///< Scratch space for us to use FieldRes::Ptr m_field; // Initialize private members to pre-opened state void init (void) { m_name.clear (); m_output = NULL; m_subimage = -1; m_nsubimages = 0; m_specs.clear (); m_writepending = false; } // Add a parameter to the output bool put_parameter (const std::string &name, TypeDesc type, const void *data); bool prep_subimage (); bool write_current_subimage (); template bool prep_subimage_specialized (); template bool write_current_subimage_specialized (); template bool write_current_subimage_specialized_vec (); template bool write_scanline_specialized (int y, int z, const T *data); template bool write_tile_specialized (int x, int y, int z, const T *data); }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput * field3d_output_imageio_create () { return new Field3DOutput; } OIIO_EXPORT int field3d_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* field3d_imageio_library_version () { return ustring::format("Field3d %d.%d.%d", FIELD3D_MAJOR_VER, FIELD3D_MINOR_VER, FIELD3D_MICRO_VER).c_str(); } OIIO_EXPORT const char * field3d_output_extensions[] = { "f3d", NULL }; OIIO_PLUGIN_EXPORTS_END namespace { // anon namespace // format-specific metadata prefixes static std::vector format_prefixes; static atomic_int format_prefixes_initialized; static spin_mutex format_prefixes_mutex; // guard } Field3DOutput::Field3DOutput () { init (); } Field3DOutput::~Field3DOutput () { // Close, if not already done. close (); } int Field3DOutput::supports (string_view feature) const { return (feature == "tiles" || feature == "multiimage" || feature == "random_access" || feature == "arbitrary_metadata" || feature == "exif" // Because of arbitrary_metadata || feature == "iptc"); // Because of arbitrary_metadata // FIXME: we could support "empty" // FIXME: newer releases of Field3D support mipmap } bool Field3DOutput::open (const std::string &name, const ImageSpec &userspec, OpenMode mode) { // If called the old-fashioned way, for one subimage, just turn it into // a call to the multi-subimage open() with a single subimage. if (mode == Create) return open (name, 1, &userspec); if (mode == AppendMIPLevel) { error ("%s does not support MIP-mapping", format_name()); return false; } ASSERT (mode == AppendSubimage && "invalid open() mode"); write_current_subimage (); ++m_subimage; if (m_subimage >= m_nsubimages) { error ("Appending past the pre-declared number of subimages (%d)", m_nsubimages); return false; } if (! prep_subimage ()) return false; return true; } bool Field3DOutput::open (const std::string &name, int subimages, const ImageSpec *specs) { if (m_output) close(); if (subimages < 1) { error ("%s does not support %d subimages.", format_name(), subimages); return false; } oiio_field3d_initialize (); m_nsubimages = subimages; m_subimage = 0; { spin_lock lock (field3d_mutex()); m_output = new Field3DOutputFile; bool ok = false; try { ok = m_output->create (name); } catch (...) { ok = false; } if (! ok) { delete m_output; m_output = NULL; m_name.clear (); return false; } m_name = name; } m_specs.assign (specs, specs+subimages); for (int s = 0; s < m_nsubimages; ++s) { ImageSpec &spec (m_specs[s]); if (spec.format != TypeDesc::HALF && spec.format != TypeDesc::DOUBLE) { spec.format = TypeDesc::FLOAT; } if (spec.nchannels != 1 && spec.nchannels != 3) { error ("%s does not allow %d channels in a field (subimage %d)", format_name(), spec.nchannels, s); return false; } } if (! prep_subimage ()) // get ready for first subimage return false; return true; } bool Field3DOutput::put_parameter (const std::string &name, TypeDesc type, const void *data) { if (Strutil::istarts_with (name, "field3d:") || Strutil::istarts_with (name, "oiio:")) return false; // skip these; handled separately or not at all // Before handling general named metadata, suppress non-openexr // format-specific metadata. if (const char *colon = strchr (name.c_str(), ':')) { std::string prefix (name.c_str(), colon); if (! Strutil::iequals (prefix, "openexr")) { if (! format_prefixes_initialized) { // Retrieve and split the list, only the first time spin_lock lock (format_prefixes_mutex); std::string format_list; OIIO::getattribute ("format_list", format_list); Strutil::split (format_list, format_prefixes, ","); format_prefixes_initialized = true; } for (size_t i = 0, e = format_prefixes.size(); i < e; ++i) if (Strutil::iequals (prefix, format_prefixes[i])) return false; } } if (type == TypeDesc::TypeString) m_field->metadata().setStrMetadata (name, *(const char **)data); else if (type == TypeDesc::TypeInt) m_field->metadata().setIntMetadata (name, *(const int *)data); else if (type == TypeDesc::TypeFloat) m_field->metadata().setFloatMetadata (name, *(const float *)data); else if (type.basetype == TypeDesc::FLOAT && type.aggregate == 3) m_field->metadata().setVecFloatMetadata (name, *(const FIELD3D_NS::V3f *)data); else if (type.basetype == TypeDesc::INT && type.aggregate == 3) m_field->metadata().setVecIntMetadata (name, *(const FIELD3D_NS::V3i *)data); else return false; return true; } bool Field3DOutput::close () { spin_lock lock (field3d_mutex()); if (m_output) { write_current_subimage (); m_output->close (); delete m_output; // implicity closes m_output = NULL; } init (); // re-initialize return true; // How can we fail? } template bool Field3DOutput::write_scanline_specialized (int y, int z, const T *data) { int xend = m_spec.x + m_spec.width; if (typename DenseField::Ptr f = field_dynamic_cast >(m_field)) { for (int x = m_spec.x; x < xend; ++x) f->lvalue(x, y, z) = *data++; return true; } if (typename SparseField::Ptr f = field_dynamic_cast >(m_field)) { for (int x = m_spec.x; x < xend; ++x) f->lvalue(x, y, z) = *data++; return true; } error ("Unknown field type"); return false; } bool Field3DOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { m_spec.auto_stride (xstride, format, spec().nchannels); data = to_native_scanline (format, data, xstride, m_scratch); if (m_spec.format == TypeDesc::FLOAT) { if (m_spec.nchannels == 1) return write_scanline_specialized(y, z, (const float *)data); else return write_scanline_specialized(y, z, (const FIELD3D_VEC3_T *)data); } else if (m_spec.format == TypeDesc::DOUBLE) { if (m_spec.nchannels == 1) return write_scanline_specialized(y, z, (const double *)data); else return write_scanline_specialized(y, z, (const FIELD3D_VEC3_T *)data); } else if (m_spec.format == TypeDesc::HALF) { if (m_spec.nchannels == 1) return write_scanline_specialized(y, z, (const FIELD3D_NS::half *)data); else return write_scanline_specialized(y, z, (const FIELD3D_VEC3_T *)data); } else { ASSERT (0 && "Unsupported data format for field3d"); } return false; } template bool Field3DOutput::write_tile_specialized (int x, int y, int z, const T *data) { int xend = std::min (x + m_spec.tile_width, m_spec.x + m_spec.width); int yend = std::min (y + m_spec.tile_height, m_spec.y + m_spec.height); int zend = std::min (z + m_spec.tile_depth, m_spec.z + m_spec.depth); if (typename DenseField::Ptr f = field_dynamic_cast >(m_field)) { for (int k = z; k < zend; ++k) { for (int j = y; j < yend; ++j) { const T *d = data + (k-z)*(m_spec.tile_width*m_spec.tile_height) + (j-y)*m_spec.tile_width; for (int i = x; i < xend; ++i) f->lvalue (i, j, k) = *d++; } } return true; } if (typename SparseField::Ptr f = field_dynamic_cast >(m_field)) { for (int k = z; k < zend; ++k) { for (int j = y; j < yend; ++j) { const T *d = data + (k-z)*(m_spec.tile_width*m_spec.tile_height) + (j-y)*m_spec.tile_width; for (int i = x; i < xend; ++i) f->lvalue (i, j, k) = *d++; } } return true; } error ("Unknown field type"); return false; } bool Field3DOutput::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { m_spec.auto_stride (xstride, ystride, zstride, format, spec().nchannels, spec().tile_width, spec().tile_height); data = to_native_tile (format, data, xstride, ystride, zstride, m_scratch); if (m_spec.format == TypeDesc::FLOAT) { if (m_spec.nchannels == 1) return write_tile_specialized (x, y, z, (const float *)data); else return write_tile_specialized (x, y, z, (const FIELD3D_VEC3_T *)data); } else if (m_spec.format == TypeDesc::DOUBLE) { if (m_spec.nchannels == 1) return write_tile_specialized (x, y, z, (const double *)data); else return write_tile_specialized (x, y, z, (const FIELD3D_VEC3_T *)data); } else if (m_spec.format == TypeDesc::HALF) { if (m_spec.nchannels == 1) return write_tile_specialized (x, y, z, (const FIELD3D_NS::half *)data); else return write_tile_specialized (x, y, z, (const FIELD3D_VEC3_T *)data); } else { ASSERT (0 && "Unsupported data format for field3d"); } return false; } template bool Field3DOutput::prep_subimage_specialized () { m_spec = m_specs[m_subimage]; ASSERT (m_spec.nchannels == 1 || m_spec.nchannels == 3); Box3i extents (V3i (m_spec.full_x, m_spec.full_y, m_spec.full_z), V3i (m_spec.full_x+m_spec.full_width-1, m_spec.full_y+m_spec.full_height-1, m_spec.full_z+m_spec.full_depth-1)); Box3i datawin (V3i (m_spec.x, m_spec.y, m_spec.z), V3i (m_spec.x+m_spec.width-1, m_spec.y+m_spec.height-1, m_spec.z+m_spec.depth-1)); std::string fieldtype = m_spec.get_string_attribute ("field3d:fieldtype"); if (Strutil::iequals (fieldtype, "SparseField")) { // Sparse SparseField *f (new SparseField); f->setSize (extents, datawin); m_field.reset (f); } else if (Strutil::iequals (fieldtype, "MAC")) { // FIXME ASSERT (0 && "MAC fields not yet supported"); } else { // Dense DenseField *f (new DenseField); f->setSize (extents, datawin); m_field.reset (f); } std::string name = m_spec.get_string_attribute ("field3d:partition"); std::string attribute = m_spec.get_string_attribute ("field3d:layer"); if (! name.size() && ! attribute.size()) { // Try to extract from the subimagename or if that fails, // ImageDescription std::string unique_name = m_spec.get_string_attribute ("oiio:subimagename"); if (unique_name.size() == 0) unique_name = m_spec.get_string_attribute ("ImageDescription"); if (unique_name.size() == 0) unique_name = "name:attribute"; // punt std::vector pieces; Strutil::split (unique_name, pieces); if (pieces.size() > 0) name = pieces[0]; if (pieces.size() > 1) attribute = pieces[1]; } m_field->name = name; m_field->attribute = attribute; // Mapping matrix TypeDesc TypeMatrixD (TypeDesc::DOUBLE, TypeDesc::MATRIX44); if (ImageIOParameter *mx = m_spec.find_attribute ("field3d:localtoworld", TypeMatrixD)) { MatrixFieldMapping::Ptr mapping (new MatrixFieldMapping); mapping->setLocalToWorld (*((FIELD3D_NS::M44d*)mx->data())); m_field->setMapping (mapping); } else if (ImageIOParameter *mx = m_spec.find_attribute ("worldtocamera", TypeDesc::TypeMatrix)) { Imath::M44f m = *((Imath::M44f*)mx->data()); m = m.inverse(); FIELD3D_NS::M44d md (m[0][0], m[0][1], m[0][1], m[0][3], m[1][0], m[1][1], m[1][1], m[1][3], m[2][0], m[2][1], m[2][1], m[2][3], m[3][0], m[3][1], m[3][1], m[3][3]); MatrixFieldMapping::Ptr mapping (new MatrixFieldMapping); mapping->setLocalToWorld (md); m_field->setMapping (mapping); } // Miscellaneous metadata for (size_t p = 0; p < spec().extra_attribs.size(); ++p) put_parameter (spec().extra_attribs[p].name().string(), spec().extra_attribs[p].type(), spec().extra_attribs[p].data()); return true; } bool Field3DOutput::prep_subimage () { m_spec = m_specs[m_subimage]; ASSERT (m_spec.nchannels == 1 || m_spec.nchannels == 3); if (m_spec.format == TypeDesc::FLOAT) { if (m_spec.nchannels == 1) prep_subimage_specialized(); else prep_subimage_specialized >(); } else if (m_spec.format == TypeDesc::DOUBLE) { if (m_spec.nchannels == 1) prep_subimage_specialized(); else prep_subimage_specialized >(); } else if (m_spec.format == TypeDesc::HALF) { if (m_spec.nchannels == 1) prep_subimage_specialized(); else prep_subimage_specialized >(); } else { ASSERT (0 && "Unsupported data format for field3d"); } m_writepending = true; return true; } template bool Field3DOutput::write_current_subimage_specialized () { if (typename DenseField::Ptr df = field_dynamic_cast >(m_field)) { m_output->writeScalarLayer (df); return true; } if (typename SparseField::Ptr sf = field_dynamic_cast >(m_field)) { m_output->writeScalarLayer (sf); return true; } return false; } template bool Field3DOutput::write_current_subimage_specialized_vec () { typedef FIELD3D_VEC3_T V; if (typename DenseField::Ptr df = field_dynamic_cast >(m_field)) { m_output->writeVectorLayer (df); return true; } if (typename SparseField::Ptr sf = field_dynamic_cast >(m_field)) { m_output->writeVectorLayer (sf); return true; } return false; } bool Field3DOutput::write_current_subimage () { if (! m_writepending) return true; bool ok = false; if (m_spec.format == TypeDesc::FLOAT) { if (m_spec.nchannels == 1) ok = write_current_subimage_specialized (); else ok = write_current_subimage_specialized_vec (); } else if (m_spec.format == TypeDesc::DOUBLE) { if (m_spec.nchannels == 1) ok = write_current_subimage_specialized (); else ok = write_current_subimage_specialized_vec (); } else if (m_spec.format == TypeDesc::HALF) { if (m_spec.nchannels == 1) ok = write_current_subimage_specialized (); else ok = write_current_subimage_specialized_vec (); } m_writepending = false; m_field.reset (); return ok; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/field3d.imageio/field3d_pvt.h0000644000175000017500000000557713151711064022431 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include #ifndef FIELD3D_NS #define FIELD3D_NS Field3D #endif using namespace FIELD3D_NS; OIIO_NAMESPACE_BEGIN namespace f3dpvt { enum FieldType { Dense, Sparse, MAC }; struct layerrecord { std::string name; std::string attribute; std::string unique_name; TypeDesc datatype; FieldType fieldtype; bool vecfield; // true=vector, false=scalar Box3i extents; Box3i dataWindow; ImageSpec spec; FieldRes::Ptr field; layerrecord () : vecfield(false) { } }; // Define an abstract interface that allows us to get special information // from the Field3DInput. class Field3DInput_Interface : public ImageInput { public: Field3DInput_Interface () { } // Transform world space P to local space P. virtual void worldToLocal (const Imath::V3f &wsP, Imath::V3f &lsP, float time) const = 0; }; // Return a reference to the mutex that allows us to use f3d with multiple // threads. spin_mutex &field3d_mutex (); void oiio_field3d_initialize (); } // end namespace f3dpvt OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/field3d.imageio/field3dinput.cpp0000644000175000017500000004347513151711064023152 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/filesystem.h" #include #include #include "field3d_pvt.h" using namespace OIIO_NAMESPACE::f3dpvt; OIIO_PLUGIN_NAMESPACE_BEGIN spin_mutex & f3dpvt::field3d_mutex () { static spin_mutex m; return m; } class Field3DInput : public Field3DInput_Interface { public: Field3DInput () { init(); } virtual ~Field3DInput () { close(); } virtual const char * format_name (void) const { return "field3d"; } virtual int supports (string_view feature) const { return (feature == "arbitrary_metadata"); } virtual bool valid_file (const std::string &filename) const; virtual bool open (const std::string &name, ImageSpec &newspec); virtual bool close (); virtual int current_subimage (void) const { return m_subimage; } virtual bool seek_subimage (int subimage, int miplevel, ImageSpec &newspec); virtual bool read_native_scanline (int y, int z, void *data); virtual bool read_native_tile (int x, int y, int z, void *data); /// Transform a world space position to local coordinates, using the /// mapping of the current subimage. virtual void worldToLocal (const Imath::V3f &wsP, Imath::V3f &lsP, float time) const; private: std::string m_name; Field3DInputFile *m_input; int m_subimage; ///< What subimage/field are we looking at? int m_nsubimages; ///< How many fields in the file? std::vector m_layers; std::vector m_scratch; ///< Scratch space for us to use template void read_layers (TypeDesc datatype); void read_one_layer (FieldRes::Ptr field, layerrecord &lay, TypeDesc datatype, size_t layernum); template bool readtile (int x, int y, int z, T *data); void init () { m_name.clear (); m_input = NULL; m_subimage = -1; m_nsubimages = 0; m_layers.clear (); } }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageInput * field3d_input_imageio_create () { return new Field3DInput; } // OIIO_EXPORT int field3d_imageio_version = OIIO_PLUGIN_VERSION; // it's in field3doutput.cpp OIIO_EXPORT const char * field3d_input_extensions[] = { "f3d", NULL }; OIIO_PLUGIN_EXPORTS_END void f3dpvt::oiio_field3d_initialize () { static volatile bool initialized = false; if (! initialized) { spin_lock lock (field3d_mutex()); if (! initialized) { initIO (); // Minimize Field3D's own internal caching SparseFileManager::singleton().setLimitMemUse(true); // Enables cache SparseFileManager::singleton().setMaxMemUse(20.0f); // In MB #if (100*FIELD3D_MAJOR_VER + FIELD3D_MINOR_VER) >= 104 Msg::setVerbosity (0); // Turn off console messages from F3D #endif initialized = true; } } } template inline int blocksize (FieldRes::Ptr &f) { ASSERT (f && "taking blocksize of null ptr"); typename SparseField::Ptr sf (field_dynamic_cast >(f)); if (sf) return sf->blockSize(); typename SparseField::Ptr vsf (field_dynamic_cast >(f)); if (vsf) return vsf->blockSize(); return 0; } template static void read_metadata (const M &meta, ImageSpec &spec) { for (typename M::StrMetadata::const_iterator i = meta.strMetadata().begin(), e = meta.strMetadata().end(); i != e; ++i) spec.attribute (i->first, i->second); for (typename M::IntMetadata::const_iterator i = meta.intMetadata().begin(), e = meta.intMetadata().end(); i != e; ++i) spec.attribute (i->first, i->second); for (typename M::FloatMetadata::const_iterator i = meta.floatMetadata().begin(), e = meta.floatMetadata().end(); i != e; ++i) spec.attribute (i->first, i->second); for (typename M::VecIntMetadata::const_iterator i = meta.vecIntMetadata().begin(), e = meta.vecIntMetadata().end(); i != e; ++i) { spec.attribute (i->first, TypeDesc(TypeDesc::INT,3), &(i->second)); } for (typename M::VecFloatMetadata::const_iterator i = meta.vecFloatMetadata().begin(), e = meta.vecFloatMetadata().end(); i != e; ++i) { spec.attribute (i->first, TypeDesc::TypeVector, &(i->second)); } } void Field3DInput::read_one_layer (FieldRes::Ptr field, layerrecord &lay, TypeDesc datatype, size_t layernum) { lay.name = field->name; lay.attribute = field->attribute; lay.datatype = datatype; lay.extents = field->extents(); lay.dataWindow = field->dataWindow(); lay.field = field; // Generate a unique name for the layer. Field3D files can have // multiple partitions (aka fields) with the same name, and // different partitions can each have attributes (aka layers) with // identical names. The convention is that if there are duplicates, // insert a number to disambiguate. int duplicates = 0; for (int i = 0; i < (int)layernum; ++i) if (m_layers[i].name == lay.name && m_layers[i].attribute == lay.attribute) ++duplicates; if (! duplicates && lay.name == lay.attribute) lay.unique_name = lay.name; else lay.unique_name = duplicates ? Strutil::format ("%s.%u:%s", lay.name, duplicates+1, lay.attribute) : Strutil::format ("%s:%s", lay.name, lay.attribute); lay.spec = ImageSpec(); // Clear everything with default constructor lay.spec.format = lay.datatype; if (lay.vecfield) { lay.spec.nchannels = 3; lay.spec.channelnames.push_back (lay.attribute + ".x"); lay.spec.channelnames.push_back (lay.attribute + ".y"); lay.spec.channelnames.push_back (lay.attribute + ".z"); } else { lay.spec.channelnames.push_back (lay.attribute); lay.spec.nchannels = 1; } lay.spec.x = lay.dataWindow.min.x; lay.spec.y = lay.dataWindow.min.y; lay.spec.z = lay.dataWindow.min.z; lay.spec.width = lay.dataWindow.max.x - lay.dataWindow.min.x + 1; lay.spec.height = lay.dataWindow.max.y - lay.dataWindow.min.y + 1; lay.spec.depth = lay.dataWindow.max.z - lay.dataWindow.min.z + 1; lay.spec.full_x = lay.extents.min.x; lay.spec.full_y = lay.extents.min.y; lay.spec.full_z = lay.extents.min.z; lay.spec.full_width = lay.extents.max.x - lay.extents.min.x + 1; lay.spec.full_height = lay.extents.max.y - lay.extents.min.y + 1; lay.spec.full_depth = lay.extents.max.z - lay.extents.min.z + 1; // Always appear tiled int b = 0; if (lay.fieldtype == f3dpvt::Sparse) { if (datatype == TypeDesc::FLOAT) b = blocksize(field); else if (datatype == TypeDesc::HALF) b = blocksize(field); else if (datatype == TypeDesc::DOUBLE) b = blocksize(field); } if (b) { // There was a block size found lay.spec.tile_width = b; lay.spec.tile_height = b; lay.spec.tile_depth = b; } else { // Make the tiles be the volume size lay.spec.tile_width = lay.spec.width; lay.spec.tile_height = lay.spec.height; lay.spec.tile_depth = lay.spec.depth; } ASSERT (lay.spec.tile_width > 0 && lay.spec.tile_height > 0 && lay.spec.tile_depth > 0); lay.spec.attribute ("ImageDescription", lay.unique_name); lay.spec.attribute ("oiio:subimagename", lay.unique_name); lay.spec.attribute ("field3d:partition", lay.name); lay.spec.attribute ("field3d:layer", lay.attribute); lay.spec.attribute ("field3d:fieldtype", field->className()); FieldMapping::Ptr mapping = field->mapping(); lay.spec.attribute ("field3d:mapping", mapping->className()); MatrixFieldMapping::Ptr matrixMapping = boost::dynamic_pointer_cast(mapping); if (matrixMapping) { Imath::M44d md = matrixMapping->localToWorld(); lay.spec.attribute ("field3d:localtoworld", TypeDesc(TypeDesc::DOUBLE, TypeDesc::MATRIX44), &md); Imath::M44f m ((float)md[0][0], (float)md[0][1], (float)md[0][2], (float)md[0][3], (float)md[1][0], (float)md[1][1], (float)md[1][2], (float)md[1][3], (float)md[2][0], (float)md[2][1], (float)md[2][2], (float)md[2][3], (float)md[3][0], (float)md[3][1], (float)md[3][2], (float)md[3][3]); m = m.inverse(); lay.spec.attribute ("worldtocamera", TypeDesc::TypeMatrix, &m); } // Other metadata read_metadata (m_input->metadata(), lay.spec); // global read_metadata (field->metadata(), lay.spec); // specific to this field } /// Read all layers from the open file that match the data type. /// Find the list of scalar and vector fields. template void Field3DInput::read_layers (TypeDesc datatype) { typedef typename Field::Vec SFieldList; SFieldList sFields = m_input->readScalarLayers(); if (sFields.size() > 0) { for (typename SFieldList::const_iterator i = sFields.begin(); i != sFields.end(); ++i) { size_t layernum = m_layers.size(); m_layers.resize (layernum+1); layerrecord &lay (m_layers.back()); if (field_dynamic_cast >(*i)) lay.fieldtype = f3dpvt::Dense; else if (field_dynamic_cast >(*i)) lay.fieldtype = f3dpvt::Sparse; else ASSERT (0 && "unknown field type"); read_one_layer (*i, lay, datatype, layernum); } } // Note that both scalar and vector calls take the scalar type as argument typedef typename Field >::Vec VFieldList; VFieldList vFields = m_input->readVectorLayers(); if (vFields.size() > 0) { for (typename VFieldList::const_iterator i = vFields.begin(); i != vFields.end(); ++i) { size_t layernum = m_layers.size(); m_layers.resize (layernum+1); layerrecord &lay (m_layers.back()); typedef FIELD3D_VEC3_T VecData_T; if (field_dynamic_cast >(*i)) lay.fieldtype = f3dpvt::Dense; else if (field_dynamic_cast >(*i)) lay.fieldtype = f3dpvt::Sparse; else if (field_dynamic_cast >(*i)) lay.fieldtype = f3dpvt::MAC; else ASSERT (0 && "unknown field type"); lay.vecfield = true; read_one_layer (*i, lay, datatype, layernum); } } } bool Field3DInput::valid_file (const std::string &filename) const { if (! Filesystem::is_regular (filename)) return false; oiio_field3d_initialize (); bool ok = false; Field3DInputFile *input = NULL; { spin_lock lock (field3d_mutex()); input = new Field3DInputFile; try { ok = input->open (filename); } catch (...) { ok = false; } delete input; } return ok; } bool Field3DInput::open (const std::string &name, ImageSpec &newspec) { if (m_input) close(); if (! Filesystem::is_regular (name)) return false; oiio_field3d_initialize (); { spin_lock lock (field3d_mutex()); m_input = new Field3DInputFile; bool ok = false; try { ok = m_input->open (name); } catch (...) { ok = false; } if (! ok) { delete m_input; m_input = NULL; m_name.clear (); return false; } m_name = name; std::vector partitions; m_input->getPartitionNames (partitions); // There's no apparent way to loop over all fields and layers -- the // Field3D library is templated so that it's most natural to have // the "outer loop" be the data type. So for each type, we augment // our list of layers with the matching ones in the file. read_layers (TypeDesc::HALF); read_layers (TypeDesc::FLOAT); read_layers (TypeDesc::DOUBLE); } m_nsubimages = (int) m_layers.size(); return seek_subimage (0, 0, newspec); } bool Field3DInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec) { if (subimage < 0 || subimage >= m_nsubimages) // out of range return false; if (miplevel != 0) return false; m_subimage = subimage; m_spec = m_layers[subimage].spec; newspec = m_spec; return true; } bool Field3DInput::close () { spin_lock lock (field3d_mutex()); if (m_input) { m_input->close (); delete m_input; // implicity closes m_input = NULL; m_name.clear (); } init (); // Reset to initial state return true; } bool Field3DInput::read_native_scanline (int y, int z, void *data) { // scanlines not supported return false; } template bool Field3DInput::readtile (int x, int y, int z, T *data) { layerrecord &lay (m_layers[m_subimage]); int xend = std::min (x+lay.spec.tile_width, lay.spec.x+lay.spec.width); int yend = std::min (y+lay.spec.tile_height, lay.spec.y+lay.spec.height); int zend = std::min (z+lay.spec.tile_depth, lay.spec.z+lay.spec.depth); { typename DenseField::Ptr f = field_dynamic_cast > (lay.field); if (f) { //std::cerr << "readtile dense " << x << '-' << xend << " x " // << y << '-' << yend << " x " << z << '-' << zend << "\n"; for (int k = z; k < zend; ++k) { for (int j = y; j < yend; ++j) { T *d = data + (k-z)*(lay.spec.tile_width*lay.spec.tile_height) + (j-y)*lay.spec.tile_width; for (int i = x; i < xend; ++i, ++d) { *d = f->fastValue (i, j, k); } } } return true; } } { typename SparseField::Ptr f = field_dynamic_cast > (lay.field); if (f) { //std::cerr << "readtile sparse " << x << '-' << xend << " x " // << y << '-' << yend << " x " << z << '-' << zend << "\n"; for (int k = z; k < zend; ++k) { for (int j = y; j < yend; ++j) { T *d = data + (k-z)*(lay.spec.tile_width*lay.spec.tile_height) + (j-y)*lay.spec.tile_width; for (int i = x; i < xend; ++i, ++d) { *d = f->fastValue (i, j, k); } } } return true; } } return false; } bool Field3DInput::read_native_tile (int x, int y, int z, void *data) { spin_lock lock (field3d_mutex()); layerrecord &lay (m_layers[m_subimage]); if (lay.datatype == TypeDesc::FLOAT) { if (lay.vecfield) return readtile (x, y, z, (FIELD3D_VEC3_T *)data); else return readtile (x, y, z, (float *)data); } else if (lay.datatype == TypeDesc::HALF) { if (lay.vecfield) return readtile (x, y, z, (FIELD3D_VEC3_T *)data); else return readtile (x, y, z, (FIELD3D_NS::half *)data); } else if (lay.datatype == TypeDesc::DOUBLE) { if (lay.vecfield) return readtile (x, y, z, (FIELD3D_VEC3_T *)data); else return readtile (x, y, z, (double *)data); } return false; } void Field3DInput::worldToLocal (const Imath::V3f &wsP, Imath::V3f &lsP, float time) const { spin_lock lock (field3d_mutex()); const layerrecord &lay (m_layers[m_subimage]); V3d Pw (wsP[0], wsP[1], wsP[2]); V3d Pl; lay.field->mapping()->worldToLocal(Pw, Pl, time); lsP[0] = (float) Pl[0]; lsP[1] = (float) Pl[1]; lsP[2] = (float) Pl[2]; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/cineon.imageio/0000755000175000017500000000000013151711064020000 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/cineon.imageio/CMakeLists.txt0000644000175000017500000000036613151711064022545 0ustar mfvmfvadd_oiio_plugin (cineoninput.cpp cineonoutput.cpp libcineon/Cineon.cpp libcineon/OutStream.cpp libcineon/Codec.cpp libcineon/Reader.cpp libcineon/Writer.cpp libcineon/CineonHeader.cpp libcineon/ElementReadStream.cpp libcineon/InStream.cpp) openimageio-1.7.17~dfsg0.orig/src/cineon.imageio/libcineon/0000755000175000017500000000000013151711064021742 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/cineon.imageio/libcineon/CineonHeader.h0000644000175000017500000011472613151711064024452 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /*! \file CineonHeader.h */ /* * Copyright (c) 2010, Patrick A. Palmer and Leszek Godlewski. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ // Cineon graphic file format v4.5 #ifndef _CINEON_CINEONHEADER_H #define _CINEON_CINEONHEADER_H 1 #include #include #if defined(_MSC_VER) && _MSC_VER < 1600 typedef __int32 int32_t; typedef unsigned __int32 uint32_t; typedef __int64 int64_t; typedef unsigned __int64 uint64_t; #else # include #endif #include "CineonStream.h" /*! * \def SPEC_VERSION * \brief Cineon format version */ #define SPEC_VERSION "V4.5" /*! * \def MAX_ELEMENTS * \brief Maximum number of image elements */ #define MAX_ELEMENTS 8 /*! * \def MAX_COMPONENTS * \brief Maximum number of components per image element */ #define MAX_COMPONENTS 8 /*! * \def MAGIC_COOKIE * \brief HEX value of 0x802A5FD7 */ #define MAGIC_COOKIE 0x802A5FD7 namespace cineon { // DPX data types /*! * \typedef unsigned char U8 * \brief Unsigned 8 bit integer */ typedef unsigned char U8; /*! * \typedef unsigned short U16 * \brief Unsigned 16 bit integer */ typedef unsigned short U16; /*! * \typedef unsigned int U32 * \brief Unsigned 32 bit integer */ typedef unsigned int U32; /*! * \typedef signed char U32 * \brief Signed 32 bit integer */ typedef signed int S32; /*! * \typedef unsigned long long U64 * \brief Unsigned 64 bit integer */ typedef uint64_t U64; /*! * \typedef float R32 * \brief 32 bit floating point number */ typedef float R32; /*! * \typedef float R64 * \brief 64 bit floating point number */ typedef double R64; /*! * \typedef char ASCII * \brief ASCII character */ typedef char ASCII; /*! * \enum DataSize * \brief Component Data Storage Data Type */ enum DataSize { kByte, //!< 8-bit size component kWord, //!< kInt, //!< kLongLong //!< 64-bit integer }; /*! * \enum Orientation * \brief Image Orientation Code */ enum Orientation { kLeftToRightTopToBottom = 0, //!< Oriented left to right, top to bottom kRightToLeftTopToBottom = 1, //!< Oriented right to left, top to bottom kLeftToRightBottomToTop = 2, //!< Oriented left to right, bottom to top kRightToLeftBottomToTop = 3, //!< Oriented right to left, bottom to top kTopToBottomLeftToRight = 4, //!< Oriented top to bottom, left to right kTopToBottomRightToLeft = 5, //!< Oriented top to bottom, right to left kBottomToTopLeftToRight = 6, //!< Oriented bottom to top, left to right kBottomToTopRightToLeft = 7, //!< Oriented bottom to top, right to left kUndefinedOrientation = 0xff //!< Undefined orientation }; /*! * \enum Descriptor * \brief Image element Descriptor (second byte) */ enum Descriptor { kGrayscale = 0, //!< Grayscale kPrintingDensityRed = 1, //!< Red kPrintingDensityGreen = 2, //!< Green kPrintingDensityBlue = 3, //!< Blue kRec709Red = 4, //!< Red kRec709Green = 5, //!< Green kRec709Blue = 6, //!< Blue kUndefinedDescriptor = 0xff //!< Undefined descriptor }; /*! * \enum Interleave * \brief Component interleaving method */ enum Interleave { kPixel = 0, //!< Pixel interleave (rgbrgbrgb...) kLine = 1, //!< Line interleave (rrr.ggg.bbb.rrr.ggg.bbb.) kChannel = 2 //!< Channel interleave (rrr..ggg..bbb..) }; /*! * \enum Packing * \brief Component data packing method */ enum Packing { kPacked = 0, //!< Use all bits (tight packing) kByteLeft = 1, //!< Byte (8-bit) boundary, left justified kByteRight = 2, //!< Byte (8-bit) boundary, right justified kWordLeft = 3, //!< Word (16-bit) boundary, left justified kWordRight = 4, //!< Word (16-bit) boundary, right justified kLongWordLeft = 5, //!< Longword (32-bit) boundary, left justified kLongWordRight = 6, //!< Longword (32-bit) boundary, right justified kPackAsManyAsPossible = 0x80 //!< Bitflag - if present, pack as many fields as possible per cell, only one otherwise }; /*! * \struct ImageElement * \brief Data Structure for Image Element */ struct ImageElement { U8 designator[2]; //!< Channel descriptor \see Descriptor U8 bitDepth; //!< Bits per pixel U8 unused1; //!< Unused U32 pixelsPerLine; //!< Pixels per line U32 linesPerElement; //!< Lines per element R32 lowData; //!< Reference low data code value R32 lowQuantity; //!< Reference low quantity represented R32 highData; //!< Reference high data code value R32 highQuantity; //!< Reference high quantity represented /*! * \brief Constructor */ ImageElement(); }; /*! * \struct GenericHeader * \brief Generic File and Image Header Information */ struct GenericHeader { /*! * \name File Information Members */ //@{ U32 magicNumber; //!< Indicates start of DPX image file and is used to determine byte order. U32 imageOffset; //!< Offset to image data (in bytes) U32 genericSize; //!< Generic Header length (in bytes) U32 industrySize; //!< Industry Header length (in bytes) U32 userSize; //!< User defined header length (in bytes) U32 fileSize; //!< Total file size (in bytes) ASCII version[8]; //!< Version number of header format ASCII fileName[100]; //!< File name ASCII creationDate[12]; //!< Create date /see DateTimeFormat ASCII creationTime[12]; //!< Create time /see DateTimeFormat ASCII reserved1[36]; //!< Reserved /* end of group */ //@} /*! * \name Image Information Members */ //@{ U8 imageOrientation; //!< Image orientation \see Orientation U8 numberOfElements; //!< Number of elements (1-8) U8 unused1[2]; //!< Unused (word alignment) ImageElement chan[MAX_ELEMENTS]; //!< Image element data structures R32 whitePoint[2]; //!< White point (x, y pair) R32 redPrimary[2]; //!< Red primary chromaticity (x, y pair) R32 greenPrimary[2]; //!< Green primary chromaticity (x, y pair) R32 bluePrimary[2]; //!< Blue primary chromaticity (x, y pair) ASCII labelText[200]; //!< Label text ASCII reserved2[28]; //!< Reserved U8 interleave; //!< Data interleave \see Interleave U8 packing; //!< Packing \see Packing U8 dataSign; //!< Data sign (0 = unsigned, 1 = signed) U8 imageSense; //!< Image sense (0 = positive image, 1 = negative image) U32 endOfLinePadding; //!< End-of-Line Padding U32 endOfImagePadding; //!< End-of-Image Padding ASCII reserved3[20]; /* end of group */ //@} /*! * \name Image Origination Members */ //@{ S32 xOffset; //!< X offset S32 yOffset; //!< Y offset ASCII sourceImageFileName[100]; //!< Source image file name ASCII sourceDate[12]; //!< Source date /see DateTimeFormat ASCII sourceTime[12]; //!< Source time /see DateTimeFormat ASCII inputDevice[64]; //!< Input device name ASCII inputDeviceModelNumber[32]; //!< Input device model number ASCII inputDeviceSerialNumber[32]; //!< Input device serial number R32 xDevicePitch; //!< X device pitch (samples/mm) R32 yDevicePitch; //!< Y device pitch (samples/mm) R32 gamma; //!< Gamma ASCII reserved4[40]; //!< Reserved /* end of group */ //@} /*! * \brief Constructor */ GenericHeader(); /*! * \brief Reset class to initial state */ void Reset(); /*! * \name File Information Methods */ //@{ /*! * \brief Get magic number, used for byte ordering identification * \return magic number */ inline U32 MagicNumber() const; /*! * \brief Get the offset in bytes to the start of the first image element * \return offset */ inline U32 ImageOffset() const; /*! * \brief Set the offset in bytes to the start of the first image element * \param offset offset in bytes */ inline void SetImageOffset(const U32 offset); /*! * \brief Get the size of the generic section within the header * \return generic header size in bytes */ inline U32 GenericSize() const; /*! * \brief Get the size of the industry section within the header * \return industry header size in bytes */ inline U32 IndustrySize() const; /*! * \brief Get the size of the user data * \return user data size in bytes */ inline U32 UserSize() const; /*! * \brief Set the size of the user data * \param size user data size in bytes */ inline void SetUserSize(const U32 size); /*! * \brief Get the size of the entire file * \return file size in bytes */ inline U32 FileSize() const; /*! * \brief Set the size of the entire file * \param fs file size in bytes */ inline void SetFileSize(const U32 fs); /*! * \brief Get current version string of header * \param v buffer to place string, needs to be at least 8+1 bytes long */ inline void Version(char *v) const; /*! * \brief Set the version string * \param v version string */ inline void SetVersion(const char *v); /*! * \brief Get the file name * \param fn buffer to store filename (100+1 chars) */ inline void FileName(char *fn) const; /*! * \brief Set the file name * \param fn buffer with filename */ inline void SetFileName(const char *fn); /*! * \brief Get the creation time/date * \param ct buffer to store creation time/date (24+1 chars) */ inline void CreationDate(char *ct) const; /*! * \brief Set the creation time/date * \param ct buffer with creation time/date */ inline void SetCreationDate(const char *ct); /*! * \brief Get the creation time/date * \param ct buffer to store creation time/date (24+1 chars) */ inline void CreationTime(char *ct) const; /*! * \brief Set the creation time/date * \param ct buffer with creation time/date */ inline void SetCreationTime(const char *ct); /*! * \brief Set the creation time/date * \param secs number of seconds since January 1, 1970 00:00 */ void SetCreationTimeDate(const long secs); /* end of group */ //@} /*! * \name Image Information Methods */ //@{ /*! * \brief Get the image orientation * \return orientation enum */ inline Orientation ImageOrientation() const; /*! * \brief Set the image orientation * \param orient orientation */ inline void SetImageOrientation(const Orientation orient); /*! * \brief Get the number of elements * \return element count */ inline U8 NumberOfElements() const; /*! * \brief Set the number of elements * \param num element count */ inline void SetNumberOfElements(const U8 num); /*! * \brief Get the first byte of the channel designator - metric info * \param i element index (0-7) * \return 0 = universal metric, 1-254 = vendor-specific */ inline U8 Metric(const int i) const; /*! * \brief Set the first byte of the channel designator - metric info * \param i element index (0-7) * \param ppl metric */ inline void SetMetric(const int i, const U8 m); /*! * \brief Get the second byte of the channel designator * \param i element index (0-7) * \return channel descriptor */ inline Descriptor ImageDescriptor(const int i) const; /*! * \brief Set the second byte of the channel designator * \param i element index (0-7) * \param d channel descriptor */ inline void SetImageDescriptor(const int i, const Descriptor d); /*! * \brief Get the bits per pixel * \param i element index (0-7) * \return bit count */ inline U8 BitDepth(const int i) const; /*! * \brief Set the bits per pixel * \param i element index (0-7) * \param bpp bit count */ inline void SetBitDepth(const int i, const U8 bpp); /*! * \brief Get the pixels per line * \param i element index (0-7) * \return pixel count */ inline U32 PixelsPerLine(const int i) const; /*! * \brief Set the pixels per line * \param i element index (0-7) * \param ppl pixel count */ inline void SetPixelsPerLine(const int i, const U32 ppl); /*! * \brief Get the lines per element * \param i element index (0-7) * \return lines count */ inline U32 LinesPerElement(const int i) const; /*! * \brief Set the lines per element * \param i element index (0-7) * \param lpe lines count */ inline void SetLinesPerElement(const int i, const U32 lpe); /*! * \brief Get the minimum data value * \param i element index (0-7) * \return minimum value */ inline R32 LowData(const int i) const; /*! * \brief Set the minimum data value * \param i element index (0-7) * \param data minimum value */ inline void SetLowData(const int i, const R32 data); /*! * \brief Get the quantity of minimum data value * \param i element index (0-7) * \return quantity */ inline R32 LowQuantity(const int i) const; /*! * \brief Set the quantity of minimum data value * \param i element index (0-7) * \param quant quantity */ inline void SetLowQuantity(const int i, const R32 quant); /*! * \brief Get the maximum data value * \param i element index (0-7) * \return maximum value */ inline R32 HighData(const int i) const; /*! * \brief Set the maximum data value * \param i element index (0-7) * \param data maximum value */ inline void SetHighData(const int i, const R32 data); /*! * \brief Get the quantity of maximum data value * \param i element index (0-7) * \return quantity */ inline R32 HighQuantity(const int i) const; /*! * \brief Set the quantity of maximum data value * \param i element index (0-7) * \param quant quantity */ inline void SetHighQuantity(const int i, const R32 quant); /*! * \brief Get the white point primary x, y pair * \param xy buffer to store the x, y pair (2 32-bit floats) */ inline void WhitePoint(R32 xy[2]) const; /*! * \brief Set the white point primary x, y pair * \param xy the x, y pair (2 32-bit floats) */ inline void SetWhitePoint(const R32 xy[2]); /*! * \brief Get the red primary x, y pair * \param xy buffer to store the x, y pair (2 32-bit floats) */ inline void RedPrimary(R32 xy[2]) const; /*! * \brief Set the red primary x, y pair * \param xy the x, y pair (2 32-bit floats) */ inline void SetRedPrimary(const R32 xy[2]); /*! * \brief Get the green primary x, y pair * \param xy buffer to store the x, y pair (2 32-bit floats) */ inline void GreenPrimary(R32 xy[2]) const; /*! * \brief Set the green primary x, y pair * \param xy the x, y pair (2 32-bit floats) */ inline void SetGreenPrimary(const R32 xy[2]); /*! * \brief Get the blue primary x, y pair * \param xy buffer to store the x, y pair (2 32-bit floats) */ inline void BluePrimary(R32 xy[2]) const; /*! * \brief Set the blue primary x, y pair * \param xy the x, y pair (2 32-bit floats) */ inline void SetBluePrimary(const R32 xy[2]); /*! * \brief Get the label text * \param ct buffer to store label text (200 chars) */ inline void LabelText(char *ct) const; /*! * \brief Set the label text * \param ct buffer with label text */ inline void SetLabelText(const char *ct); /*! * \brief Get the data interleave mode * \return interleave method */ inline Interleave ImageInterleave() const; /*! * \brief Set the data intearleave mode * \param inter intearleave method */ inline void SetImageInterleave(const Interleave inter); /*! * \brief Get the data packing mode * \return packing method */ inline Packing ImagePacking() const; /*! * \brief Set the data packing mode * \param pack packing method */ inline void SetImagePacking(const Packing pack); /*! * \brief Get the data sign (0 = unsigned, 1 = signed) * \return data sign */ inline U8 DataSign() const; /*! * \brief Set the data sign (0 = unsigned, 1 = signed) * \param sign data sign */ inline void SetDataSign(const U8 sign); /*! * \brief Get the image sense (0 = positive, 1 = negative) * \return image sense */ inline U8 ImageSense() const; /*! * \brief Set the image sense (0 = positive, 1 = negative) * \param sense image sense */ inline void SetImageSense(const U8 sense); /*! * \brief Get the number of bytes padding the end of each line * \param i element index (0-7) * \return count */ inline U32 EndOfLinePadding() const; /*! * \brief Set the number of bytes padding the end of each line * \param i element index (0-7) * \param eolp count */ inline void SetEndOfLinePadding(const U32 eolp); /*! * \brief Get the number of bytes padding the end of the image element * \param i element index (0-7) * \return count */ inline U32 EndOfImagePadding() const; /*! * \brief Set the number of bytes padding the end of the image element * \param i element index (0-7) * \param eoip count */ inline void SetEndOfImagePadding(const U32 eoip); /* end of group */ //@} /*! * \name Image Origination Methods */ //@{ /*! * \brief Get the line offset (in pixels) from the first pixel in original image * \return offset count */ inline S32 XOffset() const; /*! * \brief Set the line offset (in pixels) from the first pixel in original image * \param offset offset count */ inline void SetXOffset(const S32 offset); /*! * \brief Get the frame offset (in lines) from the first line in original image * \return offset count */ inline S32 YOffset() const; /*! * \brief Set the frame offset (in lines) from the first line in original image * \param offset offset count */ inline void SetYOffset(const S32 offset); /*! * \brief Get the source image file name that this image was extracted * \param fn buffer to write source file name (100+1) */ inline void SourceImageFileName(char *fn) const; /*! * \brief Set the source image file name that this image was extracted * \param fn buffer with source file name */ inline void SetSourceImageFileName(const char *fn); /*! * \brief Get the source image time and date that this image was extracted * \param td buffer to write time/date string (24+1) */ inline void SourceDate(char *td) const; /*! * \brief Set the source image time and date that this image was extracted * \param td buffer with time/date string */ inline void SetSourceDate(const char *td); /*! * \brief Get the source image time and date that this image was extracted * \param td buffer to write time/date string (24+1) */ inline void SourceTime(char *td) const; /*! * \brief Set the source image time and date that this image was extracted * \param td buffer with time/date string */ inline void SetSourceTime(const char *td); /*! * \brief Set the source image time and date that this image was extracted * \param secs number of seconds since January 1, 1970 00:00 */ void SetSourceTimeDate(const long secs); /*! * \brief Get the input device name * \param dev buffer to write device (64+1) */ inline void InputDevice(char *dev) const; /*! * \brief Set the input device name * \param dev buffer with device name */ inline void SetInputDevice(const char *dev); /*! * \brief Get the input device model number * \param sn buffer to write device model number (32+1) */ inline void InputDeviceModelNumber(char *sn) const; /*! * \brief Set the input device model number * \param sn buffer with device model number */ inline void SetInputDeviceModelNumber(const char *sn); /*! * \brief Get the input device serial number * \param sn buffer to write device serial number (32+1) */ inline void InputDeviceSerialNumber(char *sn) const; /*! * \brief Set the input device serial number * \param sn buffer with device serial number */ inline void SetInputDeviceSerialNumber(const char *sn); /*! * \brief Get the horizontal pitch of the device * \return pitch in samples/mm */ inline R32 XDevicePitch() const; /*! * \brief Set the horizontal pitch of the device * \param size pitch in samples/mm */ inline void SetXDevicePitch(const R32 size); /*! * \brief Get the veritcal pitch of the device * \return pitch in samples/mm */ inline R32 YDevicePitch() const; /*! * \brief Set the vertical pitch of the device * \param size pitch in samples/mm */ inline void SetYDevicePitch(const R32 size); /*! * \brief Get the gamma correction exponent * \return gamma exponent */ inline R32 Gamma() const; /*! * \brief Set the gamma correction exponent * \param gamma gamma exponent */ inline void SetGamma(const R32 gamma); /* end of group */ //@} /*! * \brief Number of Active Elements in the Image * \return element count */ int ImageElementCount() const; /*! * \brief Set member numberOfElements based on channel structure */ void CalculateNumberOfElements(); /*! * \brief DataSize required for individual image element components * \return datasize of element */ DataSize ComponentDataSize(const int element) const; /*! * \brief Byte count of data element components * \return byte count */ int ComponentByteCount(const int element) const; /* * \brief Byte size for each DataSize * \return byte count */ static int DataSizeByteCount(const DataSize ds); }; /*! * \struct IndustryHeader * \brief Motion Picture and Television Industry Specific Information */ struct IndustryHeader { /*! * \name Motion Picture Industry Specific Members */ //@{ U8 filmManufacturingIdCode; //!< Film edge code manufacturing ID code U8 filmType; //!< Film edge code type U8 perfsOffset; //!< Film edge code offset in perfs U8 unused1; //!< Unused (word alignment) U32 prefix; //!< Film edge code prefix U32 count; //!< Film edge code count ASCII format[32]; //!< Format string, e.g. Academy U32 framePosition; //!< Frame position in sequence R32 frameRate; //!< Frame rate of original (frame / sec) ASCII frameId[32]; //!< Frame identification, e.g. keyframe ASCII slateInfo[200]; //!< Slate information ASCII reserved1[740]; //!< Reserved /* end of group */ //@} /*! * \brief Constructor */ IndustryHeader(); /*! * \brief Reset class to initial state */ void Reset(); // set/get functions for the data methods /*! * \name Motion Picture Industry Specific Methods */ //@{ /*! * \brief Get the film edge code information that is machine readable * \param edge buffer to write film edge code information (16+1 chars) */ void FilmEdgeCode(char *edge) const; /*! * \brief Set the film edge code information that is machine readable * \param edge buffer with film edge code information */ void SetFilmEdgeCode(const char *edge); /*! * \brief Get the format (e.g., Academy) * \param fmt buffer to write format information (32+1 chars) */ inline void Format(char *fmt) const; /*! * \brief Set the format (e.g., Academy) * \param fmt buffer with format information */ inline void SetFormat(const char *fmt); /*! * \brief Get the frame position in sequence * \return position */ inline U32 FramePosition() const; /*! * \brief Set the frame position in sequence * \param pos position */ inline void SetFramePosition(const U32 pos); /*! * \brief Get the frame rate (frames / second) * \return rate */ inline R32 FrameRate() const; /*! * \brief Set the frame rate (frames / second) * \param rate rate */ inline void SetFrameRate(const R32 rate); /*! * \brief Get the user-defined frame identification * \param id buffer to write frame identification (32+1 chars) */ inline void FrameId(char *id) const; /*! * \brief Set the user-defined frame identification * \param id buffer with frame identification */ inline void SetFrameId(const char *id); /*! * \brief Get the production information from the camera slate * \param slate buffer to write slate information (200+1 chars) */ inline void SlateInfo(char *slate) const; /*! * \brief Set the production information from the camera slate * \param slate buffer with slate information */ inline void SetSlateInfo(const char *slate); /* end of group */ //@} protected: U32 TCFromString(const char *str) const; }; /*! * \brief Complete DPX Header */ struct Header : public GenericHeader, public IndustryHeader { Header(); /*! * \brief Set the header data to a known start state */ void Reset(); /*! * \brief Set the Input Stream object to read header from */ bool Read(InStream *); /*! * \brief Set the Output Stream object to write header to */ bool Write(OutStream *); // write the offset within the header bool WriteOffsetData(OutStream *); /*! * \brief Validate the header */ bool Validate(); /*! * \brief Does header require endian byte swap * \return swap required true/false */ inline bool RequiresByteSwap() const; /*! * \brief Check magic cookie * \return valid true/false */ static bool ValidMagicCookie(const U32 magic); /*! * \brief Returns the size of the header * \return 2048 as defined by the standard */ const U32 Size() const; /*! * \brief Calculate all of the offset members in the header */ void CalculateOffsets(); /*! * \brief Set whether reader/writer should swap component ordering * \param swap allow swapping true/false */ void SetDatumSwap(const bool swap); // system check, used only during platform port bool Check(); /*! * \brief Height of the image (maximum of all elements' heights) adjusted for orientation * \return height */ U32 Height() const; /*! * \brief Width of the image (maximum of all elements' widths) adjusted for orientation * \param element image element * \return width */ U32 Width() const; protected: bool DetermineByteSwap(const U32 magic) const; }; inline bool Header::RequiresByteSwap() const { return this->DetermineByteSwap(this->magicNumber); } inline const U32 Header::Size() const { return 2048; } inline U32 GenericHeader::MagicNumber() const { return this->magicNumber; } inline U32 GenericHeader::ImageOffset() const { return this->imageOffset; } inline void GenericHeader::SetImageOffset(const U32 offset) { this->imageOffset = offset; } inline void GenericHeader::Version(char *v) const { ::strncpy(v, this->version, 8); v[8] = '\0'; } inline void GenericHeader::SetVersion(const char * v) { ::strncpy(this->version, v, 8); } inline U32 GenericHeader::FileSize() const { return this->fileSize; } inline void GenericHeader::SetFileSize(const U32 fs) { this->fileSize = fs; } inline U32 GenericHeader::GenericSize() const { return this->genericSize; } inline U32 GenericHeader::IndustrySize() const { return this->industrySize; } inline U32 GenericHeader::UserSize() const { return this->userSize; } inline void GenericHeader::SetUserSize(const U32 size) { this->userSize = size; } inline void GenericHeader::FileName(char *fn) const { ::strncpy(fn, this->fileName, 100); fn[100] = '\0'; } inline void GenericHeader::SetFileName(const char *fn) { ::strncpy(this->fileName, fn, 100); } inline void GenericHeader::CreationDate(char *ct) const { ::strncpy(ct, this->creationDate, 12); ct[24] = '\0'; } inline void GenericHeader::SetCreationDate(const char *ct) { ::strncpy(this->creationDate, ct, 12); } inline void GenericHeader::CreationTime(char *ct) const { ::strncpy(ct, this->creationTime, 12); ct[24] = '\0'; } inline void GenericHeader::SetCreationTime(const char *ct) { ::strncpy(this->creationTime, ct, 12); } inline Orientation GenericHeader::ImageOrientation() const { return Orientation(this->imageOrientation); } inline void GenericHeader::SetImageOrientation(const Orientation orient) { this->imageOrientation = orient; } inline U8 GenericHeader::NumberOfElements() const { return this->numberOfElements; } inline void GenericHeader::SetNumberOfElements(const U8 num) { this->numberOfElements = num; } inline U32 GenericHeader::PixelsPerLine(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return 0xffffffff; return this->chan[i].pixelsPerLine; } inline void GenericHeader::SetPixelsPerLine(const int i, const U32 ppl) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].pixelsPerLine = ppl; } inline U32 GenericHeader::LinesPerElement(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return 0xffffffff; return this->chan[i].linesPerElement; } inline void GenericHeader::SetLinesPerElement(const int i, const U32 lpe) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].linesPerElement = lpe; } inline U8 GenericHeader::DataSign() const { return this->dataSign; } inline void GenericHeader::SetDataSign(const U8 sign) { this->dataSign = sign; } inline R32 GenericHeader::LowData(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return std::numeric_limits::infinity(); return this->chan[i].lowData; } inline void GenericHeader::SetLowData(const int i, const R32 data) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].lowData = data; } inline R32 GenericHeader::LowQuantity(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return std::numeric_limits::max(); return this->chan[i].lowQuantity; } inline void GenericHeader::SetLowQuantity(const int i, const R32 quant) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].lowQuantity = quant; } inline R32 GenericHeader::HighData(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return std::numeric_limits::max(); return this->chan[i].highData; } inline void GenericHeader::SetHighData(const int i, const R32 data) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].highData = data; } inline R32 GenericHeader::HighQuantity(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return std::numeric_limits::max(); return this->chan[i].highQuantity; } inline void GenericHeader::SetHighQuantity(const int i, const R32 quant) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].highQuantity = quant; } inline U8 GenericHeader::Metric(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return 0xff; return this->chan[i].designator[0]; } inline void GenericHeader::SetMetric(const int i, const U8 m) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].designator[0] = m; } inline Descriptor GenericHeader::ImageDescriptor(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return Descriptor(0xff); return Descriptor(this->chan[i].designator[1]); } inline void GenericHeader::SetImageDescriptor(const int i, const Descriptor desc) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].designator[1] = (U8)desc; } inline U8 GenericHeader::BitDepth(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return 0xff; return this->chan[i].bitDepth; } inline void GenericHeader::SetBitDepth(const int i, const U8 depth) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].bitDepth = depth; } inline Interleave GenericHeader::ImageInterleave() const { return Interleave(this->interleave); } inline void GenericHeader::SetImageInterleave(const Interleave inter) { this->interleave = (U8)inter; } inline Packing GenericHeader::ImagePacking() const { return Packing(this->packing); } inline void GenericHeader::SetImagePacking(const Packing pack) { this->packing = (U8)pack; } inline U32 GenericHeader::EndOfLinePadding() const { if (this->endOfLinePadding == 0xffffffff) return 0; return this->endOfLinePadding; } inline void GenericHeader::SetEndOfLinePadding(const U32 eolp) { this->endOfLinePadding = eolp; } inline U32 GenericHeader::EndOfImagePadding() const { if (this->endOfImagePadding == 0xffffffff) return 0; return this->endOfImagePadding; } inline void GenericHeader::SetEndOfImagePadding(const U32 eoip) { this->endOfImagePadding = eoip; } inline void GenericHeader::LabelText(char *desc) const { strncpy(desc, this->labelText, 200); } inline void GenericHeader::SetLabelText(const char *desc) { ::strncpy(this->labelText, desc, 200); } inline S32 GenericHeader::XOffset() const { return this->xOffset; } inline void GenericHeader::SetXOffset(const S32 offset) { this->xOffset = offset; } inline S32 GenericHeader::YOffset() const { return this->yOffset; } inline void GenericHeader::SetYOffset(const S32 offset) { this->yOffset = offset; } inline void GenericHeader::WhitePoint(R32 xy[2]) const { memcpy(xy, this->whitePoint, sizeof(xy[0]) * 2); } inline void GenericHeader::SetWhitePoint(const R32 xy[2]) { memcpy(this->whitePoint, xy, sizeof(this->whitePoint[0]) * 2); } inline void GenericHeader::RedPrimary(R32 xy[2]) const { memcpy(xy, this->redPrimary, sizeof(xy[0]) * 2); } inline void GenericHeader::SetRedPrimary(const R32 xy[2]) { memcpy(this->redPrimary, xy, sizeof(this->redPrimary[0]) * 2); } inline void GenericHeader::GreenPrimary(R32 xy[2]) const { memcpy(xy, this->greenPrimary, sizeof(xy[0]) * 2); } inline void GenericHeader::SetGreenPrimary(const R32 xy[2]) { memcpy(this->greenPrimary, xy, sizeof(this->greenPrimary[0]) * 2); } inline void GenericHeader::BluePrimary(R32 xy[2]) const { memcpy(xy, this->bluePrimary, sizeof(xy[0]) * 2); } inline void GenericHeader::SetBluePrimary(const R32 xy[2]) { memcpy(this->bluePrimary, xy, sizeof(this->bluePrimary[0]) * 2); } inline void GenericHeader::SourceImageFileName(char *fn) const { ::strncpy(fn, this->sourceImageFileName, 100); fn[100] = '\0'; } inline void GenericHeader::SetSourceImageFileName(const char *fn) { ::strncpy(this->sourceImageFileName, fn, 100); } inline void GenericHeader::SourceDate(char *td) const { ::strncpy(td, this->sourceDate, 12); td[12] = '\0'; } inline void GenericHeader::SetSourceDate(const char *td) { ::strncpy(this->sourceDate, td, 12); } inline void GenericHeader::SourceTime(char *td) const { ::strncpy(td, this->sourceTime, 12); td[12] = '\0'; } inline void GenericHeader::SetSourceTime(const char *td) { ::strncpy(this->sourceTime, td, 12); } inline void GenericHeader::InputDevice(char *dev) const { ::strncpy(dev, this->inputDevice, 32); dev[32] = '\0'; } inline void GenericHeader::SetInputDevice(const char *dev) { ::strncpy(this->inputDevice, dev, 32); } inline void GenericHeader::InputDeviceModelNumber(char *sn) const { ::strncpy(sn, this->inputDeviceModelNumber, 32); sn[32] = '\0'; } inline void GenericHeader::SetInputDeviceModelNumber(const char *sn) { ::strncpy(this->inputDeviceModelNumber, sn, 32); } inline void GenericHeader::InputDeviceSerialNumber(char *sn) const { ::strncpy(sn, this->inputDeviceSerialNumber, 32); sn[32] = '\0'; } inline void GenericHeader::SetInputDeviceSerialNumber(const char *sn) { ::strncpy(this->inputDeviceSerialNumber, sn, 32); } inline R32 GenericHeader::XDevicePitch() const { return this->xDevicePitch; } inline void GenericHeader::SetXDevicePitch(const R32 size) { this->xDevicePitch = size; } inline R32 GenericHeader::YDevicePitch() const { return this->yDevicePitch; } inline void GenericHeader::SetYDevicePitch(const R32 size) { this->yDevicePitch = size; } inline void IndustryHeader::Format(char *fmt) const { ::strncpy(fmt, this->format, 32); fmt[32] = '\0'; } inline void IndustryHeader::SetFormat(const char *fmt) { ::strncpy(this->format, fmt, 32); } inline U32 IndustryHeader::FramePosition() const { return this->framePosition; } inline void IndustryHeader::SetFramePosition(const U32 pos) { this->framePosition = pos; } inline R32 IndustryHeader::FrameRate() const { return this->frameRate; } inline void IndustryHeader::SetFrameRate(const R32 rate) { this->frameRate = rate; } inline void IndustryHeader::FrameId(char *id) const { ::strncpy(id, this->frameId, 32); id[32] = '\0'; } inline void IndustryHeader::SetFrameId(const char *id) { ::strncpy(this->frameId, id, 32); } inline void IndustryHeader::SlateInfo(char *slate) const { ::strncpy(slate, this->slateInfo, 100); slate[100] = '\0'; } inline void IndustryHeader::SetSlateInfo(const char *slate) { ::strncpy(this->slateInfo, slate, 100); } inline R32 GenericHeader::Gamma() const { return this->gamma; } inline void GenericHeader::SetGamma(const R32 g) { this->gamma = g; } } #endif openimageio-1.7.17~dfsg0.orig/src/cineon.imageio/libcineon/Writer.cpp0000644000175000017500000002265013151711064023727 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2010, Patrick A. Palmer and Leszek Godlewski. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "Cineon.h" #include "CineonStream.h" #include "EndianSwap.h" #include "WriterInternal.h" cineon::Writer::Writer() : fileLoc(0) { } cineon::Writer::~Writer() { } void cineon::Writer::Start() { } void cineon::Writer::SetFileInfo(const char *fileName, const char *creationDate, const char *creationTime) { if (fileName) this->header.SetFileName(fileName); if (creationDate && creationTime) { this->header.SetCreationDate(creationDate); this->header.SetCreationTime(creationTime); } else { time_t seconds = time(0); this->header.SetCreationTimeDate(seconds); } } void cineon::Writer::SetImageInfo(const U32 width, const U32 height) { this->header.SetImageOrientation(kLeftToRightTopToBottom); } // returns next available or MAX_ELEMENTS if full int cineon::Writer::NextAvailElement() const { unsigned int i; for (i = 0; i < MAX_ELEMENTS; i++) { if (this->header.ImageDescriptor(i) == kUndefinedDescriptor) break; } return i; } void cineon::Writer::SetOutStream(OutStream *fd) { this->fd = fd; } bool cineon::Writer::WriteHeader() { // calculate any header info this->header.CalculateOffsets(); // seek to the beginning of the file if (!this->fd->Seek(0, OutStream::kStart)) return false; // writing the header count this->fileLoc = this->header.Size(); return this->header.Write(fd); } void cineon::Writer::SetUserData(const long size) { // TODO } bool cineon::Writer::WriteUserData(void *data) { // XXX TODO return false; } void cineon::Writer::SetElement(const int num, const Descriptor desc, const U8 bitDepth, const U32 pixelsPerLine, const U32 linesPerElement, const R32 lowData, const R32 lowQuantity, const R32 highData, const R32 highQuantity) { // make sure the range is good if (num < 0 || num >= MAX_ELEMENTS) return; // set values this->header.SetLowData(num, lowData); this->header.SetLowQuantity(num, lowQuantity); this->header.SetHighData(num, highData); this->header.SetHighQuantity(num, highQuantity); this->header.SetImageDescriptor(num, desc); this->header.SetBitDepth(num, bitDepth); // determine if increases element count this->header.CalculateNumberOfElements(); } // the data is processed so write it straight through // argument count is total size in bytes of the passed data bool cineon::Writer::WriteElement(const int element, void *data, const long count) { // make sure the range is good if (element < 0 || element >= MAX_ELEMENTS) return false; // make sure the entry is valid if (this->header.ImageDescriptor(element) == kUndefinedDescriptor) return false; // update file ptr //this->header.SetDataOffset(element, this->fileLoc); this->fileLoc += count; // write return (this->fd->Write(data, count) > 0); } bool cineon::Writer::WriteElement(const int element, void *data) { // make sure the range is good if (element < 0 || element >= MAX_ELEMENTS) return false; // make sure the entry is valid if (this->header.ImageDescriptor(element) == kUndefinedDescriptor) return false; return this->WriteElement(element, data, this->header.ComponentDataSize(element)); } bool cineon::Writer::WriteElement(const int element, void *data, const DataSize size) { bool status = true; // make sure the range is good if (element < 0 || element >= MAX_ELEMENTS) return false; // make sure the entry is valid if (this->header.ImageDescriptor(element) == kUndefinedDescriptor) return false; // mark location in headers if (element == 0) this->header.SetImageOffset(this->fileLoc); //this->header.SetDataOffset(element, this->fileLoc); // reverse the order of the components bool reverse = false; // image parameters const U32 eolnPad = this->header.EndOfLinePadding(); const U32 eoimPad = this->header.EndOfImagePadding(); const U8 bitDepth = this->header.BitDepth(element); const U32 width = this->header.Width(); const U32 height = this->header.Height(); const int noc = this->header.NumberOfElements(); const Packing packing = this->header.ImagePacking(); // check width & height, just in case if (width == 0 || height == 0) return false; // sizeof a component in an image const int bytes = (bitDepth + 7) / 8; // allocate memory for use to write blank space char *blank = 0; if (eolnPad || eoimPad) { int bsize = eolnPad > eoimPad ? eolnPad : eoimPad; blank = new char[bsize]; memset(blank, bsize, sizeof(char)); } // can we write the entire memory chunk at once without any additional processing if ((bitDepth == 8 && size == cineon::kByte) || (bitDepth == 12 && size == cineon::kWord /*&& packing == kFilledMethodA*/) || (bitDepth == 16 && size == cineon::kWord)) { status = this->WriteThrough(data, width, height, noc, bytes, eolnPad, eoimPad, blank); if (blank) delete [] blank; return status; } else { switch (bitDepth) { case 8: if (size == cineon::kByte) this->fileLoc += WriteBuffer(this->fd, size, data, width, height, noc, packing, reverse, eolnPad, blank, status); else this->fileLoc += WriteBuffer(this->fd, size, data, width, height, noc, packing, reverse, eolnPad, blank, status); break; case 10: // are the channels stored in reverse /*if (this->header.ImageDescriptor(element) == kRGB && this->header.DatumSwap(element) && bitDepth == 10) reverse = true;*/ if (size == cineon::kWord) this->fileLoc += WriteBuffer(this->fd, size, data, width, height, noc, packing, reverse, eolnPad, blank, status); else this->fileLoc += WriteBuffer(this->fd, size, data, width, height, noc, packing, reverse, eolnPad, blank, status); break; case 12: if (size == cineon::kWord) this->fileLoc += WriteBuffer(this->fd, size, data, width, height, noc, packing, reverse, eolnPad, blank, status); else this->fileLoc += WriteBuffer(this->fd, size, data, width, height, noc, packing, reverse, eolnPad, blank, status); break; case 16: if (size == cineon::kWord) this->fileLoc += WriteBuffer(this->fd, size, data, width, height, noc, packing, reverse, eolnPad, blank, status); else this->fileLoc += WriteBuffer(this->fd, size, data, width, height, noc, packing, reverse, eolnPad, blank, status); break; default: return false; } } // if successful if (status && eoimPad) { // end of image padding this->fileLoc += eoimPad; status = (this->fd->Write(blank, eoimPad) > 0); } // rid of memory if (blank) delete [] blank; return status; } // the passed in image buffer is written to the file untouched bool cineon::Writer::WriteThrough(void *data, const U32 width, const U32 height, const int noc, const int bytes, const U32 eolnPad, const U32 eoimPad, char *blank) { bool status = true; const int count = width * height * noc; unsigned int i; unsigned char *imageBuf = reinterpret_cast(data); // file pointer location after write this->fileLoc += bytes * count + (eolnPad * height); // write data if (eolnPad) { // loop if have end of line padding for (i = 0; i < height; i++) { // write one line if (this->fd->Write(imageBuf+(width*bytes*i), bytes * width) == false) { status = false; break; } // write end of line padding if (this->fd->Write(blank, eoimPad) == false) { status = false; break; } } } else { // write data as one chunk if (this->fd->Write(imageBuf, bytes * count) == false) { status = false; } } // end of image padding if (status && eoimPad) { this->fileLoc += eoimPad; status = (this->fd->Write(blank, eoimPad) > 0); } return status; } bool cineon::Writer::Finish() { // write the file size in the header this->header.SetFileSize(this->fileLoc); // rewrite all of the offsets in the header return this->header.WriteOffsetData(this->fd); } openimageio-1.7.17~dfsg0.orig/src/cineon.imageio/libcineon/Codec.cpp0000644000175000017500000000541313151711064023466 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2010, Patrick A. Palmer and Leszek Godlewski. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "Cineon.h" #include "Codec.h" #include "ElementReadStream.h" #include "ReaderInternal.h" cineon::Codec::Codec() : scanline(0) { } cineon::Codec::~Codec() { if (this->scanline) delete [] scanline; } void cineon::Codec::Reset() { if (this->scanline) { delete [] scanline; this->scanline = 0; } } bool cineon::Codec::Read(const Header &dpxHeader, ElementReadStream *fd, const Block &block, void *data, const DataSize size) { // scanline buffer if (this->scanline == 0) { // FIXME: make this flexible enough to change per-channel differences! // get the number of components for this element descriptor const int numberOfComponents = dpxHeader.NumberOfElements(); // bit depth of the image element const int bitDepth = dpxHeader.BitDepth(0); // size of the scanline buffer is image width * number of components * bytes per component int slsize = ((numberOfComponents * dpxHeader.Width() * (bitDepth / 8 + (bitDepth % 8 ? 1 : 0))) / sizeof(U32))+1; this->scanline = new U32[slsize]; } // read the image block return ReadImageBlock(dpxHeader, this->scanline, fd, block, data, size); } openimageio-1.7.17~dfsg0.orig/src/cineon.imageio/libcineon/BaseTypeConverter.h0000644000175000017500000001121213151711064025514 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2010, Patrick A. Palmer and Leszek Godlewski. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _CINEON_BASETYPECONVERTER_H #define _CINEON_BASETYPECONVERTER_H 1 namespace cineon { // convert between all of the DPX base types in a controllable way inline void BaseTypeConverter(U8 &src, U8 &dst) { dst = src; } inline void BaseTypeConverter(U8 &src, U16 &dst) { dst = (src << 8) | src; } inline void BaseTypeConverter(U8 &src, U32 &dst) { dst = (src << 24) | (src << 16) | (src << 8) | src; } inline void BaseTypeConverter(U8 &src, U64 &dst) { dst = static_cast(src) << 56; } inline void BaseTypeConverter(U8 &src, R32 &dst) { dst = src; } inline void BaseTypeConverter(U8 &src, R64 &dst) { dst = src; } inline void BaseTypeConverter(U16 &src, U8 &dst) { dst = src >> 8; } inline void BaseTypeConverter(U16 &src, U16 &dst) { dst = src; } inline void BaseTypeConverter(U16 &src, U32 &dst) { dst = src << 16; } inline void BaseTypeConverter(U16 &src, U64 &dst) { dst = static_cast(src) << 48; } inline void BaseTypeConverter(U16 &src, R32 &dst) { dst = src; } inline void BaseTypeConverter(U16 &src, R64 &dst) { dst = src; } inline void BaseTypeConverter(U32 &src, U8 &dst) { dst = src >> 24; } inline void BaseTypeConverter(U32 &src, U16 &dst) { dst = src >> 16; } inline void BaseTypeConverter(U32 &src, U32 &dst) { dst = src; } inline void BaseTypeConverter(U32 &src, U64 &dst) { dst = static_cast(src) << 32; } inline void BaseTypeConverter(U32 &src, R32 &dst) { dst = src; } inline void BaseTypeConverter(U32 &src, R64 &dst) { dst = src; } inline void BaseTypeConverter(U64 &src, U8 &dst) { dst = src >> 56; } inline void BaseTypeConverter(U64 &src, U16 &dst) { dst = src >> 48; } inline void BaseTypeConverter(U64 &src, U32 &dst) { dst = src >> 32; } inline void BaseTypeConverter(U64 &src, U64 &dst) { dst = src; } inline void BaseTypeConverter(U64 &src, R32 &dst) { dst = src; } inline void BaseTypeConverter(U64 &src, R64 &dst) { dst = src; } inline void BaseTypeConverter(R32 &src, U8 &dst) { dst = src; } inline void BaseTypeConverter(R32 &src, U16 &dst) { dst = src; } inline void BaseTypeConverter(R32 &src, U32 &dst) { dst = src; } inline void BaseTypeConverter(R32 &src, U64 &dst) { dst = src; } inline void BaseTypeConverter(R32 &src, R32 &dst) { dst = src; } inline void BaseTypeConverter(R32 &src, R64 &dst) { dst = src; } inline void BaseTypeConverter(R64 &src, U8 &dst) { dst = src; } inline void BaseTypeConverter(R64 &src, U16 &dst) { dst = src; } inline void BaseTypeConverter(R64 &src, U32 &dst) { dst = src; } inline void BaseTypeConverter(R64 &src, U64 &dst) { dst = src; } inline void BaseTypeConverter(R64 &src, R32 &dst) { dst = src; } inline void BaseTypeConverter(R64 &src, R64 &dst) { dst = src; } inline void BaseTypeConvertU10ToU16(U16 &src, U16 &dst) { dst = (src << 6) | (src >> 4); } inline void BaseTypeConvertU12ToU16(U16 &src, U16 &dst) { dst = (src << 4) | (src >> 8); } } #endif openimageio-1.7.17~dfsg0.orig/src/cineon.imageio/libcineon/CineonStream.h0000644000175000017500000001044513151711064024506 0ustar mfvmfv/// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /*! \file CineonStream.h */ /* * Copyright (c) 2010, Patrick A. Palmer and Leszek Godlewski. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _CINEON_CINEONSTREAM_H #define _CINEON_CINEONSTREAM_H 1 #include namespace cineon { /*! * \class InStream * \brief Input Stream for reading files */ class InStream { public: /*! * \enum Origin * \brief file pointing positioning offset */ enum Origin { kStart, //!< beginning of the file kCurrent, //!< current file pointer kEnd //!< end of the file }; /*! * \brief Constructor */ InStream(); /*! * \brief Destructor */ virtual ~InStream(); /*! * \brief Open file * \param fn File name * \return success true/false */ virtual bool Open(const char * fn); /*! * \brief Close file */ virtual void Close(); /*! * \brief Rewind file pointer to beginning of file */ virtual void Rewind(); /*! * \brief Read data from file * \param buf data buffer * \param size bytes to read * \return number of bytes read */ virtual size_t Read(void * buf, const size_t size); /*! * \brief Read data from file without any buffering as fast as possible * \param buf data buffer * \param size bytes to read * \return number of bytes read */ virtual size_t ReadDirect(void * buf, const size_t size); /*! * \brief Query if end of file has been reached * \return end of file true/false */ virtual bool EndOfFile() const; /*! * \brief Seek to a position in the file * \param offset offset from originating position * \param origin originating position * \return success true/false */ virtual bool Seek(long offset, Origin origin); protected: FILE *fp; }; /*! * \class OutStream * \brief Output Stream for writing files */ class OutStream { public: /*! * \enum Origin * \brief file pointing positioning offset */ enum Origin { kStart, //!< beginning of the file kCurrent, //!< current file pointer kEnd //!< end of the file }; /*! * \brief Constructor */ OutStream(); /*! * \brief Destructor */ virtual ~OutStream(); /*! * \brief Open file * \param fn File name * \return success true/false */ virtual bool Open(const char *fn); /*! * \brief Close file */ virtual void Close(); /*! * \brief Write data to file * \param buf data buffer * \param size bytes to write * \return number of bytes written */ virtual size_t Write(void * buf, const size_t size); /*! * \brief Seek to a position in the file * \param offset offset from originating position * \param origin originating position * \return success true/false */ virtual bool Seek(long offset, Origin origin); /*! * \brief Flush any buffers */ virtual void Flush(); protected: FILE *fp; }; } #endif openimageio-1.7.17~dfsg0.orig/src/cineon.imageio/libcineon/EndianSwap.h0000644000175000017500000000635313151711064024153 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2010, Patrick A. Palmer and Leszek Godlewski. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _CINEON_ENDIANSWAP_H #define _CINEON_ENDIANSWAP_H 1 namespace cineon { template T SwapBytes(T& value) { unsigned char *pe, *ps = reinterpret_cast(&value); unsigned char c; size_t s = (sizeof(T)); pe = ps + s - 1; for (size_t i = s/2; i > 0; i--) { c = *ps; *ps = *pe; *pe = c; ps++; pe--; } return value; } template <> inline unsigned short SwapBytes( unsigned short& value ) { unsigned char *p = reinterpret_cast(&value); unsigned char c = p[0]; p[0] = p[1]; p[1] = c; return value; } template <> inline unsigned char SwapBytes( unsigned char& value ) { return value; } template <> inline char SwapBytes( char& value ) { return value; } template void SwapBuffer(T *buf, unsigned int len) { for (unsigned int i = 0; i < len; i++) SwapBytes(buf[i]); } template void EndianSwapImageBuffer(void *data, int length) { switch (SIZE) { case cineon::kByte: break; case cineon::kWord: SwapBuffer(reinterpret_cast(data), length); break; case cineon::kInt: SwapBuffer(reinterpret_cast(data), length); break; case cineon::kLongLong: SwapBuffer(reinterpret_cast(data), length); break; } } inline void EndianSwapImageBuffer(DataSize size, void *data, int length) { switch (size) { case cineon::kByte: break; case cineon::kWord: SwapBuffer(reinterpret_cast(data), length); break; case cineon::kInt: SwapBuffer(reinterpret_cast(data), length); break; case cineon::kLongLong: SwapBuffer(reinterpret_cast(data), length); break; } } } #endif openimageio-1.7.17~dfsg0.orig/src/cineon.imageio/libcineon/WriterInternal.h0000644000175000017500000002465313151711064025076 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2010, Patrick A. Palmer and Leszek Godlewski. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _CINEON_WRITERINTERNAL_H #define _CINEON_WRITERINTERNAL_H 1 #include "BaseTypeConverter.h" namespace cineon { template void MultiTypeBufferCopy(T1 *dst, T2 *src, const int len) { for (int i = 0; i < len; i++) BaseTypeConverter(src[i], dst[i]); } template void CopyWriteBuffer(DataSize src_size, unsigned char *src, IB * dst, const int len) { if (src_size == kByte) MultiTypeBufferCopy(dst, reinterpret_cast(src), len); else if (src_size == kWord) MultiTypeBufferCopy(dst, reinterpret_cast(src), len); else if (src_size == kInt) MultiTypeBufferCopy(dst, reinterpret_cast(src), len); else if (src_size == kLongLong) MultiTypeBufferCopy(dst, reinterpret_cast(src), len); } // access modifications to the buffer based on compression and packing struct BufferAccess { int offset; int length; BufferAccess() : offset(0), length(0) { } }; // \todo NOT DONE template void RleCompress(IB *src, IB *dst, const int bufsize, const int len, BufferAccess &access) { IB ch; int count; int i; int index = bufsize - 1; bool start = true; //bool match = true; // for each data type, have maximum length of rle datum // subtract one so it the LSBit can be used to state int maxCount; if (BITDEPTH == 8) maxCount = 0xff - 1; else if (BITDEPTH == 10) maxCount = 0x3ff - 1; else if (BITDEPTH == 12) maxCount = 0xfff - 1; else if (BITDEPTH == 16) maxCount = 0xffff - 1; else maxCount = 1000000; // high number for floats, doubles for (i = len - 1; i >= 0; i--) { if (start) { count = 1; start = false; ch = src[i]; dst[index--] = ch; } } access.offset = index; access.length = bufsize - index; } template void WritePackedMethod(IB *src, IB *dst, const int len, const bool reverse, BufferAccess &access) { // pack into the same memory space U32 *dst_u32 = reinterpret_cast(dst); // bit shift count for U16 source const int shift = 16 - BITDEPTH; // bit mask U32 mask = 0; if (BITDEPTH == 10) mask = 0x03ff; else if (BITDEPTH == 12) mask = 0x0fff; else if (BITDEPTH == 8) return; int i, entry; for (i = 0; i < len; i++) { // read value and determine write location U32 value = static_cast(src[i+access.offset]) >> shift; // if reverse the order /*** XXX TODO REVERSE if (reverse) // reverse the triplets so entry would be 2,1,0,5,4,3,8,7,6,... entry = ((i / 3) * 3) + (2 - (i % 3)); else ***/ entry = i; int div = (entry * BITDEPTH) / 32; // 32 bits in a U32 int rem = (entry * BITDEPTH) % 32; // write the bits that belong in the first U32 // calculate the masked bits for the added value U32 shift_mask = mask << rem; // mask sure to mask the bits to save as part of the src_buf // so if writing bits 8-18, save the bits of the source material dst_u32[div] = (dst_u32[div] & ~shift_mask) | ((value << rem) & shift_mask); // write across multiple U16? count the carry bits int carry = BITDEPTH - (32 - rem); if (carry > 0) { U32 save = BITDEPTH - carry; dst_u32[div+1] = (dst_u32[div+1] & ~(mask >> save)) | ((value >> save) & (mask >> save)); } } // adjust offset/length access.offset = 0; access.length = (((len * BITDEPTH) / 32) + ((len * BITDEPTH) % 32 ? 1 : 0)) * 2; } // this routine expects a type of U16 template void WritePackedMethodAB_10bit(IB *src, IB *dst, const int len, const bool reverse, BufferAccess &access) { // pack into the same memory space U32 *dst_u32 = reinterpret_cast(dst); // bit shift count const U32 shift = 6; // (16 - BITDEPTH) const U32 bitdepth = 10; const U32 bitmask = 0x03ff; // shift bits over 2 if Method A const int method_shift = 0;//(METHOD == kFilledMethodA ? 2 : 0); // loop through the buffer int i; U32 value = 0; for (i = 0; i < len; i++) { int div = i / 3; // 3 10-bit values in a U32 int rem = i % 3; // write previously calculated value if (i && rem == 0) { dst_u32[div-1] = value; value = 0; } // if reverse the order if (reverse) rem = 2 - rem; // place the 10 bits in the proper place with mask U32 comp = ((static_cast(src[i+access.offset]) >> shift) << (bitdepth * rem)) << method_shift; U32 mask = (bitmask << (bitdepth * rem)) << method_shift ; // overwrite only the proper 10 bits value = (value & ~mask) | (comp & mask); } // write last dst_u32[(len+2)/3-1] = value; // adjust offset/length // multiply * 2 because it takes two U16 = U32 and this func packs into a U32 access.offset = 0; access.length = ((len / 3) + (len % 3 ? 1 : 0)) * 2; } template int WriteBuffer(OutStream *fd, DataSize src_size, void *src_buf, const U32 width, const U32 height, const int noc, const Packing packing, const bool reverse, const int eolnPad, char *blank, bool &status) { int fileOffset = 0; // buffer access parameters BufferAccess bufaccess; bufaccess.offset = 0; bufaccess.length = width * noc; // allocate one line IB *src; IB *dst = new IB[(width * noc) + 1]; // each line in the buffer for (U32 h = 0; h < height; h++) { // image buffer unsigned char *imageBuf = reinterpret_cast(src_buf); const int bytes = Header::DataSizeByteCount(src_size); // copy buffer if need to promote data types from src to destination if (!SAMEBUFTYPE) { src = dst; CopyWriteBuffer(src_size, (imageBuf+(h*width*noc*bytes)+(h*eolnPad)), dst, (width*noc)); } else // not a copy, access source src = reinterpret_cast(imageBuf + (h * width * noc * bytes) + (h*eolnPad)); // if 10 or 12 bit, pack if (BITDEPTH == 10) { if (packing == cineon::kPacked) { WritePackedMethod(src, dst, (width*noc), reverse, bufaccess); } /*else if (packing == kFilledMethodA) { WritePackedMethodAB_10bit(src, dst, (width*noc), reverse, bufaccess); } else // if (packing == cineon::kFilledMethodB) { WritePackedMethodAB_10bit(src, dst, (width*noc), reverse, bufaccess); }*/ } else if (BITDEPTH == 12) { if (packing == cineon::kPacked) { WritePackedMethod(src, dst, (width*noc), reverse, bufaccess); } /*else if (packing == cineon::kFilledMethodB) { // shift 4 MSB down, so 0x0f00 would become 0x00f0 for (int w = 0; w < bufaccess.length; w++) dst[w] = src[bufaccess.offset+w] >> 4; bufaccess.offset = 0; }*/ // a bitdepth of 12 by default is packed with cineon::kFilledMethodA // assumes that either a copy or rle was required // otherwise this routine should not be called with: // 12-bit Method A with the source buffer data type is kWord } // write line fileOffset += (bufaccess.length * sizeof(IB)); if (fd->Write(dst+bufaccess.offset, (bufaccess.length * sizeof(IB))) == false) { status = false; break; } // end of line padding if (eolnPad) { fileOffset += eolnPad; if (fd->Write(blank, eolnPad) == false) { status = false; break; } } } // done with buffer delete [] dst; return fileOffset; } template int WriteFloatBuffer(OutStream *fd, DataSize src_size, void *src_buf, const U32 width, const U32 height, const int noc, const Packing packing, const int eolnPad, char *blank, bool &status) { int fileOffset = 0; // buffer access parameters BufferAccess bufaccess; bufaccess.offset = 0; bufaccess.length = width * noc; // allocate one line IB *src; IB *dst = new IB[(width * noc)]; // each line in the buffer for (U32 h = 0; h < height; h++) { // image buffer unsigned char *imageBuf = reinterpret_cast(src_buf); const int bytes = Header::DataSizeByteCount(src_size); // copy buffer if need to promote data types from src to destination if (!SAMEBUFTYPE) { src = dst; CopyWriteBuffer(src_size, (imageBuf+(h*width*noc*bytes)+(h*eolnPad)), dst, (width*noc)); } else // not a copy, access source src = reinterpret_cast(imageBuf + (h * width * noc * bytes) + (h*eolnPad)); // write line fileOffset += (bufaccess.length * sizeof(IB)); if (fd->Write(dst+bufaccess.offset, (bufaccess.length * sizeof(IB))) == false) { status = false; break; } // end of line padding if (eolnPad) { fileOffset += eolnPad; if (fd->Write(blank, eolnPad) == false) { status = false; break; } } } // done with buffer delete [] dst; return fileOffset; } } #endif openimageio-1.7.17~dfsg0.orig/src/cineon.imageio/libcineon/Cineon.cpp0000644000175000017500000000441213151711064023662 0ustar mfvmfv// vi: ts=4 /* * Copyright (c) 2010, Patrick A. Palmer and Leszek Godlewski. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "Cineon.h" // determine byte order for current system // Intel processors are Little Endian // Power PC, MIPS and Ultra Sparc are Big Endian static unsigned long lValue = 0x12345678; static unsigned long *lPtr = &lValue; cineon::Endian cineon::systemByteOrder = (*(unsigned char*)lPtr == 0x78 ? cineon::kLittleEndian : cineon::kBigEndian); bool cineon::IdentifyFile(InStream *fp) { U32 magic; fp->Rewind(); if (fp->Read(&magic, sizeof(magic)) != sizeof(magic)) return false; return cineon::Header::ValidMagicCookie(magic); } bool cineon::IdentifyFile(const void *p) { U32 magic = *((U32 *) p); return cineon::Header::ValidMagicCookie(magic); } openimageio-1.7.17~dfsg0.orig/src/cineon.imageio/libcineon/CineonHeader.cpp0000644000175000017500000003345513151711064025004 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2010, Patrick A. Palmer and Leszek Godlewski. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include "OpenImageIO/strutil.h" #include "CineonHeader.h" #include "EndianSwap.h" // shortcut macros #define EmptyString(x) memset(x, 0, sizeof(x)) #define EmptyFloat(x) x = std::numeric_limits::infinity() #define EmptyVector(x) (EmptyFloat((x)[0]), \ EmptyFloat((x)[1])) namespace cineon { char Hex(char x) { if (x >= 10) return (x-10+'A'); else return (x+'0'); } cineon::Header::Header() : GenericHeader(), IndustryHeader() { } cineon::GenericHeader::GenericHeader() { this->Reset(); } void cineon::GenericHeader::Reset() { // File Information this->magicNumber = MAGIC_COOKIE; this->imageOffset = ~0; EmptyString(this->version); OIIO::Strutil::safe_strcpy(this->version, SPEC_VERSION, sizeof(this->version)); fileSize = sizeof(cineon::Header); // genericSize is the size of the file/image/orientation headers // sizeof(cineon::GenericHeader) won't give the correct results because // of compiler padding this->genericSize = 1024; // industrySize is the size of the motion picture/television headers this->industrySize = 1024; this->userSize = 0; EmptyString(this->fileName); EmptyString(this->creationDate); EmptyString(this->creationTime); EmptyString(this->reserved1); // Image Information this->imageOrientation = kUndefinedOrientation; this->numberOfElements = 0xff; this->unused1[0] = this->unused1[1] = 0xff; EmptyVector(this->whitePoint); EmptyVector(this->redPrimary); EmptyVector(this->greenPrimary); EmptyVector(this->bluePrimary); EmptyString(this->labelText); EmptyString(this->reserved2); this->interleave = 0xff; this->packing = 0xff; this->dataSign = 0xff; this->imageSense = 0xff; this->endOfLinePadding = 0xffffffff; this->endOfImagePadding = 0xffffffff; // Image Orientation this->xOffset = this->yOffset = 0xffffffff; EmptyString(this->sourceImageFileName); EmptyString(this->sourceDate); EmptyString(this->sourceTime); EmptyString(this->inputDevice); EmptyString(this->inputDeviceModelNumber); EmptyString(this->inputDeviceSerialNumber); EmptyFloat(this->xDevicePitch); EmptyFloat(this->yDevicePitch); EmptyFloat(this->gamma); EmptyString(this->reserved3); EmptyString(this->reserved4); } cineon::IndustryHeader::IndustryHeader() { this->Reset(); } void cineon::IndustryHeader::Reset() { // Motion Picture Industry Specific this->filmManufacturingIdCode = 0xFF; this->filmType = 0xFF; this->perfsOffset = 0xFF; this->prefix = 0xFFFFFFFF; this->count = 0xFFFFFFFF; EmptyString(this->format); this->framePosition = 0xffffffff; EmptyFloat(this->frameRate); EmptyString(this->frameId); EmptyString(this->slateInfo); EmptyString(this->reserved1); } cineon::ImageElement::ImageElement() { this->lowData = 0xffffffff; this->lowQuantity = 0xffffffff; this->highData = 0xffffffff; this->highQuantity = 0xffffffff; this->bitDepth = 0xff; } bool cineon::Header::Read(InStream *io) { // rewind file io->Rewind(); // read in the header from the file size_t r = sizeof(GenericHeader) + sizeof(IndustryHeader); if (io->Read(&(this->magicNumber), r) != r) return false; // validate return this->Validate(); } // Check to see if the compiler placed the data members in the expected memory offsets bool cineon::Header::Check() { // genericSize is the size of the file/image/orientation headers // sizeof(cineon::GenericHeader) won't give the correct results because // of compiler padding // file header is 768 bytes // image header is 640 bytes // orientation header 256 bytes if (sizeof(GenericHeader) != (768 + 640 + 256)) return false; // industrySize is the size of the motion picture/television headers // motion picture header is 256 bytes // television header is 128 bytes if (sizeof(IndustryHeader) != (256 + 128)) return false; // data size checks if (sizeof(U8) != 1 || sizeof(U16) != 2 || sizeof(U32) != 4 || sizeof(R32) != 4 || sizeof(R64) != 8) return false; return true; } bool cineon::Header::Write(OutStream *io) { // write the header to the file size_t r = sizeof(GenericHeader) + sizeof(IndustryHeader); if (io->Write(&(this->magicNumber), r) != r) return false; return true; } bool cineon::Header::WriteOffsetData(OutStream *io) { // calculate the number of elements this->CalculateNumberOfElements(); // write the image offset const long FIELD2 = 4; // offset to image in header if (io->Seek(FIELD2, OutStream::kStart) == false) return false; if (io->Write(&this->imageOffset, sizeof(U32)) == false) return false; // write the file size const long FIELD4 = 16; // offset to total image file size in header if (io->Seek(FIELD4, OutStream::kStart) == false) return false; if (io->Write(&this->fileSize, sizeof(U32)) == false) return false; // write the number of elements const long FIELD19 = 770; // offset to number of image elements in header if (io->Seek(FIELD19, OutStream::kStart) == false) return false; if (io->Write(&this->numberOfElements, sizeof(U16)) == false) return false; // write the image offsets //const long FIELD21_12 = 808; // offset to image offset in image element data structure //const long IMAGE_STRUCTURE = 72; // sizeof the image data structure /*int i; for (i = 0; i < MAX_ELEMENTS; i++) { // only write if there is a defined image description if (this->chan[i].descriptor == kUndefinedDescriptor) continue; // seek to the image offset entry in each image element if (io->Seek((FIELD21_12 + (IMAGE_STRUCTURE * i)), OutStream::kStart) == false) return false; // write if (io->Write(&this->chan[i].dataOffset, sizeof(U32)) == false) return false; }*/ return true; } bool cineon::Header::ValidMagicCookie(const U32 magic) { U32 mc = MAGIC_COOKIE; if (magic == mc) return true; else if (magic == SwapBytes(mc)) return true; else return false; } bool cineon::Header::DetermineByteSwap(const U32 magic) const { U32 mc = MAGIC_COOKIE; bool byteSwap = false; if (magic != mc) byteSwap = true; return byteSwap; } bool cineon::Header::Validate() { // check magic cookie if (!this->ValidMagicCookie(this->magicNumber)) return false; // determine if bytes needs to be swapped around if (this->DetermineByteSwap(this->magicNumber)) { // File information SwapBytes(this->imageOffset); SwapBytes(this->genericSize); SwapBytes(this->industrySize); SwapBytes(this->userSize); SwapBytes(this->fileSize); // Image information for (int i = 0; i < MAX_ELEMENTS; i++) { SwapBytes(this->chan[i].pixelsPerLine); SwapBytes(this->chan[i].linesPerElement); SwapBytes(this->chan[i].lowData); SwapBytes(this->chan[i].lowQuantity); SwapBytes(this->chan[i].highData); SwapBytes(this->chan[i].highQuantity); SwapBytes(this->chan[i].bitDepth); } SwapBytes(this->whitePoint[0]); SwapBytes(this->whitePoint[1]); SwapBytes(this->redPrimary[0]); SwapBytes(this->redPrimary[1]); SwapBytes(this->greenPrimary[0]); SwapBytes(this->greenPrimary[1]); SwapBytes(this->bluePrimary[0]); SwapBytes(this->bluePrimary[1]); SwapBytes(this->endOfLinePadding); SwapBytes(this->endOfImagePadding); // Image Origination information SwapBytes(this->xOffset); SwapBytes(this->yOffset); SwapBytes(this->xDevicePitch); SwapBytes(this->yDevicePitch); SwapBytes(this->gamma); // Motion Picture Industry Specific SwapBytes(this->prefix); SwapBytes(this->count); SwapBytes(this->framePosition); SwapBytes(this->frameRate); } return true; } void cineon::Header::Reset() { GenericHeader::Reset(); IndustryHeader::Reset(); } int cineon::GenericHeader::ImageElementCount() const { int i = 0; while (i < MAX_ELEMENTS ) { if (this->ImageDescriptor(i) == kUndefinedDescriptor) break; i++; } return i; } void cineon::GenericHeader::CalculateNumberOfElements() { int i = this->ImageElementCount(); if (i == 0) this->numberOfElements = 0xff; else this->numberOfElements = U8(i); } void cineon::Header::CalculateOffsets() { int i; for (i = 0; i < MAX_ELEMENTS; i++) { // only write if there is a defined image description if (this->chan[i].designator[1] == kUndefinedDescriptor) continue; } } cineon::DataSize cineon::GenericHeader::ComponentDataSize(const int element) const { if (element < 0 || element >= MAX_ELEMENTS) return kByte; cineon::DataSize ret; switch (this->chan[element].bitDepth) { case 8: ret = kByte; break; case 10: case 12: case 16: ret = kWord; break; case 32: ret = kInt; break; case 64: ret = kLongLong; break; default: assert(0 && "Unknown bit depth"); ret = kLongLong; break; } return ret; } int cineon::GenericHeader::ComponentByteCount(const int element) const { if (element < 0 || element >= MAX_ELEMENTS) return kByte; int ret; switch (this->chan[element].bitDepth) { case 8: ret = sizeof(U8); break; case 10: case 12: case 16: ret = sizeof(U16); break; case 32: ret = sizeof(R32); break; case 64: ret = sizeof(R64); break; default: assert(0 && "Unknown bit depth"); ret = sizeof(R64); break; } return ret; } int cineon::GenericHeader::DataSizeByteCount(const DataSize ds) { int ret = 0; switch (ds) { case kByte: ret = sizeof(U8); break; case kWord: ret = sizeof(U16); break; case kInt: ret = sizeof(U32); break; case kLongLong: ret = sizeof(U64); break; } return ret; } void cineon::IndustryHeader::FilmEdgeCode(char *edge) const { if (this->filmManufacturingIdCode == 0xff && this->filmType == 0xff && this->perfsOffset == 0xff && this->prefix == 0xffffffff && this->count == 0xffffffff) *edge = 0; else sprintf(edge, "%02u%02u%02u%06u%04u", (unsigned int)this->filmManufacturingIdCode, (unsigned int)this->filmType, (unsigned int)this->perfsOffset, this->prefix, this->count); } void cineon::IndustryHeader::SetFilmEdgeCode(const char *edge) { char buf[7]; strncpy(buf, edge, 2); this->filmManufacturingIdCode = atoi(buf); strncpy(buf, edge + 2, 2); this->filmType = atoi(buf); strncpy(buf, edge + 4, 2); this->perfsOffset = atoi(buf); strncpy(buf, edge + 6, 6); this->prefix = atoi(buf); strncpy(buf, edge + 12, 4); this->count = atoi(buf); } void cineon::GenericHeader::SetCreationTimeDate(const long sec) { struct tm *tm_time; char str[32]; #ifdef WIN32 _tzset(); #endif const time_t t = time_t(sec); tm_time = ::localtime(&t); ::strftime(str, 32, "%Y:%m:%d:%H:%M:%S%Z", tm_time); ::strncpy(this->creationDate, str, 10); ::strncpy(this->creationTime, str + 11, 12); } void cineon::GenericHeader::SetSourceTimeDate(const long sec) { struct tm *tm_time; char str[32]; #ifdef WIN32 _tzset(); #endif const time_t t = time_t(sec); tm_time = ::localtime(&t); ::strftime(str, 32, "%Y:%m:%d:%H:%M:%S%Z", tm_time); ::strncpy(this->sourceDate, str, 10); ::strncpy(this->sourceTime, str + 11, 12); } // Height() // this function determines the height of the image taking in account for the image orientation // if an image is 1920x1080 but is oriented top to bottom, left to right then the height stored // in the image is 1920 rather than 1080 cineon::U32 cineon::Header::Height() const { U32 h = 0; for (int i = 0; i < this->NumberOfElements(); i++) { switch (this->ImageOrientation()) { case kTopToBottomLeftToRight: case kTopToBottomRightToLeft: case kBottomToTopLeftToRight: case kBottomToTopRightToLeft: if (this->PixelsPerLine(i) > h) h = this->PixelsPerLine(i); break; default: if (this->LinesPerElement(i) > h) h = this->LinesPerElement(i); break; } } return h; } // Width() // this function determines the width of the image taking in account for the image orientation // if an image is 1920x1080 but is oriented top to bottom, left to right then the width stored // in the image is 1920 rather than 1080 cineon::U32 cineon::Header::Width() const { U32 w = 0; for (int i = 0; i < this->NumberOfElements(); i++) { switch (this->ImageOrientation()) { case kTopToBottomLeftToRight: case kTopToBottomRightToLeft: case kBottomToTopLeftToRight: case kBottomToTopRightToLeft: if (this->LinesPerElement(i) > w) w = this->LinesPerElement(i); break; default: if (this->PixelsPerLine(i) > w) w = this->PixelsPerLine(i); break; } } return w; } } openimageio-1.7.17~dfsg0.orig/src/cineon.imageio/libcineon/OutStream.cpp0000644000175000017500000000503313151711064024372 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2010, Patrick A. Palmer and Leszek Godlewski. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include "OpenImageIO/filesystem.h" #include "CineonStream.h" namespace cineon { OutStream::OutStream() : fp(0) { } OutStream::~OutStream() { } bool OutStream::Open(const char *f) { if (this->fp) this->Close(); if ((this->fp = OIIO::Filesystem::fopen(f, "wb")) == 0) return false; return true; } void OutStream::Close() { if (this->fp) { ::fclose(this->fp); this->fp = 0; } } size_t OutStream::Write(void *buf, const size_t size) { if (this->fp == 0) return false; return ::fwrite(buf, 1, size, this->fp); } bool OutStream::Seek(long offset, Origin origin) { int o = 0; switch (origin) { case kCurrent: o = SEEK_CUR; break; case kEnd: o = SEEK_END; break; case kStart: o = SEEK_SET; break; } if (this->fp == 0) return false; return (::fseek(this->fp, offset, o) == 0); } void OutStream::Flush() { if (this->fp) ::fflush(this->fp); } } openimageio-1.7.17~dfsg0.orig/src/cineon.imageio/libcineon/ElementReadStream.h0000644000175000017500000000427213151711064025461 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2010, Patrick A. Palmer and Leszek Godlewski. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _CINEON_ELEMENTREADSTREAM_H #define _CINEON_ELEMENTREADSTREAM_H 1 #include "CineonStream.h" namespace cineon { class ElementReadStream { public: ElementReadStream(InStream *); virtual ~ElementReadStream(); virtual void Reset(); virtual bool Read(const cineon::Header &, const long offset, void * buf, const size_t size); virtual bool ReadDirect(const cineon::Header &, const long offset, void * buf, const size_t size); protected: void EndianDataCheck(const cineon::Header &, void *, const size_t size); InStream *fd; }; } #endif openimageio-1.7.17~dfsg0.orig/src/cineon.imageio/libcineon/InStream.cpp0000644000175000017500000000531013151711064024167 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2010, Patrick A. Palmer and Leszek Godlewski. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include "OpenImageIO/filesystem.h" #include "CineonStream.h" namespace cineon { InStream::InStream() : fp(0) { } InStream::~InStream() { } bool InStream::Open(const char *f) { if (this->fp) this->Close(); if ((this->fp = OIIO::Filesystem::fopen(f, "rb")) == 0) return false; return true; } void InStream::Close() { if (this->fp) { ::fclose(this->fp); this->fp = 0; } } void InStream::Rewind() { if (this->fp) ::rewind(fp); } bool InStream::Seek(long offset, Origin origin) { int o = 0; switch (origin) { case kCurrent: o = SEEK_CUR; break; case kEnd: o = SEEK_END; break; case kStart: o = SEEK_SET; break; } if (this->fp == 0) return false; return (::fseek(this->fp, offset, o) == 0); } size_t InStream::Read(void *buf, const size_t size) { if (this->fp == 0) return 0; return ::fread(buf, 1, size, this->fp); } size_t InStream::ReadDirect(void *buf, const size_t size) { return this->Read(buf, size); } bool InStream::EndOfFile() const { if (this->fp == 0) return true; return ::feof(this->fp); } } openimageio-1.7.17~dfsg0.orig/src/cineon.imageio/libcineon/Codec.h0000644000175000017500000000471413151711064023136 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2010, Patrick A. Palmer and Leszek Godlewski. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _CINEON_CODEC_H #define _CINEON_CODEC_H 1 #include "Cineon.h" namespace cineon { /*! * \brief compress / decompress data segments * base class defaults to None */ class Codec { public: /*! * \brief constructor */ Codec(); /*! * \brief destructor */ virtual ~Codec(); /*! * \brief reset instance */ virtual void Reset(); /*! * \brief read data * \param dpxHeader dpx header information * \param fd field descriptor * \param block image area to read * \param data buffer * \param size size of the buffer component * \return success */ virtual bool Read(const Header &dpxHeader, ElementReadStream *fd, const Block &block, void *data, const DataSize size); protected: U32 *scanline; //!< single scanline }; } #endif openimageio-1.7.17~dfsg0.orig/src/cineon.imageio/libcineon/ReaderInternal.h0000644000175000017500000003340613151711064025020 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2010, Patrick A. Palmer and Leszek Godlewski. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _CINEON_READERINTERNAL_H #define _CINEON_READERINTERNAL_H 1 #include #include "BaseTypeConverter.h" #define PADDINGBITS_10BITFILLEDMETHODA 2 #define PADDINGBITS_10BITFILLEDMETHODB 0 #define MASK_10BITPACKED 0xffc0 #define MULTIPLIER_10BITPACKED 2 #define REMAIN_10BITPACKED 4 #define REVERSE_10BITPACKED 6 #define MASK_12BITPACKED 0xfff0 #define MULTIPLIER_12BITPACKED 4 #define REMAIN_12BITPACKED 2 #define REVERSE_12BITPACKED 4 namespace cineon { template bool Read10bitFilled(const Header &dpxHeader, U32 *readBuf, IR *fd, const Block &block, BUF *data) { // image height to read const int height = block.y2 - block.y1 + 1; // get the number of components for this element descriptor const int numberOfComponents = dpxHeader.NumberOfElements(); // end of line padding int eolnPad = dpxHeader.EndOfLinePadding(); // number of datums in one row int datums = dpxHeader.Width() * numberOfComponents; // Line length in bytes rounded to 32 bits boundary int lineLength = ((datums - 1) / 3 + 1) * 4; // read in each line at a time directly into the user memory space for (int line = 0; line < height; line++) { // determine offset into image element int actline = line + block.y1; // first get line offset long offset = actline * lineLength; // add in eoln padding offset += line * eolnPad; // add in offset within the current line, rounding down so to catch any components within the word offset += block.x1 * numberOfComponents / 3 * 4; // get the read count in bytes, round to the 32-bit boundry int readSize = (block.x2 - block.x1 + 1) * numberOfComponents; readSize += readSize % 3; readSize = readSize / 3 * 4; // determine buffer offset int bufoff = line * dpxHeader.Width() * numberOfComponents; fd->Read(dpxHeader, offset, readBuf, readSize); // unpack the words in the buffer BUF *obuf = data + bufoff; int index = (block.x1 * sizeof(U32)) % numberOfComponents; for (int count = (block.x2 - block.x1 + 1) * numberOfComponents - 1; count >= 0; count--) { // unpacking the buffer backwords U16 d1 = U16(readBuf[(count + index) / 3] >> ((2 - (count + index) % 3) * 10 + PADDINGBITS) & 0x3ff); BaseTypeConvertU10ToU16(d1, d1); BaseTypeConverter(d1, obuf[count]); } } return true; } template bool Read10bitFilledMethodA(const Header &dpx, U32 *readBuf, IR *fd, const Block &block, BUF *data) { // padding bits for PackedMethodA is 2 return Read10bitFilled(dpx, readBuf, fd, block, data); } template bool Read10bitFilledMethodB(const Header &dpx, U32 *readBuf, IR *fd, const Block &block, BUF *data) { return Read10bitFilled(dpx, readBuf, fd, block, data); } // 10 bit, packed data // 12 bit, packed data template void UnPackPacked(U32 *readBuf, const int bitDepth, BUF *data, int count, int bufoff) { // unpack the words in the buffer BUF *obuf = data + bufoff; for (int i = count - 1; i >= 0; i--) { // unpacking the buffer backwords // find the byte that the data starts in, read in as a 16 bits then shift and mask // the pattern with byte offset is: // 10 bits datasize rotates every 4 data elements // element 0 -> 6 bit shift to normalize at MSB (10 LSB shifted 6 bits) // element 1 -> 4 bit shift to normalize at MSB // element 2 -> 2 bit shift to normalize at MSB // element 3 -> 0 bit shift to normalize at MSB // 10 bit algorithm: (6-((count % 4)*2)) // the pattern repeats every 160 bits // 12 bits datasize rotates every 2 data elements // element 0 -> 4 bit shift to normalize at MSB // element 1 -> 0 bit shift to normalize at MSB // 12 bit algorithm: (4-((count % 2)*4)) // the pattern repeats every 96 bits // first determine the word that the data element completely resides in U16 *d1 = reinterpret_cast(reinterpret_cast(readBuf)+((i * bitDepth) / 8 /*bits*/)); // place the component in the MSB and mask it for both 10-bit and 12-bit U16 d2 = (*d1 << (REVERSE - ((i % REMAIN) * MULTIPLIER))) & MASK; // For the 10/12 bit cases, specialize the 16-bit conversion by // repacking into the LSB and using a specialized conversion if(bitDepth == 10) { d2 = d2 >> REVERSE; BaseTypeConvertU10ToU16(d2, d2); } else if(bitDepth == 12) { d2 = d2 >> REVERSE; BaseTypeConvertU12ToU16(d2, d2); } BaseTypeConverter(d2, obuf[i]); } } template bool ReadPacked(const Header &dpxHeader, U32 *readBuf, IR *fd, const Block &block, BUF *data) { // image height to read const int height = block.y2 - block.y1 + 1; // get the number of components for this element descriptor const int numberOfComponents = dpxHeader.NumberOfElements(); // end of line padding int eolnPad = dpxHeader.EndOfLinePadding(); // data size in bits // FIXME!!! const int dataSize = dpxHeader.BitDepth(0); // number of bytes const int lineSize = (dpxHeader.Width() * numberOfComponents * dataSize + 31) / 32; // read in each line at a time directly into the user memory space for (int line = 0; line < height; line++) { // determine offset into image element long offset = (line + block.y1) * (lineSize * sizeof(U32)) + (block.x1 * numberOfComponents * dataSize / 32 * sizeof(U32)) + (line * eolnPad); // calculate read size int readSize = ((block.x2 - block.x1 + 1) * numberOfComponents * dataSize); readSize += (block.x1 * numberOfComponents * dataSize % 32); // add the bits left over from the beginning of the line readSize = ((readSize + 31) / 32) * sizeof(U32); // calculate buffer offset int bufoff = line * dpxHeader.Width() * numberOfComponents; fd->Read(dpxHeader, offset, readBuf, readSize); // unpack the words in the buffer int count = (block.x2 - block.x1 + 1) * numberOfComponents; UnPackPacked(readBuf, dataSize, data, count, bufoff); } return true; } template bool Read10bitPacked(const Header &dpxHeader, U32 *readBuf, IR *fd, const Block &block, BUF *data) { return ReadPacked(dpxHeader, readBuf, fd, block, data); } template bool Read12bitPacked(const Header &dpxHeader, U32 *readBuf, IR *fd, const Block &block, BUF *data) { return ReadPacked(dpxHeader, readBuf, fd, block, data); } template bool ReadBlockTypes(const Header &dpxHeader, SRC *readBuf, IR *fd, const Block &block, BUF *data) { // get the number of components for this element descriptor const int numberOfComponents = dpxHeader.NumberOfElements(); // byte count component type // FIXME!!! const int bytes = dpxHeader.ComponentByteCount(0); // image image/height to read const int width = (block.x2 - block.x1 + 1) * numberOfComponents; const int height = block.y2 - block.y1 + 1; // end of line padding int eolnPad = dpxHeader.EndOfLinePadding(); if (eolnPad == ~0) eolnPad = 0; // image width const int imageWidth = dpxHeader.Width(); // read in each line at a time directly into the user memory space for (int line = 0; line < height; line++) { // determine offset into image element long offset = (line + block.y1) * imageWidth * numberOfComponents * bytes + block.x1 * numberOfComponents * bytes + (line * eolnPad); if (BUFTYPE == SRCTYPE) { fd->ReadDirect(dpxHeader, offset, reinterpret_cast(data + (width*line)), width*bytes); } else { fd->Read(dpxHeader, offset, readBuf, width*bytes); // convert data for (int i = 0; i < width; i++) BaseTypeConverter(readBuf[i], data[width*line+i]); } } return true; } template bool Read12bitFilledMethodB(const Header &dpxHeader, U16 *readBuf, IR *fd, const Block &block, BUF *data) { // get the number of components for this element descriptor const int numberOfComponents = dpxHeader.NumberOfElements(); // image width & height to read const int width = (block.x2 - block.x1 + 1) * numberOfComponents; const int height = block.y2 - block.y1 + 1; // width of image const int imageWidth = dpxHeader.Width(); // end of line padding (not a required data element so check for ~0) int eolnPad = dpxHeader.EndOfLinePadding(); if (eolnPad == ~0) eolnPad = 0; // read in each line at a time directly into the user memory space for (int line = 0; line < height; line++) { // determine offset into image element long offset = (line + block.y1) * imageWidth * numberOfComponents * 2 + block.x1 * numberOfComponents * 2 + (line * eolnPad); fd->Read(dpxHeader, offset, readBuf, width*2); // convert data for (int i = 0; i < width; i++) { U16 d1 = readBuf[i] << 4; BaseTypeConverter(d1, data[width*line+i]); } } return true; } template bool ReadImageBlock(const Header &dpxHeader, U32 *readBuf, IR *fd, const Block &block, BUF *data) { // FIXME!!! const int bitDepth = dpxHeader.BitDepth(0); const DataSize size = dpxHeader.ComponentDataSize(0); const Packing packing = dpxHeader.ImagePacking(); if (bitDepth == 10) { if (packing == kLongWordLeft) return Read10bitFilledMethodA(dpxHeader, readBuf, fd, block, reinterpret_cast(data)); else if (packing == kLongWordRight) return Read10bitFilledMethodB(dpxHeader, readBuf, fd, block, reinterpret_cast(data)); else if (packing == kPacked) return Read10bitPacked(dpxHeader, readBuf, fd, block, reinterpret_cast(data)); } else if (bitDepth == 12) { if (packing == kPacked) return Read12bitPacked(dpxHeader, readBuf, fd, block, reinterpret_cast(data)); /*else if (packing == kFilledMethodB) // filled method B // 12 bits fill LSB of 16 bits return Read12bitFilledMethodB(dpxHeader, reinterpret_cast(readBuf), fd, block, reinterpret_cast(data)); else // filled method A // 12 bits fill MSB of 16 bits return ReadBlockTypes(dpxHeader, reinterpret_cast(readBuf), fd, block, reinterpret_cast(data));*/ } else if (size == cineon::kByte) return ReadBlockTypes(dpxHeader, reinterpret_cast(readBuf), fd, block, reinterpret_cast(data)); else if (size == cineon::kWord) return ReadBlockTypes(dpxHeader, reinterpret_cast(readBuf), fd, block, reinterpret_cast(data)); else if (size == cineon::kInt) return ReadBlockTypes(dpxHeader, reinterpret_cast(readBuf), fd, block, reinterpret_cast(data)); else if (size == cineon::kLongLong) return ReadBlockTypes(dpxHeader, reinterpret_cast(readBuf), fd, block, reinterpret_cast(data)); // should not reach here return false; } template bool ReadImageBlock(const Header &dpxHeader, U32 *readBuf, IR *fd, const Block &block, void *data, const DataSize size) { if (size == cineon::kByte) return ReadImageBlock(dpxHeader, readBuf, fd, block, reinterpret_cast(data)); else if (size == cineon::kWord) return ReadImageBlock(dpxHeader, readBuf, fd, block, reinterpret_cast(data)); else if (size == cineon::kInt) return ReadImageBlock(dpxHeader, readBuf, fd, block, reinterpret_cast(data)); else if (size == cineon::kLongLong) return ReadImageBlock(dpxHeader, readBuf, fd, block, reinterpret_cast(data)); // should not reach here return false; } } #endif openimageio-1.7.17~dfsg0.orig/src/cineon.imageio/libcineon/ElementReadStream.cpp0000644000175000017500000000701713151711064026014 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2010, Patrick A. Palmer and Leszek Godlewski. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "Cineon.h" #include "EndianSwap.h" #include "ElementReadStream.h" #include cineon::ElementReadStream::ElementReadStream(InStream *fd) : fd(fd) { } cineon::ElementReadStream::~ElementReadStream() { } void cineon::ElementReadStream::Reset() { } bool cineon::ElementReadStream::Read(const cineon::Header &dpxHeader, const long offset, void * buf, const size_t size) { long position = dpxHeader.ImageOffset() + offset; // seek to the memory position if (this->fd->Seek(position, InStream::kStart) == false) return false; // read in the data, calculate buffer offset if (this->fd->Read(buf, size) != size) return false; // swap the bytes if different byte order this->EndianDataCheck(dpxHeader, buf, size); return true; } bool cineon::ElementReadStream::ReadDirect(const cineon::Header &dpxHeader, const long offset, void * buf, const size_t size) { long position = dpxHeader.ImageOffset() + offset; // seek to the memory position if (this->fd->Seek(position, InStream::kStart) == false) return false; // read in the data, calculate buffer offset if (this->fd->ReadDirect(buf, size) != size) return false; // swap the bytes if different byte order this->EndianDataCheck(dpxHeader, buf, size); return true; } void cineon::ElementReadStream::EndianDataCheck(const cineon::Header &dpxHeader, void *buf, const size_t size) { if (dpxHeader.RequiresByteSwap()) { // FIXME!!! switch (dpxHeader.BitDepth(0)) { case 8: break; case 12: if (dpxHeader.ImagePacking() == cineon::kPacked) cineon::EndianSwapImageBuffer(buf, size / sizeof(U32)); else cineon::EndianSwapImageBuffer(buf, size / sizeof(U16)); break; case 16: cineon::EndianSwapImageBuffer(buf, size / sizeof(U16)); break; default: // 10-bit, 32-bit, 64-bit cineon::EndianSwapImageBuffer(buf, size / sizeof(U32)); } } } openimageio-1.7.17~dfsg0.orig/src/cineon.imageio/libcineon/Reader.cpp0000644000175000017500000001265213151711064023656 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2010, Patrick A. Palmer and Leszek Godlewski. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include "Cineon.h" #include "EndianSwap.h" #include "ReaderInternal.h" #include "ElementReadStream.h" #include "Codec.h" cineon::Reader::Reader() : fd(0), rio(0) { // initialize all of the Codec* to NULL this->codec = 0; } cineon::Reader::~Reader() { this->Reset(); delete this->rio; } void cineon::Reader::Reset() { // delete all of the Codec * entries delete this->codec; this->codec = 0; // Element Reader if (this->rio) { delete rio; this->rio = 0; } if (this->fd) this->rio = new ElementReadStream(this->fd); } void cineon::Reader::SetInStream(InStream *fd) { this->fd = fd; this->Reset(); } bool cineon::Reader::ReadHeader() { return this->header.Read(this->fd); } bool cineon::Reader::ReadImage(void *data, const DataSize size) { Block block(0, 0, this->header.Width()-1, this->header.Height()-1); return this->ReadBlock(data, size, block); } /* implementation notes: cineon::readBlock reads in the image starting from the beginning of the channel. This can be optimized if the image isn't encoded; we can skip forward in the file to close to the start of (block.x1, block.y1) and determine exactly which bit will be the start. This certainly will save some time for cases where we are only reading ROIs (regions of interest). */ bool cineon::Reader::ReadBlock(void *data, const DataSize size, Block &block) { int i; // check the block coordinates block.Check(); // get the number of components for this element descriptor const int numberOfComponents = this->header.NumberOfElements(); // check the widths and bit depths of the image elements bool consistentDepth = true; bool consistentWidth = true; const int bitDepth = this->header.BitDepth(0); const int width = this->header.PixelsPerLine(0); for (i = 1; i < numberOfComponents; i++) { if (this->header.BitDepth(i) != bitDepth) { consistentDepth = false; if (!consistentWidth) break; } if ((int)this->header.PixelsPerLine(i) != width) { consistentWidth = false; if (!consistentDepth) break; } } // lets see if this can be done in a single fast read if (consistentDepth && consistentWidth && this->header.EndOfLinePadding() == 0 && ((bitDepth == 8 && size == cineon::kByte) || (bitDepth == 16 && size == cineon::kWord) || (bitDepth == 32 && size == cineon::kInt) || (bitDepth == 64 && size == cineon::kLongLong)) && block.x1 == 0 && block.x2 == (int)(this->header.Width()-1)) { // seek to the beginning of the image block if (this->fd->Seek((this->header.ImageOffset() + (block.y1 * this->header.Width() * (bitDepth / 8) * numberOfComponents)), InStream::kStart) == false) return false; // size of the image const size_t imageSize = this->header.Width() * (block.y2 - block.y1 + 1) * numberOfComponents; const size_t imageByteSize = imageSize * bitDepth / 8; size_t rs = this->fd->ReadDirect(data, imageByteSize); if (rs != imageByteSize) return false; // swap the bytes if different byte order if (this->header.RequiresByteSwap()) cineon::EndianSwapImageBuffer(size, data, imageSize); return true; } // determine if the encoding system is loaded if (this->codec == 0) // this element reader has not been used this->codec = new Codec; // read the image block return this->codec->Read(this->header, this->rio, block, data, size); } bool cineon::Reader::ReadUserData(unsigned char *data) { // check to make sure there is some user data if (this->header.UserSize() == 0) return true; // seek to the beginning of the user data block if (this->fd->Seek(sizeof(GenericHeader) + sizeof(IndustryHeader), InStream::kStart) == false) return false; size_t rs = this->fd->ReadDirect(data, this->header.UserSize()); if (rs != this->header.UserSize()) return false; return true; } openimageio-1.7.17~dfsg0.orig/src/cineon.imageio/libcineon/Cineon.h0000644000175000017500000002516213151711064023334 0ustar mfvmfv// vi: ts=4 /*! \file Cineon.h */ /* * Copyright (c) 2010, Patrick A. Palmer and Leszek Godlewski. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _CINEON_H #define _CINEON_H 1 #include #include "CineonHeader.h" #include "CineonStream.h" /*! * \def LIBCINEON_VERSION * \brief LIBCINEON Version */ #define LIBCINEON_VERSION "0.1" /*! * \namespace cineon * \brief libcineon namespace */ namespace cineon { // forward definitions class Codec; class ElementReadStream; /*! * \enum Endian * \brief DPX files can be stored in big- or little-endian byte order */ enum Endian { kLittleEndian, //!< increasing numeric significance with increasing memory kBigEndian //!< big end first }; /*! \struct Block * \brief Rectangle block definition defined by two points */ struct Block { int x1, y1, x2, y2; /*! * \brief Constructor */ inline Block(); /*! * \brief Constructor * * \param x1 upper left x coordinate * \param y1 upper left y coordinate * \param x2 lower right x coordinate * \param y2 lower right y coordinate */ Block(const int x1, const int y1, const int x2, const int y2); /*! * \brief Set the block coordinates * * \param x1 upper left x coordinate * \param y1 upper left y coordinate * \param x2 lower right x coordinate * \param y2 lower right y coordinate */ void Set(const int x1, const int y1, const int x2, const int y2); /*! * \brief Check to see if a point is within the block * * \param x x coordinate * \param y y coordinate * \return true/false if coordinates within block */ inline bool Inside(const int x, const int y) const; /*! * \brief Rearrange coordinates if necessary so the first coordinate is upper left and the second is lower right */ inline void Check(); }; // Current platform endian byte order extern Endian systemByteOrder; // namespace functions /*! * \brief determine if the image file is DPX * * \param file buffer to read and search * \return true/false if identified as DPX */ bool IdentifyFile(InStream *file); /*! * \brief determine if the image file is DPX * * \param data memory to search * \return true/false if identified as DPX */ bool IdentifyFile(const void *data); /*! * \brief returns a char * of the default DPX file extension * * \return .dpx file extenion */ inline const char *DefaultExtension(); /*! * returns a string of the highest SMPTE DPX version supported by this library * * \return SMPTE DPX version */ inline const char *Version(); /*! * \brief returns the version string for this library * * \return OpenDPX version */ inline const char *LibraryVersion(); /*! * \class Reader * \brief DPX Image Reader class */ class Reader { public: /*! * \brief DPX header */ Header header; /*! * \brief Constructor */ Reader(); /*! * \brief Destructor */ virtual ~Reader(); /*! * \brief Set the InStream object to be used to read images * * \param stream Object to use for low level reads */ void SetInStream(InStream *stream); /*! * \brief clear any caching or memory allocated specific to an image */ void Reset(); /*! * \brief Read the dpx header into the header member * * \return success true/false */ bool ReadHeader(); /*! * \brief Read an image element into a buffer that matches the image description type * * The DataSize allows the user to specific the buffer DataSize which can differ * from the image element. It is possible, for example, to read an 8-bit per * component (3 components per pixel for RGB) into 16-bits. * * \param data buffer * \param size size of the buffer component * \param desc element description type * \return success true/false */ bool ReadImage(void *data, const DataSize size = kWord); /*! * \brief Read a rectangular image block into a buffer from the image element * specified by the Descriptor type * * \param data buffer * \param size size of the buffer component * \param block image area to read * \param desc element description type * \return success true/false */ bool ReadBlock(void *data, const DataSize size, Block &block); /*! * \brief Read the user data into a buffer. * * Buffer must be large enough to hold the user data. * * \param data buffer * \return success true/false */ bool ReadUserData(unsigned char *data); protected: InStream *fd; Codec *codec; ElementReadStream *rio; }; /*! * \class Writer * \brief DPX Image Writer class */ class Writer { public: /*! * \brief DPX Header */ Header header; /*! * \brief Constructor */ Writer(); /*! * \brief Destructor */ virtual ~Writer(); /*! * \brief Start defining the header and writing the images */ void Start(); /*! * \brief Set the basic file information about DPX * * \param fileName name of this created file (100 characters max) * \param creationTimeDate creation time and date - format is "YYYY:MM:DD:HH:MM:SSLTZ" * where HH is 24 hour time, LTZ is local time zone using either * three character notation (i.e., -04) or five character notation * representing hours and minutes offset from Greenwich Mean time * (i.e., -0700) (24 characters max) * \param creator creator (100 characters max) * \param project project name (200 characters max) * \param copyright copyright statement (200 characters max) * \param encryptKey encryption key */ void SetFileInfo(const char *fileName, const char *creationDate = 0, const char *creationTime = 0); /*! * \brief Set the Width and Height of the images * * \param width width of the image * \param height height of the image */ void SetImageInfo(const U32 width, const U32 height); /*! * \brief Get the next available element * \return next available */ int NextAvailElement() const; /*! * \brief Set the parameters on an element * * There are 8 elements maximum in an single DPX and each element used must be set before writing the header * * \param element element number (0-7) * \param desc image descriptor * \param bitDepth bit depth of image, valid values are [8,10,12,16,32,64] * \param transfer transfer characteristic * \param colorimetric colorimetric specification * \param packing packing type * \param encoding encoding type * \param dataSign * \param lowData * \param lowQuantity * \param highData * \param highQuantity * \param eolnPadding end of line padding (in bytes) * \param eoimPadding end of image padding (in bytes) */ void SetElement(const int element = 0, const Descriptor desc = kGrayscale, const U8 bitDepth = 10, const U32 pixelsPerLine = 1, const U32 linesPerElement = 1, const R32 lowData = ~0, const R32 lowQuantity = ~0, const R32 highData = ~0, const R32 highQuantity = ~0); /*! * \brief Set the OutStream object will use to write the files * * \param stream OutStream object */ void SetOutStream(OutStream *stream); /*! * \brief Set the size of the user data area * * \param size size of user data */ void SetUserData(const long size); /*! * \brief Write the header * * \return success true/false */ bool WriteHeader(); /*! * \brief Write the user data * * \param data buffer - must match size set in Writer::SetUserData() * \return success true/false */ bool WriteUserData(void *data); /*! * \brief Write the entire element to the dpx file * * \param element element number (0-7) * \param data buffer * \return success true/false */ bool WriteElement(const int element, void *data); bool WriteElement(const int element, void *data, const DataSize size); bool WriteElement(const int element, void *data, const long count); /** * \brief Finish up writing image * * \return success true/false */ bool Finish(); protected: long fileLoc; OutStream *fd; bool WriteThrough(void *, const U32, const U32, const int, const int, const U32, const U32, char *); }; } inline const char *cineon::DefaultExtension() { return "cin"; } inline const char *cineon::Version() { return SPEC_VERSION; } inline const char *cineon::LibraryVersion() { return LIBCINEON_VERSION; } inline cineon::Block::Block() : x1(0), y1(0), x2(0), y2(0) { } inline cineon::Block::Block(const int x1, const int y1, const int x2, const int y2) : x1(x1), y1(y1), x2(x2), y2(y2) { this->Check(); } inline void cineon::Block::Set(const int x1, const int y1, const int x2, const int y2) { this->x1 = x1; this->y1 = y1; this->x2 = x2; this->y2 = y2; } // check the coordinates that x1 < x2 and y1 < y2 inline void cineon::Block::Check() { if (this->x1 > this->x2) { int t = x1; this->x1 = this->x2; this->x2 = t; } if (this->y1 > this->y2) { int t = y1; this->y1 = this->y2; this->y2 = t; } } inline bool cineon::Block::Inside(const int x, const int y) const { if (x >= this->x1 && x <= this->x2 && y >= this->y1 && y <= this->y2) return true; return false; } #endif openimageio-1.7.17~dfsg0.orig/src/cineon.imageio/cineoninput.cpp0000644000175000017500000004163413151711064023047 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "libcineon/Cineon.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/strutil.h" using namespace cineon; OIIO_PLUGIN_NAMESPACE_BEGIN class CineonInput : public ImageInput { public: CineonInput () : m_stream(NULL) { init(); } virtual ~CineonInput () { close(); } virtual const char * format_name (void) const { return "cineon"; } virtual bool open (const std::string &name, ImageSpec &newspec); virtual bool close (); virtual bool read_native_scanline (int y, int z, void *data); private: InStream *m_stream; cineon::Reader m_cin; std::vector m_userBuf; /// Reset everything to initial state /// void init () { if (m_stream) { m_stream->Close (); delete m_stream; m_stream = NULL; } m_userBuf.clear (); } /// Helper function - retrieve string for libcineon descriptor /// char *get_descriptor_string (cineon::Descriptor c); }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageInput *cineon_input_imageio_create () { return new CineonInput; } OIIO_EXPORT int cineon_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* cineon_imageio_library_version () { return NULL; } OIIO_EXPORT const char * cineon_input_extensions[] = { "cin", NULL }; OIIO_PLUGIN_EXPORTS_END bool CineonInput::open (const std::string &name, ImageSpec &newspec) { // open the image m_stream = new InStream(); if (! m_stream->Open(name.c_str())) { error ("Could not open file \"%s\"", name.c_str()); return false; } m_cin.SetInStream(m_stream); if (! m_cin.ReadHeader()) { error ("Could not read header"); return false; } // create imagespec TypeDesc typedesc; int maxbits = 0; for (int i = 0; i < m_cin.header.NumberOfElements (); i++) { if (maxbits < m_cin.header.BitDepth (i)) maxbits = m_cin.header.BitDepth (i); } switch ((maxbits + 7) / 8) { case 1: typedesc = TypeDesc::UINT8; break; case 2: typedesc = TypeDesc::UINT16; break; case 3: case 4: typedesc = TypeDesc::UINT32; break; default: error ("Unsupported bit depth %d", maxbits); return false; } m_spec = ImageSpec (m_cin.header.Width(), m_cin.header.Height(), m_cin.header.NumberOfElements (), typedesc); // fill channel names m_spec.channelnames.clear (); int gscount = 0, rcount = 0, gcount = 0, bcount = 0; char buf[3]; for (int i = 0; i < m_cin.header.NumberOfElements (); i++) { switch (m_cin.header.ImageDescriptor (i)) { case cineon::kGrayscale: if (++gscount > 1) { std::string ch = Strutil::format ("I%d", gscount); m_spec.channelnames.push_back (ch); } else m_spec.channelnames.push_back ("I"); break; case cineon::kPrintingDensityRed: case cineon::kRec709Red: if (++gscount > 1) { std::string ch = Strutil::format ("R%d", rcount); m_spec.channelnames.push_back (ch); } else m_spec.channelnames.push_back ("R"); break; case cineon::kPrintingDensityGreen: case cineon::kRec709Green: if (++gcount > 1) { std::string ch = Strutil::format ("G%d", gcount); m_spec.channelnames.push_back (ch); } else m_spec.channelnames.push_back ("G"); break; case cineon::kPrintingDensityBlue: case cineon::kRec709Blue: if (++bcount > 1) { std::string ch = Strutil::format ("B%d", bcount); m_spec.channelnames.push_back (ch); } else m_spec.channelnames.push_back ("B"); break; default: std::string ch = Strutil::format ("channel%d", (int)m_spec.channelnames.size()); m_spec.channelnames.push_back (ch); break; } } // bits per sample m_spec.attribute ("oiio:BitsPerSample", maxbits); // image orientation - see appendix B.2 of the OIIO documentation int orientation; switch (m_cin.header.ImageOrientation ()) { case cineon::kLeftToRightTopToBottom: orientation = 1; break; case cineon::kRightToLeftTopToBottom: orientation = 2; break; case cineon::kLeftToRightBottomToTop: orientation = 4; break; case cineon::kRightToLeftBottomToTop: orientation = 3; break; case cineon::kTopToBottomLeftToRight: orientation = 5; break; case cineon::kTopToBottomRightToLeft: orientation = 6; break; case cineon::kBottomToTopLeftToRight: orientation = 8; break; case cineon::kBottomToTopRightToLeft: orientation = 7; break; default: orientation = 0; break; } m_spec.attribute ("Orientation", orientation); #if 1 // This is not very smart, but it seems that as a practical matter, // all Cineon files are log. So ignore the gamma field and just set // the color space to KodakLog. m_spec.attribute ("oiio:ColorSpace", "KodakLog"); #else // image linearity // FIXME: making this more robust would require the per-channel transfer // function functionality which isn't yet in OIIO switch (m_cin.header.ImageDescriptor (0)) { case cineon::kRec709Red: case cineon::kRec709Green: case cineon::kRec709Blue: m_spec.attribute ("oiio:ColorSpace", "Rec709"); default: // either grayscale or printing density if (!isinf (m_cin.header.Gamma ()) && m_cin.header.Gamma () != 0.0f) // actual gamma value is read later on m_spec.attribute ("oiio:ColorSpace", "GammaCorrected"); break; } // gamma exponent if (!isinf (m_cin.header.Gamma ()) && m_cin.header.Gamma () != 0.0f) m_spec.attribute ("oiio:Gamma", (float) m_cin.header.Gamma ()); #endif // general metadata // some non-compliant writers will dump a field filled with 0xFF rather // than a NULL string termination on the first character, so take that // into account, too if (m_cin.header.creationDate[0] && m_cin.header.creationTime[0]) { // libcineon's date/time format is pretty close to OIIO's (libcineon // uses %Y:%m:%d:%H:%M:%S%Z) m_spec.attribute ("DateTime", Strutil::format ("%s %s", m_cin.header.creationDate, m_cin.header.creationTime)); // FIXME: do something about the time zone } // cineon-specific metadata char *strings[8]; int ints[8]; float floats[8]; // image descriptor for (int i = 0; i < m_cin.header.NumberOfElements (); i++) strings[i] = get_descriptor_string (m_cin.header.ImageDescriptor (i)); m_spec.attribute ("cineon:ImageDescriptor", TypeDesc (TypeDesc::STRING, m_cin.header.NumberOfElements ()), &strings); // save some typing by using macros // "internal" macros // per-file attribs #define CINEON_SET_ATTRIB_S(x, n, s) m_spec.attribute (s, \ m_cin.header.x (n)) #define CINEON_SET_ATTRIB(x, n) CINEON_SET_ATTRIB_S(x, n, \ "cineon:" #x) #define CINEON_SET_ATTRIB_BYTE(x) if (m_cin.header.x () != 0xFF) \ CINEON_SET_ATTRIB(x, ) #define CINEON_SET_ATTRIB_INT(x) if (static_cast \ (m_cin.header.x ()) \ != 0xFFFFFFFF) \ CINEON_SET_ATTRIB(x, ) #define CINEON_SET_ATTRIB_FLOAT(x) if (! isinf(m_cin.header.x ())) \ CINEON_SET_ATTRIB(x, ) #define CINEON_SET_ATTRIB_COORDS(x) m_cin.header.x (floats); \ if (!isinf (floats[0]) \ && !isinf (floats[1]) \ && !(floats[0] == 0. && floats[1] == 0.)) \ m_spec.attribute ("cineon:" #x, \ TypeDesc (TypeDesc::FLOAT, 2), \ &floats[0]) #define CINEON_SET_ATTRIB_STR(X, x) if (m_cin.header.x[0] \ && m_cin.header.x[0] != char(-1)) \ m_spec.attribute ("cineon:" #X, \ m_cin.header.x) // per-element attribs #define CINEON_SET_ATTRIB_N(x, a, t, c) for (int i = 0; i < m_cin.header. \ NumberOfElements (); i++) { \ c(x) \ a[i] = m_cin.header.x (i); \ } \ m_spec.attribute ("cineon:" #x, \ TypeDesc (TypeDesc::t, \ m_cin.header.NumberOfElements()),\ &a) #define CINEON_CHECK_ATTRIB_FLOAT(x) if (!isinf (m_cin.header.x (i))) #define CINEON_SET_ATTRIB_FLOAT_N(x) CINEON_SET_ATTRIB_N(x, floats, \ FLOAT, CINEON_CHECK_ATTRIB_FLOAT) #define CINEON_CHECK_ATTRIB_INT(x) if (m_cin.header.x (i) != 0xFFFFFFFF) #define CINEON_SET_ATTRIB_INT_N(x) CINEON_SET_ATTRIB_N(x, ints, \ UINT32, CINEON_CHECK_ATTRIB_INT) #define CINEON_CHECK_ATTRIB_BYTE(x) if (m_cin.header.x (i) != 0xFF) #define CINEON_SET_ATTRIB_BYTE_N(x) CINEON_SET_ATTRIB_N(x, ints, \ UINT32, CINEON_CHECK_ATTRIB_BYTE) CINEON_SET_ATTRIB_STR(Version, version); // per-element data CINEON_SET_ATTRIB_BYTE_N(Metric); CINEON_SET_ATTRIB_BYTE_N(BitDepth); CINEON_SET_ATTRIB_INT_N(PixelsPerLine); CINEON_SET_ATTRIB_INT_N(LinesPerElement); CINEON_SET_ATTRIB_FLOAT_N(LowData); CINEON_SET_ATTRIB_FLOAT_N(LowQuantity); CINEON_SET_ATTRIB_FLOAT_N(HighData); CINEON_SET_ATTRIB_FLOAT_N(HighQuantity); CINEON_SET_ATTRIB_COORDS(WhitePoint); CINEON_SET_ATTRIB_COORDS(RedPrimary); CINEON_SET_ATTRIB_COORDS(GreenPrimary); CINEON_SET_ATTRIB_COORDS(BluePrimary); CINEON_SET_ATTRIB_STR(LabelText, labelText); CINEON_SET_ATTRIB_INT(XOffset); CINEON_SET_ATTRIB_INT(YOffset); CINEON_SET_ATTRIB_STR(SourceImageFileName, sourceImageFileName); CINEON_SET_ATTRIB_STR(InputDevice, inputDevice); CINEON_SET_ATTRIB_STR(InputDeviceModelNumber, inputDeviceModelNumber); CINEON_SET_ATTRIB_STR(InputDeviceSerialNumber, inputDeviceSerialNumber); CINEON_SET_ATTRIB_FLOAT(XDevicePitch); CINEON_SET_ATTRIB_FLOAT(YDevicePitch); CINEON_SET_ATTRIB_INT(FramePosition); CINEON_SET_ATTRIB_FLOAT(FrameRate); CINEON_SET_ATTRIB_STR(Format, format); CINEON_SET_ATTRIB_STR(FrameId, frameId); CINEON_SET_ATTRIB_STR(SlateInfo, slateInfo); #undef CINEON_SET_ATTRIB_BYTE_N #undef CINEON_CHECK_ATTRIB_BYTE #undef CINEON_SET_ATTRIB_INT_N #undef CINEON_CHECK_ATTRIB_INT #undef CINEON_SET_ATTRIB_FLOAT_N #undef CINEON_CHECK_ATTRIB_FLOAT #undef CINEON_SET_ATTRIB_N #undef CINEON_SET_ATTRIB_STR #undef CINEON_SET_ATTRIB_COORDS #undef CINEON_SET_ATTRIB_FLOAT #undef CINEON_SET_ATTRIB_INT #undef CINEON_SET_ATTRIB #undef CINEON_SET_ATTRIB_S std::string tmpstr; switch (m_cin.header.ImagePacking () & ~cineon::kPackAsManyAsPossible) { case cineon::kPacked: tmpstr = "Packed"; break; case cineon::kByteLeft: tmpstr = "8-bit boundary, left justified"; break; case cineon::kByteRight: tmpstr = "8-bit boundary, right justified"; break; case cineon::kWordLeft: tmpstr = "16-bit boundary, left justified"; break; case cineon::kWordRight: tmpstr = "16-bit boundary, right justified"; break; case cineon::kLongWordLeft: tmpstr = "32-bit boundary, left justified"; break; case cineon::kLongWordRight: tmpstr = "32-bit boundary, right justified"; break; } if (m_cin.header.ImagePacking () & cineon::kPackAsManyAsPossible) tmpstr += ", as many fields as possible per cell"; else tmpstr += ", at most one pixel per cell"; if (!tmpstr.empty ()) m_spec.attribute ("cineon:Packing", tmpstr); if (m_cin.header.sourceDate[0] && m_cin.header.sourceTime[0]) { // libcineon's date/time format is pretty close to OIIO's (libcineon // uses %Y:%m:%d:%H:%M:%S%Z) m_spec.attribute ("DateTime", Strutil::format ("%s %s", m_cin.header.sourceDate, m_cin.header.sourceTime)); // FIXME: do something about the time zone } m_cin.header.FilmEdgeCode(buf); if (buf[0]) m_spec.attribute ("cineon:FilmEdgeCode", buf); // read in user data if (m_cin.header.UserSize () != 0 && m_cin.header.UserSize () != 0xFFFFFFFF) { m_userBuf.resize (m_cin.header.UserSize ()); m_cin.ReadUserData (&m_userBuf[0]); } if (!m_userBuf.empty ()) m_spec.attribute ("cineon:UserData", TypeDesc (TypeDesc::UCHAR, m_cin.header.UserSize ()), &m_userBuf[0]); newspec = spec (); return true; } bool CineonInput::close () { init(); // Reset to initial state return true; } bool CineonInput::read_native_scanline (int y, int z, void *data) { cineon::Block block(0, y, m_cin.header.Width () - 1, y); // FIXME: un-hardcode the channel from 0 if (!m_cin.ReadBlock (data, m_cin.header.ComponentDataSize (0), block)) return false; return true; } char * CineonInput::get_descriptor_string (cineon::Descriptor c) { switch (c) { case cineon::kGrayscale: return (char *)"Grayscale"; case cineon::kPrintingDensityRed: return (char *)"Red, printing density"; case cineon::kRec709Red: return (char *)"Red, Rec709"; case cineon::kPrintingDensityGreen: return (char *)"Green, printing density"; case cineon::kRec709Green: return (char *)"Green, Rec709"; case cineon::kPrintingDensityBlue: return (char *)"Blue, printing density"; case cineon::kRec709Blue: return (char *)"Blue, Rec709"; //case cineon::kUndefinedDescriptor: default: return (char *)"Undefined"; } } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/cineon.imageio/cineonoutput.cpp0000644000175000017500000000676313151711064023254 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "libcineon/Cineon.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/fmath.h" using namespace cineon; OIIO_PLUGIN_NAMESPACE_BEGIN class CineonOutput : public ImageOutput { public: CineonOutput (); virtual ~CineonOutput (); virtual const char * format_name (void) const { return "cineon"; } virtual bool open (const std::string &name, const ImageSpec &spec, ImageOutput::OpenMode mode); virtual bool close (); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); private: // Initialize private members to pre-opened state void init (void) { } }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *cineon_output_imageio_create () { return new CineonOutput; } // OIIO_EXPORT int cineon_imageio_version = OIIO_PLUGIN_VERSION; // it's in cineoninput.cpp OIIO_EXPORT const char * cineon_output_extensions[] = { "cin", NULL }; OIIO_PLUGIN_EXPORTS_END CineonOutput::CineonOutput () { init (); } CineonOutput::~CineonOutput () { // Close, if not already done. close (); } bool CineonOutput::open (const std::string &name, const ImageSpec &userspec, ImageOutput::OpenMode mode) { error ("Cineon writer is not implemented yet, please poke Leszek in the " "mailing list"); return false; } bool CineonOutput::close () { init(); // Reset to initial state return true; // How can we fail? // Epicly. -- IneQuation } bool CineonOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { error ("Cineon writer is not implemented yet, please poke Leszek in the " "mailing list"); return false; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/raw.imageio/0000755000175000017500000000000013151711064017316 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/raw.imageio/CMakeLists.txt0000644000175000017500000000047413151711064022063 0ustar mfvmfvif (USE_LIBRAW AND LIBRAW_FOUND) add_oiio_plugin (rawinput.cpp rawoutput.cpp INCLUDE_DIRS ${LibRaw_INCLUDE_DIR} LINK_LIBRARIES ${LibRaw_r_LIBRARIES} DEFINITIONS "-DUSE_LIBRAW=1") else () message (WARNING "Raw plugin will not be built") endif () openimageio-1.7.17~dfsg0.orig/src/raw.imageio/rawoutput.cpp0000644000175000017500000000370513151711064022101 0ustar mfvmfv/* Copyright 2013 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "OpenImageIO/imageio.h" OIIO_PLUGIN_NAMESPACE_BEGIN // This probably won't ever be implemented as RAW formats usually come // directly from image sensors OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *raw_output_imageio_create () { return NULL; } OIIO_EXPORT const char *raw_output_extensions[] = { NULL }; OIIO_PLUGIN_EXPORTS_END OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/raw.imageio/rawinput.cpp0000644000175000017500000003717013151711064021703 0ustar mfvmfv/* Copyright 2013 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "OpenImageIO/imageio.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/strutil.h" #include #include /* time_t, struct tm, gmtime */ #include #include // This plugin utilises LibRaw: // http://www.libraw.org/ // Documentation: // http://www.libraw.org/docs OIIO_PLUGIN_NAMESPACE_BEGIN class RawInput : public ImageInput { public: RawInput () : m_process(true), m_image(NULL) {} virtual ~RawInput() { close(); } virtual const char * format_name (void) const { return "raw"; } virtual int supports (string_view feature) const { return (feature == "exif" /* not yet? || feature == "iptc"*/); } virtual bool open (const std::string &name, ImageSpec &newspec); virtual bool open (const std::string &name, ImageSpec &newspec, const ImageSpec &config); virtual bool close(); virtual bool read_native_scanline (int y, int z, void *data); private: bool process(); bool m_process; LibRaw m_processor; libraw_processed_image_t *m_image; void read_tiff_metadata (const std::string &filename); }; // Export version number and create function symbols OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT int raw_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* raw_imageio_library_version () { return ustring::format("libraw %s", libraw_version()).c_str(); } OIIO_EXPORT ImageInput *raw_input_imageio_create () { return new RawInput; } OIIO_EXPORT const char *raw_input_extensions[] = { "bay", "bmq", "cr2", "crw", "cs1", "dc2", "dcr", "dng", "erf", "fff", "hdr", "k25", "kdc", "mdc", "mos", "mrw", "nef", "orf", "pef", "pxn", "raf", "raw", "rdc", "sr2", "srf", "x3f", "arw", "3fr", "cine", "ia", "kc2", "mef", "nrw", "qtk", "rw2", "sti", "rwl", "srw", "drf", "dsc", "ptx", "cap", "iiq", "rwz", NULL }; OIIO_PLUGIN_EXPORTS_END bool RawInput::open (const std::string &name, ImageSpec &newspec) { // If user doesn't want to provide any config, just use an empty spec. ImageSpec config; return open(name, newspec, config); } bool RawInput::open (const std::string &name, ImageSpec &newspec, const ImageSpec &config) { int ret; // open the image if ( (ret = m_processor.open_file(name.c_str()) ) != LIBRAW_SUCCESS) { error ("Could not open file \"%s\", %s", name.c_str(), libraw_strerror(ret)); return false; } if ( (ret = m_processor.unpack() ) != LIBRAW_SUCCESS) { error ("Could not unpack \"%s\", %s",name.c_str(), libraw_strerror(ret)); return false; } // Forcing the Libraw to adjust sizes based on the capture device orientation m_processor.adjust_sizes_info_only(); // Set file information m_spec = ImageSpec(m_processor.imgdata.sizes.iwidth, m_processor.imgdata.sizes.iheight, 3, // LibRaw should only give us 3 channels TypeDesc::UINT16); // Output 16 bit images m_processor.imgdata.params.output_bps = 16; // Set the gamma curve to Linear m_spec.attribute("oiio:ColorSpace","Linear"); m_processor.imgdata.params.gamm[0] = 1.0; m_processor.imgdata.params.gamm[1] = 1.0; // Disable exposure correction (unless config "raw:auto_bright" == 1) m_processor.imgdata.params.no_auto_bright = ! config.get_int_attribute("raw:auto_bright", 0); // Use camera white balance if "raw:use_camera_wb" is not 0 m_processor.imgdata.params.use_camera_wb = config.get_int_attribute("raw:use_camera_wb", 1); // Turn off maximum threshold value (unless set to non-zero) m_processor.imgdata.params.adjust_maximum_thr = config.get_float_attribute("raw:adjust_maximum_thr", 0.0f); // Set camera maximum value if "raw:user_sat" is not 0 m_processor.imgdata.params.user_sat = config.get_int_attribute("raw:user_sat", 0); // Use embedded color profile. Values mean: // 0: do not use embedded color profile // 1 (default): use embedded color profile (if present) for DNG files // (always), for other files only if use_camera_wb is set. // 3: use embedded color data (if present) regardless of white // balance setting. m_processor.imgdata.params.use_camera_matrix = config.get_int_attribute("raw:use_camera_matrix", 1); // Check to see if the user has explicitly set the output colorspace primaries std::string cs = config.get_string_attribute ("raw:ColorSpace", "sRGB"); if (cs.size()) { static const char *colorspaces[] = { "raw", "sRGB", "Adobe", "Wide", "ProPhoto", "XYZ", #if LIBRAW_VERSION >= LIBRAW_MAKE_VERSION(0,18,0) "ACES", #endif NULL }; size_t c; for (c=0; colorspaces[c]; c++) if (Strutil::iequals (cs, colorspaces[c])) break; if (colorspaces[c]) m_processor.imgdata.params.output_color = c; else { #if LIBRAW_VERSION < LIBRAW_MAKE_VERSION(0,18,0) if (cs == "ACES") error ("raw:ColorSpace value of \"ACES\" is not supported by libRaw %d.%d.%d", LIBRAW_MAJOR_VERSION, LIBRAW_MINOR_VERSION, LIBRAW_PATCH_VERSION); else #endif error("raw:ColorSpace set to unknown value"); return false; } // Set the attribute in the output spec m_spec.attribute("raw:ColorSpace", cs); } else { // By default we use sRGB primaries for simplicity m_processor.imgdata.params.output_color = 1; m_spec.attribute("raw:ColorSpace", "sRGB"); } // Exposure adjustment float exposure = config.get_float_attribute ("raw:Exposure", -1.0f); if (exposure >= 0.0f) { if (exposure < 0.25f || exposure > 8.0f) { error("raw:Exposure invalid value. range 0.25f - 8.0f"); return false; } m_processor.imgdata.params.exp_correc = 1; // enable exposure correction m_processor.imgdata.params.exp_shift = exposure; // set exposure correction // Set the attribute in the output spec m_spec.attribute ("raw:Exposure", exposure); } // Interpolation quality // note: LibRaw must be compiled with demosaic pack GPL2 to use demosaic // algorithms 5-9. It must be compiled with demosaic pack GPL3 for // algorithm 10 (AMAzE). If either of these packs are not included, it // will silently use option 3 - AHD. std::string demosaic = config.get_string_attribute ("raw:Demosaic"); if (demosaic.size()) { static const char *demosaic_algs[] = { "linear", "VNG", "PPG", "AHD", "DCB", "AHD-Mod", "AFD", "VCD", "Mixed", "LMMSE", "AMaZE", #if LIBRAW_VERSION >= LIBRAW_MAKE_VERSION(0,16,0) "DHT", "AAHD", #endif // Future demosaicing algorithms should go here NULL }; size_t d; for (d=0; demosaic_algs[d]; d++) if (Strutil::iequals (demosaic, demosaic_algs[d])) break; if (demosaic_algs[d]) m_processor.imgdata.params.user_qual = d; else if (Strutil::iequals (demosaic, "none")) { #ifdef LIBRAW_DECODER_FLATFIELD // See if we can access the Bayer patterned data for this raw file libraw_decoder_info_t decoder_info; m_processor.get_decoder_info(&decoder_info); if (!(decoder_info.decoder_flags & LIBRAW_DECODER_FLATFIELD)) { error("Unable to extract unbayered data from file \"%s\"", name.c_str()); return false; } #endif // User has selected no demosaicing, so no processing needs to be done m_process = false; // The image width and height may be different now, so update with new values // Also we will only be reading back a single, bayered channel m_spec.width = m_processor.imgdata.sizes.raw_width; m_spec.height = m_processor.imgdata.sizes.raw_height; m_spec.nchannels = 1; m_spec.channelnames.clear(); m_spec.channelnames.push_back("R"); // Also, any previously set demosaicing options are void, so remove them m_spec.erase_attribute("oiio:Colorspace"); m_spec.erase_attribute("raw:Colorspace"); m_spec.erase_attribute("raw:Exposure"); } else { error("raw:Demosaic set to unknown value"); return false; } // Set the attribute in the output spec m_spec.attribute("raw:Demosaic", demosaic); } else { m_processor.imgdata.params.user_qual = 3; m_spec.attribute("raw:Demosaic", "AHD"); } // Metadata const libraw_image_sizes_t &sizes (m_processor.imgdata.sizes); m_spec.attribute ("PixelAspectRatio", (float)sizes.pixel_aspect); // FIXME: sizes. top_margin, left_margin, raw_pitch, flip, mask? const libraw_iparams_t &idata (m_processor.imgdata.idata); if (idata.make[0]) m_spec.attribute ("Make", idata.make); if (idata.model[0]) m_spec.attribute ("Model", idata.model); // FIXME: idata. dng_version, is_foveon, colors, filters, cdesc const libraw_colordata_t &color (m_processor.imgdata.color); m_spec.attribute("Exif:Flash", (int) color.flash_used); if (color.model2[0]) m_spec.attribute ("Software", color.model2); // FIXME -- all sorts of things in this struct const libraw_imgother_t &other (m_processor.imgdata.other); m_spec.attribute ("Exif:ISOSpeedRatings", (int) other.iso_speed); m_spec.attribute ("ExposureTime", other.shutter); m_spec.attribute ("Exif:ShutterSpeedValue", -log2f(other.shutter)); m_spec.attribute ("FNumber", other.aperture); m_spec.attribute ("Exif:ApertureValue", 2.0f * log2f(other.aperture)); m_spec.attribute ("Exif:FocalLength", other.focal_len); struct tm * m_tm = localtime(&m_processor.imgdata.other.timestamp); char datetime[20]; strftime (datetime, 20, "%Y-%m-%d %H:%M:%S", m_tm); m_spec.attribute ("DateTime", datetime); // FIXME: other.shot_order // FIXME: other.gpsdata if (other.desc[0]) m_spec.attribute ("ImageDescription", other.desc); if (other.artist[0]) m_spec.attribute ("Artist", other.artist); // FIXME -- thumbnail possibly in m_processor.imgdata.thumbnail read_tiff_metadata (name); // Copy the spec to return to the user newspec = m_spec; return true; } void RawInput::read_tiff_metadata (const std::string &filename) { // Many of these raw formats look just like TIFF files, and we can use // that to extract a bunch of extra Exif metadata and thumbnail. ImageInput *in = ImageInput::create ("tiff"); if (! in) { (void) OIIO::geterror(); // eat the error return; } ImageSpec newspec; bool ok = in->open (filename, newspec); if (ok) { // Transfer "Exif:" metadata to the raw spec. for (ParamValueList::const_iterator p = newspec.extra_attribs.begin(); p != newspec.extra_attribs.end(); ++p) { if (Strutil::istarts_with (p->name().c_str(), "Exif:")) { m_spec.attribute (p->name().c_str(), p->type(), p->data()); } } } in->close (); delete in; } bool RawInput::close() { if (m_image) { LibRaw::dcraw_clear_mem(m_image); m_image = NULL; } return true; } bool RawInput::process() { if (!m_image) { int ret = m_processor.dcraw_process(); if (ret != LIBRAW_SUCCESS) { error("Processing image failed, %s", libraw_strerror(ret)); return false; } m_image = m_processor.dcraw_make_mem_image(&ret); if (!m_image) { error("LibRaw failed to create in memory image"); return false; } if (m_image->type != LIBRAW_IMAGE_BITMAP) { error("LibRaw did not return expected image type"); return false; } if (m_image->colors != 3) { error("LibRaw did not return 3 channel image"); return false; } } return true; } bool RawInput::read_native_scanline (int y, int z, void *data) { if (y < 0 || y >= m_spec.height) // out of range scanline return false; if (! m_process) { // The user has selected not to apply any debayering. // We take the raw data directly unsigned short *scanline = &((m_processor.imgdata.rawdata.raw_image)[m_spec.width*y]); memcpy(data, scanline, m_spec.scanline_bytes(true)); return true; } // Check the state of the internal RAW reader. // Have to load the entire image at once, so only do this once if (! m_image) { if (!process()) { return false; } } int length = m_spec.width*m_image->colors; // Should always be 3 colors // Because we are reading UINT16's, we need to cast m_image->data unsigned short *scanline = &(((unsigned short *)m_image->data)[length*y]); memcpy(data, scanline, m_spec.scanline_bytes(true)); return true; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libtexture/0000755000175000017500000000000013151711064017303 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/libtexture/imagecache_pvt.h0000644000175000017500000013573113151711064022425 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// \file /// Non-public classes used internally by ImgeCacheImpl. #ifndef OPENIMAGEIO_IMAGECACHE_PVT_H #define OPENIMAGEIO_IMAGECACHE_PVT_H #include #include #include #include #if BOOST_VERSION >= 104900 # include #endif #include #include "OpenImageIO/export.h" #include "OpenImageIO/texture.h" #include "OpenImageIO/refcnt.h" #include "OpenImageIO/hash.h" #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/unordered_map_concurrent.h" OIIO_NAMESPACE_BEGIN namespace pvt { #ifndef NDEBUG # define IMAGECACHE_TIME_STATS 1 #else // Change the following to 1 to get timing statistics even for // optimized runs. Note that this has some performance penalty. # define IMAGECACHE_TIME_STATS 0 #endif #define IMAGECACHE_USE_RW_MUTEX 1 // Should we compute and store shadow matrices? Not if we don't support // shadow maps! #define USE_SHADOW_MATRICES 0 using boost::thread_specific_ptr; class ImageCacheImpl; class ImageCachePerThreadInfo; const char * texture_format_name (TexFormat f); const char * texture_type_name (TexFormat f); #ifdef BOOST_CONTAINER_FLAT_MAP_HPP typedef boost::container::flat_map UdimLookupMap; #else typedef unordered_map UdimLookupMap; #endif /// Structure to hold IC and TS statistics. We combine into a single /// structure to minimize the number of costly thread_specific_ptr /// retrievals. If somebody is using the ImageCache without a /// TextureSystem, a few extra stats come along for the ride, but this /// has no performance penalty. struct ImageCacheStatistics { // First, the ImageCache-specific fields: long long find_tile_calls; long long find_tile_microcache_misses; int find_tile_cache_misses; long long files_totalsize; long long files_totalsize_ondisk; long long bytes_read; // These stats are hard to deal with on a per-thread basis, so for // now, they are still atomics shared by the whole IC. // int tiles_created; // int tiles_current; // int tiles_peak; // int open_files_created; // int open_files_current; // int open_files_peak; int unique_files; double fileio_time; double fileopen_time; double file_locking_time; double tile_locking_time; double find_file_time; double find_tile_time; // TextureSystem-specific fields below: long long texture_queries; long long texture_batches; long long texture3d_queries; long long texture3d_batches; long long shadow_queries; long long shadow_batches; long long environment_queries; long long environment_batches; long long aniso_queries; long long aniso_probes; float max_aniso; long long closest_interps; long long bilinear_interps; long long cubic_interps; int file_retry_success; int tile_retry_success; ImageCacheStatistics () { init (); } void init (); void merge (const ImageCacheStatistics &s); }; /// Unique in-memory record for each image file on disk. Note that /// this class is not in and of itself thread-safe. It's critical that /// any calling routine use a mutex any time a ImageCacheFile's methods are /// being called, including constructing a new ImageCacheFile. /// /// The public routines of ImageCacheFile are thread-safe! In /// particular, callers do not need to lock around calls to read_tile. /// However, a few of them require passing in a pointer to the /// thread-specific IC data including microcache and statistics. /// class OIIO_API ImageCacheFile : public RefCnt { public: ImageCacheFile (ImageCacheImpl &imagecache, ImageCachePerThreadInfo *thread_info, ustring filename, ImageInput::Creator creator=NULL, const ImageSpec *config=NULL); ~ImageCacheFile (); bool broken () const { return m_broken; } int subimages () const { return (int)m_subimages.size(); } int miplevels (int subimage) const { return (int)m_subimages[subimage].levels.size(); } const ImageSpec & spec (int subimage, int miplevel) const { return levelinfo(subimage,miplevel).spec; } ImageSpec & spec (int subimage, int miplevel) { return levelinfo(subimage,miplevel).spec; } const ImageSpec & nativespec (int subimage, int miplevel) const { return levelinfo(subimage,miplevel).nativespec; } ustring filename (void) const { return m_filename; } ustring fileformat (void) const { return m_fileformat; } TexFormat textureformat () const { return m_texformat; } TextureOpt::Wrap swrap () const { return m_swrap; } TextureOpt::Wrap twrap () const { return m_twrap; } TextureOpt::Wrap rwrap () const { return m_rwrap; } TypeDesc datatype (int subimage) const { return m_subimages[subimage].datatype; } ImageCacheImpl &imagecache () const { return m_imagecache; } ImageInput *imageinput () const { return m_input.get(); } ImageInput::Creator creator () const { return m_inputcreator; } /// Load new data tile /// bool read_tile (ImageCachePerThreadInfo *thread_info, int subimage, int miplevel, int x, int y, int z, int chbegin, int chend, TypeDesc format, void *data); /// Mark the file as recently used. /// void use (void) { m_used = true; } /// Try to release resources for this file -- if recently used, mark /// as not recently used; if already not recently used, close the /// file and return true. void release (void); size_t channelsize (int subimage) const { return m_subimages[subimage].channelsize; } size_t pixelsize (int subimage) const { return m_subimages[subimage].pixelsize; } TypeDesc::BASETYPE pixeltype (int subimage) const { return (TypeDesc::BASETYPE) m_subimages[subimage].datatype.basetype; } bool mipused (void) const { return m_mipused; } bool sample_border (void) const { return m_sample_border; } bool is_udim (void) const { return m_is_udim; } const std::vector &mipreadcount (void) const { return m_mipreadcount; } void invalidate (); size_t timesopened () const { return m_timesopened; } size_t tilesread () const { return m_tilesread; } imagesize_t bytesread () const { return m_bytesread; } double & iotime () { return m_iotime; } size_t redundant_tiles () const { return (size_t) m_redundant_tiles.load(); } imagesize_t redundant_bytesread () const { return (imagesize_t) m_redundant_bytesread.load(); } void register_redundant_tile (imagesize_t bytesread) { m_redundant_tiles += 1; m_redundant_bytesread += (long long) bytesread; } std::time_t mod_time () const { return m_mod_time; } ustring fingerprint () const { return m_fingerprint; } void duplicate (ImageCacheFile *dup) { m_duplicate = dup;} ImageCacheFile *duplicate () const { return m_duplicate; } // Retrieve the average color, or try to compute it. Return true on // success, false on failure. bool get_average_color (float *avg, int subimage, int chbegin, int chend); /// Info for each MIP level that isn't in the ImageSpec, or that we /// precompute. struct LevelInfo { ImageSpec spec; ///< ImageSpec for the mip level ImageSpec nativespec; ///< Native ImageSpec for the mip level bool full_pixel_range; ///< pixel data window matches image window bool onetile; ///< Whole level fits on one tile mutable bool polecolorcomputed; ///< Pole color was computed mutable std::vector polecolor;///< Pole colors int nxtiles, nytiles, nztiles; ///< Number of tiles in each dimension atomic_ll *tiles_read; ///< Bitfield for tiles read at least once LevelInfo (const ImageSpec &spec, const ImageSpec &nativespec); ///< Initialize based on spec LevelInfo (const LevelInfo &src); // needed for vector ~LevelInfo () { delete [] tiles_read; } }; /// Info for each subimage /// struct SubimageInfo { std::vector levels; ///< Extra per-level info TypeDesc datatype; ///< Type of pixels we store internally unsigned int channelsize; ///< Channel size, in bytes unsigned int pixelsize; ///< Pixel size, in bytes bool untiled; ///< Not tiled bool unmipped; ///< Not really MIP-mapped bool volume; ///< It's a volume image bool full_pixel_range; ///< pixel data window matches image window bool is_constant_image; ///< Is the image a constant color? bool has_average_color; ///< We have an average color std::vector average_color; ///< Average color spin_mutex average_color_mutex; ///< protect average_color // The scale/offset accounts for crops or overscans, converting // 0-1 texture space relative to the "display/full window" into // 0-1 relative to the "pixel window". float sscale, soffset, tscale, toffset; ustring subimagename; SubimageInfo () : datatype(TypeDesc::UNKNOWN), channelsize(0), pixelsize(0), untiled(false), unmipped(false), volume(false), full_pixel_range(false), is_constant_image(false), has_average_color(false), sscale(1.0f), soffset(0.0f), tscale(1.0f), toffset(0.0f) { } void init (const ImageSpec &spec, bool forcefloat); ImageSpec &spec (int m) { return levels[m].spec; } const ImageSpec &spec (int m) const { return levels[m].spec; } const ImageSpec &nativespec (int m) const { return levels[m].nativespec; } int miplevels () const { return (int) levels.size(); } }; const SubimageInfo &subimageinfo (int subimage) const { return m_subimages[subimage]; } SubimageInfo &subimageinfo (int subimage) { return m_subimages[subimage]; } const LevelInfo &levelinfo (int subimage, int miplevel) const { DASSERT ((int)m_subimages.size() > subimage); DASSERT ((int)m_subimages[subimage].levels.size() > miplevel); return m_subimages[subimage].levels[miplevel]; } LevelInfo &levelinfo (int subimage, int miplevel) { DASSERT ((int)m_subimages.size() > subimage); DASSERT ((int)m_subimages[subimage].levels.size() > miplevel); return m_subimages[subimage].levels[miplevel]; } /// Do we currently have a valid spec? bool validspec () const { DASSERT ((m_validspec == false || m_subimages.size() > 0) && "validspec is true, but subimages are empty"); return m_validspec; } /// Forget the specs we know void invalidate_spec () { m_validspec = false; m_subimages.clear (); } /// Should we print an error message? Keeps track of whether the /// number of errors so far, including this one, is a above the limit /// set for errors to print for each file. int errors_should_issue () const; private: ustring m_filename_original; ///< original filename before search path ustring m_filename; ///< Filename bool m_used; ///< Recently used (in the LRU sense) bool m_broken; ///< has errors; can't be used properly shared_ptr m_input; ///< Open ImageInput, NULL if closed std::vector m_subimages; ///< Info on each subimage TexFormat m_texformat; ///< Which texture format TextureOpt::Wrap m_swrap; ///< Default wrap modes TextureOpt::Wrap m_twrap; ///< Default wrap modes TextureOpt::Wrap m_rwrap; ///< Default wrap modes #if USE_SHADOW_MATRICES Imath::M44f m_Mlocal; ///< shadows: world-to-local (light) matrix Imath::M44f m_Mproj; ///< shadows: world-to-pseudo-NDC Imath::M44f m_Mtex; ///< shadows: world-to-pNDC with camera z Imath::M44f m_Mras; ///< shadows: world-to-raster with camera z #endif EnvLayout m_envlayout; ///< env map: which layout? bool m_y_up; ///< latlong: is y "up"? (else z is up) bool m_sample_border; ///< are edge samples exactly on the border? bool m_is_udim; ///< Is tiled/UDIM? ustring m_fileformat; ///< File format name size_t m_tilesread; ///< Tiles read from this file imagesize_t m_bytesread; ///< Bytes read from this file atomic_ll m_redundant_tiles; ///< Redundant tile reads atomic_ll m_redundant_bytesread;///< Redundant bytes read size_t m_timesopened; ///< Separate times we opened this file double m_iotime; ///< I/O time for this file bool m_mipused; ///< MIP level >0 accessed volatile bool m_validspec; ///< If false, reread spec upon open mutable int m_errors_issued; ///< Errors issued for this file std::vector 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 ustring m_fingerprint; ///< Optional cryptographic fingerprint ImageCacheFile *m_duplicate; ///< Is this a duplicate? imagesize_t m_total_imagesize; ///< Total size, uncompressed imagesize_t m_total_imagesize_ondisk; ///< Total size, compressed on disk ImageInput::Creator m_inputcreator; ///< Custom ImageInput-creator boost::scoped_ptr m_configspec; // Optional configuration hints UdimLookupMap m_udim_lookup; ///< Used for decoding udim tiles // protected by mutex elsewhere! /// We will need to read pixels from the file, so be sure it's /// currently opened. Return true if ok, false if error. bool open (ImageCachePerThreadInfo *thread_info); bool opened () const { return m_input.get() != NULL; } /// Force the file to open, thread-safe. bool forceopen (ImageCachePerThreadInfo *thread_info) { recursive_lock_guard guard (m_input_mutex); return open (thread_info); } /// Close and delete the ImageInput, if currently open /// void close (void); /// Load the requested tile, from a file that's not really tiled. /// Preconditions: the ImageInput is already opened, and we already did /// a seek_subimage to the right subimage and MIP level. bool read_untiled (ImageCachePerThreadInfo *thread_info, int subimage, int miplevel, int x, int y, int z, int chbegin, int chend, TypeDesc format, void *data); /// Load the requested tile, from a file that's not really MIPmapped. /// Preconditions: the ImageInput is already opened, and we already did /// a seek_subimage to the right subimage. bool read_unmipped (ImageCachePerThreadInfo *thread_info, int subimage, int miplevel, int x, int y, int z, int chbegin, int chend, TypeDesc format, void *data); void lock_input_mutex () { m_input_mutex.lock (); } void unlock_input_mutex () { m_input_mutex.unlock (); } // Initialize a bunch of fields based on the ImageSpec. // FIXME -- this is actually deeply flawed, many of these things only // make sense if they are per subimage, not one value for the whole // file. But it will require a bigger refactor to fix that. void init_from_spec (); friend class ImageCacheImpl; friend class TextureSystemImpl; }; /// Reference-counted pointer to a ImageCacheFile /// typedef intrusive_ptr ImageCacheFileRef; /// Map file names to file references typedef unordered_map_concurrent, 8> FilenameMap; #if OIIO_CPLUSPLUS_VERSION >= 11 typedef std::unordered_map FingerprintMap; #else /* FIXME(C++11): remove this after making C++11 the baseline */ typedef boost::unordered_map FingerprintMap; #endif /// Compact identifier for a particular tile of a particular image /// class TileID { public: /// Default constructor /// TileID () : m_file(NULL) { } /// Initialize a TileID based on full elaboration of image file, /// subimage, and tile x,y,z indices. TileID (ImageCacheFile &file, int subimage, int miplevel, int x, int y, int z=0, int chbegin=0, int chend=-1) : m_x(x), m_y(y), m_z(z), m_subimage(subimage), m_miplevel(miplevel), m_chbegin(chbegin), m_chend(chend), m_file(&file) { int nc = file.spec(subimage,miplevel).nchannels; if (chend < chbegin || chend > nc) m_chend = nc; } /// Destructor is trivial, because we don't hold any resources /// of our own. This is by design. ~TileID () { } ImageCacheFile &file (void) const { return *m_file; } ImageCacheFile *file_ptr (void) const { return m_file; } int subimage (void) const { return m_subimage; } int miplevel (void) const { return m_miplevel; } int x (void) const { return m_x; } int y (void) const { return m_y; } int z (void) const { return m_z; } int chbegin () const { return m_chbegin; } int chend () const { return m_chend; } int nchannels () const { return m_chend - m_chbegin; } void x (int v) { m_x = v; } void y (int v) { m_y = v; } void z (int v) { m_z = v; } void xy (int x, int y) { m_x = x; m_y = y; } void xyz (int x, int y, int z) { m_x = x; m_y = y; m_z = z; } /// Is this an uninitialized tileID? bool empty () const { return m_file == NULL; } /// Do the two ID's refer to the same tile? /// friend bool equal (const TileID &a, const TileID &b) { // Try to speed up by comparing field by field in order of most // probable rejection if they really are unequal. return (a.m_x == b.m_x && a.m_y == b.m_y && a.m_z == b.m_z && a.m_subimage == b.m_subimage && a.m_miplevel == b.m_miplevel && (a.m_file == b.m_file) && a.m_chbegin == b.m_chbegin && a.m_chend == b.m_chend); } /// Do the two ID's refer to the same tile, given that the /// caller *guarantees* that the two tiles point to the same /// file, subimage, and miplevel (so it only has to compare xyz)? friend bool equal_same_subimage (const TileID &a, const TileID &b) { DASSERT ((a.m_file == b.m_file) && a.m_subimage == b.m_subimage && a.m_miplevel == b.m_miplevel); return (a.m_x == b.m_x && a.m_y == b.m_y && a.m_z == b.m_z && a.m_chbegin == b.m_chbegin && a.m_chend == b.m_chend); } /// Do the two ID's refer to the same tile? /// bool operator== (const TileID &b) const { return equal (*this, b); } bool operator!= (const TileID &b) const { return ! equal (*this, b); } /// Digest the TileID into a size_t to use as a hash key. size_t hash () const { #if 0 // original -- turned out not to fill hash buckets evenly return m_x * 53 + m_y * 97 + m_z * 193 + m_subimage * 389 + m_miplevel * 1543 + m_file->filename().hash() * 769; #else // Good compromise! return bjhash::bjfinal (m_x+1543, m_y + 6151 + m_z*769, m_miplevel + (m_subimage<<8) + (chbegin()<<4) + nchannels()) + m_file->filename().hash(); #endif } /// Functor that hashes a TileID class Hasher { public: size_t operator() (const TileID &a) const { return a.hash(); } }; friend std::ostream& operator<< (std::ostream& o, const TileID &id) { return (o << "{xyz=" << id.m_x << ',' << id.m_y << ',' << id.m_z << ", sub=" << id.m_subimage << ", mip=" << id.m_miplevel << ", chans=[" << id.chbegin() << "," << id.chend() << ")" << ' ' << (id.m_file ? ustring("nofile") : id.m_file->filename()) << '}'); } private: int m_x, m_y, m_z; ///< x,y,z tile index within the subimage int m_subimage; ///< subimage int m_miplevel; ///< MIP-map level short m_chbegin, m_chend; ///< Channel range ImageCacheFile *m_file; ///< Which ImageCacheFile we refer to }; /// Record for a single image tile. /// class ImageCacheTile : public RefCnt { public: /// Construct a new tile, read the pixels from disk if read_now is true. /// Requires a pointer to the thread-specific IC data including /// microcache and statistics. ImageCacheTile (const TileID &id, ImageCachePerThreadInfo *thread_info, bool read_now=true); /// Construct a new tile out of the pixels supplied. /// ImageCacheTile (const TileID &id, const void *pels, TypeDesc format, stride_t xstride, stride_t ystride, stride_t zstride); ~ImageCacheTile (); /// Actually read the pixels. The caller had better be the thread /// that constructed the tile. void read (ImageCachePerThreadInfo *thread_info); /// Return pointer to the raw pixel data const void *data (void) const { return &m_pixels[0]; } /// Return pointer to the pixel data for a particular pixel. Be /// extremely sure the pixel is within this tile! const void *data (int x, int y, int z, int c) const; /// Return pointer to the floating-point pixel data const float *floatdata (void) const { return (const float *) &m_pixels[0]; } /// Return a pointer to the character data const unsigned char *bytedata (void) const { return (unsigned char *) &m_pixels[0]; } /// Return a pointer to unsigned short data const unsigned short *ushortdata (void) const { return (unsigned short *) &m_pixels[0]; } /// Return a pointer to half data const half *halfdata (void) const { return (half *) &m_pixels[0]; } /// Return the id for this tile. /// const TileID& id (void) const { return m_id; } const ImageCacheFile & file () const { return m_id.file(); } /// Return the actual allocated memory size for this tile's pixels. /// size_t memsize () const { return m_pixels_size; } /// Return the space that will be needed for this tile's pixels. /// size_t memsize_needed () const; /// Mark the tile as recently used. /// void use () { m_used = 1; } /// Mark the tile as not recently used, return its previous value. /// bool release () { if (! pixels_ready() || ! valid()) return true; // Don't really release invalid or unready tiles // If m_used is 1, set it to zero and return true. If it was already // zero, it's fine and return false. return atomic_compare_and_exchange ((volatile int *)&m_used, 1, 0); } /// Has this tile been recently used? /// int used (void) const { return m_used; } bool valid (void) const { return m_valid; } /// Are the pixels ready for use? If false, they're still being /// read from disk. bool pixels_ready () const { return m_pixels_ready; } /// Spin until the pixels have been read and are ready for use. /// void wait_pixels_ready () const; int channelsize () const { return m_channelsize; } int pixelsize () const { return m_pixelsize; } private: TileID m_id; ///< ID of this tile boost::scoped_array m_pixels; ///< The pixel data size_t m_pixels_size; ///< How much m_pixels has allocated int m_channelsize; ///< How big is each channel (bytes) int m_pixelsize; ///< How big is each pixel (bytes) bool m_valid; ///< Valid pixels volatile bool m_pixels_ready; ///< The pixels have been read from disk atomic_int m_used; ///< Used recently }; /// Reference-counted pointer to a ImageCacheTile /// typedef intrusive_ptr ImageCacheTileRef; /// Hash table that maps TileID to ImageCacheTileRef -- this is the type of the /// main tile cache. typedef unordered_map_concurrent, 32> TileCache; /// A very small amount of per-thread data that saves us from locking /// the mutex quite as often. We store things here used by both /// ImageCache and TextureSystem, so they don't each need a costly /// thread_specific_ptr retrieval. There's no real penalty for this, /// even if you are using only ImageCache but not TextureSystem. class ImageCachePerThreadInfo { public: // Store just a few filename/fileptr pairs static const int nlastfile = 4; ustring last_filename[nlastfile]; ImageCacheFile *last_file[nlastfile]; int next_last_file; // We have a two-tile "microcache", storing the last two tiles needed. ImageCacheTileRef tile, lasttile; atomic_int purge; // If set, tile ptrs need purging! ImageCacheStatistics m_stats; bool shared; // Pointed to both by the IC and the thread_specific_ptr ImageCachePerThreadInfo () : next_last_file(0), shared(false) { // std::cout << "Creating PerThreadInfo " << (void*)this << "\n"; for (int i = 0; i < nlastfile; ++i) last_file[i] = NULL; purge = 0; } ~ImageCachePerThreadInfo () { // std::cout << "Destroying PerThreadInfo " << (void*)this << "\n"; } // Add a new filename/fileptr pair to our microcache void filename (ustring n, ImageCacheFile *f) { last_filename[next_last_file] = n; last_file[next_last_file] = f; ++next_last_file; next_last_file %= nlastfile; } // See if a filename has a fileptr in the microcache ImageCacheFile *find_file (ustring n) const { for (int i = 0; i < nlastfile; ++i) if (last_filename[i] == n) return last_file[i]; return NULL; } }; /// Working implementation of the abstract ImageCache class. /// /// Some of the methods require a pointer to the thread-specific IC data /// including microcache and statistics. /// class ImageCacheImpl : public ImageCache { public: ImageCacheImpl (); virtual ~ImageCacheImpl (); virtual bool attribute (string_view name, TypeDesc type, const void *val); virtual bool attribute (string_view name, int val) { return attribute (name, TypeDesc::INT, &val); } virtual bool attribute (string_view name, float val) { return attribute (name, TypeDesc::FLOAT, &val); } virtual bool attribute (string_view name, double val) { float f = (float) val; return attribute (name, TypeDesc::FLOAT, &f); } virtual bool attribute (string_view name, string_view val) { const char *s = val.c_str(); return attribute (name, TypeDesc::STRING, &s); } virtual bool getattribute (string_view name, TypeDesc type, void *val) const; virtual bool getattribute (string_view name, int &val) const { return getattribute (name, TypeDesc::INT, &val); } virtual bool getattribute (string_view name, float &val) const { return getattribute (name, TypeDesc::FLOAT, &val); } virtual bool getattribute (string_view name, double &val) const { float f; bool ok = getattribute (name, TypeDesc::FLOAT, &f); if (ok) val = f; return ok; } virtual bool getattribute (string_view name, char **val) const { return getattribute (name, TypeDesc::STRING, val); } virtual bool getattribute (string_view name, std::string &val) const { ustring s; bool ok = getattribute (name, TypeDesc::STRING, &s); if (ok) val = s.string(); return ok; } // Retrieve options int max_open_files () const { return m_max_open_files; } const std::string &searchpath () const { return m_searchpath; } const std::string &plugin_searchpath () const { return m_plugin_searchpath; } int autotile () const { return m_autotile; } bool autoscanline () const { return m_autoscanline; } bool automip () const { return m_automip; } bool forcefloat () const { return m_forcefloat; } bool accept_untiled () const { return m_accept_untiled; } bool accept_unmipped () const { return m_accept_unmipped; } bool unassociatedalpha () const { return m_unassociatedalpha; } int failure_retries () const { return m_failure_retries; } bool latlong_y_up_default () const { return m_latlong_y_up_default; } void get_commontoworld (Imath::M44f &result) const { result = m_Mc2w; } int max_errors_per_file () const { return m_max_errors_per_file; } virtual std::string resolve_filename (const std::string &filename) const; // Set m_max_open_files, with logic to try to clamp reasonably. void set_max_open_files (int m); /// Get information about the given image. /// virtual bool get_image_info (ustring filename, int subimage, int miplevel, ustring dataname, TypeDesc datatype, void *data); virtual bool get_image_info (ImageCacheFile *file, ImageCachePerThreadInfo *thread_info, int subimage, int miplevel, ustring dataname, TypeDesc datatype, void *data); /// Get the ImageSpec associated with the named image. If the file /// is found and is an image format that can be read, store a copy /// of its specification in spec and return true. Return false if /// the file was not found or could not be opened as an image file /// by any available ImageIO plugin. virtual bool get_imagespec (ustring filename, ImageSpec &spec, int subimage=0, int miplevel=0, bool native=false); virtual bool get_imagespec (ImageCacheFile *file, ImageCachePerThreadInfo *thread_info, ImageSpec &spec, int subimage=0, int miplevel=0, bool native=false); virtual const ImageSpec *imagespec (ustring filename, int subimage=0, int miplevel=0, bool native=false); virtual const ImageSpec *imagespec (ImageCacheFile *file, ImageCachePerThreadInfo *thread_info=NULL, int subimage=0, int miplevel=0, bool native=false); // Retrieve a rectangle of raw unfiltered pixels. virtual bool get_pixels (ustring filename, int subimage, int miplevel, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, void *result); virtual bool get_pixels (ImageCacheFile *file, ImageCachePerThreadInfo *thread_info, int subimage, int miplevel, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, void *result); virtual bool get_pixels (ustring filename, int subimage, int miplevel, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc format, void *result, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride, int cache_chbegin = 0, int cache_chend = -1); virtual bool get_pixels (ImageCacheFile *file, ImageCachePerThreadInfo *thread_info, int subimage, int miplevel, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc format, void *result, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride, int cache_chbegin = 0, int cache_chend = -1); /// Find the ImageCacheFile record for the named image, or NULL if /// no such file can be found. This returns a plain old pointer, /// which is ok because the file hash table has ref-counted pointers /// and those won't be freed until the texture system is destroyed. /// If header_only is true, we are finding the file only for the sake /// of header information (e.g., called by get_image_info). /// A call to verify_file() is still needed after find_file(). ImageCacheFile *find_file (ustring filename, ImageCachePerThreadInfo *thread_info, ImageInput::Creator creator=NULL, bool header_only=false, const ImageSpec *config=NULL); /// Verify & prep the ImageCacheFile record for the named image, /// return the pointer (which may have changed for deduplication), /// or NULL if no such file can be found. This returns a plain old /// pointer, which is ok because the file hash table has ref-counted /// pointers and those won't be freed until the texture system is /// destroyed. If header_only is true, we are finding the file only /// for the sake of header information (e.g., called by /// get_image_info). ImageCacheFile *verify_file (ImageCacheFile *tf, ImageCachePerThreadInfo *thread_info, bool header_only=false); virtual ImageCacheFile * get_image_handle (ustring filename, ImageCachePerThreadInfo *thread_info=NULL) { ImageCacheFile *file = find_file (filename, thread_info); return verify_file (file, thread_info); } virtual bool good (ImageCacheFile *handle) { return handle && ! handle->broken(); } /// Is the tile specified by the TileID already in the cache? bool tile_in_cache (const TileID &id, ImageCachePerThreadInfo *thread_info) { TileCache::iterator found = m_tilecache.find (id); return (found != m_tilecache.end()); } /// Add the tile to the cache. This will also enforce cache memory /// limits. void add_tile_to_cache (ImageCacheTileRef &tile, ImageCachePerThreadInfo *thread_info); /// Find the tile specified by id. If found, return true and place /// the tile ref in thread_info->tile; if not found, return false. /// Try to avoid looking to the big cache (and locking) most of the /// time for fairly coherent tile access patterns, by using the /// per-thread microcache to boost our hit rate over the big cache. /// Inlined for speed. The tile is marked as 'used'. bool find_tile (const TileID &id, ImageCachePerThreadInfo *thread_info) { ++thread_info->m_stats.find_tile_calls; ImageCacheTileRef &tile (thread_info->tile); if (tile) { if (tile->id() == id) { tile->use (); return true; // already have the tile we want } // Tile didn't match, maybe lasttile will? Swap tile // and last tile. Then the new one will either match, // or we'll fall through and replace tile. tile.swap (thread_info->lasttile); if (tile && tile->id() == id) { tile->use (); return true; } } return find_tile_main_cache (id, tile, thread_info); // N.B. find_tile_main_cache marks the tile as used } virtual Tile *get_tile (ustring filename, int subimage, int miplevel, int x, int y, int z, int chbegin, int chend); virtual Tile *get_tile (ImageHandle *file, Perthread *thread_info, int subimage, int miplevel, int x, int y, int z, int chbegin, int chend); virtual void release_tile (Tile *tile) const; virtual TypeDesc tile_format (const Tile *tile) const; virtual ROI tile_roi (const Tile *tile) const; virtual const void * tile_pixels (Tile *tile, TypeDesc &format) const; virtual bool add_file (ustring filename, ImageInput::Creator creator, const ImageSpec *config); virtual bool add_tile (ustring filename, int subimage, int miplevel, int x, int y, int z, int chbegin, int chend, TypeDesc format, const void *buffer, stride_t xstride, stride_t ystride, stride_t zstride); /// Return the numerical subimage index for the given subimage name, /// as stored in the "oiio:subimagename" metadata. Return -1 if no /// subimage matches its name. int subimage_from_name (ImageCacheFile *file, ustring subimagename); virtual std::string geterror () const; virtual std::string getstats (int level=1) const; virtual void reset_stats (); virtual void invalidate (ustring filename); virtual void invalidate_all (bool force=false); /// Merge all the per-thread statistics into one set of stats. /// void mergestats (ImageCacheStatistics &merged) const; void operator delete (void *todel) { ::delete ((char *)todel); } /// Called when a new file is opened, so that the system can track /// the number of simultaneously-opened files. void incr_open_files (void) { ++m_stat_open_files_created; ++m_stat_open_files_current; if (m_stat_open_files_current > m_stat_open_files_peak) m_stat_open_files_peak = m_stat_open_files_current; // FIXME -- can we make an atomic_max? } /// Called when a file is closed, so that the system can track /// the number of simultyaneously-opened files. void decr_open_files (void) { --m_stat_open_files_current; } /// Called when a new tile is created, to update all the stats. /// void incr_tiles (size_t size) { ++m_stat_tiles_created; ++m_stat_tiles_current; if (m_stat_tiles_current > m_stat_tiles_peak) m_stat_tiles_peak = m_stat_tiles_current; m_mem_used += size; } /// Called when a tile's pixel memory is allocated, but a new tile /// is not created. void incr_mem (size_t size) { m_mem_used += size; } /// Called when a tile is destroyed, to update all the stats. /// void decr_tiles (size_t size) { --m_stat_tiles_current; m_mem_used -= size; DASSERT (m_mem_used >= 0); } /// Internal error reporting routine, with printf-like arguments. /// /// void error (const char *message, ...); TINYFORMAT_WRAP_FORMAT (void, error, const, std::ostringstream msg;, msg, append_error(msg.str());) /// Append a string to the current error message void append_error (const std::string& message) const; virtual Perthread * get_perthread_info (Perthread *thread_info = NULL); virtual Perthread * create_thread_info (); virtual void destroy_thread_info (Perthread *thread_info); /// Called when the IC is destroyed. We have a list of all the /// perthread pointers -- go through and delete the ones for which we /// hold the only remaining pointer. void erase_perthread_info (); /// This is called when the thread terminates. If p->m_imagecache /// is non-NULL, there's still an imagecache alive that might want /// the per-thread info (say, for statistics, though it's safe to /// clear its tile microcache), so don't delete the perthread info /// (it will be owned thereafter by the IC). If there is no IC still /// depending on it (signalled by m_imagecache == NULL), delete it. static void cleanup_perthread_info (Perthread *thread_info); /// Ensure that the max_memory_bytes is at least newsize bytes. /// Override the previous value if necessary, with thread-safety. void set_min_cache_size (long long newsize); /// Enforce the max number of open files. void check_max_files (ImageCachePerThreadInfo *thread_info); // For virtual UDIM-like files, adjust s and t and return the concrete // ImageCacheFile pointer for the tile it's on. ImageCacheFile *resolve_udim (ImageCacheFile *file, float &s, float &t); private: void init (); /// Find a tile identified by 'id' in the tile cache, paging it in if /// needed, and store a reference to the tile. Return true if ok, /// false if no such tile exists in the file or could not be read. bool find_tile_main_cache (const TileID &id, ImageCacheTileRef &tile, ImageCachePerThreadInfo *thread_info); /// Enforce the max memory for tile data. void check_max_mem (ImageCachePerThreadInfo *thread_info); /// Internal statistics printing routine /// void printstats () const; // Helper function for printstats() std::string onefile_stat_line (const ImageCacheFileRef &file, int i, bool includestats=true) const; /// Search the fingerprint table for the given fingerprint. If it /// doesn't already have an entry in the fingerprint map, then add /// one, mapping the it to file. In either case, return the file it /// maps to (the caller can tell if it was newly added to the table /// by whether the return value is the same as the passed-in file). /// All the while, properly maintain thread safety on the /// fingerprint table. ImageCacheFile *find_fingerprint (ustring finger, ImageCacheFile *file); /// Clear all the per-thread microcaches. void purge_perthread_microcaches (); /// Clear the fingerprint list, thread-safe. void clear_fingerprints (); thread_specific_ptr< ImageCachePerThreadInfo > m_perthread_info; std::vector m_all_perthread_info; static spin_mutex m_perthread_info_mutex; ///< Thread safety for perthread int m_max_open_files; atomic_ll m_max_memory_bytes; std::string m_searchpath; ///< Colon-separated image directory list std::vector m_searchdirs; ///< Searchpath split into dirs std::string m_plugin_searchpath; ///< Colon-separated plugin directory list int m_autotile; ///< if nonzero, pretend tiles of this size bool m_autoscanline; ///< autotile using full width tiles bool m_automip; ///< auto-mipmap on demand? bool m_forcefloat; ///< force all cache tiles to be float bool m_accept_untiled; ///< Accept untiled images? bool m_accept_unmipped; ///< Accept unmipped images? bool m_read_before_insert; ///< Read tiles before adding to cache? bool m_deduplicate; ///< Detect duplicate files? bool m_unassociatedalpha; ///< Keep unassociated alpha files as they are? int m_failure_retries; ///< Times to re-try disk failures bool m_latlong_y_up_default; ///< Is +y the default "up" for latlong? Imath::M44f m_Mw2c; ///< world-to-"common" matrix Imath::M44f m_Mc2w; ///< common-to-world matrix ustring m_substitute_image; ///< Substitute this image for all others mutable FilenameMap m_files; ///< Map file names to ImageCacheFile's ustring m_file_sweep_name; ///< Sweeper for "clock" paging algorithm spin_mutex m_file_sweep_mutex; ///< Ensure only one in check_max_files spin_mutex m_fingerprints_mutex; ///< Protect m_fingerprints FingerprintMap m_fingerprints; ///< Map fingerprints to files TileCache m_tilecache; ///< Our in-memory tile cache TileID m_tile_sweep_id; ///< Sweeper for "clock" paging algorithm spin_mutex m_tile_sweep_mutex; ///< Ensure only one in check_max_mem atomic_ll m_mem_used; ///< Memory being used for tiles int m_statslevel; ///< Statistics level int m_max_errors_per_file; ///< Max errors to print for each file. /// Saved error string, per-thread /// mutable thread_specific_ptr< std::string > m_errormessage; // For debugging -- keep track of who holds the tile and file mutex private: // Statistics that are really hard to track per-thread atomic_int m_stat_tiles_created; atomic_int m_stat_tiles_current; atomic_int m_stat_tiles_peak; atomic_int m_stat_open_files_created; atomic_int m_stat_open_files_current; atomic_int m_stat_open_files_peak; // Simulate an atomic double with a long long! void incr_time_stat (double &stat, double incr) { stat += incr; return; #ifdef NOTHREADS stat += incr; #else DASSERT (sizeof (atomic_ll) == sizeof(double)); double oldval, newval; long long *lloldval = (long long *)&oldval; long long *llnewval = (long long *)&newval; atomic_ll *llstat = (atomic_ll *)&stat; // Make long long and atomic_ll pointers to the doubles in question. do { // Grab the double bits, shove into a long long *lloldval = *llstat; // increment newval = oldval + incr; // Now try to atomically swap it, and repeat until we've // done it with nobody else interfering. } while (llstat->bool_compare_and_swap (*llnewval,*lloldval)); #endif } }; } // end namespace pvt OIIO_NAMESPACE_END #endif // OPENIMAGEIO_IMAGECACHE_PVT_H openimageio-1.7.17~dfsg0.orig/src/libtexture/texture_pvt.h0000644000175000017500000007243613151711064022061 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// \file /// Non-public classes used internally by TextureSystemImpl. #ifndef OPENIMAGEIO_TEXTURE_PVT_H #define OPENIMAGEIO_TEXTURE_PVT_H #include "OpenImageIO/texture.h" #include "OpenImageIO/simd.h" OIIO_NAMESPACE_BEGIN class ImageCache; class Filter1D; namespace pvt { class TextureSystemImpl; #ifndef OPENIMAGEIO_IMAGECACHE_PVT_H class ImageCacheImpl; class ImageCacheFile; class ImageCacheTile; class ImageCacheTileRef; #endif /// Working implementation of the abstract TextureSystem class. /// class TextureSystemImpl : public TextureSystem { public: typedef ImageCacheFile TextureFile; TextureSystemImpl (ImageCache *imagecache); virtual ~TextureSystemImpl (); virtual bool attribute (string_view name, TypeDesc type, const void *val); virtual bool attribute (string_view name, int val) { return attribute (name, TypeDesc::INT, &val); } virtual bool attribute (string_view name, float val) { return attribute (name, TypeDesc::FLOAT, &val); } virtual bool attribute (string_view name, double val) { float f = (float) val; return attribute (name, TypeDesc::FLOAT, &f); } virtual bool attribute (string_view name, string_view val) { const char *s = val.c_str(); return attribute (name, TypeDesc::STRING, &s); } virtual bool getattribute (string_view name, TypeDesc type, void *val) const; virtual bool getattribute (string_view name, int &val) const { return getattribute (name, TypeDesc::INT, &val); } virtual bool getattribute (string_view name, float &val) const { return getattribute (name, TypeDesc::FLOAT, &val); } virtual bool getattribute (string_view name, double &val) const { float f; bool ok = getattribute (name, TypeDesc::FLOAT, &f); if (ok) val = f; return ok; } virtual bool getattribute (string_view name, char **val) const { return getattribute (name, TypeDesc::STRING, val); } virtual bool getattribute (string_view name, std::string &val) const { const char *s; bool ok = getattribute (name, TypeDesc::STRING, &s); if (ok) val = s; return ok; } // Retrieve options void get_commontoworld (Imath::M44f &result) const { result = m_Mc2w; } virtual Perthread *get_perthread_info (Perthread *thread_info = NULL) { return (Perthread *)m_imagecache->get_perthread_info ((ImageCachePerThreadInfo *)thread_info); } virtual Perthread *create_thread_info () { ASSERT (m_imagecache); return (Perthread *)m_imagecache->create_thread_info (); } virtual void destroy_thread_info (Perthread *threadinfo) { ASSERT (m_imagecache); m_imagecache->destroy_thread_info ((ImageCachePerThreadInfo *)threadinfo); } virtual TextureHandle *get_texture_handle (ustring filename, Perthread *thread) { PerThreadInfo *thread_info = thread ? ((PerThreadInfo *)thread) : m_imagecache->get_perthread_info (); return (TextureHandle *) find_texturefile (filename, thread_info); } virtual bool good (TextureHandle *texture_handle) { return texture_handle && ! ((TextureFile *)texture_handle)->broken(); } virtual bool texture (ustring filename, TextureOpt &options, float s, float t, float dsdx, float dtdx, float dsdy, float dtdy, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL); virtual bool texture (TextureHandle *texture_handle, Perthread *thread_info, TextureOpt &options, float s, float t, float dsdx, float dtdx, float dsdy, float dtdy, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL); virtual bool texture (ustring filename, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef s, VaryingRef t, VaryingRef dsdx, VaryingRef dtdx, VaryingRef dsdy, VaryingRef dtdy, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL); virtual bool texture (TextureHandle *texture_handle, Perthread *thread_info, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef s, VaryingRef t, VaryingRef dsdx, VaryingRef dtdx, VaryingRef dsdy, VaryingRef dtdy, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL); virtual bool texture3d (ustring filename, TextureOpt &options, const Imath::V3f &P, const Imath::V3f &dPdx, const Imath::V3f &dPdy, const Imath::V3f &dPdz, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL, float *dresultdr=NULL); virtual bool texture3d (TextureHandle *texture_handle, Perthread *thread_info, TextureOpt &options, const Imath::V3f &P, const Imath::V3f &dPdx, const Imath::V3f &dPdy, const Imath::V3f &dPdz, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL, float *dresultdr=NULL); virtual bool texture3d (ustring filename, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef P, VaryingRef dPdx, VaryingRef dPdy, VaryingRef dPdz, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL, float *dresultdr=NULL); virtual bool texture3d (TextureHandle *texture_handle, Perthread *thread_info, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef P, VaryingRef dPdx, VaryingRef dPdy, VaryingRef dPdz, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL, float *dresultdr=NULL); virtual bool shadow (ustring filename, TextureOpt &options, const Imath::V3f &P, const Imath::V3f &dPdx, const Imath::V3f &dPdy, float *result, float *dresultds=NULL, float *dresultdt=NULL) { return false; } virtual bool shadow (TextureHandle *texture_handle, Perthread *thread_info, TextureOpt &options, const Imath::V3f &P, const Imath::V3f &dPdx, const Imath::V3f &dPdy, float *result, float *dresultds=NULL, float *dresultdt=NULL) { return false; } virtual bool shadow (ustring filename, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef P, VaryingRef dPdx, VaryingRef dPdy, float *result, float *dresultds=NULL, float *dresultdt=NULL) { return false; } virtual bool shadow (TextureHandle *texture_handle, Perthread *thread_info, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef P, VaryingRef dPdx, VaryingRef dPdy, float *result, float *dresultds=NULL, float *dresultdt=NULL) { return false; } virtual bool environment (ustring filename, TextureOpt &options, const Imath::V3f &R, const Imath::V3f &dRdx, const Imath::V3f &dRdy, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL); virtual bool environment (TextureHandle *texture_handle, Perthread *thread_info, TextureOpt &options, const Imath::V3f &R, const Imath::V3f &dRdx, const Imath::V3f &dRdy, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL); virtual bool environment (ustring filename, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef R, VaryingRef dRdx, VaryingRef dRdy, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL); virtual bool environment (TextureHandle *texture_handle, Perthread *thread_info, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef R, VaryingRef dRdx, VaryingRef dRdy, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL); virtual std::string resolve_filename (const std::string &filename) const; virtual bool get_texture_info (ustring filename, int subimage, ustring dataname, TypeDesc datatype, void *data); virtual bool get_texture_info (TextureHandle *texture_handle, Perthread *thread_info, int subimage, ustring dataname, TypeDesc datatype, void *data); virtual bool get_texture_info (TextureHandle *texture_handle, int subimage, ustring dataname, TypeDesc datatype, void *data) { return get_texture_info (texture_handle, NULL, subimage, dataname, datatype, data); } virtual bool get_imagespec (ustring filename, int subimage, ImageSpec &spec); virtual bool get_imagespec (TextureHandle *texture_handle, Perthread *thread_info, int subimage, ImageSpec &spec); virtual const ImageSpec *imagespec (ustring filename, int subimage=0); virtual const ImageSpec *imagespec (TextureHandle *texture_handle, Perthread *thread_info=NULL, int subimage=0); virtual bool get_texels (ustring filename, TextureOpt &options, int miplevel, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc format, void *result); virtual bool get_texels (TextureHandle *texture_handle, Perthread *thread_info, TextureOpt &options, int miplevel, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc format, void *result); virtual std::string geterror () const; virtual std::string getstats (int level=1, bool icstats=true) const; virtual void reset_stats (); virtual void invalidate (ustring filename); virtual void invalidate_all (bool force=false); void operator delete (void *todel) { ::delete ((char *)todel); } typedef bool (*wrap_impl) (int &coord, int origin, int width); private: typedef ImageCacheTileRef TileRef; typedef ImageCachePerThreadInfo PerThreadInfo; void init (); /// Find the TextureFile record for the named texture, or NULL if no /// such file can be found. TextureFile *find_texturefile (ustring filename, PerThreadInfo *thread_info) { return m_imagecache->find_file (filename, thread_info); } TextureFile *verify_texturefile (TextureFile *texturefile, PerThreadInfo *thread_info) { texturefile = m_imagecache->verify_file (texturefile, thread_info); if (!texturefile || texturefile->broken()) { std::string err = m_imagecache->geterror(); if (err.size()) error ("%s", err.c_str()); #if 0 // If the file is "broken", at least one verbose error message // has already been issued about it, so don't belabor the point. // But for debugging purposes, these might help: else if (texturefile && texturefile->broken()) error ("(unknown error - broken texture \"%s\")", texturefile->filename()); else error ("(unknown error - NULL texturefile)"); #endif } return texturefile; } /// Find the tile specified by id. If found, return true and place /// the tile ref in thread_info->tile; if not found, return false. /// This is more efficient than find_tile_main_cache() because it /// avoids looking to the big cache (and locking) most of the time /// for fairly coherent tile access patterns, by using the /// per-thread microcache to boost our hit rate over the big cache. /// Inlined for speed. bool find_tile (const TileID &id, PerThreadInfo *thread_info) { return m_imagecache->find_tile (id, thread_info); } // Define a prototype of a member function pointer for texture // lookups. // If simd is nonzero, it's guaranteed that all float* inputs and // outputs are padded to length 'simd' and aligned to a simd*4-byte // boundary (for example, 4 for SSE). This means that the functions can // behave AS IF the number of channels being retrieved is simd, and any // extra values returned will be discarded by the caller. typedef bool (TextureSystemImpl::*texture_lookup_prototype) (TextureFile &texfile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, float _s, float _t, float _dsdx, float _dtdx, float _dsdy, float _dtdy, float *result, float *dresultds, float *resultdt); /// Look up texture from just ONE point /// bool texture_lookup (TextureFile &texfile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, float _s, float _t, float _dsdx, float _dtdx, float _dsdy, float _dtdy, float *result, float *dresultds, float *resultdt); bool texture_lookup_nomip (TextureFile &texfile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, float _s, float _t, float _dsdx, float _dtdx, float _dsdy, float _dtdy, float *result, float *dresultds, float *resultdt); bool texture_lookup_trilinear_mipmap (TextureFile &texfile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, float _s, float _t, float _dsdx, float _dtdx, float _dsdy, float _dtdy, float *result, float *dresultds, float *resultdt); // For the samplers, it's guaranteed that all float* inputs and outputs // are padded to length 'simd' and aligned to a simd*4-byte boundary // (for example, 4 for SSE). This means that the functions can behave AS // IF the number of channels being retrieved is simd, and any extra // values returned will be discarded by the caller. typedef bool (TextureSystemImpl::*sampler_prototype) (int nsamples, const float *s, const float *t, int level, TextureFile &texturefile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, const float *weight, simd::float4 *accum, simd::float4 *daccumds, simd::float4 *daccumdt); bool sample_closest (int nsamples, const float *s, const float *t, int level, TextureFile &texturefile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, const float *weight, simd::float4 *accum, simd::float4 *daccumds, simd::float4 *daccumdt); bool sample_bilinear (int nsamples, const float *s, const float *t, int level, TextureFile &texturefile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, const float *weight, simd::float4 *accum, simd::float4 *daccumds, simd::float4 *daccumdt); bool sample_bicubic (int nsamples, const float *s, const float *t, int level, TextureFile &texturefile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, const float *weight, simd::float4 *accum, simd::float4 *daccumds, simd::float4 *daccumdt); // Define a prototype of a member function pointer for texture3d // lookups. typedef bool (TextureSystemImpl::*texture3d_lookup_prototype) (TextureFile &texfile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, const Imath::V3f &P, const Imath::V3f &dPdx, const Imath::V3f &dPdy, const Imath::V3f &dPdz, float *result, float *dresultds, float *dresultdt, float *dresultdr); bool texture3d_lookup_nomip (TextureFile &texfile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, const Imath::V3f &P, const Imath::V3f &dPdx, const Imath::V3f &dPdy, const Imath::V3f &dPdz, float *result, float *dresultds, float *dresultdt, float *dresultdr); typedef bool (TextureSystemImpl::*accum3d_prototype) (const Imath::V3f &P, int level, TextureFile &texturefile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, float weight, float *accum, float *daccumds, float *daccumdt, float *daccumdr); bool accum3d_sample_closest (const Imath::V3f &P, int level, TextureFile &texturefile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, float weight, float *accum, float *daccumds, float *daccumdt, float *daccumdr); bool accum3d_sample_bilinear (const Imath::V3f &P, int level, TextureFile &texturefile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, float weight, float *accum, float *daccumds, float *daccumdt, float *daccumdr); /// Helper function to calculate the anisotropic aspect ratio from /// the major and minor ellipse axis lengths. The "clamped" aspect /// ratio is returned (possibly adjusting major and minorlength to /// conform to the aniso limits) but the true aspect is stored in /// 'trueaspect'. static float anisotropic_aspect (float &majorlength, float &minorlength, TextureOpt& options, float &trueaspect); /// Convert texture coordinates (s,t), which range on 0-1 for the /// "full" image boundary, to texel coordinates (i+ifrac,j+jfrac) /// where (i,j) is the texel to the immediate upper left of the /// sample position, and ifrac and jfrac are the fractional (0-1) /// portion of the way to the next texel to the right or down, /// respectively. void st_to_texel (float s, float t, TextureFile &texturefile, const ImageSpec &spec, int &i, int &j, float &ifrac, float &jfrac); /// Called when the requested texture is missing, fills in the /// results. bool missing_texture (TextureOpt &options, int nchannels, float *result, float *dresultds, float *dresultdt, float *dresultdr=NULL); /// Handle gray-to-RGB promotion. void fill_gray_channels (const ImageSpec &spec, int nchannels, float *result, float *dresultds, float *dresultdt, float *dresultdr=NULL); static bool wrap_periodic_sharedborder (int &coord, int origin, int width); static const wrap_impl wrap_functions[]; /// Helper function for lat-long environment maps: compute a "pole" /// pixel that's the average of all of row y. This will only be /// called for levels where the whole mipmap level fits on one tile. const float *pole_color (TextureFile &texturefile, PerThreadInfo *thread_info, const ImageCacheFile::LevelInfo &levelinfo, TileRef &tile, int subimage, int miplevel, int pole); /// Helper function for lat-long environment maps: called near pole /// regions, this figures out the average pole color and fades to it /// right at the pole, and also adjusts weight so that the regular /// interpolated texture color will be added in correctly. /// This should only be called on the edge texels. void fade_to_pole (float t, float *accum, float &weight, TextureFile &texturefile, PerThreadInfo *thread_info, const ImageCacheFile::LevelInfo &levelinfo, TextureOpt &options, int miplevel, int nchannels); /// Perform short unit tests. void unit_test_texture (); /// Internal error reporting routine, with printf-like arguments. /// /// void error (const char *message, ...) const TINYFORMAT_WRAP_FORMAT (void, error, const, std::ostringstream msg;, msg, append_error(msg.str());) /// Append a string to the current error message void append_error (const std::string& message) const; void printstats () const; // Debugging aid void visualize_ellipse (const std::string &name, float dsdx, float dtdx, float dsdy, float dtdy, float sblur, float tblur); ImageCacheImpl *m_imagecache; Imath::M44f m_Mw2c; ///< world-to-"common" matrix Imath::M44f m_Mc2w; ///< common-to-world matrix bool m_gray_to_rgb; ///< automatically copy gray to rgb channels? bool m_flip_t; ///< Flip direction of t coord? int m_max_tile_channels; ///< narrow tile ID channel range when ///< the file has more channels /// Saved error string, per-thread /// mutable thread_specific_ptr< std::string > m_errormessage; Filter1D *hq_filter; ///< Better filter for magnification int m_statslevel; friend class TextureSystem; }; inline float TextureSystemImpl::anisotropic_aspect (float &majorlength, float &minorlength, TextureOpt& options, float &trueaspect) { float aspect = Imath::clamp (majorlength / minorlength, 1.0f, 1.0e6f); trueaspect = aspect; if (aspect > options.anisotropic) { aspect = options.anisotropic; // We have to clamp the ellipse to the maximum amount of anisotropy // that we allow. How do we do it? // a. Widen the short axis so we never alias along the major // axis, but we over-blur along the minor axis. I've never // been happy with this, it visibly overblurs. // b. Clamp the long axis so we don't blur, but might alias. // c. Split the difference, take the geometric mean, this makes // it slightly too blurry along the minor axis, slightly // aliasing along the major axis. You can't please everybody. if (options.conservative_filter) { #if 0 // Solution (a) -- never alias by blurring more along minor axis minorlength = majorlength / options.anisotropic; #else // Solution (c) -- this is our default, usually a nice balance. // We used to take the geometric mean... // majorlength = sqrtf ((majorlength) * // (minorlength * options.anisotropic)); // ...but frankly I find the average to be a little more // visually pleasing. majorlength = 0.5f * ((majorlength) + (minorlength * options.anisotropic)); minorlength = majorlength / options.anisotropic; #endif } else { // Solution (b) -- alias slightly, never overblur majorlength = minorlength * options.anisotropic; } } return aspect; } inline void TextureSystemImpl::st_to_texel (float s, float t, TextureFile &texturefile, const ImageSpec &spec, int &i, int &j, float &ifrac, float &jfrac) { // As passed in, (s,t) map the texture to (0,1). Remap to texel coords. // Note that we have two modes, depending on the m_sample_border. if (texturefile.m_sample_border == 0) { // texel samples are at 0.5/res, 1.5/res, ..., (res-0.5)/res, s = s * spec.width + spec.x - 0.5f; t = t * spec.height + spec.y - 0.5f; } else { // first and last rows/columns are *exactly* on the boundary, // so samples are at 0, 1/(res-1), ..., 1. s = s * (spec.width-1) + spec.x; t = t * (spec.height-1) + spec.y; } ifrac = floorfrac (s, &i); jfrac = floorfrac (t, &j); // Now (i,j) are the integer coordinates of the texel to the // immediate "upper left" of the lookup point, and (ifrac,jfrac) are // the amount that the lookup point is actually offset from the // texel center (with (1,1) being all the way to the next texel down // and to the right). } } // end namespace pvt OIIO_NAMESPACE_END #endif // OPENIMAGEIO_TEXTURE_PVT_H openimageio-1.7.17~dfsg0.orig/src/libtexture/texoptions.cpp0000644000175000017500000001332313151711064022225 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/varyingref.h" #include "OpenImageIO/ustring.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/texture.h" OIIO_NAMESPACE_BEGIN namespace { // anonymous static float default_blur = 0; static float default_width = 1; static float default_time = 0; static float default_bias = 0; static float default_fill = 0; static int default_samples = 1; static const ustring wrap_type_name[] = { // MUST match the order of TextureOptions::Wrap ustring("default"), ustring("black"), ustring("clamp"), ustring("periodic"), ustring("mirror"), ustring("periodic_pow2"), ustring("periodic_sharedborder"), ustring() }; } // end anonymous namespace /// Special private ctr that makes a canonical default TextureOptions. /// For use internal to libtexture. Users, don't call this! TextureOptions::TextureOptions () : firstchannel(0), subimage(0), swrap(TextureOptions::WrapDefault), twrap(TextureOptions::WrapDefault), mipmode(TextureOptions::MipModeDefault), interpmode(TextureOptions::InterpSmartBicubic), anisotropic(32), conservative_filter(true), sblur(default_blur), tblur(default_blur), swidth(default_width), twidth(default_width), time(default_time), bias(default_bias), fill(default_fill), missingcolor(NULL), samples(default_samples), rwrap(TextureOptions::WrapDefault), rblur(default_blur), rwidth(default_width) { } TextureOptions::TextureOptions (const TextureOpt &opt) : firstchannel(opt.firstchannel), subimage(opt.subimage), subimagename(opt.subimagename), swrap((Wrap)opt.swrap), twrap((Wrap)opt.twrap), mipmode((MipMode)opt.mipmode), interpmode((InterpMode)opt.interpmode), anisotropic(opt.anisotropic), conservative_filter(opt.conservative_filter), sblur((float *)&opt.sblur), tblur((float *)&opt.tblur), swidth((float *)&opt.swidth), twidth((float *)&opt.twidth), time((float *)&opt.time), bias((float *)&opt.bias), fill((float *)&opt.fill), missingcolor((void *)opt.missingcolor), samples((int *)&opt.samples), rwrap((Wrap)opt.rwrap), rblur((float *)&opt.rblur), rwidth((float *)&opt.rwidth) { } TextureOpt::TextureOpt (const TextureOptions &opt, int index) : firstchannel(opt.firstchannel), subimage(opt.subimage), subimagename(opt.subimagename), swrap((Wrap)opt.swrap), twrap((Wrap)opt.twrap), mipmode((MipMode)opt.mipmode), interpmode((InterpMode)opt.interpmode), anisotropic(opt.anisotropic), conservative_filter(opt.conservative_filter), sblur(opt.sblur[index]), tblur(opt.tblur[index]), swidth(opt.swidth[index]), twidth(opt.twidth[index]), fill(opt.fill[index]), missingcolor(opt.missingcolor.ptr() ? &opt.missingcolor[index] : NULL), time(opt.time[index]), bias(opt.bias[index]), samples(opt.samples[index]), rwrap((Wrap)opt.rwrap), rblur(opt.rblur[index]), rwidth(opt.rwidth[index]), envlayout(0) { } TextureOpt::Wrap TextureOpt::decode_wrapmode (const char *name) { for (int i = 0; i < (int)WrapLast; ++i) if (! strcmp (name, wrap_type_name[i].c_str())) return (Wrap) i; return TextureOpt::WrapDefault; } TextureOpt::Wrap TextureOpt::decode_wrapmode (ustring name) { for (int i = 0; i < (int)WrapLast; ++i) if (name == wrap_type_name[i]) return (Wrap) i; return TextureOpt::WrapDefault; } void TextureOpt::parse_wrapmodes (const char *wrapmodes, TextureOpt::Wrap &swrapcode, TextureOpt::Wrap &twrapcode) { char *swrap = (char *) alloca (strlen(wrapmodes)+1); const char *twrap; int i; for (i = 0; wrapmodes[i] && wrapmodes[i] != ','; ++i) swrap[i] = wrapmodes[i]; swrap[i] = 0; if (wrapmodes[i] == ',') twrap = wrapmodes + i+1; else twrap = swrap; swrapcode = decode_wrapmode (swrap); twrapcode = decode_wrapmode (twrap); } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libtexture/imagecache.cpp0000644000175000017500000040301213151711064022055 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/varyingref.h" #include "OpenImageIO/ustring.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/sysutil.h" #include "OpenImageIO/timer.h" #include "OpenImageIO/optparser.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagecache.h" #include "OpenImageIO/texture.h" #include "OpenImageIO/simd.h" #include "imagecache_pvt.h" #include #include OIIO_NAMESPACE_BEGIN using namespace pvt; namespace pvt { // The static perthread mutex needs to outlive the shared_image_cache // instance, so must be declared first in this file to avoid static // initialization order problems. spin_mutex ImageCacheImpl::m_perthread_info_mutex; } namespace { // anonymous static shared_ptr shared_image_cache; static spin_mutex shared_image_cache_mutex; // Make some static ustring constants to avoid strcmp's static ustring s_resolution ("resolution"), s_texturetype ("texturetype"); static ustring s_textureformat ("textureformat"), s_fileformat ("fileformat"); static ustring s_format ("format"), s_cachedformat ("cachedformat"); static ustring s_channels ("channels"), s_cachedpixeltype ("cachedpixeltype"); static ustring s_exists ("exists"), s_broken ("broken"); static ustring s_UDIM ("UDIM"); static ustring s_subimages ("subimages"), s_miplevels ("miplevels"); static ustring s_datawindow ("datawindow"), s_displaywindow ("displaywindow"); static ustring s_averagecolor ("averagecolor"), s_averagealpha ("averagealpha"); static ustring s_constantcolor ("constantcolor"), s_constantalpha ("constantalpha"); // Functor to compare filenames static bool filename_compare (const ImageCacheFileRef &a, const ImageCacheFileRef &b) { return a->filename() < b->filename(); } // Functor to compare read bytes, sort in descending order static bool bytesread_compare (const ImageCacheFileRef &a, const ImageCacheFileRef &b) { return a->bytesread() > b->bytesread(); } // Functor to compare read times, sort in descending order static bool iotime_compare (const ImageCacheFileRef &a, const ImageCacheFileRef &b) { return a->iotime() > b->iotime(); } // Functor to compare read rate (MB/s), sort in ascending order static bool iorate_compare (const ImageCacheFileRef &a, const ImageCacheFileRef &b) { double arate = (a->iotime() == 0)? 0 : a->bytesread()/(1024.0*1024.0) / a->iotime(); double brate = (b->iotime() == 0)? 0 : b->bytesread()/(1024.0*1024.0) / b->iotime(); return arate < brate; } // Functor to compare amount of redundant reading, sort in descending order static bool redundantbytes_compare (const ImageCacheFileRef &a, const ImageCacheFileRef &b) { return a->redundant_bytesread() > b->redundant_bytesread(); } }; // end anonymous namespace namespace pvt { // namespace pvt void ImageCacheStatistics::init () { // ImageCache stats: find_tile_calls = 0; find_tile_microcache_misses = 0; find_tile_cache_misses = 0; // tiles_created = 0; // tiles_current = 0; // tiles_peak = 0; files_totalsize = 0; files_totalsize_ondisk = 0; bytes_read = 0; // open_files_created = 0; // open_files_current = 0; // open_files_peak = 0; unique_files = 0; fileio_time = 0; fileopen_time = 0; file_locking_time = 0; tile_locking_time = 0; find_file_time = 0; find_tile_time = 0; // TextureSystem stats: texture_queries = 0; texture_batches = 0; texture3d_queries = 0; texture3d_batches = 0; shadow_queries = 0; shadow_batches = 0; environment_queries = 0; environment_batches = 0; aniso_queries = 0; aniso_probes = 0; max_aniso = 1; closest_interps = 0; bilinear_interps = 0; cubic_interps = 0; file_retry_success = 0; tile_retry_success = 0; } void ImageCacheStatistics::merge (const ImageCacheStatistics &s) { // ImageCache stats: find_tile_calls += s.find_tile_calls; find_tile_microcache_misses += s.find_tile_microcache_misses; find_tile_cache_misses += s.find_tile_cache_misses; // tiles_created += s.tiles_created; // tiles_current += s.tiles_current; // tiles_peak += s.tiles_peak; files_totalsize += s.files_totalsize; files_totalsize_ondisk += s.files_totalsize_ondisk; bytes_read += s.bytes_read; // open_files_created += s.open_files_created; // open_files_current += s.open_files_current; // open_files_peak += s.open_files_peak; unique_files += s.unique_files; fileio_time += s.fileio_time; fileopen_time += s.fileopen_time; file_locking_time += s.file_locking_time; tile_locking_time += s.tile_locking_time; find_file_time += s.find_file_time; find_tile_time += s.find_tile_time; // TextureSystem stats: texture_queries += s.texture_queries; texture_batches += s.texture_batches; texture3d_queries += s.texture3d_queries; texture3d_batches += s.texture3d_batches; shadow_queries += s.shadow_queries; shadow_batches += s.shadow_batches; environment_queries += s.environment_queries; environment_batches += s.environment_batches; aniso_queries += s.aniso_queries; aniso_probes += s.aniso_probes; max_aniso = std::max (max_aniso, s.max_aniso); closest_interps += s.closest_interps; bilinear_interps += s.bilinear_interps; cubic_interps += s.cubic_interps; file_retry_success += s.file_retry_success; tile_retry_success += s.tile_retry_success; } ImageCacheFile::LevelInfo::LevelInfo (const ImageSpec &spec_, const ImageSpec &nativespec_) : spec(spec_), nativespec(nativespec_) { full_pixel_range = (spec.x == spec.full_x && spec.y == spec.full_y && spec.z == spec.full_z && spec.width == spec.full_width && spec.height == spec.full_height && spec.depth == spec.full_depth); onetile = (spec.width <= spec.tile_width && spec.height <= spec.tile_height && spec.depth <= spec.tile_depth); polecolorcomputed = false; // Allocate bit field for which tiles have been read at least once. if (onetile) { nxtiles = 1; nytiles = 1; nztiles = 1; } else { nxtiles = (spec.width + spec.tile_width - 1) / spec.tile_width; nytiles = (spec.height + spec.tile_height - 1) / spec.tile_height; nztiles = (spec.depth + spec.tile_depth - 1) / spec.tile_depth; } int total_tiles = nxtiles * nytiles * nztiles; ASSERT (total_tiles >= 1); tiles_read = new atomic_ll [round_to_multiple (total_tiles, 64) / 64]; } ImageCacheFile::LevelInfo::LevelInfo (const LevelInfo &src) : spec(src.spec), nativespec(src.nativespec), full_pixel_range(src.full_pixel_range), onetile(src.onetile), polecolorcomputed(src.polecolorcomputed), polecolor(src.polecolor), nxtiles(src.nxtiles), nytiles(src.nytiles), nztiles(src.nztiles) { int nwords = round_to_multiple (nxtiles * nytiles * nztiles, 64) / 64; tiles_read = new atomic_ll [nwords]; for (int i = 0; i < nwords; ++i) tiles_read[i] = src.tiles_read[i]; } ImageCacheFile::ImageCacheFile (ImageCacheImpl &imagecache, ImageCachePerThreadInfo *thread_info, ustring filename, ImageInput::Creator creator, const ImageSpec *config) : m_filename(filename), m_used(true), m_broken(false), m_texformat(TexFormatTexture), m_swrap(TextureOpt::WrapBlack), m_twrap(TextureOpt::WrapBlack), m_rwrap(TextureOpt::WrapBlack), m_envlayout(LayoutTexture), m_y_up(false), m_sample_border(false), m_is_udim(false), m_tilesread(0), m_bytesread(0), m_redundant_tiles(0), m_redundant_bytesread(0), m_timesopened(0), m_iotime(0), m_mipused(false), m_validspec(false), m_errors_issued(0), m_imagecache(imagecache), m_duplicate(NULL), m_total_imagesize(0), m_total_imagesize_ondisk(0), m_inputcreator(creator), m_configspec(config ? new ImageSpec(*config) : NULL) { m_filename_original = m_filename; m_filename = imagecache.resolve_filename (m_filename_original.string()); // N.B. the file is not opened, the ImageInput is NULL. This is // reflected by the fact that m_validspec is false. #if USE_SHADOW_MATRICES m_Mlocal.makeIdentity(); m_Mproj.makeIdentity(); m_Mtex.makeIdentity(); m_Mras.makeIdentity(); #endif // Figure out if it's a UDIM-like virtual texture if (! Filesystem::exists(m_filename.string()) && (m_filename.find("") != m_filename.npos || m_filename.find("") != m_filename.npos || m_filename.find("") != m_filename.npos || m_filename.find("") != m_filename.npos || m_filename.find("") != m_filename.npos)) { m_is_udim = true; } } ImageCacheFile::~ImageCacheFile () { close (); } void ImageCacheFile::SubimageInfo::init (const ImageSpec &spec, bool forcefloat) { volume = (spec.depth > 1 || spec.full_depth > 1); full_pixel_range = (spec.x == spec.full_x && spec.y == spec.full_y && spec.z == spec.full_z && spec.width == spec.full_width && spec.height == spec.full_height && spec.depth == spec.full_depth); if (! full_pixel_range) { sscale = float(spec.full_width) / spec.width; soffset = float(spec.full_x-spec.x) / spec.width; tscale = float(spec.full_height) / spec.height; toffset = float(spec.full_y-spec.y) / spec.height; } else { sscale = tscale = 1.0f; soffset = toffset = 0.0f; } subimagename = ustring (spec.get_string_attribute("oiio:subimagename")); datatype = TypeDesc::FLOAT; if (! forcefloat) { // If we aren't forcing everything to be float internally, then // there are a few other types we allow. if (spec.format == TypeDesc::UINT8 || spec.format == TypeDesc::UINT16 || spec.format == TypeDesc::HALF /* future expansion: || spec.format == AnotherFormat ... */) datatype = spec.format; } channelsize = datatype.size(); pixelsize = channelsize * spec.nchannels; // See if there's a constant color tag string_view software = spec.get_string_attribute ("Software"); bool from_maketx = Strutil::istarts_with (software, "OpenImageIO") || Strutil::istarts_with (software, "maketx"); string_view constant_color = spec.get_string_attribute ("oiio:ConstantColor"); if (from_maketx && constant_color.size()) { while (constant_color.size()) { float val; if (! Strutil::parse_float (constant_color, val)) break; average_color.push_back (val); if (! Strutil::parse_char (constant_color, ',')) break; } if (average_color.size() == size_t(spec.nchannels)) { is_constant_image = true; has_average_color = true; } } // See if there's an average color tag string_view avgcolor = spec.get_string_attribute ("oiio:AverageColor"); if (from_maketx && avgcolor.size()) { while (avgcolor.size()) { float val; if (! Strutil::parse_float (avgcolor, val)) break; average_color.push_back (val); if (! Strutil::parse_char (avgcolor, ',')) break; } if (average_color.size() == size_t(spec.nchannels)) has_average_color = true; } } bool ImageCacheFile::open (ImageCachePerThreadInfo *thread_info) { // N.B. open() does not need to lock the m_input_mutex, because open() // itself is only called by routines that hold the lock. // recursive_lock_guard_t guard (m_input_mutex); if (m_input) // Already opened return !m_broken; if (m_broken) // Already failed an open -- it's broken return false; if (m_inputcreator) m_input.reset (m_inputcreator()); else m_input.reset (ImageInput::create (m_filename.string(), m_imagecache.plugin_searchpath())); if (! m_input) { imagecache().error ("%s", OIIO::geterror()); m_broken = true; invalidate_spec (); return false; } ImageSpec configspec; if (m_configspec) configspec = *m_configspec; if (imagecache().unassociatedalpha()) configspec.attribute ("oiio:UnassociatedAlpha", 1); ImageSpec nativespec, tempspec; m_broken = false; bool ok = true; for (int tries = 0; tries <= imagecache().failure_retries(); ++tries) { ok = m_input->open (m_filename.c_str(), nativespec, configspec); if (ok) { tempspec = nativespec; if (tries) // succeeded, but only after a failure! ++thread_info->m_stats.file_retry_success; (void) m_input->geterror (); // Eat the errors break; } if (tries < imagecache().failure_retries()) { // We failed, but will wait a bit and try again. Sysutil::usleep (1000 * 100); // 100 ms } } if (! ok) { imagecache().error ("%s", m_input->geterror()); m_broken = true; m_input.reset (); return false; } m_fileformat = ustring (m_input->format_name()); ++m_timesopened; m_imagecache.incr_open_files (); use (); // If we are simply re-opening a closed file, and the spec is still // valid, we're done, no need to reread the subimage and mip headers. if (validspec()) return true; // From here on, we know that we've opened this file for the very // first time. So read all the subimages, fill out all the fields // of the ImageCacheFile. m_subimages.clear (); int nsubimages = 0; // Since each subimage can potentially have its own mipmap levels, // keep track of the highest level discovered imagesize_t old_total_imagesize = m_total_imagesize; imagesize_t old_total_imagesize_ondisk = m_total_imagesize_ondisk; m_total_imagesize = 0; do { m_subimages.resize (nsubimages+1); SubimageInfo &si (subimageinfo(nsubimages)); int nmip = 0; do { tempspec = nativespec; if (nmip == 0) { // Things to do on MIP level 0, i.e. once per subimage si.init (tempspec, imagecache().forcefloat()); } if (tempspec.tile_width == 0 || tempspec.tile_height == 0) { si.untiled = true; int autotile = imagecache().autotile(); if (autotile) { // Automatically make it appear as if it's tiled if (imagecache().autoscanline()) { tempspec.tile_width = tempspec.width; } else { tempspec.tile_width = std::min (tempspec.width, autotile); } tempspec.tile_height = std::min (tempspec.height, autotile); tempspec.tile_depth = std::min (std::max(tempspec.depth,1), autotile); } else { // Don't auto-tile -- which really means, make it look like // a single tile that's as big as the whole image. // We round to a power of 2 because the texture system // currently requires power of 2 tile sizes. tempspec.tile_width = tempspec.width; tempspec.tile_height = tempspec.height; tempspec.tile_depth = tempspec.depth; } } // thread_info->m_stats.files_totalsize += tempspec.image_bytes(); m_total_imagesize += tempspec.image_bytes(); // All MIP levels need the same number of channels if (nmip > 0 && tempspec.nchannels != spec(nsubimages,0).nchannels) { // No idea what to do with a subimage that doesn't have the // same number of channels as the others, so just skip it. close (); m_broken = true; invalidate_spec (); return false; } // ImageCache can't store differing formats per channel tempspec.channelformats.clear(); LevelInfo levelinfo (tempspec, nativespec); si.levels.push_back (levelinfo); ++nmip; } while (m_input->seek_subimage (nsubimages, nmip, nativespec)); // Special work for non-MIPmapped images -- but only if "automip" // is on, it's a non-mipmapped image, and it doesn't have a // "textureformat" attribute (because that would indicate somebody // constructed it as texture and specifically wants it un-mipmapped). // But not volume textures -- don't auto MIP them for now. if (nmip == 1 && !si.volume && (tempspec.width > 1 || tempspec.height > 1 || tempspec.depth > 1)) si.unmipped = true; if (si.unmipped && imagecache().automip() && ! tempspec.find_attribute ("textureformat", TypeDesc::TypeString)) { int w = tempspec.full_width; int h = tempspec.full_height; int d = tempspec.full_depth; while (w > 1 || h > 1 || d > 1) { w = std::max (1, w/2); h = std::max (1, h/2); d = std::max (1, d/2); ImageSpec s = tempspec; s.width = w; s.height = h; s.depth = d; s.full_width = w; s.full_height = h; s.full_depth = d; if (imagecache().autotile()) { if (imagecache().autoscanline()) { s.tile_width = w; } else { s.tile_width = std::min (imagecache().autotile(), w); } s.tile_height = std::min (imagecache().autotile(), h); s.tile_depth = std::min (imagecache().autotile(), d); } else { s.tile_width = w; s.tile_height = h; s.tile_depth = d; } ++nmip; LevelInfo levelinfo (s, s); si.levels.push_back (levelinfo); } } if (si.untiled && ! imagecache().accept_untiled()) { imagecache().error ("%s was untiled, rejecting", m_filename); m_broken = true; invalidate_spec (); m_input.reset (); return false; } if (si.unmipped && ! imagecache().accept_unmipped()) { imagecache().error ("%s was not MIP-mapped, rejecting", m_filename); m_broken = true; invalidate_spec (); m_input.reset (); return false; } ++nsubimages; } while (m_input->seek_subimage (nsubimages, 0, nativespec)); ASSERT ((size_t)nsubimages == m_subimages.size()); if (Filesystem::exists(m_filename.string())) m_total_imagesize_ondisk = imagesize_t(Filesystem::file_size (m_filename)); else m_total_imagesize_ondisk = 0; thread_info->m_stats.files_totalsize -= old_total_imagesize; thread_info->m_stats.files_totalsize += m_total_imagesize; thread_info->m_stats.files_totalsize_ondisk -= old_total_imagesize_ondisk; thread_info->m_stats.files_totalsize_ondisk += m_total_imagesize_ondisk; init_from_spec (); // Fill in the rest of the fields return true; } void ImageCacheFile::init_from_spec () { const ImageSpec &spec (this->spec(0,0)); const ImageIOParameter *p; // FIXME -- this should really be per-subimage if (spec.depth <= 1 && spec.full_depth <= 1) m_texformat = TexFormatTexture; else m_texformat = TexFormatTexture3d; if ((p = spec.find_attribute ("textureformat", TypeDesc::STRING))) { const char *textureformat = *(const char **)p->data(); for (int i = 0; i < TexFormatLast; ++i) if (Strutil::iequals (textureformat, texture_format_name((TexFormat)i))) { m_texformat = (TexFormat) i; break; } // For textures marked as such, doctor the full_width/full_height to // not be non-sensical. if (m_texformat == TexFormatTexture) { for (int s = 0; s < subimages(); ++s) { for (int m = 0; m < miplevels(s); ++m) { ImageSpec &spec (this->spec(s,m)); if (spec.full_width > spec.width) spec.full_width = spec.width; if (spec.full_height > spec.height) spec.full_height = spec.height; if (spec.full_depth > spec.depth) spec.full_depth = spec.depth; } } } } if ((p = spec.find_attribute ("wrapmodes", TypeDesc::STRING))) { const char *wrapmodes = *(const char **)p->data(); TextureOpt::parse_wrapmodes (wrapmodes, m_swrap, m_twrap); m_rwrap = m_swrap; // FIXME(volume) -- rwrap } m_y_up = m_imagecache.latlong_y_up_default(); m_sample_border = false; if (m_texformat == TexFormatLatLongEnv || m_texformat == TexFormatCubeFaceEnv || m_texformat == TexFormatCubeFaceShadow) { if (spec.get_string_attribute ("oiio:updirection") == "y") m_y_up = true; else if (spec.get_string_attribute ("oiio:updirection") == "z") m_y_up = false; if (spec.get_int_attribute ("oiio:sampleborder") != 0) m_sample_border = true; } if (m_texformat == TexFormatCubeFaceEnv || m_texformat == TexFormatCubeFaceShadow) { int w = std::max (spec.full_width, spec.tile_width); int h = std::max (spec.full_height, spec.tile_height); if (spec.width == 3*w && spec.height == 2*h) m_envlayout = LayoutCubeThreeByTwo; else if (spec.width == w && spec.height == 6*h) m_envlayout = LayoutCubeOneBySix; else m_envlayout = LayoutTexture; } #if USE_SHADOW_MATRICES Imath::M44f c2w; m_imagecache.get_commontoworld (c2w); if ((p = spec.find_attribute ("worldtocamera", TypeDesc::TypeMatrix))) { const Imath::M44f *m = (const Imath::M44f *)p->data(); m_Mlocal = c2w * (*m); } if ((p = spec.find_attribute ("worldtoscreen", TypeDesc::TypeMatrix))) { const Imath::M44f *m = (const Imath::M44f *)p->data(); m_Mproj = c2w * (*m); } // FIXME -- compute Mtex, Mras #endif // See if there's a SHA-1 hash in the image description std::string fing = spec.get_string_attribute ("oiio:SHA-1"); if (fing.length()) { m_fingerprint = ustring(fing); // If it looks like something other than OIIO wrote the file, forget // the fingerprint, it probably is not accurate. string_view software = spec.get_string_attribute ("Software"); if (! Strutil::istarts_with (software, "OpenImageIO") && ! Strutil::istarts_with (software, "maketx")) m_fingerprint.clear (); } m_mod_time = Filesystem::last_write_time (m_filename.string()); // Set all mipmap level read counts to zero int maxmip = 1; for (int s = 0, nsubimages = subimages(); s < nsubimages; ++s) maxmip = std::max (maxmip, miplevels(s)); m_mipreadcount.clear (); m_mipreadcount.resize(maxmip, 0); DASSERT (! m_broken); m_validspec = true; } bool ImageCacheFile::read_tile (ImageCachePerThreadInfo *thread_info, int subimage, int miplevel, int x, int y, int z, int chbegin, int chend, TypeDesc format, void *data) { ASSERT (chend > chbegin); recursive_lock_guard guard (m_input_mutex); if (! m_input && !m_broken) { // The file is already in the file cache, but the handle is // closed. We will need to re-open, so we must make sure there // will be enough file handles. // But wait, it's possible that somebody else is waiting on our // m_input_mutex, which we locked above. To avoid deadlock, we // need to release m_input_mutex while we close files. m_input_mutex.unlock (); imagecache().check_max_files (thread_info); // Now we're back, whew! Grab the lock again. m_input_mutex.lock (); } bool ok = open (thread_info); if (! ok) return false; // Mark if we ever use a mip level that's not the first if (miplevel > 0) m_mipused = true; // count how many times this mipmap level was read m_mipreadcount[miplevel]++; SubimageInfo &subinfo (subimageinfo(subimage)); // Special case for un-MIP-mapped if (subinfo.unmipped && miplevel != 0) { // For a non-base mip level of an unmipped file, release the // mutex on the ImageInput since upper levels don't need to // directly perform I/O. This prevents the deadlock that could // occur if another thread has one of the lower-level tiles and // itself blocks on the mutex (it's waiting for our mutex, we're // waiting on its tile to get filled with pixels). unlock_input_mutex (); bool ok = read_unmipped (thread_info, subimage, miplevel, x, y, z, chbegin, chend, format, data); // The lock_guard at the very top will try to unlock upon // destruction, to to make things right, we need to re-lock. lock_input_mutex (); return ok; } // Special case for untiled images -- need to do tile emulation if (subinfo.untiled) return read_untiled (thread_info, subimage, miplevel, x, y, z, chbegin, chend, format, data); // Ordinary tiled ImageSpec tmp; if (m_input->current_subimage() != subimage || m_input->current_miplevel() != miplevel) ok = m_input->seek_subimage (subimage, miplevel, tmp); if (ok) { for (int tries = 0; tries <= imagecache().failure_retries(); ++tries) { const ImageSpec &spec (m_input->spec()); ok = m_input->read_tiles (x, x+spec.tile_width, y, y+spec.tile_height, z, z+spec.tile_depth, chbegin, chend, format, data); if (ok) { if (tries) // succeeded, but only after a failure! ++thread_info->m_stats.tile_retry_success; (void) m_input->geterror (); // Eat the errors break; } if (tries < imagecache().failure_retries()) { // We failed, but will wait a bit and try again. Sysutil::usleep (1000 * 100); // 100 ms // TODO: should we attempt to close and re-open the file? } } if (! ok) { std::string err = m_input->geterror(); if (!err.empty() && errors_should_issue()) imagecache().error ("%s", err); } } if (ok) { size_t b = spec(subimage,miplevel).tile_bytes(); thread_info->m_stats.bytes_read += b; m_bytesread += b; ++m_tilesread; } return ok; } bool ImageCacheFile::read_unmipped (ImageCachePerThreadInfo *thread_info, int subimage, int miplevel, int x, int y, int z, int chbegin, int chend, TypeDesc format, void *data) { // We need a tile from an unmipmapped file, and it doesn't really // exist. So generate it out of thin air by interpolating pixels // from the next higher-res level. Of course, that may also not // exist, but it will be generated recursively, since we call // imagecache->get_pixels(), and it will ask for other tiles, which // will again call read_unmipped... eventually it will hit a subimage 0 // tile that actually exists. // N.B. No need to lock the mutex, since this is only called // from read_tile, which already holds the lock. // Figure out the size and strides for a single tile, make an ImageBuf // to hold it temporarily. const ImageSpec &spec (this->spec(subimage,miplevel)); int tw = spec.tile_width; int th = spec.tile_height; ASSERT (chend > chbegin); int nchans = chend - chbegin; ImageSpec lospec (tw, th, nchans, TypeDesc::FLOAT); ImageBuf lores (lospec); // Figure out the range of texels we need for this tile x -= spec.x; y -= spec.y; z -= spec.z; int x0 = x - (x % spec.tile_width); int x1 = std::min (x0+spec.tile_width-1, spec.full_width-1); int y0 = y - (y % spec.tile_height); int y1 = std::min (y0+spec.tile_height-1, spec.full_height-1); // int z0 = z - (z % spec.tile_depth); // int z1 = std::min (z0+spec.tile_depth-1, spec.full_depth-1); // Save the contents of the per-thread microcache. This is because // a caller several levels up may be retaining a reference to // thread_info->tile and expecting it not to suddenly point to a // different tile id! It's a very reasonable assumption that if you // ask to read the last-found tile, it will still be the last-found // tile after the pixels are read. Well, except that below our call // to get_pixels may recursively trigger more tiles to be read, and // totally change the microcache. Simple solution: save & restore it. ImageCacheTileRef oldtile = thread_info->tile; ImageCacheTileRef oldlasttile = thread_info->lasttile; // Auto-mipping will totally thrash the cache if the user unwisely // sets it to be too small compared to the image file that needs to // automipped. So we simply override bad decisions by adjusting the // cache size to be a minimum of twice as big as any image we automip. imagecache().set_min_cache_size (2 * (long long)this->spec(subimage,0).image_bytes()); // Texel by texel, generate the values by interpolating filtered // lookups form the next finer subimage. const ImageSpec &upspec (this->spec(subimage,miplevel-1)); // next higher level float *bilerppels = (float *) alloca (4 * nchans * sizeof(float)); float *resultpel = (float *) alloca (nchans * sizeof(float)); bool ok = true; // FIXME(volume) -- loop over z, too for (int j = y0; j <= y1; ++j) { float yf = (j+0.5f) / spec.full_height; int ylow; float yfrac = floorfrac (yf * upspec.full_height - 0.5, &ylow); for (int i = x0; i <= x1; ++i) { float xf = (i+0.5f) / spec.full_width; int xlow; float xfrac = floorfrac (xf * upspec.full_width - 0.5, &xlow); ok &= imagecache().get_pixels (this, thread_info, subimage, miplevel-1, xlow, xlow+2, ylow, ylow+2, 0, 1, chbegin, chend, TypeDesc::FLOAT, bilerppels); bilerp (bilerppels+0, bilerppels+nchans, bilerppels+2*nchans, bilerppels+3*nchans, xfrac, yfrac, nchans, resultpel); lores.setpixel (i-x0, j-y0, resultpel); } } // Now convert and copy those values out to the caller's buffer lores.get_pixels (ROI (0, tw, 0, th, 0, 1, chbegin, chend), format, data); // Restore the microcache to the way it was before. thread_info->tile = oldtile; thread_info->lasttile = oldlasttile; return ok; } // Helper routine for read_tile that handles the rare (but tricky) case // of reading a "tile" from a file that's scanline-oriented. bool ImageCacheFile::read_untiled (ImageCachePerThreadInfo *thread_info, int subimage, int miplevel, int x, int y, int z, int chbegin, int chend, TypeDesc format, void *data) { // N.B. No need to lock the input mutex, since this is only called // from read_tile, which already holds the lock. if (m_input->current_subimage() != subimage || m_input->current_miplevel() != miplevel) { ImageSpec tmp; if (! m_input->seek_subimage (subimage, miplevel, tmp)) { std::string err = m_input->geterror(); if (!err.empty() && errors_should_issue()) imagecache().error ("%s", err); return false; } } // Strides for a single tile ImageSpec &spec (this->spec(subimage,miplevel)); int tw = spec.tile_width; int th = spec.tile_height; ASSERT (chend > chbegin); int nchans = chend - chbegin; stride_t xstride=AutoStride, ystride=AutoStride, zstride=AutoStride; spec.auto_stride (xstride, ystride, zstride, format, nchans, tw, th); bool ok = true; if (imagecache().autotile()) { // Auto-tile is on, with a tile size that isn't the whole image. // We're only being asked for one tile, but since it's a // scanline image, we are forced to read (at the very least) a // whole row of tiles. So we add all those tiles to the cache, // if not already present, on the assumption that it's highly // likely that they will also soon be requested. // FIXME -- I don't think this works properly for 3D images size_t pixelsize = size_t (nchans * format.size()); // Because of the way we copy below, we need to allocate the // buffer to be an even multiple of the tile width, so round up. stride_t scanlinesize = tw * ((spec.width+tw-1)/tw); scanlinesize *= pixelsize; boost::scoped_array buf (new char [scanlinesize * th]); // a whole tile-row size int yy = y - spec.y; // counting from top scanline // [y0,y1] is the range of scanlines to read for a tile-row int y0 = yy - (yy % th); int y1 = std::min (y0 + th - 1, spec.height - 1); y0 += spec.y; y1 += spec.y; // Read the whole tile-row worth of scanlines ok = m_input->read_scanlines (y0, y1+1, z, chbegin, chend, format, (void *)&buf[0], pixelsize, scanlinesize); if (! ok) { std::string err = m_input->geterror(); if (!err.empty() && errors_should_issue()) imagecache().error ("%s", err); } size_t b = (y1-y0+1) * spec.scanline_bytes(); thread_info->m_stats.bytes_read += b; m_bytesread += b; ++m_tilesread; // At this point, we aren't reading from the file any longer, // and to avoid deadlock, we MUST release the input lock prior // to any attempt to add_tile_to_cache, lest another thread add // the same tile to the cache before us but need the input mutex // to actually read the texels before marking it as pixels_ready. unlock_input_mutex (); // For all tiles in the tile-row, enter them into the cache if not // already there. Special case for the tile we're actually being // asked for -- save it in 'data' rather than adding a tile. int xx = x - spec.x; // counting from left row int x0 = xx - (xx % tw); // start of the tile we are retrieving for (int i = 0; i < spec.width; i += tw) { if (i == xx) { // This is the tile we've been asked for convert_image (nchans, tw, th, 1, &buf[x0 * pixelsize], format, pixelsize, scanlinesize, scanlinesize*th, data, format, xstride, ystride, zstride); } else { // Not the tile we asked for, but it's in the same // tile-row, so let's put it in the cache anyway so // it'll be there when asked for. TileID id (*this, subimage, miplevel, i+spec.x, y0, z, chbegin, chend); if (! imagecache().tile_in_cache (id, thread_info)) { ImageCacheTileRef tile; tile = new ImageCacheTile (id, &buf[i*pixelsize], format, pixelsize, scanlinesize, scanlinesize*th); ok &= tile->valid (); imagecache().add_tile_to_cache (tile, thread_info); } } } // The lock_guard inside the calling function, read_tile, passed // us the input_mutex locked, and expects to get it back the // same way, so we need to re-lock. lock_input_mutex (); } else { // No auto-tile -- the tile is the whole image ok = m_input->read_image (chbegin, chend, format, data, xstride, ystride, zstride); if (! ok) { std::string err = m_input->geterror(); if (!err.empty() && errors_should_issue()) imagecache().error ("%s", err); } size_t b = spec.image_bytes(); thread_info->m_stats.bytes_read += b; m_bytesread += b; ++m_tilesread; // If we read the whole image, presumably we're done, so release // the file handle. close (); } return ok; } void ImageCacheFile::close () { // N.B. close() does not need to lock the m_input_mutex, because close() // itself is only called by routines that hold the lock. if (opened()) { m_input->close (); m_input.reset (); m_imagecache.decr_open_files (); } } void ImageCacheFile::release () { recursive_lock_guard guard (m_input_mutex); if (m_used) m_used = false; else close (); } void ImageCacheFile::invalidate () { recursive_lock_guard guard (m_input_mutex); close (); invalidate_spec (); m_broken = false; m_fingerprint.clear (); duplicate (NULL); m_filename = m_imagecache.resolve_filename (m_filename_original.string()); // Eat any errors that occurred in the open/close while (! imagecache().geterror().empty()) ; m_errors_issued = 0; // start error count fresh } bool ImageCacheFile::get_average_color (float *avg, int subimage, int chbegin, int chend) { if (subimage < 0 || subimage > subimages()) return false; // invalid subimage SubimageInfo &si (m_subimages[subimage]); if (! si.has_average_color) { // try to figure it out by grabbing the single pixel at the 1x1 // MIP level. int nlevels = (int) si.levels.size(); const ImageSpec &spec (si.spec(nlevels-1)); // The 1x1 level if (spec.width != 1 || spec.height != 1 || spec.depth != 1) return false; // no hope, there's no 1x1 MIP level to sample spin_lock lock (si.average_color_mutex); if (! si.has_average_color) { si.average_color.resize (spec.nchannels); bool ok = m_imagecache.get_pixels (this, NULL, subimage, nlevels-1, spec.x, spec.x+1, spec.y, spec.y+1, spec.z, spec.z+1, 0, spec.nchannels, TypeDesc::TypeFloat, &si.average_color[0]); si.has_average_color = ok; } } if (si.has_average_color) { const ImageSpec &spec (si.spec(0)); for (int i = 0, c = chbegin; c < chend; ++i, ++c) avg[i] = (c < spec.nchannels) ? si.average_color[c] : 0.0f; return true; } return false; } int ImageCacheFile::errors_should_issue () const { return (++m_errors_issued <= imagecache().max_errors_per_file()); } ImageCacheFile * ImageCacheImpl::find_file (ustring filename, ImageCachePerThreadInfo *thread_info, ImageInput::Creator creator, bool header_only, const ImageSpec *config) { // Debugging aid: attribute "substitute_image" forces all image // references to be to one named file. if (m_substitute_image) filename = m_substitute_image; // Shortcut - check the per-thread microcache before grabbing a more // expensive lock on the shared file cache. ImageCacheFile *tf = thread_info->find_file (filename); // Make sure the ImageCacheFile entry exists and is in the // file cache. For this part, we need to lock the file cache. bool newfile = false; if (! tf) { // was not found in microcache #if IMAGECACHE_TIME_STATS Timer timer; #endif size_t bin = m_files.lock_bin (filename); FilenameMap::iterator found = m_files.find (filename, false); if (found) { tf = found->second.get(); } else { // No such entry in the file cache. Add it, but don't open yet. tf = new ImageCacheFile (*this, thread_info, filename, creator, config); m_files.insert (filename, tf, false); newfile = true; } m_files.unlock_bin (bin); if (newfile) { check_max_files (thread_info); if (! tf->duplicate()) ++thread_info->m_stats.unique_files; } thread_info->filename (filename, tf); // add to the microcache #if IMAGECACHE_TIME_STATS thread_info->m_stats.find_file_time += timer(); #endif } // Ensure that it's open and do other important housekeeping. // tf = verify_file (tf, thread_info, header_only); return tf; } ImageCacheFile * ImageCacheImpl::verify_file (ImageCacheFile *tf, ImageCachePerThreadInfo *thread_info, bool header_only) { if (! tf) return NULL; if (tf->is_udim()) { // Can't really open a UDIM-like virtual file return tf; } // Open the file if it's never been opened before. // No need to have the file cache locked for this, though we lock // the tf->m_input_mutex if we need to open it. if (! tf->validspec()) { Timer timer; if (! thread_info) thread_info = get_perthread_info (); recursive_lock_guard guard (tf->m_input_mutex); if (! tf->validspec()) { tf->open (thread_info); DASSERT (tf->m_broken || tf->validspec()); double createtime = timer(); ImageCacheStatistics &stats (thread_info->m_stats); stats.fileio_time += createtime; stats.fileopen_time += createtime; tf->iotime() += createtime; // What if we've opened another file, with a different name, // but the SAME pixels? It can happen! Bad user, bad! But // let's save them from their own foolishness. if (tf->fingerprint() && m_deduplicate) { // std::cerr << filename << " hash=" << tf->fingerprint() << "\n"; ImageCacheFile *dup = find_fingerprint (tf->fingerprint(), tf); if (dup != tf) { // Already in fingerprints -- mark this one as a // duplicate, but ONLY if we don't have other // reasons not to consider them true duplicates (the // fingerprint only considers source image pixel values. // FIXME -- be sure to add extra tests // here if more metadata have significance later! bool match = (tf->subimages() == dup->subimages()); match &= (tf->m_swrap == dup->m_swrap && tf->m_twrap == dup->m_twrap && tf->m_rwrap == dup->m_rwrap && tf->m_envlayout == dup->m_envlayout && tf->m_y_up == dup->m_y_up && tf->m_sample_border == dup->m_sample_border); for (int s = 0, e = tf->subimages(); match && s < e; ++s) { match &= (tf->datatype(s) == dup->datatype(s)); } if (match) { tf->duplicate (dup); tf->close (); // std::cerr << " duplicates " // << fingerfound->second.get()->filename() << "\n"; } } } #if IMAGECACHE_TIME_STATS stats.find_file_time += timer()-createtime; #endif } } if (! header_only) { // if this is a duplicate texture, switch to the canonical copy // N.B. If looking up header info (i.e., get_image_info, rather // than getting pixels, use the original not the duplicate, since // metadata may differ even if pixels are identical). if (tf->duplicate()) tf = tf->duplicate(); tf->use (); // Mark it as recently used } return tf; } ImageCacheFile * ImageCacheImpl::find_fingerprint (ustring finger, ImageCacheFile *file) { spin_lock lock (m_fingerprints_mutex); FingerprintMap::iterator found = m_fingerprints.find (finger); if (found == m_fingerprints.end()) { // Not already in the fingerprint list -- add it m_fingerprints[finger] = file; } else { // In the list -- return its mapping file = found->second.get(); } return file; } void ImageCacheImpl::clear_fingerprints () { spin_lock lock (m_fingerprints_mutex); m_fingerprints.clear (); } void ImageCacheImpl::check_max_files (ImageCachePerThreadInfo *thread_info) { #if 0 if (! (m_stat_open_files_created % 16) || m_stat_open_files_current >= m_max_open_files) { std::cerr << "open files " << m_stat_open_files_current << ", max = " << m_max_open_files << "\n"; std::cout << " ImageInputs : " << m_stat_open_files_created << " created, " << m_stat_open_files_current << " current, " << m_stat_open_files_peak << " peak\n"; } #endif // Early out if we aren't exceeding the open file handle limit if (m_stat_open_files_current < m_max_open_files) return; // Try to grab the file_sweep_mutex lock. If somebody else holds it, // just return -- leave the handle limit enforcement to whomever is // already in this function, no need for two threads to do it at // once. If this means we may ephemerally be over the handle limit, // so be it. if (! m_file_sweep_mutex.try_lock()) return; // Now, what we want to do is have a "clock hand" that sweeps across // the cache, releasing files that haven't been used for a long // time. Because of multi-thread, rather than keep an iterator // around for this (which could be invalidated since the last time // we used it), we just remember the filename of the next file to // check, then look it up fresh. That is m_file_sweep_name. // If we don't have a valid file_sweep_name, establish it by just // looking up the filename of the first entry in the file cache. if (! m_file_sweep_name) { FilenameMap::iterator sweep = m_files.begin(); if (sweep == m_files.end()) { m_file_sweep_mutex.unlock(); return; } m_file_sweep_name = sweep->first; } // Get a (locked) iterator for the next file to be examined. FilenameMap::iterator sweep = m_files.find (m_file_sweep_name); // Loop while we still have too many files open. Also, be careful // of looping for too long, exit the loop if we just keep spinning // uncontrollably. int full_loops = 0; FilenameMap::iterator end = m_files.end(); while (m_stat_open_files_current >= m_max_open_files && full_loops <= 100) { // If we have fallen off the end of the cache, loop back to the // beginning and increment our full_loops count. if (sweep == end) { sweep = m_files.begin(); ++full_loops; } // If we're STILL at the end, it must be that somehow the entire // cache is empty. So just declare ourselves done. if (sweep == end) break; DASSERT (sweep->second); sweep->second->release (); // May reduce open files ++sweep; } // OK, by this point we have either closed enough files to be below // the limit again, or the cache is empty, or we've looped over the // cache too many times and are giving up. // Now we must save the filename for next time. Just set it to an // empty string if we don't have a valid iterator at this point. m_file_sweep_name = (sweep == end ? ustring() : sweep->first); m_file_sweep_mutex.unlock (); // N.B. As we exit, the iterators will go out of scope and we will // retain no locks on the cache. } void ImageCacheImpl::set_min_cache_size (long long newsize) { long long oldsize = m_max_memory_bytes; while (newsize > oldsize) { if (atomic_compare_and_exchange ((long long *)&m_max_memory_bytes, oldsize, newsize)) return; oldsize = m_max_memory_bytes; } } ImageCacheTile::ImageCacheTile (const TileID &id, ImageCachePerThreadInfo *thread_info, bool read_now) : m_id (id), m_valid(true) // , m_used(true) { m_used = true; m_pixels_ready = false; m_pixels_size = 0; if (read_now) { read (thread_info); } id.file().imagecache().incr_tiles (0); // mem counted separately in read } ImageCacheTile::ImageCacheTile (const TileID &id, const void *pels, TypeDesc format, stride_t xstride, stride_t ystride, stride_t zstride) : m_id (id) // , m_used(true) { m_used = true; m_pixels_size = 0; ImageCacheFile &file (m_id.file ()); const ImageSpec &spec (file.spec(id.subimage(), id.miplevel())); m_channelsize = file.datatype(id.subimage()).size(); m_pixelsize = id.nchannels() * m_channelsize; size_t size = memsize_needed (); ASSERT_MSG (size > 0 && memsize() == 0, "size was %llu, memsize = %llu", (unsigned long long)size, (unsigned long long)memsize()); m_pixels.reset (new char [m_pixels_size = size]); m_valid = convert_image (id.nchannels(), spec.tile_width, spec.tile_height, spec.tile_depth, pels, format, xstride, ystride, zstride, &m_pixels[0], file.datatype(id.subimage()), m_pixelsize, m_pixelsize * spec.tile_width, m_pixelsize * spec.tile_width * spec.tile_height); id.file().imagecache().incr_tiles (size); m_pixels_ready = true; // Caller sent us the pixels, no read necessary // FIXME -- for shadow, fill in mindepth, maxdepth } ImageCacheTile::~ImageCacheTile () { m_id.file().imagecache().decr_tiles (memsize ()); } size_t ImageCacheTile::memsize_needed () const { const ImageSpec &spec (file().spec(m_id.subimage(),m_id.miplevel())); size_t s = spec.tile_pixels() * pixelsize(); // N.B. Round up so we can use a SIMD fetch for the last pixel and // channel without running off the end. s += OIIO_SIMD_MAX_SIZE_BYTES; return s; } void ImageCacheTile::read (ImageCachePerThreadInfo *thread_info) { ImageCacheFile &file (m_id.file()); m_channelsize = file.datatype(id().subimage()).size(); m_pixelsize = m_id.nchannels() * m_channelsize; size_t size = memsize_needed (); ASSERT (memsize() == 0 && size > OIIO_SIMD_MAX_SIZE_BYTES); m_pixels.reset (new char [m_pixels_size = size]); // Clear the end pad values so there aren't NaNs sucked up by simd loads memset (m_pixels.get() + size - OIIO_SIMD_MAX_SIZE_BYTES, 0, OIIO_SIMD_MAX_SIZE_BYTES); m_valid = file.read_tile (thread_info, m_id.subimage(), m_id.miplevel(), m_id.x(), m_id.y(), m_id.z(), m_id.chbegin(), m_id.chend(), file.datatype(m_id.subimage()), &m_pixels[0]); m_id.file().imagecache().incr_mem (size); if (m_valid) { // Figure out if ImageCacheFile::LevelInfo &lev (file.levelinfo (m_id.subimage(), m_id.miplevel())); int whichtile = ((m_id.x() - lev.spec.x) / lev.spec.tile_width) + ((m_id.y() - lev.spec.y) / lev.spec.tile_height) * lev.nxtiles + ((m_id.z() - lev.spec.z) / lev.spec.tile_depth) * (lev.nxtiles*lev.nytiles); int index = whichtile / 64; int64_t bitmask = int64_t (1ULL << (whichtile & 63)); int64_t oldval = lev.tiles_read[index].fetch_or (bitmask); if (oldval & bitmask) // Was it previously read? file.register_redundant_tile (lev.spec.tile_bytes()); } else { // (! m_valid) m_used = false; // Don't let it hold mem if invalid #if 0 std::cerr << "(1) error reading tile " << m_id.x() << ' ' << m_id.y() << ' ' << m_id.z() << " subimg=" << m_id.subimage() << " mip=" << m_id.miplevel() << " from " << file.filename() << "\n"; #endif } m_pixels_ready = true; // FIXME -- for shadow, fill in mindepth, maxdepth } void ImageCacheTile::wait_pixels_ready () const { atomic_backoff backoff; while (! m_pixels_ready) { backoff(); } } const void * ImageCacheTile::data (int x, int y, int z, int c) const { const ImageSpec &spec = m_id.file().spec (m_id.subimage(), m_id.miplevel()); size_t w = spec.tile_width; size_t h = spec.tile_height; size_t d = spec.tile_depth; DASSERT (d >= 1); x -= m_id.x(); y -= m_id.y(); z -= m_id.z(); if (x < 0 || x >= (int)w || y < 0 || y >= (int)h || z < 0 || z >= (int)d || c < m_id.chbegin() || c > m_id.chend()) return NULL; size_t offset = ((z * h + y) * w + x) * pixelsize() + (c-m_id.chbegin()) * channelsize(); return (const void *)&m_pixels[offset]; } ImageCacheImpl::ImageCacheImpl () : m_perthread_info (&cleanup_perthread_info) { init (); } void ImageCacheImpl::set_max_open_files (int max_open_files) { // Clamp to somewhat less than the maximum number of files allowed // by the system. int m = int (std::min (Sysutil::max_open_files(), size_t(std::numeric_limits::max()))); m = std::max (10, m - 5 * int(Sysutil::hardware_concurrency())); m_max_open_files = std::min (max_open_files, m); // std::cout << "clamped max_open_files = " << m_max_open_files << "\n"; } void ImageCacheImpl::init () { set_max_open_files (100); m_max_memory_bytes = 256 * 1024 * 1024; // 256 MB default cache size m_autotile = 0; m_autoscanline = false; m_automip = false; m_forcefloat = false; m_accept_untiled = true; m_accept_unmipped = true; m_read_before_insert = false; m_deduplicate = true; m_unassociatedalpha = false; m_failure_retries = 0; m_latlong_y_up_default = true; m_Mw2c.makeIdentity(); m_mem_used = 0; m_statslevel = 0; m_max_errors_per_file = 100; m_stat_tiles_created = 0; m_stat_tiles_current = 0; m_stat_tiles_peak = 0; m_stat_open_files_created = 0; m_stat_open_files_current = 0; m_stat_open_files_peak = 0; // Allow environment variable to override default options const char *options = getenv ("OPENIMAGEIO_IMAGECACHE_OPTIONS"); if (options) attribute ("options", options); } ImageCacheImpl::~ImageCacheImpl () { printstats (); erase_perthread_info (); } void ImageCacheImpl::mergestats (ImageCacheStatistics &stats) const { stats.init (); spin_lock lock (m_perthread_info_mutex); for (size_t i = 0; i < m_all_perthread_info.size(); ++i) stats.merge (m_all_perthread_info[i]->m_stats); } std::string ImageCacheImpl::onefile_stat_line (const ImageCacheFileRef &file, int i, bool includestats) const { // FIXME -- make meaningful stat printouts for multi-image textures std::ostringstream out; const ImageSpec &spec (file->spec(0,0)); const char *formatcode = "u8"; switch (spec.format.basetype) { case TypeDesc::UINT8 : formatcode = "u8 "; break; case TypeDesc::INT8 : formatcode = "i8 "; break; case TypeDesc::UINT16 : formatcode = "u16"; break; case TypeDesc::INT16 : formatcode = "i16"; break; case TypeDesc::UINT : formatcode = "u32"; break; case TypeDesc::INT : formatcode = "i32"; break; case TypeDesc::UINT64 : formatcode = "i64"; break; case TypeDesc::INT64 : formatcode = "u64"; break; case TypeDesc::HALF : formatcode = "f16"; break; case TypeDesc::FLOAT : formatcode = "f32"; break; case TypeDesc::DOUBLE : formatcode = "f64"; break; default: break; } if (i >= 0) out << Strutil::format ("%7d ", i); if (includestats) { unsigned long long redund_tiles = file->redundant_tiles(); if (redund_tiles) out << Strutil::format ("%4llu %7llu %8.1f (%5llu %6.1f) %9s ", (unsigned long long) file->timesopened(), (unsigned long long) file->tilesread(), file->bytesread()/1024.0/1024.0, redund_tiles, file->redundant_bytesread()/1024.0/1024.0, Strutil::timeintervalformat(file->iotime())); else out << Strutil::format ("%4llu %7llu %8.1f %9s ", (unsigned long long) file->timesopened(), (unsigned long long) file->tilesread(), file->bytesread()/1024.0/1024.0, Strutil::timeintervalformat(file->iotime())); } if (file->subimages() > 1) out << Strutil::format ("%3d face x%d.%s", file->subimages(), spec.nchannels, formatcode); else out << Strutil::format ("%4dx%4dx%d.%s", spec.width, spec.height, spec.nchannels, formatcode); out << " " << file->filename() << " "; if (file->duplicate()) { out << " DUPLICATES " << file->duplicate()->filename(); return out.str(); } for (int s = 0; s < file->subimages(); ++s) if (file->subimageinfo(s).untiled) { out << " UNTILED"; break; } if (automip()) { // FIXME -- we should directly measure whether we ever automipped // this file. This is a little inexact. for (int s = 0; s < file->subimages(); ++s) if (file->subimageinfo(s).unmipped) { out << " UNMIPPED"; break; } } if (! file->mipused()) { for (int s = 0; s < file->subimages(); ++s) if (! file->subimageinfo(s).unmipped) { out << " MIP-UNUSED"; break; } } if (file->mipreadcount().size() > 1) { out << " MIP-COUNT["; int nmip = (int) file->mipreadcount().size(); for (int c = 0; c < nmip; c++) out << (c ? "," : "") << file->mipreadcount()[c]; out << "]"; } return out.str (); } std::string ImageCacheImpl::getstats (int level) const { // Merge all the threads ImageCacheStatistics stats; mergestats (stats); // Gather file list and statistics size_t total_opens = 0, total_tiles = 0; size_t total_redundant_tiles = 0; imagesize_t total_bytes = 0; imagesize_t total_redundant_bytes = 0; size_t total_untiled = 0, total_unmipped = 0, total_duplicates = 0; size_t total_constant = 0; double total_iotime = 0; std::vector files; { for (FilenameMap::iterator f = m_files.begin(); f != m_files.end(); ++f) { const ImageCacheFileRef &file (f->second); files.push_back (file); total_opens += file->timesopened(); total_tiles += file->tilesread(); total_redundant_tiles += file->redundant_tiles(); total_redundant_bytes += file->redundant_bytesread(); total_bytes += file->bytesread(); total_iotime += file->iotime(); if (file->duplicate()) { ++total_duplicates; continue; } bool found_untiled = false, found_unmipped = false; bool found_const = true; for (int s = 0, send = file->subimages(); s < send; ++s) { const ImageCacheFile::SubimageInfo &si (file->subimageinfo(s)); found_untiled |= si.untiled; found_unmipped |= si.unmipped; found_const &= si.is_constant_image; } total_untiled += found_untiled; total_unmipped += found_unmipped; total_constant += found_const; } } std::ostringstream out; if (level > 0) { out << "OpenImageIO ImageCache statistics ("; { spin_lock guard (shared_image_cache_mutex); if ((void *)this == (void *)shared_image_cache.get()) out << "shared"; else out << (void *)this; } out << ") ver " << OIIO_VERSION_STRING << "\n"; std::string opt; #define BOOLOPT(name) if (m_##name) opt += #name " " #define INTOPT(name) opt += Strutil::format(#name "=%d ", m_##name) #define STROPT(name) if (m_##name.size()) opt += Strutil::format(#name "=\"%s\" ", m_##name) opt += Strutil::format("max_memory_MB=%0.1f ", m_max_memory_bytes/(1024.0*1024.0)); INTOPT(max_open_files); INTOPT(autotile); INTOPT(autoscanline); INTOPT(automip); INTOPT(forcefloat); INTOPT(accept_untiled); INTOPT(accept_unmipped); INTOPT(read_before_insert); INTOPT(deduplicate); INTOPT(unassociatedalpha); INTOPT(failure_retries); #undef BOOLOPT #undef INTOPT #undef STROPT out << " Options: " << Strutil::wordwrap(opt,75,12) << "\n"; if (stats.unique_files) { out << " Images : " << stats.unique_files << " unique\n"; out << " ImageInputs : " << m_stat_open_files_created << " created, " << m_stat_open_files_current << " current, " << m_stat_open_files_peak << " peak\n"; out << " Total pixel data size of all images referenced : " << Strutil::memformat (stats.files_totalsize) << "\n"; out << " Total actual file size of all images referenced : " << Strutil::memformat (stats.files_totalsize_ondisk) << "\n"; out << " Pixel data read : " << Strutil::memformat (stats.bytes_read) << "\n"; } else { out << " No images opened\n"; } if (stats.find_file_time > 0.001) out << " Find file time : " << Strutil::timeintervalformat (stats.find_file_time) << "\n"; if (stats.fileio_time > 0.001) { out << " File I/O time : " << Strutil::timeintervalformat (stats.fileio_time); { spin_lock lock (m_perthread_info_mutex); size_t nthreads = m_all_perthread_info.size(); if (nthreads > 1) { double perthreadtime = stats.fileio_time / (float)nthreads; out << " (" << Strutil::timeintervalformat (perthreadtime) << " average per thread)"; } } out << "\n"; out << " File open time only : " << Strutil::timeintervalformat (stats.fileopen_time) << "\n"; } if (stats.file_locking_time > 0.001) out << " File mutex locking time : " << Strutil::timeintervalformat (stats.file_locking_time) << "\n"; if (m_stat_tiles_created > 0) { out << " Tiles: " << m_stat_tiles_created << " created, " << m_stat_tiles_current << " current, " << m_stat_tiles_peak << " peak\n"; out << " total tile requests : " << stats.find_tile_calls << "\n"; out << " micro-cache misses : " << stats.find_tile_microcache_misses << " (" << 100.0*(double)stats.find_tile_microcache_misses/(double)stats.find_tile_calls << "%)\n"; out << " main cache misses : " << stats.find_tile_cache_misses << " (" << 100.0*(double)stats.find_tile_cache_misses/(double)stats.find_tile_calls << "%)\n"; out << " redundant reads: " << (unsigned long long) total_redundant_tiles << " tiles, " << Strutil::memformat (total_redundant_bytes) << "\n"; } out << " Peak cache memory : " << Strutil::memformat (m_mem_used) << "\n"; if (stats.tile_locking_time > 0.001) out << " Tile mutex locking time : " << Strutil::timeintervalformat (stats.tile_locking_time) << "\n"; if (stats.find_tile_time > 0.001) out << " Find tile time : " << Strutil::timeintervalformat (stats.find_tile_time) << "\n"; if (stats.file_retry_success || stats.tile_retry_success) out << " Failure reads followed by unexplained success: " << stats.file_retry_success << " files, " << stats.tile_retry_success << " tiles\n"; } if (level >= 2 && files.size()) { out << " Image file statistics:\n"; out << " opens tiles MB read --redundant-- I/O time res File\n"; std::sort (files.begin(), files.end(), filename_compare); for (size_t i = 0; i < files.size(); ++i) { const ImageCacheFileRef &file (files[i]); ASSERT (file); if (file->is_udim()) continue; if (file->broken() || file->subimages() == 0) { out << " BROKEN " << file->filename() << "\n"; continue; } out << onefile_stat_line (file, i+1) << "\n"; } out << Strutil::format ("\n Tot: %4llu %7llu %8.1f (%5llu %6.1f) %9s\n", (unsigned long long) total_opens, (unsigned long long) total_tiles, total_bytes/1024.0/1024.0, (unsigned long long) total_redundant_tiles, total_redundant_bytes/1024.0/1024.0, Strutil::timeintervalformat(total_iotime)); } // Try to point out hot spots if (level > 0) { if (total_duplicates) out << " " << total_duplicates << " were exact duplicates of other images\n"; if (total_untiled || (total_unmipped && automip())) { out << " " << total_untiled << " not tiled, " << total_unmipped << " not MIP-mapped\n"; #if 0 if (files.size() >= 50) { out << " Untiled/unmipped files were:\n"; for (size_t i = 0; i < files.size(); ++i) { const ImageCacheFileRef &file (files[i]); if (file->untiled() || (file->unmipped() && automip())) out << onefile_stat_line (file, -1) << "\n"; } } #endif } if (total_constant) out << " " << total_constant << (total_constant == 1 ? " was" : " were") << " constant-valued in all pixels\n"; if (files.size() >= 50) { const int topN = 3; int nprinted; std::sort (files.begin(), files.end(), bytesread_compare); out << " Top files by bytes read:\n"; nprinted = 0; BOOST_FOREACH (const ImageCacheFileRef &file, files) { if (nprinted++ >= topN) break; if (file->broken() || !file->validspec()) continue; out << Strutil::format (" %d %6.1f MB (%4.1f%%) ", nprinted, file->bytesread()/1024.0/1024.0, 100.0 * (file->bytesread() / (double)total_bytes)); out << onefile_stat_line (file, -1, false) << "\n"; } std::sort (files.begin(), files.end(), iotime_compare); out << " Top files by I/O time:\n"; nprinted = 0; BOOST_FOREACH (const ImageCacheFileRef &file, files) { if (nprinted++ >= topN) break; if (file->broken() || !file->validspec()) continue; out << Strutil::format (" %d %9s (%4.1f%%) ", nprinted, Strutil::timeintervalformat (file->iotime()).c_str(), 100.0 * file->iotime() / total_iotime); out << onefile_stat_line (file, -1, false) << "\n"; } std::sort (files.begin(), files.end(), iorate_compare); out << " Files with slowest I/O rates:\n"; nprinted = 0; BOOST_FOREACH (const ImageCacheFileRef &file, files) { if (file->broken() || !file->validspec()) continue; if (file->iotime() < 0.25) continue; if (nprinted++ >= topN) break; double mb = file->bytesread()/(1024.0*1024.0); double r = mb / file->iotime(); out << Strutil::format (" %d %6.2f MB/s (%.2fMB/%.2fs) ", nprinted, r, mb, file->iotime()); out << onefile_stat_line (file, -1, false) << "\n"; } if (nprinted == 0) out << " (nothing took more than 0.25s)\n"; double fast = files.back()->bytesread()/(1024.0*1024.0) / files.back()->iotime(); out << Strutil::format (" (fastest was %.1f MB/s)\n", fast); if (total_redundant_tiles > 0) { std::sort (files.begin(), files.end(), redundantbytes_compare); out << " Top files by redundant I/O:\n"; nprinted = 0; BOOST_FOREACH (const ImageCacheFileRef &file, files) { if (nprinted++ >= topN) break; if (file->broken() || !file->validspec()) continue; out << Strutil::format (" %d %6.1f MB (%4.1f%%) ", nprinted, file->redundant_bytesread()/1024.0/1024.0, 100.0 * (file->redundant_bytesread() / (double)total_redundant_bytes)); out << onefile_stat_line (file, -1, false) << "\n"; } } } int nbroken = 0; BOOST_FOREACH (const ImageCacheFileRef &file, files) { if (file->broken()) ++nbroken; } out << " Broken or invalid files: " << nbroken << "\n"; if (nbroken) { std::sort (files.begin(), files.end(), filename_compare); int nprinted = 0; BOOST_FOREACH (const ImageCacheFileRef &file, files) { if (file->broken()) { ++nprinted; out << Strutil::format (" %4d %s\n", nprinted, file->filename()); } } } } return out.str(); } void ImageCacheImpl::printstats () const { if (m_statslevel == 0) return; std::cout << getstats (m_statslevel) << "\n\n"; } void ImageCacheImpl::reset_stats () { { spin_lock lock (m_perthread_info_mutex); for (size_t i = 0; i < m_all_perthread_info.size(); ++i) m_all_perthread_info[i]->m_stats.init (); } { for (FilenameMap::iterator f = m_files.begin(); f != m_files.end(); ++f) { const ImageCacheFileRef &file (f->second); file->m_timesopened = 0; file->m_tilesread = 0; file->m_bytesread = 0; file->m_iotime = 0; } } } bool ImageCacheImpl::attribute (string_view name, TypeDesc type, const void *val) { bool do_invalidate = false; bool force_invalidate = false; if (name == "options" && type == TypeDesc::STRING) { return optparser (*this, *(const char **)val); } if (name == "max_open_files" && type == TypeDesc::INT) { set_max_open_files (*(const int *)val); } else if (name == "max_memory_MB" && type == TypeDesc::FLOAT) { float size = *(const float *)val; #ifdef NDEBUG size = std::max (size, 10.0f); // Don't let users choose < 10 MB #else size = std::max (size, 1.0f); // But let developers debugging do it #endif m_max_memory_bytes = (long long)size * (long long)(1024*1024); ASSERT (m_max_memory_bytes >= (1024*1024)); } else if (name == "max_memory_MB" && type == TypeDesc::INT) { float size = *(const int *)val; #ifdef NDEBUG size = std::max (size, 10.0f); // Don't let users choose < 10 MB #else size = std::max (size, 1.0f); // But let developers debugging do it #endif m_max_memory_bytes = (long long)size * (long long)(1024*1024); ASSERT (m_max_memory_bytes >= (1024*1024)); } else if (name == "searchpath" && type == TypeDesc::STRING) { std::string s = std::string (*(const char **)val); if (s != m_searchpath) { m_searchpath = s; Filesystem::searchpath_split (m_searchpath, m_searchdirs, true); do_invalidate = true; // in case file can be found with new path force_invalidate = true; } } else if (name == "plugin_searchpath" && type == TypeDesc::STRING) { m_plugin_searchpath = std::string (*(const char **)val); } else if (name == "statistics:level" && type == TypeDesc::INT) { m_statslevel = *(const int *)val; } else if (name == "max_errors_per_file" && type == TypeDesc::INT) { m_max_errors_per_file = *(const int *)val; } else if (name == "autotile" && type == TypeDesc::INT) { int a = pow2roundup (*(const int *)val); // guarantee pow2 // Clamp to minimum 8x8 tiles to protect against stupid user who // think this is a boolean rather than the tile size. Unless // we're in DEBUG mode, then allow developers to play with fire. #ifdef NDEBUG if (a > 0 && a < 8) a = 8; #endif if (a != m_autotile) { m_autotile = a; do_invalidate = true; } } else if (name == "autoscanline" && type == TypeDesc::INT) { bool a = (*(const int *)val != 0); if (a != m_autoscanline) { m_autoscanline = a; do_invalidate = true; } } else if (name == "automip" && type == TypeDesc::INT) { bool a = (*(const int *)val != 0); if (a != m_automip) { m_automip = a; do_invalidate = true; } } else if (name == "forcefloat" && type == TypeDesc::INT) { bool a = (*(const int *)val != 0); if (a != m_forcefloat) { m_forcefloat = a; do_invalidate = true; } } else if (name == "accept_untiled" && type == TypeDesc::INT) { bool a = (*(const int *)val != 0); if (a != m_accept_untiled) { m_accept_untiled = a; do_invalidate = true; } } else if (name == "accept_unmipped" && type == TypeDesc::INT) { bool a = (*(const int *)val != 0); if (a != m_accept_unmipped) { m_accept_unmipped = a; do_invalidate = true; } } else if (name == "read_before_insert" && type == TypeDesc::INT) { bool r = (*(const int *)val != 0); if (r != m_read_before_insert) { m_read_before_insert = r; do_invalidate = true; } } else if (name == "deduplicate" && type == TypeDesc::INT) { bool r = (*(const int *)val != 0); if (r != m_deduplicate) { m_deduplicate = r; do_invalidate = true; } } else if (name == "unassociatedalpha" && type == TypeDesc::INT) { bool r = (*(const int *)val != 0); if (r != m_unassociatedalpha) { m_unassociatedalpha = r; do_invalidate = true; } } else if (name == "failure_retries" && type == TypeDesc::INT) { m_failure_retries = *(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) { m_latlong_y_up_default = y_up; do_invalidate = true; } } else if (name == "substitute_image" && type == TypeDesc::STRING) { m_substitute_image = ustring (*(const char **)val); do_invalidate = true; } else { // Otherwise, unknown name return false; } if (do_invalidate) invalidate_all (force_invalidate); return true; } bool ImageCacheImpl::getattribute (string_view name, TypeDesc type, void *val) const { #define ATTR_DECODE(_name,_ctype,_src) \ if (name == _name && type == BaseTypeFromC<_ctype>::value) { \ *(_ctype *)(val) = (_ctype)(_src); \ return true; \ } ATTR_DECODE ("max_open_files", int, m_max_open_files); ATTR_DECODE ("max_memory_MB", float, m_max_memory_bytes/(1024.0*1024.0)); ATTR_DECODE ("max_memory_MB", int, m_max_memory_bytes/(1024*1024)); ATTR_DECODE ("statistics:level", int, m_statslevel); ATTR_DECODE ("max_errors_per_file", int, m_max_errors_per_file); ATTR_DECODE ("autotile", int, m_autotile); ATTR_DECODE ("autoscanline", int, m_autoscanline); ATTR_DECODE ("automip", int, m_automip); ATTR_DECODE ("forcefloat", int, m_forcefloat); ATTR_DECODE ("accept_untiled", int, m_accept_untiled); ATTR_DECODE ("accept_unmipped", int, m_accept_unmipped); ATTR_DECODE ("read_before_insert", int, m_read_before_insert); ATTR_DECODE ("deduplicate", int, m_deduplicate); ATTR_DECODE ("unassociatedalpha", int, m_unassociatedalpha); ATTR_DECODE ("failure_retries", int, m_failure_retries); ATTR_DECODE ("total_files", int, m_files.size()); // The cases that don't fit in the simple ATTR_DECODE scheme if (name == "searchpath" && type == TypeDesc::STRING) { *(ustring *)val = m_searchpath; return true; } if (name == "plugin_searchpath" && type == TypeDesc::STRING) { *(ustring *)val = m_plugin_searchpath; return true; } if (name == "worldtocommon" && (type == TypeDesc::TypeMatrix || type == TypeDesc(TypeDesc::FLOAT,16))) { *(Imath::M44f *)val = m_Mw2c; return true; } if (name == "commontoworld" && (type == TypeDesc::TypeMatrix || type == TypeDesc(TypeDesc::FLOAT,16))) { *(Imath::M44f *)val = m_Mc2w; return true; } if (name == "latlong_up" && type == TypeDesc::STRING) { *(const char **)val = ustring (m_latlong_y_up_default ? "y" : "z").c_str(); return true; } if (name == "substitute_image" && type == TypeDesc::STRING) { *(const char **)val = m_substitute_image.c_str(); return true; } if (name == "all_filenames" && type.basetype == TypeDesc::STRING && type.is_sized_array()) { ustring *names = (ustring *) val; int n = type.arraylen; for (FilenameMap::iterator f = m_files.begin(); f != m_files.end() && n-- > 0; ++f) { *names++ = f->second->filename(); } return true; } if (Strutil::starts_with(name, "stat:")) { // Stats we can just grab ATTR_DECODE ("stat:cache_memory_used", long long, m_mem_used); ATTR_DECODE ("stat:tiles_created", int, m_stat_tiles_created); ATTR_DECODE ("stat:tiles_current", int, m_stat_tiles_current); ATTR_DECODE ("stat:tiles_peak", int, m_stat_tiles_peak); ATTR_DECODE ("stat:open_files_created", int, m_stat_open_files_created); ATTR_DECODE ("stat:open_files_current", int, m_stat_open_files_current); ATTR_DECODE ("stat:open_files_peak", int, m_stat_open_files_peak); // All the other stats are those that need to be summed from all // the threads. ImageCacheStatistics stats; mergestats (stats); ATTR_DECODE ("stat:find_tile_calls", long long, stats.find_tile_calls); ATTR_DECODE ("stat:find_tile_microcache_misses", long long, stats.find_tile_microcache_misses); ATTR_DECODE ("stat:find_tile_cache_misses", int, stats.find_tile_cache_misses); ATTR_DECODE ("stat:files_totalsize", long long, stats.files_totalsize); // Old name ATTR_DECODE ("stat:image_size", long long, stats.files_totalsize); ATTR_DECODE ("stat:file_size", long long, stats.files_totalsize_ondisk); ATTR_DECODE ("stat:bytes_read", long long, stats.bytes_read); ATTR_DECODE ("stat:unique_files", int, stats.unique_files); ATTR_DECODE ("stat:fileio_time", float, stats.fileio_time); ATTR_DECODE ("stat:fileopen_time", float, stats.fileopen_time); ATTR_DECODE ("stat:file_locking_time", float, stats.file_locking_time); ATTR_DECODE ("stat:tile_locking_time", float, stats.tile_locking_time); ATTR_DECODE ("stat:find_file_time", float, stats.find_file_time); ATTR_DECODE ("stat:find_tile_time", float, stats.find_tile_time); } return false; #undef ATTR_DECODE } bool ImageCacheImpl::find_tile_main_cache (const TileID &id, ImageCacheTileRef &tile, ImageCachePerThreadInfo *thread_info) { DASSERT (! id.file().broken()); ImageCacheStatistics &stats (thread_info->m_stats); ++stats.find_tile_microcache_misses; { #if IMAGECACHE_TIME_STATS Timer timer1; #endif TileCache::iterator found = m_tilecache.find (id); #if IMAGECACHE_TIME_STATS stats.find_tile_time += timer1(); #endif if (found) { tile = (*found).second; found.unlock(); // release the lock // We found the tile in the cache, but we need to make sure we // wait until the pixels are ready to read. We purposely have // released the lock (above) before calling wait_pixels_ready, // otherwise we could deadlock if another thread reading the // pixels needs to lock the cache because it's doing automip. tile->wait_pixels_ready (); tile->use (); DASSERT (id == tile->id()); DASSERT (tile); return true; } } // The tile was not found in cache. ++stats.find_tile_cache_misses; // Yes, we're creating and reading a tile with no lock -- this is to // prevent all the other threads from blocking because of our // expensive disk read. We believe this is safe, since underneath // the ImageCacheFile will lock itself for the read_tile and there are // no other non-threadsafe side effects. Timer timer; tile = new ImageCacheTile (id, thread_info, m_read_before_insert); // N.B. the ImageCacheTile ctr starts the tile out as 'used' DASSERT (tile); DASSERT (id == tile->id()); double readtime = timer(); stats.fileio_time += readtime; id.file().iotime() += readtime; add_tile_to_cache (tile, thread_info); DASSERT (id == tile->id()); return tile->valid(); } void ImageCacheImpl::add_tile_to_cache (ImageCacheTileRef &tile, ImageCachePerThreadInfo *thread_info) { bool ourtile = true; { // Protect us from using too much memory if another thread added the // same tile just before us TileCache::iterator found = m_tilecache.find (tile->id()); if (found != m_tilecache.end ()) { // Already added! Use the other one, discard ours. tile = (*found).second; found.unlock (); ourtile = false; // Don't need to add it } else { // Still not in cache, add ours to the cache. // N.B. at this time, we do not hold any locks. check_max_mem (thread_info); m_tilecache.insert (tile->id(), tile); } } // At this point, we no longer have the write lock, and we are no // longer modifying the cache itself. However, if we added a new // tile to the cache, we may still need to read the pixels; and if // we found the tile in cache, we may need to wait for somebody else // to read the pixels. if (ourtile) { if (! tile->pixels_ready ()) { Timer timer; tile->read (thread_info); double readtime = timer(); thread_info->m_stats.fileio_time += readtime; tile->id().file().iotime() += readtime; } } else { tile->wait_pixels_ready (); } } void ImageCacheImpl::check_max_mem (ImageCachePerThreadInfo *thread_info) { DASSERT (m_mem_used < (long long)m_max_memory_bytes*10); // sanity #if 0 static atomic_int n; if (! (n++ % 64) || m_mem_used >= (long long)m_max_memory_bytes) std::cerr << "mem used: " << m_mem_used << ", max = " << m_max_memory_bytes << "\n"; #endif // Early out if the cache is empty if (m_tilecache.empty()) return; // Early out if we aren't exceeding the tile memory limit if (m_mem_used < (long long)m_max_memory_bytes) return; // Try to grab the tile_sweep_mutex lock. If somebody else holds it, // just return -- leave the memory limit enforcement to whomever is // already in this function, no need for two threads to do it at // once. If this means we may ephemerally be over the memory limit // (because another thread adds a tile before we have freed enough // here), so be it. if (! m_tile_sweep_mutex.try_lock()) return; // Now, what we want to do is have a "clock hand" that sweeps across // the cache, releasing tiles that haven't been used for a long // time. Because of multi-thread, rather than keep an iterator // around for this (which could be invalidated since the last time // we used it), we just remember the tileID of the next tile to // check, then look it up fresh. That is m_tile_sweep_id. // If we don't have a valid tile_sweep_id, establish it by just // looking up the first entry in the tile cache. if (m_tile_sweep_id.empty()) { TileCache::iterator sweep = m_tilecache.begin(); if (sweep == m_tilecache.end()) { m_tile_sweep_mutex.unlock(); return; } m_tile_sweep_id = (*sweep).first; } // Get a (locked) iterator for the next tile to be examined. TileCache::iterator sweep = m_tilecache.find (m_tile_sweep_id); // Loop while we still use too much tile memory. Also, be careful // of looping for too long, exit the loop if we just keep spinning // uncontrollably. int full_loops = 0; TileCache::iterator end = m_tilecache.end(); while (m_mem_used >= (long long)m_max_memory_bytes && full_loops < 100) { // If we have fallen off the end of the cache, loop back to the // beginning and increment our full_loops count. if (sweep == end) { sweep = m_tilecache.begin(); ++full_loops; } // If we're STILL at the end, it must be that somehow the entire // cache is empty. So just declare ourselves done. if (sweep == end) break; DASSERT (sweep->second); if (! sweep->second->release ()) { // This is a tile we should delete. To keep iterating // safely, we have a good trick: // 1. remember the TileID of the tile to delete TileID todelete = sweep->first; size_t size = sweep->second->memsize(); ASSERT (m_mem_used >= (long long)size); // 2. Increment the iterator to the next item to be visited // in the cache and then unlock it (since it can't be locked // for the subsequent erase() call). ++sweep; sweep.unlock (); // 3. Erase the tile we wish to delete m_tilecache.erase (todelete); // std::cerr << " Freed tile, recovering " << size << "\n"; // 4. Re-lock the iterator, which now points to the next // item the from the cache to examine. sweep.lock (); } else { ++sweep; } } // OK, by this point we have either freed enough tiles to be below // the limit again, or the cache is empty, or we've looped over the // cache too many times and are giving up. // Now we must save the tileid for next time. Just set it to an // empty ID if we don't have a valid iterator at this point. m_tile_sweep_id = (sweep == end ? TileID() : sweep->first); m_tile_sweep_mutex.unlock (); // N.B. As we exit, the iterators will go out of scope and we will // retain no locks on the cache. } std::string ImageCacheImpl::resolve_filename (const std::string &filename) const { // Ask if the format can generate imagery procedurally. If so, don't // go looking for a file. ImageInput *input = ImageInput::create (filename); bool procedural = input ? input->supports ("procedural") : false; ImageInput::destroy (input); if (procedural) return filename; std::string s = Filesystem::searchpath_find (filename, m_searchdirs, true); return s.empty() ? filename : s; } bool ImageCacheImpl::get_image_info (ustring filename, int subimage, int miplevel, ustring dataname, TypeDesc datatype, void *data) { ImageCachePerThreadInfo *thread_info = get_perthread_info (); ImageCacheFile *file = find_file (filename, thread_info, NULL, true); if (!file && dataname != s_exists) { error ("Invalid image file \"%s\"", filename); return false; } return get_image_info (file, thread_info, subimage, miplevel, dataname, datatype, data); } bool ImageCacheImpl::get_image_info (ImageCacheFile *file, ImageCachePerThreadInfo *thread_info, int subimage, int miplevel, ustring dataname, TypeDesc datatype, void *data) { #define ATTR_DECODE(_name,_ctype,_src) \ if (dataname == _name && datatype == BaseTypeFromC<_ctype>::value) { \ *(_ctype *)(data) = (_ctype)(_src); \ return true; \ } file = verify_file (file, thread_info, true); if (dataname == s_exists && datatype == TypeDesc::TypeInt) { // Just check for existence. Need to do this before the invalid // file error below, since in this one case, it's not an error // for the file to be nonexistant or broken! *(int *)data = (file && !file->broken()); (void) geterror(); // eat any error generated by find_file return true; } if (!file) { error ("Invalid image file"); return false; } ATTR_DECODE (s_broken, int, file->broken()); if (Strutil::starts_with (dataname, "stat:")) { ATTR_DECODE ("stat:tilesread", long long, file->m_tilesread); ATTR_DECODE ("stat:bytesread", long long, file->m_bytesread); ATTR_DECODE ("stat:redundant_tiles", long long, file->m_redundant_tiles); ATTR_DECODE ("stat:redundant_bytesread", long long, file->m_redundant_bytesread); ATTR_DECODE ("stat:timesopened", int, file->m_timesopened); ATTR_DECODE ("stat:iotime", float, file->m_iotime); ATTR_DECODE ("stat:mipused", int, file->m_mipused); ATTR_DECODE ("stat:is_duplicate", int, bool(file->duplicate())); ATTR_DECODE ("stat:image_size", long long, file->m_total_imagesize); ATTR_DECODE ("stat:file_size", long long, file->m_total_imagesize_ondisk); } if (file->broken()) { if (file->errors_should_issue()) error ("Invalid image file \"%s\"", file->filename()); return false; } // No other queries below are expected to work with broken if (dataname == s_UDIM && datatype == TypeDesc::TypeInt) { // Just check for existence. Need to do this before the invalid // file error below, since in this one case, it's not an error // for the file to be nonexistant or broken! *(int *)data = file->is_udim(); return true; } if (file->is_udim() && dataname == s_channels) { // Special case -- it's ok to ask for a UDIM's channels. It'll // search for a concrete file. Beware, this will cause trouble // if different panels of the same UDIM scheme have different // numbers of channels! Search the 10x100 files for a match, give // up if not found. for (int j = 0; j < 100; ++j) { for (int i = 0; i < 10; ++i) { float s = i + 0.5f, t = j + 0.5f; ImageCacheFile *concretefile = resolve_udim (file, s, t); concretefile = verify_file (concretefile, thread_info, true); if (concretefile && !concretefile->broken()) { // Recurse to try again with the concrete file return get_image_info (concretefile, thread_info, subimage, miplevel, dataname, datatype, data); } } } } if (file->is_udim()) { return false; // UDIM-like files fail all other queries } if (dataname == s_subimages && datatype == TypeDesc::TypeInt) { *(int *)data = file->subimages(); return true; } // Make sure we have a valid subimage and miplevel BEFORE we get a // reference to the spec. if (subimage < 0 || subimage >= file->subimages()) { if (file->errors_should_issue()) error ("Unknown subimage %d (out of %d)", subimage, file->subimages()); return false; } if (miplevel < 0 || miplevel >= file->miplevels(subimage)) { if (file->errors_should_issue()) error ("Unknown mip level %d (out of %d)", miplevel, file->miplevels(subimage)); return false; } const ImageSpec &spec (file->spec(subimage,miplevel)); if (dataname == s_resolution && datatype==TypeDesc(TypeDesc::INT,2)) { int *d = (int *)data; d[0] = spec.width; d[1] = spec.height; return true; } if (dataname == s_resolution && datatype==TypeDesc(TypeDesc::INT,3)) { int *d = (int *)data; d[0] = spec.width; d[1] = spec.height; d[2] = spec.depth; return true; } if (dataname == s_texturetype && datatype == TypeDesc::TypeString) { ustring s (texture_type_name (file->textureformat())); *(const char **)data = s.c_str(); return true; } if (dataname == s_textureformat && datatype == TypeDesc::TypeString) { ustring s (texture_format_name (file->textureformat())); *(const char **)data = s.c_str(); return true; } if (dataname == s_fileformat && datatype == TypeDesc::TypeString) { *(const char **)data = file->fileformat().c_str(); return true; } if (dataname == s_channels && datatype == TypeDesc::TypeInt) { *(int *)data = spec.nchannels; return true; } if (dataname == s_channels && datatype == TypeDesc::TypeFloat) { *(float *)data = spec.nchannels; return true; } if (dataname == s_format && datatype == TypeDesc::TypeInt) { *(int *)data = (int) spec.format.basetype; return true; } if ((dataname == s_cachedformat || dataname == s_cachedpixeltype) && datatype == TypeDesc::TypeInt) { *(int *)data = (int) file->datatype(subimage).basetype; return true; } if (dataname == s_miplevels && datatype == TypeDesc::TypeInt) { *(int *)data = file->miplevels(subimage); return true; } if (dataname == s_datawindow && datatype.basetype == TypeDesc::INT && (datatype == TypeDesc(TypeDesc::INT,4) || datatype == TypeDesc(TypeDesc::INT,6))) { int *d = (int *)data; if (datatype.arraylen == 4) { d[0] = spec.x; d[1] = spec.y; d[2] = spec.x + spec.width - 1; d[3] = spec.y + spec.height - 1; } else { d[0] = spec.x; d[1] = spec.y; d[2] = spec.z; d[3] = spec.x + spec.width - 1; d[4] = spec.y + spec.height - 1; d[5] = spec.z + spec.depth - 1; } return true; } if (dataname == s_displaywindow && datatype.basetype == TypeDesc::INT && (datatype == TypeDesc(TypeDesc::INT,4) || datatype == TypeDesc(TypeDesc::INT,6))) { int *d = (int *)data; if (datatype.arraylen == 4) { d[0] = spec.full_x; d[1] = spec.full_y; d[2] = spec.full_x + spec.full_width - 1; d[3] = spec.full_y + spec.full_height - 1; } else { d[0] = spec.full_x; d[1] = spec.full_y; d[2] = spec.full_z; d[3] = spec.full_x + spec.full_width - 1; d[4] = spec.full_y + spec.full_height - 1; d[5] = spec.full_z + spec.full_depth - 1; } return true; } if (dataname == s_averagecolor && datatype.basetype == TypeDesc::FLOAT) { int datalen = datatype.numelements() * datatype.aggregate; return file->get_average_color ((float *)data, subimage, 0, datalen); } if (dataname == s_averagealpha && datatype == TypeDesc::FLOAT && spec.alpha_channel >= 0) { return file->get_average_color ((float *)data, subimage, spec.alpha_channel, spec.alpha_channel+1); } if (dataname == s_constantcolor && datatype.basetype == TypeDesc::FLOAT) { if (file->subimageinfo(subimage).is_constant_image) { int datalen = datatype.numelements() * datatype.aggregate; return file->get_average_color ((float *)data, subimage, 0, datalen); } return false; // Fail if it's not a constant image } if (dataname == s_constantalpha && datatype == TypeDesc::FLOAT && spec.alpha_channel >= 0) { if (file->subimageinfo(subimage).is_constant_image) return file->get_average_color ((float *)data, subimage, spec.alpha_channel, spec.alpha_channel+1); else return false; // Fail if it's not a constant image } // general case -- handle anything else that's able to be found by // spec.find_attribute(). const ImageIOParameter *p = spec.find_attribute (dataname.string()); if (p && p->type().arraylen == datatype.arraylen) { // First test for exact type match if (p->type() == datatype) { memcpy (data, p->data(), datatype.size()); return true; } // If the real data is int but user asks for float, translate it if (p->type().basetype == TypeDesc::FLOAT && datatype.basetype == TypeDesc::INT) { for (int i = 0; i < p->type().arraylen; ++i) ((float *)data)[i] = ((int *)p->data())[i]; return true; } } return false; #undef ATTR_DECODE } bool ImageCacheImpl::get_imagespec (ustring filename, ImageSpec &spec, int subimage, int miplevel, bool native) { const ImageSpec *specptr = imagespec (filename, subimage, miplevel, native); if (specptr) { spec = *specptr; return true; } else { return false; // imagespec() already handled the errors } } bool ImageCacheImpl::get_imagespec (ImageCacheFile *file, ImageCachePerThreadInfo *thread_info, ImageSpec &spec, int subimage, int miplevel, bool native) { const ImageSpec *specptr = imagespec (file, thread_info, subimage, miplevel, native); if (specptr) { spec = *specptr; return true; } else { return false; // imagespec() already handled the errors } } const ImageSpec * ImageCacheImpl::imagespec (ustring filename, int subimage, int miplevel, bool native) { ImageCachePerThreadInfo *thread_info = get_perthread_info (); ImageCacheFile *file = find_file (filename, thread_info, NULL, true); if (! file) { error ("Image file \"%s\" not found", filename); return NULL; } return imagespec (file, thread_info, subimage, miplevel, native); } const ImageSpec * ImageCacheImpl::imagespec (ImageCacheFile *file, ImageCachePerThreadInfo *thread_info, int subimage, int miplevel, bool native) { if (! file) { error ("Image file handle was NULL"); return NULL; } if (! thread_info) thread_info = get_perthread_info (); file = verify_file (file, thread_info, true); if (file->broken()) { if (file->errors_should_issue()) error ("Invalid image file \"%s\"", file->filename()); return NULL; } if (file->is_udim()) { error ("Cannot retrieve ImageSpec of a UDIM-like virtual file"); return NULL; // UDIM-like files don't have an ImageSpec } if (subimage < 0 || subimage >= file->subimages()) { if (file->errors_should_issue()) error ("Unknown subimage %d (out of %d)", subimage, file->subimages()); return NULL; } if (miplevel < 0 || miplevel >= file->miplevels(subimage)) { if (file->errors_should_issue()) error ("Unknown mip level %d (out of %d)", miplevel, file->miplevels(subimage)); return NULL; } const ImageSpec *spec = native ? &file->nativespec (subimage,miplevel) : &file->spec (subimage, miplevel); return spec; } int ImageCacheImpl::subimage_from_name (ImageCacheFile *file, ustring subimagename) { for (int s = 0, send = file->subimages(); s < send; ++s) { if (file->subimageinfo(s).subimagename == subimagename) return s; } return -1; // No matching subimage name } bool ImageCacheImpl::get_pixels (ustring filename, int subimage, int miplevel, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, void *result) { return get_pixels (filename, subimage, miplevel, xbegin, xend, ybegin, yend, zbegin, zend, 0, -1, format, result); } bool ImageCacheImpl::get_pixels (ImageCacheFile *file, ImageCachePerThreadInfo *thread_info, int subimage, int miplevel, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, void *result) { return get_pixels (file, thread_info, subimage, miplevel, xbegin, xend, ybegin, yend, zbegin, zend, 0, -1, format, result); } bool ImageCacheImpl::get_pixels (ustring filename, int subimage, int miplevel, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc format, void *result, stride_t xstride, stride_t ystride, stride_t zstride, int cache_chbegin, int cache_chend) { ImageCachePerThreadInfo *thread_info = get_perthread_info (); ImageCacheFile *file = find_file (filename, thread_info); if (! file) { error ("Image file \"%s\" not found", filename); return false; } return get_pixels (file, thread_info, subimage, miplevel, xbegin, xend, ybegin, yend, zbegin, zend, chbegin, chend, format, result, xstride, ystride, zstride, cache_chbegin, cache_chend); } bool ImageCacheImpl::get_pixels (ImageCacheFile *file, ImageCachePerThreadInfo *thread_info, int subimage, int miplevel, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc format, void *result, stride_t xstride, stride_t ystride, stride_t zstride, int cache_chbegin, int cache_chend) { if (! thread_info) thread_info = get_perthread_info (); file = verify_file (file, thread_info); if (file->broken()) { if (file->errors_should_issue()) error ("Invalid image file \"%s\"", file->filename()); return false; } if (file->is_udim()) { error ("Cannot get_pixels() of a UDIM-like virtual file"); return false; // UDIM-like files don't have pixels } if (subimage < 0 || subimage >= file->subimages()) { if (file->errors_should_issue()) error ("get_pixels asked for nonexistant subimage %d of \"%s\"", subimage, file->filename()); return false; } if (miplevel < 0 || miplevel >= file->miplevels(subimage)) { if (file->errors_should_issue()) error ("get_pixels asked for nonexistant MIP level %d of \"%s\"", miplevel, file->filename()); return false; } if (! thread_info) thread_info = get_perthread_info (); const ImageSpec &spec (file->spec(subimage, miplevel)); bool ok = true; // Compute channels and stride if not given (assume all channels, // contiguous data layout for strides). if (chbegin < 0 || chend < 0) { chbegin = 0; chend = spec.nchannels; } int result_nchans = chend - chbegin; if (cache_chbegin < 0 || cache_chend < 0 || cache_chbegin > chbegin || cache_chend < chend) { cache_chbegin = 0; cache_chend = spec.nchannels; } int cache_nchans = cache_chend - cache_chbegin; ImageSpec::auto_stride (xstride, ystride, zstride, format, result_nchans, xend-xbegin, yend-ybegin); // result_pixelsize, scanlinesize, and zplanesize assume contiguous // layout. This may or may not be the same as the strides passed by // the caller. TypeDesc cachetype = file->datatype(subimage); const size_t cachesize = cachetype.size(); const stride_t cache_stride = cachesize * cache_nchans; size_t formatsize = format.size(); stride_t result_pixelsize = result_nchans * formatsize; bool xcontig = (result_pixelsize == xstride && result_nchans == cache_nchans); stride_t scanlinesize = (xend-xbegin) * result_pixelsize; stride_t zplanesize = (yend-ybegin) * scanlinesize; DASSERT (spec.depth >= 1 && spec.tile_depth >= 1); char *zptr = (char *)result; for (int z = zbegin; z < zend; ++z, zptr += zstride) { if (z < spec.z || z >= (spec.z+spec.depth)) { // nonexistant planes if (xstride == result_pixelsize && ystride == scanlinesize) { // Can zero out the plane in one shot memset (zptr, 0, zplanesize); } else { // Non-contiguous strides -- zero out individual pixels char *yptr = zptr; for (int y = ybegin; y < yend; ++y, yptr += ystride) { char *xptr = yptr; for (int x = xbegin; x < xend; ++x, xptr += xstride) memset (xptr, 0, result_pixelsize); } } continue; } int old_tx = -100000, old_ty = -100000, old_tz = -100000; int tz = z - ((z - spec.z) % spec.tile_depth); char *yptr = zptr; int ty = ybegin - ((ybegin - spec.y) % spec.tile_height); int tyend = ty + spec.tile_height; for (int y = ybegin; y < yend; ++y, yptr += ystride) { if (y == tyend) { ty = tyend; tyend += spec.tile_height; } if (y < spec.y || y >= (spec.y+spec.height)) { // nonexistant scanlines if (xstride == result_pixelsize) { // Can zero out the scanline in one shot memset (yptr, 0, scanlinesize); } else { // Non-contiguous strides -- zero out individual pixels char *xptr = yptr; for (int x = xbegin; x < xend; ++x, xptr += xstride) memset (xptr, 0, result_pixelsize); } continue; } // int ty = y - ((y - spec.y) % spec.tile_height); char *xptr = yptr; const char *data = NULL; for (int x = xbegin; x < xend; ++x, xptr += xstride) { if (x < spec.x || x >= (spec.x+spec.width)) { // nonexistant columns memset (xptr, 0, result_pixelsize); continue; } int tx = x - ((x - spec.x) % spec.tile_width); if (old_tx != tx || old_ty != ty || old_tz != tz) { // Only do a find_tile and re-setup of the data // pointer when we move across a tile boundary. TileID tileid (*file, subimage, miplevel, tx, ty, tz, cache_chbegin, cache_chend); ok &= find_tile (tileid, thread_info); if (! ok) return false; // Just stop if file read failed old_tx = tx; old_ty = ty; old_tz = tz; data = NULL; } if (! data) { ImageCacheTileRef &tile (thread_info->tile); ASSERT (tile); data = (const char *)tile->data (x, y, z, chbegin); ASSERT (data); } if (xcontig) { // Special case for a contiguous span within one tile int spanend = std::min (tx + spec.tile_width, xend); stride_t span = spanend - x; convert_types (cachetype, data, format, xptr, result_nchans*span); x += (span-1); xptr += xstride * (span-1); // no need to increment data, since next read will // be from a different tile } else { convert_types (cachetype, data, format, xptr, result_nchans); data += cache_stride; } } } } return ok; } ImageCache::Tile * ImageCacheImpl::get_tile (ustring filename, int subimage, int miplevel, int x, int y, int z, int chbegin, int chend) { ImageCachePerThreadInfo *thread_info = get_perthread_info (); ImageCacheFile *file = find_file (filename, thread_info); return get_tile (file, thread_info, subimage, miplevel, x, y, z, chbegin, chend); } ImageCache::Tile * ImageCacheImpl::get_tile (ImageHandle *file, Perthread *thread_info, int subimage, int miplevel, int x, int y, int z, int chbegin, int chend) { if (! thread_info) thread_info = get_perthread_info (); file = verify_file (file, thread_info); if (! file || file->broken() || file->is_udim()) return NULL; const ImageSpec &spec (file->spec(subimage,miplevel)); // Snap x,y,z to the corner of the tile int xtile = (x-spec.x) / spec.tile_width; int ytile = (y-spec.y) / spec.tile_height; int ztile = (z-spec.z) / spec.tile_depth; x = spec.x + xtile * spec.tile_width; y = spec.y + ytile * spec.tile_height; z = spec.z + ztile * spec.tile_depth; if (chend < chbegin) chend = spec.nchannels; TileID id (*file, subimage, miplevel, x, y, z, chbegin, chend); if (find_tile(id, thread_info)) { ImageCacheTileRef tile(thread_info->tile); tile->_incref(); // Fake an extra reference count return (ImageCache::Tile *) tile.get(); } else { return NULL; } } void ImageCacheImpl::release_tile (ImageCache::Tile *tile) const { if (! tile) return; ImageCacheTileRef tileref((ImageCacheTile *)tile); tileref->use (); tileref->_decref(); // Reduce ref count that we bumped in get_tile // when we exit scope, tileref will do the final dereference } TypeDesc ImageCacheImpl::tile_format (const Tile *tile) const { const TileID &id (((const ImageCacheTile *)tile)->id()); const ImageSpec &spec (id.file().spec (id.subimage(), id.miplevel())); return spec.format; } ROI ImageCacheImpl::tile_roi (const Tile *tile) const { const TileID &id (((const ImageCacheTile *)tile)->id()); const ImageSpec &spec (id.file().spec (id.subimage(), id.miplevel())); return ROI (id.x(), id.x()+spec.tile_width, id.y(), id.y()+spec.tile_height, id.z(), id.z()+spec.tile_depth, id.chbegin(), id.chend()); } const void * ImageCacheImpl::tile_pixels (ImageCache::Tile *tile, TypeDesc &format) const { if (! tile) return NULL; ImageCacheTile * t = (ImageCacheTile *)tile; format = t->file().datatype(t->id().subimage()); return t->data (); } bool ImageCacheImpl::add_file (ustring filename, ImageInput::Creator creator, const ImageSpec *config) { ImageCachePerThreadInfo *thread_info = get_perthread_info (); ImageCacheFile *file = find_file (filename, thread_info, creator, false, config); file = verify_file (file, thread_info); if (!file || file->broken() || file->is_udim()) return false; return true; } bool ImageCacheImpl::add_tile (ustring filename, int subimage, int miplevel, int x, int y, int z, int chbegin, int chend, TypeDesc format, const void *buffer, stride_t xstride, stride_t ystride, stride_t zstride) { ImageCachePerThreadInfo *thread_info = get_perthread_info (); ImageCacheFile *file = find_file (filename, thread_info); file = verify_file (file, thread_info); if (! file || file->broken()) { if (!file || file->errors_should_issue()) error ("Cannot add_tile for an image file that was not set up with add_file()"); return false; } if (file->is_udim()) { error ("Cannot add_tile to a UDIM-like virtual file"); return false; } if (chend < chbegin) chend = file->spec(subimage,miplevel).nchannels; TileID tileid (*file, subimage, miplevel, x, y, z, chbegin, chend); ImageCacheTileRef tile = new ImageCacheTile (tileid, buffer, format, xstride, ystride, zstride); if (! tile || ! tile->valid()) { if (file->errors_should_issue()) error ("Could not construct the tile; unknown reasons."); return false; } add_tile_to_cache (tile, thread_info); return true; } void ImageCacheImpl::invalidate (ustring filename) { ImageCacheFile *file = NULL; { FilenameMap::iterator fileit = m_files.find (filename); if (fileit != m_files.end()) file = fileit->second.get(); else return; // no such file } // Iterate over the entire tilecache, record the TileID's of all // tiles that are from the file we are invalidating. std::vector tiles_to_delete; for (TileCache::iterator tci = m_tilecache.begin(), e = m_tilecache.end(); tci != e; ++tci) { if (&(*tci).second->file() == file) tiles_to_delete.push_back ((*tci).second->id()); } // N.B. at this point, we hold no locks! // Safely erase all the tiles we found BOOST_FOREACH (const TileID &id, tiles_to_delete) { m_tilecache.erase (id); } // Invalidate the file itself (close it and clear its spec) file->invalidate (); // Remove the fingerprint corresponding to this file { spin_lock lock (m_fingerprints_mutex); FingerprintMap::iterator f = m_fingerprints.find (filename); if (f != m_fingerprints.end()) m_fingerprints.erase (f); } purge_perthread_microcaches (); } void ImageCacheImpl::invalidate_all (bool force) { // Special case: invalidate EVERYTHING -- we can take some shortcuts // to do it all in one shot. if (force) { // Clear the whole tile cache std::vector tiles_to_delete; for (TileCache::iterator t = m_tilecache.begin(), e = m_tilecache.end(); t != e; ++t) { tiles_to_delete.push_back (t->second->id()); } BOOST_FOREACH (const TileID &id, tiles_to_delete) { m_tilecache.erase (id); } // Invalidate (close and clear spec) all individual files for (FilenameMap::iterator fileit = m_files.begin(), e = m_files.end(); fileit != e; ++fileit) { fileit->second->invalidate (); } // Clear fingerprints list clear_fingerprints (); // Mark the per-thread microcaches as invalid purge_perthread_microcaches (); return; } // Not forced... we need to look for particular files that seem // to need invalidation. // Make a list of all files that need to be invalidated std::vector all_files; for (FilenameMap::iterator fileit = m_files.begin(), e = m_files.end(); fileit != e; ++fileit) { ImageCacheFileRef &f (fileit->second); ustring name = f->filename(); recursive_lock_guard guard (f->m_input_mutex); // If the file was broken when we opened it, or if it no longer // exists, definitely invalidate it. if (f->broken() || ! Filesystem::exists(name.string())) { all_files.push_back (name); continue; } // Invalidate the file if it has been modified since it was // last opened. std::time_t t = Filesystem::last_write_time (name.string()); if (t != f->mod_time()) { all_files.push_back (name); continue; } for (int s = 0; s < f->subimages(); ++s) { const ImageCacheFile::SubimageInfo &sub (f->subimageinfo(s)); // Invalidate if any unmipped subimage didn't automip but // automip is now on, or did automip but automip is now off. if (sub.unmipped && ((m_automip && f->miplevels(s) <= 1) || (!m_automip && f->miplevels(s) > 1))) { all_files.push_back (name); break; } // Invalidate if any untiled subimage doesn't match the current // auto-tile setting. if (sub.untiled) { for (int m = 0, mend = f->miplevels(s); m < mend; ++m) { const ImageCacheFile::LevelInfo &level (f->levelinfo(s,m)); if (level.spec.tile_width != m_autotile || level.spec.tile_height != m_autotile) { all_files.push_back (name); break; } } } } } // Now, invalidate all the files in our "needs invalidation" list BOOST_FOREACH (ustring f, all_files) { // fprintf (stderr, "Invalidating %s\n", f.c_str()); invalidate (f); } // Mark the per-thread microcaches as invalid purge_perthread_microcaches (); } namespace { // Mutex to protect all the UDIM table access. static mutex_pool udim_lookup_mutex_pool; // static spin_rw_mutex udim_lookup_mutex; } ImageCacheFile * ImageCacheImpl::resolve_udim (ImageCacheFile *udimfile, float &s, float &t) { // Find the u and v tile IDs, and adjust s,t to take their floors int utile = std::max (0, int(s)); int vtile = std::max (0, int(t)); s = s - utile; t = t - vtile; // Synthesized a single combined ID that we'll use as an index. uint64_t id = (uint64_t(vtile) << 32) + uint64_t(utile); // Which is our mutex from the pool? Use a hash baseed on the filename. spin_rw_mutex &udim_lookup_mutex (udim_lookup_mutex_pool[udimfile->filename()]); // First, try a read lock and see if we already have an entry ImageCacheFile *realfile = NULL; { spin_rw_mutex::read_lock_guard rlock (udim_lookup_mutex); UdimLookupMap::iterator f = udimfile->m_udim_lookup.find (id); if (f != udimfile->m_udim_lookup.end()) realfile = f->second; } // If that didn't work, get a write lock and we'll make the entry for // the first time. if (! realfile) { // Here's the one spot where we do string manipulation -- only the // first time a particular tiled region is needed. Just go ahead and // do all possible substitutions we support! ustring realname = udimfile->filename(); int udim_tile = 1001 + utile + 10*vtile; realname = Strutil::replace (realname, "", Strutil::format("%04d", udim_tile), true); realname = Strutil::replace (realname, "", Strutil::format("u%d", utile), true); realname = Strutil::replace (realname, "", Strutil::format("v%d", vtile), true); realname = Strutil::replace (realname, "", Strutil::format("u%d", utile+1), true); realname = Strutil::replace (realname, "", Strutil::format("v%d", vtile+1), true); realfile = find_file (realname, get_perthread_info()); // Now grab the actual write lock, and double check that it hasn't // been added by another thread during the brief time when we // weren't holding any lock. spin_rw_mutex::write_lock_guard rlock (udim_lookup_mutex); UdimLookupMap::iterator f = udimfile->m_udim_lookup.find (id); if (f == udimfile->m_udim_lookup.end()) { // Not yet in the lookup table, so create one so we don't have // to do that lookup again. udimfile->m_udim_lookup[id] = realfile; // std::cout << "Associate " << id << " with " << (void*)realfile << "\n"; } } return realfile; } ImageCachePerThreadInfo * ImageCacheImpl::create_thread_info () { ImageCachePerThreadInfo *p = new ImageCachePerThreadInfo; // printf ("New perthread %p\n", (void *)p); spin_lock lock (m_perthread_info_mutex); m_all_perthread_info.push_back (p); p->shared = true; // both the IC and the caller point to it return p; } void ImageCacheImpl::destroy_thread_info (ImageCachePerThreadInfo *thread_info) { if (! thread_info) return; spin_lock lock (m_perthread_info_mutex); for (size_t i = 0; i < m_all_perthread_info.size(); ++i) { if (m_all_perthread_info[i] == thread_info) { m_all_perthread_info[i] = NULL; break; } } delete thread_info; } ImageCachePerThreadInfo * ImageCacheImpl::get_perthread_info (ImageCachePerThreadInfo *p) { if (!p) p = m_perthread_info.get(); if (! p) { p = new ImageCachePerThreadInfo; m_perthread_info.reset (p); // printf ("New perthread %p\n", (void *)p); spin_lock lock (m_perthread_info_mutex); m_all_perthread_info.push_back (p); p->shared = true; // both the IC and the thread point to it } if (p->purge) { // has somebody requested a tile purge? // This is safe, because it's our thread. spin_lock lock (m_perthread_info_mutex); p->tile = NULL; p->lasttile = NULL; p->purge = 0; for (int i = 0; i < ImageCachePerThreadInfo::nlastfile; ++i) { p->last_filename[i] = ustring(); p->last_file[i] = NULL; } } return p; } void ImageCacheImpl::erase_perthread_info () { spin_lock lock (m_perthread_info_mutex); for (size_t i = 0; i < m_all_perthread_info.size(); ++i) { ImageCachePerThreadInfo *p = m_all_perthread_info[i]; if (p) { // Clear the microcache. p->tile = NULL; p->lasttile = NULL; if (p->shared) { // Pointed to by both thread-specific-ptr and our list. // Just remove from out list, then ownership is only // by the thread-specific-ptr. p->shared = false; } else { // Only pointed to by us -- delete it! delete p; } m_all_perthread_info[i] = NULL; } } } void ImageCacheImpl::cleanup_perthread_info (ImageCachePerThreadInfo *p) { spin_lock lock (m_perthread_info_mutex); if (p) { // Clear the microcache. p->tile = NULL; p->lasttile = NULL; if (! p->shared) // If we own it, delete it delete p; else p->shared = false; // thread disappearing, no longer shared } } void ImageCacheImpl::purge_perthread_microcaches () { // Mark the per-thread microcaches as invalid spin_lock lock (m_perthread_info_mutex); for (size_t i = 0, e = m_all_perthread_info.size(); i < e; ++i) if (m_all_perthread_info[i]) m_all_perthread_info[i]->purge = 1; } std::string ImageCacheImpl::geterror () const { std::string e; std::string *errptr = m_errormessage.get (); if (errptr) { e = *errptr; errptr->clear (); } return e; } void ImageCacheImpl::append_error (const std::string& message) const { std::string *errptr = m_errormessage.get (); if (! errptr) { errptr = new std::string; m_errormessage.reset (errptr); } ASSERT (errptr != NULL); ASSERT (errptr->size() < 1024*1024*16 && "Accumulated error messages > 16MB. Try checking return codes!"); if (errptr->size()) *errptr += '\n'; *errptr += message; } } // end namespace pvt ImageCache * ImageCache::create (bool shared) { if (shared) { // They requested a shared cache. If a shared cache already // exists, just return it, otherwise record the new cache. spin_lock guard (shared_image_cache_mutex); if (! shared_image_cache.get()) shared_image_cache.reset (new ImageCacheImpl); #if 0 std::cerr << " shared ImageCache is " << (void *)shared_image_cache.get() << "\n"; #endif return shared_image_cache.get (); } // Doesn't need a shared cache ImageCacheImpl *ic = new ImageCacheImpl; #if 0 std::cerr << "creating new ImageCache " << (void *)ic << "\n"; #endif return ic; } void ImageCache::destroy (ImageCache *x, bool teardown) { if (! x) return; spin_lock guard (shared_image_cache_mutex); if (x == shared_image_cache.get()) { // This is the shared cache, so don't really delete it. Invalidate // it fully, closing the files and throwing out any tiles that // nobody is currently holding references to. But only delete the // IC fully if 'teardown' is true, and even then, it won't destroy // until nobody else is still holding a shared_ptr to it. ((ImageCacheImpl *)x)->invalidate_all (teardown); if (teardown) shared_image_cache.reset (); } else { // Not a shared cache, we are the only owner, so truly destroy it. delete (ImageCacheImpl *) x; } } void ImageCache::destroy (ImageCache *x) { destroy (x, false); } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libtexture/environment.cpp0000644000175000017500000005350313151711064022361 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/varyingref.h" #include "OpenImageIO/ustring.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/filter.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/texture.h" #include "OpenImageIO/imagecache.h" #include "OpenImageIO/imagecache.h" #include "imagecache_pvt.h" #include "texture_pvt.h" /* Discussion about environment map issues and conventions: Latlong maps (spherical parameterization) come in two varieties that OIIO supports: (a) Our default follows the RenderMan convention of "z is up" and left-handed, with the north pole (t=0) pointing toward +z and the "center" (0.5,0.5) pointing toward +y: --s--> (0,0,1) (0,0) +---------------------------------------+ (1,0) | | | | | t |(-1,0,0) (1,0,0) | | + + + + | V | (0,-1,0) (0,1,0) | | | | | (0,1) +---------------------------------------+ (1,1) (0,0,-1) (b) If the metadata "oiio:updirection" is "y", the map is assumed to use the OpenEXR convention where the +y axis is "up", and the coordinate system is right-handed, and the center pixel points toward the +x axis: --s--> (0,1,0) (0,0) +---------------------------------------+ (1,0) | | | | | t |(0,0,-1) (0,0,1) | | + + + + | V | (1,0,0) (0,-1,0) | | | | | (0,1) +---------------------------------------+ (1,1) (0,-1,0) By default, we assume the conversion between pixel coordinates and texture coordinates is the same as for 2D textures; that is, pixel (i,j) is located at s,t = ( (i+0.5)/xres, (j+0.5)/yres ). We believe that this is the usual interpretation of latlong maps. However, if the metadata "oiio:sampleborder" is present and nonzero, we assume instead that pixel (i,j) has st coordinates ( i/(xres-1), j/(yres-1) ), in other words, that the edge texels correspond exactly to the pole or median seam, and therefore that pixel (0,j) and (xres-1,j) should be identical for all j, pixel (i,0) should be identical for all i, and pixel (i,yres-1) should be identical for all i. This latter convention is dictated by OpenEXR. Cubeface environment maps are composed of six orthogonal faces (that we name px, nx, py, ny, pz, nz). major +s dir +t dir Face axis (right) (down) ---- ----- ------- ------ px +x -z -y nx -x +z -y py +y +x +z ny -y +x -z pz +z +x -y nz -z -x -y The cubeface layout is easily visualized by "unwrapping": +-------------+ |py | | | | +y->+x | | | | | V | | +z | +-------------|-------------|-------------+-------------+ |nx |pz |px |nz | | | | | | | -x->+z | +z->+x | +x->-z | -z->-x | | | | | | | | | | | V | V | V | V | | -y | -y | -y | -y | +-------------+-------------+-------------+-------------+ |ny | | | | -y->+x | | | | | V | | -z | +-------------+ But that's just for visualization. The way it's actually stored in a file varies by convention of the file format. Here are the two conventions that we support natively: (a) "2x3" (a.k.a. the RenderMan/BMRT convention) wherein all six faces are arranged within a single image: +-------------+-------------+-------------+ |px |py |pz | | | | | | +x->-z | +y->+x | +z->+x | | | | | | | | | V | V | V | | -y | +z | -y | |-------------|-------------|-------------| |nx |ny |nz | | | | | | -x->+z | -y->+x | -z->-x | | | | | | | | | V | V | V | | -y | -z | -y | +-------------+-------------+-------------+ The space for each "face" is an integer multiple of the tile size, padded by black pixels if necessary (i.e. if the face res is not a full multiple of the tile size). For example, +--------+--------+--------+ |px| |py| |pz| | |--+ |--+ |--+ | | (black)| | | |--------+--------+--------| |nx| |ny| |nz| | |--+ |--+ |--+ | | | | | +--------+--------+--------+ This might happen on low-res MIP levels if the tile size is 64x64 but each face is only 8x8, say. The way we signal these things is for the ImageSpec width,height to be the true data window size (3 x res, 2 x res), but for full_width,full_height to be the size of the valid area of each face. (b) "6x1" (the OpenEXR convention) wherein the six faces are arranged in a vertical stack like this: +--+ |px| +--+ |nx| +--+ |py| +--+ |ny| +--+ |pz| +--+ |nz| +--+ Which of these conventions is being followed in a particular cubeface environment map file should be obvious merely by looking at the aspect ratio -- 3:2 or 1:6. As with latlong maps, by default we assume the conversion between pixel coordinates and texture coordinates within a face is the same as for 2D textures; that is, pixel (i,j) is located at s,t = ( (i+0.5)/faceres, (j+0.5)/faceres ). However, if the metadata "oiio:sampleborder" is present and nonzero, we assume instead that pixel (i,j) has st coordinates ( i/(faceres-1), j/(faceres-1) ), in other words, that the edge texels correspond exactly to the cube edge itself, and therefore that each cube face's edge texels are identical to the bordering face, and that any corner pixel values are identical for all three faces that share the corner. This convention is dictated by OpenEXR. */ OIIO_NAMESPACE_BEGIN using namespace pvt; using namespace simd; namespace { // anonymous static EightBitConverter uchar2float; } // end anonymous namespace namespace pvt { // namespace pvt bool TextureSystemImpl::environment (ustring filename, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef R, VaryingRef dRdx, VaryingRef dRdy, int nchannels, float *result, float *dresultds, float *dresultdt) { Perthread *thread_info = get_perthread_info(); TextureHandle *texture_handle = get_texture_handle (filename, thread_info); return environment (texture_handle, thread_info, options, runflags, beginactive, endactive, R, dRdx, dRdy, nchannels, result, dresultds, dresultdt); } bool TextureSystemImpl::environment (TextureHandle *texture_handle, Perthread *thread_info, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef R, VaryingRef dRdx, VaryingRef dRdy, int nchannels, float *result, float *dresultds, float *dresultdt) { if (! texture_handle) return false; bool ok = true; result += beginactive*nchannels; if (dresultds) { dresultds += beginactive*nchannels; dresultdt += beginactive*nchannels; } for (int i = beginactive; i < endactive; ++i) { if (runflags[i]) { TextureOpt opt (options, i); ok &= environment (texture_handle, thread_info, opt, R[i], dRdx[i], dRdy[i], nchannels, result, dresultds, dresultdt); } result += nchannels; if (dresultds) { dresultds += nchannels; dresultdt += nchannels; } } return ok; } /// Convert a direction vector to latlong st coordinates /// inline void vector_to_latlong (const Imath::V3f& R, bool y_is_up, float &s, float &t) { if (y_is_up) { s = atan2f (-R[0], R[2]) / (2.0f*(float)M_PI) + 0.5f; t = 0.5f - atan2f(R[1], hypotf(R[2],-R[0])) / (float)M_PI; } else { s = atan2f (R[1], R[0]) / (2.0f*(float)M_PI) + 0.5f; t = 0.5f - atan2f(R[2], hypotf(R[0],R[1])) / (float)M_PI; } // learned from experience, beware NaNs if (isnan(s)) s = 0.0f; if (isnan(t)) t = 0.0f; } bool TextureSystemImpl::environment (ustring filename, TextureOpt &options, const Imath::V3f &R, const Imath::V3f &dRdx, const Imath::V3f &dRdy, int nchannels, float *result, float *dresultds, float *dresultdt) { PerThreadInfo *thread_info = m_imagecache->get_perthread_info (); TextureFile *texturefile = find_texturefile (filename, thread_info); return environment ((TextureHandle *)texturefile, (Perthread *)thread_info, options, R, dRdx, dRdy, nchannels, result, dresultds, dresultdt); } bool TextureSystemImpl::environment (TextureHandle *texture_handle_, Perthread *thread_info_, TextureOpt &options, const Imath::V3f &_R, const Imath::V3f &_dRdx, const Imath::V3f &_dRdy, int nchannels, float *result, float *dresultds, float *dresultdt) { // Handle >4 channel lookups by recursion. if (nchannels > 4) { int save_firstchannel = options.firstchannel; while (nchannels) { int n = std::min (nchannels, 4); bool ok = environment (texture_handle_, thread_info_, options, _R, _dRdx, _dRdy, n, result, dresultds, dresultdt); if (! ok) return false; result += n; if (dresultds) dresultds += n; if (dresultdt) dresultdt += n; options.firstchannel += n; nchannels -= n; } options.firstchannel = save_firstchannel; // restore what we changed return true; } PerThreadInfo *thread_info = m_imagecache->get_perthread_info((PerThreadInfo *)thread_info_); TextureFile *texturefile = verify_texturefile ((TextureFile *)texture_handle_, thread_info); ImageCacheStatistics &stats (thread_info->m_stats); ++stats.environment_batches; ++stats.environment_queries; if (! texturefile || texturefile->broken()) return missing_texture (options, nchannels, result, dresultds, dresultdt); const ImageSpec &spec (texturefile->spec(options.subimage, 0)); // Environment maps dictate particular wrap modes options.swrap = texturefile->m_sample_border ? TextureOpt::WrapPeriodicSharedBorder : TextureOpt::WrapPeriodic; options.twrap = TextureOpt::WrapClamp; options.envlayout = LayoutLatLong; int actualchannels = Imath::clamp (spec.nchannels - options.firstchannel, 0, nchannels); // Initialize results to 0. We'll add from here on as we sample. for (int c = 0; c < nchannels; ++c) result[c] = 0; if (dresultds) { for (int c = 0; c < nchannels; ++c) dresultds[c] = 0; for (int c = 0; c < nchannels; ++c) dresultdt[c] = 0; } // If the user only provided us with one pointer, clear both to simplify // the rest of the code, but only after we zero out the data for them so // they know something went wrong. if (!(dresultds && dresultdt)) dresultds = dresultdt = NULL; // Calculate unit-length vectors in the direction of R, R+dRdx, R+dRdy. // These define the ellipse we're filtering over. Imath::V3f R = _R; R.normalize(); // center Imath::V3f Rx = _R + _dRdx; Rx.normalize(); // x axis of the ellipse Imath::V3f Ry = _R + _dRdy; Ry.normalize(); // y axis of the ellipse // angles formed by the ellipse axes. float xfilt_noblur = std::max (safe_acos(R.dot(Rx)), 1e-8f); float yfilt_noblur = std::max (safe_acos(R.dot(Ry)), 1e-8f); int naturalres = int((float)M_PI / std::min (xfilt_noblur, yfilt_noblur)); // FIXME -- figure naturalres sepearately for s and t // FIXME -- ick, why is it x and y at all, shouldn't it be s and t? // N.B. naturalres formulated for latlong // Account for width and blur float xfilt = xfilt_noblur * options.swidth + options.sblur; float yfilt = yfilt_noblur * options.twidth + options.tblur; // Figure out major versus minor, and aspect ratio Imath::V3f Rmajor; // major axis float majorlength, minorlength; bool x_is_majoraxis = (xfilt >= yfilt); if (x_is_majoraxis) { Rmajor = Rx; majorlength = xfilt; minorlength = yfilt; } else { Rmajor = Ry; majorlength = yfilt; minorlength = xfilt; } sampler_prototype sampler; long long *probecount; switch (options.interpmode) { case TextureOpt::InterpClosest : sampler = &TextureSystemImpl::sample_closest; probecount = &stats.closest_interps; break; case TextureOpt::InterpBilinear : sampler = &TextureSystemImpl::sample_bilinear; probecount = &stats.bilinear_interps; break; case TextureOpt::InterpBicubic : sampler = &TextureSystemImpl::sample_bicubic; probecount = &stats.cubic_interps; break; default: sampler = NULL; probecount = NULL; break; } TextureOpt::MipMode mipmode = options.mipmode; bool aniso = (mipmode == TextureOpt::MipModeDefault || mipmode == TextureOpt::MipModeAniso); float aspect, trueaspect, filtwidth; int nsamples; float invsamples; if (aniso) { aspect = anisotropic_aspect (majorlength, minorlength, options, trueaspect); filtwidth = minorlength; if (trueaspect > stats.max_aniso) stats.max_aniso = trueaspect; nsamples = std::max (1, (int) ceilf (aspect - 0.25f)); invsamples = 1.0f / nsamples; } else { filtwidth = options.conservative_filter ? majorlength : minorlength; nsamples = 1; invsamples = 1.0f; } ImageCacheFile::SubimageInfo &subinfo (texturefile->subimageinfo(options.subimage)); // FIXME -- assuming latlong bool ok = true; float pos = -0.5f + 0.5f * invsamples; for (int sample = 0; sample < nsamples; ++sample, pos += invsamples) { Imath::V3f Rsamp = R + pos*Rmajor; float s, t; vector_to_latlong (Rsamp, texturefile->m_y_up, s, t); // Determine the MIP-map level(s) we need: we will blend // data(miplevel[0]) * (1-levelblend) + data(miplevel[1]) * levelblend int miplevel[2] = { -1, -1 }; float levelblend = 0; int nmiplevels = (int)subinfo.levels.size(); for (int m = 0; m < nmiplevels; ++m) { // Compute the filter size in raster space at this MIP level. // Filters are in radians, and the vertical resolution of a // latlong map is PI radians. So to compute the raster size of // our filter width... float filtwidth_ras = subinfo.spec(m).full_height * filtwidth * M_1_PI; // Once the filter width is smaller than one texel at this level, // we've gone too far, so we know that we want to interpolate the // previous level and the current level. Note that filtwidth_ras // is expected to be >= 0.5, or would have stopped one level ago. if (filtwidth_ras <= 1) { miplevel[0] = m-1; miplevel[1] = m; levelblend = Imath::clamp (2.0f*filtwidth_ras - 1.0f, 0.0f, 1.0f); break; } } if (miplevel[1] < 0) { // We'd like to blur even more, but make due with the coarsest // MIP level. miplevel[0] = nmiplevels - 1; miplevel[1] = miplevel[0]; levelblend = 0; } else if (miplevel[0] < 0) { // We wish we had even more resolution than the finest MIP level, // but tough for us. miplevel[0] = 0; miplevel[1] = 0; levelblend = 0; } if (options.mipmode == TextureOpt::MipModeOneLevel) { // Force use of just one mipmap level miplevel[1] = miplevel[0]; levelblend = 0; } else if (mipmode == TextureOpt::MipModeNoMIP) { // Just sample from lowest level miplevel[0] = 0; miplevel[1] = 0; levelblend = 0; } float levelweight[2] = { 1.0f - levelblend, levelblend }; int npointson = 0; for (int level = 0; level < 2; ++level) { if (! levelweight[level]) continue; ++npointson; int lev = miplevel[level]; if (options.interpmode == TextureOpt::InterpSmartBicubic) { if (lev == 0 || (texturefile->spec(options.subimage,lev).full_height < naturalres/2)) { sampler = &TextureSystemImpl::sample_bicubic; ++stats.cubic_interps; } else { sampler = &TextureSystemImpl::sample_bilinear; ++stats.bilinear_interps; } } else { *probecount += 1; } OIIO_SIMD4_ALIGN float sval[4] = { s, 0.0f, 0.0f, 0.0f }; OIIO_SIMD4_ALIGN float tval[4] = { t, 0.0f, 0.0f, 0.0f }; OIIO_SIMD4_ALIGN float weight[4] = { levelweight[level]*invsamples, 0.0f, 0.0f, 0.0f }; float4 r, drds, drdt; ok &= (this->*sampler) (1, sval, tval, miplevel[level], *texturefile, thread_info, options, nchannels, actualchannels, weight, &r, dresultds ? &drds : NULL, dresultds ? &drdt : NULL); for (int c = 0; c < nchannels; ++c) result[c] += r[c]; if (dresultds) { for (int c = 0; c < nchannels; ++c) { dresultds[c] += drds[c]; dresultdt[c] += drdt[c]; } } } } stats.aniso_probes += nsamples; ++stats.aniso_queries; if (actualchannels < nchannels && options.firstchannel == 0 && m_gray_to_rgb) fill_gray_channels (spec, nchannels, result, dresultds, dresultdt); return ok; } } // end namespace pvt OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libtexture/texture3d.cpp0000644000175000017500000010710513151711064021742 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/varyingref.h" #include "OpenImageIO/ustring.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/filter.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/texture.h" #include "OpenImageIO/imagecache.h" #include "imagecache_pvt.h" #include "texture_pvt.h" #include "../field3d.imageio/field3d_backdoor.h" OIIO_NAMESPACE_BEGIN using namespace pvt; using namespace f3dpvt; namespace { // anonymous static EightBitConverter uchar2float; static ustring s_field3d ("field3d"); // OIIO_FORCEINLINE float uchar2float (unsigned char val) { // return float(val) * (1.0f/255.0f); // } OIIO_FORCEINLINE float ushort2float (unsigned short val) { return float(val) * (1.0f/65535.0f); } OIIO_FORCEINLINE float half2float (half val) { return float(val); } } // end anonymous namespace namespace pvt { // namespace pvt bool TextureSystemImpl::texture3d (ustring filename, TextureOpt &options, const Imath::V3f &P, const Imath::V3f &dPdx, const Imath::V3f &dPdy, const Imath::V3f &dPdz, int nchannels, float *result, float *dresultds, float *dresultdt, float *dresultdr) { PerThreadInfo *thread_info = m_imagecache->get_perthread_info (); TextureFile *texturefile = find_texturefile (filename, thread_info); return texture3d ((TextureHandle *)texturefile, (Perthread *)thread_info, options, P, dPdx, dPdy, dPdz, nchannels, result, dresultds, dresultdt); } bool TextureSystemImpl::texture3d (TextureHandle *texture_handle_, Perthread *thread_info_, TextureOpt &options, const Imath::V3f &P, const Imath::V3f &dPdx, const Imath::V3f &dPdy, const Imath::V3f &dPdz, int nchannels, float *result, float *dresultds, float *dresultdt, float *dresultdr) { // Handle >4 channel lookups by recursion. if (nchannels > 4) { int save_firstchannel = options.firstchannel; while (nchannels) { int n = std::min (nchannels, 4); bool ok = texture3d (texture_handle_, thread_info_, options, P, dPdx, dPdy, dPdz, n, result, dresultds, dresultdt, dresultdr); if (! ok) return false; result += n; if (dresultds) dresultds += n; if (dresultdt) dresultdt += n; if (dresultdr) dresultdr += n; options.firstchannel += n; nchannels -= n; } options.firstchannel = save_firstchannel; // restore what we changed return true; } #if 0 // FIXME: currently, no support of actual MIPmapping. No rush, // since the only volume format we currently support, Field3D, // doens't support MIPmapping. static const texture3d_lookup_prototype lookup_functions[] = { // Must be in the same order as Mipmode enum &TextureSystemImpl::texture3d_lookup, &TextureSystemImpl::texture3d_lookup_nomip, &TextureSystemImpl::texture3d_lookup_trilinear_mipmap, &TextureSystemImpl::texture3d_lookup_trilinear_mipmap, &TextureSystemImpl::texture3d_lookup }; texture3d_lookup_prototype lookup = lookup_functions[(int)options.mipmode]; #else texture3d_lookup_prototype lookup = &TextureSystemImpl::texture3d_lookup_nomip; #endif PerThreadInfo *thread_info = m_imagecache->get_perthread_info((PerThreadInfo *)thread_info_); TextureFile *texturefile = verify_texturefile ((TextureFile *)texture_handle_, thread_info); ImageCacheStatistics &stats (thread_info->m_stats); ++stats.texture3d_batches; ++stats.texture3d_queries; if (! texturefile || texturefile->broken()) return missing_texture (options, nchannels, result, dresultds, dresultdt, dresultdr); if (options.subimagename) { // If subimage was specified by name, figure out its index. int s = m_imagecache->subimage_from_name (texturefile, options.subimagename); if (s < 0) { error ("Unknown subimage \"%s\" in texture \"%s\"", options.subimagename.c_str(), texturefile->filename().c_str()); return false; } options.subimage = s; options.subimagename.clear(); } const ImageSpec &spec (texturefile->spec(options.subimage, 0)); // Figure out the wrap functions if (options.swrap == TextureOpt::WrapDefault) options.swrap = (TextureOpt::Wrap)texturefile->swrap(); if (options.swrap == TextureOpt::WrapPeriodic && ispow2(spec.width)) options.swrap = TextureOpt::WrapPeriodicPow2; if (options.twrap == TextureOpt::WrapDefault) options.twrap = (TextureOpt::Wrap)texturefile->twrap(); if (options.twrap == TextureOpt::WrapPeriodic && ispow2(spec.height)) options.twrap = TextureOpt::WrapPeriodicPow2; if (options.rwrap == TextureOpt::WrapDefault) options.rwrap = (TextureOpt::Wrap)texturefile->rwrap(); if (options.rwrap == TextureOpt::WrapPeriodic && ispow2(spec.depth)) options.rwrap = TextureOpt::WrapPeriodicPow2; int actualchannels = Imath::clamp (spec.nchannels - options.firstchannel, 0, nchannels); // Do the volume lookup in local space. There's not actually a way // to ask for point transforms via the ImageInput interface, so use // knowledge of the few volume reader internals to the back doors. Imath::V3f Plocal; if (texturefile->fileformat() == s_field3d) { if (! texturefile->opened()) { // We need a valid ImageInput pointer below. If the handle // has been invalidated, force it open again. texturefile->forceopen (thread_info); } Field3DInput_Interface *f3di = (Field3DInput_Interface *)texturefile->imageinput(); ASSERT (f3di); f3di->worldToLocal (P, Plocal, options.time); } else { Plocal = P; } // FIXME: we don't bother with this for dPdx, dPdy, and dPdz only // because we know that we don't currently filter volume lookups and // therefore don't actually use the derivs. If/when we do, we'll // need to transform them into local space as well. bool ok = (this->*lookup) (*texturefile, thread_info, options, nchannels, actualchannels, Plocal, dPdx, dPdy, dPdz, result, dresultds, dresultdt, dresultdr); if (actualchannels < nchannels && options.firstchannel == 0 && m_gray_to_rgb) fill_gray_channels (spec, nchannels, result, dresultds, dresultdt, dresultdr); return ok; } bool TextureSystemImpl::texture3d (ustring filename, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef P, VaryingRef dPdx, VaryingRef dPdy, VaryingRef dPdz, int nchannels, float *result, float *dresultds, float *dresultdt, float *dresultdr) { Perthread *thread_info = get_perthread_info(); TextureHandle *texture_handle = get_texture_handle (filename, thread_info); return texture3d (texture_handle, thread_info, options, runflags, beginactive, endactive, P, dPdx, dPdy, dPdz, nchannels, result, dresultds, dresultdt, dresultdr); } bool TextureSystemImpl::texture3d (TextureHandle *texture_handle, Perthread *thread_info, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef P, VaryingRef dPdx, VaryingRef dPdy, VaryingRef dPdz, int nchannels, float *result, float *dresultds, float *dresultdt, float *dresultdr) { bool ok = true; result += beginactive*nchannels; if (dresultds) { dresultds += beginactive*nchannels; dresultdt += beginactive*nchannels; } for (int i = beginactive; i < endactive; ++i) { if (runflags[i]) { TextureOpt opt (options, i); ok &= texture3d (texture_handle, thread_info, opt, P[i], dPdx[i], dPdy[i], dPdz[i], 4, result, dresultds, dresultdt, dresultdr); } result += nchannels; if (dresultds) { dresultds += nchannels; dresultdt += nchannels; dresultdr += nchannels; } } return ok; } bool TextureSystemImpl::texture3d_lookup_nomip (TextureFile &texturefile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, const Imath::V3f &P, const Imath::V3f &dPdx, const Imath::V3f &dPdy, const Imath::V3f &dPdz, float *result, float *dresultds, float *dresultdt, float *dresultdr) { // Initialize results to 0. We'll add from here on as we sample. for (int c = 0; c < nchannels_result; ++c) result[c] = 0; if (dresultds) { DASSERT (dresultdt && dresultdr); for (int c = 0; c < nchannels_result; ++c) dresultds[c] = 0; for (int c = 0; c < nchannels_result; ++c) dresultdt[c] = 0; for (int c = 0; c < nchannels_result; ++c) dresultdr[c] = 0; } // If the user only provided us with one pointer, clear all to simplify // the rest of the code, but only after we zero out the data for them so // they know something went wrong. if (!(dresultds && dresultdt && dresultdr)) dresultds = dresultdt = dresultdr = NULL; static const accum3d_prototype accum_functions[] = { // Must be in the same order as InterpMode enum &TextureSystemImpl::accum3d_sample_closest, &TextureSystemImpl::accum3d_sample_bilinear, &TextureSystemImpl::accum3d_sample_bilinear, // FIXME: bicubic, &TextureSystemImpl::accum3d_sample_bilinear, }; accum3d_prototype accumer = accum_functions[(int)options.interpmode]; bool ok = (this->*accumer) (P, 0, texturefile, thread_info, options, nchannels_result, actualchannels, 1.0f, result, dresultds, dresultdt, dresultdr); // Update stats ImageCacheStatistics &stats (thread_info->m_stats); ++stats.aniso_queries; ++stats.aniso_probes; switch (options.interpmode) { case TextureOpt::InterpClosest : ++stats.closest_interps; break; case TextureOpt::InterpBilinear : ++stats.bilinear_interps; break; case TextureOpt::InterpBicubic : ++stats.cubic_interps; break; case TextureOpt::InterpSmartBicubic : ++stats.bilinear_interps; break; } return ok; } bool TextureSystemImpl::accum3d_sample_closest (const Imath::V3f &P, int miplevel, TextureFile &texturefile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, float weight, float *accum, float *daccumds, float *daccumdt, float *daccumdr) { const ImageSpec &spec (texturefile.spec (options.subimage, miplevel)); const ImageCacheFile::LevelInfo &levelinfo (texturefile.levelinfo(options.subimage,miplevel)); TypeDesc::BASETYPE pixeltype = texturefile.pixeltype(options.subimage); // As passed in, (s,t) map the texture to (0,1). Remap to texel coords. float s = P[0] * spec.full_width + spec.full_x; float t = P[1] * spec.full_height + spec.full_y; float r = P[2] * spec.full_depth + spec.full_z; int stex, ttex, rtex; // Texel coordintes (void) floorfrac (s, &stex); // don't need fractional result (void) floorfrac (t, &ttex); (void) floorfrac (r, &rtex); wrap_impl swrap_func = wrap_functions[(int)options.swrap]; wrap_impl twrap_func = wrap_functions[(int)options.twrap]; wrap_impl rwrap_func = wrap_functions[(int)options.rwrap]; bool svalid, tvalid, rvalid; // Valid texels? false means black border svalid = swrap_func (stex, spec.x, spec.width); tvalid = twrap_func (ttex, spec.y, spec.height); rvalid = rwrap_func (rtex, spec.z, spec.depth); if (! levelinfo.full_pixel_range) { svalid &= (stex >= spec.x && stex < (spec.x+spec.width)); // data window tvalid &= (ttex >= spec.y && ttex < (spec.y+spec.height)); rvalid &= (rtex >= spec.z && rtex < (spec.z+spec.depth)); } if (! (svalid & tvalid & rvalid)) { // All texels we need were out of range and using 'black' wrap. return true; } int tile_chbegin = 0, tile_chend = spec.nchannels; if (spec.nchannels > m_max_tile_channels) { // For files with many channels, narrow the range we cache tile_chbegin = options.firstchannel; tile_chend = options.firstchannel+actualchannels; } int tile_s = (stex - spec.x) % spec.tile_width; int tile_t = (ttex - spec.y) % spec.tile_height; int tile_r = (rtex - spec.z) % spec.tile_depth; TileID id (texturefile, options.subimage, miplevel, stex - tile_s, ttex - tile_t, rtex - tile_r, tile_chbegin, tile_chend); bool ok = find_tile (id, thread_info); if (! ok) error ("%s", m_imagecache->geterror().c_str()); TileRef &tile (thread_info->tile); if (! tile || ! ok) return false; int tilepel = (tile_r * spec.tile_height + tile_t) * spec.tile_width + tile_s; int startchan_in_tile = options.firstchannel - id.chbegin(); int offset = spec.nchannels * tilepel + startchan_in_tile; DASSERT ((size_t)offset < spec.nchannels*spec.tile_pixels()); if (pixeltype == TypeDesc::UINT8) { const unsigned char *texel = tile->bytedata() + offset; for (int c = 0; c < actualchannels; ++c) accum[c] += weight * uchar2float(texel[c]); } else if (pixeltype == TypeDesc::UINT16) { const unsigned short *texel = tile->ushortdata() + offset; for (int c = 0; c < actualchannels; ++c) accum[c] += weight * ushort2float(texel[c]); } else if (pixeltype == TypeDesc::HALF) { const half *texel = tile->halfdata() + offset; for (int c = 0; c < actualchannels; ++c) accum[c] += weight * half2float(texel[c]); } else { DASSERT (pixeltype == TypeDesc::FLOAT); const float *texel = tile->floatdata() + offset; for (int c = 0; c < actualchannels; ++c) accum[c] += weight * texel[c]; } // Add appropriate amount of "fill" color to extra channels in // non-"black"-wrapped regions. if (nchannels_result > actualchannels && options.fill) { float f = weight * options.fill; for (int c = actualchannels; c < nchannels_result; ++c) accum[c] += f; } return true; } bool TextureSystemImpl::accum3d_sample_bilinear (const Imath::V3f &P, int miplevel, TextureFile &texturefile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, float weight, float *accum, float *daccumds, float *daccumdt, float *daccumdr) { const ImageSpec &spec (texturefile.spec (options.subimage, miplevel)); const ImageCacheFile::LevelInfo &levelinfo (texturefile.levelinfo(options.subimage,miplevel)); TypeDesc::BASETYPE pixeltype = texturefile.pixeltype(options.subimage); // As passed in, (s,t) map the texture to (0,1). Remap to texel coords // and subtract 0.5 because samples are at texel centers. float s = P[0] * spec.full_width + spec.full_x - 0.5f; float t = P[1] * spec.full_height + spec.full_y - 0.5f; float r = P[2] * spec.full_depth + spec.full_z - 0.5f; int sint, tint, rint; float sfrac = floorfrac (s, &sint); float tfrac = floorfrac (t, &tint); float rfrac = floorfrac (r, &rint); // Now (sint,tint,rint) are the integer coordinates of the texel to the // immediate "upper left" of the lookup point, and (sfrac,tfrac,rfrac) are // the amount that the lookup point is actually offset from the // texel center (with (1,1) being all the way to the next texel down // and to the right). // Wrap wrap_impl swrap_func = wrap_functions[(int)options.swrap]; wrap_impl twrap_func = wrap_functions[(int)options.twrap]; wrap_impl rwrap_func = wrap_functions[(int)options.rwrap]; int stex[2], ttex[2], rtex[2]; // Texel coords stex[0] = sint; stex[1] = sint+1; ttex[0] = tint; ttex[1] = tint+1; rtex[0] = rint; rtex[1] = rint+1; // bool svalid[2], tvalid[2], rvalid[2]; // Valid texels? false means black border union { bool bvalid[6]; unsigned long long ivalid; } valid_storage; valid_storage.ivalid = 0; DASSERT (sizeof(valid_storage) == 8); const unsigned long long none_valid = 0; const unsigned long long all_valid = littleendian() ? 0x010101010101LL : 0x01010101010100LL; bool *svalid = valid_storage.bvalid; bool *tvalid = valid_storage.bvalid + 2; bool *rvalid = valid_storage.bvalid + 4; svalid[0] = swrap_func (stex[0], spec.x, spec.width); svalid[1] = swrap_func (stex[1], spec.x, spec.width); tvalid[0] = twrap_func (ttex[0], spec.y, spec.height); tvalid[1] = twrap_func (ttex[1], spec.y, spec.height); rvalid[0] = rwrap_func (rtex[0], spec.z, spec.depth); rvalid[1] = rwrap_func (rtex[1], spec.z, spec.depth); // Account for crop windows if (! levelinfo.full_pixel_range) { svalid[0] &= (stex[0] >= spec.x && stex[0] < spec.x+spec.width); svalid[1] &= (stex[1] >= spec.x && stex[1] < spec.x+spec.width); tvalid[0] &= (ttex[0] >= spec.y && ttex[0] < spec.y+spec.height); tvalid[1] &= (ttex[1] >= spec.y && ttex[1] < spec.y+spec.height); rvalid[0] &= (rtex[0] >= spec.z && rtex[0] < spec.z+spec.depth); rvalid[1] &= (rtex[1] >= spec.z && rtex[1] < spec.z+spec.depth); } // if (! (svalid[0] | svalid[1] | tvalid[0] | tvalid[1] | rvalid[0] | rvalid[1])) if (valid_storage.ivalid == none_valid) return true; // All texels we need were out of range and using 'black' wrap int tilewidthmask = spec.tile_width - 1; // e.g. 63 int tileheightmask = spec.tile_height - 1; int tiledepthmask = spec.tile_depth - 1; const unsigned char *texel[2][2][2]; TileRef savetile[2][2][2]; static float black[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; int tile_s = (stex[0] - spec.x) % spec.tile_width; int tile_t = (ttex[0] - spec.y) % spec.tile_height; int tile_r = (rtex[0] - spec.z) % spec.tile_depth; bool s_onetile = (tile_s != tilewidthmask) & (stex[0]+1 == stex[1]); bool t_onetile = (tile_t != tileheightmask) & (ttex[0]+1 == ttex[1]); bool r_onetile = (tile_r != tiledepthmask) & (rtex[0]+1 == rtex[1]); bool onetile = (s_onetile & t_onetile & r_onetile); size_t channelsize = texturefile.channelsize(options.subimage); size_t pixelsize = texturefile.pixelsize(options.subimage); int tile_chbegin = 0, tile_chend = spec.nchannels; if (spec.nchannels > m_max_tile_channels) { // For files with many channels, narrow the range we cache tile_chbegin = options.firstchannel; tile_chend = options.firstchannel+actualchannels; } TileID id (texturefile, options.subimage, miplevel, 0, 0, 0, tile_chbegin, tile_chend); int startchan_in_tile = options.firstchannel - id.chbegin(); if (onetile && valid_storage.ivalid == all_valid) { // Shortcut if all the texels we need are on the same tile id.xyz (stex[0] - tile_s, ttex[0] - tile_t, rtex[0] - tile_r); bool ok = find_tile (id, thread_info); if (! ok) error ("%s", m_imagecache->geterror().c_str()); TileRef &tile (thread_info->tile); if (! tile->valid()) return false; size_t tilepel = (tile_r * spec.tile_height + tile_t) * spec.tile_width + tile_s; size_t offset = (spec.nchannels * tilepel + startchan_in_tile) * channelsize; DASSERT ((size_t)offset < spec.tile_width*spec.tile_height*spec.tile_depth*pixelsize); const unsigned char *b = tile->bytedata() + offset; texel[0][0][0] = b; texel[0][0][1] = b + pixelsize; texel[0][1][0] = b + pixelsize * spec.tile_width; texel[0][1][1] = b + pixelsize * spec.tile_width + pixelsize; b += pixelsize * spec.tile_width * spec.tile_height; texel[1][0][0] = b; texel[1][0][1] = b + pixelsize; texel[1][1][0] = b + pixelsize * spec.tile_width; texel[1][1][1] = b + pixelsize * spec.tile_width + pixelsize; } else { for (int k = 0; k < 2; ++k) { for (int j = 0; j < 2; ++j) { for (int i = 0; i < 2; ++i) { if (! (svalid[i] && tvalid[j] && rvalid[k])) { texel[k][j][i] = (unsigned char *)black; continue; } tile_s = (stex[i] - spec.x) % spec.tile_width; tile_t = (ttex[j] - spec.y) % spec.tile_height; tile_r = (rtex[k] - spec.z) % spec.tile_depth; id.xyz (stex[i] - tile_s, ttex[j] - tile_t, rtex[k] - tile_r); bool ok = find_tile (id, thread_info); if (! ok) error ("%s", m_imagecache->geterror().c_str()); TileRef &tile (thread_info->tile); if (! tile->valid()) return false; savetile[k][j][i] = tile; size_t tilepel = (tile_r * spec.tile_height + tile_t) * spec.tile_width + tile_s; size_t offset = (spec.nchannels * tilepel + startchan_in_tile) * channelsize; #ifndef NDEBUG if ((size_t)offset >= spec.tile_width*spec.tile_height*spec.tile_depth*pixelsize) std::cerr << "offset=" << offset << ", whd " << spec.tile_width << ' ' << spec.tile_height << ' ' << spec.tile_depth << " pixsize " << pixelsize << "\n"; #endif DASSERT ((size_t)offset < spec.tile_width*spec.tile_height*spec.tile_depth*pixelsize); texel[k][j][i] = tile->bytedata() + offset; DASSERT (tile->id() == id); } } } } // FIXME -- optimize the above loop by unrolling if (pixeltype == TypeDesc::UINT8) { for (int c = 0; c < actualchannels; ++c) accum[c] += weight * trilerp (uchar2float(texel[0][0][0][c]), uchar2float(texel[0][0][1][c]), uchar2float(texel[0][1][0][c]), uchar2float(texel[0][1][1][c]), uchar2float(texel[1][0][0][c]), uchar2float(texel[1][0][1][c]), uchar2float(texel[1][1][0][c]), uchar2float(texel[1][1][1][c]), sfrac, tfrac, rfrac); if (daccumds) { float scalex = weight * spec.full_width; float scaley = weight * spec.full_height; float scalez = weight * spec.full_depth; for (int c = 0; c < actualchannels; ++c) { daccumds[c] += scalex * bilerp( uchar2float(texel[0][0][1][c]) - uchar2float(texel[0][0][0][c]), uchar2float(texel[0][1][1][c]) - uchar2float(texel[0][1][0][c]), uchar2float(texel[1][0][1][c]) - uchar2float(texel[1][0][0][c]), uchar2float(texel[1][1][1][c]) - uchar2float(texel[1][1][0][c]), tfrac, rfrac ); daccumdt[c] += scaley * bilerp( uchar2float(texel[0][1][0][c]) - uchar2float(texel[0][0][0][c]), uchar2float(texel[0][1][1][c]) - uchar2float(texel[0][0][1][c]), uchar2float(texel[1][1][0][c]) - uchar2float(texel[1][0][0][c]), uchar2float(texel[1][1][1][c]) - uchar2float(texel[1][0][1][c]), sfrac, rfrac ); daccumdr[c] += scalez * bilerp( uchar2float(texel[0][1][0][c]) - uchar2float(texel[1][1][0][c]), uchar2float(texel[0][1][1][c]) - uchar2float(texel[1][1][1][c]), uchar2float(texel[0][0][1][c]) - uchar2float(texel[1][0][0][c]), uchar2float(texel[0][1][1][c]) - uchar2float(texel[1][1][1][c]), sfrac, tfrac ); } } } else if (pixeltype == TypeDesc::UINT16) { for (int c = 0; c < actualchannels; ++c) accum[c] += weight * trilerp (ushort2float(((const uint16_t *)texel[0][0][0])[c]), ushort2float(((const uint16_t *)texel[0][0][1])[c]), ushort2float(((const uint16_t *)texel[0][1][0])[c]), ushort2float(((const uint16_t *)texel[0][1][1])[c]), ushort2float(((const uint16_t *)texel[1][0][0])[c]), ushort2float(((const uint16_t *)texel[1][0][1])[c]), ushort2float(((const uint16_t *)texel[1][1][0])[c]), ushort2float(((const uint16_t *)texel[1][1][1])[c]), sfrac, tfrac, rfrac); if (daccumds) { float scalex = weight * spec.full_width; float scaley = weight * spec.full_height; float scalez = weight * spec.full_depth; for (int c = 0; c < actualchannels; ++c) { daccumds[c] += scalex * bilerp( ushort2float(((const uint16_t *)texel[0][0][1])[c]) - ushort2float(((const uint16_t *)texel[0][0][0])[c]), ushort2float(((const uint16_t *)texel[0][1][1])[c]) - ushort2float(((const uint16_t *)texel[0][1][0])[c]), ushort2float(((const uint16_t *)texel[1][0][1])[c]) - ushort2float(((const uint16_t *)texel[1][0][0])[c]), ushort2float(((const uint16_t *)texel[1][1][1])[c]) - ushort2float(((const uint16_t *)texel[1][1][0])[c]), tfrac, rfrac ); daccumdt[c] += scaley * bilerp( ushort2float(((const uint16_t *)texel[0][1][0])[c]) - ushort2float(((const uint16_t *)texel[0][0][0])[c]), ushort2float(((const uint16_t *)texel[0][1][1])[c]) - ushort2float(((const uint16_t *)texel[0][0][1])[c]), ushort2float(((const uint16_t *)texel[1][1][0])[c]) - ushort2float(((const uint16_t *)texel[1][0][0])[c]), ushort2float(((const uint16_t *)texel[1][1][1])[c]) - ushort2float(((const uint16_t *)texel[1][0][1])[c]), sfrac, rfrac ); daccumdr[c] += scalez * bilerp( ushort2float(((const uint16_t *)texel[0][1][0])[c]) - ushort2float(((const uint16_t *)texel[1][1][0])[c]), ushort2float(((const uint16_t *)texel[0][1][1])[c]) - ushort2float(((const uint16_t *)texel[1][1][1])[c]), ushort2float(((const uint16_t *)texel[0][0][1])[c]) - ushort2float(((const uint16_t *)texel[1][0][0])[c]), ushort2float(((const uint16_t *)texel[0][1][1])[c]) - ushort2float(((const uint16_t *)texel[1][1][1])[c]), sfrac, tfrac ); } } } else if (pixeltype == TypeDesc::HALF) { for (int c = 0; c < actualchannels; ++c) accum[c] += weight * trilerp (half2float(((const half *)texel[0][0][0])[c]), half2float(((const half *)texel[0][0][1])[c]), half2float(((const half *)texel[0][1][0])[c]), half2float(((const half *)texel[0][1][1])[c]), half2float(((const half *)texel[1][0][0])[c]), half2float(((const half *)texel[1][0][1])[c]), half2float(((const half *)texel[1][1][0])[c]), half2float(((const half *)texel[1][1][1])[c]), sfrac, tfrac, rfrac); if (daccumds) { float scalex = weight * spec.full_width; float scaley = weight * spec.full_height; float scalez = weight * spec.full_depth; for (int c = 0; c < actualchannels; ++c) { daccumds[c] += scalex * bilerp( half2float(((const half *)texel[0][0][1])[c]) - half2float(((const half *)texel[0][0][0])[c]), half2float(((const half *)texel[0][1][1])[c]) - half2float(((const half *)texel[0][1][0])[c]), half2float(((const half *)texel[1][0][1])[c]) - half2float(((const half *)texel[1][0][0])[c]), half2float(((const half *)texel[1][1][1])[c]) - half2float(((const half *)texel[1][1][0])[c]), tfrac, rfrac ); daccumdt[c] += scaley * bilerp( half2float(((const half *)texel[0][1][0])[c]) - half2float(((const half *)texel[0][0][0])[c]), half2float(((const half *)texel[0][1][1])[c]) - half2float(((const half *)texel[0][0][1])[c]), half2float(((const half *)texel[1][1][0])[c]) - half2float(((const half *)texel[1][0][0])[c]), half2float(((const half *)texel[1][1][1])[c]) - half2float(((const half *)texel[1][0][1])[c]), sfrac, rfrac ); daccumdr[c] += scalez * bilerp( half2float(((const half *)texel[0][1][0])[c]) - half2float(((const half *)texel[1][1][0])[c]), half2float(((const half *)texel[0][1][1])[c]) - half2float(((const half *)texel[1][1][1])[c]), half2float(((const half *)texel[0][0][1])[c]) - half2float(((const half *)texel[1][0][0])[c]), half2float(((const half *)texel[0][1][1])[c]) - half2float(((const half *)texel[1][1][1])[c]), sfrac, tfrac ); } } } else { // General case for float tiles trilerp_mad ((const float *)texel[0][0][0], (const float *)texel[0][0][1], (const float *)texel[0][1][0], (const float *)texel[0][1][1], (const float *)texel[1][0][0], (const float *)texel[1][0][1], (const float *)texel[1][1][0], (const float *)texel[1][1][1], sfrac, tfrac, rfrac, weight, actualchannels, accum); if (daccumds) { float scalex = weight * spec.full_width; float scaley = weight * spec.full_height; float scalez = weight * spec.full_depth; for (int c = 0; c < actualchannels; ++c) { daccumds[c] += scalex * bilerp( ((const float *) texel[0][0][1])[c] - ((const float *) texel[0][0][0])[c], ((const float *) texel[0][1][1])[c] - ((const float *) texel[0][1][0])[c], ((const float *) texel[1][0][1])[c] - ((const float *) texel[1][0][0])[c], ((const float *) texel[1][1][1])[c] - ((const float *) texel[1][1][0])[c], tfrac, rfrac ); daccumdt[c] += scaley * bilerp( ((const float *) texel[0][1][0])[c] - ((const float *) texel[0][0][0])[c], ((const float *) texel[0][1][1])[c] - ((const float *) texel[0][0][1])[c], ((const float *) texel[1][1][0])[c] - ((const float *) texel[1][0][0])[c], ((const float *) texel[1][1][1])[c] - ((const float *) texel[1][0][1])[c], sfrac, rfrac ); daccumdr[c] += scalez * bilerp( ((const float *) texel[0][1][0])[c] - ((const float *) texel[1][1][0])[c], ((const float *) texel[0][1][1])[c] - ((const float *) texel[1][1][1])[c], ((const float *) texel[0][0][1])[c] - ((const float *) texel[1][0][0])[c], ((const float *) texel[0][1][1])[c] - ((const float *) texel[1][1][1])[c], sfrac, tfrac ); } } } // Add appropriate amount of "fill" color to extra channels in // non-"black"-wrapped regions. if (nchannels_result > actualchannels && options.fill) { float f = trilerp (1.0f*(rvalid[0]*tvalid[0]*svalid[0]), 1.0f*(rvalid[0]*tvalid[0]*svalid[1]), 1.0f*(rvalid[0]*tvalid[1]*svalid[0]), 1.0f*(rvalid[0]*tvalid[1]*svalid[1]), 1.0f*(rvalid[1]*tvalid[0]*svalid[0]), 1.0f*(rvalid[1]*tvalid[0]*svalid[1]), 1.0f*(rvalid[1]*tvalid[1]*svalid[0]), 1.0f*(rvalid[1]*tvalid[1]*svalid[1]), sfrac, tfrac, rfrac); f *= weight * options.fill; for (int c = actualchannels; c < nchannels_result; ++c) accum[c] += f; } return true; } } // end namespace pvt OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libtexture/texturesys.cpp0000644000175000017500000032353113151711064022255 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/varyingref.h" #include "OpenImageIO/ustring.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/sysutil.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/simd.h" #include "OpenImageIO/filter.h" #include "OpenImageIO/optparser.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/texture.h" #include "OpenImageIO/imagecache.h" #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include #include "imagecache_pvt.h" #include "texture_pvt.h" #define TEX_FAST_MATH 1 OIIO_NAMESPACE_BEGIN using namespace pvt; using namespace simd; namespace { // anonymous // We would like shared_texturesys to be a shared_ptr so that it is // automatically deleted when the app exists, but because it contains a // reference to an ImageCache, we get into the "destruction order fiasco." // The only easy way to fix this is to make shared_texturesys be an ordinary // pointer and just let it leak (who cares? the app is done, and it only // contains a few hundred bytes). static TextureSystemImpl *shared_texturesys = NULL; static spin_mutex shared_texturesys_mutex; static EightBitConverter uchar2float; static float4 u8scale (1.0f/255.0f); static float4 u16scale (1.0f/65535.0f); OIIO_FORCEINLINE float4 uchar2float4 (const unsigned char *c) { return float4(c) * u8scale; } OIIO_FORCEINLINE float4 ushort2float4 (const unsigned short *s) { return float4(s) * u16scale; } OIIO_FORCEINLINE float4 half2float4 (const half *h) { return float4(h); } static const OIIO_SIMD4_ALIGN mask4 channel_masks[5] = { mask4(false, false, false, false), mask4(true, false, false, false), mask4(true, true, false, false), mask4(true, true, true, false), mask4(true, true, true, true), }; } // end anonymous namespace TextureSystem * TextureSystem::create (bool shared) { if (shared) { // They requested a shared texture system. If a shared one already // exists, just return it, otherwise record the new texture system. spin_lock guard (shared_texturesys_mutex); if (! shared_texturesys) shared_texturesys = new TextureSystemImpl(ImageCache::create(true)); #if 0 std::cerr << " shared TextureSystem is " << (void *)shared_texturesys << "\n"; #endif return shared_texturesys; } // Doesn't need a shared cache TextureSystemImpl *ts = new TextureSystemImpl (ImageCache::create(false)); #if 0 std::cerr << "creating new ImageCache " << (void *)ts << "\n"; #endif return ts; } void TextureSystem::destroy (TextureSystem *x, bool teardown_imagecache) { // std::cerr << "Destroying TS " << (void *)x << "\n"; if (! x) return; TextureSystemImpl *impl = (TextureSystemImpl *) x; if (teardown_imagecache) { ImageCache::destroy (impl->m_imagecache, true); impl->m_imagecache = NULL; } spin_lock guard (shared_texturesys_mutex); if (impl == shared_texturesys) { // This is the shared TS, so don't really delete it. } else { // Not a shared cache, we are the only owner, so truly destroy it. delete x; } } void TextureSystem::destroy (TextureSystem *x) { destroy (x, false); } namespace pvt { // namespace pvt // Wrap functions wrap 'coord' around 'width', and return true if the // result is a valid pixel coordinate, false if black should be used // instead. bool TextureSystemImpl::wrap_periodic_sharedborder (int &coord, int origin, int width) { // Like periodic, but knowing that the first column and last are // actually the same position, so we essentially skip the last // column in the next cycle. if (width <= 2) { coord = origin; // special case -- just 1 pixel wide } else { coord -= origin; coord %= (width-1); if (coord < 0) // Fix negative values coord += width; coord += origin; } return true; } const TextureSystemImpl::wrap_impl TextureSystemImpl::wrap_functions[] = { // Must be in same order as Wrap enum wrap_black, wrap_black, wrap_clamp, wrap_periodic, wrap_mirror, wrap_periodic_pow2, wrap_periodic_sharedborder }; simd::mask4 wrap_black_simd (simd::int4 &coord_, const simd::int4& origin, const simd::int4& width) { simd::int4 coord (coord_); return (coord >= origin) & (coord < (width+origin)); } simd::mask4 wrap_clamp_simd (simd::int4 &coord_, const simd::int4& origin, const simd::int4& width) { simd::int4 coord (coord_); coord = simd::blend (coord, origin, coord < origin); coord = simd::blend (coord, (origin+width-1), coord >= (origin+width)); coord_ = coord; return simd::mask4::True(); } simd::mask4 wrap_periodic_simd (simd::int4 &coord_, const simd::int4& origin, const simd::int4& width) { simd::int4 coord (coord_); coord = coord - origin; coord = coord % width; coord = simd::blend (coord, coord+width, coord < 0); coord = coord + origin; coord_ = coord; return simd::mask4::True(); } simd::mask4 wrap_periodic_pow2_simd (simd::int4 &coord_, const simd::int4& origin, const simd::int4& width) { simd::int4 coord (coord_); // DASSERT (ispow2(width)); coord = coord - origin; coord = coord & (width - 1); // Shortcut periodic if we're sure it's a pow of 2 coord = coord + origin; coord_ = coord; return simd::mask4::True(); } simd::mask4 wrap_mirror_simd (simd::int4 &coord_, const simd::int4& origin, const simd::int4& width) { simd::int4 coord (coord_); coord = coord - origin; coord = simd::blend (coord, -1 - coord, coord < 0); simd::int4 iter = coord / width; // Which iteration of the pattern? coord -= iter * width; // Odd iterations -- flip the sense coord = blend (coord, (width - 1) - coord, (iter & 1) != 0); // DASSERT_MSG (coord >= 0 && coord < width, // "width=%d, origin=%d, result=%d", width, origin, coord); coord += origin; coord_ = coord; return simd::mask4::True(); } simd::mask4 wrap_periodic_sharedborder_simd (simd::int4 &coord_, const simd::int4& origin, const simd::int4& width) { // Like periodic, but knowing that the first column and last are // actually the same position, so we essentially skip the last // column in the next cycle. simd::int4 coord (coord_); coord = coord - origin; coord = coord % (width-1); coord += blend (simd::int4(origin), width+origin, coord < 0); // Fix negative values coord = blend (coord, origin, width <= 2); // special case -- just 1 pixel wide coord_ = coord; return true; } typedef simd::mask4 (*wrap_impl_simd) (simd::int4 &coord, const simd::int4& origin, const simd::int4& width); const wrap_impl_simd wrap_functions_simd[] = { // Must be in same order as Wrap enum wrap_black_simd, wrap_black_simd, wrap_clamp_simd, wrap_periodic_simd, wrap_mirror_simd, wrap_periodic_pow2_simd, wrap_periodic_sharedborder_simd }; const char * texture_format_name (TexFormat f) { static const char * texture_format_names[] = { // MUST match the order of TexFormat "unknown", "Plain Texture", "Volume Texture", "Shadow", "CubeFace Shadow", "Volume Shadow", "LatLong Environment", "CubeFace Environment", "" }; return texture_format_names[(int)f]; } const char * texture_type_name (TexFormat f) { static const char * texture_type_names[] = { // MUST match the order of TexFormat "unknown", "Plain Texture", "Volume Texture", "Shadow", "Shadow", "Shadow", "Environment", "Environment", "" }; return texture_type_names[(int)f]; } TextureSystemImpl::TextureSystemImpl (ImageCache *imagecache) : hq_filter(NULL) { m_imagecache = (ImageCacheImpl *) imagecache; init (); } void TextureSystemImpl::init () { m_Mw2c.makeIdentity(); m_gray_to_rgb = false; m_flip_t = false; m_max_tile_channels = 5; delete hq_filter; hq_filter = Filter1D::create ("b-spline", 4); m_statslevel = 0; // Allow environment variable to override default options const char *options = getenv ("OPENIMAGEIO_TEXTURE_OPTIONS"); if (options) attribute ("options", options); #if 0 unit_test_texture (); #endif } TextureSystemImpl::~TextureSystemImpl () { printstats (); ImageCache::destroy (m_imagecache); m_imagecache = NULL; delete hq_filter; } std::string TextureSystemImpl::getstats (int level, bool icstats) const { // Merge all the threads ImageCacheStatistics stats; m_imagecache->mergestats (stats); std::ostringstream out; bool anytexture = (stats.texture_queries + stats.texture3d_queries + stats.shadow_queries + stats.environment_queries); if (level > 0 && anytexture) { out << "OpenImageIO Texture statistics\n"; std::string opt; #define BOOLOPT(name) if (m_##name) opt += #name " " #define INTOPT(name) opt += Strutil::format(#name "=%d ", m_##name) #define STROPT(name) if (m_##name.size()) opt += Strutil::format(#name "=\"%s\" ", m_##name) INTOPT(gray_to_rgb); INTOPT(flip_t); INTOPT(max_tile_channels); #undef BOOLOPT #undef INTOPT #undef STROPT out << " Options: " << Strutil::wordwrap(opt,75,12) << "\n"; out << " Queries/batches : \n"; out << " texture : " << stats.texture_queries << " queries in " << stats.texture_batches << " batches\n"; out << " texture 3d : " << stats.texture3d_queries << " queries in " << stats.texture3d_batches << " batches\n"; out << " shadow : " << stats.shadow_queries << " queries in " << stats.shadow_batches << " batches\n"; out << " environment : " << stats.environment_queries << " queries in " << stats.environment_batches << " batches\n"; out << " Interpolations :\n"; out << " closest : " << stats.closest_interps << "\n"; out << " bilinear : " << stats.bilinear_interps << "\n"; out << " bicubic : " << stats.cubic_interps << "\n"; if (stats.aniso_queries) out << Strutil::format (" Average anisotropic probes : %.3g\n", (double)stats.aniso_probes/(double)stats.aniso_queries); else out << Strutil::format (" Average anisotropic probes : 0\n"); out << Strutil::format (" Max anisotropy in the wild : %.3g\n", stats.max_aniso); if (icstats) out << "\n"; } if (icstats) out << m_imagecache->getstats (level); return out.str(); } void TextureSystemImpl::printstats () const { if (m_statslevel == 0) return; std::cout << getstats (m_statslevel, false) << "\n\n"; } void TextureSystemImpl::reset_stats () { ASSERT (m_imagecache); m_imagecache->reset_stats (); } bool TextureSystemImpl::attribute (string_view name, TypeDesc type, const void *val) { if (name == "options" && type == TypeDesc::STRING) { return optparser (*this, *(const char **)val); } if (name == "worldtocommon" && (type == TypeDesc::TypeMatrix || type == TypeDesc(TypeDesc::FLOAT,16))) { m_Mw2c = *(const Imath::M44f *)val; m_Mc2w = m_Mw2c.inverse(); return true; } if (name == "commontoworld" && (type == TypeDesc::TypeMatrix || type == TypeDesc(TypeDesc::FLOAT,16))) { m_Mc2w = *(const Imath::M44f *)val; m_Mw2c = m_Mc2w.inverse(); return true; } if ((name == "gray_to_rgb" || name == "grey_to_rgb") && (type == TypeDesc::TypeInt)) { m_gray_to_rgb = *(const int *)val; return true; } if (name == "flip_t" && type == TypeDesc::TypeInt) { m_flip_t = *(const int *)val; return true; } if (name == "m_max_tile_channels" && type == TypeDesc::TypeInt) { m_max_tile_channels = *(const int *)val; return true; } if (name == "statistics:level" && type == TypeDesc::TypeInt) { m_statslevel = *(const int *)val; // DO NOT RETURN! pass the same message to the image cache } // Maybe it's meant for the cache? return m_imagecache->attribute (name, type, val); } bool TextureSystemImpl::getattribute (string_view name, TypeDesc type, void *val) const { if (name == "worldtocommon" && (type == TypeDesc::TypeMatrix || type == TypeDesc(TypeDesc::FLOAT,16))) { *(Imath::M44f *)val = m_Mw2c; return true; } if (name == "commontoworld" && (type == TypeDesc::TypeMatrix || type == TypeDesc(TypeDesc::FLOAT,16))) { *(Imath::M44f *)val = m_Mc2w; return true; } if ((name == "gray_to_rgb" || name == "grey_to_rgb") && (type == TypeDesc::TypeInt)) { *(int *)val = m_gray_to_rgb; return true; } if (name == "flip_t" && type == TypeDesc::TypeInt) { *(int *)val = m_flip_t; return true; } if (name == "m_max_tile_channels" && type == TypeDesc::TypeInt) { *(int *)val = m_max_tile_channels; return true; } // If not one of these, maybe it's an attribute meant for the image cache? return m_imagecache->getattribute (name, type, val); return false; } std::string TextureSystemImpl::resolve_filename (const std::string &filename) const { return m_imagecache->resolve_filename (filename); } bool TextureSystemImpl::get_texture_info (ustring filename, int subimage, ustring dataname, TypeDesc datatype, void *data) { bool ok = m_imagecache->get_image_info (filename, subimage, 0, dataname, datatype, data); if (! ok) { std::string err = m_imagecache->geterror(); if (!err.empty()) error ("%s", err); } return ok; } bool TextureSystemImpl::get_texture_info (TextureHandle *texture_handle, Perthread *thread_info, int subimage, ustring dataname, TypeDesc datatype, void *data) { bool ok = m_imagecache->get_image_info ((ImageCache::ImageHandle *)texture_handle, (ImageCache::Perthread *)thread_info, subimage, 0, dataname, datatype, data); if (! ok) { std::string err = m_imagecache->geterror(); if (!err.empty()) error ("%s", err); } return ok; } bool TextureSystemImpl::get_imagespec (ustring filename, int subimage, ImageSpec &spec) { bool ok = m_imagecache->get_imagespec (filename, spec, subimage); if (! ok) { std::string err = m_imagecache->geterror(); if (!err.empty()) error ("%s", err); } return ok; } bool TextureSystemImpl::get_imagespec (TextureHandle *texture_handle, Perthread *thread_info, int subimage, ImageSpec &spec) { bool ok = m_imagecache->get_imagespec ((ImageCache::ImageHandle *)texture_handle, (ImageCache::Perthread *)thread_info, spec, subimage); if (! ok) { std::string err = m_imagecache->geterror(); if (!err.empty()) error ("%s", err); } return ok; } const ImageSpec * TextureSystemImpl::imagespec (ustring filename, int subimage) { const ImageSpec *spec = m_imagecache->imagespec (filename, subimage); if (! spec) error ("%s", m_imagecache->geterror()); return spec; } const ImageSpec * TextureSystemImpl::imagespec (TextureHandle *texture_handle, Perthread *thread_info, int subimage) { const ImageSpec *spec = m_imagecache->imagespec ((ImageCache::ImageHandle *)texture_handle, (ImageCache::Perthread *)thread_info, subimage); if (! spec) { std::string err = m_imagecache->geterror(); if (!err.empty()) error ("%s", err); } return spec; } bool TextureSystemImpl::get_texels (ustring filename, TextureOpt &options, int miplevel, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc format, void *result) { PerThreadInfo *thread_info = m_imagecache->get_perthread_info (); TextureFile *texfile = find_texturefile (filename, thread_info); if (! texfile) { error ("Texture file \"%s\" not found", filename); return false; } return get_texels ((TextureHandle *)texfile, (Perthread *)thread_info, options, miplevel, xbegin, xend, ybegin, yend, zbegin, zend, chbegin, chend, format, result); } bool TextureSystemImpl::get_texels (TextureHandle *texture_handle_, Perthread *thread_info_, TextureOpt &options, int miplevel, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc format, void *result) { PerThreadInfo *thread_info = m_imagecache->get_perthread_info((PerThreadInfo *)thread_info_); TextureFile *texfile = verify_texturefile ((TextureFile *)texture_handle_, thread_info); if (! texfile) { error ("Invalid texture handle NULL"); return false; } if (texfile->broken()) { if (texfile->errors_should_issue()) error ("Invalid texture file \"%s\"", texfile->filename()); return false; } int subimage = options.subimage; if (subimage < 0 || subimage >= texfile->subimages()) { error ("get_texel asked for nonexistant subimage %d of \"%s\"", subimage, texfile->filename()); return false; } if (miplevel < 0 || miplevel >= texfile->miplevels(subimage)) { if (texfile->errors_should_issue()) error ("get_texel asked for nonexistant MIP level %d of \"%s\"", miplevel, texfile->filename()); return false; } const ImageSpec &spec (texfile->spec(subimage, miplevel)); // FIXME -- this could be WAY more efficient than starting from // scratch for each pixel within the rectangle. Instead, we should // grab a whole tile at a time and memcpy it rapidly. But no point // doing anything more complicated (not to mention bug-prone) until // somebody reports this routine as being a bottleneck. int nchannels = chend - chbegin; int actualchannels = Imath::clamp (spec.nchannels - chbegin, 0, nchannels); int tile_chbegin = 0, tile_chend = spec.nchannels; if (spec.nchannels > m_max_tile_channels) { // For files with many channels, narrow the range we cache tile_chbegin = chbegin; tile_chend = chbegin+actualchannels; } TileID tileid (*texfile, subimage, miplevel, 0, 0, 0, tile_chbegin, tile_chend); size_t formatchannelsize = format.size(); size_t formatpixelsize = nchannels * formatchannelsize; size_t scanlinesize = (xend-xbegin) * formatpixelsize; size_t zplanesize = (yend-ybegin) * scanlinesize; bool ok = true; for (int z = zbegin; z < zend; ++z) { if (z < spec.z || z >= (spec.z+std::max(spec.depth,1))) { // nonexistant planes memset (result, 0, zplanesize); result = (void *) ((char *) result + zplanesize); continue; } tileid.z (z - ((z - spec.z) % std::max (1, spec.tile_depth))); for (int y = ybegin; y < yend; ++y) { if (y < spec.y || y >= (spec.y+spec.height)) { // nonexistant scanlines memset (result, 0, scanlinesize); result = (void *) ((char *) result + scanlinesize); continue; } tileid.y (y - ((y - spec.y) % spec.tile_height)); for (int x = xbegin; x < xend; ++x) { if (x < spec.x || x >= (spec.x+spec.width)) { // nonexistant columns memset (result, 0, formatpixelsize); result = (void *) ((char *) result + formatpixelsize); continue; } tileid.x (x - ((x - spec.x) % spec.tile_width)); ok &= find_tile (tileid, thread_info); TileRef &tile (thread_info->tile); const char *data; if (tile && (data = (const char *)tile->data (x, y, z, chbegin))) { convert_types (texfile->datatype(subimage), data, format, result, actualchannels); for (int c = actualchannels; c < nchannels; ++c) convert_types (TypeDesc::FLOAT, &options.fill, format, (char *)result+c*formatchannelsize, 1); } else { memset (result, 0, formatpixelsize); } result = (void *) ((char *) result + formatpixelsize); } } } if (! ok) { std::string err = m_imagecache->geterror(); if (! err.empty()) error ("%s", err); } return ok; } std::string TextureSystemImpl::geterror () const { std::string e; std::string *errptr = m_errormessage.get (); if (errptr) { e = *errptr; errptr->clear (); } return e; } void TextureSystemImpl::append_error (const std::string& message) const { std::string *errptr = m_errormessage.get (); if (! errptr) { errptr = new std::string; m_errormessage.reset (errptr); } ASSERT (errptr != NULL); ASSERT (errptr->size() < 1024*1024*16 && "Accumulated error messages > 16MB. Try checking return codes!"); if (errptr->size()) *errptr += '\n'; *errptr += message; } // Implementation of invalidate -- just invalidate the image cache. void TextureSystemImpl::invalidate (ustring filename) { m_imagecache->invalidate (filename); } // Implementation of invalidate -- just invalidate the image cache. void TextureSystemImpl::invalidate_all (bool force) { m_imagecache->invalidate_all (force); } bool TextureSystemImpl::missing_texture (TextureOpt &options, int nchannels, float *result, float *dresultds, float *dresultdt, float *dresultdr) { for (int c = 0; c < nchannels; ++c) { if (options.missingcolor) result[c] = options.missingcolor[c]; else result[c] = options.fill; if (dresultds) dresultds[c] = 0; if (dresultdt) dresultdt[c] = 0; if (dresultdr) dresultdr[c] = 0; } if (options.missingcolor) { // don't treat it as an error if missingcolor was supplied (void) geterror (); // eat the error return true; } else { return false; } } void TextureSystemImpl::fill_gray_channels (const ImageSpec &spec, int nchannels, float *result, float *dresultds, float *dresultdt, float *dresultdr) { int specchans = spec.nchannels; if (specchans == 1 && nchannels >= 3) { // Asked for RGB or RGBA, texture was just R... // copy the one channel to G and B *(simd::float4 *)result = simd::shuffle<0,0,0,3>(*(simd::float4 *)result); if (dresultds) { *(simd::float4 *)dresultds = simd::shuffle<0,0,0,3>(*(simd::float4 *)dresultds); *(simd::float4 *)dresultdt = simd::shuffle<0,0,0,3>(*(simd::float4 *)dresultdt); if (dresultdr) *(simd::float4 *)dresultdr = simd::shuffle<0,0,0,3>(*(simd::float4 *)dresultdr); } } else if (specchans == 2 && nchannels == 4 && spec.alpha_channel == 1) { // Asked for RGBA, texture was RA // Shuffle into RRRA *(simd::float4 *)result = simd::shuffle<0,0,0,1>(*(simd::float4 *)result); if (dresultds) { *(simd::float4 *)dresultds = simd::shuffle<0,0,0,1>(*(simd::float4 *)dresultds); *(simd::float4 *)dresultdt = simd::shuffle<0,0,0,1>(*(simd::float4 *)dresultdt); if (dresultdr) *(simd::float4 *)dresultdr = simd::shuffle<0,0,0,1>(*(simd::float4 *)dresultdr); } } } bool TextureSystemImpl::texture (ustring filename, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef s, VaryingRef t, VaryingRef dsdx, VaryingRef dtdx, VaryingRef dsdy, VaryingRef dtdy, int nchannels, float *result, float *dresultds, float *dresultdt) { Perthread *thread_info = get_perthread_info(); TextureHandle *texture_handle = get_texture_handle (filename, thread_info); return texture (texture_handle, thread_info, options, runflags, beginactive, endactive, s, t, dsdx, dtdx, dsdy, dtdy, nchannels, result, dresultds, dresultdt); } bool TextureSystemImpl::texture (TextureHandle *texture_handle, Perthread *thread_info, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef s, VaryingRef t, VaryingRef dsdx, VaryingRef dtdx, VaryingRef dsdy, VaryingRef dtdy, int nchannels, float *result, float *dresultds, float *dresultdt) { if (! texture_handle) return false; bool ok = true; result += beginactive*nchannels; if (dresultds) { dresultds += beginactive*nchannels; dresultdt += beginactive*nchannels; } for (int i = beginactive; i < endactive; ++i) { if (runflags[i]) { TextureOpt opt (options, i); ok &= texture (texture_handle, thread_info, opt, s[i], t[i], dsdx[i], dtdx[i], dsdy[i], dtdy[i], nchannels, result, dresultds, dresultdt); } result += nchannels; if (dresultds) { dresultds += nchannels; dresultdt += nchannels; } } return ok; } bool TextureSystemImpl::texture (ustring filename, TextureOpt &options, float s, float t, float dsdx, float dtdx, float dsdy, float dtdy, int nchannels, float *result, float *dresultds, float *dresultdt) { PerThreadInfo *thread_info = m_imagecache->get_perthread_info (); TextureFile *texturefile = find_texturefile (filename, thread_info); return texture ((TextureHandle *)texturefile, (Perthread *)thread_info, options, s, t, dsdx, dtdx, dsdy, dtdy, nchannels, result, dresultds, dresultdt); } bool TextureSystemImpl::texture (TextureHandle *texture_handle_, Perthread *thread_info_, TextureOpt &options, float s, float t, float dsdx, float dtdx, float dsdy, float dtdy, int nchannels, float *result, float *dresultds, float *dresultdt) { // Handle >4 channel lookups by recursion. if (nchannels > 4) { int save_firstchannel = options.firstchannel; while (nchannels) { int n = std::min (nchannels, 4); bool ok = texture (texture_handle_, thread_info_, options, s, t, dsdx, dtdx, dsdy, dtdy, n /* chans */, result, dresultds, dresultdt); if (! ok) return false; result += n; if (dresultds) dresultds += n; if (dresultdt) dresultdt += n; options.firstchannel += n; nchannels -= n; } options.firstchannel = save_firstchannel; // restore what we changed return true; } static const texture_lookup_prototype lookup_functions[] = { // Must be in the same order as Mipmode enum &TextureSystemImpl::texture_lookup, &TextureSystemImpl::texture_lookup_nomip, &TextureSystemImpl::texture_lookup_trilinear_mipmap, &TextureSystemImpl::texture_lookup_trilinear_mipmap, &TextureSystemImpl::texture_lookup }; texture_lookup_prototype lookup = lookup_functions[(int)options.mipmode]; PerThreadInfo *thread_info = m_imagecache->get_perthread_info((PerThreadInfo *)thread_info_); TextureFile *texturefile = (TextureFile *)texture_handle_; if (texturefile->is_udim()) texturefile = m_imagecache->resolve_udim (texturefile, s, t); texturefile = verify_texturefile (texturefile, thread_info); ImageCacheStatistics &stats (thread_info->m_stats); ++stats.texture_batches; ++stats.texture_queries; if (! texturefile || texturefile->broken()) return missing_texture (options, nchannels, result, dresultds, dresultdt); if (options.subimagename) { // If subimage was specified by name, figure out its index. int s = m_imagecache->subimage_from_name (texturefile, options.subimagename); if (s < 0) { error ("Unknown subimage \"%s\" in texture \"%s\"", options.subimagename, texturefile->filename()); return false; } options.subimage = s; options.subimagename.clear(); } const ImageCacheFile::SubimageInfo &subinfo (texturefile->subimageinfo(options.subimage)); const ImageSpec &spec (texturefile->spec(options.subimage, 0)); int actualchannels = Imath::clamp (spec.nchannels - options.firstchannel, 0, nchannels); // Figure out the wrap functions if (options.swrap == TextureOpt::WrapDefault) options.swrap = (TextureOpt::Wrap)texturefile->swrap(); if (options.swrap == TextureOpt::WrapPeriodic && ispow2(spec.width)) options.swrap = TextureOpt::WrapPeriodicPow2; if (options.twrap == TextureOpt::WrapDefault) options.twrap = (TextureOpt::Wrap)texturefile->twrap(); if (options.twrap == TextureOpt::WrapPeriodic && ispow2(spec.height)) options.twrap = TextureOpt::WrapPeriodicPow2; if (subinfo.is_constant_image && options.swrap != TextureOpt::WrapBlack && options.twrap != TextureOpt::WrapBlack) { // Lookup of constant color texture, non-black wrap -- skip all the // hard stuff. for (int c = 0; c < actualchannels; ++c) result[c] = subinfo.average_color[c+options.firstchannel]; for (int c = actualchannels; c < nchannels; ++c) result[c] = options.fill; if (dresultds) { // Derivs are always 0 from a constant texture lookup for (int c = 0; c < nchannels; ++c) { dresultds[c] = 0.0f; dresultdt[c] = 0.0f; } } if (actualchannels < nchannels && options.firstchannel == 0 && m_gray_to_rgb) fill_gray_channels (spec, nchannels, result, dresultds, dresultdt); return true; } if (m_flip_t) { t = 1.0f - t; dtdx *= -1.0f; dtdy *= -1.0f; } if (! subinfo.full_pixel_range) { // remap st for overscan or crop s = s * subinfo.sscale + subinfo.soffset; dsdx *= subinfo.sscale; dsdy *= subinfo.sscale; t = t * subinfo.tscale + subinfo.toffset; dtdx *= subinfo.tscale; dtdy *= subinfo.tscale; } bool ok; // Everything from the lookup function on down will assume that there // is space for a float4 in all of the result locations, so if that's // not the case (or it's not properly aligned), make a local copy and // then copy back when we're done. // FIXME -- is this necessary at all? Can we eliminate the conditional // and the duplicated code by always doing the simd copy thing? Come // back here and time whether for 4-channnel textures it really matters. bool simd_copy = (nchannels != 4 || ((size_t)result&0x0f) || ((size_t)dresultds & 0x0f) || /* FIXME -- necessary? */ ((size_t)dresultdt & 0x0f)); if (simd_copy) { simd::float4 result_simd, dresultds_simd, dresultdt_simd; float * OIIO_RESTRICT saved_dresultds = dresultds; float * OIIO_RESTRICT saved_dresultdt = dresultdt; if (saved_dresultds) { dresultds = (float *)&dresultds_simd; dresultdt = (float *)&dresultdt_simd; } ok = (this->*lookup) (*texturefile, thread_info, options, nchannels, actualchannels, s, t, dsdx, dtdx, dsdy, dtdy, (float *)&result_simd, dresultds, dresultdt); if (actualchannels < nchannels && options.firstchannel == 0 && m_gray_to_rgb) fill_gray_channels (spec, nchannels, (float *)&result_simd, dresultds, dresultdt); result_simd.store (result, nchannels); if (saved_dresultds) { if (m_flip_t) dresultdt_simd = -dresultdt_simd; dresultds_simd.store (saved_dresultds, nchannels); dresultdt_simd.store (saved_dresultdt, nchannels); } } else { // All provided output slots are aligned 4-floats, use them directly ok = (this->*lookup) (*texturefile, thread_info, options, nchannels, actualchannels, s, t, dsdx, dtdx, dsdy, dtdy, result, dresultds, dresultdt); if (actualchannels < nchannels && options.firstchannel == 0 && m_gray_to_rgb) fill_gray_channels (spec, nchannels, result, dresultds, dresultdt); if (m_flip_t && dresultdt) *(float4 *)dresultdt = -(*(float4 *)dresultdt); } return ok; } bool TextureSystemImpl::texture_lookup_nomip (TextureFile &texturefile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, float s, float t, float dsdx, float dtdx, float dsdy, float dtdy, float *result, float *dresultds, float *dresultdt) { // Initialize results to 0. We'll add from here on as we sample. DASSERT ((dresultds == NULL) == (dresultdt == NULL)); ((simd::float4 *)result)->clear(); if (dresultds) { ((simd::float4 *)dresultds)->clear(); ((simd::float4 *)dresultdt)->clear(); } static const sampler_prototype sample_functions[] = { // Must be in the same order as InterpMode enum &TextureSystemImpl::sample_closest, &TextureSystemImpl::sample_bilinear, &TextureSystemImpl::sample_bicubic, &TextureSystemImpl::sample_bilinear, }; sampler_prototype sampler = sample_functions[(int)options.interpmode]; OIIO_SIMD4_ALIGN float sval[4] = { s, 0.0f, 0.0f, 0.0f }; OIIO_SIMD4_ALIGN float tval[4] = { t, 0.0f, 0.0f, 0.0f }; static OIIO_SIMD4_ALIGN float weight[4] = { 1.0f, 0.0f, 0.0f, 0.0f }; bool ok = (this->*sampler) (1, sval, tval, 0 /*miplevel*/, texturefile, thread_info, options, nchannels_result, actualchannels, weight, (float4 *)result, (float4 *)dresultds, (float4 *)dresultdt); // Update stats ImageCacheStatistics &stats (thread_info->m_stats); ++stats.aniso_queries; ++stats.aniso_probes; switch (options.interpmode) { case TextureOpt::InterpClosest : ++stats.closest_interps; break; case TextureOpt::InterpBilinear : ++stats.bilinear_interps; break; case TextureOpt::InterpBicubic : ++stats.cubic_interps; break; case TextureOpt::InterpSmartBicubic : ++stats.bilinear_interps; break; } return ok; } // Scale the derivs as dictated by 'width' and 'blur', and also make sure // they are all some minimum value to make the subsequent math clean. inline void adjust_width (float &dsdx, float &dtdx, float &dsdy, float &dtdy, float swidth, float twidth) { // Trust user not to use nonsensical width<0 dsdx *= swidth; dtdx *= twidth; dsdy *= swidth; dtdy *= twidth; // Clamp degenerate derivatives so they don't cause mathematical problems static const float eps = 1.0e-8f, eps2 = eps*eps; float dxlen2 = dsdx*dsdx + dtdx*dtdx; float dylen2 = dsdy*dsdy + dtdy*dtdy; if (dxlen2 < eps2) { // Tiny dx if (dylen2 < eps2) { // Tiny dx and dy: Essentially point sampling. Substitute a // tiny but finite filter. dsdx = eps; dsdy = 0; dtdx = 0; dtdy = eps; dxlen2 = dylen2 = eps2; } else { // Tiny dx, sane dy -- pick a small dx orthogonal to dy, but // of length eps. float scale = eps / sqrtf(dylen2); dsdx = dtdy * scale; dtdx = -dsdy * scale; dxlen2 = eps2; } } else if (dylen2 < eps2) { // Tiny dy, sane dx -- pick a small dy orthogonal to dx, but of // length eps. float scale = eps / sqrtf(dxlen2); dsdy = -dtdx * scale; dtdy = dsdx * scale; dylen2 = eps2; } } // Adjust the ellipse major and minor axes based on the blur, if nonzero. // Trust user not to use nonsensical blur<0 inline void adjust_blur (float &majorlength, float &minorlength, float theta, float sblur, float tblur) { if (sblur+tblur != 0.0f /* avoid the work when blur is zero */) { // Carefully add blur to the right derivative components in the // right proportions -- merely adding the same amount of blur // to all four derivatives blurs too much at some angles. // FIXME -- we should benchmark whether a fast approximate rsqrt // here could have any detectable performance improvement. DASSERT (majorlength > 0.0f && minorlength > 0.0f); float sintheta, costheta; #ifdef TEX_FAST_MATH fast_sincos (theta, &sintheta, &costheta); #else sincos (theta, &sintheta, &costheta); #endif sintheta = fabsf(sintheta); costheta = fabsf(costheta); majorlength += sblur * costheta + tblur * sintheta; minorlength += sblur * sintheta + tblur * costheta; } } // For the given texture file, options, and ellipse major and minor // lengths and aspect ratio, compute the two MIPmap levels and // respective weights to use for a texture lookup. The general strategy // is that we choose the MIPmap level so that the minor axis length is // pixel-sized (and then we will sample several times along the major // axis in order to handle anisotropy), but we make adjustments in // corner cases where the ideal sampling is too high or too low resolution // given the MIPmap levels we have available. inline void compute_miplevels (TextureSystemImpl::TextureFile &texturefile, TextureOpt &options, float majorlength, float minorlength, float &aspect, int *miplevel, float *levelweight) { ImageCacheFile::SubimageInfo &subinfo (texturefile.subimageinfo(options.subimage)); float levelblend = 0.0f; int nmiplevels = (int)subinfo.levels.size(); for (int m = 0; m < nmiplevels; ++m) { // Compute the filter size (minor axis) in raster space at this // MIP level. We use the smaller of the two texture resolutions, // which is better than just using one, but a more principled // approach is desired but remains elusive. FIXME. float filtwidth_ras = minorlength * std::min (subinfo.spec(m).width, subinfo.spec(m).height); // Once the filter width is smaller than one texel at this level, // we've gone too far, so we know that we want to interpolate the // previous level and the current level. Note that filtwidth_ras // is expected to be >= 0.5, or would have stopped one level ago. if (filtwidth_ras <= 1.0f) { miplevel[0] = m-1; miplevel[1] = m; levelblend = Imath::clamp (2.0f*filtwidth_ras - 1.0f, 0.0f, 1.0f); break; } } if (miplevel[1] < 0) { // We'd like to blur even more, but make due with the coarsest // MIP level. miplevel[0] = nmiplevels - 1; miplevel[1] = miplevel[0]; levelblend = 0; } else if (miplevel[0] < 0) { // We wish we had even more resolution than the finest MIP level, // but tough for us. miplevel[0] = 0; miplevel[1] = 0; levelblend = 0; // It's possible that minorlength is degenerate, giving an aspect // ratio that implies a huge nsamples, which is pointless if those // samples are too close. So if minorlength is less than 1/2 texel // at the finest resolution, clamp it and recalculate aspect. int r = std::max (subinfo.spec(0).full_width, subinfo.spec(0).full_height); if (minorlength*r < 0.5f) { aspect = Imath::clamp (majorlength * r * 2.0f, 1.0f, float(options.anisotropic)); } } if (options.mipmode == TextureOpt::MipModeOneLevel) { miplevel[0] = miplevel[1]; levelblend = 0; } levelweight[0] = 1.0f - levelblend; levelweight[1] = levelblend; } bool TextureSystemImpl::texture_lookup_trilinear_mipmap (TextureFile &texturefile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, float s, float t, float dsdx, float dtdx, float dsdy, float dtdy, float *result, float *dresultds, float *dresultdt) { // Initialize results to 0. We'll add from here on as we sample. DASSERT ((dresultds == NULL) == (dresultdt == NULL)); ((simd::float4 *)result)->clear(); if (dresultds) { ((simd::float4 *)dresultds)->clear(); ((simd::float4 *)dresultdt)->clear(); } adjust_width (dsdx, dtdx, dsdy, dtdy, options.swidth, options.twidth); // Determine the MIP-map level(s) we need: we will blend // data(miplevel[0]) * (1-levelblend) + data(miplevel[1]) * levelblend int miplevel[2] = { -1, -1 }; float levelweight[2] = { 0, 0 }; float sfilt = std::max (fabsf(dsdx), fabsf(dsdy)); float tfilt = std::max (fabsf(dtdx), fabsf(dtdy)); float filtwidth = options.conservative_filter ? std::max (sfilt, tfilt) : std::min (sfilt, tfilt); // account for blur filtwidth += std::max (options.sblur, options.tblur); float aspect = 1.0f; compute_miplevels (texturefile, options, filtwidth, filtwidth, aspect, miplevel, levelweight); static const sampler_prototype sample_functions[] = { // Must be in the same order as InterpMode enum &TextureSystemImpl::sample_closest, &TextureSystemImpl::sample_bilinear, &TextureSystemImpl::sample_bicubic, &TextureSystemImpl::sample_bilinear, }; sampler_prototype sampler = sample_functions[(int)options.interpmode]; // FIXME -- support for smart cubic? OIIO_SIMD4_ALIGN float sval[4] = { s, 0.0f, 0.0f, 0.0f }; OIIO_SIMD4_ALIGN float tval[4] = { t, 0.0f, 0.0f, 0.0f }; OIIO_SIMD4_ALIGN float weight[4] = { 1.0f, 0.0f, 0.0f, 0.0f }; bool ok = true; int npointson = 0; float4 r_sum, drds_sum, drdt_sum; r_sum.clear(); if (dresultds) { drds_sum.clear(); drdt_sum.clear(); } for (int level = 0; level < 2; ++level) { if (! levelweight[level]) // No contribution from this level, skip it continue; float4 r, drds, drdt; ok &= (this->*sampler) (1, sval, tval, miplevel[level], texturefile, thread_info, options, nchannels_result, actualchannels, weight, &r, dresultds ? &drds : NULL, dresultds ? &drdt : NULL); ++npointson; float4 lw = levelweight[level]; r_sum += lw * r; if (dresultds) { drds_sum += lw * drds; drdt_sum += lw * drdt; } } *(simd::float4 *)(result) = r_sum; if (dresultds) { *(simd::float4 *)(dresultds) = drds_sum; *(simd::float4 *)(dresultdt) = drdt_sum; } // Update stats ImageCacheStatistics &stats (thread_info->m_stats); stats.aniso_queries += npointson; stats.aniso_probes += npointson; switch (options.interpmode) { case TextureOpt::InterpClosest : stats.closest_interps += npointson; break; case TextureOpt::InterpBilinear : stats.bilinear_interps += npointson; break; case TextureOpt::InterpBicubic : stats.cubic_interps += npointson; break; case TextureOpt::InterpSmartBicubic : stats.bilinear_interps += npointson; break; } return ok; } // Given pixel derivatives, calculate major and minor axis lengths and // major axis orientation angle of the ellipse. See Greene's EWA paper // or Mavridis (2011). If ABCF is non-NULL, it should point to space // for 4 floats, which will be used to store the ellipse parameters A, // B, C, F. inline void ellipse_axes (float dsdx, float dtdx, float dsdy, float dtdy, float &majorlength, float &minorlength, float &theta, float *ABCF = NULL) { float dsdx2 = dsdx*dsdx; float dtdx2 = dtdx*dtdx; float dsdy2 = dsdy*dsdy; float dtdy2 = dtdy*dtdy; double A = dtdx2 + dtdy2; double B = -2.0 * (dsdx * dtdx + dsdy * dtdy); double C = dsdx2 + dsdy2; double root = hypot (A-C, B); // equivalent: sqrt (A*A - 2AC + C*C + B*B) double Aprime = (A + C - root) * 0.5; double Cprime = (A + C + root) * 0.5; #if 0 double F = A*C - B*B*0.25; majorlength = std::min(safe_sqrt (float(F / Aprime)), 1000.0f); minorlength = std::min(safe_sqrt (float(F / Cprime)), 1000.0f); #else // Wolfram says that this is equivalent to: majorlength = std::min (safe_sqrt(float(Cprime)), 1000.0f); minorlength = std::min (safe_sqrt(float(Aprime)), 1000.0f); #endif #ifdef TEX_FAST_MATH theta = fast_atan2 (B, A-C) * 0.5f + float(M_PI_2); #else theta = atan2 (B, A-C) * 0.5 + M_PI_2; #endif if (ABCF) { // Optionally store the ellipse equation parameters, the ellipse // is given by: A*u^2 + B*u*v + C*v^2 < 1 double F = A*C - B*B*0.25; double Finv = 1.0f / F; ABCF[0] = A * Finv; ABCF[1] = B * Finv; ABCF[2] = C * Finv; ABCF[3] = F; } // N.B. If the derivs passed in are the full pixel-to-pixel // derivatives, then majorlength/minorlength are the (diameter) axes // of the ellipse; if the derivs are the "to edge of pixel" 1/2 // length derivs, then majorlength/minorlength are the major and // minur radii of the elipse. We do the former! So it's important // to consider that factor of 2 in compute_ellipse_sampling. } // Given the aspect ratio, major axis orientation angle, and axis lengths, // calculate the smajor & tmajor values that give the orientation of the // line on which samples should be distributed. If there are n samples, // they should be positioned as: // p_i = 2*(i+0.5)/n - 1.0; // sample_i = (s + p_i*smajor, t + p_i*tmajor) // If a weights ptr is supplied, it will be filled in [0..nsamples-1] with // normalized weights for each sample. inline int compute_ellipse_sampling (float aspect, float theta, float majorlength, float minorlength, float &smajor, float &tmajor, float &invsamples, float *weights=NULL) { // Compute the sin and cos of the sampling direction, given major // axis angle sincos (theta, &tmajor, &smajor); float L = 2.0f * (majorlength - minorlength); smajor *= L; tmajor *= L; #if 1 // This is the theoretically correct number of samples. int nsamples = std::max (1, int(2.0f*aspect-1.0f)); #else int nsamples = std::max (1, (int) ceilf (aspect - 0.3f)); // This approach does fewer samples for high aspect ratios, but I see // artifacts. #endif invsamples = 1.0f / nsamples; if (weights) { if (nsamples == 1) { weights[0] = 1.0f; } else if (nsamples == 2) { weights[0] = 0.5f; weights[1] = 0.5f; } else { float scale = majorlength / L; // 1/(L/major) for (int i = 0, e = (nsamples+1)/2; i < e; ++i) { float x = (2.0f*(i+0.5f)*invsamples - 1.0f) * scale; #ifdef TEX_FAST_MATH float w = fast_exp(-2.0f*x*x); #else float w = expf(-2.0f*x*x); #endif weights[nsamples-i-1] = weights[i] = w; } float sumw = 0.0f; for (int i = 0; i < nsamples; ++i) sumw += weights[i]; for (int i = 0; i < nsamples; ++i) weights[i] /= sumw; } } return nsamples; } bool TextureSystemImpl::texture_lookup (TextureFile &texturefile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, float s, float t, float dsdx, float dtdx, float dsdy, float dtdy, float *result, float *dresultds, float *dresultdt) { DASSERT ((dresultds == NULL) == (dresultdt == NULL)); // Compute the natural resolution we want for the bare derivs, this // will be the threshold for knowing we're maxifying (and therefore // wanting cubic interpolation). float sfilt_noblur = std::max (std::max (fabsf(dsdx), fabsf(dsdy)), 1e-8f); float tfilt_noblur = std::max (std::max (fabsf(dtdx), fabsf(dtdy)), 1e-8f); int naturalsres = (int) (1.0f / sfilt_noblur); int naturaltres = (int) (1.0f / tfilt_noblur); // Scale by 'width' adjust_width (dsdx, dtdx, dsdy, dtdy, options.swidth, options.twidth); // Determine the MIP-map level(s) we need: we will blend // data(miplevel[0]) * (1-levelblend) + data(miplevel[1]) * levelblend float smajor, tmajor; float majorlength, minorlength; float theta; // Do a bit more math and get the exact ellipse axis lengths, and // therefore a more accurate aspect ratio as well. Looks much MUCH // better, but for scenes with lots of grazing angles, it can greatly // increase the average anisotropy, therefore the number of bilinear // or bicubic texture probes, and therefore runtime! ellipse_axes (dsdx, dtdx, dsdy, dtdy, majorlength, minorlength, theta); adjust_blur (majorlength, minorlength, theta, options.sblur, options.tblur); float aspect, trueaspect; aspect = anisotropic_aspect (majorlength, minorlength, options, trueaspect); int miplevel[2] = { -1, -1 }; float levelweight[2] = { 0, 0 }; compute_miplevels (texturefile, options, majorlength, minorlength, aspect, miplevel, levelweight); float *lineweight = ALLOCA (float, round_to_multiple_of_pow2(2*options.anisotropic, 4)); float invsamples; int nsamples = compute_ellipse_sampling (aspect, theta, majorlength, minorlength, smajor, tmajor, invsamples, lineweight); // All the computations were done assuming full diametric axes of // the ellipse, but our derivatives are pixel-to-pixel, yielding // semi-major and semi-minor lengths, so we need to scale everything // by 1/2. smajor *= 0.5f; tmajor *= 0.5f; bool ok = true; int npointson = 0; int closestprobes = 0, bilinearprobes = 0, bicubicprobes = 0; int nsamples_padded = round_to_multiple_of_pow2 (nsamples, 4); float *sval = OIIO_ALLOCA (float, nsamples_padded); float *tval = OIIO_ALLOCA (float, nsamples_padded); // Compute the s and t positions of the samples along the major axis. #if OIIO_SIMD // Do the computations in batches of 4, with SIMD ops. static OIIO_SIMD4_ALIGN float iota_start[4] = { 0.5f, 1.5f, 2.5f, 3.5f }; float4 iota = *(const float4 *)iota_start; for (int sample = 0; sample < nsamples; sample += 4) { float4 pos = 2.0f * (iota * invsamples - 0.5f); float4 ss = s + pos * smajor; float4 tt = t + pos * tmajor; ss.store (sval+sample); tt.store (tval+sample); iota += 4.0f; } #else // Non-SIMD, reference code for (int sample = 0; sample < nsamples; ++sample) { float pos = 2.0f * ((sample + 0.5f) * invsamples - 0.5f); sval[sample] = s + pos * smajor; tval[sample] = t + pos * tmajor; } #endif float4 r_sum, drds_sum, drdt_sum; r_sum.clear(); if (dresultds) { drds_sum.clear(); drdt_sum.clear(); } for (int level = 0; level < 2; ++level) { if (! levelweight[level]) // No contribution from this level, skip it continue; ++npointson; float4 r, drds, drdt; int lev = miplevel[level]; switch (options.interpmode) { case TextureOpt::InterpClosest : ok &= sample_closest (nsamples, sval, tval, lev, texturefile, thread_info, options, nchannels_result, actualchannels, lineweight, &r, NULL, NULL); ++closestprobes; break; case TextureOpt::InterpBilinear : ok &= sample_bilinear (nsamples, sval, tval, lev, texturefile, thread_info, options, nchannels_result, actualchannels, lineweight, &r, dresultds ? &drds : NULL, dresultds ? &drdt : NULL); ++bilinearprobes; break; case TextureOpt::InterpBicubic : ok &= sample_bicubic (nsamples, sval, tval, lev, texturefile, thread_info, options, nchannels_result, actualchannels, lineweight, &r, dresultds ? &drds : NULL, dresultds ? &drdt : NULL); ++bicubicprobes; break; case TextureOpt::InterpSmartBicubic : if (lev == 0 || (texturefile.spec(options.subimage,lev).width < naturalsres/2) || (texturefile.spec(options.subimage,lev).height < naturaltres/2)) { ok &= sample_bicubic (nsamples, sval, tval, lev, texturefile, thread_info, options, nchannels_result, actualchannels, lineweight, &r, dresultds ? &drds : NULL, dresultds ? &drdt : NULL); ++bicubicprobes; } else { ok &= sample_bilinear (nsamples, sval, tval, lev, texturefile, thread_info, options, nchannels_result, actualchannels, lineweight, &r, dresultds ? &drds : NULL, dresultds ? &drdt : NULL); ++bilinearprobes; } break; } float4 lw = levelweight[level]; r_sum += lw * r; if (dresultds) { drds_sum += lw * drds; drdt_sum += lw * drdt; } } *(simd::float4 *)(result) = r_sum; if (dresultds) { *(simd::float4 *)(dresultds) = drds_sum; *(simd::float4 *)(dresultdt) = drdt_sum; } // Update stats ImageCacheStatistics &stats (thread_info->m_stats); stats.aniso_queries += npointson; stats.aniso_probes += npointson * nsamples; if (trueaspect > stats.max_aniso) stats.max_aniso = trueaspect; // FIXME? stats.closest_interps += closestprobes * nsamples; stats.bilinear_interps += bilinearprobes * nsamples; stats.cubic_interps += bicubicprobes * nsamples; return ok; } const float * TextureSystemImpl::pole_color (TextureFile &texturefile, PerThreadInfo *thread_info, const ImageCacheFile::LevelInfo &levelinfo, TileRef &tile, int subimage, int miplevel, int pole) { if (! levelinfo.onetile) return NULL; // Only compute color for one-tile MIP levels const ImageSpec &spec (levelinfo.spec); if (! levelinfo.polecolorcomputed) { static spin_mutex mutex; // Protect everybody's polecolor spin_lock lock (mutex); if (! levelinfo.polecolorcomputed) { DASSERT (levelinfo.polecolor.size() == 0); levelinfo.polecolor.resize (2*spec.nchannels); ASSERT (tile->id().nchannels() == spec.nchannels && "pole_color doesn't work for channel subsets"); int pixelsize = tile->pixelsize(); TypeDesc::BASETYPE pixeltype = texturefile.pixeltype(subimage); // We store north and south poles adjacently in polecolor float *p = &(levelinfo.polecolor[0]); int width = spec.width; float scale = 1.0f / width; for (int pole = 0; pole <= 1; ++pole, p += spec.nchannels) { int y = pole * (spec.height-1); // 0 or height-1 for (int c = 0; c < spec.nchannels; ++c) p[c] = 0.0f; const unsigned char *texel = tile->bytedata() + y*spec.tile_width*pixelsize; for (int i = 0; i < width; ++i, texel += pixelsize) for (int c = 0; c < spec.nchannels; ++c) { if (pixeltype == TypeDesc::UINT8) p[c] += uchar2float(texel[c]); else if (pixeltype == TypeDesc::UINT16) p[c] += convert_type (((const uint16_t *)texel)[c]); else if (pixeltype == TypeDesc::HALF) p[c] += ((const half *)texel)[c]; else { DASSERT (pixeltype == TypeDesc::FLOAT); p[c] += ((const float *)texel)[c]; } } for (int c = 0; c < spec.nchannels; ++c) p[c] *= scale; } levelinfo.polecolorcomputed = true; } } return &(levelinfo.polecolor[pole*spec.nchannels]); } void TextureSystemImpl::fade_to_pole (float t, float *accum, float &weight, TextureFile &texturefile, PerThreadInfo *thread_info, const ImageCacheFile::LevelInfo &levelinfo, TextureOpt &options, int miplevel, int nchannels) { // N.B. We want to fade to pole colors right at texture // boundaries t==0 and t==height, but at the very top of this // function, we subtracted another 0.5 from t, so we need to // undo that here. float pole; const float *polecolor; if (t < 1.0f) { pole = (1.0f - t); polecolor = pole_color (texturefile, thread_info, levelinfo, thread_info->tile, options.subimage, miplevel, 0); } else { pole = t - floorf(t); polecolor = pole_color (texturefile, thread_info, levelinfo, thread_info->tile, options.subimage, miplevel, 1); } pole = Imath::clamp (pole, 0.0f, 1.0f); pole *= pole; // squaring makes more pleasing appearance polecolor += options.firstchannel; for (int c = 0; c < nchannels; ++c) accum[c] += weight * pole * polecolor[c]; weight *= 1.0f - pole; } bool TextureSystemImpl::sample_closest (int nsamples, const float *s_, const float *t_, int miplevel, TextureFile &texturefile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, const float *weight_, float4 *accum_, float4 *daccumds_, float4 *daccumdt_) { bool allok = true; const ImageSpec &spec (texturefile.spec (options.subimage, miplevel)); const ImageCacheFile::LevelInfo &levelinfo (texturefile.levelinfo(options.subimage,miplevel)); TypeDesc::BASETYPE pixeltype = texturefile.pixeltype(options.subimage); wrap_impl swrap_func = wrap_functions[(int)options.swrap]; wrap_impl twrap_func = wrap_functions[(int)options.twrap]; float4 accum; accum.clear(); float nonfill = 0.0f; int firstchannel = options.firstchannel; int tile_chbegin = 0, tile_chend = spec.nchannels; if (spec.nchannels > m_max_tile_channels) { // For files with many channels, narrow the range we cache tile_chbegin = options.firstchannel; tile_chend = options.firstchannel+actualchannels; } TileID id (texturefile, options.subimage, miplevel, 0, 0, 0, tile_chbegin, tile_chend); for (int sample = 0; sample < nsamples; ++sample) { float s = s_[sample], t = t_[sample]; float weight = weight_[sample]; int stex, ttex; // Texel coordintes float sfrac, tfrac; st_to_texel (s, t, texturefile, spec, stex, ttex, sfrac, tfrac); if (sfrac > 0.5f) ++stex; if (tfrac > 0.5f) ++ttex; // Wrap bool svalid, tvalid; // Valid texels? false means black border svalid = swrap_func (stex, spec.x, spec.width); tvalid = twrap_func (ttex, spec.y, spec.height); if (! levelinfo.full_pixel_range) { svalid &= (stex >= spec.x && stex < (spec.x+spec.width)); // data window tvalid &= (ttex >= spec.y && ttex < (spec.y+spec.height)); } if (! (svalid & tvalid)) { // All texels we need were out of range and using 'black' wrap. nonfill += weight; continue; } int tile_s = (stex - spec.x) % spec.tile_width; int tile_t = (ttex - spec.y) % spec.tile_height; id.xy (stex - tile_s, ttex - tile_t); bool ok = find_tile (id, thread_info); if (! ok) error ("%s", m_imagecache->geterror()); TileRef &tile (thread_info->tile); if (! tile || ! ok) { allok = false; continue; } int offset = id.nchannels() * (tile_t * spec.tile_width + tile_s) + (firstchannel - id.chbegin()); DASSERT ((size_t)offset < spec.nchannels*spec.tile_pixels()); simd::float4 texel_simd; if (pixeltype == TypeDesc::UINT8) { // special case for 8-bit tiles texel_simd = uchar2float4 (tile->bytedata() + offset); } else if (pixeltype == TypeDesc::UINT16) { texel_simd = ushort2float4 (tile->ushortdata() + offset); } else if (pixeltype == TypeDesc::HALF) { texel_simd = half2float4 (tile->halfdata() + offset); } else { DASSERT (pixeltype == TypeDesc::FLOAT); texel_simd.load (tile->floatdata() + offset); } accum += weight * texel_simd; } simd::mask4 channel_mask = channel_masks[actualchannels]; accum = blend0(accum, channel_mask); if (nonfill < 1.0f && nchannels_result > actualchannels && options.fill) { // Add the weighted fill color accum += blend0not(float4((1.0f - nonfill) * options.fill), channel_mask); } *accum_ = accum; if (daccumds_) { daccumds_->clear(); // constant interp has 0 derivatives daccumdt_->clear(); } return allok; } // return the greatest integer <= x, for 4 values at once OIIO_FORCEINLINE int4 quick_floor (const float4& x) { #if 0 // Even on SSE 4.1, this is actually very slightly slower! // Continue to test on future architectures. return floori(x); #else int4 b (x); // truncates int4 isneg = bitcast_to_int4 (x < float4::Zero()); return b + isneg; // Trick here (thanks, Cycles, for letting me spy on your code): the // comparison will return (int)-1 for components that are less than // zero, and adding that is the same as subtracting one! #endif } // floatfrac for four sets of values at once. inline float4 floorfrac (const float4& x, int4 * i) { #if 0 float4 thefloor = floor(x); *i = int4(thefloor); return x-thefloor; #else int4 thefloor = quick_floor (x); *i = thefloor; return x - float4(thefloor); #endif } /// Convert texture coordinates (s,t), which range on 0-1 for the "full" /// image boundary, to texel coordinates (i+ifrac,j+jfrac) where (i,j) is /// the texel to the immediate upper left of the sample position, and ifrac /// and jfrac are the fractional (0-1) portion of the way to the next texel /// to the right or down, respectively. Do this for 4 s,t values at a time. inline void st_to_texel_simd (const float4& s_, const float4& t_, TextureSystemImpl::TextureFile &texturefile, const ImageSpec &spec, int4 &i, int4 &j, float4 &ifrac, float4 &jfrac) { float4 s,t; // As passed in, (s,t) map the texture to (0,1). Remap to texel coords. // Note that we have two modes, depending on the m_sample_border. if (texturefile.sample_border() == 0) { // texel samples are at 0.5/res, 1.5/res, ..., (res-0.5)/res, s = s_ * float(spec.width) + (spec.x - 0.5f); t = t_ * float(spec.height) + (spec.y - 0.5f); } else { // first and last rows/columns are *exactly* on the boundary, // so samples are at 0, 1/(res-1), ..., 1. s = s_ * float(spec.width-1) + float(spec.x); t = t_ * float(spec.height-1) + float(spec.y); } ifrac = floorfrac (s, &i); jfrac = floorfrac (t, &j); // Now (i,j) are the integer coordinates of the texel to the // immediate "upper left" of the lookup point, and (ifrac,jfrac) are // the amount that the lookup point is actually offset from the // texel center (with (1,1) being all the way to the next texel down // and to the right). } bool TextureSystemImpl::sample_bilinear (int nsamples, const float *s_, const float *t_, int miplevel, TextureFile &texturefile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, const float *weight_, float4 *accum_, float4 *daccumds_, float4 *daccumdt_) { const ImageSpec &spec (texturefile.spec (options.subimage, miplevel)); const ImageCacheFile::LevelInfo &levelinfo (texturefile.levelinfo(options.subimage,miplevel)); TypeDesc::BASETYPE pixeltype = texturefile.pixeltype(options.subimage); wrap_impl swrap_func = wrap_functions[(int)options.swrap]; wrap_impl twrap_func = wrap_functions[(int)options.twrap]; wrap_impl_simd wrap_func = (swrap_func == twrap_func) ? wrap_functions_simd[(int)options.swrap] : NULL; simd::int4 xy (spec.x, spec.y); simd::int4 widthheight (spec.width, spec.height); simd::int4 tilewh (spec.tile_width, spec.tile_height); simd::int4 tilewhmask = tilewh - 1; bool use_fill = (nchannels_result > actualchannels && options.fill); bool tilepow2 = ispow2(spec.tile_width) && ispow2(spec.tile_height); size_t channelsize = texturefile.channelsize(options.subimage); int firstchannel = options.firstchannel; int tile_chbegin = 0, tile_chend = spec.nchannels; // need_pole: do we potentially need to fade to special pole color? // If we do, can't restrict channel range or fade_to_pole won't work. bool need_pole = (options.envlayout == LayoutLatLong && levelinfo.onetile); if (spec.nchannels > m_max_tile_channels && !need_pole) { // For files with many channels, narrow the range we cache tile_chbegin = options.firstchannel; tile_chend = options.firstchannel+actualchannels; } TileID id (texturefile, options.subimage, miplevel, 0, 0, 0, tile_chbegin, tile_chend); float nonfill = 0.0f; // The degree to which we DON'T need fill // N.B. What's up with "nofill"? We need to consider fill only when we // are inside the valid texture region. Outside, i.e. in the black wrap // region, black takes precedence over fill. By keeping track of when // we don't need to worry about fill, which is the comparitively rare // case, we do a lot less math and have fewer rounding errors. float4 accum, daccumds, daccumdt; accum.clear(); if (daccumds_) { daccumds.clear(); daccumdt.clear(); } float4 s_simd, t_simd; int4 sint_simd, tint_simd; float4 sfrac_simd, tfrac_simd; for (int sample = 0; sample < nsamples; ++sample) { // To utilize SIMD ops in an inherently scalar loop, every fourth // step, we compute the st_to_texel for the next four samples. int sample4 = sample & 3; if (sample4 == 0) { s_simd.load (s_ + sample); t_simd.load (t_ + sample); st_to_texel_simd (s_simd, t_simd, texturefile, spec, sint_simd, tint_simd, sfrac_simd, tfrac_simd); } int sint = sint_simd[sample4], tint = tint_simd[sample4]; float sfrac = sfrac_simd[sample4], tfrac = tfrac_simd[sample4]; float weight = weight_[sample]; // SIMD-ize the indices. We have four texels, fit them into one SIMD // 4-vector as S0,S1,T0,T1. enum { S0=0, S1=1, T0=2, T1=3 }; simd::int4 sttex (sint, sint+1, tint, tint+1); // Texel coords: s0,s1,t0,t1 simd::mask4 stvalid; if (wrap_func) { // Both directions use the same wrap function, call in parallel. stvalid = wrap_func (sttex, xy, widthheight); } else { stvalid.load (swrap_func (sttex[S0], spec.x, spec.width), swrap_func (sttex[S1], spec.x, spec.width), twrap_func (sttex[T0], spec.y, spec.height), twrap_func (sttex[T1], spec.y, spec.height)); } // Account for crop windows if (! levelinfo.full_pixel_range) { stvalid &= (sttex >= xy) & (sttex < (xy + widthheight)); } if (none (stvalid)) { nonfill += weight; continue; // All texels we need were out of range and using 'black' wrap } simd::float4 texel_simd[2][2]; simd::int4 tile_st = (simd::int4(simd::shuffle(sttex)) - xy); if (tilepow2) tile_st &= tilewhmask; else tile_st %= tilewh; bool s_onetile = (tile_st[S0] != tilewhmask[S0]) & (sttex[S0]+1 == sttex[S1]); bool t_onetile = (tile_st[T0] != tilewhmask[T0]) & (sttex[T0]+1 == sttex[T1]); bool onetile = (s_onetile & t_onetile); if (onetile && all(stvalid)) { // Shortcut if all the texels we need are on the same tile id.xy (sttex[S0] - tile_st[S0], sttex[T0] - tile_st[T0]); bool ok = find_tile (id, thread_info); if (! ok) error ("%s", m_imagecache->geterror()); TileRef &tile (thread_info->tile); if (! tile->valid()) return false; int pixelsize = tile->pixelsize(); int offset = pixelsize * (tile_st[T0] * spec.tile_width + tile_st[S0]); const unsigned char *p = tile->bytedata() + offset + channelsize * (firstchannel - id.chbegin()); if (pixeltype == TypeDesc::UINT8) { texel_simd[0][0] = uchar2float4 (p); texel_simd[0][1] = uchar2float4 (p+pixelsize); p += pixelsize * spec.tile_width; texel_simd[1][0] = uchar2float4 (p); texel_simd[1][1] = uchar2float4 (p+pixelsize); } else if (pixeltype == TypeDesc::UINT16) { texel_simd[0][0] = ushort2float4 ((uint16_t *)p); texel_simd[0][1] = ushort2float4 ((uint16_t *)(p+pixelsize)); p += pixelsize * spec.tile_width; texel_simd[1][0] = ushort2float4 ((uint16_t *)p); texel_simd[1][1] = ushort2float4 ((uint16_t *)(p+pixelsize)); } else if (pixeltype == TypeDesc::HALF) { texel_simd[0][0] = half2float4 ((half *)p); texel_simd[0][1] = half2float4 ((half *)(p+pixelsize)); p += pixelsize * spec.tile_width; texel_simd[1][0] = half2float4 ((half *)p); texel_simd[1][1] = half2float4 ((half *)(p+pixelsize)); } else { DASSERT (pixeltype == TypeDesc::FLOAT); texel_simd[0][0].load ((const float *)p); texel_simd[0][1].load ((const float *)(p+pixelsize)); p += pixelsize * spec.tile_width; texel_simd[1][0].load ((const float *)p); texel_simd[1][1].load ((const float *)(p+pixelsize)); } } else { bool noreusetile = (options.swrap == TextureOpt::WrapMirror); simd::int4 tile_st = (sttex - xy) % tilewh; simd::int4 tile_edge = sttex - tile_st; for (int j = 0; j < 2; ++j) { if (! stvalid[T0+j]) { texel_simd[j][0].clear(); texel_simd[j][1].clear(); continue; } int tile_t = tile_st[T0+j]; for (int i = 0; i < 2; ++i) { if (! stvalid[S0+i]) { texel_simd[j][i].clear(); continue; } int tile_s = tile_st[S0+i]; // Trick: we only need to find a tile if i == 0 or if we // just crossed a tile bouncary (if tile_s == 0). // Otherwise, we are still on the same tile as the last // iteration, as long as we aren't using mirror wrap mode! if (i == 0 || tile_s == 0 || noreusetile) { id.xy (tile_edge[S0+i], tile_edge[T0+j]); bool ok = find_tile (id, thread_info); if (! ok) error ("%s", m_imagecache->geterror()); if (! thread_info->tile->valid()) { return false; } DASSERT (thread_info->tile->id() == id); } TileRef &tile (thread_info->tile); int pixelsize = tile->pixelsize(); int offset = pixelsize * (tile_t * spec.tile_width + tile_s); offset += (firstchannel - id.chbegin()) * channelsize; DASSERT (offset < spec.tile_width*spec.tile_height*spec.tile_depth*pixelsize); if (pixeltype == TypeDesc::UINT8) texel_simd[j][i] = uchar2float4 ((const unsigned char *)(tile->bytedata() + offset)); else if (pixeltype == TypeDesc::UINT16) texel_simd[j][i] = ushort2float4 ((const unsigned short *)(tile->bytedata() + offset)); else if (pixeltype == TypeDesc::HALF) texel_simd[j][i] = half2float4 ((const half *)(tile->bytedata() + offset)); else { DASSERT (pixeltype == TypeDesc::FLOAT); texel_simd[j][i].load ((const float *)(tile->bytedata() + offset)); } } } } // When we're on the lowest res mipmap levels, it's more pleasing if // we converge to a single pole color right at the pole. Fade to // the average color over the texel height right next to the pole. if (need_pole) { float height = spec.height; if (texturefile.m_sample_border) height -= 1.0f; float tt = t_[sample] * height; if (tt < 1.0f || tt > (height-1.0f)) fade_to_pole (tt, (float *)&accum, weight, texturefile, thread_info, levelinfo, options, miplevel, actualchannels); } simd::float4 weight_simd = weight; accum += weight_simd * bilerp(texel_simd[0][0], texel_simd[0][1], texel_simd[1][0], texel_simd[1][1], sfrac, tfrac); if (daccumds_) { simd::float4 scalex = weight_simd * float(spec.width); simd::float4 scaley = weight_simd * float(spec.height); daccumds += scalex * lerp (texel_simd[0][1] - texel_simd[0][0], texel_simd[1][1] - texel_simd[1][0], tfrac); daccumdt += scaley * lerp (texel_simd[1][0] - texel_simd[0][0], texel_simd[1][1] - texel_simd[0][1], sfrac); } if (use_fill && ! all (stvalid)) { // Compute appropriate amount of "fill" color to extra channels in // non-"black"-wrapped regions. float f = bilerp (float(stvalid[S0]*stvalid[T0]), float(stvalid[S1]*stvalid[T0]), float(stvalid[S0]*stvalid[T1]), float(stvalid[S1]*stvalid[T1]), sfrac, tfrac); nonfill += (1.0f - f) * weight; } } simd::mask4 channel_mask = channel_masks[actualchannels]; accum = blend0(accum, channel_mask); if (use_fill) { // Add the weighted fill color accum += blend0not(float4((1.0f - nonfill) * options.fill), channel_mask); } *accum_ = accum; if (daccumds_) { *daccumds_ = blend0(daccumds, channel_mask); *daccumdt_ = blend0(daccumdt, channel_mask); } return true; } namespace { // Evaluate Bspline weights for both value and derivatives (if dw is not // NULL) into w[0..3] and dw[0..3]. This is the canonical version for // reference, but we don't actually call it, instead favoring the much // harder to read SIMD versions below. template inline void evalBSplineWeights_and_derivs (T *w, T fraction, T *dw = NULL) { T one_frac = 1.0 - fraction; w[0] = T(1.0 / 6.0) * one_frac * one_frac * one_frac; w[1] = T(2.0 / 3.0) - T(0.5) * fraction * fraction * (T(2.0) - fraction); w[2] = T(2.0 / 3.0) - T(0.5) * one_frac * one_frac * (T(2.0) - one_frac); w[3] = T(1.0 / 6.0) * fraction * fraction * fraction; if (dw) { dw[0] = T(-0.5) * one_frac * one_frac; dw[1] = T( 0.5) * fraction * (T(3.0) * fraction - T(4.0)); dw[2] = T(-0.5) * one_frac * (T(3.0) * one_frac - T(4.0)); dw[3] = T( 0.5) * fraction * fraction; } } // Evaluate the 4 Bspline weights (no derivs), returing them as a float4. // The fraction also comes in as a float4 (assuming the same value in all 4 // slots). inline float4 evalBSplineWeights (const float4& fraction) { #if 0 // Version that's easy to read and understand: float one_frac = 1.0f - fraction; float4 w; w[0] = 0.0f + (1.0f / 6.0f) * one_frac * one_frac * one_frac; w[1] = (2.0f / 3.0f) + (-0.5f) * fraction * fraction * (2.0f - fraction); w[2] = (2.0f / 3.0f) + (-0.5f) * one_frac * one_frac * (2.0f - one_frac); w[3] = 0.0f + (1.0f / 6.0f) * fraction * fraction * fraction; return w; #else // Not as clear, but fastest version I've been able to achieve: OIIO_SIMD_FLOAT4_CONST4 (A, 0.0f, 2.0f/3.0f, 2.0f/3.0f, 0.0f); OIIO_SIMD_FLOAT4_CONST4 (B, 1.0f / 6.0f, -0.5f, -0.5f, 1.0f / 6.0f); OIIO_SIMD_FLOAT4_CONST4 (om1m1o, 1.0f, -1.0f, -1.0f, 1.0f); OIIO_SIMD_FLOAT4_CONST4 (z22z, 0.0f, 2.0f, 2.0f, 0.0f); simd::float4 one_frac = float4::One() - fraction; simd::float4 ofof = AxBxAyBy (one_frac, fraction); // 1-frac, frac, 1-frac, frac simd::float4 C = (*(float4*)&om1m1o) * ofof + (*(float4*)&z22z); return (*(float4*)&A) + (*(float4*)&B) * ofof * ofof * C; #endif } // Evaluate Bspline weights for both value and derivatives (if dw is not // NULL), returning the 4 coefficients for each as float4's. inline void evalBSplineWeights_and_derivs (simd::float4 *w, float fraction, simd::float4 *dw = NULL) { #if 0 // Version that's easy to read and understand: float one_frac = 1.0f - fraction; (*w)[0] = 0.0f + (1.0f / 6.0f) * one_frac * one_frac * one_frac; (*w)[1] = (2.0f / 3.0f) + (-0.5f) * fraction * fraction * (2.0f - fraction); (*w)[2] = (2.0f / 3.0f) + (-0.5f) * one_frac * one_frac * (2.0f - one_frac); (*w)[3] = 0.0f + (1.0f / 6.0f) * fraction * fraction * fraction; if (dw) { (*dw)[0] = -0.5f * one_frac * (1.0f * one_frac - 0.0f); (*dw)[1] = 0.5f * fraction * (3.0f * fraction - 4.0f); (*dw)[2] = -0.5f * one_frac * (3.0f * one_frac - 4.0f); (*dw)[3] = 0.5f * fraction * (1.0f * fraction - 0.0f); } #else // Not as clear, but fastest version I've been able to achieve: OIIO_SIMD_FLOAT4_CONST4 (A, 0.0f, 2.0f/3.0f, 2.0f/3.0f, 0.0f); OIIO_SIMD_FLOAT4_CONST4 (B, 1.0f / 6.0f, -0.5f, -0.5f, 1.0f / 6.0f); float one_frac = 1.0f - fraction; simd::float4 ofof (one_frac, fraction, one_frac, fraction); simd::float4 C (one_frac, 2.0f-fraction, 2.0f-one_frac, fraction); *w = (*(float4*)&A) + (*(float4*)&B) * ofof * ofof * C; if (dw) { const simd::float4 D (-0.5f, 0.5f, -0.5f, 0.5f); const simd::float4 E (1.0f, 3.0f, 3.0f, 1.0f); const simd::float4 F (0.0f, 4.0f, 4.0f, 0.0f); *dw = D * ofof * (E * ofof - F); } #endif } } // anonymous namespace bool TextureSystemImpl::sample_bicubic (int nsamples, const float *s_, const float *t_, int miplevel, TextureFile &texturefile, PerThreadInfo *thread_info, TextureOpt &options, int nchannels_result, int actualchannels, const float *weight_, float4 *accum_, float4 *daccumds_, float4 *daccumdt_) { const ImageSpec &spec (texturefile.spec (options.subimage, miplevel)); const ImageCacheFile::LevelInfo &levelinfo (texturefile.levelinfo(options.subimage,miplevel)); TypeDesc::BASETYPE pixeltype = texturefile.pixeltype(options.subimage); wrap_impl_simd swrap_func_simd = wrap_functions_simd[(int)options.swrap]; wrap_impl_simd twrap_func_simd = wrap_functions_simd[(int)options.twrap]; int4 spec_x_simd (spec.x); int4 spec_y_simd (spec.y); int4 spec_width_simd (spec.width); int4 spec_height_simd (spec.height); int4 spec_x_plus_width_simd = spec_x_simd + spec_width_simd; int4 spec_y_plus_height_simd = spec_y_simd + spec_height_simd; bool use_fill = (nchannels_result > actualchannels && options.fill); bool tilepow2 = ispow2(spec.tile_width) && ispow2(spec.tile_height); int tilewidthmask = spec.tile_width - 1; // e.g. 63 int tileheightmask = spec.tile_height - 1; size_t channelsize = texturefile.channelsize(options.subimage); int firstchannel = options.firstchannel; float nonfill = 0.0f; // The degree to which we DON'T need fill // N.B. What's up with "nofill"? We need to consider fill only when we // are inside the valid texture region. Outside, i.e. in the black wrap // region, black takes precedence over fill. By keeping track of when // we don't need to worry about fill, which is the comparitively rare // case, we do a lot less math and have fewer rounding errors. // need_pole: do we potentially need to fade to special pole color? // If we do, can't restrict channel range or fade_to_pole won't work. bool need_pole = (options.envlayout == LayoutLatLong && levelinfo.onetile); int tile_chbegin = 0, tile_chend = spec.nchannels; if (spec.nchannels > m_max_tile_channels) { // For files with many channels, narrow the range we cache tile_chbegin = options.firstchannel; tile_chend = options.firstchannel+actualchannels; } TileID id (texturefile, options.subimage, miplevel, 0, 0, 0, tile_chbegin, tile_chend); size_t pixelsize = channelsize * id.nchannels(); size_t firstchannel_offset_bytes = channelsize * (firstchannel - id.chbegin()); float4 accum, daccumds, daccumdt; accum.clear(); if (daccumds_) { daccumds.clear(); daccumdt.clear(); } float4 s_simd, t_simd; int4 sint_simd, tint_simd; float4 sfrac_simd, tfrac_simd; for (int sample = 0; sample < nsamples; ++sample) { // To utilize SIMD ops in an inherently scalar loop, every fourth // step, we compute the st_to_texel for the next four samples. int sample4 = sample & 3; if (sample4 == 0) { s_simd.load (s_ + sample); t_simd.load (t_ + sample); st_to_texel_simd (s_simd, t_simd, texturefile, spec, sint_simd, tint_simd, sfrac_simd, tfrac_simd); } int sint = sint_simd[sample4], tint = tint_simd[sample4]; float sfrac = sfrac_simd[sample4], tfrac = tfrac_simd[sample4]; float weight = weight_[sample]; // We're gathering 4x4 samples and 4x weights. Indices: texels 0, // 1, 2, 3. The sample lies between samples 1 and 2. static const OIIO_SIMD4_ALIGN int iota[4] = { 0, 1, 2, 3 }; static const OIIO_SIMD4_ALIGN int iota_1[4] = { -1, 0, 1, 2 }; simd::int4 stex, ttex; // Texel coords for each row and column stex = sint + (*(int4 *)iota_1); ttex = tint + (*(int4 *)iota_1); simd::mask4 svalid = swrap_func_simd (stex, spec_x_simd, spec_width_simd); simd::mask4 tvalid = twrap_func_simd (ttex, spec_y_simd, spec_height_simd); bool allvalid = reduce_and(svalid & tvalid); bool anyvalid = reduce_or (svalid | tvalid); if (! levelinfo.full_pixel_range && anyvalid) { // Handle case of crop windows or overscan svalid &= (stex >= spec_x_simd) & (stex < spec_x_plus_width_simd); tvalid &= (ttex >= spec_y_simd) & (ttex < spec_y_plus_height_simd); allvalid = reduce_and(svalid & tvalid); anyvalid = reduce_or (svalid | tvalid); } if (! anyvalid) { // All texels we need were out of range and using 'black' wrap. nonfill += weight; continue; } simd::float4 texel_simd[4][4]; // int tile_s = (stex[0] - spec.x) % spec.tile_width; // int tile_t = (ttex[0] - spec.y) % spec.tile_height; int tile_s = (stex[0] - spec.x); int tile_t = (ttex[0] - spec.y); if (tilepow2) { tile_s &= tilewidthmask; tile_t &= tileheightmask; } else { tile_s %= spec.tile_width; tile_t %= spec.tile_height; } bool s_onetile = (tile_s <= tilewidthmask-3); bool t_onetile = (tile_t <= tileheightmask-3); if (s_onetile & t_onetile) { // If we thought it was one tile, realize that it isn't unless // it's ascending. s_onetile &= all (stex == (simd::shuffle<0>(stex)+(*(int4 *)iota))); t_onetile &= all (ttex == (simd::shuffle<0>(ttex)+(*(int4 *)iota))); } bool onetile = (s_onetile & t_onetile); if (onetile & allvalid) { // Shortcut if all the texels we need are on the same tile id.xy (stex[0] - tile_s, ttex[0] - tile_t); bool ok = find_tile (id, thread_info); if (! ok) error ("%s", m_imagecache->geterror()); TileRef &tile (thread_info->tile); if (! tile) { return false; } // N.B. thread_info->tile will keep holding a ref-counted pointer // to the tile for the duration that we're using the tile data. int offset = pixelsize * (tile_t * spec.tile_width + tile_s); const unsigned char *base = tile->bytedata() + offset + firstchannel_offset_bytes; DASSERT (tile->data()); if (pixeltype == TypeDesc::UINT8) { for (int j = 0, j_offset = 0; j < 4; ++j, j_offset += pixelsize*spec.tile_width) for (int i = 0, i_offset = j_offset; i < 4; ++i, i_offset += pixelsize) texel_simd[j][i] = uchar2float4 (base + i_offset); } else if (pixeltype == TypeDesc::UINT16) { for (int j = 0, j_offset = 0; j < 4; ++j, j_offset += pixelsize*spec.tile_width) for (int i = 0, i_offset = j_offset; i < 4; ++i, i_offset += pixelsize) texel_simd[j][i] = ushort2float4 ((const uint16_t *)(base + i_offset)); } else if (pixeltype == TypeDesc::HALF) { for (int j = 0, j_offset = 0; j < 4; ++j, j_offset += pixelsize*spec.tile_width) for (int i = 0, i_offset = j_offset; i < 4; ++i, i_offset += pixelsize) texel_simd[j][i] = half2float4 ((const half *)(base + i_offset)); } else { for (int j = 0, j_offset = 0; j < 4; ++j, j_offset += pixelsize*spec.tile_width) for (int i = 0, i_offset = j_offset; i < 4; ++i, i_offset += pixelsize) texel_simd[j][i].load ((const float *)(base + i_offset)); } } else { simd::int4 tile_s, tile_t; // texel offset WITHIN its tile simd::int4 tile_s_edge, tile_t_edge; // coordinate of the tile edge tile_s = (stex - spec_x_simd) % spec.tile_width; tile_t = (ttex - spec_y_simd) % spec.tile_height; tile_s_edge = stex - tile_s; tile_t_edge = ttex - tile_t; simd::int4 column_offset_bytes = tile_s * pixelsize + firstchannel_offset_bytes; for (int j = 0; j < 4; ++j) { if (! tvalid[j]) { for (int i = 0; i < 4; ++i) texel_simd[j][i].clear(); continue; } int row_offset_bytes = tile_t[j] * (spec.tile_width * pixelsize); for (int i = 0; i < 4; ++i) { if (! svalid[i]) { texel_simd[j][i].clear(); continue; } // Trick: we only need to find a tile if i == 0 or if we // just crossed a tile boundary (if tile_s[i] == 0). // Otherwise, we are still on the same tile as the last // iteration, as long as we aren't using mirror wrap mode! if (i == 0 || tile_s[i] == 0 || options.swrap == TextureOpt::WrapMirror) { id.xy (tile_s_edge[i], tile_t_edge[j]); bool ok = find_tile (id, thread_info); if (! ok) error ("%s", m_imagecache->geterror()); DASSERT (thread_info->tile->id() == id); if (! thread_info->tile->valid()) return false; } TileRef &tile (thread_info->tile); DASSERT (tile->data()); int offset = row_offset_bytes + column_offset_bytes[i]; // const unsigned char *pixelptr = tile->bytedata() + offset[i]; if (pixeltype == TypeDesc::UINT8) texel_simd[j][i] = uchar2float4 (tile->bytedata() + offset); else if (pixeltype == TypeDesc::UINT16) texel_simd[j][i] = ushort2float4 ((const uint16_t *)(tile->bytedata() + offset)); else if (pixeltype == TypeDesc::HALF) texel_simd[j][i] = half2float4 ((const half *)(tile->bytedata() + offset)); else texel_simd[j][i].load ((const float *)(tile->bytedata() + offset)); } } } // When we're on the lowest res mipmap levels, it's more pleasing if // we converge to a single pole color right at the pole. Fade to // the average color over the texel height right next to the pole. if (need_pole) { float height = spec.height; if (texturefile.m_sample_border) height -= 1.0f; float tt = t_[sample] * height; if (tt < 1.0f || tt > (height-1.0f)) fade_to_pole (tt, (float *)&accum, weight, texturefile, thread_info, levelinfo, options, miplevel, actualchannels); } // We use a formulation of cubic B-spline evaluation that reduces to // lerps. It's tricky to follow, but the references are: // * Ruijters, Daniel et al, "Efficient GPU-Based Texture // Interpolation using Uniform B-Splines", Journal of Graphics // Tools 13(4), pp. 61-69, 2008. // http://jgt.akpeters.com/papers/RuijtersEtAl08/ // * Sigg, Christian and Markus Hadwiger, "Fast Third-Order Texture // Filtering", in GPU Gems 2 (Chapter 20), Pharr and Fernando, ed. // http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter20.html // We like this formulation because it's slightly faster than any of // the other B-spline evaluation routines we tried, and also the lerp // guarantees that the filtered results will be non-negative for // non-negative texel values (which we had trouble with before due to // numerical imprecision). float4 wx, dwx; float4 wy, dwy; if (daccumds_) { evalBSplineWeights_and_derivs (&wx, sfrac, &dwx); evalBSplineWeights_and_derivs (&wy, tfrac, &dwy); } else { wx = evalBSplineWeights (float4(sfrac)); wy = evalBSplineWeights (float4(tfrac)); #if (defined(__i386__) && !defined(__x86_64__)) || defined(__aarch64__) // Some platforms complain here about these being uninitialized, // so initialize them. Don't waste the cycles for platforms that // don't seem to have that error. It's a false positive -- this // code really is safe without the initialization, but that // doesn't seem to get figured out on some platforms (even when // running the same gcc version). dwx = float4::Zero(); dwy = float4::Zero(); #endif } // figure out lerp weights so we can turn the filter into a sequence of lerp's // Here's the obvious scalar code: // float g0x = wx[0] + wx[1]; float h0x = (wx[1] / g0x); // float g1x = wx[2] + wx[3]; float h1x = (wx[3] / g1x); // float g0y = wy[0] + wy[1]; float h0y = (wy[1] / g0y); // float g1y = wy[2] + wy[3]; float h1y = (wy[3] / g1y); // But instead, we convolutedly (but quickly!) compute the four g // and four h values using SIMD ops: float4 wx_0213 = simd::shuffle<0,2,1,3>(wx); float4 wx_1302 = simd::shuffle<1,3,0,2>(wx); float4 wx_01_23_01_23 = wx_0213 + wx_1302; // wx[0]+wx[1] wx[2]+wx[3] .. float4 wy_0213 = simd::shuffle<0,2,1,3>(wy); float4 wy_1302 = simd::shuffle<1,3,0,2>(wy); float4 wy_01_23_01_23 = wy_0213 + wy_1302; // wy[0]+wy[1] wy[2]+wy[3] .. float4 g = AxyBxy (wx_01_23_01_23, wy_01_23_01_23); // wx[0]+wx[1] wx[2]+wx[3] wy[0]+wy[1] wy[2]+wy[3] float4 wx13_wy13 = AxyBxy (wx_1302, wy_1302); float4 h = wx13_wy13 / g; // [ h0x h1x h0y h1y ] simd::float4 col[4]; for (int j = 0; j < 4; ++j) { simd::float4 lx = lerp (texel_simd[j][0], texel_simd[j][1], shuffle<0>(h) /*h0x*/); simd::float4 rx = lerp (texel_simd[j][2], texel_simd[j][3], shuffle<1>(h) /*h1x*/); col[j] = lerp (lx, rx, shuffle<1>(g) /*g1x*/); } simd::float4 ly = lerp (col[0], col[1], shuffle<2>(h) /*h0y*/); simd::float4 ry = lerp (col[2], col[3], shuffle<3>(h) /*h1y*/); simd::float4 weight_simd = weight; accum += weight_simd * lerp (ly, ry, shuffle<3>(g) /*g1y*/); if (daccumds_) { simd::float4 scalex = weight_simd * float(spec.width); simd::float4 scaley = weight_simd * float(spec.height); daccumds += scalex * ( dwx[0] * (wy[0] * texel_simd[0][0] + wy[1] * texel_simd[1][0] + wy[2] * texel_simd[2][0] + wy[3] * texel_simd[3][0]) + dwx[1] * (wy[0] * texel_simd[0][1] + wy[1] * texel_simd[1][1] + wy[2] * texel_simd[2][1] + wy[3] * texel_simd[3][1]) + dwx[2] * (wy[0] * texel_simd[0][2] + wy[1] * texel_simd[1][2] + wy[2] * texel_simd[2][2] + wy[3] * texel_simd[3][2]) + dwx[3] * (wy[0] * texel_simd[0][3] + wy[1] * texel_simd[1][3] + wy[2] * texel_simd[2][3] + wy[3] * texel_simd[3][3]) ); daccumdt += scaley * ( dwy[0] * (wx[0] * texel_simd[0][0] + wx[1] * texel_simd[0][1] + wx[2] * texel_simd[0][2] + wx[3] * texel_simd[0][3]) + dwy[1] * (wx[0] * texel_simd[1][0] + wx[1] * texel_simd[1][1] + wx[2] * texel_simd[1][2] + wx[3] * texel_simd[1][3]) + dwy[2] * (wx[0] * texel_simd[2][0] + wx[1] * texel_simd[2][1] + wx[2] * texel_simd[2][2] + wx[3] * texel_simd[2][3]) + dwy[3] * (wx[0] * texel_simd[3][0] + wx[1] * texel_simd[3][1] + wx[2] * texel_simd[3][2] + wx[3] * texel_simd[3][3]) ); } // Compute appropriate amount of "fill" color to extra channels in // non-"black"-wrapped regions. if (!allvalid && use_fill) { float col[4]; for (int j = 0; j < 4; ++j) { float lx = lerp (1.0f*tvalid[j]*svalid[0], 1.0f*tvalid[j]*svalid[1], extract<0>(h) /*h0x*/); float rx = lerp (1.0f*tvalid[j]*svalid[2], 1.0f*tvalid[j]*svalid[3], extract<1>(h) /*h1x*/); col[j] = lerp (lx, rx, extract<1>(g) /*g1x*/); } float ly = lerp (col[0], col[1], extract<2>(h) /*h0y*/); float ry = lerp (col[2], col[3], extract<3>(h) /*h1y*/); nonfill += weight * (1.0f - lerp (ly, ry, extract<3>(g) /*g1y*/)); } } simd::mask4 channel_mask = channel_masks[actualchannels]; accum = blend0(accum, channel_mask); if (use_fill) { // Add the weighted fill color accum += blend0not(float4((1.0f - nonfill) * options.fill), channel_mask); } *accum_ = accum; if (daccumds_) { *daccumds_ = blend0(daccumds, channel_mask); *daccumdt_ = blend0(daccumdt, channel_mask); } return true; } void TextureSystemImpl::visualize_ellipse (const std::string &name, float dsdx, float dtdx, float dsdy, float dtdy, float sblur, float tblur) { std::cout << name << " derivs dx " << dsdx << ' ' << dtdx << ", dt " << dtdx << ' ' << dtdy << "\n"; adjust_width (dsdx, dtdx, dsdy, dtdy, 1.0f, 1.0f); float majorlength, minorlength, theta; float ABCF[4]; ellipse_axes (dsdx, dtdx, dsdy, dtdy, majorlength, minorlength, theta, ABCF); std::cout << " ellipse major " << majorlength << ", minor " << minorlength << ", theta " << theta << "\n"; adjust_blur (majorlength, minorlength, theta, sblur, tblur); std::cout << " post " << sblur << ' ' << tblur << " blur: major " << majorlength << ", minor " << minorlength << "\n\n"; TextureOpt options; float trueaspect; float aspect = TextureSystemImpl::anisotropic_aspect (majorlength, minorlength, options, trueaspect); float *lineweight = ALLOCA (float, 2*options.anisotropic); float smajor, tmajor, invsamples; int nsamples = compute_ellipse_sampling (aspect, theta, majorlength, minorlength, smajor, tmajor, invsamples, lineweight); // Make an ImageBuf to hold our visualization image, set it to grey float scale = 100; int w = 256, h = 256; ImageSpec spec (w, h, 3); ImageBuf ib (spec); static float dark[3] = { 0.2f, 0.2f, 0.2f }; static float white[3] = { 1, 1, 1 }; static float grey[3] = { 0.5, 0.5, 0.5 }; static float red[3] = { 1, 0, 0 }; static float green[3] = { 0, 1, 0 }; ImageBufAlgo::fill (ib, grey); // scan all the pixels, darken the ellipse interior for (int j = 0; j < h; ++j) { float y = (j-h/2)/scale; for (int i = 0; i < w; ++i) { float x = (i-w/2)/scale; float d2 = ABCF[0]*x*x + ABCF[1]*x*y + ABCF[2]*y*y; if (d2 < 1.0f) ib.setpixel (i, h-1-j, dark); } } // Draw red and green axes for the dx and dy derivatives, respectively for (int i = 0, e = std::max(fabsf(dsdx),fabsf(dtdx))*scale; i < e; ++i) ib.setpixel (w/2+int(float(i)/e*dsdx*scale), h/2-int(float(i)/e*dtdx*scale), red); for (int i = 0, e = std::max(fabsf(dsdy),fabsf(dtdy))*scale; i < e; ++i) ib.setpixel (w/2+int(float(i)/e*dsdy*scale), h/2-int(float(i)/e*dtdy*scale), green); float bigweight = 0; for (int i = 0; i < nsamples; ++i) bigweight = std::max(lineweight[i],bigweight); // Plop white dots at the sample positions for (int sample = 0; sample < nsamples; ++sample) { float pos = 1.0f * (sample + 0.5f) * invsamples - 0.5f; float x = pos*smajor, y = pos*tmajor; int xx = w/2+int(x*scale), yy = h/2-int(y*scale); int size = int (5 * lineweight[sample]/bigweight); ImageBufAlgo::fill (ib, white, ROI(xx-size/2, xx+size/2+1, yy-size/2, yy+size/2+1)); } ib.write (name); } void TextureSystemImpl::unit_test_texture () { float blur = 0; float dsdx, dtdx, dsdy, dtdy; dsdx = 0.4; dtdx = 0.0; dsdy = 0.0; dtdy = 0.2; visualize_ellipse ("0.tif", dsdx, dtdx, dsdy, dtdy, blur, blur); dsdx = 0.2; dtdx = 0.0; dsdy = 0.0; dtdy = 0.4; visualize_ellipse ("1.tif", dsdx, dtdx, dsdy, dtdy, blur, blur); dsdx = 0.2; dtdx = 0.2; dsdy = -0.2; dtdy = 0.2; visualize_ellipse ("2.tif", dsdx, dtdx, dsdy, dtdy, blur, blur); dsdx = 0.35; dtdx = 0.27; dsdy = 0.1; dtdy = 0.35; visualize_ellipse ("3.tif", dsdx, dtdx, dsdy, dtdy, blur, blur); dsdx = 0.35; dtdx = 0.27; dsdy = 0.1; dtdy = -0.35; visualize_ellipse ("4.tif", dsdx, dtdx, dsdy, dtdy, blur, blur); boost::mt19937 rndgen; boost::uniform_01 rnd(rndgen); for (int i = 0; i < 100; ++i) { dsdx = 1.5f*(rnd() - 0.5f); dtdx = 1.5f*(rnd() - 0.5f); dsdy = 1.5f*(rnd() - 0.5f); dtdy = 1.5f*(rnd() - 0.5f); visualize_ellipse (Strutil::format("%d.tif", 100+i), dsdx, dtdx, dsdy, dtdy, blur, blur); } } } // end namespace pvt OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/jpeg.imageio/0000755000175000017500000000000013151711064017452 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/jpeg.imageio/CMakeLists.txt0000644000175000017500000000022313151711064022207 0ustar mfvmfvadd_oiio_plugin (jpeginput.cpp jpegoutput.cpp INCLUDE_DIRS ${JPEG_INCLUDE_DIR} LINK_LIBRARIES ${JPEG_LIBRARIES}) openimageio-1.7.17~dfsg0.orig/src/jpeg.imageio/jpeginput.cpp0000644000175000017500000004142213151711064022166 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Based on BSD-licensed software Copyright 2004 NVIDIA Corp. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include "OpenImageIO/imageio.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/color.h" #include "jpeg_pvt.h" OIIO_PLUGIN_NAMESPACE_BEGIN // N.B. The class definition for JpgInput is in jpeg_pvt.h. // Export version number and create function symbols OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT int jpeg_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* jpeg_imageio_library_version () { #define STRINGIZE2(a) #a #define STRINGIZE(a) STRINGIZE2(a) #ifdef LIBJPEG_TURBO_VERSION return "jpeg-turbo " STRINGIZE(LIBJPEG_TURBO_VERSION); #else return "jpeglib " STRINGIZE(JPEG_LIB_VERSION_MAJOR) "." STRINGIZE(JPEG_LIB_VERSION_MINOR); #endif } OIIO_EXPORT ImageInput *jpeg_input_imageio_create () { return new JpgInput; } OIIO_EXPORT const char *jpeg_input_extensions[] = { "jpg", "jpe", "jpeg", "jif", "jfif", "jfi", NULL }; OIIO_PLUGIN_EXPORTS_END static const uint8_t JPEG_MAGIC1 = 0xff; static const uint8_t JPEG_MAGIC2 = 0xd8; // For explanations of the error handling, see the "example.c" in the // libjpeg distribution. static void my_error_exit (j_common_ptr cinfo) { /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */ JpgInput::my_error_ptr myerr = (JpgInput::my_error_ptr) cinfo->err; /* Always display the message. */ /* We could postpone this until after returning, if we chose. */ // (*cinfo->err->output_message) (cinfo); myerr->jpginput->jpegerror (myerr, true); /* Return control to the setjmp point */ longjmp(myerr->setjmp_buffer, 1); } static void my_output_message (j_common_ptr cinfo) { JpgInput::my_error_ptr myerr = (JpgInput::my_error_ptr) cinfo->err; myerr->jpginput->jpegerror (myerr, true); } static std::string comp_info_to_attr (const jpeg_decompress_struct &cinfo) { // Compare the current 6 samples with our known definitions // to determine the corresponding subsampling attr std::vector comp; comp.push_back(cinfo.comp_info[0].h_samp_factor); comp.push_back(cinfo.comp_info[0].v_samp_factor); comp.push_back(cinfo.comp_info[1].h_samp_factor); comp.push_back(cinfo.comp_info[1].v_samp_factor); comp.push_back(cinfo.comp_info[2].h_samp_factor); comp.push_back(cinfo.comp_info[2].v_samp_factor); size_t size = comp.size(); if (std::equal(JPEG_444_COMP, JPEG_444_COMP+size, comp.begin())) return JPEG_444_STR; else if (std::equal(JPEG_422_COMP, JPEG_422_COMP+size, comp.begin())) return JPEG_422_STR; else if (std::equal(JPEG_420_COMP, JPEG_420_COMP+size, comp.begin())) return JPEG_420_STR; else if (std::equal(JPEG_411_COMP, JPEG_411_COMP+size, comp.begin())) return JPEG_411_STR; return ""; } void JpgInput::jpegerror (my_error_ptr myerr, bool fatal) { // Send the error message to the ImageInput char errbuf[JMSG_LENGTH_MAX]; (*m_cinfo.err->format_message) ((j_common_ptr)&m_cinfo, errbuf); error ("JPEG error: %s (\"%s\")", errbuf, filename().c_str()); // Shut it down and clean it up if (fatal) { m_fatalerr = true; close (); m_fatalerr = true; // because close() will reset it } } bool JpgInput::valid_file (const std::string &filename) const { FILE *fd = Filesystem::fopen (filename, "rb"); if (! fd) return false; // Check magic number to assure this is a JPEG file uint8_t magic[2] = {0, 0}; bool ok = (fread (magic, sizeof(magic), 1, fd) == 1); fclose (fd); if (magic[0] != JPEG_MAGIC1 || magic[1] != JPEG_MAGIC2) { ok = false; } return ok; } bool JpgInput::open (const std::string &name, ImageSpec &newspec, const ImageSpec &config) { const ImageIOParameter *p = config.find_attribute ("_jpeg:raw", TypeDesc::TypeInt); m_raw = p && *(int *)p->data(); return open (name, newspec); } bool JpgInput::open (const std::string &name, ImageSpec &newspec) { // Check that file exists and can be opened m_filename = name; m_fd = Filesystem::fopen (name, "rb"); if (m_fd == NULL) { error ("Could not open file \"%s\"", name.c_str()); return false; } // Check magic number to assure this is a JPEG file uint8_t magic[2] = {0, 0}; if (fread (magic, sizeof(magic), 1, m_fd) != 1) { error ("Empty file \"%s\"", name.c_str()); close_file (); return false; } rewind (m_fd); if (magic[0] != JPEG_MAGIC1 || magic[1] != JPEG_MAGIC2) { close_file (); error ("\"%s\" is not a JPEG file, magic number doesn't match (was 0x%x%x)", name.c_str(), int(magic[0]), int(magic[1])); return false; } // Set up the normal JPEG error routines, then override error_exit and // output_message so we intercept all the errors. m_cinfo.err = jpeg_std_error ((jpeg_error_mgr *)&m_jerr); m_jerr.pub.error_exit = my_error_exit; m_jerr.pub.output_message = my_output_message; if (setjmp (m_jerr.setjmp_buffer)) { // Jump to here if there's a libjpeg internal error // Prevent memory leaks, see example.c in jpeg distribution jpeg_destroy_decompress (&m_cinfo); close_file (); return false; } jpeg_create_decompress (&m_cinfo); // initialize decompressor jpeg_stdio_src (&m_cinfo, m_fd); // specify the data source // Request saving of EXIF and other special tags for later spelunking for (int mark = 0; mark < 16; ++mark) jpeg_save_markers (&m_cinfo, JPEG_APP0+mark, 0xffff); jpeg_save_markers (&m_cinfo, JPEG_COM, 0xffff); // comment marker // read the file parameters if (jpeg_read_header (&m_cinfo, FALSE) != JPEG_HEADER_OK || m_fatalerr) { error ("Bad JPEG header for \"%s\"", filename().c_str()); return false; } int nchannels = m_cinfo.num_components; if (m_cinfo.jpeg_color_space == JCS_CMYK || m_cinfo.jpeg_color_space == JCS_YCCK) { // CMYK jpegs get converted by us to RGB m_cinfo.out_color_space = JCS_CMYK; // pre-convert YCbCrK->CMYK nchannels = 3; m_cmyk = true; } if (m_raw) m_coeffs = jpeg_read_coefficients (&m_cinfo); else jpeg_start_decompress (&m_cinfo); // start working if (m_fatalerr) return false; m_next_scanline = 0; // next scanline we'll read m_spec = ImageSpec (m_cinfo.output_width, m_cinfo.output_height, nchannels, TypeDesc::UINT8); // Assume JPEG is in sRGB unless the Exif or XMP tags say otherwise. m_spec.attribute ("oiio:ColorSpace", "sRGB"); if (m_cinfo.jpeg_color_space == JCS_CMYK) m_spec.attribute ("jpeg:ColorSpace", "CMYK"); else if (m_cinfo.jpeg_color_space == JCS_YCCK) m_spec.attribute ("jpeg:ColorSpace", "YCbCrK"); // If the chroma subsampling is detected and matches something // we expect, then set an attribute so that it can be preserved // in future operations. std::string subsampling = comp_info_to_attr(m_cinfo); if (!subsampling.empty()) m_spec.attribute(JPEG_SUBSAMPLING_ATTR, subsampling); for (jpeg_saved_marker_ptr m = m_cinfo.marker_list; m; m = m->next) { if (m->marker == (JPEG_APP0+1) && ! strcmp ((const char *)m->data, "Exif")) { // The block starts with "Exif\0\0", so skip 6 bytes to get // to the start of the actual Exif data TIFF directory decode_exif (string_view((char *)m->data+6, m->data_length-6), m_spec); } else if (m->marker == (JPEG_APP0+1) && ! strcmp ((const char *)m->data, "http://ns.adobe.com/xap/1.0/")) { #ifndef NDEBUG std::cerr << "Found APP1 XMP! length " << m->data_length << "\n"; #endif std::string xml ((const char *)m->data, m->data_length); decode_xmp (xml, m_spec); } else if (m->marker == (JPEG_APP0+13) && ! strcmp ((const char *)m->data, "Photoshop 3.0")) jpeg_decode_iptc ((unsigned char *)m->data); else if (m->marker == JPEG_COM) { if (! m_spec.find_attribute ("ImageDescription", TypeDesc::STRING)) m_spec.attribute ("ImageDescription", std::string ((const char *)m->data, m->data_length)); } } // Handle density/pixelaspect. We need to do this AFTER the exif is // decoded, in case it contains useful information. float xdensity = m_spec.get_float_attribute ("XResolution"); float ydensity = m_spec.get_float_attribute ("YResolution"); if (! xdensity || ! ydensity) { xdensity = float(m_cinfo.X_density); ydensity = float(m_cinfo.Y_density); if (xdensity && ydensity) { m_spec.attribute ("XResolution", xdensity); m_spec.attribute ("YResolution", ydensity); } } if (xdensity && ydensity) { float aspect = ydensity/xdensity; if (aspect != 1.0f) m_spec.attribute ("PixelAspectRatio", aspect); switch (m_cinfo.density_unit) { case 0 : m_spec.attribute ("ResolutionUnit", "none"); break; case 1 : m_spec.attribute ("ResolutionUnit", "in"); break; case 2 : m_spec.attribute ("ResolutionUnit", "cm"); break; } } read_icc_profile(&m_cinfo, m_spec); /// try to read icc profile newspec = m_spec; return true; } bool JpgInput::read_icc_profile (j_decompress_ptr cinfo, ImageSpec& spec) { int num_markers = 0; std::vector icc_buf; unsigned int total_length = 0; const int MAX_SEQ_NO = 255; unsigned char marker_present[MAX_SEQ_NO + 1]; // one extra is used to store the flag if marker is found, set to one if marker is found unsigned int data_length[MAX_SEQ_NO + 1]; // store the size of each marker unsigned int data_offset[MAX_SEQ_NO + 1]; // store the offset of each marker memset (marker_present, 0, (MAX_SEQ_NO + 1)); for (jpeg_saved_marker_ptr m = cinfo->marker_list; m; m = m->next) { if (m->marker == (JPEG_APP0 + 2) && !strcmp((const char *)m->data, "ICC_PROFILE")) { if (num_markers == 0) num_markers = GETJOCTET(m->data[13]); else if (num_markers != GETJOCTET(m->data[13])) return false; int seq_no = GETJOCTET(m->data[12]); if (seq_no <= 0 || seq_no > num_markers) return false; if (marker_present[seq_no]) // duplicate marker return false; marker_present[seq_no] = 1; // flag found marker data_length[seq_no] = m->data_length - ICC_HEADER_SIZE; } } if (num_markers == 0) return false; // checking for missing markers for (int seq_no = 1; seq_no <= num_markers; seq_no++){ if (marker_present[seq_no] == 0) return false; // missing sequence number data_offset[seq_no] = total_length; total_length += data_length[seq_no]; } if (total_length == 0) return false; // found only empty markers icc_buf.resize (total_length*sizeof(JOCTET)); // and fill it in for (jpeg_saved_marker_ptr m = cinfo->marker_list; m; m = m->next) { if (m->marker == (JPEG_APP0 + 2) && !strcmp((const char *)m->data, "ICC_PROFILE")) { int seq_no = GETJOCTET(m->data[12]); memcpy (&icc_buf[0] + data_offset[seq_no], m->data + ICC_HEADER_SIZE, data_length[seq_no]); } } spec.attribute(ICC_PROFILE_ATTR, TypeDesc(TypeDesc::UINT8, total_length), &icc_buf[0]); return true; } static void cmyk_to_rgb (int n, const unsigned char *cmyk, size_t cmyk_stride, unsigned char *rgb, size_t rgb_stride) { for ( ; n; --n, cmyk += cmyk_stride, rgb += rgb_stride) { // JPEG seems to store CMYK as 1-x float C = convert_type(cmyk[0]); float M = convert_type(cmyk[1]); float Y = convert_type(cmyk[2]); float K = convert_type(cmyk[3]); float R = C * K; float G = M * K; float B = Y * K; rgb[0] = convert_type(R); rgb[1] = convert_type(G); rgb[2] = convert_type(B); } } bool JpgInput::read_native_scanline (int y, int z, void *data) { if (m_raw) return false; if (y < 0 || y >= (int)m_cinfo.output_height) // out of range scanline return false; if (m_next_scanline > y) { // User is trying to read an earlier scanline than the one we're // up to. Easy fix: close the file and re-open. ImageSpec dummyspec; int subimage = current_subimage(); if (! close () || ! open (m_filename, dummyspec) || ! seek_subimage (subimage, 0, dummyspec)) return false; // Somehow, the re-open failed assert (m_next_scanline == 0 && current_subimage() == subimage); } // Set up our custom error handler if (setjmp (m_jerr.setjmp_buffer)) { // Jump to here if there's a libjpeg internal error return false; } void *readdata = data; if (m_cmyk) { // If the file's data is CMYK, read into a 4-channel buffer, then // we'll have to convert. m_cmyk_buf.resize (m_spec.width * 4); readdata = &m_cmyk_buf[0]; ASSERT (m_spec.nchannels == 3); } for ( ; m_next_scanline <= y; ++m_next_scanline) { // Keep reading until we've read the scanline we really need if (jpeg_read_scanlines (&m_cinfo, (JSAMPLE **)&readdata, 1) != 1 || m_fatalerr) { error ("JPEG failed scanline read (\"%s\")", filename().c_str()); return false; } } if (m_cmyk) cmyk_to_rgb (m_spec.width, (unsigned char *)readdata, 4, (unsigned char *)data, 3); return true; } bool JpgInput::close () { if (m_fd != NULL) { // unnecessary? jpeg_abort_decompress (&m_cinfo); jpeg_destroy_decompress (&m_cinfo); close_file (); } init (); // Reset to initial state return true; } void JpgInput::jpeg_decode_iptc (const unsigned char *buf) { // APP13 blob doesn't have to be IPTC info. Look for the IPTC marker, // which is the string "Photoshop 3.0" followed by a null character. if (strcmp ((const char *)buf, "Photoshop 3.0")) return; buf += strlen("Photoshop 3.0") + 1; // Next are the 4 bytes "8BIM" if (strncmp ((const char *)buf, "8BIM", 4)) return; buf += 4; // Next two bytes are the segment type, in big endian. // We expect 1028 to indicate IPTC data block. if (((buf[0] << 8) + buf[1]) != 1028) return; buf += 2; // Next are 4 bytes of 0 padding, just skip it. buf += 4; // Next is 2 byte (big endian) giving the size of the segment int segmentsize = (buf[0] << 8) + buf[1]; buf += 2; decode_iptc_iim (buf, segmentsize, m_spec); } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/jpeg.imageio/jpegoutput.cpp0000644000175000017500000004460313151711064022373 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Based on BSD-licensed software Copyright 2004 NVIDIA Corp. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include "OpenImageIO/imageio.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" #include "jpeg_pvt.h" OIIO_PLUGIN_NAMESPACE_BEGIN #define DBG if(0) // References: // * JPEG library documentation: /usr/share/doc/libjpeg-devel-6b // * JFIF spec: https://www.w3.org/Graphics/JPEG/jfif3.pdf // * ITU T.871 (aka ISO/IEC 10918-5): // https://www.itu.int/rec/T-REC-T.871-201105-I/en class JpgOutput : public ImageOutput { public: JpgOutput () { init(); } virtual ~JpgOutput () { close(); } virtual const char * format_name (void) const { return "jpeg"; } virtual int supports (string_view feature) const { return (feature == "exif" || feature == "iptc"); } virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode=Create); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); virtual bool close (); virtual bool copy_image (ImageInput *in); private: FILE *m_fd; std::string m_filename; unsigned int m_dither; int m_next_scanline; // Which scanline is the next to write? std::vector m_scratch; struct jpeg_compress_struct m_cinfo; struct jpeg_error_mgr c_jerr; jvirt_barray_ptr *m_copy_coeffs; struct jpeg_decompress_struct *m_copy_decompressor; std::vector m_tilebuffer; void init (void) { m_fd = NULL; m_copy_coeffs = NULL; m_copy_decompressor = NULL; } void set_subsampling (const int components[]) { jpeg_set_colorspace (&m_cinfo, JCS_YCbCr); m_cinfo.comp_info[0].h_samp_factor = components[0]; m_cinfo.comp_info[0].v_samp_factor = components[1]; m_cinfo.comp_info[1].h_samp_factor = components[2]; m_cinfo.comp_info[1].v_samp_factor = components[3]; m_cinfo.comp_info[2].h_samp_factor = components[4]; m_cinfo.comp_info[2].v_samp_factor = components[5]; } }; OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *jpeg_output_imageio_create () { return new JpgOutput; } OIIO_EXPORT const char *jpeg_output_extensions[] = { "jpg", "jpe", "jpeg", "jif", "jfif", "jfi", NULL }; OIIO_PLUGIN_EXPORTS_END bool JpgOutput::open (const std::string &name, const ImageSpec &newspec, OpenMode mode) { if (mode != Create) { error ("%s does not support subimages or MIP levels", format_name()); return false; } // Save name and spec for later use m_filename = name; m_spec = newspec; // Check for things this format doesn't support if (m_spec.width < 1 || m_spec.height < 1) { error ("Image resolution must be at least 1x1, you asked for %d x %d", m_spec.width, m_spec.height); return false; } if (m_spec.depth < 1) m_spec.depth = 1; if (m_spec.depth > 1) { error ("%s does not support volume images (depth > 1)", format_name()); return false; } if (m_spec.nchannels != 1 && m_spec.nchannels != 3 && m_spec.nchannels != 4) { error ("%s does not support %d-channel images", format_name(), m_spec.nchannels); return false; } m_fd = Filesystem::fopen (name, "wb"); if (m_fd == NULL) { error ("Unable to open file \"%s\"", name.c_str()); return false; } m_cinfo.err = jpeg_std_error (&c_jerr); // set error handler jpeg_create_compress (&m_cinfo); // create compressor jpeg_stdio_dest (&m_cinfo, m_fd); // set output stream // Set image and compression parameters m_cinfo.image_width = m_spec.width; m_cinfo.image_height = m_spec.height; if (m_spec.nchannels == 3 || m_spec.nchannels == 4) { m_cinfo.input_components = 3; m_cinfo.in_color_space = JCS_RGB; } else if (m_spec.nchannels == 1) { m_cinfo.input_components = 1; m_cinfo.in_color_space = JCS_GRAYSCALE; } string_view resunit = m_spec.get_string_attribute ("ResolutionUnit"); if (Strutil::iequals (resunit, "none")) m_cinfo.density_unit = 0; else if (Strutil::iequals (resunit, "in")) m_cinfo.density_unit = 1; else if (Strutil::iequals (resunit, "cm")) m_cinfo.density_unit = 2; else m_cinfo.density_unit = 0; m_cinfo.X_density = int (m_spec.get_float_attribute ("XResolution")); m_cinfo.Y_density = int (m_spec.get_float_attribute ("YResolution")); const float aspect = m_spec.get_float_attribute ("PixelAspectRatio", 1.0f); if (aspect != 1.0f && m_cinfo.X_density <= 1 && m_cinfo.Y_density <= 1) { // No useful [XY]Resolution, but there is an aspect ratio requested. // Arbitrarily pick 72 dots per undefined unit, and jigger it to // honor it as best as we can. // // Here's where things get tricky. By logic and reason, as well as // the JFIF spec and ITU T.871, the pixel aspect ratio is clearly // ydensity/xdensity (because aspect is xlength/ylength, and density // is 1/length). BUT... for reasons lost to history, a number of // apps get this exactly backwards, and these include PhotoShop, // Nuke, and RV. So, alas, we must replicate the mistake, or else // all these common applications will misunderstand the JPEG files // written by OIIO and vice versa. m_cinfo.Y_density = 72; m_cinfo.X_density = int (m_cinfo.Y_density * aspect + 0.5f); m_spec.attribute ("XResolution", float(m_cinfo.Y_density * aspect + 0.5f)); m_spec.attribute ("YResolution", float(m_cinfo.Y_density)); } m_cinfo.write_JFIF_header = TRUE; if (m_copy_coeffs) { // Back door for copy() jpeg_copy_critical_parameters (m_copy_decompressor, &m_cinfo); DBG std::cout << "out open: copy_critical_parameters\n"; jpeg_write_coefficients (&m_cinfo, m_copy_coeffs); DBG std::cout << "out open: write_coefficients\n"; } else { // normal write of scanlines jpeg_set_defaults (&m_cinfo); // default compression // Careful -- jpeg_set_defaults overwrites density m_cinfo.X_density = int (m_spec.get_float_attribute ("XResolution")); m_cinfo.Y_density = int (m_spec.get_float_attribute ("YResolution", m_cinfo.X_density)); DBG std::cout << "out open: set_defaults\n"; int quality = newspec.get_int_attribute ("CompressionQuality", 98); jpeg_set_quality (&m_cinfo, quality, TRUE); // baseline values DBG std::cout << "out open: set_quality\n"; if (m_cinfo.input_components == 3) { std::string subsampling = m_spec.get_string_attribute (JPEG_SUBSAMPLING_ATTR); if (subsampling == JPEG_444_STR) set_subsampling(JPEG_444_COMP); else if (subsampling == JPEG_422_STR) set_subsampling(JPEG_422_COMP); else if (subsampling == JPEG_420_STR) set_subsampling(JPEG_420_COMP); else if (subsampling == JPEG_411_STR) set_subsampling(JPEG_411_COMP); } DBG std::cout << "out open: set_colorspace\n"; jpeg_start_compress (&m_cinfo, TRUE); // start working DBG std::cout << "out open: start_compress\n"; } m_next_scanline = 0; // next scanline we'll write // Write JPEG comment, if sent an 'ImageDescription' ImageIOParameter *comment = m_spec.find_attribute ("ImageDescription", TypeDesc::STRING); if (comment && comment->data()) { const char **c = (const char **) comment->data(); jpeg_write_marker (&m_cinfo, JPEG_COM, (JOCTET*)*c, strlen(*c) + 1); } if (Strutil::iequals (m_spec.get_string_attribute ("oiio:ColorSpace"), "sRGB")) m_spec.attribute ("Exif:ColorSpace", 1); // Write EXIF info std::vector exif; // Start the blob with "Exif" and two nulls. That's how it // always is in the JPEG files I've examined. exif.push_back ('E'); exif.push_back ('x'); exif.push_back ('i'); exif.push_back ('f'); exif.push_back (0); exif.push_back (0); encode_exif (m_spec, exif); jpeg_write_marker (&m_cinfo, JPEG_APP0+1, (JOCTET*)&exif[0], exif.size()); // Write IPTC IIM metadata tags, if we have anything std::vector iptc; encode_iptc_iim (m_spec, iptc); if (iptc.size()) { static char photoshop[] = "Photoshop 3.0"; std::vector head (photoshop, photoshop+strlen(photoshop)+1); static char _8BIM[] = "8BIM"; head.insert (head.end(), _8BIM, _8BIM+4); head.push_back (4); // 0x0404 head.push_back (4); head.push_back (0); // four bytes of zeroes head.push_back (0); head.push_back (0); head.push_back (0); head.push_back ((char)(iptc.size() >> 8)); // size of block head.push_back ((char)(iptc.size() & 0xff)); iptc.insert (iptc.begin(), head.begin(), head.end()); jpeg_write_marker (&m_cinfo, JPEG_APP0+13, (JOCTET*)&iptc[0], iptc.size()); } // Write XMP packet, if we have anything std::string xmp = encode_xmp (m_spec, true); if (! xmp.empty()) { static char prefix[] = "http://ns.adobe.com/xap/1.0/"; std::vector block (prefix, prefix+strlen(prefix)+1); block.insert (block.end(), xmp.c_str(), xmp.c_str()+xmp.length()); jpeg_write_marker (&m_cinfo, JPEG_APP0+1, (JOCTET*)&block[0], block.size()); } m_spec.set_format (TypeDesc::UINT8); // JPG is only 8 bit // Write ICC profile, if we have anything const ImageIOParameter* icc_profile_parameter = m_spec.find_attribute(ICC_PROFILE_ATTR); if (icc_profile_parameter != NULL) { unsigned char *icc_profile = (unsigned char*)icc_profile_parameter->data(); unsigned int icc_profile_length = icc_profile_parameter->type().size(); if (icc_profile && icc_profile_length){ /* Calculate the number of markers we'll need, rounding up of course */ int num_markers = icc_profile_length / MAX_DATA_BYTES_IN_MARKER; if ((unsigned int)(num_markers * MAX_DATA_BYTES_IN_MARKER) != icc_profile_length) num_markers++; int curr_marker = 1; /* per spec, count strarts at 1*/ std::vector profile (MAX_DATA_BYTES_IN_MARKER + ICC_HEADER_SIZE); while (icc_profile_length > 0) { // length of profile to put in this marker unsigned int length = std::min (icc_profile_length, (unsigned int)MAX_DATA_BYTES_IN_MARKER); icc_profile_length -= length; // Write the JPEG marker header (APP2 code and marker length) strcpy ((char *)&profile[0], "ICC_PROFILE"); profile[11] = 0; profile[12] = curr_marker; profile[13] = (unsigned char) num_markers; memcpy(&profile[0] + ICC_HEADER_SIZE, icc_profile+length*(curr_marker-1), length); jpeg_write_marker(&m_cinfo, JPEG_APP0 + 2, &profile[0], ICC_HEADER_SIZE+length); curr_marker++; } } } m_dither = m_spec.get_int_attribute ("oiio:dither", 0); // If user asked for tiles -- which JPEG doesn't support, emulate it by // buffering the whole image. if (m_spec.tile_width && m_spec.tile_height) m_tilebuffer.resize (m_spec.image_bytes()); return true; } bool JpgOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { y -= m_spec.y; if (y != m_next_scanline) { error ("Attempt to write scanlines out of order to %s", m_filename.c_str()); return false; } if (y >= (int)m_cinfo.image_height) { error ("Attempt to write too many scanlines to %s", m_filename.c_str()); return false; } assert (y == (int)m_cinfo.next_scanline); // It's so common to want to write RGBA data out as JPEG (which only // supports RGB) than it would be too frustrating to reject it. // Instead, we just silently drop the alpha. Here's where we do the // dirty work, temporarily doctoring the spec so that // to_native_scanline properly contiguizes the first three channels, // then we restore it. The call to to_native_scanline below needs // m_spec.nchannels to be set to the true number of channels we're // writing, or it won't arrange the data properly. But if we // doctored m_spec.nchannels = 3 permanently, then subsequent calls // to write_scanline (including any surrounding call to write_image) // with stride=AutoStride would screw up the strides since the // user's stride is actually not 3 channels. int save_nchannels = m_spec.nchannels; m_spec.nchannels = m_cinfo.input_components; data = to_native_scanline (format, data, xstride, m_scratch, m_dither, y, z); m_spec.nchannels = save_nchannels; jpeg_write_scanlines (&m_cinfo, (JSAMPLE**)&data, 1); ++m_next_scanline; return true; } bool JpgOutput::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { // Emulate tiles by buffering the whole image return copy_tile_to_image_buffer (x, y, z, format, data, xstride, ystride, zstride, &m_tilebuffer[0]); } bool JpgOutput::close () { if (! m_fd) { // Already closed return true; init(); } bool ok = true; if (m_spec.tile_width) { // We've been emulating tiles; now dump as scanlines. ASSERT (m_tilebuffer.size()); ok &= write_scanlines (m_spec.y, m_spec.y+m_spec.height, 0, m_spec.format, &m_tilebuffer[0]); std::vector().swap (m_tilebuffer); // free it } if (m_next_scanline < spec().height && m_copy_coeffs == NULL) { // But if we've only written some scanlines, write the rest to avoid // errors std::vector buf (spec().scanline_bytes(), 0); char *data = &buf[0]; while (m_next_scanline < spec().height) { jpeg_write_scanlines (&m_cinfo, (JSAMPLE **)&data, 1); // DBG std::cout << "out close: write_scanlines\n"; ++m_next_scanline; } } if (m_next_scanline >= spec().height || m_copy_coeffs) { DBG std::cout << "out close: about to finish_compress\n"; jpeg_finish_compress (&m_cinfo); DBG std::cout << "out close: finish_compress\n"; } else { DBG std::cout << "out close: about to abort_compress\n"; jpeg_abort_compress (&m_cinfo); DBG std::cout << "out close: abort_compress\n"; } DBG std::cout << "out close: about to destroy_compress\n"; jpeg_destroy_compress (&m_cinfo); fclose (m_fd); m_fd = NULL; init(); return ok; } bool JpgOutput::copy_image (ImageInput *in) { if (in && !strcmp(in->format_name(), "jpeg")) { JpgInput *jpg_in = dynamic_cast (in); std::string in_name = jpg_in->filename (); DBG std::cout << "JPG copy_image from " << in_name << "\n"; // Save the original input spec and close it ImageSpec orig_in_spec = in->spec(); in->close (); DBG std::cout << "Closed old file\n"; // Re-open the input spec, with special request that the JpgInput // will recognize as a request to merely open, but not start the // decompressor. ImageSpec in_spec; ImageSpec config_spec; config_spec.attribute ("_jpeg:raw", 1); in->open (in_name, in_spec, config_spec); // Re-open the output std::string out_name = m_filename; ImageSpec orig_out_spec = spec(); close (); m_copy_coeffs = (jvirt_barray_ptr *)jpg_in->coeffs(); m_copy_decompressor = &jpg_in->m_cinfo; open (out_name, orig_out_spec); // Strangeness -- the write_coefficients somehow sets things up // so that certain writes only happen in close(), which MUST // happen while the input file is still open. So we go ahead // and close() now, so that the caller of copy_image() doesn't // close the input file first and then wonder why they crashed. close (); return true; } return ImageOutput::copy_image (in); } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/jpeg.imageio/jpeg_pvt.h0000644000175000017500000001175613151711064021453 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ ///////////////////////////////////////////////////////////////////////////// // Private definitions internal to the jpeg.imageio plugin ///////////////////////////////////////////////////////////////////////////// #ifndef OPENIMAGEIO_JPEG_PVT_H #define OPENIMAGEIO_JPEG_PVT_H #include #ifdef WIN32 #undef FAR #define XMD_H #endif extern "C" { #include "jpeglib.h" } OIIO_PLUGIN_NAMESPACE_BEGIN #define MAX_DATA_BYTES_IN_MARKER 65519L #define ICC_HEADER_SIZE 14 #define ICC_PROFILE_ATTR "ICCProfile" // Chroma sub-sampling values for jpeg_compress_struct / jpeg_component_info #define JPEG_SUBSAMPLING_ATTR "jpeg:subsampling" #define JPEG_444_STR "4:4:4" #define JPEG_422_STR "4:2:2" #define JPEG_420_STR "4:2:0" #define JPEG_411_STR "4:1:1" static const int JPEG_444_COMP[6] = {1,1, 1,1, 1,1}; static const int JPEG_422_COMP[6] = {2,1, 1,1, 1,1}; static const int JPEG_420_COMP[6] = {2,2, 1,1, 1,1}; static const int JPEG_411_COMP[6] = {4,1, 1,1, 1,1}; class JpgInput : public ImageInput { public: JpgInput () { init(); } virtual ~JpgInput () { close(); } virtual const char * format_name (void) const { return "jpeg"; } virtual int supports (string_view feature) const { return (feature == "exif" || feature == "iptc"); } virtual bool valid_file (const std::string &filename) const; virtual bool open (const std::string &name, ImageSpec &spec); virtual bool open (const std::string &name, ImageSpec &spec, const ImageSpec &config); virtual bool read_native_scanline (int y, int z, void *data); virtual bool close (); const std::string &filename () const { return m_filename; } void * coeffs () const { return m_coeffs; } struct my_error_mgr { struct jpeg_error_mgr pub; /* "public" fields */ jmp_buf setjmp_buffer; /* for return to caller */ JpgInput *jpginput; /* back pointer to *this */ }; typedef struct my_error_mgr * my_error_ptr; // Called by my_error_exit void jpegerror (my_error_ptr myerr, bool fatal=false); private: FILE *m_fd; std::string m_filename; int m_next_scanline; // Which scanline is the next to read? bool m_raw; // Read raw coefficients, not scanlines bool m_cmyk; // The input file is cmyk bool m_fatalerr; // JPEG reader hit a fatal error struct jpeg_decompress_struct m_cinfo; my_error_mgr m_jerr; jvirt_barray_ptr *m_coeffs; std::vector m_cmyk_buf; // For CMYK translation void init () { m_fd = NULL; m_raw = false; m_cmyk = false; m_fatalerr = false; m_coeffs = NULL; m_jerr.jpginput = this; } // Rummage through the JPEG "APP1" marker pointed to by buf, decoding // IPTC (International Press Telecommunications Council) metadata // information and adding attributes to spec. This assumes it's in // the form of an IIM (Information Interchange Model), which is actually // considered obsolete and is replaced by an XML scheme called XMP. void jpeg_decode_iptc (const unsigned char *buf); bool read_icc_profile (j_decompress_ptr cinfo, ImageSpec& spec); void close_file () { if (m_fd) fclose (m_fd); // N.B. the init() will set m_fd to NULL init (); } friend class JpgOutput; }; OIIO_PLUGIN_NAMESPACE_END #endif /* OPENIMAGEIO_JPEG_PVT_H */ openimageio-1.7.17~dfsg0.orig/src/targa.imageio/0000755000175000017500000000000013151711064017623 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/targa.imageio/CMakeLists.txt0000644000175000017500000000006113151711064022360 0ustar mfvmfvadd_oiio_plugin (targainput.cpp targaoutput.cpp) openimageio-1.7.17~dfsg0.orig/src/targa.imageio/targainput.cpp0000644000175000017500000007075513151711064022523 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include "targa_pvt.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/fmath.h" OIIO_PLUGIN_NAMESPACE_BEGIN using namespace TGA_pvt; class TGAInput : public ImageInput { public: TGAInput () { init(); } virtual ~TGAInput () { close(); } virtual const char * format_name (void) const { return "targa"; } virtual bool open (const std::string &name, ImageSpec &newspec); virtual bool open (const std::string &name, ImageSpec &newspec, const ImageSpec &config); virtual bool close (); virtual bool read_native_scanline (int y, int z, void *data); private: std::string m_filename; ///< Stash the filename FILE *m_file; ///< Open image handle tga_header m_tga; ///< Targa header tga_footer m_foot; ///< Targa 2.0 footer unsigned int m_ofs_colcorr_tbl; ///< Offset to colour correction table tga_alpha_type m_alpha; ///< Alpha type bool m_keep_unassociated_alpha; ///< Do not convert unassociated alpha std::vector m_buf; ///< Buffer the image pixels /// Reset everything to initial state /// void init () { m_file = NULL; m_buf.clear (); m_ofs_colcorr_tbl = 0; m_alpha = TGA_ALPHA_NONE; m_keep_unassociated_alpha = false; } /// Helper function: read the image. /// bool readimg (); /// Helper function: decode a pixel. inline void decode_pixel (unsigned char *in, unsigned char *out, unsigned char *palette, int& bytespp, int& palbytespp, int& alphabits); /// Helper: read, with error detection /// bool fread (void *buf, size_t itemsize, size_t nitems) { size_t n = ::fread (buf, itemsize, nitems, m_file); if (n != nitems) error ("Read error"); return n == nitems; } }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageInput *targa_input_imageio_create () { return new TGAInput; } OIIO_EXPORT int targa_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char * targa_input_extensions[] = { "tga", "tpic", NULL }; OIIO_EXPORT const char* targa_imageio_library_version () { return NULL; } OIIO_PLUGIN_EXPORTS_END bool TGAInput::open (const std::string &name, ImageSpec &newspec) { m_filename = name; m_file = Filesystem::fopen (name, "rb"); if (! m_file) { error ("Could not open file \"%s\"", name.c_str()); return false; } // due to struct packing, we may get a corrupt header if we just load the // struct from file; to adress that, read every member individually // save some typing #define RH(memb) if (! fread (&m_tga.memb, sizeof (m_tga.memb), 1)) \ return false RH(idlen); RH(cmap_type); RH(type); RH(cmap_first); RH(cmap_length); RH(cmap_size); RH(x_origin); RH(y_origin); RH(width); RH(height); RH(bpp); RH(attr); #undef RH if (bigendian()) { // TGAs are little-endian swap_endian (&m_tga.idlen); swap_endian (&m_tga.cmap_type); swap_endian (&m_tga.type); swap_endian (&m_tga.cmap_first); swap_endian (&m_tga.cmap_length); swap_endian (&m_tga.cmap_size); swap_endian (&m_tga.x_origin); swap_endian (&m_tga.y_origin); swap_endian (&m_tga.width); swap_endian (&m_tga.height); swap_endian (&m_tga.bpp); swap_endian (&m_tga.attr); } if (m_tga.bpp != 8 && m_tga.bpp != 15 && m_tga.bpp != 16 && m_tga.bpp != 24 && m_tga.bpp != 32) { error ("Illegal pixel size: %d bits per pixel", m_tga.bpp); return false; } if (m_tga.type == TYPE_NODATA) { error ("Image with no data"); return false; } if (m_tga.type != TYPE_PALETTED && m_tga.type != TYPE_RGB && m_tga.type != TYPE_GRAY && m_tga.type != TYPE_PALETTED_RLE && m_tga.type != TYPE_RGB_RLE && m_tga.type != TYPE_GRAY_RLE) { error ("Illegal image type: %d", m_tga.type); return false; } if (m_tga.cmap_type && (m_tga.type == TYPE_GRAY || m_tga.type == TYPE_GRAY_RLE)) { // it should be an error for TYPE_RGB* as well, but apparently some // *very* old TGAs can be this way, so we'll hack around it error ("Palette defined for grayscale image"); return false; } if (m_tga.cmap_type && (m_tga.cmap_size != 15 && m_tga.cmap_size != 16 && m_tga.cmap_size != 24 && m_tga.cmap_size != 32)) { error ("Illegal palette entry size: %d bits", m_tga.cmap_size); return false; } m_alpha = TGA_ALPHA_NONE; if (((m_tga.type == TYPE_RGB || m_tga.type == TYPE_RGB_RLE) && m_tga.bpp == 32) || ((m_tga.type == TYPE_GRAY || m_tga.type == TYPE_GRAY_RLE) && m_tga.bpp > 8)) { m_alpha = (m_tga.attr & 0x08) > 0 ? TGA_ALPHA_USEFUL : TGA_ALPHA_NONE; } m_spec = ImageSpec ((int)m_tga.width, (int)m_tga.height, // colour channels ((m_tga.type == TYPE_GRAY || m_tga.type == TYPE_GRAY_RLE) ? 1 : 3) // have we got alpha? + (m_tga.bpp == 32 || m_alpha >= TGA_ALPHA_UNDEFINED_RETAIN), TypeDesc::UINT8); m_spec.attribute ("oiio:BitsPerSample", m_tga.bpp/m_spec.nchannels); m_spec.default_channel_names (); #if 0 // no one seems to adhere to this part of the spec... if (m_tga.attr & FLAG_X_FLIP) m_spec.x = m_spec.width - m_tga.x_origin - 1; else m_spec.x = m_tga.x_origin; if (m_tga.attr & FLAG_Y_FLIP) m_spec.y = m_tga.y_origin; else m_spec.y = m_spec.width - m_tga.y_origin - 1; #endif if (m_tga.type >= TYPE_PALETTED_RLE) m_spec.attribute ("compression", "rle"); /*std::cerr << "[tga] " << m_tga.width << "x" << m_tga.height << "@" << (int)m_tga.bpp << " (" << m_spec.nchannels << ") type " << (int)m_tga.type << "\n";*/ // load image ID if (m_tga.idlen) { // TGA comments can be at most 255 bytes long, but we add 1 extra byte // in case the comment lacks null termination char id[256]; memset (id, 0, sizeof (id)); if (! fread (id, m_tga.idlen, 1)) return false; m_spec.attribute ("targa:ImageID", id); } int ofs = ftell (m_file); // now try and see if it's a TGA 2.0 image // TGA 2.0 files are identified by a nifty "TRUEVISION-XFILE.\0" signature fseek (m_file, -26, SEEK_END); if (fread (&m_foot.ofs_ext, sizeof (m_foot.ofs_ext), 1) && fread (&m_foot.ofs_dev, sizeof (m_foot.ofs_dev), 1) && fread (&m_foot.signature, sizeof (m_foot.signature), 1) && !strncmp (m_foot.signature, "TRUEVISION-XFILE.", 17)) { //std::cerr << "[tga] this is a TGA 2.0 file\n"; if (bigendian()) { swap_endian (&m_foot.ofs_ext); swap_endian (&m_foot.ofs_dev); } // read the extension area fseek (m_file, m_foot.ofs_ext, SEEK_SET); // check if this is a TGA 2.0 extension area // according to the 2.0 spec, the size for valid 2.0 files is exactly // 495 bytes, and the reader should only read as much as it understands // for < 495, we ignore this section of the file altogether // for > 495, we only read what we know uint16_t s; if (! fread (&s, 2, 1)) return false; if (bigendian()) swap_endian (&s); //std::cerr << "[tga] extension area size: " << s << "\n"; if (s >= 495) { union { unsigned char c[324]; // so as to accomodate the comments uint16_t s[6]; uint32_t l; } buf; // load image author if (! fread (buf.c, 41, 1)) return false; if (buf.c[0]) m_spec.attribute ("Artist", (char *)buf.c); // load image comments if (! fread (buf.c, 324, 1)) return false; // concatenate the lines into a single string std::string tmpstr ((const char *)buf.c); if (buf.c[81]) { tmpstr += "\n"; tmpstr += (const char *)&buf.c[81]; } if (buf.c[162]) { tmpstr += "\n"; tmpstr += (const char *)&buf.c[162]; } if (buf.c[243]) { tmpstr += "\n"; tmpstr += (const char *)&buf.c[243]; } if (tmpstr.length () > 0) m_spec.attribute ("ImageDescription", tmpstr); // timestamp if (! fread (buf.s, 2, 6)) return false; if (buf.s[0] || buf.s[1] || buf.s[2] || buf.s[3] || buf.s[4] || buf.s[5]) { if (bigendian()) swap_endian (&buf.s[0], 6); sprintf ((char *)&buf.c[12], "%04u:%02u:%02u %02u:%02u:%02u", buf.s[2], buf.s[0], buf.s[1], buf.s[3], buf.s[4], buf.s[5]); m_spec.attribute ("DateTime", (char *)&buf.c[12]); } // job name/ID if (! fread (buf.c, 41, 1)) return false; if (buf.c[0]) m_spec.attribute ("DocumentName", (char *)buf.c); // job time if (! fread (buf.s, 2, 3)) return false; if (buf.s[0] || buf.s[1] || buf.s[2]) { if (bigendian()) swap_endian (&buf.s[0], 3); sprintf ((char *)&buf.c[6], "%u:%02u:%02u", buf.s[0], buf.s[1], buf.s[2]); m_spec.attribute ("targa:JobTime", (char *)&buf.c[6]); } // software if (! fread (buf.c, 41, 1)) return false; if (buf.c[0]) { // tack on the version number and letter uint16_t n; char l; if (! fread (&n, 2, 1) || ! fread (&l, 1, 1)) return false; sprintf ((char *)&buf.c[strlen ((char *)buf.c)], " %u.%u%c", n / 100, n % 100, l != ' ' ? l : 0); m_spec.attribute ("Software", (char *)buf.c); } // background (key) colour if (! fread (buf.c, 4, 1)) return false; // FIXME: what do we do with it? // aspect ratio if (! fread (buf.s, 2, 2)) return false; // if the denominator is zero, it's unused if (buf.s[1]) { if (bigendian()) swap_endian (&buf.s[0], 2); m_spec.attribute ("PixelAspectRatio", (float)buf.s[0] / (float)buf.s[1]); } // gamma if (! fread (buf.s, 2, 2)) return false; // if the denominator is zero, it's unused if (buf.s[1]) { if (bigendian()) swap_endian (&buf.s[0], 2); float gamma = (float)buf.s[0] / (float)buf.s[1]; if (gamma == 1.f) { m_spec.attribute ("oiio:ColorSpace", "Linear"); } else { m_spec.attribute ("oiio:ColorSpace", "GammaCorrected"); m_spec.attribute ("oiio:Gamma", gamma); } } // offset to colour correction table if (! fread (&buf.l, 4, 1)) return false; if (bigendian()) swap_endian (&buf.l); m_ofs_colcorr_tbl = buf.l; /*std::cerr << "[tga] colour correction table offset: " << (int)m_ofs_colcorr_tbl << "\n";*/ // offset to thumbnail if (! fread (&buf.l, 4, 1)) return false; if (bigendian()) swap_endian (&buf.l); unsigned int ofs_thumb = buf.l; // offset to scan-line table if (! fread (&buf.l, 4, 1)) return false; // TODO: can we find any use for this? we can't advertise random // access anyway, because not all RLE-compressed files will have // this table // alpha type if (! fread (buf.c, 1, 1)) return false; m_alpha = (tga_alpha_type)buf.c[0]; // now load the thumbnail if (ofs_thumb) { fseek (m_file, ofs_thumb, SEEK_SET); // most of this code is a dupe of readimg(); according to the // spec, the thumbnail is in the same format as the main image // but uncompressed // thumbnail dimensions if (! fread (&buf.c, 2, 1)) return false; m_spec.attribute ("thumbnail_width", (int)buf.c[0]); m_spec.attribute ("thumbnail_height", (int)buf.c[1]); m_spec.attribute ("thumbnail_nchannels", m_spec.nchannels); // load image data // reuse the image buffer m_buf.resize (buf.c[0] * buf.c[1] * m_spec.nchannels); int bytespp = (m_tga.bpp == 15) ? 2 : (m_tga.bpp / 8); int palbytespp = (m_tga.cmap_size == 15) ? 2 : (m_tga.cmap_size / 8); int alphabits = m_tga.attr & 0x0F; if (alphabits == 0 && m_tga.bpp == 32) alphabits = 8; // read palette, if there is any unsigned char *palette = NULL; if (m_tga.cmap_type) { fseek (m_file, ofs, SEEK_SET); palette = new unsigned char[palbytespp * m_tga.cmap_length]; if (! fread (palette, palbytespp, m_tga.cmap_length)) return false; fseek (m_file, ofs_thumb + 2, SEEK_SET); } unsigned char pixel[4]; unsigned char in[4]; for (int y = buf.c[1] - 1; y >= 0; y--) { for (int x = 0; x < buf.c[0]; x++) { if (! fread (in, bytespp, 1)) return false; decode_pixel (in, pixel, palette, bytespp, palbytespp, alphabits); memcpy (&m_buf[y * buf.c[0] * m_spec.nchannels + x * m_spec.nchannels], pixel, m_spec.nchannels); } } //std::cerr << "[tga] buffer size: " << m_buf.size() << "\n"; // finally, add the thumbnail to attributes m_spec.attribute ("thumbnail_image", TypeDesc (TypeDesc::UINT8, m_buf.size()), &m_buf[0]); m_buf.clear(); } } // FIXME: provide access to the developer area; according to Larry, // it's probably safe to ignore it altogether until someone complains // that it's missing :) } if (m_spec.alpha_channel != -1 && m_alpha != TGA_ALPHA_PREMULTIPLIED) if (m_keep_unassociated_alpha) m_spec.attribute ("oiio:UnassociatedAlpha", 1); fseek (m_file, ofs, SEEK_SET); newspec = spec (); return true; } bool TGAInput::open (const std::string &name, ImageSpec &newspec, const ImageSpec &config) { // Check 'config' for any special requests if (config.get_int_attribute("oiio:UnassociatedAlpha", 0) == 1) m_keep_unassociated_alpha = true; return open (name, newspec); } inline void TGAInput::decode_pixel (unsigned char *in, unsigned char *out, unsigned char *palette, int& bytespp, int& palbytespp, int& alphabits) { unsigned int k = 0; // I hate nested switches... switch (m_tga.type) { case TYPE_PALETTED: case TYPE_PALETTED_RLE: for (int i = 0; i < bytespp; ++i) k |= in[i] << (8*i); // Assemble it in little endian order k = (m_tga.cmap_first + k) * palbytespp; switch (palbytespp) { case 2: // see the comment for 16bpp RGB below for an explanation of this out[0] = bit_range_convert<5, 8> ((palette[k + 1] & 0x7C) >> 2); out[1] = bit_range_convert<5, 8> (((palette[k + 0] & 0xE0) >> 5) | ((palette[k + 1] & 0x03) << 3)); out[2] = bit_range_convert<5, 8> (palette[k + 0] & 0x1F); break; case 3: out[0] = palette[k + 2]; out[1] = palette[k + 1]; out[2] = palette[k + 0]; break; case 4: out[0] = palette[k + 2]; out[1] = palette[k + 1]; out[2] = palette[k + 0]; out[3] = palette[k + 3]; break; } break; case TYPE_RGB: case TYPE_RGB_RLE: switch (bytespp) { case 2: // This format is pretty funky. It's a 1A-5R-5G-5B layout, // with the first bit alpha (or unused if only 3 channels), // but thanks to the little-endianness, the order is pretty // bizarre. The bits are non-contiguous, so we have to // extract the relevant ones and synthesize the colour // values from the two bytes. // NOTE: This way of handling the pixel as two independent bytes // (as opposed to a single short int) makes it independent from // endianness. // Here's what the layout looks like: // MSb unused LSb // v v v // GGGBBBBB RRRRRGG // [||||||||] [||||||||] // While red and blue channels are quite self-explanatory, the // green one needs a few words. The 5 bits are composed of the // 2 from the second byte as the more significant and the 3 from // the first one as the less significant ones. // extract the bits to valid 5-bit integers and expand to full range out[0] = bit_range_convert<5, 8> ((in[1] & 0x7C) >> 2); out[1] = bit_range_convert<5, 8> (((in[0] & 0xE0) >> 5) | ((in[1] & 0x03) << 3)); out[2] = bit_range_convert<5, 8> (in[0] & 0x1F); if (m_spec.nchannels > 3) out[3] = (in[0] & 0x80) ? 255 : 0; break; case 3: out[0] = in[2]; out[1] = in[1]; out[2] = in[0]; break; case 4: out[0] = in[2]; out[1] = in[1]; out[2] = in[0]; out[3] = in[3]; break; } break; case TYPE_GRAY: case TYPE_GRAY_RLE: if (bigendian ()) { for (int i = bytespp - 1; i >= 0; i--) out[i] = in[bytespp - i - 1]; } else memcpy (out, in, bytespp); break; } } template static void associateAlpha (T * data, int size, int channels, int alpha_channel, float gamma) { T max = std::numeric_limits::max(); if (gamma == 1) { for (int x = 0; x < size; ++x, data += channels) for (int c = 0; c < channels; c++) if (c != alpha_channel){ unsigned int f = data[c]; data[c] = (f * data[alpha_channel]) / max; } } else { //With gamma correction float inv_max = 1.0 / max; for (int x = 0; x < size; ++x, data += channels) { float alpha_associate = pow(data[alpha_channel]*inv_max, gamma); // We need to transform to linear space, associate the alpha, and // then transform back. That is, if D = data[c], we want // // D' = max * ( (D/max)^(1/gamma) * (alpha/max) ) ^ gamma // // This happens to simplify to something which looks like // multiplying by a nonlinear alpha: // // D' = D * (alpha/max)^gamma for (int c = 0; c < channels; c++) if (c != alpha_channel) data[c] = static_cast(data[c] * alpha_associate); } } } bool TGAInput::readimg () { // how many bytes we actually read // for 15-bit read 2 bytes and ignore the 16th bit int bytespp = (m_tga.bpp == 15) ? 2 : (m_tga.bpp / 8); int palbytespp = (m_tga.cmap_size == 15) ? 2 : (m_tga.cmap_size / 8); int alphabits = m_tga.attr & 0x0F; if (alphabits == 0 && m_tga.bpp == 32) alphabits = 8; /*std::cerr << "[tga] bytespp = " << bytespp << " palbytespp = " << palbytespp << " alphabits = " << alphabits << "\n";*/ m_buf.resize (m_spec.image_bytes()); // read palette, if there is any unsigned char *palette = NULL; if (m_tga.cmap_type) { palette = new unsigned char[palbytespp * m_tga.cmap_length]; if (! fread (palette, palbytespp, m_tga.cmap_length)) return false; } unsigned char pixel[4]; if (m_tga.type < TYPE_PALETTED_RLE) { // uncompressed image data unsigned char in[4]; for (int y = m_spec.height - 1; y >= 0; y--) { for (int x = 0; x < m_spec.width; x++) { if (! fread (in, bytespp, 1)) return false; decode_pixel (in, pixel, palette, bytespp, palbytespp, alphabits); memcpy (&m_buf[y * m_spec.width * m_spec.nchannels + x * m_spec.nchannels], pixel, m_spec.nchannels); } } } else { // Run Length Encoded image unsigned char in[5]; int packet_size; for (int y = m_spec.height - 1; y >= 0; y--) { for (int x = 0; x < m_spec.width; x++) { if (! fread (in, 1 + bytespp, 1)) return false; packet_size = 1 + (in[0] & 0x7f); decode_pixel (&in[1], pixel, palette, bytespp, palbytespp, alphabits); if (in[0] & 0x80) { // run length packet /*std::cerr << "[tga] run length packet " << packet_size << "\n";*/ for (int i = 0; i < packet_size; i++) { memcpy (&m_buf[y * m_spec.width * m_spec.nchannels + x * m_spec.nchannels], pixel, m_spec.nchannels); if (i < packet_size - 1) { x++; if (x >= m_spec.width) { // run spans across multiple scanlines x = 0; if (y > 0) y--; else goto loop_break; } } } } else { // non-rle packet /*std::cerr << "[tga] non-run length packet " << packet_size << "\n";*/ for (int i = 0; i < packet_size; i++) { memcpy (&m_buf[y * m_spec.width * m_spec.nchannels + x * m_spec.nchannels], pixel, m_spec.nchannels); if (i < packet_size - 1) { x++; if (x >= m_spec.width) { // run spans across multiple scanlines x = 0; if (y > 0) y--; else goto loop_break; } // skip the packet header byte if (! fread (&in[1], bytespp, 1)) return false; decode_pixel(&in[1], pixel, palette, bytespp, palbytespp, alphabits); } } } } loop_break:; } } delete [] palette; // flip the image, if necessary if (m_tga.cmap_type) bytespp = palbytespp; // Y-flipping is now done in read_native_scanline instead /*if (m_tga.attr & FLAG_Y_FLIP) { //std::cerr << "[tga] y flipping\n"; std::vector flip (m_spec.width * bytespp); unsigned char *src, *dst, *tmp = &flip[0]; for (int y = 0; y < m_spec.height / 2; y++) { src = &m_buf[(m_spec.height - y - 1) * m_spec.width * bytespp]; dst = &m_buf[y * m_spec.width * bytespp]; memcpy(tmp, src, m_spec.width * bytespp); memcpy(src, dst, m_spec.width * bytespp); memcpy(dst, tmp, m_spec.width * bytespp); } }*/ if (m_tga.attr & FLAG_X_FLIP) { //std::cerr << "[tga] x flipping\n"; std::vector flip (bytespp * m_spec.width / 2); unsigned char *src, *dst, *tmp = &flip[0]; for (int y = 0; y < m_spec.height; y++) { src = &m_buf[y * m_spec.width * bytespp]; dst = &m_buf[(y * m_spec.width + m_spec.width / 2) * bytespp]; memcpy(tmp, src, bytespp * m_spec.width / 2); memcpy(src, dst, bytespp * m_spec.width / 2); memcpy(dst, tmp, bytespp * m_spec.width / 2); } } if (m_alpha != TGA_ALPHA_PREMULTIPLIED) { // Convert to associated unless we were requested not to do so if (m_spec.alpha_channel != -1 && !m_keep_unassociated_alpha) { int size = m_spec.width * m_spec.height; float gamma = m_spec.get_float_attribute ("oiio:Gamma", 1.0f); associateAlpha ((unsigned char *)&m_buf[0], size, m_spec.nchannels, m_spec.alpha_channel, gamma); } } return true; } bool TGAInput::close () { if (m_file) { fclose (m_file); m_file = NULL; } init(); // Reset to initial state return true; } bool TGAInput::read_native_scanline (int y, int z, void *data) { if (m_buf.empty ()) readimg (); if (m_tga.attr & FLAG_Y_FLIP) y = m_spec.height - y - 1; size_t size = spec().scanline_bytes(); memcpy (data, &m_buf[0] + y * size, size); return true; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/targa.imageio/targa_pvt.h0000644000175000017500000001013313151711064021761 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_TARGA_PVT_H #define OPENIMAGEIO_TARGA_PVT_H #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" OIIO_PLUGIN_NAMESPACE_BEGIN namespace TGA_pvt { // IneQuation was here enum tga_image_type { TYPE_NODATA = 0, ///< image with no data (why even spec it?) TYPE_PALETTED = 1, ///< paletted RGB TYPE_RGB = 2, ///< can include alpha TYPE_GRAY = 3, ///< can include alpha TYPE_PALETTED_RLE = 9, ///< same as PALETTED but run-length encoded TYPE_RGB_RLE = 10, ///< same as RGB but run-length encoded TYPE_GRAY_RLE = 11 ///< same as GRAY but run-length encoded }; enum tga_flags { FLAG_X_FLIP = 0x10, ///< right-left image FLAG_Y_FLIP = 0x20 ///< top-down image }; /// Targa file header. /// typedef struct { uint8_t idlen; ///< image comment length uint8_t cmap_type; ///< palette type uint8_t type; ///< image type (see tga_image_type) uint16_t cmap_first; ///< offset to first entry uint16_t cmap_length; ///< uint8_t cmap_size; ///< palette size uint16_t x_origin; ///< uint16_t y_origin; ///< uint16_t width; ///< image width uint16_t height; ///< image height uint8_t bpp; ///< bits per pixel uint8_t attr; ///< attribs (alpha bits and \ref tga_flags) } tga_header; /// TGA 2.0 file footer. /// typedef struct { uint32_t ofs_ext; ///< offset to the extension area uint32_t ofs_dev; ///< offset to the developer directory char signature[18]; ///< file signature string } tga_footer; /// TGA 2.0 developer directory entry typedef struct { uint16_t tag; ///< tag uint32_t ofs; ///< byte offset to the tag data uint32_t size; ///< tag data length } tga_devdir_tag; // this is used in the extension area enum tga_alpha_type { TGA_ALPHA_NONE = 0, ///< no alpha data included TGA_ALPHA_UNDEFINED_IGNORE = 1, ///< can ignore alpha TGA_ALPHA_UNDEFINED_RETAIN = 2, ///< undefined, but should be retained TGA_ALPHA_USEFUL = 3, ///< useful alpha data is present TGA_ALPHA_PREMULTIPLIED = 4 ///< alpha is pre-multiplied (arrrgh!) // values 5-127 are reserved // values 128-255 are unassigned }; } // namespace TGA_pvt OIIO_PLUGIN_NAMESPACE_END #endif // OPENIMAGEIO_TARGA_PVT_H openimageio-1.7.17~dfsg0.orig/src/targa.imageio/targaoutput.cpp0000644000175000017500000006030313151711064022710 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include "targa_pvt.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/fmath.h" OIIO_PLUGIN_NAMESPACE_BEGIN using namespace TGA_pvt; class TGAOutput : public ImageOutput { public: TGAOutput (); virtual ~TGAOutput (); virtual const char * format_name (void) const { return "targa"; } virtual int supports (string_view feature) const { return (feature == "alpha"); } virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode=Create); virtual bool close (); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); private: std::string m_filename; ///< Stash the filename FILE *m_file; ///< Open image handle bool m_want_rle; ///< Whether the client asked for RLE bool m_convert_alpha; ///< Do we deassociate alpha? float m_gamma; ///< Gamma to use for alpha conversion std::vector m_scratch; int m_idlen; ///< Length of the TGA ID block unsigned int m_dither; std::vector m_tilebuffer; // Initialize private members to pre-opened state void init (void) { m_file = NULL; m_convert_alpha = true; m_gamma = 1.0; } // Helper function to write the TGA 2.0 data fields, called by close() bool write_tga20_data_fields (); /// Helper function to flush a non-run-length packet /// inline void flush_rawp (unsigned char *& src, int size, int start); /// Helper function to flush a run-length packet /// inline void flush_rlp (unsigned char *buf, int size); /// Helper - write, with error detection (no byte swapping!) template bool fwrite (const T *buf, size_t itemsize=sizeof(T), size_t nitems=1) { if (itemsize*nitems == 0) return true; size_t n = std::fwrite (buf, itemsize, nitems, m_file); if (n != nitems) error ("Write error: wrote %d records of %d", (int)n, (int)nitems); return n == nitems; } /// Helper -- write a 'short' with byte swapping if necessary bool fwrite (uint16_t s) { if (bigendian()) swap_endian (&s); return fwrite (&s, sizeof(s), 1); } bool fwrite (uint32_t i) { if (bigendian()) swap_endian (&i); return fwrite (&i, sizeof(i), 1); } /// Helper -- pad with zeroes bool pad (size_t n=1) { while (n--) if (fputc (0, m_file)) return false; return true; } /// Helper -- write string, with padding and/or truncation bool fwrite_padded (const std::string &s, size_t len) { size_t slen = std::min (s.length(), len-1); return fwrite (s.c_str(), slen) && pad(len-slen); } }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *targa_output_imageio_create () { return new TGAOutput; } // OIIO_EXPORT int tga_imageio_version = OIIO_PLUGIN_VERSION; // it's in tgainput.cpp OIIO_EXPORT const char * targa_output_extensions[] = { "tga", "tpic", NULL }; OIIO_PLUGIN_EXPORTS_END TGAOutput::TGAOutput () { init (); } TGAOutput::~TGAOutput () { // Close, if not already done. close (); } bool TGAOutput::open (const std::string &name, const ImageSpec &userspec, OpenMode mode) { if (mode != Create) { error ("%s does not support subimages or MIP levels", format_name()); return false; } close (); // Close any already-opened file m_spec = userspec; // Stash the spec // Check for things this format doesn't support if (m_spec.width < 1 || m_spec.height < 1) { error ("Image resolution must be at least 1x1, you asked for %d x %d", m_spec.width, m_spec.height); return false; } if (m_spec.depth < 1) m_spec.depth = 1; else if (m_spec.depth > 1) { error ("TGA does not support volume images (depth > 1)"); return false; } if (m_spec.nchannels < 1 || m_spec.nchannels > 4) { error ("TGA only supports 1-4 channels, not %d", m_spec.nchannels); return false; } m_file = Filesystem::fopen (name, "wb"); if (! m_file) { error ("Could not open file \"%s\"", name.c_str()); return false; } // Force 8 bit integers m_spec.set_format (TypeDesc::UINT8); m_dither = m_spec.get_int_attribute ("oiio:dither", 0); // check if the client wants the image to be run length encoded // currently only RGB RLE is supported m_want_rle = (m_spec.get_string_attribute ("compression", "none") != std::string("none")) && m_spec.nchannels >= 3; // TGA does not dictate unassociated (un-"premultiplied") alpha but many // implementations assume it even if we set TGA_ALPHA_PREMULTIPLIED, so // always write unassociated alpha m_convert_alpha = m_spec.alpha_channel != -1 && !m_spec.get_int_attribute("oiio:UnassociatedAlpha", 0); m_gamma = m_spec.get_float_attribute ("oiio:Gamma", 1.0); // prepare and write Targa header tga_header tga; memset (&tga, 0, sizeof (tga)); tga.type = m_spec.nchannels <= 2 ? TYPE_GRAY : (m_want_rle ? TYPE_RGB_RLE : TYPE_RGB); tga.bpp = m_spec.nchannels * 8; tga.width = m_spec.width; tga.height = m_spec.height; #if 0 // no one seems to adhere to this part of the spec... tga.x_origin = m_spec.x; tga.y_origin = m_spec.y; #endif // handle image ID; save it to disk later on std::string id = m_spec.get_string_attribute ("targa:ImageID", ""); // the format only allows for 255 bytes tga.idlen = std::min(id.length(), (size_t)255); m_idlen = tga.idlen; if (m_spec.nchannels % 2 == 0) // if we have alpha tga.attr = 8; // 8 bits of alpha // force Y flip when using RLE // for raw (non-RLE) images we can use random access, so we can dump the // image in the default top-bottom scanline order for maximum // compatibility (not all software supports the Y flip flag); however, // once RLE kicks in, we lose the ability to predict the byte offsets of // scanlines, so we just dump the data in the order it comes in and use // this flag instead if (m_want_rle) tga.attr |= FLAG_Y_FLIP; if (bigendian()) { // TGAs are little-endian swap_endian (&tga.cmap_type); swap_endian (&tga.type); swap_endian (&tga.cmap_first); swap_endian (&tga.cmap_length); swap_endian (&tga.cmap_size); swap_endian (&tga.x_origin); swap_endian (&tga.y_origin); swap_endian (&tga.width); swap_endian (&tga.height); swap_endian (&tga.bpp); swap_endian (&tga.attr); } // due to struct packing, we may get a corrupt header if we just dump the // struct to the file; to adress that, write every member individually // save some typing if (!fwrite(&tga.idlen) || !fwrite(&tga.cmap_type) || !fwrite(&tga.type) || !fwrite(&tga.cmap_first) || !fwrite(&tga.cmap_length) || !fwrite(&tga.cmap_size) || !fwrite(&tga.x_origin) || !fwrite(&tga.y_origin) || !fwrite(&tga.width) || !fwrite(&tga.height) || !fwrite(&tga.bpp) || !fwrite(&tga.attr)) { fclose (m_file); m_file = NULL; return false; } // dump comment to file, don't bother about null termination if (tga.idlen) { if (!fwrite(id.c_str(), tga.idlen)) { fclose (m_file); m_file = NULL; return false; } } // If user asked for tiles -- which this format doesn't support, emulate // it by buffering the whole image. if (m_spec.tile_width && m_spec.tile_height) m_tilebuffer.resize (m_spec.image_bytes()); return true; } bool TGAOutput::write_tga20_data_fields () { if (m_file) { // write out the TGA 2.0 data fields // FIXME: write out the developer area; according to Larry, // it's probably safe to ignore it altogether until someone complains // that it's missing :) fseek (m_file, 0, SEEK_END); // write out the thumbnail, if there is one uint32_t ofs_thumb = 0; unsigned char tw = m_spec.get_int_attribute ("thumbnail_width", 0); unsigned char th = m_spec.get_int_attribute ("thumbnail_width", 0); int tc = m_spec.get_int_attribute ("thumbnail_nchannels", 0); if (tw && th && tc == m_spec.nchannels) { ImageIOParameter *p = m_spec.find_attribute ("thumbnail_image"); if (p) { ofs_thumb = (uint32_t) ftell (m_file); // dump thumbnail size if (!fwrite (&tw) || !fwrite (&th) || !fwrite (p->data(), p->datasize())) { return false; } } } // prepare the footer tga_footer foot = {(uint32_t)ftell (m_file), 0, "TRUEVISION-XFILE."}; // write out the extension area // ext area size -- 2 bytes, always short(495) fwrite (uint16_t(495)); // author - 41 bytes fwrite_padded (m_spec.get_string_attribute("Artist"), 41); // image comment - 324 bytes fwrite_padded (m_spec.get_string_attribute("ImageDescription"), 324); // timestamp - 6 shorts (month, day, year, hour, minute, second) { std::string dt = m_spec.get_string_attribute ("DateTime", ""); uint16_t y, m, d, h, i, s; if (dt.length() > 0) sscanf (dt.c_str(), "%04hu:%02hu:%02hu %02hu:%02hu:%02hu", &y, &m, &d, &h, &i, &s); else y = m = d = h = i = s = 0; if (!fwrite(m) || !fwrite(d) || !fwrite(y) || !fwrite(h) || !fwrite(i) || !fwrite(s)) { return false; } } // job ID - 41 bytes fwrite_padded (m_spec.get_string_attribute("DocumentName"), 41); // job time - 3 shorts (hours, minutes, seconds) { std::string jt = m_spec.get_string_attribute ("targa:JobTime", ""); uint16_t h, m, s; if (jt.length() > 0) sscanf (jt.c_str(), "%hu:%02hu:%02hu", &h, &m, &s); else h = m = s = 0; if (!fwrite(h) || !fwrite(m) || !fwrite(s)) return false; } // software ID -- 41 bytes fwrite_padded (m_spec.get_string_attribute("Software"), 41); // software version - 3 bytes (first 2 bytes: version*100) if (!fwrite(uint16_t(OIIO_VERSION))) return false; pad (1); // key colour (ARGB) -- punt and write 0 pad (4); // pixel aspect ratio -- two shorts, giving a ratio { float ratio = m_spec.get_float_attribute ("PixelAspectRatio", 1.f); float EPS = 1E-5f; if (ratio >= (0.f+EPS) && ((ratio <= (1.f-EPS))||(ratio >= (1.f+EPS)))) { // FIXME: invent a smarter way to convert to a vulgar fraction? // numerator fwrite (uint16_t(ratio * 10000.f)); // numerator fwrite (uint16_t(10000)); // denominator } else { // just dump two zeros in there fwrite (uint16_t(0)); fwrite (uint16_t(0)); } } // gamma -- two shorts, giving a ratio if (Strutil::iequals (m_spec.get_string_attribute("oiio:ColorSpace"), "GammaCorrected")) { // FIXME: invent a smarter way to convert to a vulgar fraction? // NOTE: the spec states that only 1 decimal place of precision // is needed, thus the expansion by 10 // numerator fwrite (uint16_t(m_gamma*10.0f)); fwrite (uint16_t(10)); } else { // just dump two zeros in there fwrite (uint16_t(0)); fwrite (uint16_t(0)); } // offset to colour correction table - 4 bytes // FIXME: support this once it becomes clear how it's actually supposed // to be used... the spec is very unclear about this // for the time being just dump four NULL bytes pad (4); // offset to thumbnail - 4 bytes if (!fwrite(ofs_thumb)) return false; // offset to scanline table - 4 bytes // not used very widely, don't bother unless someone complains pad (4); // alpha type - one byte unsigned char at = (m_spec.nchannels % 2 == 0) ? TGA_ALPHA_USEFUL : TGA_ALPHA_NONE; if (!fwrite(&at)) return false; // write out the TGA footer if (!fwrite(foot.ofs_ext) || !fwrite(foot.ofs_dev) || !fwrite(&foot.signature, 1, sizeof(foot.signature))) { return false; } } return true; } bool TGAOutput::close () { if (! m_file) { // already closed init (); return true; } bool ok = true; if (m_spec.tile_width) { // Handle tile emulation -- output the buffered pixels ASSERT (m_tilebuffer.size()); ok &= write_scanlines (m_spec.y, m_spec.y+m_spec.height, 0, m_spec.format, &m_tilebuffer[0]); std::vector().swap (m_tilebuffer); } ok &= write_tga20_data_fields (); fclose (m_file); // close the stream m_file = NULL; init (); // re-initialize return ok; } inline void TGAOutput::flush_rlp (unsigned char *buf, int size) { // early out if (size < 1) return; // write packet header unsigned char h = (size - 1) | 0x80; // write packet pixel if (!fwrite(&h) || !fwrite (buf, m_spec.nchannels)) { // do something intelligent? return; } } inline void TGAOutput::flush_rawp (unsigned char *& src, int size, int start) { // early out if (size < 1) return; // write packet header unsigned char h = (size - 1) & ~0x80; if (!fwrite (&h)) return; // rewind the scanline and flush packet pixels unsigned char buf[4]; int n = m_spec.nchannels; for (int i = 0; i < size; i++) { if (n <= 2) { // 1- and 2-channels can write directly if (!fwrite (src+start, n)) { return; } } else { // 3- and 4-channel must swap red and blue buf[0] = src[(start + i) * n + 2]; buf[1] = src[(start + i) * n + 1]; buf[2] = src[(start + i) * n + 0]; if (n > 3) buf[3] = src[(start + i) * n + 3]; if (!fwrite (buf, n)) { return; } } } } template static void deassociateAlpha (T * data, int size, int channels, int alpha_channel, float gamma) { unsigned int max = std::numeric_limits::max(); if (gamma == 1){ for (int x = 0; x < size; ++x, data += channels) if (data[alpha_channel]) for (int c = 0; c < channels; c++) if (c != alpha_channel) { unsigned int f = data[c]; f = (f * max) / data[alpha_channel]; data[c] = (T) std::min (max, f); } } else { for (int x = 0; x < size; ++x, data += channels) if (data[alpha_channel]) { // See associateAlpha() for an explanation. float alpha_deassociate = pow((float)max / data[alpha_channel], gamma); for (int c = 0; c < channels; c++) if (c != alpha_channel) data[c] = static_cast (std::min (max, (unsigned int)(data[c] * alpha_deassociate))); } } } bool TGAOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { y -= m_spec.y; m_spec.auto_stride (xstride, format, spec().nchannels); data = to_native_scanline (format, data, xstride, m_scratch, m_dither, y, z); if (m_scratch.empty() || data != &m_scratch[0]) { m_scratch.assign ((unsigned char *)data, (unsigned char *)data+m_spec.scanline_bytes()); data = &m_scratch[0]; } if (m_convert_alpha) { deassociateAlpha ((unsigned char *)data, m_spec.width, m_spec.nchannels, m_spec.alpha_channel, m_gamma); } unsigned char *bdata = (unsigned char *)data; if (m_want_rle) { // Run Length Encoding // it's only profitable if n * b > 1 + b, where: // n is the number of pixels in a run // b is the pixel size in bytes // FIXME: optimize runs spanning across multiple scanlines? unsigned char buf[4] = { 0, 0, 0, 0 }; unsigned char buf2[4] = { 0, 0, 0, 0 }; bool rlp = false; int rlcount = 0, rawcount = 0; for (int x = 0; x < m_spec.width; x++) { // save off previous pixel memcpy (buf2, buf, sizeof (buf2)); // read the new one switch (m_spec.nchannels) { #if 0 case 1: buf[0] = bdata[x ]; break; case 2: buf[0] = bdata[(x * 2 + 0)]; buf[1] = bdata[(x * 2 + 1)]; break; #endif case 3: buf[0] = bdata[(x * 3 + 2)]; buf[1] = bdata[(x * 3 + 1)]; buf[2] = bdata[(x * 3 + 0)]; break; case 4: buf[0] = bdata[(x * 4 + 2)]; buf[1] = bdata[(x * 4 + 1)]; buf[2] = bdata[(x * 4 + 0)]; buf[3] = bdata[(x * 4 + 3)]; break; } //std::cerr << "[tga] x = " << x << "\n"; if (x == 0) { // initial encoder state rlp = false; rlcount = 0; rawcount = 1; continue; // nothing to work with yet (need 2 pixels) } if (rlp) { // in the middle of a run-length packet // flush the packet if the run ends or max packet size is hit if (rlcount < 0x80 && buf[0] == buf2[0] && buf[1] == buf2[1] && buf[2] == buf2[2] && buf[3] == buf2[3]) rlcount++; else { // run broken or max size hit, flush RL packet and start // a new raw one flush_rlp (&buf2[0], rlcount); // count raw pixel rawcount++; // reset state rlcount -= 0x80; if (rlcount < 0) rlcount = 0; rlp = false; } } else { // in the middle of a raw data packet if (rawcount > 0 // make sure we have material to check && buf[0] == buf2[0] && buf[1] == buf2[1] && buf[2] == buf2[2] && buf[3] == buf2[3]) { // run continues, possibly material for RLE if (rlcount == 0) { // join the previous pixel into the run rawcount--; rlcount++; } rlcount++; } else { // run broken // apart from the pixel we've just read, add any remaining // ones we may have considered for RLE rawcount += 1 + rlcount; rlcount = 0; // flush the packet if max packet size is hit if (rawcount >= 0x80) { // subtract 128 instead of setting to 0 because there // is a chance that rawcount is now > 128; if so, we'll // catch the remainder in the next iteration rawcount -= 0x80; flush_rawp (bdata, 0x80, x - 0x80 + 1); } } // check the encoding profitability condition //if (rlcount * m_spec.nchannels > 1 + m_spec.nchannels) { // NOTE: the condition below is valid, nchannels can be 1 if (rlcount > 1 + 1 / m_spec.nchannels) { // flush a packet of what we had so far flush_rawp (bdata, rawcount, x - rawcount - rlcount + 1); // reset state rawcount = 0; // mark this as a run-length packet rlp = true; } } } // flush anything that may be left if (rlp) flush_rlp (&buf2[0], rlcount); else { rawcount += rlcount; flush_rawp (bdata, rawcount, m_spec.width - rawcount); } } else { // raw, non-compressed data // seek to the correct scanline int n = m_spec.nchannels; int w = m_spec.width; fseek(m_file, 18 + m_idlen + (m_spec.height - y - 1) * w * n, SEEK_SET); if (n <= 2) { // 1- and 2-channels can write directly if (!fwrite (bdata, n, w)) { return false; } } else { // 3- and 4-channels must swap R and B std::vector buf; buf.assign (bdata, bdata + n*w); for (int x = 0; x < m_spec.width; x++) std::swap (buf[x*n], buf[x*n+2]); if (!fwrite (&buf[0], n, w)) { return false; } } } return true; } bool TGAOutput::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { // Emulate tiles by buffering the whole image return copy_tile_to_image_buffer (x, y, z, format, data, xstride, ystride, zstride, &m_tilebuffer[0]); } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/cmake/0000755000175000017500000000000013151711064016174 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/cmake/oiio_macros.cmake0000644000175000017500000001473513151711064021513 0ustar mfvmfv# Macro to install targets to the appropriate locations. Use this instead of # the install(TARGETS ...) signature. # # Usage: # # oiio_install_targets (target1 [target2 ...]) # macro (oiio_install_targets) install (TARGETS ${ARGN} RUNTIME DESTINATION "${BIN_INSTALL_DIR}" COMPONENT user LIBRARY DESTINATION "${LIB_INSTALL_DIR}" COMPONENT user ARCHIVE DESTINATION "${LIB_INSTALL_DIR}" COMPONENT developer) endmacro () # Macro to add a build target for an IO plugin. # # Usage: # # add_oiio_plugin ( source1 [source2 ...] # [ INCLUDE_DIRS include_dir1 ... ] # [ LINK_LIBRARIES external_lib1 ... ] # [ DEFINITIONS -DFOO=bar ... ]) # # The plugin name is deduced from the name of the current directory and the # source is automatically linked against OpenImageIO. Additional include # directories (just for this target) may be specified after the optional # INCLUDE_DIRS keyword. Additional libraries (for example, libpng) may be # specified after the optionl LINK_LIBRARIES keyword. Additional # preprocessor definitions may be specified after the optional DEFINITIONS # keyword. # # What goes on under the covers is quite different depending on whether # EMBEDPLUGINS is 0 or 1. If EMBEDPLUGINS is 0 (in which case this is # expected to be called *after* the OpenImageIO target is declared), it will # create a new target to build the full plugin. On the other hand, if # EMBEDPLUGINS is 1 (in which case this should be called *before* the # OpenImageIO target is declared), it will merely append the required # definitions, includs, and libraries to lists format_plugin_blah that will # be handed off too the setup of the later OpenImageIO target. # macro (add_oiio_plugin) if (CMAKE_VERSION VERSION_LESS 2.8.3) parse_arguments (_plugin "LINK_LIBRARIES;INCLUDE_DIRS;DEFINITIONS" "" ${ARGN}) set (_plugin_UNPARSED_ARGUMENTS ${_plugin_DEFAULT_ARGS}) else () # Modern cmake has this functionality built-in cmake_parse_arguments (_plugin "" "" "INCLUDE_DIRS;LINK_LIBRARIES;DEFINITIONS" ${ARGN}) # Arguments: endif () if (EMBEDPLUGINS) set (_target_name OpenImageIO) # Add each source file to the libOpenImageIO_srcs, but it takes some # bending over backwards to change it in the parent scope. set (_plugin_all_source ${libOpenImageIO_srcs}) foreach (_plugin_source_file ${_plugin_UNPARSED_ARGUMENTS}) list (APPEND _plugin_all_source "${CMAKE_CURRENT_SOURCE_DIR}/${_plugin_source_file}") endforeach () set (libOpenImageIO_srcs "${_plugin_all_source}" PARENT_SCOPE) set (format_plugin_definitions ${format_plugin_definitions} ${_plugin_DEFINITIONS} PARENT_SCOPE) set (format_plugin_include_dirs ${format_plugin_include_dirs} ${_plugin_INCLUDE_DIRS} PARENT_SCOPE) set (format_plugin_libs ${format_plugin_libs} ${_plugin_LINK_LIBRARIES} PARENT_SCOPE) else () # Get the name of the current directory and use it as the target name. set (_target_name ${CMAKE_CURRENT_SOURCE_DIR}) get_filename_component (_target_name ${CMAKE_CURRENT_SOURCE_DIR} NAME) add_library (${_target_name} SHARED ${_plugin_UNPARSED_ARGUMENTS}) add_definitions (${_plugin_DEFINITIONS}) include_directories (${_target_name} PRIVATE ${_plugin_INCLUDE_DIRS}) target_link_libraries (${_target_name} OpenImageIO ${_plugin_LINK_LIBRARIES}) set_target_properties (${_target_name} PROPERTIES PREFIX "" FOLDER "Plugins") oiio_install_targets (${_target_name}) endif () endmacro () # oiio_add_tests() - add a set of test cases. # # Usage: # oiio_add_tests ( test1 [ test2 ... ] # [ IMAGEDIR name_of_reference_image_directory ] # [ URL http://find.reference.cases.here.com ] ) # # The optional argument IMAGEDIR is used to check whether external test images # (not supplied with OIIO) are present, and to disable the test cases if # they're not. If IMAGEDIR is present, URL should also be included to tell # the user where to find such tests. # macro (oiio_add_tests) parse_arguments (_ats "URL;IMAGEDIR;LABEL;FOUNDVAR" "" ${ARGN}) set (_ats_testdir "${PROJECT_SOURCE_DIR}/../${_ats_IMAGEDIR}") # If there was a FOUNDVAR param specified and that variable name is # not defined, mark the test as broken. if (_ats_FOUNDVAR AND NOT ${_ats_FOUNDVAR}) set (_ats_LABEL "broken") endif () if (_ats_IMAGEDIR AND NOT EXISTS ${_ats_testdir}) # If the directory containig reference data (images) for the test # isn't found, point the user at the URL. message (STATUS "\n\nDid not find ${_ats_testdir}") message (STATUS " -> Will not run tests ${_ats_DEFAULT_ARGS}") message (STATUS " -> You can find it at ${_ats_URL}\n") else () # Add the tests if all is well. if (DEFINED CMAKE_VERSION AND NOT CMAKE_VERSION VERSION_LESS 2.8) set (_has_generator_expr TRUE) endif () foreach (_testname ${_ats_DEFAULT_ARGS}) set (_testsrcdir "${CMAKE_SOURCE_DIR}/testsuite/${_testname}") set (_testdir "${CMAKE_BINARY_DIR}/testsuite/${_testname}") if (_ats_LABEL MATCHES "broken") set (_testname "${_testname}-broken") endif () if (_has_generator_expr) set (_add_test_args NAME ${_testname} # WORKING_DIRECTORY ${_testdir} COMMAND python) if (MSVC_IDE) set (_extra_test_args --devenv-config $ --solution-path "${PROJECT_BINARY_DIR}" ) else () set (_extra_test_args "") endif () else () set (_add_test_args ${_testname} python) set (_extra_test_args "") endif () if (VERBOSE) message (STATUS "TEST ${_testname}: ${CMAKE_BINARY_DIR}/testsuite/runtest.py ${_testdir} ${_extra_test_args}") endif () # Make the build test directory and copy file (MAKE_DIRECTORY "${_testdir}") add_test (${_add_test_args} "${CMAKE_SOURCE_DIR}/testsuite/runtest.py" ${_testdir} ${_extra_test_args}) endforeach () endif () endmacro () openimageio-1.7.17~dfsg0.orig/src/cmake/modules/0000755000175000017500000000000013151711064017644 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/cmake/modules/FindPugiXML.cmake0000644000175000017500000000202113151711064022727 0ustar mfvmfv# Find the pugixml XML parsing library. # # Sets the usual variables expected for find_package scripts: # # PUGIXML_INCLUDE_DIR - header location # PUGIXML_LIBRARIES - library to link against # PUGIXML_FOUND - true if pugixml was found. find_path (PUGIXML_INCLUDE_DIR NAMES pugixml.hpp PATHS ${PUGIXML_HOME}/include) find_library (PUGIXML_LIBRARY NAMES pugixml PATHS ${PUGIXML_HOME}/lib) # Support the REQUIRED and QUIET arguments, and set PUGIXML_FOUND if found. include (FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS (PugiXML DEFAULT_MSG PUGIXML_LIBRARY PUGIXML_INCLUDE_DIR) if (PUGIXML_FOUND) set (PUGIXML_LIBRARIES ${PUGIXML_LIBRARY}) if (NOT PugiXML_FIND_QUIETLY) message (STATUS "PugiXML include = ${PUGIXML_INCLUDE_DIR}") message (STATUS "PugiXML library = ${PUGIXML_LIBRARY}") endif () else () message (STATUS "No PugiXML found") endif() mark_as_advanced (PUGIXML_LIBRARY PUGIXML_INCLUDE_DIR) openimageio-1.7.17~dfsg0.orig/src/cmake/modules/FindFreetype.cmake0000644000175000017500000000321613151711064023234 0ustar mfvmfv# Module to find FreeType # # This module will first look into the directories defined by the variables: # - FREETYPE_PATH, FREETYPE_INCLUDE_PATH, FREETYPE_LIBRARY_PATH # # This module defines the following variables: # # FREETYPE_FOUND - True if Freetype was found. # FREETYPE_INCLUDE_DIRS - where to find Freetype.h # FREETYPE_LIBRARIES - list of libraries to link against when using Freetype # Other standarnd issue macros include (FindPackageHandleStandardArgs) include (FindPackageMessage) FIND_PATH (FREETYPE_INCLUDE_DIRS ft2build.h ${FREETYPE_INCLUDE_PATH} ${FREETYPE_PATH}/include/ /usr/include /usr/include/freetype2 /usr/include/freetype2/freetype /usr/local/include /usr/local/include/freetype2 /usr/local/include/freetype2/freetype /sw/include /opt/local/include DOC "The directory where freetype.h resides") FIND_LIBRARY (FREETYPE_LIBRARIES NAMES FREETYPE freetype PATHS ${FREETYPE_LIBRARY_PATH} ${FREETYPE_PATH}/lib/ /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib /sw/lib /opt/local/lib DOC "The FREETYPE library") if (FREETYPE_INCLUDE_DIRS AND FREETYPE_LIBRARIES) set (FREETYPE_FOUND TRUE) list (APPEND FREETYPE_INCLUDE_DIRS ${FREETYPE_INCLUDE_DIRS}/freetype2 ${FREETYPE_INCLUDE_DIRS}/freetype2/freetype) if (VERBOSE) message (STATUS "Found FREETYPE library ${FREETYPE_LIBRARIES}") message (STATUS "Found FREETYPE includes ${FREETYPE_INCLUDE_DIRS}") endif () else() set (FREETYPE_FOUND FALSE) message (STATUS "FREETYPE not found. Specify FREETYPE_PATH to locate it") endif() openimageio-1.7.17~dfsg0.orig/src/cmake/modules/FindOpenCV.cmake0000644000175000017500000000567213151711064022613 0ustar mfvmfv# - Find OpenCV # Find the OpenCV library # This module defines # OpenCV_VERSION, the version string of OpenCV # OpenCV_INCLUDE_DIR, where to find header files # OpenCV_LIBRARIES, the libraries needed to use OpenCV # OpenCV_DEFINITIONS, the definitions needed to use OpenCV FIND_PACKAGE(PkgConfig) IF(PKG_CONFIG_FOUND AND NOT LIBRAW_PATH) PKG_CHECK_MODULES(PC_LIBRAW QUIET libraw) SET(LibRaw_DEFINITIONS ${PC_LIBRAW_CFLAGS_OTHER}) PKG_CHECK_MODULES(PC_LIBRAW_R QUIET libraw_r) SET(LibRaw_r_DEFINITIONS ${PC_LIBRAW_R_CFLAGS_OTHER}) ENDIF() find_path (OpenCV_INCLUDE_DIR opencv/cv.h "${PROJECT_SOURCE_DIR}/include" "${OpenCV_DIR}/include" "$ENV{OpenCV_DIR}/include" /usr/local/include /opt/local/include /usr/local/opt/opencv3/include ) if (OpenCV_INCLUDE_DIR AND EXISTS "${OpenCV_INCLUDE_DIR}/opencv2/core/version.hpp") file (STRINGS "${OpenCV_INCLUDE_DIR}/opencv2/core/version.hpp" TMP REGEX "^#define CV_VERSION_EPOCH .*$") if (TMP) string (REGEX MATCHALL "[0-9]+" CV_VERSION_EPOCH ${TMP}) endif () file (STRINGS "${OpenCV_INCLUDE_DIR}/opencv2/core/version.hpp" TMP REGEX "^#define CV_VERSION_MAJOR .*$") string (REGEX MATCHALL "[0-9]+" CV_VERSION_MAJOR ${TMP}) file (STRINGS "${OpenCV_INCLUDE_DIR}/opencv2/core/version.hpp" TMP REGEX "^#define CV_VERSION_MINOR .*$") string (REGEX MATCHALL "[0-9]+" CV_VERSION_MINOR ${TMP}) file (STRINGS "${OpenCV_INCLUDE_DIR}/opencv2/core/version.hpp" TMP REGEX "^#define CV_VERSION_REVISION .*$") string (REGEX MATCHALL "[0-9]+" CV_VERSION_REVISION ${TMP}) if (CV_VERSION_EPOCH) set (OpenCV_VERSION "${CV_VERSION_EPOCH}.${CV_VERSION_MAJOR}.${CV_VERSION_MINOR}") else () set (OpenCV_VERSION "${CV_VERSION_MAJOR}.${CV_VERSION_MINOR}.${CV_VERSION_REVISION}") endif () endif () set (libdirs "${PROJECT_SOURCE_DIR}/lib" "${OpenCV_DIR}/lib" "$ENV{OpenCV_DIR}/lib" /usr/local/lib /opt/local/lib /usr/local/opt/opencv3/lib ) set (opencv_components opencv_highgui opencv_imgproc opencv_core) if (NOT ${OpenCV_VERSION} VERSION_LESS 3.0.0) set (opencv_components opencv_videoio ${opencv_components}) endif () foreach (component ${opencv_components}) find_library (${component}_lib NAMES ${component} PATHS ${libdirs} ) if (${component}_lib) set (OpenCV_LIBS ${OpenCV_LIBS} ${${component}_lib}) endif () endforeach () if (OpenCV_INCLUDE_DIR AND OpenCV_LIBS) set (OpenCV_FOUND TRUE) endif () include (FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS (OpenCV REQUIRED_VARS OpenCV_LIBS OpenCV_INCLUDE_DIR VERSION_VAR OpenCV_VERSION ) MARK_AS_ADVANCED (OpenCV_VERSION OpenCV_INCLUDE_DIR OpenCV_LIBS OpenCV_DEFINITIONS ) openimageio-1.7.17~dfsg0.orig/src/cmake/modules/FindOpenJpeg.cmake0000644000175000017500000001241013151711064023154 0ustar mfvmfv# Module to find OpenJpeg. # # This module will first look into the directories defined by the variables: # - OPENJPEG_HOME # # This module defines the following variables: # # OPENJPEG_INCLUDE_DIR - where to find openjpeg.h # OPENJPEG_LIBRARIES - list of libraries to link against when using OpenJpeg. # OPENJPEG_FOUND - True if OpenJpeg was found. # OPENJPEG_VERSION - Set to the OpenJPEG version found include (FindPackageHandleStandardArgs) include (FindPackageMessage) include (SelectLibraryConfigurations) macro (PREFIX_FIND_INCLUDE_DIR prefix includefile libpath_var) string (TOUPPER ${prefix}_INCLUDE_DIR tmp_varname) find_path(${tmp_varname} ${includefile} PATHS ${${libpath_var}} PATH_SUFFIXES include NO_DEFAULT_PATH ) if (${tmp_varname}) mark_as_advanced (${tmp_varname}) endif () unset (tmp_varname) endmacro () macro (PREFIX_FIND_LIB prefix libname libpath_var liblist_var cachelist_var) string (TOUPPER ${prefix}_${libname} tmp_prefix) find_library(${tmp_prefix}_LIBRARY_RELEASE NAMES ${libname} PATHS ${${libpath_var}} PATH_SUFFIXES lib NO_DEFAULT_PATH ) find_library(${tmp_prefix}_LIBRARY_DEBUG NAMES ${libname}d ${libname}_d ${libname}debug ${libname}_debug PATHS ${${libpath_var}} PATH_SUFFIXES lib NO_DEFAULT_PATH ) # Properly define ${tmp_prefix}_LIBRARY (cached) and ${tmp_prefix}_LIBRARIES select_library_configurations (${tmp_prefix}) list (APPEND ${liblist_var} ${tmp_prefix}_LIBRARIES) # Add to the list of variables which should be reset list (APPEND ${cachelist_var} ${tmp_prefix}_LIBRARY ${tmp_prefix}_LIBRARY_RELEASE ${tmp_prefix}_LIBRARY_DEBUG) mark_as_advanced ( ${tmp_prefix}_LIBRARY ${tmp_prefix}_LIBRARY_RELEASE ${tmp_prefix}_LIBRARY_DEBUG) unset (tmp_prefix) endmacro () # Generic search paths set (OpenJpeg_include_paths /usr/local/include/openjpeg-2.2 /usr/local/include/openjpeg-2.1 /usr/local/include/openjpeg-2.0 /usr/local/include/openjpeg /usr/local/include /usr/include/openjpeg /usr/include/openjpeg-1.5 /usr/local/include/openjpeg-1.5 /usr/include /opt/local/include) set (OpenJpeg_library_paths /usr/lib /usr/lib/${CMAKE_LIBRARY_ARCHITECTURE} /usr/local/lib /sw/lib /opt/local/lib) if (OPENJPEG_HOME) set (OpenJpeg_library_paths ${OPENJPEG_HOME}/lib ${OPENJPEG_HOME}/lib64 ${OPENJPEG_HOME}/bin ${OpenJpeg_library_paths} ) set (OpenJpeg_include_paths ${OPENJPEG_HOME}/include/openjpeg-2.2 ${OPENJPEG_HOME}/include/openjpeg-2.1 ${OPENJPEG_HOME}/include/openjpeg-2.0 ${OPENJPEG_HOME}/include/openjpeg ${OPENJPEG_HOME}/include ${OpenJpeg_include_paths} ) endif() # Locate the header files PREFIX_FIND_INCLUDE_DIR (OpenJpeg openjpeg.h OpenJpeg_include_paths) # If the headers were found, add its parent to the list of lib directories if (OPENJPEG_INCLUDE_DIR) get_filename_component (tmp_extra_dir "${OPENJPEG_INCLUDE_DIR}/../" ABSOLUTE) list (APPEND OpenJPEG_library_paths ${tmp_extra_dir}) unset (tmp_extra_dir) endif () # Search for opj_config.h -- it is only part of OpenJpeg >= 2.0, and will # contain symbols OPJ_VERSION_MAJOR and OPJ_VERSION_MINOR. If the file # doesn't exist, we're dealing with OpenJPEG 1.x. # Note that for OpenJPEG 2.x, the library is named libopenjp2, not # libopenjpeg (which is for 1.x) set (OPENJPEG_CONFIG_FILE "${OPENJPEG_INCLUDE_DIR}/opj_config.h") if (EXISTS "${OPENJPEG_CONFIG_FILE}") file(STRINGS "${OPENJPEG_CONFIG_FILE}" TMP REGEX "^#define OPJ_PACKAGE_VERSION .*$") if (TMP) # 2.0 is the only one with this construct set (OPJ_VERSION_MAJOR 2) set (OPJ_VERSION_MINOR 0) else () # 2.1 and beyond file(STRINGS "${OPENJPEG_CONFIG_FILE}" TMP REGEX "^#define OPJ_VERSION_MAJOR .*$") string (REGEX MATCHALL "[0-9]+" OPJ_VERSION_MAJOR ${TMP}) file(STRINGS "${OPENJPEG_CONFIG_FILE}" TMP REGEX "^#define OPJ_VERSION_MINOR .*$") string (REGEX MATCHALL "[0-9]+" OPJ_VERSION_MINOR ${TMP}) endif () else () # Guess OpenJPEG 1.5 -- older versions didn't have the version readily # apparent in the headers. set (OPJ_VERSION_MAJOR 1) set (OPJ_VERSION_MINOR 5) endif () set (OPENJPEG_VERSION "${OPJ_VERSION_MAJOR}.${OPJ_VERSION_MINOR}") # Locate the OpenJpeg library set (OpenJpeg_libvars "") set (OpenJpeg_cachevars "") if ("${OPENJPEG_VERSION}" VERSION_LESS 2.0) PREFIX_FIND_LIB (OpenJpeg openjpeg OpenJpeg_library_paths OpenJpeg_libvars OpenJpeg_cachevars) else () PREFIX_FIND_LIB (OpenJpeg openjp2 OpenJpeg_library_paths OpenJpeg_libvars OpenJpeg_cachevars) endif () # Use the standard function to handle OPENJPEG_FOUND FIND_PACKAGE_HANDLE_STANDARD_ARGS (OpenJpeg DEFAULT_MSG OPENJPEG_INCLUDE_DIR ${OpenJpeg_libvars}) if (OPENJPEG_FOUND) set (OPENJPEG_LIBRARIES "") foreach (tmplib ${OpenJpeg_libvars}) list (APPEND OPENJPEG_LIBRARIES ${${tmplib}}) endforeach () if (NOT OpenJpeg_FIND_QUIETLY) FIND_PACKAGE_MESSAGE (OPENJPEG "Found OpenJpeg: v${OPENJPEG_VERSION} ${OPENJPEG_LIBRARIES}" "[${OPENJPEG_INCLUDE_DIR}][${OPENJPEG_LIBRARIES}]" ) endif () endif () unset (OpenJpeg_include_paths) unset (OpenJpeg_library_paths) unset (OpenJpeg_libvars) unset (OpenJpeg_cachevars) openimageio-1.7.17~dfsg0.orig/src/cmake/modules/FindOpenColorIO.cmake0000644000175000017500000000357013151711064023604 0ustar mfvmfv# Module to find OpenColorIO # # This module will first look into the directories defined by the variables: # - OCIO_PATH, OCIO_INCLUDE_PATH, OCIO_LIBRARY_PATH # # This module defines the following variables: # # OCIO_FOUND - True if OpenColorIO was found. # OCIO_INCLUDES - where to find OpenColorIO.h # OCIO_LIBRARIES - list of libraries to link against when using OpenColorIO # Other standarnd issue macros include (FindPackageHandleStandardArgs) include (FindPackageMessage) if (NOT OpenColorIO_FIND_QUIETLY) if (OCIO_PATH) message(STATUS "OCIO path explicitly specified: ${OCIO_PATH}") endif() if (OCIO_INCLUDE_PATH) message(STATUS "OCIO INCLUDE_PATH explicitly specified: ${OCIO_INCLUDE_PATH}") endif() if (OCIO_LIBRARY_PATH) message(STATUS "OCIO LIBRARY_PATH explicitly specified: ${OCIO_LIBRARY_PATH}") endif() endif () FIND_PATH(OCIO_INCLUDES OpenColorIO/OpenColorIO.h PATHS ${OCIO_INCLUDE_PATH} ${OCIO_PATH}/include/ /usr/include /usr/local/include /sw/include /opt/local/include DOC "The directory where OpenColorIO/OpenColorIO.h resides") FIND_LIBRARY(OCIO_LIBRARIES NAMES OCIO OpenColorIO PATHS ${OCIO_LIBRARY_PATH} ${OCIO_PATH}/lib/ /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib /sw/lib /opt/local/lib DOC "The OCIO library") if(OCIO_INCLUDES AND OCIO_LIBRARIES) set(OCIO_FOUND TRUE) if (NOT OpenColorIO_FIND_QUIETLY) message(STATUS "Found OCIO library ${OCIO_LIBRARIES}") message(STATUS "Found OCIO includes ${OCIO_INCLUDES}") endif () else() set(OCIO_FOUND FALSE) message(STATUS "OCIO not found. Specify OCIO_PATH to locate it") endif() openimageio-1.7.17~dfsg0.orig/src/cmake/modules/FindFFmpeg.cmake0000644000175000017500000000522413151711064022616 0ustar mfvmfv# - Try to find ffmpeg libraries (libavcodec, libavformat and libavutil) # Once done this will define # # FFMPEG_FOUND - system has ffmpeg or libav # FFMPEG_INCLUDE_DIR - the ffmpeg include directory # FFMPEG_LIBRARIES - Link these to use ffmpeg # FFMPEG_LIBAVCODEC # FFMPEG_LIBAVFORMAT # FFMPEG_LIBAVUTIL # # Copyright (c) 2008 Andreas Schneider # Modified for other libraries by Lasse Kärkkäinen # Modified for Hedgewars by Stepik777 # # Redistribution and use is allowed according to the terms of the New # BSD license. # if (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR) # in cache already set(FFMPEG_FOUND TRUE) else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR) # use pkg-config to get the directories and then use these values # in the FIND_PATH() and FIND_LIBRARY() calls find_package(PkgConfig) if (PKG_CONFIG_FOUND) pkg_check_modules(_FFMPEG_AVCODEC QUIET libavcodec) pkg_check_modules(_FFMPEG_AVFORMAT QUIET libavformat) pkg_check_modules(_FFMPEG_AVUTIL QUIET libavutil) pkg_check_modules(_FFMPEG_SWSCALE QUIET libswscale) endif (PKG_CONFIG_FOUND) find_path(FFMPEG_AVCODEC_INCLUDE_DIR NAMES libavcodec/version.h PATHS ${_FFMPEG_AVCODEC_INCLUDE_DIRS} /usr/include /usr/local/include /opt/local/include /sw/include PATH_SUFFIXES ffmpeg libav ) find_library(FFMPEG_LIBAVCODEC NAMES avcodec PATHS ${_FFMPEG_AVCODEC_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib ) find_library(FFMPEG_LIBAVFORMAT NAMES avformat PATHS ${_FFMPEG_AVFORMAT_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib ) find_library(FFMPEG_LIBAVUTIL NAMES avutil PATHS ${_FFMPEG_AVUTIL_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib ) find_library(FFMPEG_LIBSWSCALE NAMES swscale PATHS ${_FFMPEG_SWSCALE_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib ) if (FFMPEG_LIBAVCODEC AND FFMPEG_LIBAVFORMAT AND FFMPEG_AVCODEC_INCLUDE_DIR) set(FFMPEG_FOUND TRUE) endif() if (FFMPEG_FOUND) set(FFMPEG_INCLUDE_DIR ${FFMPEG_AVCODEC_INCLUDE_DIR}) set(FFMPEG_LIBRARIES ${FFMPEG_LIBAVCODEC} ${FFMPEG_LIBAVFORMAT} ${FFMPEG_LIBAVUTIL} ${FFMPEG_LIBSWSCALE} ) endif (FFMPEG_FOUND) if (FFMPEG_FOUND) if (NOT FFmpeg_FIND_QUIETLY) message(STATUS "Found FFMPEG or Libav: ${FFMPEG_LIBRARIES}, ${FFMPEG_INCLUDE_DIR}") endif (NOT FFmpeg_FIND_QUIETLY) else (FFMPEG_FOUND) if (FFMPEG_FIND_REQUIRED) message(FATAL_ERROR "Could not find libavcodec or libavformat or libavutil or libswscale") endif (FFMPEG_FIND_REQUIRED) endif (FFMPEG_FOUND) endif (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR) openimageio-1.7.17~dfsg0.orig/src/cmake/modules/FindPTex.cmake0000644000175000017500000000773313151711064022341 0ustar mfvmfv # Copyright Disney Enterprises, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License # and the following modification to it: Section 6 Trademarks. # deleted and replaced with: # # 6. Trademarks. This License does not grant permission to use the # trade names, trademarks, service marks, or product names of the # Licensor and its affiliates, except as required for reproducing # the content of the NOTICE file. # # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 if (WIN32) find_path( PTEX_INCLUDE_DIR NAMES Ptexture.h HINTS "${PTEX_LOCATION}/include" "$ENV{PTEX_LOCATION}/include" PATHS "$ENV{PROGRAMFILES}/Ptex/include" /usr/include DOC "The directory where Ptexture.h resides") find_library( PTEX_LIBRARY NAMES Ptex32 Ptex32s Ptex HINTS "${PTEX_LOCATION}/lib64" "${PTEX_LOCATION}/lib" "$ENV{PTEX_LOCATION}/lib64" "$ENV{PTEX_LOCATION}/lib" PATHS "$ENV{PROGRAMFILES}/Ptex/lib" /usr/lib64 /usr/lib /usr/lib/w32api /usr/local/lib64 /usr/local/lib DOC "The Ptex library") elseif (APPLE) find_path( PTEX_INCLUDE_DIR NAMES Ptexture.h HINTS "${PTEX_LOCATION}/include" "$ENV{PTEX_LOCATION}/include" PATHS DOC "The directory where Ptexture.h resides") find_library( PTEX_LIBRARY NAMES Ptex libPtex.a PATHS "${PTEX_LOCATION}/lib" "$ENV{PTEX_LOCATION}/lib" DOC "The Ptex Library") else () find_path( PTEX_INCLUDE_DIR NAMES Ptexture.h HINTS "${PTEX_LOCATION}/include" "${PTEX_LOCATION}/include/wdas" "$ENV{PTEX_LOCATION}/include" "$ENV{PTEX_LOCATION}/include/wdas" PATHS /usr/include /usr/local/include DOC "The directory where Ptexture.h resides") find_library( PTEX_LIBRARY NAMES Ptex wdasPtex HINTS "${PTEX_LOCATION}/lib64" "${PTEX_LOCATION}/lib" "$ENV{PTEX_LOCATION}/lib64" "$ENV{PTEX_LOCATION}/lib" PATHS /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib DOC "The Ptex library") endif () if (PTEX_INCLUDE_DIR AND EXISTS "${PTEX_INCLUDE_DIR}/PtexVersion.h" ) file(STRINGS "${PTEX_INCLUDE_DIR}/PtexVersion.h" TMP REGEX "^#define PtexAPIVersion.*$") string(REGEX MATCHALL "[0-9]+" API ${TMP}) file(STRINGS "${PTEX_INCLUDE_DIR}/PtexVersion.h" TMP REGEX "^#define PtexFileMajorVersion.*$") string(REGEX MATCHALL "[0-9]+" MAJOR ${TMP}) file(STRINGS "${PTEX_INCLUDE_DIR}/PtexVersion.h" TMP REGEX "^#define PtexFileMinorVersion.*$") string(REGEX MATCHALL "[0-9]+" MINOR ${TMP}) set(PTEX_VERSION ${API}.${MAJOR}.${MINOR}) elseif (PTEX_INCLUDE_DIR AND EXISTS "${PTEX_INCLUDE_DIR}/Ptexture.h" ) file(STRINGS "${PTEX_INCLUDE_DIR}/Ptexture.h" TMP REGEX "^#define PtexAPIVersion.*$") string(REGEX MATCHALL "[0-9]+" API ${TMP}) file(STRINGS "${PTEX_INCLUDE_DIR}/Ptexture.h" TMP REGEX "^#define PtexFileMajorVersion.*$") string(REGEX MATCHALL "[0-9]+" MAJOR ${TMP}) file(STRINGS "${PTEX_INCLUDE_DIR}/Ptexture.h" TMP REGEX "^#define PtexFileMinorVersion.*$") string(REGEX MATCHALL "[0-9]+" MINOR ${TMP}) set(PTEX_VERSION ${API}.${MAJOR}.${MINOR}) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(PTex REQUIRED_VARS PTEX_INCLUDE_DIR PTEX_LIBRARY VERSION_VAR PTEX_VERSION ) if (PTEX_FOUND) set(PTEX_LIBRARIES ${PTEX_LIBRARY}) endif(PTEX_FOUND) mark_as_advanced( PTEX_INCLUDE_DIR PTEX_LIBRARY ) openimageio-1.7.17~dfsg0.orig/src/cmake/modules/FindLibRaw.cmake0000644000175000017500000000573113151711064022635 0ustar mfvmfv# - Find LibRaw # Find the LibRaw library # This module defines # LibRaw_VERSION_STRING, the version string of LibRaw # LibRaw_INCLUDE_DIR, where to find libraw.h # LibRaw_LIBRARIES, the libraries needed to use LibRaw (non-thread-safe) # LibRaw_r_LIBRARIES, the libraries needed to use LibRaw (thread-safe) # LibRaw_DEFINITIONS, the definitions needed to use LibRaw (non-thread-safe) # LibRaw_r_DEFINITIONS, the definitions needed to use LibRaw (thread-safe) # # Copyright (c) 2013, Pino Toscano # Copyright (c) 2013, Gilles Caulier # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. FIND_PACKAGE(PkgConfig) IF(PKG_CONFIG_FOUND AND NOT LIBRAW_PATH) PKG_CHECK_MODULES(PC_LIBRAW QUIET libraw) SET(LibRaw_DEFINITIONS ${PC_LIBRAW_CFLAGS_OTHER}) PKG_CHECK_MODULES(PC_LIBRAW_R QUIET libraw_r) SET(LibRaw_r_DEFINITIONS ${PC_LIBRAW_R_CFLAGS_OTHER}) ENDIF() FIND_PATH(LibRaw_INCLUDE_DIR libraw/libraw.h HINTS ${PC_LIBRAW_INCLUDEDIR} ${PC_LibRaw_INCLUDE_DIRS} PATH_SUFFIXES libraw ) FIND_LIBRARY(LibRaw_LIBRARIES NAMES raw HINTS ${PC_LIBRAW_LIBDIR} ${PC_LIBRAW_LIBRARY_DIRS} ) FIND_LIBRARY(LibRaw_r_LIBRARIES NAMES raw_r HINTS ${PC_LIBRAW_R_LIBDIR} ${PC_LIBRAW_R_LIBRARY_DIRS} ) IF(LibRaw_INCLUDE_DIR) FILE(READ ${LibRaw_INCLUDE_DIR}/libraw/libraw_version.h _libraw_version_content) STRING(REGEX MATCH "#define LIBRAW_MAJOR_VERSION[ \t]*([0-9]*)\n" _version_major_match ${_libraw_version_content}) SET(_libraw_version_major "${CMAKE_MATCH_1}") STRING(REGEX MATCH "#define LIBRAW_MINOR_VERSION[ \t]*([0-9]*)\n" _version_minor_match ${_libraw_version_content}) SET(_libraw_version_minor "${CMAKE_MATCH_1}") STRING(REGEX MATCH "#define LIBRAW_PATCH_VERSION[ \t]*([0-9]*)\n" _version_patch_match ${_libraw_version_content}) SET(_libraw_version_patch "${CMAKE_MATCH_1}") IF(_version_major_match AND _version_minor_match AND _version_patch_match) SET(LibRaw_VERSION_STRING "${_libraw_version_major}.${_libraw_version_minor}.${_libraw_version_patch}") ELSE() IF(NOT LibRaw_FIND_QUIETLY) MESSAGE(STATUS "Failed to get version information from ${LibRaw_INCLUDE_DIR}/libraw/libraw_version.h") ENDIF() ENDIF() ENDIF() INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibRaw REQUIRED_VARS LibRaw_LIBRARIES LibRaw_INCLUDE_DIR VERSION_VAR LibRaw_VERSION_STRING ) MARK_AS_ADVANCED(LibRaw_VERSION_STRING LibRaw_INCLUDE_DIR LibRaw_LIBRARIES LibRaw_r_LIBRARIES LibRaw_DEFINITIONS LibRaw_r_DEFINITIONS ) openimageio-1.7.17~dfsg0.orig/src/cmake/modules/FindJPEGTurbo.cmake0000644000175000017500000000245413151711064023215 0ustar mfvmfv# - Try to find libjpeg-turbo # Once done, this will define # # JPEG_FOUND - system has libjpeg-turbo # JPEG_INCLUDE_DIRS - the libjpeg-turbo include directories # JPEG_LIBRARIES - link these to use libjpeg-turbo # include (FindPackageHandleStandardArgs) find_path(JPEG_INCLUDE_DIR turbojpeg.h PATHS ${JPEGTURBO_PATH}/include /usr/local/opt/jpeg-turbo/include) set(JPEG_NAMES ${JPEG_NAMES} jpeg libjpeg turbojpeg libturbojpeg) find_library(JPEG_LIBRARY NAMES ${JPEG_NAMES} PATHS ${JPEG_INCLUDE_DIR}/../lib ${JPEGTURBO_PATH}/lib64 ${JPEGTURBO_PATH}/lib /usr/local/opt/jpeg-turbo/lib /opt/libjpeg-turbo/lib64 /opt/libjpeg-turbo/lib NO_DEFAULT_PATH) if (NOT JPEG_LIBRARY) find_library(JPEG_LIBRARY NAMES ${JPEG_NAMES} PATHS ${JPEG_INCLUDE_DIR}/../lib ${JPEGTURBO_PATH}/lib /usr/local/opt/jpeg-turbo/lib) endif () # handle the QUIETLY and REQUIRED arguments and set JPEG_FOUND to TRUE if # all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS(JPEG DEFAULT_MSG JPEG_LIBRARY JPEG_INCLUDE_DIR) if(JPEG_FOUND) set(JPEG_LIBRARIES ${JPEG_LIBRARY}) endif() mark_as_advanced(JPEG_LIBRARY JPEG_INCLUDE_DIR ) openimageio-1.7.17~dfsg0.orig/src/cmake/modules/SelectLibraryConfigurations.cmake0000644000175000017500000000773113151711064026335 0ustar mfvmfv# select_library_configurations( basename ) # # This macro takes a library base name as an argument, and will choose good # values for basename_LIBRARY, basename_LIBRARIES, basename_LIBRARY_DEBUG, and # basename_LIBRARY_RELEASE depending on what has been found and set. If only # basename_LIBRARY_RELEASE is defined, basename_LIBRARY, basename_LIBRARY_DEBUG, # and basename_LIBRARY_RELEASE will be set to the release value. If only # basename_LIBRARY_DEBUG is defined, then basename_LIBRARY, # basename_LIBRARY_DEBUG and basename_LIBRARY_RELEASE will take the debug value. # # If the generator supports configuration types, then basename_LIBRARY and # basename_LIBRARIES will be set with debug and optimized flags specifying the # library to be used for the given configuration. If no build type has been set # or the generator in use does not support configuration types, then # basename_LIBRARY and basename_LIBRARIES will take only the release values. #============================================================================= # Copyright 2009 Kitware, Inc. # Copyright 2009 Will Dicharry # Copyright 2005-2009 Kitware, Inc. # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) # This macro was adapted from the FindQt4 CMake module and is maintained by Will # Dicharry . # Utility macro to check if one variable exists while another doesn't, and set # one that doesn't exist to the one that exists. macro( _set_library_name basename GOOD BAD ) if( ${basename}_LIBRARY_${GOOD} AND NOT ${basename}_LIBRARY_${BAD} ) set( ${basename}_LIBRARY_${BAD} ${${basename}_LIBRARY_${GOOD}} ) set( ${basename}_LIBRARY ${${basename}_LIBRARY_${GOOD}} ) set( ${basename}_LIBRARIES ${${basename}_LIBRARY_${GOOD}} ) endif( ${basename}_LIBRARY_${GOOD} AND NOT ${basename}_LIBRARY_${BAD} ) endmacro( _set_library_name ) macro( select_library_configurations basename ) # if only the release version was found, set the debug to be the release # version. _set_library_name( ${basename} RELEASE DEBUG ) # if only the debug version was found, set the release value to be the # debug value. _set_library_name( ${basename} DEBUG RELEASE ) if (${basename}_LIBRARY_DEBUG AND ${basename}_LIBRARY_RELEASE ) # if the generator supports configuration types or CMAKE_BUILD_TYPE # is set, then set optimized and debug options. if( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE ) set( ${basename}_LIBRARY optimized ${${basename}_LIBRARY_RELEASE} debug ${${basename}_LIBRARY_DEBUG} ) set( ${basename}_LIBRARIES optimized ${${basename}_LIBRARY_RELEASE} debug ${${basename}_LIBRARY_DEBUG} ) else( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE ) # If there are no configuration types or build type, just use # the release version set( ${basename}_LIBRARY ${${basename}_LIBRARY_RELEASE} ) set( ${basename}_LIBRARIES ${${basename}_LIBRARY_RELEASE} ) endif( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE ) endif( ${basename}_LIBRARY_DEBUG AND ${basename}_LIBRARY_RELEASE ) set( ${basename}_LIBRARY ${${basename}_LIBRARY} CACHE FILEPATH "The ${basename} library" ) if( ${basename}_LIBRARY ) set( ${basename}_FOUND TRUE ) endif( ${basename}_LIBRARY ) mark_as_advanced( ${basename}_LIBRARY ${basename}_LIBRARY_RELEASE ${basename}_LIBRARY_DEBUG ) endmacro( select_library_configurations ) openimageio-1.7.17~dfsg0.orig/src/cmake/modules/FindOpenEXR.cmake0000644000175000017500000001572013151711064022734 0ustar mfvmfv# Module to find OpenEXR. # # This module will set # OPENEXR_FOUND true, if found # OPENEXR_INCLUDE_DIR directory where headers are found # OPENEXR_LIBRARIES libraries for OpenEXR + IlmBase # ILMBASE_LIBRARIES libraries just IlmBase # OPENEXR_VERSION OpenEXR version (accurate for >= 2.0.0, # otherwise will just guess 1.6.1) # # Special inputs: # OPENEXR_CUSTOM_INCLUDE_DIR - custom location of headers # OPENEXR_CUSTOM_LIB_DIR - custom location of libraries # OPENEXR_CUSTOM_LIB_PREFIX - special snowflake library prefix # OPENEXR_CUSTOM_LIB_SUFFIX - special snowflake library suffix # # Other standard issue macros include (FindPackageHandleStandardArgs) include (SelectLibraryConfigurations) find_package (ZLIB REQUIRED) # Link with pthreads if required find_package (Threads) if (CMAKE_USE_PTHREADS_INIT) set (ILMBASE_PTHREADS ${CMAKE_THREAD_LIBS_INIT}) endif () # List of likely places to find the headers -- note priority override of # OPENEXR_CUSTOM_INCLUDE_DIR and ${OPENEXR_HOME}/include. # ILMBASE is needed in case ilmbase an openexr are installed in separate # directories, like NixOS does set (GENERIC_INCLUDE_PATHS ${OPENEXR_CUSTOM_INCLUDE_DIR} ${OPENEXR_HOME}/include ${ILMBASE_HOME}/include /usr/local/include /usr/include /usr/include/${CMAKE_LIBRARY_ARCHITECTURE} /sw/include /opt/local/include ) # Find the include file locations. We call find_path twice -- first using # only the custom paths, then if that fails, try the default paths only. # This seems to be the most robust way I can find to not get confused when # both system and custom libraries are present. find_path (ILMBASE_INCLUDE_PATH OpenEXR/IlmBaseConfig.h PATHS ${GENERIC_INCLUDE_PATHS} NO_DEFAULT_PATH) find_path (ILMBASE_INCLUDE_PATH OpenEXR/IlmBaseConfig.h) find_path (OPENEXR_INCLUDE_PATH OpenEXR/OpenEXRConfig.h PATHS ${GENERIC_INCLUDE_PATHS} NO_DEFAULT_PATH) find_path (OPENEXR_INCLUDE_PATH OpenEXR/OpenEXRConfig.h) # Try to figure out version number if (EXISTS "${OPENEXR_INCLUDE_PATH}/OpenEXR/ImfMultiPartInputFile.h") # Must be at least 2.0 file(STRINGS "${OPENEXR_INCLUDE_PATH}/OpenEXR/OpenEXRConfig.h" TMP REGEX "^#define OPENEXR_VERSION_STRING .*$") string (REGEX MATCHALL "[0-9]+[.0-9]+" OPENEXR_VERSION ${TMP}) file(STRINGS "${OPENEXR_INCLUDE_PATH}/OpenEXR/OpenEXRConfig.h" TMP REGEX "^#define OPENEXR_VERSION_MAJOR .*$") string (REGEX MATCHALL "[0-9]+" OPENEXR_VERSION_MAJOR ${TMP}) file(STRINGS "${OPENEXR_INCLUDE_PATH}/OpenEXR/OpenEXRConfig.h" TMP REGEX "^#define OPENEXR_VERSION_MINOR .*$") string (REGEX MATCHALL "[0-9]+" OPENEXR_VERSION_MINOR ${TMP}) else () # Assume an old one, predates 2.x that had versions set (OPENEXR_VERSION 1.6.1) set (OPENEXR_MAJOR 1) set (OPENEXR_MINOR 6) endif () # List of likely places to find the libraries -- note priority override of # OPENEXR_CUSTOM_LIB_DIR and ${OPENEXR_HOME}/lib. # If there's no OPENEXR_HOME or ILMBASE_HOME, then the path will point to # "/lib", which may not always be wanted/expected. if (OPENEXR_CUSTOM_LIB_DIR) set (GENERIC_LIBRARY_PATHS ${GENERIC_LIBRARY_PATHS} ${OPENEXR_CUSTOM_LIB_DIR}) endif() if (OPENEXR_HOME) set (GENERIC_LIBRARY_PATHS ${GENERIC_LIBRARY_PATHS} ${OPENEXR_HOME}) endif() if (ILMBASE_HOME) set (GENERIC_LIBRARY_PATHS ${GENERIC_LIBRARY_PATHS} ${ILMBASE_HOME}) endif() set (GENERIC_LIBRARY_PATHS ${GENERIC_LIBRARY_PATHS} ${OPENEXR_INCLUDE_PATH}/../lib ${ILMBASE_INCLUDE_PATH}/../lib /usr/local/lib /usr/local/lib/${CMAKE_LIBRARY_ARCHITECTURE} /usr/lib /usr/lib/${CMAKE_LIBRARY_ARCHITECTURE} /sw/lib /opt/local/lib $ENV{PROGRAM_FILES}/OpenEXR/lib/static ) # Handle request for static libs by altering CMAKE_FIND_LIBRARY_SUFFIXES. # We will restore it at the end of this file. set (_openexr_orig_suffixes ${CMAKE_FIND_LIBRARY_SUFFIXES}) if (OpenEXR_USE_STATIC_LIBS) if (WIN32) set (CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) else () set (CMAKE_FIND_LIBRARY_SUFFIXES .a) endif () endif () # Look for the libraries themselves, for all the components. Like with the # headers, we do two finds -- first for custom locations, then for default. # This is complicated because the OpenEXR libraries may or may not be # built with version numbers embedded. set (_openexr_components IlmThread IlmImf Imath Iex Half) foreach (COMPONENT ${_openexr_components}) string (TOUPPER ${COMPONENT} UPPERCOMPONENT) # First try with the version embedded set (FULL_COMPONENT_NAME ${OPENEXR_CUSTOM_LIB_PREFIX}${COMPONENT}-${OPENEXR_VERSION_MAJOR}_${OPENEXR_VERSION_MINOR}${OPENEXR_CUSTOM_LIB_SUFFIX}) find_library (OPENEXR_${UPPERCOMPONENT}_LIBRARY ${FULL_COMPONENT_NAME} PATHS ${GENERIC_LIBRARY_PATHS} NO_DEFAULT_PATH) # Again, with no directory restrictions find_library (OPENEXR_${UPPERCOMPONENT}_LIBRARY ${FULL_COMPONENT_NAME}) # Try again without the version set (FULL_COMPONENT_NAME ${OPENEXR_CUSTOM_LIB_PREFIX}${COMPONENT}${OPENEXR_CUSTOM_LIB_SUFFIX}) find_library (OPENEXR_${UPPERCOMPONENT}_LIBRARY ${FULL_COMPONENT_NAME} PATHS ${GENERIC_LIBRARY_PATHS} NO_DEFAULT_PATH) # One more time, with no restrictions find_library (OPENEXR_${UPPERCOMPONENT}_LIBRARY ${FULL_COMPONENT_NAME}) endforeach () #Half usually has no suffix find_library (OPENEXR_HALF_LIBRARY ${OPENEXR_CUSTOM_LIB_PREFIX}Half PATHS ${GENERIC_LIBRARY_PATHS} NO_DEFAULT_PATH) find_library (OPENEXR_HALF_LIBRARY ${OPENEXR_CUSTOM_LIB_PREFIX}Half) # Set the FOUND, INCLUDE_DIR, and LIBRARIES variables. if (ILMBASE_INCLUDE_PATH AND OPENEXR_INCLUDE_PATH AND OPENEXR_IMATH_LIBRARY AND OPENEXR_ILMIMF_LIBRARY AND OPENEXR_IEX_LIBRARY AND OPENEXR_HALF_LIBRARY) set (OPENEXR_FOUND TRUE) set (ILMBASE_FOUND TRUE) set (ILMBASE_INCLUDE_DIR ${ILMBASE_INCLUDE_PATH} CACHE STRING "The include paths needed to use IlmBase") set (OPENEXR_INCLUDE_DIR ${OPENEXR_INCLUDE_PATH} CACHE STRING "The include paths needed to use OpenEXR") set (ILMBASE_LIBRARIES ${OPENEXR_IMATH_LIBRARY} ${OPENEXR_IEX_LIBRARY} ${OPENEXR_HALF_LIBRARY} ${OPENEXR_ILMTHREAD_LIBRARY} ${ILMBASE_PTHREADS} CACHE STRING "The libraries needed to use IlmBase") set (OPENEXR_LIBRARIES ${OPENEXR_ILMIMF_LIBRARY} ${ILMBASE_LIBRARIES} ${ZLIB_LIBRARIES} CACHE STRING "The libraries needed to use OpenEXR") endif () find_package_handle_standard_args (OpenEXR REQUIRED_VARS ILMBASE_INCLUDE_PATH OPENEXR_INCLUDE_PATH OPENEXR_IMATH_LIBRARY OPENEXR_ILMIMF_LIBRARY OPENEXR_IEX_LIBRARY OPENEXR_HALF_LIBRARY VERSION_VAR OPENEXR_VERSION ) MARK_AS_ADVANCED( ILMBASE_INCLUDE_DIR OPENEXR_INCLUDE_DIR ILMBASE_LIBRARIES OPENEXR_LIBRARIES OPENEXR_ILMIMF_LIBRARY OPENEXR_IMATH_LIBRARY OPENEXR_IEX_LIBRARY OPENEXR_HALF_LIBRARY OPENEXR_VERSION) # Restore the original CMAKE_FIND_LIBRARY_SUFFIXES set (CMAKE_FIND_LIBRARY_SUFFIXES ${_openexr_orig_suffixes}) openimageio-1.7.17~dfsg0.orig/src/cmake/modules/FindNuke.cmake0000644000175000017500000001500313151711064022350 0ustar mfvmfv# - CMake find module for Nuke # # If requesting a specific release, the Nuke version string must be converted # to a CMake-compatible version number before being passed to `find_package`. # This should be done as follows: # 6.3v8 -> 6.3.8 # 7.0v1b100 -> 7.0.1.100 # # Input variables: # Nuke_ROOT # # Output variables: # NUKE_FOUND # NUKE_EXECUTABLE # NUKE_INCLUDE_DIRS # NUKE_LIBRARY_DIRS # NUKE_LIBRARIES # NUKE_DDIMAGE_LIBRARY # NUKE_VERSION_MAJOR # NUKE_VERSION_MINOR # NUKE_VERSION_RELEASE # # # Version table (for reference only) # 5.0v1 5.0v2 # 5.1v1 5.1v2 5.1v3 5.1v4 5.1v5 5.1v6 # 5.2v1 5.2v2 5.2v3 # 6.0v1 6.0v2 6.0v3 6.0v4 6.0v5 6.0v6 6.0v7 # 6.1v1 6.1v2 6.1v3 6.1v4 6.1v5 # 6.2v1 6.2v2 6.2v3 6.2v4 6.2v5 6.2v6 # 6.3v1 6.3v2 6.3v3 6.3v4 6.3v5 6.3v6 6.3v7 6.3v8 6.3v9 # 7.0v1 7.0v2 7.0v3 7.0v4) set(_nuke_KNOWN_VERSIONS 5.0 5.1 5.2 6.0 6.1 6.2 6.3 7.0 8.0 9.0) set(_nuke_TEST_VERSIONS) # List of Nuke-style strings (e.g. "7.0v4") # If Nuke_ROOT is set, don't even bother with anything else if(Nuke_ROOT) set(_nuke_TEST_PATHS ${Nuke_ROOT}) else() # TODO: Macro for duplicated nested loop code? (to generate permutations) if(Nuke_FIND_VERSION) if(Nuke_FIND_VERSION_EXACT) if(Nuke_FIND_VERSION_COUNT LESS 3) # An "exact" version was requested, but we weren't given a release. message(SEND_ERROR "'Exact' Nuke version requested, but no release specified. Nuke will not be found.") endif() set(_nuke_VERSION_STRING "${Nuke_FIND_VERSION_MAJOR}.${Nuke_FIND_VERSION_MINOR}v${Nuke_FIND_VERSION_PATCH}") if(Nuke_FIND_VERSION_TWEAK) # Beta version set(_nuke_VERSION_STRING "${_nuke_VERSION_STRING}b${Nuke_FIND_VERSION_TWEAK}") endif() list(APPEND _nuke_TEST_VERSIONS ${_nuke_VERSION_STRING}) else() if(Nuke_FIND_VERSION_COUNT LESS 3) # Partial version if(Nuke_FIND_VERSION_COUNT EQUAL 1) # E.g. 6 set(_nuke_FIND_MAJORMINOR "${Nuke_FIND_VERSION}.0") set(_nuke_VERSION_PATTERN "^${Nuke_FIND_VERSION}\\.[0-9]$") # Go for highest 6.x version list(REVERSE _nuke_KNOWN_VERSIONS) elseif(Nuke_FIND_VERSION_COUNT EQUAL 2) # E.g. 6.3 set(_nuke_FIND_MAJORMINOR ${Nuke_FIND_VERSION}) set(_nuke_VERSION_PATTERN "^${Nuke_FIND_VERSION_MAJOR}\\.${Nuke_FIND_VERSION_MINOR}$") endif() foreach(_known_version ${_nuke_KNOWN_VERSIONS}) # To avoid the need to keep this module up to date with the full Nuke # release list, we just build a list of possible releases for the # MAJOR.MINOR pair (currently using possible release versions v1-v13) # We don't try and auto-locate beta versions. string(REGEX MATCH ${_nuke_VERSION_PATTERN} _nuke_VERSION_PREFIX ${_known_version}) if(_nuke_VERSION_PREFIX) if(NOT ${_known_version} VERSION_LESS ${_nuke_FIND_MAJORMINOR}) foreach(_release_num RANGE 13 1 -1) list(APPEND _nuke_TEST_VERSIONS "${_known_version}v${_release_num}") endforeach() endif() endif() endforeach() else() # Full version or beta set(_nuke_VERSION_STRING "${Nuke_FIND_VERSION_MAJOR}.${Nuke_FIND_VERSION_MINOR}v${Nuke_FIND_VERSION_PATCH}") if(Nuke_FIND_VERSION_TWEAK) # Beta version set(_nuke_VERSION_STRING "${_nuke_VERSION_STRING}b${Nuke_FIND_VERSION_TWEAK}") endif() list(APPEND _nuke_TEST_VERSIONS ${_nuke_VERSION_STRING}) endif() endif() else() # If we're just grabbing any available version, we want the *highest* one # we can find, so flip the known versions list. list(REVERSE _nuke_KNOWN_VERSIONS) foreach(_known_version ${_nuke_KNOWN_VERSIONS}) foreach(_release_num RANGE 13 1 -1) list(APPEND _nuke_TEST_VERSIONS "${_known_version}v${_release_num}") endforeach() endforeach() endif() if(APPLE) set(_nuke_TEMPLATE_PATH "/Applications/Nuke/Nuke.app/Contents/MacOS") elseif(WIN32) set(_nuke_TEMPLATE_PATH "C:/Program Files/Nuke") else() # Linux set(_nuke_TEMPLATE_PATH "/usr/local/Nuke") endif() foreach(_test_version ${_nuke_TEST_VERSIONS}) string(REPLACE "" ${_test_version} _test_path ${_nuke_TEMPLATE_PATH}) list(APPEND _nuke_TEST_PATHS ${_test_path}) endforeach() endif() # Base search around DDImage, since its name is unversioned find_library(NUKE_DDIMAGE_LIBRARY DDImage HINTS ${_nuke_TEST_PATHS} DOC "Nuke DDImage library path" NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH NO_SYSTEM_ENVIRONMENT_PATH) # Sanity-check to avoid a bunch of redundant errors. if(NUKE_DDIMAGE_LIBRARY) get_filename_component(NUKE_LIBRARY_DIRS ${NUKE_DDIMAGE_LIBRARY} PATH) find_path(NUKE_INCLUDE_DIRS DDImage/Op.h "${NUKE_LIBRARY_DIRS}/include") # Pull version information from header # (We could pull the DDImage path apart instead, but this avoids dealing # with platform-specific naming.) file(STRINGS "${NUKE_INCLUDE_DIRS}/DDImage/ddImageVersionNumbers.h" _nuke_DDIMAGE_VERSION_H) string(REGEX REPLACE ".*#define kDDImageVersionMajorNum ([0-9]+).*" "\\1" NUKE_VERSION_MAJOR ${_nuke_DDIMAGE_VERSION_H}) string(REGEX REPLACE ".*#define kDDImageVersionMinorNum ([0-9]+).*" "\\1" NUKE_VERSION_MINOR ${_nuke_DDIMAGE_VERSION_H}) string(REGEX REPLACE ".*#define kDDImageVersionReleaseNum ([0-9]+).*" "\\1" NUKE_VERSION_RELEASE ${_nuke_DDIMAGE_VERSION_H}) find_program(NUKE_EXECUTABLE NAMES Nuke "Nuke${NUKE_VERSION_MAJOR}.${NUKE_VERSION_MINOR}" "Nuke${NUKE_VERSION_MAJOR}.${NUKE_VERSION_MINOR}v${NUKE_VERSION_RELEASE}" PATHS ${NUKE_LIBRARY_DIRS} NO_SYSTEM_ENVIRONMENT_PATH DOC "Nuke executable path") endif() # Finalize search include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Nuke DEFAULT_MSG NUKE_DDIMAGE_LIBRARY NUKE_INCLUDE_DIRS NUKE_LIBRARY_DIRS NUKE_EXECUTABLE) openimageio-1.7.17~dfsg0.orig/src/cmake/modules/FindJPEG.cmake0000644000175000017500000000323113151711064022173 0ustar mfvmfv# - Find JPEG # Find the native JPEG includes and library # This module defines # JPEG_INCLUDE_DIR, where to find jpeglib.h, etc. # JPEG_LIBRARIES, the libraries needed to use JPEG. # JPEG_FOUND, If false, do not try to use JPEG. # also defined, but not for general use are # JPEG_LIBRARY, where to find the JPEG library. #============================================================================= # Copyright 2001-2009 Kitware, Inc. # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) include (FindPackageHandleStandardArgs) find_path (JPEG_INCLUDE_DIR NAMES jpeglib.h HINTS "${JPEG_PATH}/include" ) set(JPEG_NAMES ${JPEG_NAMES} jpeg libjpeg) find_library (JPEG_LIBRARY NAMES ${JPEG_NAMES} HINTS "${JPEG_PATH}/lib") # handle the QUIETLY and REQUIRED arguments and set JPEG_FOUND to TRUE if # all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS(JPEG DEFAULT_MSG JPEG_LIBRARY JPEG_INCLUDE_DIR) if(JPEG_FOUND) set(JPEG_LIBRARIES ${JPEG_LIBRARY}) endif() # Deprecated declarations. set (NATIVE_JPEG_INCLUDE_PATH ${JPEG_INCLUDE_DIR} ) if(JPEG_LIBRARY) get_filename_component (NATIVE_JPEG_LIB_PATH ${JPEG_LIBRARY} PATH) endif() mark_as_advanced(JPEG_LIBRARY JPEG_INCLUDE_DIR ) openimageio-1.7.17~dfsg0.orig/src/cmake/util_macros.cmake0000644000175000017500000001032313151711064021516 0ustar mfvmfv# The PARSE_ARGUMENTS macro will take the arguments of another macro and define # several variables. The first argument to PARSE_ARGUMENTS is a prefix to put # on all variables it creates. The second argument is a list of names, and the # third argument is a list of options. Both of these lists should be quoted. # The rest of the PARSE_ARGUMENTS args are arguments from another macro to be # parsed. # # PARSE_ARGUMENTS(prefix arg_names options arg1 arg2...) # # For each item in options, PARSE_ARGUMENTS will create a variable with that # name, prefixed with prefix_. So, for example, if prefix is MY_MACRO and # options is OPTION1;OPTION2, then PARSE_ARGUMENTS will create the variables # MY_MACRO_OPTION1 and MY_MACRO_OPTION2. These variables will be set to true if # the option exists in the command line or false otherwise. # # For each item in arg_names, PARSE_ARGUMENTS will create a variable with that # name, prefixed with prefix_. Each variable will be filled with the arguments # that occur after the given arg_name is encountered up to the next arg_name or # the end of the arguments. All options are removed from these lists. # PARSE_ARGUMENTS also creates a prefix_DEFAULT_ARGS variable containing the # list of all arguments up to the first arg_name encountered. # # Downloaded from: http://www.itk.org/Wiki/CMakeMacroParseArguments # # ----------- # FIXME: Once the minimum cmake version we expect is 2.8.3 or higher, this # macro can be eliminated in favor of cmake_parse_arguments which ships # with cmake. # ----------- MACRO(PARSE_ARGUMENTS prefix arg_names option_names) SET(DEFAULT_ARGS) FOREACH(arg_name ${arg_names}) SET(${prefix}_${arg_name}) ENDFOREACH(arg_name) FOREACH(option ${option_names}) SET(${prefix}_${option} FALSE) ENDFOREACH(option) SET(current_arg_name DEFAULT_ARGS) SET(current_arg_list) FOREACH(arg ${ARGN}) SET(larg_names ${arg_names}) LIST(FIND larg_names "${arg}" is_arg_name) IF (is_arg_name GREATER -1) SET(${prefix}_${current_arg_name} ${current_arg_list}) SET(current_arg_name ${arg}) SET(current_arg_list) ELSE (is_arg_name GREATER -1) SET(loption_names ${option_names}) LIST(FIND loption_names "${arg}" is_option) IF (is_option GREATER -1) SET(${prefix}_${arg} TRUE) ELSE (is_option GREATER -1) SET(current_arg_list ${current_arg_list} ${arg}) ENDIF (is_option GREATER -1) ENDIF (is_arg_name GREATER -1) ENDFOREACH(arg) SET(${prefix}_${current_arg_name} ${current_arg_list}) ENDMACRO(PARSE_ARGUMENTS) # Macro to set a variable with our funny overrides: # If the variable is already set (by -D on the command line), leave it alone. # If an environment variable of the same name is set, use that value # (making it super easy for sites to override external tool locations). # If neither of those, then use the default passed. macro (setup_string name defaultval explanation) # If the named variable already has a value (was set by -D...), leave # it alone. But if it's not yet set... if ("${${name}}" STREQUAL "") # If there's an environment variable of the same name that's # nonempty, use the env variable. Otherwise, use the default. if (NOT $ENV{${name}} STREQUAL "") set (${name} $ENV{${name}} CACHE STRING ${explanation}) else () set (${name} ${defaultval} CACHE STRING ${explanation}) endif () endif () if (VERBOSE) message (STATUS "${name} = ${${name}}") endif () endmacro () macro (setup_path name defaultval explanation) # If the named variable already has a value (was set by -D...), leave # it alone. But if it's not yet set... if ("${${name}}" STREQUAL "") # If there's an environment variable of the same name that's # nonempty, use the env variable. Otherwise, use the default. if (NOT $ENV{${name}} STREQUAL "") set (${name} $ENV{${name}}) # CACHE PATH ${explanation}) else () set (${name} ${defaultval}) # CACHE PATH ${explanation}) endif () endif () if (VERBOSE) message (STATUS "${name} = ${${name}}") endif () endmacro () openimageio-1.7.17~dfsg0.orig/src/cmake/externalpackages.cmake0000644000175000017500000004717113151711064022531 0ustar mfvmfv########################################################################### # Find libraries # When not in VERBOSE mode, try to make things as quiet as possible if (NOT VERBOSE) set (Boost_FIND_QUIETLY true) set (FFmpeg_FIND_QUIETLY true) set (Field3D_FIND_QUIETLY true) set (Freetype_FIND_QUIETLY true) set (GIF_FIND_QUIETLY true) set (GLEW_FIND_QUIETLY true) set (HDF5_FIND_QUIETLY true) set (IlmBase_FIND_QUIETLY true) set (JPEG_FIND_QUIETLY true) set (LibRaw_FIND_QUIETLY true) set (Nuke_FIND_QUIETLY true) set (OpenColorIO_FIND_QUIETLY true) set (OpenCV_FIND_QUIETLY true) set (OpenEXR_FIND_QUIETLY true) set (OpenGL_FIND_QUIETLY true) set (OpenJpeg_FIND_QUIETLY true) set (PkgConfig_FIND_QUIETLY true) set (PNG_FIND_QUIETLY TRUE) set (PTex_FIND_QUIETLY TRUE) set (PugiXML_FIND_QUIETLY TRUE) set (PythonInterp_FIND_QUIETLY true) set (PythonLibs_FIND_QUIETLY true) set (Qt4_FIND_QUIETLY true) set (Threads_FIND_QUIETLY true) set (TIFF_FIND_QUIETLY true) set (WEBP_FIND_QUIETLY true) set (ZLIB_FIND_QUIETLY true) endif () setup_path (THIRD_PARTY_TOOLS_HOME "unknown" "Location of third party libraries in the external project") # Add all third party tool directories to the include and library paths so # that they'll be correctly found by the various FIND_PACKAGE() invocations. if (THIRD_PARTY_TOOLS_HOME AND EXISTS "${THIRD_PARTY_TOOLS_HOME}") set (CMAKE_INCLUDE_PATH "${THIRD_PARTY_TOOLS_HOME}/include" "${CMAKE_INCLUDE_PATH}") # Detect third party tools which have been successfully built using the # lock files which are placed there by the external project Makefile. file (GLOB _external_dir_lockfiles "${THIRD_PARTY_TOOLS_HOME}/*.d") foreach (_dir_lockfile ${_external_dir_lockfiles}) # Grab the tool directory_name.d get_filename_component (_ext_dirname ${_dir_lockfile} NAME) # Strip off the .d extension string (REGEX REPLACE "\\.d$" "" _ext_dirname ${_ext_dirname}) set (CMAKE_INCLUDE_PATH "${THIRD_PARTY_TOOLS_HOME}/include/${_ext_dirname}" ${CMAKE_INCLUDE_PATH}) set (CMAKE_LIBRARY_PATH "${THIRD_PARTY_TOOLS_HOME}/lib/${_ext_dirname}" ${CMAKE_LIBRARY_PATH}) endforeach () endif () setup_string (SPECIAL_COMPILE_FLAGS "" "Custom compilation flags") if (SPECIAL_COMPILE_FLAGS) add_definitions (${SPECIAL_COMPILE_FLAGS}) endif () ########################################################################### # TIFF if (NOT TIFF_LIBRARIES OR NOT TIFF_INCLUDE_DIR) find_package (TIFF REQUIRED) include_directories (${TIFF_INCLUDE_DIR}) else () message (STATUS "Custom TIFF_LIBRARIES ${TIFF_LIBRARIES}") message (STATUS "Custom TIFF_INCLUDE_DIR ${TIFF_INCLUDE_DIR}") endif () ########################################################################### # Several packages need Zlib find_package (ZLIB REQUIRED) include_directories (${ZLIB_INCLUDE_DIR}) ########################################################################### # PNG find_package (PNG REQUIRED) ########################################################################### # IlmBase & OpenEXR setup find_package (OpenEXR REQUIRED) #OpenEXR 2.2 still has problems with importing ImathInt64.h unqualified #thus need for ilmbase/OpenEXR include_directories ("${OPENEXR_INCLUDE_DIR}" "${ILMBASE_INCLUDE_DIR}" "${ILMBASE_INCLUDE_DIR}/OpenEXR") if (${OPENEXR_VERSION} VERSION_LESS 2.0.0) # OpenEXR 1.x had weird #include dirctives, this is also necessary: include_directories ("${OPENEXR_INCLUDE_DIR}/OpenEXR") else () add_definitions (-DUSE_OPENEXR_VERSION2=1) endif () if (NOT OpenEXR_FIND_QUIETLY) message (STATUS "OPENEXR_INCLUDE_DIR = ${OPENEXR_INCLUDE_DIR}") message (STATUS "OPENEXR_LIBRARIES = ${OPENEXR_LIBRARIES}") endif () # OpenEXR setup ########################################################################### ########################################################################### # Boost setup if (NOT Boost_FIND_QUIETLY) message (STATUS "BOOST_ROOT ${BOOST_ROOT}") endif () if (NOT DEFINED Boost_ADDITIONAL_VERSIONS) set (Boost_ADDITIONAL_VERSIONS "1.60" "1.59" "1.58" "1.57" "1.56" "1.55" "1.54" "1.53" "1.52" "1.51" "1.50" "1.49" "1.48" "1.47" "1.46" "1.45" "1.44" "1.43" "1.43.0" "1.42" "1.42.0") endif () if (LINKSTATIC) set (Boost_USE_STATIC_LIBS ON) endif () set (Boost_USE_MULTITHREADED ON) if (BOOST_CUSTOM) set (Boost_FOUND true) # N.B. For a custom version, the caller had better set up the variables # Boost_VERSION, Boost_INCLUDE_DIRS, Boost_LIBRARY_DIRS, Boost_LIBRARIES. else () set (Boost_COMPONENTS filesystem regex system thread) find_package (Boost 1.42 REQUIRED COMPONENTS ${Boost_COMPONENTS} ) # Try to figure out if this boost distro has Boost::python. If we # include python in the component list above, cmake will abort if # it's not found. So we resort to checking for the boost_python # library's existance to get a soft failure. find_library (my_boost_python_lib boost_python PATHS ${Boost_LIBRARY_DIRS} NO_DEFAULT_PATH) mark_as_advanced (my_boost_python_lib) if (NOT my_boost_python_lib AND Boost_SYSTEM_LIBRARY_RELEASE) get_filename_component (my_boost_PYTHON_rel ${Boost_SYSTEM_LIBRARY_RELEASE} NAME ) string (REGEX REPLACE "^(lib)?(.+)_system(.+)$" "\\2_python\\3" my_boost_PYTHON_rel ${my_boost_PYTHON_rel} ) find_library (my_boost_PYTHON_LIBRARY_RELEASE NAMES ${my_boost_PYTHON_rel} lib${my_boost_PYTHON_rel} HINTS ${Boost_LIBRARY_DIRS} NO_DEFAULT_PATH ) mark_as_advanced (my_boost_PYTHON_LIBRARY_RELEASE) endif () if (NOT my_boost_python_lib AND Boost_SYSTEM_LIBRARY_DEBUG) get_filename_component (my_boost_PYTHON_dbg ${Boost_SYSTEM_LIBRARY_DEBUG} NAME ) string (REGEX REPLACE "^(lib)?(.+)_system(.+)$" "\\2_python\\3" my_boost_PYTHON_dbg ${my_boost_PYTHON_dbg} ) find_library (my_boost_PYTHON_LIBRARY_DEBUG NAMES ${my_boost_PYTHON_dbg} lib${my_boost_PYTHON_dbg} HINTS ${Boost_LIBRARY_DIRS} NO_DEFAULT_PATH ) mark_as_advanced (my_boost_PYTHON_LIBRARY_DEBUG) endif () if (my_boost_python_lib OR my_boost_PYTHON_LIBRARY_RELEASE OR my_boost_PYTHON_LIBRARY_DEBUG) set (boost_PYTHON_FOUND ON) else () set (boost_PYTHON_FOUND OFF) endif () endif () # On Linux, Boost 1.55 and higher seems to need to link against -lrt if (CMAKE_SYSTEM_NAME MATCHES "Linux" AND ${Boost_VERSION} GREATER 105499) list (APPEND Boost_LIBRARIES "rt") endif () if (NOT Boost_FIND_QUIETLY) message (STATUS "BOOST_ROOT ${BOOST_ROOT}") message (STATUS "Boost found ${Boost_FOUND} ") message (STATUS "Boost version ${Boost_VERSION}") message (STATUS "Boost include dirs ${Boost_INCLUDE_DIRS}") message (STATUS "Boost library dirs ${Boost_LIBRARY_DIRS}") message (STATUS "Boost libraries ${Boost_LIBRARIES}") message (STATUS "Boost python found ${boost_PYTHON_FOUND}") endif () if (NOT boost_PYTHON_FOUND) # If Boost python components were not found, turn off all python support. message (STATUS "Boost python support not found -- will not build python components!") if (APPLE AND USE_PYTHON) message (STATUS " If your Boost is from Macports, you need the +python26 variant to get Python support.") endif () set (USE_PYTHON OFF) set (PYTHONLIBS_FOUND OFF) endif () include_directories (SYSTEM "${Boost_INCLUDE_DIRS}") link_directories ("${Boost_LIBRARY_DIRS}") # end Boost setup ########################################################################### ########################################################################### # OpenGL setup if (USE_OPENGL) find_package (OpenGL) if (NOT OpenGL_FIND_QUIETLY) message (STATUS "OPENGL_FOUND=${OPENGL_FOUND} USE_OPENGL=${USE_OPENGL}") endif () endif () # end OpenGL setup ########################################################################### ########################################################################### # OpenColorIO Setup if (USE_OCIO) # If 'OCIO_PATH' not set, use the env variable of that name if available if (NOT OCIO_PATH) if (NOT $ENV{OCIO_PATH} STREQUAL "") set (OCIO_PATH $ENV{OCIO_PATH}) endif () endif() find_package (OpenColorIO) if (OCIO_FOUND) include_directories (${OCIO_INCLUDES}) add_definitions ("-DUSE_OCIO=1") else () message (STATUS "Skipping OpenColorIO support") endif () if (LINKSTATIC) find_library (TINYXML_LIBRARY NAMES tinyxml) if (TINYXML_LIBRARY) set (OCIO_LIBRARIES ${OCIO_LIBRARIES} ${TINYXML_LIBRARY}) endif () find_library (YAML_LIBRARY NAMES yaml-cpp) if (YAML_LIBRARY) set (OCIO_LIBRARIES ${OCIO_LIBRARIES} ${YAML_LIBRARY}) endif () find_library (LCMS2_LIBRARY NAMES lcms2) if (LCMS2_LIBRARY) set (OCIO_LIBRARIES ${OCIO_LIBRARIES} ${LCMS2_LIBRARY}) endif () endif () else () message (STATUS "OpenColorIO disabled") endif () # end OpenColorIO setup ########################################################################### ########################################################################### # Qt setup if (USE_QT) if (USE_OPENGL) set (QT_USE_QTOPENGL true) endif () find_package (Qt4) endif () if (USE_QT AND QT4_FOUND) if (NOT Qt4_FIND_QUIETLY) message (STATUS "QT4_FOUND=${QT4_FOUND}") message (STATUS "QT_INCLUDES=${QT_INCLUDES}") message (STATUS "QT_LIBRARIES=${QT_LIBRARIES}") endif () else () message (STATUS "No Qt4 -- skipping components that need Qt4.") endif () # end Qt setup ########################################################################### ########################################################################### # GL Extension Wrangler library setup if (USE_OPENGL) set (GLEW_VERSION 1.5.1) find_library (GLEW_LIBRARIES NAMES GLEW glew32) find_path (GLEW_INCLUDES NAMES glew.h PATH_SUFFIXES GL) if (GLEW_INCLUDES AND GLEW_LIBRARIES) set (GLEW_FOUND TRUE) if (NOT GLEW_FIND_QUIETLY) message (STATUS "GLEW includes = ${GLEW_INCLUDES}") message (STATUS "GLEW library = ${GLEW_LIBRARIES}") endif () else () message (STATUS "GLEW not found") endif () else () message (STATUS "USE_OPENGL=0, skipping components that need OpenGL") endif (USE_OPENGL) # end GL Extension Wrangler library setup ########################################################################### ########################################################################### # BZIP2 - used by ffmped and freetype find_package (BZip2) # Used by ffmpeg if (NOT BZIP2_FOUND) set (BZIP2_LIBRARIES "") endif () ########################################################################### # FFmpeg if (USE_FFMPEG) find_package (FFmpeg) if (FFMPEG_INCLUDE_DIR AND FFMPEG_LIBRARIES) set (FFMPEG_FOUND TRUE) if (NOT FFmpeg_FIND_QUIETLY) message (STATUS "FFMPEG includes = ${FFMPEG_INCLUDE_DIR}") message (STATUS "FFMPEG library = ${FFMPEG_LIBRARIES}") endif () else () message (STATUS "FFMPEG not found") endif () endif() # end FFmpeg setup ########################################################################### ########################################################################### # Field3d if (USE_FIELD3D) if (HDF5_CUSTOM) if (NOT HDF5_FIND_QUIETLY) message (STATUS "Using custom HDF5") endif () set (HDF5_FOUND true) # N.B. For a custom version, the caller had better set up the # variables HDF5_INCLUDE_DIRS and HDF5_LIBRARIES. else () find_library (HDF5_LIBRARIES NAMES hdf5 PATHS "${THIRD_PARTY_TOOLS_HOME}/lib/" /usr/local/lib /opt/local/lib ) if (HDF5_LIBRARIES) set (HDF5_FOUND true) endif () endif () if (NOT HDF5_FIND_QUIETLY) message (STATUS "HDF5_FOUND=${HDF5_FOUND}") message (STATUS "HDF5_LIBRARIES=${HDF5_LIBRARIES}") endif () endif () if (USE_FIELD3D AND HDF5_FOUND) if (NOT Field3D_FIND_QUIETLY) message (STATUS "FIELD3D_HOME=${FIELD3D_HOME}") endif () if (FIELD3D_HOME) set (FIELD3D_INCLUDES "${FIELD3D_HOME}/include") else () find_path (FIELD3D_INCLUDES Field3D/Field.h "${THIRD_PARTY_TOOLS}/include" "${PROJECT_SOURCE_DIR}/src/include" "${FIELD3D_HOME}/include" ) endif () find_library (FIELD3D_LIBRARY NAMES Field3D PATHS "${THIRD_PARTY_TOOLS_HOME}/lib/" "${FIELD3D_HOME}/lib" ) if (FIELD3D_INCLUDES AND FIELD3D_LIBRARY) set (FIELD3D_FOUND TRUE) if (NOT Field3D_FIND_QUIETLY) message (STATUS "Field3D includes = ${FIELD3D_INCLUDES}") message (STATUS "Field3D library = ${FIELD3D_LIBRARY}") endif () add_definitions ("-DUSE_FIELD3D=1") include_directories ("${FIELD3D_INCLUDES}") else () message (STATUS "Field3D not found") add_definitions ("-UUSE_FIELD3D") set (FIELD3D_FOUND FALSE) endif () else () add_definitions ("-UUSE_FIELD3D") message (STATUS "Field3d will not be used") endif () # end Field3d setup ########################################################################### ########################################################################### # JPEG if (USE_JPEGTURBO) find_package (JPEGTurbo) endif () if (JPEG_FOUND) add_definitions ("-DUSE_JPEG_TURBO=1") else () # Try to find the non-turbo version find_package (JPEG REQUIRED) endif () include_directories (${JPEG_INCLUDE_DIR}) # end JPEG ########################################################################### ########################################################################### # OpenJpeg if (USE_OPENJPEG) find_package (OpenJpeg) endif() # end OpenJpeg setup ########################################################################### ########################################################################### # LibRaw if (USE_LIBRAW) if (NOT LibRaw_FIND_QUIETLY) message (STATUS "Looking for LibRaw with ${LIBRAW_PATH}") endif () if (LIBRAW_PATH) # Customized path requested, don't use find_package FIND_PATH(LibRaw_INCLUDE_DIR libraw/libraw.h PATHS "${LIBRAW_PATH}/include" NO_DEFAULT_PATH ) FIND_LIBRARY(LibRaw_r_LIBRARIES NAMES raw_r PATHS "${LIBRAW_PATH}/lib" NO_DEFAULT_PATH ) else () find_package (LibRaw) endif () if (LibRaw_r_LIBRARIES AND LibRaw_INCLUDE_DIR) set (LIBRAW_FOUND TRUE) include_directories (${LibRaw_INCLUDE_DIR}) if (NOT LibRaw_FIND_QUIETLY) message (STATUS "Found LibRaw, include ${LibRaw_INCLUDE_DIR}") endif () else () set (LIBRAW_FOUND FALSE) message (STATUS "LibRaw not found!") endif() if (LINKSTATIC) find_package (Jasper) find_library (LCMS2_LIBRARIES NAMES lcms2) set (LibRaw_r_LIBRARIES ${LibRaw_r_LIBRARIES} ${JASPER_LIBRARIES} ${LCMS2_LIBRARIES}) endif () else () message (STATUS "Not using LibRaw") endif() # end LibRaw setup ########################################################################### ########################################################################### # WebP setup if (NOT WEBP_FIND_QUIETLY) message (STATUS "WEBP_HOME=${WEBP_HOME}") endif () find_path (WEBP_INCLUDE_DIR webp/encode.h "${THIRD_PARTY_TOOLS}/include" "${PROJECT_SOURCE_DIR}/src/include" "${WEBP_HOME}") find_library (WEBP_LIBRARY NAMES webp PATHS "${THIRD_PARTY_TOOLS_HOME}/lib/" "${WEBP_HOME}" ) if (WEBP_INCLUDE_DIR AND WEBP_LIBRARY) set (WEBP_FOUND TRUE) if (NOT WEBP_FIND_QUIETLY) message (STATUS "WEBP includes = ${WEBP_INCLUDE_DIR} ") message (STATUS "WEBP library = ${WEBP_LIBRARY} ") endif () else() set (WEBP_FOUND FALSE) message (STATUS "WebP library not found") endif() # end Webp setup ########################################################################### ########################################################################### # Pugixml setup. Normally we just use the version bundled with oiio, but # some linux distros are quite particular about having separate packages so we # allow this to be overridden to use the distro-provided package if desired. if (USE_EXTERNAL_PUGIXML) find_package (PugiXML REQUIRED) # insert include path to pugixml first, to ensure that the external # pugixml is found, and not the one in OIIO's include directory. include_directories (BEFORE ${PUGIXML_INCLUDE_DIR}) add_definitions ("-DUSE_EXTERNAL_PUGIXML=1") endif() ########################################################################### # OpenCV setup if (USE_OPENCV) find_package (OpenCV) if (OpenCV_FOUND) add_definitions ("-DUSE_OPENCV") endif () endif () # end OpenCV setup ########################################################################### ########################################################################### # Freetype setup if (USE_FREETYPE) find_package (Freetype) if (FREETYPE_FOUND) add_definitions ("-DUSE_FREETYPE") if (NOT Freetype_FIND_QUIETLY) message (STATUS "Freetype includes = ${FREETYPE_INCLUDE_DIRS} ") message (STATUS "Freetype libs = ${FREETYPE_LIBRARIES} ") endif () else () message (STATUS "Freetype library not found") endif () else () message (STATUS "Not using Freetype") endif () # end Freetype setup ########################################################################### ########################################################################### # OpenSSL Setup if (USE_OPENSSL) find_package (OpenSSL) if (OPENSSL_FOUND) if (NOT OpenSSL_FIND_QUIETLY) message (STATUS "OpenSSL enabled") message(STATUS "OPENSSL_INCLUDES: ${OPENSSL_INCLUDE_DIR}") endif () include_directories (${OPENSSL_INCLUDE_DIR}) add_definitions ("-DUSE_OPENSSL=1") else () message (STATUS "Skipping OpenSSL support") endif () else () message (STATUS "OpenSSL disabled") endif () # end OpenSSL setup ########################################################################### ########################################################################### # GIF if (USE_GIF) find_package (GIF) endif() # end GIF setup ########################################################################### ########################################################################### # PTex if (USE_PTEX) find_package (PTex) if (NOT PTEX_FOUND) set (PTEX_INCLUDE_DIR "") set (PTEX_LIBRARIES "") endif () endif() # end PTEX setup ########################################################################### openimageio-1.7.17~dfsg0.orig/src/cmake/platform.cmake0000644000175000017500000000267213151711064021031 0ustar mfvmfv########################################################################### # Figure out what platform we're on, and set some variables appropriately if (VERBOSE) message (STATUS "CMAKE_SYSTEM_NAME = ${CMAKE_SYSTEM_NAME}") message (STATUS "CMAKE_SYSTEM_VERSION = ${CMAKE_SYSTEM_VERSION}") message (STATUS "CMAKE_SYSTEM_PROCESSOR = ${CMAKE_SYSTEM_PROCESSOR}") endif () if (UNIX) if (VERBOSE) message (STATUS "Unix! ${CMAKE_SYSTEM_NAME}") endif () if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set (platform "linux") set (CXXFLAGS "${CXXFLAGS} -DLINUX") if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64") set (platform "linux64") set (CXXFLAGS "${CXXFLAGS} -DLINUX64") endif () elseif (APPLE) set (platform "macosx") elseif (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") set (platform "FreeBSD") set (CXXFLAGS "${CXXFLAGS} -DFREEBSD") if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i386") # to use gcc atomics we need cpu instructions only available # with arch of i586 or higher set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=i586") endif() else () string (TOLOWER ${CMAKE_SYSTEM_NAME} platform) endif () endif () if (WIN32) set (platform "windows") endif () if (platform) message (STATUS "platform = ${platform}") else () message (FATAL_ERROR "'platform' not defined") endif () openimageio-1.7.17~dfsg0.orig/src/zfile.imageio/0000755000175000017500000000000013151711064017636 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/zfile.imageio/CMakeLists.txt0000644000175000017500000000020013151711064022366 0ustar mfvmfvadd_oiio_plugin (zfile.cpp INCLUDE_DIRS ${ZLIB_INCLUDE_DIR} LINK_LIBRARIES ${ZLIB_LIBRARIES}) openimageio-1.7.17~dfsg0.orig/src/zfile.imageio/zfile.cpp0000644000175000017500000002745513151711064021470 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include "zlib.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" OIIO_PLUGIN_NAMESPACE_BEGIN namespace { // anon namespace struct ZfileHeader { int magic; short width; short height; float worldtoscreen[16]; float worldtocamera[16]; }; static const int zfile_magic = 0x2f0867ab; static const int zfile_magic_endian = 0xab67082f; // other endianness } // end anon namespace class ZfileInput : public ImageInput { public: ZfileInput () { init(); } virtual ~ZfileInput () { close(); } virtual const char * format_name (void) const { return "zfile"; } virtual bool valid_file (const std::string &filename) const; virtual bool open (const std::string &name, ImageSpec &newspec); virtual bool close (); virtual bool read_native_scanline (int y, int z, void *data); private: std::string m_filename; ///< Stash the filename gzFile m_gz; ///< Handle for compressed files bool m_swab; ///< swap bytes for other endianness? int m_next_scanline; ///< Which scanline is the next to be read? // Reset everything to initial state void init () { m_gz = 0; m_swab = false; m_next_scanline = 0; } }; class ZfileOutput : public ImageOutput { public: ZfileOutput () { init(); } virtual ~ZfileOutput () { close(); } virtual const char * format_name (void) const { return "zfile"; } virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode=Create); virtual bool close (); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); private: std::string m_filename; ///< Stash the filename FILE *m_file; ///< Open image handle for not compresed gzFile m_gz; ///< Handle for compressed files std::vector m_scratch; std::vector m_tilebuffer; // Initialize private members to pre-opened state void init (void) { m_file = NULL; m_gz = 0; } }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT int zfile_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* zfile_imageio_library_version () { return NULL; } OIIO_EXPORT ImageInput *zfile_input_imageio_create () { return new ZfileInput; } OIIO_EXPORT const char * zfile_input_extensions[] = { "zfile", NULL }; OIIO_EXPORT ImageOutput *zfile_output_imageio_create () { return new ZfileOutput; } OIIO_EXPORT const char * zfile_output_extensions[] = { "zfile", NULL }; OIIO_PLUGIN_EXPORTS_END bool ZfileInput::valid_file (const std::string &filename) const { FILE *fd = Filesystem::fopen (filename, "rb"); gzFile gz = (fd) ? gzdopen (fileno (fd), "rb") : NULL; if (! gz) { if (fd) fclose (fd); return false; } ZfileHeader header; gzread (gz, &header, sizeof(header)); bool ok = (header.magic == zfile_magic || header.magic == zfile_magic_endian); gzclose (gz); return ok; } bool ZfileInput::open (const std::string &name, ImageSpec &newspec) { m_filename = name; FILE *fd = Filesystem::fopen (name, "rb"); m_gz = (fd) ? gzdopen (fileno (fd), "rb") : NULL; if (! m_gz) { if (fd) fclose (fd); error ("Could not open file \"%s\"", name.c_str()); return false; } ZfileHeader header; ASSERT (sizeof(header) == 136); gzread (m_gz, &header, sizeof(header)); if (header.magic != zfile_magic && header.magic != zfile_magic_endian) { error ("Not a valid Zfile"); return false; } m_swab = (header.magic == zfile_magic_endian); if (m_swab) { swap_endian (&header.width); swap_endian (&header.height); swap_endian ((float *)&header.worldtoscreen, 16); swap_endian ((float *)&header.worldtocamera, 16); } m_spec = ImageSpec (header.width, header.height, 1, TypeDesc::FLOAT); if (m_spec.channelnames.size() == 0) m_spec.channelnames.push_back ("z"); else m_spec.channelnames[0] = "z"; m_spec.z_channel = 0; m_spec.attribute ("worldtoscreen", TypeDesc::TypeMatrix, (float *)&header.worldtoscreen); m_spec.attribute ("worldtocamera", TypeDesc::TypeMatrix, (float *)&header.worldtocamera); newspec = spec (); return true; } bool ZfileInput::close () { if (m_gz) { gzclose (m_gz); m_gz = 0; } init(); // Reset to initial state return true; } bool ZfileInput::read_native_scanline (int y, int z, void *data) { if (m_next_scanline > y) { // User is trying to read an earlier scanline than the one we're // up to. Easy fix: close the file and re-open. ImageSpec dummyspec; int subimage = current_subimage(); if (! close () || ! open (m_filename, dummyspec) || ! seek_subimage (subimage, dummyspec)) return false; // Somehow, the re-open failed ASSERT (m_next_scanline == 0 && current_subimage() == subimage); } while (m_next_scanline <= y) { // Keep reading until we're read the scanline we really need gzread (m_gz, data, m_spec.width*sizeof(float)); ++m_next_scanline; } if (m_swab) swap_endian ((float *)data, m_spec.width); return true; } bool ZfileOutput::open (const std::string &name, const ImageSpec &userspec, OpenMode mode) { if (mode != Create) { error ("%s does not support subimages or MIP levels", format_name()); return false; } close (); // Close any already-opened file m_gz = 0; m_file = NULL; m_spec = userspec; // Stash the spec // Check for things this format doesn't support if (m_spec.width < 1 || m_spec.height < 1) { error ("Image resolution must be at least 1x1, you asked for %d x %d", m_spec.width, m_spec.height); return false; } if (m_spec.depth < 1) m_spec.depth = 1; if (m_spec.depth > 1) { error ("%s does not support volume images (depth > 1)", format_name()); return false; } if (m_spec.nchannels != 1) { error ("Zfile only supports 1 channel, not %d", m_spec.nchannels); return false; } // Force float if (m_spec.format != TypeDesc::FLOAT) m_spec.format = TypeDesc::FLOAT; ZfileHeader header; header.magic = zfile_magic; header.width = (int)m_spec.width; header.height = (int)m_spec.height; ImageIOParameter *p; static float ident[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; if ((p = m_spec.find_attribute ("worldtocamera", TypeDesc::TypeMatrix))) memcpy (header.worldtocamera, p->data(), 16*sizeof(float)); else memcpy (header.worldtocamera, ident, 16*sizeof(float)); if ((p = m_spec.find_attribute ("worldtoscreen", TypeDesc::TypeMatrix))) memcpy (header.worldtoscreen, p->data(), 16*sizeof(float)); else memcpy (header.worldtoscreen, ident, 16*sizeof(float)); if (m_spec.get_string_attribute ("compression", "none") != std::string("none")) { FILE *fd = Filesystem::fopen (name, "wb"); if (fd) { m_gz = gzdopen (fileno (fd), "wb"); if (!m_gz) fclose (fd); } } else m_file = Filesystem::fopen (name, "wb"); if (! m_file && ! m_gz) { error ("Could not open file \"%s\"", name.c_str()); return false; } if (m_gz) gzwrite (m_gz, &header, sizeof(header)); else { size_t b = fwrite (&header, sizeof(header), 1, m_file); if (b != 1) { error ("Failed write zfile::open (err: %d)", b); return false; } } // If user asked for tiles -- which this format doesn't support, emulate // it by buffering the whole image.this form if (m_spec.tile_width && m_spec.tile_height) m_tilebuffer.resize (m_spec.image_bytes()); return true; } bool ZfileOutput::close () { bool ok = true; if (m_spec.tile_width) { // We've been emulating tiles; now dump as scanlines. ASSERT (m_tilebuffer.size()); ok &= write_scanlines (m_spec.y, m_spec.y+m_spec.height, 0, m_spec.format, &m_tilebuffer[0]); std::vector().swap (m_tilebuffer); } if (m_gz) { gzclose (m_gz); m_gz = 0; } if (m_file) { fclose (m_file); m_file = NULL; } init (); // re-initialize return ok; } bool ZfileOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { y -= m_spec.y; m_spec.auto_stride (xstride, format, spec().nchannels); const void *origdata = data; data = to_native_scanline (format, data, xstride, m_scratch); if (data == origdata) { m_scratch.assign ((unsigned char *)data, (unsigned char *)data+m_spec.scanline_bytes()); data = &m_scratch[0]; } if (m_gz) gzwrite (m_gz, data, m_spec.width*sizeof(float)); else { size_t b = fwrite (data, sizeof(float), m_spec.width, m_file); if (b != (size_t)m_spec.width) { error ("Failed write zfile::open (err: %d)", b); return false; } } return true; } bool ZfileOutput::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { // Emulate tiles by buffering the whole image return copy_tile_to_image_buffer (x, y, z, format, data, xstride, ystride, zstride, &m_tilebuffer[0]); } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/iv/0000755000175000017500000000000013151711064015532 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/iv/CMakeLists.txt0000644000175000017500000000276113151711064020300 0ustar mfvmfvif (QT4_FOUND AND OPENGL_FOUND AND GLEW_FOUND) include (${QT_USE_FILE}) include_directories (${QT_INCLUDES} ${OPENGL_INCLUDE_DIR} ${GLEW_INCLUDES}) add_custom_command (OUTPUT "${CMAKE_BINARY_DIR}/src/iv/moc_imageviewer.cpp" DEPENDS imageviewer.h imageviewer.cpp COMMAND ${QT_MOC_EXECUTABLE} ARGS "${PROJECT_SOURCE_DIR}/src/iv/imageviewer.h" -o "${CMAKE_BINARY_DIR}/src/iv/moc_imageviewer.cpp" ) add_custom_command (OUTPUT ${CMAKE_BINARY_DIR}/src/iv/moc_ivgl.cpp DEPENDS ivgl.h ivgl.cpp COMMAND ${QT_MOC_EXECUTABLE} ARGS "${PROJECT_SOURCE_DIR}/src/iv/ivgl.h" -o "${CMAKE_BINARY_DIR}/src/iv/moc_ivgl.cpp" ) set (iv_srcs imageviewer.cpp ivimage.cpp ivgl.cpp ivinfowin.cpp ivpref.cpp ivmain.cpp moc_imageviewer.cpp moc_ivgl.cpp) if (FORCE_OPENGL_1) add_definitions(-DFORCE_OPENGL_1) endif() add_executable (iv ${iv_srcs}) set_target_properties (iv PROPERTIES FOLDER "Tools") target_link_libraries (iv OpenImageIO ${QT_LIBRARIES} ${OPENGL_LIBRARIES} ${Boost_LIBRARIES} ${CMAKE_DL_LIBS} ${GLEW_LIBRARIES}) oiio_install_targets (iv) else () message (STATUS "\n\n WARNING: Qt, OpenGL, or GLEW not found -- 'iv' will not be built!\n") endif () openimageio-1.7.17~dfsg0.orig/src/iv/ivutils.h0000644000175000017500000000165113151711064017405 0ustar mfvmfv#ifndef OPENIMAGEIO_IV_UTILS_H #define OPENIMAGEIO_IV_UTILS_H #include "OpenImageIO/fmath.h" OIIO_NAMESPACE_BEGIN /// Round up to the next power of 2 /// TODO: This should be optimized to use bit arithmetic on the ieee float /// representation. Once optimized and tested, move to fmath.h inline float pow2roundupf (float f) { float logval = logf (f) / logf (2.0f); logval += 1e-6f; // add floating point slop. this supports [0.00012207,8192] return powf (2.0f, ceilf (logval)); } /// Round down to the next power of 2 /// TODO: This should be optimized to use bit arithmetic on the ieee float /// representation. Once optimized and tested, move to fmath.h inline float pow2rounddownf (float f) { float logval = logf (f) / logf (2.0f); logval -= 1e-6f; // add floating point slop. this supports [0.00012207,8192] return powf (2.0f, floorf (logval)); } OIIO_NAMESPACE_END #endif // OPENIMAGEIO_IV_UTILS_H openimageio-1.7.17~dfsg0.orig/src/iv/ivgl.cpp0000644000175000017500000015741513151711064017214 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "imageviewer.h" #include "ivgl.h" #include // This needs to be included before GL.h // (which is included by QtOpenGL and QGLFormat) #include #include #include #include #include #include #include #include #include using boost::algorithm::iequals; #include #include #include #include #include "ivutils.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/timer.h" static const char * gl_err_to_string (GLenum err) { // Thanks, Dan Wexler, for this function switch (err) { case GL_NO_ERROR: return "No error"; case GL_INVALID_ENUM: return "Invalid enum"; case GL_INVALID_OPERATION: return "Invalid operation"; case GL_INVALID_VALUE: return "Invalid value"; case GL_OUT_OF_MEMORY: return "Out of memory"; case GL_INVALID_FRAMEBUFFER_OPERATION: return "Invalid framebuffer operation"; default: return "Unknown"; } } #define GLERRPRINT(msg) \ for (GLenum err = glGetError(); err != GL_NO_ERROR; err = glGetError()) \ std::cerr << "GL error " << msg << " " << (int)err << " - " << gl_err_to_string(err) << "\n"; \ IvGL::IvGL (QWidget *parent, ImageViewer &viewer) : QGLWidget(parent), m_viewer(viewer), m_shaders_created(false), m_tex_created(false), m_zoom(1.0), m_centerx(0), m_centery(0), m_dragging(false), m_use_shaders(false), m_shaders_using_extensions(false), m_use_halffloat(false), m_use_float(false), m_use_srgb(false), m_use_pbo(false), m_texture_width(1), m_texture_height(1), m_last_pbo_used(0), m_current_image(NULL), m_pixelview_left_corner(true), m_last_texbuf_used(0) { #if 0 QGLFormat format; format.setRedBufferSize (32); format.setGreenBufferSize (32); format.setBlueBufferSize (32); format.setAlphaBufferSize (32); format.setDepth (true); setFormat (format); #endif m_mouse_activation = false; this->setFocusPolicy (Qt::StrongFocus); setMouseTracking (true); } IvGL::~IvGL () { } void IvGL::initializeGL () { GLenum glew_error = glewInit (); if (glew_error != GLEW_OK) { std::cerr << "GLEW init error " << glewGetErrorString (glew_error) << "\n"; } glClearColor (0.05f, 0.05f, 0.05f, 1.0f); glShadeModel (GL_FLAT); glEnable (GL_DEPTH_TEST); glDisable (GL_CULL_FACE); glEnable (GL_BLEND); glEnable (GL_TEXTURE_2D); // glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Make sure initial matrix is identity (returning to this stack level loads // back this matrix). glLoadIdentity(); // There's this small detail in the OpenGL 2.1 (probably earlier versions // too) spec: // // (For TexImage3D, TexImage2D and TexImage1D): // The values of UNPACK ROW LENGTH and UNPACK ALIGNMENT control the row-to- // row spacing in these images in the same manner as DrawPixels. // // UNPACK_ALIGNMENT has a default value of 4 according to the spec. Which // means that it was expecting images to be Aligned to 4-bytes, and making // several odd "skew-like effects" in the displayed images. Setting the // alignment to 1 byte fixes this problems. glPixelStorei (GL_UNPACK_ALIGNMENT, 1); // here we check what OpenGL extensions are available, and take action // if needed check_gl_extensions (); create_textures (); create_shaders (); } void IvGL::create_textures (void) { if (m_tex_created) return; // FIXME: Determine this dynamically. const int total_texbufs = 4; GLuint textures[total_texbufs]; glGenTextures (total_texbufs, textures); // Initialize texture objects for (int i = 0; i < total_texbufs; i++) { m_texbufs.push_back (TexBuffer()); glBindTexture (GL_TEXTURE_2D, textures[i]); GLERRPRINT ("bind tex"); glTexImage2D (GL_TEXTURE_2D, 0 /*mip level*/, 4 /*internal format - color components */, 1 /*width*/, 1 /*height*/, 0 /*border width*/, GL_RGBA /*type - GL_RGB, GL_RGBA, GL_LUMINANCE */, GL_FLOAT /*format - GL_FLOAT */, NULL /*data*/); GLERRPRINT ("tex image 2d"); // Initialize tex parameters. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); GLERRPRINT ("After tex parameters"); m_texbufs.back().tex_object = textures[i]; m_texbufs.back().x = 0; m_texbufs.back().y = 0; m_texbufs.back().width = 0; m_texbufs.back().height = 0; } // Create another texture for the pixelview. glGenTextures (1, &m_pixelview_tex); glBindTexture (GL_TEXTURE_2D, m_pixelview_tex); glTexImage2D (GL_TEXTURE_2D, 0, 4, closeuptexsize, closeuptexsize, 0, GL_RGBA, GL_FLOAT, NULL); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); if (m_use_pbo) { glGenBuffersARB(2, m_pbo_objects); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, m_pbo_objects[0]); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, m_pbo_objects[1]); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0); } m_tex_created = true; } void IvGL::create_shaders (void) { static const GLchar *vertex_source = "varying vec2 vTexCoord;\n" "void main ()\n" "{\n" " vTexCoord = gl_MultiTexCoord0.xy;\n" " gl_Position = ftransform();\n" "}\n"; static const GLchar *fragment_source = "uniform sampler2D imgtex;\n" "varying vec2 vTexCoord;\n" "uniform float gain;\n" "uniform float gamma;\n" "uniform int startchannel;\n" "uniform int colormode;\n" // Remember, if imgchannels == 2, second channel would be channel 4 (a). "uniform int imgchannels;\n" "uniform int pixelview;\n" "uniform int linearinterp;\n" "uniform int width;\n" "uniform int height;\n" "vec4 rgba_mode (vec4 C)\n" "{\n" " if (imgchannels <= 2) {\n" " if (startchannel == 1)\n" " return vec4(C.aaa, 1.0);\n" " return C.rrra;\n" " }\n" " return C;\n" "}\n" "vec4 rgb_mode (vec4 C)\n" "{\n" " if (imgchannels <= 2) {\n" " if (startchannel == 1)\n" " return vec4(C.aaa, 1.0);\n" " return vec4 (C.rrr, 1.0);\n" " }\n" " float C2[4];\n" " C2[0]=C.x; C2[1]=C.y; C2[2]=C.z; C2[3]=C.w;\n" " return vec4 (C2[startchannel], C2[startchannel+1], C2[startchannel+2], 1.0);\n" "}\n" "vec4 singlechannel_mode (vec4 C)\n" "{\n" " float C2[4];\n" " C2[0]=C.x; C2[1]=C.y; C2[2]=C.z; C2[3]=C.w;\n" " if (startchannel > imgchannels)\n" " return vec4 (0.0,0.0,0.0,1.0);\n" " return vec4 (C2[startchannel], C2[startchannel], C2[startchannel], 1.0);\n" "}\n" "vec4 luminance_mode (vec4 C)\n" "{\n" " if (imgchannels <= 2)\n" " return vec4 (C.rrr, C.a);\n" " float lum = dot (C.rgb, vec3(0.2126, 0.7152, 0.0722));\n" " return vec4 (lum, lum, lum, C.a);\n" "}\n" "float heat_red(float x)\n" "{\n" " return clamp (mix(0.0, 1.0, (x-0.35)/(0.66-0.35)), 0.0, 1.0) -\n" " clamp (mix(0.0, 0.5, (x-0.89)/(1.0-0.89)), 0.0, 1.0);\n" "}\n" "float heat_green(float x)\n" "{\n" " return clamp (mix(0.0, 1.0, (x-0.125)/(0.375-0.125)), 0.0, 1.0) -\n" " clamp (mix(0.0, 1.0, (x-0.64)/(0.91-0.64)), 0.0, 1.0);\n" "}\n" "vec4 heatmap_mode (vec4 C)\n" "{\n" " float C2[4];\n" " C2[0]=C.x; C2[1]=C.y; C2[2]=C.z; C2[3]=C.w;\n" " return vec4(heat_red(C2[startchannel]),\n" " heat_green(C2[startchannel]),\n" " heat_red(1.0-C2[startchannel]),\n" " 1.0);\n" "}\n" "void main ()\n" "{\n" " vec2 st = vTexCoord;\n" " float black = 0.0;\n" " if (pixelview != 0 || linearinterp == 0) {\n" " vec2 wh = vec2(width,height);\n" " vec2 onehalf = vec2(0.5,0.5);\n" " vec2 st_res = st * wh /* + onehalf */ ;\n" " vec2 st_pix = floor (st_res);\n" " vec2 st_rem = st_res - st_pix;\n" " st = (st_pix + onehalf) / wh;\n" " if (pixelview != 0) {\n" " if (st.x < 0.0 || st.x >= 1.0 || \n" " st.y < 0.0 || st.y >= 1.0 || \n" " st_rem.x < 0.05 || st_rem.x >= 0.95 || \n" " st_rem.y < 0.05 || st_rem.y >= 0.95)\n" " black = 1.0;\n" " }\n" " }\n" " vec4 C = texture2D (imgtex, st);\n" " C = mix (C, vec4(0.05,0.05,0.05,1.0), black);\n" " if (startchannel < 0)\n" " C = vec4(0.0,0.0,0.0,1.0);\n" " else if (colormode == 0)\n" // RGBA " C = rgba_mode (C);\n" " else if (colormode == 1)\n" // RGB (i.e., ignore alpha). " C = rgb_mode (C);\n" " else if (colormode == 2)\n" // Single channel. " C = singlechannel_mode (C);\n" " else if (colormode == 3)\n" // Luminance. " C = luminance_mode (C);\n" " else if (colormode == 4)\n" // Heatmap. " C = heatmap_mode (C);\n" " if (pixelview != 0)\n" " C.a = 1.0;\n" " C.xyz *= gain;\n" " float invgamma = 1.0/gamma;\n" " C.xyz = pow (C.xyz, vec3 (invgamma, invgamma, invgamma));\n" " gl_FragColor = C;\n" "}\n"; if (!m_use_shaders) { std::cerr << "Not using shaders!\n"; return; } if (m_shaders_created) return; //initialize shader object handles for abort function m_shader_program = 0; m_vertex_shader = 0; m_fragment_shader = 0; // When using extensions to support shaders, we need to load the function // entry points (which is actually done by GLEW) and then call them. So // we have to get the functions through the right symbols otherwise // extension-based shaders won't work. if (m_shaders_using_extensions) { m_shader_program = glCreateProgramObjectARB (); } else { m_shader_program = glCreateProgram (); } GLERRPRINT ("create progam"); // This holds the compilation status GLint status; if (m_shaders_using_extensions) { m_vertex_shader = glCreateShaderObjectARB (GL_VERTEX_SHADER_ARB); glShaderSourceARB (m_vertex_shader, 1, &vertex_source, NULL); glCompileShaderARB (m_vertex_shader); glGetObjectParameterivARB (m_vertex_shader, GL_OBJECT_COMPILE_STATUS_ARB, &status); } else { m_vertex_shader = glCreateShader (GL_VERTEX_SHADER); glShaderSource (m_vertex_shader, 1, &vertex_source, NULL); glCompileShader (m_vertex_shader); glGetShaderiv (m_vertex_shader, GL_COMPILE_STATUS, &status); } if (! status) { std::cerr << "vertex shader compile status: " << status << "\n"; print_shader_log (std::cerr, m_vertex_shader); create_shaders_abort (); return; } if (m_shaders_using_extensions) { glAttachObjectARB (m_shader_program, m_vertex_shader); } else { glAttachShader (m_shader_program, m_vertex_shader); } GLERRPRINT ("After attach vertex shader."); if (m_shaders_using_extensions) { m_fragment_shader = glCreateShaderObjectARB (GL_FRAGMENT_SHADER_ARB); glShaderSourceARB (m_fragment_shader, 1, &fragment_source, NULL); glCompileShaderARB (m_fragment_shader); glGetObjectParameterivARB (m_fragment_shader, GL_OBJECT_COMPILE_STATUS_ARB, &status); } else { m_fragment_shader = glCreateShader (GL_FRAGMENT_SHADER); glShaderSource (m_fragment_shader, 1, &fragment_source, NULL); glCompileShader (m_fragment_shader); glGetShaderiv (m_fragment_shader, GL_COMPILE_STATUS, &status); } if (! status) { std::cerr << "fragment shader compile status: " << status << "\n"; print_shader_log(std::cerr, m_fragment_shader); create_shaders_abort (); return; } if (m_shaders_using_extensions) { glAttachObjectARB (m_shader_program, m_fragment_shader); } else { glAttachShader (m_shader_program, m_fragment_shader); } GLERRPRINT ("After attach fragment shader"); if (m_shaders_using_extensions) { glLinkProgramARB (m_shader_program); } else { glLinkProgram (m_shader_program); } GLERRPRINT ("link"); GLint linked; if (m_shaders_using_extensions) { glGetObjectParameterivARB (m_shader_program, GL_OBJECT_LINK_STATUS_ARB, &linked); } else { glGetProgramiv (m_shader_program, GL_LINK_STATUS, &linked); } if (! linked) { std::cerr << "NOT LINKED\n"; char buf[10000]; buf[0] = 0; GLsizei len; if (m_shaders_using_extensions) { glGetInfoLogARB (m_shader_program, sizeof(buf), &len, buf); } else { glGetProgramInfoLog (m_shader_program, sizeof(buf), &len, buf); } std::cerr << "link log:\n" << buf << "---\n"; create_shaders_abort (); return; } m_shaders_created = true; } void IvGL::create_shaders_abort (void) { if (m_shaders_using_extensions) { glUseProgramObjectARB (0); if (m_shader_program) //this will also detach related shaders glDeleteObjectARB (m_shader_program); if (m_vertex_shader) glDeleteObjectARB (m_vertex_shader); if (m_fragment_shader) glDeleteObjectARB (m_fragment_shader); } else { glUseProgram (0); if (m_shader_program) glDeleteProgram (m_shader_program); if (m_vertex_shader) glDeleteShader (m_vertex_shader); if (m_fragment_shader) glDeleteShader (m_fragment_shader); } GLERRPRINT ("After delete shaders"); m_use_shaders = false; } void IvGL::resizeGL (int w, int h) { GLERRPRINT ("resizeGL entry"); glViewport (0, 0, w, h); glMatrixMode (GL_PROJECTION); glLoadIdentity (); glOrtho (-w/2.0, w/2.0, -h/2.0, h/2.0, 0, 10); // Main GL viewport is set up for orthographic view centered at // (0,0) and with width and height equal to the window dimensions IN // PIXEL UNITS. glMatrixMode (GL_MODELVIEW); clamp_view_to_window (); GLERRPRINT ("resizeGL exit"); } static void gl_rect (float xmin, float ymin, float xmax, float ymax, float z = 0, float smin = 0, float tmin = 0, float smax = 1, float tmax = 1, int rotate = 0) { float tex[] = { smin, tmin, smax, tmin, smax, tmax, smin, tmax }; glBegin (GL_POLYGON); glTexCoord2f (tex[(0+2*rotate)&7], tex[(1+2*rotate)&7]); glVertex3f (xmin, ymin, z); glTexCoord2f (tex[(2+2*rotate)&7], tex[(3+2*rotate)&7]); glVertex3f (xmax, ymin, z); glTexCoord2f (tex[(4+2*rotate)&7], tex[(5+2*rotate)&7]); glVertex3f (xmax, ymax, z); glTexCoord2f (tex[(6+2*rotate)&7], tex[(7+2*rotate)&7]); glVertex3f (xmin, ymax, z); glEnd (); } static void handle_orientation (int orientation, int width, int height, float &scale_x, float &scale_y, float &rotate_z, float &point_x, float &point_y, bool pixel=false) { switch (orientation) { case 2: // flipped horizontally scale_x = -1; point_x = width - point_x; if (pixel) // We want to access the pixel at (point_x,pointy), so we have to // substract 1 to get the right index. --point_x; break; case 3: // bottom up, rigth to left (rotated 180). scale_x = -1; scale_y = -1; point_x = width - point_x; point_y = height - point_y; if (pixel) { --point_x; --point_y; } break; case 4: // flipped vertically. scale_y = -1; point_y = height - point_y; if (pixel) --point_y; break; case 5: // transposed (flip horizontal & rotated 90 ccw). scale_x = -1; rotate_z = 90.0; std::swap (point_x, point_y); break; case 6: // rotated 90 cw. rotate_z = -270.0; std::swap (point_x, point_y); point_y = height - point_y; if (pixel) --point_y; break; case 7: // transverse, (flip horizontal & rotated 90 cw, r-to-l, b-to-t) scale_x = -1; rotate_z= -90.0; std::swap (point_x, point_y); point_x = width - point_x; point_y = height - point_y; if (pixel) { --point_x; --point_y; } break; case 8: // rotated 90 ccw. rotate_z = -90.0; std::swap (point_x, point_y); point_x = width - point_x; if (pixel) --point_x; break; case 1: // horizontal case 0: // unknown default: break; } } void IvGL::paintGL () { #ifndef NDEBUG Timer paint_image_time; paint_image_time.start(); #endif //std::cerr << "paintGL " << m_viewer.current_image() << " with zoom " << m_zoom << "\n"; glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); IvImage *img = m_current_image; if (! img || ! img->image_valid()) return; const ImageSpec &spec (img->spec()); float z = m_zoom; glPushMatrix (); // Transform is now same as the main GL viewport -- window pixels as // units, with (0,0) at the center of the visible unit. glTranslatef (0, 0, -5); // Pushed away from the camera 5 units. glScalef (1, -1, 1); // Flip y, because OGL's y runs from bottom to top. glScalef (z, z, 1); // Scaled by zoom level. So now xy units are image pixels as // displayed at the current zoom level, with the origin at the // center of the visible window. // Handle the orientation with OpenGL *before* translating our center. float scale_x = 1; float scale_y = 1; float rotate_z= 0; float real_centerx = m_centerx; float real_centery = m_centery; handle_orientation (img->orientation(), spec.width, spec.height, scale_x, scale_y, rotate_z, real_centerx, real_centery); glScalef (scale_x, scale_y, 1); glRotatef (rotate_z, 0, 0, 1); glTranslatef (-real_centerx, -real_centery, 0.0f); // Recentered so that the pixel space (m_centerx,m_centery) position is // at the center of the visible window. useshader (m_texture_width, m_texture_height); float smin = 0, smax = 1.0; float tmin = 0, tmax = 1.0; // Image pixels shown from the center to the edge of the window. int wincenterx = (int) ceil (width()/(2*m_zoom)); int wincentery = (int) ceil (height()/(2*m_zoom)); if (img->orientation() > 4) { std::swap (wincenterx, wincentery); } int xbegin = (int) floor (real_centerx) - wincenterx; xbegin = std::max (spec.x, xbegin - (xbegin % m_texture_width)); int ybegin = (int) floor (real_centery) - wincentery; ybegin = std::max (spec.y, ybegin - (ybegin % m_texture_height)); int xend = (int) floor (real_centerx) + wincenterx; xend = std::min (spec.x + spec.width, xend + m_texture_width - (xend % m_texture_width)); int yend = (int) floor (real_centery) + wincentery; yend = std::min (spec.y + spec.height, yend + m_texture_height - (yend % m_texture_height)); //std::cerr << "(" << xbegin << ',' << ybegin << ") - (" << xend << ',' << yend << ")\n"; // Provide some feedback int total_tiles = (int) (ceilf(float(xend-xbegin)/m_texture_width) * ceilf(float(yend-ybegin)/m_texture_height)); float tile_advance = 1.0f/total_tiles; float percent = tile_advance; m_viewer.statusViewInfo->hide (); m_viewer.statusProgress->show (); // FIXME: change the code path so we can take full advantage of async DMA // when using PBO. for (int ystart = ybegin ; ystart < yend; ystart += m_texture_height) { for (int xstart = xbegin ; xstart < xend; xstart += m_texture_width) { int tile_width = std::min (xend - xstart, m_texture_width); int tile_height = std::min (yend - ystart, m_texture_height); smax = tile_width/float (m_texture_width); tmax = tile_height/float (m_texture_height); //std::cerr << "xstart: " << xstart << ". ystart: " << ystart << "\n"; //std::cerr << "tile_width: " << tile_width << ". tile_height: " << tile_height << "\n"; // FIXME: This can get too slow. Some ideas: avoid sending the tex // images more than necessary, figure an optimum texture size, use // multiple texture objects. load_texture (xstart, ystart, tile_width, tile_height, percent); gl_rect (xstart, ystart, xstart+tile_width, ystart+tile_height, 0, smin, tmin, smax, tmax); percent += tile_advance; } } glPopMatrix (); if (m_viewer.pixelviewOn()) { paint_pixelview (); } // Show the status info again. m_viewer.statusProgress->hide (); m_viewer.statusViewInfo->show (); unsetCursor (); #ifndef NDEBUG std::cerr << "paintGL elapsed time: " << paint_image_time() << " seconds\n"; #endif } void IvGL::shadowed_text (float x, float y, float z, const std::string &s, const QFont &font) { QString q (s.c_str()); #if 0 glColor4f (0, 0, 0, 1); const int b = 2; // blur size for (int i = -b; i <= b; ++i) for (int j = -b; j <= b; ++j) renderText (x+i, y+j, q, font); #endif glColor4f (1, 1, 1, 1); renderText (x, y, z, q, font); } static int num_channels (int current_channel, int nchannels, ImageViewer::COLOR_MODE color_mode) { switch (color_mode) { case ImageViewer::RGBA: return clamp (nchannels-current_channel, 0, 4); case ImageViewer::RGB: case ImageViewer::LUMINANCE: return clamp (nchannels-current_channel, 0, 3); break; case ImageViewer::SINGLE_CHANNEL: case ImageViewer::HEATMAP: return 1; default: return nchannels; } } void IvGL::paint_pixelview () { IvImage *img = m_current_image; const ImageSpec &spec (img->spec()); // (xw,yw) are the window coordinates of the mouse. int xw, yw; get_focus_window_pixel (xw, yw); // (xp,yp) are the image-space [0..res-1] position of the mouse. int xp, yp; get_focus_image_pixel (xp, yp); glPushMatrix (); // Transform is now same as the main GL viewport -- window pixels as // units, with (0,0) at the center of the visible window. glTranslatef (0, 0, -1); // Pushed away from the camera 1 unit. This makes the pixel view // elements closer to the camera than the main view. if (m_viewer.pixelviewFollowsMouse()) { // Display closeup overtop mouse -- translate the coordinate system // so that it is centered at the mouse position. glTranslatef (xw - width()/2, -yw + height()/2, 0); } else { // Display closeup in corner -- translate the coordinate system so that // it is centered near the corner of the window. if (m_pixelview_left_corner) { glTranslatef (closeupsize * 0.5f + 5 - width () / 2, -closeupsize * 0.5f - 5 + height () / 2, 0); // If the mouse cursor is over the pixelview closeup when it's on // the upper left, switch to the upper right if ((xw < closeupsize + 5) && yw < (closeupsize + 5)) m_pixelview_left_corner = false; } else { glTranslatef (-closeupsize * 0.5f - 5 + width () / 2, -closeupsize * 0.5f + 5 + height () / 2, 0); // If the mouse cursor is over the pixelview closeup when it's on // the upper right, switch to the upper left if (xw > (width() - closeupsize - 5) && yw < (closeupsize + 5)) m_pixelview_left_corner = true; } } // In either case, the GL coordinate system is now scaled to window // pixel units, and centered on the middle of where the closeup // window is going to appear. All other coordinates from here on // (in this procedure) should be relative to the closeup window center. glPushAttrib (GL_ENABLE_BIT | GL_TEXTURE_BIT); useshader (closeuptexsize, closeuptexsize, true); float scale_x = 1.0f; float scale_y = 1.0f; float rotate_z= 0.0f; float real_xp = xp; float real_yp = yp; handle_orientation (img->orientation(), spec.width, spec.height, scale_x, scale_y, rotate_z, real_xp, real_yp, true); float smin = 0; float tmin = 0; float smax = 1.0f; float tmax = 1.0f; if (xp >= 0 && xp < img->oriented_width() && yp >= 0 && yp < img->oriented_height()) { // Keep the view within ncloseuppixels pixels. int xpp = clamp (real_xp, ncloseuppixels/2, spec.width - ncloseuppixels/2 - 1); int ypp = clamp (real_yp, ncloseuppixels/2, spec.height - ncloseuppixels/2 - 1); // Calculate patch of the image to use for the pixelview. int xbegin = std::max (xpp - ncloseuppixels/2, 0); int ybegin = std::max (ypp - ncloseuppixels/2, 0); int xend = std::min (xpp + ncloseuppixels/2+1, spec.width); int yend = std::min (ypp + ncloseuppixels/2+1, spec.height); smin = 0; tmin = 0; smax = float (xend-xbegin)/closeuptexsize; tmax = float (yend-ybegin)/closeuptexsize; //std::cerr << "img (" << xbegin << "," << ybegin << ") - (" << xend << "," << yend << ")\n"; //std::cerr << "tex (" << smin << "," << tmin << ") - (" << smax << "," << tmax << ")\n"; //std::cerr << "center mouse (" << xp << "," << yp << "), real (" << real_xp << "," << real_yp << ")\n"; int nchannels = img->nchannels(); // For simplicity, we don't support more than 4 channels without shaders // (yet). if (m_use_shaders) { nchannels = num_channels(m_viewer.current_channel(), nchannels, m_viewer.current_color_mode()); } void *zoombuffer = alloca ((xend-xbegin)*(yend-ybegin)*nchannels*spec.channel_bytes()); if (! m_use_shaders) { img->get_pixels (ROI (spec.x + xbegin, spec.x + xend, spec.y + ybegin, spec.y + yend), spec.format, zoombuffer); } else { ROI roi (spec.x + xbegin, spec.x + xend, spec.y + ybegin, spec.y + yend, 0, 1, m_viewer.current_channel(), m_viewer.current_channel()+nchannels); img->get_pixels (roi, spec.format, zoombuffer); } GLenum glformat, gltype, glinternalformat; typespec_to_opengl (spec, nchannels, gltype, glformat, glinternalformat); // Use pixelview's own texture, and upload the corresponding image patch. if (m_use_pbo) { glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 0); } glBindTexture (GL_TEXTURE_2D, m_pixelview_tex); glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, xend-xbegin, yend-ybegin, glformat, gltype, zoombuffer); GLERRPRINT ("After tsi2d"); } else { smin = -1; smax = -1; glDisable (GL_TEXTURE_2D); glColor3f (0.1f,0.1f,0.1f); } if (! m_use_shaders) { glDisable (GL_BLEND); } glPushMatrix (); glScalef (1, -1, 1); // Run y from top to bottom. glScalef (scale_x, scale_y, 1); glRotatef (rotate_z, 0, 0, 1); // This square is the closeup window itself gl_rect (-0.5f*closeupsize, -0.5f*closeupsize, 0.5f*closeupsize, 0.5f*closeupsize, 0, smin, tmin, smax, tmax); glPopMatrix (); glPopAttrib (); // Draw a second window, slightly behind the closeup window, as a // backdrop. It's partially transparent, having the effect of // darkening the main image view beneath the closeup window. It // extends slightly out from the closeup window (making it more // clearly visible), and also all the way down to cover the area // where the text will be printed, so it is very readable. const int yspacing = 18; glPushAttrib (GL_ENABLE_BIT | GL_CURRENT_BIT); glDisable (GL_TEXTURE_2D); if (m_use_shaders) { // Disable shaders for this. gl_use_program (0); } float extraspace = yspacing * (1 + spec.nchannels) + 4; glColor4f (0.1f, 0.1f, 0.1f, 0.5f); gl_rect (-0.5f*closeupsize-2, 0.5f*closeupsize+2, 0.5f*closeupsize+2, -0.5f*closeupsize - extraspace, -0.1f); if (1 /*xp >= 0 && xp < img->oriented_width() && yp >= 0 && yp < img->oriented_height()*/) { // Now we print text giving the mouse coordinates and the numerical // values of the pixel that the mouse is over. QFont font; font.setFixedPitch (true); float *fpixel = (float *) alloca (spec.nchannels*sizeof(float)); int textx = - closeupsize/2 + 4; int texty = - closeupsize/2 - yspacing; std::string s = Strutil::format ("(%d, %d)", (int) real_xp+spec.x, (int) real_yp+spec.y); shadowed_text (textx, texty, 0.0f, s, font); texty -= yspacing; img->getpixel ((int) real_xp+spec.x, (int) real_yp+spec.y, fpixel); for (int i = 0; i < spec.nchannels; ++i) { switch (spec.format.basetype) { case TypeDesc::UINT8 : { ImageBuf::ConstIterator p (*img, (int) real_xp+spec.x, (int) real_yp+spec.y); s = Strutil::format ("%s: %3d (%5.3f)", spec.channelnames[i].c_str(), (int)(p[i]), fpixel[i]); } break; case TypeDesc::UINT16 : { ImageBuf::ConstIterator p (*img, (int) real_xp+spec.x, (int) real_yp+spec.y); s = Strutil::format ("%s: %3d (%5.3f)", spec.channelnames[i].c_str(), (int)(p[i]), fpixel[i]); } break; default: // everything else, treat as float s = Strutil::format ("%s: %5.3f", spec.channelnames[i].c_str(), fpixel[i]); } shadowed_text (textx, texty, 0.0f, s, font); texty -= yspacing; } } glPopAttrib (); glPopMatrix (); } void IvGL::useshader (int tex_width, int tex_height, bool pixelview) { IvImage *img = m_viewer.cur(); if (! img) return; if (!m_use_shaders) { glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); BOOST_FOREACH (TexBuffer &tb, m_texbufs) { glBindTexture (GL_TEXTURE_2D, tb.tex_object); if (m_viewer.linearInterpolation ()) { glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } } return; } const ImageSpec &spec (img->spec()); gl_use_program (m_shader_program); GLERRPRINT ("After use program"); GLint loc; loc = gl_get_uniform_location ("startchannel"); if (m_viewer.current_channel()>=spec.nchannels) { gl_uniform (loc, -1); return; } gl_uniform (loc, 0); loc = gl_get_uniform_location ("imgtex"); // This is the texture unit, not the texture object gl_uniform (loc, 0); loc = gl_get_uniform_location ("gain"); float gain = powf (2.0, img->exposure ()); gl_uniform (loc, gain); loc = gl_get_uniform_location ("gamma"); gl_uniform (loc, img->gamma ()); loc = gl_get_uniform_location ("colormode"); gl_uniform (loc, m_viewer.current_color_mode()); loc = gl_get_uniform_location ("imgchannels"); gl_uniform (loc, spec.nchannels); loc = gl_get_uniform_location ("pixelview"); gl_uniform (loc, pixelview); loc = gl_get_uniform_location ("linearinterp"); gl_uniform (loc, m_viewer.linearInterpolation ()); loc = gl_get_uniform_location ("width"); gl_uniform (loc, tex_width); loc = gl_get_uniform_location ("height"); gl_uniform (loc, tex_height); GLERRPRINT ("After settting uniforms"); } void IvGL::update () { //std::cerr << "update image\n"; IvImage* img = m_viewer.cur(); if (! img) { m_current_image = NULL; return; } const ImageSpec &spec (img->spec()); int nchannels = img->nchannels(); // For simplicity, we don't support more than 4 channels without shaders // (yet). if (m_use_shaders) { nchannels = num_channels(m_viewer.current_channel(), nchannels, m_viewer.current_color_mode()); } if (! nchannels) return; // Don't bother, the shader will show blackness for us. GLenum gltype = GL_UNSIGNED_BYTE; GLenum glformat = GL_RGB; GLenum glinternalformat = GL_RGB; typespec_to_opengl (spec, nchannels, gltype, glformat, glinternalformat); m_texture_width = clamp (pow2roundup(spec.width), 1, m_max_texture_size); m_texture_height= clamp (pow2roundup(spec.height), 1, m_max_texture_size); if (m_use_pbo) { // Otherwise OpenGL will confuse the NULL with an index into one of // the PBOs. glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 0); } BOOST_FOREACH (TexBuffer &tb, m_texbufs) { tb.width = 0; tb.height= 0; glBindTexture (GL_TEXTURE_2D, tb.tex_object); glTexImage2D (GL_TEXTURE_2D, 0 /*mip level*/, glinternalformat, m_texture_width, m_texture_height, 0 /*border width*/, glformat, gltype, NULL /*data*/); GLERRPRINT ("Setting up texture"); } // Set the right type for the texture used for pixelview. glBindTexture (GL_TEXTURE_2D, m_pixelview_tex); glTexImage2D (GL_TEXTURE_2D, 0, glinternalformat, closeuptexsize, closeuptexsize, 0, glformat, gltype, NULL); GLERRPRINT ("Setting up pixelview texture"); // Resize the buffer at once, rather than create one each drawing. m_tex_buffer.resize (m_texture_width * m_texture_height * nchannels * spec.channel_bytes()); m_current_image = img; } void IvGL::view (float xcenter, float ycenter, float zoom, bool redraw) { m_centerx = xcenter; m_centery = ycenter; m_zoom = zoom; if (redraw) trigger_redraw (); } void IvGL::zoom (float newzoom, bool redraw) { view (m_centerx, m_centery, newzoom, redraw); } void IvGL::center (float x, float y, bool redraw) { view (x, y, m_viewer.zoom(), redraw); } void IvGL::pan (float dx, float dy) { center (m_centerx + dx, m_centery + dy); } void IvGL::remember_mouse (const QPoint &pos) { m_mousex = pos.x(); m_mousey = pos.y(); } void IvGL::clamp_view_to_window () { IvImage *img = m_current_image; if (! img) return; int w = width(), h = height(); float zoomedwidth = m_zoom * img->oriented_full_width(); float zoomedheight = m_zoom * img->oriented_full_height(); #if 0 float left = m_centerx - 0.5 * ((float)w / m_zoom); float top = m_centery - 0.5 * ((float)h / m_zoom); float right = m_centerx + 0.5 * ((float)w / m_zoom); float bottom = m_centery + 0.5 * ((float)h / m_zoom); std::cerr << "Window size is " << w << " x " << h << "\n"; std::cerr << "Center (pixel coords) is " << m_centerx << ", " << m_centery << "\n"; std::cerr << "Top left (pixel coords) is " << left << ", " << top << "\n"; std::cerr << "Bottom right (pixel coords) is " << right << ", " << bottom << "\n"; #endif int xmin = std::min (img->oriented_x(), img->oriented_full_x()); int xmax = std::max (img->oriented_x()+img->oriented_width(), img->oriented_full_x()+img->oriented_full_width()); int ymin = std::min (img->oriented_y(), img->oriented_full_y()); int ymax = std::max (img->oriented_y()+img->oriented_height(), img->oriented_full_y()+img->oriented_full_height()); // Don't let us scroll off the edges if (zoomedwidth >= w) { m_centerx = Imath::clamp (m_centerx, xmin + 0.5f*w/m_zoom, xmax - 0.5f*w/m_zoom); } else { m_centerx = img->oriented_full_x() + img->oriented_full_width()/2; } if (zoomedheight >= h) { m_centery = Imath::clamp (m_centery, ymin + 0.5f*h/m_zoom, ymax - 0.5f*h/m_zoom); } else { m_centery = img->oriented_full_y() + img->oriented_full_height()/2; } } void IvGL::mousePressEvent (QMouseEvent *event) { remember_mouse (event->pos()); int mousemode = m_viewer.mouseModeComboBox->currentIndex (); bool Alt = (event->modifiers() & Qt::AltModifier); m_drag_button = event->button(); if (! m_mouse_activation) { switch (event->button()) { case Qt::LeftButton : if (mousemode == ImageViewer::MouseModeZoom && !Alt) m_viewer.zoomIn(); else m_dragging = true; return; case Qt::RightButton : if (mousemode == ImageViewer::MouseModeZoom && !Alt) m_viewer.zoomOut(); else m_dragging = true; return; case Qt::MidButton : m_dragging = true; // FIXME: should this be return rather than break? break; default: break; } } else m_mouse_activation = false; parent_t::mousePressEvent (event); } void IvGL::mouseReleaseEvent (QMouseEvent *event) { remember_mouse (event->pos()); m_drag_button = Qt::NoButton; m_dragging = false; parent_t::mouseReleaseEvent (event); } void IvGL::mouseMoveEvent (QMouseEvent *event) { QPoint pos = event->pos(); // FIXME - there's probably a better Qt way than tracking the button // myself. bool Alt = (event->modifiers() & Qt::AltModifier); int mousemode = m_viewer.mouseModeComboBox->currentIndex (); bool do_pan = false, do_zoom = false, do_wipe = false; bool do_select = false, do_annotate = false; switch (mousemode) { case ImageViewer::MouseModeZoom : if ((m_drag_button == Qt::MidButton) || (m_drag_button == Qt::LeftButton && Alt)) { do_pan = true; } else if (m_drag_button == Qt::RightButton && Alt) { do_zoom = true; } break; case ImageViewer::MouseModePan : if (m_drag_button != Qt::NoButton) do_pan = true; break; case ImageViewer::MouseModeWipe : if (m_drag_button != Qt::NoButton) do_wipe = true; break; case ImageViewer::MouseModeSelect : if (m_drag_button != Qt::NoButton) do_select = true; break; case ImageViewer::MouseModeAnnotate : if (m_drag_button != Qt::NoButton) do_annotate = true; break; } if (do_pan) { float dx = (pos.x() - m_mousex) / m_zoom; float dy = (pos.y() - m_mousey) / m_zoom; pan (-dx, -dy); } else if (do_zoom) { float dx = (pos.x() - m_mousex); float dy = (pos.y() - m_mousey); float z = m_viewer.zoom() * (1.0 + 0.005 * (dx + dy)); z = Imath::clamp (z, 0.01f, 256.0f); m_viewer.zoom (z); m_viewer.fitImageToWindowAct->setChecked (false); } else if (do_wipe) { // FIXME -- unimplemented } else if (do_select) { // FIXME -- unimplemented } else if (do_annotate) { // FIXME -- unimplemented } remember_mouse (pos); if (m_viewer.pixelviewOn()) trigger_redraw (); parent_t::mouseMoveEvent (event); } void IvGL::wheelEvent (QWheelEvent *event) { m_mouse_activation = false; if (event->orientation() == Qt::Vertical) { // TODO: Update this to keep the zoom centered on the event .x, .y float oldzoom = m_viewer.zoom(); float newzoom = (event->delta()>0) ? \ pow2roundupf (oldzoom) : pow2rounddownf (oldzoom); m_viewer.zoom (newzoom); event->accept(); } } void IvGL::focusOutEvent (QFocusEvent*) { m_mouse_activation = true; } void IvGL::get_focus_window_pixel (int &x, int &y) { x = m_mousex; y = m_mousey; } void IvGL::get_focus_image_pixel (int &x, int &y) { // w,h are the dimensions of the visible window, in pixels int w = width(), h = height(); float z = m_zoom; // left,top,right,bottom are the borders of the visible window, in // pixel coordinates float left = m_centerx - 0.5 * w / z; float top = m_centery - 0.5 * h / z; float right = m_centerx + 0.5 * w / z; float bottom = m_centery + 0.5 * h / z; // normx,normy are the position of the mouse, in normalized (i.e. [0..1]) // visible window coordinates. float normx = (float)(m_mousex + 0.5f) / w; float normy = (float)(m_mousey + 0.5f) / h; // imgx,imgy are the position of the mouse, in pixel coordinates float imgx = Imath::lerp (left, right, normx); float imgy = Imath::lerp (top, bottom, normy); // So finally x,y are the coordinates of the image pixel (on [0,res-1]) // underneath the mouse cursor. //FIXME: Shouldn't this take image rotation into account? x = (int) floorf (imgx); y = (int) floorf (imgy); #if 0 std::cerr << "get_focus_pixel\n"; std::cerr << " mouse window pixel coords " << m_mousex << ' ' << m_mousey << "\n"; std::cerr << " mouse window NDC coords " << normx << ' ' << normy << '\n'; std::cerr << " center image coords " << m_centerx << ' ' << m_centery << "\n"; std::cerr << " left,top = " << left << ' ' << top << "\n"; std::cerr << " right,bottom = " << right << ' ' << bottom << "\n"; std::cerr << " mouse image coords " << imgx << ' ' << imgy << "\n"; std::cerr << " mouse pixel image coords " << x << ' ' << y << "\n"; #endif } inline void IvGL::gl_use_program (int program) { if (m_shaders_using_extensions) glUseProgramObjectARB (program); else glUseProgram (program); } inline GLint IvGL::gl_get_uniform_location (const char *uniform) { if (m_shaders_using_extensions) return glGetUniformLocationARB (m_shader_program, uniform); else return glGetUniformLocation (m_shader_program, uniform); } inline void IvGL::gl_uniform (GLint location, float value) { if (m_shaders_using_extensions) glUniform1fARB (location, value); else glUniform1f (location, value); } inline void IvGL::gl_uniform (GLint location, int value) { if (m_shaders_using_extensions) glUniform1iARB (location, value); else glUniform1i (location, value); } void IvGL::print_shader_log (std::ostream& out, const GLuint shader_id) const { GLint size = 0; glGetShaderiv (shader_id, GL_INFO_LOG_LENGTH, &size); if (size > 0) { GLchar* log = new GLchar[size]; if (m_shaders_using_extensions) { glGetInfoLogARB (shader_id, size, NULL, log); } else { glGetShaderInfoLog (shader_id, size, NULL, log); } out << "compile log:\n" << log << "---\n"; delete[] log; } } void IvGL::check_gl_extensions (void) { #ifndef FORCE_OPENGL_1 m_use_shaders = glewIsSupported("GL_VERSION_2_0"); if (!m_use_shaders && glewIsSupported("GL_ARB_shader_objects " "GL_ARB_vertex_shader " "GL_ARB_fragment_shader")) { m_use_shaders = true; m_shaders_using_extensions = true; } m_use_srgb = glewIsSupported("GL_VERSION_2_1") || glewIsSupported("GL_EXT_texture_sRGB"); m_use_halffloat = glewIsSupported("GL_VERSION_3_0") || glewIsSupported("GL_ARB_half_float_pixel") || glewIsSupported("GL_NV_half_float_pixel"); m_use_float = glewIsSupported("GL_VERSION_3_0") || glewIsSupported("GL_ARB_texture_float") || glewIsSupported("GL_ATI_texture_float"); m_use_pbo = glewIsSupported("GL_VERSION_1_5") || glewIsSupported("GL_ARB_pixel_buffer_object"); #else std::cerr << "Not checking GL extensions\n"; #endif m_max_texture_size = 0; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_max_texture_size); // FIXME: Need a smarter way to handle (video) memory. // Don't assume that systems capable of using 8k^2 textures have enough // resources to use more than one of those at the same time. m_max_texture_size = std::min(m_max_texture_size, 4096); #ifndef NDEBUG // Report back... std::cerr << "OpenGL Shading Language supported: " << m_use_shaders << "\n"; if (m_shaders_using_extensions) { std::cerr << "\t(with extensions)\n"; } std::cerr << "OpenGL sRGB color space textures supported: " << m_use_srgb << "\n"; std::cerr << "OpenGL half-float pixels supported: " << m_use_halffloat << "\n"; std::cerr << "OpenGL float texture storage supported: " << m_use_float << "\n"; std::cerr << "OpenGL pixel buffer object supported: " << m_use_pbo << "\n"; std::cerr << "OpenGL max texture dimension: " << m_max_texture_size << "\n"; #endif } void IvGL::typespec_to_opengl (const ImageSpec &spec, int nchannels, GLenum &gltype, GLenum &glformat, GLenum &glinternalformat) const { switch (spec.format.basetype) { case TypeDesc::FLOAT : gltype = GL_FLOAT; break; case TypeDesc::HALF : if (m_use_halffloat) { gltype = GL_HALF_FLOAT_ARB; } else { // If we reach here then something really wrong // happened: When half-float is not supported, // the image should be loaded as UINT8 (no GLSL // support) or FLOAT (GLSL support). // See IvImage::loadCurrentImage() std::cerr << "Tried to load an unsupported half-float image.\n"; gltype = GL_INVALID_ENUM; } break; case TypeDesc::INT : gltype = GL_INT; break; case TypeDesc::UINT : gltype = GL_UNSIGNED_INT; break; case TypeDesc::INT16 : gltype = GL_SHORT; break; case TypeDesc::UINT16 : gltype = GL_UNSIGNED_SHORT; break; case TypeDesc::INT8 : gltype = GL_BYTE; break; case TypeDesc::UINT8 : gltype = GL_UNSIGNED_BYTE; break; default: gltype = GL_UNSIGNED_BYTE; // punt break; } bool issrgb = iequals (spec.get_string_attribute ("oiio:ColorSpace"), "sRGB"); glinternalformat = nchannels; if (nchannels == 1) { glformat = GL_LUMINANCE; if (m_use_srgb && issrgb) { if (spec.format.basetype == TypeDesc::UINT8) { glinternalformat = GL_SLUMINANCE8; } else { glinternalformat = GL_SLUMINANCE; } } else if (spec.format.basetype == TypeDesc::UINT8) { glinternalformat = GL_LUMINANCE8; } else if (spec.format.basetype == TypeDesc::UINT16) { glinternalformat = GL_LUMINANCE16; } else if (m_use_float && spec.format.basetype == TypeDesc::FLOAT) { glinternalformat = GL_LUMINANCE32F_ARB; } else if (m_use_float && spec.format.basetype == TypeDesc::HALF) { glinternalformat = GL_LUMINANCE16F_ARB; } } else if (nchannels == 2) { glformat = GL_LUMINANCE_ALPHA; if (m_use_srgb && issrgb) { if (spec.format.basetype == TypeDesc::UINT8) { glinternalformat = GL_SLUMINANCE8_ALPHA8; } else { glinternalformat = GL_SLUMINANCE_ALPHA; } } else if (spec.format.basetype == TypeDesc::UINT8) { glinternalformat = GL_LUMINANCE8_ALPHA8; } else if (spec.format.basetype == TypeDesc::UINT16) { glinternalformat = GL_LUMINANCE16_ALPHA16; } else if (m_use_float && spec.format.basetype == TypeDesc::FLOAT) { glinternalformat = GL_LUMINANCE_ALPHA32F_ARB; } else if (m_use_float && spec.format.basetype == TypeDesc::HALF) { glinternalformat = GL_LUMINANCE_ALPHA16F_ARB; } } else if (nchannels == 3) { glformat = GL_RGB; if (m_use_srgb && issrgb) { if (spec.format.basetype == TypeDesc::UINT8) { glinternalformat = GL_SRGB8; } else { glinternalformat = GL_SRGB; } } else if (spec.format.basetype == TypeDesc::UINT8) { glinternalformat = GL_RGB8; } else if (spec.format.basetype == TypeDesc::UINT16) { glinternalformat = GL_RGB16; } else if (m_use_float && spec.format.basetype == TypeDesc::FLOAT) { glinternalformat = GL_RGB32F_ARB; } else if (m_use_float && spec.format.basetype == TypeDesc::HALF) { glinternalformat = GL_RGB16F_ARB; } } else if (nchannels == 4) { glformat = GL_RGBA; if (m_use_srgb && issrgb) { if (spec.format.basetype == TypeDesc::UINT8) { glinternalformat = GL_SRGB8_ALPHA8; } else { glinternalformat = GL_SRGB_ALPHA; } } else if (spec.format.basetype == TypeDesc::UINT8) { glinternalformat = GL_RGBA8; } else if (spec.format.basetype == TypeDesc::UINT16) { glinternalformat = GL_RGBA16; } else if (m_use_float && spec.format.basetype == TypeDesc::FLOAT) { glinternalformat = GL_RGBA32F_ARB; } else if (m_use_float && spec.format.basetype == TypeDesc::HALF) { glinternalformat = GL_RGBA16F_ARB; } } else { glformat = GL_INVALID_ENUM; glinternalformat = GL_INVALID_ENUM; } } void IvGL::load_texture (int x, int y, int width, int height, float percent) { const ImageSpec &spec = m_current_image->spec (); // Find if this has already been loaded. BOOST_FOREACH (TexBuffer &tb, m_texbufs) { if (tb.x == x && tb.y == y && tb.width >= width && tb.height >= height) { glBindTexture (GL_TEXTURE_2D, tb.tex_object); return; } } // Make it somewhat obvious to the user that some progress is happening // here. m_viewer.statusProgress->setValue ((int)(percent*100)); // FIXME: Check whether this works ok (ie, no 'recursive repaint' messages) // on all platforms. m_viewer.statusProgress->repaint (); setCursor (Qt::WaitCursor); int nchannels = spec.nchannels; // For simplicity, we don't support more than 4 channels without shaders // (yet). if (m_use_shaders) { nchannels = num_channels(m_viewer.current_channel(), nchannels, m_viewer.current_color_mode()); } GLenum gltype, glformat, glinternalformat; typespec_to_opengl (spec, nchannels, gltype, glformat, glinternalformat); TexBuffer &tb = m_texbufs[m_last_texbuf_used]; tb.x = x; tb.y = y; tb.width = width; tb.height = height; // Copy the imagebuf pixels we need, that's the only way we can do // it safely since ImageBuf has a cache underneath and the whole image // may not be resident at once. if (! m_use_shaders) { m_current_image->get_pixels (ROI (x, x + width, y, y + height), spec.format, &m_tex_buffer[0]); } else { m_current_image->get_pixels (ROI (x, x+width, y, y+height, 0, 1, m_viewer.current_channel(), m_viewer.current_channel() + nchannels), spec.format, &m_tex_buffer[0]); } if (m_use_pbo) { glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, m_pbo_objects[m_last_pbo_used]); glBufferDataARB (GL_PIXEL_UNPACK_BUFFER_ARB, width * height * spec.pixel_bytes(), &m_tex_buffer[0], GL_STREAM_DRAW_ARB); GLERRPRINT ("After buffer data"); m_last_pbo_used = (m_last_pbo_used + 1) & 1; } // When using PBO this is the offset within the buffer. void *data = 0; if (! m_use_pbo) data = &m_tex_buffer[0]; glBindTexture (GL_TEXTURE_2D, tb.tex_object); GLERRPRINT ("After bind texture"); glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, width, height, glformat, gltype, data); GLERRPRINT ("After loading sub image"); m_last_texbuf_used = (m_last_texbuf_used + 1) % m_texbufs.size(); } bool IvGL::is_too_big (float width, float height) { unsigned int tiles = (unsigned int) (ceilf(width/m_max_texture_size) * ceilf(height/m_max_texture_size)); return tiles > m_texbufs.size(); } openimageio-1.7.17~dfsg0.orig/src/iv/imageviewer.cpp0000644000175000017500000017605213151711064020555 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "imageviewer.h" #include "ivgl.h" #include #include #ifndef WIN32 #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/timer.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/sysutil.h" #include "OpenImageIO/filesystem.h" #include "ivutils.h" namespace { bool IsSpecSrgb(const ImageSpec & spec) { return (Strutil::iequals (spec.get_string_attribute ("oiio:ColorSpace"), "sRGB")); } } static const char *s_file_filters = "" "Image Files (*.bmp *.cin *.dds *.dpx *.f3d *.fits *.gif *.hdr *.ico *.iff " "*.jpg *.jpe *.jpeg *.jif *.jfif *.jfi *.jp2 *.j2k *.exr *.png *.pbm *.pgm " "*.ppm *.ptex *.rla *.sgi *.rgb *.rgba *.bw *.int *.inta *.pic *.tga " "*.tpic *.tif *.tiff *.tx *.env *.sm *.vsm *.zfile);;" "BMP (*.bmp);;" "Cineon (*.cin);;" "Direct Draw Surface (*.dds);;" "DPX (*.dpx);;" "Field3D (*.f3d);;" "FITS (*.fits);;" "GIF (*.gif);;" "HDR/RGBE (*.hdr);;" "Icon (*.ico);;" "IFF (*.iff);;" "JPEG (*.jpg *.jpe *.jpeg *.jif *.jfif *.jfi);;" "JPEG-2000 (*.jp2 *.j2k);;" "OpenEXR (*.exr);;" "Portable Network Graphics (*.png);;" "PNM / Netpbm (*.pbm *.pgm *.ppm);;" "Ptex (*.ptex);;" "RLA (*.rla);;" "SGI (*.sgi *.rgb *.rgba *.bw *.int *.inta);;" "Softimage PIC (*.pic);;" "Targa (*.tga *.tpic);;" "TIFF (*.tif *.tiff *.tx *.env *.sm *.vsm);;" "Zfile (*.zfile);;" "All Files (*)"; ImageViewer::ImageViewer () : infoWindow(NULL), preferenceWindow(NULL), darkPaletteBox(NULL), m_current_image(-1), m_current_channel(0), m_color_mode(RGBA), m_last_image(-1), m_zoom(1), m_fullscreen(false), m_default_gamma(1), m_darkPalette(false) { readSettings (false); const char *gamenv = getenv ("GAMMA"); if (gamenv) { float g = atof (gamenv); if (g >= 0.1 && g <= 5) m_default_gamma = g; } // FIXME -- would be nice to have a more nuanced approach to display // color space, in particular knowing whether the display is sRGB. // Also, some time in the future we may want a real 3D LUT for // "film look", etc. if (darkPalette()) m_palette = QPalette (Qt::darkGray); // darkGray? else m_palette = QPalette (); QApplication::setPalette (m_palette); // FIXME -- why not work? this->setPalette (m_palette); slideTimer = new QTimer(); slideDuration_ms = 5000; slide_loop = true; glwin = new IvGL (this, *this); glwin->setPalette (m_palette); glwin->resize (m_default_width, m_default_height); setCentralWidget (glwin); createActions(); createMenus(); createToolBars(); createStatusBar(); readSettings(); setWindowTitle (tr("Image Viewer")); resize (m_default_width, m_default_height); // setSizePolicy (QSizePolicy::Ignored, QSizePolicy::Ignored); } ImageViewer::~ImageViewer () { BOOST_FOREACH (IvImage *i, m_images) delete i; } void ImageViewer::closeEvent (QCloseEvent*) { writeSettings (); } void ImageViewer::createActions() { openAct = new QAction(tr("&Open..."), this); openAct->setShortcut(tr("Ctrl+O")); connect(openAct, SIGNAL(triggered()), this, SLOT(open())); for (size_t i = 0; i < MaxRecentFiles; ++i) { openRecentAct[i] = new QAction (this); openRecentAct[i]->setVisible (false); connect (openRecentAct[i], SIGNAL(triggered()), this, SLOT(openRecentFile())); } reloadAct = new QAction(tr("&Reload image"), this); reloadAct->setShortcut(tr("Ctrl+R")); connect(reloadAct, SIGNAL(triggered()), this, SLOT(reload())); closeImgAct = new QAction(tr("&Close Image"), this); closeImgAct->setShortcut(tr("Ctrl+W")); connect(closeImgAct, SIGNAL(triggered()), this, SLOT(closeImg())); saveAsAct = new QAction(tr("&Save As..."), this); saveAsAct->setShortcut(tr("Ctrl+S")); connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs())); saveWindowAsAct = new QAction(tr("Save Window As..."), this); connect(saveWindowAsAct, SIGNAL(triggered()), this, SLOT(saveWindowAs())); saveSelectionAsAct = new QAction(tr("Save Selection As..."), this); connect(saveSelectionAsAct, SIGNAL(triggered()), this, SLOT(saveSelectionAs())); printAct = new QAction(tr("&Print..."), this); printAct->setShortcut(tr("Ctrl+P")); printAct->setEnabled(false); connect(printAct, SIGNAL(triggered()), this, SLOT(print())); deleteCurrentImageAct = new QAction(tr("&Delete from disk"), this); deleteCurrentImageAct->setShortcut(tr("Delete")); connect(deleteCurrentImageAct, SIGNAL(triggered()), this, SLOT(deleteCurrentImage())); editPreferencesAct = new QAction(tr("&Preferences..."), this); editPreferencesAct->setShortcut(tr("Ctrl+,")); editPreferencesAct->setEnabled (true); connect (editPreferencesAct, SIGNAL(triggered()), this, SLOT(editPreferences())); exitAct = new QAction(tr("E&xit"), this); exitAct->setShortcut(tr("Ctrl+Q")); connect(exitAct, SIGNAL(triggered()), this, SLOT(close())); exposurePlusOneTenthStopAct = new QAction(tr("Exposure +1/10 stop"), this); exposurePlusOneTenthStopAct->setShortcut(tr("]")); connect(exposurePlusOneTenthStopAct, SIGNAL(triggered()), this, SLOT(exposurePlusOneTenthStop())); exposurePlusOneHalfStopAct = new QAction(tr("Exposure +1/2 stop"), this); exposurePlusOneHalfStopAct->setShortcut(tr("}")); connect(exposurePlusOneHalfStopAct, SIGNAL(triggered()), this, SLOT(exposurePlusOneHalfStop())); exposureMinusOneTenthStopAct = new QAction(tr("Exposure -1/10 stop"), this); exposureMinusOneTenthStopAct->setShortcut(tr("[")); connect(exposureMinusOneTenthStopAct, SIGNAL(triggered()), this, SLOT(exposureMinusOneTenthStop())); exposureMinusOneHalfStopAct = new QAction(tr("Exposure -1/2 stop"), this); exposureMinusOneHalfStopAct->setShortcut(tr("{")); connect(exposureMinusOneHalfStopAct, SIGNAL(triggered()), this, SLOT(exposureMinusOneHalfStop())); gammaPlusAct = new QAction(tr("Gamma +0.1"), this); gammaPlusAct->setShortcut(tr(")")); connect(gammaPlusAct, SIGNAL(triggered()), this, SLOT(gammaPlus())); gammaMinusAct = new QAction(tr("Gamma -0.1"), this); gammaMinusAct->setShortcut(tr("(")); connect(gammaMinusAct, SIGNAL(triggered()), this, SLOT(gammaMinus())); viewChannelFullAct = new QAction(tr("Full Color"), this); viewChannelFullAct->setShortcut(tr("c")); viewChannelFullAct->setCheckable (true); viewChannelFullAct->setChecked (true); connect(viewChannelFullAct, SIGNAL(triggered()), this, SLOT(viewChannelFull())); viewChannelRedAct = new QAction(tr("Red"), this); viewChannelRedAct->setShortcut(tr("r")); viewChannelRedAct->setCheckable (true); connect(viewChannelRedAct, SIGNAL(triggered()), this, SLOT(viewChannelRed())); viewChannelGreenAct = new QAction(tr("Green"), this); viewChannelGreenAct->setShortcut(tr("g")); viewChannelGreenAct->setCheckable (true); connect(viewChannelGreenAct, SIGNAL(triggered()), this, SLOT(viewChannelGreen())); viewChannelBlueAct = new QAction(tr("Blue"), this); viewChannelBlueAct->setShortcut(tr("b")); viewChannelBlueAct->setCheckable (true); connect(viewChannelBlueAct, SIGNAL(triggered()), this, SLOT(viewChannelBlue())); viewChannelAlphaAct = new QAction(tr("Alpha"), this); viewChannelAlphaAct->setShortcut(tr("a")); viewChannelAlphaAct->setCheckable (true); connect(viewChannelAlphaAct, SIGNAL(triggered()), this, SLOT(viewChannelAlpha())); viewColorLumAct = new QAction (tr("Luminance"), this); viewColorLumAct->setShortcut (tr("l")); viewColorLumAct->setCheckable (true); connect(viewColorLumAct, SIGNAL(triggered()), this, SLOT(viewChannelLuminance())); viewColorRGBAAct = new QAction (tr("RGBA"), this); //viewColorRGBAAct->setShortcut (tr("Ctrl+c")); viewColorRGBAAct->setCheckable (true); viewColorRGBAAct->setChecked (true); connect(viewColorRGBAAct, SIGNAL(triggered()), this, SLOT(viewColorRGBA())); viewColorRGBAct = new QAction (tr("RGB"), this); //viewColorRGBAct->setShortcut (tr("Ctrl+a")); viewColorRGBAct->setCheckable (true); connect(viewColorRGBAct, SIGNAL(triggered()), this, SLOT(viewColorRGB())); viewColor1ChAct = new QAction (tr("Single channel"), this); viewColor1ChAct->setShortcut (tr("1")); viewColor1ChAct->setCheckable (true); connect(viewColor1ChAct, SIGNAL(triggered()), this, SLOT(viewColor1Ch())); viewColorHeatmapAct = new QAction (tr("Single channel (Heatmap)"), this); viewColorHeatmapAct->setShortcut (tr("h")); viewColorHeatmapAct->setCheckable (true); connect(viewColorHeatmapAct, SIGNAL(triggered()), this, SLOT(viewColorHeatmap())); viewChannelPrevAct = new QAction(tr("Prev Channel"), this); viewChannelPrevAct->setShortcut(tr(",")); connect(viewChannelPrevAct, SIGNAL(triggered()), this, SLOT(viewChannelPrev())); viewChannelNextAct = new QAction(tr("Next Channel"), this); viewChannelNextAct->setShortcut(tr(".")); connect(viewChannelNextAct, SIGNAL(triggered()), this, SLOT(viewChannelNext())); viewSubimagePrevAct = new QAction(tr("Prev Subimage"), this); viewSubimagePrevAct->setShortcut(tr("<")); connect(viewSubimagePrevAct, SIGNAL(triggered()), this, SLOT(viewSubimagePrev())); viewSubimageNextAct = new QAction(tr("Next Subimage"), this); viewSubimageNextAct->setShortcut(tr(">")); connect(viewSubimageNextAct, SIGNAL(triggered()), this, SLOT(viewSubimageNext())); zoomInAct = new QAction(tr("Zoom &In"), this); zoomInAct->setShortcut(tr("Ctrl++")); connect(zoomInAct, SIGNAL(triggered()), this, SLOT(zoomIn())); zoomOutAct = new QAction(tr("Zoom &Out"), this); zoomOutAct->setShortcut(tr("Ctrl+-")); connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(zoomOut())); normalSizeAct = new QAction(tr("&Normal Size (1:1)"), this); normalSizeAct->setShortcut(tr("Ctrl+0")); connect(normalSizeAct, SIGNAL(triggered()), this, SLOT(normalSize())); fitWindowToImageAct = new QAction(tr("&Fit Window to Image"), this); fitWindowToImageAct->setShortcut(tr("f")); connect(fitWindowToImageAct, SIGNAL(triggered()), this, SLOT(fitWindowToImage())); fitImageToWindowAct = new QAction(tr("Fit Image to Window"), this); // fitImageToWindowAct->setEnabled(false); fitImageToWindowAct->setCheckable(true); fitImageToWindowAct->setShortcut(tr("Alt+f")); connect(fitImageToWindowAct, SIGNAL(triggered()), this, SLOT(fitImageToWindow())); fullScreenAct = new QAction(tr("Full screen"), this); // fullScreenAct->setEnabled(false); // fullScreenAct->setCheckable(true); fullScreenAct->setShortcut(tr("Ctrl+f")); connect(fullScreenAct, SIGNAL(triggered()), this, SLOT(fullScreenToggle())); aboutAct = new QAction(tr("&About"), this); connect(aboutAct, SIGNAL(triggered()), this, SLOT(about())); prevImageAct = new QAction(tr("Previous Image"), this); prevImageAct->setShortcut(tr("PgUp")); // prevImageAct->setEnabled(true); connect (prevImageAct, SIGNAL(triggered()), this, SLOT(prevImage())); nextImageAct = new QAction(tr("Next Image"), this); nextImageAct->setShortcut(tr("PgDown")); // nextImageAct->setEnabled(true); connect (nextImageAct, SIGNAL(triggered()), this, SLOT(nextImage())); toggleImageAct = new QAction(tr("Toggle image"), this); toggleImageAct->setShortcut(tr("T")); // toggleImageAct->setEnabled(true); connect (toggleImageAct, SIGNAL(triggered()), this, SLOT(toggleImage())); slideShowAct = new QAction(tr("Start Slide Show"), this); connect(slideShowAct, SIGNAL(triggered()), this, SLOT(slideShow())); slideLoopAct = new QAction(tr("Loop slide show"), this); slideLoopAct->setCheckable (true); slideLoopAct->setChecked (true); connect(slideLoopAct, SIGNAL(triggered()), this, SLOT(slideLoop())); slideNoLoopAct = new QAction(tr("Stop at end"), this); slideNoLoopAct->setCheckable (true); connect(slideNoLoopAct, SIGNAL(triggered()), this, SLOT(slideNoLoop())); sortByNameAct = new QAction(tr("By Name"), this); connect(sortByNameAct, SIGNAL(triggered()), this, SLOT(sortByName())); sortByPathAct = new QAction(tr("By File Path"), this); connect(sortByPathAct, SIGNAL(triggered()), this, SLOT(sortByPath())); sortByImageDateAct = new QAction(tr("By Image Date"), this); connect(sortByImageDateAct, SIGNAL(triggered()), this, SLOT(sortByImageDate())); sortByFileDateAct = new QAction(tr("By File Date"), this); connect(sortByFileDateAct, SIGNAL(triggered()), this, SLOT(sortByFileDate())); sortReverseAct = new QAction(tr("Reverse current order"), this); connect(sortReverseAct, SIGNAL(triggered()), this, SLOT(sortReverse())); showInfoWindowAct = new QAction(tr("&Image info..."), this); showInfoWindowAct->setShortcut(tr("Ctrl+I")); // showInfoWindowAct->setEnabled(true); connect (showInfoWindowAct, SIGNAL(triggered()), this, SLOT(showInfoWindow())); showPixelviewWindowAct = new QAction(tr("&Pixel closeup view..."), this); showPixelviewWindowAct->setCheckable (true); showPixelviewWindowAct->setShortcut(tr("P")); connect (showPixelviewWindowAct, SIGNAL(triggered()), this, SLOT(showPixelviewWindow())); pixelviewFollowsMouseBox = new QCheckBox (tr("Pixel view follows mouse")); pixelviewFollowsMouseBox->setChecked (false); linearInterpolationBox = new QCheckBox (tr("Linear interpolation")); linearInterpolationBox->setChecked (true); darkPaletteBox = new QCheckBox (tr("Dark palette")); darkPaletteBox->setChecked (true); autoMipmap = new QCheckBox (tr("Generate mipmaps (requires restart)")); autoMipmap->setChecked (false); maxMemoryICLabel = new QLabel (tr("Image Cache max memory (requires restart)")); maxMemoryIC = new QSpinBox (); if (sizeof (void *) == 4) maxMemoryIC->setRange (128, 2048); //2GB is enough for 32 bit machines else maxMemoryIC->setRange (128, 8192); //8GB probably ok for 64 bit maxMemoryIC->setSingleStep (64); maxMemoryIC->setSuffix (" MB"); slideShowDurationLabel = new QLabel (tr("Slide Show delay")); slideShowDuration = new QSpinBox (); slideShowDuration->setRange (1, 3600); slideShowDuration->setSingleStep (1); slideShowDuration->setSuffix (" s"); slideShowDuration->setAccelerated (true); connect(slideShowDuration, SIGNAL(valueChanged(int)), this, SLOT(setSlideShowDuration(int))); } void ImageViewer::createMenus() { openRecentMenu = new QMenu(tr("Open recent..."), this); for (size_t i = 0; i < MaxRecentFiles; ++i) openRecentMenu->addAction (openRecentAct[i]); fileMenu = new QMenu(tr("&File"), this); fileMenu->addAction (openAct); fileMenu->addMenu (openRecentMenu); fileMenu->addAction (reloadAct); fileMenu->addAction (closeImgAct); fileMenu->addSeparator (); fileMenu->addAction (saveAsAct); fileMenu->addAction (saveWindowAsAct); fileMenu->addAction (saveSelectionAsAct); fileMenu->addSeparator (); fileMenu->addAction (printAct); fileMenu->addAction (deleteCurrentImageAct); fileMenu->addSeparator (); fileMenu->addAction (editPreferencesAct); fileMenu->addAction (exitAct); menuBar()->addMenu (fileMenu); // Copy // Paste // Clear selection // radio: prioritize selection, crop selection expgamMenu = new QMenu(tr("Exposure/gamma")); // submenu expgamMenu->addAction (exposureMinusOneHalfStopAct); expgamMenu->addAction (exposureMinusOneTenthStopAct); expgamMenu->addAction (exposurePlusOneHalfStopAct); expgamMenu->addAction (exposurePlusOneTenthStopAct); expgamMenu->addAction (gammaMinusAct); expgamMenu->addAction (gammaPlusAct); // imageMenu = new QMenu(tr("&Image"), this); // menuBar()->addMenu (imageMenu); slideMenu = new QMenu(tr("Slide Show")); slideMenu->addAction (slideShowAct); slideMenu->addAction (slideLoopAct); slideMenu->addAction (slideNoLoopAct); sortMenu = new QMenu(tr("Sort")); sortMenu->addAction (sortByNameAct); sortMenu->addAction (sortByPathAct); sortMenu->addAction (sortByImageDateAct); sortMenu->addAction (sortByFileDateAct); sortMenu->addAction (sortReverseAct); channelMenu = new QMenu(tr("Channels")); // Color mode: true, random, falsegrgbacCrgR channelMenu->addAction (viewChannelFullAct); channelMenu->addAction (viewChannelRedAct); channelMenu->addAction (viewChannelGreenAct); channelMenu->addAction (viewChannelBlueAct); channelMenu->addAction (viewChannelAlphaAct); channelMenu->addAction (viewChannelPrevAct); channelMenu->addAction (viewChannelNextAct); colormodeMenu = new QMenu(tr("Color mode")); colormodeMenu->addAction (viewColorRGBAAct); colormodeMenu->addAction (viewColorRGBAct); colormodeMenu->addAction (viewColor1ChAct); colormodeMenu->addAction (viewColorLumAct); colormodeMenu->addAction (viewColorHeatmapAct); viewMenu = new QMenu(tr("&View"), this); viewMenu->addAction (prevImageAct); viewMenu->addAction (nextImageAct); viewMenu->addAction (toggleImageAct); viewMenu->addSeparator (); viewMenu->addAction (zoomInAct); viewMenu->addAction (zoomOutAct); viewMenu->addAction (normalSizeAct); viewMenu->addAction (fitWindowToImageAct); viewMenu->addAction (fitImageToWindowAct); viewMenu->addAction (fullScreenAct); viewMenu->addSeparator (); viewMenu->addAction (viewSubimagePrevAct); viewMenu->addAction (viewSubimageNextAct); viewMenu->addMenu (channelMenu); viewMenu->addMenu (colormodeMenu); viewMenu->addMenu (expgamMenu); menuBar()->addMenu (viewMenu); // Full screen mode // prev subimage <, next subimage > // fg/bg image... toolsMenu = new QMenu(tr("&Tools"), this); // Mode: select, zoom, pan, wipe toolsMenu->addAction (showInfoWindowAct); toolsMenu->addAction (showPixelviewWindowAct); toolsMenu->addMenu (slideMenu); toolsMenu->addMenu (sortMenu); // Menus, toolbars, & status // Annotate // [check] overwrite render // connect renderer // kill renderer // store render // Playback: forward, reverse, faster, slower, loop/pingpong menuBar()->addMenu (toolsMenu); helpMenu = new QMenu(tr("&Help"), this); helpMenu->addAction (aboutAct); menuBar()->addMenu (helpMenu); // Bring up user's guide } void ImageViewer::createToolBars() { #if 0 fileToolBar = addToolBar(tr("File")); fileToolBar->addAction(newAct); fileToolBar->addAction(openAct); fileToolBar->addAction(saveAct); editToolBar = addToolBar(tr("Edit")); editToolBar->addAction(cutAct); editToolBar->addAction(copyAct); editToolBar->addAction(pasteAct); #endif } void ImageViewer::createStatusBar() { statusImgInfo = new QLabel; statusBar()->addWidget (statusImgInfo); statusViewInfo = new QLabel; statusBar()->addWidget (statusViewInfo); statusProgress = new QProgressBar; statusProgress->setRange (0, 100); statusProgress->reset (); statusBar()->addWidget (statusProgress); mouseModeComboBox = new QComboBox; mouseModeComboBox->addItem (tr("Zoom")); mouseModeComboBox->addItem (tr("Pan")); mouseModeComboBox->addItem (tr("Wipe")); mouseModeComboBox->addItem (tr("Select")); mouseModeComboBox->addItem (tr("Annotate")); // Note: the order of the above MUST match the order of enum MouseMode statusBar()->addWidget (mouseModeComboBox); mouseModeComboBox->hide (); } void ImageViewer::readSettings (bool ui_is_set_up) { QSettings settings("OpenImageIO", "iv"); m_darkPalette = settings.value ("darkPalette").toBool(); if (! ui_is_set_up) return; pixelviewFollowsMouseBox->setChecked (settings.value ("pixelviewFollowsMouse").toBool()); linearInterpolationBox->setChecked (settings.value ("linearInterpolation").toBool()); darkPaletteBox->setChecked (settings.value ("darkPalette").toBool()); QStringList recent = settings.value ("RecentFiles").toStringList(); BOOST_FOREACH (const QString &s, recent) addRecentFile (s.toStdString()); updateRecentFilesMenu (); // only safe because it's called after menu setup autoMipmap->setChecked (settings.value ("autoMipmap", false).toBool()); if (sizeof(void *) == 4) // 32 bit or 64? maxMemoryIC->setValue (settings.value ("maxMemoryIC", 512).toInt()); else maxMemoryIC->setValue (settings.value ("maxMemoryIC", 2048).toInt()); slideShowDuration->setValue (settings.value ("slideShowDuration", 10).toInt()); ImageCache *imagecache = ImageCache::create (true); imagecache->attribute ("automip", autoMipmap->isChecked()); imagecache->attribute ("max_memory_MB", (float) maxMemoryIC->value ()); } void ImageViewer::writeSettings() { QSettings settings("OpenImageIO", "iv"); settings.setValue ("pixelviewFollowsMouse", pixelviewFollowsMouseBox->isChecked()); settings.setValue ("linearInterpolation", linearInterpolationBox->isChecked()); settings.setValue ("darkPalette", darkPaletteBox->isChecked()); settings.setValue ("autoMipmap", autoMipmap->isChecked()); settings.setValue ("maxMemoryIC", maxMemoryIC->value()); settings.setValue ("slideShowDuration", slideShowDuration->value()); QStringList recent; BOOST_FOREACH (const std::string &s, m_recent_files) recent.push_front (QString(s.c_str())); settings.setValue ("RecentFiles", recent); } bool image_progress_callback (void *opaque, float done) { ImageViewer *viewer = (ImageViewer *) opaque; viewer->statusProgress->setValue ((int)(done*100)); QApplication::processEvents(); return false; } void ImageViewer::open() { static QString openPath = QDir::currentPath(); QFileDialog dialog(NULL, tr("Open File(s)"), openPath, tr(s_file_filters)); dialog.setAcceptMode (QFileDialog::AcceptOpen); dialog.setFileMode (QFileDialog::ExistingFiles); if (!dialog.exec()) return; openPath = dialog.directory().path(); QStringList names = dialog.selectedFiles(); int old_lastimage = m_images.size()-1; for (QStringList::Iterator it = names.begin(); it != names.end(); ++it) { std::string filename = it->toUtf8().data(); if (filename.empty()) continue; add_image (filename); // int n = m_images.size()-1; // IvImage *newimage = m_images[n]; // newimage->read_iv (0, false, image_progress_callback, this); } if (old_lastimage >= 0) { // Otherwise, add_image already did this for us. current_image (old_lastimage + 1); fitWindowToImage (true, true); } } void ImageViewer::openRecentFile () { QAction *action = qobject_cast(sender()); if (action) { std::string filename = action->data().toString().toStdString(); // If it's an image we already have loaded, just switch to it // (and reload) rather than loading a second copy. for (size_t i = 0; i < m_images.size(); ++i) { if (m_images[i]->name() == filename) { current_image (i); reload (); return; } } // It's not an image we already have loaded add_image (filename); if (m_images.size() > 1) { // Otherwise, add_image already did this for us. current_image (m_images.size() - 1); fitWindowToImage (true, true); } } } void ImageViewer::addRecentFile (const std::string &name) { removeRecentFile (name); m_recent_files.insert (m_recent_files.begin(), name); if (m_recent_files.size() > MaxRecentFiles) m_recent_files.resize (MaxRecentFiles); } void ImageViewer::removeRecentFile (const std::string &name) { for (size_t i = 0; i < m_recent_files.size(); ++i) { if (m_recent_files[i] == name) { m_recent_files.erase (m_recent_files.begin()+i); --i; } } } void ImageViewer::updateRecentFilesMenu () { for (size_t i = 0; i < MaxRecentFiles; ++i) { if (i < m_recent_files.size()) { std::string name = Filesystem::filename (m_recent_files[i]); openRecentAct[i]->setText (QString::fromStdString (name)); openRecentAct[i]->setData (m_recent_files[i].c_str()); openRecentAct[i]->setVisible (true); } else { openRecentAct[i]->setVisible (false); } } } void ImageViewer::reload() { if (m_images.empty()) return; IvImage *newimage = m_images[m_current_image]; newimage->invalidate (); //glwin->trigger_redraw (); displayCurrentImage (); } void ImageViewer::add_image (const std::string &filename) { if (filename.empty()) return; IvImage *newimage = new IvImage(filename); ASSERT (newimage); newimage->gamma (m_default_gamma); m_images.push_back (newimage); addRecentFile (filename); updateRecentFilesMenu (); #if 0 if (getspec) { if (! newimage->init_spec (filename)) { QMessageBox::information (this, tr("iv Image Viewer"), tr("%1").arg(newimage->geterror().c_str())); } else { std::cerr << "Added image " << filename << ": " << newimage->spec().width << " x " << newimage->spec().height << "\n"; } return; } #endif if (m_images.size() == 1) { // If this is the first image, resize to fit it displayCurrentImage (); fitWindowToImage (true, true); } } void ImageViewer::saveAs() { IvImage *img = cur(); if (! img) return; QString name; name = QFileDialog::getSaveFileName (this, tr("Save Image"), QString(img->name().c_str()), tr(s_file_filters)); if (name.isEmpty()) return; bool ok = img->write (name.toStdString(), "", image_progress_callback, this); if (! ok) { std::cerr << "Save failed: " << img->geterror() << "\n"; } } void ImageViewer::saveWindowAs() { IvImage *img = cur(); if (! img) return; QString name; name = QFileDialog::getSaveFileName (this, tr("Save Window"), QString(img->name().c_str())); if (name.isEmpty()) return; img->write (name.toStdString(), "", image_progress_callback, this); // FIXME } void ImageViewer::saveSelectionAs() { IvImage *img = cur(); if (! img) return; QString name; name = QFileDialog::getSaveFileName (this, tr("Save Selection"), QString(img->name().c_str())); if (name.isEmpty()) return; img->write (name.toStdString(), "", image_progress_callback, this); // FIXME } void ImageViewer::updateTitle () { IvImage *img = cur(); if (! img) { setWindowTitle (tr("iv Image Viewer (no image loaded)")); return; } std::string message; message = Strutil::format ("%s - iv Image Viewer", img->name().c_str()); setWindowTitle (QString::fromLocal8Bit(message.c_str())); } void ImageViewer::updateStatusBar () { const ImageSpec *spec = curspec(); if (! spec) { statusImgInfo->setText (tr("No image loaded")); statusViewInfo->setText (tr("")); return; } std::string message; message = Strutil::format ("(%d/%d) : ", m_current_image+1, (int) m_images.size()); message += cur()->shortinfo(); statusImgInfo->setText (message.c_str()); message.clear(); switch (m_color_mode) { case RGBA: message = Strutil::format ("RGBA (%d-%d)", m_current_channel, m_current_channel+3); break; case RGB: message = Strutil::format ("RGB (%d-%d)", m_current_channel, m_current_channel+2); break; case LUMINANCE: message = Strutil::format ("Lum (%d-%d)", m_current_channel, m_current_channel+2); break; case HEATMAP: message = "Heat "; case SINGLE_CHANNEL: if ((int)spec->channelnames.size() > m_current_channel && spec->channelnames[m_current_channel].size()) message += spec->channelnames[m_current_channel]; else if (m_color_mode == HEATMAP) { message += Strutil::format ("%d", m_current_channel); } else { message = Strutil::format ("chan %d", m_current_channel); } break; } message += Strutil::format (" %g:%g exp %+.1f gam %.2f", zoom() >= 1 ? zoom() : 1.0f, zoom() >= 1 ? 1.0f : 1.0f/zoom(), cur()->exposure(), cur()->gamma()); if (cur()->nsubimages() > 1) { if (cur()->auto_subimage()) { message += Strutil::format (" subimg AUTO (%d/%d)", cur()->subimage()+1, cur()->nsubimages()); } else { message += Strutil::format (" subimg %d/%d", cur()->subimage()+1, cur()->nsubimages()); } } if (cur()->nmiplevels() > 1) { message += Strutil::format (" MIP %d/%d", cur()->miplevel()+1, cur()->nmiplevels()); } statusViewInfo->setText(message.c_str()); // tr("iv status")); } bool ImageViewer::loadCurrentImage (int subimage, int miplevel) { if (m_current_image < 0 || m_current_image >= (int)m_images.size()) { m_current_image = 0; } IvImage *img = cur (); if (img) { // We need the spec available to compare the image format with // opengl's capabilities. if (! img->init_spec (img->name(), subimage, miplevel)) { statusImgInfo->setText (tr ("Could not display image: %1.").arg (img->name().c_str())); statusViewInfo->setText (tr("")); return false; } // Used to check whether we'll need to do adjustments in the // CPU. If true, images should be loaded as UINT8. bool allow_transforms = false; bool srgb_transform = false; // By default, we try to load into OpenGL with the same format, TypeDesc read_format = TypeDesc::UNKNOWN; const ImageSpec &image_spec = img->spec(); if (image_spec.format.basetype == TypeDesc::DOUBLE) { // AFAIK, OpenGL doesn't support 64-bit floats as pixel size. read_format = TypeDesc::FLOAT; } if (glwin->is_glsl_capable ()) { if (image_spec.format.basetype == TypeDesc::HALF && ! glwin->is_half_capable ()) { //std::cerr << "Loading HALF-FLOAT as FLOAT\n"; read_format = TypeDesc::FLOAT; } if (IsSpecSrgb(image_spec) && !glwin->is_srgb_capable ()) { // If the image is in sRGB, but OpenGL can't load sRGB textures then // we'll need to do the transformation on the CPU after loading the // image. We (so far) can only do this with UINT8 images, so make // sure that it gets loaded in this format. //std::cerr << "Loading as UINT8 to do sRGB\n"; read_format = TypeDesc::UINT8; srgb_transform = true; allow_transforms = true; } } else { //std::cerr << "Loading as UINT8\n"; read_format = TypeDesc::UINT8; allow_transforms = true; if (IsSpecSrgb(image_spec) && !glwin->is_srgb_capable ()) srgb_transform = true; } // FIXME: This actually won't work since the ImageCacheFile has already // been created when we did the init_spec. // Check whether IvGL recommends generating mipmaps for this image. //ImageCache *imagecache = ImageCache::create (true); //if (glwin->is_too_big (img) && autoMipmap->isChecked ()) { // imagecache->attribute ("automip", 1); //} else { // imagecache->attribute ("automip", 0); //} // Read the image from disk or from the ImageCache if available. if (img->read_iv (subimage, miplevel, false, read_format, image_progress_callback, this, allow_transforms)) { // The image was read succesfully. // Check if we've got to do sRGB to linear (ie, when not supported // by OpenGL). // Do the first pixel transform to fill-in the secondary image // buffer. if (allow_transforms) { img->pixel_transform (srgb_transform, (int) current_color_mode(), current_channel()); } return true; } else { statusImgInfo->setText (tr ("Could not display image: %1.").arg (img->name().c_str())); statusViewInfo->setText (tr("")); return false; } } return false; } void ImageViewer::displayCurrentImage (bool update) { if (m_current_image < 0 || m_current_image >= (int)m_images.size()) m_current_image = 0; IvImage *img = cur(); if (img) { if (! img->image_valid()) { bool load_result = false; statusViewInfo->hide (); statusProgress->show (); load_result = loadCurrentImage (std::max (0, img->subimage()), std::max (0, img->miplevel())); statusProgress->hide (); statusViewInfo->show (); if (load_result) { update = true; } else { return; } } } else { m_current_image = m_last_image = -1; repaint(); // add repaint event to Qt queue glwin->trigger_redraw(); // then make sure GL canvas is cleared } if (update) { glwin->update (); } float z = zoom(); if (fitImageToWindowAct->isChecked ()) z = zoom_needed_to_fit (glwin->width(), glwin->height()); zoom (z); // glwin->trigger_redraw (); updateTitle(); updateStatusBar(); if (infoWindow) infoWindow->update (img); // printAct->setEnabled(true); // fitImageToWindowAct->setEnabled(true); // fullScreenAct->setEnabled(true); updateActions(); } void ImageViewer::deleteCurrentImage() { IvImage *img = cur(); if (img) { const char *filename = img->name().c_str(); QString message ("Are you sure you want to remove "); message = message + QString(filename) + QString(" file from disk?"); QMessageBox::StandardButton button; button = QMessageBox::question (this, "", message, QMessageBox::Yes | QMessageBox::No); if (button == QMessageBox::Yes) { closeImg(); int r = remove (filename); if (r) QMessageBox::information (this, "", "Unable to delete file"); } } } void ImageViewer::current_image (int newimage) { #ifndef NDEBUG Timer swap_image_time; swap_image_time.start(); #endif if (m_images.empty() || newimage < 0 || newimage >= (int)m_images.size()) m_current_image = 0; if (m_current_image != newimage) { m_last_image = (m_current_image >= 0) ? m_current_image : newimage; m_current_image = newimage; displayCurrentImage (); } else { displayCurrentImage (false); } #ifndef NDEBUG swap_image_time.stop(); std::cerr << "Current Image change elapsed time: " << swap_image_time() << " seconds \n"; #endif } void ImageViewer::prevImage () { if (m_images.empty()) return; if (m_current_image == 0) current_image ((int)m_images.size() - 1); else current_image (current_image() - 1); } void ImageViewer::nextImage () { if (m_images.empty()) return; if (m_current_image >= (int)m_images.size()-1) current_image (0); else current_image (current_image() + 1); } void ImageViewer::toggleImage () { current_image (m_last_image); } void ImageViewer::exposureMinusOneTenthStop () { if (m_images.empty()) return; IvImage *img = m_images[m_current_image]; img->exposure (img->exposure() - 0.1); if (! glwin->is_glsl_capable ()) { bool srgb_transform = (! glwin->is_srgb_capable () && IsSpecSrgb(img->spec())); img->pixel_transform (srgb_transform, (int) current_color_mode(), current_channel()); displayCurrentImage (); } else { displayCurrentImage (false); } } void ImageViewer::exposureMinusOneHalfStop () { if (m_images.empty()) return; IvImage *img = m_images[m_current_image]; img->exposure (img->exposure() - 0.5); if (! glwin->is_glsl_capable ()) { bool srgb_transform = (! glwin->is_srgb_capable () && IsSpecSrgb(img->spec())); img->pixel_transform (srgb_transform, (int) current_color_mode(), current_channel()); displayCurrentImage (); } else { displayCurrentImage (false); } } void ImageViewer::exposurePlusOneTenthStop () { if (m_images.empty()) return; IvImage *img = m_images[m_current_image]; img->exposure (img->exposure() + 0.1); if (! glwin->is_glsl_capable ()) { bool srgb_transform = (! glwin->is_srgb_capable () && IsSpecSrgb(img->spec())); img->pixel_transform (srgb_transform, (int) current_color_mode(), current_channel()); displayCurrentImage (); } else { displayCurrentImage (false); } } void ImageViewer::exposurePlusOneHalfStop () { if (m_images.empty()) return; IvImage *img = m_images[m_current_image]; img->exposure (img->exposure() + 0.5); if (! glwin->is_glsl_capable ()) { bool srgb_transform = (! glwin->is_srgb_capable () && IsSpecSrgb(img->spec())); img->pixel_transform (srgb_transform, (int) current_color_mode(), current_channel()); displayCurrentImage (); } else { displayCurrentImage (false); } } void ImageViewer::gammaMinus () { if (m_images.empty()) return; IvImage *img = m_images[m_current_image]; img->gamma (img->gamma() - 0.05); if (! glwin->is_glsl_capable ()) { bool srgb_transform = (! glwin->is_srgb_capable () && IsSpecSrgb(img->spec())); img->pixel_transform (srgb_transform, (int) current_color_mode(), current_channel()); displayCurrentImage (); } else { displayCurrentImage (false); } } void ImageViewer::gammaPlus () { if (m_images.empty()) return; IvImage *img = m_images[m_current_image]; img->gamma (img->gamma() + 0.05); if (! glwin->is_glsl_capable ()) { bool srgb_transform = (! glwin->is_srgb_capable () && IsSpecSrgb(img->spec())); img->pixel_transform (srgb_transform, (int) current_color_mode(), current_channel()); displayCurrentImage (); } else { displayCurrentImage (false); } } void ImageViewer::slide (long t, bool b) { slideLoopAct->setChecked (b == true); slideNoLoopAct->setChecked (b == false); } void ImageViewer::viewChannel (int c, COLOR_MODE colormode) { #ifndef NDEBUG Timer change_channel_time; change_channel_time.start(); #endif if (m_current_channel != c || colormode != m_color_mode) { bool update = true; if (! glwin->is_glsl_capable ()) { IvImage *img = cur (); if (img) { bool srgb_transform = (! glwin->is_srgb_capable () && IsSpecSrgb(img->spec())); img->pixel_transform (srgb_transform, (int)colormode, c); } } else { // FIXME: There are even more chances to avoid updating the textures // if we can keep trac of which channels are in the texture. if (m_current_channel == c) { if (m_color_mode == SINGLE_CHANNEL || m_color_mode == HEATMAP) { if (colormode == HEATMAP || colormode == SINGLE_CHANNEL) update = false; } else if (m_color_mode == RGB || m_color_mode == LUMINANCE) { if (colormode == RGB || colormode == LUMINANCE) update = false; } } } m_current_channel = c; m_color_mode = colormode; displayCurrentImage (update); viewChannelFullAct->setChecked (c == 0 && m_color_mode == RGBA); viewChannelRedAct->setChecked (c == 0 && m_color_mode == SINGLE_CHANNEL); viewChannelGreenAct->setChecked (c == 1 && m_color_mode == SINGLE_CHANNEL); viewChannelBlueAct->setChecked (c == 2 && m_color_mode == SINGLE_CHANNEL); viewChannelAlphaAct->setChecked (c == 3 && m_color_mode == SINGLE_CHANNEL); viewColorLumAct->setChecked (m_color_mode == LUMINANCE); viewColorRGBAAct->setChecked (m_color_mode == RGBA); viewColorRGBAct->setChecked (m_color_mode == RGB); viewColor1ChAct->setChecked (m_color_mode == SINGLE_CHANNEL); viewColorHeatmapAct->setChecked (m_color_mode == HEATMAP); } #ifndef NDEBUG change_channel_time.stop(); std::cerr << "Change channel elapsed time: " << change_channel_time() << " seconds \n"; #endif } void ImageViewer::slideImages() { if (m_images.empty()) return; if (m_current_image >= (int)m_images.size()-1) { if (slide_loop == true) current_image (0); else { slideTimer->stop(); disconnect(slideTimer,0,0,0); } } else current_image (current_image() + 1); } void ImageViewer::slideShow () { fullScreenToggle(); connect(slideTimer,SIGNAL(timeout()),this,SLOT(slideImages())); slideTimer->start(slideDuration_ms); updateActions(); } void ImageViewer::slideLoop () { slide_loop = true; slide(slideDuration_ms, slide_loop); } void ImageViewer::slideNoLoop () { slide_loop = false; slide(slideDuration_ms, slide_loop); } void ImageViewer::setSlideShowDuration (int seconds) { slideDuration_ms = seconds * 1000; } static bool compName (IvImage *first, IvImage *second) { std::string firstFile = Filesystem::filename (first->name()); std::string secondFile = Filesystem::filename (second->name()); return (firstFile.compare(secondFile) < 0); } void ImageViewer::sortByName () { int numImg = m_images.size(); if (numImg < 2) return; std::sort (m_images.begin(), m_images.end(), &compName); current_image (0); displayCurrentImage(); // updateActions(); } static bool compPath (IvImage *first, IvImage *second) { std::string firstFile = first->name (); std::string secondFile = second->name (); return (firstFile.compare(secondFile) < 0); } void ImageViewer::sortByPath () { int numImg = m_images.size(); if (numImg < 2) return; std::sort (m_images.begin(), m_images.end(), &compPath); current_image(0); displayCurrentImage(); // updateActions(); } static bool DateTime_to_time_t (const char *datetime, time_t &timet) { int year, month, day, hour, min, sec; int r = sscanf (datetime, "%d:%d:%d %d:%d:%d", &year, &month, &day, &hour, &min, &sec); // printf ("%d %d:%d:%d %d:%d:%d\n", r, year, month, day, hour, min, sec); if (r != 6) return false; struct tm tmtime; time_t now; Sysutil::get_local_time (&now, &tmtime); // fill in defaults tmtime.tm_sec = sec; tmtime.tm_min = min; tmtime.tm_hour = hour; tmtime.tm_mday = day; tmtime.tm_mon = month-1; tmtime.tm_year = year-1900; timet = mktime (&tmtime); return true; } static bool compImageDate (IvImage *first, IvImage *second) { std::time_t firstFile = time(NULL); std::time_t secondFile = time(NULL); double diff; std::string metadatatime = first->spec ().get_string_attribute ("DateTime"); if (metadatatime.empty()) { if (first->init_spec (first->name(), 0, 0)) { metadatatime = first->spec ().get_string_attribute ("DateTime"); if (metadatatime.empty()){ if (! Filesystem::exists (first->name ())) return false; firstFile = Filesystem::last_write_time (first->name ()); } } else return false; } DateTime_to_time_t (metadatatime.c_str(), firstFile); metadatatime = second->spec().get_string_attribute ("DateTime"); if (metadatatime.empty()) { if (second->init_spec(second->name(), 0, 0)) { metadatatime = second->spec ().get_string_attribute ("DateTime"); if (metadatatime.empty()){ if (! Filesystem::exists (second->name ())) return true; secondFile = Filesystem::last_write_time (second->name()); } } else return true; } DateTime_to_time_t (metadatatime.c_str(), secondFile); diff = difftime(firstFile, secondFile); if (diff == 0) return compName(first,second); return (diff < 0); } void ImageViewer::sortByImageDate () { int numImg = m_images.size(); if (numImg < 2) return; std::sort( m_images.begin(), m_images.end(), &compImageDate); current_image(0); displayCurrentImage(); // updateActions(); } static bool compFileDate (IvImage *first, IvImage *second) { std::time_t firstFile, secondFile; double diff; if (! Filesystem::exists (first->name ())) return false; firstFile = Filesystem::last_write_time (first->name()); if (! Filesystem::exists (second->name ())) return true; secondFile = Filesystem::last_write_time (second->name()); diff = difftime(firstFile, secondFile); if (diff == 0) return compName(first, second); return (diff < 0); } void ImageViewer::sortByFileDate () { int numImg = m_images.size(); if (numImg<2) return; std::sort( m_images.begin(), m_images.end(), &compFileDate); current_image(0); displayCurrentImage(); // updateActions(); } void ImageViewer::sortReverse () { int numImg = m_images.size(); if (numImg < 2) return; std::reverse( m_images.begin(), m_images.end()); current_image(0); displayCurrentImage(); // updateActions(); } void ImageViewer::viewChannelFull () { viewChannel (0, RGBA); } void ImageViewer::viewChannelRed () { viewChannel (0, SINGLE_CHANNEL); } void ImageViewer::viewChannelGreen () { viewChannel (1, SINGLE_CHANNEL); } void ImageViewer::viewChannelBlue () { viewChannel (2, SINGLE_CHANNEL); } void ImageViewer::viewChannelAlpha () { viewChannel (3, SINGLE_CHANNEL); } void ImageViewer::viewChannelLuminance () { viewChannel (m_current_channel, LUMINANCE); } void ImageViewer::viewColorRGBA () { viewChannel (m_current_channel, RGBA); } void ImageViewer::viewColorRGB () { viewChannel (m_current_channel, RGB); } void ImageViewer::viewColor1Ch () { viewChannel (m_current_channel, SINGLE_CHANNEL); } void ImageViewer::viewColorHeatmap () { viewChannel (m_current_channel, HEATMAP); } void ImageViewer::viewChannelPrev () { if (glwin->is_glsl_capable()) { if (m_current_channel > 0) viewChannel (m_current_channel-1, m_color_mode); } else { // Simulate old behavior. if (m_color_mode == RGBA || m_color_mode == RGB) { viewChannel (m_current_channel, LUMINANCE); } else if (m_color_mode == SINGLE_CHANNEL) { if (m_current_channel == 0) viewChannelFull(); else viewChannel (m_current_channel-1, SINGLE_CHANNEL); } } } void ImageViewer::viewChannelNext () { if (glwin->is_glsl_capable()) { viewChannel (m_current_channel+1, m_color_mode); } else { // Simulate old behavior. if (m_color_mode == LUMINANCE) { viewChannelFull(); } else if (m_color_mode == RGBA || m_color_mode == RGB) { viewChannelRed(); } else if (m_color_mode == SINGLE_CHANNEL) { viewChannel (m_current_channel+1, SINGLE_CHANNEL); } } } void ImageViewer::viewSubimagePrev () { IvImage *img = cur(); if (! img) return; bool ok = false; if (img->miplevel() > 0) { ok = loadCurrentImage (img->subimage(), img->miplevel()-1); } else if (img->subimage() > 0) { ok = loadCurrentImage (img->subimage()-1); } else if (img->nsubimages() > 0) { img->auto_subimage (true); ok = loadCurrentImage (0); } if (ok) { if (fitImageToWindowAct->isChecked ()) fitImageToWindow (); displayCurrentImage (); } } void ImageViewer::viewSubimageNext () { IvImage *img = cur(); if (! img) return; bool ok = false; if (img->auto_subimage()) { img->auto_subimage(false); ok = loadCurrentImage (0); } else if (img->miplevel() < img->nmiplevels()-1) { ok = loadCurrentImage (img->subimage(), img->miplevel()+1); } else if (img->subimage() < img->nsubimages()-1) { ok = loadCurrentImage (img->subimage()+1); } if (ok) { if (fitImageToWindowAct->isChecked ()) fitImageToWindow (); displayCurrentImage (); } } void ImageViewer::keyPressEvent (QKeyEvent *event) { switch (event->key()) { case Qt::Key_Left : case Qt::Key_Up : case Qt::Key_PageUp : prevImage(); return; //break; case Qt::Key_Right : // std::cerr << "Modifier is " << (int)event->modifiers() << '\n'; // fprintf (stderr, "%x\n", (int)event->modifiers()); // if (event->modifiers() & Qt::ShiftModifier) // std::cerr << "hey, ctrl right\n"; case Qt::Key_Down : case Qt::Key_PageDown : nextImage(); return; //break; case Qt::Key_Escape : if (m_fullscreen) fullScreenToggle(); return; case Qt::Key_Minus : case Qt::Key_Underscore : zoomOut(); break; case Qt::Key_Plus : case Qt::Key_Equal : zoomIn(); break; default: // std::cerr << "ImageViewer key " << (int)event->key() << '\n'; QMainWindow::keyPressEvent (event); } } void ImageViewer::resizeEvent (QResizeEvent *event) { if (fitImageToWindowAct->isChecked ()) fitImageToWindow (); QMainWindow::resizeEvent (event); } void ImageViewer::closeImg() { if (m_images.empty()) return; delete m_images[m_current_image]; m_images[m_current_image] = NULL; m_images.erase (m_images.begin()+m_current_image); // Update image indices // This should be done for all image indices we may be storing if (m_last_image == m_current_image) { if (!m_images.empty() && m_last_image > 0) m_last_image = 0; else m_last_image = -1; } if (m_last_image > m_current_image) m_last_image --; m_current_image = m_current_image < (int)m_images.size() ? m_current_image : 0; displayCurrentImage (); } void ImageViewer::print() { #if 0 Q_ASSERT(imageLabel->pixmap()); QPrintDialog dialog(&printer, this); if (dialog.exec()) { QPainter painter(&printer); QRect rect = painter.viewport(); QSize size = imageLabel->pixmap()->size(); size.scale(rect.size(), Qt::KeepAspectRatio); painter.setViewport(rect.x(), rect.y(), size.width(), size.height()); painter.setWindow(imageLabel->pixmap()->rect()); painter.drawPixmap(0, 0, *imageLabel->pixmap()); } #endif } void ImageViewer::zoomIn() { IvImage *img = cur(); if (! img) return; if (zoom() >= 64) return; float oldzoom = zoom (); float newzoom = pow2roundupf (oldzoom); float xc, yc; // Center view position glwin->get_center (xc, yc); int xm, ym; // Mouse position glwin->get_focus_image_pixel (xm, ym); float xoffset = xc - xm; float yoffset = yc - ym; float maxzoomratio = std::max (oldzoom/newzoom, newzoom/oldzoom); int nsteps = (int) Imath::clamp (20 * (maxzoomratio - 1), 2.0f, 10.0f); for (int i = 1; i <= nsteps; ++i) { float a = (float)i/(float)nsteps; // Interpolation amount float z = Imath::lerp (oldzoom, newzoom, a); float zoomratio = z / oldzoom; view (xm + xoffset/zoomratio, ym + yoffset/zoomratio, z, false); if (i != nsteps) Sysutil::usleep (1000000 / 4 / nsteps); } fitImageToWindowAct->setChecked (false); } void ImageViewer::zoomOut() { IvImage *img = cur(); if (! img) return; if (zoom() <= 1.0f/64) return; float oldzoom = zoom (); float newzoom = pow2rounddownf (oldzoom); float xcpel, ycpel; // Center view position glwin->get_center (xcpel, ycpel); int xmpel, ympel; // Mouse position glwin->get_focus_image_pixel (xmpel, ympel); float xoffset = xcpel - xmpel; float yoffset = ycpel - ympel; float maxzoomratio = std::max (oldzoom/newzoom, newzoom/oldzoom); int nsteps = (int) Imath::clamp (20 * (maxzoomratio - 1), 2.0f, 10.0f); for (int i = 1; i <= nsteps; ++i) { float a = (float)i/(float)nsteps; // Interpolation amount float z = Imath::lerp (oldzoom, newzoom, a); float zoomratio = z / oldzoom; view (xmpel + xoffset/zoomratio, ympel + yoffset/zoomratio, z, false); if (i != nsteps) Sysutil::usleep (1000000 / 4 / nsteps); } fitImageToWindowAct->setChecked (false); } void ImageViewer::normalSize() { IvImage *img = cur(); if (! img) return; fitImageToWindowAct->setChecked (false); float xcenter = img->oriented_full_x() + 0.5 * img->oriented_full_width(); float ycenter = img->oriented_full_y() + 0.5 * img->oriented_full_height(); view (xcenter, ycenter, 1.0, true); fitWindowToImage (false); } float ImageViewer::zoom_needed_to_fit (int w, int h) { IvImage *img = cur(); if (! img) return 1; float zw = (float) w / img->oriented_width (); float zh = (float) h / img->oriented_height (); return std::min (zw, zh); } void ImageViewer::fitImageToWindow() { IvImage *img = cur(); if (! img) return; fitImageToWindowAct->setChecked (true); zoom (zoom_needed_to_fit (glwin->width(), glwin->height())); } void ImageViewer::fitWindowToImage (bool zoomok, bool minsize) { IvImage *img = cur(); // Don't resize when there's no image or the image hasn't been opened yet // (or we failed to open it). if (! img || ! img->image_valid ()) return; // FIXME -- figure out a way to make it exactly right, even for the // main window border, etc. #ifdef __APPLE__ int extraw = 0; //12; // width() - minimumWidth(); int extrah = statusBar()->height() + 0; //40; // height() - minimumHeight(); #else int extraw = 4; //12; // width() - minimumWidth(); int extrah = statusBar()->height() + 4; //40; // height() - minimumHeight(); #endif // std::cerr << "extra wh = " << extraw << ' ' << extrah << '\n'; float z = zoom(); int w = (int)(img->oriented_full_width() * z)+extraw; int h = (int)(img->oriented_full_height() * z)+extrah; if (minsize) { if (w < m_default_width) { w = m_default_width; } if (h < m_default_height) { h = m_default_height; } } if (! m_fullscreen) { QDesktopWidget *desktop = QApplication::desktop (); QRect availgeom = desktop->availableGeometry (this); int availwidth = availgeom.width() - extraw - 20; int availheight = availgeom.height() - extrah - menuBar()->height() - 20; #if 0 QRect screengeom = desktop->screenGeometry (this); std::cerr << "available desktop geom " << availgeom.x() << ' ' << availgeom.y() << ' ' << availgeom.width() << "x" << availgeom.height() << "\n"; std::cerr << "screen desktop geom " << screengeom.x() << ' ' << screengeom.y() << ' ' << screengeom.width() << "x" << screengeom.height() << "\n"; #endif if (w > availwidth || h > availheight) { w = std::min (w, availwidth); h = std::min (h, availheight); if (zoomok) { z = zoom_needed_to_fit (w, h); w = (int)(img->oriented_full_width() * z) + extraw; h = (int)(img->oriented_full_height() * z) + extrah; // std::cerr << "must rezoom to " << z << " to fit\n"; } // std::cerr << "New window geom " << w << "x" << h << "\n"; int posx = x(), posy = y(); if (posx + w > availwidth || posy + h > availheight) { if (posx + w > availwidth) posx = std::max (0, availwidth - w) + availgeom.x(); if (posy + h > availheight) posy = std::max (0, availheight - h) + availgeom.y(); // std::cerr << "New position " << posx << ' ' << posy << "\n"; move (QPoint (posx, posy)); } } } float midx = img->oriented_full_x() + 0.5 * img->oriented_full_width(); float midy = img->oriented_full_y() + 0.5 * img->oriented_full_height(); view (midx, midy, z, false, false); resize (w, h); // Resize will trigger a repaint. #if 0 QRect g = geometry(); std::cerr << "geom " << g.x() << ' ' << g.y() << ' ' << g.width() << "x" << g.height() << "\n"; g = frameGeometry(); std::cerr << "frame geom " << g.x() << ' ' << g.y() << ' ' << g.width() << "x" << g.height() << "\n"; g = glwin->geometry(); std::cerr << "ogl geom " << g.x() << ' ' << g.y() << ' ' << g.width() << "x" << g.height() << "\n"; std::cerr << "Status bar height = " << statusBar()->height() << "\n"; #endif #if 0 bool fit = fitWindowToImageAct->isChecked(); if (!fit) { normalSize(); } #endif updateActions(); } void ImageViewer::fullScreenToggle() { if (m_fullscreen) { menuBar()->show (); statusBar()->show (); showNormal (); m_fullscreen = false; slideTimer->stop(); disconnect(slideTimer,0,0,0); } else { menuBar()->hide (); statusBar()->hide (); showFullScreen (); m_fullscreen = true; fitImageToWindow (); } } void ImageViewer::about() { QMessageBox::about(this, tr("About iv"), tr("

iv is the image viewer for OpenImageIO.

" "

(c) Copyright 2008 Larry Gritz et al. All Rights Reserved.

" "

See http://openimageio.org for details.

")); } void ImageViewer::updateActions() { // zoomInAct->setEnabled(!fitImageToWindowAct->isChecked()); // zoomOutAct->setEnabled(!fitImageToWindowAct->isChecked()); // normalSizeAct->setEnabled(!fitImageToWindowAct->isChecked()); } static inline void calc_subimage_from_zoom (const IvImage *img, int &subimage, float &zoom, float &xcenter, float &ycenter) { int rel_subimage = Imath::trunc (log2f (1/zoom)); subimage = clamp (img->subimage() + rel_subimage, 0, img->nsubimages()-1); if (! (img->subimage() == 0 && zoom > 1) && ! (img->subimage() == img->nsubimages()-1 && zoom < 1)) { float pow_zoom = powf (2.0f, (float) rel_subimage); zoom *= pow_zoom; xcenter /= pow_zoom; ycenter /= pow_zoom; } } void ImageViewer::view (float xcenter, float ycenter, float newzoom, bool smooth, bool redraw) { IvImage *img = cur(); if (! img) return; float oldzoom = m_zoom; float oldxcenter, oldycenter; glwin->get_center (oldxcenter, oldycenter); float zoomratio = std::max (oldzoom/newzoom, newzoom/oldzoom); int nsteps = (int) Imath::clamp (20 * (zoomratio - 1), 2.0f, 10.0f); if (! smooth || ! redraw) nsteps = 1; for (int i = 1; i <= nsteps; ++i) { float a = (float)i/(float)nsteps; // Interpolation amount float xc = Imath::lerp (oldxcenter, xcenter, a); float yc = Imath::lerp (oldycenter, ycenter, a); m_zoom = Imath::lerp (oldzoom, newzoom, a); glwin->view (xc, yc, m_zoom, redraw); // Triggers redraw automatically if (i != nsteps) Sysutil::usleep (1000000 / 4 / nsteps); } if (img->auto_subimage ()) { int subimage = 0; calc_subimage_from_zoom (img, subimage, m_zoom, xcenter, ycenter); if (subimage != img->subimage ()) { //std::cerr << "Changing to subimage " << subimage; //std::cerr << " With zoom: " << m_zoom << '\n'; loadCurrentImage (subimage); glwin->update (); glwin->view (xcenter, ycenter, m_zoom, redraw); } } // zoomInAct->setEnabled (zoom() < 64.0); // zoomOutAct->setEnabled (zoom() > 1.0/64); updateStatusBar (); } void ImageViewer::zoom (float newzoom, bool smooth) { float xcenter, ycenter; glwin->get_center (xcenter, ycenter); view (xcenter, ycenter, newzoom, smooth); } void ImageViewer::showInfoWindow () { if (! infoWindow) { infoWindow = new IvInfoWindow (*this, true); infoWindow->setPalette (m_palette); } infoWindow->update (cur()); if (infoWindow->isHidden ()) infoWindow->show (); else infoWindow->hide (); } void ImageViewer::showPixelviewWindow () { glwin->trigger_redraw (); } void ImageViewer::editPreferences () { if (! preferenceWindow) { preferenceWindow = new IvPreferenceWindow (*this); preferenceWindow->setPalette (m_palette); } preferenceWindow->show (); } openimageio-1.7.17~dfsg0.orig/src/iv/ivpref.cpp0000644000175000017500000000646013151711064017537 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include "imageviewer.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/strutil.h" IvPreferenceWindow::IvPreferenceWindow (ImageViewer &viewer) : QDialog(&viewer), m_viewer(viewer) { closeButton = new QPushButton (tr("Close")); closeButton->setShortcut (tr("Ctrl+W")); connect (closeButton, SIGNAL(clicked()), this, SLOT(hide())); layout = new QVBoxLayout; layout->addWidget (viewer.pixelviewFollowsMouseBox); layout->addWidget (viewer.linearInterpolationBox); layout->addWidget (viewer.darkPaletteBox); layout->addWidget (viewer.autoMipmap); QLayout *inner_layout = new QHBoxLayout; inner_layout->addWidget (viewer.maxMemoryICLabel); inner_layout->addWidget (viewer.maxMemoryIC); QLayout *slideShowLayout = new QHBoxLayout; slideShowLayout->addWidget (viewer.slideShowDurationLabel); slideShowLayout->addWidget (viewer.slideShowDuration); layout->addLayout (inner_layout); layout->addLayout (slideShowLayout); layout->addWidget (closeButton); setLayout (layout); setWindowTitle (tr("iv Preferences")); } void IvPreferenceWindow::keyPressEvent (QKeyEvent *event) { // if (event->key() == 'w' && (event->modifiers() & Qt::ControlModifier)) { if (event->key() == Qt::Key_W) { std::cerr << "found w\n"; if ((event->modifiers() & Qt::ControlModifier)) { std::cerr << "think we did ctrl-w\n"; event->accept(); hide(); } else std::cerr << "modifier " << (int)event->modifiers() << "\n"; } else { event->ignore(); } } openimageio-1.7.17~dfsg0.orig/src/iv/ivgl.h0000644000175000017500000002147613151711064016656 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_IVGL_H #define OPENIMAGEIO_IVGL_H #if defined (_MSC_VER) // Ignore warnings about conditional expressions that always evaluate true // on a given platform but may evaluate differently on another. There's // nothing wrong with such conditionals. // Also ignore warnings about not being able to generate default assignment // operators for some Qt classes included in headers below. # pragma warning (disable : 4127 4512) #endif // included to remove std::min/std::max errors #include "OpenImageIO/platform.h" #include // This needs to be included before GL.h #include #include #include "OpenImageIO/imageio.h" #include "OpenImageIO/imagebuf.h" OIIO_NAMESPACE_USING; class IvImage; class ImageViewer; class IvGL : public QGLWidget { Q_OBJECT public: IvGL (QWidget *parent, ImageViewer &viewer); virtual ~IvGL (); /// Update the image texture. /// virtual void update (); /// Update the view -- center (in pixel coordinates) and zoom level. /// virtual void view (float centerx, float centery, float zoom, bool redraw=true); /// Update just the zoom, keep the old center /// void zoom (float newzoom, bool redraw=true); /// Update just the center (in pixel coordinates), keep the old zoom. /// void center (float x, float y, bool redraw=true); /// Get the center of the view, in pixel coordinates. /// void get_center (float &x, float &y) { x = m_centerx; y = m_centery; } void pan (float dx, float dy); /// Let the widget know which pixel the mouse is over /// void remember_mouse (const QPoint &pos); /// Which image pixel is the mouse over? /// void get_focus_image_pixel (int &x, int &y); /// Which display window pixel is the mouse over? (Relative to /// widget boundaries) void get_focus_window_pixel (int &x, int &y); void trigger_redraw (void) { glDraw(); } /// Returns true if OpenGL is capable of loading textures in the sRGB color /// space. bool is_srgb_capable (void) const { return m_use_srgb; } /// Returns true if OpenGL can use GLSL, either with extensions or as /// implementation of version 2.0 bool is_glsl_capable (void) const { return m_use_shaders; } /// Is OpenGL capable of reading half-float textures? /// bool is_half_capable (void) const { return m_use_halffloat; } /// Returns true if the image is too big to fit within allocated textures /// (i.e., it's recommended to use lower resolution versions when zoomed out). bool is_too_big (float width, float height); void typespec_to_opengl (const ImageSpec& spec, int nchannels, GLenum &gltype, GLenum &glformat, GLenum &glinternal) const; protected: ImageViewer &m_viewer; ///< Backpointer to viewer bool m_shaders_created; ///< Have the shaders been created? GLuint m_vertex_shader; ///< Vertex shader id GLuint m_fragment_shader; ///< Fragment shader id GLuint m_shader_program; ///< GL shader program id bool m_tex_created; ///< Have the textures been created? float m_zoom; ///< Zoom ratio float m_centerx, m_centery; ///< Center of view, in pixel coords bool m_dragging; ///< Are we dragging? int m_mousex, m_mousey; ///< Last mouse position Qt::MouseButton m_drag_button; ///< Button on when dragging bool m_use_shaders; ///< Are shaders supported? bool m_shaders_using_extensions; ///< Are we using ARB_*_shader? bool m_use_halffloat; ///< Are half-float textures supported? bool m_use_float; ///< Are float textures supported? bool m_use_srgb; ///< Are sRGB-space textures supported? bool m_use_npot_texture; ///< Can we handle NPOT textures? bool m_use_pbo; ///< Can we use PBO to upload the texture? GLint m_max_texture_size; ///< Maximum allowed texture dimension. GLsizei m_texture_width; GLsizei m_texture_height; GLuint m_pbo_objects[2]; ///< Pixel buffer objects int m_last_pbo_used; ///< Last used pixel buffer object. IvImage *m_current_image; ///< Image to show on screen. GLuint m_pixelview_tex; ///< Pixelview's own texture. bool m_pixelview_left_corner; ///< Draw pixelview in upper left or right /// Buffer passed to IvImage::copy_image when not using PBO. /// std::vector m_tex_buffer; /// Represents a texture object being used as a buffer. /// struct TexBuffer { GLuint tex_object; int x; int y; int width; int height; }; std::vector m_texbufs; int m_last_texbuf_used; bool m_mouse_activation; ///< Can we expect the window to be activated by mouse? virtual void initializeGL (); virtual void resizeGL (int w, int h); virtual void paintGL (); virtual void mousePressEvent (QMouseEvent *event); virtual void mouseReleaseEvent (QMouseEvent *event); virtual void mouseMoveEvent (QMouseEvent *event); virtual void wheelEvent (QWheelEvent *event); virtual void focusOutEvent (QFocusEvent *event); void paint_pixelview (); void glSquare (float xmin, float ymin, float xmax, float ymax, float z=0); virtual void create_shaders (void); virtual void create_textures (void); virtual void useshader (int tex_width, int tex_height, bool pixelview=false); void shadowed_text (float x, float y, float z, const std::string &s, const QFont &font); private: typedef QGLWidget parent_t; /// ncloseuppixels is the number of big pixels (in each direction) /// visible in our closeup window. const static int ncloseuppixels = 9; /// closeuppixelzoom is the zoom factor we use for closeup pixels -- /// i.e. one image pixel will appear in the closeup window as a /// closeuppixelzoom x closeuppixelzoom square. const static int closeuppixelzoom = 24; /// closeupsize is the size, in pixels, of the closeup window itself -- /// just the number of pixels times the width of each closeup pixel. const static int closeupsize = ncloseuppixels * closeuppixelzoom; /// closeuptexsize is the size of the texture used to upload the pixelview /// to OpenGL. const static int closeuptexsize = 16; void clamp_view_to_window (); // Small wrappers to handle ARB shaders. void gl_use_program (int program); GLint gl_get_uniform_location (const char*); void gl_uniform (GLint location, float value); void gl_uniform (GLint location, int value); /// checks what OpenGL extensions we have /// void check_gl_extensions (void); /// print shader info to out stream /// void print_shader_log (std::ostream& out, const GLuint shader_id) const; /// Loads the given patch of the image, but first figures if it's already /// been loaded. void load_texture (int x, int y, int width, int height, float percent); /// Destroys shaders and selects fixed-function pipeline void create_shaders_abort (void); }; #endif // OPENIMAGEIO_IVGL_H openimageio-1.7.17~dfsg0.orig/src/iv/ivimage.cpp0000644000175000017500000003416013151711064017663 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include "imageviewer.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/fmath.h" IvImage::IvImage (const std::string &filename) : ImageBuf(filename), m_thumbnail(NULL), m_thumbnail_valid(false), m_gamma(1), m_exposure(0), m_file_dataformat(TypeDesc::UNKNOWN), m_image_valid(false), m_auto_subimage(false) { } IvImage::~IvImage () { delete [] m_thumbnail; } bool IvImage::init_spec_iv (const std::string &filename, int subimage, int miplevel) { // invalidate info strings m_shortinfo.clear (); m_longinfo.clear (); // If we're changing mip levels or subimages, the pixels will no // longer be valid. if (subimage != this->subimage() || miplevel != this->miplevel()) m_image_valid = false; bool ok = ImageBuf::init_spec (filename, subimage, miplevel); if (ok && m_file_dataformat.basetype == TypeDesc::UNKNOWN) { m_file_dataformat = spec().format; } return ok; } bool IvImage::read_iv (int subimage, int miplevel, bool force, TypeDesc format, ProgressCallback progress_callback, void *progress_callback_data, bool secondary_data) { // Don't read if we already have it in memory, unless force is true. // FIXME: should we also check the time on the file to see if it's // been updated since we last loaded? if (m_image_valid && !force && subimage == this->subimage() && miplevel != this->miplevel()) return true; m_image_valid = init_spec_iv (name(), subimage, miplevel); if (m_image_valid) m_image_valid = ImageBuf::read (subimage, miplevel, force, format, progress_callback, progress_callback_data); if (m_image_valid && secondary_data && spec().format == TypeDesc::UINT8) { m_corrected_image.reset ("", ImageSpec (spec().width, spec().height, std::min(spec().nchannels, 4), spec().format)); } else { m_corrected_image.clear (); } return m_image_valid; } std::string IvImage::shortinfo () const { if (m_shortinfo.empty()) { m_shortinfo = Strutil::format ("%d x %d", spec().width, spec().height); if (spec().depth > 1) m_shortinfo += Strutil::format (" x %d", spec().depth); m_shortinfo += Strutil::format (" x %d channel %s (%.2f MB)", spec().nchannels, m_file_dataformat.c_str(), (float)spec().image_bytes() / (1024.0*1024.0)); } return m_shortinfo; } // Format name/value pairs as HTML table entries. std::string html_table_row (const char *name, const std::string &value) { std::string line = Strutil::format ("%s :   ", name); line += Strutil::format ("%s\n", value.c_str()); return line; } std::string html_table_row (const char *name, int value) { return html_table_row (name, Strutil::format ("%d", value)); } std::string html_table_row (const char *name, float value) { return html_table_row (name, Strutil::format ("%g", value)); } std::string IvImage::longinfo () const { using Strutil::format; // shorthand if (m_longinfo.empty()) { const ImageSpec &m_spec (nativespec()); m_longinfo += ""; // m_longinfo += html_table_row (format("%s", m_name.c_str()).c_str(), // std::string()); if (m_spec.depth <= 1) m_longinfo += html_table_row ("Dimensions", format ("%d x %d pixels", m_spec.width, m_spec.height)); else m_longinfo += html_table_row ("Dimensions", format ("%d x %d x %d pixels", m_spec.width, m_spec.height, m_spec.depth)); m_longinfo += html_table_row ("Channels", m_spec.nchannels); std::string chanlist; for (int i = 0; i < m_spec.nchannels; ++i) { chanlist += m_spec.channelnames[i].c_str(); if (i != m_spec.nchannels-1) chanlist += ", "; } m_longinfo += html_table_row ("Channel list", chanlist); m_longinfo += html_table_row ("File format", file_format_name()); m_longinfo += html_table_row ("Data format", m_file_dataformat.c_str()); m_longinfo += html_table_row ("Data size", format("%.2f MB", (float)m_spec.image_bytes() / (1024.0*1024.0))); m_longinfo += html_table_row ("Image origin", format ("%d, %d, %d", m_spec.x, m_spec.y, m_spec.z)); m_longinfo += html_table_row ("Full/display size", format ("%d x %d x %d", m_spec.full_width, m_spec.full_height, m_spec.full_depth)); m_longinfo += html_table_row ("Full/display origin", format ("%d, %d, %d", m_spec.full_x, m_spec.full_y, m_spec.full_z)); if (m_spec.tile_width) m_longinfo += html_table_row ("Scanline/tile", format ("tiled %d x %d x %d", m_spec.tile_width, m_spec.tile_height, m_spec.tile_depth)); else m_longinfo += html_table_row ("Scanline/tile", "scanline"); if (m_spec.alpha_channel >= 0) m_longinfo += html_table_row ("Alpha channel", m_spec.alpha_channel); if (m_spec.z_channel >= 0) m_longinfo += html_table_row ("Depth (z) channel", m_spec.z_channel); BOOST_FOREACH (const ImageIOParameter &p, m_spec.extra_attribs) { std::string s = m_spec.metadata_val (p, true); m_longinfo += html_table_row (p.name().c_str(), s); } m_longinfo += "
"; } return m_longinfo; } // Used by pixel_transform to convert from UINT8 to float. static EightBitConverter converter; /// Helper routine: compute (gain*value)^invgamma /// namespace { inline float calc_exposure (float value, float gain, float invgamma) { if (invgamma != 1 && value >= 0) return powf (gain * value, invgamma); // Simple case - skip the expensive pow; also fall back to this // case for negative values, for which gamma makes no sense. return gain * value; } } void IvImage::pixel_transform(bool srgb_to_linear, int color_mode, int select_channel) { /// This table obeys the following function: /// /// unsigned char srgb2linear(unsigned char x) /// { /// float x_f = x/255.0; /// float x_l = 0.0; /// if (x_f <= 0.04045) /// x_l = x_f/12.92; /// else /// x_l = powf((x_f+0.055)/1.055,2.4); /// return (unsigned char)(x_l * 255 + 0.5) /// } /// /// It's used to transform from sRGB color space to linear color space. static const unsigned char srgb_to_linear_lut[256] = { 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 17, 18, 18, 19, 19, 20, 20, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29, 30, 30, 31, 32, 32, 33, 34, 35, 35, 36, 37, 37, 38, 39, 40, 41, 41, 42, 43, 44, 45, 45, 46, 47, 48, 49, 50, 51, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 76, 77, 78, 79, 80, 81, 82, 84, 85, 86, 87, 88, 90, 91, 92, 93, 95, 96, 97, 99, 100, 101, 103, 104, 105, 107, 108, 109, 111, 112, 114, 115, 116, 118, 119, 121, 122, 124, 125, 127, 128, 130, 131, 133, 134, 136, 138, 139, 141, 142, 144, 146, 147, 149, 151, 152, 154, 156, 157, 159, 161, 163, 164, 166, 168, 170, 171, 173, 175, 177, 179, 181, 183, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222, 224, 226, 229, 231, 233, 235, 237, 239, 242, 244, 246, 248, 250, 253, 255 }; unsigned char correction_table[256]; int total_channels = spec().nchannels; int color_channels = spec().nchannels; int max_channels = m_corrected_image.nchannels(); // FIXME: Now with the iterator and data proxy in place, it should be // trivial to apply the transformations to any kind of data, not just // UINT8. if (spec().format != TypeDesc::UINT8 || ! m_corrected_image.localpixels()) { return; } if (color_channels > 3) { color_channels = 3; } else if (color_channels == 2) { color_channels = 1; } // This image is Luminance or Luminance + Alpha, and we are asked to show // luminance. if (color_channels == 1 && color_mode == 3) { color_mode = 0; // Just copy as usual. } // Happy path: if (! srgb_to_linear && color_mode <= 1 && m_gamma == 1.0 && m_exposure == 0.0) { ImageBuf::ConstIterator src (*this); ImageBuf::Iterator dst (m_corrected_image); for ( ; src.valid (); ++src) { dst.pos (src.x(), src.y()); for (int i = 0; i < max_channels; i++) dst[i] = src[i]; } return; } // fill the correction_table if (gamma() == 1.0 && exposure() == 0.0) { for (int pixelvalue = 0; pixelvalue < 256; ++pixelvalue) { correction_table[pixelvalue] = pixelvalue; } } else { float inv_gamma = 1.0/gamma(); float gain = powf (2.0f, exposure()); for (int pixelvalue = 0; pixelvalue < 256; ++pixelvalue) { float pv_f = converter (pixelvalue); pv_f = clamp (calc_exposure (pv_f, gain, inv_gamma), 0.0f, 1.0f); correction_table[pixelvalue] = (unsigned char) (pv_f*255 + 0.5); } } ImageBuf::ConstIterator src (*this); ImageBuf::Iterator dst (m_corrected_image); for ( ; src.valid(); ++src) { dst.pos (src.x(), src.y()); if (color_mode == 0 || color_mode == 1) { // RGBA, RGB modes. int ch = 0; for (ch = 0; ch < color_channels; ch++) { if (srgb_to_linear) dst[ch] = correction_table[srgb_to_linear_lut[src[ch]]]; else dst[ch] = correction_table[src[ch]]; } for (; ch < max_channels; ch++) { dst[ch] = src[ch]; } } else if (color_mode == 3) { // Convert RGB to luminance, (Rec. 709 luma coefficients). float luminance; if (srgb_to_linear) { luminance = converter (srgb_to_linear_lut[src[0]])*0.2126f + converter (srgb_to_linear_lut[src[1]])*0.7152f + converter (srgb_to_linear_lut[src[2]])*0.0722f; } else { luminance = converter (src[0])*0.2126f + converter (src[1])*0.7152f + converter (src[2])*0.0722f; } unsigned char val = (unsigned char) (clamp (luminance, 0.0f, 1.0f) * 255.0 + 0.5); val = correction_table[val]; dst[0] = val; dst[1] = val; dst[2] = val; // Handle the rest of the channels for (int ch = 3; ch < total_channels; ++ch) { dst[ch] = src[ch]; } } else { // Single channel, heatmap. unsigned char v = 0; if (select_channel < color_channels) { if (srgb_to_linear) v = correction_table[srgb_to_linear_lut[src[select_channel]]]; else v = correction_table[src[select_channel]]; } else if (select_channel < total_channels) { v = src[select_channel]; } int ch = 0; for (; ch < color_channels; ++ch) { dst[ch] = v; } for (; ch < max_channels; ++ch) { dst[ch] = src[ch]; } } } } void IvImage::invalidate () { ustring filename (name()); reset (filename.string()); m_thumbnail_valid = false; m_image_valid = false; if (imagecache()) imagecache()->invalidate (filename); } openimageio-1.7.17~dfsg0.orig/src/iv/imageviewer.h0000644000175000017500000004173013151711064020214 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_IMAGEVIEWER_H #define OPENIMAGEIO_IMAGEVIEWER_H #if defined (_MSC_VER) // Ignore warnings about conditional expressions that always evaluate true // on a given platform but may evaluate differently on another. There's // nothing wrong with such conditionals. // Also ignore warnings about not being able to generate default assignment // operators for some Qt classes included in headers below. # pragma warning (disable : 4127 4512) #endif // included to remove std::min/std::max errors #include "OpenImageIO/platform.h" #include // This needs to be included before GL.h #include #include #include #include #include #ifndef QT_NO_PRINTER #include #endif #include "OpenImageIO/imageio.h" #include "OpenImageIO/imagebuf.h" OIIO_NAMESPACE_USING; class QComboBox; class QLabel; class QMenu; class QMenuBar; class QProgressBar; class QPushButton; class QSpinBox; class QScrollArea; class QStatusBar; class QVBoxLayout; class IvMainWindow; class IvInfoWindow; class IvPreferenceWindow; class IvCanvas; class IvGL; class IvImage; class IvImage : public ImageBuf { public: IvImage (const std::string &filename); virtual ~IvImage (); /// Read the image into ram. /// If secondary buffer is true, and the format is UINT8, then a secondary /// buffer will be created and the apply_corrections(), and /// select_channel() methods will work. /// Also, scanline will return a pointer to that buffer instead of the read /// buffer. bool read_iv (int subimage=0, int miplevel=0, bool force=false, TypeDesc format = TypeDesc::UNKNOWN, ProgressCallback progress_callback=NULL, void *progress_callback_data=NULL, bool secondary_buffer=false); bool init_spec_iv (const std::string &filename, int subimage, int miplevel); float gamma (void) const { return m_gamma; } void gamma (float e) { m_gamma = e; } float exposure (void) const { return m_exposure; } void exposure (float e) { m_exposure = e; } int nchannels () const { if (m_corrected_image.localpixels()) { return m_corrected_image.nchannels(); } return spec().nchannels; } std::string shortinfo () const; std::string longinfo () const; void invalidate (); /// Can we read the pixels of this image already? /// bool image_valid () const { return m_image_valid; } /// Copies data from the read buffer to the secondary buffer, selecting the /// given channel: /// -2 = luminance /// -1 = all channels /// 0 = red /// 1 = green /// 2 = blue /// 3 = alpha /// Then applies gamma/exposure correction (if any). This only works when /// the image is UINT8 (for now at least). It also performs sRGB to linear /// color space correction when indicated. void pixel_transform (bool srgb_to_linear, int color_mode, int channel); bool get_pixels (ROI roi, TypeDesc format, void *result) { if (m_corrected_image.localpixels ()) return m_corrected_image.get_pixels (roi, format, result); else return ImageBuf::get_pixels (roi, format, result); } bool auto_subimage (void) const { return m_auto_subimage; } void auto_subimage (bool v) { m_auto_subimage = v; } private: ImageBuf m_corrected_image; ///< Colorspace/gamma/exposure corrected image. char *m_thumbnail; ///< Thumbnail image bool m_thumbnail_valid; ///< Thumbnail is valid float m_gamma; ///< Gamma correction of this image float m_exposure; ///< Exposure gain of this image, in stops TypeDesc m_file_dataformat; ///< TypeDesc of the image on disk (not in ram) mutable std::string m_shortinfo; mutable std::string m_longinfo; bool m_image_valid; ///< Image is valid and pixels can be read. bool m_auto_subimage; ///< Automatically use subimages when zooming-in/out. }; class ImageViewer : public QMainWindow { Q_OBJECT public: ImageViewer(); ~ImageViewer(); enum COLOR_MODE { RGBA = 0, RGB = 1, SINGLE_CHANNEL = 2, LUMINANCE = 3, HEATMAP = 4 }; /// Tell the viewer about an image, but don't load it yet. void add_image (const std::string &filename); /// View this image. /// void current_image (int newimage); /// Which image index are we viewing? /// int current_image (void) const { return m_current_image; } /// View slide show (cycle through images with timed interval) /// void slide (long t, bool b); /// View a particular channel /// void viewChannel (int channel, COLOR_MODE colormode); /// Which channel are we viewing? /// int current_channel (void) const { return m_current_channel; } /// In what color mode are we? /// COLOR_MODE current_color_mode (void) const { return m_color_mode; } /// Return the current zoom level. 1.0 == 1:1 pixel ratio. Positive /// is a "zoom in" (closer/maxify), negative is zoom out (farther/minify). float zoom (void) const { return m_zoom; } /// Set a new view (zoom level and center position). If smooth is /// true, switch to the new view smoothly over many gradual steps, /// otherwise do it all in one step. The center position is measured /// in pixel coordinates. void view (float xcenter, float ycenter, float zoom, bool smooth=false, bool redraw=true); /// Set a new zoom level, keeping the center of view. If smooth is /// true, switch to the new zoom level smoothly over many gradual /// steps, otherwise do it all in one step. void zoom (float newzoom, bool smooth=false); /// Return a ptr to the current image, or NULL if there is no /// current image. IvImage *cur (void) const { if (m_images.empty()) return NULL; return m_current_image >= 0 ? m_images[m_current_image] : NULL; } /// Return a ref to the current image spec, or NULL if there is no /// current image. const ImageSpec *curspec (void) const { IvImage *img = cur(); return img ? &img->spec() : NULL; } bool pixelviewOn (void) const { return showPixelviewWindowAct && showPixelviewWindowAct->isChecked(); } bool pixelviewFollowsMouse (void) const { return pixelviewFollowsMouseBox && pixelviewFollowsMouseBox->isChecked(); } bool linearInterpolation (void) const { return linearInterpolationBox && linearInterpolationBox->isChecked(); } bool darkPalette (void) const { return darkPaletteBox ? darkPaletteBox->isChecked() : m_darkPalette; } QPalette palette (void) const { return m_palette; } private slots: void open(); ///< Dialog to open new image from file void reload(); ///< Reread current image from disk void openRecentFile(); ///< Open a recent file void closeImg(); ///< Close the current image void saveAs(); ///< Save As... functionality void saveWindowAs(); ///< Save As... functionality void saveSelectionAs(); ///< Save As... functionality void print(); ///< Print current image void deleteCurrentImage(); ///< Deleting displayed image void zoomIn(); ///< Zoom in to next power of 2 void zoomOut(); ///< Zoom out to next power of 2 void normalSize(); ///< Adjust zoom to 1:1 void fitImageToWindow(); ///< Adjust zoom to fit window exactly /// Resize window to fit image exactly. If zoomok is false, do not /// change the zoom, even to fit on screen. If minsize is true, do not /// resize smaller than default_width x default_height. void fitWindowToImage(bool zoomok=true, bool minsize=false); void fullScreenToggle(); ///< Toggle full screen mode void about(); ///< Show "about iv" dialog void prevImage(); ///< View previous image in sequence void nextImage(); ///< View next image in sequence void toggleImage(); ///< View most recently viewed image void exposureMinusOneTenthStop(); ///< Decrease exposure 1/10 stop void exposureMinusOneHalfStop(); ///< Decrease exposure 1/2 stop void exposurePlusOneTenthStop(); ///< Increase exposure 1/10 stop void exposurePlusOneHalfStop(); ///< Increase exposure 1/2 stop void gammaMinus(); ///< Decrease gamma 0.05 void gammaPlus(); ///< Increase gamma 0.05 void viewChannelFull(); ///< View RGB void viewChannelRed(); ///< View just red as gray void viewChannelGreen(); ///< View just green as gray void viewChannelBlue(); ///< View just blue as gray void viewChannelAlpha(); ///< View alpha as gray void viewChannelLuminance(); ///< View current 3 channels as luminance void viewChannelPrev(); ///< View just prev channel as gray void viewChannelNext(); ///< View just next channel as gray void viewColorRGBA(); ///< View current 4 channels as RGBA void viewColorRGB(); ///< View current 3 channels as RGB void viewColor1Ch(); ///< View current channel as gray void viewColorHeatmap(); ///< View current channel as heatmap. void viewSubimagePrev(); ///< View prev subimage void viewSubimageNext(); ///< View next subimage void sortByName(); ///< Sort images by Name. void sortByPath(); ///< Sort images based on full file path void sortByImageDate(); ///< Sort images by metadata date void sortByFileDate(); ///< Sort images by file Date Stamp. void sortReverse(); ///< Reverse the current order of images void slideShow(); ///< Starts slide show void slideLoop(); ///< Slide show in a loop void slideNoLoop(); ///< Slide show without loop void setSlideShowDuration(int seconds); ///< Set the slide show duration in seconds void slideImages(); ///< Slide show - move to next image void showInfoWindow(); ///< View extended info on image void showPixelviewWindow(); ///< View closeup pixel view void editPreferences(); ///< Edit viewer preferences private: void createActions (); void createMenus (); void createToolBars (); void createStatusBar (); void readSettings (bool ui_is_set_up=true); void writeSettings (); void updateActions (); void addRecentFile (const std::string &name); void removeRecentFile (const std::string &name); void updateRecentFilesMenu (); bool loadCurrentImage (int subimage = 0, int miplevel = 0); void displayCurrentImage (bool update = true); void updateTitle (); void updateStatusBar (); void keyPressEvent (QKeyEvent *event); void resizeEvent (QResizeEvent *event); void closeEvent (QCloseEvent *event); QTimer *slideTimer; ///< Timer to use for slide show mode long slideDuration_ms; ///< Slide show mode duration (in ms) bool slide_loop; ///< Do we loop when in slide mode? IvGL *glwin; IvInfoWindow *infoWindow; IvPreferenceWindow *preferenceWindow; #ifndef QT_NO_PRINTER QPrinter printer; #endif QAction *openAct, *reloadAct, *closeImgAct; static const unsigned int MaxRecentFiles = 10; QAction *openRecentAct[MaxRecentFiles]; QAction *saveAsAct, *saveWindowAsAct, *saveSelectionAsAct; QAction *printAct; QAction *deleteCurrentImageAct; QAction *exitAct; QAction *gammaPlusAct, *gammaMinusAct; QAction *exposurePlusOneTenthStopAct, *exposurePlusOneHalfStopAct; QAction *exposureMinusOneTenthStopAct, *exposureMinusOneHalfStopAct; QAction *viewChannelFullAct, *viewChannelRedAct, *viewChannelGreenAct; QAction *viewChannelBlueAct, *viewChannelAlphaAct; QAction *viewChannelPrevAct, *viewChannelNextAct; QAction *viewColorRGBAAct, *viewColorRGBAct, *viewColor1ChAct; QAction *viewColorLumAct, *viewColorHeatmapAct; QAction *viewSubimagePrevAct, *viewSubimageNextAct; QAction *zoomInAct; QAction *zoomOutAct; QAction *normalSizeAct; QAction *fitWindowToImageAct, *fitImageToWindowAct; QAction *fullScreenAct; QAction *aboutAct; QAction *nextImageAct, *prevImageAct, *toggleImageAct; QAction *sortByNameAct, *sortByPathAct, *sortReverseAct; QAction *sortByImageDateAct, *sortByFileDateAct; QAction *slideShowAct, *slideLoopAct, *slideNoLoopAct; QAction *showInfoWindowAct; QAction *editPreferencesAct; QAction *showPixelviewWindowAct; QMenu *fileMenu, *editMenu, /**imageMenu,*/ *viewMenu, *toolsMenu, *helpMenu; QMenu *openRecentMenu; QMenu *expgamMenu, *channelMenu, *colormodeMenu, *slideMenu, *sortMenu; QLabel *statusImgInfo, *statusViewInfo; QProgressBar *statusProgress; QComboBox *mouseModeComboBox; enum MouseMode { MouseModeZoom, MouseModePan, MouseModeWipe, MouseModeSelect, MouseModeAnnotate }; QCheckBox *pixelviewFollowsMouseBox; QCheckBox *linearInterpolationBox; QCheckBox *darkPaletteBox; QCheckBox *autoMipmap; QLabel *maxMemoryICLabel; QSpinBox *maxMemoryIC; QLabel *slideShowDurationLabel; QSpinBox *slideShowDuration; std::vector m_images; ///< List of images int m_current_image; ///< Index of current image, -1 if none int m_current_channel; ///< Channel we're viewing. COLOR_MODE m_color_mode; ///< How to show the current channel(s). int m_last_image; ///< Last image we viewed float m_zoom; ///< Zoom amount (positive maxifies) bool m_fullscreen; ///< Full screen mode std::vector m_recent_files; ///< Recently opened files float m_default_gamma; ///< Default gamma of the display QPalette m_palette; ///< Custom palette bool m_darkPalette; ///< Use dark palette? static const int m_default_width = 640; ///< The default width of the window. static const int m_default_height = 480; ///< The default height of the window. // What zoom do we need to fit these window dimensions? float zoom_needed_to_fit (int w, int h); friend class IvCanvas; friend class IvGL; friend class IvInfoWindow; friend class IvPreferenceWindow; friend bool image_progress_callback (void *opaque, float done); }; class IvInfoWindow : public QDialog { Q_OBJECT public: IvInfoWindow (ImageViewer &viewer, bool visible=true); void update (IvImage *img); protected: void keyPressEvent (QKeyEvent *event); private: QPushButton *closeButton; QScrollArea *scrollArea; QLabel *infoLabel; ImageViewer &m_viewer; bool m_visible; }; class IvPreferenceWindow : public QDialog { Q_OBJECT public: IvPreferenceWindow (ImageViewer &viewer); protected: void keyPressEvent (QKeyEvent *event); private: QVBoxLayout *layout; QPushButton *closeButton; ImageViewer &m_viewer; }; #endif // OPENIMAGEIO_IMAGEVIEWER_H openimageio-1.7.17~dfsg0.orig/src/iv/ivmain.cpp0000644000175000017500000001145713151711064017531 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #if defined(_MSC_VER) // Ignore warnings about conditional expressions that always evaluate true // on a given platform but may evaluate differently on another. There's // nothing wrong with such conditionals. // Also ignore warnings about not being able to generate default assignment // operators for some Qt classes included in headers below. # pragma warning (disable : 4127 4512) #endif #include #include #include #include #include #include #include #include #include "imageviewer.h" #include "OpenImageIO/timer.h" #include "OpenImageIO/argparse.h" #include "OpenImageIO/sysutil.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/imagecache.h" #include "OpenImageIO/filesystem.h" OIIO_NAMESPACE_USING; static bool verbose = false; static bool foreground_mode = false; static std::vector filenames; static int parse_files (int argc, const char *argv[]) { for (int i = 0; i < argc; i++) filenames.push_back (argv[i]); return 0; } static void getargs (int argc, char *argv[]) { bool help = false; ArgParse ap; ap.options ("iv -- image viewer\n" OIIO_INTRO_STRING "\n" "Usage: iv [options] [filename...]", "%*", parse_files, "", "--help", &help, "Print help message", "-v", &verbose, "Verbose status messages", "-F", &foreground_mode, "Foreground mode", NULL); if (ap.parse (argc, (const char**)argv) < 0) { std::cerr << ap.geterror() << std::endl; ap.usage (); exit (EXIT_FAILURE); } if (help) { ap.usage (); exit (EXIT_FAILURE); } } #ifdef WIN32 // if we are not in DEBUG mode this code switch the app to // full windowed mode (no console and no need to define WinMain) // FIXME: this should be done in CMakeLists.txt but first we have to // fix Windows Debug build # ifdef NDEBUG # pragma comment(linker, "/subsystem:windows /entry:mainCRTStartup") # endif #endif int main (int argc, char *argv[]) { Filesystem::convert_native_arguments (argc, (const char **)argv); getargs (argc, argv); if (! foreground_mode) Sysutil::put_in_background (argc, argv); // LG // Q_INIT_RESOURCE(iv); QApplication app(argc, argv); ImageViewer *mainWin = new ImageViewer; mainWin->show(); // Set up the imagecache with parameters that make sense for iv ImageCache *imagecache = ImageCache::create (true); imagecache->attribute ("autotile", 256); imagecache->attribute ("deduplicate", (int)0); // Make sure we are the top window with the focus. mainWin->raise (); mainWin->activateWindow (); // Add the images BOOST_FOREACH (const std::string &s, filenames) { mainWin->add_image (s); } mainWin->current_image (0); int r = app.exec(); // OK to clean up here #ifdef NDEBUG if (verbose) #endif { size_t mem = Sysutil::memory_used (true); std::cout << "iv total memory used: " << Strutil::memformat (mem) << "\n"; std::cout << "\n"; std::cout << imagecache->getstats (1+verbose) << "\n"; } return r; } openimageio-1.7.17~dfsg0.orig/src/iv/ivinfowin.cpp0000644000175000017500000000664313151711064020257 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include "imageviewer.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/strutil.h" IvInfoWindow::IvInfoWindow (ImageViewer &viewer, bool visible) : QDialog(&viewer), m_viewer(viewer), m_visible (visible) { infoLabel = new QLabel; infoLabel->setPalette (viewer.palette()); scrollArea = new QScrollArea; scrollArea->setPalette (viewer.palette()); scrollArea->setWidgetResizable (true); scrollArea->setWidget (infoLabel); scrollArea->setSizePolicy (QSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding, QSizePolicy::Label)); scrollArea->setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff); scrollArea->setFrameStyle (QFrame::NoFrame); scrollArea->setAlignment (Qt::AlignTop); closeButton = new QPushButton (tr("Close")); connect (closeButton, SIGNAL(clicked()), this, SLOT(hide())); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget (scrollArea); mainLayout->addWidget (closeButton); setLayout (mainLayout); infoLabel->show(); scrollArea->show(); setWindowTitle (tr("Image Info")); } void IvInfoWindow::update (IvImage *img) { std::string newtitle; if (img) { newtitle = Strutil::format ("%s - iv Info", img->name().c_str()); infoLabel->setText (img->longinfo().c_str()); } else { newtitle = Strutil::format ("iv Info"); infoLabel->setText (tr("No image loaded.")); } setWindowTitle (newtitle.c_str()); } void IvInfoWindow::keyPressEvent (QKeyEvent *event) { if (event->key() == Qt::Key_W && (event->modifiers() & Qt::ControlModifier)) { event->accept(); hide(); } else { event->ignore(); } } openimageio-1.7.17~dfsg0.orig/src/tiff.imageio/0000755000175000017500000000000013151711064017455 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/tiff.imageio/CMakeLists.txt0000644000175000017500000000032713151711064022217 0ustar mfvmfvadd_oiio_plugin (tiffinput.cpp tiffoutput.cpp INCLUDE_DIRS ${TIFF_INCLUDE_DIR} LINK_LIBRARIES ${TIFF_LIBRARIES} ${JPEG_LIBRARIES} ${ZLIB_LIBRARIES}) openimageio-1.7.17~dfsg0.orig/src/tiff.imageio/tiffoutput.cpp0000644000175000017500000013234313151711064022400 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include // Some EXIF tags that don't seem to be in tiff.h #ifndef EXIFTAG_SECURITYCLASSIFICATION #define EXIFTAG_SECURITYCLASSIFICATION 37394 #endif #ifndef EXIFTAG_IMAGEHISTORY #define EXIFTAG_IMAGEHISTORY 37395 #endif #include "OpenImageIO/dassert.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/sysutil.h" #include "OpenImageIO/timer.h" #include "OpenImageIO/fmath.h" #include OIIO_PLUGIN_NAMESPACE_BEGIN namespace { // This is the default interval between checkpoints. // While these are cheap, we need to throttle them // so we don't checkpoint too often... (each checkpoint // re-writes the tiff header and any new tiles / scanlines) static double DEFAULT_CHECKPOINT_INTERVAL_SECONDS = 5.0; static int MIN_SCANLINES_OR_TILES_PER_CHECKPOINT = 64; } class TIFFOutput : public ImageOutput { public: TIFFOutput (); virtual ~TIFFOutput (); virtual const char * format_name (void) const { return "tiff"; } virtual int supports (string_view feature) const; virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode=Create); virtual bool close (); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); private: TIFF *m_tif; std::vector m_scratch; Timer m_checkpointTimer; int m_checkpointItems; unsigned int m_dither; // The following fields are describing what we are writing to the file, // not what's in the client's view of the buffer. int m_planarconfig; int m_compression; int m_photometric; unsigned int m_bitspersample; ///< Of the *file*, not the client's view int m_outputchans; // Number of channels for the output bool m_convert_rgb_to_cmyk; // Initialize private members to pre-opened state void init (void) { m_tif = NULL; m_checkpointItems = 0; m_compression = COMPRESSION_ADOBE_DEFLATE; m_photometric = PHOTOMETRIC_RGB; m_outputchans = 0; m_convert_rgb_to_cmyk = false; } // Convert planar contiguous to planar separate data format void contig_to_separate (int n, const char *contig, char *separate); // Convert RGB to CMYK void* convert_to_cmyk (int npixels, const void* data); // Add a parameter to the output bool put_parameter (const std::string &name, TypeDesc type, const void *data); bool write_exif_data (); // Make our best guess about whether the spec is describing data that // is in true CMYK values. bool source_is_cmyk (const ImageSpec &spec); // Are we fairly certain that the spec is describing RGB values? bool source_is_rgb (const ImageSpec &spec); }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *tiff_output_imageio_create () { return new TIFFOutput; } OIIO_EXPORT int tiff_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* tiff_imageio_library_version () { string_view v (TIFFGetVersion()); v = v.substr (0, v.find ('\n')); return v.c_str(); } OIIO_EXPORT const char * tiff_output_extensions[] = { "tiff", "tif", "tx", "env", "sm", "vsm", NULL }; OIIO_PLUGIN_EXPORTS_END extern std::string & oiio_tiff_last_error (); extern void oiio_tiff_set_error_handler (); struct CompressionCode { int code; const char *name; }; // We comment out a lot of these because we only support a subset for // writing. These can be uncommented on a case by case basis as they are // thoroughly tested and included in the testsuite. static CompressionCode tiff_compressions[] = { { COMPRESSION_NONE, "none" }, // no compression { COMPRESSION_LZW, "lzw" }, // LZW { COMPRESSION_ADOBE_DEFLATE, "zip" }, // deflate / zip { COMPRESSION_DEFLATE, "zip" }, // deflate / zip { COMPRESSION_CCITTRLE, "ccittrle" }, // CCITT RLE // { COMPRESSION_CCITTFAX3, "ccittfax3" }, // CCITT group 3 fax // { COMPRESSION_CCITT_T4, "ccitt_t4" }, // CCITT T.4 // { COMPRESSION_CCITTFAX4, "ccittfax4" }, // CCITT group 4 fax // { COMPRESSION_CCITT_T6, "ccitt_t6" }, // CCITT T.6 // { COMPRESSION_OJPEG, "ojpeg" }, // old (pre-TIFF6.0) JPEG { COMPRESSION_JPEG, "jpeg" }, // JPEG // { COMPRESSION_NEXT, "next" }, // NeXT 2-bit RLE // { COMPRESSION_CCITTRLEW, "ccittrle2" }, // #1 w/ word alignment { COMPRESSION_PACKBITS, "packbits" }, // Macintosh RLE // { COMPRESSION_THUNDERSCAN, "thunderscan" }, // ThundeScan RLE // { COMPRESSION_IT8CTPAD, "IT8CTPAD" }, // IT8 CT w/ patting // { COMPRESSION_IT8LW, "IT8LW" }, // IT8 linework RLE // { COMPRESSION_IT8MP, "IT8MP" }, // IT8 monochrome picture // { COMPRESSION_IT8BL, "IT8BL" }, // IT8 binary line art // { COMPRESSION_PIXARFILM, "pixarfilm" }, // Pixar 10 bit LZW // { COMPRESSION_PIXARLOG, "pixarlog" }, // Pixar 11 bit ZIP // { COMPRESSION_DCS, "dcs" }, // Kodak DCS encoding // { COMPRESSION_JBIG, "isojbig" }, // ISO JBIG // { COMPRESSION_SGILOG, "sgilog" }, // SGI log luminance RLE // { COMPRESSION_SGILOG24, "sgilog24" }, // SGI log 24bit // { COMPRESSION_JP2000, "jp2000" }, // Leadtools JPEG2000 #if defined(TIFF_VERSION_BIG) && TIFFLIB_VERSION >= 20120922 // Others supported in more recent TIFF library versions. // { COMPRESSION_T85, "T85" }, // TIFF/FX T.85 JBIG // { COMPRESSION_T43, "T43" }, // TIFF/FX T.43 color layered JBIG // { COMPRESSION_LZMA, "lzma" }, // LZMA2 #endif { -1, NULL } }; static int tiff_compression_code (string_view name) { for (int i = 0; tiff_compressions[i].name; ++i) if (Strutil::iequals (name, tiff_compressions[i].name)) return tiff_compressions[i].code; return COMPRESSION_ADOBE_DEFLATE; // default } TIFFOutput::TIFFOutput () { oiio_tiff_set_error_handler (); init (); } TIFFOutput::~TIFFOutput () { // Close, if not already done. close (); } int TIFFOutput::supports (string_view feature) const { if (feature == "tiles") return true; if (feature == "multiimage") return true; if (feature == "appendsubimage") return true; if (feature == "alpha") return true; if (feature == "nchannels") return true; if (feature == "displaywindow") return true; if (feature == "origin") return true; // N.B. TIFF doesn't support "negativeorigin" if (feature == "exif") return true; if (feature == "iptc") return true; // N.B. TIFF doesn't support arbitrary metadata. // FIXME: we could support "volumes" and "empty" // Everything else, we either don't support or don't know about return false; } #define ICC_PROFILE_ATTR "ICCProfile" bool TIFFOutput::open (const std::string &name, const ImageSpec &userspec, OpenMode mode) { if (mode == AppendMIPLevel) { error ("%s does not support MIP levels", format_name()); return false; } close (); // Close any already-opened file m_spec = userspec; // Stash the spec // Check for things this format doesn't support if (m_spec.width < 1 || m_spec.height < 1) { error ("Image resolution must be at least 1x1, you asked for %d x %d", m_spec.width, m_spec.height); return false; } if (m_spec.tile_width) { if (m_spec.tile_width % 16 != 0 || m_spec.tile_height % 16 != 0 || m_spec.tile_height == 0) { error("Tile size must be a multiple of 16, you asked for %d x %d", m_spec.tile_width, m_spec.tile_height); return false; } } if (m_spec.depth < 1) m_spec.depth = 1; // Open the file #ifdef _WIN32 std::wstring wname = Strutil::utf8_to_utf16 (name); m_tif = TIFFOpenW (wname.c_str(), mode == AppendSubimage ? "a" : "w"); #else m_tif = TIFFOpen (name.c_str(), mode == AppendSubimage ? "a" : "w"); #endif if (! m_tif) { error ("Can't open \"%s\" for output.", name.c_str()); return false; } // N.B. Clamp position at 0... TIFF is internally incapable of having // negative origin. TIFFSetField (m_tif, TIFFTAG_XPOSITION, (float)std::max (0, m_spec.x)); TIFFSetField (m_tif, TIFFTAG_YPOSITION, (float)std::max (0, m_spec.y)); TIFFSetField (m_tif, TIFFTAG_IMAGEWIDTH, m_spec.width); TIFFSetField (m_tif, TIFFTAG_IMAGELENGTH, m_spec.height); // Handle display window or "full" size. Note that TIFF can't represent // nonzero offsets of the full size, so we may need to expand the // display window to encompass the origin. if ((m_spec.full_width != 0 || m_spec.full_height != 0) && (m_spec.full_width != m_spec.width || m_spec.full_height != m_spec.height || m_spec.full_x != 0 || m_spec.full_y != 0)) { TIFFSetField (m_tif, TIFFTAG_PIXAR_IMAGEFULLWIDTH, m_spec.full_width+m_spec.full_x); TIFFSetField (m_tif, TIFFTAG_PIXAR_IMAGEFULLLENGTH, m_spec.full_height+m_spec.full_y); } if (m_spec.tile_width) { TIFFSetField (m_tif, TIFFTAG_TILEWIDTH, m_spec.tile_width); TIFFSetField (m_tif, TIFFTAG_TILELENGTH, m_spec.tile_height); } else { // Scanline images must set rowsperstrip TIFFSetField (m_tif, TIFFTAG_ROWSPERSTRIP, 32); } TIFFSetField (m_tif, TIFFTAG_SAMPLESPERPIXEL, m_spec.nchannels); int orientation = m_spec.get_int_attribute("Orientation", 1); TIFFSetField (m_tif, TIFFTAG_ORIENTATION, orientation); m_bitspersample = m_spec.get_int_attribute ("oiio:BitsPerSample"); int sampformat; switch (m_spec.format.basetype) { case TypeDesc::INT8: m_bitspersample = 8; sampformat = SAMPLEFORMAT_INT; break; case TypeDesc::UINT8: if (m_bitspersample != 2 && m_bitspersample != 4) m_bitspersample = 8; sampformat = SAMPLEFORMAT_UINT; break; case TypeDesc::INT16: m_bitspersample = 16; sampformat = SAMPLEFORMAT_INT; break; case TypeDesc::UINT16: if (m_bitspersample != 10 && m_bitspersample != 12) m_bitspersample = 16; sampformat = SAMPLEFORMAT_UINT; break; case TypeDesc::INT32: m_bitspersample = 32; sampformat = SAMPLEFORMAT_INT; break; case TypeDesc::UINT32: m_bitspersample = 32; sampformat = SAMPLEFORMAT_UINT; break; case TypeDesc::HALF: // Adobe extension, see http://chriscox.org/TIFFTN3d1.pdf // Unfortunately, Nuke 9.0, and probably many other apps we care // about, cannot read 16 bit float TIFFs correctly. Revisit this // again in future releases. (comment added Feb 2015) // For now, the default is to NOT write this (instead writing float) // unless the "tiff:half" attribute is nonzero -- use the global // OIIO attribute, but override with a specific attribute for this // file. if (m_spec.get_int_attribute("tiff:half", OIIO::get_int_attribute("tiff:half"))) { m_bitspersample = 16; } else { // Silently change requests for unsupported 'half' to 'float' m_bitspersample = 32; m_spec.set_format (TypeDesc::FLOAT); } sampformat = SAMPLEFORMAT_IEEEFP; break; case TypeDesc::FLOAT: m_bitspersample = 32; sampformat = SAMPLEFORMAT_IEEEFP; break; case TypeDesc::DOUBLE: m_bitspersample = 64; sampformat = SAMPLEFORMAT_IEEEFP; break; default: // Everything else, including UNKNOWN -- default to 8 bit m_bitspersample = 8; sampformat = SAMPLEFORMAT_UINT; m_spec.set_format (TypeDesc::UINT8); break; } TIFFSetField (m_tif, TIFFTAG_BITSPERSAMPLE, m_bitspersample); TIFFSetField (m_tif, TIFFTAG_SAMPLEFORMAT, sampformat); m_photometric = (m_spec.nchannels >= 3 ? PHOTOMETRIC_RGB : PHOTOMETRIC_MINISBLACK); string_view comp = m_spec.get_string_attribute("Compression", "zip"); if (Strutil::iequals (comp, "jpeg") && (m_spec.format != TypeDesc::UINT8 || m_spec.nchannels != 3)) { comp = "zip"; // can't use JPEG for anything but 3xUINT8 } m_compression = tiff_compression_code (comp); TIFFSetField (m_tif, TIFFTAG_COMPRESSION, m_compression); // Use predictor when using compression if (m_compression == COMPRESSION_LZW || m_compression == COMPRESSION_ADOBE_DEFLATE) { if (m_spec.format == TypeDesc::FLOAT || m_spec.format == TypeDesc::DOUBLE || m_spec.format == TypeDesc::HALF) { TIFFSetField (m_tif, TIFFTAG_PREDICTOR, PREDICTOR_FLOATINGPOINT); // N.B. Very old versions of libtiff did not support this // predictor. It's possible that certain apps can't read // floating point TIFFs with this set. But since it's been // documented since 2005, let's take our chances. Comment // out the above line if this is problematic. } else if (m_bitspersample == 8 || m_bitspersample == 16) { // predictors not supported for unusual bit depths (e.g. 10) TIFFSetField (m_tif, TIFFTAG_PREDICTOR, PREDICTOR_HORIZONTAL); } if (m_compression == COMPRESSION_ADOBE_DEFLATE) { int q = m_spec.get_int_attribute ("tiff:zipquality", -1); if (q >= 0) TIFFSetField (m_tif, TIFFTAG_ZIPQUALITY, OIIO::clamp(q, 1, 9)); } } else if (m_compression == COMPRESSION_JPEG) { TIFFSetField (m_tif, TIFFTAG_JPEGQUALITY, m_spec.get_int_attribute("CompressionQuality", 95)); TIFFSetField (m_tif, TIFFTAG_ROWSPERSTRIP, 64); m_spec.attribute ("tiff:RowsPerStrip", 64); if (m_photometric == PHOTOMETRIC_RGB) { // Compression works so much better when we ask the library to // auto-convert RGB to YCbCr. TIFFSetField (m_tif, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB); m_photometric = PHOTOMETRIC_YCBCR; } } m_outputchans = m_spec.nchannels; if (m_photometric == PHOTOMETRIC_RGB) { // There are a few ways in which we allow allow the user to specify // translation to different photometric types. string_view photo = m_spec.get_string_attribute("tiff:ColorSpace"); if (Strutil::iequals (photo, "CMYK") || Strutil::iequals (photo, "color separated")) { // User has requested via the "tiff:ColorSpace" attribute that // the file be written as color separated channels. m_photometric = PHOTOMETRIC_SEPARATED; if (m_spec.format != TypeDesc::UINT8 || m_spec.format != TypeDesc::UINT16) { m_spec.format = TypeDesc::UINT8; m_bitspersample = 8; TIFFSetField (m_tif, TIFFTAG_BITSPERSAMPLE, m_bitspersample); TIFFSetField (m_tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); } if (source_is_rgb(m_spec)) { // Case: RGB -> CMYK, do the conversions per pixel m_convert_rgb_to_cmyk = true; m_outputchans = 4; // output 4, not 4 chans TIFFSetField (m_tif, TIFFTAG_SAMPLESPERPIXEL, m_outputchans); TIFFSetField (m_tif, TIFFTAG_INKSET, INKSET_CMYK); } else if (source_is_cmyk(m_spec)) { // Case: CMYK -> CMYK (do not transform) m_convert_rgb_to_cmyk = false; TIFFSetField (m_tif, TIFFTAG_INKSET, INKSET_CMYK); } else { // Case: arbitrary inks m_convert_rgb_to_cmyk = false; TIFFSetField (m_tif, TIFFTAG_INKSET, INKSET_MULTIINK); std::string inknames; for (int i = 0; i < m_spec.nchannels; ++i) { if (i) inknames.insert (inknames.size(), 1, '\0'); if (i < (int)m_spec.channelnames.size()) inknames.insert (inknames.size(), m_spec.channelnames[i]); else inknames.insert (inknames.size(), Strutil::format("ink%d", i)); } TIFFSetField (m_tif, TIFFTAG_INKNAMES, int(inknames.size()+1), &inknames[0]); TIFFSetField (m_tif, TIFFTAG_NUMBEROFINKS, m_spec.nchannels); } } } TIFFSetField (m_tif, TIFFTAG_PHOTOMETRIC, m_photometric); // ExtraSamples tag if ((m_spec.alpha_channel >= 0 || m_spec.nchannels > 3) && m_photometric != PHOTOMETRIC_SEPARATED) { bool unass = m_spec.get_int_attribute("oiio:UnassociatedAlpha", 0); int defaultchans = m_spec.nchannels >= 3 ? 3 : 1; short e = m_spec.nchannels - defaultchans; std::vector extra (e); for (int c = 0; c < e; ++c) { if (m_spec.alpha_channel == (c+defaultchans)) extra[c] = unass ? EXTRASAMPLE_UNASSALPHA : EXTRASAMPLE_ASSOCALPHA; else extra[c] = EXTRASAMPLE_UNSPECIFIED; } TIFFSetField (m_tif, TIFFTAG_EXTRASAMPLES, e, &extra[0]); } ImageIOParameter *param; const char *str = NULL; // Did the user request separate planar configuration? m_planarconfig = PLANARCONFIG_CONTIG; if ((param = m_spec.find_attribute("planarconfig", TypeDesc::STRING)) || (param = m_spec.find_attribute("tiff:planarconfig", TypeDesc::STRING))) { str = *(char **)param->data(); if (str && Strutil::iequals (str, "separate")) m_planarconfig = PLANARCONFIG_SEPARATE; } // Can't deal with the headache of separate image planes when using // bit packing, or CMYK. Just punt by forcing contig in those cases. if (m_bitspersample != spec().format.size()*8 || m_photometric == PHOTOMETRIC_SEPARATED) m_planarconfig = PLANARCONFIG_CONTIG; if (m_planarconfig == PLANARCONFIG_SEPARATE) { if (! m_spec.tile_width) { // I can only seem to make separate planarconfig work when // rowsperstrip is 1. TIFFSetField (m_tif, TIFFTAG_ROWSPERSTRIP, 1); } } TIFFSetField (m_tif, TIFFTAG_PLANARCONFIG, m_planarconfig); // Automatically set date field if the client didn't supply it. if (! m_spec.find_attribute("DateTime")) { time_t now; time (&now); struct tm mytm; Sysutil::get_local_time (&now, &mytm); std::string date = Strutil::format ("%4d:%02d:%02d %2d:%02d:%02d", mytm.tm_year+1900, mytm.tm_mon+1, mytm.tm_mday, mytm.tm_hour, mytm.tm_min, mytm.tm_sec); m_spec.attribute ("DateTime", date); } // Write ICC profile, if we have anything const ImageIOParameter* icc_profile_parameter = m_spec.find_attribute(ICC_PROFILE_ATTR); if (icc_profile_parameter != NULL) { unsigned char *icc_profile = (unsigned char*)icc_profile_parameter->data(); uint32 length = icc_profile_parameter->type().size(); if (icc_profile && length) TIFFSetField (m_tif, TIFFTAG_ICCPROFILE, length, icc_profile); } if (Strutil::iequals (m_spec.get_string_attribute ("oiio:ColorSpace"), "sRGB")) m_spec.attribute ("Exif:ColorSpace", 1); // Deal with missing XResolution or YResolution, or a PixelAspectRatio // that contradicts them. float X_density = m_spec.get_float_attribute ("XResolution", 1.0f); float Y_density = m_spec.get_float_attribute ("YResolution", 1.0f); float aspect = m_spec.get_float_attribute ("PixelAspectRatio", 1.0f); if (X_density < 1.0f || Y_density < 1.0f || aspect*X_density != Y_density) { if (X_density < 1.0f || Y_density < 1.0f) { X_density = Y_density = 1.0f; m_spec.attribute ("ResolutionUnit", "none"); } m_spec.attribute ("XResolution", X_density); m_spec.attribute ("YResolution", X_density * aspect); } // Deal with all other params for (size_t p = 0; p < m_spec.extra_attribs.size(); ++p) put_parameter (m_spec.extra_attribs[p].name().string(), m_spec.extra_attribs[p].type(), m_spec.extra_attribs[p].data()); std::vector iptc; encode_iptc_iim (m_spec, iptc); if (iptc.size()) { iptc.resize ((iptc.size()+3) & (0xffff-3)); // round up TIFFSetField (m_tif, TIFFTAG_RICHTIFFIPTC, iptc.size()/4, &iptc[0]); } std::string xmp = encode_xmp (m_spec, true); if (! xmp.empty()) TIFFSetField (m_tif, TIFFTAG_XMLPACKET, xmp.size(), xmp.c_str()); TIFFCheckpointDirectory (m_tif); // Ensure the header is written early m_checkpointTimer.start(); // Initialize the to the fileopen time m_checkpointItems = 0; // Number of tiles or scanlines we've written m_dither = (m_spec.format == TypeDesc::UINT8) ? m_spec.get_int_attribute ("oiio:dither", 0) : 0; return true; } bool TIFFOutput::put_parameter (const std::string &name, TypeDesc type, const void *data) { if (Strutil::iequals(name, "Artist") && type == TypeDesc::STRING) { TIFFSetField (m_tif, TIFFTAG_ARTIST, *(char**)data); return true; } if (Strutil::iequals(name, "Copyright") && type == TypeDesc::STRING) { TIFFSetField (m_tif, TIFFTAG_COPYRIGHT, *(char**)data); return true; } if (Strutil::iequals(name, "DateTime") && type == TypeDesc::STRING) { TIFFSetField (m_tif, TIFFTAG_DATETIME, *(char**)data); return true; } if ((Strutil::iequals(name, "name") || Strutil::iequals(name, "DocumentName")) && type == TypeDesc::STRING) { TIFFSetField (m_tif, TIFFTAG_DOCUMENTNAME, *(char**)data); return true; } if (Strutil::iequals(name,"fovcot") && type == TypeDesc::FLOAT) { double d = *(float *)data; TIFFSetField (m_tif, TIFFTAG_PIXAR_FOVCOT, d); return true; } if ((Strutil::iequals(name, "host") || Strutil::iequals(name, "HostComputer")) && type == TypeDesc::STRING) { TIFFSetField (m_tif, TIFFTAG_HOSTCOMPUTER, *(char**)data); return true; } if ((Strutil::iequals(name, "description") || Strutil::iequals(name, "ImageDescription")) && type == TypeDesc::STRING) { TIFFSetField (m_tif, TIFFTAG_IMAGEDESCRIPTION, *(char**)data); return true; } if (Strutil::iequals(name, "tiff:Predictor") && type == TypeDesc::INT) { TIFFSetField (m_tif, TIFFTAG_PREDICTOR, *(int *)data); return true; } if (Strutil::iequals(name, "ResolutionUnit") && type == TypeDesc::STRING) { const char *s = *(char**)data; bool ok = true; if (Strutil::iequals (s, "none")) TIFFSetField (m_tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE); else if (Strutil::iequals (s, "in") || Strutil::iequals (s, "inch")) TIFFSetField (m_tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); else if (Strutil::iequals (s, "cm")) TIFFSetField (m_tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_CENTIMETER); else ok = false; return ok; } if (Strutil::iequals(name, "tiff:RowsPerStrip") && ! m_spec.tile_width /* don't set rps for tiled files */ && m_planarconfig == PLANARCONFIG_CONTIG /* only for contig */) { if (type == TypeDesc::INT) { TIFFSetField (m_tif, TIFFTAG_ROWSPERSTRIP, std::min (*(int*)data, m_spec.height)); return true; } else if (type == TypeDesc::STRING) { // Back-compatibility with Entropy and PRMan TIFFSetField (m_tif, TIFFTAG_ROWSPERSTRIP, std::min (atoi(*(char **)data), m_spec.height)); return true; } } if (Strutil::iequals(name, "Make") && type == TypeDesc::STRING) { TIFFSetField (m_tif, TIFFTAG_MAKE, *(char**)data); return true; } if (Strutil::iequals(name, "Model") && type == TypeDesc::STRING) { TIFFSetField (m_tif, TIFFTAG_MODEL, *(char**)data); return true; } if (Strutil::iequals(name, "Software") && type == TypeDesc::STRING) { TIFFSetField (m_tif, TIFFTAG_SOFTWARE, *(char**)data); return true; } if (Strutil::iequals(name, "tiff:SubFileType") && type == TypeDesc::INT) { TIFFSetField (m_tif, TIFFTAG_SUBFILETYPE, *(int*)data); return true; } if (Strutil::iequals(name, "textureformat") && type == TypeDesc::STRING) { TIFFSetField (m_tif, TIFFTAG_PIXAR_TEXTUREFORMAT, *(char**)data); return true; } if (Strutil::iequals(name, "wrapmodes") && type == TypeDesc::STRING) { TIFFSetField (m_tif, TIFFTAG_PIXAR_WRAPMODES, *(char**)data); return true; } if (Strutil::iequals(name, "worldtocamera") && type == TypeDesc::TypeMatrix) { TIFFSetField (m_tif, TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA, data); return true; } if (Strutil::iequals(name, "worldtoscreen") && type == TypeDesc::TypeMatrix) { TIFFSetField (m_tif, TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN, data); return true; } if (Strutil::iequals(name, "XResolution") && type == TypeDesc::FLOAT) { TIFFSetField (m_tif, TIFFTAG_XRESOLUTION, *(float *)data); return true; } if (Strutil::iequals(name, "YResolution") && type == TypeDesc::FLOAT) { TIFFSetField (m_tif, TIFFTAG_YRESOLUTION, *(float *)data); return true; } return false; } bool TIFFOutput::write_exif_data () { #if defined(TIFF_VERSION_BIG) && TIFFLIB_VERSION >= 20120922 // Older versions of libtiff do not support writing Exif directories if (m_spec.get_int_attribute ("tiff:write_exif", 1) == 0) { // The special metadata "tiff:write_exif", if present and set to 0 // (the default is 1), will cause us to skip outputting Exif data. // This is useful in cases where we think the TIFF file will need to // be read by an app that links against an old version of libtiff // that will have trouble reading the Exif directory. return true; } // First, see if we have any Exif data at all bool any_exif = false; for (size_t i = 0, e = m_spec.extra_attribs.size(); i < e; ++i) { const ImageIOParameter &p (m_spec.extra_attribs[i]); int tag, tifftype, count; if (exif_tag_lookup (p.name(), tag, tifftype, count) && tifftype != TIFF_NOTYPE) { if (tag == EXIFTAG_SECURITYCLASSIFICATION || tag == EXIFTAG_IMAGEHISTORY || tag == EXIFTAG_ISOSPEEDRATINGS) continue; // libtiff doesn't understand these any_exif = true; break; } } if (! any_exif) return true; if (m_compression == COMPRESSION_JPEG) { // For reasons we don't understand, JPEG-compressed TIFF seems // to not output properly without a directory checkpoint here. TIFFCheckpointDirectory (m_tif); } // First, finish writing the current directory if (! TIFFWriteDirectory (m_tif)) { error ("failed TIFFWriteDirectory()"); return false; } // Create an Exif directory if (TIFFCreateEXIFDirectory (m_tif) != 0) { error ("failed TIFFCreateEXIFDirectory()"); return false; } for (size_t i = 0, e = m_spec.extra_attribs.size(); i < e; ++i) { const ImageIOParameter &p (m_spec.extra_attribs[i]); int tag, tifftype, count; if (exif_tag_lookup (p.name(), tag, tifftype, count) && tifftype != TIFF_NOTYPE) { if (tag == EXIFTAG_SECURITYCLASSIFICATION || tag == EXIFTAG_IMAGEHISTORY || tag == EXIFTAG_ISOSPEEDRATINGS) continue; // libtiff doesn't understand these bool ok = false; if (tifftype == TIFF_ASCII) { ok = TIFFSetField (m_tif, tag, *(char**)p.data()); } else if ((tifftype == TIFF_SHORT || tifftype == TIFF_LONG) && p.type() == TypeDesc::SHORT) { ok = TIFFSetField (m_tif, tag, (int)*(short *)p.data()); } else if ((tifftype == TIFF_SHORT || tifftype == TIFF_LONG) && p.type() == TypeDesc::INT) { ok = TIFFSetField (m_tif, tag, *(int *)p.data()); } else if ((tifftype == TIFF_RATIONAL || tifftype == TIFF_SRATIONAL) && p.type() == TypeDesc::FLOAT) { ok = TIFFSetField (m_tif, tag, *(float *)p.data()); } else if ((tifftype == TIFF_RATIONAL || tifftype == TIFF_SRATIONAL) && p.type() == TypeDesc::DOUBLE) { ok = TIFFSetField (m_tif, tag, *(double *)p.data()); } if (! ok) { // std::cout << "Unhandled EXIF " << p.name() << " " << p.type() << "\n"; } } } // Now write the directory of Exif data uint64 dir_offset = 0; if (! TIFFWriteCustomDirectory (m_tif, &dir_offset)) { error ("failed TIFFWriteCustomDirectory() of the Exif data"); return false; } // Go back to the first directory, and add the EXIFIFD pointer. // std::cout << "diffdir = " << tiffdir << "\n"; TIFFSetDirectory (m_tif, 0); TIFFSetField (m_tif, TIFFTAG_EXIFIFD, dir_offset); #endif return true; // all is ok } bool TIFFOutput::close () { if (m_tif) { write_exif_data (); TIFFClose (m_tif); // N.B. TIFFClose doesn't return a status code } init (); // re-initialize return true; // How can we fail? } /// Helper: Convert n pixels from contiguous (RGBRGBRGB) to separate /// (RRRGGGBBB) planarconfig. void TIFFOutput::contig_to_separate (int n, const char *contig, char *separate) { int channelbytes = m_spec.channel_bytes(); for (int p = 0; p < n; ++p) // loop over pixels for (int c = 0; c < m_spec.nchannels; ++c) // loop over channels for (int i = 0; i < channelbytes; ++i) // loop over data bytes separate[(c*n+p)*channelbytes+i] = contig[(p*m_spec.nchannels+c)*channelbytes+i]; } // Convert T data[0..nvals-1] in-place to BITS_TO per sample, *packed*. // The bit width of T is definitely wider than BITS_TO. T should be an // unsigned type. template static void convert_pack_bits (T *data, int nvals) { const int BITS_FROM = sizeof(T)*8; T *in = data; T *out = in - 1; // because we'll increment first time through int bitstofill = 0; // Invariant: the next value to convert is *in. We're going to write // the result of the conversion starting at *out, which still has // bitstofill bits left before moving on to the next slot. for (int i = 0; i < nvals; ++i) { // Grab the next value and convert it T val = bit_range_convert (*in++); // If we have no more bits to fill in the slot, move on to the // next slot. if (bitstofill == 0) { ++out; *out = 0; // move to next slot and clear its bits bitstofill = BITS_FROM; // all bits are for the taking } if (bitstofill >= BITS_TO) { // we can fit the whole val in this slot *out |= val << (bitstofill - BITS_TO); bitstofill -= BITS_TO; // printf ("\t\t%d bits left\n", bitstofill); } else { // not enough bits -- will need to split across slots int bitsinnext = BITS_TO - bitstofill; *out |= val >> bitsinnext; val &= (1 << bitsinnext) - 1; // mask out bits we saved ++out; *out = 0; // move to next slot and clear its bits *out |= val << (BITS_FROM - bitsinnext); bitstofill = BITS_FROM - bitsinnext; } } // Because we filled in a big-endian way, swap bytes if we need to if (littleendian()) swap_endian (data, nvals); } template static void rgb_to_cmyk (int n, const T *rgb, size_t rgb_stride, T *cmyk, size_t cmyk_stride) { for ( ; n; --n, cmyk += cmyk_stride, rgb += rgb_stride) { float R = convert_type(rgb[0]); float G = convert_type(rgb[1]); float B = convert_type(rgb[2]); float one_minus_K = std::max (R, std::max(G, B)); float one_minus_K_inv = (one_minus_K <= 1e-6) ? 0.0f : 1.0f/one_minus_K; float C = (one_minus_K - R) * one_minus_K_inv; float M = (one_minus_K - G) * one_minus_K_inv; float Y = (one_minus_K - B) * one_minus_K_inv; float K = 1.0f - one_minus_K; cmyk[0] = convert_type(C); cmyk[1] = convert_type(M); cmyk[2] = convert_type(Y); cmyk[3] = convert_type(K); } } void* TIFFOutput::convert_to_cmyk (int npixels, const void* data) { std::vector cmyk (m_outputchans * spec().format.size() * npixels); if (spec().format == TypeDesc::UINT8) { rgb_to_cmyk (npixels, (unsigned char *)data, m_spec.nchannels, (unsigned char *)&cmyk[0], m_outputchans); } else if (spec().format == TypeDesc::UINT16) { rgb_to_cmyk (npixels, (unsigned short *)data, m_spec.nchannels, (unsigned short *)&cmyk[0], m_outputchans); } else { ASSERT (0 && "CMYK should be forced to UINT8 or UINT16"); } m_scratch.swap (cmyk); return &m_scratch[0]; } bool TIFFOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { m_spec.auto_stride (xstride, format, spec().nchannels); const void *origdata = data; data = to_native_scanline (format, data, xstride, m_scratch, m_dither, y, z); // Handle weird photometric/color spaces if (m_photometric == PHOTOMETRIC_SEPARATED && m_convert_rgb_to_cmyk) data = convert_to_cmyk (spec().width, data); // Handle weird bit depths if (spec().format.size()*8 != m_bitspersample) { // Move to scratch area if not already there imagesize_t nbytes = spec().scanline_bytes(); int nvals = spec().width * m_outputchans; if (data == origdata) { m_scratch.assign ((unsigned char *)data, (unsigned char *)data+nbytes); data = &m_scratch[0]; } if (spec().format == TypeDesc::UINT16 && m_bitspersample == 10) { convert_pack_bits ((unsigned short *)data, nvals); } else if (spec().format == TypeDesc::UINT16 && m_bitspersample == 12) { convert_pack_bits ((unsigned short *)data, nvals); } else if (spec().format == TypeDesc::UINT8 && m_bitspersample == 4) { convert_pack_bits ((unsigned char *)data, nvals); } else if (spec().format == TypeDesc::UINT8 && m_bitspersample == 2) { convert_pack_bits ((unsigned char *)data, nvals); } else { ASSERT (0 && "unsupported bit conversion -- shouldn't reach here"); } } y -= m_spec.y; if (m_planarconfig == PLANARCONFIG_SEPARATE) { // Convert from contiguous (RGBRGBRGB) to separate (RRRGGGBBB) int plane_bytes = m_spec.width * m_spec.format.size(); std::vector scratch2 (m_spec.scanline_bytes()); std::swap (m_scratch, scratch2); m_scratch.resize (m_spec.scanline_bytes()); contig_to_separate (m_spec.width, (const char *)data, (char *)&m_scratch[0]); for (int c = 0; c < m_spec.nchannels; ++c) { if (TIFFWriteScanline (m_tif, (tdata_t)&m_scratch[plane_bytes*c], y, c) < 0) { std::string err = oiio_tiff_last_error(); error ("TIFFWriteScanline failed writing line y=%d,z=%d (%s)", y, z, err.size() ? err.c_str() : "unknown error"); return false; } } } else { // No contig->separate is necessary. But we still use scratch // space since TIFFWriteScanline is destructive when // TIFFTAG_PREDICTOR is used. if (data == origdata) { m_scratch.assign ((unsigned char *)data, (unsigned char *)data+m_spec.scanline_bytes()); data = &m_scratch[0]; } if (TIFFWriteScanline (m_tif, (tdata_t)data, y) < 0) { std::string err = oiio_tiff_last_error(); error ("TIFFWriteScanline failed writing line y=%d,z=%d (%s)", y, z, err.size() ? err.c_str() : "unknown error"); return false; } } // Should we checkpoint? Only if we have enough scanlines and enough // time has passed (or if using JPEG compression, for which it seems // necessary). ++m_checkpointItems; if ((m_checkpointTimer() > DEFAULT_CHECKPOINT_INTERVAL_SECONDS || m_compression == COMPRESSION_JPEG) && m_checkpointItems >= MIN_SCANLINES_OR_TILES_PER_CHECKPOINT) { TIFFCheckpointDirectory (m_tif); m_checkpointTimer.lap(); m_checkpointItems = 0; } return true; } bool TIFFOutput::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { if (! m_spec.valid_tile_range (x, x, y, y, z, z)) return false; m_spec.auto_stride (xstride, ystride, zstride, format, spec().nchannels, spec().tile_width, spec().tile_height); x -= m_spec.x; // Account for offset, so x,y are file relative, not y -= m_spec.y; // image relative z -= m_spec.z; const void *origdata = data; // Stash original pointer data = to_native_tile (format, data, xstride, ystride, zstride, m_scratch, m_dither, x, y, z); // Handle weird photometric/color spaces if (m_photometric == PHOTOMETRIC_SEPARATED && m_convert_rgb_to_cmyk) data = convert_to_cmyk (spec().tile_pixels(), data); // Handle weird bit depths if (spec().format.size()*8 != m_bitspersample) { // Move to scratch area if not already there imagesize_t nbytes = spec().scanline_bytes(); int nvals = int (spec().tile_pixels()) * spec().nchannels; if (data == origdata) { m_scratch.assign ((unsigned char *)data, (unsigned char *)data+nbytes); data = &m_scratch[0]; } if (spec().format == TypeDesc::UINT16 && m_bitspersample == 10) { convert_pack_bits ((unsigned short *)data, nvals); } else if (spec().format == TypeDesc::UINT16 && m_bitspersample == 12) { convert_pack_bits ((unsigned short *)data, nvals); } else if (spec().format == TypeDesc::UINT8 && m_bitspersample == 4) { convert_pack_bits ((unsigned char *)data, nvals); } else if (spec().format == TypeDesc::UINT8 && m_bitspersample == 2) { convert_pack_bits ((unsigned char *)data, nvals); } else { ASSERT (0 && "unsupported bit conversion -- shouldn't reach here"); } } if (m_planarconfig == PLANARCONFIG_SEPARATE && m_spec.nchannels > 1) { // Convert from contiguous (RGBRGBRGB) to separate (RRRGGGBBB) imagesize_t tile_pixels = m_spec.tile_pixels(); imagesize_t plane_bytes = tile_pixels * m_spec.format.size(); DASSERT (plane_bytes*m_spec.nchannels == m_spec.tile_bytes()); boost::scoped_array separate_heap; char *separate = NULL; imagesize_t separate_size = plane_bytes * m_spec.nchannels; if (separate_size <= (1<<16)) separate = ALLOCA (char, separate_size); // <=64k ? stack else { // >64k ? heap separate_heap.reset (new char [separate_size]); // will auto-free separate = separate_heap.get(); } contig_to_separate (tile_pixels, (const char *)data, separate); for (int c = 0; c < m_spec.nchannels; ++c) { if (TIFFWriteTile (m_tif, (tdata_t)&separate[plane_bytes*c], x, y, z, c) < 0) { std::string err = oiio_tiff_last_error(); error ("TIFFWriteTile failed writing tile x=%d,y=%d,z=%d (%s)", x+m_spec.x, y+m_spec.y, z+m_spec.z, err.size() ? err.c_str() : "unknown error"); return false; } } } else { // No contig->separate is necessary. But we still use scratch // space since TIFFWriteTile is destructive when // TIFFTAG_PREDICTOR is used. if (data == origdata) { m_scratch.assign ((unsigned char *)data, (unsigned char *)data + m_spec.tile_bytes()); data = &m_scratch[0]; } if (TIFFWriteTile (m_tif, (tdata_t)data, x, y, z, 0) < 0) { std::string err = oiio_tiff_last_error(); error ("TIFFWriteTile failed writing tile x=%d,y=%d,z=%d (%s)", x+m_spec.x, y+m_spec.y, z+m_spec.z, err.size() ? err.c_str() : "unknown error"); return false; } } // Should we checkpoint? Only if we have enough tiles and enough // time has passed (or if using JPEG compression, for which it seems // necessary). ++m_checkpointItems; if ((m_checkpointTimer() > DEFAULT_CHECKPOINT_INTERVAL_SECONDS || m_compression == COMPRESSION_JPEG) && m_checkpointItems >= MIN_SCANLINES_OR_TILES_PER_CHECKPOINT) { TIFFCheckpointDirectory (m_tif); m_checkpointTimer.lap(); m_checkpointItems = 0; } return true; } bool TIFFOutput::source_is_cmyk (const ImageSpec &spec) { if (spec.nchannels != 4) { return false; // Can't be CMYK if it's not 4 channels } if (Strutil::iequals(spec.channelnames[0], "C") && Strutil::iequals(spec.channelnames[1], "M") && Strutil::iequals(spec.channelnames[2], "Y") && Strutil::iequals(spec.channelnames[3], "K")) return true; if (Strutil::iequals(spec.channelnames[0], "Cyan") && Strutil::iequals(spec.channelnames[1], "Magenta") && Strutil::iequals(spec.channelnames[2], "Yellow") && Strutil::iequals(spec.channelnames[3], "Black")) return true; string_view oiiocs = spec.get_string_attribute("oiio:ColorSpace"); if (Strutil::iequals (oiiocs, "CMYK")) return true; return false; } bool TIFFOutput::source_is_rgb (const ImageSpec &spec) { string_view oiiocs = spec.get_string_attribute("oiio:ColorSpace"); if (Strutil::iequals (oiiocs, "CMYK") || Strutil::iequals (oiiocs, "color separated")) return false; // It's a color space mode that means something else if (spec.nchannels != 3) return false; // Can't be RGB if it's not 3 channels if (Strutil::iequals(spec.channelnames[0], "R") && Strutil::iequals(spec.channelnames[1], "G") && Strutil::iequals(spec.channelnames[2], "B")) return true; if (Strutil::iequals(spec.channelnames[0], "Red") && Strutil::iequals(spec.channelnames[1], "Green") && Strutil::iequals(spec.channelnames[2], "Blue")) return true; return false; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/tiff.imageio/tiffinput.cpp0000644000175000017500000020327113151711064022176 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" OIIO_PLUGIN_NAMESPACE_BEGIN // General TIFF information: // TIFF 6.0 spec: // http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf // Other Adobe TIFF docs: // http://partners.adobe.com/public/developer/tiff/index.html // Adobe extensions to allow 16 (and 24) bit float in TIFF (ugh, not on // their developer page, only on Chris Cox's web site?): // http://chriscox.org/TIFFTN3d1.pdf // Libtiff: // http://remotesensing.org/libtiff/ // Helper struct for constructing tables of TIFF tags struct TIFF_tag_info { int tifftag; // TIFF tag used for this info const char *name; // Attribute name we use, or NULL to ignore the tag TIFFDataType tifftype; // Data type that TIFF wants }; // Note about MIP-maps versus subimages: // // TIFF files support subimages, but do not explicitly support // multiresolution/MIP maps. So we have always used subimages to store // MIP levels. // // At present, TIFF is the only format people use for multires textures // that don't explicitly support it, so rather than make the // TextureSystem have to handle both cases, we choose instead to emulate // MIP with subimage in a way that's purely within the TIFFInput class. // To the outside world, it really does look MIP-mapped. This only // kicks in for TIFF files that have the "textureformat" metadata set. // // The internal m_subimage really does contain the subimage, but for the // MIP emulation case, we report the subimage as the MIP level, and 0 as // the subimage. It is indeed a tangled web of deceit we weave. class TIFFInput : public ImageInput { public: TIFFInput (); virtual ~TIFFInput (); virtual const char * format_name (void) const { return "tiff"; } virtual bool valid_file (const std::string &filename) const; virtual int supports (string_view feature) const { return (feature == "exif" || feature == "iptc"); // N.B. No support for arbitrary metadata. } virtual bool open (const std::string &name, ImageSpec &newspec); virtual bool open (const std::string &name, ImageSpec &newspec, const ImageSpec &config); virtual bool close (); virtual int current_subimage (void) const { // If m_emulate_mipmap is true, pretend subimages are mipmap levels return m_emulate_mipmap ? 0 : m_subimage; } virtual int current_miplevel (void) const { // If m_emulate_mipmap is true, pretend subimages are mipmap levels return m_emulate_mipmap ? m_subimage : 0; } virtual bool seek_subimage (int subimage, int miplevel, ImageSpec &newspec); virtual bool read_native_scanline (int y, int z, void *data); virtual bool read_native_tile (int x, int y, int z, void *data); virtual bool read_scanline (int y, int z, TypeDesc format, void *data, stride_t xstride); virtual bool read_scanlines (int ybegin, int yend, int z, int chbegin, int chend, TypeDesc format, void *data, stride_t xstride, stride_t ystride); virtual bool read_tile (int x, int y, int z, TypeDesc format, void *data, stride_t xstride, stride_t ystride, stride_t zstride); virtual bool read_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc format, void *data, stride_t xstride, stride_t ystride, stride_t zstride); private: TIFF *m_tif; ///< libtiff handle std::string m_filename; ///< Stash the filename std::vector m_scratch; ///< Scratch space for us to use std::vector m_scratch2; ///< More scratch int m_subimage; ///< What subimage are we looking at? int m_next_scanline; ///< Next scanline we'll read bool m_no_random_access; ///< Should we avoid random access? bool m_emulate_mipmap; ///< Should we emulate mip with subimage? bool m_keep_unassociated_alpha; ///< If the image is unassociated, please ///< try to keep it that way! bool m_raw_color; ///< If the image is not RGB, don't ///< transform the color. bool m_convert_alpha; ///< Do we need to associate alpha? bool m_separate; ///< Separate planarconfig? bool m_testopenconfig; ///< Debug aid to test open-with-config bool m_use_rgba_interface; ///< Sometimes we punt unsigned short m_planarconfig; ///< Planar config of the file unsigned short m_bitspersample; ///< Of the *file*, not the client's view unsigned short m_photometric; ///< Of the *file*, not the client's view unsigned short m_compression; ///< TIFF compression tag unsigned short m_inputchannels; ///< Channels in the file (careful with CMYK) std::vector m_colormap; ///< Color map for palette images std::vector m_rgbadata; ///< Sometimes we punt // Reset everything to initial state void init () { m_tif = NULL; m_subimage = -1; m_emulate_mipmap = false; m_keep_unassociated_alpha = false; m_raw_color = false; m_convert_alpha = false; m_separate = false; m_inputchannels = 0; m_testopenconfig = false; m_colormap.clear(); m_use_rgba_interface = false; } void close_tif () { if (m_tif) { TIFFClose (m_tif); m_tif = NULL; if (m_rgbadata.size()) std::vector().swap(m_rgbadata); // release } } // Read tags from the current directory of m_tif and fill out spec. // If read_meta is false, assume that m_spec already contains valid // metadata and should not be cleared or rewritten. void readspec (bool read_meta=true); // Figure out all the photometric-related aspects of the header void readspec_photometric (); // Convert planar separate to contiguous data format void separate_to_contig (int nplanes, int nvals, const unsigned char *separate, unsigned char *contig); // Convert palette to RGB void palette_to_rgb (int n, const unsigned char *palettepels, unsigned char *rgb); // Convert in-bits to out-bits (outbits must be 8, 16, 32, and // inbits < outbits) void bit_convert (int n, const unsigned char *in, int inbits, void *out, int outbits); void invert_photometric (int n, void *data); // Calling TIFFGetField (tif, tag, &dest) is supposed to work fine for // simple types... as long as the tag types in the file are the correct // advertised types. But for some types -- which we never expect, but // it turns out can sometimes happen, TIFFGetField will try to pull // a second argument (a void**) off the stack, and that can crash the // program! Ick. So to avoid this, we always push a pointer, which // we expect NOT to be altered, and if it is, it's a danger sign (plus // we didn't crash). bool safe_tiffgetfield (const std::string &name, int tag, void *dest) { void *ptr = NULL; // dummy -- expect it to stay NULL bool ok = TIFFGetField (m_tif, tag, dest, &ptr); if (ptr) { #ifndef NDEBUG std::cerr << "Error safe_tiffgetfield : did not expect ptr set on " << name << " " << (void *)ptr << "\n"; #endif return false; } return ok; } // Get a string tiff tag field and put it into extra_params void get_string_attribute (const std::string &name, int tag) { char *s = NULL; void *ptr = NULL; // dummy -- expect it to stay NULL bool ok = TIFFGetField (m_tif, tag, &s, &ptr); if (ok && ptr) { // Oy, some tags need 2 args, which are count, then ptr. // There's no way to know ahead of time which ones, so we send // a second pointer. If it gets overwritten, then we understand // and try it again with 2 args, first one is count. unsigned short count; ok = TIFFGetField (m_tif, tag, &count, &s); m_spec.attribute (name, string_view(s,count)); } else if (ok && s && *s) m_spec.attribute (name, s); } // Get a matrix tiff tag field and put it into extra_params void get_matrix_attribute (const std::string &name, int tag) { float *f = NULL; if (safe_tiffgetfield (name, tag, &f) && f) m_spec.attribute (name, TypeDesc::TypeMatrix, f); } // Get a float tiff tag field and put it into extra_params void get_float_attribute (const std::string &name, int tag) { float f[16]; if (safe_tiffgetfield (name, tag, f)) m_spec.attribute (name, f[0]); } // Get an int tiff tag field and put it into extra_params void get_int_attribute (const std::string &name, int tag) { int i; if (safe_tiffgetfield (name, tag, &i)) m_spec.attribute (name, i); } // Get an int tiff tag field and put it into extra_params void get_short_attribute (const std::string &name, int tag) { // Make room for two shorts, in case the tag is not the type we // expect, and libtiff writes a long instead. unsigned short s[2] = {0,0}; if (safe_tiffgetfield (name, tag, &s)) { int i = s[0]; m_spec.attribute (name, i); } } // Search for TIFF tag 'tagid' having type 'tifftype', and if found, // add it in the obvious way to m_spec under the name 'oiioname'. void find_tag (int tifftag, TIFFDataType tifftype, const char *oiioname) { #ifdef TIFF_VERSION_BIG const TIFFField *info = TIFFFindField (m_tif, tifftag, tifftype); #else const TIFFFieldInfo *info = TIFFFindFieldInfo (m_tif, tifftag, tifftype); #endif if (! info) { // Something has gone wrong, libtiff doesn't think the field type // is the same as we do. return; } if (tifftype == TIFF_ASCII) get_string_attribute (oiioname, tifftag); else if (tifftype == TIFF_SHORT) get_short_attribute (oiioname, tifftag); else if (tifftype == TIFF_LONG) get_int_attribute (oiioname, tifftag); else if (tifftype == TIFF_RATIONAL || tifftype == TIFF_SRATIONAL || tifftype == TIFF_FLOAT || tifftype == TIFF_DOUBLE) get_float_attribute (oiioname, tifftag); } }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageInput *tiff_input_imageio_create () { return new TIFFInput; } // OIIO_EXPORT int tiff_imageio_version = OIIO_PLUGIN_VERSION; // it's in tiffoutput.cpp OIIO_EXPORT const char * tiff_input_extensions[] = { "tiff", "tif", "tx", "env", "sm", "vsm", NULL }; OIIO_PLUGIN_EXPORTS_END // Someplace to store an error message from the TIFF error handler // To avoid thread oddities, we have the storage area buffering error // messages for seterror()/geterror() be thread-specific. static boost::thread_specific_ptr thread_error_msg; static atomic_int handler_set; static spin_mutex handler_mutex; std::string & oiio_tiff_last_error () { std::string *e = thread_error_msg.get(); if (! e) { e = new std::string; thread_error_msg.reset (e); } return *e; } static void my_error_handler (const char *str, const char *format, va_list ap) { oiio_tiff_last_error() = Strutil::vformat (format, ap); } void oiio_tiff_set_error_handler () { if (! handler_set) { spin_lock lock (handler_mutex); if (! handler_set) { TIFFSetErrorHandler (my_error_handler); TIFFSetWarningHandler (my_error_handler); handler_set = 1; } } } struct CompressionCode { int code; const char *name; }; static CompressionCode tiff_compressions[] = { { COMPRESSION_NONE, "none" }, // no compression { COMPRESSION_LZW, "lzw" }, // LZW { COMPRESSION_ADOBE_DEFLATE, "zip" }, // deflate / zip { COMPRESSION_DEFLATE, "zip" }, // deflate / zip { COMPRESSION_CCITTRLE, "ccittrle" }, // CCITT RLE { COMPRESSION_CCITTFAX3, "ccittfax3" }, // CCITT group 3 fax { COMPRESSION_CCITT_T4, "ccitt_t4" }, // CCITT T.4 { COMPRESSION_CCITTFAX4, "ccittfax4" }, // CCITT group 4 fax { COMPRESSION_CCITT_T6, "ccitt_t6" }, // CCITT T.6 { COMPRESSION_OJPEG, "ojpeg" }, // old (pre-TIFF6.0) JPEG { COMPRESSION_JPEG, "jpeg" }, // JPEG { COMPRESSION_NEXT, "next" }, // NeXT 2-bit RLE { COMPRESSION_CCITTRLEW, "ccittrle2" }, // #1 w/ word alignment { COMPRESSION_PACKBITS, "packbits" }, // Macintosh RLE { COMPRESSION_THUNDERSCAN, "thunderscan" }, // ThundeScan RLE { COMPRESSION_IT8CTPAD, "IT8CTPAD" }, // IT8 CT w/ patting { COMPRESSION_IT8LW, "IT8LW" }, // IT8 linework RLE { COMPRESSION_IT8MP, "IT8MP" }, // IT8 monochrome picture { COMPRESSION_IT8BL, "IT8BL" }, // IT8 binary line art { COMPRESSION_PIXARFILM, "pixarfilm" }, // Pixar 10 bit LZW { COMPRESSION_PIXARLOG, "pixarlog" }, // Pixar 11 bit ZIP { COMPRESSION_DCS, "dcs" }, // Kodak DCS encoding { COMPRESSION_JBIG, "isojbig" }, // ISO JBIG { COMPRESSION_SGILOG, "sgilog" }, // SGI log luminance RLE { COMPRESSION_SGILOG24, "sgilog24" }, // SGI log 24bit { COMPRESSION_JP2000, "jp2000" }, // Leadtools JPEG2000 #if defined(TIFF_VERSION_BIG) && TIFFLIB_VERSION >= 20120922 // Others supported in more recent TIFF library versions. { COMPRESSION_T85, "T85" }, // TIFF/FX T.85 JBIG { COMPRESSION_T43, "T43" }, // TIFF/FX T.43 color layered JBIG { COMPRESSION_LZMA, "lzma" }, // LZMA2 #endif { -1, NULL } }; static const char * tiff_compression_name (int code) { for (int i = 0; tiff_compressions[i].name; ++i) if (code == tiff_compressions[i].code) return tiff_compressions[i].name; return NULL; } TIFFInput::TIFFInput () { oiio_tiff_set_error_handler (); init (); } TIFFInput::~TIFFInput () { // Close, if not already done. close (); } bool TIFFInput::valid_file (const std::string &filename) const { FILE *file = Filesystem::fopen (filename, "r"); if (! file) return false; // needs to be able to open unsigned short magic[2] = { 0, 0 }; size_t numRead = fread (magic, sizeof(unsigned short), 2, file); fclose (file); if (numRead != 2) // fread failed return false; if (magic[0] != TIFF_LITTLEENDIAN && magic[0] != TIFF_BIGENDIAN) return false; // not the right byte order if ((magic[0] == TIFF_LITTLEENDIAN) != littleendian()) swap_endian (&magic[1], 1); return (magic[1] == 42 /* Classic TIFF */ || magic[1] == 43 /* Big TIFF */); } bool TIFFInput::open (const std::string &name, ImageSpec &newspec) { oiio_tiff_set_error_handler (); m_filename = name; m_subimage = -1; return seek_subimage (0, 0, newspec); } bool TIFFInput::open (const std::string &name, ImageSpec &newspec, const ImageSpec &config) { // Check 'config' for any special requests if (config.get_int_attribute("oiio:UnassociatedAlpha", 0) == 1) m_keep_unassociated_alpha = true; if (config.get_int_attribute("oiio:RawColor", 0) == 1) m_raw_color = true; // This configuration hint has no function other than as a debugging aid // for testing whether configurations are received properly from other // OIIO components. if (config.get_int_attribute("oiio:DebugOpenConfig!", 0)) m_testopenconfig = true; return open (name, newspec); } bool TIFFInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec) { if (subimage < 0) // Illegal return false; if (m_emulate_mipmap) { // Emulating MIPmap? Pretend one subimage, many MIP levels. if (subimage != 0) return false; subimage = miplevel; } else { // No MIPmap emulation if (miplevel != 0) return false; } if (subimage == m_subimage) { // We're already pointing to the right subimage newspec = m_spec; return true; } // If we're emulating a MIPmap, only resolution is allowed to change // between MIP levels, so if we already have a valid level in m_spec, // we don't need to re-parse metadata, it's guaranteed to be the same. bool read_meta = !(m_emulate_mipmap && m_tif && m_subimage >= 0); if (! m_tif) { #ifdef _WIN32 std::wstring wfilename = Strutil::utf8_to_utf16 (m_filename); m_tif = TIFFOpenW (wfilename.c_str(), "rm"); #else m_tif = TIFFOpen (m_filename.c_str(), "rm"); #endif if (m_tif == NULL) { std::string e = oiio_tiff_last_error(); error ("Could not open file: %s", e.length() ? e : m_filename); return false; } m_subimage = 0; } m_next_scanline = 0; // next scanline we'll read if (TIFFSetDirectory (m_tif, subimage)) { m_subimage = subimage; readspec (read_meta); // OK, some edge cases we just don't handle. For those, fall back on // the TIFFRGBA interface. bool is_jpeg = (m_compression == COMPRESSION_JPEG || m_compression == COMPRESSION_OJPEG); bool is_nonspectral = (m_photometric == PHOTOMETRIC_YCBCR || m_photometric == PHOTOMETRIC_CIELAB || m_photometric == PHOTOMETRIC_ICCLAB || m_photometric == PHOTOMETRIC_ITULAB || m_photometric == PHOTOMETRIC_LOGL || m_photometric == PHOTOMETRIC_LOGLUV); if (is_jpeg || (is_nonspectral && ! m_raw_color)) { char emsg[1024]; m_use_rgba_interface = true; if (! TIFFRGBAImageOK (m_tif, emsg)) { error ("No support for this flavor of TIFF file"); return false; } // This falls back to looking like uint8 images m_spec.format = TypeDesc::UINT8; m_spec.channelformats.clear (); m_photometric = PHOTOMETRIC_RGB; } newspec = m_spec; if (newspec.format == TypeDesc::UNKNOWN) { error ("No support for data format of \"%s\"", m_filename.c_str()); return false; } return true; } else { std::string e = oiio_tiff_last_error(); error ("%s", e.length() ? e : m_filename); m_subimage = -1; return false; } } // Tags we can handle in a totally automated fasion, just copying // straight to an ImageSpec. static const TIFF_tag_info tiff_tag_table[] = { { TIFFTAG_IMAGEDESCRIPTION, "ImageDescription", TIFF_ASCII }, { TIFFTAG_ORIENTATION, "Orientation", TIFF_SHORT }, { TIFFTAG_XRESOLUTION, "XResolution", TIFF_RATIONAL }, { TIFFTAG_YRESOLUTION, "YResolution", TIFF_RATIONAL }, { TIFFTAG_RESOLUTIONUNIT, "ResolutionUnit",TIFF_SHORT }, { TIFFTAG_MAKE, "Make", TIFF_ASCII }, { TIFFTAG_MODEL, "Model", TIFF_ASCII }, { TIFFTAG_SOFTWARE, "Software", TIFF_ASCII }, { TIFFTAG_ARTIST, "Artist", TIFF_ASCII }, { TIFFTAG_COPYRIGHT, "Copyright", TIFF_ASCII }, { TIFFTAG_DATETIME, "DateTime", TIFF_ASCII }, { TIFFTAG_DOCUMENTNAME, "DocumentName", TIFF_ASCII }, { TIFFTAG_PAGENAME, "tiff:PageName", TIFF_ASCII }, { TIFFTAG_PAGENUMBER, "tiff:PageNumber", TIFF_SHORT }, { TIFFTAG_HOSTCOMPUTER, "HostComputer", TIFF_ASCII }, { TIFFTAG_PIXAR_TEXTUREFORMAT, "textureformat", TIFF_ASCII }, { TIFFTAG_PIXAR_WRAPMODES, "wrapmodes", TIFF_ASCII }, { TIFFTAG_PIXAR_FOVCOT, "fovcot", TIFF_FLOAT }, { TIFFTAG_JPEGQUALITY, "CompressionQuality", TIFF_LONG }, { TIFFTAG_ZIPQUALITY, "tiff:zipquality", TIFF_LONG }, { 0, NULL, TIFF_NOTYPE } }; // Tags we may come across in the EXIF directory. static const TIFF_tag_info exif_tag_table[] = { { EXIFTAG_EXPOSURETIME, "ExposureTime", TIFF_RATIONAL }, { EXIFTAG_FNUMBER, "FNumber", TIFF_RATIONAL }, { EXIFTAG_EXPOSUREPROGRAM, "Exif:ExposureProgram", TIFF_SHORT }, // ?? translate to ascii names? { EXIFTAG_SPECTRALSENSITIVITY, "Exif:SpectralSensitivity", TIFF_ASCII }, { EXIFTAG_ISOSPEEDRATINGS, "Exif:ISOSpeedRatings", TIFF_SHORT }, { EXIFTAG_OECF, "Exif:OECF", TIFF_NOTYPE }, // skip it { EXIFTAG_EXIFVERSION, "Exif:ExifVersion", TIFF_NOTYPE }, // skip it { EXIFTAG_DATETIMEORIGINAL, "Exif:DateTimeOriginal", TIFF_ASCII }, { EXIFTAG_DATETIMEDIGITIZED,"Exif:DateTimeDigitized", TIFF_ASCII }, { EXIFTAG_COMPONENTSCONFIGURATION, "Exif:ComponentsConfiguration", TIFF_UNDEFINED }, { EXIFTAG_COMPRESSEDBITSPERPIXEL, "Exif:CompressedBitsPerPixel", TIFF_RATIONAL }, { EXIFTAG_SHUTTERSPEEDVALUE,"Exif:ShutterSpeedValue", TIFF_SRATIONAL }, // APEX units { EXIFTAG_APERTUREVALUE, "Exif:ApertureValue", TIFF_RATIONAL }, // APEX units { EXIFTAG_BRIGHTNESSVALUE, "Exif:BrightnessValue", TIFF_SRATIONAL }, { EXIFTAG_EXPOSUREBIASVALUE,"Exif:ExposureBiasValue", TIFF_SRATIONAL }, { EXIFTAG_MAXAPERTUREVALUE, "Exif:MaxApertureValue",TIFF_RATIONAL }, { EXIFTAG_SUBJECTDISTANCE, "Exif:SubjectDistance", TIFF_RATIONAL }, { EXIFTAG_METERINGMODE, "Exif:MeteringMode", TIFF_SHORT }, { EXIFTAG_LIGHTSOURCE, "Exif:LightSource", TIFF_SHORT }, { EXIFTAG_FLASH, "Exif:Flash", TIFF_SHORT }, { EXIFTAG_FOCALLENGTH, "Exif:FocalLength", TIFF_RATIONAL }, // mm { EXIFTAG_SUBJECTAREA, "Exif:SubjectArea", TIFF_NOTYPE }, // skip { EXIFTAG_MAKERNOTE, "Exif:MakerNote", TIFF_NOTYPE }, // skip it { EXIFTAG_USERCOMMENT, "Exif:UserComment", TIFF_NOTYPE }, // skip it { EXIFTAG_SUBSECTIME, "Exif:SubsecTime", TIFF_ASCII }, { EXIFTAG_SUBSECTIMEORIGINAL,"Exif:SubsecTimeOriginal", TIFF_ASCII }, { EXIFTAG_SUBSECTIMEDIGITIZED,"Exif:SubsecTimeDigitized", TIFF_ASCII }, { EXIFTAG_FLASHPIXVERSION, "Exif:FlashPixVersion", TIFF_NOTYPE }, // skip "Exif:FlashPixVesion", TIFF_NOTYPE }, { EXIFTAG_COLORSPACE, "Exif:ColorSpace", TIFF_SHORT }, { EXIFTAG_PIXELXDIMENSION, "Exif:PixelXDimension", TIFF_LONG }, { EXIFTAG_PIXELYDIMENSION, "Exif:PixelYDimension", TIFF_LONG }, { EXIFTAG_RELATEDSOUNDFILE, "Exif:RelatedSoundFile", TIFF_NOTYPE }, // skip { EXIFTAG_FLASHENERGY, "Exif:FlashEnergy", TIFF_RATIONAL }, { EXIFTAG_SPATIALFREQUENCYRESPONSE, "Exif:SpatialFrequencyResponse", TIFF_NOTYPE }, { EXIFTAG_FOCALPLANEXRESOLUTION, "Exif:FocalPlaneXResolution", TIFF_RATIONAL }, { EXIFTAG_FOCALPLANEYRESOLUTION, "Exif:FocalPlaneYResolution", TIFF_RATIONAL }, { EXIFTAG_FOCALPLANERESOLUTIONUNIT, "Exif:FocalPlaneResolutionUnit", TIFF_SHORT }, // Symbolic? { EXIFTAG_SUBJECTLOCATION, "Exif:SubjectLocation", TIFF_SHORT }, // FIXME: short[2] { EXIFTAG_EXPOSUREINDEX, "Exif:ExposureIndex", TIFF_RATIONAL }, { EXIFTAG_SENSINGMETHOD, "Exif:SensingMethod", TIFF_SHORT }, { EXIFTAG_FILESOURCE, "Exif:FileSource", TIFF_NOTYPE }, { EXIFTAG_SCENETYPE, "Exif:SceneType", TIFF_NOTYPE }, { EXIFTAG_CFAPATTERN, "Exif:CFAPattern", TIFF_NOTYPE }, { EXIFTAG_CUSTOMRENDERED, "Exif:CustomRendered", TIFF_SHORT }, { EXIFTAG_EXPOSUREMODE, "Exif:ExposureMode", TIFF_SHORT }, { EXIFTAG_WHITEBALANCE, "Exif:WhiteBalance", TIFF_SHORT }, { EXIFTAG_DIGITALZOOMRATIO, "Exif:DigitalZoomRatio",TIFF_RATIONAL }, { EXIFTAG_FOCALLENGTHIN35MMFILM, "Exif:FocalLengthIn35mmFilm", TIFF_SHORT }, { EXIFTAG_SCENECAPTURETYPE, "Exif:SceneCaptureType",TIFF_SHORT }, { EXIFTAG_GAINCONTROL, "Exif:GainControl", TIFF_RATIONAL }, { EXIFTAG_CONTRAST, "Exif:Contrast", TIFF_SHORT }, { EXIFTAG_SATURATION, "Exif:Saturation", TIFF_SHORT }, { EXIFTAG_SHARPNESS, "Exif:Sharpness", TIFF_SHORT }, { EXIFTAG_DEVICESETTINGDESCRIPTION, "Exif:DeviceSettingDescription", TIFF_NOTYPE }, { EXIFTAG_SUBJECTDISTANCERANGE, "Exif:SubjectDistanceRange", TIFF_SHORT }, { EXIFTAG_IMAGEUNIQUEID, "Exif:ImageUniqueID", TIFF_ASCII }, { 0, NULL, TIFF_NOTYPE } }; #define ICC_PROFILE_ATTR "ICCProfile" void TIFFInput::readspec (bool read_meta) { uint32 width = 0, height = 0, depth = 0; TIFFGetField (m_tif, TIFFTAG_IMAGEWIDTH, &width); TIFFGetField (m_tif, TIFFTAG_IMAGELENGTH, &height); TIFFGetFieldDefaulted (m_tif, TIFFTAG_IMAGEDEPTH, &depth); TIFFGetFieldDefaulted (m_tif, TIFFTAG_SAMPLESPERPIXEL, &m_inputchannels); if (read_meta) { // clear the whole m_spec and start fresh m_spec = ImageSpec ((int)width, (int)height, (int)m_inputchannels); } else { // assume m_spec is valid, except for things that might differ // between MIP levels m_spec.width = (int)width; m_spec.height = (int)height; m_spec.depth = (int)depth; m_spec.nchannels = (int)m_inputchannels; } float x = 0, y = 0; TIFFGetField (m_tif, TIFFTAG_XPOSITION, &x); TIFFGetField (m_tif, TIFFTAG_YPOSITION, &y); m_spec.x = (int)x; m_spec.y = (int)y; m_spec.z = 0; // FIXME? - TIFF spec describes the positions as in resolutionunit. // What happens if this is not unitless pixels? Are we interpreting // it all wrong? // Start by assuming the "full" (aka display) window is the same as the // data window. That's what we'll stick to if there is no further // information in the file. But if the file has tags for hte "full" // size, assume a display window with origin (0,0) and those dimensions. // (Unfortunately, there are no TIFF tags for "full" origin.) m_spec.full_x = m_spec.x; m_spec.full_y = m_spec.y; m_spec.full_z = m_spec.z; m_spec.full_width = m_spec.width; m_spec.full_height = m_spec.height; m_spec.full_depth = m_spec.depth; if (TIFFGetField (m_tif, TIFFTAG_PIXAR_IMAGEFULLWIDTH, &width) == 1 && TIFFGetField (m_tif, TIFFTAG_PIXAR_IMAGEFULLLENGTH, &height) == 1 && width > 0 && height > 0) { m_spec.full_width = width; m_spec.full_height = height; m_spec.full_x = 0; m_spec.full_y = 0; } if (TIFFIsTiled (m_tif)) { TIFFGetField (m_tif, TIFFTAG_TILEWIDTH, &m_spec.tile_width); TIFFGetField (m_tif, TIFFTAG_TILELENGTH, &m_spec.tile_height); TIFFGetFieldDefaulted (m_tif, TIFFTAG_TILEDEPTH, &m_spec.tile_depth); } else { m_spec.tile_width = 0; m_spec.tile_height = 0; m_spec.tile_depth = 0; } m_bitspersample = 8; TIFFGetField (m_tif, TIFFTAG_BITSPERSAMPLE, &m_bitspersample); m_spec.attribute ("oiio:BitsPerSample", (int)m_bitspersample); unsigned short sampleformat = SAMPLEFORMAT_UINT; TIFFGetFieldDefaulted (m_tif, TIFFTAG_SAMPLEFORMAT, &sampleformat); switch (m_bitspersample) { case 1: case 2: case 4: case 6: // Make 1, 2, 4, 6 bpp look like byte images case 8: if (sampleformat == SAMPLEFORMAT_UINT) m_spec.set_format (TypeDesc::UINT8); else if (sampleformat == SAMPLEFORMAT_INT) m_spec.set_format (TypeDesc::INT8); else m_spec.set_format (TypeDesc::UINT8); // punt break; case 10: case 12: case 14: // Make 10, 12, 14 bpp look like 16 bit images case 16: if (sampleformat == SAMPLEFORMAT_UINT) m_spec.set_format (TypeDesc::UINT16); else if (sampleformat == SAMPLEFORMAT_INT) m_spec.set_format (TypeDesc::INT16); else if (sampleformat == SAMPLEFORMAT_IEEEFP) { m_spec.set_format (TypeDesc::HALF); // Adobe extension, see http://chriscox.org/TIFFTN3d1.pdf } else m_spec.set_format (TypeDesc::UNKNOWN); break; case 32: if (sampleformat == SAMPLEFORMAT_IEEEFP) m_spec.set_format (TypeDesc::FLOAT); else if (sampleformat == SAMPLEFORMAT_UINT) m_spec.set_format (TypeDesc::UINT32); else if (sampleformat == SAMPLEFORMAT_INT) m_spec.set_format (TypeDesc::INT32); else m_spec.set_format (TypeDesc::UNKNOWN); break; case 64: if (sampleformat == SAMPLEFORMAT_IEEEFP) m_spec.set_format (TypeDesc::DOUBLE); else m_spec.set_format (TypeDesc::UNKNOWN); break; default: m_spec.set_format (TypeDesc::UNKNOWN); break; } // Use the table for all the obvious things that can be mindlessly // shoved into the image spec. if (read_meta) { for (int i = 0; tiff_tag_table[i].name; ++i) find_tag (tiff_tag_table[i].tifftag, tiff_tag_table[i].tifftype, tiff_tag_table[i].name); for (int i = 0; exif_tag_table[i].name; ++i) find_tag (exif_tag_table[i].tifftag, exif_tag_table[i].tifftype, exif_tag_table[i].name); } // Now we need to get fields "by hand" for anything else that is less // straightforward... readspec_photometric (); TIFFGetFieldDefaulted (m_tif, TIFFTAG_PLANARCONFIG, &m_planarconfig); m_separate = (m_planarconfig == PLANARCONFIG_SEPARATE && m_spec.nchannels > 1 && m_photometric != PHOTOMETRIC_PALETTE); m_spec.attribute ("tiff:PlanarConfiguration", (int)m_planarconfig); if (m_planarconfig == PLANARCONFIG_SEPARATE) m_spec.attribute ("planarconfig", "separate"); else m_spec.attribute ("planarconfig", "contig"); m_compression = 0; TIFFGetFieldDefaulted (m_tif, TIFFTAG_COMPRESSION, &m_compression); m_spec.attribute ("tiff:Compression", (int)m_compression); if (const char *compressname = tiff_compression_name(m_compression)) m_spec.attribute ("compression", compressname); int rowsperstrip = -1; if (! m_spec.tile_width) { TIFFGetField (m_tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip); if (rowsperstrip > 0) m_spec.attribute ("tiff:RowsPerStrip", rowsperstrip); } // The libtiff docs say that only uncompressed images, or those with // rowsperstrip==1, support random access to scanlines. m_no_random_access = (m_compression != COMPRESSION_NONE && rowsperstrip != 1); // Do we care about fillorder? No, the TIFF spec says, "We // recommend that FillOrder=2 (lsb-to-msb) be used only in // special-purpose applications". So OIIO will assume msb-to-lsb // convention until somebody finds a TIFF file in the wild that // breaks this assumption. unsigned short *sampleinfo = NULL; unsigned short extrasamples = 0; TIFFGetFieldDefaulted (m_tif, TIFFTAG_EXTRASAMPLES, &extrasamples, &sampleinfo); // std::cerr << "Extra samples = " << extrasamples << "\n"; bool alpha_is_unassociated = false; // basic assumption if (extrasamples) { // If the TIFF ExtraSamples tag was specified, use that to figure // out the meaning of alpha. int colorchannels = 3; if (m_photometric == PHOTOMETRIC_MINISWHITE || m_photometric == PHOTOMETRIC_MINISBLACK || m_photometric == PHOTOMETRIC_PALETTE || m_photometric == PHOTOMETRIC_MASK) colorchannels = 1; for (int i = 0, c = colorchannels; i < extrasamples && c < m_inputchannels; ++i, ++c) { // std::cerr << " extra " << i << " " << sampleinfo[i] << "\n"; if (sampleinfo[i] == EXTRASAMPLE_ASSOCALPHA) { // This is the alpha channel, associated as usual m_spec.alpha_channel = c; } else if (sampleinfo[i] == EXTRASAMPLE_UNASSALPHA) { // This is the alpha channel, but color is unassociated m_spec.alpha_channel = c; alpha_is_unassociated = true; if (m_keep_unassociated_alpha) m_spec.attribute ("oiio:UnassociatedAlpha", 1); } else { DASSERT (sampleinfo[i] == EXTRASAMPLE_UNSPECIFIED); // This extra channel is not alpha at all. Undo any // assumptions we previously made about this channel. if (m_spec.alpha_channel == c) { m_spec.channelnames[c] = Strutil::format("channel%d", c); m_spec.alpha_channel = -1; } } } if (m_spec.alpha_channel >= 0) { m_spec.channelnames[m_spec.alpha_channel] = "A"; // Special case: "R","A" should really be named "Y","A", since // the first channel is luminance, not red. if (m_spec.nchannels == 2 && m_spec.alpha_channel == 1) m_spec.channelnames[0] = "Y"; } } // Will we need to do alpha conversions? m_convert_alpha = (m_spec.alpha_channel >= 0 && alpha_is_unassociated && ! m_keep_unassociated_alpha); // N.B. we currently ignore the following TIFF fields: // GrayResponseCurve GrayResponseUnit // MaxSampleValue MinSampleValue // NewSubfileType SubfileType(deprecated) // Colorimetry fields // If we've been instructed to skip reading metadata, because it is // assumed to be identical to what we already have in m_spec, // skip everything following. if (! read_meta) return; short resunit = -1; TIFFGetField (m_tif, TIFFTAG_RESOLUTIONUNIT, &resunit); switch (resunit) { case RESUNIT_NONE : m_spec.attribute ("ResolutionUnit", "none"); break; case RESUNIT_INCH : m_spec.attribute ("ResolutionUnit", "in"); break; case RESUNIT_CENTIMETER : m_spec.attribute ("ResolutionUnit", "cm"); break; } float xdensity = m_spec.get_float_attribute ("XResolution", 0.0f); float ydensity = m_spec.get_float_attribute ("YResolution", 0.0f); if (xdensity && ydensity) m_spec.attribute ("PixelAspectRatio", ydensity/xdensity); get_matrix_attribute ("worldtocamera", TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA); get_matrix_attribute ("worldtoscreen", TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN); get_int_attribute ("tiff:subfiletype", TIFFTAG_SUBFILETYPE); // FIXME -- should subfiletype be "conventionized" and used for all // plugins uniformly? // Special names for shadow maps char *s = NULL; TIFFGetField (m_tif, TIFFTAG_PIXAR_TEXTUREFORMAT, &s); if (s) m_emulate_mipmap = true; if (s && ! strcmp (s, "Shadow")) { for (int c = 0; c < m_spec.nchannels; ++c) m_spec.channelnames[c] = "z"; } /// read color profile unsigned int icc_datasize = 0; unsigned char *icc_buf = NULL; TIFFGetField (m_tif, TIFFTAG_ICCPROFILE, &icc_datasize, &icc_buf); if (icc_datasize && icc_buf) m_spec.attribute (ICC_PROFILE_ATTR, TypeDesc(TypeDesc::UINT8, icc_datasize), icc_buf); // Search for an EXIF IFD in the TIFF file, and if found, rummage // around for Exif fields. #if TIFFLIB_VERSION > 20050912 /* compat with old TIFF libs - skip Exif */ toff_t exifoffset = 0; if (TIFFGetField (m_tif, TIFFTAG_EXIFIFD, &exifoffset) && TIFFReadEXIFDirectory (m_tif, exifoffset)) { for (int i = 0; exif_tag_table[i].name; ++i) find_tag (exif_tag_table[i].tifftag, exif_tag_table[i].tifftype, exif_tag_table[i].name); // I'm not sure what state TIFFReadEXIFDirectory leaves us. // So to be safe, close and re-seek. TIFFClose (m_tif); #ifdef _WIN32 std::wstring wfilename = Strutil::utf8_to_utf16 (m_filename); m_tif = TIFFOpenW (wfilename.c_str(), "rm"); #else m_tif = TIFFOpen (m_filename.c_str(), "rm"); #endif TIFFSetDirectory (m_tif, m_subimage); // A few tidbits to look for ImageIOParameter *p; if ((p = m_spec.find_attribute ("Exif:ColorSpace", TypeDesc::INT))) { // Exif spec says that anything other than 0xffff==uncalibrated // should be interpreted to be sRGB. if (*(const int *)p->data() != 0xffff) m_spec.attribute ("oiio:ColorSpace", "sRGB"); } } #endif #if TIFFLIB_VERSION >= 20051230 // Search for IPTC metadata in IIM form -- but older versions of // libtiff botch the size, so ignore it for very old libtiff. int iptcsize = 0; const void *iptcdata = NULL; if (TIFFGetField (m_tif, TIFFTAG_RICHTIFFIPTC, &iptcsize, &iptcdata)) { std::vector iptc ((uint32 *)iptcdata, (uint32 *)iptcdata+iptcsize); if (TIFFIsByteSwapped (m_tif)) TIFFSwabArrayOfLong ((uint32*)&iptc[0], iptcsize); decode_iptc_iim (&iptc[0], iptcsize*4, m_spec); } #endif // Search for an XML packet containing XMP (IPTC, Exif, etc.) int xmlsize = 0; const void *xmldata = NULL; if (TIFFGetField (m_tif, TIFFTAG_XMLPACKET, &xmlsize, &xmldata)) { // std::cerr << "Found XML data, size " << xmlsize << "\n"; if (xmldata && xmlsize) { std::string xml ((const char *)xmldata, xmlsize); decode_xmp (xml, m_spec); } } #if 0 // Experimental -- look for photoshop data int photoshopsize = 0; const void *photoshopdata = NULL; if (TIFFGetField (m_tif, TIFFTAG_PHOTOSHOP, &photoshopsize, &photoshopdata)) { std::cerr << "Found PHOTOSHOP data, size " << photoshopsize << "\n"; if (photoshopdata && photoshopsize) { // std::string photoshop ((const char *)photoshopdata, photoshopsize); // std::cerr << "PHOTOSHOP:\n" << photoshop << "\n---\n"; } } #endif // If Software and IPTC:OriginatingProgram are identical, kill the latter if (m_spec.get_string_attribute("Software") == m_spec.get_string_attribute("IPTC:OriginatingProgram")) m_spec.erase_attribute ("IPTC:OriginatingProgram"); std::string desc = m_spec.get_string_attribute ("ImageDescription"); // If ImageDescription and IPTC:Caption are identical, kill the latter if (desc == m_spec.get_string_attribute("IPTC:Caption")) m_spec.erase_attribute ("IPTC:Caption"); // Because TIFF doesn't support arbitrary metadata, we look for certain // hints in the ImageDescription and turn them into metadata. bool updatedDesc = false; static const char *fp_number_pattern = "([+-]?((?:(?:[[:digit:]]*\\.)?[[:digit:]]+(?:[eE][+-]?[[:digit:]]+)?)))"; size_t found; found = desc.rfind ("oiio:ConstantColor="); if (found != std::string::npos) { size_t begin = desc.find_first_of ('=', found) + 1; size_t end = std::min (desc.find_first_of (' ', begin), desc.size()); string_view s = string_view (desc.data()+begin, end-begin); m_spec.attribute ("oiio:ConstantColor", s); const std::string constcolor_pattern = std::string ("oiio:ConstantColor=(\\[?") + fp_number_pattern + ",?)+\\]?[ ]*"; desc = boost::regex_replace (desc, boost::regex(constcolor_pattern), ""); updatedDesc = true; } found = desc.rfind ("oiio:AverageColor="); if (found != std::string::npos) { size_t begin = desc.find_first_of ('=', found) + 1; size_t end = std::min (desc.find_first_of (' ', begin), desc.size()); string_view s = string_view (desc.data()+begin, end-begin); m_spec.attribute ("oiio:AverageColor", s); const std::string average_pattern = std::string ("oiio:AverageColor=(\\[?") + fp_number_pattern + ",?)+\\]?[ ]*"; desc = boost::regex_replace (desc, boost::regex(average_pattern), ""); updatedDesc = true; } found = desc.rfind ("oiio:SHA-1="); if (found == std::string::npos) // back compatibility with < 1.5 found = desc.rfind ("SHA-1="); if (found != std::string::npos) { size_t begin = desc.find_first_of ('=', found) + 1; size_t end = std::min (begin+40, desc.size()); string_view s = string_view (desc.data()+begin, end-begin); m_spec.attribute ("oiio:SHA-1", s); desc = boost::regex_replace (desc, boost::regex("oiio:SHA-1=[[:xdigit:]]*[ ]*"), ""); desc = boost::regex_replace (desc, boost::regex("SHA-1=[[:xdigit:]]*[ ]*"), ""); updatedDesc = true; } if (updatedDesc) { if (desc.size()) m_spec.attribute ("ImageDescription", desc); else m_spec.erase_attribute ("ImageDescription"); } if (m_testopenconfig) // open-with-config debugging m_spec.attribute ("oiio:DebugOpenConfig!", 42); } void TIFFInput::readspec_photometric () { m_photometric = (m_spec.nchannels == 1 ? PHOTOMETRIC_MINISBLACK : PHOTOMETRIC_RGB); TIFFGetField (m_tif, TIFFTAG_PHOTOMETRIC, &m_photometric); m_spec.attribute ("tiff:PhotometricInterpretation", (int)m_photometric); switch (m_photometric) { case PHOTOMETRIC_SEPARATED : { // Photometric "separated" is "usually CMYK". m_spec.channelnames.clear (); short inkset = INKSET_CMYK; short numberofinks = 0; TIFFGetFieldDefaulted (m_tif, TIFFTAG_INKSET, &inkset); TIFFGetFieldDefaulted (m_tif, TIFFTAG_NUMBEROFINKS, &numberofinks); if (inkset == INKSET_CMYK && m_spec.nchannels == 4) { // True CMYK m_spec.attribute ("tiff:ColorSpace", "CMYK"); if (m_raw_color) { m_spec.channelnames.resize (4); m_spec.channelnames[0] = "C"; m_spec.channelnames[1] = "M"; m_spec.channelnames[2] = "Y"; m_spec.channelnames[3] = "K"; m_spec.attribute ("oiio:ColorSpace", "CMYK"); } else { // Silently convert to RGB m_spec.nchannels = 3; m_spec.default_channel_names (); } } else { // Non-CMYK ink set m_spec.attribute ("tiff:ColorSpace", "color separated"); m_spec.attribute ("oiio:ColorSpace", "color separated"); m_raw_color = true; // Conversion to RGB doesn't make sense const char* inknames = NULL; safe_tiffgetfield ("tiff:InkNames", TIFFTAG_INKNAMES, &inknames); if (inknames && inknames[0] && numberofinks) { m_spec.channelnames.clear (); // Decode the ink names, which are all concatenated together. for (int i = 0; i < int(numberofinks); ++i) { string_view ink (inknames); if (ink.size()) { m_spec.channelnames.push_back (ink); inknames += ink.size() + 1; } else { // Run out of road numberofinks = i; } } } else { numberofinks = 0; } // No ink names. Make it up. for (int i = numberofinks; i < m_spec.nchannels; ++i) m_spec.channelnames.push_back (Strutil::format ("ink%d", i)); } break; } case PHOTOMETRIC_YCBCR : m_spec.attribute ("tiff:ColorSpace", "YCbCr"); break; case PHOTOMETRIC_CIELAB : m_spec.attribute ("tiff:ColorSpace", "CIELAB"); break; case PHOTOMETRIC_ICCLAB : m_spec.attribute ("tiff:ColorSpace", "ICCLAB"); break; case PHOTOMETRIC_ITULAB : m_spec.attribute ("tiff:ColorSpace", "ITULAB"); break; case PHOTOMETRIC_LOGL : m_spec.attribute ("tiff:ColorSpace", "LOGL"); break; case PHOTOMETRIC_LOGLUV : m_spec.attribute ("tiff:ColorSpace", "LOGLUV"); break; case PHOTOMETRIC_PALETTE : { m_spec.attribute ("tiff:ColorSpace", "palette"); // Read the color map unsigned short *r = NULL, *g = NULL, *b = NULL; TIFFGetField (m_tif, TIFFTAG_COLORMAP, &r, &g, &b); ASSERT (r != NULL && g != NULL && b != NULL); m_colormap.clear (); m_colormap.insert (m_colormap.end(), r, r + (1 << m_bitspersample)); m_colormap.insert (m_colormap.end(), g, g + (1 << m_bitspersample)); m_colormap.insert (m_colormap.end(), b, b + (1 << m_bitspersample)); // Palette TIFF images are always 3 channels (to the client) m_spec.nchannels = 3; m_spec.default_channel_names (); if (m_bitspersample != m_spec.format.size()*8) { // For palette images with unusual bits per sample, set // oiio:BitsPerSample to the "full" version, to avoid problems // when copying the file back to a TIFF file (we don't write // palette images), but do leave "tiff:BitsPerSample" to reflect // the original file. m_spec.attribute ("tiff:BitsPerSample", (int)m_bitspersample); m_spec.attribute ("oiio:BitsPerSample", (int)m_spec.format.size()*8); } // FIXME - what about palette + extra (alpha?) channels? Is that // allowed? And if so, ever encountered in the wild? break; } } } bool TIFFInput::close () { close_tif (); init(); // Reset to initial state return true; } /// Helper: Convert n pixels from separate (RRRGGGBBB) to contiguous /// (RGBRGBRGB) planarconfig. void TIFFInput::separate_to_contig (int nplanes, int nvals, const unsigned char *separate, unsigned char *contig) { int channelbytes = m_spec.channel_bytes(); for (int p = 0; p < nvals; ++p) // loop over pixels for (int c = 0; c < nplanes; ++c) // loop over channels for (int i = 0; i < channelbytes; ++i) // loop over data bytes contig[(p*nplanes+c)*channelbytes+i] = separate[(c*nvals+p)*channelbytes+i]; } void TIFFInput::palette_to_rgb (int n, const unsigned char *palettepels, unsigned char *rgb) { size_t vals_per_byte = 8 / m_bitspersample; size_t entries = 1 << m_bitspersample; int highest = entries-1; DASSERT (m_spec.nchannels == 3); DASSERT (m_colormap.size() == 3*entries); for (int x = 0; x < n; ++x) { int i = palettepels[x/vals_per_byte]; i >>= (m_bitspersample * (vals_per_byte - 1 - (x % vals_per_byte))); i &= highest; *rgb++ = m_colormap[0*entries+i] / 257; *rgb++ = m_colormap[1*entries+i] / 257; *rgb++ = m_colormap[2*entries+i] / 257; } } void TIFFInput::bit_convert (int n, const unsigned char *in, int inbits, void *out, int outbits) { ASSERT (inbits >= 1 && inbits < 31); // surely bugs if not int highest = (1 << inbits) - 1; int B = 0, b = 0; // Invariant: // So far, we have used in[0..B-1] and the high b bits of in[B]. for (int i = 0; i < n; ++i) { long long val = 0; int valbits = 0; // bits so far we've accumulated in val while (valbits < inbits) { // Invariant: we have already accumulated valbits of the next // needed value (of a total of inbits), living in the valbits // low bits of val. int out_left = inbits - valbits; // How much more we still need int in_left = 8 - b; // Bits still available in in[B]. if (in_left <= out_left) { // Eat the rest of this byte: // |---------|--------| // b in_left val <<= in_left; val |= in[B] & ~(0xffffffff << in_left); ++B; b = 0; valbits += in_left; } else { // Eat just the bits we need: // |--|---------|-----| // b out_left extra val <<= out_left; int extra = 8 - b - out_left; val |= (in[B] >> extra) & ~(0xffffffff << out_left); b += out_left; valbits = inbits; } } if (outbits == 8) ((unsigned char *)out)[i] = (unsigned char) ((val * 0xff) / highest); else if (outbits == 16) ((unsigned short *)out)[i] = (unsigned short) ((val * 0xffff) / highest); else ((unsigned int *)out)[i] = (unsigned int) ((val * 0xffffffff) / highest); } } void TIFFInput::invert_photometric (int n, void *data) { switch (m_spec.format.basetype) { case TypeDesc::UINT8: { unsigned char *d = (unsigned char *) data; for (int i = 0; i < n; ++i) d[i] = 255 - d[i]; break; } default: break; } } template static void cmyk_to_rgb (int n, const T *cmyk, size_t cmyk_stride, T *rgb, size_t rgb_stride) { for ( ; n; --n, cmyk += cmyk_stride, rgb += rgb_stride) { float C = convert_type(cmyk[0]); float M = convert_type(cmyk[1]); float Y = convert_type(cmyk[2]); float K = convert_type(cmyk[3]); float R = (1.0f - C) * (1.0f - K); float G = (1.0f - M) * (1.0f - K); float B = (1.0f - Y) * (1.0f - K); rgb[0] = convert_type(R); rgb[1] = convert_type(G); rgb[2] = convert_type(B); } } bool TIFFInput::read_native_scanline (int y, int z, void *data) { y -= m_spec.y; if (m_use_rgba_interface) { // We punted and used the RGBA image interface -- copy from buffer. // libtiff has no way to read just one scanline as RGBA. So we // buffer the whole image. if (! m_rgbadata.size()) { // first time through: allocate & read m_rgbadata.resize (m_spec.width * m_spec.height * m_spec.depth); bool ok = TIFFReadRGBAImageOriented (m_tif, m_spec.width, m_spec.height, &m_rgbadata[0], ORIENTATION_TOPLEFT, 0); if (! ok) { error ("Unknown error trying to read TIFF as RGBA"); return false; } } copy_image (m_spec.nchannels, m_spec.width, 1, 1, &m_rgbadata[y*m_spec.width], m_spec.nchannels, 4, 4*m_spec.width, AutoStride, data, m_spec.nchannels, m_spec.width*m_spec.nchannels, AutoStride); return true; } // For compression modes that don't support random access to scanlines // (which I *think* is only LZW), we need to emulate random access by // re-seeking. if (m_no_random_access) { if (m_next_scanline > y) { // User is trying to read an earlier scanline than the one we're // up to. Easy fix: start over. // FIXME: I'm too tired to look into it now, but I wonder if // it is able to randomly seek to the first line in any // "strip", in which case we don't need to start from 0, just // start from the beginning of the strip we need. ImageSpec dummyspec; int old_subimage = current_subimage(); int old_miplevel = current_miplevel(); if (! close () || ! open (m_filename, dummyspec) || ! seek_subimage (old_subimage, old_miplevel, dummyspec)) { return false; // Somehow, the re-open failed } ASSERT (m_next_scanline == 0 && current_subimage() == old_subimage && current_miplevel() == old_miplevel); } while (m_next_scanline < y) { // Keep reading until we're read the scanline we really need m_scratch.resize (m_spec.scanline_bytes()); if (TIFFReadScanline (m_tif, &m_scratch[0], m_next_scanline) < 0) { error ("%s", oiio_tiff_last_error()); return false; } ++m_next_scanline; } } m_next_scanline = y+1; int nvals = m_spec.width * m_inputchannels; m_scratch.resize (nvals * m_spec.format.size()); bool need_bit_convert = (m_bitspersample != 8 && m_bitspersample != 16 && m_bitspersample != 32); if (m_photometric == PHOTOMETRIC_PALETTE) { // Convert from palette to RGB if (TIFFReadScanline (m_tif, &m_scratch[0], y) < 0) { error ("%s", oiio_tiff_last_error()); return false; } palette_to_rgb (m_spec.width, &m_scratch[0], (unsigned char *)data); return true; } // Not palette... int plane_bytes = m_spec.width * m_spec.format.size(); int planes = m_separate ? m_inputchannels : 1; int input_bytes = plane_bytes * m_inputchannels; // Where to read? Directly into user data if no channel shuffling, bit // shifting, or CMYK conversion is needed, otherwise into scratch space. unsigned char *readbuf = (unsigned char *)data; if (need_bit_convert || m_separate || (m_photometric == PHOTOMETRIC_SEPARATED && ! m_raw_color)) readbuf = &m_scratch[0]; // Perform the reads. Note that for contig, planes==1, so it will // only do one TIFFReadScanline. for (int c = 0; c < planes; ++c) { /* planes==1 for contig */ if (TIFFReadScanline (m_tif, &readbuf[plane_bytes*c], y, c) < 0) { error ("%s", oiio_tiff_last_error()); return false; } } // Handle less-than-full bit depths if (m_bitspersample < 8) { // m_scratch now holds nvals n-bit values, contig or separate m_scratch2.resize (input_bytes); m_scratch.swap (m_scratch2); for (int c = 0; c < planes; ++c) /* planes==1 for contig */ bit_convert (m_separate ? m_spec.width : nvals, &m_scratch2[plane_bytes*c], m_bitspersample, m_separate ? &m_scratch[plane_bytes*c] : (unsigned char *)data+plane_bytes*c, 8); } else if (m_bitspersample > 8 && m_bitspersample < 16) { // m_scratch now holds nvals n-bit values, contig or separate m_scratch2.resize (input_bytes); m_scratch.swap (m_scratch2); for (int c = 0; c < planes; ++c) /* planes==1 for contig */ bit_convert (m_separate ? m_spec.width : nvals, &m_scratch2[plane_bytes*c], m_bitspersample, m_separate ? &m_scratch[plane_bytes*c] : (unsigned char *)data+plane_bytes*c, 16); } // Handle "separate" planarconfig if (m_separate) { // Convert from separate (RRRGGGBBB) to contiguous (RGBRGBRGB). // We know the data is in m_scratch at this point, so // contiguize it into the user data area. if (m_photometric == PHOTOMETRIC_SEPARATED && ! m_raw_color) { // CMYK->RGB means we need temp storage. m_scratch2.resize (input_bytes); separate_to_contig (planes, m_spec.width, &m_scratch[0], &m_scratch2[0]); m_scratch.swap (m_scratch2); } else { // If no CMYK->RGB conversion is necessary, we can "separate" // straight into the data area. separate_to_contig (planes, m_spec.width, &m_scratch[0], (unsigned char *)data); } } // Handle CMYK if (m_photometric == PHOTOMETRIC_SEPARATED && ! m_raw_color) { // The CMYK will be in m_scratch. if (spec().format == TypeDesc::UINT8) { cmyk_to_rgb (m_spec.width, (unsigned char *)&m_scratch[0], m_inputchannels, (unsigned char *)data, m_spec.nchannels); } else if (spec().format == TypeDesc::UINT16) { cmyk_to_rgb (m_spec.width, (unsigned short *)&m_scratch[0], m_inputchannels, (unsigned short *)data, m_spec.nchannels); } else { error ("CMYK only supported for UINT8, UINT16"); return false; } } if (m_photometric == PHOTOMETRIC_MINISWHITE) invert_photometric (nvals, data); return true; } bool TIFFInput::read_native_tile (int x, int y, int z, void *data) { x -= m_spec.x; y -= m_spec.y; if (m_use_rgba_interface) { // We punted and used the RGBA image interface // libtiff has a call to read just one tile as RGBA. So that's all // we need to do, not buffer the whole image. m_rgbadata.resize (m_spec.tile_pixels() * 4); bool ok = TIFFReadRGBATile (m_tif, x, y, &m_rgbadata[0]); if (!ok) { error ("Unknown error trying to read TIFF as RGBA"); return false; } // Copy, and use stride magic to reverse top-to-bottom int tw = std::min (m_spec.tile_width, m_spec.width-x); int th = std::min (m_spec.tile_height, m_spec.height-y); copy_image (m_spec.nchannels, tw, th, 1, &m_rgbadata[(th-1)*m_spec.tile_width], m_spec.nchannels, 4, -m_spec.tile_width*4, AutoStride, data, m_spec.nchannels, m_spec.nchannels*m_spec.tile_width, AutoStride); return true; } imagesize_t tile_pixels = m_spec.tile_pixels(); imagesize_t nvals = tile_pixels * m_spec.nchannels; m_scratch.resize (m_spec.tile_bytes()); bool no_bit_convert = (m_bitspersample == 8 || m_bitspersample == 16 || m_bitspersample == 32); if (m_photometric == PHOTOMETRIC_PALETTE) { // Convert from palette to RGB if (TIFFReadTile (m_tif, &m_scratch[0], x, y, z, 0) < 0) { error ("%s", oiio_tiff_last_error()); return false; } palette_to_rgb (tile_pixels, &m_scratch[0], (unsigned char *)data); } else { // Not palette imagesize_t plane_bytes = m_spec.tile_pixels() * m_spec.format.size(); int planes = m_separate ? m_spec.nchannels : 1; std::vector scratch2 (m_separate ? m_spec.tile_bytes() : 0); // Where to read? Directly into user data if no channel shuffling // or bit shifting is needed, otherwise into scratch space. unsigned char *readbuf = (no_bit_convert && !m_separate) ? (unsigned char *)data : &m_scratch[0]; // Perform the reads. Note that for contig, planes==1, so it will // only do one TIFFReadTile. for (int c = 0; c < planes; ++c) /* planes==1 for contig */ if (TIFFReadTile (m_tif, &readbuf[plane_bytes*c], x, y, z, c) < 0) { error ("%s", oiio_tiff_last_error()); return false; } if (m_bitspersample < 8) { // m_scratch now holds nvals n-bit values, contig or separate std::swap (m_scratch, scratch2); for (int c = 0; c < planes; ++c) /* planes==1 for contig */ bit_convert (m_separate ? tile_pixels : nvals, &scratch2[plane_bytes*c], m_bitspersample, m_separate ? &m_scratch[plane_bytes*c] : (unsigned char *)data+plane_bytes*c, 8); } else if (m_bitspersample > 8 && m_bitspersample < 16) { // m_scratch now holds nvals n-bit values, contig or separate std::swap (m_scratch, scratch2); for (int c = 0; c < planes; ++c) /* planes==1 for contig */ bit_convert (m_separate ? tile_pixels : nvals, &scratch2[plane_bytes*c], m_bitspersample, m_separate ? &m_scratch[plane_bytes*c] : (unsigned char *)data+plane_bytes*c, 16); } if (m_separate) { // Convert from separate (RRRGGGBBB) to contiguous (RGBRGBRGB). // We know the data is in m_scratch at this point, so // contiguize it into the user data area. separate_to_contig (planes, tile_pixels, &m_scratch[0], (unsigned char *)data); } } if (m_photometric == PHOTOMETRIC_MINISWHITE) invert_photometric (nvals, data); return true; } bool TIFFInput::read_scanline (int y, int z, TypeDesc format, void *data, stride_t xstride) { bool ok = ImageInput::read_scanline (y, z, format, data, xstride); if (ok && m_convert_alpha) { // If alpha is unassociated and we aren't requested to keep it that // way, multiply the colors by alpha per the usual OIIO conventions // to deliver associated color & alpha. Any auto-premultiplication // by alpha should happen after we've already done data format // conversions. That's why we do it here, rather than in // read_native_blah. OIIO::premult (m_spec.nchannels, m_spec.width, 1, 1, 0 /*chbegin*/, m_spec.nchannels /*chend*/, format, data, xstride, AutoStride, AutoStride, m_spec.alpha_channel, m_spec.z_channel); } return ok; } bool TIFFInput::read_scanlines (int ybegin, int yend, int z, int chbegin, int chend, TypeDesc format, void *data, stride_t xstride, stride_t ystride) { bool ok = ImageInput::read_scanlines (ybegin, yend, z, chbegin, chend, format, data, xstride, ystride); if (ok && m_convert_alpha) { // If alpha is unassociated and we aren't requested to keep it that // way, multiply the colors by alpha per the usual OIIO conventions // to deliver associated color & alpha. Any auto-premultiplication // by alpha should happen after we've already done data format // conversions. That's why we do it here, rather than in // read_native_blah. OIIO::premult (m_spec.nchannels, m_spec.width, yend-ybegin, 1, chbegin, chend, format, data, xstride, ystride, AutoStride, m_spec.alpha_channel, m_spec.z_channel); } return ok; } bool TIFFInput::read_tile (int x, int y, int z, TypeDesc format, void *data, stride_t xstride, stride_t ystride, stride_t zstride) { bool ok = ImageInput::read_tile (x, y, z, format, data, xstride, ystride, zstride); if (ok && m_convert_alpha) { // If alpha is unassociated and we aren't requested to keep it that // way, multiply the colors by alpha per the usual OIIO conventions // to deliver associated color & alpha. Any auto-premultiplication // by alpha should happen after we've already done data format // conversions. That's why we do it here, rather than in // read_native_blah. OIIO::premult (m_spec.nchannels, m_spec.tile_width, m_spec.tile_height, std::max (1, m_spec.tile_depth), 0, m_spec.nchannels, format, data, xstride, AutoStride, AutoStride, m_spec.alpha_channel, m_spec.z_channel); } return ok; } bool TIFFInput::read_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc format, void *data, stride_t xstride, stride_t ystride, stride_t zstride) { bool ok = ImageInput::read_tiles (xbegin, xend, ybegin, yend, zbegin, zend, chbegin, chend, format, data, xstride, ystride, zstride); if (ok && m_convert_alpha) { // If alpha is unassociated and we aren't requested to keep it that // way, multiply the colors by alpha per the usual OIIO conventions // to deliver associated color & alpha. Any auto-premultiplication // by alpha should happen after we've already done data format // conversions. That's why we do it here, rather than in // read_native_blah. OIIO::premult (m_spec.nchannels, m_spec.tile_width, m_spec.tile_height, std::max (1, m_spec.tile_depth), chbegin, chend, format, data, xstride, AutoStride, AutoStride, m_spec.alpha_channel, m_spec.z_channel); } return ok; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/pnm.imageio/0000755000175000017500000000000013151711064017317 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/pnm.imageio/pnmoutput.cpp0000644000175000017500000002204013151711064022074 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include "OpenImageIO/filesystem.h" #include "OpenImageIO/imageio.h" OIIO_PLUGIN_NAMESPACE_BEGIN class PNMOutput : public ImageOutput { public: virtual ~PNMOutput (); virtual const char * format_name (void) const { return "pnm"; } virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode=Create); virtual bool close (); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); private: std::string m_filename; ///< Stash the filename OIIO::ofstream m_file; unsigned int m_max_val, m_pnm_type; unsigned int m_dither; std::vector m_scratch; std::vector m_tilebuffer; }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *pnm_output_imageio_create () { return new PNMOutput; } OIIO_EXPORT const char * pnm_output_extensions[] = { "ppm","pgm","pbm","pnm", NULL }; OIIO_PLUGIN_EXPORTS_END inline void write_ascii_binary (std::ostream & file, const unsigned char * data, const stride_t stride, const ImageSpec & spec) { for (int x = 0; x < spec.width; x++) file << (data[x * stride] ? '1' : '0') << "\n"; } inline void write_raw_binary (std::ostream & file, const unsigned char * data, const stride_t stride, const ImageSpec & spec) { unsigned char val; for (int x = 0; x < spec.width;) { val=0; for (int bit=7; bit >= 0 && x < spec.width; x++, bit--) val += (data[x * stride] ? (1 << bit) : 0); file.write ((char*)&val, sizeof (char)); } } template inline void write_ascii (std::ostream &file, const T *data, const stride_t stride, const ImageSpec &spec, unsigned int max_val) { unsigned int pixel, val; for (int x = 0; x < spec.width; x++){ pixel = x * stride; for (int c = 0; c < spec.nchannels; c++) { val = data [pixel + c]; val = val * max_val / std::numeric_limits::max(); file << val << "\n"; } } } template inline void write_raw (std::ostream &file, const T *data, const stride_t stride, const ImageSpec &spec, unsigned int max_val) { unsigned char byte; unsigned int pixel, val; for (int x = 0; x < spec.width; x++) { pixel = x * stride; for (int c = 0; c < spec.nchannels; c++) { val = data[pixel + c]; val = val * max_val / std::numeric_limits::max(); if (sizeof(T) == 2) { // Writing a 16bit ppm file // I'll adopt the practice of Netpbm and write the MSB first byte = static_cast(val >> 8); file.write ((char*)&byte, 1); byte = static_cast(val & 0xff); file.write ((char*)&byte, 1); } else { // This must be an 8bit ppm file byte = static_cast(val); file.write ((char*)&byte, 1); } } } } PNMOutput::~PNMOutput () { close (); } bool PNMOutput::open (const std::string &name, const ImageSpec &userspec, OpenMode mode) { if (mode != Create) { error ("%s does not support subimages or MIP levels", format_name()); return false; } close (); // Close any already-opened file m_spec = userspec; // Stash the spec m_spec.set_format (TypeDesc::UINT8); // Force 8 bit output int bits_per_sample = m_spec.get_int_attribute ("oiio:BitsPerSample", 8); m_dither = (m_spec.format == TypeDesc::UINT8) ? m_spec.get_int_attribute ("oiio:dither", 0) : 0; if (m_spec.nchannels != 1 && m_spec.nchannels != 3) { error ("%s does not support %d-channel images\n", format_name(), m_spec.nchannels); return false; } if (bits_per_sample == 1) m_pnm_type = 4; else if (m_spec.nchannels == 1) m_pnm_type = 5; else m_pnm_type = 6; if (!m_spec.get_int_attribute ("pnm:binary", 1)) { m_pnm_type -= 3; Filesystem::open (m_file, name); } else { Filesystem::open (m_file, name, std::ios::out|std::ios::binary); } if (!m_file) return false; m_max_val = (1 << bits_per_sample) - 1; // Write header m_file << "P" << m_pnm_type << std::endl; m_file << m_spec.width << " " << m_spec.height << std::endl; if (m_pnm_type != 1 && m_pnm_type != 4) // only non-monochrome m_file << m_max_val << std::endl; // If user asked for tiles -- which this format doesn't support, emulate // it by buffering the whole image. if (m_spec.tile_width && m_spec.tile_height) m_tilebuffer.resize (m_spec.image_bytes()); return m_file.good(); } bool PNMOutput::close () { if (!m_file) { // already closed return true; } bool ok = true; if (m_spec.tile_width) { // Handle tile emulation -- output the buffered pixels ASSERT (m_tilebuffer.size()); ok &= write_scanlines (m_spec.y, m_spec.y+m_spec.height, 0, m_spec.format, &m_tilebuffer[0]); std::vector().swap (m_tilebuffer); } m_file.close(); return true; } bool PNMOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { if (!m_file) return false; if (z) return false; m_spec.auto_stride (xstride, format, spec().nchannels); const void *origdata = data; data = to_native_scanline (format, data, xstride, m_scratch, m_dither, y, z); if (data != origdata) // a conversion happened... xstride = spec().nchannels; switch (m_pnm_type){ case 1: write_ascii_binary (m_file, (unsigned char *) data, xstride, m_spec); break; case 2: case 3: if (m_max_val > std::numeric_limits::max()) write_ascii (m_file, (unsigned short *) data, xstride, m_spec, m_max_val); else write_ascii (m_file, (unsigned char *) data, xstride, m_spec, m_max_val); break; case 4: write_raw_binary (m_file, (unsigned char *) data, xstride, m_spec); break; case 5: case 6: if (m_max_val > std::numeric_limits::max()) write_raw (m_file, (unsigned short *) data, xstride, m_spec, m_max_val); else write_raw (m_file, (unsigned char *) data, xstride, m_spec, m_max_val); break; default: return false; } return m_file.good(); } bool PNMOutput::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { // Emulate tiles by buffering the whole image return copy_tile_to_image_buffer (x, y, z, format, data, xstride, ystride, zstride, &m_tilebuffer[0]); } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/pnm.imageio/CMakeLists.txt0000644000175000017500000000005513151711064022057 0ustar mfvmfvadd_oiio_plugin (pnminput.cpp pnmoutput.cpp) openimageio-1.7.17~dfsg0.orig/src/pnm.imageio/pnminput.cpp0000644000175000017500000003160213151711064021677 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/imageio.h" OIIO_PLUGIN_NAMESPACE_BEGIN class PNMInput : public ImageInput { public: PNMInput() { } virtual ~PNMInput() { close(); } virtual const char* format_name (void) const { return "pnm"; } virtual bool open (const std::string &name, ImageSpec &newspec); virtual bool close (); virtual int current_subimage (void) const { return 0; } virtual bool read_native_scanline (int y, int z, void *data); private: enum PNMType { P1, P2, P3, P4, P5, P6, Pf, PF }; OIIO::ifstream m_file; std::streampos m_header_end_pos; // file position after the header std::string m_current_line; ///< Buffer the image pixels const char * m_pos; PNMType m_pnm_type; unsigned int m_max_val; float m_scaling_factor; bool read_file_scanline (void * data, int y); bool read_file_header (); }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageInput* pnm_input_imageio_create () { return new PNMInput; } OIIO_EXPORT int pnm_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* pnm_imageio_library_version() { return NULL; } OIIO_EXPORT const char* pnm_input_extensions[] = { "ppm","pgm","pbm","pnm", "pfm", NULL }; OIIO_PLUGIN_EXPORTS_END inline bool nextLine (std::istream &file, std::string ¤t_line, const char * &pos) { if (!file.good()) return false; getline (file, current_line); if (file.fail()) return false; pos = current_line.c_str(); return true; } inline const char * nextToken (std::istream &file, std::string ¤t_line, const char * &pos) { while (1) { while (isspace (*pos)) pos++; if (*pos) break; else nextLine (file, current_line, pos); } return pos; } inline const char * skipComments (std::istream &file, std::string ¤t_line, const char * & pos, char comment = '#') { while (1) { nextToken (file, current_line, pos); if (*pos == comment) nextLine (file, current_line, pos); else break; } return pos; } inline bool nextVal (std::istream & file, std::string ¤t_line, const char * &pos, int &val, char comment = '#') { skipComments (file, current_line, pos, comment); if (!isdigit (*pos)) return false; val = strtol (pos,(char**) &pos, 10); return true; } template inline void invert (const T *read, T *write, imagesize_t nvals) { for (imagesize_t i=0; i < nvals; i++) write[i] = std::numeric_limits::max() - read[i]; } template inline bool ascii_to_raw (std::istream &file, std::string ¤t_line, const char * &pos, T *write, imagesize_t nvals, T max) { if (max) for (imagesize_t i=0; i < nvals; i++) { int tmp; if (!nextVal (file, current_line, pos, tmp)) return false; write[i] = std::min ((int)max, tmp) * std::numeric_limits::max() / max; } else for (imagesize_t i=0; i < nvals; i++) write[i] = std::numeric_limits::max(); return true; } template inline void raw_to_raw (const T *read, T *write, imagesize_t nvals, T max) { if (max) for (imagesize_t i=0; i < nvals; i++) { int tmp = read[i]; write[i] = std::min ((int)max, tmp) * std::numeric_limits::max() / max; } else for (imagesize_t i=0; i < nvals; i++) write[i] = std::numeric_limits::max(); } inline void unpack (const unsigned char * read, unsigned char * write, imagesize_t size) { imagesize_t w = 0, r = 0; unsigned char bit = 0x7, byte = 0; for (imagesize_t x = 0; x < size; x++) { if (bit == 0x7) byte = ~read[r++]; write[w++] = 0 - ((byte & (1 << bit)) >> bit);//assign expanded bit bit = (bit - 1) & 0x7; // limit bit to [0; 8[ } } inline void unpack_floats(const unsigned char * read, float * write, imagesize_t numsamples, float scaling_factor) { float *read_floats = (float *)read; if((scaling_factor < 0 && bigendian()) || (scaling_factor > 0 && littleendian())) { swap_endian(read_floats, numsamples); } float absfactor = fabs(scaling_factor); for(imagesize_t i = 0; i < numsamples; i++) { write[i] = absfactor * read_floats[i]; } } template inline bool read_int (std::istream &in, T &dest, char comment='#') { T ret; char c; while (!in.eof()) { in >> ret; if (!in.good()){ in.clear(); in >> c; if (c == comment) in.ignore (std::numeric_limits::max(), '\n'); else return false; } else { dest = ret; return true; } } return false; } bool PNMInput::read_file_scanline (void * data, int y) { try { std::vector buf; bool good = true; if (!m_file) return false; int nsamples = m_spec.width * m_spec.nchannels; // PFM files are bottom-to-top, so we need to seek to the right spot if (m_pnm_type == PF || m_pnm_type == Pf) { int file_scanline = m_spec.height - 1 - (y-m_spec.y); std::streampos offset = file_scanline * m_spec.scanline_bytes(); m_file.seekg (m_header_end_pos + offset, std::ios_base::beg); } if ((m_pnm_type >= P4 && m_pnm_type <= P6) || m_pnm_type == PF || m_pnm_type == Pf){ int numbytes; if (m_pnm_type == P4) numbytes = (m_spec.width + 7) / 8; else if(m_pnm_type == PF || m_pnm_type == Pf) numbytes = m_spec.nchannels * 4 * m_spec.width; else numbytes = m_spec.scanline_bytes(); buf.resize (numbytes); m_file.read ((char*)&buf[0], numbytes); if (!m_file.good()) return false; } switch (m_pnm_type) { //Ascii case P1: good &= ascii_to_raw (m_file, m_current_line, m_pos, (unsigned char *) data, nsamples, (unsigned char)m_max_val); invert ((unsigned char *)data, (unsigned char *)data, nsamples); break; case P2: case P3: if (m_max_val > std::numeric_limits::max()) good &= ascii_to_raw (m_file, m_current_line, m_pos, (unsigned short *) data, nsamples, (unsigned short)m_max_val); else good &= ascii_to_raw (m_file, m_current_line, m_pos, (unsigned char *) data, nsamples, (unsigned char)m_max_val); break; //Raw case P4: unpack (&buf[0], (unsigned char *)data, nsamples); break; case P5: case P6: if (m_max_val > std::numeric_limits::max()) { if (littleendian()) swap_endian ((unsigned short *)&buf[0], nsamples); raw_to_raw ((unsigned short *)&buf[0], (unsigned short *) data, nsamples, (unsigned short)m_max_val); } else { raw_to_raw ((unsigned char *)&buf[0], (unsigned char *) data, nsamples, (unsigned char)m_max_val); } break; //Floating point case Pf: case PF: unpack_floats(&buf[0], (float *)data, nsamples, m_scaling_factor); break; default: return false; } return good; } catch (const std::exception &e) { error ("PNM exception: %s", e.what()); return false; } } bool PNMInput::read_file_header () { try { unsigned int width, height; char c; if (!m_file) return false; //MagicNumber m_file >> c; if (c != 'P') return false; m_file >> c; switch(c) { case '1': m_pnm_type = P1; break; case '2': m_pnm_type = P2; break; case '3': m_pnm_type = P3; break; case '4': m_pnm_type = P4; break; case '5': m_pnm_type = P5; break; case '6': m_pnm_type = P6; break; case 'f': m_pnm_type = Pf; break; case 'F': m_pnm_type = PF; break; default: return false; } //Size if (!read_int (m_file, width)) return false; if (!read_int (m_file, height)) return false; if(m_pnm_type != PF && m_pnm_type != Pf) { //Max Val if (m_pnm_type != P1 && m_pnm_type != P4) { if (!read_int (m_file, m_max_val)) return false; } else m_max_val = 1; //Space before content if (!(isspace (m_file.get()) && m_file.good())) return false; m_header_end_pos = m_file.tellg (); // remember file pos if (m_pnm_type == P3 || m_pnm_type == P6) m_spec = ImageSpec (width, height, 3, (m_max_val > 255) ? TypeDesc::UINT16 : TypeDesc::UINT8); else m_spec = ImageSpec (width, height, 1, (m_max_val > 255) ? TypeDesc::UINT16 : TypeDesc::UINT8); if (m_spec.nchannels == 1) m_spec.channelnames[0] = "I"; else m_spec.default_channel_names(); if (m_pnm_type >= P1 && m_pnm_type <= P3) m_spec.attribute ("pnm:binary", 0); else m_spec.attribute ("pnm:binary", 1); m_spec.attribute ("oiio:BitsPerSample", ceilf (logf (m_max_val + 1)/logf (2))); return true; } else { //Read scaling factor if(!read_int(m_file, m_scaling_factor)) { return false; } //Space before content if (!(isspace (m_file.get()) && m_file.good())) return false; m_header_end_pos = m_file.tellg (); // remember file pos if(m_pnm_type == PF) { m_spec = ImageSpec(width, height, 3, TypeDesc::FLOAT); m_spec.default_channel_names(); } else { m_spec = ImageSpec(width, height, 1, TypeDesc::FLOAT); m_spec.channelnames[0] = "I"; } if(m_scaling_factor < 0) { m_spec.attribute("pnm:bigendian", 0); } else { m_spec.attribute("pnm:bigendian", 1); } return true; } } catch (const std::exception &e) { error ("PNM exception: %s", e.what()); return false; } } bool PNMInput::open (const std::string &name, ImageSpec &newspec) { close(); //close previously opened file Filesystem::open (m_file, name, std::ios::in|std::ios::binary); m_current_line = ""; m_pos = m_current_line.c_str(); if (!read_file_header()) return false; newspec = m_spec; return true; } bool PNMInput::close () { m_file.close(); return true; } bool PNMInput::read_native_scanline (int y, int z, void *data) { if (z) return false; if (!read_file_scanline (data, y)) return false; return true; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/ico.imageio/0000755000175000017500000000000013151711064017277 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/ico.imageio/CMakeLists.txt0000644000175000017500000000040313151711064022034 0ustar mfvmfvif (PNG_FOUND) add_oiio_plugin (icoinput.cpp icooutput.cpp INCLUDE_DIRS ${PNG_INCLUDE_DIR} LINK_LIBRARIES ${PNG_LIBRARIES}) else () message (WARNING "libpng not found, so ICO support will not work") endif () openimageio-1.7.17~dfsg0.orig/src/ico.imageio/ico.h0000644000175000017500000000731013151711064020223 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_ICO_H #define OPENIMAGEIO_ICO_H #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" OIIO_PLUGIN_NAMESPACE_BEGIN namespace ICO_pvt { // IneQuation was here // Win32 (pre-Vista) ICO format as described in these documents: // http://msdn.microsoft.com/en-us/library/ms997538.aspx // http://en.wikipedia.org/wiki/ICO_(icon_image_file_format) // http://msdn.microsoft.com/en-us/library/dd183376(VS.85).aspx // ...plus some of my own magic. /// Win32 DIB (Device-Independent Bitmap) header. /// According to MSDN, only size, width, height, planes, bpp and len are /// valid for ICOs. struct ico_bitmapinfo { int32_t size; ///< structure size in bytes (how about a sizeof?) int32_t width; int32_t height; int16_t planes; ///< # of colour planes int16_t bpp; ///< bits per pixel int32_t compression; ///< unused: compression type int32_t len; ///< image size in bytes; may be 0 for uncompressed bitmaps int32_t x_res; ///< unused: resolution of target device in pixels per metre int32_t y_res; ///< unused: resolution of target device in pixels per metre int32_t clrs_used; ///< # of colours used (if using a palette) int32_t clrs_required; ///< # of colours required to display the bitmap; 0 = all of them }; /// Icon palette entry. Attached at each struct ico_palette_entry { int8_t b, g, r; int8_t reserved; // unused }; struct ico_subimage { uint8_t width; ///< 0 means 256 pixels uint8_t height; ///< 0 means 256 pixels uint8_t numColours; ///< 0 means >= 256 uint8_t reserved; ///< should always be 0 uint16_t planes; ///< # of colour planes uint16_t bpp; ///< bits per pixel uint32_t len; ///< size (in bytes) of bitmap data uint32_t ofs; ///< offset to bitmap data }; struct ico_header { int16_t reserved; ///< should always be 0 int16_t type; ///< 1 is icon, 2 is cursor int16_t count; ///< number of subimages in the file }; } // namespace ICO_pvt OIIO_PLUGIN_NAMESPACE_END #endif // OPENIMAGEIO_ICO_H openimageio-1.7.17~dfsg0.orig/src/ico.imageio/icoinput.cpp0000644000175000017500000003523213151711064021642 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include "ico.h" #include "../png.imageio/png_pvt.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/fmath.h" OIIO_PLUGIN_NAMESPACE_BEGIN using namespace ICO_pvt; class ICOInput : public ImageInput { public: ICOInput () { init(); } virtual ~ICOInput () { close(); } virtual const char * format_name (void) const { return "ico"; } virtual bool open (const std::string &name, ImageSpec &newspec); virtual bool close (); virtual int current_subimage (void) const { return m_subimage; } virtual bool seek_subimage (int subimage, int miplevel, ImageSpec &newspec); virtual bool read_native_scanline (int y, int z, void *data); private: std::string m_filename; ///< Stash the filename FILE *m_file; ///< Open image handle ico_header m_ico; ///< ICO header std::vector m_buf; ///< Buffer the image pixels int m_subimage; ///< What subimage are we looking at? int m_bpp; ///< Bits per pixel int m_offset; ///< Offset to image data int m_subimage_size; ///< Length (in bytes) of image data int m_palette_size; ///< Number of colours in palette (0 means 256) png_structp m_png; ///< PNG read structure pointer png_infop m_info; ///< PNG image info structure pointer int m_color_type; ///< PNG color model type int m_interlace_type; ///< PNG interlace type Imath::Color3f m_bg; ///< PNG background color /// Reset everything to initial state /// void init () { m_subimage = -1; m_file = NULL; m_png = NULL; m_info = NULL; memset (&m_ico, 0, sizeof (m_ico)); m_buf.clear (); } /// Helper function: read the image. /// bool readimg (); /// Helper: read, with error detection /// bool fread (void *buf, size_t itemsize, size_t nitems) { size_t n = ::fread (buf, itemsize, nitems, m_file); if (n != nitems) error ("Read error"); return n == nitems; } }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageInput *ico_input_imageio_create () { return new ICOInput; } OIIO_EXPORT int ico_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* ico_imageio_library_version () { return NULL; } OIIO_EXPORT const char * ico_input_extensions[] = { "ico", NULL }; OIIO_PLUGIN_EXPORTS_END bool ICOInput::open (const std::string &name, ImageSpec &newspec) { m_filename = name; m_file = Filesystem::fopen (name, "rb"); if (! m_file) { error ("Could not open file \"%s\"", name.c_str()); return false; } if (! fread (&m_ico, 1, sizeof(m_ico))) return false; if (bigendian()) { // ICOs are little endian //swap_endian (&m_ico.reserved); // no use flipping, it's 0 anyway swap_endian (&m_ico.type); swap_endian (&m_ico.count); } if (m_ico.reserved != 0 || m_ico.type != 1) { error ("File failed ICO header check"); return false; } // default to subimage #0, according to convention seek_subimage (0, 0, m_spec); newspec = spec (); return true; } bool ICOInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec) { /*std::cerr << "[ico] seeking subimage " << index << " (current " << m_subimage << ") out of " << m_ico.count << "\n";*/ if (miplevel != 0 || subimage < 0 || subimage >= m_ico.count) return false; if (subimage == m_subimage) { newspec = spec(); return true; } // clear the buffer of previous data m_buf.clear (); // deinitialize PNG structs, in case they were used if (m_png && m_info) PNG_pvt::destroy_read_struct (m_png, m_info); m_subimage = subimage; // read subimage header fseek (m_file, sizeof(ico_header) + m_subimage * sizeof(ico_subimage), SEEK_SET); ico_subimage subimg; if (! fread (&subimg, 1, sizeof(subimg))) return false; if (bigendian()) { // ICOs are little endian swap_endian (&subimg.bpp); swap_endian (&subimg.width); swap_endian (&subimg.height); swap_endian (&subimg.len); swap_endian (&subimg.ofs); swap_endian (&subimg.numColours); } fseek (m_file, subimg.ofs, SEEK_SET); // test for a PNG icon char temp[8]; if (! fread (temp, 1, sizeof(temp))) return false; if (temp[1] == 'P' && temp[2] == 'N' && temp[3] == 'G') { // standard PNG initalization if (png_sig_cmp ((png_bytep)temp, 0, 7)) { error ("Subimage failed PNG signature check"); return false; } //std::cerr << "[ico] creating PNG read struct\n"; std::string s = PNG_pvt::create_read_struct (m_png, m_info); if (s.length ()) { error ("%s", s.c_str ()); return false; } //std::cerr << "[ico] reading PNG info\n"; png_init_io (m_png, m_file); png_set_sig_bytes (m_png, 8); // already read 8 bytes PNG_pvt::read_info (m_png, m_info, m_bpp, m_color_type, m_interlace_type, m_bg, m_spec, true); m_spec.attribute ("oiio:BitsPerSample", m_bpp / m_spec.nchannels); newspec = spec (); return true; } // otherwise it's a plain, ol' windoze DIB (device-independent bitmap) // roll back to where we began and read in the DIB header fseek (m_file, subimg.ofs, SEEK_SET); ico_bitmapinfo bmi; if (! fread (&bmi, 1, sizeof(bmi))) return false; if (bigendian()) { // ICOs are little endian // according to MSDN, only these are valid in an ICO DIB header swap_endian (&bmi.size); swap_endian (&bmi.bpp); swap_endian (&bmi.width); swap_endian (&bmi.height); swap_endian (&bmi.len); } /*std::cerr << "[ico] " << (int)subimg.width << "x" << (int)subimg.height << "@" << (int)bmi.bpp << " (subimg len=" << (int)subimg.len << ", bm len=" << (int)bmi.len << ", ofs=" << (int)subimg.ofs << "), c#" << (int)subimg.numColours << ", p#" << (int)subimg.planes << ":" << (int)bmi.planes << "\n";*/ // copy off values for later use m_bpp = bmi.bpp; // some sanity checking if (m_bpp != 1 && m_bpp != 4 && m_bpp != 8 /*&& m_bpp != 16*/ && m_bpp != 24 && m_bpp != 32) { error ("Unsupported image color depth, probably corrupt file"); return false; } m_offset = subimg.ofs; m_subimage_size = subimg.len; // palette size of 0 actually indicates 256 colours m_palette_size = (subimg.numColours == 0 && m_bpp < 16) ? 256 : (int)subimg.numColours; m_spec = ImageSpec ((int)subimg.width, (int)subimg.height, 4, // always RGBA TypeDesc::UINT8); // 4- and 16-bit are expanded to 8bpp m_spec.default_channel_names (); // add 1 bit for < 32bpp images due to the 1-bit alpha mask m_spec.attribute ("oiio:BitsPerSample", m_bpp/m_spec.nchannels + (m_bpp == 32 ? 0 : 1)); /*std::cerr << "[ico] expected bytes: scanline " << m_spec.scanline_bytes() << ", image " << m_spec.image_bytes() << "\n";*/ newspec = spec (); return true; } bool ICOInput::readimg () { if (m_png) { // subimage is a PNG std::string s = PNG_pvt::read_into_buffer (m_png, m_info, m_spec, m_bpp, m_color_type, m_buf); //std::cerr << "[ico] PNG buffer size = " << m_buf.size () << "\n"; if (s.length ()) { error ("%s", s.c_str ()); return false; } return true; } // otherwise we're dealing with a DIB DASSERT (m_spec.scanline_bytes() == ((size_t)m_spec.width * 4)); m_buf.resize (m_spec.image_bytes()); //std::cerr << "[ico] DIB buffer size = " << m_buf.size () << "\n"; // icons < 16bpp are colour-indexed, so load the palette // a palette consists of 4-byte BGR quads, with the last byte unused (reserved) std::vector palette (m_palette_size); if (m_bpp < 16) { // >= 16-bit icons are unpaletted for (int i = 0; i < m_palette_size; i++) if (! fread (&palette[i], 1, sizeof (ico_palette_entry))) return false; } // read the colour data (the 1-bit transparency is added later on) // scanline length in bytes (aligned to a multiple of 32 bits) int slb = (m_spec.width * m_bpp + 7) / 8 // real data bytes + (4 - ((m_spec.width * m_bpp + 7) / 8) % 4) % 4; // padding std::vector scanline (slb); ico_palette_entry *pe; int k; for (int y = m_spec.height - 1; y >= 0; y--) { if (! fread (&scanline[0], 1, slb)) return false; for (int x = 0; x < m_spec.width; x++) { k = y * m_spec.width * 4 + x * 4; // fill the buffer switch (m_bpp) { case 1: pe = &palette[(scanline[x / 8] & (1 << (7 - x % 8))) != 0]; m_buf[k + 0] = pe->r; m_buf[k + 1] = pe->g; m_buf[k + 2] = pe->b; break; case 4: pe = &palette[(scanline[x / 2] & 0xF0) >> 4]; m_buf[k + 0] = pe->r; m_buf[k + 1] = pe->g; m_buf[k + 2] = pe->b; // 2 pixels per byte pe = &palette[scanline[x / 2] & 0x0F]; if (x == m_spec.width - 1) break; // avoid buffer overflows x++; m_buf[k + 4] = pe->r; m_buf[k + 5] = pe->g; m_buf[k + 6] = pe->b; /*std::cerr << "[ico] " << y << " 2*4bit pixel: " << ((int)scanline[x / 2]) << " -> " << ((int)(scanline[x / 2] & 0xF0) >> 4) << " & " << ((int)(scanline[x / 2]) & 0x0F) << "\n";*/ break; case 8: pe = &palette[scanline[x]]; m_buf[k + 0] = pe->r; m_buf[k + 1] = pe->g; m_buf[k + 2] = pe->b; break; // bpp values > 8 mean non-indexed BGR(A) images #if 0 // doesn't seem like ICOs can really be 16-bit, where did I even get // this notion from? case 16: // FIXME: find out exactly which channel gets the 1 extra // bit; currently I assume it's green: 5B, 6G, 5R // extract and shift the bits m_buf[k + 0] = (scanline[x * 2 + 1] & 0x1F) << 3; m_buf[k + 1] = ((scanline[x * 2 + 1] & 0xE0) >> 3) | ((scanline[x * 2 + 0] & 0x07) << 5); m_buf[k + 2] = scanline[x * 2 + 0] & 0xF8; break; #endif case 24: m_buf[k + 0] = scanline[x * 3 + 2]; m_buf[k + 1] = scanline[x * 3 + 1]; m_buf[k + 2] = scanline[x * 3 + 0]; break; case 32: m_buf[k + 0] = scanline[x * 4 + 2]; m_buf[k + 1] = scanline[x * 4 + 1]; m_buf[k + 2] = scanline[x * 4 + 0]; m_buf[k + 3] = scanline[x * 4 + 3]; break; } } } // read the 1-bit transparency for < 32-bit icons if (m_bpp < 32) { // also aligned to a multiple of 32 bits slb = (m_spec.width + 7) / 8 // real data bytes + (4 - ((m_spec.width + 7) / 8) % 4) % 4; // padding scanline.resize (slb); for (int y = m_spec.height -1; y >= 0; y--) { if (! fread (&scanline[0], 1, slb)) return false; for (int x = 0; x < m_spec.width; x += 8) { for (int b = 0; b < 8; b++) { // bit k = y * m_spec.width * 4 + (x + 7 - b) * 4; if (scanline[x / 8] & (1 << b)) m_buf[k + 3] = 0; else m_buf[k + 3] = 255; } } } } return true; } bool ICOInput::close () { if (m_png && m_info) PNG_pvt::destroy_read_struct (m_png, m_info); if (m_file) { fclose (m_file); m_file = NULL; } init(); // Reset to initial state return true; } bool ICOInput::read_native_scanline (int y, int z, void *data) { if (m_buf.empty ()) { if (!readimg ()) return false; } size_t size = spec().scanline_bytes(); //std::cerr << "[ico] reading scanline " << y << " (" << size << " bytes)\n"; memcpy (data, &m_buf[y * size], size); return true; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/ico.imageio/icooutput.cpp0000644000175000017500000004556413151711064022054 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include "ico.h" #include "../png.imageio/png_pvt.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/fmath.h" OIIO_PLUGIN_NAMESPACE_BEGIN using namespace ICO_pvt; class ICOOutput : public ImageOutput { public: ICOOutput (); virtual ~ICOOutput (); virtual const char * format_name (void) const { return "ico"; } virtual int supports (string_view feature) const; virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode=Create); virtual bool close (); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); private: std::string m_filename; ///< Stash the filename FILE *m_file; ///< Open image handle int m_color_type; ///< Requested colour type bool m_want_png; ///< Whether the client requested PNG std::vector m_scratch; ///< Scratch buffer int m_offset; ///< Offset to subimage data chunk int m_xor_slb; ///< XOR mask scanline length in bytes int m_and_slb; ///< AND mask scanline length in bytes int m_bpp; ///< Bits per pixel unsigned int m_dither; std::vector m_tilebuffer; png_structp m_png; ///< PNG read structure pointer png_infop m_info; ///< PNG image info structure pointer std::vector m_pngtext; /// Initialize private members to pre-opened state void init (void) { m_file = NULL; m_png = NULL; m_info = NULL; m_pngtext.clear (); } /// Add a parameter to the output bool put_parameter (const std::string &name, TypeDesc type, const void *data); /// Finish the writing of a PNG subimage void finish_png_image (); /// Helper: read, with error detection /// template bool fwrite (const T *buf, size_t itemsize=sizeof(T), size_t nitems=1) { size_t n = ::fwrite (buf, itemsize, nitems, m_file); if (n != nitems) error ("Write error"); return n == nitems; } /// Helper: read, with error detection /// template bool fread (T *buf, size_t itemsize=sizeof(T), size_t nitems=1) { size_t n = ::fread (buf, itemsize, nitems, m_file); if (n != nitems) error ("Read error"); return n == nitems; } }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *ico_output_imageio_create () { return new ICOOutput; } // OIIO_EXPORT int ico_imageio_version = OIIO_PLUGIN_VERSION; // it's in icoinput.cpp OIIO_EXPORT const char * ico_output_extensions[] = { "ico", NULL }; OIIO_PLUGIN_EXPORTS_END ICOOutput::ICOOutput () { init (); } ICOOutput::~ICOOutput () { // Close, if not already done. close (); } bool ICOOutput::open (const std::string &name, const ImageSpec &userspec, OpenMode mode) { if (mode == AppendMIPLevel) { error ("%s does not support MIP levels", format_name()); return false; } close (); // Close any already-opened file m_spec = userspec; // Stash the spec if (m_spec.format == TypeDesc::UNKNOWN) // if unknown, default to 8 bits m_spec.set_format (TypeDesc::UINT8); // Check for things this format doesn't support if (m_spec.width < 1 || m_spec.height < 1) { error ("Image resolution must be at least 1x1, you asked for %d x %d", m_spec.width, m_spec.height); return false; } else if (m_spec.width > 256 || m_spec.height > 256) { error ("Image resolution must be at most 256x256, you asked for %d x %d", m_spec.width, m_spec.height); return false; } if (m_spec.depth < 1) m_spec.depth = 1; if (m_spec.depth > 1) { error ("%s does not support volume images (depth > 1)", format_name()); return false; } // check if the client wants this subimage written as PNG // also force PNG if image size is 256 because ico_header->width and height // are 8-bit const ImageIOParameter *p = m_spec.find_attribute ("ico:PNG", TypeDesc::TypeInt); m_want_png = (p && *(int *)p->data()) || m_spec.width == 256 || m_spec.height == 256; if (m_want_png) { std::string s = PNG_pvt::create_write_struct (m_png, m_info, m_color_type, m_spec); if (s.length ()) { error ("%s", s.c_str ()); return false; } } else { // reuse PNG constants for DIBs as well switch (m_spec.nchannels) { case 1 : m_color_type = PNG_COLOR_TYPE_GRAY; break; case 2 : m_color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; case 3 : m_color_type = PNG_COLOR_TYPE_RGB; break; case 4 : m_color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; default: error ("ICO only supports 1-4 channels, not %d", m_spec.nchannels); return false; } m_bpp = (m_color_type == PNG_COLOR_TYPE_GRAY_ALPHA || m_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ? 32 : 24; m_xor_slb = (m_spec.width * m_bpp + 7) / 8 // real data bytes + (4 - ((m_spec.width * m_bpp + 7) / 8) % 4) % 4; // padding m_and_slb = (m_spec.width + 7) / 8 // real data bytes + (4 - ((m_spec.width + 7) / 8) % 4) % 4; // padding // Force 8 bit integers if (m_spec.format != TypeDesc::UINT8) m_spec.set_format (TypeDesc::UINT8); } m_dither = (m_spec.format == TypeDesc::UINT8) ? m_spec.get_int_attribute ("oiio:dither", 0) : 0; //std::cerr << "[ico] writing at " << m_bpp << "bpp\n"; m_file = Filesystem::fopen (name, mode == AppendSubimage ? "r+b" : "wb"); if (! m_file) { error ("Could not open file \"%s\"", name.c_str()); return false; } ico_header ico; if (mode == Create) { // creating new file, write ICO header memset (&ico, 0, sizeof(ico)); ico.type = 1; ico.count = 1; if (bigendian()) { // ICOs are little endian swap_endian (&ico.type); swap_endian (&ico.count); } if (!fwrite(&ico)) { return false; } m_offset = sizeof(ico_header) + sizeof(ico_subimage); } else { // we'll be appending data, so see what's already in the file if (! fread (&ico)) return false; if (bigendian()) { // ICOs are little endian swap_endian (&ico.type); swap_endian (&ico.count); } /*std::cerr << "[ico] reserved = " << ico.reserved << " type = " << ico.type << " count = " << ico.count << "\n";*/ if (ico.reserved != 0 || ico.type != 1) { error ("File failed ICO header check"); return false; } // need to move stuff around to make room for another subimage header int subimage = ico.count++; fseek (m_file, 0, SEEK_END); int len = ftell (m_file); unsigned char buf[512]; // append null data at the end of file so that we don't seek beyond eof if (!fwrite (buf, sizeof(ico_subimage))) { return false; } // do the actual moving, 0.5kB per iteration int amount, skip = sizeof (ico_header) + sizeof (ico_subimage) * (subimage - 1); for (int left = len - skip; left > 0; left -= sizeof (buf)) { amount = std::min (left, (int)sizeof (buf)); /*std::cerr << "[ico] moving " << amount << " bytes (" << left << " vs " << sizeof (buf) << ")\n";*/ fseek (m_file, skip + left - amount, SEEK_SET); if (! fread (buf, 1, amount)) return false; fseek (m_file, skip + left - amount + sizeof (ico_subimage), SEEK_SET); if (!fwrite (buf, 1, amount)) { return false; } } // update header fseek (m_file, 0, SEEK_SET); // swap these back to little endian, if needed if (bigendian()) { swap_endian (&ico.type); swap_endian (&ico.count); } if (!fwrite(&ico)) { return false; } // and finally, update the offsets in subimage headers to point to // their data correctly uint32_t temp; fseek (m_file, offsetof (ico_subimage, ofs), SEEK_CUR); for (int i = 0; i < subimage; i++) { if (! fread (&temp)) return false; if (bigendian()) swap_endian (&temp); temp += sizeof (ico_subimage); if (bigendian()) swap_endian (&temp); // roll back 4 bytes, we need to rewrite the value we just read fseek (m_file, -4, SEEK_CUR); if (!fwrite(&temp)) { return false; } // skip to the next subimage; subtract 4 bytes because that's how // much we've just written fseek (m_file, sizeof (ico_subimage) - 4, SEEK_CUR); } // offset at which we'll be writing new image data m_offset = len + sizeof (ico_subimage); // next part of code expects the file pointer to be where the new // subimage header is to be written fseek (m_file, sizeof (ico_header) + subimage * sizeof (ico_subimage), SEEK_SET); } // write subimage header ico_subimage subimg; memset (&subimg, 0, sizeof(subimg)); subimg.width = m_spec.width; subimg.height = m_spec.height; subimg.bpp = m_bpp; if (!m_want_png) subimg.len = sizeof (ico_bitmapinfo) // for PNG images this is zero + (m_xor_slb + m_and_slb) * m_spec.height; subimg.ofs = m_offset; if (bigendian()) { swap_endian (&subimg.width); swap_endian (&subimg.height); swap_endian (&subimg.planes); swap_endian (&subimg.bpp); swap_endian (&subimg.len); swap_endian (&subimg.ofs); } if (!fwrite(&subimg)) { return false; } fseek (m_file, m_offset, SEEK_SET); if (m_want_png) { // unused still, should do conversion to unassociated bool convert_alpha; float gamma; png_init_io (m_png, m_file); png_set_compression_level (m_png, Z_BEST_COMPRESSION); PNG_pvt::write_info (m_png, m_info, m_color_type, m_spec, m_pngtext, convert_alpha, gamma); } else { // write DIB header ico_bitmapinfo bmi; memset (&bmi, 0, sizeof (bmi)); bmi.size = sizeof (bmi); bmi.width = m_spec.width; // this value is sum of heights of both XOR and AND masks bmi.height = m_spec.height * 2; bmi.bpp = m_bpp; bmi.planes = 1; bmi.len = subimg.len - sizeof (ico_bitmapinfo); if (bigendian()) { // ICOs are little endian swap_endian (&bmi.size); swap_endian (&bmi.bpp); swap_endian (&bmi.width); swap_endian (&bmi.height); swap_endian (&bmi.len); } if (!fwrite(&bmi)) { return false; } // append null data so that we don't seek beyond eof in write_scanline char buf[512]; memset (buf, 0, sizeof (buf)); for (int left = bmi.len; left > 0; left -= sizeof (buf)) { if (! fwrite (buf, 1, std::min (left, (int)sizeof (buf)))) { return false; } } fseek (m_file, m_offset + sizeof (bmi), SEEK_SET); } // If user asked for tiles -- which this format doesn't support, emulate // it by buffering the whole image. if (m_spec.tile_width && m_spec.tile_height) m_tilebuffer.resize (m_spec.image_bytes()); return true; } int ICOOutput::supports (string_view feature) const { // advertise our support for subimages if (Strutil::iequals (feature, "multiimage")) return true; if (Strutil::iequals (feature, "alpha")) return true; return false; } bool ICOOutput::close () { if (! m_file) { // already closed init (); return true; } bool ok = true; if (m_spec.tile_width) { // Handle tile emulation -- output the buffered pixels ASSERT (m_tilebuffer.size()); ok &= write_scanlines (m_spec.y, m_spec.y+m_spec.height, 0, m_spec.format, &m_tilebuffer[0]); std::vector().swap (m_tilebuffer); } if (m_png && m_info) { PNG_pvt::finish_image (m_png); PNG_pvt::destroy_write_struct (m_png, m_info); } fclose (m_file); m_file = NULL; init (); // re-initialize return ok; } bool ICOOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { m_spec.auto_stride (xstride, format, spec().nchannels); const void *origdata = data; data = to_native_scanline (format, data, xstride, m_scratch, m_dither, y, z); if (data == origdata) { m_scratch.assign ((unsigned char *)data, (unsigned char *)data + m_spec.scanline_bytes()); data = &m_scratch[0]; } if (m_want_png) { if (!PNG_pvt::write_row (m_png, (png_byte *)data)) { error ("PNG library error"); return false; } } else { unsigned char *bdata = (unsigned char *)data; unsigned char buf[4]; fseek (m_file, m_offset + sizeof (ico_bitmapinfo) + (m_spec.height - y - 1) * m_xor_slb, SEEK_SET); // write the XOR mask size_t buff_size = 0; for (int x = 0; x < m_spec.width; x++) { switch (m_color_type) { // reuse PNG constants case PNG_COLOR_TYPE_GRAY: buf[0] = buf[1] = buf[2] = bdata[x]; buff_size = 3; break; case PNG_COLOR_TYPE_GRAY_ALPHA: buf[0] = buf[1] = buf[2] = bdata[x * 2 + 0]; buf[3] = bdata[x * 2 + 1]; buff_size = 4; break; case PNG_COLOR_TYPE_RGB: buf[0] = bdata[x * 3 + 2]; buf[1] = bdata[x * 3 + 1]; buf[2] = bdata[x * 3 + 0]; buff_size = 3; break; case PNG_COLOR_TYPE_RGB_ALPHA: buf[0] = bdata[x * 4 + 2]; buf[1] = bdata[x * 4 + 1]; buf[2] = bdata[x * 4 + 0]; buf[3] = bdata[x * 4 + 3]; buff_size = 4; break; } if (!fwrite (buf, 1, buff_size)) { return false; } } fseek (m_file, m_offset + sizeof (ico_bitmapinfo) + m_spec.height * m_xor_slb + (m_spec.height - y - 1) * m_and_slb, SEEK_SET); // write the AND mask // It's required even for 32-bit images because it can be used when // drawing at colour depths lower than 24-bit. If it's not present, // Windows will read out-of-bounds, treating any data that it // encounters as the AND mask, resulting in ugly transparency effects. // Only need to do this for images with alpha, becasue 0 is opaque, // and we've already filled the file with zeros. if (m_color_type != PNG_COLOR_TYPE_GRAY && m_color_type != PNG_COLOR_TYPE_RGB) { for (int x = 0; x < m_spec.width; x += 8) { buf[0] = 0; for (int b = 0; b < 8 && x + b < m_spec.width; b++) { switch (m_color_type) { case PNG_COLOR_TYPE_GRAY_ALPHA: buf[0] |= bdata[(x + b) * 2 + 1] <= 127 ? (1 << (7 - b)) : 0; break; case PNG_COLOR_TYPE_RGB_ALPHA: buf[0] |= bdata[(x + b) * 4 + 3] <= 127 ? (1 << (7 - b)) : 0; break; } } if (!fwrite(&buf[0], 1, 1)) { return false; } } } } return true; } bool ICOOutput::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { // Emulate tiles by buffering the whole image return copy_tile_to_image_buffer (x, y, z, format, data, xstride, ystride, zstride, &m_tilebuffer[0]); } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/softimage.imageio/0000755000175000017500000000000013151711064020503 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/softimage.imageio/CMakeLists.txt0000644000175000017500000000011313151711064023236 0ustar mfvmfvadd_oiio_plugin (softimageinput.cpp softimageoutput.cpp softimage_pvt.cpp) openimageio-1.7.17~dfsg0.orig/src/softimage.imageio/softimage_pvt.cpp0000644000175000017500000000574113151711064024065 0ustar mfvmfv/* OpenImageIO and all code, documentation, and other materials contained therein are: Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "softimage_pvt.h" OIIO_PLUGIN_NAMESPACE_BEGIN // Obligatory material to make this a recognizeable imageio plugin OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT int softimage_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* softimage_imageio_library_version () { return NULL; } OIIO_PLUGIN_EXPORTS_END namespace softimage_pvt { void PicFileHeader::swap_endian() { OIIO::swap_endian (&magic); OIIO::swap_endian (&width); OIIO::swap_endian (&height); OIIO::swap_endian (&version); OIIO::swap_endian (&ratio); OIIO::swap_endian (&fields); } bool PicFileHeader::read_header (FILE* fd) { int byte_count = 0; byte_count += fread (this, 1, sizeof (PicFileHeader), fd); // Check if we're running on a little endian processor if (littleendian ()) swap_endian(); return (byte_count == sizeof (PicFileHeader)); } std::vector ChannelPacket::channels () const { std::vector chanMap; // Check for the channels and add them to the chanMap if (channelCode & RED_CHANNEL) chanMap.push_back (0); if (channelCode & GREEN_CHANNEL) chanMap.push_back (1); if (channelCode & BLUE_CHANNEL) chanMap.push_back (2); if (channelCode & ALPHA_CHANNEL) chanMap.push_back (3); return chanMap; } } // namespace softimage_pvt OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/softimage.imageio/softimageinput.cpp0000644000175000017500000004343513151711064024256 0ustar mfvmfv/* OpenImageIO and all code, documentation, and other materials contained therein are: Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "softimage_pvt.h" OIIO_PLUGIN_NAMESPACE_BEGIN using namespace softimage_pvt; class SoftimageInput : public ImageInput { public: SoftimageInput() { init(); } virtual ~SoftimageInput() { close(); } virtual const char *format_name (void) const { return "softimage"; } virtual bool open (const std::string &name, ImageSpec &spec); virtual bool close(); virtual bool read_native_scanline (int y, int z, void *data); private: /// Resets the core data members to defaults. /// void init (); /// Read a scanline from m_fd. /// bool read_next_scanline (void * data); /// Read uncompressed pixel data from m_fd. /// bool read_pixels_uncompressed (const softimage_pvt::ChannelPacket & curPacket, void * data); /// Read pure run length encoded pixels. /// bool read_pixels_pure_run_length (const softimage_pvt::ChannelPacket & curPacket, void * data); /// Read mixed run length encoded pixels. /// bool read_pixels_mixed_run_length (const softimage_pvt::ChannelPacket & curPacket, void * data); FILE *m_fd; softimage_pvt::PicFileHeader m_pic_header; std::vector m_channel_packets; std::string m_filename; std::vector m_scanline_markers; }; // symbols required for OpenImageIO plugin OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageInput *softimage_input_imageio_create() { return new SoftimageInput; } OIIO_EXPORT const char *softimage_input_extensions[] = { "pic", NULL }; OIIO_PLUGIN_EXPORTS_END void SoftimageInput::init () { m_fd = NULL; m_filename.clear(); m_channel_packets.clear(); m_scanline_markers.clear(); } bool SoftimageInput::open (const std::string& name, ImageSpec& spec) { // Remember the filename m_filename = name; m_fd = Filesystem::fopen (m_filename, "rb"); if (!m_fd) { error ("Could not open file \"%s\"", name.c_str()); return false; } // Try read the header if (! m_pic_header.read_header (m_fd)) { error ("\"%s\": failed to read header", m_filename.c_str()); close(); return false; } // Check whether it has the pic magic number if (m_pic_header.magic != 0x5380f634) { error ("\"%s\" is not a Softimage Pic file, magic number of 0x%X is not Pic", m_filename.c_str(), m_pic_header.magic); close(); return false; } // Get the ChannelPackets ChannelPacket curPacket; int nchannels = 0; do { // Read the next packet into curPacket and store it off if (fread (&curPacket, 1, sizeof (ChannelPacket), m_fd) != sizeof (ChannelPacket)) { error ("Unexpected end of file \"%s\".", m_filename.c_str()); close(); return false; } m_channel_packets.push_back (curPacket); // Add the number of channels in this packet to nchannels nchannels += curPacket.channels().size(); } while (curPacket.chained); // Get the depth per pixel per channel TypeDesc chanType = TypeDesc::UINT8; if (curPacket.size == 16) chanType = TypeDesc::UINT16; // Set the details in the ImageSpec m_spec = ImageSpec (m_pic_header.width, m_pic_header.height, nchannels, chanType); m_spec.attribute ("BitsPerSample", (int)curPacket.size); if (m_pic_header.comment[0] != 0) { char comment[81]; strncpy (comment, m_pic_header.comment, 80); comment[80] = 0; m_spec.attribute ("ImageDescription", comment); } // Build the scanline index fpos_t curPos; fgetpos (m_fd, &curPos); m_scanline_markers.push_back(curPos); spec = m_spec; return true; } bool SoftimageInput::read_native_scanline (int y, int z, void* data) { bool result = false; if (y == (int)m_scanline_markers.size() - 1) { // we're up to this scanline result = read_next_scanline(data); // save the marker for the next scanline if we haven't got the who images if (m_scanline_markers.size() < m_pic_header.height) { fpos_t curPos; fgetpos(m_fd, &curPos); m_scanline_markers.push_back(curPos); } } else if (y >= (int)m_scanline_markers.size()) { // we haven't yet read this far fpos_t curPos; // Store the ones before this without pulling the pixels do { if (!read_next_scanline(NULL)) return false; fgetpos(m_fd, &curPos); m_scanline_markers.push_back(curPos); } while ((int)m_scanline_markers.size() <= y); result = read_next_scanline(data); fgetpos(m_fd, &curPos); m_scanline_markers.push_back(curPos); } else { // We've already got the index for this scanline and moved past // Let's seek to the scanline's data if (fsetpos (m_fd, &m_scanline_markers[y])) { error ("Failed to seek to scanline %d in \"%s\"", y, m_filename.c_str()); close(); return false; } result = read_next_scanline(data); // If the index isn't complete let's shift the file pointer back to the latest readline if (m_scanline_markers.size() < m_pic_header.height) { if (fsetpos (m_fd, &m_scanline_markers[m_scanline_markers.size() - 1])) { error ("Failed to restore to scanline %llu in \"%s\"", (long long unsigned int)m_scanline_markers.size() - 1, m_filename.c_str()); close(); return false; } } } return result; } bool SoftimageInput::close() { if (m_fd) { fclose (m_fd); m_fd = NULL; } init (); return true; } inline bool SoftimageInput::read_next_scanline (void * data) { // Each scanline is stored using one or more channel packets. // We go through each of those to pull the data for (size_t i = 0; i < m_channel_packets.size(); i++) { if (m_channel_packets[i].type & UNCOMPRESSED) { if (!read_pixels_uncompressed (m_channel_packets[i], data)) { error ("Failed to read uncompressed pixel data from \"%s\"", m_filename.c_str()); close(); return false; } } else if (m_channel_packets[i].type & PURE_RUN_LENGTH) { if (!read_pixels_pure_run_length (m_channel_packets[i], data)) { error ("Failed to read pure run length encoded pixel data from \"%s\"", m_filename.c_str()); close(); return false; } } else if (m_channel_packets[i].type & MIXED_RUN_LENGTH) { if (!read_pixels_mixed_run_length (m_channel_packets[i], data)) { error ("Failed to read mixed run length encoded pixel data from \"%s\"", m_filename.c_str()); close(); return false; } } } return true; } inline bool SoftimageInput::read_pixels_uncompressed (const softimage_pvt::ChannelPacket & curPacket, void * data) { // We're going to need to use the channels more than once std::vector channels = curPacket.channels(); // We'll need to use the pixelChannelSize a bit size_t pixelChannelSize = curPacket.size / 8; if (data) { // data pointer is set so we're supposed to write data there uint8_t * scanlineData = (uint8_t *)data; for (size_t pixelX=0; pixelX < m_pic_header.width; pixelX++) { for (size_t curChan=0; curChan < channels.size(); curChan++) { for (size_t byte=0; byte < pixelChannelSize; byte++) { // Get which byte we should be placing this in depending on endianness size_t curByte = byte; if (littleendian()) curByte = ((pixelChannelSize) - 1) - curByte; //read the data into the correct place if (fread (&scanlineData[(pixelX * pixelChannelSize * m_spec.nchannels) + (channels[curChan] * pixelChannelSize) + curByte], 1, 1, m_fd) != 1) return false; } } } } else { // data pointer is null so we should just seek to the next scanline // If the seek fails return false if (fseek (m_fd, m_pic_header.width * pixelChannelSize * channels.size(), SEEK_CUR)) return false; } return true; } inline bool SoftimageInput::read_pixels_pure_run_length (const softimage_pvt::ChannelPacket & curPacket, void * data) { // How many pixels we've read so far this line size_t linePixelCount = 0; // Number of repeats of this value uint8_t curCount = 0; // We'll need to use the pixelChannelSize a bit size_t pixelChannelSize = curPacket.size / 8; // We're going to need to use the channels more than once std::vector channels = curPacket.channels(); // Read the pixels until we've read them all while (linePixelCount < m_pic_header.width) { // Read the repeats for the run length - return false if read fails if (fread (&curCount, 1, 1, m_fd) != 1) return false; if (data) { // data pointer is set so we're supposed to write data there size_t pixelSize = pixelChannelSize * channels.size(); uint8_t * pixelData = new uint8_t[pixelSize]; if (fread (pixelData, pixelSize, 1, m_fd) != pixelSize) return false; // Now we've got the pixel value we need to push it into the data uint8_t * scanlineData = (uint8_t *)data; for (size_t pixelX=linePixelCount; pixelX < linePixelCount+curCount; pixelX++) { for (size_t curChan=0; curChan < channels.size(); curChan++) { for (size_t byte=0; byte < pixelChannelSize; byte++) { // Get which byte we should be placing this in depending on endianness size_t curByte = byte; if (littleendian()) curByte = ((pixelChannelSize) - 1) - curByte; //put the data into the correct place scanlineData[(pixelX * pixelChannelSize * m_spec.nchannels) + (channels[curChan] * pixelChannelSize) + curByte] = pixelData[(curChan * pixelChannelSize) + curByte]; } } } delete[] pixelData; } else { // data pointer is null so we should just seek to the next scanline // If the seek fails return false if (fseek (m_fd, pixelChannelSize * channels.size(), SEEK_CUR)) return false; } // Add these pixels to the current pixel count linePixelCount += curCount; } return true; } inline bool SoftimageInput::read_pixels_mixed_run_length (const softimage_pvt::ChannelPacket & curPacket, void * data) { // How many pixels we've read so far this line size_t linePixelCount = 0; // Number of repeats of this value uint8_t curCount = 0; // We'll need to use the pixelChannelSize a bit size_t pixelChannelSize = curPacket.size / 8; // We're going to need to use the channels more than once std::vector channels = curPacket.channels(); // Read the pixels until we've read them all while (linePixelCount < m_pic_header.width) { // Read the repeats for the run length - return false if read fails if (fread (&curCount, 1, 1, m_fd) != 1) return false; if (curCount < 128) { // It's a raw packet - so this means the count is 1 less then the actual value curCount++; // Just to be safe let's make sure this wouldn't take us // past the end of this scanline if (curCount + linePixelCount > m_pic_header.width) curCount = m_pic_header.width - linePixelCount; if (data) { // data pointer is set so we're supposed to write data there uint8_t * scanlineData = (uint8_t *)data; for (size_t pixelX=linePixelCount; pixelX < linePixelCount+curCount; pixelX++) { for (size_t curChan=0; curChan < channels.size(); curChan++) { for (size_t byte=0; byte < pixelChannelSize; byte++) { // Get which byte we should be placing this in depending on endianness size_t curByte = byte; if (littleendian()) curByte = ((pixelChannelSize) - 1) - curByte; //read the data into the correct place if (fread (&scanlineData[(pixelX * pixelChannelSize * m_spec.nchannels) + (channels[curChan] * pixelChannelSize) + curByte], 1, 1, m_fd) != 1) return false; } } } } else { // data pointer is null so we should just seek to the // next scanline If the seek fails return false. if (fseek (m_fd, curCount * pixelChannelSize * channels.size(), SEEK_CUR)) return false; } // Add these pixels to the current pixel count linePixelCount += curCount; } else { // It's a run length encoded packet uint16_t longCount = 0; if (curCount == 128) { // This is a long count so the next 16bits of the file // are an unsigned int containing the count. If the // read fails we should return false. if (fread (&longCount, 1, 2, m_fd) != 2) return false; // longCount is in big endian format - if we're not // let's swap it if (littleendian()) OIIO::swap_endian (&longCount); } else { longCount = curCount - 127; } if (data) { // data pointer is set so we're supposed to write data there size_t pixelSize = pixelChannelSize * channels.size(); uint8_t * pixelData = new uint8_t[pixelSize]; if (fread (pixelData, 1, pixelSize, m_fd) != pixelSize) return false; // Now we've got the pixel value we need to push it into // the data. uint8_t * scanlineData = (uint8_t *)data; for (size_t pixelX=linePixelCount; pixelX < linePixelCount+longCount; pixelX++) { for (size_t curChan=0; curChan < channels.size(); curChan++) { for (size_t byte=0; byte < pixelChannelSize; byte++) { // Get which byte we should be placing this // in depending on endianness. size_t curByte = byte; if (littleendian()) curByte = ((pixelChannelSize) - 1) - curByte; //put the data into the correct place scanlineData[(pixelX * pixelChannelSize * m_spec.nchannels) + (channels[curChan] * pixelChannelSize) + curByte] = pixelData[(curChan * pixelChannelSize) + curByte]; } } } delete[] pixelData; } else { // data pointer is null so we should just seek to the // next scanline. If the seek fails return false. if (fseek (m_fd, pixelChannelSize * channels.size(), SEEK_CUR)) return false; } // Add these pixels to the current pixel count. linePixelCount += longCount; } } return true; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/softimage.imageio/softimage_pvt.h0000644000175000017500000000657113151711064023534 0ustar mfvmfv/* OpenImageIO and all code, documentation, and other materials contained therein are: Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_SOFTIMAGE_H #define OPENIMAGEIO_SOFTIMAGE_H #include #include "OpenImageIO/fmath.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/filesystem.h" OIIO_PLUGIN_NAMESPACE_BEGIN namespace softimage_pvt { class PicFileHeader { public: // Read pic header from file bool read_header (FILE *fd); // PIC header uint32_t magic; // Softimage magic number float version; // Storage format - 1 is RLE, 0 is RAW char comment[80]; // Comment char id[4]; // ID - should be PICT uint16_t width; // X size in pixels uint16_t height; // Y size in pixels float ratio; // Pixel aspect ratio uint16_t fields; // The scanline setting - No Pictures, Odd, Even or every uint16_t pad; // unused private: void swap_endian(); }; // class PicFileHeader class ChannelPacket { public: //channel packet contains info on the image data ChannelPacket() { chained = 0; } // !brief get a list of the channels contained in this channel packet std::vector channels() const; uint8_t chained; //0 if this is the last channel packet uint8_t size; //Number of bits per pixel per channel uint8_t type; //Data encoding and type uint8_t channelCode; //bitset for channels }; // class ChannelPacket enum channelCodes { RED_CHANNEL = 0x80, GREEN_CHANNEL = 0x40, BLUE_CHANNEL = 0x20, ALPHA_CHANNEL = 0x10 }; // enum channelCodes enum encoding { UNCOMPRESSED, PURE_RUN_LENGTH, MIXED_RUN_LENGTH }; // enum encoding } //namespace softimage_pvt OIIO_PLUGIN_NAMESPACE_END #endif // OPENIMAGEIO_PIC_H openimageio-1.7.17~dfsg0.orig/src/softimage.imageio/softimageoutput.cpp0000644000175000017500000000402513151711064024447 0ustar mfvmfv/* OpenImageIO and all code, documentation, and other materials contained therein are: Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "softimage_pvt.h" OIIO_PLUGIN_NAMESPACE_BEGIN using namespace softimage_pvt; // symbols required for OpenImageIO plugin OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *softimage_output_imageio_create() { return NULL; // new SoftimageOutput; } OIIO_EXPORT const char *softimage_output_extensions[] = { "pic", NULL }; OIIO_PLUGIN_EXPORTS_END OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/sgi.imageio/0000755000175000017500000000000013151711064017307 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/sgi.imageio/CMakeLists.txt0000644000175000017500000000005513151711064022047 0ustar mfvmfvadd_oiio_plugin (sgiinput.cpp sgioutput.cpp) openimageio-1.7.17~dfsg0.orig/src/sgi.imageio/sgi_pvt.h0000644000175000017500000001515713151711064021144 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_SGI_H #define OPENIMAGEIO_SGI_H // Format reference: ftp://ftp.sgi.com/graphics/SGIIMAGESPEC #include #include "OpenImageIO/imageio.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" OIIO_PLUGIN_NAMESPACE_BEGIN namespace sgi_pvt { // magic number identifying SGI file const short SGI_MAGIC = 0x01DA; // SGI file header - all fields are written in big-endian to file struct SgiHeader { int16_t magic; // must be 0xDA01 (big-endian) int8_t storage; // compression used, see StorageFormat enum int8_t bpc; // number of bytes per pixel channel uint16_t dimension; // dimension of he image, see Dimension uint16_t xsize; // width in pixels uint16_t ysize; // height in pixels uint16_t zsize; // number of channels: 1(B/W), 3(RGB) or 4(RGBA) int32_t pixmin; // minimum pixel value int32_t pixmax; // maximum pixel value int32_t dummy; // unused, should be set to 0 char imagename[80]; // null terminated ASCII string int32_t colormap; // how pixels should be interpreted // see ColorMap enum }; // size of the header with all dummy bytes const int SGI_HEADER_LEN = 512; enum StorageFormat { VERBATIM= 0, // uncompressed RLE // RLE compressed }; enum Dimension { ONE_SCANLINE_ONE_CHANNEL = 1, // single scanline and single channel MULTI_SCANLINE_ONE_CHANNEL, // multiscanline, single channel MULTI_SCANLINE_MULTI_CHANNEL // multiscanline, multichannel }; enum ColorMap { NORMAL = 0, // B/W image for 1 channel, RGB for 3 channels and RGBA for 4 DITHERED, // only one channel of data, RGB values are packed int one byte: // red and gree - 3 bits, blue - 2 bits; obsolete SCREEN, // obsolete COLORMAP // TODO: what is this? }; } // namespace sgi_pvt class SgiInput : public ImageInput { public: SgiInput () { init(); } virtual ~SgiInput () { close(); } virtual const char *format_name (void) const { return "sgi"; } virtual bool valid_file (const std::string &filename) const; virtual bool open (const std::string &name, ImageSpec &spec); virtual bool close (void); virtual bool read_native_scanline (int y, int z, void *data); private: FILE *m_fd; std::string m_filename; sgi_pvt::SgiHeader m_sgi_header; std::vector start_tab; std::vector length_tab; void init() { m_fd = NULL; memset (&m_sgi_header, 0, sizeof(m_sgi_header)); } // reads SGI file header (512 bytes) into m_sgi_header // Return true if ok, false if there was a read error. bool read_header(); // reads RLE scanline start offset and RLE scanline length tables // RLE scanline start offset is stored in start_tab // RLE scanline length is stored in length_tab // Return true if ok, false if there was a read error. bool read_offset_tables(); // read channel scanline data from file, uncompress it and save the data to // 'out' buffer; 'out' should be allocate before call to this method. // Return true if ok, false if there was a read error. bool uncompress_rle_channel (int scanline_off, int scanline_len, unsigned char *out); /// Helper: read, with error detection /// bool fread (void *buf, size_t itemsize, size_t nitems) { size_t n = ::fread (buf, itemsize, nitems, m_fd); if (n != nitems) error ("Read error"); return n == nitems; } }; class SgiOutput : public ImageOutput { public: SgiOutput () : m_fd(NULL) { } virtual ~SgiOutput () { close(); } virtual const char *format_name (void) const { return "sgi"; } virtual int supports (string_view feature) const; virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode=Create); virtual bool close (void); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); private: FILE *m_fd; std::string m_filename; std::vector m_scratch; unsigned int m_dither; std::vector m_tilebuffer; void init () { m_fd = NULL; } bool create_and_write_header(); /// Helper - write, with error detection template bool fwrite (const T *buf, size_t itemsize=sizeof(T), size_t nitems=1) { size_t n = std::fwrite (buf, itemsize, nitems, m_fd); if (n != nitems) error ("Error writing \"%s\" (wrote %d/%d records)", m_filename, (int)n, (int)nitems); return n == nitems; } }; OIIO_PLUGIN_NAMESPACE_END #endif // OPENIMAGEIO_SGI_H openimageio-1.7.17~dfsg0.orig/src/sgi.imageio/sgioutput.cpp0000644000175000017500000001751713151711064022071 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "sgi_pvt.h" OIIO_PLUGIN_NAMESPACE_BEGIN // Obligatory material to make this a recognizeable imageio plugin OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *sgi_output_imageio_create () { return new SgiOutput; } OIIO_EXPORT const char *sgi_output_extensions[] = { "sgi", "rgb", "rgba", "bw", "int", "inta", NULL }; OIIO_PLUGIN_EXPORTS_END int SgiOutput::supports (string_view feature) const { return (feature == "alpha" || feature == "nchannels"); } bool SgiOutput::open (const std::string &name, const ImageSpec &spec, OpenMode mode) { if (mode != Create) { error ("%s does not support subimages or MIP levels", format_name()); return false; } close (); // Close any already-opened file // saving 'name' and 'spec' for later use m_filename = name; m_spec = spec; m_fd = Filesystem::fopen (m_filename, "wb"); if (!m_fd) { error ("Unable to open file \"%s\"", m_filename.c_str ()); return false; } // SGI image files only supports UINT8 and UINT16. If something // else was requested, revert to the one most likely to be readable // by any SGI reader: UINT8 if (m_spec.format != TypeDesc::UINT8 && m_spec.format != TypeDesc::UINT16) m_spec.set_format (TypeDesc::UINT8); m_dither = (m_spec.format == TypeDesc::UINT8) ? m_spec.get_int_attribute ("oiio:dither", 0) : 0; // If user asked for tiles -- which this format doesn't support, emulate // it by buffering the whole image. if (m_spec.tile_width && m_spec.tile_height) m_tilebuffer.resize (m_spec.image_bytes()); return create_and_write_header(); } bool SgiOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { y = m_spec.height - y - 1; data = to_native_scanline (format, data, xstride, m_scratch, m_dither, y, z); // In SGI format all channels are saved to file separately: firsty all // channel 1 scanlines are saved, then all channel2 scanlines are saved // and so on. // // Note that since SGI images are pretty archaic and most probably // people won't be too picky about full flexibility writing them, we // content ourselves with only writing uncompressed data, and don't // attempt to write with RLE encoding. int bpc = m_spec.format.size(); // bytes per channel std::vector channeldata (m_spec.width * bpc); for (int c = 0; c < m_spec.nchannels; ++c) { unsigned char *cdata = (unsigned char *)data + c*bpc; for (int x = 0; x < m_spec.width; ++x) { channeldata[x*bpc] = cdata[0]; if (bpc == 2) channeldata[x*bpc+1] = cdata[1]; cdata += m_spec.nchannels * bpc; // advance to next pixel } if (bpc == 2 && littleendian()) swap_endian ((unsigned short *)&channeldata[0], m_spec.width); long scanline_offset = sgi_pvt::SGI_HEADER_LEN + (c * m_spec.height + y) * m_spec.width * bpc; fseek (m_fd, scanline_offset, SEEK_SET); if (!fwrite (&channeldata[0], 1, m_spec.width * bpc)) { return false; } } return true; } bool SgiOutput::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { // Emulate tiles by buffering the whole image return copy_tile_to_image_buffer (x, y, z, format, data, xstride, ystride, zstride, &m_tilebuffer[0]); } bool SgiOutput::close () { if (! m_fd) { // already closed init (); return true; } bool ok = true; if (m_spec.tile_width) { // Handle tile emulation -- output the buffered pixels ASSERT (m_tilebuffer.size()); ok &= write_scanlines (m_spec.y, m_spec.y+m_spec.height, 0, m_spec.format, &m_tilebuffer[0]); std::vector().swap (m_tilebuffer); } fclose (m_fd); init (); return ok; } bool SgiOutput::create_and_write_header() { sgi_pvt::SgiHeader sgi_header; sgi_header.magic = sgi_pvt::SGI_MAGIC; sgi_header.storage = sgi_pvt::VERBATIM; sgi_header.bpc = m_spec.format.size(); if (m_spec.height == 1 && m_spec.nchannels == 1) sgi_header.dimension = sgi_pvt::ONE_SCANLINE_ONE_CHANNEL; else if (m_spec.nchannels == 1) sgi_header.dimension = sgi_pvt::MULTI_SCANLINE_ONE_CHANNEL; else sgi_header.dimension = sgi_pvt::MULTI_SCANLINE_MULTI_CHANNEL; sgi_header.xsize = m_spec.width; sgi_header.ysize = m_spec.height; sgi_header.zsize = m_spec.nchannels; sgi_header.pixmin = 0; sgi_header.pixmax = (sgi_header.bpc == 1) ? 255 : 65535; sgi_header.dummy = 0; ImageIOParameter *ip = m_spec.find_attribute ("ImageDescription", TypeDesc::STRING); if (ip && ip->data()) { const char** img_descr = (const char**)ip->data(); strncpy (sgi_header.imagename, *img_descr, 80); sgi_header.imagename[79] = 0; } sgi_header.colormap = sgi_pvt::NORMAL; if (littleendian()) { swap_endian(&sgi_header.magic); swap_endian(&sgi_header.dimension); swap_endian(&sgi_header.xsize); swap_endian(&sgi_header.ysize); swap_endian(&sgi_header.zsize); swap_endian(&sgi_header.pixmin); swap_endian(&sgi_header.pixmax); swap_endian(&sgi_header.colormap); } char dummy[404] = {0}; if (!fwrite(&sgi_header.magic) || !fwrite(&sgi_header.storage) || !fwrite(&sgi_header.bpc) || !fwrite(&sgi_header.dimension) || !fwrite(&sgi_header.xsize) || !fwrite(&sgi_header.ysize) || !fwrite(&sgi_header.zsize) || !fwrite(&sgi_header.pixmin) || !fwrite(&sgi_header.pixmax) || !fwrite(&sgi_header.dummy) || !fwrite(sgi_header.imagename, 1, 80) || !fwrite(&sgi_header.colormap) || !fwrite(dummy, 404, 1)) { error ("Error writing to \"%s\"", m_filename); return false; } return true; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/sgi.imageio/sgiinput.cpp0000644000175000017500000002535113151711064021663 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "sgi_pvt.h" #include "OpenImageIO/dassert.h" OIIO_PLUGIN_NAMESPACE_BEGIN // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT int sgi_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* sgi_imageio_library_version () { return NULL; } OIIO_EXPORT ImageInput *sgi_input_imageio_create () { return new SgiInput; } OIIO_EXPORT const char *sgi_input_extensions[] = { "sgi", "rgb", "rgba", "bw", "int", "inta", NULL }; OIIO_PLUGIN_EXPORTS_END bool SgiInput::valid_file (const std::string &filename) const { FILE *fd = Filesystem::fopen (filename, "rb"); if (!fd) return false; int16_t magic; bool ok = (::fread (&magic, sizeof(magic), 1, fd) == 1) && (magic == sgi_pvt::SGI_MAGIC); fclose (fd); return ok; } bool SgiInput::open (const std::string &name, ImageSpec &spec) { // saving name for later use m_filename = name; m_fd = Filesystem::fopen (m_filename, "rb"); if (!m_fd) { error ("Could not open file \"%s\"", name.c_str()); return false; } if (! read_header ()) return false; if (m_sgi_header.magic != sgi_pvt::SGI_MAGIC) { error ("\"%s\" is not a SGI file, magic number doesn't match", m_filename.c_str()); close (); return false; } int height = 0; int nchannels = 0; switch (m_sgi_header.dimension) { case sgi_pvt::ONE_SCANLINE_ONE_CHANNEL: height = 1; nchannels = 1; break; case sgi_pvt::MULTI_SCANLINE_ONE_CHANNEL: height = m_sgi_header.ysize; nchannels = 1; break; case sgi_pvt::MULTI_SCANLINE_MULTI_CHANNEL: height = m_sgi_header.ysize; nchannels = m_sgi_header.zsize; break; default: error ("Bad dimension: %d", m_sgi_header.dimension); close (); return false; } if (m_sgi_header.colormap == sgi_pvt::COLORMAP || m_sgi_header.colormap == sgi_pvt::SCREEN) { error ("COLORMAP and SCREEN color map types aren't supported"); close (); return false; } m_spec = ImageSpec (m_sgi_header.xsize, height, nchannels, m_sgi_header.bpc == 1 ? TypeDesc::UINT8 : TypeDesc::UINT16); if (strlen (m_sgi_header.imagename)) m_spec.attribute("ImageDescription", m_sgi_header.imagename); if (m_sgi_header.storage == sgi_pvt::RLE) { m_spec.attribute("compression", "rle"); if (! read_offset_tables ()) return false; } spec = m_spec; return true; } bool SgiInput::read_native_scanline (int y, int z, void *data) { if (y < 0 || y > m_spec.height) return false; y = m_spec.height - y - 1; int bpc = m_sgi_header.bpc; std::vector > channeldata (m_spec.nchannels); if (m_sgi_header.storage == sgi_pvt::RLE) { // reading and uncompressing first channel (red in RGBA images) for (int c = 0; c < m_spec.nchannels; ++c) { int off = y + c*m_spec.height; // offset for this scanline/channel int scanline_offset = start_tab[off]; int scanline_length = length_tab[off]; channeldata[c].resize (m_spec.width * bpc); uncompress_rle_channel (scanline_offset, scanline_length, &(channeldata[c][0])); } } else { // non-RLE case -- just read directly into our channel data for (int c = 0; c < m_spec.nchannels; ++c) { int off = y + c*m_spec.height; // offset for this scanline/channel int scanline_offset = sgi_pvt::SGI_HEADER_LEN + off * m_spec.width * bpc; fseek (m_fd, scanline_offset, SEEK_SET); channeldata[c].resize (m_spec.width * bpc); if (! fread (&(channeldata[c][0]), 1, m_spec.width * bpc)) return false; } } if (m_spec.nchannels == 1) { // If just one channel, no interleaving is necessary, just memcpy memcpy (data, &(channeldata[0][0]), channeldata[0].size()); } else { unsigned char *cdata = (unsigned char *)data; for (int x = 0; x < m_spec.width; ++x) { for (int c = 0; c < m_spec.nchannels; ++c) { *cdata++ = channeldata[c][x*bpc]; if (bpc == 2) *cdata++ = channeldata[c][x*bpc+1]; } } } // Swap endianness if needed if (bpc == 2 && littleendian()) swap_endian ((unsigned short *)data, m_spec.width*m_spec.nchannels); return true; } bool SgiInput::uncompress_rle_channel(int scanline_off, int scanline_len, unsigned char *out) { int bpc = m_sgi_header.bpc; std::vector rle_scanline (scanline_len); fseek (m_fd, scanline_off, SEEK_SET); if (! fread (&rle_scanline[0], 1, scanline_len)) return false; int limit = m_spec.width; int i = 0; if (bpc == 1) { // 1 bit per channel while (i < scanline_len) { // Read a byte, it is the count. unsigned char value = rle_scanline[i++]; int count = value & 0x7F; // If the count is zero, we're done if (! count) break; // If the high bit is set, we just copy the next 'count' values if (value & 0x80) { while (count--) { DASSERT (i < scanline_len && limit > 0); *(out++) = rle_scanline[i++]; --limit; } } // If the high bit is zero, we copy the NEXT value, count times else { value = rle_scanline[i++]; while (count--) { DASSERT (limit > 0); *(out++) = value; --limit; } } } } else { // 2 bits per channel ASSERT (bpc == 2); while (i < scanline_len) { // Read a byte, it is the count. unsigned short value = (rle_scanline[i] << 8) | rle_scanline[i+1]; i += 2; int count = value & 0x7F; // If the count is zero, we're done if (! count) break; // If the high bit is set, we just copy the next 'count' values if (value & 0x80) { while (count--) { DASSERT (i+1 < scanline_len && limit > 0); *(out++) = rle_scanline[i++]; *(out++) = rle_scanline[i++]; --limit; } } // If the high bit is zero, we copy the NEXT value, count times else { while (count--) { DASSERT (limit > 0); *(out++) = rle_scanline[i]; *(out++) = rle_scanline[i+1]; --limit; } i += 2; } } } if (i != scanline_len || limit != 0) { error ("Corrupt RLE data"); return false; } return true; } bool SgiInput::close() { if (m_fd) fclose (m_fd); init (); return true; } bool SgiInput::read_header() { if (!fread(&m_sgi_header.magic, sizeof(m_sgi_header.magic), 1) || !fread(&m_sgi_header.storage, sizeof(m_sgi_header.storage), 1) || !fread(&m_sgi_header.bpc, sizeof(m_sgi_header.bpc), 1) || !fread(&m_sgi_header.dimension, sizeof(m_sgi_header.dimension), 1) || !fread(&m_sgi_header.xsize, sizeof(m_sgi_header.xsize), 1) || !fread(&m_sgi_header.ysize, sizeof(m_sgi_header.ysize), 1) || !fread(&m_sgi_header.zsize, sizeof(m_sgi_header.zsize), 1) || !fread(&m_sgi_header.pixmin, sizeof(m_sgi_header.pixmin), 1) || !fread(&m_sgi_header.pixmax, sizeof(m_sgi_header.pixmax), 1) || !fread(&m_sgi_header.dummy, sizeof(m_sgi_header.dummy), 1) || !fread(&m_sgi_header.imagename, sizeof(m_sgi_header.imagename), 1)) return false; m_sgi_header.imagename[79] = '\0'; if (! fread(&m_sgi_header.colormap, sizeof(m_sgi_header.colormap), 1)) return false; //don't read dummy bytes fseek (m_fd, 404, SEEK_CUR); if (littleendian()) { swap_endian(&m_sgi_header.magic); swap_endian(&m_sgi_header.dimension); swap_endian(&m_sgi_header.xsize); swap_endian(&m_sgi_header.ysize); swap_endian(&m_sgi_header.zsize); swap_endian(&m_sgi_header.pixmin); swap_endian(&m_sgi_header.pixmax); swap_endian(&m_sgi_header.colormap); } return true; } bool SgiInput::read_offset_tables () { int tables_size = m_sgi_header.ysize * m_sgi_header.zsize; start_tab.resize(tables_size); length_tab.resize(tables_size); if (!fread (&start_tab[0], sizeof(uint32_t), tables_size) || !fread (&length_tab[0], sizeof(uint32_t), tables_size)) return false; if (littleendian ()) { swap_endian (&length_tab[0], length_tab.size ()); swap_endian (&start_tab[0], start_tab.size()); } return true; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/rla.imageio/0000755000175000017500000000000013151711064017303 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/rla.imageio/CMakeLists.txt0000644000175000017500000000005513151711064022043 0ustar mfvmfvadd_oiio_plugin (rlainput.cpp rlaoutput.cpp) openimageio-1.7.17~dfsg0.orig/src/rla.imageio/rlainput.cpp0000644000175000017500000006250213151711064021652 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" #include "rla_pvt.h" OIIO_PLUGIN_NAMESPACE_BEGIN using namespace RLA_pvt; class RLAInput : public ImageInput { public: RLAInput () { init(); } virtual ~RLAInput () { close(); } virtual const char * format_name (void) const { return "rla"; } virtual bool open (const std::string &name, ImageSpec &newspec); virtual int current_subimage (void) const { return m_subimage; } virtual bool seek_subimage (int subimage, int miplevel, ImageSpec &newspec); virtual bool close (); virtual bool read_native_scanline (int y, int z, void *data); private: std::string m_filename; ///< Stash the filename FILE *m_file; ///< Open image handle RLAHeader m_rla; ///< Wavefront RLA header std::vector m_buf; ///< Buffer the image pixels int m_subimage; ///< Current subimage index std::vector m_sot; ///< Scanline offsets table int m_stride; ///< Number of bytes a contig pixel takes /// Reset everything to initial state /// void init () { m_file = NULL; m_buf.clear (); } /// Helper: raw read, with error detection /// bool fread (void *buf, size_t itemsize, size_t nitems) { size_t n = ::fread (buf, itemsize, nitems, m_file); if (n != nitems) error ("Read error: read %d records but %d expected %s", (int)n, (int)nitems, feof(m_file) ? " (hit EOF)" : ""); return n == nitems; } /// Helper: read buf[0..nitems-1], swap endianness if necessary template bool read (T *buf, size_t nitems=1) { if (! fread (buf, sizeof(T), nitems)) return false; if (littleendian() && (is_same::value || is_same::value || is_same::value || is_same::value)) { swap_endian (buf, nitems); } return true; } /// Helper function: translate 3-letter month abbreviation to number. /// inline int get_month_number (const char *s); /// Helper: read the RLA header and scanline offset table. /// inline bool read_header (); /// Helper: read and decode a single channel group consisting of /// channels [first_channel .. first_channel+num_channels-1], which /// all share the same number of significant bits. bool decode_channel_group (int first_channel, short num_channels, short num_bits, int y); /// Helper: decode a span of n RLE-encoded bytes from encoded[0..elen-1] /// into buf[0],buf[stride],buf[2*stride]...buf[(n-1)*stride]. /// Return the number of encoded bytes we ate to fill buf. size_t decode_rle_span (unsigned char *buf, int n, int stride, const char *encoded, size_t elen); /// Helper: determine channel TypeDesc inline TypeDesc get_channel_typedesc (short chan_type, short chan_bits); // debugging aid void preview (std::ostream &out) { ASSERT (!feof(m_file)); long pos = ftell (m_file); out << "@" << pos << ", next 4 bytes are "; union { // trickery to avoid punned pointer warnings unsigned char c[4]; uint16_t s[2]; uint32_t i; } u; read (&u.c, 4); // because it's char, it didn't swap endian uint16_t s[2] = { u.s[0], u.s[1] }; uint32_t i = u.i; if (littleendian()) { swap_endian (s, 2); swap_endian (&i); } out << Strutil::format ("%d/%u %d/%u %d/%u %d/%u (%d %d) (%u)\n", u.c[0], ((char *)u.c)[0], u.c[1], ((char *)u.c)[1], u.c[2], ((char *)u.c)[2], u.c[3], ((char *)u.c)[3], s[0], s[1], i); fseek (m_file, pos, SEEK_SET); } }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageInput *rla_input_imageio_create () { return new RLAInput; } OIIO_EXPORT int rla_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* rla_imageio_library_version () { return NULL; } OIIO_EXPORT const char * rla_input_extensions[] = { "rla", NULL }; OIIO_PLUGIN_EXPORTS_END bool RLAInput::open (const std::string &name, ImageSpec &newspec) { m_filename = name; m_file = Filesystem::fopen (name, "rb"); if (! m_file) { error ("Could not open file \"%s\"", name.c_str()); return false; } // set a bogus subimage index so that seek_subimage actually seeks m_subimage = 1; return seek_subimage (0, 0, newspec); } inline bool RLAInput::read_header () { // Read the image header, which should have the same exact layout as // the m_rla structure (except for endianness issues). ASSERT (sizeof(m_rla) == 740 && "Bad RLA struct size"); if (! read (&m_rla)) { error ("RLA could not read the image header"); return false; } m_rla.rla_swap_endian (); // fix endianness if (m_rla.Revision != (int16_t)0xFFFE && m_rla.Revision != 0 /* for some reason, this can happen */) { error ("RLA header Revision number unrecognized: %d", m_rla.Revision); return false; // unknown file revision } if (m_rla.NumOfChannelBits == 0) m_rla.NumOfChannelBits = 8; // apparently, this can happen // Immediately following the header is the scanline offset table -- // one uint32_t for each scanline, giving absolute offsets (from the // beginning of the file) where the RLE records start for each // scanline of this subimage. m_sot.resize (std::abs (m_rla.ActiveBottom - m_rla.ActiveTop) + 1, 0); if (! read (&m_sot[0], m_sot.size())) { error ("RLA could not read the scanline offset table"); return false; } return true; } bool RLAInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec) { if (miplevel != 0 || subimage < 0) return false; if (subimage == current_subimage()) return true; // already on the right level // RLA images allow multiple subimages; they are simply concatenated // together, wth image N's header field NextOffset giving the // absolute offset of the start of image N+1. int diff = subimage - current_subimage (); if (subimage - current_subimage () < 0) { // If we are requesting an image earlier than the current one, // reset to the first subimage. fseek (m_file, 0, SEEK_SET); if (!read_header ()) return false; // read_header always calls error() diff = subimage; } // forward scrolling -- skip subimages until we're at the right place while (diff > 0 && m_rla.NextOffset != 0) { fseek (m_file, m_rla.NextOffset, SEEK_SET); if (!read_header ()) return false; // read_header always calls error() --diff; } if (diff > 0 && m_rla.NextOffset == 0) { // no more subimages to read error ("Unknown subimage"); return false; } // Now m_rla holds the header of the requested subimage. Examine it // to fill out our ImageSpec. if (m_rla.ColorChannelType > CT_FLOAT) { error ("Illegal color channel type: %d", m_rla.ColorChannelType); return false; } if (m_rla.MatteChannelType > CT_FLOAT) { error ("Illegal matte channel type: %d", m_rla.MatteChannelType); return false; } if (m_rla.AuxChannelType > CT_FLOAT) { error ("Illegal auxiliary channel type: %d", m_rla.AuxChannelType); return false; } // pick maximum precision for the time being int maxbytes = (std::max (m_rla.NumOfChannelBits * (m_rla.NumOfColorChannels > 0 ? 1 : 0), std::max (m_rla.NumOfMatteBits * (m_rla.NumOfMatteChannels > 0 ? 1 : 0), m_rla.NumOfAuxBits * (m_rla.NumOfAuxChannels > 0 ? 1 : 0))) + 7) / 8; int nchannels = m_rla.NumOfColorChannels + m_rla.NumOfMatteChannels + m_rla.NumOfAuxChannels; TypeDesc maxtype = (maxbytes == 4) ? TypeDesc::FLOAT : (maxbytes == 2 ? TypeDesc::UINT16 : TypeDesc::UINT8); if (nchannels < 1 || nchannels > 16 || (maxbytes != 1 && maxbytes != 2 && maxbytes != 4)) { error ("Failed channel bytes sanity check"); return false; // failed sanity check } m_spec = ImageSpec (m_rla.ActiveRight - m_rla.ActiveLeft + 1, (m_rla.ActiveTop - m_rla.ActiveBottom + 1) / (m_rla.FieldRendered ? 2 : 1), // interlaced image? m_rla.NumOfColorChannels + m_rla.NumOfMatteChannels + m_rla.NumOfAuxChannels, maxtype); // set window dimensions etc. m_spec.x = m_rla.ActiveLeft; m_spec.y = m_spec.height-1 - m_rla.ActiveTop; m_spec.full_width = m_rla.WindowRight - m_rla.WindowLeft + 1; m_spec.full_height = m_rla.WindowTop - m_rla.WindowBottom + 1; m_spec.full_depth = 1; m_spec.full_x = m_rla.WindowLeft; m_spec.full_y = m_spec.full_height-1 - m_rla.WindowTop; // set channel formats and stride int z_channel = -1; m_stride = 0; TypeDesc t = get_channel_typedesc (m_rla.ColorChannelType, m_rla.NumOfChannelBits); for (int i = 0; i < m_rla.NumOfColorChannels; ++i) m_spec.channelformats.push_back (t); m_stride += m_rla.NumOfColorChannels * t.size (); t = get_channel_typedesc (m_rla.MatteChannelType, m_rla.NumOfMatteBits); for (int i = 0; i < m_rla.NumOfMatteChannels; ++i) m_spec.channelformats.push_back (t); if (m_rla.NumOfMatteChannels >= 1) m_spec.alpha_channel = m_rla.NumOfColorChannels; else m_spec.alpha_channel = -1; m_stride += m_rla.NumOfMatteChannels * t.size (); t = get_channel_typedesc (m_rla.AuxChannelType, m_rla.NumOfAuxBits); for (int i = 0; i < m_rla.NumOfAuxChannels; ++i) { m_spec.channelformats.push_back (t); // assume first float aux or 32 bit int channel is z if (z_channel < 0 && (t == TypeDesc::FLOAT || t == TypeDesc::INT32 || t == TypeDesc::UINT32)) { z_channel = m_rla.NumOfColorChannels + m_rla.NumOfMatteChannels; m_spec.z_channel = z_channel; m_spec.channelnames[z_channel] = "Z"; } } m_stride += m_rla.NumOfAuxChannels * t.size (); // But if all channels turned out the same, just use 'format' and don't // bother sending back channelformats at all. bool allsame = true; for (int c = 1; c < m_spec.nchannels; ++c) allsame &= (m_spec.channelformats[c] == m_spec.channelformats[0]); if (allsame) { m_spec.format = m_spec.channelformats[0]; m_spec.channelformats.clear(); m_spec.attribute ("oiio:BitsPerSample", m_rla.NumOfChannelBits); // N.B. don't set bps for mixed formats, it isn't well defined } // this is always true m_spec.attribute ("compression", "rle"); if (m_rla.DateCreated[0]) { char month[4] = {0, 0, 0, 0}; int d, h, M, m, y; if (sscanf (m_rla.DateCreated, "%c%c%c %d %d:%d %d", month + 0, month + 1, month + 2, &d, &h, &m, &y) == 7) { M = get_month_number (month); if (M > 0) { // construct a date/time marker in OIIO convention m_spec.attribute ("DateTime", Strutil::format("%4d:%02d:%02d %02d:%02d:00", y, M, d, h, m)); } } } // save some typing by using macros #define FIELD(x,name) if (m_rla.x > 0) \ m_spec.attribute (name, m_rla.x) #define STRING_FIELD(x,name) if (m_rla.x[0]) \ m_spec.attribute (name, m_rla.x) STRING_FIELD (Description, "ImageDescription"); FIELD (FrameNumber, "rla:FrameNumber"); FIELD (Revision, "rla:Revision"); FIELD (JobNumber, "rla:JobNumber"); FIELD (FieldRendered, "rla:FieldRendered"); STRING_FIELD (FileName, "rla:FileName"); STRING_FIELD (ProgramName, "Software"); STRING_FIELD (MachineName, "HostComputer"); STRING_FIELD (UserName, "Artist"); STRING_FIELD (Aspect, "rla:Aspect"); STRING_FIELD (ColorChannel, "rla:ColorChannel"); STRING_FIELD (Time, "rla:Time"); STRING_FIELD (Filter, "rla:Filter"); STRING_FIELD (AuxData, "rla:AuxData"); #undef STRING_FIELD #undef FIELD float f[3]; // variable will be reused for chroma, thus the array f[0] = atof (m_rla.Gamma); if (f[0] > 0.f) { if (f[0] == 1.f) m_spec.attribute ("oiio:ColorSpace", "Linear"); else { m_spec.attribute ("oiio:ColorSpace", "GammaCorrected"); m_spec.attribute ("oiio:Gamma", f[0]); } } f[0] = atof (m_rla.AspectRatio); if (f[0] > 0.f) m_spec.attribute ("PixelAspectRatio", f[0]); // read chromaticity points if (m_rla.RedChroma[0]) { int num = sscanf(m_rla.RedChroma, "%f %f %f", f + 0, f + 1, f + 2); if (num >= 2) m_spec.attribute ("rla:RedChroma", TypeDesc(TypeDesc::FLOAT, num == 2 ? TypeDesc::VEC2 : TypeDesc::VEC3, TypeDesc::POINT), f); } if (m_rla.GreenChroma[0]) { int num = sscanf(m_rla.GreenChroma, "%f %f %f", f + 0, f + 1, f + 2); if (num >= 2) m_spec.attribute ("rla:GreenChroma", TypeDesc(TypeDesc::FLOAT, num == 2 ? TypeDesc::VEC2 : TypeDesc::VEC3, TypeDesc::POINT), f); } if (m_rla.BlueChroma[0]) { int num = sscanf(m_rla.BlueChroma, "%f %f %f", f + 0, f + 1, f + 2); if (num >= 2) m_spec.attribute ("rla:BlueChroma", TypeDesc(TypeDesc::FLOAT, num == 2 ? TypeDesc::VEC2 : TypeDesc::VEC3, TypeDesc::POINT), f); } if (m_rla.WhitePoint[0]) { int num = sscanf(m_rla.WhitePoint, "%f %f %f", f + 0, f + 1, f + 2); if (num >= 2) m_spec.attribute ("rla:WhitePoint", TypeDesc(TypeDesc::FLOAT, num == 2 ? TypeDesc::VEC2 : TypeDesc::VEC3, TypeDesc::POINT), f); } newspec = spec (); m_subimage = subimage; // N.B. the file pointer is now immediately after the scanline // offset table for this subimage. return true; } bool RLAInput::close () { if (m_file) { fclose (m_file); m_file = NULL; } init(); // Reset to initial state return true; } size_t RLAInput::decode_rle_span (unsigned char *buf, int n, int stride, const char *encoded, size_t elen) { size_t e = 0; while (n > 0 && e < elen) { signed char count = (signed char) encoded[e++]; if (count >= 0) { // run count positive: value repeated count+1 times for (int i = 0; i <= count && n; ++i, buf += stride, --n) *buf = encoded[e]; ++e; } else { // run count negative: repeat bytes literally count = -count; // make it positive for ( ; count && n > 0 && e < elen; --count, buf += stride, --n) *buf = encoded[e++]; } } if (n != 0) { error ("Read error: malformed RLE record"); return 0; } return e; } bool RLAInput::decode_channel_group (int first_channel, short num_channels, short num_bits, int y) { // Some preliminaries -- figure out various sizes and offsets int chsize; // size of the channels in this group, in bytes int offset; // buffer offset to first channel int pixelsize; // spacing between pixels (in bytes) in the output TypeDesc chantype; // data type for the channel if (! m_spec.channelformats.size()) { // No per-channel formats, they are all the same, so it's easy chantype = m_spec.format; chsize = chantype.size (); offset = first_channel * chsize; pixelsize = chsize * m_spec.nchannels; } else { // Per-channel formats differ, need to sum them up chantype = m_spec.channelformats[first_channel]; chsize = chantype.size (); offset = 0; pixelsize = m_spec.pixel_bytes (true); for (int i = 0; i < first_channel; ++i) offset += m_spec.channelformats[i].size (); } // Read the big-endian values into the buffer. // The channels are simply contatenated together in order. // Each channel starts with a length, from which we know how many // bytes of encoded RLE data to read. Then there are RLE // spans for each 8-bit slice of the channel. std::vector encoded; for (int c = 0; c < num_channels; ++c) { // Read the length uint16_t length; // number of encoded bytes if (!read (&length)) { error ("Read error: couldn't read RLE record length"); return false; } // Read the encoded RLE record encoded.resize (length); if (!read (&encoded[0], length)) { error ("Read error: couldn't read RLE data span"); return false; } if (chantype == TypeDesc::FLOAT) { // Special case -- float data is just dumped raw, no RLE for (int x = 0; x < m_spec.width; ++x) *((float *)&m_buf[offset+c*chsize+x*pixelsize]) = ((float *)&encoded[0])[x]; continue; } // Decode RLE -- one pass for each significant byte of the file, // which we re-interleave properly by passing the right offsets // and strides to decode_rle_span. size_t eoffset = 0; for (int bytes = 0; bytes < chsize; ++bytes) { size_t e = decode_rle_span (&m_buf[offset+c*chsize+bytes], m_spec.width, pixelsize, &encoded[eoffset], length); if (! e) return false; eoffset += e; } } // If we're little endian, swap endianness in place for 2- and // 4-byte pixel data. if (littleendian()) { if (chsize == 2) { if (num_channels == m_spec.nchannels) swap_endian ((uint16_t *)&m_buf[0], num_channels*m_spec.width); else for (int x = 0; x < m_spec.width; ++x) swap_endian ((uint16_t *)&m_buf[offset+x*pixelsize], num_channels); } else if (chsize == 4 && chantype != TypeDesc::FLOAT) { if (num_channels == m_spec.nchannels) swap_endian ((uint32_t *)&m_buf[0], num_channels*m_spec.width); else for (int x = 0; x < m_spec.width; ++x) swap_endian ((uint32_t *)&m_buf[offset+x*pixelsize], num_channels); } } // If not 8*2^n bits, need to rescale. For example, if num_bits is // 10, the data values run 0-1023, but are stored in uint16. So we // now rescale to the full range of the output buffer range, per // OIIO conventions. if (num_bits == 8 || num_bits == 16 || num_bits == 32) { // ok -- no rescaling needed } else if (num_bits == 10) { // fast, common case -- use templated hard-code for (int x = 0; x < m_spec.width; ++x) { uint16_t *b = (uint16_t *)(&m_buf[offset+x*pixelsize]); for (int c = 0; c < num_channels; ++c) b[c] = bit_range_convert<10,16> (b[c]); } } else if (num_bits < 8) { // rare case, use slow code to make this clause short and simple for (int x = 0; x < m_spec.width; ++x) { uint8_t *b = (uint8_t *)&m_buf[offset+x*pixelsize]; for (int c = 0; c < num_channels; ++c) b[c] = bit_range_convert (b[c], num_bits, 8); } } else if (num_bits > 8 && num_bits < 16) { // rare case, use slow code to make this clause short and simple for (int x = 0; x < m_spec.width; ++x) { uint16_t *b = (uint16_t *)&m_buf[offset+x*pixelsize]; for (int c = 0; c < num_channels; ++c) b[c] = bit_range_convert (b[c], num_bits, 16); } } else if (num_bits > 16 && num_bits < 32) { // rare case, use slow code to make this clause short and simple for (int x = 0; x < m_spec.width; ++x) { uint32_t *b = (uint32_t *)&m_buf[offset+x*pixelsize]; for (int c = 0; c < num_channels; ++c) b[c] = bit_range_convert (b[c], num_bits, 32); } } return true; } bool RLAInput::read_native_scanline (int y, int z, void *data) { // By convention, RLA images store their images bottom-to-top. y = m_spec.height - (y - m_spec.y) - 1; // Seek to scanline start, based on the scanline offset table fseek (m_file, m_sot[y], SEEK_SET); // Now decode and interleave the channels. // The channels are non-interleaved (i.e. rrrrrgggggbbbbb...). // Color first, then matte, then auxiliary channels. We can't // decode all in one shot, though, because the data type and number // of significant bits may be may be different for each class of // channels, so we deal with them separately and interleave into // our buffer as we go. size_t size = m_spec.scanline_bytes(true); m_buf.resize (size); if (m_rla.NumOfColorChannels > 0) if (!decode_channel_group(0, m_rla.NumOfColorChannels, m_rla.NumOfChannelBits, y)) return false; if (m_rla.NumOfMatteChannels > 0) if (!decode_channel_group(m_rla.NumOfColorChannels, m_rla.NumOfMatteChannels, m_rla.NumOfMatteBits, y)) return false; if (m_rla.NumOfAuxChannels > 0) if (!decode_channel_group(m_rla.NumOfColorChannels + m_rla.NumOfMatteChannels, m_rla.NumOfAuxChannels, m_rla.NumOfAuxBits, y)) return false; memcpy (data, &m_buf[0], size); return true; } inline int RLAInput::get_month_number (const char *s) { static const char *months[] = { "", "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" }; for (int i = 1; i <= 12; ++i) if (Strutil::iequals (s, months[i])) return i; return -1; } inline TypeDesc RLAInput::get_channel_typedesc (short chan_type, short chan_bits) { switch (chan_type) { case CT_BYTE: // some non-spec-compliant images > 8bpc will have it set to // byte anyway, so try guessing by bit depth instead if (chan_bits > 8) { switch ((chan_bits + 7) / 8) { case 2: return TypeDesc::UINT16; case 3: case 4: return TypeDesc::UINT32; default: ASSERT(!"Invalid colour channel type"); } } else return TypeDesc::UINT8; case CT_WORD: return TypeDesc::UINT16; case CT_DWORD: return TypeDesc::UINT32; case CT_FLOAT: return TypeDesc::FLOAT; default: ASSERT(!"Invalid colour channel type"); } // shut up compiler return TypeDesc::UINT8; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/rla.imageio/rla_pvt.h0000644000175000017500000002020613151711064021123 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_RLA_PVT_H #define OPENIMAGEIO_RLA_PVT_H /* Brief documentation about the RLA format: * The file consists of multiple subimages, merely contatenated together. Each subimage starts with a RLAHeader, and within the header is a NextOffset field that gives the absolute offset (relative to the start of the file) of the beginning of the next subimage, or 0 if there is no next subimage. * Immediately following the header is the scanline offset table, which is one uint32 for each scanline, giving the absolute offset for the beginning of that scanline record. By convention, RLA scanline 0 is displayed at the bottom of the image (the opposite of OIIO convention). * Each scanline consists of up to three channel groups, concatenated together: color, then matte, then auxiliary. Each group may have a different data type and bit depth. * A channel group consists of its channels (separate, non-interleaved) concatenated together. * A channel is stored in an RLE record, which consists of a uint16 given the length of encoded data, then the encoded data run. * The encoded data run consists of a signed "count" byte. If the count >= 0, the next byte is the pixel value, which is be repeated count+1 times as output. If count < 0, then the next abs(count) bytes should be copied directly to as output. * For SHORT (16 bit), LONG (32 bit), or FLOAT pixel data types, the most significant 8 bits of each pixel come first, then the next less significant 8 bits, and so on. For example, for 16 bit data (HL), the sequence will be H0 H1 H2 ... L0 L1 L2 ... Therefore, the bytes will need to be re-interleaved to form contiguous 16 or 32 bit values in the output buffer. * But float data is not RLE compressed, instead just dumped raw after the RLE length. Well, at least according to old code at SPI. We have no original RLA specification that stipulates this to be the case. * RLA files are "big endian" for all 16 and 32 bit data: header fields, offsets, and pixel data. */ OIIO_PLUGIN_NAMESPACE_BEGIN namespace RLA_pvt { // code below adapted from // http://www.fileformat.info/format/wavefrontrla/egff.htm struct RLAHeader { int16_t WindowLeft; // Left side of the full image int16_t WindowRight; // Right side of the full image int16_t WindowBottom; // Bottom of the full image int16_t WindowTop; // Top of the full image int16_t ActiveLeft; // Left side of the viewable image int16_t ActiveRight; // Right side of viewable image int16_t ActiveBottom; // Bottom of the viewable image int16_t ActiveTop; // Top of the viewable image int16_t FrameNumber; // Frame sequence number int16_t ColorChannelType; // Data format of the image channels int16_t NumOfColorChannels; // Number of color channels in image int16_t NumOfMatteChannels; // Number of matte channels in image int16_t NumOfAuxChannels; // Number of auxiliary channels in image int16_t Revision; // File format revision number char Gamma[16]; // Gamma setting of image char RedChroma[24]; // Red chromaticity char GreenChroma[24]; // Green chromaticity char BlueChroma[24]; // Blue chromaticity char WhitePoint[24]; // White point chromaticity*/ int32_t JobNumber; // Job number ID of the file char FileName[128]; // Image file name char Description[128]; // Description of the file contents char ProgramName[64]; // Name of the program that created the file char MachineName[32]; // Name of machine used to create the file char UserName[32]; // Name of user who created the file char DateCreated[20]; // Date the file was created char Aspect[24]; // Aspect format of the image char AspectRatio[8]; // Aspect ratio of the image char ColorChannel[32]; // Format of color channel data int16_t FieldRendered; // Image contains field-rendered data char Time[12]; // Length of time used to create the image file char Filter[32]; // Name of post-processing filter int16_t NumOfChannelBits; // Number of bits in each color channel pixel int16_t MatteChannelType; // Data format of the matte channels int16_t NumOfMatteBits; // Number of bits in each matte channel pixel int16_t AuxChannelType; // Data format of the auxiliary channels int16_t NumOfAuxBits; // Number of bits in each auxiliary channel pixel char AuxData[32]; // Auxiliary channel data description char Reserved[36]; // Unused int32_t NextOffset; // Location of the next image header in the file void rla_swap_endian () { if (littleendian()) { // RLAs are big-endian swap_endian (&WindowLeft); swap_endian (&WindowRight); swap_endian (&WindowBottom); swap_endian (&WindowTop); swap_endian (&ActiveLeft); swap_endian (&ActiveRight); swap_endian (&ActiveBottom); swap_endian (&ActiveTop); swap_endian (&FrameNumber); swap_endian (&ColorChannelType); swap_endian (&NumOfColorChannels); swap_endian (&NumOfMatteChannels); swap_endian (&NumOfAuxChannels); swap_endian (&Revision); swap_endian (&JobNumber); swap_endian (&FieldRendered); swap_endian (&NumOfChannelBits); swap_endian (&MatteChannelType); swap_endian (&NumOfMatteBits); swap_endian (&AuxChannelType); swap_endian (&NumOfAuxBits); swap_endian (&NextOffset); } } }; /// format of data enum rla_channel_type { CT_BYTE = 0, CT_WORD = 1, CT_DWORD = 2, CT_FLOAT = 4 }; inline rla_channel_type rla_type (TypeDesc t) { if (t == TypeDesc::UINT16) return CT_WORD; if (t == TypeDesc::UINT32) return CT_DWORD; if (t == TypeDesc::FLOAT) return CT_FLOAT; return CT_BYTE; } } // namespace RLA_pvt OIIO_PLUGIN_NAMESPACE_END #endif // OPENIMAGEIO_RLA_PVT_H openimageio-1.7.17~dfsg0.orig/src/rla.imageio/rlaoutput.cpp0000644000175000017500000005460313151711064022056 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/sysutil.h" #include "rla_pvt.h" #ifdef WIN32 # define snprintf _snprintf #endif OIIO_PLUGIN_NAMESPACE_BEGIN using namespace RLA_pvt; class RLAOutput : public ImageOutput { public: RLAOutput (); virtual ~RLAOutput (); virtual const char * format_name (void) const { return "rla"; } virtual int supports (string_view feature) const; virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode=Create); virtual bool close (); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); private: std::string m_filename; ///< Stash the filename FILE *m_file; ///< Open image handle std::vector m_scratch; RLAHeader m_rla; ///< Wavefront RLA header std::vector m_sot; ///< Scanline offset table std::vector m_rle; ///< Run record buffer for RLE std::vector m_tilebuffer; unsigned int m_dither; // Initialize private members to pre-opened state void init (void) { m_file = NULL; m_sot.clear (); } /// Helper - sets a chromaticity from attribute inline void set_chromaticity (const ImageIOParameter *p, char *dst, size_t field_size, const char *default_val); // Helper - handles the repetitive work of encoding and writing a // channel. The data is guaranteed to be in the scratch area and need // not be preserved. bool encode_channel (unsigned char *data, stride_t xstride, TypeDesc chantype, int bits); /// Helper - write, with error detection bool fwrite (const void *buf, size_t itemsize, size_t nitems) { size_t n = ::fwrite (buf, itemsize, nitems, m_file); if (n != nitems) error ("Write error: wrote %d records of %d", (int)n, (int)nitems); return n == nitems; } /// Helper: write buf[0..nitems-1], swap endianness if necessary template bool write (const T *buf, size_t nitems=1) { if (littleendian() && (is_same::value || is_same::value || is_same::value || is_same::value)) { T *newbuf = ALLOCA(T,nitems); memcpy (newbuf, buf, nitems*sizeof(T)); swap_endian (newbuf, nitems); buf = newbuf; } return fwrite (buf, sizeof(T), nitems); } }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *rla_output_imageio_create () { return new RLAOutput; } // OIIO_EXPORT int rla_imageio_version = OIIO_PLUGIN_VERSION; // it's in rlainput.cpp OIIO_EXPORT const char * rla_output_extensions[] = { "rla", NULL }; OIIO_PLUGIN_EXPORTS_END RLAOutput::RLAOutput () { init (); } RLAOutput::~RLAOutput () { // Close, if not already done. close (); } int RLAOutput::supports (string_view feature) const { if (feature == "random_access") return true; if (feature == "displaywindow") return true; if (feature == "origin") return true; if (feature == "negativeorigin") return true; if (feature == "alpha") return true; if (feature == "nchannels") return true; if (feature == "channelformats") return true; // Support nothing else nonstandard return false; } bool RLAOutput::open (const std::string &name, const ImageSpec &userspec, OpenMode mode) { if (mode != Create) { error ("%s does not support subimages or MIP levels", format_name()); return false; // FIXME -- the RLA format supports subimages, but our writer // doesn't. I'm not sure if it's worth worrying about for an // old format that is so rarely used. We'll come back to it if // anybody actually encounters a multi-subimage RLA in the wild. } close (); // Close any already-opened file m_spec = userspec; // Stash the spec if (m_spec.format == TypeDesc::UNKNOWN) m_spec.format = TypeDesc::UINT8; // Default to uint8 if unknown m_file = Filesystem::fopen (name, "wb"); if (! m_file) { error ("Could not open file \"%s\"", name.c_str()); return false; } // Check for things this format doesn't support if (m_spec.width < 1 || m_spec.height < 1) { error ("Image resolution must be at least 1x1, you asked for %d x %d", m_spec.width, m_spec.height); return false; } if (m_spec.width > 65535 || m_spec.height > 65535) { error ("Image resolution %d x %d too large for RLA (maxiumum 65535x65535)", m_spec.width, m_spec.height); return false; } if (m_spec.depth < 1) m_spec.depth = 1; else if (m_spec.depth > 1) { error ("%s does not support volume images (depth > 1)", format_name()); return false; } m_dither = (m_spec.format == TypeDesc::UINT8) ? m_spec.get_int_attribute ("oiio:dither", 0) : 0; // prepare and write the RLA header memset (&m_rla, 0, sizeof (m_rla)); // frame and window coordinates m_rla.WindowLeft = m_spec.full_x; m_rla.WindowRight = m_spec.full_x + m_spec.full_width - 1; m_rla.WindowTop = m_spec.full_height-1 - m_spec.full_y; m_rla.WindowBottom = m_rla.WindowTop - m_spec.full_height + 1; m_rla.ActiveLeft = m_spec.x; m_rla.ActiveRight = m_spec.x + m_spec.width - 1; m_rla.ActiveTop = m_spec.height-1 - m_spec.y; m_rla.ActiveBottom = m_rla.ActiveTop - m_spec.height + 1; m_rla.FrameNumber = m_spec.get_int_attribute ("rla:FrameNumber", 0); // figure out what's going on with the channels int remaining = m_spec.nchannels; if (m_spec.channelformats.size ()) { int streak; // accomodate first 3 channels of the same type as colour ones for (streak = 1; streak <= 3 && remaining > 0; ++streak, --remaining) if (m_spec.channelformats[streak] != m_spec.channelformats[0] || m_spec.alpha_channel == streak || m_spec.z_channel == streak) break; m_rla.ColorChannelType = rla_type(m_spec.channelformats[0]); int bits = m_spec.get_int_attribute ("oiio:BitsPerSample", 0); m_rla.NumOfChannelBits = bits ? bits : m_spec.channelformats[0].size () * 8; // limit to 3 in case the loop went further m_rla.NumOfColorChannels = std::min (streak, 3); // if we have anything left and it looks like alpha, treat it as alpha if (remaining && m_spec.z_channel != m_rla.NumOfColorChannels) { for (streak = 1; remaining > 0; ++streak, --remaining) if (m_spec.channelformats[m_rla.NumOfColorChannels + streak] != m_spec.channelformats[m_rla.NumOfColorChannels]) break; m_rla.MatteChannelType = rla_type(m_spec.channelformats[m_rla.NumOfColorChannels]); m_rla.NumOfMatteBits = bits ? bits : m_spec.channelformats[m_rla.NumOfColorChannels].size () * 8; m_rla.NumOfMatteChannels = streak; } else { m_rla.MatteChannelType = CT_BYTE; m_rla.NumOfMatteBits = 8; m_rla.NumOfMatteChannels = 0; } // and if there's something more left, put it in auxiliary if (remaining) { for (streak = 1; remaining > 0; ++streak, --remaining) if (m_spec.channelformats[m_rla.NumOfColorChannels + m_rla.NumOfMatteChannels + streak] != m_spec.channelformats[m_rla.NumOfColorChannels + m_rla.NumOfMatteChannels]) break; m_rla.AuxChannelType = rla_type (m_spec.channelformats[m_rla.NumOfColorChannels + m_rla.NumOfMatteChannels]); m_rla.NumOfAuxBits = m_spec.channelformats[m_rla.NumOfColorChannels + m_rla.NumOfMatteChannels].size () * 8; m_rla.NumOfAuxChannels = streak; } } else { m_rla.ColorChannelType = m_rla.MatteChannelType = m_rla.AuxChannelType = rla_type(m_spec.format); int bits = m_spec.get_int_attribute ("oiio:BitsPerSample", 0); if (bits) { m_rla.NumOfChannelBits = bits; } else { if (m_spec.channelformats.size()) m_rla.NumOfChannelBits = m_spec.channelformats[0].size() * 8; else m_rla.NumOfChannelBits = m_spec.format.size() * 8; } m_rla.NumOfMatteBits = m_rla.NumOfAuxBits = m_rla.NumOfChannelBits; if (remaining >= 3) { // if we have at least 3 channels, treat them as colour m_rla.NumOfColorChannels = 3; remaining -= 3; } else { // otherwise let's say it's luminosity m_rla.NumOfColorChannels = 1; --remaining; } // if there's at least 1 more channel, it's alpha if (remaining && m_spec.z_channel != m_rla.NumOfColorChannels) { --remaining; ++m_rla.NumOfMatteChannels; } // anything left is auxiliary if (remaining > 0) m_rla.NumOfAuxChannels = remaining; } // std::cout << "color chans " << m_rla.NumOfColorChannels << " a " // << m_rla.NumOfMatteChannels << " z " << m_rla.NumOfAuxChannels << "\n"; m_rla.Revision = 0xFFFE; std::string s = m_spec.get_string_attribute ("oiio:ColorSpace", "Unknown"); if (Strutil::iequals(s, "Linear")) Strutil::safe_strcpy (m_rla.Gamma, "1.0", sizeof(m_rla.Gamma)); else if (Strutil::iequals(s, "GammaCorrected")) snprintf (m_rla.Gamma, sizeof(m_rla.Gamma), "%.10f", m_spec.get_float_attribute ("oiio:Gamma", 1.f)); const ImageIOParameter *p; // default NTSC chromaticities p = m_spec.find_attribute ("rla:RedChroma"); set_chromaticity (p, m_rla.RedChroma, sizeof (m_rla.RedChroma), "0.67 0.08"); p = m_spec.find_attribute ("rla:GreenChroma"); set_chromaticity (p, m_rla.GreenChroma, sizeof (m_rla.GreenChroma), "0.21 0.71"); p = m_spec.find_attribute ("rla:BlueChroma"); set_chromaticity (p, m_rla.BlueChroma, sizeof (m_rla.BlueChroma), "0.14 0.33"); p = m_spec.find_attribute ("rla:WhitePoint"); set_chromaticity (p, m_rla.WhitePoint, sizeof (m_rla.WhitePoint), "0.31 0.316"); #define STRING_FIELD(rlafield,name) \ { \ std::string s = m_spec.get_string_attribute (name); \ if (s.length()) { \ strncpy (m_rla.rlafield, s.c_str(), sizeof(m_rla.rlafield));\ m_rla.rlafield[sizeof(m_rla.rlafield)-1] = 0; \ } else { \ m_rla.rlafield[0] = 0; \ } \ } m_rla.JobNumber = m_spec.get_int_attribute ("rla:JobNumber", 0); STRING_FIELD (FileName, "rla:FileName"); STRING_FIELD (Description, "ImageDescription"); STRING_FIELD (ProgramName, "Software"); STRING_FIELD (MachineName, "HostComputer"); STRING_FIELD (UserName, "Artist"); // the month number will be replaced with the 3-letter abbreviation time_t t = time (NULL); strftime (m_rla.DateCreated, sizeof (m_rla.DateCreated), "%m %d %H:%M %Y", localtime (&t)); // nice little trick - atoi() will convert the month number to integer, // which we then use to index this array of constants, and copy the // abbreviation back into the date string static const char months[12][4] = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; memcpy(m_rla.DateCreated, months[atoi (m_rla.DateCreated) - 1], 3); // FIXME: it appears that Wavefront have defined a set of aspect names; // I think it's safe not to care until someone complains STRING_FIELD (Aspect, "rla:Aspect"); snprintf (m_rla.AspectRatio, sizeof(m_rla.AspectRatio), "%.10f", m_spec.get_float_attribute ("PixelAspectRatio", 1.f)); Strutil::safe_strcpy (m_rla.ColorChannel, m_spec.get_string_attribute ("rla:ColorChannel", "rgb"), sizeof(m_rla.ColorChannel)); m_rla.FieldRendered = m_spec.get_int_attribute ("rla:FieldRendered", 0); STRING_FIELD (Time, "rla:Time"); STRING_FIELD (Filter, "rla:Filter"); STRING_FIELD (AuxData, "rla:AuxData"); m_rla.rla_swap_endian (); // RLAs are big-endian write (&m_rla); m_rla.rla_swap_endian (); // flip back the endianness to native // write placeholder values - not all systems may expand the file with // zeroes upon seek m_sot.resize (m_spec.height, (int32_t)0); write (&m_sot[0], m_sot.size()); // If user asked for tiles -- which this format doesn't support, emulate // it by buffering the whole image. if (m_spec.tile_width && m_spec.tile_height) m_tilebuffer.resize (m_spec.image_bytes()); return true; } inline void RLAOutput::set_chromaticity (const ImageIOParameter *p, char *dst, size_t field_size, const char *default_val) { if (p && p->type().basetype == TypeDesc::FLOAT) { switch (p->type().aggregate) { case TypeDesc::VEC2: snprintf (dst, field_size, "%.4f %.4f", ((float *)p->data ())[0], ((float *)p->data ())[1]); break; case TypeDesc::VEC3: snprintf (dst, field_size, "%.4f %.4f %.4f", ((float *)p->data ())[0], ((float *)p->data ())[1], ((float *)p->data ())[2]); break; } } else Strutil::safe_strcpy (dst, default_val, field_size); } bool RLAOutput::close () { if (! m_file) { // already closed init (); return true; } bool ok = true; if (m_spec.tile_width) { // Handle tile emulation -- output the buffered pixels ASSERT (m_tilebuffer.size()); ok &= write_scanlines (m_spec.y, m_spec.y+m_spec.height, 0, m_spec.format, &m_tilebuffer[0]); std::vector().swap (m_tilebuffer); } // Now that all scanlines have been output, return to write the // correct scanline offset table to file and close the stream. fseek (m_file, sizeof(RLAHeader), SEEK_SET); write (&m_sot[0], m_sot.size()); fclose (m_file); m_file = NULL; init (); // re-initialize return ok; } bool RLAOutput::encode_channel (unsigned char *data, stride_t xstride, TypeDesc chantype, int bits) { if (chantype == TypeDesc::FLOAT) { // Special case -- float data is just dumped raw, no RLE uint16_t size = m_spec.width * sizeof(float); write (&size); for (int x = 0; x < m_spec.width; ++x) write ((const float *)&data[x*xstride]); return true; } if (chantype == TypeDesc::UINT16 && bits != 16) { // Need to do bit scaling. Safe to overwrite data in place. for (int x = 0; x < m_spec.width; ++x) { unsigned short *s = (unsigned short *)(data + x*xstride); *s = bit_range_convert (*s, 16, bits); } } m_rle.resize (2); // reserve t bytes for the encoded size // multi-byte data types are sliced to MSB, nextSB, ..., LSB int chsize = (int)chantype.size(); for (int byte = 0; byte < chsize; ++byte) { int lastval = -1; // last value int count = 0; // count of raw or repeats bool repeat = false; // if true, we're repeating int runbegin = 0; // where did the run begin int byteoffset = bigendian() ? byte : (chsize-byte-1); for (int x = 0; x < m_spec.width; ++x) { int newval = data[x*xstride+byteoffset]; if (count == 0) { // beginning of a run. count = 1; repeat = true; // presumptive runbegin = x; } else if (repeat) { // We've seen one or more repeating characters if (newval == lastval) { // another repeating value ++count; } else { // We stopped repeating. if (count < 3) { // If we didn't even have 3 in a row, just // retroactively treat it as a raw run. ++count; repeat = false; } else { // We are ending a 3+ repetition m_rle.push_back (count-1); m_rle.push_back (lastval); count = 1; runbegin = x; } } } else { // Have not been repeating if (newval == lastval) { // starting a repetition? Output previous ASSERT (count > 1); // write everything but the last char --count; m_rle.push_back (-count); for (int i = 0; i < count; ++i) m_rle.push_back (data[(runbegin+i)*xstride+byteoffset]); count = 2; runbegin = x - 1; repeat = true; } else { ++count; // another non-repeat } } // If the run is too long or we're at the scanline end, write if (count == 127 || x == m_spec.width-1) { if (repeat) { m_rle.push_back (count-1); m_rle.push_back (lastval); } else { m_rle.push_back (-count); for (int i = 0; i < count; ++i) m_rle.push_back (data[(runbegin+i)*xstride+byteoffset]); } count = 0; } lastval = newval; } ASSERT (count == 0); } // Now that we know the size of the encoded buffer, save it at the // beginning uint16_t size = uint16_t (m_rle.size() - 2); m_rle[0] = size >> 8; m_rle[1] = size & 255; // And write the channel to the file return write (&m_rle[0], m_rle.size()); } bool RLAOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { m_spec.auto_stride (xstride, format, spec().nchannels); const void *origdata = data; data = to_native_scanline (format, data, xstride, m_scratch, m_dither, y, z); ASSERT (data != NULL); if (data == origdata) { m_scratch.assign ((unsigned char *)data, (unsigned char *)data+m_spec.scanline_bytes()); data = &m_scratch[0]; } // store the offset to the scanline. We'll swap_endian if necessary // when we go to actually write it. m_sot[m_spec.height-1 - (y-m_spec.y)] = (uint32_t)ftell (m_file); size_t pixelsize = m_spec.pixel_bytes (true /*native*/); int offset = 0; for (int c = 0; c < m_spec.nchannels; ++c) { TypeDesc chantype = m_spec.channelformats.size() ? m_spec.channelformats[c] : m_spec.format; int bits = (c < m_rla.NumOfColorChannels) ? m_rla.NumOfChannelBits : (c < (m_rla.NumOfColorChannels+m_rla.NumOfMatteBits)) ? m_rla.NumOfMatteBits : m_rla.NumOfAuxBits; if (!encode_channel ((unsigned char *)data + offset, pixelsize, chantype, bits)) return false; offset += chantype.size(); } return true; } bool RLAOutput::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { // Emulate tiles by buffering the whole image return copy_tile_to_image_buffer (x, y, z, format, data, xstride, ystride, zstride, &m_tilebuffer[0]); } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/bmp.imageio/0000755000175000017500000000000013151711064017303 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/bmp.imageio/bmp_pvt.h0000644000175000017500000001557113151711064021134 0ustar mfvmfv/* Copyright 2008-2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_BMP_H #define OPENIMAGEIO_BMP_H #include #include "OpenImageIO/imageio.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" OIIO_PLUGIN_NAMESPACE_BEGIN namespace bmp_pvt { // size of the BMP file header (the first header that occur in BMP file) const int BMP_HEADER_SIZE = 14; // sizes of various DIB haders const int OS2_V1 = 12; const int WINDOWS_V3 = 40; const int WINDOWS_V4 = 108; const int WINDOWS_V5 = 124; // bmp magic numbers const int16_t MAGIC_BM = 0x4D42; const int16_t MAGIC_BA = 0x4142; const int16_t MAGIC_CI = 0x4943; const int16_t MAGIC_CP = 0x5043; const int16_t MAGIC_PT = 0x5450; //const int32_t RLE4_COMPRESSION = 2; // store informations about BMP file class BmpFileHeader { public: // reads informations about BMP file bool read_header (FILE *fd); // writes information about bmp file to given file bool write_header (FILE *fd); // return true if given file is BMP file bool isBmp () const; int16_t magic; // used to identify BMP file int32_t fsize; // size of the BMP file int16_t res1; // reserved int16_t res2; // reserved int32_t offset; //offset of image data private: void swap_endian (void); }; // stores information about bitmap class DibInformationHeader { public: // reads informations about bitmap bool read_header (FILE *fd); // writes informations about bitmap bool write_header (FILE *fd); int32_t size; // size of the header int32_t width; // bitmap width in pixels int32_t height; // bitmap height in pixels int16_t cplanes; // number of color planes - always 1 int16_t bpp; // number of bits per pixel, image color depth int32_t compression; // compression used in file int32_t isize; // size of the raw image data int32_t hres; // horizontal resolution in pixels per meter int32_t vres; // vertical resolutions in pixels per meter int32_t cpalete; // number of entries in the color palete int32_t important; // number of importatn color used, // 0 - all colors are important, // in most cases ignored // added in Version 4 of the format int32_t red_mask; int32_t blue_mask; int32_t green_mask; int32_t alpha_mask; int32_t cs_type; //color space type int32_t red_x; int32_t red_y; int32_t red_z; int32_t green_x; int32_t green_y; int32_t green_z; int32_t blue_x; int32_t blue_y; int32_t blue_z; int32_t gamma_x; int32_t gamma_y; int32_t gamma_z; // added in Version 5 of the format int32_t intent; int32_t profile_data; int32_t profile_size; int32_t reserved; private: void swap_endian (void); }; struct color_table { uint8_t b; uint8_t g; uint8_t r; uint8_t unused; }; } //namespace bmp_pvt class BmpInput : public ImageInput { public: BmpInput () { init (); } virtual ~BmpInput () { close (); } virtual const char *format_name (void) const { return "bmp"; } virtual bool valid_file (const std::string &filename) const; virtual bool open (const std::string &name, ImageSpec &spec); virtual bool close (void); virtual bool read_native_scanline (int y, int z, void *data); private: int m_padded_scanline_size; int m_pad_size; FILE *m_fd; bmp_pvt::BmpFileHeader m_bmp_header; bmp_pvt::DibInformationHeader m_dib_header; std::string m_filename; std::vector m_colortable; fpos_t m_image_start; void init (void) { m_padded_scanline_size = 0; m_pad_size = 0; m_fd = NULL; m_filename.clear (); m_colortable.clear (); } bool read_color_table (void); }; class BmpOutput : public ImageOutput { public: BmpOutput () { init (); } virtual ~BmpOutput () { close (); } virtual const char *format_name (void) const { return "bmp"; } virtual int supports (string_view feature) const; virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode); virtual bool close (void); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); private: int m_padded_scanline_size; FILE *m_fd; std::string m_filename; bmp_pvt::BmpFileHeader m_bmp_header; bmp_pvt::DibInformationHeader m_dib_header; fpos_t m_image_start; unsigned int m_dither; std::vector m_tilebuffer; void init (void) { m_padded_scanline_size = 0; m_fd = NULL; m_filename.clear (); } void create_and_write_file_header (void); void create_and_write_bitmap_header (void); }; OIIO_PLUGIN_NAMESPACE_END #endif // OPENIMAGEIO_BMP_H openimageio-1.7.17~dfsg0.orig/src/bmp.imageio/CMakeLists.txt0000644000175000017500000000007113151711064022041 0ustar mfvmfvadd_oiio_plugin (bmpinput.cpp bmpoutput.cpp bmp_pvt.cpp) openimageio-1.7.17~dfsg0.orig/src/bmp.imageio/bmp_pvt.cpp0000644000175000017500000001367513151711064021472 0ustar mfvmfv/* Copyright 2008-2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "bmp_pvt.h" OIIO_PLUGIN_NAMESPACE_BEGIN namespace bmp_pvt { /// Helper - write, with error detection template bool fwrite (FILE *fd, const T *buf) { size_t n = std::fwrite (buf, sizeof(T), 1, fd); return n == 1; } /// Helper - read, with error detection template bool fread (FILE *fd, T *buf, size_t itemsize=sizeof(T)) { size_t n = std::fread (buf, itemsize, 1, fd); return n == 1; } bool BmpFileHeader::read_header (FILE *fd) { if (!fread(fd, &magic) || !fread(fd, &fsize) || !fread(fd, &res1) || !fread(fd, &res2) || !fread(fd, &offset)) { return false; } if (bigendian ()) swap_endian (); return true; } bool BmpFileHeader::write_header (FILE *fd) { if (bigendian ()) swap_endian (); if (!fwrite(fd, &magic) || !fwrite(fd, &fsize) || !fwrite(fd, &res1) || !fwrite(fd, &res2) || !fwrite(fd, &offset)) { return false; } return true; } bool BmpFileHeader::isBmp () const { switch (magic) { case MAGIC_BM: case MAGIC_BA: case MAGIC_CI: case MAGIC_CP: case MAGIC_PT: return true; } return false; } void BmpFileHeader::swap_endian (void) { OIIO::swap_endian (&magic); OIIO::swap_endian (&fsize); OIIO::swap_endian (&offset); } bool DibInformationHeader::read_header (FILE *fd) { if (!fread (fd, &size)) return false; if (size == WINDOWS_V3 || size == WINDOWS_V4 || size == WINDOWS_V5) { if (!fread(fd, &width) || !fread(fd, &height) || !fread(fd, &cplanes) || !fread(fd, &bpp) || !fread(fd, &compression) || !fread(fd, &isize) || !fread(fd, &hres) || !fread(fd, &vres) || !fread(fd, &cpalete) || !fread(fd, &important)) { return false; } if (size == WINDOWS_V4 || size == WINDOWS_V5) { if (!fread (fd, &red_mask) || !fread (fd, &blue_mask) || !fread (fd, &green_mask) || !fread (fd, &alpha_mask) || !fread (fd, &cs_type) || !fread (fd, &red_x) || !fread (fd, &red_y) || !fread (fd, &red_z) || !fread (fd, &green_x) || !fread (fd, &green_y) || !fread (fd, &green_z) || !fread (fd, &blue_x) || !fread (fd, &blue_y) || !fread (fd, &blue_z) || !fread (fd, &gamma_x) || !fread (fd, &gamma_y) || !fread (fd, &gamma_z)) { return false; } } if (size == WINDOWS_V5) { if (!fread (fd, &intent) || !fread (fd, &profile_data) || !fread (fd, &profile_size) || !fread (fd, &reserved)) { return false; } } } else if (size == OS2_V1) { // some of theses fields are smaller then in WINDOWS_Vx headers, // so we use hardcoded counts width = 0; height = 0; if (!fread (fd, &width, 2) || !fread (fd, &height, 2) || !fread (fd, &cplanes) || !fread (fd, &bpp)) { return false; } } if (bigendian ()) swap_endian (); return true; } bool DibInformationHeader::write_header (FILE *fd) { if (bigendian ()) swap_endian (); if (!fwrite (fd, &size) || !fwrite (fd, &width) || !fwrite (fd, &height) || !fwrite (fd, &cplanes) || !fwrite (fd, &bpp) || !fwrite (fd, &compression) || !fwrite (fd, &isize) || !fwrite (fd, &hres) || !fwrite (fd, &vres) || !fwrite (fd, &cpalete) || !fwrite (fd, &important)) { return false; } return (true); } void DibInformationHeader::swap_endian () { OIIO::swap_endian (&size); OIIO::swap_endian (&width); OIIO::swap_endian (&height); OIIO::swap_endian (&cplanes); OIIO::swap_endian (&bpp); OIIO::swap_endian (&compression); OIIO::swap_endian (&isize); OIIO::swap_endian (&hres); OIIO::swap_endian (&vres); OIIO::swap_endian (&cpalete); OIIO::swap_endian (&important); } } // bmp_pvt namespace OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/bmp.imageio/bmpinput.cpp0000644000175000017500000002243613151711064021654 0ustar mfvmfv/* Copyright 2008-2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "bmp_pvt.h" OIIO_PLUGIN_NAMESPACE_BEGIN using namespace bmp_pvt; // Obligatory material to make this a recognizeable imageio plugin OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT int bmp_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* bmp_imageio_library_version () { return NULL; } OIIO_EXPORT ImageInput *bmp_input_imageio_create () { return new BmpInput; } OIIO_EXPORT const char *bmp_input_extensions[] = { "bmp", NULL }; OIIO_PLUGIN_EXPORTS_END bool BmpInput::valid_file (const std::string &filename) const { FILE *fd = Filesystem::fopen (filename, "rb"); if (!fd) return false; bmp_pvt::BmpFileHeader bmp_header; bool ok = bmp_header.read_header(fd) && bmp_header.isBmp(); fclose (fd); return ok; } bool BmpInput::open (const std::string &name, ImageSpec &spec) { // saving 'name' for later use m_filename = name; m_fd = Filesystem::fopen (m_filename, "rb"); if (!m_fd) { error ("Could not open file \"%s\"", name.c_str()); return false; } // we read header of the file that we think is BMP file if (! m_bmp_header.read_header (m_fd)) { error ("\"%s\": wrong bmp header size", m_filename.c_str()); close (); return false; } if (! m_bmp_header.isBmp ()) { error ("\"%s\" is not a BMP file, magic number doesn't match", m_filename.c_str()); close (); return false; } if (! m_dib_header.read_header (m_fd)) { error ("\"%s\": wrong bitmap header size", m_filename.c_str()); close (); return false; } const int nchannels = (m_dib_header.bpp == 32) ? 4 : 3; const int height = (m_dib_header.height >= 0) ? m_dib_header.height : -m_dib_header.height; m_spec = ImageSpec (m_dib_header.width, height, nchannels, TypeDesc::UINT8); m_spec.attribute ("XResolution", (int)m_dib_header.hres); m_spec.attribute ("YResolution", (int)m_dib_header.vres); m_spec.attribute ("ResolutionUnit", "m"); // comupting size of one scanline - this is the size of one scanline that // is stored in the file, not in the memory int swidth = 0; switch (m_dib_header.bpp) { case 32 : case 24 : m_padded_scanline_size = ((m_spec.width * m_spec.nchannels) + 3) & ~3; break; case 16 : m_padded_scanline_size = ((m_spec.width << 1) + 3) & ~3; m_spec.attribute ("oiio:BitsPerSample", 4); break; case 8 : m_padded_scanline_size = (m_spec.width + 3) & ~3; if (! read_color_table ()) return false; break; case 4 : swidth = (m_spec.width + 1) / 2; m_padded_scanline_size = (swidth + 3) & ~3; if (! read_color_table ()) return false; break; case 1 : swidth = (m_spec.width + 7) / 8; m_padded_scanline_size = (swidth + 3) & ~3; if (! read_color_table ()) return false; break; } // file pointer is set to the beginning of image data // we save this position - it will be helpfull in read_native_scanline fgetpos (m_fd, &m_image_start); spec = m_spec; return true; } bool BmpInput::read_native_scanline (int y, int z, void *data) { if (y < 0 || y > m_spec.height) return false; // if the height is positive scanlines are stored bottom-up if (m_dib_header.width >= 0) y = m_spec.height - y - 1; const int scanline_off = y * m_padded_scanline_size; std::vector fscanline (m_padded_scanline_size); fsetpos (m_fd, &m_image_start); fseek (m_fd, scanline_off, SEEK_CUR); size_t n = fread (&fscanline[0], 1, m_padded_scanline_size, m_fd); if (n != (size_t)m_padded_scanline_size) { if (feof (m_fd)) error ("Hit end of file unexpectedly"); else error ("read error"); return false; // Read failed } // in each case we process only first m_spec.scanline_bytes () bytes // as only they contain information about pixels. The rest are just // because scanline size have to be 32-bit boundary if (m_dib_header.bpp == 24 || m_dib_header.bpp == 32) { for (unsigned int i = 0; i < m_spec.scanline_bytes (); i += m_spec.nchannels) std::swap (fscanline[i], fscanline[i+2]); memcpy (data, &fscanline[0], m_spec.scanline_bytes()); return true; } std::vector mscanline (m_spec.scanline_bytes()); if (m_dib_header.bpp == 16) { const uint16_t RED = 0x7C00; const uint16_t GREEN = 0x03E0; const uint16_t BLUE = 0x001F; for (unsigned int i = 0, j = 0; j < m_spec.scanline_bytes(); i+=2, j+=3) { uint16_t pixel = (uint16_t)*(&fscanline[i]); mscanline[j] = (uint8_t)((pixel & RED) >> 8); mscanline[j+1] = (uint8_t)((pixel & GREEN) >> 4); mscanline[j+2] = (uint8_t)(pixel & BLUE); } } if (m_dib_header.bpp == 8) { for (unsigned int i = 0, j = 0; j < m_spec.scanline_bytes(); ++i, j+=3) { mscanline[j] = m_colortable[fscanline[i]].r; mscanline[j+1] = m_colortable[fscanline[i]].g; mscanline[j+2] = m_colortable[fscanline[i]].b; } } if (m_dib_header.bpp == 4) { for (unsigned int i = 0, j = 0; j + 6 < m_spec.scanline_bytes(); ++i, j+=6) { uint8_t mask = 0xF0; mscanline[j] = m_colortable[(fscanline[i] & mask) >> 4].r; mscanline[j+1] = m_colortable[(fscanline[i] & mask) >> 4].g; mscanline[j+2] = m_colortable[(fscanline[i] & mask) >> 4].b; mask = 0x0F; mscanline[j+3] = m_colortable[fscanline[i] & mask].r; mscanline[j+4] = m_colortable[fscanline[i] & mask].g; mscanline[j+5] = m_colortable[fscanline[i] & mask].b; } } if (m_dib_header.bpp == 1) { for (unsigned int i = 0, k = 0; i < fscanline.size (); ++i) { for (int j = 7; j >= 0; --j, k+=3) { if (k + 2 >= mscanline.size()) break; int index = 0; if (fscanline[i] & (1 << j)) index = 1; mscanline[k] = m_colortable[index].r; mscanline[k+1] = m_colortable[index].g; mscanline[k+2] = m_colortable[index].b; } } } memcpy (data, &mscanline[0], m_spec.scanline_bytes()); return true; } bool inline BmpInput::close (void) { if (m_fd) { fclose (m_fd); m_fd = NULL; } init (); return true; } bool BmpInput::read_color_table (void) { // size of color table is defined by m_dib_header.cpalete // if this field is 0 - color table has max colors: // pow(2, m_dib_header.cpalete) otherwise color table have // m_dib_header.cpalete entries const int32_t colors = (m_dib_header.cpalete) ? m_dib_header.cpalete : 1 << m_dib_header.bpp; size_t entry_size = 4; // if the file is OS V2 bitmap color table entr has only 3 bytes, not four if (m_dib_header.size == OS2_V1) entry_size = 3; m_colortable.resize (colors); for (int i = 0; i < colors; i++) { size_t n = fread (&m_colortable[i], 1, entry_size, m_fd); if (n != entry_size) { if (feof (m_fd)) error ("Hit end of file unexpectedly while reading color table"); else error ("read error while reading color table"); return false; // Read failed } } return true; // ok } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/bmp.imageio/bmpoutput.cpp0000644000175000017500000001567113151711064022060 0ustar mfvmfv/* Copyright 2008-2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "OpenImageIO/strutil.h" #include "bmp_pvt.h" OIIO_PLUGIN_NAMESPACE_BEGIN using namespace bmp_pvt; // Obligatory material to make this a recognizeable imageio plugin OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *bmp_output_imageio_create () { return new BmpOutput; } OIIO_EXPORT const char *bmp_output_extensions[] = { "bmp", NULL }; OIIO_PLUGIN_EXPORTS_END int BmpOutput::supports (string_view feature) const { return (feature == "alpha"); } bool BmpOutput::open (const std::string &name, const ImageSpec &spec, OpenMode mode) { if (mode != Create) { error ("%s does not support subimages or MIP levels", format_name()); return false; } // saving 'name' and 'spec' for later use m_filename = name; m_spec = spec; if (m_spec.nchannels != 3 && m_spec.nchannels != 4) { error ("%s does not support %d-channel images\n", format_name(), m_spec.nchannels); return false; } m_fd = Filesystem::fopen (m_filename, "wb"); if (! m_fd) { error ("Unable to open file \"%s\"", m_filename.c_str ()); return false; } create_and_write_file_header (); create_and_write_bitmap_header (); // Scanline size is rounded up to align to 4-byte boundary m_padded_scanline_size = ((m_spec.width * m_spec.nchannels) + 3) & ~3; fgetpos (m_fd, &m_image_start); // Only support 8 bit channels for now. m_spec.set_format (TypeDesc::UINT8); m_dither = m_spec.get_int_attribute ("oiio:dither", 0); // If user asked for tiles -- which this format doesn't support, emulate // it by buffering the whole image. if (m_spec.tile_width && m_spec.tile_height) m_tilebuffer.resize (m_spec.image_bytes()); return true; } bool BmpOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { if (y > m_spec.height) { error ("Attempt to write too many scanlines to %s", m_filename.c_str()); close (); return false; } if (m_spec.width >= 0) y = (m_spec.height - y - 1); int scanline_off = y * m_padded_scanline_size; fsetpos (m_fd, &m_image_start); fseek (m_fd, scanline_off, SEEK_CUR); std::vector scratch; data = to_native_scanline (format, data, xstride, scratch, m_dither, y, z); std::vector buf (m_padded_scanline_size); memcpy (&buf[0], data, m_spec.scanline_bytes()); // Swap RGB pixels into BGR format if (m_spec.nchannels >= 3) for (int i = 0, iend = buf.size() - 2; i < iend; i += m_spec.nchannels) std::swap (buf[i], buf[i+2]); size_t byte_count = fwrite (&buf[0], 1, buf.size (), m_fd); return byte_count == buf.size (); // true if wrote all bytes (no error) } bool BmpOutput::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { // Emulate tiles by buffering the whole image return copy_tile_to_image_buffer (x, y, z, format, data, xstride, ystride, zstride, &m_tilebuffer[0]); } bool BmpOutput::close (void) { if (! m_fd) { // already closed init (); return true; } bool ok = true; if (m_spec.tile_width) { // Handle tile emulation -- output the buffered pixels ASSERT (m_tilebuffer.size()); ok &= write_scanlines (m_spec.y, m_spec.y+m_spec.height, 0, m_spec.format, &m_tilebuffer[0]); std::vector().swap (m_tilebuffer); } fclose (m_fd); m_fd = NULL; return ok; } void BmpOutput::create_and_write_file_header (void) { m_bmp_header.magic = MAGIC_BM; const int data_size = m_spec.width * m_spec.height * m_spec.nchannels; const int file_size = data_size + BMP_HEADER_SIZE + WINDOWS_V3; m_bmp_header.fsize = file_size; m_bmp_header.res1 = 0; m_bmp_header.res2 = 0; m_bmp_header.offset = BMP_HEADER_SIZE + WINDOWS_V3; m_bmp_header.write_header (m_fd); } void BmpOutput::create_and_write_bitmap_header (void) { m_dib_header.size = WINDOWS_V3; m_dib_header.width = m_spec.width; m_dib_header.height = m_spec.height; m_dib_header.cplanes = 1; m_dib_header.compression = 0; m_dib_header.bpp = m_spec.nchannels << 3; m_dib_header.isize = m_spec.width * m_spec.height * m_spec.nchannels; m_dib_header.hres = 0; m_dib_header.vres = 0; m_dib_header.cpalete = 0; m_dib_header.important = 0; ImageIOParameter *p = NULL; p = m_spec.find_attribute ("ResolutionUnit", TypeDesc::STRING); if (p && p->data()) { std::string res_units = *(char**)p->data (); if (Strutil::iequals (res_units, "m") || Strutil::iequals (res_units, "pixel per meter")) { ImageIOParameter *resx = NULL, *resy = NULL; resx = m_spec.find_attribute ("XResolution", TypeDesc::INT32); if (resx && resx->data()) m_dib_header.hres = *(int*)resx->data (); resy = m_spec.find_attribute ("YResolution", TypeDesc::INT32); if (resy && resy->data()) m_dib_header.vres = *(int*)resy->data (); } } m_dib_header.write_header (m_fd); } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/iff.imageio/0000755000175000017500000000000013151711064017271 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/iff.imageio/CMakeLists.txt0000644000175000017500000000007113151711064022027 0ustar mfvmfvadd_oiio_plugin (iffinput.cpp iffoutput.cpp iff_pvt.cpp) openimageio-1.7.17~dfsg0.orig/src/iff.imageio/iffoutput.cpp0000644000175000017500000005476213151711064022040 0ustar mfvmfv/* Copyright 2008-2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "iff_pvt.h" OIIO_PLUGIN_NAMESPACE_BEGIN using namespace iff_pvt; // Obligatory material to make this a recognizeable imageio plugin OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *iff_output_imageio_create () { return new IffOutput; } OIIO_EXPORT const char *iff_output_extensions[] = { "iff", "z", NULL }; OIIO_PLUGIN_EXPORTS_END int IffOutput::supports (string_view feature) const { return (feature == "tiles" || feature == "alpha" || feature == "nchannels" ); } bool IffOutput::open (const std::string &name, const ImageSpec &spec, OpenMode mode) { // Autodesk Maya documentation: // "Maya Image File Format - IFF // // Maya supports images in the Interchange File Format (IFF). // IFF is a generic structured file access mechanism, and is not only // limited to images. // // The openimageio IFF implementation deals specifically with Maya IFF // images with it's data blocks structured as follows: // // Header: // FOR4 CIMG // TBHD flags, width, height, compression ... // AUTH attribute ... // DATE attribute ... // FOR4 TBMP // Tiles: // RGBA tile pixels // RGBA tile pixels // RGBA tile pixels // ... if (mode != Create) { error ("%s does not support subimages or MIP levels", format_name()); return false; } close (); // Close any already-opened file // saving 'name' and 'spec' for later use m_filename = name; m_spec = spec; // tiles m_spec.tile_width = tile_width(); m_spec.tile_height = tile_height(); m_spec.tile_depth = 1; m_fd = Filesystem::fopen (m_filename, "wb"); if (!m_fd) { error ("Unable to open file \"%s\"", m_filename.c_str ()); return false; } // IFF image files only supports UINT8 and UINT16. If something // else was requested, revert to the one most likely to be readable // by any IFF reader: UINT8 if (m_spec.format != TypeDesc::UINT8 && m_spec.format != TypeDesc::UINT16) m_spec.set_format (TypeDesc::UINT8); m_dither = (m_spec.format == TypeDesc::UINT8) ? m_spec.get_int_attribute ("oiio:dither", 0) : 0; // check if the client wants the image to be run length encoded // currently only RGB RLE compression is supported, we default to RLE // as Maya does not handle non-compressed IFF's very well. m_iff_header.compression = (m_spec.get_string_attribute("compression") == "none") ? NONE : RLE; // we write the header of the file m_iff_header.x = m_spec.x; m_iff_header.y = m_spec.y; m_iff_header.width = m_spec.width; m_iff_header.height = m_spec.height; m_iff_header.tiles = tile_width_size (m_spec.width) * tile_height_size (m_spec.height); m_iff_header.pixel_bits = m_spec.format == TypeDesc::UINT8 ? 8 : 16; m_iff_header.pixel_channels = m_spec.nchannels; m_iff_header.author = m_spec.get_string_attribute ("Artist"); m_iff_header.date = m_spec.get_string_attribute ("DateTime"); if (!write_header (m_iff_header)) { error ("\"%s\": could not write iff header", m_filename.c_str ()); close (); return false; } m_buf.resize (m_spec.image_bytes()); return true; } bool IffOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { // scanline not used for Maya IFF, uses tiles instead. // Emulate by copying the scanline to the buffer we're accumulating. std::vector scratch; data = to_native_scanline (format, data, xstride, scratch, m_dither, y, z); size_t scanlinesize = spec().scanline_bytes(true); size_t offset = scanlinesize * (y-spec().y) + scanlinesize * spec().height * (z-spec().z); memcpy (&m_buf[offset], data, scanlinesize); return false; } bool IffOutput::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { // auto stride m_spec.auto_stride (xstride, ystride, zstride, format, spec().nchannels, spec().tile_width, spec().tile_height); // native tile data = to_native_tile (format, data, xstride, ystride, zstride, scratch, m_dither, x, y, z); x -= m_spec.x; // Account for offset, so x,y are file relative, not y -= m_spec.y; // image relative // tile size int w = m_spec.width; int tw = std::min (x + m_spec.tile_width, m_spec.width) - x; int th = std::min (y + m_spec.tile_height, m_spec.height) - y; // tile data int iy=0; for(int oy = y; oy < y + th; oy++) { // in uint8_t *in_p = (uint8_t*)data + (iy * m_spec.tile_width) * m_spec.pixel_bytes(); // out uint8_t *out_p = &m_buf[0] + (oy * w + x) * m_spec.pixel_bytes(); // copy memcpy (out_p, in_p, tw * m_spec.pixel_bytes()); iy++; } return true; } inline bool IffOutput::close (void) { if (m_fd) { // flip buffer to make write tile easier, // from tga.imageio: int bytespp = m_spec.pixel_bytes(); std::vector flip (m_spec.width * bytespp); unsigned char *src, *dst, *tmp = &flip[0]; for (int y = 0; y < m_spec.height / 2; y++) { src = &m_buf[(m_spec.height - y - 1) * m_spec.width * bytespp]; dst = &m_buf[y * m_spec.width * bytespp]; memcpy (tmp, src, m_spec.width * bytespp); memcpy (src, dst, m_spec.width * bytespp); memcpy (dst, tmp, m_spec.width * bytespp); } // write y-tiles for(uint32_t ty=0; ty scratch; scratch.resize (tile_length); uint8_t * out_p = static_cast(&scratch[0]); // handle 8-bit data if (m_spec.format == TypeDesc::UINT8) { if (tile_compress) { uint32_t index = 0, size = 0; std::vector tmp; // set bytes. tmp.resize (tile_length * 2); // map: RGB(A) to BGRA for (int c =(channels * m_spec.channel_bytes()) - 1; c>=0; --c) { std::vector in (tw * th); uint8_t *in_p = &in[0]; // set tile for (uint16_t py=ymin; py<=ymax; py++) { const uint8_t * in_dy = &m_buf[0] + (py * m_spec.width) * m_spec.pixel_bytes(); for (uint16_t px=xmin; px<=xmax; px++) { // get pixel uint8_t pixel; const uint8_t * in_dx = in_dy + px * m_spec.pixel_bytes() + c; memcpy (&pixel, in_dx, 1); // set pixel *in_p++ = pixel; } } // compress rle channel size = compress_rle_channel (&in[0], &tmp[0] + index, tw * th); index += size; } // if size exceeds tile length write uncompressed if (index < tile_length) { memcpy (&scratch[0], &tmp[0], index); // set tile length tile_length = index; // append xmin, xmax, ymin and ymax length = index + 8; // set length uint32_t align = align_size (length, 4); if (align > length) { out_p = &scratch[0] + index; // Pad. for (uint32_t i=0; i=0; --c) { // get pixel uint8_t pixel; const uint8_t * in_dx = in_dy + px * m_spec.pixel_bytes() + c * m_spec.channel_bytes(); memcpy (&pixel, in_dx, 1); // set pixel *out_p++ = pixel; } } } } } // handle 16-bit data else if (m_spec.format == TypeDesc::UINT16) { if (tile_compress) { uint32_t index = 0, size = 0; std::vector tmp; // set bytes. tmp.resize (tile_length * 2); // set map std::vector map; if (littleendian()) { int rgb16[] = { 0, 2, 4, 1, 3, 5 }; int rgba16[] = { 0, 2, 4, 7, 1, 3, 5, 6 }; if (m_iff_header.pixel_channels == 3) { map = std::vector( rgb16, &rgb16[6] ); } else { map = std::vector( rgba16, &rgba16[8] ); } } else { int rgb16[] = { 1, 3, 5, 0, 2, 4 }; int rgba16[] = { 1, 3, 5, 7, 0, 2, 4, 6 }; if (m_iff_header.pixel_channels == 3) { map = std::vector( rgb16, &rgb16[6] ); } else { map = std::vector( rgba16, &rgba16[8] ); } } // map: RRGGBB(AA) to BGR(A)BGR(A) for (int c =(channels * m_spec.channel_bytes()) - 1; c>=0; --c) { int mc = map[c]; std::vector in (tw * th); uint8_t *in_p = &in[0]; // set tile for (uint16_t py=ymin; py<=ymax; py++) { const uint8_t * in_dy = &m_buf[0] + (py * m_spec.width) * m_spec.pixel_bytes(); for (uint16_t px=xmin; px<=xmax; px++) { // get pixel uint8_t pixel; const uint8_t * in_dx = in_dy + px * m_spec.pixel_bytes() + mc; memcpy (&pixel, in_dx, 1); // set pixel. *in_p++ = pixel; } } // compress rle channel size = compress_rle_channel (&in[0], &tmp[0] + index, tw * th); index += size; } // if size exceeds tile length write uncompressed if (index < tile_length) { memcpy (&scratch[0], &tmp[0], index); // set tile length tile_length = index; // append xmin, xmax, ymin and ymax length = index + 8; // set length uint32_t align = align_size (length, 4); if (align > length) { out_p = &scratch[0] + index; // Pad. for (uint32_t i=0; i=0; --c) { uint16_t pixel; const uint8_t * in_dx = in_dy + px * m_spec.pixel_bytes() + c * m_spec.channel_bytes(); memcpy (&pixel, in_dx, 2); if (littleendian()) { swap_endian (&pixel); } // set pixel *out_p++ = pixel & 0xff; *out_p++ = pixel >> 8; } } } } } // write 'RGBA' length if (littleendian()) swap_endian (&length); if (!fwrite (&length, sizeof (length), 1, m_fd)) return false; // write xmin, xmax, ymin and ymax if (littleendian()) { swap_endian (&xmin); swap_endian (&ymin); swap_endian (&xmax); swap_endian (&ymax); } if (!fwrite (&xmin, sizeof (xmin), 1, m_fd) || !fwrite (&ymin, sizeof (ymin), 1, m_fd) || !fwrite (&xmax, sizeof (xmax), 1, m_fd) || !fwrite (&ymax, sizeof (ymax), 1, m_fd)) return false; // write tile if (!fwrite (&scratch[0], tile_length, 1, m_fd)) return false; } } // set sizes uint32_t pos, tmppos; pos = ftell (m_fd); uint32_t p0 = pos - 8; uint32_t p1 = p0 - m_iff_header.for4_start; // set pos tmppos = 4; fseek (m_fd, tmppos, SEEK_SET); // write FOR4 CIMG if (littleendian()) { swap_endian (&p0); } if (!fwrite (&p0, sizeof (p0), 1, m_fd)) return false; // set pos tmppos = m_iff_header.for4_start + 4; fseek (m_fd, tmppos, SEEK_SET); // write FOR4 TBMP if (littleendian()) { swap_endian (&p1); } if (!fwrite (&p1, sizeof (p1), 1, m_fd)) return false; // close the stream fclose (m_fd); m_fd = NULL; } return true; } void IffOutput::compress_verbatim ( const uint8_t *& in, uint8_t *& out, int size) { int count = 1; unsigned char byte = 0; // two in a row or count for (; count < size; ++count) { if (in[count - 1] == in[count]) { if (byte == in[count - 1]) { count -= 2; break; } } byte = in[count - 1]; } // copy *out++ = count - 1; memcpy (out, in, count); out += count; in += count; } void IffOutput::compress_duplicate ( const uint8_t *& in, uint8_t *& out, int size) { int count = 1; for (; count < size; ++count) { if (in[count - 1] != in[count]) break; } const bool run = count > 1; const int length = run ? 1 : count; // copy *out++ = ((count - 1) & 0x7f) | (run << 7); *out = *in; out += length; in += count; } size_t IffOutput::compress_rle_channel ( const uint8_t * in, uint8_t * out, int size) { const uint8_t * const _out = out; const uint8_t * const end = in + size; while (in < end) { // find runs const int max = std::min (0x7f + 1, static_cast(end - in)); if (max > 0) { if (in < (end-1) && in[0] == in[1]) { // compress duplicate compress_duplicate (in, out, max); } else { // compress verbatim compress_verbatim (in, out, max); } } } const size_t r = out - _out; return r; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/iff.imageio/iff_pvt.cpp0000644000175000017500000003552713151711064021446 0ustar mfvmfv/* Copyright 2008-2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "iff_pvt.h" #include "OpenImageIO/dassert.h" OIIO_PLUGIN_NAMESPACE_BEGIN using namespace iff_pvt; bool IffFileHeader::read_header (FILE *fd) { uint8_t type[4]; uint32_t size; uint32_t chunksize; uint32_t tbhdsize; uint32_t flags; uint16_t bytes; uint16_t prnum; uint16_t prden; // read FOR4 CIMG. for (;;) { // get type if (!fread (&type, 1, sizeof (type), fd) || // get length !fread (&size, 1, sizeof (size), fd)) return false; if (littleendian()) swap_endian (&size); chunksize = align_size (size, 4); if (type[0] == 'F' && type[1] == 'O' && type[2] == 'R' && type[3] == '4') { // get type if (!fread (&type, 1, sizeof (type), fd)) return false; // check if CIMG if (type[0] == 'C' && type[1] == 'I' && type[2] == 'M' && type[3] == 'G') { // read TBHD. for (;;) { // get type if (!fread (&type, 1, sizeof (type), fd) || // get length !fread (&size, 1, sizeof (size), fd)) return false; if (littleendian()) swap_endian (&size); chunksize = align_size (size, 4); if (type[0] == 'T' && type[1] == 'B' && type[2] == 'H' && type[3] == 'D') { tbhdsize = size; // test if table header size is correct if (tbhdsize!=24 && tbhdsize!=32) return false; // bad table header size // get width and height if (!fread (&width, 1, sizeof (width), fd) || !fread (&height, 1, sizeof (height), fd) || // get prnum and prdeb !fread (&prnum, 1, sizeof (prnum), fd) || !fread (&prden, 1, sizeof (prden), fd) || // get flags, bytes, tiles and compressed !fread (&flags, 1, sizeof (flags), fd) || !fread (&bytes, 1, sizeof (bytes), fd) || !fread (&tiles, 1, sizeof (tiles), fd) || !fread (&compression, 1, sizeof (compression), fd)) return false; // get xy if (tbhdsize == 32) { if (!fread (&x, 1, sizeof (x), fd) || !fread (&y, 1, sizeof (y), fd)) return false; } else { x = 0; y = 0; } // swap endianness if (littleendian()) { swap_endian (&width); swap_endian (&height); swap_endian (&prnum); swap_endian (&prden); swap_endian (&flags); swap_endian (&bytes); swap_endian (&tiles); swap_endian (&compression); } // tiles if (tiles == 0) return false; // non-tiles not supported // 0 no compression // 1 RLE compression // 2 QRL (not supported) // 3 QR4 (not supported) if (compression > 1) return false; // test format. if (flags & RGBA) { // test if black is set DASSERT (!(flags & BLACK)); // test for RGB channels. if (flags & RGB) { pixel_channels = 3; } // test for alpha channel if (flags & ALPHA) { pixel_channels++; } // test pixel bits if (!bytes) { pixel_bits = 8; // 8bit } else { pixel_bits = 16; // 16bit } } // Z format. else if (flags & ZBUFFER) { pixel_channels = 1; pixel_bits = 32; // 32bit // NOTE: Z_F32 support - not supported DASSERT (bytes==0); } // read AUTH, DATE or FOR4 for (;;) { // get type if (!fread (&type, 1, sizeof (type), fd) || // get length !fread (&size, 1, sizeof (size), fd)) return false; if (littleendian()) swap_endian (&size); chunksize = align_size (size, 4); if (type[0] == 'A' && type[1] == 'U' && type[2] == 'T' && type[3] == 'H') { std::vector str (chunksize); if (!fread (&str[0], 1, chunksize, fd)) return false; // get author author = std::string (&str[0], size); } else if (type[0] == 'D' && type[1] == 'A' && type[2] == 'T' && type[3] == 'E') { std::vector str (chunksize); if (!fread (&str[0], 1, chunksize, fd)) return false; // get date date = std::string (&str[0], size); } else if (type[0] == 'F' && type[1] == 'O' && type[2] == 'R' && type[3] == '4') { // get type if (!fread (&type, 1, sizeof (type), fd)) return false; // check if CIMG if (type[0] == 'T' && type[1] == 'B' && type[2] == 'M' && type[3] == 'P') { // tbmp position for later user in in // read_native_tile tbmp_start = ftell (fd); // read first RGBA block to detect tile size. for (unsigned int t=0; t xmax || ymin > ymax || xmax >= width || ymax >= height) { return false; } // set tile width and height tile_width = xmax - xmin + 1; tile_height = ymax - ymin + 1; // done, return return true; } // skip to the next block. if (fseek (fd, chunksize, SEEK_CUR)) return false; } } else { // skip to the next block. if (fseek (fd, chunksize, SEEK_CUR)) return false; } } else { // skip to the next block. if (fseek (fd, chunksize, SEEK_CUR)) return false; } } // TBHD done, break break; } // skip to the next block. if (fseek (fd, chunksize, SEEK_CUR)) return false; } } } // skip to the next block. if (fseek (fd, chunksize, SEEK_CUR)) return false; } return false; } bool IffOutput::write_header (IffFileHeader &header) { // write 'FOR4' type, with 0 length for now (to reserve it) if (! ( write_str ("FOR4") && write_int (0))) return false; // write 'CIMG' type if (! write_str ("CIMG")) return false; // write 'TBHD' type if (! write_str ("TBHD")) return false; // 'TBHD' length, 32 bytes if (! write_int (32)) return false; if (! write_int (header.width) || ! write_int (header.height)) return false; // write prnum and prden (pixel aspect ratio? -- FIXME) if (! write_short (1) || ! write_short (1)) return false; // write flags and channels if (! write_int (header.pixel_channels == 3 ? RGB : RGBA) || ! write_short (header.pixel_bits == 8 ? 0 : 1) || ! write_short (header.tiles)) return false; // write compression // 0 no compression // 1 RLE compression // 2 QRL (not supported) // 3 QR4 (not supported) if (! write_int (header.compression)) return false; // write x and y if (! write_int(header.x) || ! write_int(header.y)) return false; // Write metadata write_meta_string ("AUTH", header.author); write_meta_string ("DATE", header.date); // for4 position for later user in close header.for4_start = ftell (m_fd); // write 'FOR4' type, with 0 length to reserve it for now if (! write_str ("FOR4") || ! write_int (0)) return false; // write 'TBMP' type if (! write_str ("TBMP")) return false; return true; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/iff.imageio/iffinput.cpp0000644000175000017500000004021113151711064021617 0ustar mfvmfv/* Copyright 2008-2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "iff_pvt.h" #include OIIO_PLUGIN_NAMESPACE_BEGIN using namespace iff_pvt; // Obligatory material to make this a recognizeable imageio plugin OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT int iff_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* iff_imageio_library_version () { return NULL; } OIIO_EXPORT ImageInput *iff_input_imageio_create () { return new IffInput; } OIIO_EXPORT const char *iff_input_extensions[] = { "iff", "z", NULL }; OIIO_PLUGIN_EXPORTS_END bool IffInput::open (const std::string &name, ImageSpec &spec) { // Autodesk Maya documentation: // "Maya Image File Format - IFF // // Maya supports images in the Interchange File Format (IFF). // IFF is a generic structured file access mechanism, and is not only // limited to images. // // The openimageio IFF implementation deals specifically with Maya IFF // images with it's data blocks structured as follows: // // Header: // FOR4 CIMG // TBHD flags, width, height, compression ... // AUTH attribute ... // DATE attribute ... // FOR4 TBMP // Tiles: // RGBA tile pixels // RGBA tile pixels // RGBA tile pixels // ... // saving 'name' for later use m_filename = name; m_fd = Filesystem::fopen (m_filename, "rb"); if (!m_fd) { error ("Could not open file \"%s\"", name.c_str()); return false; } // we read header of the file that we think is IFF file if (!m_iff_header.read_header (m_fd)) { error ("\"%s\": could not read iff header", m_filename.c_str ()); close (); return false; } // image specification m_spec = ImageSpec (m_iff_header.width, m_iff_header.height, m_iff_header.pixel_channels, m_iff_header.pixel_bits == 8 ? TypeDesc::UINT8 : TypeDesc::UINT16); // set x, y m_spec.x = m_iff_header.x; m_spec.y = m_iff_header.y; // set full width, height m_spec.full_width = m_iff_header.width; m_spec.full_height = m_iff_header.height; // tiles if (m_iff_header.tile_width > 0 || m_iff_header.tile_height > 0) { m_spec.tile_width = m_iff_header.tile_width; m_spec.tile_height = m_iff_header.tile_height; // only 1 subimage for IFF m_spec.tile_depth = 1; } else { error ("\"%s\": wrong tile size", m_filename.c_str()); close (); return false; } // attributes // compression if (m_iff_header.compression == iff_pvt::RLE) { m_spec.attribute ("compression", "rle"); } // author if (m_iff_header.author.size()) { m_spec.attribute ("Artist", m_iff_header.author); } // date if (m_iff_header.date.size()) { m_spec.attribute ("DateTime", m_iff_header.date); } // file pointer is set to the beginning of tbmp data // we save this position - it will be helpful in read_native_tile m_tbmp_start = m_iff_header.tbmp_start; spec = m_spec; return true; } bool IffInput::read_native_scanline (int y, int z, void *data) { // scanline not used for Maya IFF, uses tiles instead. return false; } bool IffInput::read_native_tile (int x, int y, int z, void *data) { if (m_buf.empty ()) readimg (); // tile size int w = m_spec.width; int tw = std::min (x + m_spec.tile_width, m_spec.width) - x; int th = std::min (y + m_spec.tile_height, m_spec.height) - y; // tile data int oy=0; for(int iy = y; iy < y + th; iy++) { // in uint8_t *in_p = &m_buf[0] + (iy * w + x) * m_spec.pixel_bytes(); // out uint8_t *out_p = (uint8_t*)data + (oy * m_spec.tile_width) * m_spec.pixel_bytes(); // copy memcpy (out_p, in_p, tw * m_spec.pixel_bytes()); oy++; } return true; } bool inline IffInput::close (void) { if (m_fd) { fclose (m_fd); m_fd = NULL; } init (); return true; } bool IffInput::readimg() { uint8_t type[4]; uint32_t size; uint32_t chunksize; // seek pos // set position tile may be called randomly fseek (m_fd, m_tbmp_start, SEEK_SET); // resize buffer m_buf.resize (m_spec.image_bytes()); for (unsigned int t=0; t xmax || ymin > ymax || xmax >= m_spec.width || ymax >= m_spec.height || !tw || !th) { return false; } // tile compress bool tile_compress = false; // if tile compression fails to be less than image data stored // uncompressed the tile is written uncompressed // set channels uint8_t channels = m_iff_header.pixel_channels; // set tile size uint32_t tile_size = tw * th * channels * m_spec.channel_bytes() + 8; // test if compressed // we use the non aligned size if (tile_size > size) { tile_compress = true; } // handle 8-bit data. if (m_iff_header.pixel_bits == 8) { std::vector scratch; // set bytes. scratch.resize (image_size); if (!fread (&scratch[0], 1, scratch.size(), m_fd)) return false; // set tile data uint8_t * p = static_cast(&scratch[0]); // tile compress. if (tile_compress) { // map BGR(A) to RGB(A) for (int c =(channels * m_spec.channel_bytes()) - 1; c>=0; --c) { std::vector in (tw * th); uint8_t *in_p = &in[0]; // uncompress and increment p += uncompress_rle_channel (p, in_p, tw * th); // set tile for (uint16_t py=ymin; py<=ymax; py++) { uint8_t *out_dy = static_cast (&m_buf[0]) + (py * m_spec.width) * m_spec.pixel_bytes(); for (uint16_t px=xmin; px<=xmax; px++) { uint8_t *out_p = out_dy + px * m_spec.pixel_bytes() + c; *out_p++ = *in_p++; } } } } else { int sy=0; for (uint16_t py=ymin; py<=ymax; py++) { uint8_t *out_dy = static_cast(&m_buf[0]) + (py * m_spec.width + xmin) * m_spec.pixel_bytes(); // set tile int sx=0; for (uint16_t px=xmin; px<=xmax; px++) { uint8_t *in_p = p + (sy * tw + sx) * m_spec.pixel_bytes(); // map BGR(A) to RGB(A) for (int c=channels - 1; c>=0; --c) { uint8_t *out_p = in_p + (c * m_spec.channel_bytes()); *out_dy++ = *out_p; } sx++; } sy++; } } } // handle 16-bit data. else if (m_iff_header.pixel_bits == 16) { std::vector scratch; // set bytes. scratch.resize (image_size); if (!fread (&scratch[0], 1, scratch.size(), m_fd)) return false; // set tile data uint8_t * p = static_cast(&scratch[0]); if (tile_compress) { // set map std::vector map; if (littleendian()) { int rgb16[] = { 0, 2, 4, 1, 3, 5 }; int rgba16[] = { 0, 2, 4, 6, 1, 3, 5, 7 }; if (m_iff_header.pixel_channels == 3) { map = std::vector( rgb16, &rgb16[6] ); } else { map = std::vector( rgba16, &rgba16[8] ); } } else { int rgb16[] = { 1, 3, 5, 0, 2, 4 }; int rgba16[] = { 1, 3, 5, 7, 0, 2, 4, 6 }; if (m_iff_header.pixel_channels == 3) { map = std::vector( rgb16, &rgb16[6] ); } else { map = std::vector( rgba16, &rgba16[8] ); } } // map BGR(A)BGR(A) to RRGGBB(AA) for (int c =(channels * m_spec.channel_bytes()) - 1; c>=0; --c) { int mc = map[c]; std::vector in (tw * th); uint8_t *in_p = &in[0]; // uncompress and increment p += uncompress_rle_channel (p, in_p, tw * th); // set tile for (uint16_t py=ymin; py<=ymax; py++) { uint8_t *out_dy = static_cast (&m_buf[0]) + (py * m_spec.width) * m_spec.pixel_bytes(); for (uint16_t px=xmin; px<=xmax; px++) { uint8_t *out_p = out_dy + px * m_spec.pixel_bytes() + mc; *out_p++ = *in_p++; } } } } else { int sy=0; for (uint16_t py=ymin; py<=ymax; py++) { uint8_t *out_dy = static_cast(&m_buf[0]) + (py * m_spec.width + xmin) * m_spec.pixel_bytes(); // set scanline, make copy easier std::vector scanline (tw * m_spec.pixel_bytes()); uint16_t * sl_p = &scanline[0]; // set tile int sx=0; for (uint16_t px=xmin; px<=xmax; px++) { uint8_t *in_p = p + (sy * tw + sx) * m_spec.pixel_bytes(); // map BGR(A) to RGB(A) for (int c=channels - 1; c>=0; --c) { uint16_t pixel; uint8_t * out_p = in_p + (c * m_spec.channel_bytes()); memcpy (&pixel, out_p, 2); // swap endianness if (littleendian()) { swap_endian (&pixel); } *sl_p++ = pixel; } sx++; } // copy data memcpy (out_dy, &scanline[0], tw * m_spec.pixel_bytes()); sy++; } } } else { error ("\"%s\": unsupported number of bits per pixel for tile", m_filename.c_str()); return false; } // tile t++; } else { // skip to the next block if (fseek (m_fd, chunksize, SEEK_CUR)) return false; } } // flip buffer to make read_native_tile easier, // from tga.imageio: int bytespp = m_spec.pixel_bytes(); std::vector flip (m_spec.width * bytespp); unsigned char *src, *dst, *tmp = &flip[0]; for (int y = 0; y < m_spec.height / 2; y++) { src = &m_buf[(m_spec.height - y - 1) * m_spec.width * bytespp]; dst = &m_buf[y * m_spec.width * bytespp]; memcpy (tmp, src, m_spec.width * bytespp); memcpy (src, dst, m_spec.width * bytespp); memcpy (dst, tmp, m_spec.width * bytespp); } return true; } size_t IffInput::uncompress_rle_channel( const uint8_t * in, uint8_t * out, int size ) { const uint8_t * const _in = in; const uint8_t * const end = out + size; while (out < end) { // information. const uint8_t count = (*in & 0x7f) + 1; const bool run = (*in & 0x80) ? true : false; ++in; // find runs if (!run) { // verbatim for (int i=0; i #include "OpenImageIO/imageio.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" OIIO_PLUGIN_NAMESPACE_BEGIN namespace iff_pvt { // compression numbers const uint32_t NONE = 0; const uint32_t RLE = 1; const uint32_t QRL = 2; const uint32_t QR4 = 3; const uint32_t RGB = 0x00000001; const uint32_t ALPHA = 0x00000002; const uint32_t RGBA = RGB | ALPHA; const uint32_t ZBUFFER = 0x00000004; const uint32_t BLACK = 0x00000010; // store informations about IFF file class IffFileHeader { public: // reads information about IFF file bool read_header (FILE *fd); // header information uint32_t x; uint32_t y; uint32_t width; uint32_t height; uint32_t compression; uint8_t pixel_bits; uint8_t pixel_channels; uint16_t tiles; uint16_t tile_width; uint16_t tile_height; // author string std::string author; // date string std::string date; // tbmp start uint32_t tbmp_start; // for4 start uint32_t for4_start; }; // align size inline uint32_t align_size (uint32_t size, uint32_t alignment) { uint32_t mod = size % alignment; if (mod) { mod = alignment - mod; size += mod; } return size; } // tile width inline const int & tile_width() { static int tile_w = 64; return tile_w; } // tile height inline const int & tile_height() { static int tile_h = 64; return tile_h; } // tile width size inline uint32_t tile_width_size (uint32_t width) { uint32_t tw = tile_width(); return (width + tw - 1) / tw; } // tile height size inline uint32_t tile_height_size (uint32_t height) { uint32_t th = tile_height(); return (height + th - 1) / th; } } // namespace iff_pvt class IffInput : public ImageInput { public: IffInput () { init(); } virtual ~IffInput () { close(); } virtual const char *format_name (void) const { return "iff"; } virtual bool open (const std::string &name, ImageSpec &spec); virtual bool close (void); virtual bool read_native_scanline (int y, int z, void *data); virtual bool read_native_tile (int x, int y, int z, void *data); private: FILE *m_fd; std::string m_filename; iff_pvt::IffFileHeader m_iff_header; std::vector m_buf; uint32_t m_tbmp_start; // init to initialize state void init (void) { m_fd = NULL; m_filename.clear (); m_buf.clear(); } // helper to read an image bool readimg (void); // helper to uncompress a rle channel size_t uncompress_rle_channel(const uint8_t *in, uint8_t * out, int size); bool read_short (uint16_t& val) { bool ok = fread (&val, sizeof(val), 1, m_fd); if (littleendian()) swap_endian (&val); return ok; } bool read_int (uint32_t& val) { bool ok = fread (&val, sizeof(val), 1, m_fd); if (littleendian()) swap_endian (&val); return ok; } bool read_str (std::string &val, uint32_t len, uint32_t round = 4) { const uint32_t big = 1024; char strbuf[big]; len = std::min (len, big); bool ok = fread (strbuf, len, 1, m_fd); val.assign (strbuf, len); for (uint32_t pad = len%round; pad; --pad) fgetc (m_fd); return ok; } bool read_type_len (std::string &type, uint32_t &len) { return read_str (type, 4) && read_int (len); } bool read_meta_string (std::string &name, std::string &val) { uint32_t len = 0; return read_type_len (name, len) && read_str (val, len); } }; class IffOutput : public ImageOutput { public: IffOutput () { init (); } virtual ~IffOutput () { close (); } virtual const char *format_name (void) const { return "iff"; } virtual int supports (string_view feature) const; virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode); virtual bool close (void); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); private: FILE *m_fd; std::string m_filename; iff_pvt::IffFileHeader m_iff_header; std::vector m_buf; unsigned int m_dither; std::vector scratch; void init (void) { m_fd = NULL; m_filename.clear (); } // writes information about iff file to give file bool write_header (iff_pvt::IffFileHeader &header); bool write_short (uint16_t val) { if (littleendian()) swap_endian (&val); return fwrite (&val, sizeof(val), 1, m_fd); } bool write_int (uint32_t val) { if (littleendian()) swap_endian (&val); return fwrite (&val, sizeof(val), 1, m_fd); } bool write_str (string_view val, size_t round = 4) { bool ok = fwrite (val.data(), val.size(), 1, m_fd); for (size_t i = val.size(); i < round_to_multiple(val.size(), round); ++i) ok &= (fputc (' ', m_fd) != EOF); return ok; } bool write_meta_string (string_view name, string_view val, bool write_if_empty = false) { if (val.empty() && ! write_if_empty) return true; return write_str (name) && write_int (int(val.size())) && (val.size() == 0 || write_str (val)); } // helper to compress verbatim void compress_verbatim (const uint8_t *& in, uint8_t *& out, int size); // helper to compress duplicate void compress_duplicate (const uint8_t *&in, uint8_t *& out, int size); // helper to compress a rle channel size_t compress_rle_channel (const uint8_t *in, uint8_t *out, int size); }; OIIO_PLUGIN_NAMESPACE_END #endif // OPENIMAGEIO_IFF_H openimageio-1.7.17~dfsg0.orig/src/iinfo/0000755000175000017500000000000013151711064016220 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/iinfo/CMakeLists.txt0000644000175000017500000000050213151711064020755 0ustar mfvmfvset (iinfo_srcs iinfo.cpp) add_executable (iinfo ${iinfo_srcs}) set_target_properties (iinfo PROPERTIES FOLDER "Tools") if (MSVC) set_target_properties (OpenImageIO PROPERTIES LINK_FLAGS psapi.lib) endif (MSVC) target_link_libraries (iinfo OpenImageIO ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) oiio_install_targets (iinfo) openimageio-1.7.17~dfsg0.orig/src/iinfo/iinfo.cpp0000644000175000017500000005713713151711064020045 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include #include #include "OpenImageIO/argparse.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/deepdata.h" #include "OpenImageIO/hash.h" #include "OpenImageIO/filesystem.h" OIIO_NAMESPACE_USING; using namespace ImageBufAlgo; static bool verbose = false; static bool sum = false; static bool help = false; static std::vector filenames; static std::string metamatch; static bool filenameprefix = false; static boost::regex field_re; static bool subimages = false; static bool compute_sha1 = false; static bool compute_stats = false; static void print_sha1 (ImageInput *input) { SHA1 sha; const ImageSpec &spec (input->spec()); if (spec.deep) { // Special handling of deep data DeepData dd; if (! input->read_native_deep_image (dd)) { printf (" SHA-1: unable to compute, could not read image\n"); return; } // Hash both the sample counds and the data block sha.append (dd.all_samples()); sha.append (dd.all_data()); } else { imagesize_t size = input->spec().image_bytes (true /*native*/); if (size >= std::numeric_limits::max()) { printf (" SHA-1: unable to compute, image is too big\n"); return; } boost::scoped_array buf (new char [size]); if (! input->read_image (TypeDesc::UNKNOWN /*native*/, &buf[0])) { printf (" SHA-1: unable to compute, could not read image\n"); return; } sha.append (&buf[0], size); } printf (" SHA-1: %s\n", sha.digest().c_str()); } /////////////////////////////////////////////////////////////////////////////// // Stats static bool read_input (const std::string &filename, ImageBuf &img, int subimage=0, int miplevel=0) { if (img.subimage() >= 0 && img.subimage() == subimage) return true; if (img.init_spec (filename, subimage, miplevel) && img.read (subimage, miplevel, false, TypeDesc::FLOAT)) return true; std::cerr << "iinfo ERROR: Could not read " << filename << ":\n\t" << img.geterror() << "\n"; return false; } static void print_stats_num (float val, int maxval, bool round) { if (maxval == 0) { printf("%f",val); } else { float fval = val * static_cast(maxval); if (round) { int v = static_cast(roundf (fval)); printf ("%d", v); } else { printf ("%0.2f", fval); } } } // First check oiio:BitsPerSample int attribute. If not set, // fall back on the TypeDesc. return 0 for float types // or those that exceed the int range (long long, etc) static unsigned long long get_intsample_maxval (const ImageSpec &spec) { TypeDesc type = spec.format; int bits = spec.get_int_attribute ("oiio:BitsPerSample"); if (bits > 0) { if (type.basetype == TypeDesc::UINT8 || type.basetype == TypeDesc::UINT16 || type.basetype == TypeDesc::UINT32) return ((1LL) << bits) - 1; if (type.basetype == TypeDesc::INT8 || type.basetype == TypeDesc::INT16 || type.basetype == TypeDesc::INT32) return ((1LL) << (bits-1)) - 1; } // These correspond to all the int enums in typedesc.h <= int if (type.basetype == TypeDesc::UCHAR) return 0xff; if (type.basetype == TypeDesc::CHAR) return 0x7f; if (type.basetype == TypeDesc::USHORT) return 0xffff; if (type.basetype == TypeDesc::SHORT) return 0x7fff; if (type.basetype == TypeDesc::UINT) return 0xffffffff; if (type.basetype == TypeDesc::INT) return 0x7fffffff; return 0; } static void print_stats_footer (unsigned int maxval) { if (maxval==0) printf ("(float)"); else printf ("(of %u)", maxval); } static void print_stats (const std::string &filename, const ImageSpec &originalspec, int subimage=0, int miplevel=0, bool indentmip=false) { PixelStats stats; const char *indent = indentmip ? " " : " "; ImageBuf input; if (! read_input (filename, input, subimage, miplevel)) { std::cerr << "Stats: read error: " << input.geterror() << "\n"; return; } if (! computePixelStats (stats, input)) { printf ("%sStats: (unable to compute)\n", indent); if (input.has_error()) std::cerr << "Error: " << input.geterror() << "\n"; return; } // The original spec is used, otherwise the bit depth will // be reported incorrectly (as FLOAT) unsigned int maxval = (unsigned int)get_intsample_maxval (originalspec); printf ("%sStats Min: ", indent); for (unsigned int i=0; ipixels(); size_t totalsamples = 0, emptypixels = 0; size_t maxsamples = 0, minsamples = std::numeric_limits::max(); for (size_t p = 0; p < npixels; ++p) { size_t c = dd->samples(p); totalsamples += c; if (c > maxsamples) maxsamples = c; if (c < minsamples) minsamples = c; if (c == 0) ++emptypixels; } printf ("%sMin deep samples in any pixel : %llu\n", indent, (unsigned long long)minsamples); printf ("%sMax deep samples in any pixel : %llu\n", indent, (unsigned long long)maxsamples); printf ("%sAverage deep samples per pixel: %.2f\n", indent, double(totalsamples)/double(npixels)); printf ("%sTotal deep samples in all pixels: %llu\n", indent, (unsigned long long)totalsamples); printf ("%sPixels with deep samples : %llu\n", indent, (unsigned long long)(npixels-emptypixels)); printf ("%sPixels with no deep samples: %llu\n", indent, (unsigned long long)emptypixels); } else { std::vector constantValues(input.spec().nchannels); if (isConstantColor(input, &constantValues[0])) { printf ("%sConstant: Yes\n", indent); printf ("%sConstant Color: ", indent); for (unsigned int i=0; i 1) printf (", z=%d", spec.z); printf ("\n"); printed = true; } } if (spec.full_x || spec.full_y || spec.full_z || (spec.full_width != spec.width && spec.full_width != 0) || (spec.full_height != spec.height && spec.full_height != 0) || (spec.full_depth != spec.depth && spec.full_depth != 0)) { if (metamatch.empty() || boost::regex_search ("full/display size", field_re)) { if (filenameprefix) printf ("%s : ", filename.c_str()); printf (" full/display size: %d x %d", spec.full_width, spec.full_height); if (spec.depth > 1) printf (" x %d", spec.full_depth); printf ("\n"); printed = true; } if (metamatch.empty() || boost::regex_search ("full/display origin", field_re)) { if (filenameprefix) printf ("%s : ", filename.c_str()); printf (" full/display origin: %d, %d", spec.full_x, spec.full_y); if (spec.depth > 1) printf (", %d", spec.full_z); printf ("\n"); printed = true; } } if (spec.tile_width) { if (metamatch.empty() || boost::regex_search ("tile", field_re)) { if (filenameprefix) printf ("%s : ", filename.c_str()); printf (" tile size: %d x %d", spec.tile_width, spec.tile_height); if (spec.depth > 1) printf (" x %d", spec.tile_depth); printf ("\n"); printed = true; } } BOOST_FOREACH (const ImageIOParameter &p, spec.extra_attribs) { if (! metamatch.empty() && ! boost::regex_search (p.name().c_str(), field_re)) continue; std::string s = spec.metadata_val (p, true); if (filenameprefix) printf ("%s : ", filename.c_str()); printf (" %s: ", p.name().c_str()); if (! strcmp (s.c_str(), "1.#INF")) printf ("inf"); else printf ("%s", s.c_str()); printf ("\n"); printed = true; } if (! printed && !metamatch.empty()) { if (filenameprefix) printf ("%s : ", filename.c_str()); printf (" %s: \n", metamatch.c_str()); } } static const char * extended_format_name (TypeDesc type, int bits) { if (bits && bits < (int)type.size()*8) { // The "oiio:BitsPerSample" betrays a different bit depth in the // file than the data type we are passing. if (type == TypeDesc::UINT8 || type == TypeDesc::UINT16 || type == TypeDesc::UINT32 || type == TypeDesc::UINT64) return ustring::format("uint%d", bits).c_str(); if (type == TypeDesc::INT8 || type == TypeDesc::INT16 || type == TypeDesc::INT32 || type == TypeDesc::INT64) return ustring::format("int%d", bits).c_str(); } return type.c_str(); // use the name implied by type } static const char * brief_format_name (TypeDesc type, int bits=0) { if (! bits) bits = (int)type.size()*8; if (type.is_floating_point()) { if (type.basetype == TypeDesc::FLOAT) return "f"; if (type.basetype == TypeDesc::HALF) return "h"; return ustring::format("f%d", bits).c_str(); } else if (type.is_signed()) { return ustring::format("i%d", bits).c_str(); } else { return ustring::format("u%d", bits).c_str(); } return type.c_str(); // use the name implied by type } // prints basic info (resolution, width, height, depth, channels, data format, // and format name) about given subimage. static void print_info_subimage (int current_subimage, int max_subimages, ImageSpec &spec, ImageInput *input, const std::string &filename) { if ( ! input->seek_subimage (current_subimage, 0, spec) ) return; if (! metamatch.empty() && ! boost::regex_search ("resolution, width, height, depth, channels, sha-1, stats", field_re)) { // nothing to do here return; } int nmip = 1; bool printres = verbose && (metamatch.empty() || boost::regex_search ("resolution, width, height, depth, channels", field_re)); if (printres && max_subimages > 1 && subimages) { printf (" subimage %2d: ", current_subimage); printf ("%4d x %4d", spec.width, spec.height); if (spec.depth > 1) printf (" x %4d", spec.depth); int bits = spec.get_int_attribute ("oiio:BitsPerSample", 0); printf (", %d channel, %s%s%s", spec.nchannels, spec.deep ? "deep " : "", spec.depth > 1 ? "volume " : "", extended_format_name(spec.format, bits)); printf (" %s", input->format_name()); printf ("\n"); } // Count MIP levels ImageSpec mipspec; while (input->seek_subimage (current_subimage, nmip, mipspec)) { if (printres) { if (nmip == 1) printf (" MIP-map levels: %dx%d", spec.width, spec.height); printf (" %dx%d", mipspec.width, mipspec.height); } ++nmip; } if (printres && nmip > 1) printf ("\n"); if (compute_sha1 && (metamatch.empty() || boost::regex_search ("sha-1", field_re))) { if (filenameprefix) printf ("%s : ", filename.c_str()); // Before sha-1, be sure to point back to the highest-res MIP level ImageSpec tmpspec; input->seek_subimage (current_subimage, 0, tmpspec); print_sha1 (input); } if (verbose) print_metadata (spec, filename); if (compute_stats && (metamatch.empty() || boost::regex_search ("stats", field_re))) { for (int m = 0; m < nmip; ++m) { ImageSpec mipspec; input->seek_subimage (current_subimage, m, mipspec); if (filenameprefix) printf ("%s : ", filename.c_str()); if (nmip > 1 && (subimages || m == 0)) { printf (" MIP %d of %d (%d x %d):\n", m, nmip, mipspec.width, mipspec.height); } print_stats (filename, spec, current_subimage, m, nmip>1); } } if ( ! input->seek_subimage (current_subimage, 0, spec) ) return; } static void print_info (const std::string &filename, size_t namefieldlength, ImageInput *input, ImageSpec &spec, bool verbose, bool sum, long long &totalsize) { int padlen = std::max (0, (int)namefieldlength - (int)filename.length()); std::string padding (padlen, ' '); // checking how many subimages and mipmap levels are stored in the file int num_of_subimages = 1; bool any_mipmapping = false; std::vector num_of_miplevels; { int nmip = 1; while (input->seek_subimage (input->current_subimage(), nmip, spec)) { ++nmip; any_mipmapping = true; } num_of_miplevels.push_back (nmip); } while (input->seek_subimage (num_of_subimages, 0, spec)) { // maybe we should do this more gently? ++num_of_subimages; int nmip = 1; while (input->seek_subimage (input->current_subimage(), nmip, spec)) { ++nmip; any_mipmapping = true; } num_of_miplevels.push_back (nmip); } input->seek_subimage (0, 0, spec); // re-seek to the first if (metamatch.empty() || boost::regex_search ("resolution, width, height, depth, channels", field_re)) { printf ("%s%s : %4d x %4d", filename.c_str(), padding.c_str(), spec.width, spec.height); if (spec.depth > 1) printf (" x %4d", spec.depth); printf (", %d channel, %s%s", spec.nchannels, spec.deep ? "deep " : "", spec.depth > 1 ? "volume " : ""); if (spec.channelformats.size()) { for (size_t c = 0; c < spec.channelformats.size(); ++c) printf ("%s%s", c ? "/" : "", spec.channelformats[c].c_str()); } else { int bits = spec.get_int_attribute ("oiio:BitsPerSample", 0); printf ("%s", extended_format_name(spec.format, bits)); } printf (" %s", input->format_name()); if (sum) { imagesize_t imagebytes = spec.image_bytes (true); totalsize += imagebytes; printf (" (%.2f MB)", (float)imagebytes / (1024.0*1024.0)); } // we print info about how many subimages are stored in file // only when we have more then one subimage if ( ! verbose && num_of_subimages != 1) printf (" (%d subimages%s)", num_of_subimages, any_mipmapping ? " +mipmap)" : ""); if (! verbose && num_of_subimages == 1 && any_mipmapping) printf (" (+mipmap)"); printf ("\n"); } int movie = spec.get_int_attribute ("oiio:Movie"); if (verbose && num_of_subimages != 1) { // info about num of subimages and their resolutions printf (" %d subimages: ", num_of_subimages); for (int i = 0; i < num_of_subimages; ++i) { input->seek_subimage (i, 0, spec); int bits = spec.get_int_attribute ("oiio:BitsPerSample", spec.format.size()*8); if (i) printf (", "); if (spec.depth > 1) printf ("%dx%dx%d ", spec.width, spec.height, spec.depth); else printf ("%dx%d ", spec.width, spec.height); // printf ("["); for (int c = 0; c < spec.nchannels; ++c) printf ("%c%s", c ? ',' : '[', brief_format_name(spec.channelformat(c), bits)); printf ("]"); if (movie) break; } printf ("\n"); } // if the '-a' flag is not set we print info // about first subimage only if ( ! subimages) num_of_subimages = 1; for (int i = 0; i < num_of_subimages; ++i) { print_info_subimage (i, num_of_subimages, spec, input, filename); } } static int parse_files (int argc, const char *argv[]) { for (int i = 0; i < argc; i++) filenames.push_back (argv[i]); return 0; } int main (int argc, const char *argv[]) { Filesystem::convert_native_arguments (argc, (const char **)argv); ArgParse ap; ap.options ("iinfo -- print information about images\n" OIIO_INTRO_STRING "\n" "Usage: iinfo [options] filename...", "%*", parse_files, "", "--help", &help, "Print help message", "-v", &verbose, "Verbose output", "-m %s", &metamatch, "Metadata names to print (default: all)", "-f", &filenameprefix, "Prefix each line with the filename", "-s", &sum, "Sum the image sizes", "-a", &subimages, "Print info about all subimages", "--hash", &compute_sha1, "Print SHA-1 hash of pixel values", "--stats", &compute_stats, "Print image pixel statistics (data window)", NULL); if (ap.parse(argc, argv) < 0 || filenames.empty()) { std::cerr << ap.geterror() << std::endl; ap.usage (); return EXIT_FAILURE; } if (help) { ap.usage (); exit (EXIT_FAILURE); } if (! metamatch.empty()) field_re.assign (metamatch, boost::regex::extended | boost::regex_constants::icase); // Find the longest filename size_t longestname = 0; BOOST_FOREACH (const std::string &s, filenames) longestname = std::max (longestname, s.length()); longestname = std::min (longestname, (size_t)40); long long totalsize = 0; BOOST_FOREACH (const std::string &s, filenames) { ImageInput *in = ImageInput::open (s.c_str()); if (! in) { std::string err = geterror(); if (err.empty()) err = Strutil::format ("Could not open \"%s\"", s.c_str()); std::cerr << "iinfo: " << err << "\n"; continue; } ImageSpec spec = in->spec(); print_info (s, longestname, in, spec, verbose, sum, totalsize); in->close (); delete in; } if (sum) { double t = (double)totalsize / (1024.0*1024.0); if (t > 1024.0) printf ("Total size: %.2f GB\n", t/1024.0); else printf ("Total size: %.2f MB\n", t); } return 0; } openimageio-1.7.17~dfsg0.orig/src/igrep/0000755000175000017500000000000013151711064016222 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/igrep/CMakeLists.txt0000644000175000017500000000034313151711064020762 0ustar mfvmfvset (igrep_srcs igrep.cpp) add_executable (igrep ${igrep_srcs}) set_target_properties (igrep PROPERTIES FOLDER "Tools") target_link_libraries (igrep OpenImageIO ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) oiio_install_targets (igrep) openimageio-1.7.17~dfsg0.orig/src/igrep/igrep.cpp0000644000175000017500000001462513151711064020044 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include #include #include "OpenImageIO/argparse.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/imageio.h" OIIO_NAMESPACE_USING; static bool help = false; static bool invert_match = false; static bool ignore_case = false; static bool list_files = false; static bool recursive = false; static bool file_match = false; static bool print_dirs = false; static bool all_subimages = false; static bool extended_regex = false; static std::string pattern; static std::vector filenames; static bool grep_file (const std::string &filename, boost::regex &re, bool ignore_nonimage_files=false) { if (! Filesystem::exists (filename)) { std::cerr << "igrep: " << filename << ": No such file or directory\n"; return false; } if (Filesystem::is_directory (filename)) { if (! recursive) return false; if (print_dirs) { std::cout << "(" << filename << "/)\n"; std::cout.flush(); } bool r = false; std::vector directory_entries; Filesystem::get_directory_entries (filename, directory_entries); for (size_t i = 0, e = directory_entries.size(); i < e; ++i) r |= grep_file (directory_entries[i], re, true); return r; } boost::scoped_ptr in (ImageInput::open (filename.c_str())); if (! in.get()) { if (! ignore_nonimage_files) std::cerr << geterror() << "\n"; return false; } ImageSpec spec = in->spec(); if (file_match) { bool match = boost::regex_search (filename, re); if (match && ! invert_match) { std::cout << filename << "\n"; return true; } } bool found = false; int subimage = 0; do { if (!all_subimages && subimage > 0) break; BOOST_FOREACH (const ImageIOParameter &p, spec.extra_attribs) { TypeDesc t = p.type(); if (t.elementtype() == TypeDesc::STRING) { int n = t.numelements(); for (int i = 0; i < n; ++i) { bool match = boost::regex_search (((const char **)p.data())[i], re); found |= match; if (match && ! invert_match) { if (list_files) { std::cout << filename << "\n"; return found; } std::cout << filename << ": " << p.name() << " = " << ((const char **)p.data())[i] << "\n"; } } } } } while (in->seek_subimage (++subimage, 0, spec)); if (invert_match) { found = !found; if (found) std::cout << filename << "\n"; } return found; } static int parse_files (int argc, const char *argv[]) { for (int i = 0; i < argc; i++) { if (pattern.empty()) pattern = argv[i]; else filenames.push_back (argv[i]); } return 0; } int main (int argc, const char *argv[]) { Filesystem::convert_native_arguments (argc, argv); ArgParse ap; ap.options ("igrep -- search images for matching metadata\n" OIIO_INTRO_STRING "\n" "Usage: igrep [options] pattern filename...", "%*", parse_files, "", "-i", &ignore_case, "Ignore upper/lower case distinctions", "-v", &invert_match, "Invert match (select non-matching files)", "-E", &extended_regex, "Pattern is an extended regular expression", "-f", &file_match, "Match against file name as well as metadata", "-l", &list_files, "List the matching files (no detail)", "-r", &recursive, "Recurse into directories", "-d", &print_dirs, "Print directories (when recursive)", "-a", &all_subimages, "Search all subimages of each file", "--help", &help, "Print help message", NULL); if (ap.parse(argc, argv) < 0 || pattern.empty() || filenames.empty()) { std::cerr << ap.geterror() << std::endl; ap.usage (); return EXIT_FAILURE; } if (help) { ap.usage (); exit (EXIT_FAILURE); } boost::regex_constants::syntax_option_type flag = boost::regex_constants::grep; if (extended_regex) flag = boost::regex::extended; if (ignore_case) flag |= boost::regex_constants::icase; boost::regex re (pattern, flag); BOOST_FOREACH (const std::string &s, filenames) { grep_file (s, re); } return 0; } openimageio-1.7.17~dfsg0.orig/src/jpeg2000.imageio/0000755000175000017500000000000013151711064017754 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/jpeg2000.imageio/CMakeLists.txt0000644000175000017500000000143213151711064022514 0ustar mfvmfvif (USE_OPENJPEG AND OPENJPEG_FOUND) if (${OPENJPEG_VERSION} VERSION_LESS 2.0) # Old OpenJpeg 1.5. Remove this eventually. add_oiio_plugin (jpeg2000input-v1.cpp jpeg2000output-v1.cpp INCLUDE_DIRS ${OPENJPEG_INCLUDE_DIR} LINK_LIBRARIES ${OPENJPEG_LIBRARIES} DEFINITIONS "-DUSE_OPENJPEG") else () # OpenJpeg 2.x. Eventually this should be the only one we need. add_oiio_plugin (jpeg2000input.cpp jpeg2000output.cpp INCLUDE_DIRS ${OPENJPEG_INCLUDE_DIR} LINK_LIBRARIES ${OPENJPEG_LIBRARIES} DEFINITIONS "-DUSE_OPENJPEG") endif () else() message (WARNING "Jpeg-2000 plugin will not be built") endif() openimageio-1.7.17~dfsg0.orig/src/jpeg2000.imageio/jpeg2000input.cpp0000644000175000017500000003420313151711064022771 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/imagebuf.h" OIIO_PLUGIN_NAMESPACE_BEGIN namespace { static void openjpeg_error_callback(const char *msg, void *data) { if (ImageInput *input = (ImageInput *)data) { if (! msg || ! msg[0]) msg = "Unknown OpenJpeg error"; input->error ("%s", msg); } } static void openjpeg_dummy_callback(const char *msg, void *data) { } // TODO(sergey): This actually a stright duplication from the png reader, // consider de-duplicating the code somehow? template void associateAlpha (T * data, int size, int channels, int alpha_channel, float gamma) { T max = std::numeric_limits::max(); if (gamma == 1) { for (int x = 0; x < size; ++x, data += channels) for (int c = 0; c < channels; c++) if (c != alpha_channel){ unsigned int f = data[c]; data[c] = (f * data[alpha_channel]) / max; } } else { //With gamma correction float inv_max = 1.0 / max; for (int x = 0; x < size; ++x, data += channels) { float alpha_associate = pow(data[alpha_channel]*inv_max, gamma); // We need to transform to linear space, associate the alpha, and // then transform back. That is, if D = data[c], we want // // D' = max * ( (D/max)^(1/gamma) * (alpha/max) ) ^ gamma // // This happens to simplify to something which looks like // multiplying by a nonlinear alpha: // // D' = D * (alpha/max)^gamma for (int c = 0; c < channels; c++) if (c != alpha_channel) data[c] = static_cast(data[c] * alpha_associate); } } } } // namespace class Jpeg2000Input : public ImageInput { public: Jpeg2000Input () { init (); } virtual ~Jpeg2000Input () { close (); } virtual const char *format_name (void) const { return "jpeg2000"; } virtual int supports (string_view feature) const { return false; // FIXME: we should support Exif/IPTC, but currently don't. } virtual bool open (const std::string &name, ImageSpec &spec); virtual bool open (const std::string &name, ImageSpec &newspec, const ImageSpec &config); virtual bool close (void); virtual bool read_native_scanline (int y, int z, void *data); private: std::string m_filename; std::vector m_bpp; // per channel bpp opj_image_t *m_image; FILE *m_file; opj_codec_t *m_codec; opj_stream_t *m_stream; bool m_keep_unassociated_alpha; // Do not convert unassociated alpha void init (void); bool isJp2File(const int* const p_magicTable) const; opj_codec_t* create_decompressor(); void destroy_decompressor(); void destroy_stream () { if (m_stream) { opj_stream_destroy (m_stream); m_stream = NULL; } } template void read_scanline(int y, int z, void *data); uint16_t baseTypeConvertU10ToU16(int src) { return (uint16_t)((src << 6) | (src >> 4)); } uint16_t baseTypeConvertU12ToU16(int src) { return (uint16_t)((src << 4) | (src >> 8)); } template void yuv_to_rgb(T *p_scanline) { for (int x = 0, i = 0; x < m_spec.width; ++x, i += m_spec.nchannels) { float y = convert_type(p_scanline[i+0]); float u = convert_type(p_scanline[i+1])-0.5f; float v = convert_type(p_scanline[i+2])-0.5f; float r = y + 1.402*v; float g = y - 0.344*u - 0.714*v; float b = y + 1.772*u; p_scanline[i+0] = convert_type(r); p_scanline[i+1] = convert_type(g); p_scanline[i+2] = convert_type(b); } } void setup_event_mgr (opj_codec_t* codec) { opj_set_error_handler (codec, openjpeg_error_callback, this); opj_set_warning_handler (codec, openjpeg_dummy_callback, NULL); opj_set_info_handler (codec, openjpeg_dummy_callback, NULL); } }; // Obligatory material to make this a recognizeable imageio plugin OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT int jpeg2000_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* jpeg2000_imageio_library_version () { return ustring::format("OpenJpeg %s", opj_version()).c_str(); } OIIO_EXPORT ImageInput *jpeg2000_input_imageio_create () { return new Jpeg2000Input; } OIIO_EXPORT const char *jpeg2000_input_extensions[] = { "jp2", "j2k", "j2c", NULL }; OIIO_PLUGIN_EXPORTS_END void Jpeg2000Input::init (void) { m_file = NULL; m_image = NULL; m_codec = NULL; m_stream = NULL; m_keep_unassociated_alpha = false; } bool Jpeg2000Input::open (const std::string &p_name, ImageSpec &p_spec) { m_filename = p_name; if (! Filesystem::exists(m_filename)) { error ("Could not open file \"%s\"", m_filename); return false; } m_codec = create_decompressor(); if (!m_codec) { error ("Could not create Jpeg2000 stream decompressor"); close(); return false; } setup_event_mgr (m_codec); opj_dparameters_t parameters; opj_set_default_decoder_parameters(¶meters); opj_setup_decoder(m_codec, ¶meters); #if defined(OPJ_VERSION_MAJOR) // OpenJpeg >= 2.1 m_stream = opj_stream_create_default_file_stream (m_filename.c_str(), true); #else // OpenJpeg 2.0: need to open a stream ourselves m_file = Filesystem::fopen (m_filename, "rb"); m_stream = opj_stream_create_default_file_stream (m_file, true); #endif if (!m_stream) { error ("Could not open Jpeg2000 stream"); close(); return false; } ASSERT (m_image == NULL); if (! opj_read_header (m_stream, m_codec, &m_image)) { error ("Could not read Jpeg2000 header"); close (); return false; } opj_decode (m_codec, m_stream, m_image); destroy_decompressor (); destroy_stream (); // we support only one, three or four components in image const int channelCount = m_image->numcomps; if (channelCount != 1 && channelCount != 3 && channelCount != 4) { error ("Only images with one, three or four components are supported"); close(); return false; } unsigned int maxPrecision = 0; ROI datawindow; m_bpp.clear (); m_bpp.reserve (channelCount); std::vector chantypes (channelCount, TypeDesc::UINT8); for (int i = 0; i < channelCount; i++) { const opj_image_comp_t &comp (m_image->comps[i]); m_bpp.push_back (comp.prec); maxPrecision = std::max(comp.prec, maxPrecision); ROI roichan (comp.x0, comp.x0+comp.w*comp.dx, comp.y0, comp.y0+comp.h*comp.dy); datawindow = roi_union (datawindow, roichan); // std::cout << " chan " << i << "\n"; // std::cout << " dx=" << comp.dx << " dy=" << comp.dy // << " x0=" << comp.x0 << " y0=" << comp.y0 // << " w=" << comp.w << " h=" << comp.h // << " prec=" << comp.prec << " bpp=" << comp.bpp << "\n"; // std::cout << " sgnd=" << comp.sgnd << " resno_decoded=" << comp.resno_decoded << " factor=" << comp.factor << "\n"; // std::cout << " roichan=" << roichan << "\n"; } // std::cout << "overall x0=" << m_image->x0 << " y0=" << m_image->y0 // << " x1=" << m_image->x1 << " y1=" << m_image->y1 << "\n"; // std::cout << "color_space=" << m_image->color_space << "\n"; const TypeDesc format = (maxPrecision <= 8) ? TypeDesc::UINT8 : TypeDesc::UINT16; m_spec = ImageSpec (datawindow.width(), datawindow.height(), channelCount, format); m_spec.x = datawindow.xbegin; m_spec.y = datawindow.ybegin; m_spec.full_x = m_image->x0; m_spec.full_y = m_image->y0; m_spec.full_width = m_image->x1; m_spec.full_height = m_image->y1; m_spec.attribute ("oiio:BitsPerSample", maxPrecision); m_spec.attribute ("oiio:Orientation", 1); m_spec.attribute ("oiio:ColorSpace", "sRGB"); #ifndef OPENJPEG_VERSION // Sigh... openjpeg.h doesn't seem to have a clear version #define. // OPENJPEG_VERSION only seems to exist in 1.3, which doesn't have // the ICC fields. So assume its absence in the newer one (at least, // 1.5) means the field is valid. if (m_image->icc_profile_len && m_image->icc_profile_buf) m_spec.attribute ("ICCProfile", TypeDesc(TypeDesc::UINT8,m_image->icc_profile_len), m_image->icc_profile_buf); #endif p_spec = m_spec; return true; } bool Jpeg2000Input::open (const std::string &name, ImageSpec &newspec, const ImageSpec &config) { // Check 'config' for any special requests if (config.get_int_attribute("oiio:UnassociatedAlpha", 0) == 1) m_keep_unassociated_alpha = true; return open (name, newspec); } bool Jpeg2000Input::read_native_scanline (int y, int z, void *data) { if (m_spec.format == TypeDesc::UINT8) read_scanline(y, z, data); else read_scanline(y, z, data); // JPEG2000 specifically dictates unassociated (un-"premultiplied") alpha. // Convert to associated unless we were requested not to do so. if (m_spec.alpha_channel != -1 && !m_keep_unassociated_alpha) { float gamma = m_spec.get_float_attribute ("oiio:Gamma", 2.2f); if (m_spec.format == TypeDesc::UINT16) associateAlpha ((unsigned short *)data, m_spec.width, m_spec.nchannels, m_spec.alpha_channel, gamma); else associateAlpha ((unsigned char *)data, m_spec.width, m_spec.nchannels, m_spec.alpha_channel, gamma); } return true; } inline bool Jpeg2000Input::close (void) { if (m_image) { opj_image_destroy(m_image); m_image = NULL; } destroy_decompressor (); destroy_stream (); if (m_file) { fclose (m_file); m_file = NULL; } return true; } bool Jpeg2000Input::isJp2File(const int* const p_magicTable) const { const int32_t JP2_MAGIC = 0x0000000C, JP2_MAGIC2 = 0x0C000000; if (p_magicTable[0] == JP2_MAGIC || p_magicTable[0] == JP2_MAGIC2) { const int32_t JP2_SIG1_MAGIC = 0x6A502020, JP2_SIG1_MAGIC2 = 0x2020506A; const int32_t JP2_SIG2_MAGIC = 0x0D0A870A, JP2_SIG2_MAGIC2 = 0x0A870A0D; if ((p_magicTable[1] == JP2_SIG1_MAGIC || p_magicTable[1] == JP2_SIG1_MAGIC2) && (p_magicTable[2] == JP2_SIG2_MAGIC || p_magicTable[2] == JP2_SIG2_MAGIC2)) { return true; } } return false; } opj_codec_t* Jpeg2000Input::create_decompressor() { int magic[3]; size_t r = Filesystem::read_bytes (m_filename, magic, sizeof(magic)); if (r != 3*sizeof(int)) { error ("Empty file \"%s\"", m_filename); return NULL; } opj_codec_t* codec = NULL; if (isJp2File(magic)) codec = opj_create_decompress (OPJ_CODEC_JP2); else codec = opj_create_decompress (OPJ_CODEC_J2K); return codec; } void Jpeg2000Input::destroy_decompressor() { if (m_codec) { opj_destroy_codec(m_codec); m_codec = NULL; } } template void Jpeg2000Input::read_scanline(int y, int z, void *data) { T* scanline = static_cast(data); int nc = m_spec.nchannels; // It's easier to loop over channels int bits = sizeof(T)*8; for (int c = 0; c < nc; ++c) { const opj_image_comp_t &comp (m_image->comps[c]); int chan_ybegin = comp.y0, chan_yend = comp.y0 + comp.h*comp.dy; int chan_xend = comp.w * comp.dx; int yoff = (y - comp.y0) / comp.dy; for (int x = 0; x < m_spec.width; ++x) { if (yoff < chan_ybegin || yoff >= chan_yend || x > chan_xend) { // Outside the window of this channel scanline[x*nc+c] = T(0); } else { unsigned int val = comp.data[yoff*comp.w + x/comp.dx]; if (comp.sgnd) val += (1<<(bits/2-1)); scanline[x*nc+c] = (T) bit_range_convert (val, comp.prec, bits); } } } if (m_image->color_space == OPJ_CLRSPC_SYCC) yuv_to_rgb(scanline); } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/jpeg2000.imageio/jpeg2000output.cpp0000644000175000017500000004543113151711064023177 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include "openjpeg.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/imageio.h" OIIO_PLUGIN_NAMESPACE_BEGIN static void openjpeg_error_callback(const char *msg, void *data) { if (ImageOutput *input = (ImageOutput *)data) { if (! msg || ! msg[0]) msg = "Unknown OpenJpeg error"; input->error ("%s", msg); } } static void openjpeg_dummy_callback(const char *msg, void *data) { } class Jpeg2000Output : public ImageOutput { public: Jpeg2000Output () { init (); } virtual ~Jpeg2000Output () { close (); } virtual const char *format_name (void) const { return "jpeg2000"; } virtual int supports (string_view feature) const { return (feature == "alpha"); // FIXME: we should support Exif/IPTC, but currently don't. } virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode=Create); virtual bool close (); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); private: std::string m_filename; FILE *m_file; opj_cparameters_t m_compression_parameters; opj_image_t *m_image; opj_codec_t *m_codec; opj_stream_t *m_stream; unsigned int m_dither; bool m_convert_alpha; //< Do we deassociate alpha? std::vector m_tilebuffer; std::vector m_scratch; void init (void) { m_file = NULL; m_image = NULL; m_codec = NULL; m_stream = NULL; m_convert_alpha = true; } opj_image_t* create_jpeg2000_image(); void init_components(opj_image_cmptparm_t *components, int precision); opj_codec_t* create_compressor(); void destroy_compressor () { if (m_codec) { opj_destroy_codec(m_codec); m_codec = NULL; } } void destroy_stream () { if (m_stream) { opj_stream_destroy (m_stream); m_stream = NULL; } } bool save_image(); template void write_scanline(int y, int z, const void *data); void setup_cinema_compression(OPJ_RSIZ_CAPABILITIES p_rsizCap); void setup_compression_params(); OPJ_PROG_ORDER get_progression_order(const std::string& progression_order); }; // Obligatory material to make this a recognizeable imageio plugin OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *jpeg2000_output_imageio_create () { return new Jpeg2000Output; } OIIO_EXPORT const char *jpeg2000_output_extensions[] = { "jp2", "j2k", NULL }; OIIO_PLUGIN_EXPORTS_END bool Jpeg2000Output::open (const std::string &name, const ImageSpec &spec, OpenMode mode) { if (mode != Create) { error ("%s does not support subimages or MIP levels", format_name()); return false; } m_filename = name; m_spec = spec; // Check for things this format doesn't support if (m_spec.width < 1 || m_spec.height < 1) { error ("Image resolution must be at least 1x1, you asked for %d x %d", m_spec.width, m_spec.height); return false; } if (m_spec.depth < 1) m_spec.depth = 1; if (m_spec.depth > 1) { error ("%s does not support volume images (depth > 1)", format_name()); return false; } if (m_spec.nchannels != 1 && m_spec.nchannels != 3 && m_spec.nchannels != 4) { error ("%s does not support %d-channel images\n", format_name(), m_spec.nchannels); return false; } // If not uint8 or uint16, default to uint8 if (m_spec.format != TypeDesc::UINT8 && m_spec.format != TypeDesc::UINT16) m_spec.set_format (TypeDesc::UINT8); m_dither = (m_spec.format == TypeDesc::UINT8) ? m_spec.get_int_attribute ("oiio:dither", 0) : 0; m_convert_alpha = m_spec.alpha_channel != -1 && !m_spec.get_int_attribute("oiio:UnassociatedAlpha", 0); m_file = Filesystem::fopen (m_filename, "wb"); if (m_file == NULL) { error ("Unable to open file \"%s\"", m_filename.c_str()); return false; } // If user asked for tiles -- which this format doesn't support, emulate // it by buffering the whole image. if (m_spec.tile_width && m_spec.tile_height) m_tilebuffer.resize (m_spec.image_bytes()); m_image = create_jpeg2000_image(); return true; } template static void deassociateAlpha (T * data, int size, int channels, int alpha_channel, float gamma) { unsigned int max = std::numeric_limits::max(); if (gamma == 1) { for (int x = 0; x < size; ++x, data += channels) if (data[alpha_channel]) for (int c = 0; c < channels; c++) if (c != alpha_channel) { unsigned int f = data[c]; f = (f * max) / data[alpha_channel]; data[c] = (T) std::min (max, f); } } else { for (int x = 0; x < size; ++x, data += channels) if (data[alpha_channel]) { // See associateAlpha() for an explanation. float alpha_deassociate = pow((float)max / data[alpha_channel], gamma); for (int c = 0; c < channels; c++) if (c != alpha_channel) data[c] = static_cast (std::min (max, (unsigned int)(data[c] * alpha_deassociate))); } } } bool Jpeg2000Output::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { y -= m_spec.y; if (y > m_spec.height) { error ("Attempt to write too many scanlines to %s", m_filename); return false; } m_spec.auto_stride (xstride, format, spec().nchannels); const void *origdata = data; data = to_native_scanline (format, data, xstride, m_scratch, m_dither, y, z); if (data == origdata) { m_scratch.assign ((unsigned char *)data, (unsigned char *)data+m_spec.scanline_bytes()); data = &m_scratch[0]; } // JPEG-2000 specifically dictates unassociated (un-"premultiplied") alpha if (m_convert_alpha) { if (m_spec.format == TypeDesc::UINT16) deassociateAlpha ((unsigned short *)data, m_spec.width, m_spec.nchannels, m_spec.alpha_channel, 2.2f); else deassociateAlpha ((unsigned char *)data, m_spec.width, m_spec.nchannels, m_spec.alpha_channel, 2.2f); } if (m_spec.format == TypeDesc::UINT8) write_scanline(y, z, data); else write_scanline(y, z, data); if (y == m_spec.height - 1) save_image(); return true; } bool Jpeg2000Output::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { // Emulate tiles by buffering the whole image return copy_tile_to_image_buffer (x, y, z, format, data, xstride, ystride, zstride, &m_tilebuffer[0]); } bool Jpeg2000Output::close () { if (! m_stream) { // Already closed return true; } bool ok = true; if (m_spec.tile_width) { // We've been emulating tiles; now dump as scanlines. ASSERT (m_tilebuffer.size()); ok &= write_scanlines (m_spec.y, m_spec.y+m_spec.height, 0, m_spec.format, &m_tilebuffer[0]); std::vector().swap (m_tilebuffer); } if (m_image) { opj_image_destroy(m_image); m_image = NULL; } destroy_compressor (); destroy_stream (); if (m_file) { fclose (m_file); m_file = NULL; } return ok; } bool Jpeg2000Output::save_image() { m_codec = create_compressor(); if (!m_codec) return false; opj_set_error_handler (m_codec, openjpeg_error_callback, this); opj_set_warning_handler (m_codec, openjpeg_dummy_callback, NULL); opj_set_info_handler (m_codec, openjpeg_dummy_callback, NULL); opj_setup_encoder(m_codec, &m_compression_parameters, m_image); #if defined(OPJ_VERSION_MAJOR) // OpenJpeg >= 2.1 m_stream = opj_stream_create_default_file_stream (m_filename.c_str(), false); #else // OpenJpeg 2.0: need to open a stream ourselves m_file = Filesystem::fopen (m_filename, "wb"); m_stream = opj_stream_create_default_file_stream (m_file, false); #endif if (! m_stream) { error ("Failed write jpeg2000::save_image"); return false; } if (! opj_start_compress (m_codec, m_image, m_stream) || ! opj_encode (m_codec, m_stream) || ! opj_end_compress (m_codec, m_stream)) { error ("Failed write jpeg2000::save_image"); return false; } return true; } opj_image_t* Jpeg2000Output::create_jpeg2000_image() { setup_compression_params(); OPJ_COLOR_SPACE color_space = OPJ_CLRSPC_SRGB; if (m_spec.nchannels == 1) color_space = OPJ_CLRSPC_GRAY; int precision = 16; const ImageIOParameter *prec = m_spec.find_attribute ("oiio:BitsPerSample", TypeDesc::INT); if (prec) precision = *(int*)prec->data(); else if (m_spec.format == TypeDesc::UINT8 || m_spec.format == TypeDesc::INT8) precision = 8; const int MAX_COMPONENTS = 4; opj_image_cmptparm_t component_params[MAX_COMPONENTS]; init_components(component_params, precision); m_image = opj_image_create(m_spec.nchannels, &component_params[0], color_space); m_image->x0 = m_compression_parameters.image_offset_x0; m_image->y0 = m_compression_parameters.image_offset_y0; m_image->x1 = m_compression_parameters.image_offset_x0 + (m_spec.width - 1) * m_compression_parameters.subsampling_dx + 1; m_image->y1 = m_compression_parameters.image_offset_y0 + (m_spec.height - 1) * m_compression_parameters.subsampling_dy + 1; #if 0 // FIXME: I seem to get crashes with OpenJpeg 2.x in the presence of ICC // profiles. I have no idea why. It seems like losing the ability to // write ICC profiles is the lesser evil compared to either restricting // ourselves to OpenJpeg 1.5 or living with crashes. I'm at the limit of // my knowledge of OpenJPEG, which frankly has a poor API and abysmal // documentation. So I'll leave the repair of this for later. If // someboody comes along that desperately needs JPEG2000 and ICC // profiles, maybe they will be motivated enough to track down the // problem. const ImageIOParameter *icc = m_spec.find_attribute ("ICCProfile"); if (icc && icc->type().basetype == TypeDesc::UINT8 && icc->type().arraylen > 0) { m_image->icc_profile_len = icc->type().arraylen; m_image->icc_profile_buf = (unsigned char *) icc->data(); } #endif return m_image; } inline void Jpeg2000Output::init_components(opj_image_cmptparm_t *components, int precision) { memset(components, 0x00, m_spec.nchannels * sizeof(opj_image_cmptparm_t)); for(int i = 0; i < m_spec.nchannels; i++) { components[i].dx = m_compression_parameters.subsampling_dx; components[i].dy = m_compression_parameters.subsampling_dy; components[i].w = m_spec.width; components[i].h = m_spec.height; components[i].prec = precision; components[i].bpp = precision; components[i].sgnd = 0; } } opj_codec_t* Jpeg2000Output::create_compressor() { std::string ext = Filesystem::extension(m_filename); opj_codec_t *compressor = NULL; if (ext == ".j2k") compressor = opj_create_compress (OPJ_CODEC_J2K); else if (ext == ".jp2") compressor = opj_create_compress (OPJ_CODEC_JP2); return compressor; } template void Jpeg2000Output::write_scanline(int y, int z, const void *data) { int bits = sizeof(T)*8; const T* scanline = static_cast(data); const size_t scanline_pos = (y - m_spec.y) * m_spec.width; for (int i = 0, j = 0; i < m_spec.width; i++) { for (int c = 0; c < m_spec.nchannels; ++c) { unsigned int val = scanline[j++]; if (bits != m_image->comps[c].prec) val = bit_range_convert (val, bits, m_image->comps[c].prec); m_image->comps[c].data[scanline_pos + i] = val; } } } void Jpeg2000Output::setup_cinema_compression(OPJ_RSIZ_CAPABILITIES p_rsizCap) { m_compression_parameters.tile_size_on = false; m_compression_parameters.cp_tdx=1; m_compression_parameters.cp_tdy=1; m_compression_parameters.tp_flag = 'C'; m_compression_parameters.tp_on = 1; m_compression_parameters.cp_tx0 = 0; m_compression_parameters.cp_ty0 = 0; m_compression_parameters.image_offset_x0 = 0; m_compression_parameters.image_offset_y0 = 0; m_compression_parameters.cblockw_init = 32; m_compression_parameters.cblockh_init = 32; m_compression_parameters.csty |= 0x01; m_compression_parameters.prog_order = OPJ_CPRL; m_compression_parameters.roi_compno = -1; m_compression_parameters.subsampling_dx = 1; m_compression_parameters.subsampling_dy = 1; m_compression_parameters.irreversible = 1; m_compression_parameters.cp_rsiz = p_rsizCap; if (p_rsizCap == OPJ_CINEMA4K) { m_compression_parameters.cp_cinema = OPJ_CINEMA4K_24; m_compression_parameters.POC[0].tile = 1; m_compression_parameters.POC[0].resno0 = 0; m_compression_parameters.POC[0].compno0 = 0; m_compression_parameters.POC[0].layno1 = 1; m_compression_parameters.POC[0].resno1 = m_compression_parameters.numresolution-1; m_compression_parameters.POC[0].compno1 = 3; m_compression_parameters.POC[0].prg1 = OPJ_CPRL; m_compression_parameters.POC[1].tile = 1; m_compression_parameters.POC[1].resno0 = m_compression_parameters.numresolution-1; m_compression_parameters.POC[1].compno0 = 0; m_compression_parameters.POC[1].layno1 = 1; m_compression_parameters.POC[1].resno1 = m_compression_parameters.numresolution; m_compression_parameters.POC[1].compno1 = 3; m_compression_parameters.POC[1].prg1 = OPJ_CPRL; } else if (p_rsizCap == OPJ_CINEMA2K) { m_compression_parameters.cp_cinema = OPJ_CINEMA2K_24; } } void Jpeg2000Output::setup_compression_params() { opj_set_default_encoder_parameters(&m_compression_parameters); m_compression_parameters.tcp_rates[0] = 0; m_compression_parameters.tcp_numlayers++; m_compression_parameters.cp_disto_alloc = 1; const ImageIOParameter *is_cinema2k = m_spec.find_attribute ("jpeg2000:Cinema2K", TypeDesc::UINT); if (is_cinema2k) setup_cinema_compression(OPJ_CINEMA2K); const ImageIOParameter *is_cinema4k = m_spec.find_attribute ("jpeg2000:Cinema4K", TypeDesc::UINT); if (is_cinema4k) setup_cinema_compression(OPJ_CINEMA4K); const ImageIOParameter *initial_cb_width = m_spec.find_attribute ("jpeg2000:InitialCodeBlockWidth", TypeDesc::UINT); if (initial_cb_width && initial_cb_width->data()) m_compression_parameters.cblockw_init = *(unsigned int*)initial_cb_width->data(); const ImageIOParameter *initial_cb_height = m_spec.find_attribute ("jpeg2000:InitialCodeBlockHeight", TypeDesc::UINT); if (initial_cb_height && initial_cb_height->data()) m_compression_parameters.cblockh_init = *(unsigned int*)initial_cb_height->data(); const ImageIOParameter *progression_order = m_spec.find_attribute ("jpeg2000:ProgressionOrder", TypeDesc::STRING); if (progression_order && progression_order->data()) { std::string prog_order((const char*)progression_order->data()); m_compression_parameters.prog_order = get_progression_order(prog_order); } const ImageIOParameter *compression_mode = m_spec.find_attribute ("jpeg2000:CompressionMode", TypeDesc::INT); if (compression_mode && compression_mode->data()) m_compression_parameters.mode = *(int*)compression_mode->data(); } OPJ_PROG_ORDER Jpeg2000Output::get_progression_order(const std::string &progression_order) { if (progression_order == "LRCP") return OPJ_LRCP; else if (progression_order == "RLCP") return OPJ_RLCP; else if (progression_order == "RPCL") return OPJ_RPCL; else if (progression_order == "PCRL") return OPJ_PCRL; else if (progression_order == "PCRL") return OPJ_CPRL; return OPJ_PROG_UNKNOWN; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/jpeg2000.imageio/jpeg2000input-v1.cpp0000644000175000017500000003364613151711064023327 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/imagebuf.h" OIIO_PLUGIN_NAMESPACE_BEGIN namespace { void openjpeg_dummy_callback(const char*, void*) {} // TODO(sergey): This actually a stright duplication from the png reader, // consider de-duplicating the code somehow? template void associateAlpha (T * data, int size, int channels, int alpha_channel, float gamma) { T max = std::numeric_limits::max(); if (gamma == 1) { for (int x = 0; x < size; ++x, data += channels) for (int c = 0; c < channels; c++) if (c != alpha_channel){ unsigned int f = data[c]; data[c] = (f * data[alpha_channel]) / max; } } else { //With gamma correction float inv_max = 1.0 / max; for (int x = 0; x < size; ++x, data += channels) { float alpha_associate = pow(data[alpha_channel]*inv_max, gamma); // We need to transform to linear space, associate the alpha, and // then transform back. That is, if D = data[c], we want // // D' = max * ( (D/max)^(1/gamma) * (alpha/max) ) ^ gamma // // This happens to simplify to something which looks like // multiplying by a nonlinear alpha: // // D' = D * (alpha/max)^gamma for (int c = 0; c < channels; c++) if (c != alpha_channel) data[c] = static_cast(data[c] * alpha_associate); } } } } // namespace class Jpeg2000Input : public ImageInput { public: Jpeg2000Input () { init (); } virtual ~Jpeg2000Input () { close (); } virtual const char *format_name (void) const { return "jpeg2000"; } virtual int supports (string_view feature) const { return false; // FIXME: we should support Exif/IPTC, but currently don't. } virtual bool open (const std::string &name, ImageSpec &spec); virtual bool open (const std::string &name, ImageSpec &newspec, const ImageSpec &config); virtual bool close (void); virtual bool read_native_scanline (int y, int z, void *data); private: std::string m_filename; std::vector m_bpp; // per channel bpp opj_image_t *m_image; FILE *m_file; bool m_keep_unassociated_alpha; // Do not convert unassociated alpha void init (void); bool isJp2File(const int* const p_magicTable) const; opj_dinfo_t* create_decompressor(); template void read_scanline(int y, int z, void *data); uint16_t baseTypeConvertU10ToU16(int src) { return (uint16_t)((src << 6) | (src >> 4)); } uint16_t baseTypeConvertU12ToU16(int src) { return (uint16_t)((src << 4) | (src >> 8)); } size_t get_file_length(FILE *p_file) { fseek(p_file, 0, SEEK_END); const size_t fileLength = ftell(p_file); rewind(m_file); return fileLength; } template void yuv_to_rgb(T *p_scanline) { for (int x = 0, i = 0; x < m_spec.width; ++x, i += m_spec.nchannels) { float y = convert_type(p_scanline[i+0]); float u = convert_type(p_scanline[i+1])-0.5f; float v = convert_type(p_scanline[i+2])-0.5f; float r = y + 1.402*v; float g = y - 0.344*u - 0.714*v; float b = y + 1.772*u; p_scanline[i+0] = convert_type(r); p_scanline[i+1] = convert_type(g); p_scanline[i+2] = convert_type(b); } } void setup_event_mgr(opj_event_mgr_t& event_mgr, opj_dinfo_t* p_decompressor) { event_mgr.error_handler = openjpeg_dummy_callback; event_mgr.warning_handler = openjpeg_dummy_callback; event_mgr.info_handler = openjpeg_dummy_callback; opj_set_event_mgr((opj_common_ptr) p_decompressor, &event_mgr, NULL); } bool fread (void *p_buf, size_t p_itemSize, size_t p_nitems) { size_t n = ::fread (p_buf, p_itemSize, p_nitems, m_file); if (n != p_nitems) error ("Read error"); return n == p_nitems; } }; // Obligatory material to make this a recognizeable imageio plugin OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT int jpeg2000_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* jpeg2000_imageio_library_version () { return ustring::format("OpenJpeg %s", opj_version()).c_str(); } OIIO_EXPORT ImageInput *jpeg2000_input_imageio_create () { return new Jpeg2000Input; } OIIO_EXPORT const char *jpeg2000_input_extensions[] = { "jp2", "j2k", "j2c", NULL }; OIIO_PLUGIN_EXPORTS_END void Jpeg2000Input::init (void) { m_file = NULL; m_image = NULL; m_keep_unassociated_alpha = false; } bool Jpeg2000Input::open (const std::string &p_name, ImageSpec &p_spec) { m_filename = p_name; m_file = Filesystem::fopen(m_filename, "rb"); if (!m_file) { error ("Could not open file \"%s\"", m_filename.c_str()); return false; } opj_dinfo_t* decompressor = create_decompressor(); if (!decompressor) { error ("Could not create Jpeg2000 stream decompressor"); close(); return false; } opj_event_mgr_t event_mgr; setup_event_mgr(event_mgr, decompressor); opj_dparameters_t parameters; opj_set_default_decoder_parameters(¶meters); opj_setup_decoder(decompressor, ¶meters); const size_t fileLength = get_file_length(m_file); std::vector fileContent(fileLength+1, 0); fread(&fileContent[0], sizeof(uint8_t), fileLength); opj_cio_t *cio = opj_cio_open((opj_common_ptr)decompressor, &fileContent[0], (int) fileLength); if (!cio) { error ("Could not open Jpeg2000 stream"); opj_destroy_decompress(decompressor); close(); return false; } m_image = opj_decode(decompressor, cio); opj_cio_close(cio); opj_destroy_decompress(decompressor); if (!m_image) { error ("Could not decode Jpeg2000 stream"); close(); return false; } // we support only one, three or four components in image const int channelCount = m_image->numcomps; if (channelCount != 1 && channelCount != 3 && channelCount != 4) { error ("Only images with one, three or four components are supported"); close(); return false; } int maxPrecision = 0; ROI datawindow; m_bpp.clear (); m_bpp.reserve (channelCount); std::vector chantypes (channelCount, TypeDesc::UINT8); for (int i = 0; i < channelCount; i++) { const opj_image_comp_t &comp (m_image->comps[i]); m_bpp.push_back (comp.prec); maxPrecision = std::max(comp.prec, maxPrecision); ROI roichan (comp.x0, comp.x0+comp.w*comp.dx, comp.y0, comp.y0+comp.h*comp.dy); datawindow = roi_union (datawindow, roichan); // std::cout << " chan " << i << "\n"; // std::cout << " dx=" << comp.dx << " dy=" << comp.dy // << " x0=" << comp.x0 << " y0=" << comp.y0 // << " w=" << comp.w << " h=" << comp.h // << " prec=" << comp.prec << " bpp=" << comp.bpp << "\n"; // std::cout << " sgnd=" << comp.sgnd << " resno_decoded=" << comp.resno_decoded << " factor=" << comp.factor << "\n"; // std::cout << " roichan=" << roichan << "\n"; } // std::cout << "overall x0=" << m_image->x0 << " y0=" << m_image->y0 // << " x1=" << m_image->x1 << " y1=" << m_image->y1 << "\n"; // std::cout << "color_space=" << m_image->color_space << "\n"; const TypeDesc format = (maxPrecision <= 8) ? TypeDesc::UINT8 : TypeDesc::UINT16; m_spec = ImageSpec (datawindow.width(), datawindow.height(), channelCount, format); m_spec.x = datawindow.xbegin; m_spec.y = datawindow.ybegin; m_spec.full_x = m_image->x0; m_spec.full_y = m_image->y0; m_spec.full_width = m_image->x1; m_spec.full_height = m_image->y1; m_spec.attribute ("oiio:BitsPerSample", maxPrecision); m_spec.attribute ("oiio:Orientation", 1); m_spec.attribute ("oiio:ColorSpace", "sRGB"); #ifndef OPENJPEG_VERSION // Sigh... openjpeg.h doesn't seem to have a clear version #define. // OPENJPEG_VERSION only seems to exist in 1.3, which doesn't have // the ICC fields. So assume its absence in the newer one (at least, // 1.5) means the field is valid. if (m_image->icc_profile_len && m_image->icc_profile_buf) m_spec.attribute ("ICCProfile", TypeDesc(TypeDesc::UINT8,m_image->icc_profile_len), m_image->icc_profile_buf); #endif p_spec = m_spec; return true; } bool Jpeg2000Input::open (const std::string &name, ImageSpec &newspec, const ImageSpec &config) { // Check 'config' for any special requests if (config.get_int_attribute("oiio:UnassociatedAlpha", 0) == 1) m_keep_unassociated_alpha = true; return open (name, newspec); } bool Jpeg2000Input::read_native_scanline (int y, int z, void *data) { if (m_spec.format == TypeDesc::UINT8) read_scanline(y, z, data); else read_scanline(y, z, data); // JPEG2000 specifically dictates unassociated (un-"premultiplied") alpha. // Convert to associated unless we were requested not to do so. if (m_spec.alpha_channel != -1 && !m_keep_unassociated_alpha) { float gamma = m_spec.get_float_attribute ("oiio:Gamma", 2.2f); if (m_spec.format == TypeDesc::UINT16) associateAlpha ((unsigned short *)data, m_spec.width, m_spec.nchannels, m_spec.alpha_channel, gamma); else associateAlpha ((unsigned char *)data, m_spec.width, m_spec.nchannels, m_spec.alpha_channel, gamma); } return true; } inline bool Jpeg2000Input::close (void) { if (m_file) { fclose(m_file); m_file = NULL; } if (m_image) { opj_image_destroy(m_image); m_image = NULL; } return true; } bool Jpeg2000Input::isJp2File(const int* const p_magicTable) const { const int32_t JP2_MAGIC = 0x0000000C, JP2_MAGIC2 = 0x0C000000; if (p_magicTable[0] == JP2_MAGIC || p_magicTable[0] == JP2_MAGIC2) { const int32_t JP2_SIG1_MAGIC = 0x6A502020, JP2_SIG1_MAGIC2 = 0x2020506A; const int32_t JP2_SIG2_MAGIC = 0x0D0A870A, JP2_SIG2_MAGIC2 = 0x0A870A0D; if ((p_magicTable[1] == JP2_SIG1_MAGIC || p_magicTable[1] == JP2_SIG1_MAGIC2) && (p_magicTable[2] == JP2_SIG2_MAGIC || p_magicTable[2] == JP2_SIG2_MAGIC2)) { return true; } } return false; } opj_dinfo_t* Jpeg2000Input::create_decompressor() { int magic[3]; if (::fread (&magic, 4, 3, m_file) != 3) { error ("Empty file \"%s\"", m_filename.c_str()); return NULL; } opj_dinfo_t* dinfo = NULL; if (isJp2File(magic)) dinfo = opj_create_decompress(CODEC_JP2); else dinfo = opj_create_decompress(CODEC_J2K); rewind(m_file); return dinfo; } template void Jpeg2000Input::read_scanline(int y, int z, void *data) { T* scanline = static_cast(data); int nc = m_spec.nchannels; // It's easier to loop over channels int bits = sizeof(T)*8; for (int c = 0; c < nc; ++c) { const opj_image_comp_t &comp (m_image->comps[c]); int chan_ybegin = comp.y0, chan_yend = comp.y0 + comp.h*comp.dy; int chan_xend = comp.w * comp.dx; int yoff = (y - comp.y0) / comp.dy; for (int x = 0; x < m_spec.width; ++x) { if (yoff < chan_ybegin || yoff >= chan_yend || x > chan_xend) { // Outside the window of this channel scanline[x*nc+c] = T(0); } else { unsigned int val = comp.data[yoff*comp.w + x/comp.dx]; if (comp.sgnd) val += (1<<(bits/2-1)); scanline[x*nc+c] = (T) bit_range_convert (val, comp.prec, bits); } } } if (m_image->color_space == CLRSPC_SYCC) yuv_to_rgb(scanline); } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/jpeg2000.imageio/jpeg2000output-v1.cpp0000644000175000017500000004276413151711064023531 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include "openjpeg.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/imageio.h" OIIO_PLUGIN_NAMESPACE_BEGIN static void openjpeg_dummy_callback(const char*, void*) {} class Jpeg2000Output : public ImageOutput { public: Jpeg2000Output () { init (); } virtual ~Jpeg2000Output () { close (); } virtual const char *format_name (void) const { return "jpeg2000"; } virtual int supports (string_view feature) const { return (feature == "alpha"); // FIXME: we should support Exif/IPTC, but currently don't. } virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode=Create); virtual bool close (); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); private: std::string m_filename; FILE *m_file; opj_cparameters_t m_compression_parameters; opj_image_t *m_image; unsigned int m_dither; bool m_convert_alpha; //< Do we deassociate alpha? std::vector m_tilebuffer; std::vector m_scratch; void init (void) { m_file = NULL; m_image = NULL; m_convert_alpha = true; } opj_image_t* create_jpeg2000_image(); void init_components(opj_image_cmptparm_t *components, int precision); opj_cinfo_t* create_compressor(); bool save_image(); template void write_scanline(int y, int z, const void *data); void setup_cinema_compression(OPJ_RSIZ_CAPABILITIES p_rsizCap); void setup_compression_params(); OPJ_PROG_ORDER get_progression_order(const std::string& progression_order); }; // Obligatory material to make this a recognizeable imageio plugin OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *jpeg2000_output_imageio_create () { return new Jpeg2000Output; } OIIO_EXPORT const char *jpeg2000_output_extensions[] = { "jp2", "j2k", NULL }; OIIO_PLUGIN_EXPORTS_END bool Jpeg2000Output::open (const std::string &name, const ImageSpec &spec, OpenMode mode) { if (mode != Create) { error ("%s does not support subimages or MIP levels", format_name()); return false; } m_filename = name; m_spec = spec; // Check for things this format doesn't support if (m_spec.width < 1 || m_spec.height < 1) { error ("Image resolution must be at least 1x1, you asked for %d x %d", m_spec.width, m_spec.height); return false; } if (m_spec.depth < 1) m_spec.depth = 1; if (m_spec.depth > 1) { error ("%s does not support volume images (depth > 1)", format_name()); return false; } if (m_spec.nchannels != 1 && m_spec.nchannels != 3 && m_spec.nchannels != 4) { error ("%s does not support %d-channel images\n", format_name(), m_spec.nchannels); return false; } // If not uint8 or uint16, default to uint8 if (m_spec.format != TypeDesc::UINT8 && m_spec.format != TypeDesc::UINT16) m_spec.set_format (TypeDesc::UINT8); m_dither = (m_spec.format == TypeDesc::UINT8) ? m_spec.get_int_attribute ("oiio:dither", 0) : 0; m_convert_alpha = m_spec.alpha_channel != -1 && !m_spec.get_int_attribute("oiio:UnassociatedAlpha", 0); m_file = Filesystem::fopen (m_filename, "wb"); if (m_file == NULL) { error ("Unable to open file \"%s\"", m_filename.c_str()); return false; } // If user asked for tiles -- which this format doesn't support, emulate // it by buffering the whole image. if (m_spec.tile_width && m_spec.tile_height) m_tilebuffer.resize (m_spec.image_bytes()); m_image = create_jpeg2000_image(); return true; } template static void deassociateAlpha (T * data, int size, int channels, int alpha_channel, float gamma) { unsigned int max = std::numeric_limits::max(); if (gamma == 1) { for (int x = 0; x < size; ++x, data += channels) if (data[alpha_channel]) for (int c = 0; c < channels; c++) if (c != alpha_channel) { unsigned int f = data[c]; f = (f * max) / data[alpha_channel]; data[c] = (T) std::min (max, f); } } else { for (int x = 0; x < size; ++x, data += channels) if (data[alpha_channel]) { // See associateAlpha() for an explanation. float alpha_deassociate = pow((float)max / data[alpha_channel], gamma); for (int c = 0; c < channels; c++) if (c != alpha_channel) data[c] = static_cast (std::min (max, (unsigned int)(data[c] * alpha_deassociate))); } } } bool Jpeg2000Output::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { y -= m_spec.y; if (y > m_spec.height) { error ("Attempt to write too many scanlines to %s", m_filename); return false; } m_spec.auto_stride (xstride, format, spec().nchannels); const void *origdata = data; data = to_native_scanline (format, data, xstride, m_scratch, m_dither, y, z); if (data == origdata) { m_scratch.assign ((unsigned char *)data, (unsigned char *)data+m_spec.scanline_bytes()); data = &m_scratch[0]; } // JPEG-2000 specifically dictates unassociated (un-"premultiplied") alpha if (m_convert_alpha) { if (m_spec.format == TypeDesc::UINT16) deassociateAlpha ((unsigned short *)data, m_spec.width, m_spec.nchannels, m_spec.alpha_channel, 2.2f); else deassociateAlpha ((unsigned char *)data, m_spec.width, m_spec.nchannels, m_spec.alpha_channel, 2.2f); } if (m_spec.format == TypeDesc::UINT8) write_scanline(y, z, data); else write_scanline(y, z, data); if (y == m_spec.height - 1) save_image(); return true; } bool Jpeg2000Output::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { // Emulate tiles by buffering the whole image return copy_tile_to_image_buffer (x, y, z, format, data, xstride, ystride, zstride, &m_tilebuffer[0]); } bool Jpeg2000Output::close () { if (! m_file) { // Already closed return true; init(); } bool ok = true; if (m_spec.tile_width) { // We've been emulating tiles; now dump as scanlines. ASSERT (m_tilebuffer.size()); ok &= write_scanlines (m_spec.y, m_spec.y+m_spec.height, 0, m_spec.format, &m_tilebuffer[0]); std::vector().swap (m_tilebuffer); } fclose(m_file); m_file = NULL; if (m_image) { opj_image_destroy(m_image); m_image = NULL; } return ok; } bool Jpeg2000Output::save_image() { opj_cinfo_t* compressor = create_compressor(); if (!compressor) return false; opj_event_mgr_t event_mgr; event_mgr.error_handler = openjpeg_dummy_callback; event_mgr.warning_handler = openjpeg_dummy_callback; event_mgr.info_handler = openjpeg_dummy_callback; opj_set_event_mgr((opj_common_ptr)compressor, &event_mgr, NULL); opj_setup_encoder(compressor, &m_compression_parameters, m_image); opj_cio_t *cio = opj_cio_open((opj_common_ptr)compressor, NULL, 0); opj_encode(compressor, cio, m_image, NULL); size_t wb = fwrite(cio->buffer, 1, cio_tell(cio), m_file); if (wb != (size_t)cio_tell(cio)) { error ("Failed write jpeg2000::save_image (err: %d)", wb); return false; } opj_destroy_compress(compressor); opj_cio_close(cio); return true; } opj_image_t* Jpeg2000Output::create_jpeg2000_image() { setup_compression_params(); OPJ_COLOR_SPACE color_space = CLRSPC_SRGB; if (m_spec.nchannels == 1) color_space = CLRSPC_GRAY; int precision = 16; const ImageIOParameter *prec = m_spec.find_attribute ("oiio:BitsPerSample", TypeDesc::INT); if (prec) precision = *(int*)prec->data(); else if (m_spec.format == TypeDesc::UINT8 || m_spec.format == TypeDesc::INT8) precision = 8; const int MAX_COMPONENTS = 4; opj_image_cmptparm_t component_params[MAX_COMPONENTS]; init_components(component_params, precision); m_image = opj_image_create(m_spec.nchannels, &component_params[0], color_space); m_image->x0 = m_compression_parameters.image_offset_x0; m_image->y0 = m_compression_parameters.image_offset_y0; m_image->x1 = m_compression_parameters.image_offset_x0 + (m_spec.width - 1) * m_compression_parameters.subsampling_dx + 1; m_image->y1 = m_compression_parameters.image_offset_y0 + (m_spec.height - 1) * m_compression_parameters.subsampling_dy + 1; #ifndef OPENJPEG_VERSION // Sigh... openjpeg.h doesn't seem to have a clear version #define. // OPENJPEG_VERSION only seems to exist in 1.3, which doesn't have // the ICC fields. So assume its absence in the newer one (at least, // 1.5) means the field is valid. const ImageIOParameter *icc = m_spec.find_attribute ("ICCProfile"); if (icc && icc->type().basetype == TypeDesc::UINT8 && icc->type().arraylen > 0) { m_image->icc_profile_len = icc->type().arraylen; m_image->icc_profile_buf = (unsigned char *) icc->data(); } #endif return m_image; } inline void Jpeg2000Output::init_components(opj_image_cmptparm_t *components, int precision) { memset(components, 0x00, m_spec.nchannels * sizeof(opj_image_cmptparm_t)); for(int i = 0; i < m_spec.nchannels; i++) { components[i].dx = m_compression_parameters.subsampling_dx; components[i].dy = m_compression_parameters.subsampling_dy; components[i].w = m_spec.width; components[i].h = m_spec.height; components[i].prec = precision; components[i].bpp = precision; components[i].sgnd = 0; } } opj_cinfo_t* Jpeg2000Output::create_compressor() { std::string ext = Filesystem::extension(m_filename); opj_cinfo_t *compressor = NULL; if (ext == ".j2k") compressor = opj_create_compress(CODEC_J2K); else if (ext == ".jp2") compressor = opj_create_compress(CODEC_JP2); return compressor; } template void Jpeg2000Output::write_scanline(int y, int z, const void *data) { int bits = sizeof(T)*8; const T* scanline = static_cast(data); const size_t scanline_pos = (y - m_spec.y) * m_spec.width; for (int i = 0, j = 0; i < m_spec.width; i++) { for (int c = 0; c < m_spec.nchannels; ++c) { unsigned int val = scanline[j++]; if (bits != m_image->comps[c].prec) val = bit_range_convert (val, bits, m_image->comps[c].prec); m_image->comps[c].data[scanline_pos + i] = val; } } } void Jpeg2000Output::setup_cinema_compression(OPJ_RSIZ_CAPABILITIES p_rsizCap) { m_compression_parameters.tile_size_on = false; m_compression_parameters.cp_tdx=1; m_compression_parameters.cp_tdy=1; m_compression_parameters.tp_flag = 'C'; m_compression_parameters.tp_on = 1; m_compression_parameters.cp_tx0 = 0; m_compression_parameters.cp_ty0 = 0; m_compression_parameters.image_offset_x0 = 0; m_compression_parameters.image_offset_y0 = 0; m_compression_parameters.cblockw_init = 32; m_compression_parameters.cblockh_init = 32; m_compression_parameters.csty |= 0x01; m_compression_parameters.prog_order = CPRL; m_compression_parameters.roi_compno = -1; m_compression_parameters.subsampling_dx = 1; m_compression_parameters.subsampling_dy = 1; m_compression_parameters.irreversible = 1; m_compression_parameters.cp_rsiz = p_rsizCap; if (p_rsizCap == CINEMA4K) { m_compression_parameters.cp_cinema = CINEMA4K_24; m_compression_parameters.POC[0].tile = 1; m_compression_parameters.POC[0].resno0 = 0; m_compression_parameters.POC[0].compno0 = 0; m_compression_parameters.POC[0].layno1 = 1; m_compression_parameters.POC[0].resno1 = m_compression_parameters.numresolution-1; m_compression_parameters.POC[0].compno1 = 3; m_compression_parameters.POC[0].prg1 = CPRL; m_compression_parameters.POC[1].tile = 1; m_compression_parameters.POC[1].resno0 = m_compression_parameters.numresolution-1; m_compression_parameters.POC[1].compno0 = 0; m_compression_parameters.POC[1].layno1 = 1; m_compression_parameters.POC[1].resno1 = m_compression_parameters.numresolution; m_compression_parameters.POC[1].compno1 = 3; m_compression_parameters.POC[1].prg1 = CPRL; } else if (p_rsizCap == CINEMA2K) { m_compression_parameters.cp_cinema = CINEMA2K_24; } } void Jpeg2000Output::setup_compression_params() { opj_set_default_encoder_parameters(&m_compression_parameters); m_compression_parameters.tcp_rates[0] = 0; m_compression_parameters.tcp_numlayers++; m_compression_parameters.cp_disto_alloc = 1; const ImageIOParameter *is_cinema2k = m_spec.find_attribute ("jpeg2000:Cinema2K", TypeDesc::UINT); if (is_cinema2k) setup_cinema_compression(CINEMA2K); const ImageIOParameter *is_cinema4k = m_spec.find_attribute ("jpeg2000:Cinema4K", TypeDesc::UINT); if (is_cinema4k) setup_cinema_compression(CINEMA4K); const ImageIOParameter *initial_cb_width = m_spec.find_attribute ("jpeg2000:InitialCodeBlockWidth", TypeDesc::UINT); if (initial_cb_width && initial_cb_width->data()) m_compression_parameters.cblockw_init = *(unsigned int*)initial_cb_width->data(); const ImageIOParameter *initial_cb_height = m_spec.find_attribute ("jpeg2000:InitialCodeBlockHeight", TypeDesc::UINT); if (initial_cb_height && initial_cb_height->data()) m_compression_parameters.cblockh_init = *(unsigned int*)initial_cb_height->data(); const ImageIOParameter *progression_order = m_spec.find_attribute ("jpeg2000:ProgressionOrder", TypeDesc::STRING); if (progression_order && progression_order->data()) { std::string prog_order((const char*)progression_order->data()); m_compression_parameters.prog_order = get_progression_order(prog_order); } const ImageIOParameter *compression_mode = m_spec.find_attribute ("jpeg2000:CompressionMode", TypeDesc::INT); if (compression_mode && compression_mode->data()) m_compression_parameters.mode = *(int*)compression_mode->data(); } OPJ_PROG_ORDER Jpeg2000Output::get_progression_order(const std::string &progression_order) { if (progression_order == "LRCP") return LRCP; else if (progression_order == "RLCP") return RLCP; else if (progression_order == "RPCL") return RPCL; else if (progression_order == "PCRL") return PCRL; else if (progression_order == "PCRL") return CPRL; return PROG_UNKNOWN; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/python/0000755000175000017500000000000013151711064016435 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/python/py_roi.cpp0000644000175000017500000000645413151711064020453 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "py_oiio.h" namespace PyOpenImageIO { using namespace boost::python; using self_ns::str; static ROI ROI_All; // Declare the OIIO ROI class to Python void declare_roi() { class_("ROI") .def_readwrite("xbegin", &ROI::xbegin) .def_readwrite("xend", &ROI::xend) .def_readwrite("ybegin", &ROI::ybegin) .def_readwrite("yend", &ROI::yend) .def_readwrite("zbegin", &ROI::zbegin) .def_readwrite("zend", &ROI::zend) .def_readwrite("chbegin", &ROI::chbegin) .def_readwrite("chend", &ROI::chend) .def(init()) .def(init()) .def(init()) .def(init()) .add_property("defined", &ROI::defined) .add_property("width", &ROI::width) .add_property("height", &ROI::height) .add_property("depth", &ROI::depth) .add_property("nchannels", &ROI::nchannels) .add_property("npixels", &ROI::npixels) .def_readonly("All", &ROI_All) // Define Python str(ROI), it automatically uses '<<' .def(str(self)) // __str__ // roi_union, roi_intersection, get_roi(spec), get_roi_full(spec) // set_roi(spec,newroi), set_roi_full(newroi) // overloaded operators .def(self == other()) // operator== .def(self != other()) // operator!= ; def("union", &roi_union); def("intersection", &roi_intersection); def("get_roi", &get_roi); def("get_roi_full", &get_roi_full); def("set_roi", &set_roi); def("set_roi_full", &set_roi_full); } } // namespace PyOpenImageIO openimageio-1.7.17~dfsg0.orig/src/python/py_imagecache.cpp0000644000175000017500000001602413151711064021722 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include "py_oiio.h" #include "OpenImageIO/ustring.h" namespace PyOpenImageIO { using namespace boost::python; ImageCacheWrap* ImageCacheWrap::create (bool shared=true) { ImageCacheWrap *icw = new ImageCacheWrap; icw->m_cache = ImageCache::create(shared); return icw; } void ImageCacheWrap::destroy (ImageCacheWrap *x) { ImageCache::destroy(x->m_cache); } std::string ImageCacheWrap::resolve_filename (const std::string &val) { ScopedGILRelease gil; return m_cache->resolve_filename(val); } #if 0 object ImageCacheWrap::get_image_info (ustring filename, int subimage, int miplevel, ustring dataname, TypeDesc datatype) { ScopedGILRelease gil; return m_cache->get_image_info(filename, subimage, miplevel, dataname, datatype, data); } object ImageCacheWrap::get_imagespec(ustring filename, int subimage=0) { ScopedGILRelease gil; return m_cache->get_imagespec(filename, spec, subimage); } #endif object ImageCacheWrap::get_pixels (const std::string &filename_, int subimage, int miplevel, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc datatype) { ScopedGILRelease gil; ustring filename (filename_); int chbegin = 0, chend = 0; if (! m_cache->get_image_info (filename, subimage, miplevel, ustring("channels"), TypeDesc::INT, &chend)) return object(handle<>(Py_None)); // couldn't open file size_t size = size_t ((xend-xbegin) * (yend-ybegin) * (zend-zbegin) * (chend-chbegin) * datatype.size()); boost::scoped_array data (new char [size]); if (! m_cache->get_pixels (filename, subimage, miplevel, xbegin, xend, ybegin, yend, zbegin, zend, datatype, &data[0])) return object(handle<>(Py_None)); // get_pixels failed; return C_array_to_Python_array (data.get(), datatype, size); } //Not sure how to expose this to Python. /* Tile *get_tile (ImageCache &ic, ustring filename, int subimage, int x, int y, int z) { return ic.get_tile(filename, subimage, x, y, z); } void release_tile (ImageCache &ic, Tile *tile) const { ic.release-tile(tile); } const void *tile_pixels (ImageCache &ic, Tile *tile, TypeDesc &format) const { ic.tile_pixels(tile, format); } */ std::string ImageCacheWrap::geterror () const { return m_cache->geterror(); } std::string ImageCacheWrap::getstats (int level=1) const { ScopedGILRelease gil; return m_cache->getstats(level); } void ImageCacheWrap::invalidate (ustring filename) { ScopedGILRelease gil; return m_cache->invalidate(filename); } void ImageCacheWrap::invalidate_all (bool force=false) { ScopedGILRelease gil; return m_cache->invalidate_all(force); } void ImageCacheWrap::attribute_int (const std::string &name, int val) { m_cache->attribute (name, val); } void ImageCacheWrap::attribute_float (const std::string &name, float val) { m_cache->attribute (name, val); } void ImageCacheWrap::attribute_string (const std::string &name, const std::string &val) { m_cache->attribute (name, val); } void ImageCacheWrap::attribute_typed (const std::string &name, TypeDesc type, object &obj) { ::PyOpenImageIO::attribute_typed (*m_cache, name, type, obj); } void ImageCacheWrap::attribute_tuple_typed (const std::string &name, TypeDesc type, tuple &obj) { ::PyOpenImageIO::attribute_tuple_typed (*m_cache, name, type, obj); } object ImageCacheWrap::getattribute_typed (const std::string &name, TypeDesc type) { return ::PyOpenImageIO::getattribute_typed (*m_cache, name, type); } void declare_imagecache() { class_("ImageCache", no_init) .def("create", &ImageCacheWrap::create, (arg("shared")), return_value_policy()) .staticmethod("create") .def("destroy", &ImageCacheWrap::destroy) .staticmethod("destroy") .def("attribute", &ImageCacheWrap::attribute_float) .def("attribute", &ImageCacheWrap::attribute_int) .def("attribute", &ImageCacheWrap::attribute_string) .def("attribute", &ImageCacheWrap::attribute_typed) .def("attribute", &ImageCacheWrap::attribute_tuple_typed) .def("getattribute", &ImageCacheWrap::getattribute_typed) // .def("getattribute", &ImageCacheWrap::get_attribute_untyped) .def("resolve_filename", &ImageCacheWrap::resolve_filename) // .def("get_image_info", &ImageCacheWrap::get_image_info) // .def("get_imagespec", &ImageCacheWrap::get_imagespec, // (arg("subimage")=0)), .def("get_pixels", &ImageCacheWrap::get_pixels) // .def("get_tile", &ImageCacheWrap::get_tile) // .def("release_tile", &ImageCacheWrap::release_tile) // .def("tile_pixels", &ImageCacheWrap::tile_pixels) //added _ to the method names for consistency .def("geterror", &ImageCacheWrap::geterror) .def("getstats", &ImageCacheWrap::getstats) .def("invalidate", &ImageCacheWrap::invalidate) .def("invalidate_all", &ImageCacheWrap::invalidate_all) ; } } // namespace PyOpenImageIO openimageio-1.7.17~dfsg0.orig/src/python/py_deepdata.cpp0000644000175000017500000001337313151711064021427 0ustar mfvmfv/* Copyright 2015 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "py_oiio.h" namespace PyOpenImageIO { using namespace boost::python; using self_ns::str; void DeepData_init (DeepData &dd, int npix, int nchan, tuple tuple_channeltypes, tuple tuple_channelnames) { std::vector chantypes; py_to_stdvector (chantypes, tuple_channeltypes); std::vector channames; py_to_stdvector (channames, tuple_channelnames); ScopedGILRelease gil; dd.init (npix, nchan, chantypes, channames); } void DeepData_init_spec (DeepData &dd, const ImageSpec &spec) { ScopedGILRelease gil; dd.init (spec); } int DeepData_get_capacity (const DeepData &dd, int pixel) { return int (dd.capacity(pixel)); } int DeepData_get_samples (const DeepData &dd, int pixel) { return int (dd.samples(pixel)); } void DeepData_set_nsamples (DeepData &dd, int pixel, int nsamples) { dd.set_samples (pixel, uint32_t(nsamples)); } void DeepData_set_deep_value_float (DeepData &dd, int pixel, int channel, int sample, float value) { dd.set_deep_value (pixel, channel, sample, value); } void DeepData_set_deep_value_uint (DeepData &dd, int pixel, int channel, int sample, uint32_t value) { dd.set_deep_value (pixel, channel, sample, value); } std::string DeepData_channelname (const DeepData &dd, int c) { // std::cout << "channelname(" << c << ")\n"; return dd.channelname (c); } int DeepData_channelsize (const DeepData &dd, int c) { return int (dd.channelsize (c)); } // Declare the OIIO DeepData class to Python void declare_deepdata() { class_("DeepData") .def_readonly ("npixels", &DeepData::pixels) //DEPRECATED(1.7) .def_readonly ("nchannels", &DeepData::channels) //DEPRECATED(1.7) .def_readonly ("pixels", &DeepData::pixels) .def_readonly ("channels", &DeepData::channels) .def("init", &DeepData_init, (arg("npixels"), arg("nchannels"), arg("channeltypes"), arg("channelnames"))) .def("init", &DeepData_init_spec) .def("clear", &DeepData::clear) .def("free", &DeepData::free) .def("samples", &DeepData_get_samples, (arg("pixel"))) .def("set_samples", &DeepData::set_samples, (arg("pixel"), arg("nsamples"))) .def("capacity", &DeepData_get_capacity, (arg("pixel"))) .def("set_capacity", &DeepData::set_capacity, (arg("pixel"), arg("nsamples"))) .def("insert_samples", &DeepData::insert_samples, (arg("pixel"), arg("samplepos"), arg("nsamples")=1)) .def("erase_samples", &DeepData::erase_samples, (arg("pixel"), arg("samplepos"), arg("nsamples")=1)) .def("channelname", &DeepData_channelname) .def("channeltype", &DeepData::channeltype) .def("channelsize", &DeepData_channelsize) .def("samplesize", &DeepData::samplesize) .def("deep_value", &DeepData::deep_value, (arg("pixel"), arg("channel"), arg("sample"))) .def("deep_value_uint", &DeepData::deep_value_uint, (arg("pixel"), arg("channel"), arg("sample"))) .def("set_deep_value", &DeepData_set_deep_value_float, (arg("pixel"), arg("channel"), arg("sample"), arg("value"))) .def("set_deep_value_uint", &DeepData_set_deep_value_uint, (arg("pixel"), arg("channel"), arg("sample"), arg("value"))) .def("copy_deep_sample", &DeepData::copy_deep_sample, (arg("pixel"), arg("sample"), arg("src"), arg("srcpixel"), arg("srcsample"))) .def("copy_deep_pixel", &DeepData::copy_deep_pixel, (arg("pixel"), arg("src"), arg("srcpixel"))) .def("split", &DeepData::split, (arg("pixel"), arg("depth"))) .def("sort", &DeepData::sort, (arg("pixel"))) .def("merge_overlaps", &DeepData::merge_overlaps, (arg("pixel"))) .def("merge_deep_pixels", &DeepData::merge_deep_pixels, (arg("pixel"), arg("src"), arg("srcpixel"))) .def("occlusion_cull", &DeepData::occlusion_cull, (arg("pixel"))) ; } } // namespace PyOpenImageIO openimageio-1.7.17~dfsg0.orig/src/python/CMakeLists.txt0000644000175000017500000001242113151711064021175 0ustar mfvmfv#if the CMAKE_CURRENT_BINARY_DIR is python3, then build the python3 module, #otherwise the python2 module string (REGEX MATCH "python3\$" _py3_subdir ${CMAKE_CURRENT_BINARY_DIR}) if (_py3_subdir) set (BUILD_PY3 ON) else () set (BUILD_PY3 OFF) endif () if (NOT BOOST_CUSTOM AND NOT BUILD_PY3) #Unset those, otherwise find_package(PythonLibs) will pick up old stuff #if it has been run before unset(Python_ADDITIONAL_VERSIONS) unset(PYTHON_LIBRARY) unset(PYTHON_LIBRARY CACHE) unset(PYTHON_INCLUDE_DIR) unset(PYTHON_INCLUDE_DIR CACHE) unset(PYTHON_INCLUDE_PATH) unset(PYTHON_INCLUDE_PATH CACHE) find_package (PythonLibs ${PYTHON_VERSION} REQUIRED) find_package (Boost 1.42 REQUIRED COMPONENTS python) elseif (BOOST_CUSTOM AND NOT BUILD_PY3) find_package (PythonLibs ${PYTHON_VERSION} REQUIRED) else () #BOOST_CUSTOM is ignored for python3 #Unset those, otherwise find_package(PythonLibs) will pick up old stuff #if it has been run before unset(PYTHON_LIBRARY) unset(PYTHON_LIBRARY CACHE) unset(PYTHON_INCLUDE_DIR) unset(PYTHON_INCLUDE_DIR CACHE) unset(PYTHON_INCLUDE_PATH) unset(PYTHON_INCLUDE_PATH CACHE) #cmake 2.8 does not look for python 3.4 set(Python_ADDITIONAL_VERSIONS 3.4) find_package (PythonInterp ${PYTHON3_VERSION} REQUIRED) find_package (PythonLibs ${PYTHON3_VERSION} REQUIRED) #Finding the python3 component for boost is a little tricky, since it has #different names on different systems. Try the most common ones #(boost_python3, boost_python-py34, …). foreach (_boost_py3_lib python3 python-py34 python-py33 python-py32) find_package (Boost 1.42 QUIET COMPONENTS ${_boost_py3_lib}) string (TOUPPER ${_boost_py3_lib} boost_py3_lib_name) if (Boost_${boost_py3_lib_name}_FOUND) #Not the most beautiful thing to do, but that gets them included in #the target_link_libraries(…) call farther down set (Boost_PYTHON_LIBRARIES ${Boost_${boost_py3_lib_name}_LIBRARIES}) break () endif () endforeach () endif () if (APPLE) # set (PYTHON_LIBRARIES /opt/local/lib) endif () # Disable some warnings for Clang, it's a little too picky with boost if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_definitions ("-Wno-array-bounds") endif () if (BUILD_PY3) set (target_name Py3OpenImageIO) else () set (target_name PyOpenImageIO) endif () if (BOOST_CUSTOM OR Boost_FOUND AND PYTHONLIBS_FOUND) set (python_srcs py_imageinput.cpp py_imageoutput.cpp py_imagecache.cpp py_imagespec.cpp py_roi.cpp py_imagebuf.cpp py_imagebufalgo.cpp py_typedesc.cpp py_paramvalue.cpp py_deepdata.cpp py_oiio.cpp) if (VERBOSE) message (STATUS "Python found ${PYTHONLIBS_FOUND} ") message (STATUS "Python include dirs ${PYTHON_INCLUDE_PATH}") message (STATUS "Python libraries ${PYTHON_LIBRARIES}") message (STATUS "Python to include 'lib' prefix: ${PYLIB_LIB_PREFIX}") message (STATUS "Python to include SO version: ${PYLIB_INCLUDE_SONAME}") endif () include_directories (${PYTHON_INCLUDE_PATH} ${Boost_INCLUDE_DIRS}) add_library (${target_name} MODULE ${python_srcs}) if (APPLE) target_link_libraries (${target_name} OpenImageIO ${Boost_LIBRARIES} ${Boost_PYTHON_LIBRARIES} ${CMAKE_DL_LIBS}) set_target_properties (${target_name} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") else () target_link_libraries (${target_name} OpenImageIO ${Boost_LIBRARIES} ${Boost_PYTHON_LIBRARIES} ${PYTHON_LIBRARIES} ${CMAKE_DL_LIBS}) endif () # Exclude the 'lib' prefix from the name if(NOT PYLIB_LIB_PREFIX) add_definitions("-DOIIO_PYMODULE_NAME=OpenImageIO") set_target_properties (${target_name} PROPERTIES OUTPUT_NAME OpenImageIO PREFIX "") else() add_definitions("-DOIIO_PYMODULE_NAME=PyOpenImageIO") set_target_properties (${target_name} PROPERTIES OUTPUT_NAME PyOpenImageIO PREFIX lib) endif () if(PYLIB_INCLUDE_SONAME) if (VERBOSE) message(STATUS "Setting PyOIIO SOVERSION to: ${SOVERSION}") endif () set_target_properties(${target_name} PROPERTIES VERSION ${OIIO_VERSION_MAJOR}.${OIIO_VERSION_MINOR} SOVERSION ${SOVERSION} ) endif() if (WIN32) set_target_properties (${target_name} PROPERTIES DEBUG_POSTFIX "_d" SUFFIX ".pyd") endif() if (BUILD_PY3) install (TARGETS ${target_name} RUNTIME DESTINATION ${PYLIB3_INSTALL_DIR} COMPONENT user LIBRARY DESTINATION ${PYLIB3_INSTALL_DIR} COMPONENT user) else () install (TARGETS ${target_name} RUNTIME DESTINATION ${PYLIB_INSTALL_DIR} COMPONENT user LIBRARY DESTINATION ${PYLIB_INSTALL_DIR} COMPONENT user) endif () elseif (BUILD_PY3) if (NOT PYTHONLIBS_FOUND) message (STATUS "Python3 libraries not found") endif () if (NOT Boost_FOUND) message (STATUS "Boost python3 component not found") endif () set(USE_PYTHON3 OFF) endif () openimageio-1.7.17~dfsg0.orig/src/python/py_oiio.cpp0000644000175000017500000003026313151711064020614 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "py_oiio.h" namespace PyOpenImageIO { using namespace boost::python; const char * python_array_code (TypeDesc format) { switch (format.basetype) { case TypeDesc::UINT8 : return "B"; case TypeDesc::INT8 : return "b"; case TypeDesc::UINT16 : return "H"; case TypeDesc::INT16 : return "h"; case TypeDesc::UINT32 : return "I"; case TypeDesc::INT32 : return "i"; case TypeDesc::FLOAT : return "f"; case TypeDesc::DOUBLE : return "d"; case TypeDesc::HALF : return "H"; // Return half in uint16 default : // For any other type, including UNKNOWN, pack it into an // unsigned byte array. return "B"; } } TypeDesc typedesc_from_python_array_code (char code) { switch (code) { case 'b' : case 'c' : return TypeDesc::INT8; case 'B' : return TypeDesc::UINT8; case 'h' : return TypeDesc::INT16; case 'H' : return TypeDesc::UINT16; case 'i' : return TypeDesc::INT; case 'I' : return TypeDesc::UINT; case 'l' : return TypeDesc::INT; case 'L' : return TypeDesc::UINT; case 'f' : return TypeDesc::FLOAT; case 'd' : return TypeDesc::DOUBLE; } return TypeDesc::UNKNOWN; } std::string object_classname (const object& obj) { return extract(obj.attr("__class__").attr("__name__")); } object C_array_to_Python_array (const char *data, TypeDesc type, size_t size) { // Figure out what kind of array to return and create it object arr_module(handle<>(PyImport_ImportModule("array"))); object array = arr_module.attr("array")(python_array_code(type)); // Create a Python byte array (or string for Python2) to hold the // data. #if PY_MAJOR_VERSION >= 3 object string_py(handle<>(PyBytes_FromStringAndSize(data, size))); #else object string_py(handle<>(PyString_FromStringAndSize(data, size))); #endif // Copy the data from the string to the array, then return the array. #if (PY_MAJOR_VERSION < 3) || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 2) array.attr("fromstring")(string_py); #else array.attr("frombytes")(string_py); #endif return array; } struct ustring_to_python_str { static PyObject* convert(ustring const& s) { return boost::python::incref(boost::python::object(s.string()).ptr()); } }; struct ustring_from_python_str { ustring_from_python_str() { boost::python::converter::registry::push_back( &convertible, &construct, boost::python::type_id()); } static void* convertible(PyObject* obj_ptr) { #if PY_MAJOR_VERSION >= 3 if (!PyUnicode_Check(obj_ptr)) return 0; #else if (!PyString_Check(obj_ptr)) return 0; #endif return obj_ptr; } static void construct( PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data) { #if PY_MAJOR_VERSION >= 3 PyObject* pyStr = PyUnicode_AsUTF8String(obj_ptr); const char* value = PyBytes_AsString(pyStr); #else const char* value = PyString_AsString(obj_ptr); #endif if (value == 0) boost::python::throw_error_already_set(); void* storage = ( (boost::python::converter::rvalue_from_python_storage*) data)->storage.bytes; new (storage) ustring(value); data->convertible = storage; } }; bool oiio_attribute_int (const std::string &name, int val) { return OIIO::attribute (name, val); } bool oiio_attribute_float (const std::string &name, float val) { return OIIO::attribute (name, val); } bool oiio_attribute_string (const std::string &name, const std::string &val) { return OIIO::attribute (name, val); } bool oiio_attribute_typed (const std::string &name, TypeDesc type, object &obj) { if (type.basetype == TypeDesc::INT) { std::vector vals; py_to_stdvector (vals, obj); if (vals.size() == type.numelements()*type.aggregate) return OIIO::attribute (name, type, &vals[0]); return false; } if (type.basetype == TypeDesc::FLOAT) { std::vector vals; py_to_stdvector (vals, obj); if (vals.size() == type.numelements()*type.aggregate) return OIIO::attribute (name, type, &vals[0]); return false; } if (type.basetype == TypeDesc::STRING) { std::vector vals; py_to_stdvector (vals, obj); if (vals.size() == type.numelements()*type.aggregate) { std::vector u; for (size_t i = 0, e = vals.size(); i < e; ++i) u.push_back (ustring(vals[i])); return OIIO::attribute (name, type, &u[0]); } return false; } return false; } bool oiio_attribute_tuple_typed (const std::string &name, TypeDesc type, tuple &obj) { if (type.basetype == TypeDesc::INT) { std::vector vals; py_to_stdvector (vals, obj); if (vals.size() == type.numelements()*type.aggregate) return OIIO::attribute (name, type, &vals[0]); return false; } if (type.basetype == TypeDesc::FLOAT) { std::vector vals; py_to_stdvector (vals, obj); if (vals.size() == type.numelements()*type.aggregate) return OIIO::attribute (name, type, &vals[0]); return false; } if (type.basetype == TypeDesc::STRING) { std::vector vals; py_to_stdvector (vals, obj); if (vals.size() == type.numelements()*type.aggregate) { std::vector u; for (size_t i = 0, e = vals.size(); i < e; ++i) u.push_back (ustring(vals[i])); return OIIO::attribute (name, type, &u[0]); } return false; } return false; } static object oiio_getattribute_typed (const std::string &name, TypeDesc type) { if (type == TypeDesc::UNKNOWN) return object(); char *data = OIIO_ALLOCA(char, type.size()); if (OIIO::getattribute (name, type, data)) { if (type.basetype == TypeDesc::INT) { #if PY_MAJOR_VERSION >= 3 return C_to_val_or_tuple ((const int *)data, type, PyLong_FromLong); #else return C_to_val_or_tuple ((const int *)data, type, PyInt_FromLong); #endif } if (type.basetype == TypeDesc::FLOAT) { return C_to_val_or_tuple ((const float *)data, type, PyFloat_FromDouble); } if (type.basetype == TypeDesc::STRING) { #if PY_MAJOR_VERSION >= 3 return C_to_val_or_tuple ((const char **)data, type, PyUnicode_FromString); #else return C_to_val_or_tuple ((const char **)data, type, PyString_FromString); #endif } } return object(); } static int oiio_get_int_attribute (const char *name) { return OIIO::get_int_attribute (name); } static int oiio_get_int_attribute_d (const char *name, int defaultval) { return OIIO::get_int_attribute (name, defaultval); } static float oiio_get_float_attribute (const char *name) { return OIIO::get_float_attribute (name); } static float oiio_get_float_attribute_d (const char *name, float defaultval) { return OIIO::get_float_attribute (name, defaultval); } static std::string oiio_get_string_attribute (const char *name) { return OIIO::get_string_attribute (name); } static std::string oiio_get_string_attribute_d (const char *name, const char *defaultval) { return OIIO::get_string_attribute (name, defaultval); } const void * python_array_address (const object &data, TypeDesc &elementtype, size_t &numelements) { // Figure out the type of the array object tcobj; try { tcobj = data.attr("typecode"); } catch(...) { return NULL; } if (! tcobj) return NULL; extract tce (tcobj); char typecode = tce.check() ? (char)tce : 0; elementtype = typedesc_from_python_array_code (typecode); if (elementtype == TypeDesc::UNKNOWN) return NULL; // TODO: The PyObject_AsReadBuffer is a deprecated API dating from // Python 1.6 (see https://docs.python.org/2/c-api/objbuffer.html). It // still works in 2.x, but for future-proofing, we should switch to the // memory buffer interface: // https://docs.python.org/2/c-api/buffer.html#bufferobjects // https://docs.python.org/3/c-api/buffer.html const void *addr = NULL; Py_ssize_t pylen = 0; int success = PyObject_AsReadBuffer(data.ptr(), &addr, &pylen); if (success != 0) { throw_error_already_set(); return NULL; } numelements = size_t(pylen) / elementtype.size(); return addr; } // This OIIO_DECLARE_PYMODULE mojo is necessary if we want to pass in the // MODULE name as a #define. Google for Argument-Prescan for additional // info on why this is necessary #define OIIO_DECLARE_PYMODULE(x) BOOST_PYTHON_MODULE(x) OIIO_DECLARE_PYMODULE(OIIO_PYMODULE_NAME) { // Conversion back and forth to ustring boost::python::to_python_converter< ustring, ustring_to_python_str>(); ustring_from_python_str(); // Basic helper classes declare_typedesc(); declare_paramvalue(); declare_imagespec(); declare_roi(); declare_deepdata(); // Main OIIO I/O classes declare_imageinput(); declare_imageoutput(); declare_imagebuf(); declare_imagecache(); declare_imagebufalgo(); // Global (OpenImageIO scope) functions and symbols def("geterror", &OIIO::geterror); def("attribute", &oiio_attribute_float); def("attribute", &oiio_attribute_int); def("attribute", &oiio_attribute_string); def("attribute", &oiio_attribute_typed); def("attribute", &oiio_attribute_tuple_typed); def("get_int_attribute", &oiio_get_int_attribute); def("get_int_attribute", &oiio_get_int_attribute_d); def("get_float_attribute", &oiio_get_float_attribute); def("get_float_attribute", &oiio_get_float_attribute_d); def("get_string_attribute", &oiio_get_string_attribute); def("get_string_attribute", &oiio_get_string_attribute_d); def("getattribute", &oiio_getattribute_typed); scope().attr("AutoStride") = AutoStride; scope().attr("openimageio_version") = OIIO_VERSION; scope().attr("VERSION") = OIIO_VERSION; scope().attr("VERSION_STRING") = OIIO_VERSION_STRING; scope().attr("VERSION_MAJOR") = OIIO_VERSION_MAJOR; scope().attr("VERSION_MINOR") = OIIO_VERSION_MINOR; scope().attr("VERSION_PATCH") = OIIO_VERSION_PATCH; scope().attr("INTRO_STRING") = OIIO_INTRO_STRING; #if BOOST_VERSION < 106500 boost::python::numeric::array::set_module_and_type("array", "array"); #endif } } // namespace PyOpenImageIO openimageio-1.7.17~dfsg0.orig/src/python/py_oiio.h0000644000175000017500000003500013151711064020253 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef PYOPENIMAGEIO_PY_OIIO_H #define PYOPENIMAGEIO_PY_OIIO_H // Avoid a compiler warning from a duplication in tiffconf.h/pyconfig.h #undef SIZEOF_LONG #include #include "OpenImageIO/imageio.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/imagecache.h" #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/deepdata.h" #if PY_MAJOR_VERSION < 2 || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 5) #define Py_ssize_t int #endif namespace PyOpenImageIO { using namespace boost::python; OIIO_NAMESPACE_USING void declare_imagespec(); void declare_imageinput(); void declare_imageoutput(); void declare_typedesc(); void declare_roi(); void declare_deepdata(); void declare_imagecache(); void declare_imagebuf(); void declare_imagebufalgo(); void declare_paramvalue(); void declare_global(); bool PyProgressCallback(void*, float); object C_array_to_Python_array (const char *data, TypeDesc type, size_t size); const char * python_array_code (TypeDesc format); TypeDesc typedesc_from_python_array_code (char code); std::string object_classname (const object& obj); // Given python array 'data', figure out its element type and number of // elements, and return the memory address of its contents. Return NULL as // the address for an error. const void * python_array_address (const object &data, TypeDesc &elementtype, size_t &numelements); // Suck up one or more presumed T values into a vector template void py_to_stdvector (std::vector &vals, const object &obj) { extract tup (obj); if (tup.check()) { // tuple case: recurse for (int i = 0, e = len(tup()); i < e; ++i) py_to_stdvector (vals, tup()[i]); } else { // non-tuple case (presumably scalar) extract t (obj); vals.push_back (t.check() ? t() : T()); } } // Suck up a tuple of presumed T values into a vector template void py_to_stdvector (std::vector &vals, const tuple &tup) { for (int i = 0, e = len(tup); i < e; ++i) py_to_stdvector (vals, tup[i]); } // Convert an array of T values into either tuple. FUNC is a conversion // function such as PyInt_FromLong, PyFloat_FromDouble, or // PyString_FromString. template object C_to_tuple (const T *vals, int size, FUNC f) { PyObject* result = PyTuple_New (size); for (int i = 0; i < size; ++i) PyTuple_SetItem(result, i, f(vals[i])); return object(handle<>(result)); } // Convert an array of T values (described by type) into either a simple // Python object (if it's an int, float, or string and a SCALAR) or a // Python tuple. FUNC is a conversion function such as PyInt_FromLong, // PyFloat_FromDouble, or PyString_FromString. template object C_to_val_or_tuple (const T *vals, TypeDesc type, FUNC f) { if (type.arraylen == 0 && type.aggregate == TypeDesc::SCALAR) { // scalar case return object (vals[0]); } // Array/aggregate case -- return a tuple int size = type.numelements() * type.aggregate; return C_to_tuple (vals, size, f); } template void attribute_typed (T &myobj, string_view name, TypeDesc type, object &dataobj) { if (type.basetype == TypeDesc::INT) { std::vector vals; py_to_stdvector (vals, dataobj); if (vals.size() == type.numelements()*type.aggregate) myobj.attribute (name, type, &vals[0]); return; } if (type.basetype == TypeDesc::FLOAT) { std::vector vals; py_to_stdvector (vals, dataobj); if (vals.size() == type.numelements()*type.aggregate) myobj.attribute (name, type, &vals[0]); return; } if (type.basetype == TypeDesc::STRING) { std::vector vals; py_to_stdvector (vals, dataobj); if (vals.size() == type.numelements()*type.aggregate) { std::vector u; for (size_t i = 0, e = vals.size(); i < e; ++i) u.push_back (ustring(vals[i])); myobj.attribute (name, type, &u[0]); } return; } } template void attribute_tuple_typed (T &myobj, string_view name, TypeDesc type, tuple &dataobj) { if (type.basetype == TypeDesc::INT) { std::vector vals; py_to_stdvector (vals, dataobj); if (vals.size() == type.numelements()*type.aggregate) myobj.attribute (name, type, &vals[0]); return; } if (type.basetype == TypeDesc::FLOAT) { std::vector vals; py_to_stdvector (vals, dataobj); if (vals.size() == type.numelements()*type.aggregate) myobj.attribute (name, type, &vals[0]); return; } if (type.basetype == TypeDesc::STRING) { std::vector vals; py_to_stdvector (vals, dataobj); if (vals.size() == type.numelements()*type.aggregate) { std::vector u; for (size_t i = 0, e = vals.size(); i < e; ++i) u.push_back (ustring(vals[i])); myobj.attribute (name, type, &u[0]); } return; } } template object getattribute_typed (const T& obj, string_view name, TypeDesc type) { if (type == TypeDesc::UNKNOWN) return object(); // require a type char *data = OIIO_ALLOCA (char, type.size()); bool ok = obj.getattribute (name, type, data); if (! ok) return object(); // None if (type.basetype == TypeDesc::INT) { #if PY_MAJOR_VERSION >= 3 return C_to_val_or_tuple ((const int *)data, type, PyLong_FromLong); #else return C_to_val_or_tuple ((const int *)data, type, PyInt_FromLong); #endif } if (type.basetype == TypeDesc::FLOAT) { return C_to_val_or_tuple ((const float *)data, type, PyFloat_FromDouble); } if (type.basetype == TypeDesc::STRING) { #if PY_MAJOR_VERSION >= 3 return C_to_val_or_tuple ((const char **)data, type, PyUnicode_FromString); #else return C_to_val_or_tuple ((const char **)data, type, PyString_FromString); #endif } return object(); } // Helper class to release the GIL, allowing other Python threads to // proceed, then re-acquire it again when the scope ends. class ScopedGILRelease { public: ScopedGILRelease () : m_thread_state(PyEval_SaveThread()) { } ~ScopedGILRelease () { PyEval_RestoreThread (m_thread_state); } private: PyThreadState *m_thread_state; }; class ImageInputWrap { private: /// Friend declaration for ImageOutputWrap::copy_image friend class ImageOutputWrap; ImageInput *m_input; public: virtual ~ImageInputWrap(); static object create(const std::string&, const std::string&); static object open_static_regular(const std::string&); static object open_static_with_config(const std::string&,const ImageSpec&); const char *format_name () const; bool valid_file (const std::string &name) const; bool open_regular (const std::string &name); bool open_with_config(const std::string &name, const ImageSpec &config); const ImageSpec &spec() const; int supports (const std::string &feature) const; bool close(); int current_subimage() const; int current_miplevel() const; bool seek_subimage (int, int); object read_image (int chbegin, int chend, TypeDesc); object read_scanline (int y, int z, TypeDesc format); object read_scanlines (int ybegin, int yend, int z, int chbegin, int chend, TypeDesc format); object read_tile (int x, int y, int z, TypeDesc format); object read_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc format); object read_native_deep_scanlines (int ybegin, int yend, int z, int chbegin, int chend); object read_native_deep_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend); object read_native_deep_image (); std::string geterror() const; }; class ImageOutputWrap { private: ImageOutput *m_output; const void *make_read_buffer (object &buffer, imagesize_t size); public: virtual ~ImageOutputWrap(); static boost::python::object create(const std::string&, const std::string&); const ImageSpec &spec() const; bool open (const std::string&, const ImageSpec&, ImageOutput::OpenMode); bool open_specs (const std::string&, tuple &specs); bool close(); bool write_scanline (int, int, TypeDesc, boost::python::object&, stride_t xstride=AutoStride); bool write_scanline_bt (int, int, TypeDesc::BASETYPE, boost::python::object&, stride_t xstride=AutoStride); bool write_scanline_array (int, int, object&); bool write_scanlines (int, int, int, TypeDesc, boost::python::object&, stride_t xstride=AutoStride); bool write_scanlines_bt (int, int, int, TypeDesc::BASETYPE, boost::python::object&, stride_t xstride=AutoStride); bool write_scanlines_array (int, int, int, object&); bool write_tile (int, int, int, TypeDesc, boost::python::object&, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride); bool write_tile_bt (int, int, int, TypeDesc::BASETYPE, boost::python::object&, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride); bool write_tile_array (int, int, int, object&); bool write_tiles (int, int, int, int, int, int, TypeDesc, boost::python::object&, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride); bool write_tiles_bt (int, int, int, int, int, int, TypeDesc::BASETYPE, boost::python::object&, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride); bool write_tiles_array (int, int, int, int, int, int, object&); bool write_image (TypeDesc format, object &buffer, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride); bool write_image_bt (TypeDesc::BASETYPE basetype, object &buffer, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride); bool write_image_array (object &buffer); bool write_deep_scanlines (int ybegin, int yend, int z, const DeepData &deepdata); bool write_deep_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, const DeepData &deepdata); bool write_deep_image (const DeepData &deepdata); bool copy_image (ImageInputWrap *iiw); const char *format_name () const; int supports (const std::string&) const; std::string geterror()const; }; class ImageCacheWrap { private: ImageCache *m_cache; public: static ImageCacheWrap *create (bool); static void destroy (ImageCacheWrap*); void attribute_int (const std::string&, int ); void attribute_float (const std::string&, float); void attribute_string (const std::string&, const std::string&); void attribute_typed (const std::string&, TypeDesc, object &obj); void attribute_tuple_typed (const std::string&, TypeDesc, tuple &obj); object getattribute_typed (const std::string&, TypeDesc); std::string resolve_filename (const std::string& filename); // object get_image_info (const std::string &filename, int subimage, // int miplevel, const std::string &dataname, // TypeDesc datatype); // object get_imagespec (const std::string &filename, int subimage=0, // int miplevel=0, bool native=false); object get_pixels (const std::string &filename, int subimage, int miplevel, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc datatype); //First needs to be exposed to python in imagecache.cpp /* Tile *get_tile (ustring filename, int subimage, int x, int y, int z) { return m_cache->get_tile(filename, subimage, x, y, z); } void release_tile (Tile *tile) const { m_cache->release-tile(tile); } const void *tile_pixels (Tile *tile, TypeDesc &format) const { m_cache->tile_pixels(tile, format); } */ std::string geterror () const; std::string getstats (int) const; void invalidate (ustring); void invalidate_all (bool); }; } // namespace PyOpenImageIO #endif // PYOPENIMAGEIO_PY_OIIO_H openimageio-1.7.17~dfsg0.orig/src/python/py_imagebuf.cpp0000644000175000017500000004375113151711064021442 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include "py_oiio.h" #include "OpenImageIO/platform.h" namespace PyOpenImageIO { using namespace boost::python; std::string ImageBuf_name (const ImageBuf &buf) { return buf.name(); } std::string ImageBuf_file_format_name (const ImageBuf &buf) { return buf.file_format_name(); } void ImageBuf_reset_name (ImageBuf &buf, const std::string &name) { buf.reset (name); } void ImageBuf_reset_name2 (ImageBuf &buf, const std::string &name, int subimage, int miplevel) { buf.reset (name, subimage, miplevel); } void ImageBuf_reset_name_config (ImageBuf &buf, const std::string &name, int subimage, int miplevel, const ImageSpec &config) { buf.reset (name, subimage, miplevel, NULL, &config); } void ImageBuf_reset_spec (ImageBuf &buf, const ImageSpec &spec) { buf.reset (spec); } bool ImageBuf_read (ImageBuf &buf, int subimage=0, int miplevel=0, bool force=false, TypeDesc convert=TypeDesc::UNKNOWN) { ScopedGILRelease gil; return buf.read (subimage, miplevel, force, convert); } bool ImageBuf_read2 (ImageBuf &buf, int subimage=0, int miplevel=0, bool force=false, TypeDesc::BASETYPE convert=TypeDesc::UNKNOWN) { ScopedGILRelease gil; return buf.read (subimage, miplevel, force, convert); } BOOST_PYTHON_FUNCTION_OVERLOADS(ImageBuf_read_overloads, ImageBuf_read, 1, 5) BOOST_PYTHON_FUNCTION_OVERLOADS(ImageBuf_read2_overloads, ImageBuf_read2, 1, 5) bool ImageBuf_write (const ImageBuf &buf, const std::string &filename, const std::string &fileformat="") { ScopedGILRelease gil; return buf.write (filename, fileformat); } BOOST_PYTHON_FUNCTION_OVERLOADS(ImageBuf_write_overloads, ImageBuf_write, 2, 3) bool ImageBuf_make_writeable (ImageBuf &buf, bool keep_cache_type) { ScopedGILRelease gil; return buf.make_writeable (keep_cache_type); } void ImageBuf_set_write_format (ImageBuf &buf, TypeDesc::BASETYPE format) { buf.set_write_format (format); } bool ImageBuf_copy (ImageBuf &buf, const ImageBuf &src, TypeDesc format = TypeDesc::UNKNOWN) { ScopedGILRelease gil; return buf.copy (src, format); } bool ImageBuf_copy2 (ImageBuf &buf, const ImageBuf &src, TypeDesc::BASETYPE format = TypeDesc::UNKNOWN) { ScopedGILRelease gil; return buf.copy (src, format); } BOOST_PYTHON_FUNCTION_OVERLOADS(ImageBuf_copy_overloads, ImageBuf_copy, 2, 3) BOOST_PYTHON_FUNCTION_OVERLOADS(ImageBuf_copy2_overloads, ImageBuf_copy2, 2, 3) void ImageBuf_set_full (ImageBuf &buf, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend) { buf.set_full (xbegin, xend, ybegin, yend, zbegin, zend); } float ImageBuf_getchannel (const ImageBuf &buf, int x, int y, int z, int c, ImageBuf::WrapMode wrap = ImageBuf::WrapBlack) { return buf.getchannel(x, y, z, c, wrap); } BOOST_PYTHON_FUNCTION_OVERLOADS(ImageBuf_getchannel_overloads, ImageBuf_getchannel, 5, 6) object ImageBuf_getpixel (const ImageBuf &buf, int x, int y, int z=0, ImageBuf::WrapMode wrap = ImageBuf::WrapBlack) { int nchans = buf.nchannels(); float *pixel = ALLOCA (float, nchans); buf.getpixel (x, y, z, pixel, nchans, wrap); PyObject *result = PyTuple_New (nchans); for (int i = 0; i < nchans; ++i) PyTuple_SetItem (result, i, PyFloat_FromDouble(pixel[i])); return object(handle<>(result)); } BOOST_PYTHON_FUNCTION_OVERLOADS(ImageBuf_getpixel_overloads, ImageBuf_getpixel, 3, 5) object ImageBuf_interppixel (const ImageBuf &buf, float x, float y, ImageBuf::WrapMode wrap = ImageBuf::WrapBlack) { int nchans = buf.nchannels(); float *pixel = ALLOCA (float, nchans); buf.interppixel (x, y, pixel, wrap); PyObject *result = PyTuple_New (nchans); for (int i = 0; i < nchans; ++i) PyTuple_SetItem (result, i, PyFloat_FromDouble(pixel[i])); return object(handle<>(result)); } BOOST_PYTHON_FUNCTION_OVERLOADS(ImageBuf_interppixel_overloads, ImageBuf_interppixel, 3, 4) object ImageBuf_interppixel_NDC (const ImageBuf &buf, float x, float y, ImageBuf::WrapMode wrap = ImageBuf::WrapBlack) { int nchans = buf.nchannels(); float *pixel = ALLOCA (float, nchans); buf.interppixel_NDC (x, y, pixel, wrap); return C_to_val_or_tuple (pixel, TypeDesc(TypeDesc::FLOAT,nchans), PyFloat_FromDouble); } BOOST_PYTHON_FUNCTION_OVERLOADS(ImageBuf_interppixel_NDC_overloads, ImageBuf_interppixel_NDC, 3, 4) object ImageBuf_interppixel_bicubic (const ImageBuf &buf, float x, float y, ImageBuf::WrapMode wrap = ImageBuf::WrapBlack) { int nchans = buf.nchannels(); float *pixel = ALLOCA (float, nchans); buf.interppixel_bicubic (x, y, pixel, wrap); PyObject *result = PyTuple_New (nchans); for (int i = 0; i < nchans; ++i) PyTuple_SetItem (result, i, PyFloat_FromDouble(pixel[i])); return object(handle<>(result)); } BOOST_PYTHON_FUNCTION_OVERLOADS(ImageBuf_interppixel_bicubic_overloads, ImageBuf_interppixel_bicubic, 3, 4) object ImageBuf_interppixel_bicubic_NDC (const ImageBuf &buf, float x, float y, ImageBuf::WrapMode wrap = ImageBuf::WrapBlack) { int nchans = buf.nchannels(); float *pixel = ALLOCA (float, nchans); buf.interppixel_bicubic_NDC (x, y, pixel, wrap); PyObject *result = PyTuple_New (nchans); for (int i = 0; i < nchans; ++i) PyTuple_SetItem (result, i, PyFloat_FromDouble(pixel[i])); return object(handle<>(result)); } BOOST_PYTHON_FUNCTION_OVERLOADS(ImageBuf_interppixel_bicubic_NDC_overloads, ImageBuf_interppixel_bicubic_NDC, 3, 4) void ImageBuf_setpixel (ImageBuf &buf, int x, int y, int z, tuple p) { std::vector pixel; py_to_stdvector (pixel, p); if (pixel.size()) buf.setpixel (x, y, z, &pixel[0], pixel.size()); } void ImageBuf_setpixel2 (ImageBuf &buf, int x, int y, tuple p) { ImageBuf_setpixel (buf, x, y, 0, p); } void ImageBuf_setpixel1 (ImageBuf &buf, int i, tuple p) { std::vector pixel; py_to_stdvector (pixel, p); if (pixel.size()) buf.setpixel (i, &pixel[0], pixel.size()); } object ImageBuf_get_pixels (const ImageBuf &buf, TypeDesc format, ROI roi=ROI::All()) { // Allocate our own temp buffer and try to read the image into it. // If the read fails, return None. if (! roi.defined()) roi = buf.roi(); roi.chend = std::min (roi.chend, buf.nchannels()+1); size_t size = (size_t) roi.npixels() * roi.nchannels() * format.size(); boost::scoped_array data (new char [size]); if (! buf.get_pixels (roi, format, &data[0])) { return object(handle<>(Py_None)); } return C_array_to_Python_array (data.get(), format, size); } BOOST_PYTHON_FUNCTION_OVERLOADS(ImageBuf_get_pixels_overloads, ImageBuf_get_pixels, 2, 3) object ImageBuf_get_pixels_bt (const ImageBuf &buf, TypeDesc::BASETYPE format, ROI roi=ROI::All()) { return ImageBuf_get_pixels (buf, TypeDesc(format), roi); } BOOST_PYTHON_FUNCTION_OVERLOADS(ImageBuf_get_pixels_bt_overloads, ImageBuf_get_pixels_bt, 2, 3) void ImageBuf_set_deep_value (ImageBuf &buf, int x, int y, int z, int c, int s, float value) { buf.set_deep_value (x, y, z, c, s, value); } void ImageBuf_set_deep_value_uint (ImageBuf &buf, int x, int y, int z, int c, int s, uint32_t value) { buf.set_deep_value (x, y, z, c, s, value); } bool ImageBuf_set_pixels_tuple (ImageBuf &buf, ROI roi, const tuple& data) { if (! roi.defined()) roi = buf.roi(); roi.chend = std::min (roi.chend, buf.nchannels()+1); size_t size = (size_t) roi.npixels() * roi.nchannels(); if (size == 0) return true; // done std::vector vals; py_to_stdvector (vals, data); if (size > vals.size()) return false; // Not enough data to fill our ROI buf.set_pixels (roi, TypeDesc::TypeFloat, &vals[0]); return true; } bool ImageBuf_set_pixels_array (ImageBuf &buf, ROI roi, const object& data) { // If it's a tuple, we handle that with the other function extract tup (data); if (tup.check()) return ImageBuf_set_pixels_tuple (buf, roi, tup()); if (! roi.defined()) roi = buf.roi(); roi.chend = std::min (roi.chend, buf.nchannels()+1); size_t size = (size_t) roi.npixels() * roi.nchannels(); if (size == 0) return true; // done TypeDesc elementtype; size_t numelements; const void* addr = python_array_address (data, elementtype, numelements); if (!addr || size > numelements) return false; // Not enough data to fill our ROI std::vector vals (numelements); convert_types (elementtype, addr, TypeDesc::TypeFloat, vals.data(), int(numelements)); buf.set_pixels (roi, TypeDesc::TypeFloat, &vals[0]); return true; } DeepData& ImageBuf_deepdataref (ImageBuf *ib) { return *ib->deepdata(); } static void ImageBuf_deep_alloc_dummy () { } void declare_imagebuf() { enum_("WrapMode") .value("WrapDefault", ImageBuf::WrapDefault ) .value("WrapBlack", ImageBuf::WrapBlack ) .value("WrapClamp", ImageBuf::WrapClamp ) .value("WrapPeriodic", ImageBuf::WrapPeriodic ) .value("WrapMirror", ImageBuf::WrapMirror ) .export_values(); class_ ("ImageBuf") .def(init()) .def(init()) .def(init()) .def("clear", &ImageBuf::clear) .def("reset", &ImageBuf_reset_name, (arg("name"))) .def("reset", &ImageBuf_reset_name2, (arg("name"), arg("subimage")=0, arg("miplevel")=0)) .def("reset", &ImageBuf_reset_name_config, (arg("name"), arg("subimage")=0, arg("miplevel")=0, arg("config")=ImageSpec())) .def("reset", &ImageBuf_reset_spec) .add_property ("initialized", &ImageBuf::initialized) .def("init_spec", &ImageBuf::init_spec) .def("read", &ImageBuf_read, ImageBuf_read_overloads()) .def("read", &ImageBuf_read2, ImageBuf_read2_overloads()) .def("write", &ImageBuf_write, ImageBuf_write_overloads()) // FIXME -- write(ImageOut&) .def("make_writeable", &ImageBuf_make_writeable, (arg("keep_cache_type")=false)) .def("set_write_format", &ImageBuf_set_write_format) .def("set_write_tiles", &ImageBuf::set_write_tiles, (arg("width")=0, arg("height")=0, arg("depth")=0)) .def("spec", &ImageBuf::spec, return_value_policy()) .def("nativespec", &ImageBuf::nativespec, return_value_policy()) .def("specmod", &ImageBuf::specmod, return_value_policy()) .add_property("name", &ImageBuf_name) .add_property("file_format_name", &ImageBuf_file_format_name) .add_property("subimage", &ImageBuf::subimage) .add_property("nsubimages", &ImageBuf::nsubimages) .add_property("miplevel", &ImageBuf::miplevel) .add_property("nmiplevels", &ImageBuf::nmiplevels) .add_property("nchannels", &ImageBuf::nchannels) .add_property("orientation", &ImageBuf::orientation, &ImageBuf::set_orientation) .add_property("oriented_width", &ImageBuf::oriented_width) .add_property("oriented_height", &ImageBuf::oriented_height) .add_property("oriented_x", &ImageBuf::oriented_x) .add_property("oriented_y", &ImageBuf::oriented_y) .add_property("oriented_full_width", &ImageBuf::oriented_full_width) .add_property("oriented_full_height", &ImageBuf::oriented_full_height) .add_property("oriented_full_x", &ImageBuf::oriented_full_x) .add_property("oriented_full_y", &ImageBuf::oriented_full_y) .add_property("xbegin", &ImageBuf::xbegin) .add_property("xend", &ImageBuf::xend) .add_property("ybegin", &ImageBuf::ybegin) .add_property("yend", &ImageBuf::yend) .add_property("zbegin", &ImageBuf::zbegin) .add_property("zend", &ImageBuf::zend) .add_property("xmin", &ImageBuf::xmin) .add_property("xmax", &ImageBuf::xmax) .add_property("ymin", &ImageBuf::ymin) .add_property("ymax", &ImageBuf::ymax) .add_property("zmin", &ImageBuf::zmin) .add_property("zmax", &ImageBuf::zmax) .add_property("roi", &ImageBuf::roi) .add_property("roi_full", &ImageBuf::roi_full, &ImageBuf::set_roi_full) .def("set_full", &ImageBuf_set_full) .add_property("pixels_valid", &ImageBuf::pixels_valid) .add_property("pixeltype", &ImageBuf::pixeltype) .add_property("has_error", &ImageBuf::has_error) .def("geterror", &ImageBuf::geterror) .def("pixelindex", &ImageBuf::pixelindex, (arg("x"), arg("y"), arg("z"), arg("check_range")=false)) .def("copy_metadata", &ImageBuf::copy_metadata) .def("copy_pixels", &ImageBuf::copy_pixels) .def("copy", &ImageBuf_copy, ImageBuf_copy_overloads()) .def("copy", &ImageBuf_copy2, ImageBuf_copy2_overloads()) .def("swap", &ImageBuf::swap) .def("getchannel", &ImageBuf_getchannel, ImageBuf_getchannel_overloads()) .def("getpixel", &ImageBuf_getpixel, ImageBuf_getpixel_overloads()) .def("interppixel", &ImageBuf_interppixel, ImageBuf_interppixel_overloads()) .def("interppixel_NDC", &ImageBuf_interppixel_NDC, ImageBuf_interppixel_NDC_overloads()) .def("interppixel_NDC_full", &ImageBuf_interppixel_NDC, ImageBuf_interppixel_NDC_overloads()) .def("interppixel_bicubic", &ImageBuf_interppixel_bicubic, ImageBuf_interppixel_bicubic_overloads()) .def("interppixel_bicubic_NDC", &ImageBuf_interppixel_bicubic_NDC, ImageBuf_interppixel_bicubic_NDC_overloads()) .def("setpixel", &ImageBuf_setpixel) .def("setpixel", &ImageBuf_setpixel2) .def("setpixel", &ImageBuf_setpixel1) .def("get_pixels", &ImageBuf_get_pixels, ImageBuf_get_pixels_overloads()) .def("get_pixels", &ImageBuf_get_pixels_bt, ImageBuf_get_pixels_bt_overloads()) .def("set_pixels", &ImageBuf_set_pixels_tuple) .def("set_pixels", &ImageBuf_set_pixels_array) .add_property("deep", &ImageBuf::deep) .def("deep_samples", &ImageBuf::deep_samples, (arg("x"), arg("y"), arg("z")=0)) .def("set_deep_samples", &ImageBuf::set_deep_samples, (arg("x"), arg("y"), arg("z")=0, arg("nsamples")=1)) .def("deep_insert_samples", &ImageBuf::deep_insert_samples, (arg("x"), arg("y"), arg("z")=0, arg("samplepos"), arg("nsamples")=1)) .def("deep_erase_samples", &ImageBuf::deep_erase_samples, (arg("x"), arg("y"), arg("z")=0, arg("samplepos"), arg("nsamples")=1)) .def("deep_value", &ImageBuf::deep_value, (arg("x"), arg("y"), arg("z")=0, arg("channel"), arg("sample"))) .def("deep_value_uint", &ImageBuf::deep_value_uint, (arg("x"), arg("y"), arg("z")=0, arg("channel"), arg("sample"))) .def("set_deep_value", &ImageBuf_set_deep_value, (arg("x"), arg("y"), arg("z")=0, arg("channel"), arg("sample"), arg("value")=0.0f)) .def("set_deep_value_uint", &ImageBuf_set_deep_value_uint, (arg("x"), arg("y"), arg("z")=0, arg("channel"), arg("sample"), arg("value")=0)) .def("deep_alloc", &ImageBuf_deep_alloc_dummy) // DEPRECATED(1.7) .def("deepdata", &ImageBuf_deepdataref, return_value_policy()) // FIXME -- do we want to provide pixel iterators? ; } } // namespace PyOpenImageIO openimageio-1.7.17~dfsg0.orig/src/python/python_tests0000644000175000017500000000572013151711064021127 0ustar mfvmfvI just copy-paste these bits of code to the Python console. read-image: import OpenImageIO as o import array spec = o.ImageSpec() pic = o.ImageInput.create("test.jpg", "/home/dgaletic/code/oiio-trunk/dist/linux/lib") pic.open("test.jpg", spec) desc = spec.format arr = array.array("B", "\0" * spec.image_bytes()) pic.read_image(desc, arr) read-image-simple (reads to continuous float pixels): import OpenImageIO as o spec = o.ImageSpec() pic = o.ImageInput.create("test.jpg", "/home/dgaletic/code/oiio-trunk/dist/linux/lib") pic.open("test.jpg", spec) desc = spec.format arr = pic.read_image() read and write file scanline by scanline: import OpenImageIO as o import array spec = o.ImageSpec() pic = o.ImageInput.create("test.jpg", "/home/dgaletic/code/oiio-trunk/dist/linux/lib") pic.open("test.jpg", spec) out = o.ImageOutput.create("test-scanline.jpg", "/home/dgaletic/code/oiio-trunk/dist/linux/lib") out.open("test-scanline.jpg", spec, False) desc = spec.format for i in range(spec.height): arr = array.array("B", "\0" * spec.width * spec.nchannels) pic.read_scanline(i, 0, desc, arr) out.write_scanline(i, 0, desc, arr) out.close() write-image: out = o.ImageOutput.create("test.jpg", "/home/dgaletic/code/oiio-trunk/dist/linux/lib") out.open("test-scanline.jpg", spec, False) out.write_image(desc, arr) out.close() brighter: for channel in range (len(arr)): if arr[channel] <= 240: arr[channel] += 15 else: arr[channel] = 255 darken only the very bright areas of the picture (please don't consider this a serious algorithm): for channel in range (0, len(arr), 3): if arr[channel] + arr[channel+1] + arr[channel+2] > 600: arr[channel] += -60 arr[channel+1] += -60 arr[channel+2] += -60 To alter which color gets changed, simply change the start value in range() function. If there are three channels (RGB) and the step is set to 3, a start value of 0 will change all the red pixel values, 1 green, and 2 blue. red: for channel in range (0, len(arr), 3): if arr[channel] <= 200: arr[channel] += 55 else: arr[channel] = 255 The step is set to 4 here because the test was performed on a four channel image (RGBA). only red: for channel in range (0, len(arr), 4): arr[channel+1] = 0 arr[channel+2] = 0 high red: for channel in range (0, len(arr), 4): arr[channel] = 255 gamma: for channel in range (len(arr)): value = arr[channel]**0.98 arr[channel] = int(value) random (this one does nothing useful, just changes the RGB values of each pixel by +-50, to a maximum of 0/255 ): import random for channel in range (len(arr)): value = random.randrange(arr[channel]-50, arr[channel]+50) if value > 255: arr[channel] = 255 elif value < 0: arr[channel] = 0 else: arr[channel] = value grayscale: for channel in range(0, len(arr), 3): arr[channel] = arr[channel+1] = arr[channel+2] = (arr[channel] + arr[channel+1] + arr[channel+2]) / 3 openimageio-1.7.17~dfsg0.orig/src/python/py_typedesc.cpp0000644000175000017500000001736513151711064021505 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "py_oiio.h" namespace PyOpenImageIO { using namespace boost::python; using self_ns::str; static TypeDesc::BASETYPE TypeDesc_get_basetype (const TypeDesc &t) { return (TypeDesc::BASETYPE)t.basetype; } static TypeDesc::AGGREGATE TypeDesc_get_aggregate (const TypeDesc &t) { return (TypeDesc::AGGREGATE)t.aggregate; } static TypeDesc::VECSEMANTICS TypeDesc_get_vecsemantics (const TypeDesc &t) { return (TypeDesc::VECSEMANTICS)t.vecsemantics; } static void TypeDesc_set_basetype (TypeDesc &t, TypeDesc::BASETYPE val) { t.basetype = val; } static void TypeDesc_set_aggregate (TypeDesc &t, TypeDesc::AGGREGATE val) { t.aggregate = val; } static void TypeDesc_set_vecsemantics (TypeDesc &t, TypeDesc::VECSEMANTICS val) { t.vecsemantics = val; } static void TypeDesc_fromstring (TypeDesc &t, const char* typestring) { t.fromstring (typestring); } // Declare the OIIO TypeDesc type to Python void declare_typedesc() { enum_("BASETYPE") .value("UNKNOWN", TypeDesc::UNKNOWN) .value("NONE", TypeDesc::NONE) .value("UCHAR", TypeDesc::UCHAR) .value("UINT8", TypeDesc::UINT8) .value("CHAR", TypeDesc::CHAR) .value("INT8", TypeDesc::INT8) .value("USHORT", TypeDesc::USHORT) .value("UINT16", TypeDesc::UINT16) .value("SHORT", TypeDesc::SHORT) .value("INT16", TypeDesc::INT16) .value("UINT", TypeDesc::UINT) .value("UINT32", TypeDesc::UINT32) .value("INT", TypeDesc::INT) .value("INT32", TypeDesc::INT32) .value("ULONGLONG", TypeDesc::ULONGLONG) .value("UINT64", TypeDesc::UINT64) .value("LONGLONG", TypeDesc::LONGLONG) .value("INT64", TypeDesc::INT64) .value("HALF", TypeDesc::HALF) .value("FLOAT", TypeDesc::FLOAT) .value("DOUBLE", TypeDesc::DOUBLE) .value("STRING", TypeDesc::STRING) .value("PTR", TypeDesc::PTR) .value("LASTBASE", TypeDesc::LASTBASE) .export_values() ; enum_("AGGREGATE") .value("SCALAR", TypeDesc::SCALAR) .value("VEC2", TypeDesc::VEC2) .value("VEC3", TypeDesc::VEC3) .value("VEC4", TypeDesc::VEC4) .value("MATRIX33", TypeDesc::MATRIX33) .value("MATRIX44", TypeDesc::MATRIX44) .export_values() ; enum_("VECSEMANTICS") .value("NOXFORM", TypeDesc::NOXFORM) .value("NOSEMANTICS", TypeDesc::NOSEMANTICS) .value("COLOR", TypeDesc::COLOR) .value("POINT", TypeDesc::POINT) .value("VECTOR", TypeDesc::VECTOR) .value("NORMAL", TypeDesc::NORMAL) .value("TIMECODE", TypeDesc::TIMECODE) .value("KEYCODE", TypeDesc::KEYCODE) .export_values() ; class_("TypeDesc") // basetype, aggregate, and vecsemantics should look like BASETYPE, // AGGREGATE, VECSEMANTICS, but since they are stored as unsigned // char, def_readwrite() doesn't do the right thing. Instead, we // use set_foo/get_foo wrappers, but from Python it looks like // regular member access. .add_property("basetype", &TypeDesc_get_basetype, &TypeDesc_set_basetype) .add_property("aggregate", &TypeDesc_get_aggregate, &TypeDesc_set_aggregate) .add_property("vecsemantics", &TypeDesc_get_vecsemantics, &TypeDesc_set_vecsemantics) .def_readwrite("arraylen", &TypeDesc::arraylen) // Constructors: () [defined implicitly], (base), (base, agg), // (base,agg,vecsem), (base,agg,vecsem,arraylen), string. .def(init()) .def(init()) .def(init()) .def(init()) .def(init()) // Unfortunately, overloading the int varieties, as we do in C++, // doesn't seem to work properly, it can't distinguish between an // int and an AGGREGATE, for example. Maybe in C++11 with strong // enum typing, it will work. But for now, we must forego these // variants of the constructors: // .def(init()) // .def(init()) .def("c_str", &TypeDesc::c_str) .def("numelements", &TypeDesc::numelements) .def("size", &TypeDesc::size) .def("elementtype", &TypeDesc::elementtype) .def("elementsize", &TypeDesc::elementsize) .def("basesize", &TypeDesc::basesize) .def("fromstring", &TypeDesc_fromstring) .def("equivalent", &TypeDesc::equivalent) .def("unarray", &TypeDesc::unarray) .def("is_vec3", &TypeDesc::is_vec3) .def("is_vec4", &TypeDesc::is_vec4) // overloaded operators .def(self == other()) // operator== .def(self != other()) // operator!= // Define Python str(TypeDesc), it automatically uses '<<' .def(str(self)) // __str__ // Static members of pre-constructed types .def_readonly("TypeFloat", &TypeDesc::TypeFloat) .def_readonly("TypeColor", &TypeDesc::TypeColor) .def_readonly("TypeString", &TypeDesc::TypeString) .def_readonly("TypeInt", &TypeDesc::TypeInt) .def_readonly("TypeHalf", &TypeDesc::TypeHalf) .def_readonly("TypePoint", &TypeDesc::TypePoint) .def_readonly("TypeVector", &TypeDesc::TypeVector) .def_readonly("TypeNormal", &TypeDesc::TypeNormal) .def_readonly("TypeMatrix", &TypeDesc::TypeMatrix) .def_readonly("TypeMatrix33", &TypeDesc::TypeMatrix33) .def_readonly("TypeMatrix44", &TypeDesc::TypeMatrix44) .def_readonly("TypeTimeCode", &TypeDesc::TypeTimeCode) .def_readonly("TypeKeyCode", &TypeDesc::TypeKeyCode) .def_readonly("TypeFloat4", &TypeDesc::TypeFloat4) ; } } // namespace PyOpenImageIO openimageio-1.7.17~dfsg0.orig/src/python/py_paramvalue.cpp0000644000175000017500000001270513151711064022013 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "py_oiio.h" namespace PyOpenImageIO { using boost::python::make_tuple; using namespace std; template object ParamValue_convert(const TypeDesc& t, int n, const BaseType* data) { switch (t.aggregate) { case TypeDesc::SCALAR: return object(data[n]); case TypeDesc::VEC2: return make_tuple(data[n*2], data[n*2+1]); case TypeDesc::VEC3: return make_tuple(data[n*3], data[n*3+1], data[n*3+2]); case TypeDesc::VEC4: return make_tuple(data[n*4], data[n*4+1], data[n*4+2], data[n*4+3]); // Bypass the make_tuple argument list size limit by making two // tuples and adding them. Inefficient, but not likely to be a bottleneck. // If it turns out we need efficient access to this stuff we should look // at an array/ctypes interface. case TypeDesc::MATRIX44: return make_tuple( data[n*16+0], data[n*16+1], data[n*16+2], data[n*16+3], data[n*16+4], data[n*16+5], data[n*16+6], data[n*16+7]) + make_tuple(data[n*16+8], data[n*16+9], data[n*16+10], data[n*16+11], data[n*16+12], data[n*16+13], data[n*16+14], data[n*16+15]); default: PyErr_SetString(PyExc_TypeError, "Unable to convert ParamValue with unknown TypeDesc"); throw_error_already_set(); } return object(); } object ParamValue_getitem(const ParamValue& self, int n) { if (n >= self.nvalues()) { PyErr_SetString(PyExc_IndexError, "ParamValue index out of range"); throw_error_already_set(); } TypeDesc t = self.type(); #define ParamValue_convert_dispatch(TYPE) \ case TypeDesc::TYPE: \ return ParamValue_convert(t,n,(CType::type*)self.data()); switch (t.basetype) { ParamValue_convert_dispatch(UCHAR) ParamValue_convert_dispatch(CHAR) ParamValue_convert_dispatch(USHORT) ParamValue_convert_dispatch(SHORT) ParamValue_convert_dispatch(UINT) ParamValue_convert_dispatch(INT) ParamValue_convert_dispatch(ULONGLONG) ParamValue_convert_dispatch(LONGLONG) #ifdef _HALF_H_ ParamValue_convert_dispatch(HALF) #endif ParamValue_convert_dispatch(FLOAT) ParamValue_convert_dispatch(DOUBLE) case TypeDesc::STRING: return ParamValue_convert(t, n, (ustring*)self.data()); default: return object(); } #undef ParamValue_convert_dispatch } static std::string ParamValue_name(const ParamValue& self) { return self.name().string(); } static object ParamValue_value (const ParamValue& self) { return ParamValue_getitem (self, 0); } ParamValue& ParamValueList_getitem(ParamValueList& self, int i) { return self[i]; } void declare_paramvalue() { enum_("Interp") .value("INTERP_CONSTANT", ParamValue::INTERP_CONSTANT) .value("INTERP_PERPIECE", ParamValue::INTERP_PERPIECE) .value("INTERP_LINEAR", ParamValue::INTERP_LINEAR) .value("INTERP_VERTEX", ParamValue::INTERP_VERTEX) ; class_("ParamValue") .add_property("name", &ParamValue_name) .add_property("type", &ParamValue::type) .add_property("value", &ParamValue_value) .def("__getitem__", &ParamValue_getitem) .def("__len__", &ParamValue::nvalues) ; class_("ParamValueList") .def("__getitem__", &ParamValueList_getitem, return_internal_reference<>()) .def("__iter__", boost::python::iterator()) .def("__len__", &ParamValueList::size) .def("grow", &ParamValueList::grow, return_internal_reference<>()) .def("append", &ParamValueList::push_back) .def("clear", &ParamValueList::clear) .def("free", &ParamValueList::free) .def("resize", &ParamValueList::resize) ; } } openimageio-1.7.17~dfsg0.orig/src/python/py_imagebufalgo.cpp0000644000175000017500000016574013151711064022310 0ustar mfvmfv/* Copyright 2013 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "OpenImageIO/color.h" #include "OpenImageIO/imagebufalgo.h" #include "py_oiio.h" namespace PyOpenImageIO { using namespace boost::python; class IBA_dummy { }; // dummy class to establish a scope bool IBA_zero (ImageBuf &dst, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::zero (dst, roi, nthreads); } bool IBA_fill (ImageBuf &dst, tuple values_tuple, ROI roi=ROI::All(), int nthreads=0) { std::vector values; py_to_stdvector (values, values_tuple); if (dst.initialized()) values.resize (dst.nchannels(), 0.0f); else if (roi.defined()) values.resize (roi.nchannels(), 0.0f); else return false; ASSERT (values.size() > 0); ScopedGILRelease gil; return ImageBufAlgo::fill (dst, &values[0], roi, nthreads); } bool IBA_fill2 (ImageBuf &dst, tuple top_tuple, tuple bottom_tuple, ROI roi=ROI::All(), int nthreads=0) { std::vector top, bottom; py_to_stdvector (top, top_tuple); py_to_stdvector (bottom, bottom_tuple); if (dst.initialized()) { top.resize (dst.nchannels(), 0.0f); bottom.resize (dst.nchannels(), 0.0f); } else if (roi.defined()) { top.resize (roi.nchannels(), 0.0f); bottom.resize (roi.nchannels(), 0.0f); } else return false; ASSERT (top.size() > 0 && bottom.size() > 0); ScopedGILRelease gil; return ImageBufAlgo::fill (dst, &top[0], &bottom[0], roi, nthreads); } bool IBA_fill4 (ImageBuf &dst, tuple top_left_tuple, tuple top_right_tuple, tuple bottom_left_tuple, tuple bottom_right_tuple, ROI roi=ROI::All(), int nthreads=0) { std::vector top_left, top_right, bottom_left, bottom_right; py_to_stdvector (top_left, top_left_tuple); py_to_stdvector (top_right, top_right_tuple); py_to_stdvector (bottom_left, bottom_left_tuple); py_to_stdvector (bottom_right, bottom_right_tuple); if (dst.initialized()) { top_left.resize (dst.nchannels(), 0.0f); top_right.resize (dst.nchannels(), 0.0f); bottom_left.resize (dst.nchannels(), 0.0f); bottom_right.resize (dst.nchannels(), 0.0f); } else if (roi.defined()) { top_left.resize (roi.nchannels(), 0.0f); top_right.resize (roi.nchannels(), 0.0f); bottom_left.resize (roi.nchannels(), 0.0f); bottom_right.resize (roi.nchannels(), 0.0f); } else return false; ASSERT (top_left.size() > 0 && top_right.size() > 0 && bottom_left.size() > 0 && bottom_right.size() > 0); ScopedGILRelease gil; return ImageBufAlgo::fill (dst, &top_left[0], &top_right[0], &bottom_left[0], &bottom_right[0], roi, nthreads); } bool IBA_checker (ImageBuf &dst, int width, int height, int depth, tuple color1_tuple, tuple color2_tuple, int xoffset, int yoffset, int zoffset, ROI roi, int nthreads) { std::vector color1, color2; py_to_stdvector (color1, color1_tuple); py_to_stdvector (color2, color2_tuple); if (dst.initialized()) color1.resize (dst.nchannels(), 0.0f); else if (roi.defined()) color1.resize (roi.nchannels(), 0.0f); else return false; if (dst.initialized()) color2.resize (dst.nchannels(), 0.0f); else if (roi.defined()) color2.resize (roi.nchannels(), 0.0f); else return false; ScopedGILRelease gil; return ImageBufAlgo::checker (dst, width, height, depth, &color1[0], &color2[0], xoffset, yoffset, zoffset, roi, nthreads); } bool IBA_noise (ImageBuf &dst, std::string type, float A, float B, bool mono, int seed, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::noise (dst, type, A, B, mono, seed, roi, nthreads); } bool IBA_channels (ImageBuf &dst, const ImageBuf &src, tuple channelorder_, tuple newchannelnames_, bool shuffle_channel_names, int nthreads) { size_t nchannels = (size_t) len(channelorder_); if (nchannels < 1) { dst.error ("No channels selected"); return false; } std::vector channelorder (nchannels, -1); std::vector channelvalues (nchannels, 0.0f); for (size_t i = 0; i < nchannels; ++i) { extract chnum (channelorder_[i]); if (chnum.check()) { channelorder[i] = chnum(); continue; } extract chval (channelorder_[i]); if (chval.check()) { channelvalues[i] = chval(); continue; } extract chname (channelorder_[i]); if (chname.check()) { for (int c = 0; c < src.nchannels(); ++c) { if (src.spec().channelnames[c] == chname()) channelorder[i] = c; } continue; } } std::vector newchannelnames; py_to_stdvector (newchannelnames, newchannelnames_); if (newchannelnames.size() != 0 && newchannelnames.size() != nchannels) { dst.error ("Inconsistent number of channel arguments"); return false; } ScopedGILRelease gil; return ImageBufAlgo::channels (dst, src, (int)nchannels, &channelorder[0], channelvalues.size() ? &channelvalues[0] : NULL, newchannelnames.size() ? &newchannelnames[0] : NULL, shuffle_channel_names, nthreads); } bool IBA_channel_append (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::channel_append (dst, A, B, roi, nthreads); } bool IBA_deepen (ImageBuf &dst, const ImageBuf &src, float zvalue, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::deepen (dst, src, zvalue, roi, nthreads); } bool IBA_flatten (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::flatten (dst, src, roi, nthreads); } bool IBA_deep_merge (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, bool occlusion_cull, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::deep_merge (dst, A, B, occlusion_cull, roi, nthreads); } bool IBA_copy (ImageBuf &dst, const ImageBuf &src, TypeDesc::BASETYPE convert, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::copy (dst, src, convert, roi, nthreads); } bool IBA_crop (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::crop (dst, src, roi, nthreads); } bool IBA_cut (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::cut (dst, src, roi, nthreads); } bool IBA_paste (ImageBuf &dst, int xbegin, int ybegin, int zbegin, int chbegin, const ImageBuf &src, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::paste (dst, xbegin, ybegin, zbegin, chbegin, src, roi, nthreads); } bool IBA_rotate90 (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::rotate90 (dst, src, roi, nthreads); } bool IBA_rotate180 (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::rotate180 (dst, src, roi, nthreads); } bool IBA_rotate270 (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::rotate270 (dst, src, roi, nthreads); } bool IBA_flip (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::flip (dst, src, roi, nthreads); } bool IBA_flop (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::flop (dst, src, roi, nthreads); } bool IBA_reorient (ImageBuf &dst, const ImageBuf &src, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::reorient (dst, src, nthreads); } bool IBA_transpose (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::transpose (dst, src, roi, nthreads); } bool IBA_circular_shift (ImageBuf &dst, const ImageBuf &src, int xshift, int yshift, int zshift, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::circular_shift (dst, src, xshift, yshift, zshift, roi, nthreads); } bool IBA_add_color (ImageBuf &dst, const ImageBuf &A, tuple values_tuple, ROI roi=ROI::All(), int nthreads=0) { std::vector values; py_to_stdvector (values, values_tuple); if (roi.defined()) values.resize (roi.nchannels(), 0.0f); else if (A.initialized()) values.resize (A.nchannels(), 0.0f); else return false; ASSERT (values.size() > 0); ScopedGILRelease gil; return ImageBufAlgo::add (dst, A, &values[0], roi, nthreads); } bool IBA_add_float (ImageBuf &dst, const ImageBuf &A, float val, ROI roi=ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::add (dst, A, val, roi, nthreads); } bool IBA_add_images (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi=ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::add (dst, A, B, roi, nthreads); } bool IBA_sub_color (ImageBuf &dst, const ImageBuf &A, tuple values_tuple, ROI roi=ROI::All(), int nthreads=0) { std::vector values; py_to_stdvector (values, values_tuple); if (roi.defined()) values.resize (roi.nchannels(), 0.0f); else if (A.initialized()) values.resize (A.nchannels(), 0.0f); else return false; ASSERT (values.size() > 0); ScopedGILRelease gil; return ImageBufAlgo::sub (dst, A, &values[0], roi, nthreads); } bool IBA_sub_float (ImageBuf &dst, const ImageBuf &A, float val, ROI roi=ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::sub (dst, A, val, roi, nthreads); } bool IBA_sub_images (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi=ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::sub (dst, A, B, roi, nthreads); } bool IBA_absdiff_color (ImageBuf &dst, const ImageBuf &A, tuple values_tuple, ROI roi=ROI::All(), int nthreads=0) { std::vector values; py_to_stdvector (values, values_tuple); if (roi.defined()) values.resize (roi.nchannels(), 0.0f); else if (A.initialized()) values.resize (A.nchannels(), 0.0f); else return false; ASSERT (values.size() > 0); ScopedGILRelease gil; return ImageBufAlgo::absdiff (dst, A, &values[0], roi, nthreads); } bool IBA_absdiff_float (ImageBuf &dst, const ImageBuf &A, float val, ROI roi=ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::absdiff (dst, A, val, roi, nthreads); } bool IBA_absdiff_images (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi=ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::absdiff (dst, A, B, roi, nthreads); } bool IBA_abs (ImageBuf &dst, const ImageBuf &A, ROI roi=ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::abs (dst, A, roi, nthreads); } bool IBA_mul_color (ImageBuf &dst, const ImageBuf &A, tuple values_tuple, ROI roi=ROI::All(), int nthreads=0) { std::vector values; py_to_stdvector (values, values_tuple); if (roi.defined()) values.resize (roi.nchannels(), 0.0f); else if (A.initialized()) values.resize (A.nchannels(), 0.0f); else return false; ASSERT (values.size() > 0); ScopedGILRelease gil; return ImageBufAlgo::mul (dst, A, &values[0], roi, nthreads); } bool IBA_mul_float (ImageBuf &dst, const ImageBuf &A, float B, ROI roi=ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::mul (dst, A, B, roi, nthreads); } bool IBA_mul_images (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi=ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::mul (dst, A, B, roi, nthreads); } bool IBA_div_color (ImageBuf &dst, const ImageBuf &A, tuple values_tuple, ROI roi=ROI::All(), int nthreads=0) { std::vector values; py_to_stdvector (values, values_tuple); if (roi.defined()) values.resize (roi.nchannels(), 0.0f); else if (A.initialized()) values.resize (A.nchannels(), 0.0f); else return false; ASSERT (values.size() > 0); ScopedGILRelease gil; return ImageBufAlgo::div (dst, A, &values[0], roi, nthreads); } bool IBA_div_float (ImageBuf &dst, const ImageBuf &A, float B, ROI roi=ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::div (dst, A, B, roi, nthreads); } bool IBA_div_images (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi=ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::div (dst, A, B, roi, nthreads); } bool IBA_mad_color (ImageBuf &dst, const ImageBuf &A, tuple Bvalues_tuple, tuple Cvalues_tuple, ROI roi=ROI::All(), int nthreads=0) { std::vector Bvalues, Cvalues; py_to_stdvector (Bvalues, Bvalues_tuple); if (roi.defined()) Bvalues.resize (roi.nchannels(), 0.0f); else if (A.initialized()) Bvalues.resize (A.nchannels(), 0.0f); else return false; py_to_stdvector (Cvalues, Cvalues_tuple); if (roi.defined()) Cvalues.resize (roi.nchannels(), 0.0f); else if (A.initialized()) Cvalues.resize (A.nchannels(), 0.0f); else return false; ASSERT (Bvalues.size() > 0 && Cvalues.size() > 0); ScopedGILRelease gil; return ImageBufAlgo::mad (dst, A, &Bvalues[0], &Cvalues[0], roi, nthreads); } bool IBA_mad_float (ImageBuf &dst, const ImageBuf &A, float B, float C, ROI roi=ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::mad (dst, A, B, C, roi, nthreads); } bool IBA_mad_images (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, const ImageBuf &C, ROI roi=ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::mad (dst, A, B, C, roi, nthreads); } bool IBA_invert (ImageBuf &dst, const ImageBuf &A, ROI roi=ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::invert (dst, A, roi, nthreads); } bool IBA_pow_color (ImageBuf &dst, const ImageBuf &A, tuple values_tuple, ROI roi=ROI::All(), int nthreads=0) { std::vector values; py_to_stdvector (values, values_tuple); if (roi.defined()) values.resize (roi.nchannels(), 0.0f); else if (A.initialized()) values.resize (A.nchannels(), 0.0f); else return false; ASSERT (values.size() > 0); ScopedGILRelease gil; return ImageBufAlgo::pow (dst, A, &values[0], roi, nthreads); } bool IBA_pow_float (ImageBuf &dst, const ImageBuf &A, float B, ROI roi=ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::pow (dst, A, B, roi, nthreads); } bool IBA_clamp (ImageBuf &dst, const ImageBuf &src, tuple min_, tuple max_, bool clampalpha01 = false, ROI roi = ROI::All(), int nthreads=0) { if (! src.initialized()) return false; std::vector min, max; py_to_stdvector (min, min_); py_to_stdvector (max, max_); min.resize (src.nchannels(), -std::numeric_limits::max()); max.resize (src.nchannels(), std::numeric_limits::max()); ScopedGILRelease gil; return ImageBufAlgo::clamp (dst, src, &min[0], &max[0], clampalpha01, roi, nthreads); } bool IBA_clamp_float (ImageBuf &dst, const ImageBuf &src, float min_, float max_, bool clampalpha01 = false, ROI roi = ROI::All(), int nthreads=0) { ScopedGILRelease gil; if (! src.initialized()) return false; std::vector min, max; min.resize (src.nchannels(), min_); max.resize (src.nchannels(), max_); return ImageBufAlgo::clamp (dst, src, &min[0], &max[0], clampalpha01, roi, nthreads); } bool IBA_channel_sum_weight (ImageBuf &dst, const ImageBuf &src, tuple weight_tuple, ROI roi=ROI::All(), int nthreads=0) { std::vector weight; py_to_stdvector (weight, weight_tuple); if (! src.initialized()) { dst.error ("Uninitialized source image for channel_sum"); return false; } if (weight.size() == 0) weight.resize (src.nchannels(), 1.0f); // no weights -> uniform else weight.resize (src.nchannels(), 0.0f); // missing weights -> 0 ScopedGILRelease gil; return ImageBufAlgo::channel_sum (dst, src, &weight[0], roi, nthreads); } bool IBA_channel_sum (ImageBuf &dst, const ImageBuf &src, ROI roi=ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::channel_sum (dst, src, NULL, roi, nthreads); } bool IBA_rangeexpand (ImageBuf &dst, const ImageBuf &src, bool useluma = false, ROI roi = ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::rangeexpand (dst, src, useluma, roi, nthreads); } bool IBA_rangecompress (ImageBuf &dst, const ImageBuf &src, bool useluma = false, ROI roi = ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::rangecompress (dst, src, useluma, roi, nthreads); } bool IBA_premult (ImageBuf &dst, const ImageBuf &src, ROI roi = ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::premult (dst, src, roi, nthreads); } bool IBA_unpremult (ImageBuf &dst, const ImageBuf &src, ROI roi = ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::unpremult (dst, src, roi, nthreads); } bool IBA_computePixelStats (const ImageBuf &src, ImageBufAlgo::PixelStats &stats, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::computePixelStats (stats, src, roi, nthreads); } bool IBA_compare (const ImageBuf &A, const ImageBuf &B, float failthresh, float warnthresh, ImageBufAlgo::CompareResults &result, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::compare (A, B, failthresh, warnthresh, result, roi, nthreads); } bool IBA_compare_Yee (const ImageBuf &A, const ImageBuf &B, ImageBufAlgo::CompareResults &result, float luminance, float fov, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::compare_Yee (A, B, result, luminance, fov, roi, nthreads); } std::string IBA_computePixelHashSHA1 (const ImageBuf &src, const std::string &extrainfo = std::string(), ROI roi = ROI::All(), int blocksize = 0, int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::computePixelHashSHA1 (src, extrainfo, roi, blocksize, nthreads); } bool IBA_warp (ImageBuf &dst, const ImageBuf &src, tuple values_M, const std::string &filtername = "", float filterwidth = 0.0f, bool recompute_roi = false, ImageBuf::WrapMode wrap = ImageBuf::WrapDefault, ROI roi=ROI::All(), int nthreads=0) { std::vector M; py_to_stdvector (M, values_M); if (M.size() != 9) return false; ScopedGILRelease gil; return ImageBufAlgo::warp (dst, src, *(Imath::M33f *)&M[0], filtername, filterwidth, recompute_roi, wrap, roi, nthreads); } bool IBA_rotate (ImageBuf &dst, const ImageBuf &src, float angle, const std::string &filtername = "", float filterwidth = 0.0f, bool recompute_roi = false, ROI roi=ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::rotate (dst, src, angle, filtername, filterwidth, recompute_roi, roi, nthreads); } bool IBA_rotate2 (ImageBuf &dst, const ImageBuf &src, float angle, float center_x, float center_y, const std::string &filtername = "", float filterwidth = 0.0f, bool recompute_roi = false, ROI roi=ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::rotate (dst, src, angle, center_x, center_y, filtername, filterwidth, recompute_roi, roi, nthreads); } bool IBA_resize (ImageBuf &dst, const ImageBuf &src, const std::string &filtername = "", float filterwidth = 0.0f, ROI roi=ROI::All(), int nthreads=0) { ScopedGILRelease gil; return ImageBufAlgo::resize (dst, src, filtername, filterwidth, roi, nthreads); } bool IBA_resample (ImageBuf &dst, const ImageBuf &src, bool interpolate, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::resample (dst, src, interpolate, roi, nthreads); } bool IBA_make_kernel (ImageBuf &dst, const std::string &name, float width, float height, float depth, bool normalize) { ScopedGILRelease gil; return ImageBufAlgo::make_kernel (dst, name, width, height, depth, normalize); } bool IBA_convolve (ImageBuf &dst, const ImageBuf &src, const ImageBuf &kernel, bool normalize, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::convolve (dst, src, kernel, normalize, roi, nthreads); } bool IBA_unsharp_mask (ImageBuf &dst, const ImageBuf &src, const std::string &kernel, float width, float contrast, float threshold, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::unsharp_mask (dst, src, kernel, width, contrast, threshold, roi, nthreads); } bool IBA_median_filter (ImageBuf &dst, const ImageBuf &src, int width, int height, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::median_filter (dst, src, width, height, roi, nthreads); } bool IBA_dilate (ImageBuf &dst, const ImageBuf &src, int width, int height, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::dilate (dst, src, width, height, roi, nthreads); } bool IBA_erode (ImageBuf &dst, const ImageBuf &src, int width, int height, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::erode (dst, src, width, height, roi, nthreads); } bool IBA_laplacian (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::laplacian (dst, src, roi, nthreads); } bool IBA_fft (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::fft (dst, src, roi, nthreads); } bool IBA_ifft (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::ifft (dst, src, roi, nthreads); } bool IBA_polar_to_complex (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::polar_to_complex (dst, src, roi, nthreads); } bool IBA_complex_to_polar (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::complex_to_polar (dst, src, roi, nthreads); } bool IBA_fillholes_pushpull (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::fillholes_pushpull (dst, src, roi, nthreads); } bool IBA_over (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi = ROI::All(), int nthreads = 0) { ScopedGILRelease gil; return ImageBufAlgo::over (dst, A, B, roi, nthreads); } bool IBA_zover (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, bool z_zeroisinf = false, ROI roi = ROI::All(), int nthreads = 0) { ScopedGILRelease gil; return ImageBufAlgo::zover (dst, A, B, z_zeroisinf, roi, nthreads); } bool IBA_colorconvert (ImageBuf &dst, const ImageBuf &src, const std::string &from, const std::string &to, bool unpremult = false, ROI roi = ROI::All(), int nthreads = 0) { ScopedGILRelease gil; return ImageBufAlgo::colorconvert (dst, src, from, to, unpremult, NULL, roi, nthreads); } bool IBA_colorconvert_colorconfig (ImageBuf &dst, const ImageBuf &src, const std::string &from, const std::string &to, bool unpremult = false, const std::string &context_key="", const std::string &context_value="", const std::string &colorconfig="", ROI roi = ROI::All(), int nthreads = 0) { ColorConfig config (colorconfig); ScopedGILRelease gil; return ImageBufAlgo::colorconvert (dst, src, from, to, unpremult, context_key, context_value, &config, roi, nthreads); } bool IBA_ociolook (ImageBuf &dst, const ImageBuf &src, const std::string &looks, const std::string &from, const std::string &to, bool inverse, bool unpremult, const std::string &context_key, const std::string &context_value, ROI roi = ROI::All(), int nthreads = 0) { ScopedGILRelease gil; return ImageBufAlgo::ociolook (dst, src, looks, from, to, inverse, unpremult, context_key, context_value, NULL, roi, nthreads); } bool IBA_ociolook_colorconfig (ImageBuf &dst, const ImageBuf &src, const std::string &looks, const std::string &from, const std::string &to, bool inverse, bool unpremult, const std::string &context_key, const std::string &context_value, const std::string &colorconfig="", ROI roi = ROI::All(), int nthreads = 0) { ColorConfig config (colorconfig); ScopedGILRelease gil; return ImageBufAlgo::ociolook (dst, src, looks, from, to, inverse, unpremult, context_key, context_value, &config, roi, nthreads); } bool IBA_ociodisplay (ImageBuf &dst, const ImageBuf &src, const std::string &display, const std::string &view, const object &from, const object &looks, bool unpremult, const std::string &context_key, const std::string &context_value, ROI roi = ROI::All(), int nthreads = 0) { std::string from_str, looks_str; if (from != object()) from_str = extract(from); if (looks != object()) looks_str = extract(looks); ScopedGILRelease gil; return ImageBufAlgo::ociodisplay (dst, src, display.c_str(), view.c_str(), from == object() ? NULL : from_str.c_str(), looks == object() ? NULL : looks_str.c_str(), unpremult, context_key, context_value, NULL, roi, nthreads); } bool IBA_ociodisplay_colorconfig (ImageBuf &dst, const ImageBuf &src, const std::string &display, const std::string &view, const object &from, const object &looks, bool unpremult, const std::string &context_key, const std::string &context_value, const std::string &colorconfig = "", ROI roi = ROI::All(), int nthreads = 0) { ColorConfig config (colorconfig); std::string from_str, looks_str; if (from != object()) from_str = extract(from); if (looks != object()) looks_str = extract(looks); ScopedGILRelease gil; return ImageBufAlgo::ociodisplay (dst, src, display.c_str(), view.c_str(), from == object() ? NULL : from_str.c_str(), looks == object() ? NULL : looks_str.c_str(), unpremult, context_key, context_value, &config, roi, nthreads); } bool IBA_ociofiletransform (ImageBuf &dst, const ImageBuf &src, const std::string &name, bool inverse, bool unpremult, ROI roi = ROI::All(), int nthreads = 0) { ScopedGILRelease gil; return ImageBufAlgo::ociofiletransform (dst, src, name, inverse, unpremult, NULL, roi, nthreads); } bool IBA_ociofiletransform_colorconfig (ImageBuf &dst, const ImageBuf &src, const std::string &name, bool inverse, bool unpremult, const std::string &colorconfig="", ROI roi = ROI::All(), int nthreads = 0) { ColorConfig config (colorconfig); ScopedGILRelease gil; return ImageBufAlgo::ociofiletransform (dst, src, name, inverse, unpremult, &config, roi, nthreads); } object IBA_isConstantColor (const ImageBuf &src, ROI roi = ROI::All(), int nthreads = 0) { std::vector constcolor (src.nchannels()); bool r; { ScopedGILRelease gil; r = ImageBufAlgo::isConstantColor (src, &constcolor[0], roi, nthreads); } if (r) { return C_to_tuple (&constcolor[0], (int)constcolor.size(), PyFloat_FromDouble); } else { return object(); } } bool IBA_isConstantChannel (const ImageBuf &src, int channel, float val, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::isConstantChannel (src, channel, val, roi, nthreads); } bool IBA_isMonochrome (const ImageBuf &src, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::isMonochrome (src, roi, nthreads); } ROI IBA_nonzero_region(const ImageBuf &src, ROI roi, int nthreads) { ScopedGILRelease gil; return ImageBufAlgo::nonzero_region(src, roi, nthreads); } bool IBA_fixNonFinite (ImageBuf &dst, const ImageBuf &src, ImageBufAlgo::NonFiniteFixMode mode=ImageBufAlgo::NONFINITE_BOX3, ROI roi = ROI::All(), int nthreads = 0) { ScopedGILRelease gil; return ImageBufAlgo::fixNonFinite (dst, src, mode, NULL, roi, nthreads); } bool IBA_render_point (ImageBuf &dst, int x, int y, tuple color_ = tuple()) { std::vector color; py_to_stdvector (color, color_); color.resize (dst.nchannels(), 1.0f); ScopedGILRelease gil; return ImageBufAlgo::render_point (dst, x, y, color); } bool IBA_render_line (ImageBuf &dst, int x1, int y1, int x2, int y2, tuple color_ = tuple(), bool skip_first_point=false) { std::vector color; py_to_stdvector (color, color_); color.resize (dst.nchannels(), 1.0f); ScopedGILRelease gil; return ImageBufAlgo::render_line (dst, x1, y1, x2, y2, color, skip_first_point); } bool IBA_render_box (ImageBuf &dst, int x1, int y1, int x2, int y2, tuple color_ = tuple(), bool fill=false) { std::vector color; py_to_stdvector (color, color_); color.resize (dst.nchannels(), 1.0f); ScopedGILRelease gil; return ImageBufAlgo::render_box (dst, x1, y1, x2, y2, color, fill); } bool IBA_render_text (ImageBuf &dst, int x, int y, const std::string &text, int fontsize=16, const std::string &fontname="", tuple textcolor_ = tuple()) { std::vector textcolor; py_to_stdvector (textcolor, textcolor_); textcolor.resize (dst.nchannels(), 1.0f); ScopedGILRelease gil; return ImageBufAlgo::render_text (dst, x, y, text, fontsize, fontname, &textcolor[0]); } bool IBA_capture_image (ImageBuf &dst, int cameranum, TypeDesc::BASETYPE convert = TypeDesc::UNKNOWN) { ScopedGILRelease gil; return ImageBufAlgo::capture_image (dst, cameranum, convert); } bool IBA_make_texture_ib (ImageBufAlgo::MakeTextureMode mode, const ImageBuf &buf, const std::string &outputfilename, const ImageSpec &config) { ScopedGILRelease gil; return ImageBufAlgo::make_texture (mode, buf, outputfilename, config); } bool IBA_make_texture_filename (ImageBufAlgo::MakeTextureMode mode, const std::string &filename, const std::string &outputfilename, const ImageSpec &config) { ScopedGILRelease gil; return ImageBufAlgo::make_texture (mode, filename, outputfilename, config); } #if PY_MAJOR_VERSION >= 3 # define PYLONG(x) PyLong_FromLong((long)x) #else # define PYLONG(x) PyInt_FromLong((long)x) #endif static object PixelStats_get_min(const ImageBufAlgo::PixelStats& stats) { size_t size = stats.min.size(); PyObject* result = PyTuple_New(size); for (size_t i = 0; i < size; ++i) PyTuple_SetItem(result, i, PyFloat_FromDouble(stats.min[i])); return object(handle<>(result)); } static object PixelStats_get_max(const ImageBufAlgo::PixelStats& stats) { size_t size = stats.min.size(); PyObject* result = PyTuple_New(size); for (size_t i = 0; i < size; ++i) PyTuple_SetItem(result, i, PyFloat_FromDouble(stats.max[i])); return object(handle<>(result)); } static object PixelStats_get_avg(const ImageBufAlgo::PixelStats& stats) { size_t size = stats.min.size(); PyObject* result = PyTuple_New(size); for (size_t i = 0; i < size; ++i) PyTuple_SetItem(result, i, PyFloat_FromDouble(stats.avg[i])); return object(handle<>(result)); } static object PixelStats_get_stddev(const ImageBufAlgo::PixelStats& stats) { size_t size = stats.min.size(); PyObject* result = PyTuple_New(size); for (size_t i = 0; i < size; ++i) PyTuple_SetItem(result, i, PyFloat_FromDouble(stats.stddev[i])); return object(handle<>(result)); } static object PixelStats_get_nancount(const ImageBufAlgo::PixelStats& stats) { size_t size = stats.min.size(); PyObject* result = PyTuple_New(size); for (size_t i = 0; i < size; ++i) PyTuple_SetItem(result, i, PYLONG((long)stats.nancount[i])); return object(handle<>(result)); } static object PixelStats_get_infcount(const ImageBufAlgo::PixelStats& stats) { size_t size = stats.min.size(); PyObject* result = PyTuple_New(size); for (size_t i = 0; i < size; ++i) PyTuple_SetItem(result, i, PYLONG((long)stats.infcount[i])); return object(handle<>(result)); } static object PixelStats_get_finitecount(const ImageBufAlgo::PixelStats& stats) { size_t size = stats.min.size(); PyObject* result = PyTuple_New(size); for (size_t i = 0; i < size; ++i) PyTuple_SetItem(result, i, PYLONG((long)stats.finitecount[i])); return object(handle<>(result)); } static object PixelStats_get_sum(const ImageBufAlgo::PixelStats& stats) { size_t size = stats.min.size(); PyObject* result = PyTuple_New(size); for (size_t i = 0; i < size; ++i) PyTuple_SetItem(result, i, PyFloat_FromDouble(stats.sum[i])); return object(handle<>(result)); } static object PixelStats_get_sum2(const ImageBufAlgo::PixelStats& stats) { size_t size = stats.min.size(); PyObject* result = PyTuple_New(size); for (size_t i = 0; i < size; ++i) PyTuple_SetItem(result, i, PyFloat_FromDouble(stats.sum2[i])); return object(handle<>(result)); } void declare_imagebufalgo() { enum_("NonFiniteFixMode") .value("NONFINITE_NONE", ImageBufAlgo::NONFINITE_NONE) .value("NONFINITE_BLACK", ImageBufAlgo::NONFINITE_BLACK) .value("NONFINITE_BOX3", ImageBufAlgo::NONFINITE_BOX3) .export_values() ; enum_("MakeTextureMode") .value("MakeTxTexture", ImageBufAlgo::MakeTxTexture) .value("MakeTxShadow", ImageBufAlgo::MakeTxShadow) .value("MakeTxEnvLatl", ImageBufAlgo::MakeTxEnvLatl) .value("MakeTxEnvLatlFromLightProbe", ImageBufAlgo::MakeTxEnvLatlFromLightProbe) .export_values() ; class_("PixelStats") .add_property("min", &PixelStats_get_min) .add_property("max", &PixelStats_get_max) .add_property("avg", &PixelStats_get_avg) .add_property("stddev", &PixelStats_get_stddev) .add_property("nancount", &PixelStats_get_nancount) .add_property("infcount", &PixelStats_get_infcount) .add_property("finitecount", &PixelStats_get_finitecount) .add_property("sum", &PixelStats_get_sum) .add_property("sum2", &PixelStats_get_sum2) ; class_("CompareResults") .def_readwrite("meanerror", &ImageBufAlgo::CompareResults::meanerror) .def_readwrite("rms_error", &ImageBufAlgo::CompareResults::rms_error) .def_readwrite("PSNR", &ImageBufAlgo::CompareResults::PSNR) .def_readwrite("maxerror", &ImageBufAlgo::CompareResults::maxerror) .def_readwrite("maxx", &ImageBufAlgo::CompareResults::maxx) .def_readwrite("maxy", &ImageBufAlgo::CompareResults::maxy) .def_readwrite("maxz", &ImageBufAlgo::CompareResults::maxz) .def_readwrite("maxc", &ImageBufAlgo::CompareResults::maxc) .def_readwrite("nwarn", &ImageBufAlgo::CompareResults::nwarn) .def_readwrite("nfail", &ImageBufAlgo::CompareResults::nfail) ; // Use a boost::python::scope to put this all inside "ImageBufAlgo" boost::python::scope IBA = class_("ImageBufAlgo") .def("zero", &IBA_zero, (arg("dst"), arg("roi")=ROI::All(), arg("nthreads")=0) ) .staticmethod("zero") .def("fill", &IBA_fill, (arg("dst"), arg("values"), arg("roi")=ROI::All(), arg("nthreads")=0) ) .def("fill", &IBA_fill2, (arg("dst"), arg("top"), arg("bottom"), arg("roi")=ROI::All(), arg("nthreads")=0) ) .def("fill", &IBA_fill4, (arg("dst"), arg("topleft"), arg("topright"), arg("bottomleft"), arg("bottomright"), arg("roi")=ROI::All(), arg("nthreads")=0) ) .staticmethod("fill") .def("checker", &IBA_checker, (arg("dst"), arg("width"), arg("height"), arg("depth"), arg("color1"), arg("color2"), arg("xoffset")=0, arg("yoffset")=0, arg("zoffset")=0, arg("roi")=ROI::All(), arg("nthreads")=0) ) .staticmethod("checker") .def("noise", &IBA_noise, (arg("dst"), arg("type")="gaussian", arg("A")=0.0f, arg("B")=0.1f, arg("mono")=false, arg("seed")=0, arg("roi")=ROI::All(), arg("nthreads")=0) ) .staticmethod("noise") .def("channels", &IBA_channels, (arg("dst"), arg("src"), arg("channelorder"), arg("newchannelnames")=tuple(), arg("shuffle_channel_names")=false, arg("nthreads")=0) ) .staticmethod("channels") .def("channel_append", IBA_channel_append, (arg("dst"), arg("A"), arg("B"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("channel_append") .def("deepen", IBA_deepen, (arg("dst"), arg("src"), arg("zvalue")=1.0f, arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("deepen") .def("flatten", IBA_flatten, (arg("dst"), arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("flatten") .def("deep_merge", IBA_deep_merge, (arg("dst"), arg("A"), arg("B"), arg("occlusion_cull")=true, arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("deep_merge") .def("copy", IBA_copy, (arg("dst"), arg("src"), arg("convert")=TypeDesc::UNKNOWN, arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("copy") .def("crop", IBA_crop, (arg("dst"), arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("crop") .def("cut", IBA_cut, (arg("dst"), arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("cut") .def("paste", IBA_paste, (arg("dst"), arg("xbegin"), arg("ybegin"), arg("zbegin"), arg("chbegin"), arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("paste") .def("rotate90", IBA_rotate90, (arg("dst"), arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("rotate90") .def("rotate180", IBA_rotate180, (arg("dst"), arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("rotate180") .def("rotate270", IBA_rotate270, (arg("dst"), arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("rotate270") .def("flip", IBA_flip, (arg("dst"), arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("flip") .def("flop", IBA_flop, (arg("dst"), arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("flop") .def("reorient", IBA_reorient, (arg("dst"), arg("src"), arg("nthreads")=0)) .staticmethod("reorient") .def("transpose", IBA_transpose, (arg("dst"), arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("transpose") .def("circular_shift", &IBA_circular_shift, (arg("dst"), arg("src"), arg("xshift"), arg("yshift"), arg("zshift")=0, arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("circular_shift") .def("add", &IBA_add_images, (arg("dst"), arg("A"), arg("B"), arg("roi")=ROI::All(), arg("nthreads")=0)) .def("add", &IBA_add_float, (arg("dst"), arg("A"), arg("B"), arg("roi")=ROI::All(), arg("nthreads")=0)) .def("add", IBA_add_color, (arg("dst"), arg("A"), arg("B"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("add") .def("sub", &IBA_sub_images, (arg("dst"), arg("A"), arg("B"), arg("roi")=ROI::All(), arg("nthreads")=0)) .def("sub", &IBA_sub_float, (arg("dst"), arg("A"), arg("B"), arg("roi")=ROI::All(), arg("nthreads")=0)) .def("sub", IBA_sub_color, (arg("dst"), arg("A"), arg("B"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("sub") .def("absdiff", &IBA_absdiff_images, (arg("dst"), arg("A"), arg("B"), arg("roi")=ROI::All(), arg("nthreads")=0)) .def("absdiff", &IBA_absdiff_float, (arg("dst"), arg("A"), arg("B"), arg("roi")=ROI::All(), arg("nthreads")=0)) .def("absdiff", IBA_absdiff_color, (arg("dst"), arg("A"), arg("B"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("absdiff") .def("abs", &IBA_abs, (arg("dst"), arg("A"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("abs") .def("mul", &IBA_mul_images, (arg("dst"), arg("A"), arg("B"), arg("roi")=ROI::All(), arg("nthreads")=0)) .def("mul", &IBA_mul_float, (arg("dst"), arg("A"), arg("B"), arg("roi")=ROI::All(), arg("nthreads")=0)) .def("mul", &IBA_mul_color, (arg("dst"), arg("A"), arg("B"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("mul") .def("div", &IBA_div_images, (arg("dst"), arg("A"), arg("B"), arg("roi")=ROI::All(), arg("nthreads")=0)) .def("div", &IBA_div_float, (arg("dst"), arg("A"), arg("B"), arg("roi")=ROI::All(), arg("nthreads")=0)) .def("div", &IBA_div_color, (arg("dst"), arg("A"), arg("B"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("div") .def("mad", &IBA_mad_images, (arg("dst"), arg("A"), arg("B"), arg("C"), arg("roi")=ROI::All(), arg("nthreads")=0)) .def("mad", &IBA_mad_float, (arg("dst"), arg("A"), arg("B"), arg("C"), arg("roi")=ROI::All(), arg("nthreads")=0)) .def("mad", &IBA_mad_color, (arg("dst"), arg("A"), arg("B"), arg("C"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("mad") .def("invert", &IBA_invert, (arg("dst"), arg("A"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("invert") .def("pow", &IBA_pow_float, (arg("dst"), arg("A"), arg("B"), arg("roi")=ROI::All(), arg("nthreads")=0)) .def("pow", &IBA_pow_color, (arg("dst"), arg("A"), arg("B"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("pow") .def("channel_sum", &IBA_channel_sum, (arg("dst"), arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .def("channel_sum", &IBA_channel_sum_weight, (arg("dst"), arg("src"), arg("weight"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("channel_sum") .def("rangecompress", &IBA_rangecompress, (arg("dst"), arg("src"), arg("useluma")=false, arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("rangecompress") .def("rangeexpand", &IBA_rangeexpand, (arg("dst"), arg("src"), arg("useluma")=false, arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("rangeexpand") .def("premult", &IBA_premult, (arg("dst"), arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("premult") .def("unpremult", &IBA_unpremult, (arg("dst"), arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("unpremult") .def("clamp", &IBA_clamp, (arg("dst"), arg("src"), arg("min")=tuple(), arg("max")=tuple(), arg("clampalpha01")=false, arg("roi")=ROI::All(), arg("nthreads")=0)) .def("clamp", &IBA_clamp_float, (arg("dst"), arg("src"), arg("min")=-std::numeric_limits::max(), arg("max")=std::numeric_limits::max(), arg("clampalpha01")=false, arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("clamp") .def("colorconvert", &IBA_colorconvert, (arg("dst"), arg("src"), arg("from"), arg("to"), arg("unpremult")=false, arg("roi")=ROI::All(), arg("nthreads")=0)) .def("colorconvert", &IBA_colorconvert_colorconfig, (arg("dst"), arg("src"), arg("from"), arg("to"), arg("unpremult")=false, arg("context_key")="", arg("context_value")="", arg("colorconfig")="", arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("colorconvert") .def("ociolook", &IBA_ociolook, (arg("dst"), arg("src"), arg("looks"), arg("from"), arg("to"), arg("unpremult")=false, arg("invert")=false, arg("context_key")="", arg("context_value")="", arg("roi")=ROI::All(), arg("nthreads")=0)) .def("ociolook", &IBA_ociolook_colorconfig, (arg("dst"), arg("src"), arg("looks"), arg("from"), arg("to"), arg("unpremult")=false, arg("invert")=false, arg("context_key")="", arg("context_value")="", arg("colorconfig")="", arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("ociolook") .def("ociodisplay", &IBA_ociodisplay, (arg("dst"), arg("src"), arg("display"), arg("view"), arg("from")=object(), arg("looks")=object(), arg("unpremult")=false, arg("context_key")="", arg("context_value")="", arg("roi")=ROI::All(), arg("nthreads")=0)) .def("ociodisplay", &IBA_ociodisplay_colorconfig, (arg("dst"), arg("src"), arg("display"), arg("view"), arg("from")=object(), arg("looks")=object(), arg("unpremult")=false, arg("context_key")="", arg("context_value")="", arg("colorconfig")="", arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("ociodisplay") .def("ociofiletransform", &IBA_ociofiletransform, (arg("dst"), arg("src"), arg("name"), arg("unpremult")=false, arg("invert")=false, arg("roi")=ROI::All(), arg("nthreads")=0)) .def("ociofiletransform", &IBA_ociofiletransform_colorconfig, (arg("dst"), arg("src"), arg("name"), arg("unpremult")=false, arg("invert")=false, arg("colorconfig")="", arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("ociofiletransform") .def("computePixelStats", &IBA_computePixelStats, (arg("src"), arg("stats"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("computePixelStats") .def("compare", &IBA_compare, (arg("A"), arg("B"), arg("failthresh"), arg("warnthresh"), arg("result"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("compare") .def("compare_Yee", &IBA_compare_Yee, (arg("A"), arg("B"), arg("result"), arg("luminance")=100, arg("fov")=45, arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("compare_Yee") .def("isConstantColor", &IBA_isConstantColor, (arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("isConstantColor") .def("isConstantChannel", &IBA_isConstantChannel, (arg("src"), arg("channel"), arg("val"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("isConstantChannel") .def("isMonochrome", &IBA_isMonochrome, (arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("isMonochrome") // color_count, color_range_check .def("nonzero_region", &IBA_nonzero_region, (arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("nonzero_region") .def("computePixelHashSHA1", &IBA_computePixelHashSHA1, (arg("src"), arg("extrainfo")="", arg("roi")=ROI::All(), arg("blocksize")=0, arg("nthreads")=0)) .staticmethod("computePixelHashSHA1") .def("warp", &IBA_warp, (arg("dst"), arg("src"), arg("M"), arg("filtername")="", arg("filterwidth")=0.0f, arg("recompute_roi")=false, arg("wrap")=ImageBuf::WrapDefault, arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("warp") .def("rotate", &IBA_rotate, (arg("dst"), arg("src"), arg("angle"), arg("filtername")="", arg("filterwidth")=0.0f, arg("recompute_roi")=false, arg("roi")=ROI::All(), arg("nthreads")=0)) .def("rotate", &IBA_rotate2, (arg("dst"), arg("src"), arg("angle"), arg("center_x"), arg("center_y"), arg("filtername")="", arg("filterwidth")=0.0f, arg("recompute_roi")=false, arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("rotate") .def("resize", &IBA_resize, (arg("dst"), arg("src"), arg("filtername")="", arg("filterwidth")=0.0f, arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("resize") .def("resample", &IBA_resample, (arg("dst"), arg("src"), arg("interpolate")=true, arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("resample") .def("make_kernel", &IBA_make_kernel, (arg("dst"), arg("name"), arg("width"), arg("height"), arg("depth")=1.0f, arg("normalize")=true)) .staticmethod("make_kernel") .def("convolve", &IBA_convolve, (arg("dst"), arg("src"), arg("kernel"), arg("normalze")=true, arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("convolve") .def("unsharp_mask", &IBA_unsharp_mask, (arg("dst"), arg("src"), arg("kernel")="gaussian", arg("width")=3.0f, arg("contrast")=1.0f, arg("threshold")=0.0f, arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("unsharp_mask") .def("median_filter", &IBA_median_filter, (arg("dst"), arg("src"), arg("width")=3, arg("height")=-1, arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("median_filter") .def("dilate", &IBA_dilate, (arg("dst"), arg("src"), arg("width")=3, arg("height")=-1, arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("dilate") .def("erode", &IBA_erode, (arg("dst"), arg("src"), arg("width")=3, arg("height")=-1, arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("erode") .def("laplacian", &IBA_laplacian, (arg("dst"), arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("laplacian") .def("fft", &IBA_fft, (arg("dst"), arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("fft") .def("ifft", &IBA_ifft, (arg("dst"), arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("ifft") .def("polar_to_complex", &IBA_polar_to_complex, (arg("dst"), arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("polar_to_complex") .def("complex_to_polar", &IBA_complex_to_polar, (arg("dst"), arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("complex_to_polar") .def("fixNonFinite", &IBA_fixNonFinite, (arg("dst"), arg("src"), arg("mode")=ImageBufAlgo::NONFINITE_BOX3, arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("fixNonFinite") .def("fillholes_pushpull", &IBA_fillholes_pushpull, (arg("dst"), arg("src"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("fillholes_pushpull") .def("capture_image", &IBA_capture_image, (arg("dst"), arg("cameranum")=0, arg("convert")=TypeDesc::UNKNOWN)) .staticmethod("capture_image") .def("over", &IBA_over, (arg("dst"), arg("A"), arg("B"), arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("over") .def("zover", &IBA_zover, (arg("dst"), arg("A"), arg("B"), arg("z_zeroisinf")=false, arg("roi")=ROI::All(), arg("nthreads")=0)) .staticmethod("zover") .def("render_point", &IBA_render_point, (arg("dst"), arg("x"), arg("y"), arg("color")=tuple())) .staticmethod("render_point") .def("render_line", &IBA_render_line, (arg("dst"), arg("x1"), arg("y1"), arg("x2"), arg("y2"), arg("color")=tuple(), arg("skip_first_point")=false)) .staticmethod("render_line") .def("render_box", &IBA_render_box, (arg("dst"), arg("x1"), arg("y1"), arg("x2"), arg("y2"), arg("color")=tuple(), arg("fill")=false)) .staticmethod("render_box") .def("render_text", &IBA_render_text, (arg("dst"), arg("x"), arg("y"), arg("text"), arg("fontsize")=16, arg("fontname")="", arg("textcolor")=tuple())) .staticmethod("render_text") // histogram, histogram_draw, .def("make_texture", &IBA_make_texture_filename, (arg("mode"), arg("filename"), arg("outputfilename"), arg("config")=ImageSpec())) .def("make_texture", &IBA_make_texture_ib, (arg("mode"), arg("buf"), arg("outputfilename"), arg("config")=ImageSpec())) .staticmethod("make_texture") ; } } // namespace PyOpenImageIO openimageio-1.7.17~dfsg0.orig/src/python/istitch.py0000755000175000017500000003243013151711064020463 0ustar mfvmfv#!/usr/bin/env python # Parse the options the user provided from the command line # -i takes three arguments: x and y coordinates and filename; # -o takes one argument (the name of to file where the stitched image will be written) # -p takes one argument (path to the oiio plugin library) def option_parser(): parser = OptionParser() parser.add_option("-i", nargs=3, action="append", dest="arguments") parser.add_option("-o", dest="output_file") parser.add_option("-p", dest="plugin_path", help="OIIO plugin path") (options, args) = parser.parse_args() coordinates = [] images = [] for i in options.arguments: xy = (int(i[0]), int(i[1])) filepath = i[2] coordinates.append(xy) images.append(filepath) plugin_path = options.plugin_path if not plugin_path: plugin_path = "" filename = options.output_file return (coordinates, images, plugin_path, filename) # If the user didn't provide any arguments from the command line, or he ran # the script as a module from Python, an interactive mode will be used to # get the needed information. # The function stops when the user enters an empty path to the image (just hits enter) def user_prompt(): coordinates = [] images = [] filepath = raw_input("Enter the path to the image: ") while filepath != "": if not os.path.exists(filepath): return False xy = raw_input("Specify position of image inside the stitched image (x y): ") xy = (int(xy[0]), int(xy[2])) images.append(filepath) coordinates.append(xy) filepath = raw_input("Enter the path to the image: ") return (coordinates, images) # Check if the coordinates our user provided make sense. This means that there # can't be any "holes" in the provided coordinates. Examples of wrong coordinates: # 0 0, 2 0 (needs 1 0) # 0 0, 1 0, 0 1, 0 3 (needs 0 2) def validate_coordinates(images_info): coordinates = images_info[0] zipped = zip(*coordinates) xmax = max(zipped[0]) ymax = max(zipped[1]) for x in range(xmax): if list(zipped[0]).count(x) == 0: return False for y in range(ymax): if list(zipped[1]).count(y) == 0: return False return True # Open every image in the list of filepaths, return a list of opened # ImageInput instances. # TODO: A function which would close (or delete?) all the instances def open_images(filepaths): images = [] for i in filepaths: inp = o.ImageInput.create(i, path) spec = o.ImageSpec() if inp.open(i, spec): images.append(inp) else: return False return images # Takes a list consisting of two sublists: [0] is a list of coordinates (which # are stored as tuples), [1] is a list of opened ImageInput instances; and puts # them in a dictionary (coordinates as keys). The dictionary makes using coordinates # very easy, since grid((x,y)) returns a matching image. def convert_to_grid(images_data): grid = {} for i in range(len(images_data[0])): grid[images_data[0][i]] = images_data[1][i] return grid # Check if all the images which should form a row have the same heights. # Returns False (if they don't) or the width of the row they would form. # This isn't used in this example, but might be useful. # TODO: check_constraints() actually does this, since it can check both a single row # and the whole grid. Is there any reason to keep check_row()? # def check_row(images): spec_0 = images[0].spec() row_width = spec_0.width for i in images[1:]: spec_i = i.spec() if spec_i.height != spec_0.height: return False row_width += spec_i.width return row_width # It uses check_row() to check if the whole grid can be merged (images in a row # must have matching heights, rows should have matching widths) # TODO: Also not used anywhere. It uses multiple calls to check_row() to achieve # what the more powerful check_constraints() can do by itself. Delete it? def can_stitch(images_table): width = check_row(images_table[0]) if width: for row in images_table[1:]: row_width = check_row(row) if row_width != width: return False return True else: return False # stitch the images which make a row to each other. # it takes a list of opened ImageInput instances def stitch_row(images): arr = array.array("B") row_width = check_row(images) if row_width: spec_0 = images[0].spec() for row in range(spec_0.height): for i in images: spec_i = i.spec() arr_i = array.array("B", "\0" * spec_i.scanline_bytes()) if i.read_scanline(row, 0, spec_i.format, arr_i): arr += arr_i else: return False return arr else: return False # it takes a list of rows. Each row is actually an array, so the joining # is trivial. def join_rows(rows): arr = array.array("B") for i in rows: arr += i return arr # This function takes a dictionary of opened # ImageInputs and checks can they be stitched together according to the # coordinates provided (as dictionary keys). def check_constraints(images): # Extract the number of rows and the number of images in the row with # the most images. Rows can have different number of images as long # as their widths add up to the same number. # Add +1 to both so we can properly use range() in for loops which iterate # over rows and columns. coordinates = images.keys() zipped = zip(*coordinates) n_columns_max = int(max(zipped[0])) + 1 n_rows = int(max(zipped[1])) + 1 # this will be the height of the final, stitched image height = 0 for y in range(n_rows): row_height = images[(0,y)].spec().height row_width = 0 for x in range(n_columns_max): if row_height == images[(x,y)].spec().height: row_width += images[(x,y)].spec().width else: return False # if the current row has less images than n_columns_max, # break the inner for loop when you reach the end if not images.get((x+1,y)): break height += row_height if y == 0: base_width = row_width else: if base_width != row_width: return False return (row_width, height) # Given a dictionary of opened ImageInput instances, # the function stitches them and returns a single # array.array which can then be written to a file. def stitch_images(images, name): # Let us first call check_constraints() in case the user forgot # to do so. Also, we'll get the resolution of the image. xy = check_constraints(images) if not xy: return False # Extract the number of rows and the number of images in the row with # the most images. Rows can have different number of images as long # as their widths add up to the same number. coordinates = images.keys() zipped = zip(*coordinates) n_columns_max = max(zipped[0]) + 1 n_rows = max(zipped[1]) + 1 # Form a row from the images with the same y coordinate. # Stitch the images in a row by calling stitch_row(row), which returns # an array representing the data of what is now a single image. # rows = [] for y in range(n_rows): row = [] for x in range(n_columns_max): row.append(images[(x,y)]) if images.get((x+1,y)) == None: break stitched_row = stitch_row(row) rows.append(stitched_row) data = join_rows(rows) if data: desc = images[(0,0)].spec().format spec = o.ImageSpec(xy[0], xy[1], images[(0,0)].spec().nchannels, desc) out = o.ImageOutput.create(name, path) out.open(name, spec, False) out.write_image(desc, data) out.close() else: return False ################################### # TODO: A function which would check if the user gave the proper xy coordinates, # i.e. an image at 1 1 is not possible without images at 0 0 and 1 0. Be careful # to give proper coordinates for now (though the order in which the images are # given does not matter). # TODO: Currently, the coordinates should be entered as "x y", without the "". # So, 0 0 works for now, and inputs like (0,0) or "0, 0" (with the "") will # be supported. # main() if __name__ == "__main__": import OpenImageIO as o import array import os import sys from optparse import OptionParser if len(sys.argv) > 1: parsed = option_parser() filename = parsed[3] path = parsed[2] images_info = (parsed[0], parsed[1]) else: filename = raw_input("Enter the desired name of the final stitched image: ") path = raw_input("Enter the path to the oiio plugin dir: ") images_info = user_prompt() if not validate_coordinates(images_info): print "Coordinates not valid" else: ii_instances = open_images(images_info[1]) if not ii_instances: print "Can't open given images" else: images_info = (images_info[0], ii_instances) grid = convert_to_grid(images_info) # the main part of the stitcher. Check if the images can be stitched, and # stitch them if so. print "Checking whether the images can be merged..." if check_constraints(grid): print "Check ok, merging images..." stitch_images(grid, filename) print "The merging is complete." else: print "Can't stitch the images" ################################### # this was for testing only def stitch_halves(left, right): spec_left = o.ImageSpec() spec_right = o.ImageSpec() inp_left = o.ImageInput.create("jpg", "/home/dgaletic/code/oiio-trunk/dist/linux/lib") inp_right = o.ImageInput.create("jpg", "/home/dgaletic/code/oiio-trunk/dist/linux/lib") inp_left.open(left, spec_left) inp_right.open(right, spec_right) if spec_right.height == spec_left.height: xres = spec_left.width + spec_right.width yres = spec_left.height desc = spec_left.format spec_new = o.ImageSpec(xres, yres, spec_left.nchannels, desc) out = o.ImageOutput.create("jpg", "/home/dgaletic/code/oiio-trunk/dist/linux/lib") out.open("test-join.jpg", spec_new, False) for scanline in range(0, spec_left.height): arr_l = array.array("B", "\0" * spec_left.scanline_bytes()) arr_r = array.array("B", "\0" * spec_right.scanline_bytes()) arr_new = array.array("B") inp_left.read_scanline(scanline, 0, spec_left.format, arr_l) inp_right.read_scanline(scanline, 0, spec_right.format, arr_r) arr_new = arr_l + arr_r out.write_scanline(scanline, 0, spec_new.format, arr_new) out.close() else: print "Heights do not match" # stitches together an image split into three parts # this was also for testing only def test_stitch_thirds(): pic_third1 = "/home/dgaletic/code/oiio-testimages/stitch_me/tahoe-0.jpg" pic_third2 = "/home/dgaletic/code/oiio-testimages/stitch_me/tahoe-1.jpg" pic_third3 = "/home/dgaletic/code/oiio-testimages/stitch_me/tahoe-2.jpg" path = "/home/dgaletic/code/oiio-trunk/dist/linux/lib" spec1 = o.ImageSpec() spec2 = o.ImageSpec() spec3 = o.ImageSpec() pic1 = o.ImageInput.create("jpg", path) pic2 = o.ImageInput.create("jpg", path) pic3 = o.ImageInput.create("jpg", path) pic1.open(pic_third1, spec1) pic2.open(pic_third2, spec2) pic3.open(pic_third3, spec3) spec_stitched = o.ImageSpec(spec1.width + spec2.width + spec3.width, spec1.height, spec1.nchannels, spec1.format) # create a list of opened ImageInput instances images = [pic1, pic2, pic3] # stitch the images next to each other, return the resulting array arr = stitch_row(images) if arr: out = o.ImageOutput.create("jpg", path) out.open("/home/dgaletic/code/branch/src/python/thirds.jpg", spec_stitched, False) out.write_image(spec_stitched.format, arr) out.close() #return True #else: #return False """ inp00 = o.ImageInput.create("jpg", path) spec00 = o.ImageSpec() inp00.open("/home/dgaletic/code/oiio-testimages/stitch_me/tahoe00.jpg", spec00) inp10 = o.ImageInput.create("jpg", path) spec10 = o.ImageSpec() inp10.open("/home/dgaletic/code/oiio-testimages/stitch_me/tahoe10.jpg", spec10) inp20 = o.ImageInput.create("jpg", path) spec20 = o.ImageSpec() inp20.open("/home/dgaletic/code/oiio-testimages/stitch_me/tahoe20.jpg", spec20) inp01 = o.ImageInput.create("jpg", path) spec01 = o.ImageSpec() inp01.open("/home/dgaletic/code/oiio-testimages/stitch_me/tahoe01.jpg", spec01) inp11 = o.ImageInput.create("jpg", path) spec11 = o.ImageSpec() inp11.open("/home/dgaletic/code/oiio-testimages/stitch_me/tahoe11.jpg", spec11) inp02 = o.ImageInput.create("jpg", path) spec02 = o.ImageSpec() inp02.open("/home/dgaletic/code/oiio-testimages/stitch_me/tahoe02.jpg", spec02) images = {(0,0):inp00, (1,0):inp10, (2,0):inp20, (0,1):inp01, (1,1):inp11, (0,2):inp02} """ openimageio-1.7.17~dfsg0.orig/src/python/icrypt.py0000644000175000017500000000646113151711064020330 0ustar mfvmfvdef crypt(): import OpenImageIO as o import array import random import pickle path = raw_input("Enter OIIO plugin path: ") in_file = raw_input("Enter the name of the file for encryption: ") out_file = raw_input("Enter the name of the resulting file: ") key_path = raw_input("Enter the name of the file in which to store the key: ") # open the input file spec = o.ImageSpec() pic = o.ImageInput.create(in_file, path) pic.open(in_file, spec) desc = spec.format # Create a couple of arrays where we'll store the data. # They all need to be the same size, and we'll fill them with dummy data for now. # We'll read the original pixel values in arr arr = array.array("B", "\0" * spec.image_bytes()) # the values we'll write to the encrypted file new_values = arr[:] length = range(len(new_values)) print "Working, please wait..." pic.read_image(desc, arr) # save the state of the random number generator so we can use it # to decode the image state = random.getstate() # generate random values, add them to the original values. # Do % 256 so nothing overflows for i in length: rand_val = random.randint(0, 255) new_values[i] = (arr[i] + rand_val) % 256 # write new values to the output file, close everything out = o.ImageOutput.create(out_file, path) out.open(out_file, spec, False) out.write_image(desc, new_values) out.close() # save the state of the RNG - that's the key for decryption f = open(key_path, "w") pickle.dump(state, f) f.close() return True def decrypt(): import OpenImageIO as o import array import pickle import random path = raw_input("Enter OIIO plugin path: ") key_path = raw_input("Name of the file with the key: ") in_file = raw_input("Name of the encrypted file: ") # Open the input files, read the RNG state and store it in "key" f = open(key_path, "r") key = pickle.load(f) spec_cr = o.ImageSpec() pic_cr = o.ImageInput.create(in_file, path) pic_cr.open(in_file, spec_cr) desc_cr = spec_cr.format # The encrypted pixel values will be stored here. # The decoding will be done inplace, so that will also be # the output buffer once the decryption is done. arr_cr = array.array("B", "\0" * spec_cr.image_bytes()) length = range(len(arr_cr)) print "Working, please wait..." # Let's read the encrypted image pic_cr.read_image(desc_cr, arr_cr) # Set the state of the RNG to match the state of the RNG which coded # the image. After this, we can generate the same random sequence # used to code the image. random.setstate(key) # Decryption! for i in length: rand_val = random.randint(0, 255) # This is the inverse of the encryption. restored_pixel = arr_cr[i] - rand_val if restored_pixel < 0: arr_dec[i] = 256 + restored_pixel else: arr_dec[i] = restored_pixel print "Decryption completed!" image = raw_input("Enter the name under which to store the result: ") print "Working, please wait..." out_dec = o.ImageOutput.create(image, path) out_dec.open(image, spec_cr, False) out_dec.write_image(desc_cr, arr_dec) out_dec.close() return True openimageio-1.7.17~dfsg0.orig/src/python/py_imageoutput.cpp0000644000175000017500000004042013151711064022214 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ // Avoid a compiler warning from a duplication in tiffconf.h/pyconfig.h #undef SIZEOF_LONG #include #include "py_oiio.h" namespace PyOpenImageIO { using namespace boost::python; object ImageOutputWrap::create (const std::string &filename, const std::string& plugin_searchpath="") { ImageOutputWrap *iow = new ImageOutputWrap; iow->m_output = ImageOutput::create(filename, plugin_searchpath); if (iow->m_output == NULL) { delete iow; return object(handle<>(Py_None)); } else { return object(iow); } } ImageOutputWrap::~ImageOutputWrap() { delete m_output; } const ImageSpec& ImageOutputWrap::spec () const { return m_output->spec(); } bool ImageOutputWrap::open (const std::string &name, const ImageSpec &newspec, ImageOutput::OpenMode mode=ImageOutput::Create) { return m_output->open(name, newspec, mode); } bool ImageOutputWrap::open_specs (const std::string &name, tuple &specs) { const size_t length = len(specs); if (length == 0) return false; std::vector Cspecs (length); for (size_t i = 0; i < length; ++i) { extract s (specs[i]); if (! s.check()) { // Tuple item was not an ImageSpec return false; } Cspecs[i] = s(); } return m_output->open (name, int(length), &Cspecs[0]); } bool ImageOutputWrap::close() { return m_output->close(); } // this function creates a read buffer from PyObject which will be used // for all write_ functions. const void * ImageOutputWrap::make_read_buffer (object &buffer, imagesize_t size) { const void *buf = NULL; Py_ssize_t len = 0; int success = PyObject_AsReadBuffer(buffer.ptr(), &buf, &len); if (success != 0 || imagesize_t(len) < size) { throw_error_already_set(); } return buf; } bool ImageOutputWrap::write_scanline_array (int y, int z, object &buffer) { TypeDesc format; size_t numelements = 0; const void *array = python_array_address (buffer, format, numelements); if (static_cast(numelements) < spec().width*spec().nchannels) { m_output->error ("write_scanline was not passed a long enough array"); return false; } if (! array) { return false; } ScopedGILRelease gil; return m_output->write_scanline (y, z, format, array); } // DEPRECATED (1.6) bool ImageOutputWrap::write_scanline (int y, int z, TypeDesc format, object &buffer, stride_t xstride) { bool native = (format == TypeDesc::UNKNOWN); imagesize_t size = native ? m_output->spec().scanline_bytes (native) : format.size() * m_output->spec().nchannels * m_output->spec().width; const void *array = make_read_buffer (buffer, size); ScopedGILRelease gil; return m_output->write_scanline(y, z, format, array, xstride); } // DEPRECATED (1.6) bool ImageOutputWrap::write_scanline_bt (int y, int z, TypeDesc::BASETYPE format, object &buffer, stride_t xstride) { return write_scanline (y, z, format, buffer, xstride); } bool ImageOutputWrap::write_scanlines_array (int ybegin, int yend, int z, object &buffer) { TypeDesc format; size_t numelements = 0; const void *array = python_array_address (buffer, format, numelements); if (static_cast(numelements) < spec().width*spec().nchannels*(yend-ybegin)) { m_output->error ("write_scanlines was not passed a long enough array"); return false; } if (! array) { return false; } ScopedGILRelease gil; return m_output->write_scanlines (ybegin, yend, z, format, array); } // DEPRECATED (1.6) bool ImageOutputWrap::write_scanlines (int ybegin, int yend, int z, TypeDesc format, object &buffer, stride_t xstride) { bool native = (format == TypeDesc::UNKNOWN); imagesize_t size = native ? m_output->spec().scanline_bytes (native) : format.size() * m_output->spec().nchannels * m_output->spec().width; const void *array = make_read_buffer (buffer, size); ScopedGILRelease gil; return m_output->write_scanlines(ybegin, yend, z, format, array, xstride); } // DEPRECATED (1.6) bool ImageOutputWrap::write_scanlines_bt (int ybegin, int yend, int z, TypeDesc::BASETYPE format, object &buffer, stride_t xstride) { return write_scanlines (ybegin, yend, z, format, buffer, xstride); } bool ImageOutputWrap::write_tile_array (int x, int y, int z, object &buffer) { TypeDesc format; size_t numelements = 0; const void *array = python_array_address (buffer, format, numelements); if (numelements < spec().tile_pixels()*spec().nchannels) { m_output->error ("write_tile was not passed a long enough array"); return false; } if (! array) { return false; } ScopedGILRelease gil; return m_output->write_tile (x, y, z, format, array); } // DEPRECATED (1.6) bool ImageOutputWrap::write_tile (int x, int y, int z, TypeDesc format, object &buffer, stride_t xstride, stride_t ystride, stride_t zstride) { bool native = (format == TypeDesc::UNKNOWN); imagesize_t size = native ? m_output->spec().tile_bytes (native) : format.size() * m_output->spec().nchannels * m_output->spec().tile_pixels(); const void *array = make_read_buffer(buffer, size); ScopedGILRelease gil; return m_output->write_tile(x, y, z, format, array, xstride, ystride, zstride); } // DEPRECATED (1.6) bool ImageOutputWrap::write_tile_bt (int x, int y, int z, TypeDesc::BASETYPE format, object &buffer, stride_t xstride, stride_t ystride, stride_t zstride) { return write_tile(x, y, z, format, buffer, xstride, ystride, zstride); } bool ImageOutputWrap::write_tiles_array (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, object &buffer) { TypeDesc format; size_t numelements = 0; const void *array = python_array_address (buffer, format, numelements); if (static_cast(numelements) < (xend-xbegin)*(yend-ybegin)*(zend-zbegin)*spec().nchannels) { m_output->error ("write_tiles was not passed a long enough array"); return false; } if (! array) { return false; } ScopedGILRelease gil; return m_output->write_tiles (xbegin, xend, ybegin, yend, zbegin, zend, format, array); } // DEPRECATED (1.6) bool ImageOutputWrap::write_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, object &buffer, stride_t xstride, stride_t ystride, stride_t zstride) { bool native = (format == TypeDesc::UNKNOWN); imagesize_t size = native ? m_output->spec().tile_bytes (native) : format.size() * m_output->spec().nchannels * m_output->spec().tile_pixels(); const void *array = make_read_buffer(buffer, size); ScopedGILRelease gil; return m_output->write_tiles (xbegin, xend, ybegin, yend, zbegin, zend, format, array, xstride, ystride, zstride); } // DEPRECATED (1.6) bool ImageOutputWrap::write_tiles_bt (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc::BASETYPE format, object &buffer, stride_t xstride, stride_t ystride, stride_t zstride) { return write_tiles (xbegin, xend, ybegin, yend, zbegin, zend, format, buffer, xstride, ystride, zstride); } bool ImageOutputWrap::write_image_array (object &buffer) { TypeDesc format; size_t numelements = 0; const void *array = python_array_address (buffer, format, numelements); if (numelements < spec().image_pixels()*spec().nchannels) { m_output->error ("write_image was not passed a long enough array"); return false; } if (! array) { return false; } ScopedGILRelease gil; return m_output->write_image (format, array); } // DEPRECATED (1.6) bool ImageOutputWrap::write_image (TypeDesc format, object &buffer, stride_t xstride, stride_t ystride, stride_t zstride) { bool native = (format == TypeDesc::UNKNOWN); imagesize_t size = native ? m_output->spec().image_bytes (native) : format.size() * m_output->spec().nchannels * m_output->spec().image_pixels(); const void *array = make_read_buffer (buffer, size); ScopedGILRelease gil; if (array) return m_output->write_image (format, array, xstride, ystride, zstride); return false; } // DEPRECATED (1.6) bool ImageOutputWrap::write_image_bt (TypeDesc::BASETYPE format, object &data, stride_t xstride, stride_t ystride, stride_t zstride) { return write_image (format, data, xstride, ystride, zstride); } BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(ImageOutputWrap_write_image_overloads, write_image, 2, 5) BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(ImageOutputWrap_write_image_bt_overloads, write_image_bt, 2, 5) BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(ImageOutputWrap_write_scanline_overloads, write_scanline, 4, 5) BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(ImageOutputWrap_write_scanline_bt_overloads, write_scanline_bt, 4, 5) BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(ImageOutputWrap_write_scanlines_overloads, write_scanlines, 5, 6) BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(ImageOutputWrap_write_scanlines_bt_overloads, write_scanlines_bt, 5, 6) BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(ImageOutputWrap_write_tile_overloads, write_tile, 5, 8) BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(ImageOutputWrap_write_tile_bt_overloads, write_tile_bt, 5, 8) BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(ImageOutputWrap_write_tiles_overloads, write_tiles, 8, 11) BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(ImageOutputWrap_write_tiles_bt_overloads, write_tiles_bt, 8, 11) bool ImageOutputWrap::write_deep_scanlines (int ybegin, int yend, int z, const DeepData &deepdata) { ScopedGILRelease gil; return m_output->write_deep_scanlines (ybegin, yend, z, deepdata); } bool ImageOutputWrap::write_deep_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, const DeepData &deepdata) { ScopedGILRelease gil; return m_output->write_deep_tiles (xbegin, xend, ybegin, yend, zbegin, zend, deepdata); } bool ImageOutputWrap::write_deep_image (const DeepData &deepdata) { ScopedGILRelease gil; return m_output->write_deep_image (deepdata); } bool ImageOutputWrap::copy_image (ImageInputWrap *iiw) { return m_output->copy_image(iiw->m_input); } const char* ImageOutputWrap::format_name (void) const { return m_output->format_name(); } int ImageOutputWrap::supports (const std::string &feature) const { return m_output->supports(feature); } std::string ImageOutputWrap::geterror()const { return m_output->geterror(); } void declare_imageoutput() { class_("ImageOutput", no_init) .def("create", &ImageOutputWrap::create, (arg("filename"), arg("plugin_searchpath")="")) .staticmethod("create") .def("format_name", &ImageOutputWrap::format_name) .def("supports", &ImageOutputWrap::supports) .def("spec", &ImageOutputWrap::spec, return_value_policy()) .def("open", &ImageOutputWrap::open) .def("open", &ImageOutputWrap::open_specs) .def("close", &ImageOutputWrap::close) .def("write_image", &ImageOutputWrap::write_image, ImageOutputWrap_write_image_overloads()) .def("write_image", &ImageOutputWrap::write_image_bt, ImageOutputWrap_write_image_bt_overloads()) .def("write_image", &ImageOutputWrap::write_image_array) .def("write_scanline", &ImageOutputWrap::write_scanline, ImageOutputWrap_write_scanline_overloads()) .def("write_scanline", &ImageOutputWrap::write_scanline_bt, ImageOutputWrap_write_scanline_bt_overloads()) .def("write_scanline", &ImageOutputWrap::write_scanline_array) .def("write_scanlines", &ImageOutputWrap::write_scanlines, ImageOutputWrap_write_scanlines_overloads()) .def("write_scanlines", &ImageOutputWrap::write_scanlines_bt, ImageOutputWrap_write_scanlines_bt_overloads()) .def("write_scanlines", &ImageOutputWrap::write_scanlines_array) .def("write_tile", &ImageOutputWrap::write_tile, ImageOutputWrap_write_tile_overloads()) .def("write_tile", &ImageOutputWrap::write_tile_bt, ImageOutputWrap_write_tile_bt_overloads()) .def("write_tile", &ImageOutputWrap::write_tile_array) .def("write_tiles", &ImageOutputWrap::write_tiles, ImageOutputWrap_write_tiles_overloads()) .def("write_tiles", &ImageOutputWrap::write_tiles_bt, ImageOutputWrap_write_tiles_bt_overloads()) .def("write_tiles", &ImageOutputWrap::write_tiles_array) .def("write_deep_scanlines", &ImageOutputWrap::write_deep_scanlines) .def("write_deep_tiles", &ImageOutputWrap::write_deep_tiles) .def("write_deep_image", &ImageOutputWrap::write_deep_image) // FIXME - write_deep_{image,scanlines,tiles} .def("copy_image", &ImageOutputWrap::copy_image) .def("geterror", &ImageOutputWrap::geterror) ; enum_("ImageOutputOpenMode") .value("Create", ImageOutput::Create ) .value("AppendSubimage", ImageOutput::AppendSubimage) .value("AppendMIPLevel", ImageOutput::AppendMIPLevel) .export_values(); } } // namespace PyOpenImageIO openimageio-1.7.17~dfsg0.orig/src/python/unit_tests_imagecache.py0000644000175000017500000002737213151711064023351 0ustar mfvmfv# unit tests for ImageCache def ic_create_test(): print "Starting ImageCache.create() tests..." print "Every other test requires these tests to pass." # test 1 try: cache = None cache = oiio.ImageCache.create() if cache != None: print "Test 1 passed" else: print "Test 1 failed" except: print "Test 1 failed" # test 2 try: cache = None cache = oiio.ImageCache.create(False) if cache != None: print "Test 2 passed" else: print "Test 2 failed" except: print "Test 2 failed" # test 3 try: cache = None cache = oiio.ImageCache.create(True) if cache != None: print "Test 3 passed" else: print "Test 3 failed" except: print "Test 3 failed" # test 4 try: cache = None cache = oiio.ImageCache.create("not a proper argument") if cache != None: print "Test 4 passed" else: print "Test 4 failed" except: print "Test 4 failed" print def ic_clear_test(): # create an instance on which we can test # if ic_create_test() failed, this can't be done cache = oiio.ImageCache.create() print "Starting ImageCache.clear() tests..." # test 1 try: cache.clear("not a proper argument") print "Test 1 failed" except: print "Test 1 passed" # test 2 try: cache.clear() print "Test 2 passed" except: print "Test 2 failed" print def ic_attribute_test(): # create an instance on which we can test # if ic_create_test() failed, this can't be done cache = oiio.ImageCache.create() print "Starting ImageCache.attribute() tests..." # test 1 try: cache.attribute("max_open_files", 2) print "Test 1 passed" except: print "Test 1 failed" # test 2 try: cache.attribute("max_memory_MB", 3.14) print "Test 2 passed" except: print "Test 2 failed" # test 3 try: cache.attribute("searchpath", "") print "Test 3 passed" except: print "Test 3 failed" # test 4 try: att_name = "a_string_attribute" att_value = "should return false" cache.attribute(att_name, att_value) print "Test 4 passed" except: print "Test 4 failed" print def ic_getattribute_test(): # create an instance on which we can test # if ic_create_test() failed, this can't be done cache = oiio.ImageCache.create() print "Starting ImageCache.getattribute() tests..." # test 1 try: val = None cache.getattribute("max_open_tiles", val) print "Test 1 passed" except: print "Test 1 failed" # test 2 try: val = None cache.getattribute("max_memory_mb", val) print "Test 2 passed" except: print "Test 2 failed" # test 3 try: val = None cache.getattribute("searchpath", val) print "Test 3 passed" except: print "Test 3 failed" # test 4 try: val = None cache.getattribute("autotile", val) print "Test 4 passed" except: print "Test 4 failed" # test 5 try: val = None cache.getattribute("automip", val) print "Test 5 passed" except: print "Test 5 failed" # test 6 try: val = None cache.getattribute("some_attribute", val) print "Test 6 passed" except: print "Test 6 failed" print def ic_resolve_filename_test(): # create an instance on which we can test # if ic_create_test() failed, this can't be done cache = oiio.ImageCache.create() print "Starting ImageCache.resolve_filename() tests..." # test 1 pic_path = "../../../oiio-images/tahoe-gps.jpg" try: cache.resolve_filename(pic_path) print "Test 1 passed" except: print "Test 1 failed" # test 2 try: cache.resolve_filename("nonexisting_pic.jpg") print "Test 2 passed" except: print "Test 2 failed" print def ic_get_image_info_test(): # create an instance on which we can test # if ic_create_test() failed, this can't be done cache = oiio.ImageCache.create() print "Starting ImageCache.get_image_info() tests..." print "Needs more tests!" desc = oiio.TypeDesc() # test 1 data = None # not sure what filename and dataname are supposed so we test # only if get_image_info() returns False when given dummy data # TODO: expand the tests with proper data, so we can see when # get_image_info() actually does what it's supposed to. filename = "filename" dataname = "dataname" try: cache.get_image_info(filename, dataname, desc, data) print "Test 1 passed" except: print "Test 1 failed" # test 2 print def ic_get_imagespec_test(): # create an instance on which we can test # if ic_create_test() failed, this can't be done cache = oiio.ImageCache.create() print "Starting ImageCache.get_imagespec() tests..." filename = "filename" spec = oiio.ImageSpec() # test 1 try: cache.get_imagespec(filename, spec, 0) print "Test 1 passed" except: print "Test 1 failed" # test 2 try: # third argument is 0 by default cache.get_imagespec(filename, spec) print "Test 2 passed" except: print "Test 2 failed" # test 3 try: cache.get_imagespec(filename, spec, 2) print "Test 3 passed" except: print "Test 3 failed" print def ic_get_pixels_test(): # create an instance on which we can test # if ic_create_test() failed, this can't be done cache = oiio.ImageCache.create() print "Starting ImageCache.get_pixels() tests..." filename = "filename" desc = oiio.TypeDesc() subimage = 0 xbegin = ybegin = 0 xend = yend = 1 zbegin = zend = 0 # test 1 try: # this array will have to be modified to be an appropriate # buffer once it's determined how the buffers are to be passed # from Python result = array.array("B") cache.get_pixels(filename, subimage, xbegin, xend, ybegin, yend, \ zbegin, zend, desc, result) print "Test 1 passed" except: print "Test 1 failed" # test 2 - pass an impossible pixel location as xbegin (-1) # Not sure if get_pixels() is supposed to return False or raise # an exception if given an impossible xbegin (or any other arg). # Currently, I supposed it should return False. try: # this array will have to be modified to be an appropriate # buffer once it's determined how the buffers are to be passed # from Python result = array.array("B") cache.get_pixels(filename, subimage, -1, xend, ybegin, yend, \ zbegin, zend, desc, result) print "Test 2 passed (check the comments!)" except: print "Test 2 failed (check the comments!)" print def ic_get_tile_test(): # create an instance on which we can test # if ic_create_test() failed, this can't be done cache = oiio.ImageCache.create() print "Starting ImageCache.get_tile() tests..." print "These tests are required to pass before we can test release_tile() and tile_pixels()" filename = "filename" subimage = 0 x = y = z = 0 # test 1 try: tile = None tile = cache.get_tile(filename, subimage, x, y, z) if tile != None: print "Test 1 passed" else: print "Test 1 failed" except: print "Test 1 failed" # test 2 - pass an impossible value for x (-1) # same issue as with get_pixels_test() #1 try: tile = None tile = cache.get_tile(filename, subimage, -1, y, z) if tile != None: print "Test 2 passed (check the comments!)" else: print "Test 2 failed (check the comments!)" except: print "Test 2 failed (check the comments!)" print def ic_release_tile_test(): # create an instance on which we can test # if ic_create_test() failed, this can't be done cache = oiio.ImageCache.create() # run get_tile() so we have something to release filename = "filename" subimage = 0 x = y = z = 0 tile = cache.get_tile(filename, subimage, x, y, z) print "Starting ImageCache.get_tile() tests..." # test 1 try: cache.release_tile(tile) print "Test 1 passed" except: print "Test 1 failed" print def ic_tile_pixels_test(): # create an instance on which we can test # if ic_create_test() failed, this can't be done cache = oiio.ImageCache.create() # run get_tile() so we have something to fetch filename = "filename" subimage = 0 x = y = z = 0 tile = cache.get_tile(filename, subimage, x, y, z) # the format of fetched pixels, currently set to "Unknown" desc = TypeDesc() # test 1 try: pixels = None pixels = cache.tile_pixels(tile, desc) if pixels != None: print "Test 1 passed" else: print "Test 1 failed (nothing returned)" # OTH, the data given is very likely to be wrong so that # can be the reason for failing except: print "Test 1 failed (exception raised)" print def ic_geterror_test(): # create an instance on which we can test # if ic_create_test() failed, this can't be done cache = oiio.ImageCache.create() # test 1 try: error_message = None error_message = cache.geterror() if len(error_message) >= 0: print "Test 1 passed" except: print "Test 1 failed" print def ic_getstats_test(): # create an instance on which we can test # if ic_create_test() failed, this can't be done cache = oiio.ImageCache.create() # test 1 try: stats = None stats = cache.getstats() #default arg is 1 if type(stats) == str: print "Test 1 passed" else: print "Test 1 failed (nothing returned)" except: print "Test 1 failed (exception raised)" print def ic_invalidate_test(): # create an instance on which we can test # if ic_create_test() failed, this can't be done cache = oiio.ImageCache.create() filename = "filename" # test 1 try: cache.invalidate(filename) print "Test 1 passed" except: print "Test 1 failed" print def ic_invalidate_all_test(): # create an instance on which we can test # if ic_create_test() failed, this can't be done cache = oiio.ImageCache.create() # test 1 try: cache.invalidate_all() # default arg is force=False print "Test 1 passed" except: print "Test 1 failed" # test 2 try: cache.invalidate_all(True) print "Test 2 passed" except: print "Test 2 failed" print ### def run_tests(): ic_create_test() ic_clear_test() ic_attribute_test() ic_getattribute_test() ic_resolve_filename_test() ic_get_image_info_test() ic_get_imagespec_test() ic_get_pixels_test() ic_get_tile_test() ic_release_tile_test() ic_tile_pixels_test() ic_geterror_test() ic_getstats_test() ic_invalidate_test() ic_invalidate_all_test() ### import OpenImageIO as oiio import array plugin_path = raw_input("Enter the oiio plugin search path (probably /dist/ARCH/lib) :\n ") run_tests() openimageio-1.7.17~dfsg0.orig/src/python/py_imagespec.cpp0000644000175000017500000003332613151711064021615 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "py_oiio.h" namespace PyOpenImageIO { using namespace boost::python; // Accessor for channelnames, converts a vector to a tuple static object ImageSpec_get_channelnames(const ImageSpec& spec) { size_t size = spec.channelnames.size(); PyObject* result = PyTuple_New(size); for (size_t i = 0; i < size; ++i) { #if PY_MAJOR_VERSION >= 3 PyObject* name = PyUnicode_FromString(spec.channelnames[i].c_str()); #else PyObject* name = PyString_FromString(spec.channelnames[i].c_str()); #endif PyTuple_SetItem(result, i, name); } return object(handle<>(result)); } // Mutator for channelnames, sets a vector using a tuple static void ImageSpec_set_channelnames(ImageSpec& spec, const tuple& channelnames) { const size_t length = len(channelnames); spec.channelnames.resize(length); for (size_t i = 0; i < length; ++i) { extract e (channelnames[i]); spec.channelnames[i] = e.check() ? e() : std::string(); } } // Accessor for channelformats, converts a vector to a tuple // of ints holding the BASETYPE. static object ImageSpec_get_channelformats(const ImageSpec& spec) { size_t size = spec.channelformats.size(); PyObject* result = PyTuple_New(size); for (size_t i = 0; i < size; ++i) #if PY_MAJOR_VERSION >= 3 PyTuple_SetItem(result, i, PyLong_FromLong((long)spec.channelformats[i].basetype)); #else PyTuple_SetItem(result, i, PyInt_FromLong((long)spec.channelformats[i].basetype)); #endif return object(handle<>(result)); } // Mutator for channelformats, initialized using a tuple whose int entries // give the BASETYPE values. static void ImageSpec_set_channelformats(ImageSpec& spec, const tuple& channelformats) { const size_t length = len(channelformats); spec.channelformats.resize(length, spec.format); for (size_t i = 0; i < length; ++i) { extract base (channelformats[i]); if (base.check()) { spec.channelformats[i] = (TypeDesc::BASETYPE)base(); continue; } extract type (channelformats[i]); if (type.check()) { spec.channelformats[i] = type(); continue; } } } #if 0 // In this version we lose some functionality - all the inputs are // assumed to have been autostride. static object ImageSpec_auto_stride_1(const TypeDesc& format, int nchannels, int width, int height) { stride_t x = AutoStride, y = AutoStride, z = AutoStride; ImageSpec::auto_stride(x, y, z, format, nchannels, width, height); return object(handle<>(Py_BuildValue("(iii)", x, y, z))); } // xstride is assumed to have been AutoStride. static stride_t ImageSpec_auto_stride_2(const TypeDesc& format, int nchannels) { stride_t x = AutoStride; ImageSpec::auto_stride(x, format, nchannels); return x; } #endif static stride_t ImageSpec_channel_bytes_1(ImageSpec& spec) { return spec.channel_bytes (); } static stride_t ImageSpec_channel_bytes_2(ImageSpec& spec, int chan) { return spec.channel_bytes (chan); } static stride_t ImageSpec_channel_bytes_3(ImageSpec& spec, int chan, bool native) { return spec.channel_bytes (chan, native); } static stride_t ImageSpec_pixel_bytes_0(ImageSpec& spec) { return spec.pixel_bytes (); } static stride_t ImageSpec_pixel_bytes_1(ImageSpec& spec, bool native) { return spec.pixel_bytes (native); } static stride_t ImageSpec_pixel_bytes_2(ImageSpec& spec, int chbegin, int chend) { return spec.pixel_bytes (chbegin, chend); } static stride_t ImageSpec_pixel_bytes_3(ImageSpec& spec, int chbegin, int chend, bool native) { return spec.pixel_bytes (chbegin, chend, native); } BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(ImageSpec_scanline_bytes_overloads, scanline_bytes, 0, 1) BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(ImageSpec_tile_bytes_overloads, tile_bytes, 0, 1) BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(ImageSpec_image_bytes_overloads, image_bytes, 0, 1) static void ImageSpec_set_format_2(ImageSpec& spec, TypeDesc::BASETYPE basetype) { spec.set_format (basetype); } static void ImageSpec_erase_attribute (ImageSpec& spec, const std::string &name, TypeDesc searchtype=TypeDesc::UNKNOWN, bool casesensitive=false) { spec.erase_attribute (name, searchtype, casesensitive); } static void ImageSpec_attribute_int (ImageSpec& spec, const std::string &name, int val) { spec.attribute (name, val); } static void ImageSpec_attribute_float (ImageSpec& spec, const std::string &name, float val) { spec.attribute (name, val); } static void ImageSpec_attribute_string (ImageSpec& spec, const std::string &name, const std::string &val) { spec.attribute (name, val); } static void ImageSpec_attribute_typed (ImageSpec& spec, const std::string &name, TypeDesc type, object &obj) { attribute_typed (spec, name, type, obj); } static void ImageSpec_attribute_tuple_typed (ImageSpec& spec, const std::string &name, TypeDesc type, tuple &obj) { attribute_tuple_typed (spec, name, type, obj); } static object ImageSpec_get_attribute_typed (const ImageSpec& spec, const std::string &name, TypeDesc type) { ImageIOParameter tmpparam; const ImageIOParameter *p = spec.find_attribute (name, tmpparam, type); if (!p) return object(); // None type = p->type(); if (type.basetype == TypeDesc::INT) { #if PY_MAJOR_VERSION >= 3 return C_to_val_or_tuple ((const int *)p->data(), type, PyLong_FromLong); #else return C_to_val_or_tuple ((const int *)p->data(), type, PyInt_FromLong); #endif } if (type.basetype == TypeDesc::FLOAT) { return C_to_val_or_tuple ((const float *)p->data(), type, PyFloat_FromDouble); } if (type.basetype == TypeDesc::STRING) { #if PY_MAJOR_VERSION >= 3 return C_to_val_or_tuple ((const char **)p->data(), type, PyUnicode_FromString); #else return C_to_val_or_tuple ((const char **)p->data(), type, PyString_FromString); #endif } return object(); } static object ImageSpec_get_attribute_untyped (const ImageSpec& spec, const std::string &name) { return ImageSpec_get_attribute_typed (spec, name, TypeDesc::UNKNOWN); } static int ImageSpec_get_int_attribute (const ImageSpec& spec, const char *name) { return spec.get_int_attribute (name); } static int ImageSpec_get_int_attribute_d (const ImageSpec& spec, const char *name, int defaultval) { return spec.get_int_attribute (name, defaultval); } static float ImageSpec_get_float_attribute (const ImageSpec& spec, const char *name) { return spec.get_float_attribute (name); } static float ImageSpec_get_float_attribute_d (const ImageSpec& spec, const char *name, float defaultval) { return spec.get_float_attribute (name, defaultval); } static std::string ImageSpec_get_string_attribute (const ImageSpec& spec, const char *name) { return spec.get_string_attribute (name); } static std::string ImageSpec_get_string_attribute_d (const ImageSpec& spec, const char *name, const char *defaultval) { return spec.get_string_attribute (name, defaultval); } void declare_imagespec() { class_("ImageSpec") .def_readwrite("x", &ImageSpec::x) .def_readwrite("y", &ImageSpec::y) .def_readwrite("z", &ImageSpec::z) .def_readwrite("width", &ImageSpec::width) .def_readwrite("height", &ImageSpec::height) .def_readwrite("depth", &ImageSpec::depth) .def_readwrite("full_x", &ImageSpec::full_x) .def_readwrite("full_y", &ImageSpec::full_y) .def_readwrite("full_z", &ImageSpec::full_z) .def_readwrite("full_width", &ImageSpec::full_width) .def_readwrite("full_height", &ImageSpec::full_height) .def_readwrite("full_depth", &ImageSpec::full_depth) .def_readwrite("tile_width", &ImageSpec::tile_width) .def_readwrite("tile_height", &ImageSpec::tile_height) .def_readwrite("tile_depth", &ImageSpec::tile_depth) .def_readwrite("format", &ImageSpec::format) //TypeDesc .def_readwrite("nchannels", &ImageSpec::nchannels) .add_property("channelnames", &ImageSpec_get_channelnames, &ImageSpec_set_channelnames) .add_property("channelformats", &ImageSpec_get_channelformats, &ImageSpec_set_channelformats) .def_readwrite("alpha_channel", &ImageSpec::alpha_channel) .def_readwrite("z_channel", &ImageSpec::z_channel) .def_readwrite("deep", &ImageSpec::deep) .add_property("extra_attribs", make_getter(&ImageSpec::extra_attribs))//ImageIOParameterList .def(init()) .def(init()) .def(init()) .def(init()) .def(init()) .def("set_format", &ImageSpec::set_format) .def("set_format", &ImageSpec_set_format_2) .def("default_channel_names", &ImageSpec::default_channel_names) .def("channel_bytes", &ImageSpec_channel_bytes_1) .def("channel_bytes", &ImageSpec_channel_bytes_2) .def("channel_bytes", &ImageSpec_channel_bytes_3) .def("pixel_bytes", &ImageSpec_pixel_bytes_0) .def("pixel_bytes", &ImageSpec_pixel_bytes_1) .def("pixel_bytes", &ImageSpec_pixel_bytes_2) .def("pixel_bytes", &ImageSpec_pixel_bytes_3) .def("scanline_bytes", &ImageSpec::scanline_bytes, ImageSpec_scanline_bytes_overloads(args("native"))) .def("tile_bytes", &ImageSpec::tile_bytes, ImageSpec_tile_bytes_overloads(args("native"))) .def("image_bytes", &ImageSpec::image_bytes, ImageSpec_image_bytes_overloads(args("native"))) .def("tile_pixels", &ImageSpec::tile_pixels) .def("image_pixels", &ImageSpec::image_pixels) .def("size_t_safe", &ImageSpec::size_t_safe) // For now, do not expose auto_stride. It's not obvious that // anybody will want to do pointer work and strides from Python. // // auto_stride is overloaded so needs explicit function casts // .def("auto_stride", &ImageSpec_auto_stride_1) // .def("auto_stride", &ImageSpec_auto_stride_2) // .staticmethod("auto_stride") .def("attribute", &ImageSpec_attribute_float) .def("attribute", &ImageSpec_attribute_int) .def("attribute", &ImageSpec_attribute_string) .def("attribute", &ImageSpec_attribute_typed) .def("attribute", &ImageSpec_attribute_tuple_typed) .def("get_int_attribute", &ImageSpec_get_int_attribute) .def("get_int_attribute", &ImageSpec_get_int_attribute_d) .def("get_float_attribute", &ImageSpec_get_float_attribute) .def("get_float_attribute", &ImageSpec_get_float_attribute_d) .def("get_string_attribute", &ImageSpec_get_string_attribute) .def("get_string_attribute", &ImageSpec_get_string_attribute_d) .def("getattribute", &ImageSpec_get_attribute_typed) .def("getattribute", &ImageSpec_get_attribute_untyped) .def("get_attribute", &ImageSpec_get_attribute_typed) // DEPRECATED(1.7) .def("get_attribute", &ImageSpec_get_attribute_untyped) // DEPRECATED(1.7) .def("erase_attribute", &ImageSpec_erase_attribute, (arg("name")="", arg("type")=TypeDesc(TypeDesc::UNKNOWN), arg("casesensitive")=false)) .def("metadata_val", &ImageSpec::metadata_val) .staticmethod("metadata_val") ; } } // namespace PyOpenImageIO openimageio-1.7.17~dfsg0.orig/src/python/py_imageinput.cpp0000644000175000017500000003517613151711064022027 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "py_oiio.h" namespace PyOpenImageIO { using namespace boost::python; object ImageInputWrap::open_static_regular (const std::string &filename) { ImageInputWrap* iiw = new ImageInputWrap; { ScopedGILRelease gil; iiw->m_input = ImageInput::open(filename); } if (iiw->m_input == NULL) { delete iiw; return object(handle<>(Py_None)); } else { return object(iiw); } } object ImageInputWrap::open_static_with_config (const std::string &filename, const ImageSpec &config) { ImageInputWrap* iiw = new ImageInputWrap; { ScopedGILRelease gil; iiw->m_input = ImageInput::open(filename, &config); } if (iiw->m_input == NULL) { delete iiw; return object(handle<>(Py_None)); } else { return object(iiw); } } object ImageInputWrap::create(const std::string &filename, const std::string &plugin_searchpath) { ImageInputWrap* iiw = new ImageInputWrap; { ScopedGILRelease gil; iiw->m_input = ImageInput::create(filename, plugin_searchpath); } if (iiw->m_input == NULL) { delete iiw; return object(handle<>(Py_None)); } else { return object(iiw); } } ImageInputWrap::~ImageInputWrap() { delete m_input; } const char* ImageInputWrap::format_name() const { return m_input->format_name(); } bool ImageInputWrap::valid_file (const std::string &filename) const { ScopedGILRelease gil; return m_input->valid_file (filename); } bool ImageInputWrap::open_regular (const std::string &name) { ScopedGILRelease gil; ImageSpec newspec; return m_input->open(name, newspec); } bool ImageInputWrap::open_with_config (const std::string &name, const ImageSpec &config) { ScopedGILRelease gil; ImageSpec newspec; return m_input->open(name, newspec, config); } const ImageSpec& ImageInputWrap::spec() const { return m_input->spec(); } int ImageInputWrap::supports (const std::string &feature) const { return m_input->supports (feature); } bool ImageInputWrap::close() { return m_input->close(); } int ImageInputWrap::current_subimage() const { return m_input->current_subimage(); } int ImageInputWrap::current_miplevel() const { return m_input->current_miplevel(); } bool ImageInputWrap::seek_subimage(int subimage, int miplevel) { ScopedGILRelease gil; ImageSpec dummyspec; return m_input->seek_subimage (subimage, miplevel, dummyspec); } // The read_image method is a bit different from the c++ interface. // "function" is a function which takes a float, and the // PyProgressCallback function is called automatically. object ImageInputWrap::read_image (int chbegin, int chend, TypeDesc format) { // Allocate our own temp buffer and try to read the image into it. // If the read fails, return None. const ImageSpec &spec = m_input->spec(); bool native = (format.basetype == TypeDesc::UNKNOWN); if (chend < 0) chend = spec.nchannels; chend = clamp (chend, chbegin+1, spec.nchannels); size_t nchans = size_t(chend - chbegin); size_t pixelsize = native ? spec.pixel_bytes(chbegin, chend, native) : size_t(nchans * format.size()); size_t size = size_t(spec.image_pixels()) * pixelsize; char *data = new char[size]; bool ok; { ScopedGILRelease gil; ok = m_input->read_image (chbegin, chend, format, data); } if (! ok) { delete [] data; // never mind return object(handle<>(Py_None)); } object array = C_array_to_Python_array (data, format, size); // clean up and return the array handle delete [] data; return array; } object ImageInputWrap_read_image_bt (ImageInputWrap& in, TypeDesc::BASETYPE format) { return in.read_image (0, -1, format); } object ImageInputWrap_read_image_bt_chans (ImageInputWrap& in, int chbegin, int chend, TypeDesc::BASETYPE format) { return in.read_image (chbegin, chend, format); } object ImageInputWrap_read_image_default (ImageInputWrap& in) { return in.read_image (0, -1, TypeDesc::FLOAT); } object ImageInputWrap_read_image_default_chans (ImageInputWrap& in, int chbegin, int chend) { return in.read_image (chbegin, chend, TypeDesc::FLOAT); } object ImageInputWrap::read_scanline (int y, int z, TypeDesc format) { const ImageSpec &spec = m_input->spec(); return read_scanlines (y, y+1, z, 0, spec.nchannels, format); } object ImageInputWrap_read_scanline_bt (ImageInputWrap& in, int y, int z, TypeDesc::BASETYPE format) { return in.read_scanline (y, z, format); } object ImageInputWrap_read_scanline_default (ImageInputWrap& in, int y, int z) { return in.read_scanline (y, z, TypeDesc::FLOAT); } object ImageInputWrap::read_scanlines (int ybegin, int yend, int z, int chbegin, int chend, TypeDesc format) { // Allocate our own temp buffer and try to read the scanline into it. // If the read fails, return None. ASSERT (m_input); const ImageSpec &spec = m_input->spec(); bool native = (format.basetype == TypeDesc::UNKNOWN); chend = clamp (chend, chbegin+1, spec.nchannels); size_t nchans = size_t(chend - chbegin); size_t pixelsize = native ? spec.pixel_bytes(chbegin, chend, native) : size_t(nchans * format.size()); size_t size = size_t((yend-ybegin) * spec.width) * pixelsize; char *data = new char[size]; bool ok; { ScopedGILRelease gil; ok = m_input->read_scanlines (ybegin, yend, z, chbegin, chend, format, data); } if (! ok) { delete [] data; // never mind return object(handle<>(Py_None)); } object array = C_array_to_Python_array (data, format, size); // clean up and return the array handle delete [] data; return array; } object ImageInputWrap_read_scanlines_bt (ImageInputWrap& in, int ybegin, int yend, int z, int chbegin, int chend, TypeDesc::BASETYPE format) { return in.read_scanlines (ybegin, yend, z, chbegin, chend, format); } object ImageInputWrap_read_scanlines_default (ImageInputWrap& in, int ybegin, int yend, int z, int chbegin, int chend) { return in.read_scanlines (ybegin, yend, z, chbegin, chend, TypeDesc::FLOAT); } object ImageInputWrap::read_tile (int x, int y, int z, TypeDesc format) { const ImageSpec &spec = m_input->spec(); return read_tiles (x, x+spec.tile_width, y, y+spec.tile_height, z, z+spec.tile_depth, 0, spec.nchannels, format); } object ImageInputWrap_read_tile_bt (ImageInputWrap& in, int x, int y, int z, TypeDesc::BASETYPE format) { return in.read_tile (x, y, z, format); } object ImageInputWrap_read_tile_default (ImageInputWrap& in, int x, int y, int z) { return in.read_tile (x, y, z, TypeDesc::FLOAT); } object ImageInputWrap::read_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc format) { // Allocate our own temp buffer and try to read the scanline into it. // If the read fails, return None. const ImageSpec &spec = m_input->spec(); bool native = (format.basetype == TypeDesc::UNKNOWN); if (chend < 0) chend = spec.nchannels; chend = clamp (chend, chbegin+1, spec.nchannels); size_t nchans = size_t(chend - chbegin); size_t pixelsize = native ? spec.pixel_bytes(chbegin, chend, native) : size_t(nchans * format.size()); size_t size = size_t((xend-xbegin) * (yend-ybegin) * (zend-zbegin)) * pixelsize; char *data = new char[size]; bool ok; { ScopedGILRelease gil; ok = m_input->read_tiles (xbegin, xend, ybegin, yend, zbegin, zend, chbegin, chend, format, data); } if (! ok) { delete [] data; // never mind return object(handle<>(Py_None)); } object array = C_array_to_Python_array (data, format, size); // clean up and return the array handle delete [] data; return array; } object ImageInputWrap_read_tiles_bt (ImageInputWrap& in, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc::BASETYPE format) { return in.read_tiles (xbegin, xend, ybegin, yend, zbegin, zend, chbegin, chend, format); } object ImageInputWrap_read_tiles_default (ImageInputWrap& in, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend) { return in.read_tiles (xbegin, xend, ybegin, yend, zbegin, zend, chbegin, chend, TypeDesc::FLOAT); } object ImageInputWrap::read_native_deep_scanlines (int ybegin, int yend, int z, int chbegin, int chend) { DeepData* dd = NULL; bool ok = true; { ScopedGILRelease gil; dd = new DeepData; ok = m_input->read_native_deep_scanlines (ybegin, yend, z, chbegin, chend, *dd); } if (ok) return object(dd); delete dd; return object(handle<>(Py_None)); } object ImageInputWrap::read_native_deep_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend) { DeepData* dd = NULL; bool ok = true; { ScopedGILRelease gil; dd = new DeepData; ok = m_input->read_native_deep_tiles (xbegin, xend, ybegin, yend, zbegin, zend, chbegin, chend, *dd); } if (ok) return object(dd); delete dd; return object(handle<>(Py_None)); } object ImageInputWrap::read_native_deep_image () { DeepData* dd = NULL; bool ok = true; { ScopedGILRelease gil; dd = new DeepData; ok = m_input->read_native_deep_image (*dd); } if (ok) return object(dd); delete dd; return object(handle<>(Py_None)); } std::string ImageInputWrap::geterror() const { return m_input->geterror(); } void declare_imageinput() { class_("ImageInput", no_init) .def("create", &ImageInputWrap::create, (arg("filename"), arg("plugin_searchpath")="")) .staticmethod("create") .def("open", &ImageInputWrap::open_static_regular, (arg("filename"))) .def("open", &ImageInputWrap::open_static_with_config, (arg("filename"))) .staticmethod("open") .def("format_name", &ImageInputWrap::format_name) .def("valid_file", &ImageInputWrap::valid_file) // .def("open", &ImageInputWrap::open_regular) // .def("open", &ImageInputWrap::open_with_config) .def("spec", &ImageInputWrap::spec, return_value_policy()) .def("supports", &ImageInputWrap::supports) .def("close", &ImageInputWrap::close) .def("current_subimage", &ImageInputWrap::current_subimage) .def("current_miplevel", &ImageInputWrap::current_miplevel) .def("seek_subimage", &ImageInputWrap::seek_subimage) .def("read_scanline", &ImageInputWrap::read_scanline) .def("read_scanline", &ImageInputWrap_read_scanline_bt) .def("read_scanline", &ImageInputWrap_read_scanline_default) .def("read_scanlines", &ImageInputWrap::read_scanlines) .def("read_scanlines", &ImageInputWrap_read_scanlines_bt) .def("read_scanlines", &ImageInputWrap_read_scanlines_default) .def("read_tile", &ImageInputWrap::read_tile) .def("read_tile", &ImageInputWrap_read_tile_bt) .def("read_tile", &ImageInputWrap_read_tile_default) .def("read_tiles", &ImageInputWrap::read_tiles) .def("read_tiles", &ImageInputWrap_read_tiles_bt) .def("read_tiles", &ImageInputWrap_read_tiles_default) .def("read_image", &ImageInputWrap::read_image) .def("read_image", &ImageInputWrap_read_image_bt) .def("read_image", &ImageInputWrap_read_image_bt_chans) .def("read_image", &ImageInputWrap_read_image_default) .def("read_image", &ImageInputWrap_read_image_default_chans) .def("read_native_deep_scanlines", &ImageInputWrap::read_native_deep_scanlines) .def("read_native_deep_tiles", &ImageInputWrap::read_native_deep_tiles) .def("read_native_deep_image", &ImageInputWrap::read_native_deep_image) .def("geterror", &ImageInputWrap::geterror) ; } } // namespace PyOpenImageIO openimageio-1.7.17~dfsg0.orig/src/python/iconvert.py0000755000175000017500000002225613151711064020652 0ustar mfvmfv#!/usr/bin/env python # Parse the options the user provided from the command line def option_parser(): parser = OptionParser() parser.add_option("-v", action="store_true", dest="verbose", default=False) parser.add_option("--inplace", action="store_true", dest="inplace", default=False) parser.add_option("-d", dest="data_format_name", default="") parser.add_option("--sRGB", action="store_true", dest="sRGB", default=False) parser.add_option("--tile", nargs=3, dest="tile") parser.add_option("--scanline", action="store_true", dest="scanline", default=False) parser.add_option("--separate", action="store_true", dest="separate", default=False) parser.add_option("--contig", action="store_true", dest="contig", default=False) parser.add_option("--compression", dest="compression") parser.add_option("--quality", type="int", dest="quality", default = -1) parser.add_option("--no-copy-image", action="store_true", dest="no_copy", default=False) parser.add_option("--adjust-time", action="store_true", dest="adjust_time", default=False) parser.add_option("--caption", dest="caption", default=None) parser.add_option("-k", "--keyword", action="append", dest="keywords") parser.add_option("--clear-keywords", action="store_true", default=False) parser.add_option("--attrib", nargs=2, action="append", dest="attribs") parser.add_option("--orientation", type="int", dest="orientation", default = 0) parser.add_option("--rotcw", action="store_true", dest="rotcw", default=False) parser.add_option("--rotccw", action="store_true", dest="rotccw", default=False) parser.add_option("--rot180", action="store_true", dest="rot180", default=False) parser.add_option("--plugin-path", dest="path", default="") # FIXME: I suppose there should be a way to enter input/output files without # having to specify an option, like "python iconvert.py -g 0.9 input.jpg output.jpg" # However, I could not find it in the docs so I've set it that the user has # to put -i and -o before input/output. parser.add_option("-i", action="append", dest="input_files", default=[]) parser.add_option("-o", action="append", dest="output_files", default=[]) (options, args) = parser.parse_args() if len(options.input_files) > len(options.output_files) and not options.inplace: print "Must have both an input and output filename specified" return False if len(options.input_files) == 0 and options.inplace: print "Must have at least one filename specified" return False if (int(options.rotcw) + int(options.rotccw) + int(options.rot180) + \ (options.orientation>0)) > 1: print "iconvert: more than one of --rotcw, --rotccw, --rot180, --orientation" return False if options.path == "": print "OIIO plugin path not provided, assuming \"\"" return parser.parse_args() def convert_files(in_file, out_file): nocopy = options.no_copy tempname = out_file # Check whether the conversion is inplace. if tempname == in_file: try: ext = out_file.rfind(".") tempname += ".tmp" + out_file[ext:] except: print "Error: Output file does not have an extension" # image input inp = oiio.ImageInput.create(in_file, options.path) if not inp: msg = "Could not crete ImageInput for " + in_file sys.exit(msg) inspec = oiio.ImageSpec() inp.open(in_file, inspec) # image output out = oiio.ImageOutput.create(tempname, options.path) if not out: msg = "Unable to create ImageOutput for " + out_file sys.exit(msg) # adjust spec outspec = inspec nocopy = adjust_spec(inp, inspec, outspec) out.open(tempname, outspec, oiio.ImageOutputOpenMode.Create) # convert if nocopy == False: ok = out.copy_image(inp) if not ok: print "Error" else: arr = array.array("B", "\0" * inspec.image_bytes()) ok = inp.read_image(outspec.format, arr) if not ok: print "Error reading" else: ok = out.write_image(outspec.format, arr) if not ok: print "Error writing" out.close() inp.close() # if the conversion was --inplace, this will result to True if out_file != tempname: if ok: # since it was inplace, in_file == out_file # so we need to replace the original file with tempfile os.remove(out_file) os.rename(tempname, out_file) else: os.remove(tempname) def adjust_spec(inp, inspec, outspec): nocopy = options.no_copy # the following line is from the original iconvert, but I'm not sure # it is needed. It's already outspec = inspec, right? #outspec.set_format(inspec.format) if options.data_format_name != "": if data_format_name == "uint8": outspec.set_format(oiio.BASETYPE.UINT8) elif data_format_name == "int8": outspec.set_format(oiio.BASETYPE.INT8) elif data_format_name == "uint16": outspec.set_format(oiio.BASETYPE.UINT16) elif data_format_name == "int16": outspec.set_format(oiio.BASETYPE.INT16) elif data_format_name == "half": outspec.set_format(oiio.BASETYPE.HALF) elif data_format_name == "float": outspec.set_format(oiio.BASETYPE.FLOAT) elif data_format_name == "double": outspec.set_format(oiio.BASETYPE.DOUBLE) if outspec.format != inspec.format: nocopy = True if options.sRGB: outspec.linearity = oiio.sRGB #ImageSpec.find_attribute() is not exposed to Python #if inp.format_name() != "jpeg" or outspec.find_attribute("Exif:ColorSpace"): #outspec.attribute("Exif:ColorSpace", 1) # handling tiles is not exposed to Python if options.tile: outspec.tile_width = options.tile[0] outspec.tile_height = options.tile[1] outspec.tile_depth = options.tile[2] if options.scanline: outspec.tile_width = 0 outspec.tile_height = 0 outspec.tile_depth = 0 if outspec.tile_width != inspec.tile_width or \ outspec.tile_height != inspec.tile_height or \ outspec.tile_depth != inspec.tile_depth: nocopy = True if options.compression: outspec.attribute("compression", options.compression) # 2nd argument should be exposed as default if options.compression != inspec.get_string_attribute("compression", ""): nocopy = True # FIXME: If quality is provided, the resultig image is larger than the # input image, and it is always the same no matter what quality (1-100). # (I suppose it uses the maximum possible value) # Should a --compression method be provided if --quality is used? if options.quality > 0: outspec.attribute("CompressionQuality", options.quality) # the 2nd argument should be exposed as default (in ImageSpec wrapper) # FIXME: The default arg is supposed to be 0, and get_int_attribute always # returns whatever is provided as the 2nd argument - 0 in this case. # I can't find out what's wrong in the binding. if options.quality != inspec.get_int_attribute("CompressionQuality", 0): nocopy = True if options.contig: outspec.attribute("planarconfig", "contig") if options.separate: outspec.attribute("planarconfig", "separate") if options.orientation >= 1: outspec.attribute("Orientation", options.orientation) else: orientation = outspec.get_int_attribute("Orientation", 1) if orientation >= 1 and orientation <= 8: cw = [0, 6, 7, 8, 5, 2, 3, 4, 1] if options.rotcw or options.rotccw or options.rot180: orientation = cw[orientation] if options.rotcw or options.rot180: orientation = cw[orientation] if options.rotccw: orientation = cw[orientation] outspec.attribute("Orientation", orientation) if options.caption != None: outspec.attribute("ImageDescription", options.caption) if options.clear_keywords == True: outspec.attribute("Keywords", "") # this looks a lot simpler than in c++ :) if options.keywords != None: oldkw = outspec.get_string_attribute("Keywords", "") newkw = oldkw for keyword in options.keywords: newkw += "; " + keyword outspec.attribute("Keywords", newkw) if options.attribs: for i in options.attribs: outspec.attribute(i[0], i[1]) return nocopy # main import OpenImageIO as oiio import array from optparse import OptionParser import os import sys (options, args) = option_parser() if options.inplace: for image in options.input_files: if convert_files(image, image) == False: sys.exit("Conversion failed") else: for i in range(len(options.input_files)): if convert_files(options.input_files[i], options.output_files[i]) == False: sys.exit("Conversion failed") openimageio-1.7.17~dfsg0.orig/src/include/0000755000175000017500000000000013151711064016537 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/include/CMakeLists.txt0000644000175000017500000000160013151711064021274 0ustar mfvmfvfile (GLOB public_headers OpenImageIO/*.h) if (NOT USE_EXTERNAL_PUGIXML) list (APPEND public_headers OpenImageIO/pugixml.hpp OpenImageIO/pugiconfig.hpp OpenImageIO/pugixml.cpp) endif () if (VERBOSE) message(STATUS "Create oiioversion.h from oiioversion.h.in") endif () # Mangle the SOVERSION so that it's a valid C++ identifier for the versioning # namespace defined in oiioversion.h string (REGEX REPLACE "\\." "_" MANGLED_SOVERSION ${SOVERSION}) set (OIIO_VERSION_NS "v${MANGLED_SOVERSION}") set (OIIO_BUILD_CPP11 ${USE_CPP11}) set (OIIO_BUILD_CPP14 ${USE_CPP14}) configure_file(OpenImageIO/oiioversion.h.in "${CMAKE_BINARY_DIR}/include/OpenImageIO/oiioversion.h" @ONLY) list(APPEND public_headers "${CMAKE_BINARY_DIR}/include/OpenImageIO/oiioversion.h") install (FILES ${public_headers} DESTINATION ${INCLUDE_INSTALL_DIR} COMPONENT developer) openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/0000755000175000017500000000000013151711064020633 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/strided_ptr.h0000644000175000017500000000754113151711064023336 0ustar mfvmfv/* Copyright 2014 Larry Gritz and the other authors and contributors. All Rights Reserved. Based on BSD-licensed software Copyright 2004 NVIDIA Corp. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #pragma once #include #include "oiioversion.h" OIIO_NAMESPACE_BEGIN /// strided_ptr looks like a 'T*', but it incorporates a stride (in /// bytes) that may be different than sizeof(T). Operators ++, --, [], and /// so on, take the stride into account when computing where each "array /// element" actually exists. A strided_ptr is mutable (the values /// pointed to may be modified), whereas an strided_ptr is not /// mutable. template class strided_ptr { public: strided_ptr (T* ptr=NULL, ptrdiff_t stride=sizeof(T)) : m_ptr(ptr), m_stride(stride) { } strided_ptr (const strided_ptr &p) : m_ptr(p.m_ptr), m_stride(p.m_stride) {} const strided_ptr& operator= (const strided_ptr &p) { m_ptr = p.m_ptr; m_stride = p.m_stride; return *this; } T& operator* () const { return *m_ptr; } T& operator[] (ptrdiff_t pos) const { return get(pos); } T* data() const { return m_ptr; } ptrdiff_t stride () const { return m_stride; } bool operator== (const T *p) const { return m_ptr == p; } bool operator!= (const T *p) const { return m_ptr != p; } const strided_ptr& operator++ () { // prefix m_ptr = getptr(1); return *this; } strided_ptr operator++(int) { // postfix strided_ptr r; ++(*this); return r; } const strided_ptr& operator-- () { // prefix m_ptr = getptr(-1); return *this; } strided_ptr operator--(int) { // postfix strided_ptr r; --(*this); return r; } strided_ptr operator+ (int d) const { return strided_ptr (getptr(d), m_stride); } const strided_ptr& operator+= (int d) { m_ptr = getptr(d); return *this; } strided_ptr operator- (int d) const { return strided_ptr (getptr(-d), m_stride); } const strided_ptr& operator-= (int d) { m_ptr = getptr(-d); return *this; } private: T * m_ptr; ptrdiff_t m_stride; inline T* getptr (ptrdiff_t pos=0) const { return (T*)((char *)m_ptr + pos*m_stride); } inline T& get (ptrdiff_t pos=0) const { return *getptr(pos); } }; OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/filesystem.h0000644000175000017500000003353313151711064023177 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// @file filesystem.h /// /// @brief Utilities for dealing with file names and files portably. /// /// Some helpful nomenclature: /// - "filename" - a file or directory name, relative or absolute /// - "searchpath" - a list of directories separated by ':' or ';'. /// #ifndef OPENIMAGEIO_FILESYSTEM_H #define OPENIMAGEIO_FILESYSTEM_H #include #include #include #include #include #include #include "export.h" #include "oiioversion.h" #include "string_view.h" #if defined(_WIN32) && defined(__GLIBCXX__) #define OIIO_FILESYSTEM_USE_STDIO_FILEBUF 1 #include "fstream_mingw.h" #endif OIIO_NAMESPACE_BEGIN #if OIIO_FILESYSTEM_USE_STDIO_FILEBUF // MingW uses GCC to build, but does not support having a wchar_t* passed as argument // of ifstream::open or ofstream::open. To properly support UTF-8 encoding on MingW we must // use the __gnu_cxx::stdio_filebuf GNU extension that can be used with _wfsopen and returned // into a istream which share the same API as ifsteam. The same reasoning holds for ofstream. typedef basic_ifstream ifstream; typedef basic_ofstream ofstream; #else typedef std::ifstream ifstream; typedef std::ofstream ofstream; #endif /// @namespace Filesystem /// /// @brief Platform-independent utilities for manipulating file names, /// files, directories, and other file system miscellany. namespace Filesystem { /// Return the filename (excluding any directories, but including the /// file extension, if any) of a filepath. OIIO_API std::string filename (const std::string &filepath); /// Return the file extension (including the last '.' if /// include_dot=true) of a filename or filepath. OIIO_API std::string extension (const std::string &filepath, bool include_dot=true); /// Return all but the last part of the path, for example, /// parent_path("foo/bar") returns "foo", and parent_path("foo") /// returns "". OIIO_API std::string parent_path (const std::string &filepath); /// Replace the file extension of a filename or filepath. Does not alter /// filepath, just returns a new string. Note that the new_extension /// should contain a leading '.' dot. OIIO_API std::string replace_extension (const std::string &filepath, const std::string &new_extension); /// Turn a searchpath (multiple directory paths separated by ':' or ';') /// into a vector containing each individual directory. If /// validonly is true, only existing and readable directories will end /// up in the list. N.B., the directory names will not have trailing /// slashes. OIIO_API void searchpath_split (const std::string &searchpath, std::vector &dirs, bool validonly = false); /// Find the first instance of a filename existing in a vector of /// directories, returning the full path as a string. If the file is /// not found in any of the listed directories, return an empty string. /// If the filename is absolute, the directory list will not be used. /// If testcwd is true, "." will be tested before the searchpath; /// otherwise, "." will only be tested if it's explicitly in dirs. If /// recursive is true, the directories will be searched recursively, /// finding a matching file in any subdirectory of the directories /// listed in dirs; otherwise. OIIO_API std::string searchpath_find (const std::string &filename, const std::vector &dirs, bool testcwd = true, bool recursive = false); /// Fill a vector-of-strings with the names of all files contained by /// directory dirname. If recursive is true, it will return all files /// below the directory (even in subdirectories), but if recursive is /// false (the default)If filter_regex is supplied and non-empty, only /// filenames matching the regular expression will be returned. Return /// true if ok, false if there was an error (such as dirname not being /// found or not actually being a directory). OIIO_API bool get_directory_entries (const std::string &dirname, std::vector &filenames, bool recursive = false, const std::string &filter_regex=std::string()); /// Return true if the path is an "absolute" (not relative) path. /// If 'dot_is_absolute' is true, consider "./foo" absolute. OIIO_API bool path_is_absolute (const std::string &path, bool dot_is_absolute=false); /// Return true if the file exists. /// OIIO_API bool exists (const std::string &path); /// Return true if the file exists and is a directory. /// OIIO_API bool is_directory (const std::string &path); /// Return true if the file exists and is a regular file. /// OIIO_API bool is_regular (const std::string &path); /// Create the directory. Return true for success, false for failure and /// place an error message in err. OIIO_API bool create_directory (string_view path, std::string &err); inline bool create_directory (string_view path) { std::string err; return create_directory (path, err); } /// Copy a file, directory, or link. It is an error if 'to' already exists. /// Return true upon success, false upon failure and place an error message /// in err. OIIO_API bool copy (string_view from, string_view to, std::string &err); inline bool copy (string_view from, string_view to) { std::string err; return copy (from, to, err); } /// Rename (or move) a file, directory, or link. Return true upon success, /// false upon failure and place an error message in err. OIIO_API bool rename (string_view from, string_view to, std::string &err); inline bool rename (string_view from, string_view to) { std::string err; return rename (from, to, err); } /// Remove the file or directory. Return true for success, false for /// failure and place an error message in err. OIIO_API bool remove (string_view path, std::string &err); inline bool remove (string_view path) { std::string err; return remove (path, err); } /// Remove the file or directory, including any children (recursively). /// Return the number of files removed. Place an error message (if /// applicable in err. OIIO_API unsigned long long remove_all (string_view path, std::string &err); inline unsigned long long remove_all (string_view path) { std::string err; return remove_all (path, err); } /// Return a directory path where temporary files can be made. /// OIIO_API std::string temp_directory_path (); /// Return a unique filename suitable for making a temporary file or /// directory. OIIO_API std::string unique_path (string_view model="%%%%-%%%%-%%%%-%%%%"); /// Version of fopen that can handle UTF-8 paths even on Windows /// OIIO_API FILE *fopen (string_view path, string_view mode); /// Return the current (".") directory path. /// OIIO_API std::string current_path (); /// Version of std::ifstream.open that can handle UTF-8 paths /// OIIO_API void open (OIIO::ifstream &stream, string_view path, std::ios_base::openmode mode = std::ios_base::in); /// Version of std::ofstream.open that can handle UTF-8 paths /// OIIO_API void open (OIIO::ofstream &stream, string_view path, std::ios_base::openmode mode = std::ios_base::out); /// Read the entire contents of the named text file and place it in str, /// returning true on success, false on failure. OIIO_API bool read_text_file (string_view filename, std::string &str); /// Read a maximum of n bytes from the named file, starting at position pos /// (which defaults to the start of the file), storing results in /// buffer[0..n-1]. Return the number of bytes read, which will be n for /// full success, less than n if the file was fewer than n+pos bytes long, /// or 0 if the file did not exist or could not be read. OIIO_API size_t read_bytes (string_view path, void *buffer, size_t n, size_t pos=0); /// Get last modified time of file /// OIIO_API std::time_t last_write_time (const std::string& path); /// Set last modified time on file /// OIIO_API void last_write_time (const std::string& path, std::time_t time); /// Return the size of the file (in bytes), or uint64_t(-1) if there is any /// error. OIIO_API uint64_t file_size (string_view path); /// Ensure command line arguments are UTF-8 everywhere /// OIIO_API void convert_native_arguments (int argc, const char *argv[]); /// Turn a sequence description string into a vector of integers. /// The sequence description can be any of the following /// * A value (e.g., "3") /// * A value range ("1-10", "10-1", "1-10x3", "1-10y3"): /// START-FINISH A range, inclusive of start & finish /// START-FINISHxSTEP A range with step size /// START-FINISHySTEP The complement of a stepped range, that is, /// all numbers within the range that would /// NOT have been selected by 'x'. /// Note that START may be > FINISH, or STEP may be negative. /// * Multiple values or ranges, separated by a comma (e.g., "3,4,10-20x2") /// Retrn true upon success, false if the description was too malformed /// to generate a sequence. OIIO_API bool enumerate_sequence (string_view desc, std::vector &numbers); /// Given a pattern (such as "foo.#.tif" or "bar.1-10#.exr"), return a /// normalized pattern in printf format (such as "foo.%04d.tif") and a /// framespec (such as "1-10"). /// /// If framepadding_override is > 0, it overrides any specific padding amount /// in the original pattern. /// /// Return true upon success, false if the description was too malformed /// to generate a sequence. OIIO_API bool parse_pattern (const char *pattern, int framepadding_override, std::string &normalized_pattern, std::string &framespec); /// Given a normalized pattern (such as "foo.%04d.tif") and a list of frame /// numbers, generate a list of filenames. /// /// Return true upon success, false if the description was too malformed /// to generate a sequence. OIIO_API bool enumerate_file_sequence (const std::string &pattern, const std::vector &numbers, std::vector &filenames); /// Given a normalized pattern (such as "foo_%V.%04d.tif") and a list of frame /// numbers, generate a list of filenames. "views" is list of per-frame /// views, or empty. In each frame filename, "%V" is replaced with the view, /// and "%v" is replaced with the first character of the view. /// /// Return true upon success, false if the description was too malformed /// to generate a sequence. OIIO_API bool enumerate_file_sequence (const std::string &pattern, const std::vector &numbers, const std::vector &views, std::vector &filenames); /// Given a normalized pattern (such as "/path/to/foo.%04d.tif") scan the /// containing directory (/path/to) for matching frame numbers, views and files. /// "%V" in the pattern matches views, while "%v" matches the first character /// of each entry in views. /// /// Return true upon success, false if the directory doesn't exist or the /// pattern can't be parsed. OIIO_API bool scan_for_matching_filenames (const std::string &pattern, const std::vector &views, std::vector &frame_numbers, std::vector &frame_views, std::vector &filenames); /// Given a normalized pattern (such as "/path/to/foo.%04d.tif") scan the /// containing directory (/path/to) for matching frame numbers and files. /// /// Return true upon success, false if the directory doesn't exist or the /// pattern can't be parsed. OIIO_API bool scan_for_matching_filenames (const std::string &pattern, std::vector &numbers, std::vector &filenames); }; // namespace Filesystem OIIO_NAMESPACE_END #endif // OPENIMAGEIO_FILESYSTEM_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/version.h0000644000175000017500000000021213151711064022464 0ustar mfvmfv// DEPRECATED header OpenImageIO/version.h // For back compatibility, just include the new name, oiioversion.h. #include "oiioversion.h" openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/missing_math.h0000644000175000017500000001347713151711064023502 0ustar mfvmfv/* Copyright 2008-2014 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// \file /// /// This adds definitions we think *should* be in math.h, but which on /// on some platforms appears to be missing in action. /// #pragma once #include #include "oiioversion.h" /* Just for the OIIO_NAMESPACE stuff */ #ifndef M_PI /// PI # define M_PI 3.14159265358979323846264338327950288 #endif #ifndef M_PI_2 /// PI / 2 # define M_PI_2 1.57079632679489661923132169163975144 #endif #ifndef M_PI_4 /// PI / 4 # define M_PI_4 0.785398163397448309615660845819875721 #endif #ifndef M_TWO_PI /// PI * 2 # define M_TWO_PI (M_PI * 2.0) #endif #ifndef M_1_PI /// 1/PI # define M_1_PI 0.318309886183790671537767526745028724 #endif #ifndef M_2_PI /// 2/PI # define M_2_PI 0.636619772367581343075535053490057448 #endif #ifndef M_SQRT2 /// sqrt(2) # define M_SQRT2 1.41421356237309504880168872420969808 #endif #ifndef M_SQRT1_2 /// 1/sqrt(2) # define M_SQRT1_2 0.707106781186547524400844362104849039 #endif #ifndef M_LN2 /// ln(2) # define M_LN2 0.69314718055994530941723212145817656 #endif #ifndef M_LN10 /// ln(10) # define M_LN10 2.30258509299404568401799145468436421 #endif #ifndef M_E /// e, Euler's number # define M_E 2.71828182845904523536028747135266250 #endif #ifndef M_LOG2E /// log2(e) # define M_LOG2E 1.44269504088896340735992468100189214 #endif OIIO_NAMESPACE_BEGIN #ifdef _WIN32 // Windows doesn't define these functions from math.h #define hypotf _hypotf #define copysign(x,y) _copysign(x,y) #define copysignf(x,y) float(copysign(x,y)) inline float truncf (float val) { return (float)(int)val; } #if defined(_MSC_VER) #if _MSC_VER < 1800 /* Needed for MSVS prior to 2013 */ template inline int isnan (T x) { return _isnan(x); } template inline int isfinite (T x) { return _finite(x); } template inline int isinf (T x) { return (isfinite(x)||isnan(x)) ? 0 : static_cast(copysign(T(1.0), x)); } inline double round (float val) { return floor (val + 0.5); } inline float roundf (float val) { return static_cast(round (val)); } inline float log2f (float val) { return logf (val)/static_cast(M_LN2); } inline float cbrtf (float val) { return powf (val, 1.0/3.0); } inline float rintf (float val) { return val + copysignf(0.5f, val); } #elif _MSC_VER >= 1800 && __cplusplus <= 201103L // Prior to c++11, these were implementation defined, and on msvc, were not in the // std namespace using ::isnan; using ::isinf; using ::isfinite; #else using std::isnan; using std::isinf; using std::isfinite; #endif #endif /* MSVS < 2013 */ inline float exp2f (float val) { // 2^val = e^(val*ln(2)) return (float) exp( val * M_LN2 ); } #if defined(_MSC_VER) && _MSC_VER < 1800 /* Needed for MSVS prior to 2013 */ inline float logbf (float val) { // please see http://www.kernel.org/doc/man-pages/online/pages/man3/logb.3.html return floorf (logf(fabsf(val))/logf(FLT_RADIX)); } // from http://www.johndcook.com/cpp_expm1.html inline double expm1(double val) { // exp(x) - 1 without loss of precision for small values of x. if (fabs(val) < 1e-5) return val + 0.5*val*val; else return exp(val) - 1.0; } inline float expm1f (float val) { return (float)expm1(val); } // from http://www.johndcook.com/cpp_erf.html inline double erf(double x) { // constants double a1 = 0.254829592; double a2 = -0.284496736; double a3 = 1.421413741; double a4 = -1.453152027; double a5 = 1.061405429; double p = 0.3275911; // Save the sign of x int sign = 1; if (x < 0) sign = -1; x = fabs(x); // A&S formula 7.1.26 double t = 1.0/(1.0 + p*x); double y = 1.0 - (((((a5*t + a4)*t) + a3)*t + a2)*t + a1)*t*exp(-x*x); return sign*y; } inline float erff (float val) { return (float)erf(val); } inline double erfc(double val) { return 1.0 - erf(val); } inline float erfcf (float val) { return (float)erfc(val); } #endif /* MSVS < 2013 */ #endif /* _WIN32 */ #if !defined(_MSC_VER) using std::isnan; using std::isinf; using std::isfinite; #endif // Functions missing from FreeBSD #if (defined(__FreeBSD__) && (__FreeBSD_version < 803000)) inline float log2f (float val) { return logf (val)/static_cast(M_LN2); } #endif OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/varyingref.h0000644000175000017500000002143013151711064023160 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// @file varyingref.h /// @Brief Define the VaryingRef class. #ifndef OPENIMAGEIO_VARYINGREF_H #define OPENIMAGEIO_VARYINGREF_H #include "oiioversion.h" OIIO_NAMESPACE_BEGIN /// VaryingRef is a templated class (on class T) that holds either a /// pointer to a single T value, or an "array" of T values, each /// separated by a certain number of bytes. For those versed in the /// lingo of SIMD shading, this encapsulates 'uniform' and 'varying' /// references. /// /// Suppose you have a computation 'kernel' that is performing an /// operation while looping over several computation 'points.' Each of /// the several operands of the kernel may either be a 'uniform' value /// (identical for each point), or 'varying' (having a potentially /// different value for each point). /// /// Here is a concrete example. Suppose you have the following function: /// \code /// void add (int n, float *a, float *b, float *result) { /// for (int i = 0; i < n; ++i) /// result[i] = a[i] + b[i]; /// } /// \endcode /// /// But if the caller of this function has only a single b value (let's /// say, you always want to add 3 to every a[i]), you would be forced /// to replicate an entire array full of 3's in order to call the function. /// /// Instead, we may wish to generalize the function so that each operand /// may rever to EITHER a single value or an array of values, without /// making the code more complicated. We can do this with VaryingRef: /// \code /// void add (int n, VaryingRef a, VaryingRef b, /// float *result) { /// for (int i = 0; i < n; ++i) /// result[i] = a[i] + b[i]; /// } /// \endcode /// /// VaryingRef overloads operator [] to properly decode whether it is /// uniform (point to the one value) or varying (index the right array /// element). It also overloads the increment operator ++ and the pointer /// indirection operator '*', so you could also write the function /// equivalently as: /// \code /// void add (int n, VaryingRef a, VaryingRef b, /// float *result) { /// for (int i = 0; i < n; ++i, ++a, ++b) // note increments /// result[i] = (*a) + (*b); /// } /// \endcode /// /// An example of calling this function would be: /// \code /// float a[n]; /// float b; // just 1 value /// float result[n]; /// add (n, VaryingRef(a,sizeof(a[0])), /// VaryingRef(b), result); /// \endcode /// /// In this example, we're passing a truly varying 'a' (signified by /// giving a step size from element to element), but a uniform 'b' (signified /// by no step size, or a step size of zero). /// /// There are Varying() and Uniform() templated functions that provide /// a helpful shorthand: /// \code /// add (n, Varying(a), Uniform(b), result); /// \endcode /// /// Now let's take it a step further and fully optimize the 'add' function /// for when both operands are uniform: /// \code /// void add (int n, VaryingRef a, VaryingRef b, /// VaryingRef result) { /// if (a.is_uniform() && b.is_uniform()) { /// float r = (*a) + (*b); /// for (int i = 0; i < n; ++i) /// result[i] = r; /// } else { /// // One or both are varying /// for (int i = 0; i < n; ++i, ++a, ++b) /// result[i] = (*a) + (*b); /// } /// } /// \endcode /// This is the basis for handling uniform and varying values efficiently /// inside a SIMD shading system. template class VaryingRef { public: VaryingRef () { init (0, 0); } /// Construct a VaryingRef either of a single value pointed to by ptr /// (if step == 0 or no step is provided), or of a varying set of /// values beginning with ptr and with successive values every 'step' /// bytes. VaryingRef (void *ptr_, int step_=0) { init ((T *)ptr_,step_); } /// Construct a uniform VaryingRef from a single value. /// VaryingRef (T &ptr_) { init (&ptr_, 0); } /// Initialize this VaryingRef to either of a single value pointed /// to by ptr (if step == 0 or no step is provided), or of a varying /// set of values beginning with ptr and with successive values /// every 'step' bytes. void init (T *ptr_, int step_=0) { m_ptr = ptr_; m_step = step_; } /// Initialize this VaryingRef to be uniform and point to a particular /// value reference. const VaryingRef & operator= (T &ptr_) { init (&ptr_); return *this; } /// Is this reference pointing nowhere? /// bool is_null () const { return (m_ptr == 0); } /// Cast to void* returns the pointer, but the real purpose is so /// you can use a VaryingRef as if it were a 'bool' value in a test. operator void*() const { return m_ptr; } /// Is this VaryingRef referring to a varying value, signified by /// having a nonzero step size between elements? bool is_varying () const { return (m_step != 0); } /// Is this VaryingRef referring to a uniform value, signified by /// having a step size of zero between elements? bool is_uniform () const { return (m_step == 0); } /// Pre-increment: If this VaryingRef is varying, increment its /// pointer to the next element in the series, but don't change /// anything if it's uniform. In either case, return a reference to /// its new state. VaryingRef & operator++ () { // Prefix form ++i char *p = (char *)m_ptr; p += m_step; m_ptr = (T *) p; return *this; } /// Post-increment: If this VaryingRef is varying, increment its /// pointer to the next element in the series, but don't change /// anything if it's uniform. No value is returned, so it's not /// legal to do 'bar = foo++' if foo and bar are VaryingRef's. void operator++ (int) { // Postfix form i++ : return nothing to avoid copy // VaryingRef tmp = *this; char *p = (char *)m_ptr; p += m_step; m_ptr = (T *) p; // return tmp; } /// Pointer indirection will return the first value currently /// pointed to by this VaryingRef. T & operator* () const { return *m_ptr; } /// Array indexing operator will return a reference to the single /// element if *this is uniform, or to the i-th element of the /// series if *this is varying. T & operator[] (int i) const { return *(T *) ((char *)m_ptr + i*m_step); } /// Return the raw pointer underneath. /// T * ptr () const { return m_ptr; } /// Return the raw step underneath. /// int step () const { return m_step; } private: T *m_ptr; ///< Pointer to value int m_step; ///< Distance between successive values -- in BYTES! }; /// Helper function wraps a varying reference with default step size. /// template VaryingRef Varying (T *x) { return VaryingRef (x, sizeof(T)); } /// Helper function wraps a uniform reference. /// template VaryingRef Uniform (T *x) { return VaryingRef (x, 0); } /// Helper function wraps a uniform reference. /// template VaryingRef Uniform (T &x) { return VaryingRef (&x, 0); } OIIO_NAMESPACE_END #endif // OPENIMAGEIO_VARYINGREF_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/platform.h0000644000175000017500000003457113151711064022642 0ustar mfvmfv/* Copyright 2014 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ ///////////////////////////////////////////////////////////////////////// /// @file platform.h /// /// @brief Platform-related macros. ///////////////////////////////////////////////////////////////////////// #pragma once // Make sure all platforms have the explicit sized integer types #if defined(_MSC_VER) && _MSC_VER < 1600 typedef __int8 int8_t; typedef __int16 int16_t; typedef __int32 int32_t; typedef __int64 int64_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; # ifndef _UINT64_T typedef unsigned __int32 uint32_t; typedef unsigned __int64 uint64_t; # define _UINT32_T # define _UINT64_T # endif #else # ifndef __STDC_LIMIT_MACROS # define __STDC_LIMIT_MACROS /* needed for some defs in stdint.h */ # endif # include #endif #if defined(__FreeBSD__) #include #endif #ifdef __MINGW32__ #include // for alloca #endif #if defined(_MSC_VER) || defined(_WIN32) # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif # ifndef VC_EXTRALEAN # define VC_EXTRALEAN # endif # ifndef NOMINMAX # define NOMINMAX # endif # include #endif # ifdef _MSC_VER #include #endif #include "oiioversion.h" // Detect which C++ standard we're using, and handy macros. // // OIIO_CPLUSPLUS_VERSION : which C++ standard is compiling (3, 11, 14, ...) // OIIO_USING_CPP11 : (deprecated) defined and 1 if using C++11 or newer. // OIIO_CONSTEXPR : constexpr for C++ >= 11, otherwise nothing // OIIO_CONSTEXPR_OR_CONST : constexpr for C++ >= 11, otherwise const // OIIO_CONSTEXPR14 : constexpr for C++ >= 14, otherwise nothing (this is // useful for things that can only be constexpr for 14) // OIIO_NOEXCEPT : noexcept for C++ >= 11, otherwise nothing // // Note: oiioversion.h defines OIIO_BUILD_CPP11 or OIIO_BUILD_CPP14 to be 1 // if OIIO itself was built using C++11 or C++14, respectively. In contrast, // OIIO_CPLUSPLUS_VERSION defined below will be set to the right number for // the C++ standard being compiled RIGHT NOW. These two things may be the // same when compiling OIIO, but they may not be the same if another // packages is compiling against OIIO and using these headers (OIIO may be // C++11 but the client package may be older, or vice versa -- use these two // symbols to differentiate these cases, when important). #if (__cplusplus >= 201402L) # define OIIO_USING_CPP11 1 # define OIIO_CPLUSPLUS_VERSION 14 # define OIIO_CONSTEXPR constexpr # define OIIO_CONSTEXPR_OR_CONST constexpr # define OIIO_CONSTEXPR14 constexpr # define OIIO_NOEXCEPT noexcept #elif (__cplusplus >= 201103L) || _MSC_VER >= 1900 # define OIIO_USING_CPP11 1 /* deprecated */ # define OIIO_CPLUSPLUS_VERSION 11 # define OIIO_CONSTEXPR constexpr # define OIIO_CONSTEXPR_OR_CONST constexpr # define OIIO_CONSTEXPR14 /* not constexpr before C++14 */ # define OIIO_NOEXCEPT noexcept #else # define OIIO_CPLUSPLUS_VERSION 3 /* presume C++03 */ # define OIIO_CONSTEXPR /* not constexpr before C++11 */ # define OIIO_CONSTEXPR_OR_CONST const /* not constexpr before C++11 */ # define OIIO_CONSTEXPR14 /* not constexpr before C++14 */ # define OIIO_NOEXCEPT /* no noexcept before C++11 */ #endif // Fallback definitions for feature testing. Some newer compilers define // these for real, and it may be standard for C++17. #ifndef __has_feature # define __has_feature(x) 0 #endif #ifndef __has_extension # define __has_extension(x) __has_feature(x) #endif #ifndef __has_attribute # define __has_attribute(x) 0 #endif #ifndef __has_builtin # define __has_builtin(x) 0 #endif #ifndef __has_include # define __has_include(x) 0 #endif // Define OIIO_GNUC_VERSION to hold an encoded gcc version (e.g. 40802 for // 4.8.2), or 0 if not a GCC release. N.B.: This will be 0 for clang. #if defined(__GNUC__) && !defined(__clang__) # define OIIO_GNUC_VERSION (10000*__GNUC__ + 100*__GNUC_MINOR__ + __GNUC_PATCHLEVEL__) #else # define OIIO_GNUC_VERSION 0 #endif // Define OIIO_CLANG_VERSION to hold an encoded generic Clang version (e.g. // 30402 for clang 3.4.2), or 0 if not a generic Clang release. // N.B. This will be 0 for the clang Apple distributes (which has different // version numbers entirely). #if defined(__clang__) && !defined(__apple_build_version__) # define OIIO_CLANG_VERSION (10000*__clang_major__ + 100*__clang_minor__ + __clang_patchlevel__) #else # define OIIO_CLANG_VERSION 0 #endif // Define OIIO_APPLE_CLANG_VERSION to hold an encoded Apple Clang version // (e.g. 70002 for clang 7.0.2), or 0 if not an Apple Clang release. #if defined(__clang__) && defined(__apple_build_version__) # define OIIO_APPLE_CLANG_VERSION (10000*__clang_major__ + 100*__clang_minor__ + __clang_patchlevel__) #else # define OIIO_APPLE_CLANG_VERSION 0 #endif // Tests for MSVS versions, always 0 if not MSVS at all. #define OIIO_MSVS_AT_LEAST_2013 (defined(_MSC_VER) && _MSC_VER >= 1800) #define OIIO_MSVS_BEFORE_2013 (defined(_MSC_VER) && _MSC_VER < 1800) #define OIIO_MSVS_AT_LEAST_2015 (defined(_MSC_VER) && _MSC_VER >= 1900) #define OIIO_MSVS_BEFORE_2015 (defined(_MSC_VER) && _MSC_VER < 1900) /// allocates memory, equivalent of C99 type var_name[size] #define OIIO_ALLOCA(type, size) ((type*)alloca((size) * sizeof (type))) /// Deprecated (for namespace pollution reasons) #define ALLOCA(type, size) ((type*)alloca((size) * sizeof (type))) // Define a macro that can be used for memory alignment. // I think that in a future world of C++1x compatibility, all these can // be replaced with [[ align(size) ]]. #if defined (__GNUC__) || __has_attribute(aligned) # define OIIO_ALIGN(size) __attribute__((aligned(size))) #elif defined (_MSC_VER) # define OIIO_ALIGN(size) __declspec(align(size)) #elif defined (__INTEL_COMPILER) # define OIIO_ALIGN(size) __declspec(align((size))) #else # error "Don't know how to define OIIO_ALIGN" #endif // Cache line size is 64 on all modern x86 CPUs. If this changes or we // anticipate ports to other architectures, we'll need to change this. #define OIIO_CACHE_LINE_SIZE 64 // Align the next declaration to be on its own cache line #define OIIO_CACHE_ALIGN OIIO_ALIGN(OIIO_CACHE_LINE_SIZE) // gcc defines a special intrinsic to use in conditionals that can speed // up extremely performance-critical spots if the conditional usually // (or rarely) is true. You use it by replacing // if (x) ... // with // if (OIIO_LIKELY(x)) ... // if you think x will usually be true // or // if (OIIO_UNLIKELY(x)) ... // if you think x will rarely be true // Caveat: Programmers are notoriously bad at guessing this, so it // should be used only with thorough benchmarking. #if defined(__GNUC__) || defined(__clang__) || defined(__INTEL_COMPILER) #define OIIO_LIKELY(x) (__builtin_expect(bool(x), true)) #define OIIO_UNLIKELY(x) (__builtin_expect(bool(x), false)) #else #define OIIO_LIKELY(x) (x) #define OIIO_UNLIKELY(x) (x) #endif // OIIO_FORCELINE is a function attribute that attempts to make the function // always inline. On many compilers regular 'inline' is only advisory. Put // this attribute before the function return type, just like you would use // 'inline'. #if defined(__GNUC__) || defined(__clang__) || __has_attribute(always_inline) # define OIIO_FORCEINLINE inline __attribute__((always_inline)) #elif defined(_MSC_VER) || defined(__INTEL_COMPILER) # define OIIO_FORCEINLINE __forceinline #else # define OIIO_FORCEINLINE inline #endif // OIIO_PURE_FUNC is a function attribute that assures the compiler that the // function does not write to any non-local memory other than its return // value and has no side effects. This can ebable additional compiler // optimizations by knowing that calling the function cannot possibly alter // any other memory. This declaration goes after the function declaration: // int blah (int arg) OIIO_PURE_FUNC; #if defined(__GNUC__) || defined(__clang__) || defined(__INTEL_COMPILER) || __has_attribute(pure) # define OIIO_PURE_FUNC __attribute__((pure)) #elif defined(_MSC_VER) # define OIIO_PURE_FUNC /* seems not supported by MSVS */ #else # define OIIO_PURE_FUNC #endif // OIIO_CONST_FUNC is a function attribute that assures the compiler that // the function does not examine (read) any values except for its arguments, // does not write any non-local memory other than its return value, and has // no side effects. This is even more strict than 'pure', and allows even // more optimizations (such as eliminating multiple calls to the function // that have the exact same argument values). #if defined(__GNUC__) || defined(__clang__) || defined(__INTEL_COMPILER) || __has_attribute(const) # define OIIO_CONST_FUNC __attribute__((const)) #elif defined(_MSC_VER) # define OIIO_CONST_FUNC /* seems not supported by MSVS */ #else # define OIIO_CONST_FUNC #endif // OIIO_NOTHROW is a function attribute that assures the compiler that // neither the function nor any other function it calls will throw an // exception. This declaration goes after the // function declaration: int blah (int arg) OIIO_NOTHROW; #if defined(__GNUC__) || defined(__clang__) || defined(__INTEL_COMPILER) || __has_attribute(nothrow) # define OIIO_NOTHROW __attribute__((nothrow)) #elif defined(_MSC_VER) # define OIIO_NOTHROW __declspec(nothrow) #else # define OIIO_NOTHROW #endif // OIIO_UNUSED_OK is a function or variable attribute that assures tells the // compiler that it's fine for the item to appear to be unused. #if defined(__GNUC__) || defined(__clang__) || defined(__INTEL_COMPILER) || __has_attribute(unused) # define OIIO_UNUSED_OK __attribute__((unused)) #else # define OIIO_UNUSED_OK #endif // OIIO_RESTRICT is a parameter attribute that indicates a promise that the // parameter definitely will not alias any other parameters in such a way // that creates a data dependency. Use with caution! #if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) || defined(__INTEL_COMPILER) # define OIIO_RESTRICT __restrict #else # define OIIO_RESTRICT #endif #if OIIO_CPLUSPLUS_VERSION >= 14 && __has_attribute(deprecated) # define OIIO_DEPRECATED(msg) [[deprecated(msg)]] #elif OIIO_GNUC_VERSION >= 40600 || defined(__clang__) # define OIIO_DEPRECATED(msg) __attribute__((deprecated(msg))) #elif defined(__GNUC__) /* older gcc -- only the one with no message */ # define OIIO_DEPRECATED(msg) __attribute__((deprecated)) #elif defined(_MSC_VER) # define OIIO_DEPRECATED(msg) __declspec(deprecated(msg)) #else # define OIIO_DEPRECATED(msg) #endif // Try to deduce endianness #if (defined(_WIN32) || defined(__i386__) || defined(__x86_64__)) # ifndef __LITTLE_ENDIAN__ # define __LITTLE_ENDIAN__ 1 # undef __BIG_ENDIAN__ # endif #endif OIIO_NAMESPACE_BEGIN /// Return true if the architecture we are running on is little endian OIIO_FORCEINLINE bool littleendian (void) { #if defined(__BIG_ENDIAN__) return false; #elif defined(__LITTLE_ENDIAN__) return true; #else // Otherwise, do something quick to compute it int i = 1; return *((char *) &i); #endif } /// Return true if the architecture we are running on is big endian OIIO_FORCEINLINE bool bigendian (void) { return ! littleendian(); } /// Retrieve cpuid flags into 'info'. inline void cpuid (int info[4], int infoType, int extra) { // Implementation cribbed from Halide (http://halide-lang.org), which // cribbed it from ISPC (https://github.com/ispc/ispc). #if (defined(_WIN32) || defined(__i386__) || defined(__x86_64__)) # ifdef _MSC_VER __cpuidex(info, infoType, extra); # elif defined(__x86_64__) __asm__ __volatile__ ( "cpuid \n\t" : "=a" (info[0]), "=b" (info[1]), "=c" (info[2]), "=d" (info[3]) : "0" (infoType), "2" (extra)); # else __asm__ __volatile__ ( "mov{l}\t{%%}ebx, %1 \n\t" "cpuid \n\t" "xchg{l}\t{%%}ebx, %1 \n\t" : "=a" (info[0]), "=r" (info[1]), "=c" (info[2]), "=d" (info[3]) : "0" (infoType), "2" (extra)); # endif #else info[0] = 0; info[1] = 0; info[2] = 0; info[3] = 0; #endif } inline bool cpu_has_sse2 () {int i[4]; cpuid(i,1,0); return (i[3] & (1<<26)) != 0; } inline bool cpu_has_sse3 () {int i[4]; cpuid(i,1,0); return (i[2] & (1<<0)) != 0; } inline bool cpu_has_ssse3 () {int i[4]; cpuid(i,1,0); return (i[2] & (1<<9)) != 0; } inline bool cpu_has_fma () {int i[4]; cpuid(i,1,0); return (i[2] & (1<<12)) != 0; } inline bool cpu_has_sse41 () {int i[4]; cpuid(i,1,0); return (i[2] & (1<<19)) != 0; } inline bool cpu_has_sse42 () {int i[4]; cpuid(i,1,0); return (i[2] & (1<<20)) != 0; } inline bool cpu_has_popcnt() {int i[4]; cpuid(i,1,0); return (i[2] & (1<<23)) != 0; } inline bool cpu_has_avx () {int i[4]; cpuid(i,1,0); return (i[2] & (1<<28)) != 0; } inline bool cpu_has_f16c () {int i[4]; cpuid(i,1,0); return (i[2] & (1<<29)) != 0; } inline bool cpu_has_rdrand() {int i[4]; cpuid(i,1,0); return (i[2] & (1<<30)) != 0; } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/export.h0000644000175000017500000001031513151711064022325 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_EXPORT_H #define OPENIMAGEIO_EXPORT_H /// \file /// Macros necessary for proper symbol export from dynamic libraries. /// /// On Windows, when compiling code that will end up in a DLL, symbols /// must be marked as 'exported' (i.e. __declspec(dllexport)) or they /// won't be visible to programs linking against the DLL. /// /// In addition, when compiling the application code that calls the DLL, /// if a routine is marked as 'imported' (i.e. __declspec(dllimport)), /// the compiler can be smart about eliminating a level of calling /// indirection. But you DON'T want to use __declspec(dllimport) when /// calling a function from within its own DLL (it will still compile /// correctly, just not with maximal efficiency). Which is quite the /// dilemma since the same header file is used by both the library and /// its clients. Sheesh! /// /// But on Linux/OSX as well, we want to only have the DSO export the /// symbols we designate as the public interface. So we link with /// -fvisibility=hidden to default to hiding the symbols. See /// http://gcc.gnu.org/wiki/Visibility /// /// We solve this awful mess by defining these macros: /// /// OIIO_API - used for the OpenImageIO public API. Normally, assumes /// that it's being seen by a client of the library, and /// therefore declare as 'imported'. But if /// OpenImageIO_EXPORT is defined (as is done by CMake /// when compiling the library itself), change the /// declaration to 'exported'. /// OIIO_EXPORT - explicitly exports a symbol that isn't part of the /// public API but still needs to be visible. /// OIIO_LOCAL - explicitly hides a symbol that might otherwise be /// exported /// /// #if defined(_MSC_VER) || defined(__CYGWIN__) #ifndef OIIO_STATIC_BUILD #define OIIO_IMPORT __declspec(dllimport) #define OIIO_EXPORT __declspec(dllexport) #else #define OIIO_IMPORT #define OIIO_EXPORT #endif #define OIIO_LOCAL #else #if (10000*__GNUC__ + 100*__GNUC_MINOR__ + __GNUC_PATCHLEVEL__) > 40102 #define OIIO_IMPORT __attribute__ ((visibility ("default"))) #define OIIO_EXPORT __attribute__ ((visibility ("default"))) #define OIIO_LOCAL __attribute__ ((visibility ("hidden"))) #else #define OIIO_IMPORT #define OIIO_EXPORT #define OIIO_LOCAL #endif #endif #if defined(OpenImageIO_EXPORTS) || defined(OpenImageIO_Util_EXPORTS) # define OIIO_API OIIO_EXPORT #else # define OIIO_API OIIO_IMPORT #endif // Back compatibility macros (DEPRECATED) #define DLLPUBLIC OIIO_API #define DLLEXPORT OIIO_EXPORT #endif // OPENIMAGEIO_EXPORT_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/fmath.h0000644000175000017500000016161413151711064022114 0ustar mfvmfv/* Copyright 2008-2014 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) A few bits here are based upon code from NVIDIA that was also released under the same modified BSD license, and marked as: Copyright 2004 NVIDIA Corporation. All Rights Reserved. Some parts of this file were first open-sourced in Open Shading Language, then later moved here. The original copyright notice was: Copyright (c) 2009-2014 Sony Pictures Imageworks Inc., et al. Many of the math functions were copied from or inspired by other public domain sources or open source packages with compatible licenses. The individual functions give references were applicable. */ /// \file /// /// A variety of floating-point math helper routines (and, slight /// misnomer, some int stuff as well). /// #pragma once #include #include #include #include #include #include #include "oiioversion.h" /* Just for the OIIO_NAMESPACE stuff */ #include "platform.h" #include "dassert.h" #include "missing_math.h" #include "simd.h" OIIO_NAMESPACE_BEGIN /// Helper template to let us tell if two types are the same. template struct is_same { static const bool value = false; }; template struct is_same { static const bool value = true; }; //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// // // INTEGER HELPER FUNCTIONS // // A variety of handy functions that operate on integers. // /// Quick test for whether an integer is a power of 2. /// template inline bool ispow2 (T x) { // Numerous references for this bit trick are on the web. The // principle is that x is a power of 2 <=> x == 1< x-1 is // all 1 bits for bits < b. return (x & (x-1)) == 0 && (x >= 0); } /// Round up to next higher power of 2 (return x if it's already a power /// of 2). inline int pow2roundup (int x) { // Here's a version with no loops. if (x < 0) return 0; // Subtract 1, then round up to a power of 2, that way if we are // already a power of 2, we end up with the same number. --x; // Make all bits past the first 1 also be 1, i.e. 0001xxxx -> 00011111 x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16; // Now we have 2^n-1, by adding 1, we make it a power of 2 again return x+1; } /// Round down to next lower power of 2 (return x if it's already a power /// of 2). inline int pow2rounddown (int x) { // Make all bits past the first 1 also be 1, i.e. 0001xxxx -> 00011111 x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16; // Strip off all but the high bit, i.e. 00011111 -> 00010000 // That's the power of two <= the original x return x & ~(x >> 1); } /// Round value up to the next whole multiple. /// For example, round_to_multiple(7,10) returns 10. template inline V round_to_multiple (V value, M multiple) { return V (((value + V(multiple) - 1) / V(multiple)) * V(multiple)); } /// Round up to the next whole multiple of m, for the special case where /// m is definitely a power of 2 (somewhat simpler than the more general /// round_to_multiple). This is a template that should work for any // integer type. template inline T round_to_multiple_of_pow2 (T x, T m) { DASSERT (ispow2 (m)); return (x + m - 1) & (~(m-1)); } /// Multiply two unsigned 32-bit ints safely, carefully checking for /// overflow, and clamping to uint32_t's maximum value. inline uint32_t clamped_mult32 (uint32_t a, uint32_t b) { const uint32_t Err = std::numeric_limits::max(); uint64_t r = (uint64_t)a * (uint64_t)b; // Multiply into a bigger int return r < Err ? (uint32_t)r : Err; } /// Multiply two unsigned 64-bit ints safely, carefully checking for /// overflow, and clamping to uint64_t's maximum value. inline uint64_t clamped_mult64 (uint64_t a, uint64_t b) { uint64_t ab = a*b; if (b && ab/b != a) return std::numeric_limits::max(); else return ab; } /// Bitwise circular rotation left by k bits (for 32 bit unsigned integers) OIIO_FORCEINLINE uint32_t rotl32 (uint32_t x, int k) { return (x<>(32-k)); } /// Bitwise circular rotation left by k bits (for 64 bit unsigned integers) OIIO_FORCEINLINE uint64_t rotl64 (uint64_t x, int k) { return (x<>(64-k)); } // (end of integer helper functions) //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// // // FLOAT UTILITY FUNCTIONS // // Helper/utility functions: clamps, blends, interpolations... // /// clamp a to bounds [low,high]. template inline T clamp (const T& a, const T& low, const T& high) { return (a >= low) ? ((a <= high) ? a : high) : low; } // Specialization of clamp for float4 template<> inline simd::float4 clamp (const simd::float4& a, const simd::float4& low, const simd::float4& high) { return simd::min (high, simd::max (low, a)); } template<> inline simd::float8 clamp (const simd::float8& a, const simd::float8& low, const simd::float8& high) { return simd::min (high, simd::max (low, a)); } /// Fused multiply and add: (a*b + c) inline float madd (float a, float b, float c) { #if OIIO_FMA_ENABLED && (OIIO_CPLUSPLUS_VERSION >= 11) // C++11 defines std::fma, which we assume is implemented using an // intrinsic. return std::fma (a, b, c); #else // NOTE: GCC/ICC will turn this (for float) into a FMA unless // explicitly asked not to, clang will do so if -ffp-contract=fast. return a * b + c; #endif } /// Fused multiply and subtract: -(a*b - c) inline float msub (float a, float b, float c) { return a * b - c; // Hope for the best } /// Fused negative multiply and add: -(a*b) + c inline float nmadd (float a, float b, float c) { return c - (a * b); // Hope for the best } /// Negative fused multiply and subtract: -(a*b) - c inline float nmsub (float a, float b, float c) { return -(a * b) - c; // Hope for the best } /// Linearly interpolate values v0-v1 at x: v0*(1-x) + v1*x. /// This is a template, and so should work for any types. template inline T lerp (const T& v0, const T& v1, const Q& x) { // NOTE: a*(1-x) + b*x is much more numerically stable than a+x*(b-a) return v0*(Q(1)-x) + v1*x; } /// Bilinearly interoplate values v0-v3 (v0 upper left, v1 upper right, /// v2 lower left, v3 lower right) at coordinates (s,t) and return the /// result. This is a template, and so should work for any types. template inline T bilerp(const T& v0, const T& v1, const T& v2, const T& v3, const Q& s, const Q& t) { // NOTE: a*(t-1) + b*t is much more numerically stable than a+t*(b-a) Q s1 = Q(1) - s; return T ((Q(1)-t)*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)); } /// Bilinearly interoplate arrays of values v0-v3 (v0 upper left, v1 /// upper right, v2 lower left, v3 lower right) at coordinates (s,t), /// storing the results in 'result'. These are all vectors, so do it /// for each of 'n' contiguous values (using the same s,t interpolants). template inline void bilerp (const T *v0, const T *v1, const T *v2, const T *v3, Q s, Q t, int n, T *result) { Q s1 = Q(1) - s; Q t1 = Q(1) - t; for (int i = 0; i < n; ++i) result[i] = T (t1*(v0[i]*s1 + v1[i]*s) + t*(v2[i]*s1 + v3[i]*s)); } /// Bilinearly interoplate arrays of values v0-v3 (v0 upper left, v1 /// upper right, v2 lower left, v3 lower right) at coordinates (s,t), /// SCALING the interpolated value by 'scale' and then ADDING to /// 'result'. These are all vectors, so do it for each of 'n' /// contiguous values (using the same s,t interpolants). template inline void bilerp_mad (const T *v0, const T *v1, const T *v2, const T *v3, Q s, Q t, Q scale, int n, T *result) { Q s1 = Q(1) - s; Q t1 = Q(1) - t; for (int i = 0; i < n; ++i) result[i] += T (scale * (t1*(v0[i]*s1 + v1[i]*s) + t*(v2[i]*s1 + v3[i]*s))); } /// Trilinearly interoplate arrays of values v0-v7 (v0 upper left top, v1 /// upper right top, ...) at coordinates (s,t,r), and return the /// result. This is a template, and so should work for any types. template inline T trilerp (T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7, Q s, Q t, Q r) { // NOTE: a*(t-1) + b*t is much more numerically stable than a+t*(b-a) Q s1 = Q(1) - s; Q t1 = Q(1) - t; Q r1 = Q(1) - r; return T (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) + r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s))); } /// Trilinearly interoplate arrays of values v0-v7 (v0 upper left top, v1 /// upper right top, ...) at coordinates (s,t,r), /// storing the results in 'result'. These are all vectors, so do it /// for each of 'n' contiguous values (using the same s,t,r interpolants). template inline void trilerp (const T *v0, const T *v1, const T *v2, const T *v3, const T *v4, const T *v5, const T *v6, const T *v7, Q s, Q t, Q r, int n, T *result) { Q s1 = Q(1) - s; Q t1 = Q(1) - t; Q r1 = Q(1) - r; for (int i = 0; i < n; ++i) result[i] = T (r1*(t1*(v0[i]*s1 + v1[i]*s) + t*(v2[i]*s1 + v3[i]*s)) + r*(t1*(v4[i]*s1 + v5[i]*s) + t*(v6[i]*s1 + v7[i]*s))); } /// Trilinearly interoplate arrays of values v0-v7 (v0 upper left top, v1 /// upper right top, ...) at coordinates (s,t,r), /// SCALING the interpolated value by 'scale' and then ADDING to /// 'result'. These are all vectors, so do it for each of 'n' /// contiguous values (using the same s,t,r interpolants). template inline void trilerp_mad (const T *v0, const T *v1, const T *v2, const T *v3, const T *v4, const T *v5, const T *v6, const T *v7, Q s, Q t, Q r, Q scale, int n, T *result) { Q r1 = Q(1) - r; bilerp_mad (v0, v1, v2, v3, s, t, scale*r1, n, result); bilerp_mad (v4, v5, v6, v7, s, t, scale*r, n, result); } /// Evaluate B-spline weights in w[0..3] for the given fraction. This /// is an important component of performing a cubic interpolation. template inline void evalBSplineWeights (T w[4], T fraction) { T one_frac = 1 - fraction; w[0] = T(1.0 / 6.0) * one_frac * one_frac * one_frac; w[1] = T(2.0 / 3.0) - T(0.5) * fraction * fraction * (2 - fraction); w[2] = T(2.0 / 3.0) - T(0.5) * one_frac * one_frac * (2 - one_frac); w[3] = T(1.0 / 6.0) * fraction * fraction * fraction; } /// Evaluate B-spline derivative weights in w[0..3] for the given /// fraction. This is an important component of performing a cubic /// interpolation with derivatives. template inline void evalBSplineWeightDerivs (T dw[4], T fraction) { T one_frac = 1 - fraction; dw[0] = -T(0.5) * one_frac * one_frac; dw[1] = T(0.5) * fraction * (3 * fraction - 4); dw[2] = -T(0.5) * one_frac * (3 * one_frac - 4); dw[3] = T(0.5) * fraction * fraction; } /// Bicubically interoplate arrays of pointers arranged in a 4x4 pattern /// with val[0] pointing to the data in the upper left corner, val[15] /// pointing to the lower right) at coordinates (s,t), storing the /// results in 'result'. These are all vectors, so do it for each of /// 'n' contiguous values (using the same s,t interpolants). template inline void bicubic_interp (const T **val, T s, T t, int n, T *result) { for (int c = 0; c < n; ++c) result[c] = T(0); T wx[4]; evalBSplineWeights (wx, s); T wy[4]; evalBSplineWeights (wy, t); for (int j = 0; j < 4; ++j) { for (int i = 0; i < 4; ++i) { T w = wx[i] * wy[j]; for (int c = 0; c < n; ++c) result[c] += w * val[j*4+i][c]; } } } /// Return floor(x) as an int, as efficiently as possible. inline int ifloor (float x) { // Find the greatest whole number <= x. This cast is faster than // calling floorf. return (int) x - (x < 0.0f ? 1 : 0); } /// Return (x-floor(x)) and put (int)floor(x) in *xint. This is similar /// to the built-in modf, but returns a true int, always rounds down /// (compared to modf which rounds toward 0), and always returns /// frac >= 0 (comapred to modf which can return <0 if x<0). inline float floorfrac (float x, int *xint) { int i = ifloor(x); *xint = i; return x - static_cast(i); // Return the fraction left over } /// Convert degrees to radians. template inline T radians (T deg) { return deg * T(M_PI / 180.0); } /// Convert radians to degrees template inline T degrees (T rad) { return rad * T(180.0 / M_PI); } inline void sincos (float x, float* sine, float* cosine) { #if defined(__GNUC__) && defined(__linux__) && !defined(__clang__) __builtin_sincosf(x, sine, cosine); #else *sine = std::sin(x); *cosine = std::cos(x); #endif } inline void sincos (double x, double* sine, double* cosine) { #if defined(__GNUC__) && defined(__linux__) && !defined(__clang__) __builtin_sincos(x, sine, cosine); #else *sine = std::sin(x); *cosine = std::cos(x); #endif } // (end of float helper functions) //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// // // CONVERSION // // Type and range conversion helper functions and classes. template inline OUT_TYPE bit_cast (const IN_TYPE in) { // NOTE: this is the only standards compliant way of doing this type of casting, // luckily the compilers we care about know how to optimize away this idiom. OUT_TYPE out; memcpy (&out, &in, sizeof(IN_TYPE)); return out; } inline int bitcast_to_int (float x) { return bit_cast(x); } inline float bitcast_to_float (int x) { return bit_cast(x); } /// Change endian-ness of one or more data items that are each 2, 4, /// or 8 bytes. This should work for any of short, unsigned short, int, /// unsigned int, float, long long, pointers. template inline void swap_endian (T *f, int len=1) { for (char *c = (char *) f; len--; c += sizeof(T)) { if (sizeof(T) == 2) { std::swap (c[0], c[1]); } else if (sizeof(T) == 4) { std::swap (c[0], c[3]); std::swap (c[1], c[2]); } else if (sizeof(T) == 8) { std::swap (c[0], c[7]); std::swap (c[1], c[6]); std::swap (c[2], c[5]); std::swap (c[3], c[4]); } } } // big_enough_float::float_t is a floating-point type big enough to // handle the range and precision of a . It's a float, unless T is big. template struct big_enough_float { typedef float float_t; }; template<> struct big_enough_float { typedef double float_t; }; template<> struct big_enough_float { typedef double float_t; }; template<> struct big_enough_float { typedef double float_t; }; template<> struct big_enough_float { typedef double float_t; }; template<> struct big_enough_float { typedef double float_t; }; /// Multiply src by scale, clamp to [min,max], and round to the nearest D /// (presumed to be integer). This is just a helper for the convert_type /// templates, it probably has no other use. template inline D scaled_conversion (const S &src, F scale, F min, F max) { if (std::numeric_limits::is_signed) { F s = src * scale; s += (s < 0 ? (F)-0.5 : (F)0.5); return (D) clamp (s, min, max); } else { return (D) clamp ((F)src * scale + (F)0.5, min, max); } } /// Convert n consecutive values from the type of S to the type of D. /// The conversion is not a simple cast, but correctly remaps the /// 0.0->1.0 range from and to the full positive range of integral /// types. Take a memcpy shortcut if both types are the same and no /// conversion is necessary. Optional arguments can give nonstandard /// quantizations. // // FIXME: make table-based specializations for common types with only a // few possible src values (like unsigned char -> float). template void convert_type (const S *src, D *dst, size_t n, D _min, D _max) { if (is_same::value) { // They must be the same type. Just memcpy. memcpy (dst, src, n*sizeof(D)); return; } typedef typename big_enough_float::float_t F; F scale = std::numeric_limits::is_integer ? ((F)1.0)/std::numeric_limits::max() : (F)1.0; if (std::numeric_limits::is_integer) { // Converting to an integer-like type. F min = (F)_min; // std::numeric_limits::min(); F max = (F)_max; // std::numeric_limits::max(); scale *= _max; // Unroll loop for speed for ( ; n >= 16; n -= 16) { *dst++ = scaled_conversion (*src++, scale, min, max); *dst++ = scaled_conversion (*src++, scale, min, max); *dst++ = scaled_conversion (*src++, scale, min, max); *dst++ = scaled_conversion (*src++, scale, min, max); *dst++ = scaled_conversion (*src++, scale, min, max); *dst++ = scaled_conversion (*src++, scale, min, max); *dst++ = scaled_conversion (*src++, scale, min, max); *dst++ = scaled_conversion (*src++, scale, min, max); *dst++ = scaled_conversion (*src++, scale, min, max); *dst++ = scaled_conversion (*src++, scale, min, max); *dst++ = scaled_conversion (*src++, scale, min, max); *dst++ = scaled_conversion (*src++, scale, min, max); *dst++ = scaled_conversion (*src++, scale, min, max); *dst++ = scaled_conversion (*src++, scale, min, max); *dst++ = scaled_conversion (*src++, scale, min, max); *dst++ = scaled_conversion (*src++, scale, min, max); } while (n--) *dst++ = scaled_conversion (*src++, scale, min, max); } else { // Converting to a float-like type, so we don't need to remap // the range // Unroll loop for speed for ( ; n >= 16; n -= 16) { *dst++ = (D)((*src++) * scale); *dst++ = (D)((*src++) * scale); *dst++ = (D)((*src++) * scale); *dst++ = (D)((*src++) * scale); *dst++ = (D)((*src++) * scale); *dst++ = (D)((*src++) * scale); *dst++ = (D)((*src++) * scale); *dst++ = (D)((*src++) * scale); *dst++ = (D)((*src++) * scale); *dst++ = (D)((*src++) * scale); *dst++ = (D)((*src++) * scale); *dst++ = (D)((*src++) * scale); *dst++ = (D)((*src++) * scale); *dst++ = (D)((*src++) * scale); *dst++ = (D)((*src++) * scale); *dst++ = (D)((*src++) * scale); } while (n--) *dst++ = (D)((*src++) * scale); } } template<> inline void convert_type (const uint8_t *src, float *dst, size_t n, float _min, float _max) { float scale (1.0f/std::numeric_limits::max()); simd::float4 scale_simd (scale); for ( ; n >= 4; n -= 4, src += 4, dst += 4) { simd::float4 s_simd (src); simd::float4 d_simd = s_simd * scale_simd; d_simd.store (dst); } while (n--) *dst++ = (*src++) * scale; } template<> inline void convert_type (const uint16_t *src, float *dst, size_t n, float _min, float _max) { float scale (1.0f/std::numeric_limits::max()); simd::float4 scale_simd (scale); for ( ; n >= 4; n -= 4, src += 4, dst += 4) { simd::float4 s_simd (src); simd::float4 d_simd = s_simd * scale_simd; d_simd.store (dst); } while (n--) *dst++ = (*src++) * scale; } #ifdef _HALF_H_ template<> inline void convert_type (const half *src, float *dst, size_t n, float _min, float _max) { for ( ; n >= 4; n -= 4, src += 4, dst += 4) { simd::float4 s_simd (src); s_simd.store (dst); } while (n--) *dst++ = (*src++); } #endif template<> inline void convert_type (const float *src, uint16_t *dst, size_t n, uint16_t _min, uint16_t _max) { float min = std::numeric_limits::min(); float max = std::numeric_limits::max(); float scale = max; simd::float4 max_simd (max); simd::float4 one_half_simd (0.5f); simd::float4 zero_simd (0.0f); for ( ; n >= 4; n -= 4, src += 4, dst += 4) { simd::float4 scaled = simd::round (simd::float4(src) * max_simd); simd::float4 clamped = clamp (scaled, zero_simd, max_simd); simd::int4 i (clamped); i.store (dst); } while (n--) *dst++ = scaled_conversion (*src++, scale, min, max); } template<> inline void convert_type (const float *src, uint8_t *dst, size_t n, uint8_t _min, uint8_t _max) { float min = std::numeric_limits::min(); float max = std::numeric_limits::max(); float scale = max; simd::float4 max_simd (max); simd::float4 one_half_simd (0.5f); simd::float4 zero_simd (0.0f); for ( ; n >= 4; n -= 4, src += 4, dst += 4) { simd::float4 scaled = simd::round (simd::float4(src) * max_simd); simd::float4 clamped = clamp (scaled, zero_simd, max_simd); simd::int4 i (clamped); i.store (dst); } while (n--) *dst++ = scaled_conversion (*src++, scale, min, max); } #ifdef _HALF_H_ template<> inline void convert_type (const float *src, half *dst, size_t n, half _min, half _max) { for ( ; n >= 4; n -= 4, src += 4, dst += 4) { simd::float4 s (src); s.store (dst); } while (n--) *dst++ = *src++; } #endif template inline void convert_type (const S *src, D *dst, size_t n) { convert_type (src, dst, n, std::numeric_limits::min(), std::numeric_limits::max()); } /// Convert a single value from the type of S to the type of D. /// The conversion is not a simple cast, but correctly remaps the /// 0.0->1.0 range from and to the full positive range of integral /// types. Take a copy shortcut if both types are the same and no /// conversion is necessary. template inline D convert_type (const S &src) { if (is_same::value) { // They must be the same type. Just return it. return (D)src; } typedef typename big_enough_float::float_t F; F scale = std::numeric_limits::is_integer ? ((F)1.0)/std::numeric_limits::max() : (F)1.0; if (std::numeric_limits::is_integer) { // Converting to an integer-like type. F min = (F) std::numeric_limits::min(); F max = (F) std::numeric_limits::max(); scale *= max; return scaled_conversion (src, scale, min, max); } else { // Converting to a float-like type, so we don't need to remap // the range return (D)((F)src * scale); } } /// Helper function to convert channel values between different bit depths. /// Roughly equivalent to: /// /// out = round (in * (pow (2, TO_BITS) - 1) / (pow (2, FROM_BITS) - 1)); /// /// but utilizes an integer math trick for speed. It can be proven that the /// absolute error of this method is less or equal to 1, with an average error /// (with respect to the entire domain) below 0.2. /// /// It is assumed that the original value is a valid FROM_BITS integer, i.e. /// shifted fully to the right. template inline unsigned int bit_range_convert(unsigned int in) { unsigned int out = 0; int shift = TO_BITS - FROM_BITS; for (; shift > 0; shift -= FROM_BITS) out |= in << shift; out |= in >> -shift; return out; } // non-templated version. Slow but general inline unsigned int bit_range_convert(unsigned int in, unsigned int FROM_BITS, unsigned int TO_BITS) { unsigned int out = 0; int shift = TO_BITS - FROM_BITS; for (; shift > 0; shift -= FROM_BITS) out |= in << shift; out |= in >> -shift; return out; } /// A DataProxy looks like an (E &), but it really holds an (I &) /// and does conversions (via convert_type) as it reads in and out. /// (I and E are for INTERNAL and EXTERNAL data types, respectively). template struct DataProxy { DataProxy (I &data) : m_data(data) { } E operator= (E newval) { m_data = convert_type(newval); return newval; } operator E () const { return convert_type(m_data); } private: DataProxy& operator = (const DataProxy&); // Do not implement I &m_data; }; /// A ConstDataProxy looks like a (const E &), but it really holds /// a (const I &) and does conversions (via convert_type) as it reads. /// (I and E are for INTERNAL and EXTERNAL data types, respectively). template struct ConstDataProxy { ConstDataProxy (const I &data) : m_data(data) { } operator E () const { return convert_type(*m_data); } private: const I &m_data; }; /// A DataArrayProxy looks like an (E *), but it really holds an (I *) /// and does conversions (via convert_type) as it reads in and out. /// (I and E are for INTERNAL and EXTERNAL data types, respectively). template struct DataArrayProxy { DataArrayProxy (I *data=NULL) : m_data(data) { } E operator* () const { return convert_type(*m_data); } E operator[] (int i) const { return convert_type(m_data[i]); } DataProxy operator[] (int i) { return DataProxy (m_data[i]); } void set (I *data) { m_data = data; } I * get () const { return m_data; } const DataArrayProxy & operator+= (int i) { m_data += i; return *this; } private: I *m_data; }; /// A ConstDataArrayProxy looks like an (E *), but it really holds an /// (I *) and does conversions (via convert_type) as it reads in and out. /// (I and E are for INTERNAL and EXTERNAL data types, respectively). template struct ConstDataArrayProxy { ConstDataArrayProxy (const I *data=NULL) : m_data(data) { } E operator* () const { return convert_type(*m_data); } E operator[] (int i) const { return convert_type(m_data[i]); } void set (const I *data) { m_data = data; } const I * get () const { return m_data; } const ConstDataArrayProxy & operator+= (int i) { m_data += i; return *this; } private: const I *m_data; }; /// Fast table-based conversion of 8-bit to other types. Declare this /// as static to avoid the expensive ctr being called all the time. template class EightBitConverter { public: EightBitConverter () { init(); } T operator() (unsigned char c) const { return val[c]; } private: T val[256]; void init () { float scale = 1.0f / 255.0f; if (std::numeric_limits::is_integer) scale *= (float)std::numeric_limits::max(); for (int i = 0; i < 256; ++i) val[i] = (T)(i * scale); } }; /// Simple conversion of a (presumably non-negative) float into a /// rational. This does not attempt to find the simplest fraction /// that approximates the float, for example 52.83 will simply /// return 5283/100. This does not attempt to gracefully handle /// floats that are out of range that could be easily int/int. inline void float_to_rational (float f, unsigned int &num, unsigned int &den) { if (f <= 0) { // Trivial case of zero, and handle all negative values num = 0; den = 1; } else if ((int)(1.0/f) == (1.0/f)) { // Exact results for perfect inverses num = 1; den = (int)f; } else { num = (int)f; den = 1; while (fabsf(f-static_cast(num)) > 0.00001f && den < 1000000) { den *= 10; f *= 10; num = (int)f; } } } /// Simple conversion of a float into a rational. This does not attempt /// to find the simplest fraction that approximates the float, for /// example 52.83 will simply return 5283/100. This does not attempt to /// gracefully handle floats that are out of range that could be easily /// int/int. inline void float_to_rational (float f, int &num, int &den) { unsigned int n, d; float_to_rational (fabsf(f), n, d); num = (f >= 0) ? (int)n : -(int)n; den = (int) d; } // (end of conversion helpers) //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// // // SAFE MATH // // The functions named "safe_*" are versions with various internal clamps // or other deviations from IEEE standards with the specific intent of // never producing NaN or Inf values or throwing exceptions. But within the // valid range, they should be full precision and match IEEE standards. // /// Safe (clamping) sqrt: safe_sqrt(x<0) returns 0, not NaN. template inline T safe_sqrt (T x) { return x >= T(0) ? std::sqrt(x) : T(0); } /// Safe (clamping) inverse sqrt: safe_inversesqrt(x<=0) returns 0. template inline T safe_inversesqrt (T x) { return x > T(0) ? T(1) / std::sqrt(x) : T(0); } /// Safe (clamping) arcsine: clamp to the valid domain. template inline T safe_asin (T x) { if (x <= T(-1)) return T(-M_PI_2); if (x >= T(+1)) return T(+M_PI_2); return std::asin(x); } /// Safe (clamping) arccosine: clamp to the valid domain. template inline T safe_acos (T x) { if (x <= T(-1)) return T(M_PI); if (x >= T(+1)) return T(0); return std::acos(x); } /// Safe log2: clamp to valid domain. template inline T safe_log2 (T x) { // match clamping from fast version if (x < std::numeric_limits::min()) x = std::numeric_limits::min(); if (x > std::numeric_limits::max()) x = std::numeric_limits::max(); #if OIIO_CPLUSPLUS_VERSION >= 11 return std::log2(x); #else return log2f(x); // punt: just use the float one #endif } /// Safe log: clamp to valid domain. template inline T safe_log (T x) { // slightly different than fast version since clamping happens before scaling if (x < std::numeric_limits::min()) x = std::numeric_limits::min(); if (x > std::numeric_limits::max()) x = std::numeric_limits::max(); return std::log(x); } /// Safe log10: clamp to valid domain. template inline T safe_log10 (T x) { // slightly different than fast version since clamping happens before scaling if (x < std::numeric_limits::min()) x = std::numeric_limits::min(); if (x > std::numeric_limits::max()) x = std::numeric_limits::max(); return log10f(x); } /// Safe logb: clamp to valid domain. template inline T safe_logb (T x) { #if OIIO_CPLUSPLUS_VERSION >= 11 return (x != T(0)) ? std::logb(x) : -std::numeric_limits::max(); #else return (x != T(0)) ? logbf(x) : -std::numeric_limits::max(); #endif } /// Safe pow: clamp the domain so it never returns Inf or NaN or has divide /// by zero error. template inline T safe_pow (T x, T y) { if (y == T(0)) return T(1); if (x == T(0)) return T(0); // if x is negative, only deal with integer powers if ((x < T(0)) && (y != floor(y))) return T(0); // FIXME: this does not match "fast" variant because clamping limits are different T r = std::pow(x, y); // Clamp to avoid returning Inf. const T big = std::numeric_limits::max(); return clamp (r, -big, big); } // (end of safe_* functions) //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// // // FAST & APPROXIMATE MATH // // The functions named "fast_*" provide a set of replacements to libm that // are much faster at the expense of some accuracy and robust handling of // extreme values. One design goal for these approximation was to avoid // branches as much as possible and operate on single precision values only // so that SIMD versions should be straightforward ports We also try to // implement "safe" semantics (ie: clamp to valid range where possible) // natively since wrapping these inline calls in another layer would be // wasteful. // // Some functions are fast_safe_*, which is both a faster approximation as // well as clamped input domain to ensure no NaN, Inf, or divide by zero. // /// Round to nearest integer, returning as an int. inline int fast_rint (float x) { // used by sin/cos/tan range reduction #if OIIO_SIMD_SSE >= 4 // single roundps instruction on SSE4.1+ (for gcc/clang at least) return static_cast(rintf(x)); #else // emulate rounding by adding/substracting 0.5 return static_cast(x + copysignf(0.5f, x)); #endif } inline simd::int4 fast_rint (const simd::float4& x) { return simd::rint (x); } inline float fast_sin (float x) { // very accurate argument reduction from SLEEF // starts failing around x=262000 // Results on: [-2pi,2pi] // Examined 2173837240 values of sin: 0.00662760244 avg ulp diff, 2 max ulp, 1.19209e-07 max error int q = fast_rint (x * float(M_1_PI)); float qf = q; x = madd(qf, -0.78515625f*4, x); x = madd(qf, -0.00024187564849853515625f*4, x); x = madd(qf, -3.7747668102383613586e-08f*4, x); x = madd(qf, -1.2816720341285448015e-12f*4, x); x = float(M_PI_2) - (float(M_PI_2) - x); // crush denormals float s = x * x; if ((q & 1) != 0) x = -x; // this polynomial approximation has very low error on [-pi/2,+pi/2] // 1.19209e-07 max error in total over [-2pi,+2pi] float u = 2.6083159809786593541503e-06f; u = madd(u, s, -0.0001981069071916863322258f); u = madd(u, s, +0.00833307858556509017944336f); u = madd(u, s, -0.166666597127914428710938f); u = madd(s, u * x, x); // For large x, the argument reduction can fail and the polynomial can be // evaluated with arguments outside the valid internal. Just clamp the bad // values away (setting to 0.0f means no branches need to be generated). if (fabsf(u) > 1.0f) u = 0.0f; return u; } inline float fast_cos (float x) { // same argument reduction as fast_sin int q = fast_rint (x * float(M_1_PI)); float qf = q; x = madd(qf, -0.78515625f*4, x); x = madd(qf, -0.00024187564849853515625f*4, x); x = madd(qf, -3.7747668102383613586e-08f*4, x); x = madd(qf, -1.2816720341285448015e-12f*4, x); x = float(M_PI_2) - (float(M_PI_2) - x); // crush denormals float s = x * x; // polynomial from SLEEF's sincosf, max error is // 4.33127e-07 over [-2pi,2pi] (98% of values are "exact") float u = -2.71811842367242206819355e-07f; u = madd(u, s, +2.47990446951007470488548e-05f); u = madd(u, s, -0.00138888787478208541870117f); u = madd(u, s, +0.0416666641831398010253906f); u = madd(u, s, -0.5f); u = madd(u, s, +1.0f); if ((q & 1) != 0) u = -u; if (fabsf(u) > 1.0f) u = 0.0f; return u; } inline void fast_sincos (float x, float* sine, float* cosine) { // same argument reduction as fast_sin int q = fast_rint (x * float(M_1_PI)); float qf = q; x = madd(qf, -0.78515625f*4, x); x = madd(qf, -0.00024187564849853515625f*4, x); x = madd(qf, -3.7747668102383613586e-08f*4, x); x = madd(qf, -1.2816720341285448015e-12f*4, x); x = float(M_PI_2) - (float(M_PI_2) - x); // crush denormals float s = x * x; // NOTE: same exact polynomials as fast_sin and fast_cos above if ((q & 1) != 0) x = -x; float su = 2.6083159809786593541503e-06f; su = madd(su, s, -0.0001981069071916863322258f); su = madd(su, s, +0.00833307858556509017944336f); su = madd(su, s, -0.166666597127914428710938f); su = madd(s, su * x, x); float cu = -2.71811842367242206819355e-07f; cu = madd(cu, s, +2.47990446951007470488548e-05f); cu = madd(cu, s, -0.00138888787478208541870117f); cu = madd(cu, s, +0.0416666641831398010253906f); cu = madd(cu, s, -0.5f); cu = madd(cu, s, +1.0f); if ((q & 1) != 0) cu = -cu; if (fabsf(su) > 1.0f) su = 0.0f; if (fabsf(cu) > 1.0f) cu = 0.0f; *sine = su; *cosine = cu; } // NOTE: this approximation is only valid on [-8192.0,+8192.0], it starts becoming // really poor outside of this range because the reciprocal amplifies errors inline float fast_tan (float x) { // derived from SLEEF implementation // note that we cannot apply the "denormal crush" trick everywhere because // we sometimes need to take the reciprocal of the polynomial int q = fast_rint (x * float(2 * M_1_PI)); float qf = q; x = madd(qf, -0.78515625f*2, x); x = madd(qf, -0.00024187564849853515625f*2, x); x = madd(qf, -3.7747668102383613586e-08f*2, x); x = madd(qf, -1.2816720341285448015e-12f*2, x); if ((q & 1) == 0) x = float(M_PI_4) - (float(M_PI_4) - x); // crush denormals (only if we aren't inverting the result later) float s = x * x; float u = 0.00927245803177356719970703f; u = madd(u, s, 0.00331984995864331722259521f); u = madd(u, s, 0.0242998078465461730957031f); u = madd(u, s, 0.0534495301544666290283203f); u = madd(u, s, 0.133383005857467651367188f); u = madd(u, s, 0.333331853151321411132812f); u = madd(s, u * x, x); if ((q & 1) != 0) u = -1.0f / u; return u; } /// Fast, approximate sin(x*M_PI) with maximum absolute error of 0.000918954611. /// Adapted from http://devmaster.net/posts/9648/fast-and-accurate-sine-cosine#comment-76773 /// Note that this is MUCH faster, but much less accurate than fast_sin. inline float fast_sinpi (float x) { // Fast trick to strip the integral part off, so our domain is [-1,1] const float z = x - ((x + 25165824.0f) - 25165824.0f); const float y = z - z * fabsf(z); const float Q = 3.10396624f; const float P = 3.584135056f; // P = 16-4*Q return y * (Q + P * fabsf(y)); /* The original article used used inferior constants for Q and P and * so had max error 1.091e-3. * * The optimal value for Q was determined by exhaustive search, minimizing * the absolute numerical error relative to float(std::sin(double(phi*M_PI))) * over the interval [0,2] (which is where most of the invocations happen). * * The basic idea of this approximation starts with the coarse approximation: * sin(pi*x) ~= f(x) = 4 * (x - x * abs(x)) * * This approximation always _over_ estimates the target. On the otherhand, the * curve: * sin(pi*x) ~= f(x) * abs(f(x)) / 4 * * always lies _under_ the target. Thus we can simply numerically search for the * optimal constant to LERP these curves into a more precise approximation. * After folding the constants together and simplifying the resulting math, we * end up with the compact implementation below. * * NOTE: this function actually computes sin(x * pi) which avoids one or two * mults in many cases and guarantees exact values at integer periods. */ } /// Fast approximate cos(x*M_PI) with ~0.1% absolute error. /// Note that this is MUCH faster, but much less accurate than fast_cos. inline float fast_cospi (float x) { return fast_sinpi (x+0.5f); } inline float fast_acos (float x) { const float f = fabsf(x); const float m = (f < 1.0f) ? 1.0f - (1.0f - f) : 1.0f; // clamp and crush denormals // based on http://www.pouet.net/topic.php?which=9132&page=2 // 85% accurate (ulp 0) // Examined 2130706434 values of acos: 15.2000597 avg ulp diff, 4492 max ulp, 4.51803e-05 max error // without "denormal crush" // Examined 2130706434 values of acos: 15.2007108 avg ulp diff, 4492 max ulp, 4.51803e-05 max error // with "denormal crush" const float a = sqrtf(1.0f - m) * (1.5707963267f + m * (-0.213300989f + m * (0.077980478f + m * -0.02164095f))); return x < 0 ? float(M_PI) - a : a; } inline float fast_asin (float x) { // based on acosf approximation above // max error is 4.51133e-05 (ulps are higher because we are consistently off by a little amount) const float f = fabsf(x); const float m = (f < 1.0f) ? 1.0f - (1.0f - f) : 1.0f; // clamp and crush denormals const float a = float(M_PI_2) - sqrtf(1.0f - m) * (1.5707963267f + m * (-0.213300989f + m * (0.077980478f + m * -0.02164095f))); return copysignf(a, x); } inline float fast_atan (float x) { const float a = fabsf(x); const float k = a > 1.0f ? 1 / a : a; const float s = 1.0f - (1.0f - k); // crush denormals const float t = s * s; // http://mathforum.org/library/drmath/view/62672.html // Examined 4278190080 values of atan: 2.36864877 avg ulp diff, 302 max ulp, 6.55651e-06 max error // (with denormals) // Examined 4278190080 values of atan: 171160502 avg ulp diff, 855638016 max ulp, 6.55651e-06 max error // (crush denormals) float r = s * madd(0.43157974f, t, 1.0f) / madd(madd(0.05831938f, t, 0.76443945f), t, 1.0f); if (a > 1.0f) r = 1.570796326794896557998982f - r; return copysignf(r, x); } inline float fast_atan2 (float y, float x) { // based on atan approximation above // the special cases around 0 and infinity were tested explicitly // the only case not handled correctly is x=NaN,y=0 which returns 0 instead of nan const float a = fabsf(x); const float b = fabsf(y); const float k = (b == 0) ? 0.0f : ((a == b) ? 1.0f : (b > a ? a / b : b / a)); const float s = 1.0f - (1.0f - k); // crush denormals const float t = s * s; float r = s * madd(0.43157974f, t, 1.0f) / madd(madd(0.05831938f, t, 0.76443945f), t, 1.0f); if (b > a) r = 1.570796326794896557998982f - r; // account for arg reduction if (bit_cast(x) & 0x80000000u) // test sign bit of x r = float(M_PI) - r; return copysignf(r, y); } template inline T fast_log2 (const T& xval) { using namespace simd; typedef typename T::int_t intN; // See float fast_log2 for explanations T x = clamp (xval, T(std::numeric_limits::min()), T(std::numeric_limits::max())); intN bits = bitcast_to_int(x); intN exponent = srl (bits, 23) - intN(127); T f = bitcast_to_float ((bits & intN(0x007FFFFF)) | intN(0x3f800000)) - T(1.0f); T f2 = f * f; T f4 = f2 * f2; T hi = madd(f, T(-0.00931049621349f), T( 0.05206469089414f)); T lo = madd(f, T( 0.47868480909345f), T(-0.72116591947498f)); hi = madd(f, hi, T(-0.13753123777116f)); hi = madd(f, hi, T( 0.24187369696082f)); hi = madd(f, hi, T(-0.34730547155299f)); lo = madd(f, lo, T( 1.442689881667200f)); return ((f4 * hi) + (f * lo)) + T(exponent); } template<> inline float fast_log2 (const float& xval) { // NOTE: clamp to avoid special cases and make result "safe" from large negative values/nans float x = clamp (xval, std::numeric_limits::min(), std::numeric_limits::max()); // based on https://github.com/LiraNuna/glsl-sse2/blob/master/source/vec4.h unsigned bits = bit_cast(x); int exponent = int(bits >> 23) - 127; float f = bit_cast((bits & 0x007FFFFF) | 0x3f800000) - 1.0f; // Examined 2130706432 values of log2 on [1.17549435e-38,3.40282347e+38]: 0.0797524457 avg ulp diff, 3713596 max ulp, 7.62939e-06 max error // ulp histogram: // 0 = 97.46% // 1 = 2.29% // 2 = 0.11% float f2 = f * f; float f4 = f2 * f2; float hi = madd(f, -0.00931049621349f, 0.05206469089414f); float lo = madd(f, 0.47868480909345f, -0.72116591947498f); hi = madd(f, hi, -0.13753123777116f); hi = madd(f, hi, 0.24187369696082f); hi = madd(f, hi, -0.34730547155299f); lo = madd(f, lo, 1.442689881667200f); return ((f4 * hi) + (f * lo)) + exponent; } template inline T fast_log (const T& x) { // Examined 2130706432 values of logf on [1.17549435e-38,3.40282347e+38]: 0.313865375 avg ulp diff, 5148137 max ulp, 7.62939e-06 max error return fast_log2(x) * T(M_LN2); } template inline T fast_log10 (const T& x) { // Examined 2130706432 values of log10f on [1.17549435e-38,3.40282347e+38]: 0.631237033 avg ulp diff, 4471615 max ulp, 3.8147e-06 max error return fast_log2(x) * T(M_LN2 / M_LN10); } inline float fast_logb (float x) { // don't bother with denormals x = fabsf(x); if (x < std::numeric_limits::min()) x = std::numeric_limits::min(); if (x > std::numeric_limits::max()) x = std::numeric_limits::max(); unsigned bits = bit_cast(x); return float (int(bits >> 23) - 127); } inline float fast_log1p (float x) { if (fabsf(x) < 0.01f) { float y = 1.0f - (1.0f - x); // crush denormals return copysignf(madd(-0.5f, y * y, y), x); } else { return fast_log(x + 1); } } template inline T fast_exp2 (const T& xval) { using namespace simd; typedef typename T::int_t intN; #if OIIO_SIMD_SSE // See float specialization for explanations T x = clamp (xval, T(-126.0f), T(126.0f)); intN m (x); x -= T(m); T one (1.0f); x = one - (one - x); // crush denormals (does not affect max ulps!) const T kA (1.33336498402e-3f); const T kB (9.810352697968e-3f); const T kC (5.551834031939e-2f); const T kD (0.2401793301105f); const T kE (0.693144857883f); T r (kA); r = madd(x, r, kB); r = madd(x, r, kC); r = madd(x, r, kD); r = madd(x, r, kE); r = madd(x, r, one); return bitcast_to_float (bitcast_to_int(r) + (m << 23)); #else T r; for (int i = 0; i < r.elements; ++i) r[i] = fast_exp2(xval[i]); for (int i = r.elements; i < r.paddedelements; ++i) r[i] = 0.0f; return r; #endif } template<> inline float fast_exp2 (const float& xval) { // clamp to safe range for final addition float x = clamp (xval, -126.0f, 126.0f); // range reduction int m = int(x); x -= m; x = 1.0f - (1.0f - x); // crush denormals (does not affect max ulps!) // 5th degree polynomial generated with sollya // Examined 2247622658 values of exp2 on [-126,126]: 2.75764912 avg ulp diff, 232 max ulp // ulp histogram: // 0 = 87.81% // 1 = 4.18% float r = 1.33336498402e-3f; r = madd(x, r, 9.810352697968e-3f); r = madd(x, r, 5.551834031939e-2f); r = madd(x, r, 0.2401793301105f); r = madd(x, r, 0.693144857883f); r = madd(x, r, 1.0f); // multiply by 2 ^ m by adding in the exponent // NOTE: left-shift of negative number is undefined behavior return bit_cast(bit_cast(r) + (unsigned(m) << 23)); } template inline T fast_exp (const T& x) { // Examined 2237485550 values of exp on [-87.3300018,87.3300018]: 2.6666452 avg ulp diff, 230 max ulp return fast_exp2(x * T(1 / M_LN2)); } /// Faster float exp than is in libm, but still 100% accurate inline float fast_correct_exp (float x) { #if defined(__x86_64__) && defined(__GNU_LIBRARY__) && defined(__GLIBC__ ) && defined(__GLIBC_MINOR__) && __GLIBC__ <= 2 && __GLIBC_MINOR__ < 16 // On x86_64, versions of glibc < 2.16 have an issue where expf is // much slower than the double version. This was fixed in glibc 2.16. return static_cast(std::exp(static_cast(x))); #else return std::exp(x); #endif } inline float fast_exp10 (float x) { // Examined 2217701018 values of exp10 on [-37.9290009,37.9290009]: 2.71732409 avg ulp diff, 232 max ulp return fast_exp2(x * float(M_LN10 / M_LN2)); } inline float fast_expm1 (float x) { if (fabsf(x) < 0.03f) { float y = 1.0f - (1.0f - x); // crush denormals return copysignf(madd(0.5f, y * y, y), x); } else return fast_exp(x) - 1.0f; } inline float fast_sinh (float x) { float a = fabsf(x); if (a > 1.0f) { // Examined 53389559 values of sinh on [1,87.3300018]: 33.6886442 avg ulp diff, 178 max ulp float e = fast_exp(a); return copysignf(0.5f * e - 0.5f / e, x); } else { a = 1.0f - (1.0f - a); // crush denorms float a2 = a * a; // degree 7 polynomial generated with sollya // Examined 2130706434 values of sinh on [-1,1]: 1.19209e-07 max error float r = 2.03945513931e-4f; r = madd(r, a2, 8.32990277558e-3f); r = madd(r, a2, 0.1666673421859f); r = madd(r * a, a2, a); return copysignf(r, x); } } inline float fast_cosh (float x) { // Examined 2237485550 values of cosh on [-87.3300018,87.3300018]: 1.78256726 avg ulp diff, 178 max ulp float e = fast_exp(fabsf(x)); return 0.5f * e + 0.5f / e; } inline float fast_tanh (float x) { // Examined 4278190080 values of tanh on [-3.40282347e+38,3.40282347e+38]: 3.12924e-06 max error // NOTE: ulp error is high because of sub-optimal handling around the origin float e = fast_exp(2.0f * fabsf(x)); return copysignf(1 - 2 / (1 + e), x); } inline float fast_safe_pow (float x, float y) { if (y == 0) return 1.0f; // x^0=1 if (x == 0) return 0.0f; // 0^y=0 // be cheap & exact for special case of squaring and identity if (y == 1.0f) return x; if (y == 2.0f) return std::min (x*x, std::numeric_limits::max()); float sign = 1.0f; if (x < 0) { // if x is negative, only deal with integer powers // powf returns NaN for non-integers, we will return 0 instead int ybits = bit_cast(y) & 0x7fffffff; if (ybits >= 0x4b800000) { // always even int, keep positive } else if (ybits >= 0x3f800000) { // bigger than 1, check int k = (ybits >> 23) - 127; // get exponent int j = ybits >> (23 - k); // shift out possible fractional bits if ((j << (23 - k)) == ybits) // rebuild number and check for a match sign = bit_cast(0x3f800000 | (j << 31)); // +1 for even, -1 for odd else return 0.0f; // not integer } else { return 0.0f; // not integer } } return sign * fast_exp2(y * fast_log2(fabsf(x))); } // Fast simd pow that only needs to work for positive x template inline T fast_pow_pos (const T& x, const U& y) { return fast_exp2(y * fast_log2(x)); } inline float fast_erf (float x) { // Examined 1082130433 values of erff on [0,4]: 1.93715e-06 max error // Abramowitz and Stegun, 7.1.28 const float a1 = 0.0705230784f; const float a2 = 0.0422820123f; const float a3 = 0.0092705272f; const float a4 = 0.0001520143f; const float a5 = 0.0002765672f; const float a6 = 0.0000430638f; const float a = fabsf(x); const float b = 1.0f - (1.0f - a); // crush denormals const float r = madd(madd(madd(madd(madd(madd(a6, b, a5), b, a4), b, a3), b, a2), b, a1), b, 1.0f); const float s = r * r; // ^2 const float t = s * s; // ^4 const float u = t * t; // ^8 const float v = u * u; // ^16 return copysignf(1.0f - 1.0f / v, x); } inline float fast_erfc (float x) { // Examined 2164260866 values of erfcf on [-4,4]: 1.90735e-06 max error // ulp histogram: // 0 = 80.30% return 1.0f - fast_erf(x); } inline float fast_ierf (float x) { // from: Approximating the erfinv function by Mike Giles // to avoid trouble at the limit, clamp input to 1-eps float a = fabsf(x); if (a > 0.99999994f) a = 0.99999994f; float w = -fast_log((1.0f - a) * (1.0f + a)), p; if (w < 5.0f) { w = w - 2.5f; p = 2.81022636e-08f; p = madd(p, w, 3.43273939e-07f); p = madd(p, w, -3.5233877e-06f ); p = madd(p, w, -4.39150654e-06f); p = madd(p, w, 0.00021858087f ); p = madd(p, w, -0.00125372503f ); p = madd(p, w, -0.00417768164f ); p = madd(p, w, 0.246640727f ); p = madd(p, w, 1.50140941f ); } else { w = sqrtf(w) - 3.0f; p = -0.000200214257f; p = madd(p, w, 0.000100950558f); p = madd(p, w, 0.00134934322f ); p = madd(p, w, -0.00367342844f ); p = madd(p, w, 0.00573950773f ); p = madd(p, w, -0.0076224613f ); p = madd(p, w, 0.00943887047f ); p = madd(p, w, 1.00167406f ); p = madd(p, w, 2.83297682f ); } return p * x; } // (end of fast* functions) //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// // // MISCELLANEOUS NUMERICAL METHODS // /// Solve for the x for which func(x) == y on the interval [xmin,xmax]. /// Use a maximum of maxiter iterations, and stop any time the remaining /// search interval or the function evaluations <= eps. If brack is /// non-NULL, set it to true if y is in [f(xmin), f(xmax)], otherwise /// false (in which case the caller should know that the results may be /// unreliable. Results are undefined if the function is not monotonic /// on that interval or if there are multiple roots in the interval (it /// may not converge, or may converge to any of the roots without /// telling you that there are more than one). template T invert (Func &func, T y, T xmin=0.0, T xmax=1.0, int maxiters=32, T eps=1.0e-6, bool *brack=0) { // Use the Regula Falsi method, falling back to bisection if it // hasn't converged after 3/4 of the maximum number of iterations. // See, e.g., Numerical Recipes for the basic ideas behind both // methods. T v0 = func(xmin), v1 = func(xmax); T x = xmin, v = v0; bool increasing = (v0 < v1); T vmin = increasing ? v0 : v1; T vmax = increasing ? v1 : v0; bool bracketed = (y >= vmin && y <= vmax); if (brack) *brack = bracketed; if (! bracketed) { // If our bounds don't bracket the zero, just give up, and // return the approprate "edge" of the interval return ((y < vmin) == increasing) ? xmin : xmax; } if (fabs(v0-v1) < eps) // already close enough return x; int rfiters = (3*maxiters)/4; // how many times to try regula falsi for (int iters = 0; iters < maxiters; ++iters) { T t; // interpolation factor if (iters < rfiters) { // Regula falsi t = (y-v0)/(v1-v0); if (t <= T(0) || t >= T(1)) t = T(0.5); // RF convergence failure -- bisect instead } else { t = T(0.5); // bisection } x = lerp (xmin, xmax, t); v = func(x); if ((v < y) == increasing) { xmin = x; v0 = v; } else { xmax = x; v1 = v; } if (fabs(xmax-xmin) < eps || fabs(v-y) < eps) return x; // converged } return x; } // (end miscellaneous numerical methods) //////////////////////////////////////////////////////////////////////////// OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/pugixml.cpp0000644000175000017500000100163613151711064023033 0ustar mfvmfv/** * pugixml parser - version 1.2 * -------------------------------------------------------- * Copyright (C) 2006-2012, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) * Report bugs and download new versions at http://pugixml.org/ * * This library is distributed under the MIT License. See notice at the end * of this file. * * This work is based on the pugxml parser, which is: * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) */ #ifndef SOURCE_PUGIXML_CPP #define SOURCE_PUGIXML_CPP #include "pugixml.hpp" #include #include #include #include #include #ifndef PUGIXML_NO_XPATH # include # include # ifdef PUGIXML_NO_EXCEPTIONS # include # endif #endif #ifndef PUGIXML_NO_STL # include # include # include #endif // For placement new #include #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4127) // conditional expression is constant # pragma warning(disable: 4324) // structure was padded due to __declspec(align()) # pragma warning(disable: 4611) // interaction between '_setjmp' and C++ object destruction is non-portable # pragma warning(disable: 4702) // unreachable code # pragma warning(disable: 4996) // this function or variable may be unsafe # pragma warning(disable: 4793) // function compiled as native: presence of '_setjmp' makes a function unmanaged #endif #ifdef __INTEL_COMPILER # pragma warning(disable: 177) // function was declared but never referenced # pragma warning(disable: 279) // controlling expression is constant # pragma warning(disable: 1478 1786) // function was declared "deprecated" # pragma warning(disable: 1684) // conversion from pointer to same-sized integral type #endif #if defined(__BORLANDC__) && defined(PUGIXML_HEADER_ONLY) # pragma warn -8080 // symbol is declared but never used; disabling this inside push/pop bracket does not make the warning go away #endif #ifdef __BORLANDC__ # pragma option push # pragma warn -8008 // condition is always false # pragma warn -8066 // unreachable code #endif #ifdef __SNC__ // Using diag_push/diag_pop does not disable the warnings inside templates due to a compiler bug # pragma diag_suppress=178 // function was declared but never referenced # pragma diag_suppress=237 // controlling expression is constant #endif // Inlining controls #if defined(_MSC_VER) && _MSC_VER >= 1300 # define PUGI__NO_INLINE __declspec(noinline) #elif defined(__GNUC__) # define PUGI__NO_INLINE __attribute__((noinline)) #else # define PUGI__NO_INLINE #endif // Simple static assertion #define PUGI__STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; } // Digital Mars C++ bug workaround for passing char loaded from memory via stack #ifdef __DMC__ # define PUGI__DMC_VOLATILE volatile #else # define PUGI__DMC_VOLATILE #endif // Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy because some compilers don't have it at all) #if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST) using std::memcpy; using std::memmove; #endif // In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features #if defined(_MSC_VER) && !defined(__S3E__) # define PUGI__MSVC_CRT_VERSION _MSC_VER #endif #ifdef PUGIXML_HEADER_ONLY # define PUGI__NS_BEGIN OIIO_NAMESPACE_BEGIN NAMESPACE_BEGIN(pugi) NAMESPACE_BEGIN(impl) # define PUGI__NS_END NAMESPACE_END(impl) NAMESPACE_END(pugi) OIIO_NAMESPACE_END # define PUGI__FN inline # define PUGI__FN_NO_INLINE inline #else # if defined(_MSC_VER) && _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces # define PUGI__NS_BEGIN OIIO_NAMESPACE_BEGIN NAMESPACE_BEGIN(pugi) NAMESPACE_BEGIN(impl) # define PUGI__NS_END NAMESPACE_END(impl) NAMESPACE_END(pugi) OIIO_NAMESPACE_END # else # define PUGI__NS_BEGIN OIIO_NAMESPACE_BEGIN NAMESPACE_BEGIN(pugi) NAMESPACE_BEGIN(impl) namespace { # define PUGI__NS_END NAMESPACE_END(impl) NAMESPACE_END(pugi) } OIIO_NAMESPACE_END # endif # define PUGI__FN # define PUGI__FN_NO_INLINE PUGI__NO_INLINE #endif // uintptr_t #if !defined(_MSC_VER) || _MSC_VER >= 1600 # include #else # ifndef _UINTPTR_T_DEFINED // No native uintptr_t in MSVC6 and in some WinCE versions typedef size_t uintptr_t; #define _UINTPTR_T_DEFINED # endif PUGI__NS_BEGIN typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; PUGI__NS_END #endif // Memory allocation PUGI__NS_BEGIN PUGI__FN void* default_allocate(size_t size) { return malloc(size); } PUGI__FN void default_deallocate(void* ptr) { free(ptr); } template struct xml_memory_management_function_storage { static allocation_function allocate; static deallocation_function deallocate; }; template allocation_function xml_memory_management_function_storage::allocate = default_allocate; template deallocation_function xml_memory_management_function_storage::deallocate = default_deallocate; typedef xml_memory_management_function_storage xml_memory; PUGI__NS_END // String utilities PUGI__NS_BEGIN // Get string length PUGI__FN size_t strlength(const char_t* s) { assert(s); #ifdef PUGIXML_WCHAR_MODE return wcslen(s); #else return strlen(s); #endif } // Compare two strings PUGI__FN bool strequal(const char_t* src, const char_t* dst) { assert(src && dst); #ifdef PUGIXML_WCHAR_MODE return wcscmp(src, dst) == 0; #else return strcmp(src, dst) == 0; #endif } // Compare lhs with [rhs_begin, rhs_end) PUGI__FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count) { for (size_t i = 0; i < count; ++i) if (lhs[i] != rhs[i]) return false; return lhs[count] == 0; } #ifdef PUGIXML_WCHAR_MODE // Convert string to wide string, assuming all symbols are ASCII PUGI__FN void widen_ascii(wchar_t* dest, const char* source) { for (const char* i = source; *i; ++i) *dest++ = *i; *dest = 0; } #endif PUGI__NS_END #if !defined(PUGIXML_NO_STL) || !defined(PUGIXML_NO_XPATH) // auto_ptr-like buffer holder for exception recovery PUGI__NS_BEGIN struct buffer_holder { void* data; void (*deleter)(void*); buffer_holder(void* data_, void (*deleter_)(void*)): data(data_), deleter(deleter_) { } ~buffer_holder() { if (data) deleter(data); } void* release() { void* result = data; data = 0; return result; } }; PUGI__NS_END #endif PUGI__NS_BEGIN static const size_t xml_memory_page_size = #ifdef PUGIXML_MEMORY_PAGE_SIZE PUGIXML_MEMORY_PAGE_SIZE #else 32768 #endif ; static const uintptr_t xml_memory_page_alignment = 32; static const uintptr_t xml_memory_page_pointer_mask = ~(xml_memory_page_alignment - 1); static const uintptr_t xml_memory_page_name_allocated_mask = 16; static const uintptr_t xml_memory_page_value_allocated_mask = 8; static const uintptr_t xml_memory_page_type_mask = 7; struct xml_allocator; struct xml_memory_page { static xml_memory_page* construct(void* memory) { if (!memory) return 0; //$ redundant, left for performance xml_memory_page* result = static_cast(memory); result->allocator = 0; result->memory = 0; result->prev = 0; result->next = 0; result->busy_size = 0; result->freed_size = 0; return result; } xml_allocator* allocator; void* memory; xml_memory_page* prev; xml_memory_page* next; size_t busy_size; size_t freed_size; char data[1]; }; struct xml_memory_string_header { uint16_t page_offset; // offset from page->data uint16_t full_size; // 0 if string occupies whole page }; struct xml_allocator { xml_allocator(xml_memory_page* root): _root(root), _busy_size(root->busy_size) { } xml_memory_page* allocate_page(size_t data_size) { size_t size = offsetof(xml_memory_page, data) + data_size; // allocate block with some alignment, leaving memory for worst-case padding void* memory = xml_memory::allocate(size + xml_memory_page_alignment); if (!memory) return 0; // align upwards to page boundary void* page_memory = reinterpret_cast((reinterpret_cast(memory) + (xml_memory_page_alignment - 1)) & ~(xml_memory_page_alignment - 1)); // prepare page structure xml_memory_page* page = xml_memory_page::construct(page_memory); page->memory = memory; page->allocator = _root->allocator; return page; } static void deallocate_page(xml_memory_page* page) { xml_memory::deallocate(page->memory); } void* allocate_memory_oob(size_t size, xml_memory_page*& out_page); void* allocate_memory(size_t size, xml_memory_page*& out_page) { if (_busy_size + size > xml_memory_page_size) return allocate_memory_oob(size, out_page); void* buf = _root->data + _busy_size; _busy_size += size; out_page = _root; return buf; } void deallocate_memory(void* ptr, size_t size, xml_memory_page* page) { if (page == _root) page->busy_size = _busy_size; assert(ptr >= page->data && ptr < page->data + page->busy_size); (void)!ptr; page->freed_size += size; assert(page->freed_size <= page->busy_size); if (page->freed_size == page->busy_size) { if (page->next == 0) { assert(_root == page); // top page freed, just reset sizes page->busy_size = page->freed_size = 0; _busy_size = 0; } else { assert(_root != page); assert(page->prev); // remove from the list page->prev->next = page->next; page->next->prev = page->prev; // deallocate deallocate_page(page); } } } char_t* allocate_string(size_t length) { // allocate memory for string and header block size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t); // round size up to pointer alignment boundary size_t full_size = (size + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1); xml_memory_page* page; xml_memory_string_header* header = static_cast(allocate_memory(full_size, page)); if (!header) return 0; // setup header ptrdiff_t page_offset = reinterpret_cast(header) - page->data; assert(page_offset >= 0 && page_offset < (1 << 16)); header->page_offset = static_cast(page_offset); // full_size == 0 for large strings that occupy the whole page assert(full_size < (1 << 16) || (page->busy_size == full_size && page_offset == 0)); header->full_size = static_cast(full_size < (1 << 16) ? full_size : 0); // round-trip through void* to avoid 'cast increases required alignment of target type' warning // header is guaranteed a pointer-sized alignment, which should be enough for char_t return static_cast(static_cast(header + 1)); } void deallocate_string(char_t* string) { // this function casts pointers through void* to avoid 'cast increases required alignment of target type' warnings // we're guaranteed the proper (pointer-sized) alignment on the input string if it was allocated via allocate_string // get header xml_memory_string_header* header = static_cast(static_cast(string)) - 1; // deallocate size_t page_offset = offsetof(xml_memory_page, data) + header->page_offset; xml_memory_page* page = reinterpret_cast(static_cast(reinterpret_cast(header) - page_offset)); // if full_size == 0 then this string occupies the whole page size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size; deallocate_memory(header, full_size, page); } xml_memory_page* _root; size_t _busy_size; }; PUGI__FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page) { const size_t large_allocation_threshold = xml_memory_page_size / 4; xml_memory_page* page = allocate_page(size <= large_allocation_threshold ? xml_memory_page_size : size); out_page = page; if (!page) return 0; if (size <= large_allocation_threshold) { _root->busy_size = _busy_size; // insert page at the end of linked list page->prev = _root; _root->next = page; _root = page; _busy_size = size; } else { // insert page before the end of linked list, so that it is deleted as soon as possible // the last page is not deleted even if it's empty (see deallocate_memory) assert(_root->prev); page->prev = _root->prev; page->next = _root; _root->prev->next = page; _root->prev = page; } // allocate inside page page->busy_size = size; return page->data; } PUGI__NS_END OIIO_NAMESPACE_BEGIN namespace pugi { /// A 'name=value' XML attribute structure. struct xml_attribute_struct { /// Default ctor xml_attribute_struct(impl::xml_memory_page* page): header(reinterpret_cast(page)), name(0), value(0), prev_attribute_c(0), next_attribute(0) { } uintptr_t header; char_t* name; ///< Pointer to attribute name. char_t* value; ///< Pointer to attribute value. xml_attribute_struct* prev_attribute_c; ///< Previous attribute (cyclic list) xml_attribute_struct* next_attribute; ///< Next attribute }; /// An XML document tree node. struct xml_node_struct { /// Default ctor /// \param type - node type xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(reinterpret_cast(page) | (type - 1)), parent(0), name(0), value(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0) { } uintptr_t header; xml_node_struct* parent; ///< Pointer to parent char_t* name; ///< Pointer to element name. char_t* value; ///< Pointer to any associated string data. xml_node_struct* first_child; ///< First child xml_node_struct* prev_sibling_c; ///< Left brother (cyclic list) xml_node_struct* next_sibling; ///< Right brother xml_attribute_struct* first_attribute; ///< First attribute }; } OIIO_NAMESPACE_END PUGI__NS_BEGIN struct xml_document_struct: public xml_node_struct, public xml_allocator { xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0) { } const char_t* buffer; }; inline xml_allocator& get_allocator(const xml_node_struct* node) { assert(node); return *reinterpret_cast(node->header & xml_memory_page_pointer_mask)->allocator; } PUGI__NS_END // Low-level DOM operations PUGI__NS_BEGIN inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc) { xml_memory_page* page; void* memory = alloc.allocate_memory(sizeof(xml_attribute_struct), page); return new (memory) xml_attribute_struct(page); } inline xml_node_struct* allocate_node(xml_allocator& alloc, xml_node_type type) { xml_memory_page* page; void* memory = alloc.allocate_memory(sizeof(xml_node_struct), page); return new (memory) xml_node_struct(page, type); } inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc) { uintptr_t header = a->header; if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(a->name); if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(a->value); alloc.deallocate_memory(a, sizeof(xml_attribute_struct), reinterpret_cast(header & xml_memory_page_pointer_mask)); } inline void destroy_node(xml_node_struct* n, xml_allocator& alloc) { uintptr_t header = n->header; if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(n->name); if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(n->value); for (xml_attribute_struct* attr = n->first_attribute; attr; ) { xml_attribute_struct* next = attr->next_attribute; destroy_attribute(attr, alloc); attr = next; } for (xml_node_struct* child = n->first_child; child; ) { xml_node_struct* next = child->next_sibling; destroy_node(child, alloc); child = next; } alloc.deallocate_memory(n, sizeof(xml_node_struct), reinterpret_cast(header & xml_memory_page_pointer_mask)); } PUGI__FN_NO_INLINE xml_node_struct* append_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element) { xml_node_struct* child = allocate_node(alloc, type); if (!child) return 0; child->parent = node; xml_node_struct* first_child = node->first_child; if (first_child) { xml_node_struct* last_child = first_child->prev_sibling_c; last_child->next_sibling = child; child->prev_sibling_c = last_child; first_child->prev_sibling_c = child; } else { node->first_child = child; child->prev_sibling_c = child; } return child; } PUGI__FN_NO_INLINE xml_attribute_struct* append_attribute_ll(xml_node_struct* node, xml_allocator& alloc) { xml_attribute_struct* a = allocate_attribute(alloc); if (!a) return 0; xml_attribute_struct* first_attribute = node->first_attribute; if (first_attribute) { xml_attribute_struct* last_attribute = first_attribute->prev_attribute_c; last_attribute->next_attribute = a; a->prev_attribute_c = last_attribute; first_attribute->prev_attribute_c = a; } else { node->first_attribute = a; a->prev_attribute_c = a; } return a; } PUGI__NS_END // Helper classes for code generation PUGI__NS_BEGIN struct opt_false { enum { value = 0 }; }; struct opt_true { enum { value = 1 }; }; PUGI__NS_END // Unicode utilities PUGI__NS_BEGIN inline uint16_t endian_swap(uint16_t value) { return static_cast(((value & 0xff) << 8) | (value >> 8)); } inline uint32_t endian_swap(uint32_t value) { return ((value & 0xff) << 24) | ((value & 0xff00) << 8) | ((value & 0xff0000) >> 8) | (value >> 24); } struct utf8_counter { typedef size_t value_type; static value_type low(value_type result, uint32_t ch) { // U+0000..U+007F if (ch < 0x80) return result + 1; // U+0080..U+07FF else if (ch < 0x800) return result + 2; // U+0800..U+FFFF else return result + 3; } static value_type high(value_type result, uint32_t) { // U+10000..U+10FFFF return result + 4; } }; struct utf8_writer { typedef uint8_t* value_type; static value_type low(value_type result, uint32_t ch) { // U+0000..U+007F if (ch < 0x80) { *result = static_cast(ch); return result + 1; } // U+0080..U+07FF else if (ch < 0x800) { result[0] = static_cast(0xC0 | (ch >> 6)); result[1] = static_cast(0x80 | (ch & 0x3F)); return result + 2; } // U+0800..U+FFFF else { result[0] = static_cast(0xE0 | (ch >> 12)); result[1] = static_cast(0x80 | ((ch >> 6) & 0x3F)); result[2] = static_cast(0x80 | (ch & 0x3F)); return result + 3; } } static value_type high(value_type result, uint32_t ch) { // U+10000..U+10FFFF result[0] = static_cast(0xF0 | (ch >> 18)); result[1] = static_cast(0x80 | ((ch >> 12) & 0x3F)); result[2] = static_cast(0x80 | ((ch >> 6) & 0x3F)); result[3] = static_cast(0x80 | (ch & 0x3F)); return result + 4; } static value_type any(value_type result, uint32_t ch) { return (ch < 0x10000) ? low(result, ch) : high(result, ch); } }; struct utf16_counter { typedef size_t value_type; static value_type low(value_type result, uint32_t) { return result + 1; } static value_type high(value_type result, uint32_t) { return result + 2; } }; struct utf16_writer { typedef uint16_t* value_type; static value_type low(value_type result, uint32_t ch) { *result = static_cast(ch); return result + 1; } static value_type high(value_type result, uint32_t ch) { uint32_t msh = static_cast(ch - 0x10000) >> 10; uint32_t lsh = static_cast(ch - 0x10000) & 0x3ff; result[0] = static_cast(0xD800 + msh); result[1] = static_cast(0xDC00 + lsh); return result + 2; } static value_type any(value_type result, uint32_t ch) { return (ch < 0x10000) ? low(result, ch) : high(result, ch); } }; struct utf32_counter { typedef size_t value_type; static value_type low(value_type result, uint32_t) { return result + 1; } static value_type high(value_type result, uint32_t) { return result + 1; } }; struct utf32_writer { typedef uint32_t* value_type; static value_type low(value_type result, uint32_t ch) { *result = ch; return result + 1; } static value_type high(value_type result, uint32_t ch) { *result = ch; return result + 1; } static value_type any(value_type result, uint32_t ch) { *result = ch; return result + 1; } }; struct latin1_writer { typedef uint8_t* value_type; static value_type low(value_type result, uint32_t ch) { *result = static_cast(ch > 255 ? '?' : ch); return result + 1; } static value_type high(value_type result, uint32_t ch) { (void)ch; *result = '?'; return result + 1; } }; template struct wchar_selector; template <> struct wchar_selector<2> { typedef uint16_t type; typedef utf16_counter counter; typedef utf16_writer writer; }; template <> struct wchar_selector<4> { typedef uint32_t type; typedef utf32_counter counter; typedef utf32_writer writer; }; typedef wchar_selector::counter wchar_counter; typedef wchar_selector::writer wchar_writer; template struct utf_decoder { static inline typename Traits::value_type decode_utf8_block(const uint8_t* data, size_t size, typename Traits::value_type result) { const uint8_t utf8_byte_mask = 0x3f; while (size) { uint8_t lead = *data; // 0xxxxxxx -> U+0000..U+007F if (lead < 0x80) { result = Traits::low(result, lead); data += 1; size -= 1; // process aligned single-byte (ascii) blocks if ((reinterpret_cast(data) & 3) == 0) { // round-trip through void* to silence 'cast increases required alignment of target type' warnings while (size >= 4 && (*static_cast(static_cast(data)) & 0x80808080) == 0) { result = Traits::low(result, data[0]); result = Traits::low(result, data[1]); result = Traits::low(result, data[2]); result = Traits::low(result, data[3]); data += 4; size -= 4; } } } // 110xxxxx -> U+0080..U+07FF else if (static_cast(lead - 0xC0) < 0x20 && size >= 2 && (data[1] & 0xc0) == 0x80) { result = Traits::low(result, ((lead & ~0xC0) << 6) | (data[1] & utf8_byte_mask)); data += 2; size -= 2; } // 1110xxxx -> U+0800-U+FFFF else if (static_cast(lead - 0xE0) < 0x10 && size >= 3 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80) { result = Traits::low(result, ((lead & ~0xE0) << 12) | ((data[1] & utf8_byte_mask) << 6) | (data[2] & utf8_byte_mask)); data += 3; size -= 3; } // 11110xxx -> U+10000..U+10FFFF else if (static_cast(lead - 0xF0) < 0x08 && size >= 4 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80 && (data[3] & 0xc0) == 0x80) { result = Traits::high(result, ((lead & ~0xF0) << 18) | ((data[1] & utf8_byte_mask) << 12) | ((data[2] & utf8_byte_mask) << 6) | (data[3] & utf8_byte_mask)); data += 4; size -= 4; } // 10xxxxxx or 11111xxx -> invalid else { data += 1; size -= 1; } } return result; } static inline typename Traits::value_type decode_utf16_block(const uint16_t* data, size_t size, typename Traits::value_type result) { const uint16_t* end = data + size; while (data < end) { uint16_t lead = opt_swap::value ? endian_swap(*data) : *data; // U+0000..U+D7FF if (lead < 0xD800) { result = Traits::low(result, lead); data += 1; } // U+E000..U+FFFF else if (static_cast(lead - 0xE000) < 0x2000) { result = Traits::low(result, lead); data += 1; } // surrogate pair lead else if (static_cast(lead - 0xD800) < 0x400 && data + 1 < end) { uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1]; if (static_cast(next - 0xDC00) < 0x400) { result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff)); data += 2; } else { data += 1; } } else { data += 1; } } return result; } static inline typename Traits::value_type decode_utf32_block(const uint32_t* data, size_t size, typename Traits::value_type result) { const uint32_t* end = data + size; while (data < end) { uint32_t lead = opt_swap::value ? endian_swap(*data) : *data; // U+0000..U+FFFF if (lead < 0x10000) { result = Traits::low(result, lead); data += 1; } // U+10000..U+10FFFF else { result = Traits::high(result, lead); data += 1; } } return result; } static inline typename Traits::value_type decode_latin1_block(const uint8_t* data, size_t size, typename Traits::value_type result) { for (size_t i = 0; i < size; ++i) { result = Traits::low(result, data[i]); } return result; } static inline typename Traits::value_type decode_wchar_block_impl(const uint16_t* data, size_t size, typename Traits::value_type result) { return decode_utf16_block(data, size, result); } static inline typename Traits::value_type decode_wchar_block_impl(const uint32_t* data, size_t size, typename Traits::value_type result) { return decode_utf32_block(data, size, result); } static inline typename Traits::value_type decode_wchar_block(const wchar_t* data, size_t size, typename Traits::value_type result) { return decode_wchar_block_impl(reinterpret_cast::type*>(data), size, result); } }; template PUGI__FN void convert_utf_endian_swap(T* result, const T* data, size_t length) { for (size_t i = 0; i < length; ++i) result[i] = endian_swap(data[i]); } #ifdef PUGIXML_WCHAR_MODE PUGI__FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length) { for (size_t i = 0; i < length; ++i) result[i] = static_cast(endian_swap(static_cast::type>(data[i]))); } #endif PUGI__NS_END PUGI__NS_BEGIN enum chartype_t { ct_parse_pcdata = 1, // \0, &, \r, < ct_parse_attr = 2, // \0, &, \r, ', " ct_parse_attr_ws = 4, // \0, &, \r, ', ", \n, tab ct_space = 8, // \r, \n, space, tab ct_parse_cdata = 16, // \0, ], >, \r ct_parse_comment = 32, // \0, -, >, \r ct_symbol = 64, // Any symbol > 127, a-z, A-Z, 0-9, _, :, -, . ct_start_symbol = 128 // Any symbol > 127, a-z, A-Z, _, : }; static const unsigned char chartype_table[256] = { 55, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 63, 0, 0, // 0-15 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31 8, 0, 6, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 96, 64, 0, // 32-47 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 192, 0, 1, 0, 48, 0, // 48-63 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 64-79 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 16, 0, 192, // 80-95 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 96-111 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 0, 0, 0, // 112-127 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 128+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192 }; enum chartypex_t { ctx_special_pcdata = 1, // Any symbol >= 0 and < 32 (except \t, \r, \n), &, <, > ctx_special_attr = 2, // Any symbol >= 0 and < 32 (except \t), &, <, >, " ctx_start_symbol = 4, // Any symbol > 127, a-z, A-Z, _ ctx_digit = 8, // 0-9 ctx_symbol = 16 // Any symbol > 127, a-z, A-Z, 0-9, _, -, . }; static const unsigned char chartypex_table[256] = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 2, 3, 3, // 0-15 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 16-31 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 3, 0, // 48-63 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 64-79 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 20, // 80-95 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 96-111 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 0, // 112-127 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 128+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20 }; #ifdef PUGIXML_WCHAR_MODE #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) ((static_cast(c) < 128 ? table[static_cast(c)] : table[128]) & (ct)) #else #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast(c)] & (ct)) #endif #define PUGI__IS_CHARTYPE(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartype_table) #define PUGI__IS_CHARTYPEX(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartypex_table) PUGI__FN bool is_little_endian() { unsigned int ui = 1; return *reinterpret_cast(&ui) == 1; } PUGI__FN xml_encoding get_wchar_encoding() { PUGI__STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4); if (sizeof(wchar_t) == 2) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; else return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; } PUGI__FN xml_encoding guess_buffer_encoding(uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3) { // look for BOM in first few bytes if (d0 == 0 && d1 == 0 && d2 == 0xfe && d3 == 0xff) return encoding_utf32_be; if (d0 == 0xff && d1 == 0xfe && d2 == 0 && d3 == 0) return encoding_utf32_le; if (d0 == 0xfe && d1 == 0xff) return encoding_utf16_be; if (d0 == 0xff && d1 == 0xfe) return encoding_utf16_le; if (d0 == 0xef && d1 == 0xbb && d2 == 0xbf) return encoding_utf8; // look for <, (contents); PUGI__DMC_VOLATILE uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3]; return guess_buffer_encoding(d0, d1, d2, d3); } PUGI__FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) { if (is_mutable) { out_buffer = static_cast(const_cast(contents)); } else { void* buffer = xml_memory::allocate(size > 0 ? size : 1); if (!buffer) return false; memcpy(buffer, contents, size); out_buffer = static_cast(buffer); } out_length = size / sizeof(char_t); return true; } #ifdef PUGIXML_WCHAR_MODE PUGI__FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re) { return (le == encoding_utf16_be && re == encoding_utf16_le) || (le == encoding_utf16_le && re == encoding_utf16_be) || (le == encoding_utf32_be && re == encoding_utf32_le) || (le == encoding_utf32_le && re == encoding_utf32_be); } PUGI__FN bool convert_buffer_endian_swap(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) { const char_t* data = static_cast(contents); if (is_mutable) { out_buffer = const_cast(data); } else { out_buffer = static_cast(xml_memory::allocate(size > 0 ? size : 1)); if (!out_buffer) return false; } out_length = size / sizeof(char_t); convert_wchar_endian_swap(out_buffer, data, out_length); return true; } PUGI__FN bool convert_buffer_utf8(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size) { const uint8_t* data = static_cast(contents); // first pass: get length in wchar_t units out_length = utf_decoder::decode_utf8_block(data, size, 0); // allocate buffer of suitable length out_buffer = static_cast(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t))); if (!out_buffer) return false; // second pass: convert utf8 input to wchar_t wchar_writer::value_type out_begin = reinterpret_cast(out_buffer); wchar_writer::value_type out_end = utf_decoder::decode_utf8_block(data, size, out_begin); assert(out_end == out_begin + out_length); (void)!out_end; return true; } template PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) { const uint16_t* data = static_cast(contents); size_t length = size / sizeof(uint16_t); // first pass: get length in wchar_t units out_length = utf_decoder::decode_utf16_block(data, length, 0); // allocate buffer of suitable length out_buffer = static_cast(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t))); if (!out_buffer) return false; // second pass: convert utf16 input to wchar_t wchar_writer::value_type out_begin = reinterpret_cast(out_buffer); wchar_writer::value_type out_end = utf_decoder::decode_utf16_block(data, length, out_begin); assert(out_end == out_begin + out_length); (void)!out_end; return true; } template PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) { const uint32_t* data = static_cast(contents); size_t length = size / sizeof(uint32_t); // first pass: get length in wchar_t units out_length = utf_decoder::decode_utf32_block(data, length, 0); // allocate buffer of suitable length out_buffer = static_cast(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t))); if (!out_buffer) return false; // second pass: convert utf32 input to wchar_t wchar_writer::value_type out_begin = reinterpret_cast(out_buffer); wchar_writer::value_type out_end = utf_decoder::decode_utf32_block(data, length, out_begin); assert(out_end == out_begin + out_length); (void)!out_end; return true; } PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size) { const uint8_t* data = static_cast(contents); // get length in wchar_t units out_length = size; // allocate buffer of suitable length out_buffer = static_cast(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t))); if (!out_buffer) return false; // convert latin1 input to wchar_t wchar_writer::value_type out_begin = reinterpret_cast(out_buffer); wchar_writer::value_type out_end = utf_decoder::decode_latin1_block(data, size, out_begin); assert(out_end == out_begin + out_length); (void)!out_end; return true; } PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) { // get native encoding xml_encoding wchar_encoding = get_wchar_encoding(); // fast path: no conversion required if (encoding == wchar_encoding) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); // only endian-swapping is required if (need_endian_swap_utf(encoding, wchar_encoding)) return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable); // source encoding is utf8 if (encoding == encoding_utf8) return convert_buffer_utf8(out_buffer, out_length, contents, size); // source encoding is utf16 if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; return (native_encoding == encoding) ? convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) : convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true()); } // source encoding is utf32 if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; return (native_encoding == encoding) ? convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) : convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true()); } // source encoding is latin1 if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size); assert(!"Invalid encoding"); return false; } #else template PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) { const uint16_t* data = static_cast(contents); size_t length = size / sizeof(uint16_t); // first pass: get length in utf8 units out_length = utf_decoder::decode_utf16_block(data, length, 0); // allocate buffer of suitable length out_buffer = static_cast(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t))); if (!out_buffer) return false; // second pass: convert utf16 input to utf8 uint8_t* out_begin = reinterpret_cast(out_buffer); uint8_t* out_end = utf_decoder::decode_utf16_block(data, length, out_begin); assert(out_end == out_begin + out_length); (void)!out_end; return true; } template PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) { const uint32_t* data = static_cast(contents); size_t length = size / sizeof(uint32_t); // first pass: get length in utf8 units out_length = utf_decoder::decode_utf32_block(data, length, 0); // allocate buffer of suitable length out_buffer = static_cast(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t))); if (!out_buffer) return false; // second pass: convert utf32 input to utf8 uint8_t* out_begin = reinterpret_cast(out_buffer); uint8_t* out_end = utf_decoder::decode_utf32_block(data, length, out_begin); assert(out_end == out_begin + out_length); (void)!out_end; return true; } PUGI__FN size_t get_latin1_7bit_prefix_length(const uint8_t* data, size_t size) { for (size_t i = 0; i < size; ++i) if (data[i] > 127) return i; return size; } PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) { const uint8_t* data = static_cast(contents); // get size of prefix that does not need utf8 conversion size_t prefix_length = get_latin1_7bit_prefix_length(data, size); assert(prefix_length <= size); const uint8_t* postfix = data + prefix_length; size_t postfix_length = size - prefix_length; // if no conversion is needed, just return the original buffer if (postfix_length == 0) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); // first pass: get length in utf8 units out_length = prefix_length + utf_decoder::decode_latin1_block(postfix, postfix_length, 0); // allocate buffer of suitable length out_buffer = static_cast(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t))); if (!out_buffer) return false; // second pass: convert latin1 input to utf8 memcpy(out_buffer, data, prefix_length); uint8_t* out_begin = reinterpret_cast(out_buffer); uint8_t* out_end = utf_decoder::decode_latin1_block(postfix, postfix_length, out_begin + prefix_length); assert(out_end == out_begin + out_length); (void)!out_end; return true; } PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) { // fast path: no conversion required if (encoding == encoding_utf8) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); // source encoding is utf16 if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; return (native_encoding == encoding) ? convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) : convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true()); } // source encoding is utf32 if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; return (native_encoding == encoding) ? convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) : convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true()); } // source encoding is latin1 if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable); assert(!"Invalid encoding"); return false; } #endif PUGI__FN size_t as_utf8_begin(const wchar_t* str, size_t length) { // get length in utf8 characters return utf_decoder::decode_wchar_block(str, length, 0); } PUGI__FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length) { // convert to utf8 uint8_t* begin = reinterpret_cast(buffer); uint8_t* end = utf_decoder::decode_wchar_block(str, length, begin); assert(begin + size == end); (void)!end; // zero-terminate buffer[size] = 0; } #ifndef PUGIXML_NO_STL PUGI__FN std::string as_utf8_impl(const wchar_t* str, size_t length) { // first pass: get length in utf8 characters size_t size = as_utf8_begin(str, length); // allocate resulting string std::string result; result.resize(size); // second pass: convert to utf8 if (size > 0) as_utf8_end(&result[0], size, str, length); return result; } PUGI__FN std::basic_string as_wide_impl(const char* str, size_t size) { const uint8_t* data = reinterpret_cast(str); // first pass: get length in wchar_t units size_t length = utf_decoder::decode_utf8_block(data, size, 0); // allocate resulting string std::basic_string result; result.resize(length); // second pass: convert to wchar_t if (length > 0) { wchar_writer::value_type begin = reinterpret_cast(&result[0]); wchar_writer::value_type end = utf_decoder::decode_utf8_block(data, size, begin); assert(begin + length == end); (void)!end; } return result; } #endif inline bool strcpy_insitu_allow(size_t length, uintptr_t allocated, char_t* target) { assert(target); size_t target_length = strlength(target); // always reuse document buffer memory if possible if (!allocated) return target_length >= length; // reuse heap memory if waste is not too great const size_t reuse_threshold = 32; return target_length >= length && (target_length < reuse_threshold || target_length - length < target_length / 2); } PUGI__FN bool strcpy_insitu(char_t*& dest, uintptr_t& header, uintptr_t header_mask, const char_t* source) { size_t source_length = strlength(source); if (source_length == 0) { // empty string and null pointer are equivalent, so just deallocate old memory xml_allocator* alloc = reinterpret_cast(header & xml_memory_page_pointer_mask)->allocator; if (header & header_mask) alloc->deallocate_string(dest); // mark the string as not allocated dest = 0; header &= ~header_mask; return true; } else if (dest && strcpy_insitu_allow(source_length, header & header_mask, dest)) { // we can reuse old buffer, so just copy the new data (including zero terminator) memcpy(dest, source, (source_length + 1) * sizeof(char_t)); return true; } else { xml_allocator* alloc = reinterpret_cast(header & xml_memory_page_pointer_mask)->allocator; // allocate new buffer char_t* buf = alloc->allocate_string(source_length + 1); if (!buf) return false; // copy the string (including zero terminator) memcpy(buf, source, (source_length + 1) * sizeof(char_t)); // deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures) if (header & header_mask) alloc->deallocate_string(dest); // the string is now allocated, so set the flag dest = buf; header |= header_mask; return true; } } struct gap { char_t* end; size_t size; gap(): end(0), size(0) { } // Push new gap, move s count bytes further (skipping the gap). // Collapse previous gap. void push(char_t*& s, size_t count) { if (end) // there was a gap already; collapse it { // Move [old_gap_end, new_gap_start) to [old_gap_start, ...) assert(s >= end); memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); } s += count; // end of current gap // "merge" two gaps end = s; size += count; } // Collapse all gaps, return past-the-end pointer char_t* flush(char_t* s) { if (end) { // Move [old_gap_end, current_pos) to [old_gap_start, ...) assert(s >= end); memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); return s - size; } else return s; } }; PUGI__FN char_t* strconv_escape(char_t* s, gap& g) { char_t* stre = s + 1; switch (*stre) { case '#': // &#... { unsigned int ucsc = 0; if (stre[1] == 'x') // &#x... (hex code) { stre += 2; char_t ch = *stre; if (ch == ';') return stre; for (;;) { if (static_cast(ch - '0') <= 9) ucsc = 16 * ucsc + (ch - '0'); else if (static_cast((ch | ' ') - 'a') <= 5) ucsc = 16 * ucsc + ((ch | ' ') - 'a' + 10); else if (ch == ';') break; else // cancel return stre; ch = *++stre; } ++stre; } else // &#... (dec code) { char_t ch = *++stre; if (ch == ';') return stre; for (;;) { if (static_cast(ch - '0') <= 9) ucsc = 10 * ucsc + (ch - '0'); else if (ch == ';') break; else // cancel return stre; ch = *++stre; } ++stre; } #ifdef PUGIXML_WCHAR_MODE s = reinterpret_cast(wchar_writer::any(reinterpret_cast(s), ucsc)); #else s = reinterpret_cast(utf8_writer::any(reinterpret_cast(s), ucsc)); #endif g.push(s, stre - s); return stre; } case 'a': // &a { ++stre; if (*stre == 'm') // &am { if (*++stre == 'p' && *++stre == ';') // & { *s++ = '&'; ++stre; g.push(s, stre - s); return stre; } } else if (*stre == 'p') // &ap { if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // ' { *s++ = '\''; ++stre; g.push(s, stre - s); return stre; } } break; } case 'g': // &g { if (*++stre == 't' && *++stre == ';') // > { *s++ = '>'; ++stre; g.push(s, stre - s); return stre; } break; } case 'l': // &l { if (*++stre == 't' && *++stre == ';') // < { *s++ = '<'; ++stre; g.push(s, stre - s); return stre; } break; } case 'q': // &q { if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // " { *s++ = '"'; ++stre; g.push(s, stre - s); return stre; } break; } default: break; } return stre; } // Utility macro for last character handling #define ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e))) PUGI__FN char_t* strconv_comment(char_t* s, char_t endch) { gap g; while (true) { while (!PUGI__IS_CHARTYPE(*s, ct_parse_comment)) ++s; if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair { *s++ = '\n'; // replace first one with 0x0a if (*s == '\n') g.push(s, 1); } else if (s[0] == '-' && s[1] == '-' && ENDSWITH(s[2], '>')) // comment ends here { *g.flush(s) = 0; return s + (s[2] == '>' ? 3 : 2); } else if (*s == 0) { return 0; } else ++s; } } PUGI__FN char_t* strconv_cdata(char_t* s, char_t endch) { gap g; while (true) { while (!PUGI__IS_CHARTYPE(*s, ct_parse_cdata)) ++s; if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair { *s++ = '\n'; // replace first one with 0x0a if (*s == '\n') g.push(s, 1); } else if (s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>')) // CDATA ends here { *g.flush(s) = 0; return s + 1; } else if (*s == 0) { return 0; } else ++s; } } typedef char_t* (*strconv_pcdata_t)(char_t*); template struct strconv_pcdata_impl { static char_t* parse(char_t* s) { gap g; while (true) { while (!PUGI__IS_CHARTYPE(*s, ct_parse_pcdata)) ++s; if (*s == '<') // PCDATA ends here { *g.flush(s) = 0; return s + 1; } else if (opt_eol::value && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair { *s++ = '\n'; // replace first one with 0x0a if (*s == '\n') g.push(s, 1); } else if (opt_escape::value && *s == '&') { s = strconv_escape(s, g); } else if (*s == 0) { return s; } else ++s; } } }; PUGI__FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask) { PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20); switch ((optmask >> 4) & 3) // get bitmask for flags (eol escapes) { case 0: return strconv_pcdata_impl::parse; case 1: return strconv_pcdata_impl::parse; case 2: return strconv_pcdata_impl::parse; case 3: return strconv_pcdata_impl::parse; default: return 0; // should not get here } } typedef char_t* (*strconv_attribute_t)(char_t*, char_t); template struct strconv_attribute_impl { static char_t* parse_wnorm(char_t* s, char_t end_quote) { gap g; // trim leading whitespaces if (PUGI__IS_CHARTYPE(*s, ct_space)) { char_t* str = s; do ++str; while (PUGI__IS_CHARTYPE(*str, ct_space)); g.push(s, str - s); } while (true) { while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr_ws | ct_space)) ++s; if (*s == end_quote) { char_t* str = g.flush(s); do *str-- = 0; while (PUGI__IS_CHARTYPE(*str, ct_space)); return s + 1; } else if (PUGI__IS_CHARTYPE(*s, ct_space)) { *s++ = ' '; if (PUGI__IS_CHARTYPE(*s, ct_space)) { char_t* str = s + 1; while (PUGI__IS_CHARTYPE(*str, ct_space)) ++str; g.push(s, str - s); } } else if (opt_escape::value && *s == '&') { s = strconv_escape(s, g); } else if (!*s) { return 0; } else ++s; } } static char_t* parse_wconv(char_t* s, char_t end_quote) { gap g; while (true) { while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr_ws)) ++s; if (*s == end_quote) { *g.flush(s) = 0; return s + 1; } else if (PUGI__IS_CHARTYPE(*s, ct_space)) { if (*s == '\r') { *s++ = ' '; if (*s == '\n') g.push(s, 1); } else *s++ = ' '; } else if (opt_escape::value && *s == '&') { s = strconv_escape(s, g); } else if (!*s) { return 0; } else ++s; } } static char_t* parse_eol(char_t* s, char_t end_quote) { gap g; while (true) { while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr)) ++s; if (*s == end_quote) { *g.flush(s) = 0; return s + 1; } else if (*s == '\r') { *s++ = '\n'; if (*s == '\n') g.push(s, 1); } else if (opt_escape::value && *s == '&') { s = strconv_escape(s, g); } else if (!*s) { return 0; } else ++s; } } static char_t* parse_simple(char_t* s, char_t end_quote) { gap g; while (true) { while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr)) ++s; if (*s == end_quote) { *g.flush(s) = 0; return s + 1; } else if (opt_escape::value && *s == '&') { s = strconv_escape(s, g); } else if (!*s) { return 0; } else ++s; } } }; PUGI__FN strconv_attribute_t get_strconv_attribute(unsigned int optmask) { PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80); switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes) { case 0: return strconv_attribute_impl::parse_simple; case 1: return strconv_attribute_impl::parse_simple; case 2: return strconv_attribute_impl::parse_eol; case 3: return strconv_attribute_impl::parse_eol; case 4: return strconv_attribute_impl::parse_wconv; case 5: return strconv_attribute_impl::parse_wconv; case 6: return strconv_attribute_impl::parse_wconv; case 7: return strconv_attribute_impl::parse_wconv; case 8: return strconv_attribute_impl::parse_wnorm; case 9: return strconv_attribute_impl::parse_wnorm; case 10: return strconv_attribute_impl::parse_wnorm; case 11: return strconv_attribute_impl::parse_wnorm; case 12: return strconv_attribute_impl::parse_wnorm; case 13: return strconv_attribute_impl::parse_wnorm; case 14: return strconv_attribute_impl::parse_wnorm; case 15: return strconv_attribute_impl::parse_wnorm; default: return 0; // should not get here } } inline xml_parse_result make_parse_result(xml_parse_status status, ptrdiff_t offset = 0) { xml_parse_result result; result.status = status; result.offset = offset; return result; } struct xml_parser { xml_allocator alloc; char_t* error_offset; xml_parse_status error_status; // Parser utilities. #define PUGI__SKIPWS() { while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; } #define PUGI__OPTSET(OPT) ( optmsk & (OPT) ) #define PUGI__PUSHNODE(TYPE) { cursor = append_node(cursor, alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); } #define PUGI__POPNODE() { cursor = cursor->parent; } #define PUGI__SCANFOR(X) { while (*s != 0 && !(X)) ++s; } #define PUGI__SCANWHILE(X) { while ((X)) ++s; } #define PUGI__ENDSEG() { ch = *s; *s = 0; ++s; } #define PUGI__THROW_ERROR(err, m) return error_offset = m, error_status = err, static_cast(0) #define PUGI__CHECK_ERROR(err, m) { if (*s == 0) PUGI__THROW_ERROR(err, m); } xml_parser(const xml_allocator& alloc_): alloc(alloc_), error_offset(0), error_status(status_ok) { } // DOCTYPE consists of nested sections of the following possible types: // , , "...", '...' // // // First group can not contain nested groups // Second group can contain nested groups of the same type // Third group can contain all other groups char_t* parse_doctype_primitive(char_t* s) { if (*s == '"' || *s == '\'') { // quoted string char_t ch = *s++; PUGI__SCANFOR(*s == ch); if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); s++; } else if (s[0] == '<' && s[1] == '?') { // s += 2; PUGI__SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); s += 2; } else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-') { s += 4; PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); s += 4; } else PUGI__THROW_ERROR(status_bad_doctype, s); return s; } char_t* parse_doctype_ignore(char_t* s) { assert(s[0] == '<' && s[1] == '!' && s[2] == '['); s++; while (*s) { if (s[0] == '<' && s[1] == '!' && s[2] == '[') { // nested ignore section s = parse_doctype_ignore(s); if (!s) return s; } else if (s[0] == ']' && s[1] == ']' && s[2] == '>') { // ignore section end s += 3; return s; } else s++; } PUGI__THROW_ERROR(status_bad_doctype, s); } char_t* parse_doctype_group(char_t* s, char_t endch, bool toplevel) { assert(s[0] == '<' && s[1] == '!'); s++; while (*s) { if (s[0] == '<' && s[1] == '!' && s[2] != '-') { if (s[2] == '[') { // ignore s = parse_doctype_ignore(s); if (!s) return s; } else { // some control group s = parse_doctype_group(s, endch, false); if (!s) return s; } } else if (s[0] == '<' || s[0] == '"' || s[0] == '\'') { // unknown tag (forbidden), or some primitive group s = parse_doctype_primitive(s); if (!s) return s; } else if (*s == '>') { s++; return s; } else s++; } if (!toplevel || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s); return s; } char_t* parse_exclamation(char_t* s, xml_node_struct* cursor, unsigned int optmsk, char_t endch) { // parse node contents, starting with exclamation mark ++s; if (*s == '-') // 'value = s; // Save the offset. } if (PUGI__OPTSET(parse_eol) && PUGI__OPTSET(parse_comments)) { s = strconv_comment(s, endch); if (!s) PUGI__THROW_ERROR(status_bad_comment, cursor->value); } else { // Scan for terminating '-->'. PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && ENDSWITH(s[2], '>')); PUGI__CHECK_ERROR(status_bad_comment, s); if (PUGI__OPTSET(parse_comments)) *s = 0; // Zero-terminate this segment at the first terminating '-'. s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'. } } else PUGI__THROW_ERROR(status_bad_comment, s); } else if (*s == '[') { // 'value = s; // Save the offset. if (PUGI__OPTSET(parse_eol)) { s = strconv_cdata(s, endch); if (!s) PUGI__THROW_ERROR(status_bad_cdata, cursor->value); } else { // Scan for terminating ']]>'. PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>')); PUGI__CHECK_ERROR(status_bad_cdata, s); *s++ = 0; // Zero-terminate this segment. } } else // Flagged for discard, but we still have to scan for the terminator. { // Scan for terminating ']]>'. PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>')); PUGI__CHECK_ERROR(status_bad_cdata, s); ++s; } s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'. } else PUGI__THROW_ERROR(status_bad_cdata, s); } else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && ENDSWITH(s[6], 'E')) { s -= 2; if (cursor->parent) PUGI__THROW_ERROR(status_bad_doctype, s); char_t* mark = s + 9; s = parse_doctype_group(s, endch, true); if (!s) return s; if (PUGI__OPTSET(parse_doctype)) { while (PUGI__IS_CHARTYPE(*mark, ct_space)) ++mark; PUGI__PUSHNODE(node_doctype); cursor->value = mark; assert((s[0] == 0 && endch == '>') || s[-1] == '>'); s[*s == 0 ? 0 : -1] = 0; PUGI__POPNODE(); } } else if (*s == 0 && endch == '-') PUGI__THROW_ERROR(status_bad_comment, s); else if (*s == 0 && endch == '[') PUGI__THROW_ERROR(status_bad_cdata, s); else PUGI__THROW_ERROR(status_unrecognized_tag, s); return s; } char_t* parse_question(char_t* s, xml_node_struct*& ref_cursor, unsigned int optmsk, char_t endch) { // load into registers xml_node_struct* cursor = ref_cursor; char_t ch = 0; // parse node contents, starting with question mark ++s; // read PI target char_t* target = s; if (!PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_pi, s); PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); PUGI__CHECK_ERROR(status_bad_pi, s); // determine node type; stricmp / strcasecmp is not portable bool declaration = (target[0] | ' ') == 'x' && (target[1] | ' ') == 'm' && (target[2] | ' ') == 'l' && target + 3 == s; if (declaration ? PUGI__OPTSET(parse_declaration) : PUGI__OPTSET(parse_pi)) { if (declaration) { // disallow non top-level declarations if (cursor->parent) PUGI__THROW_ERROR(status_bad_pi, s); PUGI__PUSHNODE(node_declaration); } else { PUGI__PUSHNODE(node_pi); } cursor->name = target; PUGI__ENDSEG(); // parse value/attributes if (ch == '?') { // empty node if (!ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s); s += (*s == '>'); PUGI__POPNODE(); } else if (PUGI__IS_CHARTYPE(ch, ct_space)) { PUGI__SKIPWS(); // scan for tag end char_t* value = s; PUGI__SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>')); PUGI__CHECK_ERROR(status_bad_pi, s); if (declaration) { // replace ending ? with / so that 'element' terminates properly *s = '/'; // we exit from this function with cursor at node_declaration, which is a signal to parse() to go to LOC_ATTRIBUTES s = value; } else { // store value and step over > cursor->value = value; PUGI__POPNODE(); PUGI__ENDSEG(); s += (*s == '>'); } } else PUGI__THROW_ERROR(status_bad_pi, s); } else { // scan for tag end PUGI__SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>')); PUGI__CHECK_ERROR(status_bad_pi, s); s += (s[1] == '>' ? 2 : 1); } // store from registers ref_cursor = cursor; return s; } char_t* parse(char_t* s, xml_node_struct* xmldoc, unsigned int optmsk, char_t endch) { strconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk); strconv_pcdata_t strconv_pcdata = get_strconv_pcdata(optmsk); char_t ch = 0; xml_node_struct* cursor = xmldoc; char_t* mark = s; while (*s != 0) { if (*s == '<') { ++s; LOC_TAG: if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // '<#...' { PUGI__PUSHNODE(node_element); // Append a new node to the tree. cursor->name = s; PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator. PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. if (ch == '>') { // end of tag } else if (PUGI__IS_CHARTYPE(ch, ct_space)) { LOC_ATTRIBUTES: while (true) { PUGI__SKIPWS(); // Eat any whitespace. if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #... { xml_attribute_struct* a = append_attribute_ll(cursor, alloc); // Make space for this attribute. if (!a) PUGI__THROW_ERROR(status_out_of_memory, s); a->name = s; // Save the offset. PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator. PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance if (PUGI__IS_CHARTYPE(ch, ct_space)) { PUGI__SKIPWS(); // Eat any whitespace. PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance ch = *s; ++s; } if (ch == '=') // '<... #=...' { PUGI__SKIPWS(); // Eat any whitespace. if (*s == '"' || *s == '\'') // '<... #="...' { ch = *s; // Save quote char to avoid breaking on "''" -or- '""'. ++s; // Step over the quote. a->value = s; // Save the offset. s = strconv_attribute(s, ch); if (!s) PUGI__THROW_ERROR(status_bad_attribute, a->value); // After this line the loop continues from the start; // Whitespaces, / and > are ok, symbols and EOF are wrong, // everything else will be detected if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_attribute, s); } else PUGI__THROW_ERROR(status_bad_attribute, s); } else PUGI__THROW_ERROR(status_bad_attribute, s); } else if (*s == '/') { ++s; if (*s == '>') { PUGI__POPNODE(); s++; break; } else if (*s == 0 && endch == '>') { PUGI__POPNODE(); break; } else PUGI__THROW_ERROR(status_bad_start_element, s); } else if (*s == '>') { ++s; break; } else if (*s == 0 && endch == '>') { break; } else PUGI__THROW_ERROR(status_bad_start_element, s); } // !!! } else if (ch == '/') // '<#.../' { if (!ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s); PUGI__POPNODE(); // Pop. s += (*s == '>'); } else if (ch == 0) { // we stepped over null terminator, backtrack & handle closing tag --s; if (endch != '>') PUGI__THROW_ERROR(status_bad_start_element, s); } else PUGI__THROW_ERROR(status_bad_start_element, s); } else if (*s == '/') { ++s; char_t* name = cursor->name; if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, s); while (PUGI__IS_CHARTYPE(*s, ct_symbol)) { if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, s); } if (*name) { if (*s == 0 && name[0] == endch && name[1] == 0) PUGI__THROW_ERROR(status_bad_end_element, s); else PUGI__THROW_ERROR(status_end_element_mismatch, s); } PUGI__POPNODE(); // Pop. PUGI__SKIPWS(); if (*s == 0) { if (endch != '>') PUGI__THROW_ERROR(status_bad_end_element, s); } else { if (*s != '>') PUGI__THROW_ERROR(status_bad_end_element, s); ++s; } } else if (*s == '?') // 'header & xml_memory_page_type_mask) + 1 == node_declaration) goto LOC_ATTRIBUTES; } else if (*s == '!') // 'first_child) continue; } } s = mark; if (cursor->parent) { PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree. cursor->value = s; // Save the offset. s = strconv_pcdata(s); PUGI__POPNODE(); // Pop since this is a standalone. if (!*s) break; } else { PUGI__SCANFOR(*s == '<'); // '...<' if (!*s) break; ++s; } // We're after '<' goto LOC_TAG; } } // check that last tag is closed if (cursor != xmldoc) PUGI__THROW_ERROR(status_end_element_mismatch, s); return s; } static xml_parse_result parse(char_t* buffer, size_t length, xml_node_struct* root, unsigned int optmsk) { xml_document_struct* xmldoc = static_cast(root); // store buffer for offset_debug xmldoc->buffer = buffer; // early-out for empty documents if (length == 0) return make_parse_result(status_ok); // create parser on stack xml_parser parser(*xmldoc); // save last character and make buffer zero-terminated (speeds up parsing) char_t endch = buffer[length - 1]; buffer[length - 1] = 0; // perform actual parsing parser.parse(buffer, xmldoc, optmsk, endch); xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0); assert(result.offset >= 0 && static_cast(result.offset) <= length); // update allocator state *static_cast(xmldoc) = parser.alloc; // since we removed last character, we have to handle the only possible false positive if (result && endch == '<') { // there's no possible well-formed document with < at the end return make_parse_result(status_unrecognized_tag, length); } return result; } }; // Output facilities PUGI__FN xml_encoding get_write_native_encoding() { #ifdef PUGIXML_WCHAR_MODE return get_wchar_encoding(); #else return encoding_utf8; #endif } PUGI__FN xml_encoding get_write_encoding(xml_encoding encoding) { // replace wchar encoding with utf implementation if (encoding == encoding_wchar) return get_wchar_encoding(); // replace utf16 encoding with utf16 with specific endianness if (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; // replace utf32 encoding with utf32 with specific endianness if (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; // only do autodetection if no explicit encoding is requested if (encoding != encoding_auto) return encoding; // assume utf8 encoding return encoding_utf8; } #ifdef PUGIXML_WCHAR_MODE PUGI__FN size_t get_valid_length(const char_t* data, size_t length) { assert(length > 0); // discard last character if it's the lead of a surrogate pair return (sizeof(wchar_t) == 2 && static_cast(static_cast(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length; } PUGI__FN size_t convert_buffer(char_t* r_char, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding) { // only endian-swapping is required if (need_endian_swap_utf(encoding, get_wchar_encoding())) { convert_wchar_endian_swap(r_char, data, length); return length * sizeof(char_t); } // convert to utf8 if (encoding == encoding_utf8) { uint8_t* dest = r_u8; uint8_t* end = utf_decoder::decode_wchar_block(data, length, dest); return static_cast(end - dest); } // convert to utf16 if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) { uint16_t* dest = r_u16; // convert to native utf16 uint16_t* end = utf_decoder::decode_wchar_block(data, length, dest); // swap if necessary xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); return static_cast(end - dest) * sizeof(uint16_t); } // convert to utf32 if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) { uint32_t* dest = r_u32; // convert to native utf32 uint32_t* end = utf_decoder::decode_wchar_block(data, length, dest); // swap if necessary xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); return static_cast(end - dest) * sizeof(uint32_t); } // convert to latin1 if (encoding == encoding_latin1) { uint8_t* dest = r_u8; uint8_t* end = utf_decoder::decode_wchar_block(data, length, dest); return static_cast(end - dest); } assert(!"Invalid encoding"); return 0; } #else PUGI__FN size_t get_valid_length(const char_t* data, size_t length) { assert(length > 4); for (size_t i = 1; i <= 4; ++i) { uint8_t ch = static_cast(data[length - i]); // either a standalone character or a leading one if ((ch & 0xc0) != 0x80) return length - i; } // there are four non-leading characters at the end, sequence tail is broken so might as well process the whole chunk return length; } PUGI__FN size_t convert_buffer(char_t* /* r_char */, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding) { if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) { uint16_t* dest = r_u16; // convert to native utf16 uint16_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); // swap if necessary xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); return static_cast(end - dest) * sizeof(uint16_t); } if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) { uint32_t* dest = r_u32; // convert to native utf32 uint32_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); // swap if necessary xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); return static_cast(end - dest) * sizeof(uint32_t); } if (encoding == encoding_latin1) { uint8_t* dest = r_u8; uint8_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); return static_cast(end - dest); } assert(!"Invalid encoding"); return 0; } #endif class xml_buffered_writer { xml_buffered_writer(const xml_buffered_writer&); xml_buffered_writer& operator=(const xml_buffered_writer&); public: xml_buffered_writer(xml_writer& writer_, xml_encoding user_encoding): writer(writer_), bufsize(0), encoding(get_write_encoding(user_encoding)) { PUGI__STATIC_ASSERT(bufcapacity >= 8); } ~xml_buffered_writer() { flush(); } void flush() { flush(buffer, bufsize); bufsize = 0; } void flush(const char_t* data, size_t size) { if (size == 0) return; // fast path, just write data if (encoding == get_write_native_encoding()) writer.write(data, size * sizeof(char_t)); else { // convert chunk size_t result = convert_buffer(scratch.data_char, scratch.data_u8, scratch.data_u16, scratch.data_u32, data, size, encoding); assert(result <= sizeof(scratch)); // write data writer.write(scratch.data_u8, result); } } void write(const char_t* data, size_t length) { if (bufsize + length > bufcapacity) { // flush the remaining buffer contents flush(); // handle large chunks if (length > bufcapacity) { if (encoding == get_write_native_encoding()) { // fast path, can just write data chunk writer.write(data, length * sizeof(char_t)); return; } // need to convert in suitable chunks while (length > bufcapacity) { // get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer // and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary) size_t chunk_size = get_valid_length(data, bufcapacity); // convert chunk and write flush(data, chunk_size); // iterate data += chunk_size; length -= chunk_size; } // small tail is copied below bufsize = 0; } } memcpy(buffer + bufsize, data, length * sizeof(char_t)); bufsize += length; } void write(const char_t* data) { write(data, strlength(data)); } void write(char_t d0) { if (bufsize + 1 > bufcapacity) flush(); buffer[bufsize + 0] = d0; bufsize += 1; } void write(char_t d0, char_t d1) { if (bufsize + 2 > bufcapacity) flush(); buffer[bufsize + 0] = d0; buffer[bufsize + 1] = d1; bufsize += 2; } void write(char_t d0, char_t d1, char_t d2) { if (bufsize + 3 > bufcapacity) flush(); buffer[bufsize + 0] = d0; buffer[bufsize + 1] = d1; buffer[bufsize + 2] = d2; bufsize += 3; } void write(char_t d0, char_t d1, char_t d2, char_t d3) { if (bufsize + 4 > bufcapacity) flush(); buffer[bufsize + 0] = d0; buffer[bufsize + 1] = d1; buffer[bufsize + 2] = d2; buffer[bufsize + 3] = d3; bufsize += 4; } void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4) { if (bufsize + 5 > bufcapacity) flush(); buffer[bufsize + 0] = d0; buffer[bufsize + 1] = d1; buffer[bufsize + 2] = d2; buffer[bufsize + 3] = d3; buffer[bufsize + 4] = d4; bufsize += 5; } void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5) { if (bufsize + 6 > bufcapacity) flush(); buffer[bufsize + 0] = d0; buffer[bufsize + 1] = d1; buffer[bufsize + 2] = d2; buffer[bufsize + 3] = d3; buffer[bufsize + 4] = d4; buffer[bufsize + 5] = d5; bufsize += 6; } // utf8 maximum expansion: x4 (-> utf32) // utf16 maximum expansion: x2 (-> utf32) // utf32 maximum expansion: x1 enum { bufcapacitybytes = #ifdef PUGIXML_MEMORY_OUTPUT_STACK PUGIXML_MEMORY_OUTPUT_STACK #else 10240 #endif , bufcapacity = bufcapacitybytes / (sizeof(char_t) + 4) }; char_t buffer[bufcapacity]; union { uint8_t data_u8[4 * bufcapacity]; uint16_t data_u16[2 * bufcapacity]; uint32_t data_u32[bufcapacity]; char_t data_char[bufcapacity]; } scratch; xml_writer& writer; size_t bufsize; xml_encoding encoding; }; PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type) { while (*s) { const char_t* prev = s; // While *s is a usual symbol while (!PUGI__IS_CHARTYPEX(*s, type)) ++s; writer.write(prev, static_cast(s - prev)); switch (*s) { case 0: break; case '&': writer.write('&', 'a', 'm', 'p', ';'); ++s; break; case '<': writer.write('&', 'l', 't', ';'); ++s; break; case '>': writer.write('&', 'g', 't', ';'); ++s; break; case '"': writer.write('&', 'q', 'u', 'o', 't', ';'); ++s; break; default: // s is not a usual symbol { unsigned int ch = static_cast(*s++); assert(ch < 32); writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((ch % 10) + '0'), ';'); } } } } PUGI__FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags) { if (flags & format_no_escapes) writer.write(s); else text_output_escaped(writer, s, type); } PUGI__FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s) { do { writer.write('<', '!', '[', 'C', 'D'); writer.write('A', 'T', 'A', '['); const char_t* prev = s; // look for ]]> sequence - we can't output it as is since it terminates CDATA while (*s && !(s[0] == ']' && s[1] == ']' && s[2] == '>')) ++s; // skip ]] if we stopped at ]]>, > will go to the next CDATA section if (*s) s += 2; writer.write(prev, static_cast(s - prev)); writer.write(']', ']', '>'); } while (*s); } PUGI__FN void node_output_attributes(xml_buffered_writer& writer, const xml_node& node, unsigned int flags) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); for (xml_attribute a = node.first_attribute(); a; a = a.next_attribute()) { writer.write(' '); writer.write(a.name()[0] ? a.name() : default_name); writer.write('=', '"'); text_output(writer, a.value(), ctx_special_attr, flags); writer.write('"'); } } PUGI__FN void node_output(xml_buffered_writer& writer, const xml_node& node, const char_t* indent, unsigned int flags, unsigned int depth) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); if ((flags & format_indent) != 0 && (flags & format_raw) == 0) for (unsigned int i = 0; i < depth; ++i) writer.write(indent); switch (node.type()) { case node_document: { for (xml_node n = node.first_child(); n; n = n.next_sibling()) node_output(writer, n, indent, flags, depth); break; } case node_element: { const char_t* name = node.name()[0] ? node.name() : default_name; writer.write('<'); writer.write(name); node_output_attributes(writer, node, flags); if (flags & format_raw) { if (!node.first_child()) writer.write(' ', '/', '>'); else { writer.write('>'); for (xml_node n = node.first_child(); n; n = n.next_sibling()) node_output(writer, n, indent, flags, depth + 1); writer.write('<', '/'); writer.write(name); writer.write('>'); } } else if (!node.first_child()) writer.write(' ', '/', '>', '\n'); else if (node.first_child() == node.last_child() && (node.first_child().type() == node_pcdata || node.first_child().type() == node_cdata)) { writer.write('>'); if (node.first_child().type() == node_pcdata) text_output(writer, node.first_child().value(), ctx_special_pcdata, flags); else text_output_cdata(writer, node.first_child().value()); writer.write('<', '/'); writer.write(name); writer.write('>', '\n'); } else { writer.write('>', '\n'); for (xml_node n = node.first_child(); n; n = n.next_sibling()) node_output(writer, n, indent, flags, depth + 1); if ((flags & format_indent) != 0 && (flags & format_raw) == 0) for (unsigned int i = 0; i < depth; ++i) writer.write(indent); writer.write('<', '/'); writer.write(name); writer.write('>', '\n'); } break; } case node_pcdata: text_output(writer, node.value(), ctx_special_pcdata, flags); if ((flags & format_raw) == 0) writer.write('\n'); break; case node_cdata: text_output_cdata(writer, node.value()); if ((flags & format_raw) == 0) writer.write('\n'); break; case node_comment: writer.write('<', '!', '-', '-'); writer.write(node.value()); writer.write('-', '-', '>'); if ((flags & format_raw) == 0) writer.write('\n'); break; case node_pi: case node_declaration: writer.write('<', '?'); writer.write(node.name()[0] ? node.name() : default_name); if (node.type() == node_declaration) { node_output_attributes(writer, node, flags); } else if (node.value()[0]) { writer.write(' '); writer.write(node.value()); } writer.write('?', '>'); if ((flags & format_raw) == 0) writer.write('\n'); break; case node_doctype: writer.write('<', '!', 'D', 'O', 'C'); writer.write('T', 'Y', 'P', 'E'); if (node.value()[0]) { writer.write(' '); writer.write(node.value()); } writer.write('>'); if ((flags & format_raw) == 0) writer.write('\n'); break; default: assert(!"Invalid node type"); } } inline bool has_declaration(const xml_node& node) { for (xml_node child = node.first_child(); child; child = child.next_sibling()) { xml_node_type type = child.type(); if (type == node_declaration) return true; if (type == node_element) return false; } return false; } inline bool allow_insert_child(xml_node_type parent, xml_node_type child) { if (parent != node_document && parent != node_element) return false; if (child == node_document || child == node_null) return false; if (parent != node_document && (child == node_declaration || child == node_doctype)) return false; return true; } PUGI__FN void recursive_copy_skip(xml_node& dest, const xml_node& source, const xml_node& skip) { assert(dest.type() == source.type()); switch (source.type()) { case node_element: { dest.set_name(source.name()); for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute()) dest.append_attribute(a.name()).set_value(a.value()); for (xml_node c = source.first_child(); c; c = c.next_sibling()) { if (c == skip) continue; xml_node cc = dest.append_child(c.type()); assert(cc); recursive_copy_skip(cc, c, skip); } break; } case node_pcdata: case node_cdata: case node_comment: case node_doctype: dest.set_value(source.value()); break; case node_pi: dest.set_name(source.name()); dest.set_value(source.value()); break; case node_declaration: { dest.set_name(source.name()); for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute()) dest.append_attribute(a.name()).set_value(a.value()); break; } default: assert(!"Invalid node type"); } } inline bool is_text_node(xml_node_struct* node) { xml_node_type type = static_cast((node->header & impl::xml_memory_page_type_mask) + 1); return type == node_pcdata || type == node_cdata; } // get value with conversion functions PUGI__FN int get_value_int(const char_t* value, int def) { if (!value) return def; #ifdef PUGIXML_WCHAR_MODE return static_cast(wcstol(value, 0, 10)); #else return static_cast(strtol(value, 0, 10)); #endif } PUGI__FN unsigned int get_value_uint(const char_t* value, unsigned int def) { if (!value) return def; #ifdef PUGIXML_WCHAR_MODE return static_cast(wcstoul(value, 0, 10)); #else return static_cast(strtoul(value, 0, 10)); #endif } PUGI__FN double get_value_double(const char_t* value, double def) { if (!value) return def; #ifdef PUGIXML_WCHAR_MODE return wcstod(value, 0); #else return strtod(value, 0); #endif } PUGI__FN float get_value_float(const char_t* value, float def) { if (!value) return def; #ifdef PUGIXML_WCHAR_MODE return static_cast(wcstod(value, 0)); #else return static_cast(strtod(value, 0)); #endif } PUGI__FN bool get_value_bool(const char_t* value, bool def) { if (!value) return def; // only look at first char char_t first = *value; // 1*, t* (true), T* (True), y* (yes), Y* (YES) return (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y'); } // set value with conversion functions PUGI__FN bool set_value_buffer(char_t*& dest, uintptr_t& header, uintptr_t header_mask, char (&buf)[128]) { #ifdef PUGIXML_WCHAR_MODE char_t wbuf[128]; impl::widen_ascii(wbuf, buf); return strcpy_insitu(dest, header, header_mask, wbuf); #else return strcpy_insitu(dest, header, header_mask, buf); #endif } PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, int value) { char buf[128]; sprintf(buf, "%d", value); return set_value_buffer(dest, header, header_mask, buf); } PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, unsigned int value) { char buf[128]; sprintf(buf, "%u", value); return set_value_buffer(dest, header, header_mask, buf); } PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, double value) { char buf[128]; sprintf(buf, "%g", value); return set_value_buffer(dest, header, header_mask, buf); } PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, bool value) { return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); } // we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell trick PUGI__FN xml_parse_status get_file_size(FILE* file, size_t& out_result) { #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE) // there are 64-bit versions of fseek/ftell, let's use them typedef __int64 length_type; _fseeki64(file, 0, SEEK_END); length_type length = _ftelli64(file); _fseeki64(file, 0, SEEK_SET); #elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && !defined(__STRICT_ANSI__) // there are 64-bit versions of fseek/ftell, let's use them typedef off64_t length_type; fseeko64(file, 0, SEEK_END); length_type length = ftello64(file); fseeko64(file, 0, SEEK_SET); #else // if this is a 32-bit OS, long is enough; if this is a unix system, long is 64-bit, which is enough; otherwise we can't do anything anyway. typedef long length_type; fseek(file, 0, SEEK_END); length_type length = ftell(file); fseek(file, 0, SEEK_SET); #endif // check for I/O errors if (length < 0) return status_io_error; // check for overflow size_t result = static_cast(length); if (static_cast(result) != length) return status_out_of_memory; // finalize out_result = result; return status_ok; } PUGI__FN xml_parse_result load_file_impl(xml_document& doc, FILE* file, unsigned int options, xml_encoding encoding) { if (!file) return make_parse_result(status_file_not_found); // get file size (can result in I/O errors) size_t size = 0; xml_parse_status size_status = get_file_size(file, size); if (size_status != status_ok) { fclose(file); return make_parse_result(size_status); } // allocate buffer for the whole file char* contents = static_cast(xml_memory::allocate(size > 0 ? size : 1)); if (!contents) { fclose(file); return make_parse_result(status_out_of_memory); } // read file in memory size_t read_size = fread(contents, 1, size, file); fclose(file); if (read_size != size) { xml_memory::deallocate(contents); return make_parse_result(status_io_error); } return doc.load_buffer_inplace_own(contents, size, options, encoding); } #ifndef PUGIXML_NO_STL template struct xml_stream_chunk { static xml_stream_chunk* create() { void* memory = xml_memory::allocate(sizeof(xml_stream_chunk)); return new (memory) xml_stream_chunk(); } static void destroy(void* ptr) { xml_stream_chunk* chunk = static_cast(ptr); // free chunk chain while (chunk) { xml_stream_chunk* next = chunk->next; xml_memory::deallocate(chunk); chunk = next; } } xml_stream_chunk(): next(0), size(0) { } xml_stream_chunk* next; size_t size; T data[xml_memory_page_size / sizeof(T)]; }; template PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream& stream, void** out_buffer, size_t* out_size) { buffer_holder chunks(0, xml_stream_chunk::destroy); // read file to a chunk list size_t total = 0; xml_stream_chunk* last = 0; while (!stream.eof()) { // allocate new chunk xml_stream_chunk* chunk = xml_stream_chunk::create(); if (!chunk) return status_out_of_memory; // append chunk to list if (last) last = last->next = chunk; else chunks.data = last = chunk; // read data to chunk stream.read(chunk->data, static_cast(sizeof(chunk->data) / sizeof(T))); chunk->size = static_cast(stream.gcount()) * sizeof(T); // read may set failbit | eofbit in case gcount() is less than read length, so check for other I/O errors if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error; // guard against huge files (chunk size is small enough to make this overflow check work) if (total + chunk->size < total) return status_out_of_memory; total += chunk->size; } // copy chunk list to a contiguous buffer char* buffer = static_cast(xml_memory::allocate(total)); if (!buffer) return status_out_of_memory; char* write = buffer; for (xml_stream_chunk* chunk = static_cast*>(chunks.data); chunk; chunk = chunk->next) { assert(write + chunk->size <= buffer + total); memcpy(write, chunk->data, chunk->size); write += chunk->size; } assert(write == buffer + total); // return buffer *out_buffer = buffer; *out_size = total; return status_ok; } template PUGI__FN xml_parse_status load_stream_data_seek(std::basic_istream& stream, void** out_buffer, size_t* out_size) { // get length of remaining data in stream typename std::basic_istream::pos_type pos = stream.tellg(); stream.seekg(0, std::ios::end); std::streamoff length = stream.tellg() - pos; stream.seekg(pos); if (stream.fail() || pos < 0) return status_io_error; // guard against huge files size_t read_length = static_cast(length); if (static_cast(read_length) != length || length < 0) return status_out_of_memory; // read stream data into memory (guard against stream exceptions with buffer holder) buffer_holder buffer(xml_memory::allocate((read_length > 0 ? read_length : 1) * sizeof(T)), xml_memory::deallocate); if (!buffer.data) return status_out_of_memory; stream.read(static_cast(buffer.data), static_cast(read_length)); // read may set failbit | eofbit in case gcount() is less than read_length (i.e. line ending conversion), so check for other I/O errors if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error; // return buffer size_t actual_length = static_cast(stream.gcount()); assert(actual_length <= read_length); *out_buffer = buffer.release(); *out_size = actual_length * sizeof(T); return status_ok; } template PUGI__FN xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream& stream, unsigned int options, xml_encoding encoding) { void* buffer = 0; size_t size = 0; // load stream to memory (using seek-based implementation if possible, since it's faster and takes less memory) xml_parse_status status = (stream.tellg() < 0) ? load_stream_data_noseek(stream, &buffer, &size) : load_stream_data_seek(stream, &buffer, &size); if (status != status_ok) return make_parse_result(status); return doc.load_buffer_inplace_own(buffer, size, options, encoding); } #endif #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && !defined(__STRICT_ANSI__)) PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) { return _wfopen(path, mode); } #else PUGI__FN char* convert_path_heap(const wchar_t* str) { assert(str); // first pass: get length in utf8 characters size_t length = wcslen(str); size_t size = as_utf8_begin(str, length); // allocate resulting string char* result = static_cast(xml_memory::allocate(size + 1)); if (!result) return 0; // second pass: convert to utf8 as_utf8_end(result, size, str, length); return result; } PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) { // there is no standard function to open wide paths, so our best bet is to try utf8 path char* path_utf8 = convert_path_heap(path); if (!path_utf8) return 0; // convert mode to ASCII (we mirror _wfopen interface) char mode_ascii[4] = {0}; for (size_t i = 0; mode[i]; ++i) mode_ascii[i] = static_cast(mode[i]); // try to open the utf8 path FILE* result = fopen(path_utf8, mode_ascii); // free dummy buffer xml_memory::deallocate(path_utf8); return result; } #endif PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding) { if (!file) return false; xml_writer_file writer(file); doc.save(writer, indent, flags, encoding); int result = ferror(file); fclose(file); return result == 0; } PUGI__NS_END OIIO_NAMESPACE_BEGIN namespace pugi { PUGI__FN xml_writer_file::xml_writer_file(void* file_): file(file_) { } PUGI__FN void xml_writer_file::write(const void* data, size_t size) { size_t result = fwrite(data, 1, size, static_cast(file)); (void)!result; // unfortunately we can't do proper error handling here } #ifndef PUGIXML_NO_STL PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(&stream), wide_stream(0) { } PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(0), wide_stream(&stream) { } PUGI__FN void xml_writer_stream::write(const void* data, size_t size) { if (narrow_stream) { assert(!wide_stream); narrow_stream->write(reinterpret_cast(data), static_cast(size)); } else { assert(wide_stream); assert(size % sizeof(wchar_t) == 0); wide_stream->write(reinterpret_cast(data), static_cast(size / sizeof(wchar_t))); } } #endif PUGI__FN xml_tree_walker::xml_tree_walker(): _depth(0) { } PUGI__FN xml_tree_walker::~xml_tree_walker() { } PUGI__FN int xml_tree_walker::depth() const { return _depth; } PUGI__FN bool xml_tree_walker::begin(xml_node&) { return true; } PUGI__FN bool xml_tree_walker::end(xml_node&) { return true; } PUGI__FN xml_attribute::xml_attribute(): _attr(0) { } PUGI__FN xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr) { } PUGI__FN static void unspecified_bool_xml_attribute(xml_attribute***) { } PUGI__FN xml_attribute::operator xml_attribute::unspecified_bool_type() const { return _attr ? unspecified_bool_xml_attribute : 0; } PUGI__FN bool xml_attribute::operator!() const { return !_attr; } PUGI__FN bool xml_attribute::operator==(const xml_attribute& r) const { return (_attr == r._attr); } PUGI__FN bool xml_attribute::operator!=(const xml_attribute& r) const { return (_attr != r._attr); } PUGI__FN bool xml_attribute::operator<(const xml_attribute& r) const { return (_attr < r._attr); } PUGI__FN bool xml_attribute::operator>(const xml_attribute& r) const { return (_attr > r._attr); } PUGI__FN bool xml_attribute::operator<=(const xml_attribute& r) const { return (_attr <= r._attr); } PUGI__FN bool xml_attribute::operator>=(const xml_attribute& r) const { return (_attr >= r._attr); } PUGI__FN xml_attribute xml_attribute::next_attribute() const { return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute(); } PUGI__FN xml_attribute xml_attribute::previous_attribute() const { return _attr && _attr->prev_attribute_c->next_attribute ? xml_attribute(_attr->prev_attribute_c) : xml_attribute(); } PUGI__FN const char_t* xml_attribute::as_string(const char_t* def) const { return (_attr && _attr->value) ? _attr->value : def; } PUGI__FN int xml_attribute::as_int(int def) const { return impl::get_value_int(_attr ? _attr->value : 0, def); } PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const { return impl::get_value_uint(_attr ? _attr->value : 0, def); } PUGI__FN double xml_attribute::as_double(double def) const { return impl::get_value_double(_attr ? _attr->value : 0, def); } PUGI__FN float xml_attribute::as_float(float def) const { return impl::get_value_float(_attr ? _attr->value : 0, def); } PUGI__FN bool xml_attribute::as_bool(bool def) const { return impl::get_value_bool(_attr ? _attr->value : 0, def); } PUGI__FN bool xml_attribute::empty() const { return !_attr; } PUGI__FN const char_t* xml_attribute::name() const { return (_attr && _attr->name) ? _attr->name : PUGIXML_TEXT(""); } PUGI__FN const char_t* xml_attribute::value() const { return (_attr && _attr->value) ? _attr->value : PUGIXML_TEXT(""); } PUGI__FN size_t xml_attribute::hash_value() const { return static_cast(reinterpret_cast(_attr) / sizeof(xml_attribute_struct)); } PUGI__FN xml_attribute_struct* xml_attribute::internal_object() const { return _attr; } PUGI__FN xml_attribute& xml_attribute::operator=(const char_t* rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(int rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(unsigned int rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(double rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs) { set_value(rhs); return *this; } PUGI__FN bool xml_attribute::set_name(const char_t* rhs) { if (!_attr) return false; return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs); } PUGI__FN bool xml_attribute::set_value(const char_t* rhs) { if (!_attr) return false; return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } PUGI__FN bool xml_attribute::set_value(int rhs) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } PUGI__FN bool xml_attribute::set_value(unsigned int rhs) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } PUGI__FN bool xml_attribute::set_value(double rhs) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } PUGI__FN bool xml_attribute::set_value(bool rhs) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } #ifdef __BORLANDC__ PUGI__FN bool operator&&(const xml_attribute& lhs, bool rhs) { return (bool)lhs && rhs; } PUGI__FN bool operator||(const xml_attribute& lhs, bool rhs) { return (bool)lhs || rhs; } #endif PUGI__FN xml_node::xml_node(): _root(0) { } PUGI__FN xml_node::xml_node(xml_node_struct* p): _root(p) { } PUGI__FN static void unspecified_bool_xml_node(xml_node***) { } PUGI__FN xml_node::operator xml_node::unspecified_bool_type() const { return _root ? unspecified_bool_xml_node : 0; } PUGI__FN bool xml_node::operator!() const { return !_root; } PUGI__FN xml_node::iterator xml_node::begin() const { return iterator(_root ? _root->first_child : 0, _root); } PUGI__FN xml_node::iterator xml_node::end() const { return iterator(0, _root); } PUGI__FN xml_node::attribute_iterator xml_node::attributes_begin() const { return attribute_iterator(_root ? _root->first_attribute : 0, _root); } PUGI__FN xml_node::attribute_iterator xml_node::attributes_end() const { return attribute_iterator(0, _root); } PUGI__FN xml_object_range xml_node::children() const { return xml_object_range(begin(), end()); } PUGI__FN xml_object_range xml_node::children(const char_t* name_) const { return xml_object_range(xml_named_node_iterator(child(name_), name_), xml_named_node_iterator()); } PUGI__FN xml_object_range xml_node::attributes() const { return xml_object_range(attributes_begin(), attributes_end()); } PUGI__FN bool xml_node::operator==(const xml_node& r) const { return (_root == r._root); } PUGI__FN bool xml_node::operator!=(const xml_node& r) const { return (_root != r._root); } PUGI__FN bool xml_node::operator<(const xml_node& r) const { return (_root < r._root); } PUGI__FN bool xml_node::operator>(const xml_node& r) const { return (_root > r._root); } PUGI__FN bool xml_node::operator<=(const xml_node& r) const { return (_root <= r._root); } PUGI__FN bool xml_node::operator>=(const xml_node& r) const { return (_root >= r._root); } PUGI__FN bool xml_node::empty() const { return !_root; } PUGI__FN const char_t* xml_node::name() const { return (_root && _root->name) ? _root->name : PUGIXML_TEXT(""); } PUGI__FN xml_node_type xml_node::type() const { return _root ? static_cast((_root->header & impl::xml_memory_page_type_mask) + 1) : node_null; } PUGI__FN const char_t* xml_node::value() const { return (_root && _root->value) ? _root->value : PUGIXML_TEXT(""); } PUGI__FN xml_node xml_node::child(const char_t* name_) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) if (i->name && impl::strequal(name_, i->name)) return xml_node(i); return xml_node(); } PUGI__FN xml_attribute xml_node::attribute(const char_t* name_) const { if (!_root) return xml_attribute(); for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute) if (i->name && impl::strequal(name_, i->name)) return xml_attribute(i); return xml_attribute(); } PUGI__FN xml_node xml_node::next_sibling(const char_t* name_) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling) if (i->name && impl::strequal(name_, i->name)) return xml_node(i); return xml_node(); } PUGI__FN xml_node xml_node::next_sibling() const { if (!_root) return xml_node(); if (_root->next_sibling) return xml_node(_root->next_sibling); else return xml_node(); } PUGI__FN xml_node xml_node::previous_sibling(const char_t* name_) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c) if (i->name && impl::strequal(name_, i->name)) return xml_node(i); return xml_node(); } PUGI__FN xml_node xml_node::previous_sibling() const { if (!_root) return xml_node(); if (_root->prev_sibling_c->next_sibling) return xml_node(_root->prev_sibling_c); else return xml_node(); } PUGI__FN xml_node xml_node::parent() const { return _root ? xml_node(_root->parent) : xml_node(); } PUGI__FN xml_node xml_node::root() const { if (!_root) return xml_node(); impl::xml_memory_page* page = reinterpret_cast(_root->header & impl::xml_memory_page_pointer_mask); return xml_node(static_cast(page->allocator)); } PUGI__FN xml_text xml_node::text() const { return xml_text(_root); } PUGI__FN const char_t* xml_node::child_value() const { if (!_root) return PUGIXML_TEXT(""); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) if (i->value && impl::is_text_node(i)) return i->value; return PUGIXML_TEXT(""); } PUGI__FN const char_t* xml_node::child_value(const char_t* name_) const { return child(name_).child_value(); } PUGI__FN xml_attribute xml_node::first_attribute() const { return _root ? xml_attribute(_root->first_attribute) : xml_attribute(); } PUGI__FN xml_attribute xml_node::last_attribute() const { return _root && _root->first_attribute ? xml_attribute(_root->first_attribute->prev_attribute_c) : xml_attribute(); } PUGI__FN xml_node xml_node::first_child() const { return _root ? xml_node(_root->first_child) : xml_node(); } PUGI__FN xml_node xml_node::last_child() const { return _root && _root->first_child ? xml_node(_root->first_child->prev_sibling_c) : xml_node(); } PUGI__FN bool xml_node::set_name(const char_t* rhs) { switch (type()) { case node_pi: case node_declaration: case node_element: return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs); default: return false; } } PUGI__FN bool xml_node::set_value(const char_t* rhs) { switch (type()) { case node_pi: case node_cdata: case node_pcdata: case node_comment: case node_doctype: return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs); default: return false; } } PUGI__FN xml_attribute xml_node::append_attribute(const char_t* name_) { if (type() != node_element && type() != node_declaration) return xml_attribute(); xml_attribute a(impl::append_attribute_ll(_root, impl::get_allocator(_root))); a.set_name(name_); return a; } PUGI__FN xml_attribute xml_node::prepend_attribute(const char_t* name_) { if (type() != node_element && type() != node_declaration) return xml_attribute(); xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); if (!a) return xml_attribute(); a.set_name(name_); xml_attribute_struct* head = _root->first_attribute; if (head) { a._attr->prev_attribute_c = head->prev_attribute_c; head->prev_attribute_c = a._attr; } else a._attr->prev_attribute_c = a._attr; a._attr->next_attribute = head; _root->first_attribute = a._attr; return a; } PUGI__FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr) { if ((type() != node_element && type() != node_declaration) || attr.empty()) return xml_attribute(); // check that attribute belongs to *this xml_attribute_struct* cur = attr._attr; while (cur->prev_attribute_c->next_attribute) cur = cur->prev_attribute_c; if (cur != _root->first_attribute) return xml_attribute(); xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); if (!a) return xml_attribute(); a.set_name(name_); if (attr._attr->prev_attribute_c->next_attribute) attr._attr->prev_attribute_c->next_attribute = a._attr; else _root->first_attribute = a._attr; a._attr->prev_attribute_c = attr._attr->prev_attribute_c; a._attr->next_attribute = attr._attr; attr._attr->prev_attribute_c = a._attr; return a; } PUGI__FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr) { if ((type() != node_element && type() != node_declaration) || attr.empty()) return xml_attribute(); // check that attribute belongs to *this xml_attribute_struct* cur = attr._attr; while (cur->prev_attribute_c->next_attribute) cur = cur->prev_attribute_c; if (cur != _root->first_attribute) return xml_attribute(); xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); if (!a) return xml_attribute(); a.set_name(name_); if (attr._attr->next_attribute) attr._attr->next_attribute->prev_attribute_c = a._attr; else _root->first_attribute->prev_attribute_c = a._attr; a._attr->next_attribute = attr._attr->next_attribute; a._attr->prev_attribute_c = attr._attr; attr._attr->next_attribute = a._attr; return a; } PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto) { if (!proto) return xml_attribute(); xml_attribute result = append_attribute(proto.name()); result.set_value(proto.value()); return result; } PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto) { if (!proto) return xml_attribute(); xml_attribute result = prepend_attribute(proto.name()); result.set_value(proto.value()); return result; } PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr) { if (!proto) return xml_attribute(); xml_attribute result = insert_attribute_after(proto.name(), attr); result.set_value(proto.value()); return result; } PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr) { if (!proto) return xml_attribute(); xml_attribute result = insert_attribute_before(proto.name(), attr); result.set_value(proto.value()); return result; } PUGI__FN xml_node xml_node::append_child(xml_node_type type_) { if (!impl::allow_insert_child(this->type(), type_)) return xml_node(); xml_node n(impl::append_node(_root, impl::get_allocator(_root), type_)); if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); return n; } PUGI__FN xml_node xml_node::prepend_child(xml_node_type type_) { if (!impl::allow_insert_child(this->type(), type_)) return xml_node(); xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); if (!n) return xml_node(); n._root->parent = _root; xml_node_struct* head = _root->first_child; if (head) { n._root->prev_sibling_c = head->prev_sibling_c; head->prev_sibling_c = n._root; } else n._root->prev_sibling_c = n._root; n._root->next_sibling = head; _root->first_child = n._root; if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); return n; } PUGI__FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node& node) { if (!impl::allow_insert_child(this->type(), type_)) return xml_node(); if (!node._root || node._root->parent != _root) return xml_node(); xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); if (!n) return xml_node(); n._root->parent = _root; if (node._root->prev_sibling_c->next_sibling) node._root->prev_sibling_c->next_sibling = n._root; else _root->first_child = n._root; n._root->prev_sibling_c = node._root->prev_sibling_c; n._root->next_sibling = node._root; node._root->prev_sibling_c = n._root; if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); return n; } PUGI__FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node) { if (!impl::allow_insert_child(this->type(), type_)) return xml_node(); if (!node._root || node._root->parent != _root) return xml_node(); xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); if (!n) return xml_node(); n._root->parent = _root; if (node._root->next_sibling) node._root->next_sibling->prev_sibling_c = n._root; else _root->first_child->prev_sibling_c = n._root; n._root->next_sibling = node._root->next_sibling; n._root->prev_sibling_c = node._root; node._root->next_sibling = n._root; if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); return n; } PUGI__FN xml_node xml_node::append_child(const char_t* name_) { xml_node result = append_child(node_element); result.set_name(name_); return result; } PUGI__FN xml_node xml_node::prepend_child(const char_t* name_) { xml_node result = prepend_child(node_element); result.set_name(name_); return result; } PUGI__FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node) { xml_node result = insert_child_after(node_element, node); result.set_name(name_); return result; } PUGI__FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node) { xml_node result = insert_child_before(node_element, node); result.set_name(name_); return result; } PUGI__FN xml_node xml_node::append_copy(const xml_node& proto) { xml_node result = append_child(proto.type()); if (result) impl::recursive_copy_skip(result, proto, result); return result; } PUGI__FN xml_node xml_node::prepend_copy(const xml_node& proto) { xml_node result = prepend_child(proto.type()); if (result) impl::recursive_copy_skip(result, proto, result); return result; } PUGI__FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node) { xml_node result = insert_child_after(proto.type(), node); if (result) impl::recursive_copy_skip(result, proto, result); return result; } PUGI__FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node) { xml_node result = insert_child_before(proto.type(), node); if (result) impl::recursive_copy_skip(result, proto, result); return result; } PUGI__FN bool xml_node::remove_attribute(const char_t* name_) { return remove_attribute(attribute(name_)); } PUGI__FN bool xml_node::remove_attribute(const xml_attribute& a) { if (!_root || !a._attr) return false; // check that attribute belongs to *this xml_attribute_struct* attr = a._attr; while (attr->prev_attribute_c->next_attribute) attr = attr->prev_attribute_c; if (attr != _root->first_attribute) return false; if (a._attr->next_attribute) a._attr->next_attribute->prev_attribute_c = a._attr->prev_attribute_c; else if (_root->first_attribute) _root->first_attribute->prev_attribute_c = a._attr->prev_attribute_c; if (a._attr->prev_attribute_c->next_attribute) a._attr->prev_attribute_c->next_attribute = a._attr->next_attribute; else _root->first_attribute = a._attr->next_attribute; impl::destroy_attribute(a._attr, impl::get_allocator(_root)); return true; } PUGI__FN bool xml_node::remove_child(const char_t* name_) { return remove_child(child(name_)); } PUGI__FN bool xml_node::remove_child(const xml_node& n) { if (!_root || !n._root || n._root->parent != _root) return false; if (n._root->next_sibling) n._root->next_sibling->prev_sibling_c = n._root->prev_sibling_c; else if (_root->first_child) _root->first_child->prev_sibling_c = n._root->prev_sibling_c; if (n._root->prev_sibling_c->next_sibling) n._root->prev_sibling_c->next_sibling = n._root->next_sibling; else _root->first_child = n._root->next_sibling; impl::destroy_node(n._root, impl::get_allocator(_root)); return true; } PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) if (i->name && impl::strequal(name_, i->name)) { for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) if (impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value)) return xml_node(i); } return xml_node(); } PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) if (impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value)) return xml_node(i); return xml_node(); } #ifndef PUGIXML_NO_STL PUGI__FN string_t xml_node::path(char_t delimiter) const { xml_node cursor = *this; // Make a copy. string_t result = cursor.name(); while (cursor.parent()) { cursor = cursor.parent(); string_t temp = cursor.name(); temp += delimiter; temp += result; result.swap(temp); } return result; } #endif PUGI__FN xml_node xml_node::first_element_by_path(const char_t* path_, char_t delimiter) const { xml_node found = *this; // Current search context. if (!_root || !path_ || !path_[0]) return found; if (path_[0] == delimiter) { // Absolute path; e.g. '/foo/bar' found = found.root(); ++path_; } const char_t* path_segment = path_; while (*path_segment == delimiter) ++path_segment; const char_t* path_segment_end = path_segment; while (*path_segment_end && *path_segment_end != delimiter) ++path_segment_end; if (path_segment == path_segment_end) return found; const char_t* next_segment = path_segment_end; while (*next_segment == delimiter) ++next_segment; if (*path_segment == '.' && path_segment + 1 == path_segment_end) return found.first_element_by_path(next_segment, delimiter); else if (*path_segment == '.' && *(path_segment+1) == '.' && path_segment + 2 == path_segment_end) return found.parent().first_element_by_path(next_segment, delimiter); else { for (xml_node_struct* j = found._root->first_child; j; j = j->next_sibling) { if (j->name && impl::strequalrange(j->name, path_segment, static_cast(path_segment_end - path_segment))) { xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter); if (subsearch) return subsearch; } } return xml_node(); } } PUGI__FN bool xml_node::traverse(xml_tree_walker& walker) { walker._depth = -1; xml_node arg_begin = *this; if (!walker.begin(arg_begin)) return false; xml_node cur = first_child(); if (cur) { ++walker._depth; do { xml_node arg_for_each = cur; if (!walker.for_each(arg_for_each)) return false; if (cur.first_child()) { ++walker._depth; cur = cur.first_child(); } else if (cur.next_sibling()) cur = cur.next_sibling(); else { // Borland C++ workaround while (!cur.next_sibling() && cur != *this && !cur.parent().empty()) { --walker._depth; cur = cur.parent(); } if (cur != *this) cur = cur.next_sibling(); } } while (cur && cur != *this); } assert(walker._depth == -1); xml_node arg_end = *this; return walker.end(arg_end); } PUGI__FN size_t xml_node::hash_value() const { return static_cast(reinterpret_cast(_root) / sizeof(xml_node_struct)); } PUGI__FN xml_node_struct* xml_node::internal_object() const { return _root; } PUGI__FN void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const { if (!_root) return; impl::xml_buffered_writer buffered_writer(writer, encoding); impl::node_output(buffered_writer, *this, indent, flags, depth); } #ifndef PUGIXML_NO_STL PUGI__FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const { xml_writer_stream writer(stream); print(writer, indent, flags, encoding, depth); } PUGI__FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, unsigned int depth) const { xml_writer_stream writer(stream); print(writer, indent, flags, encoding_wchar, depth); } #endif PUGI__FN ptrdiff_t xml_node::offset_debug() const { xml_node_struct* r = root()._root; if (!r) return -1; const char_t* buffer = static_cast(r)->buffer; if (!buffer) return -1; switch (type()) { case node_document: return 0; case node_element: case node_declaration: case node_pi: return (_root->header & impl::xml_memory_page_name_allocated_mask) ? -1 : _root->name - buffer; case node_pcdata: case node_cdata: case node_comment: case node_doctype: return (_root->header & impl::xml_memory_page_value_allocated_mask) ? -1 : _root->value - buffer; default: return -1; } } #ifdef __BORLANDC__ PUGI__FN bool operator&&(const xml_node& lhs, bool rhs) { return (bool)lhs && rhs; } PUGI__FN bool operator||(const xml_node& lhs, bool rhs) { return (bool)lhs || rhs; } #endif PUGI__FN xml_text::xml_text(xml_node_struct* root): _root(root) { } PUGI__FN xml_node_struct* xml_text::_data() const { if (!_root || impl::is_text_node(_root)) return _root; for (xml_node_struct* node = _root->first_child; node; node = node->next_sibling) if (impl::is_text_node(node)) return node; return 0; } PUGI__FN xml_node_struct* xml_text::_data_new() { xml_node_struct* d = _data(); if (d) return d; return xml_node(_root).append_child(node_pcdata).internal_object(); } PUGI__FN xml_text::xml_text(): _root(0) { } PUGI__FN static void unspecified_bool_xml_text(xml_text***) { } PUGI__FN xml_text::operator xml_text::unspecified_bool_type() const { return _data() ? unspecified_bool_xml_text : 0; } PUGI__FN bool xml_text::operator!() const { return !_data(); } PUGI__FN bool xml_text::empty() const { return _data() == 0; } PUGI__FN const char_t* xml_text::get() const { xml_node_struct* d = _data(); return (d && d->value) ? d->value : PUGIXML_TEXT(""); } PUGI__FN const char_t* xml_text::as_string(const char_t* def) const { xml_node_struct* d = _data(); return (d && d->value) ? d->value : def; } PUGI__FN int xml_text::as_int(int def) const { xml_node_struct* d = _data(); return impl::get_value_int(d ? d->value : 0, def); } PUGI__FN unsigned int xml_text::as_uint(unsigned int def) const { xml_node_struct* d = _data(); return impl::get_value_uint(d ? d->value : 0, def); } PUGI__FN double xml_text::as_double(double def) const { xml_node_struct* d = _data(); return impl::get_value_double(d ? d->value : 0, def); } PUGI__FN float xml_text::as_float(float def) const { xml_node_struct* d = _data(); return impl::get_value_float(d ? d->value : 0, def); } PUGI__FN bool xml_text::as_bool(bool def) const { xml_node_struct* d = _data(); return impl::get_value_bool(d ? d->value : 0, def); } PUGI__FN bool xml_text::set(const char_t* rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } PUGI__FN bool xml_text::set(int rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } PUGI__FN bool xml_text::set(unsigned int rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } PUGI__FN bool xml_text::set(double rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } PUGI__FN bool xml_text::set(bool rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } PUGI__FN xml_text& xml_text::operator=(const char_t* rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(int rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(unsigned int rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(double rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(bool rhs) { set(rhs); return *this; } PUGI__FN xml_node xml_text::data() const { return xml_node(_data()); } #ifdef __BORLANDC__ PUGI__FN bool operator&&(const xml_text& lhs, bool rhs) { return (bool)lhs && rhs; } PUGI__FN bool operator||(const xml_text& lhs, bool rhs) { return (bool)lhs || rhs; } #endif PUGI__FN xml_node_iterator::xml_node_iterator() { } PUGI__FN xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent()) { } PUGI__FN xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) { } PUGI__FN bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const { return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; } PUGI__FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const { return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; } PUGI__FN xml_node& xml_node_iterator::operator*() const { assert(_wrap._root); return _wrap; } PUGI__FN xml_node* xml_node_iterator::operator->() const { assert(_wrap._root); return const_cast(&_wrap); // BCC32 workaround } PUGI__FN const xml_node_iterator& xml_node_iterator::operator++() { assert(_wrap._root); _wrap._root = _wrap._root->next_sibling; return *this; } PUGI__FN xml_node_iterator xml_node_iterator::operator++(int) { xml_node_iterator temp = *this; ++*this; return temp; } PUGI__FN const xml_node_iterator& xml_node_iterator::operator--() { _wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child(); return *this; } PUGI__FN xml_node_iterator xml_node_iterator::operator--(int) { xml_node_iterator temp = *this; --*this; return temp; } PUGI__FN xml_attribute_iterator::xml_attribute_iterator() { } PUGI__FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent) { } PUGI__FN xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) { } PUGI__FN bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const { return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root; } PUGI__FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const { return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root; } PUGI__FN xml_attribute& xml_attribute_iterator::operator*() const { assert(_wrap._attr); return _wrap; } PUGI__FN xml_attribute* xml_attribute_iterator::operator->() const { assert(_wrap._attr); return const_cast(&_wrap); // BCC32 workaround } PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator++() { assert(_wrap._attr); _wrap._attr = _wrap._attr->next_attribute; return *this; } PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator++(int) { xml_attribute_iterator temp = *this; ++*this; return temp; } PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator--() { _wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute(); return *this; } PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator--(int) { xml_attribute_iterator temp = *this; --*this; return temp; } PUGI__FN xml_named_node_iterator::xml_named_node_iterator(): _name(0) { } PUGI__FN xml_named_node_iterator::xml_named_node_iterator(const xml_node& node, const char_t* name): _node(node), _name(name) { } PUGI__FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator& rhs) const { return _node == rhs._node; } PUGI__FN bool xml_named_node_iterator::operator!=(const xml_named_node_iterator& rhs) const { return _node != rhs._node; } PUGI__FN xml_node& xml_named_node_iterator::operator*() const { assert(_node._root); return _node; } PUGI__FN xml_node* xml_named_node_iterator::operator->() const { assert(_node._root); return const_cast(&_node); // BCC32 workaround } PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator++() { assert(_node._root); _node = _node.next_sibling(_name); return *this; } PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator++(int) { xml_named_node_iterator temp = *this; ++*this; return temp; } PUGI__FN xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto) { } PUGI__FN xml_parse_result::operator bool() const { return status == status_ok; } PUGI__FN const char* xml_parse_result::description() const { switch (status) { case status_ok: return "No error"; case status_file_not_found: return "File was not found"; case status_io_error: return "Error reading from file/stream"; case status_out_of_memory: return "Could not allocate memory"; case status_internal_error: return "Internal error occurred"; case status_unrecognized_tag: return "Could not determine tag type"; case status_bad_pi: return "Error parsing document declaration/processing instruction"; case status_bad_comment: return "Error parsing comment"; case status_bad_cdata: return "Error parsing CDATA section"; case status_bad_doctype: return "Error parsing document type declaration"; case status_bad_pcdata: return "Error parsing PCDATA section"; case status_bad_start_element: return "Error parsing start element tag"; case status_bad_attribute: return "Error parsing element attribute"; case status_bad_end_element: return "Error parsing end element tag"; case status_end_element_mismatch: return "Start-end tags mismatch"; default: return "Unknown error"; } } PUGI__FN xml_document::xml_document(): _buffer(0) { create(); } PUGI__FN xml_document::~xml_document() { destroy(); } PUGI__FN void xml_document::reset() { destroy(); create(); } PUGI__FN void xml_document::reset(const xml_document& proto) { reset(); for (xml_node cur = proto.first_child(); cur; cur = cur.next_sibling()) append_copy(cur); } PUGI__FN void xml_document::create() { // initialize sentinel page PUGI__STATIC_ASSERT(offsetof(impl::xml_memory_page, data) + sizeof(impl::xml_document_struct) + impl::xml_memory_page_alignment <= sizeof(_memory)); // align upwards to page boundary void* page_memory = reinterpret_cast((reinterpret_cast(_memory) + (impl::xml_memory_page_alignment - 1)) & ~(impl::xml_memory_page_alignment - 1)); // prepare page structure impl::xml_memory_page* page = impl::xml_memory_page::construct(page_memory); page->busy_size = impl::xml_memory_page_size; // allocate new root _root = new (page->data) impl::xml_document_struct(page); _root->prev_sibling_c = _root; // setup sentinel page page->allocator = static_cast(_root); } PUGI__FN void xml_document::destroy() { // destroy static storage if (_buffer) { impl::xml_memory::deallocate(_buffer); _buffer = 0; } // destroy dynamic storage, leave sentinel page (it's in static memory) if (_root) { impl::xml_memory_page* root_page = reinterpret_cast(_root->header & impl::xml_memory_page_pointer_mask); assert(root_page && !root_page->prev && !root_page->memory); // destroy all pages for (impl::xml_memory_page* page = root_page->next; page; ) { impl::xml_memory_page* next = page->next; impl::xml_allocator::deallocate_page(page); page = next; } // cleanup root page root_page->allocator = 0; root_page->next = 0; root_page->busy_size = root_page->freed_size = 0; _root = 0; } } #ifndef PUGIXML_NO_STL PUGI__FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options, xml_encoding encoding) { reset(); return impl::load_stream_impl(*this, stream, options, encoding); } PUGI__FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options) { reset(); return impl::load_stream_impl(*this, stream, options, encoding_wchar); } #endif PUGI__FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options) { // Force native encoding (skip autodetection) #ifdef PUGIXML_WCHAR_MODE xml_encoding encoding = encoding_wchar; #else xml_encoding encoding = encoding_utf8; #endif return load_buffer(contents, impl::strlength(contents) * sizeof(char_t), options, encoding); } PUGI__FN xml_parse_result xml_document::load_file(const char* path_, unsigned int options, xml_encoding encoding) { reset(); FILE* file = fopen(path_, "rb"); return impl::load_file_impl(*this, file, options, encoding); } PUGI__FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding) { reset(); FILE* file = impl::open_file_wide(path_, L"rb"); return impl::load_file_impl(*this, file, options, encoding); } PUGI__FN xml_parse_result xml_document::load_buffer_impl(void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own) { reset(); // check input buffer assert(contents || size == 0); // get actual encoding xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size); // get private buffer char_t* buffer = 0; size_t length = 0; if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory); // delete original buffer if we performed a conversion if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents); // parse xml_parse_result res = impl::xml_parser::parse(buffer, length, _root, options); // remember encoding res.encoding = buffer_encoding; // grab onto buffer if it's our buffer, user is responsible for deallocating contens himself if (own || buffer != contents) _buffer = buffer; return res; } PUGI__FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) { return load_buffer_impl(const_cast(contents), size, options, encoding, false, false); } PUGI__FN xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding) { return load_buffer_impl(contents, size, options, encoding, true, false); } PUGI__FN xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding) { return load_buffer_impl(contents, size, options, encoding, true, true); } PUGI__FN void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const { impl::xml_buffered_writer buffered_writer(writer, encoding); if ((flags & format_write_bom) && encoding != encoding_latin1) { // BOM always represents the codepoint U+FEFF, so just write it in native encoding #ifdef PUGIXML_WCHAR_MODE unsigned int bom = 0xfeff; buffered_writer.write(static_cast(bom)); #else buffered_writer.write('\xef', '\xbb', '\xbf'); #endif } if (!(flags & format_no_declaration) && !impl::has_declaration(*this)) { buffered_writer.write(PUGIXML_TEXT("'); if (!(flags & format_raw)) buffered_writer.write('\n'); } impl::node_output(buffered_writer, *this, indent, flags, 0); } #ifndef PUGIXML_NO_STL PUGI__FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding) const { xml_writer_stream writer(stream); save(writer, indent, flags, encoding); } PUGI__FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags) const { xml_writer_stream writer(stream); save(writer, indent, flags, encoding_wchar); } #endif PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const { FILE* file = fopen(path_, (flags & format_save_file_text) ? "w" : "wb"); return impl::save_file_impl(*this, file, indent, flags, encoding); } PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const { FILE* file = impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"); return impl::save_file_impl(*this, file, indent, flags, encoding); } PUGI__FN xml_node xml_document::document_element() const { for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) if ((i->header & impl::xml_memory_page_type_mask) + 1 == node_element) return xml_node(i); return xml_node(); } #ifndef PUGIXML_NO_STL PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str) { assert(str); return impl::as_utf8_impl(str, wcslen(str)); } PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string& str) { return impl::as_utf8_impl(str.c_str(), str.size()); } PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const char* str) { assert(str); return impl::as_wide_impl(str, strlen(str)); } PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const std::string& str) { return impl::as_wide_impl(str.c_str(), str.size()); } #endif PUGI__FN void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate) { impl::xml_memory::allocate = allocate; impl::xml_memory::deallocate = deallocate; } PUGI__FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function() { return impl::xml_memory::allocate; } PUGI__FN deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function() { return impl::xml_memory::deallocate; } } OIIO_NAMESPACE_END #if !defined(PUGIXML_NO_STL) && ((defined(_MSC_VER) && _MSC_VER < 1400) || defined(__ICC)) namespace std { // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const OpenImageIO::pugi::xml_node_iterator&) { return std::bidirectional_iterator_tag(); } PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const OpenImageIO::pugi::xml_attribute_iterator&) { return std::bidirectional_iterator_tag(); } PUGI__FN std::forward_iterator_tag _Iter_cat(const OpenImageIO::pugi::xml_named_node_iterator&) { return std::forward_iterator_tag(); } } #endif #if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) namespace std { // Workarounds for (non-standard) iterator category detection PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator&) { return std::bidirectional_iterator_tag(); } PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator&) { return std::bidirectional_iterator_tag(); } PUGI__FN std::forward_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&) { return std::forward_iterator_tag(); } } #endif #ifndef PUGIXML_NO_XPATH // STL replacements PUGI__NS_BEGIN struct equal_to { template bool operator()(const T& lhs, const T& rhs) const { return lhs == rhs; } }; struct not_equal_to { template bool operator()(const T& lhs, const T& rhs) const { return lhs != rhs; } }; struct less { template bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; } }; struct less_equal { template bool operator()(const T& lhs, const T& rhs) const { return lhs <= rhs; } }; template void swap(T& lhs, T& rhs) { T temp = lhs; lhs = rhs; rhs = temp; } template I min_element(I begin, I end, const Pred& pred) { I result = begin; for (I it = begin + 1; it != end; ++it) if (pred(*it, *result)) result = it; return result; } template void reverse(I begin, I end) { while (begin + 1 < end) swap(*begin++, *--end); } template I unique(I begin, I end) { // fast skip head while (begin + 1 < end && *begin != *(begin + 1)) begin++; if (begin == end) return begin; // last written element I write = begin++; // merge unique elements while (begin != end) { if (*begin != *write) *++write = *begin++; else begin++; } // past-the-end (write points to live element) return write + 1; } template void copy_backwards(I begin, I end, I target) { while (begin != end) *--target = *--end; } template void insertion_sort(I begin, I end, const Pred& pred, T*) { assert(begin != end); for (I it = begin + 1; it != end; ++it) { T val = *it; if (pred(val, *begin)) { // move to front copy_backwards(begin, it, it + 1); *begin = val; } else { I hole = it; // move hole backwards while (pred(val, *(hole - 1))) { *hole = *(hole - 1); hole--; } // fill hole with element *hole = val; } } } // std variant for elements with == template void partition(I begin, I middle, I end, const Pred& pred, I* out_eqbeg, I* out_eqend) { I eqbeg = middle, eqend = middle + 1; // expand equal range while (eqbeg != begin && *(eqbeg - 1) == *eqbeg) --eqbeg; while (eqend != end && *eqend == *eqbeg) ++eqend; // process outer elements I ltend = eqbeg, gtbeg = eqend; for (;;) { // find the element from the right side that belongs to the left one for (; gtbeg != end; ++gtbeg) if (!pred(*eqbeg, *gtbeg)) { if (*gtbeg == *eqbeg) swap(*gtbeg, *eqend++); else break; } // find the element from the left side that belongs to the right one for (; ltend != begin; --ltend) if (!pred(*(ltend - 1), *eqbeg)) { if (*eqbeg == *(ltend - 1)) swap(*(ltend - 1), *--eqbeg); else break; } // scanned all elements if (gtbeg == end && ltend == begin) { *out_eqbeg = eqbeg; *out_eqend = eqend; return; } // make room for elements by moving equal area if (gtbeg == end) { if (--ltend != --eqbeg) swap(*ltend, *eqbeg); swap(*eqbeg, *--eqend); } else if (ltend == begin) { if (eqend != gtbeg) swap(*eqbeg, *eqend); ++eqend; swap(*gtbeg++, *eqbeg++); } else swap(*gtbeg++, *--ltend); } } template void median3(I first, I middle, I last, const Pred& pred) { if (pred(*middle, *first)) swap(*middle, *first); if (pred(*last, *middle)) swap(*last, *middle); if (pred(*middle, *first)) swap(*middle, *first); } template void median(I first, I middle, I last, const Pred& pred) { if (last - first <= 40) { // median of three for small chunks median3(first, middle, last, pred); } else { // median of nine size_t step = (last - first + 1) / 8; median3(first, first + step, first + 2 * step, pred); median3(middle - step, middle, middle + step, pred); median3(last - 2 * step, last - step, last, pred); median3(first + step, middle, last - step, pred); } } template void sort(I begin, I end, const Pred& pred) { // sort large chunks while (end - begin > 32) { // find median element I middle = begin + (end - begin) / 2; median(begin, middle, end - 1, pred); // partition in three chunks (< = >) I eqbeg, eqend; partition(begin, middle, end, pred, &eqbeg, &eqend); // loop on larger half if (eqbeg - begin > end - eqend) { sort(eqend, end, pred); end = eqbeg; } else { sort(begin, eqbeg, pred); begin = eqend; } } // insertion sort small chunk if (begin != end) insertion_sort(begin, end, pred, &*begin); } PUGI__NS_END // Allocator used for AST and evaluation stacks PUGI__NS_BEGIN struct xpath_memory_block { xpath_memory_block* next; char data[ #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE PUGIXML_MEMORY_XPATH_PAGE_SIZE #else 4096 #endif ]; }; class xpath_allocator { xpath_memory_block* _root; size_t _root_size; public: #ifdef PUGIXML_NO_EXCEPTIONS jmp_buf* error_handler; #endif xpath_allocator(xpath_memory_block* root, size_t root_size = 0): _root(root), _root_size(root_size) { #ifdef PUGIXML_NO_EXCEPTIONS error_handler = 0; #endif } void* allocate_nothrow(size_t size) { const size_t block_capacity = sizeof(_root->data); // align size so that we're able to store pointers in subsequent blocks size = (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); if (_root_size + size <= block_capacity) { void* buf = _root->data + _root_size; _root_size += size; return buf; } else { size_t block_data_size = (size > block_capacity) ? size : block_capacity; size_t block_size = block_data_size + offsetof(xpath_memory_block, data); xpath_memory_block* block = static_cast(xml_memory::allocate(block_size)); if (!block) return 0; block->next = _root; _root = block; _root_size = size; return block->data; } } void* allocate(size_t size) { void* result = allocate_nothrow(size); if (!result) { #ifdef PUGIXML_NO_EXCEPTIONS assert(error_handler); longjmp(*error_handler, 1); #else throw std::bad_alloc(); #endif } return result; } void* reallocate(void* ptr, size_t old_size, size_t new_size) { // align size so that we're able to store pointers in subsequent blocks old_size = (old_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); new_size = (new_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); // we can only reallocate the last object assert(ptr == 0 || static_cast(ptr) + old_size == _root->data + _root_size); // adjust root size so that we have not allocated the object at all bool only_object = (_root_size == old_size); if (ptr) _root_size -= old_size; // allocate a new version (this will obviously reuse the memory if possible) void* result = allocate(new_size); assert(result); // we have a new block if (result != ptr && ptr) { // copy old data assert(new_size > old_size); memcpy(result, ptr, old_size); // free the previous page if it had no other objects if (only_object) { assert(_root->data == result); assert(_root->next); xpath_memory_block* next = _root->next->next; if (next) { // deallocate the whole page, unless it was the first one xml_memory::deallocate(_root->next); _root->next = next; } } } return result; } void revert(const xpath_allocator& state) { // free all new pages xpath_memory_block* cur = _root; while (cur != state._root) { xpath_memory_block* next = cur->next; xml_memory::deallocate(cur); cur = next; } // restore state _root = state._root; _root_size = state._root_size; } void release() { xpath_memory_block* cur = _root; assert(cur); while (cur->next) { xpath_memory_block* next = cur->next; xml_memory::deallocate(cur); cur = next; } } }; struct xpath_allocator_capture { xpath_allocator_capture(xpath_allocator* alloc): _target(alloc), _state(*alloc) { } ~xpath_allocator_capture() { _target->revert(_state); } xpath_allocator* _target; xpath_allocator _state; }; struct xpath_stack { xpath_allocator* result; xpath_allocator* temp; }; struct xpath_stack_data { xpath_memory_block blocks[2]; xpath_allocator result; xpath_allocator temp; xpath_stack stack; #ifdef PUGIXML_NO_EXCEPTIONS jmp_buf error_handler; #endif xpath_stack_data(): result(blocks + 0), temp(blocks + 1) { blocks[0].next = blocks[1].next = 0; stack.result = &result; stack.temp = &temp; #ifdef PUGIXML_NO_EXCEPTIONS result.error_handler = temp.error_handler = &error_handler; #endif } ~xpath_stack_data() { result.release(); temp.release(); } }; PUGI__NS_END // String class PUGI__NS_BEGIN class xpath_string { const char_t* _buffer; bool _uses_heap; static char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc) { char_t* result = static_cast(alloc->allocate((length + 1) * sizeof(char_t))); assert(result); memcpy(result, string, length * sizeof(char_t)); result[length] = 0; return result; } static char_t* duplicate_string(const char_t* string, xpath_allocator* alloc) { return duplicate_string(string, strlength(string), alloc); } public: xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false) { } explicit xpath_string(const char_t* str, xpath_allocator* alloc) { bool empty_ = (*str == 0); _buffer = empty_ ? PUGIXML_TEXT("") : duplicate_string(str, alloc); _uses_heap = !empty_; } explicit xpath_string(const char_t* str, bool use_heap): _buffer(str), _uses_heap(use_heap) { } xpath_string(const char_t* begin, const char_t* end, xpath_allocator* alloc) { assert(begin <= end); bool empty_ = (begin == end); _buffer = empty_ ? PUGIXML_TEXT("") : duplicate_string(begin, static_cast(end - begin), alloc); _uses_heap = !empty_; } void append(const xpath_string& o, xpath_allocator* alloc) { // skip empty sources if (!*o._buffer) return; // fast append for constant empty target and constant source if (!*_buffer && !_uses_heap && !o._uses_heap) { _buffer = o._buffer; } else { // need to make heap copy size_t target_length = strlength(_buffer); size_t source_length = strlength(o._buffer); size_t result_length = target_length + source_length; // allocate new buffer char_t* result = static_cast(alloc->reallocate(_uses_heap ? const_cast(_buffer) : 0, (target_length + 1) * sizeof(char_t), (result_length + 1) * sizeof(char_t))); assert(result); // append first string to the new buffer in case there was no reallocation if (!_uses_heap) memcpy(result, _buffer, target_length * sizeof(char_t)); // append second string to the new buffer memcpy(result + target_length, o._buffer, source_length * sizeof(char_t)); result[result_length] = 0; // finalize _buffer = result; _uses_heap = true; } } const char_t* c_str() const { return _buffer; } size_t length() const { return strlength(_buffer); } char_t* data(xpath_allocator* alloc) { // make private heap copy if (!_uses_heap) { _buffer = duplicate_string(_buffer, alloc); _uses_heap = true; } return const_cast(_buffer); } bool empty() const { return *_buffer == 0; } bool operator==(const xpath_string& o) const { return strequal(_buffer, o._buffer); } bool operator!=(const xpath_string& o) const { return !strequal(_buffer, o._buffer); } bool uses_heap() const { return _uses_heap; } }; PUGI__FN xpath_string xpath_string_const(const char_t* str) { return xpath_string(str, false); } PUGI__NS_END PUGI__NS_BEGIN PUGI__FN bool starts_with(const char_t* string, const char_t* pattern) { while (*pattern && *string == *pattern) { string++; pattern++; } return *pattern == 0; } PUGI__FN const char_t* find_char(const char_t* s, char_t c) { #ifdef PUGIXML_WCHAR_MODE return wcschr(s, c); #else return strchr(s, c); #endif } PUGI__FN const char_t* find_substring(const char_t* s, const char_t* p) { #ifdef PUGIXML_WCHAR_MODE // MSVC6 wcsstr bug workaround (if s is empty it always returns 0) return (*p == 0) ? s : wcsstr(s, p); #else return strstr(s, p); #endif } // Converts symbol to lower case, if it is an ASCII one PUGI__FN char_t tolower_ascii(char_t ch) { return static_cast(ch - 'A') < 26 ? static_cast(ch | ' ') : ch; } PUGI__FN xpath_string string_value(const xpath_node& na, xpath_allocator* alloc) { if (na.attribute()) return xpath_string_const(na.attribute().value()); else { const xml_node& n = na.node(); switch (n.type()) { case node_pcdata: case node_cdata: case node_comment: case node_pi: return xpath_string_const(n.value()); case node_document: case node_element: { xpath_string result; xml_node cur = n.first_child(); while (cur && cur != n) { if (cur.type() == node_pcdata || cur.type() == node_cdata) result.append(xpath_string_const(cur.value()), alloc); if (cur.first_child()) cur = cur.first_child(); else if (cur.next_sibling()) cur = cur.next_sibling(); else { while (!cur.next_sibling() && cur != n) cur = cur.parent(); if (cur != n) cur = cur.next_sibling(); } } return result; } default: return xpath_string(); } } } PUGI__FN unsigned int node_height(xml_node n) { unsigned int result = 0; while (n) { ++result; n = n.parent(); } return result; } PUGI__FN bool node_is_before(xml_node ln, unsigned int lh, xml_node rn, unsigned int rh) { // normalize heights for (unsigned int i = rh; i < lh; i++) ln = ln.parent(); for (unsigned int j = lh; j < rh; j++) rn = rn.parent(); // one node is the ancestor of the other if (ln == rn) return lh < rh; // find common ancestor while (ln.parent() != rn.parent()) { ln = ln.parent(); rn = rn.parent(); } // there is no common ancestor (the shared parent is null), nodes are from different documents if (!ln.parent()) return ln < rn; // determine sibling order for (; ln; ln = ln.next_sibling()) if (ln == rn) return true; return false; } PUGI__FN bool node_is_ancestor(xml_node parent, xml_node node) { while (node && node != parent) node = node.parent(); return parent && node == parent; } PUGI__FN const void* document_order(const xpath_node& xnode) { xml_node_struct* node = xnode.node().internal_object(); if (node) { if (node->name && (node->header & xml_memory_page_name_allocated_mask) == 0) return node->name; if (node->value && (node->header & xml_memory_page_value_allocated_mask) == 0) return node->value; return 0; } xml_attribute_struct* attr = xnode.attribute().internal_object(); if (attr) { if ((attr->header & xml_memory_page_name_allocated_mask) == 0) return attr->name; if ((attr->header & xml_memory_page_value_allocated_mask) == 0) return attr->value; return 0; } return 0; } struct document_order_comparator { bool operator()(const xpath_node& lhs, const xpath_node& rhs) const { // optimized document order based check const void* lo = document_order(lhs); const void* ro = document_order(rhs); if (lo && ro) return lo < ro; // slow comparison xml_node ln = lhs.node(), rn = rhs.node(); // compare attributes if (lhs.attribute() && rhs.attribute()) { // shared parent if (lhs.parent() == rhs.parent()) { // determine sibling order for (xml_attribute a = lhs.attribute(); a; a = a.next_attribute()) if (a == rhs.attribute()) return true; return false; } // compare attribute parents ln = lhs.parent(); rn = rhs.parent(); } else if (lhs.attribute()) { // attributes go after the parent element if (lhs.parent() == rhs.node()) return false; ln = lhs.parent(); } else if (rhs.attribute()) { // attributes go after the parent element if (rhs.parent() == lhs.node()) return true; rn = rhs.parent(); } if (ln == rn) return false; unsigned int lh = node_height(ln); unsigned int rh = node_height(rn); return node_is_before(ln, lh, rn, rh); } }; struct duplicate_comparator { bool operator()(const xpath_node& lhs, const xpath_node& rhs) const { if (lhs.attribute()) return rhs.attribute() ? lhs.attribute() < rhs.attribute() : true; else return rhs.attribute() ? false : lhs.node() < rhs.node(); } }; PUGI__FN double gen_nan() { #if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24)) union { float f; uint32_t i; } u[sizeof(float) == sizeof(uint32_t) ? 1 : -1]; u[0].i = 0x7fc00000; return u[0].f; #else // fallback const volatile double zero = 0.0; return zero / zero; #endif } PUGI__FN bool is_nan(double value) { #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) return !!_isnan(value); #elif defined(fpclassify) && defined(FP_NAN) return fpclassify(value) == FP_NAN; #else // fallback const volatile double v = value; return v != v; #endif } PUGI__FN const char_t* convert_number_to_string_special(double value) { #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) if (_finite(value)) return (value == 0) ? PUGIXML_TEXT("0") : 0; if (_isnan(value)) return PUGIXML_TEXT("NaN"); return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); #elif defined(fpclassify) && defined(FP_NAN) && defined(FP_INFINITE) && defined(FP_ZERO) switch (fpclassify(value)) { case FP_NAN: return PUGIXML_TEXT("NaN"); case FP_INFINITE: return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); case FP_ZERO: return PUGIXML_TEXT("0"); default: return 0; } #else // fallback const volatile double v = value; if (v == 0) return PUGIXML_TEXT("0"); if (v != v) return PUGIXML_TEXT("NaN"); if (v * 2 == v) return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); return 0; #endif } PUGI__FN bool convert_number_to_boolean(double value) { return (value != 0 && !is_nan(value)); } PUGI__FN void truncate_zeros(char* begin, char* end) { while (begin != end && end[-1] == '0') end--; *end = 0; } // gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE) PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent) { // get base values int sign, exponent; _ecvt_s(buffer, buffer_size, value, DBL_DIG + 1, &exponent, &sign); // truncate redundant zeros truncate_zeros(buffer, buffer + strlen(buffer)); // fill results *out_mantissa = buffer; *out_exponent = exponent; } #else PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent) { // get a scientific notation value with IEEE DBL_DIG decimals sprintf(buffer, "%.*e", DBL_DIG, value); assert(strlen(buffer) < buffer_size); (void)!buffer_size; // get the exponent (possibly negative) char* exponent_string = strchr(buffer, 'e'); assert(exponent_string); int exponent = atoi(exponent_string + 1); // extract mantissa string: skip sign char* mantissa = buffer[0] == '-' ? buffer + 1 : buffer; assert(mantissa[0] != '0' && mantissa[1] == '.'); // divide mantissa by 10 to eliminate integer part mantissa[1] = mantissa[0]; mantissa++; exponent++; // remove extra mantissa digits and zero-terminate mantissa truncate_zeros(mantissa, exponent_string); // fill results *out_mantissa = mantissa; *out_exponent = exponent; } #endif PUGI__FN xpath_string convert_number_to_string(double value, xpath_allocator* alloc) { // try special number conversion const char_t* special = convert_number_to_string_special(value); if (special) return xpath_string_const(special); // get mantissa + exponent form char mantissa_buffer[64]; char* mantissa; int exponent; convert_number_to_mantissa_exponent(value, mantissa_buffer, sizeof(mantissa_buffer), &mantissa, &exponent); // make the number! char_t result[512]; char_t* s = result; // sign if (value < 0) *s++ = '-'; // integer part if (exponent <= 0) { *s++ = '0'; } else { while (exponent > 0) { assert(*mantissa == 0 || static_cast(*mantissa - '0') <= 9); *s++ = *mantissa ? *mantissa++ : '0'; exponent--; } } // fractional part if (*mantissa) { // decimal point *s++ = '.'; // extra zeroes from negative exponent while (exponent < 0) { *s++ = '0'; exponent++; } // extra mantissa digits while (*mantissa) { assert(static_cast(*mantissa - '0') <= 9); *s++ = *mantissa++; } } // zero-terminate assert(s < result + sizeof(result) / sizeof(result[0])); *s = 0; return xpath_string(result, alloc); } PUGI__FN bool check_string_to_number_format(const char_t* string) { // parse leading whitespace while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string; // parse sign if (*string == '-') ++string; if (!*string) return false; // if there is no integer part, there should be a decimal part with at least one digit if (!PUGI__IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI__IS_CHARTYPEX(string[1], ctx_digit))) return false; // parse integer part while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string; // parse decimal part if (*string == '.') { ++string; while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string; } // parse trailing whitespace while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string; return *string == 0; } PUGI__FN double convert_string_to_number(const char_t* string) { // check string format if (!check_string_to_number_format(string)) return gen_nan(); // parse string #ifdef PUGIXML_WCHAR_MODE return wcstod(string, 0); #else return atof(string); #endif } PUGI__FN bool convert_string_to_number(const char_t* begin, const char_t* end, double* out_result) { char_t buffer[32]; size_t length = static_cast(end - begin); char_t* scratch = buffer; if (length >= sizeof(buffer) / sizeof(buffer[0])) { // need to make dummy on-heap copy scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!scratch) return false; } // copy string to zero-terminated buffer and perform conversion memcpy(scratch, begin, length * sizeof(char_t)); scratch[length] = 0; *out_result = convert_string_to_number(scratch); // free dummy buffer if (scratch != buffer) xml_memory::deallocate(scratch); return true; } PUGI__FN double round_nearest(double value) { return floor(value + 0.5); } PUGI__FN double round_nearest_nzero(double value) { // same as round_nearest, but returns -0 for [-0.5, -0] // ceil is used to differentiate between +0 and -0 (we return -0 for [-0.5, -0] and +0 for +0) return (value >= -0.5 && value <= 0) ? ceil(value) : floor(value + 0.5); } PUGI__FN const char_t* qualified_name(const xpath_node& node) { return node.attribute() ? node.attribute().name() : node.node().name(); } PUGI__FN const char_t* local_name(const xpath_node& node) { const char_t* name = qualified_name(node); const char_t* p = find_char(name, ':'); return p ? p + 1 : name; } struct namespace_uri_predicate { const char_t* prefix; size_t prefix_length; namespace_uri_predicate(const char_t* name) { const char_t* pos = find_char(name, ':'); prefix = pos ? name : 0; prefix_length = pos ? static_cast(pos - name) : 0; } bool operator()(const xml_attribute& a) const { const char_t* name = a.name(); if (!starts_with(name, PUGIXML_TEXT("xmlns"))) return false; return prefix ? name[5] == ':' && strequalrange(name + 6, prefix, prefix_length) : name[5] == 0; } }; PUGI__FN const char_t* namespace_uri(const xml_node& node) { namespace_uri_predicate pred = node.name(); xml_node p = node; while (p) { xml_attribute a = p.find_attribute(pred); if (a) return a.value(); p = p.parent(); } return PUGIXML_TEXT(""); } PUGI__FN const char_t* namespace_uri(const xml_attribute& attr, const xml_node& parent) { namespace_uri_predicate pred = attr.name(); // Default namespace does not apply to attributes if (!pred.prefix) return PUGIXML_TEXT(""); xml_node p = parent; while (p) { xml_attribute a = p.find_attribute(pred); if (a) return a.value(); p = p.parent(); } return PUGIXML_TEXT(""); } PUGI__FN const char_t* namespace_uri(const xpath_node& node) { return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node()); } PUGI__FN void normalize_space(char_t* buffer) { char_t* write = buffer; for (char_t* it = buffer; *it; ) { char_t ch = *it++; if (PUGI__IS_CHARTYPE(ch, ct_space)) { // replace whitespace sequence with single space while (PUGI__IS_CHARTYPE(*it, ct_space)) it++; // avoid leading spaces if (write != buffer) *write++ = ' '; } else *write++ = ch; } // remove trailing space if (write != buffer && PUGI__IS_CHARTYPE(write[-1], ct_space)) write--; // zero-terminate *write = 0; } PUGI__FN void translate(char_t* buffer, const char_t* from, const char_t* to) { size_t to_length = strlength(to); char_t* write = buffer; while (*buffer) { PUGI__DMC_VOLATILE char_t ch = *buffer++; const char_t* pos = find_char(from, ch); if (!pos) *write++ = ch; // do not process else if (static_cast(pos - from) < to_length) *write++ = to[pos - from]; // replace } // zero-terminate *write = 0; } struct xpath_variable_boolean: xpath_variable { xpath_variable_boolean(): value(false) { } bool value; char_t name[1]; }; struct xpath_variable_number: xpath_variable { xpath_variable_number(): value(0) { } double value; char_t name[1]; }; struct xpath_variable_string: xpath_variable { xpath_variable_string(): value(0) { } ~xpath_variable_string() { if (value) xml_memory::deallocate(value); } char_t* value; char_t name[1]; }; struct xpath_variable_node_set: xpath_variable { xpath_node_set value; char_t name[1]; }; static const xpath_node_set dummy_node_set; PUGI__FN unsigned int hash_string(const char_t* str) { // Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time) unsigned int result = 0; while (*str) { result += static_cast(*str++); result += result << 10; result ^= result >> 6; } result += result << 3; result ^= result >> 11; result += result << 15; return result; } template PUGI__FN T* new_xpath_variable(const char_t* name) { size_t length = strlength(name); if (length == 0) return 0; // empty variable names are invalid // $$ we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters void* memory = xml_memory::allocate(sizeof(T) + length * sizeof(char_t)); if (!memory) return 0; T* result = new (memory) T(); memcpy(result->name, name, (length + 1) * sizeof(char_t)); return result; } PUGI__FN xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name) { switch (type) { case xpath_type_node_set: return new_xpath_variable(name); case xpath_type_number: return new_xpath_variable(name); case xpath_type_string: return new_xpath_variable(name); case xpath_type_boolean: return new_xpath_variable(name); default: return 0; } } template PUGI__FN void delete_xpath_variable(T* var) { var->~T(); xml_memory::deallocate(var); } PUGI__FN void delete_xpath_variable(xpath_value_type type, xpath_variable* var) { switch (type) { case xpath_type_node_set: delete_xpath_variable(static_cast(var)); break; case xpath_type_number: delete_xpath_variable(static_cast(var)); break; case xpath_type_string: delete_xpath_variable(static_cast(var)); break; case xpath_type_boolean: delete_xpath_variable(static_cast(var)); break; default: assert(!"Invalid variable type"); } } PUGI__FN xpath_variable* get_variable(xpath_variable_set* set, const char_t* begin, const char_t* end) { char_t buffer[32]; size_t length = static_cast(end - begin); char_t* scratch = buffer; if (length >= sizeof(buffer) / sizeof(buffer[0])) { // need to make dummy on-heap copy scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!scratch) return 0; } // copy string to zero-terminated buffer and perform lookup memcpy(scratch, begin, length * sizeof(char_t)); scratch[length] = 0; xpath_variable* result = set->get(scratch); // free dummy buffer if (scratch != buffer) xml_memory::deallocate(scratch); return result; } PUGI__NS_END // Internal node set class PUGI__NS_BEGIN PUGI__FN xpath_node_set::type_t xpath_sort(xpath_node* begin, xpath_node* end, xpath_node_set::type_t type, bool rev) { xpath_node_set::type_t order = rev ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted; if (type == xpath_node_set::type_unsorted) { sort(begin, end, document_order_comparator()); type = xpath_node_set::type_sorted; } if (type != order) reverse(begin, end); return order; } PUGI__FN xpath_node xpath_first(const xpath_node* begin, const xpath_node* end, xpath_node_set::type_t type) { if (begin == end) return xpath_node(); switch (type) { case xpath_node_set::type_sorted: return *begin; case xpath_node_set::type_sorted_reverse: return *(end - 1); case xpath_node_set::type_unsorted: return *min_element(begin, end, document_order_comparator()); default: assert(!"Invalid node set type"); return xpath_node(); } } class xpath_node_set_raw { xpath_node_set::type_t _type; xpath_node* _begin; xpath_node* _end; xpath_node* _eos; public: xpath_node_set_raw(): _type(xpath_node_set::type_unsorted), _begin(0), _end(0), _eos(0) { } xpath_node* begin() const { return _begin; } xpath_node* end() const { return _end; } bool empty() const { return _begin == _end; } size_t size() const { return static_cast(_end - _begin); } xpath_node first() const { return xpath_first(_begin, _end, _type); } void push_back(const xpath_node& node, xpath_allocator* alloc) { if (_end == _eos) { size_t capacity = static_cast(_eos - _begin); // get new capacity (1.5x rule) size_t new_capacity = capacity + capacity / 2 + 1; // reallocate the old array or allocate a new one xpath_node* data = static_cast(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node))); assert(data); // finalize _begin = data; _end = data + capacity; _eos = data + new_capacity; } *_end++ = node; } void append(const xpath_node* begin_, const xpath_node* end_, xpath_allocator* alloc) { size_t size_ = static_cast(_end - _begin); size_t capacity = static_cast(_eos - _begin); size_t count = static_cast(end_ - begin_); if (size_ + count > capacity) { // reallocate the old array or allocate a new one xpath_node* data = static_cast(alloc->reallocate(_begin, capacity * sizeof(xpath_node), (size_ + count) * sizeof(xpath_node))); assert(data); // finalize _begin = data; _end = data + size_; _eos = data + size_ + count; } memcpy(_end, begin_, count * sizeof(xpath_node)); _end += count; } void sort_do() { _type = xpath_sort(_begin, _end, _type, false); } void truncate(xpath_node* pos) { assert(_begin <= pos && pos <= _end); _end = pos; } void remove_duplicates() { if (_type == xpath_node_set::type_unsorted) sort(_begin, _end, duplicate_comparator()); _end = unique(_begin, _end); } xpath_node_set::type_t type() const { return _type; } void set_type(xpath_node_set::type_t value) { _type = value; } }; PUGI__NS_END PUGI__NS_BEGIN struct xpath_context { xpath_node n; size_t position, size; xpath_context(const xpath_node& n_, size_t position_, size_t size_): n(n_), position(position_), size(size_) { } }; enum lexeme_t { lex_none = 0, lex_equal, lex_not_equal, lex_less, lex_greater, lex_less_or_equal, lex_greater_or_equal, lex_plus, lex_minus, lex_multiply, lex_union, lex_var_ref, lex_open_brace, lex_close_brace, lex_quoted_string, lex_number, lex_slash, lex_double_slash, lex_open_square_brace, lex_close_square_brace, lex_string, lex_comma, lex_axis_attribute, lex_dot, lex_double_dot, lex_double_colon, lex_eof }; struct xpath_lexer_string { const char_t* begin; const char_t* end; xpath_lexer_string(): begin(0), end(0) { } bool operator==(const char_t* other) const { size_t length = static_cast(end - begin); return strequalrange(other, begin, length); } }; class xpath_lexer { const char_t* _cur; const char_t* _cur_lexeme_pos; xpath_lexer_string _cur_lexeme_contents; lexeme_t _cur_lexeme; public: explicit xpath_lexer(const char_t* query): _cur(query) { next(); } const char_t* state() const { return _cur; } void next() { const char_t* cur = _cur; while (PUGI__IS_CHARTYPE(*cur, ct_space)) ++cur; // save lexeme position for error reporting _cur_lexeme_pos = cur; switch (*cur) { case 0: _cur_lexeme = lex_eof; break; case '>': if (*(cur+1) == '=') { cur += 2; _cur_lexeme = lex_greater_or_equal; } else { cur += 1; _cur_lexeme = lex_greater; } break; case '<': if (*(cur+1) == '=') { cur += 2; _cur_lexeme = lex_less_or_equal; } else { cur += 1; _cur_lexeme = lex_less; } break; case '!': if (*(cur+1) == '=') { cur += 2; _cur_lexeme = lex_not_equal; } else { _cur_lexeme = lex_none; } break; case '=': cur += 1; _cur_lexeme = lex_equal; break; case '+': cur += 1; _cur_lexeme = lex_plus; break; case '-': cur += 1; _cur_lexeme = lex_minus; break; case '*': cur += 1; _cur_lexeme = lex_multiply; break; case '|': cur += 1; _cur_lexeme = lex_union; break; case '$': cur += 1; if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) { _cur_lexeme_contents.begin = cur; while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; if (cur[0] == ':' && PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // qname { cur++; // : while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; } _cur_lexeme_contents.end = cur; _cur_lexeme = lex_var_ref; } else { _cur_lexeme = lex_none; } break; case '(': cur += 1; _cur_lexeme = lex_open_brace; break; case ')': cur += 1; _cur_lexeme = lex_close_brace; break; case '[': cur += 1; _cur_lexeme = lex_open_square_brace; break; case ']': cur += 1; _cur_lexeme = lex_close_square_brace; break; case ',': cur += 1; _cur_lexeme = lex_comma; break; case '/': if (*(cur+1) == '/') { cur += 2; _cur_lexeme = lex_double_slash; } else { cur += 1; _cur_lexeme = lex_slash; } break; case '.': if (*(cur+1) == '.') { cur += 2; _cur_lexeme = lex_double_dot; } else if (PUGI__IS_CHARTYPEX(*(cur+1), ctx_digit)) { _cur_lexeme_contents.begin = cur; // . ++cur; while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; _cur_lexeme_contents.end = cur; _cur_lexeme = lex_number; } else { cur += 1; _cur_lexeme = lex_dot; } break; case '@': cur += 1; _cur_lexeme = lex_axis_attribute; break; case '"': case '\'': { char_t terminator = *cur; ++cur; _cur_lexeme_contents.begin = cur; while (*cur && *cur != terminator) cur++; _cur_lexeme_contents.end = cur; if (!*cur) _cur_lexeme = lex_none; else { cur += 1; _cur_lexeme = lex_quoted_string; } break; } case ':': if (*(cur+1) == ':') { cur += 2; _cur_lexeme = lex_double_colon; } else { _cur_lexeme = lex_none; } break; default: if (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) { _cur_lexeme_contents.begin = cur; while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; if (*cur == '.') { cur++; while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; } _cur_lexeme_contents.end = cur; _cur_lexeme = lex_number; } else if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) { _cur_lexeme_contents.begin = cur; while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; if (cur[0] == ':') { if (cur[1] == '*') // namespace test ncname:* { cur += 2; // :* } else if (PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname { cur++; // : while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; } } _cur_lexeme_contents.end = cur; _cur_lexeme = lex_string; } else { _cur_lexeme = lex_none; } } _cur = cur; } lexeme_t current() const { return _cur_lexeme; } const char_t* current_pos() const { return _cur_lexeme_pos; } const xpath_lexer_string& contents() const { assert(_cur_lexeme == lex_var_ref || _cur_lexeme == lex_number || _cur_lexeme == lex_string || _cur_lexeme == lex_quoted_string); return _cur_lexeme_contents; } }; enum ast_type_t { ast_op_or, // left or right ast_op_and, // left and right ast_op_equal, // left = right ast_op_not_equal, // left != right ast_op_less, // left < right ast_op_greater, // left > right ast_op_less_or_equal, // left <= right ast_op_greater_or_equal, // left >= right ast_op_add, // left + right ast_op_subtract, // left - right ast_op_multiply, // left * right ast_op_divide, // left / right ast_op_mod, // left % right ast_op_negate, // left - right ast_op_union, // left | right ast_predicate, // apply predicate to set; next points to next predicate ast_filter, // select * from left where right ast_filter_posinv, // select * from left where right; proximity position invariant ast_string_constant, // string constant ast_number_constant, // number constant ast_variable, // variable ast_func_last, // last() ast_func_position, // position() ast_func_count, // count(left) ast_func_id, // id(left) ast_func_local_name_0, // local-name() ast_func_local_name_1, // local-name(left) ast_func_namespace_uri_0, // namespace-uri() ast_func_namespace_uri_1, // namespace-uri(left) ast_func_name_0, // name() ast_func_name_1, // name(left) ast_func_string_0, // string() ast_func_string_1, // string(left) ast_func_concat, // concat(left, right, siblings) ast_func_starts_with, // starts_with(left, right) ast_func_contains, // contains(left, right) ast_func_substring_before, // substring-before(left, right) ast_func_substring_after, // substring-after(left, right) ast_func_substring_2, // substring(left, right) ast_func_substring_3, // substring(left, right, third) ast_func_string_length_0, // string-length() ast_func_string_length_1, // string-length(left) ast_func_normalize_space_0, // normalize-space() ast_func_normalize_space_1, // normalize-space(left) ast_func_translate, // translate(left, right, third) ast_func_boolean, // boolean(left) ast_func_not, // not(left) ast_func_true, // true() ast_func_false, // false() ast_func_lang, // lang(left) ast_func_number_0, // number() ast_func_number_1, // number(left) ast_func_sum, // sum(left) ast_func_floor, // floor(left) ast_func_ceiling, // ceiling(left) ast_func_round, // round(left) ast_step, // process set left with step ast_step_root // select root node }; enum axis_t { axis_ancestor, axis_ancestor_or_self, axis_attribute, axis_child, axis_descendant, axis_descendant_or_self, axis_following, axis_following_sibling, axis_namespace, axis_parent, axis_preceding, axis_preceding_sibling, axis_self }; enum nodetest_t { nodetest_none, nodetest_name, nodetest_type_node, nodetest_type_comment, nodetest_type_pi, nodetest_type_text, nodetest_pi, nodetest_all, nodetest_all_in_namespace }; template struct axis_to_type { static const axis_t axis; }; template const axis_t axis_to_type::axis = N; class xpath_ast_node { private: // node type char _type; char _rettype; // for ast_step / ast_predicate char _axis; char _test; // tree node structure xpath_ast_node* _left; xpath_ast_node* _right; xpath_ast_node* _next; union { // value for ast_string_constant const char_t* string; // value for ast_number_constant double number; // variable for ast_variable xpath_variable* variable; // node test for ast_step (node name/namespace/node type/pi target) const char_t* nodetest; } _data; xpath_ast_node(const xpath_ast_node&); xpath_ast_node& operator=(const xpath_ast_node&); template static bool compare_eq(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp) { xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); if (lt != xpath_type_node_set && rt != xpath_type_node_set) { if (lt == xpath_type_boolean || rt == xpath_type_boolean) return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); else if (lt == xpath_type_number || rt == xpath_type_number) return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); else if (lt == xpath_type_string || rt == xpath_type_string) { xpath_allocator_capture cr(stack.result); xpath_string ls = lhs->eval_string(c, stack); xpath_string rs = rhs->eval_string(c, stack); return comp(ls, rs); } } else if (lt == xpath_type_node_set && rt == xpath_type_node_set) { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ls = lhs->eval_node_set(c, stack); xpath_node_set_raw rs = rhs->eval_node_set(c, stack); for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) { xpath_allocator_capture cri(stack.result); if (comp(string_value(*li, stack.result), string_value(*ri, stack.result))) return true; } return false; } else { if (lt == xpath_type_node_set) { swap(lhs, rhs); swap(lt, rt); } if (lt == xpath_type_boolean) return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); else if (lt == xpath_type_number) { xpath_allocator_capture cr(stack.result); double l = lhs->eval_number(c, stack); xpath_node_set_raw rs = rhs->eval_node_set(c, stack); for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) { xpath_allocator_capture cri(stack.result); if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) return true; } return false; } else if (lt == xpath_type_string) { xpath_allocator_capture cr(stack.result); xpath_string l = lhs->eval_string(c, stack); xpath_node_set_raw rs = rhs->eval_node_set(c, stack); for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) { xpath_allocator_capture cri(stack.result); if (comp(l, string_value(*ri, stack.result))) return true; } return false; } } assert(!"Wrong types"); return false; } template static bool compare_rel(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp) { xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); if (lt != xpath_type_node_set && rt != xpath_type_node_set) return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); else if (lt == xpath_type_node_set && rt == xpath_type_node_set) { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ls = lhs->eval_node_set(c, stack); xpath_node_set_raw rs = rhs->eval_node_set(c, stack); for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) { xpath_allocator_capture cri(stack.result); double l = convert_string_to_number(string_value(*li, stack.result).c_str()); for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) { xpath_allocator_capture crii(stack.result); if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) return true; } } return false; } else if (lt != xpath_type_node_set && rt == xpath_type_node_set) { xpath_allocator_capture cr(stack.result); double l = lhs->eval_number(c, stack); xpath_node_set_raw rs = rhs->eval_node_set(c, stack); for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) { xpath_allocator_capture cri(stack.result); if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) return true; } return false; } else if (lt == xpath_type_node_set && rt != xpath_type_node_set) { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ls = lhs->eval_node_set(c, stack); double r = rhs->eval_number(c, stack); for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) { xpath_allocator_capture cri(stack.result); if (comp(convert_string_to_number(string_value(*li, stack.result).c_str()), r)) return true; } return false; } else { assert(!"Wrong types"); return false; } } void apply_predicate(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack) { assert(ns.size() >= first); size_t i = 1; size_t size = ns.size() - first; xpath_node* last = ns.begin() + first; // remove_if... or well, sort of for (xpath_node* it = last; it != ns.end(); ++it, ++i) { xpath_context c(*it, i, size); if (expr->rettype() == xpath_type_number) { if (expr->eval_number(c, stack) == i) *last++ = *it; } else if (expr->eval_boolean(c, stack)) *last++ = *it; } ns.truncate(last); } void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack) { if (ns.size() == first) return; for (xpath_ast_node* pred = _right; pred; pred = pred->_next) { apply_predicate(ns, first, pred->_left, stack); } } void step_push(xpath_node_set_raw& ns, const xml_attribute& a, const xml_node& parent, xpath_allocator* alloc) { if (!a) return; const char_t* name = a.name(); // There are no attribute nodes corresponding to attributes that declare namespaces // That is, "xmlns:..." or "xmlns" if (starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':')) return; switch (_test) { case nodetest_name: if (strequal(name, _data.nodetest)) ns.push_back(xpath_node(a, parent), alloc); break; case nodetest_type_node: case nodetest_all: ns.push_back(xpath_node(a, parent), alloc); break; case nodetest_all_in_namespace: if (starts_with(name, _data.nodetest)) ns.push_back(xpath_node(a, parent), alloc); break; default: ; } } void step_push(xpath_node_set_raw& ns, const xml_node& n, xpath_allocator* alloc) { if (!n) return; switch (_test) { case nodetest_name: if (n.type() == node_element && strequal(n.name(), _data.nodetest)) ns.push_back(n, alloc); break; case nodetest_type_node: ns.push_back(n, alloc); break; case nodetest_type_comment: if (n.type() == node_comment) ns.push_back(n, alloc); break; case nodetest_type_text: if (n.type() == node_pcdata || n.type() == node_cdata) ns.push_back(n, alloc); break; case nodetest_type_pi: if (n.type() == node_pi) ns.push_back(n, alloc); break; case nodetest_pi: if (n.type() == node_pi && strequal(n.name(), _data.nodetest)) ns.push_back(n, alloc); break; case nodetest_all: if (n.type() == node_element) ns.push_back(n, alloc); break; case nodetest_all_in_namespace: if (n.type() == node_element && starts_with(n.name(), _data.nodetest)) ns.push_back(n, alloc); break; default: assert(!"Unknown axis"); } } template void step_fill(xpath_node_set_raw& ns, const xml_node& n, xpath_allocator* alloc, T) { const axis_t axis = T::axis; switch (axis) { case axis_attribute: { for (xml_attribute a = n.first_attribute(); a; a = a.next_attribute()) step_push(ns, a, n, alloc); break; } case axis_child: { for (xml_node c = n.first_child(); c; c = c.next_sibling()) step_push(ns, c, alloc); break; } case axis_descendant: case axis_descendant_or_self: { if (axis == axis_descendant_or_self) step_push(ns, n, alloc); xml_node cur = n.first_child(); while (cur && cur != n) { step_push(ns, cur, alloc); if (cur.first_child()) cur = cur.first_child(); else if (cur.next_sibling()) cur = cur.next_sibling(); else { while (!cur.next_sibling() && cur != n) cur = cur.parent(); if (cur != n) cur = cur.next_sibling(); } } break; } case axis_following_sibling: { for (xml_node c = n.next_sibling(); c; c = c.next_sibling()) step_push(ns, c, alloc); break; } case axis_preceding_sibling: { for (xml_node c = n.previous_sibling(); c; c = c.previous_sibling()) step_push(ns, c, alloc); break; } case axis_following: { xml_node cur = n; // exit from this node so that we don't include descendants while (cur && !cur.next_sibling()) cur = cur.parent(); cur = cur.next_sibling(); for (;;) { step_push(ns, cur, alloc); if (cur.first_child()) cur = cur.first_child(); else if (cur.next_sibling()) cur = cur.next_sibling(); else { while (cur && !cur.next_sibling()) cur = cur.parent(); cur = cur.next_sibling(); if (!cur) break; } } break; } case axis_preceding: { xml_node cur = n; while (cur && !cur.previous_sibling()) cur = cur.parent(); cur = cur.previous_sibling(); for (;;) { if (cur.last_child()) cur = cur.last_child(); else { // leaf node, can't be ancestor step_push(ns, cur, alloc); if (cur.previous_sibling()) cur = cur.previous_sibling(); else { do { cur = cur.parent(); if (!cur) break; if (!node_is_ancestor(cur, n)) step_push(ns, cur, alloc); } while (!cur.previous_sibling()); cur = cur.previous_sibling(); if (!cur) break; } } } break; } case axis_ancestor: case axis_ancestor_or_self: { if (axis == axis_ancestor_or_self) step_push(ns, n, alloc); xml_node cur = n.parent(); while (cur) { step_push(ns, cur, alloc); cur = cur.parent(); } break; } case axis_self: { step_push(ns, n, alloc); break; } case axis_parent: { if (n.parent()) step_push(ns, n.parent(), alloc); break; } default: assert(!"Unimplemented axis"); } } template void step_fill(xpath_node_set_raw& ns, const xml_attribute& a, const xml_node& p, xpath_allocator* alloc, T v) { const axis_t axis = T::axis; switch (axis) { case axis_ancestor: case axis_ancestor_or_self: { if (axis == axis_ancestor_or_self && _test == nodetest_type_node) // reject attributes based on principal node type test step_push(ns, a, p, alloc); xml_node cur = p; while (cur) { step_push(ns, cur, alloc); cur = cur.parent(); } break; } case axis_descendant_or_self: case axis_self: { if (_test == nodetest_type_node) // reject attributes based on principal node type test step_push(ns, a, p, alloc); break; } case axis_following: { xml_node cur = p; for (;;) { if (cur.first_child()) cur = cur.first_child(); else if (cur.next_sibling()) cur = cur.next_sibling(); else { while (cur && !cur.next_sibling()) cur = cur.parent(); cur = cur.next_sibling(); if (!cur) break; } step_push(ns, cur, alloc); } break; } case axis_parent: { step_push(ns, p, alloc); break; } case axis_preceding: { // preceding:: axis does not include attribute nodes and attribute ancestors (they are the same as parent's ancestors), so we can reuse node preceding step_fill(ns, p, alloc, v); break; } default: assert(!"Unimplemented axis"); } } template xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, T v) { const axis_t axis = T::axis; bool attributes = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self); xpath_node_set_raw ns; ns.set_type((axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling) ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted); if (_left) { xpath_node_set_raw s = _left->eval_node_set(c, stack); // self axis preserves the original order if (axis == axis_self) ns.set_type(s.type()); for (const xpath_node* it = s.begin(); it != s.end(); ++it) { size_t size = ns.size(); // in general, all axes generate elements in a particular order, but there is no order guarantee if axis is applied to two nodes if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted); if (it->node()) step_fill(ns, it->node(), stack.result, v); else if (attributes) step_fill(ns, it->attribute(), it->parent(), stack.result, v); apply_predicates(ns, size, stack); } } else { if (c.n.node()) step_fill(ns, c.n.node(), stack.result, v); else if (attributes) step_fill(ns, c.n.attribute(), c.n.parent(), stack.result, v); apply_predicates(ns, 0, stack); } // child, attribute and self axes always generate unique set of nodes // for other axis, if the set stayed sorted, it stayed unique because the traversal algorithms do not visit the same node twice if (axis != axis_child && axis != axis_attribute && axis != axis_self && ns.type() == xpath_node_set::type_unsorted) ns.remove_duplicates(); return ns; } public: xpath_ast_node(ast_type_t type, xpath_value_type rettype_, const char_t* value): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) { assert(type == ast_string_constant); _data.string = value; } xpath_ast_node(ast_type_t type, xpath_value_type rettype_, double value): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) { assert(type == ast_number_constant); _data.number = value; } xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_variable* value): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) { assert(type == ast_variable); _data.variable = value; } xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_ast_node* left = 0, xpath_ast_node* right = 0): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(left), _right(right), _next(0) { } xpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents): _type(static_cast(type)), _rettype(xpath_type_node_set), _axis(static_cast(axis)), _test(static_cast(test)), _left(left), _right(0), _next(0) { _data.nodetest = contents; } void set_next(xpath_ast_node* value) { _next = value; } void set_right(xpath_ast_node* value) { _right = value; } bool eval_boolean(const xpath_context& c, const xpath_stack& stack) { switch (_type) { case ast_op_or: return _left->eval_boolean(c, stack) || _right->eval_boolean(c, stack); case ast_op_and: return _left->eval_boolean(c, stack) && _right->eval_boolean(c, stack); case ast_op_equal: return compare_eq(_left, _right, c, stack, equal_to()); case ast_op_not_equal: return compare_eq(_left, _right, c, stack, not_equal_to()); case ast_op_less: return compare_rel(_left, _right, c, stack, less()); case ast_op_greater: return compare_rel(_right, _left, c, stack, less()); case ast_op_less_or_equal: return compare_rel(_left, _right, c, stack, less_equal()); case ast_op_greater_or_equal: return compare_rel(_right, _left, c, stack, less_equal()); case ast_func_starts_with: { xpath_allocator_capture cr(stack.result); xpath_string lr = _left->eval_string(c, stack); xpath_string rr = _right->eval_string(c, stack); return starts_with(lr.c_str(), rr.c_str()); } case ast_func_contains: { xpath_allocator_capture cr(stack.result); xpath_string lr = _left->eval_string(c, stack); xpath_string rr = _right->eval_string(c, stack); return find_substring(lr.c_str(), rr.c_str()) != 0; } case ast_func_boolean: return _left->eval_boolean(c, stack); case ast_func_not: return !_left->eval_boolean(c, stack); case ast_func_true: return true; case ast_func_false: return false; case ast_func_lang: { if (c.n.attribute()) return false; xpath_allocator_capture cr(stack.result); xpath_string lang = _left->eval_string(c, stack); for (xml_node n = c.n.node(); n; n = n.parent()) { xml_attribute a = n.attribute(PUGIXML_TEXT("xml:lang")); if (a) { const char_t* value = a.value(); // strnicmp / strncasecmp is not portable for (const char_t* lit = lang.c_str(); *lit; ++lit) { if (tolower_ascii(*lit) != tolower_ascii(*value)) return false; ++value; } return *value == 0 || *value == '-'; } } return false; } case ast_variable: { assert(_rettype == _data.variable->type()); if (_rettype == xpath_type_boolean) return _data.variable->get_boolean(); // fallthrough to type conversion } default: { switch (_rettype) { case xpath_type_number: return convert_number_to_boolean(eval_number(c, stack)); case xpath_type_string: { xpath_allocator_capture cr(stack.result); return !eval_string(c, stack).empty(); } case xpath_type_node_set: { xpath_allocator_capture cr(stack.result); return !eval_node_set(c, stack).empty(); } default: assert(!"Wrong expression for return type boolean"); return false; } } } } double eval_number(const xpath_context& c, const xpath_stack& stack) { switch (_type) { case ast_op_add: return _left->eval_number(c, stack) + _right->eval_number(c, stack); case ast_op_subtract: return _left->eval_number(c, stack) - _right->eval_number(c, stack); case ast_op_multiply: return _left->eval_number(c, stack) * _right->eval_number(c, stack); case ast_op_divide: return _left->eval_number(c, stack) / _right->eval_number(c, stack); case ast_op_mod: return fmod(_left->eval_number(c, stack), _right->eval_number(c, stack)); case ast_op_negate: return -_left->eval_number(c, stack); case ast_number_constant: return _data.number; case ast_func_last: return static_cast(c.size); case ast_func_position: return static_cast(c.position); case ast_func_count: { xpath_allocator_capture cr(stack.result); return static_cast(_left->eval_node_set(c, stack).size()); } case ast_func_string_length_0: { xpath_allocator_capture cr(stack.result); return static_cast(string_value(c.n, stack.result).length()); } case ast_func_string_length_1: { xpath_allocator_capture cr(stack.result); return static_cast(_left->eval_string(c, stack).length()); } case ast_func_number_0: { xpath_allocator_capture cr(stack.result); return convert_string_to_number(string_value(c.n, stack.result).c_str()); } case ast_func_number_1: return _left->eval_number(c, stack); case ast_func_sum: { xpath_allocator_capture cr(stack.result); double r = 0; xpath_node_set_raw ns = _left->eval_node_set(c, stack); for (const xpath_node* it = ns.begin(); it != ns.end(); ++it) { xpath_allocator_capture cri(stack.result); r += convert_string_to_number(string_value(*it, stack.result).c_str()); } return r; } case ast_func_floor: { double r = _left->eval_number(c, stack); return r == r ? floor(r) : r; } case ast_func_ceiling: { double r = _left->eval_number(c, stack); return r == r ? ceil(r) : r; } case ast_func_round: return round_nearest_nzero(_left->eval_number(c, stack)); case ast_variable: { assert(_rettype == _data.variable->type()); if (_rettype == xpath_type_number) return _data.variable->get_number(); // fallthrough to type conversion } default: { switch (_rettype) { case xpath_type_boolean: return eval_boolean(c, stack) ? 1 : 0; case xpath_type_string: { xpath_allocator_capture cr(stack.result); return convert_string_to_number(eval_string(c, stack).c_str()); } case xpath_type_node_set: { xpath_allocator_capture cr(stack.result); return convert_string_to_number(eval_string(c, stack).c_str()); } default: assert(!"Wrong expression for return type number"); return 0; } } } } xpath_string eval_string_concat(const xpath_context& c, const xpath_stack& stack) { assert(_type == ast_func_concat); xpath_allocator_capture ct(stack.temp); // count the string number size_t count = 1; for (xpath_ast_node* nc = _right; nc; nc = nc->_next) count++; // gather all strings xpath_string static_buffer[4]; xpath_string* buffer = static_buffer; // allocate on-heap for large concats if (count > sizeof(static_buffer) / sizeof(static_buffer[0])) { buffer = static_cast(stack.temp->allocate(count * sizeof(xpath_string))); assert(buffer); } // evaluate all strings to temporary stack xpath_stack swapped_stack = {stack.temp, stack.result}; buffer[0] = _left->eval_string(c, swapped_stack); size_t pos = 1; for (xpath_ast_node* n = _right; n; n = n->_next, ++pos) buffer[pos] = n->eval_string(c, swapped_stack); assert(pos == count); // get total length size_t length = 0; for (size_t i = 0; i < count; ++i) length += buffer[i].length(); // create final string char_t* result = static_cast(stack.result->allocate((length + 1) * sizeof(char_t))); assert(result); char_t* ri = result; for (size_t j = 0; j < count; ++j) for (const char_t* bi = buffer[j].c_str(); *bi; ++bi) *ri++ = *bi; *ri = 0; return xpath_string(result, true); } xpath_string eval_string(const xpath_context& c, const xpath_stack& stack) { switch (_type) { case ast_string_constant: return xpath_string_const(_data.string); case ast_func_local_name_0: { xpath_node na = c.n; return xpath_string_const(local_name(na)); } case ast_func_local_name_1: { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ns = _left->eval_node_set(c, stack); xpath_node na = ns.first(); return xpath_string_const(local_name(na)); } case ast_func_name_0: { xpath_node na = c.n; return xpath_string_const(qualified_name(na)); } case ast_func_name_1: { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ns = _left->eval_node_set(c, stack); xpath_node na = ns.first(); return xpath_string_const(qualified_name(na)); } case ast_func_namespace_uri_0: { xpath_node na = c.n; return xpath_string_const(namespace_uri(na)); } case ast_func_namespace_uri_1: { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ns = _left->eval_node_set(c, stack); xpath_node na = ns.first(); return xpath_string_const(namespace_uri(na)); } case ast_func_string_0: return string_value(c.n, stack.result); case ast_func_string_1: return _left->eval_string(c, stack); case ast_func_concat: return eval_string_concat(c, stack); case ast_func_substring_before: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_string s = _left->eval_string(c, swapped_stack); xpath_string p = _right->eval_string(c, swapped_stack); const char_t* pos = find_substring(s.c_str(), p.c_str()); return pos ? xpath_string(s.c_str(), pos, stack.result) : xpath_string(); } case ast_func_substring_after: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_string s = _left->eval_string(c, swapped_stack); xpath_string p = _right->eval_string(c, swapped_stack); const char_t* pos = find_substring(s.c_str(), p.c_str()); if (!pos) return xpath_string(); const char_t* result = pos + p.length(); return s.uses_heap() ? xpath_string(result, stack.result) : xpath_string_const(result); } case ast_func_substring_2: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_string s = _left->eval_string(c, swapped_stack); size_t s_length = s.length(); double first = round_nearest(_right->eval_number(c, stack)); if (is_nan(first)) return xpath_string(); // NaN else if (first >= s_length + 1) return xpath_string(); size_t pos = first < 1 ? 1 : static_cast(first); assert(1 <= pos && pos <= s_length + 1); const char_t* rbegin = s.c_str() + (pos - 1); return s.uses_heap() ? xpath_string(rbegin, stack.result) : xpath_string_const(rbegin); } case ast_func_substring_3: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_string s = _left->eval_string(c, swapped_stack); size_t s_length = s.length(); double first = round_nearest(_right->eval_number(c, stack)); double last = first + round_nearest(_right->_next->eval_number(c, stack)); if (is_nan(first) || is_nan(last)) return xpath_string(); else if (first >= s_length + 1) return xpath_string(); else if (first >= last) return xpath_string(); else if (last < 1) return xpath_string(); size_t pos = first < 1 ? 1 : static_cast(first); size_t end = last >= s_length + 1 ? s_length + 1 : static_cast(last); assert(1 <= pos && pos <= end && end <= s_length + 1); const char_t* rbegin = s.c_str() + (pos - 1); const char_t* rend = s.c_str() + (end - 1); return (end == s_length + 1 && !s.uses_heap()) ? xpath_string_const(rbegin) : xpath_string(rbegin, rend, stack.result); } case ast_func_normalize_space_0: { xpath_string s = string_value(c.n, stack.result); normalize_space(s.data(stack.result)); return s; } case ast_func_normalize_space_1: { xpath_string s = _left->eval_string(c, stack); normalize_space(s.data(stack.result)); return s; } case ast_func_translate: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_string s = _left->eval_string(c, stack); xpath_string from = _right->eval_string(c, swapped_stack); xpath_string to = _right->_next->eval_string(c, swapped_stack); translate(s.data(stack.result), from.c_str(), to.c_str()); return s; } case ast_variable: { assert(_rettype == _data.variable->type()); if (_rettype == xpath_type_string) return xpath_string_const(_data.variable->get_string()); // fallthrough to type conversion } default: { switch (_rettype) { case xpath_type_boolean: return xpath_string_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); case xpath_type_number: return convert_number_to_string(eval_number(c, stack), stack.result); case xpath_type_node_set: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_node_set_raw ns = eval_node_set(c, swapped_stack); return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result); } default: assert(!"Wrong expression for return type string"); return xpath_string(); } } } } xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack) { switch (_type) { case ast_op_union: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack); xpath_node_set_raw rs = _right->eval_node_set(c, stack); // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother rs.set_type(xpath_node_set::type_unsorted); rs.append(ls.begin(), ls.end(), stack.result); rs.remove_duplicates(); return rs; } case ast_filter: case ast_filter_posinv: { xpath_node_set_raw set = _left->eval_node_set(c, stack); // either expression is a number or it contains position() call; sort by document order if (_type == ast_filter) set.sort_do(); apply_predicate(set, 0, _right, stack); return set; } case ast_func_id: return xpath_node_set_raw(); case ast_step: { switch (_axis) { case axis_ancestor: return step_do(c, stack, axis_to_type()); case axis_ancestor_or_self: return step_do(c, stack, axis_to_type()); case axis_attribute: return step_do(c, stack, axis_to_type()); case axis_child: return step_do(c, stack, axis_to_type()); case axis_descendant: return step_do(c, stack, axis_to_type()); case axis_descendant_or_self: return step_do(c, stack, axis_to_type()); case axis_following: return step_do(c, stack, axis_to_type()); case axis_following_sibling: return step_do(c, stack, axis_to_type()); case axis_namespace: // namespaced axis is not supported return xpath_node_set_raw(); case axis_parent: return step_do(c, stack, axis_to_type()); case axis_preceding: return step_do(c, stack, axis_to_type()); case axis_preceding_sibling: return step_do(c, stack, axis_to_type()); case axis_self: return step_do(c, stack, axis_to_type()); default: assert(!"Unknown axis"); return xpath_node_set_raw(); } } case ast_step_root: { assert(!_right); // root step can't have any predicates xpath_node_set_raw ns; ns.set_type(xpath_node_set::type_sorted); if (c.n.node()) ns.push_back(c.n.node().root(), stack.result); else if (c.n.attribute()) ns.push_back(c.n.parent().root(), stack.result); return ns; } case ast_variable: { assert(_rettype == _data.variable->type()); if (_rettype == xpath_type_node_set) { const xpath_node_set& s = _data.variable->get_node_set(); xpath_node_set_raw ns; ns.set_type(s.type()); ns.append(s.begin(), s.end(), stack.result); return ns; } // fallthrough to type conversion } default: assert(!"Wrong expression for return type node set"); return xpath_node_set_raw(); } } bool is_posinv() { switch (_type) { case ast_func_position: return false; case ast_string_constant: case ast_number_constant: case ast_variable: return true; case ast_step: case ast_step_root: return true; case ast_predicate: case ast_filter: case ast_filter_posinv: return true; default: if (_left && !_left->is_posinv()) return false; for (xpath_ast_node* n = _right; n; n = n->_next) if (!n->is_posinv()) return false; return true; } } xpath_value_type rettype() const { return static_cast(_rettype); } }; struct xpath_parser { xpath_allocator* _alloc; xpath_lexer _lexer; const char_t* _query; xpath_variable_set* _variables; xpath_parse_result* _result; #ifdef PUGIXML_NO_EXCEPTIONS jmp_buf _error_handler; #endif void throw_error(const char* message) { _result->error = message; _result->offset = _lexer.current_pos() - _query; #ifdef PUGIXML_NO_EXCEPTIONS longjmp(_error_handler, 1); #else throw xpath_exception(*_result); #endif } void throw_error_oom() { #ifdef PUGIXML_NO_EXCEPTIONS throw_error("Out of memory"); #else throw std::bad_alloc(); #endif } void* alloc_node() { void* result = _alloc->allocate_nothrow(sizeof(xpath_ast_node)); if (!result) throw_error_oom(); return result; } const char_t* alloc_string(const xpath_lexer_string& value) { if (value.begin) { size_t length = static_cast(value.end - value.begin); char_t* c = static_cast(_alloc->allocate_nothrow((length + 1) * sizeof(char_t))); if (!c) throw_error_oom(); memcpy(c, value.begin, length * sizeof(char_t)); c[length] = 0; return c; } else return 0; } xpath_ast_node* parse_function_helper(ast_type_t type0, ast_type_t type1, size_t argc, xpath_ast_node* args[2]) { assert(argc <= 1); if (argc == 1 && args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); return new (alloc_node()) xpath_ast_node(argc == 0 ? type0 : type1, xpath_type_string, args[0]); } xpath_ast_node* parse_function(const xpath_lexer_string& name, size_t argc, xpath_ast_node* args[2]) { switch (name.begin[0]) { case 'b': if (name == PUGIXML_TEXT("boolean") && argc == 1) return new (alloc_node()) xpath_ast_node(ast_func_boolean, xpath_type_boolean, args[0]); break; case 'c': if (name == PUGIXML_TEXT("count") && argc == 1) { if (args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); return new (alloc_node()) xpath_ast_node(ast_func_count, xpath_type_number, args[0]); } else if (name == PUGIXML_TEXT("contains") && argc == 2) return new (alloc_node()) xpath_ast_node(ast_func_contains, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("concat") && argc >= 2) return new (alloc_node()) xpath_ast_node(ast_func_concat, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("ceiling") && argc == 1) return new (alloc_node()) xpath_ast_node(ast_func_ceiling, xpath_type_number, args[0]); break; case 'f': if (name == PUGIXML_TEXT("false") && argc == 0) return new (alloc_node()) xpath_ast_node(ast_func_false, xpath_type_boolean); else if (name == PUGIXML_TEXT("floor") && argc == 1) return new (alloc_node()) xpath_ast_node(ast_func_floor, xpath_type_number, args[0]); break; case 'i': if (name == PUGIXML_TEXT("id") && argc == 1) return new (alloc_node()) xpath_ast_node(ast_func_id, xpath_type_node_set, args[0]); break; case 'l': if (name == PUGIXML_TEXT("last") && argc == 0) return new (alloc_node()) xpath_ast_node(ast_func_last, xpath_type_number); else if (name == PUGIXML_TEXT("lang") && argc == 1) return new (alloc_node()) xpath_ast_node(ast_func_lang, xpath_type_boolean, args[0]); else if (name == PUGIXML_TEXT("local-name") && argc <= 1) return parse_function_helper(ast_func_local_name_0, ast_func_local_name_1, argc, args); break; case 'n': if (name == PUGIXML_TEXT("name") && argc <= 1) return parse_function_helper(ast_func_name_0, ast_func_name_1, argc, args); else if (name == PUGIXML_TEXT("namespace-uri") && argc <= 1) return parse_function_helper(ast_func_namespace_uri_0, ast_func_namespace_uri_1, argc, args); else if (name == PUGIXML_TEXT("normalize-space") && argc <= 1) return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("not") && argc == 1) return new (alloc_node()) xpath_ast_node(ast_func_not, xpath_type_boolean, args[0]); else if (name == PUGIXML_TEXT("number") && argc <= 1) return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]); break; case 'p': if (name == PUGIXML_TEXT("position") && argc == 0) return new (alloc_node()) xpath_ast_node(ast_func_position, xpath_type_number); break; case 'r': if (name == PUGIXML_TEXT("round") && argc == 1) return new (alloc_node()) xpath_ast_node(ast_func_round, xpath_type_number, args[0]); break; case 's': if (name == PUGIXML_TEXT("string") && argc <= 1) return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]); else if (name == PUGIXML_TEXT("string-length") && argc <= 1) return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_string, args[0]); else if (name == PUGIXML_TEXT("starts-with") && argc == 2) return new (alloc_node()) xpath_ast_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]); else if (name == PUGIXML_TEXT("substring-before") && argc == 2) return new (alloc_node()) xpath_ast_node(ast_func_substring_before, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("substring-after") && argc == 2) return new (alloc_node()) xpath_ast_node(ast_func_substring_after, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("substring") && (argc == 2 || argc == 3)) return new (alloc_node()) xpath_ast_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("sum") && argc == 1) { if (args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); return new (alloc_node()) xpath_ast_node(ast_func_sum, xpath_type_number, args[0]); } break; case 't': if (name == PUGIXML_TEXT("translate") && argc == 3) return new (alloc_node()) xpath_ast_node(ast_func_translate, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("true") && argc == 0) return new (alloc_node()) xpath_ast_node(ast_func_true, xpath_type_boolean); break; default: break; } throw_error("Unrecognized function or wrong parameter count"); return 0; } axis_t parse_axis_name(const xpath_lexer_string& name, bool& specified) { specified = true; switch (name.begin[0]) { case 'a': if (name == PUGIXML_TEXT("ancestor")) return axis_ancestor; else if (name == PUGIXML_TEXT("ancestor-or-self")) return axis_ancestor_or_self; else if (name == PUGIXML_TEXT("attribute")) return axis_attribute; break; case 'c': if (name == PUGIXML_TEXT("child")) return axis_child; break; case 'd': if (name == PUGIXML_TEXT("descendant")) return axis_descendant; else if (name == PUGIXML_TEXT("descendant-or-self")) return axis_descendant_or_self; break; case 'f': if (name == PUGIXML_TEXT("following")) return axis_following; else if (name == PUGIXML_TEXT("following-sibling")) return axis_following_sibling; break; case 'n': if (name == PUGIXML_TEXT("namespace")) return axis_namespace; break; case 'p': if (name == PUGIXML_TEXT("parent")) return axis_parent; else if (name == PUGIXML_TEXT("preceding")) return axis_preceding; else if (name == PUGIXML_TEXT("preceding-sibling")) return axis_preceding_sibling; break; case 's': if (name == PUGIXML_TEXT("self")) return axis_self; break; default: break; } specified = false; return axis_child; } nodetest_t parse_node_test_type(const xpath_lexer_string& name) { switch (name.begin[0]) { case 'c': if (name == PUGIXML_TEXT("comment")) return nodetest_type_comment; break; case 'n': if (name == PUGIXML_TEXT("node")) return nodetest_type_node; break; case 'p': if (name == PUGIXML_TEXT("processing-instruction")) return nodetest_type_pi; break; case 't': if (name == PUGIXML_TEXT("text")) return nodetest_type_text; break; default: break; } return nodetest_none; } // PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall xpath_ast_node* parse_primary_expression() { switch (_lexer.current()) { case lex_var_ref: { xpath_lexer_string name = _lexer.contents(); if (!_variables) throw_error("Unknown variable: variable set is not provided"); xpath_variable* var = get_variable(_variables, name.begin, name.end); if (!var) throw_error("Unknown variable: variable set does not contain the given name"); _lexer.next(); return new (alloc_node()) xpath_ast_node(ast_variable, var->type(), var); } case lex_open_brace: { _lexer.next(); xpath_ast_node* n = parse_expression(); if (_lexer.current() != lex_close_brace) throw_error("Unmatched braces"); _lexer.next(); return n; } case lex_quoted_string: { const char_t* value = alloc_string(_lexer.contents()); xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_string_constant, xpath_type_string, value); _lexer.next(); return n; } case lex_number: { double value = 0; if (!convert_string_to_number(_lexer.contents().begin, _lexer.contents().end, &value)) throw_error_oom(); xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_number_constant, xpath_type_number, value); _lexer.next(); return n; } case lex_string: { xpath_ast_node* args[2] = {0}; size_t argc = 0; xpath_lexer_string function = _lexer.contents(); _lexer.next(); xpath_ast_node* last_arg = 0; if (_lexer.current() != lex_open_brace) throw_error("Unrecognized function call"); _lexer.next(); if (_lexer.current() != lex_close_brace) args[argc++] = parse_expression(); while (_lexer.current() != lex_close_brace) { if (_lexer.current() != lex_comma) throw_error("No comma between function arguments"); _lexer.next(); xpath_ast_node* n = parse_expression(); if (argc < 2) args[argc] = n; else last_arg->set_next(n); argc++; last_arg = n; } _lexer.next(); return parse_function(function, argc, args); } default: throw_error("Unrecognizable primary expression"); return 0; } } // FilterExpr ::= PrimaryExpr | FilterExpr Predicate // Predicate ::= '[' PredicateExpr ']' // PredicateExpr ::= Expr xpath_ast_node* parse_filter_expression() { xpath_ast_node* n = parse_primary_expression(); while (_lexer.current() == lex_open_square_brace) { _lexer.next(); xpath_ast_node* expr = parse_expression(); if (n->rettype() != xpath_type_node_set) throw_error("Predicate has to be applied to node set"); bool posinv = expr->rettype() != xpath_type_number && expr->is_posinv(); n = new (alloc_node()) xpath_ast_node(posinv ? ast_filter_posinv : ast_filter, xpath_type_node_set, n, expr); if (_lexer.current() != lex_close_square_brace) throw_error("Unmatched square brace"); _lexer.next(); } return n; } // Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep // AxisSpecifier ::= AxisName '::' | '@'? // NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')' // NameTest ::= '*' | NCName ':' '*' | QName // AbbreviatedStep ::= '.' | '..' xpath_ast_node* parse_step(xpath_ast_node* set) { if (set && set->rettype() != xpath_type_node_set) throw_error("Step has to be applied to node set"); bool axis_specified = false; axis_t axis = axis_child; // implied child axis if (_lexer.current() == lex_axis_attribute) { axis = axis_attribute; axis_specified = true; _lexer.next(); } else if (_lexer.current() == lex_dot) { _lexer.next(); return new (alloc_node()) xpath_ast_node(ast_step, set, axis_self, nodetest_type_node, 0); } else if (_lexer.current() == lex_double_dot) { _lexer.next(); return new (alloc_node()) xpath_ast_node(ast_step, set, axis_parent, nodetest_type_node, 0); } nodetest_t nt_type = nodetest_none; xpath_lexer_string nt_name; if (_lexer.current() == lex_string) { // node name test nt_name = _lexer.contents(); _lexer.next(); // was it an axis name? if (_lexer.current() == lex_double_colon) { // parse axis name if (axis_specified) throw_error("Two axis specifiers in one step"); axis = parse_axis_name(nt_name, axis_specified); if (!axis_specified) throw_error("Unknown axis"); // read actual node test _lexer.next(); if (_lexer.current() == lex_multiply) { nt_type = nodetest_all; nt_name = xpath_lexer_string(); _lexer.next(); } else if (_lexer.current() == lex_string) { nt_name = _lexer.contents(); _lexer.next(); } else throw_error("Unrecognized node test"); } if (nt_type == nodetest_none) { // node type test or processing-instruction if (_lexer.current() == lex_open_brace) { _lexer.next(); if (_lexer.current() == lex_close_brace) { _lexer.next(); nt_type = parse_node_test_type(nt_name); if (nt_type == nodetest_none) throw_error("Unrecognized node type"); nt_name = xpath_lexer_string(); } else if (nt_name == PUGIXML_TEXT("processing-instruction")) { if (_lexer.current() != lex_quoted_string) throw_error("Only literals are allowed as arguments to processing-instruction()"); nt_type = nodetest_pi; nt_name = _lexer.contents(); _lexer.next(); if (_lexer.current() != lex_close_brace) throw_error("Unmatched brace near processing-instruction()"); _lexer.next(); } else throw_error("Unmatched brace near node type test"); } // QName or NCName:* else { if (nt_name.end - nt_name.begin > 2 && nt_name.end[-2] == ':' && nt_name.end[-1] == '*') // NCName:* { nt_name.end--; // erase * nt_type = nodetest_all_in_namespace; } else nt_type = nodetest_name; } } } else if (_lexer.current() == lex_multiply) { nt_type = nodetest_all; _lexer.next(); } else throw_error("Unrecognized node test"); xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step, set, axis, nt_type, alloc_string(nt_name)); xpath_ast_node* last = 0; while (_lexer.current() == lex_open_square_brace) { _lexer.next(); xpath_ast_node* expr = parse_expression(); xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, xpath_type_node_set, expr); if (_lexer.current() != lex_close_square_brace) throw_error("Unmatched square brace"); _lexer.next(); if (last) last->set_next(pred); else n->set_right(pred); last = pred; } return n; } // RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step xpath_ast_node* parse_relative_location_path(xpath_ast_node* set) { xpath_ast_node* n = parse_step(set); while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) { lexeme_t l = _lexer.current(); _lexer.next(); if (l == lex_double_slash) n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); n = parse_step(n); } return n; } // LocationPath ::= RelativeLocationPath | AbsoluteLocationPath // AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath xpath_ast_node* parse_location_path() { if (_lexer.current() == lex_slash) { _lexer.next(); xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); // relative location path can start from axis_attribute, dot, double_dot, multiply and string lexemes; any other lexeme means standalone root path lexeme_t l = _lexer.current(); if (l == lex_string || l == lex_axis_attribute || l == lex_dot || l == lex_double_dot || l == lex_multiply) return parse_relative_location_path(n); else return n; } else if (_lexer.current() == lex_double_slash) { _lexer.next(); xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); return parse_relative_location_path(n); } // else clause moved outside of if because of bogus warning 'control may reach end of non-void function being inlined' in gcc 4.0.1 return parse_relative_location_path(0); } // PathExpr ::= LocationPath // | FilterExpr // | FilterExpr '/' RelativeLocationPath // | FilterExpr '//' RelativeLocationPath xpath_ast_node* parse_path_expression() { // Clarification. // PathExpr begins with either LocationPath or FilterExpr. // FilterExpr begins with PrimaryExpr // PrimaryExpr begins with '$' in case of it being a variable reference, // '(' in case of it being an expression, string literal, number constant or // function call. if (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace || _lexer.current() == lex_quoted_string || _lexer.current() == lex_number || _lexer.current() == lex_string) { if (_lexer.current() == lex_string) { // This is either a function call, or not - if not, we shall proceed with location path const char_t* state = _lexer.state(); while (PUGI__IS_CHARTYPE(*state, ct_space)) ++state; if (*state != '(') return parse_location_path(); // This looks like a function call; however this still can be a node-test. Check it. if (parse_node_test_type(_lexer.contents()) != nodetest_none) return parse_location_path(); } xpath_ast_node* n = parse_filter_expression(); if (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) { lexeme_t l = _lexer.current(); _lexer.next(); if (l == lex_double_slash) { if (n->rettype() != xpath_type_node_set) throw_error("Step has to be applied to node set"); n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); } // select from location path return parse_relative_location_path(n); } return n; } else return parse_location_path(); } // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr xpath_ast_node* parse_union_expression() { xpath_ast_node* n = parse_path_expression(); while (_lexer.current() == lex_union) { _lexer.next(); xpath_ast_node* expr = parse_union_expression(); if (n->rettype() != xpath_type_node_set || expr->rettype() != xpath_type_node_set) throw_error("Union operator has to be applied to node sets"); n = new (alloc_node()) xpath_ast_node(ast_op_union, xpath_type_node_set, n, expr); } return n; } // UnaryExpr ::= UnionExpr | '-' UnaryExpr xpath_ast_node* parse_unary_expression() { if (_lexer.current() == lex_minus) { _lexer.next(); xpath_ast_node* expr = parse_unary_expression(); return new (alloc_node()) xpath_ast_node(ast_op_negate, xpath_type_number, expr); } else return parse_union_expression(); } // MultiplicativeExpr ::= UnaryExpr // | MultiplicativeExpr '*' UnaryExpr // | MultiplicativeExpr 'div' UnaryExpr // | MultiplicativeExpr 'mod' UnaryExpr xpath_ast_node* parse_multiplicative_expression() { xpath_ast_node* n = parse_unary_expression(); while (_lexer.current() == lex_multiply || (_lexer.current() == lex_string && (_lexer.contents() == PUGIXML_TEXT("mod") || _lexer.contents() == PUGIXML_TEXT("div")))) { ast_type_t op = _lexer.current() == lex_multiply ? ast_op_multiply : _lexer.contents().begin[0] == 'd' ? ast_op_divide : ast_op_mod; _lexer.next(); xpath_ast_node* expr = parse_unary_expression(); n = new (alloc_node()) xpath_ast_node(op, xpath_type_number, n, expr); } return n; } // AdditiveExpr ::= MultiplicativeExpr // | AdditiveExpr '+' MultiplicativeExpr // | AdditiveExpr '-' MultiplicativeExpr xpath_ast_node* parse_additive_expression() { xpath_ast_node* n = parse_multiplicative_expression(); while (_lexer.current() == lex_plus || _lexer.current() == lex_minus) { lexeme_t l = _lexer.current(); _lexer.next(); xpath_ast_node* expr = parse_multiplicative_expression(); n = new (alloc_node()) xpath_ast_node(l == lex_plus ? ast_op_add : ast_op_subtract, xpath_type_number, n, expr); } return n; } // RelationalExpr ::= AdditiveExpr // | RelationalExpr '<' AdditiveExpr // | RelationalExpr '>' AdditiveExpr // | RelationalExpr '<=' AdditiveExpr // | RelationalExpr '>=' AdditiveExpr xpath_ast_node* parse_relational_expression() { xpath_ast_node* n = parse_additive_expression(); while (_lexer.current() == lex_less || _lexer.current() == lex_less_or_equal || _lexer.current() == lex_greater || _lexer.current() == lex_greater_or_equal) { lexeme_t l = _lexer.current(); _lexer.next(); xpath_ast_node* expr = parse_additive_expression(); n = new (alloc_node()) xpath_ast_node(l == lex_less ? ast_op_less : l == lex_greater ? ast_op_greater : l == lex_less_or_equal ? ast_op_less_or_equal : ast_op_greater_or_equal, xpath_type_boolean, n, expr); } return n; } // EqualityExpr ::= RelationalExpr // | EqualityExpr '=' RelationalExpr // | EqualityExpr '!=' RelationalExpr xpath_ast_node* parse_equality_expression() { xpath_ast_node* n = parse_relational_expression(); while (_lexer.current() == lex_equal || _lexer.current() == lex_not_equal) { lexeme_t l = _lexer.current(); _lexer.next(); xpath_ast_node* expr = parse_relational_expression(); n = new (alloc_node()) xpath_ast_node(l == lex_equal ? ast_op_equal : ast_op_not_equal, xpath_type_boolean, n, expr); } return n; } // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr xpath_ast_node* parse_and_expression() { xpath_ast_node* n = parse_equality_expression(); while (_lexer.current() == lex_string && _lexer.contents() == PUGIXML_TEXT("and")) { _lexer.next(); xpath_ast_node* expr = parse_equality_expression(); n = new (alloc_node()) xpath_ast_node(ast_op_and, xpath_type_boolean, n, expr); } return n; } // OrExpr ::= AndExpr | OrExpr 'or' AndExpr xpath_ast_node* parse_or_expression() { xpath_ast_node* n = parse_and_expression(); while (_lexer.current() == lex_string && _lexer.contents() == PUGIXML_TEXT("or")) { _lexer.next(); xpath_ast_node* expr = parse_and_expression(); n = new (alloc_node()) xpath_ast_node(ast_op_or, xpath_type_boolean, n, expr); } return n; } // Expr ::= OrExpr xpath_ast_node* parse_expression() { return parse_or_expression(); } xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result) { } xpath_ast_node* parse() { xpath_ast_node* result = parse_expression(); if (_lexer.current() != lex_eof) { // there are still unparsed tokens left, error throw_error("Incorrect query"); } return result; } static xpath_ast_node* parse(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result) { xpath_parser parser(query, variables, alloc, result); #ifdef PUGIXML_NO_EXCEPTIONS int error = setjmp(parser._error_handler); return (error == 0) ? parser.parse() : 0; #else return parser.parse(); #endif } }; struct xpath_query_impl { static xpath_query_impl* create() { void* memory = xml_memory::allocate(sizeof(xpath_query_impl)); return new (memory) xpath_query_impl(); } static void destroy(void* ptr) { if (!ptr) return; // free all allocated pages static_cast(ptr)->alloc.release(); // free allocator memory (with the first page) xml_memory::deallocate(ptr); } xpath_query_impl(): root(0), alloc(&block) { block.next = 0; } xpath_ast_node* root; xpath_allocator alloc; xpath_memory_block block; }; PUGI__FN xpath_string evaluate_string_impl(xpath_query_impl* impl, const xpath_node& n, xpath_stack_data& sd) { if (!impl) return xpath_string(); #ifdef PUGIXML_NO_EXCEPTIONS if (setjmp(sd.error_handler)) return xpath_string(); #endif xpath_context c(n, 1, 1); return impl->root->eval_string(c, sd.stack); } PUGI__NS_END OIIO_NAMESPACE_BEGIN namespace pugi { #ifndef PUGIXML_NO_EXCEPTIONS PUGI__FN xpath_exception::xpath_exception(const xpath_parse_result& result_): _result(result_) { assert(_result.error); } PUGI__FN const char* xpath_exception::what() const throw() { return _result.error; } PUGI__FN const xpath_parse_result& xpath_exception::result() const { return _result; } #endif PUGI__FN xpath_node::xpath_node() { } PUGI__FN xpath_node::xpath_node(const xml_node& node_): _node(node_) { } PUGI__FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_) { } PUGI__FN xml_node xpath_node::node() const { return _attribute ? xml_node() : _node; } PUGI__FN xml_attribute xpath_node::attribute() const { return _attribute; } PUGI__FN xml_node xpath_node::parent() const { return _attribute ? _node : _node.parent(); } PUGI__FN static void unspecified_bool_xpath_node(xpath_node***) { } PUGI__FN xpath_node::operator xpath_node::unspecified_bool_type() const { return (_node || _attribute) ? unspecified_bool_xpath_node : 0; } PUGI__FN bool xpath_node::operator!() const { return !(_node || _attribute); } PUGI__FN bool xpath_node::operator==(const xpath_node& n) const { return _node == n._node && _attribute == n._attribute; } PUGI__FN bool xpath_node::operator!=(const xpath_node& n) const { return _node != n._node || _attribute != n._attribute; } #ifdef __BORLANDC__ PUGI__FN bool operator&&(const xpath_node& lhs, bool rhs) { return (bool)lhs && rhs; } PUGI__FN bool operator||(const xpath_node& lhs, bool rhs) { return (bool)lhs || rhs; } #endif PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_) { assert(begin_ <= end_); size_t size_ = static_cast(end_ - begin_); if (size_ <= 1) { // deallocate old buffer if (_begin != &_storage) impl::xml_memory::deallocate(_begin); // use internal buffer if (begin_ != end_) _storage = *begin_; _begin = &_storage; _end = &_storage + size_; } else { // make heap copy xpath_node* storage = static_cast(impl::xml_memory::allocate(size_ * sizeof(xpath_node))); if (!storage) { #ifdef PUGIXML_NO_EXCEPTIONS return; #else throw std::bad_alloc(); #endif } memcpy(storage, begin_, size_ * sizeof(xpath_node)); // deallocate old buffer if (_begin != &_storage) impl::xml_memory::deallocate(_begin); // finalize _begin = storage; _end = storage + size_; } } PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage) { } PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_), _begin(&_storage), _end(&_storage) { _assign(begin_, end_); } PUGI__FN xpath_node_set::~xpath_node_set() { if (_begin != &_storage) impl::xml_memory::deallocate(_begin); } PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(ns._type), _begin(&_storage), _end(&_storage) { _assign(ns._begin, ns._end); } PUGI__FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns) { if (this == &ns) return *this; _type = ns._type; _assign(ns._begin, ns._end); return *this; } PUGI__FN xpath_node_set::type_t xpath_node_set::type() const { return _type; } PUGI__FN size_t xpath_node_set::size() const { return _end - _begin; } PUGI__FN bool xpath_node_set::empty() const { return _begin == _end; } PUGI__FN const xpath_node& xpath_node_set::operator[](size_t index) const { assert(index < size()); return _begin[index]; } PUGI__FN xpath_node_set::const_iterator xpath_node_set::begin() const { return _begin; } PUGI__FN xpath_node_set::const_iterator xpath_node_set::end() const { return _end; } PUGI__FN void xpath_node_set::sort(bool reverse) { _type = impl::xpath_sort(_begin, _end, _type, reverse); } PUGI__FN xpath_node xpath_node_set::first() const { return impl::xpath_first(_begin, _end, _type); } PUGI__FN xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0) { } PUGI__FN xpath_parse_result::operator bool() const { return error == 0; } PUGI__FN const char* xpath_parse_result::description() const { return error ? error : "No error"; } PUGI__FN xpath_variable::xpath_variable() { } PUGI__FN const char_t* xpath_variable::name() const { switch (_type) { case xpath_type_node_set: return static_cast(this)->name; case xpath_type_number: return static_cast(this)->name; case xpath_type_string: return static_cast(this)->name; case xpath_type_boolean: return static_cast(this)->name; default: assert(!"Invalid variable type"); return 0; } } PUGI__FN xpath_value_type xpath_variable::type() const { return _type; } PUGI__FN bool xpath_variable::get_boolean() const { return (_type == xpath_type_boolean) ? static_cast(this)->value : false; } PUGI__FN double xpath_variable::get_number() const { return (_type == xpath_type_number) ? static_cast(this)->value : impl::gen_nan(); } PUGI__FN const char_t* xpath_variable::get_string() const { const char_t* value = (_type == xpath_type_string) ? static_cast(this)->value : 0; return value ? value : PUGIXML_TEXT(""); } PUGI__FN const xpath_node_set& xpath_variable::get_node_set() const { return (_type == xpath_type_node_set) ? static_cast(this)->value : impl::dummy_node_set; } PUGI__FN bool xpath_variable::set(bool value) { if (_type != xpath_type_boolean) return false; static_cast(this)->value = value; return true; } PUGI__FN bool xpath_variable::set(double value) { if (_type != xpath_type_number) return false; static_cast(this)->value = value; return true; } PUGI__FN bool xpath_variable::set(const char_t* value) { if (_type != xpath_type_string) return false; impl::xpath_variable_string* var = static_cast(this); // duplicate string size_t size = (impl::strlength(value) + 1) * sizeof(char_t); char_t* copy = static_cast(impl::xml_memory::allocate(size)); if (!copy) return false; memcpy(copy, value, size); // replace old string if (var->value) impl::xml_memory::deallocate(var->value); var->value = copy; return true; } PUGI__FN bool xpath_variable::set(const xpath_node_set& value) { if (_type != xpath_type_node_set) return false; static_cast(this)->value = value; return true; } PUGI__FN xpath_variable_set::xpath_variable_set() { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0; } PUGI__FN xpath_variable_set::~xpath_variable_set() { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) { xpath_variable* var = _data[i]; while (var) { xpath_variable* next = var->_next; impl::delete_xpath_variable(var->_type, var); var = next; } } } PUGI__FN xpath_variable* xpath_variable_set::find(const char_t* name) const { const size_t hash_size = sizeof(_data) / sizeof(_data[0]); size_t hash = impl::hash_string(name) % hash_size; // look for existing variable for (xpath_variable* var = _data[hash]; var; var = var->_next) if (impl::strequal(var->name(), name)) return var; return 0; } PUGI__FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type) { const size_t hash_size = sizeof(_data) / sizeof(_data[0]); size_t hash = impl::hash_string(name) % hash_size; // look for existing variable for (xpath_variable* var = _data[hash]; var; var = var->_next) if (impl::strequal(var->name(), name)) return var->type() == type ? var : 0; // add new variable xpath_variable* result = impl::new_xpath_variable(type, name); if (result) { result->_type = type; result->_next = _data[hash]; _data[hash] = result; } return result; } PUGI__FN bool xpath_variable_set::set(const char_t* name, bool value) { xpath_variable* var = add(name, xpath_type_boolean); return var ? var->set(value) : false; } PUGI__FN bool xpath_variable_set::set(const char_t* name, double value) { xpath_variable* var = add(name, xpath_type_number); return var ? var->set(value) : false; } PUGI__FN bool xpath_variable_set::set(const char_t* name, const char_t* value) { xpath_variable* var = add(name, xpath_type_string); return var ? var->set(value) : false; } PUGI__FN bool xpath_variable_set::set(const char_t* name, const xpath_node_set& value) { xpath_variable* var = add(name, xpath_type_node_set); return var ? var->set(value) : false; } PUGI__FN xpath_variable* xpath_variable_set::get(const char_t* name) { return find(name); } PUGI__FN const xpath_variable* xpath_variable_set::get(const char_t* name) const { return find(name); } PUGI__FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0) { impl::xpath_query_impl* qimpl = impl::xpath_query_impl::create(); if (!qimpl) { #ifdef PUGIXML_NO_EXCEPTIONS _result.error = "Out of memory"; #else throw std::bad_alloc(); #endif } else { impl::buffer_holder impl_holder(qimpl, impl::xpath_query_impl::destroy); qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result); if (qimpl->root) { _impl = static_cast(impl_holder.release()); _result.error = 0; } } } PUGI__FN xpath_query::~xpath_query() { impl::xpath_query_impl::destroy(_impl); } PUGI__FN xpath_value_type xpath_query::return_type() const { if (!_impl) return xpath_type_none; return static_cast(_impl)->root->rettype(); } PUGI__FN bool xpath_query::evaluate_boolean(const xpath_node& n) const { if (!_impl) return false; impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; #ifdef PUGIXML_NO_EXCEPTIONS if (setjmp(sd.error_handler)) return false; #endif return static_cast(_impl)->root->eval_boolean(c, sd.stack); } PUGI__FN double xpath_query::evaluate_number(const xpath_node& n) const { if (!_impl) return impl::gen_nan(); impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; #ifdef PUGIXML_NO_EXCEPTIONS if (setjmp(sd.error_handler)) return impl::gen_nan(); #endif return static_cast(_impl)->root->eval_number(c, sd.stack); } #ifndef PUGIXML_NO_STL PUGI__FN string_t xpath_query::evaluate_string(const xpath_node& n) const { impl::xpath_stack_data sd; return impl::evaluate_string_impl(static_cast(_impl), n, sd).c_str(); } #endif PUGI__FN size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const { impl::xpath_stack_data sd; impl::xpath_string r = impl::evaluate_string_impl(static_cast(_impl), n, sd); size_t full_size = r.length() + 1; if (capacity > 0) { size_t size = (full_size < capacity) ? full_size : capacity; assert(size > 0); memcpy(buffer, r.c_str(), (size - 1) * sizeof(char_t)); buffer[size - 1] = 0; } return full_size; } PUGI__FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const { if (!_impl) return xpath_node_set(); impl::xpath_ast_node* root = static_cast(_impl)->root; if (root->rettype() != xpath_type_node_set) { #ifdef PUGIXML_NO_EXCEPTIONS return xpath_node_set(); #else xpath_parse_result res; res.error = "Expression does not evaluate to node set"; throw xpath_exception(res); #endif } impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; #ifdef PUGIXML_NO_EXCEPTIONS if (setjmp(sd.error_handler)) return xpath_node_set(); #endif impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack); return xpath_node_set(r.begin(), r.end(), r.type()); } PUGI__FN const xpath_parse_result& xpath_query::result() const { return _result; } PUGI__FN static void unspecified_bool_xpath_query(xpath_query***) { } PUGI__FN xpath_query::operator xpath_query::unspecified_bool_type() const { return _impl ? unspecified_bool_xpath_query : 0; } PUGI__FN bool xpath_query::operator!() const { return !_impl; } PUGI__FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const { xpath_query q(query, variables); return select_single_node(q); } PUGI__FN xpath_node xml_node::select_single_node(const xpath_query& query) const { xpath_node_set s = query.evaluate_node_set(*this); return s.empty() ? xpath_node() : s.first(); } PUGI__FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const { xpath_query q(query, variables); return select_nodes(q); } PUGI__FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const { return query.evaluate_node_set(*this); } } OIIO_NAMESPACE_END #endif #ifdef __BORLANDC__ # pragma option pop #endif // Intel C++ does not properly keep warning state for function templates, // so popping warning state at the end of translation unit leads to warnings in the middle. #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) # pragma warning(pop) #endif // Undefine all local macros (makes sure we're not leaking macros in header-only mode) #undef PUGI__NO_INLINE #undef PUGI__STATIC_ASSERT #undef PUGI__DMC_VOLATILE #undef PUGI__MSVC_CRT_VERSION #undef PUGI__NS_BEGIN #undef PUGI__NS_END #undef PUGI__FN #undef PUGI__FN_NO_INLINE #undef PUGI__IS_CHARTYPE_IMPL #undef PUGI__IS_CHARTYPE #undef PUGI__IS_CHARTYPEX #undef PUGI__SKIPWS #undef PUGI__OPTSET #undef PUGI__PUSHNODE #undef PUGI__POPNODE #undef PUGI__SCANFOR #undef PUGI__SCANWHILE #undef PUGI__ENDSEG #undef PUGI__THROW_ERROR #undef PUGI__CHECK_ERROR #endif /** * Copyright (c) 2006-2012 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/strutil.h0000644000175000017500000005266113151711064022524 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ ///////////////////////////////////////////////////////////////////////// /// @file strutil.h /// /// @brief String-related utilities, all in namespace Strutil. ///////////////////////////////////////////////////////////////////////// #ifndef OPENIMAGEIO_STRUTIL_H #define OPENIMAGEIO_STRUTIL_H #include #include #include #include #include #include #include "export.h" #include "oiioversion.h" #include "tinyformat.h" #include "string_view.h" #include "hash.h" #ifndef OPENIMAGEIO_PRINTF_ARGS # ifndef __GNUC__ # define __attribute__(x) # endif // Enable printf-like warnings with gcc by attaching // OPENIMAGEIO_PRINTF_ARGS to printf-like functions. Eg: // // void foo (const char* fmt, ...) OPENIMAGEIO_PRINTF_ARGS(1,2); // // The arguments specify the positions of the format string and the the // beginning of the varargs parameter list respectively. // // For member functions with arguments like the example above, you need // OPENIMAGEIO_PRINTF_ARGS(2,3) instead. (gcc includes the implicit this // pointer when it counts member function arguments.) # define OPENIMAGEIO_PRINTF_ARGS(fmtarg_pos, vararg_pos) \ __attribute__ ((format (printf, fmtarg_pos, vararg_pos) )) #endif OIIO_NAMESPACE_BEGIN /// @namespace Strutil /// /// @brief String-related utilities. namespace Strutil { /// Construct a std::string in a printf-like fashion. In other words, /// something like: /// std::string s = Strutil::format ("blah %d %g", (int)foo, (float)bar); /// /// The printf argument list is fully typesafe via tinyformat; format /// conceptually has the signature /// /// std::string Strutil::format (const char *fmt, ...); TINYFORMAT_WRAP_FORMAT (std::string, format, /**/, std::ostringstream msg;, msg, return msg.str();) /// Return a std::string formatted from printf-like arguments. Like the /// real sprintf, this is not guaranteed type-safe and is not extensible /// like format(). You would only want to use this instead of the safe /// format() in rare situations where you really need to use obscure /// printf features that aren't supported by tinyformat. std::string OIIO_API format_raw (const char *fmt, ...) OPENIMAGEIO_PRINTF_ARGS(1,2); /// Return a std::string formatted from printf-like arguments -- passed /// already as a va_list. Like vsprintf, this is not guaranteed /// type-safe and is not extensible like format(). std::string OIIO_API vformat (const char *fmt, va_list ap) OPENIMAGEIO_PRINTF_ARGS(1,0); /// Return a string expressing a number of bytes, in human readable form. /// - memformat(153) -> "153 B" /// - memformat(15300) -> "14.9 KB" /// - memformat(15300000) -> "14.6 MB" /// - memformat(15300000000LL) -> "14.2 GB" std::string OIIO_API memformat (long long bytes, int digits=1); /// Return a string expressing an elapsed time, in human readable form. /// e.g. "0:35.2" std::string OIIO_API timeintervalformat (double secs, int digits=1); /// Get a map with RESTful arguments extracted from the given string 'str'. /// Add it into the 'result' argument (Warning: the 'result' argument may /// be changed even if 'get_rest_arguments ()' return an error!). /// Return true on success, false on error. /// Acceptable forms: /// - text?arg1=val1&arg2=val2... /// - ?arg1=val1&arg2=val2... /// Everything before question mark will be saved into the 'base' argument. bool OIIO_API get_rest_arguments (const std::string &str, std::string &base, std::map &result); /// Take a string that may have embedded newlines, tabs, etc., and turn /// those characters into escape sequences like \n, \t, \v, \b, \r, \f, /// \a, \\, \". std::string OIIO_API escape_chars (string_view unescaped); /// Take a string that has embedded escape sequences (\\, \", \n, etc.) /// and collapse them into the 'real' characters. std::string OIIO_API unescape_chars (string_view escaped); /// Word-wrap string 'src' to no more than columns width, splitting at /// space characters. It assumes that 'prefix' characters are already /// printed, and furthermore, if it should need to wrap, it prefixes that /// number of spaces in front of subsequent lines. By illustration, /// wordwrap("0 1 2 3 4 5 6 7 8", 4, 10) should return: /// "0 1 2\n 3 4 5\n 6 7 8" std::string OIIO_API wordwrap (string_view src, int columns=80, int prefix=0); /// Hash a string_view. inline size_t strhash (string_view s) { return s.length() ? farmhash::Hash (s) : 0; } /// Case-insensitive comparison of strings. For speed, this always uses /// a static locale that doesn't require a mutex. bool OIIO_API iequals (string_view a, string_view b); /// Does 'a' start with the string 'b', with a case-sensitive comparison? bool OIIO_API starts_with (string_view a, string_view b); /// Does 'a' start with the string 'b', with a case-insensitive comparison? /// For speed, this always uses a static locale that doesn't require a mutex. bool OIIO_API istarts_with (string_view a, string_view b); /// Does 'a' end with the string 'b', with a case-sensitive comparison? bool OIIO_API ends_with (string_view a, string_view b); /// Does 'a' end with the string 'b', with a case-insensitive comparison? /// For speed, this always uses a static locale that doesn't require a mutex. bool OIIO_API iends_with (string_view a, string_view b); /// Does 'a' contain the string 'b' within it? bool OIIO_API contains (string_view a, string_view b); /// Does 'a' contain the string 'b' within it, using a case-insensitive /// comparison? bool OIIO_API icontains (string_view a, string_view b); /// Convert to upper case, faster than std::toupper because we use /// a static locale that doesn't require a mutex lock. void OIIO_API to_lower (std::string &a); /// Convert to upper case, faster than std::toupper because we use /// a static locale that doesn't require a mutex lock. void OIIO_API to_upper (std::string &a); /// Return a reference to the section of str that has all consecutive /// characters in chars removed from the beginning and ending. If chars is /// empty, it will be interpreted as " \t\n\r\f\v" (whitespace). string_view OIIO_API strip (string_view str, string_view chars=string_view()); /// Fills the "result" list with the words in the string, using sep as /// the delimiter string. If maxsplit is > -1, at most maxsplit splits /// are done. If sep is "", any whitespace string is a separator. void OIIO_API split (string_view str, std::vector &result, string_view sep = string_view(), int maxsplit = -1); void OIIO_API split (string_view str, std::vector &result, string_view sep = string_view(), int maxsplit = -1); /// Join all the strings in 'seq' into one big string, separated by the /// 'sep' string. std::string OIIO_API join (const std::vector &seq, string_view sep = string_view()); std::string OIIO_API join (const std::vector &seq, string_view sep = string_view()); /// Repeat a string formed by concatenating str n times. std::string OIIO_API repeat (string_view str, int n); /// Replace a pattern inside a string and return the result. If global is /// true, replace all instances of the pattern, otherwise just the first. std::string OIIO_API replace (string_view str, string_view pattern, string_view replacement, bool global=false); // Helper template to test if a string is a generic type template inline bool string_is (string_view /*s*/) { return false; // Generic: assume there is an explicit specialization } // Special case for int template <> inline bool string_is (string_view s) { char *endptr = 0; strtol (s.data(), &endptr, 10); return (s.data() + s.size() == endptr); } // Special case for float template <> inline bool string_is (string_view s) { char *endptr = 0; strtod (s.data(), &endptr); return (s.data() + s.size() == endptr); } // Helper template to convert from generic type to string template inline T from_string (string_view s) { return T(s); // Generic: assume there is an explicit converter } // Special case for int template<> inline int from_string (string_view s) { return s.size() ? strtol (s.c_str(), NULL, 10) : 0; } // Special case for uint template<> inline unsigned int from_string (string_view s) { return s.size() ? strtoul (s.c_str(), NULL, 10) : (unsigned int)0; } // Special case for float template<> inline float from_string (string_view s) { return s.size() ? (float)strtod (s.c_str(), NULL) : 0.0f; } /// Given a string containing values separated by a comma (or optionally /// another separator), extract the individual values, placing them into /// vals[] which is presumed to already contain defaults. If only a single /// value was in the list, replace all elements of vals[] with the value. /// Otherwise, replace them in the same order. A missing value will simply /// not be replaced. Return the number of values found in the list /// (including blank or malformed ones). If the vals vector was empty /// initially, grow it as necessary. /// /// For example, if T=float, suppose initially, vals[] = {0, 1, 2}, then /// "3.14" results in vals[] = {3.14, 3.14, 3.14} /// "3.14,,-2.0" results in vals[] = {3.14, 1, -2.0} /// /// This can work for type T = int, float, or any type for that has /// an explicit constructor from a std::string. template int extract_from_list_string (std::vector &vals, string_view list, string_view sep = ",") { size_t nvals = vals.size(); std::vector valuestrings; Strutil::split (list, valuestrings, sep); for (size_t i = 0, e = valuestrings.size(); i < e; ++i) { T v = from_string (valuestrings[i]); if (nvals == 0) vals.push_back (v); else if (valuestrings[i].size()) { if (vals.size() > i) // don't replace non-existnt entries vals[i] = from_string (valuestrings[i]); } /* Otherwise, empty space between commas, so leave default alone */ } if (valuestrings.size() == 1 && nvals > 0) { vals.resize (1); vals.resize (nvals, vals[0]); } return list.size() ? (int) valuestrings.size() : 0; } /// C++ functor wrapper class for using strhash for unordered_map or /// unordered_set. The way this is used, in conjunction with /// StringEqual, to build an efficient hash map for char*'s or /// std::string's is as follows: /// \code /// unordered_map /// \endcode class StringHash { public: size_t operator() (string_view s) const { return (size_t)Strutil::strhash(s); } }; /// C++ functor class for comparing two char*'s or std::string's for /// equality of their strings. class StringEqual { public: bool operator() (const char *a, const char *b) const { return strcmp (a, b) == 0; } bool operator() (string_view a, string_view b) const { return a == b; } }; #ifdef _WIN32 /// Conversion functions between UTF-8 and UTF-16 for windows. /// /// For historical reasons, the standard encoding for strings on windows is /// UTF-16, whereas the unix world seems to have settled on UTF-8. These two /// encodings can be stored in std::string and std::wstring respectively, with /// the caveat that they're both variable-width encodings, so not all the /// standard string methods will make sense (for example std::string::size() /// won't return the number of glyphs in a UTF-8 string, unless it happens to /// be made up of only the 7-bit ASCII subset). /// /// The standard windows API functions usually have two versions, a UTF-16 /// version with a 'W' suffix (using wchar_t* strings), and an ANSI version /// with a 'A' suffix (using char* strings) which uses the current windows /// code page to define the encoding. (To make matters more confusing there is /// also a further "TCHAR" version which is #defined to the UTF-16 or ANSI /// version, depending on whether UNICODE is defined during compilation. /// This is meant to make it possible to support compiling libraries in /// either unicode or ansi mode from the same codebase.) /// /// Using std::string as the string container (as in OIIO) implies that we /// can't use UTF-16. It also means we need a variable-width encoding to /// represent characters in non-Latin alphabets in an unambiguous way; the /// obvious candidate is UTF-8. File paths in OIIO are considered to be /// represented in UTF-8, and must be converted to UTF-16 before passing to /// windows API file opening functions. /// On the other hand, the encoding used for the ANSI versions of the windows /// API is the current windows code page. This is more compatible with the /// default setup of the standard windows command prompt, and may be more /// appropriate for error messages. // Conversion to wide char // std::wstring OIIO_API utf8_to_utf16 (string_view utf8str); // Conversion from wide char // std::string OIIO_API utf16_to_utf8(const std::wstring& utf16str); #endif /// Safe C string copy. Basically strncpy but ensuring that there's a /// terminating 0 character at the end of the resulting string. OIIO_API char * safe_strcpy (char *dst, const char *src, size_t size); inline char * safe_strcpy (char *dst, const std::string &src, size_t size) { return safe_strcpy (dst, src.length() ? src.c_str() : NULL, size); } /// Modify str to trim any whitespace (space, tab, linefeed, cr) from the /// front. void OIIO_API skip_whitespace (string_view &str); /// If str's first character is c (or first non-whitespace char is c, if /// skip_whitespace is true), return true and additionally modify str to /// skip over that first character if eat is also true. Otherwise, if str /// does not begin with character c, return false and don't modify str. bool OIIO_API parse_char (string_view &str, char c, bool skip_whitespace = true, bool eat=true); /// Modify str to trim all characters up to (but not including) the first /// occurrence of c, and return true if c was found or false if the whole /// string was trimmed without ever finding c. But if eat is false, then /// don't modify str, just return true if any c is found, false if no c /// is found. bool OIIO_API parse_until_char (string_view &str, char c, bool eat=true); /// If str's first non-whitespace characters are the prefix, return true and /// additionally modify str to skip over that prefix if eat is also true. /// Otherwise, if str doesn't start with optional whitespace and the prefix, /// return false and don't modify str. bool OIIO_API parse_prefix (string_view &str, string_view prefix, bool eat=true); /// If str's first non-whitespace characters form a valid integer, return /// true, place the integer's value in val, and additionally modify str to /// skip over the parsed integer if eat is also true. Otherwise, if no /// integer is found at the beginning of str, return false and don't modify /// val or str. bool OIIO_API parse_int (string_view &str, int &val, bool eat=true); /// If str's first non-whitespace characters form a valid float, return /// true, place the float's value in val, and additionally modify str to /// skip over the parsed float if eat is also true. Otherwise, if no float /// is found at the beginning of str, return false and don't modify val or /// str. bool OIIO_API parse_float (string_view &str, float &val, bool eat=true); enum QuoteBehavior { DeleteQuotes, KeepQuotes }; /// If str's first non-whitespace characters form a valid string (either a /// single word separated by whitespace or anything inside a double-quoted /// string (""), return true, place the string's value (not including /// surrounding double quotes) in val, and additionally modify str to skip /// over the parsed string if eat is also true. Otherwise, if no string is /// found at the beginning of str, return false and don't modify val or str. /// If keep_quotes is true, the surrounding double quotes (if present) /// will be kept in val. bool OIIO_API parse_string (string_view &str, string_view &val, bool eat=true, QuoteBehavior keep_quotes=DeleteQuotes); /// Return the first "word" (set of contiguous alphabetical characters) in /// str, and additionally modify str to skip over the parsed word if eat is /// also true. Otherwise, if no word is found at the beginning of str, /// return an empty string_view and don't modify str. string_view OIIO_API parse_word (string_view &str, bool eat=true); /// If str's first non-whitespace characters form a valid C-like identifier, /// return the identifier, and additionally modify str to skip over the /// parsed identifier if eat is also true. Otherwise, if no identifier is /// found at the beginning of str, return an empty string_view and don't /// modify str. string_view OIIO_API parse_identifier (string_view &str, bool eat=true); /// If str's first non-whitespace characters form a valid C-like identifier, /// return the identifier, and additionally modify str to skip over the /// parsed identifier if eat is also true. Otherwise, if no identifier is /// found at the beginning of str, return an empty string_view and don't /// modify str. The 'allowed' parameter may specify a additional characters /// accepted that would not ordinarily be allowed in C identifiers, for /// example, parse_identifier (blah, "$:") would allow "identifiers" /// containing dollar signs and colons as well as the usual alphanumeric and /// underscore characters. string_view OIIO_API parse_identifier (string_view &str, string_view allowed, bool eat); /// If the C-like identifier at the head of str exactly matches id, /// return true, and also advance str if eat is true. If it is not a match /// for id, return false and do not alter str. bool OIIO_API parse_identifier_if (string_view &str, string_view id, bool eat=true); /// Return the characters until any character in sep is found, storing it in /// str, and additionally modify str to skip over the parsed section if eat /// is also true. Otherwise, if no word is found at the beginning of str, /// return an empty string_view and don't modify str. string_view OIIO_API parse_until (string_view &str, string_view sep=" \t\r\n", bool eat=true); /// Assuming the string str starts with either '(', '[', or '{', return the /// head, up to and including the corresponding closing character (')', ']', /// or '}', respectively), recognizing nesting structures. For example, /// parse_nested("(a(b)c)d") should return "(a(b)c)", NOT "(a(b)". Return an /// empty string if str doesn't start with one of those characters, or /// doesn't contain a correctly matching nested pair. If eat==true, str will /// be modified to trim off the part of the string that is returned as the /// match. string_view OIIO_API parse_nested (string_view &str, bool eat=true); /// Converts utf-8 string to vector of unicode codepoints. This function /// will not stop on invalid sequences. It will let through some invalid /// utf-8 sequences like: 0xfdd0-0xfdef, 0x??fffe/0x??ffff. It does not /// support 5-6 bytes long utf-8 sequences. Will skip trailing character if /// there are not enough bytes for decoding a codepoint. /// /// N.B. Following should probably return u32string instead of taking /// vector, but C++11 support is not yet stabilized across compilers. /// We will eventually add that and deprecate this one, after everybody /// is caught up to C++11. void OIIO_API utf8_to_unicode (string_view str, std::vector &uvec); /// Encode the string in base64. /// https://en.wikipedia.org/wiki/Base64 std::string OIIO_API base64_encode (string_view str); } // namespace Strutil OIIO_NAMESPACE_END #endif // OPENIMAGEIO_STRUTIL_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/pugixml.hpp0000644000175000017500000012670513151711064023044 0ustar mfvmfv/** * pugixml parser - version 1.2 * -------------------------------------------------------- * Copyright (C) 2006-2012, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) * Report bugs and download new versions at http://pugixml.org/ * * This library is distributed under the MIT License. See notice at the end * of this file. * * This work is based on the pugxml parser, which is: * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) */ #ifndef PUGIXML_VERSION // Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons # define PUGIXML_VERSION 120 #endif // Include user configuration file (this can define various configuration macros) #include "pugiconfig.hpp" #ifndef HEADER_PUGIXML_HPP #define HEADER_PUGIXML_HPP #include "oiioversion.h" #define USING_OIIO_PUGI 1 // Include stddef.h for size_t and ptrdiff_t #include // Include exception header for XPath #if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS) # include #endif // Include STL headers #ifndef PUGIXML_NO_STL # include # include # include #endif // Macro for deprecated features #ifndef PUGIXML_DEPRECATED # if defined(__GNUC__) # define PUGIXML_DEPRECATED __attribute__((deprecated)) # elif defined(_MSC_VER) && _MSC_VER >= 1300 # define PUGIXML_DEPRECATED __declspec(deprecated) # else # define PUGIXML_DEPRECATED # endif #endif // If no API is defined, assume default #ifndef PUGIXML_API # define PUGIXML_API #endif // If no API for classes is defined, assume default #ifndef PUGIXML_CLASS # define PUGIXML_CLASS PUGIXML_API #endif // If no API for functions is defined, assume default #ifndef PUGIXML_FUNCTION # define PUGIXML_FUNCTION PUGIXML_API #endif // Character interface macros #ifdef PUGIXML_WCHAR_MODE # define PUGIXML_TEXT(t) L ## t # define PUGIXML_CHAR wchar_t #else # define PUGIXML_TEXT(t) t # define PUGIXML_CHAR char #endif OIIO_NAMESPACE_BEGIN namespace pugi { // Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE typedef PUGIXML_CHAR char_t; #ifndef PUGIXML_NO_STL // String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE typedef std::basic_string, std::allocator > string_t; #endif } // The PugiXML namespace namespace pugi { // Tree node types enum xml_node_type { node_null, // Empty (null) node handle node_document, // A document tree's absolute root node_element, // Element tag, i.e. '' node_pcdata, // Plain character data, i.e. 'text' node_cdata, // Character data, i.e. '' node_comment, // Comment tag, i.e. '' node_pi, // Processing instruction, i.e. '' node_declaration, // Document declaration, i.e. '' node_doctype // Document type declaration, i.e. '' }; // Parsing options // Minimal parsing mode (equivalent to turning all other flags off). // Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed. const unsigned int parse_minimal = 0x0000; // This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default. const unsigned int parse_pi = 0x0001; // This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default. const unsigned int parse_comments = 0x0002; // This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default. const unsigned int parse_cdata = 0x0004; // This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree. // This flag is off by default; turning it on usually results in slower parsing and more memory consumption. const unsigned int parse_ws_pcdata = 0x0008; // This flag determines if character and entity references are expanded during parsing. This flag is on by default. const unsigned int parse_escapes = 0x0010; // This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default. const unsigned int parse_eol = 0x0020; // This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default. const unsigned int parse_wconv_attribute = 0x0040; // This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default. const unsigned int parse_wnorm_attribute = 0x0080; // This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default. const unsigned int parse_declaration = 0x0100; // This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default. const unsigned int parse_doctype = 0x0200; // This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only // of whitespace is added to the DOM tree. // This flag is off by default; turning it on may result in slower parsing and more memory consumption. const unsigned int parse_ws_pcdata_single = 0x0400; // The default parsing mode. // Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded, // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol; // The full parsing mode. // Nodes of all types are added to the DOM tree, character/reference entities are expanded, // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype; // These flags determine the encoding of input data for XML document enum xml_encoding { encoding_auto, // Auto-detect input encoding using BOM or < / class xml_object_range { public: typedef It const_iterator; xml_object_range(It b, It e): _begin(b), _end(e) { } It begin() const { return _begin; } It end() const { return _end; } private: It _begin, _end; }; // Writer interface for node printing (see xml_node::print) class PUGIXML_CLASS xml_writer { public: virtual ~xml_writer() {} // Write memory chunk into stream/file/whatever virtual void write(const void* data, size_t size) = 0; }; // xml_writer implementation for FILE* class PUGIXML_CLASS xml_writer_file: public xml_writer { public: // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio xml_writer_file(void* file); virtual void write(const void* data, size_t size); private: void* file; }; #ifndef PUGIXML_NO_STL // xml_writer implementation for streams class PUGIXML_CLASS xml_writer_stream: public xml_writer { public: // Construct writer from an output stream object xml_writer_stream(std::basic_ostream >& stream); xml_writer_stream(std::basic_ostream >& stream); virtual void write(const void* data, size_t size); private: std::basic_ostream >* narrow_stream; std::basic_ostream >* wide_stream; }; #endif // A light-weight handle for manipulating attributes in DOM tree class PUGIXML_CLASS xml_attribute { friend class xml_attribute_iterator; friend class xml_node; private: xml_attribute_struct* _attr; typedef void (*unspecified_bool_type)(xml_attribute***); public: // Default constructor. Constructs an empty attribute. xml_attribute(); // Constructs attribute from internal pointer explicit xml_attribute(xml_attribute_struct* attr); // Safe bool conversion operator operator unspecified_bool_type() const; // Borland C++ workaround bool operator!() const; // Comparison operators (compares wrapped attribute pointers) bool operator==(const xml_attribute& r) const; bool operator!=(const xml_attribute& r) const; bool operator<(const xml_attribute& r) const; bool operator>(const xml_attribute& r) const; bool operator<=(const xml_attribute& r) const; bool operator>=(const xml_attribute& r) const; // Check if attribute is empty bool empty() const; // Get attribute name/value, or "" if attribute is empty const char_t* name() const; const char_t* value() const; // Get attribute value, or the default value if attribute is empty const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty int as_int(int def = 0) const; unsigned int as_uint(unsigned int def = 0) const; double as_double(double def = 0) const; float as_float(float def = 0) const; // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty bool as_bool(bool def = false) const; // Set attribute name/value (returns false if attribute is empty or there is not enough memory) bool set_name(const char_t* rhs); bool set_value(const char_t* rhs); // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") bool set_value(int rhs); bool set_value(unsigned int rhs); bool set_value(double rhs); bool set_value(bool rhs); // Set attribute value (equivalent to set_value without error checking) xml_attribute& operator=(const char_t* rhs); xml_attribute& operator=(int rhs); xml_attribute& operator=(unsigned int rhs); xml_attribute& operator=(double rhs); xml_attribute& operator=(bool rhs); // Get next/previous attribute in the attribute list of the parent node xml_attribute next_attribute() const; xml_attribute previous_attribute() const; // Get hash value (unique for handles to the same object) size_t hash_value() const; // Get internal pointer xml_attribute_struct* internal_object() const; }; #ifdef __BORLANDC__ // Borland C++ workaround bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs); bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs); #endif // A light-weight handle for manipulating nodes in DOM tree class PUGIXML_CLASS xml_node { friend class xml_attribute_iterator; friend class xml_node_iterator; friend class xml_named_node_iterator; protected: xml_node_struct* _root; typedef void (*unspecified_bool_type)(xml_node***); public: // Default constructor. Constructs an empty node. xml_node(); // Constructs node from internal pointer explicit xml_node(xml_node_struct* p); // Safe bool conversion operator operator unspecified_bool_type() const; // Borland C++ workaround bool operator!() const; // Comparison operators (compares wrapped node pointers) bool operator==(const xml_node& r) const; bool operator!=(const xml_node& r) const; bool operator<(const xml_node& r) const; bool operator>(const xml_node& r) const; bool operator<=(const xml_node& r) const; bool operator>=(const xml_node& r) const; // Check if node is empty. bool empty() const; // Get node type xml_node_type type() const; // Get node name/value, or "" if node is empty or it has no name/value const char_t* name() const; const char_t* value() const; // Get attribute list xml_attribute first_attribute() const; xml_attribute last_attribute() const; // Get children list xml_node first_child() const; xml_node last_child() const; // Get next/previous sibling in the children list of the parent node xml_node next_sibling() const; xml_node previous_sibling() const; // Get parent node xml_node parent() const; // Get root of DOM tree this node belongs to xml_node root() const; // Get text object for the current node xml_text text() const; // Get child, attribute or next/previous sibling with the specified name xml_node child(const char_t* name) const; xml_attribute attribute(const char_t* name) const; xml_node next_sibling(const char_t* name) const; xml_node previous_sibling(const char_t* name) const; // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA const char_t* child_value() const; // Get child value of child with specified name. Equivalent to child(name).child_value(). const char_t* child_value(const char_t* name) const; // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value) bool set_name(const char_t* rhs); bool set_value(const char_t* rhs); // Add attribute with specified name. Returns added attribute, or empty attribute on errors. xml_attribute append_attribute(const char_t* name); xml_attribute prepend_attribute(const char_t* name); xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr); xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr); // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors. xml_attribute append_copy(const xml_attribute& proto); xml_attribute prepend_copy(const xml_attribute& proto); xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr); xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr); // Add child node with specified type. Returns added node, or empty node on errors. xml_node append_child(xml_node_type type = node_element); xml_node prepend_child(xml_node_type type = node_element); xml_node insert_child_after(xml_node_type type, const xml_node& node); xml_node insert_child_before(xml_node_type type, const xml_node& node); // Add child element with specified name. Returns added node, or empty node on errors. xml_node append_child(const char_t* name); xml_node prepend_child(const char_t* name); xml_node insert_child_after(const char_t* name, const xml_node& node); xml_node insert_child_before(const char_t* name, const xml_node& node); // Add a copy of the specified node as a child. Returns added node, or empty node on errors. xml_node append_copy(const xml_node& proto); xml_node prepend_copy(const xml_node& proto); xml_node insert_copy_after(const xml_node& proto, const xml_node& node); xml_node insert_copy_before(const xml_node& proto, const xml_node& node); // Remove specified attribute bool remove_attribute(const xml_attribute& a); bool remove_attribute(const char_t* name); // Remove specified child bool remove_child(const xml_node& n); bool remove_child(const char_t* name); // Find attribute using predicate. Returns first attribute for which predicate returned true. template xml_attribute find_attribute(Predicate pred) const { if (!_root) return xml_attribute(); for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute()) if (pred(attrib)) return attrib; return xml_attribute(); } // Find child node using predicate. Returns first child for which predicate returned true. template xml_node find_child(Predicate pred) const { if (!_root) return xml_node(); for (xml_node node = first_child(); node; node = node.next_sibling()) if (pred(node)) return node; return xml_node(); } // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true. template xml_node find_node(Predicate pred) const { if (!_root) return xml_node(); xml_node cur = first_child(); while (cur._root && cur._root != _root) { if (pred(cur)) return cur; if (cur.first_child()) cur = cur.first_child(); else if (cur.next_sibling()) cur = cur.next_sibling(); else { while (!cur.next_sibling() && cur._root != _root) cur = cur.parent(); if (cur._root != _root) cur = cur.next_sibling(); } } return xml_node(); } // Find child node by attribute name/value xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const; xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const; #ifndef PUGIXML_NO_STL // Get the absolute node path from root as a text string. string_t path(char_t delimiter = '/') const; #endif // Search for a node by path consisting of node names and . or .. elements. xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const; // Recursively traverse subtree with xml_tree_walker bool traverse(xml_tree_walker& walker); #ifndef PUGIXML_NO_XPATH // Select single node by evaluating XPath query. Returns first node from the resulting node set. xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const; xpath_node select_single_node(const xpath_query& query) const; // Select node set by evaluating XPath query xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const; xpath_node_set select_nodes(const xpath_query& query) const; #endif // Print subtree using a writer object void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; #ifndef PUGIXML_NO_STL // Print subtree to stream void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const; #endif // Child nodes iterators typedef xml_node_iterator iterator; iterator begin() const; iterator end() const; // Attribute iterators typedef xml_attribute_iterator attribute_iterator; attribute_iterator attributes_begin() const; attribute_iterator attributes_end() const; // Range-based for support xml_object_range children() const; xml_object_range children(const char_t* name) const; xml_object_range attributes() const; // Get node offset in parsed file/string (in char_t units) for debugging purposes ptrdiff_t offset_debug() const; // Get hash value (unique for handles to the same object) size_t hash_value() const; // Get internal pointer xml_node_struct* internal_object() const; }; #ifdef __BORLANDC__ // Borland C++ workaround bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs); bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs); #endif // A helper for working with text inside PCDATA nodes class PUGIXML_CLASS xml_text { friend class xml_node; xml_node_struct* _root; typedef void (*unspecified_bool_type)(xml_text***); explicit xml_text(xml_node_struct* root); xml_node_struct* _data_new(); xml_node_struct* _data() const; public: // Default constructor. Constructs an empty object. xml_text(); // Safe bool conversion operator operator unspecified_bool_type() const; // Borland C++ workaround bool operator!() const; // Check if text object is empty bool empty() const; // Get text, or "" if object is empty const char_t* get() const; // Get text, or the default value if object is empty const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; // Get text as a number, or the default value if conversion did not succeed or object is empty int as_int(int def = 0) const; unsigned int as_uint(unsigned int def = 0) const; double as_double(double def = 0) const; float as_float(float def = 0) const; // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty bool as_bool(bool def = false) const; // Set text (returns false if object is empty or there is not enough memory) bool set(const char_t* rhs); // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") bool set(int rhs); bool set(unsigned int rhs); bool set(double rhs); bool set(bool rhs); // Set text (equivalent to set without error checking) xml_text& operator=(const char_t* rhs); xml_text& operator=(int rhs); xml_text& operator=(unsigned int rhs); xml_text& operator=(double rhs); xml_text& operator=(bool rhs); // Get the data node (node_pcdata or node_cdata) for this object xml_node data() const; }; #ifdef __BORLANDC__ // Borland C++ workaround bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs); bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs); #endif // Child node iterator (a bidirectional iterator over a collection of xml_node) class PUGIXML_CLASS xml_node_iterator { friend class xml_node; private: mutable xml_node _wrap; xml_node _parent; xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent); public: // Iterator traits typedef ptrdiff_t difference_type; typedef xml_node value_type; typedef xml_node* pointer; typedef xml_node& reference; #ifndef PUGIXML_NO_STL typedef std::bidirectional_iterator_tag iterator_category; #endif // Default constructor xml_node_iterator(); // Construct an iterator which points to the specified node xml_node_iterator(const xml_node& node); // Iterator operators bool operator==(const xml_node_iterator& rhs) const; bool operator!=(const xml_node_iterator& rhs) const; xml_node& operator*() const; xml_node* operator->() const; const xml_node_iterator& operator++(); xml_node_iterator operator++(int); const xml_node_iterator& operator--(); xml_node_iterator operator--(int); }; // Attribute iterator (a bidirectional iterator over a collection of xml_attribute) class PUGIXML_CLASS xml_attribute_iterator { friend class xml_node; private: mutable xml_attribute _wrap; xml_node _parent; xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent); public: // Iterator traits typedef ptrdiff_t difference_type; typedef xml_attribute value_type; typedef xml_attribute* pointer; typedef xml_attribute& reference; #ifndef PUGIXML_NO_STL typedef std::bidirectional_iterator_tag iterator_category; #endif // Default constructor xml_attribute_iterator(); // Construct an iterator which points to the specified attribute xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent); // Iterator operators bool operator==(const xml_attribute_iterator& rhs) const; bool operator!=(const xml_attribute_iterator& rhs) const; xml_attribute& operator*() const; xml_attribute* operator->() const; const xml_attribute_iterator& operator++(); xml_attribute_iterator operator++(int); const xml_attribute_iterator& operator--(); xml_attribute_iterator operator--(int); }; // Named node range helper class xml_named_node_iterator { public: // Iterator traits typedef ptrdiff_t difference_type; typedef xml_node value_type; typedef xml_node* pointer; typedef xml_node& reference; #ifndef PUGIXML_NO_STL typedef std::forward_iterator_tag iterator_category; #endif // Default constructor xml_named_node_iterator(); // Construct an iterator which points to the specified node xml_named_node_iterator(const xml_node& node, const char_t* name); // Iterator operators bool operator==(const xml_named_node_iterator& rhs) const; bool operator!=(const xml_named_node_iterator& rhs) const; xml_node& operator*() const; xml_node* operator->() const; const xml_named_node_iterator& operator++(); xml_named_node_iterator operator++(int); private: mutable xml_node _node; const char_t* _name; }; // Abstract tree walker class (see xml_node::traverse) class PUGIXML_CLASS xml_tree_walker { friend class xml_node; private: int _depth; protected: // Get current traversal depth int depth() const; public: xml_tree_walker(); virtual ~xml_tree_walker(); // Callback that is called when traversal begins virtual bool begin(xml_node& node); // Callback that is called for each node traversed virtual bool for_each(xml_node& node) = 0; // Callback that is called when traversal ends virtual bool end(xml_node& node); }; // Parsing status, returned as part of xml_parse_result object enum xml_parse_status { status_ok = 0, // No error status_file_not_found, // File was not found during load_file() status_io_error, // Error reading from file/stream status_out_of_memory, // Could not allocate memory status_internal_error, // Internal error occurred status_unrecognized_tag, // Parser could not determine tag type status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction status_bad_comment, // Parsing error occurred while parsing comment status_bad_cdata, // Parsing error occurred while parsing CDATA section status_bad_doctype, // Parsing error occurred while parsing document type declaration status_bad_pcdata, // Parsing error occurred while parsing PCDATA section status_bad_start_element, // Parsing error occurred while parsing start element tag status_bad_attribute, // Parsing error occurred while parsing element attribute status_bad_end_element, // Parsing error occurred while parsing end element tag status_end_element_mismatch // There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag) }; // Parsing result struct PUGIXML_CLASS xml_parse_result { // Parsing status (see xml_parse_status) xml_parse_status status; // Last parsed offset (in char_t units from start of input data) ptrdiff_t offset; // Source document encoding xml_encoding encoding; // Default constructor, initializes object to failed state xml_parse_result(); // Cast to bool operator operator bool() const; // Get error description const char* description() const; }; // Document class (DOM tree root) class PUGIXML_CLASS xml_document: public xml_node { private: char_t* _buffer; char _memory[192]; // Non-copyable semantics xml_document(const xml_document&); const xml_document& operator=(const xml_document&); void create(); void destroy(); xml_parse_result load_buffer_impl(void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own); public: // Default constructor, makes empty document xml_document(); // Destructor, invalidates all node/attribute handles to this document ~xml_document(); // Removes all nodes, leaving the empty document void reset(); // Removes all nodes, then copies the entire contents of the specified document void reset(const xml_document& proto); #ifndef PUGIXML_NO_STL // Load document from stream. xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default); #endif // Load document from zero-terminated string. No encoding conversions are applied. xml_parse_result load(const char_t* contents, unsigned int options = parse_default); // Load document from file xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns. xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). // You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed. xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). // You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore). xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details). void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; #ifndef PUGIXML_NO_STL // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details). void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const; #endif // Save XML to file bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; // Get document element xml_node document_element() const; }; #ifndef PUGIXML_NO_XPATH // XPath query return type enum xpath_value_type { xpath_type_none, // Unknown type (query failed to compile) xpath_type_node_set, // Node set (xpath_node_set) xpath_type_number, // Number xpath_type_string, // String xpath_type_boolean // Boolean }; // XPath parsing result struct PUGIXML_CLASS xpath_parse_result { // Error message (0 if no error) const char* error; // Last parsed offset (in char_t units from string start) ptrdiff_t offset; // Default constructor, initializes object to failed state xpath_parse_result(); // Cast to bool operator operator bool() const; // Get error description const char* description() const; }; // A single XPath variable class PUGIXML_CLASS xpath_variable { friend class xpath_variable_set; protected: xpath_value_type _type; xpath_variable* _next; xpath_variable(); // Non-copyable semantics xpath_variable(const xpath_variable&); xpath_variable& operator=(const xpath_variable&); public: // Get variable name const char_t* name() const; // Get variable type xpath_value_type type() const; // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error bool get_boolean() const; double get_number() const; const char_t* get_string() const; const xpath_node_set& get_node_set() const; // Set variable value; no type conversion is performed, false is returned on type mismatch error bool set(bool value); bool set(double value); bool set(const char_t* value); bool set(const xpath_node_set& value); }; // A set of XPath variables class PUGIXML_CLASS xpath_variable_set { private: xpath_variable* _data[64]; // Non-copyable semantics xpath_variable_set(const xpath_variable_set&); xpath_variable_set& operator=(const xpath_variable_set&); xpath_variable* find(const char_t* name) const; public: // Default constructor/destructor xpath_variable_set(); ~xpath_variable_set(); // Add a new variable or get the existing one, if the types match xpath_variable* add(const char_t* name, xpath_value_type type); // Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch bool set(const char_t* name, bool value); bool set(const char_t* name, double value); bool set(const char_t* name, const char_t* value); bool set(const char_t* name, const xpath_node_set& value); // Get existing variable by name xpath_variable* get(const char_t* name); const xpath_variable* get(const char_t* name) const; }; // A compiled XPath query object class PUGIXML_CLASS xpath_query { private: void* _impl; xpath_parse_result _result; typedef void (*unspecified_bool_type)(xpath_query***); // Non-copyable semantics xpath_query(const xpath_query&); xpath_query& operator=(const xpath_query&); public: // Construct a compiled object from XPath expression. // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0); // Destructor ~xpath_query(); // Get query expression return type xpath_value_type return_type() const; // Evaluate expression as boolean value in the specified context; performs type conversion if necessary. // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. bool evaluate_boolean(const xpath_node& n) const; // Evaluate expression as double value in the specified context; performs type conversion if necessary. // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. double evaluate_number(const xpath_node& n) const; #ifndef PUGIXML_NO_STL // Evaluate expression as string value in the specified context; performs type conversion if necessary. // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. string_t evaluate_string(const xpath_node& n) const; #endif // Evaluate expression as string value in the specified context; performs type conversion if necessary. // At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero). // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. // If PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead. size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const; // Evaluate expression as node set in the specified context. // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead. xpath_node_set evaluate_node_set(const xpath_node& n) const; // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode) const xpath_parse_result& result() const; // Safe bool conversion operator operator unspecified_bool_type() const; // Borland C++ workaround bool operator!() const; }; #ifndef PUGIXML_NO_EXCEPTIONS // XPath exception class class PUGIXML_CLASS xpath_exception: public std::exception { private: xpath_parse_result _result; public: // Construct exception from parse result explicit xpath_exception(const xpath_parse_result& result); // Get error message virtual const char* what() const throw(); // Get parse result const xpath_parse_result& result() const; }; #endif // XPath node class (either xml_node or xml_attribute) class PUGIXML_CLASS xpath_node { private: xml_node _node; xml_attribute _attribute; typedef void (*unspecified_bool_type)(xpath_node***); public: // Default constructor; constructs empty XPath node xpath_node(); // Construct XPath node from XML node/attribute xpath_node(const xml_node& node); xpath_node(const xml_attribute& attribute, const xml_node& parent); // Get node/attribute, if any xml_node node() const; xml_attribute attribute() const; // Get parent of contained node/attribute xml_node parent() const; // Safe bool conversion operator operator unspecified_bool_type() const; // Borland C++ workaround bool operator!() const; // Comparison operators bool operator==(const xpath_node& n) const; bool operator!=(const xpath_node& n) const; }; #ifdef __BORLANDC__ // Borland C++ workaround bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs); bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs); #endif // A fixed-size collection of XPath nodes class PUGIXML_CLASS xpath_node_set { public: // Collection type enum type_t { type_unsorted, // Not ordered type_sorted, // Sorted by document order (ascending) type_sorted_reverse // Sorted by document order (descending) }; // Constant iterator type typedef const xpath_node* const_iterator; // Default constructor. Constructs empty set. xpath_node_set(); // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted); // Destructor ~xpath_node_set(); // Copy constructor/assignment operator xpath_node_set(const xpath_node_set& ns); xpath_node_set& operator=(const xpath_node_set& ns); // Get collection type type_t type() const; // Get collection size size_t size() const; // Indexing operator const xpath_node& operator[](size_t index) const; // Collection iterators const_iterator begin() const; const_iterator end() const; // Sort the collection in ascending/descending order by document order void sort(bool reverse = false); // Get first node in the collection by document order xpath_node first() const; // Check if collection is empty bool empty() const; private: type_t _type; xpath_node _storage; xpath_node* _begin; xpath_node* _end; void _assign(const_iterator begin, const_iterator end); }; #endif #ifndef PUGIXML_NO_STL // Convert wide string to UTF8 std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const wchar_t* str); std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const std::basic_string, std::allocator >& str); // Convert UTF8 to wide string std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const char* str); std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const std::basic_string, std::allocator >& str); #endif // Memory allocation function interface; returns pointer to allocated memory or NULL on failure typedef void* (*allocation_function)(size_t size); // Memory deallocation function interface typedef void (*deallocation_function)(void* ptr); // Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions. void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate); // Get current memory management functions allocation_function PUGIXML_FUNCTION get_memory_allocation_function(); deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function(); } OIIO_NAMESPACE_END #if !defined(PUGIXML_NO_STL) && ((defined(_MSC_VER) && _MSC_VER < 1400) || defined(__ICC)) namespace std { // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const OpenImageIO::pugi::xml_node_iterator&); std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const OpenImageIO::pugi::xml_attribute_iterator&); std::forward_iterator_tag PUGIXML_FUNCTION _Iter_cat(const OpenImageIO::pugi::xml_named_node_iterator&); } #endif #if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) namespace std { // Workarounds for (non-standard) iterator category detection std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const OpenImageIO::pugi::xml_node_iterator&); std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const OpenImageIO::pugi::xml_attribute_iterator&); std::forward_iterator_tag PUGIXML_FUNCTION __iterator_category(const OpenImageIO::pugi::xml_named_node_iterator&); } #endif #endif /** * Copyright (c) 2006-2012 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/unordered_map_concurrent.h0000644000175000017500000003613513151711064026102 0ustar mfvmfv/* Copyright 2012 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #pragma once #ifndef OPENIMAGEIO_UNORDERED_MAP_CONCURRENT_H #define OPENIMAGEIO_UNORDERED_MAP_CONCURRENT_H #include #include #include OIIO_NAMESPACE_BEGIN /// unordered_map_concurrent provides an unordered_map replacement that /// is optimized for concurrent access. Its principle of operation is /// similar to Java's ConcurrentHashMap. /// /// With naive use of an unordered_map, multiple threads would have to /// lock a mutex of some kind to control access to the map, either with /// a unique full lock, or with a reader/writer lock. But as the number /// of threads contending for this shared resource rises, they end up /// locking each other out and the map becomes a thread bottleneck. /// /// unordered_map_concurrent solves this problem by internally splitting /// the hash map into several disjoint bins, each of which is a standard /// unordered_map. For any given map item, the hash of its key /// determines both the bin as well as its hashing within the bin (i.e., /// we break a big hash map into lots of little hash maps, /// deterministically determined by the key). Thus, we should expect /// map entries to be spread more or less evenly among the bins. There /// is no mutex that locks the map as a whole; instead, each bin is /// locked individually. If the number of bins is larger than the /// typical number of threads, most of the time two (or more) threads /// accessing the map simultaneously will not be accessing the same bin, /// and therefore will not be contending for the same lock. /// /// unordered_map_concurrent provides an iterator which points to an /// entry in the map and also knows which bin it is in and implicitly /// holds a lock on the bin. When the iterator is destroyed, the lock /// on that bin is released. When the iterator is incremented in such a /// way that it transitions from the last entry of its current bin to /// the first entry of the next bin, it will also release its current /// lock and obtain a lock on the next bin. /// template, class PRED=std::equal_to, size_t BINS=16, class BINMAP=unordered_map > class unordered_map_concurrent { public: typedef BINMAP BinMap_t; typedef typename BINMAP::iterator BinMap_iterator_t; public: unordered_map_concurrent () { m_size = 0; } ~unordered_map_concurrent () { // for (size_t i = 0; i < BINS; ++i) // std::cout << "Bin " << i << ": " << m_bins[i].map.size() << "\n"; } /// An unordered_map_concurrent::iterator points to a specific entry /// in the umc, and holds a lock to the bin the entry is in. class iterator { public: friend class unordered_map_concurrent; public: /// Construct an unordered_map_concurrent iterator that points /// to nothing. iterator (unordered_map_concurrent *umc = NULL) : m_umc(umc), m_bin(-1), m_locked(false) { } /// Copy constructor of an unordered_map_concurrent iterator /// transfers the lock (if held) to this. Caveat: the copied /// iterator no longer holds the lock! iterator (const iterator &src) { m_umc = src.m_umc; m_bin = src.m_bin; m_biniterator = src.m_biniterator; m_locked = src.m_locked; // assignment transfers lock ownership *(const_cast(&src.m_locked)) = false; } /// Destroying an unordered_map_concurrent iterator releases any /// bin locks it held. ~iterator () { clear(); } /// Totally invalidate this iterator -- point it to nothing /// (releasing any locks it may have had). void clear () { if (m_umc) { unbin (); m_umc = NULL; } } // Dereferencing returns a reference to the hash table entry the // iterator refers to. typename BinMap_t::value_type & operator* () { return *m_biniterator; } /// Dereferencing returns a reference to the hash table entry the /// iterator refers to. typename BinMap_t::value_type * operator-> () { return &(*m_biniterator); } /// Treating an iterator as a bool yields true if it points to a /// valid element of one of the bins of the map, false if it's /// equivalent to the end() iterator. operator bool() { return m_umc && m_bin >= 0 && m_biniterator != m_umc->m_bins[m_bin].map.end(); } /// Iterator assignment transfers ownership of any bin locks /// held by the operand. iterator& operator= (const iterator &src) { unbin(); m_umc = src.m_umc; m_bin = src.m_bin; m_biniterator = src.m_biniterator; m_locked = src.m_locked; // assignment transfers lock ownership *(const_cast(&src.m_locked)) = false; return *this; } bool operator== (const iterator &other) const { if (m_umc != other.m_umc) return false; if (m_bin == -1 && other.m_bin == -1) return true; return m_bin == other.m_bin && m_biniterator == other.m_biniterator; } bool operator!= (const iterator &other) { return ! (*this == other); } /// Increment to the next entry in the map. If we finish the /// bin we're in, move on to the next bin (releasing our lock on /// the old bin and acquiring a lock on the new bin). If we /// finish the last bin of the map, return the end() iterator. void operator++ () { DASSERT (m_umc); DASSERT (m_bin >= 0); ++m_biniterator; while (m_biniterator == m_umc->m_bins[m_bin].map.end()) { if (m_bin == BINS-1) { // ran off the end unbin(); return; } rebin (m_bin+1); } } void operator++ (int) { ++(*this); } /// Lock the bin we point to, if not already locked. void lock () { if (m_bin >= 0 && !m_locked) { m_umc->m_bins[m_bin].lock(); m_locked = true; } } /// Unlock the bin we point to, if locked. void unlock () { if (m_bin >= 0 && m_locked) { m_umc->m_bins[m_bin].unlock(); m_locked = false; } } /// Without changing the lock status (i.e., the caller already /// holds the lock on the iterator's bin), increment to the next /// element within the bin. Return true if it's pointing to a /// valid element afterwards, false if it ran off the end of the /// bin contents. bool incr_no_lock () { ++m_biniterator; return (m_biniterator != m_umc->m_bins[m_bin].map.end()); } private: // No longer refer to a particular bin, release lock on the bin // it had (if any). void unbin () { if (m_bin >= 0) { if (m_locked) unlock (); m_bin = -1; } } // Point this iterator to a different bin, releasing locks on // the bin it previously referred to. void rebin (int newbin) { DASSERT (m_umc); unbin (); m_bin = newbin; lock (); m_biniterator = m_umc->m_bins[m_bin].map.begin(); } unordered_map_concurrent *m_umc; // which umc this iterator refers to int m_bin; // which bin within the umc BinMap_iterator_t m_biniterator; // which entry within the bin bool m_locked; // do we own the lock on the bin? }; /// Return an interator pointing to the first entry in the map. iterator begin () { iterator i (this); i.rebin (0); while (i.m_biniterator == m_bins[i.m_bin].map.end()) { if (i.m_bin == BINS-1) { // ran off the end i.unbin(); return i; } i.rebin (i.m_bin+1); } return i; } /// Return an iterator signifying the end of the map (no valid /// entry pointed to). iterator end () { iterator i (this); return i; } /// Search for key. If found, return an iterator referring to the /// element, otherwise, return an iterator that is equivalent to /// this->end(). If do_lock is true, lock the bin that we're /// searching and return the iterator in a locked state, and unlock /// the bin again if not found; however, if do_lock is false, assume /// that the caller already has the bin locked, so do no locking or /// unlocking and return an iterator that is unaware that it holds a /// lock. iterator find (const KEY &key, bool do_lock = true) { size_t b = whichbin(key); Bin &bin (m_bins[b]); if (do_lock) bin.lock (); typename BinMap_t::iterator it = bin.map.find (key); if (it == bin.map.end()) { // not found -- return the 'end' iterator if (do_lock) bin.unlock(); return end(); } // Found iterator i (this); i.m_bin = (unsigned) b; i.m_biniterator = it; i.m_locked = do_lock; return i; } /// Search for key. If found, return true and store the value. If not /// found, return false and do not alter value. If do_lock is true, /// read-lock the bin while we're searching, and release it before /// returning; however, if do_lock is false, assume that the caller /// already has the bin locked, so do no locking or unlocking. bool retrieve (const KEY &key, VALUE &value, bool do_lock = true) { size_t b = whichbin(key); Bin &bin (m_bins[b]); if (do_lock) bin.lock (); typename BinMap_t::iterator it = bin.map.find (key); bool found = (it != bin.map.end()); if (found) value = it->second; if (do_lock) bin.unlock(); return found; } /// Insert into the hash map if it's not already there. /// Return true if added, false if it was already present. /// If do_lock is true, lock the bin containing key while doing this /// operation; if do_lock is false, assume that the caller already /// has the bin locked, so do no locking or unlocking. bool insert (const KEY &key, const VALUE &value, bool do_lock = true) { size_t b = whichbin(key); Bin &bin (m_bins[b]); if (do_lock) bin.lock (); bool add = (bin.map.find (key) == bin.map.end()); if (add) { // not found -- add it! bin.map[key] = value; ++m_size; } if (do_lock) bin.unlock(); return add; } /// If the key is in the map, safely erase it. /// If do_lock is true, lock the bin containing key while doing this /// operation; if do_lock is false, assume that the caller already /// has the bin locked, so do no locking or unlocking. void erase (const KEY &key, bool do_lock = true) { size_t b = whichbin(key); Bin &bin (m_bins[b]); if (do_lock) bin.lock (); typename BinMap_t::iterator it = bin.map.find (key); if (it != bin.map.end()) { bin.map.erase (it); } if (do_lock) bin.unlock(); } /// Return true if the entire map is empty. bool empty() { return m_size == 0; } /// Return the total number of entries in the map. size_t size () { return size_t(m_size); } /// Expliticly lock the bin that will contain the key (regardless of /// whether there is such an entry in the map), and return its bin /// number. size_t lock_bin (const KEY &key) { size_t b = whichbin(key); m_bins[b].lock (); return b; } /// Explicitly unlock the specified bin (this assumes that the caller /// holds the lock). void unlock_bin (size_t bin) { m_bins[bin].unlock (); } private: struct Bin { OIIO_CACHE_ALIGN // align bin to cache line mutable spin_mutex mutex; // mutex for this bin BinMap_t map; // hash map for this bin #ifndef NDEBUG mutable atomic_int m_nlocks; // for debugging #endif Bin () { #ifndef NDEBUG m_nlocks = 0; #endif } ~Bin () { #ifndef NDEBUG DASSERT (m_nlocks == 0); #endif } void lock () const { mutex.lock(); #ifndef NDEBUG ++m_nlocks; DASSERT_MSG (m_nlocks == 1, "oops, m_nlocks = %d", (int)m_nlocks); #endif } void unlock () const { #ifndef NDEBUG DASSERT_MSG (m_nlocks == 1, "oops, m_nlocks = %d", (int)m_nlocks); --m_nlocks; #endif mutex.unlock(); } }; HASH m_hash; // hashing function atomic_int m_size; // total entries in all bins Bin m_bins[BINS]; // the bins // Which bin will this key always appear in? size_t whichbin (const KEY &key) { size_t h = m_hash(key); h = (size_t) murmur::fmix (uint64_t(h)); // scramble again return h % BINS; } }; OIIO_NAMESPACE_END #endif // OPENIMAGEIO_UNORDERED_MAP_CONCURRENT_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/paramlist.h0000644000175000017500000002344113151711064023004 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// \file /// /// Define the ParamValue and ParamValueList classes, which are used to /// store lists of arbitrary name/data pairs for internal storage of /// parameter lists, attributes, geometric primitive data, etc. #ifndef OPENIMAGEIO_PARAMLIST_H #define OPENIMAGEIO_PARAMLIST_H #if defined(_MSC_VER) // Ignore warnings about DLL exported classes with member variables that are template classes. // This happens with the Rep m_vals member variable of ParamValueList below, which is a std::vector. # pragma warning (disable : 4251) #endif #include #include "export.h" #include "typedesc.h" #include "ustring.h" OIIO_NAMESPACE_BEGIN /// ParamValue holds a parameter and a pointer to its value(s) /// /// Nomenclature: if you have an array of 4 colors for each of 15 points... /// - There are 15 VALUES /// - Each value has an array of 4 ELEMENTS, each of which is a color /// - A color has 3 COMPONENTS (R, G, B) /// class OIIO_API ParamValue { public: /// Interpolation types /// enum Interp { INTERP_CONSTANT = 0, ///< Constant for all pieces/faces INTERP_PERPIECE = 1, ///< Piecewise constant per piece/face INTERP_LINEAR = 2, ///< Linearly interpolated across each piece/face INTERP_VERTEX = 3 ///< Interpolated like vertices }; ParamValue () : m_type(TypeDesc::UNKNOWN), m_nvalues(0), m_interp(INTERP_CONSTANT), m_copy(false), m_nonlocal(false) { m_data.ptr = NULL; } ParamValue (const ustring &_name, TypeDesc _type, int _nvalues, const void *_value, bool _copy=true) { init_noclear (_name, _type, _nvalues, _value, _copy); } ParamValue (const ustring &_name, TypeDesc _type, int _nvalues, Interp _interp, const void *_value, bool _copy=true) { init_noclear (_name, _type, _nvalues, _interp, _value, _copy); } ParamValue (string_view _name, TypeDesc _type, int _nvalues, const void *_value, bool _copy=true) { init_noclear (ustring(_name), _type, _nvalues, _value, _copy); } ParamValue (string_view _name, TypeDesc _type, int _nvalues, Interp _interp, const void *_value, bool _copy=true) { init_noclear (ustring(_name), _type, _nvalues, _interp, _value, _copy); } ParamValue (const ParamValue &p, bool _copy=true) { init_noclear (p.name(), p.type(), p.nvalues(), p.interp(), p.data(), _copy); } ~ParamValue () { clear_value(); } void init (ustring _name, TypeDesc _type, int _nvalues, Interp _interp, const void *_value, bool _copy=true) { clear_value (); init_noclear (_name, _type, _nvalues, _interp, _value, _copy); } void init (ustring _name, TypeDesc _type, int _nvalues, const void *_value, bool _copy=true) { init (_name, _type, _nvalues, INTERP_CONSTANT, _value, _copy); } void init (string_view _name, TypeDesc _type, int _nvalues, const void *_value, bool _copy=true) { init (ustring(_name), _type, _nvalues, _value, _copy); } void init (string_view _name, TypeDesc _type, int _nvalues, Interp _interp, const void *_value, bool _copy=true) { init (ustring(_name), _type, _nvalues, _interp, _value, _copy); } const ParamValue& operator= (const ParamValue &p) { init (p.name(), p.type(), p.nvalues(), p.interp(), p.data(), p.m_copy); return *this; } // FIXME -- some time in the future (after more cleanup), we should make // name() return a string_view, and use uname() for the rare time when // the caller truly requires the ustring. const ustring &name () const { return m_name; } const ustring &uname () const { return m_name; } TypeDesc type () const { return m_type; } int nvalues () const { return m_nvalues; } const void *data () const { return m_nonlocal ? m_data.ptr : &m_data; } int datasize () const { return m_nvalues * static_cast(m_type.size()); } Interp interp () const { return (Interp)m_interp; } void interp (Interp i) { m_interp = (unsigned char )i; } friend void swap (ParamValue &a, ParamValue &b) { std::swap (a.m_name, b.m_name); std::swap (a.m_type, b.m_type); std::swap (a.m_nvalues, b.m_nvalues); std::swap (a.m_interp, b.m_interp); std::swap (a.m_data.ptr, b.m_data.ptr); std::swap (a.m_copy, b.m_copy); std::swap (a.m_nonlocal, b.m_nonlocal); } private: ustring m_name; ///< data name TypeDesc m_type; ///< data type, which may itself be an array int m_nvalues; ///< number of values of the given type unsigned char m_interp; ///< Interpolation type bool m_copy, m_nonlocal; union { ptrdiff_t localval; const void *ptr; } m_data; ///< Our data, either a pointer or small local value void init_noclear (ustring _name, TypeDesc _type, int _nvalues, const void *_value, bool _copy=true); void init_noclear (ustring _name, TypeDesc _type,int _nvalues, Interp _interp, const void *_value, bool _copy=true); void clear_value(); }; /// A list of ParamValue entries, that can be iterated over or searched. /// class OIIO_API ParamValueList { typedef std::vector Rep; public: ParamValueList () { } typedef Rep::iterator iterator; typedef Rep::const_iterator const_iterator; typedef ParamValue value_type; typedef value_type & reference; typedef const value_type & const_reference; typedef value_type * pointer; typedef const value_type * const_pointer; iterator begin () { return m_vals.begin(); } iterator end () { return m_vals.end(); } const_iterator begin () const { return m_vals.begin(); } const_iterator end () const { return m_vals.end(); } const_iterator cbegin () const { return m_vals.begin(); } const_iterator cend () const { return m_vals.end(); } reference front () { return m_vals.front(); } reference back () { return m_vals.back(); } const_reference front () const { return m_vals.front(); } const_reference back () const { return m_vals.back(); } reference operator[] (int i) { return m_vals[i]; } const_reference operator[] (int i) const { return m_vals[i]; } reference operator[] (size_t i) { return m_vals[i]; } const_reference operator[] (size_t i) const { return m_vals[i]; } void resize (size_t newsize) { m_vals.resize (newsize); } size_t size () const { return m_vals.size(); } /// Add space for one more ParamValue to the list, and return a /// reference to its slot. reference grow () { resize (size()+1); return back (); } /// Add a ParamValue to the end of the list. /// void push_back (const ParamValue &p) { m_vals.push_back (p); } /// Find the first entry with matching name, and if type != UNKNOWN, /// then also with matching type. The name search is case sensitive if /// casesensitive == true. iterator find (string_view name, TypeDesc type = TypeDesc::UNKNOWN, bool casesensitive = true); iterator find (ustring name, TypeDesc type = TypeDesc::UNKNOWN, bool casesensitive = true); const_iterator find (string_view name, TypeDesc type = TypeDesc::UNKNOWN, bool casesensitive = true) const; const_iterator find (ustring name, TypeDesc type = TypeDesc::UNKNOWN, bool casesensitive = true) const; /// Removes from the ParamValueList container a single element. /// iterator erase (iterator position) { return m_vals.erase (position); } /// Removes from the ParamValueList container a range of elements ([first,last)). /// iterator erase (iterator first, iterator last) { return m_vals.erase (first, last); } /// Remove all the values in the list. /// void clear () { m_vals.clear(); } /// Even more radical than clear, free ALL memory associated with the /// list itself. void free () { Rep tmp; std::swap (m_vals, tmp); } private: Rep m_vals; }; OIIO_NAMESPACE_END #endif /* !defined(OPENIMAGEIO_PARAMLIST_H) */ openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/coordinate.h0000644000175000017500000003700413151711064023137 0ustar mfvmfv/* Copyright 2015 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #pragma once #include #include #include #include "oiioversion.h" #include "platform.h" #include "dassert.h" #if OIIO_CPLUSPLUS_VERSION >= 11 # include #endif OIIO_NAMESPACE_BEGIN template class offset; template class bounds; template class bounds_iterator; /// An offset represents a Rank-dimensional offset. Think of it as /// a generalization of an array index. Underneath, it's a bit like a /// int[Rank]. template class offset { OIIO_STATIC_ASSERT (Rank >= 1); public: // constants and types #if OIIO_CPLUSPLUS_VERSION >= 11 static OIIO_CONSTEXPR_OR_CONST size_t rank = Rank; using reference = ptrdiff_t&; using const_reference = const ptrdiff_t&; using size_type = size_t; using value_type = ptrdiff_t; #else static const size_t rank = Rank; typedef ptrdiff_t& reference; typedef const ptrdiff_t& const_reference; typedef size_t size_type; typedef ptrdiff_t value_type; #endif /// Default constructor OIIO_CONSTEXPR14 offset() OIIO_NOEXCEPT { std::fill (m_ind+0, m_ind+Rank, 0); } /// Constructor for 1D case OIIO_CONSTEXPR14 offset (value_type v) OIIO_NOEXCEPT { DASSERT (Rank == 1); m_ind[0] = v; std::fill (m_ind+1, m_ind+Rank, 1); } /// Constructor for 2D case OIIO_CONSTEXPR14 offset (value_type v0, value_type v1) OIIO_NOEXCEPT { DASSERT (Rank == 2); m_ind[0] = v0; m_ind[1] = v1; std::fill (m_ind+2, m_ind+Rank, 1); } #if OIIO_CPLUSPLUS_VERSION >= 11 /// Constructor from initializer_list. Only for C++11. OIIO_CONSTEXPR14 offset (std::initializer_list il) { DASSERT (il.size() == Rank); std::copy (il.begin(), il.end(), m_ind+0); } #endif /// Equality test. OIIO_CONSTEXPR bool operator== (const offset& rhs) const OIIO_NOEXCEPT { return std::equal (m_ind+0, m_ind+Rank, rhs.m_ind+0); } /// Inequality test. OIIO_CONSTEXPR bool operator!= (const offset& rhs) const OIIO_NOEXCEPT { return ! (*this == rhs); } /// Component access reference operator[](size_type n) { DASSERT (n < Rank); return m_ind[n]; } OIIO_CONSTEXPR14 const_reference operator[] (size_type n) const { DASSERT (n < Rank); return m_ind[n]; } // Arithmetic OIIO_CONSTEXPR14 offset operator+ (const offset& rhs) const { offset result; for (size_t i = 0; i < Rank; ++i) result[i] = m_ind[i] + rhs[i]; return result; } OIIO_CONSTEXPR14 offset operator- (const offset& rhs) const { offset result; for (size_t i = 0; i < Rank; ++i) result[i] = m_ind[i] - rhs[i]; return result; }; OIIO_CONSTEXPR14 offset& operator+= (const offset& rhs) { for (size_t i = 0; i < Rank; ++i) m_ind[i] += rhs[i]; return *this; } OIIO_CONSTEXPR14 offset& operator-= (const offset& rhs) { for (size_t i = 0; i < Rank; ++i) m_ind[i] -= rhs[i]; return *this; } OIIO_CONSTEXPR14 offset& operator++ () { // prefix increment DASSERT (Rank == 1); ++m_ind[0]; return *this; } OIIO_CONSTEXPR14 offset operator++ (int) { // postfix increment DASSERT (Rank == 1); offset ret; ++(*this); return ret; } OIIO_CONSTEXPR14 offset& operator-- () { // prefix increment DASSERT (Rank == 1); --m_ind[0]; return *this; } OIIO_CONSTEXPR14 offset operator-- (int) { // postfix increment DASSERT (Rank == 1); offset ret; --(*this); return ret; } OIIO_CONSTEXPR offset operator+ () const OIIO_NOEXCEPT { return *this; } OIIO_CONSTEXPR14 offset operator- () const { offset result; for (size_t i = 0; i < Rank; ++i) result[i] = -m_ind[i]; return result; }; OIIO_CONSTEXPR14 offset operator* (value_type v) const { offset result = *this; result *= v; return result; } friend OIIO_CONSTEXPR14 offset operator* (value_type v, const offset &off) { offset result = off; result *= v; return result; } OIIO_CONSTEXPR14 offset operator/ (value_type v) const { offset result = *this; result /= v; return result; } OIIO_CONSTEXPR14 offset& operator*= (value_type v) { for (size_t i = 0; i < Rank; ++i) m_ind[i] *= v; return *this; } OIIO_CONSTEXPR14 offset& operator/= (value_type v) { for (size_t i = 0; i < Rank; ++i) m_ind[i] /= v; return *this; } friend std::ostream& operator<< (std::ostream& out, const offset& off) { out << off[0]; for (size_t i = 1; i < Rank; ++i) out << ',' << off[i]; return out; } private: value_type m_ind[Rank]; }; /// A bounds represents the size of a Rank-dimensional array. template class bounds { OIIO_STATIC_ASSERT (Rank >= 1); public: // constants and types #if OIIO_CPLUSPLUS_VERSION >= 11 static OIIO_CONSTEXPR_OR_CONST size_t rank = Rank; using reference = ptrdiff_t&; using const_reference = const ptrdiff_t&; using size_type = size_t; using value_type = ptrdiff_t; using iterator = bounds_iterator; using const_iterator = bounds_iterator; #else static const size_t rank = Rank; typedef ptrdiff_t& reference; typedef const ptrdiff_t& const_reference; typedef size_t size_type; typedef ptrdiff_t value_type; typedef bounds_iterator iterator; typedef bounds_iterator const_iterator; #endif /// Default constructor OIIO_CONSTEXPR14 bounds() OIIO_NOEXCEPT { std::fill (m_bnd+0, m_bnd+Rank, 0); } /// Constructor for 1D case OIIO_CONSTEXPR14 bounds (value_type v) { DASSERT (Rank == 1); // only if Rank == 1 m_bnd[0] = v; std::fill (m_bnd+1, m_bnd+Rank, 1); } /// Constructor for 2D case OIIO_CONSTEXPR14 bounds (value_type v0, value_type v1) { DASSERT (Rank == 2); m_bnd[0] = v0; m_bnd[1] = v1; std::fill (m_bnd+2, m_bnd+Rank, 1); } #if OIIO_CPLUSPLUS_VERSION >= 11 /// Constructor from initializer_list. Only for C++11. OIIO_CONSTEXPR14 bounds (std::initializer_list il) { DASSERT (il.size() == Rank); std::copy (il.begin(), il.end(), m_bnd+0); } #endif OIIO_CONSTEXPR14 size_type size() const OIIO_NOEXCEPT { size_type r = m_bnd[0]; for (size_t i = 1; i < Rank; ++i) r *= m_bnd[i]; return r; } OIIO_CONSTEXPR14 bool contains(const offset& idx) const OIIO_NOEXCEPT { for (size_t i = 0; i < Rank; ++i) if (idx[i] < 0 || idx[i] > m_bnd[i]) return false; return true; } /// Equality test. OIIO_CONSTEXPR bool operator== (const bounds& rhs) const OIIO_NOEXCEPT { return std::equal (m_bnd+0, m_bnd+Rank, rhs.m_bnd+0); } /// Inequality test. OIIO_CONSTEXPR bool operator!= (const bounds& rhs) const OIIO_NOEXCEPT { return ! (*this == rhs); } // bounds iterators const_iterator begin() const OIIO_NOEXCEPT { return const_iterator(*this, offset()); } // Return a bounds_iterator that is one-past-the-end of *this. const_iterator end() const OIIO_NOEXCEPT { offset off; off[0] = m_bnd[0]; return const_iterator (*this, off); } /// Component access OIIO_CONSTEXPR14 reference operator[] (size_type n) { DASSERT (n < Rank); return m_bnd[n]; } OIIO_CONSTEXPR14 const_reference operator[] (size_type n) const { DASSERT (n < Rank); return m_bnd[n]; } // bounds arithmetic friend OIIO_CONSTEXPR14 bounds operator+ (const bounds& bnd, const offset& off) { bounds result; for (size_t i = 0; i < Rank; ++i) result[i] = bnd[i] + off[i]; return result; } friend OIIO_CONSTEXPR14 bounds operator+ (const offset& off, const bounds& bnd) { return bnd + off; } friend OIIO_CONSTEXPR14 bounds operator- (const bounds& bnd, const offset& off) { bounds result; for (size_t i = 0; i < Rank; ++i) result[i] = bnd[i] - off[i]; return result; } friend OIIO_CONSTEXPR14 bounds operator- (const offset& off, const bounds& bnd) { return bnd - off; } friend OIIO_CONSTEXPR14 bounds operator* (const bounds& bnd, const value_type v) { bounds result; for (size_t i = 0; i < Rank; ++i) result[i] = bnd[i] * v; return result; } friend OIIO_CONSTEXPR14 bounds operator* (const value_type v, const bounds& bnd) { return bnd * v; } friend OIIO_CONSTEXPR14 bounds operator/ (const bounds& bnd, const value_type v) { bounds result; for (size_t i = 0; i < Rank; ++i) result[i] = bnd[i] / v; return result; } OIIO_CONSTEXPR14 bounds& operator+= (const offset& rhs) { for (size_t i = 0; i < Rank; ++i) m_bnd[i] += rhs[i]; return *this; } OIIO_CONSTEXPR14 bounds& operator-= (const offset& rhs) { for (size_t i = 0; i < Rank; ++i) m_bnd[i] -= rhs[i]; return *this; } OIIO_CONSTEXPR14 bounds& operator*= (value_type v) { for (size_t i = 0; i < Rank; ++i) m_bnd[i] *= v; return *this; } OIIO_CONSTEXPR14 bounds& operator/= (value_type v) { for (size_t i = 0; i < Rank; ++i) m_bnd[i] /= v; return *this; } friend std::ostream& operator<< (std::ostream& out, const bounds& bnd) { out << bnd[0]; for (size_t i = 1; i < Rank; ++i) out << ',' << bnd[i]; return out; } private: value_type m_bnd[Rank]; }; namespace detail { template class bounds_iterator_pointer { public: explicit bounds_iterator_pointer (offset const& off) : m_off(off) { } offset const& operator*() const { return m_off; } offset const* operator->() const { return &m_off; } private: offset m_off; }; } // end namespace detail template class bounds_iterator { OIIO_STATIC_ASSERT (Rank >= 1); public: #if OIIO_CPLUSPLUS_VERSION >= 11 using iterator_category = std::random_access_iterator_tag; using value_type = offset; using difference_type = ptrdiff_t; using pointer = detail::bounds_iterator_pointer; using reference = const offset; #else typedef offset value_type; typedef ptrdiff_t difference_type; typedef detail::bounds_iterator_pointer pointer; typedef const offset reference; #endif explicit bounds_iterator (const bounds &bnd, const offset &off) : bnd_(bnd), off_(off) {} bool operator== (const bounds_iterator &rhs) const { return off_ == rhs.off_; } bool operator!= (const bounds_iterator &rhs) const { return ! (*this == rhs); } bool operator< (bounds_iterator const& rhs) const { for (std::size_t i = 0; i < Rank; ++i) { if (off_[i] < rhs.off_[i]) return true; else if (off_[i] > rhs.off_[i]) return false; } return false; } bool operator<= (bounds_iterator const& rhs) const { return !(rhs < *this); } bool operator> (bounds_iterator const& rhs) const { return rhs < *this; } bool operator>= (bounds_iterator const& rhs) const { return !(*this < rhs); } bounds_iterator& operator++() { for (int i = Rank - 1; i >= 0; --i) { if (++off_[i] < bnd_[i]) return *this; off_[i] = 0; } off_[0] = bnd_[0]; return *this; } bounds_iterator operator++ (int) { bounds_iterator r(*this); ++(*this); return r; } bounds_iterator& operator--() { for (int i = int(Rank) - 1; i >= 0; --i) { if (--off_[i] >= 0) return *this; off_[i] = bnd_[i] - 1; } // off_[Rank - 1] == -1; return *this; } bounds_iterator operator-- (int) { bounds_iterator r(*this); --(*this); return r; } bounds_iterator operator+ (difference_type n) const { return bounds_iterator(*this) += n; } bounds_iterator& operator+= (difference_type n) { for (int i = Rank - 1; i >= 0 && n != 0; --i) { std::ptrdiff_t nx = off_[i] + n; if (nx >= bnd_[i]) { n = nx / bnd_[i]; off_[i] = nx % bnd_[i]; } else { off_[i] = nx; return *this; } } off_[0] = bnd_[0]; return *this; } bounds_iterator operator- (difference_type n) const { return bounds_iterator(*this) -= n; } bounds_iterator& operator-= (difference_type n) { return (*this += (-n)); } difference_type operator- (bounds_iterator const& rhs) const { difference_type r = 0; difference_type flat_bounds = 1; for (int i = Rank - 1; i >= 0; --i) { r += (off_[i] - rhs.off_[i]) * flat_bounds; flat_bounds *= bnd_[i]; } return r; } reference operator*() const { return off_; } pointer operator->() const { return pointer(off_); } reference operator[] (difference_type n) const { return *(*this+n); } friend std::ostream& operator<< (std::ostream& out, const bounds_iterator& i) { return out << i.off_; } private: bounds bnd_; // exposition only offset off_; // exposition only }; OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/string_view.h0000644000175000017500000003210113151711064023341 0ustar mfvmfv/* Copyright 2014 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #pragma once #include #include #include #include #include #include #include "oiioversion.h" #include "export.h" OIIO_NAMESPACE_BEGIN /// string_view : a non-owning, non-copying, non-allocating reference to a /// sequence of characters. It encapsulates both a character pointer and a /// length. /// /// A function that takes a string input (but does not need to alter the /// string in place) may use a string_view parameter and accept input that /// is any of char* (C string), string literal (constant char array), a /// std::string (C++ string), or OIIO ustring. For all of these cases, no /// extra allocations are performed, and no extra copies of the string /// contents are performed (as they would be, for example, if the function /// took a const std::string& argument but was passed a char* or string /// literal). /// /// Furthermore, a function that returns a copy or a substring of one of its /// inputs (for example, a substr()-like function) may return a string_view /// rather than a std::string, and thus generate its return value without /// any allocation or copying. Upon assignment to a std::string or ustring, /// it will properly auto-convert. /// /// There are two important caveats to using this class: /// 1. The string_view merely refers to characters owned by another string, /// so the string_view may not be used outside the lifetime of the string /// it refers to. Thus, string_view is great for parameter passing, but /// it's not a good idea to use a string_view to store strings in a data /// structure (unless you are really sure you know what you're doing). /// 2. Because the run of characters that the string_view refers to may not /// be 0-terminated, it is important to distinguish between the data() /// method, which returns the pointer to the characters, and the c_str() /// method, which is guaranteed to return a valid C string that is /// 0-terminated. Thus, if you want to pass the contents of a string_view /// to a function that expects a 0-terminated string (say, fopen), you /// must call fopen(my_string_view.c_str()). Note that the usual case /// is that the string_view does refer to a 0-terminated string, and in /// that case c_str() returns the same thing as data() without any extra /// expense; but in the rare case that it is not 0-terminated, c_str() /// will incur extra expense to internally allocate a valid C string. /// class OIIO_API string_view { public: typedef char charT; typedef charT value_type; typedef const charT* pointer; typedef const charT* const_pointer; typedef const charT& reference; typedef const charT& const_reference; typedef const_pointer const_iterator; typedef const_iterator iterator; typedef std::reverse_iterator const_reverse_iterator; typedef const_reverse_iterator reverse_iterator; typedef size_t size_type; typedef ptrdiff_t difference_type; typedef std::char_traits traits; static const size_type npos = ~size_type(0); /// Default ctr string_view () { init(NULL,0); } /// Copy ctr string_view (const string_view ©) { init (copy.m_chars, copy.m_len); } /// Construct from char* and length. string_view (const charT *chars, size_t len) { init (chars, len); } /// Construct from char*, use strlen to determine length. string_view (const charT *chars) { init (chars, chars ? strlen(chars) : 0); } /// Construct from std::string. string_view (const std::string &str) { init (str.data(), str.size()); } std::string str() const { return (m_chars ? std::string(m_chars,m_len) : std::string()); } /// Explicitly request a 0-terminated string. USUALLY, this turns out to /// be just data(), with no significant added expense. But in the more /// rare case that the string_view represetns a non-0-terminated /// substring, it will force an allocation and copy underneath. const char * c_str() const; // assignments string_view& operator= (const string_view ©) { init (copy.data(), copy.length()); return *this; } operator std::string() const { return str(); } // iterators const_iterator begin() const { return m_chars; } const_iterator end() const { return m_chars + m_len; } const_iterator cbegin() const { return m_chars; } const_iterator cend() const { return m_chars + m_len; } const_reverse_iterator rbegin() const { return const_reverse_iterator (end()); } const_reverse_iterator rend() const { return const_reverse_iterator (begin()); } const_reverse_iterator crbegin() const { return const_reverse_iterator (end()); } const_reverse_iterator crend() const { return const_reverse_iterator (begin()); } // capacity size_type size() const { return m_len; } size_type length() const { return m_len; } size_type max_size() const { return m_len; } bool empty() const { return m_len == 0; } // element access const charT& operator[] (size_type pos) const { return m_chars[pos]; } const charT& at (size_t pos) const { if (pos >= m_len) throw (std::out_of_range ("OpenImageIO::string_view::at")); return m_chars[pos]; } const charT& front() const { return m_chars[0]; } const charT& back() const { return m_chars[m_len-1]; } const charT* data() const { return m_chars; } // modifiers void clear() { init(NULL,0); } void remove_prefix(size_type n) { if (n > m_len) n = m_len; m_chars += n; m_len -= n; } void remove_suffix(size_type n) { if (n > m_len) n = m_len; m_len -= n; } string_view substr (size_type pos, size_type n=npos) const { if (pos > size()) return string_view(); // start past end -> return empty if (n == npos || pos + n > size()) n = size() - pos; return string_view (data() + pos, n); } int compare (string_view x) const { const int cmp = std::char_traits::compare (m_chars, x.m_chars, (std::min)(m_len, x.m_len)); return cmp != 0 ? cmp : int(m_len - x.m_len); // Equivalent to: // cmp != 0 ? cmp : (m_len == x.m_len ? 0 : (m_len < x.m_len ? -1 : 1)); } #if 0 // Do these later if anybody needs them bool starts_with(string_view x) const; bool ends_with(string_view x) const; #endif /// Find the first occurrence of substring s in *this, starting at /// position pos. size_type find (string_view s, size_t pos=0) const { if (pos > size()) pos = size(); const_iterator i = std::search (this->cbegin()+pos, this->cend(), s.cbegin(), s.cend(), traits::eq); return i == this->cend() ? npos : std::distance (this->cbegin(), i); } /// Find the first occurrence of character c in *this, starting at /// position pos. size_type find (charT c, size_t pos=0) const { if (pos > size()) pos = size(); const_iterator i = std::find_if (this->cbegin()+pos, this->cend(), traits_eq(c)); return i == this->cend() ? npos : std::distance (this->cbegin(), i); } /// Find the last occurrence of substring s *this, but only those /// occurrences earlier than position pos. size_type rfind (string_view s, size_t pos=npos) const { if (pos > size()) pos = size(); const_reverse_iterator b = this->crbegin()+(size()-pos); const_reverse_iterator e = this->crend(); const_reverse_iterator i = std::search (b, e, s.crbegin(), s.crend(), traits::eq); return i == e ? npos : (reverse_distance(this->crbegin(),i) - s.size() + 1); } /// Find the last occurrence of character c in *this, but only those /// occurrences earlier than position pos. size_type rfind (charT c, size_t pos=npos) const { if (pos > size()) pos = size(); const_reverse_iterator b = this->crbegin()+(size()-pos); const_reverse_iterator e = this->crend(); const_reverse_iterator i = std::find_if (b, e, traits_eq(c)); return i == e ? npos : reverse_distance (this->crbegin(),i); } size_type find_first_of (charT c, size_t pos=0) const { return find (c, pos); } size_type find_last_of (charT c, size_t pos=npos) const { return rfind (c, pos); } size_type find_first_of (string_view s) const { const_iterator i = std::find_first_of (this->cbegin(), this->cend(), s.cbegin(), s.cend(), traits::eq); return i == this->cend() ? npos : std::distance (this->cbegin(), i); } size_type find_last_of (string_view s) const { const_reverse_iterator i = std::find_first_of (this->crbegin(), this->crend(), s.cbegin(), s.cend(), traits::eq); return i == this->crend() ? npos : reverse_distance (this->crbegin(), i); } size_type find_first_not_of (string_view s) const { const_iterator i = find_not_of (this->cbegin(), this->cend(), s); return i == this->cend() ? npos : std::distance (this->cbegin(), i); } size_type find_first_not_of (charT c) const { for (const_iterator i = this->cbegin(); i != this->cend(); ++i) if (! traits::eq (c, *i)) return std::distance (this->cbegin(), i); return npos; } size_type find_last_not_of (string_view s) const { const_reverse_iterator i = find_not_of (this->crbegin(), this->crend(), s); return i == this->crend() ? npos : reverse_distance (this->crbegin(), i); } size_type find_last_not_of (charT c) const { for (const_reverse_iterator i = this->crbegin(); i != this->crend(); ++i) if (! traits::eq (c, *i)) return reverse_distance (this->crbegin(), i); return npos; } private: const charT * m_chars; size_t m_len; void init (const charT *chars, size_t len) { m_chars = chars; m_len = len; } template size_type reverse_distance (r_iter first, r_iter last) const { return m_len - 1 - std::distance (first, last); } template iter find_not_of (iter first, iter last, string_view s) const { for ( ; first != last ; ++first) if (! traits::find (s.data(), s.length(), *first)) return first; return last; } class traits_eq { public: traits_eq (charT ch) : ch(ch) {} bool operator () (charT val) const { return traits::eq (ch, val); } charT ch; }; }; inline bool operator== (string_view x, string_view y) { return x.size() == y.size() ? (x.compare (y) == 0) : false; } inline bool operator!= (string_view x, string_view y) { return x.size() == y.size() ? (x.compare (y) != 0) : true; } inline bool operator< (string_view x, string_view y) { return x.compare(y) < 0; } inline bool operator> (string_view x, string_view y) { return x.compare(y) > 0; } inline bool operator<= (string_view x, string_view y) { return x.compare(y) <= 0; } inline bool operator>= (string_view x, string_view y) { return x.compare(y) >= 0; } // Inserter inline std::ostream& operator<< (std::ostream& out, const string_view& str) { if (out.good()) out.write (str.data(), str.size()); return out; } // Temporary name equivalence typedef string_view string_ref; OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/optparser.h0000644000175000017500000001100213151711064023015 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ ///////////////////////////////////////////////////////////////////////// /// @file optparser.h /// /// @brief Option parser template ///////////////////////////////////////////////////////////////////////// #ifndef OPENIMAGEIO_OPTPARSER_H #define OPENIMAGEIO_OPTPARSER_H #include OIIO_NAMESPACE_BEGIN /// Parse a string of the form "name=value" and then call /// system.attribute (name, value), with appropriate type conversions. template inline bool optparse1 (C &system, const std::string &opt) { std::string::size_type eq_pos = opt.find_first_of ("="); if (eq_pos == std::string::npos) { // malformed option return false; } std::string name (opt, 0, eq_pos); // trim the name while (name.size() && name[0] == ' ') name.erase (0); while (name.size() && name[name.size()-1] == ' ') name.erase (name.size()-1); std::string value (opt, eq_pos+1, std::string::npos); if (name.empty()) return false; char v = value.size() ? value[0] : ' '; if ((v >= '0' && v <= '9') || v == '+' || v == '-') { // numeric if (strchr (value.c_str(), '.')) // float return system.attribute (name.c_str(), (float)atof(value.c_str())); else // int return system.attribute (name.c_str(), (int)atoi(value.c_str())); } // otherwise treat it as a string // trim surrounding double quotes if (value.size() >= 2 && value[0] == '\"' && value[value.size()-1] == '\"') value = std::string (value, 1, value.size()-2); return system.attribute (name.c_str(), value.c_str()); } /// Parse a string with comma-separated name=value directives, calling /// system.attribute(name,value) for each one, with appropriate type /// conversions. Examples: /// optparser(texturesystem, "verbose=1"); /// optparser(texturesystem, "max_memory_MB=32.0"); /// optparser(texturesystem, "a=1,b=2,c=3.14,d=\"a string\""); template inline bool optparser (C &system, const std::string &optstring) { bool ok = true; size_t len = optstring.length(); size_t pos = 0; while (pos < len) { std::string opt; bool inquote = false; while (pos < len) { unsigned char c = optstring[pos]; if (c == '\"') { // Hit a double quote -- toggle "inquote" and add the quote inquote = !inquote; opt += c; ++pos; } else if (c == ',' && !inquote) { // Hit a comma and not inside a quote -- we have an option ++pos; // skip the comma break; // done with option } else { // Anything else: add to the option opt += c; ++pos; } } // At this point, opt holds an option ok &= optparse1 (system, opt); } return ok; } OIIO_NAMESPACE_END #endif // OPENIMAGEIO_OPTPARSER_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/imagebufalgo_util.h0000644000175000017500000006032713151711064024473 0ustar mfvmfv/* Copyright 2013 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_IMAGEBUFALGO_UTIL_H #define OPENIMAGEIO_IMAGEBUFALGO_UTIL_H #include #include #include #if OIIO_CPLUSPLUS_VERSION >= 11 # include #else # include #endif OIIO_NAMESPACE_BEGIN #if OIIO_CPLUSPLUS_VERSION >= 11 using std::bind; using std::ref; using std::cref; using namespace std::placeholders; using std::placeholders::_1; #else using boost::bind; using boost::ref; using boost::cref; using boost::this_thread::get_id; #endif namespace ImageBufAlgo { enum SplitDir { Split_X, Split_Y, Split_Z, Split_Biggest }; /// Helper template for generalized multithreading for image processing /// functions. Some function/functor f is applied to every pixel the /// region of interest roi, dividing the region into multiple threads if /// threads != 1. Note that threads == 0 indicates that the number of /// threads should be as set by the global OIIO "threads" attribute. /// /// The optional splitdir determines along which axis the split will be /// made. The default is Split_Y (vertical splits), which generally seems /// the fastest (due to cache layout issues?), but perhaps there are /// algorithms where it's better to split in X, Z, or along the longest /// axis. /// /// Most image operations will require additional arguments, including /// additional input and output images or other parameters. The /// parallel_image template can still be used by employing the /// std::bind (or boost::bind, for C++ < 11). For example, suppose you /// have an image operation defined as: /// void my_image_op (ImageBuf &out, const ImageBuf &in, /// float scale, ROI roi); /// Then you can parallelize it as follows: /// ImageBuf R, A; // result, input /// ROI roi = get_roi (R.spec()); /// parallel_image (bind(my_image_op,ref(R), cref(A),3.14,_1), roi); /// template void parallel_image (Func f, ROI roi, int nthreads=0, SplitDir splitdir=Split_Y) { // Special case: threads <= 0 means to use the "threads" attribute if (nthreads <= 0) OIIO::getattribute ("threads", nthreads); // Try not to assign a thread less than 16k pixels, or it's not worth // the thread startup/teardown cost. nthreads = std::min (nthreads, 1 + int(roi.npixels() / size_t(16384))); if (nthreads <= 1) { // Just one thread, or a small image region: use this thread only f (roi); return; } // If splitdir was not explicit, find the longest edge. if (splitdir >= Split_Biggest) splitdir = roi.width() > roi.height() ? Split_X : Split_Y; int minmax[6] = { roi.xbegin, roi.xend, roi.ybegin, roi.yend, roi.zbegin, roi.zend }; int roi_begin = minmax[2*int(splitdir)]; int roi_end = minmax[2*int(splitdir)+1]; int splitlen = roi_end - roi_begin; nthreads = std::min (nthreads, splitlen); // Spawn threads by dividing the region into bands. OIIO::thread_group threads; int blocksize = std::max (1, (splitlen + nthreads - 1) / nthreads); for (int i = 0; i < nthreads; i++) { if (splitdir == Split_Y) { roi.ybegin = roi_begin + i * blocksize; roi.yend = std::min (roi.ybegin + blocksize, roi_end); if (roi.ybegin >= roi.yend) break; // no more work to dole out } else if (splitdir == Split_X) { roi.xbegin = roi_begin + i * blocksize; roi.xend = std::min (roi.xbegin + blocksize, roi_end); if (roi.xbegin >= roi.xend) break; // no more work to dole out } else { // if (splitdir == Split_Z) roi.zbegin = roi_begin + i * blocksize; roi.zend = std::min (roi.zbegin + blocksize, roi_end); if (roi.zbegin >= roi.zend) break; // no more work to dole out } if (i < nthreads-1) threads.add_thread (new OIIO::thread (f, roi)); else f (roi); // Run the last one in the calling thread } threads.join_all (); } /// Common preparation for IBA functions: Given an ROI (which may or may not /// be the default ROI::All()), destination image (which may or may not yet /// be allocated), and optional input images, adjust roi if necessary and /// allocate pixels for dst if necessary. If dst is already initialized, it /// will keep its "full" (aka display) window, otherwise its full/display /// window will be set to the union of A's and B's full/display windows. If /// dst is uninitialized and force_spec is not NULL, use *force_spec as /// dst's new spec rather than using A's. Also, if A or B inputs are /// specified but not initialized or broken, it's an error so return false. /// If all is ok, return true. Some additional checks and behaviors may be /// specified by the 'prepflags', which is a bit field defined by /// IBAprep_flags. bool OIIO_API IBAprep (ROI &roi, ImageBuf *dst, const ImageBuf *A=NULL, const ImageBuf *B=NULL, const ImageBuf *C=NULL, ImageSpec *force_spec=NULL, int prepflags=0); inline bool IBAprep (ROI &roi, ImageBuf *dst, const ImageBuf *A, const ImageBuf *B, ImageSpec *force_spec, int prepflags=0) { return IBAprep (roi, dst, A, B, NULL, force_spec, prepflags); } inline bool IBAprep (ROI &roi, ImageBuf *dst, const ImageBuf *A, int prepflags) { return IBAprep (roi, dst, A, NULL, NULL, NULL, prepflags); } enum IBAprep_flags { IBAprep_DEFAULT = 0, IBAprep_REQUIRE_ALPHA = 1<<0, IBAprep_REQUIRE_Z = 1<<1, IBAprep_REQUIRE_SAME_NCHANNELS = 1<<2, IBAprep_NO_COPY_ROI_FULL = 1<<3, // Don't copy the src's roi_full IBAprep_NO_SUPPORT_VOLUME = 1<<4, // Don't know how to do volumes IBAprep_NO_COPY_METADATA = 1<<8, // N.B. default copies all metadata IBAprep_COPY_ALL_METADATA = 1<<9, // Even unsafe things IBAprep_CLAMP_MUTUAL_NCHANNELS = 1<<10, // Clamp roi.chend to max of inputs IBAprep_SUPPORT_DEEP = 1<<11, // Operation allows deep images IBAprep_DEEP_MIXED = 1<<12, // Allow deep & non-deep combinations IBAprep_DST_FLOAT_PIXELS = 1<<13, // If dst is uninit, make it float IBAprep_MINIMIZE_NCHANNELS = 1<<14, // Multi-inputs get min(nchannels) }; /// Given data types a and b, return a type that is a best guess for one /// that can handle both without any loss of range or precision. TypeDesc::BASETYPE OIIO_API type_merge (TypeDesc::BASETYPE a, TypeDesc::BASETYPE b); inline TypeDesc::BASETYPE type_merge (TypeDesc::BASETYPE a, TypeDesc::BASETYPE b, TypeDesc::BASETYPE c) { return type_merge (type_merge(a,b), c); } inline TypeDesc type_merge (TypeDesc a, TypeDesc b) { return type_merge (TypeDesc::BASETYPE(a.basetype), TypeDesc::BASETYPE(b.basetype)); } inline TypeDesc type_merge (TypeDesc a, TypeDesc b, TypeDesc c) { return type_merge (type_merge(a,b), c); } // Macro to call a type-specialzed version func(R,...) #define OIIO_DISPATCH_TYPES(ret,name,func,type,R,...) \ switch (type.basetype) { \ case TypeDesc::FLOAT : \ ret = func (R, __VA_ARGS__); break; \ case TypeDesc::UINT8 : \ ret = func (R, __VA_ARGS__); break; \ case TypeDesc::HALF : \ ret = func (R, __VA_ARGS__); break; \ case TypeDesc::UINT16: \ ret = func (R, __VA_ARGS__); break; \ case TypeDesc::INT8 : \ ret = func (R, __VA_ARGS__); break; \ case TypeDesc::INT16 : \ ret = func (R, __VA_ARGS__); break; \ case TypeDesc::UINT : \ ret = func (R, __VA_ARGS__); break; \ case TypeDesc::INT : \ ret = func (R, __VA_ARGS__); break; \ case TypeDesc::DOUBLE: \ ret = func (R, __VA_ARGS__); break; \ default: \ (R).error ("%s: Unsupported pixel data format '%s'", name, type); \ ret = false; \ } // Helper, do not call from the outside world. #define OIIO_DISPATCH_TYPES2_HELP(ret,name,func,Rtype,Atype,R,...) \ switch (Atype.basetype) { \ case TypeDesc::FLOAT : \ ret = func (R, __VA_ARGS__); break; \ case TypeDesc::UINT8 : \ ret = func (R, __VA_ARGS__); break; \ case TypeDesc::HALF : \ ret = func (R, __VA_ARGS__); break; \ case TypeDesc::UINT16: \ ret = func (R, __VA_ARGS__); break; \ case TypeDesc::INT8 : \ ret = func (R, __VA_ARGS__); break; \ case TypeDesc::INT16 : \ ret = func (R, __VA_ARGS__); break; \ case TypeDesc::UINT : \ ret = func (R, __VA_ARGS__); break; \ case TypeDesc::INT : \ ret = func (R, __VA_ARGS__); break; \ case TypeDesc::DOUBLE : \ ret = func (R, __VA_ARGS__); break; \ default: \ (R).error ("%s: Unsupported pixel data format '%s'", name, Atype); \ ret = false; \ } // Macro to call a type-specialzed version func(R,...). #define OIIO_DISPATCH_TYPES2(ret,name,func,Rtype,Atype,R,...) \ switch (Rtype.basetype) { \ case TypeDesc::FLOAT : \ OIIO_DISPATCH_TYPES2_HELP(ret,name,func,float,Atype,R,__VA_ARGS__); \ break; \ case TypeDesc::UINT8 : \ OIIO_DISPATCH_TYPES2_HELP(ret,name,func,unsigned char,Atype,R,__VA_ARGS__); \ break; \ case TypeDesc::HALF : \ OIIO_DISPATCH_TYPES2_HELP(ret,name,func,half,Atype,R,__VA_ARGS__); \ break; \ case TypeDesc::UINT16: \ OIIO_DISPATCH_TYPES2_HELP(ret,name,func,unsigned short,Atype,R,__VA_ARGS__); \ break; \ case TypeDesc::INT8: \ OIIO_DISPATCH_TYPES2_HELP(ret,name,func,char,Atype,R,__VA_ARGS__); \ break; \ case TypeDesc::INT16: \ OIIO_DISPATCH_TYPES2_HELP(ret,name,func,short,Atype,R,__VA_ARGS__); \ break; \ case TypeDesc::UINT: \ OIIO_DISPATCH_TYPES2_HELP(ret,name,func,unsigned int,Atype,R,__VA_ARGS__); \ break; \ case TypeDesc::INT: \ OIIO_DISPATCH_TYPES2_HELP(ret,name,func,int,Atype,R,__VA_ARGS__); \ break; \ case TypeDesc::DOUBLE: \ OIIO_DISPATCH_TYPES2_HELP(ret,name,func,double,Atype,R,__VA_ARGS__);\ break; \ default: \ (R).error ("%s: Unsupported pixel data format '%s'", name, Rtype); \ ret = false; \ } // Macro to call a type-specialzed version func(R,...) for // the most common types, will auto-convert the rest to float. #define OIIO_DISPATCH_COMMON_TYPES(ret,name,func,type,R,...) \ switch (type.basetype) { \ case TypeDesc::FLOAT : \ ret = func (R, __VA_ARGS__); break; \ case TypeDesc::UINT8 : \ ret = func (R, __VA_ARGS__); break; \ case TypeDesc::HALF : \ ret = func (R, __VA_ARGS__); break; \ case TypeDesc::UINT16: \ ret = func (R, __VA_ARGS__); break; \ default: { \ /* other types: punt and convert to float, then copy back */ \ ImageBuf Rtmp; \ if ((R).initialized()) \ Rtmp.copy (R, TypeDesc::FLOAT); \ ret = func (Rtmp, __VA_ARGS__); \ if (ret) \ (R).copy (Rtmp); \ else \ (R).error ("%s", Rtmp.geterror()); \ } \ } // Helper, do not call from the outside world. #define OIIO_DISPATCH_COMMON_TYPES2_HELP(ret,name,func,Rtype,Atype,R,A,...) \ switch (Atype.basetype) { \ case TypeDesc::FLOAT : \ ret = func (R, A, __VA_ARGS__); break; \ case TypeDesc::UINT8 : \ ret = func (R, A, __VA_ARGS__); break; \ case TypeDesc::HALF : \ ret = func (R, A, __VA_ARGS__); break; \ case TypeDesc::UINT16: \ ret = func (R, A, __VA_ARGS__); break; \ default: { \ /* other types: punt and convert to float, then copy back */ \ ImageBuf Atmp; \ Atmp.copy (A, TypeDesc::FLOAT); \ ret = func (R, Atmp, __VA_ARGS__); \ } \ } // Macro to call a type-specialzed version func(R,A,...) for // the most common types, will auto-convert the rest to float. #define OIIO_DISPATCH_COMMON_TYPES2(ret,name,func,Rtype,Atype,R,A,...) \ switch (Rtype.basetype) { \ case TypeDesc::FLOAT : \ OIIO_DISPATCH_COMMON_TYPES2_HELP(ret,name,func,float,Atype,R,A,__VA_ARGS__); \ break; \ case TypeDesc::UINT8 : \ OIIO_DISPATCH_COMMON_TYPES2_HELP(ret,name,func,unsigned char,Atype,R,A,__VA_ARGS__); \ break; \ case TypeDesc::HALF : \ OIIO_DISPATCH_COMMON_TYPES2_HELP(ret,name,func,half,Atype,R,A,__VA_ARGS__); \ break; \ case TypeDesc::UINT16: \ OIIO_DISPATCH_COMMON_TYPES2_HELP(ret,name,func,unsigned short,Atype,R,A,__VA_ARGS__); \ break; \ default: { \ /* other types: punt and convert to float, then copy back */ \ ImageBuf Rtmp; \ if ((R).initialized()) \ Rtmp.copy (R, TypeDesc::FLOAT); \ OIIO_DISPATCH_COMMON_TYPES2_HELP(ret,name,func,float,Atype,Rtmp,A,__VA_ARGS__); \ if (ret) \ (R).copy (Rtmp); \ else \ (R).error ("%s", Rtmp.geterror()); \ } \ } // Helper, do not call from the outside world. #define OIIO_DISPATCH_COMMON_TYPES3_HELP2(ret,name,func,Rtype,Atype,Btype,R,A,B,...) \ switch (Rtype.basetype) { \ case TypeDesc::FLOAT : \ ret = func (R,A,B,__VA_ARGS__); break; \ case TypeDesc::UINT8 : \ ret = func (R,A,B,__VA_ARGS__); break; \ case TypeDesc::HALF : \ ret = func (R,A,B,__VA_ARGS__); break; \ case TypeDesc::UINT16: \ ret = func (R,A,B,__VA_ARGS__); break; \ default: { \ /* other types: punt and convert to float, then copy back */ \ ImageBuf Rtmp; \ if ((R).initialized()) \ Rtmp.copy (R, TypeDesc::FLOAT); \ ret = func (R,A,B,__VA_ARGS__); \ if (ret) \ (R).copy (Rtmp); \ else \ (R).error ("%s", Rtmp.geterror()); \ } \ } // Helper, do not call from the outside world. #define OIIO_DISPATCH_COMMON_TYPES3_HELP(ret,name,func,Rtype,Atype,Btype,R,A,B,...) \ switch (Btype.basetype) { \ case TypeDesc::FLOAT : \ OIIO_DISPATCH_COMMON_TYPES3_HELP2(ret,name,func,Rtype,Atype,float,R,A,B,__VA_ARGS__); \ break; \ case TypeDesc::UINT8 : \ OIIO_DISPATCH_COMMON_TYPES3_HELP2(ret,name,func,Rtype,Atype,unsigned char,R,A,B,__VA_ARGS__); \ break; \ case TypeDesc::HALF : \ OIIO_DISPATCH_COMMON_TYPES3_HELP2(ret,name,func,Rtype,Atype,half,R,A,B,__VA_ARGS__); \ break; \ case TypeDesc::UINT16 : \ OIIO_DISPATCH_COMMON_TYPES3_HELP2(ret,name,func,Rtype,Atype,unsigned short,R,A,B,__VA_ARGS__); \ break; \ default: { \ /* other types: punt and convert to float */ \ ImageBuf Btmp; \ Btmp.copy (B, TypeDesc::FLOAT); \ OIIO_DISPATCH_COMMON_TYPES3_HELP2(ret,name,func,Rtype,Atype,float,R,A,Btmp,__VA_ARGS__); \ } \ } // Macro to call a type-specialzed version func(R,A,B,...) // the most common types, will auto-convert the rest to float. #define OIIO_DISPATCH_COMMON_TYPES3(ret,name,func,Rtype,Atype,Btype,R,A,B,...) \ switch (Atype.basetype) { \ case TypeDesc::FLOAT : \ OIIO_DISPATCH_COMMON_TYPES3_HELP(ret,name,func,Rtype,float,Btype,R,A,B,__VA_ARGS__); \ break; \ case TypeDesc::UINT8 : \ OIIO_DISPATCH_COMMON_TYPES3_HELP(ret,name,func,Rtype,unsigned char,Btype,R,A,B,__VA_ARGS__); \ break; \ case TypeDesc::HALF : \ OIIO_DISPATCH_COMMON_TYPES3_HELP(ret,name,func,Rtype,half,Btype,R,A,B,__VA_ARGS__); \ break; \ case TypeDesc::UINT16: \ OIIO_DISPATCH_COMMON_TYPES3_HELP(ret,name,func,Rtype,unsigned short,Btype,R,A,B,__VA_ARGS__); \ break; \ default: \ /* other types: punt and convert to float */ \ ImageBuf Atmp; \ Atmp.copy (A, TypeDesc::FLOAT); \ OIIO_DISPATCH_COMMON_TYPES3_HELP(ret,name,func,Rtype,float,Btype,R,Atmp,B,__VA_ARGS__); \ } } // end namespace ImageBufAlgo OIIO_NAMESPACE_END #endif // OPENIMAGEIO_IMAGEBUFALGO_UTIL_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/sysutil.h0000644000175000017500000001437413151711064022531 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ ///////////////////////////////////////////////////////////////////////// /// @file sysutil.h /// /// @brief Platform-independent utilities for various OS, hardware, and /// system resource functionality, all in namespace Sysutil. ///////////////////////////////////////////////////////////////////////// #pragma once #include #include #ifdef __MINGW32__ #include // for alloca #endif #include "export.h" #include "oiioversion.h" #include "platform.h" #include "string_view.h" OIIO_NAMESPACE_BEGIN /// @namespace Sysutil /// /// @brief Platform-independent utilities for various OS, hardware, and /// system resource functionality. namespace Sysutil { /// The amount of memory currently being used by this process, in bytes. /// If resident==true (the default), it will report just the resident /// set in RAM; if resident==false, it returns the full virtual arena /// (which can be misleading because gcc allocates quite a bit of /// virtual, but not actually resident until malloced, memory per /// thread). OIIO_API size_t memory_used (bool resident=true); /// The amount of physical RAM on this machine, in bytes. /// If it can't figure it out, it will return 0. OIIO_API size_t physical_memory (); /// Convert calendar time pointed by 'time' into local time and save it in /// 'converted_time' variable OIIO_API void get_local_time (const time_t *time, struct tm *converted_time); /// Return the full path of the currently-running executable program. /// OIIO_API std::string this_program_path (); /// Return the value of an environment variable (or the empty string_view /// if it is not found in the environment.) OIIO_API string_view getenv (string_view name); /// Sleep for the given number of microseconds. /// OIIO_API void usleep (unsigned long useconds); /// Try to put the process into the background so it doesn't continue to /// tie up any shell that it was launched from. The arguments are the /// argc/argv that describe the program and its command line arguments. /// Return true if successful, false if it was unable to do so. OIIO_API bool put_in_background (int argc, char* argv[]); /// Number of virtual cores available on this platform (including /// hyperthreads). OIIO_API unsigned int hardware_concurrency (); /// Number of full hardware cores available on this platform (does not /// include hyperthreads). This is not always accurate and on some /// platforms will return the number of virtual cores. OIIO_API unsigned int physical_concurrency (); /// Get the maximum number of open file handles allowed on this system. OIIO_API size_t max_open_files (); /// Try to figure out how many columns wide the terminal window is. May not /// be correct on all systems, will default to 80 if it can't figure it out. OIIO_API int terminal_columns (); /// Try to figure out how many rows tall the terminal window is. May not be /// correct on all systems, will default to 24 if it can't figure it out. OIIO_API int terminal_rows (); /// Term object encapsulates information about terminal output for the sake /// of constructing ANSI escape sequences. class OIIO_API Term { public: /// Default ctr: assume ANSI escape sequences are ok. Term () : m_is_console(true) { } /// Construct from a FILE*: ANSI codes ok if the file describes a /// live console, otherwise they will be supressed. Term (FILE *file); /// Construct from a stream: ANSI codes ok if the file describes a /// live console, otherwise they will be supressed. Term (const std::ostream &stream); /// ansi("appearance") returns the ANSI escape sequence for the named /// command (if ANSI codes are ok, otherwise it will return the empty /// string). Accepted commands include: "default", "bold", "underscore", /// "blink", "reverse", "concealed", "black", "red", "green", "yellow", /// "blue", "magenta", "cyan", "white", "black_bg", "red_bg", /// "green_bg", "yellow_bg", "blue_bg", "magenta_bg", "cyan_bg", /// "white_bg". Commands may be combined with "," for example: /// "bold,green,white_bg". std::string ansi (string_view command) const; /// ansi("appearance", "text") returns the text, with the formatting /// command, then the text, then the formatting command to return to /// default appearance. std::string ansi (string_view command, string_view text) const { return std::string(ansi(command)) + std::string(text) + ansi("default"); } /// Extended color control: take RGB values from 0-255 std::string ansi_fgcolor (int r, int g, int b); std::string ansi_bgcolor (int r, int g, int b); bool is_console () const { return m_is_console; } private: bool m_is_console; }; } // namespace Sysutils OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/array_view.h0000644000175000017500000002661013151711064023161 0ustar mfvmfv/* Copyright 2015 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #pragma once #include #include #include #if OIIO_CPLUSPLUS_VERSION >= 11 # include # include #else /* FIXME(C++11): this case can go away when C++11 is our minimum */ # include #endif #include "oiioversion.h" #include "platform.h" #include "dassert.h" #include "coordinate.h" OIIO_NAMESPACE_BEGIN #if OIIO_CPLUSPLUS_VERSION >= 11 using std::remove_const; using std::is_array; #else /* FIXME(C++11): this case can go away when C++11 is our minimum */ using boost::remove_const; using boost::is_array; #endif template class array_view; template class array_view_strided; /// array_view : a non-owning reference to a contiguous array with /// known length. If Rank > 1, it's multi-dimensional. An array_view is /// mutable (the values in the array may be modified), whereas an /// array_view is not mutable. /// /// Background: Functions whose input requires a set of contiguous values /// (an array) are faced with a dilemma. If the caller passes just a /// pointer, the function has no inherent way to determine how many elements /// may safely be accessed. Passing a std::vector& is "safe", but the caller /// may not have the data in a vector. The function could require an /// explicit length to be passed (or a begin/end pair of iterators or /// pointers). Any way you shake it, there is some awkwardness. /// /// The array_view template tries to address this problem by providing /// a way to pass array parameters that are non-owning, non-copying, /// non-allocating, and contain a length reference (which in many cases /// is transparently and automatically computed without additional user /// code). template class array_view { OIIO_STATIC_ASSERT (Rank >= 1); OIIO_STATIC_ASSERT (is_array::value == false); public: #if OIIO_CPLUSPLUS_VERSION >= 11 // using iterator = bounds_iterator; // using const_iterator = bounds_iterator; static OIIO_CONSTEXPR_OR_CONST size_t rank = Rank; using offset_type = offset; using bounds_type = OIIO::bounds; using stride_type = offset; using size_type = size_t; using value_type = T; using pointer = T*; using const_pointer = const T*; using reference = T&; #else static const size_t rank = Rank; typedef offset offset_type; typedef OIIO::bounds bounds_type; typedef offset stride_type; typedef size_t size_type; typedef T value_type; typedef T* pointer; typedef const T* const_pointer; typedef T& reference; #endif /// Default ctr -- points to nothing array_view () : m_data(NULL) { } /// Copy constructor array_view (const array_view ©) : m_data(copy.data()), m_bounds(copy.bounds()) {} /// Construct from T* and length. array_view (pointer data, bounds_type bounds) : m_data(data), m_bounds(bounds) { } /// Construct from a single T&. array_view (T &data) : m_data(&data), m_bounds(1) { } /// Construct from a fixed-length C array. Template magic automatically /// finds the length from the declared type of the array. template array_view (T (&data)[N]) : m_data(data), m_bounds(N) { DASSERT (Rank == 1); } /// Construct from std::vector. array_view (std::vector &v) : m_data(v.size() ? &v[0] : NULL), m_bounds(v.size()) { DASSERT (Rank == 1); } /// Construct from const std::vector. /// This turns const std::vector into an array_view (the /// array_view isn't const, but the data it points to will be). array_view (const std::vector::type> &v) : m_data(v.size() ? &v[0] : NULL), m_bounds(v.size()) { DASSERT (Rank == 1); } #if OIIO_CPLUSPLUS_VERSION >= 11 /// Construct an array_view from an initializer_list. constexpr array_view (std::initializer_list il) : array_view (il.begin(), il.size()) { } #endif // assignments array_view& operator= (const array_view ©) { m_data = copy.data(); m_bounds = copy.bounds(); return *this; } OIIO_CONSTEXPR bounds_type bounds() const OIIO_NOEXCEPT { return m_bounds; } OIIO_CONSTEXPR14 size_type size() const OIIO_NOEXCEPT { return m_bounds.size(); } OIIO_CONSTEXPR14 offset_type stride() const OIIO_NOEXCEPT { if (Rank == 1) { return offset_type(1); } else { offset_type offset; offset[Rank-1] = 1; for (int i = int(Rank)-2; i >= 0; --i) offset[i] = offset[i+1] * m_bounds[i+1]; return offset; } } OIIO_CONSTEXPR pointer data() const OIIO_NOEXCEPT { return m_data; } OIIO_CONSTEXPR T& operator[] (offset_type idx) const { return VIEW_ACCESS(data(), idx, stride(), Rank); } T& at (offset_type idx) const { // FIXME -- should be offset_type if (! bounds().contains(idx)) throw (std::out_of_range ("OpenImageIO::array_view::at")); return VIEW_ACCESS(data(), idx, stride(), Rank); } // T& front() const { return m_data[0]; } // FIXME - delete? // T& back() const { return m_data[size()-1]; } // FIXME - delete? // FIXME -- slicing and sectioning private: T * m_data; bounds_type m_bounds; reference VIEW_ACCESS (T* data, const offset_type &idx, const stride_type &stride, size_t rank=Rank) const { ptrdiff_t offset = 0; for (size_t i = 0; i < rank; ++i) offset += idx[i] * stride[i]; return data[offset]; } }; /// array_view_strided : a non-owning, mutable reference to a contiguous /// array with known length and optionally non-default strides through the /// data. An array_view_strided is mutable (the values in the array may /// be modified), whereas an array_view_strided is not mutable. template class array_view_strided { OIIO_STATIC_ASSERT (Rank >= 1); OIIO_STATIC_ASSERT (is_array::value == false); public: #if OIIO_CPLUSPLUS_VERSION >= 11 static OIIO_CONSTEXPR_OR_CONST size_t rank = Rank; using offset_type = offset; using bounds_type = OIIO::bounds; using stride_type = offset; using size_type = size_t; using value_type = T; using pointer = T*; using const_pointer = const T*; using reference = T&; #else static const size_t rank = Rank; typedef offset offset_type; typedef OIIO::bounds bounds_type; typedef offset stride_type; typedef size_t size_type; typedef T value_type; typedef T* pointer; typedef const T* const_pointer; typedef T& reference; #endif /// Default ctr -- points to nothing array_view_strided () : m_data(NULL), m_stride(0) { } /// Copy constructor array_view_strided (const array_view_strided ©) : m_data(copy.data()), m_bounds(copy.bounds()), m_stride(copy.stride()) {} /// Construct from T* and bounds. array_view_strided (T *data, bounds_type bounds) : m_data(data), m_bounds(bounds), m_stride(1) { } /// Construct from T*, bounds, and stride. array_view_strided (T *data, bounds_type bounds, stride_type stride) : m_data(data), m_bounds(bounds), m_stride(stride) { } /// Construct from a single T&. array_view_strided (T &data) : m_data(&data), m_bounds(1), m_stride(1) { } /// Construct from a fixed-length C array. Template magic automatically /// finds the length from the declared type of the array. template array_view_strided (T (&data)[N]) : m_data(data), m_bounds(N), m_stride(1) { DASSERT (Rank == 1); } /// Construct from std::vector. array_view_strided (std::vector &v) : m_data(v.size() ? &v[0] : NULL), m_bounds(v.size()), m_stride(1) { DASSERT (Rank == 1); } /// Construct from const std::vector. /// This turns const std::vector into an array_view (the /// array_view isn't const, but the data it points to will be). array_view_strided (const std::vector::type> &v) : m_data(v.size() ? &v[0] : NULL), m_bounds(v.size()), m_stride(1) { DASSERT (Rank == 1); } #if OIIO_CPLUSPLUS_VERSION >= 11 /// Construct an array_view from an initializer_list. constexpr array_view_strided (std::initializer_list il) : array_view_strided (il.begin(), il.size()) { } #endif // assignments array_view_strided& operator= (const array_view_strided ©) { m_data = copy.data(); m_bounds = copy.bounds(); m_stride = copy.stride(); return *this; } size_type size() const { return m_bounds.size(); } stride_type stride() const { return m_stride; } OIIO_CONSTEXPR T& operator[] (size_type idx) const { return VIEW_ACCESS(data(), idx, stride(), Rank); } const T& at (size_t idx) const { if (! bounds().contains(idx)) throw (std::out_of_range ("OpenImageIO::array_view_strided::at")); return VIEW_ACCESS(data(), idx, stride(), Rank); } T& front() const { return m_data[0]; } T& back() const { return get(size()-1); } pointer data() const { return m_data; } bounds_type bounds () const { return m_bounds; } private: T * m_data; bounds_type m_bounds; stride_type m_stride; reference VIEW_ACCESS (T* data, const offset_type &idx, const stride_type &stride, size_t rank=Rank) const { ptrdiff_t offset = 0; for (size_t i = 0; i < rank; ++i) offset += idx[i] * stride[i]; return data[offset]; } }; OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/unittest.h0000644000175000017500000001227413151711064022671 0ustar mfvmfv /* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_UNITTEST_H #define OPENIMAGEIO_UNITTEST_H #include static int unit_test_failures = 0; /// OIIO_CHECK_* macros checks if the conditions is met, and if not, /// prints an error message indicating the module and line where the /// error occurred, but does NOT abort. This is helpful for unit tests /// where we do not want one failure. #define OIIO_CHECK_ASSERT(x) \ ((x) ? ((void)0) \ : ((std::cout << __FILE__ << ":" << __LINE__ << ":\n" \ << "FAILED: " << #x << "\n"), \ (void)++unit_test_failures)) #define OIIO_CHECK_EQUAL(x,y) \ (((x) == (y)) ? ((void)0) \ : ((std::cout << __FILE__ << ":" << __LINE__ << ":\n" \ << "FAILED: " << #x << " == " << #y << "\n" \ << "\tvalues were '" << (x) << "' and '" << (y) << "'\n"), \ (void)++unit_test_failures)) #define OIIO_CHECK_EQUAL_THRESH(x,y,eps) \ ((std::abs((x)-(y)) <= eps) ? ((void)0) \ : ((std::cout << __FILE__ << ":" << __LINE__ << ":\n" \ << "FAILED: " << #x << " == " << #y << "\n" \ << "\tvalues were '" << (x) << "' and '" << (y) << "'" \ << ", diff was " << std::abs((x)-(y)) << "\n"), \ (void)++unit_test_failures)) #define OIIO_CHECK_NE(x,y) \ (((x) != (y)) ? ((void)0) \ : ((std::cout << __FILE__ << ":" << __LINE__ << ":\n" \ << "FAILED: " << #x << " != " << #y << "\n" \ << "\tvalues were '" << (x) << "' and '" << (y) << "'\n"), \ (void)++unit_test_failures)) #define OIIO_CHECK_LT(x,y) \ (((x) < (y)) ? ((void)0) \ : ((std::cout << __FILE__ << ":" << __LINE__ << ":\n" \ << "FAILED: " << #x << " < " << #y << "\n" \ << "\tvalues were '" << (x) << "' and '" << (y) << "'\n"), \ (void)++unit_test_failures)) #define OIIO_CHECK_GT(x,y) \ (((x) > (y)) ? ((void)0) \ : ((std::cout << __FILE__ << ":" << __LINE__ << ":\n" \ << "FAILED: " << #x << " > " << #y << "\n" \ << "\tvalues were '" << (x) << "' and '" << (y) << "'\n"), \ (void)++unit_test_failures)) #define OIIO_CHECK_LE(x,y) \ (((x) <= (y)) ? ((void)0) \ : ((std::cout << __FILE__ << ":" << __LINE__ << ":\n" \ << "FAILED: " << #x << " <= " << #y << "\n" \ << "\tvalues were '" << (x) << "' and '" << (y) << "'\n"), \ (void)++unit_test_failures)) #define OIIO_CHECK_GE(x,y) \ (((x) >= (y)) ? ((void)0) \ : ((std::cout << __FILE__ << ":" << __LINE__ << ":\n" \ << "FAILED: " << #x << " >= " << #y << "\n" \ << "\tvalues were '" << (x) << "' and '" << (y) << "'\n"), \ (void)++unit_test_failures)) #endif /* OPENIMAGEIO_UNITTEST_H */ openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/thread.h0000644000175000017500000004247213151711064022264 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ ///////////////////////////////////////////////////////////////////////// /// @file thread.h /// /// @brief Wrappers and utilities for multithreading. ///////////////////////////////////////////////////////////////////////// #ifndef OPENIMAGEIO_THREAD_H #define OPENIMAGEIO_THREAD_H #include #include "oiioversion.h" #include "platform.h" #include "atomic.h" #if OIIO_CPLUSPLUS_VERSION >= 11 # include # include # include # define not_yet_OIIO_USE_STDATOMIC 1 #else /* prior to C++11... */ // Use Boost mutexes & guards when C++11 is not available # include # if defined(__GNUC__) && (BOOST_VERSION == 104500) // gcc reports errors inside some of the boost headers with boost 1.45 // See: https://svn.boost.org/trac/boost/ticket/4818 # pragma GCC diagnostic ignored "-Wunused-variable" # endif # include # if defined(__GNUC__) && (BOOST_VERSION == 104500) // can't restore via push/pop in all versions of gcc (warning push/pop implemented for 4.6+ only) # pragma GCC diagnostic error "-Wunused-variable" # endif #endif // OIIO_THREAD_ALLOW_DCLP, if set to 0, prevents us from using a dodgy // "double checked lock pattern" (DCLP). We are very careful to construct // it safely and correctly, and these uses improve thread performance for // us. But it confuses Thread Sanitizer, so this switch allows you to turn // it off. Also set to 0 if you don't believe that we are correct in // allowing this construct on all platforms. #ifndef OIIO_THREAD_ALLOW_DCLP #define OIIO_THREAD_ALLOW_DCLP 1 #endif // Some helpful links: // // Descriptions of the "new" gcc atomic intrinsics: // https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html // Old gcc atomic intrinsics: // https://gcc.gnu.org/onlinedocs/gcc-4.4.2/gcc/Atomic-Builtins.html // C++11 and beyond std::atomic: // http://en.cppreference.com/w/cpp/atomic OIIO_NAMESPACE_BEGIN /// Null mutex that can be substituted for a real one to test how much /// overhead is associated with a particular mutex. class null_mutex { public: null_mutex () { } ~null_mutex () { } void lock () { } void unlock () { } void lock_shared () { } void unlock_shared () { } bool try_lock () { return true; } }; /// Null lock that can be substituted for a real one to test how much /// overhead is associated with a particular lock. template class null_lock { public: null_lock (T &m) { } }; #ifdef NOTHREADS // Definitions that we use for debugging to turn off all mutexes, locks, // and atomics in order to test the performance hit of our thread safety. typedef null_mutex mutex; typedef null_mutex recursive_mutex; typedef null_lock lock_guard; typedef null_lock recursive_lock_guard; #elif OIIO_CPLUSPLUS_VERSION >= 11 typedef std::mutex mutex; typedef std::recursive_mutex recursive_mutex; typedef std::lock_guard< mutex > lock_guard; typedef std::lock_guard< recursive_mutex > recursive_lock_guard; typedef std::thread thread; #else // Fairly modern Boost has all the mutex and lock types we need. typedef boost::mutex mutex; typedef boost::recursive_mutex recursive_mutex; typedef boost::lock_guard< mutex > lock_guard; typedef boost::lock_guard< recursive_mutex > recursive_lock_guard; typedef boost::thread thread; #endif /// Yield the processor for the rest of the timeslice. /// inline void yield () { #if defined(__GNUC__) sched_yield (); #elif defined(_MSC_VER) SwitchToThread (); #else # error No yield on this platform. #endif } // Slight pause inline void pause (int delay) { #if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) for (int i = 0; i < delay; ++i) __asm__ __volatile__("pause;"); #elif defined(__GNUC__) && (defined(__arm__) || defined(__s390__)) for (int i = 0; i < delay; ++i) __asm__ __volatile__("NOP;"); #elif defined(_MSC_VER) for (int i = 0; i < delay; ++i) { #if defined (_WIN64) YieldProcessor(); #else _asm pause #endif /* _WIN64 */ } #else // No pause on this platform, just punt for (int i = 0; i < delay; ++i) ; #endif } // Helper class to deliver ever longer pauses until we yield our timeslice. class atomic_backoff { public: atomic_backoff () : m_count(1) { } void operator() () { if (m_count <= 16) { pause (m_count); m_count *= 2; } else { yield(); } } private: int m_count; }; #ifdef NOTHREADS typedef null_mutex spin_mutex; typedef null_lock spin_lock; #else // Define our own spin locks. /// A spin_mutex is semantically equivalent to a regular mutex, except /// for the following: /// - A spin_mutex is just 4 bytes, whereas a regular mutex is quite /// large (44 bytes for pthread). /// - A spin_mutex is extremely fast to lock and unlock, whereas a regular /// mutex is surprisingly expensive just to acquire a lock. /// - A spin_mutex takes CPU while it waits, so this can be very /// wasteful compared to a regular mutex that blocks (gives up its /// CPU slices until it acquires the lock). /// /// The bottom line is that mutex is the usual choice, but in cases where /// you need to acquire locks very frequently, but only need to hold the /// lock for a very short period of time, you may save runtime by using /// a spin_mutex, even though it's non-blocking. /// /// N.B. A spin_mutex is only the size of an int. To avoid "false /// sharing", be careful not to put two spin_mutex objects on the same /// cache line (within 128 bytes of each other), or the two mutexes may /// effectively (and wastefully) lock against each other. /// class spin_mutex { public: /// Default constructor -- initialize to unlocked. /// spin_mutex (void) { m_locked = 0; } ~spin_mutex (void) { } /// Copy constructor -- initialize to unlocked. /// spin_mutex (const spin_mutex &) { m_locked = 0; } /// Assignment does not do anything, since lockedness should not /// transfer. const spin_mutex& operator= (const spin_mutex&) { return *this; } /// Acquire the lock, spin until we have it. /// void lock () { // To avoid spinning too tightly, we use the atomic_backoff to // provide increasingly longer pauses, and if the lock is under // lots of contention, eventually yield the timeslice. atomic_backoff backoff; // Try to get ownership of the lock. Though experimentation, we // found that OIIO_UNLIKELY makes this just a bit faster on // gcc x86/x86_64 systems. while (! OIIO_UNLIKELY(try_lock())) { #if OIIO_THREAD_ALLOW_DCLP // The full try_lock() involves a compare_and_swap, which // writes memory, and that will lock the bus. But a normal // read of m_locked will let us spin until the value // changes, without locking the bus. So it's faster to // check in this manner until the mutex appears to be free. // HOWEVER... Thread Sanitizer things this is an instance of // an unsafe "double checked lock pattern" (DCLP) and flags it // as an error. I think it's a false negative, because the // outer loop is still an atomic check, the inner non-atomic // loop only serves to delay, and can't lead to a true data // race. But we provide this build-time switch to, at least, // give a way to use tsan for other checks. do { backoff(); } while (m_locked); #else backoff(); #endif } } /// Release the lock that we hold. /// void unlock () { // Fastest way to do it is with a store with "release" semantics #if defined(OIIO_USE_GCC_NEW_ATOMICS) __atomic_clear (&m_locked, __ATOMIC_RELEASE); #elif defined(USE_GCC_ATOMICS) __sync_lock_release (&m_locked); // Equivalent, x86 specific code: // __asm__ __volatile__("": : :"memory"); // m_locked = 0; #elif defined(_MSC_VER) MemoryBarrier (); m_locked = 0; #else // Otherwise, just assign zero to the atomic (but that's a full // memory barrier). *(atomic_int *)&m_locked = 0; #endif } /// Try to acquire the lock. Return true if we have it, false if /// somebody else is holding the lock. bool try_lock () { #if defined(OIIO_USE_GCC_NEW_ATOMICS) return __atomic_test_and_set (&m_locked, __ATOMIC_ACQUIRE) == 0; #elif defined(USE_GCC_ATOMICS) // GCC gives us an intrinsic that is even better -- an atomic // exchange with "acquire" barrier semantics. return __sync_lock_test_and_set (&m_locked, 1) == 0; #else // Our compare_and_swap returns true if it swapped return atomic_compare_and_exchange (&m_locked, 0, 1); #endif } /// Helper class: scoped lock for a spin_mutex -- grabs the lock upon /// construction, releases the lock when it exits scope. class lock_guard { public: lock_guard (spin_mutex &fm) : m_fm(fm) { m_fm.lock(); } ~lock_guard () { m_fm.unlock(); } private: lock_guard(); // Do not implement lock_guard(const lock_guard& other); // Do not implement lock_guard& operator = (const lock_guard& other); // Do not implement spin_mutex & m_fm; }; private: #if defined(OIIO_USE_GCC_NEW_ATOMICS) // Using the gcc >= 4.8 new atomics, we can easily do a single byte flag volatile char m_locked; ///< Atomic counter is zero if nobody holds the lock #else // Otherwise, fall back on it being an int volatile int m_locked; ///< Atomic counter is zero if nobody holds the lock #endif }; typedef spin_mutex::lock_guard spin_lock; #endif /// Spinning reader/writer mutex. This is just like spin_mutex, except /// that there are separate locking mechanisms for "writers" (exclusive /// holders of the lock, presumably because they are modifying whatever /// the lock is protecting) and "readers" (non-exclusive, non-modifying /// tasks that may access the protectee simultaneously). class spin_rw_mutex { public: /// Default constructor -- initialize to unlocked. /// spin_rw_mutex (void) { m_readers = 0; } ~spin_rw_mutex (void) { } /// Copy constructor -- initialize to unlocked. /// spin_rw_mutex (const spin_rw_mutex &) { m_readers = 0; } /// Assignment does not do anything, since lockedness should not /// transfer. const spin_rw_mutex& operator= (const spin_rw_mutex&) { return *this; } /// Acquire the reader lock. /// void read_lock () { // Spin until there are no writers active m_locked.lock(); // Register ourself as a reader ++m_readers; // Release the lock, to let other readers work m_locked.unlock(); } /// Release the reader lock. /// void read_unlock () { --m_readers; // it's atomic, no need to lock to release } /// Acquire the writer lock. /// void write_lock () { // Make sure no new readers (or writers) can start m_locked.lock(); // Spin until the last reader is done, at which point we will be // the sole owners and nobody else (reader or writer) can acquire // the resource until we release it. #if OIIO_THREAD_ALLOW_DCLP while (*(volatile int *)&m_readers > 0) ; #else while (m_readers > 0) ; #endif } /// Release the writer lock. /// void write_unlock () { // Let other readers or writers get the lock m_locked.unlock (); } /// Acquire an exclusive ("writer") lock. void lock () { write_lock(); } /// Release an exclusive ("writer") lock. void unlock () { write_unlock(); } /// Acquire a shared ("reader") lock. void lock_shared () { read_lock(); } /// Release a shared ("reader") lock. void unlock_shared () { read_unlock(); } /// Helper class: scoped read lock for a spin_rw_mutex -- grabs the /// read lock upon construction, releases the lock when it exits scope. class read_lock_guard { public: read_lock_guard (spin_rw_mutex &fm) : m_fm(fm) { m_fm.read_lock(); } ~read_lock_guard () { m_fm.read_unlock(); } private: read_lock_guard(); // Do not implement read_lock_guard(const read_lock_guard& other); // Do not implement read_lock_guard& operator = (const read_lock_guard& other); // Do not implement spin_rw_mutex & m_fm; }; /// Helper class: scoped write lock for a spin_rw_mutex -- grabs the /// read lock upon construction, releases the lock when it exits scope. class write_lock_guard { public: write_lock_guard (spin_rw_mutex &fm) : m_fm(fm) { m_fm.write_lock(); } ~write_lock_guard () { m_fm.write_unlock(); } private: write_lock_guard(); // Do not implement write_lock_guard(const write_lock_guard& other); // Do not implement write_lock_guard& operator = (const write_lock_guard& other); // Do not implement spin_rw_mutex & m_fm; }; private: OIIO_CACHE_ALIGN spin_mutex m_locked; // write lock char pad1_[OIIO_CACHE_LINE_SIZE-sizeof(spin_mutex)]; OIIO_CACHE_ALIGN atomic_int m_readers; // number of readers char pad2_[OIIO_CACHE_LINE_SIZE-sizeof(atomic_int)]; }; typedef spin_rw_mutex::read_lock_guard spin_rw_read_lock; typedef spin_rw_mutex::write_lock_guard spin_rw_write_lock; /// Mutex pool. Sometimes, we have lots of objects that need to be /// individually locked for thread safety, but two separate objects don't /// need to lock against each other. If there are many more objects than /// threads, it's wasteful for each object to contain its own mutex. So a /// solution is to make a mutex_pool -- a collection of several mutexes. /// Each object uses a hash to choose a consistent mutex for itself, but /// which will be unlikely to be locked simultaneously by different object. /// Semantically, it looks rather like an associative array of mutexes. We /// also ensure that the mutexes are all on different cache lines, to ensure /// that they don't exhibit false sharing. Try to choose Bins larger than /// the expected number of threads that will be simultaneously locking /// mutexes. template class mutex_pool { public: mutex_pool () { } Mutex& operator[] (const Key &key) { return m_mutex[m_hash(key) % Bins].m; } private: // Helper type -- force cache line alignment. This should make an array // of these also have padding so that each individual mutex is aligned // to its own cache line, thus eliminating any "false sharing." struct AlignedMutex { OIIO_CACHE_ALIGN Mutex m; }; AlignedMutex m_mutex[Bins]; Hash m_hash; }; /// Simple thread group class. This is just as good as boost::thread_group, /// for the limited functionality that we use. class thread_group { public: thread_group () {} ~thread_group () { for (size_t i = 0, e = m_threads.size(); i < e; ++i) delete m_threads[i]; } void add_thread (thread *t) { if (t) { lock_guard lock (m_mutex); m_threads.push_back (t); } } template thread *create_thread (FUNC func) { lock_guard lock (m_mutex); thread *t = new thread (func); m_threads.push_back (t); return t; } void join_all () { lock_guard lock (m_mutex); for (size_t i = 0, e = m_threads.size(); i < e; ++i) { if (m_threads[i]->joinable()) m_threads[i]->join(); } } size_t size () { lock_guard lock (m_mutex); return m_threads.size(); } private: mutex m_mutex; std::vector m_threads; }; OIIO_NAMESPACE_END #endif // OPENIMAGEIO_THREAD_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/refcnt.h0000644000175000017500000001435713151711064022277 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ ///////////////////////////////////////////////////////////////////////// /// \file /// /// Wrappers and utilities for reference counting. ///////////////////////////////////////////////////////////////////////// #ifndef OPENIMAGEIO_REFCNT_H #define OPENIMAGEIO_REFCNT_H #include #if OIIO_CPLUSPLUS_VERSION >= 11 # include #else # include #endif OIIO_NAMESPACE_BEGIN #if OIIO_CPLUSPLUS_VERSION < 11 using boost::shared_ptr; #else using std::shared_ptr; #endif /// A simple intrusive pointer, modeled after std::shared_ptr and /// boost::intrusive_ptr. template class intrusive_ptr { public: typedef T element_type; /// Default ctr intrusive_ptr () OIIO_NOEXCEPT : m_ptr(NULL) { } /// Construct from a raw pointer (presumed to be just now allocated, /// and now owned by us). intrusive_ptr (T *ptr) : m_ptr(ptr) { if (m_ptr) intrusive_ptr_add_ref(m_ptr); } /// Construct from another intrusive_ptr. intrusive_ptr (const intrusive_ptr &r) : m_ptr(r.get()) { if (m_ptr) intrusive_ptr_add_ref (m_ptr); } #if OIIO_CPLUSPLUS_VERSION >= 11 /// Move construct from another intrusive_ptr. intrusive_ptr (intrusive_ptr &&r) OIIO_NOEXCEPT : m_ptr(r.get()) { r.m_ptr = NULL; } #endif /// Destructor ~intrusive_ptr () { if (m_ptr) intrusive_ptr_release (m_ptr); } /// Assign from intrusive_ptr intrusive_ptr & operator= (const intrusive_ptr &r) { intrusive_ptr(r).swap (*this); return *this; } #if OIIO_CPLUSPLUS_VERSION >= 11 /// Move assignment from intrusive_ptr intrusive_ptr & operator= (intrusive_ptr&& r) OIIO_NOEXCEPT { intrusive_ptr (static_cast(r)).swap(*this); return *this; } #endif /// Reset to null reference void reset () OIIO_NOEXCEPT { if (m_ptr) { intrusive_ptr_release (m_ptr); m_ptr = NULL; } } /// Reset to point to a pointer void reset (T *r) { if (r != m_ptr) { if (r) intrusive_ptr_add_ref (r); if (m_ptr) intrusive_ptr_release (m_ptr); m_ptr = r; } } /// Swap intrusive pointers void swap (intrusive_ptr &r) OIIO_NOEXCEPT { T *tmp = m_ptr; m_ptr = r.m_ptr; r.m_ptr = tmp; } /// Dereference T& operator*() const { DASSERT (m_ptr); return *m_ptr; } /// Dereference T* operator->() const { DASSERT (m_ptr); return m_ptr; } /// Get raw pointer T* get() const OIIO_NOEXCEPT { return m_ptr; } /// Cast to bool to detect whether it points to anything operator bool () const OIIO_NOEXCEPT { return m_ptr != NULL; } private: T* m_ptr; // the raw pointer }; /// Mix-in class that adds a reference count, implemented as an atomic /// counter. class RefCnt { protected: // Declare RefCnt constructors and destructors protected because they // should only be called implicitly from within child class constructors or // destructors. In particular, this prevents users from deleting a RefCnt* // which is important because the destructor is non-virtual. RefCnt () { m_refcnt = 0; } /// Define copy constructor to NOT COPY reference counts! Copying a /// struct doesn't change how many other things point to it. RefCnt (RefCnt&) { m_refcnt = 0; } ~RefCnt () {} public: /// Add a reference /// void _incref () const { ++m_refcnt; } /// Delete a reference, return true if that was the last reference. /// bool _decref () const { return (--m_refcnt) == 0; } /// Define operator= to NOT COPY reference counts! Assigning a struct /// doesn't change how many other things point to it. const RefCnt & operator= (const RefCnt&) const { return *this; } private: mutable atomic_int m_refcnt; }; /// Implementation of intrusive_ptr_add_ref, which is needed for /// any class that you use with intrusive_ptr. template inline void intrusive_ptr_add_ref (T *x) { x->_incref (); } /// Implementation of intrusive_ptr_release, which is needed for /// any class that you use with intrusive_ptr. template inline void intrusive_ptr_release (T *x) { if (x->_decref ()) delete x; } // Note that intrusive_ptr_add_ref and intrusive_ptr_release MUST be a // templated on the full type, so that they pass the right address to // 'delete' and destroy the right type. If you try to just // 'inline void intrusive_ptr_release (RefCnt *x)', that might seem // clever, but it will end up getting the address of (and destroying) // just the inherited RefCnt sub-object, not the full subclass you // meant to delete and destroy. OIIO_NAMESPACE_END #endif // OPENIMAGEIO_REFCNT_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/imageio.h0000644000175000017500000022564513151711064022434 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Based on BSD-licensed software Copyright 2004 NVIDIA Corp. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ ///////////////////////////////////////////////////////////////////////////// /// \file /// /// Provides a simple API that abstracts the reading and writing of /// images. Subclasses, which may be found in DSO/DLL's, implement /// particular formats. /// ///////////////////////////////////////////////////////////////////////////// #ifndef OPENIMAGEIO_IMAGEIO_H #define OPENIMAGEIO_IMAGEIO_H #if defined(_MSC_VER) // Ignore warnings about DLL exported classes with member variables that are template classes. // This happens with the std::vector and std::string members of the classes below. # pragma warning (disable : 4251) #endif #include #include #include #include #include "export.h" #include "oiioversion.h" #include "platform.h" #include "typedesc.h" /* Needed for TypeDesc definition */ #include "paramlist.h" #include "array_view.h" OIIO_NAMESPACE_BEGIN class DeepData; /// Type we use for stride lengths. This is only used to designate /// pixel, scanline, tile, or image plane sizes in user-allocated memory, /// so it doesn't need to represent sizes larger than can be malloced, /// therefore ptrdiff_t seemed right. typedef ptrdiff_t stride_t; /// Type we use to express how many pixels (or bytes) constitute an image, /// tile, or scanline. Needs to be large enough to handle very big images /// (which we presume could be > 4GB). #if defined(LINUX64) || defined(_WIN64) || defined(__x86_64__) /* add others if we know for sure size_t is ok */ typedef size_t imagesize_t; #else typedef unsigned long long imagesize_t; #endif /// Special value to indicate a stride length that should be /// auto-computed. const stride_t AutoStride = std::numeric_limits::min(); /// Pointer to a function called periodically by read_image and /// write_image. This can be used to implement progress feedback, etc. /// It takes an opaque data pointer (passed to read_image/write_image) /// and a float giving the portion of work done so far. It returns a /// bool, which if 'true' will STOP the read or write. typedef bool (*ProgressCallback)(void *opaque_data, float portion_done); typedef ParamValue ImageIOParameter; typedef ParamValueList ImageIOParameterList; /// ImageSpec describes the data format of an image -- /// dimensions, layout, number and meanings of image channels. class OIIO_API ImageSpec { public: int x, y, z; ///< origin (upper left corner) of pixel data int width; ///< width of the pixel data window int height; ///< height of the pixel data window int depth; ///< depth of pixel data, >1 indicates a "volume" int full_x; ///< origin of the full (display) window int full_y; ///< origin of the full (display) window int full_z; ///< origin of the full (display) window int full_width; ///< width of the full (display) window int full_height; ///< height of the full (display) window int full_depth; ///< depth of the full (display) window int tile_width; ///< tile width (0 for a non-tiled image) int tile_height; ///< tile height (0 for a non-tiled image) int tile_depth; ///< tile depth (0 for a non-tiled image, ///< 1 for a non-volume image) int nchannels; ///< number of image channels, e.g., 4 for RGBA TypeDesc format; ///< data format of the channels std::vector channelformats; ///< Optional per-channel formats std::vector channelnames; ///< Names for each channel, ///< e.g., {"R","G","B","A"} int alpha_channel; ///< Index of alpha channel, or -1 if not known int z_channel; ///< Index of depth channel, or -1 if not known bool deep; ///< Contains deep data /// The above contains all the information that is likely needed for /// every image file, and common to all formats. Rather than bloat /// this structure, customize it for new formats, or break back /// compatibility as we think of new things, we provide extra_attribs /// as a holder for any other properties of the image. The public /// functions attribute and find_attribute may be used to access /// these data. Note, however, that the names and semantics of such /// extra attributes are plugin-dependent and are not enforced by /// the imageio library itself. ImageIOParameterList extra_attribs; ///< Additional attributes /// Constructor: given just the data format, set all other fields to /// something reasonable. ImageSpec (TypeDesc format = TypeDesc::UNKNOWN); /// Constructor for simple 2D scanline image with nothing special. /// If fmt is not supplied, default to unsigned 8-bit data. ImageSpec (int xres, int yres, int nchans, TypeDesc fmt = TypeDesc::UINT8); /// Set the data format. void set_format (TypeDesc fmt); /// Set the channelnames to reasonable defaults ("R", "G", "B", "A"), /// and alpha_channel, based on the number of channels. void default_channel_names (); /// Return the number of bytes for each channel datum, assuming they /// are all stored using the data format given by this->format. size_t channel_bytes() const { return format.size(); } /// Return the number of bytes needed for the single specified /// channel. If native is false (default), compute the size of one /// channel of this->format, but if native is true, compute the size /// of the channel in terms of the "native" data format of that /// channel as stored in the file. size_t channel_bytes (int chan, bool native=false) const; /// Return the number of bytes for each pixel (counting all channels). /// If native is false (default), assume all channels are in /// this->format, but if native is true, compute the size of a pixel /// in the "native" data format of the file (these may differ in /// the case of per-channel formats). /// This will return std::numeric_limits::max() in the /// event of an overflow where it's not representable in a size_t. size_t pixel_bytes (bool native=false) const; /// Return the number of bytes for just the subset of channels in /// each pixel described by [chbegin,chend). /// If native is false (default), assume all channels are in /// this->format, but if native is true, compute the size of a pixel /// in the "native" data format of the file (these may differ in /// the case of per-channel formats). /// This will return std::numeric_limits::max() in the /// event of an overflow where it's not representable in a size_t. size_t pixel_bytes (int chbegin, int chend, bool native=false) const; /// Return the number of bytes for each scanline. This will return /// std::numeric_limits::max() in the event of an /// overflow where it's not representable in an imagesize_t. /// If native is false (default), assume all channels are in /// this->format, but if native is true, compute the size of a pixel /// in the "native" data format of the file (these may differ in /// the case of per-channel formats). imagesize_t scanline_bytes (bool native=false) const; /// Return the number of pixels for a tile. This will return /// std::numeric_limits::max() in the event of an /// overflow where it's not representable in an imagesize_t. imagesize_t tile_pixels () const; /// Return the number of bytes for each a tile of the image. This /// will return std::numeric_limits::max() in the event /// of an overflow where it's not representable in an imagesize_t. /// If native is false (default), assume all channels are in /// this->format, but if native is true, compute the size of a pixel /// in the "native" data format of the file (these may differ in /// the case of per-channel formats). imagesize_t tile_bytes (bool native=false) const; /// Return the number of pixels for an entire image. This will /// return std::numeric_limits::max() in the event of /// an overflow where it's not representable in an imagesize_t. imagesize_t image_pixels () const; /// Return the number of bytes for an entire image. This will /// return std::numeric_limits::max() in the event of /// an overflow where it's not representable in an imagesize_t. /// If native is false (default), assume all channels are in /// this->format, but if native is true, compute the size of a pixel /// in the "native" data format of the file (these may differ in /// the case of per-channel formats). imagesize_t image_bytes (bool native=false) const; /// Verify that on this platform, a size_t is big enough to hold the /// number of bytes (and pixels) in a scanline, a tile, and the /// whole image. If this returns false, the image is much too big /// to allocate and read all at once, so client apps beware and check /// these routines for overflows! bool size_t_safe() const { const imagesize_t big = std::numeric_limits::max(); return image_bytes() < big && scanline_bytes() < big && tile_bytes() < big; } /// Adjust the stride values, if set to AutoStride, to be the right /// sizes for contiguous data with the given format, channels, /// width, height. static void auto_stride (stride_t &xstride, stride_t &ystride, stride_t &zstride, stride_t channelsize, int nchannels, int width, int height) { if (xstride == AutoStride) xstride = nchannels * channelsize; if (ystride == AutoStride) ystride = xstride * width; if (zstride == AutoStride) zstride = ystride * height; } /// Adjust the stride values, if set to AutoStride, to be the right /// sizes for contiguous data with the given format, channels, /// width, height. static void auto_stride (stride_t &xstride, stride_t &ystride, stride_t &zstride, TypeDesc format, int nchannels, int width, int height) { auto_stride (xstride, ystride, zstride, format.size(), nchannels, width, height); } /// Adjust xstride, if set to AutoStride, to be the right size for /// contiguous data with the given format and channels. static void auto_stride (stride_t &xstride, TypeDesc format, int nchannels) { if (xstride == AutoStride) xstride = nchannels * format.size(); } /// Add an optional attribute to the extra attribute list /// void attribute (string_view name, TypeDesc type, const void *value); /// Add an optional attribute to the extra attribute list. /// void attribute (string_view name, TypeDesc type, string_view value); /// Add an unsigned int attribute /// void attribute (string_view name, unsigned int value) { attribute (name, TypeDesc::UINT, &value); } /// Add an int attribute /// void attribute (string_view name, int value) { attribute (name, TypeDesc::INT, &value); } /// Add a float attribute /// void attribute (string_view name, float value) { attribute (name, TypeDesc::FLOAT, &value); } /// Add a string attribute /// void attribute (string_view name, string_view value) { const char *s = value.c_str(); attribute (name, TypeDesc::STRING, &s); } /// Remove the specified attribute from the list of extra_attribs. If /// not found, do nothing. If searchtype is anything but UNKNOWN, /// restrict matches to only those of the given type. If casesensitive /// is true, the name search will be case-sensitive, otherwise the name /// search will be performed without regard to case (this is the /// default). void erase_attribute (string_view name, TypeDesc searchtype=TypeDesc::UNKNOWN, bool casesensitive=false); /// Search for an attribute of the given name in the list of /// extra_attribs. If searchtype is anything but UNKNOWN, restrict /// matches to only those of the given type. If casesensitive is true, /// the name search will be case-sensitive, otherwise the name search /// will be performed without regard to case (this is the default). ImageIOParameter * find_attribute (string_view name, TypeDesc searchtype=TypeDesc::UNKNOWN, bool casesensitive=false); const ImageIOParameter *find_attribute (string_view name, TypeDesc searchtype=TypeDesc::UNKNOWN, bool casesensitive=false) const; /// Search for the named attribute and return a pointer to an /// ImageIOParameter record, or NULL if not found. This variety of /// find_attribute() can retrieve items such as "width", which are part /// of the ImageSpec, but not in extra_attribs. The tmpparam is a /// temporary storage area owned by the caller, which is used as /// temporary buffer in cases where the information does not correspond /// to an actual extra_attribs (in this case, the return value will be /// &tmpparam). const ImageIOParameter * find_attribute (string_view name, ImageIOParameter &tmpparam, TypeDesc searchtype=TypeDesc::UNKNOWN, bool casesensitive=false) const; /// Simple way to get an integer attribute, with default provided. /// Automatically will return an int even if the data is really /// unsigned, short, or byte. int get_int_attribute (string_view name, int defaultval=0) const; /// Simple way to get a float attribute, with default provided. /// Automatically will return a float even if the data is really /// double or half. float get_float_attribute (string_view name, float defaultval=0) const; /// Simple way to get a string attribute, with default provided. /// string_view get_string_attribute (string_view name, string_view defaultval = string_view()) const; /// For a given parameter p, format the value nicely as a string. If /// 'human' is true, use especially human-readable explanations (units, /// or decoding of values) for certain known metadata. static std::string metadata_val (const ImageIOParameter &p, bool human=false); /// Convert ImageSpec class into XML string. /// std::string to_xml () const; /// Get an ImageSpec class from XML string. /// void from_xml (const char *xml); /// Helper function to verify that the given pixel range exactly covers /// a set of tiles. Also returns false if the spec indicates that the /// image isn't tiled at all. bool valid_tile_range (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend) { return (tile_width && ((xbegin-x) % tile_width) == 0 && ((ybegin-y) % tile_height) == 0 && ((zbegin-z) % tile_depth) == 0 && (((xend-x) % tile_width) == 0 || (xend-x) == width) && (((yend-y) % tile_height) == 0 || (yend-y) == height) && (((zend-z) % tile_depth) == 0 || (zend-z) == depth)); } /// Return teh channelformat of the given channel. TypeDesc channelformat (int chan) const { return chan >= 0 && chan < (int)channelformats.size() ? channelformats[chan] : format; } /// Fill in an array of channel formats describing all channels in /// the image. (Note that this differs slightly from the member /// data channelformats, which is empty if there are not separate /// per-channel formats.) void get_channelformats (std::vector &formats) const { formats = channelformats; if ((int)formats.size() < nchannels) formats.resize (nchannels, format); } }; /// ImageInput abstracts the reading of an image file in a file /// format-agnostic manner. class OIIO_API ImageInput { public: /// Create an ImageInput subclass instance that is able to read /// the given file and open it, returning the opened ImageInput if /// successful. If it fails, return NULL and set an error that can /// be retrieved by OpenImageIO::geterror(). /// /// The 'config', if not NULL, points to an ImageSpec giving /// requests or special instructions. ImageInput implementations /// are free to not respond to any such requests, so the default /// implementation is just to ignore config. /// /// open() will first try to make an ImageInput corresponding to /// the format implied by the file extension (for example, "foo.tif" /// will try the TIFF plugin), but if one is not found or if the /// inferred one does not open the file, every known ImageInput type /// will be tried until one is found that will open the file. static ImageInput *open (const std::string &filename, const ImageSpec *config = NULL); /// Create and return an ImageInput implementation that is willing /// to read the given file. The plugin_searchpath parameter is a /// colon-separated list of directories to search for ImageIO plugin /// DSO/DLL's (not a searchpath for the image itself!). This will /// actually just try every imageio plugin it can locate, until it /// finds one that's able to open the file without error. This just /// creates the ImageInput, it does not open the file. /// /// If the caller intends to immediately open the file, then it is /// simpler to call static ImageInput::open(). static ImageInput *create (const std::string &filename, const std::string &plugin_searchpath=""); /// Destroy an ImageInput that was created using ImageInput::create() or /// the static open(). For some systems (Windows, I'm looking at you), /// it is not necessarily safe to allocate memory in one DLL and free it /// in another, so directly calling 'delete' on an ImageInput allocated /// by create() or open() may be unusafe, but passing it to destroy() /// should be safe. static void destroy (ImageInput *x); ImageInput (); virtual ~ImageInput (); /// Return the name of the format implemented by this class. /// virtual const char *format_name (void) const = 0; /// Given the name of a 'feature', return whether this ImageInput /// supports input of images with the given properties. Most queries /// will simply return 0 for "doesn't support" and nonzero for "supports /// it", but it is acceptable to have queries return other nonzero /// integers to indicate varying degrees of support or limits (but /// should be clearly documented as such). /// /// Feature names that ImageIO plugins are expected to recognize /// include: /// "arbitrary_metadata" Does this format allow metadata with /// arbitrary names and types? /// "exif" Can this format store Exif camera data? /// "iptc" Can this format store IPTC data? /// "procedural" Can this format create images without reading /// from a disk file? /// /// Note that main advantage of this approach, versus having /// separate individual supports_foo() methods, is that this allows /// future expansion of the set of possible queries without changing /// the API, adding new entry points, or breaking linkage /// compatibility. virtual int supports (string_view feature) const { return false; } /// Return true if the named file is file of the type for this /// ImageInput. The implementation will try to determine this as /// efficiently as possible, in most cases much less expensively /// than doing a full open(). Note that a file can appear to be of /// the right type (i.e., valid_file() returning true) but still /// fail a subsequent call to open(), such as if the contents of the /// file are truncated, nonsensical, or otherwise corrupted. virtual bool valid_file (const std::string &filename) const; /// Open file with given name. Various file attributes are put in /// newspec and a copy is also saved in this->spec. From these /// attributes, you can discern the resolution, if it's tiled, /// number of channels, and native data format. Return true if the /// file was found and opened okay. virtual bool open (const std::string &name, ImageSpec &newspec) = 0; /// Open file with given name, similar to open(name,newspec). The /// 'config' is an ImageSpec giving requests or special /// instructions. ImageInput implementations are free to not /// respond to any such requests, so the default implementation is /// just to ignore config and call regular open(name,newspec). virtual bool open (const std::string &name, ImageSpec &newspec, const ImageSpec & /*config*/) { return open(name,newspec); } /// Return a reference to the image format specification of the /// current subimage/MIPlevel. Note that the contents of the spec /// are invalid before open() or after close(), and may change with /// a call to seek_subimage(). const ImageSpec &spec (void) const { return m_spec; } /// Close an image that we are totally done with. /// virtual bool close () = 0; /// Returns the index of the subimage that is currently being read. /// The first subimage (or the only subimage, if there is just one) /// is number 0. virtual int current_subimage (void) const { return 0; } /// Returns the index of the MIPmap image that is currently being read. /// The highest-res MIP level (or the only level, if there is just /// one) is number 0. virtual int current_miplevel (void) const { return 0; } /// Seek to the given subimage and MIP-map level within the open /// image file. The first subimage of the file has index 0, the /// highest-resolution MIP level has index 0. Return true on /// success, false on failure (including that there is not a /// subimage or MIP level with the specified index). The new /// subimage's vital statistics are put in newspec (and also saved /// in this->spec). The reader is expected to give the appearance /// of random access to subimages and MIP levels -- in other words, /// if it can't randomly seek to the given subimage/level, it should /// transparently close, reopen, and sequentially read through prior /// subimages and levels. virtual bool seek_subimage (int subimage, int miplevel, ImageSpec &newspec) { if (subimage == current_subimage() && miplevel == current_miplevel()) { newspec = spec(); return true; } return false; } /// Seek to the given subimage -- backwards-compatible call that /// doesn't worry about MIP-map levels at all. bool seek_subimage (int subimage, ImageSpec &newspec) { return seek_subimage (subimage, 0 /* miplevel */, newspec); } /// Read the scanline that includes pixels (*,y,z) into data, /// converting if necessary from the native data format of the file /// into the 'format' specified (z==0 for non-volume images). The /// stride value gives the data spacing of adjacent pixels (in /// bytes). Strides set to AutoStride imply 'contiguous' data, i.e., /// xstride == spec.nchannels*format.size() /// If format is TypeDesc::UNKNOWN, then rather than converting to /// format, it will just copy pixels in the file's native data layout /// (including, possibly, per-channel data formats). /// The reader is expected to give the appearance of random access -- /// in other words, if it can't randomly seek to the given scanline, /// it should transparently close, reopen, and sequentially read /// through prior scanlines. The base ImageInput class has a /// default implementation that calls read_native_scanline and then /// does appropriate format conversion, so there's no reason for /// each format plugin to override this method. virtual bool read_scanline (int y, int z, TypeDesc format, void *data, stride_t xstride=AutoStride); /// /// Simple read_scanline reads to contiguous float pixels. bool read_scanline (int y, int z, float *data) { return read_scanline (y, z, TypeDesc::FLOAT, data); } /// Read multiple scanlines that include pixels (*,y,z) for all /// ybegin <= y < yend, into data, using the strides given and /// converting to the requested data format (unless format is /// TypeDesc::UNKNOWN, in which case pixels will be copied in the /// native data layout, including per-channel data formats). This /// is analogous to read_scanline except that it may be used to read /// more than one scanline at a time (which, for some formats, may /// be able to be done much more efficiently or in parallel). virtual bool read_scanlines (int ybegin, int yend, int z, TypeDesc format, void *data, stride_t xstride=AutoStride, stride_t ystride=AutoStride); /// Read multiple scanlines that include pixels (*,y,z) for all /// ybegin <= y < yend, into data, using the strides given and /// converting to the requested data format (unless format is /// TypeDesc::UNKNOWN, in which case pixels will be copied in the /// native data layout, including per-channel data formats). Only /// channels [chbegin,chend) will be read/copied (chbegin=0, /// chend=spec.nchannels reads all channels, yielding equivalent /// behavior to the simpler variant of read_scanlines). virtual bool read_scanlines (int ybegin, int yend, int z, int chbegin, int chend, TypeDesc format, void *data, stride_t xstride=AutoStride, stride_t ystride=AutoStride); /// Read the tile whose upper-left origin is (x,y,z) into data, /// converting if necessary from the native data format of the file /// into the 'format' specified. (z==0 for non-volume images.) The /// stride values give the data spacing of adjacent pixels, scanlines, /// and volumetric slices (measured in bytes). Strides set to AutoStride /// imply 'contiguous' data in the shape of a full tile, i.e., /// xstride == spec.nchannels*format.size() /// ystride == xstride*spec.tile_width /// zstride == ystride*spec.tile_height /// If format is TypeDesc::UNKNOWN, then rather than converting to /// format, it will just copy pixels in the file's native data layout /// (including, possibly, per-channel data formats). /// The reader is expected to give the appearance of random access /// -- in other words, if it can't randomly seek to the given tile, /// it should transparently close, reopen, and sequentially read /// through prior tiles. The base ImageInput class has a default /// implementation that calls read_native_tile and then does /// appropriate format conversion, so there's no reason for each /// format plugin to override this method. virtual bool read_tile (int x, int y, int z, TypeDesc format, void *data, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride); /// /// Simple read_tile reads to contiguous float pixels. bool read_tile (int x, int y, int z, float *data) { return read_tile (x, y, z, TypeDesc::FLOAT, data, AutoStride, AutoStride, AutoStride); } /// Read the block of multiple tiles that include all pixels in /// [xbegin,xend) X [ybegin,yend) X [zbegin,zend), into data, using /// the strides given and converting to the requested data format /// (unless format is TypeDesc::UNKNOWN, in which case pixels will /// be copied in the native data layout, including per-channel data /// formats). This is analogous to read_tile except that it may be /// used to read more than one tile at a time (which, for some /// formats, may be able to be done much more efficiently or in /// parallel). The begin/end pairs must correctly delineate tile /// boundaries, with the exception that it may also be the end of /// the image data if the image resolution is not a whole multiple /// of the tile size. /// The stride values give the data spacing of adjacent pixels, /// scanlines, and volumetric slices (measured in bytes). Strides set to /// AutoStride imply 'contiguous' data in the shape of the [begin,end) /// region, i.e., /// xstride == spec.nchannels*format.size() /// ystride == xstride * (xend-xbegin) /// zstride == ystride * (yend-ybegin) virtual bool read_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, void *data, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride); /// Read the block of multiple tiles that include all pixels in /// [xbegin,xend) X [ybegin,yend) X [zbegin,zend), into data, using /// the strides given and converting to the requested data format /// (unless format is TypeDesc::UNKNOWN, in which case pixels will /// be copied in the native data layout, including per-channel data /// formats). Only channels [chbegin,chend) will be read/copied /// (chbegin=0, chend=spec.nchannels reads all channels, yielding /// equivalent behavior to the simpler variant of read_tiles). virtual bool read_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc format, void *data, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride); /// Read the entire image of spec.width x spec.height x spec.depth /// pixels into data (which must already be sized large enough for /// the entire image) with the given strides and in the desired /// format. Read tiles or scanlines automatically. Strides set to /// AutoStride imply 'contiguous' data, i.e., /// xstride == spec.nchannels*format.size() /// ystride == xstride*spec.width /// zstride == ystride*spec.height /// If format is TypeDesc::UNKNOWN, then rather than converting to /// format, it will just copy pixels in the file's native data layout /// (including, possibly, per-channel data formats). /// Because this may be an expensive operation, a progress callback /// may be passed. Periodically, it will be called as follows: /// progress_callback (progress_callback_data, float done) /// where 'done' gives the portion of the image virtual bool read_image (TypeDesc format, void *data, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride, ProgressCallback progress_callback=NULL, void *progress_callback_data=NULL); /// Read the entire image of spec.width x spec.height x spec.depth /// pixels into data (which must already be sized large enough for /// the entire image) with the given strides and in the desired /// format. Read tiles or scanlines automatically. Only channels /// [chbegin,chend) will be read/copied (chbegin=0, chend=spec.nchannels /// reads all channels, yielding equivalent behavior to the simpler /// variant of read_image). virtual bool read_image (int chbegin, int chend, TypeDesc format, void *data, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride, ProgressCallback progress_callback=NULL, void *progress_callback_data=NULL); /// /// Simple read_image reads to contiguous float pixels. bool read_image (float *data) { return read_image (TypeDesc::FLOAT, data); } /// read_native_scanline is just like read_scanline, except that it /// keeps the data in the native format of the disk file and always /// reads into contiguous memory (no strides). It's up to the user to /// have enough space allocated and know what to do with the data. /// IT IS EXPECTED THAT EACH FORMAT PLUGIN WILL OVERRIDE THIS METHOD. virtual bool read_native_scanline (int y, int z, void *data) = 0; /// read_native_scanlines is just like read_scanlines, except that /// it keeps the data in the native format of the disk file and /// always reads into contiguous memory (no strides). It's up to /// the user to have enough space allocated and know what to do with /// the data. If a format reader subclass does not override this /// method, the default implementation it will simply be a loop /// calling read_native_scanline for each scanline. virtual bool read_native_scanlines (int ybegin, int yend, int z, void *data); /// A variant of read_native_scanlines that reads only channels /// [chbegin,chend). If a format reader subclass does /// not override this method, the default implementation will simply /// call the all-channel version of read_native_scanlines into a /// temporary buffer and copy the subset of channels. virtual bool read_native_scanlines (int ybegin, int yend, int z, int chbegin, int chend, void *data); /// read_native_tile is just like read_tile, except that it /// keeps the data in the native format of the disk file and always /// read into contiguous memory (no strides). It's up to the user to /// have enough space allocated and know what to do with the data. /// IT IS EXPECTED THAT EACH FORMAT PLUGIN WILL OVERRIDE THIS METHOD /// IF IT SUPPORTS TILED IMAGES. virtual bool read_native_tile (int x, int y, int z, void *data); /// read_native_tiles is just like read_tiles, except that it keeps /// the data in the native format of the disk file and always reads /// into contiguous memory (no strides). It's up to the caller to /// have enough space allocated and know what to do with the data. /// If a format reader does not override this method, the default /// implementation it will simply be a loop calling read_native_tile /// for each tile in the block. virtual bool read_native_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, void *data); /// A variant of read_native_tiles that reads only channels /// [chbegin,chend). If a format reader subclass does /// not override this method, the default implementation will simply /// call the all-channel version of read_native_tiles into a /// temporary buffer and copy the subset of channels. virtual bool read_native_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, void *data); /// Read native deep data from multiple scanlines that include /// pixels (*,y,z) for all ybegin <= y < yend, into deepdata. Only /// channels [chbegin, chend) will be read (chbegin=0, /// chend=spec.nchannels reads all channels). virtual bool read_native_deep_scanlines (int ybegin, int yend, int z, int chbegin, int chend, DeepData &deepdata); /// Read the block of multiple native deep data tiles that include /// all pixels in [xbegin,xend) X [ybegin,yend) X [zbegin,zend), /// into deepdata. Only channels [chbegin,chend) will /// be read (chbegin=0, chend=spec.nchannels reads all channels). virtual bool read_native_deep_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, DeepData &deepdata); /// Read the entire deep data image of spec.width x spec.height x /// spec.depth pixels, all channels, into deepdata. virtual bool read_native_deep_image (DeepData &deepdata); /// General message passing between client and image input server /// virtual int send_to_input (const char *format, ...); int send_to_client (const char *format, ...); /// If any of the API routines returned false indicating an error, /// this routine will return the error string (and clear any error /// flags). If no error has occurred since the last time geterror() /// was called, it will return an empty string. std::string geterror () const { std::string e = m_errmessage; m_errmessage.clear (); return e; } /// An ImageInput::Creator is a function that creates and returns an /// ImageInput. Once invoked, the resulting ImageInput is owned by /// the caller, who is responsible for deleting it when done with it. typedef ImageInput* (*Creator)(); /// Error reporting for the plugin implementation: call this with /// printf-like arguments. Note however that this is fully typesafe! // void error (const char *format, ...) const; TINYFORMAT_WRAP_FORMAT (void, error, const, std::ostringstream msg;, msg, append_error(msg.str());) /// Set the current thread-spawning policy: the maximum number of /// threads that may be spawned by ImageInput internals. A value of 1 /// means all work will be done by the calling thread; 0 means to use /// the global OIIO::attribute("threads") value. void threads (int n) { m_threads = n; } /// Retrieve the current thread-spawning policy. int threads () const { return m_threads; } protected: ImageSpec m_spec; // format spec of the current open subimage/MIPlevel private: mutable std::string m_errmessage; // private storage of error message int m_threads; // Thread policy void append_error (const std::string& message) const; // add to m_errmessage static ImageInput *create (const std::string &filename, bool do_open, const std::string &plugin_searchpath); }; /// ImageOutput abstracts the writing of an image file in a file /// format-agnostic manner. class OIIO_API ImageOutput { public: /// Create an ImageOutput that will write to a file, with the format /// inferred from the extension of the name. The plugin_searchpath /// parameter is a colon-separated list of directories to search for /// ImageIO plugin DSO/DLL's. This just creates the ImageOutput, it /// does not open the file. static ImageOutput *create (const std::string &filename, const std::string &plugin_searchpath=""); /// Destroy an ImageOutput that was created using ImageOutput::create(). /// For some systems (Windows, I'm looking at you), it is not /// necessarily safe to allocate memory in one DLL and free it in /// another, so directly calling 'delete' on an ImageOutput allocated by /// create() may be unusafe, but passing it to destroy() should be safe. static void destroy (ImageOutput *x); ImageOutput (); virtual ~ImageOutput (); /// Return the name of the format implemented by this class. /// virtual const char *format_name (void) const = 0; // Overrride these functions in your derived output class // to inform the client which formats are supported /// Given the name of a 'feature', return whether this ImageOutput /// supports output of images with the given properties. Most queries /// will simply return 0 for "doesn't support" and nonzero for "supports /// it", but it is acceptable to have queries return other nonzero /// integers to indicate varying degrees of support or limits (but /// should be clearly documented as such). /// /// Feature names that ImageIO plugins are expected to recognize /// include: /// "tiles" Is this format able to write tiled images? /// "rectangles" Does this plugin accept arbitrary rectangular /// pixel regions, not necessarily aligned to /// scanlines or tiles? /// "random_access" May tiles or scanlines be written in /// any order (false indicates that they MUST /// be in successive order). /// "multiimage" Does this format support multiple subimages /// within a file? /// "appendsubimage" Does this format support adding subimages one at /// a time through open(name,spec,AppendSubimage)? /// If not, then open(name,subimages,specs) must /// be used instead. /// "mipmap" Does this format support multiple resolutions /// for an image/subimage? /// "volumes" Does this format support "3D" pixel arrays? /// "alpha" Can this format support an alpha channel? /// "nchannels" Can this format support arbitrary number of /// channels (beyond RGBA)? /// "rewrite" May the same scanline or tile be sent more than /// once? (Generally, this will be true for /// plugins that implement interactive display.) /// "empty" Does this plugin support passing a NULL data /// pointer to write_scanline or write_tile to /// indicate that the entire data block is zero? /// "channelformats" Does the plugin/format support per-channel /// data formats? /// "displaywindow" Does the format support display ("full") windows /// distinct from the pixel data window? /// "origin" Does the format support a nonzero x,y,z /// origin of the pixel data window? /// "negativeorigin" Does the format support negative x,y,z /// and full_{x,y,z} origin values? /// "deepdata" Deep (multi-sample per pixel) data /// "arbitrary_metadata" Does this format allow metadata with /// arbitrary names and types? /// "exif" Can this format store Exif camera data? /// "iptc" Can this format store IPTC data? /// /// Note that main advantage of this approach, versus having /// separate individual supports_foo() methods, is that this allows /// future expansion of the set of possible queries without changing /// the API, adding new entry points, or breaking linkage /// compatibility. virtual int supports (string_view feature) const { return false; } enum OpenMode { Create, AppendSubimage, AppendMIPLevel }; /// Open the file with given name, with resolution and other format /// data as given in newspec. Open returns true for success, false /// for failure. Note that it is legal to call open multiple times /// on the same file without a call to close(), if it supports /// multiimage and mode is AppendSubimage, or if it supports /// MIP-maps and mode is AppendMIPLevel -- this is interpreted as /// appending a subimage, or a MIP level to the current subimage, /// respectively. virtual bool open (const std::string &name, const ImageSpec &newspec, OpenMode mode=Create) = 0; /// Open the file with given name, expecting to have a given total /// number of subimages, described by specs[0..subimages-1]. /// Return true for success, false for failure. Upon success, the /// first subimage will be open and ready for transmission of /// pixels. Subsequent subimages will be denoted with the usual /// call of open(name,spec,AppendSubimage) (and MIP levels by /// open(name,spec,AppendMIPLevel)). /// /// The purpose of this call is to accommodate format-writing /// libraries that fmust know the number and specifications of the /// subimages upon first opening the file; such formats can be /// detected by /// supports("multiimage") && !supports("appendsubimage") /// The individual specs passed to the appending open() calls for /// subsequent subimages MUST match the ones originally passed. virtual bool open (const std::string &name, int subimages, const ImageSpec *specs) { // Default implementation: just a regular open, assume that // appending will work. return open (name, specs[0]); } /// Return a reference to the image format specification of the /// current subimage. Note that the contents of the spec are /// invalid before open() or after close(). const ImageSpec &spec (void) const { return m_spec; } /// Close an image that we are totally done with. This should leave /// the plugin in a state where it could open a new file safely, /// without having to destroy the writer. virtual bool close () = 0; /// Write a full scanline that includes pixels (*,y,z). (z is /// ignored for 2D non-volume images.) The stride value gives the /// distance between successive pixels (in bytes). Strides set to /// AutoStride imply 'contiguous' data, i.e., /// xstride == spec.nchannels*format.size() /// The data are automatically converted from 'format' to the actual /// output format (as specified to open()) by this method. /// If format is TypeDesc::UNKNOWN, then rather than converting from /// format, it will just copy pixels in the file's native data layout /// (including, possibly, per-channel data formats). /// Return true for success, false for failure. It is a failure to /// call write_scanline with an out-of-order scanline if this format /// driver does not support random access. virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride=AutoStride); /// Write multiple scanlines that include pixels (*,y,z) for all /// ybegin <= y < yend, from data. This is analogous to /// write_scanline except that it may be used to write more than one /// scanline at a time (which, for some formats, may be able to be /// done much more efficiently or in parallel). virtual bool write_scanlines (int ybegin, int yend, int z, TypeDesc format, const void *data, stride_t xstride=AutoStride, stride_t ystride=AutoStride); /// Write the tile with (x,y,z) as the upper left corner. (z is /// ignored for 2D non-volume images.) The three stride values give /// the distance (in bytes) between successive pixels, scanlines, /// and volumetric slices, respectively. Strides set to AutoStride /// imply 'contiguous' data in the shape of a full tile, i.e., /// xstride == spec.nchannels*format.size() /// ystride == xstride*spec.tile_width /// zstride == ystride*spec.tile_height /// The data are automatically converted from 'format' to the actual /// output format (as specified to open()) by this method. /// If format is TypeDesc::UNKNOWN, then rather than converting from /// format, it will just copy pixels in the file's native data layout /// (including, possibly, per-channel data formats). /// Return true for success, false for failure. It is a failure to /// call write_tile with an out-of-order tile if this format driver /// does not support random access. virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride); /// Write the block of multiple tiles that include all pixels in /// [xbegin,xend) X [ybegin,yend) X [zbegin,zend). This is analogous to /// write_tile except that it may be used to write more than one tile at /// a time (which, for some formats, may be able to be done much more /// efficiently or in parallel). /// The begin/end pairs must correctly delineate tile boundaries, with /// the exception that it may also be the end of the image data if the /// image resolution is not a whole multiple of the tile size. /// The stride values give the data spacing of adjacent pixels, /// scanlines, and volumetric slices (measured in bytes). Strides set to /// AutoStride imply 'contiguous' data in the shape of the [begin,end) /// region, i.e., /// xstride == spec.nchannels*format.size() /// ystride == xstride * (xend-xbegin) /// zstride == ystride * (yend-ybegin) virtual bool write_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, const void *data, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride); /// Write a rectangle of pixels given by the range /// [xbegin,xend) X [ybegin,yend) X [zbegin,zend) /// The stride values give the data spacing of adjacent pixels, /// scanlines, and volumetric slices (measured in bytes). Strides set to /// AutoStride imply 'contiguous' data in the shape of the [begin,end) /// region, i.e., /// xstride == spec.nchannels*format.size() /// ystride == xstride * (xend-xbegin) /// zstride == ystride * (yend-ybegin) /// The data are automatically converted from 'format' to the actual /// output format (as specified to open()) by this method. If /// format is TypeDesc::UNKNOWN, it will just copy pixels assuming /// they are already in the file's native data layout (including, /// possibly, per-channel data formats). /// /// Return true for success, false for failure. It is a failure to /// call write_rectangle for a format plugin that does not return /// true for supports("rectangles"). virtual bool write_rectangle (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, const void *data, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride); /// Write the entire image of spec.width x spec.height x spec.depth /// pixels, with the given strides and in the desired format. /// Strides set to AutoStride imply 'contiguous' data, i.e., /// xstride == spec.nchannels*format.size() /// ystride == xstride*spec.width /// zstride == ystride*spec.height /// Depending on spec, write either all tiles or all scanlines. /// Assume that data points to a layout in row-major order. /// If format is TypeDesc::UNKNOWN, then rather than converting from /// format, it will just copy pixels in the file's native data layout /// (including, possibly, per-channel data formats). /// Because this may be an expensive operation, a progress callback /// may be passed. Periodically, it will be called as follows: /// progress_callback (progress_callback_data, float done) /// where 'done' gives the portion of the image virtual bool write_image (TypeDesc format, const void *data, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride, ProgressCallback progress_callback=NULL, void *progress_callback_data=NULL); /// Write deep scanlines containing pixels (*,y,z), for all y in /// [ybegin,yend), to a deep file. virtual bool write_deep_scanlines (int ybegin, int yend, int z, const DeepData &deepdata); /// Write the block of deep tiles that include all pixels in /// [xbegin,xend) X [ybegin,yend) X [zbegin,zend). /// The begin/end pairs must correctly delineate tile boundaries, /// with the exception that it may also be the end of the image data /// if the image resolution is not a whole multiple of the tile size. virtual bool write_deep_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, const DeepData &deepdata); /// Write the entire deep image denoted by data. virtual bool write_deep_image (const DeepData &deepdata); /// Read the current subimage of 'in', and write it as the next /// subimage of *this, in a way that is efficient and does not alter /// pixel values, if at all possible. Both in and this must be a /// properly-opened ImageInput and ImageOutput, respectively, and /// their current images must match in size and number of channels. /// Return true if it works ok, false if for some reason the /// operation wasn't possible. /// /// If a particular ImageOutput implementation does not supply a /// copy_image method, it will inherit the default implementation, /// which is to simply read scanlines or tiles from 'in' and write /// them to *this. However, some ImageIO implementations may have a /// special technique for directly copying raw pixel data from the /// input to the output, when both input and output are the SAME /// file type and the same data format. This can be more efficient /// than in->read_image followed by out->write_image, and avoids any /// unintended pixel alterations, especially for formats that use /// lossy compression. virtual bool copy_image (ImageInput *in); /// General message passing between client and image output server /// virtual int send_to_output (const char *format, ...); int send_to_client (const char *format, ...); /// If any of the API routines returned false indicating an error, /// this routine will return the error string (and clear any error /// flags). If no error has occurred since the last time geterror() /// was called, it will return an empty string. std::string geterror () const { std::string e = m_errmessage; m_errmessage.clear (); return e; } /// An ImageOutput::Creator is a function that creates and returns an /// ImageOutput. Once invoked, the resulting ImageOutput is owned by /// the caller, who is responsible for deleting it when done with it. typedef ImageOutput* (*Creator)(); /// Error reporting for the plugin implementation: call this with /// printf-like arguments. Note however that this is fully typesafe! /// void error (const char *format, ...) TINYFORMAT_WRAP_FORMAT (void, error, const, std::ostringstream msg;, msg, append_error(msg.str());) /// Set the current thread-spawning policy: the maximum number of /// threads that may be spawned by ImageOutput internals. A value of 1 /// means all work will be done by the calling thread; 0 means to use /// the global OIIO::attribute("threads") value. void threads (int n) { m_threads = n; } /// Retrieve the current thread-spawning policy. int threads () const { return m_threads; } protected: /// Helper routines used by write_* implementations: convert data (in /// the given format and stride) to the "native" format of the file /// (described by the 'spec' member variable), in contiguous order. This /// requires a scratch space to be passed in so that there are no memory /// leaks. Returns a pointer to the native data, which may be the /// original data if it was already in native format and contiguous, or /// it may point to the scratch space if it needed to make a copy or do /// conversions. For float->uint8 conversions only, if dither is /// nonzero, random dither will be added to reduce quantization banding /// artifacts; in this case, the specific nonzero dither value is used /// as a seed for the hash function that produces the per-pixel dither /// amounts, and the optional [xyz]origin parameters help it to align /// the pixels to the right position in the dither pattern. const void *to_native_scanline (TypeDesc format, const void *data, stride_t xstride, std::vector &scratch, unsigned int dither=0, int yorigin=0, int zorigin=0); const void *to_native_tile (TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride, std::vector &scratch, unsigned int dither=0, int xorigin=0, int yorigin=0, int zorigin=0); const void *to_native_rectangle (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride, std::vector &scratch, unsigned int dither=0, int xorigin=0, int yorigin=0, int zorigin=0); /// Helper function to copy a rectangle of data into the right spot in /// an image-sized buffer. In addition to copying to the right place, /// this handles data format conversion and dither (if the spec's /// "oiio:dither" is nonzero, and if it's converting from a float-like /// type to UINT8). The buf_format describes the type of image_buffer, /// if it's TypeDesc::UNKNOWN it will be assumed to be spec.format. bool copy_to_image_buffer (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride, void *image_buffer, TypeDesc buf_format = TypeDesc::UNKNOWN); /// Helper function to copy a tile of data into the right spot in an /// image-sized buffer. This is really just a wrapper for /// copy_to_image_buffer, passing all the right parameters to copy /// exactly one tile. bool copy_tile_to_image_buffer (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride, void *image_buffer, TypeDesc buf_format = TypeDesc::UNKNOWN); protected: ImageSpec m_spec; ///< format spec of the currently open image private: void append_error (const std::string& message) const; // add to m_errmessage mutable std::string m_errmessage; ///< private storage of error message int m_threads; // Thread policy }; // Utility functions /// Retrieve the version of OpenImageIO for the library. This is so /// plugins can query to be sure they are linked against an adequate /// version of the library. OIIO_API int openimageio_version (); /// Special geterror() called after ImageInput::create or /// ImageOutput::create, since if create fails, there's no object on /// which call obj->geterror(). This function returns the last error /// for this particular thread; separate threads will not clobber each /// other's global error messages. OIIO_API std::string geterror (); /// Set a global attribute controlling OpenImageIO. Return true /// if the name and type were recognized and the attribute was set. /// /// Documented attributes: /// int threads /// How many threads to use for operations that can be sped /// by spawning threads (default=0, meaning to use the full /// available hardware concurrency detected). /// int exr_threads /// The size of the internal OpenEXR thread pool. The default /// is to use the full available hardware concurrency detected. /// Default is 0 meaning to use full available hardware /// concurrency detected, -1 means to disable usage of the OpenEXR /// thread pool and execute everything in the caller thread. /// string plugin_searchpath /// Colon-separated list of directories to search for /// dynamically-loaded format plugins. /// string format_list (for 'getattribute' only, cannot set) /// Comma-separated list of all format names supported /// or for which plugins could be found. /// string extension_list (for 'getattribute' only, cannot set) /// For each format, the format name followed by a colon, /// followed by comma-separated list of all extensions that /// are presumed to be used for that format. Semicolons /// separate the lists for formats. For example, /// "tiff:tif;jpeg:jpg,jpeg;openexr:exr" /// int read_chunk /// The number of scanlines that will be attempted to read at /// once for read_image calls (default: 256). /// int debug /// When nonzero, various debug messages may be printed. /// The default is 0 for release builds, 1 for DEBUG builds, /// but also may be overridden by the OPENIMAGEIO_DEBUG env /// variable. /// int tiff:half /// When nonzero, allows TIFF to write 'half' pixel data. /// N.B. Most apps may not read these correctly, but OIIO will. /// That's why the default is not to support it. /// OIIO_API bool attribute (string_view name, TypeDesc type, const void *val); // Shortcuts for common types inline bool attribute (string_view name, int val) { return attribute (name, TypeDesc::TypeInt, &val); } inline bool attribute (string_view name, float val) { return attribute (name, TypeDesc::TypeFloat, &val); } inline bool attribute (string_view name, string_view val) { const char *s = val.c_str(); return attribute (name, TypeDesc::TypeString, &s); } /// Get the named global attribute of OpenImageIO, store it in *val. /// Return true if found and it was compatible with the type specified, /// otherwise return false and do not modify the contents of *val. It /// is up to the caller to ensure that val points to the right kind and /// size of storage for the given type. OIIO_API bool getattribute (string_view name, TypeDesc type, void *val); // Shortcuts for common types inline bool getattribute (string_view name, int &val) { return getattribute (name, TypeDesc::TypeInt, &val); } inline bool getattribute (string_view name, float &val) { return getattribute (name, TypeDesc::TypeFloat, &val); } inline bool getattribute (string_view name, char **val) { return getattribute (name, TypeDesc::TypeString, val); } inline bool getattribute (string_view name, std::string &val) { ustring s; bool ok = getattribute (name, TypeDesc::TypeString, &s); if (ok) val = s.string(); return ok; } inline int get_int_attribute (string_view name, int defaultval=0) { int val; return getattribute (name, TypeDesc::TypeInt, &val) ? val : defaultval; } inline float get_float_attribute (string_view name, float defaultval=0) { float val; return getattribute (name, TypeDesc::TypeFloat, &val) ? val : defaultval; } inline string_view get_string_attribute (string_view name, string_view defaultval = string_view()) { ustring val; return getattribute (name, TypeDesc::TypeString, &val) ? string_view(val) : defaultval; } /// Register the input and output 'create' routines and list of file /// extensions for a particular format. OIIO_API void declare_imageio_format (const std::string &format_name, ImageInput::Creator input_creator, const char **input_extensions, ImageOutput::Creator output_creator, const char **output_extensions, const char *lib_version); /// Helper function: convert contiguous arbitrary data between two /// arbitrary types (specified by TypeDesc's) /// Return true if ok, false if it didn't know how to do the /// conversion. If dst_type is UNKNOWN, it will be assumed to be the /// same as src_type. OIIO_API bool convert_types (TypeDesc src_type, const void *src, TypeDesc dst_type, void *dst, int n); /// Helper routine for data conversion: Convert an image of nchannels x /// width x height x depth from src to dst. The src and dst may have /// different data formats and layouts. Clever use of this function can /// not only exchange data among different formats (e.g., half to 8-bit /// unsigned), but also can copy selective channels, copy subimages, /// etc. If you're lazy, it's ok to pass AutoStride for any of the /// stride values, and they will be auto-computed assuming contiguous /// data. Return true if ok, false if it didn't know how to do the /// conversion. OIIO_API bool convert_image (int nchannels, int width, int height, int depth, const void *src, TypeDesc src_type, stride_t src_xstride, stride_t src_ystride, stride_t src_zstride, void *dst, TypeDesc dst_type, stride_t dst_xstride, stride_t dst_ystride, stride_t dst_zstride, int alpha_channel = -1, int z_channel = -1); /// A version of convert_image that will break up big jobs into multiple /// threads. OIIO_API bool parallel_convert_image ( int nchannels, int width, int height, int depth, const void *src, TypeDesc src_type, stride_t src_xstride, stride_t src_ystride, stride_t src_zstride, void *dst, TypeDesc dst_type, stride_t dst_xstride, stride_t dst_ystride, stride_t dst_zstride, int alpha_channel=-1, int z_channel=-1, int nthreads=0); /// Add random [-theramplitude,ditheramplitude] dither to the color channels /// of the image. Dither will not be added to the alpha or z channel. The /// image origin and dither seed values allow a reproducible (or variable) /// dither pattern. If the strides are set to AutoStride, they will be /// assumed to be contiguous floats in data of the given dimensions. OIIO_API void add_dither (int nchannels, int width, int height, int depth, float *data, stride_t xstride, stride_t ystride, stride_t zstride, float ditheramplitude, int alpha_channel = -1, int z_channel = -1, unsigned int ditherseed = 1, int chorigin=0, int xorigin=0, int yorigin=0, int zorigin=0); /// Convert unassociated to associated alpha by premultiplying all color /// (non-alpha, non-z) channels by alpha. OIIO_API void premult (int nchannels, int width, int height, int depth, int chbegin, int chend, TypeDesc datatype, void *data, stride_t xstride, stride_t ystride, stride_t zstride, int alpha_channel = -1, int z_channel = -1); /// Helper routine for data conversion: Copy an image of nchannels x /// width x height x depth from src to dst. The src and dst may have /// different data layouts, but must have the same data type. Clever /// use of this function can change layouts or strides, copy selective /// channels, copy subimages, etc. If you're lazy, it's ok to pass /// AutoStride for any of the stride values, and they will be /// auto-computed assuming contiguous data. Return true if ok, false if /// it didn't know how to do the conversion. OIIO_API bool copy_image (int nchannels, int width, int height, int depth, const void *src, stride_t pixelsize, stride_t src_xstride, stride_t src_ystride, stride_t src_zstride, void *dst, stride_t dst_xstride, stride_t dst_ystride, stride_t dst_zstride); /// Decode a raw Exif data block and save all the metadata in an /// ImageSpec. Return true if all is ok, false if the exif block was /// somehow malformed. The binary data pointed to by 'exif' should /// start with a TIFF directory header. OIIO_API bool decode_exif (string_view exif, ImageSpec &spec); OIIO_API bool decode_exif (const void *exif, int length, ImageSpec &spec); // DEPRECATED (1.8) /// Construct an Exif data block from the ImageSpec, appending the Exif /// data as a big blob to the char vector. OIIO_API void encode_exif (const ImageSpec &spec, std::vector &blob); /// Helper: For the given OIIO metadata attribute name, look up the Exif tag /// ID, TIFFDataType (expressed as an int), and count. Return true and fill /// in the fields if found, return false if not found. OIIO_API bool exif_tag_lookup (string_view name, int &tag, int &tifftype, int &count); /// Add metadata to spec based on raw IPTC (International Press /// Telecommunications Council) metadata in the form of an IIM /// (Information Interchange Model). Return true if all is ok, false if /// the iptc block was somehow malformed. This is a utility function to /// make it easy for multiple format plugins to support embedding IPTC /// metadata without having to duplicate functionality within each /// plugin. Note that IIM is actually considered obsolete and is /// replaced by an XML scheme called XMP. OIIO_API bool decode_iptc_iim (const void *iptc, int length, ImageSpec &spec); /// Find all the IPTC-amenable metadata in spec and assemble it into an /// IIM data block in iptc. This is a utility function to make it easy /// for multiple format plugins to support embedding IPTC metadata /// without having to duplicate functionality within each plugin. Note /// that IIM is actually considered obsolete and is replaced by an XML /// scheme called XMP. OIIO_API void encode_iptc_iim (const ImageSpec &spec, std::vector &iptc); /// Add metadata to spec based on XMP data in an XML block. Return true /// if all is ok, false if the xml was somehow malformed. This is a /// utility function to make it easy for multiple format plugins to /// support embedding XMP metadata without having to duplicate /// functionality within each plugin. OIIO_API bool decode_xmp (const std::string &xml, ImageSpec &spec); /// Find all the relavant metadata (IPTC, Exif, etc.) in spec and /// assemble it into an XMP XML string. This is a utility function to /// make it easy for multiple format plugins to support embedding XMP /// metadata without having to duplicate functionality within each /// plugin. If 'minimal' is true, then don't encode things that would /// be part of ordinary TIFF or exif tags. OIIO_API std::string encode_xmp (const ImageSpec &spec, bool minimal=false); // All the wrap_foo functions implement a wrap mode, wherein coord is // altered to be origin <= coord < origin+width. The return value // indicates if the resulting wrapped value is valid (example, for // wrap_black, values outside the region are invalid and do not modify // the coord parameter). OIIO_API bool wrap_black (int &coord, int origin, int width); OIIO_API bool wrap_clamp (int &coord, int origin, int width); OIIO_API bool wrap_periodic (int &coord, int origin, int width); OIIO_API bool wrap_periodic_pow2 (int &coord, int origin, int width); OIIO_API bool wrap_mirror (int &coord, int origin, int width); // Typedef for the function signature of a wrap implementation. typedef bool (*wrap_impl) (int &coord, int origin, int width); namespace pvt { // For internal use - use debugmsg() below for a nicer interface. OIIO_API void debugmsg_ (string_view message); }; /// debugmsg(format, ...) prints debugging message when attribute "debug" is /// nonzero, which it is by default for DEBUG compiles or when the /// environment variable OPENIMAGEIO_DEBUG is set. This is preferred to raw /// output to stderr for debugging statements. /// void debugmsg (const char *format, ...); TINYFORMAT_WRAP_FORMAT (void, debugmsg, /**/, std::ostringstream msg;, msg, pvt::debugmsg_(msg.str());) // to force correct linkage on some systems OIIO_API void _ImageIO_force_link (); OIIO_NAMESPACE_END #endif // OPENIMAGEIO_IMAGEIO_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/dassert.h0000644000175000017500000001255613151711064022462 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_DASSERT_H #define OPENIMAGEIO_DASSERT_H #include #include #include "platform.h" /// \file /// /// Handy macros for debugging assertions. /// /// - ASSERT (if not already defined) is defined to check if a condition /// is met, and if not, calls ABORT with an error message /// indicating the module and line where it occurred. /// - ASSERT_MSG: like ASSERT, but takes printf-like extra arguments /// - DASSERT is the same as ASSERT when NDEBUG is not defined but a /// no-op when not in debug mode. /// - DASSERT_MSG: like DASSERT, but takes printf-like extra arguments /// - OIIO_STATIC_ASSERT(cond) : static assertion /// - OIIO_STATIC_ASSERT_MSG(cond,msg) : static assertion + message /// /// The presumed usage is that you want ASSERT for dire conditions that /// must be checked at runtime even in an optimized build. DASSERT is /// for checks we should do for debugging, but that we don't want to /// bother with in a shipping optimized build. /// /// In both cases, these are NOT a substitute for actual error checking /// and recovery! Never ASSERT or DASSERT to check invalid user input, /// for example. They should be used only to verify that there aren't /// errors in the *code* that are so severe that there is no point even /// trying to recover gracefully. /// ASSERT(condition) checks if the condition is met, and if not, prints /// an error message indicating the module and line where the error /// occurred and then aborts. #ifndef ASSERT # define ASSERT(x) \ (OIIO_LIKELY(x) ? ((void)0) \ : (fprintf (stderr, "%s:%u: failed assertion '%s'\n", \ __FILE__, __LINE__, #x), abort())) #endif /// ASSERT_MSG(condition,msg,...) is like ASSERT, but lets you add /// formatted output (a la printf) to the failure message. #ifndef ASSERT_MSG # define ASSERT_MSG(x,msg,...) \ (OIIO_LIKELY(x) ? ((void)0) \ : (fprintf (stderr, "%s:%u: failed assertion '%s': " msg "\n", \ __FILE__, __LINE__, #x, __VA_ARGS__), abort())) #endif #ifndef ASSERTMSG #define ASSERTMSG ASSERT_MSG #endif /// DASSERT(condition) is just like ASSERT, except that it only is /// functional in DEBUG mode, but does nothing when in a non-DEBUG /// (optimized, shipping) build. #ifndef NDEBUG # define DASSERT(x) ASSERT(x) #else /* DASSERT does nothing when not debugging; sizeof trick prevents warnings */ # define DASSERT(x) ((void)sizeof(x)) #endif /// DASSERT_MSG(condition,msg,...) is just like ASSERT_MSG, except that it /// only is functional in DEBUG mode, but does nothing when in a /// non-DEBUG (optimized, shipping) build. #ifndef NDEBUG # define DASSERT_MSG ASSERT_MSG #else # define DASSERT_MSG(x,...) ((void)sizeof(x)) /* does nothing when not debugging */ #endif #ifndef DASSERTMSG #define DASSERTMSG DASSERT_MSG #endif /// Define OIIO_STATIC_ASSERT and OIIO_STATIC_ASSERT_MSG as wrappers around /// static_assert and static_assert_msg, with appropriate fallbacks for /// older C++ standards. #if (__cplusplus >= 201700L) /* FIXME - guess the token, fix when C++17 */ # define OIIO_STATIC_ASSERT(cond) static_assert(cond) # define OIIO_STATIC_ASSERT_MSG(cond,msg) static_assert(cond,msg) #elif (__cplusplus >= 201103L) # define OIIO_STATIC_ASSERT(cond) static_assert(cond,"") # define OIIO_STATIC_ASSERT_MSG(cond,msg) static_assert(cond,msg) #else /* FIXME(C++11): this case can go away when C++11 is our minimum */ # include # define OIIO_STATIC_ASSERT(cond) BOOST_STATIC_ASSERT(cond) # define OIIO_STATIC_ASSERT_MSG(cond,msg) BOOST_STATIC_ASSERT_MSG(cond,msg) #endif #endif // OPENIMAGEIO_DASSERT_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/ustring.h0000644000175000017500000006603513151711064022511 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// \file /// Define the ustring class, unique strings with efficient storage and /// very fast copy and comparison. ///////////////////////////////////////////////////////////////////////////// /// \class ustring /// /// A ustring is an alternative to char* or std::string for storing /// strings, in which the character sequence is unique (allowing many /// speed advantages for assignment, equality testing, and inequality /// testing). /// /// The implementation is that behind the scenes there is a hash set of /// allocated strings, so the characters of each string are unique. A /// ustring itself is a pointer to the characters of one of these canonical /// strings. Therefore, assignment and equality testing is just a single /// 32- or 64-bit int operation, the only mutex is when a ustring is /// created from raw characters, and the only malloc is the first time /// each canonical ustring is created. /// /// The internal table also contains a std::string version and the length /// of the string, so converting a ustring to a std::string (via /// ustring::string()) or querying the number of characters (via /// ustring::size() or ustring::length()) is extremely inexpensive, and does /// not involve creation/allocation of a new std::string or a call to /// strlen. /// /// We try very hard to completely mimic the API of std::string, /// including all the constructors, comparisons, iterations, etc. Of /// course, the charaters of a ustring are non-modifiable, so we do not /// replicate any of the non-const methods of std::string. But in most /// other ways it looks and acts like a std::string and so most templated /// algorthms that would work on a "const std::string &" will also work /// on a ustring. /// /// Usage guidelines: /// /// Compared to standard strings, ustrings have several advantages: /// /// - Each individual ustring is very small -- in fact, we guarantee that /// a ustring is the same size and memory layout as an ordinary char*. /// - Storage is frugal, since there is only one allocated copy of each /// unique character sequence, throughout the lifetime of the program. /// - Assignment from one ustring to another is just copy of the pointer; /// no allocation, no character copying, no reference counting. /// - Equality testing (do the strings contain the same characters) is /// a single operation, the comparison of the pointer. /// - Memory allocation only occurs when a new ustring is construted from /// raw characters the FIRST time -- subsequent constructions of the /// same string just finds it in the canonial string set, but doesn't /// need to allocate new storage. Destruction of a ustring is trivial, /// there is no de-allocation because the canonical version stays in /// the set. Also, therefore, no user code mistake can lead to /// memory leaks. /// /// But there are some problems, too. Canonical strings are never freed /// from the table. So in some sense all the strings "leak", but they /// only leak one copy for each unique string that the program ever comes /// across. Also, creation of unique strings from raw characters is more /// expensive than for standard strings, due to hashing, table queries, /// and other overhead. /// /// On the whole, ustrings are a really great string representation /// - if you tend to have (relatively) few unique strings, but many /// copies of those strings; /// - if the creation of strings from raw characters is relatively /// rare compared to copying or comparing to existing strings; /// - if you tend to make the same strings over and over again, and /// if it's relatively rare that a single unique character sequence /// is used only once in the entire lifetime of the program; /// - if your most common string operations are assignment and equality /// testing and you want them to be as fast as possible; /// - if you are doing relatively little character-by-character assembly /// of strings, string concatenation, or other "string manipulation" /// (other than equality testing). /// /// ustrings are not so hot /// - if your program tends to have very few copies of each character /// sequence over the entire lifetime of the program; /// - if your program tends to generate a huge variety of unique /// strings over its lifetime, each of which is used only a short /// time and then discarded, never to be needed again; /// - if you don't need to do a lot of string assignment or equality /// testing, but lots of more complex string manipulation. /// ///////////////////////////////////////////////////////////////////////////// #ifndef OPENIMAGEIO_USTRING_H #define OPENIMAGEIO_USTRING_H #if defined(_MSC_VER) // Ignore warnings about DLL exported classes with member variables that are template classes. // This happens with the std::string empty_std_string static member variable of ustring below. // Also remove a warning about the strncpy function not being safe and deprecated in MSVC. // There is no equivalent safe and portable function and trying to fix this is more trouble than // its worth. (see http://stackoverflow.com/questions/858252/alternatives-to-ms-strncpy-s) # pragma warning (disable : 4251 4996) #endif #include #include #include #include "export.h" #include "strutil.h" #include "string_view.h" #include "dassert.h" #include "oiioversion.h" #ifndef NULL #define NULL 0 #endif OIIO_NAMESPACE_BEGIN class OIIO_API ustring { public: typedef char value_type; typedef value_type * pointer; typedef value_type & reference; typedef const value_type & const_reference; typedef size_t size_type; static const size_type npos = static_cast(-1); typedef std::string::const_iterator const_iterator; typedef std::string::const_reverse_iterator const_reverse_iterator; /// Default ctr for ustring -- make an empty string. /// ustring (void) : m_chars(NULL) { } /// Construct a ustring from a null-terminated C string (char *). /// explicit ustring (const char *str) { m_chars = str ? make_unique(str) : NULL; } /// Construct a ustring from a string_view, which can be auto-converted /// from either a null-terminated C string (char *) or a C++ /// std::string. explicit ustring (string_view str) { m_chars = str.data() ? make_unique(str) : NULL; } /// Construct a ustring from at most n characters of str, starting at /// position pos. ustring (const char *str, size_type pos, size_type n) : m_chars (make_unique(std::string(str,pos,n).c_str())) { } /// Construct a ustring from the first n characters of str. /// ustring (const char *str, size_type n) : m_chars (make_unique(string_view(str,n))) { } /// Construct a ustring from n copies of character c. /// ustring (size_type n, char c) : m_chars (make_unique(std::string(n,c).c_str())) { } /// Construct a ustring from an indexed substring of a std::string. /// ustring (const std::string &str, size_type pos, size_type n=npos) { string_view sref(str); sref = sref.substr (pos, n); m_chars = make_unique(sref); } /// Copy construct a ustring from another ustring. /// ustring (const ustring &str) : m_chars(str.m_chars) { } /// Construct a ustring from an indexed substring of a ustring. /// ustring (const ustring &str, size_type pos, size_type n=npos) { string_view sref(str); sref = sref.substr (pos, n); m_chars = make_unique(sref); } /// ustring destructor. /// ~ustring () { } /// Conversion to string_view operator string_view() const { return string_view(c_str(), length()); } /// Assign a ustring to *this. /// const ustring & assign (const ustring &str) { m_chars = str.m_chars; return *this; } /// Assign a substring of a ustring to *this. /// const ustring & assign (const ustring &str, size_type pos, size_type n=npos) { *this = ustring(str,pos,n); return *this; } /// Assign a std::string to *this. /// const ustring & assign (const std::string &str) { assign (str.c_str()); return *this; } /// Assign a substring of a std::string to *this. /// const ustring & assign (const std::string &str, size_type pos, size_type n=npos) { *this = ustring(str,pos,n); return *this; } /// Assign a null-terminated C string (char*) to *this. /// const ustring & assign (const char *str) { m_chars = str ? make_unique(str) : NULL; return *this; } /// Assign the first n characters of str to *this. /// const ustring & assign (const char *str, size_type n) { *this = ustring(str,n); return *this; } /// Assign n copies of c to *this. /// const ustring & assign (size_type n, char c) { *this = ustring(n,c); return *this; } /// Assign a string_view to *this. const ustring & assign (string_view str) { m_chars = str.length() ? make_unique(str) : NULL; return *this; } /// Assign a ustring to another ustring. /// const ustring & operator= (const ustring &str) { return assign(str); } /// Assign a null-terminated C string (char *) to a ustring. /// const ustring & operator= (const char *str) { return assign(str); } /// Assign a C++ std::string to a ustring. /// const ustring & operator= (const std::string &str) { return assign(str); } /// Assign a string_view to a ustring. /// const ustring & operator= (string_view str) { return assign(str); } /// Assign a single char to a ustring. /// const ustring & operator= (char c) { char s[2]; s[0] = c; s[1] = 0; *this = ustring (s); return *this; } /// Return a C string representation of a ustring. /// const char *c_str () const { return m_chars; } /// Return a C string representation of a ustring. /// const char *data () const { return c_str(); } /// Return a C++ std::string representation of a ustring. /// const std::string & string () const { if (m_chars) { const TableRep *rep = (const TableRep *)m_chars - 1; return rep->str; } else return empty_std_string; } /// Reset to an empty string. /// void clear (void) { m_chars = NULL; } /// Return the number of characters in the string. /// size_t length (void) const { if (! m_chars) return 0; const TableRep *rep = ((const TableRep *)m_chars) - 1; return rep->length; } /// Return a hashed version of the string /// size_t hash (void) const { if (! m_chars) return 0; const TableRep *rep = ((const TableRep *)m_chars) - 1; return rep->hashed; } /// Return the number of characters in the string. /// size_t size (void) const { return length(); } /// Is the string empty -- i.e., is it the NULL pointer or does it /// point to an empty string? bool empty (void) const { return (size() == 0); } /// Cast to int, which is interpreted as testing whether it's not an /// empty string. This allows you to write "if (t)" with the same /// semantics as if it were a char*. operator int (void) const { return !empty(); } /// Return a const_iterator that references the first character of /// the string. const_iterator begin () const { return string().begin(); } /// Return a const_iterator that references the end of a traversal /// of the characters of the string. const_iterator end () const { return string().end(); } /// Return a const_reverse_iterator that references the last /// character of the string. const_reverse_iterator rbegin () const { return string().rbegin(); } /// Return a const_reverse_iterator that references the end of /// a reverse traversal of the characters of the string. const_reverse_iterator rend () const { return string().rend(); } /// Return a reference to the character at the given position. /// Note that it's up to the caller to be sure pos is within the /// size of the string. const_reference operator[] (size_type pos) const { return c_str()[pos]; } /// Dump into character array s the characters of this ustring, /// beginning with position pos and copying at most n characters. size_type copy (char* s, size_type n, size_type pos = 0) const { if (m_chars == NULL) { s[0] = 0; return 0; } char *c = strncpy (s, c_str()+pos, n); return (size_type)(c-s); } /// Returns a substring of the ustring object consisting of n /// characters starting at position pos. ustring substr (size_type pos = 0, size_type n = npos) const { return ustring (*this, pos, n); } // FIXME: implement compare. size_type find(const ustring &str, size_type pos = 0) const { return string().find(str.string(), pos); } size_type find(const std::string &str, size_type pos = 0) const { return string().find(str, pos); } size_type find(const char *s, size_type pos, size_type n) const { return string().find(s, pos, n); } size_type find(const char *s, size_type pos = 0) const { return string().find(s, pos); } size_type find(char c, size_type pos = 0) const { return string().find(c, pos); } size_type rfind(const ustring &str, size_type pos = npos) const { return string().rfind(str.string(), pos); } size_type rfind(const std::string &str, size_type pos = npos) const { return string().rfind(str, pos); } size_type rfind(const char *s, size_type pos, size_type n) const { return string().rfind(s, pos, n); } size_type rfind(const char *s, size_type pos = npos) const { return string().rfind(s, pos); } size_type rfind(char c, size_type pos = npos) const { return string().rfind(c, pos); } size_type find_first_of(const ustring &str, size_type pos = 0) const { return string().find_first_of(str.string(), pos); } size_type find_first_of(const std::string &str, size_type pos = 0) const { return string().find_first_of(str, pos); } size_type find_first_of(const char *s, size_type pos, size_type n) const { return string().find_first_of(s, pos, n); } size_type find_first_of(const char *s, size_type pos = 0) const { return string().find_first_of(s, pos); } size_type find_first_of(char c, size_type pos = 0) const { return string().find_first_of(c, pos); } size_type find_last_of(const ustring &str, size_type pos = npos) const { return string().find_last_of(str.string(), pos); } size_type find_last_of(const std::string &str, size_type pos = npos) const { return string().find_last_of(str, pos); } size_type find_last_of(const char *s, size_type pos, size_type n) const { return string().find_last_of(s, pos, n); } size_type find_last_of(const char *s, size_type pos = npos) const { return string().find_last_of(s, pos); } size_type find_last_of(char c, size_type pos = npos) const { return string().find_last_of(c, pos); } size_type find_first_not_of(const ustring &str, size_type pos = 0) const { return string().find_first_not_of(str.string(), pos); } size_type find_first_not_of(const std::string &str, size_type pos = 0) const { return string().find_first_not_of(str, pos); } size_type find_first_not_of(const char *s, size_type pos, size_type n) const { return string().find_first_not_of(s, pos, n); } size_type find_first_not_of(const char *s, size_type pos = 0) const { return string().find_first_not_of(s, pos); } size_type find_first_not_of(char c, size_type pos = 0) const { return string().find_first_not_of(c, pos); } size_type find_last_not_of(const ustring &str, size_type pos = npos) const { return string().find_last_not_of(str.string(), pos); } size_type find_last_not_of(const std::string &str, size_type pos = npos) const { return string().find_last_not_of(str, pos); } size_type find_last_not_of(const char *s, size_type pos, size_type n) const { return string().find_last_not_of(s, pos, n); } size_type find_last_not_of(const char *s, size_type pos = npos) const { return string().find_last_not_of(s, pos); } size_type find_last_not_of(char c, size_type pos = npos) const { return string().find_last_not_of(c, pos); } /// Return 0 if *this is lexicographically equal to str, -1 if /// *this is lexicographically earlier than str, 1 if *this is /// lexicographically after str. int compare (const ustring& str) const { return (c_str() == str.c_str()) ? 0 : strcmp (c_str() ? c_str() : "", str.c_str() ? str.c_str() : ""); } /// Return 0 if *this is lexicographically equal to str, -1 if /// *this is lexicographically earlier than str, 1 if *this is /// lexicographically after str. int compare (const std::string& str) const { return strcmp (c_str() ? c_str() : "", str.c_str()); } /// Return 0 if *this is lexicographically equal to str, -1 if /// *this is lexicographically earlier than str, 1 if *this is /// lexicographically after str. int compare (string_view str) const { return strncmp (c_str() ? c_str() : "", str.data() ? str.data() : "", str.length()); } /// Return 0 if *this is lexicographically equal to str, -1 if /// *this is lexicographically earlier than str, 1 if *this is /// lexicographically after str. int compare (const char *str) const { return strcmp (c_str() ? c_str() : "", str ? str : ""); } /// Return 0 if a is lexicographically equal to b, -1 if a is /// lexicographically earlier than b, 1 if a is lexicographically /// after b. friend int compare (const std::string& a, const ustring &b) { return strcmp (a.c_str(), b.c_str() ? b.c_str() : ""); } /// Test two ustrings for equality -- are they comprised of the same /// sequence of characters. Note that because ustrings are unique, /// this is a trivial pointer comparison, not a char-by-char loop as /// would be the case with a char* or a std::string. bool operator== (const ustring &str) const { return c_str() == str.c_str(); } /// Test two ustrings for inequality -- are they comprised of different /// sequences of characters. Note that because ustrings are unique, /// this is a trivial pointer comparison, not a char-by-char loop as /// would be the case with a char* or a std::string. bool operator!= (const ustring &str) const { return c_str() != str.c_str(); } /// Test a ustring (*this) for lexicographic equality with std::string /// x. bool operator== (const std::string &x) const { return compare(x) == 0; } /// Test a ustring (*this) for lexicographic equality with string_view /// x. bool operator== (string_view x) const { return compare(x) == 0; } /// Test a ustring (*this) for lexicographic equality with char* x. bool operator== (const char *x) const { return compare(x) == 0; } /// Test for lexicographic equality between std::string a and ustring /// b. friend bool operator== (const std::string &a, const ustring &b) { return b.compare(a) == 0; } /// Test for lexicographic equality between string_view a and ustring /// b. friend bool operator== (string_view a, const ustring &b) { return b.compare(a) == 0; } /// Test for lexicographic equality between char* a and ustring /// b. friend bool operator== (const char *a, const ustring &b) { return b.compare(a) == 0; } /// Test a ustring (*this) for lexicographic inequality with /// std::string x. bool operator!= (const std::string &x) const { return compare(x) != 0; } /// Test a ustring (*this) for lexicographic inequality with /// string_view x. bool operator!= (string_view x) const { return compare(x) != 0; } /// Test a ustring (*this) for lexicographic inequality with /// char* x. bool operator!= (const char *x) const { return compare(x) != 0; } /// Test for lexicographic inequality between std::string a and /// ustring b. friend bool operator!= (const std::string &a, const ustring &b) { return b.compare(a) != 0; } /// Test for lexicographic inequality between string_view a and /// ustring b. friend bool operator!= (string_view a, const ustring &b) { return b.compare(a) != 0; } /// Test for lexicographic inequality between char* a and /// ustring b. friend bool operator!= (const char *a, const ustring &b) { return b.compare(a) != 0; } /// Test for lexicographic 'less', comes in handy for lots of STL /// containers and algorithms. bool operator< (const ustring &x) const { return compare(x) < 0; } /// Construct a ustring in a printf-like fashion. In other words, /// something like: /// ustring s = ustring::format ("blah %d %g", (int)foo, (float)bar); /// /// The printf argument list is fully typesafe via tinyformat; format /// conceptually has the signature /// /// static ustring format (const char *fmt, ...); TINYFORMAT_WRAP_FORMAT (static ustring, format, /**/, std::ostringstream msg;, msg, return ustring(msg.str());) /// Generic stream output of a ustring. /// friend std::ostream & operator<< (std::ostream &out, const ustring &str) { if (str.c_str() && out.good()) out.write (str.c_str(), str.size()); return out; } /// Return the statistics output as a string. /// static std::string getstats (bool verbose = true); /// Return the amount of memory consumed by the ustring table. /// static size_t memory (); /// Given a string_view, return a pointer to the unique /// version kept in the internal table (creating a new table entry /// if we haven't seen this sequence of characters before). /// N.B.: this is equivalent to ustring(str).c_str(). It's also the /// routine that is used directly by ustring's internals to generate /// the canonical unique copy of the characters. static const char * make_unique (string_view str); /// Is this character pointer a unique ustring representation of /// those characters? Useful for diagnostics and debugging. static bool is_unique (const char *str) { return str == NULL || make_unique(str) == str; } /// Create a ustring from characters guaranteed to already be /// ustring-clean, without having to run through the hash yet /// again. Use with extreme caution!!! static ustring from_unique (const char *unique) { DASSERT (is_unique(unique)); // DEBUG builds -- check it! ustring u; u.m_chars = unique; return u; } private: // Individual ustring internal representation -- the unique characters. // const char *m_chars; public: // Representation within the hidden string table -- DON'T EVER CREATE // ONE OF THESE YOURSELF! // The characters are found directly after the rep. So that means that // if you know the rep, the chars are at (char *)(rep+1), and if you // know the chars, the rep is at ((TableRep *)chars - 1). struct TableRep { size_t hashed; // precomputed Hash value std::string str; // String representation size_t length; // Length of the string; must be right before cap size_t dummy_capacity; // Dummy field! must be right before refcount int dummy_refcount; // Dummy field! must be right before chars TableRep (string_view strref, size_t hash); ~TableRep (); const char *c_str () const { return (const char *)(this + 1); } }; private: static std::string empty_std_string; }; /// Functor class to use as a hasher when you want to make a hash_map or /// hash_set using ustring as a key. class ustringHash { public: size_t operator() (const ustring &s) const { return s.hash(); } }; /// Functor class to use for comparisons when sorting ustrings, if you /// want the strings sorted lexicographically. class ustringLess { public: size_t operator() (ustring a, ustring b) const {return a members of PixelStats below. # pragma warning (disable : 4251) #endif #include "imageio.h" #include "imagebuf.h" #include "fmath.h" #include "color.h" #include /* because we need M33f */ #include #if !defined(__OPENCV_CORE_TYPES_H__) && !defined(OPENCV_CORE_TYPES_H) struct IplImage; // Forward declaration; used by Intel Image lib & OpenCV #endif OIIO_NAMESPACE_BEGIN class Filter2D; // forward declaration /// Some generalities about ImageBufAlgo functions: /// /// All IBA functions take a ROI. Only the pixels (and channels) in dst /// that are specified by the ROI will be altered; the default ROI is to /// alter all the pixels in dst. Exceptions will be noted, including /// functions that do not honor their channel range. /// /// In general, IBA functions that are passed an initialized 'dst' or /// 'result' image do not reallocate it or alter its existing pixels /// that lie outside the ROI (exceptions will be noted). If passed an /// uninitialized result image, it will be reallocatd to be the size of /// the ROI (and with float pixels). If the result image passed is /// uninitialized and also the ROI is undefined, the ROI will be the /// union of the pixel data regions of any input images. (A small /// number of IBA functions, such as fill(), have only a result image /// and no input image; in such cases, it's an error to have both an /// uninitiailized result image and an undefined ROI.) /// /// IBA functions that have an 'nthreads' parameter use it to specify /// how many threads (potentially) may be used, but it's not a /// guarantee. If nthreads == 0, it will use the global OIIO attribute /// "nthreads". If nthreads == 1, it guarantees that it will not launch /// any new threads. /// /// All IBA functions return true on success, false on error (with an /// appropriate error message set in dst). namespace ImageBufAlgo { /// Zero out (set to 0, black) the image region. /// /// Only the pixels (and channels) in dst that are specified by roi will /// be altered; the default roi is to alter all the pixels in dst. /// /// If dst is uninitialized, it will be resized to be a float ImageBuf /// large enough to hold the region specified by roi. It is an error to /// pass both an uninitialied dst and an undefined roi. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API zero (ImageBuf &dst, ROI roi=ROI::All(), int nthreads=0); /// Fill the image region with given channel values. Note that the /// values pointer starts with channel 0, even if the ROI indicates that /// a later channel is the first to be changed. /// /// Three varieties of fill() exist: (a) a single set of channel values that /// will apply to the whole ROI, (b) two sets of values that will create a /// linearly interpolated gradient from top to bottom of the ROI, (c) four /// sets of values that will be bilnearly interpolated across all four /// corners of the ROI. /// /// Only the pixels (and channels) in dst that are specified by roi will /// be altered; the default roi is to alter all the pixels in dst. /// /// If dst is uninitialized, it will be resized to be a float ImageBuf /// large enough to hold the region specified by roi. It is an error to /// pass both an uninitialied dst and an undefined roi. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API fill (ImageBuf &dst, const float *values, ROI roi=ROI::All(), int nthreads=0); bool OIIO_API fill (ImageBuf &dst, const float *top, const float *bottom, ROI roi=ROI::All(), int nthreads=0); bool OIIO_API fill (ImageBuf &dst, const float *topleft, const float *topright, const float *bottomleft, const float *bottomright, ROI roi=ROI::All(), int nthreads=0); /// Fill the image region with a checkerboard with origin /// (xoffset,yoffset,zoffset) and that alternates between color1[] and /// color2[] every width pixels in x, every height pixels in y, and /// every depth pixels in z. The pattern is definied in abstract "image /// space" independently of the pixel data window of dst or the ROI. /// /// Only the pixels (and channels) in dst that are specified by roi will /// be altered; the default roi is to alter all the pixels in dst. /// /// If dst is uninitialized, it will be resized to be a float ImageBuf /// large enough to hold the region specified by roi. It is an error /// to pass both an uninitialied dst and an undefined roi. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API checker (ImageBuf &dst, int width, int height, int depth, const float *color1, const float *color2, int xoffset=0, int yoffset=0, int zoffset=0, ROI roi=ROI::All(), int nthreads=0); /// Inject pseudorandom noise into image dst, in every pixel and channel /// specified by the roi (defaulting to all pixels, all channels). There are /// several noise types to choose from, and each behaves differently and has /// a different interpretation of the A and B parameters: /// "gaussian" adds Gaussian (normal distribution) noise values with /// mean value A and standard deviation B. /// "uniform" adds noise values uninformly distributed on range [A,B). /// "salt" changes to value A a portion of pixels given by B. /// If the 'mono' flag is true, a single noise value will be applied to all /// channels specified by roi, but if 'mono' is false, a separate noise /// value will be computed for each channel in the region. /// /// The random number generator is actually driven by a hash on the "image /// space" coordinates and channel, independently of the pixel data window /// of dst or the ROI. Choosing different seed values will result in a /// different pattern, but for the same seed value, the noise at a given /// pixel coordinate (x,y,z) channel c will is completely deterministic and /// repeatable. /// /// If dst is uninitialized, it will be resized to be a float ImageBuf /// large enough to hold the region specified by roi. It is an error /// to pass both an uninitialied dst and an undefined roi. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API noise (ImageBuf &dst, string_view noisetype, float A = 0.0f, float B = 0.1f, bool mono = false, int seed = 0, ROI roi=ROI::All(), int nthreads=0); /// Generic channel shuffling -- copy src to dst, but with channels in /// the order channelorder[0..nchannels-1]. Does not support in-place /// operation. For any channel in which channelorder[i] < 0, it will /// just make dst channel i a constant color -- set to channelvalues[i] /// (if channelvalues != NULL) or 0.0 (if channelvalues == NULL). /// /// If channelorder is NULL, it will be interpreted as /// {0, 1, ..., nchannels-1}, meaning that it's only renaming channels, /// not reordering them. /// /// If newchannelnames is not NULL, it points to an array of new channel /// names. Channels for which newchannelnames[i] is the empty string (or /// all channels, if newchannelnames == NULL) will be named as follows: /// If shuffle_channel_names is false, the resulting dst image will have /// default channel names in the usual order ("R", "G", etc.), but if /// shuffle_channel_names is true, the names will be taken from the /// corresponding channels of the source image -- be careful with this, /// shuffling both channel ordering and their names could result in no /// semantic change at all, if you catch the drift. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// N.B. If you are merely interested in extending the number of channels /// or truncating channels at the end (but leaving the other channels /// intact), then you should call this as: /// channels (dst, src, nchannels, NULL, NULL, NULL, true); bool OIIO_API channels (ImageBuf &dst, const ImageBuf &src, int nchannels, const int *channelorder, const float *channelvalues=NULL, const std::string *newchannelnames=NULL, bool shuffle_channel_names=false, int nthreads=0); /// Append the channels of A and B together into dst over the region of /// interest. If the region passed is uninitialized (the default), it /// will be interpreted as being the union of the pixel windows of A and /// B (and all channels of both images). If dst is not already /// initialized, it will be resized to be big enough for the region. bool OIIO_API channel_append (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi=ROI::All(), int nthreads=0); /// Set dst to the "deep" version of "flat" input src. Turning a flat /// image into a deep one means: /// /// If the src image has a "Z" channel: if the source pixel's Z channel /// value is not infinite, the corresponding pixel of dst will get a single /// depth sample that copies the data from the soruce pixel; otherwise, dst /// will get an empty pixel. In other words, infinitely far pixels will not /// turn into deep samples. /// /// If the src image lacks a "Z" channel: if any of the source pixel's /// channel values are nonzero, the corresponding pixel of dst will get a /// single depth sample that copies the data from the source pixel and uses /// the zvalue parameter for the depth; otherwise, if all source channels in /// that pixel are zero, the destination pixel will get no depth samples. /// /// If src is already a deep image, it will just copy pixel values from src /// to dst. If dst is not already an initialized ImageBuf, it will be sized /// to match src (but made deep). /// /// 'roi' specifies the region of dst's pixels which will be computed; /// existing pixels outside this range will not be altered. If not /// specified, the default ROI value will be the pixel data window of src. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. bool OIIO_API deepen (ImageBuf &dst, const ImageBuf &src, float zvalue = 1.0f, ROI roi = ROI::All(), int nthreads = 0); /// Set dst to the ``flattened'' composite of deep image src. That is, it /// converts a deep image to a simple flat image by front-to-back /// compositing the samples within each pixel. If src is already a non- /// deep/flat image, it will just copy pixel values from src to dst. If dst /// is not already an initialized ImageBuf, it will be sized to match src /// (but made non-deep). /// /// 'roi' specifies the region of dst's pixels which will be computed; /// existing pixels outside this range will not be altered. If not /// specified, the default ROI value will be the pixel data window of src. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. bool OIIO_API flatten (ImageBuf &dst, const ImageBuf &src, ROI roi = ROI::All(), int nthreads = 0); /// Set dst to the deep merge of the samples of deep images A and B, /// overwriting any existing samples of dst in the ROI. /// If occlusion_cull is true, any samples occluded by an opaque /// sample will be deleted. /// /// 'roi' specifies the region of dst's pixels which will be computed; /// existing pixels outside this range will not be altered. If not /// specified, the default ROI value will be the pixel data window of src. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. bool OIIO_API deep_merge (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, bool occlusion_cull = true, ROI roi = ROI::All(), int nthreads = 0); /// Copy the specified region of pixels of src into dst at the same /// locations, without changing any existing pixels of dst outside the /// region. If dst is not already initialized, it will be set to the same /// size as roi (defaulting to all of src), optionally with the pixel type /// overridden by convert (if it is not UNKNOWN). /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Works on all pixel data types. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API copy (ImageBuf &dst, const ImageBuf &src, TypeDesc convert=TypeDesc::UNKNOWN, ROI roi = ROI::All(), int nthreads = 0); /// Reset dst to be the specified region of src. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API crop (ImageBuf &dst, const ImageBuf &src, ROI roi = ROI::All(), int nthreads = 0); /// Assign to dst the designated region of src, but shifted to be at the /// (0,0) origin, and with the full/display resolution set to be identical /// to the data region. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API cut (ImageBuf &dst, const ImageBuf &src, ROI roi=ROI::All(), int nthreads = 0); /// Copy into dst, beginning at (xbegin,ybegin,zbegin), the pixels of /// src described by srcroi. If srcroi is ROI::All(), the entirety of src /// will be used. It will copy into channels [chbegin...], as many /// channels as are described by srcroi. Pixels or channels of dst outside /// the range of roi will not be altered. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API paste (ImageBuf &dst, int xbegin, int ybegin, int zbegin, int chbegin, const ImageBuf &src, ROI srcroi=ROI::All(), int nthreads = 0); /// Copy src to dst, but with the image pixels rotated 90 degrees /// clockwise. In other words, /// AB --> CA /// CD DB /// /// Only the pixels (and channels) in src that are specified by roi will be /// copied to their corresponding positions in dst; the default roi is to /// copy the whole data region of src. If dst is uninitialized, it will be /// resized to be a float ImageBuf large enough to hold the region specified /// by roi. It is an error to pass both an uninitialied dst and an undefined /// roi. /// /// The nthreads parameter specifies how many threads (potentially) may be /// used, but it's not a guarantee. If nthreads == 0, it will use the /// global OIIO attribute "nthreads". If nthreads == 1, it guarantees that /// it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API rotate90 (ImageBuf &dst, const ImageBuf &src, ROI roi=ROI::All(), int nthreads=0); /// Copy src to dst, but with the image pixels rotated 180 degrees. /// In other words, /// AB --> DC /// CD BA /// /// Only the pixels (and channels) in src that are specified by roi will be /// copied to their corresponding positions in dst; the default roi is to /// copy the whole data region of src. If dst is uninitialized, it will be /// resized to be a float ImageBuf large enough to hold the region specified /// by roi. It is an error to pass both an uninitialied dst and an undefined /// roi. /// /// The nthreads parameter specifies how many threads (potentially) may be /// used, but it's not a guarantee. If nthreads == 0, it will use the /// global OIIO attribute "nthreads". If nthreads == 1, it guarantees that /// it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API rotate180 (ImageBuf &dst, const ImageBuf &src, ROI roi=ROI::All(), int nthreads=0); /// Copy src to dst, but with the image pixels rotated 90 degrees /// clockwise. In other words, /// AB --> BD /// CD AC /// /// Only the pixels (and channels) in src that are specified by roi will be /// copied to their corresponding positions in dst; the default roi is to /// copy the whole data region of src. If dst is uninitialized, it will be /// resized to be a float ImageBuf large enough to hold the region specified /// by roi. It is an error to pass both an uninitialied dst and an undefined /// roi. /// /// The nthreads parameter specifies how many threads (potentially) may be /// used, but it's not a guarantee. If nthreads == 0, it will use the /// global OIIO attribute "nthreads". If nthreads == 1, it guarantees that /// it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API rotate270 (ImageBuf &dst, const ImageBuf &src, ROI roi=ROI::All(), int nthreads=0); /// Copy src to dst, but with the scanlines exchanged vertically within /// the display/full window. In other words, /// AB --> CD /// CD AB /// /// Only the pixels (and channels) in src that are specified by roi will be /// copied to their corresponding positions in dst; the default roi is to /// copy the whole data region of src. If dst is uninitialized, it will be /// resized to be a float ImageBuf large enough to hold the region specified /// by roi. It is an error to pass both an uninitialied dst and an undefined /// roi. /// /// The nthreads parameter specifies how many threads (potentially) may be /// used, but it's not a guarantee. If nthreads == 0, it will use the /// global OIIO attribute "nthreads". If nthreads == 1, it guarantees that /// it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API flip (ImageBuf &dst, const ImageBuf &src, ROI roi=ROI::All(), int nthreads=0); /// Copy src to dst, but with the columns exchanged horizontally within /// the display/full window. In other words, /// AB --> BA /// CD DC /// /// Only the pixels (and channels) in src that are specified by roi will be /// copied to their corresponding positions in dst; the default roi is to /// copy the whole data region of src. If dst is uninitialized, it will be /// resized to be a float ImageBuf large enough to hold the region specified /// by roi. It is an error to pass both an uninitialied dst and an undefined /// roi. /// /// The nthreads parameter specifies how many threads (potentially) may be /// used, but it's not a guarantee. If nthreads == 0, it will use the /// global OIIO attribute "nthreads". If nthreads == 1, it guarantees that /// it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API flop (ImageBuf &dst, const ImageBuf &src, ROI roi=ROI::All(), int nthreads=0); /// Copy src to dst, but with whatever seties of rotations, flips, or flops /// are necessary to transform the pixels into the configuration suggested /// by the Orientation metadata of the image (and the Orientation metadata /// is then set to 1, ordinary orientation). /// /// The nthreads parameter specifies how many threads (potentially) may be /// used, but it's not a guarantee. If nthreads == 0, it will use the /// global OIIO attribute "nthreads". If nthreads == 1, it guarantees that /// it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API reorient (ImageBuf &dst, const ImageBuf &src, int nthreads=0); /// Copy a subregion of src to the corresponding transposed (x<->y) /// pixels of dst. In other words, for all (x,y) within the ROI, set /// dst[y,x] = src[x,y]. /// AB --> AC /// CD BD /// /// Only the pixels (and channels) of src that are specified by roi will /// be copied to dst; the default roi is to alter all the pixels in dst. /// If dst is uninitialized, it will be resized to be an ImageBuf large /// enough to hold the region specified by the transposed roi. It is an /// error to pass both an uninitialied dst and an undefined roi. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API transpose (ImageBuf &dst, const ImageBuf &src, ROI roi=ROI::All(), int nthreads=0); /// Copy a subregion of src to the pixels of dst, but circularly /// shifting by the given amount. To clarify, the circular shift /// of [0,1,2,3,4,5] by +2 is [4,5,0,1,2,3]. /// /// Only the pixels (and channels) of src that are specified by roi will /// be copied to dst; the default roi is to alter all the pixels in dst. /// If dst is uninitialized, it will be resized to be an ImageBuf large /// enough to hold the region specified by the transposed roi. It is an /// error to pass both an uninitialied dst and an undefined roi. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API circular_shift (ImageBuf &dst, const ImageBuf &src, int xshift, int yshift, int zshift=0, ROI roi=ROI::All(), int nthreads=0); /// Copy pixels from src to dst (within the ROI), clamping the values /// as follows: /// min[0..nchans-1] specifies the minimum clamp value for each channel /// (if min is NULL, no minimum clamping is performed). /// max[0..nchans-1] specifies the maximum clamp value for each channel /// (if max is NULL, no maximum clamping is performed). /// If clampalpha01 is true, then additionally any alpha channel is /// clamped to the 0-1 range. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API clamp (ImageBuf &dst, const ImageBuf &src, const float *min=NULL, const float *max=NULL, bool clampalpha01 = false, ROI roi = ROI::All(), int nthreads = 0); /// Copy pixels from src to dst (within the ROI), clamping the values of /// as follows: /// All channels are clamped to [min,max]. /// If clampalpha01 is true, then additionally any alpha channel is /// clamped to the 0-1 range. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API clamp (ImageBuf &dst, const ImageBuf &src, float min=-std::numeric_limits::max(), float max=std::numeric_limits::max(), bool clampalpha01 = false, ROI roi = ROI::All(), int nthreads = 0); /// For all pixels within the designated region, set dst = A + B. /// It is permitted for any of dst, A, or B to be the same image. /// /// If roi is not initialized, it will be set to the union of the pixel /// regions of A and B. If dst is not initialized, it will be sized /// based on roi. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API add (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi=ROI::All(), int nthreads=0); /// For all pixels and channels of dst within the designated region, set /// dst = A + B. (B must point to nchannels floats.) It is permitted for /// dst and A to be the same image. /// /// If roi is not initialized, it will be set to the pixel region of A. /// If dst is not initialized, it will be sized based on roi. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API add (ImageBuf &dst, const ImageBuf &A, const float *B, ROI roi=ROI::All(), int nthreads=0); /// For all pixels and channels of dst within the designated region, set /// dst = A + B. (B is a single float that is added to all channels.) /// It is permitted for dst and A to be the same image. /// /// If roi is not initialized, it will be set to the pixel region of A. /// If dst is not initialized, it will be sized based on roi. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API add (ImageBuf &dst, const ImageBuf &A, float B, ROI roi=ROI::All(), int nthreads=0); /// For all pixels within the designated ROI, compute dst = A - B. /// It is permitted for any of dst, A, or B to be the same image. /// /// If roi is not initialized, it will be set to the union of the pixel /// regions of A and B. If dst is not initialized, it will be sized /// based on roi. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API sub (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi=ROI::All(), int nthreads=0); /// For all pixels and channels of dst within the designated region, set /// dst = A - B. (B must point to nchannels floats.) /// It is permitted for dst and A to be the same image. /// /// If roi is not initialized, it will be set to the pixel region of A. /// If dst is not initialized, it will be sized based on roi. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API sub (ImageBuf &dst, const ImageBuf &A, const float *B, ROI roi=ROI::All(), int nthreads=0); /// For all pixels and channels of dst within the designated region, set /// dst = A - B. (B is a single float that is added to all channels.) /// It is permitted for dst and A to be the same image. /// /// If roi is not initialized, it will be set to the pixel region of A. /// If dst is not initialized, it will be sized based on roi. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API sub (ImageBuf &dst, const ImageBuf &A, float B, ROI roi=ROI::All(), int nthreads=0); /// For all pixels within the designated ROI, compute dst = abs(A - B). /// It is permitted for any of dst, A, or B to be the same image. /// /// A is an ImageBuf. B may be an ImageBuf (with the same number of channels /// as A), or an array of floats (one per channel, for each channel of A), /// or a single float (same value for all channels). /// /// If roi is not initialized, it will be set to the union of the pixel /// regions of A and B. If dst is not initialized, it will be sized based /// on roi. If dst is initialized, it also must have the same number of /// channels as A. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API absdiff (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi=ROI::All(), int nthreads=0); bool OIIO_API absdiff (ImageBuf &dst, const ImageBuf &A, const float *B, ROI roi=ROI::All(), int nthreads=0); bool OIIO_API absdiff (ImageBuf &dst, const ImageBuf &A, float B, ROI roi=ROI::All(), int nthreads=0); /// For all pixels within the designated ROI, compute dst = abs(A). /// It is permitted for dst and A to be the same image. /// /// If roi is not initialized, it will be set to the pixel data region of A. /// If dst is not initialized, it will be sized based on roi. If dst is /// initialized, it must have the same number of channels as A. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API abs (ImageBuf &dst, const ImageBuf &A, ROI roi=ROI::All(), int nthreads=0); /// For all pixels within the designated ROI, compute dst = A * B. /// All three images must have the same number of channels. /// It is permitted for dst and A to be the same image. /// /// If roi is not initialized, it will be set to the union of the pixel /// regions of A and B. If dst is not initialized, it will be sized /// based on roi. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API mul (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi=ROI::All(), int nthreads=0); /// For all pixels and channels of dst within region roi (defaulting to /// all the defined pixels of dst), set dst = A * B. /// It is permitted for dst and A to be the same image. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API mul (ImageBuf &dst, const ImageBuf &A, float B, ROI roi=ROI::All(), int nthreads=0); /// For all pixels and channels of dst within region roi (defaulting to /// all the defined pixels of dst), set dst = A * B. /// It is permitted for dst and A to be the same image. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API mul (ImageBuf &dst, const ImageBuf &A, const float *B, ROI roi=ROI::All(), int nthreads=0); /// For all pixels within the designated ROI, compute dst = A / B. /// We define division-by-zero to result in 0. /// It is permitted for any of dst, A, or B to be the same image. /// /// A is an ImageBuf. B may be an ImageBuf (with the same number of channels /// as A), or an array of floats (one per channel, for each channel of A), /// or a single float (same value for all channels). /// /// If roi is not initialized, it will be set to the union of the pixel /// regions of A and B. If dst is not initialized, it will be sized based /// on roi. If dst is initialized, it also must have the same number of /// channels as A. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API div (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi=ROI::All(), int nthreads=0); bool OIIO_API div (ImageBuf &dst, const ImageBuf &A, const float *B, ROI roi=ROI::All(), int nthreads=0); bool OIIO_API div (ImageBuf &dst, const ImageBuf &A, float B, ROI roi=ROI::All(), int nthreads=0); /// For all pixels and channels of dst within region roi (defaulting to /// all the defined pixels of dst), set dst = A * B + C. This is equivalent /// to the sequence { mul(tmp,A,B); add(dst,tmp,C); }, but is likely to be /// more efficient and not require a temporary ImageBuf. /// It is permitted for any of dst, A, B, or C to be the same image. /// /// A is always an ImageBuf. B and C may either both be ImageBuf or both be /// arrays of floats (one per channel, for each channel of A), /// or both be a single float (same value for all channels). /// /// If roi is not initialized, it will be set to the union of the pixel /// regions of A and B. If dst is not initialized, it will be sized based /// on roi. If dst is initialized, it also must have the same number of /// channels as A. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API mad (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, const ImageBuf &C, ROI roi=ROI::All(), int nthreads=0); bool OIIO_API mad (ImageBuf &dst, const ImageBuf &A, const float *B, const float *C, ROI roi=ROI::All(), int nthreads=0); bool OIIO_API mad (ImageBuf &dst, const ImageBuf &A, float B, float C, ROI roi=ROI::All(), int nthreads=0); inline bool OIIO_API mad (ImageBuf &dst, float A, const ImageBuf &B, float C, ROI roi=ROI::All(), int nthreads=0) { return mad (dst, B, A, C, roi, nthreads); } /// For all pixels and channels within the designated ROI, compute /// dst = 1 - A. It is permitted for dst and A to be the same image. /// /// Tips for callers: (1) You probably want to set roi to restrict the /// operation to only the color channels, and not accidentally include /// alpha, z, or others. (2) There may be situations where you want to /// unpremult() before the inverst, then premult() the result, so that you /// are computing the inverse of the unmasked color. /// /// If roi is not initialized, it will be set to the pixel data region of A. /// If dst is not initialized, it will be sized based on roi. If dst is /// initialized, it must have the same number of channels as A. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API invert (ImageBuf &dst, const ImageBuf &A, ROI roi=ROI::All(), int nthreads=0); /// For all pixels and channels of dst within region roi (defaulting to /// all the defined pixels of dst), set dst = A ^ b. (raise to power) /// B may be either a single float (for all channels), or a float* pointing /// to one value per channel of A. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API pow (ImageBuf &dst, const ImageBuf &A, float B, ROI roi=ROI::All(), int nthreads=0); bool OIIO_API pow (ImageBuf &dst, const ImageBuf &A, const float *B, ROI roi=ROI::All(), int nthreads=0); /// Converts a multi-channel image into a 1-channel image via a weighted /// sum of channels. For each pixel of src within the designated ROI /// (defaulting to all of src, if not defined), sum the channels /// designated by roi and store the result in channel 0 of dst. If /// weights is not NULL, weight[i] will provide a per-channel weight /// (rather than defaulting to 1.0 for each channel). /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API channel_sum (ImageBuf &dst, const ImageBuf &src, const float *weights=NULL, ROI roi=ROI::All(), int nthreads=0); /// For all pixels and color channels within region roi (defaulting to all /// the defined pixels of dst), copy pixels from src to dst, rescaling their /// range with a logarithmic transformation. Alpha and z channels are not /// transformed. If dst is not already defined and allocated, it will be /// sized based on src and roi. /// /// If useluma is true, the luma of channels [roi.chbegin..roi.chbegin+2] /// (presumed to be R, G, and B) are used to compute a single scale /// factor for all color channels, rather than scaling all channels /// individually (which could result in a color shift). /// /// Some image operations (such as resizing with a "good" filter that /// contains negative lobes) can have objectionable artifacts when applied /// to images with very high-contrast regions involving extra bright pixels /// (such as highlights in HDR captured or rendered images). By compressing /// the range pixel values, then performing the operation, then expanding /// the range of the result again, the result can be much more pleasing /// (even if not exactly correct). /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API rangecompress (ImageBuf &dst, const ImageBuf &src, bool useluma = false, ROI roi = ROI::All(), int nthreads=0); /// rangeexpand is the opposite operation of rangecompress -- rescales /// the logarithmic color channel values back to a linear response. bool OIIO_API rangeexpand (ImageBuf &dst, const ImageBuf &src, bool useluma = false, ROI roi = ROI::All(), int nthreads=0); /// Copy pixels within the ROI from src to dst, applying a color transform. /// In-place operations (dst == src) are supported. /// /// If dst is not yet initialized, it will be allocated to the same /// size as specified by roi. If roi is not defined it will be all /// of dst, if dst is defined, or all of src, if dst is not yet defined. /// /// If unpremult is true, unpremultiply before color conversion, then /// premultiply after the color conversion. You may want to use this /// flag if your image contains an alpha channel. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API colorconvert (ImageBuf &dst, const ImageBuf &src, string_view from, string_view to, bool unpremult=false, string_view context_key="", string_view context_value="", ColorConfig *colorconfig=NULL, ROI roi=ROI::All(), int nthreads=0); // DEPRECATED: [1.7] bool OIIO_API colorconvert (ImageBuf &dst, const ImageBuf &src, string_view from, string_view to, bool unpremult=false, ColorConfig *colorconfig=NULL, ROI roi=ROI::All(), int nthreads=0); OIIO_DEPRECATED("Use the version that takes a ColorConfig*. [1.6]") bool OIIO_API colorconvert (ImageBuf &dst, const ImageBuf &src, string_view from, string_view to, bool unpremult, ROI roi, int nthreads=0); /// Copy pixels within the ROI from src to dst, applying a color transform. /// In-place operations (dst == src) are supported. /// /// If dst is not yet initialized, it will be allocated to the same /// size as specified by roi. If roi is not defined it will be all /// of dst, if dst is defined, or all of src, if dst is not yet defined. /// /// If unpremult is true, unpremultiply before color conversion, then /// premultiply after the color conversion. You may want to use this /// flag if your image contains an alpha channel. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API colorconvert (ImageBuf &dst, const ImageBuf &src, const ColorProcessor *processor, bool unpremult, ROI roi=ROI::All(), int nthreads=0); /// Apply a color transform in-place to just one color: /// color[0..nchannels-1]. nchannels should either be 3 or 4 (if 4, the /// last channel is alpha). /// /// If unpremult is true, unpremultiply before color conversion, then /// premultiply after the color conversion. You'll may want to use this /// flag if your image contains an alpha channel. bool OIIO_API colorconvert (float *color, int nchannels, const ColorProcessor *processor, bool unpremult); /// Copy pixels within the ROI from src to dst, applying an OpenColorIO /// "look" transform. In-place operations (dst == src) are supported. /// /// If dst is not yet initialized, it will be allocated to the same /// size as specified by roi. If roi is not defined it will be all /// of dst, if dst is defined, or all of src, if dst is not yet defined. /// /// If unpremult is true, unpremultiply before color conversion, then /// premultiply after the color conversion. You may want to use this /// flag if your image contains an alpha channel. If inverse is true, it /// will reverse the color transformation. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API ociolook (ImageBuf &dst, const ImageBuf &src, string_view looks, string_view from, string_view to, bool unpremult=false, bool inverse=false, string_view key="", string_view value="", ColorConfig *colorconfig=NULL, ROI roi=ROI::All(), int nthreads=0); OIIO_DEPRECATED("Use the version that takes a ColorConfig*. [1.6]") bool OIIO_API ociolook (ImageBuf &dst, const ImageBuf &src, string_view looks, string_view from, string_view to, bool unpremult, bool inverse, string_view key, string_view value, ROI roi, int nthreads=0); /// Copy pixels within the ROI from src to dst, applying an OpenColorIO /// "display" transform. If from or looks are NULL, it will not /// override the look or source color space (subtly different than /// passing "", the empty string, which means to use no look or source /// space). /// /// If dst is not yet initialized, it will be allocated to the same /// size as specified by roi. If roi is not defined it will be all /// of dst, if dst is defined, or all of src, if dst is not yet defined. /// In-place operations (dst == src) are supported. /// /// If unpremult is true, unpremultiply before color conversion, then /// premultiply after the color conversion. You may want to use this /// flag if your image contains an alpha channel. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API ociodisplay (ImageBuf &dst, const ImageBuf &src, string_view display, string_view view, string_view from="", string_view looks="", bool unpremult=false, string_view key="", string_view value="", ColorConfig *colorconfig=NULL, ROI roi=ROI::All(), int nthreads=0); OIIO_DEPRECATED("Use the version that takes a ColorConfig*. [1.6]") bool OIIO_API ociodisplay (ImageBuf &dst, const ImageBuf &src, string_view display, string_view view, string_view from, string_view looks, bool unpremult, string_view key, string_view value, ROI roi, int nthreads=0); /// Copy pixels within the ROI from src to dst, applying an OpenColorIO /// "file" transform. If inverse is true, it will reverse the color /// transformation. In-place operations (dst == src) are supported. /// /// If dst is not yet initialized, it will be allocated to the same /// size as specified by roi. If roi is not defined it will be all /// of dst, if dst is defined, or all of src, if dst is not yet defined. /// /// If unpremult is true, unpremultiply before color conversion, then /// premultiply after the color conversion. You may want to use this /// flag if your image contains an alpha channel. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API ociofiletransform (ImageBuf &dst, const ImageBuf &src, string_view name, bool unpremult=false, bool inverse=false, ColorConfig *colorconfig=NULL, ROI roi=ROI::All(), int nthreads=0); /// Copy pixels from dst to src, and in the process divide all color /// channels (those not alpha or z) by the alpha value, to "un-premultiply" /// them. This presumes that the image starts of as "associated alpha" /// a.k.a. "premultipled." The alterations are restricted to the pixels and /// channels of the supplied ROI (which defaults to all of src). Pixels in /// which the alpha channel is 0 will not be modified (since the operation /// is undefined in that case). This is just a copy if there is no /// identified alpha channel (and a no-op if dst and src are the same /// image). /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API unpremult (ImageBuf &dst, const ImageBuf &src, ROI roi = ROI::All(), int nthreads = 0); /// Copy pixels from dst to src, and in the process multiply all color /// channels (those not alpha or z) by the alpha value, to "premultiply" /// them. This presumes that the image starts off as "unassociated alpha" /// a.k.a. "non-premultiplied." The alterations are restricted to the /// pixels and channels of the supplied ROI (which defaults to all of src). /// Pixels in which the alpha channel is 0 will not be modified (since the /// operation is undefined in that case). This is just a copy if there is /// no identified alpha channel (and a no-op if dst and src are the same /// image). /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API premult (ImageBuf &dst, const ImageBuf &src, ROI roi = ROI::All(), int nthreads = 0); struct OIIO_API PixelStats { std::vector min; std::vector max; std::vector avg; std::vector stddev; std::vector nancount; std::vector infcount; std::vector finitecount; std::vector sum, sum2; // for intermediate calculation }; /// Compute statistics about the ROI of the specified image. Upon success, /// the returned vectors will have size == src.nchannels(). /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API computePixelStats (PixelStats &stats, const ImageBuf &src, ROI roi=ROI::All(), int nthreads=0); /// Struct holding all the results computed by ImageBufAlgo::compare(). /// (maxx,maxy,maxz,maxc) gives the pixel coordintes (x,y,z) and color /// channel of the pixel that differed maximally between the two images. /// nwarn and nfail are the number of "warnings" and "failures", /// respectively. struct CompareResults { double meanerror, rms_error, PSNR, maxerror; int maxx, maxy, maxz, maxc; imagesize_t nwarn, nfail; }; /// Numerically compare two images. The difference threshold (for any /// individual color channel in any pixel) for a "failure" is /// failthresh, and for a "warning" is warnthresh. The results are /// stored in result. If roi is defined, pixels will be compared for /// the pixel and channel range that is specified. If roi is not /// defined, the comparison will be for all channels, on the union of /// the defined pixel windows of the two images (for either image, /// undefined pixels will be assumed to be black). /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error. bool OIIO_API compare (const ImageBuf &A, const ImageBuf &B, float failthresh, float warnthresh, CompareResults &result, ROI roi = ROI::All(), int nthreads=0); /// Compare two images using Hector Yee's perceptual metric, returning /// the number of pixels that fail the comparison. Only the first three /// channels (or first three channels specified by roi) are compared. /// Free parameters are the ambient luminance in the room and the field /// of view of the image display; our defaults are probably reasonable /// guesses for an office environment. The 'result' structure will /// store the maxerror, and the maxx, maxy, maxz of the pixel that /// failed most severely. (The other fields of the CompareResults /// are not used for Yee comparison.) /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Works for all pixel types. But it's basically meaningless if the /// first three channels aren't RGB in a linear color space that sort /// of resembles AdobeRGB. /// /// Return true on success, false on error. int OIIO_API compare_Yee (const ImageBuf &A, const ImageBuf &B, CompareResults &result, float luminance = 100, float fov = 45, ROI roi = ROI::All(), int nthreads = 0); /// Do all pixels within the ROI have the same values for channels /// [roi.chbegin..roi.chend-1]? If so, return true and store that color /// in color[chbegin...chend-1] (if color != NULL); otherwise return /// false. If roi is not defined (the default), it will be understood /// to be all of the defined pixels and channels of source. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. bool OIIO_API isConstantColor (const ImageBuf &src, float *color = NULL, ROI roi = ROI::All(), int nthreads=0); /// Does the requested channel have a given value over the ROI? (For /// this function, the ROI's chbegin/chend are ignored.) Return true if /// so, otherwise return false. If roi is not defined (the default), it /// will be understood to be all of the defined pixels and channels of /// source. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. bool OIIO_API isConstantChannel (const ImageBuf &src, int channel, float val, ROI roi = ROI::All(), int nthreads = 0); /// Is the image monochrome within the ROI, i.e., for all pixels within /// the region, do all channels [roi.chbegin, roi.chend) have the same /// value? If roi is not defined (the default), it will be understood /// to be all of the defined pixels and channels of source. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. bool OIIO_API isMonochrome (const ImageBuf &src, ROI roi = ROI::All(), int nthreads = 0); /// Count how many pixels in the ROI match a list of colors. /// /// The colors to match are in colors[0..nchans-1], /// colors[nchans..2*nchans-1], and so on, a total of ncolors /// consecutive colors of nchans each. /// /// eps[0..nchans-1] are the error tolerances for a match, for each /// channel. Setting eps[c] = numeric_limits::max() will /// effectively make it ignore the channel. Passing eps == NULL will be /// interpreted as a tolerance of 0.001 for all channels (requires exact /// matches for 8 bit images, but allows a wee bit of imprecision for /// float images. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Upon success, return true and store the number of pixels that /// matched each color count[..ncolors-1]. If there is an error, /// returns false and sets an appropriate error message set in src. bool OIIO_API color_count (const ImageBuf &src, imagesize_t *count, int ncolors, const float *color, const float *eps=NULL, ROI roi=ROI::All(), int nthreads=0); /// Count how many pixels in the ROI are outside the value range. /// low[0..nchans-1] and high[0..nchans-1] are the low and high /// acceptable values for each color channel. /// /// The number of pixels containing values that fall below the lower /// bound will be stored in *lowcount, the number of pixels containing /// values that fall above the upper bound will be stored in *highcount, /// and the number of pixels for which all channels fell within the /// bounds will be stored in *inrangecount. Any of these may be NULL, /// which simply means that the counts need not be collected or stored. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true if the operation can be performed, false if there is /// some sort of error (and sets an appropriate error message in src). bool OIIO_API color_range_check (const ImageBuf &src, imagesize_t *lowcount, imagesize_t *highcount, imagesize_t *inrangecount, const float *low, const float *high, ROI roi=ROI::All(), int nthreads=0); /// Find the minimal rectangular region within roi (which defaults to /// the entire pixel data window of src) that consists of nonzero pixel /// values. In other words, gives the region that is a "shrink-wraps" /// of src to exclude black border pixels. Note that if the entire /// image was black, the ROI returned will contain no pixels. /// /// For "deep" images, this function returns the smallest ROI that contains /// all pixels that contain depth samples, and excludes the border pixels /// that contain no depth samples at all. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. OIIO_API ROI nonzero_region (const ImageBuf &src, ROI roi=ROI::All(), int nthreads=0); /// Compute the SHA-1 byte hash for all the pixels in the specifed /// region of the image. If blocksize > 0, the function will compute /// separate SHA-1 hashes of each 'blocksize' batch of scanlines, then /// return a hash of the individual hashes. This is just as strong a /// hash, but will NOT match a single hash of the entire image /// (blocksize==0). But by breaking up the hash into independent /// blocks, we can parallelize across multiple threads, given by /// nthreads (if nthreads is 0, it will use the global OIIO thread /// count). The 'extrainfo' provides additional text that will be /// incorporated into the hash. std::string OIIO_API computePixelHashSHA1 (const ImageBuf &src, string_view extrainfo = "", ROI roi = ROI::All(), int blocksize = 0, int nthreads=0); /// Warp the src image using the supplied 3x3 transformation matrix. /// /// Only the pixels (and channels) of dst that are specified by roi will be /// copied from the warped src; the default roi is to alter all the pixels /// in dst. If dst is uninitialized, it will be sized to be an ImageBuf /// large enough to hold the warped image if recompute_roi is true, or /// will have the same ROI as src if recompute_roi is false. It is an error /// to pass both an uninitialied dst and an undefined roi. /// /// The filter is used to weight the src pixels falling underneath it for /// each dst pixel. The caller may specify a reconstruction filter by name /// and width (expressed in pixels unts of the dst image), or rotate() will /// choose a reasonable default high-quality default filter (lanczos3) if /// the empty string is passed, and a reasonable filter width if filterwidth /// is 0. (Note that some filter choices only make sense with particular /// width, in which case this filterwidth parameter may be ignored.) /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API warp (ImageBuf &dst, const ImageBuf &src, const Imath::M33f &M, string_view filtername = string_view(), float filterwidth = 0.0f, bool recompute_roi = false, ImageBuf::WrapMode wrap = ImageBuf::WrapDefault, ROI roi=ROI::All(), int nthreads=0); bool OIIO_API warp (ImageBuf &dst, const ImageBuf &src, const Imath::M33f &M, const Filter2D *filter, bool recompute_roi = false, ImageBuf::WrapMode wrap = ImageBuf::WrapDefault, ROI roi = ROI::All(), int nthreads = 0); /// Rotate the src image by the angle (in radians, with positive angles /// clockwise). When center_x and center_y are supplied, they denote the /// center of rotation; in their absence, the rotation will be about the /// center of the image's display window. /// /// Only the pixels (and channels) of dst that are specified by roi will be /// copied from the rotated src; the default roi is to alter all the pixels /// in dst. If dst is uninitialized, it will be resized to be an ImageBuf /// large enough to hold the rotated image if recompute_roi is true, or will /// have the same ROI as src if recompute_roi is false. It is an error to /// pass both an uninitialied dst and an undefined roi. /// /// The filter is used to weight the src pixels falling underneath it for /// each dst pixel. The caller may specify a reconstruction filter by name /// and width (expressed in pixels unts of the dst image), or rotate() will /// choose a reasonable default high-quality default filter (lanczos3) if /// the empty string is passed, and a reasonable filter width if filterwidth /// is 0. (Note that some filter choices only make sense with particular /// width, in which case this filterwidth parameter may be ignored.) /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API rotate (ImageBuf &dst, const ImageBuf &src, float angle, string_view filtername = string_view(), float filterwidth = 0.0f, bool recompute_roi = false, ROI roi=ROI::All(), int nthreads=0); bool OIIO_API rotate (ImageBuf &dst, const ImageBuf &src, float angle, Filter2D *filter, bool recompute_roi = false, ROI roi = ROI::All(), int nthreads = 0); bool OIIO_API rotate (ImageBuf &dst, const ImageBuf &src, float angle, float center_x, float center_y, string_view filtername = string_view(), float filterwidth = 0.0f, bool recompute_roi = false, ROI roi=ROI::All(), int nthreads=0); bool OIIO_API rotate (ImageBuf &dst, const ImageBuf &src, float angle, float center_x, float center_y, Filter2D *filter, bool recompute_roi = false, ROI roi = ROI::All(), int nthreads = 0); /// Set dst, over the region of interest, to be a resized version of the /// corresponding portion of src (mapping such that the "full" image /// window of each correspond to each other, regardless of resolution). /// /// The filter is used to weight the src pixels falling underneath it for /// each dst pixel. The caller may specify a reconstruction filter by name /// and width (expressed in pixels unts of the dst image), or resize() will /// choose a reasonable default high-quality default filter (blackman-harris /// when upsizing, lanczos3 when downsizing) if the empty string is passed /// or if filterwidth is 0. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API resize (ImageBuf &dst, const ImageBuf &src, string_view filtername = "", float filterwidth = 0.0f, ROI roi = ROI::All(), int nthreads = 0); /// Set dst, over the region of interest, to be a resized version of the /// corresponding portion of src (mapping such that the "full" image /// window of each correspond to each other, regardless of resolution). /// /// The caller may explicitly pass a reconstruction filter, or resize() /// will choose a reasonable default if NULL is passed. The filter is /// used to weight the src pixels falling underneath it for each dst /// pixel; the filter's size is expressed in pixel units of the dst /// image. If no filter is supplied, a default medium-quality /// (triangle) filter will be used. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API resize (ImageBuf &dst, const ImageBuf &src, Filter2D *filter, ROI roi = ROI::All(), int nthreads = 0); /// Set dst, over the region of interest, to be a resampled version of the /// corresponding portion of src (mapping such that the "full" image /// window of each correspond to each other, regardless of resolution). /// /// Unlike ImageBufAlgo::resize(), resample does not take a filter; it /// just samples either with a bilinear interpolation (if interpolate is /// true, the default) or uses the single "closest" pixel (if /// interpolate is false). This makes it a lot faster than a proper /// resize(), though obviously with lower quality (aliasing when /// downsizing, pixel replication when upsizing). /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API resample (ImageBuf &dst, const ImageBuf &src, bool interpolate = true, ROI roi = ROI::All(), int nthreads = 0); /// Replace the given ROI of dst with the convolution of src and /// a kernel. If roi is not defined, it defaults to the full size /// of dst (or src, if dst was uninitialized). If dst is uninitialized, /// it will be allocated to be the size specified by roi. If /// normalized is true, the kernel will be normalized for the /// convolution, otherwise the original values will be used. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API convolve (ImageBuf &dst, const ImageBuf &src, const ImageBuf &kernel, bool normalize = true, ROI roi = ROI::All(), int nthreads = 0); /// Initialize dst to be a 1-channel FLOAT image of the named kernel. /// The size of the dst image will be big enough to contain the kernel /// given its size (width x height) and rounded up to odd resolution so /// that the center of the kernel can be at the center of the middle /// pixel. The kernel image will be offset so that its center is at the /// (0,0) coordinate. If normalize is true, the values will be /// normalized so that they sum to 1.0. /// /// If depth > 1, a volumetric kernel will be created. Use with caution! /// /// Kernel names can be: "gaussian", "sharp-gaussian", "box", /// "triangle", "blackman-harris", "mitchell", "b-spline", "catmull-rom", /// "lanczos3", "disk", "binomial", "laplacian". /// /// Note that "catmull-rom" and "lanczos3" are fixed-size kernels that /// don't scale with the width, and are therefore probably less useful /// in most cases. /// bool OIIO_API make_kernel (ImageBuf &dst, string_view name, float width, float height, float depth = 1.0f, bool normalize = true); /// Replace the given ROI of dst with a sharpened version of the /// corresponding region of src using the ``unsharp mask'' technique. /// Unsharp masking basically works by first blurring the image (low /// pass filter), subtracting this from the original image, then /// adding the residual back to the original to emphasize the edges. /// Roughly speaking, /// dst = src + contrast * thresh(src - blur(src)) /// /// The specific blur can be selected by kernel name and width. The /// contrast is a multiplier on the overall sharpening effect. The /// thresholding step causes all differences less than 'threshold' to be /// squashed to zero, which can be useful for suppressing sharpening of /// low-contrast details (like noise) but allow sharpening of /// higher-contrast edges. /// /// If roi is not defined, it defaults to the full size of dst (or src, /// if dst was undefined). If dst is uninitialized, it will be /// allocated to be the size specified by roi. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API unsharp_mask (ImageBuf &dst, const ImageBuf &src, string_view kernel="gaussian", float width = 3.0f, float contrast = 1.0f, float threshold = 0.0f, ROI roi = ROI::All(), int nthreads = 0); /// Replace the given ROI of dst with the Laplacian of the corresponding /// region of src. This is approximated by convolving src with the discrete /// 3x3 Laplacian kernel, /// [ 0 1 0 ] /// [ 1 -4 1 ] /// [ 0 1 0 ] /// /// If roi is not defined, it defaults to the full size of dst (or src, /// if dst was undefined). If dst is uninitialized, it will be /// allocated to be the size specified by roi. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API laplacian (ImageBuf &dst, const ImageBuf &src, ROI roi = ROI::All(), int nthreads = 0); /// Replace the given ROI of dst with a median-filtered version of the /// corresponding region of src. The size of the window over which the /// median is computed is given by width and height (if height is <= 0, /// it will be set to width, making a square filter). /// /// Median filters are good for removing high-frequency detail smaller than /// the window size (including noise), without blurring edges that are /// larger than the window size. /// /// If roi is not defined, it defaults to the full size of dst (or src, /// if dst was undefined). If dst is uninitialized, it will be /// allocated to be the size specified by roi. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API median_filter (ImageBuf &dst, const ImageBuf &src, int width = 3, int height = -1, ROI roi = ROI::All(), int nthreads = 0); /// Replace the given ROI of dst with the dilated version of the /// corresponding region of src. Dilation is definted as the maximum /// value of all pixels under nonzero values of the structuring element /// (which is taken to be a width x height square). If height is not /// set, it will default to be the same as width. /// /// If roi is not defined, it defaults to the full size of dst (or src, /// if dst was undefined). If dst is uninitialized, it will be /// allocated to be the size specified by roi. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API dilate (ImageBuf &dst, const ImageBuf &src, int width = 3, int height = -1, ROI roi = ROI::All(), int nthreads = 0); /// Replace the given ROI of dst with the eroded version of the /// corresponding region of src. Erosion is definted as the minimum /// value of all pixels under nonzero values of the structuring element /// (which is taken to be a width x height square). If height is not /// set, it will default to be the same as width. /// /// If roi is not defined, it defaults to the full size of dst (or src, /// if dst was undefined). If dst is uninitialized, it will be /// allocated to be the size specified by roi. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API erode (ImageBuf &dst, const ImageBuf &src, int width = 3, int height = -1, ROI roi = ROI::All(), int nthreads = 0); /// Take the discrete Fourier transform (DFT) of the section of src /// denoted by roi, store it in dst. If roi is not defined, it will be /// all of src's pixels. Only one channel of src may be FFT'd at a /// time, so it will be the first channel described by roi (or, again, /// channel 0 if roi is undefined). If not already in the correct /// format, dst will be re-allocated to be a 2-channel float buffer of /// size width x height, with channel 0 being the "real" part and /// channel 1 being the the "imaginary" part. The values returned are /// actually the unitary DFT, meaning that it is scaled by 1/sqrt(npixels). /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Works on all pixel data type for src; dst will always be reallocated /// as FLOAT. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API fft (ImageBuf &dst, const ImageBuf &src, ROI roi = ROI::All(), int nthreads = 0); /// Take the inverse discrete Fourier transform of the section of src /// denoted by roi, store it in dst. If roi is not defined, it will be /// all of src's pixels. /// /// Src MUST be a 2-channel float image, and is assumed to be a complex /// frequency-domain signal with the "real" component in channel 0 and /// the "imaginary" component in channel 1. Dst will end up being a /// float image of one channel (the real component is kept, the /// imaginary component of the spatial-domain will be discarded). /// Just as with fft(), the ifft() function is dealing with the unitary /// DFT, so it is scaled by 1/sqrt(npixels). /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API ifft (ImageBuf &dst, const ImageBuf &src, ROI roi = ROI::All(), int nthreads = 0); /// Convert a 2-channel image with "polar" values (amplitude, phase) /// into a 2-channel image with complex values (real, imaginary). /// /// The transformation between the two representations are: /// real = amplitude * cos(phase); /// imag = amplitude * sin(phase); /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API polar_to_complex (ImageBuf &dst, const ImageBuf &src, ROI roi = ROI::All(), int nthreads = 0); /// Convert a 2-channel image with complex values (real, imaginary) into a /// 2-channel image with "polar" values (amplitude, phase). /// /// The transformation between the two representations are: /// amplitude = hypot (real, imag); /// phase = atan2 (imag, real); /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API complex_to_polar (ImageBuf &dst, const ImageBuf &src, ROI roi = ROI::All(), int nthreads = 0); enum OIIO_API NonFiniteFixMode { NONFINITE_NONE = 0, ///< Do nothing NONFINITE_BLACK = 1, ///< Replace nonfinite pixels with black NONFINITE_BOX3 = 2, ///< Replace nonfinite pixels with 3x3 finite average NONFINITE_ERROR = 100, ///< Error if any nonfinite pixels are found }; /// Copy the values of src (within the ROI) to dst, while repairing any /// non-finite (NaN/Inf) pixels. If pixelsFound is not NULL, store in it the /// number of pixels that contained non-finite value. It is permissible /// to operate in-place (with src and dst referring to the same image). /// /// How the non-finite values are repaired is specified by one of the /// following modes: /// NONFINITE_NONE do not alter the pixels (but do count the number /// of nonfinite pixels in *pixelsFixed, if non-NULL). /// NONFINITE_BLACK change non-finite values to 0. /// NONFINITE_BOX3 replace non-finite values by the average of any /// finite pixels within a 3x3 window. /// NONFINITE_ERROR return false (error), but don't change any values, /// if any nonfinite values are found. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Works on all pixel data types, though it's just a copy for images with /// pixel data types that cannot represent NaN or Inf values. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API fixNonFinite (ImageBuf &dst, const ImageBuf &src, NonFiniteFixMode mode=NONFINITE_BOX3, int *pixelsFixed = NULL, ROI roi = ROI::All(), int nthreads = 0); /// Fill the holes using a push-pull technique. The src image must have /// an alpha channel. The dst image will end up with a copy of src, but /// will have an alpha of 1.0 everywhere, and any place where the alpha /// of src was < 1, dst will have a pixel color that is a plausible /// "filling" of the original alpha hole. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. /// /// Return true on success, false on error (with an appropriate error /// message set in dst). bool OIIO_API fillholes_pushpull (ImageBuf &dst, const ImageBuf &src, ROI roi = ROI::All(), int nthreads = 0); /// Convert an IplImage, used by OpenCV and Intel's Image Libray, and /// set ImageBuf dst to be the same image (copying the pixels). If /// convert is not set to UNKNOWN, try to establish dst as holding that /// data type and convert the IplImage data. Return true if ok, false /// if it couldn't figure out how to make the conversion from IplImage /// to ImageBuf. If OpenImageIO was compiled without OpenCV support, /// this function will return false without modifying dst. bool OIIO_API from_IplImage (ImageBuf &dst, const IplImage *ipl, TypeDesc convert=TypeDesc::UNKNOWN); /// Construct an IplImage*, used by OpenCV and Intel's Image Library, /// that is equivalent to the ImageBuf src. If it is not possible, or /// if OpenImageIO was compiled without OpenCV support, then return /// NULL. The ownership of the IplImage is fully transferred to the /// calling application. OIIO_API IplImage* to_IplImage (const ImageBuf &src); /// Capture a still image from a designated camera. If able to do so, /// store the image in dst and return true. If there is no such device, /// or support for camera capture is not available (such as if OpenCV /// support was not enabled at compile time), return false and do not /// alter dst. bool OIIO_API capture_image (ImageBuf &dst, int cameranum = 0, TypeDesc convert=TypeDesc::UNKNOWN); /// Set dst to the composite of A over B using the Porter/Duff definition /// of "over", returning true upon success and false for any of a /// variety of failures (as described below). /// /// A and B (and dst, if already defined/allocated) must have valid alpha /// channels identified by their ImageSpec alpha_channel field. If A or /// B do not have alpha channels (as determined by those rules) or if /// the number of non-alpha channels do not match between A and B, /// over() will fail, returning false. /// /// If dst is not already an initialized ImageBuf, it will be sized to /// encompass the minimal rectangular pixel region containing the union /// of the defined pixels of A and B, and with a number of channels /// equal to the number of non-alpha channels of A and B, plus an alpha /// channel. However, if dst is already initialized, it will not be /// resized, and the "over" operation will apply to its existing pixel /// data window. In this case, dst must have an alpha channel designated /// and must have the same number of non-alpha channels as A and B, /// otherwise it will fail, returning false. /// /// 'roi' specifies the region of dst's pixels which will be computed; /// existing pixels outside this range will not be altered. If not /// specified, the default ROI value will be interpreted as a request to /// apply "A over B" to the entire region of dst's pixel data. /// /// A, B, and dst need not perfectly overlap in their pixel data windows; /// pixel values of A or B that are outside their respective pixel data /// window will be treated as having "zero" (0,0,0...) value. /// /// The nthreads parameter specifies how many threads (potentially) may /// be used, but it's not a guarantee. If nthreads == 0, it will use /// the global OIIO attribute "nthreads". If nthreads == 1, it /// guarantees that it will not launch any new threads. bool OIIO_API over (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi = ROI::All(), int nthreads = 0); /// Just like ImageBufAlgo::over(), but inputs A and B must have /// designated 'z' channels, and on a pixel-by-pixel basis, the z values /// will determine which of A or B will be considered the foreground or /// background (lower z is foreground). If z_zeroisinf is true, then /// z=0 values will be treated as if they are infinitely far away. bool OIIO_API zover (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, bool z_zeroisinf = false, ROI roi = ROI::All(), int nthreads = 0); /// Render a single point at (x,y) of the given color "over" the existing /// image dst. If there is no alpha channel, the color will be written /// unconditionally (as if the alpha is 1.0). The color array view must /// contain at least as many values as channels in the image. bool OIIO_API render_point (ImageBuf &dst, int x, int y, array_view color, ROI roi = ROI::All(), int nthreads = 0); /// Render a line from (x1,y1) to (x2,y2) of the given color "over" the /// existing image dst. If there is no alpha channel, the color will be /// written unconditionally (as if the alpha is 1.0). The color array view /// must contain at least as many values as channels in the image. /// If skip_first_point is true, the very first point (x1,y1) will not /// be rendered; this can be useful for rendering segments of poly-lines /// to avoid double-rendering the vertex positions. bool OIIO_API render_line (ImageBuf &dst, int x1, int y1, int x2, int y2, array_view color, bool skip_first_point = false, ROI roi = ROI::All(), int nthreads = 0); /// Render an a filled or unfilled box with corners (x1,y1) and (x2,y2) of /// the given color "over" the existing image dst. If there is no alpha /// channel, the color will be written unconditionally (as if the alpha is /// 1.0). The color array view must contain at least as many values as /// channels in the image. bool OIIO_API render_box (ImageBuf &dst, int x1, int y1, int x2, int y2, array_view color, bool fill = false, ROI roi = ROI::All(), int nthreads = 0); /// Render a text string (encoded as UTF-8) into image dst, essentially /// doing an "over" of the character into the existing pixel data. The /// baseline of the first character will start at position (x,y). The font /// is given by fontname as a full pathname to the font file (defaulting to /// some reasonable system font if not supplied at all), and with a nominal /// height of fontsize (in pixels). The characters will be drawn in opaque /// white (1.0,1.0,...) in all channels, unless textcolor is supplied (and /// is expected to point to a float array of length at least equal to /// R.spec().nchannels). bool OIIO_API render_text (ImageBuf &dst, int x, int y, string_view text, int fontsize=16, string_view fontname="", const float *textcolor = NULL); /// ImageBufAlgo::histogram -------------------------------------------------- /// Parameters: /// src - Input image that contains the one channel to be histogramed. /// src must contain float pixel data and have at least 1 channel, /// but it can have more. /// channel - Only this channel in src will be histogramed. It must satisfy /// 0 <= channel < src.nchannels(). /// histogram - Clear old content and store the histogram here. /// bins - Number of bins must be at least 1. /// min, max - Pixel values outside of the min->max range are not used for /// computing the histogram. If min max. /// roi - Only pixels in this region of the image are histogramed. If /// roi is not defined then the full size image will be /// histogramed. /// -------------------------------------------------------------------------- bool OIIO_API histogram (const ImageBuf &src, int channel, std::vector &histogram, int bins=256, float min=0, float max=1, imagesize_t *submin=NULL, imagesize_t *supermax=NULL, ROI roi=ROI::All()); /// ImageBufAlgo::histogram_draw --------------------------------------------- /// Parameters: /// dst - The histogram will be drawn in the image dst. which must /// have only 1 channel with float pixel data, and width equal /// to the number of bins, that is elements in histogram. /// histogram - The histogram to be drawn, must have at least 1 bin. /// -------------------------------------------------------------------------- bool OIIO_API histogram_draw (ImageBuf &dst, const std::vector &histogram); enum OIIO_API MakeTextureMode { MakeTxTexture, MakeTxShadow, MakeTxEnvLatl, MakeTxEnvLatlFromLightProbe, _MakeTxLast }; /// Turn an image into a tiled, MIP-mapped, texture file and write it to /// disk (outputfilename). The 'mode' describes what type of texture /// file we are creating and may be one of: /// MakeTxTexture Ordinary 2D texture /// MakeTxEnvLatl Latitude-longitude environment map /// MakeTxEnvLatlFromLightProbe Latitude-longitude environment map /// constructed from a "light probe" image. /// /// If the outstream pointer is not NULL, it should point to a stream /// (for example, &std::out, or a pointer to a local std::stringstream /// to capture output), which is where console output and error messages /// will be deposited. /// /// The 'config' is an ImageSpec that contains all the information and /// special instructions for making the texture. Anything set in config /// (format, tile size, or named metadata) will take precedence over /// whatever is specified by the input file itself. Additionally, named /// metadata that starts with "maketx:" will not be output to the file /// itself, but may contain instructions controlling how the texture is /// created. The full list of supported configuration options is: /// /// Named fields: /// format Data format of the texture file (default: UNKNOWN = /// same format as the input) /// tile_width Preferred tile size (default: 64x64x1) /// tile_height /// tile_depth /// Metadata in config.extra_attribs: /// compression (string) Default: "zip" /// fovcot (float) Default: aspect ratio of the image resolution /// planarconfig (string) Default: "separate" /// worldtocamera (matrix) World-to-camera matrix of the view. /// worldtoscreen (matrix) World-to-screen space matrix of the view. /// wrapmodes (string) Default: "black,black" /// maketx:verbose (int) How much detail should go to outstream (0). /// maketx:runstats (int) If nonzero, print run stats to outstream (0). /// maketx:resize (int) If nonzero, resize to power of 2. (0) /// maketx:nomipmap (int) If nonzero, only output the top MIP level (0). /// maketx:updatemode (int) If nonzero, write new output only if the /// output file doesn't already exist, or is /// older than the input file, or was created /// with different command-line arguments. (0) /// maketx:constant_color_detect (int) /// If nonzero, detect images that are entirely /// one color, and change them to be low /// resolution (default: 0). /// maketx:monochrome_detect (int) /// If nonzero, change RGB images which have /// R==G==B everywhere to single-channel /// grayscale (default: 0). /// maketx:opaque_detect (int) /// If nonzero, drop the alpha channel if alpha /// is 1.0 in all pixels (default: 0). /// maketx:compute_average (int) /// If nonzero, compute and store the average /// color of the texture (default: 1). /// maketx:unpremult (int) If nonzero, unpremultiply color by alpha before /// color conversion, then multiply by alpha /// after color conversion (default: 0). /// maketx:incolorspace (string) /// maketx:outcolorspace (string) /// These two together will apply a color conversion /// (with OpenColorIO, if compiled). Default: "" /// maketx:checknan (int) If nonzero, will consider it an error if the /// input image has any NaN pixels. (0) /// maketx:fixnan (string) If set to "black" or "box3", will attempt /// to repair any NaN pixels found in the /// input image (default: "none"). /// maketx:set_full_to_pixels (int) /// If nonzero, doctors the full/display window /// of the texture to be identical to the /// pixel/data window and reset the origin /// to 0,0 (default: 0). /// maketx:filtername (string) /// If set, will specify the name of a high-quality /// filter to use when resampling for MIPmap /// levels. Default: "", use bilinear resampling. /// maketx:highlightcomp (int) /// If nonzero, performs highlight compensation -- /// range compression and expansion around /// the resize, plus clamping negative plxel /// values to zero. This reduces ringing when /// using filters with negative lobes on HDR /// images. /// maketx:sharpen (float) If nonzero, sharpens details when creating /// MIPmap levels. The amount is the contrast /// matric. The default is 0, meaning no /// sharpening. /// maketx:nchannels (int) If nonzero, will specify how many channels /// the output texture should have, padding with /// 0 values or dropping channels, if it doesn't /// the number of channels in the input. /// (default: 0, meaning keep all input channels) /// maketx:channelnames (string) /// If set, overrides the channel names of the /// output image (comma-separated). /// maketx:fileformatname (string) /// If set, will specify the output file format. /// (default: "", meaning infer the format from /// the output filename) /// maketx:prman_metadata (int) /// If set, output some metadata that PRMan will /// need for its textures. (0) /// maketx:oiio_options (int) /// (Deprecated; all are handled by default) /// maketx:prman_options (int) /// If nonzero, override a whole bunch of settings /// as needed to make textures that are /// compatible with PRMan. (0) /// maketx:mipimages (string) /// Semicolon-separated list of alternate images /// to be used for individual MIPmap levels, /// rather than simply downsizing. (default: "") /// maketx:full_command_line (string) /// The command or program used to generate this /// call, will be embedded in the metadata. /// (default: "") /// maketx:ignore_unassoc (int) /// If nonzero, will disbelieve any evidence that /// the input image is unassociated alpha. (0) /// maketx:read_local_MB (int) /// If nonzero, will read the full input file locally /// if it is smaller than this threshold. Zero /// causes the system to make a good guess at /// a reasonable threshold (e.g. 1 GB). (0) /// maketx:forcefloat (int) /// Forces a conversion through float data for /// the sake of ImageBuf math. (1) /// maketx:hash (int) /// Compute the sha1 hash of the file in parallel. (1) /// maketx:allow_pixel_shift (int) /// Allow up to a half pixel shift per mipmap level. /// The fastest path may result in a slight shift /// in the image, accumulated for each mip level /// with an odd resolution. (0) /// bool OIIO_API make_texture (MakeTextureMode mode, const ImageBuf &input, string_view outputfilename, const ImageSpec &config, std::ostream *outstream = NULL); /// Version of make_texture that starts with a filename and reads the input /// from that file, rather than being given an ImageBuf directly. bool OIIO_API make_texture (MakeTextureMode mode, string_view filename, string_view outputfilename, const ImageSpec &config, std::ostream *outstream = NULL); /// Version of make_texture that takes multiple filenames (reserved for /// future expansion, such as assembling several faces into a cube map). bool OIIO_API make_texture (MakeTextureMode mode, const std::vector &filenames, string_view outputfilename, const ImageSpec &config, std::ostream *outstream = NULL); } // end namespace ImageBufAlgo OIIO_NAMESPACE_END #endif // OPENIMAGEIO_IMAGEBUFALGO_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/texture.h0000644000175000017500000010246613151711064022515 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// \file /// An API for accessing filtered texture lookups via a system that /// automatically manages a cache of resident texture. #ifndef OPENIMAGEIO_TEXTURE_H #define OPENIMAGEIO_TEXTURE_H #include "varyingref.h" #include "ustring.h" #include "imageio.h" #include /* because we need V3f */ OIIO_NAMESPACE_BEGIN // Forward declaration namespace pvt { class TextureSystemImpl; // Used internally by TextureSystem. Unfortunately, this is the only // clean place to store it. Sorry, users, this isn't really for you. enum TexFormat { TexFormatUnknown, TexFormatTexture, TexFormatTexture3d, TexFormatShadow, TexFormatCubeFaceShadow, TexFormatVolumeShadow, TexFormatLatLongEnv, TexFormatCubeFaceEnv, TexFormatLast }; enum EnvLayout { LayoutTexture=0 /* ordinary texture - no special env wrap */, LayoutLatLong, LayoutCubeThreeByTwo, LayoutCubeOneBySix, EnvLayoutLast }; } // pvt namespace /// Data type for flags that indicate on a point-by-point basis whether /// we want computations to be performed. typedef unsigned char Runflag; /// Pre-defined values for Runflag's. /// enum RunFlagVal { RunFlagOff = 0, RunFlagOn = 255 }; class TextureOptions; // forward declaration /// Encapsulate all the options needed for texture lookups. Making /// these options all separate parameters to the texture API routines is /// very ugly and also a big pain whenever we think of new options to /// add. So instead we collect all those little options into one /// structure that can just be passed by reference to the texture API /// routines. class OIIO_API TextureOpt { public: /// Wrap mode describes what happens when texture coordinates describe /// a value outside the usual [0,1] range where a texture is defined. enum Wrap { WrapDefault, ///< Use the default found in the file WrapBlack, ///< Black outside [0..1] WrapClamp, ///< Clamp to [0..1] WrapPeriodic, ///< Periodic mod 1 WrapMirror, ///< Mirror the image WrapPeriodicPow2, ///< Periodic, but only for powers of 2!!! WrapPeriodicSharedBorder, ///< Periodic with shared border (env) WrapLast ///< Mark the end -- don't use this! }; /// Mip mode determines if/how we use mipmaps /// enum MipMode { MipModeDefault, ///< Default high-quality lookup MipModeNoMIP, ///< Just use highest-res image, no MIP mapping MipModeOneLevel, ///< Use just one mipmap level MipModeTrilinear, ///< Use two MIPmap levels (trilinear) MipModeAniso ///< Use two MIPmap levels w/ anisotropic }; /// Interp mode determines how we sample within a mipmap level /// enum InterpMode { InterpClosest, ///< Force closest texel InterpBilinear, ///< Force bilinear lookup within a mip level InterpBicubic, ///< Force cubic lookup within a mip level InterpSmartBicubic ///< Bicubic when maxifying, else bilinear }; /// Create a TextureOpt with all fields initialized to reasonable /// defaults. TextureOpt () : firstchannel(0), subimage(0), swrap(WrapDefault), twrap(WrapDefault), mipmode(MipModeDefault), interpmode(InterpSmartBicubic), anisotropic(32), conservative_filter(true), sblur(0.0f), tblur(0.0f), swidth(1.0f), twidth(1.0f), fill(0.0f), missingcolor(NULL), // dresultds(NULL), dresultdt(NULL), time(0.0f), // bias(0.0f), samples(1), rwrap(WrapDefault), rblur(0.0f), rwidth(1.0f), // dresultdr(NULL), // actualchannels(0), envlayout(0) { } /// Convert a TextureOptions for one index into a TextureOpt. /// TextureOpt (const TextureOptions &opt, int index); int firstchannel; ///< First channel of the lookup int subimage; ///< Subimage or face ID ustring subimagename; ///< Subimage name Wrap swrap; ///< Wrap mode in the s direction Wrap twrap; ///< Wrap mode in the t direction MipMode mipmode; ///< Mip mode InterpMode interpmode; ///< Interpolation mode int anisotropic; ///< Maximum anisotropic ratio bool conservative_filter; ///< True == over-blur rather than alias float sblur, tblur; ///< Blur amount float swidth, twidth; ///< Multiplier for derivatives float fill; ///< Fill value for missing channels const float *missingcolor;///< Color for missing texture float time; ///< Time (for time-dependent texture lookups) float bias; ///< Bias for shadows int samples; ///< Number of samples for shadows // For 3D volume texture lookups only: Wrap rwrap; ///< Wrap mode in the r direction float rblur; ///< Blur amount in the r direction float rwidth; ///< Multiplier for derivatives in r direction /// Utility: Return the Wrap enum corresponding to a wrap name: /// "default", "black", "clamp", "periodic", "mirror". static Wrap decode_wrapmode (const char *name); static Wrap decode_wrapmode (ustring name); /// Utility: Parse a single wrap mode (e.g., "periodic") or a /// comma-separated wrap modes string (e.g., "black,clamp") into /// separate Wrap enums for s and t. static void parse_wrapmodes (const char *wrapmodes, TextureOpt::Wrap &swrapcode, TextureOpt::Wrap &twrapcode); private: // Options set INTERNALLY by libtexture after the options are passed // by the user. Users should not attempt to alter these! int envlayout; // Layout for environment wrap friend class pvt::TextureSystemImpl; }; /// Encapsulate all the options needed for texture lookups. Making /// these options all separate parameters to the texture API routines is /// very ugly and also a big pain whenever we think of new options to /// add. So instead we collect all those little options into one /// structure that can just be passed by reference to the texture API /// routines. class OIIO_API TextureOptions { public: /// Wrap mode describes what happens when texture coordinates describe /// a value outside the usual [0,1] range where a texture is defined. enum Wrap { WrapDefault, ///< Use the default found in the file WrapBlack, ///< Black outside [0..1] WrapClamp, ///< Clamp to [0..1] WrapPeriodic, ///< Periodic mod 1 WrapMirror, ///< Mirror the image WrapPeriodicPow2, ///< Periodic, but only for powers of 2!!! WrapPeriodicSharedBorder, ///< Periodic with shared border (env) WrapLast ///< Mark the end -- don't use this! }; /// Mip mode determines if/how we use mipmaps /// enum MipMode { MipModeDefault, ///< Default high-quality lookup MipModeNoMIP, ///< Just use highest-res image, no MIP mapping MipModeOneLevel, ///< Use just one mipmap level MipModeTrilinear, ///< Use two MIPmap levels (trilinear) MipModeAniso ///< Use two MIPmap levels w/ anisotropic }; /// Interp mode determines how we sample within a mipmap level /// enum InterpMode { InterpClosest, ///< Force closest texel InterpBilinear, ///< Force bilinear lookup within a mip level InterpBicubic, ///< Force cubic lookup within a mip level InterpSmartBicubic ///< Bicubic when maxifying, else bilinear }; /// Create a TextureOptions with all fields initialized to reasonable /// defaults. TextureOptions (); /// Convert a TextureOpt for one point into a TextureOptions with /// uninform values. TextureOptions (const TextureOpt &opt); // Options that must be the same for all points we're texturing at once int firstchannel; ///< First channel of the lookup int subimage; ///< Subimage or face ID ustring subimagename; ///< Subimage name Wrap swrap; ///< Wrap mode in the s direction Wrap twrap; ///< Wrap mode in the t direction MipMode mipmode; ///< Mip mode InterpMode interpmode; ///< Interpolation mode int anisotropic; ///< Maximum anisotropic ratio bool conservative_filter; ///< True == over-blur rather than alias // Options that may be different for each point we're texturing VaryingRef sblur, tblur; ///< Blur amount VaryingRef swidth, twidth; ///< Multiplier for derivatives VaryingRef time; ///< Time VaryingRef bias; ///< Bias VaryingRef fill; ///< Fill value for missing channels VaryingRef missingcolor; ///< Color for missing texture VaryingRef samples; ///< Number of samples // For 3D volume texture lookups only: Wrap rwrap; ///< Wrap mode in the r direction VaryingRef rblur; ///< Blur amount in the r direction VaryingRef rwidth; ///< Multiplier for derivatives in r direction /// Utility: Return the Wrap enum corresponding to a wrap name: /// "default", "black", "clamp", "periodic", "mirror". static Wrap decode_wrapmode (const char *name) { return (Wrap)TextureOpt::decode_wrapmode (name); } static Wrap decode_wrapmode (ustring name) { return (Wrap)TextureOpt::decode_wrapmode (name); } /// Utility: Parse a single wrap mode (e.g., "periodic") or a /// comma-separated wrap modes string (e.g., "black,clamp") into /// separate Wrap enums for s and t. static void parse_wrapmodes (const char *wrapmodes, TextureOptions::Wrap &swrapcode, TextureOptions::Wrap &twrapcode) { TextureOpt::parse_wrapmodes (wrapmodes, *(TextureOpt::Wrap *)&swrapcode, *(TextureOpt::Wrap *)&twrapcode); } private: // Options set INTERNALLY by libtexture after the options are passed // by the user. Users should not attempt to alter these! friend class pvt::TextureSystemImpl; friend class TextureOpt; }; /// Define an API to an abstract class that that manages texture files, /// caches of open file handles as well as tiles of texels so that truly /// huge amounts of texture may be accessed by an application with low /// memory footprint, and ways to perform antialiased texture, shadow /// map, and environment map lookups. class OIIO_API TextureSystem { public: /// Create a TextureSystem and return a pointer. This should only be /// freed by passing it to TextureSystem::destroy()! /// /// If shared==true, it's intended to be shared with other like-minded /// owners in the same process who also ask for a shared texture system. /// If false, a private texture system and cache will be created. static TextureSystem *create (bool shared=true); /// Destroy a TextureSystem that was created using /// TextureSystem::create(). For the variety that takes a /// teardown_imagecache parameter, if set to true it will cause the /// underlying ImageCache to be fully destroyed, even if it's the /// "shared" ImageCache. static void destroy (TextureSystem *x); static void destroy (TextureSystem *x, bool teardown_imagecache); TextureSystem (void) { } virtual ~TextureSystem () { } OIIO_DEPRECATED("clear() was never implemented. Don't bother calling it. [1.7]") virtual void clear () { } /// Set an attribute controlling the texture system. Return true /// if the name and type were recognized and the attrib was set. /// Documented attributes: /// int max_open_files : maximum number of file handles held open /// float max_memory_MB : maximum tile cache size, in MB /// string searchpath : colon-separated search path for texture files /// string plugin_searchpath : colon-separated search path for plugins /// matrix44 worldtocommon : the world-to-common transformation /// matrix44 commontoworld : the common-to-world transformation /// int autotile : if >0, tile size to emulate for non-tiled images /// int autoscanline : autotile using full width tiles /// int automip : if nonzero, emulate mipmap on the fly /// int accept_untiled : if nonzero, accept untiled images /// int accept_unmipped : if nonzero, accept unmipped images /// int failure_retries : how many times to retry a read failure /// int deduplicate : if nonzero, detect duplicate textures (default=1) /// int gray_to_rgb : make 1-channel images fill RGB lookups /// int max_tile_channels : max channels to store all chans in a tile /// string latlong_up : default "up" direction for latlong ("y") /// int flip_t : flip v coord for texture lookups? /// int max_errors_per_file : Limits how many errors to issue for /// issue for each (default: 100) /// virtual bool attribute (string_view name, TypeDesc type, const void *val) = 0; // Shortcuts for common types virtual bool attribute (string_view name, int val) = 0; virtual bool attribute (string_view name, float val) = 0; virtual bool attribute (string_view name, double val) = 0; virtual bool attribute (string_view name, string_view val) = 0; /// Get the named attribute, store it in value. virtual bool getattribute (string_view name, TypeDesc type, void *val) const = 0; // Shortcuts for common types virtual bool getattribute (string_view name, int &val) const = 0; virtual bool getattribute (string_view name, float &val) const = 0; virtual bool getattribute (string_view name, double &val) const = 0; virtual bool getattribute (string_view name, char **val) const = 0; virtual bool getattribute (string_view name, std::string &val) const = 0; /// Define an opaque data type that allows us to have a pointer /// to certain per-thread information that the TextureSystem maintains. /// Any given one of these should NEVER be shared between running /// threads. class Perthread; /// Retrieve a Perthread, unique to the calling thread. This is a /// thread-specific pointer that will always return the Perthread for a /// thread, which will also be automatically destroyed when the thread /// terminates. /// /// Applications that want to manage their own Perthread pointers (with /// create_thread_info and destroy_thread_info) should still call this, /// but passing in their managed pointer. If the passed-in threadinfo is /// not NULL, it won't create a new one or retrieve a TSP, but it will /// do other necessary housekeeping on the Perthread information. virtual Perthread * get_perthread_info (Perthread *thread_info=NULL) = 0; /// Create a new Perthread. It is the caller's responsibility to /// eventually destroy it using destroy_thread_info(). virtual Perthread * create_thread_info () = 0; /// Destroy a Perthread that was allocated by create_thread_info(). virtual void destroy_thread_info (Perthread *threadinfo) = 0; /// Define an opaque data type that allows us to have a handle to a /// texture (already having its name resolved) but without exposing /// any internals. class TextureHandle; /// Retrieve an opaque handle for fast texture lookups. The opaque /// pointer thread_info is thread-specific information returned by /// get_perthread_info(). Return NULL if something has gone /// horribly wrong. virtual TextureHandle * get_texture_handle (ustring filename, Perthread *thread_info=NULL) = 0; /// Return true if the texture handle (previously returned by /// get_texture_handle()) is a valid texture that can be subsequently /// read or sampled. virtual bool good (TextureHandle *texture_handle) = 0; /// Filtered 2D texture lookup for a single point. /// /// s,t are the texture coordinates; dsdx, dtdx, dsdy, and dtdy are /// the differentials of s and t change in some canonical directions /// x and y. The choice of x and y are not important to the /// implementation; it can be any imposed 2D coordinates, such as /// pixels in screen space, adjacent samples in parameter space on a /// surface, etc. /// /// The result is placed in result[0..nchannels-1]. If dresuls and /// dresultdt are non-NULL, then they [0..nchannels-1] will get the /// texture gradients, i.e., the rate of change per unit s and t, /// respectively, of the texture. /// /// Return true if the file is found and could be opened by an /// available ImageIO plugin, otherwise return false. virtual bool texture (ustring filename, TextureOpt &options, float s, float t, float dsdx, float dtdx, float dsdy, float dtdy, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL) = 0; /// Version that takes nchannels and derivatives explicitly, if the app /// already has a texture handle and per-thread info. virtual bool texture (TextureHandle *texture_handle, Perthread *thread_info, TextureOpt &options, float s, float t, float dsdx, float dtdx, float dsdy, float dtdy, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL) = 0; /// Retrieve filtered (possibly anisotropic) texture lookups for /// several points at once. /// /// All of the VaryingRef parameters (and fields in options) /// describe texture lookup parameters at an array of positions. /// But this routine only computes them from indices i where /// beginactive <= i < endactive, and ONLY when runflags[i] is /// nonzero. /// /// Return true if the file is found and could be opened by an /// available ImageIO plugin, otherwise return false. virtual bool texture (ustring filename, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef s, VaryingRef t, VaryingRef dsdx, VaryingRef dtdx, VaryingRef dsdy, VaryingRef dtdy, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL) = 0; virtual bool texture (TextureHandle *texture_handle, Perthread *thread_info, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef s, VaryingRef t, VaryingRef dsdx, VaryingRef dtdx, VaryingRef dsdy, VaryingRef dtdy, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL) = 0; /// Retrieve a 3D texture lookup at a single point. /// /// Return true if the file is found and could be opened by an /// available ImageIO plugin, otherwise return false. virtual bool texture3d (ustring filename, TextureOpt &options, const Imath::V3f &P, const Imath::V3f &dPdx, const Imath::V3f &dPdy, const Imath::V3f &dPdz, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL, float *dresultdr=NULL) = 0; /// Slightly faster version of texture3d() lookup if the app already /// has a texture handle and per-thread info. virtual bool texture3d (TextureHandle *texture_handle, Perthread *thread_info, TextureOpt &options, const Imath::V3f &P, const Imath::V3f &dPdx, const Imath::V3f &dPdy, const Imath::V3f &dPdz, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL, float *dresultdr=NULL) = 0; /// Retrieve a 3D texture lookup at many points at once. /// /// Return true if the file is found and could be opened by an /// available ImageIO plugin, otherwise return false. virtual bool texture3d (ustring filename, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef P, VaryingRef dPdx, VaryingRef dPdy, VaryingRef dPdz, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL, float *dresultdr=NULL) = 0; virtual bool texture3d (TextureHandle *texture_handle, Perthread *thread_info, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef P, VaryingRef dPdx, VaryingRef dPdy, VaryingRef dPdz, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL, float *dresultdr=NULL) = 0; /// Retrieve a shadow lookup for a single position P. /// /// Return true if the file is found and could be opened by an /// available ImageIO plugin, otherwise return false. virtual bool shadow (ustring filename, TextureOpt &options, const Imath::V3f &P, const Imath::V3f &dPdx, const Imath::V3f &dPdy, float *result, float *dresultds=NULL, float *dresultdt=NULL) = 0; /// Slightly faster version of shadow() lookup if the app already /// has a texture handle and per-thread info. virtual bool shadow (TextureHandle *texture_handle, Perthread *thread_info, TextureOpt &options, const Imath::V3f &P, const Imath::V3f &dPdx, const Imath::V3f &dPdy, float *result, float *dresultds=NULL, float *dresultdt=NULL) = 0; /// Retrieve a shadow lookup for position P at many points at once. /// /// Return true if the file is found and could be opened by an /// available ImageIO plugin, otherwise return false. virtual bool shadow (ustring filename, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef P, VaryingRef dPdx, VaryingRef dPdy, float *result, float *dresultds=NULL, float *dresultdt=NULL) = 0; virtual bool shadow (TextureHandle *texture_handle, Perthread *thread_info, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef P, VaryingRef dPdx, VaryingRef dPdy, float *result, float *dresultds=NULL, float *dresultdt=NULL) = 0; /// Retrieve an environment map lookup for direction R. /// /// Return true if the file is found and could be opened by an /// available ImageIO plugin, otherwise return false. virtual bool environment (ustring filename, TextureOpt &options, const Imath::V3f &R, const Imath::V3f &dRdx, const Imath::V3f &dRdy, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL) = 0; /// Slightly faster version of environment() lookup if the app already /// has a texture handle and per-thread info. virtual bool environment (TextureHandle *texture_handle, Perthread *thread_info, TextureOpt &options, const Imath::V3f &R, const Imath::V3f &dRdx, const Imath::V3f &dRdy, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL) = 0; /// Retrieve an environment map lookup for direction R, for many /// points at once. /// /// Return true if the file is found and could be opened by an /// available ImageIO plugin, otherwise return false. virtual bool environment (ustring filename, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef R, VaryingRef dRdx, VaryingRef dRdy, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL) = 0; virtual bool environment (TextureHandle *texture_handle, Perthread *thread_info, TextureOptions &options, Runflag *runflags, int beginactive, int endactive, VaryingRef R, VaryingRef dRdx, VaryingRef dRdy, int nchannels, float *result, float *dresultds=NULL, float *dresultdt=NULL) = 0; /// Given possibly-relative 'filename', resolve it using the search /// path rules and return the full resolved filename. virtual std::string resolve_filename (const std::string &filename) const=0; /// Get information about the given texture. Return true if found /// and the data has been put in *data. Return false if the texture /// doesn't exist, doesn't have the requested data, if the data /// doesn't match the type requested. or some other failure. virtual bool get_texture_info (ustring filename, int subimage, ustring dataname, TypeDesc datatype, void *data) = 0; virtual bool get_texture_info (TextureHandle *texture_handle, Perthread *thread_info, int subimage, ustring dataname, TypeDesc datatype, void *data) = 0; OIIO_DEPRECATED("Use get_texture_info variety that is passed a Perthread*. [1.6.2]") virtual bool get_texture_info (TextureHandle *texture_handle, int subimage, ustring dataname, TypeDesc datatype, void *data) = 0; /// Get the ImageSpec associated with the named texture /// (specifically, the first MIP-map level). If the file is found /// and is an image format that can be read, store a copy of its /// specification in spec and return true. Return false if the file /// was not found or could not be opened as an image file by any /// available ImageIO plugin. virtual bool get_imagespec (ustring filename, int subimage, ImageSpec &spec) = 0; virtual bool get_imagespec (TextureHandle *texture_handle, Perthread *thread_info, int subimage, ImageSpec &spec) = 0; /// Return a pointer to an ImageSpec associated with the named /// texture (specifically, the first MIP-map level) if the file is /// found and is an image format that can be read, otherwise return /// NULL. /// /// This method is much more efficient than get_imagespec(), since /// it just returns a pointer to the spec held internally by the /// underlying ImageCache (rather than copying the spec to the /// user's memory). However, the caller must beware that the /// pointer is only valid as long as nobody (even other threads) /// calls invalidate() on the file, or invalidate_all(), or destroys /// the TextureSystem. virtual const ImageSpec *imagespec (ustring filename, int subimage=0) = 0; virtual const ImageSpec *imagespec (TextureHandle *texture_handle, Perthread *thread_info = NULL, int subimage=0) = 0; /// Retrieve the rectangle of raw unfiltered texels spanning /// [xbegin..xend) X [ybegin..yend) X [zbegin..zend), with /// "exclusive end" a la STL, specified as integer pixel coordinates /// in the designated MIP-map level, storing the texel values /// beginning at the address specified by result. /// The texel values will be converted to the type specified by /// format. It is up to the caller to ensure that result points to /// an area of memory big enough to accommodate the requested /// rectangle (taking into consideration its dimensions, number of /// channels, and data format). Requested pixels outside /// the valid pixel data region will be filled in with 0 values. /// /// Return true if the file is found and could be opened by an /// available ImageIO plugin, otherwise return false. virtual bool get_texels (ustring filename, TextureOpt &options, int miplevel, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc format, void *result) = 0; virtual bool get_texels (TextureHandle *texture_handle, Perthread *thread_info, TextureOpt &options, int miplevel, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc format, void *result) = 0; /// If any of the API routines returned false indicating an error, /// this routine will return the error string (and clear any error /// flags). If no error has occurred since the last time geterror() /// was called, it will return an empty string. virtual std::string geterror () const = 0; /// Return the statistics output as a huge string. /// virtual std::string getstats (int level=1, bool icstats=true) const = 0; /// Invalidate any cached information about the named file. A client /// might do this if, for example, they are aware that an image /// being held in the cache has been updated on disk. virtual void invalidate (ustring filename) = 0; /// Invalidate all cached data for all textures. If force is true, /// everything will be invalidated, no matter how wasteful it is, /// but if force is false, in actuality files will only be /// invalidated if their modification times have been changed since /// they were first opened. virtual void invalidate_all (bool force=false) = 0; /// Reset most statistics to be as they were with a fresh /// TextureSystem. Caveat emptor: this does not flush the cache /// itelf, so the resulting statistics from the next set of texture /// requests will not match the number of tile reads, etc., that /// would have resulted from a new TextureSystem. virtual void reset_stats () = 0; private: // Make delete private and unimplemented in order to prevent apps // from calling it. Instead, they should call TextureSystem::destroy(). void operator delete (void * /*todel*/) { } }; OIIO_NAMESPACE_END #endif // OPENIMAGEIO_TEXTURE_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/osdep.h0000644000175000017500000000342413151711064022121 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_OSDEP_H #define OPENIMAGEIO_OSDEP_H #ifdef _WIN32 # define WIN32_LEAN_AND_MEAN # define VC_EXTRALEAN # define NOMINMAX # include #endif #endif // OPENIMAGEIO_OSDEP_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/fstream_mingw.h0000644000175000017500000002255413151711064023656 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// @file fstream_mingw.h /// /// @brief Utilities for dealing with fstream on MingW. /// Basically accepting wchar_t* filenames in the std::ifstream::open function /// is a Windows MSVC extension and does not work on MingW. This file implements /// ifstream and ofstream so that they work with UTF-16 filenames. #ifndef OPENIMAGEIO_FSTREAM_MINGW_H #define OPENIMAGEIO_FSTREAM_MINGW_H #include #include #include #if defined(_WIN32) && defined(__GLIBCXX__) #include // __gnu_cxx::stdio_filebuf #include #include #include OIIO_NAMESPACE_BEGIN template > class basic_ifstream : public std::basic_istream<_CharT, _Traits> { public: typedef _CharT char_type; typedef _Traits traits_type; typedef typename traits_type::int_type int_type; typedef typename traits_type::pos_type pos_type; typedef typename traits_type::off_type off_type; typedef typename __gnu_cxx::stdio_filebuf stdio_filebuf; basic_ifstream(); explicit basic_ifstream(const std::wstring& path, std::ios_base::openmode __mode = std::ios_base::in); virtual ~basic_ifstream(); stdio_filebuf* rdbuf() const; bool is_open() const; void open(const std::wstring& path, std::ios_base::openmode __mode = std::ios_base::in); void close(); private: void open_internal(const std::wstring& path, std::ios_base::openmode mode); stdio_filebuf* __sb_; }; template inline basic_ifstream<_CharT, _Traits>::basic_ifstream() : std::basic_istream(0) , __sb_(0) { } template inline basic_ifstream<_CharT, _Traits>::basic_ifstream(const std::wstring& path, std::ios_base::openmode __mode) : std::basic_istream(0) , __sb_(0) { open_internal(path, __mode | std::ios_base::in); } template inline basic_ifstream<_CharT, _Traits>::~basic_ifstream() { delete __sb_; } inline int ios_open_mode_to_oflag(std::ios_base::openmode mode) { int f = 0; if (mode & std::ios_base::in) { f |= _O_RDONLY; } if (mode & std::ios_base::out) { f |= _O_WRONLY; f |= _O_CREAT; if (mode & std::ios_base::app) { f |= _O_APPEND; } if (mode & std::ios_base::trunc) { f |= _O_TRUNC; } } if (mode & std::ios_base::binary) { f |= _O_BINARY; } else { f |= _O_TEXT; } return f; } template inline void basic_ifstream<_CharT, _Traits>::open_internal(const std::wstring& path, std::ios_base::openmode mode) { if (is_open()) { // if the stream is already associated with a file (i.e., it is already open), calling this function fails. this->setstate(std::ios_base::failbit); return; } int fd; int oflag = ios_open_mode_to_oflag(mode); errno_t errcode = _wsopen_s(&fd, path.c_str(), oflag, _SH_DENYNO, _S_IREAD | _S_IWRITE); if (errcode != 0) { this->setstate(std::ios_base::failbit); return; } __sb_ = new stdio_filebuf(fd, mode, 1); if (__sb_ == 0) { this->setstate(std::ios_base::failbit); return; } // 409. Closing an fstream should clear error state this->clear(); assert(__sb_); // In init() the rdstate() is set to badbit if __sb_ is NULL and // goodbit otherwise. The assert afterwards ensures this. this->init(__sb_); assert(this->good() && !this->fail()); } template inline typename basic_ifstream<_CharT, _Traits>::stdio_filebuf* basic_ifstream<_CharT, _Traits>::rdbuf() const { return const_cast(__sb_); } template inline bool basic_ifstream<_CharT, _Traits>::is_open() const { return __sb_ && __sb_->is_open(); } template void basic_ifstream<_CharT, _Traits>::open(const std::wstring& path, std::ios_base::openmode __mode) { open_internal(path, __mode | std::ios_base::in); } template inline void basic_ifstream<_CharT, _Traits>::close() { if (!__sb_) { return; } if (__sb_->close() == 0) this->setstate(std::ios_base::failbit); delete __sb_; __sb_= 0; } template > class basic_ofstream : public std::basic_ostream<_CharT, _Traits> { public: typedef _CharT char_type; typedef _Traits traits_type; typedef typename traits_type::int_type int_type; typedef typename traits_type::pos_type pos_type; typedef typename traits_type::off_type off_type; typedef typename __gnu_cxx::stdio_filebuf stdio_filebuf; basic_ofstream(); explicit basic_ofstream(const std::wstring& path, std::ios_base::openmode __mode = std::ios_base::out); virtual ~basic_ofstream(); stdio_filebuf* rdbuf() const; bool is_open() const; void open(const std::wstring& path, std::ios_base::openmode __mode = std::ios_base::out); void close(); private: void open_internal(const std::wstring& path, std::ios_base::openmode mode); stdio_filebuf* __sb_; }; template inline basic_ofstream<_CharT, _Traits>::basic_ofstream() : std::basic_ostream(0) , __sb_(0) { } template inline basic_ofstream<_CharT, _Traits>::basic_ofstream(const std::wstring& path, std::ios_base::openmode __mode) : std::basic_ostream(0) , __sb_(0) { open_internal(path, __mode | std::ios_base::out); } template inline basic_ofstream<_CharT, _Traits>::~basic_ofstream() { delete __sb_; } template inline void basic_ofstream<_CharT, _Traits>::open_internal(const std::wstring& path, std::ios_base::openmode mode) { if (is_open()) { // if the stream is already associated with a file (i.e., it is already open), calling this function fails. this->setstate(std::ios_base::failbit); return; } int fd; int oflag = ios_open_mode_to_oflag(mode); errno_t errcode = _wsopen_s(&fd, path.c_str(), oflag, _SH_DENYNO, _S_IREAD | _S_IWRITE); if (errcode != 0) { this->setstate(std::ios_base::failbit); return; } __sb_ = new stdio_filebuf(fd, mode, 1); if (__sb_ == 0) { this->setstate(std::ios_base::failbit); return; } // 409. Closing an fstream should clear error state this->clear(); assert(__sb_); // In init() the rdstate() is set to badbit if __sb_ is NULL and // goodbit otherwise. The assert afterwards ensures this. this->init(__sb_); assert(this->good() && !this->fail()); } template inline typename basic_ofstream<_CharT, _Traits>::stdio_filebuf* basic_ofstream<_CharT, _Traits>::rdbuf() const { return const_cast(__sb_); } template inline bool basic_ofstream<_CharT, _Traits>::is_open() const { return __sb_ && __sb_->is_open(); } template void basic_ofstream<_CharT, _Traits>::open(const std::wstring& path, std::ios_base::openmode __mode) { open_internal(path, __mode | std::ios_base::out); } template inline void basic_ofstream<_CharT, _Traits>::close() { if (!__sb_) { return; } if (__sb_->close() == 0) this->setstate(std::ios_base::failbit); delete __sb_; __sb_= 0; } // basic_fstream OIIO_NAMESPACE_END #endif // #if defined(_WIN32) && defined(__GLIBCXX__) #endif // OPENIMAGEIO_FSTREAM_MINGW_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/imagebuf.h0000644000175000017500000015102213151711064022564 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// \file /// Classes for in-memory storage and simple manipulation of whole images, /// which uses ImageInput and ImageOutput underneath for the file access. #ifndef OPENIMAGEIO_IMAGEBUF_H #define OPENIMAGEIO_IMAGEBUF_H #if defined(_MSC_VER) // Ignore warnings about DLL exported classes with member variables that are template classes. // This happens with the std::vector and std::string protected members of ImageBuf below. # pragma warning (disable : 4251) #endif #include "imageio.h" #include "fmath.h" #include "imagecache.h" #include "dassert.h" #include OIIO_NAMESPACE_BEGIN class ImageBuf; /// Helper struct describing a region of interest in an image. /// The region is [xbegin,xend) x [begin,yend) x [zbegin,zend), /// with the "end" designators signifying one past the last pixel, /// a la STL style. struct ROI { int xbegin, xend, ybegin, yend, zbegin, zend; int chbegin, chend; /// Default constructor is an undefined region. /// ROI () : xbegin(std::numeric_limits::min()), xend(0), ybegin(0), yend(0), zbegin(0), zend(0), chbegin(0), chend(0) { } /// Constructor with an explicitly defined region. /// ROI (int xbegin, int xend, int ybegin, int yend, int zbegin=0, int zend=1, int chbegin=0, int chend=10000) : xbegin(xbegin), xend(xend), ybegin(ybegin), yend(yend), zbegin(zbegin), zend(zend), chbegin(chbegin), chend(chend) { } /// Is a region defined? bool defined () const { return (xbegin != std::numeric_limits::min()); } // Region dimensions. int width () const { return xend - xbegin; } int height () const { return yend - ybegin; } int depth () const { return zend - zbegin; } /// Number of channels in the region. Beware -- this defaults to a /// huge number, and to be meaningful you must consider /// std::min (imagebuf.nchannels(), roi.nchannels()). int nchannels () const { return chend - chbegin; } /// Total number of pixels in the region. imagesize_t npixels () const { if (! defined()) return 0; imagesize_t w = width(), h = height(), d = depth(); return w*h*d; } /// Documentary sugar -- although the static ROI::All() function /// simply returns the results of the default ROI constructor, it /// makes it very clear when using as a default function argument /// that it means "all" of the image. For example, /// float myfunc (ImageBuf &buf, ROI roi = ROI::All()); /// Doesn't that make it abundantly clear? static ROI All () { return ROI(); } /// Test equality of two ROIs friend bool operator== (const ROI &a, const ROI &b) { return (a.xbegin == b.xbegin && a.xend == b.xend && a.ybegin == b.ybegin && a.yend == b.yend && a.zbegin == b.zbegin && a.zend == b.zend && a.chbegin == b.chbegin && a.chend == b.chend); } /// Test inequality of two ROIs friend bool operator!= (const ROI &a, const ROI &b) { return (a.xbegin != b.xbegin || a.xend != b.xend || a.ybegin != b.ybegin || a.yend != b.yend || a.zbegin != b.zbegin || a.zend != b.zend || a.chbegin != b.chbegin || a.chend != b.chend); } /// Stream output of the range friend std::ostream & operator<< (std::ostream &out, const ROI &roi) { out << roi.xbegin << ' ' << roi.xend << ' ' << roi.ybegin << ' ' << roi.yend << ' ' << roi.zbegin << ' ' << roi.zend << ' ' << roi.chbegin << ' ' << roi.chend; return out; } }; /// Union of two regions, the smallest region containing both. OIIO_API ROI roi_union (const ROI &A, const ROI &B); /// Intersection of two regions. OIIO_API ROI roi_intersection (const ROI &A, const ROI &B); /// Return pixel data window for this ImageSpec as a ROI. OIIO_API ROI get_roi (const ImageSpec &spec); /// Return full/display window for this ImageSpec as a ROI. OIIO_API ROI get_roi_full (const ImageSpec &spec); /// Set pixel data window for this ImageSpec to a ROI. /// Does NOT change the channels of the spec, regardless of newroi. OIIO_API void set_roi (ImageSpec &spec, const ROI &newroi); /// Set full/display window for this ImageSpec to a ROI. /// Does NOT change the channels of the spec, regardless of newroi. OIIO_API void set_roi_full (ImageSpec &spec, const ROI &newroi); class ImageBufImpl; // Opaque pointer /// An ImageBuf is a simple in-memory representation of a 2D image. It /// uses ImageInput and ImageOutput underneath for its file I/O, and has /// simple routines for setting and getting individual pixels, that /// hides most of the details of memory layout and data representation /// (translating to/from float automatically). class OIIO_API ImageBuf { public: /// Construct an empty/uninitialized ImageBuf. This is relatively /// useless until you call reset(). ImageBuf (); /// Construct an ImageBuf to read the named image (at the designated /// subimage/MIPlevel -- but don't actually read it yet! The image /// will actually be read when other methods need to access the spec /// and/or pixels, or when an explicit call to init_spec() or read() is /// made, whichever comes first. If a non-NULL imagecache is supplied, /// it will specifiy a custom ImageCache to use; if otherwise, the /// global/shared ImageCache will be used. /// If 'config' is not NULL, it points to an ImageSpec giving requests /// or special instructions to be passed on to the eventual /// ImageInput::open() call. explicit ImageBuf (string_view name, int subimage=0, int miplevel=0, ImageCache *imagecache = NULL, const ImageSpec *config = NULL); /// Construct an ImageBuf to read the named image -- but don't actually /// read it yet! The image will actually be read when other methods /// need to access the spec and/or pixels, or when an explicit call to /// init_spec() or read() is made, whichever comes first. If a non-NULL /// imagecache is supplied, it will specifiy a custom ImageCache to use; /// if otherwise, the global/shared ImageCache will be used. ImageBuf (string_view name, ImageCache *imagecache); /// Construct an Imagebuf given a proposed spec describing the image /// size and type, and allocate storage for the pixels of the image /// (whose values will be uninitialized). explicit ImageBuf (const ImageSpec &spec); /// Construct an Imagebuf given both a name and a proposed spec /// describing the image size and type, and allocate storage for /// the pixels of the image (whose values will be undefined). ImageBuf (string_view name, const ImageSpec &spec); /// Construct an ImageBuf that "wraps" a memory buffer owned by the /// calling application. It can write pixels to this buffer, but /// can't change its resolution or data type. ImageBuf (const ImageSpec &spec, void *buffer); /// Construct an ImageBuf that "wraps" a memory buffer owned by the /// calling application. It can write pixels to this buffer, but /// can't change its resolution or data type. ImageBuf (string_view name, const ImageSpec &spec, void *buffer); /// Construct a copy of an ImageBuf. /// ImageBuf (const ImageBuf &src); /// Destructor for an ImageBuf. /// ~ImageBuf (); /// Description of where the pixels live for this ImageBuf. enum IBStorage { UNINITIALIZED, // no pixel memory LOCALBUFFER, // The IB owns the memory APPBUFFER, // The IB wraps app's memory IMAGECACHE // Backed by ImageCache }; /// Restore the ImageBuf to an uninitialized state. /// void clear (); /// Forget all previous info, reset this ImageBuf to a new image /// that is uninitialized (no pixel values, no size or spec). /// If 'config' is not NULL, it points to an ImageSpec giving requests /// or special instructions to be passed on to the eventual /// ImageInput::open() call. void reset (string_view name, int subimage, int miplevel, ImageCache *imagecache = NULL, const ImageSpec *config = NULL); /// Forget all previous info, reset this ImageBuf to a new image /// that is uninitialized (no pixel values, no size or spec). void reset (string_view name, ImageCache *imagecache=NULL); /// Forget all previous info, reset this ImageBuf to a blank /// image of the given dimensions. void reset (const ImageSpec &spec); /// Forget all previous info, reset this ImageBuf to a blank /// image of the given name and dimensions. void reset (string_view name, const ImageSpec &spec); /// Which type of storage is being used for the pixels? IBStorage storage () const; /// Is this ImageBuf object initialized? bool initialized () const; /// Read the file from disk. Generally will skip the read if we've /// already got a current version of the image in memory, unless /// force==true. This uses ImageInput underneath, so will read any /// file format for which an appropriate imageio plugin can be found. /// Return value is true if all is ok, otherwise false. bool read (int subimage=0, int miplevel=0, bool force=false, TypeDesc convert=TypeDesc::UNKNOWN, ProgressCallback progress_callback=NULL, void *progress_callback_data=NULL); /// Initialize this ImageBuf with the named image file, and read its /// header to fill out the spec correctly. Return true if this /// succeeded, false if the file could not be read. But don't /// allocate or read the pixels. bool init_spec (string_view filename, int subimage, int miplevel); /// Write the image to the named file and file format ("" means to infer /// the type from the filename extension). Return true if all went ok, /// false if there were errors writing. bool write (string_view filename, string_view fileformat = string_view(), ProgressCallback progress_callback=NULL, void *progress_callback_data=NULL) const; /// Inform the ImageBuf what data format you'd like for any subsequent /// write(). void set_write_format (TypeDesc format); /// Inform the ImageBuf what tile size (or no tiling, for 0) for /// any subsequent write(). void set_write_tiles (int width=0, int height=0, int depth=0); /// Write the image to the open ImageOutput 'out'. Return true if /// all went ok, false if there were errors writing. It does NOT /// close the file when it's done (and so may be called in a loop to /// write a multi-image file). bool write (ImageOutput *out, ProgressCallback progress_callback=NULL, void *progress_callback_data=NULL) const; /// Force the ImageBuf to be writeable. That means that if it was /// previously backed by ImageCache (storage was IMAGECACHE), it will /// force a full read so that the whole image is in local memory. This /// will invalidate any current iterators on the image. It has no effect /// if the image storage not IMAGECACHE. Return true if it works /// (including if no read was necessary), false if something went /// horribly wrong. If keep_cache_type is true, it preserves any IC- /// forced data types (you might want to do this if it is critical that /// the apparent data type doesn't change, for example if you are /// calling make_writeable from within a type-specialized function). bool make_writeable (bool keep_cache_type = false); /// Copy all the metadata from src to *this (except for pixel data /// resolution, channel information, and data format). void copy_metadata (const ImageBuf &src); /// Copy the pixel data from src to *this, automatically converting /// to the existing data format of *this. It only copies pixels in /// the overlap regions (and channels) of the two images; pixel data /// in *this that do exist in src will be set to 0, and pixel data /// in src that do not exist in *this will not be copied. bool copy_pixels (const ImageBuf &src); /// Try to copy the pixels and metadata from src to *this, returning /// true upon success and false upon error/failure. /// /// If the previous state of *this was uninitialized, owning its own /// local pixel memory, or referring to a read-only image backed by /// ImageCache, then local pixel memory will be allocated to hold /// the new pixels and the call always succeeds unless the memory /// cannot be allocated. /// /// If *this previously referred to an app-owned memory buffer, the /// memory cannot be re-allocated, so the call will only succeed if /// the app-owned buffer is already the correct resolution and /// number of channels. The data type of the pixels will be /// converted automatically to the data type of the app buffer. bool copy (const ImageBuf &src); /// copy(src), but with optional override of pixel data type bool copy (const ImageBuf &src, TypeDesc format /*= TypeDesc::UNKNOWN*/); /// Swap with another ImageBuf void swap (ImageBuf &other) { std::swap (m_impl, other.m_impl); } /// Error reporting for ImageBuf: call this with printf-like /// arguments. Note however that this is fully typesafe! /// void error (const char *format, ...) TINYFORMAT_WRAP_FORMAT (void, error, const, std::ostringstream msg;, msg, append_error(msg.str());) /// Return true if the IB has had an error and has an error message /// to retrieve via geterror(). bool has_error (void) const; /// Return the text of all error messages issued since geterror() /// was called (or an empty string if no errors are pending). This /// also clears the error message for next time. std::string geterror (void) const; /// Return a read-only (const) reference to the image spec that /// describes the buffer. const ImageSpec & spec () const; /// Return a writable reference to the image spec that describes the /// buffer. Use with extreme caution! If you use this for anything /// other than adding attribute metadata, you are really taking your /// chances! ImageSpec & specmod (); /// Return a read-only (const) reference to the "native" image spec /// (that describes the file, which may be slightly different than /// the spec of the ImageBuf, particularly if the IB is backed by an /// ImageCache that is imposing some particular data format or tile /// size). const ImageSpec & nativespec () const; /// Return the name of this image. /// string_view name (void) const; /// Return the name of the image file format of the disk file we /// read into this image. Returns an empty string if this image /// was not the result of a read(). string_view file_format_name (void) const; /// Return the index of the subimage are we currently viewing /// int subimage () const; /// Return the number of subimages in the file. /// int nsubimages () const; /// Return the index of the miplevel are we currently viewing /// int miplevel () const; /// Return the number of miplevels of the current subimage. /// int nmiplevels () const; /// Return the number of color channels in the image. /// int nchannels () const; /// Wrap mode describes what happens when an iterator points to /// a value outside the usual data range of an image. enum WrapMode { WrapDefault, WrapBlack, WrapClamp, WrapPeriodic, WrapMirror, _WrapLast }; /// Retrieve a single channel of one pixel. /// float getchannel (int x, int y, int z, int c, WrapMode wrap=WrapBlack) const; /// Retrieve the pixel value by x and y pixel indices, /// storing the floating point version in pixel[]. Retrieve at most /// maxchannels (will be clamped to the actual number of channels). void getpixel (int x, int y, float *pixel, int maxchannels=1000) const { getpixel (x, y, 0, pixel, maxchannels); } /// Retrieve the pixel value by x, y, z pixel indices, /// storing the floating point version in pixel[]. Retrieve at most /// maxchannels (will be clamped to the actual number of channels). void getpixel (int x, int y, int z, float *pixel, int maxchannels=1000, WrapMode wrap=WrapBlack) const; /// Sample the image plane at coordinates (x,y), using linear /// interpolation between pixels, placing the result in pixel[0..n-1] /// where n is the smaller of maxchannels or the actual number of /// channels stored in the buffer. It is up to the application to /// ensure that pixel points to enough memory to hold the required /// number of channels. Note that pixel data values themselves are at /// the pixel centers, so pixel (i,j) is at image plane coordinate /// (i+0.5, j+0.5). void interppixel (float x, float y, float *pixel, WrapMode wrap=WrapBlack) const; /// Linearly interpolate at NDC coordinates (s,t), where (0,0) is /// the upper left corner of the display window, (1,1) the lower /// right corner of the display window. void interppixel_NDC (float s, float t, float *pixel, WrapMode wrap=WrapBlack) const; /// DEPCRECATED (1.5) synonym for interppixel_NDC. void interppixel_NDC_full (float s, float t, float *pixel, WrapMode wrap=WrapBlack) const; /// Bicubic interpolation at pixel coordinates (x,y), where (0,0) is /// the upper left corner, (xres,yres) the lower right corner of /// the pixel data. void interppixel_bicubic (float x, float y, float *pixel, WrapMode wrap=WrapBlack) const; /// Bicubic interpolattion at NDC space coordinates (s,t), where (0,0) /// is the upper left corner of the display (aka "full") window, (1,1) /// the lower right corner of the display window. void interppixel_bicubic_NDC (float s, float t, float *pixel, WrapMode wrap=WrapBlack) const; /// Set the pixel with coordinates (x,y,0) to have the values in /// pixel[0..n-1]. The number of channels copied, n, is the minimum /// of maxchannels and the actual number of channels in the image. void setpixel (int x, int y, const float *pixel, int maxchannels=1000) { setpixel (x, y, 0, pixel, maxchannels); } /// Set the pixel with coordinates (x,y,z) to have the values in /// pixel[0..n-1]. The number of channels copied, n, is the minimum /// of maxchannels and the actual number of channels in the image. void setpixel (int x, int y, int z, const float *pixel, int maxchannels=1000); /// Set the i-th pixel value of the image (out of width*height*depth), /// from floating-point values in pixel[]. Set at most /// maxchannels (will be clamped to the actual number of channels). void setpixel (int i, const float *pixel, int maxchannels=1000); /// Retrieve the rectangle of pixels spanning the ROI (including /// channels) at the current subimage and MIP-map level, storing the /// pixel values beginning at the address specified by result and with /// the given strides (by default, AutoStride means the usual contiguous /// packing of pixels) and converting into the data type described by /// 'format'. It is up to the caller to ensure that result points to an /// area of memory big enough to accommodate the requested rectangle. /// Return true if the operation could be completed, otherwise return /// false. bool get_pixels (ROI roi, TypeDesc format, void *result, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride) const; /// Copy the data into the given ROI of the ImageBuf. The data points to /// values specified by 'format', with layout detailed by the stride /// values (in bytes, with AutoStride indicating "contiguous" layout). /// It is up to the caller to ensure that data points to an area of /// memory big enough to account for the ROI. Return true if the /// operation could be completed, otherwise return false. bool set_pixels (ROI roi, TypeDesc format, const void *data, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride); OIIO_DEPRECATED("Use get_pixels(ROI, ...) instead. [1.6]") bool get_pixel_channels (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc format, void *result, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride) const; OIIO_DEPRECATED("Use get_pixels(ROI, ...) instead. [1.6]") bool get_pixels (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, void *result, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride) const; template OIIO_DEPRECATED("Use get_pixels(ROI, ...) instead. [1.6]") bool get_pixel_channels (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, T *result, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride) const; template OIIO_DEPRECATED("Use get_pixels(ROI, ...) instead. [1.6]") bool get_pixels (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, T *result, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride) const { return get_pixel_channels (xbegin, xend, ybegin, yend, zbegin, zend, 0, nchannels(), result, xstride, ystride, zstride); } template OIIO_DEPRECATED("Use get_pixels(ROI, ...) instead. [1.6]") bool get_pixels (int xbegin_, int xend_, int ybegin_, int yend_, int zbegin_, int zend_, std::vector &result) const { result.resize (nchannels() * ((zend_-zbegin_)*(yend_-ybegin_)*(xend_-xbegin_))); return get_pixels (xbegin_, xend_, ybegin_, yend_, zbegin_, zend_, &result[0]); } int orientation () const; void set_orientation (int orient); int oriented_width () const; int oriented_height () const; int oriented_x () const; int oriented_y () const; int oriented_full_width () const; int oriented_full_height () const; int oriented_full_x () const; int oriented_full_y () const; /// Return the beginning (minimum) x coordinate of the defined image. int xbegin () const; /// Return the end (one past maximum) x coordinate of the defined image. int xend () const; /// Return the beginning (minimum) y coordinate of the defined image. int ybegin () const; /// Return the end (one past maximum) y coordinate of the defined image. int yend () const; /// Return the beginning (minimum) z coordinate of the defined image. int zbegin () const; /// Return the end (one past maximum) z coordinate of the defined image. int zend () const; /// Return the minimum x coordinate of the defined image. int xmin () const; /// Return the maximum x coordinate of the defined image. int xmax () const; /// Return the minimum y coordinate of the defined image. int ymin () const; /// Return the maximum y coordinate of the defined image. int ymax () const; /// Return the minimum z coordinate of the defined image. int zmin () const; /// Return the maximum z coordinate of the defined image. int zmax () const; /// Set the "full" (a.k.a. display) window to [xbegin,xend) x /// [ybegin,yend) x [zbegin,zend). void set_full (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend); /// Return pixel data window for this ImageBuf as a ROI. ROI roi () const; /// Return full/display window for this ImageBuf as a ROI. ROI roi_full () const; /// Set full/display window for this ImageBuf to a ROI. /// Does NOT change the channels of the spec, regardless of newroi. void set_roi_full (const ROI &newroi); /// Is the specified roi completely contained in the data window of /// this ImageBuf? bool contains_roi (ROI roi) const; bool pixels_valid (void) const; TypeDesc pixeltype () const; /// A raw pointer to "local" pixel memory, if they are fully in RAM /// and not backed by an ImageCache, or NULL otherwise. You can /// also test it like a bool to find out if pixels are local. void *localpixels (); const void *localpixels () const; /// Are the pixels backed by an ImageCache, rather than the whole /// image being in RAM somewhere? bool cachedpixels () const; ImageCache *imagecache () const; /// Return the address where pixel (x,y,z) is stored in the image buffer. /// Use with extreme caution! Will return NULL if the pixel values /// aren't local. const void *pixeladdr (int x, int y, int z=0) const; /// Return the address where pixel (x,y) is stored in the image buffer. /// Use with extreme caution! Will return NULL if the pixel values /// aren't local. void *pixeladdr (int x, int y) { return pixeladdr (x, y, 0); } /// Return the address where pixel (x,y,z) is stored in the image buffer. /// Use with extreme caution! Will return NULL if the pixel values /// aren't local. void *pixeladdr (int x, int y, int z); /// Return the index of pixel (x,y,z). If check_range is true, return /// -1 for an invalid coordinate that is not within the data window. int pixelindex (int x, int y, int z, bool check_range=false) const; /// Does this ImageBuf store deep data? bool deep () const; /// Retrieve the number of deep data samples corresponding to pixel /// (x,y,z). Return 0 if not a deep image or if the pixel is out of /// range or has no deep samples. int deep_samples (int x, int y, int z=0) const; /// Return a pointer to the raw data of pixel (x,y,z), channel c, sample /// s. Return NULL if the pixel coordinates or channel number are out of /// range, if the pixel/channel has no deep samples, or if the image is /// not deep. const void *deep_pixel_ptr (int x, int y, int z, int c, int s=0) const; /// Return the value (as a float) of sample s of channel c of pixel /// (x,y,z). Return 0.0 if not a deep image or if the pixel /// coordinates or channel number are out of range or if it has no /// deep samples. float deep_value (int x, int y, int z, int c, int s) const; /// Retrieve deep sample value within a pixel, as an untigned int. uint32_t deep_value_uint (int x, int y, int z, int c, int s) const; /// Set the number of deep samples for a particular pixel. void set_deep_samples (int x, int y, int z, int nsamples); /// Set the number of deep samples for a particular pixel. void deep_insert_samples (int x, int y, int z, int samplepos, int nsamples); /// Set the number of deep samples for a particular pixel. void deep_erase_samples (int x, int y, int z, int samplepos, int nsamples); /// Set deep sample value within a pixel, as a float. void set_deep_value (int x, int y, int z, int c, int s, float value); /// Set deep sample value within a pixel, as a uint32. void set_deep_value (int x, int y, int z, int c, int s, uint32_t value); OIIO_DEPRECATED("Use set_deep_value() [1.7]") void set_deep_value_uint (int x, int y, int z, int c, int s, uint32_t value); /// Allocate all the deep samples, called after deepdata()->nsamples /// is set. OIIO_DEPRECATED("No longer necessary to call deep_alloc [1.7]") void deep_alloc (); /// Retrieve the "deep" data. DeepData *deepdata (); const DeepData *deepdata () const; /// Set the current thread-spawning policy: the maximum number of /// threads that may be spawned by ImagBuf internals. A value of 1 /// means all work will be done by the calling thread; 0 means to use /// the global OIIO::attribute("threads") value. void threads (int n) const; /// Retrieve the current thread-spawning policy of this ImageBuf. int threads () const; friend class IteratorBase; class IteratorBase { public: IteratorBase (const ImageBuf &ib, WrapMode wrap) : m_ib(&ib), m_tile(NULL), m_proxydata(NULL) { init_ib (wrap); range_is_image (); } /// Construct valid iteration region from ImageBuf and ROI. IteratorBase (const ImageBuf &ib, const ROI &roi, WrapMode wrap) : m_ib(&ib), m_tile(NULL), m_proxydata(NULL) { init_ib (wrap); if (roi.defined()) { m_rng_xbegin = roi.xbegin; m_rng_xend = roi.xend; m_rng_ybegin = roi.ybegin; m_rng_yend = roi.yend; m_rng_zbegin = roi.zbegin; m_rng_zend = roi.zend; } else { range_is_image (); } } /// Construct from an ImageBuf and designated region -- iterate /// over region, starting with the upper left pixel. IteratorBase (const ImageBuf &ib, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, WrapMode wrap) : m_ib(&ib), m_tile(NULL), m_proxydata(NULL) { init_ib (wrap); m_rng_xbegin = xbegin; m_rng_xend = xend; m_rng_ybegin = ybegin; m_rng_yend = yend; m_rng_zbegin = zbegin; m_rng_zend = zend; } IteratorBase (const IteratorBase &i) : m_ib (i.m_ib), m_rng_xbegin(i.m_rng_xbegin), m_rng_xend(i.m_rng_xend), m_rng_ybegin(i.m_rng_ybegin), m_rng_yend(i.m_rng_yend), m_rng_zbegin(i.m_rng_zbegin), m_rng_zend(i.m_rng_zend), m_tile(NULL), m_proxydata(i.m_proxydata) { init_ib (i.m_wrap); } ~IteratorBase () { if (m_tile) m_ib->imagecache()->release_tile (m_tile); } /// Assign one IteratorBase to another /// const IteratorBase & assign_base (const IteratorBase &i) { if (m_tile) m_ib->imagecache()->release_tile (m_tile); m_tile = NULL; m_proxydata = i.m_proxydata; m_ib = i.m_ib; init_ib (i.m_wrap); m_rng_xbegin = i.m_rng_xbegin; m_rng_xend = i.m_rng_xend; m_rng_ybegin = i.m_rng_ybegin; m_rng_yend = i.m_rng_yend; m_rng_zbegin = i.m_rng_zbegin; m_rng_zend = i.m_rng_zend; return *this; } /// Retrieve the current x location of the iterator. /// int x () const { return m_x; } /// Retrieve the current y location of the iterator. /// int y () const { return m_y; } /// Retrieve the current z location of the iterator. /// int z () const { return m_z; } /// Is the current location within the designated iteration range? bool valid () const { return m_valid; } /// Is the location (x,y[,z]) within the designated iteration /// range? bool valid (int x_, int y_, int z_=0) const { return (x_ >= m_rng_xbegin && x_ < m_rng_xend && y_ >= m_rng_ybegin && y_ < m_rng_yend && z_ >= m_rng_zbegin && z_ < m_rng_zend); } /// Is the location (x,y[,z]) within the region of the ImageBuf /// that contains pixel values (sometimes called the "data window")? bool exists (int x_, int y_, int z_=0) const { return (x_ >= m_img_xbegin && x_ < m_img_xend && y_ >= m_img_ybegin && y_ < m_img_yend && z_ >= m_img_zbegin && z_ < m_img_zend); } /// Does the current location exist within the ImageBuf's /// data window? bool exists () const { return m_exists; } /// Are we finished iterating over the region? // bool done () const { // We're "done" if we are both invalid and in exactly the // spot that we would end up after iterating off of the last // pixel in the range. (The m_valid test is just a quick // early-out for when we're in the correct pixel range.) return (m_valid == false && m_x == m_rng_xbegin && m_y == m_rng_ybegin && m_z == m_rng_zend); } /// Retrieve the number of deep data samples at this pixel. int deep_samples () const { return m_ib->deep_samples (m_x, m_y, m_z); } /// Return the wrap mode WrapMode wrap () const { return m_wrap; } /// Explicitly point the iterator. This results in an invalid /// iterator if outside the previously-designated region. void pos (int x_, int y_, int z_=0) { if (x_ == m_x+1 && x_ < m_rng_xend && y_ == m_y && z_ == m_z && m_valid && m_exists) { // Special case for what is in effect just incrementing x // within the iteration region. m_x = x_; pos_xincr (); // Not necessary? m_exists = (x_ < m_img_xend); DASSERT ((x_ < m_img_xend) == m_exists); return; } bool v = valid(x_,y_,z_); bool e = exists(x_,y_,z_); if (m_localpixels) { if (e) m_proxydata = (char *)m_ib->pixeladdr (x_, y_, z_); else { // pixel not in data window m_x = x_; m_y = y_; m_z = z_; if (m_wrap == WrapBlack) { m_proxydata = (char *)m_ib->blackpixel(); } else { if (m_ib->do_wrap (x_, y_, z_, m_wrap)) m_proxydata = (char *)m_ib->pixeladdr (x_, y_, z_); else m_proxydata = (char *)m_ib->blackpixel(); } m_valid = v; m_exists = e; return; } } else if (! m_deep) m_proxydata = (char *)m_ib->retile (x_, y_, z_, m_tile, m_tilexbegin, m_tileybegin, m_tilezbegin, m_tilexend, e, m_wrap); m_x = x_; m_y = y_; m_z = z_; m_valid = v; m_exists = e; } /// Increment to the next pixel in the region. /// OIIO_FORCEINLINE void operator++ () { if (++m_x < m_rng_xend) { // Special case: we only incremented x, didn't change y // or z, and the previous position was within the data // window. Call a shortcut version of pos. if (m_exists) { pos_xincr (); return; } } else { // Wrap to the next scanline m_x = m_rng_xbegin; if (++m_y >= m_rng_yend) { m_y = m_rng_ybegin; if (++m_z >= m_rng_zend) { m_valid = false; // shortcut -- finished iterating return; } } } pos (m_x, m_y, m_z); } /// Increment to the next pixel in the region. /// void operator++ (int) { ++(*this); } /// Return the iteration range ROI range () const { return ROI (m_rng_xbegin, m_rng_xend, m_rng_ybegin, m_rng_yend, m_rng_zbegin, m_rng_zend, 0, m_ib->nchannels()); } /// Reset the iteration range for this iterator and reposition to /// the beginning of the range, but keep referring to the same /// image. void rerange (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, WrapMode wrap=WrapDefault) { m_x = 1<<31; m_y = 1<<31; m_z = 1<<31; m_wrap = (wrap == WrapDefault ? WrapBlack : wrap); m_rng_xbegin = xbegin; m_rng_xend = xend; m_rng_ybegin = ybegin; m_rng_yend = yend; m_rng_zbegin = zbegin; m_rng_zend = zend; pos (xbegin, ybegin, zbegin); } protected: friend class ImageBuf; friend class ImageBufImpl; const ImageBuf *m_ib; bool m_valid, m_exists; bool m_deep; bool m_localpixels; // Image boundaries int m_img_xbegin, m_img_xend, m_img_ybegin, m_img_yend, m_img_zbegin, m_img_zend; // Iteration range int m_rng_xbegin, m_rng_xend, m_rng_ybegin, m_rng_yend, m_rng_zbegin, m_rng_zend; int m_x, m_y, m_z; ImageCache::Tile *m_tile; int m_tilexbegin, m_tileybegin, m_tilezbegin; int m_tilexend; int m_nchannels; size_t m_pixel_bytes; char *m_proxydata; WrapMode m_wrap; // Helper called by ctrs -- set up some locally cached values // that are copied or derived from the ImageBuf. void init_ib (WrapMode wrap) { const ImageSpec &spec (m_ib->spec()); m_deep = spec.deep; m_localpixels = (m_ib->localpixels() != NULL); m_img_xbegin = spec.x; m_img_xend = spec.x+spec.width; m_img_ybegin = spec.y; m_img_yend = spec.y+spec.height; m_img_zbegin = spec.z; m_img_zend = spec.z+spec.depth; m_nchannels = spec.nchannels; // m_tilewidth = spec.tile_width; m_pixel_bytes = spec.pixel_bytes(); m_x = 1<<31; m_y = 1<<31; m_z = 1<<31; m_wrap = (wrap == WrapDefault ? WrapBlack : wrap); } // Helper called by ctrs -- make the iteration range the full // image data window. void range_is_image () { m_rng_xbegin = m_img_xbegin; m_rng_xend = m_img_xend; m_rng_ybegin = m_img_ybegin; m_rng_yend = m_img_yend; m_rng_zbegin = m_img_zbegin; m_rng_zend = m_img_zend; } // Helper called by pos(), but ONLY for the case where we are // moving from an existing pixel to the next spot in +x. // Note: called *after* m_x was incremented! OIIO_FORCEINLINE void pos_xincr () { DASSERT (m_exists && m_valid); // precondition DASSERT (valid(m_x,m_y,m_z)); // should be true by definition m_proxydata += m_pixel_bytes; if (m_localpixels) { if (OIIO_UNLIKELY(m_x >= m_img_xend)) { // Ran off the end of the row m_exists = false; if (m_wrap == WrapBlack) { m_proxydata = (char *)m_ib->blackpixel(); } else { int x = m_x, y = m_y, z = m_z; if (m_ib->do_wrap (x, y, z, m_wrap)) m_proxydata = (char *)m_ib->pixeladdr (x, y, z); else m_proxydata = (char *)m_ib->blackpixel(); } } } else if (m_deep) { m_proxydata = NULL; } else { // Cached image bool e = m_x < m_img_xend; if (OIIO_UNLIKELY( !(e && m_x < m_tilexend && m_tile))) { // Crossed a tile boundary m_proxydata = (char *)m_ib->retile (m_x, m_y, m_z, m_tile, m_tilexbegin, m_tileybegin, m_tilezbegin, m_tilexend, e, m_wrap); m_exists = e; } } } // Set to the "done" position void pos_done () { m_valid = false; m_x = m_rng_xbegin; m_y = m_rng_ybegin; m_z = m_rng_zend; } // Make sure it's writeable. Use with caution! void make_writeable () { if (! m_localpixels) { const_cast(m_ib)->make_writeable (true); DASSERT (m_ib->storage() != IMAGECACHE); m_tile = NULL; m_proxydata = NULL; init_ib (m_wrap); } } }; /// Templated class for referring to an individual pixel in an /// ImageBuf, iterating over the pixels of an ImageBuf, or iterating /// over the pixels of a specified region of the ImageBuf /// [xbegin..xend) X [ybegin..yend). It is templated on BUFT, the /// type known to be in the internal representation of the ImageBuf, /// and USERT, the type that the user wants to retrieve or set the /// data (defaulting to float). the whole idea is to allow this: /// \code /// ImageBuf img (...); /// ImageBuf::Iterator pixel (img, 0, 512, 0, 512); /// for ( ; ! pixel.done(); ++pixel) { /// for (int c = 0; c < img.nchannels(); ++c) { /// float x = pixel[c]; /// pixel[c] = ...; /// } /// } /// \endcode /// template class Iterator : public IteratorBase { public: /// Construct from just an ImageBuf -- iterate over the whole /// region, starting with the upper left pixel of the region. Iterator (ImageBuf &ib, WrapMode wrap=WrapDefault) : IteratorBase(ib,wrap) { make_writeable (); pos (m_rng_xbegin,m_rng_ybegin,m_rng_zbegin); if (m_rng_xbegin == m_rng_xend || m_rng_ybegin == m_rng_yend || m_rng_zbegin == m_rng_zend) pos_done(); // make empty range look "done" } /// Construct from an ImageBuf and a specific pixel index. /// The iteration range is the full image. Iterator (ImageBuf &ib, int x, int y, int z=0, WrapMode wrap=WrapDefault) : IteratorBase(ib,wrap) { make_writeable (); pos (x, y, z); } /// Construct read-write iteration region from ImageBuf and ROI. Iterator (ImageBuf &ib, const ROI &roi, WrapMode wrap=WrapDefault) : IteratorBase (ib, roi, wrap) { make_writeable (); pos (m_rng_xbegin, m_rng_ybegin, m_rng_zbegin); if (m_rng_xbegin == m_rng_xend || m_rng_ybegin == m_rng_yend || m_rng_zbegin == m_rng_zend) pos_done(); // make empty range look "done" } /// Construct from an ImageBuf and designated region -- iterate /// over region, starting with the upper left pixel. Iterator (ImageBuf &ib, int xbegin, int xend, int ybegin, int yend, int zbegin=0, int zend=1, WrapMode wrap=WrapDefault) : IteratorBase(ib, xbegin, xend, ybegin, yend, zbegin, zend, wrap) { make_writeable (); pos (m_rng_xbegin, m_rng_ybegin, m_rng_zbegin); if (m_rng_xbegin == m_rng_xend || m_rng_ybegin == m_rng_yend || m_rng_zbegin == m_rng_zend) pos_done(); // make empty range look "done" } /// Copy constructor. /// Iterator (Iterator &i) : IteratorBase (i.m_ib, i.m_wrap) { make_writeable (); pos (i.m_x, i.m_y, i.m_z); } ~Iterator () { } /// Assign one Iterator to another /// const Iterator & operator= (const Iterator &i) { assign_base (i); pos (i.m_x, i.m_y, i.m_z); return *this; } /// Dereferencing the iterator gives us a proxy for the pixel, /// which we can index for reading or assignment. DataArrayProxy& operator* () { return *(DataArrayProxy *)(void *)&m_proxydata; } /// Array indexing retrieves the value of the i-th channel of /// the current pixel. USERT operator[] (int i) const { DataArrayProxy proxy((BUFT*)m_proxydata); return proxy[i]; } /// Array referencing retrieve a proxy (which may be "assigned /// to") of i-th channel of the current pixel, so that this /// works: me[i] = val; DataProxy operator[] (int i) { DataArrayProxy proxy((BUFT*)m_proxydata); return proxy[i]; } void * rawptr () const { return m_proxydata; } /// Set the number of deep data samples at this pixel. (Only use /// this if deep_alloc() has not yet been called on the buffer.) void set_deep_samples (int n) { return const_cast(m_ib)->set_deep_samples (m_x, m_y, m_z, n); } /// Retrieve the deep data value of sample s of channel c. USERT deep_value (int c, int s) const { return convert_type(m_ib->deep_value (m_x, m_y, m_z, c, s)); } uint32_t deep_value_uint (int c, int s) const { return m_ib->deep_value_uint (m_x, m_y, m_z, c, s); } /// Set the deep data value of sample s of channel c. (Only use this /// if deep_alloc() has been called.) void set_deep_value (int c, int s, float value) { return const_cast(m_ib)->set_deep_value (m_x, m_y, m_z, c, s, value); } void set_deep_value (int c, int s, uint32_t value) { return const_cast(m_ib)->set_deep_value (m_x, m_y, m_z, c, s, value); } }; /// Just like an ImageBuf::Iterator, except that it refers to a /// const ImageBuf. template class ConstIterator : public IteratorBase { public: /// Construct from just an ImageBuf -- iterate over the whole /// region, starting with the upper left pixel of the region. ConstIterator (const ImageBuf &ib, WrapMode wrap=WrapDefault) : IteratorBase(ib,wrap) { pos (m_rng_xbegin,m_rng_ybegin,m_rng_zbegin); if (m_rng_xbegin == m_rng_xend || m_rng_ybegin == m_rng_yend || m_rng_zbegin == m_rng_zend) pos_done(); // make empty range look "done" } /// Construct from an ImageBuf and a specific pixel index. /// The iteration range is the full image. ConstIterator (const ImageBuf &ib, int x_, int y_, int z_=0, WrapMode wrap=WrapDefault) : IteratorBase(ib,wrap) { pos (x_, y_, z_); } /// Construct read-only iteration region from ImageBuf and ROI. ConstIterator (const ImageBuf &ib, const ROI &roi, WrapMode wrap=WrapDefault) : IteratorBase (ib, roi, wrap) { pos (m_rng_xbegin, m_rng_ybegin, m_rng_zbegin); if (m_rng_xbegin == m_rng_xend || m_rng_ybegin == m_rng_yend || m_rng_zbegin == m_rng_zend) pos_done(); // make empty range look "done" } /// Construct from an ImageBuf and designated region -- iterate /// over region, starting with the upper left pixel. ConstIterator (const ImageBuf &ib, int xbegin, int xend, int ybegin, int yend, int zbegin=0, int zend=1, WrapMode wrap=WrapDefault) : IteratorBase(ib, xbegin, xend, ybegin, yend, zbegin, zend, wrap) { pos (m_rng_xbegin, m_rng_ybegin, m_rng_zbegin); if (m_rng_xbegin == m_rng_xend || m_rng_ybegin == m_rng_yend || m_rng_zbegin == m_rng_zend) pos_done(); // make empty range look "done" } /// Copy constructor. /// ConstIterator (const ConstIterator &i) : IteratorBase (i) { pos (i.m_x, i.m_y, i.m_z); } ~ConstIterator () { } /// Assign one ConstIterator to another /// const ConstIterator & operator= (const ConstIterator &i) { assign_base (i); pos (i.m_x, i.m_y, i.m_z); return *this; } /// Dereferencing the iterator gives us a proxy for the pixel, /// which we can index for reading or assignment. ConstDataArrayProxy& operator* () const { return *(ConstDataArrayProxy *)&m_proxydata; } /// Array indexing retrieves the value of the i-th channel of /// the current pixel. USERT operator[] (int i) const { ConstDataArrayProxy proxy ((BUFT*)m_proxydata); return proxy[i]; } const void * rawptr () const { return m_proxydata; } /// Retrieve the deep data value of sample s of channel c. USERT deep_value (int c, int s) const { return convert_type(m_ib->deep_value (m_x, m_y, m_z, c, s)); } uint32_t deep_value_uint (int c, int s) const { return m_ib->deep_value_uint (m_x, m_y, m_z, c, s); } }; protected: ImageBufImpl *m_impl; //< PIMPL idiom ImageBufImpl * impl () { return m_impl; } const ImageBufImpl * impl () const { return m_impl; } // Reset the ImageCache::Tile * to reserve and point to the correct // tile for the given pixel, and return the ptr to the actual pixel // within the tile. const void * retile (int x, int y, int z, ImageCache::Tile* &tile, int &tilexbegin, int &tileybegin, int &tilezbegin, int &tilexend, bool exists, WrapMode wrap=WrapDefault) const; const void *blackpixel () const; // Given x,y,z known to be outside the pixel data range, and a wrap // mode, alter xyz to implement the wrap. Return true if the resulting // x,y,z is within the valid pixel data window, false if it still is // not. bool do_wrap (int &x, int &y, int &z, WrapMode wrap) const; /// Private and unimplemented. const ImageBuf& operator= (const ImageBuf &src); /// Add to the error message list for this IB. void append_error (const std::string& message) const; }; OIIO_NAMESPACE_END #endif // OPENIMAGEIO_IMAGEBUF_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/argparse.h0000644000175000017500000001755213151711064022622 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Based on BSD-licensed software Copyright 2004 NVIDIA Corp. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// \file /// \brief Simple parsing of program command-line arguments. #ifndef OPENIMAGEIO_ARGPARSE_H #define OPENIMAGEIO_ARGPARSE_H #if defined(_MSC_VER) // Ignore warnings about DLL exported classes with member variables that are template classes. // This happens with the std::string m_errmessage member of ArgParse below. # pragma warning (disable : 4251) #endif #include #include "export.h" #include "oiioversion.h" #include "tinyformat.h" OIIO_NAMESPACE_BEGIN class ArgOption; // Forward declaration ///////////////////////////////////////////////////////////////////////////// /// /// \class ArgParse /// /// Argument Parsing /// /// The parse function takes a list of options and variables or functions /// for storing option values and return <0 on failure: /// /// \code /// static int parse_files (int argc, const char *argv[]) /// { /// for (int i = 0; i < argc; i++) /// filenames.push_back (argv[i]); /// return 0; /// } /// /// static int blah_callback (int argc, const char *argv[]) /// { /// std::cout << "blah argument was " << argv[1] << "\n"; /// return 0; /// } /// /// ... /// /// ArgParse ap; /// /// ap.options ("Usage: myapp [options] filename...", /// "%*", parse_objects, "", /// "-camera %f %f %f", &camera[0], &camera[1], &camera[2], /// "set the camera position", /// "-lookat %f %f %f", &lx, &ly, &lz, /// "set the position of interest", /// "-oversampling %d", &oversampling, "oversamping rate", /// "-passes %d", &passes, "number of passes", /// "-lens %f %f %f", &aperture, &focalDistance, &focalLength, /// "set aperture, focal distance, focal length", /// "-format %d %d %f", &width, &height, &aspect, /// "set width, height, aspect ratio", /// "-v", &verbose, "verbose output", /// "-q %!", &verbose, "quiet mode", /// "--blah %@ %s", blahcallback, "Make the callback", /// NULL); /// /// if (ap.parse (argc, argv) < 0) { /// std::cerr << ap.geterror() << std::endl; /// ap.usage (); /// return EXIT_FAILURE; /// } /// \endcode /// /// The available argument types are: /// - no \% argument - bool flag /// - \%! - a bool flag, but set it to false if the option is set /// - \%d - 32bit integer /// - \%f - 32bit float /// - \%F - 64bit float (double) /// - \%s - std::string /// - \%L - std::vector (takes 1 arg, appends to list) /// - \%@ - a function pointer for a callback function will be invoked /// immediately. The prototype for the callback is /// int callback (int argc, char *argv[]) /// - \%* - catch all non-options and pass individually as an (argc,argv) /// sublist to a callback, each immediately after it's found /// /// There are several special format tokens: /// - "" - not an option at all, just a description to print /// in the usage output. /// /// Notes: /// - If an option doesn't have any arguments, a bool flag argument is /// assumed. /// - No argument destinations are initialized. /// - The empty string, "", is used as a global sublist (ie. "%*"). /// - Sublist functions are all of the form "int func(int argc, char **argv)". /// - If a sublist function returns -1, parse() will terminate early. /// - It is perfectly legal for the user to append ':' and more characters /// to the end of an option name, it will match only the portion before /// the semicolon (but a callback can detect the full string, this is /// useful for making arguments: myprog --flag:myopt=1 foobar /// ///////////////////////////////////////////////////////////////////////////// class OIIO_API ArgParse { public: ArgParse (int argc=0, const char **argv=NULL); ~ArgParse (); /// Declare the command line options. After the introductory /// message, parameters are a set of format strings and variable /// pointers. Each string contains an option name and a scanf-like /// format string to enumerate the arguments of that option /// (eg. "-option %d %f %s"). The format string is followed by a /// list of pointers to the argument variables, just like scanf. A /// NULL terminates the list. Multiple calls to options() will /// append additional options. int options (const char *intro, ...); /// With the options already set up, parse the command line. /// Return 0 if ok, -1 if it's a malformed command line. int parse (int argc, const char **argv); /// Return any error messages generated during the course of parse() /// (and clear any error flags). If no error has occurred since the /// last time geterror() was called, it will return an empty string. std::string geterror () const; /// Print the usage message to stdout. The usage message is /// generated and formatted automatically based on the command and /// description arguments passed to parse(). void usage () const; /// Print a brief usage message to stdout. The usage message is /// generated and formatted automatically based on the command and /// description arguments passed to parse(). void briefusage () const; /// Return the entire command-line as one string. /// std::string command_line () const; private: int m_argc; // a copy of the command line argc const char **m_argv; // a copy of the command line argv mutable std::string m_errmessage; // error message ArgOption *m_global; // option for extra cmd line arguments std::string m_intro; std::vector m_option; ArgOption *find_option(const char *name); // void error (const char *format, ...) TINYFORMAT_WRAP_FORMAT (void, error, /**/, std::ostringstream msg;, msg, m_errmessage = msg.str();) int found (const char *option); // number of times option was parsed }; // Define symbols that let client applications determine if newly added // features are supported. #define OIIO_ARGPARSE_SUPPORTS_BRIEFUSAGE 1 OIIO_NAMESPACE_END #endif // OPENIMAGEIO_ARGPARSE_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/simd.h0000644000175000017500000054301513151711064021750 0ustar mfvmfv/* Copyright (c) 2014 Larry Gritz et al. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sony Pictures Imageworks nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /// @file simd.h /// /// @brief Classes for SIMD processing. /// /// Nice references for all the Intel intrinsics (SSE*, AVX*, etc.): /// https://software.intel.com/sites/landingpage/IntrinsicsGuide/ /// /// It helped me a lot to peruse the source of these packages: /// Syrah: https://github.com/boulos/syrah /// Embree: https://github.com/embree /// Vectorial: https://github.com/scoopr/vectorial /// /// To find out which CPU features you have: /// Linux: cat /proc/cpuinfo /// OSX: sysctl machdep.cpu.features /// /// Additional web resources: /// http://www.codersnotes.com/notes/maths-lib-2016/ #pragma once #ifndef OIIO_SIMD_H #define OIIO_SIMD_H 1 #include #include #include #include #include #include #include ////////////////////////////////////////////////////////////////////////// // Sort out which SIMD capabilities we have and set definitions // appropriately. This is mostly for internal (within this file) use, // but client applications using this header may find a few of the macros // we define to be useful: // // OIIO_SIMD : Will be 0 if no hardware SIMD support is specified. If SIMD // hardware is available, this will hold the width in number of // float SIMD "lanes" of widest SIMD registers available. For // example, OIIO_SIMD will be 4 if float4/int4/bool4 are // hardware accelerated, 8 if float8/int8/bool8 are accelerated, // etc. Using SIMD classes wider than this should work (will be // emulated with narrower SIMD or scalar operations), but is not // expected to have high performance. // OIIO_SIMD_SSE : if Intel SSE is supported, this will be nonzero, // specifically 2 for SSE2, 3 for SSSE3, 4 for SSE4.1 or // higher (including AVX). // OIIO_SIMD_AVX : If Intel AVX is supported, this will be nonzero, and // specifically 1 for AVX (1.0), 2 for AVX2, 512 for AVX512f. // OIIO_SIMD_NEON : If ARM NEON is supported, this will be nonzero. // OIIO_SIMD_MAX_SIZE : holds the width in bytes of the widest SIMD // available (generally will be OIIO_SIMD*4). // OIIO_SIMD4_ALIGN : macro for best alignment of 4-wide SIMD values in mem. // OIIO_SIMD8_ALIGN : macro for best alignment of 8-wide SIMD values in mem. // OIIO_SIMD_HAS_MATRIX4 : nonzero if matrix44 is defined // OIIO_SIMD_HAS_FLOAT8 : nonzero if float8, int8, bool8 are defined #if (defined(__SSE2__) || (_MSC_VER >= 1300 && !_M_CEE_PURE)) && !defined(OIIO_NO_SSE) # include # if (defined(__i386__) || defined(__x86_64__)) && (OIIO_GNUC_VERSION > 40400 || __clang__) # include # endif # if (defined(__SSE4_1__) || defined(__SSE4_2__)) # define OIIO_SIMD_SSE 4 /* N.B. We consider both SSE4.1 and SSE4.2 to be "4". There are a few * instructions specific to 4.2, but they are all related to string * comparisons and CRCs, which don't currently seem relevant to OIIO, * so for simplicity, we sweep this difference under the rug. */ # elif defined(__SSSE3__) # define OIIO_SIMD_SSE 3 /* N.B. We only use OIIO_SIMD_SSE = 3 when fully at SSSE3. In theory, * there are a few older architectures that are SSE3 but not SSSE3, * and this simplification means that these particular old platforms * will only get SSE2 goodness out of our code. So be it. Anybody who * cares about performance is probably using a 64 bit machine that's * SSE 4.x or AVX by now. */ # else # define OIIO_SIMD_SSE 2 # endif # define OIIO_SIMD 4 # define OIIO_SIMD_MAX_SIZE_BYTES 16 # define OIIO_SIMD_ALIGN OIIO_ALIGN(16) # define OIIO_SIMD4_ALIGN OIIO_ALIGN(16) # define OIIO_SSE_ALIGN OIIO_ALIGN(16) #endif #if defined(__AVX__) && !defined(OIIO_NO_AVX) // N.B. Any machine with AVX will also have SSE # if defined(__AVX2__) && !defined(OIIO_NO_AVX2) # define OIIO_SIMD_AVX 2 # else # define OIIO_SIMD_AVX 1 # endif # undef OIIO_SIMD # define OIIO_SIMD 8 # undef OIIO_SIMD_MAX_SIZE_BYTES # define OIIO_SIMD_MAX_SIZE_BYTES 32 # define OIIO_SIMD8_ALIGN OIIO_ALIGN(32) # define OIIO_AVX_ALIGN OIIO_ALIGN(32) # if defined(__AVX512__) && !defined(OIIO_NO_AVX512) # define OIIO_SIMD_AVX 512 # undef OIIO_SIMD_MAX_SIZE_BYTES # define OIIO_SIMD_MAX_SIZE_BYTES 64 # define OIIO_SIMD16_ALIGN OIIO_ALIGN(64) # define OIIO_AVX512_ALIGN OIIO_ALIGN(64) # endif #endif #if defined(__FMA__) # define OIIO_FMA_ENABLED 1 #endif // FIXME Future: support ARM Neon // Uncomment this when somebody with Neon can verify it works #if 0 && defined(__ARM_NEON__) && !defined(OIIO_NO_NEON) # include # define OIIO_SIMD 1 # define OIIO_SIMD_NEON 1 # define OIIO_SIMD_MAX_SIZE_BYTES 16 # define OIIO_SIMD_ALIGN OIIO_ALIGN(16) # define OIIO_SIMD4_ALIGN OIIO_ALIGN(16) # define OIIO_SSE_ALIGN OIIO_ALIGN(16) #endif #ifndef OIIO_SIMD // No SIMD available # define OIIO_SIMD 0 # define OIIO_SIMD_ALIGN # define OIIO_SIMD4_ALIGN # define OIIO_SIMD8_ALIGN # define OIIO_SIMD_MAX_SIZE_BYTES 16 #endif // General features that client apps may want to test for, for conditional // compilation. Will add to this over time as needed. Note that just // because a feature is present doesn't mean it's fast -- HAS_FLOAT8 means // the float8 class (and friends) are in this version of simd.h, but that's // different from OIIO_SIMD >= 8, which means it's supported in hardware. #define OIIO_SIMD_HAS_MATRIX4 1 /* matrix44 defined */ #define OIIO_SIMD_HAS_FLOAT8 1 /* float8, int8, bool8 defined */ OIIO_NAMESPACE_BEGIN namespace simd { ////////////////////////////////////////////////////////////////////////// // Forward declarations of our main SIMD classes class bool4; class int4; class float4; class float3; class matrix44; typedef bool4 mask4; // old name class bool8; class int8; class float8; ////////////////////////////////////////////////////////////////////////// // Template magic to determine the raw SIMD types involved, and other // things helpful for metaprogramming. template struct simd_raw_t { struct type { T val[N]; }; }; template struct simd_bool_t { struct type { int val[N]; }; }; #if OIIO_SIMD_SSE template<> struct simd_raw_t { typedef __m128i type; }; template<> struct simd_raw_t { typedef __m128 type; }; template<> struct simd_bool_t<4> { typedef __m128 type; }; #endif #if OIIO_SIMD_AVX template<> struct simd_raw_t { typedef __m256i type; }; template<> struct simd_raw_t { typedef __m256 type; }; template<> struct simd_bool_t<8> { typedef __m256 type; }; #endif #if OIIO_SIMD_AVX >= 512 template<> struct simd_raw_t { typedef __m512i type; }; template<> struct simd_raw_t { typedef __m512 type; }; template<> struct simd_bool_t<16> { typedef __m512 type; }; #endif #if OIIO_SIMD_NEON template<> struct simd_raw_t { typedef int32x4 type; }; template<> struct simd_raw_t { typedef float32x4_t type; }; template<> struct simd_bool_t<4> { typedef int32x4 type; }; #endif /// Template to retrieve the vector type from the scalar. For example, /// simd::VecType will be float4. template struct VecType {}; template<> struct VecType { typedef int type; }; template<> struct VecType { typedef float type; }; template<> struct VecType { typedef int4 type; }; template<> struct VecType { typedef float3 type; }; template<> struct VecType { typedef bool4 type; }; template<> struct VecType { typedef int8 type; }; template<> struct VecType { typedef float8 type; }; template<> struct VecType { typedef bool8 type; }; /// Template to retrieve the SIMD size of a SIMD type. Rigged to be 1 for /// anything but our SIMD types. template struct SimdSize { static const int size = 1; }; template<> struct SimdSize { static const int size = 4; }; template<> struct SimdSize { static const int size = 4; }; template<> struct SimdSize { static const int size = 4; }; template<> struct SimdSize { static const int size = 4; }; template<> struct SimdSize { static const int size = 8; }; template<> struct SimdSize { static const int size = 8; }; template<> struct SimdSize { static const int size = 8; }; /// Template to retrieve the number of elements size of a SIMD type. Rigged /// to be 1 for anything but our SIMD types. template struct SimdElements { static const int size = SimdSize::size; }; template<> struct SimdElements { static const int size = 3; }; /// Template giving a printable name for each type template struct SimdTypeName { static const char *name() { return "unknown"; } }; template<> struct SimdTypeName { static const char *name() { return "float4"; } }; template<> struct SimdTypeName { static const char *name() { return "int4"; } }; template<> struct SimdTypeName { static const char *name() { return "bool4"; } }; template<> struct SimdTypeName { static const char *name() { return "float8"; } }; template<> struct SimdTypeName { static const char *name() { return "int8"; } }; template<> struct SimdTypeName { static const char *name() { return "bool8"; } }; ////////////////////////////////////////////////////////////////////////// // Macros helpful for making static constants in code. # define OIIO_SIMD_FLOAT4_CONST(name,val) \ static const OIIO_SIMD4_ALIGN float name[4] = { (val), (val), (val), (val) } # define OIIO_SIMD_FLOAT4_CONST4(name,v0,v1,v2,v3) \ static const OIIO_SIMD4_ALIGN float name[4] = { (v0), (v1), (v2), (v3) } # define OIIO_SIMD_INT4_CONST(name,val) \ static const OIIO_SIMD4_ALIGN int name[4] = { (val), (val), (val), (val) } # define OIIO_SIMD_INT4_CONST4(name,v0,v1,v2,v3) \ static const OIIO_SIMD4_ALIGN int name[4] = { (v0), (v1), (v2), (v3) } # define OIIO_SIMD_UINT4_CONST(name,val) \ static const OIIO_SIMD4_ALIGN uint32_t name[4] = { (val), (val), (val), (val) } # define OIIO_SIMD_UINT4_CONST4(name,v0,v1,v2,v3) \ static const OIIO_SIMD4_ALIGN uint32_t name[4] = { (v0), (v1), (v2), (v3) } # define OIIO_SIMD_FLOAT8_CONST(name,val) \ static const OIIO_SIMD8_ALIGN float name[8] = { (val), (val), (val), (val), \ (val), (val), (val), (val) } # define OIIO_SIMD_FLOAT8_CONST8(name,v0,v1,v2,v3,v4,v5,v6,v7) \ static const OIIO_SIMD8_ALIGN float name[8] = { (v0), (v1), (v2), (v3), \ (v4), (v5), (v6), (v7) } # define OIIO_SIMD_INT8_CONST(name,val) \ static const OIIO_SIMD8_ALIGN int name[8] = { (val), (val), (val), (val), \ (val), (val), (val), (val) } # define OIIO_SIMD_INT8_CONST8(name,v0,v1,v2,v3,v4,v5,v6,v7) \ static const OIIO_SIMD8_ALIGN int name[8] = { (v0), (v1), (v2), (v3), \ (v4), (v5), (v6), (v7) } # define OIIO_SIMD_UINT8_CONST(name,val) \ static const OIIO_SIMD8_ALIGN uint32_t name[8] = { (val), (val), (val), (val), \ (val), (val), (val), (val) } # define OIIO_SIMD_UINT8_CONST8(name,v0,v1,v2,v3,v4,v5,v6,v7) \ static const OIIO_SIMD8_ALIGN uint32_t name[8] = { (v0), (v1), (v2), (v3), \ (v4), (v5), (v6), (v7) } ////////////////////////////////////////////////////////////////////////// // Some macros just for use in this file (#undef-ed at the end) making // it more succinct to express per-element operations. #define SIMD_DO(x) for (int i = 0; i < elements; ++i) x #define SIMD_CONSTRUCT(x) for (int i = 0; i < elements; ++i) m_val[i] = (x) #define SIMD_CONSTRUCT_PAD(x) for (int i = 0; i < elements; ++i) m_val[i] = (x); \ for (int i = elements; i < paddedelements; ++i) m_val[i] = 0 #define SIMD_RETURN(T,x) T r; for (int i = 0; i < r.elements; ++i) r[i] = (x); return r #define SIMD_RETURN_REDUCE(T,init,op) T r = init; for (int i = 0; i < v.elements; ++i) op; return r ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // The public declarations of the main SIMD classes follow: boolN, intN, // floatN, matrix44. // // These class declarations are intended to be brief and self-documenting, // and give all the information that users or client applications need to // know to use these classes. // // No implementations are given inline except for the briefest, completely // generic methods that don't have any architecture-specific overloads. // After the class defintions, there will be an immense pile of full // implementation definitions, which casual users are not expected to // understand. ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// /// bool4: An 4-vector whose elements act mostly like bools, accelerated by /// SIMD instructions when available. This is what is naturally produced by /// SIMD comparison operators on the float4 and int4 types. class bool4 { public: static const char* type_name() { return "bool4"; } typedef bool value_t; ///< Underlying equivalent scalar value type enum { elements = 4 }; ///< Number of scalar elements enum { paddedelements = 4 }; ///< Number of scalar elements for full pad enum { bits = elements*32 }; ///< Total number of bits typedef simd_bool_t<4>::type simd_t; ///< the native SIMD type used /// Default constructor (contents undefined) bool4 () { } /// Construct from a single value (store it in all slots) bool4 (bool a) { load(a); } explicit bool4 (const bool *a); /// Construct from 4 values bool4 (bool a, bool b, bool c, bool d) { load (a, b, c, d); } /// Copy construct from another bool4 bool4 (const bool4 &other) { m_simd = other.m_simd; } /// Construct from a SIMD int (is each element nonzero?) bool4 (const int4 &i); /// Construct from the underlying SIMD type bool4 (const simd_t& m) : m_simd(m) { } /// Return the raw SIMD type operator simd_t () const { return m_simd; } simd_t simd () const { return m_simd; } /// Set all components to false void clear (); /// Return a bool4 the is 'false' for all values static const bool4 False (); /// Return a bool4 the is 'true' for all values static const bool4 True (); /// Assign one value to all components const bool4 & operator= (bool a) { load(a); return *this; } /// Assignment of another bool4 const bool4 & operator= (const bool4 & other); /// Component access (get) int operator[] (int i) const; /// Component access (set). void setcomp (int i, bool value); /// Component access (set). /// NOTE: avoid this unsafe construct. It will go away some day. int& operator[] (int i); /// Helper: load a single value into all components. void load (bool a); /// Helper: load separate values into each component. void load (bool a, bool b, bool c, bool d); /// Helper: store the values into memory as bools. void store (bool *values) const; /// Store the first n values into memory. void store (bool *values, int n) const; /// Logical/bitwise operators, component-by-component friend bool4 operator! (const bool4& a); friend bool4 operator& (const bool4& a, const bool4& b); friend bool4 operator| (const bool4& a, const bool4& b); friend bool4 operator^ (const bool4& a, const bool4& b); friend bool4 operator~ (const bool4& a); friend const bool4& operator&= (bool4& a, const bool4& b); friend const bool4& operator|= (bool4& a, const bool4& b); friend const bool4& operator^= (bool4& a, const bool4& b); /// Comparison operators, component by component friend bool4 operator== (const bool4& a, const bool4& b); friend bool4 operator!= (const bool4& a, const bool4& b); /// Stream output friend std::ostream& operator<< (std::ostream& cout, const bool4 & a); private: // The actual data representation union { simd_t m_simd; int m_val[paddedelements]; }; }; /// Helper: shuffle/swizzle with constant (templated) indices. /// Example: shuffle<1,1,2,2>(bool4(a,b,c,d)) returns (b,b,c,c) template bool4 shuffle (const bool4& a); /// shuffle(a) is the same as shuffle(a) template bool4 shuffle (const bool4& a); /// Helper: as rapid as possible extraction of one component, when the /// index is fixed. template bool extract (const bool4& a); /// Helper: substitute val for a[i] template bool4 insert (const bool4& a, bool val); /// Logical reduction across all components. bool reduce_and (const bool4& v); bool reduce_or (const bool4& v); // Are all/any/no components true? bool all (const bool4& v); bool any (const bool4& v); bool none (const bool4& v); /// bool8: An 8-vector whose elements act mostly like bools, accelerated by /// SIMD instructions when available. This is what is naturally produced by /// SIMD comparison operators on the float8 and int8 types. class bool8 { public: static const char* type_name() { return "bool8"; } typedef bool value_t; ///< Underlying equivalent scalar value type enum { elements = 8 }; ///< Number of scalar elements enum { paddedelements = 8 }; ///< Number of scalar elements for full pad enum { bits = elements*32 }; ///< Total number of bits typedef simd_bool_t<8>::type simd_t; ///< the native SIMD type used /// Default constructor (contents undefined) bool8 () { } /// Construct from a single value (store it in all slots) bool8 (bool a) { load (a); } explicit bool8 (const bool *values); /// Construct from 8 values bool8 (bool a, bool b, bool c, bool d, bool e, bool f, bool g, bool h); /// Copy construct from another bool8 bool8 (const bool8 &other) { m_simd = other.m_simd; } /// Construct from a SIMD int (is each element nonzero?) bool8 (const int8 &i); /// Construct from two bool4's bool8 (const bool4 &lo, const bool4 &hi); /// Construct from the underlying SIMD type bool8 (const simd_t& m) : m_simd(m) { } /// Return the raw SIMD type operator simd_t () const { return m_simd; } simd_t simd () const { return m_simd; } /// Set all components to false void clear (); /// Return a bool8 the is 'false' for all values static const bool8 False (); /// Return a bool8 the is 'true' for all values static const bool8 True (); /// Assign one value to all components const bool8 & operator= (bool a); /// Assignment of another bool8 const bool8 & operator= (const bool8 & other); /// Component access (get) int operator[] (int i) const; /// Component access (set). void setcomp (int i, bool value); /// Component access (set). /// NOTE: avoid this unsafe construct. It will go away some day. int& operator[] (int i); /// Extract the lower percision bool4 bool4 lo () const; /// Extract the higher percision bool4 bool4 hi () const; /// Helper: load a single value into all components. void load (bool a); /// Helper: load separate values into each component. void load (bool a, bool b, bool c, bool d, bool e, bool f, bool g, bool h); /// Helper: store the values into memory as bools. void store (bool *values) const; /// Store the first n values into memory. void store (bool *values, int n) const; /// Logical/bitwise operators, component-by-component friend bool8 operator! (const bool8& a); friend bool8 operator& (const bool8& a, const bool8& b); friend bool8 operator| (const bool8& a, const bool8& b); friend bool8 operator^ (const bool8& a, const bool8& b); friend bool8 operator~ (const bool8& a); friend const bool8& operator&= (bool8& a, const bool8& b); friend const bool8& operator|= (bool8& a, const bool8& b); friend const bool8& operator^= (bool8& a, const bool8& b); /// Comparison operators, component by component friend bool8 operator== (const bool8& a, const bool8& b); friend bool8 operator!= (const bool8& a, const bool8& b); /// Stream output friend std::ostream& operator<< (std::ostream& cout, const bool8 & a); private: // The actual data representation union { simd_t m_simd; int m_val[paddedelements]; simd_bool_t<4>::type m_4[2]; }; }; /// Helper: shuffle/swizzle with constant (templated) indices. /// Example: shuffle<1,1,2,2>(bool4(a,b,c,d)) returns (b,b,c,c) template bool8 shuffle (const bool8& a); /// shuffle(a) is the same as shuffle(a) template bool8 shuffle (const bool8& a); /// Helper: as rapid as possible extraction of one component, when the /// index is fixed. template bool extract (const bool8& a); /// Helper: substitute val for a[i] template bool8 insert (const bool8& a, bool val); /// Logical reduction across all components. bool reduce_and (const bool8& v); bool reduce_or (const bool8& v); // Are all/any/no components true? bool all (const bool8& v); bool any (const bool8& v); bool none (const bool8& v); /// Integer 4-vector, accelerated by SIMD instructions when available. class int4 { public: static const char* type_name() { return "int4"; } typedef int value_t; ///< Underlying equivalent scalar value type enum { elements = 4 }; ///< Number of scalar elements enum { paddedelements =4 }; ///< Number of scalar elements for full pad enum { bits = 128 }; ///< Total number of bits typedef simd_raw_t::type simd_t; ///< the native SIMD type used typedef bool4 bool_t; ///< bool type of the same length typedef float4 float_t; ///< float type of the same length /// Default constructor (contents undefined) int4 () { } /// Construct from a single value (store it in all slots) int4 (int a); /// Construct from 2 values -- (a,a,b,b) int4 (int a, int b); /// Construct from 4 values int4 (int a, int b, int c, int d); /// Construct from a pointer to values int4 (const int *vals); /// Construct from a pointer to unsigned short values explicit int4 (const unsigned short *vals); /// Construct from a pointer to signed short values explicit int4 (const short *vals); /// Construct from a pointer to unsigned char values (0 - 255) explicit int4 (const unsigned char *vals); /// Construct from a pointer to signed char values (-128 - 127) explicit int4 (const char *vals); /// Copy construct from another int4 int4 (const int4 & other) { m_simd = other.m_simd; } /// Convert a float_t to an int4. Equivalent to i = (int)f; explicit int4 (const float_t& f); // implementation below /// Construct from the underlying SIMD type int4 (const simd_t& m) : m_simd(m) { } /// Return the raw SIMD type operator simd_t () const { return m_simd; } simd_t simd () const { return m_simd; } /// Sset all components to 0 void clear () ; /// Return an int4 with all components set to 0 static const int4 Zero (); /// Return an int4 with all components set to 1 static const int4 One (); /// Return an int4 with all components set to -1 (aka 0xffffffff) static const int4 NegOne (); /// Return an int4 with incremented components (e.g., 0,1,2,3). /// Optional argument can give a non-zero starting point. static const int4 Iota (int start=0, int step=1); /// Assign one value to all components. const int4 & operator= (int a); /// Assignment from another int4 const int4 & operator= (const int4& other) ; /// Component access (set) int& operator[] (int i) ; /// Component access (get) int operator[] (int i) const; value_t x () const; value_t y () const; value_t z () const; value_t w () const; void set_x (value_t val); void set_y (value_t val); void set_z (value_t val); void set_w (value_t val); /// Helper: load a single int into all components void load (int a); /// Helper: load separate values into each component. void load (int a, int b, int c, int d); /// Load from an array of 4 values void load (const int *values); void load (const int *values, int n) ; /// Load from an array of 4 unsigned short values, convert to int4 void load (const unsigned short *values) ; /// Load from an array of 4 unsigned short values, convert to int4 void load (const short *values); /// Load from an array of 4 unsigned char values, convert to int4 void load (const unsigned char *values); /// Load from an array of 4 unsigned char values, convert to int4 void load (const char *values); /// Store the values into memory void store (int *values) const; /// Store the first n values into memory void store (int *values, int n) const; /// Store the least significant 16 bits of each element into adjacent /// unsigned shorts. void store (unsigned short *values) const; /// Store the least significant 8 bits of each element into adjacent /// unsigned chars. void store (unsigned char *values) const; // Arithmetic operators (component-by-component) friend int4 operator+ (const int4& a, const int4& b); friend int4 operator- (const int4& a); friend int4 operator- (const int4& a, const int4& b); friend int4 operator* (const int4& a, const int4& b); friend int4 operator/ (const int4& a, const int4& b); friend int4 operator% (const int4& a, const int4& b); friend const int4 & operator+= (int4& a, const int4& b); friend const int4 & operator-= (int4& a, const int4& b); friend const int4 & operator*= (int4& a, const int4& b); friend const int4 & operator/= (int4& a, const int4& b); friend const int4 & operator%= (int4& a, const int4& b); // Bitwise operators (component-by-component) friend int4 operator& (const int4& a, const int4& b); friend int4 operator| (const int4& a, const int4& b); friend int4 operator^ (const int4& a, const int4& b); friend const int4& operator&= (int4& a, const int4& b); friend const int4& operator|= (int4& a, const int4& b); friend const int4& operator^= (int4& a, const int4& b); friend int4 operator~ (const int4& a); friend int4 operator<< (const int4& a, unsigned int bits); friend int4 operator>> (const int4& a, unsigned int bits); friend const int4& operator<<= (int4& a, unsigned int bits); friend const int4& operator>>= (int4& a, unsigned int bits); // Comparison operators (component-by-component) friend bool4 operator== (const int4& a, const int4& b); friend bool4 operator!= (const int4& a, const int4& b); friend bool4 operator< (const int4& a, const int4& b); friend bool4 operator> (const int4& a, const int4& b); friend bool4 operator>= (const int4& a, const int4& b); friend bool4 operator<= (const int4& a, const int4& b); /// Stream output friend std::ostream& operator<< (std::ostream& cout, const int4 & a); private: // The actual data representation union { simd_t m_simd; value_t m_val[elements]; }; }; // Shift right logical -- unsigned shift. This differs from operator>> // in how it handles the sign bit. (1<<31) >> 1 == (1<<31), but // srl((1<<31),1) == 1<<30. int4 srl (const int4& val, const unsigned int bits); /// Helper: shuffle/swizzle with constant (templated) indices. /// Example: shuffle<1,1,2,2>(bool4(a,b,c,d)) returns (b,b,c,c) template int4 shuffle (const int4& a); /// shuffle(a) is the same as shuffle(a) template int4 shuffle (const int4& a); /// Helper: as rapid as possible extraction of one component, when the /// index is fixed. template int extract (const int4& v); /// The sum of all components, returned in all components. int4 vreduce_add (const int4& v); // Reduction across all components int reduce_add (const int4& v); int reduce_and (const int4& v); int reduce_or (const int4& v); /// Use a bool mask to select between components of a (if mask[i] is false) /// and b (if mask[i] is true), i.e., mask[i] ? b[i] : a[i]. int4 blend (const int4& a, const int4& b, const bool4& mask); /// Use a bool mask to select between `a` (if mask[i] is true) or 0 if /// mask[i] is false), i.e., mask[i] ? a[i] : 0. Equivalent to /// blend(0,a,mask). int4 blend0 (const int4& a, const bool4& mask); /// Use a bool mask to select between components of a (if mask[i] is false) /// or 0 (if mask[i] is true), i.e., mask[i] ? 0 : a[i]. Equivalent to /// blend(0,a,!mask), or blend(a,0,mask). int4 blend0not (const int4& a, const bool4& mask); /// Select 'a' where mask is true, 'b' where mask is false. Sure, it's a /// synonym for blend with arguments rearranged, but this is more clear /// because the arguments are symmetric to scalar (cond ? a : b). int4 select (const bool4& mask, const int4& a, const int4& b); // Per-element math int4 abs (const int4& a); int4 min (const int4& a, const int4& b); int4 max (const int4& a, const int4& b); // Circular bit rotate by k bits, for N values at once. int4 rotl32 (const int4& x, const unsigned int k); /// andnot(a,b) returns ((~a) & b) int4 andnot (const int4& a, const int4& b); /// Bitcast back and forth to intN (not a convert -- move the bits!) int4 bitcast_to_int (const bool4& x); int4 bitcast_to_int (const float4& x); float4 bitcast_to_float (const int4& x); void transpose (int4 &a, int4 &b, int4 &c, int4 &d); void transpose (const int4& a, const int4& b, const int4& c, const int4& d, int4 &r0, int4 &r1, int4 &r2, int4 &r3); int4 AxBxCxDx (const int4& a, const int4& b, const int4& c, const int4& d); /// Integer 8-vector, accelerated by SIMD instructions when available. class int8 { public: static const char* type_name() { return "int8"; } typedef int value_t; ///< Underlying equivalent scalar value type enum { elements = 8 }; ///< Number of scalar elements enum { paddedelements =8 }; ///< Number of scalar elements for full pad enum { bits = elements*32 }; ///< Total number of bits typedef simd_raw_t::type simd_t; ///< the native SIMD type used typedef bool8 bool_t; ///< bool type of the same length typedef float8 float_t; ///< float type of the same length /// Default constructor (contents undefined) int8 () { } /// Construct from a single value (store it in all slots) int8 (int a); /// Construct from 2 values -- (a,a,b,b) int8 (int a, int b); /// Construct from 8 values (won't work for int8) int8 (int a, int b, int c, int d, int e, int f, int g, int h); /// Construct from a pointer to values int8 (const int *vals); /// Construct from a pointer to unsigned short values explicit int8 (const unsigned short *vals); /// Construct from a pointer to signed short values explicit int8 (const short *vals); /// Construct from a pointer to unsigned char values (0 - 255) explicit int8 (const unsigned char *vals); /// Construct from a pointer to signed char values (-128 - 127) explicit int8 (const char *vals); /// Copy construct from another int8 int8 (const int8 & other) { m_simd = other.m_simd; } /// Convert a float8 to an int8. Equivalent to i = (int)f; explicit int8 (const float8& f); // implementation below /// Construct from two int4's int8 (const int4 &lo, const int4 &hi); /// Construct from the underlying SIMD type int8 (const simd_t& m) : m_simd(m) { } /// Return the raw SIMD type operator simd_t () const { return m_simd; } simd_t simd () const { return m_simd; } /// Sset all components to 0 void clear () ; /// Return an int8 with all components set to 0 static const int8 Zero (); /// Return an int8 with all components set to 1 static const int8 One (); /// Return an int8 with all components set to -1 (aka 0xffffffff) static const int8 NegOne (); /// Return an int8 with incremented components (e.g., 0,1,2,3). /// Optional argument can give a non-zero starting point. static const int8 Iota (int start=0, int step=1); /// Assign one value to all components. const int8 & operator= (int a); /// Assignment from another int8 const int8 & operator= (const int8& other) ; /// Component access (set) int& operator[] (int i) ; /// Component access (get) int operator[] (int i) const; value_t x () const; value_t y () const; value_t z () const; value_t w () const; void set_x (value_t val); void set_y (value_t val); void set_z (value_t val); void set_w (value_t val); /// Extract the lower percision int4 int4 lo () const; /// Extract the higher percision int4 int4 hi () const; /// Helper: load a single int into all components void load (int a); /// Load separate values into each component. (doesn't work for int8) void load (int a, int b, int c, int d, int e, int f, int g, int h); /// Load from an array of 8 values void load (const int *values); void load (const int *values, int n) ; /// Load from an array of 8 unsigned short values, convert to int8 void load (const unsigned short *values) ; /// Load from an array of 8 unsigned short values, convert to int8 void load (const short *values); /// Load from an array of 8 unsigned char values, convert to int8 void load (const unsigned char *values); /// Load from an array of 8 unsigned char values, convert to int8 void load (const char *values); /// Store the values into memory void store (int *values) const; /// Store the first n values into memory void store (int *values, int n) const; /// Store the least significant 16 bits of each element into adjacent /// unsigned shorts. void store (unsigned short *values) const; /// Store the least significant 8 bits of each element into adjacent /// unsigned chars. void store (unsigned char *values) const; // Arithmetic operators (component-by-component) friend int8 operator+ (const int8& a, const int8& b); friend int8 operator- (const int8& a); friend int8 operator- (const int8& a, const int8& b); friend int8 operator* (const int8& a, const int8& b); friend int8 operator/ (const int8& a, const int8& b); friend int8 operator% (const int8& a, const int8& b); friend const int8 & operator+= (int8& a, const int8& b); friend const int8 & operator-= (int8& a, const int8& b); friend const int8 & operator*= (int8& a, const int8& b); friend const int8 & operator/= (int8& a, const int8& b); friend const int8 & operator%= (int8& a, const int8& b); // Bitwise operators (component-by-component) friend int8 operator& (const int8& a, const int8& b); friend int8 operator| (const int8& a, const int8& b); friend int8 operator^ (const int8& a, const int8& b); friend const int8& operator&= (int8& a, const int8& b); friend const int8& operator|= (int8& a, const int8& b); friend const int8& operator^= (int8& a, const int8& b); friend int8 operator~ (const int8& a); friend int8 operator<< (const int8& a, unsigned int bits); friend int8 operator>> (const int8& a, unsigned int bits); friend const int8& operator<<= (int8& a, unsigned int bits); friend const int8& operator>>= (int8& a, unsigned int bits); // Comparison operators (component-by-component) friend bool8 operator== (const int8& a, const int8& b); friend bool8 operator!= (const int8& a, const int8& b); friend bool8 operator< (const int8& a, const int8& b); friend bool8 operator> (const int8& a, const int8& b); friend bool8 operator>= (const int8& a, const int8& b); friend bool8 operator<= (const int8& a, const int8& b); /// Stream output friend std::ostream& operator<< (std::ostream& cout, const int8& a); private: // The actual data representation union { simd_t m_simd; value_t m_val[elements]; simd_raw_t::type m_4[2]; }; }; // Shift right logical -- unsigned shift. This differs from operator>> // in how it handles the sign bit. (1<<31) >> 1 == (1<<31), but // srl((1<<31),1) == 1<<30. int8 srl (const int8& val, const unsigned int bits); /// Helper: shuffle/swizzle with constant (templated) indices. /// Example: shuffle<1,1,2,2>(bool4(a,b,c,d)) returns (b,b,c,c) template int8 shuffle (const int8& a); /// shuffle(a) is the same as shuffle(a) template int8 shuffle (const int8& a); /// Helper: as rapid as possible extraction of one component, when the /// index is fixed. template int extract (const int8& v); /// Helper: substitute val for a[i] template int8 insert (const int8& a, int val); /// The sum of all components, returned in all components. int8 vreduce_add (const int8& v); // Reduction across all components int reduce_add (const int8& v); int reduce_and (const int8& v); int reduce_or (const int8& v); /// Use a bool mask to select between components of a (if mask[i] is false) /// and b (if mask[i] is true), i.e., mask[i] ? b[i] : a[i]. int8 blend (const int8& a, const int8& b, const bool8& mask); /// Use a bool mask to select between `a` (if mask[i] is true) or 0 if /// mask[i] is false), i.e., mask[i] ? a[i] : 0. Equivalent to /// blend(0,a,mask). int8 blend0 (const int8& a, const bool8& mask); /// Use a bool mask to select between components of a (if mask[i] is false) /// or 0 (if mask[i] is true), i.e., mask[i] ? 0 : a[i]. Equivalent to /// blend(0,a,!mask), or blend(a,0,mask). int8 blend0not (const int8& a, const bool8& mask); /// Select 'a' where mask is true, 'b' where mask is false. Sure, it's a /// synonym for blend with arguments rearranged, but this is more clear /// because the arguments are symmetric to scalar (cond ? a : b). int8 select (const bool8& mask, const int8& a, const int8& b); // Per-element math int8 abs (const int8& a); int8 min (const int8& a, const int8& b); int8 max (const int8& a, const int8& b); // Circular bit rotate by k bits, for N values at once. int8 rotl32 (const int8& x, const unsigned int k); /// andnot(a,b) returns ((~a) & b) int8 andnot (const int8& a, const int8& b); /// Bitcast back and forth to intN (not a convert -- move the bits!) int8 bitcast_to_int (const bool8& x); int8 bitcast_to_int (const float8& x); float8 bitcast_to_float (const int8& x); /// Floating point 4-vector, accelerated by SIMD instructions when /// available. class float4 { public: static const char* type_name() { return "float4"; } typedef float value_t; ///< Underlying equivalent scalar value type typedef int4 int_t; ///< SIMD int type typedef bool4 bool_t; ///< SIMD bool type enum { elements = 4 }; ///< Number of scalar elements enum { paddedelements = 4 }; ///< Number of scalar elements for full pad enum { bits = elements*32 }; ///< Total number of bits typedef simd_raw_t::type simd_t; ///< the native SIMD type used /// Default constructor (contents undefined) float4 () { } /// Construct from a single value (store it in all slots) float4 (float a) { load(a); } /// Construct from 3 or 4 values float4 (float a, float b, float c, float d=0.0f) { load(a,b,c,d); } /// Construct from a pointer to 4 values float4 (const float *f) { load (f); } /// Copy construct from another float4 float4 (const float4 &other) { m_simd = other.m_simd; } /// Construct from an int4 (promoting all components to float) explicit float4 (const int4& ival); /// Construct from the underlying SIMD type float4 (const simd_t& m) : m_simd(m) { } /// Return the raw SIMD type operator simd_t () const { return m_simd; } simd_t simd () const { return m_simd; } /// Construct from a Imath::V3f float4 (const Imath::V3f &v) { load (v[0], v[1], v[2]); } /// Cast to a Imath::V3f const Imath::V3f& V3f () const { return *(const Imath::V3f*)this; } #if defined(ILMBASE_VERSION_MAJOR) && ILMBASE_VERSION_MAJOR >= 2 // V4f is not defined for older Ilmbase. It's certainly safe for 2.x. /// Construct from a Imath::V4f float4 (const Imath::V4f &v) { load ((const float *)&v); } /// Cast to a Imath::V4f const Imath::V4f& V4f () const { return *(const Imath::V4f*)this; } #endif /// Construct from a pointer to 4 unsigned short values explicit float4 (const unsigned short *vals) { load(vals); } /// Construct from a pointer to 4 short values explicit float4 (const short *vals) { load(vals); } /// Construct from a pointer to 4 unsigned char values explicit float4 (const unsigned char *vals) { load(vals); } /// Construct from a pointer to 4 char values explicit float4 (const char *vals) { load(vals); } #ifdef _HALF_H_ /// Construct from a pointer to 4 half (16 bit float) values explicit float4 (const half *vals) { load(vals); } #endif /// Assign a single value to all components const float4 & operator= (float a) { load(a); return *this; } /// Assign a float4 const float4 & operator= (float4 other) { m_simd = other.m_simd; return *this; } /// Return a float4 with all components set to 0.0 static const float4 Zero (); /// Return a float4 with all components set to 1.0 static const float4 One (); /// Return a float4 with incremented components (e.g., 0.0,1.0,2.0,3.0). /// Optional argument can give a non-zero starting point and non-1 step. static const float4 Iota (float start=0.0f, float step=1.0f); /// Set all components to 0.0 void clear (); #if defined(ILMBASE_VERSION_MAJOR) && ILMBASE_VERSION_MAJOR >= 2 /// Assign from a Imath::V4f const float4 & operator= (const Imath::V4f &v); #endif /// Assign from a Imath::V3f const float4 & operator= (const Imath::V3f &v); /// Component access (set) float& operator[] (int i); /// Component access (get) float operator[] (int i) const; value_t x () const; value_t y () const; value_t z () const; value_t w () const; void set_x (value_t val); void set_y (value_t val); void set_z (value_t val); void set_w (value_t val); /// Helper: load a single value into all components void load (float val); /// Helper: load 3 or 4 values. (If 3 are supplied, the 4th will be 0.) void load (float a, float b, float c, float d=0.0f); /// Load from an array of 4 values void load (const float *values); /// Load from a partial array of <=4 values. Unassigned values are /// undefined. void load (const float *values, int n); /// Load from an array of 4 unsigned short values, convert to float void load (const unsigned short *values); /// Load from an array of 4 short values, convert to float void load (const short *values); /// Load from an array of 4 unsigned char values, convert to float void load (const unsigned char *values); /// Load from an array of 4 char values, convert to float void load (const char *values); #ifdef _HALF_H_ /// Load from an array of 4 half values, convert to float void load (const half *values); #endif /* _HALF_H_ */ void store (float *values) const; /// Store the first n values into memory void store (float *values, int n) const; #ifdef _HALF_H_ void store (half *values) const; #endif // Arithmetic operators friend float4 operator+ (const float4& a, const float4& b); const float4 & operator+= (const float4& a); float4 operator- () const; friend float4 operator- (const float4& a, const float4& b); const float4 & operator-= (const float4& a); friend float4 operator* (const float4& a, const float4& b); const float4 & operator*= (const float4& a); const float4 & operator*= (float val); friend float4 operator/ (const float4& a, const float4& b); const float4 & operator/= (const float4& a); const float4 & operator/= (float val); // Comparison operations friend bool4 operator== (const float4& a, const float4& b); friend bool4 operator!= (const float4& a, const float4& b); friend bool4 operator< (const float4& a, const float4& b); friend bool4 operator> (const float4& a, const float4& b); friend bool4 operator>= (const float4& a, const float4& b); friend bool4 operator<= (const float4& a, const float4& b); // Some oddball items that are handy /// Combine the first two components of A with the first two components /// of B. friend float4 AxyBxy (const float4& a, const float4& b); /// Combine the first two components of A with the first two components /// of B, but interleaved. friend float4 AxBxAyBy (const float4& a, const float4& b); /// Return xyz components, plus 0 for w float4 xyz0 () const; /// Return xyz components, plus 1 for w float4 xyz1 () const; /// Stream output friend inline std::ostream& operator<< (std::ostream& cout, const float4& val); protected: // The actual data representation union { simd_t m_simd; value_t m_val[paddedelements]; }; }; /// Helper: shuffle/swizzle with constant (templated) indices. /// Example: shuffle<1,1,2,2>(bool4(a,b,c,d)) returns (b,b,c,c) template float4 shuffle (const float4& a); /// shuffle(a) is the same as shuffle(a) template float4 shuffle (const float4& a); /// Helper: as rapid as possible extraction of one component, when the /// index is fixed. template float extract (const float4& a); /// Helper: substitute val for a[i] template float4 insert (const float4& a, float val); /// The sum of all components, returned in all components. float4 vreduce_add (const float4& v); /// The sum of all components, returned as a scalar. float reduce_add (const float4& v); /// Return the float dot (inner) product of a and b in every component. float4 vdot (const float4 &a, const float4 &b); /// Return the float dot (inner) product of a and b. float dot (const float4 &a, const float4 &b); /// Return the float 3-component dot (inner) product of a and b in /// all components. float4 vdot3 (const float4 &a, const float4 &b); /// Return the float 3-component dot (inner) product of a and b. float dot3 (const float4 &a, const float4 &b); /// Use a bool mask to select between components of a (if mask[i] is false) /// and b (if mask[i] is true), i.e., mask[i] ? b[i] : a[i]. float4 blend (const float4& a, const float4& b, const bool4& mask); /// Use a bool mask to select between `a` (if mask[i] is true) or 0 if /// mask[i] is false), i.e., mask[i] ? a[i] : 0. Equivalent to /// blend(0,a,mask). float4 blend0 (const float4& a, const bool4& mask); /// Use a bool mask to select between components of a (if mask[i] is false) /// or 0 (if mask[i] is true), i.e., mask[i] ? 0 : a[i]. Equivalent to /// blend(0,a,!mask), or blend(a,0,mask). float4 blend0not (const float4& a, const bool4& mask); /// "Safe" divide of float4/float4 -- for any component of the divisor /// that is 0, return 0 rather than Inf. float4 safe_div (const float4 &a, const float4 &b); /// Homogeneous divide to turn a float4 into a float3. float3 hdiv (const float4 &a); /// Select 'a' where mask is true, 'b' where mask is false. Sure, it's a /// synonym for blend with arguments rearranged, but this is more clear /// because the arguments are symmetric to scalar (cond ? a : b). float4 select (const bool4& mask, const float4& a, const float4& b); // Per-element math float4 abs (const float4& a); ///< absolute value (float) float4 sign (const float4& a); ///< 1.0 when value >= 0, -1 when negative float4 ceil (const float4& a); float4 floor (const float4& a); int4 floori (const float4& a); ///< (int)floor /// Per-element round to nearest integer (rounding away from 0 in cases /// that are exactly half way). float4 round (const float4& a); /// Per-element round to nearest integer (rounding away from 0 in cases /// that are exactly half way). int4 rint (const float4& a); float4 sqrt (const float4 &a); float4 rsqrt (const float4 &a); ///< Fully accurate 1/sqrt float4 rsqrt_fast (const float4 &a); ///< Fast, approximate 1/sqrt float4 min (const float4& a, const float4& b); ///< Per-element min float4 max (const float4& a, const float4& b); ///< Per-element max template T exp (const T& v); // template for all SIMD variants template T log (const T& v); /// andnot(a,b) returns ((~a) & b) float4 andnot (const float4& a, const float4& b); // Fused multiply and add (or subtract): float4 madd (const float4& a, const float4& b, const float4& c); // a*b + c float4 msub (const float4& a, const float4& b, const float4& c); // a*b - c float4 nmadd (const float4& a, const float4& b, const float4& c); // -a*b + c float4 nmsub (const float4& a, const float4& b, const float4& c); // -a*b - c /// Transpose the rows and columns of the 4x4 matrix [a b c d]. /// In the end, a will have the original (a[0], b[0], c[0], d[0]), /// b will have the original (a[1], b[1], c[1], d[1]), and so on. void transpose (float4 &a, float4 &b, float4 &c, float4 &d); void transpose (const float4& a, const float4& b, const float4& c, const float4& d, float4 &r0, float4 &r1, float4 &r2, float4 &r3); /// Make a float4 consisting of the first element of each of 4 float4's. float4 AxBxCxDx (const float4& a, const float4& b, const float4& c, const float4& d); /// Floating point 3-vector, aligned to be internally identical to a float4. /// The way it differs from float4 is that all of he load functions only /// load three values, and all the stores only store 3 values. The vast /// majority of ops just fall back to the float4 version, and so will /// operate on the 4th component, but we won't care about that results. class float3 : public float4 { public: static const char* type_name() { return "float3"; } enum { elements = 3 }; ///< Number of scalar elements enum { paddedelements = 4 }; ///< Number of scalar elements for full pad /// Default constructor (contents undefined) float3 () { } /// Construct from a single value (store it in all slots) float3 (float a) { load(a); } /// Construct from 3 or 4 values float3 (float a, float b, float c) { float4::load(a,b,c); } /// Construct from a pointer to 4 values float3 (const float *f) { load (f); } /// Copy construct from another float3 float3 (const float3 &other); explicit float3 (const float4 &other); #if OIIO_SIMD /// Construct from the underlying SIMD type explicit float3 (const simd_t& m) : float4(m) { } #endif /// Construct from a Imath::V3f float3 (const Imath::V3f &v) : float4(v) { } /// Cast to a Imath::V3f const Imath::V3f& V3f () const { return *(const Imath::V3f*)this; } /// Construct from a pointer to 4 unsigned short values explicit float3 (const unsigned short *vals) { load(vals); } /// Construct from a pointer to 4 short values explicit float3 (const short *vals) { load(vals); } /// Construct from a pointer to 4 unsigned char values explicit float3 (const unsigned char *vals) { load(vals); } /// Construct from a pointer to 4 char values explicit float3 (const char *vals) { load(vals); } #ifdef _HALF_H_ /// Construct from a pointer to 4 half (16 bit float) values explicit float3 (const half *vals) { load(vals); } #endif /// Assign a single value to all components const float3 & operator= (float a) { load(a); return *this; } /// Return a float3 with all components set to 0.0 static const float3 Zero (); /// Return a float3 with all components set to 1.0 static const float3 One (); /// Return a float3 with incremented components (e.g., 0.0,1.0,2.0). /// Optional argument can give a non-zero starting point and non-1 step. static const float3 Iota (float start=0.0f, float step=1.0f); /// Helper: load a single value into all components void load (float val); /// Load from an array of 4 values void load (const float *values); /// Load from an array of 4 values void load (const float *values, int n); /// Load from an array of 4 unsigned short values, convert to float void load (const unsigned short *values); /// Load from an array of 4 short values, convert to float void load (const short *values); /// Load from an array of 4 unsigned char values, convert to float void load (const unsigned char *values); /// Load from an array of 4 char values, convert to float void load (const char *values); #ifdef _HALF_H_ /// Load from an array of 4 half values, convert to float void load (const half *values); #endif /* _HALF_H_ */ void store (float *values) const; void store (float *values, int n) const; #ifdef _HALF_H_ void store (half *values) const; #endif /// Store into an Imath::V3f reference. void store (Imath::V3f &vec) const; // Math operators -- define in terms of float3. friend float3 operator+ (const float3& a, const float3& b); const float3 & operator+= (const float3& a); float3 operator- () const; friend float3 operator- (const float3& a, const float3& b); const float3 & operator-= (const float3& a); friend float3 operator* (const float3& a, const float3& b); const float3 & operator*= (const float3& a); const float3 & operator*= (float a); friend float3 operator/ (const float3& a, const float3& b); const float3 & operator/= (const float3& a); const float3 & operator/= (float a); float3 normalized () const; float3 normalized_fast () const; /// Stream output friend inline std::ostream& operator<< (std::ostream& cout, const float3& val); }; /// SIMD-based 4x4 matrix. This is guaranteed to have memory layout (when /// not in registers) isomorphic to Imath::M44f. class matrix44 { public: // Uninitialized OIIO_FORCEINLINE matrix44 () #ifndef OIIO_SIMD_SSE : m_mat(Imath::UNINITIALIZED) #endif { } /// Construct from a reference to an Imath::M44f OIIO_FORCEINLINE matrix44 (const Imath::M44f &M) { #if OIIO_SIMD_SSE m_row[0].load (M[0]); m_row[1].load (M[1]); m_row[2].load (M[2]); m_row[3].load (M[3]); #else m_mat = M; #endif } /// Construct from a float array OIIO_FORCEINLINE explicit matrix44 (const float *f) { #if OIIO_SIMD_SSE m_row[0].load (f+0); m_row[1].load (f+4); m_row[2].load (f+8); m_row[3].load (f+12); #else memcpy (&m_mat, f, 16*sizeof(float)); #endif } /// Construct from 4 float4 rows OIIO_FORCEINLINE explicit matrix44 (const float4& a, const float4& b, const float4& c, const float4& d) { #if OIIO_SIMD_SSE m_row[0] = a; m_row[1] = b; m_row[2] = c; m_row[3] = d; #else a.store (m_mat[0]); b.store (m_mat[1]); c.store (m_mat[2]); d.store (m_mat[3]); #endif } /// Construct from 4 float[4] rows OIIO_FORCEINLINE explicit matrix44 (const float *a, const float *b, const float *c, const float *d) { #if OIIO_SIMD_SSE m_row[0].load(a); m_row[1].load(b); m_row[2].load(c); m_row[3].load(d); #else memcpy (m_mat[0], a, 4*sizeof(float)); memcpy (m_mat[1], b, 4*sizeof(float)); memcpy (m_mat[2], c, 4*sizeof(float)); memcpy (m_mat[3], d, 4*sizeof(float)); #endif } /// Present as an Imath::M44f const Imath::M44f& M44f() const; /// Return one row float4 operator[] (int i) const; /// Return the transposed matrix matrix44 transposed () const; /// Transform 3-point V by 4x4 matrix M. float3 transformp (const float3 &V) const; /// Transform 3-vector V by 4x4 matrix M. float3 transformv (const float3 &V) const; /// Transform 3-vector V by the transpose of 4x4 matrix M. float3 transformvT (const float3 &V) const; bool operator== (const matrix44& m) const; bool operator== (const Imath::M44f& m) const ; friend bool operator== (const Imath::M44f& a, const matrix44 &b); bool operator!= (const matrix44& m) const; bool operator!= (const Imath::M44f& m) const; friend bool operator!= (const Imath::M44f& a, const matrix44 &b); /// Return the inverse of the matrix. matrix44 inverse() const; /// Stream output friend inline std::ostream& operator<< (std::ostream& cout, const matrix44 &M); private: #if OIIO_SIMD_SSE float4 m_row[4]; #else Imath::M44f m_mat; #endif }; /// Transform 3-point V by 4x4 matrix M. float3 transformp (const matrix44 &M, const float3 &V); float3 transformp (const Imath::M44f &M, const float3 &V); /// Transform 3-vector V by 4x4 matrix M. float3 transformv (const matrix44 &M, const float3 &V); float3 transformv (const Imath::M44f &M, const float3 &V); // Transform 3-vector by the transpose of 4x4 matrix M. float3 transformvT (const matrix44 &M, const float3 &V); float3 transformvT (const Imath::M44f &M, const float3 &V); /// Floating point 8-vector, accelerated by SIMD instructions when /// available. class float8 { public: enum { elements = 8 }; ///< Number of scalar elements enum { paddedelements = 8 }; ///< Number of scalar elements for full pad enum { bits = elements*32 }; ///< Total number of bits typedef float value_t; ///< Underlying equivalent scalar value type typedef simd_raw_t::type simd_t; ///< the native SIMD type used typedef bool8 bool_t; ///< bool type of the same length typedef int8 int_t; ///< int type of the same length static const char* type_name() { return "float8"; } /// Default constructor (contents undefined) float8 () { } /// Construct from a single value (store it in all slots) float8 (float a) { load(a); } /// Construct from 8 values float8 (float a, float b, float c, float d, float e, float f, float g, float h) { load(a,b,c,d,e,f,g,h); } /// Construct from a pointer to 8 values float8 (const float *f) { load (f); } /// Copy construct from another float8 float8 (const float8 &other) { m_simd = other.m_simd; } /// Construct from an int vector (promoting all components to float) explicit float8 (const int8& ival); /// Construct from two float4's float8 (const float4 &lo, const float4 &hi); /// Construct from the underlying SIMD type float8 (const simd_t& m) : m_simd(m) { } /// Return the raw SIMD type operator simd_t () const { return m_simd; } simd_t simd () const { return m_simd; } /// Construct from a pointer to unsigned short values explicit float8 (const unsigned short *vals) { load(vals); } /// Construct from a pointer to short values explicit float8 (const short *vals) { load(vals); } /// Construct from a pointer to unsigned char values explicit float8 (const unsigned char *vals) { load(vals); } /// Construct from a pointer to char values explicit float8 (const char *vals) { load(vals); } #ifdef _HALF_H_ /// Construct from a pointer to half (16 bit float) values explicit float8 (const half *vals) { load(vals); } #endif /// Assign a single value to all components const float8& operator= (float a) { load(a); return *this; } /// Assign a float8 const float8& operator= (float8 other) { m_simd = other.m_simd; return *this; } /// Return a float8 with all components set to 0.0 static const float8 Zero (); /// Return a float8 with all components set to 1.0 static const float8 One (); /// Return a float8 with incremented components (e.g., 0,1,2,3,...) /// Optional argument can give a non-zero starting point and non-1 step. static const float8 Iota (float start=0.0f, float step=1.0f); /// Set all components to 0.0 void clear (); /// Component access (set) float& operator[] (int i); /// Component access (get) float operator[] (int i) const; value_t x () const; value_t y () const; value_t z () const; value_t w () const; void set_x (value_t val); void set_y (value_t val); void set_z (value_t val); void set_w (value_t val); /// Extract the lower percision float4 float4 lo () const; /// Extract the higher percision float4 float4 hi () const; /// Helper: load a single value into all components void load (float val); /// Helper: load 8 values void load (float a, float b, float c, float d, float e, float f, float g, float h); /// Load from an array of values void load (const float *values); /// Load from a partial array of <=8 values. Unassigned values are /// undefined. void load (const float *values, int n); /// Load from an array of 8 unsigned short values, convert to float void load (const unsigned short *values); /// Load from an array of 8 short values, convert to float void load (const short *values); /// Load from an array of 8 unsigned char values, convert to float void load (const unsigned char *values); /// Load from an array of 8 char values, convert to float void load (const char *values); #ifdef _HALF_H_ /// Load from an array of 8 half values, convert to float void load (const half *values); #endif /* _HALF_H_ */ void store (float *values) const; /// Store the first n values into memory void store (float *values, int n) const; #ifdef _HALF_H_ void store (half *values) const; #endif // Arithmetic operators friend float8 operator+ (const float8& a, const float8& b); const float8 & operator+= (const float8& a); float8 operator- () const; friend float8 operator- (const float8& a, const float8& b); const float8 & operator-= (const float8& a); friend float8 operator* (const float8& a, const float8& b); const float8 & operator*= (const float8& a); const float8 & operator*= (float val); friend float8 operator/ (const float8& a, const float8& b); const float8 & operator/= (const float8& a); const float8 & operator/= (float val); // Comparison operations friend bool8 operator== (const float8& a, const float8& b); friend bool8 operator!= (const float8& a, const float8& b); friend bool8 operator< (const float8& a, const float8& b); friend bool8 operator> (const float8& a, const float8& b); friend bool8 operator>= (const float8& a, const float8& b); friend bool8 operator<= (const float8& a, const float8& b); // Some oddball items that are handy /// Stream output friend inline std::ostream& operator<< (std::ostream& cout, const float8& val); protected: // The actual data representation union { simd_t m_simd; value_t m_val[paddedelements]; simd_raw_t::type m_4[2]; }; }; /// Helper: shuffle/swizzle with constant (templated) indices. /// Example: shuffle<1,1,2,2>(bool4(a,b,c,d)) returns (b,b,c,c) template float8 shuffle (const float8& a); /// shuffle(a) is the same as shuffle(a) template float8 shuffle (const float8& a); /// Helper: as rapid as possible extraction of one component, when the /// index is fixed. template float extract (const float8& a); /// Helper: substitute val for a[i] template float8 insert (const float8& a, float val); /// The sum of all components, returned in all components. float8 vreduce_add (const float8& v); /// The sum of all components, returned as a scalar. float reduce_add (const float8& v); /// Return the float dot (inner) product of a and b in every component. float8 vdot (const float8 &a, const float8 &b); /// Return the float dot (inner) product of a and b. float dot (const float8 &a, const float8 &b); /// Return the float 3-component dot (inner) product of a and b in /// all components. float8 vdot3 (const float8 &a, const float8 &b); /// Return the float 3-component dot (inner) product of a and b. float dot3 (const float8 &a, const float8 &b); /// Use a bool mask to select between components of a (if mask[i] is false) /// and b (if mask[i] is true), i.e., mask[i] ? b[i] : a[i]. float8 blend (const float8& a, const float8& b, const bool8& mask); /// Use a bool mask to select between `a` (if mask[i] is true) or 0 if /// mask[i] is false), i.e., mask[i] ? a[i] : 0. Equivalent to /// blend(0,a,mask). float8 blend0 (const float8& a, const bool8& mask); /// Use a bool mask to select between components of a (if mask[i] is false) /// or 0 (if mask[i] is true), i.e., mask[i] ? 0 : a[i]. Equivalent to /// blend(0,a,!mask), or blend(a,0,mask). float8 blend0not (const float8& a, const bool8& mask); /// "Safe" divide of float8/float8 -- for any component of the divisor /// that is 0, return 0 rather than Inf. float8 safe_div (const float8 &a, const float8 &b); /// Select 'a' where mask is true, 'b' where mask is false. Sure, it's a /// synonym for blend with arguments rearranged, but this is more clear /// because the arguments are symmetric to scalar (cond ? a : b). float8 select (const bool8& mask, const float8& a, const float8& b); // Per-element math float8 abs (const float8& a); ///< absolute value (float) float8 sign (const float8& a); ///< 1.0 when value >= 0, -1 when negative float8 ceil (const float8& a); float8 floor (const float8& a); int8 floori (const float8& a); ///< (int)floor /// Per-element round to nearest integer (rounding away from 0 in cases /// that are exactly half way). float8 round (const float8& a); /// Per-element round to nearest integer (rounding away from 0 in cases /// that are exactly half way). int8 rint (const float8& a); float8 sqrt (const float8 &a); float8 rsqrt (const float8 &a); ///< Fully accurate 1/sqrt float8 rsqrt_fast (const float8 &a); ///< Fast, approximate 1/sqrt float8 min (const float8& a, const float8& b); ///< Per-element min float8 max (const float8& a, const float8& b); ///< Per-element max // float8 exp (const float8& v); // See template with float4 // float8 log (const float8& v); // See template with float4 /// andnot(a,b) returns ((~a) & b) float8 andnot (const float8& a, const float8& b); // Fused multiply and add (or subtract): float8 madd (const float8& a, const float8& b, const float8& c); // a*b + c float8 msub (const float8& a, const float8& b, const float8& c); // a*b - c float8 nmadd (const float8& a, const float8& b, const float8& c); // -a*b + c float8 nmsub (const float8& a, const float8& b, const float8& c); // -a*b - c ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // // Gory implementation details follow. // // ^^^ All declarations and documention is above ^^^ // // vvv Below is the implementation, often considerably cluttered with // #if's for each architeture, and unapologitic use of intrinsics and // every manner of dirty trick we can think of to make things fast. // Some of this isn't pretty. We won't recapitulate comments or // documentation of what the functions are supposed to do, please // consult the declarations above for that. // // Here be dragons. // ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // bool4 implementation OIIO_FORCEINLINE int bool4::operator[] (int i) const { DASSERT(i >= 0 && i < elements); #if OIIO_SIMD_SSE return ((_mm_movemask_ps(m_simd) >> i) & 1) ? -1 : 0; #else return m_val[i]; #endif } OIIO_FORCEINLINE int& bool4::operator[] (int i) { DASSERT(i >= 0 && i < elements); return m_val[i]; } OIIO_FORCEINLINE void bool4::setcomp (int i, bool value) { DASSERT(i >= 0 && i < elements); m_val[i] = value ? -1 : 0; } OIIO_FORCEINLINE std::ostream& operator<< (std::ostream& cout, const bool4& a) { cout << a[0]; for (int i = 1; i < a.elements; ++i) cout << ' ' << a[i]; return cout; } OIIO_FORCEINLINE void bool4::load (bool a) { #if OIIO_SIMD_SSE m_simd = _mm_castsi128_ps(_mm_set1_epi32(-int(a))); #else int val = -int(a); SIMD_CONSTRUCT (val); #endif } OIIO_FORCEINLINE void bool4::load (bool a, bool b, bool c, bool d) { #if OIIO_SIMD_SSE // N.B. -- we need to reverse the order because of our convention // of storing a,b,c,d in the same order in memory. m_simd = _mm_castsi128_ps(_mm_set_epi32(-int(d), -int(c), -int(b), -int(a))); #else m_val[0] = -int(a); m_val[1] = -int(b); m_val[2] = -int(c); m_val[3] = -int(d); #endif } OIIO_FORCEINLINE bool4::bool4 (const bool *a) { load (a[0], a[1], a[2], a[3]); } OIIO_FORCEINLINE const bool4& bool4::operator= (const bool4 & other) { m_simd = other.m_simd; return *this; } OIIO_FORCEINLINE void bool4::clear () { #if OIIO_SIMD_SSE m_simd = _mm_setzero_ps(); #else *this = false; #endif } OIIO_FORCEINLINE const bool4 bool4::False () { #if OIIO_SIMD_SSE return _mm_setzero_ps(); #else return false; #endif } OIIO_FORCEINLINE const bool4 bool4::True () { // Fastest way to fill with all 1 bits is to cmp any value to itself. #if OIIO_SIMD_SSE # if OIIO_SIMD_AVX && (OIIO_GNUC_VERSION > 50000) __m128i anyval = _mm_undefined_si128(); # else __m128i anyval = _mm_setzero_si128(); # endif return _mm_castsi128_ps (_mm_cmpeq_epi8 (anyval, anyval)); #else return true; #endif } OIIO_FORCEINLINE void bool4::store (bool *values) const { SIMD_DO (values[i] = m_val[i] ? true : false); } OIIO_FORCEINLINE void bool4::store (bool *values, int n) const { DASSERT (n >= 0 && n <= elements); for (int i = 0; i < n; ++i) values[i] = m_val[i] ? true : false; } OIIO_FORCEINLINE bool4 operator! (const bool4 & a) { #if OIIO_SIMD_SSE return _mm_xor_ps (a.simd(), bool4::True()); #else SIMD_RETURN (bool4, a[i] ^ (-1)); #endif } OIIO_FORCEINLINE bool4 operator& (const bool4 & a, const bool4 & b) { #if OIIO_SIMD_SSE return _mm_and_ps (a.simd(), b.simd()); #else SIMD_RETURN (bool4, a[i] & b[i]); #endif } OIIO_FORCEINLINE bool4 operator| (const bool4 & a, const bool4 & b) { #if OIIO_SIMD_SSE return _mm_or_ps (a.simd(), b.simd()); #else SIMD_RETURN (bool4, a[i] | b[i]); #endif } OIIO_FORCEINLINE bool4 operator^ (const bool4& a, const bool4& b) { #if OIIO_SIMD_SSE return _mm_xor_ps (a.simd(), b.simd()); #else SIMD_RETURN (bool4, a[i] ^ b[i]); #endif } OIIO_FORCEINLINE const bool4& operator&= (bool4& a, const bool4 &b) { return a = a & b; } OIIO_FORCEINLINE const bool4& operator|= (bool4& a, const bool4& b) { return a = a | b; } OIIO_FORCEINLINE const bool4& operator^= (bool4& a, const bool4& b) { return a = a ^ b; } OIIO_FORCEINLINE bool4 operator~ (const bool4& a) { #if OIIO_SIMD_SSE // Fastest way to bit-complement in SSE is to xor with 0xffffffff. return _mm_xor_ps (a.simd(), bool4::True()); #else SIMD_RETURN (bool4, ~a[i]); #endif } OIIO_FORCEINLINE bool4 operator== (const bool4 & a, const bool4 & b) { #if OIIO_SIMD_SSE return _mm_castsi128_ps (_mm_cmpeq_epi32 (_mm_castps_si128 (a), _mm_castps_si128(b))); #else SIMD_RETURN (bool4, a[i] == b[i] ? -1 : 0); #endif } OIIO_FORCEINLINE bool4 operator!= (const bool4 & a, const bool4 & b) { #if OIIO_SIMD_SSE return _mm_xor_ps (a, b); #else SIMD_RETURN (bool4, a[i] != b[i] ? -1 : 0); #endif } #if OIIO_SIMD_SSE // Shuffling. Use like this: x = shuffle<3,2,1,0>(b) template OIIO_FORCEINLINE __m128i shuffle_sse (__m128i v) { return _mm_shuffle_epi32(v, _MM_SHUFFLE(i3, i2, i1, i0)); } #endif #if OIIO_SIMD_SSE >= 3 // SSE3 has intrinsics for a few special cases template<> OIIO_FORCEINLINE __m128i shuffle_sse<0, 0, 2, 2> (__m128i a) { return _mm_castps_si128(_mm_moveldup_ps(_mm_castsi128_ps(a))); } template<> OIIO_FORCEINLINE __m128i shuffle_sse<1, 1, 3, 3> (__m128i a) { return _mm_castps_si128(_mm_movehdup_ps(_mm_castsi128_ps(a))); } template<> OIIO_FORCEINLINE __m128i shuffle_sse<0, 1, 0, 1> (__m128i a) { return _mm_castpd_si128(_mm_movedup_pd(_mm_castsi128_pd(a))); } #endif #if OIIO_SIMD_SSE template OIIO_FORCEINLINE __m128 shuffle_sse (__m128 a) { return _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(a), _MM_SHUFFLE(i3, i2, i1, i0))); } #endif #if OIIO_SIMD_SSE >= 3 // SSE3 has intrinsics for a few special cases template<> OIIO_FORCEINLINE __m128 shuffle_sse<0, 0, 2, 2> (__m128 a) { return _mm_moveldup_ps(a); } template<> OIIO_FORCEINLINE __m128 shuffle_sse<1, 1, 3, 3> (__m128 a) { return _mm_movehdup_ps(a); } template<> OIIO_FORCEINLINE __m128 shuffle_sse<0, 1, 0, 1> (__m128 a) { return _mm_castpd_ps(_mm_movedup_pd(_mm_castps_pd(a))); } #endif /// Helper: shuffle/swizzle with constant (templated) indices. /// Example: shuffle<1,1,2,2>(bool4(a,b,c,d)) returns (b,b,c,c) template OIIO_FORCEINLINE bool4 shuffle (const bool4& a) { #if OIIO_SIMD_SSE return shuffle_sse (a.simd()); #else return bool4 (a[i0], a[i1], a[i2], a[i3]); #endif } /// shuffle(a) is the same as shuffle(a) template OIIO_FORCEINLINE bool4 shuffle (const bool4& a) { return shuffle(a); } /// Helper: as rapid as possible extraction of one component, when the /// index is fixed. template OIIO_FORCEINLINE bool extract (const bool4& a) { #if OIIO_SIMD_SSE >= 4 return _mm_extract_epi32(_mm_castps_si128(a.simd()), i); // SSE4.1 only #else return a[i]; #endif } /// Helper: substitute val for a[i] template OIIO_FORCEINLINE bool4 insert (const bool4& a, bool val) { #if OIIO_SIMD_SSE >= 4 int ival = -int(val); return _mm_castsi128_ps (_mm_insert_epi32 (_mm_castps_si128(a), ival, i)); #else bool4 tmp = a; tmp[i] = -int(val); return tmp; #endif } OIIO_FORCEINLINE bool reduce_and (const bool4& v) { #if OIIO_SIMD_AVX return _mm_testc_ps (v, bool4(true)) != 0; #elif OIIO_SIMD_SSE return _mm_movemask_ps(v.simd()) == 0xf; #else SIMD_RETURN_REDUCE (bool, true, r &= (v[i] != 0)); #endif } OIIO_FORCEINLINE bool reduce_or (const bool4& v) { #if OIIO_SIMD_AVX return ! _mm_testz_ps (v, v); #elif OIIO_SIMD_SSE return _mm_movemask_ps(v) != 0; #else SIMD_RETURN_REDUCE (bool, false, r |= (v[i] != 0)); #endif } OIIO_FORCEINLINE bool all (const bool4& v) { return reduce_and(v) == true; } OIIO_FORCEINLINE bool any (const bool4& v) { return reduce_or(v) == true; } OIIO_FORCEINLINE bool none (const bool4& v) { return reduce_or(v) == false; } ////////////////////////////////////////////////////////////////////// // bool8 implementation OIIO_FORCEINLINE int bool8::operator[] (int i) const { DASSERT(i >= 0 && i < elements); #if OIIO_SIMD_AVX return ((_mm256_movemask_ps(m_simd) >> i) & 1) ? -1 : 0; #else return m_val[i]; #endif } OIIO_FORCEINLINE void bool8::setcomp (int i, bool value) { DASSERT(i >= 0 && i < elements); m_val[i] = value ? -1 : 0; } OIIO_FORCEINLINE int& bool8::operator[] (int i) { DASSERT(i >= 0 && i < elements); return m_val[i]; } OIIO_FORCEINLINE std::ostream& operator<< (std::ostream& cout, const bool8& a) { cout << a[0]; for (int i = 1; i < a.elements; ++i) cout << ' ' << a[i]; return cout; } OIIO_FORCEINLINE void bool8::load (bool a) { #if OIIO_SIMD_AVX m_simd = _mm256_castsi256_ps(_mm256_set1_epi32(-int(a))); #else int val = -int(a); SIMD_CONSTRUCT (val); #endif } OIIO_FORCEINLINE void bool8::load (bool a, bool b, bool c, bool d, bool e, bool f, bool g, bool h) { #if OIIO_SIMD_AVX // N.B. -- we need to reverse the order because of our convention // of storing a,b,c,d in the same order in memory. m_simd = _mm256_castsi256_ps(_mm256_set_epi32(-int(h), -int(g), -int(f), -int(e), -int(d), -int(c), -int(b), -int(a))); #else m_val[0] = -int(a); m_val[1] = -int(b); m_val[2] = -int(c); m_val[3] = -int(d); m_val[4] = -int(e); m_val[5] = -int(f); m_val[6] = -int(g); m_val[7] = -int(h); #endif } OIIO_FORCEINLINE bool8::bool8 (bool a, bool b, bool c, bool d, bool e, bool f, bool g, bool h) { load (a, b, c, d, e, f, g, h); } OIIO_FORCEINLINE bool8::bool8 (const bool *a) { load (a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]); } OIIO_FORCEINLINE const bool8& bool8::operator= (bool a) { load(a); return *this; } OIIO_FORCEINLINE const bool8& bool8::operator= (const bool8 & other) { m_simd = other.m_simd; return *this; } OIIO_FORCEINLINE void bool8::clear () { #if OIIO_SIMD_AVX m_simd = _mm256_setzero_ps(); #else *this = false; #endif } OIIO_FORCEINLINE const bool8 bool8::False () { #if OIIO_SIMD_AVX return _mm256_setzero_ps(); #else return false; #endif } OIIO_FORCEINLINE const bool8 bool8::True () { #if OIIO_SIMD_AVX # if OIIO_SIMD_AVX >= 2 && (OIIO_GNUC_VERSION > 50000) // Fastest way to fill with all 1 bits is to cmp any value to itself. __m256i anyval = _mm256_undefined_si256(); return _mm256_castsi256_ps (_mm256_cmpeq_epi8 (anyval, anyval)); # else return _mm256_castsi256_ps (_mm256_set1_epi32 (-1)); # endif #else return true; #endif } OIIO_FORCEINLINE void bool8::store (bool *values) const { SIMD_DO (values[i] = m_val[i] ? true : false); } OIIO_FORCEINLINE void bool8::store (bool *values, int n) const { DASSERT (n >= 0 && n <= elements); for (int i = 0; i < n; ++i) values[i] = m_val[i] ? true : false; } OIIO_FORCEINLINE bool4 bool8::lo () const { #if OIIO_SIMD_AVX return _mm256_castps256_ps128 (simd()); #else return m_4[0]; #endif } OIIO_FORCEINLINE bool4 bool8::hi () const { #if OIIO_SIMD_AVX return _mm256_extractf128_ps (simd(), 1); #else return m_4[1]; #endif } OIIO_FORCEINLINE bool8::bool8 (const bool4& lo, const bool4 &hi) { #if OIIO_SIMD_AVX __m256 r = _mm256_castps128_ps256 (lo); m_simd = _mm256_insertf128_ps (r, hi, 1); // N.B. equivalent, if available: m_simd = _mm256_set_m128 (hi, lo); #else m_4[0] = lo; m_4[1] = hi; #endif } OIIO_FORCEINLINE bool8 operator! (const bool8 & a) { #if OIIO_SIMD_AVX return _mm256_xor_ps (a.simd(), bool8::True()); #else SIMD_RETURN (bool8, a[i] ^ (-1)); #endif } OIIO_FORCEINLINE bool8 operator& (const bool8 & a, const bool8 & b) { #if OIIO_SIMD_AVX return _mm256_and_ps (a.simd(), b.simd()); #else SIMD_RETURN (bool8, a[i] & b[i]); #endif } OIIO_FORCEINLINE bool8 operator| (const bool8 & a, const bool8 & b) { #if OIIO_SIMD_AVX return _mm256_or_ps (a.simd(), b.simd()); #else SIMD_RETURN (bool8, a[i] | b[i]); #endif } OIIO_FORCEINLINE bool8 operator^ (const bool8& a, const bool8& b) { #if OIIO_SIMD_AVX return _mm256_xor_ps (a.simd(), b.simd()); #else SIMD_RETURN (bool8, a[i] ^ b[i]); #endif } OIIO_FORCEINLINE const bool8& operator&= (bool8& a, const bool8 &b) { return a = a & b; } OIIO_FORCEINLINE const bool8& operator|= (bool8& a, const bool8& b) { return a = a | b; } OIIO_FORCEINLINE const bool8& operator^= (bool8& a, const bool8& b) { return a = a ^ b; } OIIO_FORCEINLINE bool8 operator~ (const bool8& a) { #if OIIO_SIMD_AVX // Fastest way to bit-complement in SSE is to xor with 0xffffffff. return _mm256_xor_ps (a.simd(), bool8::True()); #else SIMD_RETURN (bool8, ~a[i]); #endif } OIIO_FORCEINLINE bool8 operator== (const bool8 & a, const bool8 & b) { #if OIIO_SIMD_AVX >= 2 return _mm256_castsi256_ps (_mm256_cmpeq_epi32 (_mm256_castps_si256 (a), _mm256_castps_si256(b))); #elif OIIO_SIMD_AVX return _mm256_cmp_ps (a, b, _CMP_EQ_UQ); #else SIMD_RETURN (bool8, a[i] == b[i] ? -1 : 0); #endif } OIIO_FORCEINLINE bool8 operator!= (const bool8 & a, const bool8 & b) { #if OIIO_SIMD_AVX return _mm256_xor_ps (a, b); #else SIMD_RETURN (bool8, a[i] != b[i] ? -1 : 0); #endif } template OIIO_FORCEINLINE bool8 shuffle (const bool8& a) { #if OIIO_SIMD_AVX >= 2 int8 index (i0, i1, i2, i3, i4, i5, i6, i7); return _mm256_permutevar8x32_ps (a.simd(), index.simd()); #else return bool8 (a[i0], a[i1], a[i2], a[i3], a[i4], a[i5], a[i6], a[i7]); #endif } template OIIO_FORCEINLINE bool8 shuffle (const bool8& a) { return shuffle(a); } template OIIO_FORCEINLINE bool extract (const bool8& a) { #if OIIO_SIMD_AVX && !_WIN32 return _mm256_extract_epi32(_mm256_castps_si256(a.simd()), i); // SSE4.1 only #else return a[i]; #endif } template OIIO_FORCEINLINE bool8 insert (const bool8& a, bool val) { #if OIIO_SIMD_AVX && !_WIN32 int ival = -int(val); return _mm256_castsi256_ps (_mm256_insert_epi32 (_mm256_castps_si256(a.simd()), ival, i)); #else bool8 tmp = a; tmp[i] = -int(val); return tmp; #endif } OIIO_FORCEINLINE bool reduce_and (const bool8& v) { #if OIIO_SIMD_AVX return _mm256_testc_ps (v, bool8(true)) != 0; // return _mm256_movemask_ps(v.simd()) == 0xff; #else SIMD_RETURN_REDUCE (bool, true, r &= bool(v[i])); #endif } OIIO_FORCEINLINE bool reduce_or (const bool8& v) { #if OIIO_SIMD_AVX return ! _mm256_testz_ps (v, v); // FIXME? Not in all immintrin.h ! // return _mm256_movemask_ps(v) != 0; #else SIMD_RETURN_REDUCE (bool, false, r |= bool(v[i])); #endif } OIIO_FORCEINLINE bool all (const bool8& v) { return reduce_and(v) == true; } OIIO_FORCEINLINE bool any (const bool8& v) { return reduce_or(v) == true; } OIIO_FORCEINLINE bool none (const bool8& v) { return reduce_or(v) == false; } ////////////////////////////////////////////////////////////////////// // int4 implementation OIIO_FORCEINLINE const int4 & int4::operator= (const int4& other) { m_simd = other.m_simd; return *this; } OIIO_FORCEINLINE int& int4::operator[] (int i) { DASSERT(i= 4 // Trickery: load one double worth of bits = 4 uint16's! simd_t a = _mm_castpd_si128 (_mm_load_sd ((const double *)values)); m_simd = _mm_cvtepu16_epi32 (a); #else SIMD_CONSTRUCT (values[i]); #endif } OIIO_FORCEINLINE void int4::load (const short *values) { #if OIIO_SIMD_SSE >= 4 // Trickery: load one double worth of bits = 4 int16's! simd_t a = _mm_castpd_si128 (_mm_load_sd ((const double *)values)); m_simd = _mm_cvtepi16_epi32 (a); #else SIMD_CONSTRUCT (values[i]); #endif } OIIO_FORCEINLINE void int4::load (const unsigned char *values) { #if OIIO_SIMD_SSE >= 4 // Trickery: load one float worth of bits = 4 uint8's! simd_t a = _mm_castps_si128 (_mm_load_ss ((const float *)values)); m_simd = _mm_cvtepu8_epi32 (a); #else SIMD_CONSTRUCT (values[i]); #endif } OIIO_FORCEINLINE void int4::load (const char *values) { #if OIIO_SIMD_SSE >= 4 // Trickery: load one float worth of bits = 4 uint8's! simd_t a = _mm_castps_si128 (_mm_load_ss ((const float *)values)); m_simd = _mm_cvtepi8_epi32 (a); #else SIMD_CONSTRUCT (values[i]); #endif } OIIO_FORCEINLINE int4::int4 (int a) { load(a); } OIIO_FORCEINLINE int4::int4 (int a, int b) { load(a,a,b,b); } OIIO_FORCEINLINE int4::int4 (int a, int b, int c, int d) { load(a,b,c,d); } // OIIO_FORCEINLINE int4::int4 (int a, int b, int c, int d, // int e, int f, int g, int h) { // load(a,b,c,d,e,f,g,h); // } OIIO_FORCEINLINE int4::int4 (const int *vals) { load (vals); } OIIO_FORCEINLINE int4::int4 (const unsigned short *vals) { load(vals); } OIIO_FORCEINLINE int4::int4 (const short *vals) { load(vals); } OIIO_FORCEINLINE int4::int4 (const unsigned char *vals) { load(vals); } OIIO_FORCEINLINE int4::int4 (const char *vals) { load(vals); } OIIO_FORCEINLINE const int4 & int4::operator= (int a) { load(a); return *this; } OIIO_FORCEINLINE void int4::store (int *values) const { #if OIIO_SIMD_SSE // Use an unaligned store -- it's just as fast when the memory turns // out to be aligned, nearly as fast even when unaligned. Not worth // the headache of using stores that require alignment. _mm_storeu_si128 ((simd_t *)values, m_simd); #else SIMD_DO (values[i] = m_val[i]); #endif } OIIO_FORCEINLINE void int4::clear () { #if OIIO_SIMD_SSE m_simd = _mm_setzero_si128(); #else *this = 0; #endif } OIIO_FORCEINLINE const int4 int4::Zero () { #if OIIO_SIMD_SSE return _mm_setzero_si128(); #else return 0; #endif } OIIO_FORCEINLINE const int4 int4::One () { return int4(1); } OIIO_FORCEINLINE const int4 int4::NegOne () { #if OIIO_SIMD_SSE // Fastest way to fill an __m128 with all 1 bits is to cmpeq_epi8 // any value to itself. # if OIIO_SIMD_AVX && (OIIO_GNUC_VERSION > 50000) __m128i anyval = _mm_undefined_si128(); # else __m128i anyval = _mm_setzero_si128(); # endif return _mm_cmpeq_epi8 (anyval, anyval); #else return int4(-1); #endif } OIIO_FORCEINLINE const int4 int4::Iota (int start, int step) { return int4 (start+0*step, start+1*step, start+2*step, start+3*step); } OIIO_FORCEINLINE int4 operator+ (const int4& a, const int4& b) { #if OIIO_SIMD_SSE return _mm_add_epi32 (a.simd(), b.simd()); #else SIMD_RETURN (int4, a[i] + b[i]); #endif } OIIO_FORCEINLINE const int4& operator+= (int4& a, const int4& b) { return a = a + b; } OIIO_FORCEINLINE int4 operator- (const int4& a) { #if OIIO_SIMD_SSE return _mm_sub_epi32 (_mm_setzero_si128(), a); #else SIMD_RETURN (int4, -a[i]); #endif } OIIO_FORCEINLINE int4 operator- (const int4& a, const int4& b) { #if OIIO_SIMD_SSE return _mm_sub_epi32 (a.simd(), b.simd()); #else SIMD_RETURN (int4, a[i] - b[i]); #endif } OIIO_FORCEINLINE const int4 &operator-= (int4& a, const int4& b) { return a = a - b; } #if OIIO_SIMD_SSE // Shamelessly lifted from Syrah which lifted from Manta which lifted it // from intel.com OIIO_FORCEINLINE __m128i mul_epi32 (__m128i a, __m128i b) { #if OIIO_SIMD_SSE >= 4 /* SSE >= 4.1 */ return _mm_mullo_epi32(a, b); #else // Prior to SSE 4.1, there is no _mm_mullo_epi32 instruction, so we have // to fake it. __m128i t0; __m128i t1; t0 = _mm_mul_epu32 (a, b); t1 = _mm_mul_epu32 (_mm_shuffle_epi32 (a, 0xB1), _mm_shuffle_epi32 (b, 0xB1)); t0 = _mm_shuffle_epi32 (t0, 0xD8); t1 = _mm_shuffle_epi32 (t1, 0xD8); return _mm_unpacklo_epi32 (t0, t1); #endif } #endif OIIO_FORCEINLINE int4 operator* (const int4& a, const int4& b) { #if OIIO_SIMD_SSE return mul_epi32 (a.simd(), b.simd()); #else SIMD_RETURN (int4, a[i] * b[i]); #endif } OIIO_FORCEINLINE const int4& operator*= (int4& a, const int4& b) { return a = a * b; } OIIO_FORCEINLINE const int4& operator*= (int4& a, int b) { return a = a * b; } OIIO_FORCEINLINE int4 operator/ (const int4& a, const int4& b) { // NO INTEGER DIVISION IN SSE! SIMD_RETURN (int4, a[i] / b[i]); } OIIO_FORCEINLINE const int4& operator/= (int4& a, const int4& b) { return a = a / b; } OIIO_FORCEINLINE int4 operator% (const int4& a, const int4& b) { // NO INTEGER MODULUS IN SSE! SIMD_RETURN (int4, a[i] % b[i]); } OIIO_FORCEINLINE const int4& operator%= (int4& a, const int4& b) { return a = a % b; } OIIO_FORCEINLINE int4 operator% (const int4& a, int w) { // NO INTEGER MODULUS in SSE! SIMD_RETURN (int4, a[i] % w); } OIIO_FORCEINLINE const int4& operator%= (int4& a, int b) { return a = a % b; } OIIO_FORCEINLINE int4 operator& (const int4& a, const int4& b) { #if OIIO_SIMD_SSE return _mm_and_si128 (a.simd(), b.simd()); #else SIMD_RETURN (int4, a[i] & b[i]); #endif } OIIO_FORCEINLINE const int4& operator&= (int4& a, const int4& b) { return a = a & b; } OIIO_FORCEINLINE int4 operator| (const int4& a, const int4& b) { #if OIIO_SIMD_SSE return _mm_or_si128 (a.simd(), b.simd()); #else SIMD_RETURN (int4, a[i] | b[i]); #endif } OIIO_FORCEINLINE const int4& operator|= (int4& a, const int4& b) { return a = a | b; } OIIO_FORCEINLINE int4 operator^ (const int4& a, const int4& b) { #if OIIO_SIMD_SSE return _mm_xor_si128 (a.simd(), b.simd()); #else SIMD_RETURN (int4, a[i] ^ b[i]); #endif } OIIO_FORCEINLINE const int4& operator^= (int4& a, const int4& b) { return a = a ^ b; } OIIO_FORCEINLINE int4 operator~ (const int4& a) { #if OIIO_SIMD_SSE return a ^ a.NegOne(); #else SIMD_RETURN (int4, ~a[i]); #endif } OIIO_FORCEINLINE int4 operator<< (const int4& a, unsigned int bits) { #if OIIO_SIMD_SSE return _mm_slli_epi32 (a, bits); #else SIMD_RETURN (int4, a[i] << bits); #endif } OIIO_FORCEINLINE const int4& operator<<= (int4& a, const unsigned int bits) { return a = a << bits; } OIIO_FORCEINLINE int4 operator>> (const int4& a, const unsigned int bits) { #if OIIO_SIMD_SSE return _mm_srai_epi32 (a, bits); #else SIMD_RETURN (int4, a[i] >> bits); #endif } OIIO_FORCEINLINE const int4& operator>>= (int4& a, const unsigned int bits) { return a = a >> bits; } OIIO_FORCEINLINE int4 srl (const int4& a, const unsigned int bits) { #if OIIO_SIMD_SSE return _mm_srli_epi32 (a, bits); #else SIMD_RETURN (int4, int ((unsigned int)(a[i]) >> bits)); #endif } OIIO_FORCEINLINE bool4 operator== (const int4& a, const int4& b) { #if OIIO_SIMD_SSE return _mm_castsi128_ps(_mm_cmpeq_epi32 (a, b)); #else SIMD_RETURN (bool4, a[i] == b[i] ? -1 : 0); #endif } OIIO_FORCEINLINE bool4 operator!= (const int4& a, const int4& b) { return ! (a == b); } OIIO_FORCEINLINE bool4 operator> (const int4& a, const int4& b) { #if OIIO_SIMD_SSE return _mm_castsi128_ps(_mm_cmpgt_epi32 (a, b)); #else SIMD_RETURN (bool4, a[i] > b[i] ? -1 : 0); #endif } OIIO_FORCEINLINE bool4 operator< (const int4& a, const int4& b) { #if OIIO_SIMD_SSE return _mm_castsi128_ps(_mm_cmplt_epi32 (a, b)); #else SIMD_RETURN (bool4, a[i] < b[i] ? -1 : 0); #endif } OIIO_FORCEINLINE bool4 operator>= (const int4& a, const int4& b) { return (b < a) | (a == b); } OIIO_FORCEINLINE bool4 operator<= (const int4& a, const int4& b) { return (b > a) | (a == b); } inline std::ostream& operator<< (std::ostream& cout, const int4& val) { cout << val[0]; for (int i = 1; i < val.elements; ++i) cout << ' ' << val[i]; return cout; } // FIXME(SSE,AVX): is there a faster way to do a partial store? 512! OIIO_FORCEINLINE void int4::store (int *values, int n) const { DASSERT (n >= 0 && n <= elements); #if defined(OIIO_SIMD) // For full SIMD, there is a speed advantage to storing all components. if (n == elements) store (values); else #endif for (int i = 0; i < n; ++i) values[i] = m_val[i]; } // FIXME(SSE,AVX): is there a faster way to do a partial store? 512! OIIO_FORCEINLINE void int4::store (unsigned short *values) const { #if OIIO_SIMD_SSE // Expressed as half-words and considering little endianness, we // currently have xAxBxCxD (the 'x' means don't care). int4 clamped = m_val & int4(0xffff); // A0B0C0D0 int4 low = _mm_shufflelo_epi16 (clamped, (0<<0) | (2<<2) | (1<<4) | (1<<6)); // low = AB00xxxx int4 high = _mm_shufflehi_epi16 (clamped, (1<<0) | (1<<2) | (0<<4) | (2<<6)); // high = xxxx00CD int4 highswapped = shuffle_sse<2,3,0,1>(high); // 00CDxxxx int4 result = low | highswapped; // ABCDxxxx _mm_storel_pd ((double *)values, _mm_castsi128_pd(result)); // At this point, values[] should hold A,B,C,D #else SIMD_DO (values[i] = m_val[i]); #endif } OIIO_FORCEINLINE void int4::store (unsigned char *values) const { #if OIIO_SIMD_SSE // Expressed as bytes and considering little endianness, we // currently have xAxBxCxD (the 'x' means don't care). int4 clamped = m_val & int4(0xff); // A000 B000 C000 D000 int4 swapped = shuffle_sse<1,0,3,2>(clamped); // B000 A000 D000 C000 int4 shifted = swapped << 8; // 0B00 0A00 0D00 0C00 int4 merged = clamped | shifted; // AB00 xxxx CD00 xxxx int4 merged2 = shuffle_sse<2,2,2,2>(merged); // CD00 ... int4 shifted2 = merged2 << 16; // 00CD ... int4 result = merged | shifted2; // ABCD ... *(int*)values = result[0]; //extract<0>(result); // At this point, values[] should hold A,B,C,D #else SIMD_DO (values[i] = m_val[i]); #endif } template OIIO_FORCEINLINE int4 shuffle (const int4& a) { #if OIIO_SIMD_SSE return shuffle_sse (__m128i(a)); #else return int4(a[i0], a[i1], a[i2], a[i3]); #endif } template OIIO_FORCEINLINE int4 shuffle (const int4& a) { return shuffle(a); } template OIIO_FORCEINLINE int extract (const int4& v) { #if OIIO_SIMD_SSE >= 4 return _mm_extract_epi32(v.simd(), i); // SSE4.1 only #else return v[i]; #endif } #if OIIO_SIMD_SSE template<> OIIO_FORCEINLINE int extract<0> (const int4& v) { return _mm_cvtsi128_si32(v.simd()); } #endif template OIIO_FORCEINLINE int4 insert (const int4& a, int val) { #if OIIO_SIMD_SSE >= 4 return _mm_insert_epi32 (a.simd(), val, i); #else int4 tmp = a; tmp[i] = val; return tmp; #endif } OIIO_FORCEINLINE int int4::x () const { return extract<0>(*this); } OIIO_FORCEINLINE int int4::y () const { return extract<1>(*this); } OIIO_FORCEINLINE int int4::z () const { return extract<2>(*this); } OIIO_FORCEINLINE int int4::w () const { return extract<3>(*this); } OIIO_FORCEINLINE void int4::set_x (int val) { *this = insert<0>(*this, val); } OIIO_FORCEINLINE void int4::set_y (int val) { *this = insert<1>(*this, val); } OIIO_FORCEINLINE void int4::set_z (int val) { *this = insert<2>(*this, val); } OIIO_FORCEINLINE void int4::set_w (int val) { *this = insert<3>(*this, val); } OIIO_FORCEINLINE int4 bitcast_to_int (const bool4& x) { #if OIIO_SIMD_SSE return _mm_castps_si128 (x.simd()); #else return *(int4 *)&x; #endif } // Old names: inline int4 bitcast_to_int4 (const bool4& x) { return bitcast_to_int(x); } OIIO_FORCEINLINE int4 vreduce_add (const int4& v) { #if OIIO_SIMD_SSE >= 3 // People seem to agree that SSE3 does add reduction best with 2 // horizontal adds. // suppose v = (a, b, c, d) simd::int4 ab_cd = _mm_hadd_epi32 (v.simd(), v.simd()); // ab_cd = (a+b, c+d, a+b, c+d) simd::int4 abcd = _mm_hadd_epi32 (ab_cd.simd(), ab_cd.simd()); // all abcd elements are a+b+c+d, return an element as fast as possible return abcd; #elif OIIO_SIMD_SSE >= 2 // I think this is the best we can do for SSE2, and I'm still not sure // it's faster than the default scalar operation. But anyway... // suppose v = (a, b, c, d) int4 ab_ab_cd_cd = shuffle<1,0,3,2>(v) + v; // ab_ab_cd_cd = (b,a,d,c) + (a,b,c,d) = (a+b,a+b,c+d,c+d) int4 cd_cd_ab_ab = shuffle<2,3,0,1>(ab_ab_cd_cd); // cd_cd_ab_ab = (c+d,c+d,a+b,a+b) int4 abcd = ab_ab_cd_cd + cd_cd_ab_ab; // a+b+c+d in all components return abcd; #else SIMD_RETURN_REDUCE (int4, 0, r += v[i]); #endif } OIIO_FORCEINLINE int reduce_add (const int4& v) { #if OIIO_SIMD_SSE return extract<0> (vreduce_add(v)); #else SIMD_RETURN_REDUCE (int, 0, r += v[i]); #endif } OIIO_FORCEINLINE int reduce_and (const int4& v) { #if OIIO_SIMD_SSE int4 ab = v & shuffle<1,1,3,3>(v); // ab bb cd dd int4 abcd = ab & shuffle<2>(ab); return extract<0>(abcd); #else SIMD_RETURN_REDUCE (int, -1, r &= v[i]); #endif } OIIO_FORCEINLINE int reduce_or (const int4& v) { #if OIIO_SIMD_SSE int4 ab = v | shuffle<1,1,3,3>(v); // ab bb cd dd int4 abcd = ab | shuffle<2>(ab); return extract<0>(abcd); #else SIMD_RETURN_REDUCE (int, 0, r |= v[i]); #endif } OIIO_FORCEINLINE int4 blend (const int4& a, const int4& b, const bool4& mask) { #if OIIO_SIMD_SSE >= 4 /* SSE >= 4.1 */ return _mm_castps_si128 (_mm_blendv_ps (_mm_castsi128_ps(a.simd()), _mm_castsi128_ps(b.simd()), mask)); #elif OIIO_SIMD_SSE return _mm_or_si128 (_mm_and_si128(_mm_castps_si128(mask.simd()), b.simd()), _mm_andnot_si128(_mm_castps_si128(mask.simd()), a.simd())); #else SIMD_RETURN (int4, mask[i] ? b[i] : a[i]); #endif } OIIO_FORCEINLINE int4 blend0 (const int4& a, const bool4& mask) { #if OIIO_SIMD_SSE return _mm_and_si128(_mm_castps_si128(mask), a.simd()); #else SIMD_RETURN (int4, mask[i] ? a[i] : 0.0f); #endif } OIIO_FORCEINLINE int4 blend0not (const int4& a, const bool4& mask) { #if OIIO_SIMD_SSE return _mm_andnot_si128(_mm_castps_si128(mask), a.simd()); #else SIMD_RETURN (int4, mask[i] ? 0.0f : a[i]); #endif } OIIO_FORCEINLINE int4 select (const bool4& mask, const int4& a, const int4& b) { return blend (b, a, mask); } OIIO_FORCEINLINE int4 abs (const int4& a) { #if OIIO_SIMD_SSE >= 3 return _mm_abs_epi32(a.simd()); #else SIMD_RETURN (int4, std::abs(a[i])); #endif } OIIO_FORCEINLINE int4 min (const int4& a, const int4& b) { #if OIIO_SIMD_SSE >= 4 /* SSE >= 4.1 */ return _mm_min_epi32 (a, b); #else SIMD_RETURN (int4, std::min(a[i], b[i])); #endif } OIIO_FORCEINLINE int4 max (const int4& a, const int4& b) { #if OIIO_SIMD_SSE >= 4 /* SSE >= 4.1 */ return _mm_max_epi32 (a, b); #else SIMD_RETURN (int4, std::max(a[i], b[i])); #endif } OIIO_FORCEINLINE int4 rotl32 (const int4& x, const unsigned int k) { return (x< 0 && n <= 4) { int4 l; l.load (values, n); m_simd = int8(l, int4::Zero()); } else if (n > 4 && n <= 8) { int4 h; h.load (values+4, n-4); m_simd = int8(int4(values), h); } else clear(); #else for (int i = 0; i < n; ++i) m_val[i] = values[i]; for (int i = n; i < elements; ++i) m_val[i] = 0; #endif } // FIXME(AVX): fast load from unsigned short, short, unsigned char, char OIIO_FORCEINLINE void int8::load (const short *values) { SIMD_CONSTRUCT (values[i]); } OIIO_FORCEINLINE void int8::load (const unsigned short *values) { SIMD_CONSTRUCT (values[i]); } OIIO_FORCEINLINE void int8::load (const char *values) { SIMD_CONSTRUCT (values[i]); } OIIO_FORCEINLINE void int8::load (const unsigned char *values) { SIMD_CONSTRUCT (values[i]); } OIIO_FORCEINLINE int8::int8 (int a) { load(a); } // OIIO_FORCEINLINE int8::int8 (int a, int b, int c, int d) { load(a,b,c,d); } OIIO_FORCEINLINE int8::int8 (int a, int b, int c, int d, int e, int f, int g, int h) { load(a,b,c,d,e,f,g,h); } OIIO_FORCEINLINE int8::int8 (const int *vals) { load (vals); } OIIO_FORCEINLINE int8::int8 (const unsigned short *vals) { load(vals); } OIIO_FORCEINLINE int8::int8 (const short *vals) { load(vals); } OIIO_FORCEINLINE int8::int8 (const unsigned char *vals) { load(vals); } OIIO_FORCEINLINE int8::int8 (const char *vals) { load(vals); } OIIO_FORCEINLINE const int8 & int8::operator= (int a) { load(a); return *this; } OIIO_FORCEINLINE void int8::store (int *values) const { #if OIIO_SIMD_AVX // Use an unaligned store -- it's just as fast when the memory turns // out to be aligned, nearly as fast even when unaligned. Not worth // the headache of using stores that require alignment. _mm256_storeu_si256 ((simd_t *)values, m_simd); #else SIMD_DO (values[i] = m_val[i]); #endif } OIIO_FORCEINLINE void int8::clear () { #if OIIO_SIMD_AVX m_simd = _mm256_setzero_si256(); #else *this = 0; #endif } OIIO_FORCEINLINE const int8 int8::Zero () { #if OIIO_SIMD_AVX return _mm256_setzero_si256(); #else return 0; #endif } OIIO_FORCEINLINE const int8 int8::One () { return int8(1); } OIIO_FORCEINLINE const int8 int8::NegOne () { return int8(-1); } OIIO_FORCEINLINE const int8 int8::Iota (int start, int step) { return int8 (start+0*step, start+1*step, start+2*step, start+3*step, start+4*step, start+5*step, start+6*step, start+7*step); } OIIO_FORCEINLINE int4 int8::lo () const { #if OIIO_SIMD_AVX return _mm256_castsi256_si128 (simd()); #else return m_4[0]; #endif } OIIO_FORCEINLINE int4 int8::hi () const { #if OIIO_SIMD_AVX return _mm256_extractf128_si256 (simd(), 1); #else return m_4[1]; #endif } OIIO_FORCEINLINE int8::int8 (const int4& lo, const int4 &hi) { #if OIIO_SIMD_AVX __m256i r = _mm256_castsi128_si256 (lo); m_simd = _mm256_insertf128_si256 (r, hi, 1); // N.B. equivalent, if available: m_simd = _mm256_set_m128i (hi, lo); #else m_4[0] = lo; m_4[1] = hi; #endif } OIIO_FORCEINLINE int8 operator+ (const int8& a, const int8& b) { #if OIIO_SIMD_AVX >= 2 return _mm256_add_epi32 (a.simd(), b.simd()); #else SIMD_RETURN (int8, a[i] + b[i]); #endif } OIIO_FORCEINLINE const int8& operator+= (int8& a, const int8& b) { return a = a + b; } OIIO_FORCEINLINE int8 operator- (const int8& a) { #if OIIO_SIMD_AVX >= 2 return _mm256_sub_epi32 (_mm256_setzero_si256(), a); #else SIMD_RETURN (int8, -a[i]); #endif } OIIO_FORCEINLINE int8 operator- (const int8& a, const int8& b) { #if OIIO_SIMD_AVX >= 2 return _mm256_sub_epi32 (a.simd(), b.simd()); #else SIMD_RETURN (int8, a[i] - b[i]); #endif } OIIO_FORCEINLINE const int8 &operator-= (int8& a, const int8& b) { return a = a - b; } OIIO_FORCEINLINE int8 operator* (const int8& a, const int8& b) { #if OIIO_SIMD_AVX >= 2 return _mm256_mullo_epi32 (a.simd(), b.simd()); #else SIMD_RETURN (int8, a[i] * b[i]); #endif } OIIO_FORCEINLINE const int8& operator*= (int8& a, const int8& b) { return a = a * b; } OIIO_FORCEINLINE const int8& operator*= (int8& a, int b) { return a = a * b; } OIIO_FORCEINLINE int8 operator/ (const int8& a, const int8& b) { // NO INTEGER DIVISION IN SSE or AVX! SIMD_RETURN (int8, a[i] / b[i]); } OIIO_FORCEINLINE const int8& operator/= (int8& a, const int8& b) { return a = a / b; } OIIO_FORCEINLINE int8 operator% (const int8& a, const int8& b) { // NO INTEGER MODULUS IN SSE or AVX! SIMD_RETURN (int8, a[i] % b[i]); } OIIO_FORCEINLINE const int8& operator%= (int8& a, const int8& b) { return a = a % b; } OIIO_FORCEINLINE int8 operator% (const int8& a, int w) { // NO INTEGER MODULUS in SSE or AVX! SIMD_RETURN (int8, a[i] % w); } OIIO_FORCEINLINE const int8& operator%= (int8& a, int b) { return a = a % b; } OIIO_FORCEINLINE int8 operator& (const int8& a, const int8& b) { #if OIIO_SIMD_AVX >= 2 return _mm256_and_si256 (a.simd(), b.simd()); #else SIMD_RETURN (int8, a[i] & b[i]); #endif } OIIO_FORCEINLINE const int8& operator&= (int8& a, const int8& b) { return a = a & b; } OIIO_FORCEINLINE int8 operator| (const int8& a, const int8& b) { #if OIIO_SIMD_AVX >= 2 return _mm256_or_si256 (a.simd(), b.simd()); #else SIMD_RETURN (int8, a[i] | b[i]); #endif } OIIO_FORCEINLINE const int8& operator|= (int8& a, const int8& b) { return a = a | b; } OIIO_FORCEINLINE int8 operator^ (const int8& a, const int8& b) { #if OIIO_SIMD_AVX >= 2 return _mm256_xor_si256 (a.simd(), b.simd()); #else SIMD_RETURN (int8, a[i] ^ b[i]); #endif } OIIO_FORCEINLINE const int8& operator^= (int8& a, const int8& b) { return a = a ^ b; } OIIO_FORCEINLINE int8 operator~ (const int8& a) { #if OIIO_SIMD_AVX >= 2 return a ^ a.NegOne(); #else SIMD_RETURN (int8, ~a[i]); #endif } OIIO_FORCEINLINE int8 operator<< (const int8& a, unsigned int bits) { #if OIIO_SIMD_AVX >= 2 return _mm256_slli_epi32 (a, bits); #elif OIIO_SIMD_SSE return int8 (a.lo() << bits, a.hi() << bits); #else SIMD_RETURN (int8, a[i] << bits); #endif } OIIO_FORCEINLINE const int8& operator<<= (int8& a, const unsigned int bits) { return a = a << bits; } OIIO_FORCEINLINE int8 operator>> (const int8& a, const unsigned int bits) { #if OIIO_SIMD_AVX >= 2 return _mm256_srai_epi32 (a, bits); #elif OIIO_SIMD_SSE return int8 (a.lo() >> bits, a.hi() >> bits); #else SIMD_RETURN (int8, a[i] >> bits); #endif } OIIO_FORCEINLINE const int8& operator>>= (int8& a, const unsigned int bits) { return a = a >> bits; } OIIO_FORCEINLINE int8 srl (const int8& a, const unsigned int bits) { #if OIIO_SIMD_AVX >= 2 return _mm256_srli_epi32 (a, bits); #else SIMD_RETURN (int8, int ((unsigned int)(a[i]) >> bits)); #endif } OIIO_FORCEINLINE bool8 operator== (const int8& a, const int8& b) { #if OIIO_SIMD_AVX >= 2 return _mm256_castsi256_ps(_mm256_cmpeq_epi32 (a.m_simd, b.m_simd)); #elif OIIO_SIMD_SSE /* Fall back to 4-wide */ return bool8 (a.lo() == b.lo(), a.hi() == b.hi()); #else SIMD_RETURN (bool8, a[i] == b[i] ? -1 : 0); #endif } OIIO_FORCEINLINE bool8 operator!= (const int8& a, const int8& b) { return ! (a == b); } OIIO_FORCEINLINE bool8 operator> (const int8& a, const int8& b) { #if OIIO_SIMD_AVX >= 2 return _mm256_castsi256_ps(_mm256_cmpgt_epi32 (a, b)); #elif OIIO_SIMD_SSE /* Fall back to 4-wide */ return bool8 (a.lo() > b.lo(), a.hi() > b.hi()); #else SIMD_RETURN (bool8, a[i] > b[i] ? -1 : 0); #endif } OIIO_FORCEINLINE bool8 operator< (const int8& a, const int8& b) { #if OIIO_SIMD_AVX >= 2 // No lt or lte! return (b > a); #elif OIIO_SIMD_SSE /* Fall back to 4-wide */ return bool8 (a.lo() < b.lo(), a.hi() < b.hi()); #else SIMD_RETURN (bool8, a[i] < b[i] ? -1 : 0); #endif } OIIO_FORCEINLINE bool8 operator>= (const int8& a, const int8& b) { return (a > b) | (a == b); } OIIO_FORCEINLINE bool8 operator<= (const int8& a, const int8& b) { return (b > a) | (a == b); } inline std::ostream& operator<< (std::ostream& cout, const int8& val) { cout << val[0]; for (int i = 1; i < val.elements; ++i) cout << ' ' << val[i]; return cout; } OIIO_FORCEINLINE void int8::store (int *values, int n) const { DASSERT (n >= 0 && n <= elements); // FIXME: is this faster with AVX masked stores? #if OIIO_SIMD_SSE if (n <= 4) { lo().store (values, n); } else if (n <= 8) { lo().store (values); hi().store (values+4, n-4); } #endif for (int i = 0; i < n; ++i) values[i] = m_val[i]; } // FIXME(AVX): fast int8 store to unsigned short, unsigned char OIIO_FORCEINLINE void int8::store (unsigned short *values) const { #if 0 && OIIO_SIMD_AVX >= 2 // FIXME -- try to determine if this is faster: lo().store (values); hi().store (values+4); #else SIMD_DO (values[i] = m_val[i]); #endif } OIIO_FORCEINLINE void int8::store (unsigned char *values) const { #if OIIO_SIMD_SSE lo().store (values); hi().store (values+4); #else SIMD_DO (values[i] = m_val[i]); #endif } template OIIO_FORCEINLINE int8 shuffle (const int8& a) { #if OIIO_SIMD_AVX >= 2 int8 index (i0, i1, i2, i3, i4, i5, i6, i7); return _mm256_castps_si256 (_mm256_permutevar8x32_ps (_mm256_castsi256_ps(a.simd()), index.simd())); #else return int8 (a[i0], a[i1], a[i2], a[i3], a[i4], a[i5], a[i6], a[i7]); #endif } template OIIO_FORCEINLINE int8 shuffle (const int8& a) { return shuffle(a); } template OIIO_FORCEINLINE int extract (const int8& v) { #if OIIO_SIMD_AVX && !_WIN32 return _mm256_extract_epi32(v.simd(), i); #else return v[i]; #endif } template OIIO_FORCEINLINE int8 insert (const int8& a, int val) { #if OIIO_SIMD_AVX && !_WIN32 return _mm256_insert_epi32 (a.simd(), val, i); #else int8 tmp = a; tmp[i] = val; return tmp; #endif } OIIO_FORCEINLINE int int8::x () const { return extract<0>(*this); } OIIO_FORCEINLINE int int8::y () const { return extract<1>(*this); } OIIO_FORCEINLINE int int8::z () const { return extract<2>(*this); } OIIO_FORCEINLINE int int8::w () const { return extract<3>(*this); } OIIO_FORCEINLINE void int8::set_x (int val) { *this = insert<0>(*this, val); } OIIO_FORCEINLINE void int8::set_y (int val) { *this = insert<1>(*this, val); } OIIO_FORCEINLINE void int8::set_z (int val) { *this = insert<2>(*this, val); } OIIO_FORCEINLINE void int8::set_w (int val) { *this = insert<3>(*this, val); } OIIO_FORCEINLINE int8 bitcast_to_int (const bool8& x) { #if OIIO_SIMD_AVX return _mm256_castps_si256 (x.simd()); #else return *(int8 *)&x; #endif } OIIO_FORCEINLINE int8 vreduce_add (const int8& v) { #if OIIO_SIMD_AVX >= 2 // From Syrah: int8 ab_cd_0_0_ef_gh_0_0 = _mm256_hadd_epi32(v.simd(), _mm256_setzero_si256()); int8 abcd_0_0_0_efgh_0_0_0 = _mm256_hadd_epi32(ab_cd_0_0_ef_gh_0_0, _mm256_setzero_si256()); // get efgh in the 0-idx slot int8 efgh = shuffle<4>(abcd_0_0_0_efgh_0_0_0); int8 final_sum = abcd_0_0_0_efgh_0_0_0 + efgh; return shuffle<0>(final_sum); #elif OIIO_SIMD_SSE int4 hadd4 = vreduce_add(v.lo()) + vreduce_add(v.hi()); return int8(hadd4, hadd4); #else SIMD_RETURN_REDUCE (int8, 0, r += v[i]); #endif } OIIO_FORCEINLINE int reduce_add (const int8& v) { #if OIIO_SIMD_SSE return extract<0> (vreduce_add(v)); #else SIMD_RETURN_REDUCE (int, 0, r += v[i]); #endif } OIIO_FORCEINLINE int reduce_and (const int8& v) { #if OIIO_SSE_AVX >= 2 int8 ab = v & shuffle<1,1,3,3,5,5,7,7>(v); // ab bb cd dd ef ff gh hh int8 abcd = ab & shuffle<2,2,2,2,6,6,6,6>(ab); // abcd x x x efgh x x x int8 abcdefgh = abcd & shuffle<4>(abcdefgh); // abcdefgh x x x x x x x return extract<0> (abcdefgh); #else // AVX 1.0 or less -- use SSE return reduce_and(v.lo() & v.hi()); #endif } OIIO_FORCEINLINE int reduce_or (const int8& v) { #if OIIO_SSE_AVX >= 2 int8 ab = v | shuffle<1,1,3,3,5,5,7,7>(v); // ab bb cd dd ef ff gh hh int8 abcd = ab | shuffle<2,2,2,2,6,6,6,6>(ab); // abcd x x x efgh x x x int8 abcdefgh = abcd | shuffle<4>(abcdefgh); // abcdefgh x x x x x x x return extract<0> (abcdefgh); #else // AVX 1.0 or less -- use SSE return reduce_or(v.lo() | v.hi()); #endif } OIIO_FORCEINLINE int8 blend (const int8& a, const int8& b, const bool8& mask) { #if OIIO_SIMD_AVX return _mm256_castps_si256 (_mm256_blendv_ps (_mm256_castsi256_ps(a.simd()), _mm256_castsi256_ps(b.simd()), mask)); #else SIMD_RETURN (int8, mask[i] ? b[i] : a[i]); #endif } OIIO_FORCEINLINE int8 blend0 (const int8& a, const bool8& mask) { #if OIIO_SIMD_AVX return _mm256_castps_si256(_mm256_and_ps(_mm256_castsi256_ps(a.simd()), mask)); #else SIMD_RETURN (int8, mask[i] ? a[i] : 0.0f); #endif } OIIO_FORCEINLINE int8 blend0not (const int8& a, const bool8& mask) { #if OIIO_SIMD_AVX return _mm256_castps_si256 (_mm256_andnot_ps (mask.simd(), _mm256_castsi256_ps(a.simd()))); #else SIMD_RETURN (int8, mask[i] ? 0.0f : a[i]); #endif } OIIO_FORCEINLINE int8 select (const bool8& mask, const int8& a, const int8& b) { return blend (b, a, mask); } OIIO_FORCEINLINE int8 abs (const int8& a) { #if OIIO_SIMD_AVX >= 2 return _mm256_abs_epi32(a.simd()); #else SIMD_RETURN (int8, std::abs(a[i])); #endif } OIIO_FORCEINLINE int8 min (const int8& a, const int8& b) { #if OIIO_SIMD_AVX >= 2 return _mm256_min_epi32 (a, b); #else SIMD_RETURN (int8, std::min(a[i], b[i])); #endif } OIIO_FORCEINLINE int8 max (const int8& a, const int8& b) { #if OIIO_SIMD_AVX >= 2 return _mm256_max_epi32 (a, b); #else SIMD_RETURN (int8, std::max(a[i], b[i])); #endif } OIIO_FORCEINLINE int8 rotl32 (const int8& x, const unsigned int k) { return (x<= 2 return _mm256_andnot_si256 (a.simd(), b.simd()); #else SIMD_RETURN (int8, ~(a[i]) & b[i]); #endif } // Implementation had to be after the definition of int8::Zero. OIIO_FORCEINLINE bool8::bool8 (const int8& ival) { m_simd = (ival != int8::Zero()); } ////////////////////////////////////////////////////////////////////// // float4 implementation OIIO_FORCEINLINE float4::float4 (const int4& ival) { #if OIIO_SIMD_SSE m_simd = _mm_cvtepi32_ps (ival.simd()); #else SIMD_CONSTRUCT (float(ival[i])); #endif } OIIO_FORCEINLINE const float4 float4::Zero () { #if OIIO_SIMD_SSE return _mm_setzero_ps(); #else return float4(0.0f); #endif } OIIO_FORCEINLINE const float4 float4::One () { return float4(1.0f); } OIIO_FORCEINLINE const float4 float4::Iota (float start, float step) { return float4 (start+0.0f*step, start+1.0f*step, start+2.0f*step, start+3.0f*step); } /// Set all components to 0.0 OIIO_FORCEINLINE void float4::clear () { #if OIIO_SIMD_SSE m_simd = _mm_setzero_ps(); #else load (0.0f); #endif } #if defined(ILMBASE_VERSION_MAJOR) && ILMBASE_VERSION_MAJOR >= 2 OIIO_FORCEINLINE const float4 & float4::operator= (const Imath::V4f &v) { load ((const float *)&v); return *this; } #endif OIIO_FORCEINLINE const float4 & float4::operator= (const Imath::V3f &v) { load (v[0], v[1], v[2], 0.0f); return *this; } OIIO_FORCEINLINE float& float4::operator[] (int i) { DASSERT(i= 2 m_simd = _mm_cvtepi32_ps (int4(values).simd()); // You might guess that the following is faster, but it's NOT: // NO! m_simd = _mm_cvtpu16_ps (*(__m64*)values); #else SIMD_CONSTRUCT (values[i]); #endif } OIIO_FORCEINLINE void float4::load (const short *values) { #if defined(OIIO_SIMD_SSE) && OIIO_SIMD_SSE >= 2 m_simd = _mm_cvtepi32_ps (int4(values).simd()); #else SIMD_CONSTRUCT (values[i]); #endif } OIIO_FORCEINLINE void float4::load (const unsigned char *values) { #if defined(OIIO_SIMD_SSE) && OIIO_SIMD_SSE >= 2 m_simd = _mm_cvtepi32_ps (int4(values).simd()); #else SIMD_CONSTRUCT (values[i]); #endif } /// Load from an array of 4 char values, convert to float OIIO_FORCEINLINE void float4::load (const char *values) { #if defined(OIIO_SIMD_SSE) && OIIO_SIMD_SSE >= 2 m_simd = _mm_cvtepi32_ps (int4(values).simd()); #else SIMD_CONSTRUCT (values[i]); #endif } #ifdef _HALF_H_ OIIO_FORCEINLINE void float4::load (const half *values) { #if defined(__F16C__) && defined(OIIO_SIMD_SSE) /* Enabled 16 bit float instructions! */ __m128i a = _mm_castpd_si128 (_mm_load_sd ((const double *)values)); m_simd = _mm_cvtph_ps (a); #elif defined(OIIO_SIMD_SSE) && OIIO_SIMD_SSE >= 2 // SSE half-to-float by Fabian "ryg" Giesen. Public domain. // https://gist.github.com/rygorous/2144712 int4 h ((const unsigned short *)values); # define CONSTI(name) *(const __m128i *)&name # define CONSTF(name) *(const __m128 *)&name OIIO_SIMD_UINT4_CONST(mask_nosign, 0x7fff); OIIO_SIMD_UINT4_CONST(magic, (254 - 15) << 23); OIIO_SIMD_UINT4_CONST(was_infnan, 0x7bff); OIIO_SIMD_UINT4_CONST(exp_infnan, 255 << 23); __m128i mnosign = CONSTI(mask_nosign); __m128i expmant = _mm_and_si128(mnosign, h); __m128i justsign = _mm_xor_si128(h, expmant); __m128i expmant2 = expmant; // copy (just here for counting purposes) __m128i shifted = _mm_slli_epi32(expmant, 13); __m128 scaled = _mm_mul_ps(_mm_castsi128_ps(shifted), *(const __m128 *)&magic); __m128i b_wasinfnan = _mm_cmpgt_epi32(expmant2, CONSTI(was_infnan)); __m128i sign = _mm_slli_epi32(justsign, 16); __m128 infnanexp = _mm_and_ps(_mm_castsi128_ps(b_wasinfnan), CONSTF(exp_infnan)); __m128 sign_inf = _mm_or_ps(_mm_castsi128_ps(sign), infnanexp); __m128 final = _mm_or_ps(scaled, sign_inf); // ~11 SSE2 ops. m_simd = final; # undef CONSTI # undef CONSTF #else /* No SIMD defined: */ SIMD_CONSTRUCT (values[i]); #endif } #endif /* _HALF_H_ */ OIIO_FORCEINLINE void float4::store (float *values) const { #if OIIO_SIMD_SSE // Use an unaligned store -- it's just as fast when the memory turns // out to be aligned, nearly as fast even when unaligned. Not worth // the headache of using stores that require alignment. _mm_storeu_ps (values, m_simd); #elif defined(OIIO_SIMD_NEON) vst1q_f32 (values, m_simd); #else SIMD_DO (values[i] = m_val[i]); #endif } OIIO_FORCEINLINE void float4::store (float *values, int n) const { DASSERT (n >= 0 && n <= 4); #if OIIO_SIMD_SSE switch (n) { case 1: _mm_store_ss (values, m_simd); break; case 2: // Trickery: store two floats as a double worth of bits _mm_store_sd ((double*)values, _mm_castps_pd(m_simd)); break; case 3: values[0] = m_val[0]; values[1] = m_val[1]; values[2] = m_val[2]; // This looks wasteful, but benchmarks show that it's the // fastest way to store 3 values, in benchmarks was faster than // this, below: // _mm_store_sd ((double*)values, _mm_castps_pd(m_simd)); // _mm_store_ss (values + 2, _mm_movehl_ps(m_simd,m_simd)); break; case 4: store (values); break; default: break; } #elif defined(OIIO_SIMD_NEON) switch (n) { case 1: vst1q_lane_f32 (values, m_simd, 0); break; case 2: vst1q_lane_f32 (values++, m_simd, 0); vst1q_lane_f32 (values, m_simd, 1); break; case 3: vst1q_lane_f32 (values++, m_simd, 0); vst1q_lane_f32 (values++, m_simd, 1); vst1q_lane_f32 (values, m_simd, 2); break; case 4: vst1q_f32 (values, m_simd); break; default: break; } #else for (int i = 0; i < n; ++i) values[i] = m_val[i]; #endif } #ifdef _HALF_H_ OIIO_FORCEINLINE void float4::store (half *values) const { #if defined(__F16C__) && defined(OIIO_SIMD_SSE) __m128i h = _mm_cvtps_ph (m_simd, (_MM_FROUND_TO_NEAREST_INT |_MM_FROUND_NO_EXC)); _mm_store_sd ((double *)values, _mm_castsi128_pd(h)); #else SIMD_DO (values[i] = m_val[i]); #endif } #endif OIIO_FORCEINLINE float4 operator+ (const float4& a, const float4& b) { #if OIIO_SIMD_SSE return _mm_add_ps (a.m_simd, b.m_simd); #elif defined(OIIO_SIMD_NEON) return vaddq_f32 (a.m_simd, b.m_simd); #else SIMD_RETURN (float4, a[i] + b[i]); #endif } OIIO_FORCEINLINE const float4 & float4::operator+= (const float4& a) { #if OIIO_SIMD_SSE m_simd = _mm_add_ps (m_simd, a.m_simd); #elif defined(OIIO_SIMD_NEON) m_simd = vaddq_f32 (m_simd, a.m_simd); #else SIMD_DO (m_val[i] += a[i]); #endif return *this; } OIIO_FORCEINLINE float4 float4::operator- () const { #if OIIO_SIMD_SSE return _mm_sub_ps (_mm_setzero_ps(), m_simd); #elif defined(OIIO_SIMD_NEON) return vsubq_f32 (Zero(), m_simd); #else SIMD_RETURN (float4, -m_val[i]); #endif } OIIO_FORCEINLINE float4 operator- (const float4& a, const float4& b) { #if OIIO_SIMD_SSE return _mm_sub_ps (a.m_simd, b.m_simd); #elif defined(OIIO_SIMD_NEON) return vsubq_f32 (a.m_simd, b.m_simd); #else SIMD_RETURN (float4, a[i] - b[i]); #endif } OIIO_FORCEINLINE const float4 & float4::operator-= (const float4& a) { #if OIIO_SIMD_SSE m_simd = _mm_sub_ps (m_simd, a.m_simd); #elif defined(OIIO_SIMD_NEON) m_simd = vsubq_f32 (m_simd, a.m_simd); #else SIMD_DO (m_val[i] -= a[i]); #endif return *this; } OIIO_FORCEINLINE float4 operator* (const float4& a, const float4& b) { #if OIIO_SIMD_SSE return _mm_mul_ps (a.m_simd, b.m_simd); #elif defined(OIIO_SIMD_NEON) return vmulq_f32 (a.m_simd, b.m_simd); #else SIMD_RETURN (float4, a[i] * b[i]); #endif } OIIO_FORCEINLINE const float4 & float4::operator*= (const float4& a) { #if OIIO_SIMD_SSE m_simd = _mm_mul_ps (m_simd, a.m_simd); #elif defined(OIIO_SIMD_NEON) m_simd = vmulq_f32 (m_simd, a.m_simd); #else SIMD_DO (m_val[i] *= a[i]); #endif return *this; } OIIO_FORCEINLINE const float4 & float4::operator*= (float val) { #if OIIO_SIMD_SSE m_simd = _mm_mul_ps (m_simd, _mm_set1_ps(val)); #elif defined(OIIO_SIMD_NEON) m_simd = vmulq_f32 (m_simd, vdupq_n_f32(val)); #else SIMD_DO (m_val[i] *= val); #endif return *this; } OIIO_FORCEINLINE float4 operator/ (const float4& a, const float4& b) { #if OIIO_SIMD_SSE return _mm_div_ps (a.m_simd, b.m_simd); #else SIMD_RETURN (float4, a[i] / b[i]); #endif } OIIO_FORCEINLINE const float4 & float4::operator/= (const float4& a) { #if OIIO_SIMD_SSE m_simd = _mm_div_ps (m_simd, a.m_simd); #else SIMD_DO (m_val[i] /= a[i]); #endif return *this; } OIIO_FORCEINLINE const float4 & float4::operator/= (float val) { #if OIIO_SIMD_SSE m_simd = _mm_div_ps (m_simd, _mm_set1_ps(val)); #else SIMD_DO (m_val[i] /= val); #endif return *this; } OIIO_FORCEINLINE bool4 operator== (const float4& a, const float4& b) { #if OIIO_SIMD_SSE return _mm_cmpeq_ps (a.m_simd, b.m_simd); #else SIMD_RETURN (bool4, a[i] == b[i] ? -1 : 0); #endif } OIIO_FORCEINLINE bool4 operator!= (const float4& a, const float4& b) { #if OIIO_SIMD_SSE return _mm_cmpneq_ps (a.m_simd, b.m_simd); #else SIMD_RETURN (bool4, a[i] != b[i] ? -1 : 0); #endif } OIIO_FORCEINLINE bool4 operator< (const float4& a, const float4& b) { #if OIIO_SIMD_SSE return _mm_cmplt_ps (a.m_simd, b.m_simd); #else SIMD_RETURN (bool4, a[i] < b[i] ? -1 : 0); #endif } OIIO_FORCEINLINE bool4 operator> (const float4& a, const float4& b) { #if OIIO_SIMD_SSE return _mm_cmpgt_ps (a.m_simd, b.m_simd); #else SIMD_RETURN (bool4, a[i] > b[i] ? -1 : 0); #endif } OIIO_FORCEINLINE bool4 operator>= (const float4& a, const float4& b) { #if OIIO_SIMD_SSE return _mm_cmpge_ps (a.m_simd, b.m_simd); #else SIMD_RETURN (bool4, a[i] >= b[i] ? -1 : 0); #endif } OIIO_FORCEINLINE bool4 operator<= (const float4& a, const float4& b) { #if OIIO_SIMD_SSE return _mm_cmple_ps (a.m_simd, b.m_simd); #else SIMD_RETURN (bool4, a[i] <= b[i] ? -1 : 0); #endif } OIIO_FORCEINLINE float4 AxyBxy (const float4& a, const float4& b) { #if OIIO_SIMD_SSE return _mm_movelh_ps (a.m_simd, b.m_simd); #else return float4 (a[0], a[1], b[0], b[1]); #endif } OIIO_FORCEINLINE float4 AxBxAyBy (const float4& a, const float4& b) { #if OIIO_SIMD_SSE return _mm_unpacklo_ps (a.m_simd, b.m_simd); #else return float4 (a[0], b[0], a[1], b[1]); #endif } OIIO_FORCEINLINE float4 float4::xyz0 () const { return insert<3>(*this, 0.0f); } OIIO_FORCEINLINE float4 float4::xyz1 () const { return insert<3>(*this, 1.0f); } inline std::ostream& operator<< (std::ostream& cout, const float4& val) { cout << val[0]; for (int i = 1; i < val.elements; ++i) cout << ' ' << val[i]; return cout; } // Implementation had to be after the definition of float4. OIIO_FORCEINLINE int4::int4 (const float4& f) { #if OIIO_SIMD_SSE m_simd = _mm_cvttps_epi32(f.simd()); #else SIMD_CONSTRUCT ((int) f[i]); #endif } template OIIO_FORCEINLINE float4 shuffle (const float4& a) { #if OIIO_SIMD_SSE return shuffle_sse (__m128(a)); #else return float4(a[i0], a[i1], a[i2], a[i3]); #endif } template OIIO_FORCEINLINE float4 shuffle (const float4& a) { return shuffle(a); } #if defined(OIIO_SIMD_NEON) template<> OIIO_FORCEINLINE float4 shuffle<0> (const float4& a) { float32x2_t t = vget_low_f32(a.m_simd); return vdupq_lane_f32(t,0); } template<> OIIO_FORCEINLINE float4 shuffle<1> (const float4& a) { float32x2_t t = vget_low_f32(a.m_simd); return vdupq_lane_f32(t,1); } template<> OIIO_FORCEINLINE float4 shuffle<2> (const float4& a) { float32x2_t t = vget_high_f32(a.m_simd); return vdupq_lane_f32(t,0); } template<> OIIO_FORCEINLINE float4 shuffle<3> (const float4& a) { float32x2_t t = vget_high_f32(a.m_simd); return vdupq_lane_f32(t,1); } #endif /// Helper: as rapid as possible extraction of one component, when the /// index is fixed. template OIIO_FORCEINLINE float extract (const float4& a) { #if OIIO_SIMD_SSE return _mm_cvtss_f32(shuffle_sse(a.simd())); #else return a[i]; #endif } #if OIIO_SIMD_SSE template<> OIIO_FORCEINLINE float extract<0> (const float4& a) { return _mm_cvtss_f32(a.simd()); } #endif /// Helper: substitute val for a[i] template OIIO_FORCEINLINE float4 insert (const float4& a, float val) { #if OIIO_SIMD_SSE >= 4 return _mm_insert_ps (a, _mm_set_ss(val), i<<4); #else float4 tmp = a; tmp[i] = val; return tmp; #endif } #if OIIO_SIMD_SSE // Slightly faster special cases for SSE template<> OIIO_FORCEINLINE float4 insert<0> (const float4& a, float val) { return _mm_move_ss (a.simd(), _mm_set_ss(val)); } #endif OIIO_FORCEINLINE float float4::x () const { return extract<0>(*this); } OIIO_FORCEINLINE float float4::y () const { return extract<1>(*this); } OIIO_FORCEINLINE float float4::z () const { return extract<2>(*this); } OIIO_FORCEINLINE float float4::w () const { return extract<3>(*this); } OIIO_FORCEINLINE void float4::set_x (float val) { *this = insert<0>(*this, val); } OIIO_FORCEINLINE void float4::set_y (float val) { *this = insert<1>(*this, val); } OIIO_FORCEINLINE void float4::set_z (float val) { *this = insert<2>(*this, val); } OIIO_FORCEINLINE void float4::set_w (float val) { *this = insert<3>(*this, val); } OIIO_FORCEINLINE int4 bitcast_to_int (const float4& x) { #if OIIO_SIMD_SSE return _mm_castps_si128 (x.simd()); #else return *(int4 *)&x; #endif } OIIO_FORCEINLINE float4 bitcast_to_float (const int4& x) { #if OIIO_SIMD_SSE return _mm_castsi128_ps (x.simd()); #else return *(float4 *)&x; #endif } // Old names: inline int4 bitcast_to_int4 (const float4& x) { return bitcast_to_int(x); } inline float4 bitcast_to_float4 (const int4& x) { return bitcast_to_float(x); } OIIO_FORCEINLINE float4 vreduce_add (const float4& v) { #if OIIO_SIMD_SSE >= 3 // People seem to agree that SSE3 does add reduction best with 2 // horizontal adds. // suppose v = (a, b, c, d) simd::float4 ab_cd = _mm_hadd_ps (v.simd(), v.simd()); // ab_cd = (a+b, c+d, a+b, c+d) simd::float4 abcd = _mm_hadd_ps (ab_cd.simd(), ab_cd.simd()); // all abcd elements are a+b+c+d return abcd; #elif defined(OIIO_SIMD_SSE) // I think this is the best we can do for SSE2, and I'm still not sure // it's faster than the default scalar operation. But anyway... // suppose v = (a, b, c, d) float4 ab_ab_cd_cd = shuffle<1,0,3,2>(v) + v; // now x = (b,a,d,c) + (a,b,c,d) = (a+b,a+b,c+d,c+d) float4 cd_cd_ab_ab = shuffle<2,3,0,1>(ab_ab_cd_cd); // now y = (c+d,c+d,a+b,a+b) float4 abcd = ab_ab_cd_cd + cd_cd_ab_ab; // a+b+c+d in all components return abcd; #else return float4 (v[0] + v[1] + v[2] + v[3]); #endif } OIIO_FORCEINLINE float reduce_add (const float4& v) { #if OIIO_SIMD_SSE return _mm_cvtss_f32(vreduce_add (v)); #else return v[0] + v[1] + v[2] + v[3]; #endif } OIIO_FORCEINLINE float4 vdot (const float4 &a, const float4 &b) { #if OIIO_SIMD_SSE >= 4 return _mm_dp_ps (a.simd(), b.simd(), 0xff); #elif OIIO_SIMD_NEON float32x4_t ab = vmulq_f32(a, b); float32x4_t sum1 = vaddq_f32(ab, vrev64q_f32(ab)); return vaddq_f32(sum1, vcombine_f32(vget_high_f32(sum1), vget_low_f32(sum1))); #else return vreduce_add (a*b); #endif } OIIO_FORCEINLINE float dot (const float4 &a, const float4 &b) { #if OIIO_SIMD_SSE >= 4 return _mm_cvtss_f32 (_mm_dp_ps (a.simd(), b.simd(), 0xff)); #else return reduce_add (a*b); #endif } OIIO_FORCEINLINE float4 vdot3 (const float4 &a, const float4 &b) { #if OIIO_SIMD_SSE >= 4 return _mm_dp_ps (a.simd(), b.simd(), 0x7f); #else return vreduce_add((a*b).xyz0()); #endif } OIIO_FORCEINLINE float dot3 (const float4 &a, const float4 &b) { #if OIIO_SIMD_SSE >= 4 return _mm_cvtss_f32 (_mm_dp_ps (a.simd(), b.simd(), 0x77)); #else return reduce_add ((a*b).xyz0()); #endif } OIIO_FORCEINLINE float4 blend (const float4& a, const float4& b, const bool4& mask) { #if OIIO_SIMD_SSE >= 4 // SSE >= 4.1 only return _mm_blendv_ps (a.simd(), b.simd(), mask.simd()); #elif defined(OIIO_SIMD_SSE) // Trick for SSE < 4.1 return _mm_or_ps (_mm_and_ps(mask.simd(), b.simd()), _mm_andnot_ps(mask.simd(), a.simd())); #else return float4 (mask[0] ? b[0] : a[0], mask[1] ? b[1] : a[1], mask[2] ? b[2] : a[2], mask[3] ? b[3] : a[3]); #endif } OIIO_FORCEINLINE float4 blend0 (const float4& a, const bool4& mask) { #if OIIO_SIMD_SSE return _mm_and_ps(mask.simd(), a.simd()); #else return float4 (mask[0] ? a[0] : 0.0f, mask[1] ? a[1] : 0.0f, mask[2] ? a[2] : 0.0f, mask[3] ? a[3] : 0.0f); #endif } OIIO_FORCEINLINE float4 blend0not (const float4& a, const bool4& mask) { #if OIIO_SIMD_SSE return _mm_andnot_ps(mask.simd(), a.simd()); #else return float4 (mask[0] ? 0.0f : a[0], mask[1] ? 0.0f : a[1], mask[2] ? 0.0f : a[2], mask[3] ? 0.0f : a[3]); #endif } OIIO_FORCEINLINE float4 safe_div (const float4 &a, const float4 &b) { #if OIIO_SIMD_SSE return blend0not (a/b, b == float4::Zero()); #else return float4 (b[0] == 0.0f ? 0.0f : a[0] / b[0], b[1] == 0.0f ? 0.0f : a[1] / b[1], b[2] == 0.0f ? 0.0f : a[2] / b[2], b[3] == 0.0f ? 0.0f : a[3] / b[3]); #endif } OIIO_FORCEINLINE float3 hdiv (const float4 &a) { #if OIIO_SIMD_SSE return float3(safe_div(a, shuffle<3>(a)).xyz0()); #else float d = a[3]; return d == 0.0f ? float3 (0.0f) : float3 (a[0]/d, a[1]/d, a[2]/d); #endif } OIIO_FORCEINLINE float4 select (const bool4& mask, const float4& a, const float4& b) { return blend (b, a, mask); } OIIO_FORCEINLINE float4 abs (const float4& a) { #if OIIO_SIMD_SSE // Just clear the sign bit for cheap fabsf return _mm_and_ps (a.simd(), _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff))); #else SIMD_RETURN (float4, fabsf(a[i])); #endif } OIIO_FORCEINLINE float4 sign (const float4& a) { float4 one(1.0f); return blend (one, -one, a < float4::Zero()); } OIIO_FORCEINLINE float4 ceil (const float4& a) { #if defined(OIIO_SIMD_SSE) && OIIO_SIMD_SSE >= 4 /* SSE >= 4.1 */ return _mm_ceil_ps (a); #else SIMD_RETURN (float4, ceilf(a[i])); #endif } OIIO_FORCEINLINE float4 floor (const float4& a) { #if defined(OIIO_SIMD_SSE) && OIIO_SIMD_SSE >= 4 /* SSE >= 4.1 */ return _mm_floor_ps (a); #else SIMD_RETURN (float4, floorf(a[i])); #endif } OIIO_FORCEINLINE float4 round (const float4& a) { #if defined(OIIO_SIMD_SSE) && OIIO_SIMD_SSE >= 4 /* SSE >= 4.1 */ return _mm_round_ps (a, (_MM_FROUND_TO_NEAREST_INT |_MM_FROUND_NO_EXC)); #else SIMD_RETURN (float4, roundf(a[i])); #endif } OIIO_FORCEINLINE int4 floori (const float4& a) { #if defined(OIIO_SIMD_SSE) && OIIO_SIMD_SSE >= 4 /* SSE >= 4.1 */ return int4(floor(a)); #elif defined(OIIO_SIMD_SSE) /* SSE2/3 */ int4 i (a); // truncates int4 isneg = bitcast_to_int (a < float4::Zero()); return i + isneg; // The trick here (thanks, Cycles, for letting me spy on your code) is // that the comparison will return (int)-1 for components that are less // than zero, and adding that is the same as subtracting one! #else SIMD_RETURN (int4, (int)floorf(a[i])); #endif } OIIO_FORCEINLINE int4 rint (const float4& a) { return int4 (round(a)); } OIIO_FORCEINLINE float4 sqrt (const float4 &a) { #if OIIO_SIMD_SSE return _mm_sqrt_ps (a.simd()); #else SIMD_RETURN (float4, sqrtf(a[i])); #endif } OIIO_FORCEINLINE float4 rsqrt (const float4 &a) { #if OIIO_SIMD_SSE return _mm_div_ps (_mm_set1_ps(1.0f), _mm_sqrt_ps (a.simd())); #else SIMD_RETURN (float4, 1.0f/sqrtf(a[i])); #endif } OIIO_FORCEINLINE float4 rsqrt_fast (const float4 &a) { #if OIIO_SIMD_SSE return _mm_rsqrt_ps (a.simd()); #else SIMD_RETURN (float4, 1.0f/sqrtf(a[i])); #endif } OIIO_FORCEINLINE float4 min (const float4& a, const float4& b) { #if OIIO_SIMD_SSE return _mm_min_ps (a, b); #else SIMD_RETURN (float4, std::min (a[i], b[i])); #endif } OIIO_FORCEINLINE float4 max (const float4& a, const float4& b) { #if OIIO_SIMD_SSE return _mm_max_ps (a, b); #else SIMD_RETURN (float4, std::max (a[i], b[i])); #endif } OIIO_FORCEINLINE float4 andnot (const float4& a, const float4& b) { #if OIIO_SIMD_SSE return _mm_andnot_ps (a.simd(), b.simd()); #else const int *ai = (const int *)&a; const int *bi = (const int *)&b; return bitcast_to_float (int4(~(ai[0]) & bi[0], ~(ai[1]) & bi[1], ~(ai[2]) & bi[2], ~(ai[3]) & bi[3])); #endif } OIIO_FORCEINLINE float4 madd (const simd::float4& a, const simd::float4& b, const simd::float4& c) { #if OIIO_SIMD_SSE && OIIO_FMA_ENABLED // If we are sure _mm_fmadd_ps intrinsic is available, use it. return _mm_fmadd_ps (a, b, c); #elif OIIO_SIMD_SSE && !defined(_MSC_VER) // If we directly access the underlying __m128, on some platforms and // compiler flags, it will turn into fma anyway, even if we don't use // the intrinsic. return a.simd() * b.simd() + c.simd(); #else // Fallback: just use regular math and hope for the best. return a * b + c; #endif } OIIO_FORCEINLINE float4 msub (const simd::float4& a, const simd::float4& b, const simd::float4& c) { #if OIIO_SIMD_SSE && OIIO_FMA_ENABLED // If we are sure _mm_fnmsub_ps intrinsic is available, use it. return _mm_fmsub_ps (a, b, c); #elif OIIO_SIMD_SSE && !defined(_MSC_VER) // If we directly access the underlying __m128, on some platforms and // compiler flags, it will turn into fma anyway, even if we don't use // the intrinsic. return a.simd() * b.simd() - c.simd(); #else // Fallback: just use regular math and hope for the best. return a * b - c; #endif } OIIO_FORCEINLINE float4 nmadd (const simd::float4& a, const simd::float4& b, const simd::float4& c) { #if OIIO_SIMD_SSE && OIIO_FMA_ENABLED // If we are sure _mm_fnmadd_ps intrinsic is available, use it. return _mm_fnmadd_ps (a, b, c); #elif OIIO_SIMD_SSE && !defined(_MSC_VER) // If we directly access the underlying __m128, on some platforms and // compiler flags, it will turn into fma anyway, even if we don't use // the intrinsic. return c.simd() - a.simd() * b.simd(); #else // Fallback: just use regular math and hope for the best. return c - a * b; #endif } OIIO_FORCEINLINE float4 nmsub (const simd::float4& a, const simd::float4& b, const simd::float4& c) { #if OIIO_SIMD_SSE && OIIO_FMA_ENABLED // If we are sure _mm_fnmsub_ps intrinsic is available, use it. return _mm_fnmsub_ps (a, b, c); #elif OIIO_SIMD_SSE && !defined(_MSC_VER) // If we directly access the underlying __m128, on some platforms and // compiler flags, it will turn into fma anyway, even if we don't use // the intrinsic. return -(a.simd() * b.simd()) - c.simd(); #else // Fallback: just use regular math and hope for the best. return -(a * b) - c; #endif } // Full precision exp() of all components of a SIMD vector. template OIIO_FORCEINLINE T exp (const T& v) { #if OIIO_SIMD_SSE // Implementation inspired by: // https://github.com/embree/embree/blob/master/common/simd/sse_special.h // Which is listed as Copyright (C) 2007 Julien Pommier and distributed // under the zlib license. typedef typename T::int_t int_t; T x = v; const float exp_hi (88.3762626647949f); const float exp_lo (-88.3762626647949f); const float cephes_LOG2EF (1.44269504088896341f); const float cephes_exp_C1 (0.693359375f); const float cephes_exp_C2 (-2.12194440e-4f); const float cephes_exp_p0 (1.9875691500E-4f); const float cephes_exp_p1 (1.3981999507E-3f); const float cephes_exp_p2 (8.3334519073E-3f); const float cephes_exp_p3 (4.1665795894E-2f); const float cephes_exp_p4 (1.6666665459E-1f); const float cephes_exp_p5 (5.0000001201E-1f); T tmp (0.0f); T one (1.0f); x = min (x, T(exp_hi)); x = max (x, T(exp_lo)); T fx = madd (x, T(cephes_LOG2EF), T(0.5f)); int_t emm0 = int_t(fx); tmp = T(emm0); T mask = bitcast_to_float (bitcast_to_int(tmp > fx) & bitcast_to_int(one)); fx = tmp - mask; tmp = fx * cephes_exp_C1; T z = fx * cephes_exp_C2; x = x - tmp; x = x - z; z = x * x; T y = cephes_exp_p0; y = madd (y, x, cephes_exp_p1); y = madd (y, x, cephes_exp_p2); y = madd (y, x, cephes_exp_p3); y = madd (y, x, cephes_exp_p4); y = madd (y, x, cephes_exp_p5); y = madd (y, z, x); y = y + one; emm0 = (int_t(fx) + int_t(0x7f)) << 23; T pow2n = bitcast_to_float(emm0); y = y * pow2n; return y; #else SIMD_RETURN (T, expf(v[i])); #endif } // Full precision log() of all components of a SIMD vector. template OIIO_FORCEINLINE T log (const T& v) { #if OIIO_SIMD_SSE // Implementation inspired by: // https://github.com/embree/embree/blob/master/common/simd/sse_special.h // Which is listed as Copyright (C) 2007 Julien Pommier and distributed // under the zlib license. typedef typename T::int_t int_t; typedef typename T::bool_t bool_t; T x = v; int_t emm0; T zero (T::Zero()); T one (1.0f); bool_t invalid_mask = (x <= zero); const int min_norm_pos ((int)0x00800000); const int inv_mant_mask ((int)~0x7f800000); x = max(x, bitcast_to_float(int_t(min_norm_pos))); /* cut off denormalized stuff */ emm0 = srl (bitcast_to_int(x), 23); /* keep only the fractional part */ x = bitcast_to_float (bitcast_to_int(x) & int_t(inv_mant_mask)); x = bitcast_to_float (bitcast_to_int(x) | bitcast_to_int(T(0.5f))); emm0 = emm0 - int_t(0x7f); T e (emm0); e = e + one; // OIIO_SIMD_FLOAT4_CONST (cephes_SQRTHF, 0.707106781186547524f); const float cephes_SQRTHF (0.707106781186547524f); bool_t mask = (x < T(cephes_SQRTHF)); T tmp = bitcast_to_float (bitcast_to_int(x) & bitcast_to_int(mask)); x = x - one; e = e - bitcast_to_float (bitcast_to_int(one) & bitcast_to_int(mask)); x = x + tmp; T z = x * x; const float cephes_log_p0 (7.0376836292E-2f); const float cephes_log_p1 (- 1.1514610310E-1f); const float cephes_log_p2 (1.1676998740E-1f); const float cephes_log_p3 (- 1.2420140846E-1f); const float cephes_log_p4 (+ 1.4249322787E-1f); const float cephes_log_p5 (- 1.6668057665E-1f); const float cephes_log_p6 (+ 2.0000714765E-1f); const float cephes_log_p7 (- 2.4999993993E-1f); const float cephes_log_p8 (+ 3.3333331174E-1f); const float cephes_log_q1 (-2.12194440e-4f); const float cephes_log_q2 (0.693359375f); T y = cephes_log_p0; y = madd (y, x, T(cephes_log_p1)); y = madd (y, x, T(cephes_log_p2)); y = madd (y, x, T(cephes_log_p3)); y = madd (y, x, T(cephes_log_p4)); y = madd (y, x, T(cephes_log_p5)); y = madd (y, x, T(cephes_log_p6)); y = madd (y, x, T(cephes_log_p7)); y = madd (y, x, T(cephes_log_p8)); y = y * x; y = y * z; y = madd(e, T(cephes_log_q1), y); y = nmadd (z, 0.5f, y); x = x + y; x = madd (e, T(cephes_log_q2), x); x = bitcast_to_float (bitcast_to_int(x) | bitcast_to_int(invalid_mask)); // negative arg will be NAN return x; #else SIMD_RETURN (T, logf(v[i])); #endif } OIIO_FORCEINLINE void transpose (float4 &a, float4 &b, float4 &c, float4 &d) { #if OIIO_SIMD_SSE _MM_TRANSPOSE4_PS (a, b, c, d); #else float4 A (a[0], b[0], c[0], d[0]); float4 B (a[1], b[1], c[1], d[1]); float4 C (a[2], b[2], c[2], d[2]); float4 D (a[3], b[3], c[3], d[3]); a = A; b = B; c = C; d = D; #endif } OIIO_FORCEINLINE void transpose (const float4& a, const float4& b, const float4& c, const float4& d, float4 &r0, float4 &r1, float4 &r2, float4 &r3) { #if OIIO_SIMD_SSE //_MM_TRANSPOSE4_PS (a, b, c, d); float4 l02 = _mm_unpacklo_ps (a, c); float4 h02 = _mm_unpackhi_ps (a, c); float4 l13 = _mm_unpacklo_ps (b, d); float4 h13 = _mm_unpackhi_ps (b, d); r0 = _mm_unpacklo_ps (l02, l13); r1 = _mm_unpackhi_ps (l02, l13); r2 = _mm_unpacklo_ps (h02, h13); r3 = _mm_unpackhi_ps (h02, h13); #else r0.load (a[0], b[0], c[0], d[0]); r1.load (a[1], b[1], c[1], d[1]); r2.load (a[2], b[2], c[2], d[2]); r3.load (a[3], b[3], c[3], d[3]); #endif } OIIO_FORCEINLINE void transpose (int4 &a, int4 &b, int4 &c, int4 &d) { #if OIIO_SIMD_SSE __m128 A = _mm_castsi128_ps (a); __m128 B = _mm_castsi128_ps (b); __m128 C = _mm_castsi128_ps (c); __m128 D = _mm_castsi128_ps (d); _MM_TRANSPOSE4_PS (A, B, C, D); a = _mm_castps_si128 (A); b = _mm_castps_si128 (B); c = _mm_castps_si128 (C); d = _mm_castps_si128 (D); #else int4 A (a[0], b[0], c[0], d[0]); int4 B (a[1], b[1], c[1], d[1]); int4 C (a[2], b[2], c[2], d[2]); int4 D (a[3], b[3], c[3], d[3]); a = A; b = B; c = C; d = D; #endif } OIIO_FORCEINLINE void transpose (const int4& a, const int4& b, const int4& c, const int4& d, int4 &r0, int4 &r1, int4 &r2, int4 &r3) { #if OIIO_SIMD_SSE //_MM_TRANSPOSE4_PS (a, b, c, d); __m128 A = _mm_castsi128_ps (a); __m128 B = _mm_castsi128_ps (b); __m128 C = _mm_castsi128_ps (c); __m128 D = _mm_castsi128_ps (d); _MM_TRANSPOSE4_PS (A, B, C, D); r0 = _mm_castps_si128 (A); r1 = _mm_castps_si128 (B); r2 = _mm_castps_si128 (C); r3 = _mm_castps_si128 (D); #else r0.load (a[0], b[0], c[0], d[0]); r1.load (a[1], b[1], c[1], d[1]); r2.load (a[2], b[2], c[2], d[2]); r3.load (a[3], b[3], c[3], d[3]); #endif } OIIO_FORCEINLINE float4 AxBxCxDx (const float4& a, const float4& b, const float4& c, const float4& d) { #if OIIO_SIMD_SSE float4 l02 = _mm_unpacklo_ps (a, c); float4 l13 = _mm_unpacklo_ps (b, d); return _mm_unpacklo_ps (l02, l13); #else return float4 (a[0], b[0], c[0], d[0]); #endif } OIIO_FORCEINLINE int4 AxBxCxDx (const int4& a, const int4& b, const int4& c, const int4& d) { #if OIIO_SIMD_SSE int4 l02 = _mm_unpacklo_epi32 (a, c); int4 l13 = _mm_unpacklo_epi32 (b, d); return _mm_unpacklo_epi32 (l02, l13); #else return int4 (a[0], b[0], c[0], d[0]); #endif } ////////////////////////////////////////////////////////////////////// // float3 implementation OIIO_FORCEINLINE float3::float3 (const float3 &other) { #if defined(OIIO_SIMD_SSE) || defined(OIIO_SIMD_NEON) m_simd = other.m_simd; #else SIMD_CONSTRUCT_PAD (other[i]); #endif } OIIO_FORCEINLINE float3::float3 (const float4 &other) { #if defined(OIIO_SIMD_SSE) || defined(OIIO_SIMD_NEON) m_simd = other.simd(); #else SIMD_CONSTRUCT_PAD (other[i]); m_val[3] = 0.0f; #endif } OIIO_FORCEINLINE const float3 float3::Zero () { return float3(float4::Zero()); } OIIO_FORCEINLINE const float3 float3::One () { return float3(1.0f); } OIIO_FORCEINLINE const float3 float3::Iota (float start, float step) { return float3 (start+0.0f*step, start+1.0f*step, start+2.0f*step); } OIIO_FORCEINLINE void float3::load (float val) { float4::load (val, val, val, 0.0f); } OIIO_FORCEINLINE void float3::load (const float *values) { float4::load (values, 3); } OIIO_FORCEINLINE void float3::load (const float *values, int n) { float4::load (values, n); } OIIO_FORCEINLINE void float3::load (const unsigned short *values) { float4::load (float(values[0]), float(values[1]), float(values[2])); } OIIO_FORCEINLINE void float3::load (const short *values) { float4::load (float(values[0]), float(values[1]), float(values[2])); } OIIO_FORCEINLINE void float3::load (const unsigned char *values) { float4::load (float(values[0]), float(values[1]), float(values[2])); } OIIO_FORCEINLINE void float3::load (const char *values) { float4::load (float(values[0]), float(values[1]), float(values[2])); } #ifdef _HALF_H_ OIIO_FORCEINLINE void float3::load (const half *values) { float4::load (float(values[0]), float(values[1]), float(values[2])); } #endif /* _HALF_H_ */ OIIO_FORCEINLINE void float3::store (float *values) const { float4::store (values, 3); } OIIO_FORCEINLINE void float3::store (float *values, int n) const { float4::store (values, n); } #ifdef _HALF_H_ OIIO_FORCEINLINE void float3::store (half *values) const { SIMD_DO (values[i] = m_val[i]); } #endif OIIO_FORCEINLINE void float3::store (Imath::V3f &vec) const { store ((float *)&vec); } OIIO_FORCEINLINE float3 operator+ (const float3& a, const float3& b) { return float3 (float4(a) + float4(b)); } OIIO_FORCEINLINE const float3 & float3::operator+= (const float3& a) { *this = *this + a; return *this; } OIIO_FORCEINLINE float3 float3::operator- () const { return float3 (-float4(*this)); } OIIO_FORCEINLINE float3 operator- (const float3& a, const float3& b) { return float3 (float4(a) - float4(b)); } OIIO_FORCEINLINE const float3 & float3::operator-= (const float3& a) { *this = *this - a; return *this; } OIIO_FORCEINLINE float3 operator* (const float3& a, const float3& b) { return float3 (float4(a) * float4(b)); } OIIO_FORCEINLINE const float3 & float3::operator*= (const float3& a) { *this = *this * a; return *this; } OIIO_FORCEINLINE const float3 & float3::operator*= (float a) { *this = *this * a; return *this; } OIIO_FORCEINLINE float3 operator/ (const float3& a, const float3& b) { return float3 (float4(a) / b.xyz1()); // Avoid divide by zero! } OIIO_FORCEINLINE const float3 & float3::operator/= (const float3& a) { *this = *this / a; return *this; } OIIO_FORCEINLINE const float3 & float3::operator/= (float a) { *this = *this / a; return *this; } inline std::ostream& operator<< (std::ostream& cout, const float3& val) { cout << val[0]; for (int i = 1; i < val.elements; ++i) cout << ' ' << val[i]; return cout; } OIIO_FORCEINLINE float3 vreduce_add (const float3& v) { #if OIIO_SIMD_SSE return float3 ((vreduce_add(float4(v))).xyz0()); #else return float3 (v[0] + v[1] + v[2]); #endif } OIIO_FORCEINLINE float3 vdot (const float3 &a, const float3 &b) { #if OIIO_SIMD_SSE >= 4 return float3(_mm_dp_ps (a.simd(), b.simd(), 0x77)); #else return vreduce_add (a*b); #endif } OIIO_FORCEINLINE float dot (const float3 &a, const float3 &b) { #if OIIO_SIMD_SSE >= 4 return _mm_cvtss_f32 (_mm_dp_ps (a.simd(), b.simd(), 0x77)); #else return reduce_add (a*b); #endif } OIIO_FORCEINLINE float3 vdot3 (const float3 &a, const float3 &b) { #if OIIO_SIMD_SSE >= 4 return float3(_mm_dp_ps (a.simd(), b.simd(), 0x77)); #else return float3 (vreduce_add((a*b).xyz0()).xyz0()); #endif } OIIO_FORCEINLINE float3 float3::normalized () const { #if OIIO_SIMD float3 len2 = vdot3 (*this, *this); return float3 (safe_div (*this, sqrt(len2))); #else float len2 = dot (*this, *this); return len2 > 0.0f ? (*this) / sqrtf(len2) : float3::Zero(); #endif } OIIO_FORCEINLINE float3 float3::normalized_fast () const { #if OIIO_SIMD float3 len2 = vdot3 (*this, *this); float4 invlen = blend0not (rsqrt_fast (len2), len2 == float4::Zero()); return float3 ((*this) * invlen); #else float len2 = dot (*this, *this); return len2 > 0.0f ? (*this) / sqrtf(len2) : float3::Zero(); #endif } ////////////////////////////////////////////////////////////////////// // matrix44 implementation OIIO_FORCEINLINE const Imath::M44f& matrix44::M44f() const { return *(Imath::M44f*)this; } OIIO_FORCEINLINE float4 matrix44::operator[] (int i) const { #if OIIO_SIMD_SSE return m_row[i]; #else return float4 (m_mat[i]); #endif } OIIO_FORCEINLINE matrix44 matrix44::transposed () const { matrix44 T; #if OIIO_SIMD_SSE simd::transpose (m_row[0], m_row[1], m_row[2], m_row[3], T.m_row[0], T.m_row[1], T.m_row[2], T.m_row[3]); #else T = m_mat.transposed(); #endif return T; } OIIO_FORCEINLINE float3 matrix44::transformp (const float3 &V) const { #if OIIO_SIMD_SSE float4 R = shuffle<0>(V) * m_row[0] + shuffle<1>(V) * m_row[1] + shuffle<2>(V) * m_row[2] + m_row[3]; R = R / shuffle<3>(R); return float3 (R.xyz0()); #else Imath::V3f R; m_mat.multVecMatrix (*(Imath::V3f *)&V, R); return float3(R); #endif } OIIO_FORCEINLINE float3 matrix44::transformv (const float3 &V) const { #if OIIO_SIMD_SSE float4 R = shuffle<0>(V) * m_row[0] + shuffle<1>(V) * m_row[1] + shuffle<2>(V) * m_row[2]; return float3 (R.xyz0()); #else Imath::V3f R; m_mat.multDirMatrix (*(Imath::V3f *)&V, R); return float3(R); #endif } OIIO_FORCEINLINE float3 matrix44::transformvT (const float3 &V) const { #if OIIO_SIMD_SSE matrix44 T = transposed(); float4 R = shuffle<0>(V) * T[0] + shuffle<1>(V) * T[1] + shuffle<2>(V) * T[2]; return float3 (R.xyz0()); #else Imath::V3f R; m_mat.transposed().multDirMatrix (*(Imath::V3f *)&V, R); return float3(R); #endif } OIIO_FORCEINLINE bool matrix44::operator== (const matrix44& m) const { #if OIIO_SIMD_SSE bool4 b0 = (m_row[0] == m[0]); bool4 b1 = (m_row[1] == m[1]); bool4 b2 = (m_row[2] == m[2]); bool4 b3 = (m_row[3] == m[3]); return simd::all (b0 & b1 & b2 & b3); #else return memcmp(this, &m, 16*sizeof(float)) == 0; #endif } OIIO_FORCEINLINE bool matrix44::operator== (const Imath::M44f& m) const { return memcmp(this, &m, 16*sizeof(float)) == 0; } OIIO_FORCEINLINE bool operator== (const Imath::M44f& a, const matrix44 &b) { return (b == a); } OIIO_FORCEINLINE bool matrix44::operator!= (const matrix44& m) const { #if OIIO_SIMD_SSE bool4 b0 = (m_row[0] != m[0]); bool4 b1 = (m_row[1] != m[1]); bool4 b2 = (m_row[2] != m[2]); bool4 b3 = (m_row[3] != m[3]); return simd::any (b0 | b1 | b2 | b3); #else return memcmp(this, &m, 16*sizeof(float)) != 0; #endif } OIIO_FORCEINLINE bool matrix44::operator!= (const Imath::M44f& m) const { return memcmp(this, &m, 16*sizeof(float)) != 0; } OIIO_FORCEINLINE bool operator!= (const Imath::M44f& a, const matrix44 &b) { return (b != a); } OIIO_FORCEINLINE matrix44 matrix44::inverse() const { #if OIIO_SIMD_SSE // Adapted from this code from Intel: // ftp://download.intel.com/design/pentiumiii/sml/24504301.pdf float4 minor0, minor1, minor2, minor3; float4 row0, row1, row2, row3; float4 det, tmp1; const float *src = (const float *)this; float4 zero = float4::Zero(); tmp1 = _mm_loadh_pi(_mm_loadl_pi(zero, (__m64*)(src)), (__m64*)(src+ 4)); row1 = _mm_loadh_pi(_mm_loadl_pi(zero, (__m64*)(src+8)), (__m64*)(src+12)); row0 = _mm_shuffle_ps(tmp1, row1, 0x88); row1 = _mm_shuffle_ps(row1, tmp1, 0xDD); tmp1 = _mm_loadh_pi(_mm_loadl_pi(tmp1, (__m64*)(src+ 2)), (__m64*)(src+ 6)); row3 = _mm_loadh_pi(_mm_loadl_pi(zero, (__m64*)(src+10)), (__m64*)(src+14)); row2 = _mm_shuffle_ps(tmp1, row3, 0x88); row3 = _mm_shuffle_ps(row3, tmp1, 0xDD); // ----------------------------------------------- tmp1 = row2 * row3; tmp1 = shuffle<1,0,3,2>(tmp1); minor0 = row1 * tmp1; minor1 = row0 * tmp1; tmp1 = shuffle<2,3,0,1>(tmp1); minor0 = (row1 * tmp1) - minor0; minor1 = (row0 * tmp1) - minor1; minor1 = shuffle<2,3,0,1>(minor1); // ----------------------------------------------- tmp1 = row1 * row2; tmp1 = shuffle<1,0,3,2>(tmp1); minor0 = (row3 * tmp1) + minor0; minor3 = row0 * tmp1; tmp1 = shuffle<2,3,0,1>(tmp1); minor0 = minor0 - (row3 * tmp1); minor3 = (row0 * tmp1) - minor3; minor3 = shuffle<2,3,0,1>(minor3); // ----------------------------------------------- tmp1 = shuffle<2,3,0,1>(row1) * row3; tmp1 = shuffle<1,0,3,2>(tmp1); row2 = shuffle<2,3,0,1>(row2); minor0 = (row2 * tmp1) + minor0; minor2 = row0 * tmp1; tmp1 = shuffle<2,3,0,1>(tmp1); minor0 = minor0 - (row2 * tmp1); minor2 = (row0 * tmp1) - minor2; minor2 = shuffle<2,3,0,1>(minor2); // ----------------------------------------------- tmp1 = row0 * row1; tmp1 = shuffle<1,0,3,2>(tmp1); minor2 = (row3 * tmp1) + minor2; minor3 = (row2 * tmp1) - minor3; tmp1 = shuffle<2,3,0,1>(tmp1); minor2 = (row3 * tmp1) - minor2; minor3 = minor3 - (row2 * tmp1); // ----------------------------------------------- tmp1 = row0 * row3; tmp1 = shuffle<1,0,3,2>(tmp1); minor1 = minor1 - (row2 * tmp1); minor2 = (row1 * tmp1) + minor2; tmp1 = shuffle<2,3,0,1>(tmp1); minor1 = (row2 * tmp1) + minor1; minor2 = minor2 - (row1 * tmp1); // ----------------------------------------------- tmp1 = row0 * row2; tmp1 = shuffle<1,0,3,2>(tmp1); minor1 = (row3 * tmp1) + minor1; minor3 = minor3 - (row1 * tmp1); tmp1 = shuffle<2,3,0,1>(tmp1); minor1 = minor1 - (row3 * tmp1); minor3 = (row1 * tmp1) + minor3; // ----------------------------------------------- det = row0 * minor0; det = shuffle<2,3,0,1>(det) + det; det = _mm_add_ss(shuffle<1,0,3,2>(det), det); tmp1 = _mm_rcp_ss(det); det = _mm_sub_ss(_mm_add_ss(tmp1, tmp1), _mm_mul_ss(det, _mm_mul_ss(tmp1, tmp1))); det = shuffle<0>(det); return matrix44 (det*minor0, det*minor1, det*minor2, det*minor3); #else return m_mat.inverse(); #endif } inline std::ostream& operator<< (std::ostream& cout, const matrix44 &M) { const float *m = (const float *)&M; cout << m[0]; for (int i = 1; i < 16; ++i) cout << ' ' << m[i]; return cout; } OIIO_FORCEINLINE float3 transformp (const matrix44 &M, const float3 &V) { return M.transformp (V); } OIIO_FORCEINLINE float3 transformp (const Imath::M44f &M, const float3 &V) { #if OIIO_SIMD return matrix44(M).transformp (V); #else Imath::V3f R; M.multVecMatrix (*(Imath::V3f *)&V, R); return float3(R); #endif } OIIO_FORCEINLINE float3 transformv (const matrix44 &M, const float3 &V) { return M.transformv (V); } OIIO_FORCEINLINE float3 transformv (const Imath::M44f &M, const float3 &V) { #if OIIO_SIMD return matrix44(M).transformv (V); #else Imath::V3f R; M.multDirMatrix (*(Imath::V3f *)&V, R); return float3(R); #endif } OIIO_FORCEINLINE float3 transformvT (const matrix44 &M, const float3 &V) { return M.transformvT (V); } OIIO_FORCEINLINE float3 transformvT (const Imath::M44f &M, const float3 &V) { #if OIIO_SIMD return matrix44(M).transformvT(V); #else return transformv (M.transposed(), V); #endif } ////////////////////////////////////////////////////////////////////// // float8 implementation OIIO_FORCEINLINE float& float8::operator[] (int i) { DASSERT(i 0 && n <= 4) { float4 l; l.load (values, n); m_simd = float8(l, float4::Zero()); } else if (n > 4 && n <= 8) { float4 h; h.load (values+4, n-4); m_simd = float8(float4(values), h); } else clear(); #else for (int i = 0; i < n; ++i) m_val[i] = values[i]; for (int i = n; i < paddedelements; ++i) m_val[i] = 0; #endif } OIIO_FORCEINLINE void float8::load (const unsigned short *values) { #if OIIO_SIMD_AVX // Rely on the uint16->int conversion, then convert to float m_simd = _mm256_cvtepi32_ps (int8(values).simd()); #else SIMD_CONSTRUCT (values[i]); #endif } OIIO_FORCEINLINE void float8::load (const short *values) { #if OIIO_SIMD_AVX // Rely on the int16->int conversion, then convert to float m_simd = _mm256_cvtepi32_ps (int8(values).simd()); #else SIMD_CONSTRUCT (values[i]); #endif } OIIO_FORCEINLINE void float8::load (const unsigned char *values) { #if OIIO_SIMD_AVX m_simd = _mm256_cvtepi32_ps (int8(values).simd()); #else SIMD_CONSTRUCT (values[i]); #endif } OIIO_FORCEINLINE void float8::load (const char *values) { #if OIIO_SIMD_AVX m_simd = _mm256_cvtepi32_ps (int8(values).simd()); #else SIMD_CONSTRUCT (values[i]); #endif } #ifdef _HALF_H_ OIIO_FORCEINLINE void float8::load (const half *values) { #if OIIO_SIMD_AVX && defined(__F16C__) /* Enabled 16 bit float instructions! */ int4 a ((const int *)values); m_simd = _mm256_cvtph_ps (a); #elif OIIO_SIMD_SSE >= 2 m_4[0] = float4(values); m_4[1] = float4(values+4); #else /* No SIMD defined: */ SIMD_CONSTRUCT (values[i]); #endif } #endif /* _HALF_H_ */ OIIO_FORCEINLINE void float8::store (float *values) const { #if OIIO_SIMD_AVX // Use an unaligned store -- it's just as fast when the memory turns // out to be aligned, nearly as fast even when unaligned. Not worth // the headache of using stores that require alignment. _mm256_storeu_ps (values, m_simd); #else SIMD_DO (values[i] = m_val[i]); #endif } OIIO_FORCEINLINE void float8::store (float *values, int n) const { DASSERT (n >= 0 && n <= elements); // FIXME: is this faster with AVX masked stores? #if OIIO_SIMD_SSE if (n <= 4) { lo().store (values, n); } else if (n <= 8) { lo().store (values); hi().store (values+4, n-4); } #else for (int i = 0; i < n; ++i) values[i] = m_val[i]; #endif } #ifdef _HALF_H_ OIIO_FORCEINLINE void float8::store (half *values) const { #if OIIO_SIMD_AVX && defined(__F16C__) __m128i h = _mm256_cvtps_ph (m_simd, (_MM_FROUND_TO_NEAREST_INT |_MM_FROUND_NO_EXC)); _mm_storeu_si128 ((__m128i *)values, h); #else SIMD_DO (values[i] = m_val[i]); #endif } #endif OIIO_FORCEINLINE float8 operator+ (const float8& a, const float8& b) { #if OIIO_SIMD_AVX return _mm256_add_ps (a.m_simd, b.m_simd); #else SIMD_RETURN (float8, a[i] + b[i]); #endif } OIIO_FORCEINLINE const float8 & float8::operator+= (const float8& a) { #if OIIO_SIMD_AVX m_simd = _mm256_add_ps (m_simd, a.m_simd); #else SIMD_DO (m_val[i] += a[i]); #endif return *this; } OIIO_FORCEINLINE float8 float8::operator- () const { #if OIIO_SIMD_AVX return _mm256_sub_ps (_mm256_setzero_ps(), m_simd); #else SIMD_RETURN (float8, -m_val[i]); #endif } OIIO_FORCEINLINE float8 operator- (const float8& a, const float8& b) { #if OIIO_SIMD_AVX return _mm256_sub_ps (a.m_simd, b.m_simd); #else SIMD_RETURN (float8, a[i] - b[i]); #endif } OIIO_FORCEINLINE const float8 & float8::operator-= (const float8& a) { #if OIIO_SIMD_AVX m_simd = _mm256_sub_ps (m_simd, a.m_simd); #else SIMD_DO (m_val[i] -= a[i]); #endif return *this; } OIIO_FORCEINLINE float8 operator* (const float8& a, const float8& b) { #if OIIO_SIMD_AVX return _mm256_mul_ps (a.m_simd, b.m_simd); #else SIMD_RETURN (float8, a[i] * b[i]); #endif } OIIO_FORCEINLINE const float8 & float8::operator*= (const float8& a) { #if OIIO_SIMD_AVX m_simd = _mm256_mul_ps (m_simd, a.m_simd); #else SIMD_DO (m_val[i] *= a[i]); #endif return *this; } OIIO_FORCEINLINE const float8 & float8::operator*= (float val) { #if OIIO_SIMD_AVX m_simd = _mm256_mul_ps (m_simd, _mm256_set1_ps(val)); #else SIMD_DO (m_val[i] *= val); #endif return *this; } OIIO_FORCEINLINE float8 operator/ (const float8& a, const float8& b) { #if OIIO_SIMD_AVX return _mm256_div_ps (a.m_simd, b.m_simd); #else SIMD_RETURN (float8, a[i] / b[i]); #endif } OIIO_FORCEINLINE const float8 & float8::operator/= (const float8& a) { #if OIIO_SIMD_AVX m_simd = _mm256_div_ps (m_simd, a.m_simd); #else SIMD_DO (m_val[i] /= a[i]); #endif return *this; } OIIO_FORCEINLINE const float8 & float8::operator/= (float val) { #if OIIO_SIMD_AVX m_simd = _mm256_div_ps (m_simd, _mm256_set1_ps(val)); #else SIMD_DO (m_val[i] /= val); #endif return *this; } OIIO_FORCEINLINE bool8 operator== (const float8& a, const float8& b) { #if OIIO_SIMD_AVX return _mm256_cmp_ps (a.m_simd, b.m_simd, _CMP_EQ_OQ); #else SIMD_RETURN (bool8, a[i] == b[i] ? -1 : 0); #endif } OIIO_FORCEINLINE bool8 operator!= (const float8& a, const float8& b) { #if OIIO_SIMD_AVX return _mm256_cmp_ps (a.m_simd, b.m_simd, _CMP_NEQ_OQ); #else SIMD_RETURN (bool8, a[i] != b[i] ? -1 : 0); #endif } OIIO_FORCEINLINE bool8 operator< (const float8& a, const float8& b) { #if OIIO_SIMD_AVX return _mm256_cmp_ps (a.m_simd, b.m_simd, _CMP_LT_OQ); #else SIMD_RETURN (bool8, a[i] < b[i] ? -1 : 0); #endif } OIIO_FORCEINLINE bool8 operator> (const float8& a, const float8& b) { #if OIIO_SIMD_AVX return _mm256_cmp_ps (a.m_simd, b.m_simd, _CMP_GT_OQ); #else SIMD_RETURN (bool8, a[i] > b[i] ? -1 : 0); #endif } OIIO_FORCEINLINE bool8 operator>= (const float8& a, const float8& b) { #if OIIO_SIMD_AVX return _mm256_cmp_ps (a.m_simd, b.m_simd, _CMP_GE_OQ); #else SIMD_RETURN (bool8, a[i] >= b[i] ? -1 : 0); #endif } OIIO_FORCEINLINE bool8 operator<= (const float8& a, const float8& b) { #if OIIO_SIMD_AVX return _mm256_cmp_ps (a.m_simd, b.m_simd, _CMP_LE_OQ); #else SIMD_RETURN (bool8, a[i] <= b[i] ? -1 : 0); #endif } // Implementation had to be after the definition of float8. OIIO_FORCEINLINE int8::int8 (const float8& f) { #if OIIO_SIMD_AVX m_simd = _mm256_cvttps_epi32(f); #else SIMD_CONSTRUCT ((int) f[i]); #endif } template OIIO_FORCEINLINE float8 shuffle (const float8& a) { #if OIIO_SIMD_AVX >= 2 int8 index (i0, i1, i2, i3, i4, i5, i6, i7); return _mm256_permutevar8x32_ps (a, index); #else return float8 (a[i0], a[i1], a[i2], a[i3], a[i4], a[i5], a[i6], a[i7]); #endif } template OIIO_FORCEINLINE float8 shuffle (const float8& a) { #if OIIO_SIMD_AVX >= 2 return _mm256_permutevar8x32_ps (a, int8(i)); #else return shuffle(a); #endif } template OIIO_FORCEINLINE float extract (const float8& v) { #if OIIO_SIMD_AVX_NO_FIXME // Looks like the fastest we can do it is to extract a float4, // shuffle its one element everywhere, then extract element 0. _m128 f4 = _mm256_extractf128_ps (i >> 2); int j = i & 3; return _mm_cvtss_f32(shuffle_sse(a.simd())); #else return v[i]; #endif } template OIIO_FORCEINLINE float8 insert (const float8& a, float val) { #if OIIO_SIMD_AVX_NO_FIXME return _mm256_insert_epi32 (a, val, i); #else float8 tmp = a; tmp[i] = val; return tmp; #endif } OIIO_FORCEINLINE float float8::x () const { return extract<0>(*this); } OIIO_FORCEINLINE float float8::y () const { return extract<1>(*this); } OIIO_FORCEINLINE float float8::z () const { return extract<2>(*this); } OIIO_FORCEINLINE float float8::w () const { return extract<3>(*this); } OIIO_FORCEINLINE void float8::set_x (float val) { *this = insert<0>(*this, val); } OIIO_FORCEINLINE void float8::set_y (float val) { *this = insert<1>(*this, val); } OIIO_FORCEINLINE void float8::set_z (float val) { *this = insert<2>(*this, val); } OIIO_FORCEINLINE void float8::set_w (float val) { *this = insert<3>(*this, val); } OIIO_FORCEINLINE int8 bitcast_to_int (const float8& x) { #if OIIO_SIMD_AVX return _mm256_castps_si256 (x.simd()); #else return *(int8 *)&x; #endif } OIIO_FORCEINLINE float8 bitcast_to_float (const int8& x) { #if OIIO_SIMD_AVX return _mm256_castsi256_ps (x.simd()); #else return *(float8 *)&x; #endif } OIIO_FORCEINLINE float8 vreduce_add (const float8& v) { #if OIIO_SIMD_AVX // From Syrah: float8 ab_cd_0_0_ef_gh_0_0 = _mm256_hadd_ps(v.simd(), _mm256_setzero_ps()); float8 abcd_0_0_0_efgh_0_0_0 = _mm256_hadd_ps(ab_cd_0_0_ef_gh_0_0, _mm256_setzero_ps()); // get efgh in the 0-idx slot float8 efgh = shuffle<4>(abcd_0_0_0_efgh_0_0_0); float8 final_sum = abcd_0_0_0_efgh_0_0_0 + efgh; return shuffle<0>(final_sum); #else float4 hadd4 = vreduce_add(v.lo()) + vreduce_add(v.hi()); return float8(hadd4, hadd4); #endif } OIIO_FORCEINLINE float reduce_add (const float8& v) { #if OIIO_SIMD_AVX >= 2 return extract<0>(vreduce_add(v)); #else return reduce_add(v.lo()) + reduce_add(v.hi()); #endif } OIIO_FORCEINLINE float8 blend (const float8& a, const float8& b, const bool8& mask) { #if OIIO_SIMD_AVX return _mm256_blendv_ps (a, b, mask); #elif defined(OIIO_SIMD_SSE) return float8 (blend (a.lo(), b.lo(), mask.lo()), blend (a.hi(), b.hi(), mask.hi())); #else SIMD_RETURN (float8, mask[i] ? b[i] : a[i]); #endif } OIIO_FORCEINLINE float8 blend0 (const float8& a, const bool8& mask) { #if OIIO_SIMD_AVX return _mm256_and_ps(mask, a); #elif defined(OIIO_SIMD_SSE) return float8 (blend0 (a.lo(), mask.lo()), blend0 (a.hi(), mask.hi())); #else SIMD_RETURN (float8, mask[i] ? a[i] : 0.0f); #endif } OIIO_FORCEINLINE float8 blend0not (const float8& a, const bool8& mask) { #if OIIO_SIMD_AVX return _mm256_andnot_ps(mask, a); #elif defined(OIIO_SIMD_SSE) return float8 (blend0not (a.lo(), mask.lo()), blend0not (a.hi(), mask.hi())); #else SIMD_RETURN (float8, mask[i] ? 0.0f : a[i]); #endif } OIIO_FORCEINLINE float8 safe_div (const float8 &a, const float8 &b) { #if OIIO_SIMD_SSE return blend0not (a/b, b == float8::Zero()); #else SIMD_RETURN (float8, b[i] == 0.0f ? 0.0f : a[i] / b[i]); #endif } OIIO_FORCEINLINE float8 select (const bool8& mask, const float8& a, const float8& b) { return blend (b, a, mask); } OIIO_FORCEINLINE float8 abs (const float8& a) { #if OIIO_SIMD_AVX // Just clear the sign bit for cheap fabsf return _mm256_and_ps (a.simd(), _mm256_castsi256_ps(_mm256_set1_epi32(0x7fffffff))); #else SIMD_RETURN (float8, fabsf(a[i])); #endif } OIIO_FORCEINLINE float8 sign (const float8& a) { float8 one(1.0f); return blend (one, -one, a < float8::Zero()); } OIIO_FORCEINLINE float8 ceil (const float8& a) { #if OIIO_SIMD_AVX return _mm256_ceil_ps (a); #else SIMD_RETURN (float8, ceilf(a[i])); #endif } OIIO_FORCEINLINE float8 floor (const float8& a) { #if OIIO_SIMD_AVX return _mm256_floor_ps (a); #else SIMD_RETURN (float8, floorf(a[i])); #endif } OIIO_FORCEINLINE float8 round (const float8& a) { #if OIIO_SIMD_AVX return _mm256_round_ps (a, (_MM_FROUND_TO_NEAREST_INT |_MM_FROUND_NO_EXC)); #else SIMD_RETURN (float8, roundf(a[i])); #endif } OIIO_FORCEINLINE int8 floori (const float8& a) { #if OIIO_SIMD_AVX return int8(floor(a)); #elif defined(OIIO_SIMD_SSE) /* SSE2/3 */ int8 i (a); // truncates int8 isneg = bitcast_to_int (a < float8::Zero()); return i + isneg; // The trick here (thanks, Cycles, for letting me spy on your code) is // that the comparison will return (int)-1 for components that are less // than zero, and adding that is the same as subtracting one! #else SIMD_RETURN (int8, (int)floorf(a[i])); #endif } OIIO_FORCEINLINE int8 rint (const float8& a) { return int8 (round(a)); } OIIO_FORCEINLINE float8 sqrt (const float8 &a) { #if OIIO_SIMD_AVX return _mm256_sqrt_ps (a.simd()); #else SIMD_RETURN (float8, sqrtf(a[i])); #endif } OIIO_FORCEINLINE float8 rsqrt (const float8 &a) { #if OIIO_SIMD_AVX return _mm256_div_ps (_mm256_set1_ps(1.0f), _mm256_sqrt_ps (a.simd())); #else SIMD_RETURN (float8, 1.0f/sqrtf(a[i])); #endif } OIIO_FORCEINLINE float8 rsqrt_fast (const float8 &a) { #if OIIO_SIMD_AVX return _mm256_rsqrt_ps (a.simd()); #else SIMD_RETURN (float8, 1.0f/sqrtf(a[i])); #endif } OIIO_FORCEINLINE float8 min (const float8& a, const float8& b) { #if OIIO_SIMD_AVX return _mm256_min_ps (a, b); #else SIMD_RETURN (float8, std::min (a[i], b[i])); #endif } OIIO_FORCEINLINE float8 max (const float8& a, const float8& b) { #if OIIO_SIMD_AVX return _mm256_max_ps (a, b); #else SIMD_RETURN (float8, std::max (a[i], b[i])); #endif } OIIO_FORCEINLINE float8 andnot (const float8& a, const float8& b) { #if OIIO_SIMD_AVX return _mm256_andnot_ps (a.simd(), b.simd()); #else const int *ai = (const int *)&a; const int *bi = (const int *)&b; return bitcast_to_float (int8(~(ai[0]) & bi[0], ~(ai[1]) & bi[1], ~(ai[2]) & bi[2], ~(ai[3]) & bi[3], ~(ai[4]) & bi[4], ~(ai[5]) & bi[5], ~(ai[6]) & bi[6], ~(ai[7]) & bi[7])); #endif } OIIO_FORCEINLINE float8 madd (const simd::float8& a, const simd::float8& b, const simd::float8& c) { #if OIIO_SIMD_AVX && OIIO_FMA_ENABLED // If we are sure _mm256_fmadd_ps intrinsic is available, use it. return _mm256_fmadd_ps (a, b, c); #else // Fallback: just use regular math and hope for the best. return a * b + c; #endif } OIIO_FORCEINLINE float8 msub (const simd::float8& a, const simd::float8& b, const simd::float8& c) { #if OIIO_SIMD_AVX && OIIO_FMA_ENABLED // If we are sure _mm256_fnmsub_ps intrinsic is available, use it. return _mm256_fmsub_ps (a, b, c); #else // Fallback: just use regular math and hope for the best. return a * b - c; #endif } OIIO_FORCEINLINE float8 nmadd (const simd::float8& a, const simd::float8& b, const simd::float8& c) { #if OIIO_SIMD_AVX && OIIO_FMA_ENABLED // If we are sure _mm256_fnmadd_ps intrinsic is available, use it. return _mm256_fnmadd_ps (a, b, c); #else // Fallback: just use regular math and hope for the best. return c - a * b; #endif } OIIO_FORCEINLINE float8 nmsub (const simd::float8& a, const simd::float8& b, const simd::float8& c) { #if OIIO_SIMD_AVX && OIIO_FMA_ENABLED // If we are sure _mm256_fnmsub_ps intrinsic is available, use it. return _mm256_fnmsub_ps (a, b, c); #else // Fallback: just use regular math and hope for the best. return -(a * b) - c; #endif } } // end namespace simd OIIO_NAMESPACE_END #undef SIMD_DO #undef SIMD_CONSTRUCT #undef SIMD_CONSTRUCT_PAD #undef SIMD_RETURN #undef SIMD_RETURN_REDUCE #endif /* OIIO_SIMD_H */ openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/timer.h0000644000175000017500000002733713151711064022140 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// @file timer.h /// @brief Simple timer class. #ifndef OPENIMAGEIO_TIMER_H #define OPENIMAGEIO_TIMER_H #include "oiioversion.h" #include "export.h" #include "platform.h" #ifdef _WIN32 //# include // Already done by platform.h #elif defined(__APPLE__) # include #else #include #include // Just for NULL definition #endif #include OIIO_NAMESPACE_BEGIN /// Simple timer class. /// /// This class allows you to time things, for runtime statistics and the /// like. The simplest usage pattern is illustrated by the following /// example: /// /// \code /// Timer mytimer; // automatically starts upon construction /// ...do stuff /// float t = mytimer(); // seconds elapsed since start /// /// Timer another (false); // false means don't start ticking yet /// another.start (); // start ticking now /// another.stop (); // stop ticking /// another.start (); // start again where we left off /// another.stop (); /// another.reset (); // reset to zero time again /// \endcode /// /// These are not very high-resolution timers. A Timer begin/end pair /// takes somewhere in the neighborhood of 0.1 - 0.3 us (microseconds), /// and can vary by OS. This means that (a) it's not useful for timing /// individual events near or below that resolution (things that would /// take only tens or hundreds of processor cycles, for example), and /// (b) calling it millions of times could make your program appreciably /// more expensive due to the timers themselves. /// class OIIO_API Timer { public: typedef long long ticks_t; enum StartNowVal { DontStartNow, StartNow }; enum PrintDtrVal { DontPrintDtr, PrintDtr }; /// Constructor -- reset at zero, and start timing unless optional /// 'startnow' argument is false. Timer (StartNowVal startnow=StartNow, PrintDtrVal printdtr=DontPrintDtr, const char *name=NULL) : m_ticking(false), m_printdtr(printdtr==PrintDtr), m_starttime(0), m_elapsed_ticks(0), m_name(name) { if (startnow == StartNow) start(); } /// Constructor -- reset at zero, and start timing unless optional /// 'startnow' argument is false. Timer (bool startnow) : m_ticking(false), m_printdtr(DontPrintDtr), m_starttime(0), m_elapsed_ticks(0), m_name(NULL) { if (startnow) start(); } /// Destructor. ~Timer () { if (m_printdtr == PrintDtr) std::cout << "Timer " << (m_name?m_name:"") << ": " << seconds(ticks()) << "s\n"; } /// Start (or restart) ticking, if we are not currently. void start () { if (! m_ticking) { m_starttime = now(); m_ticking = true; } } /// Stop ticking, return the total amount of time that has ticked /// (both this round as well as previous laps). Current ticks will /// be added to previous elapsed time. double stop () { if (m_ticking) { ticks_t n = now(); m_elapsed_ticks += tickdiff (m_starttime, n); m_ticking = false; } return seconds(m_elapsed_ticks); } /// Reset at zero and stop ticking. /// void reset (void) { m_elapsed_ticks = 0; m_ticking = false; } /// Return just the ticks of the current lap (since the last call to /// start() or lap()), add that to the previous elapsed time, reset /// current start time to now, keep the timer going (if it was). ticks_t lap_ticks () { ticks_t n = now(); ticks_t r = m_ticking ? tickdiff (m_starttime, n) : ticks_t(0); m_elapsed_ticks += r; m_starttime = n; m_ticking = true; return r; } /// Return just the time of the current lap (since the last call to /// start() or lap()), add that to the previous elapsed time, reset /// current start time to now, keep the timer going (if it was). double lap () { return seconds(lap_ticks()); } /// Total number of elapsed ticks so far, including both the currently- /// ticking clock as well as any previously elapsed time. ticks_t ticks () const { return ticks_since_start() + m_elapsed_ticks; } /// Operator () returns the elapsed time so far, in seconds, including /// both the currently-ticking clock as well as any previously elapsed /// time. double operator() (void) const { return seconds (ticks()); } /// Return just the ticks since we called start(), not any elapsed /// time in previous start-stop segments. ticks_t ticks_since_start (void) const { return m_ticking ? tickdiff (m_starttime, now()) : ticks_t(0); } /// Return just the time since we called start(), not any elapsed /// time in previous start-stop segments. double time_since_start (void) const { return seconds (ticks_since_start()); } /// Convert number of ticks to seconds. static double seconds (ticks_t ticks) { return ticks * seconds_per_tick; } /// Is the timer currently ticking? bool ticking () const { return m_ticking; } private: bool m_ticking; ///< Are we currently ticking? bool m_printdtr; ///< Print upon destruction? ticks_t m_starttime; ///< Time since last call to start() ticks_t m_elapsed_ticks; ///< Time elapsed BEFORE the current start(). const char *m_name; ///< Timer name /// Platform-dependent grab of current time, expressed as ticks_t. /// ticks_t now (void) const { #ifdef _WIN32 LARGE_INTEGER n; QueryPerformanceCounter (&n); // From MSDN web site return n.QuadPart; #elif defined(__APPLE__) return mach_absolute_time(); #else struct timeval t; gettimeofday (&t, NULL); return (long long) t.tv_sec*1000000ll + t.tv_usec; #endif } /// Difference between two times, expressed in (platform-dependent) /// ticks. ticks_t tickdiff (ticks_t then, ticks_t now) const { return (now>then) ? now-then : then-now; } /// Difference between two times, expressed in seconds. double diff (ticks_t then, ticks_t now) const { return seconds (tickdiff (then, now)); } static double seconds_per_tick; friend class TimerSetupOnce; }; /// Helper class that starts and stops a timer when the ScopedTimer goes /// in and out of scope. class ScopedTimer { public: /// Given a reference to a timer, start it when this constructor /// occurs. ScopedTimer (Timer &t) : m_timer(t) { start(); } /// Stop the timer from ticking when this object is destroyed (i.e. /// it leaves scope). ~ScopedTimer () { stop(); } /// Explicit start of the timer. /// void start () { m_timer.start(); } /// Explicit stop of the timer. /// void stop () { m_timer.stop(); } /// Explicit reset of the timer. /// void reset () { m_timer.reset(); } private: Timer &m_timer; }; /// DoNotOptimize(val) is a helper function for timing benchmarks that fools /// the compiler into thinking the the location 'val' is used and will not /// optimize it away. For benchmarks only, do not use in production code! /// May not work on all platforms. References: /// * Chandler Carruth's CppCon 2015 talk /// * Folly https://github.com/facebook/folly/blob/master/folly/Benchmark.h /// * Google Benchmark https://github.com/google/benchmark/blob/master/include/benchmark/benchmark_api.h #if __has_attribute(__optnone__) // If __optnone__ attribute is available: make a null function with no // optimization, that's all we need. template inline void __attribute__((__optnone__)) DoNotOptimize (T const &val) { } #elif ((OIIO_GNUC_VERSION && NDEBUG) || OIIO_CLANG_VERSION >= 30500 || OIIO_APPLE_CLANG_VERSION >= 70000 || defined(__INTEL_COMPILER)) && (defined(__x86_64__) || defined(__i386__)) // Major non-MS compilers on x86/x86_64: use asm trick to indicate that // the value is needed. template inline void DoNotOptimize (T const &val) { asm volatile("" : "+rm" (const_cast(val))); } #elif _MSC_VER // Microsoft of course has its own way of turning off optimizations. #pragma optimize("", off) template inline void DoNotOptimize (T const &val) { const_cast(val) = val; } #pragma optimize("", on) #else // Otherwise, it won't work, just make a stub. template inline void DoNotOptimize (T const &val) { } #endif /// clobber_all_memory() is a helper function for timing benchmarks that /// fools the compiler into thinking that potentially any part of memory /// has been modified, and thus serves as a barrier where the optimizer /// won't assume anything about the state of memory preceding it. #if ((OIIO_GNUC_VERSION && NDEBUG) || OIIO_CLANG_VERSION >= 30500 || OIIO_APPLE_CLANG_VERSION >= 70000 || defined(__INTEL_COMPILER)) && (defined(__x86_64__) || defined(__i386__)) // Special trick for x86/x86_64 and gcc-like compilers inline void clobber_all_memory() { asm volatile ("" : : : "memory"); } #else // No fallback for other CPUs or compilers. Suggestions? inline void clobber_all_memory() { } #endif /// Helper template that runs a function (or functor) n times, using a /// Timer to benchmark the results, and returning the fastest trial. If /// 'range' is non-NULL, the range (max-min) of the various time trials /// will be stored there. template double time_trial (FUNC func, int ntrials=1, int nrepeats = 1, double *range=NULL) { double mintime = 1.0e30, maxtime = 0.0; while (ntrials-- > 0) { Timer timer; for (int i = 0; i < nrepeats; ++i) { // Be sure that the repeated calls to func aren't optimzed away: clobber_all_memory(); func (); } double t = timer(); if (t < mintime) mintime = t; if (t > maxtime) maxtime = t; } if (range) *range = maxtime-mintime; return mintime; } /// Version without repeats. template double time_trial (FUNC func, int ntrials, double *range) { return time_trial (func, ntrials, 1, range); } OIIO_NAMESPACE_END #endif // OPENIMAGEIO_TIMER_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/filter.h0000644000175000017500000001273113151711064022275 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Based on BSD-licensed software Copyright 2004 NVIDIA Corp. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_FILTER_H #define OPENIMAGEIO_FILTER_H #include "oiioversion.h" #include "export.h" #include "string_view.h" OIIO_NAMESPACE_BEGIN /// Quick structure that describes a filter. /// class OIIO_API FilterDesc { public: const char *name; ///< name of the filter int dim; ///< dimensionality: 1 or 2 float width; ///< Recommended width or window bool fixedwidth; ///< Is the width the only one that makes sense? bool scalable; ///< Is it scalable (otherwise, the width is a window)? bool separable; ///< Is it separable? (only matters if dim==2) }; /// Filter1D is the abstract data type for a 1D filter. /// The filters are NOT expected to have their results normalized. class OIIO_API Filter1D { public: Filter1D (float width) : m_w(width) { } virtual ~Filter1D (void) { }; /// Get the width of the filter float width (void) const { return m_w; } /// Evalutate the filter at an x position (relative to filter center) virtual float operator() (float x) const = 0; /// Return the name of the filter, e.g., "box", "gaussian" virtual string_view name (void) const = 0; /// This static function allocates and returns an instance of the /// specific filter implementation for the name you provide. /// Example use: /// Filter1D *myfilt = Filter1::create ("box", 1); /// The caller is responsible for deleting it when it's done. /// If the name is not recognized, return NULL. static Filter1D *create (string_view filtername, float width); /// Destroy a filter that was created with create(). static void destroy (Filter1D *filt); /// Get the number of filters supported. static int num_filters (); /// Get the info for a particular index (0..num_filters()-1). static void get_filterdesc (int filternum, FilterDesc *filterdesc); protected: float m_w; }; /// Filter2D is the abstract data type for a 2D filter. /// The filters are NOT expected to have their results normalized. class OIIO_API Filter2D { public: Filter2D (float width, float height) : m_w(width), m_h(height) { } virtual ~Filter2D (void) { }; /// Get the width of the filter float width (void) const { return m_w; } /// Get the height of the filter float height (void) const { return m_h; } /// Is the filter separable? /// virtual bool separable () const { return false; } /// Evalutate the filter at an x and y position (relative to filter /// center). virtual float operator() (float x, float y) const = 0; /// Evaluate just the horizontal filter (if separable; for non-separable /// it just evaluates at (x,0). virtual float xfilt (float x) const { return (*this)(x,0.0f); } /// Evaluate just the vertical filter (if separable; for non-separable /// it just evaluates at (0,y). virtual float yfilt (float y) const { return (*this)(0.0f,y); } /// Return the name of the filter, e.g., "box", "gaussian" virtual string_view name (void) const = 0; /// This static function allocates and returns an instance of the /// specific filter implementation for the name you provide. /// Example use: /// Filter2D *myfilt = Filter2::create ("box", 1, 1); /// The caller is responsible for deleting it when it's done. /// If the name is not recognized, return NULL. static Filter2D *create (string_view filtername, float width, float height); /// Destroy a filter that was created with create(). static void destroy (Filter2D *filt); /// Get the number of filters supported. static int num_filters (); /// Get the info for a particular index (0..num_filters()-1). static void get_filterdesc (int filternum, FilterDesc *filterdesc); protected: float m_w, m_h; }; OIIO_NAMESPACE_END #endif // OPENIMAGEIO_FILTER_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/errorhandler.h0000644000175000017500000001516213151711064023500 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_ERRORMANAGER_H #define OPENIMAGEIO_ERRORMANAGER_H #include #include "export.h" #include "oiioversion.h" #include "strutil.h" OIIO_NAMESPACE_BEGIN /// ErrorHandler is a simple class that accepts error messages /// (classified as errors, severe errors, warnings, info, messages, or /// debug output) and handles them somehow. By default it just prints /// the messages to stdout and/or stderr (and supresses some based on a /// "verbosity" level). /// /// The basic idea is that your library code has no idea whether some /// application that will use it someday will want errors or other /// output to be sent to the console, go to a log file, be intercepted /// by the calling application, or something else. So you punt, by /// having your library take a pointer to an ErrorHandler, passed in /// from the calling app (and possibly subclassed to have arbitrarily /// different behavior from the default console output) and make all /// error-like output via the ErrorHandler*. /// class OIIO_API ErrorHandler { public: /// Error categories. We use broad categories in the high order bits. /// A library may just use these categories, or may create individual /// error codes as long as they have the right high bits to designate /// their category (file not found = ERROR + 1, etc.). enum ErrCode { EH_NO_ERROR = 0, // never sent to handler EH_MESSAGE = 0 << 16, EH_INFO = 1 << 16, EH_WARNING = 2 << 16, EH_ERROR = 3 << 16, EH_SEVERE = 4 << 16, EH_DEBUG = 5 << 16 }; /// VerbosityLevel controls how much detail the calling app wants. /// enum VerbosityLevel { QUIET = 0, ///< Show MESSAGE, SEVERE, ERROR only NORMAL = 1, ///< Show MESSAGE, SEVERE, ERROR, WARNING VERBOSE = 2 ///< Like NORMAL, but also show INFO }; ErrorHandler () : m_verbosity(NORMAL) { } virtual ~ErrorHandler () { } /// The main (or "full detail") method -- takes a code (with high /// bits being an ErrCode) and writes the message, with a prefix /// indicating the error category (no prefix for "MESSAGE") and /// error string. virtual void operator () (int errcode, const std::string &msg); /// Info message with printf-like formatted error message. /// Will not print unless verbosity >= VERBOSE. void info (const char *format, ...) OPENIMAGEIO_PRINTF_ARGS(2,3); /// Warning message with printf-like formatted error message. /// Will not print unless verbosity >= NORMAL (i.e. will suppress /// for QUIET). void warning (const char *format, ...) OPENIMAGEIO_PRINTF_ARGS(2,3); /// Error message with printf-like formatted error message. /// Will print regardless of verbosity. void error (const char *format, ...) OPENIMAGEIO_PRINTF_ARGS(2,3); /// Severe error message with printf-like formatted error message. /// Will print regardless of verbosity. void severe (const char *format, ...) OPENIMAGEIO_PRINTF_ARGS(2,3); /// Prefix-less message with printf-like formatted error message. /// Will not print if verbosity is QUIET. Also note that unlike /// the other routines, message() will NOT append a newline. void message (const char *format, ...) OPENIMAGEIO_PRINTF_ARGS(2,3); /// Debugging message with printf-like formatted error message. /// This will not produce any output if not in DEBUG mode, or /// if verbosity is QUIET. #ifndef NDEBUG void debug (const char *format, ...) OPENIMAGEIO_PRINTF_ARGS(2,3); #else void debug (const char * /*format*/, ...) OPENIMAGEIO_PRINTF_ARGS(2,3) { } #endif void vInfo (const char *format, va_list argptr); void vWarning (const char *format, va_list argptr); void vError (const char *format, va_list argptr); void vSevere (const char *format, va_list argptr); void vMessage (const char *format, va_list argptr); #ifndef NDEBUG void vDebug (const char *format, va_list argptr); #else void vDebug (const char *, va_list) { } #endif void info (const std::string &msg) { (*this)(EH_INFO, msg); } void warning (const std::string &msg) { (*this)(EH_WARNING, msg); } void error (const std::string &msg) { (*this)(EH_ERROR, msg); } void severe (const std::string &msg) { (*this)(EH_SEVERE, msg); } void message (const std::string &msg) { (*this)(EH_MESSAGE, msg); } #ifndef NDEBUG void debug (const std::string &msg) { (*this)(EH_DEBUG, msg); } #else void debug (const std::string &) { } #endif /// Set desired verbosity level. /// void verbosity (int v) { m_verbosity = v; } /// Return the current verbosity level. /// int verbosity () const { return m_verbosity; } /// One built-in handler that can always be counted on to be present /// and just echoes the error messages to the console (stdout or /// stderr, depending on the error category). static ErrorHandler & default_handler (); private: int m_verbosity; }; OIIO_NAMESPACE_END #endif /* !defined(OPENIMAGEIO_ERRORMANAGER_H) */ openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/typedesc.h0000644000175000017500000003674413151711064022642 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// \file /// The TypeDesc class is used to describe simple data types. #ifndef OPENIMAGEIO_TYPEDESC_H #define OPENIMAGEIO_TYPEDESC_H #if defined(_MSC_VER) // Ignore warnings about conditional expressions that always evaluate true // on a given platform but may evaluate differently on another. There's // nothing wrong with such conditionals. # pragma warning (disable : 4127) #endif #ifndef NULL #define NULL 0 #endif #include #include #include #include #include "export.h" #include "oiioversion.h" #include "dassert.h" #include "string_view.h" OIIO_NAMESPACE_BEGIN ///////////////////////////////////////////////////////////////////////////// /// A TypeDesc describes simple data types. /// /// It frequently comes up (in my experience, with renderers and image /// handling programs) that you want a way to describe data that is passed /// through APIs through blind pointers. These are some simple classes /// that provide a simple type descriptor system. This is not meant to /// be comprehensive -- for example, there is no provision for structs, /// unions, pointers, const, or 'nested' type definitions. Just simple /// integer and floating point, *common* aggregates such as 3-points, /// and reasonably-lengthed arrays thereof. /// ///////////////////////////////////////////////////////////////////////////// struct OIIO_API TypeDesc { /// BASETYPE is a simple enum for the C/C++ built-in types. /// enum BASETYPE { UNKNOWN, NONE, UCHAR, UINT8=UCHAR, CHAR, INT8=CHAR, USHORT, UINT16=USHORT, SHORT, INT16=SHORT, UINT, UINT32=UINT, INT, INT32=INT, ULONGLONG, UINT64=ULONGLONG, LONGLONG, INT64=LONGLONG, HALF, FLOAT, DOUBLE, STRING, PTR, LASTBASE }; /// AGGREGATE describes whether our type is a simple scalar of /// one of the BASETYPE's, or one of several simple aggregates. enum AGGREGATE { SCALAR=1, VEC2=2, VEC3=3, VEC4=4, MATRIX33=9, MATRIX44=16 }; /// VECSEMANTICS gives hints about what the data represent (for /// example, if a spatial vector, whether it should transform as /// a point, direction vector, or surface normal). enum VECSEMANTICS { NOXFORM=0, NOSEMANTICS=0, // no semantic hints COLOR, // color POINT, // spatial location VECTOR, // spatial direction NORMAL, // surface normal TIMECODE, // SMPTE timecode (should be int[2]) KEYCODE // SMPTE keycode (should be int[7]) }; unsigned char basetype; ///< C data type at the heart of our type unsigned char aggregate; ///< What kind of AGGREGATE is it? unsigned char vecsemantics; ///< What does the vec represent? unsigned char reserved; ///< Reserved for future expansion int arraylen; ///< Array length, 0 = not array, -1 = unsized /// Construct from a BASETYPE and optional aggregateness and /// transformation rules. TypeDesc (BASETYPE btype=UNKNOWN, AGGREGATE agg=SCALAR, VECSEMANTICS xform=NOXFORM) : basetype(static_cast(btype)), aggregate(static_cast(agg)), vecsemantics(static_cast(xform)), reserved(0), arraylen(0) { } /// Construct an array of a non-aggregate BASETYPE. /// TypeDesc (BASETYPE btype, int arraylength) : basetype(static_cast(btype)), aggregate(SCALAR), vecsemantics(NOXFORM), reserved(0), arraylen(arraylength) { } /// Construct an array from BASETYPE, AGGREGATE, and array length, /// with unspecified (or moot) vector transformation semantics. TypeDesc (BASETYPE btype, AGGREGATE agg, int arraylength) : basetype(static_cast(btype)), aggregate(static_cast(agg)), vecsemantics(NOXFORM), reserved(0), arraylen(arraylength) { } /// Construct an array from BASETYPE, AGGREGATE, VECSEMANTICS, and /// array length. TypeDesc (BASETYPE btype, AGGREGATE agg, VECSEMANTICS xform, int arraylength) : basetype(static_cast(btype)), aggregate(static_cast(agg)), vecsemantics(static_cast(xform)), reserved(0), arraylen(arraylength) { } /// Construct from a string (e.g., "float[3]"). If no valid /// type could be assembled, set base to UNKNOWN. TypeDesc (string_view typestring); /// Copy constructor. TypeDesc (const TypeDesc &t) : basetype(t.basetype), aggregate(t.aggregate), vecsemantics(t.vecsemantics), reserved(0), arraylen(t.arraylen) { } /// Return the name, for printing and whatnot. For example, /// "float", "int[5]", "normal" const char *c_str() const; friend std::ostream& operator<< (std::ostream& o, TypeDesc t) { o << t.c_str(); return o; } /// Return the number of elements: 1 if not an array, or the array /// length. size_t numelements () const { DASSERT_MSG (arraylen >= 0, "Called numelements() on TypeDesc " "of array with unspecified length (%d)", arraylen); return (arraylen >= 1 ? arraylen : 1); } /// Does this TypeDesc describe an array? bool is_array () const { return (arraylen != 0); } /// Does this TypeDesc describe an array, but whose length is not /// specified? bool is_unsized_array () const { return (arraylen < 0); } /// Does this TypeDesc describe an array, whose length is specified? bool is_sized_array () const { return (arraylen > 0); } /// Return the size, in bytes, of this type. /// size_t size () const { DASSERT_MSG (arraylen >= 0, "Called size() on TypeDesc " "of array with unspecified length (%d)", arraylen); size_t a = (size_t) (arraylen > 0 ? arraylen : 1); if (sizeof(size_t) > sizeof(int)) { // size_t has plenty of room for this multiplication return a * elementsize(); } else { // need overflow protection unsigned long long s = (unsigned long long) a * elementsize(); const size_t toobig = std::numeric_limits::max(); return s < toobig ? (size_t)s : toobig; } } /// Return the type of one element, i.e., strip out the array-ness. /// TypeDesc elementtype () const { TypeDesc t (*this); t.arraylen = 0; return t; } /// Return the size, in bytes, of one element of this type (that is, /// ignoring whether it's an array). size_t elementsize () const { return aggregate * basesize(); } // /// Return just the underlying C scalar type, i.e., strip out the // /// array-ness and the aggregateness. // BASETYPE basetype () const { return TypeDesc(base); } /// Return the base type size, i.e., stripped of both array-ness /// and aggregateness. size_t basesize () const; /// True if it's a floating-point type (versus a fundamentally /// integral type or something else like a string). bool is_floating_point () const; /// True if it's a signed type that allows for negative values. bool is_signed () const; /// Shortcut: is it UNKNOWN? bool is_unknown () const { return (basetype == UNKNOWN); } /// if (typespec) is the same as asking whether it's not UNKNOWN. operator bool () const { return (basetype != UNKNOWN); } /// Set *this to the type described in the string. Return the /// length of the part of the string that describes the type. If /// no valid type could be assembled, return 0 and do not modify /// *this. size_t fromstring (string_view typestring); /// Compare two TypeDesc values for equality. /// bool operator== (const TypeDesc &t) const { return basetype == t.basetype && aggregate == t.aggregate && vecsemantics == t.vecsemantics && arraylen == t.arraylen; } /// Compare two TypeDesc values for inequality. /// bool operator!= (const TypeDesc &t) const { return ! (*this == t); } /// Compare a TypeDesc to a basetype (it's the same if it has the /// same base type and is not an aggregate or an array). friend bool operator== (const TypeDesc &t, BASETYPE b) { return (BASETYPE)t.basetype == b && (AGGREGATE)t.aggregate == SCALAR && !t.is_array(); } friend bool operator== (BASETYPE b, const TypeDesc &t) { return (BASETYPE)t.basetype == b && (AGGREGATE)t.aggregate == SCALAR && !t.is_array(); } /// Compare a TypeDesc to a basetype (it's the same if it has the /// same base type and is not an aggregate or an array). friend bool operator!= (const TypeDesc &t, BASETYPE b) { return (BASETYPE)t.basetype != b || (AGGREGATE)t.aggregate != SCALAR || t.is_array(); } friend bool operator!= (BASETYPE b, const TypeDesc &t) { return (BASETYPE)t.basetype != b || (AGGREGATE)t.aggregate != SCALAR || t.is_array(); } /// TypeDesc's are equivalent if they are equal, or if their only /// inequality is differing vector semantics. friend bool equivalent (const TypeDesc &a, const TypeDesc &b) { return a.basetype == b.basetype && a.aggregate == b.aggregate && (a.arraylen == b.arraylen || (a.is_unsized_array() && b.is_sized_array()) || (a.is_sized_array() && b.is_unsized_array())); } /// Member version of equivalent bool equivalent (const TypeDesc &b) const { return this->basetype == b.basetype && this->aggregate == b.aggregate && (this->arraylen == b.arraylen || (this->is_unsized_array() && b.is_sized_array()) || (this->is_sized_array() && b.is_unsized_array())); } /// Is this a 3-vector aggregate (of the given type, float by default)? bool is_vec3 (BASETYPE b=FLOAT) const { return this->aggregate == VEC3 && this->basetype == b && !is_array(); } /// Is this a 3-vector aggregate (of the given type, float by default)? bool is_vec4 (BASETYPE b=FLOAT) const { return this->aggregate == VEC4 && this->basetype == b && !is_array(); } /// Demote the type to a non-array /// void unarray (void) { arraylen = 0; } /// Test for lexicographic 'less', comes in handy for lots of STL /// containers and algorithms. bool operator< (const TypeDesc &x) const; static const TypeDesc TypeFloat; static const TypeDesc TypeColor; static const TypeDesc TypeString; static const TypeDesc TypeInt; static const TypeDesc TypeHalf; static const TypeDesc TypePoint; static const TypeDesc TypeVector; static const TypeDesc TypeNormal; static const TypeDesc TypeMatrix; static const TypeDesc TypeMatrix33; static const TypeDesc TypeMatrix44; static const TypeDesc TypeTimeCode; static const TypeDesc TypeKeyCode; static const TypeDesc TypeFloat4; }; /// Return a string containing the data values formatted according /// to the type and the optional formatting arguments. std::string tostring (TypeDesc type, const void *data, const char *float_fmt = "%f", // E.g. "%g" const char *string_fmt = "%s", // E.g. "\"%s\"" const char aggregate_delim[2] = "()", // Both sides of vector const char *aggregate_sep = ",", // E.g. ", " const char array_delim[2] = "{}", // Both sides of array const char *array_sep = ","); // E.g. "; " /// A template mechanism for getting the a base type from C type /// template struct BaseTypeFromC {}; template<> struct BaseTypeFromC { static const TypeDesc::BASETYPE value = TypeDesc::UINT8; }; template<> struct BaseTypeFromC { static const TypeDesc::BASETYPE value = TypeDesc::INT8; }; template<> struct BaseTypeFromC { static const TypeDesc::BASETYPE value = TypeDesc::UINT16; }; template<> struct BaseTypeFromC { static const TypeDesc::BASETYPE value = TypeDesc::INT16; }; template<> struct BaseTypeFromC { static const TypeDesc::BASETYPE value = TypeDesc::UINT; }; template<> struct BaseTypeFromC { static const TypeDesc::BASETYPE value = TypeDesc::INT; }; template<> struct BaseTypeFromC { static const TypeDesc::BASETYPE value = TypeDesc::UINT64; }; template<> struct BaseTypeFromC { static const TypeDesc::BASETYPE value = TypeDesc::INT64; }; #ifdef _HALF_H_ template<> struct BaseTypeFromC { static const TypeDesc::BASETYPE value = TypeDesc::HALF; }; #endif template<> struct BaseTypeFromC { static const TypeDesc::BASETYPE value = TypeDesc::FLOAT; }; template<> struct BaseTypeFromC { static const TypeDesc::BASETYPE value = TypeDesc::DOUBLE; }; /// A template mechanism for getting C type of TypeDesc::BASETYPE. /// template struct CType {}; template<> struct CType<(int)TypeDesc::UINT8> { typedef unsigned char type; }; template<> struct CType<(int)TypeDesc::INT8> { typedef char type; }; template<> struct CType<(int)TypeDesc::UINT16> { typedef unsigned short type; }; template<> struct CType<(int)TypeDesc::INT16> { typedef short type; }; template<> struct CType<(int)TypeDesc::UINT> { typedef unsigned int type; }; template<> struct CType<(int)TypeDesc::INT> { typedef int type; }; template<> struct CType<(int)TypeDesc::UINT64> { typedef unsigned long long type; }; template<> struct CType<(int)TypeDesc::INT64> { typedef long long type; }; #ifdef _HALF_H_ template<> struct CType<(int)TypeDesc::HALF> { typedef half type; }; #endif template<> struct CType<(int)TypeDesc::FLOAT> { typedef float type; }; template<> struct CType<(int)TypeDesc::DOUBLE> { typedef double type; }; OIIO_NAMESPACE_END #endif /* !defined(OPENIMAGEIO_TYPEDESC_H) */ openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/plugin.h0000644000175000017500000000753213151711064022311 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ ///////////////////////////////////////////////////////////////////////// /// \file /// /// Helper routines for managing runtime-loadable "plugins", implemented /// variously as DSO's (traditional Unix/Linux), dynamic libraries (Mac /// OS X), DLL's (Windows). ///////////////////////////////////////////////////////////////////////// #ifndef OPENIMAGEIO_PLUGIN_H #define OPENIMAGEIO_PLUGIN_H #include #include "export.h" #include "oiioversion.h" OIIO_NAMESPACE_BEGIN namespace Plugin { typedef void * Handle; /// Return the platform-dependent suffix for plug-ins ("dll" on /// Windows, "so" on Linux, "dylib" on Mac OS X. OIIO_API const char *plugin_extension (void); /// Open the named plugin, return its handle. If it could not be /// opened, return 0 and the next call to geterror() will contain /// an explanatory message. If the 'global' parameter is true, all /// symbols from the plugin will be available to the app (on Unix-like /// platforms; this has no effect on Windows). OIIO_API Handle open (const char *plugin_filename, bool global=true); inline Handle open (const std::string &plugin_filename, bool global=true) { return open (plugin_filename.c_str(), global); } /// Close the open plugin with the given handle and return true upon /// success. If some error occurred, return false and the next call to /// geterror() will contain an explanatory message. OIIO_API bool close (Handle plugin_handle); /// Get the address of the named symbol from the open plugin handle. If /// some error occurred, return NULL and the next call to /// geterror() will contain an explanatory message. OIIO_API void * getsym (Handle plugin_handle, const char *symbol_name); inline void * getsym (Handle plugin_handle, const std::string &symbol_name) { return getsym (plugin_handle, symbol_name.c_str()); } /// Return any error messages associated with the last call to any of /// open, close, or getsym. Note that in a multithreaded environment, /// it's up to the caller to properly mutex to ensure that no other /// thread has called open, close, or getsym (all of which clear or /// overwrite the error message) between the error-generating call and /// geterror. OIIO_API std::string geterror (void); } // namespace Plugin OIIO_NAMESPACE_END #endif // OPENIMAGEIO_PLUGIN_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/pugiconfig.hpp0000644000175000017500000000527513151711064023507 0ustar mfvmfv/** * pugixml parser - version 1.2 * -------------------------------------------------------- * Copyright (C) 2006-2012, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) * Report bugs and download new versions at http://pugixml.org/ * * This library is distributed under the MIT License. See notice at the end * of this file. * * This work is based on the pugxml parser, which is: * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) */ #ifndef HEADER_PUGICONFIG_HPP #define HEADER_PUGICONFIG_HPP // Uncomment this to enable wchar_t mode // #define PUGIXML_WCHAR_MODE // Uncomment this to disable XPath // #define PUGIXML_NO_XPATH // Uncomment this to disable STL // #define PUGIXML_NO_STL // Uncomment this to disable exceptions // #define PUGIXML_NO_EXCEPTIONS // Set this to control attributes for public classes/functions, i.e.: // #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL // #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL // #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall // In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead // OIIO: use already defined OIIO_API #include "export.h" #define PUGIXML_API OIIO_API // Uncomment this to switch to header-only version #define PUGIXML_HEADER_ONLY #include "pugixml.cpp" // Tune these constants to adjust memory-related behavior // #define PUGIXML_MEMORY_PAGE_SIZE 32768 // #define PUGIXML_MEMORY_OUTPUT_STACK 10240 // #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096 #endif /** * Copyright (c) 2006-2012 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/color.h0000644000175000017500000003110113151711064022116 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_COLOR_H #define OPENIMAGEIO_COLOR_H #include "export.h" #include "oiioversion.h" #include "typedesc.h" #include "fmath.h" OIIO_NAMESPACE_BEGIN /// The ColorProcessor encapsulates a baked color transformation, suitable for /// application to raw pixels, or ImageBuf(s). These are generated using /// ColorConfig::createColorProcessor, and referenced in ImageBufAlgo /// (amongst other places) class OIIO_API ColorProcessor; /// Represents the set of all color transformations that are allowed. /// If OpenColorIO is enabled at build time, this configuration is loaded /// at runtime, allowing the user to have complete control of all color /// transformation math. ($OCIO) (See opencolorio.org for details). /// If OpenColorIO is not enabled at build time, a generic color configuration /// is provided for minimal color support. /// /// NOTE: ColorConfig(s) and ColorProcessor(s) are potentially heavy-weight. /// Their construction / destruction should be kept to a minimum. class OIIO_API ColorConfig { public: /// Construct a ColorConfig using the named OCIO configuration file, /// or if filename is empty, to the current color configuration /// specified by env variable $OCIO. /// /// Multiple calls to this are potentially expensive. A ColorConfig /// should usually be shared by an app for its entire runtime. ColorConfig (string_view filename = ""); ~ColorConfig(); /// Reset the config to the named OCIO configuration file, or if /// filename is empty, to the current color configuration specified /// by env variable $OCIO. Return true for success, false if there /// was an error. /// /// Multiple calls to this are potentially expensive. A ColorConfig /// should usually be shared by an app for its entire runtime. bool reset (string_view filename = ""); /// Has an error string occurred? /// (This will not affect the error state.) bool error () const; /// This routine will return the error string (and clear any error /// flags). If no error has occurred since the last time geterror() /// was called, it will return an empty string. std::string geterror (); /// Get the number of ColorSpace(s) defined in this configuration int getNumColorSpaces() const; /// Query the name of the specified ColorSpace. const char * getColorSpaceNameByIndex (int index) const; /// Get the name of the color space representing the named role, /// or NULL if none could be identified. const char * getColorSpaceNameByRole (string_view role) const; /// Get the data type that OCIO thinks this color space is. The name /// may be either a color space name or a role. OIIO::TypeDesc getColorSpaceDataType (string_view name, int *bits) const; /// Get the number of Looks defined in this configuration int getNumLooks() const; /// Query the name of the specified Look. const char * getLookNameByIndex (int index) const; /// Given the specified input and output ColorSpace, construct the /// processor. It is possible that this will return NULL, if the /// inputColorSpace doesnt exist, the outputColorSpace doesn't /// exist, or if the specified transformation is illegal (for /// example, it may require the inversion of a 3D-LUT, etc). When /// the user is finished with a ColorProcess, deleteColorProcessor /// should be called. ColorProcessor(s) remain valid even if the /// ColorConfig that created them no longer exists. /// /// Multiple calls to this are potentially expensive, so you should /// call once to create a ColorProcessor to use on an entire image /// (or multiple images), NOT for every scanline or pixel /// separately! ColorProcessor* createColorProcessor (string_view inputColorSpace, string_view outputColorSpace, string_view context_key /* ="" */, string_view context_value="") const; // DEPRECATED (1.7): ColorProcessor* createColorProcessor (string_view inputColorSpace, string_view outputColorSpace) const; /// Given the named look(s), input and output color spaces, construct a /// color processor that applies an OCIO look transformation. If /// inverse==true, construct the inverse transformation. The /// context_key and context_value can optionally be used to establish /// extra key/value pairs in the OCIO context if they are comma- /// separated lists of ontext keys and values, respectively. /// /// It is possible that this will return NULL, if one of the color /// spaces or the look itself doesnt exist or is not allowed. When /// the user is finished with a ColorProcess, deleteColorProcessor /// should be called. ColorProcessor(s) remain valid even if the /// ColorConfig that created them no longer exists. /// /// Multiple calls to this are potentially expensive, so you should /// call once to create a ColorProcessor to use on an entire image /// (or multiple images), NOT for every scanline or pixel /// separately! ColorProcessor* createLookTransform (string_view looks, string_view inputColorSpace, string_view outputColorSpace, bool inverse=false, string_view context_key="", string_view context_value="") const; /// Get the number of displays defined in this configuration int getNumDisplays() const; /// Query the name of the specified display. const char * getDisplayNameByIndex (int index) const; /// Get the number of views for a given display defined in this configuration int getNumViews (string_view display) const; /// Query the name of the specified view for the specified display const char * getViewNameByIndex (string_view display, int index) const; /// Query the name of the default display const char * getDefaultDisplayName() const; /// Query the name of the default view for the specified display const char * getDefaultViewName (string_view display) const; /// Construct a processor to transform from the given color space /// to the color space of the given display and view. You may optionally /// override the looks that are, by default, used with the display/view /// combination. Looks is a potentially comma (or colon) delimited list /// of lookNames, where +/- prefixes are optionally allowed to denote /// forward/inverse transformation (and forward is assumed in the /// absence of either). It is possible to remove all looks from the /// display by passing an empty string. The context_key and context_value /// can optionally be used to establish extra key/value pair in the OCIO /// context if they are comma-separated lists of context keys and /// values, respectively. /// /// It is possible that this will return NULL, if one of the color /// spaces or the display or view doesn't exist or is not allowed. When /// the user is finished with a ColorProcess, deleteColorProcessor /// should be called. ColorProcessor(s) remain valid even if the /// ColorConfig that created them no longer exists. /// /// Multiple calls to this are potentially expensive, so you should /// call once to create a ColorProcessor to use on an entire image /// (or multiple images), NOT for every scanline or pixel /// separately! ColorProcessor* createDisplayTransform (string_view display, string_view view, string_view inputColorSpace, string_view looks="", string_view context_key="", string_view context_value="") const; /// Construct a processor to perform color transforms determined by an /// OpenColorIO FileTransform. /// /// It is possible that this will return NULL, if the FileTransform /// doesn't exist or is not allowed. When the user is finished with a /// ColorProcess, deleteColorProcessor should be called. /// ColorProcessor(s) remain valid even if the ColorConfig that created /// them no longer exists. /// /// Multiple calls to this are potentially expensive, so you should /// call once to create a ColorProcessor to use on an entire image /// (or multiple images), NOT for every scanline or pixel /// separately! ColorProcessor* createFileTransform (string_view name, bool inverse=false) const; /// Given a string (like a filename), look for the longest, right-most /// colorspace substring that appears. Returns "" if no such color space /// is found. (This is just a wrapper around OCIO's /// ColorConfig::parseColorSpaceFromString.) string_view parseColorSpaceFromString (string_view str) const; /// Delete the specified ColorProcessor static void deleteColorProcessor(ColorProcessor * processor); /// Return if OpenImageIO was built with OCIO support static bool supportsOpenColorIO(); private: ColorConfig(const ColorConfig &); ColorConfig& operator= (const ColorConfig &); class Impl; friend class Impl; Impl * m_impl; Impl * getImpl() { return m_impl; } const Impl * getImpl() const { return m_impl; } }; /// Utility -- convert sRGB value to linear /// http://en.wikipedia.org/wiki/SRGB inline float sRGB_to_linear (float x) { return (x <= 0.04045f) ? (x * (1.0f/12.92f)) : powf ((x + 0.055f) * (1.0f / 1.055f), 2.4f); } inline simd::float4 sRGB_to_linear (const simd::float4& x) { return simd::select (x <= 0.04045f, x * (1.0f/12.92f), fast_pow_pos (madd (x, (1.0f / 1.055f), 0.055f*(1.0f/1.055f)), 2.4f)); } /// Utility -- convert linear value to sRGB inline float linear_to_sRGB (float x) { return (x <= 0.0031308f) ? (12.92f * x) : (1.055f * powf (x, 1.f/2.4f) - 0.055f); } /// Utility -- convert linear value to sRGB inline simd::float4 linear_to_sRGB (const simd::float4& x) { // x = simd::max (x, simd::float4::Zero()); return simd::select (x <= 0.0031308f, 12.92f * x, madd (1.055f, fast_pow_pos (x, 1.f/2.4f), -0.055f)); } /// Utility -- convert Rec709 value to linear /// http://en.wikipedia.org/wiki/Rec._709 inline float Rec709_to_linear (float x) { if (x < 0.081f) return x * (1.0f/4.5f); else return powf ((x + 0.099f) * (1.0f/1.099f), (1.0f/0.45f)); } /// Utility -- convert linear value to Rec709 inline float linear_to_Rec709 (float x) { if (x < 0.018f) return x * 4.5f; else return 1.099f * powf(x, 0.45f) - 0.099f; } OIIO_NAMESPACE_END #endif // OPENIMAGEIO_COLOR_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/image_view.h0000644000175000017500000001436713151711064023133 0ustar mfvmfv/* Copyright 2014 Larry Gritz and the other authors and contributors. All Rights Reserved. Based on BSD-licensed software Copyright 2004 NVIDIA Corp. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #pragma once #include #include // We're including stdint.h to get int64_t and INT64_MIN. But on some // platforms, stdint.h only defines them if __STDC_LIMIT_MACROS is defined, // so we do so. But, oops, if user code included stdint.h before this file, // and without defining the macro, it may have had ints one and only include // and not seen the definitions we need, so at least try to make a helpful // compile-time error in that case. // And very old MSVC 9 versions don't even have stdint.h. #if defined(_MSC_VER) && _MSC_VER < 1600 typedef __int64 int64_t; #else # ifndef __STDC_LIMIT_MACROS # define __STDC_LIMIT_MACROS /* needed for some defs in stdint.h */ # endif # include # if ! defined(INT64_MIN) # error You must define __STDC_LIMIT_MACROS prior to including stdint.h # endif #endif #include "oiioversion.h" #include "strided_ptr.h" OIIO_NAMESPACE_BEGIN /// image_view : a non-owning reference to an image-like array (indexed by /// x, y, z, and channel) with known dimensions and optionally non-default /// strides (expressed in bytes) through the data. An image_view is /// mutable (the values in the image may be modified), whereas an /// image_view is not mutable. template class image_view { public: typedef T value_type; typedef T& reference; typedef const T& const_reference; typedef int64_t stride_t; #ifdef INT64_MIN static const stride_t AutoStride = INT64_MIN; #else // Some systems don't have INT64_MIN defined. Sheesh. static const stride_t AutoStride = (-9223372036854775807LL-1); #endif /// Default ctr -- points to nothing image_view () { init(); } /// Copy constructor image_view (const image_view ©) { init (copy.m_data, copy.m_nchannels,copy.m_width, copy.m_height, copy.m_depth, copy.m_chanstride, copy.m_xstride, copy.m_ystride, copy.m_zstride); } /// Construct from T*, dimensions, and (possibly default) strides (in /// bytes). image_view (T *data, int nchannels, int width, int height, int depth=1, stride_t chanstride=AutoStride, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride) { init (data, nchannels, width, height, depth, chanstride, xstride, ystride, zstride); } /// assignments -- not a deep copy, just make this image_view /// point to the same data as the operand. image_view& operator= (const image_view ©) { init (copy.m_data, copy.m_nchannels,copy.m_width, copy.m_height, copy.m_depth, copy.m_chanstride, copy.m_xstride, copy.m_ystride, copy.m_zstride); return *this; } /// iav(x,y,z)returns a strided_ptr for the pixel (x,y,z). The z /// can be omitted for 2D images. Note than the resulting /// strided_ptr can then have individual channels accessed with /// operator[]. strided_ptr operator() (int x, int y, int z=0) { return strided_ptr (getptr(0,x,y,z), m_chanstride); } int nchannels() const { return m_nchannels; } int width() const { return m_width; } int height() const { return m_height; } int depth() const { return m_depth; } stride_t chanstride() const { return m_chanstride; } stride_t xstride() const { return m_xstride; } stride_t ystride() const { return m_ystride; } stride_t zstride() const { return m_zstride; } const T* data() const { return m_data; } void clear() { init(); } private: const T * m_data; int m_nchannels, m_width, m_height, m_depth; stride_t m_chanstride, m_xstride, m_ystride, m_zstride; void init (T *data, int nchannels, int width, int height, int depth=1, stride_t chanstride=AutoStride, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride) { m_data = data; m_nchannels = nchannels; m_width = width; m_height = height; m_depth = depth; m_chanstride = chanstride != AutoStride ? chanstride : sizeof(T); m_xstride = xstride != AutoStride ? xstride : m_nchannels * m_chanstride; m_ystride = ystride != AutoStride ? ystride : m_width * m_xstride; m_zstride = zstride != AutoStride ? zstride : m_height * m_ystride; } inline T* getptr (int c, int x, int y, int z=0) const { return (T*)((char *)m_data + c*m_chanstride + x*m_xstride + y*m_ystride + z*m_zstride); } inline T& get (int c, int x, int y, int z=0) const { return *getptr (c, x, y, z); } }; OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/tinyformat.h0000644000175000017500000012607713151711064023215 0ustar mfvmfv// tinyformat.h // Copyright (C) 2011, Chris Foster [chris42f (at) gmail (d0t) com] // // Boost Software License - Version 1.0 // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. //------------------------------------------------------------------------------ // Tinyformat: A minimal type safe printf replacement // // tinyformat.h is a type safe printf replacement library in a single C++ // header file. Design goals include: // // * Type safety and extensibility for user defined types. // * C99 printf() compatibility, to the extent possible using std::ostream // * Simplicity and minimalism. A single header file to include and distribute // with your projects. // * Augment rather than replace the standard stream formatting mechanism // * C++98 support, with optional C++11 niceties // // // Main interface example usage // ---------------------------- // // To print a date to std::cout: // // std::string weekday = "Wednesday"; // const char* month = "July"; // size_t day = 27; // long hour = 14; // int min = 44; // // tfm::printf("%s, %s %d, %.2d:%.2d\n", weekday, month, day, hour, min); // // The strange types here emphasize the type safety of the interface; it is // possible to print a std::string using the "%s" conversion, and a // size_t using the "%d" conversion. A similar result could be achieved // using either of the tfm::format() functions. One prints on a user provided // stream: // // tfm::format(std::cerr, "%s, %s %d, %.2d:%.2d\n", // weekday, month, day, hour, min); // // The other returns a std::string: // // std::string date = tfm::format("%s, %s %d, %.2d:%.2d\n", // weekday, month, day, hour, min); // std::cout << date; // // These are the three primary interface functions. // // // User defined format functions // ----------------------------- // // Simulating variadic templates in C++98 is pretty painful since it requires // writing out the same function for each desired number of arguments. To make // this bearable tinyformat comes with a set of macros which are used // internally to generate the API, but which may also be used in user code. // // The three macros TINYFORMAT_ARGTYPES(n), TINYFORMAT_VARARGS(n) and // TINYFORMAT_PASSARGS(n) will generate a list of n argument types, // type/name pairs and argument names respectively when called with an integer // n between 1 and 16. We can use these to define a macro which generates the // desired user defined function with n arguments. To generate all 16 user // defined function bodies, use the macro TINYFORMAT_FOREACH_ARGNUM. For an // example, see the implementation of printf() at the end of the source file. // // // Additional API information // -------------------------- // // Error handling: Define TINYFORMAT_ERROR to customize the error handling for // format strings which are unsupported or have the wrong number of format // specifiers (calls assert() by default). // // User defined types: Uses operator<< for user defined types by default. // Overload formatValue() for more control. #ifndef TINYFORMAT_H_INCLUDED #define TINYFORMAT_H_INCLUDED namespace tinyformat {} //------------------------------------------------------------------------------ // Config section. Customize to your liking! // Namespace alias to encourage brevity namespace tfm = tinyformat; // Error handling; calls assert() by default. // #define TINYFORMAT_ERROR(reasonString) your_error_handler(reasonString) // Define for C++11 variadic templates which make the code shorter & more // general. If you don't define this, C++11 support is autodetected below. // #define TINYFORMAT_USE_VARIADIC_TEMPLATES //------------------------------------------------------------------------------ // Implementation details. #include #include #include #ifndef TINYFORMAT_ERROR # define TINYFORMAT_ERROR(reason) assert(0 && reason) #endif #if !defined(TINYFORMAT_USE_VARIADIC_TEMPLATES) && !defined(TINYFORMAT_NO_VARIADIC_TEMPLATES) # ifdef __GXX_EXPERIMENTAL_CXX0X__ # define TINYFORMAT_USE_VARIADIC_TEMPLATES # endif #endif #ifdef __GNUC__ # define TINYFORMAT_NOINLINE __attribute__((noinline)) #elif defined(_MSC_VER) # define TINYFORMAT_NOINLINE __declspec(noinline) #else # define TINYFORMAT_NOINLINE #endif #if defined(__GLIBCXX__) && __GLIBCXX__ < 20080201 // std::showpos is broken on old libstdc++ as provided with OSX. See // http://gcc.gnu.org/ml/libstdc++/2007-11/msg00075.html # define TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND #endif namespace tinyformat { //------------------------------------------------------------------------------ namespace detail { // Test whether type T1 is convertible to type T2 template struct is_convertible { private: // two types of different size struct fail { char dummy[2]; }; struct succeed { char dummy; }; // Try to convert a T1 to a T2 by plugging into tryConvert static fail tryConvert(...); static succeed tryConvert(const T2&); static const T1& makeT1(); public: # ifdef _MSC_VER // Disable spurious loss of precision warnings in tryConvert(makeT1()) # pragma warning(push) # pragma warning(disable:4244) # pragma warning(disable:4267) # endif // Standard trick: the (...) version of tryConvert will be chosen from // the overload set only if the version taking a T2 doesn't match. // Then we compare the sizes of the return types to check which // function matched. Very neat, in a disgusting kind of way :) static const bool value = sizeof(tryConvert(makeT1())) == sizeof(succeed); # ifdef _MSC_VER # pragma warning(pop) # endif }; // Detect when a type is not a wchar_t string template struct is_wchar { typedef int tinyformat_wchar_is_not_supported; }; template<> struct is_wchar {}; template<> struct is_wchar {}; template struct is_wchar {}; template struct is_wchar {}; // Format the value by casting to type fmtT. This default implementation // should never be called. template::value> struct formatValueAsType { static void invoke(std::ostream& /*out*/, const T& /*value*/) { assert(0); } }; // Specialized version for types that can actually be converted to fmtT, as // indicated by the "convertible" template parameter. template struct formatValueAsType { static void invoke(std::ostream& out, const T& value) { out << static_cast(value); } }; #ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND template::value> struct formatZeroIntegerWorkaround { static bool invoke(std::ostream& /**/, const T& /**/) { return false; } }; template struct formatZeroIntegerWorkaround { static bool invoke(std::ostream& out, const T& value) { if (static_cast(value) == 0 && out.flags() & std::ios::showpos) { out << "+0"; return true; } return false; } }; #endif // TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND // Convert an arbitrary type to integer. The version with convertible=false // throws an error. template::value> struct convertToInt { static int invoke(const T& /*value*/) { TINYFORMAT_ERROR("tinyformat: Cannot convert from argument type to " "integer for use as variable width or precision"); return 0; } }; // Specialization for convertToInt when conversion is possible template struct convertToInt { static int invoke(const T& value) { return static_cast(value); } }; } // namespace detail //------------------------------------------------------------------------------ // Variable formatting functions. May be overridden for user-defined types if // desired. // Format a value into a stream. Called from format() for all types by default. // // Users may override this for their own types. When this function is called, // the stream flags will have been modified according to the format string. // The format specification is provided in the range [fmtBegin, fmtEnd). // // By default, formatValue() uses the usual stream insertion operator // operator<< to format the type T, with special cases for the %c and %p // conversions. template inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, const T& value) { #ifndef TINYFORMAT_ALLOW_WCHAR_STRINGS // Since we don't support printing of wchar_t using "%ls", make it fail at // compile time in preference to printing as a void* at runtime. typedef typename detail::is_wchar::tinyformat_wchar_is_not_supported DummyType; (void) DummyType(); // avoid unused type warning with gcc-4.8 #endif // The mess here is to support the %c and %p conversions: if these // conversions are active we try to convert the type to a char or const // void* respectively and format that instead of the value itself. For the // %p conversion it's important to avoid dereferencing the pointer, which // could otherwise lead to a crash when printing a dangling (const char*). const bool canConvertToChar = detail::is_convertible::value; const bool canConvertToVoidPtr = detail::is_convertible::value; if(canConvertToChar && *(fmtEnd-1) == 'c') detail::formatValueAsType::invoke(out, value); else if(canConvertToVoidPtr && *(fmtEnd-1) == 'p') detail::formatValueAsType::invoke(out, value); #ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND else if(detail::formatZeroIntegerWorkaround::invoke(out, value)) /**/; #endif else out << value; } // Overloaded version for char types to support printing as an integer #define TINYFORMAT_DEFINE_FORMATVALUE_CHAR(charType) \ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, \ const char* fmtEnd, charType value) \ { \ switch(*(fmtEnd-1)) \ { \ case 'u': case 'd': case 'i': case 'o': case 'X': case 'x': \ out << static_cast(value); break; \ default: \ out << value; break; \ } \ } // per 3.9.1: char, signed char and unsigned char are all distinct types TINYFORMAT_DEFINE_FORMATVALUE_CHAR(char) TINYFORMAT_DEFINE_FORMATVALUE_CHAR(signed char) TINYFORMAT_DEFINE_FORMATVALUE_CHAR(unsigned char) #undef TINYFORMAT_DEFINE_FORMATVALUE_CHAR //------------------------------------------------------------------------------ // Tools for emulating variadic templates in C++98. The basic idea here is // stolen from the boost preprocessor metaprogramming library and cut down to // be just general enough for what we need. #define TINYFORMAT_ARGTYPES(n) TINYFORMAT_ARGTYPES_ ## n #define TINYFORMAT_VARARGS(n) TINYFORMAT_VARARGS_ ## n #define TINYFORMAT_PASSARGS(n) TINYFORMAT_PASSARGS_ ## n #define TINYFORMAT_PASSARGS_TAIL(n) TINYFORMAT_PASSARGS_TAIL_ ## n // To keep it as transparent as possible, the macros below have been generated // using python via the excellent cog.py code generation script. This avoids // the need for a bunch of complex (but more general) preprocessor tricks as // used in boost.preprocessor. // // To rerun the code generation in place, use `cog.py -r tinyformat.h` // (see http://nedbatchelder.com/code/cog). Alternatively you can just create // extra versions by hand. /*[[[cog maxParams = 16 def makeCommaSepLists(lineTemplate, elemTemplate, startInd=1): for j in range(startInd,maxParams+1): list = ', '.join([elemTemplate % {'i':i} for i in range(startInd,j+1)]) cog.outl(lineTemplate % {'j':j, 'list':list}) makeCommaSepLists('#define TINYFORMAT_ARGTYPES_%(j)d %(list)s', 'class T%(i)d') cog.outl() makeCommaSepLists('#define TINYFORMAT_VARARGS_%(j)d %(list)s', 'const T%(i)d& v%(i)d') cog.outl() makeCommaSepLists('#define TINYFORMAT_PASSARGS_%(j)d %(list)s', 'v%(i)d') cog.outl() cog.outl('#define TINYFORMAT_PASSARGS_TAIL_1') makeCommaSepLists('#define TINYFORMAT_PASSARGS_TAIL_%(j)d , %(list)s', 'v%(i)d', startInd = 2) cog.outl() cog.outl('#define TINYFORMAT_FOREACH_ARGNUM(m) \\\n ' + ' '.join(['m(%d)' % (j,) for j in range(1,maxParams+1)])) ]]]*/ #define TINYFORMAT_ARGTYPES_1 class T1 #define TINYFORMAT_ARGTYPES_2 class T1, class T2 #define TINYFORMAT_ARGTYPES_3 class T1, class T2, class T3 #define TINYFORMAT_ARGTYPES_4 class T1, class T2, class T3, class T4 #define TINYFORMAT_ARGTYPES_5 class T1, class T2, class T3, class T4, class T5 #define TINYFORMAT_ARGTYPES_6 class T1, class T2, class T3, class T4, class T5, class T6 #define TINYFORMAT_ARGTYPES_7 class T1, class T2, class T3, class T4, class T5, class T6, class T7 #define TINYFORMAT_ARGTYPES_8 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8 #define TINYFORMAT_ARGTYPES_9 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9 #define TINYFORMAT_ARGTYPES_10 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10 #define TINYFORMAT_ARGTYPES_11 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11 #define TINYFORMAT_ARGTYPES_12 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12 #define TINYFORMAT_ARGTYPES_13 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13 #define TINYFORMAT_ARGTYPES_14 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14 #define TINYFORMAT_ARGTYPES_15 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 #define TINYFORMAT_ARGTYPES_16 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15, class T16 #define TINYFORMAT_VARARGS_1 const T1& v1 #define TINYFORMAT_VARARGS_2 const T1& v1, const T2& v2 #define TINYFORMAT_VARARGS_3 const T1& v1, const T2& v2, const T3& v3 #define TINYFORMAT_VARARGS_4 const T1& v1, const T2& v2, const T3& v3, const T4& v4 #define TINYFORMAT_VARARGS_5 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5 #define TINYFORMAT_VARARGS_6 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6 #define TINYFORMAT_VARARGS_7 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7 #define TINYFORMAT_VARARGS_8 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8 #define TINYFORMAT_VARARGS_9 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9 #define TINYFORMAT_VARARGS_10 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10 #define TINYFORMAT_VARARGS_11 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11 #define TINYFORMAT_VARARGS_12 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12 #define TINYFORMAT_VARARGS_13 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13 #define TINYFORMAT_VARARGS_14 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14 #define TINYFORMAT_VARARGS_15 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14, const T15& v15 #define TINYFORMAT_VARARGS_16 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14, const T15& v15, const T16& v16 #define TINYFORMAT_PASSARGS_1 v1 #define TINYFORMAT_PASSARGS_2 v1, v2 #define TINYFORMAT_PASSARGS_3 v1, v2, v3 #define TINYFORMAT_PASSARGS_4 v1, v2, v3, v4 #define TINYFORMAT_PASSARGS_5 v1, v2, v3, v4, v5 #define TINYFORMAT_PASSARGS_6 v1, v2, v3, v4, v5, v6 #define TINYFORMAT_PASSARGS_7 v1, v2, v3, v4, v5, v6, v7 #define TINYFORMAT_PASSARGS_8 v1, v2, v3, v4, v5, v6, v7, v8 #define TINYFORMAT_PASSARGS_9 v1, v2, v3, v4, v5, v6, v7, v8, v9 #define TINYFORMAT_PASSARGS_10 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10 #define TINYFORMAT_PASSARGS_11 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11 #define TINYFORMAT_PASSARGS_12 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12 #define TINYFORMAT_PASSARGS_13 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13 #define TINYFORMAT_PASSARGS_14 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14 #define TINYFORMAT_PASSARGS_15 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 #define TINYFORMAT_PASSARGS_16 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 #define TINYFORMAT_PASSARGS_TAIL_1 #define TINYFORMAT_PASSARGS_TAIL_2 , v2 #define TINYFORMAT_PASSARGS_TAIL_3 , v2, v3 #define TINYFORMAT_PASSARGS_TAIL_4 , v2, v3, v4 #define TINYFORMAT_PASSARGS_TAIL_5 , v2, v3, v4, v5 #define TINYFORMAT_PASSARGS_TAIL_6 , v2, v3, v4, v5, v6 #define TINYFORMAT_PASSARGS_TAIL_7 , v2, v3, v4, v5, v6, v7 #define TINYFORMAT_PASSARGS_TAIL_8 , v2, v3, v4, v5, v6, v7, v8 #define TINYFORMAT_PASSARGS_TAIL_9 , v2, v3, v4, v5, v6, v7, v8, v9 #define TINYFORMAT_PASSARGS_TAIL_10 , v2, v3, v4, v5, v6, v7, v8, v9, v10 #define TINYFORMAT_PASSARGS_TAIL_11 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11 #define TINYFORMAT_PASSARGS_TAIL_12 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12 #define TINYFORMAT_PASSARGS_TAIL_13 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13 #define TINYFORMAT_PASSARGS_TAIL_14 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14 #define TINYFORMAT_PASSARGS_TAIL_15 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 #define TINYFORMAT_PASSARGS_TAIL_16 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 #define TINYFORMAT_FOREACH_ARGNUM(m) \ m(1) m(2) m(3) m(4) m(5) m(6) m(7) m(8) m(9) m(10) m(11) m(12) m(13) m(14) m(15) m(16) //[[[end]]] namespace detail { // Class holding current position in format string and an output stream into // which arguments are formatted. class FormatIterator { public: // Flags for features not representable with standard stream state enum ExtraFormatFlags { Flag_None = 0, Flag_TruncateToPrecision = 1<<0, // truncate length to stream precision() Flag_SpacePadPositive = 1<<1, // pad positive values with spaces Flag_VariableWidth = 1<<2, // variable field width in arg list Flag_VariablePrecision = 1<<3 // variable field precision in arg list }; // out is the output stream, fmt is the full format string FormatIterator(std::ostream& out, const char* fmt) : m_out(out), m_fmt(fmt), m_extraFlags(Flag_None), m_wantWidth(false), m_wantPrecision(false), m_variableWidth(0), m_variablePrecision(0), m_origWidth(out.width()), m_origPrecision(out.precision()), m_origFlags(out.flags()), m_origFill(out.fill()) { } // Print remaining part of format string. void finish() { // It would be nice if we could do this from the destructor, but we // can't if TINFORMAT_ERROR is used to throw an exception! m_fmt = printFormatStringLiteral(m_out, m_fmt); if(*m_fmt != '\0') TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string"); } ~FormatIterator() { // Restore stream state m_out.width(m_origWidth); m_out.precision(m_origPrecision); m_out.flags(m_origFlags); m_out.fill(m_origFill); } template void accept(const T& value); private: // Parse and return an integer from the string c, as atoi() // On return, c is set to one past the end of the integer. static int parseIntAndAdvance(const char*& c) { int i = 0; for(;*c >= '0' && *c <= '9'; ++c) i = 10*i + (*c - '0'); return i; } // Format at most truncLen characters of a C string to the given // stream. Return true if formatting proceeded (generic version always // returns false) template static bool formatCStringTruncate(std::ostream& /*out*/, const T& /*value*/, std::streamsize /*truncLen*/) { return false; } # define TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE(type) \ static bool formatCStringTruncate(std::ostream& out, type* value, \ std::streamsize truncLen) \ { \ std::streamsize len = 0; \ while(len < truncLen && value[len] != 0) \ ++len; \ out.write(value, len); \ return true; \ } // Overload for const char* and char*. Could overload for signed & // unsigned char too, but these are technically unneeded for printf // compatibility. TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE(const char) TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE(char) # undef TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE // Print literal part of format string and return next format spec // position. // // Skips over any occurrences of '%%', printing a literal '%' to the // output. The position of the first % character of the next // nontrivial format spec is returned, or the end of string. static const char* printFormatStringLiteral(std::ostream& out, const char* fmt) { const char* c = fmt; for(; true; ++c) { switch(*c) { case '\0': out.write(fmt, static_cast(c - fmt)); return c; case '%': out.write(fmt, static_cast(c - fmt)); if(*(c+1) != '%') return c; // for "%%", tack trailing % onto next literal section. fmt = ++c; break; } } } static const char* streamStateFromFormat(std::ostream& out, unsigned int& extraFlags, const char* fmtStart, int variableWidth, int variablePrecision); // Private copy & assign: Kill gcc warnings with -Weffc++ FormatIterator(const FormatIterator&); FormatIterator& operator=(const FormatIterator&); // Stream, current format string & state std::ostream& m_out; const char* m_fmt; unsigned int m_extraFlags; // State machine info for handling of variable width & precision bool m_wantWidth; bool m_wantPrecision; int m_variableWidth; int m_variablePrecision; // Saved stream state std::streamsize m_origWidth; std::streamsize m_origPrecision; std::ios::fmtflags m_origFlags; char m_origFill; }; // Accept a value for formatting into the internal stream. template TINYFORMAT_NOINLINE // < greatly reduces bloat in optimized builds void FormatIterator::accept(const T& value) { // Parse the format string const char* fmtEnd = 0; if(m_extraFlags == Flag_None && !m_wantWidth && !m_wantPrecision) { m_fmt = printFormatStringLiteral(m_out, m_fmt); fmtEnd = streamStateFromFormat(m_out, m_extraFlags, m_fmt, 0, 0); m_wantWidth = (m_extraFlags & Flag_VariableWidth) != 0; m_wantPrecision = (m_extraFlags & Flag_VariablePrecision) != 0; } // Consume value as variable width and precision specifier if necessary if(m_extraFlags & (Flag_VariableWidth | Flag_VariablePrecision)) { if(m_wantWidth || m_wantPrecision) { int v = convertToInt::invoke(value); if(m_wantWidth) { m_variableWidth = v; m_wantWidth = false; } else if(m_wantPrecision) { m_variablePrecision = v; m_wantPrecision = false; } return; } // If we get here, we've set both the variable precision and width as // required and we need to rerun the stream state setup to insert these. fmtEnd = streamStateFromFormat(m_out, m_extraFlags, m_fmt, m_variableWidth, m_variablePrecision); } // Format the value into the stream. if(!(m_extraFlags & (Flag_SpacePadPositive | Flag_TruncateToPrecision))) formatValue(m_out, m_fmt, fmtEnd, value); else { // The following are special cases where there's no direct // correspondence between stream formatting and the printf() behaviour. // Instead, we simulate the behaviour crudely by formatting into a // temporary string stream and munging the resulting string. std::ostringstream tmpStream; tmpStream.copyfmt(m_out); if(m_extraFlags & Flag_SpacePadPositive) tmpStream.setf(std::ios::showpos); // formatCStringTruncate is required for truncating conversions like // "%.4s" where at most 4 characters of the c-string should be read. // If we didn't include this special case, we might read off the end. if(!( (m_extraFlags & Flag_TruncateToPrecision) && formatCStringTruncate(tmpStream, value, m_out.precision()) )) { // Not a truncated c-string; just format normally. formatValue(tmpStream, m_fmt, fmtEnd, value); } std::string result = tmpStream.str(); // allocates... yuck. if(m_extraFlags & Flag_SpacePadPositive) { for(size_t i = 0, iend = result.size(); i < iend; ++i) if(result[i] == '+') result[i] = ' '; } if((m_extraFlags & Flag_TruncateToPrecision) && (int)result.size() > (int)m_out.precision()) m_out.write(result.c_str(), m_out.precision()); else m_out << result; } m_extraFlags = Flag_None; m_fmt = fmtEnd; } // Parse a format string and set the stream state accordingly. // // The format mini-language recognized here is meant to be the one from C99, // with the form "%[flags][width][.precision][length]type". // // Formatting options which can't be natively represented using the ostream // state are returned in the extraFlags parameter which is a bitwise // combination of values from the ExtraFormatFlags enum. inline const char* FormatIterator::streamStateFromFormat(std::ostream& out, unsigned int& extraFlags, const char* fmtStart, int variableWidth, int variablePrecision) { if(*fmtStart != '%') { TINYFORMAT_ERROR("tinyformat: Not enough conversion specifiers in format string"); return fmtStart; } // Reset stream state to defaults. out.width(0); out.precision(6); out.fill(' '); // Reset most flags; ignore irrelevant unitbuf & skipws. out.unsetf(std::ios::adjustfield | std::ios::basefield | std::ios::floatfield | std::ios::showbase | std::ios::boolalpha | std::ios::showpoint | std::ios::showpos | std::ios::uppercase); extraFlags = Flag_None; bool precisionSet = false; bool widthSet = false; const char* c = fmtStart + 1; // 1) Parse flags for(;; ++c) { switch(*c) { case '#': out.setf(std::ios::showpoint | std::ios::showbase); continue; case '0': // overridden by left alignment ('-' flag) if(!(out.flags() & std::ios::left)) { // Use internal padding so that numeric values are // formatted correctly, eg -00010 rather than 000-10 out.fill('0'); out.setf(std::ios::internal, std::ios::adjustfield); } continue; case '-': out.fill(' '); out.setf(std::ios::left, std::ios::adjustfield); continue; case ' ': // overridden by show positive sign, '+' flag. if(!(out.flags() & std::ios::showpos)) extraFlags |= Flag_SpacePadPositive; continue; case '+': out.setf(std::ios::showpos); extraFlags &= ~Flag_SpacePadPositive; continue; } break; } // 2) Parse width if(*c >= '0' && *c <= '9') { widthSet = true; out.width(parseIntAndAdvance(c)); } if(*c == '*') { widthSet = true; if(variableWidth < 0) { // negative widths correspond to '-' flag set out.fill(' '); out.setf(std::ios::left, std::ios::adjustfield); variableWidth = -variableWidth; } out.width(variableWidth); extraFlags |= Flag_VariableWidth; ++c; } // 3) Parse precision if(*c == '.') { ++c; int precision = 0; if(*c == '*') { ++c; extraFlags |= Flag_VariablePrecision; precision = variablePrecision; } else { if(*c >= '0' && *c <= '9') precision = parseIntAndAdvance(c); else if(*c == '-') // negative precisions ignored, treated as zero. parseIntAndAdvance(++c); } out.precision(precision); precisionSet = true; } // 4) Ignore any C99 length modifier while(*c == 'l' || *c == 'h' || *c == 'L' || *c == 'j' || *c == 'z' || *c == 't') ++c; // 5) We're up to the conversion specifier character. // Set stream flags based on conversion specifier (thanks to the // boost::format class for forging the way here). bool intConversion = false; switch(*c) { case 'u': case 'd': case 'i': out.setf(std::ios::dec, std::ios::basefield); intConversion = true; break; case 'o': out.setf(std::ios::oct, std::ios::basefield); intConversion = true; break; case 'X': out.setf(std::ios::uppercase); case 'x': case 'p': out.setf(std::ios::hex, std::ios::basefield); intConversion = true; break; case 'E': out.setf(std::ios::uppercase); case 'e': out.setf(std::ios::scientific, std::ios::floatfield); out.setf(std::ios::dec, std::ios::basefield); break; case 'F': out.setf(std::ios::uppercase); case 'f': out.setf(std::ios::fixed, std::ios::floatfield); break; case 'G': out.setf(std::ios::uppercase); case 'g': out.setf(std::ios::dec, std::ios::basefield); // As in boost::format, let stream decide float format. out.flags(out.flags() & ~std::ios::floatfield); break; case 'a': case 'A': TINYFORMAT_ERROR("tinyformat: the %a and %A conversion specs " "are not supported"); break; case 'c': // Handled as special case inside formatValue() break; case 's': if(precisionSet) extraFlags |= Flag_TruncateToPrecision; // Make %s print booleans as "true" and "false" out.setf(std::ios::boolalpha); break; case 'n': // Not supported - will cause problems! TINYFORMAT_ERROR("tinyformat: %n conversion spec not supported"); break; case '\0': TINYFORMAT_ERROR("tinyformat: Conversion spec incorrectly " "terminated by end of string"); return c; } if(intConversion && precisionSet && !widthSet) { // "precision" for integers gives the minimum number of digits (to be // padded with zeros on the left). This isn't really supported by the // iostreams, but we can approximately simulate it with the width if // the width isn't otherwise used. out.width(out.precision()); out.setf(std::ios::internal, std::ios::adjustfield); out.fill('0'); } return c+1; } //------------------------------------------------------------------------------ // Private format function on top of which the public interface is implemented. // We enforce a mimimum of one value to be formatted to prevent bugs looking like // // const char* myStr = "100% broken"; // printf(myStr); // Parses % as a format specifier #ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES template void format(FormatIterator& fmtIter, const T1& value1) { fmtIter.accept(value1); fmtIter.finish(); } // General version for C++11 template void format(FormatIterator& fmtIter, const T1& value1, const Args&... args) { fmtIter.accept(value1); format(fmtIter, args...); } #else inline void format(FormatIterator& fmtIter) { fmtIter.finish(); } // General version for C++98 #define TINYFORMAT_MAKE_FORMAT_DETAIL(n) \ template \ void format(detail::FormatIterator& fmtIter, TINYFORMAT_VARARGS(n)) \ { \ fmtIter.accept(v1); \ format(fmtIter TINYFORMAT_PASSARGS_TAIL(n)); \ } TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_DETAIL) #undef TINYFORMAT_MAKE_FORMAT_DETAIL #endif // End C++98 variadic template emulation for format() } // namespace detail //------------------------------------------------------------------------------ // Implement all the main interface functions here in terms of detail::format() #ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES // C++11 - the simple case template void format(std::ostream& out, const char* fmt, const T1& v1, const Args&... args) { detail::FormatIterator fmtIter(out, fmt); format(fmtIter, v1, args...); } template std::string format(const char* fmt, const T1& v1, const Args&... args) { std::ostringstream oss; format(oss, fmt, v1, args...); return oss.str(); } template void printf(const char* fmt, const T1& v1, const Args&... args) { format(std::cout, fmt, v1, args...); } #else // C++98 - define the interface functions using the wrapping macros #define TINYFORMAT_MAKE_FORMAT_FUNCS(n) \ \ template \ void format(std::ostream& out, const char* fmt, TINYFORMAT_VARARGS(n)) \ { \ tinyformat::detail::FormatIterator fmtIter(out, fmt); \ tinyformat::detail::format(fmtIter, TINYFORMAT_PASSARGS(n)); \ } \ \ template \ std::string format(const char* fmt, TINYFORMAT_VARARGS(n)) \ { \ std::ostringstream oss; \ tinyformat::format(oss, fmt, TINYFORMAT_PASSARGS(n)); \ return oss.str(); \ } \ \ template \ void printf(const char* fmt, TINYFORMAT_VARARGS(n)) \ { \ tinyformat::format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \ } TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_FUNCS) #undef TINYFORMAT_MAKE_FORMAT_FUNCS #endif //------------------------------------------------------------------------------ // Define deprecated wrapping macro for backward compatibility in tinyformat // 1.x. Will be removed in version 2! #define TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS #define TINYFORMAT_WRAP_FORMAT_N(n, returnType, funcName, funcDeclSuffix, \ bodyPrefix, streamName, bodySuffix) \ template \ returnType funcName(TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS const char* fmt, \ TINYFORMAT_VARARGS(n)) funcDeclSuffix \ { \ bodyPrefix \ tinyformat::format(streamName, fmt, TINYFORMAT_PASSARGS(n)); \ bodySuffix \ } \ #define TINYFORMAT_WRAP_FORMAT(returnType, funcName, funcDeclSuffix, \ bodyPrefix, streamName, bodySuffix) \ inline \ returnType funcName(TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS const char* fmt \ ) funcDeclSuffix \ { \ bodyPrefix \ tinyformat::detail::FormatIterator(streamName, fmt).finish(); \ bodySuffix \ } \ TINYFORMAT_WRAP_FORMAT_N(1 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ TINYFORMAT_WRAP_FORMAT_N(2 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ TINYFORMAT_WRAP_FORMAT_N(3 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ TINYFORMAT_WRAP_FORMAT_N(4 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ TINYFORMAT_WRAP_FORMAT_N(5 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ TINYFORMAT_WRAP_FORMAT_N(6 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ TINYFORMAT_WRAP_FORMAT_N(7 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ TINYFORMAT_WRAP_FORMAT_N(8 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ TINYFORMAT_WRAP_FORMAT_N(9 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ TINYFORMAT_WRAP_FORMAT_N(10, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ TINYFORMAT_WRAP_FORMAT_N(11, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ TINYFORMAT_WRAP_FORMAT_N(12, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ TINYFORMAT_WRAP_FORMAT_N(13, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ TINYFORMAT_WRAP_FORMAT_N(14, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ TINYFORMAT_WRAP_FORMAT_N(15, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ TINYFORMAT_WRAP_FORMAT_N(16, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ } // namespace tinyformat #endif // TINYFORMAT_H_INCLUDED openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/imagecache.h0000644000175000017500000005335413151711064023064 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// \file /// An API for accessing images via a system that /// automatically manages a cache of resident image data. #ifndef OPENIMAGEIO_IMAGECACHE_H #define OPENIMAGEIO_IMAGECACHE_H #include "OpenImageIO/ustring.h" #include "OpenImageIO/imageio.h" OIIO_NAMESPACE_BEGIN struct ROI; namespace pvt { // Forward declaration class ImageCacheImpl; class ImageCacheFile; class ImageCachePerThreadInfo; }; /// Define an API to an abstract class that manages image files, /// caches of open file handles as well as tiles of pixels so that truly /// huge amounts of image data may be accessed by an application with low /// memory footprint. class OIIO_API ImageCache { public: /// Create a ImageCache and return a pointer. This should only be /// freed by passing it to ImageCache::destroy()! /// /// If shared==true, it's intended to be shared with other like-minded /// owners in the same process who also ask for a shared cache. If /// false, a private image cache will be created. static ImageCache *create (bool shared=true); /// Destroy a ImageCache that was created using ImageCache::create(). /// The variety that takes a 'teardown' parameter, when set to true, /// will fully destroy even a "shared" ImageCache. static void destroy (ImageCache * x); static void destroy (ImageCache * x, bool teardown); ImageCache (void) { } virtual ~ImageCache () { } OIIO_DEPRECATED("clear() was never implemented. Don't bother calling it. [1.7]") virtual void clear () { } /// Set an attribute controlling the image cache. Return true /// if the name and type were recognized and the attrib was set. /// Documented attributes: /// int max_open_files : maximum number of file handles held open /// float max_memory_MB : maximum tile cache size, in MB /// string searchpath : colon-separated search path for images /// string plugin_searchpath : colon-separated search path for plugins /// int autotile : if >0, tile size to emulate for non-tiled images /// int autoscanline : autotile using full width tiles /// int automip : if nonzero, emulate mipmap on the fly /// int accept_untiled : if nonzero, accept untiled images, but /// if zero, reject untiled images (default=1) /// int accept_unmipped : if nonzero, accept unmipped images (def=1) /// int statistics:level : verbosity of statistics auto-printed. /// int forcefloat : if nonzero, convert all to float. /// int failure_retries : number of times to retry a read before fail. /// int deduplicate : if nonzero, detect duplicate textures (default=1) /// string substitute_image : uses the named image in place of all /// texture and image references. /// int unassociatedalpha : if nonzero, keep unassociated alpha images /// int max_errors_per_file : Limits how many errors to issue for /// issue for each (default: 100) /// virtual bool attribute (string_view name, TypeDesc type, const void *val) = 0; // Shortcuts for common types virtual bool attribute (string_view name, int val) = 0; virtual bool attribute (string_view name, float val) = 0; virtual bool attribute (string_view name, double val) = 0; virtual bool attribute (string_view name, string_view val) = 0; /// Get the named attribute, store it in *val. All of the attributes /// that may be set with the attribute() call may also be queried with /// getattribute(). /// /// Additionally, there are some read-only attributes that can be /// queried with getattribute(): /// int total_files : the total number of unique files referenced by /// calls to the ImageCache. /// string[] all_filenames : an array that will be filled with the /// list of the names of all files referenced by calls to /// the ImageCache. (The array is of ustrings or char*'s.) /// stat:* : a variety of statistics (see full docs for details). /// virtual bool getattribute (string_view name, TypeDesc type, void *val) const = 0; // Shortcuts for common types virtual bool getattribute (string_view name, int &val) const = 0; virtual bool getattribute (string_view name, float &val) const = 0; virtual bool getattribute (string_view name, double &val) const = 0; virtual bool getattribute (string_view name, char **val) const = 0; virtual bool getattribute (string_view name, std::string &val) const = 0; /// Define an opaque data type that allows us to have a pointer /// to certain per-thread information that the ImageCache maintains. /// Any given one of these should NEVER be shared between running /// threads. typedef pvt::ImageCachePerThreadInfo Perthread; /// Retrieve a Perthread, unique to the calling thread. This is a /// thread-specific pointer that will always return the Perthread for a /// thread, which will also be automatically destroyed when the thread /// terminates. /// /// Applications that want to manage their own Perthread pointers (with /// create_thread_info and destroy_thread_info) should still call this, /// but passing in their managed pointer. If the passed-in thread_info /// is not NULL, it won't create a new one or retrieve a TSP, but it /// will do other necessary housekeeping on the Perthread information. virtual Perthread * get_perthread_info (Perthread *thread_info = NULL) = 0; /// Create a new Perthread. It is the caller's responsibility to /// eventually destroy it using destroy_thread_info(). virtual Perthread * create_thread_info () = 0; /// Destroy a Perthread that was allocated by create_thread_info(). virtual void destroy_thread_info (Perthread *thread_info) = 0; /// Define an opaque data type that allows us to have a handle to an /// image (already having its name resolved) but without exposing /// any internals. typedef pvt::ImageCacheFile ImageHandle; /// Retrieve an opaque handle for fast image lookups. The opaque /// pointer thread_info is thread-specific information returned by /// get_perthread_info(). Return NULL if something has gone /// horribly wrong. virtual ImageHandle * get_image_handle (ustring filename, Perthread *thread_info=NULL) = 0; /// Return true if the image handle (previously returned by /// get_image_handle()) is a valid image that can be subsequently read. virtual bool good (ImageHandle *file) = 0; /// Given possibly-relative 'filename', resolve it using the search /// path rules and return the full resolved filename. virtual std::string resolve_filename (const std::string &filename) const=0; /// Get information about the named image. Return true if found /// and the data has been put in *data. Return false if the image /// doesn't exist, doesn't have the requested data, if the data /// doesn't match the type requested. or some other failure. virtual bool get_image_info (ustring filename, int subimage, int miplevel, ustring dataname, TypeDesc datatype, void *data) = 0; virtual bool get_image_info (ImageHandle *file, Perthread *thread_info, int subimage, int miplevel, ustring dataname, TypeDesc datatype, void *data) = 0; /// Get the ImageSpec associated with the named image (the first /// subimage & miplevel by default, or as set by 'subimage' and /// 'miplevel'). If the file is found and is an image format that /// can be read, store a copy of its specification in spec and /// return true. Return false if the file was not found or could /// not be opened as an image file by any available ImageIO plugin. virtual bool get_imagespec (ustring filename, ImageSpec &spec, int subimage=0, int miplevel=0, bool native=false) = 0; virtual bool get_imagespec (ImageHandle *file, Perthread *thread_info, ImageSpec &spec, int subimage=0, int miplevel=0, bool native=false) = 0; /// Return a pointer to an ImageSpec associated with the named image /// (the first subimage & miplevel by default, or as set by /// 'subimage' and 'miplevel') if the file is found and is an image /// format that can be read, otherwise return NULL. /// /// This method is much more efficient than get_imagespec(), since /// it just returns a pointer to the spec held internally by the /// ImageCache (rather than copying the spec to the user's memory). /// However, the caller must beware that the pointer is only valid /// as long as nobody (even other threads) calls invalidate() on the /// file, or invalidate_all(), or destroys the ImageCache. virtual const ImageSpec *imagespec (ustring filename, int subimage=0, int miplevel=0, bool native=false) = 0; virtual const ImageSpec *imagespec (ImageHandle *file, Perthread *thread_info, int subimage=0, int miplevel=0, bool native=false) = 0; /// Retrieve the rectangle of pixels spanning [xbegin..xend) X /// [ybegin..yend) X [zbegin..zend), with "exclusive end" a la STL, /// specified as integer pixel coordinates in the designated /// subimage & miplevel, storing the pixel values beginning at the /// address specified by result. The pixel values will be converted /// to the type specified by format. It is up to the caller to /// ensure that result points to an area of memory big enough to /// accommodate the requested rectangle (taking into consideration /// its dimensions, number of channels, and data format). Requested /// pixels outside the valid pixel data region will be filled in /// with 0 values. /// /// Return true if the file is found and could be opened by an /// available ImageIO plugin, otherwise return false. virtual bool get_pixels (ustring filename, int subimage, int miplevel, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, void *result) = 0; virtual bool get_pixels (ImageHandle *file, Perthread *thread_info, int subimage, int miplevel, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, void *result) = 0; /// Retrieve the rectangle of pixels spanning [xbegin..xend) X /// [ybegin..yend) X [zbegin..zend), channels [chbegin..chend), /// with "exclusive end" a la STL, specified as integer pixel /// coordinates in the designated subimage & miplevel, storing the /// pixel values beginning at the address specified by result and /// with the given x, y, and z strides (in bytes). The pixel values /// will be converted to the type specified by format. If the /// strides are set to AutoStride, they will be automatically /// computed assuming a contiguous data layout. It is up to the /// caller to ensure that result points to an area of memory big /// enough to accommodate the requested rectangle (taking into /// consideration its dimensions, number of channels, and data /// format). Requested pixels outside the valid pixel data region /// will be filled in with 0 values. The optional cache_chbegin and /// cache_chend hint as to which range of channels should be cached /// (which by default will be all channels of the file). /// /// Return true if the file is found and could be opened by an /// available ImageIO plugin, otherwise return false. virtual bool get_pixels (ustring filename, int subimage, int miplevel, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc format, void *result, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride, int cache_chbegin = 0, int cache_chend = -1) = 0; virtual bool get_pixels (ImageHandle *file, Perthread *thread_info, int subimage, int miplevel, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc format, void *result, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride, int cache_chbegin = 0, int cache_chend = -1) = 0; /// Define an opaque data type that allows us to have a pointer /// to a tile but without exposing any internals. class Tile; /// Find a tile given by an image filename, subimage & miplevel, channel /// range, and pixel coordinates. An opaque pointer to the tile will be /// returned, or NULL if no such file (or tile within the file) exists /// or can be read. The tile will not be purged from the cache until /// after release_tile() is called on the tile pointer the same number /// of times that get_tile() was called (refcnt). This is thread-safe! /// If chend < chbegin, it will retrieve a tile containing all channels /// in the file. virtual Tile * get_tile (ustring filename, int subimage, int miplevel, int x, int y, int z, int chbegin = 0, int chend = -1) = 0; virtual Tile * get_tile (ImageHandle *file, Perthread *thread_info, int subimage, int miplevel, int x, int y, int z, int chbegin = 0, int chend = -1) = 0; /// After finishing with a tile, release_tile will allow it to /// once again be purged from the tile cache if required. virtual void release_tile (Tile *tile) const = 0; /// Retrieve the data type of the pixels stored in the tile, which may /// be different than the type of the pixels in the disk file. virtual TypeDesc tile_format (const Tile *tile) const = 0; /// Retrieve the ROI describing the pixels and channels stored in the /// tile. virtual ROI tile_roi (const Tile *tile) const = 0; /// For a tile retrived by get_tile(), return a pointer to the /// pixel data itself, and also store in 'format' the data type that /// the pixels are internally stored in (which may be different than /// the data type of the pixels in the disk file). virtual const void * tile_pixels (Tile *tile, TypeDesc &format) const = 0; /// The add_file() call causes a file to be opened or added to the /// cache. There is no reason to use this method unless you are /// supplying a custom creator, or configuration, or both. /// /// If creator is not NULL, it points to an ImageInput::Creator that /// will be used rather than the default ImageInput::create(), thus /// instead of reading from disk, creates and uses a custom ImageInput /// to generate the image. The 'creator' is a factory that creates the /// custom ImageInput and will be called like this: /// ImageInput *in = creator(); /// Once created, the ImageCache owns the ImageInput and is responsible /// for destroying it when done. Custom ImageInputs allow "procedural" /// images, among other things. Also, this is the method you use to set /// up a "writeable" ImageCache images (perhaps with a type of /// ImageInput that's just a stub that does as little as possible). /// /// If config is not NULL, it points to an ImageSpec with configuration /// options/hints that will be passed to the underlying /// ImageInput::open() call. Thus, this can be used to ensure that the /// ImageCache opens a call with special configuration options. /// /// This call (including any custom creator or configuration hints) will /// have no effect if there's already an image by the same name in the /// cache. Custom creators or configurations only "work" the FIRST time /// a particular filename is referenced in the lifetime of the /// ImageCache. virtual bool add_file (ustring filename, ImageInput::Creator creator=NULL, const ImageSpec *config=NULL) = 0; /// Preemptively add a tile corresponding to the named image, at the /// given subimage, MIP level, and channel range. The tile added is the /// one whose corner is (x,y,z), and buffer points to the pixels (in the /// given format, with supplied strides) which will be copied and /// inserted into the cache and made available for future lookups. /// If chend < chbegin, it will add a tile containing the full set of /// channels for the image. virtual bool add_tile (ustring filename, int subimage, int miplevel, int x, int y, int z, int chbegin, int chend, TypeDesc format, const void *buffer, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride) = 0; OIIO_DEPRECATED("Use the version of add_tile with channel range. [1.6]") virtual bool add_tile (ustring filename, int subimage, int miplevel, int x, int y, int z, TypeDesc format, const void *buffer, stride_t xstride=AutoStride, stride_t ystride=AutoStride, stride_t zstride=AutoStride) { return add_tile (filename, subimage, miplevel, x, y, z, 0, -1, format, buffer, xstride, ystride, zstride); } /// If any of the API routines returned false indicating an error, /// this routine will return the error string (and clear any error /// flags). If no error has occurred since the last time geterror() /// was called, it will return an empty string. virtual std::string geterror () const = 0; /// Return the statistics output as a huge string. /// virtual std::string getstats (int level=1) const = 0; /// Reset most statistics to be as they were with a fresh /// ImageCache. Caveat emptor: this does not flush the cache itelf, /// so the resulting statistics from the next set of texture /// requests will not match the number of tile reads, etc., that /// would have resulted from a new ImageCache. virtual void reset_stats () = 0; /// Invalidate any loaded tiles or open file handles associated with /// the filename, so that any subsequent queries will be forced to /// re-open the file or re-load any tiles (even those that were /// previously loaded and would ordinarily be reused). A client /// might do this if, for example, they are aware that an image /// being held in the cache has been updated on disk. This is safe /// to do even if other procedures are currently holding /// reference-counted tile pointers from the named image, but those /// procedures will not get updated pixels until they release the /// tiles they are holding. virtual void invalidate (ustring filename) = 0; /// Invalidate all loaded tiles and open file handles. This is safe /// to do even if other procedures are currently holding /// reference-counted tile pointers from the named image, but those /// procedures will not get updated pixels until they release the /// tiles they are holding. If force is true, everything will be /// invalidated, no matter how wasteful it is, but if force is /// false, in actuality files will only be invalidated if their /// modification times have been changed since they were first /// opened. virtual void invalidate_all (bool force=false) = 0; private: // Make delete private and unimplemented in order to prevent apps // from calling it. Instead, they should call ImageCache::destroy(). void operator delete (void * /*todel*/) { } }; OIIO_NAMESPACE_END #endif // OPENIMAGEIO_IMAGECACHE_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/atomic.h0000644000175000017500000003752713151711064022276 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ ///////////////////////////////////////////////////////////////////////// /// @file atomic.h /// /// @brief Wrappers and utilities for atomics. ///////////////////////////////////////////////////////////////////////// #ifndef OPENIMAGEIO_ATOMIC_H #define OPENIMAGEIO_ATOMIC_H #include "oiioversion.h" #include "platform.h" #if defined(_MSC_VER) // N.B. including platform.h also included # pragma intrinsic (_InterlockedExchangeAdd) # pragma intrinsic (_InterlockedCompareExchange) # pragma intrinsic (_InterlockedCompareExchange64) # if defined(_WIN64) # pragma intrinsic(_InterlockedExchangeAdd64) # endif // InterlockedExchangeAdd64 & InterlockedExchange64 are not available for XP # if defined(_WIN32_WINNT) && _WIN32_WINNT <= 0x0501 inline long long InterlockedExchangeAdd64 (volatile long long *Addend, long long Value) { long long Old; do { Old = *Addend; } while (_InterlockedCompareExchange64(Addend, Old + Value, Old) != Old); return Old; } inline long long InterlockedExchange64 (volatile long long *Target, long long Value) { long long Old; do { Old = *Target; } while (_InterlockedCompareExchange64(Target, Value, Old) != Old); return Old; } # endif #endif #if defined(__GNUC__) && (defined(_GLIBCXX_ATOMIC_BUILTINS) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 401)) # define USE_GCC_ATOMICS # if !defined(__clang__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 408) # define OIIO_USE_GCC_NEW_ATOMICS # endif #endif OIIO_NAMESPACE_BEGIN #if OIIO_USE_STDATOMIC using std::memory_order; #else enum memory_order { #if defined(OIIO_USE_GCC_NEW_ATOMICS) memory_order_relaxed = __ATOMIC_RELAXED, memory_order_consume = __ATOMIC_CONSUME, memory_order_acquire = __ATOMIC_ACQUIRE, memory_order_release = __ATOMIC_RELEASE, memory_order_acq_rel = __ATOMIC_ACQ_REL, memory_order_seq_cst = __ATOMIC_SEQ_CST #else memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_seq_cst #endif }; #endif /// Atomic version of: r = *at, *at += x, return r /// For each of several architectures. inline int atomic_exchange_and_add (volatile int *at, int x, memory_order order = memory_order_seq_cst) { #ifdef NOTHREADS int r = *at; *at += x; return r; #elif defined(OIIO_USE_GCC_NEW_ATOMICS) return __atomic_fetch_add (at, x, order); #elif defined(USE_GCC_ATOMICS) return __sync_fetch_and_add ((int *)at, x); #elif defined(_MSC_VER) // Windows return _InterlockedExchangeAdd ((volatile LONG *)at, x); #else # error No atomics on this platform. #endif } inline long long atomic_exchange_and_add (volatile long long *at, long long x, memory_order order = memory_order_seq_cst) { #ifdef NOTHREADS long long r = *at; *at += x; return r; #elif defined(OIIO_USE_GCC_NEW_ATOMICS) return __atomic_fetch_add (at, x, order); #elif defined(USE_GCC_ATOMICS) return __sync_fetch_and_add (at, x); #elif defined(_MSC_VER) // Windows # if defined(_WIN64) return _InterlockedExchangeAdd64 ((volatile LONGLONG *)at, x); # else return InterlockedExchangeAdd64 ((volatile LONGLONG *)at, x); # endif #else # error No atomics on this platform. #endif } /// Atomic version of: r = *at, *at &= x, return r /// For each of several architectures. inline int atomic_exchange_and_and (volatile int *at, int x, memory_order order = memory_order_seq_cst) { #ifdef NOTHREADS int r = *at; *at &= x; return r; #elif defined(OIIO_USE_GCC_NEW_ATOMICS) return __atomic_fetch_and (at, x, order); #elif defined(USE_GCC_ATOMICS) return __sync_fetch_and_and ((int *)at, x); #elif defined(_MSC_VER) // Windows return _InterlockedAnd ((volatile LONG *)at, x); #else # error No atomics on this platform. #endif } inline long long atomic_exchange_and_and (volatile long long *at, long long x, memory_order order = memory_order_seq_cst) { #ifdef NOTHREADS long long r = *at; *at &= x; return r; #elif defined(OIIO_USE_GCC_NEW_ATOMICS) return __atomic_fetch_and (at, x, order); #elif defined(USE_GCC_ATOMICS) return __sync_fetch_and_and (at, x); #elif defined(_MSC_VER) // Windows # if defined(_WIN64) return _InterlockedAnd64 ((volatile LONGLONG *)at, x); # else return InterlockedAnd64 ((volatile LONGLONG *)at, x); # endif #else # error No atomics on this platform. #endif } /// Atomic version of: r = *at, *at |= x, return r /// For each of several architectures. inline int atomic_exchange_and_or (volatile int *at, int x, memory_order order = memory_order_seq_cst) { #ifdef NOTHREADS int r = *at; *at |= x; return r; #elif defined(OIIO_USE_GCC_NEW_ATOMICS) return __atomic_fetch_or (at, x, order); #elif defined(USE_GCC_ATOMICS) return __sync_fetch_and_or ((int *)at, x); #elif defined(_MSC_VER) // Windows return _InterlockedOr ((volatile LONG *)at, x); #else # error No atomics on this platform. #endif } inline long long atomic_exchange_and_or (volatile long long *at, long long x, memory_order order = memory_order_seq_cst) { #ifdef NOTHREADS long long r = *at; *at |= x; return r; #elif defined(OIIO_USE_GCC_NEW_ATOMICS) return __atomic_fetch_or (at, x, order); #elif defined(USE_GCC_ATOMICS) return __sync_fetch_and_or (at, x); #elif defined(_MSC_VER) // Windows # if defined(_WIN64) return _InterlockedOr64 ((volatile LONGLONG *)at, x); # else return InterlockedOr64 ((volatile LONGLONG *)at, x); # endif #else # error No atomics on this platform. #endif } /// Atomic version of: r = *at, *at ^= x, return r /// For each of several architectures. inline int atomic_exchange_and_xor (volatile int *at, int x, memory_order order = memory_order_seq_cst) { #ifdef NOTHREADS int r = *at; *at ^= x; return r; #elif defined(OIIO_USE_GCC_NEW_ATOMICS) return __atomic_fetch_xor (at, x, order); #elif defined(USE_GCC_ATOMICS) return __sync_fetch_and_xor ((int *)at, x); #elif defined(_MSC_VER) // Windows return _InterlockedXor ((volatile LONG *)at, x); #else # error No atomics on this platform. #endif } inline long long atomic_exchange_and_xor (volatile long long *at, long long x, memory_order order = memory_order_seq_cst) { #ifdef NOTHREADS long long r = *at; *at ^= x; return r; #elif defined(OIIO_USE_GCC_NEW_ATOMICS) return __atomic_fetch_xor (at, x, order); #elif defined(USE_GCC_ATOMICS) return __sync_fetch_and_xor (at, x); #elif defined(_MSC_VER) // Windows # if defined(_WIN64) return _InterlockedXor64 ((volatile LONGLONG *)at, x); # else return InterlockedXor64 ((volatile LONGLONG *)at, x); # endif #else # error No atomics on this platform. #endif } /// Atomic version of: /// if (*at == compareval) { /// *at = newval; return true; /// } else { /// return false; /// } inline bool atomic_compare_and_exchange (volatile int *at, int compareval, int newval, bool weak = false, memory_order success = memory_order_seq_cst, memory_order failure = memory_order_seq_cst) { #ifdef NOTHREADS if (*at == compareval) { *at = newval; return true; } else { return false; } #elif defined(OIIO_USE_GCC_NEW_ATOMICS) return __atomic_compare_exchange_n (at, &compareval, newval, weak, success, failure); #elif defined(USE_GCC_ATOMICS) return __sync_bool_compare_and_swap (at, compareval, newval); #elif defined(_MSC_VER) return (_InterlockedCompareExchange ((volatile LONG *)at, newval, compareval) == compareval); #else # error No atomics on this platform. #endif } inline bool atomic_compare_and_exchange (volatile long long *at, long long compareval, long long newval, bool weak = false, memory_order success = memory_order_seq_cst, memory_order failure = memory_order_seq_cst) { #ifdef NOTHREADS if (*at == compareval) { *at = newval; return true; } else { return false; } #elif defined(OIIO_USE_GCC_NEW_ATOMICS) return __atomic_compare_exchange_n (at, &compareval, newval, weak, success, failure); #elif defined(USE_GCC_ATOMICS) return __sync_bool_compare_and_swap (at, compareval, newval); #elif defined(_MSC_VER) return (_InterlockedCompareExchange64 ((volatile LONGLONG *)at, newval, compareval) == compareval); #else # error No atomics on this platform. #endif } /// Atomic version of: r = *at, *at = x, return r /// For each of several architectures. inline int atomic_exchange (volatile int *at, int x, memory_order order = memory_order_seq_cst) { #ifdef NOTHREADS int r = *at; *at = x; return r; #elif defined(OIIO_USE_GCC_NEW_ATOMICS) return __atomic_exchange_n (at, x, order); #elif defined(USE_GCC_ATOMICS) // No __sync version of atomic exchange! Do it the hard way: while (1) { int old = *at; if (atomic_compare_and_exchange (at, old, x)) return old; } return 0; // can never happen #elif defined(_MSC_VER) // Windows return _InterlockedExchange ((volatile LONG *)at, x); #else # error No atomics on this platform. #endif } inline long long atomic_exchange (volatile long long *at, long long x, memory_order order = memory_order_seq_cst) { #ifdef NOTHREADS long long r = *at; *at = x; return r; #elif defined(OIIO_USE_GCC_NEW_ATOMICS) return __atomic_exchange_n (at, x, order); #elif defined(USE_GCC_ATOMICS) // No __sync version of atomic exchange! Do it the hard way: while (1) { long long old = *at; if (atomic_compare_and_exchange (at, old, x)) return old; } return 0; // can never happen #elif defined(_MSC_VER) // Windows # if defined(_WIN64) return _InterlockedExchange64 ((volatile LONGLONG *)at, x); # else return InterlockedExchange64 ((volatile LONGLONG *)at, x); # endif #else # error No atomics on this platform. #endif } /// Memory fence / synchronization barrier OIIO_FORCEINLINE void atomic_thread_fence (memory_order order = memory_order_seq_cst) { #ifdef NOTHREADS // nothing #elif OIIO_USE_STDATOMIC std::__atomic_thread_fence (order); #elif defined(OIIO_USE_GCC_NEW_ATOMICS) __atomic_thread_fence (order); #elif defined(USE_GCC_ATOMICS) __sync_synchronize (); #elif defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) __asm__ __volatile__ ("":::"memory"); #elif defined(_MSC_VER) MemoryBarrier (); #else # error No atomics on this platform. #endif } /// Atomic integer. Increment, decrement, add, and subtract in a /// totally thread-safe manner. template class atomic { public: /// Construct with initial value. /// atomic (T val=0) : m_val(val) { } ~atomic () { } /// Retrieve value /// T load (memory_order order = memory_order_seq_cst) const { return atomic_exchange_and_add (&m_val, 0, order); } /// Retrieve value /// T operator() () const { return load(); } /// Retrieve value /// operator T() const { return load(); } /// Fast retrieval of value, no interchange, don't care about memory /// fences. Use with extreme caution! T fast_value () const { return m_val; } /// Assign new value, atomically. void store (T x, memory_order order = memory_order_seq_cst) { atomic_exchange (&m_val, x, order); } /// Atomic exchange T exchange (T x, memory_order order = memory_order_seq_cst) { return atomic_exchange (&m_val, x, order); } /// Atomic fetch-and-add: add x and return the old value. T fetch_add (T x, memory_order order = memory_order_seq_cst) { return atomic_exchange_and_add (&m_val, x, order); } /// Atomic fetch-and-subtract: subtract x and return the old value. T fetch_sub (T x, memory_order order = memory_order_seq_cst) { return atomic_exchange_and_add (&m_val, -x, order); } /// Atomic fetch-and-and: bitwise and with x and return the old value. T fetch_and (T x, memory_order order = memory_order_seq_cst) { return atomic_exchange_and_and (&m_val, x, order); } /// Atomic fetch-and-or: bitwise or with x and return the old value. T fetch_or (T x, memory_order order = memory_order_seq_cst) { return atomic_exchange_and_or (&m_val, x, order); } /// Atomic fetch-and-xor: bitwise xor with x and return the old value. T fetch_xor (T x, memory_order order = memory_order_seq_cst) { return atomic_exchange_and_xor (&m_val, x, order); } /// Assign new value. /// T operator= (T x) { store(x); return x; } /// Pre-increment: ++foo /// T operator++ () { return fetch_add(1) + 1; } /// Post-increment: foo++ /// T operator++ (int) { return fetch_add(1); } /// Pre-decrement: --foo /// T operator-- () { return fetch_sub(1) - 1; } /// Post-decrement: foo-- /// T operator-- (int) { return fetch_sub(1); } /// Add to the value, return the new result /// T operator+= (T x) { return fetch_add(x) + x; } /// Subtract from the value, return the new result /// T operator-= (T x) { return fetch_sub(x) - x; } /// Logical and, return the new result /// T operator&= (T x) { return fetch_and(x) & x; } /// Logical or, return the new result /// T operator|= (T x) { return fetch_or(x) | x; } /// Logical xor, return the new result /// T operator^= (T x) { return fetch_xor(x) ^ x; } bool bool_compare_and_swap (T compareval, T newval) { return atomic_compare_and_exchange (&m_val, compareval, newval); } T operator= (const atomic &x) { T r = x(); *this = r; return r; } private: #ifdef __arm__ OIIO_ALIGN(8) #endif volatile mutable T m_val; // Disallow copy construction by making private and unimplemented. atomic (atomic const &); }; #ifdef NOTHREADS typedef int atomic_int; typedef long long atomic_ll; #else typedef atomic atomic_int; typedef atomic atomic_ll; #endif OIIO_NAMESPACE_END #endif // OPENIMAGEIO_ATOMIC_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/hash.h0000644000175000017500000004122313151711064021731 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// \file /// /// Wrapper so that hash_map and hash_set mean what we want regardless /// of the compiler. /// #ifndef OPENIMAGEIO_HASH_H #define OPENIMAGEIO_HASH_H #include #include #include #include #include // for memcpy and memset #include #include "export.h" #include "oiioversion.h" #include "platform.h" #include "fmath.h" /* for endian */ #include "string_view.h" #include "array_view.h" #if OIIO_CPLUSPLUS_VERSION >= 11 || OIIO_MSVS_AT_LEAST_2013 # include #else /* FIXME(C++11): remove this after making C++11 the baseline */ # include #endif OIIO_NAMESPACE_BEGIN // Define OIIO::unordered_map and OIIO::hash as either std or boost. #if OIIO_CPLUSPLUS_VERSION >= 11 || OIIO_MSVS_AT_LEAST_2013 using std::unordered_map; using std::hash; #else /* FIXME(C++11): remove this after making C++11 the baseline */ using boost::unordered_map; using boost::hash; #endif namespace xxhash { // xxhash: http://code.google.com/p/xxhash/ // It's BSD licensed. OIIO_DEPRECATED("Use XXH32(). (Deprecated since 1.6.") unsigned int OIIO_API XXH_fast32 (const void* input, int len, unsigned int seed=1771); OIIO_DEPRECATED("Use XXH32(). (Deprecated since 1.6.") unsigned int OIIO_API XXH_strong32 (const void* input, int len, unsigned int seed=1771); unsigned int OIIO_API XXH32 (const void* input, size_t length, unsigned seed=1771); unsigned long long OIIO_API XXH64 (const void* input, size_t length, unsigned long long seed=1771); inline size_t xxhash (const void* input, size_t length, size_t seed=1771) { return size_t (XXH64 (input, length, (unsigned long long)seed)); } template inline size_t xxhash (const Str& s, size_t seed=1771) { assert(sizeof(s[0]) == 1); return xxhash (s.data(), s.length(), seed); } } // end namespace xxhash namespace bjhash { // Bob Jenkins "lookup3" hashes: http://burtleburtle.net/bob/c/lookup3.c // It's in the public domain. // Mix up the bits of a, b, and c (changing their values in place). inline void bjmix (uint32_t &a, uint32_t &b, uint32_t &c) { a -= c; a ^= rotl32(c, 4); c += b; b -= a; b ^= rotl32(a, 6); a += c; c -= b; c ^= rotl32(b, 8); b += a; a -= c; a ^= rotl32(c,16); c += b; b -= a; b ^= rotl32(a,19); a += c; c -= b; c ^= rotl32(b, 4); b += a; } // Mix up and combine the bits of a, b, and c (doesn't change them, but // returns a hash of those three original values). 21 ops inline uint32_t bjfinal (uint32_t a, uint32_t b, uint32_t c=0xdeadbeef) { c ^= b; c -= rotl32(b,14); a ^= c; a -= rotl32(c,11); b ^= a; b -= rotl32(a,25); c ^= b; c -= rotl32(b,16); a ^= c; a -= rotl32(c,4); b ^= a; b -= rotl32(a,14); c ^= b; c -= rotl32(b,24); return c; } // Mix up 4 64-bit inputs (non-destructively), and return a 64 bit hash. // Adapted from http://burtleburtle.net/bob/c/SpookyV2.h 33 ops inline uint64_t bjfinal64 (uint64_t h0, uint64_t h1, uint64_t h2, uint64_t h3) { h3 ^= h2; h2 = rotl64(h2,15); h3 += h2; h0 ^= h3; h3 = rotl64(h3,52); h0 += h3; h1 ^= h0; h0 = rotl64(h0,26); h1 += h0; h2 ^= h1; h1 = rotl64(h1,51); h2 += h1; h3 ^= h2; h2 = rotl64(h2,28); h3 += h2; h0 ^= h3; h3 = rotl64(h3,9); h0 += h3; h1 ^= h0; h0 = rotl64(h0,47); h1 += h0; h2 ^= h1; h1 = rotl64(h1,54); h2 += h1; h3 ^= h2; h2 = rotl64(h2,32); h3 += h2; h0 ^= h3; h3 = rotl64(h3,25); h0 += h3; h1 ^= h0; h0 = rotl64(h0,63); h1 += h0; return h1; } // Standard "lookup3" hash, arbitrary length in bytes. uint32_t OIIO_API hashlittle (const void *key, size_t length, uint32_t seed=1771); // Hash an array of 32 bit words -- faster than hashlittle if you know // it's a whole number of 4-byte words. uint32_t OIIO_API hashword (const uint32_t *key, size_t nwords, uint32_t seed=1771); // Hash a string without pre-known length. We use the Jenkins // one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function), // which seems to be a good speed/quality/requirements compromise. inline size_t strhash (const char *s) { if (! s) return 0; unsigned int h = 0; while (*s) { h += (unsigned char)(*s); h += h << 10; h ^= h >> 6; ++s; } h += h << 3; h ^= h >> 11; h += h << 15; return h; } // Hash a string_view. We use the Jenkins // one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function), // which seems to be a good speed/quality/requirements compromise. inline size_t strhash (string_view s) { size_t len = s.length(); if (! len) return 0; unsigned int h = 0; for (size_t i = 0; i < len; ++i) { h += (unsigned char)(s[i]); h += h << 10; h ^= h >> 6; } h += h << 3; h ^= h >> 11; h += h << 15; return h; } } // end namespace bjhash namespace murmur { // These functions were lifted from the public domain Murmurhash3. We // don't bother using Murmurhash -- in my tests, it was slower than // xxhash in all cases, and comparable to bjhash. But these two fmix // functions are useful for scrambling the bits of a single 32 or 64 bit // value. inline uint32_t fmix (uint32_t h) { h ^= h >> 16; h *= 0x85ebca6b; h ^= h >> 13; h *= 0xc2b2ae35; h ^= h >> 16; return h; } inline uint64_t fmix (uint64_t k) { k ^= k >> 33; k *= 0xff51afd7ed558ccdULL; k ^= k >> 33; k *= 0xc4ceb9fe1a85ec53ULL; k ^= k >> 33; return k; } } // end namespace murmur namespace farmhash { // Copyright (c) 2014 Google, Inc. // http://code.google.com/p/farmhash/ // See OpenImageIO's hashes.cpp for the MIT license for this code. #if defined(FARMHASH_UINT128_T_DEFINED) inline uint64_t Uint128Low64(const uint128_t x) { return static_cast(x); } inline uint64_t Uint128High64(const uint128_t x) { return static_cast(x >> 64); } inline uint128_t Uint128(uint64_t lo, uint64_t hi) { return lo + (((uint128_t)hi) << 64); } #else typedef std::pair uint128_t; inline uint64_t Uint128Low64(const uint128_t x) { return x.first; } inline uint64_t Uint128High64(const uint128_t x) { return x.second; } inline uint128_t Uint128(uint64_t lo, uint64_t hi) { return uint128_t(lo, hi); } #endif // BASIC STRING HASHING // Hash function for a byte array. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. size_t OIIO_API Hash(const char* s, size_t len); // Hash function for a byte array. Most useful in 32-bit binaries. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. uint32_t OIIO_API Hash32(const char* s, size_t len); // Hash function for a byte array. For convenience, a 32-bit seed is also // hashed into the result. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. uint32_t OIIO_API Hash32WithSeed(const char* s, size_t len, uint32_t seed); // Hash 128 input bits down to 64 bits of output. // Hash function for a byte array. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. uint64_t OIIO_API Hash64(const char* s, size_t len); // Hash function for a byte array. For convenience, a 64-bit seed is also // hashed into the result. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. uint64_t OIIO_API Hash64WithSeed(const char* s, size_t len, uint64_t seed); // Hash function for a byte array. For convenience, two seeds are also // hashed into the result. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. uint64_t OIIO_API Hash64WithSeeds(const char* s, size_t len, uint64_t seed0, uint64_t seed1); // Hash function for a byte array. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. uint128_t OIIO_API Hash128(const char* s, size_t len); // Hash function for a byte array. For convenience, a 128-bit seed is also // hashed into the result. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. uint128_t OIIO_API Hash128WithSeed(const char* s, size_t len, uint128_t seed); // BASIC NON-STRING HASHING // This is intended to be a reasonably good hash function. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. inline uint64_t Hash128to64(uint128_t x) { // Murmur-inspired hashing. const uint64_t kMul = 0x9ddfea08eb382d69ULL; uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; a ^= (a >> 47); uint64_t b = (Uint128High64(x) ^ a) * kMul; b ^= (b >> 47); b *= kMul; return b; } // FINGERPRINTING (i.e., good, portable, forever-fixed hash functions) // Fingerprint function for a byte array. Most useful in 32-bit binaries. uint32_t OIIO_API Fingerprint32(const char* s, size_t len); // Fingerprint function for a byte array. uint64_t OIIO_API Fingerprint64(const char* s, size_t len); // Fingerprint function for a byte array. uint128_t OIIO_API Fingerprint128(const char* s, size_t len); // This is intended to be a good fingerprinting primitive. // See below for more overloads. inline uint64_t Fingerprint(uint128_t x) { // Murmur-inspired hashing. const uint64_t kMul = 0x9ddfea08eb382d69ULL; uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; a ^= (a >> 47); uint64_t b = (Uint128High64(x) ^ a) * kMul; b ^= (b >> 44); b *= kMul; b ^= (b >> 41); b *= kMul; return b; } // This is intended to be a good fingerprinting primitive. inline uint64_t Fingerprint(uint64_t x) { // Murmur-inspired hashing. const uint64_t kMul = 0x9ddfea08eb382d69ULL; uint64_t b = x * kMul; b ^= (b >> 44); b *= kMul; b ^= (b >> 41); b *= kMul; return b; } #ifndef FARMHASH_NO_CXX_STRING // Convenience functions to hash or fingerprint C++ strings. // These require that Str::data() return a pointer to the first char // (as a const char*) and that Str::length() return the string's length; // they work with std::string, for example. // Hash function for a byte array. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. template inline size_t Hash(const Str& s) { assert(sizeof(s[0]) == 1); return Hash(s.data(), s.length()); } // Hash function for a byte array. Most useful in 32-bit binaries. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. template inline uint32_t Hash32(const Str& s) { assert(sizeof(s[0]) == 1); return Hash32(s.data(), s.length()); } // Hash function for a byte array. For convenience, a 32-bit seed is also // hashed into the result. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. template inline uint32_t Hash32WithSeed(const Str& s, uint32_t seed) { assert(sizeof(s[0]) == 1); return Hash32WithSeed(s.data(), s.length(), seed); } // Hash 128 input bits down to 64 bits of output. // Hash function for a byte array. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. template inline uint64_t Hash64(const Str& s) { assert(sizeof(s[0]) == 1); return Hash64(s.data(), s.length()); } // Hash function for a byte array. For convenience, a 64-bit seed is also // hashed into the result. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. template inline uint64_t Hash64WithSeed(const Str& s, uint64_t seed) { assert(sizeof(s[0]) == 1); return Hash64WithSeed(s.data(), s.length(), seed); } // Hash function for a byte array. For convenience, two seeds are also // hashed into the result. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. template inline uint64_t Hash64WithSeeds(const Str& s, uint64_t seed0, uint64_t seed1) { assert(sizeof(s[0]) == 1); return Hash64WithSeeds(s.data(), s.length(), seed0, seed1); } // Hash function for a byte array. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. template inline uint128_t Hash128(const Str& s) { assert(sizeof(s[0]) == 1); return Hash128(s.data(), s.length()); } // Hash function for a byte array. For convenience, a 128-bit seed is also // hashed into the result. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. template inline uint128_t Hash128WithSeed(const Str& s, uint128_t seed) { assert(sizeof(s[0]) == 1); return Hash128(s.data(), s.length(), seed); } // FINGERPRINTING (i.e., good, portable, forever-fixed hash functions) // Fingerprint function for a byte array. Most useful in 32-bit binaries. template inline uint32_t Fingerprint32(const Str& s) { assert(sizeof(s[0]) == 1); return Fingerprint32(s.data(), s.length()); } // Fingerprint 128 input bits down to 64 bits of output. // Fingerprint function for a byte array. template inline uint64_t Fingerprint64(const Str& s) { assert(sizeof(s[0]) == 1); return Fingerprint64(s.data(), s.length()); } // Fingerprint function for a byte array. template inline uint128_t Fingerprint128(const Str& s) { assert(sizeof(s[0]) == 1); return Fingerprint128(s.data(), s.length()); } #endif } // namespace farmhash class CSHA1; // opaque forward declaration /// Class that encapsulates SHA-1 hashing, a crypticographic-strength /// 160-bit hash function. It's not as fast as our other hashing /// methods, but has an extremely low chance of having collisions. class OIIO_API SHA1 { public: /// Create SHA1, optionally read data SHA1 (const void *data=NULL, size_t size=0); ~SHA1 (); /// Append more data void append (const void *data, size_t size); /// Append more data from an array_view, without thinking about sizes. template void append (array_view v) { append (v.data(), v.size()*sizeof(T)); } template OIIO_DEPRECATED("Use append(). [1.6]") void appendvec (array_view v) { append (v.data(), v.size()*sizeof(T)); } /// Type for storing the raw bits of the hash struct Hash { unsigned char hash[20]; }; /// Get the digest and store it in Hash h. void gethash (Hash &h); /// Get the digest and store it in h (must point to enough storage /// to accommodate 20 bytes). void gethash (void *h) { gethash (*(Hash *)h); } /// Return the digest as a hex string std::string digest (); /// Roll the whole thing into one functor, return the string digest. static std::string digest (const void *data, size_t size) { SHA1 s (data, size); return s.digest(); } private: CSHA1 *m_csha1; bool m_final; }; OIIO_NAMESPACE_END #endif // OPENIMAGEIO_HASH_H openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/SHA1.h0000644000175000017500000001406413151711064021505 0ustar mfvmfv/* 100% free public domain implementation of the SHA-1 algorithm by Dominik Reichl Web: http://www.dominik-reichl.de/ Version 1.8 - 2008-03-16 - Converted project files to Visual Studio 2008 format. - Added Unicode support for HashFile utility method. - Added support for hashing files using the HashFile method that are larger than 2 GB. - HashFile now returns an error code instead of copying an error message into the output buffer. - GetHash now returns an error code and validates the input parameter. - Added ReportHashStl STL utility method. - Added REPORT_HEX_SHORT reporting mode. - Improved Linux compatibility of test program. Version 1.7 - 2006-12-21 - Fixed buffer underrun warning that appeared when compiling with Borland C Builder (thanks to Rex Bloom and Tim Gallagher for the patch). - Breaking change: ReportHash writes the final hash to the start of the buffer, i.e. it's not appending it to the string anymore. - Made some function parameters const. - Added Visual Studio 2005 project files to demo project. Version 1.6 - 2005-02-07 (thanks to Howard Kapustein for patches) - You can set the endianness in your files, no need to modify the header file of the CSHA1 class anymore. - Aligned data support. - Made support/compilation of the utility functions (ReportHash and HashFile) optional (useful when bytes count, for example in embedded environments). Version 1.5 - 2005-01-01 - 64-bit compiler compatibility added. - Made variable wiping optional (define SHA1_WIPE_VARIABLES). - Removed unnecessary variable initializations. - ROL32 improvement for the Microsoft compiler (using _rotl). Version 1.4 - 2004-07-22 - CSHA1 now compiles fine with GCC 3.3 under MacOS X (thanks to Larry Hastings). Version 1.3 - 2003-08-17 - Fixed a small memory bug and made a buffer array a class member to ensure correct working when using multiple CSHA1 class instances at one time. Version 1.2 - 2002-11-16 - Borlands C++ compiler seems to have problems with string addition using sprintf. Fixed the bug which caused the digest report function not to work properly. CSHA1 is now Borland compatible. Version 1.1 - 2002-10-11 - Removed two unnecessary header file includes and changed BOOL to bool. Fixed some minor bugs in the web page contents. Version 1.0 - 2002-06-20 - First official release. ======== Test Vectors (from FIPS PUB 180-1) ======== SHA1("abc") = A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D SHA1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 SHA1(A million repetitions of "a") = 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ #ifndef ___SHA1_HDR___ #define ___SHA1_HDR___ #include #include "export.h" #include "oiioversion.h" #include "platform.h" #if !defined(SHA1_UTILITY_FUNCTIONS) && !defined(SHA1_NO_UTILITY_FUNCTIONS) #define SHA1_UTILITY_FUNCTIONS #endif #if !defined(SHA1_STL_FUNCTIONS) && !defined(SHA1_NO_STL_FUNCTIONS) #define SHA1_STL_FUNCTIONS #if !defined(SHA1_UTILITY_FUNCTIONS) #error STL functions require SHA1_UTILITY_FUNCTIONS. #endif #endif #include #ifdef SHA1_UTILITY_FUNCTIONS #include #include #endif #ifdef SHA1_STL_FUNCTIONS #include #endif #ifdef _MSC_VER #include #endif // You can define the endian mode in your files without modifying the SHA-1 // source files. Just #define SHA1_LITTLE_ENDIAN or #define SHA1_BIG_ENDIAN // in your files, before including the SHA1.h header file. If you don't // define anything, the class defaults to little endian. #if !defined(SHA1_LITTLE_ENDIAN) && !defined(SHA1_BIG_ENDIAN) #define SHA1_LITTLE_ENDIAN #endif // If you want variable wiping, #define SHA1_WIPE_VARIABLES, if not, // #define SHA1_NO_WIPE_VARIABLES. If you don't define anything, it // defaults to wiping. #if !defined(SHA1_WIPE_VARIABLES) && !defined(SHA1_NO_WIPE_VARIABLES) #define SHA1_WIPE_VARIABLES #endif #if defined(SHA1_HAS_TCHAR) #include #else #ifdef _MSC_VER #include #else #ifndef TCHAR #define TCHAR char #endif #ifndef _T #define _T(__x) (__x) #define _tmain main #define _tprintf printf #define _getts gets #define _tcslen strlen #define _tfopen fopen #define _tcscpy strcpy #define _tcscat strcat #define _sntprintf snprintf #endif #endif #endif // Fallback, if no 64-bit support #ifndef _fseeki64 #define _fseeki64 fseek #endif #ifndef _ftelli64 #define _ftelli64 ftell #endif /////////////////////////////////////////////////////////////////////////// // Define variable types #define UINT_8 uint8_t #define UINT_32 uint32_t #define UINT_64 uint64_t #define INT_64 int64_t /////////////////////////////////////////////////////////////////////////// // Declare SHA-1 workspace OIIO_NAMESPACE_BEGIN typedef union { UINT_8 c[64]; UINT_32 l[16]; } SHA1_WORKSPACE_BLOCK; class CSHA1 { public: #ifdef SHA1_UTILITY_FUNCTIONS // Different formats for ReportHash enum REPORT_TYPE { REPORT_HEX = 0, REPORT_DIGIT = 1, REPORT_HEX_SHORT = 2 }; #endif // Constructor and destructor CSHA1(); ~CSHA1(); UINT_32 m_state[5]; UINT_32 m_count[2]; UINT_32 m_reserved0[1]; // Memory alignment padding UINT_8 m_buffer[64]; UINT_8 m_digest[20]; UINT_32 m_reserved1[3]; // Memory alignment padding void Reset(); // Update the hash value void Update(const UINT_8* pbData, UINT_32 uLen); #ifdef SHA1_UTILITY_FUNCTIONS // Hash in file contents bool HashFile(const TCHAR* tszFileName); #endif // Finalize hash, call before using ReportHash(Stl) void Final(); #ifdef SHA1_UTILITY_FUNCTIONS bool ReportHash(TCHAR* tszReport, REPORT_TYPE rtReportType = REPORT_HEX) const; #endif #ifdef SHA1_STL_FUNCTIONS bool ReportHashStl(std::basic_string& strOut, REPORT_TYPE rtReportType = REPORT_HEX) const; #endif bool GetHash(UINT_8* pbDest) const; private: // Private SHA-1 transformation void Transform(UINT_32* pState, const UINT_8* pBuffer); // Member variables UINT_8 m_workspace[64]; SHA1_WORKSPACE_BLOCK* m_block; // SHA1 pointer to the byte array above }; OIIO_NAMESPACE_END #endif // ___SHA1_HDR___ openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/oiioversion.h.in0000644000175000017500000001275313151711064023766 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_VERSION_H #define OPENIMAGEIO_VERSION_H // Versioning of the OpenImageIO software #define OIIO_NAMESPACE @OIIO_NAMESPACE@ #define OIIO_VERSION_MAJOR @OIIO_VERSION_MAJOR@ #define OIIO_VERSION_MINOR @OIIO_VERSION_MINOR@ #define OIIO_VERSION_PATCH @OIIO_VERSION_PATCH@ #define OIIO_VERSION_RELEASE_TYPE @OIIO_VERSION_RELEASE_TYPE@ #define OIIO_VERSION_NS @OIIO_VERSION_NS@ #define OIIO_VERSION (10000 * OIIO_VERSION_MAJOR + \ 100 * OIIO_VERSION_MINOR + \ OIIO_VERSION_PATCH) // We also define the old name for backwards compatibility purposes. #define OPENIMAGEIO_VERSION OIIO_VERSION // Magic macros to make OIIO_VERSION_STRING that looks like "1.2.3" #define OIIO_MAKE_VERSION_STRING2(a,b,c,d) #a "." #b "." #c #d #define OIIO_MAKE_VERSION_STRING(a,b,c,d) OIIO_MAKE_VERSION_STRING2(a,b,c,d) #define OIIO_VERSION_STRING \ OIIO_MAKE_VERSION_STRING(OIIO_VERSION_MAJOR, \ OIIO_VERSION_MINOR, OIIO_VERSION_PATCH, \ OIIO_VERSION_RELEASE_TYPE) #define OIIO_INTRO_STRING "OpenImageIO " OIIO_VERSION_STRING " http://www.openimageio.org" #ifndef NAMESPACE_BEGIN # define NAMESPACE_BEGIN(name) namespace name { #endif #ifndef NAMESPACE_END # define NAMESPACE_END(name) } #endif // Macros to use in each file to enter and exit the right name spaces. #define OIIO_NAMESPACE_BEGIN \ NAMESPACE_BEGIN(OIIO_NAMESPACE) \ NAMESPACE_BEGIN(OIIO_VERSION_NS) #define OIIO_NAMESPACE_END \ NAMESPACE_END(OIIO_VERSION_NS) \ using namespace OIIO_VERSION_NS;\ NAMESPACE_END(OIIO_NAMESPACE) #define OIIO_NAMESPACE_USING using namespace OIIO_NAMESPACE; // Establish the name spaces and make an alias 'OIIO' that gives us what // everybody really wants. namespace OIIO_NAMESPACE { namespace OIIO_VERSION_NS { } } namespace OIIO = OIIO_NAMESPACE::OIIO_VERSION_NS; /// Each imageio DSO/DLL should include this statement: /// DLLPUBLIC int FORMAT_imageio_version = OPENIMAGEIO_PLUGIN_VERSION; /// libOpenImageIO will check for compatibility this way. /// This should get bumped any time we change the API in any way that /// will make previously-compiled plugins break. /// /// History: /// Version 3 added supports_rectangles() and write_rectangle() to /// ImageOutput, and added stride parameters to the ImageInput read /// routines. /// Version 10 represents forking from NVIDIA's open source version, /// with which we break backwards compatibility. /// Version 11 teased apart subimage versus miplevel specification in /// the APIs and per-channel formats (introduced in OIIO 0.9). /// Version 12 added read_scanlines(), write_scanlines(), read_tiles(), /// write_tiles(), and ImageInput::supports(). (OIIO 1.0) /// Version 13 added ImageInput::valid_file(). (OIIO 1.1) /// Version 14 added ImageOutput::open() variety for multiple subimages. /// Version 15 added support for "deep" images (changing ImageSpec, /// ImageInput, ImageOutput). /// Version 16 changed the ImageInput functions taking channel ranges /// from firstchan,nchans to chbegin,chend. /// Version 17 changed to int supports(string_view) rather than /// bool supports(const std::string&)). (OIIO 1.6) /// Version 18 changed to add an m_threads member to ImageInput/Output. /// Version 19 changed the definition of DeepData. /// Version 20 added FMT_imageio_library_version() to plugins. (OIIO 1.7) #define OIIO_PLUGIN_VERSION 20 #define OIIO_PLUGIN_NAMESPACE_BEGIN OIIO_NAMESPACE_BEGIN #define OIIO_PLUGIN_NAMESPACE_END OIIO_NAMESPACE_END #ifdef EMBED_PLUGINS #define OIIO_PLUGIN_EXPORTS_BEGIN #define OIIO_PLUGIN_EXPORTS_END #else #define OIIO_PLUGIN_EXPORTS_BEGIN extern "C" { #define OIIO_PLUGIN_EXPORTS_END } #endif // OIIO_BUILD_CPP11 will be 1 if this OIIO was built using C++11 #cmakedefine01 OIIO_BUILD_CPP11 // OIIO_BUILD_CPP14 will be 1 if this OIIO was built using C++14 #cmakedefine01 OIIO_BUILD_CPP14 #endif openimageio-1.7.17~dfsg0.orig/src/include/OpenImageIO/deepdata.h0000644000175000017500000001574213151711064022564 0ustar mfvmfv/* Copyright 2015 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #pragma once #ifndef OPENIMAGEIO_DEEPDATA_H #define OPENIMAGEIO_DEEPDATA_H #include "export.h" #include "oiioversion.h" #include "array_view.h" OIIO_NAMESPACE_BEGIN struct TypeDesc; class ImageSpec; /// Structure to hold "deep" data -- multiple samples per pixel. class OIIO_API DeepData { public: /// Construct an empty DeepData. DeepData (); /// Construct and init from an ImageSpec. DeepData (const ImageSpec &spec); /// Copy constructor DeepData (const DeepData &d); ~DeepData (); /// Copy assignment const DeepData& operator= (const DeepData &d); /// Clear the vectors and reset size to 0. void clear (); /// Deallocate all space in the vectors void free (); /// Initialize size and allocate nsamples, pointers. void init (int npix, int nchan, array_view channeltypes, array_view channelnames); /// Initialize size and allocate nsamples based on the number /// of pixels, channels, and channel types in the ImageSpec. void init (const ImageSpec &spec); /// Retrieve the total number of pixels. int pixels () const; /// Retrieve the number of channels. int channels () const; /// The name of channel c. string_view channelname (int c) const; /// Retrieve the channel type of channel c. TypeDesc channeltype (int c) const; /// The size for each sample of channel c size_t channelsize (int c) const; /// The size for all channels of one sample. size_t samplesize () const; /// Retrieve the number of samples for the given pixel index. int samples (int pixel) const; /// Set the number of samples for the given pixel. This must be called /// after init(). void set_samples (int pixel, int samps); /// Set the number of samples for all pixels. The samples.size() is /// required to match pixels(). void set_all_samples (array_view samples); /// Set the capacity of samples for the given pixel. This must be called /// after init(). void set_capacity (int pixel, int samps); /// Retrieve the capacity (number of allocated samples) for the given /// pixel index. int capacity (int pixel) const; /// Insert n samples at the given pixel, starting at the indexed /// position. void insert_samples (int pixel, int samplepos, int n=1); /// Erase n samples at the given pixel, starting at the indexed /// position. void erase_samples (int pixel, int samplepos, int n=1); /// Retrieve deep sample value within a pixel, cast to a float. float deep_value (int pixel, int channel, int sample) const; /// Retrieve deep sample value within a pixel, as an untigned int. uint32_t deep_value_uint (int pixel, int channel, int sample) const; /// Set deep sample value within a pixel, as a float. void set_deep_value (int pixel, int channel, int sample, float value); /// Set deep sample value within a pixel, as a uint32. void set_deep_value (int pixel, int channel, int sample, uint32_t value); /// Retrieve the pointer to a given pixel/channel/sample, or NULL if /// there are no samples for that pixel. Use with care, and note that /// calls to insert_samples and erase_samples can invalidate pointers /// returend by prior calls to data_ptr. void *data_ptr (int pixel, int channel, int sample); const void *data_ptr (int pixel, int channel, int sample) const; array_view all_channeltypes () const; array_view all_samples () const; array_view all_data () const; /// Fill in the vector with pointers to the start of the first /// channel for each pixel. void get_pointers (std::vector &pointers) const; /// Copy the designated sample from a source DeepData into this /// DeepData. The two DeepData structures need to have the same channel /// layout. bool copy_deep_sample (int pixel, int sample, const DeepData &src, int srcpixel, int srcsample); /// Copy the designated pixel from a source DeepData into this DeepData. /// The two DeepData structures need to have the same channel layout. bool copy_deep_pixel (int pixel, const DeepData &src, int srcpixel); /// Split all samples of that pixel at the given depth zsplit. Samples /// that span z (i.e. z < zsplit < zback) will be split into two samples /// with depth ranges [z,zsplit] and [zsplit,zback] with appropriate /// changes to their color and alpha values. Samples not spanning zsplit /// will remain intact. This operation will have no effect if there are /// not Z and Zback channels present. void split (int pixel, float depth); /// Sort the samples of a pixel by Z. void sort (int pixel); /// Merge any adjacent samples in the pixel that exactly overlap in z /// range. This is only useful if the pixel has previously been split at /// all sample starts and ends, and sorted by Z. Note that this may /// change the number of samples in the pixel. void merge_overlaps (int pixel); /// Merge src's samples into dst's samples void merge_deep_pixels (int pixel, const DeepData &src, int srcpixel); /// Occlusion cull samples hidden behind opaque samples. void occlusion_cull (int pixel); private: class Impl; Impl *m_impl; // holds all the nontrivial stuff int m_npixels, m_nchannels; }; OIIO_NAMESPACE_END #endif // OPENIMAGEIO_DEEPDATA_H openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/0000755000175000017500000000000013151711064017677 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/imageio.cpp0000644000175000017500000007622113151711064022025 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/sysutil.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/hash.h" #include "OpenImageIO/imageio.h" #include "imageio_pvt.h" OIIO_NAMESPACE_BEGIN // Global private data namespace pvt { recursive_mutex imageio_mutex; atomic_int oiio_threads (Sysutil::hardware_concurrency()); atomic_int oiio_exr_threads (Sysutil::hardware_concurrency()); atomic_int oiio_read_chunk (256); int tiff_half (0); ustring plugin_searchpath (OIIO_DEFAULT_PLUGIN_SEARCHPATH); std::string format_list; // comma-separated list of all formats std::string extension_list; // list of all extensions for all formats std::string library_list; // list of all libraries for all formats } using namespace pvt; namespace { // Hidden global OIIO data. static spin_mutex attrib_mutex; static const int maxthreads = 256; // reasonable maximum for sanity check const char *oiio_debug_env = getenv("OPENIMAGEIO_DEBUG"); #ifdef NDEBUG int print_debug (oiio_debug_env ? atoi(oiio_debug_env) : 0); #else int print_debug (oiio_debug_env ? atoi(oiio_debug_env) : 1); #endif }; int openimageio_version () { return OIIO_VERSION; } // To avoid thread oddities, we have the storage area buffering error // messages for seterror()/geterror() be thread-specific. static boost::thread_specific_ptr thread_error_msg; // Return a reference to the string for this thread's error messages, // creating it if none exists for this thread thus far. static std::string & error_msg () { std::string *e = thread_error_msg.get(); if (! e) { e = new std::string; thread_error_msg.reset (e); } return *e; } /// Error reporting for the plugin implementation: call this with /// printf-like arguments. void pvt::seterror (const std::string& message) { error_msg() = message; } std::string geterror () { std::string e = error_msg(); error_msg().clear (); return e; } void pvt::debugmsg_ (string_view message) { recursive_lock_guard lock (pvt::imageio_mutex); if (print_debug) { std::cerr << "OIIO DEBUG: " << message << (message.back() == '\n' ? "" : "\n"); } } bool attribute (string_view name, TypeDesc type, const void *val) { if (name == "threads" && type == TypeDesc::TypeInt) { int ot = Imath::clamp (*(const int *)val, 0, maxthreads); if (ot == 0) ot = Sysutil::hardware_concurrency(); oiio_threads = ot; return true; } spin_lock lock (attrib_mutex); if (name == "read_chunk" && type == TypeDesc::TypeInt) { oiio_read_chunk = *(const int *)val; return true; } if (name == "plugin_searchpath" && type == TypeDesc::TypeString) { plugin_searchpath = ustring (*(const char **)val); return true; } if (name == "exr_threads" && type == TypeDesc::TypeInt) { oiio_exr_threads = Imath::clamp (*(const int *)val, -1, maxthreads); return true; } if (name == "tiff:half" && type == TypeDesc::TypeInt) { tiff_half = *(const int *)val; return true; } if (name == "debug" && type == TypeDesc::TypeInt) { print_debug = *(const int *)val; return true; } return false; } bool getattribute (string_view name, TypeDesc type, void *val) { if (name == "threads" && type == TypeDesc::TypeInt) { *(int *)val = oiio_threads; return true; } spin_lock lock (attrib_mutex); if (name == "read_chunk" && type == TypeDesc::TypeInt) { *(int *)val = oiio_read_chunk; return true; } if (name == "plugin_searchpath" && type == TypeDesc::TypeString) { *(ustring *)val = plugin_searchpath; return true; } if (name == "format_list" && type == TypeDesc::TypeString) { if (format_list.empty()) pvt::catalog_all_plugins (plugin_searchpath.string()); *(ustring *)val = ustring(format_list); return true; } if (name == "extension_list" && type == TypeDesc::TypeString) { if (extension_list.empty()) pvt::catalog_all_plugins (plugin_searchpath.string()); *(ustring *)val = ustring(extension_list); return true; } if (name == "library_list" && type == TypeDesc::TypeString) { if (library_list.empty()) pvt::catalog_all_plugins (plugin_searchpath.string()); *(ustring *)val = ustring(library_list); return true; } if (name == "exr_threads" && type == TypeDesc::TypeInt) { *(int *)val = oiio_exr_threads; return true; } if (name == "tiff:half" && type == TypeDesc::TypeInt) { *(int *)val = tiff_half; return true; } if (name == "debug" && type == TypeDesc::TypeInt) { *(int *)val = print_debug; return true; } return false; } inline long long quantize (float value, long long quant_min, long long quant_max) { value = value * quant_max; return Imath::clamp ((long long)(value + 0.5f), quant_min, quant_max); } namespace { /// Type-independent template for turning potentially /// non-contiguous-stride data (e.g. "RGB RGB ") into contiguous-stride /// ("RGBRGB"). Caller must pass in a dst pointing to enough memory to /// hold the contiguous rectangle. Return a ptr to where the contiguous /// data ended up, which is either dst or src (if the strides indicated /// that data were already contiguous). template const T * _contiguize (const T *src, int nchannels, stride_t xstride, stride_t ystride, stride_t zstride, T *dst, int width, int height, int depth) { int datasize = sizeof(T); if (xstride == nchannels*datasize && ystride == xstride*width && (zstride == ystride*height || !zstride)) return src; if (depth < 1) // Safeguard against volume-unaware clients depth = 1; T *dstsave = dst; if (xstride == nchannels*datasize) { // Optimize for contiguous scanlines, but not from scanline to scanline for (int z = 0; z < depth; ++z, src = (const T *)((char *)src + zstride)) { const T *scanline = src; for (int y = 0; y < height; ++y, dst += nchannels*width, scanline = (const T *)((char *)scanline + ystride)) memcpy(dst, scanline, xstride * width); } } else { for (int z = 0; z < depth; ++z, src = (const T *)((char *)src + zstride)) { const T *scanline = src; for (int y = 0; y < height; ++y, scanline = (const T *)((char *)scanline + ystride)) { const T *pixel = scanline; for (int x = 0; x < width; ++x, pixel = (const T *)((char *)pixel + xstride)) for (int c = 0; c < nchannels; ++c) *dst++ = pixel[c]; } } } return dstsave; } } const void * pvt::contiguize (const void *src, int nchannels, stride_t xstride, stride_t ystride, stride_t zstride, void *dst, int width, int height, int depth, TypeDesc format) { switch (format.basetype) { case TypeDesc::FLOAT : return _contiguize ((const float *)src, nchannels, xstride, ystride, zstride, (float *)dst, width, height, depth); case TypeDesc::INT8: case TypeDesc::UINT8 : return _contiguize ((const char *)src, nchannels, xstride, ystride, zstride, (char *)dst, width, height, depth); case TypeDesc::HALF : DASSERT (sizeof(half) == sizeof(short)); case TypeDesc::INT16 : case TypeDesc::UINT16 : return _contiguize ((const short *)src, nchannels, xstride, ystride, zstride, (short *)dst, width, height, depth); case TypeDesc::INT : case TypeDesc::UINT : return _contiguize ((const int *)src, nchannels, xstride, ystride, zstride, (int *)dst, width, height, depth); case TypeDesc::INT64 : case TypeDesc::UINT64 : return _contiguize ((const long long *)src, nchannels, xstride, ystride, zstride, (long long *)dst, width, height, depth); case TypeDesc::DOUBLE : return _contiguize ((const double *)src, nchannels, xstride, ystride, zstride, (double *)dst, width, height, depth); default: ASSERT (0 && "OpenImageIO::contiguize : bad format"); return NULL; } } const float * pvt::convert_to_float (const void *src, float *dst, int nvals, TypeDesc format) { switch (format.basetype) { case TypeDesc::FLOAT : return (float *)src; case TypeDesc::UINT8 : convert_type ((const unsigned char *)src, dst, nvals); break; case TypeDesc::HALF : convert_type ((const half *)src, dst, nvals); break; case TypeDesc::UINT16 : convert_type ((const unsigned short *)src, dst, nvals); break; case TypeDesc::INT8: convert_type ((const char *)src, dst, nvals); break; case TypeDesc::INT16 : convert_type ((const short *)src, dst, nvals); break; case TypeDesc::INT : convert_type ((const int *)src, dst, nvals); break; case TypeDesc::UINT : convert_type ((const unsigned int *)src, dst, nvals); break; case TypeDesc::INT64 : convert_type ((const long long *)src, dst, nvals); break; case TypeDesc::UINT64 : convert_type ((const unsigned long long *)src, dst, nvals); break; case TypeDesc::DOUBLE : convert_type ((const double *)src, dst, nvals); break; default: ASSERT (0 && "ERROR to_float: bad format"); return NULL; } return dst; } template const void * _from_float (const float *src, T *dst, size_t nvals, long long quant_min, long long quant_max) { if (! src) { // If no source pixels, assume zeroes T z = T(0); for (size_t p = 0; p < nvals; ++p) dst[p] = z; } else if (std::numeric_limits ::is_integer) { // Convert float to non-float native format, with quantization for (size_t p = 0; p < nvals; ++p) dst[p] = (T) quantize (src[p], quant_min, quant_max); } else { // It's a floating-point type of some kind -- we don't apply // quantization if (sizeof(T) == sizeof(float)) { // It's already float -- return the source itself return src; } // Otherwise, it's converting between two fp types for (size_t p = 0; p < nvals; ++p) dst[p] = (T) src[p]; } return dst; } const void * pvt::convert_from_float (const float *src, void *dst, size_t nvals, long long quant_min, long long quant_max, TypeDesc format) { switch (format.basetype) { case TypeDesc::FLOAT : return src; case TypeDesc::HALF : return _from_float (src, (half *)dst, nvals, quant_min, quant_max); case TypeDesc::DOUBLE : return _from_float (src, (double *)dst, nvals, quant_min, quant_max); case TypeDesc::INT8: return _from_float (src, (char *)dst, nvals, quant_min, quant_max); case TypeDesc::UINT8 : return _from_float (src, (unsigned char *)dst, nvals, quant_min, quant_max); case TypeDesc::INT16 : return _from_float (src, (short *)dst, nvals, quant_min, quant_max); case TypeDesc::UINT16 : return _from_float (src, (unsigned short *)dst, nvals, quant_min, quant_max); case TypeDesc::INT : return _from_float (src, (int *)dst, nvals, quant_min, quant_max); case TypeDesc::UINT : return _from_float (src, (unsigned int *)dst, nvals, quant_min, quant_max); case TypeDesc::INT64 : return _from_float (src, (long long *)dst, nvals, quant_min, quant_max); case TypeDesc::UINT64 : return _from_float (src, (unsigned long long *)dst, nvals, quant_min, quant_max); default: ASSERT (0 && "ERROR from_float: bad format"); return NULL; } } const void * pvt::parallel_convert_from_float (const float *src, void *dst, size_t nvals, TypeDesc format, int nthreads) { if (format.basetype == TypeDesc::FLOAT) return src; const size_t quanta = 30000; if (nvals < quanta) nthreads = 1; if (nthreads <= 0) nthreads = oiio_threads; long long quant_min, quant_max; get_default_quantize (format, quant_min, quant_max); if (nthreads <= 1) return convert_from_float (src, dst, nvals, quant_min, quant_max, format); boost::thread_group threads; size_t blocksize = std::max (quanta, size_t((nvals + nthreads - 1) / nthreads)); for (size_t i = 0; i < size_t(nthreads); i++) { size_t begin = i * blocksize; if (begin >= nvals) break; // no more work to divvy up size_t end = std::min (begin + blocksize, nvals); threads.add_thread (new boost::thread ( boost::bind (convert_from_float, src+begin, (char *)dst+begin*format.size(), end-begin, quant_min, quant_max, format))); } threads.join_all (); return dst; } bool convert_types (TypeDesc src_type, const void *src, TypeDesc dst_type, void *dst, int n) { // If no conversion is necessary, just memcpy if ((src_type == dst_type || dst_type.basetype == TypeDesc::UNKNOWN)) { memcpy (dst, src, n * src_type.size()); return true; } if (dst_type == TypeDesc::TypeFloat) { // Special case -- converting non-float to float pvt::convert_to_float (src, (float *)dst, n, src_type); return true; } // Conversion is to a non-float type boost::scoped_array tmp; // In case we need a lot of temp space float *buf = (float *)src; if (src_type != TypeDesc::TypeFloat) { // If src is also not float, convert through an intermediate buffer if (n <= 4096) // If < 16k, use the stack buf = ALLOCA (float, n); else { tmp.reset (new float[n]); // Freed when tmp exists its scope buf = tmp.get(); } pvt::convert_to_float (src, buf, n, src_type); } // Convert float to 'dst_type' switch (dst_type.basetype) { case TypeDesc::UINT8 : convert_type (buf, (unsigned char *)dst, n); break; case TypeDesc::UINT16 : convert_type (buf, (unsigned short *)dst, n); break; case TypeDesc::HALF : convert_type (buf, (half *)dst, n); break; case TypeDesc::INT8 : convert_type (buf, (char *)dst, n); break; case TypeDesc::INT16 : convert_type (buf, (short *)dst, n); break; case TypeDesc::INT : convert_type (buf, (int *)dst, n); break; case TypeDesc::UINT : convert_type (buf, (unsigned int *)dst, n); break; case TypeDesc::INT64 : convert_type (buf, (long long *)dst, n); break; case TypeDesc::UINT64 : convert_type (buf, (unsigned long long *)dst, n); break; case TypeDesc::DOUBLE : convert_type (buf, (double *)dst, n); break; default: return false; // unknown format } return true; } bool convert_image (int nchannels, int width, int height, int depth, const void *src, TypeDesc src_type, stride_t src_xstride, stride_t src_ystride, stride_t src_zstride, void *dst, TypeDesc dst_type, stride_t dst_xstride, stride_t dst_ystride, stride_t dst_zstride, int alpha_channel, int z_channel) { // If no format conversion is taking place, use the simplified // copy_image. if (src_type == dst_type) return copy_image (nchannels, width, height, depth, src, src_type.size()*nchannels, src_xstride, src_ystride, src_zstride, dst, dst_xstride, dst_ystride, dst_zstride); ImageSpec::auto_stride (src_xstride, src_ystride, src_zstride, src_type, nchannels, width, height); ImageSpec::auto_stride (dst_xstride, dst_ystride, dst_zstride, dst_type, nchannels, width, height); bool result = true; bool contig = (src_xstride == stride_t(nchannels * src_type.size()) && dst_xstride == stride_t(nchannels * dst_type.size())); for (int z = 0; z < depth; ++z) { for (int y = 0; y < height; ++y) { const char *f = (const char *)src + (z*src_zstride + y*src_ystride); char *t = (char *)dst + (z*dst_zstride + y*dst_ystride); if (contig) { // Special case: pixels within each row are contiguous // in both src and dst and we're copying all channels. // Be efficient by converting each scanline as a single // unit. (Note that within convert_types, a memcpy will // be used if the formats are identical.) result &= convert_types (src_type, f, dst_type, t, nchannels*width); } else { // General case -- anything goes with strides. for (int x = 0; x < width; ++x) { result &= convert_types (src_type, f, dst_type, t, nchannels); f += src_xstride; t += dst_xstride; } } } } return result; } namespace { // This nonsense is just to get around the 10-arg limits of boost::bind // for compilers that don't have variadic templates. struct convert_image_wrapper { convert_image_wrapper (int nchannels, int width, int height, int depth, const void *src, TypeDesc src_type, stride_t src_xstride, stride_t src_ystride, stride_t src_zstride, void *dst, TypeDesc dst_type, stride_t dst_xstride, stride_t dst_ystride, stride_t dst_zstride, int alpha_channel, int z_channel) : nchannels(nchannels), width(width), height(height), depth(depth), src(src), src_type(src_type), src_xstride(src_xstride), src_ystride(src_ystride), src_zstride(src_zstride), dst(dst), dst_type(dst_type), dst_xstride(dst_xstride), dst_ystride(dst_ystride), dst_zstride(dst_zstride), alpha_channel(alpha_channel), z_channel(z_channel) { } void operator() () { convert_image (nchannels, width, height, depth, src, src_type, src_xstride, src_ystride, src_zstride, dst, dst_type, dst_xstride, dst_ystride, dst_zstride, alpha_channel, z_channel); } private: int nchannels, width, height, depth; const void *src; TypeDesc src_type; stride_t src_xstride, src_ystride, src_zstride; void *dst; TypeDesc dst_type; stride_t dst_xstride, dst_ystride, dst_zstride; int alpha_channel, z_channel; }; } // anon namespace bool parallel_convert_image (int nchannels, int width, int height, int depth, const void *src, TypeDesc src_type, stride_t src_xstride, stride_t src_ystride, stride_t src_zstride, void *dst, TypeDesc dst_type, stride_t dst_xstride, stride_t dst_ystride, stride_t dst_zstride, int alpha_channel, int z_channel, int nthreads) { if (imagesize_t(width)*height*depth*nchannels < 30000) nthreads = 1; if (nthreads <= 0) nthreads = oiio_threads; if (nthreads <= 1) return convert_image (nchannels, width, height, depth, src, src_type, src_xstride, src_ystride, src_zstride, dst, dst_type, dst_xstride, dst_ystride, dst_zstride, alpha_channel, z_channel); ImageSpec::auto_stride (src_xstride, src_ystride, src_zstride, src_type, nchannels, width, height); ImageSpec::auto_stride (dst_xstride, dst_ystride, dst_zstride, dst_type, nchannels, width, height); boost::thread_group threads; int blocksize = std::max (1, (height + nthreads - 1) / nthreads); for (int i = 0; i < nthreads; i++) { int ybegin = i * blocksize; if (ybegin >= height) break; // no more work to divvy up int yend = std::min (ybegin + blocksize, height); convert_image_wrapper ciw (nchannels, width, yend-ybegin, depth, (const char *)src+src_ystride*ybegin, src_type, src_xstride, src_ystride, src_zstride, (char *)dst+dst_ystride*ybegin, dst_type, dst_xstride, dst_ystride, dst_zstride, alpha_channel, z_channel); threads.add_thread (new boost::thread (ciw)); } threads.join_all (); return true; } bool copy_image (int nchannels, int width, int height, int depth, const void *src, stride_t pixelsize, stride_t src_xstride, stride_t src_ystride, stride_t src_zstride, void *dst, stride_t dst_xstride, stride_t dst_ystride, stride_t dst_zstride) { stride_t channelsize = pixelsize / nchannels; ImageSpec::auto_stride (src_xstride, src_ystride, src_zstride, channelsize, nchannels, width, height); ImageSpec::auto_stride (dst_xstride, dst_ystride, dst_zstride, channelsize, nchannels, width, height); bool contig = (src_xstride == dst_xstride && src_xstride == (stride_t)pixelsize); for (int z = 0; z < depth; ++z) { for (int y = 0; y < height; ++y) { const char *f = (const char *)src + (z*src_zstride + y*src_ystride); char *t = (char *)dst + (z*dst_zstride + y*dst_ystride); if (contig) { // Special case: pixels within each row are contiguous // in both src and dst and we're copying all channels. // Be efficient by converting each scanline as a single // unit. memcpy (t, f, width*pixelsize); } else { // General case -- anything goes with strides. for (int x = 0; x < width; ++x) { memcpy (t, f, pixelsize); f += src_xstride; t += dst_xstride; } } } } return true; } void add_dither (int nchannels, int width, int height, int depth, float *data, stride_t xstride, stride_t ystride, stride_t zstride, float ditheramplitude, int alpha_channel, int z_channel, unsigned int ditherseed, int chorigin, int xorigin, int yorigin, int zorigin) { ImageSpec::auto_stride (xstride, ystride, zstride, sizeof(float), nchannels, width, height); char *plane = (char *)data; for (int z = 0; z < depth; ++z, plane += zstride) { char *scanline = plane; for (int y = 0; y < height; ++y, scanline += ystride) { char *pixel = scanline; uint32_t ba = (z+zorigin)*1311 + yorigin+y; uint32_t bb = ditherseed + (chorigin<<24); uint32_t bc = xorigin; for (int x = 0; x < width; ++x, pixel += xstride) { float *val = (float *)pixel; for (int c = 0; c < nchannels; ++c, ++val, ++bc) { bjhash::bjmix (ba, bb, bc); int channel = c+chorigin; if (channel == alpha_channel || channel == z_channel) continue; float dither = bc / float(std::numeric_limits::max()); *val += ditheramplitude * (dither - 0.5f); } } } } } template static void premult_impl (int nchannels, int width, int height, int depth, int chbegin, int chend, T *data, stride_t xstride, stride_t ystride, stride_t zstride, int alpha_channel, int z_channel) { char *plane = (char *)data; for (int z = 0; z < depth; ++z, plane += zstride) { char *scanline = plane; for (int y = 0; y < height; ++y, scanline += ystride) { char *pixel = scanline; for (int x = 0; x < width; ++x, pixel += xstride) { DataArrayProxy val ((T*)pixel); float alpha = val[alpha_channel]; for (int c = chbegin; c < chend; ++c) { if (c == alpha_channel || c == z_channel) continue; val[c] = alpha * val[c]; } } } } } void premult (int nchannels, int width, int height, int depth, int chbegin, int chend, TypeDesc datatype, void *data, stride_t xstride, stride_t ystride, stride_t zstride, int alpha_channel, int z_channel) { if (alpha_channel < 0 || alpha_channel > nchannels) return; // nothing to do ImageSpec::auto_stride (xstride, ystride, zstride, datatype.size(), nchannels, width, height); switch (datatype.basetype) { case TypeDesc::FLOAT : premult_impl (nchannels, width, height, depth, chbegin, chend, (float*)data, xstride, ystride, zstride, alpha_channel, z_channel); break; case TypeDesc::UINT8 : premult_impl (nchannels, width, height, depth, chbegin, chend, (unsigned char*)data, xstride, ystride, zstride, alpha_channel, z_channel); break; case TypeDesc::UINT16 : premult_impl (nchannels, width, height, depth, chbegin, chend, (unsigned short*)data, xstride, ystride, zstride, alpha_channel, z_channel); break; case TypeDesc::HALF : premult_impl (nchannels, width, height, depth, chbegin, chend, (half*)data, xstride, ystride, zstride, alpha_channel, z_channel); break; case TypeDesc::INT8 : premult_impl (nchannels, width, height, depth, chbegin, chend, (char*)data, xstride, ystride, zstride, alpha_channel, z_channel); break; case TypeDesc::INT16 : premult_impl (nchannels, width, height, depth, chbegin, chend, (short*)data, xstride, ystride, zstride, alpha_channel, z_channel); break; case TypeDesc::INT : premult_impl (nchannels, width, height, depth, chbegin, chend, (int*)data, xstride, ystride, zstride, alpha_channel, z_channel); break; case TypeDesc::UINT : premult_impl (nchannels, width, height, depth, chbegin, chend, (unsigned int*)data, xstride, ystride, zstride, alpha_channel, z_channel); break; case TypeDesc::INT64 : premult_impl (nchannels, width, height, depth, chbegin, chend, (int64_t*)data, xstride, ystride, zstride, alpha_channel, z_channel); break; case TypeDesc::UINT64 : premult_impl (nchannels, width, height, depth, chbegin, chend, (uint64_t*)data, xstride, ystride, zstride, alpha_channel, z_channel); break; case TypeDesc::DOUBLE : premult_impl (nchannels, width, height, depth, chbegin, chend, (double*)data, xstride, ystride, zstride, alpha_channel, z_channel); break; default: break; } } bool wrap_black (int &coord, int origin, int width) { return (coord >= origin && coord < (width+origin)); } bool wrap_clamp (int &coord, int origin, int width) { if (coord < origin) coord = origin; else if (coord >= origin+width) coord = origin+width-1; return true; } bool wrap_periodic (int &coord, int origin, int width) { coord -= origin; coord %= width; if (coord < 0) // Fix negative values coord += width; coord += origin; return true; } bool wrap_periodic_pow2 (int &coord, int origin, int width) { DASSERT (ispow2(width)); coord -= origin; coord &= (width - 1); // Shortcut periodic if we're sure it's a pow of 2 coord += origin; return true; } bool wrap_mirror (int &coord, int origin, int width) { coord -= origin; if (coord < 0) coord = -coord - 1; int iter = coord / width; // Which iteration of the pattern? coord -= iter * width; if (iter & 1) // Odd iterations -- flip the sense coord = width - 1 - coord; DASSERT_MSG (coord >= 0 && coord < width, "width=%d, origin=%d, result=%d", width, origin, coord); coord += origin; return true; } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/formatspec.cpp0000644000175000017500000007672113151711064022563 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/imageio.h" #include "imageio_pvt.h" #if USE_EXTERNAL_PUGIXML # include "pugixml.hpp" #else # include "OpenImageIO/pugixml.hpp" #endif OIIO_NAMESPACE_BEGIN // Generate the default quantization parameters, templated on the data // type. template inline void get_default_quantize_ (long long &quant_min, long long &quant_max) { if (std::numeric_limits ::is_integer) { quant_min = (long long) std::numeric_limits ::min(); quant_max = (long long) std::numeric_limits ::max(); } else { quant_min = 0; quant_max = 0; } } // Given the format, set the default quantization range. // Rely on the template version to make life easy. void pvt::get_default_quantize (TypeDesc format, long long &quant_min, long long &quant_max) { switch (format.basetype) { case TypeDesc::UNKNOWN: case TypeDesc::UINT8: get_default_quantize_ (quant_min, quant_max); break; case TypeDesc::UINT16: get_default_quantize_ (quant_min, quant_max); break; case TypeDesc::HALF: get_default_quantize_ (quant_min, quant_max); break; case TypeDesc::FLOAT: get_default_quantize_ (quant_min, quant_max); break; case TypeDesc::INT8: get_default_quantize_ (quant_min, quant_max); break; case TypeDesc::INT16: get_default_quantize_ (quant_min, quant_max); break; case TypeDesc::INT: get_default_quantize_ (quant_min, quant_max); break; case TypeDesc::UINT: get_default_quantize_ (quant_min, quant_max); break; case TypeDesc::INT64: get_default_quantize_ (quant_min, quant_max); break; case TypeDesc::UINT64: get_default_quantize_ (quant_min, quant_max); break; case TypeDesc::DOUBLE: get_default_quantize_ (quant_min, quant_max); break; default: ASSERT_MSG (0, "Unknown data format %d", format.basetype); } } ImageSpec::ImageSpec (TypeDesc format) : x(0), y(0), z(0), width(0), height(0), depth(1), full_x(0), full_y(0), full_z(0), full_width(0), full_height(0), full_depth(0), tile_width(0), tile_height(0), tile_depth(1), nchannels(0), format(format), alpha_channel(-1), z_channel(-1), deep(false) { } ImageSpec::ImageSpec (int xres, int yres, int nchans, TypeDesc format) : x(0), y(0), z(0), width(xres), height(yres), depth(1), full_x(0), full_y(0), full_z(0), full_width(xres), full_height(yres), full_depth(1), tile_width(0), tile_height(0), tile_depth(1), nchannels(nchans), format(format), alpha_channel(-1), z_channel(-1), deep(false) { default_channel_names (); } void ImageSpec::set_format (TypeDesc fmt) { format = fmt; channelformats.clear (); } void ImageSpec::default_channel_names () { channelnames.clear(); channelnames.reserve (nchannels); alpha_channel = -1; z_channel = -1; if (nchannels == 1) { // Special case: 1-channel is named "Y" channelnames.push_back ("Y"); return; } // General case: name channels R, G, B, A, channel4, channel5, ... if (nchannels >= 1) channelnames.push_back ("R"); if (nchannels >= 2) channelnames.push_back ("G"); if (nchannels >= 3) channelnames.push_back ("B"); if (nchannels >= 4) { channelnames.push_back ("A"); alpha_channel = 3; } for (int c = 4; c < nchannels; ++c) channelnames.push_back (Strutil::format("channel%d", c)); } size_t ImageSpec::channel_bytes (int chan, bool native) const { if (chan >= nchannels) return 0; if (!native || channelformats.empty()) return format.size(); else return channelformats[chan].size(); } size_t ImageSpec::pixel_bytes (bool native) const { if (nchannels < 0) return 0; if (!native || channelformats.empty()) return clamped_mult32 ((size_t)nchannels, channel_bytes()); else { size_t sum = 0; for (int i = 0; i < nchannels; ++i) sum += channelformats[i].size(); return sum; } } size_t ImageSpec::pixel_bytes (int chbegin, int chend, bool native) const { if (chbegin < 0) return 0; chend = std::max (chend, chbegin); if (!native || channelformats.empty()) return clamped_mult32 ((size_t)(chend-chbegin), channel_bytes()); else { size_t sum = 0; for (int i = chbegin; i < chend; ++i) sum += channelformats[i].size(); return sum; } } imagesize_t ImageSpec::scanline_bytes (bool native) const { if (width < 0) return 0; return clamped_mult64 ((imagesize_t)width, (imagesize_t)pixel_bytes(native)); } imagesize_t ImageSpec::tile_pixels () const { if (tile_width <= 0 || tile_height <= 0 || tile_depth <= 0) return 0; imagesize_t r = clamped_mult64 ((imagesize_t)tile_width, (imagesize_t)tile_height); if (tile_depth > 1) r = clamped_mult64 (r, (imagesize_t)tile_depth); return r; } imagesize_t ImageSpec::tile_bytes (bool native) const { return clamped_mult64 (tile_pixels(), (imagesize_t)pixel_bytes(native)); } imagesize_t ImageSpec::image_pixels () const { if (width < 0 || height < 0 || depth < 0) return 0; imagesize_t r = clamped_mult64 ((imagesize_t)width, (imagesize_t)height); if (depth > 1) r = clamped_mult64 (r, (imagesize_t)depth); return r; } imagesize_t ImageSpec::image_bytes (bool native) const { return clamped_mult64 (image_pixels(), (imagesize_t)pixel_bytes(native)); } void ImageSpec::attribute (string_view name, TypeDesc type, const void *value) { // Don't allow duplicates ImageIOParameter *f = find_attribute (name); if (! f) { extra_attribs.resize (extra_attribs.size() + 1); f = &extra_attribs.back(); } f->init (name, type, 1, value); } template static void parse_elements (string_view name, TypeDesc type, const char *type_code, string_view value, ImageIOParameter ¶m) { int num_items = type.numelements() * type.aggregate; T *data = (T*) param.data(); // Erase any leading whitespace value.remove_prefix (value.find_first_not_of (" \t")); for (int i = 0; i < num_items; ++i) { // Make a temporary copy so we for sure have a 0-terminated string. std::string temp = value; // Grab the first value from it sscanf (temp.c_str(), type_code, &data[i]); // Skip the value (eat until we find a delimiter -- space, comma, tab) value.remove_prefix (value.find_first_of (" ,\t")); // Skip the delimiter value.remove_prefix (value.find_first_not_of (" ,\t")); if (value.empty()) break; // done if nothing left to parse } } void ImageSpec::attribute (string_view name, TypeDesc type, string_view value) { ImageIOParameter param (name, type, 1, NULL); TypeDesc::BASETYPE basetype = (TypeDesc::BASETYPE)type.basetype; if (basetype == TypeDesc::INT) { parse_elements (name, type, "%d", value, param); } else if (basetype == TypeDesc::UINT) { parse_elements (name, type, "%u", value, param); } else if (basetype == TypeDesc::FLOAT) { parse_elements (name, type, "%f", value, param); } else if (basetype == TypeDesc::DOUBLE) { parse_elements (name, type, "%lf", value, param); } else if (basetype == TypeDesc::INT64) { parse_elements (name, type, "%lld", value, param); } else if (basetype == TypeDesc::UINT64) { parse_elements (name, type, "%llu", value, param); } else if (basetype == TypeDesc::INT16) { parse_elements (name, type, "%hd", value, param); } else if (basetype == TypeDesc::UINT16) { parse_elements (name, type, "%hu", value, param); } else if (type == TypeDesc::STRING) { ustring s (value); param.init (name, TypeDesc::TypeString, 1, &s); } // Don't allow duplicates ImageIOParameter *f = find_attribute (name); if (f) { *f = param; } else { extra_attribs.push_back (param); } } void ImageSpec::erase_attribute (string_view name, TypeDesc searchtype, bool casesensitive) { ImageIOParameterList::iterator iter = extra_attribs.find (name, searchtype, casesensitive); if (iter != extra_attribs.end()) extra_attribs.erase (iter); } ImageIOParameter * ImageSpec::find_attribute (string_view name, TypeDesc searchtype, bool casesensitive) { ImageIOParameterList::iterator iter = extra_attribs.find (name, searchtype, casesensitive); if (iter != extra_attribs.end()) return &(*iter); return NULL; } const ImageIOParameter * ImageSpec::find_attribute (string_view name, TypeDesc searchtype, bool casesensitive) const { ImageIOParameterList::const_iterator iter = extra_attribs.find (name, searchtype, casesensitive); if (iter != extra_attribs.end()) return &(*iter); return NULL; } const ImageIOParameter * ImageSpec::find_attribute (string_view name, ImageIOParameter &tmpparam, TypeDesc searchtype, bool casesensitive) const { ImageIOParameterList::const_iterator iter = extra_attribs.find (name, searchtype, casesensitive); if (iter != extra_attribs.end()) return &(*iter); // Check named items in the ImageSpec structs, not in extra_attrubs #define MATCH(n,t) (((!casesensitive && Strutil::iequals(name,n)) || \ ( casesensitive && name == n)) && \ (searchtype == TypeDesc::UNKNOWN || searchtype == t)) #define GETINT(n) if (MATCH(#n,TypeDesc::TypeInt)) { \ tmpparam.init (#n, TypeDesc::TypeInt, 1, &this->n); \ return &tmpparam; \ } GETINT(nchannels); GETINT(width); GETINT(height); GETINT(depth); GETINT(x); GETINT(y); GETINT(z); GETINT(full_width); GETINT(full_height); GETINT(full_depth); GETINT(full_x); GETINT(full_y); GETINT(full_z); GETINT(tile_width); GETINT(tile_height); GETINT(tile_depth); GETINT(alpha_channel); GETINT(z_channel); // some special cases if (MATCH("geom", TypeDesc::TypeString)) { ustring s = (depth <= 1 && full_depth <= 1) ? ustring::format ("%dx%d%+d%+d", width, height, x, y) : ustring::format ("%dx%dx%d%+d%+d%+d", width, height, depth, x, y, z); tmpparam.init ("geom", TypeDesc::TypeString, 1, &s); return &tmpparam; } if (MATCH("full_geom", TypeDesc::TypeString)) { ustring s = (depth <= 1 && full_depth <= 1) ? ustring::format ("%dx%d%+d%+d", full_width, full_height, full_x, full_y) : ustring::format ("%dx%dx%d%+d%+d%+d", full_width, full_height, full_depth, full_x, full_y, full_z); tmpparam.init ("full_geom", TypeDesc::TypeString, 1, &s); return &tmpparam; } #undef GETINT #undef MATCH return NULL; } int ImageSpec::get_int_attribute (string_view name, int val) const { ImageIOParameter tmpparam; const ImageIOParameter *p = find_attribute (name, tmpparam); if (p) { if (p->type() == TypeDesc::INT) val = *(const int *)p->data(); else if (p->type() == TypeDesc::UINT) val = (int) *(const unsigned int *)p->data(); else if (p->type() == TypeDesc::INT16) val = *(const short *)p->data(); else if (p->type() == TypeDesc::UINT16) val = *(const unsigned short *)p->data(); else if (p->type() == TypeDesc::INT8) val = *(const char *)p->data(); else if (p->type() == TypeDesc::UINT8) val = *(const unsigned char *)p->data(); else if (p->type() == TypeDesc::INT64) val = *(const long long *)p->data(); else if (p->type() == TypeDesc::UINT64) val = *(const unsigned long long *)p->data(); } return val; } float ImageSpec::get_float_attribute (string_view name, float val) const { ImageIOParameter tmpparam; const ImageIOParameter *p = find_attribute (name, tmpparam); if (p) { if (p->type() == TypeDesc::FLOAT) val = *(const float *)p->data(); else if (p->type() == TypeDesc::HALF) val = *(const half *)p->data(); else if (p->type() == TypeDesc::DOUBLE) val = (float) *(const double *)p->data(); else if (p->type() == TypeDesc::INT) val = (float) *(const int *)p->data(); else if (p->type() == TypeDesc::UINT) val = (float) *(const unsigned int *)p->data(); else if (p->type() == TypeDesc::INT16) val = (float) *(const short *)p->data(); else if (p->type() == TypeDesc::UINT16) val = (float) *(const unsigned short *)p->data(); else if (p->type() == TypeDesc::INT8) val = (float) *(const char *)p->data(); else if (p->type() == TypeDesc::UINT8) val = (float) *(const unsigned char *)p->data(); else if (p->type() == TypeDesc::INT64) val = (float) *(const long long *)p->data(); else if (p->type() == TypeDesc::UINT64) val = (float) *(const unsigned long long *)p->data(); } return val; } string_view ImageSpec::get_string_attribute (string_view name, string_view val) const { ImageIOParameter tmpparam; const ImageIOParameter *p = find_attribute (name, tmpparam, TypeDesc::STRING); if (p) return *(ustring *)p->data(); else return val; } namespace { // make an anon namespace template < typename T > void formatType(const ImageIOParameter& p, const int n, const TypeDesc& element, const char* formatString, std::string& out) { const T *f = (const T *)p.data(); for (int i = 0; i < n; ++i) { if (i) out += ", "; for (int c = 0; c < (int)element.aggregate; ++c, ++f) out += Strutil::format (formatString, (c ? " " : ""), f[0]); } } static std::string format_raw_metadata (const ImageIOParameter &p, int maxsize=16) { std::string out; TypeDesc element = p.type().elementtype(); int nfull = int(p.type().numelements()) * p.nvalues(); int n = std::min (nfull, maxsize); if (element.basetype == TypeDesc::STRING) { for (int i = 0; i < n; ++i) { const char *s = ((const char **)p.data())[i]; out += Strutil::format ("%s\"%s\"", (i ? ", " : ""), s ? s : ""); } } else if (element.basetype == TypeDesc::FLOAT) { formatType< float >(p, n, element, "%s%g", out); } else if (element.basetype == TypeDesc::DOUBLE) { formatType< double >(p, n, element, "%s%g", out); } else if (element.basetype == TypeDesc::HALF) { formatType< half >(p, n, element, "%s%g", out); } else if (element.basetype == TypeDesc::INT) { formatType< int >(p, n, element, "%s%d", out); } else if (element.basetype == TypeDesc::UINT) { formatType< unsigned int >(p, n, element, "%s%d", out); } else if (element.basetype == TypeDesc::UINT16) { formatType< unsigned short >(p, n, element, "%s%u", out); } else if (element.basetype == TypeDesc::INT16) { formatType< short >(p, n, element, "%s%d", out); } else if (element.basetype == TypeDesc::UINT64) { formatType< unsigned long long >(p, n, element, "%s%llu", out); } else if (element.basetype == TypeDesc::INT64) { formatType< long long >(p, n, element, "%s%lld", out); } else if (element.basetype == TypeDesc::UINT8) { formatType< unsigned char >(p, n, element, "%s%d", out); } else if (element.basetype == TypeDesc::INT8) { formatType< char >(p, n, element, "%s%d", out); } else { out += Strutil::format (" (base %d, agg %d vec %d)", p.type().basetype, p.type().aggregate, p.type().vecsemantics); } if (n < nfull) out += ", ..."; return out; } struct LabelTable { int value; const char *label; }; static std::string explain_justprint (const ImageIOParameter &p, const void *extradata) { return format_raw_metadata(p) + " " + std::string ((const char *)extradata); } static std::string explain_labeltable (const ImageIOParameter &p, const void *extradata) { int val; if (p.type() == TypeDesc::INT) val = *(const int *)p.data(); else if (p.type() == TypeDesc::UINT) val = (int) *(const unsigned int *)p.data(); else if (p.type() == TypeDesc::STRING) val = (int) **(const char **)p.data(); else return std::string(); for (const LabelTable *lt = (const LabelTable *)extradata; lt->label; ++lt) if (val == lt->value) return std::string (lt->label); return std::string(); // nothing } static std::string explain_shutterapex (const ImageIOParameter &p, const void *extradata) { if (p.type() == TypeDesc::FLOAT) { double val = pow (2.0, - (double)*(float *)p.data()); if (val > 1) return Strutil::format ("%g s", val); else return Strutil::format ("1/%g s", floor(1.0/val)); } return std::string(); } static std::string explain_apertureapex (const ImageIOParameter &p, const void *extradata) { if (p.type() == TypeDesc::FLOAT) return Strutil::format ("f/%g", powf (2.0f, *(float *)p.data()/2.0f)); return std::string(); } static std::string explain_ExifFlash (const ImageIOParameter &p, const void *extradata) { int val = 0; if (p.type() == TypeDesc::INT) val = *(int *)p.data(); else if (p.type() == TypeDesc::UINT) val = *(unsigned int *)p.data(); else return std::string(); return Strutil::format ("%s%s%s%s%s%s%s%s", (val&1) ? "flash fired" : "no flash", (val&6) == 4 ? ", no strobe return" : "", (val&6) == 6 ? ", strobe return" : "", (val&24) == 8 ? ", compulsary flash" : "", (val&24) == 16 ? ", flash supression" : "", (val&24) == 24 ? ", auto flash" : "", (val&32) ? ", no flash available" : "", (val&64) ? ", red-eye reduction" : ""); } static LabelTable ExifExposureProgram_table[] = { { 0, "" }, { 1, "manual" }, { 2, "normal program" }, { 3, "aperture priority" }, { 4, "shutter priority" }, { 5, "Creative program, biased toward DOF" }, { 6, "Action program, biased toward fast shutter" }, { 7, "Portrait mode, foreground in focus" }, { 8, "Landscape mode, background in focus" }, { 9, "bulb" }, { -1, NULL } }; static LabelTable ExifLightSource_table[] = { { 0, "unknown" }, { 1, "daylight" }, { 2, "tungsten/incandescent" }, { 4, "flash" }, { 9, "fine weather" }, { 10, "cloudy" }, { 11, "shade" }, { 12, "daylight fluorescent D 5700-7100K" }, { 13, "day white fluorescent N 4600-5400K" }, { 14, "cool white fluorescent W 3900-4500K" }, { 15, "white fluorescent WW 3200-3700K" }, { 17, "standard light A" }, { 18, "standard light B" }, { 19, "standard light C" }, { 20, "D55" }, { 21, "D65" }, { 22, "D75" }, { 23, "D50" }, { 24, "ISO studio tungsten" }, { 255, "other" }, { -1, NULL } }; static LabelTable ExifMeteringMode_table[] = { { 0, "" }, { 1, "average" }, { 2, "center-weighted average" }, { 3, "spot" }, { 4, "multi-spot" }, { 5, "pattern" }, { 6, "partial" }, { -1, NULL } }; static LabelTable ExifSubjectDistanceRange_table[] = { { 0, "unknown" }, { 1, "macro" }, { 2, "close" }, { 3, "distant" }, { -1, NULL } }; static LabelTable ExifSceneCaptureType_table[] = { { 0, "standard" }, { 1, "landscape" }, { 2, "portrait" }, { 3, "night scene" }, { -1, NULL } }; static LabelTable orientation_table[] = { { 1, "normal" }, { 2, "flipped horizontally" }, { 3, "rotated 180 deg" }, { 4, "flipped vertically" }, { 5, "transposed top<->left" }, { 6, "rotated 90 deg CW" }, { 7, "transverse top<->right" }, { 8, "rotated 90 deg CCW" }, { -1, NULL } }; static LabelTable resunit_table[] = { { 1, "none" }, { 2, "inches" }, { 3, "cm" }, { 4, "mm" }, { 5, "um" }, { -1, NULL } }; static LabelTable ExifSensingMethod_table[] = { { 1, "undefined" }, { 2, "1-chip color area" }, { 3, "2-chip color area" }, { 4, "3-chip color area" }, { 5, "color sequential area" }, { 7, "trilinear" }, { 8, "color trilinear" }, { -1, NULL } }; static LabelTable ExifFileSource_table[] = { { 1, "film scanner" }, { 2, "reflection print scanner" }, { 3, "digital camera" }, { -1, NULL } }; static LabelTable ExifSceneType_table[] = { { 1, "directly photographed" }, { -1, NULL } }; static LabelTable ExifExposureMode_table[] = { { 0, "auto" }, { 1, "manual" }, { 2, "auto-bracket" }, { -1, NULL } }; static LabelTable ExifWhiteBalance_table[] = { { 0, "auto" }, { 1, "manual" }, { -1, NULL } }; static LabelTable ExifGainControl_table[] = { { 0, "none" }, { 1, "low gain up" }, { 2, "high gain up" }, { 3, "low gain down" }, { 4, "high gain down" }, { -1, NULL } }; static LabelTable yesno_table[] = { { 0, "no" }, { 1, "yes" }, { -1, NULL } }; static LabelTable softhard_table[] = { { 0, "normal" }, { 1, "soft" }, { 2, "hard" }, { -1, NULL } }; static LabelTable lowhi_table[] = { { 0, "normal" }, { 1, "low" }, { 2, "high" }, { -1, NULL } }; static LabelTable GPSAltitudeRef_table[] = { { 0, "above sea level" }, { 1, "below sea level" }, { -1, NULL } }; static LabelTable GPSStatus_table[] = { { 'A', "measurement active" }, { 'V', "measurement void" }, { -1, NULL } }; static LabelTable GPSMeasureMode_table[] = { { '2', "2-D" }, { '3', "3-D" }, { -1, NULL } }; static LabelTable GPSSpeedRef_table[] = { { 'K', "km/hour" }, { 'M', "miles/hour" }, { 'N', "knots" }, { -1, NULL } }; static LabelTable GPSDestDistanceRef_table[] = { { 'K', "km" }, { 'M', "miles" }, { 'N', "nautical miles" }, { -1, NULL } }; static LabelTable magnetic_table[] = { { 'T', "true north" }, { 'M', "magnetic north" }, { -1, NULL } }; typedef std::string (*ExplainerFunc) (const ImageIOParameter &p, const void *extradata); struct ExplanationTableEntry { const char *oiioname; ExplainerFunc explainer; const void *extradata; }; static ExplanationTableEntry explanation[] = { { "ResolutionUnit", explain_labeltable, resunit_table }, { "Orientation", explain_labeltable, orientation_table }, { "Exif:ExposureProgram", explain_labeltable, ExifExposureProgram_table }, { "Exif:ShutterSpeedValue", explain_shutterapex, NULL }, { "Exif:ApertureValue", explain_apertureapex, NULL }, { "Exif:MaxApertureValue", explain_apertureapex, NULL }, { "Exif:SubjectDistance", explain_justprint, "m" }, { "Exif:MeteringMode", explain_labeltable, ExifMeteringMode_table }, { "Exif:LightSource", explain_labeltable, ExifLightSource_table }, { "Exif:Flash", explain_ExifFlash, NULL }, { "Exif:FocalLength", explain_justprint, "mm" }, { "Exif:FlashEnergy", explain_justprint, "BCPS" }, { "Exif:FocalPlaneResolutionUnit", explain_labeltable, resunit_table }, { "Exif:SensingMethod", explain_labeltable, ExifSensingMethod_table }, { "Exif:FileSource", explain_labeltable, ExifFileSource_table }, { "Exif:SceneType", explain_labeltable, ExifSceneType_table }, { "Exif:CustomRendered", explain_labeltable, yesno_table }, { "Exif:ExposureMode", explain_labeltable, ExifExposureMode_table }, { "Exif:WhiteBalance", explain_labeltable, ExifWhiteBalance_table }, { "Exif:SceneCaptureType", explain_labeltable, ExifSceneCaptureType_table }, { "Exif:GainControl", explain_labeltable, ExifGainControl_table }, { "Exif:Contrast", explain_labeltable, softhard_table }, { "Exif:Saturation", explain_labeltable, lowhi_table }, { "Exif:Sharpness", explain_labeltable, softhard_table }, { "Exif:SubjectDistanceRange", explain_labeltable, ExifSubjectDistanceRange_table }, { "GPS:AltitudeRef", explain_labeltable, GPSAltitudeRef_table }, { "GPS:Altitude", explain_justprint, "m" }, { "GPS:Status", explain_labeltable, GPSStatus_table }, { "GPS:MeasureMode", explain_labeltable, GPSMeasureMode_table }, { "GPS:SpeedRef", explain_labeltable, GPSSpeedRef_table }, { "GPS:TrackRef", explain_labeltable, magnetic_table }, { "GPS:ImgDirectionRef", explain_labeltable, magnetic_table }, { "GPS:DestBearingRef", explain_labeltable, magnetic_table }, { "GPS:DestDistanceRef", explain_labeltable, GPSDestDistanceRef_table }, { "GPS:Differential", explain_labeltable, yesno_table }, { NULL, NULL, NULL } }; } // end anon namespace std::string ImageSpec::metadata_val (const ImageIOParameter &p, bool human) { std::string out = format_raw_metadata (p, human ? 16 : 1024); if (human) { std::string nice; for (int e = 0; explanation[e].oiioname; ++e) { if (! strcmp (explanation[e].oiioname, p.name().c_str()) && explanation[e].explainer) { nice = explanation[e].explainer (p, explanation[e].extradata); break; } } if (p.type() == TypeDesc::TypeTimeCode) { Imf::TimeCode tc = *reinterpret_cast(p.data()); nice = Strutil::format ("%02d:%02d:%02d:%02d", tc.hours(), tc.minutes(), tc.seconds(), tc.frame()); } if (nice.length()) out = out + " (" + nice + ")"; } return out; } namespace { // Helper functions for from_xml () and to_xml () methods. using namespace pugi; static void add_node (xml_node &node, const std::string &node_name, const char *val) { xml_node newnode = node.append_child(); newnode.set_name (node_name.c_str ()); newnode.append_child (node_pcdata).set_value (val); } static void add_node (xml_node &node, const std::string &node_name, const int val) { char buf[64]; sprintf (buf, "%d", val); add_node (node, node_name, buf); } static void add_channelnames_node (xml_document &doc, const std::vector &channelnames) { xml_node channel_node = doc.child ("ImageSpec").append_child(); channel_node.set_name ("channelnames"); BOOST_FOREACH (std::string name, channelnames) { add_node (channel_node, "channelname", name.c_str ()); } } static void get_channelnames (const xml_node &n, std::vector &channelnames) { xml_node channel_node = n.child ("channelnames"); for (xml_node n = channel_node.child ("channelname"); n; n = n.next_sibling ("channelname")) { channelnames.push_back (n.child_value ()); } } } // end of anonymous namespace std::string ImageSpec::to_xml () const { xml_document doc; doc.append_child ().set_name ("ImageSpec"); doc.child ("ImageSpec").append_attribute ("version") = OIIO_PLUGIN_VERSION; xml_node node = doc.child ("ImageSpec"); add_node (node, "x", x); add_node (node, "y", y); add_node (node, "z", z); add_node (node, "width", width); add_node (node, "height", height); add_node (node, "depth", depth); add_node (node, "full_x", full_x); add_node (node, "full_y", full_y); add_node (node, "full_z", full_z); add_node (node, "full_width", full_width); add_node (node, "full_height", full_height); add_node (node, "full_depth", full_depth); add_node (node, "tile_width", tile_width); add_node (node, "tile_height", tile_height); add_node (node, "tile_depth", tile_depth); add_node (node, "format", format.c_str ()); add_node (node, "nchannels", nchannels); add_channelnames_node (doc, channelnames); add_node (node, "alpha_channel", alpha_channel); add_node (node, "z_channel", z_channel); add_node (node, "deep", int(deep)); // FIXME: What about extra attributes? std::ostringstream result; doc.print (result, ""); return result.str(); } void ImageSpec::from_xml (const char *xml) { xml_document doc; doc.load (xml); xml_node n = doc.child ("ImageSpec"); //int version = n.attribute ("version").as_int(); // Fields for version == 10 (current) x = atoi (n.child_value ("x")); y = atoi (n.child_value ("y")); z = atoi (n.child_value ("z")); width = atoi (n.child_value ("width")); height = atoi (n.child_value ("height")); depth = atoi (n.child_value ("depth")); full_x = atoi (n.child_value ("full_x")); full_y = atoi (n.child_value ("full_y")); full_z = atoi (n.child_value ("full_z")); full_width = atoi (n.child_value ("full_width")); full_height = atoi (n.child_value ("full_height")); full_depth = atoi (n.child_value ("full_depth")); tile_width = atoi (n.child_value ("tile_width")); tile_height = atoi (n.child_value ("tile_height")); tile_depth = atoi (n.child_value ("tile_depth")); format = TypeDesc (n.child_value ("format")); nchannels = atoi (n.child_value ("nchannels")); get_channelnames (n, channelnames); alpha_channel = atoi (n.child_value ("alpha_channel")); z_channel = atoi (n.child_value ("z_channel")); deep = atoi (n.child_value ("deep")); // FIXME: What about extra attributes? // If version == 11 {fill new fields} } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/CMakeLists.txt0000644000175000017500000002010613151711064022436 0ustar mfvmfvif (VERBOSE) message (STATUS "Create imagio_pvt.h from imageio_pvt.h.in") endif () file (TO_NATIVE_PATH "${PLUGIN_SEARCH_PATH}" PLUGIN_SEARCH_PATH_NATIVE) configure_file (imageio_pvt.h.in "${CMAKE_CURRENT_BINARY_DIR}/imageio_pvt.h" @ONLY) include_directories("${CMAKE_CURRENT_BINARY_DIR}") file (GLOB libOpenImageIO_hdrs ../include/OpenImageIO/*.h) if (NOT USE_EXTERNAL_PUGIXML) list (APPEND libOpenImageIO_hdrs ../include/OpenImageIO/pugiconfig.hpp ../include/OpenImageIO/pugixml.hpp ../include/OpenImageIO/pugixml.cpp ) if (CMAKE_COMPILER_IS_GNUCC AND NOT ${GCC_VERSION} VERSION_LESS 6.0) set_source_files_properties (formatspec.cpp xmp.cpp PROPERTIES COMPILE_FLAGS -Wno-error=placement-new) endif () endif() # Make the build complete for newer ffmpeg versions (3.1.1+) that have # marked m_format_context->streams[i]->codec as deprecated. # FIXME -- at some point, come back and figure out how to fix for real # before the field disappears entirely. if (NOT MSVC) set_source_files_properties (../ffmpeg.imageio/ffmpeginput.cpp PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations") endif() list (APPEND libOpenImageIO_srcs deepdata.cpp exif.cpp formatspec.cpp imagebuf.cpp imageinput.cpp imageio.cpp imageioplugin.cpp imageoutput.cpp iptc.cpp xmp.cpp color_ocio.cpp imagebufalgo.cpp imagebufalgo_compare.cpp imagebufalgo_copy.cpp imagebufalgo_deep.cpp imagebufalgo_draw.cpp imagebufalgo_pixelmath.cpp imagebufalgo_xform.cpp imagebufalgo_yee.cpp imagebufalgo_opencv.cpp maketexture.cpp ../libutil/argparse.cpp ../libutil/errorhandler.cpp ../libutil/filesystem.cpp ../libutil/farmhash.cpp ../libutil/filter.cpp ../libutil/hashes.cpp ../libutil/paramlist.cpp ../libutil/plugin.cpp ../libutil/SHA1.cpp ../libutil/strutil.cpp ../libutil/sysutil.cpp ../libutil/timer.cpp ../libutil/typedesc.cpp ../libutil/ustring.cpp ../libutil/xxhash.cpp ../libtexture/texturesys.cpp ../libtexture/texture3d.cpp ../libtexture/environment.cpp ../libtexture/texoptions.cpp ../libtexture/imagecache.cpp ${libOpenImageIO_hdrs} ) # If the 'EMBEDPLUGINS' option is set, we want to compile the source for # all the plugins into libOpenImageIO. if (EMBEDPLUGINS) add_definitions ("-DEMBED_PLUGINS=1" ${format_plugin_definitions}) include_directories (${format_plugin_include_dirs}) # Organize the embedded plugins into source folders set (plugin_types "") foreach (src ${libOpenImageIO_srcs}) if (src MATCHES "^.+/([^/]+)\\.imageio/.+$") set (plugin_types ${plugin_types} ${CMAKE_MATCH_1}) endif () endforeach () list (REMOVE_DUPLICATES plugin_types) foreach (plugin ${plugin_types}) source_group ("Plugins\\${plugin}" REGULAR_EXPRESSION "^.+/${plugin}\\.imageio/.+$" ) endforeach () endif () # Source groups for libutil and libtexture source_group ("libutil" REGULAR_EXPRESSION ".+/libutil/.+") source_group ("libtexture" REGULAR_EXPRESSION ".+/libtexture/.+") if (BUILDSTATIC) add_library (OpenImageIO STATIC ${libOpenImageIO_srcs}) else () add_library (OpenImageIO SHARED ${libOpenImageIO_srcs}) endif () target_link_libraries (OpenImageIO ${VISIBILITY_COMMAND} ${VISIBILITY_MAP_COMMAND} ${format_plugin_libs} # Add all the target link libraries from the plugins ${Boost_LIBRARIES}) # Include OpenColorIO if using it if (USE_OCIO AND OCIO_FOUND) if (VERBOSE) message (STATUS "Linking OpenColorIO ${OCIO_LIBRARIES}") endif () target_link_libraries (OpenImageIO ${OCIO_LIBRARIES}) endif () # Include OpenCV if using it if (OpenCV_FOUND) include_directories (${OpenCV_INCLUDE_DIR}) target_link_libraries (OpenImageIO ${OpenCV_LIBS}) endif () # Include OpenSSL if using it if (OPENSSL_FOUND) include_directories (${OPENSSL_INCLUDE_DIR}) target_link_libraries (OpenImageIO ${OPENSSL_LIBRARIES}) endif () # Include Freetype if using it if (FREETYPE_FOUND) include_directories (${FREETYPE_INCLUDE_DIRS}) target_link_libraries (OpenImageIO ${FREETYPE_LIBRARIES} ${BZIP2_LIBRARIES}) endif () if (WIN32) target_link_libraries (OpenImageIO psapi.lib) endif () add_dependencies (OpenImageIO "${CMAKE_CURRENT_SOURCE_DIR}/libOpenImageIO.map") if (USE_EXTERNAL_PUGIXML) target_link_libraries (OpenImageIO ${PUGIXML_LIBRARIES}) endif () target_link_libraries (OpenImageIO ${ILMBASE_LIBRARIES}) target_link_libraries (OpenImageIO ${OPENEXR_LIBRARIES}) target_link_libraries (OpenImageIO ${ZLIB_LIBRARIES}) if (VERBOSE) message(STATUS "Setting SOVERSION to: ${SOVERSION}") endif () set_target_properties(OpenImageIO PROPERTIES VERSION ${OIIO_VERSION_MAJOR}.${OIIO_VERSION_MINOR}.${OIIO_VERSION_PATCH} SOVERSION ${SOVERSION} ) # For consistency with the linux SpComp2s, create Mac OS X SpComp2s # with a .so suffix instead of a .dylib suffix. if(DEFINED OVERRIDE_SHARED_LIBRARY_SUFFIX) if (VERBOSE) message(STATUS "Setting SUFFIX to: ${OVERRIDE_SHARED_LIBRARY_SUFFIX}") endif () set_target_properties(OpenImageIO PROPERTIES SUFFIX ${OVERRIDE_SHARED_LIBRARY_SUFFIX} ) endif(DEFINED OVERRIDE_SHARED_LIBRARY_SUFFIX) if (EXTRA_DSO_LINK_ARGS) set_target_properties (OpenImageIO PROPERTIES LINK_FLAGS ${EXTRA_DSO_LINK_ARGS}) endif() oiio_install_targets (OpenImageIO) # Testing if (OIIO_BUILD_TESTS) add_executable (imagebuf_test imagebuf_test.cpp) set_target_properties (imagebuf_test PROPERTIES FOLDER "Unit Tests") target_link_libraries (imagebuf_test OpenImageIO ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) add_test (unit_imagebuf imagebuf_test) add_executable (imagecache_test imagecache_test.cpp) set_target_properties (imagecache_test PROPERTIES FOLDER "Unit Tests") target_link_libraries (imagecache_test OpenImageIO ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) add_test (unit_imagecache imagecache_test) add_executable (imagebufalgo_test imagebufalgo_test.cpp) set_target_properties (imagebufalgo_test PROPERTIES FOLDER "Unit Tests") target_link_libraries (imagebufalgo_test OpenImageIO ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) add_test (unit_imagebufalgo imagebufalgo_test) add_executable (imagespec_test imagespec_test.cpp) set_target_properties (imagespec_test PROPERTIES FOLDER "Unit Tests") target_link_libraries (imagespec_test OpenImageIO ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) add_test (unit_imagespec imagespec_test) add_executable (imagespeed_test imagespeed_test.cpp) set_target_properties (imagespeed_test PROPERTIES FOLDER "Unit Tests") target_link_libraries (imagespeed_test OpenImageIO ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) #add_test (imagespeed_test imagespeed_test) add_executable (compute_test compute_test.cpp) set_target_properties (compute_test PROPERTIES FOLDER "Unit Tests") target_link_libraries (compute_test OpenImageIO ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) add_test (unit_compute compute_test) endif (OIIO_BUILD_TESTS) openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/imagebufalgo_pixelmath.cpp0000644000175000017500000015572113151711064025113 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// \file /// Implementation of ImageBufAlgo algorithms that do math on /// single pixels at a time. #include #include #include #include #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/imagebufalgo_util.h" #include "OpenImageIO/deepdata.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/simd.h" OIIO_NAMESPACE_BEGIN template static bool clamp_ (ImageBuf &dst, const ImageBuf &src, const float *min, const float *max, bool clampalpha01, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(clamp_, OIIO::ref(dst), OIIO::cref(src), min, max, clampalpha01, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case ImageBuf::ConstIterator s (src, roi); for (ImageBuf::Iterator d (dst, roi); ! d.done(); ++d, ++s) { for (int c = roi.chbegin; c < roi.chend; ++c) d[c] = OIIO::clamp (s[c], min[c], max[c]); } int a = src.spec().alpha_channel; if (clampalpha01 && a >= roi.chbegin && a < roi.chend) { for (ImageBuf::Iterator d (dst, roi); ! d.done(); ++d) d[a] = OIIO::clamp (d[a], 0.0f, 1.0f); } return true; } bool ImageBufAlgo::clamp (ImageBuf &dst, const ImageBuf &src, const float *min, const float *max, bool clampalpha01, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &src)) return false; std::vector minvec, maxvec; if (! min) { minvec.resize (dst.nchannels(), -std::numeric_limits::max()); min = &minvec[0]; } if (! max) { maxvec.resize (dst.nchannels(), std::numeric_limits::max()); max = &maxvec[0]; } bool ok; OIIO_DISPATCH_TYPES2 (ok, "clamp", clamp_, dst.spec().format, src.spec().format, dst, src, min, max, clampalpha01, roi, nthreads); return ok; } bool ImageBufAlgo::clamp (ImageBuf &dst, const ImageBuf &src, float min, float max, bool clampalpha01, ROI roi, int nthreads) { std::vector minvec (src.nchannels(), min); std::vector maxvec (src.nchannels(), max); return clamp (dst, src, &minvec[0], &maxvec[0], clampalpha01, roi, nthreads); } template static bool add_impl (ImageBuf &R, const ImageBuf &A, const ImageBuf &B, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(add_impl, OIIO::ref(R), OIIO::cref(A), OIIO::cref(B), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case ImageBuf::Iterator r (R, roi); ImageBuf::ConstIterator a (A, roi); ImageBuf::ConstIterator b (B, roi); for ( ; !r.done(); ++r, ++a, ++b) for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = a[c] + b[c]; return true; } template static bool add_impl (ImageBuf &R, const ImageBuf &A, const float *b, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(add_impl, OIIO::ref(R), OIIO::cref(A), b, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case: if (R.deep()) { // Deep case array_view channeltypes (R.deepdata()->all_channeltypes()); ImageBuf::Iterator r (R, roi); ImageBuf::ConstIterator a (A, roi); for ( ; !r.done(); ++r, ++a) { for (int samp = 0, samples = r.deep_samples(); samp < samples; ++samp) { for (int c = roi.chbegin; c < roi.chend; ++c) { if (channeltypes[c].basetype == TypeDesc::UINT32) r.set_deep_value (c, samp, a.deep_value_uint(c, samp)); else r.set_deep_value (c, samp, a.deep_value(c, samp) + b[c]); } } } return true; } ImageBuf::Iterator r (R, roi); ImageBuf::ConstIterator a (A, roi); for ( ; !r.done(); ++r, ++a) for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = a[c] + b[c]; return true; } bool ImageBufAlgo::add (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &A, &B)) return false; ROI origroi = roi; roi.chend = std::min (roi.chend, std::min (A.nchannels(), B.nchannels())); bool ok; OIIO_DISPATCH_COMMON_TYPES3 (ok, "add", add_impl, dst.spec().format, A.spec().format, B.spec().format, dst, A, B, roi, nthreads); if (roi.chend < origroi.chend && A.nchannels() != B.nchannels()) { // Edge case: A and B differed in nchannels, we allocated dst to be // the bigger of them, but adjusted roi to be the lesser. Now handle // the channels that got left out because they were not common to // all the inputs. ASSERT (roi.chend <= dst.nchannels()); roi.chbegin = roi.chend; roi.chend = origroi.chend; if (A.nchannels() > B.nchannels()) { // A exists copy (dst, A, dst.spec().format, roi, nthreads); } else { // B exists copy (dst, B, dst.spec().format, roi, nthreads); } } return ok; } bool ImageBufAlgo::add (ImageBuf &dst, const ImageBuf &A, const float *b, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &A, IBAprep_CLAMP_MUTUAL_NCHANNELS | IBAprep_SUPPORT_DEEP)) return false; if (dst.deep()) { // While still serial, set up all the sample counts dst.deepdata()->set_all_samples (A.deepdata()->all_samples()); } bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "add", add_impl, dst.spec().format, A.spec().format, dst, A, b, roi, nthreads); return ok; } bool ImageBufAlgo::add (ImageBuf &dst, const ImageBuf &A, float b, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &A, IBAprep_CLAMP_MUTUAL_NCHANNELS)) return false; int nc = A.nchannels(); float *vals = ALLOCA (float, nc); for (int c = 0; c < nc; ++c) vals[c] = b; bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "add", add_impl, dst.spec().format, A.spec().format, dst, A, vals, roi, nthreads); return ok; } template static bool sub_impl (ImageBuf &R, const ImageBuf &A, const ImageBuf &B, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(sub_impl, OIIO::ref(R), OIIO::cref(A), OIIO::cref(B), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case ImageBuf::Iterator r (R, roi); ImageBuf::ConstIterator a (A, roi); ImageBuf::ConstIterator b (B, roi); for ( ; !r.done(); ++r, ++a, ++b) for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = a[c] - b[c]; return true; } bool ImageBufAlgo::sub (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &A, &B)) return false; ROI origroi = roi; roi.chend = std::min (roi.chend, std::min (A.nchannels(), B.nchannels())); bool ok; OIIO_DISPATCH_COMMON_TYPES3 (ok, "sub", sub_impl, dst.spec().format, A.spec().format, B.spec().format, dst, A, B, roi, nthreads); if (roi.chend < origroi.chend && A.nchannels() != B.nchannels()) { // Edge case: A and B differed in nchannels, we allocated dst to be // the bigger of them, but adjusted roi to be the lesser. Now handle // the channels that got left out because they were not common to // all the inputs. ASSERT (roi.chend <= dst.nchannels()); roi.chbegin = roi.chend; roi.chend = origroi.chend; if (A.nchannels() > B.nchannels()) { // A exists copy (dst, A, dst.spec().format, roi, nthreads); } else { // B exists sub (dst, dst, B, roi, nthreads); } } return ok; } bool ImageBufAlgo::sub (ImageBuf &dst, const ImageBuf &A, const float *b, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &A, IBAprep_CLAMP_MUTUAL_NCHANNELS | IBAprep_SUPPORT_DEEP)) return false; if (dst.deep()) { // While still serial, set up all the sample counts dst.deepdata()->set_all_samples (A.deepdata()->all_samples()); } int nc = A.nchannels(); float *vals = ALLOCA (float, nc); for (int c = 0; c < nc; ++c) vals[c] = -b[c]; bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "sub", add_impl, dst.spec().format, A.spec().format, dst, A, vals, roi, nthreads); return ok; } bool ImageBufAlgo::sub (ImageBuf &dst, const ImageBuf &A, float b, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &A, IBAprep_CLAMP_MUTUAL_NCHANNELS)) return false; int nc = A.nchannels(); float *vals = ALLOCA (float, nc); for (int c = 0; c < nc; ++c) vals[c] = -b; bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "sub", add_impl, dst.spec().format, A.spec().format, dst, A, vals, roi, nthreads); return ok; } template static bool absdiff_impl (ImageBuf &R, const ImageBuf &A, const ImageBuf &B, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(absdiff_impl, OIIO::ref(R), OIIO::cref(A), OIIO::cref(B), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case ImageBuf::Iterator r (R, roi); ImageBuf::ConstIterator a (A, roi); ImageBuf::ConstIterator b (B, roi); for ( ; !r.done(); ++r, ++a, ++b) for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = std::abs (a[c] - b[c]); return true; } template static bool absdiff_impl (ImageBuf &R, const ImageBuf &A, const float *b, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(absdiff_impl, OIIO::ref(R), OIIO::cref(A), b, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case ImageBuf::Iterator r (R, roi); ImageBuf::ConstIterator a (A, roi); for ( ; !r.done(); ++r, ++a) for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = std::abs (a[c] - b[c]); return true; } bool ImageBufAlgo::absdiff (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &A, &B)) return false; ROI origroi = roi; roi.chend = std::min (roi.chend, std::min (A.nchannels(), B.nchannels())); bool ok; OIIO_DISPATCH_COMMON_TYPES3 (ok, "absdiff", absdiff_impl, dst.spec().format, A.spec().format, B.spec().format, dst, A, B, roi, nthreads); if (roi.chend < origroi.chend && A.nchannels() != B.nchannels()) { // Edge case: A and B differed in nchannels, we allocated dst to be // the bigger of them, but adjusted roi to be the lesser. Now handle // the channels that got left out because they were not common to // all the inputs. ASSERT (roi.chend <= dst.nchannels()); roi.chbegin = roi.chend; roi.chend = origroi.chend; if (A.nchannels() > B.nchannels()) { // A exists abs (dst, A, roi, nthreads); } else { // B exists abs (dst, B, roi, nthreads); } } return ok; } bool ImageBufAlgo::absdiff (ImageBuf &dst, const ImageBuf &A, const float *b, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &A, IBAprep_CLAMP_MUTUAL_NCHANNELS)) return false; bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "absdiff", absdiff_impl, dst.spec().format, A.spec().format, dst, A, b, roi, nthreads); return ok; } bool ImageBufAlgo::absdiff (ImageBuf &dst, const ImageBuf &A, float b, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &A, IBAprep_CLAMP_MUTUAL_NCHANNELS)) return false; int nc = dst.nchannels(); float *vals = ALLOCA (float, nc); for (int c = 0; c < nc; ++c) vals[c] = b; bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "absdiff", absdiff_impl, dst.spec().format, A.spec().format, dst, A, vals, roi, nthreads); return ok; } bool ImageBufAlgo::abs (ImageBuf &dst, const ImageBuf &A, ROI roi, int nthreads) { // Define abs in terms of absdiff(A,0.0) return absdiff (dst, A, 0.0f, roi, nthreads); } template static bool mul_impl (ImageBuf &R, const ImageBuf &A, const ImageBuf &B, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(mul_impl, OIIO::ref(R), OIIO::cref(A), OIIO::cref(B), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case ImageBuf::Iterator r (R, roi); ImageBuf::ConstIterator a (A, roi); ImageBuf::ConstIterator b (B, roi); for ( ; !r.done(); ++r, ++a, ++b) for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = a[c] * b[c]; return true; } bool ImageBufAlgo::mul (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &A, &B, NULL, IBAprep_CLAMP_MUTUAL_NCHANNELS)) return false; bool ok; OIIO_DISPATCH_COMMON_TYPES3 (ok, "mul", mul_impl, dst.spec().format, A.spec().format, B.spec().format, dst, A, B, roi, nthreads); // N.B. No need to consider the case where A and B have differing number // of channels. Missing channels are assumed 0, multiplication by 0 is // 0, so it all just works through the magic of IBAprep. return ok; } template static bool mul_impl (ImageBuf &R, const ImageBuf &A, const float *b, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(mul_impl, OIIO::ref(R), OIIO::cref(A), b, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case: if (R.deep()) { // Deep case array_view channeltypes (R.deepdata()->all_channeltypes()); ImageBuf::Iterator r (R, roi); ImageBuf::ConstIterator a (A, roi); for ( ; !r.done(); ++r, ++a) { for (int samp = 0, samples = r.deep_samples(); samp < samples; ++samp) { for (int c = roi.chbegin; c < roi.chend; ++c) { if (channeltypes[c].basetype == TypeDesc::UINT32) r.set_deep_value (c, samp, a.deep_value_uint(c, samp)); else r.set_deep_value (c, samp, a.deep_value(c, samp) * b[c]); } } } return true; } ImageBuf::ConstIterator a (A, roi); for (ImageBuf::Iterator r (R, roi); !r.done(); ++r, ++a) for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = a[c] * b[c]; return true; } bool ImageBufAlgo::mul (ImageBuf &dst, const ImageBuf &A, const float *b, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &A, IBAprep_CLAMP_MUTUAL_NCHANNELS | IBAprep_SUPPORT_DEEP)) return false; if (dst.deep()) { // While still serial, set up all the sample counts dst.deepdata()->set_all_samples (A.deepdata()->all_samples()); } bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "mul", mul_impl, dst.spec().format, A.spec().format, dst, A, b, roi, nthreads); return ok; } bool ImageBufAlgo::mul (ImageBuf &dst, const ImageBuf &A, float b, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &A, IBAprep_CLAMP_MUTUAL_NCHANNELS)) return false; int nc = A.nchannels(); float *vals = ALLOCA (float, nc); for (int c = 0; c < nc; ++c) vals[c] = b; bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "mul", mul_impl, dst.spec().format, A.spec().format, dst, A, vals, roi, nthreads); return ok; } template static bool div_impl (ImageBuf &R, const ImageBuf &A, const ImageBuf &B, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(div_impl, OIIO::ref(R), OIIO::cref(A), OIIO::cref(B), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case ImageBuf::Iterator r (R, roi); ImageBuf::ConstIterator a (A, roi); ImageBuf::ConstIterator b (B, roi); for ( ; !r.done(); ++r, ++a, ++b) for (int c = roi.chbegin; c < roi.chend; ++c) { float v = b[c]; r[c] = (v == 0.0f) ? 0.0f : (a[c] / v); } return true; } bool ImageBufAlgo::div (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &A, &B, NULL, IBAprep_CLAMP_MUTUAL_NCHANNELS)) return false; bool ok; OIIO_DISPATCH_COMMON_TYPES3 (ok, "div", div_impl, dst.spec().format, A.spec().format, B.spec().format, dst, A, B, roi, nthreads); return ok; } bool ImageBufAlgo::div (ImageBuf &dst, const ImageBuf &A, const float *b, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &A, IBAprep_CLAMP_MUTUAL_NCHANNELS | IBAprep_SUPPORT_DEEP)) return false; if (dst.deep()) { // While still serial, set up all the sample counts dst.deepdata()->set_all_samples (A.deepdata()->all_samples()); } int nc = dst.nchannels(); float *binv = OIIO_ALLOCA (float, nc); for (int c = 0; c < nc; ++c) binv[c] = (b[c] == 0.0f) ? 0.0f : 1.0f/b[c]; bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "div", mul_impl, dst.spec().format, A.spec().format, dst, A, binv, roi, nthreads); return ok; } bool ImageBufAlgo::div (ImageBuf &dst, const ImageBuf &A, float b, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &A, IBAprep_CLAMP_MUTUAL_NCHANNELS)) return false; b = (b == 0.0f) ? 1.0f : 1.0f/b; int nc = dst.nchannels(); float *binv = ALLOCA (float, nc); for (int c = 0; c < nc; ++c) binv[c] = b; bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "div", mul_impl, dst.spec().format, A.spec().format, dst, A, binv, roi, nthreads); return ok; } template static bool mad_impl (ImageBuf &R, const ImageBuf &A, const ImageBuf &B, const ImageBuf &C, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(mad_impl, OIIO::ref(R), OIIO::cref(A), OIIO::cref(B), OIIO::cref(C), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case if ( (is_same::value || is_same::value) && (is_same::value || is_same::value) // && R.localpixels() // has to be, because it's writeable && A.localpixels() && B.localpixels() && C.localpixels() // && R.contains_roi(roi) // has to be, because IBAPrep && A.contains_roi(roi) && B.contains_roi(roi) && C.contains_roi(roi) && roi.chbegin == 0 && roi.chend == R.nchannels() && roi.chend == A.nchannels() && roi.chend == B.nchannels() && roi.chend == C.nchannels()) { // Special case when all inputs are either float or half, with in- // memory contiguous data and we're operating on the full channel // range: skip iterators: For these circumstances, we can operate on // the raw memory very efficiently. Otherwise, we will need the // magic of the the Iterators (and pay the price). int nxvalues = roi.width() * R.nchannels(); for (int z = roi.zbegin; z < roi.zend; ++z) for (int y = roi.ybegin; y < roi.yend; ++y) { Rtype *rraw = (Rtype *) R.pixeladdr (roi.xbegin, y, z); const ABCtype *araw = (const ABCtype *) A.pixeladdr (roi.xbegin, y, z); const ABCtype *braw = (const ABCtype *) B.pixeladdr (roi.xbegin, y, z); const ABCtype *craw = (const ABCtype *) C.pixeladdr (roi.xbegin, y, z); DASSERT (araw && braw && craw); // The straightforward loop auto-vectorizes very well, // there's no benefit to using explicit SIMD here. for (int x = 0; x < nxvalues; ++x) rraw[x] = araw[x] * braw[x] + craw[x]; // But if you did want to explicitly vectorize, this is // how it would look: // int simdend = nxvalues & (~3); // how many float4's? // for (int x = 0; x < simdend; x += 4) { // simd::float4 a_simd(araw+x), b_simd(braw+x), c_simd(craw+x); // simd::float4 r_simd = a_simd * b_simd + c_simd; // r_simd.store (rraw+x); // } // for (int x = simdend; x < nxvalues; ++x) // rraw[x] = araw[x] * braw[x] + craw[x]; } } else { ImageBuf::Iterator r (R, roi); ImageBuf::ConstIterator a (A, roi); ImageBuf::ConstIterator b (B, roi); ImageBuf::ConstIterator c (C, roi); for ( ; !r.done(); ++r, ++a, ++b, ++c) { for (int ch = roi.chbegin; ch < roi.chend; ++ch) r[ch] = a[ch] * b[ch] + c[ch]; } } return true; } template static bool mad_implf (ImageBuf &R, const ImageBuf &A, const float *b, const float *c, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(mad_implf, OIIO::ref(R), OIIO::cref(A), b, c, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case ImageBuf::Iterator r (R, roi); ImageBuf::ConstIterator a (A, roi); for ( ; !r.done(); ++r, ++a) for (int ch = roi.chbegin; ch < roi.chend; ++ch) r[ch] = a[ch] * b[ch] + c[ch]; return true; } bool ImageBufAlgo::mad (ImageBuf &dst, const ImageBuf &A_, const ImageBuf &B_, const ImageBuf &C_, ROI roi, int nthreads) { const ImageBuf *A = &A_, *B = &B_, *C = &C_; if (!A->initialized() || !B->initialized() || !C->initialized()) { dst.error ("Uninitialized input image"); return false; } // To avoid the full cross-product of dst/A/B/C types, force A,B,C to // all be the same data type, copying if we have to. TypeDesc abc_type = type_merge (A->spec().format, B->spec().format, C->spec().format); ImageBuf Anew, Bnew, Cnew; if (A->spec().format != abc_type) { Anew.copy (*A, abc_type); A = &Anew; } if (B->spec().format != abc_type) { Bnew.copy (*B, abc_type); B = &Bnew; } if (C->spec().format != abc_type) { Cnew.copy (*C, abc_type); C = &Cnew; } ASSERT (A->spec().format == B->spec().format && A->spec().format == C->spec().format); if (! IBAprep (roi, &dst, A, B, C)) return false; bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "mad", mad_impl, dst.spec().format, abc_type, dst, *A, *B, *C, roi, nthreads); return ok; } bool ImageBufAlgo::mad (ImageBuf &dst, const ImageBuf &A, const float *B, const float *C, ROI roi, int nthreads) { if (!A.initialized()) { dst.error ("Uninitialized input image"); return false; } if (! IBAprep (roi, &dst, &A)) return false; bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "mad", mad_implf, dst.spec().format, A.spec().format, dst, A, B, C, roi, nthreads); return ok; } bool ImageBufAlgo::mad (ImageBuf &dst, const ImageBuf &A, float b, float c, ROI roi, int nthreads) { if (!A.initialized()) { dst.error ("Uninitialized input image"); return false; } if (! IBAprep (roi, &dst, &A)) return false; std::vector B (roi.chend, b); std::vector C (roi.chend, c); bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "mad", mad_implf, dst.spec().format, A.spec().format, dst, A, &B[0], &C[0], roi, nthreads); return ok; } bool ImageBufAlgo::invert (ImageBuf &dst, const ImageBuf &A, ROI roi, int nthreads) { // Calculate invert as simply 1-A == A*(-1)+1 return mad (dst, A, -1.0, 1.0, roi, nthreads); } template static bool pow_impl (ImageBuf &R, const ImageBuf &A, const float *b, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(pow_impl, OIIO::ref(R), OIIO::cref(A), b, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } ImageBuf::ConstIterator a (A, roi); for (ImageBuf::Iterator r (R, roi); !r.done(); ++r, ++a) for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = pow (a[c], b[c]); return true; } bool ImageBufAlgo::pow (ImageBuf &dst, const ImageBuf &A, const float *b, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &A, IBAprep_CLAMP_MUTUAL_NCHANNELS)) return false; bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "pow", pow_impl, dst.spec().format, A.spec().format, dst, A, b, roi, nthreads); return ok; } bool ImageBufAlgo::pow (ImageBuf &dst, const ImageBuf &A, float b, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &A, IBAprep_CLAMP_MUTUAL_NCHANNELS)) return false; int nc = A.nchannels(); float *vals = ALLOCA (float, nc); for (int c = 0; c < nc; ++c) vals[c] = b; bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "pow", pow_impl, dst.spec().format, A.spec().format, dst, A, vals, roi, nthreads); return ok; } template static bool channel_sum_ (ImageBuf &dst, const ImageBuf &src, const float *weights, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(channel_sum_, OIIO::ref(dst), OIIO::cref(src), weights, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } ImageBuf::Iterator d (dst, roi); ImageBuf::ConstIterator s (src, roi); for ( ; !d.done(); ++d, ++s) { float sum = 0.0f; for (int c = roi.chbegin; c < roi.chend; ++c) sum += s[c] * weights[c]; d[0] = sum; } return true; } bool ImageBufAlgo::channel_sum (ImageBuf &dst, const ImageBuf &src, const float *weights, ROI roi, int nthreads) { if (! roi.defined()) roi = get_roi(src.spec()); roi.chend = std::min (roi.chend, src.nchannels()); ROI dstroi = roi; dstroi.chbegin = 0; dstroi.chend = 1; if (! IBAprep (dstroi, &dst)) return false; if (! weights) { float *local_weights = ALLOCA (float, roi.chend); for (int c = 0; c < roi.chend; ++c) local_weights[c] = 1.0f; weights = &local_weights[0]; } bool ok; OIIO_DISPATCH_TYPES2 (ok, "channel_sum", channel_sum_, dst.spec().format, src.spec().format, dst, src, weights, roi, nthreads); return ok; } inline float rangecompress (float x) { // Formula courtesy of Sony Pictures Imageworks #if 0 /* original coeffs -- identity transform for vals < 1 */ const float x1 = 1.0, a = 1.2607481479644775391; const float b = 0.28785100579261779785, c = -1.4042005538940429688; #else /* but received wisdom is that these work better */ const float x1 = 0.18, a = -0.54576885700225830078; const float b = 0.18351669609546661377, c = 284.3577880859375; #endif float absx = fabsf(x); if (absx <= x1) return x; return copysignf (a + b * logf(fabsf(c*absx + 1.0f)), x); } inline float rangeexpand (float y) { // Formula courtesy of Sony Pictures Imageworks #if 0 /* original coeffs -- identity transform for vals < 1 */ const float x1 = 1.0, a = 1.2607481479644775391; const float b = 0.28785100579261779785, c = -1.4042005538940429688; #else /* but received wisdom is that these work better */ const float x1 = 0.18, a = -0.54576885700225830078; const float b = 0.18351669609546661377, c = 284.3577880859375; #endif float absy = fabsf(y); if (absy <= x1) return y; float xIntermediate = expf ((absy - a)/b); // Since the compression step includes an absolute value, there are // two possible results here. If x < x1 it is the incorrect result, // so pick the other value. float x = (xIntermediate - 1.0f) / c; if (x < x1) x = (-xIntermediate - 1.0f) / c; return copysign (x, y); } template static bool rangecompress_ (ImageBuf &R, const ImageBuf &A, bool useluma, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(rangecompress_, OIIO::ref(R), OIIO::cref(A), useluma, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } const ImageSpec &Aspec (A.spec()); int alpha_channel = Aspec.alpha_channel; int z_channel = Aspec.z_channel; if (roi.nchannels() < 3 || (alpha_channel >= roi.chbegin && alpha_channel < roi.chbegin+3) || (z_channel >= roi.chbegin && z_channel < roi.chbegin+3)) { useluma = false; // No way to use luma } if (&R == &A) { // Special case: operate in-place for (ImageBuf::Iterator r (R, roi); !r.done(); ++r) { if (useluma) { float luma = 0.21264f * r[roi.chbegin] + 0.71517f * r[roi.chbegin+1] + 0.07219f * r[roi.chbegin+2]; float scale = luma > 0.0f ? rangecompress (luma) / luma : 0.0f; for (int c = roi.chbegin; c < roi.chend; ++c) { if (c == alpha_channel || c == z_channel) continue; r[c] = r[c] * scale; } } else { for (int c = roi.chbegin; c < roi.chend; ++c) { if (c == alpha_channel || c == z_channel) continue; r[c] = rangecompress (r[c]); } } } } else { ImageBuf::ConstIterator a (A, roi); for (ImageBuf::Iterator r (R, roi); !r.done(); ++r, ++a) { if (useluma) { float luma = 0.21264f * a[roi.chbegin] + 0.71517f * a[roi.chbegin+1] + 0.07219f * a[roi.chbegin+2]; float scale = luma > 0.0f ? rangecompress (luma) / luma : 0.0f; for (int c = roi.chbegin; c < roi.chend; ++c) { if (c == alpha_channel || c == z_channel) r[c] = a[c]; else r[c] = a[c] * scale; } } else { for (int c = roi.chbegin; c < roi.chend; ++c) { if (c == alpha_channel || c == z_channel) r[c] = a[c]; else r[c] = rangecompress (a[c]); } } } } return true; } template static bool rangeexpand_ (ImageBuf &R, const ImageBuf &A, bool useluma, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(rangeexpand_, OIIO::ref(R), OIIO::cref(A), useluma, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } const ImageSpec &Aspec (A.spec()); int alpha_channel = Aspec.alpha_channel; int z_channel = Aspec.z_channel; if (roi.nchannels() < 3 || (alpha_channel >= roi.chbegin && alpha_channel < roi.chbegin+3) || (z_channel >= roi.chbegin && z_channel < roi.chbegin+3)) { useluma = false; // No way to use luma } if (&R == &A) { // Special case: operate in-place for (ImageBuf::Iterator r (R, roi); !r.done(); ++r) { if (useluma) { float luma = 0.21264f * r[roi.chbegin] + 0.71517f * r[roi.chbegin+1] + 0.07219f * r[roi.chbegin+2]; float scale = luma > 0.0f ? rangeexpand (luma) / luma : 0.0f; for (int c = roi.chbegin; c < roi.chend; ++c) { if (c == alpha_channel || c == z_channel) continue; r[c] = r[c] * scale; } } else { for (int c = roi.chbegin; c < roi.chend; ++c) { if (c == alpha_channel || c == z_channel) continue; r[c] = rangeexpand (r[c]); } } } } else { ImageBuf::ConstIterator a (A, roi); for (ImageBuf::Iterator r (R, roi); !r.done(); ++r, ++a) { if (useluma) { float luma = 0.21264f * a[roi.chbegin] + 0.71517f * a[roi.chbegin+1] + 0.07219f * a[roi.chbegin+2]; float scale = luma > 0.0f ? rangeexpand (luma) / luma : 0.0f; for (int c = roi.chbegin; c < roi.chend; ++c) { if (c == alpha_channel || c == z_channel) r[c] = a[c]; else r[c] = a[c] * scale; } } else { for (int c = roi.chbegin; c < roi.chend; ++c) { if (c == alpha_channel || c == z_channel) r[c] = a[c]; else r[c] = rangeexpand (a[c]); } } } } return true; } bool ImageBufAlgo::rangecompress (ImageBuf &dst, const ImageBuf &src, bool useluma, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &src, IBAprep_CLAMP_MUTUAL_NCHANNELS)) return false; bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "rangecompress", rangecompress_, dst.spec().format, src.spec().format, dst, src, useluma, roi, nthreads); return ok; } bool ImageBufAlgo::rangeexpand (ImageBuf &dst, const ImageBuf &src, bool useluma, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &src, IBAprep_CLAMP_MUTUAL_NCHANNELS)) return false; bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "rangeexpand", rangeexpand_, dst.spec().format, src.spec().format, dst, src, useluma, roi, nthreads); return ok; } template static bool unpremult_ (ImageBuf &R, const ImageBuf &A, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(unpremult_, OIIO::ref(R), OIIO::cref(A), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } int alpha_channel = A.spec().alpha_channel; int z_channel = A.spec().z_channel; if (&R == &A) { for (ImageBuf::Iterator r (R, roi); !r.done(); ++r) { float alpha = r[alpha_channel]; if (alpha == 0.0f || alpha == 1.0f) continue; for (int c = roi.chbegin; c < roi.chend; ++c) if (c != alpha_channel && c != z_channel) r[c] = r[c] / alpha; } } else { ImageBuf::ConstIterator a (A, roi); for (ImageBuf::Iterator r (R, roi); !r.done(); ++r, ++a) { float alpha = a[alpha_channel]; if (alpha == 0.0f || alpha == 1.0f) { for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = a[c]; continue; } for (int c = roi.chbegin; c < roi.chend; ++c) if (c != alpha_channel && c != z_channel) r[c] = a[c] / alpha; else r[c] = a[c]; } } return true; } bool ImageBufAlgo::unpremult (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &src, IBAprep_CLAMP_MUTUAL_NCHANNELS)) return false; if (src.spec().alpha_channel < 0) { if (&dst != &src) return paste (dst, src.spec().x, src.spec().y, src.spec().z, roi.chbegin, src, roi, nthreads); return true; } bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "unpremult", unpremult_, dst.spec().format, src.spec().format, dst, src, roi, nthreads); return ok; } template static bool premult_ (ImageBuf &R, const ImageBuf &A, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(premult_, OIIO::ref(R), OIIO::cref(A), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } int alpha_channel = A.spec().alpha_channel; int z_channel = A.spec().z_channel; if (&R == &A) { for (ImageBuf::Iterator r (R, roi); !r.done(); ++r) { float alpha = r[alpha_channel]; if (alpha == 1.0f) continue; for (int c = roi.chbegin; c < roi.chend; ++c) if (c != alpha_channel && c != z_channel) r[c] = r[c] * alpha; } } else { ImageBuf::ConstIterator a (A, roi); for (ImageBuf::Iterator r (R, roi); !r.done(); ++r, ++a) { float alpha = a[alpha_channel]; for (int c = roi.chbegin; c < roi.chend; ++c) if (c != alpha_channel && c != z_channel) r[c] = a[c] * alpha; else r[c] = a[c]; } } return true; } bool ImageBufAlgo::premult (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &src, IBAprep_CLAMP_MUTUAL_NCHANNELS)) return false; if (src.spec().alpha_channel < 0) { if (&dst != &src) return paste (dst, src.spec().x, src.spec().y, src.spec().z, roi.chbegin, src, roi, nthreads); return true; } bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "premult", premult_, dst.spec().format, src.spec().format, dst, src, roi, nthreads); return ok; } namespace { // Make sure isfinite is defined for 'half' inline bool isfinite (half h) { return h.isFinite(); } template bool fixNonFinite_ (ImageBuf &dst, ImageBufAlgo::NonFiniteFixMode mode, int *pixelsFixed, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(fixNonFinite_, OIIO::ref(dst), mode, pixelsFixed, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case ROI dstroi = get_roi (dst.spec()); int count = 0; // Number of pixels with nonfinite values if (mode == ImageBufAlgo::NONFINITE_NONE || mode == ImageBufAlgo::NONFINITE_ERROR) { // Just count the number of pixels with non-finite values for (ImageBuf::Iterator pixel (dst, roi); ! pixel.done(); ++pixel) { for (int c = roi.chbegin; c < roi.chend; ++c) { T value = pixel[c]; if (! isfinite(value)) { ++count; break; // only count one per pixel } } } } else if (mode == ImageBufAlgo::NONFINITE_BLACK) { // Replace non-finite pixels with black for (ImageBuf::Iterator pixel (dst, roi); ! pixel.done(); ++pixel) { bool fixed = false; for (int c = roi.chbegin; c < roi.chend; ++c) { T value = pixel[c]; if (! isfinite(value)) { pixel[c] = T(0.0); fixed = true; } } if (fixed) ++count; } } else if (mode == ImageBufAlgo::NONFINITE_BOX3) { // Replace non-finite pixels with a simple 3x3 window average // (the average excluding non-finite pixels, of course) for (ImageBuf::Iterator pixel (dst, roi); ! pixel.done(); ++pixel) { bool fixed = false; for (int c = roi.chbegin; c < roi.chend; ++c) { T value = pixel[c]; if (! isfinite (value)) { int numvals = 0; T sum (0.0); ROI roi2 (pixel.x()-1, pixel.x()+2, pixel.y()-1, pixel.y()+2, pixel.z()-1, pixel.z()+2); roi2 = roi_intersection (roi2, dstroi); for (ImageBuf::Iterator i(dst,roi2); !i.done(); ++i) { T v = i[c]; if (isfinite (v)) { sum += v; ++numvals; } } pixel[c] = numvals ? T(sum / numvals) : T(0.0); fixed = true; } } if (fixed) ++count; } } if (pixelsFixed) { // Update pixelsFixed atomically -- that's what makes this whole // function thread-safe. *(atomic_int *)pixelsFixed += count; } return true; } bool fixNonFinite_deep_ (ImageBuf &dst, ImageBufAlgo::NonFiniteFixMode mode, int *pixelsFixed, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(fixNonFinite_deep_, OIIO::ref(dst), mode, pixelsFixed, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case int count = 0; // Number of pixels with nonfinite values if (mode == ImageBufAlgo::NONFINITE_NONE || mode == ImageBufAlgo::NONFINITE_ERROR) { // Just count the number of pixels with non-finite values for (ImageBuf::Iterator pixel (dst, roi); ! pixel.done(); ++pixel) { int samples = pixel.deep_samples (); if (samples == 0) continue; bool bad = false; for (int samp = 0; samp < samples && !bad; ++samp) for (int c = roi.chbegin; c < roi.chend; ++c) { float value = pixel.deep_value (c, samp); if (! isfinite(value)) { ++count; bad = true; break; } } } } else { // We don't know what to do with BOX3, so just always set to black. // Replace non-finite pixels with black for (ImageBuf::Iterator pixel (dst, roi); ! pixel.done(); ++pixel) { int samples = pixel.deep_samples (); if (samples == 0) continue; bool fixed = false; for (int samp = 0; samp < samples; ++samp) for (int c = roi.chbegin; c < roi.chend; ++c) { float value = pixel.deep_value (c, samp); if (! isfinite(value)) { pixel.set_deep_value (c, samp, 0.0f); fixed = true; } } if (fixed) ++count; } } if (pixelsFixed) { // Update pixelsFixed atomically -- that's what makes this whole // function thread-safe. *(atomic_int *)pixelsFixed += count; } return true; } } // anon namespace /// Fix all non-finite pixels (nan/inf) using the specified approach bool ImageBufAlgo::fixNonFinite (ImageBuf &dst, const ImageBuf &src, NonFiniteFixMode mode, int *pixelsFixed, ROI roi, int nthreads) { if (mode != ImageBufAlgo::NONFINITE_NONE && mode != ImageBufAlgo::NONFINITE_BLACK && mode != ImageBufAlgo::NONFINITE_BOX3 && mode != ImageBufAlgo::NONFINITE_ERROR) { // Something went wrong dst.error ("fixNonFinite: unknown repair mode"); return false; } if (! IBAprep (roi, &dst, &src, IBAprep_SUPPORT_DEEP)) return false; // Initialize bool ok = true; int pixelsFixed_local; if (! pixelsFixed) pixelsFixed = &pixelsFixed_local; *pixelsFixed = 0; // Start by copying dst to src, if they aren't the same image if (&dst != &src) ok &= ImageBufAlgo::copy (dst, src, TypeDesc::UNKNOWN, roi, nthreads); if (dst.deep()) ok &= fixNonFinite_deep_ (dst, mode, pixelsFixed, roi, nthreads); else if (src.spec().format.basetype == TypeDesc::FLOAT) ok &= fixNonFinite_ (dst, mode, pixelsFixed, roi, nthreads); else if (src.spec().format.basetype == TypeDesc::HALF) ok &= fixNonFinite_ (dst, mode, pixelsFixed, roi, nthreads); else if (src.spec().format.basetype == TypeDesc::DOUBLE) ok &= fixNonFinite_ (dst, mode, pixelsFixed, roi, nthreads); // All other format types aren't capable of having nonfinite // pixel values, so the copy was enough. if (mode == ImageBufAlgo::NONFINITE_ERROR && *pixelsFixed) { dst.error ("Nonfinite pixel values found"); ok = false; } return ok; } static bool decode_over_channels (const ImageBuf &R, int &nchannels, int &alpha, int &z, int &colors) { if (! R.initialized()) { alpha = -1; z = -1; colors = 0; return false; } const ImageSpec &spec (R.spec()); alpha = spec.alpha_channel; bool has_alpha = (alpha >= 0); z = spec.z_channel; bool has_z = (z >= 0); nchannels = spec.nchannels; colors = nchannels - has_alpha - has_z; if (! has_alpha && colors == 4) { // No marked alpha channel, but suspiciously 4 channel -- assume // it's RGBA. has_alpha = true; colors -= 1; // Assume alpha is the highest channel that's not z alpha = nchannels - 1; if (alpha == z) --alpha; } return true; } // Fully type-specialized version of over. template static bool over_impl (ImageBuf &R, const ImageBuf &A, const ImageBuf &B, bool zcomp, bool z_zeroisinf, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(over_impl, OIIO::ref(R), OIIO::cref(A), OIIO::cref(B), zcomp, z_zeroisinf, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case... // It's already guaranteed that R, A, and B have matching channel // ordering, and have an alpha channel. So just decode one. int nchannels = 0, alpha_channel = 0, z_channel = 0, ncolor_channels = 0; decode_over_channels (R, nchannels, alpha_channel, z_channel, ncolor_channels); bool has_z = (z_channel >= 0); ImageBuf::ConstIterator a (A, roi); ImageBuf::ConstIterator b (B, roi); ImageBuf::Iterator r (R, roi); for ( ; ! r.done(); ++r, ++a, ++b) { float az = 0.0f, bz = 0.0f; bool a_is_closer = true; // will remain true if !zcomp if (zcomp && has_z) { az = a[z_channel]; bz = b[z_channel]; if (z_zeroisinf) { if (az == 0.0f) az = std::numeric_limits::max(); if (bz == 0.0f) bz = std::numeric_limits::max(); } a_is_closer = (az <= bz); } if (a_is_closer) { // A over B float alpha = clamp (a[alpha_channel], 0.0f, 1.0f); float one_minus_alpha = 1.0f - alpha; for (int c = roi.chbegin; c < roi.chend; c++) r[c] = a[c] + one_minus_alpha * b[c]; if (has_z) r[z_channel] = (alpha != 0.0) ? a[z_channel] : b[z_channel]; } else { // B over A -- because we're doing a Z composite float alpha = clamp (b[alpha_channel], 0.0f, 1.0f); float one_minus_alpha = 1.0f - alpha; for (int c = roi.chbegin; c < roi.chend; c++) r[c] = b[c] + one_minus_alpha * a[c]; r[z_channel] = (alpha != 0.0) ? b[z_channel] : a[z_channel]; } } return true; } bool ImageBufAlgo::over (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &A, &B, NULL, IBAprep_REQUIRE_ALPHA | IBAprep_REQUIRE_SAME_NCHANNELS)) return false; bool ok; OIIO_DISPATCH_COMMON_TYPES3 (ok, "over", over_impl, dst.spec().format, A.spec().format, B.spec().format, dst, A, B, false, false, roi, nthreads); return ok && ! dst.has_error(); } bool ImageBufAlgo::zover (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, bool z_zeroisinf, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &A, &B, NULL, IBAprep_REQUIRE_ALPHA | IBAprep_REQUIRE_Z | IBAprep_REQUIRE_SAME_NCHANNELS)) return false; bool ok; OIIO_DISPATCH_COMMON_TYPES3 (ok, "zover", over_impl, dst.spec().format, A.spec().format, B.spec().format, dst, A, B, true, z_zeroisinf, roi, nthreads); return ok && ! dst.has_error(); } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/imagebufalgo_deep.cpp0000644000175000017500000003113213151711064024022 0ustar mfvmfv/* Copyright 2013 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/imagebufalgo_util.h" #include "OpenImageIO/deepdata.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/thread.h" OIIO_NAMESPACE_BEGIN // Helper for flatten: identify channels in the spec that are important to // deciphering deep images. Return true if appropriate alphas were found. static bool find_deep_channels (const ImageSpec &spec, int &alpha_channel, int &AR_channel, int &AG_channel, int &AB_channel, int &R_channel, int &G_channel, int &B_channel, int &Z_channel, int &Zback_channel) { static const char *names[] = { "A", "RA", "GA", "BA", // old OpenEXR recommendation "AR", "AG", "AB", // new OpenEXR recommendation "R", "G", "B", "Z", "Zback", NULL }; int *chans[] = { &alpha_channel, &AR_channel, &AG_channel, &AB_channel, &AR_channel, &AG_channel, &AB_channel, &R_channel, &G_channel, &B_channel, &Z_channel, &Zback_channel }; for (int i = 0; names[i]; ++i) *chans[i] = -1; for (int c = 0, e = int(spec.channelnames.size()); c < e; ++c) { for (int i = 0; names[i]; ++i) { if (spec.channelnames[c] == names[i]) { *chans[i] = c; break; } } } if (Zback_channel < 0) Zback_channel = Z_channel; return (alpha_channel >= 0 || (AR_channel >= 0 && AG_channel >= 0 && AB_channel >= 0)); } // FIXME -- NOT CORRECT! This code assumes sorted, non-overlapping samples. // That is not a valid assumption in general. We will come back to fix this. template static bool flatten_ (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(flatten_, OIIO::ref(dst), OIIO::cref(src), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } const ImageSpec &srcspec (src.spec()); int nc = srcspec.nchannels; int alpha_channel, AR_channel, AG_channel, AB_channel; int R_channel, G_channel, B_channel; int Z_channel, Zback_channel; if (! find_deep_channels (srcspec, alpha_channel, AR_channel, AG_channel, AB_channel, R_channel, G_channel, B_channel, Z_channel, Zback_channel)) { dst.error ("No alpha channel could be identified"); return false; } ASSERT (alpha_channel >= 0 || (AR_channel >= 0 && AG_channel >= 0 && AB_channel >= 0)); float *val = ALLOCA (float, nc); float &ARval (AR_channel >= 0 ? val[AR_channel] : val[alpha_channel]); float &AGval (AG_channel >= 0 ? val[AG_channel] : val[alpha_channel]); float &ABval (AB_channel >= 0 ? val[AB_channel] : val[alpha_channel]); for (ImageBuf::Iterator r (dst, roi); !r.done(); ++r) { int x = r.x(), y = r.y(), z = r.z(); int samps = src.deep_samples (x, y, z); // Clear accumulated values for this pixel (0 for colors, big for Z) memset (val, 0, nc*sizeof(float)); if (Z_channel >= 0 && samps == 0) val[Z_channel] = 1.0e30; if (Zback_channel >= 0 && samps == 0) val[Zback_channel] = 1.0e30; for (int s = 0; s < samps; ++s) { float AR = ARval, AG = AGval, AB = ABval; // make copies float alpha = (AR + AG + AB) / 3.0f; if (alpha >= 1.0f) break; for (int c = 0; c < nc; ++c) { float v = src.deep_value (x, y, z, c, s); if (c == Z_channel || c == Zback_channel) val[c] *= alpha; // because Z are not premultiplied float a; if (c == R_channel) a = AR; else if (c == G_channel) a = AG; else if (c == B_channel) a = AB; else a = alpha; val[c] += (1.0f - a) * v; } } for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = val[c]; } return true; } bool ImageBufAlgo::flatten (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (! src.deep()) { // For some reason, we were asked to flatten an already-flat image. // So just copy it. return dst.copy (src); } // Construct an ideal spec for dst, which is like src but not deep. ImageSpec force_spec = src.spec(); force_spec.deep = false; force_spec.channelformats.clear(); if (! IBAprep (roi, &dst, &src, NULL, &force_spec, IBAprep_SUPPORT_DEEP | IBAprep_DEEP_MIXED)) return false; if (dst.spec().deep) { dst.error ("Cannot flatten to a deep image"); return false; } const ImageSpec &srcspec (src.spec()); int alpha_channel, AR_channel, AG_channel, AB_channel; int R_channel, G_channel, B_channel, Z_channel, Zback_channel; if (! find_deep_channels (srcspec, alpha_channel, AR_channel, AG_channel, AB_channel, R_channel, G_channel, B_channel, Z_channel, Zback_channel)) { dst.error ("No alpha channel could be identified"); return false; } bool ok; OIIO_DISPATCH_TYPES (ok, "flatten", flatten_, dst.spec().format, dst, src, roi, nthreads); return ok; } bool ImageBufAlgo::deepen (ImageBuf &dst, const ImageBuf &src, float zvalue, ROI roi, int nthreads) { if (src.deep()) { // For some reason, we were asked to deepen an already-deep image. // So just copy it. return dst.copy (src); // FIXME: once paste works for deep files, this should really be // return paste (dst, roi.xbegin, roi.ybegin, roi.zbegin, roi.chbegin, // src, roi, nthreads); } // Construct an ideal spec for dst, which is like src but deep. const ImageSpec &srcspec (src.spec()); int nc = srcspec.nchannels; int zback_channel = -1; ImageSpec force_spec = srcspec; force_spec.deep = true; force_spec.set_format (TypeDesc::FLOAT); force_spec.channelformats.clear(); for (int c = 0; c < nc; ++c) { if (force_spec.channelnames[c] == "Z") force_spec.z_channel = c; else if (force_spec.channelnames[c] == "Zback") zback_channel = c; } bool add_z_channel = (force_spec.z_channel < 0); if (add_z_channel) { // No z channel? Make one. force_spec.z_channel = force_spec.nchannels++; force_spec.channelnames.push_back ("Z"); } if (! IBAprep (roi, &dst, &src, NULL, &force_spec, IBAprep_SUPPORT_DEEP | IBAprep_DEEP_MIXED)) return false; if (! dst.deep()) { dst.error ("Cannot deepen to a flat image"); return false; } float *pixel = OIIO_ALLOCA (float, nc); // First, figure out which pixels get a sample and which do not for (int z = roi.zbegin; z < roi.zend; ++z) for (int y = roi.ybegin; y < roi.yend; ++y) for (int x = roi.xbegin; x < roi.xend; ++x) { bool has_sample = false; src.getpixel (x, y, z, pixel); for (int c = 0; c < nc; ++c) if (c != force_spec.z_channel && c != zback_channel && pixel[c] != 0.0f) { has_sample = true; break; } if (! has_sample && ! add_z_channel) for (int c = 0; c < nc; ++c) if ((c == force_spec.z_channel || c == zback_channel) && (pixel[c] != 0.0f && pixel[c] < 1e30)) { has_sample = true; break; } if (has_sample) dst.set_deep_samples (x, y, z, 1); } // Now actually set the values for (int z = roi.zbegin; z < roi.zend; ++z) for (int y = roi.ybegin; y < roi.yend; ++y) for (int x = roi.xbegin; x < roi.xend; ++x) { if (dst.deep_samples (x, y, z) == 0) continue; for (int c = 0; c < nc; ++c) dst.set_deep_value (x, y, z, c, 0 /*sample*/, src.getchannel (x, y, z, c)); if (add_z_channel) dst.set_deep_value (x, y, z, nc, 0, zvalue); } bool ok = true; // FIXME -- the above doesn't split into threads. Someday, it should // be refactored like this: // OIIO_DISPATCH_COMMON_TYPES2 (ok, "deepen", deepen_, // dst.spec().format, srcspec.format, // dst, src, add_z_channel, z, roi, nthreads); return ok; } bool ImageBufAlgo::deep_merge (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, bool occlusion_cull, ROI roi, int nthreads) { if (! A.deep() || ! B.deep()) { // For some reason, we were asked to merge a flat image. dst.error ("deep_merge can only be performed on deep images"); return false; } if (! IBAprep (roi, &dst, &A, &B, NULL, IBAprep_SUPPORT_DEEP | IBAprep_REQUIRE_SAME_NCHANNELS)) return false; if (! dst.deep()) { dst.error ("Cannot deep_merge to a flat image"); return false; } // First, set the capacity of the dst image to reserve enough space for // the segments of both source images. It may be that more insertions // are needed, due to overlaps, but those will be compartively fewer // than doing reallocations for every single sample. DeepData &dstdd (*dst.deepdata()); const DeepData &Add (*A.deepdata()); const DeepData &Bdd (*B.deepdata()); for (int z = roi.zbegin; z < roi.zend; ++z) for (int y = roi.ybegin; y < roi.yend; ++y) for (int x = roi.xbegin; x < roi.xend; ++x) { int dstpixel = dst.pixelindex (x, y, z, true); int Apixel = A.pixelindex (x, y, z, true); int Bpixel = B.pixelindex (x, y, z, true); dstdd.set_capacity (dstpixel, Add.capacity(Apixel) + Bdd.capacity(Bpixel)); } bool ok = ImageBufAlgo::copy (dst, A, TypeDesc::UNKNOWN, roi, nthreads); for (int z = roi.zbegin; z < roi.zend; ++z) for (int y = roi.ybegin; y < roi.yend; ++y) for (int x = roi.xbegin; x < roi.xend; ++x) { int dstpixel = dst.pixelindex (x, y, z, true); int Bpixel = B.pixelindex (x, y, z, true); DASSERT (dstpixel >= 0); dstdd.merge_deep_pixels (dstpixel, Bdd, Bpixel); if (occlusion_cull) dstdd.occlusion_cull (dstpixel); } return ok; } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/iptc.cpp0000644000175000017500000002204313151711064021343 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include "OpenImageIO/imageio.h" #define DEBUG_IPTC_READ 0 #define DEBUG_IPTC_WRITE 0 OIIO_NAMESPACE_BEGIN namespace { struct IIMtag { int tag; // IIM code const char *name; // Attribute name we use const char *anothername; // Optional second name bool repeatable; // May repeat }; static IIMtag iimtag [] = { { 3, "IPTC:ObjectTypeReference", NULL, false }, { 4, "IPTC:ObjectAttributeReference", NULL, true }, { 5, "IPTC:ObjectName", NULL, false }, { 7, "IPTC:EditStatus", NULL, false }, { 10, "IPTC:Urgency", NULL, false }, // deprecated by IPTC { 12, "IPTC:SubjectReference", NULL, true }, { 15, "IPTC:Category", NULL, false }, { 20, "IPTC:SupplementalCategories", NULL, true }, // deprecated by IPTC { 22, "IPTC:FixtureIdentifier", NULL, false }, { 25, "Keywords", NULL, true }, { 26, "IPTC:ContentLocationCode", NULL, true }, { 27, "IPTC:ContentLocationName", NULL, true }, { 30, "IPTC:ReleaseDate", NULL, false }, { 35, "IPTC:ReleaseTime", NULL, false }, { 37, "IPTC:ExpirationDate", NULL, false }, { 38, "IPTC:ExpirationTime", NULL, false }, { 40, "IPTC:Instructions", NULL, false }, { 45, "IPTC:ReferenceService", NULL, true }, { 47, "IPTC:ReferenceDate", NULL, false }, { 50, "IPTC:ReferenceNumber", NULL, true }, { 55, "IPTC:DateCreated", NULL, false }, { 60, "IPTC:TimeCreated", NULL, false }, { 62, "IPTC:DigitalCreationDate", NULL, false }, { 63, "IPTC:DigitalCreationTime", NULL, false }, { 65, "IPTC:OriginatingProgram", "Software", false }, { 70, "IPTC:ProgramVersion", NULL, false }, { 80, "IPTC:Creator", "Artist", true }, // sometimes called "byline" { 85, "IPTC:AuthorsPosition", NULL, true }, // sometimes "byline title" { 90, "IPTC:City", NULL, false }, { 92, "IPTC:Sublocation", NULL, false }, { 95, "IPTC:State", NULL, false }, // sometimes "Province/State" { 100, "IPTC:CountryCode", NULL, false }, { 101, "IPTC:Country", NULL, false }, { 103, "IPTC:TransmissionReference", NULL, false }, { 105, "IPTC:Headline", NULL, false }, { 110, "IPTC:Provider", NULL, false }, // aka Credit { 115, "IPTC:Source", NULL, false }, { 116, "IPTC:CopyrightNotice", "Copyright", false }, { 118, "IPTC:Contact", NULL, false }, { 120, "IPTC:Caption", "ImageDescription", false}, { 121, "IPTC:LocalCaption", NULL, false}, { 122, "IPTC:CaptionWriter", NULL, false }, // aka Writer/Editor // Note: 150-154 is audio sampling stuff { 184, "IPTC:JobID", NULL, false }, { 185, "IPTC:MasterDocumentID", NULL, false }, { 186, "IPTC:ShortDocumentID", NULL, false }, { 187, "IPTC:UniqueDocumentID", NULL, false }, { 188, "IPTC:OwnerID", NULL, false }, { 221, "IPTC:Prefs", NULL, false }, { 225, "IPTC:ClassifyState", NULL, false }, { 228, "IPTC:SimilarityIndex", NULL, false }, { 230, "IPTC:DocumentNotes", NULL, false }, { 231, "IPTC:DocumentHistory", NULL, false }, { -1, NULL, NULL, false } }; // N.B. All "Date" fields are 8 digit strings: CCYYMMDD // All "Time" fields are 11 digit strings (what format?) } // anonymous namespace bool decode_iptc_iim (const void *iptc, int length, ImageSpec &spec) { const unsigned char *buf = (const unsigned char *) iptc; #if DEBUG_IPTC_READ std::cerr << "IPTC dump:\n"; for (int i = 0; i < 100; ++i) { if (buf[i] >= ' ') std::cerr << (char)buf[i] << ' '; std::cerr << "(" << (int)(unsigned char)buf[i] << ") "; } std::cerr << "\n"; #endif // Now there are a series of data blocks. Each one starts with 1C // 02, then a single byte indicating the tag type, then 2 byte (big // endian) giving the tag length, then the data itself. This // repeats until we've used up the whole segment buffer, or I guess // until we don't find another 1C 02 tag start. // N.B. I don't know why, but Picasa sometimes uses 1C 01 ! while (length > 0 && buf[0] == 0x1c && (buf[1] == 0x02 || buf[1] == 0x01)) { int secondbyte = buf[1]; int tagtype = buf[2]; int tagsize = (buf[3] << 8) + buf[4]; buf += 5; length -= 5; #if DEBUG_IPTC_READ std::cerr << "iptc tag " << tagtype << ":\n"; for (int i = 0; i < tagsize; ++i) { if (buf[i] >= ' ') std::cerr << (char)buf[i] << ' '; std::cerr << "(" << (int)(unsigned char)buf[i] << ") "; } std::cerr << "\n"; #endif if (secondbyte == 0x02) { std::string s ((const char *)buf, tagsize); for (int i = 0; iimtag[i].name; ++i) { if (tagtype == iimtag[i].tag) { if (iimtag[i].repeatable) { // For repeatable IIM tags, concatenate them // together separated by semicolons s = Strutil::strip (s); std::string old = spec.get_string_attribute (iimtag[i].name); if (old.size()) old += "; "; spec.attribute (iimtag[i].name, old+s); } else { spec.attribute (iimtag[i].name, s); } if (iimtag[i].anothername) spec.attribute (iimtag[i].anothername, s); break; } } } buf += tagsize; length -= tagsize; } return true; } static void encode_iptc_iim_one_tag (int tag, const char *name, TypeDesc type, const void *data, std::vector &iptc) { if (type == TypeDesc::STRING) { iptc.push_back ((char)0x1c); iptc.push_back ((char)0x02); iptc.push_back ((char)tag); const char *str = ((const char **)data)[0]; int tagsize = strlen(str); tagsize = std::min (tagsize, 0xffff - 1); // Prevent 16 bit overflow iptc.push_back ((char)(tagsize >> 8)); iptc.push_back ((char)(tagsize & 0xff)); iptc.insert (iptc.end(), str, str+tagsize); } } void encode_iptc_iim (const ImageSpec &spec, std::vector &iptc) { iptc.clear (); const ImageIOParameter *p; for (int i = 0; iimtag[i].name; ++i) { if ((p = spec.find_attribute (iimtag[i].name))) { if (iimtag[i].repeatable) { std::string allvals (*(const char **)p->data()); std::vector tokens; Strutil::split (allvals, tokens, ";"); for (size_t t = 0, e = tokens.size(); t < e; ++t) { tokens[t] = Strutil::strip (tokens[t]); if (tokens[t].size()) { const char *tok = &tokens[t][0]; encode_iptc_iim_one_tag (iimtag[i].tag, iimtag[i].name, p->type(), &tok, iptc); } } } else { // Regular, non-repeating encode_iptc_iim_one_tag (iimtag[i].tag, iimtag[i].name, p->type(), p->data(), iptc); } } if (iimtag[i].anothername) { if ((p = spec.find_attribute (iimtag[i].anothername))) encode_iptc_iim_one_tag (iimtag[i].tag, iimtag[i].anothername, p->type(), p->data(), iptc); } } } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/maketexture.cpp0000644000175000017500000017651113151711064022754 0ustar mfvmfv/* Copyright 2013 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include #include #include #include #include "OpenImageIO/argparse.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/sysutil.h" #include "OpenImageIO/timer.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/imagebufalgo_util.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/filter.h" #include "OpenImageIO/refcnt.h" OIIO_NAMESPACE_USING static spin_mutex maketx_mutex; // for anything that needs locking static Filter2D * setup_filter (const ImageSpec &dstspec, const ImageSpec &srcspec, std::string filtername = std::string()) { // Resize ratio float wratio = float(dstspec.full_width) / float(srcspec.full_width); float hratio = float(dstspec.full_height) / float(srcspec.full_height); float w = std::max (1.0f, wratio); float h = std::max (1.0f, hratio); // Default filter, if none supplied if (filtername.empty()) { // No filter name supplied -- pick a good default if (wratio > 1.0f || hratio > 1.0f) filtername = "blackman-harris"; else filtername = "lanczos3"; } // Figure out the recommended filter width for the named filter for (int i = 0, e = Filter2D::num_filters(); i < e; ++i) { FilterDesc d; Filter2D::get_filterdesc (i, &d); if (filtername == d.name) return Filter2D::create (filtername, w*d.width, h*d.width); } return NULL; // couldn't find a matching name } static TypeDesc set_prman_options(TypeDesc out_dataformat, ImageSpec &configspec) { // Force separate planar image handling, and also emit prman metadata configspec.attribute ("planarconfig", "separate"); configspec.attribute ("maketx:prman_metadata", 1); // 8-bit : 64x64 if (out_dataformat == TypeDesc::UINT8 || out_dataformat == TypeDesc::INT8) { configspec.tile_width = 64; configspec.tile_height = 64; } // 16-bit : 64x32 // Force u16 -> s16 // In prman's txmake (last tested in 15.0) // specifying -short creates a signed int representation if (out_dataformat == TypeDesc::UINT16) out_dataformat = TypeDesc::INT16; if (out_dataformat == TypeDesc::INT16) { configspec.tile_width = 64; configspec.tile_height = 32; } // Float: 32x32 // In prman's txmake (last tested in 15.0) // specifying -half or -float make 32x32 tile size if (out_dataformat == TypeDesc::DOUBLE) out_dataformat = TypeDesc::FLOAT; if (out_dataformat == TypeDesc::HALF || out_dataformat == TypeDesc::FLOAT) { configspec.tile_width = 32; configspec.tile_height = 32; } return out_dataformat; } static TypeDesc set_oiio_options(TypeDesc out_dataformat, ImageSpec &configspec) { // Interleaved channels are faster to read configspec.attribute ("planarconfig", "contig"); // Force fixed tile-size across the board configspec.tile_width = 64; configspec.tile_height = 64; return out_dataformat; } static std::string datestring (time_t t) { struct tm mytm; Sysutil::get_local_time (&t, &mytm); return Strutil::format ("%4d:%02d:%02d %2d:%02d:%02d", mytm.tm_year+1900, mytm.tm_mon+1, mytm.tm_mday, mytm.tm_hour, mytm.tm_min, mytm.tm_sec); } template static void interppixel_NDC_clamped (const ImageBuf &buf, float x, float y, float *pixel, bool envlatlmode) { int fx = buf.spec().full_x; int fy = buf.spec().full_y; int fw = buf.spec().full_width; int fh = buf.spec().full_height; x = static_cast(fx) + x * static_cast(fw); y = static_cast(fy) + y * static_cast(fh); int n = buf.spec().nchannels; float *p0 = ALLOCA(float, 4*n), *p1 = p0+n, *p2 = p1+n, *p3 = p2+n; x -= 0.5f; y -= 0.5f; int xtexel, ytexel; float xfrac, yfrac; xfrac = floorfrac (x, &xtexel); yfrac = floorfrac (y, &ytexel); // Get the four texels ImageBuf::ConstIterator it (buf, ROI(xtexel, xtexel+2, ytexel, ytexel+2), ImageBuf::WrapClamp); for (int c = 0; c < n; ++c) p0[c] = it[c]; ++it; for (int c = 0; c < n; ++c) p1[c] = it[c]; ++it; for (int c = 0; c < n; ++c) p2[c] = it[c]; ++it; for (int c = 0; c < n; ++c) p3[c] = it[c]; if (envlatlmode) { // For latlong environment maps, in order to conserve energy, we // must weight the pixels by sin(t*PI) because pixels closer to // the pole are actually less area on the sphere. Doing this // wrong will tend to over-represent the high latitudes in // low-res MIP levels. We fold the area weighting into our // linear interpolation by adjusting yfrac. int ynext = Imath::clamp (ytexel+1, buf.ymin(), buf.ymax()); ytexel = Imath::clamp (ytexel, buf.ymin(), buf.ymax()); float w0 = (1.0f - yfrac) * sinf ((float)M_PI * (ytexel+0.5f)/(float)fh); float w1 = yfrac * sinf ((float)M_PI * (ynext+0.5f)/(float)fh); yfrac = w1 / (w0 + w1); } // Bilinearly interpolate bilerp (p0, p1, p2, p3, xfrac, yfrac, n, pixel); } // Resize src into dst, relying on the linear interpolation of // interppixel_NDC_full or interppixel_NDC_clamped, for the pixel range. template static bool resize_block_ (ImageBuf &dst, const ImageBuf &src, ROI roi, bool envlatlmode) { int x0 = roi.xbegin, x1 = roi.xend, y0 = roi.ybegin, y1 = roi.yend; const ImageSpec &srcspec (src.spec()); bool src_is_crop = (srcspec.x > srcspec.full_x || srcspec.y > srcspec.full_y || srcspec.z > srcspec.full_z || srcspec.x+srcspec.width < srcspec.full_x+srcspec.full_width || srcspec.y+srcspec.height < srcspec.full_y+srcspec.full_height || srcspec.z+srcspec.depth < srcspec.full_z+srcspec.full_depth); const ImageSpec &dstspec (dst.spec()); float *pel = ALLOCA (float, dstspec.nchannels); float xoffset = (float) dstspec.full_x; float yoffset = (float) dstspec.full_y; float xscale = 1.0f / (float)dstspec.full_width; float yscale = 1.0f / (float)dstspec.full_height; int nchannels = dst.nchannels(); ASSERT (dst.spec().format == TypeDesc::TypeFloat); ImageBuf::Iterator d (dst, roi); for (int y = y0; y < y1; ++y) { float t = (y+0.5f)*yscale + yoffset; for (int x = x0; x < x1; ++x, ++d) { float s = (x+0.5f)*xscale + xoffset; if (src_is_crop) src.interppixel_NDC_full (s, t, pel); else interppixel_NDC_clamped (src, s, t, pel, envlatlmode); for (int c = 0; c < nchannels; ++c) d[c] = pel[c]; } } return true; } // Helper function to compute the first bilerp pass into a scanline buffer template static void halve_scanline(const SRCTYPE *s, const int nchannels, size_t sw, float *dst) { for (size_t i = 0; i < sw; i += 2, s += nchannels) { for (int j = 0; j < nchannels; ++j, ++dst, ++s) *dst = 0.5f * (float) (*s + *(s + nchannels)); } } // Bilinear resize performed as a 2-pass filter. // Optimized to assume that the images are contiguous. template static bool resize_block_2pass (ImageBuf &dst, const ImageBuf &src, ROI roi, bool allow_shift) { // Two-pass filtering introduces a half-pixel shift for odd resolutions. // Revert to correct bilerp sampling unless shift is explicitly allowed. if (!allow_shift && (src.spec().width % 2 || src.spec().height % 2)) return resize_block_(dst, src, roi, false); DASSERT(roi.ybegin + roi.height() <= dst.spec().height); // Allocate two scanline buffers to hold the result of the first pass const int nchannels = dst.nchannels(); const size_t row_elem = roi.width() * nchannels; // # floats in scanline boost::scoped_array S0 (new float [row_elem]); boost::scoped_array S1 (new float [row_elem]); // We know that the buffers created for mipmapping are all contiguous, // so we can skip the iterators for a bilerp resize entirely along with // any NDC -> pixel math, and just directly traverse pixels. const SRCTYPE *s = (const SRCTYPE *)src.localpixels(); SRCTYPE *d = (SRCTYPE *)dst.localpixels(); ASSERT(s && d); // Assume contig bufs d += roi.ybegin * dst.spec().width * nchannels; // Top of dst ROI const size_t ystride = src.spec().width * nchannels;// Scanline offset s += 2 * roi.ybegin * ystride; // Top of src ROI // Run through destination rows, doing the two-pass bilerp filter const size_t dw = roi.width(), dh = roi.height(); // Loop invariants const size_t sw = dw * 2; // Handle odd res for (size_t y = 0; y < dh; ++y) { // For each dst ROI row halve_scanline(s, nchannels, sw, &S0[0]); s += ystride; halve_scanline(s, nchannels, sw, &S1[0]); s += ystride; const float *s0 = &S0[0], *s1 = &S1[0]; for (size_t x = 0; x < dw; ++x) { // For each dst ROI col for (int i = 0; i < nchannels; ++i, ++s0, ++s1, ++d) *d = (SRCTYPE) (0.5f * (*s0 + *s1)); // Average vertically } } return true; } static bool resize_block (ImageBuf &dst, const ImageBuf &src, ROI roi, bool envlatlmode, bool allow_shift) { const ImageSpec &srcspec (src.spec()); const ImageSpec &dstspec (dst.spec()); DASSERT (dstspec.nchannels == srcspec.nchannels); DASSERT (dst.localpixels()); bool ok; if (src.localpixels() && // Not a cached image !envlatlmode && // not latlong wrap mode roi.xbegin == 0 && // Region x at origin dstspec.width == roi.width() && // Full width ROI dstspec.width == (srcspec.width / 2) && // Src is 2x resize dstspec.format == srcspec.format && // Same formats dstspec.x == 0 && dstspec.y == 0 && // Not a crop or overscan srcspec.x == 0 && srcspec.y == 0) { // If all these conditions are met, we have a special case that // can be more highly optimized. OIIO_DISPATCH_TYPES (ok, "resize_block_2pass", resize_block_2pass, srcspec.format, dst, src, roi, allow_shift); } else { ASSERT (dst.spec().format == TypeDesc::TypeFloat); OIIO_DISPATCH_TYPES (ok, "resize_block", resize_block_, srcspec.format, dst, src, roi, envlatlmode); } return ok; } // Copy src into dst, but only for the range [x0,x1) x [y0,y1). static void check_nan_block (const ImageBuf &src, ROI roi, int &found_nonfinite) { int x0 = roi.xbegin, x1 = roi.xend, y0 = roi.ybegin, y1 = roi.yend; const ImageSpec &spec (src.spec()); float *pel = ALLOCA (float, spec.nchannels); for (int y = y0; y < y1; ++y) { for (int x = x0; x < x1; ++x) { src.getpixel (x, y, pel); for (int c = 0; c < spec.nchannels; ++c) { if (! isfinite(pel[c])) { spin_lock lock (maketx_mutex); if (found_nonfinite < 3) std::cerr << "maketx ERROR: Found " << pel[c] << " at (x=" << x << ", y=" << y << ")\n"; ++found_nonfinite; break; // skip other channels, there's no point } } } } } inline Imath::V3f latlong_to_dir (float s, float t, bool y_is_up=true) { float theta = 2.0f*M_PI * s; float phi = t * M_PI; float sinphi, cosphi; sincos (phi, &sinphi, &cosphi); if (y_is_up) return Imath::V3f (sinphi*sinf(theta), cosphi, -sinphi*cosf(theta)); else return Imath::V3f (-sinphi*cosf(theta), -sinphi*sinf(theta), cosphi); } static bool lightprobe_to_envlatl (ImageBuf &dst, const ImageBuf &src, bool y_is_up, ROI roi=ROI::All(), int nthreads=0) { ASSERT (dst.initialized() && src.nchannels() == dst.nchannels()); if (! roi.defined()) roi = get_roi (dst.spec()); roi.chend = std::min (roi.chend, dst.nchannels()); if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(lightprobe_to_envlatl, OIIO::ref(dst), OIIO::cref(src), y_is_up, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case const ImageSpec &dstspec (dst.spec()); int nchannels = dstspec.nchannels; ASSERT (dstspec.format == TypeDesc::FLOAT); float *pixel = ALLOCA (float, nchannels); float dw = dstspec.width, dh = dstspec.height; for (ImageBuf::Iterator d (dst, roi); ! d.done(); ++d) { Imath::V3f V = latlong_to_dir ((d.x()+0.5f)/dw, (dh-1.0f-d.y()+0.5f)/dh, y_is_up); float r = M_1_PI*acosf(V[2]) / hypotf(V[0],V[1]); float u = (V[0]*r + 1.0f) * 0.5f; float v = (V[1]*r + 1.0f) * 0.5f; interppixel_NDC_clamped (src, float(u), float(v), pixel, false); for (int c = roi.chbegin; c < roi.chend; ++c) d[c] = pixel[c]; } return true; } static void fix_latl_edges (ImageBuf &buf) { int n = buf.nchannels(); float *left = ALLOCA (float, n); float *right = ALLOCA (float, n); // Make the whole first and last row be solid, since they are exactly // on the pole float wscale = 1.0f / (buf.spec().width); for (int j = 0; j <= 1; ++j) { int y = (j==0) ? buf.ybegin() : buf.yend()-1; // use left for the sum, right for each new pixel for (int c = 0; c < n; ++c) left[c] = 0.0f; for (int x = buf.xbegin(); x < buf.xend(); ++x) { buf.getpixel (x, y, right); for (int c = 0; c < n; ++c) left[c] += right[c]; } for (int c = 0; c < n; ++c) left[c] *= wscale; for (int x = buf.xbegin(); x < buf.xend(); ++x) buf.setpixel (x, y, left); } // Make the left and right match, since they are both right on the // prime meridian. for (int y = buf.ybegin(); y < buf.yend(); ++y) { buf.getpixel (buf.xbegin(), y, left); buf.getpixel (buf.xend()-1, y, right); for (int c = 0; c < n; ++c) left[c] = 0.5f * left[c] + 0.5f * right[c]; buf.setpixel (buf.xbegin(), y, left); buf.setpixel (buf.xend()-1, y, left); } } static std::string formatres (const ImageSpec &spec, bool extended=false) { std::string s; s = Strutil::format("%dx%d", spec.width, spec.height); if (extended) { if (spec.x || spec.y) s += Strutil::format("%+d%+d", spec.x, spec.y); if (spec.width != spec.full_width || spec.height != spec.full_height || spec.x != spec.full_x || spec.y != spec.full_y) { s += " (full/display window is "; s += Strutil::format("%dx%d", spec.full_width, spec.full_height); if (spec.full_x || spec.full_y) s += Strutil::format("%+d%+d", spec.full_x, spec.full_y); s += ")"; } } return s; } static void maketx_merge_spec (ImageSpec &dstspec, const ImageSpec &srcspec) { for (size_t i = 0, e = srcspec.extra_attribs.size(); i < e; ++i) { const ImageIOParameter &p (srcspec.extra_attribs[i]); ustring name = p.name(); if (Strutil::istarts_with (name.string(), "maketx:")) { // Special instruction -- don't copy it to the destination spec } else { // just an attribute that should be set upon output dstspec.attribute (name.string(), p.type(), p.data()); } } } static bool write_mipmap (ImageBufAlgo::MakeTextureMode mode, OIIO::shared_ptr &img, const ImageSpec &outspec_template, std::string outputfilename, ImageOutput *out, TypeDesc outputdatatype, bool mipmap, string_view filtername, const ImageSpec &configspec, std::ostream &outstream, double &stat_writetime, double &stat_miptime, size_t &peak_mem) { bool envlatlmode = (mode == ImageBufAlgo::MakeTxEnvLatl); bool orig_was_overscan = (img->spec().x || img->spec().y || img->spec().z || img->spec().full_x || img->spec().full_y || img->spec().full_z); ImageSpec outspec = outspec_template; outspec.set_format (outputdatatype); if (mipmap && !out->supports ("multiimage") && !out->supports ("mipmap")) { outstream << "maketx ERROR: \"" << outputfilename << "\" format does not support multires images\n"; return false; } if (! mipmap && ! strcmp (out->format_name(), "openexr")) { // Send hint to OpenEXR driver that we won't specify a MIPmap outspec.attribute ("openexr:levelmode", 0 /* ONE_LEVEL */); } if (mipmap && ! strcmp (out->format_name(), "openexr")) { outspec.attribute ("openexr:roundingmode", 0 /* ROUND_DOWN */); } // OpenEXR always uses border sampling for environment maps bool src_samples_border = false; if (envlatlmode && !strcmp(out->format_name(), "openexr")) { src_samples_border = true; outspec.attribute ("oiio:updirection", "y"); outspec.attribute ("oiio:sampleborder", 1); } if (envlatlmode && src_samples_border) fix_latl_edges (*img); bool do_highlight_compensation = configspec.get_int_attribute ("maketx:highlightcomp", 0); float sharpen = configspec.get_float_attribute ("maketx:sharpen", 0.0f); string_view sharpenfilt = "gaussian"; bool sharpen_first = true; if (Strutil::istarts_with (filtername, "post-")) { sharpen_first = false; filtername.remove_prefix (5); } if (Strutil::istarts_with (filtername, "unsharp-")) { filtername.remove_prefix (8); sharpenfilt = filtername; filtername = "lanczos3"; } Timer writetimer; if (! out->open (outputfilename.c_str(), outspec)) { outstream << "maketx ERROR: Could not open \"" << outputfilename << "\" : " << out->geterror() << "\n"; return false; } // Write out the image bool verbose = configspec.get_int_attribute ("maketx:verbose") != 0; if (verbose) { outstream << " Writing file: " << outputfilename << std::endl; outstream << " Filter \"" << filtername << "\"\n"; outstream << " Top level is " << formatres(outspec) << std::endl; } if (! img->write (out)) { // ImageBuf::write transfers any errors from the ImageOutput to // the ImageBuf. outstream << "maketx ERROR: Write failed \" : " << img->geterror() << "\n"; out->close (); return false; } stat_writetime += writetimer(); if (mipmap) { // Mipmap levels: if (verbose) outstream << " Mipmapping...\n" << std::flush; std::vector mipimages; std::string mipimages_unsplit = configspec.get_string_attribute ("maketx:mipimages"); if (mipimages_unsplit.length()) Strutil::split (mipimages_unsplit, mipimages, ";"); bool allow_shift = configspec.get_int_attribute("maketx:allow_pixel_shift") != 0; OIIO::shared_ptr small (new ImageBuf); while (outspec.width > 1 || outspec.height > 1) { Timer miptimer; ImageSpec smallspec; if (mipimages.size()) { // Special case -- the user specified a custom MIP level small->reset (mipimages[0]); small->read (0, 0, true, TypeDesc::FLOAT); smallspec = small->spec(); if (smallspec.nchannels != outspec.nchannels) { outstream << "WARNING: Custom mip level \"" << mipimages[0] << " had the wrong number of channels.\n"; OIIO::shared_ptr t (new ImageBuf (smallspec)); ImageBufAlgo::channels(*t, *small, outspec.nchannels, NULL, NULL, NULL, true); std::swap (t, small); } smallspec.tile_width = outspec.tile_width; smallspec.tile_height = outspec.tile_height; smallspec.tile_depth = outspec.tile_depth; mipimages.erase (mipimages.begin()); } else { // Resize a factor of two smaller smallspec = outspec; smallspec.width = img->spec().width; smallspec.height = img->spec().height; smallspec.depth = img->spec().depth; if (smallspec.width > 1) smallspec.width /= 2; if (smallspec.height > 1) smallspec.height /= 2; smallspec.full_width = smallspec.width; smallspec.full_height = smallspec.height; smallspec.full_depth = smallspec.depth; if (!allow_shift || configspec.get_int_attribute("maketx:forcefloat", 1)) smallspec.set_format (TypeDesc::FLOAT); // Trick: to get the resize working properly, we reset // both display and pixel windows to match, and have 0 // offset, AND doctor the big image to have its display // and pixel windows match. Don't worry, the texture // engine doesn't care what the upper MIP levels have // for the window sizes, it uses level 0 to determine // the relatinship between texture 0-1 space (display // window) and the pixels. smallspec.x = 0; smallspec.y = 0; smallspec.full_x = 0; smallspec.full_y = 0; small->reset (smallspec); // Realocate with new size img->set_full (img->xbegin(), img->xend(), img->ybegin(), img->yend(), img->zbegin(), img->zend()); if (filtername == "box" && !orig_was_overscan && sharpen <= 0.0f) { ImageBufAlgo::parallel_image (OIIO::bind(resize_block, OIIO::ref(*small), OIIO::cref(*img), _1, envlatlmode, allow_shift), OIIO::get_roi(small->spec())); } else { Filter2D *filter = setup_filter (small->spec(), img->spec(), filtername); if (! filter) { outstream << "maketx ERROR: could not make filter \"" << filtername << "\"\n"; return false; } if (verbose) { outstream << " Downsampling filter \"" << filter->name() << "\" width = " << filter->width(); if (sharpen > 0.0f) { outstream << ", sharpening " << sharpen << " with " << sharpenfilt << " unsharp mask " << (sharpen_first ? "before" : "after") << " the resize"; } outstream << "\n"; } if (do_highlight_compensation) ImageBufAlgo::rangecompress (*img, *img); if (sharpen > 0.0f && sharpen_first) { OIIO::shared_ptr sharp (new ImageBuf); bool uok = ImageBufAlgo::unsharp_mask (*sharp, *img, sharpenfilt, 3.0, sharpen, 0.0f); if (! uok) outstream << "maketx ERROR: " << sharp->geterror() << "\n"; std::swap (img, sharp); } ImageBufAlgo::resize (*small, *img, filter); if (sharpen > 0.0f && ! sharpen_first) { OIIO::shared_ptr sharp (new ImageBuf); bool uok = ImageBufAlgo::unsharp_mask (*sharp, *small, sharpenfilt, 3.0, sharpen, 0.0f); if (! uok) outstream << "maketx ERROR: " << sharp->geterror() << "\n"; std::swap (small, sharp); } if (do_highlight_compensation) { ImageBufAlgo::rangeexpand (*small, *small); ImageBufAlgo::clamp (*small, *small, 0.0f, std::numeric_limits::max(), true); } Filter2D::destroy (filter); } } stat_miptime += miptimer(); outspec = smallspec; outspec.set_format (outputdatatype); if (envlatlmode && src_samples_border) fix_latl_edges (*small); Timer writetimer; // If the format explicitly supports MIP-maps, use that, // otherwise try to simulate MIP-mapping with multi-image. ImageOutput::OpenMode mode = out->supports ("mipmap") ? ImageOutput::AppendMIPLevel : ImageOutput::AppendSubimage; if (! out->open (outputfilename.c_str(), outspec, mode)) { outstream << "maketx ERROR: Could not append \"" << outputfilename << "\" : " << out->geterror() << "\n"; return false; } if (! small->write (out)) { // ImageBuf::write transfers any errors from the // ImageOutput to the ImageBuf. outstream << "maketx ERROR writing \"" << outputfilename << "\" : " << small->geterror() << "\n"; out->close (); return false; } stat_writetime += writetimer(); if (verbose) { size_t mem = Sysutil::memory_used(true); peak_mem = std::max (peak_mem, mem); outstream << Strutil::format (" %-15s (%s)", formatres(smallspec), Strutil::memformat(mem)) << std::endl; } std::swap (img, small); } } if (verbose) outstream << " Wrote file: " << outputfilename << " (" << Strutil::memformat(Sysutil::memory_used(true)) << ")\n"; writetimer.reset (); writetimer.start (); if (! out->close ()) { outstream << "maketx ERROR writing \"" << outputfilename << "\" : " << out->geterror() << "\n"; return false; } stat_writetime += writetimer (); return true; } static bool make_texture_impl (ImageBufAlgo::MakeTextureMode mode, const ImageBuf *input, std::string filename, std::string outputfilename, const ImageSpec &_configspec, std::ostream *outstream_ptr) { ASSERT (mode >= 0 && mode < ImageBufAlgo::_MakeTxLast); double stat_readtime = 0; double stat_writetime = 0; double stat_resizetime = 0; double stat_miptime = 0; double stat_colorconverttime = 0; size_t peak_mem = 0; Timer alltime; #define STATUS(task,timer) \ { \ size_t mem = Sysutil::memory_used(true); \ peak_mem = std::max (peak_mem, mem); \ if (verbose) \ outstream << Strutil::format (" %-25s %s (%s)\n", task, \ Strutil::timeintervalformat(timer,2), \ Strutil::memformat(mem)); \ } ImageSpec configspec = _configspec; std::stringstream localstream; // catch output when user doesn't want it std::ostream &outstream (outstream_ptr ? *outstream_ptr : localstream); bool from_filename = (input == NULL); if (from_filename && ! Filesystem::exists (filename)) { outstream << "maketx ERROR: \"" << filename << "\" does not exist\n"; return false; } OIIO::shared_ptr src; if (input == NULL) { // No buffer supplied -- create one to read the file src.reset (new ImageBuf(filename)); src->init_spec (filename, 0, 0); // force it to get the spec, not read } else if (input->cachedpixels()) { // Image buffer supplied that's backed by ImageCache -- create a // copy (very light weight, just another cache reference) src.reset (new ImageBuf(*input)); } else { // Image buffer supplied that has pixels -- wrap it src.reset (new ImageBuf(input->name(), input->spec(), (void *)input->localpixels())); } ASSERT (src.get()); if (! outputfilename.length()) { std::string fn = src->name(); if (fn.length()) { if (Filesystem::extension(fn).length() > 1) outputfilename = Filesystem::replace_extension (fn, ".tx"); else outputfilename = outputfilename + ".tx"; } else { outstream << "maketx: no output filename supplied\n"; return false; } } // Write the texture to a temp file first, then rename it to the final // destination (same directory). This improves robustness. There is less // chance a crash during texture conversion will leave behind a // partially formed tx with incomplete mipmaps levels which happesn to // be extremely slow to use in a raytracer. // We also force a unique filename to protect against multiple maketx // processes running at the same time on the same file. std::string extension = Filesystem::extension(outputfilename); std::string tmpfilename = Filesystem::replace_extension (outputfilename, ".%%%%%%%%.temp"+extension); tmpfilename = Filesystem::unique_path(tmpfilename); // When was the input file last modified? // This is only used when we're reading from a filename std::time_t in_time; if (from_filename) in_time = Filesystem::last_write_time (src->name()); else time (&in_time); // make it look initialized // When in update mode, skip making the texture if the output already // exists and has the same file modification time as the input file and // was created with identical command line arguments. bool updatemode = configspec.get_int_attribute ("maketx:updatemode"); if (updatemode && from_filename && Filesystem::exists(outputfilename) && in_time == Filesystem::last_write_time (outputfilename)) { std::string lastcmdline; if (ImageInput *in = ImageInput::open (outputfilename)) { lastcmdline = in->spec().get_string_attribute ("Software"); ImageInput::destroy (in); } std::string newcmdline = configspec.get_string_attribute("maketx:full_command_line"); if (lastcmdline.size() && lastcmdline == newcmdline) { outstream << "maketx: no update required for \"" << outputfilename << "\"\n"; return true; } } bool shadowmode = (mode == ImageBufAlgo::MakeTxShadow); bool envlatlmode = (mode == ImageBufAlgo::MakeTxEnvLatl || mode == ImageBufAlgo::MakeTxEnvLatlFromLightProbe); // Find an ImageIO plugin that can open the output file, and open it std::string outformat = configspec.get_string_attribute ("maketx:fileformatname", outputfilename); ImageOutput *out = ImageOutput::create (outformat.c_str()); if (! out) { outstream << "maketx ERROR: Could not find an ImageIO plugin to write " << outformat << " files:" << geterror() << "\n"; return false; } if (! out->supports ("tiles")) { outstream << "maketx ERROR: \"" << outputfilename << "\" format does not support tiled images\n"; return false; } // The cache might mess with the apparent data format, so make sure // it's the nativespec that we consult for data format of the file. TypeDesc out_dataformat = src->nativespec().format; if (configspec.format != TypeDesc::UNKNOWN) out_dataformat = configspec.format; // We cannot compute the prman / oiio options until after out_dataformat // has been determined, as it's required (and can potentially change // out_dataformat too!) if (configspec.get_int_attribute("maketx:prman_options")) out_dataformat = set_prman_options (out_dataformat, configspec); else if (configspec.get_int_attribute("maketx:oiio_options")) out_dataformat = set_oiio_options (out_dataformat, configspec); // Read the full file locally if it's less than 1 GB, otherwise // allow the ImageBuf to use ImageCache to manage memory. int local_mb_thresh = configspec.get_int_attribute("maketx:read_local_MB", 1024); bool read_local = (src->spec().image_bytes() < imagesize_t(local_mb_thresh * 1024*1024)); bool verbose = configspec.get_int_attribute ("maketx:verbose") != 0; double misc_time_1 = alltime.lap(); STATUS ("prep", misc_time_1); if (from_filename) { if (verbose) outstream << "Reading file: " << src->name() << std::endl; if (! src->read (0, 0, read_local)) { outstream << "maketx ERROR: Could not read \"" << src->name() << "\" : " << src->geterror() << "\n"; return false; } } stat_readtime += alltime.lap(); STATUS (Strutil::format("read \"%s\"", src->name()), stat_readtime); if (mode == ImageBufAlgo::MakeTxEnvLatlFromLightProbe) { ImageSpec newspec = src->spec(); newspec.width = newspec.full_width = src->spec().width; newspec.height = newspec.full_height = src->spec().height/2; newspec.tile_width = newspec.tile_height = 0; newspec.format = TypeDesc::FLOAT; OIIO::shared_ptr latlong (new ImageBuf(newspec)); // Now lightprobe holds the original lightprobe, src is a blank // image that will be the unwrapped latlong version of it. lightprobe_to_envlatl (*latlong, *src, true); // Carry on with the lat-long environment map from here on out mode = ImageBufAlgo::MakeTxEnvLatl; src = latlong; } // Some things require knowing a bunch about the pixel statistics. bool constant_color_detect = configspec.get_int_attribute("maketx:constant_color_detect"); bool opaque_detect = configspec.get_int_attribute("maketx:opaque_detect"); bool compute_average_color = configspec.get_int_attribute("maketx:compute_average", 1); ImageBufAlgo::PixelStats pixel_stats; bool compute_stats = (constant_color_detect || opaque_detect || compute_average_color); if (compute_stats) ImageBufAlgo::computePixelStats (pixel_stats, *src); // If requested - and we're a constant color - make a tiny texture instead // Only safe if the full/display window is the same as the data window. // Also note that this could affect the appearance when using "black" // wrap mode at runtime. std::vector constantColor(src->nchannels()); bool isConstantColor = false; if (compute_stats && src->spec().x == 0 && src->spec().y == 0 && src->spec().z == 0 && src->spec().full_x == 0 && src->spec().full_y == 0 && src->spec().full_z == 0 && src->spec().full_width == src->spec().width && src->spec().full_height == src->spec().height && src->spec().full_depth == src->spec().depth) { isConstantColor = (pixel_stats.min == pixel_stats.max); if (isConstantColor) constantColor = pixel_stats.min; if (isConstantColor && constant_color_detect) { // Reset the image, to a new image, at the tile size ImageSpec newspec = src->spec(); newspec.width = std::min (configspec.tile_width, src->spec().width); newspec.height = std::min (configspec.tile_height, src->spec().height); newspec.depth = std::min (configspec.tile_depth, src->spec().depth); newspec.full_width = newspec.width; newspec.full_height = newspec.height; newspec.full_depth = newspec.depth; std::string name = std::string(src->name()) + ".constant_color"; src->reset(name, newspec); ImageBufAlgo::fill (*src, &constantColor[0]); if (verbose) { outstream << " Constant color image detected. "; outstream << "Creating " << newspec.width << "x" << newspec.height << " texture instead.\n"; } } } int nchannels = configspec.get_int_attribute ("maketx:nchannels", -1); // If requested -- and alpha is 1.0 everywhere -- drop it. if (opaque_detect && src->spec().alpha_channel == src->nchannels()-1 && nchannels <= 0 && pixel_stats.min[src->spec().alpha_channel] == 1.0f && pixel_stats.max[src->spec().alpha_channel] == 1.0f) { if (verbose) outstream << " Alpha==1 image detected. Dropping the alpha channel.\n"; OIIO::shared_ptr newsrc (new ImageBuf(src->spec())); ImageBufAlgo::channels (*newsrc, *src, src->nchannels()-1, NULL, NULL, NULL, true); std::swap (src, newsrc); // N.B. the old src will delete } // If requested - and we're a monochrome image - drop the extra channels if (configspec.get_int_attribute("maketx:monochrome_detect") && nchannels <= 0 && src->nchannels() == 3 && src->spec().alpha_channel < 0 && // RGB only ImageBufAlgo::isMonochrome(*src)) { if (verbose) outstream << " Monochrome image detected. Converting to single channel texture.\n"; OIIO::shared_ptr newsrc (new ImageBuf(src->spec())); ImageBufAlgo::channels (*newsrc, *src, 1, NULL, NULL, NULL, true); std::swap (src, newsrc); } // If we've otherwise explicitly requested to write out a // specific number of channels, do it. if ((nchannels > 0) && (nchannels != src->nchannels())) { if (verbose) outstream << " Overriding number of channels to " << nchannels << "\n"; OIIO::shared_ptr newsrc (new ImageBuf(src->spec())); ImageBufAlgo::channels (*newsrc, *src, nchannels, NULL, NULL, NULL, true); std::swap (src, newsrc); } std::string channelnames = configspec.get_string_attribute ("maketx:channelnames"); if (channelnames.size()) { std::vector newchannelnames; Strutil::split (channelnames, newchannelnames, ","); ImageSpec &spec (src->specmod()); // writeable version for (int c = 0; c < spec.nchannels; ++c) { if (c < (int)newchannelnames.size() && newchannelnames[c].size()) { std::string name = newchannelnames[c]; spec.channelnames[c] = name; if (Strutil::iequals(name,"A") || Strutil::iends_with(name,".A") || Strutil::iequals(name,"Alpha") || Strutil::iends_with(name,".Alpha")) spec.alpha_channel = c; if (Strutil::iequals(name,"Z") || Strutil::iends_with(name,".Z") || Strutil::iequals(name,"Depth") || Strutil::iends_with(name,".Depth")) spec.z_channel = c; } } } if (shadowmode) { // Some special checks for shadow maps if (src->spec().nchannels != 1) { outstream << "maketx ERROR: shadow maps require 1-channel images,\n" << "\t\"" << src->name() << "\" is " << src->spec().nchannels << " channels\n"; return false; } // Shadow maps only make sense for floating-point data. if (out_dataformat != TypeDesc::FLOAT && out_dataformat != TypeDesc::HALF && out_dataformat != TypeDesc::DOUBLE) out_dataformat = TypeDesc::FLOAT; } if (configspec.get_int_attribute("maketx:set_full_to_pixels")) { // User requested that we treat the image as uncropped or not // overscan ImageSpec &spec (src->specmod()); spec.full_x = spec.x = 0; spec.full_y = spec.y = 0; spec.full_z = spec.z = 0; spec.full_width = spec.width; spec.full_height = spec.height; spec.full_depth = spec.depth; } // Copy the input spec ImageSpec srcspec = src->spec(); ImageSpec dstspec = srcspec; bool do_resize = false; // If the pixel window is not a superset of the display window, pad it // with black. ROI roi = get_roi(dstspec); ROI roi_full = get_roi_full(dstspec); roi.xbegin = std::min (roi.xbegin, roi_full.xbegin); roi.ybegin = std::min (roi.ybegin, roi_full.ybegin); roi.zbegin = std::min (roi.zbegin, roi_full.zbegin); roi.xend = std::max (roi.xend, roi_full.xend); roi.yend = std::max (roi.yend, roi_full.yend); roi.zend = std::max (roi.zend, roi_full.zend); if (roi != get_roi(srcspec)) { do_resize = true; // do the resize if we were a cropped image set_roi (dstspec, roi); } bool orig_was_overscan = (roi != roi_full); if (orig_was_overscan) { configspec.attribute ("wrapmodes", "black,black"); } if ((dstspec.x < 0 || dstspec.y < 0 || dstspec.z < 0) && (out && !out->supports("negativeorigin"))) { // User passed negative origin but the output format doesn't // support it. Try to salvage the situation by shifting the // image into the positive range. if (dstspec.x < 0) { dstspec.full_x -= dstspec.x; dstspec.x = 0; } if (dstspec.y < 0) { dstspec.full_y -= dstspec.y; dstspec.y = 0; } if (dstspec.z < 0) { dstspec.full_z -= dstspec.z; dstspec.z = 0; } } // Make the output tiled, regardless of input dstspec.tile_width = configspec.tile_width ? configspec.tile_width : 64; dstspec.tile_height = configspec.tile_height ? configspec.tile_height : 64; dstspec.tile_depth = configspec.tile_depth ? configspec.tile_depth : 1; // Try to force zip (still can be overriden by configspec dstspec.attribute ("compression", "zip"); // Always prefer contiguous channels, unless overridden by configspec dstspec.attribute ("planarconfig", "contig"); // Default to black wrap mode, unless overridden by configspec dstspec.attribute ("wrapmodes", "black,black"); if (configspec.get_int_attribute ("maketx:ignore_unassoc")) dstspec.erase_attribute ("oiio:UnassociatedAlpha"); // Put a DateTime in the out file, either now, or matching the date // stamp of the input file (if update mode). time_t date; if (updatemode && from_filename) date = in_time; // update mode: use the time stamp of the input else time (&date); // not update: get the time now dstspec.attribute ("DateTime", datestring(date)); std::string cmdline = configspec.get_string_attribute ("maketx:full_command_line"); if (! cmdline.empty()) { // Append command to image history std::string history = dstspec.get_string_attribute ("Exif:ImageHistory"); if (history.length() && ! Strutil::iends_with (history, "\n")) history += std::string("\n"); history += cmdline; dstspec.attribute ("Exif:ImageHistory", history); } bool prman_metadata = configspec.get_int_attribute ("maketx:prman_metadata") != 0; if (shadowmode) { dstspec.attribute ("textureformat", "Shadow"); if (prman_metadata) dstspec.attribute ("PixarTextureFormat", "Shadow"); } else if (envlatlmode) { dstspec.attribute ("textureformat", "LatLong Environment"); configspec.attribute ("wrapmodes", "periodic,clamp"); if (prman_metadata) dstspec.attribute ("PixarTextureFormat", "LatLong Environment"); } else { dstspec.attribute ("textureformat", "Plain Texture"); if (prman_metadata) dstspec.attribute ("PixarTextureFormat", "Plain Texture"); } if (prman_metadata) { // Suppress writing of exif directory in the TIFF file to not // confuse the older libtiff that PRMan uses. dstspec.attribute ("tiff:write_exif", 0); } // FIXME -- should we allow tile sizes to reduce if the image is // smaller than the tile size? And when we do, should we also try // to make it bigger in the other direction to make the total tile // size more constant? // Fix nans/infs (if requested) std::string fixnan = configspec.get_string_attribute("maketx:fixnan"); ImageBufAlgo::NonFiniteFixMode fixmode = ImageBufAlgo::NONFINITE_NONE; if (fixnan.empty() || fixnan == "none") { } else if (fixnan == "black") { fixmode = ImageBufAlgo::NONFINITE_BLACK; } else if (fixnan == "box3") { fixmode = ImageBufAlgo::NONFINITE_BOX3; } else { outstream << "maketx ERROR: Unknown --fixnan mode " << " fixnan\n"; return false; } int pixelsFixed = 0; if (fixmode != ImageBufAlgo::NONFINITE_NONE && (srcspec.format.basetype == TypeDesc::FLOAT || srcspec.format.basetype == TypeDesc::HALF || srcspec.format.basetype == TypeDesc::DOUBLE) && ! ImageBufAlgo::fixNonFinite (*src, *src, fixmode, &pixelsFixed)) { outstream << "maketx ERROR: Error fixing nans/infs.\n"; return false; } if (verbose && pixelsFixed) outstream << " Warning: " << pixelsFixed << " nan/inf pixels fixed.\n"; // If --checknan was used and it's a floating point image, check for // nonfinite (NaN or Inf) values and abort if they are found. if (configspec.get_int_attribute("maketx:checknan") && (srcspec.format.basetype == TypeDesc::FLOAT || srcspec.format.basetype == TypeDesc::HALF || srcspec.format.basetype == TypeDesc::DOUBLE)) { int found_nonfinite = 0; ImageBufAlgo::parallel_image (OIIO::bind(check_nan_block, OIIO::ref(*src), _1, OIIO::ref(found_nonfinite)), OIIO::get_roi(srcspec)); if (found_nonfinite) { if (found_nonfinite > 3) outstream << "maketx ERROR: ...and Nan/Inf at " << (found_nonfinite-3) << " other pixels\n"; return false; } } double misc_time_2 = alltime.lap(); STATUS ("misc2", misc_time_2); // Color convert the pixels, if needed, in place. If a color // conversion is required we will promote the src to floating point // (or there wont be enough precision potentially). Also, // independently color convert the constant color metadata std::string incolorspace = configspec.get_string_attribute ("maketx:incolorspace"); std::string outcolorspace = configspec.get_string_attribute ("maketx:outcolorspace"); if (!incolorspace.empty() && !outcolorspace.empty() && incolorspace != outcolorspace) { if (verbose) outstream << " Converting from colorspace " << incolorspace << " to colorspace " << outcolorspace << std::endl; // Buffer for the color-corrected version. Start by making it just // another pointer to the original source. OIIO::shared_ptr ccSrc (src); // color-corrected buffer if (src->spec().format != TypeDesc::FLOAT) { // If the original src buffer isn't float, make a scratch space // that is float. ImageSpec floatSpec = src->spec(); floatSpec.set_format (TypeDesc::FLOAT); ccSrc.reset (new ImageBuf (floatSpec)); } ColorConfig colorconfig; if (colorconfig.error()) { outstream << "Error Creating ColorConfig\n"; outstream << colorconfig.geterror() << std::endl; return false; } ColorProcessor * processor = colorconfig.createColorProcessor ( incolorspace.c_str(), outcolorspace.c_str()); if (!processor || colorconfig.error()) { outstream << "Error Creating Color Processor." << std::endl; outstream << colorconfig.geterror() << std::endl; return false; } bool unpremult = configspec.get_int_attribute ("maketx:unpremult") != 0; if (unpremult && verbose) outstream << " Unpremulting image..." << std::endl; if (!ImageBufAlgo::colorconvert (*ccSrc, *src, processor, unpremult)) { outstream << "Error applying color conversion to image.\n"; return false; } if (isConstantColor) { if (!ImageBufAlgo::colorconvert (&constantColor[0], static_cast(constantColor.size()), processor, unpremult)) { outstream << "Error applying color conversion to constant color.\n"; return false; } } if (compute_average_color) { if (!ImageBufAlgo::colorconvert (&pixel_stats.avg[0], static_cast(pixel_stats.avg.size()), processor, unpremult)) { outstream << "Error applying color conversion to average color.\n"; return false; } } ColorConfig::deleteColorProcessor(processor); processor = NULL; // swap the color-converted buffer and src (making src be the // working master that's color converted). std::swap (src, ccSrc); // N.B. at this point, ccSrc will go out of scope, freeing it if // it was a scratch buffer. stat_colorconverttime += alltime.lap(); STATUS ("color convert", stat_colorconverttime); } // Force float for the sake of the ImageBuf math. // Also force float if we do not allow for the pixel shift, // since resize_block_ requires floating point buffers. const int allow_shift = configspec.get_int_attribute("maketx:allow_pixel_shift"); if (configspec.get_int_attribute("maketx:forcefloat", 1) || !allow_shift) dstspec.set_format (TypeDesc::FLOAT); // Handle resize to power of two, if called for if (configspec.get_int_attribute("maketx:resize") && ! shadowmode) { dstspec.width = pow2roundup (dstspec.width); dstspec.height = pow2roundup (dstspec.height); dstspec.full_width = dstspec.width; dstspec.full_height = dstspec.height; } // Resize if we're up-resing for pow2 if (dstspec.width != srcspec.width || dstspec.height != srcspec.height || dstspec.full_depth != srcspec.full_depth) do_resize = true; // resize if we're converting from non-border sampling to border sampling // (converting TO an OpenEXR environment map). if (envlatlmode && (Strutil::iequals(configspec.get_string_attribute("maketx:fileformatname"),"openexr") || Strutil::iends_with(outputfilename,".exr"))) do_resize = true; if (do_resize && orig_was_overscan && out && !out->supports("displaywindow")) { outstream << "maketx ERROR: format " << out->format_name() << " does not support separate display windows,\n" << " which is necessary when combining resizing" << " and an input image with overscan."; return false; } std::string filtername = configspec.get_string_attribute ("maketx:filtername", "box"); double misc_time_3 = alltime.lap(); STATUS ("misc3", misc_time_3); OIIO::shared_ptr toplevel; // Ptr to top level of mipmap if (! do_resize && dstspec.format == src->spec().format) { // No resize needed, no format conversion needed -- just stick to // the image we've already got toplevel = src; } else if (! do_resize) { // Need format conversion, but no resize -- just copy the pixels toplevel.reset (new ImageBuf (dstspec)); toplevel->copy_pixels (*src); } else { // Resize if (verbose) outstream << " Resizing image to " << dstspec.width << " x " << dstspec.height << std::endl; string_view resize_filter (filtername); if (Strutil::istarts_with (resize_filter, "unsharp-")) resize_filter = "lanczos3"; toplevel.reset (new ImageBuf (dstspec)); if ((resize_filter == "box" || resize_filter == "triangle") && !orig_was_overscan) { ImageBufAlgo::parallel_image (OIIO::bind(resize_block, OIIO::ref(*toplevel), OIIO::cref(*src), _1, envlatlmode, allow_shift), OIIO::get_roi(dstspec)); } else { Filter2D *filter = setup_filter (toplevel->spec(), src->spec(), resize_filter); if (! filter) { outstream << "maketx ERROR: could not make filter \"" << resize_filter << "\"\n"; return false; } ImageBufAlgo::resize (*toplevel, *src, filter); Filter2D::destroy (filter); } } stat_resizetime += alltime.lap(); STATUS ("resize & data convert", stat_resizetime); // toplevel now holds the color converted, format converted, resized // master copy. We can release src. src.reset (); // Update the toplevel ImageDescription with the sha1 pixel hash and // constant color std::string desc = dstspec.get_string_attribute ("ImageDescription"); bool updatedDesc = false; // Eliminate any SHA-1 or ConstantColor hints in the ImageDescription. if (desc.size()) { desc = boost::regex_replace (desc, boost::regex("SHA-1=[[:xdigit:]]*[ ]*"), ""); static const char *fp_number_pattern = "([+-]?((?:(?:[[:digit:]]*\\.)?[[:digit:]]+(?:[eE][+-]?[[:digit:]]+)?)))"; const std::string constcolor_pattern = std::string ("ConstantColor=(\\[?") + fp_number_pattern + ",?)+\\]?[ ]*"; const std::string average_pattern = std::string ("AverageColor=(\\[?") + fp_number_pattern + ",?)+\\]?[ ]*"; desc = boost::regex_replace (desc, boost::regex(constcolor_pattern), ""); desc = boost::regex_replace (desc, boost::regex(average_pattern), ""); updatedDesc = true; } // The hash is only computed for the top mipmap level of pixel data. // Thus, any additional information that will affect the lower levels // (such as filtering information) needs to be manually added into the // hash. std::ostringstream addlHashData; addlHashData << filtername << " "; float sharpen = configspec.get_float_attribute ("maketx:sharpen", 0.0f); if (sharpen != 0.0f) { addlHashData << "sharpen_A=" << sharpen << " "; // NB if we change the sharpening algorithm, change the letter! } if (configspec.get_int_attribute ("maketx:highlightcomp", 0)) addlHashData << "highlightcomp=1 "; const int sha1_blocksize = 256; std::string hash_digest = configspec.get_int_attribute("maketx:hash", 1) ? ImageBufAlgo::computePixelHashSHA1 (*toplevel, addlHashData.str(), ROI::All(), sha1_blocksize) : ""; if (hash_digest.length()) { if (out->supports("arbitrary_metadata")) { dstspec.attribute ("oiio:SHA-1", hash_digest); } else { if (desc.length()) desc += " "; desc += "oiio:SHA-1="; desc += hash_digest; updatedDesc = true; } if (verbose) outstream << " SHA-1: " << hash_digest << std::endl; } double stat_hashtime = alltime.lap(); STATUS ("SHA-1 hash", stat_hashtime); if (isConstantColor) { std::ostringstream os; // Emulate a JSON array for (int i = 0; i < dstspec.nchannels; ++i) { if (i!=0) os << ","; os << (i<(int)constantColor.size() ? constantColor[i] : 0.0f); } if (out->supports("arbitrary_metadata")) { dstspec.attribute ("oiio:ConstantColor", os.str()); } else { if (desc.length()) desc += " "; desc += "oiio:ConstantColor="; desc += os.str(); updatedDesc = true; } if (verbose) outstream << " ConstantColor: " << os.str() << std::endl; } if (compute_average_color) { std::ostringstream os; // Emulate a JSON array for (int i = 0; i < dstspec.nchannels; ++i) { if (i!=0) os << ","; os << (i<(int)pixel_stats.avg.size() ? pixel_stats.avg[i] : 0.0f); } if (out->supports("arbitrary_metadata")) { dstspec.attribute ("oiio:AverageColor", os.str()); } else { // if arbitrary metadata is not supported, cram it into the // ImageDescription. if (desc.length()) desc += " "; desc += "oiio:AverageColor="; desc += os.str(); updatedDesc = true; } if (verbose) outstream << " AverageColor: " << os.str() << std::endl; } if (updatedDesc) { dstspec.attribute ("ImageDescription", desc); } if (configspec.get_float_attribute("fovcot") == 0.0f) { configspec.attribute("fovcot", float(srcspec.full_width) / float(srcspec.full_height)); } maketx_merge_spec (dstspec, configspec); double misc_time_4 = alltime.lap(); STATUS ("misc4", misc_time_4); // Write out, and compute, the mipmap levels for the speicifed image bool nomipmap = configspec.get_int_attribute ("maketx:nomipmap") != 0; bool ok = write_mipmap (mode, toplevel, dstspec, tmpfilename, out, out_dataformat, !shadowmode && !nomipmap, filtername, configspec, outstream, stat_writetime, stat_miptime, peak_mem); delete out; // don't need it any more // If using update mode, stamp the output file with a modification time // matching that of the input file. if (ok && updatemode && from_filename) Filesystem::last_write_time (tmpfilename, in_time); // Since we wrote the texture to a temp file first, now we rename it to // the final destination. if (ok) { std::string err; ok = Filesystem::rename (tmpfilename, outputfilename, err); if (! ok) outstream << "maketx ERROR: could not rename file: " << err << "\n"; } if (! ok) Filesystem::remove (tmpfilename); if (verbose || configspec.get_int_attribute("maketx:runstats") || configspec.get_int_attribute("maketx:stats")) { double all = alltime(); outstream << Strutil::format ("maketx run time (seconds): %5.2f\n", all);; outstream << Strutil::format (" file read: %5.2f\n", stat_readtime); outstream << Strutil::format (" file write: %5.2f\n", stat_writetime); outstream << Strutil::format (" initial resize: %5.2f\n", stat_resizetime); outstream << Strutil::format (" hash: %5.2f\n", stat_hashtime); outstream << Strutil::format (" mip computation: %5.2f\n", stat_miptime); outstream << Strutil::format (" color convert: %5.2f\n", stat_colorconverttime); outstream << Strutil::format (" unaccounted: %5.2f (%5.2f %5.2f %5.2f %5.2f)\n", all-stat_readtime-stat_writetime-stat_resizetime-stat_hashtime-stat_miptime, misc_time_1, misc_time_2, misc_time_3, misc_time_4); outstream << Strutil::format ("maketx peak memory used: %s\n", Strutil::memformat(peak_mem)); } #undef STATUS return ok; } bool ImageBufAlgo::make_texture (ImageBufAlgo::MakeTextureMode mode, string_view filename, string_view outputfilename, const ImageSpec &configspec, std::ostream *outstream) { return make_texture_impl (mode, NULL, filename, outputfilename, configspec, outstream); } bool ImageBufAlgo::make_texture (ImageBufAlgo::MakeTextureMode mode, const std::vector &filenames, string_view outputfilename, const ImageSpec &configspec, std::ostream *outstream_ptr) { return make_texture_impl (mode, NULL, filenames[0], outputfilename, configspec, outstream_ptr); } bool ImageBufAlgo::make_texture (ImageBufAlgo::MakeTextureMode mode, const ImageBuf &input, string_view outputfilename, const ImageSpec &configspec, std::ostream *outstream) { return make_texture_impl (mode, &input, "", outputfilename, configspec, outstream); } openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/libOpenImageIO.map0000644000175000017500000000006613151711064023163 0ustar mfvmfv{ global: *OpenImageIO*; *OIIO*; local: *; }; openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/imagebufalgo_yee.cpp0000644000175000017500000003047613151711064023701 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// \file /// Implementation of ImageBufAlgo algorithms. #include #include #include #include using Imath::Color3f; #include "OpenImageIO/fmath.h" #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/imagebufalgo_util.h" #include "OpenImageIO/dassert.h" template inline Imath::Vec3 powf (const Imath::Vec3 &x, float y) { return Imath::Vec3 (powf (x[0], y), powf (x[1], y), powf (x[2], y)); } OIIO_NAMESPACE_BEGIN namespace { #define PYRAMID_MAX_LEVELS 8 class GaussianPyramid { public: GaussianPyramid (ImageBuf &image) { level[0].swap (image); // swallow the source as the top level ImageBuf kernel; ImageBufAlgo::make_kernel (kernel, "gaussian", 5, 5); for (int i = 1; i < PYRAMID_MAX_LEVELS; ++i) ImageBufAlgo::convolve (level[i], level[i-1], kernel); } ~GaussianPyramid () { } float value (int x, int y, int lev) const { if (lev >= PYRAMID_MAX_LEVELS) return 0.0f; else return level[lev].getchannel(x,y,0,1); } ImageBuf &operator[] (int lev) { DASSERT (lev < PYRAMID_MAX_LEVELS); return level[lev]; } float operator() (int x, int y, int lev) const { DASSERT (lev < PYRAMID_MAX_LEVELS); return level[lev].getchannel(x,y,0,1); } private: ImageBuf level[PYRAMID_MAX_LEVELS]; }; // Adobe RGB (1998) with reference white D65 -> XYZ // matrix is from http://www.brucelindbloom.com/ inline Color3f AdobeRGBToXYZ_color (const Color3f &rgb) { return Color3f (rgb[0] * 0.576700f + rgb[1] * 0.185556f + rgb[2] * 0.188212f, rgb[0] * 0.297361f + rgb[1] * 0.627355f + rgb[2] * 0.0752847f, rgb[0] * 0.0270328f + rgb[1] * 0.0706879f + rgb[2] * 0.991248f); } static bool AdobeRGBToXYZ (ImageBuf &A, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image (OIIO::bind(AdobeRGBToXYZ, OIIO::ref(A), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case for (ImageBuf::Iterator a (A, roi); !a.done(); ++a) { Color3f rgb (a[0], a[1], a[2]); Color3f XYZ = AdobeRGBToXYZ_color (rgb); a[0] = XYZ[0]; a[1] = XYZ[1]; a[2] = XYZ[2]; } return true; } /// Convert a color in XYZ space to LAB space. /// static Color3f XYZToLAB_color (const Color3f xyz) { // Reference white point static const Color3f white (0.576700f + 0.185556f + 0.188212f, 0.297361f + 0.627355f + 0.0752847f, 0.0270328f + 0.0706879f + 0.991248f); const float epsilon = 216.0f / 24389.0f; const float kappa = 24389.0f / 27.0f; Color3f r = xyz / white; Color3f f; for (int i = 0; i < 3; i++) { if (r[i] > epsilon) f[i] = powf (r[i], 1.0f / 3.0f); else f[i] = (kappa * r[i] + 16.0f) / 116.0f; } return Color3f (116.0f * f[1] - 16.0f, // L 500.0f * (f[0] - f[1]), // A 200.0f * (f[1] - f[2])); // B } static bool XYZToLAB (ImageBuf &A, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image (OIIO::bind(XYZToLAB, OIIO::ref(A), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case for (ImageBuf::Iterator a (A, roi); !a.done(); ++a) { Color3f XYZ (a[0], a[1], a[2]); Color3f LAB = XYZToLAB_color (XYZ); a[0] = LAB[0]; a[1] = LAB[1]; a[2] = LAB[2]; } return true; } // Contrast sensitivity function (Barten SPIE 1989) static float contrast_sensitivity (float cyclesperdegree, float luminance) { float a = 440.0f * powf ((1.0f + 0.7f / luminance), -0.2f); float b = 0.3f * powf ((1.0f + 100.0f / luminance), 0.15f); return a * cyclesperdegree * expf(-b * cyclesperdegree) * sqrtf(1.0f + 0.06f * expf(b * cyclesperdegree)); } // Visual Masking Function from Daly 1993 inline float mask (float contrast) { float a = powf (392.498f * contrast, 0.7f); float b = powf (0.0153f * a, 4.0f); return powf (1.0f + b, 0.25f); } // Given the adaptation luminance, this function returns the // threshold of visibility in cd per m^2 // TVI means Threshold vs Intensity function // This version comes from Ward Larson Siggraph 1997 static float tvi (float adaptation_luminance) { // returns the threshold luminance given the adaptation luminance // units are candelas per meter squared float r; float log_a = log10f(adaptation_luminance); if (log_a < -3.94f) r = -2.86f; else if (log_a < -1.44f) r = powf(0.405f * log_a + 1.6f , 2.18f) - 2.86f; else if (log_a < -0.0184f) r = log_a - 0.395f; else if (log_a < 1.9f) r = powf(0.249f * log_a + 0.65f, 2.7f) - 0.72f; else r = log_a - 1.255f; return powf (10.0f, r); } } int ImageBufAlgo::compare_Yee (const ImageBuf &img0, const ImageBuf &img1, CompareResults &result, float luminance, float fov, ROI roi, int nthreads) { if (! roi.defined()) roi = roi_union (get_roi(img0.spec()), get_roi(img1.spec())); roi.chend = std::max (roi.chend, roi.chbegin+3); // max of 3 channels result.maxerror = 0; result.maxx=0, result.maxy=0, result.maxz=0, result.maxc=0; result.nfail = 0, result.nwarn = 0; int nscanlines = roi.height() * roi.depth(); bool luminanceOnly = false; // assuming colorspaces are in Adobe RGB (1998), convert to LAB // paste() to copy of up to 3 channels, converting to float, and // ending up with a 0-origin image. End up with an LAB image in // aLAB, and a luminance image in aLum. ImageSpec spec (roi.width(), roi.height(), 3 /*chans*/, TypeDesc::FLOAT); ImageBuf aLAB (spec); ImageBufAlgo::paste (aLAB, 0, 0, 0, 0, img0, roi, nthreads); AdobeRGBToXYZ (aLAB, ROI::All(), nthreads); // contains XYZ now ImageBuf aLum; int channelorder[] = { 1 }; // channel to copy ImageBufAlgo::channels (aLum, aLAB, 1, channelorder); ImageBufAlgo::mul (aLum, aLum, luminance, ROI::All(), nthreads); XYZToLAB (aLAB, ROI::All(), nthreads); // now it's LAB // Same thing for img1/bLAB/bLum ImageBuf bLAB (spec); ImageBufAlgo::paste (bLAB, 0, 0, 0, 0, img1, roi, nthreads); AdobeRGBToXYZ (bLAB, ROI::All(), nthreads); // contains XYZ now ImageBuf bLum; ImageBufAlgo::channels (bLum, bLAB, 1, channelorder); ImageBufAlgo::mul (bLum, bLum, luminance, ROI::All(), nthreads); XYZToLAB (bLAB, ROI::All(), nthreads); // now it's LAB // Construct Gaussian pyramids (not really pyramids, because they all // have the same resolution, but really just a bunch of successively // more blurred images). GaussianPyramid la (aLum); GaussianPyramid lb (bLum); float num_one_degree_pixels = (float) (2 * tan(fov * 0.5 * M_PI / 180) * 180 / M_PI); float pixels_per_degree = roi.width() / num_one_degree_pixels; unsigned int adaptation_level = 0; for (int i = 0, npixels = 1; i < PYRAMID_MAX_LEVELS && npixels <= num_one_degree_pixels; ++i, npixels *= 2) adaptation_level = i; float cpd[PYRAMID_MAX_LEVELS]; cpd[0] = 0.5f * pixels_per_degree; for (int i = 1; i < PYRAMID_MAX_LEVELS; ++i) cpd[i] = 0.5f * cpd[i - 1]; float csf_max = contrast_sensitivity (3.248f, 100.0f); float F_freq[PYRAMID_MAX_LEVELS - 2]; for (int i = 0; i < PYRAMID_MAX_LEVELS - 2; ++i) F_freq[i] = csf_max / contrast_sensitivity (cpd[i], 100.0f); for (int y = 0; y < nscanlines; ++y) { for (int x = 0; x < roi.width(); ++x) { float contrast[PYRAMID_MAX_LEVELS - 2]; float sum_contrast = 0; for (int i = 0; i < PYRAMID_MAX_LEVELS - 2; i++) { float n1 = fabsf (la.value(x,y,i) - la.value(x,y,i+1)); float n2 = fabsf (lb.value(x,y,i) - lb.value(x,y,i+1)); float numerator = std::max (n1, n2); float d1 = fabsf (la.value(x,y,i+2)); float d2 = fabsf (lb.value(x,y,i+2)); float denominator = std::max (std::max (d1, d2), 1.0e-5f); contrast[i] = numerator / denominator; sum_contrast += contrast[i]; } if (sum_contrast < 1e-5) sum_contrast = 1e-5f; float F_mask[PYRAMID_MAX_LEVELS - 2]; float adapt = la.value(x,y,adaptation_level) + lb.value(x,y,adaptation_level); adapt *= 0.5f; if (adapt < 1e-5) adapt = 1e-5f; for (int i = 0; i < PYRAMID_MAX_LEVELS - 2; i++) F_mask[i] = mask(contrast[i] * contrast_sensitivity(cpd[i], adapt)); float factor = 0; for (int i = 0; i < PYRAMID_MAX_LEVELS - 2; i++) factor += contrast[i] * F_freq[i] * F_mask[i] / sum_contrast; factor = Imath::clamp (factor, 1.0f, 10.0f); float delta = fabsf (la.value(x,y,0) - lb.value(x,y,0)); bool pass = true; // pure luminance test delta /= tvi(adapt); if (delta > factor) { pass = false; } else if (! luminanceOnly) { // CIE delta E test with modifications float color_scale = 1.0f; // ramp down the color test in scotopic regions if (adapt < 10.0f) { color_scale = 1.0f - (10.0f - color_scale) / 10.0f; color_scale = color_scale * color_scale; } float da = aLAB.getchannel(x,y,0,1) - bLAB.getchannel(x,y,0,1); // diff in A float db = aLAB.getchannel(x,y,0,2) - bLAB.getchannel(x,y,0,2); // diff in B da = da * da; db = db * db; delta = (da + db) * color_scale; if (delta > factor) pass = false; } if (!pass) { ++result.nfail; if (factor > result.maxerror) { result.maxerror = factor; result.maxx = x; result.maxy = y; // result.maxz = z; } } } } return result.nfail; } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/imagebufalgo_test.cpp0000644000175000017500000005656513151711064024105 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ // Based on the sample at: // http://code.google.com/p/googletest/wiki/GoogleTestPrimer#Writing_the_main()_Function #include "OpenImageIO/imageio.h" #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/imagebufalgo_util.h" #include "OpenImageIO/unittest.h" #include #include #include #include OIIO_NAMESPACE_USING; void test_type_merge () { std::cout << "test type_merge\n"; using namespace OIIO::ImageBufAlgo; OIIO_CHECK_EQUAL (type_merge(TypeDesc::UINT8, TypeDesc::UINT8), TypeDesc::UINT8); OIIO_CHECK_EQUAL (type_merge(TypeDesc::UINT8, TypeDesc::FLOAT), TypeDesc::FLOAT); OIIO_CHECK_EQUAL (type_merge(TypeDesc::FLOAT, TypeDesc::UINT8), TypeDesc::FLOAT); OIIO_CHECK_EQUAL (type_merge(TypeDesc::UINT8, TypeDesc::UINT16), TypeDesc::UINT16); OIIO_CHECK_EQUAL (type_merge(TypeDesc::UINT16, TypeDesc::FLOAT), TypeDesc::FLOAT); OIIO_CHECK_EQUAL (type_merge(TypeDesc::HALF, TypeDesc::FLOAT), TypeDesc::FLOAT); OIIO_CHECK_EQUAL (type_merge(TypeDesc::HALF, TypeDesc::UINT8), TypeDesc::HALF); OIIO_CHECK_EQUAL (type_merge(TypeDesc::HALF, TypeDesc::UNKNOWN), TypeDesc::HALF); OIIO_CHECK_EQUAL (type_merge(TypeDesc::FLOAT, TypeDesc::UNKNOWN), TypeDesc::FLOAT); OIIO_CHECK_EQUAL (type_merge(TypeDesc::UINT8, TypeDesc::UNKNOWN), TypeDesc::UINT8); } // Test ImageBuf::zero and ImageBuf::fill void test_zero_fill () { std::cout << "test zero_fill\n"; const int WIDTH = 8; const int HEIGHT = 6; const int CHANNELS = 4; ImageSpec spec (WIDTH, HEIGHT, CHANNELS, TypeDesc::FLOAT); spec.alpha_channel = 3; // Create a buffer -- pixels should be undefined ImageBuf A (spec); // Set a pixel to an odd value, make sure it takes const float arbitrary1[CHANNELS] = { 0.2f, 0.3f, 0.4f, 0.5f }; A.setpixel (1, 1, arbitrary1); float pixel[CHANNELS]; // test pixel A.getpixel (1, 1, pixel); for (int c = 0; c < CHANNELS; ++c) OIIO_CHECK_EQUAL (pixel[c], arbitrary1[c]); // Zero out and test that it worked ImageBufAlgo::zero (A); for (int j = 0; j < HEIGHT; ++j) { for (int i = 0; i < WIDTH; ++i) { float pixel[CHANNELS]; A.getpixel (i, j, pixel); for (int c = 0; c < CHANNELS; ++c) OIIO_CHECK_EQUAL (pixel[c], 0.0f); } } // Test fill of whole image const float arbitrary2[CHANNELS] = { 0.6f, 0.7f, 0.3f, 0.9f }; ImageBufAlgo::fill (A, arbitrary2); for (int j = 0; j < HEIGHT; ++j) { for (int i = 0; i < WIDTH; ++i) { float pixel[CHANNELS]; A.getpixel (i, j, pixel); for (int c = 0; c < CHANNELS; ++c) OIIO_CHECK_EQUAL (pixel[c], arbitrary2[c]); } } // Test fill of partial image const float arbitrary3[CHANNELS] = { 0.42f, 0.43f, 0.44f, 0.45f }; { const int xbegin = 3, xend = 5, ybegin = 0, yend = 4; ImageBufAlgo::fill (A, arbitrary3, ROI(xbegin, xend, ybegin, yend)); for (int j = 0; j < HEIGHT; ++j) { for (int i = 0; i < WIDTH; ++i) { float pixel[CHANNELS]; A.getpixel (i, j, pixel); if (j >= ybegin && j < yend && i >= xbegin && i < xend) { for (int c = 0; c < CHANNELS; ++c) OIIO_CHECK_EQUAL (pixel[c], arbitrary3[c]); } else { for (int c = 0; c < CHANNELS; ++c) OIIO_CHECK_EQUAL (pixel[c], arbitrary2[c]); } } } } } // Test ImageBuf::crop void test_crop () { std::cout << "test crop\n"; int WIDTH = 8, HEIGHT = 6, CHANNELS = 4; // Crop region we'll work with int xbegin = 3, xend = 5, ybegin = 0, yend = 4; ImageSpec spec (WIDTH, HEIGHT, CHANNELS, TypeDesc::FLOAT); spec.alpha_channel = 3; ImageBuf A, B; A.reset (spec); B.reset (spec); float arbitrary1[4]; arbitrary1[0] = 0.2f; arbitrary1[1] = 0.3f; arbitrary1[2] = 0.4f; arbitrary1[3] = 0.5f; ImageBufAlgo::fill (A, arbitrary1); // Test CUT crop ImageBufAlgo::crop (B, A, ROI(xbegin, xend, ybegin, yend)); // Should have changed the data window (origin and width/height) OIIO_CHECK_EQUAL (B.spec().x, xbegin); OIIO_CHECK_EQUAL (B.spec().width, xend-xbegin); OIIO_CHECK_EQUAL (B.spec().y, ybegin); OIIO_CHECK_EQUAL (B.spec().height, yend-ybegin); float *pixel = ALLOCA(float, CHANNELS); for (int j = 0; j < B.spec().height; ++j) { for (int i = 0; i < B.spec().width; ++i) { B.getpixel (i+B.xbegin(), j+B.ybegin(), pixel); // Inside the crop region should match what it always was for (int c = 0; c < CHANNELS; ++c) OIIO_CHECK_EQUAL (pixel[c], arbitrary1[c]); } } } void test_paste () { std::cout << "test paste\n"; // Create the source image, make it a gradient ImageSpec Aspec (4, 4, 3, TypeDesc::FLOAT); ImageBuf A (Aspec); for (ImageBuf::Iterator it (A); !it.done(); ++it) { it[0] = float(it.x()) / float(Aspec.width-1); it[1] = float(it.y()) / float(Aspec.height-1); it[2] = 0.1f; } // Create destination image -- black it out ImageSpec Bspec (8, 8, 3, TypeDesc::FLOAT); ImageBuf B (Bspec); float gray[3] = { .1, .1, .1 }; ImageBufAlgo::fill (B, gray); // Paste a few pixels from A into B -- include offsets ImageBufAlgo::paste (B, 2, 2, 0, 1 /* chan offset */, A, ROI(1, 4, 1, 4)); // Spot check float a[3], b[3]; B.getpixel (1, 1, 0, b); OIIO_CHECK_EQUAL (b[0], gray[0]); OIIO_CHECK_EQUAL (b[1], gray[1]); OIIO_CHECK_EQUAL (b[2], gray[2]); B.getpixel (2, 2, 0, b); A.getpixel (1, 1, 0, a); OIIO_CHECK_EQUAL (b[0], gray[0]); OIIO_CHECK_EQUAL (b[1], a[0]); OIIO_CHECK_EQUAL (b[2], a[1]); B.getpixel (3, 4, 0, b); A.getpixel (2, 3, 0, a); OIIO_CHECK_EQUAL (b[0], gray[0]); OIIO_CHECK_EQUAL (b[1], a[0]); OIIO_CHECK_EQUAL (b[2], a[1]); } void test_channel_append () { std::cout << "test channel_append\n"; ImageSpec spec (2, 2, 1, TypeDesc::FLOAT); ImageBuf A (spec); ImageBuf B (spec); float Acolor = 0.1, Bcolor = 0.2; ImageBufAlgo::fill (A, &Acolor); ImageBufAlgo::fill (B, &Bcolor); ImageBuf R ("R"); ImageBufAlgo::channel_append (R, A, B); OIIO_CHECK_EQUAL (R.spec().width, spec.width); OIIO_CHECK_EQUAL (R.spec().height, spec.height); OIIO_CHECK_EQUAL (R.nchannels(), 2); for (ImageBuf::ConstIterator r(R); !r.done(); ++r) { OIIO_CHECK_EQUAL (r[0], Acolor); OIIO_CHECK_EQUAL (r[1], Bcolor); } } // Tests ImageBufAlgo::add void test_add () { std::cout << "test add\n"; const int WIDTH = 4, HEIGHT = 4, CHANNELS = 4; ImageSpec spec (WIDTH, HEIGHT, CHANNELS, TypeDesc::FLOAT); // Create buffers ImageBuf A (spec); const float Aval[CHANNELS] = { 0.1, 0.2, 0.3, 0.4 }; ImageBufAlgo::fill (A, Aval); ImageBuf B (spec); const float Bval[CHANNELS] = { 0.01, 0.02, 0.03, 0.04 }; ImageBufAlgo::fill (B, Bval); // Test addition of images ImageBuf R (spec); ImageBufAlgo::add (R, A, B); for (int j = 0; j < spec.height; ++j) for (int i = 0; i < spec.width; ++i) for (int c = 0; c < spec.nchannels; ++c) OIIO_CHECK_EQUAL (R.getchannel (i, j, 0, c), Aval[c] + Bval[c]); // Test addition of image and constant color ImageBuf D (spec); ImageBufAlgo::add (D, A, Bval); ImageBufAlgo::CompareResults comp; ImageBufAlgo::compare (R, D, 1e-6, 1e-6, comp); OIIO_CHECK_EQUAL (comp.maxerror, 0.0); } // Tests ImageBufAlgo::sub void test_sub () { std::cout << "test sub\n"; const int WIDTH = 4, HEIGHT = 4, CHANNELS = 4; ImageSpec spec (WIDTH, HEIGHT, CHANNELS, TypeDesc::FLOAT); // Create buffers ImageBuf A (spec); const float Aval[CHANNELS] = { 0.1, 0.2, 0.3, 0.4 }; ImageBufAlgo::fill (A, Aval); ImageBuf B (spec); const float Bval[CHANNELS] = { 0.01, 0.02, 0.03, 0.04 }; ImageBufAlgo::fill (B, Bval); // Test subtraction of images ImageBuf R (spec); ImageBufAlgo::sub (R, A, B); for (int j = 0; j < spec.height; ++j) for (int i = 0; i < spec.width; ++i) for (int c = 0; c < spec.nchannels; ++c) OIIO_CHECK_EQUAL (R.getchannel (i, j, 0, c), Aval[c] - Bval[c]); // Test subtraction of image and constant color ImageBuf D (spec); ImageBufAlgo::sub (D, A, Bval); ImageBufAlgo::CompareResults comp; ImageBufAlgo::compare (R, D, 1e-6, 1e-6, comp); OIIO_CHECK_EQUAL (comp.maxerror, 0.0); } // Tests ImageBufAlgo::mul void test_mul () { std::cout << "test mul\n"; const int WIDTH = 4, HEIGHT = 4, CHANNELS = 4; ImageSpec spec (WIDTH, HEIGHT, CHANNELS, TypeDesc::FLOAT); // Create buffers ImageBuf A (spec); const float Aval[CHANNELS] = { 0.1, 0.2, 0.3, 0.4 }; ImageBufAlgo::fill (A, Aval); ImageBuf B (spec); const float Bval[CHANNELS] = { 0.01, 0.02, 0.03, 0.04 }; ImageBufAlgo::fill (B, Bval); // Test multiplication of images ImageBuf R (spec); ImageBufAlgo::mul (R, A, B); for (int j = 0; j < spec.height; ++j) for (int i = 0; i < spec.width; ++i) for (int c = 0; c < spec.nchannels; ++c) OIIO_CHECK_EQUAL (R.getchannel (i, j, 0, c), Aval[c] * Bval[c]); // Test multiplication of image and constant color ImageBuf D (spec); ImageBufAlgo::mul (D, A, Bval); ImageBufAlgo::CompareResults comp; ImageBufAlgo::compare (R, D, 1e-6, 1e-6, comp); OIIO_CHECK_EQUAL (comp.maxerror, 0.0); } // Tests ImageBufAlgo::mad void test_mad () { std::cout << "test mad\n"; const int WIDTH = 4, HEIGHT = 4, CHANNELS = 4; ImageSpec spec (WIDTH, HEIGHT, CHANNELS, TypeDesc::FLOAT); // Create buffers ImageBuf A (spec); const float Aval[CHANNELS] = { 0.1, 0.2, 0.3, 0.4 }; ImageBufAlgo::fill (A, Aval); ImageBuf B (spec); const float Bval[CHANNELS] = { 1, 2, 3, 4 }; ImageBufAlgo::fill (B, Bval); ImageBuf C (spec); const float Cval[CHANNELS] = { 0.01, 0.02, 0.03, 0.04 }; ImageBufAlgo::fill (C, Cval); // Test multiplication of images ImageBuf R (spec); ImageBufAlgo::mad (R, A, B, C); for (int j = 0; j < spec.height; ++j) for (int i = 0; i < spec.width; ++i) for (int c = 0; c < spec.nchannels; ++c) OIIO_CHECK_EQUAL (R.getchannel (i, j, 0, c), Aval[c] * Bval[c] + Cval[c]); // Test multiplication of image and constant color ImageBuf D (spec); ImageBufAlgo::mad (D, A, Bval, Cval); ImageBufAlgo::CompareResults comp; ImageBufAlgo::compare (R, D, 1e-6, 1e-6, comp); OIIO_CHECK_EQUAL (comp.maxerror, 0.0); } // Tests ImageBufAlgo::compare void test_compare () { std::cout << "test compare\n"; // Construct two identical 50% grey images const int WIDTH = 10, HEIGHT = 10, CHANNELS = 3; ImageSpec spec (WIDTH, HEIGHT, CHANNELS, TypeDesc::FLOAT); ImageBuf A (spec); ImageBuf B (spec); const float grey[CHANNELS] = { 0.5, 0.5, 0.5 }; ImageBufAlgo::fill (A, grey); ImageBufAlgo::fill (B, grey); // Introduce some minor differences const int NDIFFS = 10; ImageBuf::Iterator a (A); for (int i = 0; i < NDIFFS && a.valid(); ++i, ++a) { for (int c = 0; c < CHANNELS; ++c) a[c] = a[c] + 0.01f * i; } // We expect the differences to be { 0, 0.01, 0.02, 0.03, 0.04, 0.05, // 0.06, 0.07, 0.08, 0.09, 0, 0, ...}. const float failthresh = 0.05; const float warnthresh = 0.025; ImageBufAlgo::CompareResults comp; ImageBufAlgo::compare (A, B, failthresh, warnthresh, comp); // We expect 5 pixels to exceed the fail threshold, 7 pixels to // exceed the warn threshold, the maximum difference to be 0.09, // and the maximally different pixel to be (9,0). // The total error should be 3 chans * sum{0.01,...,0.09} / (pixels*chans) // = 3 * 0.45 / (100*3) = 0.0045 std::cout << "Testing comparison: " << comp.nfail << " failed, " << comp.nwarn << " warned, max diff = " << comp.maxerror << " @ (" << comp.maxx << ',' << comp.maxy << ")\n"; std::cout << " mean err " << comp.meanerror << ", RMS err " << comp.rms_error << ", PSNR = " << comp.PSNR << "\n"; OIIO_CHECK_EQUAL (comp.nfail, 5); OIIO_CHECK_EQUAL (comp.nwarn, 7); OIIO_CHECK_EQUAL_THRESH (comp.maxerror, 0.09, 1e-6); OIIO_CHECK_EQUAL (comp.maxx, 9); OIIO_CHECK_EQUAL (comp.maxy, 0); OIIO_CHECK_EQUAL_THRESH (comp.meanerror, 0.0045, 1.0e-8); } // Tests ImageBufAlgo::isConstantColor void test_isConstantColor () { std::cout << "test isConstantColor\n"; const int WIDTH = 10, HEIGHT = 10, CHANNELS = 3; ImageSpec spec (WIDTH, HEIGHT, CHANNELS, TypeDesc::FLOAT); ImageBuf A (spec); const float col[CHANNELS] = { 0.25, 0.5, 0.75 }; ImageBufAlgo::fill (A, col); float thecolor[CHANNELS] = { 0, 0, 0 }; OIIO_CHECK_EQUAL (ImageBufAlgo::isConstantColor (A), true); OIIO_CHECK_EQUAL (ImageBufAlgo::isConstantColor (A, thecolor), true); OIIO_CHECK_EQUAL (col[0], thecolor[0]); OIIO_CHECK_EQUAL (col[1], thecolor[1]); OIIO_CHECK_EQUAL (col[2], thecolor[2]); // Now introduce a difference const float another[CHANNELS] = { 0, 1, 1 }; A.setpixel (2, 2, 0, another, 3); OIIO_CHECK_EQUAL (ImageBufAlgo::isConstantColor (A), false); OIIO_CHECK_EQUAL (ImageBufAlgo::isConstantColor (A, thecolor), false); // Make sure ROI works ROI roi (0, WIDTH, 0, 2, 0, 1, 0, CHANNELS); // should match for this ROI OIIO_CHECK_EQUAL (ImageBufAlgo::isConstantColor (A, NULL, roi), true); } // Tests ImageBufAlgo::isConstantChannel void test_isConstantChannel () { std::cout << "test isConstantChannel\n"; const int WIDTH = 10, HEIGHT = 10, CHANNELS = 3; ImageSpec spec (WIDTH, HEIGHT, CHANNELS, TypeDesc::FLOAT); ImageBuf A (spec); const float col[CHANNELS] = { 0.25, 0.5, 0.75 }; ImageBufAlgo::fill (A, col); OIIO_CHECK_EQUAL (ImageBufAlgo::isConstantChannel (A, 1, 0.5f), true); // Now introduce a difference const float another[CHANNELS] = { 0, 1, 1 }; A.setpixel (2, 2, 0, another, 3); OIIO_CHECK_EQUAL (ImageBufAlgo::isConstantChannel (A, 1, 0.5f), false); // Make sure ROI works ROI roi (0, WIDTH, 0, 2, 0, 1, 0, CHANNELS); // should match for this ROI OIIO_CHECK_EQUAL (ImageBufAlgo::isConstantChannel (A, 1, 0.5f, roi), true); } // Tests ImageBufAlgo::isMonochrome void test_isMonochrome () { std::cout << "test isMonochrome\n"; const int WIDTH = 10, HEIGHT = 10, CHANNELS = 3; ImageSpec spec (WIDTH, HEIGHT, CHANNELS, TypeDesc::FLOAT); ImageBuf A (spec); const float col[CHANNELS] = { 0.25, 0.25, 0.25 }; ImageBufAlgo::fill (A, col); OIIO_CHECK_EQUAL (ImageBufAlgo::isMonochrome (A), true); // Now introduce a difference const float another[CHANNELS] = { 0.25, 0.25, 1 }; A.setpixel (2, 2, 0, another, 3); OIIO_CHECK_EQUAL (ImageBufAlgo::isMonochrome (A), false); // Make sure ROI works ROI roi (0, WIDTH, 0, 2, 0, 1, 0, CHANNELS); // should match for this ROI OIIO_CHECK_EQUAL (ImageBufAlgo::isMonochrome (A, roi), true); } // Tests ImageBufAlgo::computePixelStats() void test_computePixelStats () { std::cout << "test computePixelStats\n"; ImageBuf img (ImageSpec(2,2,3,TypeDesc::FLOAT)); float black[3] = { 0, 0, 0 }, white[3] = { 1, 1, 1 }; img.setpixel (0, 0, black); img.setpixel (1, 0, white); img.setpixel (0, 1, black); img.setpixel (1, 1, white); ImageBufAlgo::PixelStats stats; ImageBufAlgo::computePixelStats (stats, img); for (int c = 0; c < 3; ++c) { OIIO_CHECK_EQUAL (stats.min[c], 0.0f); OIIO_CHECK_EQUAL (stats.max[c], 1.0f); OIIO_CHECK_EQUAL (stats.avg[c], 0.5f); OIIO_CHECK_EQUAL (stats.stddev[c], 0.5f); OIIO_CHECK_EQUAL (stats.nancount[c], 0); OIIO_CHECK_EQUAL (stats.infcount[c], 0); OIIO_CHECK_EQUAL (stats.finitecount[c], 4); } } // Tests histogram computation. void histogram_computation_test () { const int INPUT_WIDTH = 64; const int INPUT_HEIGHT = 64; const int INPUT_CHANNEL = 0; const int HISTOGRAM_BINS = 256; const int SPIKE1 = 51; // 0.2f in range 0->1 maps to 51 in range 0->255 const int SPIKE2 = 128; // 0.5f in range 0->1 maps to 128 in range 0->255 const int SPIKE3 = 204; // 0.8f in range 0->1 maps to 204 in range 0->255 const int SPIKE1_COUNT = INPUT_WIDTH * 8; const int SPIKE2_COUNT = INPUT_WIDTH * 16; const int SPIKE3_COUNT = INPUT_WIDTH * 40; // Create input image with three regions with different pixel values. ImageSpec spec (INPUT_WIDTH, INPUT_HEIGHT, 1, TypeDesc::FLOAT); ImageBuf A (spec); float value[] = {0.2f}; ImageBufAlgo::fill (A, value, ROI(0, INPUT_WIDTH, 0, 8)); value[0] = 0.5f; ImageBufAlgo::fill (A, value, ROI(0, INPUT_WIDTH, 8, 24)); value[0] = 0.8f; ImageBufAlgo::fill (A, value, ROI(0, INPUT_WIDTH, 24, 64)); // Compute A's histogram. std::vector hist; ImageBufAlgo::histogram (A, INPUT_CHANNEL, hist, HISTOGRAM_BINS); // Does the histogram size equal the number of bins? OIIO_CHECK_EQUAL (hist.size(), (imagesize_t)HISTOGRAM_BINS); // Are the histogram values as expected? OIIO_CHECK_EQUAL (hist[SPIKE1], (imagesize_t)SPIKE1_COUNT); OIIO_CHECK_EQUAL (hist[SPIKE2], (imagesize_t)SPIKE2_COUNT); OIIO_CHECK_EQUAL (hist[SPIKE3], (imagesize_t)SPIKE3_COUNT); for (int i = 0; i < HISTOGRAM_BINS; i++) if (i!=SPIKE1 && i!=SPIKE2 && i!=SPIKE3) OIIO_CHECK_EQUAL (hist[i], 0); } // Test ability to do a maketx directly from an ImageBuf void test_maketx_from_imagebuf() { std::cout << "test make_texture from ImageBuf\n"; // Make a checkerboard const int WIDTH = 16, HEIGHT = 16, CHANNELS = 3; ImageSpec spec (WIDTH, HEIGHT, CHANNELS, TypeDesc::FLOAT); ImageBuf A (spec); float pink[] = { 0.5f, 0.3f, 0.3f }, green[] = { 0.1f, 0.5f, 0.1f }; ImageBufAlgo::checker (A, 4, 4, 4, pink, green); // Write it const char *pgname = "oiio-pgcheck.tx"; remove (pgname); // Remove it first ImageSpec configspec; ImageBufAlgo::make_texture (ImageBufAlgo::MakeTxTexture, A, pgname, configspec); // Read it back and compare it ImageBuf B (pgname); B.read (); ImageBufAlgo::CompareResults comparison; ImageBufAlgo::compare (A, B, 0, 0, comparison); OIIO_CHECK_EQUAL (comparison.nwarn, 0); OIIO_CHECK_EQUAL (comparison.nfail, 0); remove (pgname); // clean up } // Test various IBAprep features void test_IBAprep () { using namespace ImageBufAlgo; ImageBuf rgb (ImageSpec(256, 256, 3)); // Basic RGB uint8 image ImageBuf rgba (ImageSpec(256, 256, 4)); // Basic RGBA uint8 image #define CHECK(...) { ImageBuf dst; ROI roi; OIIO_CHECK_ASSERT (IBAprep (__VA_ARGS__)); } #define CHECK0(...) { ImageBuf dst; ROI roi; OIIO_CHECK_ASSERT (!IBAprep (__VA_ARGS__)); } // Test REQUIRE_ALPHA CHECK (roi, &dst, &rgba, IBAprep_REQUIRE_ALPHA); CHECK0 (roi, &dst, &rgb, IBAprep_REQUIRE_ALPHA); // Test REQUIRE_Z ImageSpec rgbaz_spec (256, 256, 5); rgbaz_spec.channelnames[4] = "Z"; rgbaz_spec.z_channel = 4; ImageBuf rgbaz (rgbaz_spec); CHECK ( roi, &dst, &rgbaz, IBAprep_REQUIRE_Z); CHECK0 (roi, &dst, &rgb, IBAprep_REQUIRE_Z); // Test REQUIRE_SAME_NCHANNELS CHECK (roi, &dst, &rgb, &rgb, NULL, NULL, IBAprep_REQUIRE_SAME_NCHANNELS); CHECK0 (roi, &dst, &rgb, &rgba, NULL, NULL, IBAprep_REQUIRE_SAME_NCHANNELS); // Test NO_SUPPOERT_VOLUME ImageSpec volspec (256, 256, 3); volspec.depth = 256; ImageBuf vol (volspec); CHECK (roi, &dst, &rgb, IBAprep_NO_SUPPORT_VOLUME); CHECK0 (roi, &dst, &vol, IBAprep_NO_SUPPORT_VOLUME); // Test SUPPORT_DEEP ImageSpec deepspec (256, 256, 3); deepspec.deep = true; ImageBuf deep (deepspec); CHECK (roi, &dst, &deep, IBAprep_SUPPORT_DEEP); CHECK0 (roi, &dst, &deep); // deep should be rejected // Test DEEP_MIXED CHECK (roi, &dst, &deep, &deep, NULL, IBAprep_SUPPORT_DEEP | IBAprep_DEEP_MIXED); CHECK (roi, &dst, &deep, &rgb, NULL, IBAprep_SUPPORT_DEEP | IBAprep_DEEP_MIXED); CHECK (roi, &dst, &deep, &deep, NULL, IBAprep_SUPPORT_DEEP); CHECK0 (roi, &dst, &deep, &rgb, NULL, IBAprep_SUPPORT_DEEP); // Test DST_FLOAT_PIXELS { ROI roi1, roi2; ImageBuf dst1, dst2; OIIO_CHECK_ASSERT (IBAprep (roi1, &dst1, &rgb)); OIIO_CHECK_EQUAL (dst1.spec().format, TypeDesc::UINT8); OIIO_CHECK_ASSERT (IBAprep (roi2, &dst2, &rgb, IBAprep_DST_FLOAT_PIXELS)); OIIO_CHECK_EQUAL (dst2.spec().format, TypeDesc::FLOAT); } // Test MINIMIZE_NCHANNELS { ROI roi1, roi2; ImageBuf dst1, dst2; OIIO_CHECK_ASSERT (IBAprep (roi1, &dst1, &rgb, &rgba)); OIIO_CHECK_EQUAL (dst1.nchannels(), 4); OIIO_CHECK_ASSERT (IBAprep (roi2, &dst2, &rgb, &rgba, NULL, NULL, IBAprep_MINIMIZE_NCHANNELS)); OIIO_CHECK_EQUAL (dst2.nchannels(), 3); } #undef CHECK } int main (int argc, char **argv) { test_type_merge (); test_zero_fill (); test_crop (); test_paste (); test_channel_append (); test_add (); test_sub (); test_mul (); test_mad (); test_compare (); test_isConstantColor (); test_isConstantChannel (); test_isMonochrome (); test_computePixelStats (); histogram_computation_test (); test_maketx_from_imagebuf (); test_IBAprep (); return unit_test_failures; } openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/kissfft.hh0000644000175000017500000002765313151711064021706 0ustar mfvmfv// From KissFFT http://sourceforge.net/projects/kissfft/ /* Copyright (c) 2003-2010 Mark Borgerding All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef KISSFFT_CLASS_HH #include #include namespace kissfft_utils { template struct traits { typedef T_scalar scalar_type; typedef std::complex cpx_type; void fill_twiddles( std::complex * dst ,int nfft,bool inverse) { T_scalar phinc = (inverse?2:-2)* acos( (T_scalar) -1) / nfft; for (int i=0;i(0,i*phinc) ); } void prepare( std::vector< std::complex > & dst, int nfft,bool inverse, std::vector & stageRadix, std::vector & stageRemainder ) { _twiddles.resize(nfft); fill_twiddles( &_twiddles[0],nfft,inverse); dst = _twiddles; //factorize //start factoring out 4's, then 2's, then 3,5,7,9,... int n= nfft; int p=4; do { while (n % p) { switch (p) { case 4: p = 2; break; case 2: p = 3; break; default: p += 2; break; } if (p*p>n) p=n;// no more factors } n /= p; stageRadix.push_back(p); stageRemainder.push_back(n); }while(n>1); } std::vector _twiddles; const cpx_type twiddle(int i) { return _twiddles[i]; } }; } template > class kissfft { public: typedef T_traits traits_type; typedef typename traits_type::scalar_type scalar_type; typedef typename traits_type::cpx_type cpx_type; kissfft(int nfft,bool inverse,const traits_type & traits=traits_type() ) :_nfft(nfft),_inverse(inverse),_traits(traits) { _traits.prepare(_twiddles, _nfft,_inverse ,_stageRadix, _stageRemainder); } void transform(const cpx_type * src , cpx_type * dst) { kf_work(0, dst, src, 1,1); } private: void kf_work( int stage,cpx_type * Fout, const cpx_type * f, size_t fstride,size_t in_stride) { int p = _stageRadix[stage]; int m = _stageRemainder[stage]; cpx_type * Fout_beg = Fout; cpx_type * Fout_end = Fout + p*m; if (m==1) { do{ *Fout = *f; f += fstride*in_stride; }while(++Fout != Fout_end ); }else{ do{ // recursive call: // DFT of size m*p performed by doing // p instances of smaller DFTs of size m, // each one takes a decimated version of the input kf_work(stage+1, Fout , f, fstride*p,in_stride); f += fstride*in_stride; }while( (Fout += m) != Fout_end ); } Fout=Fout_beg; // recombine the p smaller DFTs switch (p) { case 2: kf_bfly2(Fout,fstride,m); break; case 3: kf_bfly3(Fout,fstride,m); break; case 4: kf_bfly4(Fout,fstride,m); break; case 5: kf_bfly5(Fout,fstride,m); break; default: kf_bfly_generic(Fout,fstride,m,p); break; } } // these were #define macros in the original kiss_fft void C_ADD( cpx_type & c,const cpx_type & a,const cpx_type & b) { c=a+b;} void C_MUL( cpx_type & c,const cpx_type & a,const cpx_type & b) { c=a*b;} void C_SUB( cpx_type & c,const cpx_type & a,const cpx_type & b) { c=a-b;} void C_ADDTO( cpx_type & c,const cpx_type & a) { c+=a;} void C_FIXDIV( cpx_type & ,int ) {} // NO-OP for float types scalar_type S_MUL( const scalar_type & a,const scalar_type & b) { return a*b;} scalar_type HALF_OF( const scalar_type & a) { return a*.5;} void C_MULBYSCALAR(cpx_type & c,const scalar_type & a) {c*=a;} void kf_bfly2( cpx_type * Fout, const size_t fstride, int m) { for (int k=0;kreal() - HALF_OF(scratch[3].real() ) , Fout->imag() - HALF_OF(scratch[3].imag() ) ); C_MULBYSCALAR( scratch[0] , epi3.imag() ); C_ADDTO(*Fout,scratch[3]); Fout[m2] = cpx_type( Fout[m].real() + scratch[0].imag() , Fout[m].imag() - scratch[0].real() ); C_ADDTO( Fout[m] , cpx_type( -scratch[0].imag(),scratch[0].real() ) ); ++Fout; }while(--k); } void kf_bfly5( cpx_type * Fout, const size_t fstride, const size_t m) { cpx_type *Fout0,*Fout1,*Fout2,*Fout3,*Fout4; size_t u; cpx_type scratch[13]; cpx_type * twiddles = &_twiddles[0]; cpx_type *tw; cpx_type ya,yb; ya = twiddles[fstride*m]; yb = twiddles[fstride*2*m]; Fout0=Fout; Fout1=Fout0+m; Fout2=Fout0+2*m; Fout3=Fout0+3*m; Fout4=Fout0+4*m; tw=twiddles; for ( u=0; u=Norig) twidx-=Norig; C_MUL(t,scratchbuf[q] , twiddles[twidx] ); C_ADDTO( Fout[ k ] ,t); } k += m; } } } int _nfft; bool _inverse; std::vector _twiddles; std::vector _stageRadix; std::vector _stageRemainder; traits_type _traits; std::vector scratchbuf; }; #endif openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/imagebufalgo_opencv.cpp0000644000175000017500000002307713151711064024410 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// \file /// Implementation of ImageBufAlgo algorithms related to OpenCV. /// These are nonfunctional if OpenCV is not found at build time. #ifdef USE_OPENCV #include #include #include #endif #include #include #include #include #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/sysutil.h" OIIO_NAMESPACE_BEGIN bool ImageBufAlgo::from_IplImage (ImageBuf &dst, const IplImage *ipl, TypeDesc convert) { if (! ipl) { DASSERT (0 && "ImageBufAlgo::fromIplImage called with NULL ipl"); dst.error ("Passed NULL source IplImage"); return false; } #ifdef USE_OPENCV TypeDesc srcformat; switch (ipl->depth) { case int(IPL_DEPTH_8U) : srcformat = TypeDesc::UINT8; break; case int(IPL_DEPTH_8S) : srcformat = TypeDesc::INT8; break; case int(IPL_DEPTH_16U) : srcformat = TypeDesc::UINT16; break; case int(IPL_DEPTH_16S) : srcformat = TypeDesc::INT16; break; case int(IPL_DEPTH_32F) : srcformat = TypeDesc::FLOAT; break; case int(IPL_DEPTH_64F) : srcformat = TypeDesc::DOUBLE; break; default: DASSERT (0 && "unknown IplImage type"); dst.error ("Unsupported IplImage depth %d", (int)ipl->depth); return false; } TypeDesc dstformat = (convert != TypeDesc::UNKNOWN) ? convert : srcformat; ImageSpec spec (ipl->width, ipl->height, ipl->nChannels, dstformat); // N.B. The OpenCV headers say that ipl->alphaChannel, // ipl->colorModel, and ipl->channelSeq are ignored by OpenCV. if (ipl->dataOrder != IPL_DATA_ORDER_PIXEL) { // We don't handle separate color channels, and OpenCV doesn't either dst.error ("Unsupported IplImage data order %d", (int)ipl->dataOrder); return false; } dst.reset (dst.name(), spec); size_t pixelsize = srcformat.size()*spec.nchannels; // Account for the origin in the line step size, to end up with the // standard OIIO origin-at-upper-left: size_t linestep = ipl->origin ? -ipl->widthStep : ipl->widthStep; // Block copy and convert convert_image (spec.nchannels, spec.width, spec.height, 1, ipl->imageData, srcformat, pixelsize, linestep, 0, dst.pixeladdr(0,0), dstformat, spec.pixel_bytes(), spec.scanline_bytes(), 0); // FIXME - honor dataOrder. I'm not sure if it is ever used by // OpenCV. Fix when it becomes a problem. // OpenCV uses BGR ordering // FIXME: what do they do with alpha? if (spec.nchannels >= 3) { float pixel[4]; for (int y = 0; y < spec.height; ++y) { for (int x = 0; x < spec.width; ++x) { dst.getpixel (x, y, pixel, 4); float tmp = pixel[0]; pixel[0] = pixel[2]; pixel[2] = tmp; dst.setpixel (x, y, pixel, 4); } } } // FIXME -- the copy and channel swap should happen all as one loop, // probably templated by type. return true; #else dst.error ("fromIplImage not supported -- no OpenCV support at compile time"); return false; #endif } IplImage * ImageBufAlgo::to_IplImage (const ImageBuf &src) { #ifdef USE_OPENCV ImageBuf tmp = src; ImageSpec spec = tmp.spec(); // Make sure the image buffer is initialized. if (!tmp.initialized() && !tmp.read(tmp.subimage(), tmp.miplevel(), true)) { DASSERT (0 && "Could not initialize ImageBuf."); return NULL; } int dstFormat; TypeDesc dstSpecFormat; if (spec.format == TypeDesc(TypeDesc::UINT8)) { dstFormat = IPL_DEPTH_8U; dstSpecFormat = spec.format; } else if (spec.format == TypeDesc(TypeDesc::INT8)) { dstFormat = IPL_DEPTH_8S; dstSpecFormat = spec.format; } else if (spec.format == TypeDesc(TypeDesc::UINT16)) { dstFormat = IPL_DEPTH_16U; dstSpecFormat = spec.format; } else if (spec.format == TypeDesc(TypeDesc::INT16)) { dstFormat = IPL_DEPTH_16S; dstSpecFormat = spec.format; } else if (spec.format == TypeDesc(TypeDesc::HALF)) { dstFormat = IPL_DEPTH_32F; // OpenCV does not support half types. Switch to float instead. dstSpecFormat = TypeDesc(TypeDesc::FLOAT); } else if (spec.format == TypeDesc(TypeDesc::FLOAT)) { dstFormat = IPL_DEPTH_32F; dstSpecFormat = spec.format; } else if (spec.format == TypeDesc(TypeDesc::DOUBLE)) { dstFormat = IPL_DEPTH_64F; dstSpecFormat = spec.format; } else { DASSERT (0 && "Unknown data format in ImageBuf."); return NULL; } IplImage *ipl = cvCreateImage(cvSize(spec.width, spec.height), dstFormat, spec.nchannels); if (!ipl) { DASSERT (0 && "Unable to create IplImage."); return NULL; } size_t pixelsize = dstSpecFormat.size() * spec.nchannels; // Account for the origin in the line step size, to end up with the // standard OIIO origin-at-upper-left: size_t linestep = ipl->origin ? -ipl->widthStep : ipl->widthStep; bool converted = convert_image(spec.nchannels, spec.width, spec.height, 1, tmp.localpixels(), spec.format, spec.pixel_bytes(), spec.scanline_bytes(), 0, ipl->imageData, dstSpecFormat, pixelsize, linestep, 0); if (!converted) { DASSERT (0 && "convert_image failed."); cvReleaseImage(&ipl); return NULL; } // OpenCV uses BGR ordering if (spec.nchannels == 3) { cvCvtColor(ipl, ipl, CV_RGB2BGR); } else if (spec.nchannels == 4) { cvCvtColor(ipl, ipl, CV_RGBA2BGRA); } return ipl; #else return NULL; #endif } namespace { #ifdef USE_OPENCV static mutex opencv_mutex; class CameraHolder { public: CameraHolder () { } // Destructor frees all cameras ~CameraHolder () { for (camera_map::iterator i = m_cvcaps.begin(); i != m_cvcaps.end(); ++i) cvReleaseCapture (&(i->second)); } // Get the capture device, creating a new one if necessary. CvCapture * operator[] (int cameranum) { camera_map::iterator i = m_cvcaps.find (cameranum); if (i != m_cvcaps.end()) return i->second; CvCapture *cvcam = cvCreateCameraCapture (cameranum); m_cvcaps[cameranum] = cvcam; return cvcam; } private: typedef std::map camera_map; camera_map m_cvcaps; }; static CameraHolder cameras; #endif } bool ImageBufAlgo::capture_image (ImageBuf &dst, int cameranum, TypeDesc convert) { #ifdef USE_OPENCV IplImage *frame = NULL; { // This block is mutex-protected lock_guard lock (opencv_mutex); CvCapture *cvcam = cameras[cameranum]; if (! cvcam) { dst.error ("Could not create a capture camera (OpenCV error)"); return false; // failed somehow } frame = cvQueryFrame (cvcam); if (! frame) { dst.error ("Could not cvQueryFrame (OpenCV error)"); return false; // failed somehow } } time_t now; time (&now); struct tm tmtime; Sysutil::get_local_time (&now, &tmtime); std::string datetime = Strutil::format ("%4d:%02d:%02d %02d:%02d:%02d", tmtime.tm_year+1900, tmtime.tm_mon+1, tmtime.tm_mday, tmtime.tm_hour, tmtime.tm_min, tmtime.tm_sec); bool ok = ImageBufAlgo::from_IplImage (dst, frame, convert); // cvReleaseImage (&frame); // unnecessary? if (ok) dst.specmod().attribute ("DateTime", datetime); return ok; #else dst.error ("capture_image not supported -- no OpenCV support at compile time"); return false; #endif } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/imagebufalgo.cpp0000644000175000017500000011604113151711064023030 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/imagebufalgo_util.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/platform.h" #include "OpenImageIO/filter.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/refcnt.h" #include "kissfft.hh" /////////////////////////////////////////////////////////////////////////// // Guidelines for ImageBufAlgo functions: // // * Signature will always be: // bool function (ImageBuf &R /* result */, // const ImageBuf &A, ...other input images..., // ...other parameters... // ROI roi = ROI::All(), // int nthreads = 0); // * The ROI should restrict the operation to those pixels (and channels) // specified. Default ROI::All() means perform the operation on all // pixel in R's data window. // * It's ok to omit ROI and threads from the few functions that // (a) can't possibly be parallelized, and (b) do not make sense to // apply to anything less than the entire image. // * Be sure to clamp the channel range to those actually used. // * If R is initialized, do not change any pixels outside the ROI. // If R is uninitialized, redefine ROI to be the union of the input // images' data windows and allocate R to be that size. // * Try to always do the "reasonable thing" rather than be too brittle. // * For errors (where there is no "reasonable thing"), set R's error // condition using R.error() with R.error() and return false. // * Always use IB::Iterators/ConstIterator, NEVER use getpixel/setpixel. // * Use the iterator Black or Clamp wrap modes to avoid lots of special // cases inside the pixel loops. // * Use OIIO_DISPATCH_* macros to call type-specialized templated // implemenations. It is permissible to use OIIO_DISPATCH_COMMON_TYPES_* // to tame the cross-product of types, especially for binary functions // (A,B inputs as well as R output). /////////////////////////////////////////////////////////////////////////// OIIO_NAMESPACE_BEGIN bool ImageBufAlgo::IBAprep (ROI &roi, ImageBuf *dst, const ImageBuf *A, const ImageBuf *B, const ImageBuf *C, ImageSpec *force_spec, int prepflags) { if ((A && !A->initialized()) || (B && !B->initialized()) || (C && !C->initialized())) { if (dst) dst->error ("Uninitialized input image"); return false; } int minchans, maxchans; if (dst || A || B || C) { minchans = 10000; maxchans = 1; if (dst && dst->initialized()) { minchans = std::min (minchans, dst->spec().nchannels); maxchans = std::max (maxchans, dst->spec().nchannels); } if (A && A->initialized()) { minchans = std::min (minchans, A->spec().nchannels); maxchans = std::max (maxchans, A->spec().nchannels); } if (B && B->initialized()) { minchans = std::min (minchans, B->spec().nchannels); maxchans = std::max (maxchans, B->spec().nchannels); } if (C && C->initialized()) { minchans = std::min (minchans, C->spec().nchannels); maxchans = std::max (maxchans, C->spec().nchannels); } } else { minchans = maxchans = 1; } if (dst->initialized()) { // Valid destination image. Just need to worry about ROI. if (roi.defined()) { // Shrink-wrap ROI to the destination (including chend) roi = roi_intersection (roi, get_roi(dst->spec())); } else { // No ROI? Set it to all of dst's pixel window. roi = get_roi (dst->spec()); } // If the dst is initialized but is a cached image, we'll need // to fully read it into allocated memory so that we're able // to write to it subsequently. dst->make_writeable (true); } else { // Not an initialized destination image! ASSERT ((A || roi.defined()) && "ImageBufAlgo without any guess about region of interest"); ROI full_roi; if (! roi.defined()) { // No ROI -- make it the union of the pixel regions of the inputs roi = A->roi(); full_roi = A->roi_full(); if (B) { roi = roi_union (roi, B->roi()); full_roi = roi_union (full_roi, B->roi_full()); } if (C) { roi = roi_union (roi, C->roi()); full_roi = roi_union (full_roi, C->roi_full()); } } else { if (A) { roi.chend = std::min (roi.chend, A->nchannels()); if (! (prepflags & IBAprep_NO_COPY_ROI_FULL)) full_roi = A->roi_full(); } else { full_roi = roi; } } // Now we allocate space for dst. Give it A's spec, but adjust // the dimensions to match the ROI. ImageSpec spec; if (A) { // If there's an input image, give dst A's spec (with // modifications detailed below...) if (force_spec) { spec = *force_spec; } else { // If dst is uninitialized and no force_spec was supplied, // make it like A, but having number of channels as large as // any of the inputs. spec = A->spec(); if (prepflags & IBAprep_MINIMIZE_NCHANNELS) spec.nchannels = minchans; else spec.nchannels = maxchans; } // For multiple inputs, if they aren't the same data type, punt and // allocate a float buffer. If the user wanted something else, // they should have pre-allocated dst with their desired format. if ((B && A->spec().format != B->spec().format) || (prepflags & IBAprep_DST_FLOAT_PIXELS)) spec.set_format (TypeDesc::FLOAT); if (C && (A->spec().format != C->spec().format || B->spec().format != C->spec().format)) spec.set_format (TypeDesc::FLOAT); // No good can come from automatically polluting an ImageBuf // with some other ImageBuf's tile sizes. spec.tile_width = 0; spec.tile_height = 0; spec.tile_depth = 0; } else if (force_spec) { spec = *force_spec; } else { spec.set_format (TypeDesc::FLOAT); spec.nchannels = roi.chend; spec.default_channel_names (); } // Set the image dimensions based on ROI. set_roi (spec, roi); if (full_roi.defined()) set_roi_full (spec, full_roi); else set_roi_full (spec, roi); if (prepflags & IBAprep_NO_COPY_METADATA) spec.extra_attribs.clear(); else if (! (prepflags & IBAprep_COPY_ALL_METADATA)) { // Since we're altering pixels, be sure that any existing SHA // hash of dst's pixel values is erased. spec.erase_attribute ("oiio:SHA-1"); static boost::regex regex_sha ("SHA-1=[[:xdigit:]]*[ ]*"); std::string desc = spec.get_string_attribute ("ImageDescription"); if (desc.size()) spec.attribute ("ImageDescription", boost::regex_replace (desc, regex_sha, "")); } dst->reset (spec); // If we just allocated more channels than the caller will write, // clear the extra channels. if (prepflags & IBAprep_CLAMP_MUTUAL_NCHANNELS) roi.chend = std::min (roi.chend, minchans); roi.chend = std::min (roi.chend, spec.nchannels); if (roi.chbegin > 0) { ROI r = roi; r.chbegin = 0; r.chend = roi.chbegin; ImageBufAlgo::zero (*dst, r, 1); } if (roi.chend < dst->nchannels()) { ROI r = roi; r.chbegin = roi.chend; r.chend = dst->nchannels(); ImageBufAlgo::zero (*dst, r, 1); } } if (prepflags & IBAprep_CLAMP_MUTUAL_NCHANNELS) roi.chend = std::min (roi.chend, minchans); roi.chend = std::min (roi.chend, maxchans); if (prepflags & IBAprep_REQUIRE_ALPHA) { if (dst->spec().alpha_channel < 0 || (A && A->spec().alpha_channel < 0) || (B && B->spec().alpha_channel < 0) || (C && C->spec().alpha_channel < 0)) { dst->error ("images must have alpha channels"); return false; } } if (prepflags & IBAprep_REQUIRE_Z) { if (dst->spec().z_channel < 0 || (A && A->spec().z_channel < 0) || (B && B->spec().z_channel < 0) || (C && C->spec().z_channel < 0)) { dst->error ("images must have depth channels"); return false; } } if (prepflags & IBAprep_REQUIRE_SAME_NCHANNELS) { int n = dst->spec().nchannels; if ((A && A->spec().nchannels != n) || (B && B->spec().nchannels != n) || (C && C->spec().nchannels != n)) { dst->error ("images must have the same number of channels"); return false; } } if (prepflags & IBAprep_NO_SUPPORT_VOLUME) { if (dst->spec().depth > 1 || (A && A->spec().depth > 1) || (B && B->spec().depth > 1) || (C && C->spec().depth > 1)) { dst->error ("volumes not supported"); return false; } } if ((dst && dst->deep()) || (A && A->deep()) || (B && B->deep()) || (C && C->deep())) { // At least one image is deep if (! (prepflags & IBAprep_SUPPORT_DEEP)) { // Error if the operation doesn't support deep images dst->error ("deep images not supported"); return false; } if (! (prepflags & IBAprep_DEEP_MIXED)) { // Error if not all images are deep if ((dst && !dst->deep()) || (A && !A->deep()) || (B && !B->deep()) || (C && !C->deep())) { dst->error ("mixed deep & flat images not supported"); return false; } } } return true; } /// Given data types a and b, return a type that is a best guess for one /// that can handle both without any loss of range or precision. TypeDesc::BASETYPE ImageBufAlgo::type_merge (TypeDesc::BASETYPE a, TypeDesc::BASETYPE b) { // Same type already? done. if (a == b) return a; if (a == TypeDesc::UNKNOWN) return b; if (b == TypeDesc::UNKNOWN) return a; // Canonicalize so a's size (in bytes) is >= b's size in bytes. This // unclutters remaining cases. if (TypeDesc(a).size() < TypeDesc(b).size()) std::swap (a, b); // Double or float trump anything else if (a == TypeDesc::DOUBLE || a == TypeDesc::FLOAT) return a; if (a == TypeDesc::UINT32 && (b == TypeDesc::UINT16 || b == TypeDesc::UINT8)) return a; if (a == TypeDesc::INT32 && (b == TypeDesc::INT16 || b == TypeDesc::UINT16 || b == TypeDesc::INT8 || b == TypeDesc::UINT8)) return a; if ((a == TypeDesc::UINT16 || a == TypeDesc::HALF) && b == TypeDesc::UINT8) return a; if ((a == TypeDesc::INT16 || a == TypeDesc::HALF) && (b == TypeDesc::INT8 || b == TypeDesc::UINT8)) return a; // Out of common cases. For all remaining edge cases, punt and say that // we prefer float. return TypeDesc::FLOAT; } template static bool convolve_ (ImageBuf &dst, const ImageBuf &src, const ImageBuf &kernel, bool normalize, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(convolve_, OIIO::ref(dst), OIIO::cref(src), OIIO::cref(kernel), normalize, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case ASSERT (kernel.spec().format == TypeDesc::FLOAT && kernel.localpixels() && "kernel should be float and in local memory"); ROI kroi = kernel.roi(); int kchans = kernel.nchannels(); float scale = 1.0f; if (normalize) { scale = 0.0f; for (ImageBuf::ConstIterator k (kernel); ! k.done(); ++k) scale += k[0]; scale = 1.0f / scale; } float *sum = ALLOCA (float, roi.chend); // Final case: handle full generality ImageBuf::Iterator d (dst, roi); ImageBuf::ConstIterator s (src, roi, ImageBuf::WrapClamp); for ( ; ! d.done(); ++d) { for (int c = roi.chbegin; c < roi.chend; ++c) sum[c] = 0.0f; const float *k = (const float *)kernel.localpixels(); s.rerange (d.x() + kroi.xbegin, d.x() + kroi.xend, d.y() + kroi.ybegin, d.y() + kroi.yend, d.z() + kroi.zbegin, d.z() + kroi.zend, ImageBuf::WrapClamp); for ( ; ! s.done(); ++s, k += kchans) { for (int c = roi.chbegin; c < roi.chend; ++c) sum[c] += k[0] * s[c]; } for (int c = roi.chbegin; c < roi.chend; ++c) d[c] = scale * sum[c]; } return true; } bool ImageBufAlgo::convolve (ImageBuf &dst, const ImageBuf &src, const ImageBuf &kernel, bool normalize, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &src, IBAprep_REQUIRE_SAME_NCHANNELS)) return false; bool ok; // Ensure that the kernel is float and in local memory const ImageBuf *K = &kernel; ImageBuf Ktmp; if (kernel.spec().format != TypeDesc::FLOAT || ! kernel.localpixels()) { Ktmp.copy (kernel, TypeDesc::FLOAT); K = &Ktmp; } OIIO_DISPATCH_COMMON_TYPES2 (ok, "convolve", convolve_, dst.spec().format, src.spec().format, dst, src, *K, normalize, roi, nthreads); return ok; } inline float binomial (int n, int k) { float p = 1; for (int i = 1; i <= k; ++i) p *= float(n - (k-i)) / i; return p; } bool ImageBufAlgo::make_kernel (ImageBuf &dst, string_view name, float width, float height, float depth, bool normalize) { int w = std::max (1, (int)ceilf(width)); int h = std::max (1, (int)ceilf(height)); int d = std::max (1, (int)ceilf(depth)); // Round up size to odd w |= 1; h |= 1; d |= 1; ImageSpec spec (w, h, 1 /*channels*/, TypeDesc::FLOAT); spec.depth = d; spec.x = -w/2; spec.y = -h/2; spec.z = -d/2; spec.full_x = spec.x; spec.full_y = spec.y; spec.full_z = spec.z; spec.full_width = spec.width; spec.full_height = spec.height; spec.full_depth = spec.depth; dst.reset (spec); if (Filter2D *filter = Filter2D::create (name, width, height)) { // Named continuous filter from filter.h for (ImageBuf::Iterator p (dst); ! p.done(); ++p) p[0] = (*filter)((float)p.x(), (float)p.y()); delete filter; } else if (name == "binomial") { // Binomial filter float *wfilter = ALLOCA (float, width); for (int i = 0; i < width; ++i) wfilter[i] = binomial (width-1, i); float *hfilter = (height == width) ? wfilter : ALLOCA (float, height); if (height != width) for (int i = 0; i < height; ++i) hfilter[i] = binomial (height-1, i); float *dfilter = ALLOCA (float, depth); if (depth == 1) dfilter[0] = 1; else for (int i = 0; i < depth; ++i) dfilter[i] = binomial (depth-1, i); for (ImageBuf::Iterator p (dst); ! p.done(); ++p) p[0] = wfilter[p.x()-spec.x] * hfilter[p.y()-spec.y] * dfilter[p.z()-spec.z]; } else if (Strutil::iequals (name, "laplacian") && w == 3 && h == 3 && d == 1) { const float vals[9] = { 0, 1, 0, 1, -4, 1, 0, 1, 0 }; dst.set_pixels (dst.roi(), TypeDesc::FLOAT, vals, sizeof(float), h*sizeof(float)); normalize = false; // sums to zero, so don't normalize it */ } else { // No filter -- make a box float val = normalize ? 1.0f / ((w*h*d)) : 1.0f; for (ImageBuf::Iterator p (dst); ! p.done(); ++p) p[0] = val; dst.error ("Unknown kernel \"%s\" %gx%g", name, width, height); return false; } if (normalize) { float sum = 0; for (ImageBuf::Iterator p (dst); ! p.done(); ++p) sum += p[0]; if (sum != 0.0f) /* don't normalize a 0-sum kernel */ for (ImageBuf::Iterator p (dst); ! p.done(); ++p) p[0] = p[0] / sum; } return true; } // Helper function for unsharp mask to perform the thresholding static bool threshold_to_zero (ImageBuf &dst, float threshold, ROI roi, int nthreads) { ASSERT (dst.spec().format.basetype == TypeDesc::FLOAT); if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(threshold_to_zero, OIIO::ref(dst), threshold, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case for (ImageBuf::Iterator p (dst, roi); ! p.done(); ++p) for (int c = roi.chbegin; c < roi.chend; ++c) if (fabsf(p[c]) < threshold) p[c] = 0.0f; return true; } bool ImageBufAlgo::unsharp_mask (ImageBuf &dst, const ImageBuf &src, string_view kernel, float width, float contrast, float threshold, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &src, IBAprep_REQUIRE_SAME_NCHANNELS | IBAprep_NO_SUPPORT_VOLUME)) return false; // Blur the source image, store in Blurry ImageSpec BlurrySpec = src.spec(); BlurrySpec.set_format (TypeDesc::FLOAT); // force float ImageBuf Blurry (BlurrySpec); if (kernel == "median") { median_filter (Blurry, src, ceilf(width), 0, roi, nthreads); } else { ImageBuf K; if (! make_kernel (K, kernel, width, width)) { dst.error ("%s", K.geterror()); return false; } if (! convolve (Blurry, src, K, true, roi, nthreads)) { dst.error ("%s", Blurry.geterror()); return false; } } // Compute the difference between the source image and the blurry // version. (We store it in the same buffer we used for the difference // image.) ImageBuf &Diff (Blurry); bool ok = sub (Diff, src, Blurry, roi, nthreads); if (ok && threshold > 0.0f) ok = threshold_to_zero (Diff, threshold, roi, nthreads); // Scale the difference image by the contrast if (ok) ok = mul (Diff, Diff, contrast, roi, nthreads); if (! ok) { dst.error ("%s", Diff.geterror()); return false; } // Add the scaled difference to the original, to get the final answer ok = add (dst, src, Diff, roi, nthreads); return ok; } bool ImageBufAlgo::laplacian (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &src, IBAprep_REQUIRE_SAME_NCHANNELS | IBAprep_NO_SUPPORT_VOLUME)) return false; ImageBuf K; if (! make_kernel (K, "laplacian", 3, 3)) { dst.error ("%s", K.geterror()); return false; } // K.write ("K.exr"); bool ok = convolve (dst, src, K, false, roi, nthreads); return ok; } template static bool median_filter_impl (ImageBuf &R, const ImageBuf &A, int width, int height, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(median_filter_impl, OIIO::ref(R), OIIO::cref(A), width, height, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } if (width < 1) width = 1; if (height < 1) height = width; int w_2 = std::max (1, width/2); int h_2 = std::max (1, height/2); int windowsize = width*height; int nchannels = R.nchannels(); float **chans = OIIO_ALLOCA (float*, nchannels); for (int c = 0; c < nchannels; ++c) chans[c] = OIIO_ALLOCA (float, windowsize); ImageBuf::ConstIterator a (A, roi); for (ImageBuf::Iterator r (R, roi); !r.done(); ++r) { a.rerange (r.x()-w_2, r.x()-w_2+width, r.y()-h_2, r.y()-h_2+height, r.z(), r.z()+1, ImageBuf::WrapClamp); int n = 0; for ( ; ! a.done(); ++a) { if (a.exists()) { for (int c = 0; c < nchannels; ++c) chans[c][n] = a[c]; ++n; } } if (n) { int mid = n/2; for (int c = 0; c < nchannels; ++c) { std::sort (chans[c]+0, chans[c]+n); r[c] = chans[c][mid]; } } else { for (int c = 0; c < nchannels; ++c) r[c] = 0.0f; } } return true; } bool ImageBufAlgo::median_filter (ImageBuf &dst, const ImageBuf &src, int width, int height, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &src, IBAprep_REQUIRE_SAME_NCHANNELS | IBAprep_NO_SUPPORT_VOLUME)) return false; bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "median_filter", median_filter_impl, dst.spec().format, src.spec().format, dst, src, width, height, roi, nthreads); return ok; } enum MorphOp { MorphDilate, MorphErode }; template static bool morph_impl (ImageBuf &R, const ImageBuf &A, int width, int height, MorphOp op, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(morph_impl, OIIO::ref(R), OIIO::cref(A), width, height, op, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } if (width < 1) width = 1; if (height < 1) height = width; int w_2 = std::max (1, width/2); int h_2 = std::max (1, height/2); int nchannels = R.nchannels(); float *vals = OIIO_ALLOCA (float, nchannels); ImageBuf::ConstIterator a (A, roi); for (ImageBuf::Iterator r (R, roi); !r.done(); ++r) { a.rerange (r.x()-w_2, r.x()-w_2+width, r.y()-h_2, r.y()-h_2+height, r.z(), r.z()+1, ImageBuf::WrapClamp); if (op == MorphDilate) { for (int c = 0; c < nchannels; ++c) vals[c] = -std::numeric_limits::max(); for ( ; ! a.done(); ++a) { if (a.exists()) { for (int c = 0; c < nchannels; ++c) vals[c] = std::max(vals[c], a[c]); } } } else if (op == MorphErode) { for (int c = 0; c < nchannels; ++c) vals[c] = std::numeric_limits::max(); for ( ; ! a.done(); ++a) { if (a.exists()) { for (int c = 0; c < nchannels; ++c) vals[c] = std::min(vals[c], a[c]); } } } else { ASSERT (0 && "Unknown morphological operator"); } for (int c = 0; c < nchannels; ++c) r[c] = vals[c]; } return true; } bool ImageBufAlgo::dilate (ImageBuf &dst, const ImageBuf &src, int width, int height, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &src, IBAprep_REQUIRE_SAME_NCHANNELS | IBAprep_NO_SUPPORT_VOLUME)) return false; bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "dilate", morph_impl, dst.spec().format, src.spec().format, dst, src, width, height, MorphDilate, roi, nthreads); return ok; } bool ImageBufAlgo::erode (ImageBuf &dst, const ImageBuf &src, int width, int height, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &src, IBAprep_REQUIRE_SAME_NCHANNELS | IBAprep_NO_SUPPORT_VOLUME)) return false; bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "erode", morph_impl, dst.spec().format, src.spec().format, dst, src, width, height, MorphErode, roi, nthreads); return ok; } // Helper function: fft of the horizontal rows static bool hfft_ (ImageBuf &dst, const ImageBuf &src, bool inverse, bool unitary, ROI roi, int nthreads) { ASSERT (dst.spec().format.basetype == TypeDesc::FLOAT && src.spec().format.basetype == TypeDesc::FLOAT && dst.spec().nchannels == 2 && src.spec().nchannels == 2 && dst.roi() == src.roi() && (dst.storage() == ImageBuf::LOCALBUFFER || dst.storage() == ImageBuf::APPBUFFER) && (src.storage() == ImageBuf::LOCALBUFFER || src.storage() == ImageBuf::APPBUFFER) ); if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind (hfft_, OIIO::ref(dst), OIIO::cref(src), inverse, unitary, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case int width = roi.width(); float rescale = sqrtf (1.0f / width); kissfft F (width, inverse); for (int z = roi.zbegin; z < roi.zend; ++z) { for (int y = roi.ybegin; y < roi.yend; ++y) { std::complex *s, *d; s = (std::complex *)src.pixeladdr(roi.xbegin, y, z); d = (std::complex *)dst.pixeladdr(roi.xbegin, y, z); F.transform (s, d); if (unitary) for (int x = 0; x < width; ++x) d[x] *= rescale; } } return true; } bool ImageBufAlgo::fft (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (src.spec().depth > 1) { dst.error ("ImageBufAlgo::fft does not support volume images"); return false; } if (! roi.defined()) roi = roi_union (get_roi (src.spec()), get_roi_full (src.spec())); roi.chend = roi.chbegin+1; // One channel only // Construct a spec that describes the result ImageSpec spec = src.spec(); spec.width = spec.full_width = roi.width(); spec.height = spec.full_height = roi.height(); spec.depth = spec.full_depth = 1; spec.x = spec.full_x = 0; spec.y = spec.full_y = 0; spec.z = spec.full_z = 0; spec.set_format (TypeDesc::FLOAT); spec.channelformats.clear(); spec.nchannels = 2; spec.channelnames.clear(); spec.channelnames.push_back ("real"); spec.channelnames.push_back ("imag"); // And a spec that describes the transposed intermediate ImageSpec specT = spec; std::swap (specT.width, specT.height); std::swap (specT.full_width, specT.full_height); // Resize dst dst.reset (dst.name(), spec); // Copy src to a 2-channel (for "complex") float buffer ImageBuf A (spec); if (src.nchannels() < 2) { // If we're pasting fewer than 2 channels, zero out channel 1. ROI r = roi; r.chbegin = 1; r.chend = 2; zero (A, r); } if (! ImageBufAlgo::paste (A, 0, 0, 0, 0, src, roi, nthreads)) { dst.error ("%s", A.geterror()); return false; } // FFT the rows (into temp buffer B). ImageBuf B (spec); hfft_ (B, A, false /*inverse*/, true /*unitary*/, get_roi(B.spec()), nthreads); // Transpose and shift back to A A.clear (); ImageBufAlgo::transpose (A, B, ROI::All(), nthreads); // FFT what was originally the columns (back to B) B.reset (specT); hfft_ (B, A, false /*inverse*/, true /*unitary*/, get_roi(A.spec()), nthreads); // Transpose again, into the dest ImageBufAlgo::transpose (dst, B, ROI::All(), nthreads); return true; } bool ImageBufAlgo::ifft (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (src.nchannels() != 2 || src.spec().format != TypeDesc::FLOAT) { dst.error ("ifft can only be done on 2-channel float images"); return false; } if (src.spec().depth > 1) { dst.error ("ImageBufAlgo::ifft does not support volume images"); return false; } if (! roi.defined()) roi = roi_union (get_roi (src.spec()), get_roi_full (src.spec())); roi.chbegin = 0; roi.chend = 2; // Construct a spec that describes the result ImageSpec spec = src.spec(); spec.width = spec.full_width = roi.width(); spec.height = spec.full_height = roi.height(); spec.depth = spec.full_depth = 1; spec.x = spec.full_x = 0; spec.y = spec.full_y = 0; spec.z = spec.full_z = 0; spec.set_format (TypeDesc::FLOAT); spec.channelformats.clear(); spec.nchannels = 2; spec.channelnames.clear(); spec.channelnames.push_back ("real"); spec.channelnames.push_back ("imag"); // Inverse FFT the rows (into temp buffer B). ImageBuf B (spec); hfft_ (B, src, true /*inverse*/, true /*unitary*/, get_roi(B.spec()), nthreads); // Transpose and shift back to A ImageBuf A; ImageBufAlgo::transpose (A, B, ROI::All(), nthreads); // Inverse FFT what was originally the columns (back to B) B.reset (A.spec()); hfft_ (B, A, true /*inverse*/, true /*unitary*/, get_roi(A.spec()), nthreads); // Transpose again, into the dst, in the process throw out the // imaginary part and go back to a single (real) channel. spec.nchannels = 1; spec.channelnames.clear (); spec.channelnames.push_back ("R"); dst.reset (dst.name(), spec); ROI Broi = get_roi(B.spec()); Broi.chend = 1; ImageBufAlgo::transpose (dst, B, Broi, nthreads); return true; } template static bool polar_to_complex_impl (ImageBuf &R, const ImageBuf &A, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(polar_to_complex_impl, OIIO::ref(R), OIIO::cref(A), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } ImageBuf::ConstIterator a (A, roi); for (ImageBuf::Iterator r (R, roi); !r.done(); ++r, ++a) { float amp = a[0]; float phase = a[1]; float sine, cosine; sincos (phase, &sine, &cosine); r[0] = amp * cosine; r[1] = amp * sine; } return true; } template static bool complex_to_polar_impl (ImageBuf &R, const ImageBuf &A, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(complex_to_polar_impl, OIIO::ref(R), OIIO::cref(A), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } ImageBuf::ConstIterator a (A, roi); for (ImageBuf::Iterator r (R, roi); !r.done(); ++r, ++a) { float real = a[0]; float imag = a[1]; float phase = std::atan2 (imag, real); if (phase < 0.0f) phase += float (M_TWO_PI); r[0] = hypotf (real, imag); r[1] = phase; } return true; } bool ImageBufAlgo::polar_to_complex (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (src.nchannels() != 2) { dst.error ("polar_to_complex can only be done on 2-channel"); return false; } if (! IBAprep (roi, &dst, &src)) return false; if (dst.nchannels() != 2) { dst.error ("polar_to_complex can only be done on 2-channel"); return false; } bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "polar_to_complex", polar_to_complex_impl, dst.spec().format, src.spec().format, dst, src, roi, nthreads); return ok; } bool ImageBufAlgo::complex_to_polar (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (src.nchannels() != 2) { dst.error ("complex_to_polar can only be done on 2-channel"); return false; } if (! IBAprep (roi, &dst, &src)) return false; if (dst.nchannels() != 2) { dst.error ("complex_to_polar can only be done on 2-channel"); return false; } bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "complex_to_polar", complex_to_polar_impl, dst.spec().format, src.spec().format, dst, src, roi, nthreads); return ok; } // Helper for fillholes_pp: for any nonzero alpha pixels in dst, divide // all components by alpha. static bool divide_by_alpha (ImageBuf &dst, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(divide_by_alpha, OIIO::ref(dst), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case const ImageSpec &spec (dst.spec()); ASSERT (spec.format == TypeDesc::FLOAT); int nc = spec.nchannels; int ac = spec.alpha_channel; for (ImageBuf::Iterator d (dst, roi); ! d.done(); ++d) { float alpha = d[ac]; if (alpha != 0.0f) { for (int c = 0; c < nc; ++c) d[c] = d[c] / alpha; } } return true; } bool ImageBufAlgo::fillholes_pushpull (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &src)) return false; const ImageSpec &dstspec (dst.spec()); if (dstspec.nchannels != src.nchannels()) { dst.error ("channel number mismatch: %d vs. %d", dstspec.nchannels, src.spec().nchannels); return false; } if (dst.spec().depth > 1 || src.spec().depth > 1) { dst.error ("ImageBufAlgo::fillholes_pushpull does not support volume images"); return false; } if (dstspec.alpha_channel < 0 || dstspec.alpha_channel != src.spec().alpha_channel) { dst.error ("Must have alpha channels"); return false; } // We generate a bunch of temp images to form an image pyramid. // These give us a place to stash them and make sure they are // auto-deleted when the function exits. std::vector > pyramid; // First, make a writeable copy of the original image (converting // to float as a convenience) as the top level of the pyramid. ImageSpec topspec = src.spec(); topspec.set_format (TypeDesc::FLOAT); ImageBuf *top = new ImageBuf (topspec); paste (*top, topspec.x, topspec.y, topspec.z, 0, src); pyramid.push_back (OIIO::shared_ptr(top)); // Construct the rest of the pyramid by successive x/2 resizing and // then dividing nonzero alpha pixels by their alpha (this "spreads // out" the defined part of the image). int w = src.spec().width, h = src.spec().height; while (w > 1 || h > 1) { w = std::max (1, w/2); h = std::max (1, h/2); ImageSpec smallspec (w, h, src.nchannels(), TypeDesc::FLOAT); ImageBuf *small = new ImageBuf (smallspec); ImageBufAlgo::resize (*small, *pyramid.back(), "triangle"); divide_by_alpha (*small, get_roi(smallspec), nthreads); pyramid.push_back (OIIO::shared_ptr(small)); //debug small->save(); } // Now pull back up the pyramid by doing an alpha composite of level // i over a resized level i+1, thus filling in the alpha holes. By // time we get to the top, pixels whose original alpha are // unchanged, those with alpha < 1 are replaced by the blended // colors of the higher pyramid levels. for (int i = (int)pyramid.size()-2; i >= 0; --i) { ImageBuf &big(*pyramid[i]), &small(*pyramid[i+1]); ImageBuf blowup (big.spec()); ImageBufAlgo::resize (blowup, small, "triangle"); ImageBufAlgo::over (big, big, blowup); //debug big.save (Strutil::format ("after%d.exr", i)); } // Now copy the completed base layer of the pyramid back to the // original requested output. paste (dst, dstspec.x, dstspec.y, dstspec.z, 0, *pyramid[0]); return true; } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/imageinput.cpp0000644000175000017500000007555213151711064022563 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/deepdata.h" #include "imageio_pvt.h" #include OIIO_NAMESPACE_BEGIN using namespace pvt; ImageInput::ImageInput () : m_threads(0) { } ImageInput::~ImageInput () { } // Default implementation of valid_file: try to do a full open. If it // succeeds, it's the right kind of file. We assume that most plugins // will override this with something smarter and much less expensive, // like reading just the first few bytes of the file to check for magic // numbers. bool ImageInput::valid_file (const std::string &filename) const { ImageSpec tmpspec; bool ok = const_cast(this)->open (filename, tmpspec); if (ok) const_cast(this)->close (); return ok; } ImageInput * ImageInput::open (const std::string &filename, const ImageSpec *config) { if (config == NULL) { // Without config, this is really just a call to create-with-open. return ImageInput::create (filename, true, std::string()); } // With config, create without open, then try to open with config. ImageInput *in = ImageInput::create (filename, false, std::string()); if (! in) return NULL; // create() failed ImageSpec newspec; if (in->open (filename, newspec, *config)) return in; // creted fine, opened fine, return it // The open failed. Transfer the error from 'in' to the global OIIO // error, delete the ImageInput we allocated, and return NULL. std::string err = in->geterror(); if (err.size()) pvt::error ("%s", err.c_str()); delete in; return NULL; } bool ImageInput::read_scanline (int y, int z, TypeDesc format, void *data, stride_t xstride) { // native_pixel_bytes is the size of a pixel in the FILE, including // the per-channel format. stride_t native_pixel_bytes = (stride_t) m_spec.pixel_bytes (true); // perchanfile is true if the file has different per-channel formats bool perchanfile = m_spec.channelformats.size(); // native_data is true if the user asking for data in the native format bool native_data = (format == TypeDesc::UNKNOWN || (format == m_spec.format && !perchanfile)); if (native_data && xstride == AutoStride) xstride = native_pixel_bytes; else m_spec.auto_stride (xstride, format, m_spec.nchannels); // Do the strides indicate that the data area is contiguous? bool contiguous = (native_data && xstride == native_pixel_bytes) || (!native_data && xstride == (stride_t)m_spec.pixel_bytes(false)); // If user's format and strides are set up to accept the native data // layout, read the scanline directly into the user's buffer. if (native_data && contiguous) return read_native_scanline (y, z, data); // Complex case -- either changing data type or stride int scanline_values = m_spec.width * m_spec.nchannels; unsigned char *buf = (unsigned char *) alloca (m_spec.scanline_bytes(true)); bool ok = read_native_scanline (y, z, buf); if (! ok) return false; if (! perchanfile) { // No per-channel formats -- do the conversion in one shot ok = contiguous ? convert_types (m_spec.format, buf, format, data, scanline_values) : convert_image (m_spec.nchannels, m_spec.width, 1, 1, buf, m_spec.format, AutoStride, AutoStride, AutoStride, data, format, xstride, AutoStride, AutoStride); } else { // Per-channel formats -- have to convert/copy channels individually ASSERT (m_spec.channelformats.size() == (size_t)m_spec.nchannels); size_t offset = 0; for (int c = 0; ok && c < m_spec.nchannels; ++c) { TypeDesc chanformat = m_spec.channelformats[c]; ok = convert_image (1 /* channels */, m_spec.width, 1, 1, buf+offset, chanformat, native_pixel_bytes, AutoStride, AutoStride, (char *)data + c*format.size(), format, xstride, AutoStride, AutoStride); offset += chanformat.size (); } } if (! ok) error ("ImageInput::read_scanline : no support for format %s", m_spec.format.c_str()); return ok; } bool ImageInput::read_scanlines (int ybegin, int yend, int z, TypeDesc format, void *data, stride_t xstride, stride_t ystride) { return read_scanlines (ybegin, yend, z, 0, m_spec.nchannels, format, data, xstride, ystride); } bool ImageInput::read_scanlines (int ybegin, int yend, int z, int chbegin, int chend, TypeDesc format, void *data, stride_t xstride, stride_t ystride) { chend = clamp (chend, chbegin+1, m_spec.nchannels); int nchans = chend - chbegin; yend = std::min (yend, spec().y+spec().height); size_t native_pixel_bytes = m_spec.pixel_bytes (chbegin, chend, true); imagesize_t native_scanline_bytes = clamped_mult64 ((imagesize_t)m_spec.width, (imagesize_t)native_pixel_bytes); bool native = (format == TypeDesc::UNKNOWN); size_t pixel_bytes = native ? native_pixel_bytes : format.size()*nchans; if (native && xstride == AutoStride) xstride = pixel_bytes; stride_t zstride = AutoStride; m_spec.auto_stride (xstride, ystride, zstride, format, nchans, m_spec.width, m_spec.height); bool contiguous = (xstride == (stride_t) native_pixel_bytes && ystride == (stride_t) native_scanline_bytes); // If user's format and strides are set up to accept the native data // layout, read the scanlines directly into the user's buffer. bool rightformat = (format == TypeDesc::UNKNOWN) || (format == m_spec.format && m_spec.channelformats.empty()); if (rightformat && contiguous) { if (chbegin == 0 && chend == m_spec.nchannels) return read_native_scanlines (ybegin, yend, z, data); else return read_native_scanlines (ybegin, yend, z, chbegin, chend, data); } // No such luck. Read scanlines in chunks. const imagesize_t limit = 16*1024*1024; // Allocate 16 MB, or 1 scanline int chunk = std::max (1, int(limit / native_scanline_bytes)); boost::scoped_array buf (new char [chunk * native_scanline_bytes]); bool ok = true; int scanline_values = m_spec.width * nchans; for (; ok && ybegin < yend; ybegin += chunk) { int y1 = std::min (ybegin+chunk, yend); ok &= read_native_scanlines (ybegin, y1, z, chbegin, chend, &buf[0]); if (! ok) break; int nscanlines = y1 - ybegin; int chunkvalues = scanline_values * nscanlines; if (m_spec.channelformats.empty()) { // No per-channel formats -- do the conversion in one shot if (contiguous) { ok = convert_types (m_spec.format, &buf[0], format, data, chunkvalues); } else { ok = parallel_convert_image (nchans, m_spec.width, nscanlines, 1, &buf[0], m_spec.format, AutoStride, AutoStride, AutoStride, data, format, xstride, ystride, zstride, -1 /*alpha*/, -1 /*z*/, threads()); } } else { // Per-channel formats -- have to convert/copy channels individually size_t offset = 0; for (int c = 0; ok && c < nchans; ++c) { TypeDesc chanformat = m_spec.channelformats[c+chbegin]; ok = convert_image (1 /* channels */, m_spec.width, nscanlines, 1, &buf[offset], chanformat, native_pixel_bytes, AutoStride, AutoStride, (char *)data + c*format.size(), format, xstride, ystride, zstride); offset += chanformat.size (); } } if (! ok) error ("ImageInput::read_scanlines : no support for format %s", m_spec.format.c_str()); data = (char *)data + ystride*nscanlines; } return ok; } bool ImageInput::read_native_scanlines (int ybegin, int yend, int z, void *data) { // Base class implementation of read_native_scanlines just repeatedly // calls read_native_scanline, which is supplied by every plugin. // Only the hardcore ones will overload read_native_scanlines with // their own implementation. size_t ystride = m_spec.scanline_bytes (true); yend = std::min (yend, spec().y+spec().height); for (int y = ybegin; y < yend; ++y) { bool ok = read_native_scanline (y, z, data); if (! ok) return false; data = (char *)data + ystride; } return true; } bool ImageInput::read_native_scanlines (int ybegin, int yend, int z, int chbegin, int chend, void *data) { // All-channel case just reduces to the simpler read_native_scanlines. if (chbegin == 0 && chend >= m_spec.nchannels) return read_native_scanlines (ybegin, yend, z, data); // Base class implementation of read_native_scanlines (with channel // subset) just calls read_native_scanlines (all channels), and // copies the appropriate subset. size_t prefix_bytes = m_spec.pixel_bytes (0,chbegin,true); size_t subset_bytes = m_spec.pixel_bytes (chbegin,chend,true); size_t subset_ystride = m_spec.width * subset_bytes; size_t native_pixel_bytes = m_spec.pixel_bytes (true); size_t native_ystride = m_spec.width * native_pixel_bytes; boost::scoped_array buf (new char [native_ystride]); yend = std::min (yend, spec().y+spec().height); for (int y = ybegin; y < yend; ++y) { bool ok = read_native_scanline (y, z, &buf[0]); if (! ok) return false; for (int x = 0; x < m_spec.width; ++x) memcpy ((char *)data + subset_bytes*x, &buf[prefix_bytes+native_pixel_bytes*x], subset_bytes); data = (char *)data + subset_ystride; } return true; } bool ImageInput::read_tile (int x, int y, int z, TypeDesc format, void *data, stride_t xstride, stride_t ystride, stride_t zstride) { if (! m_spec.tile_width || ((x-m_spec.x) % m_spec.tile_width) != 0 || ((y-m_spec.y) % m_spec.tile_height) != 0 || ((z-m_spec.z) % m_spec.tile_depth) != 0) return false; // coordinates are not a tile corner // native_pixel_bytes is the size of a pixel in the FILE, including // the per-channel format. stride_t native_pixel_bytes = (stride_t) m_spec.pixel_bytes (true); // perchanfile is true if the file has different per-channel formats bool perchanfile = m_spec.channelformats.size(); // native_data is true if the user asking for data in the native format bool native_data = (format == TypeDesc::UNKNOWN || (format == m_spec.format && !perchanfile)); if (format == TypeDesc::UNKNOWN && xstride == AutoStride) xstride = native_pixel_bytes; m_spec.auto_stride (xstride, ystride, zstride, format, m_spec.nchannels, m_spec.tile_width, m_spec.tile_height); // Do the strides indicate that the data area is contiguous? bool contiguous = (native_data && xstride == native_pixel_bytes) || (!native_data && xstride == (stride_t)m_spec.pixel_bytes(false)); contiguous &= (ystride == xstride*m_spec.tile_width && (zstride == ystride*m_spec.tile_height || zstride == 0)); // If user's format and strides are set up to accept the native data // layout, read the tile directly into the user's buffer. if (native_data && contiguous) return read_native_tile (x, y, z, data); // Simple case // Complex case -- either changing data type or stride size_t tile_values = (size_t)m_spec.tile_pixels() * m_spec.nchannels; boost::scoped_array buf (new char [m_spec.tile_bytes(true)]); bool ok = read_native_tile (x, y, z, &buf[0]); if (! ok) return false; if (! perchanfile) { // No per-channel formats -- do the conversion in one shot ok = contiguous ? convert_types (m_spec.format, &buf[0], format, data, tile_values) : convert_image (m_spec.nchannels, m_spec.tile_width, m_spec.tile_height, m_spec.tile_depth, &buf[0], m_spec.format, AutoStride, AutoStride, AutoStride, data, format, xstride, ystride, zstride); } else { // Per-channel formats -- have to convert/copy channels individually ASSERT (m_spec.channelformats.size() == (size_t)m_spec.nchannels); size_t offset = 0; for (int c = 0; c < m_spec.nchannels; ++c) { TypeDesc chanformat = m_spec.channelformats[c]; ok = convert_image (1 /* channels */, m_spec.tile_width, m_spec.tile_height, m_spec.tile_depth, &buf[offset], chanformat, native_pixel_bytes, AutoStride, AutoStride, (char *)data + c*format.size(), format, xstride, AutoStride, AutoStride); offset += chanformat.size (); } } if (! ok) error ("ImageInput::read_tile : no support for format %s", m_spec.format.c_str()); return ok; } bool ImageInput::read_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, void *data, stride_t xstride, stride_t ystride, stride_t zstride) { return read_tiles (xbegin, xend, ybegin, yend, zbegin, zend, 0, m_spec.nchannels, format, data, xstride, ystride, zstride); } bool ImageInput::read_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc format, void *data, stride_t xstride, stride_t ystride, stride_t zstride) { if (! m_spec.valid_tile_range (xbegin, xend, ybegin, yend, zbegin, zend)) return false; chend = clamp (chend, chbegin+1, m_spec.nchannels); int nchans = chend - chbegin; // native_pixel_bytes is the size of a pixel in the FILE, including // the per-channel format. stride_t native_pixel_bytes = (stride_t) m_spec.pixel_bytes (chbegin, chend, true); // perchanfile is true if the file has different per-channel formats bool perchanfile = m_spec.channelformats.size(); // native_data is true if the user asking for data in the native format bool native_data = (format == TypeDesc::UNKNOWN || (format == m_spec.format && !perchanfile)); if (format == TypeDesc::UNKNOWN && xstride == AutoStride) xstride = native_pixel_bytes; m_spec.auto_stride (xstride, ystride, zstride, format, nchans, xend-xbegin, yend-ybegin); // Do the strides indicate that the data area is contiguous? bool contiguous = (native_data && xstride == native_pixel_bytes) || (!native_data && xstride == (stride_t)m_spec.pixel_bytes(false)); contiguous &= (ystride == xstride*(xend-xbegin) && (zstride == ystride*(yend-ybegin) || (zend-zbegin) <= 1)); int nxtiles = (xend - xbegin + m_spec.tile_width - 1) / m_spec.tile_width; int nytiles = (yend - ybegin + m_spec.tile_height - 1) / m_spec.tile_height; int nztiles = (zend - zbegin + m_spec.tile_depth - 1) / m_spec.tile_depth; // If user's format and strides are set up to accept the native data // layout, and we're asking for a whole number of tiles (no partial // tiles at the edges), then read the tile directly into the user's // buffer. if (native_data && contiguous && (xend-xbegin) == nxtiles*m_spec.tile_width && (yend-ybegin) == nytiles*m_spec.tile_height && (zend-zbegin) == nztiles*m_spec.tile_depth) { if (chbegin == 0 && chend == m_spec.nchannels) return read_native_tiles (xbegin, xend, ybegin, yend, zbegin, zend, data); // Simple case else return read_native_tiles (xbegin, xend, ybegin, yend, zbegin, zend, chbegin, chend, data); } // No such luck. Just punt and read tiles individually. bool ok = true; stride_t pixelsize = native_data ? native_pixel_bytes : (format.size() * nchans); stride_t full_pixelsize = native_data ? m_spec.pixel_bytes(true) : (format.size() * m_spec.nchannels); stride_t full_tilewidthbytes = full_pixelsize * m_spec.tile_width; stride_t full_tilewhbytes = full_tilewidthbytes * m_spec.tile_height; stride_t full_tilebytes = full_tilewhbytes * m_spec.tile_depth; size_t prefix_bytes = native_data ? m_spec.pixel_bytes (0,chbegin,true) : format.size() * chbegin; std::vector buf; for (int z = zbegin; z < zend; z += std::max(1,m_spec.tile_depth)) { int zd = std::min (zend-z, m_spec.tile_depth); for (int y = ybegin; y < yend; y += m_spec.tile_height) { char *tilestart = ((char *)data + (z-zbegin)*zstride + (y-ybegin)*ystride); int yh = std::min (yend-y, m_spec.tile_height); for (int x = xbegin; ok && x < xend; x += m_spec.tile_width) { int xw = std::min (xend-x, m_spec.tile_width); // Full tiles are read directly into the user buffer, // but partial tiles (such as at the image edge) or // partial channel subsets are read into a buffer and // then copied. if (xw == m_spec.tile_width && yh == m_spec.tile_height && zd == m_spec.tile_depth && !perchanfile && chbegin == 0 && chend == m_spec.nchannels) { // Full tile, either native data or not needing // per-tile data format conversion. ok &= read_tile (x, y, z, format, tilestart, xstride, ystride, zstride); if (! ok) return false; } else { buf.resize (full_tilebytes); ok &= read_tile (x, y, z, format, &buf[0], full_pixelsize, full_tilewidthbytes, full_tilewhbytes); if (ok) copy_image (nchans, xw, yh, zd, &buf[prefix_bytes], pixelsize, full_pixelsize, full_tilewidthbytes, full_tilewhbytes, tilestart, xstride, ystride, zstride); // N.B. It looks like read_tiles doesn't handle the // per-channel data types case fully, but it does! // The call to read_tile() above handles the case of // per-channel data types, converting to to desired // format, so all we have to do on our own is the // copy_image. } tilestart += m_spec.tile_width * xstride; } if (! ok) break; } } if (! ok) error ("ImageInput::read_tiles : no support for format %s", m_spec.format.c_str()); return ok; } bool ImageInput::read_native_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, void *data) { if (! m_spec.valid_tile_range (xbegin, xend, ybegin, yend, zbegin, zend)) return false; // Base class implementation of read_native_tiles just repeatedly // calls read_native_tile, which is supplied by every plugin that // supports tiles. Only the hardcore ones will overload // read_native_tiles with their own implementation. stride_t pixel_bytes = (stride_t) m_spec.pixel_bytes (true); stride_t tileystride = pixel_bytes * m_spec.tile_width; stride_t tilezstride = tileystride * m_spec.tile_height; stride_t ystride = (xend-xbegin) * pixel_bytes; stride_t zstride = (yend-ybegin) * ystride; boost::scoped_array pels (new char [m_spec.tile_bytes(true)]); 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) { bool ok = read_native_tile (x, y, z, &pels[0]); if (! ok) return false; copy_image (m_spec.nchannels, m_spec.tile_width, m_spec.tile_height, m_spec.tile_depth, &pels[0], size_t(pixel_bytes), pixel_bytes, tileystride, tilezstride, (char *)data+ (z-zbegin)*zstride + (y-ybegin)*ystride + (x-xbegin)*pixel_bytes, pixel_bytes, ystride, zstride); } } } return true; } bool ImageInput::read_native_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, void *data) { chend = clamp (chend, chbegin+1, m_spec.nchannels); int nchans = chend - chbegin; // All-channel case just reduces to the simpler read_native_scanlines. if (chbegin == 0 && chend >= m_spec.nchannels) return read_native_tiles (xbegin, xend, ybegin, yend, zbegin, zend, data); if (! m_spec.valid_tile_range (xbegin, xend, ybegin, yend, zbegin, zend)) return false; // Base class implementation of read_native_tiles just repeatedly // calls read_native_tile, which is supplied by every plugin that // supports tiles. Only the hardcore ones will overload // read_native_tiles with their own implementation. stride_t native_pixel_bytes = (stride_t) m_spec.pixel_bytes (true); stride_t native_tileystride = native_pixel_bytes * m_spec.tile_width; stride_t native_tilezstride = native_tileystride * m_spec.tile_height; size_t prefix_bytes = m_spec.pixel_bytes (0,chbegin,true); size_t subset_bytes = m_spec.pixel_bytes (chbegin,chend,true); stride_t subset_ystride = (xend-xbegin) * subset_bytes; stride_t subset_zstride = (yend-ybegin) * subset_ystride; boost::scoped_array pels (new char [m_spec.tile_bytes(true)]); 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) { bool ok = read_native_tile (x, y, z, &pels[0]); if (! ok) return false; copy_image (nchans, m_spec.tile_width, m_spec.tile_height, m_spec.tile_depth, &pels[prefix_bytes], subset_bytes, native_pixel_bytes, native_tileystride, native_tilezstride, (char *)data+ (z-zbegin)*subset_zstride + (y-ybegin)*subset_ystride + (x-xbegin)*subset_bytes, subset_bytes, subset_ystride, subset_zstride); } } } return true; } bool ImageInput::read_image (TypeDesc format, void *data, stride_t xstride, stride_t ystride, stride_t zstride, ProgressCallback progress_callback, void *progress_callback_data) { return read_image (0, -1, format, data, xstride, ystride, zstride, progress_callback, progress_callback_data); } bool ImageInput::read_image (int chbegin, int chend, TypeDesc format, void *data, stride_t xstride, stride_t ystride, stride_t zstride, ProgressCallback progress_callback, void *progress_callback_data) { if (chend < 0) chend = m_spec.nchannels; chend = clamp (chend, chbegin+1, m_spec.nchannels); int nchans = chend - chbegin; bool native = (format == TypeDesc::UNKNOWN); stride_t pixel_bytes = native ? (stride_t) m_spec.pixel_bytes (chbegin, chend, native) : (stride_t) (format.size()*nchans); if (native && xstride == AutoStride) xstride = pixel_bytes; m_spec.auto_stride (xstride, ystride, zstride, format, nchans, m_spec.width, m_spec.height); bool ok = true; if (progress_callback) if (progress_callback (progress_callback_data, 0.0f)) return ok; if (m_spec.tile_width) { // Tiled image for (int z = 0; z < m_spec.depth; z += m_spec.tile_depth) { for (int y = 0; y < m_spec.height && ok; y += m_spec.tile_height) { ok &= read_tiles (m_spec.x, m_spec.x+m_spec.width, y+m_spec.y, std::min (y+m_spec.y+m_spec.tile_height, m_spec.y+m_spec.height), z+m_spec.z, std::min (z+m_spec.z+m_spec.tile_depth, m_spec.z+m_spec.depth), chbegin, chend, format, (char *)data + z*zstride + y*ystride, xstride, ystride, zstride); if (progress_callback && progress_callback (progress_callback_data, (float)y/m_spec.height)) return ok; } } } else { // Scanline image -- rely on read_scanlines, in chunks of oiio_read_chunk int read_chunk = oiio_read_chunk; if (!read_chunk) { read_chunk = m_spec.height; } for (int z = 0; z < m_spec.depth; ++z) for (int y = 0; y < m_spec.height && ok; y += read_chunk) { int yend = std::min (y+m_spec.y+read_chunk, m_spec.y+m_spec.height); ok &= read_scanlines (y+m_spec.y, yend, z+m_spec.z, chbegin, chend, format, (char *)data + z*zstride + y*ystride, xstride, ystride); if (progress_callback) if (progress_callback (progress_callback_data, (float)y/m_spec.height)) return ok; } } if (progress_callback) progress_callback (progress_callback_data, 1.0f); return ok; } bool ImageInput::read_native_deep_scanlines (int ybegin, int yend, int z, int chbegin, int chend, DeepData &deepdata) { return false; // default: doesn't support deep images } bool ImageInput::read_native_deep_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, DeepData &deepdata) { return false; // default: doesn't support deep images } bool ImageInput::read_native_deep_image (DeepData &deepdata) { if (m_spec.depth > 1) { error ("read_native_deep_image is not supported for volume (3D) images."); return false; // FIXME? - not implementing 3D deep images for now. The only // format that supports deep images at this time is OpenEXR, and // it doesn't support volumes. } if (m_spec.tile_width) { // Tiled image return read_native_deep_tiles (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, 0, m_spec.nchannels, deepdata); } else { // Scanline image return read_native_deep_scanlines (m_spec.y, m_spec.y+m_spec.height, 0, 0, m_spec.nchannels, deepdata); } } int ImageInput::send_to_input (const char *format, ...) { // FIXME -- I can't remember how this is supposed to work return 0; } int ImageInput::send_to_client (const char *format, ...) { // FIXME -- I can't remember how this is supposed to work return 0; } void ImageInput::append_error (const std::string& message) const { ASSERT (m_errmessage.size() < 1024*1024*16 && "Accumulated error messages > 16MB. Try checking return codes!"); if (m_errmessage.size()) m_errmessage += '\n'; m_errmessage += message; } bool ImageInput::read_native_tile (int x, int y, int z, void * data) { return false; } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/imageioplugin.cpp0000644000175000017500000005166613151711064023252 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/plugin.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/imageio.h" #include "imageio_pvt.h" OIIO_NAMESPACE_BEGIN using namespace pvt; typedef std::map InputPluginMap; typedef std::map OutputPluginMap; typedef const char* (*PluginLibVersionFunc) (); namespace { // Map format name to ImageInput creation static InputPluginMap input_formats; // Map format name to ImageOutput creation static OutputPluginMap output_formats; // Map file extension to ImageInput creation static InputPluginMap input_extensions; // Map file extension to ImageOutput creation static OutputPluginMap output_extensions; // Map format name to plugin handle static std::map plugin_handles; // Map format name to full path static std::map plugin_filepaths; // Map format name to underlying implementation library static std::map format_library_versions; static std::string pattern = Strutil::format (".imageio.%s", Plugin::plugin_extension()); inline void add_if_missing (std::vector &vec, const std::string &val) { if (std::find (vec.begin(), vec.end(), val) == vec.end()) vec.push_back (val); } } // anon namespace /// Register the input and output 'create' routine and list of file /// extensions for a particular format. void declare_imageio_format (const std::string &format_name, ImageInput::Creator input_creator, const char **input_extensions, ImageOutput::Creator output_creator, const char **output_extensions, const char *lib_version) { std::vector all_extensions; // Look for input creator and list of supported extensions if (input_creator) { if (input_formats.find(format_name) != input_formats.end()) input_formats[format_name] = input_creator; std::string extsym = format_name + "_input_extensions"; for (const char **e = input_extensions; e && *e; ++e) { std::string ext (*e); Strutil::to_lower (ext); if (input_formats.find(ext) == input_formats.end()) { input_formats[ext] = input_creator; add_if_missing (all_extensions, ext); } } } // Look for output creator and list of supported extensions if (output_creator) { if (output_formats.find(format_name) != output_formats.end()) output_formats[format_name] = output_creator; for (const char **e = output_extensions; e && *e; ++e) { std::string ext (*e); Strutil::to_lower (ext); if (output_formats.find(ext) == output_formats.end()) { output_formats[ext] = output_creator; add_if_missing (all_extensions, ext); } } } // Add the name to the master list of format_names, and extensions to // their master list. recursive_lock_guard lock (pvt::imageio_mutex); if (format_list.length()) format_list += std::string(","); format_list += format_name; if (extension_list.length()) extension_list += std::string(";"); extension_list += format_name + std::string(":"); extension_list += Strutil::join(all_extensions, ","); if (lib_version) { format_library_versions[format_name] = lib_version; if (library_list.length()) library_list += std::string(";"); library_list += Strutil::format ("%s:%s", format_name, lib_version); // std::cout << format_name << ": " << lib_version << "\n"; } } static void catalog_plugin (const std::string &format_name, const std::string &plugin_fullpath) { // Remember the plugin std::map::const_iterator found_path; found_path = plugin_filepaths.find (format_name); if (found_path != plugin_filepaths.end()) { // Hey, we already have an entry for this format if (found_path->second == plugin_fullpath) { // It's ok if they're both the same file; just skip it. return; } OIIO::debugmsg ("OpenImageIO WARNING: %s had multiple plugins:\n" "\t\"%s\"\n as well as\n\t\"%s\"\n" " Ignoring all but the first one.\n", format_name, found_path->second, plugin_fullpath); return; } Plugin::Handle handle = Plugin::open (plugin_fullpath); if (! handle) { return; } std::string version_function = format_name + "_imageio_version"; int *plugin_version = (int *) Plugin::getsym (handle, version_function.c_str()); if (! plugin_version || *plugin_version != OIIO_PLUGIN_VERSION) { Plugin::close (handle); return; } std::string lib_version_function = format_name + "_imageio_library_version"; PluginLibVersionFunc plugin_lib_version = (PluginLibVersionFunc) Plugin::getsym (handle, lib_version_function.c_str()); // Add the filepath and handle to the master lists plugin_filepaths[format_name] = plugin_fullpath; plugin_handles[format_name] = handle; ImageInput::Creator input_creator = (ImageInput::Creator) Plugin::getsym (handle, format_name+"_input_imageio_create"); const char **input_extensions = (const char **) Plugin::getsym (handle, format_name+"_input_extensions"); ImageOutput::Creator output_creator = (ImageOutput::Creator) Plugin::getsym (handle, format_name+"_output_imageio_create"); const char **output_extensions = (const char **) Plugin::getsym (handle, format_name+"_output_extensions"); if (input_creator || output_creator) declare_imageio_format (format_name, input_creator, input_extensions, output_creator, output_extensions, plugin_lib_version ? plugin_lib_version() : NULL); else Plugin::close (handle); // not useful } #ifdef EMBED_PLUGINS // Make extern declarations for the input and output create routines and // list of file extensions, for the standard plugins that come with OIIO. // These won't be used unless EMBED_PLUGINS is defined. Use the PLUGENTRY // macro to make the declaration compact and easy to read. #define PLUGENTRY(name) \ ImageInput *name ## _input_imageio_create (); \ ImageOutput *name ## _output_imageio_create (); \ extern const char *name ## _output_extensions[]; \ extern const char *name ## _input_extensions[]; \ extern const char *name ## _imageio_library_version(); PLUGENTRY (bmp); PLUGENTRY (cineon); PLUGENTRY (dds); PLUGENTRY (dpx); PLUGENTRY (ffmpeg); PLUGENTRY (field3d); PLUGENTRY (fits); PLUGENTRY (gif); PLUGENTRY (hdr); PLUGENTRY (ico); PLUGENTRY (iff); PLUGENTRY (jpeg); PLUGENTRY (jpeg2000); PLUGENTRY (openexr); PLUGENTRY (png); PLUGENTRY (pnm); PLUGENTRY (psd); PLUGENTRY (ptex); PLUGENTRY (raw); PLUGENTRY (rla); PLUGENTRY (sgi); PLUGENTRY (socket); PLUGENTRY (softimage); PLUGENTRY (tiff); PLUGENTRY (targa); PLUGENTRY (webp); PLUGENTRY (zfile); #endif // defined(EMBED_PLUGINS) namespace { /// Add all the built-in plugins, those compiled right into libOpenImageIO, /// to the catalogs. This does nothing if EMBED_PLUGINS is not defined, /// in which case they'll be registered only when read from external /// DSO/DLL's. static void catalog_builtin_plugins () { #ifdef EMBED_PLUGINS // Use DECLAREPLUG macro to make this more compact and easy to read. #define DECLAREPLUG(name) \ declare_imageio_format (#name, \ (ImageInput::Creator) name ## _input_imageio_create, \ name ## _input_extensions, \ (ImageOutput::Creator) name ## _output_imageio_create, \ name ## _output_extensions, \ name ## _imageio_library_version()) DECLAREPLUG (bmp); DECLAREPLUG (cineon); DECLAREPLUG (dds); DECLAREPLUG (dpx); #ifdef USE_FFMPEG DECLAREPLUG (ffmpeg); #endif #ifdef USE_FIELD3D DECLAREPLUG (field3d); #endif DECLAREPLUG (fits); #ifdef USE_GIF DECLAREPLUG (gif); #endif DECLAREPLUG (hdr); DECLAREPLUG (ico); DECLAREPLUG (iff); DECLAREPLUG (jpeg); #ifdef USE_OPENJPEG DECLAREPLUG (jpeg2000); #endif DECLAREPLUG (openexr); DECLAREPLUG (png); DECLAREPLUG (pnm); DECLAREPLUG (psd); #ifdef USE_PTEX DECLAREPLUG (ptex); #endif #ifdef USE_LIBRAW DECLAREPLUG (raw); #endif DECLAREPLUG (rla); DECLAREPLUG (sgi); #ifdef USE_BOOST_ASIO DECLAREPLUG (socket); #endif DECLAREPLUG (softimage); DECLAREPLUG (tiff); DECLAREPLUG (targa); #ifdef USE_WEBP DECLAREPLUG (webp); #endif DECLAREPLUG (zfile); #endif } } // anon namespace end static void append_if_env_exists (std::string &searchpath, const char *env, bool prepend=false) { const char *path = getenv (env); if (path && *path) { std::string newpath = path; if (searchpath.length()) { if (prepend) newpath = newpath + ':' + searchpath; else newpath = searchpath + ':' + newpath; } searchpath = newpath; } } /// Look at ALL imageio plugins in the searchpath and add them to the /// catalog. This routine is not reentrant and should only be called /// by a routine that is holding a lock on imageio_mutex. void pvt::catalog_all_plugins (std::string searchpath) { catalog_builtin_plugins (); append_if_env_exists (searchpath, "OIIO_LIBRARY_PATH", true); #ifdef __APPLE__ append_if_env_exists (searchpath, "DYLD_LIBRARY_PATH"); #endif #if defined(__linux__) || defined(__FreeBSD__) append_if_env_exists (searchpath, "LD_LIBRARY_PATH"); #endif size_t patlen = pattern.length(); std::vector dirs; Filesystem::searchpath_split (searchpath, dirs, true); BOOST_FOREACH (const std::string &dir, dirs) { std::vector dir_entries; Filesystem::get_directory_entries (dir, dir_entries); BOOST_FOREACH (const std::string &full_filename, dir_entries) { std::string leaf = Filesystem::filename (full_filename); size_t found = leaf.find (pattern); if (found != std::string::npos && (found == leaf.length() - patlen)) { std::string pluginname (leaf.begin(), leaf.begin() + leaf.length() - patlen); catalog_plugin (pluginname, full_filename); } } } } ImageOutput * ImageOutput::create (const std::string &filename, const std::string &plugin_searchpath) { if (filename.empty()) { // Can't even guess if no filename given pvt::error ("ImageOutput::create() called with no filename"); return NULL; } // Extract the file extension from the filename (without the leading dot) std::string format = Filesystem::extension (filename, false); if (format.empty()) { // If the file had no extension, maybe it was itself the format name format = filename; } ImageOutput::Creator create_function = NULL; { // scope the lock: recursive_lock_guard lock (imageio_mutex); // Ensure thread safety // See if it's already in the table. If not, scan all plugins we can // find to populate the table. Strutil::to_lower (format); OutputPluginMap::const_iterator found = output_formats.find (format); if (found == output_formats.end()) { catalog_all_plugins (plugin_searchpath.size() ? plugin_searchpath : pvt::plugin_searchpath.string()); found = output_formats.find (format); } if (found != output_formats.end()) { create_function = found->second; } else { if (output_formats.empty()) { // This error is so fundamental, we echo it to stderr in // case the app is too dumb to do so. const char *msg = "ImageOutput::create() could not find any ImageOutput plugins! Perhaps you need to set OIIO_LIBRARY_PATH.\n"; fprintf (stderr, "%s", msg); pvt::error ("%s", msg); } else pvt::error ("OpenImageIO could not find a format writer for \"%s\". " "Is it a file format that OpenImageIO doesn't know about?\n", filename.c_str()); return NULL; } } ASSERT (create_function != NULL); ImageOutput *out = NULL; try { out = (ImageOutput *) create_function(); } catch (...) { // Safety in case the ctr throws an exception } return out; } void ImageOutput::destroy (ImageOutput *x) { delete x; } ImageInput * ImageInput::create (const std::string &filename, const std::string &plugin_searchpath) { return create (filename, false, plugin_searchpath); } ImageInput * ImageInput::create (const std::string &filename, bool do_open, const std::string &plugin_searchpath) { if (filename.empty()) { // Can't even guess if no filename given pvt::error ("ImageInput::create() called with no filename"); return NULL; } // Extract the file extension from the filename (without the leading dot) std::string format = Filesystem::extension (filename, false); if (format.empty()) { // If the file had no extension, maybe it was itself the format name format = filename; } ImageInput::Creator create_function = NULL; { // scope the lock: recursive_lock_guard lock (imageio_mutex); // Ensure thread safety // See if it's already in the table. If not, scan all plugins we can // find to populate the table. Strutil::to_lower (format); InputPluginMap::const_iterator found = input_formats.find (format); if (found == input_formats.end()) { catalog_all_plugins (plugin_searchpath.size() ? plugin_searchpath : pvt::plugin_searchpath.string()); found = input_formats.find (format); } if (found != input_formats.end()) create_function = found->second; } // Remember which prototypes we've already tried, so we don't double dip. std::vector formats_tried; std::string specific_error; if (create_function) { if (filename != format) { // If given a full filename, double-check that our guess // based on the extension actually works. You never know // when somebody will have an incorrectly-named file, let's // deal with it robustly. formats_tried.push_back (create_function); ImageInput *in = (ImageInput *)create_function(); if (! do_open && in && in->valid_file(filename)) { // Special case: we don't need to return the file // already opened, and this ImageInput says that the // file is the right type. return in; } ImageSpec tmpspec; bool ok = in && in->open (filename, tmpspec); if (ok) { // It worked if (! do_open) in->close (); return in; } else { // Oops, it failed. Apparently, this file can't be // opened with this II. Clear create_function to force // the code below to check every plugin we know. create_function = NULL; if (in) specific_error = in->geterror(); } delete in; } } if (! create_function) { // If a plugin can't be found that was explicitly designated for // this extension, then just try every one we find and see if // any will open the file. Pass it a configuration request that // includes a "nowait" option so that it returns immediately if // it's a plugin that might wait for an event, like a socket that // doesn't yet exist). ImageSpec config; config.attribute ("nowait", (int)1); recursive_lock_guard lock (imageio_mutex); // Ensure thread safety for (InputPluginMap::const_iterator plugin = input_formats.begin(); plugin != input_formats.end(); ++plugin) { // If we already tried this create function, don't do it again if (std::find (formats_tried.begin(), formats_tried.end(), plugin->second) != formats_tried.end()) continue; formats_tried.push_back (plugin->second); // remember ImageSpec tmpspec; ImageInput *in = NULL; try { in = plugin->second(); } catch (...) { // Safety in case the ctr throws an exception } if (! in) continue; if (! do_open && ! in->valid_file(filename)) { // Since we didn't need to open it, we just checked whether // it was a valid file, and it's not. Try the next one. delete in; continue; } // We either need to open it, or we already know it appears // to be a file of the right type. bool ok = in->open(filename, tmpspec, config); if (ok) { if (! do_open) in->close (); return in; } delete in; } } if (create_function == NULL) { recursive_lock_guard lock (imageio_mutex); // Ensure thread safety if (input_formats.empty()) { // This error is so fundamental, we echo it to stderr in // case the app is too dumb to do so. const char *msg = "ImageInput::create() could not find any ImageInput plugins!\n" " Perhaps you need to set OIIO_LIBRARY_PATH.\n"; fprintf (stderr, "%s", msg); pvt::error ("%s", msg); } else if (! specific_error.empty()) { // Pass along any specific error message we got from our // best guess of the format. pvt::error ("%s", specific_error); } else if (Filesystem::exists (filename)) pvt::error ("OpenImageIO could not find a format reader for \"%s\". " "Is it a file format that OpenImageIO doesn't know about?\n", filename.c_str()); else pvt::error ("Image \"%s\" does not exist. Also, it is not the name of an image format that OpenImageIO recognizes.\n", filename.c_str()); return NULL; } return (ImageInput *) create_function(); } void ImageInput::destroy (ImageInput *x) { delete x; } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/imagespec_test.cpp0000644000175000017500000002361013151711064023401 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "OpenImageIO/imageio.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/unittest.h" OIIO_NAMESPACE_USING; void test_imagespec_pixels () { std::cout << "test_imagespec_pixels\n"; // images with dimensions > 2^16 (65536) on a side have > 2^32 pixels const long long WIDTH = 456789; const long long HEIGHT = 345678; const long long CHANNELS=3; const long long BYTES_IN_FLOAT = 4; ImageSpec spec (WIDTH, HEIGHT, CHANNELS, TypeDesc::FLOAT); std::cout << "sizeof (int) = " << sizeof (int) << std::endl; std::cout << "sizeof (long) = " << sizeof (long) << std::endl; std::cout << "sizeof (long long) = " << sizeof (long long) << std::endl; std::cout << "sizeof (size_t) = " << sizeof (size_t) << std::endl; std::cout << "sizeof (imagesize_t) = " << sizeof (imagesize_t) << std::endl; std::cout << "sizeof (stride_t) = " << sizeof (stride_t) << std::endl; std::cout << "sizeof (float) = " << sizeof (float) << std::endl; OIIO_CHECK_EQUAL ((size_t)BYTES_IN_FLOAT, sizeof (float)); OIIO_CHECK_EQUAL (CHANNELS, spec.nchannels); OIIO_CHECK_EQUAL (WIDTH, spec.width); OIIO_CHECK_EQUAL (HEIGHT, spec.height); OIIO_CHECK_EQUAL (1, spec.depth); OIIO_CHECK_EQUAL (WIDTH, spec.full_width); OIIO_CHECK_EQUAL (HEIGHT, spec.full_height); OIIO_CHECK_EQUAL (1, spec.full_depth); // FIXME(nemec): uncomment after figuring out linking // OIIO_CHECK_EQUAL (TypeDesc::UINT8, spec.format); OIIO_CHECK_EQUAL ((size_t)BYTES_IN_FLOAT, spec.channel_bytes ()); OIIO_CHECK_EQUAL ((size_t)(BYTES_IN_FLOAT*CHANNELS), spec.pixel_bytes ()); OIIO_CHECK_EQUAL ((imagesize_t)(BYTES_IN_FLOAT*CHANNELS*WIDTH), spec.scanline_bytes ()); OIIO_CHECK_EQUAL ((imagesize_t)(WIDTH*HEIGHT), spec.image_pixels ()); // check that the magnitude is right (not clamped) -- should be about > 2^40 long long expected_bytes = BYTES_IN_FLOAT*CHANNELS*WIDTH*HEIGHT; // log (x) / log (2) = log2 (x) // log (2^32) / log (2) = log2 (2^32) = 32 // log (2^32) * M_LOG2E = 32 double log2_result = log ((double)expected_bytes) * M_LOG2E; OIIO_CHECK_LT (40, log2_result); OIIO_CHECK_EQUAL ((imagesize_t)expected_bytes, spec.image_bytes ()); std::cout << "expected_bytes = " << expected_bytes << ", log " << log ((double)expected_bytes) << std::endl; } static void metadata_val_test (void *data, int num_elements, TypeDesc type, std::string& val) { static ImageSpec spec; ImageIOParameter p; p.init ("name", type, num_elements, data); val = spec.metadata_val (p); } void test_imagespec_metadata_val () { std::cout << "test_imagespec_metadata_val\n"; std::string ret; int imatrix[] = {100, 200, 300, 400}; metadata_val_test (&imatrix[0], 1, TypeDesc::TypeInt, ret); OIIO_CHECK_EQUAL (ret, "100"); metadata_val_test (imatrix, sizeof (imatrix)/sizeof(int), TypeDesc::TypeInt, ret); OIIO_CHECK_EQUAL (ret, "100, 200, 300, 400"); OIIO_CHECK_NE (ret, "100, 200, 300, 400,"); float fmatrix[] = {10.12f, 200.34f, 300.11f, 400.9f}; metadata_val_test (&fmatrix[0], 1, TypeDesc::TypeFloat, ret); OIIO_CHECK_EQUAL (ret, "10.12"); metadata_val_test (fmatrix, sizeof (fmatrix) / sizeof (float), TypeDesc::TypeFloat, ret); OIIO_CHECK_EQUAL (ret, "10.12, 200.34, 300.11, 400.9"); OIIO_CHECK_NE (ret, "10, 200, 300, 400"); OIIO_CHECK_NE (ret, "10.12, 200.34, 300.11, 400.9,"); unsigned long long ullmatrix[] = {0xffffffffffffffffLL, 0xffffffffffffffffLL}; metadata_val_test (&ullmatrix, 1, TypeDesc::UINT64, ret); OIIO_CHECK_EQUAL (ret, "18446744073709551615"); metadata_val_test (&ullmatrix, sizeof (ullmatrix) / sizeof (unsigned long long), TypeDesc::UINT64, ret); OIIO_CHECK_EQUAL (ret, "18446744073709551615, 18446744073709551615"); OIIO_CHECK_NE (ret, "-1, -1"); OIIO_CHECK_NE (ret, "18446744073709551615, 18446744073709551615,"); const char* smatrix[] = {"this is \"a test\"", "this is another test"}; metadata_val_test (smatrix, 1, TypeDesc::TypeString, ret); OIIO_CHECK_EQUAL (ret, "\"this is \"a test\"\""); OIIO_CHECK_NE (ret, smatrix[0]); OIIO_CHECK_NE (ret, "\"this is \"a test\"\","); metadata_val_test (smatrix, sizeof (smatrix) / sizeof (char *), TypeDesc::TypeString, ret); OIIO_CHECK_EQUAL (ret, "\"this is \"a test\"\", \"this is another test\""); float matrix16[2][16] = {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}}; metadata_val_test (&matrix16[0], 1, TypeDesc::TypeMatrix, ret); OIIO_CHECK_EQUAL (ret, "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16"); OIIO_CHECK_NE (ret, "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16,"); metadata_val_test (matrix16, sizeof (matrix16) / (16 * sizeof (float)), TypeDesc::TypeMatrix, ret); OIIO_CHECK_EQUAL (ret, "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16, 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25"); } static void attribute_test (const std::string &data, TypeDesc type, std::string &ret) { ImageSpec spec; spec.attribute ("name", type, data); ret = spec.metadata_val (spec.extra_attribs[0]); } void test_imagespec_attribute_from_string () { std::cout << "test_imagespec_attribute_from_string\n"; TypeDesc type = TypeDesc::TypeInt; std::string ret, data, invalid_data; data = "142"; attribute_test (data, type, ret); OIIO_CHECK_EQUAL (ret, data); type = TypeDesc::TypeFloat; data = "1.23"; attribute_test (data, type, ret); OIIO_CHECK_EQUAL (ret, data); type = TypeDesc(TypeDesc::FLOAT, 5); data = "1.23, 34.23, 35.11, 99.99, 1999.99"; attribute_test (data, type, ret); OIIO_CHECK_EQUAL (ret, data); type = TypeDesc::UINT64; data = "18446744073709551615"; attribute_test (data, type, ret); OIIO_CHECK_EQUAL (ret, data); type = TypeDesc::TypeMatrix; data = "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16"; attribute_test (data, type, ret); OIIO_CHECK_EQUAL (ret, data); type = TypeDesc::TypeString; data = "foo"; attribute_test (data, type, ret); OIIO_CHECK_EQUAL (ret, "\"foo\""); } static void test_get_attribute () { std::cout << "test_get_attribute\n"; ImageSpec spec (640, 480, 4, TypeDesc::FLOAT); spec.x = 10; spec.y = 12; spec.full_x = -5; spec.full_y = -8; spec.full_width = 1024; spec.full_height = 800; spec.tile_width = 64; spec.tile_height = 32; spec.attribute ("foo", int(42)); spec.attribute ("pi", float(M_PI)); spec.attribute ("bar", "barbarbar?"); OIIO_CHECK_EQUAL (spec.get_int_attribute("width"), 640); OIIO_CHECK_EQUAL (spec.get_int_attribute("height"), 480); OIIO_CHECK_EQUAL (spec.get_int_attribute("nchannels"), 4); OIIO_CHECK_EQUAL (spec.get_int_attribute("x"), 10); OIIO_CHECK_EQUAL (spec.get_int_attribute("y"), 12); OIIO_CHECK_EQUAL (spec.get_int_attribute("full_x"), -5); OIIO_CHECK_EQUAL (spec.get_int_attribute("full_y"), -8); OIIO_CHECK_EQUAL (spec.get_int_attribute("full_width"), 1024); OIIO_CHECK_EQUAL (spec.get_int_attribute("full_height"), 800); OIIO_CHECK_EQUAL (spec.get_int_attribute("tile_width"), 64); OIIO_CHECK_EQUAL (spec.get_int_attribute("tile_height"), 32); OIIO_CHECK_EQUAL (spec.get_string_attribute("geom"), "640x480+10+12"); OIIO_CHECK_EQUAL (spec.get_string_attribute("full_geom"), "1024x800-5-8"); OIIO_CHECK_EQUAL (spec.get_int_attribute("foo"), 42); OIIO_CHECK_EQUAL (spec.get_int_attribute("pi",4), 4); // should fail int OIIO_CHECK_EQUAL (spec.get_float_attribute("pi"), float(M_PI)); OIIO_CHECK_EQUAL (spec.get_int_attribute("bar"), 0); OIIO_CHECK_EQUAL (spec.get_int_attribute("bar"), 0); OIIO_CHECK_EQUAL (spec.get_string_attribute("bar"), "barbarbar?"); OIIO_CHECK_ASSERT(spec.find_attribute("foo") != NULL); OIIO_CHECK_ASSERT(spec.find_attribute("Foo") != NULL); OIIO_CHECK_ASSERT(spec.find_attribute("Foo", TypeDesc::UNKNOWN, false) != NULL); OIIO_CHECK_ASSERT(spec.find_attribute("Foo", TypeDesc::UNKNOWN, true) == NULL); OIIO_CHECK_ASSERT(spec.find_attribute("foo", TypeDesc::INT) != NULL); OIIO_CHECK_ASSERT(spec.find_attribute("foo", TypeDesc::FLOAT) == NULL); } int main (int argc, char *argv[]) { test_imagespec_pixels (); test_imagespec_metadata_val (); test_imagespec_attribute_from_string (); test_get_attribute (); return unit_test_failures; } openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/imagebuf.cpp0000644000175000017500000020150413151711064022164 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// \file /// Implementation of ImageBuf class. #include #include #include #include #include #include "OpenImageIO/imageio.h" #include "OpenImageIO/deepdata.h" #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/imagebufalgo_util.h" #include "OpenImageIO/imagecache.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/simd.h" OIIO_NAMESPACE_BEGIN static atomic_ll IB_local_mem_current; ROI get_roi (const ImageSpec &spec) { return ROI (spec.x, spec.x + spec.width, spec.y, spec.y + spec.height, spec.z, spec.z + spec.depth, 0, spec.nchannels); } ROI get_roi_full (const ImageSpec &spec) { return ROI (spec.full_x, spec.full_x + spec.full_width, spec.full_y, spec.full_y + spec.full_height, spec.full_z, spec.full_z + spec.full_depth, 0, spec.nchannels); } void set_roi (ImageSpec &spec, const ROI &newroi) { spec.x = newroi.xbegin; spec.y = newroi.ybegin; spec.z = newroi.zbegin; spec.width = newroi.width(); spec.height = newroi.height(); spec.depth = newroi.depth(); } void set_roi_full (ImageSpec &spec, const ROI &newroi) { spec.full_x = newroi.xbegin; spec.full_y = newroi.ybegin; spec.full_z = newroi.zbegin; spec.full_width = newroi.width(); spec.full_height = newroi.height(); spec.full_depth = newroi.depth(); } ROI roi_union (const ROI &A, const ROI &B) { ROI R (A.defined() ? A : B); if (A.defined() && B.defined()) R = ROI (std::min (R.xbegin, B.xbegin), std::max (R.xend, B.xend), std::min (R.ybegin, B.ybegin), std::max (R.yend, B.yend), std::min (R.zbegin, B.zbegin), std::max (R.zend, B.zend), std::min (R.chbegin, B.chbegin), std::max (R.chend, B.chend)); return R; } ROI roi_intersection (const ROI &A, const ROI &B) { ROI R (A.defined() ? A : B); if (A.defined() && B.defined()) R = ROI (std::max (R.xbegin, B.xbegin), std::min (R.xend, B.xend), std::max (R.ybegin, B.ybegin), std::min (R.yend, B.yend), std::max (R.zbegin, B.zbegin), std::min (R.zend, B.zend), std::max (R.chbegin, B.chbegin), std::min (R.chend, B.chend)); return R; } // Expansion of the opaque type that hides all the ImageBuf implementation // detail. class ImageBufImpl { public: ImageBufImpl (string_view filename, int subimage, int miplevel, ImageCache *imagecache=NULL, const ImageSpec *spec=NULL, void *buffer=NULL, const ImageSpec *config = NULL); ImageBufImpl (const ImageBufImpl &src); ~ImageBufImpl (); void clear (); void reset (string_view name, int subimage, int miplevel, ImageCache *imagecache, const ImageSpec *config); void reset (string_view name, const ImageSpec &spec); void alloc (const ImageSpec &spec); void realloc (); bool init_spec (string_view filename, int subimage, int miplevel); bool read (int subimage=0, int miplevel=0, bool force=false, TypeDesc convert=TypeDesc::UNKNOWN, ProgressCallback progress_callback=NULL, void *progress_callback_data=NULL); void copy_metadata (const ImageBufImpl &src); // Error reporting for ImageBuf: call this with printf-like // arguments. Note however that this is fully typesafe! // void error (const char *format, ...) TINYFORMAT_WRAP_FORMAT (void, error, const, std::ostringstream msg;, msg, append_error(msg.str());) void append_error (const std::string& message) const; ImageBuf::IBStorage storage () const { return m_storage; } TypeDesc pixeltype () const { validate_spec (); return m_localpixels ? m_spec.format : m_cachedpixeltype; } DeepData *deepdata () { validate_pixels(); return m_spec.deep ? &m_deepdata : NULL; } const DeepData *deepdata () const { validate_pixels(); return m_spec.deep ? &m_deepdata : NULL; } bool initialized () const { return m_spec_valid && m_storage != ImageBuf::UNINITIALIZED; } bool cachedpixels () const { return m_storage == ImageBuf::IMAGECACHE; } const void *pixeladdr (int x, int y, int z) const; void *pixeladdr (int x, int y, int z); const void *retile (int x, int y, int z, ImageCache::Tile* &tile, int &tilexbegin, int &tileybegin, int &tilezbegin, int &tilexend, bool exists, ImageBuf::WrapMode wrap) const; bool do_wrap (int &x, int &y, int &z, ImageBuf::WrapMode wrap) const; const void *blackpixel () const { validate_spec (); return &m_blackpixel[0]; } bool validate_spec () const { if (m_spec_valid) return true; if (! m_name.size()) return false; spin_lock lock (m_valid_mutex); // prevent multiple init_spec if (m_spec_valid) return true; ImageBufImpl *imp = const_cast(this); if (imp->m_current_subimage < 0) imp->m_current_subimage = 0; if (imp->m_current_miplevel < 0) imp->m_current_miplevel = 0; return imp->init_spec(m_name.string(), m_current_subimage, m_current_miplevel); } bool validate_pixels () const { if (m_pixels_valid) return true; if (! m_name.size()) return true; spin_lock lock (m_valid_mutex); // prevent multiple read() if (m_pixels_valid) return true; ImageBufImpl *imp = const_cast(this); if (imp->m_current_subimage < 0) imp->m_current_subimage = 0; if (imp->m_current_miplevel < 0) imp->m_current_miplevel = 0; return imp->read (m_current_subimage, m_current_miplevel); } const ImageSpec & spec () const { validate_spec (); return m_spec; } const ImageSpec & nativespec () const { validate_spec (); return m_nativespec; } ImageSpec & specmod () { validate_spec (); return m_spec; } void threads (int n) const { m_threads = n; } int threads () const { return m_threads; } // Allocate m_configspec if not already done void add_configspec (const ImageSpec *config=NULL) { if (! m_configspec) m_configspec.reset (config ? new ImageSpec (*config) : new ImageSpec); } // Return the index of pixel (x,y,z). If check_range is true, return // -1 for an invalid coordinate that is not within the data window. int pixelindex (int x, int y, int z, bool check_range=false) const { x -= m_spec.x; y -= m_spec.y; z -= m_spec.z; if (check_range && (x < 0 || x >= m_spec.width || y < 0 || y >= m_spec.height || z < 0 || z >= m_spec.depth)) return -1; return (z * m_spec.height + y) * m_spec.width + x; } private: ImageBuf::IBStorage m_storage; ///< Pixel storage class ustring m_name; ///< Filename of the image ustring m_fileformat; ///< File format name int m_nsubimages; ///< How many subimages are there? int m_current_subimage; ///< Current subimage we're viewing int m_current_miplevel; ///< Current miplevel we're viewing int m_nmiplevels; ///< # of MIP levels in the current subimage mutable int m_threads; ///< thread policy for this image ImageSpec m_spec; ///< Describes the image (size, etc) ImageSpec m_nativespec; ///< Describes the true native image boost::scoped_array m_pixels; ///< Pixel data, if local and we own it char *m_localpixels; ///< Pointer to local pixels mutable spin_mutex m_valid_mutex; mutable bool m_spec_valid; ///< Is the spec valid mutable bool m_pixels_valid; ///< Image is valid bool m_badfile; ///< File not found float m_pixelaspect; ///< Pixel aspect ratio of the image size_t m_pixel_bytes; size_t m_scanline_bytes; size_t m_plane_bytes; ImageCache *m_imagecache; ///< ImageCache to use TypeDesc m_cachedpixeltype; ///< Data type stored in the cache DeepData m_deepdata; ///< Deep data size_t m_allocated_size; ///< How much memory we've allocated std::vector m_blackpixel; ///< Pixel-sized zero bytes TypeDesc m_write_format; /// Format to use for write() int m_write_tile_width; int m_write_tile_height; int m_write_tile_depth; boost::scoped_ptr m_configspec; // Configuration spec mutable std::string m_err; ///< Last error message const ImageBufImpl operator= (const ImageBufImpl &src); // unimplemented friend class ImageBuf; }; ImageBufImpl::ImageBufImpl (string_view filename, int subimage, int miplevel, ImageCache *imagecache, const ImageSpec *spec, void *buffer, const ImageSpec *config) : m_storage(ImageBuf::UNINITIALIZED), m_name(filename), m_nsubimages(0), m_current_subimage(subimage), m_current_miplevel(miplevel), m_nmiplevels(0), m_threads(0), m_localpixels(NULL), m_spec_valid(false), m_pixels_valid(false), m_badfile(false), m_pixelaspect(1), m_pixel_bytes(0), m_scanline_bytes(0), m_plane_bytes(0), m_imagecache(imagecache), m_allocated_size(0), m_write_format(TypeDesc::UNKNOWN), m_write_tile_width(0), m_write_tile_height(0), m_write_tile_depth(1) { if (spec) { m_spec = *spec; m_nativespec = *spec; m_pixel_bytes = spec->pixel_bytes(); m_scanline_bytes = spec->scanline_bytes(); m_plane_bytes = clamped_mult64 (m_scanline_bytes, (imagesize_t)m_spec.height); m_blackpixel.resize (round_to_multiple (m_pixel_bytes, OIIO_SIMD_MAX_SIZE_BYTES), 0); // NB make it big enough for SSE if (buffer) { m_localpixels = (char *)buffer; m_storage = ImageBuf::APPBUFFER; m_pixels_valid = true; } else { m_storage = ImageBuf::LOCALBUFFER; } m_spec_valid = true; } else if (filename.length() > 0) { ASSERT (buffer == NULL); // If a filename was given, read the spec and set it up as an // ImageCache-backed image. Reallocate later if an explicit read() // is called to force read into a local buffer. m_configspec.reset (config ? new ImageSpec (*config) : NULL); read (subimage, miplevel); } else { ASSERT (buffer == NULL); } } ImageBufImpl::ImageBufImpl (const ImageBufImpl &src) : m_storage(src.m_storage), m_name(src.m_name), m_fileformat(src.m_fileformat), m_nsubimages(src.m_nsubimages), m_current_subimage(src.m_current_subimage), m_current_miplevel(src.m_current_miplevel), m_nmiplevels(src.m_nmiplevels), m_threads(src.m_threads), m_spec(src.m_spec), m_nativespec(src.m_nativespec), m_badfile(src.m_badfile), m_pixelaspect(src.m_pixelaspect), m_pixel_bytes(src.m_pixel_bytes), m_scanline_bytes(src.m_scanline_bytes), m_plane_bytes(src.m_plane_bytes), m_imagecache(src.m_imagecache), m_cachedpixeltype(src.m_cachedpixeltype), m_deepdata(src.m_deepdata), m_blackpixel(src.m_blackpixel), m_write_format(src.m_write_format), m_write_tile_width(src.m_write_tile_width), m_write_tile_height(src.m_write_tile_height), m_write_tile_depth(src.m_write_tile_depth) { m_spec_valid = src.m_spec_valid; m_pixels_valid = src.m_pixels_valid; m_allocated_size = src.m_localpixels ? src.spec().image_bytes() : 0; IB_local_mem_current += m_allocated_size; if (src.m_localpixels) { // Source had the image fully in memory (no cache) if (m_storage == ImageBuf::APPBUFFER) { // Source just wrapped the client app's pixels, we do the same m_localpixels = src.m_localpixels; } else { // We own our pixels -- copy from source m_pixels.reset (new char [src.m_spec.image_bytes()]); memcpy (m_pixels.get(), src.m_pixels.get(), m_spec.image_bytes()); m_localpixels = m_pixels.get(); } } else { // Source was cache-based or deep // nothing else to do m_localpixels = NULL; } if (src.m_configspec) m_configspec.reset (new ImageSpec(*src.m_configspec)); } ImageBufImpl::~ImageBufImpl () { // Do NOT destroy m_imagecache here -- either it was created // externally and passed to the ImageBuf ctr or reset() method, or // else init_spec requested the system-wide shared cache, which // does not need to be destroyed. IB_local_mem_current -= m_allocated_size; } ImageBuf::ImageBuf () : m_impl (new ImageBufImpl (std::string(), -1, -1, NULL)) { } ImageBuf::ImageBuf (string_view filename, int subimage, int miplevel, ImageCache *imagecache, const ImageSpec *config) : m_impl (new ImageBufImpl (filename, subimage, miplevel, imagecache, NULL /*spec*/, NULL /*buffer*/, config)) { } ImageBuf::ImageBuf (string_view filename, ImageCache *imagecache) : m_impl (new ImageBufImpl (filename, 0, 0, imagecache)) { } ImageBuf::ImageBuf (const ImageSpec &spec) : m_impl (new ImageBufImpl ("", 0, 0, NULL, &spec)) { m_impl->alloc (spec); } ImageBuf::ImageBuf (string_view filename, const ImageSpec &spec) : m_impl (new ImageBufImpl (filename, 0, 0, NULL, &spec)) { m_impl->alloc (spec); } ImageBuf::ImageBuf (string_view filename, const ImageSpec &spec, void *buffer) : m_impl (new ImageBufImpl (filename, 0, 0, NULL, &spec, buffer)) { } ImageBuf::ImageBuf (const ImageSpec &spec, void *buffer) : m_impl (new ImageBufImpl ("", 0, 0, NULL, &spec, buffer)) { } ImageBuf::ImageBuf (const ImageBuf &src) : m_impl (new ImageBufImpl (*src.impl())) { } ImageBuf::~ImageBuf () { delete m_impl; m_impl = NULL; } static spin_mutex err_mutex; ///< Protect m_err fields bool ImageBuf::has_error () const { spin_lock lock (err_mutex); return ! impl()->m_err.empty(); } std::string ImageBuf::geterror (void) const { spin_lock lock (err_mutex); std::string e = impl()->m_err; impl()->m_err.clear(); return e; } void ImageBufImpl::append_error (const std::string &message) const { spin_lock lock (err_mutex); ASSERT (m_err.size() < 1024*1024*16 && "Accumulated error messages > 16MB. Try checking return codes!"); if (m_err.size() && m_err[m_err.size()-1] != '\n') m_err += '\n'; m_err += message; } void ImageBuf::append_error (const std::string &message) const { impl()->append_error (message); } ImageBuf::IBStorage ImageBuf::storage () const { return impl()->storage (); } void ImageBufImpl::clear () { m_storage = ImageBuf::UNINITIALIZED; m_name.clear (); m_fileformat.clear (); m_nsubimages = 0; m_current_subimage = -1; m_current_miplevel = -1; m_spec = ImageSpec (); m_nativespec = ImageSpec (); m_pixels.reset (); m_localpixels = NULL; m_spec_valid = false; m_pixels_valid = false; m_badfile = false; m_pixelaspect = 1; m_pixel_bytes = 0; m_scanline_bytes = 0; m_plane_bytes = 0; m_imagecache = NULL; m_deepdata.free (); m_blackpixel.clear (); m_write_format = TypeDesc::UNKNOWN; m_write_tile_width = 0; m_write_tile_height = 0; m_write_tile_depth = 0; m_configspec.reset (NULL); } void ImageBuf::clear () { impl()->clear (); } void ImageBufImpl::reset (string_view filename, int subimage, int miplevel, ImageCache *imagecache, const ImageSpec *config) { clear (); m_name = ustring (filename); m_current_subimage = subimage; m_current_miplevel = miplevel; if (imagecache) m_imagecache = imagecache; if (config) m_configspec.reset (new ImageSpec(*config)); if (m_name.length() > 0) { // If a filename was given, read the spec and set it up as an // ImageCache-backed image. Reallocate later if an explicit read() // is called to force read into a local buffer. read (subimage, miplevel); } } void ImageBuf::reset (string_view filename, int subimage, int miplevel, ImageCache *imagecache, const ImageSpec *config) { impl()->reset (filename, subimage, miplevel, imagecache, config); } void ImageBuf::reset (string_view filename, ImageCache *imagecache) { impl()->reset (filename, 0, 0, imagecache, NULL); } void ImageBufImpl::reset (string_view filename, const ImageSpec &spec) { clear (); m_name = ustring (filename); m_current_subimage = 0; m_current_miplevel = 0; alloc (spec); } void ImageBuf::reset (string_view filename, const ImageSpec &spec) { impl()->reset (filename, spec); } void ImageBuf::reset (const ImageSpec &spec) { impl()->reset ("", spec); } void ImageBufImpl::realloc () { IB_local_mem_current -= m_allocated_size; m_allocated_size = m_spec.deep ? size_t(0) : m_spec.image_bytes (); IB_local_mem_current += m_allocated_size; m_pixels.reset (m_allocated_size ? new char [m_allocated_size] : NULL); m_localpixels = m_pixels.get(); m_storage = m_allocated_size ? ImageBuf::LOCALBUFFER : ImageBuf::UNINITIALIZED; m_pixel_bytes = m_spec.pixel_bytes(); m_scanline_bytes = m_spec.scanline_bytes(); m_plane_bytes = clamped_mult64 (m_scanline_bytes, (imagesize_t)m_spec.height); m_blackpixel.resize (round_to_multiple (m_pixel_bytes, OIIO_SIMD_MAX_SIZE_BYTES), 0); // NB make it big enough for SSE if (m_allocated_size) m_pixels_valid = true; if (m_spec.deep) { m_deepdata.init (m_spec); m_storage = ImageBuf::LOCALBUFFER; } #if 0 std::cerr << "ImageBuf " << m_name << " local allocation: " << m_allocated_size << "\n"; #endif } void ImageBufImpl::alloc (const ImageSpec &spec) { m_spec = spec; // Preclude a nonsensical size m_spec.width = std::max (1, m_spec.width); m_spec.height = std::max (1, m_spec.height); m_spec.depth = std::max (1, m_spec.depth); m_spec.nchannels = std::max (1, m_spec.nchannels); m_nativespec = spec; realloc (); m_spec_valid = true; } bool ImageBufImpl::init_spec (string_view filename, int subimage, int miplevel) { if (!m_badfile && m_spec_valid && m_current_subimage >= 0 && m_current_miplevel >= 0 && m_name == filename && m_current_subimage == subimage && m_current_miplevel == miplevel) return true; // Already done if (! m_imagecache) { m_imagecache = ImageCache::create (true /* shared cache */); } m_pixels_valid = false; m_name = filename; m_nsubimages = 0; m_nmiplevels = 0; static ustring s_subimages("subimages"), s_miplevels("miplevels"); static ustring s_fileformat("fileformat"); if (m_configspec) // Pass configuration options to cache m_imagecache->add_file (m_name, NULL, m_configspec.get()); m_imagecache->get_image_info (m_name, subimage, miplevel, s_subimages, TypeDesc::TypeInt, &m_nsubimages); m_imagecache->get_image_info (m_name, subimage, miplevel, s_miplevels, TypeDesc::TypeInt, &m_nmiplevels); const char *fmt = NULL; m_imagecache->get_image_info (m_name, subimage, miplevel, s_fileformat, TypeDesc::TypeString, &fmt); m_fileformat = ustring(fmt); m_imagecache->get_imagespec (m_name, m_spec, subimage, miplevel); m_imagecache->get_imagespec (m_name, m_nativespec, subimage, miplevel, true); m_pixel_bytes = m_spec.pixel_bytes(); m_scanline_bytes = m_spec.scanline_bytes(); m_plane_bytes = clamped_mult64 (m_scanline_bytes, (imagesize_t)m_spec.height); m_blackpixel.resize (round_to_multiple (m_pixel_bytes, OIIO_SIMD_MAX_SIZE_BYTES), 0); // NB make it big enough for SSE // Subtlety: m_nativespec will have the true formats of the file, but // we rig m_spec to reflect what it will look like in the cache. // This may make m_spec appear to change if there's a subsequent read() // that forces a full read into local memory, but what else can we do? // It causes havoc for it to suddenly change in the other direction // when the file is lazily read. int peltype = TypeDesc::UNKNOWN; m_imagecache->get_image_info (m_name, subimage, miplevel, ustring("cachedpixeltype"), TypeDesc::TypeInt, &peltype); if (peltype != TypeDesc::UNKNOWN) { m_spec.format = (TypeDesc::BASETYPE)peltype; m_spec.channelformats.clear(); } if (m_nsubimages) { m_badfile = false; m_pixelaspect = m_spec.get_float_attribute ("pixelaspectratio", 1.0f); m_current_subimage = subimage; m_current_miplevel = miplevel; m_spec_valid = true; } else { m_badfile = true; m_current_subimage = -1; m_current_miplevel = -1; m_err = m_imagecache->geterror (); m_spec_valid = false; // std::cerr << "ImageBuf ERROR: " << m_err << "\n"; } return !m_badfile; } bool ImageBuf::init_spec (string_view filename, int subimage, int miplevel) { return impl()->init_spec (filename, subimage, miplevel); } bool ImageBufImpl::read (int subimage, int miplevel, bool force, TypeDesc convert, ProgressCallback progress_callback, void *progress_callback_data) { if (! m_name.length()) return true; if (m_pixels_valid && !force && subimage == m_current_subimage && miplevel == m_current_miplevel) return true; if (! init_spec (m_name.string(), subimage, miplevel)) { m_badfile = true; m_spec_valid = false; return false; } m_current_subimage = subimage; m_current_miplevel = miplevel; if (m_spec.deep) { boost::scoped_ptr input ( ImageInput::open (m_name.string(), m_configspec.get())); if (! input) { error ("%s", OIIO::geterror()); return false; } input->threads (threads()); // Pass on our thread policy ImageSpec dummyspec; if (! input->seek_subimage (subimage, miplevel, dummyspec)) { error ("%s", input->geterror()); return false; } if (! input->read_native_deep_image (m_deepdata)) { error ("%s", input->geterror()); return false; } m_spec = m_nativespec; // Deep images always use native data m_pixels_valid = true; m_storage = ImageBuf::LOCALBUFFER; return true; } // If we don't already have "local" pixels, and we aren't asking to // convert the pixels to a specific (and different) type, then take an // early out by relying on the cache. int peltype = TypeDesc::UNKNOWN; m_imagecache->get_image_info (m_name, subimage, miplevel, ustring("cachedpixeltype"), TypeDesc::TypeInt, &peltype); m_cachedpixeltype = TypeDesc ((TypeDesc::BASETYPE)peltype); if (! m_localpixels && ! force && (convert == m_cachedpixeltype || convert == TypeDesc::UNKNOWN)) { m_spec.format = m_cachedpixeltype; m_pixel_bytes = m_spec.pixel_bytes(); m_scanline_bytes = m_spec.scanline_bytes(); m_plane_bytes = clamped_mult64 (m_scanline_bytes, (imagesize_t)m_spec.height); m_blackpixel.resize (round_to_multiple (m_pixel_bytes, OIIO_SIMD_MAX_SIZE_BYTES), 0); // NB make it big enough for SSE m_pixels_valid = true; m_storage = ImageBuf::IMAGECACHE; #ifndef NDEBUG // std::cerr << "read was not necessary -- using cache\n"; #endif return true; } if (convert != TypeDesc::UNKNOWN) m_spec.format = convert; else m_spec.format = m_nativespec.format; m_pixelaspect = m_spec.get_float_attribute ("pixelaspectratio", 1.0f); realloc (); // If forcing a full read, make sure the spec reflects the nativespec's // tile sizes, rather than that imposed by the ImageCache. m_spec.tile_width = m_nativespec.tile_width; m_spec.tile_height = m_nativespec.tile_height; m_spec.tile_depth = m_nativespec.tile_depth; if (force || (convert != TypeDesc::UNKNOWN && convert != m_cachedpixeltype && convert.size() >= m_cachedpixeltype.size() && convert.size() >= m_nativespec.format.size())) { // A specific conversion type was requested which is not the cached // type and whose bit depth is as much or more than the cached type. // Bypass the cache and read directly so that there is no possible // loss of range or precision resulting from going through the // cache. int unassoc = 0; if (m_imagecache->getattribute ("unassociatedalpha", unassoc)) { // Since IB needs to act as if it's backed by an ImageCache, // even though in this case we're bypassing the IC, we need to // honor the IC's "unassociatedalpha" flag. add_configspec (); m_configspec->attribute ("oiio:UnassociatedAlpha", unassoc); } ImageInput *in = ImageInput::open (m_name.string(), m_configspec.get()); bool ok = true; if (in) { in->threads (threads()); // Pass on our thread policy if (subimage || miplevel) { ImageSpec newspec; ok &= in->seek_subimage (subimage, miplevel, newspec); } if (ok) ok &= in->read_image (convert, m_localpixels); in->close (); if (ok) { m_pixels_valid = true; } else { m_pixels_valid = false; error ("%s", in->geterror()); } delete in; } else { m_pixels_valid = false; error ("%s", OIIO::geterror()); } return m_pixels_valid; } // All other cases, no loss of precision is expected, so even a forced // read should go through the image cache. if (m_imagecache->get_pixels (m_name, subimage, miplevel, 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, m_spec.format, m_localpixels)) { m_pixels_valid = true; } else { m_pixels_valid = false; error ("%s", m_imagecache->geterror ()); } return m_pixels_valid; } bool ImageBuf::read (int subimage, int miplevel, bool force, TypeDesc convert, ProgressCallback progress_callback, void *progress_callback_data) { return impl()->read (subimage, miplevel, force, convert, progress_callback, progress_callback_data); } void ImageBuf::set_write_format (TypeDesc format) { impl()->m_write_format = format; } void ImageBuf::set_write_tiles (int width, int height, int depth) { impl()->m_write_tile_width = width; impl()->m_write_tile_height = height; impl()->m_write_tile_depth = std::max (1, depth); } bool ImageBuf::write (ImageOutput *out, ProgressCallback progress_callback, void *progress_callback_data) const { stride_t as = AutoStride; bool ok = true; const ImageBufImpl *impl = this->impl(); ok &= impl->validate_pixels (); const ImageSpec &bufspec (impl->m_spec); const ImageSpec &outspec (out->spec()); TypeDesc bufformat = spec().format; if (impl->m_localpixels) { // In-core pixel buffer for the whole image ok = out->write_image (bufformat, impl->m_localpixels, as, as, as, progress_callback, progress_callback_data); } else if (deep()) { // Deep image record ok = out->write_deep_image (impl->m_deepdata); } else { // The image we want to write is backed by ImageCache -- we must be // immediately writing out a file from disk, possibly with file // format or data format conversion, but without any ImageBufAlgo // functions having been applied. const imagesize_t budget = 1024*1024*64; // 64 MB imagesize_t imagesize = bufspec.image_bytes(); if (imagesize <= budget) { // whole image can fit within our budget boost::scoped_array tmp (new char [imagesize]); ok &= get_pixels (roi(), bufformat, &tmp[0]); ok &= out->write_image (bufformat, &tmp[0], as, as, as, progress_callback, progress_callback_data); } else if (outspec.tile_width) { // Big tiled image: break up into tile strips size_t pixelsize = bufspec.pixel_bytes(); size_t chunksize = pixelsize * outspec.width * outspec.tile_height * outspec.tile_depth; boost::scoped_array tmp (new char [chunksize]); for (int z = 0; z < outspec.depth; z += outspec.tile_depth) { int zend = std::min (z+outspec.z+outspec.tile_depth, outspec.z+outspec.depth); for (int y = 0; y < outspec.height && ok; y += outspec.tile_height) { int yend = std::min (y+outspec.y+outspec.tile_height, outspec.y+outspec.height); ok &= get_pixels (ROI(outspec.x, outspec.x+outspec.width, outspec.y+y, yend, outspec.z+z, zend), bufformat, &tmp[0]); ok &= out->write_tiles (outspec.x, outspec.x+outspec.width, y+outspec.y, yend, z+outspec.z, zend, bufformat, &tmp[0]); if (progress_callback && progress_callback (progress_callback_data, (float)(z*outspec.height+y)/(outspec.height*outspec.depth))) return ok; } } } else { // Big scanline image: break up into scanline strips imagesize_t slsize = bufspec.scanline_bytes(); int chunk = clamp (round_to_multiple(int(budget/slsize), 64), 1, 1024); boost::scoped_array tmp (new char [chunk*slsize]); for (int z = 0; z < outspec.depth; ++z) { for (int y = 0; y < outspec.height && ok; y += chunk) { int yend = std::min (y+outspec.y+chunk, outspec.y+outspec.height); ok &= get_pixels (ROI(outspec.x, outspec.x+outspec.width, outspec.y+y, yend, outspec.z, outspec.z+outspec.depth), bufformat, &tmp[0]); ok &= out->write_scanlines (y+outspec.y, yend, z+outspec.z, bufformat, &tmp[0]); if (progress_callback && progress_callback (progress_callback_data, (float)(z*outspec.height+y)/(outspec.height*outspec.depth))) return ok; } } } } if (! ok) error ("%s", out->geterror ()); return ok; } bool ImageBuf::write (string_view _filename, string_view _fileformat, ProgressCallback progress_callback, void *progress_callback_data) const { string_view filename = _filename.size() ? _filename : name(); string_view fileformat = _fileformat.size() ? _fileformat : filename; if (filename.size() == 0) { error ("ImageBuf::write() called with no filename"); return false; } impl()->validate_pixels (); boost::scoped_ptr out (ImageOutput::create (fileformat.c_str(), "" /* searchpath */)); if (! out) { error ("%s", geterror()); return false; } out->threads (threads()); // Pass on our thread policy // Write scanline files by default, but if the file type allows tiles, // user can override via ImageBuf::set_write_tiles(), or by using the // variety of IB::write() that takes the open ImageOutput* directly. ImageSpec newspec = spec(); if (out->supports("tiles") && impl()->m_write_tile_width > 0) { newspec.tile_width = impl()->m_write_tile_width; newspec.tile_height = impl()->m_write_tile_height; newspec.tile_depth = std::max (1, impl()->m_write_tile_depth); } else { newspec.tile_width = 0; newspec.tile_height = 0; newspec.tile_depth = 0; } // Allow for format override via ImageBuf::set_write_format() if (impl()->m_write_format != TypeDesc::UNKNOWN) { newspec.set_format (impl()->m_write_format); newspec.channelformats.clear(); } else { newspec.set_format (nativespec().format); newspec.channelformats = nativespec().channelformats; } if (! out->open (filename.c_str(), newspec)) { error ("%s", out->geterror()); return false; } if (! write (out.get(), progress_callback, progress_callback_data)) return false; out->close (); if (progress_callback) progress_callback (progress_callback_data, 0); return true; } bool ImageBuf::make_writeable (bool keep_cache_type) { if (storage() == IMAGECACHE) { return read (subimage(), miplevel(), true /*force*/, keep_cache_type ? impl()->m_cachedpixeltype : TypeDesc()); } return true; } void ImageBufImpl::copy_metadata (const ImageBufImpl &src) { if (this == &src) return; const ImageSpec &srcspec (src.spec()); ImageSpec &m_spec (this->specmod()); m_spec.full_x = srcspec.full_x; m_spec.full_y = srcspec.full_y; m_spec.full_z = srcspec.full_z; m_spec.full_width = srcspec.full_width; m_spec.full_height = srcspec.full_height; m_spec.full_depth = srcspec.full_depth; if (src.storage() == ImageBuf::IMAGECACHE) { // If we're copying metadata from a cached image, be sure to // get the file's tile size, not the cache's tile size. m_spec.tile_width = src.nativespec().tile_width; m_spec.tile_height = src.nativespec().tile_height; m_spec.tile_depth = src.nativespec().tile_depth; } else { m_spec.tile_width = srcspec.tile_width; m_spec.tile_height = srcspec.tile_height; m_spec.tile_depth = srcspec.tile_depth; } m_spec.extra_attribs = srcspec.extra_attribs; } void ImageBuf::copy_metadata (const ImageBuf &src) { impl()->copy_metadata (*src.impl()); } const ImageSpec & ImageBuf::spec () const { return impl()->spec(); } ImageSpec & ImageBuf::specmod () { return impl()->specmod(); } const ImageSpec & ImageBuf::nativespec () const { return impl()->nativespec(); } string_view ImageBuf::name (void) const { return impl()->m_name; } string_view ImageBuf::file_format_name (void) const { impl()->validate_spec (); return impl()->m_fileformat; } int ImageBuf::subimage () const { return impl()->m_current_subimage; } int ImageBuf::nsubimages () const { impl()->validate_spec(); return impl()->m_nsubimages; } int ImageBuf::miplevel () const { return impl()->m_current_miplevel; } int ImageBuf::nmiplevels () const { impl()->validate_spec(); return impl()->m_nmiplevels; } int ImageBuf::nchannels () const { return impl()->spec().nchannels; } int ImageBuf::orientation () const { impl()->validate_spec(); return impl()->spec().get_int_attribute ("Orientation", 1); } void ImageBuf::set_orientation (int orient) { impl()->specmod().attribute ("Orientation", orient); } bool ImageBuf::pixels_valid (void) const { return impl()->m_pixels_valid; } TypeDesc ImageBuf::pixeltype () const { return impl()->pixeltype(); } void * ImageBuf::localpixels () { impl()->validate_pixels (); return impl()->m_localpixels; } const void * ImageBuf::localpixels () const { impl()->validate_pixels (); return impl()->m_localpixels; } bool ImageBuf::cachedpixels () const { return impl()->cachedpixels(); } ImageCache * ImageBuf::imagecache () const { return impl()->m_imagecache; } bool ImageBuf::deep () const { return spec().deep; } DeepData * ImageBuf::deepdata () { return impl()->deepdata (); } const DeepData * ImageBuf::deepdata () const { return impl()->deepdata (); } bool ImageBuf::initialized () const { return impl()->initialized (); } void ImageBuf::threads (int n) const { impl()->threads(n); } int ImageBuf::threads () const { return impl()->threads(); } namespace { // Pixel-by-pixel copy fully templated by both data types. // The roi is guaranteed to exist in both images. template static bool copy_pixels_impl (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads=0) { if (nthreads != 1 && roi.npixels() >= 16384) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(copy_pixels_impl, OIIO::ref(dst), OIIO::cref(src), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case: int nchannels = roi.nchannels(); if (is_same::value) { // If both bufs are the same type, just directly copy the values if (src.localpixels() && roi.chbegin == 0 && roi.chend == dst.nchannels() && roi.chend == src.nchannels()) { // Extra shortcut -- totally local pixels for src, copying all // channels, so we can copy memory around line by line, rather // than value by value. int nxvalues = roi.width() * dst.nchannels(); for (int z = roi.zbegin; z < roi.zend; ++z) for (int y = roi.ybegin; y < roi.yend; ++y) { D *draw = (D *) dst.pixeladdr (roi.xbegin, y, z); const S *sraw = (const S *) src.pixeladdr (roi.xbegin, y, z); DASSERT (draw && sraw); for (int x = 0; x < nxvalues; ++x) draw[x] = sraw[x]; } } else { ImageBuf::Iterator d (dst, roi); ImageBuf::ConstIterator s (src, roi); for ( ; ! d.done(); ++d, ++s) { for (int c = 0; c < nchannels; ++c) d[c] = s[c]; } } } else { // If the two bufs are different types, convert through float ImageBuf::Iterator d (dst, roi); ImageBuf::ConstIterator s (src, roi); for ( ; ! d.done(); ++d, ++s) { for (int c = 0; c < nchannels; ++c) d[c] = s[c]; } } return true; } } // anon namespace bool ImageBuf::copy_pixels (const ImageBuf &src) { if (this == &src) return true; if (deep() || src.deep()) return false; // This operation is not supported for deep images // compute overlap ROI myroi = get_roi(spec()); ROI roi = roi_intersection (myroi, get_roi(src.spec())); // If we aren't copying over all our pixels, zero out the pixels if (roi != myroi) ImageBufAlgo::zero (*this); bool ok; OIIO_DISPATCH_TYPES2 (ok, "copy_pixels", copy_pixels_impl, spec().format, src.spec().format, *this, src, roi); return ok; } bool ImageBuf::copy (const ImageBuf &src, TypeDesc format) { src.impl()->validate_pixels (); if (this == &src) // self-assignment return true; if (src.storage() == UNINITIALIZED) { // buf = uninitialized clear(); return true; } if (src.deep()) { reset (src.name(), src.spec()); impl()->m_deepdata = src.impl()->m_deepdata; return true; } if (format.basetype == TypeDesc::UNKNOWN || src.deep()) reset (src.name(), src.spec()); else { ImageSpec newspec (src.spec()); newspec.set_format (format); newspec.channelformats.clear (); reset (src.name(), newspec); } return this->copy_pixels (src); } bool ImageBuf::copy (const ImageBuf &src) { return copy (src, TypeDesc::UNKNOWN); } template static inline float getchannel_ (const ImageBuf &buf, int x, int y, int z, int c, ImageBuf::WrapMode wrap) { ImageBuf::ConstIterator pixel (buf, x, y, z); return pixel[c]; } float ImageBuf::getchannel (int x, int y, int z, int c, WrapMode wrap) const { if (c < 0 || c >= spec().nchannels) return 0.0f; float ret; OIIO_DISPATCH_TYPES (ret, "getchannel", getchannel_, spec().format, *this, x, y, z, c, wrap); return ret; } template static bool getpixel_ (const ImageBuf &buf, int x, int y, int z, float *result, int chans, ImageBuf::WrapMode wrap) { ImageBuf::ConstIterator pixel (buf, x, y, z, wrap); for (int i = 0; i < chans; ++i) result[i] = pixel[i]; return true; } inline bool getpixel_wrapper (int x, int y, int z, float *pixel, int nchans, ImageBuf::WrapMode wrap, const ImageBuf &ib) { bool ok; OIIO_DISPATCH_TYPES (ok, "getpixel", getpixel_, ib.spec().format, ib, x, y, z, pixel, nchans, wrap); return ok; } void ImageBuf::getpixel (int x, int y, int z, float *pixel, int maxchannels, WrapMode wrap) const { int nchans = std::min (spec().nchannels, maxchannels); getpixel_wrapper (x, y, z, pixel, nchans, wrap, *this); } template static bool interppixel_ (const ImageBuf &img, float x, float y, float *pixel, ImageBuf::WrapMode wrap) { int n = img.spec().nchannels; float *localpixel = ALLOCA (float, n*4); float *p[4] = { localpixel, localpixel+n, localpixel+2*n, localpixel+3*n }; x -= 0.5f; y -= 0.5f; int xtexel, ytexel; float xfrac, yfrac; xfrac = floorfrac (x, &xtexel); yfrac = floorfrac (y, &ytexel); ImageBuf::ConstIterator it (img, xtexel, xtexel+2, ytexel, ytexel+2, 0, 1, wrap); for (int i = 0; i < 4; ++i, ++it) for (int c = 0; c < n; ++c) p[i][c] = it[c]; bilerp (p[0], p[1], p[2], p[3], xfrac, yfrac, n, pixel); return true; } inline bool interppixel_wrapper (float x, float y, float *pixel, ImageBuf::WrapMode wrap, const ImageBuf &img) { bool ok; OIIO_DISPATCH_TYPES (ok, "interppixel", interppixel_, img.spec().format, img, x, y, pixel, wrap); return ok; } void ImageBuf::interppixel (float x, float y, float *pixel, WrapMode wrap) const { interppixel_wrapper (x, y, pixel, wrap, *this); } void ImageBuf::interppixel_NDC (float x, float y, float *pixel, WrapMode wrap) const { const ImageSpec &spec (impl()->spec()); interppixel (static_cast(spec.full_x) + x * static_cast(spec.full_width), static_cast(spec.full_y) + y * static_cast(spec.full_height), pixel, wrap); } void ImageBuf::interppixel_NDC_full (float x, float y, float *pixel, WrapMode wrap) const { const ImageSpec &spec (impl()->spec()); interppixel (static_cast(spec.full_x) + x * static_cast(spec.full_width), static_cast(spec.full_y) + y * static_cast(spec.full_height), pixel, wrap); } template static bool interppixel_bicubic_ (const ImageBuf &img, float x, float y, float *pixel, ImageBuf::WrapMode wrap) { int n = img.spec().nchannels; x -= 0.5f; y -= 0.5f; int xtexel, ytexel; float xfrac, yfrac; xfrac = floorfrac (x, &xtexel); yfrac = floorfrac (y, &ytexel); float wx[4]; evalBSplineWeights (wx, xfrac); float wy[4]; evalBSplineWeights (wy, yfrac); for (int c = 0; c < n; ++c) pixel[c] = 0.0f; ImageBuf::ConstIterator it (img, xtexel-1, xtexel+3, ytexel-1, ytexel+3, 0, 1, wrap); for (int j = 0; j < 4; ++j) { for (int i = 0; i < 4; ++i, ++it) { float w = wx[i] * wy[j]; for (int c = 0; c < n; ++c) pixel[c] += w * it[c]; } } return true; } inline bool interppixel_bicubic_wrapper (float x, float y, float *pixel, ImageBuf::WrapMode wrap, const ImageBuf &img) { bool ok; OIIO_DISPATCH_TYPES (ok, "interppixel_bicubic", interppixel_bicubic_, img.spec().format, img, x, y, pixel, wrap); return ok; } void ImageBuf::interppixel_bicubic (float x, float y, float *pixel, WrapMode wrap) const { interppixel_bicubic_wrapper (x, y, pixel, wrap, *this); } void ImageBuf::interppixel_bicubic_NDC (float x, float y, float *pixel, WrapMode wrap) const { const ImageSpec &spec (impl()->spec()); interppixel_bicubic (static_cast(spec.full_x) + x * static_cast(spec.full_width), static_cast(spec.full_y) + y * static_cast(spec.full_height), pixel, wrap); } template inline void setpixel_ (ImageBuf &buf, int x, int y, int z, const float *data, int chans) { ImageBuf::Iterator pixel (buf, x, y, z); if (pixel.exists()) { for (int i = 0; i < chans; ++i) pixel[i] = data[i]; } } void ImageBuf::setpixel (int x, int y, int z, const float *pixel, int maxchannels) { int n = std::min (spec().nchannels, maxchannels); switch (spec().format.basetype) { case TypeDesc::FLOAT : setpixel_ (*this, x, y, z, pixel, n); break; case TypeDesc::UINT8 : setpixel_ (*this, x, y, z, pixel, n); break; case TypeDesc::INT8 : setpixel_ (*this, x, y, z, pixel, n); break; case TypeDesc::UINT16: setpixel_ (*this, x, y, z, pixel, n); break; case TypeDesc::INT16 : setpixel_ (*this, x, y, z, pixel, n); break; case TypeDesc::UINT : setpixel_ (*this, x, y, z, pixel, n); break; case TypeDesc::INT : setpixel_ (*this, x, y, z, pixel, n); break; case TypeDesc::HALF : setpixel_ (*this, x, y, z, pixel, n); break; case TypeDesc::DOUBLE: setpixel_ (*this, x, y, z, pixel, n); break; case TypeDesc::UINT64: setpixel_ (*this, x, y, z, pixel, n); break; case TypeDesc::INT64 : setpixel_ (*this, x, y, z, pixel, n); break; default: ASSERTMSG (0, "Unknown/unsupported data type %d", spec().format.basetype); } } void ImageBuf::setpixel (int i, const float *pixel, int maxchannels) { setpixel (spec().x + (i % spec().width), spec().y + (i / spec().width), pixel, maxchannels); } template static bool get_pixels_ (const ImageBuf &buf, ROI whole_roi, ROI roi, void *r_, stride_t xstride, stride_t ystride, stride_t zstride, int nthreads=0) { if (nthreads != 1 && roi.npixels() >= 64*1024) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(get_pixels_, OIIO::ref(buf), whole_roi, _1 /*roi*/, r_, xstride, ystride, zstride, 1 /*nthreads*/), roi, nthreads); return true; } D *r = (D *)r_; int nchans = roi.nchannels(); for (ImageBuf::ConstIterator p (buf, roi); !p.done(); ++p) { imagesize_t offset = (p.z()-whole_roi.zbegin)*zstride + (p.y()-whole_roi.ybegin)*ystride + (p.x()-whole_roi.xbegin)*xstride; D *rc = (D *)((char *)r + offset); for (int c = 0; c < nchans; ++c) rc[c] = p[c+roi.chbegin]; } return true; } bool ImageBuf::get_pixels (ROI roi, TypeDesc format, void *result, stride_t xstride, stride_t ystride, stride_t zstride) const { if (! roi.defined()) roi = this->roi(); roi.chend = std::min (roi.chend, nchannels()); ImageSpec::auto_stride (xstride, ystride, zstride, format.size(), roi.nchannels(), roi.width(), roi.height()); bool ok; OIIO_DISPATCH_TYPES2 (ok, "get_pixels", get_pixels_, format, spec().format, *this, roi, roi, result, xstride, ystride, zstride, threads()); return ok; } // DEPRECATED template bool ImageBuf::get_pixel_channels (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, D *r, stride_t xstride, stride_t ystride, stride_t zstride) const { ROI roi (xbegin, xend, ybegin, yend, zbegin, zend, chbegin, chend); ImageSpec::auto_stride (xstride, ystride, zstride, sizeof(D), roi.nchannels(), roi.width(), roi.height()); bool ok; OIIO_DISPATCH_TYPES2_HELP (ok, "get_pixel_channels", get_pixels_, D, spec().format, *this, roi, roi, r, xstride, ystride, zstride); return ok; } // DEPRECATED bool ImageBuf::get_pixel_channels (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, TypeDesc format, void *result, stride_t xstride, stride_t ystride, stride_t zstride) const { ROI roi (xbegin, xend, ybegin, yend, zbegin, zend, chbegin, chend); return get_pixels (roi, format, result, xstride, ystride, zstride); } // DEPRECATED bool ImageBuf::get_pixels (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, void *result, stride_t xstride, stride_t ystride, stride_t zstride) const { ROI roi (xbegin, xend, ybegin, yend, zbegin, zend, 0, nchannels()); return get_pixels (roi, format, result, xstride, ystride, zstride); } template static bool set_pixels_ (ImageBuf &buf, ROI roi, const void *data_, stride_t xstride, stride_t ystride, stride_t zstride) { const D *data = (const D *)data_; int w = roi.width(), h = roi.height(), nchans = roi.nchannels(); ImageSpec::auto_stride (xstride, ystride, zstride, sizeof(S), nchans, w, h); for (ImageBuf::Iterator p (buf, roi); !p.done(); ++p) { if (! p.exists()) continue; imagesize_t offset = (p.z()-roi.zbegin)*zstride + (p.y()-roi.ybegin)*ystride + (p.x()-roi.xbegin)*xstride; const S *src = (const S *)((const char *)data + offset); for (int c = 0; c < nchans; ++c) p[c+roi.chbegin] = src[c]; } return true; } bool ImageBuf::set_pixels (ROI roi, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { bool ok; if (! roi.defined()) roi = this->roi(); roi.chend = std::min (roi.chend, nchannels()); OIIO_DISPATCH_TYPES2 (ok, "set_pixels", set_pixels_, spec().format, format, *this, roi, data, xstride, ystride, zstride); return ok; } int ImageBuf::deep_samples (int x, int y, int z) const { impl()->validate_pixels(); if (! deep()) return 0; int p = impl()->pixelindex (x, y, z, true); return p >= 0 ? deepdata()->samples(p) : 0; } const void * ImageBuf::deep_pixel_ptr (int x, int y, int z, int c, int s) const { impl()->validate_pixels(); if (! deep()) return NULL; const ImageSpec &m_spec (spec()); int p = impl()->pixelindex (x, y, z, true); if (p < 0 || c < 0 || c >= m_spec.nchannels) return NULL; return (s < deepdata()->samples(p)) ? deepdata()->data_ptr (p, c, s) : NULL; } float ImageBuf::deep_value (int x, int y, int z, int c, int s) const { impl()->validate_pixels(); if (! deep()) return 0.0f; int p = impl()->pixelindex (x, y, z); return impl()->m_deepdata.deep_value (p, c, s); } uint32_t ImageBuf::deep_value_uint (int x, int y, int z, int c, int s) const { impl()->validate_pixels(); if (! deep()) return 0; int p = impl()->pixelindex (x, y, z); return impl()->m_deepdata.deep_value_uint (p, c, s); } void ImageBuf::set_deep_samples (int x, int y, int z, int samps) { if (! deep()) return ; int p = impl()->pixelindex (x, y, z); impl()->m_deepdata.set_samples (p, samps); } void ImageBuf::deep_insert_samples (int x, int y, int z, int samplepos, int nsamples) { if (! deep()) return ; int p = impl()->pixelindex (x, y, z); impl()->m_deepdata.insert_samples (p, samplepos, nsamples); } void ImageBuf::deep_erase_samples (int x, int y, int z, int samplepos, int nsamples) { if (! deep()) return ; int p = impl()->pixelindex (x, y, z); impl()->m_deepdata.erase_samples (p, samplepos, nsamples); } void ImageBuf::set_deep_value (int x, int y, int z, int c, int s, float value) { impl()->validate_pixels(); if (! deep()) return ; int p = impl()->pixelindex (x, y, z); return impl()->m_deepdata.set_deep_value (p, c, s, value); } void ImageBuf::set_deep_value (int x, int y, int z, int c, int s, uint32_t value) { impl()->validate_pixels(); if (! deep()) return; int p = impl()->pixelindex (x, y, z); return impl()->m_deepdata.set_deep_value (p, c, s, value); } // DEPRECATED (1.7): old name void ImageBuf::set_deep_value_uint (int x, int y, int z, int c, int s, uint32_t value) { return set_deep_value (x, y, z, c, s, value); } // DEPRECATED (1.7) void ImageBuf::deep_alloc () { ASSERT (m_impl->m_storage == ImageBuf::LOCALBUFFER); } int ImageBuf::xbegin () const { return spec().x; } int ImageBuf::xend () const { return spec().x + spec().width; } int ImageBuf::ybegin () const { return spec().y; } int ImageBuf::yend () const { return spec().y + spec().height; } int ImageBuf::zbegin () const { return spec().z; } int ImageBuf::zend () const { return spec().z + std::max(spec().depth,1); } int ImageBuf::xmin () const { return spec().x; } int ImageBuf::xmax () const { return spec().x + spec().width - 1; } int ImageBuf::ymin () const { return spec().y; } int ImageBuf::ymax () const { return spec().y + spec().height - 1; } int ImageBuf::zmin () const { return spec().z; } int ImageBuf::zmax () const { return spec().z + std::max(spec().depth,1) - 1; } int ImageBuf::oriented_width () const { const ImageBufImpl *impl (this->impl()); const ImageSpec &spec (impl->spec()); return orientation() <= 4 ? spec.width : spec.height; } int ImageBuf::oriented_height () const { const ImageBufImpl *impl (this->impl()); const ImageSpec &spec (impl->spec()); return orientation() <= 4 ? spec.height : spec.width; } int ImageBuf::oriented_x () const { const ImageBufImpl *impl (this->impl()); const ImageSpec &spec (impl->spec()); return orientation() <= 4 ? spec.x : spec.y; } int ImageBuf::oriented_y () const { const ImageBufImpl *impl (this->impl()); const ImageSpec &spec (impl->spec()); return orientation() <= 4 ? spec.y : spec.x; } int ImageBuf::oriented_full_width () const { const ImageBufImpl *impl (this->impl()); const ImageSpec &spec (impl->spec()); return orientation() <= 4 ? spec.full_width : spec.full_height; } int ImageBuf::oriented_full_height () const { const ImageBufImpl *impl (this->impl()); const ImageSpec &spec (impl->spec()); return orientation() <= 4 ? spec.full_height : spec.full_width; } int ImageBuf::oriented_full_x () const { const ImageBufImpl *impl (this->impl()); const ImageSpec &spec (impl->spec()); return orientation() <= 4 ? spec.full_x : spec.full_y; } int ImageBuf::oriented_full_y () const { const ImageBufImpl *impl (this->impl()); const ImageSpec &spec (impl->spec()); return orientation() <= 4 ? spec.full_y : spec.full_x; } void ImageBuf::set_full (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend) { ImageSpec &m_spec (impl()->specmod()); m_spec.full_x = xbegin; m_spec.full_y = ybegin; m_spec.full_z = zbegin; m_spec.full_width = xend - xbegin; m_spec.full_height = yend - ybegin; m_spec.full_depth = zend - zbegin; } ROI ImageBuf::roi () const { return get_roi(spec()); } ROI ImageBuf::roi_full () const { return get_roi_full(spec()); } void ImageBuf::set_roi_full (const ROI &newroi) { OIIO::set_roi_full (specmod(), newroi); } bool ImageBuf::contains_roi (ROI roi) const { ROI myroi = roi; return (roi.defined() && myroi.defined() && roi.xbegin >= myroi.xbegin && roi.xend <= myroi.xend && roi.ybegin >= myroi.ybegin && roi.yend <= myroi.yend && roi.zbegin >= myroi.zbegin && roi.zend <= myroi.zend && roi.chbegin >= myroi.chbegin && roi.chend <= myroi.chend); } const void * ImageBufImpl::pixeladdr (int x, int y, int z) const { if (cachedpixels()) return NULL; validate_pixels (); x -= m_spec.x; y -= m_spec.y; z -= m_spec.z; size_t p = y * m_scanline_bytes + x * m_pixel_bytes + z * m_plane_bytes; return &(m_localpixels[p]); } void * ImageBufImpl::pixeladdr (int x, int y, int z) { validate_pixels (); if (cachedpixels()) return NULL; x -= m_spec.x; y -= m_spec.y; z -= m_spec.z; size_t p = y * m_scanline_bytes + x * m_pixel_bytes + z * m_plane_bytes; return &(m_localpixels[p]); } const void * ImageBuf::pixeladdr (int x, int y, int z) const { return impl()->pixeladdr (x, y, z); } void * ImageBuf::pixeladdr (int x, int y, int z) { return impl()->pixeladdr (x, y, z); } int ImageBuf::pixelindex (int x, int y, int z, bool check_range) const { return impl()->pixelindex (x, y, z, check_range); } const void * ImageBuf::blackpixel () const { return impl()->blackpixel(); } bool ImageBufImpl::do_wrap (int &x, int &y, int &z, ImageBuf::WrapMode wrap) const { const ImageSpec &m_spec (this->spec()); // Double check that we're outside the data window -- supposedly a // precondition of calling this method. DASSERT (! (x >= m_spec.x && x < m_spec.x+m_spec.width && y >= m_spec.y && y < m_spec.y+m_spec.height && z >= m_spec.z && z < m_spec.z+m_spec.depth)); // Wrap based on the display window if (wrap == ImageBuf::WrapBlack) { // no remapping to do return false; // still outside the data window } else if (wrap == ImageBuf::WrapClamp) { x = OIIO::clamp (x, m_spec.full_x, m_spec.full_x+m_spec.full_width-1); y = OIIO::clamp (y, m_spec.full_y, m_spec.full_y+m_spec.full_height-1); z = OIIO::clamp (z, m_spec.full_z, m_spec.full_z+m_spec.full_depth-1); } else if (wrap == ImageBuf::WrapPeriodic) { wrap_periodic (x, m_spec.full_x, m_spec.full_width); wrap_periodic (y, m_spec.full_y, m_spec.full_height); wrap_periodic (z, m_spec.full_z, m_spec.full_depth); } else if (wrap == ImageBuf::WrapMirror) { wrap_mirror (x, m_spec.full_x, m_spec.full_width); wrap_mirror (y, m_spec.full_y, m_spec.full_height); wrap_mirror (z, m_spec.full_z, m_spec.full_depth); } else { ASSERT_MSG (0, "unknown wrap mode %d", (int)wrap); } // Now determine if the new position is within the data window return (x >= m_spec.x && x < m_spec.x+m_spec.width && y >= m_spec.y && y < m_spec.y+m_spec.height && z >= m_spec.z && z < m_spec.z+m_spec.depth); } bool ImageBuf::do_wrap (int &x, int &y, int &z, WrapMode wrap) const { return m_impl->do_wrap (x, y, z, wrap); } const void * ImageBufImpl::retile (int x, int y, int z, ImageCache::Tile* &tile, int &tilexbegin, int &tileybegin, int &tilezbegin, int &tilexend, bool exists, ImageBuf::WrapMode wrap) const { if (! exists) { // Special case -- (x,y,z) describes a location outside the data // window. Use the wrap mode to possibly give a meaningful data // proxy to point to. if (! do_wrap (x, y, z, wrap)) { // After wrapping, the new xyz point outside the data window. // So return the black pixel. return &m_blackpixel[0]; } // We've adjusted x,y,z, and know the wrapped coordinates are in the // pixel data window, so now fall through below to get the right // tile. } DASSERT (x >= m_spec.x && x < m_spec.x+m_spec.width && y >= m_spec.y && y < m_spec.y+m_spec.height && z >= m_spec.z && z < m_spec.z+m_spec.depth); int tw = m_spec.tile_width, th = m_spec.tile_height; int td = m_spec.tile_depth; DASSERT(m_spec.tile_depth >= 1); DASSERT (tile == NULL || tilexend == (tilexbegin+tw)); if (tile == NULL || x < tilexbegin || x >= tilexend || y < tileybegin || y >= (tileybegin+th) || z < tilezbegin || z >= (tilezbegin+td)) { // not the same tile as before if (tile) m_imagecache->release_tile (tile); int xtile = (x-m_spec.x) / tw; int ytile = (y-m_spec.y) / th; int ztile = (z-m_spec.z) / td; tilexbegin = m_spec.x + xtile*tw; tileybegin = m_spec.y + ytile*th; tilezbegin = m_spec.z + ztile*td; tilexend = tilexbegin + tw; tile = m_imagecache->get_tile (m_name, m_current_subimage, m_current_miplevel, x, y, z); if (! tile) { // Even though tile is NULL, ensure valid black pixel data std::string e = m_imagecache->geterror(); error ("%s", e.size() ? e : "unspecified ImageCache error"); return &m_blackpixel[0]; } } size_t offset = ((z - tilezbegin) * (size_t) th + (y - tileybegin)) * (size_t) tw + (x - tilexbegin); offset *= m_spec.pixel_bytes(); DASSERTMSG (m_spec.pixel_bytes() == m_pixel_bytes, "%d vs %d", (int)m_spec.pixel_bytes(), (int)m_pixel_bytes); TypeDesc format; const void* pixeldata = m_imagecache->tile_pixels (tile, format); return pixeldata ? (const char *)pixeldata + offset : NULL; } const void * ImageBuf::retile (int x, int y, int z, ImageCache::Tile* &tile, int &tilexbegin, int &tileybegin, int &tilezbegin, int &tilexend, bool exists, WrapMode wrap) const { return impl()->retile (x, y, z, tile, tilexbegin, tileybegin, tilezbegin, tilexend, exists, wrap); } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/imagecache_test.cpp0000644000175000017500000000755713151711064023526 0ustar mfvmfv/* Copyright 2015 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include OIIO_NAMESPACE_USING; void test_get_pixels_cachechannels (int chbegin = 0, int chend = 4, int cache_chbegin = 0, int cache_chend = -1) { std::cout << "\nTesting IC get_pixels of chans [" << chbegin << "," << chend << ") with cache range [" << cache_chbegin << "," << cache_chend << "):\n"; ImageCache *imagecache = ImageCache::create (false /*not shared*/); // Create a 10 channel file ustring filename ("tenchannels.tif"); const int nchans = 10; ImageBuf A (ImageSpec (64, 64, nchans, TypeDesc::FLOAT)); const float pixelvalue[nchans] = { 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f }; ImageBufAlgo::fill (A, pixelvalue); A.write (filename); // Retrieve 2 pixels of [chbegin,chend), make sure we got the right values float p[2*nchans] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; OIIO_CHECK_ASSERT (imagecache->get_pixels (filename, 0, 0, 0, 2, 0, 1, 0, 1, // pixel range chbegin, chend, TypeDesc::FLOAT, p, AutoStride, AutoStride, AutoStride, cache_chbegin, cache_chend)); int nc = chend - chbegin; for (int x = 0; x < 2; ++x) { for (int c = 0; c < nc; ++c) { std::cout << ' ' << p[x*nc+c]; OIIO_CHECK_EQUAL (p[x*nc+c], pixelvalue[c+chbegin]); } std::cout << "\n"; } for (int c = 2*nc; c < 2*nchans; ++c) OIIO_CHECK_EQUAL (p[c], -1.0f); ImageCache::destroy (imagecache); } int main (int argc, char **argv) { test_get_pixels_cachechannels (0, 10); test_get_pixels_cachechannels (0, 4); test_get_pixels_cachechannels (0, 4, 0, 6); test_get_pixels_cachechannels (0, 4, 0, 4); test_get_pixels_cachechannels (6, 9); test_get_pixels_cachechannels (6, 9, 6, 9); return unit_test_failures; } openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/imagebufalgo_draw.cpp0000644000175000017500000006412513151711064024052 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/imagebufalgo_util.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/sysutil.h" #include "OpenImageIO/filter.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/hash.h" #ifdef USE_FREETYPE #include #include FT_FREETYPE_H #endif OIIO_NAMESPACE_BEGIN template static bool fill_const_ (ImageBuf &dst, const float *values, ROI roi=ROI(), int nthreads=1) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(fill_const_, OIIO::ref(dst), values, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case for (ImageBuf::Iterator p (dst, roi); !p.done(); ++p) for (int c = roi.chbegin; c < roi.chend; ++c) p[c] = values[c]; return true; } template static bool fill_tb_ (ImageBuf &dst, const float *top, const float *bottom, ROI origroi, ROI roi=ROI(), int nthreads=1) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(fill_tb_, OIIO::ref(dst), top, bottom, origroi, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case float h = std::max (1, origroi.height() - 1); for (ImageBuf::Iterator p (dst, roi); !p.done(); ++p) { float v = (p.y() - origroi.ybegin) / h; for (int c = roi.chbegin; c < roi.chend; ++c) p[c] = lerp (top[c], bottom[c], v); } return true; } template static bool fill_corners_ (ImageBuf &dst, const float *topleft, const float *topright, const float *bottomleft, const float *bottomright, ROI origroi, ROI roi=ROI(), int nthreads=1) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(fill_corners_, OIIO::ref(dst), topleft, topright, bottomleft, bottomright, origroi, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case float w = std::max (1, origroi.width() - 1); float h = std::max (1, origroi.height() - 1); for (ImageBuf::Iterator p (dst, roi); !p.done(); ++p) { float u = (p.x() - origroi.xbegin) / w; float v = (p.y() - origroi.ybegin) / h; for (int c = roi.chbegin; c < roi.chend; ++c) p[c] = bilerp (topleft[c], topright[c], bottomleft[c], bottomright[c], u, v); } return true; } bool ImageBufAlgo::fill (ImageBuf &dst, const float *pixel, ROI roi, int nthreads) { ASSERT (pixel && "fill must have a non-NULL pixel value pointer"); if (! IBAprep (roi, &dst)) return false; bool ok; OIIO_DISPATCH_TYPES (ok, "fill", fill_const_, dst.spec().format, dst, pixel, roi, nthreads); return ok; } bool ImageBufAlgo::fill (ImageBuf &dst, const float *top, const float *bottom, ROI roi, int nthreads) { ASSERT (top && bottom && "fill must have a non-NULL pixel value pointers"); if (! IBAprep (roi, &dst)) return false; bool ok; OIIO_DISPATCH_TYPES (ok, "fill", fill_tb_, dst.spec().format, dst, top, bottom, roi, roi, nthreads); return ok; } bool ImageBufAlgo::fill (ImageBuf &dst, const float *topleft, const float *topright, const float *bottomleft, const float *bottomright, ROI roi, int nthreads) { ASSERT (topleft && topright && bottomleft && bottomright && "fill must have a non-NULL pixel value pointers"); if (! IBAprep (roi, &dst)) return false; bool ok; OIIO_DISPATCH_TYPES (ok, "fill", fill_corners_, dst.spec().format, dst, topleft, topright, bottomleft, bottomright, roi, roi, nthreads); return ok; } bool ImageBufAlgo::zero (ImageBuf &dst, ROI roi, int nthreads) { if (! IBAprep (roi, &dst)) return false; float *zero = ALLOCA(float,roi.chend); memset (zero, 0, roi.chend*sizeof(float)); return fill (dst, zero, roi, nthreads); } template static bool render_point_ (ImageBuf &dst, int x, int y, array_view color, float alpha, ROI roi, int nthreads) { ImageBuf::Iterator r (dst, x, y); for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = color[c] + r[c] * (1.0f-alpha); // "over" return true; } bool ImageBufAlgo::render_point (ImageBuf &dst, int x, int y, array_view color, ROI roi, int nthreads) { if (! IBAprep (roi, &dst)) return false; if (int(color.size()) < roi.chend) { dst.error ("Not enough channels for the color (needed %d)", roi.chend); return false; // Not enough color channels specified } if (x < roi.xbegin || x >= roi.xend || y < roi.ybegin || y >= roi.yend) return true; // outside of bounds const ImageSpec &spec (dst.spec()); // Alpha: if the image's spec designates an alpha channel, use it if // it's within the range specified by color. Otherwise, if color // includes more values than the highest channel roi says we should // modify, assume the first extra value is alpha. If all else fails, // make the line opaque (alpha=1.0). float alpha = 1.0f; if (spec.alpha_channel >= 0 && spec.alpha_channel < int(color.size())) alpha = color[spec.alpha_channel]; else if (int(color.size()) == roi.chend+1) alpha = color[roi.chend]; bool ok; OIIO_DISPATCH_TYPES (ok, "render_point", render_point_, dst.spec().format, dst, x, y, color, alpha, roi, nthreads); return ok; } // Basic Bresenham 2D line drawing algorithm. Call func(x,y) for each x,y // along the line from (x1,y1) to (x2,y2). If skip_first is true, don't draw // the very first point. template static bool bresenham2d (FUNC func, int x1, int y1, int x2, int y2, bool skip_first=false) { // Basic Bresenham int dx = abs(x2 - x1); int dy = abs(y2 - y1); int xinc = (x1 > x2) ? -1 : 1; int yinc = (y1 > y2) ? -1 : 1; if (dx >= dy) { int dpr = dy << 1; int dpru = dpr - (dx << 1); int delta = dpr - dx; for (; dx >= 0; --dx) { if (skip_first) skip_first = false; else func (x1, y1); x1 += xinc; if (delta > 0) { y1 += yinc; delta += dpru; } else { delta += dpr; } } } else { int dpr = dx << 1; int dpru = dpr - (dy << 1); int delta = dpr - dy; for (; dy >= 0; dy--) { if (skip_first) skip_first = false; else func (x1, y1); y1 += yinc; if (delta > 0) { x1 += xinc; delta += dpru; } else { delta += dpr; } } } return true; } // Helper function that holds an IB::Iterator and composites a point into // it every time drawer(x,y) is called. template struct IB_drawer { IB_drawer (ImageBuf::Iterator &r_, array_view color_, float alpha_, ROI roi_) : r(r_), color(color_), alpha(alpha_), roi(roi_) {} void operator() (int x, int y) { r.pos (x, y); if (r.valid()) for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = color[c] + r[c] * (1.0f-alpha); // "over" } ImageBuf::Iterator &r; array_view color; float alpha; ROI roi; }; template static bool render_line_ (ImageBuf &dst, int x1, int y1, int x2, int y2, array_view color, float alpha, bool skip_first, ROI roi, int nthreads) { ImageBuf::Iterator r (dst, roi); IB_drawer draw (r, color, alpha, roi); bresenham2d (draw, x1, y1, x2, y2, skip_first); return true; } bool ImageBufAlgo::render_line (ImageBuf &dst, int x1, int y1, int x2, int y2, array_view color, bool skip_first_point, ROI roi, int nthreads) { if (! IBAprep (roi, &dst)) return false; if (int(color.size()) < roi.chend) { dst.error ("Not enough channels for the color (needed %d)", roi.chend); return false; // Not enough color channels specified } const ImageSpec &spec (dst.spec()); // Alpha: if the image's spec designates an alpha channel, use it if // it's within the range specified by color. Otherwise, if color // includes more values than the highest channel roi says we should // modify, assume the first extra value is alpha. If all else fails, // make the line opaque (alpha=1.0). float alpha = 1.0f; if (spec.alpha_channel >= 0 && spec.alpha_channel < int(color.size())) alpha = color[spec.alpha_channel]; else if (int(color.size()) == roi.chend+1) alpha = color[roi.chend]; bool ok; OIIO_DISPATCH_TYPES (ok, "render_line", render_line_, dst.spec().format, dst, x1, y1, x2, y2, color, alpha, skip_first_point, roi, nthreads); return ok; } template static bool render_box_ (ImageBuf &dst, array_view color, ROI roi=ROI(), int nthreads=1) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(render_box_, OIIO::ref(dst), color, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case float alpha = 1.0f; if (dst.spec().alpha_channel >= 0 && dst.spec().alpha_channel < int(color.size())) alpha = color[dst.spec().alpha_channel]; else if (int(color.size()) == roi.chend+1) alpha = color[roi.chend]; if (alpha == 1.0f) { for (ImageBuf::Iterator r (dst, roi); !r.done(); ++r) for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = color[c]; } else { for (ImageBuf::Iterator r (dst, roi); !r.done(); ++r) for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = color[c] + r[c] * (1.0f-alpha); // "over" } return true; } bool ImageBufAlgo::render_box (ImageBuf &dst, int x1, int y1, int x2, int y2, array_view color, bool fill, ROI roi, int nthreads) { if (! IBAprep (roi, &dst)) return false; if (int(color.size()) < roi.chend) { dst.error ("Not enough channels for the color (needed %d)", roi.chend); return false; // Not enough color channels specified } if (x1 == x2 && y1 == y2) { // degenerate 1-point rectangle return render_point (dst, x1, y1, color, roi, nthreads); } // Filled case if (fill) { roi = roi_intersection (roi, ROI(x1, x2+1, y1, y2+1, 0, 1, 0, roi.chend)); bool ok; OIIO_DISPATCH_TYPES (ok, "render_box", render_box_, dst.spec().format, dst, color, roi, nthreads); return ok; } // Unfilled case: use IBA::render_line return ImageBufAlgo::render_line (dst, x1, y1, x2, y1, color,true, roi, nthreads) && ImageBufAlgo::render_line (dst, x2, y1, x2, y2, color,true, roi, nthreads) && ImageBufAlgo::render_line (dst, x2, y2, x1, y2, color,true, roi, nthreads) && ImageBufAlgo::render_line (dst, x1, y2, x1, y1, color,true, roi, nthreads); } // Convenient helper struct to bundle a 3-int describing a block size. struct Dim3 { int x, y, z; Dim3 (int x, int y=1, int z=1) : x(x), y(y), z(z) { } }; template static bool checker_ (ImageBuf &dst, Dim3 size, const float *color1, const float *color2, Dim3 offset, ROI roi, int nthreads=1) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(checker_, OIIO::ref(dst), size, color1, color2, offset, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case for (ImageBuf::Iterator p (dst, roi); !p.done(); ++p) { int xtile = (p.x()-offset.x)/size.x; xtile += (p.x() 1.0 || r2 == 0.0); float M = sqrt(-2.0 * log(r2) / r2); return xr * M; } template static bool noise_uniform_ (ImageBuf &dst, float min, float max, bool mono, int seed, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(noise_uniform_, OIIO::ref(dst), min, max, mono, seed, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case for (ImageBuf::Iterator p (dst, roi); !p.done(); ++p) { int x = p.x(), y = p.y(), z = p.z(); float n = 0.0; for (int c = roi.chbegin; c < roi.chend; ++c) { if (c == roi.chbegin || !mono) n = lerp (min, max, hashrand (x, y, z, c, seed)); p[c] = p[c] + n; } } return true; } template static bool noise_gaussian_ (ImageBuf &dst, float mean, float stddev, bool mono, int seed, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(noise_gaussian_, OIIO::ref(dst), mean, stddev, mono, seed, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case for (ImageBuf::Iterator p (dst, roi); !p.done(); ++p) { int x = p.x(), y = p.y(), z = p.z(); float n = 0.0; for (int c = roi.chbegin; c < roi.chend; ++c) { if (c == roi.chbegin || !mono) n = mean + stddev * hashnormal (x, y, z, c, seed); p[c] = p[c] + n; } } return true; } template static bool noise_salt_ (ImageBuf &dst, float saltval, float saltportion, bool mono, int seed, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(noise_salt_, OIIO::ref(dst), saltval, saltportion, mono, seed, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case for (ImageBuf::Iterator p (dst, roi); !p.done(); ++p) { int x = p.x(), y = p.y(), z = p.z(); float n = 0.0; for (int c = roi.chbegin; c < roi.chend; ++c) { if (c == roi.chbegin || !mono) n = hashrand (x, y, z, c, seed); if (n < saltportion) p[c] = saltval; } } return true; } bool ImageBufAlgo::noise (ImageBuf &dst, string_view noisetype, float A, float B, bool mono, int seed, ROI roi, int nthreads) { if (! IBAprep (roi, &dst)) return false; bool ok; if (noisetype == "gaussian" || noisetype == "normal") { OIIO_DISPATCH_TYPES (ok, "noise_gaussian", noise_gaussian_, dst.spec().format, dst, A, B, mono, seed, roi, nthreads); } else if (noisetype == "uniform") { OIIO_DISPATCH_TYPES (ok, "noise_uniform", noise_uniform_, dst.spec().format, dst, A, B, mono, seed, roi, nthreads); } else if (noisetype == "salt") { OIIO_DISPATCH_TYPES (ok, "noise_salt", noise_salt_, dst.spec().format, dst, A, B, mono, seed, roi, nthreads); } else { ok = false; dst.error ("noise", "unknown noise type \"%s\"", noisetype); } return ok; } #ifdef USE_FREETYPE namespace { // anon static mutex ft_mutex; static FT_Library ft_library = NULL; static bool ft_broken = false; static const char * default_font_name[] = { "DroidSans", "cour", "Courier New", "FreeMono", NULL }; } // anon namespace #endif bool ImageBufAlgo::render_text (ImageBuf &R, int x, int y, string_view text, int fontsize, string_view font_, const float *textcolor) { if (R.spec().depth > 1) { R.error ("ImageBufAlgo::render_text does not support volume images"); return false; } #ifdef USE_FREETYPE // If we know FT is broken, don't bother trying again if (ft_broken) return false; // Thread safety lock_guard ft_lock (ft_mutex); int error = 0; // If FT not yet initialized, do it now. if (! ft_library) { error = FT_Init_FreeType (&ft_library); if (error) { ft_broken = true; R.error ("Could not initialize FreeType for font rendering"); return false; } } // A set of likely directories for fonts to live, across several systems. std::vector search_dirs; const char *home = getenv ("HOME"); if (home && *home) { std::string h (home); search_dirs.push_back (h + "/fonts"); search_dirs.push_back (h + "/Fonts"); search_dirs.push_back (h + "/Library/Fonts"); } const char *systemRoot = getenv ("SystemRoot"); if (systemRoot && *systemRoot) search_dirs.push_back (std::string(systemRoot) + "/Fonts"); search_dirs.push_back ("/usr/share/fonts"); search_dirs.push_back ("/Library/Fonts"); search_dirs.push_back ("C:/Windows/Fonts"); search_dirs.push_back ("/usr/local/share/fonts"); search_dirs.push_back ("/opt/local/share/fonts"); // Try $OPENIMAGEIOHOME/fonts const char *oiiohomedir = getenv ("OPENIMAGEIOHOME"); if (oiiohomedir && *oiiohomedir) search_dirs.push_back (std::string(oiiohomedir) + "/fonts"); // Try ../fonts relative to where this executing binary came from std::string this_program = OIIO::Sysutil::this_program_path (); if (this_program.size()) { std::string path = Filesystem::parent_path (this_program); path = Filesystem::parent_path (path); search_dirs.push_back (path+"/fonts"); } // Try to find the font. Experiment with several extensions std::string font = font_; if (font.empty()) { // nothing specified -- look for something to use as a default. for (int j = 0; default_font_name[j] && font.empty(); ++j) { static const char *extensions[] = { "", ".ttf", ".pfa", ".pfb", NULL }; for (int i = 0; font.empty() && extensions[i]; ++i) font = Filesystem::searchpath_find (std::string(default_font_name[j])+extensions[i], search_dirs, true, true); } if (font.empty()) { R.error ("Could not set default font face"); return false; } } else if (Filesystem::is_regular (font)) { // directly specified a filename -- use it } else { // A font name was specified but it's not a full path, look for it std::string f; static const char *extensions[] = { "", ".ttf", ".pfa", ".pfb", NULL }; for (int i = 0; f.empty() && extensions[i]; ++i) f = Filesystem::searchpath_find (font+extensions[i], search_dirs, true, true); if (f.empty()) { R.error ("Could not set font face to \"%s\"", font); return false; } font = f; } ASSERT (! font.empty()); if (! Filesystem::is_regular (font)) { R.error ("Could not find font \"%s\"", font); return false; } FT_Face face; // handle to face object error = FT_New_Face (ft_library, font.c_str(), 0 /* face index */, &face); if (error) { R.error ("Could not set font face to \"%s\"", font); return false; // couldn't open the face } error = FT_Set_Pixel_Sizes (face, // handle to face object 0, // pixel_width fontsize); // pixel_heigh if (error) { FT_Done_Face (face); R.error ("Could not set font size to %d", fontsize); return false; // couldn't set the character size } FT_GlyphSlot slot = face->glyph; // a small shortcut int nchannels = R.spec().nchannels; float *pixelcolor = ALLOCA (float, nchannels); if (! textcolor) { float *localtextcolor = ALLOCA (float, nchannels); for (int c = 0; c < nchannels; ++c) localtextcolor[c] = 1.0f; textcolor = localtextcolor; } std::vector utext; utext.reserve(text.size()); //Possible overcommit, but most text will be ascii Strutil::utf8_to_unicode(text, utext); for (size_t n = 0, e = utext.size(); n < e; ++n) { error = FT_Load_Char (face, utext[n], FT_LOAD_RENDER); if (error) continue; // ignore errors // now, draw to our target surface for (int j = 0; j < static_cast(slot->bitmap.rows); ++j) { int ry = y + j - slot->bitmap_top; for (int i = 0; i < static_cast(slot->bitmap.width); ++i) { int rx = x + i + slot->bitmap_left; float b = slot->bitmap.buffer[slot->bitmap.pitch*j+i] / 255.0f; R.getpixel (rx, ry, pixelcolor); for (int c = 0; c < nchannels; ++c) pixelcolor[c] = b*textcolor[c] + (1.0f-b) * pixelcolor[c]; R.setpixel (rx, ry, pixelcolor); } } // increment pen position x += slot->advance.x >> 6; } FT_Done_Face (face); return true; #else R.error ("OpenImageIO was not compiled with FreeType for font rendering"); return false; // Font rendering not supported #endif } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/imagebufalgo_xform.cpp0000644000175000017500000007643713151711064024261 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// \file /// ImageBufAlgo functions for filtered transformations #include #include #include #include #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/imagebufalgo_util.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/filter.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/refcnt.h" OIIO_NAMESPACE_BEGIN namespace { // Poor man's Dual2 makes it easy to compute with differentials. For // a rich man's implementation and full documentation, see // OpenShadingLanguage (dual2.h). class Dual2 { public: float val() const { return m_val; } float dx() const { return m_dx; } float dy() const { return m_dy; } Dual2 (float val) : m_val(val), m_dx(0.0f), m_dy(0.0f) {} Dual2 (float val, float dx, float dy) : m_val(val), m_dx(dx), m_dy(dy) {} Dual2& operator= (float f) { m_val = f; m_dx = 0.0f; m_dy = 0.0f; return *this; } friend Dual2 operator+ (const Dual2 &a, const Dual2 &b) { return Dual2 (a.m_val+b.m_val, a.m_dx+b.m_dx, a.m_dy+b.m_dy); } friend Dual2 operator+ (const Dual2 &a, float b) { return Dual2 (a.m_val+b, a.m_dx, a.m_dy); } friend Dual2 operator* (const Dual2 &a, float b) { return Dual2 (a.m_val*b, a.m_dx*b, a.m_dy*b); } friend Dual2 operator* (const Dual2 &a, const Dual2 &b) { // Use the chain rule return Dual2 (a.m_val*b.m_val, a.m_val*b.m_dx + a.m_dx*b.m_val, a.m_val*b.m_dy + a.m_dy*b.m_val); } friend Dual2 operator/ (const Dual2 &a, const Dual2 &b) { float bvalinv = 1.0f / b.m_val; float aval_bval = a.m_val * bvalinv; return Dual2 (aval_bval, bvalinv * (a.m_dx - aval_bval * b.m_dx), bvalinv * (a.m_dy - aval_bval * b.m_dy)); } private: float m_val, m_dx, m_dy; }; /// Transform a 2D point (x,y) with derivatives by a 3x3 affine matrix to /// obtain a transformed point with derivatives. inline void robust_multVecMatrix (const Imath::M33f &M, const Dual2 &x, const Dual2 &y, Dual2 &outx, Dual2 &outy) { Dual2 a = x * M[0][0] + y * M[1][0] + M[2][0]; Dual2 b = x * M[0][1] + y * M[1][1] + M[2][1]; Dual2 w = x * M[0][2] + y * M[1][2] + M[2][2]; if (w.val() != 0.0f) { outx = a / w; outy = b / w; } else { outx = 0.0f; outy = 0.0f; } } // Transform an ROI by an affine matrix. ROI transform (const Imath::M33f &M, ROI roi) { Imath::V2f ul (roi.xbegin+0.5f, roi.ybegin+0.5f); Imath::V2f ur (roi.xend-0.5f, roi.ybegin+0.5f); Imath::V2f ll (roi.xbegin+0.5f, roi.yend-0.5f); Imath::V2f lr (roi.xend-0.5f, roi.yend-0.5f); M.multVecMatrix (ul, ul); M.multVecMatrix (ur, ur); M.multVecMatrix (ll, ll); M.multVecMatrix (lr, lr); Imath::Box2f box (ul); box.extendBy (ll); box.extendBy (ur); box.extendBy (lr); int xmin = int (floorf(box.min.x)); int ymin = int (floorf(box.min.y)); int xmax = int (floorf(box.max.x)) + 1; int ymax = int (floorf(box.max.y)) + 1; return ROI (xmin, xmax, ymin, ymax, roi.zbegin, roi.zend, roi.chbegin, roi.chend); } // Given s,t image space coordinates and their derivatives, compute a // filtered sample using the derivatives to guide the size of the filter // footprint. template inline void filtered_sample (const ImageBuf &src, float s, float t, float dsdx, float dtdx, float dsdy, float dtdy, const Filter2D *filter, ImageBuf::WrapMode wrap, float *result) { DASSERT (filter); // Just use isotropic filtering float ds = std::max (1.0f, std::max (fabsf(dsdx), fabsf(dsdy))); float dt = std::max (1.0f, std::max (fabsf(dtdx), fabsf(dtdy))); float ds_inv = 1.0f / ds; float dt_inv = 1.0f / dt; float filterrad_s = 0.5f * ds * filter->width(); float filterrad_t = 0.5f * dt * filter->width(); ImageBuf::ConstIterator samp (src, (int)floorf(s-filterrad_s), (int)ceilf(s+filterrad_s), (int)floorf(t-filterrad_t), (int)ceilf(t+filterrad_t), 0, 1, wrap); int nc = src.nchannels(); float *sum = ALLOCA (float, nc); memset (sum, 0, nc*sizeof(float)); float total_w = 0.0f; for ( ; ! samp.done(); ++samp) { float w = (*filter) (ds_inv*(samp.x()+0.5f-s), dt_inv*(samp.y()+0.5f-t)); for (int c = 0; c < nc; ++c) sum[c] += w * samp[c]; total_w += w; } if (total_w != 0.0f) for (int c = 0; c < nc; ++c) result[c] = sum[c] / total_w; else for (int c = 0; c < nc; ++c) result[c] = 0.0f; } } // end anon namespace template static bool resize_ (ImageBuf &dst, const ImageBuf &src, Filter2D *filter, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(resize_, OIIO::ref(dst), OIIO::cref(src), filter, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case const ImageSpec &srcspec (src.spec()); const ImageSpec &dstspec (dst.spec()); int nchannels = dstspec.nchannels; // Local copies of the source image window, converted to float float srcfx = srcspec.full_x; float srcfy = srcspec.full_y; float srcfw = srcspec.full_width; float srcfh = srcspec.full_height; // Ratios of dst/src size. Values larger than 1 indicate that we // are maximizing (enlarging the image), and thus want to smoothly // interpolate. Values less than 1 indicate that we are minimizing // (shrinking the image), and thus want to properly filter out the // high frequencies. float xratio = float(dstspec.full_width) / srcfw; // 2 upsize, 0.5 downsize float yratio = float(dstspec.full_height) / srcfh; float dstfx = float (dstspec.full_x); float dstfy = float (dstspec.full_y); float dstfw = float (dstspec.full_width); float dstfh = float (dstspec.full_height); float dstpixelwidth = 1.0f / dstfw; float dstpixelheight = 1.0f / dstfh; float *pel = ALLOCA (float, nchannels); float filterrad = filter->width() / 2.0f; // radi,radj is the filter radius, as an integer, in source pixels. We // will filter the source over [x-radi, x+radi] X [y-radj,y+radj]. int radi = (int) ceilf (filterrad/xratio); int radj = (int) ceilf (filterrad/yratio); int xtaps = 2*radi + 1; int ytaps = 2*radj + 1; bool separable = filter->separable(); float *yfiltval = ALLOCA (float, ytaps); float *xfiltval_all = NULL; if (separable) { // For separable filters, horizontal tap weights will be the same // for every column. So we precompute all the tap weights for every // x position we'll need. We do the same thing in y, but row by row // inside the loop (since we never revisit a y row). This // substantially speeds up resize. xfiltval_all = ALLOCA (float, xtaps * roi.width()); for (int x = roi.xbegin; x < roi.xend; ++x) { float *xfiltval = xfiltval_all + (x-roi.xbegin) * xtaps; float s = (x-dstfx+0.5f)*dstpixelwidth; float src_xf = srcfx + s * srcfw; int src_x; float src_xf_frac = floorfrac (src_xf, &src_x); for (int c = 0; c < nchannels; ++c) pel[c] = 0.0f; float totalweight_x = 0.0f; for (int i = 0; i < xtaps; ++i) { float w = filter->xfilt (xratio * (i-radi-(src_xf_frac-0.5f))); xfiltval[i] = w; totalweight_x += w; } if (totalweight_x != 0.0f) for (int i = 0; i < xtaps; ++i) // normalize x filter xfiltval[i] /= totalweight_x; // weights } } #if 0 std::cerr << "Resizing " << srcspec.full_width << "x" << srcspec.full_height << " to " << dstspec.full_width << "x" << dstspec.full_height << "\n"; std::cerr << "ratios = " << xratio << ", " << yratio << "\n"; std::cerr << "examining src filter " << filter->name() << " support radius of " << radi << " x " << radj << " pixels\n"; std::cout << " " << xtaps << "x" << ytaps << " filter taps\n"; std::cerr << "dst range " << roi << "\n"; std::cerr << "separable filter\n"; #endif #define USE_SPECIAL 0 #if USE_SPECIAL // Special case: src and dst are local memory, float buffers, and we're // operating on all channels, <= 4. bool special = ( (is_same::value || is_same::value) && (is_same::value || is_same::value) // && dst.localpixels() // has to be, because it's writeable && src.localpixels() // && R.contains_roi(roi) // has to be, because IBAPrep && src.contains_roi(roi) && roi.chbegin == 0 && roi.chend == dst.nchannels() && roi.chend == src.nchannels() && roi.chend <= 4 && separable); #endif // We're going to loop over all output pixels we're interested in. // // (s,t) = NDC space coordinates of the output sample we are computing. // This is the "sample point". // (src_xf, src_xf) = source pixel space float coordinates of the // sample we're computing. We want to compute the weighted sum // of all the source image pixels that fall under the filter when // centered at that location. // (src_x, src_y) = image space integer coordinates of the floor, // i.e., the closest pixel in the source image. // src_xf_frac and src_yf_frac are the position within that pixel // of our sample. // // Separate cases for separable and non-separable filters. if (separable) { ImageBuf::Iterator out (dst, roi); ImageBuf::ConstIterator srcpel (src, ImageBuf::WrapClamp); for (int y = roi.ybegin; y < roi.yend; ++y) { float t = (y-dstfy+0.5f)*dstpixelheight; float src_yf = srcfy + t * srcfh; int src_y; float src_yf_frac = floorfrac (src_yf, &src_y); // If using separable filters, our vertical set of filter tap // weights will be the same for the whole scanline we're on. Just // compute and normalize them once. float totalweight_y = 0.0f; for (int j = 0; j < ytaps; ++j) { float w = filter->yfilt (yratio * (j-radj-(src_yf_frac-0.5f))); yfiltval[j] = w; totalweight_y += w; } if (totalweight_y != 0.0f) for (int i = 0; i < ytaps; ++i) yfiltval[i] /= totalweight_y; for (int x = roi.xbegin; x < roi.xend; ++x, ++out) { float s = (x-dstfx+0.5f)*dstpixelwidth; float src_xf = srcfx + s * srcfw; int src_x = ifloor (src_xf); for (int c = 0; c < nchannels; ++c) pel[c] = 0.0f; const float *xfiltval = xfiltval_all + (x-roi.xbegin) * xtaps; float totalweight_x = 0.0f; for (int i = 0; i < xtaps; ++i) totalweight_x += xfiltval[i]; if (totalweight_x != 0.0f) { srcpel.rerange (src_x-radi, src_x+radi+1, src_y-radj, src_y+radj+1, 0, 1, ImageBuf::WrapClamp); for (int j = -radj; j <= radj; ++j) { float wy = yfiltval[j+radj]; if (wy == 0.0f) { // 0 weight for this y tap -- move to next line srcpel.pos (srcpel.x(), srcpel.y()+1, srcpel.z()); continue; } for (int i = 0; i < xtaps; ++i, ++srcpel) { float w = wy * xfiltval[i]; if (w) for (int c = 0; c < nchannels; ++c) pel[c] += w * srcpel[c]; } } } // Copy the pixel value (already normalized) to the output. DASSERT (out.x() == x && out.y() == y); if (totalweight_y == 0.0f) { // zero it out for (int c = 0; c < nchannels; ++c) out[c] = 0.0f; } else { for (int c = 0; c < nchannels; ++c) out[c] = pel[c]; } } } } else { // Non-separable filter ImageBuf::Iterator out (dst, roi); ImageBuf::ConstIterator srcpel (src, ImageBuf::WrapClamp); for (int y = roi.ybegin; y < roi.yend; ++y) { float t = (y-dstfy+0.5f)*dstpixelheight; float src_yf = srcfy + t * srcfh; int src_y; float src_yf_frac = floorfrac (src_yf, &src_y); for (int x = roi.xbegin; x < roi.xend; ++x, ++out) { float s = (x-dstfx+0.5f)*dstpixelwidth; float src_xf = srcfx + s * srcfw; int src_x; float src_xf_frac = floorfrac (src_xf, &src_x); for (int c = 0; c < nchannels; ++c) pel[c] = 0.0f; float totalweight = 0.0f; srcpel.rerange (src_x-radi, src_x+radi+1, src_y-radi, src_y+radi+1, 0, 1, ImageBuf::WrapClamp); for (int j = -radj; j <= radj; ++j) { for (int i = -radi; i <= radi; ++i, ++srcpel) { DASSERT (! srcpel.done()); float w = (*filter)(xratio * (i-(src_xf_frac-0.5f)), yratio * (j-(src_yf_frac-0.5f))); if (w) { totalweight += w; for (int c = 0; c < nchannels; ++c) pel[c] += w * srcpel[c]; } } } DASSERT (srcpel.done()); // Rescale pel to normalize the filter and write it to the // output image. DASSERT (out.x() == x && out.y() == y); if (totalweight == 0.0f) { // zero it out for (int c = 0; c < nchannels; ++c) out[c] = 0.0f; } else { for (int c = 0; c < nchannels; ++c) out[c] = pel[c] / totalweight; } } } } return true; } bool ImageBufAlgo::resize (ImageBuf &dst, const ImageBuf &src, Filter2D *filter, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &src, IBAprep_REQUIRE_SAME_NCHANNELS | IBAprep_NO_SUPPORT_VOLUME | IBAprep_NO_COPY_ROI_FULL)) return false; // Set up a shared pointer with custom deleter to make sure any // filter we allocate here is properly destroyed. OIIO::shared_ptr filterptr ((Filter2D*)NULL, Filter2D::destroy); bool allocfilter = (filter == NULL); if (allocfilter) { // If no filter was provided, punt and just linearly interpolate. const ImageSpec &srcspec (src.spec()); const ImageSpec &dstspec (dst.spec()); float wratio = float(dstspec.full_width) / float(srcspec.full_width); float hratio = float(dstspec.full_height) / float(srcspec.full_height); float w = 2.0f * std::max (1.0f, wratio); float h = 2.0f * std::max (1.0f, hratio); filter = Filter2D::create ("triangle", w, h); filterptr.reset (filter); } bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "resize", resize_, dst.spec().format, src.spec().format, dst, src, filter, roi, nthreads); return ok; } bool ImageBufAlgo::resize (ImageBuf &dst, const ImageBuf &src, string_view filtername_, float fwidth, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &src, IBAprep_REQUIRE_SAME_NCHANNELS | IBAprep_NO_SUPPORT_VOLUME | IBAprep_NO_COPY_ROI_FULL)) return false; const ImageSpec &srcspec (src.spec()); const ImageSpec &dstspec (dst.spec()); // Resize ratios float wratio = float(dstspec.full_width) / float(srcspec.full_width); float hratio = float(dstspec.full_height) / float(srcspec.full_height); // Set up a shared pointer with custom deleter to make sure any // filter we allocate here is properly destroyed. OIIO::shared_ptr filter ((Filter2D*)NULL, Filter2D::destroy); std::string filtername = filtername_; if (filtername.empty()) { // No filter name supplied -- pick a good default if (wratio > 1.0f || hratio > 1.0f) filtername = "blackman-harris"; else filtername = "lanczos3"; } for (int i = 0, e = Filter2D::num_filters(); i < e; ++i) { FilterDesc fd; Filter2D::get_filterdesc (i, &fd); if (fd.name == filtername) { float w = fwidth > 0.0f ? fwidth : fd.width * std::max (1.0f, wratio); float h = fwidth > 0.0f ? fwidth : fd.width * std::max (1.0f, hratio); filter.reset (Filter2D::create (filtername, w, h)); break; } } if (! filter) { dst.error ("Filter \"%s\" not recognized", filtername); return false; } bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "resize", resize_, dstspec.format, srcspec.format, dst, src, filter.get(), roi, nthreads); return ok; } template static bool resample_ (ImageBuf &dst, const ImageBuf &src, bool interpolate, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(resample_, OIIO::ref(dst), OIIO::cref(src), interpolate, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case const ImageSpec &srcspec (src.spec()); const ImageSpec &dstspec (dst.spec()); int nchannels = src.nchannels(); bool deep = src.deep(); ASSERT (deep == dst.deep()); // Local copies of the source image window, converted to float float srcfx = srcspec.full_x; float srcfy = srcspec.full_y; float srcfw = srcspec.full_width; float srcfh = srcspec.full_height; float dstfx = dstspec.full_x; float dstfy = dstspec.full_y; float dstfw = dstspec.full_width; float dstfh = dstspec.full_height; float dstpixelwidth = 1.0f / dstfw; float dstpixelheight = 1.0f / dstfh; float *pel = ALLOCA (float, nchannels); ImageBuf::Iterator out (dst, roi); ImageBuf::ConstIterator srcpel (src); for (int y = roi.ybegin; y < roi.yend; ++y) { // s,t are NDC space float t = (y-dstfy+0.5f)*dstpixelheight; // src_xf, src_xf are image space float coordinates float src_yf = srcfy + t * srcfh - 0.5f; // src_x, src_y are image space integer coordinates of the floor int src_y; (void) floorfrac (src_yf, &src_y); for (int x = roi.xbegin; x < roi.xend; ++x, ++out) { float s = (x-dstfx+0.5f)*dstpixelwidth; float src_xf = srcfx + s * srcfw - 0.5f; int src_x; (void) floorfrac (src_xf, &src_x); if (deep) { srcpel.pos (src_x, src_y, 0); int nsamps = srcpel.deep_samples(); ASSERT (nsamps == out.deep_samples()); if (! nsamps) continue; for (int c = 0; c < nchannels; ++c) { if (dstspec.channelformat(c) == TypeDesc::UINT32) for (int samp = 0; samp < nsamps; ++samp) out.set_deep_value (c, samp, srcpel.deep_value_uint(c, samp)); else for (int samp = 0; samp < nsamps; ++samp) out.set_deep_value (c, samp, srcpel.deep_value(c, samp)); } } else if (interpolate) { src.interppixel (src_xf, src_yf, pel); for (int c = roi.chbegin; c < roi.chend; ++c) out[c] = pel[c]; } else { srcpel.pos (src_x, src_y, 0); for (int c = roi.chbegin; c < roi.chend; ++c) out[c] = srcpel[c]; } } } return true; } bool ImageBufAlgo::resample (ImageBuf &dst, const ImageBuf &src, bool interpolate, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &src, IBAprep_REQUIRE_SAME_NCHANNELS | IBAprep_NO_SUPPORT_VOLUME | IBAprep_NO_COPY_ROI_FULL | IBAprep_SUPPORT_DEEP)) return false; if (dst.deep()) { // If it's deep, figure out the sample allocations first, because // it's not thread-safe to do that simultaneously with copying the // values. const ImageSpec &srcspec (src.spec()); const ImageSpec &dstspec (dst.spec()); float srcfx = srcspec.full_x; float srcfy = srcspec.full_y; float srcfw = srcspec.full_width; float srcfh = srcspec.full_height; float dstpixelwidth = 1.0f / dstspec.full_width; float dstpixelheight = 1.0f / dstspec.full_height; ImageBuf::ConstIterator srcpel (src, roi); ImageBuf::Iterator dstpel (dst, roi); for ( ; !dstpel.done(); ++dstpel, ++srcpel) { float s = (dstpel.x()-dstspec.full_x+0.5f)*dstpixelwidth; float t = (dstpel.y()-dstspec.full_y+0.5f)*dstpixelheight; int src_y = ifloor (srcfy + t * srcfh - 0.5f); int src_x = ifloor (srcfx + s * srcfw - 0.5f); srcpel.pos (src_x, src_y, 0); dstpel.set_deep_samples (srcpel.deep_samples ()); } } bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "resample", resample_, dst.spec().format, src.spec().format, dst, src, interpolate, roi, nthreads); return ok; } #if 0 template static bool affine_resample_ (ImageBuf &dst, const ImageBuf &src, const Imath::M33f &Minv, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(affine_resample_, OIIO::ref(dst), OIIO::cref(src), Minv, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case ImageBuf::Iterator d (dst, roi); ImageBuf::ConstIterator s (src); for ( ; ! d.done(); ++d) { Imath::V2f P (d.x()+0.5f, d.y()+0.5f); Minv.multVecMatrix (P, P); s.pos (int(floorf(P.x)), int(floorf(P.y)), d.z()); for (int c = roi.chbegin; c < roi.chend; ++c) d[c] = s[c]; } return true; } #endif template static bool warp_ (ImageBuf &dst, const ImageBuf &src, const Imath::M33f &M, const Filter2D *filter, ImageBuf::WrapMode wrap, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(warp_, OIIO::ref(dst), OIIO::cref(src), M, filter, wrap, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case int nc = dst.nchannels(); float *pel = ALLOCA (float, nc); memset (pel, 0, nc*sizeof(float)); Imath::M33f Minv = M.inverse(); ImageBuf::Iterator out (dst, roi); for ( ; ! out.done(); ++out) { Dual2 x (out.x()+0.5f, 1.0f, 0.0f); Dual2 y (out.y()+0.5f, 0.0f, 1.0f); robust_multVecMatrix (Minv, x, y, x, y); filtered_sample (src, x.val(), y.val(), x.dx(), y.dx(), x.dy(), y.dy(), filter, wrap, pel); for (int c = roi.chbegin; c < roi.chend; ++c) out[c] = pel[c]; } return true; } bool ImageBufAlgo::warp (ImageBuf &dst, const ImageBuf &src, const Imath::M33f &M, const Filter2D *filter, bool recompute_roi, ImageBuf::WrapMode wrap, ROI roi, int nthreads) { ROI src_roi_full = src.roi_full(); ROI dst_roi, dst_roi_full; if (dst.initialized()) { dst_roi = roi.defined() ? roi : dst.roi(); dst_roi_full = dst.roi_full(); } else { dst_roi = roi.defined() ? roi : (recompute_roi ? transform (M, src.roi()) : src.roi()); dst_roi_full = src_roi_full; } dst_roi.chend = std::min (dst_roi.chend, src.nchannels()); dst_roi_full.chend = std::min (dst_roi_full.chend, src.nchannels()); if (! IBAprep (dst_roi, &dst, &src, IBAprep_NO_SUPPORT_VOLUME)) return false; // Set up a shared pointer with custom deleter to make sure any // filter we allocate here is properly destroyed. OIIO::shared_ptr filterptr ((Filter2D*)NULL, Filter2D::destroy); if (filter == NULL) { // If no filter was provided, punt and just linearly interpolate. filterptr.reset (Filter2D::create ("lanczos3", 6.0f, 6.0f)); filter = filterptr.get(); } bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "warp", warp_, dst.spec().format, src.spec().format, dst, src, M, filter, wrap, dst_roi, nthreads); return ok; } bool ImageBufAlgo::warp (ImageBuf &dst, const ImageBuf &src, const Imath::M33f &M, string_view filtername_, float filterwidth, bool recompute_roi, ImageBuf::WrapMode wrap, ROI roi, int nthreads) { // Set up a shared pointer with custom deleter to make sure any // filter we allocate here is properly destroyed. OIIO::shared_ptr filter ((Filter2D*)NULL, Filter2D::destroy); std::string filtername = filtername_.size() ? filtername_ : "lanczos3"; for (int i = 0, e = Filter2D::num_filters(); i < e; ++i) { FilterDesc fd; Filter2D::get_filterdesc (i, &fd); if (fd.name == filtername) { float w = filterwidth > 0.0f ? filterwidth : fd.width; float h = filterwidth > 0.0f ? filterwidth : fd.width; filter.reset (Filter2D::create (filtername, w, h)); break; } } if (! filter) { dst.error ("Filter \"%s\" not recognized", filtername); return false; } return warp (dst, src, M, filter.get(), recompute_roi, wrap, roi, nthreads); } bool ImageBufAlgo::rotate (ImageBuf &dst, const ImageBuf &src, float angle, float center_x, float center_y, Filter2D *filter, bool recompute_roi, ROI roi, int nthreads) { // Calculate the rotation matrix Imath::M33f M; M.translate(Imath::V2f(-center_x, -center_y)); M.rotate(angle); M *= Imath::M33f().translate(Imath::V2f(center_x, center_y)); return ImageBufAlgo::warp (dst, src, M, filter, recompute_roi, ImageBuf::WrapBlack, roi, nthreads); } bool ImageBufAlgo::rotate (ImageBuf &dst, const ImageBuf &src, float angle, float center_x, float center_y, string_view filtername, float filterwidth, bool recompute_roi, ROI roi, int nthreads) { // Calculate the rotation matrix Imath::M33f M; M.translate(Imath::V2f(-center_x, -center_y)); M.rotate(angle); M *= Imath::M33f().translate(Imath::V2f(center_x, center_y)); return ImageBufAlgo::warp (dst, src, M, filtername, filterwidth, recompute_roi, ImageBuf::WrapBlack, roi, nthreads); } bool ImageBufAlgo::rotate (ImageBuf &dst, const ImageBuf &src, float angle, Filter2D *filter, bool recompute_roi, ROI roi, int nthreads) { ROI src_roi_full = src.roi_full(); float center_x = 0.5f * (src_roi_full.xbegin + src_roi_full.xend); float center_y = 0.5f * (src_roi_full.ybegin + src_roi_full.yend); return ImageBufAlgo::rotate (dst, src, angle, center_x, center_y, filter, recompute_roi, roi, nthreads); } bool ImageBufAlgo::rotate (ImageBuf &dst, const ImageBuf &src, float angle, string_view filtername, float filterwidth, bool recompute_roi, ROI roi, int nthreads) { ROI src_roi_full = src.roi_full(); float center_x = 0.5f * (src_roi_full.xbegin + src_roi_full.xend); float center_y = 0.5f * (src_roi_full.ybegin + src_roi_full.yend); return ImageBufAlgo::rotate (dst, src, angle, center_x, center_y, filtername, filterwidth, recompute_roi, roi, nthreads); } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/compute_test.cpp0000644000175000017500000003051613151711064023123 0ustar mfvmfv/* Copyright 2016 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ // // Task: take "images" A and B, and compute R = A*A + B. // // Do this a whole bunch of different ways and benchmark. // #include #include #include #include #include #include #include #include #include #include #include #include OIIO_NAMESPACE_USING; static int iterations = 100; static int numthreads = Sysutil::hardware_concurrency(); static int ntrials = 5; static bool verbose = false; static bool wedge = false; static bool allgpus = false; static spin_mutex print_mutex; // make the prints not clobber each other static int xres = 1920, yres = 1080, channels = 3; static int npixels = xres * yres; static int size = npixels * channels; static ImageBuf imgA, imgB, imgR; static void test_arrays (ROI roi) { const float *a = (const float *)imgA.localpixels(); ASSERT(a); const float *b = (const float *)imgB.localpixels(); ASSERT(b); float *r = (float *)imgR.localpixels(); ASSERT(r); for (int x = 0; x < size; ++x) r[x] = a[x] * a[x] + b[x]; } static void test_arrays_like_image (ROI roi) { const float *a = (const float *)imgA.localpixels(); ASSERT(a); const float *b = (const float *)imgB.localpixels(); ASSERT(b); float *r = (float *)imgR.localpixels(); ASSERT(r); int nchannels = imgA.nchannels(); for (int y = 0; y < yres; ++y) { for (int x = 0; x < xres; ++x) { int i = (y*xres + x) * nchannels; for (int c = 0; c < nchannels; ++c) r[i+c] = a[i+c] * a[i+c] + b[i+c]; } } } static void test_arrays_like_image_multithread (ROI roi) { const float *a = (const float *)imgA.localpixels(); ASSERT(a); const float *b = (const float *)imgB.localpixels(); ASSERT(b); float *r = (float *)imgR.localpixels(); ASSERT(r); int nchannels = imgA.nchannels(); for (int y = roi.ybegin; y < roi.yend; ++y) { for (int x = roi.xbegin; x < roi.xend; ++x) { int i = (y*xres + x) * nchannels; for (int c = 0; c < nchannels; ++c) r[i+c] = a[i+c] * a[i+c] + b[i+c]; } } } static void test_arrays_like_image_multithread_wrapper (ROI roi) { ImageBufAlgo::parallel_image (test_arrays_like_image_multithread, roi, numthreads); } static void test_arrays_simd4 (ROI roi) { const float *a = (const float *)imgA.localpixels(); ASSERT(a); const float *b = (const float *)imgB.localpixels(); ASSERT(b); float *r = (float *)imgR.localpixels(); ASSERT(r); int x, end4 = size - (size&3); for (x = 0; x < end4; x += 4, a += 4, b += 4, r += 4) { simd::float4 a_simd(a), b_simd(b); *(simd::float4 *)r = a_simd * a_simd + b_simd; } for ( ; x < size; ++x, ++a, ++b, ++r) { *r = a[0]*a[0] + b[0]; } } static void test_arrays_like_image_simd (ROI roi) { const float *a = (const float *)imgA.localpixels(); ASSERT(a); const float *b = (const float *)imgB.localpixels(); ASSERT(b); float *r = (float *)imgR.localpixels(); ASSERT(r); int nchannels = imgA.nchannels(); for (int y = 0; y < yres; ++y) { for (int x = 0; x < xres; ++x) { int i = (y*xres + x) * nchannels; simd::float4 a_simd, b_simd, r_simd; a_simd.load (a+i, 3); b_simd.load (b+i, 3); r_simd = a_simd * a_simd + b_simd; r_simd.store (r+i, 3); } } } static void test_arrays_like_image_simd_multithread (ROI roi) { const float *a = (const float *)imgA.localpixels(); ASSERT(a); const float *b = (const float *)imgB.localpixels(); ASSERT(b); float *r = (float *)imgR.localpixels(); ASSERT(r); int nchannels = imgA.nchannels(); for (int y = roi.ybegin; y < roi.yend; ++y) { for (int x = roi.xbegin; x < roi.xend; ++x) { int i = (y*xres + x) * nchannels; simd::float4 a_simd, b_simd, r_simd; a_simd.load (a+i, 3); b_simd.load (b+i, 3); r_simd = a_simd * a_simd + b_simd; r_simd.store (r+i, 3); } } } static void test_arrays_like_image_simd_multithread_wrapper (ROI roi) { ImageBufAlgo::parallel_image (test_arrays_like_image_simd_multithread, roi, 0); } static void test_IBA (ROI roi, int threads) { ImageBufAlgo::mad (imgR, imgA, imgA, imgB, roi, threads); } void test_compute () { double time; ROI roi (0, xres, 0, yres, 0, 1, 0, channels); std::cout << "Test straightforward as 1D array of float: "; ImageBufAlgo::zero (imgR); time = time_trial (OIIO::bind (test_arrays, roi), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); // imgR.write ("ref.exr"); std::cout << "Test array iterated like an image: "; ImageBufAlgo::zero (imgR); time = time_trial (OIIO::bind (test_arrays_like_image, roi), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); std::cout << "Test array iterated like an image, multithreaded: "; ImageBufAlgo::zero (imgR); time = time_trial (OIIO::bind (test_arrays_like_image_multithread_wrapper, roi), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); std::cout << "Test array as 1D, using SIMD: "; ImageBufAlgo::zero (imgR); time = time_trial (OIIO::bind (test_arrays_simd4, roi), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); std::cout << "Test array iterated like an image, using SIMD: "; ImageBufAlgo::zero (imgR); time = time_trial (OIIO::bind (test_arrays_like_image_simd, roi), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); std::cout << "Test array iterated like an image, using SIMD, multithreaded: "; ImageBufAlgo::zero (imgR); time = time_trial (OIIO::bind (test_arrays_like_image_simd_multithread_wrapper, roi), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); std::cout << "Test ImageBufAlgo::mad 1 thread: "; ImageBufAlgo::zero (imgR); time = time_trial (OIIO::bind (test_IBA, roi, 1), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); std::cout << "Test ImageBufAlgo::mad multi-thread " << numthreads << ": "; ImageBufAlgo::zero (imgR); time = time_trial (OIIO::bind (test_IBA, roi, numthreads), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); } static void getargs (int argc, char *argv[]) { bool help = false; ArgParse ap; ap.options ("compute_test\n" OIIO_INTRO_STRING "\n" "Usage: compute_test [options]", // "%*", parse_files, "", "--help", &help, "Print help message", "-v", &verbose, "Verbose mode", "--threads %d", &numthreads, ustring::format("Number of threads (default: %d)", numthreads).c_str(), "--iterations %d", &iterations, ustring::format("Number of iterations (default: %d)", iterations).c_str(), "--trials %d", &ntrials, "Number of trials", "--allgpus", &allgpus, "Run OpenCL tests on all devices, not just default", "--wedge", &wedge, "Do a wedge test", NULL); if (ap.parse (argc, (const char**)argv) < 0) { std::cerr << ap.geterror() << std::endl; ap.usage (); exit (EXIT_FAILURE); } if (help) { ap.usage (); exit (EXIT_FAILURE); } } int main (int argc, char *argv[]) { #if !defined(NDEBUG) || defined(OIIO_CI) || defined(OIIO_CODECOV) // For the sake of test time, reduce the default iterations for DEBUG, // CI, and code coverage builds. Explicit use of --iters or --trials // will override this, since it comes before the getargs() call. iterations /= 10; ntrials = 1; #endif getargs (argc, argv); // Initialize imgA.reset (ImageSpec (xres, yres, channels, TypeDesc::FLOAT)); imgB.reset (ImageSpec (xres, yres, channels, TypeDesc::FLOAT)); imgR.reset (ImageSpec (xres, yres, channels, TypeDesc::FLOAT)); float red[3] = { 1, 0, 0 }; float green[3] = { 0, 1, 0 }; float blue[3] = { 0, 0, 1 }; float black[3] = { 0, 0, 0 }; ImageBufAlgo::fill (imgA, red, green, red, green); ImageBufAlgo::fill (imgB, blue, blue, black, black); // imgA.write ("A.exr"); // imgB.write ("B.exr"); test_compute (); return unit_test_failures; } openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/exif.cpp0000644000175000017500000012752413151711064021351 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include #include #include extern "C" { #include "tiff.h" } // Some EXIF tags that don't seem to be in tiff.h #ifndef EXIFTAG_SECURITYCLASSIFICATION #define EXIFTAG_SECURITYCLASSIFICATION 37394 #endif #ifndef EXIFTAG_IMAGEHISTORY #define EXIFTAG_IMAGEHISTORY 37395 #endif #ifdef TIFF_VERSION_BIG // In old versions of TIFF, this was defined in tiff.h. It's gone from // "BIG TIFF" (libtiff 4.x), so we just define it here. struct TIFFHeader { uint16_t tiff_magic; /* magic number (defines byte order) */ uint16_t tiff_version;/* TIFF version number */ uint32_t tiff_diroff; /* byte offset to first directory */ }; struct TIFFDirEntry { uint16_t tdir_tag; /* tag ID */ uint16_t tdir_type; /* data type -- see TIFFDataType enum */ uint32_t tdir_count; /* number of items; length in spec */ uint32_t tdir_offset; /* byte offset to field data */ }; #endif #include "OpenImageIO/imageio.h" #define DEBUG_EXIF_READ 0 #define DEBUG_EXIF_WRITE 0 OIIO_NAMESPACE_BEGIN namespace { // Sizes of TIFFDataType members static size_t tiff_data_sizes[] = { 0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 4 }; static int tiff_data_size (const TIFFDirEntry &dir) { const int num_data_sizes = sizeof(tiff_data_sizes) / sizeof(*tiff_data_sizes); int dir_index = (int)dir.tdir_type; if (dir_index < 0 || dir_index >= num_data_sizes) { // Inform caller about corrupted entry. return -1; } return tiff_data_sizes[dir_index] * dir.tdir_count; } struct EXIF_tag_info { int tifftag; // TIFF tag used for this info const char *name; // Attribute name we use TIFFDataType tifftype; // Data type that TIFF wants int tiffcount; // Number of items }; static const EXIF_tag_info exif_tag_table[] = { // Skip ones handled by the usual JPEG code { TIFFTAG_IMAGEWIDTH, "Exif:ImageWidth", TIFF_NOTYPE, 1 }, { TIFFTAG_IMAGELENGTH, "Exif:ImageLength", TIFF_NOTYPE, 1 }, { TIFFTAG_BITSPERSAMPLE, "Exif:BitsPerSample", TIFF_NOTYPE, 1 }, { TIFFTAG_COMPRESSION, "Exif:Compression", TIFF_NOTYPE, 1 }, { TIFFTAG_PHOTOMETRIC, "Exif:Photometric", TIFF_NOTYPE, 1 }, { TIFFTAG_SAMPLESPERPIXEL, "Exif:SamplesPerPixel", TIFF_NOTYPE, 1 }, { TIFFTAG_PLANARCONFIG, "Exif:PlanarConfig", TIFF_NOTYPE, 1 }, { TIFFTAG_YCBCRSUBSAMPLING, "Exif:YCbCrSubsampling",TIFF_SHORT, 1 }, { TIFFTAG_YCBCRPOSITIONING, "Exif:YCbCrPositioning",TIFF_SHORT, 1 }, // TIFF tags we may come across { TIFFTAG_ORIENTATION, "Orientation", TIFF_SHORT, 1 }, { TIFFTAG_XRESOLUTION, "XResolution", TIFF_RATIONAL, 1 }, { TIFFTAG_YRESOLUTION, "YResolution", TIFF_RATIONAL, 1 }, { TIFFTAG_RESOLUTIONUNIT, "ResolutionUnit",TIFF_SHORT, 1 }, { TIFFTAG_IMAGEDESCRIPTION, "ImageDescription", TIFF_ASCII, 0 }, { TIFFTAG_MAKE, "Make", TIFF_ASCII, 0 }, { TIFFTAG_MODEL, "Model", TIFF_ASCII, 0 }, { TIFFTAG_SOFTWARE, "Software", TIFF_ASCII, 0 }, { TIFFTAG_ARTIST, "Artist", TIFF_ASCII, 0 }, { TIFFTAG_COPYRIGHT, "Copyright", TIFF_ASCII, 0 }, { TIFFTAG_DATETIME, "DateTime", TIFF_ASCII, 0 }, { TIFFTAG_EXIFIFD, "Exif:ExifIFD", TIFF_NOTYPE, 1 }, { TIFFTAG_INTEROPERABILITYIFD, "Exif:InteroperabilityIFD", TIFF_NOTYPE, 1 }, { TIFFTAG_GPSIFD, "Exif:GPSIFD", TIFF_NOTYPE, 1 }, // EXIF tags we may come across { EXIFTAG_EXPOSURETIME, "ExposureTime", TIFF_RATIONAL, 1 }, { EXIFTAG_FNUMBER, "FNumber", TIFF_RATIONAL, 1 }, { EXIFTAG_EXPOSUREPROGRAM, "Exif:ExposureProgram", TIFF_SHORT, 1 }, // ?? translate to ascii names? { EXIFTAG_SPECTRALSENSITIVITY,"Exif:SpectralSensitivity", TIFF_ASCII, 0 }, { EXIFTAG_ISOSPEEDRATINGS, "Exif:ISOSpeedRatings", TIFF_SHORT, 1 }, { EXIFTAG_OECF, "Exif:OECF", TIFF_NOTYPE, 1 }, // skip it { EXIFTAG_EXIFVERSION, "Exif:ExifVersion", TIFF_NOTYPE, 1 }, // skip it { EXIFTAG_DATETIMEORIGINAL, "Exif:DateTimeOriginal", TIFF_ASCII, 0 }, { EXIFTAG_DATETIMEDIGITIZED,"Exif:DateTimeDigitized", TIFF_ASCII, 0 }, { EXIFTAG_COMPONENTSCONFIGURATION, "Exif:ComponentsConfiguration", TIFF_UNDEFINED, 1 }, { EXIFTAG_COMPRESSEDBITSPERPIXEL, "Exif:CompressedBitsPerPixel", TIFF_RATIONAL, 1 }, { EXIFTAG_SHUTTERSPEEDVALUE,"Exif:ShutterSpeedValue", TIFF_SRATIONAL, 1 }, // APEX units { EXIFTAG_APERTUREVALUE, "Exif:ApertureValue", TIFF_RATIONAL, 1 }, // APEX units { EXIFTAG_BRIGHTNESSVALUE, "Exif:BrightnessValue", TIFF_SRATIONAL, 1 }, { EXIFTAG_EXPOSUREBIASVALUE,"Exif:ExposureBiasValue", TIFF_SRATIONAL, 1 }, { EXIFTAG_MAXAPERTUREVALUE, "Exif:MaxApertureValue",TIFF_RATIONAL, 1 }, { EXIFTAG_SUBJECTDISTANCE, "Exif:SubjectDistance", TIFF_RATIONAL, 1 }, { EXIFTAG_METERINGMODE, "Exif:MeteringMode", TIFF_SHORT, 1 }, { EXIFTAG_LIGHTSOURCE, "Exif:LightSource", TIFF_SHORT, 1 }, { EXIFTAG_FLASH, "Exif:Flash", TIFF_SHORT, 1 }, { EXIFTAG_FOCALLENGTH, "Exif:FocalLength", TIFF_RATIONAL, 1 }, // mm { EXIFTAG_SECURITYCLASSIFICATION, "Exif:SecurityClassification", TIFF_ASCII, 1 }, { EXIFTAG_IMAGEHISTORY, "Exif:ImageHistory", TIFF_ASCII, 1 }, { EXIFTAG_SUBJECTAREA, "Exif:SubjectArea", TIFF_NOTYPE, 1 }, // skip { EXIFTAG_MAKERNOTE, "Exif:MakerNote", TIFF_NOTYPE, 1 }, // skip it { EXIFTAG_USERCOMMENT, "Exif:UserComment", TIFF_NOTYPE, 1 }, // skip it { EXIFTAG_SUBSECTIME, "Exif:SubsecTime", TIFF_ASCII, 0 }, { EXIFTAG_SUBSECTIMEORIGINAL,"Exif:SubsecTimeOriginal", TIFF_ASCII, 0 }, { EXIFTAG_SUBSECTIMEDIGITIZED,"Exif:SubsecTimeDigitized", TIFF_ASCII, 0 }, { EXIFTAG_FLASHPIXVERSION, "Exif:FlashPixVersion", TIFF_NOTYPE, 1 }, // skip "Exif:FlashPixVesion", TIFF_NOTYPE, 1 }, { EXIFTAG_COLORSPACE, "Exif:ColorSpace", TIFF_SHORT, 1 }, { EXIFTAG_PIXELXDIMENSION, "Exif:PixelXDimension", TIFF_LONG, 1 }, { EXIFTAG_PIXELYDIMENSION, "Exif:PixelYDimension", TIFF_LONG, 1 }, { EXIFTAG_RELATEDSOUNDFILE, "Exif:RelatedSoundFile", TIFF_ASCII, 0 }, { EXIFTAG_FLASHENERGY, "Exif:FlashEnergy", TIFF_RATIONAL, 1 }, { EXIFTAG_SPATIALFREQUENCYRESPONSE, "Exif:SpatialFrequencyResponse", TIFF_NOTYPE, 1 }, { EXIFTAG_FOCALPLANEXRESOLUTION, "Exif:FocalPlaneXResolution", TIFF_RATIONAL, 1 }, { EXIFTAG_FOCALPLANEYRESOLUTION, "Exif:FocalPlaneYResolution", TIFF_RATIONAL, 1 }, { EXIFTAG_FOCALPLANERESOLUTIONUNIT, "Exif:FocalPlaneResolutionUnit", TIFF_SHORT, 1 }, // Symbolic? { EXIFTAG_SUBJECTLOCATION, "Exif:SubjectLocation", TIFF_SHORT, 1 }, // FIXME: short[2] { EXIFTAG_EXPOSUREINDEX, "Exif:ExposureIndex", TIFF_RATIONAL, 1 }, { EXIFTAG_SENSINGMETHOD, "Exif:SensingMethod", TIFF_SHORT, 1 }, { EXIFTAG_FILESOURCE, "Exif:FileSource", TIFF_SHORT, 1 }, { EXIFTAG_SCENETYPE, "Exif:SceneType", TIFF_SHORT, 1 }, { EXIFTAG_CFAPATTERN, "Exif:CFAPattern", TIFF_NOTYPE, 1 }, { EXIFTAG_CUSTOMRENDERED, "Exif:CustomRendered", TIFF_SHORT, 1 }, { EXIFTAG_EXPOSUREMODE, "Exif:ExposureMode", TIFF_SHORT, 1 }, { EXIFTAG_WHITEBALANCE, "Exif:WhiteBalance", TIFF_SHORT, 1 }, { EXIFTAG_DIGITALZOOMRATIO, "Exif:DigitalZoomRatio",TIFF_RATIONAL, 1 }, { EXIFTAG_FOCALLENGTHIN35MMFILM, "Exif:FocalLengthIn35mmFilm", TIFF_SHORT, 1 }, { EXIFTAG_SCENECAPTURETYPE, "Exif:SceneCaptureType",TIFF_SHORT, 1 }, { EXIFTAG_GAINCONTROL, "Exif:GainControl", TIFF_RATIONAL, 1 }, { EXIFTAG_CONTRAST, "Exif:Contrast", TIFF_SHORT, 1 }, { EXIFTAG_SATURATION, "Exif:Saturation", TIFF_SHORT, 1 }, { EXIFTAG_SHARPNESS, "Exif:Sharpness", TIFF_SHORT, 1 }, { EXIFTAG_DEVICESETTINGDESCRIPTION, "Exif:DeviceSettingDescription", TIFF_NOTYPE, 1 }, { EXIFTAG_SUBJECTDISTANCERANGE, "Exif:SubjectDistanceRange", TIFF_SHORT, 1 }, { EXIFTAG_IMAGEUNIQUEID, "Exif:ImageUniqueID", TIFF_ASCII, 0 }, { 34855, "Exif:PhotographicSensitivity", TIFF_SHORT, 1 }, { 34864, "Exif:SensitivityType", TIFF_SHORT, 1 }, { 34865, "Exif:StandardOutputSensitivity", TIFF_LONG, 1 }, { 34866, "Exif:RecommendedExposureIndex", TIFF_LONG, 1 }, { 34867, "Exif:ISOSpeed", TIFF_LONG, 1 }, { 34868, "Exif:ISOSpeedLatitudeyyy", TIFF_LONG, 1 }, { 34869, "Exif:ISOSpeedLatitudezzz", TIFF_LONG, 1 }, { 42032, "Exif:CameraOwnerName", TIFF_ASCII, 0 }, { 42033, "Exif:BodySerialNumber", TIFF_ASCII, 0 }, { 42034, "Exif:LensSpecification",TIFF_RATIONAL, 4 }, { 42035, "Exif:LensMake", TIFF_ASCII, 0 }, { 42036, "Exif:LensModel", TIFF_ASCII, 0 }, { 42037, "Exif:LensSerialNumber", TIFF_ASCII, 0 }, { 42240, "Exif:Gamma", TIFF_RATIONAL, 0 }, { -1, NULL } // signal end of table }; enum GPSTag { GPSTAG_VERSIONID = 0, GPSTAG_LATITUDEREF = 1, GPSTAG_LATITUDE = 2, GPSTAG_LONGITUDEREF = 3, GPSTAG_LONGITUDE = 4, GPSTAG_ALTITUDEREF = 5, GPSTAG_ALTITUDE = 6, GPSTAG_TIMESTAMP = 7, GPSTAG_SATELLITES = 8, GPSTAG_STATUS = 9, GPSTAG_MEASUREMODE = 10, GPSTAG_DOP = 11, GPSTAG_SPEEDREF = 12, GPSTAG_SPEED = 13, GPSTAG_TRACKREF = 14, GPSTAG_TRACK = 15, GPSTAG_IMGDIRECTIONREF = 16, GPSTAG_IMGDIRECTION = 17, GPSTAG_MAPDATUM = 18, GPSTAG_DESTLATITUDEREF = 19, GPSTAG_DESTLATITUDE = 20, GPSTAG_DESTLONGITUDEREF = 21, GPSTAG_DESTLONGITUDE = 22, GPSTAG_DESTBEARINGREF = 23, GPSTAG_DESTBEARING = 24, GPSTAG_DESTDISTANCEREF = 25, GPSTAG_DESTDISTANCE = 26, GPSTAG_PROCESSINGMETHOD = 27, GPSTAG_AREAINFORMATION = 28, GPSTAG_DATESTAMP = 29, GPSTAG_DIFFERENTIAL = 30, GPSTAG_HPOSITIONINGERROR = 31 }; static const EXIF_tag_info gps_tag_table[] = { { GPSTAG_VERSIONID, "GPS:VersionID", TIFF_BYTE, 4 }, { GPSTAG_LATITUDEREF, "GPS:LatitudeRef", TIFF_ASCII, 2 }, { GPSTAG_LATITUDE, "GPS:Latitude", TIFF_RATIONAL, 3 }, { GPSTAG_LONGITUDEREF, "GPS:LongitudeRef", TIFF_ASCII, 2 }, { GPSTAG_LONGITUDE, "GPS:Longitude", TIFF_RATIONAL, 3 }, { GPSTAG_ALTITUDEREF, "GPS:AltitudeRef", TIFF_BYTE, 1 }, { GPSTAG_ALTITUDE, "GPS:Altitude", TIFF_RATIONAL, 1 }, { GPSTAG_TIMESTAMP, "GPS:TimeStamp", TIFF_RATIONAL, 3 }, { GPSTAG_SATELLITES, "GPS:Satellites", TIFF_ASCII, 0 }, { GPSTAG_STATUS, "GPS:Status", TIFF_ASCII, 2 }, { GPSTAG_MEASUREMODE, "GPS:MeasureMode", TIFF_ASCII, 2 }, { GPSTAG_DOP, "GPS:DOP", TIFF_RATIONAL, 1 }, { GPSTAG_SPEEDREF, "GPS:SpeedRef", TIFF_ASCII, 2 }, { GPSTAG_SPEED, "GPS:Speed", TIFF_RATIONAL, 1 }, { GPSTAG_TRACKREF, "GPS:TrackRef", TIFF_ASCII, 2 }, { GPSTAG_TRACK, "GPS:Track", TIFF_RATIONAL, 1 }, { GPSTAG_IMGDIRECTIONREF, "GPS:ImgDirectionRef", TIFF_ASCII, 2 }, { GPSTAG_IMGDIRECTION, "GPS:ImgDirection", TIFF_RATIONAL, 1 }, { GPSTAG_MAPDATUM, "GPS:MapDatum", TIFF_ASCII, 0 }, { GPSTAG_DESTLATITUDEREF, "GPS:DestLatitudeRef", TIFF_ASCII, 2 }, { GPSTAG_DESTLATITUDE, "GPS:DestLatitude", TIFF_RATIONAL, 3 }, { GPSTAG_DESTLONGITUDEREF, "GPS:DestLongitudeRef", TIFF_ASCII, 2 }, { GPSTAG_DESTLONGITUDE, "GPS:DestLongitude", TIFF_RATIONAL, 3 }, { GPSTAG_DESTBEARINGREF, "GPS:DestBearingRef", TIFF_ASCII, 2 }, { GPSTAG_DESTBEARING, "GPS:DestBearing", TIFF_RATIONAL, 1 }, { GPSTAG_DESTDISTANCEREF, "GPS:DestDistanceRef", TIFF_ASCII, 2 }, { GPSTAG_DESTDISTANCE, "GPS:DestDistance", TIFF_RATIONAL, 1 }, { GPSTAG_PROCESSINGMETHOD, "GPS:ProcessingMethod", TIFF_UNDEFINED, 1 }, { GPSTAG_AREAINFORMATION, "GPS:AreaInformation", TIFF_UNDEFINED, 1 }, { GPSTAG_DATESTAMP, "GPS:DateStamp", TIFF_ASCII, 0 }, { GPSTAG_DIFFERENTIAL, "GPS:Differential", TIFF_SHORT, 1 }, { GPSTAG_HPOSITIONINGERROR, "GPS:HPositioningError",TIFF_RATIONAL, 1 }, { -1, NULL } // signal end of table }; class TagMap { typedef boost::container::flat_map tagmap_t; typedef boost::container::flat_map namemap_t; // Name map is lower case so it's effectively case-insensitive public: TagMap (const EXIF_tag_info *tag_table) { for (int i = 0; tag_table[i].tifftag >= 0; ++i) { const EXIF_tag_info *eti = &tag_table[i]; m_tagmap[eti->tifftag] = eti; if (eti->name) { std::string lowername (eti->name); Strutil::to_lower (lowername); m_namemap[lowername] = eti; } } } const EXIF_tag_info * find (int tag) const { tagmap_t::const_iterator i = m_tagmap.find (tag); return i == m_tagmap.end() ? NULL : i->second; } const EXIF_tag_info * find (string_view name) const { std::string lowername (name); Strutil::to_lower (lowername); namemap_t::const_iterator i = m_namemap.find (lowername); return i == m_namemap.end() ? NULL : i->second; } const char * name (int tag) const { tagmap_t::const_iterator i = m_tagmap.find (tag); return i == m_tagmap.end() ? NULL : i->second->name; } TIFFDataType tifftype (int tag) const { tagmap_t::const_iterator i = m_tagmap.find (tag); return i == m_tagmap.end() ? TIFF_NOTYPE : i->second->tifftype; } int tiffcount (int tag) const { tagmap_t::const_iterator i = m_tagmap.find (tag); return i == m_tagmap.end() ? 0 : i->second->tiffcount; } int tag (string_view name) const { std::string lowername (name); Strutil::to_lower (lowername); namemap_t::const_iterator i = m_namemap.find (lowername); return i == m_namemap.end() ? -1 : i->second->tifftag; } private: tagmap_t m_tagmap; namemap_t m_namemap; }; static TagMap& exif_tagmap_ref () { static TagMap T (exif_tag_table); return T; } static TagMap& gps_tagmap_ref () { static TagMap T (gps_tag_table); return T; } #if (DEBUG_EXIF_WRITE || DEBUG_EXIF_READ) static bool print_dir_entry (const TagMap &tagmap, const TIFFDirEntry &dir, string_view buf) { int len = tiff_data_size (dir); if (len < 0) { std::cerr << "Ignoring bad directory entry\n"; return false; } const char *mydata = NULL; if (len <= 4) { // short data is stored in the offset field mydata = (const char *)&dir.tdir_offset; } else { if (dir.tdir_offset >= buf.size() || (dir.tdir_offset+tiff_data_size(dir)) >= buf.size()) return false; // bogus! overruns the buffer mydata = buf.data() + dir.tdir_offset; } const char *name = tagmap.name(dir.tdir_tag); std::cerr << "tag=" << dir.tdir_tag << " (" << (name ? name : "unknown") << ")" << ", type=" << dir.tdir_type << ", count=" << dir.tdir_count << ", offset=" << dir.tdir_offset << " = " ; switch (dir.tdir_type) { case TIFF_ASCII : std::cerr << "'" << (char *)mydata << "'"; break; case TIFF_RATIONAL : { const unsigned int *u = (unsigned int *)mydata; for (size_t i = 0; i < dir.tdir_count; ++i) std::cerr << u[2*i] << "/" << u[2*i+1] << " = " << (double)u[2*i]/(double)u[2*i+1] << " "; } break; case TIFF_SRATIONAL : { const int *u = (int *)mydata; for (size_t i = 0; i < dir.tdir_count; ++i) std::cerr << u[2*i] << "/" << u[2*i+1] << " = " << (double)u[2*i]/(double)u[2*i+1] << " "; } break; case TIFF_SHORT : std::cerr << ((unsigned short *)mydata)[0]; break; case TIFF_LONG : std::cerr << ((unsigned int *)mydata)[0]; break; case TIFF_BYTE : case TIFF_UNDEFINED : case TIFF_NOTYPE : default: if (len <= 4 && dir.tdir_count > 4) { // Request more data than is stored. std::cerr << "Ignoring buffer with too much count of short data.\n"; return false; } for (size_t i = 0; i < dir.tdir_count; ++i) std::cerr << (int)((unsigned char *)mydata)[i] << ' '; break; } std::cerr << "\n"; return true; } #endif /// Add one EXIF directory entry's data to spec under the given 'name'. /// The directory entry is in *dirp, buf points to the beginning of the /// TIFF "file", i.e. all TIFF tag offsets are relative to buf. If swab /// is true, the endianness of the file doesn't match the endianness of /// the host CPU, therefore all integer and float data embedded in buf /// needs to be byte-swapped. Note that *dirp HAS already been swapped, /// if necessary, so no byte swapping on *dirp is necessary. static void add_exif_item_to_spec (ImageSpec &spec, const char *name, const TIFFDirEntry *dirp, string_view buf, bool swab) { if (dirp->tdir_type == TIFF_SHORT && dirp->tdir_count == 1) { union { uint32_t i32; uint16_t i16[2]; } convert; convert.i32 = dirp->tdir_offset; unsigned short d = convert.i16[0]; // N.B. The Exif spec says that for a 16 bit value, it's stored in // the *first* 16 bits of the offset area. if (swab) swap_endian (&d); spec.attribute (name, (unsigned int)d); } else if (dirp->tdir_type == TIFF_LONG && dirp->tdir_count == 1) { unsigned int d; d = * (const unsigned int *) &dirp->tdir_offset; // int stored in offset itself if (swab) swap_endian (&d); spec.attribute (name, (unsigned int)d); } else if (dirp->tdir_type == TIFF_RATIONAL) { int n = dirp->tdir_count; // How many float *f = (float *) alloca (n * sizeof(float)); for (int i = 0; i < n; ++i) { unsigned int num, den; num = ((const unsigned int *) &(buf[dirp->tdir_offset]))[2*i+0]; den = ((const unsigned int *) &(buf[dirp->tdir_offset]))[2*i+1]; if (swab) { swap_endian (&num); swap_endian (&den); } f[i] = (float) ((double)num / (double)den); } if (dirp->tdir_count == 1) spec.attribute (name, *f); else spec.attribute (name, TypeDesc(TypeDesc::FLOAT, n), f); } else if (dirp->tdir_type == TIFF_SRATIONAL) { int n = dirp->tdir_count; // How many float *f = (float *) alloca (n * sizeof(float)); for (int i = 0; i < n; ++i) { int num, den; num = ((const int *) &(buf[dirp->tdir_offset]))[2*i+0]; den = ((const int *) &(buf[dirp->tdir_offset]))[2*i+1]; if (swab) { swap_endian (&num); swap_endian (&den); } f[i] = (float) ((double)num / (double)den); } if (dirp->tdir_count == 1) spec.attribute (name, *f); else spec.attribute (name, TypeDesc(TypeDesc::FLOAT, n), f); } else if (dirp->tdir_type == TIFF_ASCII) { int len = tiff_data_size (*dirp); const char *ptr = (len <= 4) ? (const char *)&dirp->tdir_offset : (buf.data() + dirp->tdir_offset); while (len && ptr[len-1] == 0) // Don't grab the terminating null --len; std::string str (ptr, len); if (strlen(str.c_str()) < str.length()) // Stray \0 in the middle str = std::string (str.c_str()); spec.attribute (name, str); } else if (dirp->tdir_type == TIFF_BYTE && dirp->tdir_count == 1) { // Not sure how to handle "bytes" generally, but certainly for just // one, add it as an int. unsigned char d; d = * (const unsigned char *) &dirp->tdir_offset; // byte stored in offset itself spec.attribute (name, (int)d); } else if (dirp->tdir_type == TIFF_UNDEFINED || dirp->tdir_type == TIFF_BYTE) { // Add it as bytes #if 0 const void *addr = dirp->tdir_count <= 4 ? (const void *) &dirp->tdir_offset : (const void *) &buf[dirp->tdir_offset]; spec.attribute (name, TypeDesc::UINT8, dirp->tdir_count, addr); #endif } else { #ifndef NDEBUG std::cerr << "didn't know how to process " << name << ", type " << dirp->tdir_type << " x " << dirp->tdir_count << "\n"; #endif } } /// Process a single TIFF directory entry embedded in the JPEG 'APP1' /// data. The directory entry is in *dirp, buf points to the beginning /// of the TIFF "file", i.e. all TIFF tag offsets are relative to buf. /// The goal is to decode the tag and put the data into appropriate /// attribute slots of spec. If swab is true, the endianness of the /// file doesn't match the endianness of the host CPU, therefore all /// integer and float data embedded in buf needs to be byte-swapped. /// Note that *dirp has not been swapped, and so is still in the native /// endianness of the file. static void read_exif_tag (ImageSpec &spec, const TIFFDirEntry *dirp, string_view buf, bool swab, std::set &ifd_offsets_seen, const TagMap &tagmap) { if ((char*)dirp < buf.data() || (char*)dirp >= buf.data() + buf.size()) { #if DEBUG_EXIF_READ std::cerr << "Ignoring directory outside of the buffer.\n"; #endif return; } TagMap& exif_tagmap (exif_tagmap_ref()); TagMap& gps_tagmap (gps_tagmap_ref()); // Make a copy of the pointed-to TIFF directory, swab the components // if necessary. TIFFDirEntry dir = *dirp; if (swab) { swap_endian (&dir.tdir_tag); swap_endian (&dir.tdir_type); swap_endian (&dir.tdir_count); // only swab true offsets, not data embedded in the offset field if (tiff_data_size (dir) > 4) swap_endian (&dir.tdir_offset); } #if DEBUG_EXIF_READ std::cerr << "Read "; print_dir_entry (tagmap, dir, buf); #endif if (dir.tdir_tag == TIFFTAG_EXIFIFD || dir.tdir_tag == TIFFTAG_GPSIFD) { // Special case: It's a pointer to a private EXIF directory. // Handle the whole thing recursively. unsigned int offset = dirp->tdir_offset; // int stored in offset itself if (swab) swap_endian (&offset); if (offset >= buf.size()) { #if DEBUG_EXIF_READ unsigned int off2 = offset; swap_endian (&off2); std::cerr << "Bad Exif block? ExifIFD has offset " << offset << " inexplicably greater than exif buffer length " << buf.size() << " (byte swapped = " << off2 << ")\n"; #endif return; } // Don't recurse if we've already visited this IFD if (ifd_offsets_seen.find (offset) != ifd_offsets_seen.end()) { #if DEBUG_EXIF_READ std::cerr << "Early ifd exit\n"; #endif return; } ifd_offsets_seen.insert (offset); #if DEBUG_EXIF_READ std::cerr << "Now we've seen offset " << offset << "\n"; #endif const unsigned char *ifd = ((const unsigned char *)buf.data() + offset); unsigned short ndirs = *(const unsigned short *)ifd; if (swab) swap_endian (&ndirs); if (dir.tdir_tag == TIFFTAG_GPSIFD && ndirs > 32) { // We have encountered JPEG files that inexplicably have the // directory count for the GPS data using the wrong byte order. // In this case, since there are only 32 possible GPS related // tags, we use that as a sanity check and skip the corrupted // data block. This isn't a very general solution, but it's a // rare case and clearly a broken file. We're just trying not to // crash in this case. return; } #if DEBUG_EXIF_READ std::cerr << "exifid has type " << dir.tdir_type << ", offset " << dir.tdir_offset << "\n"; std::cerr << "EXIF Number of directory entries = " << ndirs << "\n"; #endif for (int d = 0; d < ndirs; ++d) read_exif_tag (spec, (const TIFFDirEntry *)(ifd+2+d*sizeof(TIFFDirEntry)), buf, swab, ifd_offsets_seen, dir.tdir_tag == TIFFTAG_EXIFIFD ? exif_tagmap : gps_tagmap); #if DEBUG_EXIF_READ std::cerr << "> End EXIF\n"; #endif } else if (dir.tdir_tag == TIFFTAG_INTEROPERABILITYIFD) { // Special case: It's a pointer to a private EXIF directory. // Handle the whole thing recursively. unsigned int offset = dirp->tdir_offset; // int stored in offset itself if (swab) swap_endian (&offset); // Don't recurse if we've already visited this IFD if (ifd_offsets_seen.find (offset) != ifd_offsets_seen.end()) return; ifd_offsets_seen.insert (offset); #if DEBUG_EXIF_READ std::cerr << "Now we've seen offset " << offset << "\n"; #endif const unsigned char *ifd = ((const unsigned char *)buf.data() + offset); unsigned short ndirs = *(const unsigned short *)ifd; if (swab) swap_endian (&ndirs); #if DEBUG_EXIF_READ std::cerr << "\n\nInteroperability has type " << dir.tdir_type << ", offset " << dir.tdir_offset << "\n"; std::cerr << "Interoperability Number of directory entries = " << ndirs << "\n"; #endif for (int d = 0; d < ndirs; ++d) read_exif_tag (spec, (const TIFFDirEntry *)(ifd+2+d*sizeof(TIFFDirEntry)), buf, swab, ifd_offsets_seen, exif_tagmap); #if DEBUG_EXIF_READ std::cerr << "> End Interoperability\n\n"; #endif } else { // Everything else -- use our table to handle the general case const char *name = tagmap.name (dir.tdir_tag); if (name) { add_exif_item_to_spec (spec, name, &dir, buf, swab); } else { #if DEBUG_EXIF_READ std::cerr << "Dir : tag=" << dir.tdir_tag << ", type=" << dir.tdir_type << ", count=" << dir.tdir_count << ", offset=" << dir.tdir_offset << "\n"; #endif } } } class tagcompare { public: int operator() (const TIFFDirEntry &a, const TIFFDirEntry &b) { return (a.tdir_tag < b.tdir_tag); } }; static void append_dir_entry (const TagMap &tagmap, std::vector &dirs, std::vector &data, int tag, TIFFDataType type, size_t count, const void *mydata) { TIFFDirEntry dir; dir.tdir_tag = tag; dir.tdir_type = type; dir.tdir_count = count; size_t len = tiff_data_sizes[(int)type] * count; if (len <= 4) { dir.tdir_offset = 0; memcpy (&dir.tdir_offset, mydata, len); } else { dir.tdir_offset = data.size(); data.insert (data.end(), (char *)mydata, (char *)mydata + len); } #if DEBUG_EXIF_WRITE std::cerr << "Adding "; print_dir_entry (tagmap, dir, string_view((const char *)mydata, len)); #endif // Don't double-add BOOST_FOREACH (TIFFDirEntry &d, dirs) { if (d.tdir_tag == tag) { d = dir; return; } } dirs.push_back (dir); } /// Convert to the desired integer type and then append_dir_entry it. /// template bool append_dir_entry_integer (const ImageIOParameter &p, const TagMap &tagmap, std::vector &dirs, std::vector &data, int tag, TIFFDataType type) { T i; switch (p.type().basetype) { case TypeDesc::UINT: i = (T) *(unsigned int *)p.data(); break; case TypeDesc::INT: i = (T) *(int *)p.data(); break; case TypeDesc::UINT16: i = (T) *(unsigned short *)p.data(); break; case TypeDesc::INT16: i = (T) *(short *)p.data(); break; default: return false; } append_dir_entry (tagmap, dirs, data, tag, type, 1, &i); return true; } /// Helper: For param that needs to be added as a tag, create a TIFF /// directory entry for it in dirs and add its data in data. Set the /// directory's offset just to the position within data where it will /// reside. Don't worry about it being relative to the start of some /// TIFF structure. static void encode_exif_entry (const ImageIOParameter &p, int tag, std::vector &dirs, std::vector &data, const TagMap &tagmap) { TIFFDataType type = tagmap.tifftype (tag); size_t count = (size_t) tagmap.tiffcount (tag); TypeDesc element = p.type().elementtype(); switch (type) { case TIFF_ASCII : if (p.type() == TypeDesc::STRING) { const char *s = *(const char **) p.data(); int len = strlen(s) + 1; append_dir_entry (tagmap, dirs, data, tag, type, len, s); return; } break; case TIFF_RATIONAL : if (element == TypeDesc::FLOAT) { unsigned int *rat = (unsigned int *) alloca (2*count*sizeof(unsigned int)); const float *f = (const float *)p.data(); for (size_t i = 0; i < count; ++i) float_to_rational (f[i], rat[2*i], rat[2*i+1]); append_dir_entry (tagmap, dirs, data, tag, type, count, rat); return; } break; case TIFF_SRATIONAL : if (element == TypeDesc::FLOAT) { int *rat = (int *) alloca (2*count*sizeof(int)); const float *f = (const float *)p.data(); for (size_t i = 0; i < count; ++i) float_to_rational (f[i], rat[2*i], rat[2*i+1]); append_dir_entry (tagmap, dirs, data, tag, type, count, rat); return; } break; case TIFF_SHORT : if (append_dir_entry_integer (p, tagmap, dirs, data, tag, type)) return; break; case TIFF_LONG : if (append_dir_entry_integer (p, tagmap, dirs, data, tag, type)) return; break; case TIFF_BYTE : if (append_dir_entry_integer (p, tagmap, dirs, data, tag, type)) return; break; default: break; } #if DEBUG_EXIF_WRITE std::cerr << " Don't know how to add " << p.name() << ", tag " << tag << ", type " << type << ' ' << p.type().c_str() << "\n"; #endif } /// Given a list of directory entries, add 'offset' to their tdir_offset /// fields (unless, of course, they are less than 4 bytes of data and are /// therefore stored locally rather than having an offset at all). static void reoffset (std::vector &dirs, const TagMap &tagmap, size_t offset) { BOOST_FOREACH (TIFFDirEntry &dir, dirs) { if (tiff_data_size (dir) <= 4 && dir.tdir_tag != TIFFTAG_EXIFIFD && dir.tdir_tag != TIFFTAG_GPSIFD) { #if DEBUG_EXIF_WRITE const char *name = tagmap.name (dir.tdir_tag); std::cerr << " NO re-offset of exif entry " << " tag " << dir.tdir_tag << " " << (name ? name : "") << " to " << dir.tdir_offset << '\n'; #endif continue; } dir.tdir_offset += offset; #if DEBUG_EXIF_WRITE const char *name = tagmap.name (dir.tdir_tag); std::cerr << " re-offsetting entry " << " tag " << dir.tdir_tag << " " << (name ? name : "") << " to " << dir.tdir_offset << '\n'; #endif } } } // anon namespace // DEPRECATED (1.8) bool decode_exif (const void *exif, int length, ImageSpec &spec) { return decode_exif (string_view ((const char *)exif, length), spec); } // Decode a raw Exif data block and save all the metadata in an // ImageSpec. Return true if all is ok, false if the exif block was // somehow malformed. bool decode_exif (string_view exif, ImageSpec &spec) { TagMap& exif_tagmap (exif_tagmap_ref()); #if DEBUG_EXIF_READ std::cerr << "Exif dump:\n"; for (size_t i = 0; i < exif.size(); ++i) { if (exif[i] >= ' ') std::cerr << (char)exif[i] << ' '; std::cerr << "(" << (int)(unsigned char)exif[i] << ") "; } std::cerr << "\n"; #endif // The first item should be a standard TIFF header. Note that HERE, // not the start of the Exif blob, is where all TIFF offsets are // relative to. The header should have the right magic number (which // also tells us the endianness of the data) and an offset to the // first TIFF directory. // // N.B. Just read libtiff's "tiff.h" for info on the structure // layout of TIFF headers and directory entries. The TIFF spec // itself is also helpful in this area. TIFFHeader head = *(const TIFFHeader *)exif.data(); if (head.tiff_magic != 0x4949 && head.tiff_magic != 0x4d4d) return false; bool host_little = littleendian(); bool file_little = (head.tiff_magic == 0x4949); bool swab = (host_little != file_little); if (swab) swap_endian (&head.tiff_diroff); // keep track of IFD offsets we've already seen to avoid infinite // recursion if there are circular references. std::set ifd_offsets_seen; // Read the directory that the header pointed to. It should contain // some number of directory entries containing tags to process. const unsigned char *ifd = ((const unsigned char *)exif.data() + head.tiff_diroff); unsigned short ndirs = *(const unsigned short *)ifd; if (swab) swap_endian (&ndirs); for (int d = 0; d < ndirs; ++d) read_exif_tag (spec, (const TIFFDirEntry *) (ifd+2+d*sizeof(TIFFDirEntry)), exif, swab, ifd_offsets_seen, exif_tagmap); // A few tidbits to look for ImageIOParameter *p; if ((p = spec.find_attribute ("Exif:ColorSpace")) || (p = spec.find_attribute ("ColorSpace"))) { int cs = -1; if (p->type() == TypeDesc::UINT) cs = *(const unsigned int *)p->data(); else if (p->type() == TypeDesc::INT) cs = *(const int *)p->data(); else if (p->type() == TypeDesc::UINT16) cs = *(const unsigned short *)p->data(); else if (p->type() == TypeDesc::INT16) cs = *(const short *)p->data(); // Exif spec says that anything other than 0xffff==uncalibrated // should be interpreted to be sRGB. if (cs != 0xffff) spec.attribute ("oiio:ColorSpace", "sRGB"); } return true; } // Construct an Exif data block from the ImageSpec, appending the Exif // data as a big blob to the char vector. void encode_exif (const ImageSpec &spec, std::vector &blob) { TagMap& exif_tagmap (exif_tagmap_ref()); TagMap& gps_tagmap (gps_tagmap_ref()); // Reserve maximum space that an APP1 can take in a JPEG file, so // we can push_back to our heart's content and know that no offsets // or pointers to the exif vector's memory will change due to // reallocation. blob.reserve (0xffff); // Layout: // (tiffstart) TIFFHeader // number of top dir entries // top dir entry 0 // ... // top dir entry (point to Exif IFD) // data for top dir entries (except Exif) // // Exif IFD number of dir entries (n) // Exif IFD entry 0 // ... // Exif IFD entry n-1 // ...More Data for Exif entries... // Here is where the TIFF info starts. All TIFF tag offsets are // relative to this position within the blob. int tiffstart = blob.size(); // Handy macro -- grow the blob to accommodate a new variable, which // we position at its end. #define BLOB_ADD(vartype, varname) \ blob.resize (blob.size() + sizeof(vartype)); \ vartype & varname (* (vartype *) (&blob[0] + blob.size() - sizeof(vartype))); // Put a TIFF header BLOB_ADD (TIFFHeader, head); bool host_little = littleendian(); head.tiff_magic = host_little ? 0x4949 : 0x4d4d; head.tiff_version = 42; head.tiff_diroff = blob.size() - tiffstart; // Placeholder for number of directories BLOB_ADD (unsigned short, ndirs); ndirs = 0; // Accumulate separate tag directories for TIFF, Exif, GPS, and Interop. std::vector tiffdirs, exifdirs, gpsdirs, interopdirs; std::vector data; // Put data here int endmarker = 0; // 4 bytes of 0's that marks the end of a directory // Go through all spec attribs, add them to the appropriate tag // directory (tiff, gps, or exif). BOOST_FOREACH (const ImageIOParameter &p, spec.extra_attribs) { // Which tag domain are we using? if (! strncmp (p.name().c_str(), "GPS:", 4)) { // GPS int tag = gps_tagmap.tag (p.name().string()); encode_exif_entry (p, tag, gpsdirs, data, gps_tagmap); } else { // Not GPS int tag = exif_tagmap.tag (p.name().string()); if (tag < EXIFTAG_EXPOSURETIME || tag > EXIFTAG_IMAGEUNIQUEID) { encode_exif_entry (p, tag, tiffdirs, data, exif_tagmap); } else { encode_exif_entry (p, tag, exifdirs, data, exif_tagmap); } } } #if DEBUG_EXIF_WRITE std::cerr << "Blob header size " << blob.size() << "\n"; std::cerr << "tiff tags: " << tiffdirs.size() << "\n"; std::cerr << "exif tags: " << exifdirs.size() << "\n"; std::cerr << "gps tags: " << gpsdirs.size() << "\n"; #endif // If any legit Exif info was found... if (exifdirs.size()) { // Add some required Exif tags that wouldn't be in the spec append_dir_entry (exif_tagmap, exifdirs, data, EXIFTAG_EXIFVERSION, TIFF_UNDEFINED, 4, "0220"); append_dir_entry (exif_tagmap, exifdirs, data, EXIFTAG_FLASHPIXVERSION, TIFF_UNDEFINED, 4, "0100"); char componentsconfig[] = { 1, 2, 3, 0 }; append_dir_entry (exif_tagmap, exifdirs, data, EXIFTAG_COMPONENTSCONFIGURATION, TIFF_UNDEFINED, 4, componentsconfig); // Sort the exif tag directory std::sort (exifdirs.begin(), exifdirs.end(), tagcompare()); // If we had exif info, add one more main dir entry to point to // the private exif tag directory. unsigned int size = (unsigned int) data.size(); append_dir_entry (exif_tagmap, tiffdirs, data, TIFFTAG_EXIFIFD, TIFF_LONG, 1, &size); // Create interop directory boilerplate. // In all honesty, I have no idea what this is all about. append_dir_entry (exif_tagmap, interopdirs, data, 1, TIFF_ASCII, 4, "R98"); append_dir_entry (exif_tagmap, interopdirs, data, 2, TIFF_UNDEFINED, 4, "0100"); std::sort (interopdirs.begin(), interopdirs.end(), tagcompare()); #if 0 // FIXME -- is this necessary? If so, it's not completed. // Add the interop directory IFD entry to the main IFD size = (unsigned int) data.size(); append_dir_entry (exif_tagmap, tiffdirs, data, TIFFTAG_INTEROPERABILITYIFD, TIFF_LONG, 1, &size); std::sort (tiffdirs.begin(), tiffdirs.end(), tagcompare()); #endif } // If any GPS info was found... if (gpsdirs.size()) { // Add some required Exif tags that wouldn't be in the spec static char ver[] = { 2, 2, 0, 0 }; append_dir_entry (gps_tagmap, gpsdirs, data, GPSTAG_VERSIONID, TIFF_BYTE, 4, &ver); // Sort the gps tag directory std::sort (gpsdirs.begin(), gpsdirs.end(), tagcompare()); // If we had gps info, add one more main dir entry to point to // the private gps tag directory. unsigned int size = (unsigned int) data.size(); if (exifdirs.size()) size += sizeof(unsigned short) + exifdirs.size()*sizeof(TIFFDirEntry) + 4; append_dir_entry (exif_tagmap, tiffdirs, data, TIFFTAG_GPSIFD, TIFF_LONG, 1, &size); } // Where will the data begin (we need this to adjust the directory // offsets once we append data to the exif blob)? size_t datastart = blob.size() - tiffstart + tiffdirs.size() * sizeof(TIFFDirEntry) + 4 /* end marker */; // Adjust the TIFF offsets, add the TIFF directory entries to the main // Exif block, followed by 4 bytes of 0's. reoffset (tiffdirs, exif_tagmap, datastart); ndirs = tiffdirs.size(); if (ndirs) blob.insert (blob.end(), (char *)&tiffdirs[0], (char *)(&tiffdirs[0] + tiffdirs.size())); blob.insert (blob.end(), (char *)&endmarker, (char *)&endmarker + sizeof(int)); // If legit Exif metadata was found, adjust the Exif directory offsets, // append the Exif tag directory entries onto the main data block, // followed by 4 bytes of 0's. if (exifdirs.size()) { reoffset (exifdirs, exif_tagmap, datastart); unsigned short nd = exifdirs.size(); data.insert (data.end(), (char *)&nd, (char *)&nd + sizeof(nd)); data.insert (data.end(), (char *)&exifdirs[0], (char *)(&exifdirs[0] + exifdirs.size())); data.insert (data.end(), (char *)&endmarker, (char *)&endmarker + sizeof(int)); } // If legit GPS metadata was found, adjust the GPS directory offsets, // append the GPS tag directory entries onto the main data block, // followed by 4 bytes of 0's. if (gpsdirs.size()) { reoffset (gpsdirs, gps_tagmap, datastart); unsigned short nd = gpsdirs.size(); data.insert (data.end(), (char *)&nd, (char *)&nd + sizeof(nd)); data.insert (data.end(), (char *)&gpsdirs[0], (char *)(&gpsdirs[0] + gpsdirs.size())); data.insert (data.end(), (char *)&endmarker, (char *)&endmarker + sizeof(int)); } // Now append the data block onto the end of the main exif block that // we're returning to the caller. blob.insert (blob.end(), data.begin(), data.end()); #if DEBUG_EXIF_WRITE std::cerr << "resulting exif block is a total of " << blob.size() << "\n"; #if 0 std::cerr << "APP1 dump:\n"; BOOST_FOREACH (char c, blob) { if (c >= ' ') std::cerr << c << ' '; std::cerr << "(" << (int)c << ") "; } #endif #endif } bool exif_tag_lookup (string_view name, int &tag, int &tifftype, int &count) { const EXIF_tag_info *e = exif_tagmap_ref().find (name); if (! e) return false; // not found tag = e->tifftag; tifftype = e->tifftype; count = e->tiffcount; return true; } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/imagespeed_test.cpp0000644000175000017500000005517213151711064023557 0ustar mfvmfv/* Copyright 2012 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "OpenImageIO/imageio.h" #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/argparse.h" #include "OpenImageIO/ustring.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/timer.h" #include "OpenImageIO/unittest.h" #include #include #include #include OIIO_NAMESPACE_USING; static bool verbose = false; static int iterations = 1; static int ntrials = 1; static int numthreads = 0; static int autotile_size = 64; static bool iter_only = false; static bool no_iter = false; static std::string conversionname; static TypeDesc conversion = TypeDesc::UNKNOWN; // native by default static std::vector input_filename; static std::string output_filename; static std::string output_format; static std::vector buffer; static ImageSpec bufspec, outspec; static ImageCache *imagecache = NULL; static imagesize_t total_image_pixels = 0; static float cache_size = 0; static int parse_files (int argc, const char *argv[]) { input_filename.push_back (ustring(argv[0])); return 0; } static void getargs (int argc, char *argv[]) { bool help = false; ArgParse ap; ap.options ("imagespeed_test\n" OIIO_INTRO_STRING "\n" "Usage: imagespeed_test [options] filename...", "%*", parse_files, "", "--help", &help, "Print help message", "-v", &verbose, "Verbose mode", "--threads %d", &numthreads, ustring::format("Number of threads (default: %d)", numthreads).c_str(), "--iters %d", &iterations, ustring::format("Number of iterations (default: %d)", iterations).c_str(), "--trials %d", &ntrials, "Number of trials", "--autotile %d", &autotile_size, ustring::format("Autotile size (when used; default: %d)", autotile_size).c_str(), "--iteronly", &iter_only, "Run ImageBuf iteration tests only (not read tests)", "--noiter", &no_iter, "Don't run ImageBuf iteration tests", "--convert %s", &conversionname, "Convert to named type upon read (default: native)", "--cache %f", &cache_size, "Specify ImageCache size, in MB", "-o %s", &output_filename, "Test output by writing to this file", "-od %s", &output_format, "Requested output format", NULL); if (ap.parse (argc, (const char**)argv) < 0) { std::cerr << ap.geterror() << std::endl; ap.usage (); exit (EXIT_FAILURE); } if (help) { ap.usage (); exit (EXIT_FAILURE); } } static void time_read_image () { BOOST_FOREACH (ustring filename, input_filename) { ImageInput *in = ImageInput::open (filename.c_str()); ASSERT (in); in->read_image (conversion, &buffer[0]); in->close (); delete in; } } static void time_read_scanline_at_a_time () { BOOST_FOREACH (ustring filename, input_filename) { ImageInput *in = ImageInput::open (filename.c_str()); ASSERT (in); const ImageSpec &spec (in->spec()); size_t pixelsize = spec.nchannels * conversion.size(); if (! pixelsize) pixelsize = spec.pixel_bytes (true); // UNKNOWN -> native imagesize_t scanlinesize = spec.width * pixelsize; for (int y = 0; y < spec.height; ++y) { in->read_scanline (y+spec.y, 0, conversion, &buffer[scanlinesize*y]); } in->close (); delete in; } } static void time_read_64_scanlines_at_a_time () { BOOST_FOREACH (ustring filename, input_filename) { ImageInput *in = ImageInput::open (filename.c_str()); ASSERT (in); const ImageSpec &spec (in->spec()); size_t pixelsize = spec.nchannels * conversion.size(); if (! pixelsize) pixelsize = spec.pixel_bytes (true); // UNKNOWN -> native imagesize_t scanlinesize = spec.width * pixelsize; for (int y = 0; y < spec.height; y += 64) { in->read_scanlines (y+spec.y, std::min(y+spec.y+64, spec.y+spec.height), 0, conversion, &buffer[scanlinesize*y]); } in->close (); delete in; } } static void time_read_imagebuf () { imagecache->invalidate_all (true); BOOST_FOREACH (ustring filename, input_filename) { ImageBuf ib (filename.string(), imagecache); ib.read (0, 0, true, conversion); } } static void time_ic_get_pixels () { imagecache->invalidate_all (true); BOOST_FOREACH (ustring filename, input_filename) { const ImageSpec spec = (*imagecache->imagespec (filename)); imagecache->get_pixels (filename, 0, 0, spec.x, spec.x+spec.width, spec.y, spec.y+spec.height, spec.z, spec.z+spec.depth, conversion, &buffer[0]); } } static void test_read (const std::string &explanation, void (*func)(), int autotile=64, int autoscanline=1) { imagecache->invalidate_all (true); // Don't hold anything imagecache->attribute ("autotile", autotile); imagecache->attribute ("autoscanline", autoscanline); double t = time_trial (func, ntrials); double rate = double(total_image_pixels) / t; std::cout << " " << explanation << ": " << Strutil::timeintervalformat(t,2) << " = " << Strutil::format("%5.1f",rate/1.0e6) << " Mpel/s" << std::endl; } static void time_write_image () { ImageOutput *out = ImageOutput::create (output_filename); ASSERT (out); bool ok = out->open (output_filename, outspec); ASSERT (ok); out->write_image (bufspec.format, &buffer[0]); out->close (); delete out; } static void time_write_scanline_at_a_time () { ImageOutput *out = ImageOutput::create (output_filename); ASSERT (out); bool ok = out->open (output_filename, outspec); ASSERT (ok); size_t pixelsize = outspec.nchannels * sizeof(float); imagesize_t scanlinesize = outspec.width * pixelsize; for (int y = 0; y < outspec.height; ++y) { out->write_scanline (y+outspec.y, outspec.z, bufspec.format, &buffer[scanlinesize*y]); } out->close (); delete out; } static void time_write_64_scanlines_at_a_time () { ImageOutput *out = ImageOutput::create (output_filename); ASSERT (out); bool ok = out->open (output_filename, outspec); ASSERT (ok); size_t pixelsize = outspec.nchannels * sizeof(float); imagesize_t scanlinesize = outspec.width * pixelsize; for (int y = 0; y < outspec.height; y += 64) { out->write_scanlines (y+outspec.y, std::min(y+outspec.y+64, outspec.y+outspec.height), outspec.z, bufspec.format, &buffer[scanlinesize*y]); } out->close (); delete out; } static void time_write_tile_at_a_time () { ImageOutput *out = ImageOutput::create (output_filename); ASSERT (out); bool ok = out->open (output_filename, outspec); ASSERT (ok); size_t pixelsize = outspec.nchannels * sizeof(float); imagesize_t scanlinesize = outspec.width * pixelsize; imagesize_t planesize = outspec.height * scanlinesize; for (int z = 0; z < outspec.depth; z += outspec.tile_depth) { for (int y = 0; y < outspec.height; y += outspec.tile_height) { for (int x = 0; x < outspec.width; x += outspec.tile_width) { out->write_tile (x+outspec.x, y+outspec.y, z+outspec.z, bufspec.format, &buffer[scanlinesize*y+pixelsize*x], pixelsize, scanlinesize, planesize); } } } out->close (); delete out; } static void time_write_tiles_row_at_a_time () { ImageOutput *out = ImageOutput::create (output_filename); ASSERT (out); bool ok = out->open (output_filename, outspec); ASSERT (ok); size_t pixelsize = outspec.nchannels * sizeof(float); imagesize_t scanlinesize = outspec.width * pixelsize; for (int z = 0; z < outspec.depth; z += outspec.tile_depth) { for (int y = 0; y < outspec.height; y += outspec.tile_height) { out->write_tiles (outspec.x, outspec.x+outspec.width, y+outspec.y, y+outspec.y+outspec.tile_height, z+outspec.z, z+outspec.z+outspec.tile_depth, bufspec.format, &buffer[scanlinesize*y], pixelsize /*xstride*/, scanlinesize /*ystride*/); } } out->close (); delete out; } static void time_write_imagebuf () { ImageBuf ib (output_filename, bufspec, &buffer[0]); // wrap the buffer ImageOutput *out = ImageOutput::create (output_filename); ASSERT (out); bool ok = out->open (output_filename, outspec); ASSERT (ok); ib.write (out); out->close (); delete out; } static void test_write (const std::string &explanation, void (*func)(), int tilesize = 0) { outspec.tile_width = tilesize; outspec.tile_height = tilesize; outspec.tile_depth = 1; double t = time_trial (func, ntrials); double rate = double(total_image_pixels) / t; std::cout << " " << explanation << ": " << Strutil::timeintervalformat(t,2) << " = " << Strutil::format("%5.1f",rate/1.0e6) << " Mpel/s" << std::endl; } static float time_loop_pixels_1D (ImageBuf &ib, int iters) { ASSERT (ib.localpixels() && ib.pixeltype() == TypeDesc::TypeFloat); const ImageSpec &spec (ib.spec()); imagesize_t npixels = spec.image_pixels(); int nchannels = spec.nchannels; double sum = 0.0f; for (int i = 0; i < iters; ++i) { const float *f = (const float *) ib.pixeladdr (spec.x, spec.y, spec.z); ASSERT (f); for (imagesize_t p = 0; p < npixels; ++p) { sum += f[0]; f += nchannels; } } // std::cout << float(sum/npixels/iters) << "\n"; return float(sum/npixels/iters); } static float time_loop_pixels_3D (ImageBuf &ib, int iters) { ASSERT (ib.localpixels() && ib.pixeltype() == TypeDesc::TypeFloat); const ImageSpec &spec (ib.spec()); imagesize_t npixels = spec.image_pixels(); int nchannels = spec.nchannels; double sum = 0.0f; for (int i = 0; i < iters; ++i) { const float *f = (const float *) ib.pixeladdr (spec.x, spec.y, spec.z); ASSERT (f); for (int z = spec.z, ze = spec.z+spec.depth; z < ze; ++z) { for (int y = spec.y, ye = spec.y+spec.height; y < ye; ++y) { for (int x = spec.x, xe = spec.x+spec.width; x < xe; ++x) { sum += f[0]; f += nchannels; } } } } // std::cout << float(sum/npixels/iters) << "\n"; return float(sum/npixels/iters); } static float time_loop_pixels_3D_getchannel (ImageBuf &ib, int iters) { ASSERT (ib.pixeltype() == TypeDesc::TypeFloat); const ImageSpec &spec (ib.spec()); imagesize_t npixels = spec.image_pixels(); double sum = 0.0f; for (int i = 0; i < iters; ++i) { for (int z = spec.z, ze = spec.z+spec.depth; z < ze; ++z) { for (int y = spec.y, ye = spec.y+spec.height; y < ye; ++y) { for (int x = spec.x, xe = spec.x+spec.width; x < xe; ++x) { sum += ib.getchannel (x, y, 0, 0); } } } } // std::cout << float(sum/npixels/iters) << "\n"; return float(sum/npixels/iters); } static float time_iterate_pixels (ImageBuf &ib, int iters) { ASSERT (ib.pixeltype() == TypeDesc::TypeFloat); const ImageSpec &spec (ib.spec()); imagesize_t npixels = spec.image_pixels(); double sum = 0.0f; for (int i = 0; i < iters; ++i) { for (ImageBuf::ConstIterator p (ib); !p.done(); ++p) { sum += p[0]; } } // std::cout << float(sum/npixels/iters) << "\n"; return float(sum/npixels/iters); } static float time_iterate_pixels_slave_pos (ImageBuf &ib, int iters) { ASSERT (ib.pixeltype() == TypeDesc::TypeFloat); const ImageSpec &spec (ib.spec()); imagesize_t npixels = spec.image_pixels(); double sum = 0.0f; for (int i = 0; i < iters; ++i) { ImageBuf::ConstIterator slave (ib); for (ImageBuf::ConstIterator p (ib); !p.done(); ++p) { slave.pos (p.x(), p.y()); sum += p[0]; } } // std::cout << float(sum/npixels/iters) << "\n"; return float(sum/npixels/iters); } static float time_iterate_pixels_slave_incr (ImageBuf &ib, int iters) { ASSERT (ib.pixeltype() == TypeDesc::TypeFloat); const ImageSpec &spec (ib.spec()); imagesize_t npixels = spec.image_pixels(); double sum = 0.0f; for (int i = 0; i < iters; ++i) { ImageBuf::ConstIterator slave (ib); for (ImageBuf::ConstIterator p (ib); !p.done(); ++p) { sum += p[0]; ++slave; } } // std::cout << float(sum/npixels/iters) << "\n"; return float(sum/npixels/iters); } static void test_pixel_iteration (const std::string &explanation, float (*func)(ImageBuf&,int), bool preload, int iters=100, int autotile=64) { imagecache->invalidate_all (true); // Don't hold anything // Force the whole image to be read at once imagecache->attribute ("autotile", autotile); imagecache->attribute ("autoscanline", 1); ImageBuf ib (input_filename[0].string(), imagecache); ib.read (0, 0, preload, TypeDesc::TypeFloat); double t = time_trial (boost::bind(func,boost::ref(ib),iters), ntrials); double rate = double(ib.spec().image_pixels()) / (t/iters); std::cout << " " << explanation << ": " << Strutil::timeintervalformat(t/iters,3) << " = " << Strutil::format("%5.1f",rate/1.0e6) << " Mpel/s" << std::endl; } static void set_dataformat (const std::string &output_format, ImageSpec &outspec) { if (output_format == "uint8") outspec.format = TypeDesc::UINT8; else if (output_format == "int8") outspec.format = TypeDesc::INT8; else if (output_format == "uint16") outspec.format = TypeDesc::UINT16; else if (output_format == "int16") outspec.format = TypeDesc::INT16; else if (output_format == "half") outspec.format = TypeDesc::HALF; else if (output_format == "float") outspec.format = TypeDesc::FLOAT; else if (output_format == "double") outspec.format = TypeDesc::DOUBLE; // Otherwise leave at the default } int main (int argc, char **argv) { getargs (argc, argv); if (input_filename.size() == 0) { std::cout << "Error: Must supply a filename.\n"; return -1; } OIIO::attribute ("threads", numthreads); OIIO::attribute ("exr_threads", numthreads); conversion.fromstring (conversionname); imagecache = ImageCache::create (); if (cache_size) imagecache->attribute ("max_memory_MB", cache_size); imagecache->attribute ("forcefloat", 1); // Allocate a buffer big enough (for floats) bool all_scanline = true; total_image_pixels = 0; imagesize_t maxpelchans = 0; for (size_t i = 0; i < input_filename.size(); ++i) { ImageSpec spec; if (! imagecache->get_imagespec (input_filename[i], spec, 0, 0, true)) { std::cout << "File \"" << input_filename[i] << "\" could not be opened.\n"; return -1; } total_image_pixels += spec.image_pixels(); maxpelchans = std::max (maxpelchans, spec.image_pixels()*spec.nchannels); all_scanline &= (spec.tile_width == 0); } imagecache->invalidate_all (true); // Don't hold anything if (! iter_only) { std::cout << "Timing various ways of reading images:\n"; if (conversion == TypeDesc::UNKNOWN) std::cout << " ImageInput reads will keep data in native format.\n"; else std::cout << " ImageInput reads will convert data to " << conversion << "\n"; buffer.resize (maxpelchans*sizeof(float), 0); test_read ("read_image ", time_read_image, 0, 0); if (all_scanline) { test_read ("read_scanline (1 at a time) ", time_read_scanline_at_a_time, 0, 0); test_read ("read_scanlines (64 at a time) ", time_read_64_scanlines_at_a_time, 0, 0); } test_read ("ImageBuf read ", time_read_imagebuf, 0, 0); test_read ("ImageCache get_pixels ", time_ic_get_pixels, 0, 0); test_read ("ImageBuf read (autotile) ", time_read_imagebuf, autotile_size, 0); test_read ("ImageCache get_pixels (autotile) ", time_ic_get_pixels, autotile_size, 0); if (all_scanline) { // don't bother for tiled images test_read ("ImageBuf read (autotile+autoscanline) ", time_read_imagebuf, autotile_size, 1); test_read ("ImageCache get_pixels (autotile+autoscanline)", time_ic_get_pixels, autotile_size, 1); } if (verbose) std::cout << "\n" << imagecache->getstats(2) << "\n"; std::cout << std::endl; } if (output_filename.size()) { // Use the first image ImageInput *in = ImageInput::open (input_filename[0].c_str()); ASSERT (in); bufspec = in->spec (); in->read_image (conversion, &buffer[0]); in->close (); delete in; std::cout << "Timing ways of writing images:\n"; // imagecache->get_imagespec (input_filename[0], bufspec, 0, 0, true); ImageOutput *out = ImageOutput::create (output_filename); ASSERT (out); bool supports_tiles = out->supports("tiles"); delete out; outspec = bufspec; set_dataformat (output_format, outspec); std::cout << " writing as format " << outspec.format << "\n"; test_write ("write_image (scanline) ", time_write_image, 0); if (supports_tiles) test_write ("write_image (tiled) ", time_write_image, 64); test_write ("write_scanline (one at a time) ", time_write_scanline_at_a_time, 0); test_write ("write_scanlines (64 at a time) ", time_write_64_scanlines_at_a_time, 0); if (supports_tiles) { test_write ("write_tile (one at a time) ", time_write_tile_at_a_time, 64); test_write ("write_tiles (a whole row at a time) ", time_write_tiles_row_at_a_time, 64); } test_write ("ImageBuf::write (scanline) ", time_write_imagebuf, 0); if (supports_tiles) test_write ("ImageBuf::write (tiled) ", time_write_imagebuf, 64); std::cout << std::endl; } if (! no_iter) { const int iters = 64; std::cout << "Timing ways of iterating over an image:\n"; test_pixel_iteration ("Loop pointers on loaded image (\"1D\") ", time_loop_pixels_1D, true, iters); test_pixel_iteration ("Loop pointers on loaded image (\"3D\") ", time_loop_pixels_3D, true, iters); test_pixel_iteration ("Loop + getchannel on loaded image (\"3D\")", time_loop_pixels_3D_getchannel, true, iters/32); test_pixel_iteration ("Loop + getchannel on cached image (\"3D\")", time_loop_pixels_3D_getchannel, false, iters/32); test_pixel_iteration ("Iterate over a loaded image ", time_iterate_pixels, true, iters); test_pixel_iteration ("Iterate over a cache image ", time_iterate_pixels, false, iters); test_pixel_iteration ("Iterate over a loaded image (pos slave) ", time_iterate_pixels_slave_pos, true, iters); test_pixel_iteration ("Iterate over a cache image (pos slave) ", time_iterate_pixels_slave_pos, false, iters); test_pixel_iteration ("Iterate over a loaded image (incr slave)", time_iterate_pixels_slave_incr, true, iters); test_pixel_iteration ("Iterate over a cache image (incr slave) ", time_iterate_pixels_slave_incr, false, iters); } if (verbose) std::cout << "\n" << imagecache->getstats(2) << "\n"; ImageCache::destroy (imagecache); return unit_test_failures; } openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/color_ocio.cpp0000644000175000017500000011551413151711064022541 0ustar mfvmfv/* Copyright 2012 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include "OpenImageIO/strutil.h" #include "OpenImageIO/color.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/imagebufalgo_util.h" #include "OpenImageIO/refcnt.h" #ifdef USE_OCIO #include namespace OCIO = OCIO_NAMESPACE; #endif OIIO_NAMESPACE_BEGIN bool ColorConfig::supportsOpenColorIO () { #ifdef USE_OCIO return true; #else return false; #endif } // Hidden implementation of ColorConfig class ColorConfig::Impl { public: #ifdef USE_OCIO OCIO::ConstConfigRcPtr config_; #endif mutable std::string error_; std::vector > colorspaces; std::string linear_alias; // Alias for a scene-linear color space Impl() { } ~Impl() { } void inventory (); void add (const std::string &name, int index) { colorspaces.push_back (std::pair (name, index)); } }; // ColorConfig utility to take inventory of the color spaces available. // It sets up knowledge of "linear", "sRGB", "Rec709", // even if the underlying OCIO configuration lacks them. void ColorConfig::Impl::inventory () { #ifdef USE_OCIO if (config_) { bool nonraw = false; for (int i = 0, e = config_->getNumColorSpaces(); i < e; ++i) nonraw |= ! Strutil::iequals(config_->getColorSpaceNameByIndex(i), "raw"); if (nonraw) { for (int i = 0, e = config_->getNumColorSpaces(); i < e; ++i) add (config_->getColorSpaceNameByIndex(i), i); OCIO::ConstColorSpaceRcPtr lin = config_->getColorSpace ("scene_linear"); if (lin) linear_alias = lin->getName(); return; // If any non-"raw" spaces were defined, we're done } } // If we had some kind of bogus configuration that seemed to define // only a "raw" color space and nothing else, that's useless, so // figure out our own way to move forward. config_.reset(); #endif // If there was no configuration, or we didn't compile with OCIO // support at all, register a few basic names we know about. add ("linear", 0); add ("sRGB", 1); add ("Rec709", 2); } ColorConfig::ColorConfig (string_view filename) : m_impl (NULL) { reset (filename); } ColorConfig::~ColorConfig() { delete m_impl; m_impl = NULL; } bool ColorConfig::reset (string_view filename) { bool ok = true; delete m_impl; m_impl = new ColorConfig::Impl; #ifdef USE_OCIO OCIO::SetLoggingLevel (OCIO::LOGGING_LEVEL_NONE); try { if (filename.empty()) { getImpl()->config_ = OCIO::GetCurrentConfig(); } else { getImpl()->config_ = OCIO::Config::CreateFromFile (filename.c_str()); } } catch(OCIO::Exception &e) { getImpl()->error_ = e.what(); ok = false; } catch(...) { getImpl()->error_ = "An unknown error occurred in OpenColorIO creating the config"; ok = false; } #endif getImpl()->inventory (); // If we populated our own, remove any errors. if (getNumColorSpaces() && !getImpl()->error_.empty()) getImpl()->error_.clear(); return ok; } bool ColorConfig::error () const { return (!getImpl()->error_.empty()); } std::string ColorConfig::geterror () { std::string olderror = getImpl()->error_; getImpl()->error_ = ""; return olderror; } int ColorConfig::getNumColorSpaces () const { return (int) getImpl()->colorspaces.size(); } const char * ColorConfig::getColorSpaceNameByIndex (int index) const { return getImpl()->colorspaces[index].first.c_str(); } int ColorConfig::getNumLooks () const { #ifdef USE_OCIO if (getImpl()->config_) return getImpl()->config_->getNumLooks(); #endif return 0; } const char * ColorConfig::getLookNameByIndex (int index) const { #ifdef USE_OCIO if (getImpl()->config_) return getImpl()->config_->getLookNameByIndex (index); #endif return NULL; } const char * ColorConfig::getColorSpaceNameByRole (string_view role) const { #ifdef USE_OCIO if (getImpl()->config_) { OCIO::ConstColorSpaceRcPtr c = getImpl()->config_->getColorSpace (role.c_str()); // Catch special case of obvious name synonyms if (!c && Strutil::iequals(role,"linear")) c = getImpl()->config_->getColorSpace ("scene_linear"); if (!c && Strutil::iequals(role,"scene_linear")) c = getImpl()->config_->getColorSpace ("linear"); if (c) return c->getName(); } #endif // No OCIO at build time, or no OCIO configuration at run time if (Strutil::iequals (role, "linear") || Strutil::iequals (role, "scene_linear")) return "linear"; return NULL; // Dunno what role } TypeDesc ColorConfig::getColorSpaceDataType (string_view name, int *bits) const { #ifdef USE_OCIO OCIO::ConstColorSpaceRcPtr c = getImpl()->config_->getColorSpace (name.c_str()); if (c) { OCIO::BitDepth b = c->getBitDepth(); switch (b) { case OCIO::BIT_DEPTH_UNKNOWN : return TypeDesc::UNKNOWN; case OCIO::BIT_DEPTH_UINT8 : *bits = 8; return TypeDesc::UINT8; case OCIO::BIT_DEPTH_UINT10 : *bits = 10; return TypeDesc::UINT16; case OCIO::BIT_DEPTH_UINT12 : *bits = 12; return TypeDesc::UINT16; case OCIO::BIT_DEPTH_UINT14 : *bits = 14; return TypeDesc::UINT16; case OCIO::BIT_DEPTH_UINT16 : *bits = 16; return TypeDesc::UINT16; case OCIO::BIT_DEPTH_UINT32 : *bits = 32; return TypeDesc::UINT32; case OCIO::BIT_DEPTH_F16 : *bits = 16; return TypeDesc::HALF; case OCIO::BIT_DEPTH_F32 : *bits = 32; return TypeDesc::FLOAT; } } #endif return TypeDesc::UNKNOWN; } int ColorConfig::getNumDisplays() const { #ifdef USE_OCIO if (getImpl()->config_) return getImpl()->config_->getNumDisplays(); #endif return 0; } const char * ColorConfig::getDisplayNameByIndex(int index) const { #ifdef USE_OCIO if (getImpl()->config_) return getImpl()->config_->getDisplay(index); #endif return NULL; } int ColorConfig::getNumViews(string_view display) const { #ifdef USE_OCIO if (getImpl()->config_) return getImpl()->config_->getNumViews(display.c_str()); #endif return 0; } const char * ColorConfig::getViewNameByIndex(string_view display, int index) const { #ifdef USE_OCIO if (getImpl()->config_) return getImpl()->config_->getView(display.c_str(), index); #endif return NULL; } const char * ColorConfig::getDefaultDisplayName() const { #ifdef USE_OCIO if (getImpl()->config_) return getImpl()->config_->getDefaultDisplay(); #endif return NULL; } const char * ColorConfig::getDefaultViewName(string_view display) const { #ifdef USE_OCIO if (getImpl()->config_) return getImpl()->config_->getDefaultView(display.c_str()); #endif return NULL; } // Abstract wrapper class for objects that will apply color transformations. class ColorProcessor { public: ColorProcessor () {}; virtual ~ColorProcessor (void) { }; virtual bool isNoOp() const { return false; } virtual bool hasChannelCrosstalk() const { return false; } virtual void apply (float *data, int width, int height, int channels, stride_t chanstride, stride_t xstride, stride_t ystride) const = 0; }; #ifdef USE_OCIO // Custom ColorProcessor that wraps an OpenColorIO Processor. class ColorProcessor_OCIO : public ColorProcessor { public: ColorProcessor_OCIO (OCIO::ConstProcessorRcPtr p) : m_p(p) {}; virtual ~ColorProcessor_OCIO (void) { }; virtual bool isNoOp() const { return m_p->isNoOp(); } virtual bool hasChannelCrosstalk() const { return m_p->hasChannelCrosstalk(); } virtual void apply (float *data, int width, int height, int channels, stride_t chanstride, stride_t xstride, stride_t ystride) const { OCIO::PackedImageDesc pid (data, width, height, channels, chanstride, xstride, ystride); m_p->apply (pid); } private: OCIO::ConstProcessorRcPtr m_p; }; #endif // ColorProcessor that hard-codes sRGB-to-linear class ColorProcessor_sRGB_to_linear : public ColorProcessor { public: ColorProcessor_sRGB_to_linear () : ColorProcessor() { }; ~ColorProcessor_sRGB_to_linear () { }; virtual void apply (float *data, int width, int height, int channels, stride_t chanstride, stride_t xstride, stride_t ystride) const { if (channels > 3) channels = 3; if (channels == 3) { for (int y = 0; y < height; ++y) { char *d = (char *)data + y*ystride; for (int x = 0; x < width; ++x, d += xstride) { simd::float4 r; r.load ((float *)d, 3); r = sRGB_to_linear (simd::float4((float *)d)); r.store ((float *)d, 3); } } } else { for (int y = 0; y < height; ++y) { char *d = (char *)data + y*ystride; for (int x = 0; x < width; ++x, d += xstride) for (int c = 0; c < channels; ++c) ((float *)d)[c] = sRGB_to_linear (((float *)d)[c]); } } } }; // ColorProcessor that hard-codes linear-to-sRGB class ColorProcessor_linear_to_sRGB : public ColorProcessor { public: ColorProcessor_linear_to_sRGB () : ColorProcessor() { }; ~ColorProcessor_linear_to_sRGB () { }; virtual void apply (float *data, int width, int height, int channels, stride_t chanstride, stride_t xstride, stride_t ystride) const { if (channels > 3) channels = 3; if (channels == 3) { for (int y = 0; y < height; ++y) { char *d = (char *)data + y*ystride; for (int x = 0; x < width; ++x, d += xstride) { simd::float4 r; r.load ((float *)d, 3); r = linear_to_sRGB (simd::float4((float *)d)); r.store ((float *)d, 3); } } } else { for (int y = 0; y < height; ++y) { char *d = (char *)data + y*ystride; for (int x = 0; x < width; ++x, d += xstride) for (int c = 0; c < channels; ++c) ((float *)d)[c] = linear_to_sRGB (((float *)d)[c]); } } } }; // ColorProcessor that hard-codes Rec709-to-linear class ColorProcessor_Rec709_to_linear : public ColorProcessor { public: ColorProcessor_Rec709_to_linear () : ColorProcessor() { }; ~ColorProcessor_Rec709_to_linear () { }; virtual void apply (float *data, int width, int height, int channels, stride_t chanstride, stride_t xstride, stride_t ystride) const { if (channels > 3) channels = 3; for (int y = 0; y < height; ++y) { char *d = (char *)data + y*ystride; for (int x = 0; x < width; ++x, d += xstride) for (int c = 0; c < channels; ++c) ((float *)d)[c] = Rec709_to_linear (((float *)d)[c]); } } }; // ColorProcessor that hard-codes linear-to-Rec709 class ColorProcessor_linear_to_Rec709 : public ColorProcessor { public: ColorProcessor_linear_to_Rec709 () : ColorProcessor() { }; ~ColorProcessor_linear_to_Rec709 () { }; virtual void apply (float *data, int width, int height, int channels, stride_t chanstride, stride_t xstride, stride_t ystride) const { if (channels > 3) channels = 3; for (int y = 0; y < height; ++y) { char *d = (char *)data + y*ystride; for (int x = 0; x < width; ++x, d += xstride) for (int c = 0; c < channels; ++c) ((float *)d)[c] = linear_to_Rec709 (((float *)d)[c]); } } }; // ColorProcessor that does nothing (identity transform) class ColorProcessor_Ident : public ColorProcessor { public: ColorProcessor_Ident () : ColorProcessor() { }; ~ColorProcessor_Ident () { }; virtual void apply (float *data, int width, int height, int channels, stride_t chanstride, stride_t xstride, stride_t ystride) const { } }; ColorProcessor* ColorConfig::createColorProcessor (string_view inputColorSpace, string_view outputColorSpace) const { return createColorProcessor (inputColorSpace, outputColorSpace, "", ""); } ColorProcessor* ColorConfig::createColorProcessor (string_view inputColorSpace, string_view outputColorSpace, string_view context_key, string_view context_value) const { string_view inputrole, outputrole; std::string pending_error; #ifdef USE_OCIO // Ask OCIO to make a Processor that can handle the requested // transformation. OCIO::ConstProcessorRcPtr p; if (getImpl()->config_) { // If the names are roles, convert them to color space names string_view name; name = getColorSpaceNameByRole (inputColorSpace); if (! name.empty()) { inputrole = inputColorSpace; inputColorSpace = name; } name = getColorSpaceNameByRole (outputColorSpace); if (! name.empty()) { outputrole = outputColorSpace; outputColorSpace = name; } OCIO::ConstConfigRcPtr config = getImpl()->config_; OCIO::ConstContextRcPtr context = config->getCurrentContext(); std::vector keys, values; Strutil::split (context_key, keys, ","); Strutil::split (context_value, values, ","); if (keys.size() && values.size() && keys.size() == values.size()) { OCIO::ContextRcPtr ctx = context->createEditableCopy(); for (size_t i = 0; i < keys.size(); ++i) ctx->setStringVar (keys[i].c_str(), values[i].c_str()); context = ctx; } try { // Get the processor corresponding to this transform. p = getImpl()->config_->getProcessor(context, inputColorSpace.c_str(), outputColorSpace.c_str()); } catch(OCIO::Exception &e) { // Don't quit yet, remember the error and see if any of our // built-in knowledge of some generic spaces will save us. p.reset(); pending_error = e.what(); } catch(...) { getImpl()->error_ = "An unknown error occurred in OpenColorIO, getProcessor"; return NULL; } getImpl()->error_ = ""; if (p && ! p->isNoOp()) { // If we got a valid processor that does something useful, // return it now. If it boils down to a no-op, give a second // chance below to recognize it as a special case. return new ColorProcessor_OCIO(p); } } #endif // Either not compiled with OCIO support, or no OCIO configuration // was found at all. There are a few color conversions we know // about even in such dire conditions. using namespace Strutil; if (iequals(inputColorSpace,outputColorSpace)) { return new ColorProcessor_Ident; } if ((iequals(inputColorSpace,"linear") || iequals(inputrole,"linear") || iequals(inputColorSpace,"lnf") || iequals(inputColorSpace,"lnh")) && iequals(outputColorSpace,"sRGB")) { return new ColorProcessor_linear_to_sRGB; } if (iequals(inputColorSpace,"sRGB") && (iequals(outputColorSpace,"linear") || iequals(outputrole,"linear") || iequals(outputColorSpace,"lnf") || iequals(outputColorSpace,"lnh"))) { return new ColorProcessor_sRGB_to_linear; } if ((iequals(inputColorSpace,"linear") || iequals(inputrole,"linear") || iequals(inputColorSpace,"lnf") || iequals(inputColorSpace,"lnh")) && iequals(outputColorSpace,"Rec709")) { return new ColorProcessor_linear_to_Rec709; } if (iequals(inputColorSpace,"Rec709") && (iequals(outputColorSpace,"linear") || iequals(outputrole,"linear") || iequals(outputColorSpace,"lnf") || iequals(outputColorSpace,"lnh"))) { return new ColorProcessor_Rec709_to_linear; } #ifdef USE_OCIO if (p) { // If we found a procesor from OCIO, even if it was a NoOp, and we // still don't have a better idea, return it. return new ColorProcessor_OCIO(p); } #endif if (pending_error.size()) getImpl()->error_ = pending_error; return NULL; // if we get this far, we've failed } ColorProcessor* ColorConfig::createLookTransform (string_view looks, string_view inputColorSpace, string_view outputColorSpace, bool inverse, string_view context_key, string_view context_value) const { #ifdef USE_OCIO // Ask OCIO to make a Processor that can handle the requested // transformation. if (getImpl()->config_) { OCIO::ConstConfigRcPtr config = getImpl()->config_; OCIO::LookTransformRcPtr transform = OCIO::LookTransform::Create(); transform->setLooks (looks.c_str()); OCIO::TransformDirection dir; if (inverse) { // The TRANSFORM_DIR_INVERSE applies an inverse for the // end-to-end transform, which would otherwise do dst->inv // look -> src. This is an unintuitive result for the // artist (who would expect in, out to remain unchanged), so // we account for that here by flipping src/dst transform->setSrc (outputColorSpace.c_str()); transform->setDst (inputColorSpace.c_str()); dir = OCIO::TRANSFORM_DIR_INVERSE; } else { // forward transform->setSrc (inputColorSpace.c_str()); transform->setDst (outputColorSpace.c_str()); dir = OCIO::TRANSFORM_DIR_FORWARD; } OCIO::ConstContextRcPtr context = config->getCurrentContext(); std::vector keys, values; Strutil::split (context_key, keys, ","); Strutil::split (context_value, values, ","); if (keys.size() && values.size() && keys.size() == values.size()) { OCIO::ContextRcPtr ctx = context->createEditableCopy(); for (size_t i = 0; i < keys.size(); ++i) ctx->setStringVar (keys[i].c_str(), values[i].c_str()); context = ctx; } OCIO::ConstProcessorRcPtr p; try { // Get the processor corresponding to this transform. p = getImpl()->config_->getProcessor (context, transform, dir); } catch(OCIO::Exception &e) { getImpl()->error_ = e.what(); return NULL; } catch(...) { getImpl()->error_ = "An unknown error occurred in OpenColorIO, getProcessor"; return NULL; } getImpl()->error_ = ""; return new ColorProcessor_OCIO(p); } #endif return NULL; // if we get this far, we've failed } ColorProcessor* ColorConfig::createDisplayTransform (string_view display, string_view view, string_view inputColorSpace, string_view looks, string_view context_key, string_view context_value) const { #ifdef USE_OCIO // Ask OCIO to make a Processor that can handle the requested // transformation. if (getImpl()->config_) { OCIO::ConstConfigRcPtr config = getImpl()->config_; OCIO::DisplayTransformRcPtr transform = OCIO::DisplayTransform::Create(); transform->setInputColorSpaceName (inputColorSpace.c_str()); transform->setDisplay(display.c_str()); transform->setView(view.c_str()); if (looks.size()) { transform->setLooksOverride(looks.c_str()); transform->setLooksOverrideEnabled(true); } else { transform->setLooksOverrideEnabled(false); } OCIO::ConstContextRcPtr context = config->getCurrentContext(); std::vector keys, values; Strutil::split (context_key, keys, ","); Strutil::split (context_value, values, ","); if (keys.size() && values.size() && keys.size() == values.size()) { OCIO::ContextRcPtr ctx = context->createEditableCopy(); for (size_t i = 0; i < keys.size(); ++i) ctx->setStringVar (keys[i].c_str(), values[i].c_str()); context = ctx; } OCIO::ConstProcessorRcPtr p; try { // Get the processor corresponding to this transform. p = getImpl()->config_->getProcessor (context, transform, OCIO::TRANSFORM_DIR_FORWARD); } catch(OCIO::Exception &e) { getImpl()->error_ = e.what(); return NULL; } catch(...) { getImpl()->error_ = "An unknown error occurred in OpenColorIO, getProcessor"; return NULL; } getImpl()->error_ = ""; return new ColorProcessor_OCIO(p); } #endif return NULL; // if we get this far, we've failed } ColorProcessor* ColorConfig::createFileTransform (string_view name, bool inverse) const { #ifdef USE_OCIO // Ask OCIO to make a Processor that can handle the requested // transformation. if (getImpl()->config_) { OCIO::ConstConfigRcPtr config = getImpl()->config_; OCIO::FileTransformRcPtr transform = OCIO::FileTransform::Create(); transform->setSrc (name.c_str()); transform->setInterpolation (OCIO::INTERP_BEST); OCIO::TransformDirection dir = inverse ? OCIO::TRANSFORM_DIR_INVERSE : OCIO::TRANSFORM_DIR_FORWARD; OCIO::ConstContextRcPtr context = config->getCurrentContext(); OCIO::ConstProcessorRcPtr p; try { // Get the processor corresponding to this transform. p = getImpl()->config_->getProcessor (context, transform, dir); } catch(OCIO::Exception &e) { getImpl()->error_ = e.what(); return NULL; } catch(...) { getImpl()->error_ = "An unknown error occurred in OpenColorIO, getProcessor"; return NULL; } getImpl()->error_ = ""; return new ColorProcessor_OCIO(p); } #endif return NULL; // if we get this far, we've failed } string_view ColorConfig::parseColorSpaceFromString (string_view str) const { #ifdef USE_OCIO if (getImpl() && getImpl()->config_) { return getImpl()->config_->parseColorSpaceFromString (str.c_str()); } #endif return ""; } void ColorConfig::deleteColorProcessor (ColorProcessor * processor) { delete processor; } ////////////////////////////////////////////////////////////////////////// // // Image Processing Implementations static OIIO::shared_ptr default_colorconfig; // default color config static spin_mutex colorconfig_mutex; bool ImageBufAlgo::colorconvert (ImageBuf &dst, const ImageBuf &src, string_view from, string_view to, bool unpremult, ROI roi, int nthreads) { return colorconvert (dst, src, from, to, unpremult, NULL, roi, nthreads); } bool ImageBufAlgo::colorconvert (ImageBuf &dst, const ImageBuf &src, string_view from, string_view to, bool unpremult, ColorConfig *colorconfig, ROI roi, int nthreads) { return colorconvert (dst, src, from, to, unpremult, "", "", colorconfig, roi, nthreads); } bool ImageBufAlgo::colorconvert (ImageBuf &dst, const ImageBuf &src, string_view from, string_view to, bool unpremult, string_view context_key, string_view context_value, ColorConfig *colorconfig, ROI roi, int nthreads) { if (from.empty() || from == "current") { from = src.spec().get_string_attribute ("oiio:Colorspace", "Linear"); } if (from.empty() || to.empty()) { dst.error ("Unknown color space name"); return false; } ColorProcessor *processor = NULL; { spin_lock lock (colorconfig_mutex); if (! colorconfig) colorconfig = default_colorconfig.get(); if (! colorconfig) default_colorconfig.reset (colorconfig = new ColorConfig); processor = colorconfig->createColorProcessor (from, to, context_key, context_value); if (! processor) { if (colorconfig->error()) dst.error ("%s", colorconfig->geterror()); else dst.error ("Could not construct the color transform %s -> %s", from, to); return false; } } bool ok = colorconvert (dst, src, processor, unpremult, roi, nthreads); if (ok) dst.specmod().attribute ("oiio:ColorSpace", to); { spin_lock lock (colorconfig_mutex); colorconfig->deleteColorProcessor (processor); } return ok; } template static bool colorconvert_impl (ImageBuf &R, const ImageBuf &A, const ColorProcessor* processor, bool unpremult, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(colorconvert_impl, OIIO::ref(R), OIIO::cref(A), processor, unpremult, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case int width = roi.width(); // Temporary space to hold one RGBA scanline std::vector scanline(width*4, 0.0f); // Only process up to, and including, the first 4 channels. This // does let us process images with fewer than 4 channels, which is // the intent. // FIXME: Instead of loading the first 4 channels, obey // Rspec.alpha_channel index (but first validate that the // index is set properly for normal formats) int channelsToCopy = std::min (4, roi.nchannels()); // Walk through all data in our buffer. (i.e., crop or overscan) // FIXME: What about the display window? Should this actually promote // the datawindow to be union of data + display? This is useful if // the color of black moves. (In which case non-zero sections should // now be promoted). Consider the lin->log of a roto element, where // black now moves to non-black. float * dstPtr = NULL; const float fltmin = std::numeric_limits::min(); // If the processor has crosstalk, and we'll be using it, we should // reset the channels to 0 before loading each scanline. bool clearScanline = (channelsToCopy<4 && (processor->hasChannelCrosstalk() || unpremult)); ImageBuf::ConstIterator a (A, roi); ImageBuf::Iterator r (R, roi); for (int k = roi.zbegin; k < roi.zend; ++k) { for (int j = roi.ybegin; j < roi.yend; ++j) { // Clear the scanline if (clearScanline) memset (&scanline[0], 0, sizeof(float)*scanline.size()); // Load the scanline dstPtr = &scanline[0]; a.rerange (roi.xbegin, roi.xend, j, j+1, k, k+1); for ( ; !a.done(); ++a, dstPtr += 4) for (int c = 0; c < channelsToCopy; ++c) dstPtr[c] = a[c]; // Optionally unpremult if ((channelsToCopy >= 4) && unpremult) { for (int i = 0; i < width; ++i) { float alpha = scanline[4*i+3]; if (alpha > fltmin) { scanline[4*i+0] /= alpha; scanline[4*i+1] /= alpha; scanline[4*i+2] /= alpha; } } } // Apply the color transformation in place processor->apply (&scanline[0], width, 1, 4, sizeof(float), 4*sizeof(float), width*4*sizeof(float)); // Optionally premult if ((channelsToCopy >= 4) && unpremult) { for (int i = 0; i < width; ++i) { float alpha = scanline[4*i+3]; if (alpha > fltmin) { scanline[4*i+0] *= alpha; scanline[4*i+1] *= alpha; scanline[4*i+2] *= alpha; } } } // Store the scanline dstPtr = &scanline[0]; r.rerange (roi.xbegin, roi.xend, j, j+1, k, k+1); for ( ; !r.done(); ++r, dstPtr += 4) for (int c = 0; c < channelsToCopy; ++c) r[c] = dstPtr[c]; } } return true; } bool ImageBufAlgo::colorconvert (ImageBuf &dst, const ImageBuf &src, const ColorProcessor* processor, bool unpremult, ROI roi, int nthreads) { // If the processor is NULL, return false (error) if (!processor) { dst.error ("Passed NULL ColorProcessor to colorconvert() [probable application bug]"); return false; } // If the processor is a no-op and the conversion is being done // in place, no work needs to be done. Early exit. if (processor->isNoOp() && (&dst == &src)) return true; if (! IBAprep (roi, &dst, &src)) return false; // If the processor is a no-op (and it's not an in-place conversion), // use paste() to simplify the operation. if (processor->isNoOp()) { roi.chend = std::max (roi.chbegin+4, roi.chend); return ImageBufAlgo::paste (dst, roi.xbegin, roi.ybegin, roi.zbegin, roi.chbegin, src, roi, nthreads); } bool ok = true; OIIO_DISPATCH_COMMON_TYPES2 (ok, "colorconvert", colorconvert_impl, dst.spec().format, src.spec().format, dst, src, processor, unpremult, roi, nthreads); return ok; } bool ImageBufAlgo::ociolook (ImageBuf &dst, const ImageBuf &src, string_view looks, string_view from, string_view to, bool inverse, bool unpremult, string_view key, string_view value, ROI roi, int nthreads) { return ociolook (dst, src, looks, from, to, inverse, unpremult, key, value, NULL, roi, nthreads); } bool ImageBufAlgo::ociolook (ImageBuf &dst, const ImageBuf &src, string_view looks, string_view from, string_view to, bool inverse, bool unpremult, string_view key, string_view value, ColorConfig *colorconfig, ROI roi, int nthreads) { if (from.empty() || from == "current") { from = src.spec().get_string_attribute ("oiio:Colorspace", "Linear"); } if (to.empty() || to == "current") { to = src.spec().get_string_attribute ("oiio:Colorspace", "Linear"); } if (from.empty() || to.empty()) { dst.error ("Unknown color space name"); return false; } ColorProcessor *processor = NULL; { spin_lock lock (colorconfig_mutex); if (! colorconfig) colorconfig = default_colorconfig.get(); if (! colorconfig) default_colorconfig.reset (colorconfig = new ColorConfig); processor = colorconfig->createLookTransform (looks, from, to, inverse, key, value); if (! processor) { if (colorconfig->error()) dst.error ("%s", colorconfig->geterror()); else dst.error ("Could not construct the color transform"); return false; } } bool ok = colorconvert (dst, src, processor, unpremult, roi, nthreads); if (ok) dst.specmod().attribute ("oiio:ColorSpace", to); { spin_lock lock (colorconfig_mutex); colorconfig->deleteColorProcessor (processor); } return ok; } bool ImageBufAlgo::ociodisplay (ImageBuf &dst, const ImageBuf &src, string_view display, string_view view, string_view from, string_view looks, bool unpremult, string_view key, string_view value, ROI roi, int nthreads) { return ociodisplay (dst, src, display, view, from, looks, unpremult, key, value, NULL, roi, nthreads); } bool ImageBufAlgo::ociodisplay (ImageBuf &dst, const ImageBuf &src, string_view display, string_view view, string_view from, string_view looks, bool unpremult, string_view key, string_view value, ColorConfig *colorconfig, ROI roi, int nthreads) { if (from.empty() || from == "current") { from = src.spec().get_string_attribute ("oiio:Colorspace", "Linear"); } if (from.empty()) { dst.error ("Unknown color space name"); return false; } ColorProcessor *processor = NULL; { spin_lock lock (colorconfig_mutex); if (! colorconfig) colorconfig = default_colorconfig.get(); if (! colorconfig) default_colorconfig.reset (colorconfig = new ColorConfig); processor = colorconfig->createDisplayTransform (display, view, from, looks, key, value); if (! processor) { if (colorconfig->error()) dst.error ("%s", colorconfig->geterror()); else dst.error ("Could not construct the color transform"); return false; } } bool ok = colorconvert (dst, src, processor, unpremult, roi, nthreads); { spin_lock lock (colorconfig_mutex); colorconfig->deleteColorProcessor (processor); } return ok; } bool ImageBufAlgo::ociofiletransform (ImageBuf &dst, const ImageBuf &src, string_view name, bool inverse, bool unpremult, ColorConfig *colorconfig, ROI roi, int nthreads) { if (name.empty()) { dst.error ("Unknown filetransform name"); return false; } ColorProcessor *processor = NULL; { spin_lock lock (colorconfig_mutex); if (! colorconfig) colorconfig = default_colorconfig.get(); if (! colorconfig) default_colorconfig.reset (colorconfig = new ColorConfig); processor = colorconfig->createFileTransform (name, inverse); if (! processor) { if (colorconfig->error()) dst.error ("%s", colorconfig->geterror()); else dst.error ("Could not construct the color transform"); return false; } } bool ok = colorconvert (dst, src, processor, unpremult, roi, nthreads); if (ok) dst.specmod().attribute ("oiio:ColorSpace", name); { spin_lock lock (colorconfig_mutex); colorconfig->deleteColorProcessor (processor); } return ok; } bool ImageBufAlgo::colorconvert (float * color, int nchannels, const ColorProcessor* processor, bool unpremult) { // If the processor is NULL, return false (error) if (!processor) { return false; } // If the processor is a no-op, no work needs to be done. Early exit. if (processor->isNoOp()) return true; // Load the pixel float rgba[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; int channelsToCopy = std::min (4, nchannels); memcpy(rgba, color, channelsToCopy*sizeof (float)); const float fltmin = std::numeric_limits::min(); // Optionally unpremult if ((channelsToCopy>=4) && unpremult) { float alpha = rgba[3]; if (alpha > fltmin) { rgba[0] /= alpha; rgba[1] /= alpha; rgba[2] /= alpha; } } // Apply the color transformation processor->apply (rgba, 1, 1, 4, sizeof(float), 4*sizeof(float), 4*sizeof(float)); // Optionally premult if ((channelsToCopy>=4) && unpremult) { float alpha = rgba[3]; if (alpha > fltmin) { rgba[0] *= alpha; rgba[1] *= alpha; rgba[2] *= alpha; } } // Store the scanline memcpy(color, rgba, channelsToCopy*sizeof(float)); return true; } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/deepdata.cpp0000644000175000017500000010555513151711064022165 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/deepdata.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/strutil.h" OIIO_NAMESPACE_BEGIN // Each pixel has a capacity (number of samples allocated) and a number of // samples currently used. Erasing samples only reduces the samples in the // pixels without changing the capacity, so there is no reallocation or data // movement except for that pixel. Samples can be added without any // reallocation or copying data (other than that one pixel) unles the // capacity of the pixel is exceeded. Furthermore, only changes in capacity // need to lock the mutex. As long as capacity is not changing, threads may // change number of samples (inserting or deleting) as well as altering // data, simultaneously, as long as they are working on separate pixels. class DeepData::Impl { // holds all the nontrivial stuff friend class DeepData; public: std::vector m_channeltypes; // for each channel [c] std::vector m_channelsizes; // for each channel [c] std::vector m_channeloffsets; // for each channel [c] std::vector m_nsamples; // for each pixel [p] std::vector m_capacity; // for each pixel [p] std::vector m_cumcapacity; // cumulative capacity before pixel [p] std::vector m_data; // for each sample [p][s][c] std::vector m_channelnames; // For each channel[c] std::vector m_myalphachannel; // For each channel[c], its alpha // myalphachannel[c] gives the alpha channel corresponding to channel // c, or c if it is itself an alpha, or -1 if it doesn't appear to // be a color channel at all. size_t m_samplesize; int m_z_channel, m_zback_channel; int m_alpha_channel; bool m_allocated; spin_mutex m_mutex; Impl () : m_allocated(false) { clear(); } void clear () { m_channeltypes.clear(); m_channelsizes.clear(); m_channeloffsets.clear(); m_nsamples.clear(); m_capacity.clear(); m_cumcapacity.clear(); m_data.clear(); m_channelnames.clear (); m_myalphachannel.clear (); m_samplesize = 0; m_z_channel = -1; m_zback_channel = -1; m_alpha_channel = -1; m_allocated = false; } // If not already done, allocate data and cumcapacity void alloc (size_t npixels) { if (! m_allocated) { spin_lock lock (m_mutex); if (! m_allocated) { // m_cumcapacity.resize (npixels); size_t totalcapacity = 0; for (size_t i = 0; i < npixels; ++i) { m_cumcapacity[i] = totalcapacity; totalcapacity += m_capacity[i]; } m_data.resize (totalcapacity * m_samplesize); m_allocated = true; } } } size_t data_offset (int pixel, int channel, int sample) { DASSERT (int(m_cumcapacity.size()) > pixel); DASSERT (m_capacity[pixel] >= m_nsamples[pixel]); return (m_cumcapacity[pixel] + sample) * m_samplesize + m_channeloffsets[channel]; } void * data_ptr (int pixel, int channel, int sample) { size_t offset = data_offset (pixel, channel, sample); DASSERT (offset < m_data.size()); return &m_data[offset]; } size_t total_capacity () const { return m_cumcapacity.back() + m_capacity.back(); } inline void sanity () const { // int nchannels = int (m_channeltypes.size()); ASSERT (m_channeltypes.size() == m_channelsizes.size()); ASSERT (m_channeltypes.size() == m_channeloffsets.size()); int npixels = int(m_capacity.size()); ASSERT (m_nsamples.size() == m_capacity.size()); ASSERT (m_cumcapacity.size() == m_capacity.size()); if (m_allocated) { size_t totalcapacity = 0; for (int p = 0; p < npixels; ++p) { ASSERT (m_cumcapacity[p] == totalcapacity); totalcapacity += m_capacity[p]; ASSERT (m_capacity[p] >= m_nsamples[p]); } ASSERT (totalcapacity * m_samplesize == m_data.size()); } } }; DeepData::DeepData () : m_impl(NULL), m_npixels(0), m_nchannels(0) { } DeepData::DeepData (const ImageSpec &spec) : m_impl(NULL) { init (spec); } DeepData::~DeepData () { delete m_impl; } DeepData::DeepData (const DeepData &d) : m_impl(NULL) { m_npixels = d.m_npixels; m_nchannels = d.m_nchannels; if (d.m_impl) { m_impl = new Impl; *m_impl = *(d.m_impl); } } const DeepData& DeepData::operator= (const DeepData &d) { if (this != &d) { m_npixels = d.m_npixels; m_nchannels = d.m_nchannels; if (! m_impl) m_impl = new Impl; if (d.m_impl) *m_impl = *(d.m_impl); else m_impl->clear (); } return *this; } int DeepData::pixels () const { return m_npixels; } int DeepData::channels () const { return m_nchannels; } string_view DeepData::channelname (int c) const { DASSERT (m_impl); return (c >= 0 && c < m_nchannels) ? string_view(m_impl->m_channelnames[c]) : string_view(); } TypeDesc DeepData::channeltype (int c) const { DASSERT (m_impl); return (c >= 0 && c < m_nchannels) ? m_impl->m_channeltypes[c] : TypeDesc(); } size_t DeepData::channelsize (int c) const { DASSERT (m_impl); return (c >= 0 && c < m_nchannels) ? m_impl->m_channelsizes[c] : 0; } size_t DeepData::samplesize () const { return m_impl->m_samplesize; } // Is name the same as suffix, or does it end in ".suffix"? inline bool is_or_endswithdot (string_view name, string_view suffix) { return (Strutil::iequals (name, suffix) || (name.size() > suffix.size() && Strutil::iends_with (name, suffix) && name[name.size()-suffix.size()-1] == '.')); } void DeepData::init (int npix, int nchan, array_view channeltypes, array_view channelnames) { clear (); m_npixels = npix; m_nchannels = nchan; ASSERT (channeltypes.size() >= 1); if (! m_impl) m_impl = new Impl; if (int(channeltypes.size()) >= nchan) { m_impl->m_channeltypes.assign (channeltypes.data(), channeltypes.data()+nchan); } else { m_impl->m_channeltypes.clear (); m_impl->m_channeltypes.resize (m_nchannels, channeltypes[0]); } m_impl->m_channelsizes.resize (m_nchannels); m_impl->m_channeloffsets.resize (m_nchannels); m_impl->m_channelnames.resize (m_nchannels); m_impl->m_myalphachannel.resize (m_nchannels, -1); m_impl->m_samplesize = 0; m_impl->m_nsamples.resize (m_npixels, 0); m_impl->m_capacity.resize (m_npixels, 0); m_impl->m_cumcapacity.resize (m_npixels, 0); // Channel name hunt // First, find Z, Zback, A for (int c = 0; c < m_nchannels; ++c) { size_t size = m_impl->m_channeltypes[c].size(); m_impl->m_channelsizes[c] = size; m_impl->m_channeloffsets[c] = m_impl->m_samplesize; m_impl->m_samplesize += size; m_impl->m_channelnames[c] = channelnames[c]; if (m_impl->m_z_channel < 0 && is_or_endswithdot (channelnames[c], "Z")) m_impl->m_z_channel = c; else if (m_impl->m_zback_channel < 0 && is_or_endswithdot (channelnames[c], "Zback")) m_impl->m_zback_channel = c; else if (m_impl->m_alpha_channel < 0 && is_or_endswithdot (channelnames[c], "A")) m_impl->m_alpha_channel = c; } // Now try to find which alpha corresponds to each channel for (int c = 0; c < m_nchannels; ++c) { // Skip non-color channels if (c == m_impl->m_z_channel || c == m_impl->m_zback_channel || m_impl->m_channeltypes[c] == TypeDesc::UINT32) continue; string_view name (channelnames[c]); // Alpha channels are their own alpha if (is_or_endswithdot (name, "A") || is_or_endswithdot (name, "RA") || is_or_endswithdot (name, "GA") || is_or_endswithdot (name, "BA")) { m_impl->m_myalphachannel[c] = c; continue; } // For anything else, try to find its channel string_view prefix = name, suffix = name; size_t dot = name.find_last_of ('.'); if (dot == string_view::npos) { // No dot prefix.clear (); } else { // dot prefix = prefix.substr (0, dot+1); suffix = suffix.substr (dot+1); } std::string targetalpha = std::string(prefix) + std::string(suffix) + "A"; for (int i = 0; i < m_nchannels; ++i) { if (Strutil::iequals (m_impl->m_channelnames[i], targetalpha)) { m_impl->m_myalphachannel[c] = i; break; } } if (m_impl->m_myalphachannel[c] < 0) m_impl->m_myalphachannel[c] = m_impl->m_alpha_channel; } } void DeepData::init (const ImageSpec &spec) { if (int(spec.channelformats.size()) == spec.nchannels) init ((int) spec.image_pixels(), spec.nchannels, spec.channelformats, spec.channelnames); else init ((int) spec.image_pixels(), spec.nchannels, spec.format, spec.channelnames); } void DeepData::clear () { m_npixels = 0; m_nchannels = 0; if (m_impl) m_impl->clear (); } void DeepData::free () { clear (); delete m_impl; m_impl = NULL; } int DeepData::capacity (int pixel) const { if (pixel < 0 || pixel >= m_npixels) return 0; DASSERT (m_impl && m_impl->m_capacity.size() > size_t(pixel)); return m_impl->m_capacity[pixel]; } void DeepData::set_capacity (int pixel, int samps) { if (pixel < 0 || pixel >= m_npixels) return; ASSERT (m_impl); spin_lock lock (m_impl->m_mutex); if (m_impl->m_allocated) { // Data already allocated. Expand capacity if necessary, don't // contract. (FIXME?) int n = (int)capacity(pixel); if (samps > n) { int toadd = samps-n; if (m_impl->m_data.empty()) { size_t newtotal = (m_impl->total_capacity()+toadd); m_impl->m_data.resize (newtotal*samplesize()); } else { size_t offset = m_impl->data_offset (pixel, 0, n); m_impl->m_data.insert (m_impl->m_data.begin() + offset, toadd*samplesize(), 0); } // Adjust the cumulative prefix sum of samples for subsequent pixels for (int p = pixel+1; p < m_npixels; ++p) m_impl->m_cumcapacity[p] += toadd; m_impl->m_capacity[pixel] = samps; } } else { m_impl->m_capacity[pixel] = samps; } } int DeepData::samples (int pixel) const { if (pixel < 0 || pixel >= m_npixels) return 0; DASSERT (m_impl); return m_impl->m_nsamples[pixel]; } void DeepData::set_samples (int pixel, int samps) { if (pixel < 0 || pixel >= m_npixels) return; ASSERT (m_impl); if (m_impl->m_allocated) { // Data already allocated. Turn it into an insert or delete int n = (int)samples(pixel); if (samps > n) insert_samples (pixel, n, samps - n); else if (samps < n) erase_samples (pixel, samps, n-samps); } else { m_impl->m_nsamples[pixel] = samps; m_impl->m_capacity[pixel] = std::max (unsigned(samps), m_impl->m_capacity[pixel]); } } void DeepData::set_all_samples (array_view samples) { if (samples.size() != size_t(m_npixels)) return; ASSERT (m_impl); if (m_impl->m_allocated) { // Data already allocated: set pixels individually for (int p = 0; p < m_npixels; ++p) set_samples (p, int(samples[p])); } else { // Data not yet allocated: copy in one shot m_impl->m_nsamples.assign (&samples[0], &samples[m_npixels]); m_impl->m_capacity.assign (&samples[0], &samples[m_npixels]); } } void DeepData::insert_samples (int pixel, int samplepos, int n) { int oldsamps = samples(pixel); set_capacity (pixel, oldsamps + n); // set_capacity is thread-safe, it locks internalls. Once the acpacity // is adjusted, we can alter nsamples or copy the data around within // the pixel without a lock, we presume that if multiple threads are // in play, they are working on separate pixels. if (m_impl->m_allocated) { // Move the data if (samplepos < oldsamps) { size_t offset = m_impl->data_offset (pixel, 0, samplepos); size_t end = m_impl->data_offset (pixel, 0, oldsamps); std::copy_backward (m_impl->m_data.begin() + offset, m_impl->m_data.begin() + end, m_impl->m_data.begin() + end + n*samplesize()); } } // Add to this pixel's sample count m_impl->m_nsamples[pixel] += n; } void DeepData::erase_samples (int pixel, int samplepos, int n) { // DON'T reduce the capacity! Just leave holes for speed. // Because erase_samples only moves data within a pixel and doesn't // change the capacity, no lock is needed. n = std::min (n, int(m_impl->m_nsamples[pixel])); if (m_impl->m_allocated) { // Move the data int oldsamps = samples(pixel); size_t offset = m_impl->data_offset (pixel, 0, samplepos); size_t end = m_impl->data_offset (pixel, 0, oldsamps); std::copy (m_impl->m_data.begin() + offset + n*samplesize(), m_impl->m_data.begin() + end, m_impl->m_data.begin() + offset); } m_impl->m_nsamples[pixel] -= n; } void * DeepData::data_ptr (int pixel, int channel, int sample) { m_impl->alloc (m_npixels); if (pixel < 0 || pixel >= m_npixels || channel < 0 || channel >= m_nchannels || !m_impl || sample < 0 || sample >= int(m_impl->m_nsamples[pixel])) return NULL; return m_impl->data_ptr (pixel, channel, sample); } const void * DeepData::data_ptr (int pixel, int channel, int sample) const { if (pixel < 0 || pixel >= m_npixels || channel < 0 || channel >= m_nchannels || !m_impl || !m_impl->m_data.size() || sample < 0 || sample >= int(m_impl->m_nsamples[pixel])) return NULL; return m_impl->data_ptr (pixel, channel, sample); } float DeepData::deep_value (int pixel, int channel, int sample) const { const void *ptr = data_ptr (pixel, channel, sample); if (! ptr) return 0.0f; switch (channeltype(channel).basetype) { case TypeDesc::FLOAT : return ((const float *)ptr)[0]; case TypeDesc::HALF : return ((const half *)ptr)[0]; case TypeDesc::UINT : return ConstDataArrayProxy((const unsigned int *)ptr)[0]; case TypeDesc::UINT8 : return ConstDataArrayProxy((const unsigned char *)ptr)[0]; case TypeDesc::INT8 : return ConstDataArrayProxy((const char *)ptr)[0]; case TypeDesc::UINT16: return ConstDataArrayProxy((const unsigned short *)ptr)[0]; case TypeDesc::INT16 : return ConstDataArrayProxy((const short *)ptr)[0]; case TypeDesc::INT : return ConstDataArrayProxy((const int *)ptr)[0]; case TypeDesc::UINT64: return ConstDataArrayProxy((const unsigned long long *)ptr)[0]; case TypeDesc::INT64 : return ConstDataArrayProxy((const long long *)ptr)[0]; default: ASSERT_MSG (0, "Unknown/unsupported data type %d", channeltype(channel).basetype); return 0.0f; } } uint32_t DeepData::deep_value_uint (int pixel, int channel, int sample) const { const void *ptr = data_ptr (pixel, channel, sample); if (! ptr) return 0; switch (channeltype(channel).basetype) { case TypeDesc::UINT : return ((const unsigned int *)ptr)[0]; case TypeDesc::FLOAT : return ConstDataArrayProxy((const float *)ptr)[0]; case TypeDesc::HALF : return ConstDataArrayProxy((const half *)ptr)[0]; case TypeDesc::UINT8 : return ConstDataArrayProxy((const unsigned char *)ptr)[0]; case TypeDesc::INT8 : return ConstDataArrayProxy((const char *)ptr)[0]; case TypeDesc::UINT16: return ConstDataArrayProxy((const unsigned short *)ptr)[0]; case TypeDesc::INT16 : return ConstDataArrayProxy((const short *)ptr)[0]; case TypeDesc::INT : return ConstDataArrayProxy((const int *)ptr)[0]; case TypeDesc::UINT64: return ConstDataArrayProxy((const unsigned long long *)ptr)[0]; case TypeDesc::INT64 : return ConstDataArrayProxy((const long long *)ptr)[0]; default: ASSERT_MSG (0, "Unknown/unsupported data type %d", channeltype(channel).basetype); return 0; } } void DeepData::set_deep_value (int pixel, int channel, int sample, float value) { void *ptr = data_ptr (pixel, channel, sample); if (! ptr) return; switch (channeltype(channel).basetype) { case TypeDesc::FLOAT : DataArrayProxy((float *)ptr)[0] = value; break; case TypeDesc::HALF : DataArrayProxy((half *)ptr)[0] = value; break; case TypeDesc::UINT : DataArrayProxy((uint32_t *)ptr)[0] = value; break; case TypeDesc::UINT8 : DataArrayProxy((unsigned char *)ptr)[0] = value; break; case TypeDesc::INT8 : DataArrayProxy((char *)ptr)[0] = value; break; case TypeDesc::UINT16: DataArrayProxy((unsigned short *)ptr)[0] = value; break; case TypeDesc::INT16 : DataArrayProxy((short *)ptr)[0] = value; break; case TypeDesc::INT : DataArrayProxy((int *)ptr)[0] = value; break; case TypeDesc::UINT64: DataArrayProxy((uint64_t *)ptr)[0] = value; break; case TypeDesc::INT64 : DataArrayProxy((int64_t *)ptr)[0] = value; break; default: ASSERT_MSG (0, "Unknown/unsupported data type %d", channeltype(channel).basetype); } } void DeepData::set_deep_value (int pixel, int channel, int sample, uint32_t value) { void *ptr = data_ptr (pixel, channel, sample); if (! ptr) return; switch (channeltype(channel).basetype) { case TypeDesc::FLOAT : DataArrayProxy((float *)ptr)[0] = value; break; case TypeDesc::HALF : DataArrayProxy((half *)ptr)[0] = value; break; case TypeDesc::UINT8 : DataArrayProxy((unsigned char *)ptr)[0] = value; break; case TypeDesc::INT8 : DataArrayProxy((char *)ptr)[0] = value; break; case TypeDesc::UINT16: DataArrayProxy((unsigned short *)ptr)[0] = value; break; case TypeDesc::INT16 : DataArrayProxy((short *)ptr)[0] = value; break; case TypeDesc::UINT : DataArrayProxy((uint32_t *)ptr)[0] = value; break; case TypeDesc::INT : DataArrayProxy((int *)ptr)[0] = value; break; case TypeDesc::UINT64: DataArrayProxy((uint64_t *)ptr)[0] = value; break; case TypeDesc::INT64 : DataArrayProxy((int64_t *)ptr)[0] = value; break; default: ASSERT_MSG (0, "Unknown/unsupported data type %d", channeltype(channel).basetype); } } array_view DeepData::all_channeltypes () const { ASSERT (m_impl); return m_impl->m_channeltypes; } array_view DeepData::all_samples () const { ASSERT (m_impl); return m_impl->m_nsamples; } array_view DeepData::all_data () const { ASSERT (m_impl); m_impl->alloc (m_npixels); return m_impl->m_data; } void DeepData::get_pointers (std::vector &pointers) const { ASSERT (m_impl); m_impl->alloc (m_npixels); pointers.resize (pixels()*channels()); for (int i = 0; i < m_npixels; ++i) { if (m_impl->m_nsamples[i]) for (int c = 0; c < m_nchannels; ++c) pointers[i*m_nchannels+c] = (void *)m_impl->data_ptr (i, c, 0); else for (int c = 0; c < m_nchannels; ++c) pointers[i*m_nchannels+c] = NULL; } } bool DeepData::copy_deep_sample (int pixel, int sample, const DeepData &src, int srcpixel, int srcsample) { const void *srcdata = src.data_ptr (srcpixel, 0, srcsample); int nchans = channels(); if (! srcdata || nchans != src.channels()) return false; int nsamples = src.samples(srcpixel); set_samples (pixel, std::max (samples(pixel), nsamples)); for (int c = 0; c < m_nchannels; ++c) { if (channeltype(c) == TypeDesc::UINT32 && src.channeltype(c) == TypeDesc::UINT32) set_deep_value (pixel, c, sample, src.deep_value_uint (srcpixel, c, srcsample)); else set_deep_value (pixel, c, sample, src.deep_value (srcpixel, c, srcsample)); } return true; } bool DeepData::copy_deep_pixel (int pixel, const DeepData &src, int srcpixel) { if (pixel < 0 || pixel >= pixels()) { // std::cout << "dst pixel was " << pixel << "\n"; DASSERT (0 && "Out of range pixel index"); return false; } if (srcpixel < 0 || srcpixel >= src.pixels()) { // Copying empty pixel -- set samples to 0 and we're done // std::cout << "Source pixel was " << srcpixel << "\n"; set_samples (pixel, 0); return true; } int nchans = channels(); if (nchans != src.channels()) { DASSERT (0 && "Number of channels don't match."); return false; } int nsamples = src.samples(srcpixel); set_samples (pixel, nsamples); if (nsamples == 0) return true; bool sametypes = samplesize() == src.samplesize(); if (sametypes) for (int c = 0; c < nchans; ++c) sametypes &= (channeltype(c) == src.channeltype(c)); if (sametypes) memcpy (data_ptr (pixel, 0, 0), src.data_ptr (srcpixel, 0, 0), samplesize()*nsamples); else { for (int c = 0; c < nchans; ++c) { if (channeltype(c) == TypeDesc::UINT32 && src.channeltype(c) == TypeDesc::UINT32) for (int s = 0; s < nsamples; ++s) set_deep_value (pixel, c, s, src.deep_value_uint (srcpixel, c, s)); else for (int s = 0; s < nsamples; ++s) set_deep_value (pixel, c, s, src.deep_value (srcpixel, c, s)); } } return true; } void DeepData::split (int pixel, float depth) { #if OIIO_CPLUSPLUS_VERSION >= 11 using std::log1p; using std::expm1; #endif int zchan = m_impl->m_z_channel; int zbackchan = m_impl->m_zback_channel; if (zchan < 0) return; // No channel labeled Z -- we don't know what to do if (zbackchan < 0) return; // The samples are not extended -- nothing to split int nchans = channels(); for (int s = 0; s < samples(pixel); ++s) { float zf = deep_value (pixel, zchan, s); // z front float zb = deep_value (pixel, zbackchan, s); // z back if (zf < depth && zb > depth) { // The sample spans depth, so split it. // See http://www.openexr.com/InterpretingDeepPixels.pdf insert_samples (pixel, s+1); copy_deep_sample (pixel, s+1, *this, pixel, s); set_deep_value (pixel, zbackchan, s, depth); set_deep_value (pixel, zchan, s+1, depth); // We have to proceed in two passes, since we may reuse the // alpha values, we can't overwrite them yet. for (int c = 0; c < nchans; ++c) { int alphachan = m_impl->m_myalphachannel[c]; if (alphachan < 0 // No alpha || alphachan == c) // This is an alpha! continue; float a = clamp (deep_value (pixel, alphachan, s), 0.0f, 1.0f); if (a == 1.0f) // Opaque or channels without alpha, we're done. continue; float xf = (depth - zf) / (zb - zf); float xb = (zb - depth) / (zb - zf); if (a > std::numeric_limits::min()) { float af = -expm1 (xf * log1p (-a)); float ab = -expm1 (xb * log1p (-a)); float val = deep_value (pixel, c, s); set_deep_value (pixel, c, s, (af/a) * val); set_deep_value (pixel, c, s+1, (ab/a) * val); } else { float val = deep_value (pixel, c, s); set_deep_value (pixel, c, s, val * xf); set_deep_value (pixel, c, s+1, val * xb); } } // Now that we've adjusted the colors, do the alphas for (int c = 0; c < nchans; ++c) { int alphachan = m_impl->m_myalphachannel[c]; if (alphachan != c) continue; // skip if not an alpha float a = clamp (deep_value (pixel, alphachan, s), 0.0f, 1.0f); if (a == 1.0f) // Opaque or channels without alpha, we're done. continue; float xf = (depth - zf) / (zb - zf); float xb = (zb - depth) / (zb - zf); if (a > std::numeric_limits::min()) { float af = -expm1 (xf * log1p (-a)); float ab = -expm1 (xb * log1p (-a)); set_deep_value (pixel, c, s, af); set_deep_value (pixel, c, s+1, ab); } else { set_deep_value (pixel, c, s, a * xf); set_deep_value (pixel, c, s+1, a * xb); } } } } } namespace { // Comparitor functor for depth sorting sample indices of a deep pixel. class SampleComparator { public: SampleComparator (const DeepData &dd, int pixel, int zchan, int zbackchan) : deepdata(dd), pixel(pixel), zchan(zchan), zbackchan(zbackchan) { } bool operator() (int i, int j) const { // If either has a lower z, that's the lower float iz = deepdata.deep_value (pixel, zchan, i); float jz = deepdata.deep_value (pixel, zchan, j); if (iz < jz) return true; if (iz > jz) return false; // If both z's are equal, sort based on zback float izb = deepdata.deep_value (pixel, zbackchan, i); float jzb = deepdata.deep_value (pixel, zbackchan, j); return izb < jzb; } private: const DeepData &deepdata; int pixel; int zchan, zbackchan; }; } void DeepData::sort (int pixel) { int zchan = m_impl->m_z_channel; if (zchan < 0) return; // No channel labeled Z -- we don't know what to do int zbackchan = m_impl->m_z_channel; if (zbackchan < 0) zbackchan = zchan; int nsamples = samples(pixel); if (nsamples < 2) return; // 0 or 1 samples -- no sort necessary // Ick, std::sort and friends take a custom comparator, but not a custom // swapper, so there's no way to std::sort a data type whose size is not // known at compile time. So we just sort the indices! int *sample_indices = OIIO_ALLOCA (int, nsamples); #if OIIO_CPLUSPLUS_VERSION >= 11 std::iota (sample_indices, sample_indices+nsamples, 0); #else for (int i = 0; i < nsamples; ++i) sample_indices[i] = i; #endif std::stable_sort (sample_indices, sample_indices+nsamples, SampleComparator(*this, pixel, zchan, zbackchan)); // Now copy around using a temp buffer size_t samplebytes = samplesize(); char *tmppixel = OIIO_ALLOCA (char, samplebytes*nsamples); memcpy (tmppixel, data_ptr (pixel, 0, 0), samplebytes*nsamples); for (int i = 0; i < nsamples; ++i) memcpy (data_ptr (pixel, 0, i), tmppixel+samplebytes*sample_indices[i], samplebytes); } void DeepData::merge_overlaps (int pixel) { #if OIIO_CPLUSPLUS_VERSION >= 11 using std::log1p; #endif int zchan = m_impl->m_z_channel; int zbackchan = m_impl->m_zback_channel; if (zchan < 0) return; // No channel labeled Z -- we don't know what to do if (zbackchan < 0) zbackchan = zchan; // Missing Zback -- use Z int nchans = channels(); for (int s = 1 /* YES, 1 */; s < samples(pixel); ++s) { float zf = deep_value (pixel, zchan, s); // z front float zb = deep_value (pixel, zbackchan, s); // z back if (zf == deep_value (pixel, zchan, s-1) && zb == deep_value (pixel, zbackchan, s-1)) { // The samples overlap exactly, merge them per // See http://www.openexr.com/InterpretingDeepPixels.pdf for (int c = 0; c < nchans; ++c) { // set the colors int alphachan = m_impl->m_myalphachannel[c]; if (alphachan < 0) continue; // Not color or alpha if (alphachan == c) continue; // Adjust the alphas in a second pass below float a1 = (alphachan < 0) ? 1.0f : clamp (deep_value (pixel, alphachan, s-1), 0.0f, 1.0f); float a2 = (alphachan < 0) ? 1.0f : clamp (deep_value (pixel, alphachan, s), 0.0f, 1.0f); float c1 = deep_value (pixel, c, s-1); float c2 = deep_value (pixel, c, s); float am = a1 + a2 - a1 * a2; float cm; if (a1 == 1.0f && a2 == 1.0f) cm = (c1 + c2) / 2.0f; else if (a1 == 1.0f) cm = c1; else if (a2 == 1.0f) cm = c2; else { static const float MAX = std::numeric_limits::max(); float u1 = -log1p (-a1); float v1 = (u1 < a1 * MAX)? u1 / a1: 1.0f; float u2 = -log1p (-a2); float v2 = (u2 < a2 * MAX)? u2 / a2: 1.0f; float u = u1 + u2; float w = (u > 1.0f || am < u * MAX)? am / u: 1.0f; cm = (c1 * v1 + c2 * v2) * w; } set_deep_value (pixel, c, s-1, cm); // setting color } for (int c = 0; c < nchans; ++c) { // set the alphas int alphachan = m_impl->m_myalphachannel[c]; if (alphachan != c) continue; // This pass is only for alphas float a1 = (alphachan < 0) ? 1.0f : clamp (deep_value (pixel, alphachan, s-1), 0.0f, 1.0f); float a2 = (alphachan < 0) ? 1.0f : clamp (deep_value (pixel, alphachan, s), 0.0f, 1.0f); float am = a1 + a2 - a1 * a2; set_deep_value (pixel, c, s-1, am); // setting alpha } // Now eliminate sample s and revisit again erase_samples (pixel, s, 1); --s; } } } void DeepData::merge_deep_pixels (int pixel, const DeepData &src, int srcpixel) { int srcsamples = src.samples(srcpixel); if (srcsamples == 0) return; // No samples to merge int dstsamples = samples(pixel); if (dstsamples == 0) { // Nothing in our pixel yet, so just copy src's pixel copy_deep_pixel (pixel, src, srcpixel); return; } // Need to merge the pixels // First, merge all of src's samples into our pixel set_samples (pixel, dstsamples+srcsamples); for (int i = 0; i < srcsamples; ++i) copy_deep_sample (pixel, dstsamples+i, src, srcpixel, i); // Now ALL the samples from both images are in our pixel. // Mutually split the samples against each other. sort (pixel); // sort first so we only loop once int zchan = m_impl->m_z_channel; int zbackchan = m_impl->m_zback_channel; for (int s = 0; s < samples(pixel); ++s) { float z = deep_value (pixel, zchan, s); float zback = deep_value (pixel, zbackchan, s); split (pixel, z); split (pixel, zback); } sort (pixel); // Now merge the overlaps merge_overlaps (pixel); } void DeepData::occlusion_cull (int pixel) { int alpha_channel = m_impl->m_alpha_channel; if (alpha_channel < 0) return; // If there isn't a definitive alpha channel, never mind int nsamples = samples(pixel); for (int s = 0; s < nsamples; ++s) { if (deep_value (pixel, alpha_channel, s) >= 1.0f) { // We hit an opaque sample. Cull everything farther. set_samples (pixel, s+1); break; } } } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/imageoutput.cpp0000644000175000017500000006064313151711064022757 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/plugin.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/deepdata.h" #include "imageio_pvt.h" #include OIIO_NAMESPACE_BEGIN using namespace pvt; ImageOutput::ImageOutput () : m_threads(0) { } ImageOutput::~ImageOutput () { } bool ImageOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { // Default implementation: don't know how to write scanlines return false; } bool ImageOutput::write_scanlines (int ybegin, int yend, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride) { // Default implementation: write each scanline individually stride_t native_pixel_bytes = (stride_t) m_spec.pixel_bytes (true); if (format == TypeDesc::UNKNOWN && xstride == AutoStride) xstride = native_pixel_bytes; stride_t zstride = AutoStride; m_spec.auto_stride (xstride, ystride, zstride, format, m_spec.nchannels, m_spec.width, yend-ybegin); bool ok = true; for (int y = ybegin; ok && y < yend; ++y) { ok &= write_scanline (y, z, format, data, xstride); data = (char *)data + ystride; } return ok; } bool ImageOutput::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { // Default implementation: don't know how to write tiles return false; } bool ImageOutput::write_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { if (! m_spec.valid_tile_range (xbegin, xend, ybegin, yend, zbegin, zend)) return false; // Default implementation: write each tile individually stride_t native_pixel_bytes = (stride_t) m_spec.pixel_bytes (true); if (format == TypeDesc::UNKNOWN && xstride == AutoStride) xstride = native_pixel_bytes; m_spec.auto_stride (xstride, ystride, zstride, format, m_spec.nchannels, xend-xbegin, yend-ybegin); bool ok = true; stride_t pixelsize = format.size() * m_spec.nchannels; boost::scoped_array buf; for (int z = zbegin; z < zend; z += std::max(1,m_spec.tile_depth)) { int zd = std::min (zend-z, m_spec.tile_depth); for (int y = ybegin; y < yend; y += m_spec.tile_height) { char *tilestart = ((char *)data + (z-zbegin)*zstride + (y-ybegin)*ystride); int yh = std::min (yend-y, m_spec.tile_height); for (int x = xbegin; ok && x < xend; x += m_spec.tile_width) { int xw = std::min (xend-x, m_spec.tile_width); // Full tiles are written directly into the user buffer, but // Partial tiles (such as at the image edge) are copied into // a padded buffer to stage them. if (xw == m_spec.tile_width && yh == m_spec.tile_height && zd == m_spec.tile_depth) { ok &= write_tile (x, y, z, format, tilestart, xstride, ystride, zstride); } else { if (! buf.get()) buf.reset (new char [pixelsize * m_spec.tile_pixels()]); OIIO::copy_image (m_spec.nchannels, xw, yh, zd, tilestart, pixelsize, xstride, ystride, zstride, &buf[0], pixelsize, pixelsize*m_spec.tile_width, pixelsize*m_spec.tile_pixels()); ok &= write_tile (x, y, z, format, &buf[0], pixelsize, pixelsize*m_spec.tile_width, pixelsize*m_spec.tile_pixels()); } tilestart += m_spec.tile_width * xstride; } } } return ok; } bool ImageOutput::write_rectangle (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { return false; } bool ImageOutput::write_deep_scanlines (int ybegin, int yend, int z, const DeepData &deepdata) { return false; // default: doesn't support deep images } bool ImageOutput::write_deep_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, const DeepData &deepdata) { return false; // default: doesn't support deep images } bool ImageOutput::write_deep_image (const DeepData &deepdata) { if (m_spec.depth > 1) { error ("write_deep_image is not supported for volume (3D) images."); return false; // FIXME? - not implementing 3D deep images for now. The only // format that supports deep images at this time is OpenEXR, and // it doesn't support volumes. } if (m_spec.tile_width) { // Tiled image return write_deep_tiles (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, deepdata); } else { // Scanline image return write_deep_scanlines (m_spec.y, m_spec.y+m_spec.height, 0, deepdata); } } int ImageOutput::send_to_output (const char *format, ...) { // FIXME -- I can't remember how this is supposed to work return 0; } int ImageOutput::send_to_client (const char *format, ...) { // FIXME -- I can't remember how this is supposed to work return 0; } void ImageOutput::append_error (const std::string& message) const { ASSERT (m_errmessage.size() < 1024*1024*16 && "Accumulated error messages > 16MB. Try checking return codes!"); if (m_errmessage.size()) m_errmessage += '\n'; m_errmessage += message; } const void * ImageOutput::to_native_scanline (TypeDesc format, const void *data, stride_t xstride, std::vector &scratch, unsigned int dither, int yorigin, int zorigin) { return to_native_rectangle (0, m_spec.width, 0, 1, 0, 1, format, data, xstride, 0, 0, scratch, dither, m_spec.x, yorigin, zorigin); } const void * ImageOutput::to_native_tile (TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride, std::vector &scratch, unsigned int dither, int xorigin, int yorigin, int zorigin) { return to_native_rectangle (0, m_spec.tile_width, 0, m_spec.tile_height, 0, std::max(1,m_spec.tile_depth), format, data, xstride, ystride, zstride, scratch, dither, xorigin, yorigin, zorigin); } const void * ImageOutput::to_native_rectangle (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride, std::vector &scratch, unsigned int dither, int xorigin, int yorigin, int zorigin) { // native_pixel_bytes is the size of a pixel in the FILE, including // the per-channel format, if specified when the file was opened. stride_t native_pixel_bytes = (stride_t) m_spec.pixel_bytes (true); // perchanfile is true if the file has different per-channel formats bool perchanfile = m_spec.channelformats.size() && supports("channelformats"); // It's an error to pass per-channel data formats to a writer that // doesn't support it. if (m_spec.channelformats.size() && !perchanfile) return NULL; // native_data is true if the user is passing data in the native format bool native_data = (format == TypeDesc::UNKNOWN || (format == m_spec.format && !perchanfile)); stride_t input_pixel_bytes = native_data ? native_pixel_bytes : stride_t(format.size()*m_spec.nchannels); // If user is passing native data and it's all one type, go ahead and // set format correctly. if (format == TypeDesc::UNKNOWN && !perchanfile) format = m_spec.format; // If the user is passing native data and they've left xstride set // to Auto, then we know it's the native pixel size. if (native_data && xstride == AutoStride) xstride = native_pixel_bytes; // Fill in the rest of the strides that haven't been set. m_spec.auto_stride (xstride, ystride, zstride, format, m_spec.nchannels, xend-xbegin, yend-ybegin); // Compute width and height from the rectangle extents int width = xend - xbegin; int height = yend - ybegin; int depth = zend - zbegin; // Do the strides indicate that the data area is contiguous? bool contiguous; if (native_data) { // If it's native data, it had better be contiguous by the // file's definition. contiguous = (xstride == (stride_t)(m_spec.pixel_bytes(native_data))); } else { // If it's not native data, we only care if the user's buffer // is contiguous. contiguous = (xstride == (stride_t)(format.size()*m_spec.nchannels)); } contiguous &= ((ystride == xstride*width || height == 1) && (zstride == ystride*height || depth == 1)); if (native_data && contiguous) { // Data are already in the native format and contiguous // just return a ptr to the original data. return data; } imagesize_t rectangle_pixels = width * height * depth; imagesize_t rectangle_values = rectangle_pixels * m_spec.nchannels; imagesize_t native_rectangle_bytes = rectangle_pixels * native_pixel_bytes; // Cases to handle: // 1. File has per-channel data, user passes native data -- this has // already returned above, since the data didn't need munging. // 2. File has per-channel data, user passes some other data type // 3. File has uniform data, user passes some other data type // 4. File has uniform data, user passes the right data -- note that // this case already returned if the user data was contiguous // Handle the per-channel format case (#2) where the user is passing // a non-native buffer. if (perchanfile) { if (native_data) { ASSERT (contiguous && "Per-channel native output requires contiguous strides"); } ASSERT (format != TypeDesc::UNKNOWN); ASSERT (m_spec.channelformats.size() == (size_t)m_spec.nchannels); scratch.resize (native_rectangle_bytes); size_t offset = 0; for (int c = 0; c < m_spec.nchannels; ++c) { TypeDesc chanformat = m_spec.channelformats[c]; convert_image (1 /* channels */, width, height, depth, (char *)data + c*format.size(), format, xstride, ystride, zstride, &scratch[offset], chanformat, native_pixel_bytes, AutoStride, AutoStride, c == m_spec.alpha_channel ? 0 : -1, c == m_spec.z_channel ? 0 : -1); offset += chanformat.size (); } return &scratch[0]; } // The remaining code is where all channels in the file have the // same data type, which may or may not be what the user passed in // (cases #3 and #4 above). imagesize_t contiguoussize = contiguous ? 0 : rectangle_values * input_pixel_bytes; contiguoussize = (contiguoussize+3) & (~3); // Round up to 4-byte boundary DASSERT ((contiguoussize & 3) == 0); imagesize_t floatsize = rectangle_values * sizeof(float); bool do_dither = (dither && format.is_floating_point() && m_spec.format.basetype == TypeDesc::UINT8); scratch.resize (contiguoussize + floatsize + native_rectangle_bytes); // Force contiguity if not already present if (! contiguous) { data = contiguize (data, m_spec.nchannels, xstride, ystride, zstride, (void *)&scratch[0], width, height, depth, format); } // Rather than implement the entire cross-product of possible // conversions, use float as an intermediate format, which generally // will always preserve enough precision. const float *buf; if (format == TypeDesc::FLOAT) { if (! do_dither) { // Already in float format and no dither -- leave it as-is. buf = (float *)data; } else { // Need to make a copy, even though it's already float, so the // dither doesn't overwrite the caller's data. buf = (float *)&scratch[contiguoussize]; memcpy ((float *)buf, data, floatsize); } } else { // Convert from 'format' to float. buf = convert_to_float (data, (float *)&scratch[contiguoussize], (int)rectangle_values, format); } if (do_dither) { stride_t pixelsize = m_spec.nchannels * sizeof(float); OIIO::add_dither (m_spec.nchannels, width, height, depth, (float *)buf, pixelsize, pixelsize*width, pixelsize*width*height, 1.0f/255.0f, m_spec.alpha_channel, m_spec.z_channel, dither, 0, xorigin, yorigin, zorigin); } // Convert from float to native format. return parallel_convert_from_float (buf, &scratch[contiguoussize+floatsize], rectangle_values, m_spec.format); } bool ImageOutput::write_image (TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride, ProgressCallback progress_callback, void *progress_callback_data) { bool native = (format == TypeDesc::UNKNOWN); stride_t pixel_bytes = native ? (stride_t) m_spec.pixel_bytes (native) : format.size() * m_spec.nchannels; if (xstride == AutoStride) xstride = pixel_bytes; m_spec.auto_stride (xstride, ystride, zstride, format, m_spec.nchannels, m_spec.width, m_spec.height); if (supports ("rectangles")) { // Use a rectangle if we can return write_rectangle (0, m_spec.width, 0, m_spec.height, 0, m_spec.depth, format, data, xstride, ystride, zstride); } bool ok = true; if (progress_callback && progress_callback (progress_callback_data, 0.0f)) return ok; if (m_spec.tile_width && supports ("tiles")) { // Tiled image for (int z = 0; z < m_spec.depth; z += m_spec.tile_depth) { int zend = std::min (z+m_spec.z+m_spec.tile_depth, m_spec.z+m_spec.depth); for (int y = 0; y < m_spec.height; y += m_spec.tile_height) { int yend = std::min (y+m_spec.y+m_spec.tile_height, m_spec.y+m_spec.height); const char *d = (const char *)data + z*zstride + y*ystride; ok &= write_tiles (m_spec.x, m_spec.x+m_spec.width, y+m_spec.y, yend, z+m_spec.z, zend, format, d, xstride, ystride, zstride); if (progress_callback && progress_callback (progress_callback_data, (float)(z*m_spec.height+y)/(m_spec.height*m_spec.depth))) return ok; } } } else { // Scanline image const int chunk = 256; for (int z = 0; z < m_spec.depth; ++z) for (int y = 0; y < m_spec.height && ok; y += chunk) { int yend = std::min (y+m_spec.y+chunk, m_spec.y+m_spec.height); const char *d = (const char *)data + z*zstride + y*ystride; ok &= write_scanlines (y+m_spec.y, yend, z+m_spec.z, format, d, xstride, ystride); if (progress_callback && progress_callback (progress_callback_data, (float)(z*m_spec.height+y)/(m_spec.height*m_spec.depth))) return ok; } } if (progress_callback) progress_callback (progress_callback_data, 1.0f); return ok; } bool ImageOutput::copy_image (ImageInput *in) { if (! in) { error ("copy_image: no input supplied"); return false; } // Make sure the images are compatible in size const ImageSpec &inspec (in->spec()); if (inspec.width != spec().width || inspec.height != spec().height || inspec.depth != spec().depth || inspec.nchannels != spec().nchannels) { error ("Could not copy %d x %d x %d channels to %d x %d x %d channels", inspec.width, inspec.height, inspec.nchannels, spec().width, spec().height, spec().nchannels); return false; } // in most cases plugins don't allow to copy 0x0 images // but there are some exceptions (like in FITS plugin) // when we want to do this. Because 0x0 means there is no image // data in the file, we simply return true so the application thought // that everything went right. if (! spec().image_bytes()) return true; if (spec().deep) { // Special case for ''deep'' images DeepData deepdata; bool ok = in->read_native_deep_image (deepdata); if (ok) ok = write_deep_image (deepdata); else error ("%s", in->geterror()); // copy err from in to out return ok; } // Naive implementation -- read the whole image and write it back out. // FIXME -- a smarter implementation would read scanlines or tiles at // a time, to minimize mem footprint. bool native = supports("channelformats") && inspec.channelformats.size(); TypeDesc format = native ? TypeDesc::UNKNOWN : inspec.format; boost::scoped_array pixels (new char [inspec.image_bytes(native)]); bool ok = in->read_image (format, &pixels[0]); if (ok) ok = write_image (format, &pixels[0]); else error ("%s", in->geterror()); // copy err from in to out return ok; } bool ImageOutput::copy_to_image_buffer (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride, void *image_buffer, TypeDesc buf_format) { const ImageSpec &spec (this->spec()); if (buf_format == TypeDesc::UNKNOWN) buf_format = spec.format; spec.auto_stride (xstride, ystride, zstride, format, spec.nchannels, spec.width, spec.height); stride_t buf_xstride = spec.nchannels * buf_format.size(); stride_t buf_ystride = buf_xstride * spec.width; stride_t buf_zstride = buf_ystride * spec.height; stride_t offset = (xbegin-spec.x)*buf_xstride + (ybegin-spec.y)*buf_ystride + (zbegin-spec.z)*buf_zstride; int width = xend-xbegin, height = yend-ybegin, depth = zend-zbegin; imagesize_t npixels = imagesize_t(width) * imagesize_t(height) * imagesize_t(depth); // Add dither if requested -- requires making a temporary staging area boost::scoped_array ditherarea; unsigned int dither = spec.get_int_attribute ("oiio:dither", 0); if (dither && format.is_floating_point() && buf_format.basetype == TypeDesc::UINT8) { stride_t pixelsize = spec.nchannels * sizeof(float); ditherarea.reset (new float [pixelsize * npixels]); OIIO::convert_image (spec.nchannels, width, height, depth, data, format, xstride, ystride, zstride, ditherarea.get(), TypeDesc::FLOAT, pixelsize, pixelsize*width, pixelsize*width*height); data = ditherarea.get(); format = TypeDesc::FLOAT; xstride = pixelsize; ystride = xstride * width; zstride = ystride * height; float ditheramp = spec.get_float_attribute ("oiio:ditheramplitude", 1.0f/255.0f); OIIO::add_dither (spec.nchannels, width, height, depth, (float *)data, pixelsize, pixelsize*width, pixelsize*width*height, ditheramp, spec.alpha_channel, spec.z_channel, dither, 0, xbegin, ybegin, zbegin); } return OIIO::convert_image (spec.nchannels, width, height, depth, data, format, xstride, ystride, zstride, (char *)image_buffer + offset, buf_format, buf_xstride, buf_ystride, buf_zstride); } bool ImageOutput::copy_tile_to_image_buffer (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride, void *image_buffer, TypeDesc buf_format) { if (! m_spec.tile_width || ! m_spec.tile_height) { error ("Called write_tile for non-tiled image."); return false; } const ImageSpec &spec (this->spec()); spec.auto_stride (xstride, ystride, zstride, format, spec.nchannels, spec.tile_width, spec.tile_height); int xend = std::min (x+spec.tile_width, spec.x+spec.width); int yend = std::min (y+spec.tile_height, spec.y+spec.height); int zend = std::min (z+spec.tile_depth, spec.z+spec.depth); return copy_to_image_buffer (x, xend, y, yend, z, zend, format, data, xstride, ystride, zstride, image_buffer, buf_format); } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/imagebufalgo_compare.cpp0000644000175000017500000010213713151711064024537 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// \file /// Implementation of ImageBufAlgo algorithms that analize or compare /// images. #include #include #include #include #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/imagebufalgo_util.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/SHA1.h" #ifdef USE_OPENSSL #ifdef __APPLE__ // Newer OSX releaes mark OpenSSL functions as deprecated, in favor of // CDSA. Make the warnings stop. #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif #include #endif OIIO_NAMESPACE_BEGIN inline void reset (ImageBufAlgo::PixelStats &p, int nchannels) { const float inf = std::numeric_limits::infinity(); p.min.clear (); p.min.resize (nchannels, inf); p.max.clear (); p.max.resize (nchannels, -inf); p.avg.clear (); p.avg.resize (nchannels); p.stddev.clear (); p.stddev.resize (nchannels); p.nancount.clear (); p.nancount.resize (nchannels, 0); p.infcount.clear (); p.infcount.resize (nchannels, 0); p.finitecount.clear (); p.finitecount.resize (nchannels, 0); p.sum.clear (); p.sum.resize (nchannels, 0.0); p.sum2.clear (); p.sum2.resize (nchannels, 0.0); } inline void merge (ImageBufAlgo::PixelStats &sum, const ImageBufAlgo::PixelStats &p) { ASSERT (sum.min.size() == p.min.size()); for (size_t c = 0, e = sum.min.size(); c < e; ++c) { sum.min[c] = std::min (sum.min[c], p.min[c]); sum.max[c] = std::max (sum.max[c], p.max[c]); sum.nancount[c] += p.nancount[c]; sum.infcount[c] += p.infcount[c]; sum.finitecount[c] += p.finitecount[c]; sum.sum[c] += p.sum[c]; sum.sum2[c] += p.sum2[c]; } } inline void val (ImageBufAlgo::PixelStats &p, int c, float value) { if (isnan (value)) { ++p.nancount[c]; return; } if (isinf (value)) { ++p.infcount[c]; return; } ++p.finitecount[c]; p.sum[c] += value; p.sum2[c] += value*value; p.min[c] = std::min (value, p.min[c]); p.max[c] = std::max (value, p.max[c]); } inline void finalize (ImageBufAlgo::PixelStats &p) { for (size_t c = 0, e = p.min.size(); c < e; ++c) { if (p.finitecount[c] == 0) { p.min[c] = 0.0; p.max[c] = 0.0; p.avg[c] = 0.0; p.stddev[c] = 0.0; } else { double Count = static_cast(p.finitecount[c]); double davg = p.sum[c] / Count; p.avg[c] = static_cast(davg); p.stddev[c] = static_cast(safe_sqrt(p.sum2[c]/Count - davg*davg)); } } } template static bool computePixelStats_ (const ImageBuf &src, ImageBufAlgo::PixelStats &stats, ROI roi, int nthreads) { if (! roi.defined()) roi = get_roi (src.spec()); else roi.chend = std::min (roi.chend, src.nchannels()); int nchannels = src.spec().nchannels; // Use local storage for smaller batches, then merge the batches // into the final results. This preserves precision for large // images, where the running total may be too big to incorporate the // contributions of individual pixel values without losing // precision. // // This approach works best when the batch size is the sqrt of // numpixels, which makes the num batches roughly equal to the // number of pixels / batch. ImageBufAlgo::PixelStats tmp; reset (tmp, nchannels); reset (stats, nchannels); int PIXELS_PER_BATCH = std::max (1024, static_cast(sqrt((double)src.spec().image_pixels()))); if (src.deep()) { // Loop over all pixels ... for (ImageBuf::ConstIterator s(src, roi); ! s.done(); ++s) { int samples = s.deep_samples(); if (! samples) continue; for (int c = roi.chbegin; c < roi.chend; ++c) { for (int i = 0; i < samples; ++i) { float value = s.deep_value (c, i); val (tmp, c, value); if ((tmp.finitecount[c] % PIXELS_PER_BATCH) == 0) { merge (stats, tmp); reset (tmp, nchannels); } } } } } else { // Non-deep case // Loop over all pixels ... for (ImageBuf::ConstIterator s(src, roi); ! s.done(); ++s) { for (int c = roi.chbegin; c < roi.chend; ++c) { float value = s[c]; val (tmp, c, value); if ((tmp.finitecount[c] % PIXELS_PER_BATCH) == 0) { merge (stats, tmp); reset (tmp, nchannels); } } } } // Merge anything left over merge (stats, tmp); // Compute final results finalize (stats); return ! src.has_error(); }; bool ImageBufAlgo::computePixelStats (PixelStats &stats, const ImageBuf &src, ROI roi, int nthreads) { if (! roi.defined()) roi = get_roi (src.spec()); else roi.chend = std::min (roi.chend, src.nchannels()); int nchannels = src.spec().nchannels; if (nchannels == 0) { src.error ("%d-channel images not supported", nchannels); return false; } bool ok; OIIO_DISPATCH_TYPES (ok, "computePixelStats", computePixelStats_, src.spec().format, src, stats, roi, nthreads); return ok; } template inline void compare_value (ImageBuf::ConstIterator &a, int chan, float aval, float bval, ImageBufAlgo::CompareResults &result, float &maxval, double &batcherror, double &batch_sqrerror, bool &failed, bool &warned, float failthresh, float warnthresh) { if (!isfinite(aval) || !isfinite(bval)) { if (isnan(aval) == isnan(bval) && isinf(aval) == isinf(bval)) return; // NaN may match NaN, Inf may match Inf if (isfinite(result.maxerror)) { // non-finite errors trump finite ones result.maxerror = std::numeric_limits::infinity(); result.maxx = a.x(); result.maxy = a.y(); result.maxz = a.z(); result.maxc = chan; return; } } maxval = std::max (maxval, std::max (aval, bval)); double f = fabs (aval - bval); batcherror += f; batch_sqrerror += f*f; // We use the awkward '!(a<=threshold)' construct so that we have // failures when f is a NaN (since all comparisons involving NaN will // return false). if (!(f <= result.maxerror)) { result.maxerror = f; result.maxx = a.x(); result.maxy = a.y(); result.maxz = a.z(); result.maxc = chan; } if (! warned && !(f <= warnthresh)) { ++result.nwarn; warned = true; } if (! failed && !(f <= failthresh)) { ++result.nfail; failed = true; } } template static bool compare_ (const ImageBuf &A, const ImageBuf &B, float failthresh, float warnthresh, ImageBufAlgo::CompareResults &result, ROI roi, int nthreads) { imagesize_t npels = roi.npixels(); imagesize_t nvals = npels * roi.nchannels(); int Achannels = A.nchannels(), Bchannels = B.nchannels(); // Compare the two images. // double totalerror = 0; double totalsqrerror = 0; result.maxerror = 0; result.maxx=0, result.maxy=0, result.maxz=0, result.maxc=0; result.nfail = 0, result.nwarn = 0; float maxval = 1.0; // max possible value ImageBuf::ConstIterator a (A, roi, ImageBuf::WrapBlack); ImageBuf::ConstIterator b (B, roi, ImageBuf::WrapBlack); bool deep = A.deep(); // Break up into batches to reduce cancelation errors as the error // sums become too much larger than the error for individual pixels. const int batchsize = 4096; // As good a guess as any for ( ; ! a.done(); ) { double batcherror = 0; double batch_sqrerror = 0; if (deep) { for (int i = 0; i < batchsize && !a.done(); ++i, ++a, ++b) { bool warned = false, failed = false; // For this pixel for (int c = roi.chbegin; c < roi.chend; ++c) for (int s = 0, e = a.deep_samples(); s < e; ++s) { compare_value (a, c, a.deep_value(c,s), b.deep_value(c,s), result, maxval, batcherror, batch_sqrerror, failed, warned, failthresh, warnthresh); } } } else { // non-deep for (int i = 0; i < batchsize && !a.done(); ++i, ++a, ++b) { bool warned = false, failed = false; // For this pixel for (int c = roi.chbegin; c < roi.chend; ++c) compare_value (a, c, c < Achannels ? a[c] : 0.0f, c < Bchannels ? b[c] : 0.0f, result, maxval, batcherror, batch_sqrerror, failed, warned, failthresh, warnthresh); } } totalerror += batcherror; totalsqrerror += batch_sqrerror; } result.meanerror = totalerror / nvals; result.rms_error = sqrt (totalsqrerror / nvals); result.PSNR = 20.0 * log10 (maxval / result.rms_error); return result.nfail == 0; } bool ImageBufAlgo::compare (const ImageBuf &A, const ImageBuf &B, float failthresh, float warnthresh, ImageBufAlgo::CompareResults &result, ROI roi, int nthreads) { // If no ROI is defined, use the union of the data windows of the two // images. if (! roi.defined()) roi = roi_union (get_roi(A.spec()), get_roi(B.spec())); roi.chend = std::min (roi.chend, std::max(A.nchannels(), B.nchannels())); // Deep and non-deep images cannot be compared if (B.deep() != A.deep()) return false; bool ok; OIIO_DISPATCH_TYPES2 (ok, "compare", compare_, A.spec().format, B.spec().format, A, B, failthresh, warnthresh, result, roi, nthreads); // FIXME - The nthreads argument is for symmetry with the rest of // ImageBufAlgo and for future expansion. But for right now, we // don't actually split by threads. Maybe later. return ok; } template static inline bool isConstantColor_ (const ImageBuf &src, float *color, ROI roi, int nthreads) { // Iterate using the native typing (for speed). std::vector constval (roi.nchannels()); ImageBuf::ConstIterator s (src, roi); for (int c = roi.chbegin; c < roi.chend; ++c) constval[c] = s[c]; // Loop over all pixels ... for ( ; ! s.done(); ++s) { for (int c = roi.chbegin; c < roi.chend; ++c) if (constval[c] != s[c]) return false; } if (color) { ImageBuf::ConstIterator s (src, roi); for (int c = 0; c < roi.chbegin; ++c) color[c] = 0.0f; for (int c = roi.chbegin; c < roi.chend; ++c) color[c] = s[c]; for (int c = roi.chend; c < src.nchannels(); ++c) color[c] = 0.0f; } return true; } bool ImageBufAlgo::isConstantColor (const ImageBuf &src, float *color, ROI roi, int nthreads) { // If no ROI is defined, use the data window of src. if (! roi.defined()) roi = get_roi(src.spec()); roi.chend = std::min (roi.chend, src.nchannels()); if (roi.nchannels() == 0) return true; bool ok; OIIO_DISPATCH_TYPES (ok, "isConstantColor", isConstantColor_, src.spec().format, src, color, roi, nthreads); return ok; // FIXME - The nthreads argument is for symmetry with the rest of // ImageBufAlgo and for future expansion. But for right now, we // don't actually split by threads. Maybe later. }; template static inline bool isConstantChannel_ (const ImageBuf &src, int channel, float val, ROI roi, int nthreads) { T v = convert_type (val); for (ImageBuf::ConstIterator s(src, roi); !s.done(); ++s) if (s[channel] != v) return false; return true; } bool ImageBufAlgo::isConstantChannel (const ImageBuf &src, int channel, float val, ROI roi, int nthreads) { // If no ROI is defined, use the data window of src. if (! roi.defined()) roi = get_roi(src.spec()); if (channel < 0 || channel >= src.nchannels()) return false; // that channel doesn't exist in the image bool ok; OIIO_DISPATCH_TYPES (ok, "isConstantChannel", isConstantChannel_, src.spec().format, src, channel, val, roi, nthreads); return ok; // FIXME - The nthreads argument is for symmetry with the rest of // ImageBufAlgo and for future expansion. But for right now, we // don't actually split by threads. Maybe later. }; template static inline bool isMonochrome_ (const ImageBuf &src, ROI roi, int nthreads) { int nchannels = src.nchannels(); if (nchannels < 2) return true; // Loop over all pixels ... for (ImageBuf::ConstIterator s(src, roi); ! s.done(); ++s) { T constvalue = s[roi.chbegin]; for (int c = roi.chbegin+1; c < roi.chend; ++c) if (s[c] != constvalue) return false; } return true; } bool ImageBufAlgo::isMonochrome (const ImageBuf &src, ROI roi, int nthreads) { // If no ROI is defined, use the data window of src. if (! roi.defined()) roi = get_roi(src.spec()); roi.chend = std::min (roi.chend, src.nchannels()); if (roi.nchannels() < 2) return true; // 1 or fewer channels are always "monochrome" bool ok; OIIO_DISPATCH_TYPES (ok, "isMonochrome", isMonochrome_, src.spec().format, src, roi, nthreads); return ok; // FIXME - The nthreads argument is for symmetry with the rest of // ImageBufAlgo and for future expansion. But for right now, we // don't actually split by threads. Maybe later. }; template static bool color_count_ (const ImageBuf &src, atomic_ll *count, int ncolors, const float *color, const float *eps, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(color_count_, OIIO::ref(src), count, ncolors, color, eps, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case int nchannels = src.nchannels(); long long *n = ALLOCA (long long, ncolors); for (int col = 0; col < ncolors; ++col) n[col] = 0; for (ImageBuf::ConstIterator p (src, roi); !p.done(); ++p) { int coloffset = 0; for (int col = 0; col < ncolors; ++col, coloffset += nchannels) { int match = 1; for (int c = roi.chbegin; c < roi.chend; ++c) { if (fabsf(p[c] - color[coloffset+c]) > eps[c]) { match = 0; break; } } n[col] += match; } } for (int col = 0; col < ncolors; ++col) count[col] += n[col]; return true; } bool ImageBufAlgo::color_count (const ImageBuf &src, imagesize_t *count, int ncolors, const float *color, const float *eps, ROI roi, int nthreads) { // If no ROI is defined, use the data window of src. if (! roi.defined()) roi = get_roi(src.spec()); roi.chend = std::min (roi.chend, src.nchannels()); if (! eps) { float *localeps = ALLOCA (float, roi.chend); for (int c = 0; c < roi.chend; ++c) localeps[c] = 0.001f; eps = localeps; } for (int col = 0; col < ncolors; ++col) count[col] = 0; bool ok; OIIO_DISPATCH_TYPES (ok, "color_count", color_count_, src.spec().format, src, (atomic_ll *)count, ncolors, color, eps, roi, nthreads); return ok; } template static bool color_range_check_ (const ImageBuf &src, atomic_ll *lowcount, atomic_ll *highcount, atomic_ll *inrangecount, const float *low, const float *high, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(color_range_check_, OIIO::ref(src), lowcount, highcount, inrangecount, low, high, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case long long lc = 0, hc = 0, inrange = 0; for (ImageBuf::ConstIterator p (src, roi); !p.done(); ++p) { bool lowval = false, highval = false; for (int c = roi.chbegin; c < roi.chend; ++c) { float f = p[c]; lowval |= (f < low[c]); highval |= (f > high[c]); } if (lowval) ++lc; if (highval) ++hc; if (!lowval && !highval) ++inrange; } if (lowcount) *lowcount += lc; if (highcount) *highcount += hc; if (inrangecount) *inrangecount += inrange; return true; } bool ImageBufAlgo::color_range_check (const ImageBuf &src, imagesize_t *lowcount, imagesize_t *highcount, imagesize_t *inrangecount, const float *low, const float *high, ROI roi, int nthreads) { // If no ROI is defined, use the data window of src. if (! roi.defined()) roi = get_roi(src.spec()); roi.chend = std::min (roi.chend, src.nchannels()); if (lowcount) *lowcount = 0; if (highcount) *highcount = 0; if (inrangecount) *inrangecount = 0; bool ok; OIIO_DISPATCH_TYPES (ok, "color_range_check", color_range_check_, src.spec().format, src, (atomic_ll *)lowcount, (atomic_ll *)highcount, (atomic_ll *)inrangecount, low, high, roi, nthreads); return ok; } // Helper: is the roi devoid of any deep samples? static ROI deep_nonempty_region (const ImageBuf &src, ROI roi) { DASSERT (src.deep()); ROI r; // Initially undefined for (int z = roi.zbegin; z < roi.zend; ++z) for (int y = roi.ybegin; y < roi.yend; ++y) for (int x = roi.xbegin; x < roi.xend; ++x) if (src.deep_samples (x, y, z) != 0) { if (! r.defined()) { r = ROI (x, x+1, y, y+1, z, z+1, 0, src.nchannels()); } else { r.xbegin = std::min (x, r.xbegin); r.xend = std::max (x+1, r.xend); r.ybegin = std::min (y, r.ybegin); r.yend = std::max (y+1, r.yend); r.zbegin = std::min (z, r.zbegin); r.zend = std::max (z+1, r.zend); } } return r; } ROI ImageBufAlgo::nonzero_region (const ImageBuf &src, ROI roi, int nthreads) { roi = roi_intersection (roi, src.roi()); if (src.deep()) { return deep_nonempty_region (src, roi); } std::vector zero (src.nchannels(), 0.0f); std::vector color (src.nchannels(), 0.0f); // Trim bottom for ( ; roi.ybegin < roi.yend; --roi.yend) { ROI test = roi; test.ybegin = roi.yend-1; if (! isConstantColor (src, &color[0], test, nthreads) || color != zero) break; } // Trim top for ( ; roi.ybegin < roi.yend; ++roi.ybegin) { ROI test = roi; test.yend = roi.ybegin+1; if (! isConstantColor (src, &color[0], test, nthreads) || color != zero) break; } // Trim right for ( ; roi.xbegin < roi.xend; --roi.xend) { ROI test = roi; test.xbegin = roi.xend-1; if (! isConstantColor (src, &color[0], test, nthreads) || color != zero) break; } // Trim left for ( ; roi.xbegin < roi.xend; ++roi.xbegin) { ROI test = roi; test.xend = roi.xbegin+1; if (! isConstantColor (src, &color[0], test, nthreads) || color != zero) break; } if (roi.depth() > 1) { // Trim zbottom for ( ; roi.zbegin < roi.zend; --roi.zend) { ROI test = roi; test.zbegin = roi.zend-1; if (! isConstantColor (src, &color[0], test, nthreads) || color != zero) break; } // Trim ztop for ( ; roi.zbegin < roi.zend; ++roi.zbegin) { ROI test = roi; test.zend = roi.zbegin+1; if (! isConstantColor (src, &color[0], test, nthreads) || color != zero) break; } } return roi; } namespace { std::string simplePixelHashSHA1 (const ImageBuf &src, string_view extrainfo, ROI roi) { if (! roi.defined()) roi = get_roi (src.spec()); bool localpixels = src.localpixels(); imagesize_t scanline_bytes = roi.width() * src.spec().pixel_bytes(); ASSERT (scanline_bytes < std::numeric_limits::max()); // Do it a few scanlines at a time int chunk = std::max (1, int(16*1024*1024/scanline_bytes)); std::vector tmp; if (! localpixels) tmp.resize (chunk*scanline_bytes); #ifdef USE_OPENSSL // If OpenSSL was available at build time, use its SHA-1 // implementation, which is about 20% faster than CSHA1. SHA_CTX sha; SHA1_Init (&sha); for (int z = roi.zbegin, zend=roi.zend; z < zend; ++z) { for (int y = roi.ybegin, yend=roi.yend; y < yend; y += chunk) { int y1 = std::min (y+chunk, yend); if (localpixels) { SHA1_Update (&sha, src.pixeladdr (roi.xbegin, y, z), (unsigned int) scanline_bytes*(y1-y)); } else { src.get_pixels (ROI (roi.xbegin, roi.xend, y, y1, z, z+1), src.spec().format, &tmp[0]); SHA1_Update (&sha, &tmp[0], (unsigned int) scanline_bytes*(y1-y)); } } } // If extra info is specified, also include it in the sha computation if (!extrainfo.empty()) SHA1_Update (&sha, extrainfo.data(), extrainfo.size()); unsigned char md[SHA_DIGEST_LENGTH]; char hash_digest[2*SHA_DIGEST_LENGTH+1]; SHA1_Final (md, &sha); for (int i = 0; i < SHA_DIGEST_LENGTH; ++i) sprintf (hash_digest+2*i, "%02X", (int)md[i]); hash_digest[2*SHA_DIGEST_LENGTH] = 0; return std::string (hash_digest); #else // Fall back on CSHA1 if OpenSSL was not available or if CSHA1 sha; sha.Reset (); for (int z = roi.zbegin, zend=roi.zend; z < zend; ++z) { for (int y = roi.ybegin, yend=roi.yend; y < yend; y += chunk) { int y1 = std::min (y+chunk, yend); if (localpixels) { sha.Update ((const unsigned char *)src.pixeladdr (roi.xbegin, y, z), (unsigned int) scanline_bytes*(y1-y)); } else { src.get_pixels (ROI (roi.xbegin, roi.xend, y, y1, z, z+1), src.spec().format, &tmp[0]); sha.Update (&tmp[0], (unsigned int) scanline_bytes*(y1-y)); } } } // If extra info is specified, also include it in the sha computation if (!extrainfo.empty()) { sha.Update ((const unsigned char*) extrainfo.data(), extrainfo.size()); } sha.Final (); std::string hash_digest; sha.ReportHashStl (hash_digest, CSHA1::REPORT_HEX_SHORT); return hash_digest; #endif } // Wrapper to single-threadedly SHA1 hash a region in blocks and store // the results in a designated place. static void sha1_hasher (const ImageBuf *src, ROI roi, int blocksize, std::string *results, int firstresult) { ROI broi = roi; for (int y = roi.ybegin; y < roi.yend; y += blocksize) { broi.ybegin = y; broi.yend = std::min (y+blocksize, roi.yend); std::string s = simplePixelHashSHA1 (*src, "", broi); results[firstresult++] = s; } } } // anon namespace std::string ImageBufAlgo::computePixelHashSHA1 (const ImageBuf &src, string_view extrainfo, ROI roi, int blocksize, int nthreads) { if (! roi.defined()) roi = get_roi (src.spec()); // Fall back to whole-image hash for only one block if (blocksize <= 0 || blocksize >= roi.height()) return simplePixelHashSHA1 (src, extrainfo, roi); // Request for 0 threads means "use the OIIO global thread count" if (nthreads <= 0) OIIO::getattribute ("threads", nthreads); int nblocks = (roi.height()+blocksize-1) / blocksize; std::vector results (nblocks); if (nthreads <= 1) { sha1_hasher (&src, roi, blocksize, &results[0], 0); } else { // parallel case OIIO::thread_group threads; int blocks_per_thread = (nblocks+nthreads-1) / nthreads; ROI broi = roi; for (int b = 0, t = 0; b < nblocks; b += blocks_per_thread, ++t) { int y = roi.ybegin + b*blocksize; if (y >= roi.yend) break; broi.ybegin = y; broi.yend = std::min (y+blocksize*blocks_per_thread, roi.yend); threads.add_thread (new OIIO::thread (sha1_hasher, &src, broi, blocksize, &results[0], b)); } threads.join_all (); } #ifdef USE_OPENSSL // If OpenSSL was available at build time, use its SHA-1 // implementation, which is about 20% faster than CSHA1. SHA_CTX sha; SHA1_Init (&sha); for (int b = 0; b < nblocks; ++b) SHA1_Update (&sha, results[b].c_str(), results[b].size()); if (extrainfo.size()) SHA1_Update (&sha, extrainfo.c_str(), extrainfo.size()); unsigned char md[SHA_DIGEST_LENGTH]; char hash_digest[2*SHA_DIGEST_LENGTH+1]; SHA1_Final (md, &sha); for (int i = 0; i < SHA_DIGEST_LENGTH; ++i) sprintf (hash_digest+2*i, "%02X", (int)md[i]); hash_digest[2*SHA_DIGEST_LENGTH] = 0; return std::string (hash_digest); #else // Fall back on CSHA1 if OpenSSL was not available or if CSHA1 sha; sha.Reset (); for (int b = 0; b < nblocks; ++b) sha.Update ((const unsigned char *)results[b].c_str(), results[b].size()); if (extrainfo.size()) sha.Update ((const unsigned char *)extrainfo.c_str(), extrainfo.size()); sha.Final (); std::string hash_digest; sha.ReportHashStl (hash_digest, CSHA1::REPORT_HEX_SHORT); return hash_digest; #endif } /// histogram_impl ----------------------------------------------------------- /// Fully type-specialized version of histogram. /// /// Pixel values in min->max range are mapped to 0->(bins-1) range, so that /// each value is placed in the appropriate bin. The formula used is: /// y = (x-min) * bins/(max-min), where y is the value in the 0->(bins-1) /// range and x is the value in the min->max range. There is one special /// case x==max for which the formula is not used and x is assigned to the /// last bin at position (bins-1) in the vector histogram. /// -------------------------------------------------------------------------- template static bool histogram_impl (const ImageBuf &A, int channel, std::vector &histogram, int bins, float min, float max, imagesize_t *submin, imagesize_t *supermax, ROI roi) { // Double check A's type. if (A.spec().format != BaseTypeFromC::value) { A.error ("Unsupported pixel data format '%s'", A.spec().format); return false; } // Initialize. ImageBuf::ConstIterator a (A, roi); float ratio = bins / (max-min); int bins_minus_1 = bins-1; bool submin_ok = submin != NULL; bool supermax_ok = supermax != NULL; if (submin_ok) *submin = 0; if (supermax_ok) *supermax = 0; histogram.assign(bins, 0); // Compute histogram. for ( ; ! a.done(); a++) { float c = a[channel]; if (c >= min && c < max) { // Map range min->max to 0->(bins-1). histogram[ (int) ((c-min) * ratio) ]++; } else if (c == max) { histogram[bins_minus_1]++; } else { if (submin_ok && c < min) (*submin)++; else if (supermax_ok) (*supermax)++; } } return true; } bool ImageBufAlgo::histogram (const ImageBuf &A, int channel, std::vector &histogram, int bins, float min, float max, imagesize_t *submin, imagesize_t *supermax, ROI roi) { if (A.spec().format != TypeDesc::TypeFloat) { A.error ("Unsupported pixel data format '%s'", A.spec().format); return false; } if (A.nchannels() == 0) { A.error ("Input image must have at least 1 channel"); return false; } if (channel < 0 || channel >= A.nchannels()) { A.error ("Invalid channel %d for input image with channels 0 to %d", channel, A.nchannels()-1); return false; } if (bins < 1) { A.error ("The number of bins must be at least 1"); return false; } if (max <= min) { A.error ("Invalid range, min must be strictly smaller than max"); return false; } // Specified ROI -> use it. Unspecified ROI -> initialize from A. if (! roi.defined()) roi = get_roi (A.spec()); histogram_impl (A, channel, histogram, bins, min, max, submin, supermax, roi); return ! A.has_error(); } bool ImageBufAlgo::histogram_draw (ImageBuf &R, const std::vector &histogram) { // Fail if there are no bins to draw. int bins = histogram.size(); if (bins == 0) { R.error ("There are no bins to draw, the histogram is empty"); return false; } // Check R and modify it if needed. int height = R.spec().height; if (R.spec().format != TypeDesc::TypeFloat || R.nchannels() != 1 || R.spec().width != bins) { ImageSpec newspec = ImageSpec (bins, height, 1, TypeDesc::FLOAT); R.reset ("dummy", newspec); } // Fill output image R with white color. ImageBuf::Iterator r (R); for ( ; ! r.done(); ++r) r[0] = 1; // Draw histogram left->right, bottom->up. imagesize_t max = *std::max_element (histogram.begin(), histogram.end()); for (int b = 0; b < bins; b++) { int bin_height = (int) ((float)histogram[b]/(float)max*height + 0.5f); if (bin_height != 0) { // Draw one bin at column b. for (int j = 1; j <= bin_height; j++) { int row = height - j; r.pos (b, row); r[0] = 0; } } } return true; } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/xmp.cpp0000644000175000017500000006442113151711064021216 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include "OpenImageIO/thread.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/imageio.h" extern "C" { #include "tiff.h" } #if USE_EXTERNAL_PUGIXML # include "pugixml.hpp" #else # include "OpenImageIO/pugixml.hpp" #endif #define DEBUG_XMP_READ 0 #define DEBUG_XMP_WRITE 0 #define MY_ENCODING "ISO-8859-1" OIIO_NAMESPACE_BEGIN namespace { // anonymous // Define special processing flags -- they're individual bits so can be // combined with '|' enum XMPspecial { NothingSpecial = 0, Rational = 1, // It needs to be expressed as A/B DateConversion = 2, // It's a date, may need conversion to canonical form TiffRedundant = 4, // It's something that's part of normal TIFF tags ExifRedundant = 8, // It's something included in Exif Suppress = 16, // Explicitly suppress it from XMP IsList = 32, // Make a semicolon-separated list out of it IsSeq = 64, // Like List, but order matters IsBool = 128 // Should be output as True/False }; struct XMPtag { const char *xmpname; // XMP name const char *oiioname; // Attribute name we use TypeDesc oiiotype; // Type we use int special; // Special handling }; static XMPtag xmptag [] = { { "photoshop:AuthorsPosition", "IPTC:AuthorsPosition", TypeDesc::STRING, 0 }, { "photoshop:CaptionWriter", "IPTC:CaptionWriter", TypeDesc::STRING, 0 }, { "photoshop:Category", "IPTC:Category", TypeDesc::STRING, 0 }, { "photoshop:City", "IPTC:City", TypeDesc::STRING, 0 }, { "photoshop:Country", "IPTC:Country", TypeDesc::STRING, 0 }, { "photoshop:Credit", "IPTC:Provider", TypeDesc::STRING, 0 }, { "photoshop:DateCreated", "DateTime", TypeDesc::STRING, DateConversion|TiffRedundant }, { "photoshop:Headline", "IPTC:Headline", TypeDesc::STRING, 0 }, { "photoshop:Instructions", "IPTC:Instructions", TypeDesc::STRING, 0 }, { "photoshop:Source", "IPTC:Source", TypeDesc::STRING, 0 }, { "photoshop:State", "IPTC:State", TypeDesc::STRING, 0 }, { "photoshop:SupplementalCategories", "IPTC:SupplementalCategories", TypeDesc::STRING, IsList|Suppress }, // FIXME -- un-suppress when we have it working { "photoshop:TransmissionReference", "IPTC:TransmissionReference", TypeDesc::STRING, 0 }, { "photoshop:Urgency", "photoshop:Urgency", TypeDesc::INT, 0 }, { "tiff:Compression", "tiff:Compression", TypeDesc::INT, TiffRedundant }, { "tiff:PlanarConfiguration", "tiff:PlanarConfiguration", TypeDesc::INT, TiffRedundant }, { "tiff:PhotometricInterpretation", "tiff:PhotometricInterpretation", TypeDesc::INT, TiffRedundant }, { "tiff:subfiletype", "tiff:subfiletype", TypeDesc::INT, TiffRedundant }, { "tiff:Orientation", "Orientation", TypeDesc::INT, TiffRedundant }, { "tiff:XResolution", "XResolution", TypeDesc::FLOAT, Rational|TiffRedundant }, { "tiff:YResolution", "YResolution", TypeDesc::FLOAT, Rational|TiffRedundant }, { "tiff:ResolutionUnit", "ResolutionUnit", TypeDesc::INT, TiffRedundant }, { "exif:ColorSpace", "Exif:ColorSpace", TypeDesc::INT, ExifRedundant }, { "exifEX:PhotographicSensitivity", "Exif:ISOSpeedRatings", TypeDesc::INT, ExifRedundant }, { "xmp:CreateDate", "DateTime", TypeDesc::STRING, DateConversion|TiffRedundant }, { "xmp:CreatorTool", "Software", TypeDesc::STRING, TiffRedundant }, { "xmp:Label", "IPTC:Label", TypeDesc::STRING, 0 }, { "xmp:MetadataDate", "IPTC:MetadataDate", TypeDesc::STRING, DateConversion }, { "xmp:ModifyDate", "IPTC:ModifyDate", TypeDesc::STRING, DateConversion }, { "xmp:Rating", "IPTC:Rating", TypeDesc::INT, 0 }, { "xmpMM:DocumentID", "IPTC:DocumentID", TypeDesc::STRING, 0 }, { "xmpMM:History", "ImageHistory", TypeDesc::STRING, IsSeq|Suppress }, { "xmpMM:InstanceID", "IPTC:InstanceID", TypeDesc::STRING, 0 }, { "xmpMM:OriginalDocumentID", "IPTC:OriginalDocumentID", TypeDesc::STRING, 0 }, { "xmpRights:Marked", "IPTC:CopyrightStatus", TypeDesc::INT, IsBool }, { "xmpRights:WebStatement", "IPTC:CopyrightInfoURL", TypeDesc::STRING, 0 }, { "xmpRights:UsageTerms", "IPTC:RightsUsageTerms", TypeDesc::STRING, 0 }, { "dc:format", "", TypeDesc::STRING, TiffRedundant|Suppress }, { "dc:Description", "ImageDescription", TypeDesc::STRING, TiffRedundant }, { "dc:Creator", "Artist", TypeDesc::STRING, TiffRedundant }, { "dc:Rights", "Copyright", TypeDesc::STRING, TiffRedundant }, { "dc:title", "IPTC:ObjectName", TypeDesc::STRING, 0 }, { "dc:subject", "Keywords", TypeDesc::STRING, IsList }, { "dc:keywords", "Keywords", TypeDesc::STRING, IsList }, { "Iptc4xmpCore:IntellectualGenre", "IPTC:IntellectualGenre", TypeDesc::STRING, 0 }, { "Iptc4xmpCore:CountryCode", "IPTC:CountryCode", TypeDesc::STRING, 0 }, { "Iptc4xmpCore:CreatorContactInfo", "IPTC:CreatorContactInfo", TypeDesc::STRING, 0 }, { "Iptc4xmpCore:ContactInfoDetails", "IPTC:Contact", TypeDesc::STRING, 0 }, { "Iptc4xmpCore:CiAdrExtadr", "IPTC:ContactInfoAddress", TypeDesc::STRING, 0 }, { "Iptc4xmpCore:CiAdrCity", "IPTC:ContactInfoCity", TypeDesc::STRING, 0 }, { "Iptc4xmpCore:CiAdrRegion", "IPTC:ContactInfoState", TypeDesc::STRING, 0 }, { "Iptc4xmpCore:CiAdrPcode", "IPTC:ContactInfoPostalCode", TypeDesc::STRING, 0 }, { "Iptc4xmpCore:CiAdrCtry", "IPTC:ContactInfoCountry", TypeDesc::STRING, 0 }, { "Iptc4xmpCore:CiEmailWork", "IPTC:ContactInfoEmail", TypeDesc::STRING, 0 }, { "Iptc4xmpCore:CiTelWork", "IPTC:ContactInfoPhone", TypeDesc::STRING, 0 }, { "Iptc4xmpCore:CiUrlWork", "IPTC:ContactInfoURL", TypeDesc::STRING, 0 }, { "Iptc4xmpCore:Location", "IPTC:Sublocation", TypeDesc::STRING, 0 }, { "Iptc4xmpCore:SubjectCode", "IPTC:SubjectCode", TypeDesc::STRING, IsList }, { "Iptc4xmpCore:Scene", "IPTC:SceneCode", TypeDesc::STRING, IsList }, { "Iptc4xmpExt:PersonInImage", "IPTC:PersonInImage", TypeDesc::STRING, IsList }, { "rdf:li", "" }, // ignore these strays { NULL, NULL } }; // Utility: add an attribute to the spec with the given xml name and // value. Search for it in xmptag, and if found that will tell us what // the type is supposed to be, as well as any special handling. If not // found in the table, add it as a string and hope for the best. static void add_attrib (ImageSpec &spec, const char *xmlname, const char *xmlvalue) { #if DEBUG_XMP_READ std::cerr << "add_attrib " << xmlname << ": '" << xmlvalue << "'\n"; #endif std::string oiioname; TypeDesc oiiotype; int special = NothingSpecial; // See if it's in the xmp table, which will tell us something about the // proper type (everything in the xml itself just looks like a string). for (int i = 0; xmptag[i].xmpname; ++i) { if (Strutil::iequals (xmptag[i].xmpname, xmlname)) { if (! xmptag[i].oiioname || ! xmptag[i].oiioname[0]) return; // ignore it purposefully // Found oiioname = xmptag[i].oiioname; oiiotype = xmptag[i].oiiotype; special = xmptag[i].special; break; } } // Also try looking it up to see if it's a known exif tag. int tag = -1, tifftype = -1, count = 0; if (Strutil::istarts_with(xmlname,"Exif:") && (exif_tag_lookup (xmlname, tag, tifftype, count) || exif_tag_lookup (xmlname+5, tag, tifftype, count))) { // It's a known Exif name if (tifftype == TIFF_SHORT && count == 1) oiiotype = TypeDesc::UINT; else if (tifftype == TIFF_LONG && count == 1) oiiotype = TypeDesc::UINT; else if ((tifftype == TIFF_RATIONAL || tifftype == TIFF_SRATIONAL) && count == 1) { oiiotype = TypeDesc::FLOAT; special = Rational; } else if (tifftype == TIFF_ASCII) oiiotype = TypeDesc::STRING; else if (tifftype == TIFF_BYTE && count == 1) oiiotype = TypeDesc::INT; else if (tifftype == TIFF_NOTYPE) return; // skip } if (oiiotype == TypeDesc::STRING) { std::string val; if (special & (IsList|IsSeq)) { // Special case -- append it to a list std::vector items; ImageIOParameter *p = spec.find_attribute (oiioname, TypeDesc::STRING); bool dup = false; if (p) { Strutil::split (*(const char **)p->data(), items, ";"); for (size_t item = 0; item < items.size(); ++item) { items[item] = Strutil::strip (items[item]); dup |= (items[item] == xmlvalue); } dup |= (xmlvalue == std::string(*(const char **)p->data())); } if (! dup) items.push_back (xmlvalue); val = Strutil::join (items, "; "); } else { val = xmlvalue; } spec.attribute (oiioname, val); return; } else if (oiiotype == TypeDesc::INT) { if (special & IsBool) spec.attribute (oiioname, (int)Strutil::iequals(xmlvalue,"true")); else // ordinary int spec.attribute (oiioname, (int)atoi(xmlvalue)); return; } else if (oiiotype == TypeDesc::UINT) { spec.attribute (oiioname, Strutil::from_string(xmlvalue)); return; } else if (oiiotype == TypeDesc::FLOAT) { float f = atoi (xmlvalue); const char *slash = strchr (xmlvalue, '/'); if (slash) // It's rational! f /= (float) atoi (slash+1); spec.attribute (oiioname, f); return; } #if (!defined(NDEBUG) || DEBUG_XMP_READ) else { std::cerr << "iptc xml add_attrib unknown type " << xmlname << ' ' << oiiotype.c_str() << "\n"; } #endif // Catch-all for unrecognized things -- just add them as a string! spec.attribute (xmlname, xmlvalue); } // Utility: Search str for the first substring in str (starting from // position pos) that starts with startmarker and ends with endmarker. // If not found, return false. If found, return true, store the // beginning and ending indices in startpos and endpos. static bool extract_middle (const std::string &str, size_t pos, const char *startmarker, const char *endmarker, size_t &startpos, size_t &endpos) { startpos = str.find (startmarker, pos); if (startpos == std::string::npos) return false; // start marker not found endpos = str.find (endmarker, startpos); if (endpos == std::string::npos) return false; // end marker not found endpos += strlen (endmarker); return true; } static void decode_xmp_node (pugi::xml_node node, ImageSpec &spec, int level=1, const char *parentname=NULL, bool isList=false) { std::string mylist; // will accumulate for list items for ( ; node; node = node.next_sibling()) { #if DEBUG_XMP_READ std::cerr << "Level " << level << " " << node.name() << " = " << node.value() << "\n"; #endif // First, encode all attributes of this node for (pugi::xml_attribute attr = node.first_attribute(); attr; attr = attr.next_attribute()) { #if DEBUG_XMP_READ std::cerr << " level " << level << " parent " << (parentname?parentname:"-") << " attr " << attr.name() << ' ' << attr.value() << "\n"; #endif if (Strutil::istarts_with(attr.name(), "xml:") || Strutil::istarts_with(attr.name(), "xmlns:")) continue; // xml attributes aren't image metadata if (attr.name()[0] && attr.value()[0]) add_attrib (spec, attr.name(), attr.value()); } if (Strutil::iequals(node.name(), "xmpMM::History")) { // FIXME -- image history is complicated. Come back to it. continue; } if (Strutil::iequals(node.name(), "rdf:Bag") || Strutil::iequals(node.name(), "rdf:Seq") || Strutil::iequals(node.name(), "rdf:Alt") || Strutil::iequals(node.name(), "rdf:li")) { // Various kinds of lists. Recuse, pass the parent name // down, and let the child know it's part of a list. decode_xmp_node (node.first_child(), spec, level+1, parentname, true); } else { // Not a list, but it's got children. Recurse. decode_xmp_node (node.first_child(), spec, level+1, node.name()); } // If this node has a value but no name, it's definitely part // of a list. Accumulate the list items, separated by semicolons. if (parentname && !node.name()[0] && node.value()[0]) { if (mylist.size()) mylist += ";"; mylist += node.value(); } } // If we have accumulated a list, turn it into an attribute if (parentname && mylist.size()) { add_attrib (spec, parentname, mylist.c_str()); } } } // anonymous namespace bool decode_xmp (const std::string &xml, ImageSpec &spec) { #if DEBUG_XMP_READ std::cerr << "XMP dump:\n---\n" << xml << "\n---\n"; #endif if (! xml.length()) return true; for (size_t startpos = 0, endpos = 0; extract_middle (xml, endpos, "", startpos, endpos); ) { // Turn that middle section into an XML document std::string rdf (xml, startpos, endpos-startpos); // scooch in #if DEBUG_XMP_READ std::cerr << "RDF is:\n---\n" << rdf << "\n---\n"; #endif pugi::xml_document doc; pugi::xml_parse_result parse_result = doc.load_buffer (&rdf[0], rdf.size()); if (! parse_result) { #if DEBUG_XMP_READ std::cerr << "Error parsing XML\n"; #endif return true; } // Decode the contents of the XML document (it will recurse) decode_xmp_node (doc.first_child(), spec); } return true; } // Turn one ImageIOParameter (whose xmp info we know) into a properly // serialized xmp string. static std::string stringize (const ImageIOParameterList::const_iterator &p, const XMPtag &xmptag) { if (p->type() == TypeDesc::STRING) { if (xmptag.special & DateConversion) { // FIXME -- convert to yyyy-mm-ddThh:mm:ss.sTZD // return std::string(); } return std::string(*(const char **)p->data()); } else if (p->type() == TypeDesc::INT) { if (xmptag.special & IsBool) return *(const int *)p->data() ? "True" : "False"; else // ordinary int return Strutil::format ("%d", *(const int *)p->data()); } else if (p->type() == TypeDesc::FLOAT) { if (xmptag.special & Rational) { unsigned int num, den; float_to_rational (*(const float *)p->data(), num, den); return Strutil::format ("%d/%d", num, den); } else { return Strutil::format ("%g", *(const float *)p->data()); } } return std::string(); } static void gather_xmp_attribs (const ImageSpec &spec, std::vector > &list) { // Loop over all params... for (ImageIOParameterList::const_iterator p = spec.extra_attribs.begin(); p != spec.extra_attribs.end(); ++p) { // For this param, see if there's a table entry with a matching // name, where the xmp name is in the right category. for (int i = 0; xmptag[i].xmpname; ++i) { if (! Strutil::iequals (p->name().c_str(), xmptag[i].oiioname)) continue; // Name doesn't match if (xmptag[i].special & Suppress) { break; // Purposely suppressing } std::string s = stringize (p,xmptag[i]); if (s.size()) { list.push_back (std::pair(i, s)); //std::cerr << " " << xmptag[i].xmpname << " = " << s << "\n"; } } } } enum XmpControl { XMP_suppress, XMP_nodes, XMP_attribs, XMP_SeqList, // sequential list XMP_BagList, // unordered list XMP_AltList // alternate list, WTF is that? }; // Turn an entire category of XMP items into a properly serialized // xml fragment. static std::string encode_xmp_category (std::vector > &list, const char *xmlnamespace, const char *pattern, const char *exclude_pattern, const char *nodename, const char *url, bool minimal, XmpControl control) { std::string category = std::string(xmlnamespace) + ':'; std::string xmp; std::string xmp_minimal; #if DEBUG_XMP_WRITE std::cerr << "Category " << xmlnamespace << ", pattern '" << pattern << "'\n"; #endif // Loop over all params... bool found = false; for (size_t li = 0; li < list.size(); ++li) { // For this param, see if there's a table entry with a matching // name, where the xmp name is in the right category. int i = list[li].first; const std::string &val (list[li].second); const char *xmpname (xmptag[i].xmpname); if (control == XMP_attribs && (xmptag[i].special & (IsList|IsSeq))) continue; // Skip lists for attrib output if (exclude_pattern && exclude_pattern[0] && Strutil::istarts_with (xmpname, exclude_pattern)) { continue; } if (Strutil::istarts_with (xmpname, pattern)) { std::string x; if (control == XMP_attribs) x = Strutil::format ("%s=\"%s\"", xmpname, val); else if (control == XMP_AltList || control == XMP_BagList) { std::vector vals; Strutil::split (val, vals, ";"); for (size_t i = 0; i < vals.size(); ++i) { vals[i] = Strutil::strip (vals[i]); x += Strutil::format ("%s", vals[i]); } } else x = Strutil::format ("<%s>%s", xmpname, val, xmpname); if (! x.empty() && control != XMP_suppress) { if (! found) { // if (nodename && nodename[0]) { // x = Strutil::format("<%s ", nodename); // } } if (minimal && (xmptag[i].special & (TiffRedundant|ExifRedundant))) { if (xmp_minimal.size()) xmp_minimal += ' '; xmp_minimal += x; } else { if (xmp.size()) xmp += ' '; xmp += x; } found = true; #if DEBUG_XMP_WRITE std::cerr << " going to output '" << x << "'\n"; #endif } #if DEBUG_XMP_WRITE else std::cerr << " NOT going to output '" << x << "'\n"; #endif list.erase (list.begin()+li); --li; } } if (xmp.length() && xmp_minimal.length()) xmp += ' ' + xmp_minimal; #if 1 if (xmp.length()) { if (control == XMP_BagList) xmp = Strutil::format ("<%s> %s ", nodename ? nodename : xmlnamespace, xmp, nodename ? nodename : xmlnamespace); else if (control == XMP_SeqList) xmp = Strutil::format ("<%s> %s ", nodename ? nodename : xmlnamespace, xmp, nodename ? nodename : xmlnamespace); else if (control == XMP_AltList) xmp = Strutil::format ("<%s> %s ", nodename ? nodename : xmlnamespace, xmp, nodename ? nodename : xmlnamespace); #if 0 else if (control == XMP_nodes) xmp = Strutil::format("<%s>%s", nodename ? nodename : xmlnamespace, xmp, nodename ? nodename : xmlnamespace); nodename); #endif std::string r; r += Strutil::format (""); r += xmp; if (control == XMP_attribs) r += "/> "; // end the > list; gather_xmp_attribs (spec, list); std::string xmp; #if 1 // This stuff seems to work xmp += encode_xmp_category (list, "photoshop", "photoshop:", NULL, NULL, "http://ns.adobe.com/photoshop/1.0/", minimal, XMP_attribs); xmp += encode_xmp_category (list, "xmp", "xmp:Rating", NULL, NULL, "http://ns.adobe.com/xap/1.0/", minimal, XMP_attribs); xmp += encode_xmp_category (list, "xmp", "xmp:CreateDate", NULL, NULL, "http://ns.adobe.com/xap/1.0/", false, XMP_attribs); xmp += encode_xmp_category (list, "xmp", "xmp:ModifyDate", NULL, NULL, "http://ns.adobe.com/xap/1.0/", false, XMP_attribs); xmp += encode_xmp_category (list, "xmp", "xmp:MetadataDate", NULL, NULL, "http://ns.adobe.com/xap/1.0/", false, XMP_attribs); xmp += encode_xmp_category (list, "xmpRights", "xmpRights:UsageTerms", NULL, "xmpRights:UsageTerms", "http://ns.adobe.com/xap/1.0/rights/", minimal, XMP_AltList); xmp += encode_xmp_category (list, "xmpRights", "xmpRights:", NULL, NULL, "http://ns.adobe.com/xap/1.0/rights/", minimal, XMP_attribs); xmp += encode_xmp_category (list, "dc", "dc:subject", NULL, "dc:subject", "http://purl.org/dc/elements/1.1/", minimal, XMP_BagList); xmp += encode_xmp_category (list, "Iptc4xmpCore", "Iptc4xmpCore:SubjectCode", NULL, "Iptc4xmpCore:SubjectCode", "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/", false, XMP_BagList); xmp += encode_xmp_category (list, "Iptc4xmpCore", "Iptc4xmpCore:", "Iptc4xmpCore:Ci", NULL, "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/", minimal, XMP_attribs); xmp += encode_xmp_category (list, "Iptc4xmpCore", "Iptc4xmpCore:Ci", NULL, "Iptc4xmpCore:CreatorContactInfo", "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/", minimal, XMP_attribs); xmp += encode_xmp_category (list, "Iptc4xmpCore", "Iptc4xmpCore:Scene", NULL, "Iptc4xmpCore:Scene", "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/", minimal, XMP_BagList); xmp += encode_xmp_category (list, "xmpMM", "xmpMM:", NULL, NULL, "http://ns.adobe.com/xap/1.0/mm/", minimal, XMP_attribs); #endif xmp += encode_xmp_category (list, "xmp", "xmp:", NULL, NULL, "http://ns.adobe.com/xap/1.0/", minimal, XMP_nodes); xmp += encode_xmp_category (list, "tiff", "tiff:", NULL, NULL, "http://ns.adobe.com/tiff/1.0/", minimal, XMP_attribs); #if 0 // Doesn't work yet xmp += encode_xmp_category (list, "xapRights", "xapRights:", NULL, NULL, "http://ns.adobe.com/xap/1.0/rights/", minimal, XMP_attribs); // xmp += encode_xmp_category (list, "dc", "dc:", NULL, NULL, // "http://purl.org/dc/elements/1.1/", minimal, XMP_attribs); #endif // FIXME exif xmp stRef stVer stJob xmpDM if (! xmp.empty()) { std::string head ( " " " " ); std::string foot (" "); xmp = head + xmp + foot; } #if DEBUG_XMP_WRITE std::cerr << "xmp to write = \n---\n" << xmp << "\n---\n"; std::cerr << "\n\nHere's what I still haven't output:\n"; for (size_t i = 0; i < list.size(); ++i) std::cerr << xmptag[list[i].first].xmpname << "\n"; #endif return xmp; } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/imagebuf_test.cpp0000644000175000017500000002533413151711064023230 0ustar mfvmfv/* Copyright 2012 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include OIIO_NAMESPACE_USING; inline int test_wrap (wrap_impl wrap, int coord, int origin, int width) { wrap (coord, origin, width); return coord; } void test_wrapmodes () { const int ori = 0; const int w = 4; static int val[] = {-7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -10}, cla[] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3}, per[] = { 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1}, mir[] = { 1, 2, 3, 3, 2, 1, 0, 0, 1, 2, 3, 3, 2, 1, 0, 0, 1}; for (int i = 0; val[i] > -10; ++i) { OIIO_CHECK_EQUAL (test_wrap (wrap_clamp, val[i], ori, w), cla[i]); OIIO_CHECK_EQUAL (test_wrap (wrap_periodic, val[i], ori, w), per[i]); OIIO_CHECK_EQUAL (test_wrap (wrap_periodic_pow2, val[i], ori, w), per[i]); OIIO_CHECK_EQUAL (test_wrap (wrap_mirror, val[i], ori, w), mir[i]); } } // Test iterators template void iterator_read_test () { const int WIDTH = 4, HEIGHT = 4, CHANNELS = 3; static float buf[HEIGHT][WIDTH][CHANNELS] = { { {0,0,0}, {1,0,1}, {2,0,2}, {3,0,3} }, { {0,1,4}, {1,1,5}, {2,1,6}, {3,1,7} }, { {0,2,8}, {1,2,9}, {2,2,10}, {3,2,11} }, { {0,3,12}, {1,3,13}, {2,3,14}, {3,3,15} } }; ImageSpec spec (WIDTH, HEIGHT, CHANNELS, TypeDesc::FLOAT); ImageBuf A (spec, buf); ITERATOR p (A); OIIO_CHECK_EQUAL (p[0], 0.0f); OIIO_CHECK_EQUAL (p[1], 0.0f); OIIO_CHECK_EQUAL (p[2], 0.0f); // Explicit position p.pos (2, 1); OIIO_CHECK_EQUAL (p.x(), 2); OIIO_CHECK_EQUAL (p.y(), 1); OIIO_CHECK_EQUAL (p[0], 2.0f); OIIO_CHECK_EQUAL (p[1], 1.0f); OIIO_CHECK_EQUAL (p[2], 6.0f); // Iterate a few times ++p; OIIO_CHECK_EQUAL (p.x(), 3); OIIO_CHECK_EQUAL (p.y(), 1); OIIO_CHECK_EQUAL (p[0], 3.0f); OIIO_CHECK_EQUAL (p[1], 1.0f); OIIO_CHECK_EQUAL (p[2], 7.0f); ++p; OIIO_CHECK_EQUAL (p.x(), 0); OIIO_CHECK_EQUAL (p.y(), 2); OIIO_CHECK_EQUAL (p[0], 0.0f); OIIO_CHECK_EQUAL (p[1], 2.0f); OIIO_CHECK_EQUAL (p[2], 8.0f); std::cout << "iterator_read_test result:"; int i = 0; for (ITERATOR p (A); !p.done(); ++p, ++i) { if ((i % 4) == 0) std::cout << "\n "; std::cout << " " << p[0] << ' ' << p[1] << ' ' << p[2]; } std::cout << "\n"; } // Test iterators template void iterator_wrap_test (ImageBuf::WrapMode wrap, std::string wrapname) { const int WIDTH = 4, HEIGHT = 4, CHANNELS = 3; static float buf[HEIGHT][WIDTH][CHANNELS] = { { {0,0,0}, {1,0,1}, {2,0,2}, {3,0,3} }, { {0,1,4}, {1,1,5}, {2,1,6}, {3,1,7} }, { {0,2,8}, {1,2,9}, {2,2,10}, {3,2,11} }, { {0,3,12}, {1,3,13}, {2,3,14}, {3,3,15} } }; ImageSpec spec (WIDTH, HEIGHT, CHANNELS, TypeDesc::FLOAT); ImageBuf A (spec, buf); std::cout << "iterator_wrap_test " << wrapname << ":"; int i = 0; int noutside = 0; for (ITERATOR p (A, ROI(-2, WIDTH+2, -2, HEIGHT+2), wrap); !p.done(); ++p, ++i) { if ((i % 8) == 0) std::cout << "\n "; std::cout << " " << p[0] << ' ' << p[1] << ' ' << p[2]; // Check wraps if (! p.exists()) { ++noutside; if (wrap == ImageBuf::WrapBlack) { OIIO_CHECK_EQUAL (p[0], 0.0f); OIIO_CHECK_EQUAL (p[1], 0.0f); OIIO_CHECK_EQUAL (p[2], 0.0f); } else if (wrap == ImageBuf::WrapClamp) { ITERATOR q = p; q.pos (clamp (p.x(), 0, WIDTH-1), clamp (p.y(), 0, HEIGHT-1)); OIIO_CHECK_EQUAL (p[0], q[0]); OIIO_CHECK_EQUAL (p[1], q[1]); OIIO_CHECK_EQUAL (p[2], q[2]); } else if (wrap == ImageBuf::WrapPeriodic) { ITERATOR q = p; q.pos (p.x() % WIDTH, p.y() % HEIGHT); OIIO_CHECK_EQUAL (p[0], q[0]); OIIO_CHECK_EQUAL (p[1], q[1]); OIIO_CHECK_EQUAL (p[2], q[2]); } else if (wrap == ImageBuf::WrapMirror) { ITERATOR q = p; int x = p.x(), y = p.y(); wrap_mirror (x, 0, WIDTH); wrap_mirror (y, 0, HEIGHT); q.pos (x, y); OIIO_CHECK_EQUAL (p[0], q[0]); OIIO_CHECK_EQUAL (p[1], q[1]); OIIO_CHECK_EQUAL (p[2], q[2]); } } } std::cout << "\n"; OIIO_CHECK_EQUAL (noutside, 48); // Should be 48 wrapped pixels } // Tests ImageBuf construction from application buffer void ImageBuf_test_appbuffer () { const int WIDTH = 8; const int HEIGHT = 8; const int CHANNELS = 3; float buf[HEIGHT][WIDTH][CHANNELS] = { { {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {1,0,0}, {0,0,0}, {0,0,0}, {0,0,0} }, { {0,0,0}, {0,0,0}, {0,0,0}, {1,0,0}, {0,0,0}, {1,0,0}, {0,0,0}, {0,0,0} }, { {0,0,0}, {0,0,0}, {1,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {1,0,0}, {0,0,0} }, { {0,0,0}, {1,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {1,0,0} }, { {0,0,0}, {0,0,0}, {1,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {1,0,0}, {0,0,0} }, { {0,0,0}, {0,0,0}, {0,0,0}, {1,0,0}, {0,0,0}, {1,0,0}, {0,0,0}, {0,0,0} }, { {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {1,0,0}, {0,0,0}, {0,0,0}, {0,0,0} }, { {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0} } }; ImageSpec spec (WIDTH, HEIGHT, CHANNELS, TypeDesc::FLOAT); ImageBuf A (spec, buf); // Make sure A now points to the buffer OIIO_CHECK_EQUAL ((void *)A.pixeladdr (0, 0, 0), (void *)buf); // write it A.write ("A_imagebuf_test.tif"); // Read it back and make sure it matches the original ImageBuf B ("A_imagebuf_test.tif"); for (int y = 0; y < HEIGHT; ++y) for (int x = 0; x < WIDTH; ++x) for (int c = 0; c < WIDTH; ++c) OIIO_CHECK_EQUAL (A.getchannel (x, y, 0, c), B.getchannel (x, y, 0, c)); // Make sure we can write to the buffer float pix[CHANNELS] = { 0.0, 42.0, 0 }; A.setpixel (3, 2, 0, pix); OIIO_CHECK_EQUAL (buf[2][3][1], 42.0); // Make sure we can copy-construct the ImageBuf and it points to the // same application buffer. ImageBuf C (A); OIIO_CHECK_EQUAL ((void *)A.pixeladdr(0,0,0), (void*)C.pixeladdr(0,0,0)); } void test_open_with_config () { // N.B. This function must run after ImageBuf_test_appbuffer, which // writes "A.tif". ImageCache *ic = ImageCache::create(false); ImageSpec config; config.attribute ("oiio:DebugOpenConfig!", 1); ImageBuf A ("A_imagebuf_test.tif", 0, 0, ic, &config); OIIO_CHECK_EQUAL (A.spec().get_int_attribute("oiio:DebugOpenConfig!",0), 42); ic->destroy (ic); } void test_empty_iterator () { // Ensure that ImageBuf iterators over empty ROIs immediately appear // done ImageBuf A (ImageSpec (64, 64, 3, TypeDesc::FLOAT)); ROI roi (10, 10, 20, 40, 0, 1); for (ImageBuf::Iterator p (A, roi); ! p.done(); ++p) { std::cout << "p is " << p.x() << ' ' << p.y() << ' ' << p.z() << "\n"; OIIO_CHECK_ASSERT (0 && "should never execute this loop body"); } } void print (const ImageBuf &A) { ASSERT (A.spec().format == TypeDesc::FLOAT); for (ImageBuf::ConstIterator p (A); ! p.done(); ++p) { std::cout << " @" << p.x() << ',' << p.y() << "=("; for (int c = 0; c < A.nchannels(); ++c) std::cout << (c ? "," : "") << p[c]; std::cout << ')' << (p.x() == A.xmax() ? "\n" : ""); } std::cout << "\n"; } void test_set_get_pixels () { std::cout << "\nTesting set_pixels, get_pixels:\n"; const int nchans = 3; ImageBuf A (ImageSpec (4, 4, nchans, TypeDesc::FLOAT)); ImageBufAlgo::zero (A); std::cout << " Cleared:\n"; print (A); float newdata[2*2*nchans] = { 1,2,3, 4,5,6, 7,8,9, 10,11,12 }; A.set_pixels (ROI(1,3,1,3), TypeDesc::FLOAT, newdata); std::cout << " After set:\n"; print (A); float retrieved[2*2*nchans] = { 9,9,9, 9,9,9, 9,9,9, 9,9,9 }; A.get_pixels (ROI(1, 3, 1, 3, 0, 1), TypeDesc::FLOAT, retrieved); OIIO_CHECK_ASSERT (0 == memcmp (retrieved, newdata, 2*2*nchans)); } int main (int argc, char **argv) { test_wrapmodes (); // Lots of tests related to ImageBuf::Iterator test_empty_iterator (); iterator_read_test > (); iterator_read_test > (); iterator_wrap_test > (ImageBuf::WrapBlack, "black"); iterator_wrap_test > (ImageBuf::WrapClamp, "clamp"); iterator_wrap_test > (ImageBuf::WrapPeriodic, "periodic"); iterator_wrap_test > (ImageBuf::WrapMirror, "mirror"); ImageBuf_test_appbuffer (); test_open_with_config (); test_set_get_pixels (); Filesystem::remove ("A_imagebuf_test.tif"); return unit_test_failures; } openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/imagebufalgo_copy.cpp0000644000175000017500000007725013151711064024072 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// \file /// Implementation of ImageBufAlgo algorithms that merely move pixels /// or channels between images without altering their values. #include #include #include #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/imagebufalgo_util.h" #include "OpenImageIO/deepdata.h" #include "OpenImageIO/thread.h" OIIO_NAMESPACE_BEGIN template static bool paste_ (ImageBuf &dst, ROI dstroi, const ImageBuf &src, ROI srcroi, int nthreads) { // N.B. Punt on parallelizing because of the subtle interplay // between srcroi and dstroi, the parallel_image idiom doesn't // handle that especially well. And it's not worth customizing for // this function which is inexpensive and not commonly used, and so // would benefit little from parallelizing. We can always revisit // this later. But in the mean time, we maintain the 'nthreads' // parameter for uniformity with the rest of IBA. int src_nchans = src.nchannels (); int dst_nchans = dst.nchannels (); ImageBuf::ConstIterator s (src, srcroi); ImageBuf::Iterator d (dst, dstroi); for ( ; ! s.done(); ++s, ++d) { if (! d.exists()) continue; // Skip paste-into pixels that don't overlap dst's data for (int c = srcroi.chbegin, c_dst = dstroi.chbegin; c < srcroi.chend; ++c, ++c_dst) { if (c_dst >= 0 && c_dst < dst_nchans) d[c_dst] = c < src_nchans ? s[c] : D(0); } } return true; } bool ImageBufAlgo::paste (ImageBuf &dst, int xbegin, int ybegin, int zbegin, int chbegin, const ImageBuf &src, ROI srcroi, int nthreads) { if (! srcroi.defined()) srcroi = get_roi(src.spec()); ROI dstroi (xbegin, xbegin+srcroi.width(), ybegin, ybegin+srcroi.height(), zbegin, zbegin+srcroi.depth(), chbegin, chbegin+srcroi.nchannels()); ROI dstroi_save = dstroi; // save the original if (! IBAprep (dstroi, &dst)) return false; // do the actual copying bool ok; OIIO_DISPATCH_TYPES2 (ok, "paste", paste_, dst.spec().format, src.spec().format, dst, dstroi_save, src, srcroi, nthreads); return ok; } template static bool copy_ (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads=1) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(copy_, OIIO::ref(dst), OIIO::cref(src), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case // Deep if (dst.deep()) { DeepData &dstdeep (*dst.deepdata()); const DeepData &srcdeep (*src.deepdata()); ImageBuf::ConstIterator s (src, roi); for (ImageBuf::Iterator d (dst, roi); ! d.done(); ++d, ++s) { int samples = s.deep_samples (); // The caller should ALREADY have set the samples, since that // is not thread-safe against the copying below. // d.set_deep_samples (samples); DASSERT (d.deep_samples() == samples); if (samples == 0) continue; for (int c = roi.chbegin; c < roi.chend; ++c) { if (dstdeep.channeltype(c) == TypeDesc::UINT32 && srcdeep.channeltype(c) == TypeDesc::UINT32) for (int samp = 0; samp < samples; ++samp) d.set_deep_value (c, samp, (uint32_t)s.deep_value_uint(c, samp)); else for (int samp = 0; samp < samples; ++samp) d.set_deep_value (c, samp, (float)s.deep_value(c, samp)); } } return true; } // Below is the non-deep case ImageBuf::ConstIterator s (src, roi); ImageBuf::Iterator d (dst, roi); for ( ; ! d.done(); ++d, ++s) { for (int c = roi.chbegin; c < roi.chend; ++c) d[c] = s[c]; } return true; } bool ImageBufAlgo::copy (ImageBuf &dst, const ImageBuf &src, TypeDesc convert, ROI roi, int nthreads) { if (&dst == &src) // trivial copy to self return true; roi.chend = std::min (roi.chend, src.nchannels()); if (! dst.initialized()) { ImageSpec newspec = src.spec(); set_roi (newspec, roi); newspec.nchannels = roi.chend; if (convert != TypeDesc::UNKNOWN) newspec.set_format (convert); dst.reset (newspec); } IBAprep (roi, &dst, &src, IBAprep_SUPPORT_DEEP); if (dst.deep()) { // If it's deep, figure out the sample allocations first, because // it's not thread-safe to do that simultaneously with copying the // values. ImageBuf::ConstIterator s (src, roi); for (ImageBuf::Iterator d (dst, roi); !d.done(); ++d, ++s) d.set_deep_samples (s.deep_samples()); } bool ok; OIIO_DISPATCH_TYPES2 (ok, "copy", copy_, dst.spec().format, src.spec().format, dst, src, roi, nthreads); return ok; } bool ImageBufAlgo::crop (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { dst.clear (); roi.chend = std::min (roi.chend, src.nchannels()); if (! IBAprep (roi, &dst, &src, IBAprep_SUPPORT_DEEP)) return false; if (dst.deep()) { // If it's deep, figure out the sample allocations first, because // it's not thread-safe to do that simultaneously with copying the // values. ImageBuf::ConstIterator s (src, roi); for (ImageBuf::Iterator d (dst, roi); !d.done(); ++d, ++s) d.set_deep_samples (s.deep_samples()); } bool ok; OIIO_DISPATCH_TYPES2 (ok, "crop", copy_, dst.spec().format, src.spec().format, dst, src, roi, nthreads); return ok; } bool ImageBufAlgo::cut (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { bool ok = crop (dst, src, roi, nthreads); ASSERT(ok); if (! ok) return false; // Crop did the heavy lifting of copying the roi of pixels from src to // dst, but now we need to make it look like we cut that rectangle out // and repositioned it at the origin. dst.specmod().x = 0; dst.specmod().y = 0; dst.specmod().z = 0; dst.set_roi_full (dst.roi()); return true; } template static bool flip_ (ImageBuf &dst, const ImageBuf &src, ROI dst_roi, int nthreads) { ROI src_roi_full = src.roi_full(); ROI dst_roi_full = dst.roi_full(); ImageBuf::ConstIterator s (src); ImageBuf::Iterator d (dst, dst_roi); for ( ; ! d.done(); ++d) { int yy = d.y() - dst_roi_full.ybegin; s.pos (d.x(), src_roi_full.yend-1 - yy, d.z()); for (int c = dst_roi.chbegin; c < dst_roi.chend; ++c) d[c] = s[c]; } return true; } bool ImageBufAlgo::flip(ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (&dst == &src) { // Handle in-place operation ImageBuf tmp; tmp.swap (const_cast(src)); return flip (dst, tmp, roi, nthreads); } ROI src_roi = roi.defined() ? roi : src.roi(); ROI src_roi_full = src.roi_full(); int offset = src_roi.ybegin - src_roi_full.ybegin; int start = src_roi_full.yend - offset - src_roi.height(); ROI dst_roi (src_roi.xbegin, src_roi.xend, start, start+src_roi.height(), src_roi.zbegin, src_roi.zend, src_roi.chbegin, src_roi.chend); ASSERT (dst_roi.width() == src_roi.width() && dst_roi.height() == src_roi.height()); // Compute the destination ROI, it's the source ROI reflected across // the midline of the display window. if (! IBAprep (dst_roi, &dst, &src)) return false; bool ok; OIIO_DISPATCH_TYPES2 (ok, "flip", flip_, dst.spec().format, src.spec().format, dst, src, dst_roi, nthreads); return ok; } template static bool flop_ (ImageBuf &dst, const ImageBuf &src, ROI dst_roi, int nthreads) { ROI src_roi_full = src.roi_full(); ROI dst_roi_full = dst.roi_full(); ImageBuf::ConstIterator s (src); ImageBuf::Iterator d (dst, dst_roi); for ( ; ! d.done(); ++d) { int xx = d.x() - dst_roi_full.xbegin; s.pos (src_roi_full.xend-1 - xx, d.y(), d.z()); for (int c = dst_roi.chbegin; c < dst_roi.chend; ++c) d[c] = s[c]; } return true; } bool ImageBufAlgo::flop(ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (&dst == &src) { // Handle in-place operation ImageBuf tmp; tmp.swap (const_cast(src)); return flop (dst, tmp, roi, nthreads); } ROI src_roi = roi.defined() ? roi : src.roi(); ROI src_roi_full = src.roi_full(); int offset = src_roi.xbegin - src_roi_full.xbegin; int start = src_roi_full.xend - offset - src_roi.width(); ROI dst_roi (start, start+src_roi.width(), src_roi.ybegin, src_roi.yend, src_roi.zbegin, src_roi.zend, src_roi.chbegin, src_roi.chend); ASSERT (dst_roi.width() == src_roi.width() && dst_roi.height() == src_roi.height()); // Compute the destination ROI, it's the source ROI reflected across // the midline of the display window. if (! IBAprep (dst_roi, &dst, &src)) return false; bool ok; OIIO_DISPATCH_TYPES2 (ok, "flop", flop_, dst.spec().format, src.spec().format, dst, src, dst_roi, nthreads); return ok; } template static bool rotate90_ (ImageBuf &dst, const ImageBuf &src, ROI dst_roi, int nthreads) { ROI dst_roi_full = dst.roi_full(); ImageBuf::ConstIterator s (src); ImageBuf::Iterator d (dst, dst_roi); for ( ; ! d.done(); ++d) { s.pos (d.y(), dst_roi_full.xend - d.x() - 1, d.z()); for (int c = dst_roi.chbegin; c < dst_roi.chend; ++c) d[c] = s[c]; } return true; } bool ImageBufAlgo::rotate90 (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (&dst == &src) { // Handle in-place operation ImageBuf tmp; tmp.swap (const_cast(src)); return rotate90 (dst, tmp, roi, nthreads); } ROI src_roi = roi.defined() ? roi : src.roi(); ROI src_roi_full = src.roi_full(); // Rotated full ROI swaps width and height, and keeps its origin // where the original origin was. ROI dst_roi_full (src_roi_full.xbegin, src_roi_full.xbegin+src_roi_full.height(), src_roi_full.ybegin, src_roi_full.ybegin+src_roi_full.width(), src_roi_full.zbegin, src_roi_full.zend, src_roi_full.chbegin, src_roi_full.chend); ROI dst_roi (src_roi_full.yend-src_roi.yend, src_roi_full.yend-src_roi.ybegin, src_roi.xbegin, src_roi.xend, src_roi.zbegin, src_roi.zend, src_roi.chbegin, src_roi.chend); ASSERT (dst_roi.width() == src_roi.height() && dst_roi.height() == src_roi.width()); bool dst_initialized = dst.initialized(); if (! IBAprep (dst_roi, &dst, &src)) return false; if (! dst_initialized) dst.set_roi_full (dst_roi_full); bool ok; OIIO_DISPATCH_TYPES2 (ok, "rotate90", rotate90_, dst.spec().format, src.spec().format, dst, src, dst_roi, nthreads); return ok; } template static bool rotate180_ (ImageBuf &dst, const ImageBuf &src, ROI dst_roi, int nthreads) { ROI src_roi_full = src.roi_full(); ROI dst_roi_full = dst.roi_full(); ImageBuf::ConstIterator s (src); ImageBuf::Iterator d (dst, dst_roi); for ( ; ! d.done(); ++d) { int xx = d.x() - dst_roi_full.xbegin; int yy = d.y() - dst_roi_full.ybegin; s.pos (src_roi_full.xend-1 - xx, src_roi_full.yend-1 - yy, d.z()); for (int c = dst_roi.chbegin; c < dst_roi.chend; ++c) d[c] = s[c]; } return true; } bool ImageBufAlgo::rotate180 (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (&dst == &src) { // Handle in-place operation ImageBuf tmp; tmp.swap (const_cast(src)); return rotate180 (dst, tmp, roi, nthreads); } ROI src_roi = roi.defined() ? roi : src.roi(); ROI src_roi_full = src.roi_full(); int xoffset = src_roi.xbegin - src_roi_full.xbegin; int xstart = src_roi_full.xend - xoffset - src_roi.width(); int yoffset = src_roi.ybegin - src_roi_full.ybegin; int ystart = src_roi_full.yend - yoffset - src_roi.height(); ROI dst_roi (xstart, xstart+src_roi.width(), ystart, ystart+src_roi.height(), src_roi.zbegin, src_roi.zend, src_roi.chbegin, src_roi.chend); ASSERT (dst_roi.width() == src_roi.width() && dst_roi.height() == src_roi.height()); // Compute the destination ROI, it's the source ROI reflected across // the midline of the display window. if (! IBAprep (dst_roi, &dst, &src)) return false; bool ok; OIIO_DISPATCH_TYPES2 (ok, "rotate180", rotate180_, dst.spec().format, src.spec().format, dst, src, dst_roi, nthreads); return ok; } template static bool rotate270_ (ImageBuf &dst, const ImageBuf &src, ROI dst_roi, int nthreads) { ROI dst_roi_full = dst.roi_full(); ImageBuf::ConstIterator s (src); ImageBuf::Iterator d (dst, dst_roi); for ( ; ! d.done(); ++d) { s.pos (dst_roi_full.yend - d.y() - 1, d.x(), d.z()); for (int c = dst_roi.chbegin; c < dst_roi.chend; ++c) d[c] = s[c]; } return true; } bool ImageBufAlgo::rotate270 (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (&dst == &src) { // Handle in-place operation ImageBuf tmp; tmp.swap (const_cast(src)); return rotate270 (dst, tmp, roi, nthreads); } ROI src_roi = roi.defined() ? roi : src.roi(); ROI src_roi_full = src.roi_full(); // Rotated full ROI swaps width and height, and keeps its origin // where the original origin was. ROI dst_roi_full (src_roi_full.xbegin, src_roi_full.xbegin+src_roi_full.height(), src_roi_full.ybegin, src_roi_full.ybegin+src_roi_full.width(), src_roi_full.zbegin, src_roi_full.zend, src_roi_full.chbegin, src_roi_full.chend); ROI dst_roi (src_roi.ybegin, src_roi.yend, src_roi_full.xend-src_roi.xend, src_roi_full.xend-src_roi.xbegin, src_roi.zbegin, src_roi.zend, src_roi.chbegin, src_roi.chend); ASSERT (dst_roi.width() == src_roi.height() && dst_roi.height() == src_roi.width()); bool dst_initialized = dst.initialized(); if (! IBAprep (dst_roi, &dst, &src)) return false; if (! dst_initialized) dst.set_roi_full (dst_roi_full); bool ok; OIIO_DISPATCH_TYPES2 (ok, "rotate270", rotate270_, dst.spec().format, src.spec().format, dst, src, dst_roi, nthreads); return ok; } bool ImageBufAlgo::reorient (ImageBuf &dst, const ImageBuf &src, int nthreads) { ImageBuf tmp; bool ok = false; switch (src.orientation()) { case 1: ok = dst.copy (src); break; case 2: ok = ImageBufAlgo::flop (dst, src); break; case 3: ok = ImageBufAlgo::rotate180 (dst, src); break; case 4: ok = ImageBufAlgo::flip (dst, src); break; case 5: ok = ImageBufAlgo::rotate270 (tmp, src); if (ok) ok = ImageBufAlgo::flop (dst, tmp); else dst.error ("%s", tmp.geterror()); break; case 6: ok = ImageBufAlgo::rotate90 (dst, src); break; case 7: ok = ImageBufAlgo::flip (tmp, src); if (ok) ok = ImageBufAlgo::rotate90 (dst, tmp); else dst.error ("%s", tmp.geterror()); break; case 8: ok = ImageBufAlgo::rotate270 (dst, src); break; } dst.set_orientation (1); return ok; } template static bool transpose_ (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(transpose_, OIIO::ref(dst), OIIO::cref(src), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case ImageBuf::ConstIterator s (src, roi); ImageBuf::Iterator d (dst); for ( ; ! s.done(); ++s) { d.pos (s.y(), s.x(), s.z()); if (! d.exists()) continue; for (int c = roi.chbegin; c < roi.chend; ++c) d[c] = s[c]; } return true; } bool ImageBufAlgo::transpose (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (! roi.defined()) roi = get_roi (src.spec()); roi.chend = std::min (roi.chend, src.nchannels()); ROI dst_roi (roi.ybegin, roi.yend, roi.xbegin, roi.xend, roi.zbegin, roi.zend, roi.chbegin, roi.chend); bool dst_initialized = dst.initialized(); if (! IBAprep (dst_roi, &dst)) return false; if (! dst_initialized) { ROI r = src.roi_full(); ROI dst_roi_full (r.ybegin, r.yend, r.xbegin, r.xend, r.zbegin, r.zend, r.chbegin, r.chend); dst.set_roi_full (dst_roi_full); } bool ok; OIIO_DISPATCH_TYPES2 (ok, "transpose", transpose_, dst.spec().format, src.spec().format, dst, src, roi, nthreads); return ok; } template static bool circular_shift_ (ImageBuf &dst, const ImageBuf &src, int xshift, int yshift, int zshift, ROI dstroi, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(circular_shift_, OIIO::ref(dst), OIIO::cref(src), xshift, yshift, zshift, dstroi, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case int width = dstroi.width(), height = dstroi.height(), depth = dstroi.depth(); ImageBuf::ConstIterator s (src, roi); ImageBuf::Iterator d (dst); for ( ; ! s.done(); ++s) { int dx = s.x() + xshift; OIIO::wrap_periodic (dx, dstroi.xbegin, width); int dy = s.y() + yshift; OIIO::wrap_periodic (dy, dstroi.ybegin, height); int dz = s.z() + zshift; OIIO::wrap_periodic (dz, dstroi.zbegin, depth); d.pos (dx, dy, dz); if (! d.exists()) continue; for (int c = roi.chbegin; c < roi.chend; ++c) d[c] = s[c]; } return true; } bool ImageBufAlgo::circular_shift (ImageBuf &dst, const ImageBuf &src, int xshift, int yshift, int zshift, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &src)) return false; bool ok; OIIO_DISPATCH_TYPES2 (ok, "circular_shift", circular_shift_, dst.spec().format, src.spec().format, dst, src, xshift, yshift, zshift, roi, roi, nthreads); return ok; } template static bool channels_ (ImageBuf &dst, const ImageBuf &src, const int *channelorder, const float *channelvalues, ROI roi, int nthreads=0) { if (nthreads != 1 && roi.npixels() >= 64*1024) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(channels_, OIIO::ref(dst), OIIO::cref(src), channelorder, channelvalues, _1 /*roi*/, 1 /*ntheads*/), roi, nthreads); return true; } int nchannels = src.nchannels(); ImageBuf::ConstIterator s (src, roi); ImageBuf::Iterator d (dst, roi); for ( ; ! s.done(); ++s, ++d) { for (int c = roi.chbegin; c < roi.chend; ++c) { int cc = channelorder[c]; if (cc >= 0 && cc < nchannels) d[c] = s[cc]; else if (channelvalues) d[c] = channelvalues[c]; } } return true; } bool ImageBufAlgo::channels (ImageBuf &dst, const ImageBuf &src, int nchannels, const int *channelorder, const float *channelvalues, const std::string *newchannelnames, bool shuffle_channel_names, int nthreads) { // Not intended to create 0-channel images. if (nchannels <= 0) { dst.error ("%d-channel images not supported", nchannels); return false; } // If we dont have a single source channel, // hard to know how big to make the additional channels if (src.spec().nchannels == 0) { dst.error ("%d-channel images not supported", src.spec().nchannels); return false; } // If channelorder is NULL, it will be interpreted as // {0, 1, ..., nchannels-1}. int *local_channelorder = NULL; if (! channelorder) { local_channelorder = ALLOCA (int, nchannels); for (int c = 0; c < nchannels; ++c) local_channelorder[c] = c; channelorder = local_channelorder; } // If this is the identity transformation, just do a simple copy bool inorder = true; for (int c = 0; c < nchannels; ++c) { inorder &= (channelorder[c] == c); if (newchannelnames && newchannelnames[c].size() && c < int(src.spec().channelnames.size())) inorder &= (newchannelnames[c] == src.spec().channelnames[c]); } if (nchannels == src.spec().nchannels && inorder) { return dst.copy (src); } // Construct a new ImageSpec that describes the desired channel ordering. ImageSpec newspec = src.spec(); newspec.nchannels = nchannels; newspec.default_channel_names (); newspec.channelformats.clear(); newspec.alpha_channel = -1; newspec.z_channel = -1; bool all_same_type = true; for (int c = 0; c < nchannels; ++c) { int csrc = channelorder[c]; // If the user gave an explicit name for this channel, use it... if (newchannelnames && newchannelnames[c].size()) newspec.channelnames[c] = newchannelnames[c]; // otherwise, if shuffle_channel_names, use the channel name of // the src channel we're using (otherwise stick to the default name) else if (shuffle_channel_names && csrc >= 0 && csrc < src.spec().nchannels) newspec.channelnames[c] = src.spec().channelnames[csrc]; // otherwise, use the name of the source in that slot else if (csrc >= 0 && csrc < src.spec().nchannels) { newspec.channelnames[c] = src.spec().channelnames[csrc]; } TypeDesc type = src.spec().channelformat(csrc); newspec.channelformats.push_back (type); if (type != newspec.channelformats.front()) all_same_type = false; // Use the names (or designation of the src image, if // shuffle_channel_names is true) to deduce the alpha and z channels. if ((shuffle_channel_names && csrc == src.spec().alpha_channel) || Strutil::iequals (newspec.channelnames[c], "A") || Strutil::iequals (newspec.channelnames[c], "alpha")) newspec.alpha_channel = c; if ((shuffle_channel_names && csrc == src.spec().z_channel) || Strutil::iequals (newspec.channelnames[c], "Z")) newspec.z_channel = c; } if (all_same_type) // clear per-chan formats if newspec.channelformats.clear(); // they're all the same // Update the image (realloc with the new spec) dst.reset (newspec); if (dst.deep()) { // Deep case: ASSERT (src.deep() && src.deepdata() && dst.deepdata()); const DeepData &srcdata (*src.deepdata()); DeepData &dstdata (*dst.deepdata()); // The earlier dst.alloc() already called dstdata.init() for (int p = 0, npels = (int)newspec.image_pixels(); p < npels; ++p) dstdata.set_samples (p, srcdata.samples(p)); for (int p = 0, npels = (int)newspec.image_pixels(); p < npels; ++p) { if (! dstdata.samples(p)) continue; // no samples for this pixel for (int c = 0; c < newspec.nchannels; ++c) { int csrc = channelorder[c]; if (csrc < 0) { // Replacing the channel with a new value float val = channelvalues ? channelvalues[c] : 0.0f; for (int s = 0, ns = dstdata.samples(p); s < ns; ++s) dstdata.set_deep_value (p, c, s, val); } else { if (dstdata.channeltype(c) == TypeDesc::UINT) for (int s = 0, ns = dstdata.samples(p); s < ns; ++s) dstdata.set_deep_value (p, c, s, srcdata.deep_value_uint(p,csrc,s)); else for (int s = 0, ns = dstdata.samples(p); s < ns; ++s) dstdata.set_deep_value (p, c, s, srcdata.deep_value(p,csrc,s)); } } } return true; } // Below is the non-deep case bool ok; OIIO_DISPATCH_TYPES (ok, "channels", channels_, dst.spec().format, dst, src, channelorder, channelvalues, dst.roi(), nthreads); return ok; } template static bool channel_append_impl (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi, int nthreads) { if (nthreads == 1 || roi.npixels() < 1000) { int na = A.nchannels(), nb = B.nchannels(); int n = std::min (dst.nchannels(), na+nb); ImageBuf::Iterator r (dst, roi); ImageBuf::ConstIterator a (A, roi); ImageBuf::ConstIterator b (B, roi); for (; !r.done(); ++r, ++a, ++b) { for (int c = 0; c < n; ++c) { if (c < na) r[c] = a.exists() ? a[c] : 0.0f; else r[c] = b.exists() ? b[c-na] : 0.0f; } } } else { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind (channel_append_impl, OIIO::ref(dst), OIIO::cref(A), OIIO::cref(B), _1, 1), roi, nthreads); } return true; } bool ImageBufAlgo::channel_append (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi, int nthreads) { // If the region is not defined, set it to the union of the valid // regions of the two source images. if (! roi.defined()) roi = roi_union (get_roi (A.spec()), get_roi (B.spec())); // If dst has not already been allocated, set it to the right size, // make it unconditinally float. if (! dst.pixels_valid()) { ImageSpec dstspec = A.spec(); dstspec.set_format (TypeDesc::TypeFloat); // Append the channel descriptions dstspec.nchannels = A.spec().nchannels + B.spec().nchannels; for (int c = 0; c < B.spec().nchannels; ++c) { std::string name = B.spec().channelnames[c]; // It's a duplicate channel name. This will wreak havoc for // OpenEXR, so we need to choose a unique name. if (std::find(dstspec.channelnames.begin(), dstspec.channelnames.end(), name) != dstspec.channelnames.end()) { // First, let's see if the original image had a subimage // name and use that. std::string subname = B.spec().get_string_attribute("oiio:subimagename"); if (subname.size()) name = subname + "." + name; } if (std::find(dstspec.channelnames.begin(), dstspec.channelnames.end(), name) != dstspec.channelnames.end()) { // If it's still a duplicate, fall back on a totally // artificial name that contains the channel number. name = Strutil::format ("channel%d", A.spec().nchannels+c); } dstspec.channelnames.push_back (name); } if (dstspec.alpha_channel < 0 && B.spec().alpha_channel >= 0) dstspec.alpha_channel = B.spec().alpha_channel + A.nchannels(); if (dstspec.z_channel < 0 && B.spec().z_channel >= 0) dstspec.z_channel = B.spec().z_channel + A.nchannels(); set_roi (dstspec, roi); dst.reset (dstspec); } // For now, only support A and B having the same type. if (A.spec().format != B.spec().format) { dst.error ("Unable to perform channel_append of %s, %s -> %s", A.spec().format, B.spec().format, dst.spec().format); return false; } bool ok; OIIO_DISPATCH_TYPES2 (ok, "channel_append", channel_append_impl, dst.spec().format, A.spec().format, dst, A, B, roi, nthreads); return ok; } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libOpenImageIO/imageio_pvt.h.in0000644000175000017500000001104313151711064022757 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ /// \file /// Declarations for things that are used privately by ImageIO. #ifndef OPENIMAGEIO_IMAGEIO_PVT_H #define OPENIMAGEIO_IMAGEIO_PVT_H #include "OpenImageIO/imageio.h" #include "OpenImageIO/thread.h" OIIO_NAMESPACE_BEGIN namespace pvt { /// Mutex allowing thread safety of ImageOutput internals /// extern recursive_mutex imageio_mutex; extern atomic_int oiio_threads; extern atomic_int oiio_read_chunk; extern ustring plugin_searchpath; extern std::string format_list; extern std::string extension_list; extern std::string library_list; // For internal use - use error() below for a nicer interface. void seterror (const std::string& message); /// Use error() privately only. Protoype is conceptually printf-like, but /// also fully typesafe: /// void error (const char *format, ...); TINYFORMAT_WRAP_FORMAT (void, error, /**/, std::ostringstream msg;, msg, seterror(msg.str());) // Make sure all plugins are inventoried. Should only be called while // imageio_mutex is held. For internal use only. void catalog_all_plugins (std::string searchpath); /// Given the format, set the default quantization range. void get_default_quantize (TypeDesc format, long long &quant_min, long long &quant_max); /// Turn potentially non-contiguous-stride data (e.g. "RGBxRGBx") into /// contiguous-stride ("RGBRGB"), for any format or stride values /// (measured in bytes). Caller must pass in a dst pointing to enough /// memory to hold the contiguous rectangle. Return a ptr to where the /// contiguous data ended up, which is either dst or src (if the strides /// indicated that data were already contiguous). const void *contiguize (const void *src, int nchannels, stride_t xstride, stride_t ystride, stride_t zstride, void *dst, int width, int height, int depth, TypeDesc format); /// Turn contiguous data from any format into float data. Return a /// pointer to the converted data (which may still point to src if no /// conversion was necessary). const float *convert_to_float (const void *src, float *dst, int nvals, TypeDesc format); /// Turn contiguous float data into any format. Return a pointer to the /// converted data (which may still point to src if no conversion was /// necessary). const void *convert_from_float (const float *src, void *dst, size_t nvals, long long quant_min, long long quant_max, TypeDesc format); /// A version of convert_from_float that will break up big jobs with /// multiple threads. const void *parallel_convert_from_float (const float *src, void *dst, size_t nvals, TypeDesc format, int nthreads=0); } // namespace pvt OIIO_NAMESPACE_END //Define a default plugin search path #define OIIO_DEFAULT_PLUGIN_SEARCHPATH "@PLUGIN_SEARCH_PATH_NATIVE@" #endif // OPENIMAGEIO_IMAGEIO_PVT_H openimageio-1.7.17~dfsg0.orig/src/make/0000755000175000017500000000000013151711064016031 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/make/detectplatform.mk0000644000175000017500000000414313151711064021401 0ustar mfvmfv# License and copyright goes here ######################################################################### # detectplatform.mk # # Figure out which platform we are building on/for, set ${platform} and # ${hw}. # # This is called from master.mk ######################################################################### ######################################################################### # Figure out which platform we are building on/for # Start with unknown platform platform ?= unknown # Use 'uname -m' to determine the hardware architecture. This should # return "x86" or "x86_64" hw := ${shell uname -m} #$(info hardware = ${hw}) ifneq (${hw},x86) ifneq (${hw},x86_64) ifneq (${hw},i386) ifneq (${hw},i686) $(error "ERROR: Unknown hardware architecture") endif endif endif endif # Use 'uname', lowercased and stripped of pesky stuff, and the hardware # architecture in ${hw} to determine the platform that we're building # for, and store its name in ${platform}. uname := ${shell uname | sed 's/_NT-.*//' | tr '[:upper:]' '[:lower:]'} #$(info uname = ${uname}) ifeq (${platform},unknown) # Linux ifeq (${uname},linux) platform := linux ifeq (${hw},x86_64) platform := linux64 endif endif # Windows ifeq (${uname},cygwin) platform := windows ifeq (${hw},x86_64) platform := windows64 endif endif # Mac OS X ifeq (${uname},darwin) platform := macosx endif # If we haven't been able to determine the platform from uname, use # whatever is in $ARCH, if it's set. ifeq (${platform},unknown) ifneq (${ARCH},) platform := ${ARCH} endif endif # Manual override: if there's an environment variable $BUILDARCH, use that # no matter what ifneq (${BUILDARCH},) platform := ${BUILDARCH} endif endif # Throw an error if nothing worked ifeq (${platform},unknown) $(error "ERROR: Could not determine the platform") endif ifneq (${VERBOSE},) $(info platform=${platform}, hw=${hw}) endif # end of section where we figure out the platform ######################################################################### openimageio-1.7.17~dfsg0.orig/src/libutil/0000755000175000017500000000000013151711064016560 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/libutil/atomic_test.cpp0000644000175000017500000001634313151711064021606 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include "OpenImageIO/thread.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/sysutil.h" #include "OpenImageIO/timer.h" #include "OpenImageIO/argparse.h" #include "OpenImageIO/ustring.h" #include #include #include "OpenImageIO/unittest.h" OIIO_NAMESPACE_USING; // How do we test atomics? Run a whole bunch of threads, incrementing // and decrementing the crap out of it, and make sure it has the right // value at the end. static int iterations = 40000000; static int numthreads = 16; static int ntrials = 1; static bool verbose = false; static bool wedge = false; static spin_mutex print_mutex; // make the prints not clobber each other atomic_int ai; atomic_ll all; static void do_int_math (int iterations) { if (verbose) { spin_lock lock(print_mutex); std::cout << "thread " << boost::this_thread::get_id() << ", ai = " << ai << "\n"; } for (int i = 0; i < iterations; ++i) { ++ai; ai += 3; --ai; ai++; ai -= 3; --ai; // That should have a net change of 0, but since other threads // are doing operations simultaneously, it's only after all // threads have finished that we can be sure it's back to the // initial value. } } void test_atomic_int (int numthreads, int iterations) { ai = 42; thread_group threads; for (int i = 0; i < numthreads; ++i) { threads.create_thread (boost::bind (do_int_math, iterations)); } ASSERT ((int)threads.size() == numthreads); threads.join_all (); OIIO_CHECK_EQUAL (ai, 42); // Test and, or, xor ai &= 15; OIIO_CHECK_EQUAL (ai, 10); ai |= 6; OIIO_CHECK_EQUAL (ai, 14); ai ^= 31; OIIO_CHECK_EQUAL (ai, 17); ai = 42; int tmp; tmp = ai.fetch_and(15); OIIO_CHECK_EQUAL(tmp,42); OIIO_CHECK_EQUAL(ai,10); tmp = ai.fetch_or ( 6); OIIO_CHECK_EQUAL(tmp,10); OIIO_CHECK_EQUAL(ai,14); tmp = ai.fetch_xor(31); OIIO_CHECK_EQUAL(tmp,14); OIIO_CHECK_EQUAL(ai,17); } static void do_int64_math (int iterations) { if (verbose) { spin_lock lock(print_mutex); std::cout << "thread " << boost::this_thread::get_id() << ", all = " << all << "\n"; } for (int i = 0; i < iterations; ++i) { ++all; all += 3; --all; all++; all -= 3; --all; // That should have a net change of 0, but since other threads // are doing operations simultaneously, it's only after all // threads have finished that we can be sure it's back to the // initial value. } } void test_atomic_int64 (int numthreads, int iterations) { all = 0; thread_group threads; for (int i = 0; i < numthreads; ++i) { threads.create_thread (boost::bind (do_int64_math, iterations)); } threads.join_all (); OIIO_CHECK_EQUAL (all, 0); // Test and, or, xor all = 42; all &= 15; OIIO_CHECK_EQUAL (all, 10); all |= 6; OIIO_CHECK_EQUAL (all, 14); all ^= 31; OIIO_CHECK_EQUAL (all, 17); all = 42; long long tmp; tmp = all.fetch_and(15); OIIO_CHECK_EQUAL(tmp,42); OIIO_CHECK_EQUAL(all,10); tmp = all.fetch_or ( 6); OIIO_CHECK_EQUAL(tmp,10); OIIO_CHECK_EQUAL(all,14); tmp = all.fetch_xor(31); OIIO_CHECK_EQUAL(tmp,14); OIIO_CHECK_EQUAL(all,17); } void test_atomics (int numthreads, int iterations) { test_atomic_int (numthreads, iterations); test_atomic_int64 (numthreads, iterations); } static void getargs (int argc, char *argv[]) { bool help = false; ArgParse ap; ap.options ("atomic_test\n" OIIO_INTRO_STRING "\n" "Usage: atomic_test [options]", // "%*", parse_files, "", "--help", &help, "Print help message", "-v", &verbose, "Verbose mode", "--threads %d", &numthreads, ustring::format("Number of threads (default: %d)", numthreads).c_str(), "--iters %d", &iterations, ustring::format("Number of iterations (default: %d)", iterations).c_str(), "--trials %d", &ntrials, "Number of trials", "--wedge", &wedge, "Do a wedge test", NULL); if (ap.parse (argc, (const char**)argv) < 0) { std::cerr << ap.geterror() << std::endl; ap.usage (); exit (EXIT_FAILURE); } if (help) { ap.usage (); exit (EXIT_FAILURE); } } int main (int argc, char *argv[]) { #if !defined(NDEBUG) || defined(OIIO_CI) || defined(OIIO_CODECOV) // For the sake of test time, reduce the default iterations for DEBUG, // CI, and code coverage builds. Explicit use of --iters or --trials // will override this, since it comes before the getargs() call. iterations /= 10; ntrials = 1; #endif getargs (argc, argv); std::cout << "hw threads = " << Sysutil::hardware_concurrency() << "\n"; std::cout << "threads\ttime (best of " << ntrials << ")\n"; std::cout << "-------\t----------\n"; static int threadcounts[] = { 1, 2, 4, 8, 12, 16, 20, 24, 28, 32, 64, 128, 1024, 1<<30 }; for (int i = 0; threadcounts[i] <= numthreads; ++i) { int nt = wedge ? threadcounts[i] : numthreads; int its = iterations/nt; double range; double t = time_trial (boost::bind(test_atomics,nt,its), ntrials, &range); std::cout << Strutil::format ("%2d\t%5.1f range %.2f\t(%d iters/thread)\n", nt, t, range, its); if (! wedge) break; // don't loop if we're not wedging } return unit_test_failures; } openimageio-1.7.17~dfsg0.orig/src/libutil/hashes.cpp0000644000175000017500000010315313151711064020542 0ustar mfvmfv#include /* defines printf for tests */ #include /* defines time_t for timings in the test */ #if defined(__linux__) || defined(__APPLE__) # include /* attempt to define endianness */ #endif #ifdef __linux__ # include /* attempt to define endianness */ #endif #include "OpenImageIO/platform.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/hash.h" OIIO_NAMESPACE_BEGIN namespace xxhash { /* xxHash - Fast Hash algorithm Copyright (C) 2012, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - xxHash source repository : http://code.google.com/p/xxhash/ */ //************************************** // Includes //************************************** //************************************** // Compiler Options //************************************** #ifdef _MSC_VER // Visual Studio #define inline __forceinline // Visual is not C99, but supports some kind of inline #endif // Check for support of _rotl // Not required for GCC 4.9x with -std=c++11 #ifndef _rotl #define _rotl(x,r) ((x << r) | (x >> (32 - r))) #endif //************************************** // Constants //************************************** #define PRIME1 2654435761U #define PRIME2 2246822519U #define PRIME3 3266489917U #define PRIME4 668265263U #define PRIME5 0x165667b1 //**************************** // Private functions //**************************** // This version is for very small inputs (< 16 bytes) inline unsigned int XXH_small(const void* key, int len, unsigned int seed) { const unsigned char* p = (unsigned char*)key; const unsigned char* const bEnd = p + len; unsigned int idx = seed + PRIME1; unsigned int crc = PRIME5; const unsigned char* const limit = bEnd - 4; while (p> 15; crc *= PRIME2; crc ^= crc >> 13; crc *= PRIME3; crc ^= crc >> 16; return crc; } //****************************** // Hash functions //****************************** unsigned int XXH_fast32(const void* input, int len, unsigned int seed) { // Special case, for small inputs if (len < 16) return XXH_small(input, len, seed); { const unsigned char* p = (const unsigned char*)input; const unsigned char* const bEnd = p + len; unsigned int v1 = seed + PRIME1; unsigned int v2 = v1 * PRIME2 + len; unsigned int v3 = v2 * PRIME3; unsigned int v4 = v3 * PRIME4; const unsigned char* const limit = bEnd - 16; unsigned int crc; while (p> 11; crc += (PRIME4+len) * PRIME1; crc ^= crc >> 15; crc *= PRIME2; crc ^= crc >> 13; return crc; } } unsigned int XXH_strong32(const void* input, int len, unsigned int seed) { // Special case, for small inputs if (len < 16) return XXH_small(input, len, seed); { const unsigned char* p = (const unsigned char*)input; const unsigned char* const bEnd = p + len; unsigned int v1 = seed + PRIME1; unsigned int v2 = v1 * PRIME2 + len; unsigned int v3 = v2 * PRIME3; unsigned int v4 = v3 * PRIME4; const unsigned char* const limit = bEnd - 16; unsigned int crc; while (p> 11; crc += (PRIME4+len) * PRIME1; crc ^= crc >> 15; crc *= PRIME2; crc ^= crc >> 13; return crc; } } } // end namespace xxhash namespace bjhash { /* ------------------------------------------------------------------------------- lookup3.c, by Bob Jenkins, May 2006, Public Domain. These are functions for producing 32-bit hashes for hash table lookup. hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() are externally useful functions. Routines to test the hash are included if SELF_TEST is defined. You can use this free for any purpose. It's in the public domain. It has no warranty. You probably want to use hashlittle(). hashlittle() and hashbig() hash byte arrays. hashlittle() is is faster than hashbig() on little-endian machines. Intel and AMD are little-endian machines. On second thought, you probably want hashlittle2(), which is identical to hashlittle() except it returns two 32-bit hashes for the price of one. You could implement hashbig2() if you wanted but I haven't bothered here. If you want to find a hash of, say, exactly 7 integers, do a = i1; b = i2; c = i3; mix(a,b,c); a += i4; b += i5; c += i6; mix(a,b,c); a += i7; final(a,b,c); then use c as the hash value. If you have a variable length array of 4-byte integers to hash, use hashword(). If you have a byte array (like a character string), use hashlittle(). If you have several byte arrays, or a mix of things, see the comments above hashlittle(). Why is this so big? I read 12 bytes at a time into 3 4-byte integers, then mix those integers. This is fast (you can do a lot more thorough mixing with 12*3 instructions on 3 integers than you can with 3 instructions on 1 byte), but shoehorning those bytes into integers efficiently is messy. ------------------------------------------------------------------------------- */ /* * My best guess at if you are big-endian or little-endian. This may * need adjustment. */ #if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ __BYTE_ORDER == __LITTLE_ENDIAN) || \ (defined(i386) || defined(__i386__) || defined(__i486__) || \ defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) # define HASH_LITTLE_ENDIAN 1 # define HASH_BIG_ENDIAN 0 #elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ __BYTE_ORDER == __BIG_ENDIAN) || \ (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) # define HASH_LITTLE_ENDIAN 0 # define HASH_BIG_ENDIAN 1 #else # define HASH_LITTLE_ENDIAN 0 # define HASH_BIG_ENDIAN 0 #endif #define hashsize(n) ((uint32_t)1<<(n)) #define hashmask(n) (hashsize(n)-1) #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) /* ------------------------------------------------------------------------------- mix -- mix 3 32-bit values reversibly. This is reversible, so any information in (a,b,c) before mix() is still in (a,b,c) after mix(). If four pairs of (a,b,c) inputs are run through mix(), or through mix() in reverse, there are at least 32 bits of the output that are sometimes the same for one pair and different for another pair. This was tested for: * pairs that differed by one bit, by two bits, in any combination of top bits of (a,b,c), or in any combination of bottom bits of (a,b,c). * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed the output delta to a Gray code (a^(a>>1)) so a string of 1's (as is commonly produced by subtraction) look like a single 1-bit difference. * the base values were pseudorandom, all zero but one bit set, or all zero plus a counter that starts at zero. Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that satisfy this are 4 6 8 16 19 4 9 15 3 18 27 15 14 9 3 7 17 3 Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing for "differ" defined as + with a one-bit base and a two-bit delta. I used http://burtleburtle.net/bob/hash/avalanche.html to choose the operations, constants, and arrangements of the variables. This does not achieve avalanche. There are input bits of (a,b,c) that fail to affect some output bits of (a,b,c), especially of a. The most thoroughly mixed value is c, but it doesn't really even achieve avalanche in c. This allows some parallelism. Read-after-writes are good at doubling the number of bits affected, so the goal of mixing pulls in the opposite direction as the goal of parallelism. I did what I could. Rotates seem to cost as much as shifts on every machine I could lay my hands on, and rotates are much kinder to the top and bottom bits, so I used rotates. ------------------------------------------------------------------------------- */ #define mix(a,b,c) \ { \ a -= c; a ^= rot(c, 4); c += b; \ b -= a; b ^= rot(a, 6); a += c; \ c -= b; c ^= rot(b, 8); b += a; \ a -= c; a ^= rot(c,16); c += b; \ b -= a; b ^= rot(a,19); a += c; \ c -= b; c ^= rot(b, 4); b += a; \ } /* ------------------------------------------------------------------------------- final -- final mixing of 3 32-bit values (a,b,c) into c Pairs of (a,b,c) values differing in only a few bits will usually produce values of c that look totally different. This was tested for * pairs that differed by one bit, by two bits, in any combination of top bits of (a,b,c), or in any combination of bottom bits of (a,b,c). * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed the output delta to a Gray code (a^(a>>1)) so a string of 1's (as is commonly produced by subtraction) look like a single 1-bit difference. * the base values were pseudorandom, all zero but one bit set, or all zero plus a counter that starts at zero. These constants passed: 14 11 25 16 4 14 24 12 14 25 16 4 14 24 and these came close: 4 8 15 26 3 22 24 10 8 15 26 3 22 24 11 8 15 26 3 22 24 ------------------------------------------------------------------------------- */ #define final(a,b,c) \ { \ c ^= b; c -= rot(b,14); \ a ^= c; a -= rot(c,11); \ b ^= a; b -= rot(a,25); \ c ^= b; c -= rot(b,16); \ a ^= c; a -= rot(c,4); \ b ^= a; b -= rot(a,14); \ c ^= b; c -= rot(b,24); \ } /* -------------------------------------------------------------------- This works on all machines. To be useful, it requires -- that the key be an array of uint32_t's, and -- that the length be the number of uint32_t's in the key The function hashword() is identical to hashlittle() on little-endian machines, and identical to hashbig() on big-endian machines, except that the length has to be measured in uint32_ts rather than in bytes. hashlittle() is more complicated than hashword() only because hashlittle() has to dance around fitting the key bytes into registers. -------------------------------------------------------------------- */ uint32_t hashword( const uint32_t *k, /* the key, an array of uint32_t values */ size_t length, /* the length of the key, in uint32_ts */ uint32_t initval) /* the previous hash, or an arbitrary value */ { uint32_t a,b,c; /* Set up the internal state */ a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval; /*------------------------------------------------- handle most of the key */ while (length > 3) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 3; k += 3; } /*------------------------------------------- handle the last 3 uint32_t's */ switch(length) /* all the case statements fall through */ { case 3 : c+=k[2]; case 2 : b+=k[1]; case 1 : a+=k[0]; final(a,b,c); case 0: /* case 0: nothing left to add */ break; } /*------------------------------------------------------ report the result */ return c; } /* -------------------------------------------------------------------- hashword2() -- same as hashword(), but take two seeds and return two 32-bit values. pc and pb must both be nonnull, and *pc and *pb must both be initialized with seeds. If you pass in (*pb)==0, the output (*pc) will be the same as the return value from hashword(). -------------------------------------------------------------------- */ void hashword2 ( const uint32_t *k, /* the key, an array of uint32_t values */ size_t length, /* the length of the key, in uint32_ts */ uint32_t *pc, /* IN: seed OUT: primary hash value */ uint32_t *pb) /* IN: more seed OUT: secondary hash value */ { uint32_t a,b,c; /* Set up the internal state */ a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc; c += *pb; /*------------------------------------------------- handle most of the key */ while (length > 3) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 3; k += 3; } /*------------------------------------------- handle the last 3 uint32_t's */ switch(length) /* all the case statements fall through */ { case 3 : c+=k[2]; case 2 : b+=k[1]; case 1 : a+=k[0]; final(a,b,c); case 0: /* case 0: nothing left to add */ break; } /*------------------------------------------------------ report the result */ *pc=c; *pb=b; } /* ------------------------------------------------------------------------------- hashlittle() -- hash a variable-length key into a 32-bit value k : the key (the unaligned variable-length array of bytes) length : the length of the key, counting by bytes initval : can be any 4-byte value Returns a 32-bit value. Every bit of the key affects every bit of the return value. Two keys differing by one or two bits will have totally different hash values. The best hash table sizes are powers of 2. There is no need to do mod a prime (mod is sooo slow!). If you need less than 32 bits, use a bitmask. For example, if you need only 10 bits, do h = (h & hashmask(10)); In which case, the hash table should have hashsize(10) elements. If you are hashing n strings (uint8_t **)k, do it like this: for (i=0, h=0; i 12) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 12; k += 3; } /*----------------------------- handle the last (probably partial) block */ /* * "k[2]&0xffffff" actually reads beyond the end of the string, but * then masks off the part it's not allowed to read. Because the * string is aligned, the masked-off tail is in the same word as the * rest of the string. Every machine with memory protection I've seen * does it on word boundaries, so is OK with this. But VALGRIND will * still catch it and complain. The masking trick does make the hash * noticably faster for short strings (like English words). */ #ifndef VALGRIND switch(length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=k[1]&0xffffff; a+=k[0]; break; case 6 : b+=k[1]&0xffff; a+=k[0]; break; case 5 : b+=k[1]&0xff; a+=k[0]; break; case 4 : a+=k[0]; break; case 3 : a+=k[0]&0xffffff; break; case 2 : a+=k[0]&0xffff; break; case 1 : a+=k[0]&0xff; break; case 0 : return c; /* zero length strings require no mixing */ } #else /* make valgrind happy */ const uint8_t *k8; k8 = (const uint8_t *)k; switch(length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ case 9 : c+=k8[8]; /* fall through */ case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ case 5 : b+=k8[4]; /* fall through */ case 4 : a+=k[0]; break; case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ case 1 : a+=k8[0]; break; case 0 : return c; } #endif /* !valgrind */ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ const uint8_t *k8; /*--------------- all but last block: aligned reads and different mixing */ while (length > 12) { a += k[0] + (((uint32_t)k[1])<<16); b += k[2] + (((uint32_t)k[3])<<16); c += k[4] + (((uint32_t)k[5])<<16); mix(a,b,c); length -= 12; k += 6; } /*----------------------------- handle the last (probably partial) block */ k8 = (const uint8_t *)k; switch(length) { case 12: c+=k[4]+(((uint32_t)k[5])<<16); b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ case 10: c+=k[4]; b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 9 : c+=k8[8]; /* fall through */ case 8 : b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ case 6 : b+=k[2]; a+=k[0]+(((uint32_t)k[1])<<16); break; case 5 : b+=k8[4]; /* fall through */ case 4 : a+=k[0]+(((uint32_t)k[1])<<16); break; case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ case 2 : a+=k[0]; break; case 1 : a+=k8[0]; break; case 0 : return c; /* zero length requires no mixing */ } } else { /* need to read the key one byte at a time */ const uint8_t *k = (const uint8_t *)key; /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ while (length > 12) { a += k[0]; a += ((uint32_t)k[1])<<8; a += ((uint32_t)k[2])<<16; a += ((uint32_t)k[3])<<24; b += k[4]; b += ((uint32_t)k[5])<<8; b += ((uint32_t)k[6])<<16; b += ((uint32_t)k[7])<<24; c += k[8]; c += ((uint32_t)k[9])<<8; c += ((uint32_t)k[10])<<16; c += ((uint32_t)k[11])<<24; mix(a,b,c); length -= 12; k += 12; } /*-------------------------------- last block: affect all 32 bits of (c) */ switch(length) /* all the case statements fall through */ { case 12: c+=((uint32_t)k[11])<<24; case 11: c+=((uint32_t)k[10])<<16; case 10: c+=((uint32_t)k[9])<<8; case 9 : c+=k[8]; case 8 : b+=((uint32_t)k[7])<<24; case 7 : b+=((uint32_t)k[6])<<16; case 6 : b+=((uint32_t)k[5])<<8; case 5 : b+=k[4]; case 4 : a+=((uint32_t)k[3])<<24; case 3 : a+=((uint32_t)k[2])<<16; case 2 : a+=((uint32_t)k[1])<<8; case 1 : a+=k[0]; break; case 0 : return c; } } final(a,b,c); return c; } /* * hashlittle2: return 2 32-bit hash values * * This is identical to hashlittle(), except it returns two 32-bit hash * values instead of just one. This is good enough for hash table * lookup with 2^^64 buckets, or if you want a second hash if you're not * happy with the first, or if you want a probably-unique 64-bit ID for * the key. *pc is better mixed than *pb, so use *pc first. If you want * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)". */ void hashlittle2( const void *key, /* the key to hash */ size_t length, /* length of the key */ uint32_t *pc, /* IN: primary initval, OUT: primary hash */ uint32_t *pb) /* IN: secondary initval, OUT: secondary hash */ { uint32_t a,b,c; /* internal state */ union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ /* Set up the internal state */ a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc; c += *pb; u.ptr = key; if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ while (length > 12) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 12; k += 3; } /*----------------------------- handle the last (probably partial) block */ /* * "k[2]&0xffffff" actually reads beyond the end of the string, but * then masks off the part it's not allowed to read. Because the * string is aligned, the masked-off tail is in the same word as the * rest of the string. Every machine with memory protection I've seen * does it on word boundaries, so is OK with this. But VALGRIND will * still catch it and complain. The masking trick does make the hash * noticably faster for short strings (like English words). */ #ifndef VALGRIND switch(length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=k[1]&0xffffff; a+=k[0]; break; case 6 : b+=k[1]&0xffff; a+=k[0]; break; case 5 : b+=k[1]&0xff; a+=k[0]; break; case 4 : a+=k[0]; break; case 3 : a+=k[0]&0xffffff; break; case 2 : a+=k[0]&0xffff; break; case 1 : a+=k[0]&0xff; break; case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ } #else /* make valgrind happy */ const uint8_t *k8; k8 = (const uint8_t *)k; switch(length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ case 9 : c+=k8[8]; /* fall through */ case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ case 5 : b+=k8[4]; /* fall through */ case 4 : a+=k[0]; break; case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ case 1 : a+=k8[0]; break; case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ } #endif /* !valgrind */ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ const uint8_t *k8; /*--------------- all but last block: aligned reads and different mixing */ while (length > 12) { a += k[0] + (((uint32_t)k[1])<<16); b += k[2] + (((uint32_t)k[3])<<16); c += k[4] + (((uint32_t)k[5])<<16); mix(a,b,c); length -= 12; k += 6; } /*----------------------------- handle the last (probably partial) block */ k8 = (const uint8_t *)k; switch(length) { case 12: c+=k[4]+(((uint32_t)k[5])<<16); b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ case 10: c+=k[4]; b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 9 : c+=k8[8]; /* fall through */ case 8 : b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ case 6 : b+=k[2]; a+=k[0]+(((uint32_t)k[1])<<16); break; case 5 : b+=k8[4]; /* fall through */ case 4 : a+=k[0]+(((uint32_t)k[1])<<16); break; case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ case 2 : a+=k[0]; break; case 1 : a+=k8[0]; break; case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ } } else { /* need to read the key one byte at a time */ const uint8_t *k = (const uint8_t *)key; /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ while (length > 12) { a += k[0]; a += ((uint32_t)k[1])<<8; a += ((uint32_t)k[2])<<16; a += ((uint32_t)k[3])<<24; b += k[4]; b += ((uint32_t)k[5])<<8; b += ((uint32_t)k[6])<<16; b += ((uint32_t)k[7])<<24; c += k[8]; c += ((uint32_t)k[9])<<8; c += ((uint32_t)k[10])<<16; c += ((uint32_t)k[11])<<24; mix(a,b,c); length -= 12; k += 12; } /*-------------------------------- last block: affect all 32 bits of (c) */ switch(length) /* all the case statements fall through */ { case 12: c+=((uint32_t)k[11])<<24; case 11: c+=((uint32_t)k[10])<<16; case 10: c+=((uint32_t)k[9])<<8; case 9 : c+=k[8]; case 8 : b+=((uint32_t)k[7])<<24; case 7 : b+=((uint32_t)k[6])<<16; case 6 : b+=((uint32_t)k[5])<<8; case 5 : b+=k[4]; case 4 : a+=((uint32_t)k[3])<<24; case 3 : a+=((uint32_t)k[2])<<16; case 2 : a+=((uint32_t)k[1])<<8; case 1 : a+=k[0]; break; case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ } } final(a,b,c); *pc=c; *pb=b; } /* * hashbig(): * This is the same as hashword() on big-endian machines. It is different * from hashlittle() on all machines. hashbig() takes advantage of * big-endian byte ordering. */ uint32_t hashbig( const void *key, size_t length, uint32_t initval) { uint32_t a,b,c; union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */ /* Set up the internal state */ a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; u.ptr = key; if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) { const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ while (length > 12) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 12; k += 3; } /*----------------------------- handle the last (probably partial) block */ /* * "k[2]<<8" actually reads beyond the end of the string, but * then shifts out the part it's not allowed to read. Because the * string is aligned, the illegal read is in the same word as the * rest of the string. Every machine with memory protection I've seen * does it on word boundaries, so is OK with this. But VALGRIND will * still catch it and complain. The masking trick does make the hash * noticably faster for short strings (like English words). */ #ifndef VALGRIND switch(length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break; case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break; case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break; case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=k[1]&0xffffff00; a+=k[0]; break; case 6 : b+=k[1]&0xffff0000; a+=k[0]; break; case 5 : b+=k[1]&0xff000000; a+=k[0]; break; case 4 : a+=k[0]; break; case 3 : a+=k[0]&0xffffff00; break; case 2 : a+=k[0]&0xffff0000; break; case 1 : a+=k[0]&0xff000000; break; case 0 : return c; /* zero length strings require no mixing */ } #else /* make valgrind happy */ const uint8_t *k8; k8 = (const uint8_t *)k; switch(length) /* all the case statements fall through */ { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=((uint32_t)k8[10])<<8; /* fall through */ case 10: c+=((uint32_t)k8[9])<<16; /* fall through */ case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */ case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */ case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */ case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */ case 4 : a+=k[0]; break; case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */ case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */ case 1 : a+=((uint32_t)k8[0])<<24; break; case 0 : return c; } #endif /* !VALGRIND */ } else { /* need to read the key one byte at a time */ const uint8_t *k = (const uint8_t *)key; /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ while (length > 12) { a += ((uint32_t)k[0])<<24; a += ((uint32_t)k[1])<<16; a += ((uint32_t)k[2])<<8; a += ((uint32_t)k[3]); b += ((uint32_t)k[4])<<24; b += ((uint32_t)k[5])<<16; b += ((uint32_t)k[6])<<8; b += ((uint32_t)k[7]); c += ((uint32_t)k[8])<<24; c += ((uint32_t)k[9])<<16; c += ((uint32_t)k[10])<<8; c += ((uint32_t)k[11]); mix(a,b,c); length -= 12; k += 12; } /*-------------------------------- last block: affect all 32 bits of (c) */ switch(length) /* all the case statements fall through */ { case 12: c+=k[11]; case 11: c+=((uint32_t)k[10])<<8; case 10: c+=((uint32_t)k[9])<<16; case 9 : c+=((uint32_t)k[8])<<24; case 8 : b+=k[7]; case 7 : b+=((uint32_t)k[6])<<8; case 6 : b+=((uint32_t)k[5])<<16; case 5 : b+=((uint32_t)k[4])<<24; case 4 : a+=k[3]; case 3 : a+=((uint32_t)k[2])<<8; case 2 : a+=((uint32_t)k[1])<<16; case 1 : a+=((uint32_t)k[0])<<24; break; case 0 : return c; } } final(a,b,c); return c; } } // end namespace bjhash OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libutil/filter.cpp0000644000175000017500000006572113151711064020564 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Based on BSD-licensed software Copyright 2004 NVIDIA Corp. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ // Filter results comparison // pow2 downres (4K -> 128 tested) // - katana 2.7.12 // - ImageMagick 6.3.6 // - prman 16.0 txmake // // box: oiio, prman, katana, imagemagick match // lanczos3: oiio, katana, imagemagick match. prman is far sharper (perhaps lanczos2?) // sinc: oiio, prman match. Katana is slighly softer. imagemagick is much softer // blackman harris: all differ. In order of decreasing sharpness... imagemagick, oiio, prman // catrom: oiio, imagemagick, prman match // gaussian: prman, katana match (gaussian3). imgmagick, oiio are sharper (gaussian2) #include #include #include #include #include "OpenImageIO/fmath.h" #include "OpenImageIO/filter.h" #include "OpenImageIO/dassert.h" OIIO_NAMESPACE_BEGIN // Below are the implementations of several 2D filters. They all // inherit their interface from Filter2D. Each must redefine two // virtual functions: // char *name() Return the filter name // float operator(float,float) Evaluate the filter // class FilterBox1D : public Filter1D { public: FilterBox1D (float width) : Filter1D(width) { } ~FilterBox1D (void) { } float operator() (float x) const { return (fabsf(x) <= m_w*0.5f) ? 1.0f : 0.0f; } string_view name (void) const { return "box"; } }; class FilterBox2D : public Filter2D { public: FilterBox2D (float width, float height) : Filter2D(width,height) { } ~FilterBox2D (void) { } float operator() (float x, float y) const { return (fabsf(x) <= m_w*0.5f && fabsf(y) <= m_h*0.5f) ? 1.0f : 0.0f; } bool separable (void) const { return true; } float xfilt (float x) const { return fabsf(x) <= m_w*0.5f ? 1.0f : 0.0f; } float yfilt (float y) const { return fabsf(y) <= m_h*0.5f ? 1.0f : 0.0f; } string_view name (void) const { return "box"; } }; class FilterTriangle1D : public Filter1D { public: FilterTriangle1D (float width) : Filter1D(width), m_rad_inv(2.0f/width) { } ~FilterTriangle1D (void) { } float operator() (float x) const { return tri1d (x * m_rad_inv); } string_view name (void) const { return "triangle"; } static float tri1d (float x) { x = fabsf(x); return (x < 1.0f) ? (1.0f - x) : 0.0f; } private: float m_rad_inv; }; class FilterTriangle2D : public Filter2D { public: FilterTriangle2D (float width, float height) : Filter2D(width,height), m_wrad_inv(2.0f/width), m_hrad_inv(2.0f/height) { } ~FilterTriangle2D (void) { } float operator() (float x, float y) const { return FilterTriangle1D::tri1d (x * m_wrad_inv) * FilterTriangle1D::tri1d (y * m_hrad_inv); } bool separable (void) const { return true; } float xfilt (float x) const { return FilterTriangle1D::tri1d (x * m_wrad_inv); } float yfilt (float y) const { return FilterTriangle1D::tri1d (y * m_hrad_inv); } string_view name (void) const { return "triangle"; } private: float m_wrad_inv, m_hrad_inv; }; class FilterGaussian1D : public Filter1D { public: FilterGaussian1D (float width) : Filter1D(width), m_rad_inv(2.0f/width) { } ~FilterGaussian1D (void) { } float operator() (float x) const { return gauss1d (x * m_rad_inv); } static float gauss1d (float x) { x = fabsf(x); return (x < 1.0f) ? fast_exp (-2.0f * (x*x)) : 0.0f; } string_view name (void) const { return "gaussian"; } private: float m_rad_inv; }; class FilterGaussian2D : public Filter2D { public: FilterGaussian2D (float width, float height) : Filter2D(width,height), m_wrad_inv(2.0f/width), m_hrad_inv(2.0f/height) { } ~FilterGaussian2D (void) { } float operator() (float x, float y) const { return FilterGaussian1D::gauss1d (x * m_wrad_inv) * FilterGaussian1D::gauss1d (y * m_hrad_inv); } bool separable (void) const { return true; } float xfilt (float x) const { return FilterGaussian1D::gauss1d (x * m_wrad_inv); } float yfilt (float y) const { return FilterGaussian1D::gauss1d (y * m_hrad_inv); } string_view name (void) const { return "gaussian"; } private: float m_wrad_inv, m_hrad_inv; }; class FilterSharpGaussian1D : public Filter1D { public: FilterSharpGaussian1D (float width) : Filter1D(width), m_rad_inv(2.0f/width) { } ~FilterSharpGaussian1D (void) { } float operator() (float x) const { return gauss1d (x * m_rad_inv); } static float gauss1d (float x) { x = fabsf(x); return (x < 1.0f) ? fast_exp (-4.0f * (x*x)) : 0.0f; } string_view name (void) const { return "gaussian"; } private: float m_rad_inv; }; class FilterSharpGaussian2D : public Filter2D { public: FilterSharpGaussian2D (float width, float height) : Filter2D(width,height), m_wrad_inv(2.0f/width), m_hrad_inv(2.0f/height) { } ~FilterSharpGaussian2D (void) { } float operator() (float x, float y) const { return FilterSharpGaussian1D::gauss1d (x * m_wrad_inv) * FilterSharpGaussian1D::gauss1d (y * m_hrad_inv); } bool separable (void) const { return true; } float xfilt (float x) const { return FilterSharpGaussian1D::gauss1d (x * m_wrad_inv); } float yfilt (float y) const { return FilterSharpGaussian1D::gauss1d (y * m_hrad_inv); } string_view name (void) const { return "gaussian"; } private: float m_wrad_inv, m_hrad_inv; }; class FilterCatmullRom1D : public Filter1D { public: FilterCatmullRom1D (float width) : Filter1D(4.0f), m_scale(4.0f/width) { } ~FilterCatmullRom1D (void) { } float operator() (float x) const { return catrom1d(x * m_scale); } string_view name (void) const { return "catmull-rom"; } static float catrom1d (float x) { x = fabsf(x); float x2 = x * x; float x3 = x * x2; return (x >= 2.0f) ? 0.0f : ((x < 1.0f) ? (3.0f * x3 - 5.0f * x2 + 2.0f) : (-x3 + 5.0f * x2 - 8.0f * x + 4.0f) ); } private: float m_scale; }; class FilterCatmullRom2D : public Filter2D { public: FilterCatmullRom2D (float width, float height) : Filter2D(width,height), m_wscale(4.0f/width), m_hscale(4.0f/height) { } ~FilterCatmullRom2D (void) { } float operator() (float x, float y) const { return FilterCatmullRom1D::catrom1d(x * m_wscale) * FilterCatmullRom1D::catrom1d(y * m_hscale); } bool separable (void) const { return true; } float xfilt (float x) const { return FilterCatmullRom1D::catrom1d(x * m_wscale); } float yfilt (float y) const { return FilterCatmullRom1D::catrom1d(y * m_hscale); } string_view name (void) const { return "catmull-rom"; } private: float m_wscale, m_hscale; }; class FilterBlackmanHarris1D : public Filter1D { public: FilterBlackmanHarris1D (float width) : Filter1D(width), m_rad_inv(2.0f/width) { } ~FilterBlackmanHarris1D (void) { } float operator() (float x) const { return bh1d (x * m_rad_inv); } string_view name (void) const { return "blackman-harris"; } static float bh1d (float x) { if (x < -1.0f || x > 1.0f) // Early out if outside filter range return 0.0f; // Compute BH. Straight from classic BH paper, but the usual // formula assumes that the filter is centered at 0.5, so scale: x = (x + 1.0f) * 0.5f; const float A0 = 0.35875f; const float A1 = -0.48829f; const float A2 = 0.14128f; const float A3 = -0.01168f; const float m_pi = float (M_PI); #if 0 // original -- three cos calls! return A0 + A1 * cosf(2.f * m_pi * x) + A2 * cosf(4.f * m_pi * x) + A3 * cosf(6.f * m_pi * x); #else // Use trig identintities to reduce to just one cos. // https://en.wikipedia.org/wiki/List_of_trigonometric_identities // cos(2x) = 2 cos^2(x) - 1 // cos(3x) = 4 cos^3(x) − 3 cos(x) float cos2pix = cosf(2.f * m_pi * x); float cos4pix = 2.0f * cos2pix * cos2pix - 1.0f; float cos6pix = cos2pix * (2.0f * cos4pix - 1.0f); return A0 + A1 * cos2pix + A2 * cos4pix + A3 * cos6pix; #endif } private: float m_rad_inv; }; class FilterBlackmanHarris2D : public Filter2D { public: FilterBlackmanHarris2D (float width, float height) : Filter2D(width,height), m_wrad_inv(2.0f/width), m_hrad_inv(2.0f/height) { } ~FilterBlackmanHarris2D (void) { } float operator() (float x, float y) const { return FilterBlackmanHarris1D::bh1d (x*m_wrad_inv) * FilterBlackmanHarris1D::bh1d (y*m_hrad_inv); } bool separable (void) const { return true; } float xfilt (float x) const { return FilterBlackmanHarris1D::bh1d(x*m_wrad_inv); } float yfilt (float y) const { return FilterBlackmanHarris1D::bh1d(y*m_hrad_inv); } string_view name (void) const { return "blackman-harris"; } private: float m_wrad_inv, m_hrad_inv; }; class FilterSinc1D : public Filter1D { public: FilterSinc1D (float width) : Filter1D(width), m_rad(width/2.0f) { } ~FilterSinc1D (void) { } float operator() (float x) const { return sinc1d (x, m_rad); } string_view name (void) const { return "sinc"; } static float sinc1d (float x, float rad) { x = fabsf(x); if (x > rad) return 0.0f; const float m_pi = float (M_PI); return (x < 0.0001f) ? 1.0f : sinf (m_pi*x)/(m_pi*x); } private: float m_rad; }; class FilterSinc2D : public Filter2D { public: FilterSinc2D (float width, float height) : Filter2D(width,height), m_wrad(width/2.0f), m_hrad(height/2.0f) { } ~FilterSinc2D (void) { } float operator() (float x, float y) const { return FilterSinc1D::sinc1d(x,m_wrad) * FilterSinc1D::sinc1d(y,m_hrad); } bool separable (void) const { return true; } float xfilt (float x) const { return FilterSinc1D::sinc1d(x,m_wrad); } float yfilt (float y) const { return FilterSinc1D::sinc1d(y,m_hrad); } string_view name (void) const { return "sinc"; } private: float m_wrad, m_hrad; }; class FilterLanczos3_1D : public Filter1D { public: FilterLanczos3_1D (float width) : Filter1D(width), m_scale(6.0f/width) { } ~FilterLanczos3_1D (void) { } float operator() (float x) const { return lanczos3 (x * m_scale); } string_view name (void) const { return "lanczos3"; } static float lanczos3 (float x) { const float a = 3.0f; // Lanczos 3 lobe const float ainv = 1.0f / a; const float m_pi = float (M_PI); x = fabsf(x); if (x > a) return 0.0f; if (x < 0.0001f) return 1.0f; #if 0 // Full precision, for reference: float pix = m_pi * x; return a/(x*x*(m_pi*m_pi)) * sinf(pix)*sinf(pix*ainv); #elif 0 // Use approximate fast_sinphi -- about 0.1% absolute error, but // around 2.5x times faster. BUT when graphed, looks very icky near // f(0). return a/(x*x*(m_pi*m_pi)) * fast_sinpi(x)*fast_sinpi(x*ainv); #else // Compromise: full-precision sin(), but use the trig identity // sin(3x) = -4 sin^3(x) + 3 sin(x) // to make it so only one call to sin is sufficient. This is still // about 1.5x the speed of the reference implementation, but with // no precision compromises. float s1 = sinf(x*ainv*m_pi); // sin(x*pi/a) float s3 = (-4.0f * s1*s1 + 3.0f) * s1; // sin(3*x*pi/a) == sin(x*pi) return a/(x*x*(m_pi*m_pi)) * s1 * s3; #endif } private: float m_scale; }; class FilterLanczos3_2D : public Filter2D { public: FilterLanczos3_2D (float width, float height) : Filter2D(width,height), m_wscale(6.0f/width), m_hscale(6.0f/height) { } ~FilterLanczos3_2D (void) { } float operator() (float x, float y) const { return FilterLanczos3_1D::lanczos3 (x * m_wscale) * FilterLanczos3_1D::lanczos3 (y * m_hscale); } bool separable (void) const { return true; } float xfilt (float x) const { return FilterLanczos3_1D::lanczos3(x * m_wscale); } float yfilt (float y) const { return FilterLanczos3_1D::lanczos3(y * m_hscale); } string_view name (void) const { return "lanczos3"; } protected: float m_wscale, m_hscale; }; class FilterRadialLanczos3_2D : public FilterLanczos3_2D { public: FilterRadialLanczos3_2D (float width, float height) : FilterLanczos3_2D(width,height) { } float operator() (float x, float y) const { x *= m_wscale; y *= m_hscale; return FilterLanczos3_1D::lanczos3(sqrtf(x*x + y*y)); } bool separable (void) const { return false; } string_view name (void) const { return "radial-lanczos3"; } }; class FilterMitchell1D : public Filter1D { public: FilterMitchell1D (float width) : Filter1D(width), m_rad_inv(2.0f/width) { } ~FilterMitchell1D (void) { } float operator() (float x) const { return mitchell1d (x * m_rad_inv); } string_view name (void) const { return "mitchell"; } static float mitchell1d (float x) { x = fabsf (x); if (x > 1.0f) return 0.0f; // Computation stright out of the classic Mitchell paper. // In the paper, the range is -2 to 2, so we rescale: x *= 2.0f; float x2 = x*x; const float B = 1.0f/3.0f; const float C = 1.0f/3.0f; const float SIXTH = 1.0f/6.0f; if (x >= 1.0f) return ((-B - 6.0f*C)*x*x2 + (6.0f*B + 30.0f*C)*x2 + (-12.0f*B - 48.0f*C)*x + (8.0f*B + 24.0f*C)) * SIXTH; else return ((12.0f - 9.0f*B - 6.0f*C)*x*x2 + (-18.0f + 12.0f*B + 6.0f*C)*x2 + (6.0f - 2.0f*B)) * SIXTH; } private: float m_rad_inv; }; class FilterMitchell2D : public Filter2D { public: FilterMitchell2D (float width, float height) : Filter2D(width,height), m_wrad_inv(2.0f/width), m_hrad_inv(2.0f/height) { } ~FilterMitchell2D (void) { } float operator() (float x, float y) const { return FilterMitchell1D::mitchell1d (x * m_wrad_inv) * FilterMitchell1D::mitchell1d (y * m_hrad_inv); } bool separable (void) const { return true; } float xfilt (float x) const { return FilterMitchell1D::mitchell1d (x * m_wrad_inv); } float yfilt (float y) const { return FilterMitchell1D::mitchell1d (y * m_hrad_inv); } string_view name (void) const { return "mitchell"; } private: float m_wrad_inv, m_hrad_inv; }; // B-spline filter from Stark et al, JGT 10(1) class FilterBSpline1D : public Filter1D { public: FilterBSpline1D (float width) : Filter1D(width), m_wscale(4.0f/width) { } ~FilterBSpline1D (void) { } float operator() (float x) const { return bspline1d (x*m_wscale); } string_view name (void) const { return "b-spline"; } static float bspline1d (float x) { x = fabsf (x); if (x <= 1.0f) return b1 (1.0f-x); else if (x < 2.0f) return b0 (2.0f-x); else return 0.0f; } private: float m_wscale; // width scale factor static float b0 (float t) { return t*t*t / 6.0f; } static float b1 (float t) { return 0.5f * t * (t * (1.0f - t) + 1.0f) + 1.0f/6.0f; } }; class FilterBSpline2D : public Filter2D { public: FilterBSpline2D (float width, float height) : Filter2D(width,height), m_wscale(4.0f/width), m_hscale(4.0f/height) { } ~FilterBSpline2D (void) { } float operator() (float x, float y) const { return FilterBSpline1D::bspline1d (x * m_wscale) * FilterBSpline1D::bspline1d (y * m_hscale); } bool separable (void) const { return true; } float xfilt (float x) const { return FilterBSpline1D::bspline1d(x*m_wscale); } float yfilt (float y) const { return FilterBSpline1D::bspline1d(y*m_hscale); } string_view name (void) const { return "b-spline"; } private: float m_wscale, m_hscale; }; class FilterDisk2D : public Filter2D { public: FilterDisk2D (float width, float height) : Filter2D(width,height) { } ~FilterDisk2D (void) { } float operator() (float x, float y) const { x /= (m_w*0.5f); y /= (m_h*0.5f); return ((x*x+y*y) < 1.0f) ? 1.0f : 0.0f; } string_view name (void) const { return "disk"; } }; class FilterCubic1D : public Filter1D { public: FilterCubic1D (float width) : Filter1D(width), m_a(0.0f), m_rad_inv(2.0f / width) { } ~FilterCubic1D (void) { } float operator() (float x) const { return cubic(x * m_rad_inv, m_a); } static float cubic (float x, float a) { x = fabsf (x); if (x > 1.0f) return 0.0f; // Because range is -2 to 2, we rescale x *= 2.0f; if (x >= 1.0f) return a * (x * (x * (x - 5.0f) + 8.0f) - 4.0f); // return a * x*x*x - 5.0f * a * x*x + 8.0f * a * x - 4.0f * a; else return x*x*((a + 2.0f) * x - (a + 3.0f)) + 1.0f; // return (a + 2.0f) * x*x*x - (a + 3.0f) * x*x + 1.0f; } virtual string_view name (void) const { return "cubic"; } protected: float m_a; float m_rad_inv; }; class FilterCubic2D : public Filter2D { public: FilterCubic2D (float width, float height) : Filter2D(width,height), m_a(0.0f) , m_wrad_inv(2.0f/width), m_hrad_inv(2.0f/height) { } ~FilterCubic2D (void) { } float operator() (float x, float y) const { return FilterCubic1D::cubic(x * m_wrad_inv, m_a) * FilterCubic1D::cubic(y * m_hrad_inv, m_a); } bool separable (void) const { return true; } float xfilt (float x) const { return FilterCubic1D::cubic (x * m_wrad_inv, m_a); } float yfilt (float y) const { return FilterCubic1D::cubic (y * m_hrad_inv, m_a); } virtual string_view name (void) const { return "cubic"; } protected: float m_a; float m_wrad_inv, m_hrad_inv; }; class FilterKeys1D : public FilterCubic1D { public: FilterKeys1D (float width) : FilterCubic1D(width) { m_a = -0.5f; } ~FilterKeys1D (void) { } virtual string_view name (void) const { return "keys"; } }; class FilterKeys2D : public FilterCubic2D { public: FilterKeys2D (float width, float height) : FilterCubic2D(width,height) { m_a = -0.5f; } ~FilterKeys2D (void) { } virtual string_view name (void) const { return "keys"; } }; class FilterSimon1D : public FilterCubic1D { public: FilterSimon1D (float width) : FilterCubic1D(width) { m_a = -0.75f; } ~FilterSimon1D (void) { } virtual string_view name (void) const { return "simon"; } }; class FilterSimon2D : public FilterCubic2D { public: FilterSimon2D (float width, float height) : FilterCubic2D(width,height) { m_a = -0.75f; } ~FilterSimon2D (void) { } virtual string_view name (void) const { return "simon"; } }; class FilterRifman1D : public FilterCubic1D { public: FilterRifman1D (float width) : FilterCubic1D(width) { m_a = -1.0f; } ~FilterRifman1D (void) { } virtual string_view name (void) const { return "rifman"; } }; class FilterRifman2D : public FilterCubic2D { public: FilterRifman2D (float width, float height) : FilterCubic2D(width,height) { m_a = -1.0f; } ~FilterRifman2D (void) { } virtual string_view name (void) const { return "rifman"; } }; namespace { FilterDesc filter1d_list[] = { // name dim width fixedwidth scalable separable { "box", 1, 1, false, true, true }, { "triangle", 1, 2, false, true, true }, { "gaussian", 1, 3, false, true, true }, { "sharp-gaussian", 1, 2, false, true, true }, { "catmull-rom", 1, 4, false, true, true }, { "blackman-harris", 1, 3, false, true, true }, { "sinc", 1, 4, false, true, true }, { "lanczos3", 1, 6, false, true, true }, { "mitchell", 1, 4, false, true, true }, { "bspline", 1, 4, false, true, true }, { "cubic", 1, 4, false, true, true }, { "keys", 1, 4, false, true, true }, { "simon", 1, 4, false, true, true }, { "rifman", 1, 4, false, true, true } }; } int Filter1D::num_filters () { return sizeof(filter1d_list)/sizeof(filter1d_list[0]); } void Filter1D::get_filterdesc (int filternum, FilterDesc *filterdesc) { ASSERT (filternum >= 0 && filternum < num_filters()); *filterdesc = filter1d_list[filternum]; } // Filter1D::create is the static method that, given a filter name, // width, and height, returns an allocated and instantiated filter of // the correct implementation. If the name is not recognized, return // NULL. Filter1D * Filter1D::create (string_view filtername, float width) { if (filtername == "box") return new FilterBox1D (width); if (filtername == "triangle") return new FilterTriangle1D (width); if (filtername == "gaussian") return new FilterGaussian1D (width); if (filtername == "sharp-gaussian") return new FilterSharpGaussian1D (width); if (filtername == "catmull-rom" || filtername == "catrom") return new FilterCatmullRom1D (width); if (filtername == "blackman-harris") return new FilterBlackmanHarris1D (width); if (filtername == "sinc") return new FilterSinc1D (width); if (filtername == "lanczos3" || filtername == "lanczos") return new FilterLanczos3_1D (width); if (filtername == "mitchell") return new FilterMitchell1D (width); if (filtername == "b-spline" || filtername == "bspline") return new FilterBSpline1D (width); if (filtername == "cubic") return new FilterCubic1D (width); if (filtername == "keys") return new FilterKeys1D (width); if (filtername == "simon") return new FilterSimon1D (width); if (filtername == "rifman") return new FilterRifman1D (width); return NULL; } void Filter1D::destroy (Filter1D *filt) { delete filt; } static FilterDesc filter2d_list[] = { // name dim width fixedwidth scalable separable { "box", 2, 1, false, true, true }, { "triangle", 2, 2, false, true, true }, { "gaussian", 2, 3, false, true, true }, { "sharp-gaussian", 2, 2, false, true, true }, { "catmull-rom", 2, 4, false, true, true }, { "blackman-harris", 2, 3, false, true, true }, { "sinc", 2, 4, false, true, true }, { "lanczos3", 2, 6, false, true, true }, { "radial-lanczos3", 2, 6, false, true, false }, { "mitchell", 2, 4, false, true, true }, { "bspline", 2, 4, false, true, true }, { "disk", 2, 1, false, true, false }, { "cubic", 2, 4, false, true, true }, { "keys", 2, 4, false, true, true }, { "simon", 2, 4, false, true, true }, { "rifman", 2, 4, false, true, true } }; int Filter2D::num_filters () { return sizeof(filter2d_list)/sizeof(filter2d_list[0]); } void Filter2D::get_filterdesc (int filternum, FilterDesc *filterdesc) { ASSERT (filternum >= 0 && filternum < num_filters()); *filterdesc = filter2d_list[filternum]; } // Filter2D::create is the static method that, given a filter name, // width, and height, returns an allocated and instantiated filter of // the correct implementation. If the name is not recognized, return // NULL. Filter2D * Filter2D::create (string_view filtername, float width, float height) { if (filtername == "box") return new FilterBox2D (width, height); if (filtername == "triangle") return new FilterTriangle2D (width, height); if (filtername == "gaussian") return new FilterGaussian2D (width, height); if (filtername == "sharp-gaussian") return new FilterSharpGaussian2D (width, height); if (filtername == "catmull-rom" || filtername == "catrom") return new FilterCatmullRom2D (width, height); if (filtername == "blackman-harris") return new FilterBlackmanHarris2D (width, height); if (filtername == "sinc") return new FilterSinc2D (width, height); if (filtername == "lanczos3" || filtername == "lanczos") return new FilterLanczos3_2D (width, height); if (filtername == "radial-lanczos3" || filtername == "radial-lanczos") return new FilterRadialLanczos3_2D (width, height); if (filtername == "mitchell") return new FilterMitchell2D (width, height); if (filtername == "b-spline" || filtername == "bspline") return new FilterBSpline2D (width, height); if (filtername == "disk") return new FilterDisk2D (width, height); if (filtername == "cubic") return new FilterCubic2D (width, height); if (filtername == "keys") return new FilterKeys2D (width, height); if (filtername == "simon") return new FilterSimon2D (width, height); if (filtername == "rifman") return new FilterRifman2D (width, height); return NULL; } void Filter2D::destroy (Filter2D *filt) { delete filt; } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libutil/CMakeLists.txt0000644000175000017500000001124513151711064021323 0ustar mfvmfvset (libOpenImageIO_Util_srcs argparse.cpp errorhandler.cpp filesystem.cpp farmhash.cpp filter.cpp hashes.cpp paramlist.cpp plugin.cpp SHA1.cpp strutil.cpp sysutil.cpp timer.cpp typedesc.cpp ustring.cpp xxhash.cpp) if (BUILDSTATIC) add_library (OpenImageIO_Util STATIC ${libOpenImageIO_Util_srcs}) else () add_library (OpenImageIO_Util SHARED ${libOpenImageIO_Util_srcs}) endif () target_link_libraries (OpenImageIO_Util ${VISIBILITY_COMMAND} ${VISIBILITY_MAP_COMMAND} ${Boost_LIBRARIES}) if (WIN32) target_link_libraries (OpenImageIO_Util psapi.lib) endif () target_link_libraries (OpenImageIO_Util ${ILMBASE_LIBRARIES}) if (VERBOSE) message(STATUS "Setting SOVERSION to: ${SOVERSION}") endif () set_target_properties(OpenImageIO_Util PROPERTIES VERSION ${OIIO_VERSION_MAJOR}.${OIIO_VERSION_MINOR}.${OIIO_VERSION_PATCH} SOVERSION ${SOVERSION} ) # For consistency with the linux SpComp2s, create Mac OS X SpComp2s # with a .so suffix instead of a .dylib suffix. if (DEFINED OVERRIDE_SHARED_LIBRARY_SUFFIX) set_target_properties (OpenImageIO_Util PROPERTIES SUFFIX ${OVERRIDE_SHARED_LIBRARY_SUFFIX}) endif () oiio_install_targets (OpenImageIO_Util) if (OIIO_BUILD_TESTS) add_executable (atomic_test atomic_test.cpp) set_target_properties (atomic_test PROPERTIES FOLDER "Unit Tests") target_link_libraries (atomic_test OpenImageIO_Util ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) add_test (unit_atomic atomic_test) add_executable (array_view_test array_view_test.cpp) set_target_properties (array_view_test PROPERTIES FOLDER "Unit Tests") target_link_libraries (array_view_test OpenImageIO_Util ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) add_test (unit_array_view array_view_test) add_executable (spinlock_test spinlock_test.cpp) set_target_properties (spinlock_test PROPERTIES FOLDER "Unit Tests") target_link_libraries (spinlock_test OpenImageIO_Util ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) add_test (unit_spinlock spinlock_test) add_executable (spin_rw_test spin_rw_test.cpp) set_target_properties (spin_rw_test PROPERTIES FOLDER "Unit Tests") target_link_libraries (spin_rw_test OpenImageIO_Util ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) add_test (unit_spin_rw spin_rw_test) add_executable (ustring_test ustring_test.cpp) set_target_properties (ustring_test PROPERTIES FOLDER "Unit Tests") target_link_libraries (ustring_test OpenImageIO_Util ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) add_test (unit_ustring ustring_test) add_executable (strutil_test strutil_test.cpp) set_target_properties (strutil_test PROPERTIES FOLDER "Unit Tests") target_link_libraries (strutil_test OpenImageIO_Util ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) add_test (unit_strutil strutil_test) add_executable (fmath_test fmath_test.cpp) set_target_properties (fmath_test PROPERTIES FOLDER "Unit Tests") target_link_libraries (fmath_test OpenImageIO_Util ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) add_test (unit_fmath fmath_test) add_executable (filesystem_test filesystem_test.cpp) set_target_properties (filesystem_test PROPERTIES FOLDER "Unit Tests") target_link_libraries (filesystem_test OpenImageIO_Util ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) add_test (unit_filesystem filesystem_test) add_executable (optparser_test optparser_test.cpp) set_target_properties (optparser_test PROPERTIES FOLDER "Unit Tests") target_link_libraries (optparser_test OpenImageIO_Util ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) add_test (unit_optparser optparser_test) add_executable (hash_test hash_test.cpp) set_target_properties (hash_test PROPERTIES FOLDER "Unit Tests") target_link_libraries (hash_test OpenImageIO_Util ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) add_test (unit_hash hash_test) add_executable (timer_test timer_test.cpp) set_target_properties (timer_test PROPERTIES FOLDER "Unit Tests") target_link_libraries (timer_test OpenImageIO_Util ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) add_test (unit_timer timer_test) add_executable (simd_test simd_test.cpp) set_target_properties (simd_test PROPERTIES FOLDER "Unit Tests") target_link_libraries (simd_test OpenImageIO_Util ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) add_test (unit_simd simd_test) add_executable (filter_test filter_test.cpp) set_target_properties (filter_test PROPERTIES FOLDER "Unit Tests") target_link_libraries (filter_test OpenImageIO ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) add_test (unit_filter filter_test) endif () openimageio-1.7.17~dfsg0.orig/src/libutil/ustring_test.cpp0000644000175000017500000001204113151711064022014 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include "OpenImageIO/thread.h" #include "OpenImageIO/ustring.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/sysutil.h" #include "OpenImageIO/timer.h" #include "OpenImageIO/argparse.h" #include "OpenImageIO/unittest.h" #include OIIO_NAMESPACE_USING; // Test ustring's internal locks by creating a bunch of strings in many // threads simultaneously. Hopefully something will crash if the // internal table is not being locked properly. static int iterations = 1000000; static int numthreads = 16; static int ntrials = 1; static bool verbose = false; static bool wedge = false; static spin_mutex print_mutex; // make the prints not clobber each other static void create_lotso_ustrings (int iterations) { if (verbose) { spin_lock lock(print_mutex); std::cout << "thread " << boost::this_thread::get_id() << "\n"; } for (int i = 0; i < iterations; ++i) { char buf[20]; sprintf (buf, "%d", i); ustring s (buf); } } void test_ustring_lock (int numthreads, int iterations) { boost::thread_group threads; for (int i = 0; i < numthreads; ++i) { threads.create_thread (boost::bind (create_lotso_ustrings, iterations)); } threads.join_all (); OIIO_CHECK_ASSERT (true); // If we make it here without crashing, pass } static void getargs (int argc, char *argv[]) { bool help = false; ArgParse ap; ap.options ("ustring_test\n" OIIO_INTRO_STRING "\n" "Usage: ustring_test [options]", // "%*", parse_files, "", "--help", &help, "Print help message", "-v", &verbose, "Verbose mode", "--threads %d", &numthreads, ustring::format("Number of threads (default: %d)", numthreads).c_str(), "--iters %d", &iterations, ustring::format("Number of iterations (default: %d)", iterations).c_str(), "--trials %d", &ntrials, "Number of trials", "--wedge", &wedge, "Do a wedge test", NULL); if (ap.parse (argc, (const char**)argv) < 0) { std::cerr << ap.geterror() << std::endl; ap.usage (); exit (EXIT_FAILURE); } if (help) { ap.usage (); exit (EXIT_FAILURE); } } int main (int argc, char *argv[]) { getargs (argc, argv); OIIO_CHECK_ASSERT(ustring("foo") == ustring("foo")); OIIO_CHECK_ASSERT(ustring("bar") != ustring("foo")); ustring foo ("foo"); OIIO_CHECK_ASSERT (foo.string() == "foo"); std::cout << "hw threads = " << Sysutil::hardware_concurrency() << "\n"; std::cout << "threads\ttime (best of " << ntrials << ")\n"; std::cout << "-------\t----------\n"; static int threadcounts[] = { 1, 2, 4, 8, 12, 16, 20, 24, 28, 32, 64, 128, 1024, 1<<30 }; for (int i = 0; threadcounts[i] <= numthreads; ++i) { int nt = wedge ? threadcounts[i] : numthreads; int its = iterations/nt; double range; double t = time_trial (boost::bind(test_ustring_lock,nt,its), ntrials, &range); std::cout << Strutil::format ("%2d\t%5.1f range %.2f\t(%d iters/thread)\n", nt, t, range, its); if (! wedge) break; // don't loop if we're not wedging } if (verbose) std::cout << "\n" << ustring::getstats() << "\n"; return unit_test_failures; } openimageio-1.7.17~dfsg0.orig/src/libutil/spinlock_test.cpp0000644000175000017500000001252113151711064022146 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include "OpenImageIO/thread.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/sysutil.h" #include "OpenImageIO/timer.h" #include "OpenImageIO/argparse.h" #include "OpenImageIO/ustring.h" #include #include #include "OpenImageIO/unittest.h" OIIO_NAMESPACE_USING; // Test spin locks by creating a bunch of threads that all increment the // accumulator many times, protected by spin locks. If, at the end, the // accumulated value is equal to iterations*threads, then the spin locks // worked. static int iterations = 40000000; static int numthreads = 16; static int ntrials = 1; static bool verbose = false; static bool wedge = false; static spin_mutex print_mutex; // make the prints not clobber each other volatile long long accum = 0; float faccum = 0; spin_mutex mymutex; static void do_accum (int iterations) { if (verbose) { spin_lock lock(print_mutex); std::cout << "thread " << boost::this_thread::get_id() << ", accum = " << accum << "\n"; } #if 1 for (int i = 0; i < iterations; ++i) { spin_lock lock (mymutex); accum += 1; } #else // Alternate one that mixes in some math to make longer lock hold time, // and also more to do between locks. Interesting contrast in timings. float last = 0.0f; for (int i = 0; i < iterations; ++i) { last = fmodf (sinf(last), 1.0f); spin_lock lock (mymutex); accum += 1; faccum = fmod (sinf(faccum+last), 1.0f); } #endif } void test_spinlock (int numthreads, int iterations) { accum = 0; boost::thread_group threads; for (int i = 0; i < numthreads; ++i) { threads.create_thread (boost::bind(do_accum,iterations)); } ASSERT ((int)threads.size() == numthreads); threads.join_all (); OIIO_CHECK_EQUAL (accum, ((long long)iterations * (long long)numthreads)); } static void getargs (int argc, char *argv[]) { bool help = false; ArgParse ap; ap.options ("spinlock_test\n" OIIO_INTRO_STRING "\n" "Usage: spinlock_test [options]", // "%*", parse_files, "", "--help", &help, "Print help message", "-v", &verbose, "Verbose mode", "--threads %d", &numthreads, ustring::format("Number of threads (default: %d)", numthreads).c_str(), "--iters %d", &iterations, ustring::format("Number of iterations (default: %d)", iterations).c_str(), "--trials %d", &ntrials, "Number of trials", "--wedge", &wedge, "Do a wedge test", NULL); if (ap.parse (argc, (const char**)argv) < 0) { std::cerr << ap.geterror() << std::endl; ap.usage (); exit (EXIT_FAILURE); } if (help) { ap.usage (); exit (EXIT_FAILURE); } } int main (int argc, char *argv[]) { getargs (argc, argv); std::cout << "hw threads = " << Sysutil::hardware_concurrency() << "\n"; std::cout << "threads\ttime (best of " << ntrials << ")\n"; std::cout << "-------\t----------\n"; static int threadcounts[] = { 1, 2, 4, 8, 12, 16, 20, 24, 28, 32, 64, 128, 1024, 1<<30 }; for (int i = 0; threadcounts[i] <= numthreads; ++i) { int nt = wedge ? threadcounts[i] : numthreads; int its = iterations/nt; double range; double t = time_trial (boost::bind(test_spinlock,nt,its), ntrials, &range); std::cout << Strutil::format ("%2d\t%5.1f range %.2f\t(%d iters/thread)\n", nt, t, range, its); if (! wedge) break; // don't loop if we're not wedging } return unit_test_failures; } openimageio-1.7.17~dfsg0.orig/src/libutil/filesystem_test.cpp0000644000175000017500000004221213151711064022510 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include "OpenImageIO/platform.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/unittest.h" #ifndef _WIN32 # include #endif OIIO_NAMESPACE_USING; // This will be run via testsuite/unit_filesystem, from the // build/ARCH/src/libOpenImageIO directory. Two levels up will be // build/ARCH. void test_filename_decomposition () { std::string test ("/directoryA/directory/filename.ext"); std::cout << "Testing filename, extension, parent_path\n"; OIIO_CHECK_EQUAL (Filesystem::filename(test), "filename.ext"); OIIO_CHECK_EQUAL (Filesystem::extension(test), ".ext"); OIIO_CHECK_EQUAL (Filesystem::extension("/directory/filename"), ""); OIIO_CHECK_EQUAL (Filesystem::extension("/directory/filename."), "."); OIIO_CHECK_EQUAL (Filesystem::parent_path(test), "/directoryA/directory"); std::cout << "Testing path_is_absolute\n"; OIIO_CHECK_EQUAL (Filesystem::path_is_absolute ("/foo/bar"), true); OIIO_CHECK_EQUAL (Filesystem::path_is_absolute ("foo/bar"), false); OIIO_CHECK_EQUAL (Filesystem::path_is_absolute ("../foo/bar"), false); std::cout << "Testing replace_extension\n"; OIIO_CHECK_EQUAL (Filesystem::replace_extension(test,"foo"), "/directoryA/directory/filename.foo"); } void test_filename_searchpath_find () { #if _WIN32 # define DIRSEP "\\" #else # define DIRSEP "/" #endif #define PATHSEP ":" std::string pathlist (".." DIRSEP ".." PATHSEP ".." DIRSEP ".." DIRSEP "cpack" PATHSEP "foo/bar/baz"); std::cout << "Testing searchpath_split\n"; std::vector dirs; Filesystem::searchpath_split (pathlist, dirs); OIIO_CHECK_EQUAL (dirs.size(), 3); OIIO_CHECK_EQUAL (dirs[0], ".." DIRSEP ".."); OIIO_CHECK_EQUAL (dirs[1], ".." DIRSEP ".." DIRSEP "cpack"); OIIO_CHECK_EQUAL (dirs[2], "foo/bar/baz"); std::cout << "Testing searchpath_find\n"; // non-recursive search success OIIO_CHECK_EQUAL (Filesystem::searchpath_find ("License.txt", dirs, false, false), ".." DIRSEP ".." DIRSEP "cpack" DIRSEP "License.txt"); // non-recursive search failure (file is in a subdirectory) OIIO_CHECK_EQUAL (Filesystem::searchpath_find ("oiioversion.h", dirs, false, false), ""); // recursive search success (file is in a subdirectory) OIIO_CHECK_EQUAL (Filesystem::searchpath_find ("oiioversion.h", dirs, false, true), ".." DIRSEP ".." DIRSEP "include" DIRSEP "OpenImageIO" DIRSEP "oiioversion.h"); } inline std::string my_read_text_file (string_view filename) { std::string err; std::string contents; bool ok = Filesystem::read_text_file (filename, contents); OIIO_CHECK_ASSERT (ok); return contents; } static void test_file_status () { // Make test file, test Filesystem::fopen in the process. FILE *file = Filesystem::fopen ("testfile", "w"); OIIO_CHECK_ASSERT (file != NULL); const char testtext[] = "test\nfoo\nbar\n"; fputs (testtext, file); fclose (file); std::cout << "Testing file_size:\n"; OIIO_CHECK_EQUAL (Filesystem::file_size("testfile"), 13); std::cout << "Testing read_text_file\n"; OIIO_CHECK_EQUAL (my_read_text_file("testfile"), testtext); std::cout << "Testing read_bytes:\n"; char buf[3]; size_t nread = Filesystem::read_bytes ("testfile", buf, 3, 5); OIIO_CHECK_EQUAL (nread, 3); OIIO_CHECK_EQUAL (buf[0], 'f'); OIIO_CHECK_EQUAL (buf[1], 'o'); OIIO_CHECK_EQUAL (buf[2], 'o'); std::cout << "Testing create_directory\n"; Filesystem::create_directory ("testdir"); std::cout << "Testing exists\n"; OIIO_CHECK_ASSERT (Filesystem::exists("testfile")); OIIO_CHECK_ASSERT (Filesystem::exists("testdir")); OIIO_CHECK_ASSERT (! Filesystem::exists("noexist")); std::cout << "Testing is_directory, is_regular\n"; OIIO_CHECK_ASSERT (Filesystem::is_regular("testfile")); OIIO_CHECK_ASSERT (! Filesystem::is_directory("testfile")); OIIO_CHECK_ASSERT (! Filesystem::is_regular("testdir")); OIIO_CHECK_ASSERT (Filesystem::is_directory("testdir")); OIIO_CHECK_ASSERT (! Filesystem::is_regular("noexist")); OIIO_CHECK_ASSERT (! Filesystem::is_directory("noexist")); std::cout << "Testing copy, rename, remove\n"; OIIO_CHECK_ASSERT (! Filesystem::exists("testfile2")); OIIO_CHECK_ASSERT (! Filesystem::exists("testfile3")); Filesystem::copy ("testfile", "testfile2"); OIIO_CHECK_ASSERT (Filesystem::exists("testfile2")); OIIO_CHECK_EQUAL (my_read_text_file("testfile2"), testtext); Filesystem::rename ("testfile2", "testfile3"); OIIO_CHECK_ASSERT (! Filesystem::exists("testfile2")); OIIO_CHECK_ASSERT (Filesystem::exists("testfile3")); OIIO_CHECK_EQUAL (my_read_text_file("testfile3"), testtext); Filesystem::remove ("testfile"); Filesystem::remove ("testfile3"); Filesystem::remove ("testdir"); OIIO_CHECK_ASSERT (! Filesystem::exists("testfile")); OIIO_CHECK_ASSERT (! Filesystem::exists("testfile2")); OIIO_CHECK_ASSERT (! Filesystem::exists("testfile3")); OIIO_CHECK_ASSERT (! Filesystem::exists("testdir")); } static void test_seq (const char *str, const char *expected) { std::vector sequence; Filesystem::enumerate_sequence (str, sequence); std::stringstream joined; for (size_t i = 0; i < sequence.size(); ++i) { if (i) joined << " "; joined << sequence[i]; } std::cout << " \"" << str << "\" -> " << joined.str() << "\n"; OIIO_CHECK_EQUAL (joined.str(), std::string(expected)); } static void test_file_seq (const char *pattern, const char *override, const std::string &expected) { std::vector numbers; std::vector names; std::string normalized_pattern; std::string frame_range; Filesystem::parse_pattern(pattern, 0, normalized_pattern, frame_range); if (override && strlen(override) > 0) frame_range = override; Filesystem::enumerate_sequence(frame_range.c_str(), numbers); Filesystem::enumerate_file_sequence (normalized_pattern, numbers, names); std::string joined = Strutil::join(names, " "); std::cout << " " << pattern; if (override) std::cout << " + " << override; std::cout << " -> " << joined << "\n"; OIIO_CHECK_EQUAL (joined, expected); } static void test_file_seq_with_view (const char *pattern, const char *override, const char *view, const std::string &expected) { std::vector numbers; std::vector views; std::vector names; std::string normalized_pattern; std::string frame_range; Filesystem::parse_pattern(pattern, 0, normalized_pattern, frame_range); if (override && strlen(override) > 0) frame_range = override; Filesystem::enumerate_sequence(frame_range.c_str(), numbers); if (view) { for (size_t i = 0, e = numbers.size(); i < e; ++i) views.push_back(view); } Filesystem::enumerate_file_sequence (normalized_pattern, numbers, views, names); std::string joined = Strutil::join(names, " "); std::cout << " " << pattern; if (override) std::cout << " + " << override; std::cout << " -> " << joined << "\n"; OIIO_CHECK_EQUAL (joined, expected); } static void test_scan_file_seq (const char *pattern, const std::string &expected) { std::vector numbers; std::vector names; std::string normalized_pattern; std::string frame_range; Filesystem::parse_pattern(pattern, 0, normalized_pattern, frame_range); Filesystem::scan_for_matching_filenames (normalized_pattern, numbers, names); std::string joined = Strutil::join(names, " "); std::cout << " " << pattern; std::cout << " -> " << joined << "\n"; OIIO_CHECK_EQUAL (joined, expected); // Check that we don't crash from exceptions generated by strangely // formed patterns. const char *weird = "{'cpu_model': 'Intel(R) Xeon(R) CPU E5-2630 @ 2.30GHz'}"; Filesystem::parse_pattern (weird, 0, normalized_pattern, frame_range); Filesystem::scan_for_matching_filenames (normalized_pattern, numbers, names); OIIO_CHECK_EQUAL (names.size(), 0); // If we didn't crash above, we're ok! } static void test_scan_file_seq_with_views (const char *pattern, const char **views_, const std::string &expected) { std::vector frame_numbers; std::vector frame_views; std::vector frame_names; std::string normalized_pattern; std::string frame_range; std::vector views; for (size_t i = 0; views_[i]; ++i) views.push_back(views_[i]); Filesystem::parse_pattern(pattern, 0, normalized_pattern, frame_range); Filesystem::scan_for_matching_filenames (normalized_pattern, views, frame_numbers, frame_views, frame_names); std::string joined = Strutil::join(frame_names, " "); std::cout << " " << pattern; std::cout << " -> " << joined << "\n"; OIIO_CHECK_EQUAL (joined, expected); } void test_frame_sequences () { std::cout << "Testing frame number sequences:\n"; test_seq ("3", "3"); test_seq ("1-5", "1 2 3 4 5"); test_seq ("5-1", "5 4 3 2 1"); test_seq ("1-3,6,10-12", "1 2 3 6 10 11 12"); test_seq ("1-5x2", "1 3 5"); test_seq ("1-5y2", "2 4"); std::cout << "\n"; test_file_seq ("foo.1-5#.exr", NULL, "foo.0001.exr foo.0002.exr foo.0003.exr foo.0004.exr foo.0005.exr"); test_file_seq ("foo.5-1#.exr", NULL, "foo.0005.exr foo.0004.exr foo.0003.exr foo.0002.exr foo.0001.exr"); test_file_seq ("foo.1-3,6,10-12#.exr", NULL, "foo.0001.exr foo.0002.exr foo.0003.exr foo.0006.exr foo.0010.exr foo.0011.exr foo.0012.exr"); test_file_seq ("foo.1-5x2#.exr", NULL, "foo.0001.exr foo.0003.exr foo.0005.exr"); test_file_seq ("foo.1-5y2#.exr", NULL, "foo.0002.exr foo.0004.exr"); test_file_seq ("foo.#.exr", "1-5", "foo.0001.exr foo.0002.exr foo.0003.exr foo.0004.exr foo.0005.exr"); test_file_seq ("foo.#.exr", "1-5x2", "foo.0001.exr foo.0003.exr foo.0005.exr"); test_file_seq ("foo.1-3@@.exr", NULL, "foo.01.exr foo.02.exr foo.03.exr"); test_file_seq ("foo.1-3@#.exr", NULL, "foo.00001.exr foo.00002.exr foo.00003.exr"); test_file_seq ("foo.1-5%04d.exr", NULL, "foo.0001.exr foo.0002.exr foo.0003.exr foo.0004.exr foo.0005.exr"); test_file_seq ("foo.%04d.exr", "1-5", "foo.0001.exr foo.0002.exr foo.0003.exr foo.0004.exr foo.0005.exr"); test_file_seq ("foo.%4d.exr", "1-5", "foo. 1.exr foo. 2.exr foo. 3.exr foo. 4.exr foo. 5.exr"); test_file_seq ("foo.%d.exr", "1-5", "foo.1.exr foo.2.exr foo.3.exr foo.4.exr foo.5.exr"); const char *views1[] = { "left", "right", "foo", "", NULL }; for (size_t i = 0; i < 5; ++i) { const char *view = views1[i]; test_file_seq_with_view ("foo.1-5#.exr", NULL, view, "foo.0001.exr foo.0002.exr foo.0003.exr foo.0004.exr foo.0005.exr"); test_file_seq_with_view ("foo.5-1#.exr", NULL, view, "foo.0005.exr foo.0004.exr foo.0003.exr foo.0002.exr foo.0001.exr"); test_file_seq_with_view ("foo.1-3,6,10-12#.exr", NULL, view, "foo.0001.exr foo.0002.exr foo.0003.exr foo.0006.exr foo.0010.exr foo.0011.exr foo.0012.exr"); test_file_seq_with_view ("foo.1-5x2#.exr", NULL, view, "foo.0001.exr foo.0003.exr foo.0005.exr"); test_file_seq_with_view ("foo.1-5y2#.exr", NULL, view, "foo.0002.exr foo.0004.exr"); test_file_seq_with_view ("foo.#.exr", "1-5", view, "foo.0001.exr foo.0002.exr foo.0003.exr foo.0004.exr foo.0005.exr"); test_file_seq_with_view ("foo.#.exr", "1-5x2", view, "foo.0001.exr foo.0003.exr foo.0005.exr"); test_file_seq_with_view ("foo.1-3@@.exr", NULL, view, "foo.01.exr foo.02.exr foo.03.exr"); test_file_seq_with_view ("foo.1-3@#.exr", NULL, view, "foo.00001.exr foo.00002.exr foo.00003.exr"); test_file_seq_with_view ("foo.1-5%04d.exr", NULL, view, "foo.0001.exr foo.0002.exr foo.0003.exr foo.0004.exr foo.0005.exr"); test_file_seq_with_view ("foo.%04d.exr", "1-5", view, "foo.0001.exr foo.0002.exr foo.0003.exr foo.0004.exr foo.0005.exr"); test_file_seq_with_view ("foo.%4d.exr", "1-5", view, "foo. 1.exr foo. 2.exr foo. 3.exr foo. 4.exr foo. 5.exr"); test_file_seq_with_view ("foo.%d.exr", "1-5", view, "foo.1.exr foo.2.exr foo.3.exr foo.4.exr foo.5.exr"); } // test_file_seq_with_view ("%V.%04d", NULL, NULL, ""); // test_file_seq_with_view ("%v", NULL, NULL, ""); // test_file_seq_with_view ("%V", NULL, "", ""); // test_file_seq_with_view ("%v", NULL, "", ""); // test_file_seq_with_view ("%V", NULL, "left", "left"); // test_file_seq_with_view ("%V", NULL, "right", "right"); // test_file_seq_with_view ("%v", NULL, "left", "l"); // test_file_seq_with_view ("%v", NULL, "right", "r"); test_file_seq_with_view ("foo_%V.1-2#.exr", NULL, "left", "foo_left.0001.exr foo_left.0002.exr"); test_file_seq_with_view ("%V/foo_%V.1-2#.exr", NULL, "left", "left/foo_left.0001.exr left/foo_left.0002.exr"); test_file_seq_with_view ("%v/foo_%V.1-2#.exr", NULL, "left", "l/foo_left.0001.exr l/foo_left.0002.exr"); test_file_seq_with_view ("%V/foo_%v.1-2#.exr", NULL, "left", "left/foo_l.0001.exr left/foo_l.0002.exr"); test_file_seq_with_view ("%v/foo_%v.1-2#.exr", NULL, "left", "l/foo_l.0001.exr l/foo_l.0002.exr"); std::cout << "\n"; } void create_test_file(const string_view& fn) { std::ofstream f(fn.c_str()); f.close(); } void test_scan_sequences () { std::cout << "Testing frame sequence scanning:\n"; std::vector< std::string > filenames; for (size_t i = 1; i <= 5; i++) { std::string fn = Strutil::format ("foo.%04d.exr", i); filenames.push_back (fn); create_test_file(fn); } #ifdef _WIN32 test_scan_file_seq ("foo.#.exr", ".\\foo.0001.exr .\\foo.0002.exr .\\foo.0003.exr .\\foo.0004.exr .\\foo.0005.exr"); #else test_scan_file_seq ("foo.#.exr", "./foo.0001.exr ./foo.0002.exr ./foo.0003.exr ./foo.0004.exr ./foo.0005.exr"); #endif filenames.clear(); Filesystem::create_directory ("left"); Filesystem::create_directory ("left/l"); for (size_t i = 1; i <= 5; i++) { std::string fn = Strutil::format ("left/l/foo_left_l.%04d.exr", i); filenames.push_back (fn); create_test_file(fn); } const char *views[] = { "left", NULL }; #ifdef _WIN32 test_scan_file_seq_with_views ("%V/%v/foo_%V_%v.#.exr", views, "left\\l\\foo_left_l.0001.exr left\\l\\foo_left_l.0002.exr left\\l\\foo_left_l.0003.exr left\\l\\foo_left_l.0004.exr left\\l\\foo_left_l.0005.exr"); #else test_scan_file_seq_with_views ("%V/%v/foo_%V_%v.#.exr", views, "left/l/foo_left_l.0001.exr left/l/foo_left_l.0002.exr left/l/foo_left_l.0003.exr left/l/foo_left_l.0004.exr left/l/foo_left_l.0005.exr"); #endif filenames.clear(); Filesystem::create_directory ("right"); Filesystem::create_directory ("right/r"); std::string fn; fn = "left/l/foo_left_l"; filenames.push_back(fn); create_test_file(fn); fn = "right/r/foo_right_r"; filenames.push_back(fn); create_test_file(fn); const char *views2[] = { "left", "right", NULL }; #ifdef _WIN32 test_scan_file_seq_with_views ("%V/%v/foo_%V_%v", views2, "left\\l\\foo_left_l right\\r\\foo_right_r"); #else test_scan_file_seq_with_views ("%V/%v/foo_%V_%v", views2, "left/l/foo_left_l right/r/foo_right_r"); #endif } int main (int argc, char *argv[]) { test_filename_decomposition (); test_filename_searchpath_find (); test_file_status (); test_frame_sequences (); test_scan_sequences (); return unit_test_failures; } openimageio-1.7.17~dfsg0.orig/src/libutil/plugin.cpp0000644000175000017500000000741113151711064020565 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include "OpenImageIO/platform.h" #ifndef _WIN32 # include #endif #include "OpenImageIO/thread.h" #include "OpenImageIO/plugin.h" OIIO_NAMESPACE_BEGIN using namespace Plugin; namespace { static mutex plugin_mutex; static std::string last_error; } const char * Plugin::plugin_extension (void) { #if defined(_WIN32) return "dll"; #elif defined(__APPLE__) return "dylib"; #else return "so"; #endif } #if defined(_WIN32) // Dummy values #define RTLD_LAZY 0 #define RTLD_GLOBAL 0 Handle dlopen (const char *plugin_filename, int) { return LoadLibrary (plugin_filename); } bool dlclose (Handle plugin_handle) { return FreeLibrary ((HMODULE)plugin_handle) != 0; } void * dlsym (Handle plugin_handle, const char *symbol_name) { return GetProcAddress ((HMODULE)plugin_handle, symbol_name); } std::string dlerror () { LPVOID lpMsgBuf; std::string win32Error; if (FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &lpMsgBuf, 0, NULL)) win32Error = (LPSTR)lpMsgBuf; LocalFree(lpMsgBuf); return win32Error; } #endif Handle Plugin::open (const char *plugin_filename, bool global) { lock_guard guard (plugin_mutex); last_error.clear (); int mode = RTLD_LAZY; if (global) mode |= RTLD_GLOBAL; Handle h = dlopen (plugin_filename, mode); if (!h) last_error = dlerror(); return h; } bool Plugin::close (Handle plugin_handle) { lock_guard guard (plugin_mutex); last_error.clear (); if (dlclose (plugin_handle)) { last_error = dlerror(); return false; } return true; } void * Plugin::getsym (Handle plugin_handle, const char *symbol_name) { lock_guard guard (plugin_mutex); last_error.clear (); void *s = dlsym (plugin_handle, symbol_name); if (!s) last_error = dlerror(); return s; } std::string Plugin::geterror (void) { lock_guard guard (plugin_mutex); std::string e = last_error; last_error.clear (); return e; } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libutil/simd_test.cpp0000644000175000017500000014575513151711064021300 0ustar mfvmfv/* Copyright (c) 2014 Larry Gritz et al. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sony Pictures Imageworks nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include OIIO_NAMESPACE_USING; using namespace OIIO::simd; static int iterations = 1000000; static int ntrials = 5; static bool verbose = false; static Sysutil::Term term(std::cout); float dummy_float[16]; float dummy_float2[16]; float dummy_int[16]; static void getargs (int argc, char *argv[]) { bool help = false; ArgParse ap; ap.options ("simd_test\n" OIIO_INTRO_STRING "\n" "Usage: simd_test [options]", // "%*", parse_files, "", "--help", &help, "Print help message", "-v", &verbose, "Verbose mode", "--iterations %d", &iterations, ustring::format("Number of iterations (default: %d)", iterations).c_str(), "--trials %d", &ntrials, "Number of trials", NULL); if (ap.parse (argc, (const char**)argv) < 0) { std::cerr << ap.geterror() << std::endl; ap.usage (); exit (EXIT_FAILURE); } if (help) { ap.usage (); exit (EXIT_FAILURE); } } static void category_heading (string_view name) { std::cout << "\n" << term.ansi("bold,underscore,yellow", name) << "\n\n"; } static void test_heading (string_view name, string_view name2="") { std::cout << term.ansi("bold") << name << ' ' << name2 << term.ansi("normal") << "\n"; } #define OIIO_CHECK_SIMD_EQUAL(x,y) \ (all ((x) == (y)) ? ((void)0) \ : ((std::cout << Sysutil::Term(std::cout).ansi("red,bold") \ << __FILE__ << ":" << __LINE__ << ":\n" \ << "FAILED: " << Sysutil::Term(std::cout).ansi("normal") \ << #x << " == " << #y << "\n" \ << "\tvalues were '" << (x) << "' and '" << (y) << "'\n"), \ (void)++unit_test_failures)) #define OIIO_CHECK_SIMD_EQUAL_THRESH(x,y,eps) \ (all (abs((x)-(y)) < (eps)) ? ((void)0) \ : ((std::cout << Sysutil::Term(std::cout).ansi("red,bold") \ << __FILE__ << ":" << __LINE__ << ":\n" \ << "FAILED: " << Sysutil::Term(std::cout).ansi("normal") \ << #x << " == " << #y << "\n" \ << "\tvalues were '" << (x) << "' and '" << (y) << "'\n"), \ (void)++unit_test_failures)) #if OIIO_CPLUSPLUS_VERSION >= 11 /* So easy with lambdas */ // What I really want to do is merge benchmark() and benchmark2() into // one template using variadic arguments, like this: // template // void benchmark (size_t work, string_view funcname, FUNC func, ARGS... args) // But it seems that although this works for Clang, it does not for gcc 4.8 // (but does for 4.9). Some day I'll get back to this simplification, but // for now, gcc 4.8 seems like an important barrier. template void benchmark (string_view funcname, FUNC func, T x, size_t work=0) { if (!work) work = SimdElements::size; auto repeat_func = [&](){ // Unroll the loop 8 times auto r = func(x); DoNotOptimize (r); clobber_all_memory(); r = func(x); DoNotOptimize (r); clobber_all_memory(); r = func(x); DoNotOptimize (r); clobber_all_memory(); r = func(x); DoNotOptimize (r); clobber_all_memory(); r = func(x); DoNotOptimize (r); clobber_all_memory(); r = func(x); DoNotOptimize (r); clobber_all_memory(); r = func(x); DoNotOptimize (r); clobber_all_memory(); r = func(x); DoNotOptimize (r); clobber_all_memory(); }; float time = time_trial (repeat_func, ntrials, iterations/8); std::cout << Strutil::format (" %s: %7.1f Mvals/sec, (%.1f Mcalls/sec)\n", funcname, ((iterations*work)/1.0e6)/time, (iterations/1.0e6)/time); } template void benchmark2 (string_view funcname, FUNC func, T x, U y, size_t work=0) { if (!work) work = SimdElements::size; auto repeat_func = [&](){ // Unroll the loop 8 times auto r = func(x, y); DoNotOptimize (r); clobber_all_memory(); r = func(x, y); DoNotOptimize (r); clobber_all_memory(); r = func(x, y); DoNotOptimize (r); clobber_all_memory(); r = func(x, y); DoNotOptimize (r); clobber_all_memory(); r = func(x, y); DoNotOptimize (r); clobber_all_memory(); r = func(x, y); DoNotOptimize (r); clobber_all_memory(); r = func(x, y); DoNotOptimize (r); clobber_all_memory(); r = func(x, y); DoNotOptimize (r); clobber_all_memory(); }; float time = time_trial (repeat_func, ntrials, iterations/8); std::cout << Strutil::format (" %s: %7.1f Mvals/sec, (%.1f Mcalls/sec)\n", funcname, ((iterations*work)/1.0e6)/time, (iterations/1.0e6)/time); } #else // No support of lambdas, just skip the benchmarks #define benchmark(a,b,c,d) #define benchmark2(a,b,c,d,e) #endif template inline VEC mkvec (typename VEC::value_t a, typename VEC::value_t b, typename VEC::value_t c, typename VEC::value_t d=0) { return VEC(a,b,c,d); } template<> inline float3 mkvec (float a, float b, float c, float d) { return float3(a,b,c); } template<> inline float8 mkvec (float a, float b, float c, float d) { return float8(a,b,c,d,a,b,c,d); } template<> inline int8 mkvec (int a, int b, int c, int d) { return int8(a,b,c,d,a,b,c,d); } template<> inline bool8 mkvec (bool a, bool b, bool c, bool d) { return bool8(a,b,c,d,a,b,c,d); } template inline VEC mkvec (typename VEC::value_t a, typename VEC::value_t b, typename VEC::value_t c, typename VEC::value_t d, typename VEC::value_t e, typename VEC::value_t f, typename VEC::value_t g, typename VEC::value_t h) { return VEC(a,b,c,d,e,f,g,h); } template<> inline bool4 mkvec (bool a, bool b, bool c, bool d, bool e, bool f, bool g, bool h) { return bool4(a,b,c,d); } template<> inline int4 mkvec (int a, int b, int c, int d, int e, int f, int g, int h) { return int4(a,b,c,d); } template<> inline float4 mkvec (float a, float b, float c, float d, float e, float f, float g, float h) { return float4(a,b,c,d); } template<> inline float3 mkvec (float a, float b, float c, float d, float e, float f, float g, float h) { return float3(a,b,c); } template inline int loadstore_vec (int dummy) { typedef typename VEC::value_t ELEM; ELEM B[VEC::elements]; VEC v; v.load ((ELEM *)dummy_float); v.store ((ELEM *)B); return 0; } template inline int loadstore_vec_N (int dummy) { typedef typename VEC::value_t ELEM; ELEM B[VEC::elements]; VEC v; v.load ((ELEM *)dummy_float, N); v.store ((ELEM *)B, N); return 0; } inline float dot_imath (const Imath::V3f &v) { return v.dot(v); } inline float dot_imath_simd (const Imath::V3f &v_) { float3 v (v_); return simd::dot(v,v); } inline float dot_simd (const simd::float3 v) { return dot(v,v); } inline Imath::V3f norm_imath (const Imath::V3f &a) { return a.normalized(); } inline Imath::V3f norm_imath_simd (float3 a) { return a.normalized().V3f(); } inline Imath::V3f norm_imath_simd_fast (float3 a) { return a.normalized_fast().V3f(); } inline float3 norm_simd_fast (float3 a) { return a.normalized_fast(); } inline float3 norm_simd (float3 a) { return a.normalized(); } inline Imath::M44f inverse_imath (const Imath::M44f &M) { return M.inverse(); } inline matrix44 inverse_simd (const matrix44 &M) { return M.inverse(); } template void test_loadstore () { typedef typename VEC::value_t ELEM; test_heading ("loadstore ", VEC::type_name()); VEC C1234 = mkvec(1, 2, 3, 4, 5, 6, 7, 8); // VEC C0 (0); ELEM partial[] = { 101, 102, 103, 104, 105, 106, 107, 108 }; for (int i = 1; i <= VEC::elements; ++i) { VEC a (ELEM(0)); a.load (partial, i); for (int j = 0; j < VEC::elements; ++j) OIIO_CHECK_EQUAL (a[j], j= 11 /* So easy with lambdas */ benchmark ("load/store", loadstore_vec, 0, VEC::elements); if (VEC::elements > 4) { benchmark ("load/store, 8 comps", loadstore_vec_N, 0, 8); benchmark ("load/store, 7 comps", loadstore_vec_N, 0, 7); benchmark ("load/store, 6 comps", loadstore_vec_N, 0, 6); benchmark ("load/store, 5 comps", loadstore_vec_N, 0, 5); } if (VEC::elements >= 4) { benchmark ("load/store, 4 comps", loadstore_vec_N, 0, 4); } benchmark ("load/store, 3 comps", loadstore_vec_N, 0, 3); benchmark ("load/store, 2 comps", loadstore_vec_N, 0, 2); benchmark ("load/store, 1 comps", loadstore_vec_N, 0, 1); #endif } template void test_vint_to_uint16s () { test_heading (Strutil::format("test converting %s to uint16", VEC::type_name())); VEC ival = VEC::Iota (0xffff0000); unsigned short buf[VEC::elements]; ival.store (buf); for (int i = 0; i < VEC::elements; ++i) OIIO_CHECK_EQUAL (int(buf[i]), i); #if OIIO_CPLUSPLUS_VERSION >= 11 /* So easy with lambdas */ benchmark2 ("convert to uint16", [](VEC& a, unsigned short *s){ a.store(s); return 1; }, ival, buf, VEC::elements); #endif } template void test_vint_to_uint8s () { test_heading (Strutil::format("test converting %s to uint8", VEC::type_name())); VEC ival = VEC::Iota (0xffffff00); unsigned char buf[VEC::elements]; ival.store (buf); for (int i = 0; i < VEC::elements; ++i) OIIO_CHECK_EQUAL (int(buf[i]), i); #if OIIO_CPLUSPLUS_VERSION >= 11 /* So easy with lambdas */ benchmark2 ("convert to uint16", [](VEC& a, unsigned char *s){ a.store(s); return 1; }, ival, buf, VEC::elements); #endif } template void test_component_access () { typedef typename VEC::value_t ELEM; test_heading ("component_access ", VEC::type_name()); const ELEM vals[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; VEC a = mkvec(0, 1, 2, 3, 4, 5, 6, 7); for (int i = 0; i < VEC::elements; ++i) OIIO_CHECK_EQUAL (a[i], vals[i]); if (VEC::elements <= 4) { OIIO_CHECK_EQUAL (a.x(), 0); OIIO_CHECK_EQUAL (a.y(), 1); OIIO_CHECK_EQUAL (a.z(), 2); if (SimdElements::size > 3) OIIO_CHECK_EQUAL (a.w(), 3); VEC t; t = a; t.set_x(42); OIIO_CHECK_SIMD_EQUAL (t, mkvec(42,1,2,3,4,5,6,7)); t = a; t.set_y(42); OIIO_CHECK_SIMD_EQUAL (t, mkvec(0,42,2,3,4,5,6,7)); t = a; t.set_z(42); OIIO_CHECK_SIMD_EQUAL (t, mkvec(0,1,42,3,4,5,6,7)); if (SimdElements::size > 3) { t = a; t.set_w(42); OIIO_CHECK_SIMD_EQUAL (t, mkvec(0,1,2,42,4,5,6,7)); } } OIIO_CHECK_EQUAL (extract<0>(a), 0); OIIO_CHECK_EQUAL (extract<1>(a), 1); OIIO_CHECK_EQUAL (extract<2>(a), 2); if (SimdElements::size > 3) OIIO_CHECK_EQUAL (extract<3>(a), 3); OIIO_CHECK_SIMD_EQUAL (insert<0>(a, ELEM(42)), mkvec(42,1,2,3,4,5,6,7)); OIIO_CHECK_SIMD_EQUAL (insert<1>(a, ELEM(42)), mkvec(0,42,2,3,4,5,6,7)); OIIO_CHECK_SIMD_EQUAL (insert<2>(a, ELEM(42)), mkvec(0,1,42,3,4,5,6,7)); if (SimdElements::size > 3) OIIO_CHECK_SIMD_EQUAL (insert<3>(a, ELEM(42)), mkvec(0,1,2,42,4,5,6,7)); VEC b (vals); for (int i = 0; i < VEC::elements; ++i) OIIO_CHECK_EQUAL (b[i], vals[i]); OIIO_CHECK_EQUAL (extract<0>(b), 0); OIIO_CHECK_EQUAL (extract<1>(b), 1); OIIO_CHECK_EQUAL (extract<2>(b), 2); if (SimdElements::size > 3) OIIO_CHECK_EQUAL (extract<3>(b), 3); if (SimdElements::size > 4) { OIIO_CHECK_EQUAL (extract<4>(b), 4); OIIO_CHECK_EQUAL (extract<5>(b), 5); OIIO_CHECK_EQUAL (extract<6>(b), 6); OIIO_CHECK_EQUAL (extract<7>(b), 7); } #if OIIO_CPLUSPLUS_VERSION >= 11 /* So easy with lambdas */ benchmark2 ("operator[i]", [&](const VEC& v, int i){ return v[i]; }, b, 2, 1 /*work*/); benchmark2 ("operator[2]", [&](const VEC& v, int i){ return v[2]; }, b, 2, 1 /*work*/); benchmark2 ("operator[0]", [&](const VEC& v, int i){ return v[0]; }, b, 0, 1 /*work*/); benchmark2 ("extract<2> ", [&](const VEC& v, int i){ return extract<2>(v); }, b, 2, 1 /*work*/); benchmark2 ("extract<0> ", [&](const VEC& v, int i){ return extract<0>(v); }, b, 0, 1 /*work*/); benchmark2 ("insert<2> ", [&](const VEC& v, ELEM i){ return insert<2>(v, i); }, b, ELEM(1), 1 /*work*/); #endif } template<> void test_component_access () { typedef bool4 VEC; typedef VEC::value_t ELEM; test_heading ("component_access ", VEC::type_name()); VEC a (false, true, true, true); OIIO_CHECK_EQUAL (bool(a[0]), false); OIIO_CHECK_EQUAL (bool(a[1]), true); OIIO_CHECK_EQUAL (bool(a[2]), true); OIIO_CHECK_EQUAL (bool(a[3]), true); OIIO_CHECK_EQUAL (extract<0>(a), false); OIIO_CHECK_EQUAL (extract<1>(a), true); OIIO_CHECK_EQUAL (extract<2>(a), true); OIIO_CHECK_EQUAL (extract<3>(a), true); OIIO_CHECK_SIMD_EQUAL (insert<0>(a, ELEM(true)), VEC(true,true,true,true)); OIIO_CHECK_SIMD_EQUAL (insert<1>(a, ELEM(false)), VEC(false,false,true,true)); OIIO_CHECK_SIMD_EQUAL (insert<2>(a, ELEM(false)), VEC(false,true,false,true)); OIIO_CHECK_SIMD_EQUAL (insert<3>(a, ELEM(false)), VEC(false,true,true,false)); } template<> void test_component_access () { typedef bool8 VEC; typedef VEC::value_t ELEM; test_heading ("component_access ", VEC::type_name()); VEC a (false, true, true, true, false, false, true, true); OIIO_CHECK_EQUAL (bool(a[0]), false); OIIO_CHECK_EQUAL (bool(a[1]), true); OIIO_CHECK_EQUAL (bool(a[2]), true); OIIO_CHECK_EQUAL (bool(a[3]), true); OIIO_CHECK_EQUAL (bool(a[4]), false); OIIO_CHECK_EQUAL (bool(a[5]), false); OIIO_CHECK_EQUAL (bool(a[6]), true); OIIO_CHECK_EQUAL (bool(a[7]), true); OIIO_CHECK_EQUAL (extract<0>(a), false); OIIO_CHECK_EQUAL (extract<1>(a), true); OIIO_CHECK_EQUAL (extract<2>(a), true); OIIO_CHECK_EQUAL (extract<3>(a), true); OIIO_CHECK_EQUAL (extract<4>(a), false); OIIO_CHECK_EQUAL (extract<5>(a), false); OIIO_CHECK_EQUAL (extract<6>(a), true); OIIO_CHECK_EQUAL (extract<7>(a), true); OIIO_CHECK_SIMD_EQUAL (insert<0>(a, ELEM(true)), VEC(true, true, true, true, false, false, true, true)); OIIO_CHECK_SIMD_EQUAL (insert<1>(a, ELEM(false)), VEC(false, false, true, true, false, false, true, true)); OIIO_CHECK_SIMD_EQUAL (insert<2>(a, ELEM(false)), VEC(false, true, false, true, false, false, true, true)); OIIO_CHECK_SIMD_EQUAL (insert<3>(a, ELEM(false)), VEC(false, true, true, false, false, false, true, true)); OIIO_CHECK_SIMD_EQUAL (insert<4>(a, ELEM(true)), VEC(false, true, true, true, true, false, true, true)); OIIO_CHECK_SIMD_EQUAL (insert<5>(a, ELEM(true)), VEC(false, true, true, true, false, true, true, true)); OIIO_CHECK_SIMD_EQUAL (insert<6>(a, ELEM(false)), VEC(false, true, true, true, false, false, false, true)); OIIO_CHECK_SIMD_EQUAL (insert<7>(a, ELEM(false)), VEC(false, true, true, true, false, false, true, false)); } template inline T do_add (const T &a, const T &b) { return a+b; } template inline T do_sub (const T &a, const T &b) { return a-b; } template inline T do_mul (const T &a, const T &b) { return a*b; } template inline T do_div (const T &a, const T &b) { return a/b; } template inline T do_safe_div (const T &a, const T &b) { return T(safe_div(a,b)); } inline Imath::V3f add_vec_simd (const Imath::V3f &a, const Imath::V3f &b) { return (float3(a)+float3(b)).V3f(); } template void test_arithmetic () { typedef typename VEC::value_t ELEM; test_heading ("arithmetic ", VEC::type_name()); VEC a = VEC::Iota (1.0f, 3.0f); VEC b = VEC::Iota (1.0f, 1.0f); VEC add(ELEM(0)), sub(ELEM(0)), mul(ELEM(0)), div(ELEM(0)); ELEM bsum(ELEM(0)); for (int i = 0; i < VEC::elements; ++i) { add[i] = a[i] + b[i]; sub[i] = a[i] - b[i]; mul[i] = a[i] * b[i]; div[i] = a[i] / b[i]; bsum += b[i]; } OIIO_CHECK_SIMD_EQUAL (a+b, add); OIIO_CHECK_SIMD_EQUAL (a-b, sub); OIIO_CHECK_SIMD_EQUAL (a*b, mul); OIIO_CHECK_SIMD_EQUAL (a/b, div); OIIO_CHECK_SIMD_EQUAL (a*ELEM(2), a*VEC(ELEM(2))); { VEC r = a; r += b; OIIO_CHECK_SIMD_EQUAL (r, add); } { VEC r = a; r -= b; OIIO_CHECK_SIMD_EQUAL (r, sub); } { VEC r = a; r *= b; OIIO_CHECK_SIMD_EQUAL (r, mul); } { VEC r = a; r /= b; OIIO_CHECK_SIMD_EQUAL (r, div); } { VEC r = a; r *= ELEM(2); OIIO_CHECK_SIMD_EQUAL (r, a*ELEM(2)); } OIIO_CHECK_EQUAL (reduce_add(b), bsum); OIIO_CHECK_SIMD_EQUAL (vreduce_add(b), VEC(bsum)); OIIO_CHECK_EQUAL (reduce_add(VEC(1.0f)), SimdElements::size); #if OIIO_CPLUSPLUS_VERSION >= 11 /* So easy with lambdas */ benchmark2 ("operator+", do_add, a, b); benchmark2 ("operator-", do_sub, a, b); benchmark2 ("operator*", do_mul, a, b); benchmark2 ("operator/", do_div, a, b); benchmark ("reduce_add", [](VEC& a){ return vreduce_add(a); }, a); if (is_same::value) { // For float3, compare to Imath Imath::V3f a(2.51f,1.0f,1.0f), b(3.1f,1.0f,1.0f); benchmark2 ("add Imath::V3f", do_add, a, b, 3 /*work*/); benchmark2 ("add Imath::V3f with simd", add_vec_simd, a, b, 3 /*work*/); benchmark2 ("sub Imath::V3f", do_sub, a, b, 3 /*work*/); benchmark2 ("mul Imath::V3f", do_mul, a, b, 3 /*work*/); benchmark2 ("div Imath::V3f", do_div, a, b, 3 /*work*/); } benchmark2 ("reference: add scalar", do_add, a[2], b[1]); benchmark2 ("reference: mul scalar", do_mul, a[2], b[1]); benchmark2 ("reference: div scalar", do_div, a[2], b[1]); #endif } template void test_fused () { typedef typename VEC::value_t ELEM; test_heading ("fused ", VEC::type_name()); VEC a = VEC::Iota (10); VEC b = VEC::Iota (1); VEC c = VEC::Iota (0.5f); OIIO_CHECK_SIMD_EQUAL (madd (a, b, c), a*b+c); OIIO_CHECK_SIMD_EQUAL (msub (a, b, c), a*b-c); OIIO_CHECK_SIMD_EQUAL (nmadd (a, b, c), -(a*b)+c); OIIO_CHECK_SIMD_EQUAL (nmsub (a, b, c), -(a*b)-c); #if OIIO_CPLUSPLUS_VERSION >= 11 /* So easy with lambdas */ benchmark2 ("madd old *+", [&](VEC&a, VEC&b){ return a*b+c; }, a, b); benchmark2 ("madd fused", [&](VEC&a, VEC&b){ return madd(a,b,c); }, a, b); benchmark2 ("msub old *-", [&](VEC&a, VEC&b){ return a*b-c; }, a, b); benchmark2 ("msub fused", [&](VEC&a, VEC&b){ return msub(a,b,c); }, a, b); benchmark2 ("nmadd old (-*)+", [&](VEC&a, VEC&b){ return c-(a*b); }, a, b); benchmark2 ("nmadd fused", [&](VEC&a, VEC&b){ return nmadd(a,b,c); }, a, b); benchmark2 ("nmsub old -(*+)", [&](VEC&a, VEC&b){ return -(a*b)-c; }, a, b); benchmark2 ("nmsub fused", [&](VEC&a, VEC&b){ return nmsub(a,b,c); }, a, b); #endif } template T do_and (const T& a, const T& b) { return a & b; } template T do_or (const T& a, const T& b) { return a | b; } template T do_xor (const T& a, const T& b) { return a ^ b; } template T do_compl (const T& a) { return ~a; } template T do_andnot (const T& a, const T& b) { return andnot(a,b); } template void test_bitwise_int () { typedef typename VEC::value_t ELEM; test_heading ("bitwise ", VEC::type_name()); VEC a (0x12341234); VEC b (0x11111111); OIIO_CHECK_SIMD_EQUAL (a & b, VEC(0x10101010)); OIIO_CHECK_SIMD_EQUAL (a | b, VEC(0x13351335)); OIIO_CHECK_SIMD_EQUAL (a ^ b, VEC(0x03250325)); OIIO_CHECK_SIMD_EQUAL (~(a), VEC(0xedcbedcb)); OIIO_CHECK_SIMD_EQUAL (andnot (b, a), (~(b)) & a); OIIO_CHECK_SIMD_EQUAL (andnot (b, a), VEC(0x02240224)); OIIO_CHECK_EQUAL (reduce_and(mkvec(15, 7, 15, 15, 15, 15, 15, 15)), 7); OIIO_CHECK_EQUAL (reduce_or(mkvec(0, 3, 4, 0, 0, 0, 0, 0)), 7); #if OIIO_CPLUSPLUS_VERSION >= 11 /* So easy with lambdas */ benchmark2 ("operator&", do_and, a, b); benchmark2 ("operator|", do_or, a, b); benchmark2 ("operator^", do_xor, a, b); benchmark ("operator!", do_compl, a); benchmark2 ("andnot", do_andnot, a, b); benchmark ("reduce_and", [](VEC& a){ return reduce_and(a); }, a); benchmark ("reduce_or ", [](VEC& a){ return reduce_or(a); }, a); #endif } template void test_bitwise_bool () { typedef int ELEM; test_heading ("bitwise ", VEC::type_name()); bool A[] = { true, true, false, false, false, false, true, true }; bool B[] = { true, false, true, false, true, false, true, false }; bool AND[] = { true, false, false, false, false, false, true, false }; bool OR[] = { true, true, true, false, true, false, true, true }; bool XOR[] = { false, true, true, false, true, false, false, true }; bool NOT[] = { false, false, true, true, true, true, false, false }; VEC a(A), b(B), rand(AND), ror(OR), rxor(XOR), rnot(NOT); OIIO_CHECK_SIMD_EQUAL (a & b, rand); OIIO_CHECK_SIMD_EQUAL (a | b, ror); OIIO_CHECK_SIMD_EQUAL (a ^ b, rxor); OIIO_CHECK_SIMD_EQUAL (~a, rnot); OIIO_CHECK_EQUAL (reduce_or(mkvec(0, 0, 0, 0, 0, 0, 0, 0)), false); OIIO_CHECK_EQUAL (reduce_or(mkvec(0, 1, 0, 0, 0, 0, 0, 0)), true); OIIO_CHECK_EQUAL (reduce_and(mkvec(1, 1, 1, 1, 1, 1, 1, 1)), true); OIIO_CHECK_EQUAL (reduce_and(mkvec(0, 1, 0, 0, 0, 0, 0, 0)), false); #if OIIO_CPLUSPLUS_VERSION >= 11 /* So easy with lambdas */ benchmark2 ("operator&", do_and, a, b); benchmark2 ("operator|", do_or, a, b); benchmark2 ("operator^", do_xor, a, b); benchmark ("operator!", do_compl, a); benchmark ("reduce_and", [](VEC& a){ return reduce_and(a); }, a); benchmark ("reduce_or ", [](VEC& a){ return reduce_or(a); }, a); #endif } template B do_lt (const T& a, const T& b) { return a < b; } template B do_gt (const T& a, const T& b) { return a > b; } template B do_le (const T& a, const T& b) { return a <= b; } template B do_ge (const T& a, const T& b) { return a >= b; } template B do_eq (const T& a, const T& b) { return a == b; } template B do_ne (const T& a, const T& b) { return a != b; } template void test_comparisons () { typedef typename VEC::value_t ELEM; typedef typename VEC::bool_t bool_t; test_heading ("comparisons ", VEC::type_name()); VEC a = VEC::Iota(); bool lt2[] = { 1, 1, 0, 0, 0, 0, 0, 0 }; bool gt2[] = { 0, 0, 0, 1, 1, 1, 1, 1 }; bool le2[] = { 1, 1, 1, 0, 0, 0, 0, 0 }; bool ge2[] = { 0, 0, 1, 1, 1, 1, 1, 1 }; bool eq2[] = { 0, 0, 1, 0, 0, 0, 0, 0 }; bool ne2[] = { 1, 1, 0, 1, 1, 1, 1, 1 }; OIIO_CHECK_SIMD_EQUAL ((a < 2), bool_t(lt2)); OIIO_CHECK_SIMD_EQUAL ((a > 2), bool_t(gt2)); OIIO_CHECK_SIMD_EQUAL ((a <= 2), bool_t(le2)); OIIO_CHECK_SIMD_EQUAL ((a >= 2), bool_t(ge2)); OIIO_CHECK_SIMD_EQUAL ((a == 2), bool_t(eq2)); OIIO_CHECK_SIMD_EQUAL ((a != 2), bool_t(ne2)); VEC b (ELEM(2)); OIIO_CHECK_SIMD_EQUAL ((a < b), bool_t(lt2)); OIIO_CHECK_SIMD_EQUAL ((a > b), bool_t(gt2)); OIIO_CHECK_SIMD_EQUAL ((a <= b), bool_t(le2)); OIIO_CHECK_SIMD_EQUAL ((a >= b), bool_t(ge2)); OIIO_CHECK_SIMD_EQUAL ((a == b), bool_t(eq2)); OIIO_CHECK_SIMD_EQUAL ((a != b), bool_t(ne2)); #if OIIO_CPLUSPLUS_VERSION >= 11 /* So easy with lambdas */ benchmark2 ("operator< ", do_lt, a, b); benchmark2 ("operator> ", do_gt, a, b); benchmark2 ("operator<=", do_le, a, b); benchmark2 ("operator>=", do_ge, a, b); benchmark2 ("operator==", do_eq, a, b); benchmark2 ("operator!=", do_ne, a, b); #endif } template void test_shuffle4 () { test_heading ("shuffle ", VEC::type_name()); VEC a (0, 1, 2, 3); OIIO_CHECK_SIMD_EQUAL ((shuffle<3,2,1,0>(a)), VEC(3,2,1,0)); OIIO_CHECK_SIMD_EQUAL ((shuffle<0,0,2,2>(a)), VEC(0,0,2,2)); OIIO_CHECK_SIMD_EQUAL ((shuffle<1,1,3,3>(a)), VEC(1,1,3,3)); OIIO_CHECK_SIMD_EQUAL ((shuffle<0,1,0,1>(a)), VEC(0,1,0,1)); OIIO_CHECK_SIMD_EQUAL ((shuffle<2>(a)), VEC(2)); #if OIIO_CPLUSPLUS_VERSION >= 11 /* So easy with lambdas */ benchmark ("shuffle<...> ", [&](VEC& v){ return shuffle<3,2,1,0>(v); }, a); benchmark ("shuffle<0> ", [&](VEC& v){ return shuffle<0>(v); }, a); benchmark ("shuffle<1> ", [&](VEC& v){ return shuffle<1>(v); }, a); benchmark ("shuffle<2> ", [&](VEC& v){ return shuffle<2>(v); }, a); benchmark ("shuffle<3> ", [&](VEC& v){ return shuffle<3>(v); }, a); #endif } template void test_shuffle8 () { test_heading ("shuffle ", VEC::type_name()); VEC a (0, 1, 2, 3, 4, 5, 6, 7); OIIO_CHECK_SIMD_EQUAL ((shuffle<3,2,1,0,3,2,1,0>(a)), VEC(3,2,1,0,3,2,1,0)); OIIO_CHECK_SIMD_EQUAL ((shuffle<0,0,2,2,0,0,2,2>(a)), VEC(0,0,2,2,0,0,2,2)); OIIO_CHECK_SIMD_EQUAL ((shuffle<1,1,3,3,1,1,3,3>(a)), VEC(1,1,3,3,1,1,3,3)); OIIO_CHECK_SIMD_EQUAL ((shuffle<0,1,0,1,0,1,0,1>(a)), VEC(0,1,0,1,0,1,0,1)); OIIO_CHECK_SIMD_EQUAL ((shuffle<2>(a)), VEC(2)); #if OIIO_CPLUSPLUS_VERSION >= 11 /* So easy with lambdas */ benchmark ("shuffle<...> ", [&](VEC& v){ return shuffle<7,6,5,4,3,2,1,0>(v); }, a); benchmark ("shuffle<0> ", [&](VEC& v){ return shuffle<0>(v); }, a); benchmark ("shuffle<1> ", [&](VEC& v){ return shuffle<1>(v); }, a); benchmark ("shuffle<2> ", [&](VEC& v){ return shuffle<2>(v); }, a); benchmark ("shuffle<3> ", [&](VEC& v){ return shuffle<3>(v); }, a); benchmark ("shuffle<4> ", [&](VEC& v){ return shuffle<4>(v); }, a); benchmark ("shuffle<5> ", [&](VEC& v){ return shuffle<5>(v); }, a); benchmark ("shuffle<6> ", [&](VEC& v){ return shuffle<6>(v); }, a); benchmark ("shuffle<7> ", [&](VEC& v){ return shuffle<7>(v); }, a); #endif } template void test_swizzle () { test_heading ("swizzle ", VEC::type_name()); VEC a = VEC::Iota(0); VEC b = VEC::Iota(10); OIIO_CHECK_SIMD_EQUAL (AxyBxy(a,b), VEC(0,1,10,11)); OIIO_CHECK_SIMD_EQUAL (AxBxAyBy(a,b), VEC(0,10,1,11)); OIIO_CHECK_SIMD_EQUAL (b.xyz0(), VEC(10,11,12,0)); OIIO_CHECK_SIMD_EQUAL (b.xyz1(), VEC(10,11,12,1)); } template void test_blend () { test_heading ("blend ", VEC::type_name()); typedef typename VEC::value_t ELEM; typedef typename VEC::bool_t bool_t; VEC a = VEC::Iota (1); VEC b = VEC::Iota (10); bool_t f(false), t(true); bool tf_values[] = { true, false, true, false, true, false, true, false }; bool_t tf ((bool *)tf_values); OIIO_CHECK_SIMD_EQUAL (blend (a, b, f), a); OIIO_CHECK_SIMD_EQUAL (blend (a, b, t), b); ELEM r1[] = { 10, 2, 12, 4, 14, 6, 16, 8 }; OIIO_CHECK_SIMD_EQUAL (blend (a, b, tf), VEC(r1)); OIIO_CHECK_SIMD_EQUAL (blend0 (a, f), VEC::Zero()); OIIO_CHECK_SIMD_EQUAL (blend0 (a, t), a); ELEM r2[] = { 1, 0, 3, 0, 5, 0, 7, 0 }; OIIO_CHECK_SIMD_EQUAL (blend0 (a, tf), VEC(r2)); OIIO_CHECK_SIMD_EQUAL (blend0not (a, f), a); OIIO_CHECK_SIMD_EQUAL (blend0not (a, t), VEC::Zero()); ELEM r3[] = { 0, 2, 0, 4, 0, 6, 0, 8 }; OIIO_CHECK_SIMD_EQUAL (blend0not (a, tf), VEC(r3)); #if OIIO_CPLUSPLUS_VERSION >= 11 /* So easy with lambdas */ benchmark2 ("blend", [&](VEC& a, VEC& b){ return blend(a,b,tf); }, a, b); benchmark2 ("blend0", [](VEC& a, bool_t& b){ return blend0(a,b); }, a, tf); benchmark2 ("blend0not", [](VEC& a, bool_t& b){ return blend0not(a,b); }, a, tf); #endif } template void test_transpose4 () { test_heading ("transpose ", VEC::type_name()); VEC a (0, 1, 2, 3); VEC b (4, 5, 6, 7); VEC c (8, 9, 10, 11); VEC d (12, 13, 14, 15); OIIO_CHECK_SIMD_EQUAL (AxBxCxDx(a,b,c,d), VEC(0,4,8,12)); std::cout << " before transpose:\n"; std::cout << "\t" << a << "\n"; std::cout << "\t" << b << "\n"; std::cout << "\t" << c << "\n"; std::cout << "\t" << d << "\n"; transpose (a, b, c, d); std::cout << " after transpose:\n"; std::cout << "\t" << a << "\n"; std::cout << "\t" << b << "\n"; std::cout << "\t" << c << "\n"; std::cout << "\t" << d << "\n"; OIIO_CHECK_SIMD_EQUAL (a, VEC(0,4,8,12)); OIIO_CHECK_SIMD_EQUAL (b, VEC(1,5,9,13)); OIIO_CHECK_SIMD_EQUAL (c, VEC(2,6,10,14)); OIIO_CHECK_SIMD_EQUAL (d, VEC(3,7,11,15)); } template inline T do_shl (const T &a, int b) { return a< inline T do_shr (const T &a, int b) { return a>>b; } template inline T do_srl (const T &a, int b) { return srl(a,b); } template void test_shift () { test_heading ("shift ", VEC::type_name()); typedef typename VEC::value_t ELEM; // Basics of << and >> VEC i = VEC::Iota (10, 10); // 10, 20, 30 ... OIIO_CHECK_SIMD_EQUAL (i << 2, VEC::Iota(40, 40)); OIIO_CHECK_SIMD_EQUAL (i >> 1, VEC::Iota(5, 5)); // Tricky cases with high bits, and the difference between >> and srl int a = 1<<31, b = -1, c = 0xffff, d = 3; VEC hard = mkvec (a, b, c, d, d, c, b, a); OIIO_CHECK_SIMD_EQUAL (hard >> 1, mkvec(a>>1, b>>1, c>>1, d>>1, d>>1, c>>1, b>>1, a>>1)); OIIO_CHECK_SIMD_EQUAL (srl(hard,1), mkvec(unsigned(a)>>1, unsigned(b)>>1, unsigned(c)>>1, unsigned(d)>>1, unsigned(d)>>1, unsigned(c)>>1, unsigned(b)>>1, unsigned(a)>>1)); std::cout << Strutil::format (" [%x] >> 1 == [%x]\n", hard, hard>>1); std::cout << Strutil::format (" [%x] srl 1 == [%x]\n", hard, srl(hard,1)); OIIO_CHECK_SIMD_EQUAL (hard >> 4, mkvec(a>>4, b>>4, c>>4, d>>4, d>>4, c>>4, b>>4, a>>4)); OIIO_CHECK_SIMD_EQUAL (srl(hard,4), mkvec(unsigned(a)>>4, unsigned(b)>>4, unsigned(c)>>4, unsigned(d)>>4, unsigned(d)>>4, unsigned(c)>>4, unsigned(b)>>4, unsigned(a)>>4)); std::cout << Strutil::format (" [%x] >> 4 == [%x]\n", hard, hard>>4); std::cout << Strutil::format (" [%x] srl 4 == [%x]\n", hard, srl(hard,4)); // Test <<= and >>= i = VEC::Iota (10, 10); i <<= 2; OIIO_CHECK_SIMD_EQUAL (i, VEC::Iota(40, 40)); i = VEC::Iota (10, 10); i >>= 1; OIIO_CHECK_SIMD_EQUAL (i, VEC::Iota(5, 5)); #if OIIO_CPLUSPLUS_VERSION >= 11 /* So easy with lambdas */ // Benchmark benchmark2 ("operator<<", do_shl, i, 2); benchmark2 ("operator>>", do_shr, i, 2); benchmark2 ("srl ", do_srl, i, 2); #endif } void test_vectorops_float4 () { typedef float4 VEC; typedef VEC::value_t ELEM; test_heading ("vectorops ", VEC::type_name()); VEC a = mkvec (10, 11, 12, 13); VEC b = mkvec (1, 2, 3, 4); OIIO_CHECK_EQUAL (dot(a,b), ELEM(10+22+36+52)); OIIO_CHECK_EQUAL (dot3(a,b), ELEM(10+22+36)); OIIO_CHECK_SIMD_EQUAL (vdot(a,b), VEC(10+22+36+52)); OIIO_CHECK_SIMD_EQUAL (vdot3(a,b), VEC(10+22+36)); OIIO_CHECK_SIMD_EQUAL (hdiv(float4(1.0f,2.0f,3.0f,2.0f)), float3(0.5f,1.0f,1.5f)); #if OIIO_CPLUSPLUS_VERSION >= 11 /* So easy with lambdas */ benchmark2 ("vdot", [](VEC& a, VEC& b){ return vdot(a,b); }, a, b); benchmark2 ("dot", [](VEC& a, VEC& b){ return dot(a,b); }, a, b); benchmark2 ("vdot3", [](VEC& a, VEC& b){ return vdot3(a,b); }, a, b); benchmark2 ("dot3", [](VEC& a, VEC& b){ return dot3(a,b); }, a, b); #endif } void test_vectorops_float3 () { typedef float3 VEC; typedef VEC::value_t ELEM; test_heading ("vectorops ", VEC::type_name()); VEC a = mkvec (10, 11, 12); VEC b = mkvec (1, 2, 3); OIIO_CHECK_EQUAL (dot(a,b), ELEM(10+22+36)); OIIO_CHECK_EQUAL (dot3(a,b), ELEM(10+22+36)); OIIO_CHECK_SIMD_EQUAL (vdot(a,b), VEC(10+22+36)); OIIO_CHECK_SIMD_EQUAL (vdot3(a,b), VEC(10+22+36)); OIIO_CHECK_SIMD_EQUAL (float3(1.0f,2.0f,3.0f).normalized(), float3(norm_imath(Imath::V3f(1.0f,2.0f,3.0f)))); OIIO_CHECK_SIMD_EQUAL_THRESH (float3(1.0f,2.0f,3.0f).normalized_fast(), float3(norm_imath(Imath::V3f(1.0f,2.0f,3.0f))), 0.0005); #if OIIO_CPLUSPLUS_VERSION >= 11 /* So easy with lambdas */ benchmark2 ("vdot", [](VEC& a, VEC& b){ return vdot(a,b); }, a, b); benchmark2 ("dot", [](VEC& a, VEC& b){ return dot(a,b); }, a, b); benchmark ("dot float3", dot_simd, float3(2.0f,1.0f,0.0f), 1); // benchmark2 ("dot Imath::V3f", [](Imath::V3f& a, Imath::V3f& b){ return a.dot(b); }, a.V3f(), b.V3f()); benchmark ("dot Imath::V3f", dot_imath, Imath::V3f(2.0f,1.0f,0.0f), 1); benchmark ("dot Imath::V3f with simd", dot_imath_simd, Imath::V3f(2.0f,1.0f,0.0f), 1); benchmark ("normalize Imath", norm_imath, Imath::V3f(1.0f,4.0f,9.0f)); benchmark ("normalize Imath with simd", norm_imath_simd, Imath::V3f(1.0f,4.0f,9.0f)); benchmark ("normalize Imath with simd fast", norm_imath_simd_fast, Imath::V3f(1.0f,4.0f,9.0f)); benchmark ("normalize simd", norm_simd, float3(1.0f,4.0f,9.0f)); benchmark ("normalize simd fast", norm_simd_fast, float3(1.0f,4.0f,9.0f)); #endif } void test_constants () { test_heading ("constants"); OIIO_CHECK_SIMD_EQUAL (bool4::False(), bool4(false)); OIIO_CHECK_SIMD_EQUAL (bool4::True(), bool4(true)); OIIO_CHECK_SIMD_EQUAL (int4::Zero(), int4(0)); OIIO_CHECK_SIMD_EQUAL (int4::One(), int4(1)); OIIO_CHECK_SIMD_EQUAL (int4::NegOne(), int4(-1)); OIIO_CHECK_SIMD_EQUAL (float4::Zero(), float4(0.0f)); OIIO_CHECK_SIMD_EQUAL (float4::One(), float4(1.0f)); OIIO_CHECK_SIMD_EQUAL (float4::Iota(), float4(0,1,2,3)); OIIO_CHECK_SIMD_EQUAL (float4::Iota(3.0f), float4(3,4,5,6)); OIIO_CHECK_SIMD_EQUAL (float4::Iota(3.0f,2.0f), float4(3,5,7,9)); OIIO_CHECK_SIMD_EQUAL (float3::Zero(), float3(0.0f)); OIIO_CHECK_SIMD_EQUAL (float3::One(), float3(1.0f)); OIIO_CHECK_SIMD_EQUAL (float3::Iota(), float3(0,1,2)); OIIO_CHECK_SIMD_EQUAL (float3::Iota(3.0f), float3(3,4,5)); OIIO_CHECK_SIMD_EQUAL (float3::Iota(3.0f,2.0f), float3(3,5,7)); OIIO_CHECK_SIMD_EQUAL (float8::Zero(), float8(0.0f)); OIIO_CHECK_SIMD_EQUAL (float8::One(), float8(1.0f)); OIIO_CHECK_SIMD_EQUAL (float8::Iota(), float8(0,1,2,3,4,5,6,7)); OIIO_CHECK_SIMD_EQUAL (float8::Iota(3.0f), float8(3,4,5,6,7,8,9,10)); OIIO_CHECK_SIMD_EQUAL (float8::Iota(3.0f,2.0f), float8(3,5,7,9,11,13,15,17)); #if OIIO_CPLUSPLUS_VERSION >= 11 /* So easy with lambdas */ benchmark ("float4 = float(const)", [](float f){ return float4(f); }, 1.0f); benchmark ("float4 = Zero()", [](int){ return float4::Zero(); }, 0); benchmark ("float4 = One()", [](int){ return float4::One(); }, 0); benchmark ("float4 = Iota()", [](int){ return float4::Iota(); }, 0); benchmark ("float8 = float(const)", [](float f){ return float8(f); }, 1.0f); benchmark ("float8 = Zero()", [](int){ return float8::Zero(); }, 0); benchmark ("float8 = One()", [](int){ return float8::One(); }, 0); benchmark ("float8 = Iota()", [](int){ return float8::Iota(); }, 0); #endif } // Miscellaneous one-off stuff not caught by other tests void test_special () { test_heading ("special"); { // Make sure a float4 constructed from saturated unsigned short, // short, unsigned char, or char values, then divided by the float // max, exactly equals 1.0. short s32767[] = {32767, 32767, 32767, 32767}; unsigned short us65535[] = {65535, 65535, 65535, 65535}; char c127[] = {127, 127, 127, 127}; unsigned char uc255[] = {255, 255, 255, 255}; OIIO_CHECK_SIMD_EQUAL (float4(us65535)/float4(65535.0), float4(1.0f)); OIIO_CHECK_SIMD_EQUAL (float4(us65535)*float4(1.0f/65535.0), float4(1.0f)); OIIO_CHECK_SIMD_EQUAL (float4(s32767)/float4(32767.0), float4(1.0f)); OIIO_CHECK_SIMD_EQUAL (float4(s32767)*float4(1.0f/32767.0), float4(1.0f)); OIIO_CHECK_SIMD_EQUAL (float4(uc255)/float4(255.0), float4(1.0f)); OIIO_CHECK_SIMD_EQUAL (float4(uc255)*float4(1.0f/255.0), float4(1.0f)); OIIO_CHECK_SIMD_EQUAL (float4(c127)/float4(127.0), float4(1.0f)); OIIO_CHECK_SIMD_EQUAL (float4(c127)*float4(1.0f/127.0), float4(1.0f)); } } // Wrappers to resolve the return type ambiguity inline float fast_exp_float (float x) { return fast_exp(x); } inline float4 fast_exp_float4 (const float4& x) { return fast_exp(x); } inline float fast_log_float (float x) { return fast_log(x); } //inline float4 fast_log_float (const float4& x) { return fast_log(x); } inline float rsqrtf (float f) { return 1.0f / sqrtf(f); } template void test_mathfuncs () { test_heading ("mathfuncs", VEC::type_name()); typedef typename VEC::value_t ELEM; VEC A = mkvec (-1.0f, 0.0f, 1.0f, 4.5f); VEC expA = mkvec (0.367879441171442f, 1.0f, 2.718281828459045f, 90.0171313005218f); OIIO_CHECK_SIMD_EQUAL (exp(A), expA); OIIO_CHECK_SIMD_EQUAL_THRESH (log(expA), A, 1e-6f); OIIO_CHECK_SIMD_EQUAL (fast_exp(A), mkvec(fast_exp(A[0]), fast_exp(A[1]), fast_exp(A[2]), fast_exp(A[3]))); OIIO_CHECK_SIMD_EQUAL (fast_log(expA), mkvec(fast_log(expA[0]), fast_log(expA[1]), fast_log(expA[2]), fast_log(expA[3]))); OIIO_CHECK_SIMD_EQUAL_THRESH (fast_pow_pos(VEC(2.0f), A), mkvec(0.5f, 1.0f, 2.0f, 22.62741699796952f), 0.0001f); OIIO_CHECK_SIMD_EQUAL (safe_div(mkvec(1.0f,2.0f,3.0f,4.0f), mkvec(2.0f,0.0f,2.0f,0.0f)), mkvec(0.5f,0.0f,1.5f,0.0f)); OIIO_CHECK_SIMD_EQUAL (sqrt(mkvec(1.0f,4.0f,9.0f,16.0f)), mkvec(1.0f,2.0f,3.0f,4.0f)); OIIO_CHECK_SIMD_EQUAL (rsqrt(mkvec(1.0f,4.0f,9.0f,16.0f)), VEC(1.0f)/mkvec(1.0f,2.0f,3.0f,4.0f)); OIIO_CHECK_SIMD_EQUAL_THRESH (rsqrt_fast(mkvec(1.0f,4.0f,9.0f,16.0f)), VEC(1.0f)/mkvec(1.0f,2.0f,3.0f,4.0f), 0.0005f); #if OIIO_CPLUSPLUS_VERSION >= 11 /* So easy with lambdas */ benchmark2 ("operator/", do_div, A, A); benchmark2 ("safe_div", do_safe_div, A, A); benchmark ("float expf", expf, 0.67f); benchmark ("fast_exp", fast_exp_float, 0.67f); benchmark ("simd::exp", [](VEC& v){ return simd::exp(v); }, VEC(0.67f)); benchmark ("simd::fast_exp", [](VEC& v){ return fast_exp(v); }, VEC(0.67f)); benchmark ("float logf", logf, 0.67f); benchmark ("fast_log", fast_log_float, 0.67f); benchmark ("simd::log", [](VEC& v){ return simd::log(v); }, VEC(0.67f)); benchmark ("simd::fast_log", fast_log, VEC(0.67f)); benchmark2 ("float powf", powf, 0.67f, 0.67f); benchmark2 ("simd fast_pow_pos", [](VEC& x,VEC& y){ return fast_pow_pos(x,y); }, VEC(0.67f), VEC(0.67f)); benchmark ("float sqrt", sqrtf, 4.0f); benchmark ("simd::sqrt", [](VEC& v){ return sqrt(v); }, mkvec(1.0f,4.0f,9.0f,16.0f)); benchmark ("float rsqrt", rsqrtf, 4.0f); benchmark ("simd::rsqrt", [](VEC& v){ return rsqrt(v); }, mkvec(1.0f,4.0f,9.0f,16.0f)); benchmark ("simd::rsqrt_fast", [](VEC& v){ return rsqrt_fast(v); }, mkvec(1.0f,4.0f,9.0f,16.0f)); #endif } void test_metaprogramming () { test_heading ("metaprogramming"); OIIO_CHECK_EQUAL (SimdSize::size, 4); OIIO_CHECK_EQUAL (SimdSize::size, 4); OIIO_CHECK_EQUAL (SimdSize::size, 4); OIIO_CHECK_EQUAL (SimdSize::size, 4); OIIO_CHECK_EQUAL (SimdSize::size, 1); OIIO_CHECK_EQUAL (SimdSize::size, 1); OIIO_CHECK_EQUAL (SimdElements::size, 4); OIIO_CHECK_EQUAL (SimdElements::size, 3); OIIO_CHECK_EQUAL (SimdElements::size, 4); OIIO_CHECK_EQUAL (SimdElements::size, 4); OIIO_CHECK_EQUAL (SimdElements::size, 1); OIIO_CHECK_EQUAL (SimdElements::size, 1); OIIO_CHECK_EQUAL (float4::elements, 4); OIIO_CHECK_EQUAL (float3::elements, 3); OIIO_CHECK_EQUAL (int4::elements, 4); OIIO_CHECK_EQUAL (bool4::elements, 4); // OIIO_CHECK_EQUAL (is_same::value, true); // OIIO_CHECK_EQUAL (is_same::value, true); // OIIO_CHECK_EQUAL (is_same::value, true); // OIIO_CHECK_EQUAL (is_same::value, true); } // Transform a point by a matrix using regular Imath inline Imath::V3f transformp_imath (const Imath::V3f &v, const Imath::M44f &m) { Imath::V3f r; m.multVecMatrix (v, r); return r; } // Transform a point by a matrix using simd ops on Imath types. inline Imath::V3f transformp_imath_simd (const Imath::V3f &v, const Imath::M44f &m) { return simd::transformp(m,v).V3f(); } // Transform a simd point by an Imath matrix using SIMD inline float3 transformp_simd (const float3 &v, const Imath::M44f &m) { return simd::transformp (m, v); } // Transform a point by a matrix using regular Imath inline Imath::V3f transformv_imath (const Imath::V3f &v, const Imath::M44f &m) { Imath::V3f r; m.multDirMatrix (v, r); return r; } inline bool mx_equal_thresh (const matrix44 &a, const matrix44 &b, float thresh) { for (int j = 0; j < 4; ++j) for (int i = 0; i < 4; ++i) if (fabsf(a[j][i] - b[j][i]) > thresh) return false; return true; } inline Imath::M44f mat_transpose (const Imath::M44f &m) { return m.transposed(); } inline Imath::M44f mat_transpose_simd (const Imath::M44f &m) { return matrix44(m).transposed().M44f(); } void test_matrix () { Imath::V3f P (1.0f, 0.0f, 0.0f); Imath::M44f Mtrans (1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 11, 12, 1); Imath::M44f Mrot = Imath::M44f().rotate(Imath::V3f(0.0f, M_PI_2, 0.0f)); test_heading ("Testing matrix ops:"); std::cout << " P = " << P << "\n"; std::cout << " Mtrans = " << Mtrans << "\n"; std::cout << " Mrot = " << Mrot << "\n"; OIIO_CHECK_EQUAL (simd::transformp(Mtrans, P).V3f(), transformp_imath(P, Mtrans)); std::cout << " P translated = " << simd::transformp(Mtrans,P) << "\n"; OIIO_CHECK_EQUAL (simd::transformv(Mtrans,P).V3f(), P); OIIO_CHECK_EQUAL (simd::transformp(Mrot, P).V3f(), transformp_imath(P, Mrot)); std::cout << " P rotated = " << simd::transformp(Mrot,P) << "\n"; OIIO_CHECK_EQUAL (simd::transformvT(Mrot, P).V3f(), transformv_imath(P, Mrot.transposed())); std::cout << " P rotated by the transpose = " << simd::transformv(Mrot,P) << "\n"; OIIO_CHECK_EQUAL (matrix44(Mrot).transposed().M44f(), Mrot.transposed()); std::cout << " Mrot transposed = " << matrix44(Mrot).transposed().M44f() << "\n"; { matrix44 mt (Mtrans), mr (Mrot); OIIO_CHECK_EQUAL (mt, mt); OIIO_CHECK_EQUAL (mt, Mtrans); OIIO_CHECK_EQUAL (Mtrans, mt); OIIO_CHECK_NE (mt, mr); OIIO_CHECK_NE (mr, Mtrans); OIIO_CHECK_NE (Mtrans, mr); } OIIO_CHECK_ASSERT (mx_equal_thresh (Mtrans.inverse(), matrix44(Mtrans).inverse(), 1.0e-6f)); OIIO_CHECK_ASSERT (mx_equal_thresh (Mrot.inverse(), matrix44(Mrot).inverse(), 1.0e-6f)); #if OIIO_CPLUSPLUS_VERSION >= 11 /* So easy with lambdas */ Imath::V3f vx (2.51f,1.0f,1.0f); Imath::M44f mx (1,0,0,0, 0,1,0,0, 0,0,1,0, 10,11,12,1); benchmark2 ("transformp Imath", transformp_imath, vx, mx, 1); benchmark2 ("transformp Imath with simd", transformp_imath_simd, vx, mx, 1); benchmark2 ("transformp simd", transformp_simd, float3(vx), mx, 1); benchmark ("transpose m44", mat_transpose, mx, 1); benchmark ("transpose m44 with simd", mat_transpose_simd, mx, 1); // Reduce the iterations of the ones below, if we can iterations /= 2; benchmark ("m44 inverse Imath", inverse_imath, mx, 1); // std::cout << "inv " << matrix44(inverse_imath(mx)) << "\n"; benchmark ("m44 inverse_simd", inverse_simd, mx, 1); // std::cout << "inv " << inverse_simd(mx) << "\n"; benchmark ("m44 inverse_simd native simd", inverse_simd, matrix44(mx), 1); // std::cout << "inv " << inverse_simd(mx) << "\n"; iterations *= 2; // put things the way they were #endif } int main (int argc, char *argv[]) { #if !defined(NDEBUG) || defined(OIIO_CI) || defined(OIIO_CODECOV) // For the sake of test time, reduce the default iterations for DEBUG, // CI, and code coverage builds. Explicit use of --iters or --trials // will override this, since it comes before the getargs() call. iterations /= 10; ntrials = 1; #endif for (int i = 0; i < 16; ++i) { dummy_float[i] = 1.0f; dummy_int[i] = 1; } getargs (argc, argv); #if defined(OIIO_SIMD_AVX) std::cout << "SIMD is AVX " << OIIO_SIMD_AVX << "\n"; #elif defined(OIIO_SIMD_SSE) std::cout << "SIMD is SSE " << OIIO_SIMD_SSE << "\n"; #elif defined(OIIO_SIMD_NEON) std::cout << "SIMD is NEON " << OIIO_SIMD_NEON << "\n"; #else std::cout << "NO SIMD!!\n"; #endif Timer timer; int4 dummy4(0); int8 dummy8(0); #if OIIO_CPLUSPLUS_VERSION >= 11 /* So easy with lambdas */ benchmark ("null benchmark 4", [](const int4&){ return int(0); }, dummy4); benchmark ("null benchmark 8", [](const int8&){ return int(0); }, dummy8); #endif category_heading ("float4"); test_loadstore (); test_component_access (); test_arithmetic (); test_comparisons (); test_shuffle4 (); test_swizzle (); test_blend (); test_transpose4 (); test_vectorops_float4 (); test_fused (); test_mathfuncs(); category_heading ("float3"); test_loadstore (); test_component_access (); test_arithmetic (); // Unnecessary to test these, they just use the float4 ops. // test_comparisons (); // test_shuffle4 (); // test_swizzle (); // test_blend (); // test_transpose4 (); test_vectorops_float3 (); test_fused (); // test_mathfuncs(); category_heading ("float8"); test_loadstore (); test_component_access (); test_arithmetic (); test_comparisons (); test_shuffle8 (); test_blend (); test_fused (); test_mathfuncs(); category_heading ("int4"); test_loadstore (); test_component_access (); test_arithmetic (); test_bitwise_int (); test_comparisons (); test_shuffle4 (); test_blend (); test_vint_to_uint16s (); test_vint_to_uint8s (); test_shift (); test_transpose4 (); category_heading ("int8"); test_loadstore (); test_component_access (); test_arithmetic (); test_bitwise_int (); test_comparisons (); test_shuffle8 (); test_blend (); test_vint_to_uint16s (); test_vint_to_uint8s (); test_shift (); category_heading ("bool4"); test_shuffle4 (); test_component_access (); test_bitwise_bool (); category_heading ("bool8"); test_shuffle8 (); test_component_access (); test_bitwise_bool (); category_heading ("Odds and ends"); test_constants(); test_special(); test_metaprogramming(); test_matrix(); std::cout << "\nTotal time: " << Strutil::timeintervalformat(timer()) << "\n"; if (unit_test_failures) std::cout << term.ansi ("red", "ERRORS!\n"); else std::cout << term.ansi ("green", "OK\n"); return unit_test_failures; } openimageio-1.7.17~dfsg0.orig/src/libutil/sysutil.cpp0000644000175000017500000003554113151711064021010 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #ifdef __linux__ # include # include # include #endif #if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) # include # include # include # include # include # include #endif #ifdef __APPLE__ # include # include # include # include # include # include #endif #include "OpenImageIO/platform.h" #ifdef _WIN32 # define WIN32_LEAN_AND_MEAN # define DEFINE_CONSOLEV2_PROPERTIES # include # include # include # include #else # include #endif #ifdef __GNU__ # include # include #endif #include #include #include #include #include OIIO_NAMESPACE_BEGIN using namespace Sysutil; size_t Sysutil::memory_used (bool resident) { #if defined(__linux__) #if 0 // doesn't seem to work? struct rusage ru; int ret = getrusage (RUSAGE_SELF, &ru); return (size_t)ru.ru_maxrss * (size_t)1024; #else // Ugh, getrusage doesn't work well on Linux. Try grabbing info // directly from the /proc pseudo-filesystem. Reading from // /proc/self/statm gives info on your own process, as one line of // numbers that are: virtual mem program size, resident set size, // shared pages, text/code, data/stack, library, dirty pages. The // mem sizes should all be multiplied by the page size. size_t size = 0; FILE *file = fopen("/proc/self/statm", "r"); if (file) { unsigned long vm = 0, rss = 0; int n = fscanf (file, "%lu %lu", &vm, &rss); if (n == 2) size = size_t(resident ? rss : vm); size *= getpagesize(); fclose (file); } return size; #endif #elif defined(__APPLE__) // Inspired by: // http://miknight.blogspot.com/2005/11/resident-set-size-in-mac-os-x.html struct task_basic_info t_info; mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; task_info(current_task(), TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count); size_t size = (resident ? t_info.resident_size : t_info.virtual_size); return size; #elif defined(_WIN32) // According to MSDN... PROCESS_MEMORY_COUNTERS counters; if (GetProcessMemoryInfo (GetCurrentProcess(), &counters, sizeof (counters))) return counters.PagefileUsage; else return 0; #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) // FIXME -- does somebody know a good method for figuring this out for // FreeBSD? return 0; // Punt #else // No idea what platform this is ASSERT (0 && "Need to implement Sysutil::memory_used on this platform"); return 0; // Punt #endif } size_t Sysutil::physical_memory () { #if defined(__linux__) size_t size = 0; FILE *file = fopen ("/proc/meminfo", "r"); if (file) { char buf[1024]; while (fgets (buf, sizeof(buf), file)) { if (! strncmp (buf, "MemTotal:", 9)) { size = 1024 * (size_t) strtol (buf+9, NULL, 10); break; } } fclose (file); } return size; #elif defined(__APPLE__) // man 3 sysctl ...or... // https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/sysctl.3.html // http://stackoverflow.com/questions/583736/determine-physical-mem-size-programmatically-on-osx int mib[2] = { CTL_HW, HW_MEMSIZE }; int64_t physical_memory; size_t length = sizeof(physical_memory); sysctl (mib, 2, &physical_memory, &length, NULL, 0); return size_t(physical_memory); #elif defined(_WIN32) // According to MSDN // (http://msdn.microsoft.com/en-us/library/windows/desktop/aa366589(v=vs.85).aspx MEMORYSTATUSEX statex; statex.dwLength = sizeof (statex); GlobalMemoryStatusEx (&statex); return size_t (statex.ullTotalPhys); // Total physical memory // N.B. Other fields nice to know (in bytes, except for dwMemoryLoad): // statex.dwMemoryLoad Percent of memory in use // statex.ullAvailPhys Free physical memory // statex.ullTotalPageFile Total size of paging file // statex.ullAvailPageFile Free mem in paging file // statex.ullTotalVirtual Total virtual memory // statex.ullAvailVirtual Free virtual memory #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) // man 3 sysctl ...or... // http://www.freebsd.org/cgi/man.cgi?query=sysctl&sektion=3 // FIXME -- Does this accept a size_t? Or only an int? I can't // seem to find an online resource that indicates that FreeBSD has a // HW_MEMESIZE like Linux and OSX have, or a HW_PHYSMEM64 like // OpenBSD has. int mib[2] = { CTL_HW, HW_PHYSMEM }; size_t physical_memory; size_t length = sizeof(physical_memory); sysctl (mib, 2, &physical_memory, &length, NULL, 0); return physical_memory; #else // No idea what platform this is ASSERT (0 && "Need to implement Sysutil::physical_memory on this platform"); return 0; // Punt #endif } void Sysutil::get_local_time (const time_t *time, struct tm *converted_time) { #ifdef _MSC_VER localtime_s (converted_time, time); #else localtime_r (time, converted_time); #endif } std::string Sysutil::this_program_path () { char filename[10240]; filename[0] = 0; #if defined(__linux__) unsigned int size = sizeof(filename); int r = readlink ("/proc/self/exe", filename, size); ASSERT(r < int(size)); // user won't get the right answer if the filename is too long to store if (r > 0) filename[r] = 0; // readlink does not fill in the 0 byte #elif defined(__APPLE__) // For info: 'man 3 dyld' unsigned int size = sizeof(filename); int r = _NSGetExecutablePath (filename, &size); if (r == 0) r = size; #elif defined(_WIN32) // According to MSDN... unsigned int size = sizeof(filename); int r = GetModuleFileName (NULL, filename, size); #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PATHNAME; mib[3] = -1; size_t cb = sizeof(filename); int r=1; sysctl(mib, 4, filename, &cb, NULL, 0); #elif defined(__GNU__) || defined(__OpenBSD__) int r = 0; #else // No idea what platform this is OIIO_STATIC_ASSERT_MSG (0, "this_program_path() unimplemented on this platform"); #endif if (r > 0) return std::string (filename); return std::string(); // Couldn't figure it out } string_view Sysutil::getenv (string_view name) { return string_view (::getenv (name.c_str())); } void Sysutil::usleep (unsigned long useconds) { #ifdef _WIN32 Sleep (useconds/1000); // Win32 Sleep() is milliseconds, not micro #else ::usleep (useconds); // *nix usleep() is in microseconds #endif } int Sysutil::terminal_columns () { int columns = 80; // a decent guess, if we have nothing more to go on #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__GNU__) struct winsize w; ioctl (0, TIOCGWINSZ, &w); columns = w.ws_col; #elif defined(_WIN32) HANDLE h = GetStdHandle (STD_OUTPUT_HANDLE); if (h != INVALID_HANDLE_VALUE) { CONSOLE_SCREEN_BUFFER_INFO csbi = { { 0 } }; GetConsoleScreenBufferInfo (h, &csbi); columns = csbi.dwSize.X; } #endif return columns; } int Sysutil::terminal_rows () { int rows = 24; // a decent guess, if we have nothing more to go on #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__GNU__) struct winsize w; ioctl (0, TIOCGWINSZ, &w); rows = w.ws_row; #elif defined(_WIN32) HANDLE h = GetStdHandle (STD_OUTPUT_HANDLE); if (h != INVALID_HANDLE_VALUE) { CONSOLE_SCREEN_BUFFER_INFO csbi = { { 0 } }; GetConsoleScreenBufferInfo (h, &csbi); rows = csbi.dwSize.Y; } #endif return rows; } #ifdef _WIN32 int isatty(int fd) { return _isatty(fd); } #endif Term::Term (FILE *file) { m_is_console = isatty (fileno((file))); } #ifdef _WIN32 // from https://msdn.microsoft.com/fr-fr/library/windows/desktop/mt638032%28v=vs.85%29.aspx #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 #endif bool enableVTMode() { // Set output mode to handle virtual terminal sequences HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); if (hOut == INVALID_HANDLE_VALUE) { return false; } DWORD dwMode = 0; if (!GetConsoleMode(hOut, &dwMode)) { return false; } dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; if (!SetConsoleMode(hOut, dwMode)) { return false; } return true; } #else bool enableVTMode() { return true; } #endif Term::Term (const std::ostream &stream) { m_is_console = (&stream == &std::cout && isatty(fileno(stdout))) || (&stream == &std::cerr && isatty(fileno(stderr))) || (&stream == &std::clog && isatty(fileno(stderr))); if (is_console()) enableVTMode(); } std::string Term::ansi (string_view command) const { static const char *codes[] = { "default", "0", "normal", "0", "reset", "0", "bold", "1", "italic", "3", // Not widely supported, sometimes inverse "underscore", "4", "underline", "4", "blink", "5", "reverse", "7", "concealed", "8", "strike", "9", // Not widely supported "black", "30", "red", "31", "green", "32", "yellow", "33", "blue", "34", "magenta", "35", "cyan", "36", "white", "37", "black_bg", "40", "red_bg", "41", "green_bg", "42", "yellow_bg", "43", "blue_bg", "44", "magenta_bg", "45", "cyan_bg", "46", "white_bg", "47", NULL }; std::string ret; if (is_console()) { std::vector cmds; Strutil::split (command, cmds, ","); for (size_t c = 0; c < cmds.size(); ++c) { for (size_t i = 0; codes[i]; i += 2) if (codes[i] == cmds[c]) { ret += c ? ";" : "\033["; ret += codes[i+1]; } } ret += "m"; } return ret; } std::string Term::ansi_fgcolor (int r, int g, int b) { std::string ret; if (is_console()) { r = std::max (0, std::min (255, r)); g = std::max (0, std::min (255, g)); b = std::max (0, std::min (255, b)); ret = Strutil::format ("\033[38;2;%d;%d;%dm", r, g, b); } return ret; } std::string Term::ansi_bgcolor (int r, int g, int b) { std::string ret; if (is_console()) { r = std::max (0, std::min (255, r)); g = std::max (0, std::min (255, g)); b = std::max (0, std::min (255, b)); ret = Strutil::format ("\033[48;2;%d;%d;%dm", r, g, b); } return ret; } bool #if !defined(_MSC_VER) Sysutil::put_in_background (int argc, char* argv[]) #else Sysutil::put_in_background (int, char* []) #endif { // You would think that this would be sufficient: // pid_t pid = fork (); // if (pid < 0) // Some kind of error, we were unable to background // return false; // if (pid == 0) // return true; // This is the child process, so continue with life // // Otherwise, this is the parent process, so terminate // exit (0); // But it's not. On OS X, it's not safe to fork() if your app is linked // against certain libraries or frameworks. So the only thing that I // think is safe is to exec a new process. // Another solution is this: // daemon (1, 1); // But it suffers from the same problem on OS X, and seems to just be // a wrapper for fork. #if defined(__linux__) || defined(__GLIBC__) // Simplest case: // daemon returns 0 if successful, thus return true if successful return daemon (1, 1) == 0; #endif #ifdef __APPLE__ std::string newcmd = std::string(argv[0]) + " -F"; for (int i = 1; i < argc; ++i) { newcmd += " \""; newcmd += argv[i]; newcmd += "\""; } newcmd += " &"; if (system (newcmd.c_str()) != -1) exit (0); return true; #endif #ifdef WIN32 return true; #endif // Otherwise, we don't know what to do return false; } unsigned int Sysutil::hardware_concurrency () { return boost::thread::hardware_concurrency(); } unsigned int Sysutil::physical_concurrency () { #if BOOST_VERSION >= 105600 return boost::thread::physical_concurrency(); #else return boost::thread::hardware_concurrency(); #endif } size_t Sysutil::max_open_files () { #if defined(_WIN32) return size_t(_getmaxstdio()); #else struct rlimit rl; if (getrlimit(RLIMIT_NOFILE, &rl) == 0) return rl.rlim_cur; #endif return size_t(-1); // Couldn't figure out, so return effectively infinity } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libutil/farmhash.cpp0000644000175000017500000105153013151711064021062 0ustar mfvmfv// Copyright (c) 2014 Google, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // // FarmHash, by Geoff Pike // http://code.google.com/p/farmhash/ //#include "farmhash.h" #include "OpenImageIO/hash.h" #define NAMESPACE_FOR_HASH_FUNCTIONS OIIO::farmhash // FARMHASH ASSUMPTIONS: Modify as needed, or use -DFARMHASH_ASSUME_SSE42 etc. // Note that if you use -DFARMHASH_ASSUME_SSE42 you likely need -msse42 // (or its equivalent for your compiler); if you use -DFARMHASH_ASSUME_AESNI // you likely need -maes (or its equivalent for your compiler). #ifdef FARMHASH_ASSUME_SSSE3 #undef FARMHASH_ASSUME_SSSE3 #define FARMHASH_ASSUME_SSSE3 1 #endif #ifdef FARMHASH_ASSUME_SSE41 #undef FARMHASH_ASSUME_SSE41 #define FARMHASH_ASSUME_SSE41 1 #endif #ifdef FARMHASH_ASSUME_SSE42 #undef FARMHASH_ASSUME_SSE42 #define FARMHASH_ASSUME_SSE42 1 #endif #ifdef FARMHASH_ASSUME_AESNI #undef FARMHASH_ASSUME_AESNI #define FARMHASH_ASSUME_AESNI 1 #endif #ifdef FARMHASH_ASSUME_AVX #undef FARMHASH_ASSUME_AVX #define FARMHASH_ASSUME_AVX 1 #endif #if !defined(FARMHASH_CAN_USE_CXX11) && defined(LANG_CXX11) #define FARMHASH_CAN_USE_CXX11 1 #else #undef FARMHASH_CAN_USE_CXX11 #define FARMHASH_CAN_USE_CXX11 0 #endif // FARMHASH PORTABILITY LAYER: Runtime error if misconfigured #ifndef FARMHASH_DIE_IF_MISCONFIGURED #define FARMHASH_DIE_IF_MISCONFIGURED do { *(char*)(len % 17) = 0; } while (0) #endif // FARMHASH PORTABILITY LAYER: "static inline" or similar #ifndef STATIC_INLINE #define STATIC_INLINE static inline #endif // FARMHASH PORTABILITY LAYER: endianness and byteswapping functions #ifdef WORDS_BIGENDIAN #undef FARMHASH_BIG_ENDIAN #define FARMHASH_BIG_ENDIAN 1 #endif #if defined(FARMHASH_LITTLE_ENDIAN) && defined(FARMHASH_BIG_ENDIAN) #error #endif #if !defined(FARMHASH_LITTLE_ENDIAN) && !defined(FARMHASH_BIG_ENDIAN) #define FARMHASH_UNKNOWN_ENDIAN 1 #endif #if !defined(bswap_32) || !defined(bswap_64) #undef bswap_32 #undef bswap_64 #if defined(HAVE_BUILTIN_BSWAP) || defined(__clang__) || \ (defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || \ __GNUC__ >= 5)) // Easy case for bswap: no header file needed. #define bswap_32(x) __builtin_bswap32(x) #define bswap_64(x) __builtin_bswap64(x) #endif #endif #if defined(FARMHASH_UNKNOWN_ENDIAN) || !defined(bswap_64) #ifdef _MSC_VER #undef bswap_32 #undef bswap_64 #define bswap_32(x) _byteswap_ulong(x) #define bswap_64(x) _byteswap_uint64(x) #elif defined(__APPLE__) // Mac OS X / Darwin features #include #undef bswap_32 #undef bswap_64 #define bswap_32(x) OSSwapInt32(x) #define bswap_64(x) OSSwapInt64(x) #elif defined(__sun) || defined(sun) #include #undef bswap_32 #undef bswap_64 #define bswap_32(x) BSWAP_32(x) #define bswap_64(x) BSWAP_64(x) #elif defined(__FreeBSD__) #include #undef bswap_32 #undef bswap_64 #define bswap_32(x) bswap32(x) #define bswap_64(x) bswap64(x) #elif defined(__OpenBSD__) #include #undef bswap_32 #undef bswap_64 #define bswap_32(x) swap32(x) #define bswap_64(x) swap64(x) #elif defined(__NetBSD__) #include #include #if defined(__BSWAP_RENAME) && !defined(__bswap_32) #undef bswap_32 #undef bswap_64 #define bswap_32(x) bswap32(x) #define bswap_64(x) bswap64(x) #endif #else #undef bswap_32 #undef bswap_64 #include #endif #ifdef WORDS_BIGENDIAN #define FARMHASH_BIG_ENDIAN 1 #endif #endif #ifdef FARMHASH_BIG_ENDIAN #define uint32_in_expected_order(x) (bswap_32(x)) #define uint64_in_expected_order(x) (bswap_64(x)) #else #define uint32_in_expected_order(x) (x) #define uint64_in_expected_order(x) (x) #endif // namespace NAMESPACE_FOR_HASH_FUNCTIONS { OIIO_NAMESPACE_BEGIN namespace farmhash { STATIC_INLINE uint64_t Fetch64(const char *p) { uint64_t result; memcpy(&result, p, sizeof(result)); return uint64_in_expected_order(result); } STATIC_INLINE uint32_t Fetch32(const char *p) { uint32_t result; memcpy(&result, p, sizeof(result)); return uint32_in_expected_order(result); } STATIC_INLINE uint32_t Bswap32(uint32_t val) { return bswap_32(val); } STATIC_INLINE uint64_t Bswap64(uint64_t val) { return bswap_64(val); } // FARMHASH PORTABILITY LAYER: bitwise rot STATIC_INLINE uint32_t BasicRotate32(uint32_t val, int shift) { // Avoid shifting by 32: doing so yields an undefined result. return shift == 0 ? val : ((val >> shift) | (val << (32 - shift))); } STATIC_INLINE uint64_t BasicRotate64(uint64_t val, int shift) { // Avoid shifting by 64: doing so yields an undefined result. return shift == 0 ? val : ((val >> shift) | (val << (64 - shift))); } #if defined(_MSC_VER) && defined(FARMHASH_ROTR) STATIC_INLINE uint32_t Rotate32(uint32_t val, int shift) { return sizeof(unsigned long) == sizeof(val) ? _lrotr(val, shift) : BasicRotate32(val, shift); } STATIC_INLINE uint64_t Rotate64(uint64_t val, int shift) { return sizeof(unsigned long) == sizeof(val) ? _lrotr(val, shift) : BasicRotate64(val, shift); } #else STATIC_INLINE uint32_t Rotate32(uint32_t val, int shift) { return BasicRotate32(val, shift); } STATIC_INLINE uint64_t Rotate64(uint64_t val, int shift) { return BasicRotate64(val, shift); } #endif // } // namespace NAMESPACE_FOR_HASH_FUNCTIONS } /*end namespace farmhash*/ OIIO_NAMESPACE_END // FARMHASH PORTABILITY LAYER: debug mode or max speed? // One may use -DFARMHASH_DEBUG=1 or -DFARMHASH_DEBUG=0 to force the issue. #define FARMHASH_DEBUG 0 /*OIIO addition to ensure no debug vs opt differences*/ #if !defined(FARMHASH_DEBUG) && (!defined(NDEBUG) || defined(_DEBUG)) #define FARMHASH_DEBUG 1 #endif #undef debug_mode #if FARMHASH_DEBUG #define debug_mode 1 #else #define debug_mode 0 #endif // PLATFORM-SPECIFIC FUNCTIONS AND MACROS #undef x86_64 #if defined (__x86_64) || defined (__x86_64__) #define x86_64 1 #else #define x86_64 0 #endif #undef x86 #if defined(__i386__) || defined(__i386) || defined(__X86__) #define x86 1 #else #define x86 x86_64 #endif #if !defined(is_64bit) #define is_64bit (x86_64 || (sizeof(void*) == 8)) #endif #undef can_use_ssse3 #if defined(__SSSE3__) || defined(FARMHASH_ASSUME_SSSE3) #include #define can_use_ssse3 1 // Now we can use _mm_hsub_epi16 and so on. #else #define can_use_ssse3 0 #endif #undef can_use_sse41 #if defined(__SSE4_1__) || defined(FARMHASH_ASSUME_SSE41) #include #define can_use_sse41 1 // Now we can use _mm_insert_epi64 and so on. #else #define can_use_sse41 0 #endif #undef can_use_sse42 #if defined(__SSE4_2__) || defined(FARMHASH_ASSUME_SSE42) #include #define can_use_sse42 1 // Now we can use _mm_crc32_u{32,16,8}. And on 64-bit platforms, _mm_crc32_u64. #else #define can_use_sse42 0 #endif #undef can_use_aesni #if defined(__AES__) || defined(FARMHASH_ASSUME_AESNI) #include #define can_use_aesni 1 // Now we can use _mm_aesimc_si128 and so on. #else #define can_use_aesni 0 #endif #undef can_use_avx #if defined(__AVX__) || defined(FARMHASH_ASSUME_AVX) #include #define can_use_avx 1 #else #define can_use_avx 0 #endif #if can_use_ssse3 || can_use_sse41 || can_use_sse42 || can_use_aesni || can_use_avx STATIC_INLINE __m128i Fetch128(const char* s) { return _mm_loadu_si128(reinterpret_cast(s)); } #endif // Building blocks for hash functions // std::swap() was in but is in from C++11 on. #if !FARMHASH_CAN_USE_CXX11 #include #endif #undef PERMUTE3 #define PERMUTE3(a, b, c) do { std::swap(a, b); std::swap(a, c); } while (0) // namespace NAMESPACE_FOR_HASH_FUNCTIONS { OIIO_NAMESPACE_BEGIN namespace farmhash { // Some primes between 2^63 and 2^64 for various uses. static const uint64_t k0 = 0xc3a5c85c97cb3127ULL; static const uint64_t k1 = 0xb492b66fbe98f273ULL; static const uint64_t k2 = 0x9ae16a3b2f90404fULL; // Magic numbers for 32-bit hashing. Copied from Murmur3. static const uint32_t c1 = 0xcc9e2d51; static const uint32_t c2 = 0x1b873593; // A 32-bit to 32-bit integer hash copied from Murmur3. STATIC_INLINE uint32_t fmix(uint32_t h) { h ^= h >> 16; h *= 0x85ebca6b; h ^= h >> 13; h *= 0xc2b2ae35; h ^= h >> 16; return h; } STATIC_INLINE uint32_t Mur(uint32_t a, uint32_t h) { // Helper from Murmur3 for combining two 32-bit values. a *= c1; a = Rotate32(a, 17); a *= c2; h ^= a; h = Rotate32(h, 19); return h * 5 + 0xe6546b64; } template STATIC_INLINE T DebugTweak(T x) { if (debug_mode) { if (sizeof(x) == 4) { x = ~Bswap32(x * c1); } else { x = ~Bswap64(x * k1); } } return x; } template <> uint128_t DebugTweak(uint128_t x) { if (debug_mode) { uint64_t y = DebugTweak(Uint128Low64(x)); uint64_t z = DebugTweak(Uint128High64(x)); y += z; z += y; x = Uint128(y, z * k1); } return x; } // } // namespace NAMESPACE_FOR_HASH_FUNCTIONS } /*end namespace farmhash*/ OIIO_NAMESPACE_END using namespace std; // using namespace NAMESPACE_FOR_HASH_FUNCTIONS; using namespace OIIO::farmhash; namespace farmhashna { #undef Fetch #define Fetch Fetch64 #undef Rotate #define Rotate Rotate64 #undef Bswap #define Bswap Bswap64 STATIC_INLINE uint64_t ShiftMix(uint64_t val) { return val ^ (val >> 47); } STATIC_INLINE uint64_t HashLen16(uint64_t u, uint64_t v) { return Hash128to64(Uint128(u, v)); } STATIC_INLINE uint64_t HashLen16(uint64_t u, uint64_t v, uint64_t mul) { // Murmur-inspired hashing. uint64_t a = (u ^ v) * mul; a ^= (a >> 47); uint64_t b = (v ^ a) * mul; b ^= (b >> 47); b *= mul; return b; } STATIC_INLINE uint64_t HashLen0to16(const char *s, size_t len) { if (len >= 8) { uint64_t mul = k2 + len * 2; uint64_t a = Fetch(s) + k2; uint64_t b = Fetch(s + len - 8); uint64_t c = Rotate(b, 37) * mul + a; uint64_t d = (Rotate(a, 25) + b) * mul; return HashLen16(c, d, mul); } if (len >= 4) { uint64_t mul = k2 + len * 2; uint64_t a = Fetch32(s); return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul); } if (len > 0) { uint8_t a = s[0]; uint8_t b = s[len >> 1]; uint8_t c = s[len - 1]; uint32_t y = static_cast(a) + (static_cast(b) << 8); uint32_t z = len + (static_cast(c) << 2); return ShiftMix(y * k2 ^ z * k0) * k2; } return k2; } // This probably works well for 16-byte strings as well, but it may be overkill // in that case. STATIC_INLINE uint64_t HashLen17to32(const char *s, size_t len) { uint64_t mul = k2 + len * 2; uint64_t a = Fetch(s) * k1; uint64_t b = Fetch(s + 8); uint64_t c = Fetch(s + len - 8) * mul; uint64_t d = Fetch(s + len - 16) * k2; return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d, a + Rotate(b + k2, 18) + c, mul); } // Return a 16-byte hash for 48 bytes. Quick and dirty. // Callers do best to use "random-looking" values for a and b. STATIC_INLINE pair WeakHashLen32WithSeeds( uint64_t w, uint64_t x, uint64_t y, uint64_t z, uint64_t a, uint64_t b) { a += w; b = Rotate(b + a + z, 21); uint64_t c = a; a += x; a += y; b += Rotate(a, 44); return make_pair(a + z, b + c); } // Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. STATIC_INLINE pair WeakHashLen32WithSeeds( const char* s, uint64_t a, uint64_t b) { return WeakHashLen32WithSeeds(Fetch(s), Fetch(s + 8), Fetch(s + 16), Fetch(s + 24), a, b); } // Return an 8-byte hash for 33 to 64 bytes. STATIC_INLINE uint64_t HashLen33to64(const char *s, size_t len) { uint64_t mul = k2 + len * 2; uint64_t a = Fetch(s) * k2; uint64_t b = Fetch(s + 8); uint64_t c = Fetch(s + len - 8) * mul; uint64_t d = Fetch(s + len - 16) * k2; uint64_t y = Rotate(a + b, 43) + Rotate(c, 30) + d; uint64_t z = HashLen16(y, a + Rotate(b + k2, 18) + c, mul); uint64_t e = Fetch(s + 16) * mul; uint64_t f = Fetch(s + 24); uint64_t g = (y + Fetch(s + len - 32)) * mul; uint64_t h = (z + Fetch(s + len - 24)) * mul; return HashLen16(Rotate(e + f, 43) + Rotate(g, 30) + h, e + Rotate(f + a, 18) + g, mul); } uint64_t Hash64(const char *s, size_t len) { const uint64_t seed = 81; if (len <= 32) { if (len <= 16) { return HashLen0to16(s, len); } else { return HashLen17to32(s, len); } } else if (len <= 64) { return HashLen33to64(s, len); } // For strings over 64 bytes we loop. Internal state consists of // 56 bytes: v, w, x, y, and z. uint64_t x = seed; uint64_t y = seed * k1 + 113; uint64_t z = ShiftMix(y * k2 + 113) * k2; pair v = make_pair(0, 0); pair w = make_pair(0, 0); x = x * k2 + Fetch(s); // Set end so that after the loop we have 1 to 64 bytes left to process. const char* end = s + ((len - 1) / 64) * 64; const char* last64 = end + ((len - 1) & 63) - 63; assert(s + len - 64 == last64); do { x = Rotate(x + y + v.first + Fetch(s + 8), 37) * k1; y = Rotate(y + v.second + Fetch(s + 48), 42) * k1; x ^= w.second; y += v.first + Fetch(s + 40); z = Rotate(z + w.first, 33) * k1; v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch(s + 16)); std::swap(z, x); s += 64; } while (s != end); uint64_t mul = k1 + ((z & 0xff) << 1); // Make s point to the last 64 bytes of input. s = last64; w.first += ((len - 1) & 63); v.first += w.first; w.first += v.first; x = Rotate(x + y + v.first + Fetch(s + 8), 37) * mul; y = Rotate(y + v.second + Fetch(s + 48), 42) * mul; x ^= w.second * 9; y += v.first * 9 + Fetch(s + 40); z = Rotate(z + w.first, 33) * mul; v = WeakHashLen32WithSeeds(s, v.second * mul, x + w.first); w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch(s + 16)); std::swap(z, x); return HashLen16(HashLen16(v.first, w.first, mul) + ShiftMix(y) * k0 + z, HashLen16(v.second, w.second, mul) + x, mul); } uint64_t Hash64WithSeeds(const char *s, size_t len, uint64_t seed0, uint64_t seed1); uint64_t Hash64WithSeed(const char *s, size_t len, uint64_t seed) { return Hash64WithSeeds(s, len, k2, seed); } uint64_t Hash64WithSeeds(const char *s, size_t len, uint64_t seed0, uint64_t seed1) { return HashLen16(Hash64(s, len) - seed0, seed1); } } // namespace farmhashna namespace farmhashuo { #undef Fetch #define Fetch Fetch64 #undef Rotate #define Rotate Rotate64 STATIC_INLINE uint64_t H(uint64_t x, uint64_t y, uint64_t mul, int r) { uint64_t a = (x ^ y) * mul; a ^= (a >> 47); uint64_t b = (y ^ a) * mul; return Rotate(b, r) * mul; } uint64_t Hash64WithSeeds(const char *s, size_t len, uint64_t seed0, uint64_t seed1) { if (len <= 64) { return farmhashna::Hash64WithSeeds(s, len, seed0, seed1); } // For strings over 64 bytes we loop. Internal state consists of // 64 bytes: u, v, w, x, y, and z. uint64_t x = seed0; uint64_t y = seed1 * k2 + 113; uint64_t z = farmhashna::ShiftMix(y * k2) * k2; pair v = make_pair(seed0, seed1); pair w = make_pair(0, 0); uint64_t u = x - z; x *= k2; uint64_t mul = k2 + (u & 0x82); // Set end so that after the loop we have 1 to 64 bytes left to process. const char* end = s + ((len - 1) / 64) * 64; const char* last64 = end + ((len - 1) & 63) - 63; assert(s + len - 64 == last64); do { uint64_t a0 = Fetch(s); uint64_t a1 = Fetch(s + 8); uint64_t a2 = Fetch(s + 16); uint64_t a3 = Fetch(s + 24); uint64_t a4 = Fetch(s + 32); uint64_t a5 = Fetch(s + 40); uint64_t a6 = Fetch(s + 48); uint64_t a7 = Fetch(s + 56); x += a0 + a1; y += a2; z += a3; v.first += a4; v.second += a5 + a1; w.first += a6; w.second += a7; x = Rotate(x, 26); x *= 9; y = Rotate(y, 29); z *= mul; v.first = Rotate(v.first, 33); v.second = Rotate(v.second, 30); w.first ^= x; w.first *= 9; z = Rotate(z, 32); z += w.second; w.second += z; z *= 9; std::swap(u, y); z += a0 + a6; v.first += a2; v.second += a3; w.first += a4; w.second += a5 + a6; x += a1; y += a7; y += v.first; v.first += x - y; v.second += w.first; w.first += v.second; w.second += x - y; x += w.second; w.second = Rotate(w.second, 34); std::swap(u, z); s += 64; } while (s != end); // Make s point to the last 64 bytes of input. s = last64; u *= 9; v.second = Rotate(v.second, 28); v.first = Rotate(v.first, 20); w.first += ((len - 1) & 63); u += y; y += u; x = Rotate(y - x + v.first + Fetch(s + 8), 37) * mul; y = Rotate(y ^ v.second ^ Fetch(s + 48), 42) * mul; x ^= w.second * 9; y += v.first + Fetch(s + 40); z = Rotate(z + w.first, 33) * mul; v = farmhashna::WeakHashLen32WithSeeds(s, v.second * mul, x + w.first); w = farmhashna::WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch(s + 16)); return H(farmhashna::HashLen16(v.first + x, w.first ^ y, mul) + z - u, H(v.second + y, w.second + z, k2, 30) ^ x, k2, 31); } uint64_t Hash64WithSeed(const char *s, size_t len, uint64_t seed) { return len <= 64 ? farmhashna::Hash64WithSeed(s, len, seed) : Hash64WithSeeds(s, len, 0, seed); } uint64_t Hash64(const char *s, size_t len) { return len <= 64 ? farmhashna::Hash64(s, len) : Hash64WithSeeds(s, len, 81, 0); } } // namespace farmhashuo namespace farmhashxo { #undef Fetch #define Fetch Fetch64 #undef Rotate #define Rotate Rotate64 STATIC_INLINE uint64_t H32(const char *s, size_t len, uint64_t mul, uint64_t seed0 = 0, uint64_t seed1 = 0) { uint64_t a = Fetch(s) * k1; uint64_t b = Fetch(s + 8); uint64_t c = Fetch(s + len - 8) * mul; uint64_t d = Fetch(s + len - 16) * k2; uint64_t u = Rotate(a + b, 43) + Rotate(c, 30) + d + seed0; uint64_t v = a + Rotate(b + k2, 18) + c + seed1; a = farmhashna::ShiftMix((u ^ v) * mul); b = farmhashna::ShiftMix((v ^ a) * mul); return b; } // Return an 8-byte hash for 33 to 64 bytes. STATIC_INLINE uint64_t HashLen33to64(const char *s, size_t len) { uint64_t mul0 = k2 - 30; uint64_t mul1 = k2 - 30 + 2 * len; uint64_t h0 = H32(s, 32, mul0); uint64_t h1 = H32(s + len - 32, 32, mul1); return ((h1 * mul1) + h0) * mul1; } // Return an 8-byte hash for 65 to 96 bytes. STATIC_INLINE uint64_t HashLen65to96(const char *s, size_t len) { uint64_t mul0 = k2 - 114; uint64_t mul1 = k2 - 114 + 2 * len; uint64_t h0 = H32(s, 32, mul0); uint64_t h1 = H32(s + 32, 32, mul1); uint64_t h2 = H32(s + len - 32, 32, mul1, h0, h1); return (h2 * 9 + (h0 >> 17) + (h1 >> 21)) * mul1; } uint64_t Hash64(const char *s, size_t len) { if (len <= 32) { if (len <= 16) { return farmhashna::HashLen0to16(s, len); } else { return farmhashna::HashLen17to32(s, len); } } else if (len <= 64) { return HashLen33to64(s, len); } else if (len <= 96) { return HashLen65to96(s, len); } else if (len <= 256) { return farmhashna::Hash64(s, len); } else { return farmhashuo::Hash64(s, len); } } uint64_t Hash64WithSeeds(const char *s, size_t len, uint64_t seed0, uint64_t seed1) { return farmhashuo::Hash64WithSeeds(s, len, seed0, seed1); } uint64_t Hash64WithSeed(const char *s, size_t len, uint64_t seed) { return farmhashuo::Hash64WithSeed(s, len, seed); } } // namespace farmhashxo namespace farmhashte { #if !can_use_sse41 || !x86_64 uint64_t Hash64(const char *s, size_t len) { FARMHASH_DIE_IF_MISCONFIGURED; return s == NULL ? 0 : len; } uint64_t Hash64WithSeed(const char *s, size_t len, uint64_t seed) { FARMHASH_DIE_IF_MISCONFIGURED; return seed + Hash64(s, len); } uint64_t Hash64WithSeeds(const char *s, size_t len, uint64_t seed0, uint64_t seed1) { FARMHASH_DIE_IF_MISCONFIGURED; return seed0 + seed1 + Hash64(s, len); } #else #undef Fetch #define Fetch Fetch64 #undef Rotate #define Rotate Rotate64 #undef Bswap #define Bswap Bswap64 // Helpers for data-parallel operations (1x 128 bits or 2x 64 or 4x 32). STATIC_INLINE __m128i Add(__m128i x, __m128i y) { return _mm_add_epi64(x, y); } STATIC_INLINE __m128i Xor(__m128i x, __m128i y) { return _mm_xor_si128(x, y); } STATIC_INLINE __m128i Mul(__m128i x, __m128i y) { return _mm_mullo_epi32(x, y); } STATIC_INLINE __m128i Shuf(__m128i x, __m128i y) { return _mm_shuffle_epi8(y, x); } // Requires n >= 256. Requires SSE4.1. Should be slightly faster if the // compiler uses AVX instructions (e.g., use the -mavx flag with GCC). STATIC_INLINE uint64_t Hash64Long(const char* s, size_t n, uint64_t seed0, uint64_t seed1) { const __m128i kShuf = _mm_set_epi8(4, 11, 10, 5, 8, 15, 6, 9, 12, 2, 14, 13, 0, 7, 3, 1); const __m128i kMult = _mm_set_epi8(0xbd, 0xd6, 0x33, 0x39, 0x45, 0x54, 0xfa, 0x03, 0x34, 0x3e, 0x33, 0xed, 0xcc, 0x9e, 0x2d, 0x51); uint64_t seed2 = (seed0 + 113) * (seed1 + 9); uint64_t seed3 = (Rotate(seed0, 23) + 27) * (Rotate(seed1, 30) + 111); __m128i d0 = _mm_cvtsi64_si128(seed0); __m128i d1 = _mm_cvtsi64_si128(seed1); __m128i d2 = Shuf(kShuf, d0); __m128i d3 = Shuf(kShuf, d1); __m128i d4 = Xor(d0, d1); __m128i d5 = Xor(d1, d2); __m128i d6 = Xor(d2, d4); __m128i d7 = _mm_set1_epi32(seed2 >> 32); __m128i d8 = Mul(kMult, d2); __m128i d9 = _mm_set1_epi32(seed3 >> 32); __m128i d10 = _mm_set1_epi32(seed3); __m128i d11 = Add(d2, _mm_set1_epi32(seed2)); const char* end = s + (n & ~static_cast(255)); do { __m128i z; z = Fetch128(s); d0 = Add(d0, z); d1 = Shuf(kShuf, d1); d2 = Xor(d2, d0); d4 = Xor(d4, z); d4 = Xor(d4, d1); std::swap(d0, d6); z = Fetch128(s + 16); d5 = Add(d5, z); d6 = Shuf(kShuf, d6); d8 = Shuf(kShuf, d8); d7 = Xor(d7, d5); d0 = Xor(d0, z); d0 = Xor(d0, d6); std::swap(d5, d11); z = Fetch128(s + 32); d1 = Add(d1, z); d2 = Shuf(kShuf, d2); d4 = Shuf(kShuf, d4); d5 = Xor(d5, z); d5 = Xor(d5, d2); std::swap(d10, d4); z = Fetch128(s + 48); d6 = Add(d6, z); d7 = Shuf(kShuf, d7); d0 = Shuf(kShuf, d0); d8 = Xor(d8, d6); d1 = Xor(d1, z); d1 = Add(d1, d7); z = Fetch128(s + 64); d2 = Add(d2, z); d5 = Shuf(kShuf, d5); d4 = Add(d4, d2); d6 = Xor(d6, z); d6 = Xor(d6, d11); std::swap(d8, d2); z = Fetch128(s + 80); d7 = Xor(d7, z); d8 = Shuf(kShuf, d8); d1 = Shuf(kShuf, d1); d0 = Add(d0, d7); d2 = Add(d2, z); d2 = Add(d2, d8); std::swap(d1, d7); z = Fetch128(s + 96); d4 = Shuf(kShuf, d4); d6 = Shuf(kShuf, d6); d8 = Mul(kMult, d8); d5 = Xor(d5, d11); d7 = Xor(d7, z); d7 = Add(d7, d4); std::swap(d6, d0); z = Fetch128(s + 112); d8 = Add(d8, z); d0 = Shuf(kShuf, d0); d2 = Shuf(kShuf, d2); d1 = Xor(d1, d8); d10 = Xor(d10, z); d10 = Xor(d10, d0); std::swap(d11, d5); z = Fetch128(s + 128); d4 = Add(d4, z); d5 = Shuf(kShuf, d5); d7 = Shuf(kShuf, d7); d6 = Add(d6, d4); d8 = Xor(d8, z); d8 = Xor(d8, d5); std::swap(d4, d10); z = Fetch128(s + 144); d0 = Add(d0, z); d1 = Shuf(kShuf, d1); d2 = Add(d2, d0); d4 = Xor(d4, z); d4 = Xor(d4, d1); z = Fetch128(s + 160); d5 = Add(d5, z); d6 = Shuf(kShuf, d6); d8 = Shuf(kShuf, d8); d7 = Xor(d7, d5); d0 = Xor(d0, z); d0 = Xor(d0, d6); std::swap(d2, d8); z = Fetch128(s + 176); d1 = Add(d1, z); d2 = Shuf(kShuf, d2); d4 = Shuf(kShuf, d4); d5 = Mul(kMult, d5); d5 = Xor(d5, z); d5 = Xor(d5, d2); std::swap(d7, d1); z = Fetch128(s + 192); d6 = Add(d6, z); d7 = Shuf(kShuf, d7); d0 = Shuf(kShuf, d0); d8 = Add(d8, d6); d1 = Xor(d1, z); d1 = Xor(d1, d7); std::swap(d0, d6); z = Fetch128(s + 208); d2 = Add(d2, z); d5 = Shuf(kShuf, d5); d4 = Xor(d4, d2); d6 = Xor(d6, z); d6 = Xor(d6, d9); std::swap(d5, d11); z = Fetch128(s + 224); d7 = Add(d7, z); d8 = Shuf(kShuf, d8); d1 = Shuf(kShuf, d1); d0 = Xor(d0, d7); d2 = Xor(d2, z); d2 = Xor(d2, d8); std::swap(d10, d4); z = Fetch128(s + 240); d3 = Add(d3, z); d4 = Shuf(kShuf, d4); d6 = Shuf(kShuf, d6); d7 = Mul(kMult, d7); d5 = Add(d5, d3); d7 = Xor(d7, z); d7 = Xor(d7, d4); std::swap(d3, d9); s += 256; } while (s != end); d6 = Add(Mul(kMult, d6), _mm_cvtsi64_si128(n)); if (n % 256 != 0) { d7 = Add(_mm_shuffle_epi32(d8, (0 << 6) + (3 << 4) + (2 << 2) + (1 << 0)), d7); d8 = Add(Mul(kMult, d8), _mm_cvtsi64_si128(farmhashxo::Hash64(s, n % 256))); } __m128i t[8]; d0 = Mul(kMult, Shuf(kShuf, Mul(kMult, d0))); d3 = Mul(kMult, Shuf(kShuf, Mul(kMult, d3))); d9 = Mul(kMult, Shuf(kShuf, Mul(kMult, d9))); d1 = Mul(kMult, Shuf(kShuf, Mul(kMult, d1))); d0 = Add(d11, d0); d3 = Xor(d7, d3); d9 = Add(d8, d9); d1 = Add(d10, d1); d4 = Add(d3, d4); d5 = Add(d9, d5); d6 = Xor(d1, d6); d2 = Add(d0, d2); t[0] = d0; t[1] = d3; t[2] = d9; t[3] = d1; t[4] = d4; t[5] = d5; t[6] = d6; t[7] = d2; return farmhashxo::Hash64(reinterpret_cast(t), sizeof(t)); } uint64_t Hash64(const char *s, size_t len) { // Empirically, farmhashxo seems faster until length 512. return len >= 512 ? Hash64Long(s, len, k2, k1) : farmhashxo::Hash64(s, len); } uint64_t Hash64WithSeed(const char *s, size_t len, uint64_t seed) { return len >= 512 ? Hash64Long(s, len, k1, seed) : farmhashxo::Hash64WithSeed(s, len, seed); } uint64_t Hash64WithSeeds(const char *s, size_t len, uint64_t seed0, uint64_t seed1) { return len >= 512 ? Hash64Long(s, len, seed0, seed1) : farmhashxo::Hash64WithSeeds(s, len, seed0, seed1); } #endif } // namespace farmhashte namespace farmhashnt { #if !can_use_sse41 || !x86_64 uint32_t Hash32(const char *s, size_t len) { FARMHASH_DIE_IF_MISCONFIGURED; return s == NULL ? 0 : len; } uint32_t Hash32WithSeed(const char *s, size_t len, uint32_t seed) { FARMHASH_DIE_IF_MISCONFIGURED; return seed + Hash32(s, len); } #else uint32_t Hash32(const char *s, size_t len) { return static_cast(farmhashte::Hash64(s, len)); } uint32_t Hash32WithSeed(const char *s, size_t len, uint32_t seed) { return static_cast(farmhashte::Hash64WithSeed(s, len, seed)); } #endif } // namespace farmhashnt namespace farmhashmk { #undef Fetch #define Fetch Fetch32 #undef Rotate #define Rotate Rotate32 #undef Bswap #define Bswap Bswap32 STATIC_INLINE uint32_t Hash32Len13to24(const char *s, size_t len, uint32_t seed = 0) { uint32_t a = Fetch(s - 4 + (len >> 1)); uint32_t b = Fetch(s + 4); uint32_t c = Fetch(s + len - 8); uint32_t d = Fetch(s + (len >> 1)); uint32_t e = Fetch(s); uint32_t f = Fetch(s + len - 4); uint32_t h = d * c1 + len + seed; a = Rotate(a, 12) + f; h = Mur(c, h) + a; a = Rotate(a, 3) + c; h = Mur(e, h) + a; a = Rotate(a + f, 12) + d; h = Mur(b ^ seed, h) + a; return fmix(h); } STATIC_INLINE uint32_t Hash32Len0to4(const char *s, size_t len, uint32_t seed = 0) { uint32_t b = seed; uint32_t c = 9; for (size_t i = 0; i < len; i++) { signed char v = s[i]; b = b * c1 + v; c ^= b; } return fmix(Mur(b, Mur(len, c))); } STATIC_INLINE uint32_t Hash32Len5to12(const char *s, size_t len, uint32_t seed = 0) { uint32_t a = len, b = len * 5, c = 9, d = b + seed; a += Fetch(s); b += Fetch(s + len - 4); c += Fetch(s + ((len >> 1) & 4)); return fmix(seed ^ Mur(c, Mur(b, Mur(a, d)))); } uint32_t Hash32(const char *s, size_t len) { if (len <= 24) { return len <= 12 ? (len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len)) : Hash32Len13to24(s, len); } // len > 24 uint32_t h = len, g = c1 * len, f = g; uint32_t a0 = Rotate(Fetch(s + len - 4) * c1, 17) * c2; uint32_t a1 = Rotate(Fetch(s + len - 8) * c1, 17) * c2; uint32_t a2 = Rotate(Fetch(s + len - 16) * c1, 17) * c2; uint32_t a3 = Rotate(Fetch(s + len - 12) * c1, 17) * c2; uint32_t a4 = Rotate(Fetch(s + len - 20) * c1, 17) * c2; h ^= a0; h = Rotate(h, 19); h = h * 5 + 0xe6546b64; h ^= a2; h = Rotate(h, 19); h = h * 5 + 0xe6546b64; g ^= a1; g = Rotate(g, 19); g = g * 5 + 0xe6546b64; g ^= a3; g = Rotate(g, 19); g = g * 5 + 0xe6546b64; f += a4; f = Rotate(f, 19) + 113; size_t iters = (len - 1) / 20; do { uint32_t a = Fetch(s); uint32_t b = Fetch(s + 4); uint32_t c = Fetch(s + 8); uint32_t d = Fetch(s + 12); uint32_t e = Fetch(s + 16); h += a; g += b; f += c; h = Mur(d, h) + e; g = Mur(c, g) + a; f = Mur(b + e * c1, f) + d; f += g; g += f; s += 20; } while (--iters != 0); g = Rotate(g, 11) * c1; g = Rotate(g, 17) * c1; f = Rotate(f, 11) * c1; f = Rotate(f, 17) * c1; h = Rotate(h + g, 19); h = h * 5 + 0xe6546b64; h = Rotate(h, 17) * c1; h = Rotate(h + f, 19); h = h * 5 + 0xe6546b64; h = Rotate(h, 17) * c1; return h; } uint32_t Hash32WithSeed(const char *s, size_t len, uint32_t seed) { if (len <= 24) { if (len >= 13) return Hash32Len13to24(s, len, seed * c1); else if (len >= 5) return Hash32Len5to12(s, len, seed); else return Hash32Len0to4(s, len, seed); } uint32_t h = Hash32Len13to24(s, 24, seed ^ len); return Mur(Hash32(s + 24, len - 24) + seed, h); } } // namespace farmhashmk namespace farmhashsu { #if !can_use_sse42 || !can_use_aesni uint32_t Hash32(const char *s, size_t len) { FARMHASH_DIE_IF_MISCONFIGURED; return s == NULL ? 0 : len; } uint32_t Hash32WithSeed(const char *s, size_t len, uint32_t seed) { FARMHASH_DIE_IF_MISCONFIGURED; return seed + Hash32(s, len); } #else #undef Fetch #define Fetch Fetch32 #undef Rotate #define Rotate Rotate32 #undef Bswap #define Bswap Bswap32 // Helpers for data-parallel operations (4x 32-bits). STATIC_INLINE __m128i Add(__m128i x, __m128i y) { return _mm_add_epi32(x, y); } STATIC_INLINE __m128i Xor(__m128i x, __m128i y) { return _mm_xor_si128(x, y); } STATIC_INLINE __m128i Or(__m128i x, __m128i y) { return _mm_or_si128(x, y); } STATIC_INLINE __m128i Mul(__m128i x, __m128i y) { return _mm_mullo_epi32(x, y); } STATIC_INLINE __m128i Mul5(__m128i x) { return Add(x, _mm_slli_epi32(x, 2)); } STATIC_INLINE __m128i RotateLeft(__m128i x, int c) { return Or(_mm_slli_epi32(x, c), _mm_srli_epi32(x, 32 - c)); } STATIC_INLINE __m128i Rol17(__m128i x) { return RotateLeft(x, 17); } STATIC_INLINE __m128i Rol19(__m128i x) { return RotateLeft(x, 19); } STATIC_INLINE __m128i Shuffle0321(__m128i x) { return _mm_shuffle_epi32(x, (0 << 6) + (3 << 4) + (2 << 2) + (1 << 0)); } uint32_t Hash32(const char *s, size_t len) { const uint32_t seed = 81; if (len <= 24) { return len <= 12 ? (len <= 4 ? farmhashmk::Hash32Len0to4(s, len) : farmhashmk::Hash32Len5to12(s, len)) : farmhashmk::Hash32Len13to24(s, len); } if (len < 40) { uint32_t a = len, b = seed * c2, c = a + b; a += Fetch(s + len - 4); b += Fetch(s + len - 20); c += Fetch(s + len - 16); uint32_t d = a; a = NAMESPACE_FOR_HASH_FUNCTIONS::Rotate32(a, 21); a = Mur(a, Mur(b, _mm_crc32_u32(c, d))); a += Fetch(s + len - 12); b += Fetch(s + len - 8); d += a; a += d; b = Mur(b, d) * c2; a = _mm_crc32_u32(a, b + c); return farmhashmk::Hash32Len13to24(s, (len + 1) / 2, a) + b; } #undef Mulc1 #define Mulc1(x) Mul((x), cc1) #undef Mulc2 #define Mulc2(x) Mul((x), cc2) #undef Murk #define Murk(a, h) \ Add(k, \ Mul5( \ Rol19( \ Xor( \ Mulc2( \ Rol17( \ Mulc1(a))), \ (h))))) const __m128i cc1 = _mm_set1_epi32(c1); const __m128i cc2 = _mm_set1_epi32(c2); __m128i h = _mm_set1_epi32(seed); __m128i g = _mm_set1_epi32(c1 * seed); __m128i f = g; __m128i k = _mm_set1_epi32(0xe6546b64); __m128i q; if (len < 80) { __m128i a = Fetch128(s); __m128i b = Fetch128(s + 16); __m128i c = Fetch128(s + (len - 15) / 2); __m128i d = Fetch128(s + len - 32); __m128i e = Fetch128(s + len - 16); h = Add(h, a); g = Add(g, b); q = g; g = Shuffle0321(g); f = Add(f, c); __m128i be = Add(b, Mulc1(e)); h = Add(h, f); f = Add(f, h); h = Add(Murk(d, h), e); k = Xor(k, _mm_shuffle_epi8(g, f)); g = Add(Xor(c, g), a); f = Add(Xor(be, f), d); k = Add(k, be); k = Add(k, _mm_shuffle_epi8(f, h)); f = Add(f, g); g = Add(g, f); g = Add(_mm_set1_epi32(len), Mulc1(g)); } else { // len >= 80 // The following is loosely modelled after farmhashmk::Hash32. size_t iters = (len - 1) / 80; len -= iters * 80; #undef Chunk #define Chunk() do { \ __m128i a = Fetch128(s); \ __m128i b = Fetch128(s + 16); \ __m128i c = Fetch128(s + 32); \ __m128i d = Fetch128(s + 48); \ __m128i e = Fetch128(s + 64); \ h = Add(h, a); \ g = Add(g, b); \ g = Shuffle0321(g); \ f = Add(f, c); \ __m128i be = Add(b, Mulc1(e)); \ h = Add(h, f); \ f = Add(f, h); \ h = Add(h, d); \ q = Add(q, e); \ h = Rol17(h); \ h = Mulc1(h); \ k = Xor(k, _mm_shuffle_epi8(g, f)); \ g = Add(Xor(c, g), a); \ f = Add(Xor(be, f), d); \ std::swap(f, q); \ q = _mm_aesimc_si128(q); \ k = Add(k, be); \ k = Add(k, _mm_shuffle_epi8(f, h)); \ f = Add(f, g); \ g = Add(g, f); \ f = Mulc1(f); \ } while (0) q = g; while (iters-- != 0) { Chunk(); s += 80; } if (len != 0) { h = Add(h, _mm_set1_epi32(len)); s = s + len - 80; Chunk(); } } g = Shuffle0321(g); k = Xor(k, g); k = Xor(k, q); h = Xor(h, q); f = Mulc1(f); k = Mulc2(k); g = Mulc1(g); h = Mulc2(h); k = Add(k, _mm_shuffle_epi8(g, f)); h = Add(h, f); f = Add(f, h); g = Add(g, k); k = Add(k, g); k = Xor(k, _mm_shuffle_epi8(f, h)); __m128i buf[4]; buf[0] = f; buf[1] = g; buf[2] = k; buf[3] = h; s = reinterpret_cast(buf); uint32_t x = Fetch(s); uint32_t y = Fetch(s+4); uint32_t z = Fetch(s+8); x = _mm_crc32_u32(x, Fetch(s+12)); y = _mm_crc32_u32(y, Fetch(s+16)); z = _mm_crc32_u32(z * c1, Fetch(s+20)); x = _mm_crc32_u32(x, Fetch(s+24)); y = _mm_crc32_u32(y * c1, Fetch(s+28)); uint32_t o = y; z = _mm_crc32_u32(z, Fetch(s+32)); x = _mm_crc32_u32(x * c1, Fetch(s+36)); y = _mm_crc32_u32(y, Fetch(s+40)); z = _mm_crc32_u32(z * c1, Fetch(s+44)); x = _mm_crc32_u32(x, Fetch(s+48)); y = _mm_crc32_u32(y * c1, Fetch(s+52)); z = _mm_crc32_u32(z, Fetch(s+56)); x = _mm_crc32_u32(x, Fetch(s+60)); return (o - x + y - z) * c1; } #undef Chunk #undef Murk #undef Mulc2 #undef Mulc1 uint32_t Hash32WithSeed(const char *s, size_t len, uint32_t seed) { if (len <= 24) { if (len >= 13) return farmhashmk::Hash32Len13to24(s, len, seed * c1); else if (len >= 5) return farmhashmk::Hash32Len5to12(s, len, seed); else return farmhashmk::Hash32Len0to4(s, len, seed); } uint32_t h = farmhashmk::Hash32Len13to24(s, 24, seed ^ len); return _mm_crc32_u32(Hash32(s + 24, len - 24) + seed, h); } #endif } // namespace farmhashsu namespace farmhashsa { #if !can_use_sse42 uint32_t Hash32(const char *s, size_t len) { FARMHASH_DIE_IF_MISCONFIGURED; return s == NULL ? 0 : len; } uint32_t Hash32WithSeed(const char *s, size_t len, uint32_t seed) { FARMHASH_DIE_IF_MISCONFIGURED; return seed + Hash32(s, len); } #else #undef Fetch #define Fetch Fetch32 #undef Rotate #define Rotate Rotate32 #undef Bswap #define Bswap Bswap32 // Helpers for data-parallel operations (4x 32-bits). STATIC_INLINE __m128i Add(__m128i x, __m128i y) { return _mm_add_epi32(x, y); } STATIC_INLINE __m128i Xor(__m128i x, __m128i y) { return _mm_xor_si128(x, y); } STATIC_INLINE __m128i Or(__m128i x, __m128i y) { return _mm_or_si128(x, y); } STATIC_INLINE __m128i Mul(__m128i x, __m128i y) { return _mm_mullo_epi32(x, y); } STATIC_INLINE __m128i Mul5(__m128i x) { return Add(x, _mm_slli_epi32(x, 2)); } STATIC_INLINE __m128i Rotate(__m128i x, int c) { return Or(_mm_slli_epi32(x, c), _mm_srli_epi32(x, 32 - c)); } STATIC_INLINE __m128i Rot17(__m128i x) { return Rotate(x, 17); } STATIC_INLINE __m128i Rot19(__m128i x) { return Rotate(x, 19); } STATIC_INLINE __m128i Shuffle0321(__m128i x) { return _mm_shuffle_epi32(x, (0 << 6) + (3 << 4) + (2 << 2) + (1 << 0)); } uint32_t Hash32(const char *s, size_t len) { const uint32_t seed = 81; if (len <= 24) { return len <= 12 ? (len <= 4 ? farmhashmk::Hash32Len0to4(s, len) : farmhashmk::Hash32Len5to12(s, len)) : farmhashmk::Hash32Len13to24(s, len); } if (len < 40) { uint32_t a = len, b = seed * c2, c = a + b; a += Fetch(s + len - 4); b += Fetch(s + len - 20); c += Fetch(s + len - 16); uint32_t d = a; a = NAMESPACE_FOR_HASH_FUNCTIONS::Rotate32(a, 21); a = Mur(a, Mur(b, Mur(c, d))); a += Fetch(s + len - 12); b += Fetch(s + len - 8); d += a; a += d; b = Mur(b, d) * c2; a = _mm_crc32_u32(a, b + c); return farmhashmk::Hash32Len13to24(s, (len + 1) / 2, a) + b; } #undef Mulc1 #define Mulc1(x) Mul((x), cc1) #undef Mulc2 #define Mulc2(x) Mul((x), cc2) #undef Murk #define Murk(a, h) \ Add(k, \ Mul5( \ Rot19( \ Xor( \ Mulc2( \ Rot17( \ Mulc1(a))), \ (h))))) const __m128i cc1 = _mm_set1_epi32(c1); const __m128i cc2 = _mm_set1_epi32(c2); __m128i h = _mm_set1_epi32(seed); __m128i g = _mm_set1_epi32(c1 * seed); __m128i f = g; __m128i k = _mm_set1_epi32(0xe6546b64); if (len < 80) { __m128i a = Fetch128(s); __m128i b = Fetch128(s + 16); __m128i c = Fetch128(s + (len - 15) / 2); __m128i d = Fetch128(s + len - 32); __m128i e = Fetch128(s + len - 16); h = Add(h, a); g = Add(g, b); g = Shuffle0321(g); f = Add(f, c); __m128i be = Add(b, Mulc1(e)); h = Add(h, f); f = Add(f, h); h = Add(Murk(d, h), e); k = Xor(k, _mm_shuffle_epi8(g, f)); g = Add(Xor(c, g), a); f = Add(Xor(be, f), d); k = Add(k, be); k = Add(k, _mm_shuffle_epi8(f, h)); f = Add(f, g); g = Add(g, f); g = Add(_mm_set1_epi32(len), Mulc1(g)); } else { // len >= 80 // The following is loosely modelled after farmhashmk::Hash32. size_t iters = (len - 1) / 80; len -= iters * 80; #undef Chunk #define Chunk() do { \ __m128i a = Fetch128(s); \ __m128i b = Fetch128(s + 16); \ __m128i c = Fetch128(s + 32); \ __m128i d = Fetch128(s + 48); \ __m128i e = Fetch128(s + 64); \ h = Add(h, a); \ g = Add(g, b); \ g = Shuffle0321(g); \ f = Add(f, c); \ __m128i be = Add(b, Mulc1(e)); \ h = Add(h, f); \ f = Add(f, h); \ h = Add(Murk(d, h), e); \ k = Xor(k, _mm_shuffle_epi8(g, f)); \ g = Add(Xor(c, g), a); \ f = Add(Xor(be, f), d); \ k = Add(k, be); \ k = Add(k, _mm_shuffle_epi8(f, h)); \ f = Add(f, g); \ g = Add(g, f); \ f = Mulc1(f); \ } while (0) while (iters-- != 0) { Chunk(); s += 80; } if (len != 0) { h = Add(h, _mm_set1_epi32(len)); s = s + len - 80; Chunk(); } } g = Shuffle0321(g); k = Xor(k, g); f = Mulc1(f); k = Mulc2(k); g = Mulc1(g); h = Mulc2(h); k = Add(k, _mm_shuffle_epi8(g, f)); h = Add(h, f); f = Add(f, h); g = Add(g, k); k = Add(k, g); k = Xor(k, _mm_shuffle_epi8(f, h)); __m128i buf[4]; buf[0] = f; buf[1] = g; buf[2] = k; buf[3] = h; s = reinterpret_cast(buf); uint32_t x = Fetch(s); uint32_t y = Fetch(s+4); uint32_t z = Fetch(s+8); x = _mm_crc32_u32(x, Fetch(s+12)); y = _mm_crc32_u32(y, Fetch(s+16)); z = _mm_crc32_u32(z * c1, Fetch(s+20)); x = _mm_crc32_u32(x, Fetch(s+24)); y = _mm_crc32_u32(y * c1, Fetch(s+28)); uint32_t o = y; z = _mm_crc32_u32(z, Fetch(s+32)); x = _mm_crc32_u32(x * c1, Fetch(s+36)); y = _mm_crc32_u32(y, Fetch(s+40)); z = _mm_crc32_u32(z * c1, Fetch(s+44)); x = _mm_crc32_u32(x, Fetch(s+48)); y = _mm_crc32_u32(y * c1, Fetch(s+52)); z = _mm_crc32_u32(z, Fetch(s+56)); x = _mm_crc32_u32(x, Fetch(s+60)); return (o - x + y - z) * c1; } #undef Chunk #undef Murk #undef Mulc2 #undef Mulc1 uint32_t Hash32WithSeed(const char *s, size_t len, uint32_t seed) { if (len <= 24) { if (len >= 13) return farmhashmk::Hash32Len13to24(s, len, seed * c1); else if (len >= 5) return farmhashmk::Hash32Len5to12(s, len, seed); else return farmhashmk::Hash32Len0to4(s, len, seed); } uint32_t h = farmhashmk::Hash32Len13to24(s, 24, seed ^ len); return _mm_crc32_u32(Hash32(s + 24, len - 24) + seed, h); } #endif } // namespace farmhashsa namespace farmhashcc { // This file provides a 32-bit hash equivalent to CityHash32 (v1.1.1) // and a 128-bit hash equivalent to CityHash128 (v1.1.1). It also provides // a seeded 32-bit hash function similar to CityHash32. #undef Fetch #define Fetch Fetch32 #undef Rotate #define Rotate Rotate32 #undef Bswap #define Bswap Bswap32 STATIC_INLINE uint32_t Hash32Len13to24(const char *s, size_t len) { uint32_t a = Fetch(s - 4 + (len >> 1)); uint32_t b = Fetch(s + 4); uint32_t c = Fetch(s + len - 8); uint32_t d = Fetch(s + (len >> 1)); uint32_t e = Fetch(s); uint32_t f = Fetch(s + len - 4); uint32_t h = len; return fmix(Mur(f, Mur(e, Mur(d, Mur(c, Mur(b, Mur(a, h))))))); } STATIC_INLINE uint32_t Hash32Len0to4(const char *s, size_t len) { uint32_t b = 0; uint32_t c = 9; for (size_t i = 0; i < len; i++) { signed char v = s[i]; b = b * c1 + v; c ^= b; } return fmix(Mur(b, Mur(len, c))); } STATIC_INLINE uint32_t Hash32Len5to12(const char *s, size_t len) { uint32_t a = len, b = len * 5, c = 9, d = b; a += Fetch(s); b += Fetch(s + len - 4); c += Fetch(s + ((len >> 1) & 4)); return fmix(Mur(c, Mur(b, Mur(a, d)))); } uint32_t Hash32(const char *s, size_t len) { if (len <= 24) { return len <= 12 ? (len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len)) : Hash32Len13to24(s, len); } // len > 24 uint32_t h = len, g = c1 * len, f = g; uint32_t a0 = Rotate(Fetch(s + len - 4) * c1, 17) * c2; uint32_t a1 = Rotate(Fetch(s + len - 8) * c1, 17) * c2; uint32_t a2 = Rotate(Fetch(s + len - 16) * c1, 17) * c2; uint32_t a3 = Rotate(Fetch(s + len - 12) * c1, 17) * c2; uint32_t a4 = Rotate(Fetch(s + len - 20) * c1, 17) * c2; h ^= a0; h = Rotate(h, 19); h = h * 5 + 0xe6546b64; h ^= a2; h = Rotate(h, 19); h = h * 5 + 0xe6546b64; g ^= a1; g = Rotate(g, 19); g = g * 5 + 0xe6546b64; g ^= a3; g = Rotate(g, 19); g = g * 5 + 0xe6546b64; f += a4; f = Rotate(f, 19); f = f * 5 + 0xe6546b64; size_t iters = (len - 1) / 20; do { uint32_t a0 = Rotate(Fetch(s) * c1, 17) * c2; uint32_t a1 = Fetch(s + 4); uint32_t a2 = Rotate(Fetch(s + 8) * c1, 17) * c2; uint32_t a3 = Rotate(Fetch(s + 12) * c1, 17) * c2; uint32_t a4 = Fetch(s + 16); h ^= a0; h = Rotate(h, 18); h = h * 5 + 0xe6546b64; f += a1; f = Rotate(f, 19); f = f * c1; g += a2; g = Rotate(g, 18); g = g * 5 + 0xe6546b64; h ^= a3 + a1; h = Rotate(h, 19); h = h * 5 + 0xe6546b64; g ^= a4; g = Bswap(g) * 5; h += a4 * 5; h = Bswap(h); f += a0; PERMUTE3(f, h, g); s += 20; } while (--iters != 0); g = Rotate(g, 11) * c1; g = Rotate(g, 17) * c1; f = Rotate(f, 11) * c1; f = Rotate(f, 17) * c1; h = Rotate(h + g, 19); h = h * 5 + 0xe6546b64; h = Rotate(h, 17) * c1; h = Rotate(h + f, 19); h = h * 5 + 0xe6546b64; h = Rotate(h, 17) * c1; return h; } uint32_t Hash32WithSeed(const char *s, size_t len, uint32_t seed) { if (len <= 24) { if (len >= 13) return farmhashmk::Hash32Len13to24(s, len, seed * c1); else if (len >= 5) return farmhashmk::Hash32Len5to12(s, len, seed); else return farmhashmk::Hash32Len0to4(s, len, seed); } uint32_t h = farmhashmk::Hash32Len13to24(s, 24, seed ^ len); return Mur(Hash32(s + 24, len - 24) + seed, h); } #undef Fetch #define Fetch Fetch64 #undef Rotate #define Rotate Rotate64 #undef Bswap #define Bswap Bswap64 STATIC_INLINE uint64_t ShiftMix(uint64_t val) { return val ^ (val >> 47); } STATIC_INLINE uint64_t HashLen16(uint64_t u, uint64_t v) { return Hash128to64(Uint128(u, v)); } STATIC_INLINE uint64_t HashLen16(uint64_t u, uint64_t v, uint64_t mul) { // Murmur-inspired hashing. uint64_t a = (u ^ v) * mul; a ^= (a >> 47); uint64_t b = (v ^ a) * mul; b ^= (b >> 47); b *= mul; return b; } STATIC_INLINE uint64_t HashLen0to16(const char *s, size_t len) { if (len >= 8) { uint64_t mul = k2 + len * 2; uint64_t a = Fetch(s) + k2; uint64_t b = Fetch(s + len - 8); uint64_t c = Rotate(b, 37) * mul + a; uint64_t d = (Rotate(a, 25) + b) * mul; return HashLen16(c, d, mul); } if (len >= 4) { uint64_t mul = k2 + len * 2; uint64_t a = Fetch32(s); return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul); } if (len > 0) { uint8_t a = s[0]; uint8_t b = s[len >> 1]; uint8_t c = s[len - 1]; uint32_t y = static_cast(a) + (static_cast(b) << 8); uint32_t z = len + (static_cast(c) << 2); return ShiftMix(y * k2 ^ z * k0) * k2; } return k2; } // Return a 16-byte hash for 48 bytes. Quick and dirty. // Callers do best to use "random-looking" values for a and b. STATIC_INLINE pair WeakHashLen32WithSeeds( uint64_t w, uint64_t x, uint64_t y, uint64_t z, uint64_t a, uint64_t b) { a += w; b = Rotate(b + a + z, 21); uint64_t c = a; a += x; a += y; b += Rotate(a, 44); return make_pair(a + z, b + c); } // Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. STATIC_INLINE pair WeakHashLen32WithSeeds( const char* s, uint64_t a, uint64_t b) { return WeakHashLen32WithSeeds(Fetch(s), Fetch(s + 8), Fetch(s + 16), Fetch(s + 24), a, b); } // A subroutine for CityHash128(). Returns a decent 128-bit hash for strings // of any length representable in signed long. Based on City and Murmur. STATIC_INLINE uint128_t CityMurmur(const char *s, size_t len, uint128_t seed) { uint64_t a = Uint128Low64(seed); uint64_t b = Uint128High64(seed); uint64_t c = 0; uint64_t d = 0; signed long l = len - 16; if (l <= 0) { // len <= 16 a = ShiftMix(a * k1) * k1; c = b * k1 + HashLen0to16(s, len); d = ShiftMix(a + (len >= 8 ? Fetch(s) : c)); } else { // len > 16 c = HashLen16(Fetch(s + len - 8) + k1, a); d = HashLen16(b + len, c + Fetch(s + len - 16)); a += d; do { a ^= ShiftMix(Fetch(s) * k1) * k1; a *= k1; b ^= a; c ^= ShiftMix(Fetch(s + 8) * k1) * k1; c *= k1; d ^= c; s += 16; l -= 16; } while (l > 0); } a = HashLen16(a, c); b = HashLen16(d, b); return uint128_t(a ^ b, HashLen16(b, a)); } uint128_t CityHash128WithSeed(const char *s, size_t len, uint128_t seed) { if (len < 128) { return CityMurmur(s, len, seed); } // We expect len >= 128 to be the common case. Keep 56 bytes of state: // v, w, x, y, and z. pair v, w; uint64_t x = Uint128Low64(seed); uint64_t y = Uint128High64(seed); uint64_t z = len * k1; v.first = Rotate(y ^ k1, 49) * k1 + Fetch(s); v.second = Rotate(v.first, 42) * k1 + Fetch(s + 8); w.first = Rotate(y + z, 35) * k1 + x; w.second = Rotate(x + Fetch(s + 88), 53) * k1; // This is the same inner loop as CityHash64(), manually unrolled. do { x = Rotate(x + y + v.first + Fetch(s + 8), 37) * k1; y = Rotate(y + v.second + Fetch(s + 48), 42) * k1; x ^= w.second; y += v.first + Fetch(s + 40); z = Rotate(z + w.first, 33) * k1; v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch(s + 16)); std::swap(z, x); s += 64; x = Rotate(x + y + v.first + Fetch(s + 8), 37) * k1; y = Rotate(y + v.second + Fetch(s + 48), 42) * k1; x ^= w.second; y += v.first + Fetch(s + 40); z = Rotate(z + w.first, 33) * k1; v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch(s + 16)); std::swap(z, x); s += 64; len -= 128; } while (OIIO_LIKELY(len >= 128)); x += Rotate(v.first + z, 49) * k0; y = y * k0 + Rotate(w.second, 37); z = z * k0 + Rotate(w.first, 27); w.first *= 9; v.first *= k0; // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. for (size_t tail_done = 0; tail_done < len; ) { tail_done += 32; y = Rotate(x + y, 42) * k0 + v.second; w.first += Fetch(s + len - tail_done + 16); x = x * k0 + w.first; z += w.second + Fetch(s + len - tail_done); w.second += v.first; v = WeakHashLen32WithSeeds(s + len - tail_done, v.first + z, v.second); v.first *= k0; } // At this point our 56 bytes of state should contain more than // enough information for a strong 128-bit hash. We use two // different 56-byte-to-8-byte hashes to get a 16-byte final result. x = HashLen16(x, v.first); y = HashLen16(y + z, w.first); return uint128_t(HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second)); } STATIC_INLINE uint128_t CityHash128(const char *s, size_t len) { return len >= 16 ? CityHash128WithSeed(s + 16, len - 16, uint128_t(Fetch(s), Fetch(s + 8) + k0)) : CityHash128WithSeed(s, len, uint128_t(k0, k1)); } uint128_t Fingerprint128(const char* s, size_t len) { return CityHash128(s, len); } } // namespace farmhashcc // namespace NAMESPACE_FOR_HASH_FUNCTIONS { OIIO_NAMESPACE_BEGIN namespace farmhash { // BASIC STRING HASHING // Hash function for a byte array. See also Hash(), below. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. uint32_t Hash32(const char* s, size_t len) { return DebugTweak( (can_use_sse41 & x86_64) ? farmhashnt::Hash32(s, len) : (can_use_sse42 & can_use_aesni) ? farmhashsu::Hash32(s, len) : can_use_sse42 ? farmhashsa::Hash32(s, len) : farmhashmk::Hash32(s, len)); } // Hash function for a byte array. For convenience, a 32-bit seed is also // hashed into the result. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. uint32_t Hash32WithSeed(const char* s, size_t len, uint32_t seed) { return DebugTweak( (can_use_sse41 & x86_64) ? farmhashnt::Hash32WithSeed(s, len, seed) : (can_use_sse42 & can_use_aesni) ? farmhashsu::Hash32WithSeed(s, len, seed) : can_use_sse42 ? farmhashsa::Hash32WithSeed(s, len, seed) : farmhashmk::Hash32WithSeed(s, len, seed)); } // Hash function for a byte array. For convenience, a 64-bit seed is also // hashed into the result. See also Hash(), below. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. uint64_t Hash64(const char* s, size_t len) { return DebugTweak( (can_use_sse42 & x86_64) ? farmhashte::Hash64(s, len) : farmhashxo::Hash64(s, len)); } // Hash function for a byte array. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. size_t Hash(const char* s, size_t len) { return sizeof(size_t) == 8 ? Hash64(s, len) : Hash32(s, len); } // Hash function for a byte array. For convenience, a 64-bit seed is also // hashed into the result. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. uint64_t Hash64WithSeed(const char* s, size_t len, uint64_t seed) { return DebugTweak(farmhashna::Hash64WithSeed(s, len, seed)); } // Hash function for a byte array. For convenience, two seeds are also // hashed into the result. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. uint64_t Hash64WithSeeds(const char* s, size_t len, uint64_t seed0, uint64_t seed1) { return DebugTweak(farmhashna::Hash64WithSeeds(s, len, seed0, seed1)); } // Hash function for a byte array. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. uint128_t Hash128(const char* s, size_t len) { return DebugTweak(farmhashcc::Fingerprint128(s, len)); } // Hash function for a byte array. For convenience, a 128-bit seed is also // hashed into the result. // May change from time to time, may differ on different platforms, may differ // depending on NDEBUG. uint128_t Hash128WithSeed(const char* s, size_t len, uint128_t seed) { return DebugTweak(farmhashcc::CityHash128WithSeed(s, len, seed)); } // BASIC NON-STRING HASHING // FINGERPRINTING (i.e., good, portable, forever-fixed hash functions) // Fingerprint function for a byte array. Most useful in 32-bit binaries. uint32_t Fingerprint32(const char* s, size_t len) { return farmhashmk::Hash32(s, len); } // Fingerprint function for a byte array. uint64_t Fingerprint64(const char* s, size_t len) { return farmhashna::Hash64(s, len); } // Fingerprint function for a byte array. uint128_t Fingerprint128(const char* s, size_t len) { return farmhashcc::Fingerprint128(s, len); } // Older and still available but perhaps not as fast as the above: // farmhashns::Hash32{,WithSeed}() // } // namespace NAMESPACE_FOR_HASH_FUNCTIONS } /*end namespace farmhash*/ OIIO_NAMESPACE_END #if FARMHASHSELFTEST #ifndef FARMHASH_SELF_TEST_GUARD #define FARMHASH_SELF_TEST_GUARD #include #include #include using std::cout; using std::cerr; using std::endl; using std::hex; static const uint64_t kSeed0 = 1234567; static const uint64_t kSeed1 = k0; static const int kDataSize = 1 << 20; static const int kTestSize = 300; #define kSeed128 Uint128(kSeed0, kSeed1) static char data[kDataSize]; static int completed_self_tests = 0; static int errors = 0; // Initialize data to pseudorandom values. void Setup() { if (completed_self_tests == 0) { uint64_t a = 9; uint64_t b = 777; for (int i = 0; i < kDataSize; i++) { a += b; b += a; a = (a ^ (a >> 41)) * k0; b = (b ^ (b >> 41)) * k0 + i; uint8_t u = b >> 37; memcpy(data + i, &u, 1); // uint8_t -> char } } } int NoteErrors() { #define NUM_SELF_TESTS 9 if (++completed_self_tests == NUM_SELF_TESTS) std::exit(errors > 0); return errors; } template inline bool IsNonZero(T x) { return x != 0; } template <> inline bool IsNonZero(uint128_t x) { return x != Uint128(0, 0); } #endif // FARMHASH_SELF_TEST_GUARD namespace farmhashccTest { uint32_t CreateSeed(int offset, int salt) { uint32_t h = static_cast(salt & 0xffffffff); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h += static_cast(offset & 0xffffffff); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); return h; } #undef SEED #undef SEED1 #undef SEED0 #define SEED CreateSeed(offset, -1) #define SEED0 CreateSeed(offset, 0) #define SEED1 CreateSeed(offset, 1) #undef TESTING #define TESTING 1 #if TESTING uint32_t expected[] = { 4223616069u, 3696677242u, 1039179260u, 1690343979u, 1018511555u, 2464489001u, 20368522u, 2663783964u, 175201532u, 1619210592u, 4081014168u, 2576519988u, 3285042206u, 502478099u, 739479538u, 1500332790u, 13754768u, 3789353455u, 3473868058u, 1909255088u, 2212771159u, 1112731063u, 826915357u, 2893489933u, 118369799u, 1848668220u, 1308219822u, 249416982u, 64306364u, 4221800195u, 1020067935u, 3955445564u, 563346294u, 550236731u, 2339016688u, 1826259714u, 3872358639u, 2295981050u, 1870005390u, 4015628802u, 1451961420u, 653440099u, 1292493871u, 164377749u, 1717712483u, 463414587u, 3924343675u, 1050492084u, 3566618804u, 2046983362u, 31917516u, 2957164615u, 230718965u, 999595115u, 3534822176u, 2175709186u, 965707431u, 441796222u, 2481718051u, 1827777486u, 2590087362u, 3879448744u, 3515079898u, 1601433082u, 982764532u, 254808716u, 1293372530u, 4205605817u, 947001462u, 1138890052u, 176305566u, 2447367541u, 2973802542u, 4123621138u, 3083865840u, 1706367795u, 792114347u, 2880110657u, 440613768u, 195054868u, 1359016305u, 3363804638u, 649488537u, 1624045597u, 1441938215u, 3147758996u, 3199173578u, 2597283203u, 2191333609u, 3763129144u, 1117290165u, 1062549743u, 2565615889u, 1046361554u, 1581968261u, 1058773671u, 1123053168u, 3807622275u, 1486749916u, 3900816089u, 2437877004u, 1894455839u, 1912520953u, 1914997013u, 561048608u, 1643267444u, 3671572006u, 194811086u, 1468911468u, 2179206286u, 673206794u, 3486923651u, 3741426466u, 3292160512u, 697001377u, 1900763774u, 3726097344u, 629282039u, 3578723715u, 2868028489u, 3269862919u, 2303349487u, 3643953525u, 2307255916u, 849996280u, 732080434u, 909961480u, 3542445214u, 2628347095u, 4236856917u, 1380660650u, 2631821908u, 2007289004u, 3509705198u, 3788541675u, 789457322u, 3090670546u, 638977894u, 3503881773u, 947102987u, 1525325287u, 1816697045u, 2706647405u, 288763142u, 3505438495u, 481308609u, 2882636782u, 3745162621u, 3503467033u, 428247823u, 176408838u, 333551502u, 1001068721u, 1681483651u, 75380831u, 4191469679u, 3627361839u, 2736617386u, 3120737438u, 1297502456u, 864896482u, 85674920u, 2886047255u, 4119881331u, 2496990525u, 3442502055u, 1806582817u, 3186345024u, 4099591287u, 2560171465u, 3489229104u, 3065015872u, 2755089808u, 3098442882u, 378524719u, 2664097023u, 1771960725u, 2901182183u, 55258521u, 1266621443u, 581644891u, 37790450u, 1800731704u, 3601350920u, 53428754u, 2759476837u, 3391093099u, 1496510311u, 2511119507u, 2636877410u, 631613207u, 1573846064u, 260484875u, 1088212603u, 2369525206u, 322522428u, 3191396600u, 2076543340u, 1552496658u, 2739811558u, 3867875546u, 2051584261u, 2126250818u, 901517871u, 3651631165u, 1323139145u, 1521111765u, 477802997u, 3508559783u, 383954241u, 3804516756u, 4250206331u, 2655954340u, 2484996477u, 1417544845u, 1520282298u, 2745204366u, 2869345147u, 1872738335u, 2592877343u, 1619744564u, 1804962124u, 3458679890u, 423948620u, 273645618u, 4187865426u, 376057175u, 2943431463u, 3581950599u, 1035398331u, 1088213445u, 861988903u, 1323370244u, 777069428u, 506235917u, 369720851u, 2789995854u, 230915180u, 1505086948u, 940361236u, 3727873235u, 1159167499u, 1860302871u, 3456858862u, 3923555152u, 2131072714u, 2910461068u, 3671950363u, 2010742682u, 4088068851u, 3616470388u, 2087714788u, 221675509u, 1230154072u, 3450704646u, 1463226695u, 1998357699u, 266026801u, 619568740u, 3560427266u, 4148162586u, 3150417316u, 1356375822u, 2056097622u, 627905802u, 3881675638u, 2309738053u, 971916703u, 3447805361u, 1673575328u, 673084328u, 3317849401u, 2836362782u, 2377208890u, 3275350588u, 158350552u, 2553241779u, 2497264995u, 3262882649u, 3897937187u, 1598963653u, 3068514414u, 601541505u, 374517071u, 3380795976u, 235752573u, 284670003u, 2990192160u, 904937105u, 2306579150u, 2117362589u, 1635274830u, 3355572906u, 170799903u, 1226685528u, 664567688u, 413219134u, 878324258u, 4026159448u, 3620649295u, 1823625377u, 3175888439u, 1759344347u, 2640637095u, 3549558u, 2192984935u, 978623493u, 804017880u, 3877562323u, 3843116489u, 1641748342u, 1853539444u, 3001178468u, 3443560727u, 2685426077u, 1653064722u, 349231508u, 2726789654u, 3136215581u, 768402830u, 269384321u, 531936536u, 2592883487u, 1343156334u, 3628619802u, 1477143570u, 4269458419u, 3285611028u, 959104925u, 2712290710u, 3480237248u, 835796333u, 2020636251u, 1191914589u, 126521603u, 4288023938u, 3731699932u, 2136758855u, 985780142u, 193807575u, 1850544433u, 653947619u, 3929316796u, 381871169u, 950486363u, 1787262279u, 360480382u, 1800636585u, 1039258631u, 3682073259u, 1262819303u, 1786000319u, 1570627191u, 893065837u, 301304916u, 1478469809u, 623018819u, 2742232545u, 2058913014u, 1706060059u, 2421125401u, 1315829592u, 3208766775u, 1805586156u, 575853086u, 3085025513u, 4010908260u, 2344058256u, 3814407434u, 1458485673u, 2474514786u, 3581895658u, 2710719679u, 190812706u, 2135454262u, 2620080728u, 3400757986u, 1669914857u, 1559978393u, 1629811331u, 3096616493u, 1391424435u, 4158376003u, 1015657076u, 794783832u, 479952178u, 1150290207u, 2497437906u, 231815090u, 755078067u, 3832053281u, 63649475u, 2415822606u, 4105027719u, 1706992318u, 1106598740u, 3941945667u, 1271300761u, 505882259u, 760186809u, 2657183368u, 1925422058u, 1039773764u, 880219458u, 4275949176u, 1556833823u, 925882132u, 4216310340u, 757497522u, 461833914u, 3884002070u, 2790957660u, 2100050089u, 651959176u, 1380301291u, 1289124125u, 452314403u, 226156280u, 3306924715u, 1750807758u, 2290180542u, 1953760569u, 2253069096u, 3960924806u, 1786291620u, 60736185u, 2569018293u, 3870479674u, 2247005661u, 2239850953u, 4261808536u, 3282975782u, 780945879u, 3349849383u, 1579362556u, 2265045884u, 905088740u, 725212379u, 3156479246u, 2501620391u, 3062836263u, 4070422690u, 996797869u, 4082582315u, 976105756u, 303983602u, 1862104804u, 3864508254u, 3383979677u, 2835500286u, 2798364010u, 519359476u, 3447342725u, 194373889u, 3313466630u, 232399983u, 2841787856u, 1672751454u, 3345183154u, 1805381384u, 2226129336u, 2847829057u, 2350774567u, 2838540121u, 2757948482u, 1017002062u, 2329150951u, 2171488196u, 3668619047u, 3874977844u, 3287966998u, 262346753u, 2493054715u, 2298644430u, 2926101182u, 1528457638u, 598656233u, 2615845874u, 989110727u, 820441411u, 253617372u, 2201077208u, 2047569338u, 3114356329u, 3335563734u, 2967673540u, 768438341u, 1417708203u, 3873718246u, 1538441843u, 1279167650u, 3917966776u, 2218481734u, 1015935150u, 1957845042u, 1318150213u, 3146423971u, 4218994877u, 1162470863u, 1519718292u, 2594658906u, 665870414u, 3430347817u, 3933868731u, 1597041394u, 3138684682u, 3398212027u, 1064647658u, 1576321132u, 14792918u, 224938029u, 3706456050u, 847274786u, 2645698692u, 1743374687u, 2343133224u, 3066596790u, 2857270120u, 200596308u, 452055528u, 2319312082u, 3488655402u, 4146865894u, 608206438u, 2699777051u, 3687240713u, 327957508u, 3664730153u, 568134564u, 2993484554u, 4159860363u, 4274533921u, 1079994063u, 2360220210u, 3609597760u, 3639708902u, 2836180437u, 1069910270u, 1892427666u, 1874729790u, 1267712826u, 121886940u, 3572289214u, 2475945610u, 783779452u, 588827737u, 1531395014u, 2085084212u, 2219189792u, 3981444548u, 2218885336u, 1691622694u, 2053232885u, 1386558530u, 2182946189u, 2365247285u, 1871081313u, 2935751853u, 38413723u, 543465863u, 900691890u, 2899905665u, 575120562u, 93133904u, 457154948u, 2983705792u, 4232229200u, 2038565963u, 614693984u, 3405328302u, 4083090010u, 2088004171u, 244031209u, 1861889294u, 2417109253u, 3299562328u, 4158642443u, 4199064449u, 3161611046u, 885015950u, 3677904099u, 2969861785u, 772348805u, 1712263832u, 3219357614u, 484271305u, 3645706114u, 2059620251u, 409557488u, 2278896731u, 224475749u, 3523022952u, 2057140088u, 449131785u, 1149879244u, 4255363996u, 3602720135u, 1690010854u, 2503998822u, 2750828466u, 3340671802u, 1447583863u, 2649684943u, 2764747249u, 3046070595u, 3441726138u, 3840332559u, 3156747501u, 1288666680u, 1472744459u, 3452391933u, 1617542784u, 217869690u, 3718469527u, 348639731u, 590532355u, 43789787u, 22606314u, 1621559290u, 2231743261u, 2234620879u, 544748955u, 3169387920u, 203343594u, 3272552527u, 1078282365u, 809576321u, 854207584u, 3625491053u, 1193737267u, 1628966807u, 2661421060u, 2433442061u, 3886639039u, 2149304418u, 303000565u, 1432830882u, 137378235u, 1135974068u, 318705754u, 2491227157u, 2627534472u, 3520352233u, 2488397682u, 3969194920u, 3843962181u, 2135981459u, 2611933220u, 799460731u, 2300968851u, 3412851628u, 3070914013u, 3555224260u, 4125937572u, 240359903u, 722496673u, 2061023600u, 3843919221u, 2759960043u, 1191155322u, 1504041490u, 3735253656u, 1773124736u, 101110011u, 1627699578u, 2645634551u, 263603947u, 1388368439u, 677146538u, 1644201982u, 2625699644u, 2403862553u, 2426069017u, 3613511705u, 915141802u, 2981654265u, 3474818167u, 2611101773u, 627891434u, 762754924u, 2143021902u, 51067670u, 4017746573u, 2269879853u, 3037857950u, 2388899692u, 582729171u, 1886116725u, 2281219772u, 264704948u, 3509984037u, 4078683368u, 2172959411u, 1807195632u, 3357092302u, 2253764928u, 2320369390u, 3076335959u, 2623583210u, 168378015u, 1435562650u, 1100977467u, 3160490319u, 2550328495u, 2396855930u, 1347823908u, 1617990918u, 3849653099u, 3224111576u, 1681539821u, 4171542880u, 552200045u, 3562947778u, 1676237880u, 3747732307u, 2453332913u, 865530667u, 3566636849u, 3485502777u, 336779723u, 2535942410u, 1685000184u, 820545711u, 1893670486u, 1273910461u, 1193758569u, 970365241u, 381205962u, 3612810852u, 1160577445u, 541488143u, 4005031080u, 2333965236u, 2419855455u, 3484533538u, 3073937876u, 908466956u, 661391539u, 2342122412u, 1467049112u, 1785800827u, 135343033u, 139643209u, 2438375667u, 974654058u, 3216478230u, 3807620420u, 779043363u, 2812846449u, 333254784u, 1025244024u, 2242303095u, 2476683742u, 350018683u, 174652916u, 933097576u, 826905896u, 559603581u, 2777181260u, 164915169u, 4070353203u, 1459055748u, 297303985u, 3103837241u, 3812514233u, 232265137u, 2032819099u, 1523091376u, 3531238208u, 1403510182u, 2886832080u, 2599705941u, 2789695716u, 68437968u, 3823813791u, 1040994569u, 3024194990u, 2461740520u, 3735391266u, 2042207153u, 2461678616u, 3519231840u, 1344224923u, 411442756u, 1179779351u, 7661528u, 778352196u, 3288808867u, 589356197u, 2627504511u, 3374744599u, 3312172905u, 357423007u, 3539567796u, 4044452215u, 1445118403u, 2937983820u, 184089910u, 346201845u, 2427295202u, 1345448010u, 2884434843u, 3085001879u, 2640105409u, 315310640u, 3530289798u, 3362974764u, 963602652u, 75228477u, 3509381180u, 4012777756u, 2380345941u, 1073137836u, 2083960378u, 1220315185u, 3628720934u, 3508867818u, 67148343u, 3558085158u, 1753943368u, 863309561u, 2844713625u, 441921850u, 854732254u, 816793316u, 2555428747u, 3440623414u, 1707304366u, 3189874375u, 1623229221u, 1220335976u, 806745430u, 3909262947u, 1680369031u, 2926179486u, 3410391660u, 3991630434u, 2876458763u, 1179167079u, 536360759u, 1592117159u, 1514343977u, 1032622306u, 2057494855u, 784938958u, 178402996u, 1152907972u, 2326185495u, 2939973666u, 4181120253u, 552831733u, 664251856u, 1297139539u, 1969357631u, 1474065957u, 3055419017u, 3395829380u, 3316562752u, 2168409017u, 614624786u, 3585854336u, 668291094u, 1162889217u, 3773171307u, 2263271126u, 355089668u, 3195850578u, 3396793277u, 3519870267u, 527857605u, 3972392320u, 2224315010u, 4047225561u, 3271434798u, 3192704713u, 2798505213u, 3932215896u, 3792924012u, 3796843756u, 453872975u, 4050552799u, 1056432676u, 928166947u, 121311642u, 930989547u, 2087070683u, 1288978057u, 1556325239u, 1812435626u, 1682385724u, 1214364933u, 904760776u, 3957045528u, 3949822847u, 2411065880u, 3716420732u, 3424837835u, 3833550693u, 1799375326u, 2012368921u, 2768764136u, 1786111037u, 4055479315u, 3751639533u, 2808224623u, 3492656387u, 1306824780u, 2624000170u, 3134795218u, 1778409297u, 3900821801u, 593336325u, 2772069220u, 2980873673u, 3574497158u, 3994780459u, 4246519854u, 3482758570u, 4228015183u, 33101083u, 1769887734u, 4158035314u, 3690638998u, 1119035482u, 4134969651u, 2483207353u, 3932823321u, 285829887u, 3485140138u, 1304815138u, 995608264u, 3133997465u, 1195477617u, 2147693728u, 3506673112u, 4234467492u, 1183174337u, 1395340482u, 769199343u, 193262308u, 2798920256u, 3827889422u, 3399695609u, 3036045724u, 2999477386u, 3567001759u, 2682864314u, 1414023907u, 3699872975u, 3369870701u, 2662284872u, 2179640019u, 2485080099u, 3234415609u, 3755915606u, 1339453220u, 1567403399u, 2076272391u, 293946298u, 3861962750u, 1291949822u, 2916864995u, 132642326u, 2215117062u, 2205863575u, 2488805750u, 405632860u, 3248129390u, 2952606864u, 896734759u, 2047417173u, 3865951392u, 657296855u, 1328547532u, 3966511825u, 3959682388u, 4171801020u, 2981416957u, 1868896247u, 790081075u, 3143666398u, 2950766549u, 2065854887u, 2737081890u, 995061774u, 1510712611u, 2865954809u, 565044286u, 1565631102u, 1500654931u, 494822108u, 2803515503u, 1058154996u, 3506280187u, 856885925u, 4204610546u, 800905649u, 1130711562u, 558146282u, 2053400666u, 449794061u, 2643520245u, 2101248725u, 3123292429u, 3583524041u, 983372394u, 1587743780u, 672870813u, 444833475u, 100741452u, 366232251u, 1717951248u, 524144122u, 1362432726u, 1304947719u, 674306020u, 405665887u, 4081931036u, 1580408204u, 2343242778u, 3901654006u, 2627173567u, 3015148205u, 814686701u, 1327920712u, 1346494176u, 2468632605u, 2259795544u, 2519278184u, 2129281928u, 2860266380u, 4001619412u, 1154910973u, 2841022216u, 1199925485u, 1372200293u, 2713179055u, 3609776550u, 2896463880u, 1056406892u, 177413841u, 40180172u, 3274788406u, 660921784u, 1686225028u, 4003382965u, 2532691887u, 4256809101u, 1186018983u, 667359096u, 2375266493u, 2760222015u, 745187078u, 312264012u, 396822261u, 2588536966u, 2026998998u, 1766454365u, 3218807676u, 3915487497u, 2630550356u, 4130063378u, 4231937074u, 752212123u, 3085144349u, 3267186363u, 4103872100u, 4193207863u, 1306401710u, 3014853131u, 1067760598u, 2306188342u, 2437881506u, 4258185052u, 2506507580u, 130876929u, 1076894205u, 4106981702u, 2799540844u, 945747327u, 1436722291u, 2499772225u, 2571537041u, 2038830635u, 2066826058u, 2892892912u, 524875858u, 3392572161u, 2869992096u, 1308273341u, 923668994u, 1980407857u, 2275009652u, 240598096u, 2658376530u, 3505603048u, 1022603789u, 582423424u, 846379327u, 4092636095u, 4177298326u, 1004173023u, 2154027018u, 2993634669u, 1098364089u, 3035642175u, 1335688126u, 1376393415u, 1252369770u, 3815033328u, 1999309358u, 1234054757u, 1388595255u, 2859334775u, 366532860u, 3453410395u, 4226967708u, 1321729870u, 2078463405u, 156766592u, 3157683394u, 3549293384u, 3348214547u, 2879648344u, 1144813399u, 2758966254u, 647753581u, 813615926u, 2035441590u, 1961053117u, 600168686u, 2192833387u, 3156481401u, 3627320321u, 383550248u, 81209584u, 2339331745u, 1284116690u, 1980144976u, 2955724163u, 789301728u, 3842040415u, 1115881490u, 965249078u, 4098663322u, 1870257033u, 2923150701u, 4217108433u, 183816559u, 2104089285u, 2640095343u, 3173757052u, 927847464u, 2383114981u, 4287174363u, 1886129652u, 70635161u, 1182924521u, 1121440038u, 4246220730u, 3890583049u, 975913757u, 2436253031u, 1074894869u, 1301280627u, 992471939u, 735658128u, 244441856u, 1541612456u, 3457776165u, 3503534059u, 1931651133u, 349142786u, 3669028584u, 1828812038u, 99128389u, 1364272849u, 1963678455u, 3971963311u, 2316950886u, 1308901796u, 2789591580u, 1460494965u, 2380227479u, 1577190651u, 1755822080u, 2911014607u, 859387544u, 13023113u, 2319243370u, 2522582211u, 2299110490u, 3342378874u, 2589323490u, 1884430765u, 3739058655u, 2419330954u, 355389916u, 273950915u, 3670136553u, 410946824u, 3174041420u, 2609010298u, 3059091350u, 2300275014u, 725729828u, 2548380995u, 1738849964u, 1257081412u, 79430455u, 810321297u, 3246190593u, 1007937684u, 912115394u, 40880059u, 3450073327u, 4289832174u, 2253485111u, 1065639151u, 2953189309u, 124779113u, 654299738u, 115760833u, 1250932069u, 884995826u, 3998908281u, 1382882981u, 1134187162u, 3202324501u, 487502928u, 3032756345u, 4057517628u, 933197381u, 2319223127u, 2044528655u, 2554572663u, 4049450620u, 1620812836u, 2832905391u, 2273005481u, 1913090121u, 1055456023u, 510593296u, 3285343192u, 2912822536u, 1645225063u, 638418430u, 452701300u, 1025483165u, 1639370512u, 167948643u, 2809842730u, 2983135664u, 407521332u, 1543756616u, 3949773145u, 4283462892u, 659962275u, 3878013463u, 1000748756u, 4053212051u, 4099239406u, 3467581965u, 354635541u, 21301844u, 3831212473u, 3189450571u, 2264401966u, 4096484849u, 1736448515u, 3976926096u, 3727194724u, 2243487039u, 585209095u, 3143046007u, 969558123u, 3037113502u, 3594170243u, 2835860223u, 3775493975u, 2787220812u, 2274252217u, 2915380701u, 3077533278u, 1252871826u, 1519790952u, 205297661u, 2950557658u, 3956882191u, 2724439401u, 3694608025u, 124028038u, 216019153u, 1533010676u, 2259986336u, 2014061617u, 2068617849u, 3078123052u, 2692046098u, 1582812948u, 396916232u, 1470894001u, 1694309312u, 300268215u, 1553892743u, 671176040u, 1544988994u, 2793402821u, 4194972569u, 2296476154u, 748354332u, 3491325898u, 4261053291u, 1104998242u, 797816835u, 243564059u, 2197717393u, 299029458u, 1675252188u, 3139770041u, 583018574u, 2532106100u, 2099391658u, 3760526730u, 3422719327u, 3556917689u, 2374009285u, 2130865894u, 3710563151u, 1437538307u, 3938030842u, 2006930694u, 2151243336u, 1939741287u, 1957068175u, 2135147479u, 649553342u, 1713643042u, 4188696599u, 1698739939u, 3549427584u, 1016382174u, 322644378u, 2476164549u, 2037263020u, 88036019u, 2548960923u, 539867919u, 2871157727u, 4031659929u, 754087252u, 972656559u, 4246379429u, 3877308578u, 2059459630u, 3614934323u, 1410565271u, 2102980459u, 215395636u, 1083393481u, 3775523015u, 2062750105u, 2475645882u, 3041186774u, 3534315423u, 758607219u, 1686100614u, 180500983u, 1155581185u, 1476664671u, 2918661695u, 3812731350u, 4003853737u, 4148884881u, 1468469436u, 3278880418u, 1045838071u, 1049161262u, 360450415u, 3158065524u, 814443735u, 3391401707u, 729968410u, 738771593u, 3662738792u, 1672830580u, 4199496163u, 188487238u, 219098233u, 2141731267u, 3890250614u, 2988780375u, 4026279523u, 3489429375u, 2468433807u, 1178270701u, 2685094218u, 2716621497u, 3718335529u, 2273344755u, 701110882u, 1925717409u, 1515176562u, 2325460593u, 3954798930u, 784566105u, 3769422266u, 1641530321u, 2703876862u, 2907480267u, 1828076455u, 1805635221u, 3883381245u, 1476756210u, 2072514392u, 3658557081u, 2003610746u, 2556845550u, 729594004u, 3303898266u, 1968227254u, 423204951u, 231828688u, 4223697811u, 698619045u, 3636824418u, 2738779239u, 2333529003u, 2833158642u, 580285428u, 3038148234u, 1012378004u, 1113647298u, 1424593483u, 4053247723u, 1167152941u, 2677383578u, 3419485379u, 2135673840u, 440478166u, 1682229112u, 3226724137u, 1217439806u, 3828726923u, 3636576271u, 3467643156u, 2005614908u, 2655346461u, 2345488441u, 1027557096u, 3594084220u, 1372306343u, 2342583762u, 4291342905u, 4094931814u, 3254771759u, 821978248u, 2404930117u, 1143937655u, 3156949255u, 3460606610u, 449701786u, 3474906110u, 1932585294u, 2283357584u, 1808481478u, 3522851029u, 3040164731u, 1530172182u, 2950426149u, 1402416557u, 756419859u, 4132576145u, 724994790u, 2852015871u, 2177908339u, 899914731u, 139675671u, 1423281870u, 3198458070u, 807581308u, 2021611521u, 1801452575u, 1425984297u, 2833835949u, 1536827865u, 3902351840u, 164546042u, 1872840974u, 3986194780u, 792156290u, 3378681896u, 941547959u, 3931328334u, 3661060482u, 2386420777u, 3920146272u, 3458621279u, 3348500844u, 2269586542u, 797371473u, 3188953649u, 80514771u, 2913333490u, 1246325623u, 3253846094u, 1723906239u, 1606413555u, 587500718u, 1412413859u, 2310046829u, 2113313263u, 3855635608u, 47271944u, 1112281934u, 3440228404u, 2633519166u, 425094457u, 307659635u, 67338587u, 2412987939u, 2363930989u, 2853008596u, 2844637339u, 922568813u, 130379293u, 2825204405u, 2904442145u, 1176875333u, 1511685505u, 599177514u, 1872681372u, 682394826u, 1888849790u, 3635304282u, 1761257265u, 1571292431u, 355247075u, 1177210823u, 1691529530u, 3629531121u, 3760474006u, 1129340625u, 868116266u, 3908237785u, 1942124366u, 1266630014u, 3214841995u, 334023850u, 1110037019u, 369650727u, 1288666741u, 70535706u, 20230114u, 4284225520u, 727856157u, 293696779u, 1244943770u, 3976592462u, 560421917u, 4171688499u, 2438786950u, 1218144639u, 3809125983u, 1302395746u, 534542359u, 2121993015u, 2899519374u, 3192177626u, 1761707794u, 3101683464u, 1555403906u, 3225675390u, 1875263768u, 4278894569u, 651707603u, 2111591484u, 3802716028u, 2900262228u, 1181469202u, 3254743797u, 1822684466u, 860641829u, 3046128268u, 1284833012u, 1125261608u, 461384524u, 2331344566u, 1274400010u, 990498321u, 3462536298u, 3796842585u, 2346607194u, 279495949u, 3951194590u, 3522664971u, 3169688303u, 726831706u, 1123875117u, 1816166599u, 3759808754u, 2918558151u, 3713203220u, 3369939267u, 466047109u, 384042536u, 587271104u, 2191634696u, 2449929095u, 1157932232u, 2084466674u, 841370485u, 3241372562u, 4277738486u, 2150836793u, 1173569449u, 778768930u, 2594706485u, 3065269405u, 3019263663u, 2660146610u, 2789946230u, 77056913u, 728174395u, 3647185904u, 804562358u, 2697276483u, 881311175u, 1178696435u, 2059173891u, 2308303791u, 221481230u, 50241451u, 3689414100u, 1969074761u, 2732071529u, 1900890356u, 840789500u, 2100609300u, 985565597u, 1220850414u, 2456636259u, 223607678u, 1016310244u, 1937434395u, 85717256u, 275058190u, 3712011133u, 171916016u, 2389569096u, 3679765802u, 3575358777u, 3481108261u, 3178286380u, 2489642395u, 2931039055u, 3086601621u, 3079518902u, 3027718495u, 2506894644u, 2976869602u, 2134336365u, 2420172217u, 918054427u, 661522682u, 1403791357u, 3587174388u, 2623673551u, 1355661457u, 4159477684u, 1109013587u, 3112183488u, 2217849279u, 3500291996u, 2419603731u, 2929886201u, 3854470013u, 1358382103u, 1357666555u, 21053566u, 2716621233u, 3094836862u, 3309729704u, 57086558u, 839187419u, 2757944838u, 3651040558u, 3607536716u, 3691257732u, 2312878285u, 1202511724u, 183479927u, 2509829803u, 109313218u, 478173887u, 2072044014u, 190631406u, 2495604975u, 1010416260u, 3679857586u, 726566957u, 258500881u, 1805873908u, 3081447051u, 2352101327u, 534922207u, 1584552873u, 813470716u, 255914637u, 249169434u, 3193498057u, 1038802706u, 2590158653u, 3147907290u, 663060128u, 1156177857u, 634616100u, 312879189u, 1545020368u, 2054634247u, 3271451914u, 3438291534u, 2181454946u, 3864535432u, 2398586877u, 896491075u, 2810631478u, 2770357487u, 3372930052u, 898070638u, 2051007323u, 392959778u, 36645539u, 3743556044u, 4134529680u, 4124451188u, 566806297u, 2936523982u, 1304761965u, 537399498u, 1940818842u, 40862381u, 36288410u, 3063605629u, 2826611650u, 3961972098u, 1871578006u, 2392095486u, 1136931591u, 513864488u, 173276451u, 3039055682u, 3543322032u, 1943592006u, 657217094u, 1751698246u, 2969618445u, 456616022u, 900309519u, 113892716u, 1126392103u, 1235651045u, 1882073852u, 2136610853u, 2353639710u, 2819956700u, 3980083530u, 828773559u, 224069850u, 902434120u, 2802008036u, 94358995u, 2777723394u, 2812641403u, 2525832595u, 4157388110u, 4235563782u, 937800324u, 141690749u, 568062536u, 550123849u, 1330316521u, 1949488696u, 2296431366u, 1958465262u, 3564751729u, 3748252207u, 120455129u, 1607318832u, 2525729790u, 2640987481u, 2332096657u, 1775969159u, 1555085077u, 2913525137u, 1347085183u, 2376253113u, 3194050574u, 1806090610u, 678641356u, 1499146713u, 383849715u, 3299835823u, 2284860330u, 2614269636u, 3913628844u, 2761334210u, 1959484587u, 529797021u, 239966995u, 3102194829u, 3602307804u, 1122192627u, 3577510006u, 164486066u, 1680137310u, 1473396395u, 1467801424u, 903493660u, 1185943071u, 2798556505u, 2306744492u, 3167201310u, 3577947177u, 3067592134u, 2905506289u, 1210366329u, 204484056u, 2347778932u, 3862374472u, 3277439508u, 4187414621u, 1646699310u, 621385800u, 3934869089u, 3975491588u, 3580085916u, 1925674500u, 2436305348u, 3983301539u, 2739439523u, 3291507446u, 3395637920u, 3753389171u, 2955202032u, 2654255623u, 3771089254u, 2140443405u, 2779834738u, 3261942805u, 3526889244u, 1842009139u, 4048484340u, 2106218403u, 2161244271u, 772152700u, 1158647659u, 3776791619u, 3882186721u, 699525237u, 2954670460u, 1007105869u, 3359152025u, 1146388699u, 1401550303u, 2326582541u, 4181783540u, 1085644043u, 1942143795u, 1038368308u, 1526153809u, 4042547244u, 1891441000u, 2573991874u, 1281441253u, 3635098284u, 1980545715u, 825985487u, 3934748116u, 4228386979u, 1480870944u, 1042194545u, 2397771642u, 2248490001u, 3817869868u, 878654626u, 3785629484u, 1672470870u, 3229367873u, 1894538933u, 1010692731u, 1733824268u, 656620328u, 3048283803u, 3353340056u, 2324965120u, 4192585951u, 2284524675u, 3483884368u, 1510168293u, 1554942691u, 1309709396u, 1241133168u, 3162179280u, 4046378054u, 3171681593u, 1165297136u, 3496703563u, 150437903u, 1948622072u, 1076332463u, 2292479143u, 1464229958u, 3479738093u, 2328067598u, 2334503110u, 833324834u, 3981605747u, 3002629155u, 2854644186u, 2832201336u, 95796957u, 3269249397u, 2358313329u, 3411860910u, 4283292480u, 2802208697u, 1305947955u, 2156803420u, 1991340283u, 189678024u, 447602599u, 1055411517u, 1531748363u, 1555852656u, 412402681u, 3774988152u, 20597551u, 2925024131u, 1423989620u, 3749428061u, 1541439448u, 112270416u, 1936224776u, 132162941u, 3772011507u, 3814102518u, 1908807815u, 444154079u, 823765347u, 3362275567u, 3419047430u, 2108287005u, 2315102125u, 658593738u, 3195094029u, 3721937534u, 3176229204u, 3398835373u, 1271898712u, 1142546577u, 3185986817u, 3562705803u, 2046119567u, 912990621u, 1829977672u, 3459576979u, 1118045834u, 1369529376u, 3320601076u, 3954988953u, 4002467635u, 3359456351u, 1314849568u, 1766750942u, 2998874853u, 1181800239u, 707328036u, 3314954697u, 2066721120u, 598194215u, 1124451278u, 3156679616u, 3742684743u, 2960199690u, 2683497915u, 2566077529u, 937014607u, 102095219u, 4262922475u, 3132264275u, 1262099830u, 862722905u, 2717653494u, 3245583534u, 3427209989u, 3220278124u, 85457091u, 2222333500u, 3513997967u, 3522324951u, 2830855552u, 2215004781u, 3482411840u, 4227160614u, 2030964411u, 1741393851u, 2643723748u, 942813508u, 403442675u, 3112048748u, 530556423u, 3817755244u, 3543286628u, 2247276090u, 1532920842u, 4101962711u, 1446540991u, 3297821473u, 1861255389u, 1984398u, 2366525138u, 377589481u, 3549193828u, 1427765914u, 506831657u, 277278988u, 1447652775u, 3214362239u, 3142198690u, 2843087541u, 468915015u, 807895062u, 2198723907u, 4031145069u, 2417156212u, 4027298697u, 637175947u, 1229254212u, 1773257887u, 1659444818u, 451148891u, 2099741368u, 735351990u, 2534775713u, 3261804619u, 712519954u, 3527962772u, 3758642738u, 4245823575u, 1281314264u, 1167866160u, 1489546151u, 1197354389u, 1043278102u, 2563326586u, 371937794u, 2320164817u, 3189512691u, 573685198u, 4108603513u, 3758899588u, 3507030163u, 2947201212u, 2529492585u, 578234375u, 3362349842u, 3318878925u, 3611203517u, 3059253190u, 4270755916u, 4291274625u, 4237586791u, 4137422245u, 2927218651u, 2444687041u, 797128811u, 2043057612u, 396533859u, 2665256178u, 3346510674u, 1779586176u, 3076562062u, 1882746214u, 921095362u, 2026988397u, 514514911u, 3886379478u, 4218272420u, 1480386793u, 3900160816u, 2292273451u, 1276138356u, 1125461821u, 1912885715u, 3365266013u, 1333211627u, 4085009861u, 1390530102u, 3347984752u, 2721771301u, 1419492325u, 4066766256u, 3250852311u, 820111852u, 1382201318u, 2366036798u, 938032241u, 3100979439u, 487048687u, 2292851045u, 3241399180u, 3912670510u, 2416437067u, 2973194517u, 3507707986u, 1935099406u, 2533441488u, 104616731u, 2892622820u, 3801190339u, 4239188808u, 807238241u, 3300121546u, 2249406147u, 4032114017u, 3713738189u, 3324425575u, 4275607376u, 3663120298u, 4173658372u, 3984289690u, 1827636846u, 3264588778u, 3297165529u, 558623533u, 2728945672u, 1566297318u, 3447249966u, 481719551u, 1596842050u, 1838185946u, 265271620u, 1050246315u, 4046655705u, 1844193138u, 3807563245u, 1075384804u, 1292554949u, 1506525927u, 2921816148u, 2051885269u, 1930534041u, 3872721086u, 1564489377u, 2272482181u, 2849358683u, 589618304u, 2262072443u, 290363051u, 299168363u, 3867603931u, 2868688756u, 2545263115u, 1092098533u, 3885725603u, 2352430409u, 1981595469u, 2047946646u, 1332642839u, 793806516u, 214858837u, 1061484659u, 3192394476u, 1115054785u, 3690637234u, 996792368u, 2023479706u, 3046498231u, 4205835102u, 3870714754u, 257472875u, 3549864599u, 2040276129u, 2414778670u, 812235477u, 2674248196u, 1864096101u, 2257492689u, 1332556794u, 1079540713u, 465530720u, 2304763972u, 830724724u, 3354588920u, 2510713652u, 3103749409u, 468835585u, 1707620787u, 3038024846u, 1000303198u, 3462270146u, 2748698899u, 2100348093u, 511537258u, 1237187486u, 102049383u, 2268226698u, 3162251739u, 4219404629u, 838822407u, 1481440623u, 2989224077u, 2676681975u, 3246551821u, 3812079906u, 370572963u, 2283154352u, 3084789986u, 1961085583u, 1955640586u, 2409348147u, 2284780581u, 1634818716u, 4018221729u, 2320761377u, 3566831899u, 1799560520u, 91431959u, 1754113747u, 1459430477u, 3613658517u, 924489906u, 3406317699u, 866289774u, 3924821603u, 1265394945u, 1870668109u, 151949856u, 2747006534u, 3111906201u, 64039467u, 2314447545u, 2600195638u, 4095795204u, 4162096026u, 1026756826u, 2460047982u, 52686887u, 823198739u, 1518045160u, 2867527376u, 566410761u, 2200433819u, 2114146405u, 2893790965u, 881504901u, 974783212u, 490815659u, 937300283u, 1523735309u, 2511976468u, 2634644947u, 355119367u, 1373773092u, 309232995u, 3088671051u, 787126032u, 3442836843u, 4289194567u, 2177850062u, 1174136430u, 3248982914u, 3129039732u, 1166851580u, 2196451882u, 469595580u, 2130837700u, 3783349021u, 3745262548u, 1236930515u, 3032131496u, 1525591437u, 1823628217u, 1939019255u, 1950270463u, 3659899927u, 3688643445u, 3004399289u, 1155199552u, 357547234u, 2213110526u, 3122658210u, 2667800490u, 2718690333u, 3512372076u, 1098611683u, 2657518392u, 4248458835u, 3109874532u, 1592908438u, 2864927516u, 3635248840u, 1251777186u, 3797340158u, 3508496870u, 303354834u, 1482394062u, 2087100120u, 1595931912u, 608574156u, 723367884u, 907938402u, 3357047807u, 1619629851u, 3092082995u, 89030300u, 916336992u, 1861180168u, 3436334155u, 1375000544u, 3472936241u, 1321217853u, 791356402u, 2872410224u, 2326250297u, 2657644088u, 1748314108u, 4146771421u, 2913114440u, 2924821844u, 2101101496u, 3268017251u, 2109603066u, 690665520u, 1830067573u, 951427661u, 2982533150u, 3884512506u, 2358657479u, 2833210784u, 3419798214u, 3785893994u, 2103940206u, 86759766u, 4031230616u, 3745237192u, 2739453927u, 497038072u, 3303159408u, 1251537249u, 1993408196u, 3185905715u, 2885948408u, 3154277110u, 2444150313u, 2505582079u, 2120610195u, 3266465773u, 1814611964u, 3080050407u, 1079915522u, 1819346505u, 2529946763u, 892097374u, 3740257161u, 3618100441u, 1079900094u, 3607172225u, 737863389u, 360704560u, 3341993089u, 1139047381u, 3132219631u, 1248981859u, 1109338159u, 2004908615u, 4022302594u, 4166640860u, 2959140950u, 3949235962u, 2832278473u, 2200524012u, 2634933043u, 2495844522u, 2613799818u, 4034096813u, 683271795u, 1673546817u, 1363163726u, 1805395136u, 511749501u, 1231032599u, 2305979751u, 345737783u, 3339868854u, 2931857933u, 2323251738u, 1332068477u, 51846558u, 3927238177u, 1387182179u, 1701238601u, 1419275173u, 2580882268u, 3357874599u, 1726558907u, 1292901039u, 1371322339u, 1311713044u, 3526735232u, 4017884184u, 3366093428u, 77140994u, 2128996229u, 1357915765u, 4019691901u, 483989024u, 2390311750u, 2766065288u, 3938587520u, 3064810344u, 1054589198u, 1274997019u, 4040589616u, 1277751144u, 2274907047u, 4170399945u, 2886368209u, 4168922115u, 3901237033u, 3252972311u, 2205185840u, 3403097556u, 3385493699u, 2809751370u, 555319628u, 399539034u, 2998971454u, 1521596214u, 178870216u, 1471733541u, 519629198u, 514159209u, 1500582242u, 1928616587u, 2686427928u, 4133138798u, 1225914083u, 1432713584u, 3559310915u, 3925489366u, 1055613123u, 4126676029u, 2723867653u, 3290604111u, 1377022957u, 2373608155u, 3615237379u, 594338683u, 2645257602u, 2408427260u, 917033274u, 750455097u, 625657657u, 121713200u, 2191273413u, 4043949724u, 3293146785u, 3809297972u, 3947296919u, 115456894u, 1529576616u, 1459278275u, 2157117997u, 1747859293u, 4106665903u, 996939232u, 2007976332u, 4274649009u, 1017725787u, 4244666096u, 1219631331u, 3072426253u, 3547691720u, 1620822012u, 1397717508u, 2031597325u, 3345983430u, 2459068000u, 3645130467u, 2308642742u, 359955852u, 1348467968u, 1133123059u, 2435919062u, 2800365907u, 4213217210u, 4056565603u, 2811666556u, 2318007236u, 3823652401u, 3654086429u, 1273260424u, 1591610446u, 943349350u, 3441227678u, 3779964757u, 233818224u, 3469971032u, 3764095096u, 4009204587u, 678472092u, 1990559652u, 2583121088u, 2978143652u, 2496370864u, 2139539656u, 4287972050u, 295832576u, 3536742861u, 2257466133u, 2738052161u, 1988611898u, 2466189642u, 3294419573u, 2311186273u, 474374532u, 3081964174u, 2515138278u, 835731677u, 1178182694u, 3352119543u, 2884763225u, 3462399574u, 2900817210u, 1993698511u, 2868445043u, 2746444849u, 1205258179u, 2353442946u, 4079040070u, 3624133102u, 2907136076u, 2902521697u, 426813211u, 1418185512u, 3711189488u, 1351506552u, 1934749519u, 46595543u, 401688809u, 3514602124u, 1396852607u, 1951477943u, 2502249173u, 3199695820u, 2890250638u, 4205072507u, 1715623846u, 3266686789u, 3218688128u, 1697759742u, 851227671u, 2358709645u, 4174233268u, 500583683u, 3805940955u, 736234120u, 2710563712u, 1949664540u, 3139414003u, 4293073253u, 1284406972u, 1785182449u, 1051548274u, 2994248357u, 2499882522u, 717208669u, 2039517285u, 518424929u, 143136433u, 2303774671u, 1272930860u, 2286410920u, 788459311u, 273225293u, 2439291703u, 2254505236u, 3446287701u, 3655156558u, 1546628787u, 340081500u, 3285722006u, 1324810435u, 1053980860u, 1779472859u, 2700355724u, 686005017u, 3762376315u, 3963193100u, 1370881135u, 661300087u, 1152753704u, 2349891598u, 3910051187u, 2109444785u, 1311123870u, 2639837565u, 1896770931u, 1081414128u, 869877586u, 4284220400u, 63045374u, 235968615u, 184451062u, 1271099822u, 1319179857u, 3274963209u, 4172272710u, 3388797445u, 2965973320u, 3793110097u, 3327241723u, 2991804005u, 1199544355u, 771553759u, 2031749842u, 2596517372u, 1199888213u, 858347951u, 3340178832u, 2903875412u, 763490382u, 76949161u, 2056544406u, 1145227689u, 998233136u, 2354530024u, 427713587u, 3537837347u, 604661755u, 923986833u, 1023730418u, 798294227u, 432557449u, 801802449u, 1861313429u, 3899128441u, 4068407979u, 2352677083u, 3783539925u, 10731973u, 3390767975u, 3949540249u, 1920121661u, 3248580201u, 641956426u, 2104847395u, 604835744u, 1491663404u, 4255204651u, 1520970746u, 2845653368u, 3247412938u, 3730629005u, 855569514u, 3073294700u, 2429691698u, 3818342476u, 3938869985u, 2731201328u, 2335202643u, 778117742u, 13298408u, 228780590u, 2871715314u, 3253688653u, 4150999702u, 3846220408u, 930808u, 1397128726u, 1964216488u, 2781092828u, 116285375u, 2271239476u, 3724347554u, 2931203895u, 3893169206u, 1883912528u, 2093892660u, 3658787024u, 3095016046u, 1094059199u, 3640239610u, 558564267u, 2102812456u, 464734873u, 925262247u, 1609838036u, 588364741u, 1731409233u, 1576165139u, 3933979268u, 375316394u, 4247099643u, 3670508019u, 4080496835u, 2371248533u, 183762693u, 2078935389u, 2699810414u, 1491815683u, 2999180789u, 1831158425u, 1603373553u, 2006136905u, 3210230591u, 416748595u, 1536971415u, 3271869367u, 1266062739u, 2138414557u, 3337114778u, 1634586826u, 36472629u, 4482244u, 568009609u, 2721216780u, 4037289545u, 2235138807u, 1789351460u, 4067539527u, 1323062829u, 3864620647u, 4192026301u, 4278901241u, 1399025382u, 2826652805u, 1363860382u, 1801770651u, 1613381526u, 1165249276u, 4046576622u, 2535596946u, 3260388176u, 1078898578u, 2259750862u, 643387587u, 237144235u, 4199571427u, 3440917581u, 3067939258u, 2018625455u, 1460528353u, 3138629939u, 1666223528u, 3841139376u, 2528281125u, 885565193u, 2609492686u, 2517257479u, 560864620u, 2261471820u, 3491559165u, 1329620416u, 622383582u, 1759597655u, 2877873893u, 584692817u, 1901728399u, 2599000260u, 3169771644u, 296332336u, 774719455u, 4175920823u, 2287316070u, 4115615023u, 1073335619u, 4240292725u, 1359158837u, 1960974237u, 3173724597u, 1619084286u, 2876340752u, 4065675347u, 480741335u, 1237329941u, 701055566u, 3729009837u, 1314736422u, 4003180069u, 3118519317u, 3035354420u, 3380357671u, 4020909015u, 253958714u, 3545798863u, 3008185002u, 2624719888u, 3219955575u, 3060719376u, 573101682u, 1580316843u, 2610493412u, 3490983536u, 3601975611u, 851470366u, 635384901u, 3427048824u, 1470002757u, 3592460087u, 2265226856u, 4124282457u, 2106385486u, 3334305617u, 4208282753u, 3798749815u, 225396466u, 118791182u, 2523395972u, 194595464u, 2563824631u, 2521301383u, 4224409406u, 468670274u, 1761966400u, 1300908277u, 2570709228u, 1847901526u, 1470099163u, 2690466752u, 1472536718u, 2399279735u, 4150607803u, 1775080054u, 2082537685u, 4080034578u, 1256001880u, 392967725u, 2055838940u, 3349115816u, 1745947263u, 2213925887u, 1836572741u, 2417722792u, 636223705u, 2423329294u, 3960951311u, 1543591052u, 1547914361u, 2760945653u, 3519014111u, 313543871u, 4119598884u, 1071003714u, 2192556597u, 1526995535u, 3929839778u, 536388591u, 3040873792u, 3752682932u, 1640614237u, 2432794021u, 385337403u, 2794410617u, 2386128075u, 1055206708u, 1422747714u, 3759330929u, 2533597496u, 30440955u, 1482899460u, 3350385050u, 616259409u, 3980103795u, 1211364140u, 1040071544u, 594746920u, 1645973936u, 2547331531u, 1097726368u, 700666526u, 2976247482u, 1144906608u, 996506677u, 1997130756u, 800321417u, 1392942823u, 1601662248u, 2079778663u, 529512908u, 2925120134u, 4106433085u, 630221833u, 2423086156u, 1119859778u, 1726827981u, 1870859181u, 2559832707u, 1792284257u, 2059356387u, 3572353364u, 3229407475u, 575621095u, 3221893291u, 2372428048u, 2020123035u, 961449593u, 2243824063u, 3803906611u, 3735348189u, 2981620804u, 4180681078u, 1555330629u, 230736535u, 2075526640u, 749652975u, 713664372u, 2152096659u, 2142067223u, 3322302242u, 1421646830u, 2092832615u, 1213735101u, 3192136753u, 1106723940u, 3455398230u, 2541685524u, 2529956739u, 3789430647u, 1950084508u, 2157395621u, 850457360u, 2758902426u, 2848030169u, 6506379u, 1162213157u, 2981459221u, 272690871u, 3059420255u, 4242691285u, 588065598u, 1206949936u, 3968214184u, 566348532u, 126142880u, 1480567086u, 2959621988u, 2050218418u, 2242731195u, 3833514449u, 1898070331u, 3687399477u, 3891859374u, 868185955u, 2335308774u, 3676335246u, 3871121805u, 2189032743u, 3275728647u, 860492892u, 1590764344u, 4130384758u, 262871548u, 3004764525u, 2685542071u, 991231482u, 435122019u, 3031116998u, 2898921700u, 2917932604u, 4238665148u, 2459072654u, 3444612545u, 4207731740u, 1808564313u, 2798532269u, 3944553556u, 3926395409u, 1633200670u, 4138335224u, 2524878605u, 4184292650u, 3563398268u, 4288943552u, 3802121210u, 957502058u, 2410820887u, 4227117506u, 4018625153u, 4284329158u, 530216712u, 2978986531u, 863452221u, 1910162118u, 4088211378u, 4091971261u, 3150811451u, 4200871487u, 3794038652u, 3041564310u, 2045287082u, 887805614u, 2889167251u, 4120352181u, 1699912580u, 3478922097u, 3211994687u, 3136177842u, 1500806861u, 3211881347u, 2147976385u, 3342722260u, 3359650541u, 4197378460u, 781354073u, 1533623029u, 2204677828u, 3228172832u, 3248592437u, 3355841359u, 560815159u, 1144951236u, 4027015711u, 2882625391u, 339363613u, 2354572719u, 1769831876u, 4238589331u, 1519732871u, 2185834614u, 1601096831u, 129709881u, 39655633u, 367604993u, 1737681770u, 3259114599u, 2767070452u, 872365177u, 1574125529u, 3405020189u, 4181346685u, 1134030380u, 403769171u, 2193351164u, 1426232618u, 2885309450u, 3033612627u, 924948363u, 935514094u, 3202053329u, 912294839u, 1618472324u, 4159158431u, 3744999487u, 777064358u, 3974213124u, 1990246048u, 309725290u, 2449849392u, 1943692420u, 2288635750u, 2433793635u, 2168904061u, 683315308u, 3081493019u, 3477759434u, 3815496269u, 2823504699u, 586945121u, 3088963200u, 3492287335u, 636875049u, 1111206944u, 2037346120u, 1282050044u, 1409681512u, 1786128584u, 755810950u, 2332676758u, 2178142310u, 957827166u, 1014983590u, 1888800725u, 3608595803u, 3200072714u, 2534008478u, 659336139u, 1281728287u, 4060560529u, 2915575125u, 3521503774u, 2926487340u, 1096297674u, 653489861u, 2352326980u, 2561136777u, 1224141198u, 1250479629u, 1297625391u, 2409997371u, 1942483722u, 2481835750u, 1394715707u, 1673070941u, 2456039704u, 3980558014u, 3547934764u, 1882038812u, 1078160498u, 2488279087u, 1848235245u, 1211914722u, 2264928765u, 2807773070u, 270145554u, 583747883u, 3826009010u, 2996618216u, 425727157u, 992726957u, 3384462280u, 726650661u, 1955043265u, 1923879512u, 1854693773u, 2987614542u, 2660044993u, 2457260810u, 426299370u, 2671892900u, 1827308087u, 3083953443u, 1791749638u, 3265087416u, 2119752201u, 2547122538u, 3990783236u, 1912713468u, 3688865211u, 1815780016u, 303699291u, 2416763742u, 2690891610u, 1535193548u, 1107803989u, 1504143133u, 2235270371u, 2545884083u, 2276278682u, 411724404u, 3416925704u, 2565792091u, 3383911757u, 546058824u, 3374654444u, 2364630415u, 2693473470u, 2622125691u, 261864817u, 55682470u, 857617568u, 141304067u, 1885488541u, 155368182u, 1281949051u, 3384522408u, 3254816901u, 1959816782u, 1452224057u, 2830267691u, 3709231247u, 58988202u, 4218130458u, 2984061349u, 1888707848u, 4223605071u, 4241442486u, 375269213u, 3208327038u, 2199916493u, 550337252u, 2855061437u, 276088636u, 114362204u, 2321163647u, 2127813633u, 3289403024u, 2686973202u, 2717376797u, 3593428039u, 3648831666u, 890925902u, 3289404818u, 3289516821u, 4248913260u, 1858916580u, 3303932308u, 1752797086u, 1628149686u, 3245893605u, 1568537311u, 2844194502u, 1593855770u, 2408174109u, 124797514u, 2085649512u, 3188565660u, 2264996276u, 1926696513u, 3053957740u, 2238806881u, 2189050973u, 203685243u, 379855590u, 3920271562u, 1058600179u, 3698061923u, 4255106849u, 608401664u, 1598041932u, 3318266418u, 2535016555u, 852760884u, 1918098822u, 2200437599u, 1532285043u, 3425662132u, 3561293706u, 2231633206u, 4108785088u, 3359152801u, 173534780u, 208383607u, 2862988169u, 2406642243u, 426814583u, 2777335795u, 3322703596u, 954190623u, 615093090u, 4179102978u, 2452847930u, 100239619u, 42471741u, 818352432u, 2190624654u, 504379960u, 3631619975u, 633412456u, 1018421783u, 842645419u, 711808707u, 3424580813u, 2132457941u, 1158335882u, 3567952480u, 2302183699u, 1145788151u, 3474264138u, 3105085243u, 3115506027u, 2783713015u, 3871785309u, 539583269u, 1400252405u, 3857849984u, 4231186588u, 1278653799u, 1760227022u, 761044088u, 3838185417u, 2439542532u, 585283357u, 2055995220u, 937117124u, 3831944855u, 1823586038u, 3287917855u, 485082427u, 3209172809u, 1984570176u, 2818337297u, 2691869057u, 3790476953u, 839035557u, 3203129010u, 669981176u, 4121157385u, 3519870450u, 3792633352u, 3017650322u, 1603459507u, 4225677666u, 376555451u, 473780127u, 2018786277u, 3299822439u, 1010254499u, 2383887565u, 3155009499u, 3108110655u, 2641738274u, 3684908622u, 1606463047u, 3311068174u, 52708046u, 754181455u, 1018079176u, 3915670272u, 3366999425u, 1012880204u, 1339439715u, 466437962u, 1402662350u, 2504046911u, 736323938u, 2037800124u, 1725908589u, 716341840u, 1750123474u, 3366342464u, 1743666195u, 2975303189u, 3821364027u, 3253707772u, 3635548377u, 3840413796u, 1955642085u, 1018315169u, 1258092848u, 2095540656u, 1076256607u, 117289557u, 1311658655u, 2118301000u, 68721550u, 2886814107u, 2712432819u, 4201862886u, 753807148u, 1940229047u, 731347296u, 1068901393u, 3873155894u, 2852787666u, 1973464853u, 79735652u, 3966380587u, 3245740712u, 2525773438u, 734938109u, 3045656416u, 3335746354u, 4099732691u, 1911896517u, 1697006473u, 1145487066u, 1605663299u, 3053606724u, 2386289465u, 3821211369u, 1006215345u, 1256304829u, 1053001668u, 1289194958u, 118761054u, 1853688730u, 2803418011u, 188650809u, 3763686458u, 1006829556u, 2961984133u, 3390525025u, 2061199893u, 141792681u, 2439893463u, 2652982650u, 1804942682u, 1546510005u, 1246961405u, 2407577046u, 565772575u, 3751844810u, 2943166103u, 3750052451u, 3022527280u, 25162928u, 397381043u, 1818337632u, 3447363730u, 3936437150u, 2569420703u, 2215592390u, 2171555672u, 3665571006u, 4021712412u, 2939158353u, 4057813172u, 1823237318u, 103999245u, 3251978010u, 3591914940u, 3582495283u, 2519035265u, 3905726135u, 3180393349u, 2743117123u, 55247368u, 3325286701u, 705195946u, 1857526853u, 1480518550u, 3809990433u, 1398189338u, 3126362926u, 3959531492u, 1503658285u, 1977847740u, 3043964489u, 2613086143u, 1518119282u, 4238434900u, 3905746486u, 3064949667u, 1028122931u, 3309119457u, 4071194920u, 3096098907u, 4137180520u, 494467959u, 1231408687u, 1691606157u, 1793452569u, 2722196118u, 3478603952u, 1059665738u, 2282032278u, 3990268388u, 1719514651u, 4248311578u, 3799146721u, 898026304u, 3367808954u, 4162472815u, 170495870u, 1308116609u, 3428285344u, 1714716475u, 395576794u, 4153638621u, 2999745812u, 3483315953u, 304980828u, 595337120u, 3486516729u, 2331563143u, 2583609459u, 1885928417u, 3834283777u, 979337825u, 932057378u, 3124081189u, 1930356777u, 3865887996u, 4178282217u, 4214219408u, 3669465884u, 1472413856u, 3356866587u, 1012769806u, 3043639963u, 996996396u, 207308216u, 982967331u, 2991319933u, 318066902u, 721489670u, 1249967713u, 749240921u, 591392325u, 2379365192u, 2250868849u, 2163259329u, 143191325u, 3778285606u, 982149096u, 3536906200u, 2244353244u, 1443862317u, 3161549210u, 2183127464u, 2015409516u, 547003700u, 2032484282u, 523677821u, 4275663308u, 3827205526u, 3903778273u, 2444530525u, 2543645801u, 1173958423u, 784740616u, 2878693675u, 3127696736u, 3832037316u, 3161002398u, 4084166400u, 4213346853u, 223390424u, 4273380883u, 2130315482u, 3429606032u, 3367732613u, 1912357694u, 422632590u, 1266957023u, 3437535648u, 736404240u, 2281709372u, 415859912u, 212948797u, 351612650u, 3920561440u, 112963586u, 2230727543u, 2851076612u, 1990662634u, 2264296857u, 3131463650u, 2704034623u, 3541637839u, 2954232792u, 533986918u, 4158757533u, 65174248u, 4232639593u, 865906667u, 1948225652u, 779656112u, 3873989249u, 2372984749u, 2346988193u, 1104345713u, 1165654138u, 4045762610u, 3588205178u, 461363991u, 1111215752u, 1389675192u, 2404325151u, 2152228101u, 3808973622u, 1901235912u, 3458690696u, 314513238u, 2539459143u, 2847998873u, 952026138u, 2325705328u, 407844712u, 3727960715u, 2996448351u, 2374336760u, 3138756390u, 2600015243u, 539980418u, 1876285352u, 1670330799u, 1709360377u, 2868531654u, 494777964u, 2773053597u, 599486162u, 3962209577u, 1871328846u, 2171933018u, 110279472u, 384074780u, 4147021936u, 2333589647u, 4251778066u, 40493468u, 3099342316u, 4108779767u, 2812424588u, 954542332u, 2040682331u, 2251152306u, 45915516u, 259525626u, 1045384743u, 4134656562u, 749389261u, 874399445u, 616549904u, 2200447504u, 436024539u, 78972290u, 3210485762u, 1907985531u, 3013721395u, 4214533685u, 4198804243u, 534879265u, 1517190881u, 3756787754u, 1152563554u, 1718750948u, 777737463u, 1402478860u, 1824562784u, 1879401449u, 3515818786u, 513165201u, 1423491227u, 2103067918u, 2291777410u, 1097943000u, }; // Return false only if offset is -1 and a spot check of 3 hashes all yield 0. bool Test(int offset, int len = 0) { #undef Check #undef IsAlive #define Check(x) do { \ const uint32_t actual = (x), e = expected[index++]; \ bool ok = actual == e; \ if (!ok) { \ cerr << "expected " << hex << e << " but got " << actual << endl; \ ++errors; \ } \ assert(ok); \ } while (0) #define IsAlive(x) do { alive += IsNonZero(x); } while (0) // After the following line is where the uses of "Check" and such will go. static int index = 0; if (offset == -1) { int alive = 0; IsAlive(farmhashcc::Hash32WithSeed(data, len++, SEED)); IsAlive(farmhashcc::Hash32(data, len++)); { uint128_t u = farmhashcc::Fingerprint128(data, len++); uint64_t h = Uint128Low64(u); IsAlive(h >> 32); IsAlive((h << 32) >> 32); h = Uint128High64(u); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } len -= 3; return alive > 0; } Check(farmhashcc::Hash32WithSeed(data + offset, len, SEED)); Check(farmhashcc::Hash32(data + offset, len)); { uint128_t u = farmhashcc::Fingerprint128(data + offset, len); uint64_t h = Uint128Low64(u); Check(h >> 32); Check((h << 32) >> 32); h = Uint128High64(u); Check(h >> 32); Check((h << 32) >> 32); } { uint128_t u = farmhashcc::CityHash128WithSeed(data + offset, len, Uint128(SEED0, SEED1)); uint64_t h = Uint128Low64(u); Check(h >> 32); Check((h << 32) >> 32); h = Uint128High64(u); Check(h >> 32); Check((h << 32) >> 32); } return true; #undef Check #undef IsAlive } int RunTest() { Setup(); int i = 0; cout << "Running farmhashccTest"; if (!Test(-1)) { cout << "... Unavailable\n"; return NoteErrors(); } // Good. The function is attempting to hash, so run the full test. int errors_prior_to_test = errors; for ( ; i < kTestSize - 1; i++) { Test(i * i, i); } for ( ; i < kDataSize; i += i / 7) { Test(0, i); } Test(0, kDataSize); cout << (errors == errors_prior_to_test ? "... OK\n" : "... Failed\n"); return NoteErrors(); } #else // After the following line is where the code to print hash codes will go. void Dump(int offset, int len) { cout << farmhashcc::Hash32WithSeed(data + offset, len, SEED) << "u," << endl; cout << farmhashcc::Hash32(data + offset, len) << "u," << endl; { uint128_t u = farmhashcc::Fingerprint128(data + offset, len); uint64_t h = Uint128Low64(u); cout << (h >> 32) << "u, " << ((h << 32) >> 32) << "u, "; h = Uint128High64(u); cout << (h >> 32) << "u, " << ((h << 32) >> 32) << "u," << endl; } { uint128_t u = farmhashcc::CityHash128WithSeed(data + offset, len, Uint128(SEED0, SEED1)); uint64_t h = Uint128Low64(u); cout << (h >> 32) << "u, " << ((h << 32) >> 32) << "u, "; h = Uint128High64(u); cout << (h >> 32) << "u, " << ((h << 32) >> 32) << "u," << endl; } } #endif #undef SEED #undef SEED1 #undef SEED0 } // namespace farmhashccTest #if TESTING static int farmhashccTestResult = farmhashccTest::RunTest(); #else int main(int argc, char** argv) { Setup(); cout << "uint32_t expected[] = {\n"; int i = 0; for ( ; i < kTestSize - 1; i++) { farmhashccTest::Dump(i * i, i); } for ( ; i < kDataSize; i += i / 7) { farmhashccTest::Dump(0, i); } farmhashccTest::Dump(0, kDataSize); cout << "};\n"; } #endif #ifndef FARMHASH_SELF_TEST_GUARD #define FARMHASH_SELF_TEST_GUARD #include #include #include using std::cout; using std::cerr; using std::endl; using std::hex; static const uint64_t kSeed0 = 1234567; static const uint64_t kSeed1 = k0; static const int kDataSize = 1 << 20; static const int kTestSize = 300; #define kSeed128 Uint128(kSeed0, kSeed1) static char data[kDataSize]; static int completed_self_tests = 0; static int errors = 0; // Initialize data to pseudorandom values. void Setup() { if (completed_self_tests == 0) { uint64_t a = 9; uint64_t b = 777; for (int i = 0; i < kDataSize; i++) { a += b; b += a; a = (a ^ (a >> 41)) * k0; b = (b ^ (b >> 41)) * k0 + i; uint8_t u = b >> 37; memcpy(data + i, &u, 1); // uint8_t -> char } } } int NoteErrors() { #define NUM_SELF_TESTS 9 if (++completed_self_tests == NUM_SELF_TESTS) std::exit(errors > 0); return errors; } template inline bool IsNonZero(T x) { return x != 0; } template <> inline bool IsNonZero(uint128_t x) { return x != Uint128(0, 0); } #endif // FARMHASH_SELF_TEST_GUARD namespace farmhashmkTest { uint32_t CreateSeed(int offset, int salt) { uint32_t h = static_cast(salt & 0xffffffff); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h += static_cast(offset & 0xffffffff); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); return h; } #undef SEED #undef SEED1 #undef SEED0 #define SEED CreateSeed(offset, -1) #define SEED0 CreateSeed(offset, 0) #define SEED1 CreateSeed(offset, 1) #undef TESTING #define TESTING 1 #if TESTING uint32_t expected[] = { 4223616069u, 3696677242u, 4081014168u, 2576519988u, 2212771159u, 1112731063u, 1020067935u, 3955445564u, 1451961420u, 653440099u, 31917516u, 2957164615u, 2590087362u, 3879448744u, 176305566u, 2447367541u, 1359016305u, 3363804638u, 1117290165u, 1062549743u, 2437877004u, 1894455839u, 673206794u, 3486923651u, 3269862919u, 2303349487u, 1380660650u, 595525107u, 1525325287u, 2025609358u, 176408838u, 1592885012u, 864896482u, 2101378090u, 3489229104u, 2118965695u, 581644891u, 2718789079u, 631613207u, 4228658372u, 3867875546u, 3531368319u, 3804516756u, 3317755099u, 1619744564u, 2884717286u, 1088213445u, 2667691076u, 3727873235u, 2330406762u, 3616470388u, 967660719u, 4148162586u, 315219121u, 673084328u, 3047602355u, 1598963653u, 1267826661u, 2117362589u, 2861192253u, 1823625377u, 1380350078u, 1641748342u, 1176094482u, 269384321u, 2178982315u, 3480237248u, 2660755208u, 1850544433u, 3429699438u, 1262819303u, 640556464u, 2421125401u, 2188368608u, 2612932825u, 1474432581u, 173790449u, 2124882189u, 831272654u, 622960146u, 4238751051u, 3250317967u, 2120810248u, 1948231495u, 1389029321u, 2200398357u, 2134232963u, 2948072329u, 617717625u, 681164587u, 114859387u, 430545646u, 57239089u, 3163338012u, 3482496399u, 557662576u, 1102441413u, 2670159360u, 991116729u, 846014240u, 4233741566u, 1802317242u, 3129528802u, 1459456375u, 1305643039u, 3258671612u, 1578285833u, 868590079u, 1631034517u, 1695432937u, 561078856u, 1004115553u, 3086090507u, 3818348650u, 731596645u, 780926790u, 2544205955u, 158479164u, 3983514188u, 2004735250u, 3436218400u, 673684751u, 1463431419u, 2880490219u, 3223748024u, 2218318859u, 1474466194u, 2636437533u, 2206794961u, 140995728u, 1186394086u, 1805716888u, 1640037724u, 3942729099u, 1944727013u, 918951560u, 498666871u, 3486974657u, 2967205462u, 1167253804u, 1884281041u, 2866015002u, 4158319270u, 2627220079u, 3733319624u, 3317092271u, 438323662u, 3195868065u, 3426606709u, 360708338u, 1905491012u, 650004803u, 1351266252u, 3133279000u, 3722811115u, 2722412434u, 918432408u, 3678271248u, 269599647u, 621514057u, 3117077855u, 1545425390u, 2597567410u, 1221437820u, 3493254589u, 102787342u, 918861168u, 348795089u, 3439883229u, 2353641807u, 2209585469u, 4035884492u, 2686995435u, 1649888022u, 3852893848u, 3042700028u, 314103172u, 726977769u, 2489830276u, 2872753660u, 1316214989u, 1488801501u, 1811420390u, 639581627u, 2362837215u, 3634581834u, 3648576802u, 1257314182u, 762118371u, 4268447045u, 730167096u, 755561509u, 882614845u, 3696972894u, 228263661u, 1478636142u, 2767751651u, 1532617116u, 3838657661u, 1944359935u, 1401102137u, 3772933173u, 1050098254u, 1658079354u, 1846025728u, 2204244794u, 2017217424u, 1275162853u, 1429816745u, 2175565479u, 1716109139u, 1187506761u, 2434641075u, 2725597783u, 1795687662u, 1393312782u, 3511565397u, 627885430u, 4145733164u, 2519005353u, 231414775u, 1242015635u, 2760723497u, 2185540568u, 727314436u, 2358790354u, 1186393454u, 4234795645u, 350567813u, 866773875u, 3145590392u, 1158374055u, 3903123687u, 1862119793u, 2204587556u, 4266276976u, 4151548555u, 915250402u, 2874695320u, 2360311410u, 1099212769u, 1271542714u, 3473148363u, 1637325418u, 1807795989u, 2493819794u, 3800917924u, 4001205856u, 2582153621u, 3365872040u, 2890146216u, 2626363824u, 3133351295u, 4046827296u, 3053118771u, 4113026751u, 884356716u, 3828347401u, 10608262u, 830987972u, 1841080500u, 3202717763u, 3561778749u, 1906000052u, 3058284660u, 1432904514u, 2567431677u, 2550162530u, 665557986u, 936887821u, 2101205308u, 4253535847u, 1662043545u, 1253611611u, 2091370094u, 2635077370u, 2602176041u, 3624115809u, 748442714u, 2709749154u, 1023493343u, 860291012u, 3924715584u, 1536436740u, 2551145800u, 2391782865u, 1467705048u, 2583909796u, 3616666170u, 1162857372u, 4228631071u, 1510132376u, 2739165009u, 2656606142u, 3454996358u, 3155038853u, 1022087316u, 100044110u, 494208296u, 2746186477u, 4216782431u, 225448834u, 3728320521u, 335282866u, 3148194874u, 953503703u, 1293353960u, 202372387u, 1326119870u, 4045123735u, 3819994846u, 1629004186u, 1081099186u, 3591584153u, 1670825804u, 3404257979u, 3262192301u, 2572846095u, 3714992543u, 4264142572u, 529616678u, 2882154574u, 3006354178u, 3865969421u, 2007174907u, 308283107u, 2629833703u, 3159124075u, 1146492131u, 494104332u, 493149727u, 1342910585u, 521642387u, 2201695937u, 2517980959u, 2426821287u, 777374655u, 2228189792u, 4027055486u, 228976000u, 3842083468u, 1723920223u, 1192126094u, 787744493u, 2740368380u, 2284153001u, 2773829458u, 442000614u, 387830783u, 2169780670u, 2253144627u, 3532502484u, 1969684059u, 1165351416u, 3055056536u, 3582324253u, 231419363u, 770979865u, 3213983597u, 3690452836u, 935794639u, 3230602762u, 2841762457u, 407598927u, 1164479891u, 3721799696u, 354738136u, 1801566618u, 3206038542u, 2621379981u, 1943487262u, 3534745636u, 1074424589u, 1304517521u, 4133400969u, 2339317978u, 2135116860u, 4180643791u, 2415309340u, 1855926417u, 3418648630u, 1968113037u, 597304222u, 3668824865u, 3810008716u, 3014702569u, 3151212026u, 156057449u, 373134533u, 2068234004u, 191580563u, 3832754488u, 2924104199u, 2026044494u, 4065780435u, 122565840u, 4194985167u, 2744823717u, 2494098735u, 3753793370u, 1885739217u, 2488161225u, 3643797615u, 2653367310u, 2494061477u, 189968132u, 899646597u, 392100396u, 4012318310u, 3855777086u, 3566860954u, 2698574996u, 2414249905u, 1330623339u, 1263222732u, 1277741760u, 2194959402u, 1629656136u, 120494320u, 1072368005u, 1084245077u, 4011372748u, 1366613353u, 3108643228u, 3332219532u, 2114746095u, 3964007334u, 371687128u, 1084813876u, 126459896u, 4292782331u, 321283184u, 398168499u, 3604983506u, 560701543u, 2073961354u, 4240841868u, 4151211362u, 1338986875u, 4093476832u, 2269279497u, 3500846299u, 2510225147u, 598000444u, 1330391422u, 1432533385u, 4171226231u, 426821154u, 2932270996u, 3378981077u, 2217871549u, 1619647984u, 4051608043u, 3180237819u, 12919578u, 1375401767u, 371320427u, 2986640571u, 2336669859u, 3796464715u, 1892383284u, 306814912u, 2125823211u, 1863678891u, 3249703818u, 3840225752u, 281579900u, 264680257u, 4266359110u, 4182229890u, 2239659703u, 3627947372u, 2373929191u, 224082765u, 4053639058u, 1862360303u, 3187739624u, 3392706679u, 948039509u, 817505760u, 1215842393u, 3462222651u, 536021853u, 182346832u, 2731944883u, 2346674384u, 2640961678u, 3446695687u, 2271722179u, 1301069656u, 2803881468u, 2832614405u, 1691544398u, 698756814u, 3980620906u, 3565421410u, 754769376u, 4115923404u, 3909962218u, 2747614077u, 2888289845u, 1016920862u, 2790946178u, 3067070960u, 3173251481u, 1572132982u, 255048203u, 2996538818u, 3405398987u, 136106013u, 3581605228u, 4277437977u, 2147300534u, 3728426265u, 3483629996u, 1478452694u, 20756076u, 2774992067u, 432987927u, 1516771026u, 3511588664u, 2130994978u, 509385406u, 873090347u, 2163904107u, 4192239086u, 2532489989u, 1090772651u, 3910797408u, 3710882132u, 155010959u, 1369823531u, 1599664937u, 4035593587u, 1212746925u, 795822552u, 116689518u, 3674240941u, 1135576664u, 756750261u, 1027431362u, 390555140u, 2228460216u, 1506940482u, 3733857700u, 3048762971u, 2511703196u, 548609887u, 1607354252u, 659053982u, 259884450u, 1793130460u, 4083364495u, 3148555881u, 1764350138u, 2436485683u, 4031563025u, 3261860724u, 2475833430u, 2101726086u, 3191176464u, 2646658847u, 2127042126u, 771316100u, 2115922959u, 3208515045u, 2355437783u, 3621147793u, 1580163615u, 3211555675u, 3299188490u, 191613920u, 466733956u, 2939029038u, 1509152039u, 130591314u, 1892874677u, 1646908044u, 3452406523u, 3998376606u, 1199243832u, 2187108812u, 3189230066u, 4161151481u, 3371454980u, 3681788646u, 180842187u, 3685022399u, 3058749895u, 3250165163u, 2895367943u, 2627101723u, 771755098u, 1332921024u, 3638871848u, 514215135u, 3591227378u, 2300310870u, 3689533503u, 851607114u, 114330368u, 2709027386u, 1743034877u, 1013693860u, 288169008u, 3545190686u, 1052165084u, 3995862307u, 96902755u, 1097819851u, 2645431442u, 2184148618u, 2151206566u, 350979797u, 3467920900u, 421116779u, 1246252u, 4057835428u, 329324407u, 4104482417u, 844624570u, 3306265806u, 3787625025u, 4263241191u, 3251413927u, 2921204431u, 2931915325u, 992134330u, 3986338354u, 1327895216u, 1458363596u, 1480608532u, 728594368u, 3804366693u, 794404223u, 1643240863u, 793417255u, 4167916443u, 2683488959u, 3124925324u, 4184843652u, 3750971752u, 308509829u, 1054550805u, 2797511972u, 4043123412u, 1587158240u, 4050518606u, 3030062190u, 2589912753u, 603440067u, 937013191u, 1071662315u, 2100661456u, 2602005741u, 435516078u, 2260470147u, 1256268350u, 3612035u, 3368856141u, 151516099u, 3081868591u, 3363755681u, 2049963149u, 2885320434u, 84682005u, 2411758308u, 2695174275u, 3099904644u, 1787308684u, 1132379308u, 564634346u, 510236510u, 2804443681u, 3931864252u, 2064427949u, 1893979229u, 2916544974u, 1885887717u, 2978018250u, 494192125u, 2642662373u, 901112508u, 636035003u, 1658643797u, 172746975u, 517504890u, 3440019372u, 4144498044u, 1854755456u, 3672653905u, 4176892856u, 382159097u, 282871690u, 3629300472u, 2500754041u, 1677659759u, 1067175061u, 161654075u, 1672575536u, 346120493u, 2730229631u, 203466442u, 1244549529u, 199761971u, 2744895408u, 3195315331u, 2124618519u, 3261045496u, 985339699u, 3385585455u, 1545740710u, 3636652160u, 2167020081u, 1207897204u, 28752417u, 2895834146u, 3640845375u, 3750293073u, 548997850u, 4207814196u, 4183030708u, 2462810989u, 3929965401u, }; // Return false only if offset is -1 and a spot check of 3 hashes all yield 0. bool Test(int offset, int len = 0) { #undef Check #undef IsAlive #define Check(x) do { \ const uint32_t actual = (x), e = expected[index++]; \ bool ok = actual == e; \ if (!ok) { \ cerr << "expected " << hex << e << " but got " << actual << endl; \ ++errors; \ } \ assert(ok); \ } while (0) #define IsAlive(x) do { alive += IsNonZero(x); } while (0) // After the following line is where the uses of "Check" and such will go. static int index = 0; if (offset == -1) { int alive = 0; IsAlive(farmhashmk::Hash32WithSeed(data, len++, SEED)); IsAlive(farmhashmk::Hash32(data, len++)); IsAlive(farmhashmk::Hash32(data, len++)); len -= 3; return alive > 0; } Check(farmhashmk::Hash32WithSeed(data + offset, len, SEED)); Check(farmhashmk::Hash32(data + offset, len)); return true; #undef Check #undef IsAlive } int RunTest() { Setup(); int i = 0; cout << "Running farmhashmkTest"; if (!Test(-1)) { cout << "... Unavailable\n"; return NoteErrors(); } // Good. The function is attempting to hash, so run the full test. int errors_prior_to_test = errors; for ( ; i < kTestSize - 1; i++) { Test(i * i, i); } for ( ; i < kDataSize; i += i / 7) { Test(0, i); } Test(0, kDataSize); cout << (errors == errors_prior_to_test ? "... OK\n" : "... Failed\n"); return NoteErrors(); } #else // After the following line is where the code to print hash codes will go. void Dump(int offset, int len) { cout << farmhashmk::Hash32WithSeed(data + offset, len, SEED) << "u," << endl; cout << farmhashmk::Hash32(data + offset, len) << "u," << endl; } #endif #undef SEED #undef SEED1 #undef SEED0 } // namespace farmhashmkTest #if TESTING static int farmhashmkTestResult = farmhashmkTest::RunTest(); #else int main(int argc, char** argv) { Setup(); cout << "uint32_t expected[] = {\n"; int i = 0; for ( ; i < kTestSize - 1; i++) { farmhashmkTest::Dump(i * i, i); } for ( ; i < kDataSize; i += i / 7) { farmhashmkTest::Dump(0, i); } farmhashmkTest::Dump(0, kDataSize); cout << "};\n"; } #endif #ifndef FARMHASH_SELF_TEST_GUARD #define FARMHASH_SELF_TEST_GUARD #include #include #include using std::cout; using std::cerr; using std::endl; using std::hex; static const uint64_t kSeed0 = 1234567; static const uint64_t kSeed1 = k0; static const int kDataSize = 1 << 20; static const int kTestSize = 300; #define kSeed128 Uint128(kSeed0, kSeed1) static char data[kDataSize]; static int completed_self_tests = 0; static int errors = 0; // Initialize data to pseudorandom values. void Setup() { if (completed_self_tests == 0) { uint64_t a = 9; uint64_t b = 777; for (int i = 0; i < kDataSize; i++) { a += b; b += a; a = (a ^ (a >> 41)) * k0; b = (b ^ (b >> 41)) * k0 + i; uint8_t u = b >> 37; memcpy(data + i, &u, 1); // uint8_t -> char } } } int NoteErrors() { #define NUM_SELF_TESTS 9 if (++completed_self_tests == NUM_SELF_TESTS) std::exit(errors > 0); return errors; } template inline bool IsNonZero(T x) { return x != 0; } template <> inline bool IsNonZero(uint128_t x) { return x != Uint128(0, 0); } #endif // FARMHASH_SELF_TEST_GUARD namespace farmhashnaTest { uint32_t CreateSeed(int offset, int salt) { uint32_t h = static_cast(salt & 0xffffffff); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h += static_cast(offset & 0xffffffff); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); return h; } #undef SEED #undef SEED1 #undef SEED0 #define SEED CreateSeed(offset, -1) #define SEED0 CreateSeed(offset, 0) #define SEED1 CreateSeed(offset, 1) #undef TESTING #define TESTING 1 #if TESTING uint32_t expected[] = { 1140953930u, 861465670u, 3277735313u, 2681724312u, 2598464059u, 797982799u, 890626835u, 800175912u, 2603993599u, 921001710u, 1410420968u, 2134990486u, 3283896453u, 1867689945u, 2914424215u, 2244477846u, 255297188u, 2992121793u, 1110588164u, 4186314283u, 161451183u, 3943596029u, 4019337850u, 452431531u, 283198166u, 2741341286u, 3379021470u, 2557197665u, 299850021u, 2532580744u, 452473466u, 1706958772u, 1298374911u, 3099673830u, 2199864459u, 3696623795u, 236935126u, 2976578695u, 4055299123u, 3281581178u, 1053458494u, 1882212500u, 2305012065u, 2169731866u, 3456121707u, 275903667u, 458884671u, 3033004529u, 3058973506u, 2379411653u, 1898235244u, 1402319660u, 2700149065u, 2699376854u, 147814787u, 720739346u, 2433714046u, 4222949502u, 4220361840u, 1712034059u, 3425469811u, 3690733394u, 4148372108u, 1330324210u, 594028478u, 2921867846u, 1635026870u, 192883107u, 780716741u, 1728752234u, 3280331829u, 326029180u, 3969463346u, 1436364519u, 393215742u, 3349570000u, 3824583307u, 1612122221u, 2859809759u, 3808705738u, 1379537552u, 1646032583u, 2233466664u, 1432476832u, 4023053163u, 2650381482u, 2052294713u, 3552092450u, 1628777059u, 1499109081u, 3476440786u, 3829307897u, 2960536756u, 1554038301u, 1145519619u, 3190844552u, 2902102606u, 3600725550u, 237495366u, 540224401u, 65721842u, 489963606u, 1448662590u, 397635823u, 1596489240u, 1562872448u, 1790705123u, 2128624475u, 180854224u, 2604346966u, 1435705557u, 1262831810u, 155445229u, 1672724608u, 1669465176u, 1341975128u, 663607706u, 2077310004u, 3610042449u, 1911523866u, 1043692997u, 1454396064u, 2563776023u, 294527927u, 1099072299u, 1389770549u, 703505868u, 678706990u, 2952353448u, 2026137563u, 3603803785u, 629449419u, 1933894405u, 3043213226u, 226132789u, 2489287368u, 1552847036u, 645684964u, 3828089804u, 3632594520u, 187883449u, 230403464u, 3151491850u, 3272648435u, 3729087873u, 1303930448u, 2002861219u, 165370827u, 916494250u, 1230085527u, 3103338579u, 3064290191u, 3807265751u, 3628174014u, 231181488u, 851743255u, 2295806711u, 1781190011u, 2988893883u, 1554380634u, 1142264800u, 3667013118u, 1968445277u, 315203929u, 2638023604u, 2290487377u, 732137533u, 1909203251u, 440398219u, 1891630171u, 1380301172u, 1498556724u, 4072067757u, 4165088768u, 4204318635u, 441430649u, 3931792696u, 197618179u, 956300927u, 914413116u, 3010839769u, 2837339569u, 2148126371u, 1913303225u, 3074915312u, 3117299654u, 4139181436u, 2993479124u, 3178848746u, 1357272220u, 1438494951u, 507436733u, 667183474u, 2084369203u, 3854939912u, 1413396341u, 126024219u, 146044391u, 1016656857u, 3022024459u, 3254014218u, 429095991u, 165589978u, 1578546616u, 985653208u, 1718653828u, 623071693u, 366414107u, 249776086u, 1207522198u, 3047342438u, 2991127487u, 3120876698u, 1684583131u, 46987739u, 1157614300u, 863214540u, 1087193030u, 199124911u, 520792961u, 3614377032u, 586863115u, 3331828431u, 1013201099u, 1716848157u, 4033596884u, 1164298657u, 4140791139u, 1146169032u, 1434258493u, 3824360466u, 3242407770u, 3725511003u, 232064808u, 872586426u, 762243036u, 2736953692u, 816692935u, 512845449u, 3748861010u, 2266795890u, 3781899767u, 4290630595u, 517646945u, 22638523u, 648000590u, 959214578u, 558910384u, 1283799121u, 3047062993u, 1024246061u, 4027776454u, 3544509313u, 622325861u, 834785312u, 382936554u, 411505255u, 1973395102u, 1825135056u, 2725923798u, 580988377u, 2826990641u, 3474970689u, 1029055034u, 812546227u, 2506885666u, 2584372201u, 1758123094u, 589567754u, 325737734u, 345313518u, 2022370576u, 3886113119u, 3338548567u, 257578986u, 3698087965u, 1776047957u, 1771384107u, 3604937815u, 3198590202u, 2305332220u, 191910725u, 4232136669u, 427759438u, 4244322689u, 542201663u, 3315355162u, 2135941665u, 556609672u, 45845311u, 1175961330u, 3948351189u, 23075771u, 3252374102u, 1634635545u, 4151937410u, 713127376u, 1467786451u, 663013031u, 3444053918u, 2638154051u, 810082938u, 3077742128u, 1062268187u, 2115441882u, 4081398201u, 3735739145u, 2794294783u, 2335576331u, 2560479831u, 1379288194u, 4225182569u, 2442302747u, 3948961926u, 3958366652u, 3067277639u, 3667516477u, 1709989541u, 1516711748u, 2339636583u, 4188504038u, 59581167u, 2725013602u, 3639843023u, 2658147000u, 2643979752u, 3758739543u, 4189944477u, 2470483982u, 877580602u, 2995362413u, 118817200u, 3252925478u, 2062343506u, 3981838403u, 3762572073u, 1231633714u, 4168280671u, 2931588131u, 3284356565u, 1129162571u, 732225574u, 4173605289u, 1407328702u, 1677744031u, 3532596884u, 3232041815u, 1652884780u, 2256541290u, 3459463480u, 3740979556u, 259034107u, 2227121257u, 1426140634u, 3606709555u, 3424793077u, 315836068u, 3200749877u, 1386256573u, 24035717u, 2982018998u, 1811050648u, 234531934u, 1115203611u, 1598686658u, 3146815575u, 1603559457u, 323296368u, 2632963283u, 1778459926u, 739944537u, 579625482u, 3486330348u, 492621815u, 1231665285u, 2457048126u, 3903349120u, 389846205u, 3355404249u, 3275550588u, 1052645068u, 862072556u, 2834153464u, 1481069623u, 2657392572u, 4279236653u, 1688445808u, 701920051u, 3740748788u, 3388062747u, 1873358321u, 2152785640u, 883382081u, 1005815394u, 1020177209u, 734239551u, 2371453141u, 100326520u, 3488500412u, 1279682138u, 2610427744u, 49703572u, 3026361211u, 605900428u, 302392721u, 2509302188u, 1416453607u, 2815915291u, 1862819968u, 519710058u, 2450888314u, 4017598378u, 937074653u, 3035635454u, 1590230729u, 3268013438u, 2710029305u, 12886044u, 3711259084u, 2627383582u, 3895772404u, 648534979u, 260307902u, 855990313u, 3669691805u, 263366740u, 2938543471u, 414331688u, 3080542944u, 3405007814u, 3565059103u, 1190977418u, 390836981u, 1606450012u, 2649808239u, 2514169310u, 2747519432u, 4129538640u, 1721522849u, 492099164u, 792990594u, 3625507637u, 2271095827u, 2993032712u, 2302363854u, 4013112951u, 1111617969u, 2183845740u, 795918276u, 1116991810u, 3110898804u, 3963062126u, 2737064702u, 462795667u, 937372240u, 1343017609u, 1091041189u, 2790555455u, 277024217u, 25485284u, 1166522068u, 1623631848u, 241727183u, 2836158787u, 3112996740u, 573836428u, 2721658101u, 1937681565u, 4175169209u, 3190765433u, 1970000788u, 1668258120u, 114616703u, 954762543u, 199237753u, 4094644498u, 2522281978u, 732086117u, 1756889687u, 2936126607u, 2437031370u, 4103143808u, 3883389541u, 3171090854u, 2483004780u, 1927385370u, 2360538162u, 2740855009u, 4241185118u, 1492209542u, 1672737098u, 2148675559u, 1789864670u, 2434313103u, 2319172611u, 2760941207u, 2636210123u, 1338083267u, 1128080590u, 822806371u, 1199583556u, 314727461u, 1335160250u, 2084630531u, 1156261526u, 316766066u, 112090465u, 3129033323u, 2746885618u, 636616055u, 2582210744u, 1721064910u, 3468394263u, 470463518u, 2076016059u, 408721884u, 2121041886u, 378460278u, 1915948002u, 357324860u, 2301682622u, 2691859523u, 1869756364u, 2429314418u, 2193146527u, 1185564327u, 2614088922u, 1975527044u, 919067651u, 2855948894u, 3662539576u, 1943802836u, 3529473373u, 1490330107u, 366036094u, 3384241033u, 4276268604u, 448403661u, 4271796078u, 1910401882u, 3077107698u, 299427366u, 2035665349u, 3201262636u, 3738454258u, 2554452696u, 3588997135u, 3363895827u, 1267505995u, 1852004679u, 2237827073u, 2803250686u, 3468044908u, 2143572850u, 1728158656u, 1022551180u, 1996680960u, 839529273u, 2400647871u, 2201096054u, 3606433628u, 2597259793u, 3544595875u, 3909443124u, 819278607u, 3447346709u, 806136613u, 2711436388u, 3656063205u, 837475154u, 694525336u, 4070212073u, 4011303412u, 1068395209u, 438095290u, 484603494u, 2673730227u, 737767009u, 642310823u, 3914002299u, 308425103u, 268427550u, 1334387085u, 4069797497u, 4280783219u, 2914011058u, 4243643405u, 2849988118u, 2504230175u, 1817156623u, 2804200483u, 3406991497u, 2948254999u, 2102063419u, 1071272117u, 514889942u, 571972433u, 1246595599u, 1735616066u, 1539151988u, 1230831543u, 277987182u, 4269526481u, 991511607u, 95237878u, 2005032160u, 1291113144u, 626619670u, 3560835907u, 164940926u, 1433635018u, 116647396u, 3039097112u, 2868163232u, 1141645918u, 1764165478u, 881378302u, 2159170082u, 2953647681u, 1011320066u, 184856151u, 1723308975u, 336034862u, 2017579106u, 1476681709u, 147523618u, 3896252223u, 2264728166u, 944743644u, 1694443528u, 2690700128u, 1947321519u, 735478508u, 4058183171u, 260177668u, 505662155u, 2391691262u, 1920739747u, 3216960415u, 1898176786u, 3722741628u, 1511077569u, 449636564u, 983350414u, 2580237367u, 2055059789u, 1103819072u, 2089123665u, 3873755579u, 2718467458u, 3124338704u, 3204250304u, 2475035432u, 1120017626u, 3873758287u, 1982999824u, 2950794582u, 780634378u, 2842141483u, 4029205195u, 1656892865u, 3330993377u, 80890710u, 1953796601u, 3873078673u, 136118734u, 2317676604u, 4199091610u, 1864448181u, 3063437608u, 1699452298u, 1403506686u, 1513069466u, 2348491299u, 4273657745u, 4055855649u, 1805475756u, 2562064338u, 973124563u, 4197091358u, 172861513u, 2858726767u, 4271866024u, 3071338162u, 3590386266u, 2328277259u, 1096050703u, 1189614342u, 459509140u, 771592405u, 817999971u, 3740825152u, 520400189u, 1941874618u, 185232757u, 4032960199u, 3928245258u, 3527721294u, 1301118856u, 752188080u, 3512945009u, 308584855u, 2105373972u, 752872278u, 3823368815u, 3760952096u, 4250142168u, 2565680167u, 3646354146u, 1259957455u, 1085857127u, 3471066607u, 38924274u, 3770488806u, 1083869477u, 3312508103u, 71956383u, 3738784936u, 3099963860u, 1255084262u, 4286969992u, 3621849251u, 1190908967u, 1831557743u, 2363435042u, 54945052u, 4059585566u, 4023974274u, 1788578453u, 3442180039u, 2534883189u, 2432427547u, 3909757989u, 731996369u, 4168347425u, 1356028512u, 2741583197u, 1280920000u, 312887059u, 3259015297u, 3946278527u, 4135481831u, 1281043691u, 1121403845u, 3312292477u, 1819941269u, 1741932545u, 3293015483u, 2127558730u, 713121337u, 2635469238u, 486003418u, 4015067527u, 2976737859u, 2108187161u, 927011680u, 1970188338u, 4177613234u, 1799789551u, 2118505126u, 4134691985u, 1958963937u, 1929210029u, 2555835851u, 2768832862u, 910892050u, 2567532373u, 4075249328u, 86689814u, 3726640307u, 1392137718u, 1240000030u, 4104757832u, 3026358429u, 313797689u, 1435798509u, 3101500919u, 1241665335u, 3573008472u, 3615577014u, 3767659003u, 3134294021u, 4063565523u, 2296824134u, 1541946015u, 3087190425u, 2693152531u, 2199672572u, 2123763822u, 1034244398u, 857839960u, 2515339233u, 2228007483u, 1628096047u, 2116502287u, 2502657424u, 2809830736u, 460237542u, 450205998u, 3646921704u, 3818199357u, 1808504491u, 1950698961u, 2069753399u, 3657033172u, 3734547671u, 4067859590u, 3292597295u, 1106466069u, 356742959u, 2469567432u, 3495418823u, 183440071u, 3248055817u, 3662626864u, 1750561299u, 3926138664u, 4088592524u, 567122118u, 3810297651u, 992181339u, 3384018814u, 3272124369u, 3177596743u, 320086295u, 2316548367u, 100741310u, 451656820u, 4086604273u, 3759628395u, 2553391092u, 1745659881u, 3650357479u, 2390172694u, 330172533u, 767377322u, 526742034u, 4102497288u, 2088767754u, 164402616u, 2482632320u, 2352347393u, 1873658044u, 3861555476u, 2751052984u, 1767810825u, 20037241u, 545143220u, 2594532522u, 472304191u, 3441135892u, 3323383489u, 258785117u, 2977745165u, 2781737565u, 2963590112u, 2756998822u, 207428029u, 2581558559u, 3824717027u, 1258619503u, 3472047571u, 2648427775u, 2360400900u, 2393763818u, 2332399088u, 3932701729u, 884421165u, 1396468647u, 1377764574u, 4061795938u, 1559119087u, 3343596838u, 3604258095u, 1435134775u, 1099809675u, 908163739u, 1418405656u, 368446627u, 3741651161u, 3374512975u, 3542220540u, 3244772570u, 200009340u, 3198975081u, 2521038253u, 4081637863u, 337070226u, 3235259030u, 3897262827u, 736956644u, 641040550u, 644850146u, 1306761320u, 4219448634u, 193750500u, 3293278106u, 1383997679u, 1242645122u, 4109252858u, 450747727u, 3716617561u, 362725793u, 2252520167u, 3377483696u, 1788337208u, 8130777u, 3226734120u, 759239140u, 1012411364u, 1658628529u, 2911512007u, 1002580201u, 1681898320u, 3039016929u, 4294520281u, 367022558u, 3071359622u, 3205848570u, 152989999u, 3839042136u, 2357687350u, 4273132307u, 3898950547u, 1176841812u, 1314157485u, 75443951u, 1027027239u, 1858986613u, 2040551642u, 36574105u, 2603059541u, 3456147251u, 2137668425u, 4077477194u, 3565689036u, 491832241u, 363703593u, 2579177168u, 3589545214u, 265993036u, 1864569342u, 4149035573u, 3189253455u, 1072259310u, 3153745937u, 923017956u, 490608221u, 855846773u, 845706553u, 1018226240u, 1604548872u, 3833372385u, 3287246572u, 2757959551u, 2452872151u, 1553870564u, 1713154780u, 2649450292u, 500120236u, 84251717u, 661869670u, 1444911517u, 2489716881u, 2810524030u, 1561519055u, 3884088359u, 2509890699u, 4247155916u, 1005636939u, 3224066062u, 2774151984u, 2035978240u, 2514910366u, 1478837908u, 3144450144u, 2107011431u, 96459446u, 3587732908u, 2389230590u, 3287635953u, 250533792u, 1235983679u, 4237425634u, 3704645833u, 3882376657u, 2976369049u, 1187061987u, 276949224u, 4100839753u, 1698347543u, 1629662314u, 1556151829u, 3784939568u, 427484362u, 4246879223u, 3155311770u, 4285163791u, 1693376813u, 124492786u, 1858777639u, 3476334357u, 1941442701u, 1121980173u, 3485932087u, 820852908u, 358032121u, 2511026735u, 1873607283u, 2556067450u, 2248275536u, 1528632094u, 1535473864u, 556796152u, 1499201704u, 1472623890u, 1526518503u, 3692729434u, 1476438092u, 2913077464u, 335109599u, 2167614601u, 4121131078u, 3158127917u, 3051522276u, 4046477658u, 2857717851u, 1863977403u, 1341023343u, 692059110u, 1802040304u, 990407433u, 3285847572u, 319814144u, 561105582u, 1540183799u, 4052924496u, 2926590471u, 2244539806u, 439121871u, 3317903224u, 3178387550u, 4265214507u, 82077489u, 1978918971u, 4279668976u, 128732476u, 2853224222u, 464407878u, 4190838199u, 997819001u, 3250520802u, 2330081301u, 4095846095u, 733509243u, 1583801700u, 722314527u, 3552883023u, 1403784280u, 432327540u, 1877837196u, 3912423882u, 505219998u, 696031431u, 908238873u, 4189387259u, 8759461u, 2540185277u, 3385159748u, 381355877u, 2519951681u, 1679786240u, 2019419351u, 4051584612u, 1933923923u, 3768201861u, 1670133081u, 3454981037u, 700836153u, 1675560450u, 371560700u, 338262316u, 847351840u, 2222395828u, 3130433948u, 405251683u, 3037574880u, 184098830u, 453340528u, 1385561439u, 2224044848u, 4071581802u, 1431235296u, 5570097u, 570114376u, 2287305551u, 2272418128u, 803575837u, 3943113491u, 414959787u, 708083137u, 2452657767u, 4019147902u, 3841480082u, 3791794715u, 2965956183u, 2763690963u, 2350937598u, 3424361375u, 779434428u, 1274947212u, 686105485u, 3426668051u, 3692865672u, 3057021940u, 2285701422u, 349809124u, 1379278508u, 3623750518u, 215970497u, 1783152480u, 823305654u, 216118434u, 1787189830u, 3692048450u, 2272612521u, 3032187389u, 4159715581u, 1388133148u, 1611772864u, 2544383526u, 552925303u, 3420960112u, 3198900547u, 3503230228u, 2603352423u, 2318375898u, 4064071435u, 3006227299u, 4194096960u, 1283392422u, 1510460996u, 174272138u, 3671038966u, 1775955687u, 1719108984u, 1763892006u, 1385029063u, 4083790740u, 406757708u, 684087286u, 531310503u, 3329923157u, 3492083607u, 1059031410u, 3037314475u, 3105682208u, 3382290593u, 2292208503u, 426380557u, 97373678u, 3842309471u, 777173623u, 3241407531u, 303065016u, 1477104583u, 4234905200u, 2512514774u, 2649684057u, 1397502982u, 1802596032u, 3973022223u, 2543566442u, 3139578968u, 3193669211u, 811750340u, 4013496209u, 567361887u, 4169410406u, 3622282782u, 3403136990u, 2540585554u, 895210040u, 3862229802u, 1145435213u, 4146963980u, 784952939u, 943914610u, 573034522u, 464420660u, 2356867109u, 3054347639u, 3985088434u, 1911188923u, 583391304u, 176468511u, 2990150068u, 2338031599u, 519948041u, 3181425568u, 496106033u, 4110294665u, 2736756930u, 1196757691u, 1089679033u, 240953857u, 3399092928u, 4040779538u, 2843673626u, 240495962u, 3017658263u, 3828377737u, 4243717901u, 2448373688u, 2759616657u, 2246245780u, 308018483u, 4262383425u, 2731780771u, 328023017u, 2884443148u, 841480070u, 3188015819u, 4051263539u, 2298178908u, 2944209234u, 1372958390u, 4164532914u, 4074952232u, 1683612329u, 2155036654u, 1872815858u, 2041174279u, 2368092311u, 206775997u, 2283918569u, 645945606u, 115406202u, 4206471368u, 3923500892u, 2217060665u, 350160869u, 706531239u, 2824302286u, 509981657u, 1469342315u, 140980u, 1891558063u, 164887091u, 3094962711u, 3437115622u, 13327420u, 422986366u, 330624974u, 3630863408u, 2425505046u, 824008515u, 3543885677u, 918718096u, 376390582u, 3224043675u, 3724791476u, 1837192976u, 2968738516u, 3424344721u, 3187805406u, 1550978788u, 1743089918u, 4251270061u, 645016762u, 3855037968u, 1928519266u, 1373803416u, 2289007286u, 1889218686u, 1610271373u, 3059200728u, 2108753646u, 582042641u, 812347242u, 3188172418u, 191994904u, 1343511943u, 2247006571u, 463291708u, 2697254095u, 1534175504u, 1106275740u, 622521957u, 917121602u, 4095777215u, 3955972648u, 3852234638u, 2845309942u, 3299763344u, 2864033668u, 2554947496u, 799569078u, 2551629074u, 1102873346u, 2661022773u, 2006922227u, 2900438444u, 1448194126u, 1321567432u, 1983773590u, 1237256330u, 3449066284u, 1691553115u, 3274671549u, 4271625619u, 2741371614u, 3285899651u, 786322314u, 1586632825u, 564385522u, 2530557509u, 2974240289u, 1244759631u, 3263135197u, 3592389776u, 3570296884u, 2749873561u, 521432811u, 987586766u, 3206261120u, 1327840078u, 4078716491u, 1753812954u, 976892272u, 1827135136u, 1781944746u, 1328622957u, 1015377974u, 3439601008u, 2209584557u, 2482286699u, 1109175923u, 874877499u, 2036083451u, 483570344u, 1091877599u, 4190721328u, 1129462471u, 640035849u, 1867372700u, 920761165u, 3273688770u, 1623777358u, 3389003793u, 3241132743u, 2734783008u, 696674661u, 2502161880u, 1646071378u, 1164309901u, 350411888u, 1978005963u, 2253937037u, 7371540u, 989577914u, 3626554867u, 3214796883u, 531343826u, 398899695u, 1145247203u, 1516846461u, 3656006011u, 529303412u, 3318455811u, 3062828129u, 1696355359u, 3698796465u, 3155218919u, 1457595996u, 3191404246u, 1395609912u, 2917345728u, 1237411891u, 1854985978u, 1091884675u, 3504488111u, 3109924189u, 1628881950u, 3939149151u, 878608872u, 778235395u, 1052990614u, 903730231u, 2069566979u, 2437686324u, 3163786257u, 2257884264u, 2123173186u, 939764916u, 2933010098u, 1235300371u, 1256485167u, 1950274665u, 2180372319u, 2648400302u, 122035049u, 1883344352u, 2083771672u, 3712110541u, 321199441u, 1896357377u, 508560958u, 3066325351u, 2770847216u, 3177982504u, 296902736u, 1486926688u, 456842861u, 601221482u, 3992583643u, 2794121515u, 1533934172u, 1706465470u, 4281971893u, 2557027816u, 900741486u, 227175484u, 550595824u, 690918144u, 2825943628u, 90375300u, 300318232u, 1985329734u, 1440763373u, 3670603707u, 2533900859u, 3253901179u, 542270815u, 3677388841u, 307706478u, 2570910669u, 3320103693u, 1273768482u, 1216399252u, 1652924805u, 1043647584u, 1120323676u, 639941430u, 325675502u, 3652676161u, 4241680335u, 1545838362u, 1991398008u, 4100211814u, 1097584090u, 3262252593u, 2254324292u, 1765019121u, 4060211241u, 2315856188u, 3704419305u, 411263051u, 238929055u, 3540688404u, 3094544537u, 3250435765u, 3460621305u, 1967599860u, 2016157366u, 847389916u, 1659615591u, 4020453639u, 901109753u, 2682611693u, 1661364280u, 177155177u, 3210561911u, 3802058181u, 797089608u, 3286110054u, 2110358240u, 1353279028u, 2479975820u, 471725410u, 2219863904u, 3623364733u, 3167128228u, 1052188336u, 3656587111u, 721788662u, 3061255808u, 1615375832u, 924941453u, 2547780700u, 3328169224u, 1310964134u, 2701956286u, 4145497671u, 1421461094u, 1221397398u, 1589183618u, 1492533854u, 449740816u, 2686506989u, 3035198924u, 1682886232u, 2529760244u, 3342031659u, 1235084019u, 2151665147u, 2315686577u, 3282027660u, 1140138691u, 2754346599u, 2091754612u, 1178454681u, 4226896579u, 2942520471u, 2122168506u, 3751680858u, 3213794286u, 2601416506u, 4142747914u, 3951404257u, 4243249649u, 748595836u, 4004834921u, 238887261u, 1927321047u, 2217148444u, 205977665u, 1885975275u, 186020771u, 2367569534u, 2941662631u, 2608559272u, 3342096731u, 741809437u, 1962659444u, 3539886328u, 3036596491u, 2282550094u, 2366462727u, 2748286642u, 2144472852u, 1390394371u, 1257385924u, 2205425874u, 2119055686u, 46865323u, 3597555910u, 3188438773u, 2372320753u, 3641116924u, 3116286108u, 2680722658u, 3371014971u, 2058751609u, 2966943726u, 2345078707u, 2330535244u, 4013841927u, 1169588594u, 857915866u, 1875260989u, 3175831309u, 3193475664u, 1955181430u, 923161569u, 4068653043u, 776445899u, 954196929u, 61509556u, 4248237857u, 3808667664u, 581227317u, 2893240187u, 4159497403u, 4212264930u, 3973886195u, 2077539039u, 851579036u, 2957587591u, 772351886u, 1173659554u, 946748363u, 2794103714u, 2094375930u, 4234750213u, 3671645488u, 2614250782u, 2620465358u, 3122317317u, 2365436865u, 3393973390u, 523513960u, 3645735309u, 2766686992u, 2023960931u, 2312244996u, 1875932218u, 3253711056u, 3622416881u, 3274929205u, 612094988u, 1555465129u, 2114270406u, 3553762793u, 1832633644u, 1087551556u, 3306195841u, 1702313921u, 3675066046u, 1735998785u, 1690923980u, 1482649756u, 1171351291u, 2043136409u, 1962596992u, 461214626u, 3278253346u, 1392428048u, 3744621107u, 1028502697u, 3991171462u, 1014064003u, 3642345425u, 3186995039u, 6114625u, 3359104346u, 414856965u, 2814387514u, 3583605071u, 2497896367u, 1024572712u, 1927582962u, 2892797583u, 845302635u, 328548052u, 1523379748u, 3392622118u, 1347167673u, 1012316581u, 37767602u, 2647726017u, 1070326065u, 2075035198u, 4202817168u, 2502924707u, 2612406822u, 2187115553u, 1180137213u, 701024148u, 1481965992u, 3223787553u, 2083541843u, 203230202u, 3876887380u, 1334816273u, 2870251538u, 2186205850u, 3985213979u, 333533378u, 806507642u, 1010064531u, 713520765u, 3084131515u, 2637421459u, 1703168933u, 1517562266u, 4089081247u, 3231042924u, 3079916123u, 3154574447u, 2253948262u, 1725190035u, 2452539325u, 1343734533u, 213706059u, 2519409656u, 108055211u, 2916327746u, 587001593u, 1917607088u, 4202913084u, 926304016u, 469255411u, 4042080256u, 3498936874u, 246692543u, 495780578u, 438717281u, 2259272650u, 4011324645u, 2836854664u, 2317249321u, 946828752u, 1280403658u, 1905648354u, 2034241661u, 774652981u, 1285694082u, 2200307766u, 2158671727u, 1135162148u, 232040752u, 397012087u, 1717527689u, 1720414106u, 918797022u, 2580119304u, 3568069742u, 2904461070u, 3893453420u, 973817938u, 667499332u, 3785870412u, 2088861715u, 1565179401u, 600903026u, 591806775u, 3512242245u, 997964515u, 2339605347u, 1134342772u, 3234226304u, 4084179455u, 302315791u, 2445626811u, 2590372496u, 345572299u, 2274770442u, 3600587867u, 3706939009u, 1430507980u, 2656330434u, 1079209397u, 2122849632u, 1423705223u, 3826321888u, 3683385276u, 1057038163u, 1242840526u, 3987000643u, 2398253089u, 1538190921u, 1295898647u, 3570196893u, 3065138774u, 3111336863u, 2524949549u, 4203895425u, 3025864372u, 968800353u, 1023721001u, 3763083325u, 526350786u, 635552097u, 2308118370u, 2166472723u, 2196937373u, 2643841788u, 3040011470u, 4010301879u, 2782379560u, 3474682856u, 4201389782u, 4223278891u, 1457302296u, 2251842132u, 1090062008u, 3188219189u, 292733931u, 1424229089u, 1590782640u, 1365212370u, 3975957073u, 3982969588u, 2927147928u, 1048291071u, 2766680094u, 884908196u, 35237839u, 2221180633u, 2490333812u, 4098360768u, 4029081103u, 3490831871u, 2392516272u, 3455379186u, 3948800722u, 335456628u, 2105117968u, 4181629008u, 1044201772u, 3335754111u, 540133451u, 3313113759u, 3786107905u, 2627207327u, 3540337875u, 3473113388u, 3430536378u, 2514123129u, 2124531276u, 3872633376u, 3272957388u, 3501994650u, 2418881542u, 487365389u, 3877672368u, 1512866656u, 3486531087u, 2102955203u, 1136054817u, 3004241477u, 1549075351u, 1302002008u, 3936430045u, 2258587644u, 4109233936u, 3679809321u, 3467083076u, 2484463221u, 1594979755u, 529218470u, 3527024461u, 1147434678u, 106799023u, 1823161970u, 1704656738u, 1675883700u, 3308746763u, 1875093248u, 1352868568u, 1898561846u, 2508994984u, 3177750780u, 4217929592u, 400784472u, 80090315u, 3564414786u, 3841585648u, 3379293868u, 160353261u, 2413172925u, 2378499279u, 673436726u, 1505702418u, 1330977363u, 1853298225u, 3201741245u, 2135714208u, 4069554166u, 3715612384u, 3692488887u, 3680311316u, 4274382900u, 914186796u, 2264886523u, 3869634032u, 1254199592u, 1131020455u, 194781179u, 429923922u, 2763792336u, 2052895198u, 3997373194u, 3440090658u, 2165746386u, 1575500242u, 3463310191u, 2064974716u, 3779513671u, 3106421434u, 880320527u, 3281914119u, 286569042u, 3909096631u, 122359727u, 1429837716u, 252230074u, 4111461225u, 762273136u, 93658514u, 2766407143u, 3623657004u, 3869801679u, 3925695921u, 2390397316u, 2499025338u, 2741806539u, 2507199021u, 1659221866u, 361292116u, 4048761557u, 3797133396u, 1517903247u, 3121647246u, 3884308578u, 1697201500u, 1558800262u, 4150812360u, 3161302278u, 2610217849u, 641564641u, 183814518u, 2075245419u, 611996508u, 2223461433u, 329123979u, 121860586u, 860985829u, 1137889144u, 4018949439u, 2904348960u, 947795261u, 1992594155u, 4255427501u, 2281583851u, 2892637604u, 1478186924u, 3050771207u, 2767035539u, 373510582u, 1963520320u, 3763848370u, 3756817798u, 627269409u, 1806905031u, 1814444610u, 3646665053u, 1822693920u, 278515794u, 584050483u, 4142579188u, 2149745808u, 3193071606u, 1179706341u, 2693495182u, 3259749808u, 644172091u, 880509048u, 3340630542u, 3365160815u, 2384445068u, 3053081915u, 2840648309u, 1986990122u, 1084703471u, 2370410550u, 1627743573u, 2244943480u, 4057483496u, 2611595995u, 2470013639u, 4024732359u, 3987190386u, 873421687u, 2447660175u, 3226583022u, 767655877u, 2528024413u, 1962070688u, 1233635843u, 2163464207u, 659054446u, 854207134u, 258410943u, 4197831420u, 2515400215u, 3100476924u, 1961549594u, 2219491151u, 3997658851u, 163850514u, 470325051u, 2598261204u, 3052145580u, 59836528u, 1376188597u, 966733415u, 850667549u, 3622479237u, 1083731990u, 1525777459u, 4005126532u, 1428155540u, 2781907007u, 943739431u, 1493961005u, 2839096988u, 2000057832u, 1941829603u, 1901484772u, 939810041u, 3377407371u, 3090115837u, 3310840540u, 2068409688u, 3261383939u, 2212130277u, 2594774045u, 2912652418u, 4179816101u, 3534504531u, 3349254805u, 2796552902u, 1385421283u, 4259908631u, 3714780837u, 3070073945u, 3372846298u, 3835884044u, 3047965714u, 3009018735u, 744091167u, 1861124263u, 2764936304u, 1338171648u, 4222019554u, 1395200692u, 1371426007u, 3338031581u, 2525665319u, 4196233786u, 2332743921u, 1474702008u, 2274266301u, 4255175517u, 2290169528u, 1793910997u, 2188254024u, 354202001u, 3864458796u, 4280290498u, 1554419340u, 1733094688u, 2010552302u, 1561807039u, 664313606u, 2548990879u, 1084699349u, 3233936866u, 973895284u, 2386881969u, 1831995860u, 2961465052u, 1428704144u, 3269904970u, 231648253u, 2602483763u, 4125013173u, 3319187387u, 3347011944u, 1892898231u, 4019114049u, 868879116u, 4085937045u, 2378411019u, 1072588531u, 3547435717u, 2208070766u, 1069899078u, 3142980597u, 2337088907u, 1593338562u, 919414554u, 688077849u, 3625708135u, 1472447348u, 1947711896u, 3953006207u, 877438080u, 845995820u, 3150361443u, 3053496713u, 2484577841u, 224271045u, 2914958001u, 2682612949u, 806655563u, 2436224507u, 1907729235u, 2920583824u, 1251814062u, 2070814520u, 4034325578u, 497847539u, 2714317144u, 385182008u, 640855184u, 1327075087u, 1062468773u, 1757405994u, 1374270191u, 4263183176u, 3041193150u, 1037871524u, 3633173991u, 4231821821u, 2830131945u, 3505072908u, 2830570613u, 4195208715u, 575398021u, 3992840257u, 3691788221u, 1949847968u, 2999344380u, 3183782163u, 3723754342u, 759716128u, 3284107364u, 1714496583u, 15918244u, 820509475u, 2553936299u, 2201876606u, 4237151697u, 2605688266u, 3253705097u, 1008333207u, 712158730u, 1722280252u, 1933868287u, 4152736859u, 2097020806u, 584426382u, 2836501956u, 2522777566u, 1996172430u, 2122199776u, 1069285218u, 1474209360u, 690831894u, 107482532u, 3695525410u, 670591796u, 768977505u, 2412057331u, 3647886687u, 3110327607u, 1072658422u, 379861934u, 1557579480u, 4124127129u, 2271365865u, 3880613089u, 739218494u, 547346027u, 388559045u, 3147335977u, 176230425u, 3094853730u, 2554321205u, 1495176194u, 4093461535u, 3521297827u, 4108148413u, 1913727929u, 1177947623u, 1911655402u, 1053371241u, 3265708874u, 1266515850u, 1045540427u, 3194420196u, 3717104621u, 1144474110u, 1464392345u, 52070157u, 4144237690u, 3350490823u, 4166253320u, 2747410691u, }; // Return false only if offset is -1 and a spot check of 3 hashes all yield 0. bool Test(int offset, int len = 0) { #undef Check #undef IsAlive #define Check(x) do { \ const uint32_t actual = (x), e = expected[index++]; \ bool ok = actual == e; \ if (!ok) { \ cerr << "expected " << hex << e << " but got " << actual << endl; \ ++errors; \ } \ assert(ok); \ } while (0) #define IsAlive(x) do { alive += IsNonZero(x); } while (0) // After the following line is where the uses of "Check" and such will go. static int index = 0; if (offset == -1) { int alive = 0; { uint64_t h = farmhashna::Hash64WithSeeds(data, len++, SEED0, SEED1); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } { uint64_t h = farmhashna::Hash64WithSeed(data, len++, SEED); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } { uint64_t h = farmhashna::Hash64(data, len++); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } len -= 3; return alive > 0; } { uint64_t h = farmhashna::Hash64WithSeeds(data + offset, len, SEED0, SEED1); Check(h >> 32); Check((h << 32) >> 32); } { uint64_t h = farmhashna::Hash64WithSeed(data + offset, len, SEED); Check(h >> 32); Check((h << 32) >> 32); } { uint64_t h = farmhashna::Hash64(data + offset, len); Check(h >> 32); Check((h << 32) >> 32); } return true; #undef Check #undef IsAlive } int RunTest() { Setup(); int i = 0; cout << "Running farmhashnaTest"; if (!Test(-1)) { cout << "... Unavailable\n"; return NoteErrors(); } // Good. The function is attempting to hash, so run the full test. int errors_prior_to_test = errors; for ( ; i < kTestSize - 1; i++) { Test(i * i, i); } for ( ; i < kDataSize; i += i / 7) { Test(0, i); } Test(0, kDataSize); cout << (errors == errors_prior_to_test ? "... OK\n" : "... Failed\n"); return NoteErrors(); } #else // After the following line is where the code to print hash codes will go. void Dump(int offset, int len) { { uint64_t h = farmhashna::Hash64WithSeeds(data + offset, len, SEED0, SEED1); cout << (h >> 32) << "u, " << ((h << 32) >> 32) << "u," << endl; } { uint64_t h = farmhashna::Hash64WithSeed(data + offset, len, SEED); cout << (h >> 32) << "u, " << ((h << 32) >> 32) << "u," << endl; } { uint64_t h = farmhashna::Hash64(data + offset, len); cout << (h >> 32) << "u, " << ((h << 32) >> 32) << "u," << endl; } } #endif #undef SEED #undef SEED1 #undef SEED0 } // namespace farmhashnaTest #if TESTING static int farmhashnaTestResult = farmhashnaTest::RunTest(); #else int main(int argc, char** argv) { Setup(); cout << "uint32_t expected[] = {\n"; int i = 0; for ( ; i < kTestSize - 1; i++) { farmhashnaTest::Dump(i * i, i); } for ( ; i < kDataSize; i += i / 7) { farmhashnaTest::Dump(0, i); } farmhashnaTest::Dump(0, kDataSize); cout << "};\n"; } #endif #ifndef FARMHASH_SELF_TEST_GUARD #define FARMHASH_SELF_TEST_GUARD #include #include #include using std::cout; using std::cerr; using std::endl; using std::hex; static const uint64_t kSeed0 = 1234567; static const uint64_t kSeed1 = k0; static const int kDataSize = 1 << 20; static const int kTestSize = 300; #define kSeed128 Uint128(kSeed0, kSeed1) static char data[kDataSize]; static int completed_self_tests = 0; static int errors = 0; // Initialize data to pseudorandom values. void Setup() { if (completed_self_tests == 0) { uint64_t a = 9; uint64_t b = 777; for (int i = 0; i < kDataSize; i++) { a += b; b += a; a = (a ^ (a >> 41)) * k0; b = (b ^ (b >> 41)) * k0 + i; uint8_t u = b >> 37; memcpy(data + i, &u, 1); // uint8_t -> char } } } int NoteErrors() { #define NUM_SELF_TESTS 9 if (++completed_self_tests == NUM_SELF_TESTS) std::exit(errors > 0); return errors; } template inline bool IsNonZero(T x) { return x != 0; } template <> inline bool IsNonZero(uint128_t x) { return x != Uint128(0, 0); } #endif // FARMHASH_SELF_TEST_GUARD namespace farmhashntTest { uint32_t CreateSeed(int offset, int salt) { uint32_t h = static_cast(salt & 0xffffffff); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h += static_cast(offset & 0xffffffff); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); return h; } #undef SEED #undef SEED1 #undef SEED0 #define SEED CreateSeed(offset, -1) #define SEED0 CreateSeed(offset, 0) #define SEED1 CreateSeed(offset, 1) #undef TESTING #define TESTING 1 #if TESTING uint32_t expected[] = { 2681724312u, 797982799u, 921001710u, 2134990486u, 2244477846u, 2992121793u, 3943596029u, 452431531u, 2557197665u, 2532580744u, 3099673830u, 3696623795u, 3281581178u, 1882212500u, 275903667u, 3033004529u, 1402319660u, 2699376854u, 4222949502u, 1712034059u, 1330324210u, 2921867846u, 1728752234u, 326029180u, 3349570000u, 1612122221u, 1646032583u, 1432476832u, 3552092450u, 1499109081u, 1554038301u, 3190844552u, 540224401u, 489963606u, 1562872448u, 2128624475u, 1262831810u, 1672724608u, 2077310004u, 1911523866u, 294527927u, 1389770549u, 2026137563u, 629449419u, 2489287368u, 645684964u, 230403464u, 3272648435u, 165370827u, 1230085527u, 3628174014u, 851743255u, 1554380634u, 3667013118u, 2290487377u, 1909203251u, 1498556724u, 4165088768u, 197618179u, 914413116u, 1913303225u, 3117299654u, 1357272220u, 507436733u, 1413396341u, 146044391u, 429095991u, 3056862311u, 366414107u, 2293458109u, 1684583131u, 1170404994u, 520792961u, 1577421232u, 4033596884u, 4229339322u, 3242407770u, 2649785113u, 816692935u, 3555213933u, 517646945u, 2180594090u, 3047062993u, 2391606125u, 382936554u, 788479970u, 2826990641u, 3167748333u, 1758123094u, 389974094u, 3338548567u, 2583576230u, 3198590202u, 4155628142u, 542201663u, 2856634168u, 3948351189u, 4194218315u, 1467786451u, 2743592929u, 1062268187u, 3810665822u, 2560479831u, 997658837u, 3067277639u, 1211737169u, 59581167u, 1389679610u, 4189944477u, 100876854u, 2062343506u, 3088828656u, 3284356565u, 3130054947u, 3532596884u, 3887208531u, 259034107u, 3233195759u, 3200749877u, 760633989u, 1115203611u, 1516407838u, 1778459926u, 2146672889u, 2457048126u, 2217471853u, 862072556u, 3745267835u, 701920051u, 581695350u, 1410111809u, 3326135446u, 2187968410u, 4267859263u, 479241367u, 2868987960u, 704325635u, 1418509533u, 735688735u, 3283299459u, 813690332u, 1439630796u, 3195309868u, 1616408198u, 3254795114u, 2799925823u, 3929484338u, 1798536177u, 4205965408u, 1499475160u, 4247675634u, 3779953975u, 785893184u, 2778575413u, 1160134629u, 823113169u, 4116162021u, 4167766971u, 2487440590u, 4004655503u, 4044418876u, 1462554406u, 2011102035u, 4265993528u, 576405853u, 4038839101u, 2425317635u, 1401013391u, 3062418115u, 3167030094u, 2602636307u, 4264167741u, 4017058800u, 1029665228u, 4036354071u, 2670703363u, 688472265u, 1054670286u, 338058159u, 1539305024u, 146827036u, 4060134777u, 2502815838u, 1603444633u, 2448966429u, 3891353218u, 1082330589u, 201837927u, 2848283092u, 883849006u, 1982110346u, 541496720u, 133643215u, 3847827123u, 4015671361u, 2849988118u, 3452457457u, 2102063419u, 3281002516u, 1539151988u, 1147951686u, 2005032160u, 2415262714u, 116647396u, 1029284767u, 2159170082u, 1919171906u, 2017579106u, 2473524405u, 1694443528u, 3671562289u, 505662155u, 1019936943u, 1511077569u, 773792826u, 2089123665u, 484732447u, 1120017626u, 2809286837u, 4029205195u, 1097806406u, 136118734u, 4017075736u, 1403506686u, 1516736273u, 2562064338u, 2984955003u, 3071338162u, 1923531348u, 771592405u, 2586632018u, 4032960199u, 2687561076u, 308584855u, 1692079268u, 2565680167u, 3674576684u, 3770488806u, 69201295u, 1255084262u, 3593730713u, 54945052u, 1939595371u, 2432427547u, 2295501078u, 1280920000u, 82177963u, 1121403845u, 2889101923u, 713121337u, 1747052377u, 927011680u, 4142246789u, 1958963937u, 1636932722u, 4075249328u, 2025886508u, 3026358429u, 1845587644u, 3615577014u, 1363253259u, 3087190425u, 341851980u, 2515339233u, 1276595523u, 460237542u, 4198897105u, 2069753399u, 4278599955u, 356742959u, 3735275001u, 1750561299u, 668829411u, 3384018814u, 4233785523u, 451656820u, 107312677u, 2390172694u, 1216645846u, 164402616u, 1689811113u, 1767810825u, 1397772514u, 3323383489u, 2986430557u, 207428029u, 2260498180u, 2360400900u, 1263709570u, 1377764574u, 4252610345u, 1099809675u, 2776960536u, 3542220540u, 3752806924u, 337070226u, 3267551635u, 1306761320u, 2220373824u, 4109252858u, 896322512u, 1788337208u, 1336556841u, 2911512007u, 3712582785u, 3071359622u, 2561488770u, 3898950547u, 536047554u, 2040551642u, 3528794619u, 3565689036u, 1197100813u, 1864569342u, 3329594980u, 490608221u, 1174785921u, 3287246572u, 2163330264u, 500120236u, 2520062970u, 1561519055u, 4042710240u, 2774151984u, 3160666939u, 96459446u, 1878067032u, 4237425634u, 2952135524u, 4100839753u, 1265237690u, 4246879223u, 834830418u, 3476334357u, 4277111759u, 2511026735u, 3065234219u, 556796152u, 198182691u, 2913077464u, 1535115487u, 4046477658u, 140762681u, 990407433u, 2198985327u, 2926590471u, 559702706u, 82077489u, 1096697687u, 4190838199u, 3046872820u, 1583801700u, 2185339100u, 3912423882u, 3703603898u, 2540185277u, 1446869792u, 4051584612u, 2719373510u, 1675560450u, 1996164093u, 405251683u, 2864244470u, 4071581802u, 2028708916u, 803575837u, 557660441u, 3841480082u, 255451671u, 779434428u, 3452203069u, 2285701422u, 1568745354u, 823305654u, 3184047862u, 4159715581u, 3160134214u, 3198900547u, 1566527339u, 4194096960u, 1496132623u, 1719108984u, 2584236470u, 531310503u, 3456882941u, 3382290593u, 467441309u, 3241407531u, 2540270567u, 1397502982u, 3348545480u, 811750340u, 1017047954u, 2540585554u, 3531646869u, 943914610u, 1903578924u, 1911188923u, 241574049u, 3181425568u, 3529565564u, 240953857u, 2964595704u, 3828377737u, 4260564140u, 4262383425u, 383233885u, 4051263539u, 919677938u, 1683612329u, 4204155962u, 2283918569u, 4153726847u, 350160869u, 1387233546u, 1891558063u, 740563169u, 330624974u, 2948665536u, 376390582u, 3799363969u, 3187805406u, 2263421398u, 1928519266u, 2746577402u, 2108753646u, 768287270u, 2247006571u, 212490675u, 917121602u, 2549835613u, 2864033668u, 3738062408u, 2006922227u, 2616619070u, 3449066284u, 431292293u, 786322314u, 1415970351u, 3263135197u, 2954777083u, 3206261120u, 2287507921u, 1781944746u, 4081586725u, 1109175923u, 1813855658u, 1129462471u, 1037031473u, 3389003793u, 3122687303u, 1164309901u, 3193251135u, 3626554867u, 3071568023u, 3656006011u, 1167681812u, 3155218919u, 2704165015u, 1854985978u, 1712976649u, 878608872u, 4155949943u, 3163786257u, 1626463554u, 1256485167u, 582664250u, 2083771672u, 804336148u, 2770847216u, 1674051445u, 3992583643u, 2966108111u, 900741486u, 4014551783u, 300318232u, 3517585534u, 542270815u, 760762191u, 1216399252u, 643179562u, 3652676161u, 2990167340u, 3262252593u, 2134299399u, 411263051u, 1342880802u, 1967599860u, 853593042u, 2682611693u, 850464484u, 3286110054u, 3842907484u, 3623364733u, 3693536939u, 1615375832u, 2318423400u, 4145497671u, 1728968857u, 2686506989u, 1502282913u, 2151665147u, 3651607391u, 1178454681u, 4146839064u, 2601416506u, 1448097974u, 238887261u, 4093725287u, 2367569534u, 679517009u, 3539886328u, 3086277222u, 1390394371u, 119173722u, 1766260771u, 751439914u, 215917713u, 2656990891u, 1570750352u, 3533987737u, 3576119563u, 963183826u, 3796810515u, 136547246u, 2592925324u, 427154472u, 1228758574u, 1464255968u, 2984611177u, 2001585786u, 1525438381u, 1348536411u, 2861338018u, 764077711u, 3785343245u, 457568934u, 4104954272u, 2381948487u, 3148473363u, 2180270337u, 1387729170u, 951677556u, 2721005055u, 66786703u, 1149351924u, 1895026827u, 3711056516u, 3638638708u, 2263003308u, 3448840877u, 225333538u, 3797521928u, 3262952567u, 2078619498u, 1178073973u, 3288261538u, 1496966875u, 2481012988u, 114945840u, 1632780103u, 2087949619u, 3787017905u, 2575395164u, 2971726178u, 3642087909u, 3894199764u, 203853421u, 425935223u, 3565833278u, 1748785729u, 580966986u, 2124704694u, 1107045577u, 1067532701u, 1406028344u, 18613994u, 3476683808u, 3762914298u, 1844996900u, 904215228u, 1118521573u, 3657647605u, 3136157065u, 2287683323u, 126005630u, 3555092974u, 49515858u, 1010661841u, 1902040126u, 1400735275u, 2771676666u, 2225229957u, 3454177594u, 2883475137u, 4144472319u, 1051332394u, 542648229u, 1669710469u, 553041029u, 584127807u, 2993670925u, 3587959456u, 1745399498u, 1404723176u, 1334333531u, 3239516985u, 1275954779u, 367320647u, 3684418197u, 4030809053u, 484559105u, 4255931645u, 4271715616u, 3171911678u, 928543347u, 2159512867u, 313902234u, 647086234u, 577214736u, 1130129573u, 995791646u, 1645086060u, 4122335794u, 1064648931u, 2752145076u, 3312498873u, 4238535494u, 1471227427u, 633688562u, 1959779970u, 766642813u, 1380896111u, 3647601207u, 1733961041u, 521947915u, 189164145u, 486382294u, 3770038872u, 3235740744u, 1912506671u, 2276864677u, 1588060152u, 2504457929u, 1471020554u, 3623212998u, 3026631806u, 2342164722u, 1674890530u, 3011542850u, 3549160092u, 4290680005u, 3943068002u, 2273781461u, 2127663659u, 1646681121u, 447810651u, 2366308558u, 970504950u, 2008155560u, 2695940969u, 3444688454u, 1739318893u, 2683090634u, 2774816580u, 437560100u, 512012738u, 3305170944u, 665292744u, 3580039116u, 1579404983u, 3397891494u, 710590371u, 2514565805u, 3624609754u, 3516075816u, 1314000850u, 1935166880u, 3257747610u, 3776931214u, 3183054185u, 675129307u, 3333261712u, 1154611403u, 2759854023u, 1963228038u, 505138315u, 1803966773u, 4032705384u, 798395739u, 3473799845u, 476400898u, 602972493u, 3289878097u, 2520311409u, 3214794876u, 748160407u, 1326769504u, 902775872u, 1372805534u, 1213925114u, 3009384989u, 3781981134u, 2835608783u, 2716786748u, 1669490957u, 1089334066u, 250756920u, 4041016629u, 2495807367u, 2008251381u, 106212622u, 1927268995u, 2251978818u, 3788056262u, 3678660147u, 2656772270u, 1997584981u, 2668998785u, 2954162084u, 845687881u, 776018378u, 2066910012u, 918315064u, }; // Return false only if offset is -1 and a spot check of 3 hashes all yield 0. bool Test(int offset, int len = 0) { #undef Check #undef IsAlive #define Check(x) do { \ const uint32_t actual = (x), e = expected[index++]; \ bool ok = actual == e; \ if (!ok) { \ cerr << "expected " << hex << e << " but got " << actual << endl; \ ++errors; \ } \ assert(ok); \ } while (0) #define IsAlive(x) do { alive += IsNonZero(x); } while (0) // After the following line is where the uses of "Check" and such will go. static int index = 0; if (offset == -1) { int alive = 0; IsAlive(farmhashnt::Hash32WithSeed(data, len++, SEED)); IsAlive(farmhashnt::Hash32(data, len++)); IsAlive(farmhashnt::Hash32(data, len++)); len -= 3; return alive > 0; } Check(farmhashnt::Hash32WithSeed(data + offset, len, SEED)); Check(farmhashnt::Hash32(data + offset, len)); return true; #undef Check #undef IsAlive } int RunTest() { Setup(); int i = 0; cout << "Running farmhashntTest"; if (!Test(-1)) { cout << "... Unavailable\n"; return NoteErrors(); } // Good. The function is attempting to hash, so run the full test. int errors_prior_to_test = errors; for ( ; i < kTestSize - 1; i++) { Test(i * i, i); } for ( ; i < kDataSize; i += i / 7) { Test(0, i); } Test(0, kDataSize); cout << (errors == errors_prior_to_test ? "... OK\n" : "... Failed\n"); return NoteErrors(); } #else // After the following line is where the code to print hash codes will go. void Dump(int offset, int len) { cout << farmhashnt::Hash32WithSeed(data + offset, len, SEED) << "u," << endl; cout << farmhashnt::Hash32(data + offset, len) << "u," << endl; } #endif #undef SEED #undef SEED1 #undef SEED0 } // namespace farmhashntTest #if TESTING static int farmhashntTestResult = farmhashntTest::RunTest(); #else int main(int argc, char** argv) { Setup(); cout << "uint32_t expected[] = {\n"; int i = 0; for ( ; i < kTestSize - 1; i++) { farmhashntTest::Dump(i * i, i); } for ( ; i < kDataSize; i += i / 7) { farmhashntTest::Dump(0, i); } farmhashntTest::Dump(0, kDataSize); cout << "};\n"; } #endif #ifndef FARMHASH_SELF_TEST_GUARD #define FARMHASH_SELF_TEST_GUARD #include #include #include using std::cout; using std::cerr; using std::endl; using std::hex; static const uint64_t kSeed0 = 1234567; static const uint64_t kSeed1 = k0; static const int kDataSize = 1 << 20; static const int kTestSize = 300; #define kSeed128 Uint128(kSeed0, kSeed1) static char data[kDataSize]; static int completed_self_tests = 0; static int errors = 0; // Initialize data to pseudorandom values. void Setup() { if (completed_self_tests == 0) { uint64_t a = 9; uint64_t b = 777; for (int i = 0; i < kDataSize; i++) { a += b; b += a; a = (a ^ (a >> 41)) * k0; b = (b ^ (b >> 41)) * k0 + i; uint8_t u = b >> 37; memcpy(data + i, &u, 1); // uint8_t -> char } } } int NoteErrors() { #define NUM_SELF_TESTS 9 if (++completed_self_tests == NUM_SELF_TESTS) std::exit(errors > 0); return errors; } template inline bool IsNonZero(T x) { return x != 0; } template <> inline bool IsNonZero(uint128_t x) { return x != Uint128(0, 0); } #endif // FARMHASH_SELF_TEST_GUARD namespace farmhashsaTest { uint32_t CreateSeed(int offset, int salt) { uint32_t h = static_cast(salt & 0xffffffff); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h += static_cast(offset & 0xffffffff); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); return h; } #undef SEED #undef SEED1 #undef SEED0 #define SEED CreateSeed(offset, -1) #define SEED0 CreateSeed(offset, 0) #define SEED1 CreateSeed(offset, 1) #undef TESTING #define TESTING 1 #if TESTING uint32_t expected[] = { 4223616069u, 3696677242u, 4081014168u, 2576519988u, 2212771159u, 1112731063u, 1020067935u, 3955445564u, 1451961420u, 653440099u, 31917516u, 2957164615u, 2590087362u, 3879448744u, 176305566u, 2447367541u, 1359016305u, 3363804638u, 1117290165u, 1062549743u, 2437877004u, 1894455839u, 673206794u, 3486923651u, 3269862919u, 2303349487u, 1380660650u, 595525107u, 1525325287u, 2025609358u, 176408838u, 1592885012u, 864896482u, 2101378090u, 3489229104u, 2118965695u, 581644891u, 2718789079u, 631613207u, 4228658372u, 3867875546u, 3531368319u, 3804516756u, 3317755099u, 1619744564u, 2884717286u, 1088213445u, 2667691076u, 3727873235u, 2330406762u, 858590707u, 123802208u, 4150036245u, 182283099u, 1478882570u, 3282617403u, 819171187u, 1172627392u, 4254302102u, 2957028020u, 437030323u, 2452147680u, 2868246750u, 3530169402u, 3154852132u, 215019192u, 357580983u, 1354454461u, 1108813287u, 2324008118u, 2315997713u, 4181601562u, 1360882441u, 92423273u, 3048866755u, 3369188505u, 3664371439u, 2920710428u, 1027891570u, 2653166430u, 3461888315u, 1475780447u, 292769636u, 1737473313u, 4064110516u, 4170160075u, 762850927u, 3630603695u, 2803307356u, 844987665u, 460980967u, 3005635467u, 2802568977u, 588668033u, 2148940781u, 3239099984u, 1266953698u, 3197808789u, 3519942533u, 2511995334u, 2553810188u, 871667697u, 1358675720u, 1499319171u, 2044931270u, 1210355103u, 807152540u, 3262320756u, 2810214575u, 1813386141u, 4089465863u, 903928165u, 1388899322u, 3209183659u, 834536144u, 2733354550u, 2742289921u, 3689042563u, 2655593281u, 4169686303u, 415985561u, 138892376u, 516115393u, 65683883u, 4162865100u, 889944635u, 313566528u, 3346420907u, 1504303591u, 2256809275u, 742243229u, 779775302u, 3140940172u, 2312556111u, 2304095772u, 1151741606u, 2194712422u, 1714084652u, 3272736835u, 1311540658u, 191179665u, 3996605106u, 1657345233u, 4205442903u, 1553339212u, 2351843044u, 1647502006u, 2525516233u, 292202846u, 1498646290u, 1429323381u, 974274898u, 3759331561u, 2881238887u, 826787221u, 1069622448u, 221991032u, 1462969082u, 2799661508u, 364022781u, 2594244377u, 797773898u, 4097839290u, 1529150125u, 2456805570u, 541503425u, 3936326142u, 3112719954u, 775223581u, 3074018423u, 3198488875u, 1772191849u, 2456535211u, 3154686028u, 1520862019u, 4005829426u, 1306433767u, 1943028506u, 2246000782u, 1057766454u, 3761996982u, 3441075333u, 898641979u, 3450209088u, 3941329307u, 3289922449u, 3085075827u, 1814193220u, 690422997u, 2627846676u, 2653520704u, 3739145533u, 3996776010u, 2287072592u, 1346671698u, 3082629900u, 2298811274u, 3639722036u, 1729419228u, 1836765953u, 3708118742u, 213436u, 950223749u, 3734247682u, 2924575678u, 1382024841u, 2431637732u, 3448846682u, 1341301397u, 4206956590u, 1730650902u, 2581075456u, 1542359141u, 707222542u, 2925350541u, 3846303536u, 3579103295u, 3932175763u, 1339615732u, 848825750u, 1070170828u, 1964973818u, 577060344u, 607721296u, 4031023048u, 406883794u, 3991905552u, 1198544082u, 872468460u, 1044847096u, 3159976313u, 3020028266u, 2108700400u, 3373767922u, 264431841u, 2817097007u, 3700061048u, 1733731531u, 3459415893u, 80378591u, 1479875104u, 19735612u, 1382658977u, 3416562245u, 1959852842u, 2384002344u, 124683828u, 3725782174u, 2300301222u, 393852269u, 1302492002u, 3623776492u, 3787086417u, 1730024749u, 1710531361u, 443700716u, 1461987482u, 671998131u, 3018380746u, 2592292305u, 3390799372u, 3945101155u, 3743494852u, 3716045582u, 996005166u, 320698449u, 3420221765u, 1518157951u, 2555810666u, 3381929684u, 2019638523u, 3088262796u, 2072178906u, 3433649364u, 203906916u, 34663784u, 290301305u, 1188021504u, 3754681145u, 3920313139u, 2840496520u, 1656802962u, 2288475489u, 3399185138u, 1296000826u, 2362384746u, 309633360u, 2719851778u, 776035930u, 3200733043u, 365690832u, 3326378243u, 1500331457u, 1625708592u, 4230903462u, 715344888u, 3363777768u, 2243620288u, 2890765789u, 553154234u, 4044100108u, 4056887320u, 1185656496u, 3671476744u, 1064586897u, 1154949698u, 3493481974u, 1294573722u, 1869224012u, 2530084956u, 995321553u, 833419249u, 563815282u, 250258043u, 2970801822u, 441007535u, 42246961u, 2820426655u, 2878882436u, 2363245780u, 2138489282u, 2972360481u, 2312619393u, 3598664848u, 3071556076u, 776990325u, 3220427357u, 2257939577u, 3817305903u, 1502979698u, 3159755934u, 3955997276u, 2423850008u, 1959927572u, 1219782288u, 4119776679u, 1124253854u, 3678052422u, 2620644947u, 1262408666u, 3480072280u, 2627137665u, 807538749u, 3276646337u, 518510128u, 1137828655u, 1498449110u, 3031692317u, 1125635969u, 1130096111u, 780007336u, 3111856399u, 1014917264u, 780877352u, 2909458336u, 4235949214u, 2423879289u, 275888892u, 3891926795u, 3538163953u, 54815161u, 162228302u, 258154068u, 3554455591u, 1801469029u, 2801563220u, 726560058u, 2450221940u, 3677582978u, 440993800u, 424762443u, 2624525253u, 2587715329u, 2292264424u, 1074856749u, 3294752007u, 3164112672u, 2399146799u, 1920182465u, 3858835361u, 193755240u, 3333610311u, 1757504059u, 2576027039u, 2775253365u, 2939191561u, 1046147275u, 235149906u, 4262218222u, 2900542726u, 2260154702u, 1019551635u, 1194720570u, 3519118691u, 3039483153u, 84918216u, 3053381097u, 2572396843u, 3849763371u, 2782686780u, 3710049554u, 3403430713u, 2346080784u, 2496307442u, 1597281872u, 696018239u, 704625714u, 623026921u, 3182413559u, 3794540330u, 305497722u, 1592680199u, 2377854072u, 3060601746u, 3953057908u, 3941551588u, 1033716182u, 2765716854u, 1309699058u, 3519400181u, 3073370877u, 115583008u, 4032909296u, 2944563574u, 3762753718u, 192842727u, 1711348701u, 3086147235u, 1658229443u, 1479783872u, 3839977157u, 225619117u, 1349684817u, 1964813173u, 565753187u, 2530252046u, 840014353u, 1645183704u, 3668429078u, 3438418557u, 639704059u, 360837811u, 2531807958u, 1572353913u, 2116037299u, 1948437512u, 744553393u, 2380697034u, 3775234105u, 3816065157u, 301868653u, 2960939561u, 3306528247u, 2389296549u, 805918610u, 1759358265u, 1760876328u, 2827601706u, 2944594708u, 3313666458u, 2022601495u, 730938791u, 193539397u, 2026103244u, 802928398u, 2630934308u, 782805818u, 3499326016u, 293509489u, 3646131514u, 3182478647u, 854800333u, 2284531628u, 438528022u, 2339298129u, 1692289216u, 2427728723u, 46501288u, 350652353u, 1355971222u, 889682372u, 944799254u, 2763906061u, 2807550612u, 2683762637u, 100870317u, 2449357318u, 2638348436u, 4206088869u, 1788948473u, 3537588549u, 2782490204u, 134406470u, 2409190528u, 2362439849u, 1861661528u, 2101513194u, 1424834765u, 3581765745u, 3185999525u, 2057487100u, 2303941176u, 3639628788u, 1180265315u, 230437935u, 2108319366u, 1131685143u, 1055685292u, 1509007009u, 1258485140u, 560525005u, 3598799040u, 3835680585u, 1851859628u, 332858996u, 641769248u, 4252450037u, 865386707u, 720719117u, 3133612164u, 3833045874u, 3492515435u, 2465970289u, 4234420011u, 573859916u, 252532886u, 870392318u, 4051320920u, 894929092u, 3748361688u, 699355960u, 1885212350u, 1609756949u, 461896870u, 1337065461u, 1775211059u, 1786193749u, 2815154643u, 2128729882u, 969639529u, 3960427545u, 859416958u, 2739758802u, 2698032197u, 2813292418u, 1985467524u, 396604317u, 4122172759u, 1201259789u, 4282051702u, 3270018895u, 961215209u, 961075860u, 4211926998u, 4088374597u, 577510509u, 3058349487u, 4025377754u, 2815478438u, 471023164u, 3947959608u, 4161486934u, 2299888461u, 1103571511u, 2450153872u, 1839939275u, 108299608u, 858086440u, 1030152945u, 3895328530u, 3009080718u, 3690840454u, 3847025277u, 152331362u, 161365689u, 831319961u, 2166017294u, 3945322722u, 4059970216u, 1420824131u, 2770648308u, 1567250186u, 2181067149u, 1939743488u, 3080158120u, 3435218248u, 2495237495u, 3814085102u, 3180983013u, 3199054292u, 2204745908u, 1140337267u, 2213569784u, 1941879842u, 2105562605u, 3618835614u, 2247103645u, 2492473487u, 856414299u, 166022030u, 4080104712u, 3218935344u, 3284220561u, 4261581452u, 1206944836u, 3496705432u, 2215996876u, 3154627465u, 3384005496u, 742170556u, 1333047620u, 802680366u, 156833431u, 2682100354u, 2493654830u, 584848366u, 1691693131u, 2169934170u, 779968026u, 2099545800u, 1423039695u, 4292110968u, 4266576788u, 149142597u, 748501873u, 3865014822u, 1913588198u, 130285614u, 3500768879u, 915458923u, 3071792750u, 1339986633u, 4143929149u, 4048379479u, 725193827u, 1375113643u, 2425277412u, 4144659274u, 465714768u, 226991589u, 2212127704u, 3936145258u, 2891024846u, 3816000225u, 979331165u, 1749907536u, 53847318u, 1462525833u, 2961425455u, 368859113u, 3572721452u, 453048644u, 1628629918u, 3497673923u, 3619079585u, 139870565u, 1518176798u, 3933074281u, 1878623729u, 2074035641u, 3016759257u, 1313053591u, 2557706970u, 2348296582u, 962370022u, 2337285014u, 1618936717u, 1915877085u, 2743743122u, 3250783882u, 1346652536u, 143311109u, 2443788461u, 1048248964u, 2806619339u, 3263266976u, 1668146349u, 3397428868u, 3276188862u, 1774196343u, 1993847813u, 2771079610u, 476672419u, 2119050359u, 2918326659u, 2245402721u, 2692910474u, 2374383269u, 342400227u, 2961437795u, 3899230368u, 337787132u, 3664444935u, 1269451153u, 2971526729u, 1486511182u, 791070133u, 2570319890u, 3482497490u, 2134230518u, 4273391202u, 1825511330u, 3947753714u, 1389755724u, 3995075516u, 2081052615u, 3626343470u, 4213603435u, 2137917278u, 2898987303u, 3059215715u, 3383237881u, 3003674434u, 409174425u, 1911915604u, 2087728055u, 2942005882u, 3386522440u, 714936074u, 261924004u, 3268784033u, 1141188757u, 2413217552u, 1515163433u, }; // Return false only if offset is -1 and a spot check of 3 hashes all yield 0. bool Test(int offset, int len = 0) { #undef Check #undef IsAlive #define Check(x) do { \ const uint32_t actual = (x), e = expected[index++]; \ bool ok = actual == e; \ if (!ok) { \ cerr << "expected " << hex << e << " but got " << actual << endl; \ ++errors; \ } \ assert(ok); \ } while (0) #define IsAlive(x) do { alive += IsNonZero(x); } while (0) // After the following line is where the uses of "Check" and such will go. static int index = 0; if (offset == -1) { int alive = 0; IsAlive(farmhashsa::Hash32WithSeed(data, len++, SEED)); IsAlive(farmhashsa::Hash32(data, len++)); IsAlive(farmhashsa::Hash32(data, len++)); len -= 3; return alive > 0; } Check(farmhashsa::Hash32WithSeed(data + offset, len, SEED)); Check(farmhashsa::Hash32(data + offset, len)); return true; #undef Check #undef IsAlive } int RunTest() { Setup(); int i = 0; cout << "Running farmhashsaTest"; if (!Test(-1)) { cout << "... Unavailable\n"; return NoteErrors(); } // Good. The function is attempting to hash, so run the full test. int errors_prior_to_test = errors; for ( ; i < kTestSize - 1; i++) { Test(i * i, i); } for ( ; i < kDataSize; i += i / 7) { Test(0, i); } Test(0, kDataSize); cout << (errors == errors_prior_to_test ? "... OK\n" : "... Failed\n"); return NoteErrors(); } #else // After the following line is where the code to print hash codes will go. void Dump(int offset, int len) { cout << farmhashsa::Hash32WithSeed(data + offset, len, SEED) << "u," << endl; cout << farmhashsa::Hash32(data + offset, len) << "u," << endl; } #endif #undef SEED #undef SEED1 #undef SEED0 } // namespace farmhashsaTest #if TESTING static int farmhashsaTestResult = farmhashsaTest::RunTest(); #else int main(int argc, char** argv) { Setup(); cout << "uint32_t expected[] = {\n"; int i = 0; for ( ; i < kTestSize - 1; i++) { farmhashsaTest::Dump(i * i, i); } for ( ; i < kDataSize; i += i / 7) { farmhashsaTest::Dump(0, i); } farmhashsaTest::Dump(0, kDataSize); cout << "};\n"; } #endif #ifndef FARMHASH_SELF_TEST_GUARD #define FARMHASH_SELF_TEST_GUARD #include #include #include using std::cout; using std::cerr; using std::endl; using std::hex; static const uint64_t kSeed0 = 1234567; static const uint64_t kSeed1 = k0; static const int kDataSize = 1 << 20; static const int kTestSize = 300; #define kSeed128 Uint128(kSeed0, kSeed1) static char data[kDataSize]; static int completed_self_tests = 0; static int errors = 0; // Initialize data to pseudorandom values. void Setup() { if (completed_self_tests == 0) { uint64_t a = 9; uint64_t b = 777; for (int i = 0; i < kDataSize; i++) { a += b; b += a; a = (a ^ (a >> 41)) * k0; b = (b ^ (b >> 41)) * k0 + i; uint8_t u = b >> 37; memcpy(data + i, &u, 1); // uint8_t -> char } } } int NoteErrors() { #define NUM_SELF_TESTS 9 if (++completed_self_tests == NUM_SELF_TESTS) std::exit(errors > 0); return errors; } template inline bool IsNonZero(T x) { return x != 0; } template <> inline bool IsNonZero(uint128_t x) { return x != Uint128(0, 0); } #endif // FARMHASH_SELF_TEST_GUARD namespace farmhashsuTest { uint32_t CreateSeed(int offset, int salt) { uint32_t h = static_cast(salt & 0xffffffff); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h += static_cast(offset & 0xffffffff); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); return h; } #undef SEED #undef SEED1 #undef SEED0 #define SEED CreateSeed(offset, -1) #define SEED0 CreateSeed(offset, 0) #define SEED1 CreateSeed(offset, 1) #undef TESTING #define TESTING 1 #if TESTING uint32_t expected[] = { 4223616069u, 3696677242u, 4081014168u, 2576519988u, 2212771159u, 1112731063u, 1020067935u, 3955445564u, 1451961420u, 653440099u, 31917516u, 2957164615u, 2590087362u, 3879448744u, 176305566u, 2447367541u, 1359016305u, 3363804638u, 1117290165u, 1062549743u, 2437877004u, 1894455839u, 673206794u, 3486923651u, 3269862919u, 2303349487u, 1380660650u, 595525107u, 1525325287u, 2025609358u, 176408838u, 1592885012u, 864896482u, 2101378090u, 3489229104u, 2118965695u, 581644891u, 2718789079u, 631613207u, 4228658372u, 3867875546u, 3531368319u, 3804516756u, 3317755099u, 1619744564u, 2884717286u, 1088213445u, 2667691076u, 3727873235u, 2330406762u, 858590707u, 457744844u, 4150036245u, 2000404290u, 1478882570u, 901678172u, 819171187u, 195942998u, 4254302102u, 3967266927u, 437030323u, 4018009204u, 2868246750u, 3540087514u, 3154852132u, 3319116625u, 357580983u, 3177665294u, 1108813287u, 1253366798u, 2315997713u, 510718750u, 1360882441u, 2770216279u, 3048866755u, 3406961221u, 3664371439u, 1151145514u, 1027891570u, 2699067992u, 3461888315u, 198061905u, 292769636u, 1106771795u, 4064110516u, 3258279756u, 762850927u, 1818699721u, 2803307356u, 3919169404u, 460980967u, 3125535078u, 2802568977u, 3582546426u, 2148940781u, 3963274378u, 1266953698u, 204185123u, 1100034381u, 3009193601u, 4200651967u, 274889605u, 2700589508u, 952511689u, 3765324859u, 3465498478u, 4014967037u, 2070988082u, 2972423530u, 3068638223u, 4156773651u, 489509804u, 1323863238u, 3731914806u, 2846098469u, 2728930632u, 346814072u, 848146907u, 551160669u, 4165126521u, 2039095001u, 4179859388u, 2434936359u, 2764414551u, 238491210u, 732483969u, 3366512764u, 478307468u, 4124179572u, 4142733597u, 1953448206u, 4199329278u, 865077060u, 2627662116u, 2802499360u, 3141206831u, 1959218197u, 911371451u, 125987200u, 2821366175u, 2530992747u, 2409206225u, 117991880u, 2133402461u, 895510531u, 428719601u, 3036014536u, 1223783733u, 733793540u, 970650405u, 547701766u, 570764615u, 3224485368u, 3192714940u, 319942831u, 3940200341u, 362056204u, 2832368105u, 1853281226u, 3296434636u, 3752508307u, 604292768u, 2231940616u, 1204094681u, 866194005u, 2405201650u, 2466384396u, 380829379u, 230033818u, 2783417588u, 4249886729u, 829569301u, 2988322580u, 2299983554u, 74748560u, 737514425u, 3153050211u, 652642663u, 1270205115u, 227197032u, 2773091790u, 325849216u, 49998791u, 4043203010u, 3662748068u, 1709364383u, 1179105165u, 1478504366u, 2980456610u, 1167476429u, 1590390732u, 1306256496u, 292008135u, 374690995u, 1809200819u, 1680595904u, 646040226u, 1742445560u, 2435776844u, 3703683804u, 478742495u, 814967947u, 2698190177u, 1003617993u, 1436118705u, 217056304u, 1412287094u, 2738417466u, 2933279339u, 3461877733u, 1203141205u, 2119492857u, 1134895723u, 1560001021u, 3786320122u, 3748116258u, 3486219595u, 702138030u, 1062984182u, 232789133u, 1566523968u, 3885443778u, 1820171888u, 3655858585u, 2316903005u, 2678779620u, 395625433u, 1609107564u, 3108726411u, 2937837224u, 3911907151u, 557272509u, 3893435978u, 1542613576u, 1079886893u, 2624566322u, 1413700616u, 2796974006u, 1922556114u, 562820464u, 2845409784u, 54180312u, 1898782464u, 3681814953u, 2417064617u, 1815464483u, 911626132u, 2964575550u, 1852696128u, 2319647785u, 1998904590u, 619992689u, 3073207513u, 1238163512u, 3199435982u, 828667254u, 3561155502u, 3943095163u, 1045711849u, 2238679131u, 2114975398u, 713808403u, 3871787494u, 2572031161u, 2360934075u, 2337781107u, 262596504u, 693836699u, 2129369850u, 3543189427u, 962205222u, 3685581020u, 692974477u, 725182211u, 646123906u, 2368836544u, 2505872733u, 1999977610u, 1639885802u, 1475058032u, 207023609u, 2773581234u, 3524857793u, 3433371102u, 3243027613u, 1787668353u, 985757946u, 3896012929u, 702356957u, 3559331129u, 884084870u, 4009998120u, 648888720u, 1403349048u, 1624342778u, 1766674171u, 2518582204u, 3251243146u, 792751003u, 1377201813u, 3629686054u, 1583734324u, 3647107626u, 4258564381u, 1469878609u, 1940598241u, 2755003690u, 1907120418u, 109916701u, 775347954u, 2090960874u, 611281803u, 3470490146u, 3301663253u, 1835412158u, 1803066146u, 591872433u, 550703713u, 1495089683u, 826492808u, 817200035u, 4177474571u, 688070143u, 971427632u, 1442499481u, 3568640348u, 2789993738u, 85808128u, 2058346726u, 394058570u, 3466511434u, 318905230u, 4149248030u, 415308316u, 165997598u, 1219639412u, 1648022659u, 2857432523u, 1422508004u, 468095522u, 296968649u, 430250611u, 1775562314u, 2976361671u, 1040036362u, 1372510167u, 292746272u, 3408238954u, 626061886u, 1317637569u, 1237775792u, 1218490455u, 2224234499u, 590942419u, 713995643u, 3541889330u, 4140218960u, 3529791107u, 354462673u, 842607274u, 365048533u, 2638303414u, 3560458014u, 31621379u, 4210854794u, 1273118792u, 2572743762u, 3513175801u, 402066986u, 602524471u, 565029192u, 180576438u, 1288605959u, 2896244423u, 1420543484u, 1329862227u, 1791567324u, 4248690247u, 12917038u, 3483481310u, 2082050731u, 1611921143u, 2443766548u, 2216338811u, 2528006095u, 2984009021u, 674210884u, 2857608106u, 2155534809u, 1023105067u, 2968955846u, 3303624302u, 2502112850u, 245749006u, 3175229091u, 3342796184u, 3613785362u, 1614168851u, 2582149283u, 895403488u, 416205023u, 3792242000u, 529397534u, 299415203u, 4284673348u, 2096851282u, 1864524731u, 2012577738u, 3426363316u, 1387308508u, 1143610148u, 2027467219u, 3772856163u, 3453862623u, 2661437174u, 2047145955u, 2533381447u, 2059534115u, 439426587u, 1537543414u, 2384289877u, 3174229055u, 2658017753u, 2293148474u, 2359450158u, 3930242475u, 1510302397u, 3354288821u, 920095603u, 2415746928u, 2729472638u, 2261143371u, 848667611u, 919157153u, 3322393117u, 4103299943u, 413569608u, 68911216u, 3334990170u, 1228068652u, 1570056373u, 1905477543u, 2622302276u, 2935063895u, 3224810004u, 4211768578u, 828688131u, 3556122839u, 1930935348u, 2605825202u, 1540993970u, 3209115883u, 122847500u, 665638794u, 506571051u, 2691795295u, 3996966556u, 714660621u, 3662432239u, 470651837u, 1807432621u, 3755290953u, 359878860u, 2793081615u, 4065031431u, 904653062u, 2317800777u, 568501094u, 3492871707u, 2738806116u, 2883859610u, 3242080257u, 364246691u, 3601786516u, 3159362524u, 1578272201u, 1283574375u, 2912186103u, 2256279032u, 1540671086u, 2356088973u, 2892277779u, 3441449267u, 2225005503u, 3846428419u, 2014549218u, 2290734767u, 2126684614u, 4235463487u, 3811556204u, 174739661u, 767525888u, 47684458u, 4211168099u, 889063422u, 469864411u, 767407110u, 413337343u, 1618456644u, 2814499820u, 2401124192u, 632089437u, 1234980238u, 1288585402u, 3153169944u, 2917822069u, 1843320264u, 3794359132u, 3074573530u, 258629454u, 3813357060u, 3806887248u, 1665524736u, 3324533324u, 3005091922u, 793108368u, 1529669805u, 2332660395u, 2217730223u, 2634687611u, 442806463u, 1968135266u, 454523002u, 3177866230u, 2808960136u, 4259114138u, 4103264843u, 3103714075u, 2462967542u, 1466891491u, 477973764u, 834565647u, 741089037u, 218837573u, 1710536528u, 2469088212u, 1229072375u, 2828341u, 176923431u, 985763350u, 4095477420u, 1984145538u, 1870791084u, 674956677u, 1978138947u, 1296493993u, 1818183554u, 3443333721u, 2124949983u, 2549590262u, 2700850794u, 2662736367u, 739638109u, 4061447096u, 2960078422u, 2453781158u, 929570940u, 3200328383u, 2406328791u, 1419180666u, 2152455739u, 2805741044u, 3305999074u, 3183816361u, 2303165050u, 4922104u, 63096005u, 936656347u, 3104453886u, 1088673880u, 1113407526u, 1457890086u, 453478383u, 1107686695u, 3626027824u, 1159687359u, 2248467888u, 2004578380u, 3274954621u, 1787958646u, 2628726704u, 1138419798u, 3735442315u, 692385301u, 313807213u, 2329068673u, 59375364u, 3261084359u, 2088644507u, 2471153194u, 788336435u, 4024527246u, 141504460u, 2307553888u, 1930559950u, 48975711u, 2745693338u, 230161982u, 3429230862u, 1335968626u, 609591304u, 57435073u, 4279281136u, 3152151665u, 3984484924u, 3459883943u, 397478330u, 1738762229u, 3033590066u, 3611539498u, 1363463523u, 3319364965u, 2671169141u, 3819548561u, 1691193757u, 2423834608u, 2820147055u, 1378120632u, 1240565187u, 3180720050u, 680831086u, 3309658414u, 1986166490u, 762099827u, 510883662u, 2047373648u, 3606742294u, 3894965352u, 2342078853u, 1091255717u, 776594727u, 3217317445u, 1574468485u, 3844504016u, 2819598918u, 1037401010u, 2550943503u, 3867184001u, 1687911772u, 165313836u, 1679575281u, 2418947263u, 2038774952u, 3913543652u, 3209155736u, 149905221u, 3859604717u, 713919631u, 4069810796u, 1882959164u, 1019939034u, 2379867302u, 3666323035u, 1157389013u, 2422300650u, 3366777340u, 2526452062u, 1313747885u, 1039617868u, 1620553692u, 2032976978u, 578789528u, 1592846839u, 2270630604u, 897850577u, 1603294178u, 3105664807u, 1442670138u, 1728019360u, 79313861u, 1683031101u, 1913067024u, 4070719870u, 708986470u, 2586453359u, 3993348863u, 3358251279u, 3003552537u, 750174793u, 836888956u, 4190747426u, 4251291318u, 4145164938u, 1366883260u, 1912910955u, 510192669u, 1851315039u, 3574241274u, 3220062924u, 2821142039u, 1317082195u, 2274293302u, 1839219569u, 126586168u, 3989293643u, 2680178207u, 347056948u, 799681430u, 2864517481u, 3180404853u, 213140045u, 1956305184u, 1474675286u, 3085723423u, 2841859626u, 308421914u, 3670309263u, 1765052231u, 245459238u, 113434331u, 4079521092u, 2115235526u, 2943408816u, 1055476938u, 1506442339u, 2291296392u, 3267864332u, 1282145528u, 3700108015u, 1932843667u, 2677701670u, 6041177u, 3889648557u, 1461025478u, }; // Return false only if offset is -1 and a spot check of 3 hashes all yield 0. bool Test(int offset, int len = 0) { #undef Check #undef IsAlive #define Check(x) do { \ const uint32_t actual = (x), e = expected[index++]; \ bool ok = actual == e; \ if (!ok) { \ cerr << "expected " << hex << e << " but got " << actual << endl; \ ++errors; \ } \ assert(ok); \ } while (0) #define IsAlive(x) do { alive += IsNonZero(x); } while (0) // After the following line is where the uses of "Check" and such will go. static int index = 0; if (offset == -1) { int alive = 0; IsAlive(farmhashsu::Hash32WithSeed(data, len++, SEED)); IsAlive(farmhashsu::Hash32(data, len++)); IsAlive(farmhashsu::Hash32(data, len++)); len -= 3; return alive > 0; } Check(farmhashsu::Hash32WithSeed(data + offset, len, SEED)); Check(farmhashsu::Hash32(data + offset, len)); return true; #undef Check #undef IsAlive } int RunTest() { Setup(); int i = 0; cout << "Running farmhashsuTest"; if (!Test(-1)) { cout << "... Unavailable\n"; return NoteErrors(); } // Good. The function is attempting to hash, so run the full test. int errors_prior_to_test = errors; for ( ; i < kTestSize - 1; i++) { Test(i * i, i); } for ( ; i < kDataSize; i += i / 7) { Test(0, i); } Test(0, kDataSize); cout << (errors == errors_prior_to_test ? "... OK\n" : "... Failed\n"); return NoteErrors(); } #else // After the following line is where the code to print hash codes will go. void Dump(int offset, int len) { cout << farmhashsu::Hash32WithSeed(data + offset, len, SEED) << "u," << endl; cout << farmhashsu::Hash32(data + offset, len) << "u," << endl; } #endif #undef SEED #undef SEED1 #undef SEED0 } // namespace farmhashsuTest #if TESTING static int farmhashsuTestResult = farmhashsuTest::RunTest(); #else int main(int argc, char** argv) { Setup(); cout << "uint32_t expected[] = {\n"; int i = 0; for ( ; i < kTestSize - 1; i++) { farmhashsuTest::Dump(i * i, i); } for ( ; i < kDataSize; i += i / 7) { farmhashsuTest::Dump(0, i); } farmhashsuTest::Dump(0, kDataSize); cout << "};\n"; } #endif #ifndef FARMHASH_SELF_TEST_GUARD #define FARMHASH_SELF_TEST_GUARD #include #include #include using std::cout; using std::cerr; using std::endl; using std::hex; static const uint64_t kSeed0 = 1234567; static const uint64_t kSeed1 = k0; static const int kDataSize = 1 << 20; static const int kTestSize = 300; #define kSeed128 Uint128(kSeed0, kSeed1) static char data[kDataSize]; static int completed_self_tests = 0; static int errors = 0; // Initialize data to pseudorandom values. void Setup() { if (completed_self_tests == 0) { uint64_t a = 9; uint64_t b = 777; for (int i = 0; i < kDataSize; i++) { a += b; b += a; a = (a ^ (a >> 41)) * k0; b = (b ^ (b >> 41)) * k0 + i; uint8_t u = b >> 37; memcpy(data + i, &u, 1); // uint8_t -> char } } } int NoteErrors() { #define NUM_SELF_TESTS 9 if (++completed_self_tests == NUM_SELF_TESTS) std::exit(errors > 0); return errors; } template inline bool IsNonZero(T x) { return x != 0; } template <> inline bool IsNonZero(uint128_t x) { return x != Uint128(0, 0); } #endif // FARMHASH_SELF_TEST_GUARD namespace farmhashteTest { uint32_t CreateSeed(int offset, int salt) { uint32_t h = static_cast(salt & 0xffffffff); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h += static_cast(offset & 0xffffffff); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); return h; } #undef SEED #undef SEED1 #undef SEED0 #define SEED CreateSeed(offset, -1) #define SEED0 CreateSeed(offset, 0) #define SEED1 CreateSeed(offset, 1) #undef TESTING #define TESTING 1 #if TESTING uint32_t expected[] = { 1140953930u, 861465670u, 3277735313u, 2681724312u, 2598464059u, 797982799u, 890626835u, 800175912u, 2603993599u, 921001710u, 1410420968u, 2134990486u, 3283896453u, 1867689945u, 2914424215u, 2244477846u, 255297188u, 2992121793u, 1110588164u, 4186314283u, 161451183u, 3943596029u, 4019337850u, 452431531u, 283198166u, 2741341286u, 3379021470u, 2557197665u, 299850021u, 2532580744u, 452473466u, 1706958772u, 1298374911u, 3099673830u, 2199864459u, 3696623795u, 236935126u, 2976578695u, 4055299123u, 3281581178u, 1053458494u, 1882212500u, 2305012065u, 2169731866u, 3456121707u, 275903667u, 458884671u, 3033004529u, 3058973506u, 2379411653u, 1898235244u, 1402319660u, 2700149065u, 2699376854u, 147814787u, 720739346u, 2433714046u, 4222949502u, 4220361840u, 1712034059u, 3425469811u, 3690733394u, 4148372108u, 1330324210u, 594028478u, 2921867846u, 1635026870u, 192883107u, 780716741u, 1728752234u, 3280331829u, 326029180u, 3969463346u, 1436364519u, 393215742u, 3349570000u, 3824583307u, 1612122221u, 2859809759u, 3808705738u, 1379537552u, 1646032583u, 2233466664u, 1432476832u, 4023053163u, 2650381482u, 2052294713u, 3552092450u, 1628777059u, 1499109081u, 3476440786u, 3829307897u, 2960536756u, 1554038301u, 1145519619u, 3190844552u, 2902102606u, 3600725550u, 237495366u, 540224401u, 65721842u, 489963606u, 1448662590u, 397635823u, 1596489240u, 1562872448u, 1790705123u, 2128624475u, 180854224u, 2604346966u, 1435705557u, 1262831810u, 155445229u, 1672724608u, 1669465176u, 1341975128u, 663607706u, 2077310004u, 3610042449u, 1911523866u, 1043692997u, 1454396064u, 2563776023u, 294527927u, 1099072299u, 1389770549u, 703505868u, 678706990u, 2952353448u, 2026137563u, 3603803785u, 629449419u, 1933894405u, 3043213226u, 226132789u, 2489287368u, 1552847036u, 645684964u, 3828089804u, 3632594520u, 187883449u, 230403464u, 3151491850u, 3272648435u, 3729087873u, 1303930448u, 2002861219u, 165370827u, 916494250u, 1230085527u, 3103338579u, 3064290191u, 3807265751u, 3628174014u, 231181488u, 851743255u, 2295806711u, 1781190011u, 2988893883u, 1554380634u, 1142264800u, 3667013118u, 1968445277u, 315203929u, 2638023604u, 2290487377u, 732137533u, 1909203251u, 440398219u, 1891630171u, 1380301172u, 1498556724u, 4072067757u, 4165088768u, 4204318635u, 441430649u, 3931792696u, 197618179u, 956300927u, 914413116u, 3010839769u, 2837339569u, 2148126371u, 1913303225u, 3074915312u, 3117299654u, 4139181436u, 2993479124u, 3178848746u, 1357272220u, 1438494951u, 507436733u, 667183474u, 2084369203u, 3854939912u, 1413396341u, 126024219u, 146044391u, 1016656857u, 3022024459u, 3254014218u, 429095991u, 990500595u, 3056862311u, 985653208u, 1718653828u, 623071693u, 366414107u, 1771289760u, 2293458109u, 3047342438u, 2991127487u, 3120876698u, 1684583131u, 3638043310u, 1170404994u, 863214540u, 1087193030u, 199124911u, 520792961u, 3169775996u, 1577421232u, 3331828431u, 1013201099u, 1716848157u, 4033596884u, 1770708857u, 4229339322u, 1146169032u, 1434258493u, 3824360466u, 3242407770u, 1926419493u, 2649785113u, 872586426u, 762243036u, 2736953692u, 816692935u, 1571283333u, 3555213933u, 2266795890u, 3781899767u, 4290630595u, 517646945u, 3006163611u, 2180594090u, 959214578u, 558910384u, 1283799121u, 3047062993u, 3830962609u, 2391606125u, 3544509313u, 622325861u, 834785312u, 382936554u, 1421463872u, 788479970u, 1825135056u, 2725923798u, 580988377u, 2826990641u, 247825043u, 3167748333u, 812546227u, 2506885666u, 2584372201u, 1758123094u, 1891789696u, 389974094u, 345313518u, 2022370576u, 3886113119u, 3338548567u, 1083486947u, 2583576230u, 1776047957u, 1771384107u, 3604937815u, 3198590202u, 3027522813u, 4155628142u, 4232136669u, 427759438u, 4244322689u, 542201663u, 1549591985u, 2856634168u, 556609672u, 45845311u, 1175961330u, 3948351189u, 4165739882u, 4194218315u, 1634635545u, 4151937410u, 713127376u, 1467786451u, 1327394015u, 2743592929u, 2638154051u, 810082938u, 3077742128u, 1062268187u, 4084325664u, 3810665822u, 3735739145u, 2794294783u, 2335576331u, 2560479831u, 690240711u, 997658837u, 2442302747u, 3948961926u, 3958366652u, 3067277639u, 2059157774u, 1211737169u, 1516711748u, 2339636583u, 4188504038u, 59581167u, 2767897792u, 1389679610u, 2658147000u, 2643979752u, 3758739543u, 4189944477u, 1454470782u, 100876854u, 2995362413u, 118817200u, 3252925478u, 2062343506u, 2804483644u, 3088828656u, 1231633714u, 4168280671u, 2931588131u, 3284356565u, 1255909792u, 3130054947u, 4173605289u, 1407328702u, 1677744031u, 3532596884u, 3162657845u, 3887208531u, 2256541290u, 3459463480u, 3740979556u, 259034107u, 392987633u, 3233195759u, 3606709555u, 3424793077u, 315836068u, 3200749877u, 4065431359u, 760633989u, 2982018998u, 1811050648u, 234531934u, 1115203611u, 3897494162u, 1516407838u, 1603559457u, 323296368u, 2632963283u, 1778459926u, 2879836826u, 2146672889u, 3486330348u, 492621815u, 1231665285u, 2457048126u, 3438440082u, 2217471853u, 3355404249u, 3275550588u, 1052645068u, 862072556u, 4110617119u, 3745267835u, 2657392572u, 4279236653u, 1688445808u, 701920051u, 956734128u, 581695350u, 3157862788u, 2585726058u, 1192588249u, 1410111809u, 1651193125u, 3326135446u, 1073280453u, 97376972u, 2513844237u, 2187968410u, 3976859649u, 4267859263u, 3429034542u, 564493077u, 3000537321u, 479241367u, 3845637831u, 2868987960u, 51544337u, 1029173765u, 393624922u, 704325635u, 2357610553u, 1418509533u, 2007814586u, 3866658271u, 3082385053u, 735688735u, 916110004u, 3283299459u, 1051684175u, 1083796807u, 4074716319u, 813690332u, 144264390u, 1439630796u, 1508556987u, 675582689u, 3748881891u, 3195309868u, 362884708u, 1616408198u, 43233176u, 837301135u, 881504822u, 3254795114u, 1385506591u, 2799925823u, 1469874582u, 3464841997u, 497175391u, 3929484338u, 3975771289u, 1798536177u, 2926265846u, 1374242438u, 3675707838u, 4205965408u, 3153165629u, 1499475160u, 187287713u, 548490821u, 3255259608u, 4247675634u, 1940181471u, 3779953975u, 687167150u, 2319566715u, 1742785722u, 785893184u, 2296977392u, 2778575413u, 1794720651u, 48131484u, 4084891412u, 1160134629u, 3737623280u, 823113169u, 3423207646u, 3803213486u, 710625654u, 4116162021u, 3693420287u, 4167766971u, 1666602807u, 295320990u, 3513255468u, 2487440590u, 234080704u, 4004655503u, 2971762528u, 1479656873u, 4090178629u, 4044418876u, 391947536u, 1462554406u, 3909295855u, 1239580330u, 1515601363u, 2011102035u, 1442068334u, 4265993528u, 1191921695u, 2291355695u, 4257172787u, 576405853u, 314332944u, 4038839101u, 55559918u, 2378985842u, 711098718u, 2425317635u, 1644327317u, 1401013391u, 4193760037u, 2958260436u, 3167371443u, 3062418115u, 3800755475u, 3167030094u, 3489648204u, 1405430357u, 526177822u, 2602636307u, 915406019u, 4264167741u, 1484090483u, 3070944737u, 254529415u, 4017058800u, 1702710265u, 1029665228u, 2000382906u, 3185573940u, 1381258384u, 4036354071u, 2900841028u, 2670703363u, 2921748807u, 2899069938u, 4130543625u, 688472265u, 4186808827u, 1054670286u, 1132985391u, 2840525968u, 4175776103u, 338058159u, 1735964501u, 1539305024u, 3497121710u, 1568260669u, 2227290760u, 146827036u, 3977176001u, 4060134777u, 857488494u, 250055052u, 4284109679u, 2502815838u, 2592281721u, 1603444633u, 1390562014u, 1556658131u, 616327404u, 2448966429u, 3051191726u, 3891353218u, 1213304082u, 762328245u, 2239052397u, 1082330589u, 2455957292u, 201837927u, 405397452u, 3079886794u, 2583939798u, 2848283092u, 3750724631u, 883849006u, 3204198988u, 3341327098u, 1855234968u, 1982110346u, 1485529487u, 541496720u, 4117290321u, 3607433551u, 2168864636u, 133643215u, 1055817409u, 3847827123u, 2960769387u, 4046101649u, 1176127003u, 4015671361u, 4243643405u, 2849988118u, 517111221u, 1796672358u, 2045051700u, 3452457457u, 2948254999u, 2102063419u, 1556410577u, 1536380876u, 3776661467u, 3281002516u, 1735616066u, 1539151988u, 1087795162u, 3332431596u, 685631442u, 1147951686u, 95237878u, 2005032160u, 4012206915u, 4224354805u, 3204999386u, 2415262714u, 1433635018u, 116647396u, 83167836u, 2881562655u, 2729416454u, 1029284767u, 881378302u, 2159170082u, 555057366u, 1169104445u, 3963877000u, 1919171906u, 336034862u, 2017579106u, 4059340529u, 3020819343u, 865146997u, 2473524405u, 944743644u, 1694443528u, 1804513294u, 2904752429u, 617975720u, 3671562289u, 260177668u, 505662155u, 1885941445u, 2504509403u, 2260041112u, 1019936943u, 3722741628u, 1511077569u, 3100701179u, 1379422864u, 1535670711u, 773792826u, 1103819072u, 2089123665u, 1157547425u, 329152940u, 4142587430u, 484732447u, 2475035432u, 1120017626u, 412145504u, 965125959u, 324924679u, 2809286837u, 2842141483u, 4029205195u, 2974306813u, 515627448u, 3791551981u, 1097806406u, 3873078673u, 136118734u, 1872130856u, 3632422367u, 3574135531u, 4017075736u, 1699452298u, 1403506686u, 344414660u, 1189129691u, 3487080616u, 1516736273u, 1805475756u, 2562064338u, 163335594u, 2732147834u, 4077452507u, 2984955003u, 4271866024u, 3071338162u, 2347111903u, 873829983u, 1948409509u, 1923531348u, 459509140u, 771592405u, 1750124750u, 2334938333u, 213811117u, 2586632018u, 185232757u, 4032960199u, 2447383637u, 284777551u, 1654276320u, 2687561076u, 3512945009u, 308584855u, 1861027147u, 4102279334u, 3203802620u, 1692079268u, 4250142168u, 2565680167u, 1507046104u, 841195925u, 520565830u, 3674576684u, 38924274u, 3770488806u, 2414430882u, 3978473838u, 3703994407u, 69201295u, 3099963860u, 1255084262u, 690971838u, 3539996781u, 3696902571u, 3593730713u, 2363435042u, 54945052u, 1785765213u, 184911581u, 1586241476u, 1939595371u, 2534883189u, 2432427547u, 2374171993u, 2039128933u, 2955715987u, 2295501078u, 2741583197u, 1280920000u, 686818699u, 1238742497u, 3843660102u, 82177963u, 1281043691u, 1121403845u, 1697846708u, 284852964u, 278661677u, 2889101923u, 2127558730u, 713121337u, 872502474u, 511142139u, 1261140657u, 1747052377u, 2108187161u, 927011680u, 955328267u, 3821994995u, 2707230761u, 4142246789u, 4134691985u, 1958963937u, 2498463509u, 1977988705u, 1419293714u, 1636932722u, 2567532373u, 4075249328u, 240575705u, 1956681213u, 2598802768u, 2025886508u, 4104757832u, 3026358429u, 3242615202u, 4026813725u, 255108733u, 1845587644u, 3573008472u, 3615577014u, 1222733548u, 1205557630u, 917608574u, 1363253259u, 1541946015u, 3087190425u, 1138008081u, 1444019663u, 109793386u, 341851980u, 857839960u, 2515339233u, 156283211u, 1906768669u, 3886713057u, 1276595523u, 2809830736u, 460237542u, 3420452099u, 142985419u, 205970448u, 4198897105u, 1950698961u, 2069753399u, 1142216925u, 1113051162u, 1033680610u, 4278599955u, 1106466069u, 356742959u, 531521052u, 3494863964u, 225629455u, 3735275001u, 3662626864u, 1750561299u, 1012864651u, 2101846429u, 1074553219u, 668829411u, 992181339u, 3384018814u, 3330664522u, 860966321u, 1885071395u, 4233785523u, 100741310u, 451656820u, 2148187612u, 1063001151u, 360256231u, 107312677u, 3650357479u, 2390172694u, 22452685u, 237319043u, 3600462351u, 1216645846u, 2088767754u, 164402616u, 2418980170u, 926137824u, 94638678u, 1689811113u, 2751052984u, 1767810825u, 271289013u, 3896132233u, 103797041u, 1397772514u, 3441135892u, 3323383489u, 2491268371u, 1662561885u, 1612872497u, 2986430557u, 2756998822u, 207428029u, 937973965u, 2791656726u, 1949717207u, 2260498180u, 2648427775u, 2360400900u, 2080496169u, 486358863u, 1582022990u, 1263709570u, 1396468647u, 1377764574u, 363008508u, 1293502429u, 224580012u, 4252610345u, 1435134775u, 1099809675u, 533671980u, 1533438766u, 1820532305u, 2776960536u, 3374512975u, 3542220540u, 822810075u, 3716663290u, 1157398049u, 3752806924u, 4081637863u, 337070226u, 3866585976u, 359270190u, 2110942730u, 3267551635u, 644850146u, 1306761320u, 746972907u, 934259457u, 2341378668u, 2220373824u, 1242645122u, 4109252858u, 1625266099u, 1173698481u, 383517064u, 896322512u, 3377483696u, 1788337208u, 455496839u, 3194373887u, 1837689083u, 1336556841u, 1658628529u, 2911512007u, 3838343487u, 2757664765u, 1537187340u, 3712582785u, 367022558u, 3071359622u, 3926147070u, 35432879u, 3093195926u, 2561488770u, 4273132307u, 3898950547u, 2838251049u, 2103926083u, 2549435227u, 536047554u, 1858986613u, 2040551642u, 1147412575u, 1972369852u, 4166184983u, 3528794619u, 4077477194u, 3565689036u, 808048238u, 3826350461u, 1359641525u, 1197100813u, 265993036u, 1864569342u, 725164342u, 2264788336u, 1831223342u, 3329594980u, 923017956u, 490608221u, 3818634478u, 258154469u, 1441714797u, 1174785921u, 3833372385u, 3287246572u, 1677395563u, 3569218731u, 868981704u, 2163330264u, 2649450292u, 500120236u, 465161780u, 746438382u, 1145009669u, 2520062970u, 2810524030u, 1561519055u, 1479878006u, 3864969305u, 2686075657u, 4042710240u, 3224066062u, 2774151984u, 2226179547u, 1643626042u, 2328730865u, 3160666939u, 2107011431u, 96459446u, 3920328742u, 3336407558u, 829404209u, 1878067032u, 1235983679u, 4237425634u, 466519055u, 3870676863u, 934312076u, 2952135524u, 276949224u, 4100839753u, 424001484u, 1955120893u, 4015478120u, 1265237690u, 427484362u, 4246879223u, 3452969617u, 1724363362u, 1553513184u, 834830418u, 1858777639u, 3476334357u, 4144030366u, 2450047160u, 2950762705u, 4277111759u, 358032121u, 2511026735u, 167923105u, 2059208280u, 251949572u, 3065234219u, 1535473864u, 556796152u, 1513237478u, 3150857516u, 1103404394u, 198182691u, 1476438092u, 2913077464u, 207119516u, 3963810232u, 2954651680u, 1535115487u, 3051522276u, 4046477658u, 917804636u, 864395565u, 632704095u, 140762681u, 1802040304u, 990407433u, 3771506212u, 4106024923u, 1287729497u, 2198985327u, 4052924496u, 2926590471u, 3084557148u, 1472898694u, 1009870118u, 559702706u, 4265214507u, 82077489u, 3067891003u, 3295678907u, 2402308151u, 1096697687u, 464407878u, 4190838199u, 4269578403u, 3060919438u, 2899950405u, 3046872820u, 733509243u, 1583801700u, 40453902u, 3879773881u, 1993425202u, 2185339100u, 1877837196u, 3912423882u, 3293122640u, 4104318469u, 1679617763u, 3703603898u, 8759461u, 2540185277u, 1152198475u, 2038345882u, 2503579743u, 1446869792u, 2019419351u, 4051584612u, 3178289407u, 3992503830u, 2879018745u, 2719373510u, 700836153u, 1675560450u, 4121245793u, 2064715719u, 343595772u, 1996164093u, 3130433948u, 405251683u, 2804817126u, 1607133689u, 463852893u, 2864244470u, 2224044848u, 4071581802u, 2537107938u, 2246347953u, 3207234525u, 2028708916u, 2272418128u, 803575837u, 38655481u, 2170452091u, 3272166407u, 557660441u, 4019147902u, 3841480082u, 298459606u, 2600943364u, 2440657523u, 255451671u, 3424361375u, 779434428u, 3088526123u, 490671625u, 1322855877u, 3452203069u, 3057021940u, 2285701422u, 2014993457u, 2390431709u, 2002090272u, 1568745354u, 1783152480u, 823305654u, 4053862835u, 2200236540u, 3009412313u, 3184047862u, 3032187389u, 4159715581u, 2966902888u, 252986948u, 1849329144u, 3160134214u, 3420960112u, 3198900547u, 749160960u, 379139040u, 1208883495u, 1566527339u, 3006227299u, 4194096960u, 556075248u, 497404038u, 1717327230u, 1496132623u, 1775955687u, 1719108984u, 1014328900u, 4189966956u, 2108574735u, 2584236470u, 684087286u, 531310503u, 4264509527u, 773405691u, 3088905079u, 3456882941u, 3105682208u, 3382290593u, 2289363624u, 3296306400u, 4168438718u, 467441309u, 777173623u, 3241407531u, 1183994815u, 1132983260u, 1610606159u, 2540270567u, 2649684057u, 1397502982u, 146657385u, 3318434267u, 2109315753u, 3348545480u, 3193669211u, 811750340u, 1073256162u, 3571673088u, 546596661u, 1017047954u, 3403136990u, 2540585554u, 1477047647u, 4145867423u, 2826408201u, 3531646869u, 784952939u, 943914610u, 2717443875u, 3657384638u, 1806867885u, 1903578924u, 3985088434u, 1911188923u, 1764002686u, 3672748083u, 1832925325u, 241574049u, 519948041u, 3181425568u, 2939747257u, 1634174593u, 3429894862u, 3529565564u, 1089679033u, 240953857u, 2025369941u, 2695166650u, 517086873u, 2964595704u, 3017658263u, 3828377737u, 2144895011u, 994799311u, 1184683823u, 4260564140u, 308018483u, 4262383425u, 1374752558u, 3431057723u, 1572637805u, 383233885u, 3188015819u, 4051263539u, 233319221u, 3794788167u, 2017406667u, 919677938u, 4074952232u, 1683612329u, 4213676186u, 327142514u, 3032591014u, 4204155962u, 206775997u, 2283918569u, 2395147154u, 3427505379u, 2211319468u, 4153726847u, 2217060665u, 350160869u, 2493667051u, 1648200185u, 3441709766u, 1387233546u, 140980u, 1891558063u, 760080239u, 2088061981u, 1580964938u, 740563169u, 422986366u, 330624974u, 4264507722u, 150928357u, 2738323042u, 2948665536u, 918718096u, 376390582u, 3966098971u, 717653678u, 3219466255u, 3799363969u, 3424344721u, 3187805406u, 375347278u, 3490350144u, 1992212097u, 2263421398u, 3855037968u, 1928519266u, 3866327955u, 1129127000u, 1782515131u, 2746577402u, 3059200728u, 2108753646u, 2738070963u, 1336849395u, 1705302106u, 768287270u, 1343511943u, 2247006571u, 1956142255u, 1780259453u, 3475618043u, 212490675u, 622521957u, 917121602u, 1852992332u, 1267987847u, 3170016833u, 2549835613u, 3299763344u, 2864033668u, 3378768767u, 1236609378u, 4169365948u, 3738062408u, 2661022773u, 2006922227u, 2760592161u, 3828932355u, 2636387819u, 2616619070u, 1237256330u, 3449066284u, 2871755260u, 3729280948u, 3862686086u, 431292293u, 3285899651u, 786322314u, 2531158535u, 724901242u, 2377363130u, 1415970351u, 1244759631u, 3263135197u, 965248856u, 174024139u, 2297418515u, 2954777083u, 987586766u, 3206261120u, 4059515114u, 3903854066u, 1931934525u, 2287507921u, 1827135136u, 1781944746u, 574617451u, 2299034788u, 2650140034u, 4081586725u, 2482286699u, 1109175923u, 458483596u, 618705848u, 4059852729u, 1813855658u, 4190721328u, 1129462471u, 4089998050u, 3575732749u, 2375584220u, 1037031473u, 1623777358u, 3389003793u, 546597541u, 352770237u, 1383747654u, 3122687303u, 1646071378u, 1164309901u, 290870767u, 830691298u, 929335420u, 3193251135u, 989577914u, 3626554867u, 591974737u, 3996958215u, 3163711272u, 3071568023u, 1516846461u, 3656006011u, 2698625268u, 2510865430u, 340274176u, 1167681812u, 3698796465u, 3155218919u, 4102288238u, 1673474350u, 3069708839u, 2704165015u, 1237411891u, 1854985978u, 3646837503u, 3625406022u, 921552000u, 1712976649u, 3939149151u, 878608872u, 3406359248u, 1068844551u, 1834682077u, 4155949943u, 2437686324u, 3163786257u, 2645117577u, 1988168803u, 747285578u, 1626463554u, 1235300371u, 1256485167u, 1914142538u, 4141546431u, 3838102563u, 582664250u, 1883344352u, 2083771672u, 2611657933u, 2139079047u, 2250573853u, 804336148u, 3066325351u, 2770847216u, 4275641370u, 1455750577u, 3346357270u, 1674051445u, 601221482u, 3992583643u, 1402445097u, 3622527604u, 2509017299u, 2966108111u, 2557027816u, 900741486u, 1790771021u, 2912643797u, 2631381069u, 4014551783u, 90375300u, 300318232u, 3269968032u, 2679371729u, 2664752123u, 3517585534u, 3253901179u, 542270815u, 1188641600u, 365479232u, 2210121140u, 760762191u, 1273768482u, 1216399252u, 3484324231u, 4287337666u, 16322182u, 643179562u, 325675502u, 3652676161u, 3120716054u, 3330259752u, 1011990087u, 2990167340u, 1097584090u, 3262252593u, 1829409951u, 3665087267u, 1214854475u, 2134299399u, 3704419305u, 411263051u, 1625446136u, 549838529u, 4283196353u, 1342880802u, 3460621305u, 1967599860u, 4282843369u, 1275671016u, 2544665755u, 853593042u, 901109753u, 2682611693u, 110631633u, 797487791u, 1472073141u, 850464484u, 797089608u, 3286110054u, 350397471u, 2775631060u, 366448238u, 3842907484u, 2219863904u, 3623364733u, 1850985302u, 4009616991u, 294963924u, 3693536939u, 3061255808u, 1615375832u, 1920066675u, 4113028420u, 4032223840u, 2318423400u, 2701956286u, 4145497671u, 3991532344u, 2536338351u, 1679099863u, 1728968857u, 449740816u, 2686506989u, 685242457u, 97590863u, 3258354115u, 1502282913u, 1235084019u, 2151665147u, 528459289u, 231097464u, 2477280726u, 3651607391u, 2091754612u, 1178454681u, 980597335u, 1604483865u, 1842333726u, 4146839064u, 3213794286u, 2601416506u, 754220096u, 3571436033u, 488595746u, 1448097974u, 4004834921u, 238887261u, 3320337489u, 1416989070u, 2928916831u, 4093725287u, 186020771u, 2367569534u, 3046087671u, 4090084518u, 3548184546u, 679517009u, 1962659444u, 3539886328u, 4192003933u, 1678423485u, 3827951761u, 3086277222u, 2144472852u, 1390394371u, 2976322029u, 1574517163u, 3553313841u, 119173722u, 1702434637u, 1766260771u, 3629581771u, 1407497759u, 895654784u, 751439914u, 4008409498u, 215917713u, 1482103833u, 695551833u, 1288382231u, 2656990891u, 2581779077u, 1570750352u, 3710689053u, 1741390464u, 2666411616u, 3533987737u, 4289478316u, 3576119563u, 4118694920u, 108199666u, 3869794273u, 963183826u, 2081410737u, 3796810515u, 791123882u, 2525792704u, 1036883117u, 136547246u, 875691100u, 2592925324u, 614302599u, 3013176417u, 2689342539u, 427154472u, 532957601u, 1228758574u, 1898117151u, 1181643858u, 1908591042u, 1464255968u, 446980910u, 2984611177u, 58509511u, 1046943619u, 3508927906u, 2001585786u, 2544767379u, 1525438381u, 552181222u, 1959725830u, 879448844u, 1348536411u, 4242243590u, 2861338018u, 1082052441u, 1034351453u, 601175800u, 764077711u, 530635011u, 3785343245u, 2178026726u, 117256687u, 2378297261u, 457568934u, 76438221u, 4104954272u, 956793873u, 3783168634u, 2485968477u, 2381948487u, 4226929450u, 3148473363u, 2518273601u, 3569490233u, 879369091u, 2180270337u, 3674375989u, 1387729170u, 977997984u, 4270646856u, 568650985u, 951677556u, 4213877384u, 2721005055u, 1073364549u, 2563403831u, 1678669911u, 66786703u, 2273631661u, 1149351924u, 3651298990u, 1581883443u, 246723096u, 1895026827u, 3810605772u, 3711056516u, 4058833288u, 2193790614u, 2080120290u, 3638638708u, 2915672708u, 2263003308u, 2361934197u, 4136767460u, 1976115991u, 3448840877u, 2019238520u, 225333538u, 874340815u, 2976159827u, 1555273378u, 3797521928u, 1942347150u, 3262952567u, 435997738u, 340403353u, 2817830907u, 2078619498u, 749534111u, 1178073973u, 894654712u, 3361226032u, 841092198u, 3288261538u, 1696412169u, 1496966875u, 697501571u, 1059158875u, 3739946319u, 2481012988u, 568983526u, 114945840u, 1559249010u, 2218244008u, 2841706923u, 1632780103u, 4020169654u, 2087949619u, 2438736103u, 24032648u, 833416317u, 3787017905u, 2373238993u, 2575395164u, 3434544481u, 3228481067u, 2542976862u, 2971726178u, 2880371864u, 3642087909u, 2407477975u, 2239080836u, 1043714217u, 3894199764u, 2235879182u, 203853421u, 2933669448u, 2504940536u, 834683330u, 425935223u, 3560796393u, 3565833278u, 1668000829u, 3683399154u, 3414330886u, 1748785729u, 1023171602u, 580966986u, 2531038985u, 3227325488u, 2657385925u, 2124704694u, 233442446u, 1107045577u, 3407293834u, 552770757u, 3899097693u, 1067532701u, 115667924u, 1406028344u, 1707768231u, 3724015962u, 2419657149u, 18613994u, 2532882091u, 3476683808u, 1560838678u, 811220224u, 895961699u, 3762914298u, 1328752423u, 1844996900u, 1420427894u, 1848067707u, 1210281744u, 904215228u, 4055325594u, 1118521573u, 2496554183u, 2579259919u, 3996647489u, 3657647605u, 325254059u, 3136157065u, 3951522674u, 4052925250u, 3341068436u, 2287683323u, 1313073005u, 126005630u, 2505120084u, 1194725057u, 853746559u, 3555092974u, 2689238752u, 49515858u, 1244776042u, 1069300695u, 61073168u, 1010661841u, 1269521335u, 1902040126u, 990632502u, 2378708922u, 3858321250u, 1400735275u, 2974699176u, 2771676666u, 170995186u, 2877798589u, 545726212u, 2225229957u, 1086473152u, 3454177594u, 3859483262u, 1499729584u, 2088002891u, 2883475137u, 3222194252u, 4144472319u, 2212229854u, 4146740722u, 567988835u, 1051332394u, 3932046135u, 542648229u, 3017852446u, 1277887997u, 162888005u, 1669710469u, 1492500905u, 553041029u, 1434876932u, 533989516u, 3817492747u, 584127807u, 4147115982u, 2993670925u, 4020312558u, 710021255u, 3509733475u, 3587959456u, 2088550465u, 1745399498u, 2952242967u, 1259815443u, 869648362u, 1404723176u, 3947542735u, 1334333531u, 3873471582u, 229399758u, 59634866u, 3239516985u, 3844250972u, 1275954779u, 492891666u, 1029533080u, 1552951157u, 367320647u, 699480890u, 3684418197u, 3707014310u, 471105777u, 1824587258u, 4030809053u, 3489914436u, 484559105u, 1235750398u, 1428453396u, 4230459084u, 4255931645u, 1848597055u, 4271715616u, 331780381u, 482425775u, 2435323270u, 3171911678u, 3507210587u, 928543347u, 4197807526u, 3680046204u, 2766042024u, 2159512867u, 179373257u, 313902234u, 4024837592u, 294795361u, 1622282562u, 647086234u, 2825039429u, 577214736u, 4043412446u, 2426981244u, 1277736097u, 1130129573u, 2601395338u, 995791646u, 36668922u, 3344746679u, 1521532225u, 1645086060u, 2622763015u, 4122335794u, 2936887705u, 494465807u, 2580840343u, 1064648931u, 1247887787u, 2752145076u, 1277612417u, 1249660507u, 2288678613u, 3312498873u, 2459273912u, 4238535494u, 3117488020u, 2571979978u, 2680188909u, 1471227427u, 1616494033u, 633688562u, 2268653416u, 3268237290u, 3021962815u, 1959779970u, 3321382074u, 766642813u, 204429780u, 1323319858u, 3676032891u, 1380896111u, 4030639049u, 3647601207u, 1830028502u, 2830263774u, 1375962216u, 1733961041u, 939765180u, 521947915u, 3903267364u, 497472767u, 1619700946u, 189164145u, 3115593885u, 486382294u, 1262445920u, 4062496162u, 2464795849u, 3770038872u, 4032121374u, 3235740744u, 3757765258u, 1777199847u, 2167243108u, 1912506671u, 4180515317u, 2276864677u, 536034089u, 2384915026u, 162938278u, 1588060152u, 4018349945u, 2504457929u, 841450426u, 2790120722u, 2719983588u, 1471020554u, 1390856732u, 3623212998u, 2506944218u, 1035080801u, 348812127u, 3026631806u, 746483541u, 2342164722u, 122104390u, 4074122771u, 3986865419u, 1674890530u, 3693306023u, 3011542850u, 1294951725u, 899303190u, 3577146915u, 3549160092u, 1241677652u, 4290680005u, 3193053279u, 2029187390u, 3298063095u, 3943068002u, 3946220635u, 2273781461u, 889053698u, 1376304022u, 1486839612u, 2127663659u, 344127443u, 1646681121u, 2780117810u, 2142045764u, 2694572773u, 447810651u, 2185527146u, 2366308558u, 290335413u, 584901173u, 2012370276u, 970504950u, 3258236042u, 2008155560u, 3945579565u, 614796295u, 24452072u, 2695940969u, 3983727134u, 3444688454u, 1327044473u, 3545633451u, 1875293322u, 1739318893u, 1707527799u, 2683090634u, 2848082386u, 2814622471u, 4111401777u, 2774816580u, 3849839194u, 437560100u, 2238350150u, 2462124836u, 665017710u, 512012738u, 2945294779u, 3305170944u, 819477765u, 59419271u, 155125658u, 665292744u, 444722813u, 3580039116u, 2355675635u, 663735032u, 3247800169u, 1579404983u, 1985115003u, 3397891494u, 358696453u, 1474896279u, 516388613u, 710590371u, 3490497111u, 2514565805u, 2386143445u, 477509654u, 412854590u, 3624609754u, 3214388668u, 3516075816u, 2731288520u, 1369482895u, 4033204378u, 1314000850u, 829769325u, 1935166880u, 1608191643u, 2607067237u, 423820371u, 3257747610u, 1355298041u, 3776931214u, 4105054901u, 2107080812u, 1911521879u, 3183054185u, 3910177801u, 675129307u, 1209358971u, 4205727791u, 1435726287u, 3333261712u, 1400982708u, 1154611403u, 1663501483u, 2837596667u, 3164734053u, 2759854023u, 4012043629u, 1963228038u, 3981675284u, 2677557877u, 520119591u, 505138315u, 897271356u, 1803966773u, 1016663294u, 616691903u, 2254742522u, 4032705384u, 2468470796u, 798395739u, 3025169002u, 3570037122u, 1461093710u, 3473799845u, 3702624858u, 476400898u, 1043039728u, 2304070437u, 181576948u, 602972493u, 3996616030u, 3289878097u, 2068516226u, 3922247304u, 1299968266u, 2520311409u, 1968824721u, 3214794876u, 1581813122u, 2668800905u, 3297613974u, 748160407u, 1145536484u, 1326769504u, 2973323521u, 3775262814u, 3218653169u, 902775872u, 3498603433u, 1372805534u, 704686363u, 3626542352u, 2271580579u, 1213925114u, 46329775u, 3009384989u, 1330254048u, 1194824134u, 514204310u, 3781981134u, 442526164u, 2835608783u, 3460471867u, 510634034u, 546406434u, 2716786748u, 2840500021u, 1669490957u, 2536189149u, 3251421224u, 1358736072u, 1089334066u, 3260749330u, 250756920u, 2974806681u, 1513718866u, 82635635u, 4041016629u, 3391765744u, 2495807367u, 3962674316u, 2822889695u, 753413337u, 2008251381u, 3123390177u, 106212622u, 490570565u, 1684884205u, 793892547u, 1927268995u, 2344148164u, 2251978818u, 437424236u, 2774023200u, 2674940754u, 3788056262u, 2597882666u, 3678660147u, 3797434193u, 3838215866u, 279687080u, 2656772270u, 2190204787u, 1997584981u, 3384401882u, 3160208845u, 3629379425u, 2668998785u, 1050036757u, 2954162084u, 917091826u, 1744374041u, 1454282570u, 845687881u, 2997173625u, 776018378u, 1137560602u, 1938378389u, 1748082354u, 2066910012u, 2677675207u, 918315064u, }; // Return false only if offset is -1 and a spot check of 3 hashes all yield 0. bool Test(int offset, int len = 0) { #undef Check #undef IsAlive #define Check(x) do { \ const uint32_t actual = (x), e = expected[index++]; \ bool ok = actual == e; \ if (!ok) { \ cerr << "expected " << hex << e << " but got " << actual << endl; \ ++errors; \ } \ assert(ok); \ } while (0) #define IsAlive(x) do { alive += IsNonZero(x); } while (0) // After the following line is where the uses of "Check" and such will go. static int index = 0; if (offset == -1) { int alive = 0; { uint64_t h = farmhashte::Hash64WithSeeds(data, len++, SEED0, SEED1); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } { uint64_t h = farmhashte::Hash64WithSeed(data, len++, SEED); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } { uint64_t h = farmhashte::Hash64(data, len++); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } len -= 3; return alive > 0; } { uint64_t h = farmhashte::Hash64WithSeeds(data + offset, len, SEED0, SEED1); Check(h >> 32); Check((h << 32) >> 32); } { uint64_t h = farmhashte::Hash64WithSeed(data + offset, len, SEED); Check(h >> 32); Check((h << 32) >> 32); } { uint64_t h = farmhashte::Hash64(data + offset, len); Check(h >> 32); Check((h << 32) >> 32); } return true; #undef Check #undef IsAlive } int RunTest() { Setup(); int i = 0; cout << "Running farmhashteTest"; if (!Test(-1)) { cout << "... Unavailable\n"; return NoteErrors(); } // Good. The function is attempting to hash, so run the full test. int errors_prior_to_test = errors; for ( ; i < kTestSize - 1; i++) { Test(i * i, i); } for ( ; i < kDataSize; i += i / 7) { Test(0, i); } Test(0, kDataSize); cout << (errors == errors_prior_to_test ? "... OK\n" : "... Failed\n"); return NoteErrors(); } #else // After the following line is where the code to print hash codes will go. void Dump(int offset, int len) { { uint64_t h = farmhashte::Hash64WithSeeds(data + offset, len, SEED0, SEED1); cout << (h >> 32) << "u, " << ((h << 32) >> 32) << "u," << endl; } { uint64_t h = farmhashte::Hash64WithSeed(data + offset, len, SEED); cout << (h >> 32) << "u, " << ((h << 32) >> 32) << "u," << endl; } { uint64_t h = farmhashte::Hash64(data + offset, len); cout << (h >> 32) << "u, " << ((h << 32) >> 32) << "u," << endl; } } #endif #undef SEED #undef SEED1 #undef SEED0 } // namespace farmhashteTest #if TESTING static int farmhashteTestResult = farmhashteTest::RunTest(); #else int main(int argc, char** argv) { Setup(); cout << "uint32_t expected[] = {\n"; int i = 0; for ( ; i < kTestSize - 1; i++) { farmhashteTest::Dump(i * i, i); } for ( ; i < kDataSize; i += i / 7) { farmhashteTest::Dump(0, i); } farmhashteTest::Dump(0, kDataSize); cout << "};\n"; } #endif #ifndef FARMHASH_SELF_TEST_GUARD #define FARMHASH_SELF_TEST_GUARD #include #include #include using std::cout; using std::cerr; using std::endl; using std::hex; static const uint64_t kSeed0 = 1234567; static const uint64_t kSeed1 = k0; static const int kDataSize = 1 << 20; static const int kTestSize = 300; #define kSeed128 Uint128(kSeed0, kSeed1) static char data[kDataSize]; static int completed_self_tests = 0; static int errors = 0; // Initialize data to pseudorandom values. void Setup() { if (completed_self_tests == 0) { uint64_t a = 9; uint64_t b = 777; for (int i = 0; i < kDataSize; i++) { a += b; b += a; a = (a ^ (a >> 41)) * k0; b = (b ^ (b >> 41)) * k0 + i; uint8_t u = b >> 37; memcpy(data + i, &u, 1); // uint8_t -> char } } } int NoteErrors() { #define NUM_SELF_TESTS 9 if (++completed_self_tests == NUM_SELF_TESTS) std::exit(errors > 0); return errors; } template inline bool IsNonZero(T x) { return x != 0; } template <> inline bool IsNonZero(uint128_t x) { return x != Uint128(0, 0); } #endif // FARMHASH_SELF_TEST_GUARD namespace farmhashuoTest { uint32_t CreateSeed(int offset, int salt) { uint32_t h = static_cast(salt & 0xffffffff); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h += static_cast(offset & 0xffffffff); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); return h; } #undef SEED #undef SEED1 #undef SEED0 #define SEED CreateSeed(offset, -1) #define SEED0 CreateSeed(offset, 0) #define SEED1 CreateSeed(offset, 1) #undef TESTING #define TESTING 1 #if TESTING uint32_t expected[] = { 3277735313u, 2681724312u, 2598464059u, 797982799u, 2603993599u, 921001710u, 1410420968u, 2134990486u, 2914424215u, 2244477846u, 255297188u, 2992121793u, 161451183u, 3943596029u, 4019337850u, 452431531u, 3379021470u, 2557197665u, 299850021u, 2532580744u, 1298374911u, 3099673830u, 2199864459u, 3696623795u, 4055299123u, 3281581178u, 1053458494u, 1882212500u, 3456121707u, 275903667u, 458884671u, 3033004529u, 1898235244u, 1402319660u, 2700149065u, 2699376854u, 2433714046u, 4222949502u, 4220361840u, 1712034059u, 4148372108u, 1330324210u, 594028478u, 2921867846u, 780716741u, 1728752234u, 3280331829u, 326029180u, 393215742u, 3349570000u, 3824583307u, 1612122221u, 1379537552u, 1646032583u, 2233466664u, 1432476832u, 2052294713u, 3552092450u, 1628777059u, 1499109081u, 2960536756u, 1554038301u, 1145519619u, 3190844552u, 237495366u, 540224401u, 65721842u, 489963606u, 1596489240u, 1562872448u, 1790705123u, 2128624475u, 1435705557u, 1262831810u, 155445229u, 1672724608u, 663607706u, 2077310004u, 3610042449u, 1911523866u, 2563776023u, 294527927u, 1099072299u, 1389770549u, 2952353448u, 2026137563u, 3603803785u, 629449419u, 226132789u, 2489287368u, 1552847036u, 645684964u, 187883449u, 230403464u, 3151491850u, 3272648435u, 2002861219u, 165370827u, 916494250u, 1230085527u, 3807265751u, 3628174014u, 231181488u, 851743255u, 2988893883u, 1554380634u, 1142264800u, 3667013118u, 2638023604u, 2290487377u, 732137533u, 1909203251u, 1380301172u, 1498556724u, 4072067757u, 4165088768u, 3931792696u, 197618179u, 956300927u, 914413116u, 2148126371u, 1913303225u, 3074915312u, 3117299654u, 3178848746u, 1357272220u, 1438494951u, 507436733u, 3854939912u, 1413396341u, 126024219u, 146044391u, 3254014218u, 429095991u, 165589978u, 1578546616u, 623071693u, 366414107u, 249776086u, 1207522198u, 3120876698u, 1684583131u, 46987739u, 1157614300u, 199124911u, 520792961u, 3614377032u, 586863115u, 1716848157u, 4033596884u, 1164298657u, 4140791139u, 3824360466u, 3242407770u, 3725511003u, 232064808u, 2736953692u, 816692935u, 512845449u, 3748861010u, 4290630595u, 517646945u, 22638523u, 648000590u, 1283799121u, 3047062993u, 1024246061u, 4027776454u, 834785312u, 382936554u, 411505255u, 1973395102u, 580988377u, 2826990641u, 3474970689u, 1029055034u, 2584372201u, 1758123094u, 589567754u, 325737734u, 3886113119u, 3338548567u, 257578986u, 3698087965u, 3604937815u, 3198590202u, 2305332220u, 191910725u, 4244322689u, 542201663u, 3315355162u, 2135941665u, 1175961330u, 3948351189u, 23075771u, 3252374102u, 713127376u, 1467786451u, 663013031u, 3444053918u, 3077742128u, 1062268187u, 2115441882u, 4081398201u, 2335576331u, 2560479831u, 1379288194u, 4225182569u, 3958366652u, 3067277639u, 3667516477u, 1709989541u, 4188504038u, 59581167u, 2725013602u, 3639843023u, 3758739543u, 4189944477u, 2470483982u, 877580602u, 3252925478u, 2062343506u, 3981838403u, 3762572073u, 2931588131u, 3284356565u, 1129162571u, 732225574u, 1677744031u, 3532596884u, 3232041815u, 1652884780u, 3740979556u, 259034107u, 2227121257u, 1426140634u, 315836068u, 3200749877u, 1386256573u, 24035717u, 234531934u, 1115203611u, 1598686658u, 3146815575u, 2632963283u, 1778459926u, 739944537u, 579625482u, 1231665285u, 2457048126u, 3903349120u, 389846205u, 1052645068u, 862072556u, 2834153464u, 1481069623u, 1688445808u, 701920051u, 3740748788u, 3388062747u, 1192588249u, 1410111809u, 2633463887u, 4050419847u, 2513844237u, 2187968410u, 2951683019u, 3015806005u, 3000537321u, 479241367u, 252167538u, 1231057113u, 393624922u, 704325635u, 1467197045u, 2066433573u, 3082385053u, 735688735u, 956434529u, 4028590195u, 4074716319u, 813690332u, 2124740535u, 804073145u, 3748881891u, 3195309868u, 841856605u, 2585865274u, 881504822u, 3254795114u, 1241815736u, 970796142u, 497175391u, 3929484338u, 4264993211u, 1835322201u, 3675707838u, 4205965408u, 300298607u, 3858319990u, 3255259608u, 4247675634u, 1095823272u, 1197245408u, 1742785722u, 785893184u, 1702965674u, 850401405u, 4084891412u, 1160134629u, 2555998391u, 1972759056u, 710625654u, 4116162021u, 3352753742u, 85121177u, 3513255468u, 2487440590u, 2480032715u, 2287747045u, 4090178629u, 4044418876u, 1703944517u, 486290428u, 1515601363u, 2011102035u, 573985957u, 3536053779u, 4257172787u, 576405853u, 1523550693u, 1014952061u, 711098718u, 2425317635u, 3460807169u, 3688987163u, 3167371443u, 3062418115u, 3330028292u, 1713171303u, 526177822u, 2602636307u, 1245357025u, 3346699703u, 254529415u, 4017058800u, 1829738451u, 2164236533u, 1381258384u, 4036354071u, 1749181924u, 4118435443u, 4130543625u, 688472265u, 2731071299u, 2547657502u, 4175776103u, 338058159u, 3729582129u, 4181845558u, 2227290760u, 146827036u, 2459178427u, 1025353883u, 4284109679u, 2502815838u, 825124804u, 2533140036u, 616327404u, 2448966429u, 413992636u, 2334782461u, 2239052397u, 1082330589u, 3381164715u, 199381437u, 2583939798u, 2848283092u, 2300168091u, 2156336315u, 1855234968u, 1982110346u, 2482046810u, 3158163887u, 2168864636u, 133643215u, 3904021624u, 3646514568u, 1176127003u, 4015671361u, 100525019u, 3534706803u, 2045051700u, 3452457457u, 1492267772u, 2308393828u, 3776661467u, 3281002516u, 4246334524u, 743955039u, 685631442u, 1147951686u, 2040912376u, 2911148054u, 3204999386u, 2415262714u, 313209105u, 777065474u, 2729416454u, 1029284767u, 1632078298u, 1817552554u, 3963877000u, 1919171906u, 3843219958u, 3073580867u, 865146997u, 2473524405u, 2593817617u, 3643076308u, 617975720u, 3671562289u, 121812599u, 2902367378u, 2260041112u, 1019936943u, 320945955u, 2337845588u, 1535670711u, 773792826u, 3152195900u, 4090794518u, 4142587430u, 484732447u, 419191319u, 3377973345u, 324924679u, 2809286837u, 1562277603u, 1378362199u, 3791551981u, 1097806406u, 1386297408u, 2304900033u, 3574135531u, 4017075736u, 1161238398u, 1358056883u, 3487080616u, 1516736273u, 851615042u, 2927899494u, 4077452507u, 2984955003u, 3907754394u, 3578173844u, 1948409509u, 1923531348u, 3578472493u, 3710074193u, 213811117u, 2586632018u, 1922589216u, 274958014u, 1654276320u, 2687561076u, 2569061755u, 3122046057u, 3203802620u, 1692079268u, 477806878u, 140587742u, 520565830u, 3674576684u, 91246882u, 1010215946u, 3703994407u, 69201295u, 776213083u, 3677771507u, 3696902571u, 3593730713u, 2907901228u, 3239753796u, 1586241476u, 1939595371u, 2268396558u, 3468719670u, 2955715987u, 2295501078u, 2775848696u, 1358532390u, 3843660102u, 82177963u, 4094477877u, 191727221u, 278661677u, 2889101923u, 1352525614u, 2844977667u, 1261140657u, 1747052377u, 2334120653u, 645125282u, 2707230761u, 4142246789u, 1068639717u, 2288162940u, 1419293714u, 1636932722u, 3252686293u, 318543902u, 2598802768u, 2025886508u, 2250788464u, 2711763065u, 255108733u, 1845587644u, 3719270134u, 3940707863u, 917608574u, 1363253259u, 788659330u, 673256220u, 109793386u, 341851980u, 2698465479u, 3011229884u, 3886713057u, 1276595523u, 2439962760u, 2700515456u, 205970448u, 4198897105u, 875511891u, 371715572u, 1033680610u, 4278599955u, 3120038721u, 1256300069u, 225629455u, 3735275001u, 3961944123u, 1769389163u, 1074553219u, 668829411u, 1098679359u, 2573697509u, 1885071395u, 4233785523u, 2513878053u, 2030193788u, 360256231u, 107312677u, 310517502u, 2618936366u, 3600462351u, 1216645846u, 2970730323u, 4278812598u, 94638678u, 1689811113u, 4125738800u, 3103759730u, 103797041u, 1397772514u, 1669653333u, 572567964u, 1612872497u, 2986430557u, 214990655u, 3117607990u, 1949717207u, 2260498180u, 1493936866u, 3554860960u, 1582022990u, 1263709570u, 1244120487u, 3416600761u, 224580012u, 4252610345u, 286306391u, 814956796u, 1820532305u, 2776960536u, 3082703465u, 1659265982u, 1157398049u, 3752806924u, 3508246460u, 2902716664u, 2110942730u, 3267551635u, 902835431u, 405228165u, 2341378668u, 2220373824u, 3303626294u, 1175118221u, 383517064u, 896322512u, 1697257567u, 2202820683u, 1837689083u, 1336556841u, 914535232u, 3634083711u, 1537187340u, 3712582785u, 1088201893u, 3270984620u, 3093195926u, 2561488770u, 1962968100u, 236189500u, 2549435227u, 536047554u, 422609195u, 2958815818u, 4166184983u, 3528794619u, 1042329086u, 3914176886u, 1359641525u, 1197100813u, 1269739674u, 3301844628u, 1831223342u, 3329594980u, 2433669782u, 494908536u, 1441714797u, 1174785921u, 1933050423u, 958901065u, 868981704u, 2163330264u, 3243110680u, 1443133429u, 1145009669u, 2520062970u, 3851564853u, 2664619323u, 2686075657u, 4042710240u, 2125408249u, 4165697916u, 2328730865u, 3160666939u, 588683409u, 2126275847u, 829404209u, 1878067032u, 2567792910u, 897670516u, 934312076u, 2952135524u, 504832490u, 3312698056u, 4015478120u, 1265237690u, 3376133707u, 967674402u, 1553513184u, 834830418u, 2396504772u, 3278582098u, 2950762705u, 4277111759u, 4159211303u, 1290097509u, 251949572u, 3065234219u, 1832020534u, 312136369u, 1103404394u, 198182691u, 1369599600u, 3906710870u, 2954651680u, 1535115487u, 2389327507u, 1813520230u, 632704095u, 140762681u, 3123202913u, 3336005523u, 1287729497u, 2198985327u, 2470730783u, 3821758006u, 1009870118u, 559702706u, 4274686257u, 3187546567u, 2402308151u, 1096697687u, 678932329u, 3716363135u, 2899950405u, 3046872820u, 3754655641u, 2021741414u, 1993425202u, 2185339100u, 2838253700u, 3099212100u, 1679617763u, 3703603898u, 1135665833u, 3559875668u, 2503579743u, 1446869792u, 879818611u, 3788305533u, 2879018745u, 2719373510u, 3606051203u, 2166567748u, 343595772u, 1996164093u, 1577656121u, 475248376u, 463852893u, 2864244470u, 1332049663u, 3326459767u, 3207234525u, 2028708916u, 938916154u, 3115246264u, 3272166407u, 557660441u, 1265684026u, 245033807u, 2440657523u, 255451671u, 3811885130u, 1399880284u, 1322855877u, 3452203069u, 1324994449u, 3796404024u, 2002090272u, 1568745354u, 3700047753u, 31799506u, 3009412313u, 3184047862u, 728680761u, 3848624873u, 1849329144u, 3160134214u, 1272923193u, 1474278816u, 1208883495u, 1566527339u, 4136466541u, 630825649u, 1717327230u, 1496132623u, 2449386742u, 128106940u, 2108574735u, 2584236470u, 2872246579u, 397338552u, 3088905079u, 3456882941u, 1715915153u, 2940716269u, 4168438718u, 467441309u, 872996731u, 3206901319u, 1610606159u, 2540270567u, 1301658081u, 2379410194u, 2109315753u, 3348545480u, 2041927873u, 2644077493u, 546596661u, 1017047954u, 2596792972u, 2783958892u, 2826408201u, 3531646869u, 2219352672u, 4217451852u, 1806867885u, 1903578924u, 2076465705u, 2373061493u, 1832925325u, 241574049u, 1509517110u, 3703614272u, 3429894862u, 3529565564u, 4010000614u, 2256197939u, 517086873u, 2964595704u, 3501035294u, 4079457298u, 1184683823u, 4260564140u, 2339268412u, 3871564102u, 1572637805u, 383233885u, 3351411126u, 3419328182u, 2017406667u, 919677938u, 29804156u, 46276077u, 3032591014u, 4204155962u, 1172319502u, 969309871u, 2211319468u, 4153726847u, 3094193193u, 4240669441u, 3441709766u, 1387233546u, 4048882438u, 1217896566u, 1580964938u, 740563169u, 3691850348u, 3176426539u, 2738323042u, 2948665536u, 1474029445u, 3513354882u, 3219466255u, 3799363969u, 3961796122u, 1055550923u, 1992212097u, 2263421398u, 4289759174u, 2516844140u, 1782515131u, 2746577402u, 721928440u, 3529570984u, 1705302106u, 768287270u, 3474902815u, 4000011125u, 3475618043u, 212490675u, 549130471u, 2970128275u, 3170016833u, 2549835613u, 3691104824u, 2694324482u, 4169365948u, 3738062408u, 602930397u, 2148954730u, 2636387819u, 2616619070u, 301617872u, 374657036u, 3862686086u, 431292293u, 4225245165u, 1358580562u, 2377363130u, 1415970351u, 3885060756u, 1438379807u, 2297418515u, 2954777083u, 3970368221u, 1229801760u, 1931934525u, 2287507921u, 1713471510u, 2145608111u, 2650140034u, 4081586725u, 4196863572u, 1896558394u, 4059852729u, 1813855658u, 2618400836u, 1396056469u, 2375584220u, 1037031473u, 249284003u, 2450077637u, 1383747654u, 3122687303u, 2664431743u, 3855028730u, 929335420u, 3193251135u, 137313762u, 1850894384u, 3163711272u, 3071568023u, 418541677u, 3621223039u, 340274176u, 1167681812u, 4106647531u, 4022465625u, 3069708839u, 2704165015u, 2332023349u, 641449034u, 921552000u, 1712976649u, 1876484273u, 2343049860u, 1834682077u, 4155949943u, 2061821157u, 4240649383u, 747285578u, 1626463554u, 165503115u, 359629739u, 3838102563u, 582664250u, 3878924635u, 4117237498u, 2250573853u, 804336148u, 331393443u, 4242530387u, 3346357270u, 1674051445u, 3348019777u, 1722242971u, 2509017299u, 2966108111u, 4189102509u, 3323592310u, 2631381069u, 4014551783u, 4250787412u, 3448394212u, 2664752123u, 3517585534u, 3605365141u, 1669471183u, 2210121140u, 760762191u, 249697459u, 3416920106u, 16322182u, 643179562u, 1564226597u, 2134630675u, 1011990087u, 2990167340u, 2349550842u, 1642428946u, 1214854475u, 2134299399u, 2704221532u, 2104175211u, 4283196353u, 1342880802u, 198529755u, 2004468390u, 2544665755u, 853593042u, 2090611294u, 2970943872u, 1472073141u, 850464484u, 1407609278u, 3062461105u, 366448238u, 3842907484u, 488797416u, 1432670231u, 294963924u, 3693536939u, 3390549825u, 1583234720u, 4032223840u, 2318423400u, 2965642867u, 930822729u, 1679099863u, 1728968857u, 900822335u, 702309817u, 3258354115u, 1502282913u, 2811888503u, 3924947660u, 2477280726u, 3651607391u, 3788310204u, 1300369123u, 1842333726u, 4146839064u, 2468893861u, 4091095953u, 488595746u, 1448097974u, 1159634090u, 1738834113u, 2928916831u, 4093725287u, 530850094u, 291657799u, 3548184546u, 679517009u, 399175380u, 2658337143u, 3827951761u, 3086277222u, 2067718397u, 3632376023u, 3553313841u, 119173722u, 1702434637u, 1766260771u, 895654784u, 751439914u, 4008409498u, 215917713u, 1288382231u, 2656990891u, 2581779077u, 1570750352u, 2666411616u, 3533987737u, 4289478316u, 3576119563u, 3869794273u, 963183826u, 2081410737u, 3796810515u, 1036883117u, 136547246u, 875691100u, 2592925324u, 2689342539u, 427154472u, 532957601u, 1228758574u, 1908591042u, 1464255968u, 446980910u, 2984611177u, 3508927906u, 2001585786u, 2544767379u, 1525438381u, 879448844u, 1348536411u, 4242243590u, 2861338018u, 601175800u, 764077711u, 530635011u, 3785343245u, 2378297261u, 457568934u, 76438221u, 4104954272u, 2485968477u, 2381948487u, 4226929450u, 3148473363u, 879369091u, 2180270337u, 3674375989u, 1387729170u, 568650985u, 951677556u, 4213877384u, 2721005055u, 1678669911u, 66786703u, 2273631661u, 1149351924u, 246723096u, 1895026827u, 3810605772u, 3711056516u, 2080120290u, 3638638708u, 2915672708u, 2263003308u, 1976115991u, 3448840877u, 2019238520u, 225333538u, 1555273378u, 3797521928u, 1942347150u, 3262952567u, 2817830907u, 2078619498u, 749534111u, 1178073973u, 841092198u, 3288261538u, 1696412169u, 1496966875u, 3739946319u, 2481012988u, 568983526u, 114945840u, 2841706923u, 1632780103u, 4020169654u, 2087949619u, 833416317u, 3787017905u, 2373238993u, 2575395164u, 2542976862u, 2971726178u, 2880371864u, 3642087909u, 1043714217u, 3894199764u, 2235879182u, 203853421u, 834683330u, 425935223u, 3560796393u, 3565833278u, 3414330886u, 1748785729u, 1023171602u, 580966986u, 2657385925u, 2124704694u, 233442446u, 1107045577u, 3899097693u, 1067532701u, 115667924u, 1406028344u, 2419657149u, 18613994u, 2532882091u, 3476683808u, 895961699u, 3762914298u, 1328752423u, 1844996900u, 1210281744u, 904215228u, 4055325594u, 1118521573u, 3996647489u, 3657647605u, 325254059u, 3136157065u, 3341068436u, 2287683323u, 1313073005u, 126005630u, 853746559u, 3555092974u, 2689238752u, 49515858u, 61073168u, 1010661841u, 1269521335u, 1902040126u, 3858321250u, 1400735275u, 2974699176u, 2771676666u, 545726212u, 2225229957u, 1086473152u, 3454177594u, 2088002891u, 2883475137u, 3222194252u, 4144472319u, 567988835u, 1051332394u, 3932046135u, 542648229u, 162888005u, 1669710469u, 1492500905u, 553041029u, 3817492747u, 584127807u, 4147115982u, 2993670925u, 3509733475u, 3587959456u, 2088550465u, 1745399498u, 869648362u, 1404723176u, 3947542735u, 1334333531u, 59634866u, 3239516985u, 3844250972u, 1275954779u, 2512155003u, 1685649437u, 639306006u, 2524620206u, 576786501u, 655707039u, 2864351838u, 3736264674u, 1200907897u, 2384379464u, 15823708u, 206117476u, 1193310960u, 1093099415u, 3696538026u, 4112584792u, 2069527017u, 547588820u, 4178147211u, 2827259351u, 940846775u, 1054995047u, 2976960697u, 1934305529u, 2199137382u, 1005722394u, 1875867180u, 2064356511u, 4019734130u, 3096333006u, 2069509024u, 2906358341u, 2232866485u, 1456016086u, 1422674894u, 867282151u, 1612503136u, 1739843072u, 134947567u, 2978775774u, 1284167756u, 1090844589u, 831688783u, 2079216362u, 1626991196u, 3644714163u, 3678110059u, 898470030u, 3916646913u, 3182422972u, 3630426828u, 969847973u, 3427164640u, 3463937250u, 3044785046u, 897322257u, 3443872170u, 4185408854u, 2557463241u, 4080940424u, 2048168570u, 2429169982u, 3174690447u, 2513494106u, 1213061732u, 3143736628u, 3482268149u, 1250714337u, 31648125u, 3872383625u, 1565760579u, 36665130u, 751041229u, 2257179590u, 2915361862u, 280819225u, 2907818413u, 4254297769u, 3493178615u, 3755944354u, 4043533423u, 1134196225u, 4177134659u, 127246419u, 2442615581u, 923049607u, 1004426206u, 782768297u, 2410586681u, 1430106871u, 4103323427u, 3168399477u, 3716682375u, 3616334719u, 3413209549u, 656672786u, 2876965944u, 182894450u, 456581318u, 2683752067u, 3877875910u, 3190666241u, 3240336907u, 4024807233u, 1681224377u, 1576191191u, 3599250276u, 2381111980u, 3495321877u, 3956024585u, 1611608524u, 3815677453u, 2062334396u, 1656117707u, 5457134u, 3234118251u, 470187419u, 2688566989u, 3259870297u, 660100446u, 442236198u, 2542452448u, 493137955u, 392411099u, 947967568u, 1234595917u, 4230082284u, 2762976773u, 2870085764u, 1455086530u, 2762099647u, 4011882747u, 1215981925u, 3227517889u, 3269061963u, 4037515364u, 3168911474u, 4255057396u, 2026092260u, 1736192508u, 3909727042u, 3114708966u, 1938800693u, 680793595u, 1525265867u, 2808224480u, 2122290603u, 1211197714u, 3520488321u, 3979192396u, 3540779343u, 4192918639u, 2736030448u, 1120335563u, 1698949078u, 3993310631u, 1966048551u, 2228221363u, 597941119u, 3498018399u, 393987327u, 454500547u, 1222959566u, 567151340u, 3774764786u, 1492844524u, 3308300614u, 805568076u, 868414882u, 177406999u, 1608110313u, 642061169u, 1027515771u, 3131251981u, 2851936150u, 4272755262u, 1532845092u, 709643652u, 682573592u, 1244104217u, 796769556u, 2500467040u, 3002618826u, 1112998535u, 1780193104u, 1243644607u, 3691719535u, 2958853053u, 466635014u, 2277292580u, 4082276003u, 1030800045u, 1750863246u, 379050598u, 3576413281u, 731493104u, 132259176u, 4115195437u, 1769890695u, 2715470335u, 1819263183u, 2028531518u, 2154809766u, 3672399742u, 76727603u, 4198182186u, 2304993586u, 1666387627u, 284366017u, 3359785538u, 3469807328u, 2926494787u, 3829072836u, 2493478921u, 3738499303u, 3311304980u, 932916545u, 2235559063u, 2909742396u, 1765719309u, 1456588655u, 508290328u, 1490719640u, 3356513470u, 2908490783u, 251085588u, 830410677u, 3172220325u, 3897208579u, 1940535730u, 151909546u, 2384458112u, }; // Return false only if offset is -1 and a spot check of 3 hashes all yield 0. bool Test(int offset, int len = 0) { #undef Check #undef IsAlive #define Check(x) do { \ const uint32_t actual = (x), e = expected[index++]; \ bool ok = actual == e; \ if (!ok) { \ cerr << "expected " << hex << e << " but got " << actual << endl; \ ++errors; \ } \ assert(ok); \ } while (0) #define IsAlive(x) do { alive += IsNonZero(x); } while (0) // After the following line is where the uses of "Check" and such will go. static int index = 0; if (offset == -1) { int alive = 0; { uint64_t h = farmhashuo::Hash64WithSeed(data, len++, SEED); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } { uint64_t h = farmhashuo::Hash64(data, len++); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } { uint64_t h = farmhashuo::Hash64(data, len++); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } len -= 3; return alive > 0; } { uint64_t h = farmhashuo::Hash64WithSeed(data + offset, len, SEED); Check(h >> 32); Check((h << 32) >> 32); } { uint64_t h = farmhashuo::Hash64(data + offset, len); Check(h >> 32); Check((h << 32) >> 32); } return true; #undef Check #undef IsAlive } int RunTest() { Setup(); int i = 0; cout << "Running farmhashuoTest"; if (!Test(-1)) { cout << "... Unavailable\n"; return NoteErrors(); } // Good. The function is attempting to hash, so run the full test. int errors_prior_to_test = errors; for ( ; i < kTestSize - 1; i++) { Test(i * i, i); } for ( ; i < kDataSize; i += i / 7) { Test(0, i); } Test(0, kDataSize); cout << (errors == errors_prior_to_test ? "... OK\n" : "... Failed\n"); return NoteErrors(); } #else // After the following line is where the code to print hash codes will go. void Dump(int offset, int len) { { uint64_t h = farmhashuo::Hash64WithSeed(data + offset, len, SEED); cout << (h >> 32) << "u, " << ((h << 32) >> 32) << "u," << endl; } { uint64_t h = farmhashuo::Hash64(data + offset, len); cout << (h >> 32) << "u, " << ((h << 32) >> 32) << "u," << endl; } } #endif #undef SEED #undef SEED1 #undef SEED0 } // namespace farmhashuoTest #if TESTING static int farmhashuoTestResult = farmhashuoTest::RunTest(); #else int main(int argc, char** argv) { Setup(); cout << "uint32_t expected[] = {\n"; int i = 0; for ( ; i < kTestSize - 1; i++) { farmhashuoTest::Dump(i * i, i); } for ( ; i < kDataSize; i += i / 7) { farmhashuoTest::Dump(0, i); } farmhashuoTest::Dump(0, kDataSize); cout << "};\n"; } #endif #ifndef FARMHASH_SELF_TEST_GUARD #define FARMHASH_SELF_TEST_GUARD #include #include #include using std::cout; using std::cerr; using std::endl; using std::hex; static const uint64_t kSeed0 = 1234567; static const uint64_t kSeed1 = k0; static const int kDataSize = 1 << 20; static const int kTestSize = 300; #define kSeed128 Uint128(kSeed0, kSeed1) static char data[kDataSize]; static int completed_self_tests = 0; static int errors = 0; // Initialize data to pseudorandom values. void Setup() { if (completed_self_tests == 0) { uint64_t a = 9; uint64_t b = 777; for (int i = 0; i < kDataSize; i++) { a += b; b += a; a = (a ^ (a >> 41)) * k0; b = (b ^ (b >> 41)) * k0 + i; uint8_t u = b >> 37; memcpy(data + i, &u, 1); // uint8_t -> char } } } int NoteErrors() { #define NUM_SELF_TESTS 9 if (++completed_self_tests == NUM_SELF_TESTS) std::exit(errors > 0); return errors; } template inline bool IsNonZero(T x) { return x != 0; } template <> inline bool IsNonZero(uint128_t x) { return x != Uint128(0, 0); } #endif // FARMHASH_SELF_TEST_GUARD namespace farmhashxoTest { uint32_t CreateSeed(int offset, int salt) { uint32_t h = static_cast(salt & 0xffffffff); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h += static_cast(offset & 0xffffffff); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); h = h * c1; h ^= (h >> 17); return h; } #undef SEED #undef SEED1 #undef SEED0 #define SEED CreateSeed(offset, -1) #define SEED0 CreateSeed(offset, 0) #define SEED1 CreateSeed(offset, 1) #undef TESTING #define TESTING 1 #if TESTING uint32_t expected[] = { 1140953930u, 861465670u, 3277735313u, 2681724312u, 2598464059u, 797982799u, 890626835u, 800175912u, 2603993599u, 921001710u, 1410420968u, 2134990486u, 3283896453u, 1867689945u, 2914424215u, 2244477846u, 255297188u, 2992121793u, 1110588164u, 4186314283u, 161451183u, 3943596029u, 4019337850u, 452431531u, 283198166u, 2741341286u, 3379021470u, 2557197665u, 299850021u, 2532580744u, 452473466u, 1706958772u, 1298374911u, 3099673830u, 2199864459u, 3696623795u, 236935126u, 2976578695u, 4055299123u, 3281581178u, 1053458494u, 1882212500u, 2305012065u, 2169731866u, 3456121707u, 275903667u, 458884671u, 3033004529u, 3058973506u, 2379411653u, 1898235244u, 1402319660u, 2700149065u, 2699376854u, 147814787u, 720739346u, 2433714046u, 4222949502u, 4220361840u, 1712034059u, 3425469811u, 3690733394u, 4148372108u, 1330324210u, 594028478u, 2921867846u, 1635026870u, 192883107u, 780716741u, 1728752234u, 3280331829u, 326029180u, 3969463346u, 1436364519u, 393215742u, 3349570000u, 3824583307u, 1612122221u, 2859809759u, 3808705738u, 1379537552u, 1646032583u, 2233466664u, 1432476832u, 4023053163u, 2650381482u, 2052294713u, 3552092450u, 1628777059u, 1499109081u, 3476440786u, 3829307897u, 2960536756u, 1554038301u, 1145519619u, 3190844552u, 2902102606u, 3600725550u, 237495366u, 540224401u, 65721842u, 489963606u, 1448662590u, 397635823u, 1596489240u, 1562872448u, 1790705123u, 2128624475u, 180854224u, 2604346966u, 1435705557u, 1262831810u, 155445229u, 1672724608u, 1669465176u, 1341975128u, 663607706u, 2077310004u, 3610042449u, 1911523866u, 1043692997u, 1454396064u, 2563776023u, 294527927u, 1099072299u, 1389770549u, 703505868u, 678706990u, 2952353448u, 2026137563u, 3603803785u, 629449419u, 1933894405u, 3043213226u, 226132789u, 2489287368u, 1552847036u, 645684964u, 3828089804u, 3632594520u, 187883449u, 230403464u, 3151491850u, 3272648435u, 3729087873u, 1303930448u, 2002861219u, 165370827u, 916494250u, 1230085527u, 3103338579u, 3064290191u, 3807265751u, 3628174014u, 231181488u, 851743255u, 2295806711u, 1781190011u, 2988893883u, 1554380634u, 1142264800u, 3667013118u, 1968445277u, 315203929u, 2638023604u, 2290487377u, 732137533u, 1909203251u, 440398219u, 1891630171u, 1380301172u, 1498556724u, 4072067757u, 4165088768u, 4204318635u, 441430649u, 3931792696u, 197618179u, 956300927u, 914413116u, 3010839769u, 2837339569u, 2148126371u, 1913303225u, 3074915312u, 3117299654u, 4139181436u, 2993479124u, 3178848746u, 1357272220u, 1438494951u, 507436733u, 667183474u, 2084369203u, 3854939912u, 1413396341u, 126024219u, 146044391u, 1016656857u, 3022024459u, 3254014218u, 429095991u, 990500595u, 3056862311u, 985653208u, 1718653828u, 623071693u, 366414107u, 1771289760u, 2293458109u, 3047342438u, 2991127487u, 3120876698u, 1684583131u, 3638043310u, 1170404994u, 863214540u, 1087193030u, 199124911u, 520792961u, 3169775996u, 1577421232u, 3331828431u, 1013201099u, 1716848157u, 4033596884u, 1770708857u, 4229339322u, 1146169032u, 1434258493u, 3824360466u, 3242407770u, 1926419493u, 2649785113u, 872586426u, 762243036u, 2736953692u, 816692935u, 1571283333u, 3555213933u, 2266795890u, 3781899767u, 4290630595u, 517646945u, 3006163611u, 2180594090u, 959214578u, 558910384u, 1283799121u, 3047062993u, 3830962609u, 2391606125u, 3544509313u, 622325861u, 834785312u, 382936554u, 1421463872u, 788479970u, 1825135056u, 2725923798u, 580988377u, 2826990641u, 247825043u, 3167748333u, 812546227u, 2506885666u, 2584372201u, 1758123094u, 1891789696u, 389974094u, 345313518u, 2022370576u, 3886113119u, 3338548567u, 1083486947u, 2583576230u, 1776047957u, 1771384107u, 3604937815u, 3198590202u, 3027522813u, 4155628142u, 4232136669u, 427759438u, 4244322689u, 542201663u, 1549591985u, 2856634168u, 556609672u, 45845311u, 1175961330u, 3948351189u, 4165739882u, 4194218315u, 1634635545u, 4151937410u, 713127376u, 1467786451u, 1327394015u, 2743592929u, 2638154051u, 810082938u, 3077742128u, 1062268187u, 4084325664u, 3810665822u, 3735739145u, 2794294783u, 2335576331u, 2560479831u, 690240711u, 997658837u, 2442302747u, 3948961926u, 3958366652u, 3067277639u, 2059157774u, 1211737169u, 1516711748u, 2339636583u, 4188504038u, 59581167u, 2767897792u, 1389679610u, 2658147000u, 2643979752u, 3758739543u, 4189944477u, 1454470782u, 100876854u, 2995362413u, 118817200u, 3252925478u, 2062343506u, 2804483644u, 3088828656u, 1231633714u, 4168280671u, 2931588131u, 3284356565u, 1255909792u, 3130054947u, 4173605289u, 1407328702u, 1677744031u, 3532596884u, 3162657845u, 3887208531u, 2256541290u, 3459463480u, 3740979556u, 259034107u, 392987633u, 3233195759u, 3606709555u, 3424793077u, 315836068u, 3200749877u, 4065431359u, 760633989u, 2982018998u, 1811050648u, 234531934u, 1115203611u, 3897494162u, 1516407838u, 1603559457u, 323296368u, 2632963283u, 1778459926u, 2879836826u, 2146672889u, 3486330348u, 492621815u, 1231665285u, 2457048126u, 3438440082u, 2217471853u, 3355404249u, 3275550588u, 1052645068u, 862072556u, 4110617119u, 3745267835u, 2657392572u, 4279236653u, 1688445808u, 701920051u, 956734128u, 581695350u, 3157862788u, 2585726058u, 1192588249u, 1410111809u, 1651193125u, 3326135446u, 1073280453u, 97376972u, 2513844237u, 2187968410u, 3976859649u, 4267859263u, 3429034542u, 564493077u, 3000537321u, 479241367u, 3845637831u, 2868987960u, 51544337u, 1029173765u, 393624922u, 704325635u, 2357610553u, 1418509533u, 2007814586u, 3866658271u, 3082385053u, 735688735u, 916110004u, 3283299459u, 1051684175u, 1083796807u, 4074716319u, 813690332u, 144264390u, 1439630796u, 1508556987u, 675582689u, 3748881891u, 3195309868u, 362884708u, 1616408198u, 43233176u, 837301135u, 881504822u, 3254795114u, 1385506591u, 2799925823u, 1469874582u, 3464841997u, 497175391u, 3929484338u, 3975771289u, 1798536177u, 2926265846u, 1374242438u, 3675707838u, 4205965408u, 3153165629u, 1499475160u, 187287713u, 548490821u, 3255259608u, 4247675634u, 1940181471u, 3779953975u, 687167150u, 2319566715u, 1742785722u, 785893184u, 2296977392u, 2778575413u, 1794720651u, 48131484u, 4084891412u, 1160134629u, 3737623280u, 823113169u, 3423207646u, 3803213486u, 710625654u, 4116162021u, 3693420287u, 4167766971u, 1666602807u, 295320990u, 3513255468u, 2487440590u, 234080704u, 4004655503u, 2971762528u, 1479656873u, 4090178629u, 4044418876u, 391947536u, 1462554406u, 3909295855u, 1239580330u, 1515601363u, 2011102035u, 1442068334u, 4265993528u, 1191921695u, 2291355695u, 4257172787u, 576405853u, 314332944u, 4038839101u, 55559918u, 2378985842u, 711098718u, 2425317635u, 1644327317u, 1401013391u, 4193760037u, 2958260436u, 3167371443u, 3062418115u, 3800755475u, 3167030094u, 3489648204u, 1405430357u, 526177822u, 2602636307u, 915406019u, 4264167741u, 1484090483u, 3070944737u, 254529415u, 4017058800u, 1702710265u, 1029665228u, 2000382906u, 3185573940u, 1381258384u, 4036354071u, 2900841028u, 2670703363u, 2921748807u, 2899069938u, 4130543625u, 688472265u, 4186808827u, 1054670286u, 1132985391u, 2840525968u, 4175776103u, 338058159u, 1735964501u, 1539305024u, 3497121710u, 1568260669u, 2227290760u, 146827036u, 3977176001u, 4060134777u, 857488494u, 250055052u, 4284109679u, 2502815838u, 2592281721u, 1603444633u, 1390562014u, 1556658131u, 616327404u, 2448966429u, 3051191726u, 3891353218u, 1213304082u, 762328245u, 2239052397u, 1082330589u, 2455957292u, 201837927u, 405397452u, 3079886794u, 2583939798u, 2848283092u, 3750724631u, 883849006u, 3204198988u, 3341327098u, 1855234968u, 1982110346u, 1485529487u, 541496720u, 4117290321u, 3607433551u, 2168864636u, 133643215u, 1055817409u, 3847827123u, 2960769387u, 4046101649u, 1176127003u, 4015671361u, 4243643405u, 2849988118u, 517111221u, 1796672358u, 2045051700u, 3452457457u, 2948254999u, 2102063419u, 1556410577u, 1536380876u, 3776661467u, 3281002516u, 1735616066u, 1539151988u, 1087795162u, 3332431596u, 685631442u, 1147951686u, 95237878u, 2005032160u, 4012206915u, 4224354805u, 3204999386u, 2415262714u, 1433635018u, 116647396u, 83167836u, 2881562655u, 2729416454u, 1029284767u, 881378302u, 2159170082u, 555057366u, 1169104445u, 3963877000u, 1919171906u, 336034862u, 2017579106u, 4059340529u, 3020819343u, 865146997u, 2473524405u, 944743644u, 1694443528u, 1804513294u, 2904752429u, 617975720u, 3671562289u, 260177668u, 505662155u, 1885941445u, 2504509403u, 2260041112u, 1019936943u, 3722741628u, 1511077569u, 3100701179u, 1379422864u, 1535670711u, 773792826u, 1103819072u, 2089123665u, 1157547425u, 329152940u, 4142587430u, 484732447u, 2475035432u, 1120017626u, 412145504u, 965125959u, 324924679u, 2809286837u, 2842141483u, 4029205195u, 2974306813u, 515627448u, 3791551981u, 1097806406u, 3873078673u, 136118734u, 1872130856u, 3632422367u, 3574135531u, 4017075736u, 1699452298u, 1403506686u, 344414660u, 1189129691u, 3487080616u, 1516736273u, 1805475756u, 2562064338u, 163335594u, 2732147834u, 4077452507u, 2984955003u, 4271866024u, 3071338162u, 2347111903u, 873829983u, 1948409509u, 1923531348u, 459509140u, 771592405u, 1750124750u, 2334938333u, 213811117u, 2586632018u, 185232757u, 4032960199u, 2447383637u, 284777551u, 1654276320u, 2687561076u, 3512945009u, 308584855u, 1861027147u, 4102279334u, 3203802620u, 1692079268u, 4250142168u, 2565680167u, 1507046104u, 841195925u, 520565830u, 3674576684u, 38924274u, 3770488806u, 2414430882u, 3978473838u, 3703994407u, 69201295u, 3099963860u, 1255084262u, 690971838u, 3539996781u, 3696902571u, 3593730713u, 2363435042u, 54945052u, 1785765213u, 184911581u, 1586241476u, 1939595371u, 2534883189u, 2432427547u, 2374171993u, 2039128933u, 2955715987u, 2295501078u, 2741583197u, 1280920000u, 686818699u, 1238742497u, 3843660102u, 82177963u, 1281043691u, 1121403845u, 1697846708u, 284852964u, 278661677u, 2889101923u, 2127558730u, 713121337u, 872502474u, 511142139u, 1261140657u, 1747052377u, 2108187161u, 927011680u, 955328267u, 3821994995u, 2707230761u, 4142246789u, 4134691985u, 1958963937u, 2498463509u, 1977988705u, 1419293714u, 1636932722u, 2567532373u, 4075249328u, 240575705u, 1956681213u, 2598802768u, 2025886508u, 4104757832u, 3026358429u, 3242615202u, 4026813725u, 255108733u, 1845587644u, 3573008472u, 3615577014u, 1222733548u, 1205557630u, 917608574u, 1363253259u, 1541946015u, 3087190425u, 1138008081u, 1444019663u, 109793386u, 341851980u, 857839960u, 2515339233u, 156283211u, 1906768669u, 3886713057u, 1276595523u, 2809830736u, 460237542u, 3420452099u, 142985419u, 205970448u, 4198897105u, 1950698961u, 2069753399u, 1142216925u, 1113051162u, 1033680610u, 4278599955u, 1106466069u, 356742959u, 531521052u, 3494863964u, 225629455u, 3735275001u, 3662626864u, 1750561299u, 1012864651u, 2101846429u, 1074553219u, 668829411u, 992181339u, 3384018814u, 3330664522u, 860966321u, 1885071395u, 4233785523u, 100741310u, 451656820u, 2148187612u, 1063001151u, 360256231u, 107312677u, 3650357479u, 2390172694u, 22452685u, 237319043u, 3600462351u, 1216645846u, 2088767754u, 164402616u, 2418980170u, 926137824u, 94638678u, 1689811113u, 2751052984u, 1767810825u, 271289013u, 3896132233u, 103797041u, 1397772514u, 3441135892u, 3323383489u, 2491268371u, 1662561885u, 1612872497u, 2986430557u, 2756998822u, 207428029u, 937973965u, 2791656726u, 1949717207u, 2260498180u, 2648427775u, 2360400900u, 2080496169u, 486358863u, 1582022990u, 1263709570u, 1396468647u, 1377764574u, 363008508u, 1293502429u, 224580012u, 4252610345u, 1435134775u, 1099809675u, 533671980u, 1533438766u, 1820532305u, 2776960536u, 3374512975u, 3542220540u, 822810075u, 3716663290u, 1157398049u, 3752806924u, 4081637863u, 337070226u, 3866585976u, 359270190u, 2110942730u, 3267551635u, 644850146u, 1306761320u, 746972907u, 934259457u, 2341378668u, 2220373824u, 1242645122u, 4109252858u, 1625266099u, 1173698481u, 383517064u, 896322512u, 3377483696u, 1788337208u, 455496839u, 3194373887u, 1837689083u, 1336556841u, 1658628529u, 2911512007u, 3838343487u, 2757664765u, 1537187340u, 3712582785u, 367022558u, 3071359622u, 3926147070u, 35432879u, 3093195926u, 2561488770u, 4273132307u, 3898950547u, 2838251049u, 2103926083u, 2549435227u, 536047554u, 1858986613u, 2040551642u, 1147412575u, 1972369852u, 4166184983u, 3528794619u, 4077477194u, 3565689036u, 808048238u, 3826350461u, 1359641525u, 1197100813u, 265993036u, 1864569342u, 725164342u, 2264788336u, 1831223342u, 3329594980u, 923017956u, 490608221u, 3818634478u, 258154469u, 1441714797u, 1174785921u, 3833372385u, 3287246572u, 1677395563u, 3569218731u, 868981704u, 2163330264u, 2649450292u, 500120236u, 465161780u, 746438382u, 1145009669u, 2520062970u, 2810524030u, 1561519055u, 1479878006u, 3864969305u, 2686075657u, 4042710240u, 3224066062u, 2774151984u, 2226179547u, 1643626042u, 2328730865u, 3160666939u, 2107011431u, 96459446u, 3920328742u, 3336407558u, 829404209u, 1878067032u, 1235983679u, 4237425634u, 466519055u, 3870676863u, 934312076u, 2952135524u, 276949224u, 4100839753u, 424001484u, 1955120893u, 4015478120u, 1265237690u, 427484362u, 4246879223u, 3452969617u, 1724363362u, 1553513184u, 834830418u, 1858777639u, 3476334357u, 4144030366u, 2450047160u, 2950762705u, 4277111759u, 358032121u, 2511026735u, 167923105u, 2059208280u, 251949572u, 3065234219u, 1535473864u, 556796152u, 1513237478u, 3150857516u, 1103404394u, 198182691u, 1476438092u, 2913077464u, 207119516u, 3963810232u, 2954651680u, 1535115487u, 3051522276u, 4046477658u, 917804636u, 864395565u, 632704095u, 140762681u, 1802040304u, 990407433u, 3771506212u, 4106024923u, 1287729497u, 2198985327u, 4052924496u, 2926590471u, 3084557148u, 1472898694u, 1009870118u, 559702706u, 4265214507u, 82077489u, 3067891003u, 3295678907u, 2402308151u, 1096697687u, 464407878u, 4190838199u, 4269578403u, 3060919438u, 2899950405u, 3046872820u, 733509243u, 1583801700u, 40453902u, 3879773881u, 1993425202u, 2185339100u, 1877837196u, 3912423882u, 3293122640u, 4104318469u, 1679617763u, 3703603898u, 8759461u, 2540185277u, 1152198475u, 2038345882u, 2503579743u, 1446869792u, 2019419351u, 4051584612u, 3178289407u, 3992503830u, 2879018745u, 2719373510u, 700836153u, 1675560450u, 4121245793u, 2064715719u, 343595772u, 1996164093u, 3130433948u, 405251683u, 2804817126u, 1607133689u, 463852893u, 2864244470u, 2224044848u, 4071581802u, 2537107938u, 2246347953u, 3207234525u, 2028708916u, 2272418128u, 803575837u, 38655481u, 2170452091u, 3272166407u, 557660441u, 4019147902u, 3841480082u, 298459606u, 2600943364u, 2440657523u, 255451671u, 3424361375u, 779434428u, 3088526123u, 490671625u, 1322855877u, 3452203069u, 3057021940u, 2285701422u, 2014993457u, 2390431709u, 2002090272u, 1568745354u, 1783152480u, 823305654u, 4053862835u, 2200236540u, 3009412313u, 3184047862u, 3032187389u, 4159715581u, 2966902888u, 252986948u, 1849329144u, 3160134214u, 3420960112u, 3198900547u, 749160960u, 379139040u, 1208883495u, 1566527339u, 3006227299u, 4194096960u, 556075248u, 497404038u, 1717327230u, 1496132623u, 1775955687u, 1719108984u, 1014328900u, 4189966956u, 2108574735u, 2584236470u, 684087286u, 531310503u, 4264509527u, 773405691u, 3088905079u, 3456882941u, 3105682208u, 3382290593u, 2289363624u, 3296306400u, 4168438718u, 467441309u, 777173623u, 3241407531u, 1183994815u, 1132983260u, 1610606159u, 2540270567u, 2649684057u, 1397502982u, 146657385u, 3318434267u, 2109315753u, 3348545480u, 3193669211u, 811750340u, 1073256162u, 3571673088u, 546596661u, 1017047954u, 3403136990u, 2540585554u, 1477047647u, 4145867423u, 2826408201u, 3531646869u, 784952939u, 943914610u, 2717443875u, 3657384638u, 1806867885u, 1903578924u, 3985088434u, 1911188923u, 1764002686u, 3672748083u, 1832925325u, 241574049u, 519948041u, 3181425568u, 2939747257u, 1634174593u, 3429894862u, 3529565564u, 1089679033u, 240953857u, 2025369941u, 2695166650u, 517086873u, 2964595704u, 3017658263u, 3828377737u, 2144895011u, 994799311u, 1184683823u, 4260564140u, 308018483u, 4262383425u, 1374752558u, 3431057723u, 1572637805u, 383233885u, 3188015819u, 4051263539u, 233319221u, 3794788167u, 2017406667u, 919677938u, 4074952232u, 1683612329u, 4213676186u, 327142514u, 3032591014u, 4204155962u, 206775997u, 2283918569u, 2395147154u, 3427505379u, 2211319468u, 4153726847u, 2217060665u, 350160869u, 2493667051u, 1648200185u, 3441709766u, 1387233546u, 140980u, 1891558063u, 760080239u, 2088061981u, 1580964938u, 740563169u, 422986366u, 330624974u, 4264507722u, 150928357u, 2738323042u, 2948665536u, 918718096u, 376390582u, 3966098971u, 717653678u, 3219466255u, 3799363969u, 3424344721u, 3187805406u, 375347278u, 3490350144u, 1992212097u, 2263421398u, 3855037968u, 1928519266u, 3866327955u, 1129127000u, 1782515131u, 2746577402u, 3059200728u, 2108753646u, 2738070963u, 1336849395u, 1705302106u, 768287270u, 1343511943u, 2247006571u, 1956142255u, 1780259453u, 3475618043u, 212490675u, 622521957u, 917121602u, 1852992332u, 1267987847u, 3170016833u, 2549835613u, 3299763344u, 2864033668u, 3378768767u, 1236609378u, 4169365948u, 3738062408u, 2661022773u, 2006922227u, 2760592161u, 3828932355u, 2636387819u, 2616619070u, 1237256330u, 3449066284u, 2871755260u, 3729280948u, 3862686086u, 431292293u, 3285899651u, 786322314u, 2531158535u, 724901242u, 2377363130u, 1415970351u, 1244759631u, 3263135197u, 965248856u, 174024139u, 2297418515u, 2954777083u, 987586766u, 3206261120u, 4059515114u, 3903854066u, 1931934525u, 2287507921u, 1827135136u, 1781944746u, 574617451u, 2299034788u, 2650140034u, 4081586725u, 2482286699u, 1109175923u, 458483596u, 618705848u, 4059852729u, 1813855658u, 4190721328u, 1129462471u, 4089998050u, 3575732749u, 2375584220u, 1037031473u, 1623777358u, 3389003793u, 546597541u, 352770237u, 1383747654u, 3122687303u, 1646071378u, 1164309901u, 290870767u, 830691298u, 929335420u, 3193251135u, 989577914u, 3626554867u, 591974737u, 3996958215u, 3163711272u, 3071568023u, 1516846461u, 3656006011u, 2698625268u, 2510865430u, 340274176u, 1167681812u, 3698796465u, 3155218919u, 4102288238u, 1673474350u, 3069708839u, 2704165015u, 1237411891u, 1854985978u, 3646837503u, 3625406022u, 921552000u, 1712976649u, 3939149151u, 878608872u, 3406359248u, 1068844551u, 1834682077u, 4155949943u, 2437686324u, 3163786257u, 2645117577u, 1988168803u, 747285578u, 1626463554u, 1235300371u, 1256485167u, 1914142538u, 4141546431u, 3838102563u, 582664250u, 1883344352u, 2083771672u, 2611657933u, 2139079047u, 2250573853u, 804336148u, 3066325351u, 2770847216u, 4275641370u, 1455750577u, 3346357270u, 1674051445u, 601221482u, 3992583643u, 1402445097u, 3622527604u, 2509017299u, 2966108111u, 2557027816u, 900741486u, 1790771021u, 2912643797u, 2631381069u, 4014551783u, 90375300u, 300318232u, 3269968032u, 2679371729u, 2664752123u, 3517585534u, 3253901179u, 542270815u, 1188641600u, 365479232u, 2210121140u, 760762191u, 1273768482u, 1216399252u, 3484324231u, 4287337666u, 16322182u, 643179562u, 325675502u, 3652676161u, 3120716054u, 3330259752u, 1011990087u, 2990167340u, 1097584090u, 3262252593u, 1829409951u, 3665087267u, 1214854475u, 2134299399u, 3704419305u, 411263051u, 1625446136u, 549838529u, 4283196353u, 1342880802u, 3460621305u, 1967599860u, 4282843369u, 1275671016u, 2544665755u, 853593042u, 901109753u, 2682611693u, 110631633u, 797487791u, 1472073141u, 850464484u, 797089608u, 3286110054u, 350397471u, 2775631060u, 366448238u, 3842907484u, 2219863904u, 3623364733u, 1850985302u, 4009616991u, 294963924u, 3693536939u, 3061255808u, 1615375832u, 1920066675u, 4113028420u, 4032223840u, 2318423400u, 2701956286u, 4145497671u, 3991532344u, 2536338351u, 1679099863u, 1728968857u, 449740816u, 2686506989u, 685242457u, 97590863u, 3258354115u, 1502282913u, 1235084019u, 2151665147u, 528459289u, 231097464u, 2477280726u, 3651607391u, 2091754612u, 1178454681u, 980597335u, 1604483865u, 1842333726u, 4146839064u, 3213794286u, 2601416506u, 754220096u, 3571436033u, 488595746u, 1448097974u, 4004834921u, 238887261u, 3320337489u, 1416989070u, 2928916831u, 4093725287u, 186020771u, 2367569534u, 3046087671u, 4090084518u, 3548184546u, 679517009u, 1962659444u, 3539886328u, 4192003933u, 1678423485u, 3827951761u, 3086277222u, 2144472852u, 1390394371u, 2976322029u, 1574517163u, 3553313841u, 119173722u, 1702434637u, 1766260771u, 3629581771u, 1407497759u, 895654784u, 751439914u, 4008409498u, 215917713u, 1482103833u, 695551833u, 1288382231u, 2656990891u, 2581779077u, 1570750352u, 3710689053u, 1741390464u, 2666411616u, 3533987737u, 4289478316u, 3576119563u, 4118694920u, 108199666u, 3869794273u, 963183826u, 2081410737u, 3796810515u, 791123882u, 2525792704u, 1036883117u, 136547246u, 875691100u, 2592925324u, 614302599u, 3013176417u, 2689342539u, 427154472u, 532957601u, 1228758574u, 1898117151u, 1181643858u, 1908591042u, 1464255968u, 446980910u, 2984611177u, 58509511u, 1046943619u, 3508927906u, 2001585786u, 2544767379u, 1525438381u, 552181222u, 1959725830u, 879448844u, 1348536411u, 4242243590u, 2861338018u, 1082052441u, 1034351453u, 601175800u, 764077711u, 530635011u, 3785343245u, 2178026726u, 117256687u, 2378297261u, 457568934u, 76438221u, 4104954272u, 956793873u, 3783168634u, 2485968477u, 2381948487u, 4226929450u, 3148473363u, 2518273601u, 3569490233u, 879369091u, 2180270337u, 3674375989u, 1387729170u, 977997984u, 4270646856u, 568650985u, 951677556u, 4213877384u, 2721005055u, 1073364549u, 2563403831u, 1678669911u, 66786703u, 2273631661u, 1149351924u, 3651298990u, 1581883443u, 246723096u, 1895026827u, 3810605772u, 3711056516u, 4058833288u, 2193790614u, 2080120290u, 3638638708u, 2915672708u, 2263003308u, 2361934197u, 4136767460u, 1976115991u, 3448840877u, 2019238520u, 225333538u, 874340815u, 2976159827u, 1555273378u, 3797521928u, 1942347150u, 3262952567u, 435997738u, 340403353u, 2817830907u, 2078619498u, 749534111u, 1178073973u, 894654712u, 3361226032u, 841092198u, 3288261538u, 1696412169u, 1496966875u, 697501571u, 1059158875u, 3739946319u, 2481012988u, 568983526u, 114945840u, 1559249010u, 2218244008u, 2841706923u, 1632780103u, 4020169654u, 2087949619u, 2438736103u, 24032648u, 833416317u, 3787017905u, 2373238993u, 2575395164u, 3434544481u, 3228481067u, 2542976862u, 2971726178u, 2880371864u, 3642087909u, 2407477975u, 2239080836u, 1043714217u, 3894199764u, 2235879182u, 203853421u, 2933669448u, 2504940536u, 834683330u, 425935223u, 3560796393u, 3565833278u, 1668000829u, 3683399154u, 3414330886u, 1748785729u, 1023171602u, 580966986u, 2531038985u, 3227325488u, 2657385925u, 2124704694u, 233442446u, 1107045577u, 3407293834u, 552770757u, 3899097693u, 1067532701u, 115667924u, 1406028344u, 1707768231u, 3724015962u, 2419657149u, 18613994u, 2532882091u, 3476683808u, 1560838678u, 811220224u, 895961699u, 3762914298u, 1328752423u, 1844996900u, 1420427894u, 1848067707u, 1210281744u, 904215228u, 4055325594u, 1118521573u, 2496554183u, 2579259919u, 3996647489u, 3657647605u, 325254059u, 3136157065u, 3951522674u, 4052925250u, 3341068436u, 2287683323u, 1313073005u, 126005630u, 2505120084u, 1194725057u, 853746559u, 3555092974u, 2689238752u, 49515858u, 1244776042u, 1069300695u, 61073168u, 1010661841u, 1269521335u, 1902040126u, 990632502u, 2378708922u, 3858321250u, 1400735275u, 2974699176u, 2771676666u, 170995186u, 2877798589u, 545726212u, 2225229957u, 1086473152u, 3454177594u, 3859483262u, 1499729584u, 2088002891u, 2883475137u, 3222194252u, 4144472319u, 2212229854u, 4146740722u, 567988835u, 1051332394u, 3932046135u, 542648229u, 3017852446u, 1277887997u, 162888005u, 1669710469u, 1492500905u, 553041029u, 1434876932u, 533989516u, 3817492747u, 584127807u, 4147115982u, 2993670925u, 4020312558u, 710021255u, 3509733475u, 3587959456u, 2088550465u, 1745399498u, 2952242967u, 1259815443u, 869648362u, 1404723176u, 3947542735u, 1334333531u, 3873471582u, 229399758u, 59634866u, 3239516985u, 3844250972u, 1275954779u, 1385684948u, 2243700741u, 2512155003u, 1685649437u, 639306006u, 2524620206u, 955360345u, 1646776457u, 576786501u, 655707039u, 2864351838u, 3736264674u, 655621239u, 362070173u, 1200907897u, 2384379464u, 15823708u, 206117476u, 3652870937u, 122927134u, 1193310960u, 1093099415u, 3696538026u, 4112584792u, 1834541277u, 845639252u, 2069527017u, 547588820u, 4178147211u, 2827259351u, 1764455305u, 3312003602u, 940846775u, 1054995047u, 2976960697u, 1934305529u, 3095615046u, 3354962706u, 2199137382u, 1005722394u, 1875867180u, 2064356511u, 3363633633u, 2688499147u, 4019734130u, 3096333006u, 2069509024u, 2906358341u, 3247463123u, 4191788132u, 2232866485u, 1456016086u, 1422674894u, 867282151u, 1851386407u, 1268304058u, 1612503136u, 1739843072u, 134947567u, 2978775774u, 2051592101u, 1017127033u, 1284167756u, 1090844589u, 831688783u, 2079216362u, 2079309682u, 1950585801u, 1626991196u, 3644714163u, 3678110059u, 898470030u, 1117570988u, 2517572125u, 3916646913u, 3182422972u, 3630426828u, 969847973u, 2835126238u, 53541366u, 3427164640u, 3463937250u, 3044785046u, 897322257u, 103038235u, 3804506837u, 3443872170u, 4185408854u, 2557463241u, 4080940424u, 3669923099u, 2789619871u, 2048168570u, 2429169982u, 3174690447u, 2513494106u, 3099587829u, 2627855577u, 1213061732u, 3143736628u, 3482268149u, 1250714337u, 3553412672u, 2689632914u, 31648125u, 3872383625u, 1565760579u, 36665130u, 1282106920u, 359361724u, 751041229u, 2257179590u, 2915361862u, 280819225u, 954406473u, 4101682199u, 2907818413u, 4254297769u, 3493178615u, 3755944354u, 3539557658u, 3330196096u, 4043533423u, 1134196225u, 4177134659u, 127246419u, 4213770762u, 1978302978u, 2442615581u, 923049607u, 1004426206u, 782768297u, 2702745496u, 1880389457u, 2410586681u, 1430106871u, 4103323427u, 3168399477u, 201787012u, 3105353527u, 3716682375u, 3616334719u, 3413209549u, 656672786u, 526032790u, 2895072131u, 2876965944u, 182894450u, 456581318u, 2683752067u, 1287916294u, 1270745752u, 3877875910u, 3190666241u, 3240336907u, 4024807233u, 4227999465u, 2389301430u, 1681224377u, 1576191191u, 3599250276u, 2381111980u, 3995044500u, 995595530u, 3495321877u, 3956024585u, 1611608524u, 3815677453u, 1520987487u, 3669102590u, 2062334396u, 1656117707u, 5457134u, 3234118251u, 4242065111u, 596879987u, 470187419u, 2688566989u, 3259870297u, 660100446u, 1042378442u, 2206034096u, 442236198u, 2542452448u, 493137955u, 392411099u, 3111186954u, 438250493u, 947967568u, 1234595917u, 4230082284u, 2762976773u, 421203727u, 3728409592u, 2870085764u, 1455086530u, 2762099647u, 4011882747u, 1785430706u, 3684427488u, 1215981925u, 3227517889u, 3269061963u, 4037515364u, 1749401388u, 2167451566u, 3168911474u, 4255057396u, 2026092260u, 1736192508u, 4123254745u, 2319366806u, 3909727042u, 3114708966u, 1938800693u, 680793595u, 3933041672u, 616863613u, 1525265867u, 2808224480u, 2122290603u, 1211197714u, 1186177814u, 2395325006u, 3520488321u, 3979192396u, 3540779343u, 4192918639u, 1763872074u, 3402419930u, 2736030448u, 1120335563u, 1698949078u, 3993310631u, 2947659998u, 1461045789u, 1966048551u, 2228221363u, 597941119u, 3498018399u, 1441110751u, 2229999711u, 393987327u, 454500547u, 1222959566u, 567151340u, 2496952483u, 1708770195u, 3774764786u, 1492844524u, 3308300614u, 805568076u, 4068812294u, 3404648243u, 868414882u, 177406999u, 1608110313u, 642061169u, 2093999089u, 222470301u, 1027515771u, 3131251981u, 2851936150u, 4272755262u, 2763002551u, 1881527822u, 1532845092u, 709643652u, 682573592u, 1244104217u, 440905170u, 1111321746u, 796769556u, 2500467040u, 3002618826u, 1112998535u, 1188525643u, 4212674512u, 1780193104u, 1243644607u, 3691719535u, 2958853053u, 2813437721u, 4036584207u, 466635014u, 2277292580u, 4082276003u, 1030800045u, 1899531424u, 609466946u, 1750863246u, 379050598u, 3576413281u, 731493104u, 2707384133u, 2289193651u, 132259176u, 4115195437u, 1769890695u, 2715470335u, 3348954692u, 2166575624u, 1819263183u, 2028531518u, 2154809766u, 3672399742u, 1142139448u, 88299682u, 76727603u, 4198182186u, 2304993586u, 1666387627u, 2488475423u, 3832777692u, 284366017u, 3359785538u, 3469807328u, 2926494787u, 1914195188u, 1134129972u, 3829072836u, 2493478921u, 3738499303u, 3311304980u, 726951526u, 911080963u, 932916545u, 2235559063u, 2909742396u, 1765719309u, 465269850u, 3803621553u, 1456588655u, 508290328u, 1490719640u, 3356513470u, 2262196163u, 1451774941u, 2908490783u, 251085588u, 830410677u, 3172220325u, 4039692645u, 1383603170u, 3897208579u, 1940535730u, 151909546u, 2384458112u, }; // Return false only if offset is -1 and a spot check of 3 hashes all yield 0. bool Test(int offset, int len = 0) { #undef Check #undef IsAlive #define Check(x) do { \ const uint32_t actual = (x), e = expected[index++]; \ bool ok = actual == e; \ if (!ok) { \ cerr << "expected " << hex << e << " but got " << actual << endl; \ ++errors; \ } \ assert(ok); \ } while (0) #define IsAlive(x) do { alive += IsNonZero(x); } while (0) // After the following line is where the uses of "Check" and such will go. static int index = 0; if (offset == -1) { int alive = 0; { uint64_t h = farmhashxo::Hash64WithSeeds(data, len++, SEED0, SEED1); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } { uint64_t h = farmhashxo::Hash64WithSeed(data, len++, SEED); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } { uint64_t h = farmhashxo::Hash64(data, len++); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } len -= 3; return alive > 0; } { uint64_t h = farmhashxo::Hash64WithSeeds(data + offset, len, SEED0, SEED1); Check(h >> 32); Check((h << 32) >> 32); } { uint64_t h = farmhashxo::Hash64WithSeed(data + offset, len, SEED); Check(h >> 32); Check((h << 32) >> 32); } { uint64_t h = farmhashxo::Hash64(data + offset, len); Check(h >> 32); Check((h << 32) >> 32); } return true; #undef Check #undef IsAlive } int RunTest() { Setup(); int i = 0; cout << "Running farmhashxoTest"; if (!Test(-1)) { cout << "... Unavailable\n"; return NoteErrors(); } // Good. The function is attempting to hash, so run the full test. int errors_prior_to_test = errors; for ( ; i < kTestSize - 1; i++) { Test(i * i, i); } for ( ; i < kDataSize; i += i / 7) { Test(0, i); } Test(0, kDataSize); cout << (errors == errors_prior_to_test ? "... OK\n" : "... Failed\n"); return NoteErrors(); } #else // After the following line is where the code to print hash codes will go. void Dump(int offset, int len) { { uint64_t h = farmhashxo::Hash64WithSeeds(data + offset, len, SEED0, SEED1); cout << (h >> 32) << "u, " << ((h << 32) >> 32) << "u," << endl; } { uint64_t h = farmhashxo::Hash64WithSeed(data + offset, len, SEED); cout << (h >> 32) << "u, " << ((h << 32) >> 32) << "u," << endl; } { uint64_t h = farmhashxo::Hash64(data + offset, len); cout << (h >> 32) << "u, " << ((h << 32) >> 32) << "u," << endl; } } #endif #undef SEED #undef SEED1 #undef SEED0 } // namespace farmhashxoTest #if TESTING static int farmhashxoTestResult = farmhashxoTest::RunTest(); #else int main(int argc, char** argv) { Setup(); cout << "uint32_t expected[] = {\n"; int i = 0; for ( ; i < kTestSize - 1; i++) { farmhashxoTest::Dump(i * i, i); } for ( ; i < kDataSize; i += i / 7) { farmhashxoTest::Dump(0, i); } farmhashxoTest::Dump(0, kDataSize); cout << "};\n"; } #endif #endif // FARMHASHSELFTEST openimageio-1.7.17~dfsg0.orig/src/libutil/filter_test.cpp0000644000175000017500000001300213151711064021604 0ustar mfvmfv/* Copyright 2016 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include #include OIIO_NAMESPACE_USING; static int iterations = 10; static int ntrials = 5; static bool verbose = false; static bool normalize = false; static int graphxres = 1280, graphyres = 500; static int graphyzero = graphyres*3/4; static int graphxzero = graphxres/2; static float graphunit = 200; static void getargs (int argc, char *argv[]) { bool help = false; ArgParse ap; ap.options ("fmath_test\n" OIIO_INTRO_STRING "\n" "Usage: fmath_test [options]", // "%*", parse_files, "", "--help", &help, "Print help message", "-v", &verbose, "Verbose mode", // "--threads %d", &numthreads, // ustring::format("Number of threads (default: %d)", numthreads).c_str(), "--iterations %d", &iterations, ustring::format("Number of iterations (default: %d)", iterations).c_str(), "--trials %d", &ntrials, "Number of trials", "--normalize", &normalize, "Normalize/rescale all filters to peak at 1", NULL); if (ap.parse (argc, (const char**)argv) < 0) { std::cerr << ap.geterror() << std::endl; ap.usage (); exit (EXIT_FAILURE); } if (help) { ap.usage (); exit (EXIT_FAILURE); } } void time_filter (Filter1D *f, const FilterDesc *filtdesc, size_t n) { float ninv = (filtdesc->width/2.0f) / n; for (size_t i = 0; i < n; ++i) { float x = i*ninv; float y = (*f) (x); DoNotOptimize (y); } } int main (int argc, char *argv[]) { #if !defined(NDEBUG) || defined(OIIO_CI) || defined(OIIO_CODECOV) // For the sake of test time, reduce the default iterations for DEBUG, // CI, and code coverage builds. Explicit use of --iters or --trials // will override this, since it comes before the getargs() call. iterations /= 10; ntrials = 1; #endif getargs (argc, argv); ImageBuf graph (ImageSpec (graphxres, graphyres, 3, TypeDesc::UINT8)); float white[3] = { 1, 1, 1 }; float black[3] = { 0, 0, 0 }; ImageBufAlgo::fill (graph, white); ImageBufAlgo::render_line (graph, 0, graphyzero, graphxres-1, graphyzero, black); ImageBufAlgo::render_line (graph, graphxzero, 0, graphxzero, graphyres-1, black); int lastx = 0, lasty = 0; for (int i = 0, e = Filter1D::num_filters(); i < e; ++i) { FilterDesc filtdesc; Filter1D::get_filterdesc (i, &filtdesc); Filter1D *f = Filter1D::create (filtdesc.name, filtdesc.width); float scale = normalize ? 1.0f/(*f)(0.0f) : 1.0f; // Graph it float color[3] = { 0.25f * (i&3), 0.25f * ((i>>2)&3), 0.25f * ((i>>4)&3) }; ImageBufAlgo::render_text (graph, 10, 20+i*20, filtdesc.name, 16, "" /*font name*/, color); for (int x = 0; x < graphxres; ++x) { float xx = float(x-graphxzero) / graphunit; float yy = (*f)(xx) * scale; int y = int (graphyzero - yy * graphunit); if (x > 0) ImageBufAlgo::render_line (graph, lastx, lasty, x, y, color); lastx = x; lasty = y; } // Time it size_t ncalls = 1000000; float time = time_trial (bind (time_filter, f, &filtdesc, ncalls), ntrials, iterations) / iterations; std::cout << Strutil::format ("%-15s %7.1f Mcalls/sec", filtdesc.name, (ncalls/1.0e6)/time) << std::endl; Filter1D::destroy (f); } graph.write ("filters.tif"); return unit_test_failures != 0; } openimageio-1.7.17~dfsg0.orig/src/libutil/SHA1.cpp0000644000175000017500000001723013151711064017763 0ustar mfvmfv/* 100% free public domain implementation of the SHA-1 algorithm by Dominik Reichl Web: http://www.dominik-reichl.de/ See header file for version history. */ // If compiling with MFC, you might want to add #include "StdAfx.h" #include "OpenImageIO/SHA1.h" #include "OpenImageIO/hash.h" #include "OpenImageIO/dassert.h" #ifdef SHA1_UTILITY_FUNCTIONS #define SHA1_MAX_FILE_BUFFER 8000 #endif // Rotate x bits to the left #ifndef ROL32 #ifdef _MSC_VER #define ROL32(_val32,_nBits) _rotl(_val32,_nBits) #else #define ROL32(_val32,_nBits) (((_val32)<<(_nBits))|((_val32)>>(32-(_nBits)))) #endif #endif #ifdef SHA1_LITTLE_ENDIAN #define SHABLK0(i) (m_block->l[i] = \ (ROL32(m_block->l[i],24) & 0xFF00FF00) | (ROL32(m_block->l[i],8) & 0x00FF00FF)) #else #define SHABLK0(i) (m_block->l[i]) #endif #define SHABLK(i) (m_block->l[i&15] = ROL32(m_block->l[(i+13)&15] ^ m_block->l[(i+8)&15] \ ^ m_block->l[(i+2)&15] ^ m_block->l[i&15],1)) // SHA-1 rounds #define _R0(v,w,x,y,z,i) {z+=((w&(x^y))^y)+SHABLK0(i)+0x5A827999+ROL32(v,5);w=ROL32(w,30);} #define _R1(v,w,x,y,z,i) {z+=((w&(x^y))^y)+SHABLK(i)+0x5A827999+ROL32(v,5);w=ROL32(w,30);} #define _R2(v,w,x,y,z,i) {z+=(w^x^y)+SHABLK(i)+0x6ED9EBA1+ROL32(v,5);w=ROL32(w,30);} #define _R3(v,w,x,y,z,i) {z+=(((w|x)&y)|(w&x))+SHABLK(i)+0x8F1BBCDC+ROL32(v,5);w=ROL32(w,30);} #define _R4(v,w,x,y,z,i) {z+=(w^x^y)+SHABLK(i)+0xCA62C1D6+ROL32(v,5);w=ROL32(w,30);} OIIO_NAMESPACE_BEGIN SHA1::SHA1 (const void *data, size_t size) { m_csha1 = new CSHA1; m_final = false; append (data, size); } SHA1::~SHA1 () { delete m_csha1; } void SHA1::append (const void *data, size_t size) { ASSERT (!m_final && "Called SHA1() after already getting digest"); if (data && size) m_csha1->Update ((const unsigned char *)data, (unsigned int)size); } void SHA1::gethash (Hash &h) { if (! m_final) { m_csha1->Final (); m_final = true; } m_csha1->GetHash (h.hash); } std::string SHA1::digest () { if (! m_final) { m_csha1->Final (); m_final = true; } std::string d; m_csha1->ReportHashStl (d, CSHA1::REPORT_HEX_SHORT); return d; } CSHA1::CSHA1() { m_block = (SHA1_WORKSPACE_BLOCK*)m_workspace; Reset(); } CSHA1::~CSHA1() { Reset(); } void CSHA1::Reset() { // SHA1 initialization constants m_state[0] = 0x67452301; m_state[1] = 0xEFCDAB89; m_state[2] = 0x98BADCFE; m_state[3] = 0x10325476; m_state[4] = 0xC3D2E1F0; m_count[0] = 0; m_count[1] = 0; } void CSHA1::Transform(UINT_32* pState, const UINT_8* pBuffer) { UINT_32 a = pState[0], b = pState[1], c = pState[2], d = pState[3], e = pState[4]; memcpy(m_block, pBuffer, 64); // 4 rounds of 20 operations each. Loop unrolled. _R0(a,b,c,d,e, 0); _R0(e,a,b,c,d, 1); _R0(d,e,a,b,c, 2); _R0(c,d,e,a,b, 3); _R0(b,c,d,e,a, 4); _R0(a,b,c,d,e, 5); _R0(e,a,b,c,d, 6); _R0(d,e,a,b,c, 7); _R0(c,d,e,a,b, 8); _R0(b,c,d,e,a, 9); _R0(a,b,c,d,e,10); _R0(e,a,b,c,d,11); _R0(d,e,a,b,c,12); _R0(c,d,e,a,b,13); _R0(b,c,d,e,a,14); _R0(a,b,c,d,e,15); _R1(e,a,b,c,d,16); _R1(d,e,a,b,c,17); _R1(c,d,e,a,b,18); _R1(b,c,d,e,a,19); _R2(a,b,c,d,e,20); _R2(e,a,b,c,d,21); _R2(d,e,a,b,c,22); _R2(c,d,e,a,b,23); _R2(b,c,d,e,a,24); _R2(a,b,c,d,e,25); _R2(e,a,b,c,d,26); _R2(d,e,a,b,c,27); _R2(c,d,e,a,b,28); _R2(b,c,d,e,a,29); _R2(a,b,c,d,e,30); _R2(e,a,b,c,d,31); _R2(d,e,a,b,c,32); _R2(c,d,e,a,b,33); _R2(b,c,d,e,a,34); _R2(a,b,c,d,e,35); _R2(e,a,b,c,d,36); _R2(d,e,a,b,c,37); _R2(c,d,e,a,b,38); _R2(b,c,d,e,a,39); _R3(a,b,c,d,e,40); _R3(e,a,b,c,d,41); _R3(d,e,a,b,c,42); _R3(c,d,e,a,b,43); _R3(b,c,d,e,a,44); _R3(a,b,c,d,e,45); _R3(e,a,b,c,d,46); _R3(d,e,a,b,c,47); _R3(c,d,e,a,b,48); _R3(b,c,d,e,a,49); _R3(a,b,c,d,e,50); _R3(e,a,b,c,d,51); _R3(d,e,a,b,c,52); _R3(c,d,e,a,b,53); _R3(b,c,d,e,a,54); _R3(a,b,c,d,e,55); _R3(e,a,b,c,d,56); _R3(d,e,a,b,c,57); _R3(c,d,e,a,b,58); _R3(b,c,d,e,a,59); _R4(a,b,c,d,e,60); _R4(e,a,b,c,d,61); _R4(d,e,a,b,c,62); _R4(c,d,e,a,b,63); _R4(b,c,d,e,a,64); _R4(a,b,c,d,e,65); _R4(e,a,b,c,d,66); _R4(d,e,a,b,c,67); _R4(c,d,e,a,b,68); _R4(b,c,d,e,a,69); _R4(a,b,c,d,e,70); _R4(e,a,b,c,d,71); _R4(d,e,a,b,c,72); _R4(c,d,e,a,b,73); _R4(b,c,d,e,a,74); _R4(a,b,c,d,e,75); _R4(e,a,b,c,d,76); _R4(d,e,a,b,c,77); _R4(c,d,e,a,b,78); _R4(b,c,d,e,a,79); // Add the working vars back into state pState[0] += a; pState[1] += b; pState[2] += c; pState[3] += d; pState[4] += e; // Wipe variables #ifdef SHA1_WIPE_VARIABLES a = b = c = d = e = 0; #endif } // Use this function to hash in binary data and strings void CSHA1::Update(const UINT_8* pbData, UINT_32 uLen) { UINT_32 j = ((m_count[0] >> 3) & 0x3F); if((m_count[0] += (uLen << 3)) < (uLen << 3)) ++m_count[1]; // Overflow m_count[1] += (uLen >> 29); UINT_32 i; if((j + uLen) > 63) { i = 64 - j; memcpy(&m_buffer[j], pbData, i); Transform(m_state, m_buffer); for( ; (i + 63) < uLen; i += 64) Transform(m_state, &pbData[i]); j = 0; } else i = 0; if((uLen - i) != 0) memcpy(&m_buffer[j], &pbData[i], uLen - i); } #ifdef SHA1_UTILITY_FUNCTIONS // Hash in file contents bool CSHA1::HashFile(const TCHAR* tszFileName) { if(tszFileName == NULL) return false; FILE* fpIn = _tfopen(tszFileName, _T("rb")); if(fpIn == NULL) return false; _fseeki64(fpIn, 0, SEEK_END); const INT_64 lFileSize = _ftelli64(fpIn); _fseeki64(fpIn, 0, SEEK_SET); const INT_64 lMaxBuf = SHA1_MAX_FILE_BUFFER; UINT_8 vData[SHA1_MAX_FILE_BUFFER]; INT_64 lRemaining = lFileSize; while(lRemaining > 0) { const size_t uMaxRead = static_cast((lRemaining > lMaxBuf) ? lMaxBuf : lRemaining); const size_t uRead = fread(vData, 1, uMaxRead, fpIn); if(uRead == 0) { fclose(fpIn); return false; } Update(vData, static_cast(uRead)); lRemaining -= static_cast(uRead); } fclose(fpIn); return (lRemaining == 0); } #endif void CSHA1::Final() { UINT_32 i; UINT_8 finalcount[8]; for(i = 0; i < 8; ++i) finalcount[i] = (UINT_8)((m_count[((i >= 4) ? 0 : 1)] >> ((3 - (i & 3)) * 8) ) & 255); // Endian independent Update((UINT_8*)"\200", 1); while ((m_count[0] & 504) != 448) Update((UINT_8*)"\0", 1); Update(finalcount, 8); // Cause a SHA1Transform() for(i = 0; i < 20; ++i) m_digest[i] = (UINT_8)((m_state[i >> 2] >> ((3 - (i & 3)) * 8)) & 0xFF); // Wipe variables for security reasons #ifdef SHA1_WIPE_VARIABLES memset(m_buffer, 0, 64); memset(m_state, 0, 20); memset(m_count, 0, 8); memset(finalcount, 0, 8); Transform(m_state, m_buffer); #endif } #ifdef SHA1_UTILITY_FUNCTIONS // Get the final hash as a pre-formatted string bool CSHA1::ReportHash(TCHAR* tszReport, REPORT_TYPE rtReportType) const { if(tszReport == NULL) return false; TCHAR tszTemp[16]; if((rtReportType == REPORT_HEX) || (rtReportType == REPORT_HEX_SHORT)) { _sntprintf(tszTemp, 15, _T("%02X"), m_digest[0]); _tcscpy(tszReport, tszTemp); const TCHAR* lpFmt = ((rtReportType == REPORT_HEX) ? _T(" %02X") : _T("%02X")); for(size_t i = 1; i < 20; ++i) { _sntprintf(tszTemp, 15, lpFmt, m_digest[i]); _tcscat(tszReport, tszTemp); } } else if(rtReportType == REPORT_DIGIT) { _sntprintf(tszTemp, 15, _T("%u"), m_digest[0]); _tcscpy(tszReport, tszTemp); for(size_t i = 1; i < 20; ++i) { _sntprintf(tszTemp, 15, _T(" %u"), m_digest[i]); _tcscat(tszReport, tszTemp); } } else return false; return true; } #endif #ifdef SHA1_STL_FUNCTIONS bool CSHA1::ReportHashStl(std::basic_string& strOut, REPORT_TYPE rtReportType) const { TCHAR tszOut[84]; const bool bResult = ReportHash(tszOut, rtReportType); if(bResult) strOut = tszOut; return bResult; } #endif // Get the raw message digest bool CSHA1::GetHash(UINT_8* pbDest) const { if(pbDest == NULL) return false; memcpy(pbDest, m_digest, 20); return true; } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libutil/array_view_test.cpp0000644000175000017500000003317213151711064022501 0ustar mfvmfv/* Copyright 2014 Larry Gritz, et al. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include "OpenImageIO/strided_ptr.h" #include "OpenImageIO/array_view.h" #include "OpenImageIO/image_view.h" #include "OpenImageIO/unittest.h" OIIO_NAMESPACE_USING; void test_offset () { // Test default constructor offset<1> off1_default; offset<2> off2_default; OIIO_CHECK_EQUAL (off1_default[0], 0); OIIO_CHECK_EQUAL (off2_default[0], 0); OIIO_CHECK_EQUAL (off2_default[0], 0); // Test explitit initializers offset<1> off1 (42), off1b(10); offset<2> off2 (14, 43), off2b(10,12); OIIO_CHECK_EQUAL (off1[0], 42); OIIO_CHECK_EQUAL (off2[0], 14); OIIO_CHECK_EQUAL (off2[1], 43); // test == and != OIIO_CHECK_EQUAL (off1, off1); OIIO_CHECK_NE (off1, off1b); OIIO_CHECK_EQUAL (off2, off2); OIIO_CHECK_NE (off2, off2b); // test arithmetic OIIO_CHECK_EQUAL (off1+off1b, offset<1>(52)); OIIO_CHECK_EQUAL (off1-off1b, offset<1>(32)); OIIO_CHECK_EQUAL (-off1, offset<1>(-42)); OIIO_CHECK_EQUAL (off1*2, offset<1>(84)); OIIO_CHECK_EQUAL (off1/2, offset<1>(21)); OIIO_CHECK_EQUAL (off2+off2b, offset<2>(24,55)); OIIO_CHECK_EQUAL (off2-off2b, offset<2>(4,31)); OIIO_CHECK_EQUAL (-off2, offset<2>(-14,-43)); OIIO_CHECK_EQUAL (off2b*2, offset<2>(20,24)); OIIO_CHECK_EQUAL (off2b/2, offset<2>(5,6)); { offset<1> o = off1; ++o; OIIO_CHECK_EQUAL(o[0], 43); } { offset<1> o = off1; o++; OIIO_CHECK_EQUAL(o[0], 43); } { offset<1> o = off1; --o; OIIO_CHECK_EQUAL(o[0], 41); } { offset<1> o = off1; o--; OIIO_CHECK_EQUAL(o[0], 41); } { offset<1> o = off1; o += off1b; OIIO_CHECK_EQUAL(o[0], 52); } { offset<1> o = off1; o -= off1b; OIIO_CHECK_EQUAL(o[0], 32); } { offset<1> o = off1; o *= 2; OIIO_CHECK_EQUAL(o[0], 84); } { offset<1> o = off1; o /= 2; OIIO_CHECK_EQUAL(o[0], 21); } { offset<2> o = off2; o += off2b; OIIO_CHECK_EQUAL(o, offset<2>(24,55)); } { offset<2> o = off2; o -= off2b; OIIO_CHECK_EQUAL(o, offset<2>(4,31)); } { offset<2> o = off2b; o *= 2; OIIO_CHECK_EQUAL(o, offset<2>(20,24)); } { offset<2> o = off2b; o /= 2; OIIO_CHECK_EQUAL(o, offset<2>(5,6)); } #if OIIO_CPLUSPLUS_VERSION >= 11 { // test initializer list offset<1> off1 {42}; offset<2> off2 {14, 43}; OIIO_CHECK_EQUAL (off1[0], 42); OIIO_CHECK_EQUAL (off2[0], 14); OIIO_CHECK_EQUAL (off2[1], 43); } #endif } void test_bounds () { // Test default constructor bounds<1> b1_default; bounds<2> b2_default; OIIO_CHECK_EQUAL (b1_default[0], 0); OIIO_CHECK_EQUAL (b2_default[0], 0); OIIO_CHECK_EQUAL (b2_default[0], 0); // Test explitit initializers bounds<1> b1 (42), b1b(10); bounds<2> b2 (14, 43), b2b(10,12); OIIO_CHECK_EQUAL (b1[0], 42); OIIO_CHECK_EQUAL (b2[0], 14); OIIO_CHECK_EQUAL (b2[1], 43); // test == and != OIIO_CHECK_EQUAL (b1, b1); OIIO_CHECK_NE (b1, b1b); OIIO_CHECK_EQUAL (b2, b2); OIIO_CHECK_NE (b2, b2b); // test arithmetic offset<1> off1b(10); offset<2> off2b(10,12); OIIO_CHECK_EQUAL (b1+off1b, bounds<1>(52)); OIIO_CHECK_EQUAL (b1-off1b, bounds<1>(32)); OIIO_CHECK_EQUAL (b1*2, bounds<1>(84)); OIIO_CHECK_EQUAL (b1/2, bounds<1>(21)); OIIO_CHECK_EQUAL (b2+off2b, bounds<2>(24,55)); OIIO_CHECK_EQUAL (b2-off2b, bounds<2>(4,31)); OIIO_CHECK_EQUAL (b2b*2, bounds<2>(20,24)); OIIO_CHECK_EQUAL (b2b/2, bounds<2>(5,6)); { bounds<1> b = b1; b += off1b; OIIO_CHECK_EQUAL(b[0], 52); } { bounds<1> b = b1; b -= off1b; OIIO_CHECK_EQUAL(b[0], 32); } { bounds<1> b = b1; b *= 2; OIIO_CHECK_EQUAL(b[0], 84); } { bounds<1> b = b1; b /= 2; OIIO_CHECK_EQUAL(b[0], 21); } { bounds<2> b = b2; b += off2b; OIIO_CHECK_EQUAL(b, bounds<2>(24,55)); } { bounds<2> b = b2; b -= off2b; OIIO_CHECK_EQUAL(b, bounds<2>(4,31)); } { bounds<2> b = b2b; b *= 2; OIIO_CHECK_EQUAL(b, bounds<2>(20,24)); } { bounds<2> b = b2b; b /= 2; OIIO_CHECK_EQUAL(b, bounds<2>(5,6)); } // test iterators { bounds<1> b (3); bounds<1>::iterator i = b.begin(); OIIO_CHECK_EQUAL (*i, 0); OIIO_CHECK_NE (i, b.end()); ++i; OIIO_CHECK_EQUAL (*i, 1); OIIO_CHECK_NE (i, b.end()); --i; OIIO_CHECK_EQUAL (*i, 0); ++i; i++; i--; OIIO_CHECK_EQUAL (*i, 1); ++i; OIIO_CHECK_EQUAL (*i, 2); i -= 1; OIIO_CHECK_EQUAL (*i, 1); i += 1; OIIO_CHECK_EQUAL (*i, 2); OIIO_CHECK_NE (i, b.end()); i += 1; OIIO_CHECK_EQUAL (i, b.end()); } { bounds<2> b (2, 2); bounds<2>::iterator i = b.begin(); OIIO_CHECK_EQUAL (*i, offset<2>(0,0)); OIIO_CHECK_NE (i, b.end()); ++i; OIIO_CHECK_EQUAL (*i, offset<2>(0,1)); OIIO_CHECK_NE (i, b.end()); --i; OIIO_CHECK_EQUAL (*i, offset<2>(0,0)); ++i; ++i; OIIO_CHECK_EQUAL (*i, offset<2>(1,0)); OIIO_CHECK_NE (i, b.end()); i--; OIIO_CHECK_EQUAL (*i, offset<2>(0,1)); i++; ++i; OIIO_CHECK_EQUAL (*i, offset<2>(1,1)); OIIO_CHECK_NE (i, b.end()); i -= 1; OIIO_CHECK_EQUAL (*i, offset<2>(1,0)); i += 1; ++i; OIIO_CHECK_EQUAL (i, b.end()); } #if OIIO_CPLUSPLUS_VERSION >= 11 { // test initializer list bounds<1> b1 {42}; bounds<2> b2 {14, 43}; OIIO_CHECK_EQUAL (b1[0], 42); OIIO_CHECK_EQUAL (b2[0], 14); OIIO_CHECK_EQUAL (b2[1], 43); } #endif } void test_array_view () { static float A[] = { 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 0 }; array_view a (A); OIIO_CHECK_EQUAL (a.size(), 12); OIIO_CHECK_EQUAL (a[0], 0.0f); OIIO_CHECK_EQUAL (a[1], 1.0f); OIIO_CHECK_EQUAL (a[2], 0.0f); OIIO_CHECK_EQUAL (a[3], 2.0f); // array_view::const_iterator i = a.begin(); // OIIO_CHECK_EQUAL (*i, 0.0f); // ++i; OIIO_CHECK_EQUAL (*i, 1.0f); } void test_array_view_mutable () { float A[] = { 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 0 }; array_view a (A); OIIO_CHECK_EQUAL (a.size(), 12); OIIO_CHECK_EQUAL (a[0], 0.0f); OIIO_CHECK_EQUAL (a[1], 1.0f); OIIO_CHECK_EQUAL (a[2], 0.0f); OIIO_CHECK_EQUAL (a[3], 2.0f); a[2] = 42.0f; OIIO_CHECK_EQUAL (a[2], 42.0f); // array_view::const_iterator i = a.begin(); // OIIO_CHECK_EQUAL (*i, 0.0f); // ++i; OIIO_CHECK_EQUAL (*i, 1.0f); } void test_array_view_initlist () { #if OIIO_CPLUSPLUS_VERSION >= 11 // Try the array_view syntax with initializer_list. array_view a { 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 0 }; OIIO_CHECK_EQUAL (a.size(), 12); OIIO_CHECK_EQUAL (a[0], 0.0f); OIIO_CHECK_EQUAL (a[1], 1.0f); OIIO_CHECK_EQUAL (a[2], 0.0f); OIIO_CHECK_EQUAL (a[3], 2.0f); #endif } void test_array_view_2D () { float A[] = { 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 0 }; array_view a (A, bounds<2>(4, 3)); OIIO_CHECK_EQUAL (a.bounds()[0], 4); OIIO_CHECK_EQUAL (a.bounds()[1], 3); OIIO_CHECK_EQUAL (a.size(), 12); OIIO_CHECK_EQUAL (a[offset<2>(0,0)], 0.0f); OIIO_CHECK_EQUAL (a[offset<2>(0,1)], 1.0f); OIIO_CHECK_EQUAL (a[offset<2>(0,2)], 0.0f); OIIO_CHECK_EQUAL (a[offset<2>(1,0)], 2.0f); OIIO_CHECK_EQUAL (a[offset<2>(1,1)], 0.0f); OIIO_CHECK_EQUAL (a[offset<2>(1,2)], 3.0f); #if 0 // Test this after we add slicing OIIO_CHECK_EQUAL (a[0][0], 0.0f); OIIO_CHECK_EQUAL (a[0][1], 1.0f); OIIO_CHECK_EQUAL (a[0][2], 0.0f); OIIO_CHECK_EQUAL (a[1][0], 2.0f); OIIO_CHECK_EQUAL (a[1][1], 0.0f); OIIO_CHECK_EQUAL (a[1][2], 3.0f); #endif } void test_const_strided_ptr () { static const float A[] = { 0, 1, 0, 2, 0, 3, 0, 4, 0, 5 }; // Make sure it works with unit stride strided_ptr a (A); OIIO_CHECK_EQUAL (*a, 0.0f); OIIO_CHECK_EQUAL (a[0], 0.0f); OIIO_CHECK_EQUAL (a[1], 1.0f); OIIO_CHECK_EQUAL (a[2], 0.0f); OIIO_CHECK_EQUAL (a[3], 2.0f); // All the other tests are with stride of 2 elements a = strided_ptr (&A[1], 2*sizeof(A[0])); OIIO_CHECK_EQUAL (*a, 1.0f); OIIO_CHECK_EQUAL (a[0], 1.0f); OIIO_CHECK_EQUAL (a[1], 2.0f); OIIO_CHECK_EQUAL (a[2], 3.0f); OIIO_CHECK_EQUAL (a[3], 4.0f); ++a; OIIO_CHECK_EQUAL (*a, 2.0f); a++; OIIO_CHECK_EQUAL (*a, 3.0f); ++a; OIIO_CHECK_EQUAL (*a, 4.0f); --a; OIIO_CHECK_EQUAL (*a, 3.0f); a--; OIIO_CHECK_EQUAL (*a, 2.0f); a += 2; OIIO_CHECK_EQUAL (*a, 4.0f); a -= 2; OIIO_CHECK_EQUAL (*a, 2.0f); a = a + 2; OIIO_CHECK_EQUAL (*a, 4.0f); a = a - 2; OIIO_CHECK_EQUAL (*a, 2.0f); } void test_strided_ptr () { static float A[] = { 0, 1, 0, 2, 0, 3, 0, 4, 0, 5 }; // Make sure it works with unit stride strided_ptr a (A); OIIO_CHECK_EQUAL (*a, 0.0f); OIIO_CHECK_EQUAL (a[0], 0.0f); OIIO_CHECK_EQUAL (a[1], 1.0f); OIIO_CHECK_EQUAL (a[2], 0.0f); OIIO_CHECK_EQUAL (a[3], 2.0f); // All the other tests are with stride of 2 elements a = strided_ptr (&A[1], 2*sizeof(A[0])); OIIO_CHECK_EQUAL (*a, 1.0f); OIIO_CHECK_EQUAL (a[0], 1.0f); OIIO_CHECK_EQUAL (a[1], 2.0f); OIIO_CHECK_EQUAL (a[2], 3.0f); OIIO_CHECK_EQUAL (a[3], 4.0f); ++a; OIIO_CHECK_EQUAL (*a, 2.0f); a++; OIIO_CHECK_EQUAL (*a, 3.0f); ++a; OIIO_CHECK_EQUAL (*a, 4.0f); --a; OIIO_CHECK_EQUAL (*a, 3.0f); a--; OIIO_CHECK_EQUAL (*a, 2.0f); a += 2; OIIO_CHECK_EQUAL (*a, 4.0f); a -= 2; OIIO_CHECK_EQUAL (*a, 2.0f); a = a + 2; OIIO_CHECK_EQUAL (*a, 4.0f); a = a - 2; OIIO_CHECK_EQUAL (*a, 2.0f); *a = 14.0; OIIO_CHECK_EQUAL (*a, 14.0f); } void test_array_view_strided () { static const float A[] = { 0, 1, 0, 2, 0, 3, 0, 4, 0, 5 }; array_view_strided a (&A[1], 5, 2); OIIO_CHECK_EQUAL (a.size(), 5); OIIO_CHECK_EQUAL (a[0], 1.0f); OIIO_CHECK_EQUAL (a[1], 2.0f); OIIO_CHECK_EQUAL (a[2], 3.0f); OIIO_CHECK_EQUAL (a[3], 4.0f); // array_view_strided::const_iterator i = a.begin(); // OIIO_CHECK_EQUAL (*i, 1.0f); // ++i; OIIO_CHECK_EQUAL (*i, 2.0f); } void test_array_view_strided_mutable () { static float A[] = { 0, 1, 0, 2, 0, 3, 0, 4, 0, 5 }; array_view_strided a (&A[1], 5, 2); OIIO_CHECK_EQUAL (a.size(), 5); OIIO_CHECK_EQUAL (a[0], 1.0f); OIIO_CHECK_EQUAL (a[1], 2.0f); OIIO_CHECK_EQUAL (a[2], 3.0f); OIIO_CHECK_EQUAL (a[3], 4.0f); // array_view_strided::iterator i = a.begin(); // OIIO_CHECK_EQUAL (*i, 1.0f); // ++i; OIIO_CHECK_EQUAL (*i, 2.0f); } void test_image_view () { const int X = 4, Y = 3, C = 3, Z = 1; static const float IMG[Z][Y][X][C] = { // 4x3 2D image with 3 channels {{{0,0,0}, {1,0,1}, {2,0,2}, {3,0,3}}, {{0,1,4}, {1,1,5}, {2,1,6}, {3,1,7}}, {{0,2,8}, {1,2,9}, {2,2,10}, {3,2,11}}} }; image_view I ((const float *)IMG, 3, 4, 3); for (int y = 0, i = 0; y < Y; ++y) { for (int x = 0; x < X; ++x, ++i) { OIIO_CHECK_EQUAL (I(x,y)[0], x); OIIO_CHECK_EQUAL (I(x,y)[1], y); OIIO_CHECK_EQUAL (I(x,y)[2], i); } } } void test_image_view_mutable () { const int X = 4, Y = 3, C = 3, Z = 1; static float IMG[Z][Y][X][C] = { // 4x3 2D image with 3 channels {{{0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}}, {{0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}}, {{0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}}} }; image_view I ((float *)IMG, 3, 4, 3); for (int y = 0, i = 0; y < Y; ++y) { for (int x = 0; x < X; ++x, ++i) { I(x,y)[0] = x; I(x,y)[1] = y; I(x,y)[2] = i; } } for (int y = 0, i = 0; y < Y; ++y) { for (int x = 0; x < X; ++x, ++i) { OIIO_CHECK_EQUAL (I(x,y)[0], x); OIIO_CHECK_EQUAL (I(x,y)[1], y); OIIO_CHECK_EQUAL (I(x,y)[2], i); } } } int main (int argc, char *argv[]) { test_offset (); test_bounds (); test_array_view (); test_array_view_mutable (); test_array_view_initlist (); test_array_view_2D (); test_const_strided_ptr (); test_strided_ptr (); test_array_view_strided (); test_array_view_strided_mutable (); test_image_view (); test_image_view_mutable (); return unit_test_failures; } openimageio-1.7.17~dfsg0.orig/src/libutil/argparse.cpp0000644000175000017500000004273313151711064021101 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Based on BSD-licensed software Copyright 2004 NVIDIA Corp. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include #include #include "OpenImageIO/strutil.h" #include "OpenImageIO/sysutil.h" #include "OpenImageIO/argparse.h" #include "OpenImageIO/dassert.h" OIIO_NAMESPACE_BEGIN class ArgOption { public: typedef int (*callback_t) (int, const char**); ArgOption (const char *str); ~ArgOption () { } int initialize (); int parameter_count () const { return m_count; } const std::string & name() const { return m_flag; } const std::string & fmt() const { return m_format; } bool is_flag () const { return m_type == Flag; } bool is_reverse_flag () const { return m_type == ReverseFlag; } bool is_sublist () const { return m_type == Sublist; } bool is_regular () const { return m_type == Regular; } bool has_callback () const { return m_has_callback; } void add_parameter (int i, void *p); void set_parameter (int i, const char *argv); void add_argument (const char *argv); int invoke_callback () const; int invoke_callback (int argc, const char **argv) const { return m_callback ? m_callback (argc, argv) : 0; } void set_callback (callback_t cb) { m_callback = cb; } void found_on_command_line () { m_repetitions++; } int parsed_count () const { return m_repetitions; } void description (const char *d) { m_descript = d; } const std::string & description() const { return m_descript; } bool is_separator () const { return fmt() == ""; } private: enum OptionType { None, Regular, Flag, ReverseFlag, Sublist }; std::string m_format; // original format string std::string m_flag; // just the -flag_foo part std::string m_code; // paramter types, eg "df" std::string m_descript; OptionType m_type; int m_count; // number of parameters std::vector m_param; // pointers to app data vars callback_t m_callback; int m_repetitions; // number of times on cmd line bool m_has_callback; // needs a callback? std::vector m_argv; }; // Constructor. Does not do any parsing or error checking. // Make sure to call initialize() right after construction. ArgOption::ArgOption (const char *str) : m_format(str), m_type(None), m_count(0), m_callback(NULL), m_repetitions(0), m_has_callback(false) { } // Parses the format string ("-option %s %d %f") to extract the // flag ("-option") and create a code string ("sdf"). After the // code string is created, the param list of void* pointers is // allocated to hold the argument variable pointers. int ArgOption::initialize() { size_t n; const char *s; if (m_format.empty() || m_format == "%*") { m_type = Sublist; m_count = 1; // sublist callback function pointer m_code = "*"; m_flag = ""; } else if (is_separator()) { } else { // extract the flag name s = &m_format[0]; assert(*s == '-'); assert(isalpha(s[1]) || (s[1] == '-' && isalpha(s[2]))); s++; if (*s == '-') s++; while (isalnum(*s) || *s == '_' || *s == '-') s++; if (! *s) { m_flag = m_format; m_type = Flag; m_count = 1; m_code = "b"; } else { n = s - (&m_format[0]); m_flag.assign (m_format.begin(), m_format.begin()+n); // Parse the scanf-like parameters m_type = Regular; n = (m_format.length() - n) / 2; // conservative estimate m_code.clear (); while (*s != '\0') { if (*s == '%') { s++; assert(*s != '\0'); m_count++; // adding another parameter switch (*s) { case 'd': // 32bit int case 'g': // float case 'f': // float case 'F': // double case 's': // string case 'L': // vector assert (m_type == Regular); m_code += *s; break; case '!': m_type = ReverseFlag; m_code += *s; break; case '*': assert(m_count == 1); m_type = Sublist; break; case '@': m_has_callback = true; --m_count; break; default: std::cerr << "Programmer error: Unknown option "; std::cerr << "type string \"" << *s << "\"" << "\n"; abort(); } } s++; } // Catch the case where only a callback was given, it's still // a bool. if (! *s && m_count == 0 && m_has_callback) { m_type = Flag; m_count = 1; m_code = "b"; } } } // A few replacements to tidy up the format string for printing size_t loc; while ((loc = m_format.find("%L")) != std::string::npos) m_format.replace (loc, 2, "%s"); while ((loc = m_format.find("%!")) != std::string::npos) m_format.replace (loc, 2, ""); while ((loc = m_format.find("%@")) != std::string::npos) m_format.replace (loc, 2, ""); // Allocate space for the parameter pointers and initialize to NULL m_param.resize (m_count, NULL); return 0; } // Stores the pointer to an argument in the param list and // initializes flag options to FALSE. // FIXME -- there is no such initialization. Bug? void ArgOption::add_parameter (int i, void *p) { assert (i >= 0 && i < m_count); m_param[i] = p; } // Given a string from argv, set the associated option parameter // at index i using the format conversion code in the code string. void ArgOption::set_parameter (int i, const char *argv) { assert(i < m_count); if (! m_param[i]) // If they passed NULL as the address, return; // don't write anything. switch (m_code[i]) { case 'd': *(int *)m_param[i] = atoi(argv); break; case 'f': case 'g': *(float *)m_param[i] = (float)atof(argv); break; case 'F': *(double *)m_param[i] = atof(argv); break; case 's': *(std::string *)m_param[i] = argv; break; case 'S': *(std::string *)m_param[i] = argv; break; case 'L': ((std::vector *)m_param[i])->push_back (argv); break; case 'b': *(bool *)m_param[i] = true; break; case '!': *(bool *)m_param[i] = false; break; case '*': default: abort(); } } // Call the sublist callback if any arguments have been parsed int ArgOption::invoke_callback () const { assert (m_count == 1); int argc = (int) m_argv.size(); if (argc == 0) return 0; // Convert the argv's to char*[] const char **myargv = (const char **) alloca (argc * sizeof(const char *)); for (int i = 0; i < argc; ++i) myargv[i] = m_argv[i].c_str(); return invoke_callback (argc, myargv); } // Add an argument to this sublist option void ArgOption::add_argument (const char *argv) { m_argv.push_back (argv); } ArgParse::ArgParse (int argc, const char **argv) : m_argc(argc), m_argv(argv), m_global(NULL) { } ArgParse::~ArgParse() { for (unsigned int i=0; ifound_on_command_line(); if (option->is_flag() || option->is_reverse_flag()) { option->set_parameter(0, NULL); if (option->has_callback()) option->invoke_callback (1, m_argv+i); } else { ASSERT (option->is_regular()); for (int j = 0; j < option->parameter_count(); j++) { if (j+i+1 >= m_argc) { error ("Missing parameter %d from option " "\"%s\"", j+1, option->name().c_str()); return -1; } option->set_parameter (j, m_argv[i+j+1]); } if (option->has_callback()) option->invoke_callback (1+option->parameter_count(), m_argv+i); i += option->parameter_count(); } } else { // not an option nor an option parameter, glob onto global list if (m_global) m_global->invoke_callback (1, m_argv+i); else { error ("Argument \"%s\" does not have an associated " "option", m_argv[i]); return -1; } } } return 0; } // Primary entry point. This function accepts a set of format strings // and variable pointers. Each string contains an option name and a // scanf-like format string to enumerate the arguments of that option // (eg. "-option %d %f %s"). The format string is followed by a list // of pointers to the argument variables, just like scanf. All format // strings and arguments are parsed to create a list of ArgOption objects. // After all ArgOptions are created, the command line is parsed and // the sublist option callbacks are invoked. int ArgParse::options (const char *intro, ...) { va_list ap; va_start (ap, intro); m_intro += intro; for (const char *cur = va_arg(ap, char *); cur; cur = va_arg(ap, char *)) { if (find_option (cur) && strcmp(cur, "")) { error ("Option \"%s\" is multiply defined", cur); return -1; } // Build a new option and then parse the values ArgOption *option = new ArgOption (cur); if (option->initialize() < 0) { return -1; } if (cur[0] == '\0' || (cur[0] == '%' && cur[1] == '*' && cur[2] == '\0')) { // set default global option m_global = option; } if (option->has_callback()) option->set_callback ((ArgOption::callback_t)va_arg(ap,void*)); // Grab any parameters and store them with this option for (int i = 0; i < option->parameter_count(); i++) { void *p = va_arg (ap, void *); option->add_parameter (i, p); if (option == m_global) option->set_callback ((ArgOption::callback_t)p); } // Last argument is description option->description ((const char *) va_arg (ap, const char *)); m_option.push_back(option); } va_end (ap); return 0; } // Find an option by name in the option vector ArgOption * ArgParse::find_option (const char *name) { for (std::vector::const_iterator i = m_option.begin(); i != m_option.end(); i++) { const char *opt = (*i)->name().c_str(); if (! strcmp(name, opt)) return *i; // Match even if the user mixes up one dash or two if (name[0] == '-' && name[1] == '-' && opt[0] == '-' && opt[1] != '-') if (! strcmp (name+1, opt)) return *i; if (name[0] == '-' && name[1] != '-' && opt[0] == '-' && opt[1] == '-') if (! strcmp (name, opt+1)) return *i; } return NULL; } int ArgParse::found (const char *option_name) { ArgOption *option = find_option(option_name); if (option == NULL) return 0; return option->parsed_count(); } std::string ArgParse::geterror () const { std::string e = m_errmessage; m_errmessage.clear (); return e; } void ArgParse::usage () const { const size_t longline = 35; std::cout << m_intro << '\n'; size_t maxlen = 0; for (unsigned int i=0; ifmt().length(); // Option lists > 40 chars will be split into multiple lines if (fmtlen < longline) maxlen = std::max (maxlen, fmtlen); } // Try to figure out how wide the terminal is, so we can word wrap. int columns = Sysutil::terminal_columns (); for (unsigned int i=0; idescription().length()) { size_t fmtlen = opt->fmt().length(); if (opt->is_separator()) { std::cout << Strutil::wordwrap(opt->description(), columns-2, 0) << '\n'; } else { std::cout << " " << opt->fmt(); if (fmtlen < longline) std::cout << std::string (maxlen + 2 - fmtlen, ' '); else std::cout << "\n " << std::string (maxlen + 2, ' '); std::cout << Strutil::wordwrap(opt->description(), columns-2, (int)maxlen+2+4+2) << '\n'; } } } } void ArgParse::briefusage () const { std::cout << m_intro << '\n'; // Try to figure out how wide the terminal is, so we can word wrap. int columns = Sysutil::terminal_columns (); std::string pending; for (unsigned int i=0; idescription().length()) { if (opt->is_separator()) { if (pending.size()) std::cout << " " << Strutil::wordwrap(pending, columns-2, 4) << '\n'; pending.clear (); std::cout << Strutil::wordwrap(opt->description(), columns-2, 0) << '\n'; } else { pending += opt->name() + " "; } } } if (pending.size()) std::cout << " " << Strutil::wordwrap(pending, columns-2, 4) << '\n'; } std::string ArgParse::command_line () const { std::string s; for (int i = 0; i < m_argc; ++i) { if (strchr (m_argv[i], ' ')) { s += '\"'; s += m_argv[i]; s += '\"'; } else { s += m_argv[i]; } if (i < m_argc-1) s += ' '; } return s; } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libutil/spin_rw_test.cpp0000644000175000017500000001307013151711064022005 0ustar mfvmfv/* Copyright 2012 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include "OpenImageIO/thread.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/sysutil.h" #include "OpenImageIO/timer.h" #include "OpenImageIO/argparse.h" #include "OpenImageIO/ustring.h" #include #include #include "OpenImageIO/unittest.h" OIIO_NAMESPACE_USING; // Test spin_rw_mutex by creating a bunch of threads usually just check // the accumulator value (requiring a read lock), but occasionally // (1/100 of the time) increment the accumulator, requiring a write // lock. If, at the end, the accumulated value is equal to // iterations/read_to_write_ratio*threads, then the locks worked. static int read_write_ratio = 99; static int iterations = 16000000; static int numthreads = 16; static int ntrials = 1; static bool verbose = false; static bool wedge = false; volatile long long accum = 0; spin_rw_mutex mymutex; static void do_accum (int iterations) { for (int i = 0; i < iterations; ++i) { if ((i % (read_write_ratio+1)) == read_write_ratio) { spin_rw_write_lock lock (mymutex); accum += 1; } else { spin_rw_read_lock lock (mymutex); // meaningless test to force examination of the variable if (accum < 0) break; } } } void test_spin_rw (int numthreads, int iterations) { accum = 0; boost::thread_group threads; for (int i = 0; i < numthreads; ++i) { threads.create_thread (boost::bind(do_accum,iterations)); } if (verbose) std::cout << "Created " << threads.size() << " threads\n"; threads.join_all (); OIIO_CHECK_EQUAL (accum, (((long long)iterations/(read_write_ratio+1)) * (long long)numthreads)); if (verbose) std::cout << "it " << iterations << ", r::w = " << read_write_ratio << ", accum = " << accum << "\n"; } static void getargs (int argc, char *argv[]) { bool help = false; ArgParse ap; ap.options ("spin_rw_test\n" OIIO_INTRO_STRING "\n" "Usage: spin_rw_test [options]", // "%*", parse_files, "", "--help", &help, "Print help message", "-v", &verbose, "Verbose mode", "--threads %d", &numthreads, ustring::format("Number of threads (default: %d)", numthreads).c_str(), "--iters %d", &iterations, ustring::format("Number of iterations (default: %d)", iterations).c_str(), "--trials %d", &ntrials, "Number of trials", "--rwratio %d", &read_write_ratio, ustring::format("Reader::writer ratio (default: %d)", read_write_ratio).c_str(), "--wedge", &wedge, "Do a wedge test", NULL); if (ap.parse (argc, (const char**)argv) < 0) { std::cerr << ap.geterror() << std::endl; ap.usage (); exit (EXIT_FAILURE); } if (help) { ap.usage (); exit (EXIT_FAILURE); } } int main (int argc, char *argv[]) { getargs (argc, argv); std::cout << "hw threads = " << Sysutil::hardware_concurrency() << "\n"; std::cout << "reader:writer ratio = " << read_write_ratio << ":1\n"; std::cout << "threads\ttime (best of " << ntrials << ")\n"; std::cout << "-------\t----------\n"; static int threadcounts[] = { 1, 2, 4, 8, 12, 16, 20, 24, 28, 32, 64, 128, 1024, 1<<30 }; for (int i = 0; threadcounts[i] <= numthreads; ++i) { int nt = threadcounts[i]; int its = iterations/nt; double range; double t = time_trial (boost::bind(test_spin_rw,nt,its), ntrials, &range); std::cout << Strutil::format ("%2d\t%s\t%5.1fs, range %.1f\t(%d iters/thread)\n", nt, Strutil::timeintervalformat(t), t, range, its); if (! wedge) break; // don't loop if we're not wedging } return unit_test_failures; } openimageio-1.7.17~dfsg0.orig/src/libutil/strutil.cpp0000644000175000017500000006571113151711064021004 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include #include #include "OpenImageIO/platform.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/ustring.h" #include "OpenImageIO/string_view.h" #ifdef _WIN32 # include #endif OIIO_NAMESPACE_BEGIN const char * string_view::c_str() const { // Usual case: either empty, or null-terminated if (m_len == 0) // empty string return ""; else if (m_chars[m_len] == 0) // 0-terminated return m_chars; // Rare case: may not be 0-terminated. Bite the bullet and construct a // 0-terminated string. We use ustring as a way to avoid any issues of // who cleans up the allocation, though it means that it will stay in // the ustring table forever. Punt on this for now, it's an edge case // that we need to handle, but is not likely to ever be an issue. return ustring(m_chars, 0, m_len).c_str(); } std::string Strutil::format_raw (const char *fmt, ...) { va_list ap; va_start (ap, fmt); std::string buf = vformat (fmt, ap); va_end (ap); return buf; } std::string Strutil::vformat (const char *fmt, va_list ap) { // Allocate a buffer on the stack that's big enough for us almost // all the time. Be prepared to allocate dynamically if it doesn't fit. size_t size = 1024; char stackbuf[1024]; std::vector dynamicbuf; char *buf = &stackbuf[0]; while (1) { // Try to vsnprintf into our buffer. va_list apsave; #ifdef va_copy va_copy (apsave, ap); #else apsave = ap; #endif int needed = vsnprintf (buf, size, fmt, ap); va_end (ap); // NB. C99 (which modern Linux and OS X follow) says vsnprintf // failure returns the length it would have needed. But older // glibc and current Windows return -1 for failure, i.e., not // telling us how much was needed. if (needed < (int)size && needed >= 0) { // It fit fine so we're done. return std::string (buf, (size_t) needed); } // vsnprintf reported that it wanted to write more characters // than we allotted. So try again using a dynamic buffer. This // doesn't happen very often if we chose our initial size well. size = (needed > 0) ? (needed+1) : (size*2); dynamicbuf.resize (size); buf = &dynamicbuf[0]; #ifdef va_copy va_copy (ap, apsave); #else ap = apsave; #endif } } std::string Strutil::memformat (long long bytes, int digits) { const long long KB = (1<<10); const long long MB = (1<<20); const long long GB = (1<<30); const char *units = "B"; double d = (double)bytes; if (bytes >= GB) { units = "GB"; d = (double)bytes / GB; } else if (bytes >= MB) { units = "MB"; d = (double)bytes / MB; } else if (bytes >= KB) { // Just KB, don't bother with decimalization return format ("%lld KB", (long long)bytes/KB); } else { // Just bytes, don't bother with decimalization return format ("%lld B", (long long)bytes); } return format ("%1.*f %s", digits, d, units); } std::string Strutil::timeintervalformat (double secs, int digits) { const double mins = 60; const double hours = mins * 60; const double days = hours * 24; std::string out; int d = (int) floor (secs / days); secs = fmod (secs, days); int h = (int) floor (secs / hours); secs = fmod (secs, hours); int m = (int) floor (secs / mins); secs = fmod (secs, mins); if (d) out += format ("%dd %dh ", d, h); else if (h) out += format ("%dh ", h); if (m || h || d) out += format ("%dm %1.*fs", m, digits, secs); else out += format ("%1.*fs", digits, secs); return out; } bool Strutil::get_rest_arguments (const std::string &str, std::string &base, std::map &result) { std::string::size_type mark_pos = str.find_first_of ("?"); if (mark_pos == std::string::npos) { base = str; return true; } base = str.substr (0, mark_pos); std::string rest = str.substr (mark_pos + 1); std::vector rest_tokens; Strutil::split (rest, rest_tokens, "&"); BOOST_FOREACH (const std::string &keyval, rest_tokens) { mark_pos = keyval.find_first_of ("="); if (mark_pos == std::string::npos) return false; result[keyval.substr (0, mark_pos)] = keyval.substr (mark_pos + 1); } return true; } std::string Strutil::escape_chars (string_view unescaped) { std::string s = unescaped; for (size_t i = 0; i < s.length(); ++i) { char c = s[i]; if (c == '\n' || c == '\t' || c == '\v' || c == '\b' || c == '\r' || c == '\f' || c == '\a' || c == '\\' || c == '\"') { s[i] = '\\'; ++i; switch (c) { case '\n' : c = 'n'; break; case '\t' : c = 't'; break; case '\v' : c = 'v'; break; case '\b' : c = 'b'; break; case '\r' : c = 'r'; break; case '\f' : c = 'f'; break; case '\a' : c = 'a'; break; } s.insert (i, &c, 1); } } return s; } std::string Strutil::unescape_chars (string_view escaped) { std::string s = escaped; for (size_t i = 0, len = s.length(); i < len; ++i) { if (s[i] == '\\') { char c = s[i+1]; if (c == 'n' || c == 't' || c == 'v' || c == 'b' || c == 'r' || c == 'f' || c == 'a' || c == '\\' || c == '\"') { s.erase (i, 1); --len; switch (c) { case 'n' : s[i] = '\n'; break; case 't' : s[i] = '\t'; break; case 'v' : s[i] = '\v'; break; case 'b' : s[i] = '\b'; break; case 'r' : s[i] = '\r'; break; case 'f' : s[i] = '\f'; break; case 'a' : s[i] = '\a'; break; // default case: the deletion is enough (backslash and quote) } } else if (c >= '0' && c < '8') { // up to 3 octal digits int octalChar = 0; for (int j = 0; j < 3 && c >= '0' && c <= '7'; ++j) { octalChar = 8*octalChar + (c - '0'); s.erase (i, 1); --len; c = i+1 < len ? s[i+1] : '\0'; } s[i] = (char) octalChar; } } } return s; } std::string Strutil::wordwrap (string_view src, int columns, int prefix) { if (columns < prefix+20) return src; // give up, no way to make it wrap std::ostringstream out; columns -= prefix; // now columns is the real width we have to work with while ((int)src.length() > columns) { // break the string in two size_t breakpoint = src.find_last_of (' ', columns); if (breakpoint == string_view::npos) breakpoint = columns; out << src.substr(0, breakpoint) << "\n" << std::string (prefix, ' '); src = src.substr (breakpoint); while (src[0] == ' ') src.remove_prefix (1); } out << src; return out.str(); } static std::locale& loc () { // Rather than have a file-level static locale which could suffer from // the static initialization order fiasco, make it a static pointer // inside the function that is allocated the first time it's needed. It // will "leak" in the sense that this one locale is never deleted, but // so what? #if OIIO_MSVS_BEFORE_2015 // MSVS prior to 2015 could not be trusted to make in-function static // initialization thread-safe, as required by C++11. static spin_mutex mutex; spin_lock lock (mutex); #endif static std::locale *loc = new std::locale (std::locale::classic()); return *loc; } bool Strutil::iequals (string_view a, string_view b) { return boost::algorithm::iequals (a, b, loc()); } bool Strutil::starts_with (string_view a, string_view b) { return boost::algorithm::starts_with (a, b); } bool Strutil::istarts_with (string_view a, string_view b) { return boost::algorithm::istarts_with (a, b, loc()); } bool Strutil::ends_with (string_view a, string_view b) { return boost::algorithm::ends_with (a, b); } bool Strutil::iends_with (string_view a, string_view b) { return boost::algorithm::iends_with (a, b, loc()); } bool Strutil::contains (string_view a, string_view b) { return boost::algorithm::contains (a, b); } bool Strutil::icontains (string_view a, string_view b) { return boost::algorithm::icontains (a, b, loc()); } void Strutil::to_lower (std::string &a) { boost::algorithm::to_lower (a, loc()); } void Strutil::to_upper (std::string &a) { boost::algorithm::to_upper (a, loc()); } string_view Strutil::strip (string_view str, string_view chars) { if (chars.empty()) chars = string_view(" \t\n\r\f\v", 6); size_t b = str.find_first_not_of (chars); if (b == std::string::npos) return string_view (); size_t e = str.find_last_not_of (chars); DASSERT (e != std::string::npos); return str.substr (b, e-b+1); } static void split_whitespace (string_view str, std::vector &result, int maxsplit) { // Implementation inspired by Pystring string_view::size_type i, j, len = str.size(); for (i = j = 0; i < len; ) { while (i < len && ::isspace(str[i])) i++; j = i; while (i < len && ! ::isspace(str[i])) i++; if (j < i) { if (--maxsplit <= 0) break; result.push_back (str.substr(j, i - j)); while (i < len && ::isspace(str[i])) i++; j = i; } } if (j < len) result.push_back (str.substr(j, len - j)); } void Strutil::split (string_view str, std::vector &result, string_view sep, int maxsplit) { std::vector sr_result; split (str, sr_result, sep, maxsplit); result.clear (); result.reserve (sr_result.size()); for (size_t i = 0, e = sr_result.size(); i != e; ++i) result.push_back (sr_result[i]); } void Strutil::split (string_view str, std::vector &result, string_view sep, int maxsplit) { // Implementation inspired by Pystring result.clear(); if (maxsplit < 0) maxsplit = std::numeric_limits::max(); if (sep.size() == 0) { split_whitespace (str, result, maxsplit); return; } size_t i = 0, j = 0, len = str.size(), n = sep.size(); while (i+n <= len) { if (str[i] == sep[0] && str.substr(i, n) == sep) { if (--maxsplit <= 0) break; result.push_back (str.substr(j, i - j)); i = j = i + n; } else { i++; } } result.push_back (str.substr(j, len-j)); } std::string Strutil::join (const std::vector &seq, string_view str) { // Implementation inspired by Pystring size_t seqlen = seq.size(); if (seqlen == 0) return std::string(); std::string result (seq[0]); for (size_t i = 1; i < seqlen; ++i) { result += str; result += seq[i]; } return result; } std::string Strutil::join (const std::vector &seq, string_view str) { std::vector seq_sr (seq.size()); for (size_t i = 0, e = seq.size(); i != e; ++i) seq_sr[i] = seq[i]; return join (seq_sr, str); } std::string Strutil::repeat (string_view str, int n) { std::ostringstream out; while (n-- > 0) out << str; return out.str(); } std::string Strutil::replace (string_view str, string_view pattern, string_view replacement, bool global) { std::string r; while (1) { size_t f = str.find (pattern); if (f != str.npos) { // Pattern found -- copy the part of str prior to the pattern, // then copy the replacement, and skip str up to the part after // the pattern and continue for another go at it. r.append (str.data(), f); r.append (replacement.data(), replacement.size()); str.remove_prefix (f + pattern.size()); if (global) continue; // Try for another match } // Pattern not found -- copy remaining string and we're done r.append (str.data(), str.size()); break; } return r; } #ifdef _WIN32 std::wstring Strutil::utf8_to_utf16 (string_view str) { std::wstring native; native.resize(MultiByteToWideChar (CP_UTF8, 0, str.data(), str.length(), NULL, 0)); MultiByteToWideChar (CP_UTF8, 0, str.data(), str.length(), &native[0], (int)native.size()); return native; } std::string Strutil::utf16_to_utf8 (const std::wstring& str) { std::string utf8; utf8.resize(WideCharToMultiByte (CP_UTF8, 0, str.data(), str.length(), NULL, 0, NULL, NULL)); WideCharToMultiByte (CP_UTF8, 0, str.data(), str.length(), &utf8[0], (int)utf8.size(), NULL, NULL); return utf8; } #endif char * Strutil::safe_strcpy (char *dst, const char *src, size_t size) { if (src) { for (size_t i = 0; i < size; ++i) { if (! (dst[i] = src[i])) return dst; // finished, and copied the 0 character } // If we got here, we have gotten to the maximum length, and still // no terminating 0, so add it! dst[size-1] = 0; } else { dst[0] = 0; } return dst; } void Strutil::skip_whitespace (string_view &str) { while (str.size() && isspace(str[0])) str.remove_prefix (1); } bool Strutil::parse_char (string_view &str, char c, bool skip_whitespace, bool eat) { string_view p = str; if (skip_whitespace) Strutil::skip_whitespace (p); if (p.size() && p[0] == c) { if (eat) { p.remove_prefix (1); str = p; } return true; } return false; } bool Strutil::parse_until_char (string_view &str, char c, bool eat) { string_view p = str; while (p.size() && p[0] != c) p.remove_prefix (1); if (eat) str = p; return p.size() && p[0] == c; } bool Strutil::parse_prefix (string_view &str, string_view prefix, bool eat) { string_view p = str; skip_whitespace (p); if (Strutil::starts_with (p, prefix)) { p.remove_prefix (prefix.size()); if (eat) str = p; return true; } return false; } bool Strutil::parse_int (string_view &str, int &val, bool eat) { string_view p = str; skip_whitespace (p); if (! p.size()) return false; const char *end = p.begin(); int v = strtol (p.begin(), (char**)&end, 10); if (end == p.begin()) return false; // no integer found if (eat) { p.remove_prefix (end-p.begin()); str = p; } val = v; return true; } bool Strutil::parse_float (string_view &str, float &val, bool eat) { string_view p = str; skip_whitespace (p); if (! p.size()) return false; const char *end = p.begin(); float v = (float) strtod (p.begin(), (char**)&end); if (end == p.begin()) return false; // no integer found if (eat) { p.remove_prefix (end-p.begin()); str = p; } val = v; return true; } bool Strutil::parse_string (string_view &str, string_view &val, bool eat, QuoteBehavior keep_quotes) { string_view p = str; skip_whitespace (p); bool quoted = parse_char (p, '\"'); const char *begin = p.begin(), *end = p.begin(); bool escaped = false; while (end != p.end()) { if (isspace(*end) && !quoted) break; // not quoted and we hit whitespace: we're done if (quoted && *end == '\"' && ! escaped) break; // closing quite -- we're done (beware embedded quote) if (p[0] == '\\') escaped = true; ++end; escaped = false; } if (quoted && keep_quotes == KeepQuotes) { if (*end == '\"') val = string_view (begin-1, size_t(end-begin)+2); else val = string_view (begin-1, size_t(end-begin)+1); } else { val = string_view (begin, size_t(end-begin)); } p.remove_prefix (size_t(end-begin)); if (quoted && p.size() && p[0] == '\"') p.remove_prefix (1); // eat closing quote if (eat) str = p; return quoted || val.size(); } string_view Strutil::parse_word (string_view &str, bool eat) { string_view p = str; skip_whitespace (p); const char *begin = p.begin(), *end = p.begin(); while (end != p.end() && isalpha(*end)) ++end; size_t wordlen = end - begin; if (eat && wordlen) { p.remove_prefix (wordlen); str = p; } return string_view (begin, wordlen); } string_view Strutil::parse_identifier (string_view &str, bool eat) { string_view p = str; skip_whitespace (p); const char *begin = p.begin(), *end = p.begin(); if (end != p.end() && (isalpha(*end) || *end == '_')) ++end; else return string_view(); // not even the start of an identifier while (end != p.end() && (isalpha(*end) || isdigit(*end) || *end == '_')) ++end; if (eat) { p.remove_prefix (size_t(end-begin)); str = p; } return string_view (begin, size_t(end-begin)); } string_view Strutil::parse_identifier (string_view &str, string_view allowed, bool eat) { string_view p = str; skip_whitespace (p); const char *begin = p.begin(), *end = p.begin(); if (end != p.end() && (isalpha(*end) || *end == '_' || allowed.find(*end) != string_view::npos)) ++end; else return string_view(); // not even the start of an identifier while (end != p.end() && (isalpha(*end) || isdigit(*end) || *end == '_' || allowed.find(*end) != string_view::npos)) ++end; if (eat) { p.remove_prefix (size_t(end-begin)); str = p; } return string_view (begin, size_t(end-begin)); } bool Strutil::parse_identifier_if (string_view &str, string_view id, bool eat) { string_view head = parse_identifier (str, false /* don't eat */); if (head == id) { if (eat) parse_identifier (str); return true; } return false; } string_view Strutil::parse_until (string_view &str, string_view sep, bool eat) { string_view p = str; const char *begin = p.begin(), *end = p.begin(); while (end != p.end() && sep.find(*end) == string_view::npos) ++end; size_t wordlen = end - begin; if (eat && wordlen) { p.remove_prefix (wordlen); str = p; } return string_view (begin, wordlen); } string_view Strutil::parse_nested (string_view &str, bool eat) { // Make sure we have a valid string and ascertain the characters that // nest and unnest. string_view p = str; if (! p.size()) return string_view(); // No proper opening char opening = p[0]; char closing = 0; if (opening == '(') closing = ')'; else if (opening == '[') closing = ']'; else if (opening == '{') closing = '}'; else return string_view(); // Walk forward in the string until we exactly unnest compared to the // start. size_t len = 1; int nesting = 1; for ( ; nesting && len < p.size(); ++len) { if (p[len] == opening) ++nesting; else if (p[len] == closing) --nesting; } if (nesting) return string_view(); // No proper closing ASSERT (p[len-1] == closing); // The result is the first len characters string_view result = str.substr (0, len); if (eat) str.remove_prefix (len); return result; } /* Copyright for decode function: See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. Copyright (c) 2008-2009 Bjoern Hoehrmann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define UTF8_ACCEPT 0 #define UTF8_REJECT 12 static const uint8_t utf8d[] = { // The first part of the table maps bytes to character classes that // to reduce the size of the transition table and create bitmasks. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, // The second part is a transition table that maps a combination // of a state of the automaton and a character class to a state. 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,12,12,12,12,12, }; inline uint32_t decode(uint32_t* state, uint32_t* codep, uint32_t byte) { uint32_t type = utf8d[byte]; *codep = (*state != UTF8_ACCEPT) ? (byte & 0x3fu) | (*codep << 6) : (0xff >> type) & (byte); *state = utf8d[256 + *state + type]; return *state; } void Strutil::utf8_to_unicode (string_view str, std::vector &uvec) { const char* begin = str.begin(); const char* end = str.end(); uint32_t state = 0; for (; begin != end; ++begin) { uint32_t codepoint; if (!decode(&state, &codepoint, (unsigned char) *begin)) uvec.push_back(codepoint); } } /* base64 code is based upon: http://www.adp-gmbh.ch/cpp/common/base64.html Copyright (C) 2004-2008 René Nyffenegger This source code is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this source code must not be misrepresented; you must not claim that you wrote the original source code. If you use this source code in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original source code. 3. This notice may not be removed or altered from any source distribution. René Nyffenegger rene.nyffenegger@adp-gmbh.ch */ std::string Strutil::base64_encode (string_view str) { static const char *base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; std::string ret; ret.reserve ((str.size() * 4 + 2)/ 3); int i = 0; unsigned char char_array_3[3]; unsigned char char_array_4[4]; while (str.size()) { char_array_3[i++] = str.front(); str.remove_prefix (1); if (i == 3) { char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for (int j = 0; j < 4; j++) ret += base64_chars[char_array_4[j]]; i = 0; } } if (i) { for (int j = i; j < 3; j++) char_array_3[j] = '\0'; char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for (int j = 0; j < i + 1; j++) ret += base64_chars[char_array_4[j]]; while (i++ < 3) ret += '='; } return ret; } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libutil/strutil_test.cpp0000644000175000017500000005376713151711064022053 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include "OpenImageIO/strutil.h" #include "OpenImageIO/unittest.h" OIIO_NAMESPACE_USING; using namespace Strutil; void test_format () { // Test formatting OIIO_CHECK_EQUAL (Strutil::format ("%d %f %g", int(3), 3.14f, 3.14f), "3 3.140000 3.14"); OIIO_CHECK_EQUAL (Strutil::format ("'%s' '%s'", "foo", std::string("foo")), "'foo' 'foo'"); OIIO_CHECK_EQUAL (Strutil::format ("'%3d' '%03d' '%-3d'", 3, 3, 3), "' 3' '003' '3 '"); // Test '+' format modifier OIIO_CHECK_EQUAL (Strutil::format ("%+d%+d%+d", 3, -3, 0), "+3-3+0"); // FIXME -- we should make comprehensive tests here } void test_memformat () { OIIO_CHECK_EQUAL (Strutil::memformat (15), "15 B"); OIIO_CHECK_EQUAL (Strutil::memformat (15LL*1024), "15 KB"); OIIO_CHECK_EQUAL (Strutil::memformat (15LL*1024*1024), "15.0 MB"); OIIO_CHECK_EQUAL (Strutil::memformat (15LL*1024*1024*1024), "15.0 GB"); OIIO_CHECK_EQUAL (Strutil::memformat (15LL*1024*1024+200000), "15.2 MB"); OIIO_CHECK_EQUAL (Strutil::memformat (15LL*1024*1024+200000, 3), "15.191 MB"); } void test_timeintervalformat () { OIIO_CHECK_EQUAL (Strutil::timeintervalformat (15.321), "15.3s"); OIIO_CHECK_EQUAL (Strutil::timeintervalformat (150.321), "2m 30.3s"); OIIO_CHECK_EQUAL (Strutil::timeintervalformat (15000.321), "4h 10m 0.3s"); OIIO_CHECK_EQUAL (Strutil::timeintervalformat (150000.321), "1d 17h 40m 0.3s"); OIIO_CHECK_EQUAL (Strutil::timeintervalformat (150.321, 2), "2m 30.32s"); } void test_get_rest_arguments () { bool ret; std::map result; std::string base; std::string url = "someplace?arg1=value1&arg2=value2"; ret = Strutil::get_rest_arguments (url, base, result); OIIO_CHECK_EQUAL (ret, true); OIIO_CHECK_EQUAL (base, "someplace"); OIIO_CHECK_EQUAL (result["arg1"], "value1"); OIIO_CHECK_EQUAL (result["arg2"], "value2"); OIIO_CHECK_EQUAL (result["arg3"], ""); result.clear(); url = "?arg1=value1&arg2=value2"; ret = Strutil::get_rest_arguments (url, base, result); OIIO_CHECK_EQUAL (ret, true); OIIO_CHECK_EQUAL (base, ""); OIIO_CHECK_EQUAL (result["arg1"], "value1"); OIIO_CHECK_EQUAL (result["arg2"], "value2"); result.clear(); url = "arg1=value1&arg2=value2"; ret = Strutil::get_rest_arguments (url, base, result); OIIO_CHECK_EQUAL (ret, true); OIIO_CHECK_EQUAL (base, "arg1=value1&arg2=value2"); OIIO_CHECK_EQUAL (result["arg1"], ""); OIIO_CHECK_EQUAL (result["arg2"], ""); result.clear(); url = ""; ret = Strutil::get_rest_arguments (url, base, result); OIIO_CHECK_EQUAL (ret, true); OIIO_CHECK_EQUAL (base, ""); OIIO_CHECK_EQUAL (result["arg1"], ""); OIIO_CHECK_EQUAL (result["arg2"], ""); result.clear(); url = "sometextwithoutasense????&&&&&arg4=val1"; ret = Strutil::get_rest_arguments (url, base, result); OIIO_CHECK_EQUAL (ret, false); OIIO_CHECK_EQUAL (base, "sometextwithoutasense"); OIIO_CHECK_EQUAL (result["arg1"], ""); OIIO_CHECK_EQUAL (result["arg2"], ""); OIIO_CHECK_EQUAL (result["arg4"], ""); result.clear(); url = "atext?arg1value1&arg2value2"; ret = Strutil::get_rest_arguments (url, base, result); OIIO_CHECK_EQUAL (ret, false); OIIO_CHECK_EQUAL (base, "atext"); OIIO_CHECK_EQUAL (result["arg1"], ""); OIIO_CHECK_EQUAL (result["arg2"], ""); result.clear(); url = "atext?arg1=value1&arg2value2"; result["arg2"] = "somevalue"; ret = Strutil::get_rest_arguments (url, base, result); OIIO_CHECK_EQUAL (ret, false); OIIO_CHECK_EQUAL (base, "atext"); OIIO_CHECK_EQUAL (result["arg1"], "value1"); OIIO_CHECK_EQUAL (result["arg2"], "somevalue"); } void test_escape_sequences () { OIIO_CHECK_EQUAL (Strutil::unescape_chars("\\\\ \\n \\r \\017"), "\\ \n \r \017"); OIIO_CHECK_EQUAL (Strutil::escape_chars("\\ \n \r"), "\\\\ \\n \\r"); } void test_wordwrap () { std::string words = "Now is the time for all good men to come to the aid of their party."; OIIO_CHECK_EQUAL (Strutil::wordwrap(words, 24), "Now is the time for all\n" "good men to come to the\n" "aid of their party."); } void test_comparisons () { OIIO_CHECK_EQUAL (Strutil::iequals ("abc", "abc"), true); OIIO_CHECK_EQUAL (Strutil::iequals ("Abc", "aBc"), true); OIIO_CHECK_EQUAL (Strutil::iequals ("abc", "adc"), false); OIIO_CHECK_EQUAL (Strutil::istarts_with ("abcd", "ab"), true); OIIO_CHECK_EQUAL (Strutil::istarts_with ("aBcd", "Ab"), true); OIIO_CHECK_EQUAL (Strutil::istarts_with ("abcd", "ba"), false); OIIO_CHECK_EQUAL (Strutil::istarts_with ("abcd", "abcde"), false); OIIO_CHECK_EQUAL (Strutil::istarts_with ("", "a"), false); OIIO_CHECK_EQUAL (Strutil::istarts_with ("", ""), true); OIIO_CHECK_EQUAL (Strutil::istarts_with ("abc", ""), true); OIIO_CHECK_EQUAL (Strutil::iends_with ("abcd", "cd"), true); OIIO_CHECK_EQUAL (Strutil::iends_with ("aBCd", "cd"), true); OIIO_CHECK_EQUAL (Strutil::iends_with ("aBcd", "CD"), true); OIIO_CHECK_EQUAL (Strutil::iends_with ("abcd", "ba"), false); OIIO_CHECK_EQUAL (Strutil::iends_with ("abcd", "xabcd"), false); OIIO_CHECK_EQUAL (Strutil::iends_with ("", "a"), false); OIIO_CHECK_EQUAL (Strutil::iends_with ("", ""), true); OIIO_CHECK_EQUAL (Strutil::iends_with ("abc", ""), true); OIIO_CHECK_EQUAL (Strutil::contains ("abcde", "ab"), true); OIIO_CHECK_EQUAL (Strutil::contains ("abcde", "bcd"), true); OIIO_CHECK_EQUAL (Strutil::contains ("abcde", "de"), true); OIIO_CHECK_EQUAL (Strutil::contains ("abcde", "cdx"), false); OIIO_CHECK_EQUAL (Strutil::contains ("abcde", ""), true); OIIO_CHECK_EQUAL (Strutil::contains ("", ""), true); OIIO_CHECK_EQUAL (Strutil::contains ("", "x"), false); OIIO_CHECK_EQUAL (Strutil::icontains ("abcde", "ab"), true); OIIO_CHECK_EQUAL (Strutil::icontains ("Abcde", "aB"), true); OIIO_CHECK_EQUAL (Strutil::icontains ("abcde", "bcd"), true); OIIO_CHECK_EQUAL (Strutil::icontains ("Abcde", "bCd"), true); OIIO_CHECK_EQUAL (Strutil::icontains ("abcDe", "dE"), true); OIIO_CHECK_EQUAL (Strutil::icontains ("abcde", "cdx"), false); OIIO_CHECK_EQUAL (Strutil::icontains ("abcde", ""), true); OIIO_CHECK_EQUAL (Strutil::icontains ("", ""), true); OIIO_CHECK_EQUAL (Strutil::icontains ("", "x"), false); } void test_case () { std::string s; s = "abcDEF,*1"; Strutil::to_lower (s); OIIO_CHECK_EQUAL (s, "abcdef,*1"); s = "abcDEF,*1"; Strutil::to_upper (s); OIIO_CHECK_EQUAL (s, "ABCDEF,*1"); } void test_strip () { OIIO_CHECK_EQUAL (Strutil::strip ("abcdefbac", "abc"), "def"); OIIO_CHECK_EQUAL (Strutil::strip ("defghi", "abc"), "defghi"); OIIO_CHECK_EQUAL (Strutil::strip (" \tHello, world\n"), "Hello, world"); OIIO_CHECK_EQUAL (Strutil::strip (" \t"), ""); OIIO_CHECK_EQUAL (Strutil::strip (""), ""); } void test_split () { std::string s ("Now\nis the time!"); std::vector splits; // test default -- split at whitespace Strutil::split (s, splits); OIIO_CHECK_EQUAL (splits.size(), 4); OIIO_CHECK_EQUAL (splits[0], "Now"); OIIO_CHECK_EQUAL (splits[1], "is"); OIIO_CHECK_EQUAL (splits[2], "the"); OIIO_CHECK_EQUAL (splits[3], "time!"); // test custom split string Strutil::split (s, splits, " t"); OIIO_CHECK_EQUAL (splits.size(), 3); OIIO_CHECK_EQUAL (splits[0], "Now\nis"); OIIO_CHECK_EQUAL (splits[1], "he "); OIIO_CHECK_EQUAL (splits[2], "ime!"); // test maxsplit Strutil::split (s, splits, "", 2); OIIO_CHECK_EQUAL (splits.size(), 2); OIIO_CHECK_EQUAL (splits[0], "Now"); OIIO_CHECK_EQUAL (splits[1], "is the time!"); // test maxsplit with non-default sep Strutil::split (s, splits, " ", 2); OIIO_CHECK_EQUAL (splits.size(), 2); OIIO_CHECK_EQUAL (splits[0], "Now\nis"); OIIO_CHECK_EQUAL (splits[1], "the time!"); Strutil::split ("blah", splits, "!"); OIIO_CHECK_EQUAL (splits.size(), 1); OIIO_CHECK_EQUAL (splits[0], "blah"); } void test_join () { std::vector seq; seq.push_back ("Now"); seq.push_back ("is"); seq.push_back ("the"); seq.push_back ("time"); OIIO_CHECK_EQUAL (Strutil::join (seq, ". "), "Now. is. the. time"); } void test_repeat () { std::cout << "Testing repeat\n"; OIIO_CHECK_EQUAL (Strutil::repeat("foo",3), "foofoofoo"); OIIO_CHECK_EQUAL (Strutil::repeat("foo",1), "foo"); OIIO_CHECK_EQUAL (Strutil::repeat("foo",0), ""); OIIO_CHECK_EQUAL (Strutil::repeat("foo",-1), ""); } void test_replace () { std::cout << "Testing replace\n"; std::string pattern ("Red rose, red rose, end."); // Replace start OIIO_CHECK_EQUAL (Strutil::replace(pattern, "Red", "foo"), "foo rose, red rose, end."); // Replace end OIIO_CHECK_EQUAL (Strutil::replace(pattern, "end.", "foo"), "Red rose, red rose, foo"); // Pattern not found OIIO_CHECK_EQUAL (Strutil::replace(pattern, "bar", "foo"), pattern); // One replacement OIIO_CHECK_EQUAL (Strutil::replace(pattern, "rose", "foo"), "Red foo, red rose, end."); // Global replacement OIIO_CHECK_EQUAL (Strutil::replace(pattern, "rose", "foo", true), "Red foo, red foo, end."); } void test_conversion () { OIIO_CHECK_EQUAL (Strutil::from_string("hi"), 0); OIIO_CHECK_EQUAL (Strutil::from_string("123"), 123); OIIO_CHECK_EQUAL (Strutil::from_string("-123"), -123); OIIO_CHECK_EQUAL (Strutil::from_string(" 123 "), 123); OIIO_CHECK_EQUAL (Strutil::from_string("123.45"), 123); OIIO_CHECK_EQUAL (Strutil::from_string("hi"), 0.0f); OIIO_CHECK_EQUAL (Strutil::from_string("123"), 123.0f); OIIO_CHECK_EQUAL (Strutil::from_string("-123"), -123.0f); OIIO_CHECK_EQUAL (Strutil::from_string("123.45"), 123.45f); OIIO_CHECK_EQUAL (Strutil::from_string(" 123.45 "), 123.45f); OIIO_CHECK_EQUAL (Strutil::from_string("123.45+12"), 123.45f); OIIO_CHECK_EQUAL (Strutil::from_string("1.2345e+2"), 123.45f); } void test_extract () { std::vector vals; int n; vals.clear(); vals.resize (3, -1); n = Strutil::extract_from_list_string (vals, "1"); OIIO_CHECK_EQUAL (vals.size(), 3); OIIO_CHECK_EQUAL (vals[0], 1); OIIO_CHECK_EQUAL (vals[1], 1); OIIO_CHECK_EQUAL (vals[2], 1); OIIO_CHECK_EQUAL (n, 1); vals.clear(); vals.resize (3, -1); n = Strutil::extract_from_list_string (vals, "1,3,5"); OIIO_CHECK_EQUAL (vals.size(), 3); OIIO_CHECK_EQUAL (vals[0], 1); OIIO_CHECK_EQUAL (vals[1], 3); OIIO_CHECK_EQUAL (vals[2], 5); OIIO_CHECK_EQUAL (n, 3); vals.clear(); vals.resize (3, -1); n = Strutil::extract_from_list_string (vals, "1,,5"); OIIO_CHECK_EQUAL (vals.size(), 3); OIIO_CHECK_EQUAL (vals[0], 1); OIIO_CHECK_EQUAL (vals[1], -1); OIIO_CHECK_EQUAL (vals[2], 5); OIIO_CHECK_EQUAL (n, 3); vals.clear(); vals.resize (3, -1); n = Strutil::extract_from_list_string (vals, "abc"); OIIO_CHECK_EQUAL (vals.size(), 3); OIIO_CHECK_EQUAL (vals[0], 0); OIIO_CHECK_EQUAL (vals[1], 0); OIIO_CHECK_EQUAL (vals[2], 0); OIIO_CHECK_EQUAL (n, 1); vals.clear(); vals.resize (3, -1); n = Strutil::extract_from_list_string (vals, ""); OIIO_CHECK_EQUAL (vals.size(), 3); OIIO_CHECK_EQUAL (vals[0], -1); OIIO_CHECK_EQUAL (vals[1], -1); OIIO_CHECK_EQUAL (vals[2], -1); OIIO_CHECK_EQUAL (n, 0); vals.clear(); n = Strutil::extract_from_list_string (vals, "1,3,5"); OIIO_CHECK_EQUAL (vals.size(), 3); OIIO_CHECK_EQUAL (vals[0], 1); OIIO_CHECK_EQUAL (vals[1], 3); OIIO_CHECK_EQUAL (vals[2], 5); OIIO_CHECK_EQUAL (n, 3); } void test_safe_strcpy () { { // test in-bounds copy char result[4] = { '0', '1', '2', '3' }; Strutil::safe_strcpy (result, "A", 3); OIIO_CHECK_EQUAL (result[0], 'A'); OIIO_CHECK_EQUAL (result[1], 0); OIIO_CHECK_EQUAL (result[2], '2'); // should not overwrite OIIO_CHECK_EQUAL (result[3], '3'); // should not overwrite } { // test over-bounds copy char result[4] = { '0', '1', '2', '3' }; Strutil::safe_strcpy (result, "ABC", 3); OIIO_CHECK_EQUAL (result[0], 'A'); OIIO_CHECK_EQUAL (result[1], 'B'); OIIO_CHECK_EQUAL (result[2], 0); OIIO_CHECK_EQUAL (result[3], '3'); // should not overwrite } { // test empty string copy char result[4] = { '0', '1', '2', '3' }; Strutil::safe_strcpy (result, "", 3); OIIO_CHECK_EQUAL (result[0], 0); OIIO_CHECK_EQUAL (result[1], '1'); // should not overwrite OIIO_CHECK_EQUAL (result[2], '2'); // should not overwrite OIIO_CHECK_EQUAL (result[3], '3'); // should not overwrite } { // test NULL case char result[4] = { '0', '1', '2', '3' }; Strutil::safe_strcpy (result, NULL, 3); OIIO_CHECK_EQUAL (result[0], 0); OIIO_CHECK_EQUAL (result[1], '1'); // should not overwrite OIIO_CHECK_EQUAL (result[2], '2'); // should not overwrite OIIO_CHECK_EQUAL (result[3], '3'); // should not overwrite } } // test some of the trickier methods in string_view. void test_string_view () { std::string s("0123401234"); string_view sr (s); OIIO_CHECK_EQUAL (sr.find("123"), s.find("123")); OIIO_CHECK_EQUAL (sr.find("123"), 1); OIIO_CHECK_EQUAL (sr.find("143"), string_view::npos); OIIO_CHECK_EQUAL (sr.find("123", 4), s.find("123", 4)); OIIO_CHECK_EQUAL (sr.find("123", 4), 6); OIIO_CHECK_EQUAL (sr.find("143", 4), string_view::npos); OIIO_CHECK_EQUAL (sr.find('1'), s.find('1')); OIIO_CHECK_EQUAL (sr.find('1'), 1); OIIO_CHECK_EQUAL (sr.find('5'), string_view::npos); OIIO_CHECK_EQUAL (sr.find('1', 4), s.find('1', 4)); OIIO_CHECK_EQUAL (sr.find('1', 4), 6); OIIO_CHECK_EQUAL (sr.find('5', 4), string_view::npos); OIIO_CHECK_EQUAL (sr.rfind("123"), s.rfind("123")); OIIO_CHECK_EQUAL (sr.rfind("123"), 6); OIIO_CHECK_EQUAL (sr.rfind("1234"), 6); OIIO_CHECK_EQUAL (sr.rfind("143"), string_view::npos); OIIO_CHECK_EQUAL (sr.rfind("123", 5), s.rfind("123", 5)); OIIO_CHECK_EQUAL (sr.rfind("123", 5), 1); OIIO_CHECK_EQUAL (sr.rfind("123", 4), 1); OIIO_CHECK_EQUAL (sr.rfind("143", 5), string_view::npos); OIIO_CHECK_EQUAL (sr.rfind("012", 4), 0); OIIO_CHECK_EQUAL (sr.rfind('1'), s.rfind('1')); OIIO_CHECK_EQUAL (sr.rfind('1'), 6); OIIO_CHECK_EQUAL (sr.rfind('5'), string_view::npos); OIIO_CHECK_EQUAL (sr.rfind('1', 4), s.rfind('1', 4)); OIIO_CHECK_EQUAL (sr.rfind('1', 4), 1); OIIO_CHECK_EQUAL (sr.rfind('5', 4), string_view::npos); } void test_parse () { std::cout << "Testing parse functions\n"; string_view s; s = ""; skip_whitespace(s); OIIO_CHECK_EQUAL (s, ""); s = " "; skip_whitespace(s); OIIO_CHECK_EQUAL (s, ""); s = "foo"; skip_whitespace(s); OIIO_CHECK_EQUAL (s, "foo"); s = " foo "; skip_whitespace(s); OIIO_CHECK_EQUAL (s, "foo "); s = "abc"; OIIO_CHECK_ASSERT (! parse_char (s, 'd') && s == "abc"); s = "abc"; OIIO_CHECK_ASSERT (parse_char (s, 'a', true, false) && s == "abc"); s = "abc"; OIIO_CHECK_ASSERT (parse_char (s, 'a') && s == "bc"); s = "abc"; OIIO_CHECK_ASSERT (parse_until_char (s, 'c', false) && s == "abc"); s = "abc"; OIIO_CHECK_ASSERT (parse_until_char (s, 'c') && s == "c"); s = "abc"; OIIO_CHECK_ASSERT (! parse_until_char (s, 'd') && s == ""); s = "abcdef"; OIIO_CHECK_ASSERT (! parse_prefix (s, "def", false) && s == "abcdef"); OIIO_CHECK_ASSERT (parse_prefix (s, "abc", false) && s == "abcdef"); OIIO_CHECK_ASSERT (parse_prefix (s, "abc") && s == "def"); int i = 0; s = "abc"; OIIO_CHECK_ASSERT (! parse_int (s, i) && s == "abc"); s = " 143 abc"; OIIO_CHECK_ASSERT (parse_int (s, i) && i == 143 && s == " abc"); s = " 143 abc"; OIIO_CHECK_ASSERT (parse_int (s, i, false) && i == 143 && s == " 143 abc"); float f = 0; s = "abc"; OIIO_CHECK_ASSERT (! parse_float (s, f) && s == "abc"); s = " 42.1 abc"; OIIO_CHECK_ASSERT (parse_float (s, f) && f == 42.1f && s == " abc"); s = " 42.1 abc"; OIIO_CHECK_ASSERT (parse_float (s, f, false) && f == 42.1f && s == " 42.1 abc"); string_view ss; s = "foo bar"; OIIO_CHECK_ASSERT (parse_string (s, ss) && ss == "foo" && s == " bar"); s = "\"foo bar\" baz"; OIIO_CHECK_ASSERT (parse_string (s, ss) && ss == "foo bar" && s == " baz"); s = "\"foo bar\" baz"; OIIO_CHECK_ASSERT (parse_string (s, ss, false) && ss == "foo bar" && s == "\"foo bar\" baz"); s = "\"foo bar\" baz"; OIIO_CHECK_ASSERT (parse_string (s, ss, true, KeepQuotes) && ss == "\"foo bar\"" && s == " baz"); s = " foo bar"; ss = parse_word (s); OIIO_CHECK_ASSERT (ss == "foo" && s == " bar"); s = " 14 foo bar"; ss = parse_word (s); OIIO_CHECK_ASSERT (ss.size() == 0 && s == " 14 foo bar"); s = "foo14 bar"; ss = parse_word (s); OIIO_CHECK_ASSERT (ss == "foo" && s == "14 bar"); s = " foo bar"; ss = parse_word (s, false); OIIO_CHECK_ASSERT (ss == "foo" && s == " foo bar"); s = " foo bar"; ss = parse_identifier (s); OIIO_CHECK_ASSERT (ss == "foo" && s == " bar"); s = " 14 foo bar"; ss = parse_identifier (s); OIIO_CHECK_ASSERT (ss.size() == 0 && s == " 14 foo bar"); s = " foo_14 bar"; ss = parse_identifier (s); OIIO_CHECK_ASSERT (ss == "foo_14" && s == " bar"); s = " foo_14 bar"; ss = parse_identifier (s, false); OIIO_CHECK_ASSERT (ss == "foo_14" && s == " foo_14 bar"); s = "fl$orp 14"; ss = parse_identifier (s); OIIO_CHECK_ASSERT (ss == "fl" && s == "$orp 14"); s = "fl$orp 14"; ss = parse_identifier (s, "$:", true); OIIO_CHECK_ASSERT (ss == "fl$orp" && s == " 14"); bool b; s = " foo bar"; b = parse_identifier_if (s, "bar"); OIIO_CHECK_ASSERT (b == false && s == " foo bar"); s = " foo bar"; b = parse_identifier_if (s, "foo"); OIIO_CHECK_ASSERT (b == true && s == " bar"); s = " foo_14 bar"; b = parse_identifier_if (s, "foo"); OIIO_CHECK_ASSERT (b == false && s == " foo_14 bar"); s = " foo_14 bar"; b = parse_identifier_if (s, "foo_14"); OIIO_CHECK_ASSERT (b == true && s == " bar"); s = "foo;bar blow"; ss = parse_until (s, ";"); OIIO_CHECK_ASSERT (ss == "foo" && s == ";bar blow"); s = "foo;bar blow"; ss = parse_until (s, "\t "); OIIO_CHECK_ASSERT (ss == "foo;bar" && s == " blow"); s = "foo;bar blow"; ss = parse_until (s, "/"); OIIO_CHECK_ASSERT (ss == "foo;bar blow" && s == ""); s = "[a([b]c)]x]"; ss = parse_nested (s); OIIO_CHECK_EQUAL (ss, "[a([b]c)]"); OIIO_CHECK_EQUAL (s, "x]"); s = "[a([b]c)]x]"; ss = parse_nested (s, false); OIIO_CHECK_EQUAL (ss, "[a([b]c)]"); OIIO_CHECK_EQUAL (s, "[a([b]c)]x]"); } void test_float_formatting () { // For every possible float value, test that printf("%.9g"), which // we are sure preserves full precision as text, exactly matches // Strutil::format("%.9g") and also matches stream output with // precision(9). VERY EXPENSIVE! Takes tens of minutes to run. // Don't do this unless you really need to test it. for (unsigned long long i = 0; i <= (unsigned long long)0xffffffff; ++i) { unsigned int i32 = (unsigned int)i; float *f = (float *)&i32; std::ostringstream sstream; sstream.precision (9); sstream << *f; char buffer[64]; sprintf (buffer, "%.9g", *f); std::string tiny = Strutil::format ("%.9g", *f); if (sstream.str() != tiny || tiny != buffer) printf ("%x stream '%s' printf '%s' Strutil::format '%s'\n", i32, sstream.str().c_str(), buffer, tiny.c_str()); if ((i32 & 0xfffffff) == 0xfffffff) { printf ("%x\n", i32); fflush (stdout); } } } int main (int argc, char *argv[]) { test_format (); test_memformat (); test_timeintervalformat (); test_get_rest_arguments (); test_escape_sequences (); test_wordwrap (); test_comparisons (); test_case (); test_strip (); test_split (); test_join (); test_repeat (); test_replace (); test_conversion (); test_extract (); test_safe_strcpy (); test_string_view (); test_parse (); // test_float_formatting (); return unit_test_failures; } openimageio-1.7.17~dfsg0.orig/src/libutil/fmath_test.cpp0000644000175000017500000002510113151711064021421 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include #include OIIO_NAMESPACE_USING; static int iterations = 10; static int ntrials = 5; static bool verbose = false; static void getargs (int argc, char *argv[]) { bool help = false; ArgParse ap; ap.options ("fmath_test\n" OIIO_INTRO_STRING "\n" "Usage: fmath_test [options]", // "%*", parse_files, "", "--help", &help, "Print help message", "-v", &verbose, "Verbose mode", // "--threads %d", &numthreads, // ustring::format("Number of threads (default: %d)", numthreads).c_str(), "--iterations %d", &iterations, ustring::format("Number of iterations (default: %d)", iterations).c_str(), "--trials %d", &ntrials, "Number of trials", NULL); if (ap.parse (argc, (const char**)argv) < 0) { std::cerr << ap.geterror() << std::endl; ap.usage (); exit (EXIT_FAILURE); } if (help) { ap.usage (); exit (EXIT_FAILURE); } } void test_int_helpers () { std::cout << "\ntest_int_helpers\n"; // ispow2 for (int i = 1; i < (1<<30); i *= 2) { OIIO_CHECK_ASSERT (ispow2(i)); if (i > 1) OIIO_CHECK_ASSERT (! ispow2(i+1)); } OIIO_CHECK_ASSERT (ispow2(int(0))); OIIO_CHECK_ASSERT (! ispow2(-1)); OIIO_CHECK_ASSERT (! ispow2(-2)); // ispow2, try size_t, which is unsigned for (size_t i = 1; i < (1<<30); i *= 2) { OIIO_CHECK_ASSERT (ispow2(i)); if (i > 1) OIIO_CHECK_ASSERT (! ispow2(i+1)); } OIIO_CHECK_ASSERT (ispow2((unsigned int)0)); // pow2roundup OIIO_CHECK_EQUAL (pow2roundup(4), 4); OIIO_CHECK_EQUAL (pow2roundup(5), 8); OIIO_CHECK_EQUAL (pow2roundup(6), 8); OIIO_CHECK_EQUAL (pow2roundup(7), 8); OIIO_CHECK_EQUAL (pow2roundup(8), 8); // pow2rounddown OIIO_CHECK_EQUAL (pow2rounddown(4), 4); OIIO_CHECK_EQUAL (pow2rounddown(5), 4); OIIO_CHECK_EQUAL (pow2rounddown(6), 4); OIIO_CHECK_EQUAL (pow2rounddown(7), 4); OIIO_CHECK_EQUAL (pow2rounddown(8), 8); // round_to_multiple OIIO_CHECK_EQUAL (round_to_multiple(1, 5), 5); OIIO_CHECK_EQUAL (round_to_multiple(2, 5), 5); OIIO_CHECK_EQUAL (round_to_multiple(3, 5), 5); OIIO_CHECK_EQUAL (round_to_multiple(4, 5), 5); OIIO_CHECK_EQUAL (round_to_multiple(5, 5), 5); OIIO_CHECK_EQUAL (round_to_multiple(6, 5), 10); OIIO_CHECK_EQUAL (round_to_multiple(size_t(5), 5), 5); OIIO_CHECK_EQUAL (round_to_multiple(size_t(6), 5), 10); // round_to_multiple_of_pow2 OIIO_CHECK_EQUAL (round_to_multiple_of_pow2(int(1), 4), 4); OIIO_CHECK_EQUAL (round_to_multiple_of_pow2(int(2), 4), 4); OIIO_CHECK_EQUAL (round_to_multiple_of_pow2(int(3), 4), 4); OIIO_CHECK_EQUAL (round_to_multiple_of_pow2(int(4), 4), 4); OIIO_CHECK_EQUAL (round_to_multiple_of_pow2(int(5), 4), 8); // round_to_multiple_of_pow2 OIIO_CHECK_EQUAL (round_to_multiple_of_pow2(size_t(1), size_t(4)), 4); OIIO_CHECK_EQUAL (round_to_multiple_of_pow2(size_t(2), size_t(4)), 4); OIIO_CHECK_EQUAL (round_to_multiple_of_pow2(size_t(3), size_t(4)), 4); OIIO_CHECK_EQUAL (round_to_multiple_of_pow2(size_t(4), size_t(4)), 4); OIIO_CHECK_EQUAL (round_to_multiple_of_pow2(size_t(5), size_t(4)), 8); } // Convert T to F to T, make sure value are preserved round trip template void test_convert_type (double tolerance = 1e-6) { if (std::numeric_limits::is_integer) { for (long long i = std::numeric_limits::min(); i <= std::numeric_limits::max(); ++i) { T in = (T)i; F f = convert_type (in); T out = convert_type (f); if (out != in) { std::cout << " convert " << (long long)in << " -> " << f << " -> " << (long long)out << "\n"; ++unit_test_failures; } } } else { for (float i = 0.0f; i <= 1.0f; i += 0.001) { T in = (T)i; F f = convert_type (in); T out = convert_type (f); if (fabs(double(out-in)) > tolerance) { std::cout << " convert " << in << " -> " << f << " -> " << out << " (diff = " << (out-in) << ")\n"; ++unit_test_failures; } } } } template void do_convert_type (const std::vector &svec, std::vector &dvec) { convert_type (&svec[0], &dvec[0], svec.size()); DoNotOptimize (dvec[0]); // Be sure nothing is optimized away } template void benchmark_convert_type () { const size_t size = 10000000; const S testval(1.0); std::vector svec (size, testval); std::vector dvec (size); std::cout << Strutil::format("Benchmark conversion of %6s -> %6s : ", TypeDesc(BaseTypeFromC::value), TypeDesc(BaseTypeFromC::value)); float time = time_trial (bind (do_convert_type, OIIO::cref(svec), OIIO::ref(dvec)), ntrials, iterations) / iterations; std::cout << Strutil::format ("%7.1f Mvals/sec", (size/1.0e6)/time) << std::endl; D r = convert_type(testval); OIIO_CHECK_EQUAL (dvec[size-1], r); } void test_bit_range_convert () { OIIO_CHECK_EQUAL ((bit_range_convert<10,16>(1023)), 65535); OIIO_CHECK_EQUAL ((bit_range_convert<2,8>(3)), 255); OIIO_CHECK_EQUAL ((bit_range_convert<8,8>(255)), 255); OIIO_CHECK_EQUAL ((bit_range_convert<16,10>(65535)), 1023); OIIO_CHECK_EQUAL ((bit_range_convert<2,20>(3)), 1048575); OIIO_CHECK_EQUAL ((bit_range_convert<20,2>(1048575)), 3); OIIO_CHECK_EQUAL ((bit_range_convert<20,21>(1048575)), 2097151); OIIO_CHECK_EQUAL ((bit_range_convert<32,32>(4294967295U)), 4294967295U); OIIO_CHECK_EQUAL ((bit_range_convert<32,16>(4294967295U)), 65535); // These are not expected to work, since bit_range_convert only takes a // regular 'unsigned int' as parameter. If we need >32 bit conversion, // we need to add a uint64_t version of bit_range_convert. // OIIO_CHECK_EQUAL ((bit_range_convert<33,16>(8589934591)), 65535); // OIIO_CHECK_EQUAL ((bit_range_convert<33,33>(8589934591)), 8589934591); // OIIO_CHECK_EQUAL ((bit_range_convert<64,32>(18446744073709551615)), 4294967295); } int main (int argc, char *argv[]) { #if !defined(NDEBUG) || defined(OIIO_CI) || defined(OIIO_CODECOV) // For the sake of test time, reduce the default iterations for DEBUG, // CI, and code coverage builds. Explicit use of --iters or --trials // will override this, since it comes before the getargs() call. iterations /= 10; ntrials = 1; #endif getargs (argc, argv); test_int_helpers (); std::cout << "\nround trip convert char/float/char\n"; test_convert_type (); std::cout << "round trip convert unsigned char/float/unsigned char\n"; test_convert_type (); std::cout << "round trip convert unsigned char/unsigned short/unsigned char\n"; test_convert_type (); std::cout << "round trip convert short/float/short\n"; test_convert_type (); std::cout << "round trip convert unsigned short/float/unsigned short\n"; test_convert_type (); std::cout << "round trip convert float/int/float \n"; test_convert_type (); std::cout << "round trip convert double/float/double\n"; test_convert_type (); std::cout << "round trip convert double/long/double\n"; test_convert_type (); std::cout << "round trip convert float/unsigned int/float\n"; test_convert_type (); benchmark_convert_type (); benchmark_convert_type (); benchmark_convert_type (); benchmark_convert_type (); benchmark_convert_type (); benchmark_convert_type (); benchmark_convert_type (); // convertion to a type smaller in bytes causes error // std::cout << "round trip convert float/short/float\n"; // test_convert_type (); // std::cout << "round trip convert unsigned float/char/float\n"; // test_convert_type (); // std::cout << "round trip convert unsigned float/unsigned char/float\n"; // test_convert_type (); // std::cout << "round trip convert unsigned short/unsigned char/unsigned short\n"; // test_convert_type (); // std::cout << "round trip convert float/unsigned short/float\n"; // test_convert_type (); test_bit_range_convert(); return unit_test_failures != 0; } openimageio-1.7.17~dfsg0.orig/src/libutil/paramlist.cpp0000644000175000017500000001276113151711064021267 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/ustring.h" #include "OpenImageIO/paramlist.h" OIIO_NAMESPACE_BEGIN void ParamValue::init_noclear (ustring _name, TypeDesc _type, int _nvalues, const void *_value, bool _copy) { init_noclear (_name, _type, _nvalues, INTERP_CONSTANT, _value, _copy); } void ParamValue::init_noclear (ustring _name, TypeDesc _type, int _nvalues, Interp _interp, const void *_value, bool _copy) { m_name = _name; m_type = _type; m_nvalues = _nvalues; m_interp = _interp; size_t n = (size_t) (m_nvalues * m_type.numelements()); size_t size = (size_t) (m_nvalues * m_type.size()); bool small = (size <= sizeof(m_data)); if (_copy || small) { if (small) { if (_value) memcpy (&m_data, _value, size); else m_data.localval = 0; m_copy = false; m_nonlocal = false; } else { m_data.ptr = malloc (size); if (_value) memcpy ((char *)m_data.ptr, _value, size); else memset ((char *)m_data.ptr, 0, size); m_copy = true; m_nonlocal = true; } if (m_type.basetype == TypeDesc::STRING) { ustring *u = (ustring *) data(); for (size_t i = 0; i < n; ++i) u[i] = ustring(u[i].c_str()); } } else { // Big enough to warrant a malloc, but the caller said don't // make a copy m_data.ptr = _value; m_copy = false; m_nonlocal = true; } } void ParamValue::clear_value () { if (m_copy && m_nonlocal && m_data.ptr) free ((void *)m_data.ptr); m_data.ptr = NULL; m_copy = false; m_nonlocal = false; } ParamValueList::const_iterator ParamValueList::find (ustring name, TypeDesc type, bool casesensitive) const { if (casesensitive) { for (const_iterator i = cbegin(), e = cend(); i != e; ++i) { if (i->name() == name && (type == TypeDesc::UNKNOWN || type == i->type())) return i; } } else { for (const_iterator i = cbegin(), e = cend(); i != e; ++i) { if (Strutil::iequals (i->name(), name) && (type == TypeDesc::UNKNOWN || type == i->type())) return i; } } return cend(); } ParamValueList::const_iterator ParamValueList::find (string_view name, TypeDesc type, bool casesensitive) const { if (casesensitive) { return find (ustring(name), type, casesensitive); } else { for (const_iterator i = cbegin(), e = cend(); i != e; ++i) { if (Strutil::iequals (i->name(), name) && (type == TypeDesc::UNKNOWN || type == i->type())) return i; } } return cend(); } ParamValueList::iterator ParamValueList::find (ustring name, TypeDesc type, bool casesensitive) { if (casesensitive) { for (iterator i = begin(), e = end(); i != e; ++i) { if (i->name() == name && (type == TypeDesc::UNKNOWN || type == i->type())) return i; } } else { for (iterator i = begin(), e = end(); i != e; ++i) { if (Strutil::iequals (i->name(), name) && (type == TypeDesc::UNKNOWN || type == i->type())) return i; } } return end(); } ParamValueList::iterator ParamValueList::find (string_view name, TypeDesc type, bool casesensitive) { if (casesensitive) { return find (ustring(name), type, casesensitive); } else { for (iterator i = begin(), e = end(); i != e; ++i) { if (Strutil::iequals (i->name(), name) && (type == TypeDesc::UNKNOWN || type == i->type())) return i; } } return end(); } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libutil/filesystem.cpp0000644000175000017500000010066213151711064021455 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include #include #include #include "OpenImageIO/platform.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/ustring.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/refcnt.h" #ifdef _WIN32 // # include // Already done by platform.h # include # include #else # include #endif OIIO_NAMESPACE_BEGIN #ifdef _MSC_VER // fix for https://svn.boost.org/trac/boost/ticket/6320 const std::string dummy_path = "../dummy_path.txt"; const std::string dummy_extension = boost::filesystem::path(dummy_path).extension().string(); #endif std::string Filesystem::filename (const std::string &filepath) { // To simplify dealing with platform-specific separators and whatnot, // just use the Boost routines: #if BOOST_FILESYSTEM_VERSION == 3 return boost::filesystem::path(filepath).filename().string(); #else return boost::filesystem::path(filepath).filename(); #endif } std::string Filesystem::extension (const std::string &filepath, bool include_dot) { std::string s; #if BOOST_FILESYSTEM_VERSION == 3 s = boost::filesystem::path(filepath).extension().string(); #else s = boost::filesystem::path(filepath).extension(); #endif if (! include_dot && !s.empty() && s[0] == '.') s.erase (0, 1); // erase the first character return s; } std::string Filesystem::parent_path (const std::string &filepath) { return boost::filesystem::path(filepath).parent_path().string(); } std::string Filesystem::replace_extension (const std::string &filepath, const std::string &new_extension) { return boost::filesystem::path(filepath).replace_extension(new_extension).string(); } void Filesystem::searchpath_split (const std::string &searchpath, std::vector &dirs, bool validonly) { dirs.clear(); std::string path_copy = searchpath; std::string last_token; typedef boost::tokenizer > tokenizer; boost::char_separator sep(":;"); tokenizer tokens (searchpath, sep); for (tokenizer::iterator tok_iter = tokens.begin(); tok_iter != tokens.end(); last_token = *tok_iter, ++tok_iter) { std::string path = *tok_iter; #ifdef _WIN32 // On Windows, we might see something like "a:foo" and any human // would know that it means drive/directory 'a:foo', NOT // separate directories 'a' and 'foo'. Implement the obvious // heuristic here. Note that this means that we simply don't // correctly support searching in *relative* directories that // consist of a single letter. if (last_token.length() == 1 && last_token[0] != '.') { // If the last token was a single letter, try prepending it path = last_token + ":" + (*tok_iter); } else #endif path = *tok_iter; // Kill trailing slashes (but not simple "/") size_t len = path.length(); while (len > 1 && (path[len-1] == '/' || path[len-1] == '\\')) path.erase (--len); // If it's a valid directory, or if validonly is false, add it // to the list if (!validonly || Filesystem::is_directory (path)) dirs.push_back (path); } #if 0 std::cerr << "Searchpath = '" << searchpath << "'\n"; BOOST_FOREACH (std::string &d, dirs) std::cerr << "\tPath = '" << d << "'\n"; std::cerr << "\n"; #endif } std::string Filesystem::searchpath_find (const std::string &filename_utf8, const std::vector &dirs, bool testcwd, bool recursive) { #ifdef _WIN32 const boost::filesystem::path filename (Strutil::utf8_to_utf16 (filename_utf8)); #else const boost::filesystem::path filename (filename_utf8); #endif #if BOOST_FILESYSTEM_VERSION == 3 bool abs = filename.is_absolute(); #else bool abs = path_is_absolute (filename.string()); #endif // If it's an absolute filename, or if we want to check "." first, // then start by checking filename outright. if (testcwd || abs) { if (is_regular (filename.string())) return filename_utf8; } // Relative filename, not yet found -- try each directory in turn BOOST_FOREACH (const std::string &d_utf8, dirs) { // std::cerr << "\tPath = '" << d << "'\n"; #ifdef _WIN32 const boost::filesystem::path d(Strutil::utf8_to_utf16 (d_utf8)); #else const boost::filesystem::path d(d_utf8); #endif boost::filesystem::path f = d / filename; // std::cerr << "\tTesting '" << f.string() << "'\n"; if (is_regular (f.string())) { // std::cerr << "Found '" << f.string() << "'\n"; #ifdef _WIN32 return Strutil::utf16_to_utf8 (f.native()); #else return f.string(); #endif } if (recursive && is_directory (d.string())) { std::vector subdirs; boost::filesystem::directory_iterator end_iter; for (boost::filesystem::directory_iterator s(d); s != end_iter; ++s) { if (is_directory (s->path().string())) { #ifdef _WIN32 subdirs.push_back (Strutil::utf16_to_utf8 (s->path().native())); #else subdirs.push_back (s->path().string()); #endif } } std::string found = searchpath_find (filename_utf8, subdirs, false, true); if (found.size()) return found; } } return std::string(); } bool Filesystem::get_directory_entries (const std::string &dirname, std::vector &filenames, bool recursive, const std::string &filter_regex) { filenames.clear (); if (dirname.size() && ! is_directory(dirname)) return false; boost::filesystem::path dirpath (dirname.size() ? dirname : std::string(".")); boost::regex re; try { re = boost::regex(filter_regex); } catch (...) { return false; } if (recursive) { #ifdef _WIN32 std::wstring wdirpath = Strutil::utf8_to_utf16 (dirpath.string()); for (boost::filesystem::recursive_directory_iterator s (wdirpath); #else for (boost::filesystem::recursive_directory_iterator s (dirpath); #endif s != boost::filesystem::recursive_directory_iterator(); ++s) { std::string file = s->path().string(); if (!filter_regex.size() || boost::regex_search (file, re)) #ifdef _WIN32 filenames.push_back (Strutil::utf16_to_utf8(s->path().native())); #else filenames.push_back (file); #endif } } else { #ifdef _WIN32 std::wstring wdirpath = Strutil::utf8_to_utf16 (dirpath.string()); for (boost::filesystem::directory_iterator s (wdirpath); #else for (boost::filesystem::directory_iterator s (dirpath); #endif s != boost::filesystem::directory_iterator(); ++s) { std::string file = s->path().string(); if (!filter_regex.size() || boost::regex_search (file, re)) #ifdef _WIN32 filenames.push_back (Strutil::utf16_to_utf8(s->path().native())); #else filenames.push_back (file); #endif } } return true; } bool Filesystem::path_is_absolute (const std::string &path, bool dot_is_absolute) { // "./foo" is considered absolute if dot_is_absolute is true. // Don't get confused by ".foo", which is not absolute! size_t len = path.length(); if (!len) return false; return (path[0] == '/') || (dot_is_absolute && path[0] == '.' && path[1] == '/') || (dot_is_absolute && path[0] == '.' && path[1] == '.' && path[2] == '/') #ifdef _WIN32 || path[0] == '\\' || (dot_is_absolute && path[0] == '.' && path[1] == '\\') || (dot_is_absolute && path[0] == '.' && path[1] == '.' && path[2] == '\\') || (isalpha(path[0]) && path[1] == ':') #endif ; } bool Filesystem::exists (const std::string &path) { bool r = false; try { #if defined(_WIN32) // boost internally doesn't use MultiByteToWideChar (CP_UTF8,... // to convert char* to wchar_t* because they do not know the encoding // See boost::filesystem::path.hpp // The only correct way to do this is to do the conversion ourselves std::wstring wpath = Strutil::utf8_to_utf16(path); r = boost::filesystem::exists (wpath); #else r = boost::filesystem::exists (path); #endif } catch (...) { r = false; } return r; } bool Filesystem::is_directory (const std::string &path) { bool r = false; try { #if defined(_WIN32) // boost internally doesn't use MultiByteToWideChar (CP_UTF8,... // to convert char* to wchar_t* because they do not know the encoding // See boost::filesystem::path.hpp // The only correct way to do this is to do the conversion ourselves std::wstring wpath = Strutil::utf8_to_utf16(path); r = boost::filesystem::is_directory (wpath); #else r = boost::filesystem::is_directory (path); #endif } catch (...) { r = false; } return r; } bool Filesystem::is_regular (const std::string &path) { bool r = false; try { #if defined(_WIN32) // boost internally doesn't use MultiByteToWideChar (CP_UTF8,... // to convert char* to wchar_t* because they do not know the encoding // See boost::filesystem::path.hpp // The only correct way to do this is to do the conversion ourselves std::wstring wpath = Strutil::utf8_to_utf16(path); r = boost::filesystem::is_regular_file (wpath); #else r = boost::filesystem::is_regular_file (path); #endif } catch (...) { r = false; } return r; } bool Filesystem::create_directory (string_view path, std::string &err) { #if defined(_WIN32) // boost internally doesn't use MultiByteToWideChar (CP_UTF8,... // to convert char* to wchar_t* because they do not know the encoding // See boost::filesystem::path.hpp // The only correct way to do this is to do the conversion ourselves std::wstring pathStr = Strutil::utf8_to_utf16(path); #else std::string pathStr = path.str(); #endif #if BOOST_FILESYSTEM_VERSION >= 3 boost::system::error_code ec; bool ok = boost::filesystem::create_directory (pathStr, ec); if (ok) err.clear(); else err = ec.message(); return ok; #else bool ok = boost::filesystem::create_directory (pathStr); if (ok) err.clear(); else err = "Could not make directory"; return ok; #endif } bool Filesystem::copy (string_view from, string_view to, std::string &err) { #if defined(_WIN32) // boost internally doesn't use MultiByteToWideChar (CP_UTF8,... // to convert char* to wchar_t* because they do not know the encoding // See boost::filesystem::path.hpp // The only correct way to do this is to do the conversion ourselves std::wstring fromStr = Strutil::utf8_to_utf16(from); std::wstring toStr = Strutil::utf8_to_utf16(to); #else std::string fromStr = from.str(); std::string toStr = to.str(); #endif #if BOOST_FILESYSTEM_VERSION >= 3 boost::system::error_code ec; # if BOOST_VERSION < 105000 boost::filesystem3::copy (fromStr, toStr, ec); # else boost::filesystem::copy (fromStr, toStr, ec); # endif if (! ec) { err.clear(); return true; } else { err = ec.message(); return false; } #else return false; // I'm too lazy to figure this out. #endif } bool Filesystem::rename (string_view from, string_view to, std::string &err) { #if defined(_WIN32) // boost internally doesn't use MultiByteToWideChar (CP_UTF8,... // to convert char* to wchar_t* because they do not know the encoding // See boost::filesystem::path.hpp // The only correct way to do this is to do the conversion ourselves std::wstring fromStr = Strutil::utf8_to_utf16(from); std::wstring toStr = Strutil::utf8_to_utf16(to); #else std::string fromStr = from.str(); std::string toStr = to.str(); #endif #if BOOST_FILESYSTEM_VERSION >= 3 boost::system::error_code ec; # if BOOST_VERSION < 105000 boost::filesystem3::rename (fromStr, toStr, ec); # else boost::filesystem::rename (fromStr, toStr, ec); # endif if (! ec) { err.clear(); return true; } else { err = ec.message(); return false; } #else return false; // I'm too lazy to figure this out. #endif } bool Filesystem::remove (string_view path, std::string &err) { #if defined(_WIN32) // boost internally doesn't use MultiByteToWideChar (CP_UTF8,... // to convert char* to wchar_t* because they do not know the encoding // See boost::filesystem::path.hpp // The only correct way to do this is to do the conversion ourselves std::wstring pathStr = Strutil::utf8_to_utf16(path); #else std::string pathStr = path.str(); #endif #if BOOST_FILESYSTEM_VERSION >= 3 boost::system::error_code ec; bool ok = boost::filesystem::remove (pathStr, ec); if (ok) err.clear(); else err = ec.message(); return ok; #else bool ok = boost::filesystem::remove (pathStr); if (ok) err.clear(); else err = "Could not remove file"; return ok; #endif } unsigned long long Filesystem::remove_all (string_view path, std::string &err) { #if defined(_WIN32) // boost internally doesn't use MultiByteToWideChar (CP_UTF8,... // to convert char* to wchar_t* because they do not know the encoding // See boost::filesystem::path.hpp // The only correct way to do this is to do the conversion ourselves std::wstring pathStr = Strutil::utf8_to_utf16(path); #else std::string pathStr = path.str(); #endif #if BOOST_FILESYSTEM_VERSION >= 3 boost::system::error_code ec; unsigned long long n = boost::filesystem::remove_all (pathStr, ec); if (!ec) err.clear(); else err = ec.message(); return n; #else unsigned long long n = boost::filesystem::remove_all (pathStr); err.clear(); return n; #endif } std::string Filesystem::temp_directory_path() { #if BOOST_FILESYSTEM_VERSION >= 3 boost::system::error_code ec; boost::filesystem::path p = boost::filesystem::temp_directory_path (ec); return ec ? std::string() : p.string(); #else const char *tmpdir = getenv("TMPDIR"); if (! tmpdir) tmpdir = getenv("TMP"); if (! tmpdir) tmpdir = "/var/tmp"; if (exists (tmpdir)) return tmpdir; // punt and hope for the best return "."; #endif } std::string Filesystem::unique_path (string_view model) { #if defined(_WIN32) // boost internally doesn't use MultiByteToWideChar (CP_UTF8,... // to convert char* to wchar_t* because they do not know the encoding // See boost::filesystem::path.hpp // The only correct way to do this is to do the conversion ourselves std::wstring modelStr = Strutil::utf8_to_utf16(model); #else std::string modelStr = model.str(); #endif #if BOOST_FILESYSTEM_VERSION >= 3 boost::system::error_code ec; boost::filesystem::path p = boost::filesystem::unique_path (modelStr, ec); return ec ? std::string() : p.string(); #elif _MSC_VER char buf[TMP_MAX]; char *result = tmpnam (buf); return result ? std::string(result) : std::string(); #else char buf[L_tmpnam]; char *result = tmpnam (buf); return result ? std::string(result) : std::string(); #endif } std::string Filesystem::current_path() { #if BOOST_FILESYSTEM_VERSION >= 3 boost::system::error_code ec; boost::filesystem::path p = boost::filesystem::current_path (ec); return ec ? std::string() : p.string(); #else // Fallback if we don't have recent Boost char path[FILENAME_MAX]; #ifdef _WIN32 bool ok = _getcwd (path, sizeof(path)); #else bool ok = getcwd (path, sizeof(path)); #endif return ok ? std::string(path) : std::string(); #endif } FILE* Filesystem::fopen (string_view path, string_view mode) { #ifdef _WIN32 // on Windows fopen does not accept UTF-8 paths, so we convert to wide char std::wstring wpath = Strutil::utf8_to_utf16 (path); std::wstring wmode = Strutil::utf8_to_utf16 (mode); return ::_wfopen (wpath.c_str(), wmode.c_str()); #else // on Unix platforms passing in UTF-8 works return ::fopen (path.c_str(), mode.c_str()); #endif } void Filesystem::open (OIIO::ifstream &stream, string_view path, std::ios_base::openmode mode) { #ifdef _WIN32 // Windows std::ifstream accepts non-standard wchar_t* // On MingW, we use our own OIIO::ifstream std::wstring wpath = Strutil::utf8_to_utf16(path); stream.open (wpath.c_str(), mode); stream.seekg (0, std::ios_base::beg); // force seek, otherwise broken #else stream.open (path.c_str(), mode); #endif } void Filesystem::open (OIIO::ofstream &stream, string_view path, std::ios_base::openmode mode) { #ifdef _WIN32 // Windows std::ofstream accepts non-standard wchar_t* // On MingW, we use our own OIIO::ofstream std::wstring wpath = Strutil::utf8_to_utf16 (path); stream.open (wpath.c_str(), mode); #else stream.open (path.c_str(), mode); #endif } /// Read the entire contents of the named file and place it in str, /// returning true on success, false on failure. bool Filesystem::read_text_file (string_view filename, std::string &str) { // For info on why this is the fastest method: // http://insanecoding.blogspot.com/2011/11/how-to-read-in-file-in-c.html OIIO::ifstream in; Filesystem::open (in, filename); // N.B. for binary read: open(in, filename, std::ios::in|std::ios::binary); if (in) { std::ostringstream contents; contents << in.rdbuf(); str = contents.str(); return true; } return false; } /// Read the entire contents of the named file and place it in str, /// returning true on success, false on failure. size_t Filesystem::read_bytes (string_view path, void *buffer, size_t n, size_t pos) { size_t ret = 0; if (FILE *file = Filesystem::fopen (path, "rb")) { #ifdef _MSC_VER _fseeki64 (file, __int64(pos), SEEK_SET); #else fseeko (file, pos, SEEK_SET); #endif ret = fread (buffer, 1, n, file); fclose (file); } return ret; } std::time_t Filesystem::last_write_time (const std::string& path) { try { #ifdef _WIN32 std::wstring wpath = Strutil::utf8_to_utf16 (path); return boost::filesystem::last_write_time (wpath); #else return boost::filesystem::last_write_time (path); #endif } catch (...) { // File doesn't exist return 0; } } void Filesystem::last_write_time (const std::string& path, std::time_t time) { try { #ifdef _WIN32 std::wstring wpath = Strutil::utf8_to_utf16 (path); boost::filesystem::last_write_time (wpath, time); #else boost::filesystem::last_write_time (path, time); #endif } catch (...) { // File doesn't exist } } uint64_t Filesystem::file_size (string_view path) { try { #ifdef _WIN32 std::wstring wpath = Strutil::utf8_to_utf16 (path); return boost::filesystem::file_size (wpath); #else return boost::filesystem::file_size (path.str()); #endif } catch (...) { // File doesn't exist return 0; } } void Filesystem::convert_native_arguments (int argc, const char *argv[]) { #ifdef _WIN32 // Windows only, standard main() entry point does not accept unicode file // paths, here we retrieve wide char arguments and convert them to utf8 if (argc == 0) return; int native_argc; wchar_t **native_argv = CommandLineToArgvW (GetCommandLineW (), &native_argc); if (!native_argv || native_argc != argc) return; for (int i = 0; i < argc; i++) { std::string utf8_arg = Strutil::utf16_to_utf8 (native_argv[i]); argv[i] = ustring (utf8_arg).c_str(); } #endif } bool Filesystem::enumerate_sequence (string_view desc, std::vector &numbers) { numbers.clear (); // Split the sequence description into comma-separated subranges. std::vector ranges; Strutil::split (desc, ranges, ","); // For each subrange... BOOST_FOREACH (string_view s, ranges) { // It's START, START-FINISH, or START-FINISHxSTEP, or START-FINISHySTEP // If START>FINISH or if STEP<0, then count down. // If 'y' is used, generate the complement. std::vector range; Strutil::split (s, range, "-", 2); int first = Strutil::from_string (range[0]); int last = first; int step = 1; bool complement = false; if (range.size() > 1) { last = Strutil::from_string (range[1]); if (const char *x = strchr (range[1].c_str(), 'x')) step = (int) strtol (x+1, NULL, 10); else if (const char *x = strchr (range[1].c_str(), 'y')) { step = (int) strtol (x+1, NULL, 10); complement = true; } if (step == 0) step = 1; if (step < 0 && first < last) std::swap (first, last); if (first > last && step > 0) step = -step; } int end = last + (step > 0 ? 1 : -1); int itstep = step > 0 ? 1 : -1; for (int i = first; i != end; i += itstep) { if ((abs(i-first) % abs(step) == 0) != complement) numbers.push_back (i); } } return true; } bool Filesystem::parse_pattern (const char *pattern_, int framepadding_override, std::string &normalized_pattern, std::string &framespec) { std::string pattern (pattern_); // The pattern is either a range (e.g., "1-15#"), a // set of hash marks (e.g. "####"), or a printf-style format // string (e.g. "%04d"). #define ONERANGE_SPEC "[0-9]+(-[0-9]+((x|y)-?[0-9]+)?)?" #define MANYRANGE_SPEC ONERANGE_SPEC "(," ONERANGE_SPEC ")*" #define SEQUENCE_SPEC "(" MANYRANGE_SPEC ")?" "((#|@)+|(%[0-9]*d))" static boost::regex sequence_re (SEQUENCE_SPEC); // std::cout << "pattern >" << (SEQUENCE_SPEC) << "<\n"; boost::match_results range_match; if (! boost::regex_search (pattern, range_match, sequence_re)) { // Not a range static boost::regex all_views_re ("%[Vv]"); if (boost::regex_search (pattern, all_views_re)) { normalized_pattern = pattern; return true; } return false; } // It's a range. Generate the names by iterating through the numbers. std::string thematch (range_match[0].first, range_match[0].second); std::string thesequence (range_match[1].first, range_match[1].second); std::string thehashes (range_match[9].first, range_match[9].second); std::string theformat (range_match[11].first, range_match[11].second); std::string prefix (range_match.prefix().first, range_match.prefix().second); std::string suffix (range_match.suffix().first, range_match.suffix().second); // std::cout << "theformat: " << theformat << "\n"; std::string fmt; if (theformat.length() > 0) { fmt = theformat; } else { // Compute the amount of padding desired int padding = 0; for (int i = (int)thematch.length()-1; i >= 0; --i) { if (thematch[i] == '#') padding += 4; else if (thematch[i] == '@') padding += 1; } if (framepadding_override > 0) padding = framepadding_override; fmt = Strutil::format ("%%0%dd", padding); } // std::cout << "Format: '" << fmt << "'\n"; normalized_pattern = prefix + fmt + suffix; framespec = thesequence; return true; } bool Filesystem::enumerate_file_sequence (const std::string &pattern, const std::vector &numbers, std::vector &filenames) { filenames.clear (); for (size_t i = 0, e = numbers.size(); i < e; ++i) { std::string f = Strutil::format (pattern.c_str(), numbers[i]); filenames.push_back (f); } return true; } bool Filesystem::enumerate_file_sequence (const std::string &pattern, const std::vector &numbers, const std::vector &views, std::vector &filenames) { ASSERT (views.size() == 0 || views.size() == numbers.size()); static boost::regex view_re ("%V"), short_view_re ("%v"); filenames.clear (); for (size_t i = 0, e = numbers.size(); i < e; ++i) { std::string f = pattern; if (views.size() > 0 && ! views[i].empty()) { f = boost::regex_replace (f, view_re, views[i]); f = boost::regex_replace (f, short_view_re, views[i].substr(0, 1)); } f = Strutil::format (f.c_str(), numbers[i]); filenames.push_back (f); } return true; } bool Filesystem::scan_for_matching_filenames(const std::string &pattern, const std::vector &views, std::vector &frame_numbers, std::vector &frame_views, std::vector &filenames) { static boost::regex format_re ("%0([0-9]+)d"); static boost::regex all_views_re ("%[Vv]"), view_re ("%V"), short_view_re ("%v"); frame_numbers.clear (); frame_views.clear (); filenames.clear (); if (boost::regex_search (pattern, all_views_re)) { if (boost::regex_search (pattern, format_re)) { // case 1: pattern has format and view std::vector< std::pair< std::pair< int, string_view>, std::string> > matches; for (int i = 0, e = views.size(); i < e; ++i) { if (views[i].empty()) continue; const string_view short_view = views[i].substr (0, 1); std::vector view_numbers; std::vector view_filenames; std::string view_pattern = pattern; view_pattern = boost::regex_replace (view_pattern, view_re, views[i]); view_pattern = boost::regex_replace (view_pattern, short_view_re, short_view); if (! scan_for_matching_filenames (view_pattern, view_numbers, view_filenames)) continue; for (int j = 0, f = view_numbers.size(); j < f; ++j) { matches.push_back (std::make_pair (std::make_pair (view_numbers[j], views[i]), view_filenames[j])); } } std::sort (matches.begin(), matches.end()); for (int i = 0, e = matches.size(); i < e; ++i) { frame_numbers.push_back (matches[i].first.first); frame_views.push_back (matches[i].first.second); filenames.push_back (matches[i].second); } } else { // case 2: pattern has view, but no format std::vector< std::pair > matches; for (int i = 0, e = views.size(); i < e; ++i) { const string_view &view = views[i]; const string_view short_view = view.substr (0, 1); std::string view_pattern = pattern; view_pattern = boost::regex_replace (view_pattern, view_re, view); view_pattern = boost::regex_replace (view_pattern, short_view_re, short_view); if (exists (view_pattern)) matches.push_back (std::make_pair (view, view_pattern)); } std::sort (matches.begin(), matches.end()); for (int i = 0, e = matches.size(); i < e; ++i) { frame_views.push_back (matches[i].first); filenames.push_back (matches[i].second); } } return true; } else { // case 3: pattern has format, but no view return scan_for_matching_filenames(pattern, frame_numbers, filenames); } return true; } bool Filesystem::scan_for_matching_filenames(const std::string &pattern_, std::vector &numbers, std::vector &filenames) { numbers.clear (); filenames.clear (); std::string pattern = pattern_; // Isolate the directory name (or '.' if none was specified) std::string directory = Filesystem::parent_path (pattern); if (directory.size() == 0) { directory = "."; #ifdef _WIN32 pattern = ".\\\\" + pattern; #else pattern = "./" + pattern; #endif } if (! exists(directory)) return false; // build a regex that matches the pattern static boost::regex format_re ("%0([0-9]+)d"); boost::match_results format_match; if (! boost::regex_search (pattern, format_match, format_re)) return false; std::string thepadding (format_match[1].first, format_match[1].second); std::string prefix (format_match.prefix().first, format_match.prefix().second); std::string suffix (format_match.suffix().first, format_match.suffix().second); std::string pattern_re_str = prefix + "([0-9]{" + thepadding + ",})" + suffix; std::vector< std::pair< int, std::string > > matches; // There are some corner cases regex that could be constructed here that // are badly structured and might throw an exception. try { boost::regex pattern_re (pattern_re_str); boost::filesystem::directory_iterator end_it; #ifdef _WIN32 std::wstring wdirectory = Strutil::utf8_to_utf16 (directory); for (boost::filesystem::directory_iterator it(wdirectory); it != end_it; ++it) { #else for (boost::filesystem::directory_iterator it(directory); it != end_it; ++it) { #endif if (is_regular(it->path().string())) { const std::string f = it->path().string(); boost::match_results frame_match; if (boost::regex_match (f, frame_match, pattern_re)) { std::string thenumber (frame_match[1].first, frame_match[1].second); int frame = (int)strtol (thenumber.c_str(), NULL, 10); matches.push_back (std::make_pair (frame, f)); } } } } catch (...) { // Botched regex. Just fail. return false; } // filesystem order is undefined, so return sorted sequences std::sort (matches.begin(), matches.end()); for (size_t i = 0, e = matches.size(); i < e; ++i) { numbers.push_back (matches[i].first); filenames.push_back (matches[i].second); } return true; } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libutil/timer.cpp0000644000175000017500000000522313151711064020406 0ustar mfvmfv/* Copyright 2013 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include "OpenImageIO/timer.h" OIIO_NAMESPACE_BEGIN double Timer::seconds_per_tick = 1.0e-6; class TimerSetupOnce { public: TimerSetupOnce () { #ifdef _WIN32 // From MSDN web site LARGE_INTEGER freq; QueryPerformanceFrequency (&freq); Timer::seconds_per_tick = 1.0 / (double)freq.QuadPart; #elif defined(__APPLE__) // NOTE(boulos): Both this value and that of the windows // counterpart above only need to be calculated once. In // Manta, we stored this on the side as a scaling factor but // that would require a .cpp file (meaning timer.h can't just // be used as a header). It is also claimed that since // Leopard, Apple returns 1 for both numer and denom. mach_timebase_info_data_t time_info; mach_timebase_info(&time_info); Timer::seconds_per_tick = (1e-9*static_cast(time_info.numer))/ static_cast(time_info.denom); #endif } }; static TimerSetupOnce once; OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libutil/xxhash.cpp0000644000175000017500000006522613151711064020602 0ustar mfvmfv/* xxHash - Fast Hash algorithm Copyright (C) 2012-2014, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - xxHash source repository : http://code.google.com/p/xxhash/ - public discussion board : https://groups.google.com/forum/#!forum/lz4c */ //************************************** // Tuning parameters //************************************** // Unaligned memory access is automatically enabled for "common" CPU, such as x86. // For others CPU, the compiler will be more cautious, and insert extra code to ensure aligned access is respected. // If you know your target CPU supports unaligned memory access, you want to force this option manually to improve performance. // You can also enable this parameter if you know your input data will always be aligned (boundaries of 4, for U32). #if defined(__ARM_FEATURE_UNALIGNED) || defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) # define XXH_USE_UNALIGNED_ACCESS 1 #endif // XXH_ACCEPT_NULL_INPUT_POINTER : // If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer. // When this option is enabled, xxHash output for null input pointers will be the same as a null-length input. // This option has a very small performance cost (only measurable on small inputs). // By default, this option is disabled. To enable it, uncomment below define : // #define XXH_ACCEPT_NULL_INPUT_POINTER 1 // XXH_FORCE_NATIVE_FORMAT : // By default, xxHash library provides endian-independant Hash values, based on little-endian convention. // Results are therefore identical for little-endian and big-endian CPU. // This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format. // Should endian-independance be of no importance for your application, you may set the #define below to 1. // It will improve speed for Big-endian CPU. // This option has no impact on Little_Endian CPU. #define XXH_FORCE_NATIVE_FORMAT 0 //************************************** // Compiler Specific Options //************************************** // Disable some Visual warning messages #ifdef _MSC_VER // Visual Studio # pragma warning(disable : 4127) // disable: C4127: conditional expression is constant #endif #ifdef _MSC_VER // Visual Studio # define FORCE_INLINE static __forceinline #else # ifdef __GNUC__ # define FORCE_INLINE static inline __attribute__((always_inline)) # else # define FORCE_INLINE static inline # endif #endif //************************************** // Includes & Memory related functions //************************************** #include "OpenImageIO/hash.h" #include /* size_t */ typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; typedef struct { long long ll[ 6]; } XXH32_state_t; typedef struct { long long ll[11]; } XXH64_state_t; //#include "xxhash.h" // Modify the local functions below should you wish to use some other memory routines // for malloc(), free() #include static void* XXH_malloc(size_t s) { return malloc(s); } static void XXH_free (void* p) { free(p); } // for memcpy() #include static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); } //************************************** // Basic Types //************************************** #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L // C99 # include typedef uint8_t BYTE; typedef uint16_t U16; typedef uint32_t U32; typedef int32_t S32; typedef uint64_t U64; #else typedef unsigned char BYTE; typedef unsigned short U16; typedef unsigned int U32; typedef signed int S32; typedef unsigned long long U64; #endif #if defined(__GNUC__) && !defined(XXH_USE_UNALIGNED_ACCESS) # define _PACKED __attribute__ ((packed)) #else # define _PACKED #endif #if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__) # ifdef __IBMC__ # pragma pack(1) # else # pragma pack(push, 1) # endif #endif OIIO_NAMESPACE_BEGIN namespace xxhash { typedef struct _U32_S { U32 v; } _PACKED U32_S; typedef struct _U64_S { U64 v; } _PACKED U64_S; #if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__) # pragma pack(pop) #endif #define A32(x) (((U32_S *)(x))->v) #define A64(x) (((U64_S *)(x))->v) //*************************************** // Compiler-specific Functions and Macros //*************************************** #define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) // Note : although _rotl exists for minGW (GCC under windows), performance seems poor #if defined(_MSC_VER) # define XXH_rotl32(x,r) _rotl(x,r) # define XXH_rotl64(x,r) _rotl64(x,r) #else # define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r))) # define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r))) #endif #if defined(_MSC_VER) // Visual Studio # define XXH_swap32 _byteswap_ulong # define XXH_swap64 _byteswap_uint64 #elif GCC_VERSION >= 403 # define XXH_swap32 __builtin_bswap32 # define XXH_swap64 __builtin_bswap64 #else static inline U32 XXH_swap32 (U32 x) { return ((x << 24) & 0xff000000 ) | ((x << 8) & 0x00ff0000 ) | ((x >> 8) & 0x0000ff00 ) | ((x >> 24) & 0x000000ff ); } static inline U64 XXH_swap64 (U64 x) { return ((x << 56) & 0xff00000000000000ULL) | ((x << 40) & 0x00ff000000000000ULL) | ((x << 24) & 0x0000ff0000000000ULL) | ((x << 8) & 0x000000ff00000000ULL) | ((x >> 8) & 0x00000000ff000000ULL) | ((x >> 24) & 0x0000000000ff0000ULL) | ((x >> 40) & 0x000000000000ff00ULL) | ((x >> 56) & 0x00000000000000ffULL); } #endif //************************************** // Constants //************************************** #define PRIME32_1 2654435761U #define PRIME32_2 2246822519U #define PRIME32_3 3266489917U #define PRIME32_4 668265263U #define PRIME32_5 374761393U #define PRIME64_1 11400714785074694791ULL #define PRIME64_2 14029467366897019727ULL #define PRIME64_3 1609587929392839161ULL #define PRIME64_4 9650029242287828579ULL #define PRIME64_5 2870177450012600261ULL //************************************** // Architecture Macros //************************************** typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; #ifndef XXH_CPU_LITTLE_ENDIAN // It is possible to define XXH_CPU_LITTLE_ENDIAN externally, for example using a compiler switch static const int one = 1; # define XXH_CPU_LITTLE_ENDIAN (*(char*)(&one)) #endif //************************************** // Macros //************************************** // FIXME -- replace with with static_assert when we're on C++11. // This construct gives warnings for some compilers. #define XXH_STATIC_ASSERT(c) { enum { XXH_static_assert = 1/int(!!(c)) }; } // use only *after* variable declarations //**************************** // Memory reads //**************************** typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; FORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align) { if (align==XXH_unaligned) return endian==XXH_littleEndian ? A32(ptr) : XXH_swap32(A32(ptr)); else return endian==XXH_littleEndian ? *(U32*)ptr : XXH_swap32(*(U32*)ptr); } FORCE_INLINE U32 XXH_readLE32(const void* ptr, XXH_endianess endian) { return XXH_readLE32_align(ptr, endian, XXH_unaligned); } FORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align) { if (align==XXH_unaligned) return endian==XXH_littleEndian ? A64(ptr) : XXH_swap64(A64(ptr)); else return endian==XXH_littleEndian ? *(U64*)ptr : XXH_swap64(*(U64*)ptr); } FORCE_INLINE U64 XXH_readLE64(const void* ptr, XXH_endianess endian) { return XXH_readLE64_align(ptr, endian, XXH_unaligned); } //**************************** // Simple Hash Functions //**************************** FORCE_INLINE U32 XXH32_endian_align(const void* input, size_t len, U32 seed, XXH_endianess endian, XXH_alignment align) { const BYTE* p = (const BYTE*)input; const BYTE* bEnd = p + len; U32 h32; #define XXH_get32bits(p) XXH_readLE32_align(p, endian, align) #ifdef XXH_ACCEPT_NULL_INPUT_POINTER if (p==NULL) { len=0; bEnd=p=(const BYTE*)(size_t)16; } #endif if (len>=16) { const BYTE* const limit = bEnd - 16; U32 v1 = seed + PRIME32_1 + PRIME32_2; U32 v2 = seed + PRIME32_2; U32 v3 = seed + 0; U32 v4 = seed - PRIME32_1; do { v1 += XXH_get32bits(p) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4; v2 += XXH_get32bits(p) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4; v3 += XXH_get32bits(p) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4; v4 += XXH_get32bits(p) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4; } while (p<=limit); h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); } else { h32 = seed + PRIME32_5; } h32 += (U32) len; while (p+4<=bEnd) { h32 += XXH_get32bits(p) * PRIME32_3; h32 = XXH_rotl32(h32, 17) * PRIME32_4 ; p+=4; } while (p> 15; h32 *= PRIME32_2; h32 ^= h32 >> 13; h32 *= PRIME32_3; h32 ^= h32 >> 16; return h32; } unsigned int XXH32 (const void* input, size_t len, unsigned seed) { #if 0 // Simple version, good for code maintenance, but unfortunately slow for small inputs XXH32_state_t state; XXH32_reset(&state, seed); XXH32_update(&state, input, len); return XXH32_digest(&state); #else XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; # if !defined(XXH_USE_UNALIGNED_ACCESS) if ((((size_t)input) & 3) == 0) // Input is aligned, let's leverage the speed advantage { if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); else return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); } # endif if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); else return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); #endif } FORCE_INLINE U64 XXH64_endian_align(const void* input, size_t len, U64 seed, XXH_endianess endian, XXH_alignment align) { const BYTE* p = (const BYTE*)input; const BYTE* bEnd = p + len; U64 h64; #define XXH_get64bits(p) XXH_readLE64_align(p, endian, align) #ifdef XXH_ACCEPT_NULL_INPUT_POINTER if (p==NULL) { len=0; bEnd=p=(const BYTE*)(size_t)32; } #endif if (len>=32) { const BYTE* const limit = bEnd - 32; U64 v1 = seed + PRIME64_1 + PRIME64_2; U64 v2 = seed + PRIME64_2; U64 v3 = seed + 0; U64 v4 = seed - PRIME64_1; do { v1 += XXH_get64bits(p) * PRIME64_2; p+=8; v1 = XXH_rotl64(v1, 31); v1 *= PRIME64_1; v2 += XXH_get64bits(p) * PRIME64_2; p+=8; v2 = XXH_rotl64(v2, 31); v2 *= PRIME64_1; v3 += XXH_get64bits(p) * PRIME64_2; p+=8; v3 = XXH_rotl64(v3, 31); v3 *= PRIME64_1; v4 += XXH_get64bits(p) * PRIME64_2; p+=8; v4 = XXH_rotl64(v4, 31); v4 *= PRIME64_1; } while (p<=limit); h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); v1 *= PRIME64_2; v1 = XXH_rotl64(v1, 31); v1 *= PRIME64_1; h64 ^= v1; h64 = h64 * PRIME64_1 + PRIME64_4; v2 *= PRIME64_2; v2 = XXH_rotl64(v2, 31); v2 *= PRIME64_1; h64 ^= v2; h64 = h64 * PRIME64_1 + PRIME64_4; v3 *= PRIME64_2; v3 = XXH_rotl64(v3, 31); v3 *= PRIME64_1; h64 ^= v3; h64 = h64 * PRIME64_1 + PRIME64_4; v4 *= PRIME64_2; v4 = XXH_rotl64(v4, 31); v4 *= PRIME64_1; h64 ^= v4; h64 = h64 * PRIME64_1 + PRIME64_4; } else { h64 = seed + PRIME64_5; } h64 += (U64) len; while (p+8<=bEnd) { U64 k1 = XXH_get64bits(p); k1 *= PRIME64_2; k1 = XXH_rotl64(k1,31); k1 *= PRIME64_1; h64 ^= k1; h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; p+=8; } if (p+4<=bEnd) { h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1; h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; p+=4; } while (p> 33; h64 *= PRIME64_2; h64 ^= h64 >> 29; h64 *= PRIME64_3; h64 ^= h64 >> 32; return h64; } unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed) { #if 0 // Simple version, good for code maintenance, but unfortunately slow for small inputs XXH64_state_t state; XXH64_reset(&state, seed); XXH64_update(&state, input, len); return XXH64_digest(&state); #else XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; # if !defined(XXH_USE_UNALIGNED_ACCESS) if ((((size_t)input) & 7)==0) // Input is aligned, let's leverage the speed advantage { if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); else return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); } # endif if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); else return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); #endif } /**************************************************** * Advanced Hash Functions ****************************************************/ /*** Allocation ***/ typedef struct { U64 total_len; U32 seed; U32 v1; U32 v2; U32 v3; U32 v4; U32 mem32[4]; /* defined as U32 for alignment */ U32 memsize; } XXH_istate32_t; typedef struct { U64 total_len; U64 seed; U64 v1; U64 v2; U64 v3; U64 v4; U64 mem64[4]; /* defined as U64 for alignment */ U32 memsize; } XXH_istate64_t; XXH32_state_t* XXH32_createState(void) { XXH_STATIC_ASSERT(sizeof(XXH32_state_t) >= sizeof(XXH_istate32_t)); // A compilation error here means XXH32_state_t is not large enough return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t)); } XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr) { XXH_free(statePtr); return XXH_OK; }; XXH64_state_t* XXH64_createState(void) { XXH_STATIC_ASSERT(sizeof(XXH64_state_t) >= sizeof(XXH_istate64_t)); // A compilation error here means XXH64_state_t is not large enough return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t)); } XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr) { XXH_free(statePtr); return XXH_OK; }; /*** Hash feed ***/ XXH_errorcode XXH32_reset(XXH32_state_t* state_in, U32 seed) { XXH_istate32_t* state = (XXH_istate32_t*) state_in; state->seed = seed; state->v1 = seed + PRIME32_1 + PRIME32_2; state->v2 = seed + PRIME32_2; state->v3 = seed + 0; state->v4 = seed - PRIME32_1; state->total_len = 0; state->memsize = 0; return XXH_OK; } XXH_errorcode XXH64_reset(XXH64_state_t* state_in, unsigned long long seed) { XXH_istate64_t* state = (XXH_istate64_t*) state_in; state->seed = seed; state->v1 = seed + PRIME64_1 + PRIME64_2; state->v2 = seed + PRIME64_2; state->v3 = seed + 0; state->v4 = seed - PRIME64_1; state->total_len = 0; state->memsize = 0; return XXH_OK; } FORCE_INLINE XXH_errorcode XXH32_update_endian (XXH32_state_t* state_in, const void* input, size_t len, XXH_endianess endian) { XXH_istate32_t* state = (XXH_istate32_t *) state_in; const BYTE* p = (const BYTE*)input; const BYTE* const bEnd = p + len; #ifdef XXH_ACCEPT_NULL_INPUT_POINTER if (input==NULL) return XXH_ERROR; #endif state->total_len += len; if (state->memsize + len < 16) // fill in tmp buffer { XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len); state->memsize += (U32)len; return XXH_OK; } if (state->memsize) // some data left from previous update { XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize); { const U32* p32 = state->mem32; state->v1 += XXH_readLE32(p32, endian) * PRIME32_2; state->v1 = XXH_rotl32(state->v1, 13); state->v1 *= PRIME32_1; p32++; state->v2 += XXH_readLE32(p32, endian) * PRIME32_2; state->v2 = XXH_rotl32(state->v2, 13); state->v2 *= PRIME32_1; p32++; state->v3 += XXH_readLE32(p32, endian) * PRIME32_2; state->v3 = XXH_rotl32(state->v3, 13); state->v3 *= PRIME32_1; p32++; state->v4 += XXH_readLE32(p32, endian) * PRIME32_2; state->v4 = XXH_rotl32(state->v4, 13); state->v4 *= PRIME32_1; p32++; } p += 16-state->memsize; state->memsize = 0; } if (p <= bEnd-16) { const BYTE* const limit = bEnd - 16; U32 v1 = state->v1; U32 v2 = state->v2; U32 v3 = state->v3; U32 v4 = state->v4; do { v1 += XXH_readLE32(p, endian) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4; v2 += XXH_readLE32(p, endian) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4; v3 += XXH_readLE32(p, endian) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4; v4 += XXH_readLE32(p, endian) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4; } while (p<=limit); state->v1 = v1; state->v2 = v2; state->v3 = v3; state->v4 = v4; } if (p < bEnd) { XXH_memcpy(state->mem32, p, bEnd-p); state->memsize = (int)(bEnd-p); } return XXH_OK; } XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len) { XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) return XXH32_update_endian(state_in, input, len, XXH_littleEndian); else return XXH32_update_endian(state_in, input, len, XXH_bigEndian); } FORCE_INLINE U32 XXH32_digest_endian (const XXH32_state_t* state_in, XXH_endianess endian) { XXH_istate32_t* state = (XXH_istate32_t*) state_in; const BYTE * p = (const BYTE*)state->mem32; BYTE* bEnd = (BYTE*)(state->mem32) + state->memsize; U32 h32; if (state->total_len >= 16) { h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18); } else { h32 = state->seed + PRIME32_5; } h32 += (U32) state->total_len; while (p+4<=bEnd) { h32 += XXH_readLE32(p, endian) * PRIME32_3; h32 = XXH_rotl32(h32, 17) * PRIME32_4; p+=4; } while (p> 15; h32 *= PRIME32_2; h32 ^= h32 >> 13; h32 *= PRIME32_3; h32 ^= h32 >> 16; return h32; } U32 XXH32_digest (const XXH32_state_t* state_in) { XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) return XXH32_digest_endian(state_in, XXH_littleEndian); else return XXH32_digest_endian(state_in, XXH_bigEndian); } FORCE_INLINE XXH_errorcode XXH64_update_endian (XXH64_state_t* state_in, const void* input, size_t len, XXH_endianess endian) { XXH_istate64_t * state = (XXH_istate64_t *) state_in; const BYTE* p = (const BYTE*)input; const BYTE* const bEnd = p + len; #ifdef XXH_ACCEPT_NULL_INPUT_POINTER if (input==NULL) return XXH_ERROR; #endif state->total_len += len; if (state->memsize + len < 32) // fill in tmp buffer { XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len); state->memsize += (U32)len; return XXH_OK; } if (state->memsize) // some data left from previous update { XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize); { const U64* p64 = state->mem64; state->v1 += XXH_readLE64(p64, endian) * PRIME64_2; state->v1 = XXH_rotl64(state->v1, 31); state->v1 *= PRIME64_1; p64++; state->v2 += XXH_readLE64(p64, endian) * PRIME64_2; state->v2 = XXH_rotl64(state->v2, 31); state->v2 *= PRIME64_1; p64++; state->v3 += XXH_readLE64(p64, endian) * PRIME64_2; state->v3 = XXH_rotl64(state->v3, 31); state->v3 *= PRIME64_1; p64++; state->v4 += XXH_readLE64(p64, endian) * PRIME64_2; state->v4 = XXH_rotl64(state->v4, 31); state->v4 *= PRIME64_1; p64++; } p += 32-state->memsize; state->memsize = 0; } if (p+32 <= bEnd) { const BYTE* const limit = bEnd - 32; U64 v1 = state->v1; U64 v2 = state->v2; U64 v3 = state->v3; U64 v4 = state->v4; do { v1 += XXH_readLE64(p, endian) * PRIME64_2; v1 = XXH_rotl64(v1, 31); v1 *= PRIME64_1; p+=8; v2 += XXH_readLE64(p, endian) * PRIME64_2; v2 = XXH_rotl64(v2, 31); v2 *= PRIME64_1; p+=8; v3 += XXH_readLE64(p, endian) * PRIME64_2; v3 = XXH_rotl64(v3, 31); v3 *= PRIME64_1; p+=8; v4 += XXH_readLE64(p, endian) * PRIME64_2; v4 = XXH_rotl64(v4, 31); v4 *= PRIME64_1; p+=8; } while (p<=limit); state->v1 = v1; state->v2 = v2; state->v3 = v3; state->v4 = v4; } if (p < bEnd) { XXH_memcpy(state->mem64, p, bEnd-p); state->memsize = (int)(bEnd-p); } return XXH_OK; } XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len) { XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) return XXH64_update_endian(state_in, input, len, XXH_littleEndian); else return XXH64_update_endian(state_in, input, len, XXH_bigEndian); } FORCE_INLINE U64 XXH64_digest_endian (const XXH64_state_t* state_in, XXH_endianess endian) { XXH_istate64_t * state = (XXH_istate64_t *) state_in; const BYTE * p = (const BYTE*)state->mem64; BYTE* bEnd = (BYTE*)state->mem64 + state->memsize; U64 h64; if (state->total_len >= 32) { U64 v1 = state->v1; U64 v2 = state->v2; U64 v3 = state->v3; U64 v4 = state->v4; h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); v1 *= PRIME64_2; v1 = XXH_rotl64(v1, 31); v1 *= PRIME64_1; h64 ^= v1; h64 = h64*PRIME64_1 + PRIME64_4; v2 *= PRIME64_2; v2 = XXH_rotl64(v2, 31); v2 *= PRIME64_1; h64 ^= v2; h64 = h64*PRIME64_1 + PRIME64_4; v3 *= PRIME64_2; v3 = XXH_rotl64(v3, 31); v3 *= PRIME64_1; h64 ^= v3; h64 = h64*PRIME64_1 + PRIME64_4; v4 *= PRIME64_2; v4 = XXH_rotl64(v4, 31); v4 *= PRIME64_1; h64 ^= v4; h64 = h64*PRIME64_1 + PRIME64_4; } else { h64 = state->seed + PRIME64_5; } h64 += (U64) state->total_len; while (p+8<=bEnd) { U64 k1 = XXH_readLE64(p, endian); k1 *= PRIME64_2; k1 = XXH_rotl64(k1,31); k1 *= PRIME64_1; h64 ^= k1; h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; p+=8; } if (p+4<=bEnd) { h64 ^= (U64)(XXH_readLE32(p, endian)) * PRIME64_1; h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; p+=4; } while (p> 33; h64 *= PRIME64_2; h64 ^= h64 >> 29; h64 *= PRIME64_3; h64 ^= h64 >> 32; return h64; } unsigned long long XXH64_digest (const XXH64_state_t* state_in) { XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) return XXH64_digest_endian(state_in, XXH_littleEndian); else return XXH64_digest_endian(state_in, XXH_bigEndian); } } // namespace xxhash OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libutil/hash_test.cpp0000644000175000017500000002120113151711064021242 0ustar mfvmfv/* Copyright 2012 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include "OpenImageIO/hash.h" #include "OpenImageIO/timer.h" #include "OpenImageIO/argparse.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/unittest.h" OIIO_NAMESPACE_USING; static int iterations = 100000000; static int ntrials = 1; static bool verbose = false; static const int shortlength = 5; static const int medlength = 50; static const int longlength = 501; // Purposely not multiple of 4 static std::string shortstring (shortlength,char('a')); static std::string medstring (medlength,char('a')); static std::string longstring (longlength,char('a')); static std::vector data (1000,42); size_t dummy = 0; size_t test_bjhash_small () { void *ptr = &data[0]; int len = 2*sizeof(int); size_t a = 0; for (int i = 0, e = iterations/len; i < e; ++i) a += bjhash::hashlittle (ptr, len); return a; } size_t test_bjhash_big () { void *ptr = &data[0]; int len = (int)(data.size() * sizeof(int)); size_t a = 0; for (int i = 0, e = iterations/len; i < e; ++i) a += bjhash::hashlittle (ptr, len); return a; } size_t test_bjhash_small_words () { uint32_t *ptr = &data[0]; int len = 2*sizeof(int); size_t a = 0; for (int i = 0, e = iterations/len; i < e; ++i) a += bjhash::hashword (ptr, len/sizeof(int)); return a; } size_t test_bjhash_big_words () { uint32_t *ptr = &data[0]; int len = (int)(data.size() * sizeof(int)); size_t a = 0; for (int i = 0, e = iterations/len; i < e; ++i) a += bjhash::hashword (ptr, len/sizeof(int)); return a; } size_t test_xxhash (const void *ptr, size_t len) { size_t a = 0; for (int i = 0, e = iterations/len; i < e; ++i) a += xxhash::xxhash (ptr, len); return a; } size_t test_bjstrhash (const std::string &str) { int len = int(str.length()); size_t a = 0; for (int i = 0, e = iterations/len; i < e; ++i) { a += bjhash::strhash (str); dummy = a; } return a; } size_t test_farmhashstr (const std::string &str) { int len = int(str.length()); size_t a = 0; for (int i = 0, e = iterations/len; i < e; ++i) { a += farmhash::Hash (str); // dummy = a; } return a; } size_t test_farmhashchar (const char *str) { int len = strlen(str); size_t a = 0; for (int i = 0, e = iterations/len; i < e; ++i) { a += farmhash::Hash (str, strlen(str)); // dummy = a; } return a; } static void getargs (int argc, char *argv[]) { bool help = false; ArgParse ap; ap.options ("hash_test\n" OIIO_INTRO_STRING "\n" "Usage: hash_test [options]", // "%*", parse_files, "", "--help", &help, "Print help message", "-v", &verbose, "Verbose mode", "--iters %d", &iterations, Strutil::format("Number of iterations (default: %d)", iterations).c_str(), "--trials %d", &ntrials, "Number of trials", NULL); if (ap.parse (argc, (const char**)argv) < 0) { std::cerr << ap.geterror() << std::endl; ap.usage (); exit (EXIT_FAILURE); } if (help) { ap.usage (); exit (EXIT_FAILURE); } } int main (int argc, char *argv[]) { #if !defined(NDEBUG) || defined(OIIO_CI) || defined(OIIO_CODECOV) // For the sake of test time, reduce the default iterations for DEBUG, // CI, and code coverage builds. Explicit use of --iters or --trials // will override this, since it comes before the getargs() call. iterations /= 10; ntrials = 1; #endif getargs (argc, argv); double t; std::cout << "All times are seconds per " << iterations << " bytes.\n\n"; t = time_trial (test_bjhash_small, ntrials); std::cout << "BJ hash of small data as bytes: " << Strutil::timeintervalformat(t, 2) << "\n"; t = time_trial (test_bjhash_small_words, ntrials); std::cout << "BJ hash of small data as words: " << Strutil::timeintervalformat(t, 2) << "\n"; t = time_trial (test_bjhash_big, ntrials); std::cout << "BJ hash of big data: " << Strutil::timeintervalformat(t, 2) << "\n"; t = time_trial (test_bjhash_big_words, ntrials); std::cout << "BJ hash of big data as words: " << Strutil::timeintervalformat(t, 2) << "\n"; t = time_trial (boost::bind(test_xxhash, &data[0], 2*sizeof(data[0])), ntrials); std::cout << "XX hash of small data: " << Strutil::timeintervalformat(t, 2) << "\n"; t = time_trial (boost::bind(test_xxhash, &data[0], data.size()*sizeof(data[0])), ntrials); std::cout << "XX hash of big data: " << Strutil::timeintervalformat(t, 2) << "\n"; t = time_trial (boost::bind(test_bjstrhash, shortstring), ntrials); std::cout << "BJ strhash hash of short string: " << Strutil::timeintervalformat(t, 2) << "\n"; t = time_trial (boost::bind(test_bjstrhash, medstring), ntrials); std::cout << "BJ strhash hash of medium string: " << Strutil::timeintervalformat(t, 2) << "\n"; t = time_trial (boost::bind(test_bjstrhash, longstring), ntrials); std::cout << "BJ strhash hash of long string: " << Strutil::timeintervalformat(t, 2) << "\n"; t = time_trial (boost::bind(test_farmhashstr, shortstring), ntrials); std::cout << "farmhash of short string: " << Strutil::timeintervalformat(t, 2) << "\n"; t = time_trial (boost::bind(test_farmhashstr, medstring), ntrials); std::cout << "farmhash of medium string: " << Strutil::timeintervalformat(t, 2) << "\n"; t = time_trial (boost::bind(test_farmhashstr, longstring), ntrials); std::cout << "farmhash of long string: " << Strutil::timeintervalformat(t, 2) << "\n"; t = time_trial (boost::bind(test_farmhashchar, shortstring.c_str()), ntrials); std::cout << "farmhash of short char*: " << Strutil::timeintervalformat(t, 2) << "\n"; t = time_trial (boost::bind(test_farmhashchar, medstring.c_str()), ntrials); std::cout << "farmhash of medium char*: " << Strutil::timeintervalformat(t, 2) << "\n"; t = time_trial (boost::bind(test_farmhashchar, longstring.c_str()), ntrials); std::cout << "farmhash of long char*: " << Strutil::timeintervalformat(t, 2) << "\n"; t = time_trial (boost::bind(test_xxhash, shortstring.c_str(), shortstring.length()), ntrials); std::cout << "xxhash XH64 of short string: " << Strutil::timeintervalformat(t, 2) << "\n"; t = time_trial (boost::bind(test_xxhash, medstring.c_str(), medstring.length()), ntrials); std::cout << "xxhash XH64 of medium string: " << Strutil::timeintervalformat(t, 2) << "\n"; t = time_trial (boost::bind(test_xxhash, longstring.c_str(), longstring.length()), ntrials); std::cout << "xxhash XH64 of long string: " << Strutil::timeintervalformat(t, 2) << "\n"; return unit_test_failures; } openimageio-1.7.17~dfsg0.orig/src/libutil/ustring.cpp0000644000175000017500000003553713151711064020774 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include "OpenImageIO/export.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/ustring.h" OIIO_NAMESPACE_BEGIN #if 0 // Use regular mutex typedef mutex ustring_mutex_t; typedef lock_guard ustring_read_lock_t; typedef lock_guard ustring_write_lock_t; #elif 0 // Use spin locks typedef spin_mutex ustring_mutex_t; typedef spin_lock ustring_read_lock_t; typedef spin_lock ustring_write_lock_t; #elif 1 // Use rw spin locks typedef spin_rw_mutex ustring_mutex_t; typedef spin_rw_read_lock ustring_read_lock_t; typedef spin_rw_write_lock ustring_write_lock_t; #else // Use null locks typedef null_mutex ustring_mutex_t; typedef null_lock ustring_read_lock_t; typedef null_lock ustring_write_lock_t; #endif // NOTE: BASE_CAPACITY must be a power of 2 template struct TableRepMap { TableRepMap() : mask(BASE_CAPACITY - 1), entries(static_cast(calloc(BASE_CAPACITY, sizeof(ustring::TableRep*)))), num_entries(0), pool(static_cast(malloc(POOL_SIZE))), pool_offset(0), memory_usage(sizeof(*this) + POOL_SIZE + sizeof(ustring::TableRep*) * BASE_CAPACITY), num_lookups(0) {} ~TableRepMap() { /* just let memory leak */ } size_t get_memory_usage() { ustring_read_lock_t lock(mutex); return memory_usage; } size_t get_num_entries() { ustring_read_lock_t lock(mutex); return num_entries; } size_t get_num_lookups() { ustring_read_lock_t lock(mutex); return num_lookups; } const char* lookup(string_view str, size_t hash) { ustring_read_lock_t lock(mutex); #if 0 // NOTE: this simple increment adds a substantial amount of overhead // so keep it off by default, unless the user really wants it // NOTE2: note that in debug, asserts like the one in ustring::from_unique // can skew the number of lookups compared to release builds ++num_lookups; #endif size_t pos = hash & mask, dist = 0; for (;;) { if (entries[pos] == 0) return 0; if (entries[pos]->hashed == hash && entries[pos]->length == str.length() && strncmp(entries[pos]->c_str(), str.data(), str.length()) == 0) return entries[pos]->c_str(); ++dist; pos = (pos + dist) & mask; // quadratic probing } } const char* insert(string_view str, size_t hash) { ustring_write_lock_t lock(mutex); size_t pos = hash & mask, dist = 0; for (;;) { if (entries[pos] == 0) break; // found insert pos if (entries[pos]->hashed == hash && entries[pos]->length == str.length() && strncmp(entries[pos]->c_str(), str.data(), str.length()) == 0) return entries[pos]->c_str(); // same string is already inserted, return the one that is already in the table ++dist; pos = (pos + dist) & mask; // quadratic probing } ustring::TableRep* rep = make_rep(str, hash); entries[pos] = rep; ++num_entries; if (2 * num_entries > mask) grow(); // maintain 0.5 load factor return rep->c_str(); // rep is now in the table } private: void grow() { size_t new_mask = mask * 2 + 1; // NOTE: only increment by half because we are doubling the entries and freeing the old memory_usage += (mask + 1) * sizeof(ustring::TableRep*); ustring::TableRep** new_entries = static_cast(calloc(new_mask + 1, sizeof(ustring::TableRep*))); size_t to_copy = num_entries; for (size_t i = 0; to_copy != 0; i++) { if (entries[i] == 0) continue; size_t pos = entries[i]->hashed & new_mask, dist = 0; for (;;) { if (new_entries[pos] == 0) break; ++dist; pos = (pos + dist) & new_mask; // quadratic probing } new_entries[pos] = entries[i]; to_copy--; } free(entries); entries = new_entries; mask = new_mask; } ustring::TableRep* make_rep(string_view str, size_t hash) { char* repmem = pool_alloc(sizeof(ustring::TableRep) + str.length() + 1); return new (repmem) ustring::TableRep (str, hash);; } char* pool_alloc(size_t len) { if (len >= POOL_SIZE) { memory_usage += len; return (char*) malloc(len); // no need to try and use the pool } if (pool_offset + len > POOL_SIZE) { // NOTE: old pool will leak - this is ok because ustrings cannot be freed memory_usage += POOL_SIZE; pool = (char*) malloc(POOL_SIZE); pool_offset = 0; } char* result = pool + pool_offset; pool_offset += len; return result; } OIIO_CACHE_ALIGN ustring_mutex_t mutex; size_t mask; ustring::TableRep** entries; size_t num_entries; char* pool; size_t pool_offset; size_t memory_usage; OIIO_CACHE_ALIGN size_t num_lookups; }; #if 0 // Naive map with a single lock for the whole table typedef TableRepMap<1 << 20, 4 << 20> UstringTable; #else // Optimized map broken up into chunks by the top bits of the hash. // This helps reduce the amount of contention for locks. struct UstringTable { const char* lookup(string_view str, size_t hash) { return whichbin(hash).lookup(str, hash); } const char* insert(string_view str, size_t hash) { return whichbin(hash).insert(str, hash); } size_t get_memory_usage() { size_t mem = 0; for (int i = 0; i < NUM_BINS; i++) mem += bins[i].get_memory_usage(); return mem; } size_t get_num_entries() { size_t num = 0; for (int i = 0; i < NUM_BINS; i++) num += bins[i].get_num_entries(); return num; } size_t get_num_lookups() { size_t num = 0; for (int i = 0; i < NUM_BINS; i++) num += bins[i].get_num_lookups(); return num; } private: enum { BIN_SHIFT = 5, NUM_BINS = 1 << BIN_SHIFT, // NOTE: this guarentees NUM_BINS is a power of 2 TOP_SHIFT = 8 * sizeof(size_t) - BIN_SHIFT }; typedef TableRepMap<(1 << 20) / NUM_BINS, (4 << 20) / NUM_BINS> Bin; Bin bins[NUM_BINS]; Bin& whichbin(size_t hash) { // use the top bits of the hash to pick a bin // (lower bits choose position within the table) return bins[(hash >> TOP_SHIFT) % NUM_BINS]; } }; #endif // This string is here so that we can return sensible values of str when the ustring's pointer is NULL std::string ustring::empty_std_string; namespace { // anonymous static UstringTable & ustring_table () { static OIIO_CACHE_ALIGN UstringTable table; return table; } } // end anonymous namespace // Put a ustring in the global scope to force at least one call to // make_unique to happen before main(), i.e. before threads are launched, // in order to eliminate any possible thread collision on construction of // the ustring_table statically declared within make_unique. namespace pvt { static ustring ustring_force_make_unique_call(""); } namespace { // Definitions to let us access libc++ string internals. // See libc++ file for details. #ifdef _LIBCPP_ALTERNATE_STRING_LAYOUT struct libcpp_string__long { std::string::pointer __data_; std::string::size_type __size_; std::string::size_type __cap_; }; #if _LIBCPP_BIG_ENDIAN enum {libcpp_string__long_mask = 0x1ul}; #else // _LIBCPP_BIG_ENDIAN enum {libcpp_string__long_mask = ~(std::string::size_type(~0) >> 1)}; #endif // _LIBCPP_BIG_ENDIAN #else struct libcpp_string__long { std::string::size_type __cap_; std::string::size_type __size_; std::string::pointer __data_; }; #if _LIBCPP_BIG_ENDIAN enum {libcpp_string__long_mask = ~(std::string::size_type(~0) >> 1)}; #else // _LIBCPP_BIG_ENDIAN enum {libcpp_string__long_mask = 0x1ul}; #endif // _LIBCPP_BIG_ENDIAN #endif enum {libcpp_string__min_cap = (sizeof(libcpp_string__long) - 1)/sizeof(std::string::value_type) > 2 ? (sizeof(libcpp_string__long) - 1)/sizeof(std::string::value_type) : 2}; } ustring::TableRep::TableRep (string_view strref, size_t hash) : hashed(hash) { length = strref.length(); memcpy ((char *)c_str(), strref.data(), length); ((char *)c_str())[length] = 0; // We don't want the internal 'std::string str' to redundantly store the // chars, along with our own allocation. So we use our knowledge of the // internal structure of std::string (for certain compilers) to force // the std::string to make it point to our chars! In such a case, the // destructor will be careful not to allow a deallocation. #if defined(__GNUC__) && !defined(_LIBCPP_VERSION) && defined(_GLIBCXX_USE_CXX11_ABI) && _GLIBCXX_USE_CXX11_ABI // NEW gcc ABI // FIXME -- do something smart with this. #elif defined(__GNUC__) && !defined(_LIBCPP_VERSION) // OLD gcc ABI // It turns out that the first field of a gcc std::string is a pointer // to the characters within the basic_string::_Rep. We merely redirect // that pointer, though for std::string to function properly, the chars // must be preceeded immediately in memory by the rest of // basic_string::_Rep, consisting of length, capacity and refcount // fields. And we have designed our TableRep to do just that! So now // we redirect the std::string's pointer to our own characters and its // mocked-up _Rep. // // See /usr/include/c++/VERSION/bits/basic_string.h for the details of // gcc's std::string implementation. dummy_capacity = length; dummy_refcount = 1; // so it never frees *(const char **)&str = c_str(); DASSERT (str.c_str() == c_str() && str.size() == length); return; #elif defined(_LIBCPP_VERSION) // libc++ uses a different std::string representation than gcc. For // long char sequences, it's two size_t's (capacity & length) followed // by the pointer to allocated characters. (Gory detail: see the // definitions above for how it varies slightly with endianness and // _LIBCPP_ALTERNATE_STRING_LAYOUT.) For short enough sequences, it's a // single byte length followed immediately by the chars (the total being // the same size as the long string). There's no savings of space or // allocations to be had for short strings, so we just let those behave // as normal. But if it's going to make a long string (we can tell from // the length), we construct it ourselves, forcing the pointer to point // to the charcters in the TableRep we allocated. if (length >= libcpp_string__min_cap /* it'll be a "long string" */) { ((libcpp_string__long *)&str)->__cap_ = libcpp_string__long_mask | (length+1); ((libcpp_string__long *)&str)->__size_ = length; ((libcpp_string__long *)&str)->__data_ = (char *)c_str(); DASSERT (str.c_str() == c_str() && str.size() == length); return; } #endif // Remaining cases - just assign the internal string. This may result // in double allocation for the chars. If you care about that, do // something special for your platform, much like we did for gcc and // libc++ above. (Windows users, I'm talking to you.) str = strref; } ustring::TableRep::~TableRep () { if (str.c_str() == c_str()) { // This is one of those cases where we've carefully doctored the // string to point to our allocated characters. To make a safe // string destroy, now force it to look like an empty string. std::string empty; memcpy (&str, &empty, sizeof(std::string)); } } const char * ustring::make_unique (string_view strref) { UstringTable &table (ustring_table()); // Eliminate NULLs if (! strref.data()) strref = string_view("", 0); size_t hash = Strutil::strhash(strref); // Check the ustring table to see if this string already exists. If so, // construct from its canonical representation. // NOTE: all locking is performed internally to the table implementation const char* result = table.lookup(strref, hash); return result ? result : table.insert(strref, hash); } std::string ustring::getstats (bool verbose) { UstringTable &table (ustring_table()); std::ostringstream out; size_t n_l = table.get_num_lookups(); size_t n_e = table.get_num_entries(); size_t mem = table.get_memory_usage(); if (verbose) { out << "ustring statistics:\n"; if (n_l) // NOTE: see #if 0 above out << " ustring requests: " << n_l << ", unique " << n_e << "\n"; else out << " unique strings: " << n_e << "\n"; out << " ustring memory: " << Strutil::memformat(mem) << "\n"; } else { if (n_l) // NOTE: see #if 0 above out << "requests: " << n_l << ", "; out << "unique " << n_e << ", " << Strutil::memformat(mem); } return out.str(); } size_t ustring::memory () { UstringTable &table (ustring_table()); return table.get_memory_usage(); } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libutil/optparser_test.cpp0000644000175000017500000000661013151711064022345 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include "OpenImageIO/platform.h" #include "OpenImageIO/optparser.h" #include "OpenImageIO/unittest.h" OIIO_NAMESPACE_USING; class MySystem { public: MySystem() : i(0), f(0) { } bool attribute (const std::string &name, int value) { std::cout << "iattribute '" << name << "' = " << value << "\n"; if (name == "i") { i = value; return true; } return false; } bool attribute (const std::string &name, float value) { std::cout << "fattribute '" << name << "' = " << value << "\n"; if (name == "f") { f = value; return true; } return false; } bool attribute (const std::string &name, const std::string &value) { std::cout << "sattribute '" << name << "' = '" << value << "'\n"; if (name == "s") { s = value; return true; } return false; } int i; float f; std::string s; }; void test_optparser () { MySystem sys; optparser (sys, "i=14"); OIIO_CHECK_EQUAL (sys.i, 14); optparser (sys, "i=-28"); OIIO_CHECK_EQUAL (sys.i, -28); optparser (sys, "f=6.28"); OIIO_CHECK_EQUAL (sys.f, 6.28f); optparser (sys, "f=-56.0"); OIIO_CHECK_EQUAL (sys.f, -56.0f); optparser (sys, "f=-1."); OIIO_CHECK_EQUAL (sys.f, -1.0f); optparser (sys, "s=foo"); OIIO_CHECK_EQUAL (sys.s, "foo"); optparser (sys, "s=\"foo, bar\""); OIIO_CHECK_EQUAL (sys.s, "foo, bar"); optparser (sys, "f=256.29,s=\"phone call\",i=100"); OIIO_CHECK_EQUAL (sys.i, 100); OIIO_CHECK_EQUAL (sys.f, 256.29f); OIIO_CHECK_EQUAL (sys.s, "phone call"); } int main (int argc, char *argv[]) { test_optparser (); return unit_test_failures; } openimageio-1.7.17~dfsg0.orig/src/libutil/errorhandler.cpp0000644000175000017500000001207713151711064021762 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include "OpenImageIO/strutil.h" #include "OpenImageIO/errorhandler.h" #include "OpenImageIO/thread.h" OIIO_NAMESPACE_BEGIN namespace { static ErrorHandler default_handler_instance; static mutex err_mutex; } ErrorHandler & ErrorHandler::default_handler () { return default_handler_instance; } void ErrorHandler::vInfo (const char *format, va_list argptr) { if (verbosity() >= VERBOSE) { std::string msg = Strutil::vformat (format, argptr); (*this) (EH_INFO, msg); } } void ErrorHandler::vWarning (const char *format, va_list argptr) { if (verbosity() >= NORMAL) { std::string msg = Strutil::vformat (format, argptr); (*this) (EH_WARNING, msg); } } void ErrorHandler::vError (const char *format, va_list argptr) { std::string msg = Strutil::vformat (format, argptr); (*this) (EH_ERROR, msg); } void ErrorHandler::vSevere (const char *format, va_list argptr) { std::string msg = Strutil::vformat (format, argptr); (*this) (EH_SEVERE, msg); } void ErrorHandler::vMessage (const char *format, va_list argptr) { if (verbosity() > QUIET) { std::string msg = Strutil::vformat (format, argptr); (*this) (EH_MESSAGE, msg); } } #ifndef NDEBUG void ErrorHandler::vDebug (const char *format, va_list argptr) { if (verbosity() > QUIET) { std::string msg = Strutil::vformat (format, argptr); (*this) (EH_MESSAGE, msg); } } #endif void ErrorHandler::info (const char *format, ...) { if (verbosity() >= VERBOSE) { va_list argptr; va_start (argptr, format); vInfo (format, argptr); va_end (argptr); } } void ErrorHandler::warning (const char *format, ...) { if (verbosity() >= NORMAL) { va_list argptr; va_start (argptr, format); vWarning (format, argptr); va_end (argptr); } } void ErrorHandler::error (const char *format, ...) { va_list argptr; va_start (argptr, format); vError (format, argptr); va_end (argptr); } void ErrorHandler::severe (const char *format, ...) { va_list argptr; va_start (argptr, format); vSevere (format, argptr); va_end (argptr); } void ErrorHandler::message (const char *format, ...) { if (verbosity() > QUIET) { va_list argptr; va_start (argptr, format); vMessage (format, argptr); va_end (argptr); } } #ifndef NDEBUG void ErrorHandler::debug (const char *format, ...) { if (verbosity() > QUIET) { va_list argptr; va_start (argptr, format); vDebug (format, argptr); va_end (argptr); } } #endif void ErrorHandler::operator() (int errcode, const std::string &msg) { lock_guard guard (err_mutex); switch (errcode & 0xffff0000) { case EH_INFO : if (verbosity() >= VERBOSE) fprintf (stdout, "INFO: %s\n", msg.c_str()); break; case EH_WARNING : if (verbosity() >= NORMAL) fprintf (stderr, "WARNING: %s\n", msg.c_str()); break; case EH_ERROR : fprintf (stderr, "ERROR: %s\n", msg.c_str()); break; case EH_SEVERE : fprintf (stderr, "SEVERE ERROR: %s\n", msg.c_str()); break; case EH_DEBUG : #ifdef NDEBUG break; #endif default : if (verbosity() > QUIET) fprintf (stdout, "%s", msg.c_str()); break; } fflush (stdout); fflush (stderr); } OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/libutil/timer_test.cpp0000644000175000017500000001342513151711064021450 0ustar mfvmfv/* Copyright 2012 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "OpenImageIO/imageio.h" #include "OpenImageIO/sysutil.h" #include "OpenImageIO/argparse.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/timer.h" #include "OpenImageIO/unittest.h" #include OIIO_NAMESPACE_USING; static int parse_files (int argc, const char *argv[]) { // input_filename = ustring(argv[0]); return 0; } static void getargs (int argc, char *argv[]) { bool help = false; ArgParse ap; ap.options ("timer_test\n" OIIO_INTRO_STRING "\n" "Usage: timer_test [options]", "%*", parse_files, "", "--help", &help, "Print help message", NULL); if (ap.parse (argc, (const char**)argv) < 0) { std::cerr << ap.geterror() << std::endl; ap.usage (); exit (EXIT_FAILURE); } if (help) { ap.usage (); exit (EXIT_FAILURE); } } int main (int argc, char **argv) { getargs (argc, argv); // First, just compute and print how expensive a Timer begin/end is, // in cycles per second. { Timer timer; int n = 10000000; int zeroes = 0; Timer::ticks_t biggest = 0; for (int i = 0; i < n; ++i) { Timer t; Timer::ticks_t ticks = t.ticks(); // force getting the time if (!ticks) ++zeroes; if (ticks > biggest) biggest = ticks; } std::cout << "Timer begin/end cost is " << double(n)/timer() << " /sec\n"; std::cout << "Out of " << n << " queries, " << zeroes << " had no time\n"; std::cout << "Longest was " << Timer::seconds(biggest) << " s.\n"; } const int interval = 100000; // 1/10 sec double eps = 0.01; // slop we allow in our timings #ifdef __APPLE__ eps = 0.03; // On some Apple OSX systems (especially >= 10.10 Yosemite), a feature // called "timer coalescing" causes sleep/wake events to merge in order // to produce longer idle periods for the CPU to go into a lower power // state. This tends to make usleep() less reliable in its timing. // // One (permanent) fix is to disable timer coalescing with this command: // $ sudo sysctl -w kern.timer.coalescing_enabled=0 // But you want better power use, so instead we just increase the timing // tolereance on Apple to make this test pass. # if defined(OIIO_CI) || defined(OIIO_CODECOV) // It seems especially bad on Travis, give extra time slop. eps *= 3; # endif #endif // Verify that Timer(false) doesn't start Timer all (Timer::StartNow); Timer selective (Timer::DontStartNow); Sysutil::usleep (interval); OIIO_CHECK_EQUAL_THRESH (selective(), 0.0, eps); OIIO_CHECK_EQUAL_THRESH (all(), 0.1, eps); // Make sure start/stop work selective.start (); Sysutil::usleep (interval); OIIO_CHECK_EQUAL_THRESH (selective(), 0.1, eps); OIIO_CHECK_EQUAL_THRESH (all(), 0.2, eps); selective.stop (); Sysutil::usleep (interval); OIIO_CHECK_EQUAL_THRESH (selective(), 0.1, eps); OIIO_CHECK_EQUAL_THRESH (all(), 0.3, eps); std::cout << "Checkpoint: All " << all() << " selective " << selective() << "\n"; // Test reset() -- should set selective to 0 and turn it off selective.reset(); Sysutil::usleep (interval); OIIO_CHECK_EQUAL_THRESH (selective(), 0.0, eps); OIIO_CHECK_EQUAL_THRESH (all(), 0.4, eps); selective.start(); Sysutil::usleep (interval); OIIO_CHECK_EQUAL_THRESH (selective(), 0.1, eps); OIIO_CHECK_EQUAL_THRESH (all(), 0.5, eps); // Test lap() double lap = selective.lap(); // lap=.1, select.time_since_start OIIO_CHECK_EQUAL_THRESH (lap, 0.1, eps); OIIO_CHECK_EQUAL_THRESH (selective(), 0.1, eps); OIIO_CHECK_EQUAL_THRESH (selective.time_since_start(), 0.0, eps); OIIO_CHECK_EQUAL_THRESH (all(), 0.5, eps); Sysutil::usleep (interval); OIIO_CHECK_EQUAL_THRESH (selective(), 0.2, eps); OIIO_CHECK_EQUAL_THRESH (selective.time_since_start(), 0.1, eps); OIIO_CHECK_EQUAL_THRESH (all(), 0.6, eps); std::cout << "Checkpoint2: All " << all() << " selective " << selective() << "\n"; return unit_test_failures; } openimageio-1.7.17~dfsg0.orig/src/libutil/typedesc.cpp0000644000175000017500000003427513151711064021117 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/ustring.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/typedesc.h" OIIO_NAMESPACE_BEGIN TypeDesc::TypeDesc (string_view typestring) : basetype(UNKNOWN), aggregate(SCALAR), vecsemantics(NOXFORM), reserved(0), arraylen(0) { fromstring (typestring); } namespace { static int basetype_size[] = { 0, // UNKNOWN 0, // VOID sizeof(unsigned char), // UCHAR sizeof(char), // CHAR sizeof(unsigned short), // USHORT sizeof(short), // SHORT sizeof(unsigned int), // UINT sizeof(int), // INT sizeof(unsigned long long), // ULONGLONG sizeof(long long), // LONGLONG sizeof(float)/2, // HALF sizeof(float), // FLOAT sizeof(double), // DOUBLE sizeof(char *), // STRING sizeof(void *) // PTR }; } size_t TypeDesc::basesize () const { DASSERT (sizeof(basetype_size)/sizeof(basetype_size[0]) == TypeDesc::LASTBASE); DASSERT (basetype < TypeDesc::LASTBASE); return basetype_size[basetype]; } bool TypeDesc::is_floating_point () const { static bool isfloat[] = { 0, // UNKNOWN 0, // VOID 0, // UCHAR 0, // CHAR 0, // USHORT 0, // SHORT 0, // UINT 0, // INT 0, // ULONGLONG 0, // LONGLONG 1, // HALF 1, // FLOAT 1, // DOUBLE 0, // STRING 0 // PTR }; DASSERT (sizeof(isfloat)/sizeof(isfloat[0]) == TypeDesc::LASTBASE); DASSERT (basetype < TypeDesc::LASTBASE); return isfloat[basetype]; } bool TypeDesc::is_signed () const { static bool issigned[] = { 0, // UNKNOWN 0, // VOID 0, // UCHAR 1, // CHAR 0, // USHORT 1, // SHORT 0, // UINT 1, // INT 0, // ULONGLONG 1, // LONGLONG 1, // HALF 1, // FLOAT 1, // DOUBLE 0, // STRING 0 // PTR }; DASSERT (sizeof(issigned)/sizeof(issigned[0]) == TypeDesc::LASTBASE); DASSERT (basetype < TypeDesc::LASTBASE); return issigned[basetype]; } namespace { static const char * basetype_name[] = { "unknown", // UNKNOWN "void", // VOID/NONE "uint8", // UCHAR "int8", // CHAR "uint16", // USHORT "int16", // SHORT "uint", // UINT "int", // INT "uint64", // ULONGLONG "int64", // LONGLONG "half", // HALF "float", // FLOAT "double", // DOUBLE "string", // STRING "pointer" // PTR }; static const char * basetype_code[] = { "unknown", // UNKNOWN "void", // VOID/NONE "uc", // UCHAR "c", // CHAR "us", // USHORT "s", // SHORT "ui", // UINT "i", // INT "ull", // ULONGLONG "ll", // LONGLONG "h", // HALF "f", // FLOAT "d", // DOUBLE "str", // STRING "ptr" // PTR }; } const char * TypeDesc::c_str () const { // FIXME : how about a per-thread cache of the last one or two, so // we don't have to re-assemble strings all the time? // Timecode and Keycode are hard coded if (basetype == UINT && vecsemantics == TIMECODE && arraylen == 2) return ustring("timecode").c_str(); else if (basetype == INT && vecsemantics == KEYCODE && arraylen == 7) return ustring("keycode").c_str(); std::string result; if (aggregate == SCALAR) result = basetype_name[basetype]; else if (aggregate == MATRIX44 && basetype == FLOAT) result = "matrix"; else if (aggregate == MATRIX33 && basetype == FLOAT) result = "matrix33"; else if (aggregate == VEC4 && basetype == FLOAT && vecsemantics == NOXFORM) result = "float4"; else if (vecsemantics == NOXFORM) { const char *agg = ""; switch (aggregate) { case VEC2 : agg = "vec2"; break; case VEC3 : agg = "vec3"; break; case VEC4 : agg = "vec4"; break; case MATRIX33 : agg = "matrix33"; break; case MATRIX44 : agg = "matrix"; break; } result = std::string (agg) + basetype_code[basetype]; } else { // Special names for vector semantics const char *vec = ""; switch (vecsemantics) { case COLOR : vec = "color"; break; case POINT : vec = "point"; break; case VECTOR : vec = "vector"; break; case NORMAL : vec = "normal"; break; default: ASSERT (0 && "Invalid vector semantics"); } const char *agg = ""; switch (aggregate) { case VEC2 : agg = "2"; break; case VEC4 : agg = "4"; break; case MATRIX33 : agg = "matrix33"; break; case MATRIX44 : agg = "matrix44"; break; } result = std::string (vec) + std::string (agg); if (basetype != FLOAT) result += basetype_code[basetype]; } if (arraylen > 0) result += Strutil::format ("[%d]", arraylen); else if (arraylen < 0) result += "[]"; return ustring(result).c_str(); } // Copy src into dst until you hit the end, find a delimiter charcter, // or have copied maxlen-1 characters, whichever comes first. Add a // terminating null charcter. Return the number of characters copied. inline size_t copy_until (const char *src, const char *delim, char *dst, size_t maxlen) { size_t i = 0; while (src[i] && i < maxlen-1) { bool found_delim = false; for (int d = 0; delim[d]; ++d) if (src[i] == delim[d]) found_delim = true; if (found_delim) break; dst[i] = src[i]; ++i; } dst[i] = 0; return i; } size_t TypeDesc::fromstring (string_view typestring) { *this = TypeDesc::UNKNOWN; string_view orig = typestring; if (typestring.empty()) { return 0; } // The first "word" should be a type name. string_view type = Strutil::parse_identifier (typestring); // Check the scalar types in our table above TypeDesc t; for (int i = 0; i < LASTBASE; ++i) { if (type == basetype_name[i]) { t.basetype = i; break; } } // Some special case names for aggregates if (t.basetype != UNKNOWN) { // already solved } else if (type == "color") t = TypeColor; else if (type == "point") t = TypePoint; else if (type == "vector") t = TypeVector; else if (type == "normal") t = TypeNormal; else if (type == "matrix33") t = TypeMatrix33; else if (type == "matrix" || type == "matrix44") t = TypeMatrix44; else if (type == "timecode") t = TypeTimeCode; else { return 0; // unknown } // Is there an array length following the type name? if (Strutil::parse_char (typestring, '[')) { int arraylen = -1; Strutil::parse_int (typestring, arraylen); if (! Strutil::parse_char (typestring, ']')) return 0; // malformed t.arraylen = arraylen; } *this = t; return orig.length() - typestring.length(); } template inline std::string sprintt (TypeDesc type, const char *format, const char *aggregate_delim, const char *aggregate_sep, const char *array_delim, const char *array_sep, T *v) { std::string val; if (type.arraylen) val += array_delim[0]; const size_t n = type.arraylen ? type.arraylen : 1; for (size_t i = 0; i < n; ++i) { if (type.aggregate > 1) val += aggregate_delim[0]; for (int j = 0; j < (int)type.aggregate; ++j, ++v) { val += Strutil::format (format, *v); if (type.aggregate > 1 && j < type.aggregate - 1) val += aggregate_sep; } if (type.aggregate > 1) val += aggregate_delim[1]; if (i < n - 1) val += array_sep; } if (type.arraylen) val += array_delim[1]; return val; } std::string tostring (TypeDesc type, const void *data, const char *float_fmt, const char *string_fmt, const char *aggregate_delim, const char *aggregate_sep, const char *array_delim, const char *array_sep) { // Perhaps there is a way to use CType<> with a dynamic argument? switch (type.basetype) { case TypeDesc::UNKNOWN: return sprintt (type, "%p", aggregate_delim, aggregate_sep, array_delim, array_sep, (void **)data); case TypeDesc::NONE: return sprintt (type, "None", aggregate_delim, aggregate_sep, array_delim, array_sep, (void **)data); case TypeDesc::UCHAR: return sprintt (type, "%uhh", aggregate_delim, aggregate_sep, array_delim, array_sep, (unsigned char *)data); case TypeDesc::CHAR: return sprintt (type, "%dhh", aggregate_delim, aggregate_sep, array_delim, array_sep, (char *)data); case TypeDesc::USHORT: return sprintt (type, "%uh", aggregate_delim, aggregate_sep, array_delim, array_sep, (unsigned short *)data); case TypeDesc::SHORT: return sprintt (type, "%dh", aggregate_delim, aggregate_sep, array_delim, array_sep, (short *)data); case TypeDesc::UINT: return sprintt (type, "%u", aggregate_delim, aggregate_sep, array_delim, array_sep, (unsigned int *)data); case TypeDesc::INT: return sprintt (type, "%d", aggregate_delim, aggregate_sep, array_delim, array_sep, (int *)data); case TypeDesc::ULONGLONG: return sprintt (type, "%ull", aggregate_delim, aggregate_sep, array_delim, array_sep, (unsigned long long *)data); case TypeDesc::LONGLONG: return sprintt (type, "%dll", aggregate_delim, aggregate_sep, array_delim, array_sep, (long long *)data); case TypeDesc::HALF: return sprintt (type, float_fmt, aggregate_delim, aggregate_sep, array_delim, array_sep, (half *) data); case TypeDesc::FLOAT: return sprintt (type, float_fmt, aggregate_delim, aggregate_sep, array_delim, array_sep, (float *)data); case TypeDesc::DOUBLE: return sprintt (type, float_fmt, aggregate_delim, aggregate_sep, array_delim, array_sep, (double *)data); case TypeDesc::STRING: return sprintt (type, string_fmt, aggregate_delim, aggregate_sep, array_delim, array_sep, (char **)data); case TypeDesc::PTR: return sprintt (type, "%p", aggregate_delim, aggregate_sep, array_delim, array_sep, (void **)data); default: return ""; } } bool TypeDesc::operator< (const TypeDesc &x) const { if (basetype != x.basetype) return basetype < x.basetype; if (aggregate != x.aggregate) return aggregate < x.aggregate; if (arraylen != x.arraylen) return arraylen < x.arraylen; if (vecsemantics != x.vecsemantics) return vecsemantics < x.vecsemantics; return false; // they are equal } const TypeDesc TypeDesc::TypeFloat (TypeDesc::FLOAT); const TypeDesc TypeDesc::TypeColor (TypeDesc::FLOAT, TypeDesc::VEC3, TypeDesc::COLOR); const TypeDesc TypeDesc::TypePoint (TypeDesc::FLOAT, TypeDesc::VEC3, TypeDesc::POINT); const TypeDesc TypeDesc::TypeVector (TypeDesc::FLOAT, TypeDesc::VEC3, TypeDesc::VECTOR); const TypeDesc TypeDesc::TypeNormal (TypeDesc::FLOAT, TypeDesc::VEC3, TypeDesc::NORMAL); const TypeDesc TypeDesc::TypeMatrix33 (TypeDesc::FLOAT,TypeDesc::MATRIX33); const TypeDesc TypeDesc::TypeMatrix44 (TypeDesc::FLOAT,TypeDesc::MATRIX44); const TypeDesc TypeDesc::TypeMatrix = TypeDesc::TypeMatrix44; const TypeDesc TypeDesc::TypeString (TypeDesc::STRING); const TypeDesc TypeDesc::TypeInt (TypeDesc::INT); const TypeDesc TypeDesc::TypeHalf (TypeDesc::HALF); const TypeDesc TypeDesc::TypeTimeCode (TypeDesc::UINT, TypeDesc::SCALAR, TypeDesc::TIMECODE, 2); const TypeDesc TypeDesc::TypeKeyCode (TypeDesc::INT, TypeDesc::SCALAR, TypeDesc::KEYCODE, 7); const TypeDesc TypeDesc::TypeFloat4 (TypeDesc::FLOAT, TypeDesc::VEC4); OIIO_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/fits.imageio/0000755000175000017500000000000013151711064017472 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/fits.imageio/fits_pvt.cpp0000644000175000017500000000741113151711064022037 0ustar mfvmfv/* Copyright 2008-2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "fits_pvt.h" #include "OpenImageIO/strutil.h" OIIO_PLUGIN_NAMESPACE_BEGIN namespace fits_pvt { std::string num2str (float val) { std::stringstream out ; out << val; std::string result (20 - out.str().size(), ' '); result += out.str (); return result; } std::string create_card (std::string keyname, std::string value) { Strutil::to_upper (keyname); if (keyname.substr (0, 7) == "COMMENT" || keyname.substr (0, 7) == "HISTORY") keyname = keyname.substr (0, 7) + " "; else if (keyname.substr (0, 8) == "HIERARCH") keyname = "HIERARCH"; else { // other keynames are separated from values by "= " keyname.resize (8, ' '); keyname += "= "; } std::string card = keyname; // boolean values are placed on byte 30 of the card // (20 of the value field) if (value.size () == 1) { std::string tmp (19, ' '); value = tmp + value; } card += value; card.resize (80, ' '); return card; } void unpack_card (const std::string &card, std::string &keyname, std::string &value) { keyname.clear (); value.clear (); // extracting keyname - first 8 bytes of the keyword (always) // we strip spaces that are placed after keyword name keyname = Strutil::strip (card.substr(0,8)); // the value starts at 10 byte of the card if "=" is present at 8 byte // or at 8 byte otherwise int start = 10; if (card[8] != '=') start = 8; // copy of the card with keyword name stripped (only value and comment) std::string card_cpy = card.substr (start, card.size ()); card_cpy = Strutil::strip (card_cpy); // retrieving value and get rid of the comment size_t begin = 0, end = std::string::npos; if (card_cpy[0] == '\'') { begin = 1; end = card_cpy.find ("'", 1); } else { end = card_cpy.find ("/", 1); } // after creating substring we strip NULL chars from the end // without this some strings are broken: see HISTORY keywords // in ftt4b/file003.fits test image for example value = Strutil::strip (card_cpy.substr (begin, end-begin).c_str()); } } // namespace fits_pvt OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/fits.imageio/CMakeLists.txt0000644000175000017500000000007413151711064022233 0ustar mfvmfvadd_oiio_plugin (fitsinput.cpp fitsoutput.cpp fits_pvt.cpp) openimageio-1.7.17~dfsg0.orig/src/fits.imageio/fitsinput.cpp0000644000175000017500000003071713151711064022233 0ustar mfvmfv/* Copyright 2008-2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include "fits_pvt.h" OIIO_PLUGIN_NAMESPACE_BEGIN // Obligatory material to make this a recognizeable imageio plugin OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT int fits_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* fits_imageio_library_version () { return NULL; } OIIO_EXPORT ImageInput *fits_input_imageio_create () { return new FitsInput; } OIIO_EXPORT const char *fits_input_extensions[] = { "fits", NULL }; OIIO_PLUGIN_EXPORTS_END bool FitsInput::valid_file (const std::string &filename) const { FILE *fd = Filesystem::fopen (filename, "rb"); if (!fd) return false; char magic[6] = {0}; bool ok = (fread (magic, 1, 6, fd) == 6) && !strncmp (magic, "SIMPLE", 6); fclose (fd); return ok; } bool FitsInput::open (const std::string &name, ImageSpec &spec) { // saving 'name' for later use m_filename = name; // checking if the file exists and can be opened in READ mode m_fd = Filesystem::fopen (m_filename, "rb"); if (!m_fd) { error ("Could not open file \"%s\"", m_filename.c_str ()); return false; } // checking if the file is FITS file char magic[6] = {0}; if (fread (magic, 1, 6, m_fd) != 6) { error ("%s isn't a FITS file", m_filename.c_str ()); return false; // Read failed } if (strncmp (magic, "SIMPLE", 6)) { error ("%s isn't a FITS file", m_filename.c_str ()); close (); return false; } // moving back to the start of the file fseek (m_fd, 0, SEEK_SET); subimage_search (); if (! set_spec_info ()) return false; spec = m_spec; return true; }; bool FitsInput::read_native_scanline (int y, int z, void *data) { // we return true just to support 0x0 images if (!m_naxes) return true; std::vector data_tmp (m_spec.scanline_bytes ()); long scanline_off = (m_spec.height - y) * m_spec.scanline_bytes (); fseek (m_fd, scanline_off, SEEK_CUR); size_t n = fread (&data_tmp[0], 1, m_spec.scanline_bytes(), m_fd); if (n != m_spec.scanline_bytes()) { if (feof (m_fd)) error ("Hit end of file unexpectedly"); else error ("read error"); return false; // Read failed } // in FITS image data is stored in big-endian so we have to switch to // little-endian on little-endian machines if (littleendian ()) { if (m_spec.format == TypeDesc::USHORT) swap_endian ((unsigned short*)&data_tmp[0], data_tmp.size () / sizeof (unsigned short)); else if (m_spec.format == TypeDesc::UINT) swap_endian ((unsigned int*)&data_tmp[0], data_tmp.size () / sizeof (unsigned int)); else if (m_spec.format == TypeDesc::FLOAT) swap_endian ((float*)&data_tmp[0], data_tmp.size () / sizeof (float)); else if (m_spec.format == TypeDesc::DOUBLE) swap_endian ((double*)&data_tmp[0], data_tmp.size () / sizeof (double)); } memcpy (data, &data_tmp[0], data_tmp.size ()); // after reading scanline we set file pointer to the start of image data fsetpos (m_fd, &m_filepos); return true; }; bool FitsInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec) { if (miplevel != 0) return false; if (subimage < 0 || subimage >= (int)m_subimages.size ()) return false; if (subimage == m_cur_subimage) { newspec = m_spec; return true; } // setting file pointer to the beginning of IMAGE extension m_cur_subimage = subimage; fseek (m_fd, m_subimages[m_cur_subimage].offset, SEEK_SET); if (! set_spec_info ()) return false; newspec = m_spec; return true; } bool FitsInput::set_spec_info () { keys.clear (); // FITS spec doesn't say anything about color space or // number of channels, so we read all images as if they // all were one-channel images m_spec = ImageSpec(0, 0, 1, TypeDesc::UNKNOWN); // reading info about current subimage if (! read_fits_header ()) return false; // we don't deal with one dimension images // it's some kind of spectral data if (! m_spec.width || ! m_spec.height) { m_spec.width = m_spec.full_width = 0; m_spec.height = m_spec.full_height = 0; } // now we can get the current position in the file // this is the start of the image data // we will need it in the read_native_scanline method fgetpos(m_fd, &m_filepos); if (m_bitpix == 8) m_spec.set_format (TypeDesc::UCHAR); else if (m_bitpix == 16) m_spec.set_format (TypeDesc::USHORT); else if (m_bitpix == 32) m_spec.set_format (TypeDesc::UINT); else if (m_bitpix == -32) m_spec.set_format (TypeDesc::FLOAT); else if (m_bitpix == -64) m_spec.set_format (TypeDesc::DOUBLE); return true; } bool FitsInput::close (void) { if (m_fd) fclose (m_fd); init (); return true; } bool FitsInput::read_fits_header (void) { std::string fits_header (HEADER_SIZE, 0); // we read whole header at once if (fread (&fits_header[0], 1, HEADER_SIZE, m_fd) != HEADER_SIZE) { if (feof (m_fd)) error ("Hit end of file unexpectedly"); else error ("read error"); return false; // Read failed } for (int i = 0; i < CARDS_PER_HEADER; ++i) { std::string card (CARD_SIZE, 0); // reading card number i memcpy (&card[0], &fits_header[i*CARD_SIZE], CARD_SIZE); std::string keyname, value; fits_pvt::unpack_card (card, keyname, value); // END means that this is end of the FITS header // we can now add to the ImageSpec COMMENT, HISTORY and HIERARCH keys if (keyname == "END") { // removing white spaces that we use to separate lines of comments // from the end ot the string m_comment = m_comment.substr (0, m_comment.size() - m_sep.size ()); m_history = m_history.substr (0, m_history.size() - m_sep.size ()); m_hierarch = m_hierarch.substr (0, m_hierarch.size() - m_sep.size ()); add_to_spec ("Comment", m_comment); add_to_spec ("History", m_history); add_to_spec ("Hierarch", m_hierarch); return true; } if (keyname == "SIMPLE" || keyname == "XTENSION") continue; // setting up some important fields // m_bitpix - format of the data (eg. bpp) // m_naxes - number of axes // width, height and depth of the image if (keyname == "BITPIX") { m_bitpix = atoi (&card[10]); continue; } if (keyname == "NAXIS") { m_naxes = atoi (&card[10]); if (m_naxes == 1) // 1 axis is w x 1 image m_spec.height = m_spec.full_height = 1; continue; } if (keyname == "NAXIS1") { m_spec.width = atoi (&card[10]); m_spec.full_width = m_spec.width; continue; } if (keyname == "NAXIS2") { m_spec.height = atoi (&card[10]); m_spec.full_height = m_spec.height; continue; } // ignoring other axis if (keyname.substr (0,5) == "NAXIS") { continue; } if (keyname == "ORIENTAT") { add_to_spec ("Orientation", value); continue; } if (keyname == "DATE") { add_to_spec ("DateTime", convert_date (value)); continue; } if (keyname == "COMMENT") { m_comment += (value + m_sep); continue; } if (keyname == "HISTORY") { m_history += (value + m_sep); continue; } if (keyname == "HIERARCH") { m_hierarch += (value + m_sep); continue; } Strutil::to_lower (keyname); // make lower case if (keyname.size() >= 1) keyname[0] = toupper (keyname[0]); add_to_spec (keyname, value); } // if we didn't found END keyword in current header, we read next one return read_fits_header (); } void FitsInput::add_to_spec (const std::string &keyname, const std::string &value) { // we don't add empty keys (or keys with empty values) to ImageSpec if (!keyname.size() || !value.size ()) return; // COMMENT, HISTORY, HIERARCH and DATE keywords we save AS-IS bool speckey = (keyname == "Comment" || keyname == "History" ||keyname == "Hierarch"); if (speckey || keyname == "DateTime") { m_spec.attribute (keyname, value); return; } // converting string to float or integer bool isNumSign = (value[0] == '+' || value[0] == '-' || value[0] == '.'); if (isdigit (value[0]) || isNumSign) { float val = atof (value.c_str ()); if (val == (int)val) m_spec.attribute (keyname, (int)val); else m_spec.attribute (keyname, val); } else m_spec.attribute (keyname, value); } void FitsInput::subimage_search () { // saving position of the file, just for safe) fpos_t fpos; fgetpos (m_fd, &fpos); // starting reading headers from the beginning of the file fseek (m_fd, 0, SEEK_SET); // we search for subimages by reading whole header and checking if it // starts by "SIMPLE" keyword (primary header is always image header) // or by "XTENSION= 'IMAGE '" (it is image extensions) std::string hdu (HEADER_SIZE, 0); size_t offset = 0; while (fread (&hdu[0], 1, HEADER_SIZE, m_fd) == HEADER_SIZE) { if (!strncmp (&hdu[0], "SIMPLE", 6) || !strncmp (&hdu[0], "XTENSION= 'IMAGE '", 20)) { fits_pvt::Subimage newSub; newSub.number = m_subimages.size (); newSub.offset = offset; m_subimages.push_back (newSub); } offset += HEADER_SIZE; } fsetpos (m_fd, &fpos); } std::string FitsInput::convert_date (const std::string &date) { std::string ndate; if (date[4] == '-') { // YYYY-MM-DDThh:mm:ss convention is used since 1 January 2000 ndate = Strutil::format ("%04u:%02u:%02u", atoi(&date[0]), atoi(&date[5]), atoi(&date[8])); if (date.size () >= 11 && date[10] == 'T') ndate += Strutil::format (" %02u:%02u:%02u", atoi (&date[11]), atoi (&date[14]), atoi (&date[17])); return ndate; } if (date[2] == '/') { // DD/MM/YY convention was used before 1 January 2000 ndate = Strutil::format ("19%02u:%02u:%02u 00:00:00", atoi(&date[6]), atoi(&date[3]), atoi(&date[0])); return ndate; } // unrecognized format return date; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/fits.imageio/fits_pvt.h0000644000175000017500000001612113151711064021502 0ustar mfvmfv/* Copyright 2008-2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_FITS_PVT_H #define OPENIMAGEIO_FITS_PVT_H #include #include #include #include "OpenImageIO/imageio.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" // This represent the size of ONE header unit in FITS file. #define HEADER_SIZE 2880 // This represent the size of ONE card unit. Card consist of // keyname, value and optional comment #define CARD_SIZE 80 // This represent the size of max number of cards in one header #define CARDS_PER_HEADER 36 OIIO_PLUGIN_NAMESPACE_BEGIN namespace fits_pvt { // struct in which we store information about one subimage. This informations // allow us to set up pointer at the beginning of given subimage struct Subimage { int number; size_t offset; }; } // namespace fits_pvt class FitsInput : public ImageInput { public: FitsInput () { init (); } virtual ~FitsInput () { close (); } virtual const char *format_name (void) const { return "fits"; } virtual int supports (string_view feature) const { return (feature == "arbitrary_metadata" || feature == "exif" // Because of arbitrary_metadata || feature == "iptc"); // Because of arbitrary_metadata } virtual bool valid_file (const std::string &filename) const; virtual bool open (const std::string &name, ImageSpec &spec); virtual bool close (void); virtual bool read_native_scanline (int y, int z, void *data); virtual bool seek_subimage (int subimage, int miplevel, ImageSpec &newspec); virtual int current_subimage () const { return m_cur_subimage; } private: FILE *m_fd; std::string m_filename; int m_cur_subimage; int m_bitpix; // number of bits that represents data value; int m_naxes; // number of axsis of the image (e.g dimensions) fpos_t m_filepos; // current position in the file // here we store informations how many times COMMENT, HISTORY, HIERARCH // keywords has occured std::map keys; // here we store informations about subimages, // eg. subimage number and subimage offset std::vector m_subimages; // here we stores content of COMMENT, HISTORY, HIERARCH keywords. Each line // of comment is separated by m_sep std::string m_comment, m_history, m_hierarch; std::string m_sep; void init (void) { m_fd = NULL; m_filename.clear (); m_cur_subimage = 0; m_bitpix = 0; m_naxes = 0; m_subimages.clear (); m_comment.clear (); m_history.clear (); m_hierarch.clear (); m_sep = '\n'; } // read keywords from FITS header and add them to the ImageSpec // sets some ImageSpec fields: width, height, depth. // Return true if all is ok, false if there was a read error. bool read_fits_header (void); // add keyword (with comment if exists) to the ImageSpec void add_to_spec (const std::string &keyname, const std::string &value); // search for subimages: in FITS subimage is a header with SIMPLE keyword // or with XTENSION keyword with value 'IMAGE '. Information about found // subimages are stored in m_subimages void subimage_search (); // set basic info (width, height) of subimage // add attributes to ImageSpec // return true if ok, false upon error reading the spec from the file. bool set_spec_info (); // converts date in FITS format (YYYY-MM-DD or DD/MM/YY) // to DateTime format std::string convert_date (const std::string &date); }; class FitsOutput : public ImageOutput { public: FitsOutput () { init (); } virtual ~FitsOutput () { close (); } virtual const char *format_name (void) const { return "fits"; } virtual int supports (string_view feature) const; virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode=Create); virtual bool close (void); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); private: FILE *m_fd; std::string m_filename; int m_bitpix; // number of bits that represents data value; fpos_t m_filepos; // current position in the file bool m_simple; // does the header with SIMPLE key was written? std::vector m_scratch; std::string m_sep; std::vector m_tilebuffer; void init (void) { m_fd = NULL; m_filename.clear (); m_bitpix = 0; m_simple = true; m_scratch.clear (); m_sep = '\n'; } // save to FITS file all attributes from ImageSpace and after writing last // attribute writes END keyword void create_fits_header (void); // save to FITS file some mandatory keywords: SIMPLE, BITPIX, NAXIS, NAXIS1 // and NAXIS2 with their values. void create_basic_header (std::string &header); }; namespace fits_pvt { // converts given number to string std::string num2str (float val); // creates FITS card from given (keyname, value, comment) strings std::string create_card (std::string keyname, std::string value); // retrieving keyname, value and comment from the given card void unpack_card (const std::string &card, std::string &keyname, std::string &value); } // namespace fits_pvt OIIO_PLUGIN_NAMESPACE_END #endif // OPENIMAGEIO_FITS_PVT_H openimageio-1.7.17~dfsg0.orig/src/fits.imageio/fitsoutput.cpp0000644000175000017500000002354413151711064022434 0ustar mfvmfv/* Copyright 2008-2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "oiioversion.h" #include "fits_pvt.h" OIIO_PLUGIN_NAMESPACE_BEGIN using namespace fits_pvt; // Obligatory material to make this a recognizeable imageio plugin OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *fits_output_imageio_create () { return new FitsOutput; } OIIO_EXPORT const char *fits_output_extensions[] = { "fits", NULL }; OIIO_PLUGIN_EXPORTS_END int FitsOutput::supports (string_view feature) const { return (feature == "multiimage" || feature == "alpha" || feature == "nchannels" || feature == "random_access" || feature == "arbitrary_metadata" || feature == "exif" // Because of arbitrary_metadata || feature == "iptc"); // Because of arbitrary_metadata } bool FitsOutput::open (const std::string &name, const ImageSpec &spec, OpenMode mode) { if (mode == AppendMIPLevel) { error ("%s does not support MIP levels", format_name()); return false; } // saving 'name' and 'spec' for later use m_filename = name; m_spec = spec; if (m_spec.format == TypeDesc::UNKNOWN) // if unknown, default to float m_spec.set_format (TypeDesc::FLOAT); // checking if the file exists and can be opened in WRITE mode m_fd = Filesystem::fopen (m_filename, mode == AppendSubimage ? "r+b" : "wb"); if (!m_fd) { error ("Unable to open file \"%s\"", m_filename.c_str()); return false; } create_fits_header (); // now we can get the current position in the file // we will need it int the write_native_scanline method fgetpos(m_fd, &m_filepos); // If user asked for tiles -- which this format doesn't support, emulate // it by buffering the whole image. if (m_spec.tile_width && m_spec.tile_height) m_tilebuffer.resize (m_spec.image_bytes()); return true; } bool FitsOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { if (m_spec.width == 0 && m_spec.height == 0) return true; if (y > m_spec.height) { error ("Attempt to write too many scanlines to %s", m_filename.c_str()); close (); return false; } data = to_native_scanline (format, data, xstride, m_scratch); std::vector data_tmp (m_spec.scanline_bytes (), 0); memcpy (&data_tmp[0], data, m_spec.scanline_bytes ()); // computing scanline offset long scanline_off = (m_spec.height - y) * m_spec.scanline_bytes (); fseek (m_fd, scanline_off, SEEK_CUR); // in FITS image data is stored in big-endian so we have to switch to // big-endian on little-endian machines if (littleendian ()) { if (m_bitpix == 16) swap_endian ((unsigned short*)&data_tmp[0], data_tmp.size () / sizeof (unsigned short)); else if (m_bitpix == 32) swap_endian ((unsigned int*)&data_tmp[0], data_tmp.size () / sizeof (unsigned int)); else if (m_bitpix == -32) swap_endian ((float*)&data_tmp[0], data_tmp.size () / sizeof (float)); else if (m_bitpix == -64) swap_endian ((double*)&data_tmp[0], data_tmp.size () / sizeof (double)); } size_t byte_count = fwrite (&data_tmp[0], 1, data_tmp.size (), m_fd); fsetpos (m_fd, &m_filepos); //byte_count == data.size --> all written return byte_count == data_tmp.size(); } bool FitsOutput::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { // Emulate tiles by buffering the whole image return copy_tile_to_image_buffer (x, y, z, format, data, xstride, ystride, zstride, &m_tilebuffer[0]); } bool FitsOutput::close (void) { if (! m_fd) { // already closed init (); return true; } bool ok = true; if (m_spec.tile_width) { // Handle tile emulation -- output the buffered pixels ASSERT (m_tilebuffer.size()); ok &= write_scanlines (m_spec.y, m_spec.y+m_spec.height, 0, m_spec.format, &m_tilebuffer[0]); std::vector().swap (m_tilebuffer); } fclose (m_fd); init (); return ok; } void FitsOutput::create_fits_header (void) { std::string header; create_basic_header (header); //we add all keywords stored in ImageSpec to the FITS file for (size_t i = 0; i < m_spec.extra_attribs.size (); ++i) { std::string keyname = m_spec.extra_attribs[i].name().string(); std::string value; TypeDesc attr_format = m_spec.extra_attribs[i].type(); if (attr_format == TypeDesc::STRING) { value = *(const char**)m_spec.extra_attribs[i].data(); } else if (attr_format == TypeDesc::INT) { int val = (*(int*)m_spec.extra_attribs[i].data()); value = num2str ((float)val); } else if (attr_format == TypeDesc::FLOAT) { float val = (*(float*)m_spec.extra_attribs[i].data()); value = num2str (val); } // Comment, History and Hierarch attributes contains multiple line of // COMMENT, HISTORY and HIERARCH keywords, so we have to split them before // adding to the file std::vector values; if (keyname == "Comment" || keyname == "History" || keyname == "Hierarch") { Strutil::split (value, values, m_sep); for (size_t i = 0; i < values.size(); ++i) header += create_card (keyname, values[i]); continue; } // FITS use Date keyword for dates so we convert our DateTime attribute // to Date format before adding it to the FITS file if (keyname == "DateTime") { keyname = "Date"; value = Strutil::format ("%04u-%02u-%02uT%02u:%02u:%02u", atoi(&value[0]), atoi(&value[5]), atoi(&value[8]), atoi(&value[11]), atoi(&value[14]), atoi(&value[17])); } header += create_card (keyname, value); } header += "END"; // header size must be multiple of HEADER_SIZE const int hsize = HEADER_SIZE - header.size () % HEADER_SIZE; if (hsize) header.resize (header.size () + hsize, ' '); size_t byte_count = fwrite (&header[0], 1, header.size (), m_fd); if (byte_count != header.size ()) { // FIXME Bad Write error ("Bad header write (err %d)", byte_count); } } void FitsOutput::create_basic_header (std::string &header) { // the first word in the header is SIMPLE, that informs if given // file is standard FITS file (T) or isn't (F) // we always set this value for T std::string key; if (m_simple) { header += create_card ("SIMPLE", "T"); m_simple = false; } else header += create_card ("XTENSION", "IMAGE "); // next, we add BITPIX value that represent how many bpp we need switch (m_spec.format.basetype) { case TypeDesc::CHAR: case TypeDesc::UCHAR: m_bitpix = 8; break; case TypeDesc::SHORT: case TypeDesc::USHORT: m_bitpix = 16; break; case TypeDesc::INT: case TypeDesc::UINT: m_bitpix = 32; break; case TypeDesc::HALF: case TypeDesc::FLOAT: m_bitpix = -32; break; case TypeDesc::DOUBLE: m_bitpix = -64; break; default: m_bitpix = -32; // punt: default to 32 bit float break; } header += create_card ("BITPIX", num2str (m_bitpix)); // NAXIS inform how many dimension have the image. // we deal only with 2D images so this value is always set to 2 int axes = 0; if (m_spec.width != 0 || m_spec.height != 0) axes = 2; header += create_card ("NAXIS", num2str (axes)); // now we save NAXIS1 and NAXIS2 // this keywords represents width and height header += create_card ("NAXIS1", num2str (m_spec.width)); header += create_card ("NAXIS2", num2str (m_spec.height)); } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/png.imageio/0000755000175000017500000000000013151711064017311 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/png.imageio/CMakeLists.txt0000644000175000017500000000040313151711064022046 0ustar mfvmfvif (PNG_FOUND) add_oiio_plugin (pnginput.cpp pngoutput.cpp INCLUDE_DIRS ${PNG_INCLUDE_DIR} LINK_LIBRARIES ${PNG_LIBRARIES}) else () message (WARNING "libpng not found, so PNG support will not work") endif () openimageio-1.7.17~dfsg0.orig/src/png.imageio/png_pvt.h0000644000175000017500000005043713151711064021150 0ustar mfvmfv/* Copyright 2009 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OPENIMAGEIO_PNG_PVT_H #define OPENIMAGEIO_PNG_PVT_H #include #include #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/sysutil.h" #define OIIO_LIBPNG_VERSION (PNG_LIBPNG_VER_MAJOR*10000 + PNG_LIBPNG_VER_MINOR*100 + PNG_LIBPNG_VER_RELEASE) /* This code has been extracted from the PNG plugin so as to provide access to PNG images embedded within any container format, without redundant code duplication. It's been done in the course of development of the ICO plugin in order to allow reading and writing Vista-style PNG icons. For further information see the following mailing list threads: http://lists.openimageio.org/pipermail/oiio-dev-openimageio.org/2009-April/000586.html http://lists.openimageio.org/pipermail/oiio-dev-openimageio.org/2009-April/000656.html */ OIIO_PLUGIN_NAMESPACE_BEGIN #define ICC_PROFILE_ATTR "ICCProfile" namespace PNG_pvt { /// Initializes a PNG read struct. /// \return empty string on success, error message on failure. /// inline const std::string create_read_struct (png_structp& sp, png_infop& ip) { sp = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (! sp) return "Could not create PNG read structure"; ip = png_create_info_struct (sp); if (! ip) return "Could not create PNG info structure"; // Must call this setjmp in every function that does PNG reads if (setjmp (png_jmpbuf(sp))) return "PNG library error"; // success return ""; } /// Helper function - reads background colour. /// inline bool get_background (png_structp& sp, png_infop& ip, ImageSpec& spec, int& bit_depth, float *red, float *green, float *blue) { if (setjmp (png_jmpbuf (sp))) return false; if (! png_get_valid (sp, ip, PNG_INFO_bKGD)) return false; png_color_16p bg; png_get_bKGD (sp, ip, &bg); if (spec.format == TypeDesc::UINT16) { *red = bg->red / 65535.0; *green = bg->green / 65535.0; *blue = bg->blue / 65535.0; } else if (spec.nchannels < 3 && bit_depth < 8) { if (bit_depth == 1) *red = *green = *blue = (bg->gray ? 1 : 0); else if (bit_depth == 2) *red = *green = *blue = bg->gray / 3.0; else // 4 bits *red = *green = *blue = bg->gray / 15.0; } else { *red = bg->red / 255.0; *green = bg->green / 255.0; *blue = bg->blue / 255.0; } return true; } /// Read information from a PNG file and fill the ImageSpec accordingly. /// inline void read_info (png_structp& sp, png_infop& ip, int& bit_depth, int& color_type, int& interlace_type, Imath::Color3f& bg, ImageSpec& spec, bool keep_unassociated_alpha) { png_read_info (sp, ip); // Auto-convert 1-, 2-, and 4- bit images to 8 bits, palette to RGB, // and transparency to alpha. png_set_expand (sp); // PNG files are naturally big-endian if (littleendian()) png_set_swap (sp); png_read_update_info (sp, ip); png_uint_32 width, height; png_get_IHDR (sp, ip, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); spec = ImageSpec ((int)width, (int)height, png_get_channels (sp, ip), bit_depth == 16 ? TypeDesc::UINT16 : TypeDesc::UINT8); spec.default_channel_names (); if (spec.nchannels == 2) { // Special case: PNG spec says 2-channel image is Gray & Alpha spec.channelnames[0] = "Y"; spec.channelnames[1] = "A"; spec.alpha_channel = 1; } int srgb_intent; if (png_get_sRGB (sp, ip, &srgb_intent)) { spec.attribute ("oiio:ColorSpace", "sRGB"); } else { double gamma; if (png_get_gAMA (sp, ip, &gamma)) { spec.attribute ("oiio:Gamma", (float)(1.0f/gamma)); spec.attribute ("oiio:ColorSpace", (gamma == 1) ? "Linear" : "GammaCorrected"); } } if (png_get_valid(sp, ip, PNG_INFO_iCCP)) { png_charp profile_name = NULL; #if OIIO_LIBPNG_VERSION > 10500 /* PNG function signatures changed */ png_bytep profile_data = NULL; #else png_charp profile_data = NULL; #endif png_uint_32 profile_length = 0; int compression_type; png_get_iCCP (sp, ip, &profile_name, &compression_type, &profile_data, &profile_length); if (profile_length && profile_data) spec.attribute (ICC_PROFILE_ATTR, TypeDesc(TypeDesc::UINT8, profile_length), profile_data); } png_timep mod_time; if (png_get_tIME (sp, ip, &mod_time)) { std::string date = Strutil::format ("%4d:%02d:%02d %2d:%02d:%02d", mod_time->year, mod_time->month, mod_time->day, mod_time->hour, mod_time->minute, mod_time->second); spec.attribute ("DateTime", date); } png_textp text_ptr; int num_comments = png_get_text (sp, ip, &text_ptr, NULL); if (num_comments) { std::string comments; for (int i = 0; i < num_comments; ++i) { if (Strutil::iequals (text_ptr[i].key, "Description")) spec.attribute ("ImageDescription", text_ptr[i].text); else if (Strutil::iequals (text_ptr[i].key, "Author")) spec.attribute ("Artist", text_ptr[i].text); else if (Strutil::iequals (text_ptr[i].key, "Title")) spec.attribute ("DocumentName", text_ptr[i].text); else spec.attribute (text_ptr[i].key, text_ptr[i].text); } } spec.x = png_get_x_offset_pixels (sp, ip); spec.y = png_get_y_offset_pixels (sp, ip); int unit; png_uint_32 resx, resy; if (png_get_pHYs (sp, ip, &resx, &resy, &unit)) { float scale = 1; if (unit == PNG_RESOLUTION_METER) { // Convert to inches, to match most other formats scale = 2.54 / 100.0; spec.attribute ("ResolutionUnit", "inch"); } else spec.attribute ("ResolutionUnit", "none"); spec.attribute ("XResolution", (float)resx*scale); spec.attribute ("YResolution", (float)resy*scale); } float aspect = (float)png_get_pixel_aspect_ratio (sp, ip); if (aspect != 0 && aspect != 1) spec.attribute ("PixelAspectRatio", aspect); float r, g, b; if (get_background (sp, ip, spec, bit_depth, &r, &g, &b)) { bg = Imath::Color3f (r, g, b); // FIXME -- should we do anything with the background color? } interlace_type = png_get_interlace_type (sp, ip); // PNG files are always "unassociated alpha" but we convert to associated // unless requested otherwise if (keep_unassociated_alpha) spec.attribute ("oiio:UnassociatedAlpha", (int)1); // FIXME -- look for an XMP packet in an iTXt chunk. } /// Reads from an open PNG file into the indicated buffer. /// \return empty string on success, error message on failure. /// inline const std::string read_into_buffer (png_structp& sp, png_infop& ip, ImageSpec& spec, int& bit_depth, int& color_type, std::vector& buffer) { // Must call this setjmp in every function that does PNG reads if (setjmp (png_jmpbuf (sp))) return "PNG library error"; #if 0 // ?? This doesn't seem necessary, but I don't know why // Make the library handle fewer significant bits // png_color_8p sig_bit; // if (png_get_sBIT (sp, ip, &sig_bit)) { // png_set_shift (sp, sig_bit); // } #endif DASSERT (spec.scanline_bytes() == png_get_rowbytes(sp,ip)); buffer.resize (spec.image_bytes()); std::vector row_pointers (spec.height); for (int i = 0; i < spec.height; ++i) row_pointers[i] = &buffer[0] + i * spec.scanline_bytes(); png_read_image (sp, &row_pointers[0]); png_read_end (sp, NULL); // success return ""; } /// Reads the next scanline from an open PNG file into the indicated buffer. /// \return empty string on success, error message on failure. /// inline const std::string read_next_scanline (png_structp& sp, void *buffer) { // Must call this setjmp in every function that does PNG reads if (setjmp (png_jmpbuf (sp))) return "PNG library error"; png_read_row (sp, (png_bytep)buffer, NULL); // success return ""; } /// Destroys a PNG read struct. /// inline void destroy_read_struct (png_structp& sp, png_infop& ip) { if (sp && ip) { png_destroy_read_struct (&sp, &ip, NULL); sp = NULL; ip = NULL; } } /// Initializes a PNG write struct. /// \return empty string on success, C-string error message on failure. /// inline const std::string create_write_struct (png_structp& sp, png_infop& ip, int& color_type, ImageSpec& spec) { // Check for things this format doesn't support if (spec.width < 1 || spec.height < 1) return Strutil::format ("Image resolution must be at least 1x1, " "you asked for %d x %d", spec.width, spec.height); if (spec.depth < 1) spec.depth = 1; if (spec.depth > 1) return "PNG does not support volume images (depth > 1)"; switch (spec.nchannels) { case 1 : color_type = PNG_COLOR_TYPE_GRAY; spec.alpha_channel = -1; break; case 2 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA; spec.alpha_channel = 1; break; case 3 : color_type = PNG_COLOR_TYPE_RGB; spec.alpha_channel = -1; break; case 4 : color_type = PNG_COLOR_TYPE_RGB_ALPHA; spec.alpha_channel = 3; break; default: return Strutil::format ("PNG only supports 1-4 channels, not %d", spec.nchannels); } // N.B. PNG is very rigid about the meaning of the channels, so enforce // which channel is alpha, that's the only way PNG can do it. sp = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (! sp) return "Could not create PNG write structure"; ip = png_create_info_struct (sp); if (! ip) return "Could not create PNG info structure"; // Must call this setjmp in every function that does PNG writes if (setjmp (png_jmpbuf(sp))) return "PNG library error"; // success return ""; } /// Helper function - writes a single parameter. /// inline bool put_parameter (png_structp& sp, png_infop& ip, const std::string &_name, TypeDesc type, const void *data, std::vector& text) { std::string name = _name; // Things to skip if (Strutil::iequals(name, "planarconfig")) // No choice for PNG files return false; if (Strutil::iequals(name, "compression")) return false; if (Strutil::iequals(name, "ResolutionUnit") || Strutil::iequals(name, "XResolution") || Strutil::iequals(name, "YResolution")) return false; // Remap some names to PNG conventions if (Strutil::iequals(name, "Artist") && type == TypeDesc::STRING) name = "Author"; if ((Strutil::iequals(name, "name") || Strutil::iequals(name, "DocumentName")) && type == TypeDesc::STRING) name = "Title"; if ((Strutil::iequals(name, "description") || Strutil::iequals(name, "ImageDescription")) && type == TypeDesc::STRING) name = "Description"; if (Strutil::iequals(name, "DateTime") && type == TypeDesc::STRING) { png_time mod_time; int year, month, day, hour, minute, second; if (sscanf (*(const char **)data, "%4d:%02d:%02d %2d:%02d:%02d", &year, &month, &day, &hour, &minute, &second) == 6) { mod_time.year = year; mod_time.month = month; mod_time.day = day; mod_time.hour = hour; mod_time.minute = minute; mod_time.second = second; png_set_tIME (sp, ip, &mod_time); return true; } else { return false; } } #if 0 if (Strutil::iequals(name, "ResolutionUnit") && type == TypeDesc::STRING) { const char *s = *(char**)data; bool ok = true; if (Strutil::iequals (s, "none")) PNGSetField (m_tif, PNGTAG_RESOLUTIONUNIT, RESUNIT_NONE); else if (Strutil::iequals (s, "in") || Strutil::iequals (s, "inch")) PNGSetField (m_tif, PNGTAG_RESOLUTIONUNIT, RESUNIT_INCH); else if (Strutil::iequals (s, "cm")) PNGSetField (m_tif, PNGTAG_RESOLUTIONUNIT, RESUNIT_CENTIMETER); else ok = false; return ok; } if (Strutil::iequals(name, "ResolutionUnit") && type == TypeDesc::UINT) { PNGSetField (m_tif, PNGTAG_RESOLUTIONUNIT, *(unsigned int *)data); return true; } if (Strutil::iequals(name, "XResolution") && type == TypeDesc::FLOAT) { PNGSetField (m_tif, PNGTAG_XRESOLUTION, *(float *)data); return true; } if (Strutil::iequals(name, "YResolution") && type == TypeDesc::FLOAT) { PNGSetField (m_tif, PNGTAG_YRESOLUTION, *(float *)data); return true; } #endif if (type == TypeDesc::STRING) { png_text t; t.compression = PNG_TEXT_COMPRESSION_NONE; t.key = (char *)ustring(name).c_str(); t.text = *(char **)data; // Already uniquified text.push_back (t); } return false; } /// Writes PNG header according to the ImageSpec. /// inline void write_info (png_structp& sp, png_infop& ip, int& color_type, ImageSpec& spec, std::vector& text, bool& convert_alpha, float& gamma) { // Force either 16 or 8 bit integers if (spec.format == TypeDesc::UINT8 || spec.format == TypeDesc::INT8) spec.set_format (TypeDesc::UINT8); else spec.set_format (TypeDesc::UINT16); // best precision available png_set_IHDR (sp, ip, spec.width, spec.height, spec.format.size()*8, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_set_oFFs (sp, ip, spec.x, spec.y, PNG_OFFSET_PIXEL); // PNG specifically dictates unassociated (un-"premultiplied") alpha convert_alpha = spec.alpha_channel != -1 && !spec.get_int_attribute("oiio:UnassociatedAlpha", 0); gamma = spec.get_float_attribute ("oiio:Gamma", 1.0); std::string colorspace = spec.get_string_attribute ("oiio:ColorSpace"); if (Strutil::iequals (colorspace, "Linear")) { png_set_gAMA (sp, ip, 1.0); } else if (Strutil::iequals (colorspace, "GammaCorrected")) { png_set_gAMA (sp, ip, 1.0f/gamma); } else if (Strutil::iequals (colorspace, "sRGB")) { png_set_sRGB_gAMA_and_cHRM (sp, ip, PNG_sRGB_INTENT_ABSOLUTE); } // Write ICC profile, if we have anything const ImageIOParameter* icc_profile_parameter = spec.find_attribute(ICC_PROFILE_ATTR); if (icc_profile_parameter != NULL) { unsigned int length = icc_profile_parameter->type().size(); #if OIIO_LIBPNG_VERSION > 10500 /* PNG function signatures changed */ unsigned char *icc_profile = (unsigned char*)icc_profile_parameter->data(); if (icc_profile && length) png_set_iCCP (sp, ip, "Embedded Profile", 0, icc_profile, length); #else char *icc_profile = (char*)icc_profile_parameter->data(); if (icc_profile && length) png_set_iCCP (sp, ip, (png_charp)"Embedded Profile", 0, icc_profile, length); #endif } if (false && ! spec.find_attribute("DateTime")) { time_t now; time (&now); struct tm mytm; Sysutil::get_local_time (&now, &mytm); std::string date = Strutil::format ("%4d:%02d:%02d %2d:%02d:%02d", mytm.tm_year+1900, mytm.tm_mon+1, mytm.tm_mday, mytm.tm_hour, mytm.tm_min, mytm.tm_sec); spec.attribute ("DateTime", date); } string_view unitname = spec.get_string_attribute ("ResolutionUnit"); float xres = spec.get_float_attribute ("XResolution"); float yres = spec.get_float_attribute ("YResolution"); float paspect = spec.get_float_attribute ("PixelAspectRatio"); if (xres || yres || paspect || unitname.size()) { int unittype = PNG_RESOLUTION_UNKNOWN; float scale = 1; if (Strutil::iequals (unitname, "meter") || Strutil::iequals (unitname, "m")) unittype = PNG_RESOLUTION_METER; else if (Strutil::iequals (unitname, "cm")) { unittype = PNG_RESOLUTION_METER; scale = 100; } else if (Strutil::iequals (unitname, "inch") || Strutil::iequals (unitname, "in")) { unittype = PNG_RESOLUTION_METER; scale = 100.0/2.54; } if (paspect) { // If pixel aspect is given, allow resolution to be reset if (xres) yres = 0.0f; else xres = 0.0f; } if (xres == 0.0f && yres == 0.0f) { xres = 100.0f; yres = xres * (paspect ? paspect : 1.0f); } else if (xres == 0.0f) { xres = yres / (paspect ? paspect : 1.0f); } else if (yres == 0.0f) { yres = xres * (paspect ? paspect : 1.0f); } png_set_pHYs (sp, ip, (png_uint_32)(xres*scale), (png_uint_32)(yres*scale), unittype); } // Deal with all other params for (size_t p = 0; p < spec.extra_attribs.size(); ++p) put_parameter (sp, ip, spec.extra_attribs[p].name().string(), spec.extra_attribs[p].type(), spec.extra_attribs[p].data(), text); if (text.size()) png_set_text (sp, ip, &text[0], text.size()); png_write_info (sp, ip); png_set_packing (sp); // Pack 1, 2, 4 bit into bytes } /// Writes a scanline. /// inline bool write_row (png_structp& sp, png_byte *data) { if (setjmp (png_jmpbuf(sp))) { //error ("PNG library error"); return false; } png_write_row (sp, data); return true; } /// Helper function - finalizes writing the image. /// inline void finish_image (png_structp& sp) { // Must call this setjmp in every function that does PNG writes if (setjmp (png_jmpbuf(sp))) { //error ("PNG library error"); return; } png_write_end (sp, NULL); } /// Destroys a PNG write struct. /// inline void destroy_write_struct (png_structp& sp, png_infop& ip) { if (sp && ip) { finish_image (sp); png_destroy_write_struct (&sp, &ip); sp = NULL; ip = NULL; } } } // namespace PNG_pvt OIIO_PLUGIN_NAMESPACE_END #endif // OPENIMAGEIO_PNG_PVT_H openimageio-1.7.17~dfsg0.orig/src/png.imageio/pnginput.cpp0000644000175000017500000002331213151711064021662 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include "png_pvt.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/fmath.h" OIIO_PLUGIN_NAMESPACE_BEGIN class PNGInput : public ImageInput { public: PNGInput () { init(); } virtual ~PNGInput () { close(); } virtual const char * format_name (void) const { return "png"; } virtual bool valid_file (const std::string &filename) const; virtual bool open (const std::string &name, ImageSpec &newspec); virtual bool open (const std::string &name, ImageSpec &newspec, const ImageSpec &config); virtual bool close (); virtual int current_subimage (void) const { return m_subimage; } virtual bool read_native_scanline (int y, int z, void *data); private: std::string m_filename; ///< Stash the filename FILE *m_file; ///< Open image handle png_structp m_png; ///< PNG read structure pointer png_infop m_info; ///< PNG image info structure pointer int m_bit_depth; ///< PNG bit depth int m_color_type; ///< PNG color model type int m_interlace_type; ///< PNG interlace type std::vector m_buf; ///< Buffer the image pixels int m_subimage; ///< What subimage are we looking at? Imath::Color3f m_bg; ///< Background color int m_next_scanline; bool m_keep_unassociated_alpha; ///< Do not convert unassociated alpha /// Reset everything to initial state /// void init () { m_subimage = -1; m_file = NULL; m_png = NULL; m_info = NULL; m_buf.clear (); m_next_scanline = 0; m_keep_unassociated_alpha = false; } /// Helper function: read the image. /// bool readimg (); /// Extract the background color. /// bool get_background (float *red, float *green, float *blue); }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageInput *png_input_imageio_create () { return new PNGInput; } OIIO_EXPORT int png_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* png_imageio_library_version () { return "libpng " PNG_LIBPNG_VER_STRING; } OIIO_EXPORT const char * png_input_extensions[] = { "png", NULL }; OIIO_PLUGIN_EXPORTS_END bool PNGInput::valid_file (const std::string &filename) const { FILE *fd = Filesystem::fopen (filename, "rb"); if (! fd) return false; unsigned char sig[8]; bool ok = (fread (sig, 1, sizeof(sig), fd) == sizeof(sig) && png_sig_cmp (sig, 0, 7) == 0); fclose (fd); return ok; } bool PNGInput::open (const std::string &name, ImageSpec &newspec) { m_filename = name; m_subimage = 0; m_file = Filesystem::fopen (name, "rb"); if (! m_file) { error ("Could not open file \"%s\"", name.c_str()); return false; } unsigned char sig[8]; if (fread (sig, 1, sizeof(sig), m_file) != sizeof(sig)) { error ("Not a PNG file"); return false; // Read failed } if (png_sig_cmp (sig, 0, 7)) { error ("File failed PNG signature check"); return false; } std::string s = PNG_pvt::create_read_struct (m_png, m_info); if (s.length ()) { close (); error ("%s", s.c_str ()); return false; } png_init_io (m_png, m_file); png_set_sig_bytes (m_png, 8); // already read 8 bytes PNG_pvt::read_info (m_png, m_info, m_bit_depth, m_color_type, m_interlace_type, m_bg, m_spec, m_keep_unassociated_alpha); newspec = spec (); m_next_scanline = 0; return true; } bool PNGInput::open (const std::string &name, ImageSpec &newspec, const ImageSpec &config) { // Check 'config' for any special requests if (config.get_int_attribute("oiio:UnassociatedAlpha", 0) == 1) m_keep_unassociated_alpha = true; return open (name, newspec); } bool PNGInput::readimg () { std::string s = PNG_pvt::read_into_buffer (m_png, m_info, m_spec, m_bit_depth, m_color_type, m_buf); if (s.length ()) { close (); error ("%s", s.c_str ()); return false; } return true; } bool PNGInput::close () { PNG_pvt::destroy_read_struct (m_png, m_info); if (m_file) { fclose (m_file); m_file = NULL; } init(); // Reset to initial state return true; } template static void associateAlpha (T * data, int size, int channels, int alpha_channel, float gamma) { T max = std::numeric_limits::max(); if (gamma == 1) { for (int x = 0; x < size; ++x, data += channels) for (int c = 0; c < channels; c++) if (c != alpha_channel){ unsigned int f = data[c]; data[c] = (f * data[alpha_channel]) / max; } } else { //With gamma correction float inv_max = 1.0 / max; for (int x = 0; x < size; ++x, data += channels) { float alpha_associate = pow(data[alpha_channel]*inv_max, gamma); // We need to transform to linear space, associate the alpha, and // then transform back. That is, if D = data[c], we want // // D' = max * ( (D/max)^(1/gamma) * (alpha/max) ) ^ gamma // // This happens to simplify to something which looks like // multiplying by a nonlinear alpha: // // D' = D * (alpha/max)^gamma for (int c = 0; c < channels; c++) if (c != alpha_channel) data[c] = static_cast(data[c] * alpha_associate); } } } bool PNGInput::read_native_scanline (int y, int z, void *data) { y -= m_spec.y; if (y < 0 || y >= m_spec.height) // out of range scanline return false; if (m_interlace_type != 0) { // Interlaced. Punt and read the whole image if (m_buf.empty ()) readimg (); size_t size = spec().scanline_bytes(); memcpy (data, &m_buf[0] + y * size, size); } else { // Not an interlaced image -- read just one row if (m_next_scanline > y) { // User is trying to read an earlier scanline than the one we're // up to. Easy fix: close the file and re-open. ImageSpec dummyspec; int subimage = current_subimage(); if (! close () || ! open (m_filename, dummyspec) || ! seek_subimage (subimage, dummyspec)) return false; // Somehow, the re-open failed assert (m_next_scanline == 0 && current_subimage() == subimage); } while (m_next_scanline <= y) { // Keep reading until we're read the scanline we really need // std::cerr << "reading scanline " << m_next_scanline << "\n"; std::string s = PNG_pvt::read_next_scanline (m_png, data); if (s.length ()) { close (); error ("%s", s.c_str ()); return false; } ++m_next_scanline; } } // PNG specifically dictates unassociated (un-"premultiplied") alpha. // Convert to associated unless we were requested not to do so. if (m_spec.alpha_channel != -1 && !m_keep_unassociated_alpha) { float gamma = m_spec.get_float_attribute ("oiio:Gamma", 1.0f); if (m_spec.format == TypeDesc::UINT16) associateAlpha ((unsigned short *)data, m_spec.width, m_spec.nchannels, m_spec.alpha_channel, gamma); else associateAlpha ((unsigned char *)data, m_spec.width, m_spec.nchannels, m_spec.alpha_channel, gamma); } return true; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/png.imageio/pngoutput.cpp0000644000175000017500000002415413151711064022070 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include "png_pvt.h" #include "OpenImageIO/dassert.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/strutil.h" OIIO_PLUGIN_NAMESPACE_BEGIN class PNGOutput : public ImageOutput { public: PNGOutput (); virtual ~PNGOutput (); virtual const char * format_name (void) const { return "png"; } virtual int supports (string_view feature) const { return (feature == "alpha"); } virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode=Create); virtual bool close (); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); private: std::string m_filename; ///< Stash the filename FILE *m_file; ///< Open image handle png_structp m_png; ///< PNG read structure pointer png_infop m_info; ///< PNG image info structure pointer unsigned int m_dither; int m_color_type; ///< PNG color model type bool m_convert_alpha; ///< Do we deassociate alpha? float m_gamma; ///< Gamma to use for alpha conversion std::vector m_scratch; std::vector m_pngtext; std::vector m_tilebuffer; // Initialize private members to pre-opened state void init (void) { m_file = NULL; m_png = NULL; m_info = NULL; m_convert_alpha = true; m_gamma = 1.0; m_pngtext.clear (); } // Add a parameter to the output bool put_parameter (const std::string &name, TypeDesc type, const void *data); void finish_image (); }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *png_output_imageio_create () { return new PNGOutput; } // OIIO_EXPORT int png_imageio_version = OIIO_PLUGIN_VERSION; // it's in pnginput.cpp OIIO_EXPORT const char * png_output_extensions[] = { "png", NULL }; OIIO_PLUGIN_EXPORTS_END PNGOutput::PNGOutput () { init (); } PNGOutput::~PNGOutput () { // Close, if not already done. close (); } bool PNGOutput::open (const std::string &name, const ImageSpec &userspec, OpenMode mode) { if (mode != Create) { error ("%s does not support subimages or MIP levels", format_name()); return false; } close (); // Close any already-opened file m_spec = userspec; // Stash the spec // If not uint8 or uint16, default to uint8 if (m_spec.format != TypeDesc::UINT8 && m_spec.format != TypeDesc::UINT16) m_spec.set_format (TypeDesc::UINT8); m_file = Filesystem::fopen (name, "wb"); if (! m_file) { error ("Could not open file \"%s\"", name.c_str()); return false; } std::string s = PNG_pvt::create_write_struct (m_png, m_info, m_color_type, m_spec); if (s.length ()) { close (); error ("%s", s.c_str ()); return false; } png_init_io (m_png, m_file); png_set_compression_level (m_png, std::max (std::min (m_spec.get_int_attribute ("png:compressionLevel", 6/* medium speed vs size tradeoff */), Z_BEST_COMPRESSION), Z_NO_COMPRESSION)); std::string compression = m_spec.get_string_attribute ("compression"); if (compression.empty ()) { png_set_compression_strategy(m_png, Z_DEFAULT_STRATEGY); } else if (Strutil::iequals (compression, "default")) { png_set_compression_strategy(m_png, Z_DEFAULT_STRATEGY); } else if (Strutil::iequals (compression, "filtered")) { png_set_compression_strategy(m_png, Z_FILTERED); } else if (Strutil::iequals (compression, "huffman")) { png_set_compression_strategy(m_png, Z_HUFFMAN_ONLY); } else if (Strutil::iequals (compression, "rle")) { png_set_compression_strategy(m_png, Z_RLE); } else if (Strutil::iequals (compression, "fixed")) { png_set_compression_strategy(m_png, Z_FIXED); } else { png_set_compression_strategy(m_png, Z_DEFAULT_STRATEGY); } PNG_pvt::write_info (m_png, m_info, m_color_type, m_spec, m_pngtext, m_convert_alpha, m_gamma); m_dither = (m_spec.format == TypeDesc::UINT8) ? m_spec.get_int_attribute ("oiio:dither", 0) : 0; m_convert_alpha = m_spec.alpha_channel != -1 && !m_spec.get_int_attribute("oiio:UnassociatedAlpha", 0); // If user asked for tiles -- which this format doesn't support, emulate // it by buffering the whole image. if (m_spec.tile_width && m_spec.tile_height) m_tilebuffer.resize (m_spec.image_bytes()); return true; } bool PNGOutput::close () { if (! m_file) { // already closed init (); return true; } bool ok = true; if (m_spec.tile_width) { // Handle tile emulation -- output the buffered pixels ASSERT (m_tilebuffer.size()); ok &= write_scanlines (m_spec.y, m_spec.y+m_spec.height, 0, m_spec.format, &m_tilebuffer[0]); std::vector().swap (m_tilebuffer); } if (m_png) PNG_pvt::finish_image (m_png); PNG_pvt::destroy_write_struct (m_png, m_info); fclose (m_file); m_file = NULL; init (); // re-initialize return ok; } template static void deassociateAlpha (T * data, int size, int channels, int alpha_channel, float gamma) { unsigned int max = std::numeric_limits::max(); if (gamma == 1){ for (int x = 0; x < size; ++x, data += channels) if (data[alpha_channel]) for (int c = 0; c < channels; c++) if (c != alpha_channel) { unsigned int f = data[c]; f = (f * max) / data[alpha_channel]; data[c] = (T) std::min (max, f); } } else { for (int x = 0; x < size; ++x, data += channels) if (data[alpha_channel]) { // See associateAlpha() for an explanation. float alpha_deassociate = pow((float)max / data[alpha_channel], gamma); for (int c = 0; c < channels; c++) if (c != alpha_channel) data[c] = static_cast (std::min (max, (unsigned int)(data[c] * alpha_deassociate))); } } } bool PNGOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { y -= m_spec.y; m_spec.auto_stride (xstride, format, spec().nchannels); const void *origdata = data; data = to_native_scanline (format, data, xstride, m_scratch, m_dither, y, z); if (data == origdata) { m_scratch.assign ((unsigned char *)data, (unsigned char *)data+m_spec.scanline_bytes()); data = &m_scratch[0]; } // PNG specifically dictates unassociated (un-"premultiplied") alpha if (m_convert_alpha) { if (m_spec.format == TypeDesc::UINT16) deassociateAlpha ((unsigned short *)data, m_spec.width, m_spec.nchannels, m_spec.alpha_channel, m_gamma); else deassociateAlpha ((unsigned char *)data, m_spec.width, m_spec.nchannels, m_spec.alpha_channel, m_gamma); } // PNG is always big endian if (littleendian() && m_spec.format == TypeDesc::UINT16) swap_endian ((unsigned short *)data, m_spec.width*m_spec.nchannels); if (!PNG_pvt::write_row (m_png, (png_byte *)data)) { error ("PNG library error"); return false; } return true; } bool PNGOutput::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { // Emulate tiles by buffering the whole image return copy_tile_to_image_buffer (x, y, z, format, data, xstride, ystride, zstride, &m_tilebuffer[0]); } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/fonts/0000755000175000017500000000000013151711064016245 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/fonts/CMakeLists.txt0000644000175000017500000000026413151711064021007 0ustar mfvmfvfile (GLOB public_fonts "*/*.ttf") if (INSTALL_FONTS AND USE_FREETYPE) install (FILES ${public_fonts} DESTINATION ${FONTS_INSTALL_DIR} COMPONENT fonts) endif () openimageio-1.7.17~dfsg0.orig/src/fonts/Droid_Serif/0000755000175000017500000000000013151711064020436 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/fonts/Droid_Serif/DroidSerif-Bold.ttf0000644000175000017500000013736013151711064024077 0ustar mfvmfv FFTMQ5GDEF GPOS9; GSUBlt OS/2ﵻ`cmapۯTcvt .L.fpgms#gasp glyfvDLhead ,6hhead$hmtx=!LlocaYQtmaxp$ nameY post;prepxe j_<P\]sswj/Z_33f@ [(1ASC D'|s J y9yd?Fh`R3u3BHyZ\{3oNyFyVyHy9yyRy\yZy=yHoZ\yyyfL^h`9Xq#9999'q9599;99N9Lq9Lq9=9!TVPNPLy^L\1#7`1^`B'{V####)V)`)/^/)==+V5+;%=y=}qyuy1yPyZq\;oy{3q3by3L3PJRRof3`Ho  PfqXq9999999950595059#9N9LqLqLqLqLqyLq!!!!9 \\\\\\\7`````#`V)`````y`V5V5V5V5)#^=s=\Z\s\\3s3!x ~1    " : D 1    " 9 D  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ardeixpkvjsgwl|cnm}bɹyqz@EYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"! ,E#F` &`&#HH-,E#F#a &a&#HH-,E#F` a F`&#HH-,E#F#a ` &a a&#HH-,E#F`@a f`&#HH-,E#F#a@` &a@a&#HH-, <<-, E# D# ZQX# D#Y QX# MD#Y &QX# D#Y!!-, EhD ` EFvhE`D-, C#Ce -, C#C -,(#p(>(#p(E: -, E%EadPQXED!!Y-,I#D-, EC`D-,CCe -, i@a ,b`+ d#da\XaY-,E+)#D)z-,Ee,#DE+#D-,KRXED!!Y-,KQXED!!Y-,%# `#-,%# a#-,%-,F#F`F# F`ab# # pE` PXaFY`h:-, E%FRKQ[X%F ha%%?#!8!Y-, E%FPX%F ha%%?#!8!Y-,CC -,!! d#d@b-,!QX d#d b@/+Y`-,!QX d#dUb/+Y`-, d#d@b`#!-,KSX%Id#Ei@ab aj#D#!# 9/Y-,KSX %Idi &%Id#ab aj#D&#D#D& 9# 9//Y-,E#E`#E`#E`#vhb -,H+-, ETX@D E@aD!!Y-,E0/E#Ea``iD-,KQX/#p#B!!Y-,KQX %EiSXD!!Y!!Y-,EC`c`iD-,/ED-,E# E`D-,E#E`D-,K#QX34 34YDD-,CX&EXdf`d `f X!@YaY#XeY)#D#)!!!!!Y-,CTXKS#KQZX8!!Y!!!!Y-,CX%Ed `f X!@Ya#XeY)#D%% XY%% F%#B<%%%% F%`#B< XY%%)) EeD%%)%% XY%%CH%%%%`CH!Y!!!!!!!-,% F%#B%%EH!!!!-,% %%CH!!!-,E# E P X#e#Y#h @PX!@Y#XeY`D-,KS#KQZX E`D!!Y-,KTX E`D!!Y-,KS#KQZX8!!Y-,!KTX8!!Y-,CTXF+!!!!Y-,CTXG+!!!Y-,CTXH+!!!!Y-,CTXI+!!!Y-, #KSKQZX#8!!Y-,%ISX @8!Y-,F#F`#Fa#  Fab@@pE`h:-, #Id#SX<!Y-,KRX}zY-,KKTB-,B#Q@SZX TXC`BY$QX @TXC`B$TX C`BKKRXC`BY@TXC`BY@cTXC`BY@cTXC`BY@cTX@C`BYYYYY-,Eh#KQX# E d@PX|Yh`YD-,%%#>#> #eB #B#?#? #eB#B-,zE#-@'/_`3U3UU`@ F@@`߶`/oϛoϖƍYi ƂUggg0f@fPff?eed@dPddd3UU3UWW`VpVVHUTT`TpTT3UU3UUGU?oTS++KRKP[%S@QZUZ[XYBK2SX`YKdSX@YKSXBYssst++++++sssss+++sssss++++ssssss+s+ssss+ss+s+++ss+NuJ'``qqHPGr7r\%oJ)RN8\*n>P x r b ( \ ZV\6:lF~"zn8P~v\ f !Z!"`"#F#$$%%&:&&'~(() )***+<++, ,-n..//0D1122F2N3D3`3445v566677l7848`889R9j99999:::::;;,;D;\;v;;<<.^?32#".dxsm->#"=..="#>-/?&&?/.>&&>y@C @ P `   ufW/ufW@ H?22/]3/+]33]]]]]]/]2]]]]]]]]]]10!#!#H?H@\39?@@K@H+@7 H#!!@ H        @!   x  /3?399//3]33333/]83/]]//8393333+3/_]+83/]33/+3/]839333310!####53#5!3333#!3#LLNNGHJLLEH'ffssssd^;BK@E@.:H53'.54>754&'>[V)pq/"" ^U'7g_sue,!6E%!=U4#bQ"8dTPPMSb 6)'MxU4(R]lBJ{\; qA\*}f(2/-b QF'+?Sȹ= H9@ H3 H/ H H @ H H H@'@7@@6,()*+))@E+++)+)+ J(J8JJ,UUUU(8'7` p  O;E1*(#?32???32/]3]2]]2]99//8]8]332]10++++++++#".54>3232>54.#"#3#".54>3232>54.#"'.[]aY**Zb\\- 5%'3 3&&5!^ž.[]aY**Zb\\- 5%'3 3&&5!gxBBxggw@@wgT[00[TRZ//ZgxAAxggw@@wgT[00[TRZ//Z`N<J^@d+'6'$ZU;UFE KK)KK ,;;7 H47D7T777`=0 @ P   HEFPP.++Z$'6wBB;?2?3]?99/339+]/]33/]+33399//3]939310!'#".54>7.54>32>=!#";32674.#">&]pM~„D9`F(;&4gfca1&MuP "  "A8+ +T7?'Fe=m2J+7  8+"3 0 +:"&>-;pgZ]A0WVZ4DnP+.Pj;>dUJ%G:Zl7/9Bm=iN-G<EMU$>..?%#@?@$6?LG@"@PufW@ H?2/]/+]2]]]]]]]10!#@\u-7@@ H@ H  /3?3/]3+]+]210.54>7Dx_GG_xDzѰ?ED?B-5@!H @ H@ H?3/3/+3+]2+]104.'55>uCx_FF_xCzЯ?D祦E?H?@-@ H  /? ` p  ?3/]/]333+]1073% %#'%H\1DJ3\?\HB\?MPRMT% [@> (  8   `p'7 ?3]]33]]]]/]3]]33]]10#!5!3!monn\7=@)Hl|oI Y 8   2/]32]]]10]+%5>54.54632(Xd-C-!'!T?WfVAzhSj %-$5)EHx3H0@"@Pp@H/3/+]/]]105!37=(H سH@H(H  ?/]10++++74>32#".->#"=..="#>-/?&&?/.>&&>N/?/3/310'3 F3'(@ )` p  #?3?3/]3]2]10#".54>3232>54.#"38zw44wy8P)H99H)(H89I*ݩlljj뫓TT擓SSV!l@4 H  ' \u  p  @ H ?23?3/+]9]/]]]]]]]3]910]+3532>5#".5>?3;Ǩ& 'E@=+ DMX3 ) m 3'0N8(8"%5%j4 2"mH+i**%*$ H @1H@H-""P`pY' ?333?33]]]/]333+22]/+310+]!26?3!5>54&#"".54>321Pvg?Yy 8/_zFUKhT:aE&9ppsw<w0Z`kaIE?R5aOul0M78dL-/X~9D@BB%B44%4*.:. .. H;;%;;!!/6@H66@F),  H, ;/)z))h)*)))$1 ' w  ?22]]?33]]]]]9/]]]39/]33+32]99//+]3]9]10+]]]]".54>332>54.+532>54&#""&54>32tk30?#%B]82[F))QvMBb@ VT7E&9rpm{B0TrC>jD](DY0'<)AbB!DuX;bG(+PoCp~6Zr=!5!3467!}->"29#=-E ,2. }}0: mm :0}}NZ`TYO7R :@/1( H0( H+'')<H:6j663@% H3$$668666.8?33]]?33]]]9/3/+33]33+322]/]10]]++%2>54&#"'!#'.#!>32#".54633[E)'?60^A u  V3-@  7(((6(F(V((7?3?33]]]9/]32]/+3]39/310]]]+">32#".54>32#4."32654&9/ #=Q/O[dRHɂ :mdnHS&~&?Q,:,.^L0#Ԉ@Z5 =  @ H )i??33]9/+329/8310!!"#!R!89 { 53gd=;%9M@&I.*%  * 0+IDo: ::D@HDWDDH0H@#H++O55FII& 0?&!? ?3?39999]/3]2]99//+++]3]+3_]]999910_]]]]4>7.54>32#".2>54.'4.#">=+PoCz1pln7'Hf@\Q%G|{;5U< "IqO"<+!=U0G/*A,8R6&6#uBjWI"GxGh?6`N=^MB!-[cm>ah7>j&D^82QGC$9GX632267.#"]P#04*;O3 ;JY5Xk;@ynoLB>Jj!9P0X``'AS,-= .P;"*'2&7kgoHP݀K>y:\'f&(H سH@H(H(H سH@"H(H  #??/]3210++++++++74>32#".4>32#".->#"=..="#>-->#"=..="#>-/?&&?/.>&&>S/?&&?/.?&&?\\+h*(H$سH @9H(HHl|o--"I Y 8   ,'?2/]32]]3/]10]+++++%5>54.546324>32#".(Xd-C-!'!T?Wf->#"=..="#>-VAzhSj %-$5)EHx/?&&?/.?&&?|@XhWg@: H'@Hk{O/? H) @p/]33]+/]_]]]+3]+9=/33]]/3]105 olTߡ>@'  x_o/]]3]/]]3/]]3]210!5!5w\@-6Ffv9iyXhWg@: H'@Hk{O/? H) @p/]33]+/]_]]]+2]+9=/33]]/2]]10]]75 5loբdi!TL%98(H2سH.@ZH((H( H$$%$0&&&&/&o&& ;P;P` !+58!?33]]]?239/]3]]299//]]]310]+++++#'>54.#"".54>324>32#".3ff)})XpA'=*.>&?jM+4kqlLV->#"=..="#>-RRl[*#_mx=1Q; *F^4#6$2W@%3a/?&&?/.>&&>h]r@g NN*N OO*O^6^F^^f%F%9gIg%gf?ffR/FF/t/tttt;0R@RPRRll* aaFE0@p@HEE@4Y@/K?KOKK/]3?39///+]]33333333/]3]]22/99//]33]]393]10]]#"&'##".54>327332>54.#"32>7#".54>$3232>7.#"JxM[~" 6DU6@uY49W{cGf V1V#)SD+U|aj;lSz016buؽo=C{\H;,?+R!4SA.#WYg(F4*VZ7w[7$+ &(% *6 D~ӍG6hꇵJ0> N)H6 +X܅խ{Bf9fm*EV-4Umpk)6@])6/%/5/u//%tf@P48 vi4)6`555% /   ?333?3399/]333/]]]333/]]]]]]33933]10;!532>7!;!532>54&/.'!;!V-'#!',%\P  H   yC mm !:.J+9!mm .#MPO$JNN$9!.;@x:3J3:2J2(2  !!55Y5y5H55"}Y /""d<((Z(H(( T  0  /====@H..////;"?33?39/]]39+]/]]]3]]]239/]]93]]]10]]]]]])532>54.+5! 32>54.+532>54.+%D_:GuU/lT''TV$&bD`<:^GNH_9<`EJBDeI4 ?[xKm 4,)3 l{ClNKoH$}?dFF^:q/;@#! 01/1+f0!(!x!!& ?33?33]]2]3/]3310%2>7#"$&546$32#4.#"fKu\E.lXZ vt:!A`>5V@aQ$,b#8F" 2+_P4ln%@W2&A03eR3R甔ޓI9%2@Hg'/''' d& ?33?332]2]10#!532>54.+5!2+32>\\V&&V cusik6om 4,)1 lY%FW9%a@7#@@H'"d&!%"""""  ?33?9///]]333233/]+39/]9310%!2>?3!532>54.+5!#'.#!!!=, PV&&Vq 54.+5!# O?(V&&Vo =cQHya,4 mm1()1 lq7[@<f8&05@5P555559/99999/++,,,,#(x #?2?33]]9/]33]]223210"$&546$32#4.#"32674&+5!#"b\b@"DbA?`Apc-/iy&L$JH.xln%@W2$?0>hJ)R甔Q-R@ll$;,779TCZ@4+5!;!532>54.+5!#"!4.+5!#";X&&X=V&&VX&&XV&%Vm 4,6,4 mm 4,)1 mm 4,u,4 mm 4,#(1m93@!!o!!!P!d ?23?332]]]]103532>54.+5!#";9V&&VV''Vm 4,,4 mm 4,.,4 m1@?!o!!!!!!/3?33/]]33]10#"+532>54.+5!V'*JgyFJ%1]H,&VJ 4,7]mK.w%Vg)1 l9D@VF "FV D####3dE o> ::;EU""D- ;)--,?3333?3993]3333/]]32]399]]3933]]10>54!" ;#".';!532>54.+5!#"y!- 4?+*KHF$}78:"nNvY@0'[(V=V&&VV(B#91,"jj0B(0?&m *5 G,4 mm1()1 ll 4,"9I@-!!!!@!!@Pd ?23?339/2/]]3]]]103532>54.+5!#"!26?39V&&VV'}HP!sm 4,,4 ll 1)[W79d6p @ H  H %%& H&/e8$@% H$ d7 )""&#%%$%4% ?33]?3333323+23+933++10!532>5#;!532>54.+5! !#"; 1 cO'8$ V&&VNTFCV&&Vm-% ,4 mm1()1 l#l 4,-,4 m9+,H@)W,e.?.Xd-&"#  ?33?3332]3]]2]310!;!532>54.+5!4.+5!#"'VV&&V&VV&h,4 mm 4,)1 l)1 ll 4,;q'.@g)) f(#?3?32]]2]10#"$&546$3232>54.#"[VV[&T_`R%%R_`T&ݩllkk뫓TT擓SS9#0n@$""$d1H**0@P22O2_2@ H$$HI$$?$$$0 ?33?339/]+]3+]]3/]3]2310]+;!532>54.+5!232>54.+@F(}V&&VuҋD^2LjC;_EP\M(1mm 4,)1 l54.#"A~y 8Qb3!\a ͈CV[&T_`R%%R_`T&ݐqS w6u}kk뫓TT擓SS90=q@ )# %@6 H&%1((d>H770@P????''111- 0= ?33?33229/]39]]22]239+10]]]732>54.+5! ;#".'#;!32>54.+9V&&V"0Od4114C\gK &X=sG_9<`Dom1()1 l¹Q~^A\,?)m)H9l(1m&KqKNlE=JE~@T-)---( (()(Y(i((AgGGG@G/G 7fF777A#2(-x--2-#& V   ?22]]?33]99]99]223]]]22]3]]10".54>332>54.'.54>32#4.#"o/'AS-*Jg=<^B"*UVkZ'Llqk4?_?4S;)J7 Khi_+N4Rf25H.XY-".+cU80G.+LII*+Zg{KcvB%#r@J%%%%%%@ H/Pgg0@ # ?33?339/]3/]]3]3/]33/]]]3+]]10%;!532>5#"#!#'.+?&V=V&.>'  '>.,4 mm 4,G%6"s"6%!5G@.   ,e7?7o777777"d634' ?3?3332]]210]]#"#".54.+5!#"32>54.+5!X&;}†ӒM%XV&+PpFDnM*&VJ 4,gr=3p~)1 ll 4,VuG#JrN#)1 l2@ @u H H H!!f y i  4  44?4O4444, Hf ;[ ,01!D!!!?33]?3339/]]+]]33]/]]]]33]93310+]]++]]#"#.+5!#">7>54&+5!c^L #$/^    -%kJ !:/L' ll3>DC;EEo "$l>@ >9#I#Y##5"E"U"u"""'""#":g>>!z h : J '7Wgw 4:&:!:F1V1f11*$$$$$$ @_@@$1!@ H/!!.*>*+::@ H&:6:::#?33]]+3?33]+9]/]]]33/]]33]9]]33]]]]]33]33]]]]]10] >7>54&+5!#"# #.+5!#">7 l?D/'.$ #-/>=  !0cZL%Zch1 5,ll #>3V? * ll03A''[]V!Bs<mJ@!3VGVGG2%C !%324 wCCCFCVCfCC:4D444!"%%"%HG32H,%42P2222D22xIYi {o[PD0%2!3G4C  =::;(,+?333?3339/]]]]]33/]]]]333]]/]]]399/]33]]910]]]]4.+5!#" ;!53254.' ;!532>7 .+5!#"7>/*0/0y(V&\ {"8D %81.f!)2##/ % ll#8*k?-mmC ,!C "#mm%5!)ll  @#E:@h+${11T1 11112VF   t [ D  2 <<<O<_<?<< (,2$1)8?23?39/]99332399]]]]/]22/]]]]]]33]]93/]]]3310!532>5.+5!#">?>54&+5!#";P`+  )#>= )7"w CD/'%!#)em2+* ll2*=Ad06E-=50ll$>0Z-2mV@^) 9    @ &60 @ P  p  ?33?99//]]33/]33]/3]]]3/]33/]3]]]10 !2>?3!5!"#!?P/ fm0>% FN/#:I'\Zf1E*o'@O /2?3/]]233]10!#";7X">-->"Xl :10; lN?//2/3103#L5@!O   0`//3?3/]]3]33]10!532>54.+5X#=--=#Xl ;01: l^9@$?O @P` `?22/3/]/]933]103# #XŠcBV@ /]3//10!5!@yLT! %@ ?_@H/+]/]3/310.'5!0umXA278SXQ"QQL\d?w@J 77 446:+"UAA6V@+1 ::1'*kZH. ?33]]]]]]?3?39/332]]2]339310]]]32>=""&54>32;!'##".546?54.8<,G3\=T2'5 CsWlk6 0"+"K8P2%R`*8!m)=((S}Uq3V>"#+;L@-**9W==== 11T<$,,'  66?33?3?3?33233]]210]]#".'#!532>54.+5!3>32"32654&7jf;`M;9{8,,;!,tdi6=P00R=eZZ)؍C0B(m 8/#,5 l V&-/LXC3hjek7`d-P+%+5+ H @! H)W/////@ HV. &!?22?332+]2/310++]".54>32#4.#"32>7\qJMjcf5;gP 3'-G3r5]L9.a:؝ކ9!:O-!?05`H++h-=#3'PA)^/AL@-  & 55UCCCC?VB-.:: 00?3?33?33?32]]23310]]%;!'##".54>323&'.=4.+5!2>54.#"b/="s9;M`32!32>7`Veb'=džDDyo|Ba'Ec@5ZI770dߪN|V. LцڒI?|zig`.-=#F,O<$''/E@P H% H&':0111@H$'' F*: ' $ -?23?333?33]]]]/]333+]/]3]910+]+]3532>5#5354>32#4.#"!!;'-3+1glcN!} '$ +3`m 4,̌L_g6)6 E@:2!;^Dp4,4 mqSbvع@ H 33)3 11)1((&(& AA+A EE)ET22!::2 J?%ccccDDZ**mmmm*`xoxxxJ ?G5^:^(^^$h`$$WGOrOGW/?3?33/399//]33]]99]]3/]3]]]//33/]3]]933/3/]310]]]]]]+#4.#"#".';2#"&5467.54>7.54632>3232654&+"32>54.#"q!9* ' $1fk ""  5+fa.Hߘ펇6*0C(-M7 I}.)4@'%7%{vRS@5""6()6 !6)(6!#+   ;DI&M`6 " +PuI_g7nu -:$":5/9Pf>%)" *UQheA4 "=6W>"!?Y79]B%&D^#!=G@, ,K, 33%36U??@?% T>**0"#?2?3?33]23]210]]]!4.#";!532>54.+5!3>32;Z'?-3D)&7"#:+,3ENW0O~W."4#BgF%4Ws?y+4 mm 8.1)1l(\'.,:B! .`h%09 m#),v@T ...@.P. ( 8  # H#T-(,,+S !?33/?3322+]3]]]]]10]]]]]]4>32#".2>54.+5!;!5/?%#@00@#%?/u3+,3+4u'8##8'&8$$8 4,n)1m,4 mm)*j@K)))(###,,P,!! HT+S&& ?3?33/2+]3]]]]10]]]]]]+532>54.+54>32#".Mq)8W;-54.+5!>54!";pnP(R+4u3+,3"73GQ6n1 $I?=,4 mm 4,9)1lI!'(!&-ggS[jQN#)@@T ?3?332]]]1072>54.+5!;!5;3+,3+4um 4,9)1l,4 mm)dS{@N 'K'I 77%7.LLLBB:UUU U@UUUUU% TT/&&&F4+!#?RBR?2333?3?3333]323]29/3]310]]]]!4.#";!532>54.+5!3>323>32;!4&#";Z'?-3D)&7"#:+'6" ENW0q%IS[0O~W."4"9IZ0D+"4#BgF%4Ws?y+4 mm 8.d+4 g:D$ Q[:D$ .`h%09 m/Pk;m09 m)!d8Q@1 'I' ..%.1U::@:% T9%%+!#7?2333?3?33]23]210]]]!4.#";!532>54.+5!3>32;Z'?-3D)&7"#:+'6" ENW0O~W."4#BgF%4Ws?y+4 mm 8.^+4 m:D$ .`h%09 m`d#$@W%?%%V$ ?3?32]210!".5!232>54.#"{ĉI{ÉI3R;;Q33R;;R2)Gؐ!Fאmp99pmnn77n^2BL@,6WDDD>))TC.221"3399 ?3?33?33?33233]210]]2>54.+5!3>32#"&'#;!52654&#"33+%3% ;M`;fj76idu,+3-agYYe=R00P 4,H/8 m(B0Cؕ׌CYK/-&V ,4 mm7kejh3^-^6FP@/  B%/UHHHH:VG(&$77==4?23?33?33?32]]23310]]532>=4>767##".54>3237!#";"32>54.`%"<-:K]:cj77kd<`M;)"=/-<"%+gWWg=Q11Qm3* '+1:#<+Cؔ֋C/B*m 8/*3m6ifjh3)b1V@ H`33++T2@ H##00?33?3?333]+23]3/]310+)532>54.+5!3>32#4.#";T#:*'6") 0C[?yr| '$4$ &5Bm :0R-7 m.E.UQ^h*@*$9HIB-7 =bA@ ??&:&98 H7@ H H H H@@ H&&&@J H    H;+K++*++(9(I((=WCC/C3VB3=#.++.+#  4 D  ?22]?33]999922]22]3]]10+]+]++++++]]]".54>332>54.'.54>32#4&#"oc-';B :Q25M1:aHSW-?tcaV(ap[Y!=-?lTDsS.;r$@X32=" @fG&'3"5/-ASkERzQ(!6D#EHfq / !4/0=OeAU\1+HR@4 H / _ p_ ?3?33/]]]333]]3/]910+%267#".5#5267673!!(I 7I\6EpN*Bh??  !M{Zi4"B]V5#J/E@(  H(U11@1 T0%& -?2?33]?3332]2310+]!'##"&54.+5!32>54.+5!;- HR[0"5%$<-1G-(6$25A# -8 mpBjK)+QsH-4m-3mJ+@4 H %`p---o--@# H i     %)*?33?333339/]]]33+]]]]]3/]]33933+]10#"!.#5!#">7>54&+5!)! '3"{V% %  -#; ;1{& mm! 0|A4@=2 9"mJ<@( 7<7! `p>/>?>o>>.@) H.'!!!.!&++<@ H (7f@ H ?3333+]3?33]+333339/]33+]]3/]339]333333103>7>54&+5!#"# #.+5!#"3>7  Z CE," ",X>= L   ?Jg&.2;&+<40mm #?2Xq * mm#30%NJB3mLGJF@[12H ) HC?Iy tcHHH88&8F8f888??@H???&??@< H?622*#00t00f000G?2 966CC6CC1)7*&)?33333?39]33393]]]]33333+]]+3]]]]]3]]]]]33333]]]993+]]+99104&+5!#";!53254./;!532>7 .+5!#"7>{90//2!-/O&t {u:E%812+V/#- ra%mm 3(;1mmC  +4"#mm 2&7J66mm  )u8JA@GAxAA== H 22`pC/CoCCCCCCCC!!%%%%%@* H%=%2 B@" ?22333?33339/]]]333+]3]]]3/]3933+]33]]10"+52>7.#5!">7>54/& $NOO%$@GVre#khC "(H?3!5!"#!+ y 1\" {t(?-BTj !7*1LX=L-u@M( H( H`//?/--!&    K : )   &!   O o  ++/2?39/]39/3]]]]]]]]9223]]10++_]".54.'5>546;#";VX-%@W22W@%P,8! jwvk !8,P*OpF^;I*}*I9`l2K0]wx]0K3m @P `??/]3]10#3=L- *س H@L H`//?/ (""""D"5"&"""(0(@((( ""!!O!o!!!/2?39/]39/]3]]]]]]]]339]]10++_]532>54675.54.+532#=P,8! kvwj !8,Pí%@W22W@%-XVm3K0^]xw]\0K2l9I*}*I;FpO*qV b## H@ H H H%   @ H  H@H ( H  /]333+3/++]333+/]]]10++]++].#"563232>7#".%8-)<;8c27C/%8.);;8b17C  ,l  !,m9VW (HسH@H(H@H`p@ H /?/+]]32]+10++++!3#".54>329ysl->#"=..="#>-?/?&&?/.?&&?u%,5(@ H'@+ H  "%% *0  H7%----@ H1 1 0,"0*,/2/3/3/3/+3]]2+9/33223/310]]+]]+%.54>753#4.'>7#;gwADybX\/;fP &4[K9)TYCJ!4%EϒՈD%9I)!?0+RD1 +;#3#J?-ߞ%<?i1=V@6 H65 H5A HB@! H ==*=O323267#".'.#"57>54&'#53.54632#4.#"!) !&&)1 -I-Fi((tQC@;2o*#=85ZA B6#  na,~$@2M[  k'')x   SDGF   /)8Mc>;a5}@HN'%@W2H^/cQ4uy*QJ@}+"6@_ H!  -?_  H#pWw H !!(0Pp @ H2 ?  /]]33+3]]]2933332+]3/]33+3]]]2933332+310467'7>327'#"&''7&732>54.#"! {gz-h:9i-g}" ygy.l;:l-viw;%?V13W?##?W31V?%;m-{f{!!h-i;;g-yfy#!whw[t1VA%%AV13WA%%AWRJ@       4-:::::y:: :K:::$?;CCv;K @40>  ;$? B:-HI/?O`2I?23?399//]]+]]9333333]32333/]]333/]]]]3333393/]]]]]3310]]]]]]]]3532>75!5!5!53.+5!#">?>54&+5!#"!!!!;/"<-&J>6N&V  9D(#" ->"/m0-qywx#* ll&*;%Ax*N,#2' *(ll$>0xwyf14m*@ P  `?3?3/]333]10#3#334 Mc@lKK%K**#* $$*$ ,V"NNA 7"/,FaYI@H"@H_,o,`I",II,"Ae/eY`pFaVaV4/' /22?339933/]3]9////]]++39933339910]]]]".54>332654.'.5467.54>32#4.#"4.'.'>5s[&0C(<[U3?T-_N2d]1C4.4\`jB%H@4+t?A% ="/*B=?("5,')@O #/32/3/]]]104>32#".%4>32#".!,,"",,!",+""+,"#1 1#"11"#1 1#"11qR7e@ H HL HR@LHcJY@T_JJTJT*g@gPgg/g0@P=8EYOEEEOOEOEO##1?3?399//]]333/]3]]]299//]33310+++4>32#".732>54.#"2>7#".54>32#4.#"q6ahhb66bhha6p.SuXYuS..SuYXuS./P@/ 'MsMfg38olGiF#&3"7)He@hb66bhhb55bhXuS..SuXXuS..Su9(2 !:0BxgdxC'5$ 7-0^Y; 1 4@ , H* H@ H+/"9IH@H66/6O6o666666 6F@7H+/+o++++ //("(!H9I( ?33]]+]?3?339/3]/]3]+]]+2]+]339310+++326=""&54>323#'##"&546?54.'. ?>/UGyju0So?'12.9E)hx\ 6,O>T?N#.)0#0at- Zd) jgfa71" | H @ H /?H@' H@H/O   ?3322333]/]+]33++33/]33++3103#%3#9ϐ9Ώ^effejeffe%%@@H'?33]]]]/]+]]210#!5%%n3HqR)6Rnɹ@ H&*!!0 0@@ZH7aEEEEp@pPpp/pS707@7P77&)& *6 )))*)**)Z>ZLh>?3?39///]]]]]3339]3333/]3]]]299//+]3333339]310+32>54.+5! 3##;!32>54.+4>32#".732>54.#"$$q` 4C# #l#fV7J,/K6P{6ahhb66bhha6p.SuXYuS..SuYXuS.fXH1J6%  E}E,D.-@'hb66bhhb55bhXuS..SuXXuS..Su@/?o/]2//10!5!@ybJ'a H H@4 H HWX )@P)WX####?3]2]]2]2]10++++4>32#".732>54.#"b1Tq@AqT11TqA@qT1.=#$>..>$#=.AqT11TqA@qT00Tq@">//>"$?..?h@D ( 8     ` p  ' 7  ?3?3]]3333]]]]/]3]]33]]3310)5!#!5!3!omonnLJ+;K@ H6$H* H*H*@` H( -P-""P`pyhO_<+ 'v $    ?33]]]]]3?33]]]]]]]]]]]/]333]22/]10++++]+]!26?3!57>54&#"".54>32BsYG,$ Lu5E(+5#*"B4 &JmFSwM$-PXhF'7擽,SPP*BJ%:I#/!%?.9QP=;˵H HHس H"@ H(66)(11 =P= ) H)@? H5566?6O6_666.).%M];*?33]]]]]]]]]]?339/]393+/+33]2]99//9]2]10+++++4&#"".54>32#"&'532654&+532>+5 + )B.&JnGGrR,pa.XD*Lu?T(8=@bmopDD&C1?D2@$"&C14K0Zq ,G8Kg?j  OYEJ`.AJR! %@  ? _  @H /+]/3/]310>7!#J781BXmu0LQQ"QXSJ5]@8H( H 31$$'U77@7H HT6& *--0?2332/3?3?322++9]2310]++"&'#".54>7!32>5!;#"&'#=Ux(.WH1F,&F5 8#<-+E17DGi 5BT?9UlC 'A0?gK=Ȁ}\7bI*.SuGs^PmbP-J3R'i@DIY( H( Hg _ o  h''))@P`   %'/333?339/]/]]3/]3]9/]]3]10++]#!532>5#".54>3!#";!\#=-ZxH"LxW##=..=#l :0AoPR`4l :10: l3{<(H سH@H(H  //]10++++4>32#".->#"=..="#>-/?&&?/.?&&?f;<@ HO o ?3//3/]29/]333]10+#".'532654.'73;*LkA.20&J:C*8!+u2Q96W=  })5& X0C`LT@8P H&  Wjz\*: 0Pp ?33?3/9/]3]]]]9]+]10;!532>5#"&5>?#Th!0K%)%[=\3ZZE9F;* ),DH 7  H H H@L H HI:) /F5&P/ ?3?3/]]3]]]]]]2]]]]]10+++++32654&#"#"&546327=MK>@LK<V`4fy||yywwy+V o@H H  H  0H H/O/O   ?3322333]/]]33++33/]33++310 #3#3ǏϏ9eƏϏ:j &{'5@ ))?))0o_]]]5]]5]]55?55 &{'t1@....@o_]]]5]]5]]5?5P&u'H4@!II_I?II===p= ==]]]]]5]]]55?55qyT%98@H2(H.(H(@.H$ H#&`0p00`p00;P; @ H !+57!/33]]]?239/+3]299//]]310]+++++4>?332>52#".#".54>32q3ef)})Wq@'=*.?&?jM+4kqlL->#"=..="#>-RlZ*#_mx=1Q; )G]5$6$2V@%3a/?&&?/.?&&?s&$C-R7&v?3!5326=!;!53267'5!#'.#!!! !, PV80)V?<#5T-? %+55+559-T@/%(g/////$(( d.#'$(?33?9/]]3333323]2910#!532>5#534.+5!2+!!32>\\V&&V cusik6om 4,})1 lY%}W9+o&1R@ 8&C3 $%+5+5qs&2CdR(&-4 %+5+5qs&2vPR@ 4&x(. %+5+5qs&2R4&(3 %+5+5qo&2R3&>. %+5+5qR&2jR7&(F %+55+55Hu v@H HH H H @0 H H H `p @p?]/]10]++]++]++]++ ' 7  ;h-h--i/gwh/-g/ifq &2Z@7*+''''  g44 f3+*"." . ?3?9392]]92]910#"'#7&546$3273.#"4&'32>[>^VUB@`&kD`T&(jE`R%ݩl5s`Iڪkw`׀M*,SNP**T!s&8CdR6&;B%+5+5!s&8v9R@ B&6<%+5+5!s&8R@B& 646A%+]5+5!R&8jR+@E&6P6@6 66=6T%+]]]]]55+55s&<vR@ G&;A*%+5+59 9e@ 4#d: o  ;O;_;.**+@ H 4/ 444+?33?99//]]33+33]]/]]32331032>54.++;!532>54.+5!#"3;2LjC;_EP@F'T=V&&VT(sҋD&R\RyO'\M<(1mm 4,,4 ll0)%=o T)R9 H98@ H3 H2*2 2 H@ H H@`H#6H6?666F; ,WT`T?TwFXFF&F6FFRNTS9,6646D66,  PPQ6;);Q##))HG@?3?3?39/?399]3399]]22]]]]]2239/]]+33]+10+++]]++]+4>32#"#"&'5332654.'.54>7.#"!532>5#53Rq}9s2WA&6W>@X77g_]Bn#5F*EQ/P=Kc;1YzH:S54U< X&^l/JՋ 5)$6//KV\0WV*%&)J7!HB$=86#Q\d5GgE%^R%"GmJjm 4,ʎ\!&DC@&EL6(%+5+5\!&Dv@ L&d@F6(%+5+5\!&DL&@K6(%+5+5\&DK&VF6(%+5+5\&DjO&@^6(%+55+55\&DE&J@6(%+55+55\dU@FAA%A "" 03;;;;;EEE&3! QDWWWWW_W!VV;@1 H;++8 EE%Jk0Z0H0.00 0>08JOJ?333]?33]]]]]]9/333333+2]]]22]9/]933]399]310]]]32>="!4&"&'#".546?54.#""&54>32>32!32>78<,G3\=T25UgcN>BÊDuW23*'5 CsWm6<[o|B`'Fc@5ZI68/b1XX%Da<!:Tv d`g](S}Uq3V>"!;Q0>K8P26>9;?|zig`.-=#F,O<$`d&Fz @ 08 868.%+]]5`;!&HC+&07%+5+5`;!&Hv@ 7&+1%+5+5`;!&H@ 7&+6%+5+5`;&Hj@ :&+I%+55+55!&C&% %+5+5#!&v#@%&ph%+]]]]5+5!&%&$ %+5+5&j(&7 %+55+55`(<@ ''!! 33$!#P`@ H##W>?>)V=$!#""@H8 P  ..?3?39/]339/+32/392]9///+]933]]10]!".54>32.'57.'5%32>54.#"zÉIA~wDu& (8'߶3sK&ipk'N\33R;:Q33R;:R2TB̉ˇC !AAA _ % #,I=eg55geif21e)!&QPD&O?7%+5+5`!&RC$&~)0%+5+5`!&Rv@ 0&$*%+5+5`!&R#0&$/%+5+5`&R!/&:*%+5+5`&Rj#3&$B%+55+55#'@## ' '& &p'')&& &&&5&!!!%%&:)0&?3/]3]]]]]]33/]3]]]]]]/]]]]9/33/3]]]10]]]]]]]]4>32#"&4>32#"&!5!***1A***1A)o`#0 0#"0 8=#0 0#"0 8`!,%H@^H6&F&&&$& &&+&9I +$%&"" W.?.. V-&%((?3?939223]9223910]]]]]]++!"'#7.5!273&#"4&'32>hBfV\H6AiRX`4e;R23a;Q3)+iG!iH?m-;V7nn8a,K9p5#!&XC0&U5<.%+5+55#!&Xv@ <&g06.%+5+55#!&XD<&0;.%+5+55#&XjJ?&0N.%+55+55!&\v@ N&BH%+5+57GL@,!!;WIIIC..TH3776'88$>> ?3?33?33?33233]210]]2>54.+5!3>32#"&'#;!52654&#"34+%3 ;M`;fj76idu,+3-agYYe>Q00P 4,/8 lS&-/(B0Cؕ׌CYK/-&V ,4 mm7kejh3&\j@ Q&B`%+55+55#J+@@PT ?3?332]]]1072>54.+5!;!5;3+,3+4um 4,n)1m,4 mm!\@5 H H 0   ? _  @H /+]32]//]933]+]]+]10>7!#.'#A@99@@:>;;=9LQQ""QQL.2222.^J'd H H@9 H  HXW XW####?_@H/+]3]2]/3]2]10++++#".54>324.#"32>J&CZ33ZC&&CZ33ZC&}!,,!!,,!7S88S76T88T6* ** *k @ H H@;HwHx8H_o6F ? _  @H /+]33]2/]33]/3]+/]3]+10++2>73#".#"#>32#(C[77YMC!#)C\77YMC'>nS0(1('>nS0(1( o?3//10!5! ݒ o?3//10!5! ݒsD@@(Hcs?oF V 7   ?3/]3]]2]10]+4>7#"&s(Wd-C-"&"U?We%AzhSj %-#6)EHx\D9@&Hl|?oI Y 8  ?2/]32]]]10]+5>54.54632(Xd-C-!'!T?WfAzhSj %-$5)EHx\7=@)Hl|oI Y 8   2/]32]]]10]+%5>54.54632(Xd-C-!'!T?WfVAzhSj %-$5)EHxsD/{+@ Hc+s+@DHcs1@ H1F V 7  *F#V#7## `-?3332/]3]]22/]3]]2_]+10_]+]+4>7#"&%4>7#"&(Wd,D-"&"U?WeT(Wd-C-"&"U?We%AzhSj %-#6)EHxiAzhSj %-#6)EHx\Dw/r@N+Hl+|+Hl|1@ H1I#Y#8##**/***I Y 8  `-?3223/]32]]2/]32]]_]+10_]+]+5>54.546325>54.54632(Xd-C-!'!T?Wf(Xd-C-!'!T?WfAzhSj %-$5)EHxiAzhSj %-$5)EHx\w7/n@L+Hl+|+Hl|I#Y#8##**/**I Y 8 *  11-02223]/]322]]/]32]]10]+]+%5>54.546325>54.54632(Xd-C-!'!T?Wf(Xd-C-!'!T?WfVAzhSj %-$5)EHxiAzhSj %-$5)EHxsf( H س H@? H( H0`P  /_o /3/]]]2/]10++++4>32#".s,Ol@=!5334673q))9{;GZZGR4mc ?&!V4j  HNl F   . .* h   6  & ,Y   (  8 \& \ T@Digitized data copyright 2007, Google Corporation.Digitized data copyright 2007, Google Corporation.Droid SerifDroid SerifBoldBoldAscender - Droid Serif BoldAscender - Droid Serif BoldDroid Serif BoldDroid Serif BoldVersion 1.00 build 112Version 1.00 build 112DroidSerif-BoldDroidSerif-BoldDroid is a trademark of Google and may be registered in certain jurisdictions.Droid is a trademark of Google and may be registered in certain jurisdictions.Ascender CorporationAscender CorporationDroid Serif is a contemporary serif typeface family designed for comfortable reading on screen. Droid Serif is slightly condensed to maximize the amount of text displayed on small screens. Vertical stress and open forms contribute to its readability while its proportion and overall design complement its companion Droid Sans.Droid Serif is a contemporary serif typeface family designed for comfortable reading on screen. Droid Serif is slightly condensed to maximize the amount of text displayed on small screens. Vertical stress and open forms contribute to its readability while its proportion and overall design complement its companion Droid Sans.http://www.ascendercorp.com/http://www.ascendercorp.com/http://www.ascendercorp.com/typedesigners.htmlhttp://www.ascendercorp.com/typedesigners.htmlLicensed under the Apache License, Version 2.0Licensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0http://www.apache.org/licenses/LICENSE-2.0ff  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~uni00AD overscore foursuperior4P ,latnkern $O8v088fvZZ$:Hj|$::  " " " " " "vvvvv8888888vvvvZ8:$$$$$$:$:::::: :   $\J\\\\\\   $\ 6\ \ q"&*24789\:q?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ardeixpkvjsgwl|cnm}bɹyqz@EYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"! ,E#F` &`&#HH-,E#F#a &a&#HH-,E#F` a F`&#HH-,E#F#a ` &a a&#HH-,E#F`@a f`&#HH-,E#F#a@` &a@a&#HH-, <<-, E# D# ZQX# D#Y QX# MD#Y &QX# D#Y!!-, EhD ` EFvhE`D-, C#Ce -, C#C -,(#p(>(#p(E: -, E%EadPQXED!!Y-,I#D-, EC`D-,CCe -, i@a ,b`+ d#da\XaY-,E+)#D)z-,Ee,#DE+#D-,KRXED!!Y-,KQXED!!Y-,%# `#-,%# a#-,%-,F#F`F# F`ab# # pE` PXaFY`h:-, E%FRKQ[X%F ha%%?#!8!Y-, E%FPX%F ha%%?#!8!Y-,CC -,!! d#d@b-,!QX d#d b@/+Y`-,!QX d#dUb/+Y`-, d#d@b`#!-,KSX%Id#Ei@ab aj#D#!# 9/Y-,KSX %Idi &%Id#ab aj#D&#D#D& 9# 9//Y-,E#E`#E`#E`#vhb -,H+-, ETX@D E@aD!!Y-,E0/E#Ea``iD-,KQX/#p#B!!Y-,KQX %EiSXD!!Y!!Y-,EC`c`iD-,/ED-,E# E`D-,E#E`D-,K#QX34 34YDD-,CX&EXdf`d `f X!@YaY#XeY)#D#)!!!!!Y-,CTXKS#KQZX8!!Y!!!!Y-,CX%Ed `f X!@Ya#XeY)#D%% XY%% F%#B<%%%% F%`#B< XY%%)) EeD%%)%% XY%%CH%%%%`CH!Y!!!!!!!-,% F%#B%%EH!!!!-,% %%CH!!!-,E# E P X#e#Y#h @PX!@Y#XeY`D-,KS#KQZX E`D!!Y-,KTX E`D!!Y-,KS#KQZX8!!Y-,!KTX8!!Y-,CTXF+!!!!Y-,CTXG+!!!Y-,CTXH+!!!!Y-,CTXI+!!!Y-, #KSKQZX#8!!Y-,%ISX @8!Y-,F#F`#Fa#  Fab@@pE`h:-, #Id#SX<!Y-,KRX}zY-,KKTB-,B#Q@SZX TXC`BY$QX @TXC`B$TX C`BKKRXC`BY@TXC`BY@cTXC`BY@cTXC`BY@cTX@C`BYYYYY-,Eh#KQX# E d@PX|Yh`YD-,%%#>#> #eB #B#?#? #eB#B-,zE#-@`SC7@rGQ@43U3UU @ Fv 00OuNtN'o7oGoUg@f Fe/e0d@dd`N_N^N3UU3U@WF0V@VPV?UOUUUUPTTHG )GG3UU3UUGU?oTS++KRKP[%S@QZUZ[XYBK2SX`YKdSX@YKSXBYssst++++++s_s+_sss+s++++++ss+s++++s++sssssss+ss+++++_ss_s+_ss_ss+_NuJVVfuqqVfnuugVfq^odoyH#CV-CyyUy"KppWP}kT<AR W]j}JrrPl4t^ , @ * R  R , | >.rtF`R.d r@: l2j$ 0 !!Z!"H##$$%D%`%&&&^&'r'(())*++L+n+v,V,l,--\--.j../,/`//00L0~11101H1`1z12B2X2p222223333333344d4455545V56p66666777788868N8f88929H9`9v999:.:::::;;;;<0<<<<=.=j=> >>?6?\???F@-WF;)o P ??9/]]9/3]]2]]103#4>32#".H^R#//##//#(5  5('6  6u%@/ o@o?22/]]103#3#>^B>^R'@]      p @/!  ?3?399//33223322]/]9//////]]333310}!!!#!##53#5!3!33#L5NzMN{NN I}KL{LLoy``yymmyo^2;F@0AAA9p%+3n7534.'>m\R%XJ'F6\`2ƶg3hc[&V+OmB`R%1ZPg.I5\d5*=((>)y%=R0BC-[K4"(P]qI  ;9kU7+T]lCEpS3 {)A809u +A2( '7F\';OSb@:RSPQSSQSQ( <2F(UoU  SQK77A-#?]?]??/]]99//]3310#".54>3232>54.#"#".54>3232>54.#"#3*T~TXR''RYT}S*$:**:##8**;%*T~TXQ''RYT}S*$:**:""8**;%ۆgxBBxggw@@wgX`22`XW_11_WgxAAxggw@@wgX`22`XW_11_J>N`@qD6%%$=$E\\OWGO --0=@=P==bJHP   /  EE9E9EDTN-M++?6666%%6%$\\$%6<?O7.54>32>=!#";%2>74.#">%YlN{yqaWbJ.UTX2DnP+.Pj;BfVM(u._[W%PV7/BM V^,=$?ShBKsO(H)F3jX'DCH+:ETuN@/o?/]]103#u>^u( H H ??/++210.54>7J@}i::ȍi}@zմ@^8!ܟ8\@BP!@  ??]2]104.'55>{A|i~;;~ȍi|AzԳ@\8ṹݠ8^@Tb@Juu zz P`p0   o  ?]]9/]]]910]]]]73% %#'%TH;59=HZH93HXEedDàf% P@1 _   P` P /]]32/]]9/]32/]]10#!5!3!y{y{{{yzy=/@ )9/?O]9/]10%5>54.54632$Q\e^"&"M9 8,VAzhSV \<);>-C3H{//105!3ᚚ!@/?) 9  ?/]]]1074>32#".#//##//#}(5  5('6  6N?//10#3yyw \'"@o)o  #uu??/]10#".54>3232>54.#";xy~u88uxx;?gJKf??eJKg@ݩlljj뫘WWUU*@n_ s?2?9/]]103532>5#"&5>?3;ʼn#=-'F@=-:?GS3.=#dV :040N8=1%5%j+0: Vh/B@'n I@H1!&& ! !t+v??99///3]+]10!2>?3!5>54.#"".54>32.XQ.<&V VRoD3O6FW2%@03ecal9uHY*7%_F;_E%2Tn= 3';eJ)1Y~wJ_@7o++707 Co7L!FCCFF>0 t / _  &u>t&??99//]]]99//]9/9]10%2>54.+532>54.#"".54>32#".5463=nT23`VAADuW25S;FX2%@03fcaq>1XxG&VTM;#,Mftz;e_-F;#BaZ"N~\AiJ)h-RuI<^B"2Tn= 3';eJ),SyNEzcF$6NgDRaD*%>S->E6[B%#\'X@7hx')& &&u&&&&  s ?2?39/]32/2]]]2210]]]];!532>=!5334>7!^->":#=-y>!%% 0: VV :0TNs-lpo0/:@=5q5g@?Io$@H$71..11o1111  uu)v??9/299/]/]]222+3/]10%2>54.#"'!#'.#!>32#".5463?kO,,RrG5M;,1B V ) s'u[c~GCurX$?B=]d#QaQxP' = > 6llh{C'32#".54>32#4."32654& =HU2^k:;qifH:XwZVT)PM,Dy(JA6'Fd=ns}h':lblHUae8$7.54>32#".2>54.'4.#">^/TrC:bF'2jv`g6(Hf>L}Z1Fpsu;CmM*&Vcar#Eh07YA9Y<#EhD:K,oJoWF !M\m@Gf>5^MCfQA$R`pCaj732#".5467>75.#"Ӫ ;K[6Yk;32#".4>32#".#//##//##//##//#}(5  5('6  6s(5  5('6!!6=T+D@+)"9"" )9-,-/-?-O-,'?]9/]3/]10%5>54.546324>32#".$Q\e^"&"M9 8,#//##//#VAzhSV \<);>-CH(5  5('6!!6I@HH @HHP/]3+3+2+2+/29105 oPHى(@ @?o/]/]3210!5!5oNyyyyJ@H@ H H@ HP/]3+3+2+2+/391075 5Poqu'HX%9X@8)0900&I&&&&&&&H ;/+5 N/?9//]99//]]]]]10#>54.#"".54>324>32#". }YyI6U;@W6%>--_caxD5f_#//##//#7$_oy=5W="*H_5!2!1T>#2_XTr^'(5  5('6  6[nb@\e&%6%%P/p;P@ HP*_h@4W@K/?99//932/+99//33]310#"&'##".54>327332>54.#"32>7#"$&54>3232>7.#"HtG[y 5CS4>qU35St^B_R1h"--^L1VՀdj11@ H)%(/$#(#.((( )##11 #$^/..( ] /?3222?339/32//33]23]93310]+];!532>73;!53254&/.'!N CE/ '*"Ơ ",/{ R 5/40VV $>1 * VVb$[J%EHO0N!.;P@// 5[p([ g==@=/"Zd<.^////;^]]"^??9/]92]]]910)532>54.+5! 32>54.+532>54.+'D]6FtS.#=..="% ^WxL"IxZˎZxH"LyWHEhN5 :]QV :0-7 Va GrRPvN&f CjJKe>u/.@! 1@o1[+f0`&_ ??]102#4.#"32>7#".54>ig30A&9[Ctf.0hqKsXC5lrPT#>T1!5%0^K/UZ0?" &'QB+lnN\%(@ [g'%Z d&$^]%^]??10#!532>54.+5!22>5+\T#=..="sZ5zz=ݨgV :0-7 VaS1BN'U@1&&o)$Zd('^$$/$$$ #^] ]^ ??9///]]2]9/10%!2>?3!532>54.+5!#'.#!!!1E.k"=..=# j +A.f.="XV 7-0: VX"=.dN`%I@+''/'_'Zd&^/_]^ ] ?2?99//]2]9/10!!;!532>54.+5!#'.#->"D"=..=" j -D0PdM0: VV 7--7 VX#=-u9?&Z7@ H7;;[f:0,]..#_ #_??9/2]+10"$&546$32#4.#"32674.+5!#"5V[ rq83E(@hKt46zŒ;o+.="4&gln#>T1!5%0^K/UT J-7 VV ;00.N CG@'+Z5!;!532>54.+5!#"!4.+5!#";#=-f->"#=..="T">--=#T#=..="V :0 0: VV :0-7 VV :0b0: VV :0-7 VN,@!_!!@!Zd ]]?2?2]]]103532>54.+5!#";N#=..=#T">-->"V :00: VV :00: V%@@Zd^ ]?2/]]1032>54.+5!#"+BgF%.="T">-H~b#u%Wl-7 VV :0#m.N@@!hI:hI:@HHX9(  ? O  /@/ HB?B@ Z/dAiHX9( @H@): 6] 7%)](?322?32229+]]]]]]]]2]+9/]99]]]]+]]10]]]]]]]]>54!" ;#".';!532>54.+5!#"!, 23#AAE(;m9gZC&->""=..="T">-q&;0$TT1G-QSV*G70: VV 7--7 VV :0N!,@ #Zd"]]^??29/103532>54.+5!#"!2>?3N#=..=#T"=..A*kV :00: VV 7-!5B!HN36v@N'$$ $  & % $ Z/e888 d7)"]  %%%% %&#4] ?332?399]]]]32]9]]]10]]]!532>7#;!532>54.+5! !#";!5%+o%'7"'"=..="#=..=#V 3*F0: VV 7--7 VmV :00: VN,F@*& ),e.d-&"]# ] ?32?322992210]]!;!532>54.+5!4.+5!#"->"#=..="w.="#=.M0: VV :0-7 V-7 VV :03s{'*@[g))@)[ f(#__??]]10#".54>3232>54.#"{TPPT-drrc,,crre-ݩllkk뫘WWVVN#0<@$*[g22_2o22$Zd1^$$0^]!]?2?9/2]103532>54.+5!2+;32>54.+N#=..="?|=9Ҙ.="Dm]T'"LyWV :0-7 V;od[M-7 V'WaVT)s){ 4F@' +[[g66@6![f50_&^ ^ /?3?]]9/99/10;#".'.54>3232>54.#"{Bz 2Nh>!XQY ъEPT-drrc,,crre-ݖ}iU$`+iwkk뫘WWVVNL.;W@3) )  ./5[) =@=/=/Zd<.% ^//;^$]%]?322?9/92]]]9910]]%;#".'#;!532>54.+5! %32>54.+o4bA`{V=#.=""=..="' 5Vl6ZxH"LyWRRV(H:Z-7 VV 7--7 VR{Z</(PxPRsI!b?B@''$Z;gAAA1Zf@6 ,^0'@'P'' ^??]99]10".546332654.'.54>32#4.#"dg4MG!BfH JyZ_^.Cxa\b3WJ6XA;]@" KzYZb4G*Kk@>L@vZ6}8VIC%'YmTX_2&@S-BC0]I.!=V5=]KC$%Re}Oaj7)#[@8Z$%%P%/%?%% #^ ]?2?29/3]]]]9/]2/]]]10%;!532>5#"#!#'.+->"//"=.-=' j  j &>.0: VV 7-q.="XfX"=.5:@% 1e77777777Zd6+' ]( _??3222]10".54.+5!#"32>54.+5!#"yF.="T">-3[}KYU).="">-A3o}-7 VV :0Y}N$0UuF/-7 VV :0fq<f*Q@/,,@ H))*#,,$ ]!??32229/23]9332310]]+]!.+5!#">7>54&+5!#"fF ",3/>= ,," CD/'+#X * VV/3$g[JJ`.5/VV #>2?N8@g/:?:O::71)G''48&4444&:0 &4*&]'88'?3?33/2229/23]9]]]33]3]333]2310]]]]]]]]]] >7>54&+5!#"# #.+5!#">7-'CD/'-$ǽ ".4/>= .)fGJcx  5/VV ">3?{ * VV/3$q]JPco7L@XN/N?NNIIJH554!!" 6 !! ""IIH5564J4 J @NH66:"44-!5I.= :];+.]-?3222?32229/3323]233310}]4.+5!#" ;!53254.' ;!532>7 .+5!#">(1.1y-P' z  AD %7/.V%].+#.  % VV#8*T<1VVD ,"F !$%#VV"6(%;1VV  $'#>>@m&6'7 ( 7Z%55/o/D/9//// M7@?/@o@@@@@60,]-<]?2?32229]]9/]]]]]]23]]]]]]]23910]]]!532>5.+5!#">?>54&+5!#";F/"=.!(3L# $2 !$@;<'(+.="0V 7-) VV  :?{06=A #?--VV%=.0: VJfr@Q  ( 8 H   '7G  _  ^ ^ ?2?2/]3]210]]]]]]]] !2>?3!5!"#!F.>& j C-=& j b.="XR.="Xf@ ??//210!#";Y"=..="YV :00: VN?//103#wyL%@ ??]/210532>54.+5!LX#=--=#XV :0H0: Vh! @  ?299/103# #Gk'V//10!5!@y! //10#.'53N)`ZJ+02SXQ"QQLf)^?D@&:G"UAoAAH6V@ M+1::1'M*N???99//9]21032>=""&54>32;!'##".546?54.-QU>eG&WuH;I)UY:eL^^/1"  54.+5!3>32"32>54.y7je;`M; $#=-.="y:L`32#4.#"3267Df|EE{a@f@\['C4;_D$_#,W?՗݆84O5G:0VA%,lP? (#F8#q-??@%3 $GUAAPA?A=HV@+M,8P .P??2?2?]]]2210%;!'##".54>323&'.=4.+5!2>54.#" .=";L`73r{ 4U%ozAcn;V)MpI5[J9,ZQ`4Kх">{y^ph1(3&G<(7)1C@((,G  @ P  233"M+ N( 1M?2?32?]9/]3210%!532>5#5354>32#4.#"!!3#=-2^TPnC-=% 4',;$!->"VVV :0af\`1&4 +;."CbAya0: /5Wlz@gG8<<2bG* MI?DuG**/**|||?|O|o|mGDDDXG222222 xNISI7hP$pNN ?I@`$$I]N/??99//]992/]]]]]]99/910#4.#"#".';2#"&54>7.5467.54>32>3232>54.+"32>54&#"5 )*#-.[] $"/\Q%A͋*G\3( @F,E/0`a%H=1(2;"-aC%::@")G!U<<<<5G T;/PM'M)?322??22]10%!532>54.+5!3632;!4.#"3L#=-.="y \W\0*:"5V>@]=.="VVV :0J-7 V>=/ai0: VChH%/W{KP-7 /u,:@##JG.-. .(SM M ??2?]]9/]1072>54.+5!;!54>32#".J"=..="n->" **** V 7--7 V0: VV@$0 0$$0 0*8@#!JG,+,,,&SMN???]9/]10+532>54.+54>32#".>(GT= 996M7%M&M?322??292]]9910]+]]]]]]]]+] ;#".';!532>54.+5!>54!"5iEYsTF+͗.="#=-.="y!+ 13GPkRVV )ODAp-7 VV :0J-7 VW*05%8*!VVd[`%@GM M ??2_]9/1072>54.+5!;!55#=-.=".=#V :0J-7 V0: VV9V^Wi@D>GvFGFWFgFFF 2G*UY/Y?Y_YoYYYYYRG TX8P$LPMD0MF2?3322??99?/]9/]]910%!532>54.+5!3>323>32;!4.#";!4.#"3`#:**:"A DMU02ZM=HQY0N|V.*:"3R;@W6*:"3R;C[8.="VVV :0-7 V9I),G39I)/ai0: VChH%1Tn>?0: VChH%6\xBP-7 9^86@'GU::3G T9-PM%M'?322??22]10%!532>54.+5!3>32;!4.#"3`#=-.="K GOY1QY/*:"5V>F_:.="VVV :0-7 V9I)/ai0: VChH%6\xBP-7 q-^#4@ HW%%%@%%/%HV$N N??]]]]10#".53232>54.#"-o{Ao{A CkMMjBBkMMjB'FאEՐru<0-M. P?2?2??222]]]10"32>54.#".'#;!532>54.+5!3>32Pk@AkQD_==a7je;`M;.="#=-.="f:L`=46767##".54>3237!#";2>54.#"D#=-;L`-.="pPkAAlQC`=vV ;0#[*02/K5C֓،C0B(V ;0-7 5loko::pkD^18@ P3303+GT2#P M1M?2??92]]10)532>54.+5!3>32#4&#";#=-.=";' *=ZDpm.H40<&<-.="9V :0-7 V.Q=#NG 6'UK+G\a`'-7 \B\A_@=+(((G=WCC/CCC3G VB3.Mt+f++++#d    ?]]?]]]]99]]]10".54>332>54.'.54>32#4&#"M}X0'-4U>7T99bJOuL&7fWIpL&MGU[4J/=aDQtK$:j7Q6)7 6^G)1E)&702 #ALaCFlI&3E&8Cgr,<$'90-"BNb@PxQ('F#@?!!GON ??2/2]10%267#".5#5267>73!!%> *16NpH#%Y"#+^ UXZ !JzXR#&iOuNib/J18@!(GU3333GT2 .M0#PM???329]10%;!'##".54.+5!32>54.+5!7.=" KT\2Q~V-.="o1S>Da?-=#n-7 V:H).bi-7 V=ChH%0WzJ0; VJ/@%'')))))9)I)))(64 D T  @'H 11@1  & 6 F   **4DT@ H)"M?32?9/+]33]]]]]+]9]33]]]]23105!#"3>7>54&+5!#"#.#>=  CD*" ",VV03#}$WTKAMP%.50VV $>1 * J?@U )9I?&6F!!!)!9!I!!!! 88(4DT@$HA222&262F2222""4(D(T((@H(8!??(,M)!?3?32223/9/+]33]]]]+]933]]]]3]]]]333]]]]23103>7>54&+5!#"#'#.+5!#"3>7  (f CE,#%ޢ ",=> `  D%USJ3cJ;50VV #>3ч * VV$20%XWLEOR%k JH@}?JOJ_JJ@H&D)EEF D332 4 EED3342F2F  HfJ 22+D449 H9++i+++E3,;8M9),M+?3222?32229/]]+2333]]+233310}]]+]4&+5!#";!53254./;!532>7 .+5!#"7>?/1/2--Q& {  :E%822-U.#. j%VV 4)k<1VVD , "##VV 3&N`58VV  ) #>J:@')9I--6--24DT@*H7.+5!#"3>7>54&+5+#$@FSj[hmI!+>=  ' CDJV $>1ckF*a>lV ) VV03##SQH-J.50VVJm@FH'7gw H( 8 h x   IWI V O O ?2?299//]2]2]10]+]+%2>?3!5!"#!$/ V R(4! Vu.=!/R*>)#Tf)H@, $)$ 0  O   '??9/]9/]32/2910".54.'5>546;#";HoJ&%BX33XB%=[MqsvnM[=*LmDd?R1V1R>eXlkdfinXy@o ??/]]]10#3y{{f)P@2# # #@###+ O)??9/]9]]22/291032654675.54&+532+f>[MmvrqM[>&AX33XA&&JnHnibfd^klX>R1V1R?DmL*q' %.@!'  @/]/]10".'.#"#>3232673@?9,+,<: x8_L"D@9*)*96{9]'&-'ty@f@(0%{r@f@VE@,o PWF;) /?9/9/3]]2]]10]]#3#".54>32H^R#//##//#(6  6('5!!5&/U@- %I* &!&!n1'H*O$$+N $ $ % ??99//3232/]]99//332210%.54>753#4.'2>7#H]p>BrXf;v^;[[$8&*UND5[fms1RB/???O??#OJ0-5**5_oSJ#_555.tO//Gt: ?#v??]?9/32/]]]9/99999]10>3232>7#".#"57>54&'#53.54>32#4.#"!cR*I")baY$=6-2BS33bdg7DvB%&K<&:lacV'WK-QAwo \3j>X7 "+6,"' lAUh=T1BC0]I.0\UK`%#7(@.`$)3P  /]33/3/]22/]310467'7>327'#"&''7.732>54.#"#"V/o?>n/V #r)G^68`F))F`86^G)>o.W"&&"Y.o>>o.V &$"V.o>6_G**G_68`G))G` oL{@F% =@@DZ u555NTd 62]D ^ A @ ^=%@ H  4K]?2?399//+33232222/]]]3/]9/]3323910!532>75!5!5!53.+5!#">?>54&+5!#"3!!!;/"<-!*>7b u9D&!#-=#/V 7-a`:) VV&*=# JH> +356[*(VV$>/`a0: Vy+@o   ??99///]32]]10#3#3y{{{{34cMvP@> HP63@ H3&)wPHHHHHe0e_Z@HZ#x>>)@ H)x0#@#P##@3H#FMF   II&>>& M.NU;N.?/9]]]]]]]]]]/+]]+]+2]]]99+2+]9910.'>54.2654.'.5467.54>32#4.#"#".54>3VJ5DsX>x19gm~|KxQsH!D5&&9dQT}S)OB/K7fq)QvNW~R'","-_hO\2'-9Y#D*8))A?B+?+:"#>@Eo^-CCQ;(S\i=Ms%-oJNyS+#=T1B;0ZG+fa6I;8&*T[f<%IC;(d?Qa632#"&%4>32#"&#&&&-;&&&-; , , , 4? , , , 4qR-Ieh@+.X7#".54>32#4.#"4>32#".732>54.#"/P@/ 'MsMfg38olGiF#&3"7)He@n6ahhb66bhha6p.SuXYuS..SuYXuS.m(2 !:0BxgdxC'5$ 7-0^Ynhb66bhhb55bhXuS..SuXXuS..SuH :M@15"<<_<<1%& H( 5.?9+/]]2]21032>=""&54>323#'##"&546?54.6("6%?2C)&NT*H`6DdC!'2% +5@'crX(@1,<$a%4z&3)0#05T=."E^'jgfa;3% H@&       /3/39=/93333]]22103#%3#9ggs9ggJd{{dBd{{d"@/]]3//10#!5{ y3H{qR)6Rn@,  * 07aEp@pppppS7@3 H7')) *6 ))**)**)>ZLh>??9///]]]29322/+]99//29910]]32>7.+5! 3##;!32>54.+4>32#".732>54.#"$$q` 4C# #l#fV7J,/K6P{6ahhb66bhha6p.SuXYuS..SuYXuS.fXH1J6%  E}E,D.-@'hb66bhhb55bhXuS..SuXXuS..Su//10!5!@ybJ')@  0@`p#?/]104>32#".732>54.#"b1Tq@AqT11TqA@qT1{3D'(D33D('D3AqT11TqA@qT00Tq@&E33E&(E33E [@7  _   P` P /]]32/]]/9/]32/]]3210#!5!3!!5!y{y{{o{yzy`yVJ)/@  +""@p'??/]33/3]10!26?3!5%>54.#""&54>324U@)/%F->% .!%4 EO$GjF0TTZ72"+\)TUT("9*1A#"6%?.qR=?W@706;;A!&@p?6O_o  + ??9/]]9/]]32]9910"&546332654&+532>54&#"".54>32d3++A-UahqCC'B26?'4 #9'%IkFDpP+"9L*)VG.Cj=]K-8 ?2S_HUT.A)DG1A#"%?.4K0-G6' .I8Kg?!  //10>73#1/+JZ_)NLQQ"QXSJ7=@&2$G'U9?99999@HGT8-M/% P/??3?9+]210"&'#".54>7332>53;#"&'#9V{) !@9#6% <. /O;9\@#%.oT`7EU?9UlC 'A09^E=Ѐ}PCnO+0WzJ-C,VSQ)D0R'@@   @ H ') MN$M'/32?9//9/+]10#!532>5#".54>3!#";!\#=-ZxH"LxW ">-->"qV :0AoPR`4V :00: VNd @/?) 9  //]]]104>32#".#//##//#(6 6('5!!5{'"@/ //99/]9/10#".'532654&'73'(**%J;BWC+adcm} h>5<<XcL@  ??/10532>5#532>73;_ g40& f^LENFEH  %@ @ ?/]1032654&#"#"&54632FXVGIVVF몥MzV.f+V J@*  0 @ P       /3/399=//3333/]3210 #3#3ff:gg9B0&{'6@#'_o_O]]]]5]]]]]5?550m&{'tT@9,,,,,,p_@;o_]]]5]]]]]]]]]5]]]]5?5R&u'HB@,MAAAAApA AA]]]]]5]]]]]]]5?55h{T%9b@@&0600&I&&&&&&&  ;H  :+5 N/?9/]]99//]]]]]10332>52#".54>7#".54>32}YyI6U;@W6%>--_caxD5f_#//##//#$`nx>5W>"*H_5!2!1T=#2_XTr]'(6  6('5!!5s&$CR0&60 %+5+5s&$vR@ <&q06 %+5+5s&$R@ <&0; %+5+51&$R@ ;&F6 %+5+5F&$jR@ ?&0L %+55+55&$%@55@5 55:0 %+55]]]]55F3;@U(o''87654;;;0Z9$#o222=/=O="^83^00/08080;#/^(($]%  ^?]322?]2299//]]9/2]99//]33323}]10%!2>?3!532>7!;!53267'5!#'.'!!!!#0E.k"<-)q?5#2>54.+!!N.="sZT#=.Czz==yy\/-7 VagV :0SꘘRdN1&1R@ 8&C3 $%+5+5s{s&2CFR(&.( %+5+5s{s&2vR@ 4&X(. %+5+5s{s&2R@ 4&(3 %+5+5s{1&2R3&>. %+5+5s{F&2jR7&(D %+55+553 Z @H7 G W H8HX@$H7GWH8HX `P/]/]10]+]+]+]+ ' 7  ;VTVTTVTVXTRVVXVs{ &16@+ '[g33@3[f2*-"_-_??99]]9910#"&'#7&54>3273&#"4&'32>vzT]BGyhyvP[BLw`-3<^re-B-2_rc,JaЩl#"bAҪk'#ӛT RVSPWs&8C=R6&<6 )%+5+5s&8vR@ B&q6< )%+5+5s&8R@ B&&6A )%+5+5F&8jR@ E&)6R )%+55+55s&<vR'@K&??p?`?0??E.%+]]]]]5+5N*7R@21[g99_9o99+#Zd8"^+7^+/+/++](]?2?299//]]22]103532>54.+5!#"3 +;32>54.+N#=..=#h/">-9Ҙ->"/m]T'"LyWV :00: VV :0>[zFR0: V!OaVxL#/Uk@BG444B8 QLLL_LoL L LBWWWG)%TVaPPPPGM/4'N(M??32??]]2]99//]]10%2654.'.54>34.#"!532>5#5354>32#"#".54>3wKa,N>@bA!)SW>aE>]>}#=-Dw^g,mf2R=Mb80YQHvT.$)/MN\^":66 >L`A>fI(aZ*(IhA\V :0a=q_*IЇSS3//#OVZ.NxP)7Q6)7 6^G)f)!&DC@&F@6(%+5+5f)!&DvP@ L&V@F6(%+5+5f)!&DL&@K6(%+5+5f)&D@K& VV5VF6!%+]]5+5f)&DjO&@\6(%+55+55f)&D@ E&JJ@6(%+]55+55fT^[@GAJH+ ['HWIW]]P]]69H'V\TTTOJOACOCOC1N> +6+6">OP"?32?99//239/999/]]]]9/]3332991032>="!4."&'#".546?54.#""&54>32632!32>7-QU>eG&WuHysz 4U9Da}LDuU0 (K?;I)UY:eLt-rcn;V'MvQ-TI;,X)`_(JjA{#?\Q`4vs.TA&'Q{T{7]C%!{y^ph1'4&G<(q^&Fz 12*%%+5q!&HC*&0*%+5+5q!&HvR@ 6&v*0%+5+5q!&H@ 6&*5%+5+5q&Hj9&*F%+55+55&u!&C&%+5+5/}!&vi@ %&d%+5+5u!&@ %&$%+]]5+5u&j(&5%+55+55q-"6@W-H  W888@88/8#HV70@"2N (N"O???/]99/]]9]]]]9///9910%#".532.'57.'32>54.#"3^QHxU/o{ADt3kN3tACkMMjBBkMMjBK6ug<{Adž DDsb"9Vik66kihi44i9&QP@ D& O?%%+5+5q-!&RC$&*$%+5+5q-!&Rvy@ 0&w$*%+5+5q-!&R0&$/%+5+5q-&R/&:*%+5+5q-&Rj"@ 3&@$0$$@%+]]55+55)V@6"+ Pp@0  @''/]]/]/]]9/3210!5!4>32#".4>32#"&o&&&&&&&-;yG , , + + , , , 2q-"-@@&&#HW///@////HV.%)N)N??99]]]]9910#"&'#7.53273&#"4'32>DGG|3JyoGJJ5Nv#DvMjB/$Z Y;MjBEۘwHߞ }^9J:srlT "&Y28%+5+5/!&X5>&2=%+5+5/&Xj9A&2N%+55+55!&\vq@ G&x;A%+5+5%yMD@(HWOOPO?O>%G4TNCPI;M<*.M- P?2?2??222]]]10"32>54&#".'#;!532>54.+5!3>32Pk@AkQC`=v=7je;`M;.="">-.="y:L`54.+5!;!5J"=..="n->"V 7--7 V0: VV!}!@  H /2+3/10>73#.'#!:9229:P;v./t:PLQQ""QQL"e11e"^J'*@`p #//]10#".54>324.#"32>J&CZ33ZC&&CZ33ZC&o%11%%11%7S88S76T88T6!//!!//@  //102>73#".#"#>32% \4N77YMC!$ \5O77YMC^'/WD((1('/VD((1( b//10!5! y b//10!5! ysu7@$&6 @/?O?]9/]]]104>7#".s$Q[e^"&"M99,'AzhSV \<);>-C=s-@ )9/?O?]9/]105>54.54632$Q\e^"&"M9 8,AzhSV \<);>-C=/@ )9/?O]9/]10%5>54.54632$Q\e^"&"M9 8,VAzhSV \<);>-Csu\/f@E&6&6 (!@@1011111@ H+?32+]]]99//]]]]]104>7#".%4>7#". $Q[d^!'!L9:+g$Q[e^"&"M99,'AzhSV \<);>-C+AzhSV \<);>-C=s'/R@4!)9( )9((1011111@ H+?32+]]]99//]]105>54.546325>54.54632$Q\e^"&"M9 8,$Q\d_"&"L9 9+AzhSV \<);>-C+AzhSV \<);>-C='/X@7!)9( )9((1011111@ H+02222+]]]99//]]10%5>54.546325>54.54632$Q\e^"&"M9 8,$Q\d_"&"L9 9+VAzhSV \<);>-C+AzhSV \<);>-Cd0@ _o@ 0@//]]]]]]104>32#".d)Hd:8bJ**Jb8:dH)TrEErTTqFFq%$@//9=/]2103#9ggJd{{d'(@0@P//9=//]310 #3'gg9??/10+3ۆ!L!G@(!!"# ?!O_o  ?2?9/]332/]39/]32310;!532>=!5334673D)=9PqEEq=4X? R&"V4j   8r NH Fd  .` .*r h   ,   ,D   (  8 \ \ TDigitized data copyright 2007, Google Corporation.Digitized data copyright 2007, Google Corporation.Droid SerifDroid SerifRegularRegularAscender - Droid SerifAscender - Droid SerifDroid SerifDroid SerifVersion 1.00 build 112Version 1.00 build 112DroidSerifDroidSerifDroid is a trademark of Google and may be registered in certain jurisdictions.Droid is a trademark of Google and may be registered in certain jurisdictions.Ascender CorporationAscender CorporationDroid Serif is a contemporary serif typeface family designed for comfortable reading on screen. Droid Serif is slightly condensed to maximize the amount of text displayed on small screens. Vertical stress and open forms contribute to its readability while its proportion and overall design complement its companion Droid Sans.Droid Serif is a contemporary serif typeface family designed for comfortable reading on screen. Droid Serif is slightly condensed to maximize the amount of text displayed on small screens. Vertical stress and open forms contribute to its readability while its proportion and overall design complement its companion Droid Sans.http://www.ascendercorp.com/http://www.ascendercorp.com/http://www.ascendercorp.com/typedesigners.htmlhttp://www.ascendercorp.com/typedesigners.htmlLicensed under the Apache License, Version 2.0Licensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0http://www.apache.org/licenses/LICENSE-2.0ff  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~uni00AD overscore foursuperior4P ,latnkern $O8v088fvZZ$:Hj|$::  " " " " " "vvvvv8888888vvvvZ8:$$$$$$:$:::::: :   $\J\\\\\\   $\ 6\ \ q"&*24789\:q?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ardeixpkvjsgwl|cnm}bɹyqz@EYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"! ,E#F` &`&#HH-,E#F#a &a&#HH-,E#F` a F`&#HH-,E#F#a ` &a a&#HH-,E#F`@a f`&#HH-,E#F#a@` &a@a&#HH-, <<-, E# D# ZQX# D#Y QX# MD#Y &QX# D#Y!!-, EhD ` EFvhE`D-, C#Ce -, C#C -,(#p(>(#p(E: -, E%EadPQXED!!Y-,I#D-, EC`D-,CCe -, i@a ,b`+ d#da\XaY-,E+)#D)z-,Ee,#DE+#D-,KRXED!!Y-,KQXED!!Y-,%# `#-,%# a#-,%-,F#F`F# F`ab# # pE` PXaFY`h:-, E%FRKQ[X%F ha%%?#!8!Y-, E%FPX%F ha%%?#!8!Y-,CC -,!! d#d@b-,!QX d#d b@/+Y`-,!QX d#dUb/+Y`-, d#d@b`#!-,KSX%Id#Ei@ab aj#D#!# 9/Y-,KSX %Idi &%Id#ab aj#D&#D#D& 9# 9//Y-,E#E`#E`#E`#vhb -,H+-, ETX@D E@aD!!Y-,E0/E#Ea``iD-,KQX/#p#B!!Y-,KQX %EiSXD!!Y!!Y-,EC`c`iD-,/ED-,E# E`D-,E#E`D-,K#QX34 34YDD-,CX&EXdf`d `f X!@YaY#XeY)#D#)!!!!!Y-,CTXKS#KQZX8!!Y!!!!Y-,CX%Ed `f X!@Ya#XeY)#D%% XY%% F%#B<%%%% F%`#B< XY%%)) EeD%%)%% XY%%CH%%%%`CH!Y!!!!!!!-,% F%#B%%EH!!!!-,% %%CH!!!-,E# E P X#e#Y#h @PX!@Y#XeY`D-,KS#KQZX E`D!!Y-,KTX E`D!!Y-,KS#KQZX8!!Y-,!KTX8!!Y-,CTXF+!!!!Y-,CTXG+!!!Y-,CTXH+!!!!Y-,CTXI+!!!Y-, #KSKQZX#8!!Y-,%ISX @8!Y-,F#F`#Fa#  Fab@@pE`h:-, #Id#SX<!Y-,KRX}zY-,KKTB-,B#Q@SZX TXC`BY$QX @TXC`B$TX C`BKKRXC`BY@TXC`BY@cTXC`BY@cTXC`BY@cTX@C`BYYYYY-,Eh#KQX# E d@PX|Yh`YD-,%%#>#> #eB #B#?#? #eB#B-,zE#-@V3U3UU?0OuNtN'o7oGoUg@f Fe/e0d@ddU3UU3U@WF0V@VPV?UOUUUUPTTSJHG GU3UU3UUGU??oTS++KRKP[%S@QZUZ[XYBK2SX`YKdSX@YKSXBYssst++++++s+sssss+s++++ss+s++++s++ssssssss+++++NuJfV\huqqVfpuugVfq^odoyHVT1yyTy"KppWP}kT X^:h(R<l4Vp `P\:t  6 |  * rT2| :vTFLnP.\Dd0j L !:!!!"("j"j"#"#$$%%%&J&&' '''( (l()")<))**F***+>++,,2,J,f,~,,-^-r-----.. .:...///2/L/00020J0d0|01111112222233323J3b3|444.4F4\4r445B5X5p5556,6F66677*7B7n7788n8899,9H9{ >@&) 9     ??]/]]]]29]3103#"54632^I>IT}GV}IT 1@4D+4D+?22/]]]3/]]103#3#٣_٤^D\ @K9. + o! D6!$         /333?33399//33333333333333/]]]2]]222]]]]]33/]]]]]]33]]3]]]]]3310!#!##73#7!3!33#)!y{yx{yw  u}wv{w uhuy``yymmyo1^D"'-@v& () !!!"$# """",,  /@H[k{?O$#( WX   @P"!!) P/]22232/3/]3399]]333/]333/]]+33/]399//39333310%.5463$546?3#4'#>54e9yhk!f 'hd1g^׮btL pM[ק " eY $w3 f ",c@<# _(.o. *% ?/??/]3/]99//]3310 #"&543254#"#3 # 43254#"5}JooooŘ+JoooolcElaH&09@X!'/#/ #*12 k    H;5@ H12 --M8'!8%M??3?99/39/+33/+]9///]]9323910!'!"&5467&546326?!#";>54&#" 32H{c֦W*qf\)R'3p?)_O5|ԪѭXzkwPV=9QPVVAoQcrIw@4D+?/]]]103#-٤^VD !@ /??/]3/910$%a^`\) #@  ??3/]3/91077$ jp^ae^]%@R'@ @+P  4D?]]/]]107% %''%h {^%>f w$fJJ%{%1}% P@3 _  ?OP /]]32/]]/]]32/]]10#!5!3!{x{{{yzy9,@$    /  /]/]]]107654'&546329BCF9EZV/}$**>7B[MR{//3/107! !ᚚZ` @)9/?/]]10"54632I>I}GV}HUB @ /?/33/310#3=I R= ,@p@Hp @ H uu??/+3/+102!"52#"THӳv{jOv?NhY@9 H  0H   k ] ? +    s?2/39?2/]]]]]]_]]+]33+3310 ;!7326767##"56?hnmaX#)~6Ræ#&RVVIJVn"|jI@*   /   n@ H tv?22?3/33/+99//]]3310#7>54&#""54632!26?3#+ῚfW7ǹʮ4C_)VR_o˃rzb=\!#)i@@&o  o@ H+  Ht]+#u  t?3?39/]]]]3/+33/+9///]9310732654#""54$32#"&546332654&#jB?ĮݿVFzh֫σw"ubDSp5@\ H     H [k?u  s?2/?339/]3322399//]]]]]]]]33/3+23/+3310!7326?!733#;67!ZaX!G#/13U_/VIJTNs(RV襁jD z@L  o@Hy" Hu  u v?3?39/]323/+33/]+399//]]]]33310'!#654#!632! 54332654&#"F8V^scaإwnN18 Ȱι{q"J@+n( H@ H$ !ttu??39//33/++]339910632#"5!2#4&# 3265#"h/FḘ|käSjOd2g  K@/ ) H)  H v ?2?39/+]33]+]39/]3103!"#!pVX hfr;D*}@O  p (t(p9 "   @H,"p@ H %t H+  H+  t??99]+]+99/+3/+9/]]]39/]999910&54632#"&546%$54&#"32654&ˡpc}bюzûѥQĜtdozosjH)$D@' @H & n@Hz#utu??39//]+333/+3310#"&5432! 54633265#"32);@9aQz iXJv?3D]n#߅mmpBT /@)9)9 @ H  ?//+]3/]10"54632"54632yI>II>IjBCF9E[9}GW}JTV/}$**>7B]Kd@@ H?O@HH @HHP/]3+3+2+2+/]]+]29105 nPHى>@(@ H @?O?o/]/]]2+]210!5!5nyymyyd@$@ H?OH@ H H@ HP/]3+3+2+2+/]]3+]91075 5Pnqu'H -^@<)9@ H  0   ?33?]/]33/39/+]]933310#$4&#""5463 "5463253}@lZ+iI>'''00  :$*4֐  *$/?9/]9/]93//]]3/+39/]]333]339/]10##"&543273325! ! 7! ! #"&#"32o arNR1hC:e4F0pBsVWE1Xe=+FC?M1&tJ\A =liB$@T$ #   }[kN&&   Y i y  ^$##   ]?3222?3399/32/]]]]3]3/]]]]933310;!732673;!73254/&'!{'r0'7J=ɠ 2=0!A/qD.JVV9g @-VVh*jo$s@H![[  &/&_&&$ H_o$^, ]^^ ] ??9/]9/]3+33]3/]9/]910'327654+7! )3 4!#3 4!#}=m{V&RVN217{j1O@5{O_o+; [_ `?3?3/]3/]]]]]]3910# !2##"!21hn[l+1b7{iIVC@*[ H_o^]^ ] ??/]33+3]3/]10'327654+7! )% !#>.B{X(Y{V&RVxhr!i@=#?#!  H _ o  !^/^]]^?3?39/]/]33+3]3/9933310%!2?3!73267654&+7!#7654#!!!{; kZhQ8XAkufXVI@02V?0d"g@:    /  $$`$ H ^ ]" ] ?32/?29/9/3+33]]3/]33910'3267654+7!#767654#!!!;!`TAk\CVBG&RV$" dM!`Vj%f@B!D{O+; '[  $]%%__??39/2/]3/]]]]]]39]33310#"# !2#4&#"!27654+7RLN}z_|stDVIK^$|aLVpF&RV7@X#$0$$ H$19_9909"!! H_o^#/"""""-)]1!0$*5 ]?3222?392229/]32/]33+3}]]9/]33+3}10!73267!;!73267654+7!#"!654+7!#";=bXeff cVTbVXZTbVVIJ+RVVKH&RVVJIb&RVVKH&RV^8@#_@ H_o] ] ?2?2/]33+3]]10#";!7327654+7^bV VJI+RVV&RVTM@ @ H H@ H]^ /?233/++]]33+3]10#"#"'73267654+7TbV6[L3Rw* VJI#\*RV2y@I'!) H)!+444*+++ H_oy))+*) /"]%2 ] ?3222?3229]/]3+33]3/93+3310'3267654+7!#"654#7!";#"&';!cVTbVX$[5D~0C VKH&RVVJIb+-TTGyVC{, RV>@$o H_o] ] ?2?3/]33+3]3/]310'3267654+7!#"!2?3!`TTaT>6jjVKH&RVVEFH-@TVf$&  H'/_/  H _o#]  +]?333222?39932/]33+33]9/]]33+339]]]]3310!73267# ;!73267654+7!!#";WFn fOѮbVVA@F+RVVHA&RVqVKH&RV%%% @P H  O - =   'o'''@'$ H_o "] %$]?32?3922/]33+33]]3/]]]]]33+3310654+7!#"#;!73267654+7!/bU `Tu&RVVKH31+RVVKH&RVj 4@ [[  __??/]]]3/]102!" 4&# $8YA3`L5B"Y@4[$?$o$"" H_o^ ]"^ ]?2?99//]3+33]3/10#73267654+7!2!#;3 !#)`V ǘI C^l^VJI&RVν*RV+j1!L@. [  ###[  _^^ ?2??/]]]3/]99/310327# &5!2% 4&#  ikJAgch$]AV!"*`L65B$-k@>$")[@ H///%-- H @H %^]-^ ] /?322?9/3/+33+3]3/+993310;!73267654+7! ;#"&''32654&+Z DfO$J:+vy\*RVVHA&RVZJi9V]`~m(C@$Z @ H &**&^#^?3?399/3]3/399//+31032654&'.54$32#4&#"#"&546a ԵrozSYza[_~|axvWc^wGUs|@M?V  H Y:)  j Y +    ^ ]?2?9923/3/]]]]]]33]]]3]33+23]33]]10%;!73267#"#!#654+//gPĒ;jVBj &RVVGBqXfP-^)g@A H+#_+%% H% _#o##%#  _]?3222?9/]33+3]9/]]]]33+3107!#"!267654+7!#"!"&547654#T`X'&`X]5`VVIJ-vIՔ/%RVVIJJSc&R!!d@>HXh H#y&  ] ?3?39222/]]]]33/+]]3933]10%67654+7!#"#.+7!#"LZl'r/'EE2V3?-/BRQɱF-IVVFY?=/VV71##g *@L&% ((%$   ,!!%$$ ](! !?3?39322233/]33333/]]39/]]333]]]3310]]654+7!#"##.+7!#"67367{'s1'BK0\ٻj2D-/1VRCZ?N%IVV@_?o75VVh*qʦoɟ6@s..-!!" / .,,!"""""F"v"IY""0V8_8$4 3)%]&".!,,,5& ] ?3222?399//99222/]]3]3/]]399//]]]]]]3310#";!732654';!73267&+7!#"654+7!8YW}-`=F'RyCiF*u+/`%tNb`<]TmVV)*cF[57VVFM!hVV9^'Z+3V-@U)))))&)))))' iy ''%%%u%h%+%%,] '!-"]!?2?39222/]]]]]33333/]]]33/]]]]]310#"67654+7!#";!73267.+7gT\1KbL''MF#P//gPP-7V7(Od&dB5VV?\%RVVGByA+V B@" @ H ^ ^?3/2?3/2/33/+3993310 !2?3!7!"#!#6kV#6jVbXRXf V,@ @ H ??/+]3333310!#"; X`WXVHK%RV#@@ H?//22/+]3103#wXy @  ??/333331073267654+7!`X`V#X}VJIP%QVy!@?/]]103# #(Hk'FV//3/10!7!By5!//10&'73=nap5;T`V4c%IX^A@(/I/N  Q??3/]/]]3]9/]10#"&5!2#4&#"325ZcSTJ &R pT>RmTX"-t@L,# ""<L\+ ///@/(I/Q M #,"%N *Q??9?9?/]]]3/]]]]33310##"&5!26?654+7!327#"54&#"327 }_sVyC:Z%/lf$5ű1^bE`VVh5AbQqփuxj!^J/@^;F3 " n0]?X:C銨눨!*rSqZ*]@8@ H, ,@,*''' /%M'* QQ??9//?/]333]3/+3331032327#"&54?654#"#654+7!ת%5H;H%tUe'/^r n yP$-HA{\b/VVkVOGvjZ`Vd0@0@`Q M??99/]33310]7!327#"&547654#D;X%J_- VQh532R_3M j+Rj579#aVٷ3\& @ H  % H%%((/((@.H[k@ HN"v"NTd?3/]]?3/]]/+]+3]3/+399//+33104&#"#"&54633254&'.54632aW\kfkI8{o^qe۰g|cMD`/nЗ}jBEIe4FbpRFL@,_o H@P`   P Q??32//3223]3/+]910%#"547#7233#32h#}q^5|mSLbM_NRuv:puJ(l@    @9H @ H * *@*$$!!!!!!$ !QQ (M??3??9/]]333]3/++33310!3273327#"&547##"547654+})di%kƍD:I%M_ on%PJ=}/nRh-?_5VbV:Z\7V=\u@N  dt ? _  k{_-=M PM??3?333/]]]]]33]3/]]3393310.+7!34#4632!"'73 >=+J eP6lUHX%<1VroER%guJH@(o @H@ H PP?33?33/+33/+399//]3310%26?3!7!"#!h1Q-VPGM-VRFu>j/RDb#T+(@  ??9/99/10"&54'565!3#";'==dVe$X&'X%@o?O??/]]]10#3{{!.@ ??9/99/]10325475&54+53 +=='򜋇b'&^XV'@@) @ H ?OP   /]33/]3/]3/+10#3232673#"'&#"y^k=96{'av:<:'ff|qlu#X C@)    &6 ?]/3/]]]]29]3102#"546#3I>Ib&_X}GW}JT99!@F ' @H # o ?3/?3/99//33333333/]3/+339/]]33310%.54?3#4'67#6!f tcSXq%fCܬ kK>Q7%O?}b{3t@<22*((5 "1///""1 ,^*% ?3/]]3333?3?39/333//]933/9993993/339/10632327#"'&#"'7$4'#73&5432##"!TI7%2'2 09 9#''#  # #->--- 0*%7O*N?3/39999/]33/]399//]3/333/]39939910732654&'&547&54632#4&#"#"&546654'&t}g1J\c_dS)ѝI$RdyNLӑxlAxf|ΦpSfxTJDspaPkeLLiAF-ÿ  4% ----++31)3< 33   3 3 ?/99//]]]3333339/]33/]3/]99//]33]33993310 !  ! 3254+5! 3##;!32654&+q5FH6yvzeep`)6mdgVgZ\kPEF57xy HXHH=!E}HEPd[K@ @/]]/3/10!5!@yJ %@_  ?3//]3/102#"&5462654&#"း~OnoNKoo nNPnmQNn [@9 _  ?O P /]]32/]]//]32/]]2]210#!5!3!!5!{x{{n{yzy`y)JJ@+   @ H ?229?33/+]23/]399//33107>54#""&54632!2?3) >ong>[ >F|}M F9J=[qwkX7,MelWa~T+P=#&Y@2 %(%?%%H I% "?33?339/33/+]33/399//]]]9923103254&+732654#""54632#"&54ZKbXDDa^$׶JKeETT|``VIefU4 VHo!//10673#ufR|J(k$$@3H@ H* *@** % Q'Q" ??333??999/]333333]3/++3339107#"5477332673327#"'##"!q=W&j<,D#f dM[:}mPu)A"E^bF@" H ?     /333?33/]3/]3/]39/+310#!53265# )#";!mcE> bFFbqVLGlVKHHKVLf @)9O//]]10"54632;I>IL}GV}ITH'  /3//3/310#"'73254'73JZOJFCN`F `@wh}f LB@)  ];K ?23?3/3/]]]]]]333107327#73273;^U}f,fH^LENNFNZG4E D '@ / ?32/]3]]3/310"&5432"32654꺂?bgc| wv^펑1 -@ O @ H  /32/+3/]10 #3#3{f+f{g+f{B{t&{'=d@DP/{o_/o_O?/]]]]]]]]]5]]]]]]]5]]]55?55s&{' tqh@HpP@_/o_O?/]]]]]]]]]5]]]]]]]]5]]]]5?5&&u'VRD@-333p3P33((((k([(K(;((]]]]]]]]5]]]]55?55{#V a@  &6@# H_O_ ?]/33/]33/]39/+]]3333102#"&54673322# J>kN:$YVU`VO%Iq~ә&\j@&?!!-%+]55+55RJ2@0@`Q M??9/]333]107!327#"&547654#oD;X%L^.O}VnPh57B]K#@$   ?/]]107654'&54632BCF9E[V/}$**>7B]K9&@$    /  /]/]]107654'&546329BCF9EZV/}$**>7B[M<@#++  0! ?223/3/]]]10#"&546%#"&546JCBF9C]NCBE:D[V.~$**>7B]K%V/}$**>7B]K@@($/?$   ?32/]]3/]]107654'&546327654'&54632BCF9E[BCD:E[V/}$**>7B]K$V/}$**>7B]KK@1?!$/?$    /  /]32/]]3/]]]107654'&546327654'&546329BCF9EZBCF9E[V/}$**>7B[M$V/}$**>7B]Ku @K{//]]]10 ! ))}w@ /3/103#gفgJdsd3-@@ H//+]10 #3{g+f}@/?/]33/]310!#3?L7|@M   H  H @ H@ H+  ?33?39/3333/]]+33/+]]]]]3992+3+39910;!732?!733673`G)=9U p+PT%4EENqF+X7q/;V4j XN F ! . .)* h    : $2 ,k "  (  8 \> \ TXDigitized data copyright 2007, Google Corporation.Digitized data copyright 2007, Google Corporation.Droid SerifDroid SerifItalicItalicAscender - Droid Serif ItalicAscender - Droid Serif ItalicDroid Serif ItalicDroid Serif ItalicVersion 1.00 build 112Version 1.00 build 112DroidSerif-ItalicDroidSerif-ItalicDroid is a trademark of Google and may be registered in certain jurisdictions.Droid is a trademark of Google and may be registered in certain jurisdictions.Ascender CorporationAscender CorporationDroid Serif is a contemporary serif typeface family designed for comfortable reading on screen. Droid Serif is slightly condensed to maximize the amount of text displayed on small screens. Vertical stress and open forms contribute to its readability while its proportion and overall design complement its companion Droid Sans.Droid Serif is a contemporary serif typeface family designed for comfortable reading on screen. Droid Serif is slightly condensed to maximize the amount of text displayed on small screens. Vertical stress and open forms contribute to its readability while its proportion and overall design complement its companion Droid Sans.http://www.ascendercorp.com/http://www.ascendercorp.com/http://www.ascendercorp.com/typedesigners.htmlhttp://www.ascendercorp.com/typedesigners.htmlLicensed under the Apache License, Version 2.0Licensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0http://www.apache.org/licenses/LICENSE-2.0ff  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~uni00AD overscore foursuperiorOP  ,latnkern O8v088fvZZ$:H$j$::       vvvvv8888888vvvvZ8:$$$$$$:$:::::: :   $\J\\\\\\   $\ 6\ \ q"&*24789\:q?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ardeixpkvjsgwl|cnm}bʹyqz@EYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"! ,E#F` &`&#HH-,E#F#a &a&#HH-,E#F` a F`&#HH-,E#F#a ` &a a&#HH-,E#F`@a f`&#HH-,E#F#a@` &a@a&#HH-, <<-, E# D# ZQX# D#Y QX# MD#Y &QX# D#Y!!-, EhD ` EFvhE`D-, C#Ce -, C#C -,(#p(>(#p(E: -, E%EadPQXED!!Y-,I#D-, EC`D-,CCe -, i@a ,b`+ d#da\XaY-,E+)#D)z-,Ee,#DE+#D-,KRXED!!Y-,KQXED!!Y-,%# `#-,%# a#-,%-,F#F`F# F`ab# # pE` PXaFY`h:-, E%FRKQ[X%F ha%%?#!8!Y-, E%FPX%F ha%%?#!8!Y-,CC -,!! d#d@b-,!QX d#d b@/+Y`-,!QX d#dUb/+Y`-, d#d@b`#!-,KSX%Id#Ei@ab aj#D#!# 9/Y-,KSX %Idi &%Id#ab aj#D&#D#D& 9# 9//Y-,E#E`#E`#E`#vhb -,H+-, ETX@D E@aD!!Y-,E0/E#Ea``iD-,KQX/#p#B!!Y-,KQX %EiSXD!!Y!!Y-,EC`c`iD-,/ED-,E# E`D-,E#E`D-,K#QX34 34YDD-,CX&EXdf`d `f X!@YaY#XeY)#D#)!!!!!Y-,CTXKS#KQZX8!!Y!!!!Y-,CX%Ed `f X!@Ya#XeY)#D%% XY%% F%#B<%%%% F%`#B< XY%%)) EeD%%)%% XY%%CH%%%%`CH!Y!!!!!!!-,% F%#B%%EH!!!!-,% %%CH!!!-,E# E P X#e#Y#h @PX!@Y#XeY`D-,KS#KQZX E`D!!Y-,KTX E`D!!Y-,KS#KQZX8!!Y-,!KTX8!!Y-,CTXF+!!!!Y-,CTXG+!!!Y-,CTXH+!!!!Y-,CTXI+!!!Y-, #KSKQZX#8!!Y-,%ISX @8!Y-,F#F`#Fa#  Fab@@pE`h:-, #Id#SX<!Y-,KRX}zY-,KKTB-,B#Q@SZX TXC`BY$QX @TXC`B$TX C`BKKRXC`BY@TXC`BY@cTXC`BY@cTXC`BY@cTX@C`BYYYYY-,Eh#KQX# E d@PX|Yh`YD-,%%#>#> #eB #B#?#? #eB#B-,zE#-@w3U3UUЖU3UU3U3UU3UUGU?oTS++KRKP[%S@QZUZ[XYBK2SX`YKdSX@YKSXBYssst++++++s+++++++s++++++NuJ``qqXKDDNvL~d 0 @  6 Z   tV^86Tr^~ ,^LJj  r!f""V###$$%z%&''((()J)))*j++v,2,Z--T. ....//0$0b01Z12*2|2233R3344D444455 5L566,6D6\6v66667f7~777778888889 9::::::;;;<<<.>>>>>>??@"@T@@A A A6AlAAB@BCC8C`CCD :@ / _   ??9/9/]]]29310!##"&54>32dw3@ K[.B)#=-3G+HK*D0$87fM@`pP`p@H 0?22/]]]]+]]10!#!#'?Շ^@39@K9. + !o! D6!$         /333?33399//33333333333333/]]]]]2]]222]]]]]3]3/]]]]]]]33]]3]]]]]3310!####73#7!3333#!3# wwyyopsutmn'ffssss3^u2=F@^!./89  BA{9+  (33)O$$H>'>> A/899B.!@ /3?399//]3333339/3]2]]]22]933]]]]]}310#7.5463.54>?3#4.'4.'>Er2r3wm->'\IpM'Czi s#6YA-V/ 1&T0M611L#?0_h8saXHnP4'O]oGU^4 nT*B.%MG9lJ1.0w4H;N#d-A/G[B@"O0Y<@H<]-#H7RC& ???32?32/329/3+229/310!#34>32#"."32>54&4>32#"."32>54&3LiRNpH#2NkSBkK)(C6)(2(C6)'N3LiRNpH#2NkSBkK)(C6)(2(C6)'@{]7.RpA;|_9'LoA7Xptm(Qa7Xqtm)R\@z]7.RpA;{_9'Kp@7Xptm(Qa7Xqtm)R\FV6DTe@=0! PM?@5EE&VMMM7 @ H 56'P?0!@ &&H<?3?39/993?3/+32]]]2]9910!'#".54>7.54>32>?!#";32674&#">VfU|v9HzZ.2>um^Z+*_n#2#B?8E-]B >Z9`?=U5II$>-((?Y8HY:hSfjL!OHXX-)Ha8GkZQ-JKMl6."HQ]Q[6P3;j<6>K71@"`p 0?/]]]]]10!#T?ՇV-%@  _ o  /3?3/]33/3/10.54>7*E2|q5/^My^F3# &?~sc$+o޶"*n||5/#@  ?3/3/]33/3104.'77>7>*E2|q5/^My^F3# &?~rc$+n޶$*n||5T@?fvfviyiy /@H@ `  4?]]/+]]]10]]]]7% ''%'X/'߇1J+T/̒ /:% 4@  P /333/]]]33310#!5!3!lnnn9@ 9/910%7654.54>329qp*8%A0NrSl2x)!4%/EX@ @ H/3/+107!0/Z;@  ?9/10%#"&54>323@ J\/A)#=-3G+HK*D0$8-5@o/?/]3]210#33i L+"@" -@ H%?3?3/+23/3104>32#".%32>54&#"L#GlrXqA"Ejq_p>VH767##"&5>? *8!dyIZ  0QFA!,7#JTa:: . mm;J>f&,#2O7OH $3&j}/8@.. 14D&;K --&&?23/?3/]22/]33/33/10#7>54.#"".54>32!2>?3+hN%6"3O8!3V?#?ytin98om1E0!xdXyA*G36]~I+C-=lQ/(IjBDYt/< %R{FL@'.3 B333))HB??. /  $:$?3?39/]39/3333/33/]39/910%2>54&+732>54.#"".54>32#".54633`J-XAVk<"5%8O6" 1U>$Oh^o<3ggGrO+4ZxExf-wj:X}(QxPv4`Q&A/2To< 2&?mQ.'KmFHsU.LiB[jK.-GX+Q]7!732>?!7!3#;:=:;>6* #2%j)ahh0#TULCm;4}N *  m7D@&$$$93_..... )?3?39/3/]33/3323/]3/]310%2>54.#"'!#>54&#!>32#".54>3CmM*&B[5%<2,V;}*:dy&dKjyBVk\)/H11V{.\\BbB  +;  2a]pP&AW1";-,\L0h*<E: &#@ H##/#O###>22+55?3?39/3/]333/]+33/310">32#".54>32#4."32>54&=?"MR$:cIlȚ]=|}ྌN";L*R\2_J-&k>mV| $@ @ H??3/+3/23/3103!"#!)8H}T53gy'7HS@2 -/### 8)88JB&BB`p5@ HD 2(=(?2?39/+33/]3]3/3]3/]39910".54>7.54>32'2>54.'4.#">l~E>lV,H49}Ōai72[L5[B&Rk6`H*:W:ut(=*0R<#;R24^MV|^H!!Q\f7Mg=-Pn@Nw^K""M[j>pj4v A`A2TKC 0~jx/*G4#A\:jGPZ9s1H:@<<JF-?**@H*2@  %A?3?39/]3/+]323/3310%2>7#".54>32#".5463>7>54.#"KyeT'@LZ5RW-]u\i8  C]wiRvL$UK*A(E;0 )7!:aF'RmNݐ2'3Z}J֙U6gaHT].ck="9L+EI'P@(Z-8<<8Ca=L{OdfZfT#*@%$o% ??]9/]10%#"&54>32#"&54>323@ J\/A)#=-3@ J\/B)#=,3G+HK*D0$84G+HL)D0$8fT*)@!,+&+?9/910%7654.54>32#"&54>329qp*8%A03@ J\/B)#=,NrSl2x)!4%/E4G+HL)D0$8"@ p/]/]29105 nmTߡ:@&pxp/]3]/]]3/]3]210!5!5w\"@ p/]/]391075 5mnբdi!Tb"4,@#++ 60(?3?/329/9/310#'>54.#""54>32#"&54>32bNmI[i9'70E.B{mfr>3@ J\/A)#=-wYyb'!OeT3N62Sl:7aH*(T3G+HK*D0$8X]rm@:^gg Ffff;RR/RR/@ H*alE@ @4Y@K/3?399//9993333/+3/]399=//]93333310#"&'##".54>327332>54.#"32>7#".54>$3232>7.#"JxM[" 6DV6AtX48XzdGe V2V$(TD+U{aj;lSz016auؽo=C{\H;+?,R"4S@/#WYg(F4*VZ7w[7$+ &(% *6 D~ӍG6hꇵJ0> N)H6 +X܅խ{Bf9fm*EV-4Umpk%2u@B%2+$$1++++F94o44P442%11 # +  ?33333?399/333=/3]]]]]933}310;!732>7!;!732654&/.'!d8;)++(++ %\/BN3 ")%8&#mm $>0J 6'mm)-!-OJF%%?AI0^&1:x@F-   '67>54.+7!2#!%32>54&+732654&+ =2%'7 {3ZzG7\B%LݑWyL#fksb^[m0('lT]< 0Kf@pw>}.ZWpmml15#P@H3/333/( ?3?3/]3]+]310%2>7#".54>32#4.#"7GrZD >ytT:nfe2,Mj> %D9QqW; 5B# &3gS3I̓{ִJ#?[7-D/+hZ7>54.+7!2#!%2>54.+ =2%'7 T3c/yR2]RVm0( lHЈzɧxBu$kf1F\.@Q+,?!O!!!$iXP`0# $$ H.# +  +$.?33?329///]399=/]]]3+33}]23]]9/]210'32>7>54.+7!#>54.#!!!!2>?3! =2%'7 XH"7(m/{D0L8' !_im0( l$'!2&{.="Xj.w@C,,iX0+.** H./+++ *  ?33?93399//]3=/]]]33+3}23]]9/10%;!732>7654.+7!#>54.#!!!(7 @ =2% '7 fG$;+w!mm0(5l&#2%yAA@"0C7766"O_C.??@@33 ?3?9/33/]3]29=/333]10#"#".54>32#4.#"3267654.+7!"?3&N>w{HP>tvt9/Pm=9W:)%%.C1 D;&L?33333?3933339/]333=/]]]33+3]9/]]]33+310!732>7!;!732>7654.+7!#"!>54.+7!#";'!>4% bd(7 @ =2% '7 "?4&TV(7 !>4% '6 m0)% mm0(5ll 2(l ll 2( m%D@) H'#?23?933]=/]]]33+310#732>7>54.+7!#";/ =2%'7 "?4&(7 m0( ll 2( m&f@D/((& HF7h Y'&!/3?3393]]]]]]]]33+3]]10#"&'732>7>54.+7!#"zi7K& !$$5[K<'7 "?4&}x: q&Wg ll 2({G@ziH)  !!fG& "  I_IoIII!GF"FF55 H5"444444A==>  "4!G5F ->*..-?3333?933333=/]]]33+3}]]2]393]]]]]]}23]]]10>54!";#".';!732>7654.+7!#" *5 (3))LNU1)+3#kf]A!yL(7 @ =2% '7 "?4&fV%;/$jj*@,9P1m@mUV mm0(5ll 2(#X@3%%" H#"  # ?33?9/923=/]]]33+3]]210%2>?3!732>7654.+7!#"B8R=.%qP =2% '7 "?4&}7T8m7m0(5ll 2(1>@W=> H= K{?@<22 H2111111::21=*>;++*?33333?39333=/]]]33+339/]]933+333310#";!732>7# ;!732>7654.+7!1!>4% '7 F!>3&Z(7  =2% '7 ;l 2( mm0)!mm0(5l3i@B2!! H!3 I Y y  5 H-2! * ?33?3933=/]]]33+339]]333+310!;!732>7654.+7!654.+7!#"F(7  =2% '7  (7  =2% k!mm0(5l)/ll 0(1/(@$  11)?3?3/]2]3/]3104>32#".%32>54.#"1bq͜\/_yљXN+Kf:UnQ5*If54.+;!732>7654.+7!2+bV]P$#>W3-(7 D =2% '7 Jx‡I3ZÄ8eSMlD mm0(5l3eb4tpeM.$<=A@# !!1  ????% *=6?3?3?399/3]]2999/3104>32;#".'.%32>54.#"1bq͜\%Ipq$>S3!\uD]p>N+Kf:UnQ5*IfHDDD% & 99 H9&888888 AA9&81B%%2B.221?333?9/39923=/]]]33+3}]3/+33/99331032>54.+72;#".'#;!732>7654.+7mAqS02L3hCi?(-5#kpJP(7 D =2% '7 &Q\E^;yhc::P1m3[I%%mm0(5lA@@&==+(@ H((CCC0C@C5_ 0#?2?3/33/]3]]3/+33/310".54>332>54.'.54>32#4.#"~?'Ga;(Hd<:Z?!8bMPyQ)Jzrq8~.N<)Q@("DfEO.Pm@0H0^[,!9M,+HDF*,]lQ\o=$>T0S_.dS64O66LA=%T˃mt=(S@2/** H+;[&?23?99339/]3=/]]33+3]]103732>7#"#!#>54&+; =2% 0C1"^GAO(7 m0(R/D-Xs18/66 mB@y Hye7(DDDDD@D:)) H);(((((x(i(Z((( (((C511:)(;2?3?3933333]]]]]]]]33+3]]9=/]]]]]]]]]]]33+310%2>7>54.+7!#"#".54>7654.+7!#"]V4'7 !>4% _Ȃx~A '6 "?4%  }6_N$ll 2(sw=/[T0317ll 0'/,/0gjb.?@ ! "0o0",((" !)! ?/?393333=/3]3/393310>7>54&+7!#"#.+7!#"A  p<;/1+&&Vͬ!+/BN'$_aW>:2#4ll 6,?'ll! V:S@) "'& ++ " 1<  8+"" 9& ?3?339933333=/]33/3933333310#"##.+7!#">73>7>54&+7! ?1.''n#,/BN'8ɖt ").6;/J !;.L/'ll!1,Yb_!I_I<,,*,><=+* #F63 $$#?3333?339=/33/3]3/]33/}310#";!732654&';!732>7.+7!#">54&+7!J25;'K&\=7}%- +"57?(&/03-m,20J"8*k?-mm%"G*&Fmm"8*"*ll;+DlT?P@+#3344 5@A.* 3#45 +=?23?3933339]33333923103732>7.+7!#">7>54&+7!#";7 <2$ ; '\/' J N5%9D+&'(->(7 2m 0(* ll D/DDA)~K!#R"ll 3'? mD@' /?  ?3?399///3]2]]]2210] !2>?3!7!"#! .RD6bf]q#dBN/4U>JZfT^w+3@  {_o;K/ /2?3/]]]]]22310!#";+7X"?6( '4Xl :1 &l(@ @HH?//+]2+2103#ݐnL@  /3?3/33310 !732>7654.+7L}X"?4( &4Xl ;0&lD@?2/]]103# #9XŠcBFV/3/10!7!By!  @//10.'7!?,c\M- '*+PWT%VRHR ^0E]@63273326732>7.#"[n&K\qKInI%%Jmh(SLClm ( 5 -BQ_)-`TCHG(h+.Et@F/O  G?GoGGGGG; < <. ;.K.. H/.... .<;?4?,'4 ?3?3?99399?9=/]]]+]333]3/]310>54.+7!3>32#".'#4.#"32>; */R %P^nDAnO-(Nqg*NC7tsH(&F?5)TD+7#".54>32#4.#"m6WF6 /ci]{H&LqoU{O&<_D&7%,M@1#r0< /(SC+4jkLl@#;O, =16aI*;a}5}R1Xm@> EK KXKhKYKK0 ; K  *   Z;@ H;KWE 0EB66&B&VW?3?3?33399999/+39]]]333]]3}1032>7.#"%3267#".54>7##".54>32>?>54.+7!)-_TCHL%:bO=( ,?-DQ^5-J5 &T_m?OqI"%Ike7#".54>32#*'"32>54&j2ZM= 0ci^}H(Ouq\(o2ZH4 La75/;/%RE-4ikLl?rSsD)w\KxI(KhA?F)3j@5) &&&1211 H541.)2!2 2.! ?3?3?339939999=/+33}33/]310!#".'732>7#?>32#4.#"!fI[jpt7$" 62M=1ϺPvhOyQ)xs $AEmmwN.w "Qfa+Lee20A%DL'I8!psQdv@&EJRR66)Ol@ Hxxx0xt@7 H-JJJJ[@@ H@e&E2VOVVoV6VFV&V&V`; o?32?399//]]]9333/+33/]33/+3]]3/+]393/33/39104>32>32#4&#"#".'#".54>7.54>7.4&/32>2>54&#"?Dw-4:B&?: 3%%#.4PnU  0@(斧U}p57\v?&6T:DLYj-O<#9cKZS($<0$ 965P6:GU"3%=04**.X/-caYD(7  ]l<%Da;6XA, #-3 867 ~:> "2?# 7) 6G"8GLIHCFhu/KB+M@O7  H>t OO+&%%%;K H/>71BK+1&#%/?39?33?39=/]+]333]9/]]33+]310%46?>54.#"!>54.+7!3>323267#"&# "#E@9/" k *+` 'S`qDAZ7 / &" 5$1>M^8u~,zN:??*"/Mchd(% "l>N#)*=Z;&AU/;<<8a+-0E3(ou)#7a@;3o))))WJ9* 98:$)$$$."?2?39?3]]]9=/]]]]]]3333/]3103267#"&5467>54.+7%".54>32m %" 6#1>N]8v~P )%X":+4F&!;-!7FJf8a+-0E3(oc,zN} "m 0 -?) 1"*?))3]@;5O555'h''&o&&& H&'&!2:) ?3]]]?3?39=/+]33/]33]3]10".54>32#"&'732>7>54.+7":+4F&!;-!7Fi^{NA^,70G8- )% 0 -?) 1"*?)n. k #Tf? "m+5@N3((--!&5%5&&%!@ H!!77;K H/&%'(3," ,/?33?39?339/=/]+]333}]3/+993}39310!>54.+7!>54!";#".'X *+l?2.Gc!>6.iKvZ@yh "l QrrD!"ggS[HX2m/I4f"C@*?$  (H Z N = .     ?3?3=/]]]]]]+q333]10%267#"&547>54.+7! 6#1>M^8v~! *+ %E3(ocXH "l8a+-0Ldv@jW W--)-- H-- !c   H j!t!!d H!!54.#"!4>?>54.#"!>54.+7!3>323>323267fu~#  !A>7-     "C>8-!sͬ )%'(U^i=@V4 &Q^nB?V6 / &" 5$1>M^pc+zN:??*".Kafc(VITXK6 W7$>P-=\=&AU/;<<7b+-/F3(L?^Go@@1  H8 II%$$$h H18+54.#"!>54.+7!3>323267#"&!! !#E@:/" hͬ )% 'S`qDAY7 . %# 5#1>M^8v~X:??*"/Mchd(%/ "m=Z;&AU/;<<8a+-0E3(oR^+,@O  -o--(@ H"?3?3/+2]3/]3104>32#".2>54&#"R$InsXtD"Gmt`r@7YG4!SH4XG6%TMj>1hrJl@5k?g6yj >54.+7!3>32#"&';u + "C?7-!N &-7_M<(N )%5#JZrLCoN+#Fij>k( )N*O>&'BV]^)  A50$ 60$?3?339999?3/+3]9=/]]]]]]]]]333}1032>7.#">767##".54>3273;!)?>9/% JF1=dP;'8  #J[vOAkL+'Jmb-VNByf2;Mh;R4%AXdk4R"@i/8=FNCmN*0^\PtE  C:%!mLu^$D@"&  "" ! H! !  ?3?3/?99=/+333}3/]3104&+7!3>32#4.#"!>2:%+ #GUlHmj|v '@@=8.Nˮ &!mJpL'RNjh.K5/RqK78#^C`@>0EE?EE&@H?:::@ H:#????5!* ?3]]]3?33]]]/+]33/+3]3/]3210%2>54.'.54>32#4.#"#".54>3*H49W<7#72>733#)0N"/"KXe;8aH){,hmm0w5{ 8 N4';Z=,.-7iDnO-I34uBJAi@>*,-,,/**I*;**** ***C:J -& *,4=,?33?39933=/]]3339/]]]]]]]333}10%4>7##".5467>54&+7!32>7!3267#".-WbrIBZ6 J 2:% &2-_WDm1 %# 5#2>M^8D[8 @hK)%>S-3z4i*D)m<<6-5QMf8a+-0E3(5G!o\%}@2'''i%%$ ) I  ''''/'@!HkXK:,  %?3??9/]]]]]]+3]]]3/]39]33]10].+7!3>54መ#0^ Fb:N9\N 8*4Unur/q * mh3H?E@Td.H3P.L^B@QDh"w))))( x0("H""H00 "6DDDDODf@HP H))(@(0! ?3?39??9//+]+3]]]]]3/]39]]]33]]]33]33]10]4.'.+7!3>?3>54.'>32ZgPΈ%3 ! 3fd`, ZvE%)4DKjp?x+gT/:! mf˱)N_Qq3#ox30#'7#ZJ'Z@3&%%##))?)''?O"%?223?333/]2]2/3]3/3]3/9939910#732>7.+7!3#";!)//8&$,%//5$ӕ!$,sm 4*T'0 m{m !9-+8! mbo^5@ 777$$$@yH$$$5555555y55  y  5 7777/7,, HXK:,  50)?3??399/]]]]]+3]3/]]3/]399]]]]]]]]]]]]3]]+]10].+7!3>54>32#"&'732>7A< =lQ/C<,<#SU6dZJ}i;o+W?^u7q=/m(kk]R;EH(A.\RP}hiG#s2UrADJJ@/ @H/O  @ H   ?3?399///+]3]]+310%2>?3!7!"#!#6) wXR)8+#}ZoR!5B!/hV0D)LkP+7s@B%./$//$ [{N%$)/.5)) O5/2?9/]99399999399/]]3}3310"&54>7654.'7>7>;#";璒=7J+3[I4 J7>?.547>54&+7Փ=7J+3ZI5 I7#".%7-);;8d27C/%7/(<:82zL27C  -l  !,67X3@ /   /?9/9/]]29310!34>32#".}bw3@ K[.B)#=-+3G+HK*D0$8-8s@>,- 21) )$:..'.w..1- 2,  ?3?399//333333/3]29=/]]33}3102>7#7.54>?3#4&' ;^K: .`e#%Qf:#Fif!!MoG"<_C&&55,K>0"D0< /&PC- ?h^GpK &:J( =1Jz#Tu> Edz~zQo@=PFF(FFCCCSOLL6390 *09994**Q33OF>66I>*)#?32?3?39/9333/]3]2999299]3]310>3232>7#".#"'7>54&'#73.54>32#4&#"!X(LnG6X'.b]S #6/.APal33aen?)E??"!-E3gS4LxW[/|PFgnl ?gWK" gD>+$l,SjN"A }&j2lt=#=Q.X]327'#"&''7&732>54.#"" {f{-g;9i-f}# xfy.l;;k-whw<%?V13V?$$?V31V?%;m-{f{!!h-i;;g-yfy#!whw\s1VA%%AV13WA%%AWFI@T7"3##"";IIxK{JKEB,))*7"@ HO"""C*?33?399//]+]33333339933339=/]33]33}33333910!!!!;!732>?!7!7!7!.+7!#">7>54&+7!#"'6 1#7 <2$ }%/:.( %  *(.")/Y5xwyb mm 0(iywx#) ll K0Ax*N, %P"llNR(@O ??99//9/]310#3#334-TUkb@7NdY#&QgPI`II50mmm?&& @ H\ Nd#5Y+dYdY:+/2?399//9999/3+2]2]2]22910".54>332>54.'.54>7.54>32#4.#".'>54.]g7&:D;U65U; X^2#B91?",3>B91?(6"79BN89(6"79BN8b5cf@>aHW>RHRHR**/?e6C\MCCCCMMMMCMCM#/#/?3?399//]]33/3]]299//33104>32#".732>54.#"2>7#".54>32#4.#"6ahhb66bhha6q.StXYuS.eXtS..PA. &MsMfg38olFjE#%3"8)He@hb66bhhb55bhXuS..SuXe.Su:(1 !:0BxgdxC'5$ 8-0^X+@8@7766B7,,1# :?3222/]3]9=/333310"&5467##".54>32733267%32>?.#"DK;DO-3K12MgP7IHFL+- +6Ae  :2( 1 3":.# 70@0N8#=R00jg\F)*C51""- 1KV&#;LPOy? 8@ 0@    /]]3/]3]2]2103#%3#wvmVffskVffs%%@ /3/]]210#!5%%nXb)6Rl@V 0* /aSaEEE/E?EEnS7') *6 ))))**)**)ZfZLf>?3?39///]]]]33333333/3]]299//]33]2991032>54.+5! 3##;!32>54.+4>32#".732>54.#"$$p` 4C# "m#gV7K,0K6P{6ahhb66bhha6q.StXYuS.eXtS.fXH1J6%  E}E,D.-@'hb66bhhb55bhXuS..SuXe.Su@ @/]]3/10!5!@yJN'#@ `p#?32/]32104>32#".732>54.#"0TqA@rT11Tr@AqT0.=#$>..>$#=.@rT11Tr@ApT00TpA#=//=#$?..?>@  P /333/3/]]]3333210)5!#!5!3!nlnnn;J;+B@&)**/-$@ H $$?23?3/+22/]3]]2]]2107%>54.#""&54>32!26?3;/>T2 !.#HT,SvK@gH'GtW-01 P9J-SPQ+&"8I(:.*F30D*+OUc>2"+;=7Ax@L:===H94Cv''***@ H:9'I' ''*''/_oK "/ ?2?39/]]]99]]39/+3]2]2]]]29/910"&54>332>54.+732>54&#""&54>32h)8 (@.%A0-G0DF+N;#%)!1#K[,SxKIkF"&E_8^q>m=YM+?3!)@-4&`6O3)42@$-2$@03D'6P:& \UEjG%-!@//10>7!#BA=A8GQTR$LQP#18;83JJH@Y322411111g1w11*1:11 11JJJJ$##"" w H 2":CC.?333??399/]+]]]3333]9]]]]]]]333310%#"&'#".54>7!3267!3267#".5##CJT3[~%  %A3/CR*1#"6B 8 +_)y7 &" 5$1>Pb=?S1/M6QB$LHA#9*-D,#8*=}}(--2%6f8a+-0E3(#9K)'1@   ')%' //333?33/29/]310#!532>5#".54>3!#";!">-ZxH"LyW"#=--=#l :0AoPR`4l :10: l2}39@    /3/99/310#"&'732>54.'73)C0=fF W##F8($2RuF-9!B_> y '!L '@ ;[k ?33?3=/]33310;!732>7#"&5>?͒ Th#{:[%04<#jO ZZE9F;* D '%@   #?32/]3]210#".54>324&#"32>2KgOGtR-4NhL@qT1*04S9..7S7+bbZE)!B`?.e`WB'>`?332>72#".4>32#".NmJ[h9(5 0E.B{mfr>3@ K[.B)#=-1Yya'!OeT3N62Rl;7aH*(S(3G+HK*D0$8s&$CR@ 3&8? %+5+5s&$vhRA&ѴB9 %+5+5s&$!R?&-K> %+5+5o&$R>&QI9 %+5+5L&$j)R8&K;A %+55+55&$=@ 888@888==0= =%=3 %+]]]]55]]]]]44#9=@S;%&=&<$#=#  ==#?O##6iX?&6#&9<%$;;;0=8 9,00/?33333?3339////]333399=/323]]29=/39/]33}10#>54.#!!!!2>?3!732>?!;!732>7'7!#H"7(l/{D0K9' !^i =2%1-y85#236%$'!2&{.="Xm0(+C mm'<)3 l}&&z)P?O?7%+]55\s&(C'R@ /&h4;.%+5+5\s&(vR=&m>5.%+5+5\s&(R@ ;&G:.%+5+5\L&(jR@ 4&7=.%+55+55s&,C6R@ &&B+2%+5+5*s&,vR4&@5,%+5+5s&,R@ 2&>1%+5+5L&,jR@ +&.4%+55+55#4@ 034/4  )@:H6/66/  H 4 #/4#30  #?3?39/]3333399=/]]]33+3]3/+3}10'32>7#73>54.+7!2#!%2>54.+!! =2%cX'6 T3c/yR2^RVlym0(} lHЈzɧxBu$kf1}o&1VR@ ?&J: +%+5+5s&2CR@ 0& 5< %+5+5s&2vyR>&8?6 %+5+5s&2BR@ <&H; %+5+5o&2R@ ;&F6 %+5+5L&2jFR@ 5&8> %+55+55Hu //10 ' 7  \h-h--i/gwh/-g/if +98@"0,,;;/!%33%?3?399/]3]3/]399104>3273#"&'#7.%.#"4&'32>1b򐢅aP`/_QA]VbN%kAUnR5V%i>UnQ5!bѽUDE֕^X" }Id`F65R+N s5/Rs&8CRC&ٴHO#%+5+5s&8vR@ Q&RI#%+5+5s&8oR@ O&j[N#%+5+5L&8jTR@ H&eKQ#%+55+55Ts&<vHR@ N&OF,%+5+54Aw@C;!CCCA5))) H5)A)))))3?23?99//]993333=/]]]3+33}]210#732>7>54.+7!#"32+;32>54.+/ =2%'7 "?4&CyI3Ză(7 jT]P$#?W3-m0( ll 2(3eb4tpeM.F m8dSLlEH+d@j3v33g333ZZhZZZJEEEERRRR; bbbP((ffff@ H-_O@ # ?3/3??39?3?3/+3]]33]]9}]]2]2]9/]3]]]2]]]]10"#"&'732>7#737>32#"#".54>332654.'.54>3>54&7U?+ EWflo4(G/ -H;0Ϧmkoo7 u=X9(E4cUAwfM~Z18M0%;*DG "=0`k?gD R'IiCX^jG+ q'U_Ќ?tc,.SvHADC+<%0.19cQ^3:R5(?+0^L/ZJ 9647dSwM$/M"TfR !&DCF&KR,%+5+5R !&Dv@ T&UL,%+5+5R !&D;@R&o^^>^Q,%+]]5+5R &DJ@Q&O\w\L,%+]5+5R &Dj+@ K&DNT,%+55+55R w&D+@ K&tPF,%+55+55Rb8Sc^@2[66%H+CC aa+eOe_e9@ HTMM"3 %"\\ ("> ?333?39/99333/+3]3]9/9933310%2>7#"&'##".54>32>32#*'%32>7>7.#""32>54&-2ZL> 0ci&&N^tKInI%%JmhgGP}\)o)%PKB '1 '3;>CrSuE)w;P18]{D5mhb*">hN#=R^d1*KjA?FR9^&Fzd&@??`?P??U?7%+]]]]]55R=!&HC@ 8&K=D%+5+5R!&HvF&IG>%+5+5RJ!&H)@ D&PC%+5+5R=&Hj@ =&@F%+55+55u!&C$&δ)0" %+5+5u!&va@ 2&3*" %+5+5?!&@ 0&32.'?.'7%#".%32>54&#"R!Bdh3_ A20l;'[N1;bF&#Gls_r?7+>&7XD1UO8YD0?d<Q6Z#1}J6N8lYe95j\Ea<3Unvu2}r3UlspZM&QZ@ S&c^N@%+5+5R!&RC@ ,&18 %+5+5R!&Rv:&A;2 %+5+5R!&RN@8&DD7 %+]5+5R&R5@ 7&B2 %+5+5R&RjJ@ 1&4: %+55+55#;@##"%$@" 0H!"/3+]9/32]104632#"&4632#"&!5!A1**1AA1**1A(n`F7 0##/ 8=F7 0##/ 8*5<@!3(&2&O7o772@ H'2++?3?399/+3]3/]399104>3273#"'#7.2>=".R$InsC4WG6$?Mj>k4jJl@5s6?g6+'V;a:##uB!&XCB&GN8%+5+5uB!&Xv@ P&QH8%+5+5uB!&Xy@ N&PZM8%+5+5uB&Xjs@ G&`JP8%+55+55o!&\vD&E<,%+5+5Vr@AO###X?XoXX -3N3NAA3[@M@/@?@@ @@LN3->@-*  *?33?33993?39?3=/]]]]]333]3/]3104.#"32>%>32#"&';!>54.+7!u + "C?7-!N &-7_M<(#JZrLCoN+#Fij>k( )Nx */R*O>&'BV]^)  D,%+55+55uJ#A@&WJ9* %$"?2?399=/]]]]]]333103267#"&5467>54.+7m %" 6#1>N]8v~P )%Jf8a+-0E3(oc,zN} "mD!!  /99/10>7!#.'#JLPL (-,021DFBKPQ$ PRN.2222.w'$@` #/32/3]210#".54>324.#"32>&CZ33ZC&&CZ33ZC&}!,,!!,,!7S99S76T88T6) )* *Du@  o /3]2/102>73#".#"#>32&{ -CY77XJA!&z /H]76UG?'1kY:(1('.kZ<(1(2}@  /9/10#"&54>323@ J\/A)#=-3G+HK*D0$8o/3/10!7!ݒo/3/10!7!ݒJ@?9/]9104>7#".9qp*7%A1NrSl2x)!4%/EJu*@ @ H?9/+_]9107654.54>32u9qp*8%A0NrSl2x)!4%/E9@ 9/910%7654.54>329qp*8%A0NrSl2x)!4%/EJV15@!(p32-?329/]]99104>7#".%4>7#".9qp*7%A19qp*7%A1NrSl2x)!4%/E-NrSl2x)!4%/EJ1=@!(@ H32-?239/+_]99107654.54>327654.54>32u9qp*8%A09qp*8%A0NrSl2x)!4%/E-NrSl2x)!4%/EV91/@!(32-2239/9910%7654.54>327654.54>329qp*8%A09qp*8%A0NrSl2x)!4%/E-NrSl2x)!4%/E!@ o H //+]10#".54>327Yo8AkM*,QsG>kN,eU%#GkIQ^4#Gl}&@/]]/]]2103#woTffu-T(@/?O/]/]]]210 #3='y@ ??/3210!#39L?$W@/$ #@ H[$k$J$$$$  ?33?339/]]]333/+3=22}310;!732>?!733>73 )):}p ;$" ZZGR4mR ?& V4j  "- N6 F Z . .b* h   D .P , *  (  K 8  \w \ TDigitized data copyright 2007, Google Corporation.Digitized data copyright 2007, Google Corporation.Droid SerifDroid SerifBold ItalicBold ItalicAscender - Droid Serif Bold ItalicAscender - Droid Serif Bold ItalicDroid Serif Bold ItalicDroid Serif Bold ItalicVersion 1.00 build 112Version 1.00 build 112DroidSerif-BoldItalicDroidSerif-BoldItalicDroid is a trademark of Google and may be registered in certain jurisdictions.Droid is a trademark of Google and may be registered in certain jurisdictions.Ascender CorporationAscender CorporationDroid Serif is a contemporary serif typeface family designed for comfortable reading on screen. Droid Serif is slightly condensed to maximize the amount of text displayed on small screens. Vertical stress and open forms contribute to its readability while its proportion and overall design complement its companion Droid Sans.Droid Serif is a contemporary serif typeface family designed for comfortable reading on screen. Droid Serif is slightly condensed to maximize the amount of text displayed on small screens. Vertical stress and open forms contribute to its readability while its proportion and overall design complement its companion Droid Sans.http://www.ascendercorp.com/http://www.ascendercorp.com/http://www.ascendercorp.com/typedesigners.htmlhttp://www.ascendercorp.com/typedesigners.htmlLicensed under the Apache License, Version 2.0Licensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0http://www.apache.org/licenses/LICENSE-2.0ff  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~uni00AD overscore anoteleia foursuperior!P ,latnkern O8v088fvZZ$:H$j$::       vvvvv8888888vvvvZ8:$$$$$$:$:::::: :   $\J\\\\\\   $\ 6\ \ q"&*24789\:qLfpgms#gasp glyfI; |o(head ,6hhea d$hmtxmsTLlocaۈmaxpi nameS{post;4prep! ._<O\YssiS/Z&33f @ [(1ASC@ Ds J '7+3h{fmhRh=hRhf?R%hbhh`hRhhhqhZhjhj%%?hfhfhfh%m}y9}R+H}}'h'`7PRmm3B)J?^qqHq%%+qq1Z!# R=h3hf'hhDh{hhy3dDRhfRdm{hf1=q%#?BT?,hD}9999>R@y/}}}}}h}7?^?^?^?^?^?^^qHqHqHqHqoqqqqqhfs  mRRff??NRNR  x ~1    " : D 1    " 9 D  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ardeixpkvjsgwl|cnm}bɹyqz@EYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"! ,E#F` &`&#HH-,E#F#a &a&#HH-,E#F` a F`&#HH-,E#F#a ` &a a&#HH-,E#F`@a f`&#HH-,E#F#a@` &a@a&#HH-, <<-, E# D# ZQX# D#Y QX# MD#Y &QX# D#Y!!-, EhD ` EFvhE`D-, C#Ce -, C#C -,(#p(>(#p(E: -, E%EadPQXED!!Y-,I#D-, EC`D-,CCe -, i@a ,b`+ d#da\XaY-,E+)#D)z-,Ee,#DE+#D-,KRXED!!Y-,KQXED!!Y-,%# `#-,%# a#-,%-,F#F`F# F`ab# # pE` PXaFY`h:-, E%FRKQ[X%F ha%%?#!8!Y-, E%FPX%F ha%%?#!8!Y-,CC -,!! d#d@b-,!QX d#d b@/+Y`-,!QX d#dUb/+Y`-, d#d@b`#!-,KSX%Id#Ei@ab aj#D#!# 9/Y-,KSX %Idi &%Id#ab aj#D&#D#D& 9# 9//Y-,E#E`#E`#E`#vhb -,H+-, ETX@D E@aD!!Y-,E0/E#Ea``iD-,KQX/#p#B!!Y-,KQX %EiSXD!!Y!!Y-,EC`c`iD-,/ED-,E# E`D-,E#E`D-,K#QX34 34YDD-,CX&EXdf`d `f X!@YaY#XeY)#D#)!!!!!Y-,CTXKS#KQZX8!!Y!!!!Y-,CX%Ed `f X!@Ya#XeY)#D%% XY%% F%#B<%%%% F%`#B< XY%%)) EeD%%)%% XY%%CH%%%%`CH!Y!!!!!!!-,% F%#B%%EH!!!!-,% %%CH!!!-,E# E P X#e#Y#h @PX!@Y#XeY`D-,KS#KQZX E`D!!Y-,KTX E`D!!Y-,KS#KQZX8!!Y-,!KTX8!!Y-,CTXF+!!!!Y-,CTXG+!!!Y-,CTXH+!!!!Y-,CTXI+!!!Y-, #KSKQZX#8!!Y-,%ISX @8!Y-,F#F`#Fa#  Fab@@pE`h:-, #Id#SX<!Y-,KRX}zY-,KKTB-,B#Q@SZX TXC`BY$QX @TXC`B$TX C`BKKRXC`BY@TXC`BY@cTXC`BY@cTXC`BY@cTX@C`BYYYYY-,Eh#KQX# E d@PX|Yh`YD-,%%#>#> #eB #B#?#? #eB#B-,zE#-@ `@+ F3UU0U0o0`@8F/?O_o@FQ_+s@ F+|@ F/?O@ F/@|P|t t0ttt oooouooKo nnnnKnU?gg/g?gg@fPfff?eeedd@Od Fa_+`_G_P"[[T[[I[;[ZZkZKZ;Z3UU3U?WW/WVFV FTF@mT FRP+?POP_PHHHeHVH:HGGG;G3UU3UUGU+oTS++KRKP[%S@QZUZ[XYBK2SX`YKdSX@YKSXBYss^stu++++++++_sssssssssss+++++_sst+++_ssssssssss++++_s^stsst++++_sssstssssststt_s+st+s+_sstt_s+_sstt_ss++sts+st+ss++s+++sss+^ NuJU}qq}}winfoxG:x Zy mmm{TooFxJ&V 8rtF"Jf " ^ P  \ $ 6j|X,B2Phd h`<z&>.n&TT v!!!"l"#Z#$$F$N%%6%%&&&''B'|''(B((())))))**(*****++ +8+P+j+++,,.,F,`,-H-`-x---. ....///2//00$0:0R0j0001D1Z1r11112D222333233344P44445(5\566~667,7J7:@ H@  H ?//+3/2]10+#34>32#".Py3"./""/."&5!!5&%5""57@# / o  ?33/3/]]]9/10#!#J)s)-)r)3@X!   P P  H   ?O   /3?399//]332233]22/]33/3/]9293/92393/3/10!!#!#!5!!5!3!3!!!?RTRNA+RR%TT#@}TTHPPH{-6?@34/))/!!p/<753.'4.'>2]T2f`T !W`e/YV*1[OdCB8JX[.+F3][(B1YSFrT7 !BUnJCoS5 *)ZBSkH!7-&b$9/&qYf3 ';?]<>@3<><>(2#(AA  0?>%7!-????/]]99//881032#"#".54>3232#"#".54>32 #GPPG$JsOIpL&#IqNKqM'GPPG#JsOJpK&#IqNKqL'՞,JHlv??vllu>>uJIHlv??vllu>>uJm}!S@M'JI,IH G6AGB B6B6B;54.#"2>7%4>7.54>32>73#'#".!4$;V8/B*Vd:bTH }4P7#B`}(MoG<-2^XS[02Tm<`+" )5A'1`l|Nis="AAC%#>@F)$=,Y(6!?HU86[A$NzdV*$MWc9KwS++SwK@m]O$73#.R$JqN%GjENqJ$1}]2w^Z=@ ??]210#>54'3$KqNEjH$NqK$1|Z^w]Rw$@?2/]/^]]10% '%7++wo`f`Fof )@   ` ?32/]]22]10!5!3!!#}}{?y 8@ +  @ H_ //]]+32]]]10%#>7j'/36z|{8=}5RBy@ @//]105!RѨ5@ 4Ddt H//+]]]]1074>32#"."./""/."o&5!!5&%5""5@ ?/382/8310 #!Jb'&@o))o  #ss??/]]10#".54>3232>54.#"3qvs93o~wt:BkMMlEElMMkBݱfffe貖KJᗖJJ5@!@n~ @ ??/^]]]3/3]10!#4>7'3ǰ`+baY"y{+`#<@ #o%%"o! " s"t?2?39/]3/3]3/310)5>54.#"'>32!p^KvS,"?V5_Ef(\jvA`l;5]K}QL;Z? M54.+532>54.#"'>32.StGAʊmUW]\W)5bYQ~U,$B\8kJ\&]n}Fln8`IxX9 `t@"-.2(JlCDa?(Jf=4R9C6}6)6a? N@, Vn  w_ t??39/322/]3]]9/]33332/]210##!533!4>7#?հ]{  eHH0d8{uf"11.*N@&o,,'$$(h#Y###@ Hs't$s ?3?9//+]3/]]333]3102#".'532>54&#"'!!>!cHDŀ3c[R!!Ybc*O|V.??9Z7' i7lir~C $ %NvQ 9]q +?7@ 1n "AA;o 6u,s's??9//]2]2104>32.#"3>32#".2>54.#"q5\ƅ./+#X+ZdC* 9L_;_l;>tfdJn#####  h88Y8(888H88C&CVCCC-s;s??9/]]]]]99/]3/]]]]2/]]99102#".54>7.54>32>54./">54&5TqB(F`8:oW5Cyfnu=-Lh:1V?%Cr DhHFkH$'If?~j}#>W30U?$~,XXClWEL_vI\h86e\Kx`JIZmBWX,5Y?##A\84TH@<Tje9R@34BT6ejj)=5@9o??/n   4u*s%u??9//]3]210#".'532>7##".54>32"32>54.5\ƅ..,#X+f+ 8L`;_l;?sfeJ%.;rjrDNG(TWFoN*/K`0CkBf'>@)))) 4Ddt@  H#/?/+]]32]1074>32#".4>32#"."./""/.""./""/."o&5!!5&%5""5'5!!5'%4""4?f a@/"""" d t P D ;  /   +  @H_ /?/]]+32]3/]]]]]]]10%#>74>32#".j'/3"./""/."6z|{8=}5'5!!5'%4""4fN@0@@o0 Pp?/^]]]q33/]]29=/33/]]10%5 d!ff\@= @ {hB9/o/]]3/^]]q/]]]]]]]]3]2105!5!fdTffN@0@@o0 Pp?/^]]]q33/]]39=/33/]]10 5f dBlfX%%';>@!2(('F F=/= -7Q?3/2/^]9/]9/3/1054>7>54.#"'>324>32#".'B20D+9U8SF?Qa]h86P64B&"./""/."%9\PM*)CEO50O94"*;3`WCiZT/-C?B,&5!!5&%5""5mJWho@?X`'''FF'N1 j@j;@NN, [d@6S@EI/3?99//^]^]322/]]q9///]]10#".'##".54>3232>54.#"32>7#"$&546$3232>?.#"%9La:-I4!6GY5MwR+;ob-ZRE"+.F/V{ZO=wod+V؂fv7jeU7N2M*Je?>}qaH)2A#%B18eVezD`5D(=hNݘOoR&,fEeՅw-SsE :^x@$FFII@ H/@_ H?2?9/9+/83^]3/8+]q39=/99]]99]]3310!!#3 .'ߢg;Dj4 Z$*[pg1111$Zd0 #`y $`"`??9/^]]92]]]9/]]q210!2#!32>54&+!2>54.#ÃB'JmEEyZ4A{oTrF XwI !K|\'Wg>lR7 -OxVdm:J;Y;xh(He=8^C%}#L@@H ` p  @ H %%[f$!_ _?3?3]3/+]]9/+]10"3267#".54>32.k{C;vvYN'NUa;LWlON?'QډۖN#ln,* . &@ [gZd``??]10#!!24.+3 `_B~uɢ ^\ՊC$ B@&g  Zd _O _ _??9/^]q229/]10)!!!!! =< p@@8 H  / Zd _?o@H@H_??9/++^]q2^]3/+]]q9/10!#!!!!}+7@++ )Zg--[ f,+_$_$_??9/]29/10!#".546$32.#"32>7!7pvKV_ oXH$SX].zB7x,I>73 ii,*Qډ؜V  =@# Ze   Zd _ ?2?39/^]2]]]210!#!#3!3պfVhRd W@& + { T + ; K   Z@ H  ?2?2/^]+]22_]]]]q10)57'5!df))ff)h)H{s/@`p/Z    _/?/^]3/]]]10"&'532>533L"N-%K=&;i{ 2XD^ie1 d@- f   /Zd  H@ H ?3?399++2]]]3/8^]33/839]310!##373=yr%3#@Zd_??]]31033!Ǻ=/@69 H9Z@ H H eO  @ H&  Z d H  H ?22+2?33+322]+^]]]993+3+2]+210]]!##!3!#46767##EAJI?9XJw4=GIQ@)(Ze'   Z dH  H ?22+?3+322]]]]2]210!###33&'.531MLA9LLJ CC> }q'4@ [g)))p)/)_)[ f(#__??]]]]10#".54>3232>54.#"qQ훣LLQ4krrk22jrrl4ݩllkk뫉ۙQQۉڗQQ3F@,[(8Hg@Zd`0@` ??9/]2^]]]]10+#!232>54&+37~Ϙj~3232>54.#"q1_]+Zyg3)LLQ4krrk22jrrl4݃ⵄ&^54.+d 1Qh7Z~Q%)SW\W]>q\#EgEHd@h3B@'Y##Zg555`5?5*Z f4*'_$ ` ?3?3992]]]3]10#"&'532654.'.54>32.#"EsoA"W`f2Iz]YU)@tawJCAXzFsT[\/aj7#"xp6PC?%#ShTX_2-#+q`9SC;!$L`~^@2  O  0 Z@Wgw@  H_??2/+]3/^]]2/]]]]]q10!#!5!!q^_/@ZeoZ d_ ?2?]]]10#".5332>7BɈąDYR(LrĐRMzH6bQ l@ `p@ H/@ H H @  H ?3?33++?3/83+]3/8+]39=/33103#3>7'*.Ja[JJa*߶HH@HHH@/H%D%%$%D%T%%% p@ H,o,, ,0,,@ H H %% H% H%?33++3?333++/83^]]]3/8+]q39=///]^]q3+3+3+3+3+3+103>73#.'&'#3>7)   ~  8pi^&&Zcg1rJ3l/7437/p6\.cb[&%blo1` @  7  8p@ H   /  @(' ?2?399]]/822/83^]3/8+]q39=/3]33]3/8310!# # 3 3`ZLN[{/L7s@  @ H@@/OZwO6?3?9]/^]]]92/8]]]33/8]]]]]3+]10 3#3TBB/R 8@ g  ? O f __?9?92/2^]22/10)5!5!!TM:ۑ9&@??/]210!!#39k1!?///8338310# J3$@`p ??]2103#5!!3jϕ)%?/3/103# )f%d!NH//3/10!5!NR! @ _/]/10#.'53x#RM?+.0SXQ"QQL^^#2T@)G#U44o40H @ H H V3P*R$P??3?9/22/++^]2210!'##".546?54.#"'>32%2>=%!BN`?EtU07Q4SB@Jdfa0/=hL+ZzI a-A*'Q{TECZ70"(8)Yb&MuOc 9Q3\V?/8-HW11@ I%GT0*P  P?2?3??22+102#".'##33>"32654&^m<32.#"3267ReJLfN268<:Q66{?Ֆۉ>"  %q04@&GU22.H V1+P P?3?3??]2210%##".54>323&'.53#%2>754.#"T;M`<]n<32!32>7"!4.`nHBxecn;L3WQL'(MQW`r 9XJ҇֕NGnq ۜDqP,p@N`?OG/ OP ???^]32/^]3/]322/]9/]]]10###5754>32.#"3-U|N;c'/I((:&?KD`kT# 0SAh%^?R^@ 2SG7/`7p777/7/'HYG@M H  0@`````@'@ H'2 7.5467.54>3232654.+"32654&#"&/_],!)8]Q$A͋kj5'BW/*6@E+G12ba%O@;aH7ZA#L?)\lcdgidcjJq#mEL^5  (!/Pm=Xa4*PqG<[B* R5=Y*?Q`3Yb4 %@.sl.:! ,M`spow{tx2@GU` G TP ?2??322]10!4&#"#33>32\ipQnC ER\0Â4f`2+?*3u%@  GTS??3/22]10!#34632#"&d=-'?,-=J)<6 +:98u!.@# #G  T"S P??3/2/22]10"&'532>534632#"&B0?6#.#"Hm=-'?,-= 'A3M{W/_<6 +:98^@ D@ H/ G T @ H H ??399++?2^]3/8+]333931073 ##3V%om7i%RZ6d@ GT??]10!#3d^,e@?# G   g w  G,U... .P..GT-#P( ?22??32232^]]]]9/]]]]210!4&#"#4&#"#33>323>32diIfAciMh? BOY.x&IW`2Â/[XÂ4f`J+?*X^/D-3^0@GU` G TP  ?2??32]10!4&#"#33>32\ipQnC ER\0Â4f`J+?*3q-^0@HW!@!!!!H V PP??^]]10#".54>3232654&#"-C}ogGC|ogG'ՑLLՉӑKKӈ?^06@.HW22& G T1 P +P?2???3222]10".'##33>32"32654&;`M; :M`<^m<754.#"".54>32373#46767#5LiAAlQf]n<H;?hK)9GX^3_QJ+P=%Z?^5H@-%GW7?7_777,G V6&)P," P?2?992]2]]]310#"&'532>54.'.54>32.#"?:m`m;LTY,A[95\HHsP+7dVaH?AGfb8^FHqP*-PxQ(#");$212#4./7#".5#5?3!!-*# (04>jM,Ni?  Ne}QNabJ0@GU`G T P??3?3]210!'##".5332>53u ER\0[\/joQnC+?).bi=4e`:Jm@ H H H@ HP/O@ G  ??39]/8^]]]3/8++9=/3+3+10!33>73w  ǼJ!hl``lh!cJ/ù/@ H/ H' @ H  H  H@ HT''@ H[  H'  '-.H.@ H...1 1011@-  'fv?33]3?3]33/83^]]3/8++39=///+]+]3+3+3+3+3+3+10!.'&'##33>733>73   翃  Ĭ   h-24:>?:2j%J-ig[Wa_!k"\_XWhm/H#J @ 6 9k{W:JdtX5E   6@H@Hk{W:J  0        ; K (    ??/]]]]]]]^]]q]]]++]]]q9=///]]]]]]]]]q3]33]]]]q10]] 33 # #u3fL J"d"H@ H$$$$P$$/$O$@ "#P?2?333/83^]]]3/8++9=/331033>73#"&'532>?  ǼNAVtP4L@#0F4%9J(XXR#Va^!c'QZ1 ,@)R5J l@  H@ H ? _   HH?@ HO HO?2+?2+/]+33+]]3/+3+]310)5!5!!5 }D='@@% '#  #_)??9/]]9/]332/210.54ᒑ4>7-A(M_6}}6_M(A-wssw0=# !GnNNgVVgMNnG! #=0i{ zj-@0@p@??/^]]q103#閖3)@@% $$$# ??9/]]9/]332/2104675.54.'53"5>5wssw-A(M_6!A`>}6_M(A-;jz {iL0=# !GnN4H-VgNnG! #=0fJZ#<@ %%   @H  ? O o  /]3/+2/]]10.#"563232>7#".%7-)<;8d27C/%7/(<;8c27C !,l  !,l ^A@ H0@ H /^]//+3/2]10+3##".54>32y3#..##..#H&5!!5&%4""4%Z@%F %'@'H 0 @  s!s@ H??99//+33/^]]29/3210$#5.54>753.#"3267vnLWb45aVH.58<;Q6 KljˈK !  %D#(u@ o# H@0 H**!@ H)!u /"""""""""ts??9/]323/+3]3/++399//^]32102.#"!!!!5>=#534>jBB8K0RY@+ )DaCՉDW_2{#7@#. !p99 $@1 H8  ) 3   ? o  /]]]2/]93]23/+]2]3/]9/]210467'7>327'#"&''7.732>54.#"#b/l<7.54>32.#"#"&'532>54.'.7>54.'-:KU7dVaH8AGcf9_FHqN*)4EL;l`l;LTY+E]73^LIsP)?eH#)!AlR/&)3S@-&rT=bD%( ';9.,/ANa>4UD1&mNGoM(! '3--1>NdY%?:7 $.8"&@;9-:3 j 5@! @P 0  /]]32/^]]104632#"&%4632#"&38('::'(8w8(#:&(8s6015522560 &522dD%AUj@C"""&L444WB& /`p-G;Q-?/99//]^]/]q99//3/10"32>7#".54>32.4>32#".732>54.#"{=^@!=_C69815<#fe36id?;>4a6ahha66ahha6meꅅeeꅅe,SxKNxR+  BzgexC!ha66ahhb55bheeꅅeeDB-N@/-///O///$ `  .-'?99/]2/]]2210'#".546?54&#"'>3232>='/8#+H4c=80Z*03u<}w3D)2*":+R# 3M3flH9d$jz:9+3-,A,1Rs `@ P`   @! H    /3/39=/93333/]]+299//]310%R5uu6tt)NNNNf9@$yVK8 ?/]]]]]]]]]10#!5RBydD:N@}R  E---P;/`p&@4J&??99//]^]q3392/]q99//]^]q929++]10]32654&+###324>32#".732>54.#"H[OSYF-9C5*! _騞6ahha66ahha6meꅅeeꅅeHEJ;0K9( nW%G8`}ha66ahhb55bheeꅅee//3/10!5! {V'C@, ))0@ o #?3/^]]]q/]]104>32#".732>54.#"{2UsAAsV22VsAAsU2{4F((F55F((F4AsV22VsAArU11UrA'E44E'(G55Gf :@!  ` ?32//]]333223]10!5!3!!#5!}}}{1Jm@@ O   @ H@H ??9/+3/+]210!57>54&#"'>32!m9H(B63]-N6RUC;"A@2&^0A!?[92VU[79h0a@<2_222@ H''@ H/_&#, ?3?39/^]9/+3/+]3/9/910#"&'532654&+532654.#"'>32NQEXX(S~VF{9?5bXk`bb\T#/;a3E=DL,EiF#NjjN73#//*?MQ#yLQQ"QXSJ7@" G U `pGTP  ?3???2]21032>53#'##"&'#3djoRnC 0gHj#4e`:ST.*&(#U*6qf7@!0@P    /2?/]]9/^]10####".54>3!fxy=U_m32#"."./""/."&5!!5&%4""4#9@ @ H //9/+9/]33310#"&'532654.'73-1GP.?%Zy9":+all+1# s):?J4@!O@ H 0 ??/]]33/+]103#4>7'3&^J<<8(I`B.@ H!! ?]+10#".54>3232654&#")MmD?jN+)LmD>kN,:KVUKKUVKmSY//YSSX..XSwyywxssTs V@/    @     /3/399=//3333/]]299//3]10 '7'7tt6huu5eN\\NbeN\\Nb?&{'J0@?@@]5]]5]]]55?55,&{'5t3(@@p@]]5]]5]5?5&u'?<@'8p8P88333d3P303 33L]]]]]]]]5]]55?55DwD^';D@2(('F == F@H ''-7Q/3?2/9/+^]9/3/103267#".54>7>=#".54>32P'A20D+9U7TE@Ra]g85Q64B&#..##..#%:[QL*)CEO50O93#*:3`XDhZT/-C>C+/&5!!5&%4""4s&$CR&%+5+5s&$vR@ !&l%+5+5s&$R&%+5+55&$R@ &,%+5+5+&$j!R@ &)%+55+55&$}1@ P@ %+55]]]]]]]55V@* Z$4T  g@  __ _ O    _?3/?99//^]q2/8329///]]]]}32310)!#!!!!!!#V%˺=ul;<}&&z O*$ %+5s&(CR &´ %+5+5s&(v?R@ &J %+5+5s&(R & %+5+5+&(jR@ & %+55+55>ds&,CR & %+5+5Rs&,vxR@ &j %+5+5s&,R@  & %+5+5@w+&,j R@ & %+55+55/]@:[g! !Zd _?o@H``??9/+^]q3222/2]9/103!2#!#%4.+!!3 /_`B~uP %\^`ՊC$5&1R@  & !/ %+5+5}qs&2CTR(&.( %+5+5}qs&2vR@ 4&X(. %+5+5}qs&2R@ (&0( %+5+5}q5&2}R0&1? %+5+5}q+&2jR@ 1&(< %+55+55-{ H@HH H H HH@0H@  P   Pp ?^]q2323/]3333]10++++++++ 7   'i=Bh?fg?i>gf=g}q&1\@:)*'[ g333p3/3_3[f2)*-"_  -_ ?3?399]]]]9910#"''7&54>327.#"'32>\[^Q훽NZa[L^BP.0C0rGrl4jX/rErk2c޷lGNd*k*&N QڊTQs&8C=R& %+5+5s&8vR@ $&H %+5+5s&8yR&  %+5+5+&8j}R@ !&, %+55+557s&<v1R@ &c %+5+53<@![g Zd``    ??99//22]]10+#33232>54&+37~Ϙ~54.'.54>54.#"#4>32+?K?+'F98X=!8eUa5AHL%8Q4+H8?U5)>H>)!W~Q'#"-@($;8:#(DCF*6O?6:C,*>)0SANhU%&Lt^!&DC3&93 "%+5+5^!&Dv5@ ?&39 "%+5+5^!&D@ 3&3;3 "%+5+5^&DŽ@ ;&)32>32!32>7#"&'#".732>="!4.^7Q4SB@Jd+3gal9`1UNJ%'KOU1>"L_tJG{Z4aO=hL+ZzI n 7T3ECZ70"(8U]U]Gnq rs6U;'Q{R\V&MuOc 9QcDqP,qo^&FzB /&  %+5q!&HC(&.(%+5+5q!&HvR@ 4&v(.%+5+5q!&H@ (&0(%+5+5q&Hj@ 1&(<%+55+55g!&CU& %+5+5B!&v0@ &t %+5+5U!&@ & %+5+5%&j@  &%+55+55o-#'9t@F(H# """ W;@;;;;2H V: #!!-P07P??99//]]339^]]9///9210#".54>327.''7.'774.#"326-C}ohG?vif+xZJ(U/FAz;JCoO,"FnKMmF!!GmL=ܘOBww~A;<vQr7{ H,quAݰ8kR2.XUL}Z1&Q@ !&"0 %+5+5q-!&RC &״& %+5+5q-!&RvP@ ,&N & %+5+5q-!&R &( %+5+5q-&R(&)7 %+5+5q-&Rj)& 4 %+55+55f+`@0-"Vf(8@( H'  `?3/^]3/]q/3+3]]3/]105!4>32#".4>32#".f)*  *))*  *)#/ /#!//#/ /#!//s/$-\@;'(%H  W/@////H V.('+"P +P??99^]]9910#"''7.54>327.#"4'326/C}o}bDP?FC|o?q1DP>EK-D'rH-'ՑL5mJHՉӑKlIIцT3џc{!&XC&! %+5+5!&Xv`@ '&W! %+5+5!&X@ &# %+5+5&Xj$&/ %+55+55 !&\v@ /&g#)%+5+5? 18@/H W33' GT2,P!P?3?3??2222]10>32#".'##3%"32654&d:M`<^m<73y3l46j3yDC;;CE"a77a"LQQ""QQLm1@@-?O_0  ?O__/]^]/]]10#".54>324&#"3261#=T12R; ;R20T>#u?12?981?3Q88O33O87O45<<55<<8@#/  @H@ H /]23/++3/^]]10".#"#>3232673(OLF -0h!5J.*QLE-.i!5J#+#5>73%'.46z|{8=|5P %@_ _o?/]/3]10#>7B'/37y}z8<|5?y 5@ H _oH?/+33/]+10%#>7j'/36z|{8=}5 b@H_o_o P`p_o ?32/]33/]/3/]3]]]10'>73!'>73'.4'.46z|{8=|56z|{8=|5 b@H_oP`p_o _o ?22/33/]/]3/]3]]]10#>7!#>7B'/3H'/37y}z8<|57y}z8<|5? ~@Q 0@`pP`p_ _o@ H ?22/+33/]/]]3/]3]_]]]10%#>7!#>7j'/3H'/36z|{8=}56z|{8=}5mF@$/_o_ o  @  H/]]/+]]]]104>32#".$?V21V@%%@V12V?$Gd??dGFd??dRs<@ H?//9=/33/]]+210R5uu)NNRs?@( ??//9=/33/]3]/]]10 '7uu5eN\\Nbh@ ??/82/8310 #h՝+J J F@*  _@ H   /  ??9/^]32/+]9/33210##5!533!5467}y}  oC*c1 %*(n4j  0i N< g < . .D*   h   *  ,;   (  8 \Y \ Ts Digitized data copyright 2007, Google Corporation.Digitized data copyright 2007, Google Corporation.Droid SansDroid SansRegularRegularAscender - Droid SansAscender - Droid SansDroid SansDroid SansVersion 1.00 build 113Version 1.00 build 113DroidSansDroidSansDroid is a trademark of Google and may be registered in certain jurisdictions.Droid is a trademark of Google and may be registered in certain jurisdictions.Ascender CorporationAscender CorporationDroid Sans is a humanist sans serif typeface designed for user interfaces and electronic communication.Droid Sans is a humanist sans serif typeface designed for user interfaces and electronic communication.http://www.ascendercorp.com/http://www.ascendercorp.com/http://www.ascendercorp.com/typedesigners.htmlhttp://www.ascendercorp.com/typedesigners.htmlLicensed under the Apache License, Version 2.0Licensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0http://www.apache.org/licenses/LICENSE-2.0Droid SansDroid Sansff  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~uni00AD overscore foursuperior3O ,latnkernfPjhJ@Fp h " " ( " : ` v | " " ( ( > ` ` *j  hhhhTrrrrrJ. " " " " " " (xxxx *HH,$,)7R9R:f;)<R=)FGHJRTW)Y)Z\)\))))R))-{&*24789:<7 &*24789:<,79;<) ) )&*24@)`)))$,79:;<== = )")$9:<@)`)==) )&*24))) )&*24)) &*24789:<$,79:;<=33$&;<=q$,79:;<=7JR R")$&*2467DFGHJPQRSTUVXYZ[\]qRR $>R R")$&*24DFGHJPQRSTUVXRR>f f$&*24DFGHJPQRSTUVX]ff ) )&*24FGHRT))DR R")$&*246DFGHJPQRSTUVX[\]qRR = === f fYZ\ff) )J)) ) )))[] f fDJffR RWRRR RIRR ) )R))= =I==R' DD"&*-^24789:<=;IWYZ\% DD"&*24789:<=;IWYZ\% DD"&*-^24789:<=;WYZ\'f f DD"&*-^24789:<=;WYZ\ff) )&24))) )&24))$ $,-679:;<=@`$0=DRR R = )")$&*-02467'9):@=DFGHIJPQRSTUVXYZ[\]`=qRR  o oI[] = ="FGHIJRTW== = =I==)$,)7R9R:f;)<R=)FGHJRTW)Y)Z\))))R $')) ,, ./ 257>DFHKNN!PR"UW%Y^(.4>CIU[abe latnopenimageio-1.7.17~dfsg0.orig/src/fonts/Droid_Sans/DroidSans-Bold.ttf0000644000175000017500000012276013151711064023565 0ustar mfvmfv FFTMQ3GDEF GPOSp9GSUBlt OS/2 `cmapۯTcvt KRQ\fpgms#gasp glyf}]p uhead ,6hhea ad$hmtxILlocada"dmaxp nameJ(post;prepeq֊ be,_<O\YwsswygU/Z&c33f @ [(1ASC Ds ^ Ju+-hb ?R!R=\?hXR?=HuNh?h\hNh9hhVhLh7hHh?HuR?hXhXhXf3#w{dwB9HND w w 1^d)jP1N 3BJLVfff)jqqqffybP/Psb P7hhXJuhhRh\hhjd/RhX=dm\hX/;L =qHu\9T . . ZB333333`w{{{{*B6/D w w w w whm wsVVVVVVVfffffqqqqJfffffhXfPPqTRRR?%?bRR w x ~1    " : D 1    " 9 D  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ardeixpkvjsgwl|cnm}bɹyqz@EYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"! ,E#F` &`&#HH-,E#F#a &a&#HH-,E#F` a F`&#HH-,E#F#a ` &a a&#HH-,E#F`@a f`&#HH-,E#F#a@` &a@a&#HH-, <<-, E# D# ZQX# D#Y QX# MD#Y &QX# D#Y!!-, EhD ` EFvhE`D-, C#Ce -, C#C -,(#p(>(#p(E: -, E%EadPQXED!!Y-,I#D-, EC`D-,CCe -, i@a ,b`+ d#da\XaY-,E+)#D)z-,Ee,#DE+#D-,KRXED!!Y-,KQXED!!Y-,%# `#-,%# a#-,%-,F#F`F# F`ab# # pE` PXaFY`h:-, E%FRKQ[X%F ha%%?#!8!Y-, E%FPX%F ha%%?#!8!Y-,CC -,!! d#d@b-,!QX d#d b@/+Y`-,!QX d#dUb/+Y`-, d#d@b`#!-,KSX%Id#Ei@ab aj#D#!# 9/Y-,KSX %Idi &%Id#ab aj#D&#D#D& 9# 9//Y-,E#E`#E`#E`#vhb -,H+-, ETX@D E@aD!!Y-,E0/E#Ea``iD-,KQX/#p#B!!Y-,KQX %EiSXD!!Y!!Y-,EC`c`iD-,/ED-,E# E`D-,E#E`D-,K#QX34 34YDD-,CX&EXdf`d `f X!@YaY#XeY)#D#)!!!!!Y-,CTXKS#KQZX8!!Y!!!!Y-,CX%Ed `f X!@Ya#XeY)#D%% XY%% F%#B<%%%% F%`#B< XY%%)) EeD%%)%% XY%%CH%%%%`CH!Y!!!!!!!-,% F%#B%%EH!!!!-,% %%CH!!!-,E# E P X#e#Y#h @PX!@Y#XeY`D-,KS#KQZX E`D!!Y-,KTX E`D!!Y-,KS#KQZX8!!Y-,!KTX8!!Y-,CTXF+!!!!Y-,CTXG+!!!Y-,CTXH+!!!!Y-,CTXI+!!!Y-, #KSKQZX#8!!Y-,%ISX @8!Y-,F#F`#Fa#  Fab@@pE`h:-, #Id#SX<!Y-,KRX}zY-,KKTB-,B#Q@SZX TXC`BY$QX @TXC`B$TX C`BKKRXC`BY@TXC`BY@cTXC`BY@cTXC`BY@cTX@C`BYYYYY-,Eh#KQX# E d@PX|Yh`YD-,%%#>#> #eB #B#?#? #eB#B-,zE#-@3 P07GW/@P0%5UeWfF@? F33U3U0@`_ 030@`F趴 F@ F/?З/?OόgFt&n6nUg fffee dd'^7^&]\Z[Z&Z6Z3UU3U/ WW+WWV"V2VVUT@!T F SSFO7OFN7N[k{ FH FGF@1F F3UU3UUGU0oTS++KRK P[%S@QZUZ[XYBK2SX`YKdSX@YKSXBYssss+++++++++ssssss+sss^sst+++s++ssssss++++ssssst+s+ss+s++s+s++++sss+++++ssssssssss+^ Nu^{18L+ff6?/!ww6? ^}TLyJx71%UX$^B>p:lR  > f \ 0 r Fz<lT8(@pJn$ph8 ` z !F!F!""#6##$$%&*&|&&'L'b''(F())T))**P*++(+R+t+,,,2,J,d,,---2-J-d-|---...F.^.v...///////0J001 1"181P1h2 262N2d2z22222333333445 585P5h55666:6z67F7\7r778.889~99::::u]@B/?wfxi ` ?//]q3]]]]2]]]]]]]10#!4>32#".3Z0@%#?00?#%@0/A((A/-@**@B/@ _ ?33/]3/^]]9/10#!#))))-@P!   ` ` p  H      T 0 @ [?O   /3?399//]]]]]]]]]33]2233]22/]]33/3/]9293/]]92393/3/10!!####5!7#5!333337#/MNLJ/!MMNN/Ljjiib%3<C@F&.@#@@?@@:0 44+4{444)OE==$=t===@? H-*AA&9.@@@@&%@H%%# &@&P&&&&&:P`@/]33/]333/]]33/+9]]333333/+]3]]2]9/]]]323]]3210#5.'&'.'.54>753.'4.'>5%5ifBpbY+*cjm4 [[-9j_WdeA>'_^. +;84967K`>  &>#L^rHK{[9 (+)#J\rR$ </?0? "-A1 H?@ H: H5 H@$ H H( H H#8.Y""V  !!@#!!).C +=&3"!?/??/99//883]3]10++++++++32654#"#".54>32 #32654#"#".54>32;-21/`2-)V[UW,(UZVX-+p-21/`2-)V[UW,(TZVX-}|{}lv??vllu>>uHJ}|{}lv??vllu>>uR-9I@A'G():::HG#$-&DDDH67;K(( @" HK.G 67??''3-$#G3??3?99/39/3/+3]9///]]]]9]9]10)'#".54>7.54>32>7!%32674.#">aPtxËL%Da<&5 =mZVi<-Lg;#5=)6F+ 9M-73#.R$JqN$HjENqJ$1}]2w^Z=d@  ??210#>54'3d$JqNEjH$NqJ$1|Z^w]?V#@@1?2/]/]]]10% '%7)u!㜉'm)hy9w)hpX @  /3322/222210!5!3!!#ondq? V@A/? < L y   *  &6F/?O @ H /+/]3]2]]]]]]10%#>7!'/36z|{8=}5=V@ ?/]]105!=u9&@/? `//]]]1074>32#".u0@%#?00?#%@0/A((A/-@**@D(@ ?//833/8210]] !D!J?)!@n#n tt??/10#".54>3232654&#")7y|=7x~~>JVjh[[h5I.۱ffgf@~\1%@n `??/]]33/310)4>7'31 NIOP! wN'!/@!n#@P`   ?2?39/]33310)5>54&#"'>32!'+XAjL*YKOP-bvXiv?54.+532>54&#"'>321UsCEٓvZ-dda+VrD%Sbhf\zIai0SG;*ctLl~EoLy[=`xC'($ :Q0-I3!9L+NX#4'/Y= 8@ n u ??39/32/29/]]332210#!!5!3!54>7#=m# -//i 1>B<- *^/V(b@#%&&&&"$! H!{!]!m!!!n*@ Ht %s"t ?3?9/]/+9/]]]+933]3102#".'32654&#"'!!>V^xDJՊ7lcY$#\cd-;94{7 U:plwF  #oylq BL+);M@/" ( !  7/n =7nP`2u*t%u??9/]3/]29/]10]]]4>32.#"3>32#".2>54&#"L;eّ230&U+f+ 9L_;_i8C|nlO)C1Y[.L63KmiпyE Cxf$?->vowEM?`Bk{$:H%3eQ27'-@ s??9/3]/]2]103!!`H!':N@j  %  #JJn00nP@@n######((nP` 66EE4EDEdEEEE-v;v??9/]]]]99/]]3/]]]]3/]]]]99]]10]]]]2#".54>7.54>32654./">54.5[zH(F`8:oV4Hmv~A,Lf:1V?%I|v3L2ih#7F#,H3!9)+9 8+*:,XYBkWDL_vI[h86d[Kx`JIYlAWY,(C0cQ*C903=H8&8#*=/%&1>(#8&?);G@* "('  7n=/n 2u*t%u??9/]]/29/]10]]]#".'532>7##".54>32%"32>54.;eْ230%U,f+ 8L`;_i8C|nlP)D1Z[.L63KFiѾxE Cye$>.>vowFM?aBj|$:H%3eQ2us'0@)))/)?) `#/?/]32]]1074>32#".4>32#".u0@%#?00?#%@00@%#?00?#%@0/A((A/-@**@g/A((A/-A))A?s k@P < L y   *  """"/"?"  `  &6F/?O @ H /+?/]3]/]]]3]]]]10%#>74>32#".'/3/0@%#?00?#%@06z|{8=}5/A((A/-A))AX`@A4Ddt5E&/O:J) 0@/]q33]]/]2]]9=/33/210]]%5 H}X2@ II @/]]3/]]/32105!5!XH'{X`@A;Kk{ 5E&/O:J) 0@/]q33]]/]3]]9=/33/310]] 5X}H=Ju';8@2((' H= -7M?3?9/9/3/10]54>7>54&#"'>324>32#".+D0*:$MOEUf+emp6fr=7S7*5 0A%#?00?#%A0J3SKG&!438%9J:*-#1^V?cUO,!1,/ 3232>54.#"32>7#"$&54>3232>?.#"-\^&C8) 2>K,SX.>tg-`YN #4"LmQFʄ8vuq3^債h6f⁞ l>L?*=' 6;R3_[#//$9iYg}E  ]**6 6\{EȊHfՑK"*1g|ßq=epc'Gc=2Ri3@ &6Fv@?H )9IyH `p@_?2?3]9//83]]]3/8]3]933999910+]+]!!!! .'de{^  ]\D`@Rcd#54&+32>54.#ЌG:S67_G)Fv6:N0ir=S35V@'Wg>lR7 -OxVdm:s*?*TI4J-)C0w#B@+e  !!Z!j!! g%%%`%%[f$_ _??]]]]210]]"3267#".546$32.%Y]0+Y`Yi0^bg;NZmddREsu}A(% lo70':# &@ Zg?Zd__??]10#!!24.+326#ec1]Wr^\zt8H F@*g  Zd _L; _ _??9/]]]]]]22210)!!!!!J W@:g  \d _oL;_??9/]]]]]]]2]]210)!!!!Fw''D@('' %\)`)p))[ f('_+"_"_??9/]]]]29/10!#".546$32.#"3267!D:vNZdu]gD^ft>+\eB[(5  ai2(".GqlH 1 G@+ Ze  Zd _L; ?2?39/]]]]]2]]210)!!!!!65w=B ;@#  @ H0  Z _ _?2?2/]3323]+]10)57'5!gRRRNR9R4@# Zeo '_/]?]2/10]"&'32>5!Ab"%Q0.O;!6IR  1R>~w8 r@N9y  I Y  9IYy di  E U  Zd $ ?3?399]2]]3/8]]33310]]]])!!7!t6zNX-`V@m)@/?Zd_??]]103!!6J H @Y HG6'I; ) H] eX 6 F   '  ^ d  ?2?33/3]]22]933+]2210]]]]]]++!#!!3!!4>767# <P{\V%NLFX^JBJL$T[@, H9I +I H^e  H @H6 F   $  F  @ H ^ d  ?22?33+]22]]++]+]22]]+10)#!!3&'.5!w >RMLA9PLJ CC>w'(@[g)/)?)[ f(#__??]10#".54>3232>54.#"OOOO (S~WYQ''Q~XWS(ݩllkksDDssDDm=@&Z ?_Zd__??9/]2]10]32654&++!!2=wO:٠Gͅ@humh`P?uw/W@8&6&[g1/1?1[f0;K[)  +_!_ /??9/83]]]]99]]10!""#".54>3232>54.#"'OwQir OOO (S~WYQ''Q~XWS(wѬ,mJlkksDDssDD ~@P  ZD   Z d gL;` _  ?3?9/]]]]]]]92]3/8]39/]9]]310]]]32654&+!! !Tpx~O (CW0oX&G8-gdhXyKz_G54.'.54>32.#"CyjT0bee23I-#?Y7.reDAxj5ecd5d-NJG$NS7W>K~[2bo<,, +")9")?74Dedbk8&  SE%924!(Se);>@' o 0 @  Z@_??2/]3/2/]]]]]10)!!! 0@  ZeZ d_ ?2?]10]]#".5!3265 EԏϋH5 ?^?uNrĐRMziQsI"X@+   `po@   ?2?3]/83]]3/8]3]93310]]!!!>79899JP``!!`_Pj6&@m 5)595I5f%v%'%%i$y$($ $&6Fk{ H dt6FV%k{9IY* dt@> H-V%%%%Y$$$$  -556`6p6666688@&5$ { , < L    -?23]]3?3]]]]]33/83]]3/8]3]9333]]3]]3310]+]]]]]]]]]]]]+]]]]]]]]]).'!!>7!>7!    1        16DJF;;EJD81=LTPEDMRG7 3 7GRMDEPTL= |@8    `p o @  ?3?399]]/83]33]]]3/8]3]933333]10) ! ! !cV N^)+q@  0  ?@dtZ0@ H?3?9/3+3/]92/8]]]]33/8]3]]]10]] !!!VNDDP\Z/1 t@1iH  @ ` p  P O f@HO __?2?23/]33]+]]]]3/]33]+]10)5!!!k}s"@??/]210!!#3s B(@ ?//833/8310]] !!!J3 @ ??3/2103#5!!3qT=7@+   ?3//23/39=/3310]]]]3#EDJNH//3/10!5!NRLP! *@E _o_/]2/]/33/]]10.'5!"_\LV+.0SXQ"QQLVu.a@>    @ .FU0O0000&G0  Q:N)N??2?3]9//]]]229/]10]]]!'##".546?54&#"'>3232>5); !BNa@DtU0PHHEcTpe=T3D7*H5-A*+W[ TEB*#/6/A(F;9S6w08@ .GW2&FT1+M  M?2?3??2210]]2#".'##!3>"32654&Vf99hX8WD331 6GZ03G,,I6[UUsJ؎ْJ(3{!M!''#<-%JqK!Q~U,fs<@&   !!/!O!o!GV M M??]]3/]310]]".54>32.#"3267qxIKvVKXBz7oddkWJ%FGMBٗ:*&%-# f=08@  %HU2.GV1+M  M?2?2??2210]]".54>323&'.5!#'#72>754.#"Vf99iX6ZH9 1; 6GY76L/.N;`Z[J؎ٓJ-;#'("M!f"=,%KpK!Q~U,fDs)e@A  %%% HW++0+FV*9I(P|ON ??9/]]]]]]2]]]9/]10]]"!.".54>32!32>7dQk0H xʒQJro}CV%C_=3[VT,(QZhrz3V?$RFבܓJCz@gG& ! )HJ@,  H/FNM ???32/]3/32]]3/9/10+#!#5754>32.#"3Ϩ4`U\~,HD.<1yyRRkT# M7.54>7.54>3232654&+"32654&#"=6jj6 *8QY0Lrr9*FY/)!$6"Xg8lh21* \0N9bg>4#nDEH@?I\3"I*Ud7%%LuQ_k9+QrH=Z@( *3 4-(&rZc4/$XJ?* 5f[dd[Zjj6@!FU0 F TM ?2??22]10])4&#"!!3>32jKN;P011`S`4yy0^Y*]'.,WM/dl/@0` FTS??3/]22]]104>32#".!!-=""<--<""=->1+9##9+*:##:^'E@,  FU))0)`))S### 'M?]?3/]]]22/210]"&'532>5!4>32#".f0f"6"-!1'W6-=""<--<""=- 'A3)Me:k+9##9+*:##:n  H@6 H0$ F T H ??399]+?23/8]]]33399]++10]]7! !!!pXlw1`TRJ @0`FT??]]10)!1s*q@4  Fy8& #F"U,_, F T+@ H'M # ?22??322+32]9/]]]]]]]210]])4&#"!33>323>32!4&#"`HM:M.)CPZ.s+DR[.HMm\yy0^Y^+>(OU+>('yyjs;@$EFU0 F TM  ?2??22]10]])4&#"!33>32jIP

(/dlfds 6@!  G W!_!GV MM??]10]]]]32654&#"#".54>32^ji^^ki]GwoLGxoL1ؔMM،ؓLLws08@ .GW2& F T1 M +M?2???22210]]".'#!33>32"32654&7WD4+6GZ7Wf8:i3G,,I6[UU(3#7;J"<-J؎ٓJ%JqK!Q~U,f=s08@  !&F#U2GV1$" M,M?2?2??2210]]%2>754.#"".54>3237!!46767#Z7K./M:`Z[Wf89iX8ZI8 6GZ%JqK%Q~U,J؎ٓK-;#9 !"=,Hs@@! HFFTEU ?3]??2]]3/+]102.#"!33>  ;cG'-8EWsCmO^+F1bs7c@ !!' HFW9909.@ H.F V8.+N( H($N H ?3+?3+992+]]+310]]#".'532>54.'.54>32.#"@vh7^TN(*]\W%):% .YKIkE"7*L=GrR,LXX,"%!%/"!APgGNuN'..$.,&!'=Nh/L\  H@4 HO_oF _ o  NM??332/]33/2]3/]+310+%267#".5#5?3!!f-Q*+KI~\5X@"UlfA>d^;@#J FU0F T  ?3?2]/]210]]!'##".5!32>5!{)ER\0R`41IP

7!u?@u^9{15w9}s^33333@ H1 H$$$$@ H#### H@ H H@Q H H$$$### 333*& **)** 12222@ H225555@1#  I  +F?33/]3?3]33/83]]38+]39]]3]3]3]3]3]3]10+]+]+]++]+]++]!.'&'#!!3>7!3>7!V   Z/q  zPu  u+?OY,gwwh,ZO@}^'moc=FI@1 \icbqp' X^ @[   iZ9I( FVf'7  @ H  /   @ Hi Z 9 I *   @ HfT6F$  ?3?399]]]]]]+]]]]]]+/83]32]]3/8+]3]9]]]]]]]]]33]10]]]] !! ! !ZZ};#PjP^@.8 7  ) 9     @, H  p  &6@ ?2?33]]3/83]]]]]]3/]]3/8+]3]]]]]93]]]3]]10!3>7!#"&'532>?N PF>֡4L@#0D1# ^4v/8:9u /B)87m^ |@IYiH   @ H   @ HFVf@H@ HNN?2?2/+]3]+]+]]3/+9/33]+]10)5!5!!mVFʹQ(N@2 ( H% H!0''oI!"??9/]]]9/]32210++4>'4>3".5}>aB!&c(A-sz-A(c&oR+D0>JiC  6+ջ# n^+6  CjJ;/@ 0@p?//]103#!(Rس H@) H %(%$o$$$I$$$??9/]]]9/332210++#5>5&675&'4.'523"&c(@-zr-@(c& Ba>}-JjC  6++^n #++6  CiJ0D+RaX'}$\@,(H %  @ H&   H @ H ?_@H/+]33/++]3/10+]+].#"5>3232>7#".%9/*><93N39F0&90*><9e39Fh !,67  -m u^i@J[/?wfxi` /?/]3]]]]]]2]]]]]]]]10_]3!#".54>323^0@%#?00?#%@0^1%/A((A/-A))A)c)@ %%%%++++n@ H**?*!u(v  /]3]/3]+]]29/32105.54>753.#"3267\h88i[&LG?V587B\;rL23|E KljˈK    (SW%RB&@j $$*$ 'Wn!k{:   @`c @w  Gst?2]]?]29/]32/]]]3/]399//]]]]3]]]]]210]2.#"!!!!5>=#5354>nP]Gu?CKN,5*C/?o0"#M_ۏ7Q:(*:R:qd.\ "6@_327'#"&''7&732>54.#"+f36`/},c68c+}53D'(F55F('D36c,*g68b-}}{}[j'E33E'(E33Eb@ O@<  @ n  @  @ H ?3?99//]]3]+3232/]33]2]292/8]3]933/8]3]9310!3#3#!5#535#53!59<\Zݲ/$@0@p?/99///]32103#3#j)CV@51E11&120B00$0:J))@ H  H$@V H$H'D HRMM(MM!!:'XoXXXX@ HDD'DD00?$HRHRHR 05, ?2?299//3333/]3/]3/+]23/]99+99+10++]]]]]]4>7.54>32.#"#"&'532>54.'.7>54.'y%0?F:k[fURDNQJ4Q8GuS.E8>?>renF'Z\Y'8J--QBLxR+qv 4[F %,K?2(vKAkL)/% 3.0*''DSe>d{%(iJJwT.)&# *'&* @Qf[?a3 $, 631$,%%@ #/]]32/2/104>32#".%4>32#"&%33&&33%&43''3>2HH$?]?]99//]]/]]99//]]310]]"3267#".54>32&4>32#".732>54.#"aj`k999vMkk64iiSDJq}6ahha66ahha6`ޥ``ޥ`򔃇D|jg{D,":ha66ahhb55bhޥ``ޥ``/.@ H  H H@D H .00000000@ H%O _  @H .((??2?39//+]3+]22]10]++++'#".54>?4&#"'>3232>=(qD2Q: +QwKZ;6(b4B>[DeB!&/ & 3$n:@7T9F="&$'7 $R^\ [@2 / _      ?322322333]]2233/]33310%R55=wwwwX?@ /3/10%#!5!#l=VdD 4H@W ?@ `  ??'J55     p     ::.DD ?]?]9///]]93/]]99//]]329910###!232654.+4>32#".732>54.#"MB/B9/ `6ahha66ahha6`ޥ``ޥ`^npR9B#. ha66ahhb55bhޥ``ޥ``?/10!5! \'@ )#?104>32#".732>54.#"\6^~HH]66]HH~^6*9 9**9 9*qG~^77^~GH~]55]~H8**8 9++9X D@(   @ H/+]3322//33223310]!5!3!!#5!onq/JR@3 H H   / _ Ee ?2?3/]33]]2+10+!57>54&#"'>32!y.=%0((W5{AmBmM+6T54&#"'>32QY3J1LABIJE&@0p\4@$ 23/T9e>g>jM,Ed *7B$y##(265&'&2&(/>!7!L0/*V *6>?:LQQ"18;82j^9@"B F U0FT M ??2??32]210]32>5!#'##"&'!!KQ:N01+ #iK6Z1yy0^YUU.,*+%T$Jq5@ H   /2?9//]9/+10####".54>3!=U_m32#".u0@%#?00?#%@0/A((A/-A))AK@0( H??//9/]9]/]3/33310+#"&'532654&'73HwW-H%'%+J\N:-9Z>! #%9= "/=\JH7@$/_p ??3/]33/3]10#4>7'%3HNm-J=>4 =9Y H @5 H H H!!!!!!!!@ HO _  ??/]+]10++++#".54>3232654&#"-TvJEuU/-SwKDsV0L7><77<>7\W]11]WW]00]WdeeddccT^^ ]@4     /_   ?322322333]]2/_]322310 '7'7^55#;w\\w9;w\\w9.'&{*@`p`]5]5]55?55.'&{t0@pD`]5]]]5]]5?5Z''u @P0]]5]55?55By^';C@) 2((' H @P` -7M/3?9/]/9/3/10]3267#".54>7>=#".54>32+D0*:$NNETg+fmp6fr=8S7)5 )0@%#?00?#%@0^J3SKF&!438%9J;)-"1]U?cUP,!1,0;V/A((A/-A))A3s&$CR&#%+5+53s&$vR@ &V%+5+53s&$NR@ &*%+5+53`&$NR@ &)%+5+53V&$jLR@ &&5%+55+553 &$LX @4.34!%+55?3/559@%FF Zp g@.H_ _  L ;    __?2?29/]]]]]]2//3]+229/]332399]]]]10])!!!!!!!!#3X[\a\`Nw&&z ,$ %+5s&(CR &%+5+5s&(v\R@  &M %+5+5 s&(R@  &%+5+5V&(jR@ & *%+55+55*s&,CR &%+5+5Bs&,vR@  &` %+5+5,s&,@R@  &%+5+56V&,j>R& *%+55+55/#g@BZg!?!X;Z'd _oL;__??9/]]]]]]]322]2]]]103!2#!#%4.+3#326/ce1]WrRd\^Tzt8`&1R&* %+5+5ws&2CTR(&-4 %+5+5ws&2vR@ (&I(/ %+5+5ws&2R(&/; %+5+5w`&2R@ +&,: %+5+5wV&2jR7&(F %+55+55m  n@D V%5Y  * :  V%5Y*: @ H_/]+]/10]]]]]]]]]]]]] 7   '՗-1-ӕ-+ј-՘w$/2@(%[g1/1?1[ f0'+ +??99]9910#"''7&54>327&#"4'32>OHRa]O[AFP^] /EaWS(+9"P0YQ'ݩl=u^d(k!o`cݸs+DstD s&8C=R&$ %+5+5 s&8vR@ &_ %+5+5 s&8R&+ %+5+5 V&8jR@ '&6 %+55+55s&<vZR@  &R %+5+5mH@,Zg?_ Zd` `   ??99//22]]10]+!!3232654&+m3u6|u9Tyxjsh^OByizlg+Co< H H@7 HF11GHF**9E  GWEE8F9TD 4M?9O?3]??99]]99]]]10+++#"&'532654.'.54>54&#"!4>32+?K?+5R91L5b<ELP"PX)JI>)dalkJusH@aL:0*"(3&AM\<"=>**1"$@?E(5N>45>(?Nahsmj4+SzV!&DC/&Ǵ4; %+5+5V!&Dvm@ /&/6 %+5+5V &D@ /&$6B %+5+5V&D@ 2&$3A %+5+5V&Dj@ >&+/M %+55+55V&D@ 4&%9/ %+55+55Vu4CL@^   G'F55 *H HHH/%WNONoN;G VM59'I'(''PGGG|GGG,(DDO (N>N,N/?2??]3?]9/]]]3]]22]2]]9/329910]]]]"&'#".546?54&#"'>32632!326732>5"!.E+Tb{RD{]6PHHEcTpm~o|CV%C_=^X(QZhe=T3D7*H5Rk/Hei6M3+W[ TEB*#/6Cz@gG&+- /A(F;9S6rz3V?$fs&Fzh@ P((  %+]5fD!&HC*&/6%+5+5fD!&Hvs@ *&l*1%+5+5fD!&H@ *& 1=%+5+5fD&Hj@ 9& *H%+55+55!&C& %+5+5!&vE@ &Z %+5+5!&& %+5+5&j&"%+55+55JH#'7v@Q $  (GW99/9_9990GV88!--N55N_K/?]]]]3?]9/]]2]]]]]10]]]].'77#".54>327.''4.#"326"N*`I9dHlG$HvoLAua` eBe1K3l]1L5j\0"E&jEyܘODzzC?1X8h/VB'?hJ)j&Q3&- %+5+5fd!&RC &%, %+5+5fd!&Rvf@  &O ' %+5+5fd!&R &'3 %+5+5fd&R#&$2 %+5+5fd&Rj/& > %+55+55X+`@7""=M} +- '?_  H//+]/]]/]]]9/]3]2105!4>32#".4>32#".X&32&&23&&32&&23&d*:##:*(:%%:*:$$:*(9%%9fd#,@S''w''#'3''&,< % '$$8$H$$GW._.7GGV-@% H& H&*!!8!H!!M**7*G**M?]?]99++]]]9910]]]]]]]]]]]]]]#"&''7.54>327&#"4'326dGw9h09DEOGx>s31>?F: (9i]%i^1ؔM^ZoJۏؓLO`bHхVA@1Nd!&XC& ' %+5+5d!&Xv@ &P" %+5+5d!&X)&". %+5+5d&Xj/*&9 %+55+55P!&\v=@ &c& %+5+5w$58@  3G W7+FT60M%M?2?2??2210]]>32#".'#!!"32654&7HY7Vf98fW7ZG613G,,I6[UU#<-J؎ٓJ&2 4;yB#%N%JqK!Q~U,P&\j@ .&= %+55+55^ @0`FT??]]10)!1^!6@!) _ o  _/]22/]3]]/33/]310.'#5>7!!3n46h3?A<d324&#"326J'E\67\A$$A\75\E(6**600*68Y>!!=Y77X=!!=W8-33--44H@0opH _ o   G_/]3]3/]]3/]3/]]310"#>3232673#".% (AX5)NMK$$ )BW4(PLKB56PtL% '!46OtM%!'!R?/105!R\R?/105!R\ E@2/)9I v 3 C %     ?/3]]]]]2]]10'>73%'.46z|{8=|5 C@1/ y < L   *  &6F ?/3]2]]]]]10#>7'/37y}z8<|5? V@A/? y < L   *  &6F/?O @ H /+/]3]2]]]]]]10%#>7!'/36z|{8=}5u x@Z)9I3Cv% )9I v 3 C   %   ?32/]3]]]]2]/3]]]]2]]10>73!%>73!(.4'.46z|{8=|56z|{8=|5u x@Z<Ly * &6F y < L   *  &6F ?32/3]2]]]]/]3]2]]]]]10#>7!#>7!'/3'/37y}z8<|57y}z8<|5? @_o 0y<L * &6FOo y < L   *  &6FO_o @ H /+32/]]3]2]]]]/]]3]2]]]]]]]10%#>7!#>7!'/3'/36z|{8=}56z|{8=}5b)G@-`p/O_  `p0H/+]/]]]]]104>32#".b,Mi=;iN--Ni;=iM,VxL##LxVUxM$$MxR^b+@?322]22310R5=wwR^b+@?322]22310 '7b5#;w\\w9w?//833/8210 #+J J \@<5E/_ 6F 2B@H ??39/332/+29/]33]22]10]##5!533!5467}} ᗗAͤ*]1 +-*n4j @~N` g ` . .h*8 h   4   ,P   (  8& \} \  T Digitized data copyright 2007, Google Corporation.Digitized data copyright 2007, Google Corporation.Droid SansDroid SansBoldBoldAscender - Droid Sans BoldAscender - Droid Sans BoldDroid Sans BoldDroid Sans BoldVersion 1.00 build 112Version 1.00 build 112DroidSans-BoldDroidSans-BoldDroid is a trademark of Google and may be registered in certain jurisdictions.Droid is a trademark of Google and may be registered in certain jurisdictions.Ascender CorporationAscender CorporationDroid Sans is a humanist sans serif typeface designed for user interfaces and electronic communication.Droid Sans is a humanist sans serif typeface designed for user interfaces and electronic communication.http://www.ascendercorp.com/http://www.ascendercorp.com/http://www.ascendercorp.com/typedesigners.htmlhttp://www.ascendercorp.com/typedesigners.htmlLicensed under the Apache License, Version 2.0Licensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0http://www.apache.org/licenses/LICENSE-2.0Droid Sans BoldDroid Sans Boldff  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~uni00AD overscore foursuperior2O ,latnkernfPjhJ@Fp h " " ( " : ` v | " " ( ( > ` ` *j  hhhhTrrrrrJ. " " " " " " (xxxx *HH,$,)7R9R:f;)<R=)FGHJRTW)Y)Z\)\))))R))-{&*24789:<7 &*24789:<,79;<) ) )&*24@)`)))$,79:;<== = )")$9:<@)`)==) )&*24))) )&*24)) &*24789:<$,79:;<=33$&;<=q$,79:;<=7JR R")$&*2467DFGHJPQRSTUVXYZ[\]qRR $>R R")$&*24DFGHJPQRSTUVXRR>f f$&*24DFGHJPQRSTUVX]ff ) )&*24FGHRT))DR R")$&*246DFGHJPQRSTUVX[\]qRR = === f fYZ\ff) )J)) ) )))[] f fDJffR RWRRR RIRR ) )R))= =I==R' DD"&*-^24789:<=;IWYZ\% DD"&*24789:<=;IWYZ\% DD"&*-^24789:<=;WYZ\'f f DD"&*-^24789:<=;WYZ\ff) )&24))) )&24))$ $,-679:;<=@`$0=DRR R = )")$&*-02467'9):@=DFGHIJPQRSTUVXYZ[\]`=qRR  o oI[] = ="FGHIJRTW== = =I==)$,)7R9R:f;)<R=)FGHJRTW)Y)Z\))))R $')) ,, ./ 257>DFHKNN!PR"UW%Y^(.4>CIU[abe latnopenimageio-1.7.17~dfsg0.orig/src/fonts/Droid_Sans/LICENSE.txt0000644000175000017500000002613613151711064022125 0ustar mfvmfv Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. openimageio-1.7.17~dfsg0.orig/src/fonts/Droid_Sans_Mono/0000755000175000017500000000000013151711064021262 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/fonts/Droid_Sans_Mono/DroidSansMono.ttf0000644000175000017500000034452013151711064024530 0ustar mfvmfv`OS/2h`cmap}V6cvt DMrfpgms#gaspD glyf"head.6hhea aN$$hmtx[locaBmaxpwH name{'8postP(?prepAڂ~D_< T\Y#s s#R/Zc33f  @ [(1ASC@ s+ J -=')=!uqTTf}!5!f!of\sR`RwNJ+\VBo!!!!!!TTTTTP}}}}!-ssssssRR!!!OufufufufHTsTsTs9fff}}}}}}!R!fffd!-PsF9+!e|z!DfT!qoTf!59J!yyR==sjJ+PZsZB!qTf5y=`Db9#!ssyRJ`}m+q{s#jR#!R{N=wR`JR yJJF}119%P9o197 q\!-BBTs}Rb=b{ \{u9PTs?R?RFPTsRXR^))/=;;+jTsfy!R!R5`qs!!-TTTsTsTsDRRR+#`q5`5`11#+'Py{uq)!!!!U!!!!!!!!YLTsTsTsJJTsTsTsTsTsTsTsTs}}}}}}}!R!R!R#999#*~ #?M   " & 0 3 : < D  !!!!"!&!.!^"""""""+"H"`"e%  #>M   & 0 2 9 < D  !!!!"!&!.!["""""""+"H"`"d%aI1vhcb]gDXz Bvt %"iOSVbrhijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstu45]^   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a rdeixpk vj0-s12gw'*).l|!cn,B/(m}  3:f45y KRqNOPzSQL@EYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"! ,E#F` &`&#HH-,E#F#a &a&#HH-,E#F` a F`&#HH-,E#F#a ` &a a&#HH-,E#F`@a f`&#HH-,E#F#a@` &a@a&#HH-, <<-, E# D# ZQX# D#Y QX# MD#Y &QX# D#Y!!-, EhD ` EFvhE`D-, C#Ce -, C#C -,(#p(>(#p(E: -, E%EadPQXED!!Y-,I#D-, EC`D-,CCe -, i@a ,b`+ d#da\XaY-,E+)#D)z-,Ee,#DE+#D-,KRXED!!Y-,KQXED!!Y-,%# `#-,%# a#-,%-,F#F`F# F`ab# # pE` PXaFY`h:-, E%FRKQ[X%F ha%%?#!8!Y-, E%FPX%F ha%%?#!8!Y-,CC -,!! d#d@b-,!QX d#d b@/+Y`-,!QX d#dUb/+Y`-, d#d@b`#!-,KSX%Id#Ei@ab aj#D#!# 9/Y-,KSX %Idi &%Id#ab aj#D&#D#D& 9# 9//Y-,E#E`#E`#E`#vhb -,H+-, ETX@D E@aD!!Y-,E0/E#Ea``iD-,KQX/#p#B!!Y-,KQX %EiSXD!!Y!!Y-,EC`c`iD-,/ED-,E# E`D-,E#E`D-,K#QX34 34YDD-,CX&EXdf`d `f X!@YaY#XeY)#D#)!!!!!Y-,CTXKS#KQZX8!!Y!!!!Y-,CX%Ed `f X!@Ya#XeY)#D%% XY%% F%#B<%%%% F%`#B< XY%%)) EeD%%)%% XY%%CH%%%%`CH!Y!!!!!!!-,% F%#B%%EH!!!!-,% %%CH!!!-,E# E P X#e#Y#h @PX!@Y#XeY`D-,KS#KQZX E`D!!Y-,KTX E`D!!Y-,KS#KQZX8!!Y-,!KTX8!!Y-,CTXF+!!!!Y-,CTXG+!!!Y-,CTXH+!!!!Y-,CTXI+!!!Y-, #KSKQZX#8!!Y-,%ISX @8!Y-,F#F`#Fa#  Fab@@pE`h:-, #Id#SX<!Y-,KRX}zY-,KKTB-,B#Q@SZX TXC`BY$QX @TXC`B$TX C`BKKRXC`BY@TXC`BY@cTXC`BY@cTXC`BY@cTX@C`BYYYYY-,Eh#KQX# E d@PX|Yh`YD-,%%#>#> #eB #B#?#? #eB#B-,zE#-@o/o3@3U3U3U@h F pU3/@FSJpМ@8 F 8 0@SGŽ@vutPsPoHnGUa`e`d_y?OFO_o[[[Z9ZZ3UU3UQP@ FIF`H;G3UU3UUGU@oTS++KRK P[%S@QZUZ[XYBK2SX`YKdSX@YKSXBYssst+++++++_ss++_s+sss+++_ss+ss+ssssss++++++++++s_ss+s++ssssss+ssss++++++s+ss+s++++s+s+++++s++ QuJs}wUinfoxH/NyL' JxyZmmsexT9zo((((Zl'Bo-JiMUg4z K a 7 o 3 n  B UOeG6o-O,z]M_1hW`bW{:  ^ !)!>!!"("""#=#_###$D$s$$%3%K%b%y%%%&:&L&d&{&&&&&' 'Z'q'''''(7(((()))]))***6*T*l**++2+H+`+w+++,5,K,b,x,,,- ------.A.Y.q.....////>h>>>>>? ?#?W???@.@@A AIAnAAABB8BQBhBBBBBBCoCwCCCCCDoDwDDDDDENEVE^EEF_FFFGG(G>GTGmGH+HHI9IIJAJlJtJKKkKLL^LMMGMMN*NO3OOOPPP.PGPPQQQ#Q[[\\q\]]]]]]^(^0^^^__E__`4`s`a3aab b bybbbbccpcxcccdd:dRdiddddddee&eZԮ] iձ>dx ôڴ !;Tm׵(AYr߶+BXoз4Lmи+AZrֹ+KbuѺ,BXoʻ&9Pg}׼~þgؿOMG '@` p?2/3/]33/]310!!7!!IhyJh 4@ /??]]9/]9/329910#32#"54y3qD.@O 0?22/]/]]]10#!# ))))-@s    @P      ?O? O    ?3?399//]]33223322/]/]]9//////]9999999103!#!##53#5!3!33!!@RTRNARR%TT#@}TTHPPH"'@$$##  $pH&X&x&&n@H@P) n  s&#  _o/?_$s H/+33/32/]3/]3392/]3/]+2]9/33322210]]]]]]]]]]]%#5"'53&&546753&'654ĩBǡQoF\@ E> ZDqNV #+Ϲ" H @ H H H H@! H H Hg****!@H@Hh&&&&!@.H!-h g($ ????/]]+]99//+]+]33]10++++++++"&54632'254#"#"&54632'254#"#'#w/J8v=%-@'& $@0H@$4$$GHO;G 0yG@ H/)H@ HFFV7@7 H!PX X&8'X')'('& O,Q ???9/]9]]]]]9+]]]/+3/3/+]99//]]_]]+]]+93910673#'#"&5467&546326654&#"32ZG!5}كdccQWcˆ]yݦֻfAOwJJ\YQv{i@ ?/]10#))' '@ ??/]3/333103#?Da) '@p ??/3333/]10#3@s wwH$@     ?2/39/]10%'%7++wo`f`Fo3 @  ?32/]3210!5!3!!#}}{?@)p@ H//]9/+]]10#79L#Cy @ 0?/]3/]105!Ѩ-@0 ?9/]102#"54f-@ ??3/33/210#sJ9 6@ oo @ Hs s??/+^]]10"!2%2#"d+n}=; +@ n  ??99/^]3/310!#47'3'Raz+Fy{+-U@o@H@ H  st?2?39/]22+]+]3/]10!!56654&#"'632!-q^|lfv}~qwԳw"_@: o _ o$sh s s ?3?39/]9/]]3]9/]3/910]!"'53 !#532654&#"'632ibr\/O`!}fwy}ƥ=f V@ H n @H@ H@ Ht  ??9/9332/+3+9/]+332210+##!533!4#fհ\{ $6eHH0dfNR@1 @ H o  s st ??39/3/]3/3333]9/+10753 !"'!!632#"PYZ7'kf;`:&9]3!O@@ Hn  #o@Hu  ss??39/]2/+]2]9/+10&#"3632#"!24&#"326G_ _hΪQ4Z hݰ;G@0yGW'(t??2/]2]9/3]10]]!!5!N31)|@  &nn n  +!nH@" H9Yiy* #s#s??9]]]+]/+]]99//999910#"&5%&54632654&#"!2654&'5鲸f~nj}cl~Ϻߡ7gejjeZ}yuZJ3!N@ H o#n  @H u us??39/2/+]]29/+10532##"&5432!"32654&#"Cc `i2󑜣gޮf-@ 0` ??9/]32102#"542#"54fffD@.) 0` /??/]9/]2/]102#"54#67m3}>&f3a@=gwhxv@? /]]33/]]]]]29333/29]10]]%53e5fà3@@' pO_oP/]]]3/]]]3/]32105!5!eTf3_@  ɷ& 6 F  @H        Hy X   H H HG Z`p  @`w&6Vf  ?2?39]]]/8]]]3/8/]299]10+]++]]]]]+]]]]]]+]!##373>yr%3.@@HZP`p_??/]+1033!{q\@ @% H'  H:   ) @M H5& H(    @P Y  ?22]2.3?33/]2]]210]]+]]]]]+]]]+]]]+]]]!##333#4#XJwf!Ds @NHE U e 4  %  HJZj;*  IYF V  ?22]?3]3/]2210]]]]+]]]]+!###3353DBL/e T{ $@ [[/_ _??/]10 ! '2# hyx",/F 8@"[ Z`  `??9/]/]2]10#! !'3265!#jƫ9TTb{U@0 H [l| H[ /  _ _?33/3?/]2/3+]9910+%&'# ! 2# ^Uy\$('y@",/ s@K+ H)   H)    F [@H Z` ` ?3?9/9/]2+99]3/8310]]+]]+]]#! #'32654&##ue夫\aiq\|?8@Z!Z  `_??99/]3/]3/10753 54&'&&54$32&# # ^е@ۃ1VhpMIˢNNf|GXfd<@$ZcP_??2/]_]_]_]]]/10!#!5!!û^_}P'@ Z Z _?2?/]]10# 3! P3'L\}! @g H) @; H&hHX*  GW%@ H?3?39]+/3]]]3/3]]]9=/3]3]10]]]]+]]]]+]]3#367')/>Jaϑc@T)9''&6) 9 (&6Hh)9@!HTEw@ Hx H    i y [ J  g & 6 F  $4d  ('K [   ?3?39/99]3/3]3/]3]9=///]]3]]3]]]]]]]3+]]]]]3+]]]]]3]]+]]]3]]10]]]]]]]]]]]]]363#&'#367= ?F( !˪X".X56JDoil'5 @$    @ H&  H  ) @< H& H )      ?2?39/33/33/]22/39]+]+]+]+10!##33ZKNZ{/L!@$ H&6hxZH)9' H@JHYH{PZcP4?3?9/3]3//]_]_]_]]]9/]]]2+]]2+10+]]]]]]]]+3#3d}/fd D@(&) __?2?2/3/33/]3/310]]]]!!5!5!!d-3@  ??/]]3/]310!!!!Z1@ ??3/33/310#sJ!14p@H ??/]33/+]10!!5!!!Zϕo%^3@ H/]33/23/229=/+33103#of%d!H//3/10!5!+!@ _/]3/310#&'53+yjVXَ{^!B@& !G#HR PN??2?39//^]3]2210!'##"&5%754#"'6323265%M?Ļ˴ĤȓaKLER`knD:@"HGPP ???2?2/]229]10%##33632#"3 !"T %ksQ;%^+@  HQ Q ?3?3/]]2/10&# !27# !2%>vi"#9cj>=#&/:@#GH P P??2?2?/]]2210%##"323&53#54&# !26qt #+ -!)ZbH^G@,HH    PPQ?3?9/^]/]q2q210!!27#"32%!! HPï=LK+ 7#1ZF@( GNP ??3?332/]3/]323/9/10!#!5%54632&#"!{=с{)hg{X?n!`#dshff^$,6@/4$ 'F_8 0?O_+F@ H"%R.)O".Q "2N?2??9/29/99/+]3/]3/]293/10#"'332!"&547&547&&54632254#"#"! 54&fAվ7d`YfV@EdJqU8MT:/kpP%e ƮX=-<@&$4G G P?2??2/^]2]10]!# #3363 wͶ gr2o333@ G V N N S???29/]322102#"54%5!!5%yj?+k`Xr:9sr{E{{V9@ G P NS???3/]9/]329102#"5453265%5!#"kkj u\ZΤrssr)'re9{Av@ )9@>HFV  +; H` G `   ??3993?/]223/]+33/39910]]]]+]]73##3 V)%RZ6+ -@G] O  NN??29/]]33210%5!!5%`Xz{{{\s^ k@EveV7GG z  G   "G@P P ?22??3299/]2]]9/]]]]]]]210!4&#"##"#3363236325>WO}TH C2LtÒrJ3-^6@!G G  P ?2??2/^]2]10!# #3363 wͶ grJo3sX^ "@H HP P??/]10"32' ! b670Η`\D^>@&HG PP ???2?2/^]22q10%##33632#"3 !"T ns!+6d)/^<@%GHP P??2??2/]]210373#47##"3254&# !26y qs)+ -)Zb#^1@ G/ Q ???39/]]23/]10&#"#336632#1{eLv1-Jwf^<@"F!F P `  PP?3?399/]3/3/10753 54&'&&54632&#"#"byƫJ`k*:JrUp' RdJ F@ Hp @ HNN?2?2/]]3/3+]2/3+10!!5!5!!+y}Db@?  p _@      /  ??9/]]]9/]]]]29/]93210$4!5 5%pJRpQKNMKQ) )@ O??/]103#`@>  P O  /  ??9/]]]9/]]3]]9/]93210475&54&'5! 5665qRIq;) )LQLLQJ3Z/@   p ?  /]]]323/]105632327#"'&#"cdGlwcdFlJl?8yl?8^ %@    ?/9/9/329910"54323#fy3H%N@F 0  H@HP Q?3/3?3/3/+]]29/]3210&# !27#5$%53碜>vizN'89bk=;EJwVR@1 o  u/_ s t?2?39/]32/]3299//3210&#"!!!!5655#534632'By!ٵyHىN1ՉD'^@ %@ H O _ o  )@ H"?Oo/]3/+]]]3/+]10'76327'#"''7&542654&#"/berrccCCacrva`AxdecbDD`erx_`BD`_vrdefdN}@ H H@<Hfv t   g n g    @0@ ?3?99//]]]332329/]]]33229]2/399//3/]]]399//10]+++33!!!#!5!5!533dZ.Ҳ-!@O??99///]32103#3#'2}@+ (/-H**I F4G//I& F&&@ H&&3(- -( N N?3?39333/+]33/]23/]999910]]]&54632&#"#"'53254&'&&54654&'Э8xcwVprq+PxHCt6N=>qXPAR9P>ThaLmYo00Ik?J 7@$p  Oo/]323/3/]102#"54!2#"54``````fggffggf ,o@F)#/? . +&!$!o `!p!!!!!! ??99//]]]]33/99//]310 ! 2#"#"&54632&#"32fCAkrܿ~=nSy^hjXWU..Um2>6G@% /0  ??2?39//]33/]]2210'#"5467754#"'63 32655!hު}p)#rxhi}vtg 3n:D?4KZn[s G@*  `  @0`   ?322/]3]]210%5uu5uu)NNNN3,@ H?2/3/+]10#!53y #+@^"$'""``"""" - $$+o "`p ??9///]]]]]]3]3/99//]]]9332910 ! 2#"###3 %3254&##fCAv^EoHTYEhjXWU..UPu`}J;?//10!5! +V @   H H@ H@! H 0  @  H?+]/]]10+]+]+]+]2#"&5462654&#"fOrsNOqp˻pNQrsPNp3 )@    ?32?/]332210!5!3!!#5!}}}{\oM@3O_ `p H  ?2?3+9/]2/22/]3/10!576654#"'632!oo=oQSNuvFkfve9sLVgxhBVN@O O_`po  H H?3+?3+9/]]]]]9/]3/3/]9/3/]910#"'53254##53254#"'632Tn]HF{aGZBom='t2|A}hoFV]m^+!@ _/]3/210673#^PpyJ=@ GG@H P  ?3??2?/+]22]21032653#'##"'#3jڔ `NjJ:XW6B7M`@%H"2  @ H Q/2?9/3/+9/]]+]10#### 63!7yy=U]3y3H^"@0` /9/]102#"54f^!C @! H0 H   _  //993/3/]993310++53254'73!"!/[x9"l\J's%o !@   ?9?9/33/103#47'J )!FIE,3X 0@    0 ??/]3/]]10"&54632'254#"dws G@*? @     ?322/]]2]210'7'7uu5itt6eN\\NbeN\\Nb'{&<9Y<@'P@]]]]]5]]]]]55?55'{&tZaD@-   P@]]]]5]]5]]]]]]5?5'u&5<NY8@$&&&&&&!!!!!!]]]]]5]]]]55?55w^![@7!@P# #G Q0 ?]]/]39/]9/]]]]9/]2310"5432327#"&54676655'YvT>kwAH%wTate]qZjãlWkZ!s&$CR&%+5+5!s&$vfR@ &f%+5+5!s&$KR@ &%+5+5!5&$RR&%+5+5!+&$jR&%+55+55!&$P}4@P@ %+55]]]]]]]55@T H7 ZO_ _ _ h    __/3?299//]]/329///]q323]]99+10!!!#!!!!!3#s`Rh#Jwj;<&&zP (" %+5#s&(CR & %+5+5#s&(v?R@ &' %+5+5#s&(K#R@  &  %+5+5#+&(jR@ &%+55+55s&,CR & %+5+5s&,vwR@ &x %+5+5s&,KR@  & %+5+5+&,jR@ &%+55+55{ I@,[ Z_h/ ``??9/]]32/]329103#53! !!!3 !V=ar|` C4D5&1RR@ &%+5+5T{s&2CR&%+5+5T{s&2vZR@ &Y%+5+5T{s&2KR&%+5+5T{5&2RR@ &%+5+5T{+&2jR@ &%+55+55-{ \H@ HH H H  HH@ H/]/]]10++++++++7'i=Bh?ff?i>gf=gP!`@8 H H [  #[/_ _ ?3?399/]329910++%'7&!27#"&# 3245\yu|\}slPzPJNŸ`iNɼxD^V",}Ps&8CR&%+5+5}Ps&8vLR@ &L%+5+5}Ps&8KR@ &%+5+5}P+&8jR@ &%+55+55!s&<vLR@ &M %+5+5F >@$[ Z ``??99///]22]10#33 !'3265!#jƫ=T*r@K F GO _     G&,G P)P???39/]9///]]]]]]10]%53254&'&&547654#"#!2#"ߏ[b{dمT1:z^ȷ1VQf=NzWujjZNouJ>!'HFV  +; H` F `    ?3?3993/]223/]+33/39910]]]]+]]73##3 VR}Js&/veR &H %+5+5+&OvN@ &A %+5+5;&/9' %+5+5;+&O9 'ݴ %+5+5&/8 %+55R&O8sf %+55&/O %+557&OOf8Y %+]55HD }@6  y *v%  Z@ H  _??99//9/+]33/29/10]]]]}!'73%!}Iƻ+N{N}}X}F+@i f     H@< H o    G@]O N   N??99//929/]]3/]322/]210++}]]'7%5!7!5%NäM`XP\yz_ly{{Ds&1vbR@ &c%+5+5-!&Qvo@ &o%+5+5;D&19'ϴ%+5+5;-^&Q9'%+5+5Ds&1LR&%+5+5-!&QL @ & %+5+5j7@G G/ P??3??2/32/]210#6# #33663 2~<(`㶔 *gnJrJNZo3{DbH@6HEUe4% _ ?33?33/3/]22229/10]]]]++##3353#"'5325)B Ȼ^>MQ^-^<@#G  GP P???2?3/]2]9/10!#3363 !"'5325# V gQ46=JomrT{&2MR@ &%+5+5sXo&RM@ & %+5+5T{@&2NR&%+5+5sX&RN@ & %+5+5T{s&2SPR@ &N%+55+55sX!&RSR@ &R %+55+55I@)Z Z_h _ ___????9/]/299//]210!!# !2!!!!!%&# !203.RHrg:I5F < X!9^$)l@) )G@Pp%F@ H +"G@HP))' P P?3322?3229//+]+29/]29910%#"32632!327#"&%2#"#"XYTIl`^{W䨪ZNO{} 3 +qLIv!1s&5vR&ݴ %+5+5#!&Uv\@ &-%+5+5;&59' %+5+5;#^&U9&@'_?%+]]]]5+5s&5LR& %+5+5#!&UL &ݴ%+5+5?s&6v`R@ &&` #%+5+5!&Vv^@ &&^ #%+5+5?s&6KR@  && %+5+5!&VK@  && %+5+5?&6z ,%+5^&Vz ,%+55?s&6LR@ $&( %+5+5!&VL @ $& ( %+5+5f;d&79 'ߴ %+5+5;o&W9@ '% %+5+5fds&7LR@  &%+5+5&W81Q %+55fdY@3  Z     c  P  ` _?2?9/32/]_]_]_]]]39/2/9/10!5!!!!#!5^_&ڻ3ߤ!bop@CG  /?N  N P?3?33329/32]2/9/]]333/]922910%# 5#535!5%3!!!!32z MiJ3] X^1ۉ}P5&8RR@ &%+5+5-&XR@ &%+5+5}P&8MR&%+5+5-o&XM@ &%+5+5}P@&8NR&%+5+5-&XN@ &%+5+5}P&8PR@ &%+55+55-&XP&%+55+55}Ps&8SLR@ &K%+55+55-!&XSP@ &O%+55+55}9P&8Q%+559dJ&XQ5@%+]]55s&:KR@ & %+5+5!&ZK& %+5+5!s&<KR@  & %+5+5Ry!&\K@ & %+5+5!+&<jR@  &%+55+55fds&=v9R@ &: %+5+5!&]vN@ &O %+5+5fd7&=OR@ & %+5+5&]O@ & %+5+5fds&=LR@ & %+5+5!&]L @ &  %+5+5d .@GO_ P ??3]9/]10&#"#4632)izw\}`wNR@*! o   ! u s s?3?39/329/]33/299//99//10#"'53265#5754632&#"33F;8\?™ad/OA\?D?fqKD%fq!s&$&P}vHRR@0(`(P(@((DP@ &)%%+55+5]]]]]]]55?]]]]5&D&Pv^H@1@@@@`@@@""""&@<%+%!%+%+55+5+]]]55]]]]]]5s&vR@ &%+5+5-!&vq@ :&p47 %+5+5Ps&v9R@ (&5"% %+5+5sX!&vP@ *&Q$'%+5+5;?&69@ "' #%+5+5;^&V9"' #%+5+5!+@  _/]293/329/3310#&'#567673yU~Nx"%%;82&OOC!+@   _/]293/329/3310#&''53673"##;xRzYy&LLC6;FoA@p@ H@ H/+]3/+]3/]10!!F?o9 L@ `p@ H   _/]23/]3/+]10# 3327 mOh L1}J@4[B& Oo/]3]]]]]]]]]102#"54fkkjrssrH   @ H@ H H H@*H H  H /@ H_/]+]++]+10+]+]+]+]"&54632"32654&bfyyfeg1?p2??tcctv_bwH<5q<55<9/8@"-=M0 H O_ /]/3/3/]9910+]#"&547332/ISw~Bqm^l}q`3@    /]323/3/]10#323273#"'&#"fh:gf"Pi9dd(R88r98!C@* @P O _ o   _/]323/]3/]2210673#%673#^PpdjWWpev! @  _/]3/3/3107673# c=mkrPS+ W@9  O o   /]329//3/]99//]310673#'2#"54!2#"542)]zRa8)`aa`lf16gffggf!&$TED%+]5?5H^"@0` /9/]102#"54f^#&(T!@     %+]]]5?5eF&+T!@     %+]]]5?5&,T Ŵ %+]5?5|{&2T%+5?5&<T4 4 %+5?5z&vT%+5?5&UZ %+555!$q%/9@@HZPH@ H_??/++]q+10!#/D չ @HHH) @ HG& H @THW G %    ` HX H *    /Oo[)9IH _?99?3]+]]/]]3]]]]+/]3]]]]+9=/33]]10+]]]]+]]]]]+]+%!53!&»_3%4}}1n #(fd=F+T{ a@@t/?{ 0X  [W[/`h _ _??9/]]/]]]99//]]]]10 ! '2# !!heyx",/,.! @H  HH )     @HG &   H   @DHWG%HXH* /H H ?2?3]++/]2]]]]+/3]]]]+9=/33]]10+]]]]+]]]]]+]+!#&'#3?-+ǍȆ`q\0D1o\ G@)/? 0   _h _ _??9/]/3/q2/99//]]10!!!!!5eQs<T{2F%@Z Z_?2?/]10!#!#!FF3H @W7 &   7&8)   _ _?9?9=/39/]333]29/310]]]]]]]]]355!!!n}Xgf%fd7!<'@Y& ) )& Z'   r @ P ` 4   HZ/?o)#@$ H#Z'` P  ``p?2/]32?3/]32/+]+9/_]]_]_]]]332210]]]]332###5#"543332654&+"33//! ˴>6SջѬ5;9u@NZIrP`4D Z/?oZ  w` ??339/3]2/]]9/_]]_]_]+]3210####"&333332653嬊B!ZJw@N HO_  H@PX[ W[/_ _?32?/]3]2]9/]]3+]]]2+10%!5!&32!!5$! -  +8\S褓 E+&,jR@ &%+55+55!+&<jR@  &%+55+55y!&~T.&&* %+5+5!&T`@ (&] $%+5+5-!&TP@ &O%+5+5!&T@ &* %+5+5;&U!% %+555555y^%=@"G '!H #P P??2?3322/]]222103673327#"'##"3254&# !26^ &3T4?( lUg-]o )*ZbP&Q@.* !HG( G  P P$P??3?9/9/]23/9/]910]#"'#46323 4&##532654&# }غu燚%^Fr=0?4ҷyP+~vRyJk@%HD T t   H 0@ H/? @ H ??33+?33/]3+3/]3+9/]93310%#433673ne7G$H?)>hiF"O@1+  F!H$HP P?3?9/]]29/9910]]&54632&#"#"$5%3 4-IJVTi%'˅mjI@?gdt,d^^Q@/  !GGPyPP ?3?39/]]9/]3/]29/910# !27#"&5475&54632&#"!HĪا?VKK BLHo+@W *  &  + HF  H{mI;-ykI;- P?2?99]]]]]]]]]]]/]3]2+3/3]10]]!#654&'&&5%#!FtŦ嫗yygBx׉v'eob9A# -^1@G G P ????2/]2]10# #3363 wͶ frJoG9 O@2 ;(H  '7GO  /  PP??9/]]]/]]2]_]210"32!#"!32d3+n|n;mJ &@G  o P??3]9/]10327# bpe]J XJ=!U@3 $! H/?H  PP ??39]?/]+333]]99333103'&&#"5632327#"&'&'#=5"VI80A>FF%A2ARWE$S;hH )o PWz-J:@# GG P  ?3??2?/]2]21032653#'##"'#3V a̭K:XW6=JJ t@F  H 5 E U $  )9  c s   DtG @ H ?2?3/8+2]9=/]3]]]]310]]]]]+3363#=n JEro+,@_)F"$_$$F  .H{mI;-Hy k I ; -  #O/,,,%"P#??29/]999]]]]]+]]]]]]/]]9///]]]99310#"#654&'$4675&5467##5!#"3LyycJזh>Eٷw[i8 dod5ADZ* ;r3 qhsX^RJy@5K [ k : (   G TE6'G @ P ` @H    P  P?3??22999//+]33]]]]]3]]]]10%#"!##57!#324Umފ&Z.PJPN};^5@H @H GPP ???22/]22+10%##32#"'32654&# TרqpFa0dto)^g@B FH{mI;,ykI;,Q ??399]]]]]]]]]]]]/]]2310#654&'$!2&# yT7>rfoc<;GZ;9H#BJ 2@HH  P P??2/]]2910!#"!#"3265+;;ǨJ>п j9JS@ G  @H= /   @ H_ PP ?3?29]2+9/]]]+]3]10!327# !579%kTT†J#XsJP;J3 F @H G GP ?2?/]3+103!2654'3# ??>JJ^\@:VfGI F IP/?P P??32?3/9/]]]9/33]]210$7!2$#" >xaD 1:ZΆ!^}&o+N@\&IY& )FV )  H @+ H  P  P ?3?3?39?399993/]3/33/+3/+399933]]]]]]]]}103327#"&'#&#"5632}7_.P@,$B4fG۴DT"5LQn7P3rYZy|> mPi@V f G  I FF@+HF@P/?P  ???32?3/]]]2+9/3]]210$43#&3-??'Ӭs&'ZZuJ@VG6@cHW6FV f G  II?!YH9HX9II0@P/ P?2?3229//]]3]]+3]]]]9/]]93]]+3]]]10332653323!"'## åhCKO?iiߵ2 2Jo~2΂2ز(.&j@ & %+55+55;&j& %+55+55sX!&RTH@ &H %+5+5;!&T'@ & %+5+5Zu!&T3@ (&1 $%+5+5#+&(jR@ &%+55+55}@IH Z  F V 7 /?  Z _@_ _ ??2?39/]99//]]]]]3/]2/+]210%532554###!5!!!2#"DCIνX#o/s&avmR@  &: %+5+5D@'@H [  _h__ ?3?39/]/]299//+10!!!27# !2&#"LuŒf6ܣNN:;iZVN?6,+&,jR@ &%+55+55- @X HxWg H x   H Z`G" _`_ a?3??9//]99//]]23+]]+]]2+10#"'532667!3 ##3 4&##wp8( $#5)'Ϧ- Fa^ xBC@% ZZ@ H_ h `?2?39/]32/+29/3210!!#3!332#'3 4&##%¥>')- Vhܠ xi@;H Z  F V 7  Z _ _0@  ?3?9/]29/]]]3/]22/+]10!2#4###!5!!κI#s&vR@ &B %+5+5i&6R@ &%+5+5F C@*Z Z Z _/33/?3/]9/]10!!#!3!3Fv{I!$o 3@[ Z_ _ `??9//]22103!!! #%! 4&##+R<^ܠ yq%/a @ H  H  H @ H[Z  @% H    Z _  _?3/322?/3/99//]+]+]10+]+]+#!#36!3!#űq Pk'Xjk#(@Lg w   6 F V  F V 7    Zhx 9IYg  g@ g@2hh   @ P    ?33?339933/8]3]3/83]/83]3/82]9/]]33]]]]]23]]10333###u]Vs<<</#Q@.[!! [%  `h ` ` ?3?39/]]9/]39/3/9]10!"'53 !#532654&#"'6!2cvɌv]O`}gzy}ƥFb@@H5EH:J )  i&  ?22?33/]22]]2]210]+]+333#4##BլpJFFi&6R&%+5+5 @`% *'  Z`p)  )@`?2?399/8]]]2]2/83]/]23]10]]!##336<BFo@! @H | Z j - = M   @ H HZ@ HZ_ ` ??3?/9/+]10++]]]]]+!#!#"'532!FuGB98fn~/iq\0F+T{2FnF3&fd7@    H@YHIY8) FV7&O_oe6%  _?2?39]]]3/]3]]]]]]3/3]]]]]]99=//++33]10#"'53267337TLx~hJU]ny6  '8b-gz?ms5; :@" Z Z _?3/2?3/]]3/10%3#!3!3 }F/@Z Z _??39/3/]210!## 33273FgnۻVZsGтePyV \@@eV7G{Z Z? Z0@P _?2?33/]]]9/]]]]10!!33333V#=@Ye V 7 G {  Z  Z?Z/o  _?3/22?33/]]]]3/]9/]]]]10%3#!33333 u ;@! Z[ _ _ `??9/3/9/]210!!5!3 !'3 4&##FD#^bRD z`m S@5I  0p? Z 0_ `?2?39/3/]2]9/]]10333 #'3 4&##3`/JNR/^ܠ yVJF <@ [ Z@H_ `??9/3/+]2]10333 #'3 4&##Rƪ^ܠ yDLJ@+ [ /  _h_ _?3?39/]3/]3]29/]10'63 !"'53 !5!&&#"M0YԒŃqٜV{k;:bn@+/O_@H @ H 7Zf@ H_h __ ????9/]3/+]]2+9/+]310##3332#"26&#"qj_`il^]V]"qs;eh9 q@   H Z   H[@H` ` ?3?9/23/+]3/83+]299+10]]#$!!## 33b}tʤ\b6J\^D;#>@%H @H !HPP?2?9/22/]2+2103632#"4>! #"!s tܺ>h% _#CAҪh? j9wlTJ [ @1 HF FGPx PP??9/]]]9/]23/910+]#!! !2654#!254&#L5%r>F#ߕJ@HT>\H#J.@GoP??99//]]10!#!˶PJ!hJ t@ ( H@5 H P     F GO _ o  GN  P?3/322?/]3/99//]]33++10]]#!#3!3!#hVP$}+P$H^HJ@iyK[)9 fvDT&6 V f  7 G   G   hx H H@Pgw H  H @   ?33?3399333/83+]3/83+]3/8]3+]3/83+]9/]]33]]]]]22]10]]]]]]]]]]33###3^mo5--5^ W@3 F F"`p!P  x  PP ?3?39/]]]93/]33/9/9102654#"'632#"'53 54!#5?ɖOWHL9 AϗEV-J H @P H  H H iyfviF f  F    ?33?33/]22]]]2]]210]]]]]++]++3##L  JdjJ-&6%@&p$ %+]]]5+5J @NX HveFV5&X H   G@H ?2?39/+]2222/310]]+]]]]]]]]]]]+]3##36ݴJ-JJ@ H @oHTd2B$ H )x9Ii     H ) 9    0@HGP O ??3?/]9/+]]]3]]+]]]]3]]]3]+10]]]]]]+]+!#!#"'532!%>o$/ sZJ@HGH@7H H\l|:J)I@0HScs5E& I  @ P p    ?333?333/]2]]]+]]]2]]]+]9=/3+]3+]10]]!##&'#367673Z Շ<00 dU&yJpM '"-J ?@&G  G  Px ?2?39/]]]/]2]210!3#!#V!߶J9JsX^R-J)@G GP?2?/]]10!#!#!VJD^S%^FyRJ<@%G P??29/]3/]2/]10!#!5!RooPRyJ\J ]@;VfGI  IIP / ?  P P??32?32?/]]]9/33]]2210#$%3$51ϝ#Z2*$-  )s<~!y`jJ[J 8@ GGo   G P?3/2?3/]]3/10#!3!33}JPP%J/@ GG P ?2?9/22/]]2103273##"&5NͫȠJfs}RJ Z@?FVfI 7I? 8I0@PP ?33?2/]]]]]9/]10%33!333+JPmJm@IIFVfI 7I?8  I 0 @ P   P   ?33?3/22/]]]]]]9/]3/10%333#!333vVP}JP+J C@) GI P P  P??9/]/]9/]2103 !!!5!3254&#J\vZRqZJc@BI Y i  I`p7I88  I7 P P?3?39/]/]]]2]]9/]]]103 !!33254&##3\ޡv¢JZRJDJ>@ F G@H P P??9/]/+]2]10! !!3!254&#\9vJZR%^i69  H@1 HH Q/xQ Q?3?39/]]]3/]3]29/10++]]753 !5!!"'63 !"_l> )>\=9;{^f@!II @ H 8I@ HPx PP ????9/]]]/+]2+9/]310##33!2! %2654&#"#LSg]\jh]]J9ŗs J o@$H)   G  Ww@ 7G PP ?2?9/9/]]3/]83]]299]]]+10!#&5463!#!%!"39#̭CfH&Hj@ &! %+55+55-"b@: G$ G  P!PO   ??99//]323?/]32]9/9105325#"##5353!!363 !"6=좓{ jQ-DՉo#!&vh@  &6 %+5+5%^]@:69     HQ/x Q Q ?3?39/]]]/]2]29/10]]]]&# !!!27# !2%>p]# #9Ú>=$$^V3L3&j&%+55+55VMJy@N  Hz   H H HG`I PPO P??3?9//]]99//]23++33+10]+]3 !###"'5326!3254&#3\Ѻ!4\`>)1-#7v&k T~ZRjJV@8I I8I@ H PP ?2?9/]32?/+]29/3]210!33 !#!#3254&# +3\բo7vJ9=JZR-!&vZ@ & %+5+5Ry&\6 @ &  %+5+5-J D@+G G G  P  ?3?33//]]9/]10%!3!#!3V!}J#>@@H ZH@ H_??3//++]+103!#s--#-@Go GO??3//]]10!#!3߶!?J?s&:CR&%+5+5!&ZC&%+5+5s&:vRR@  &R%+5+5!&Zvh@  &h%+5+5+&:jR@ & $%+55+55&Zj& $%+55+55!s&<CR & %+5+5Ry!&\C& %+5+5y@ ?/]3/105!\Ѩy?/3/105!Ѩy?/3/105!Ѩ1<@% & )/]]]]/]]]]/3210!5!!5!  ^ *@&o?9/]]10'636{>& 8@() 0p ?9/]]10#77yA$ C@1) 0p /?/]]9/]]10%#77yG$ (@&o?9/]]10#'7(<|5 J@2&/ &    ?329/]]3/]]10'63!'636{>&{6{>& \@A )  /   )@Pp  ?329/]]]]3/]]10#7!#7)7yA$7yA$   ]@@ )  ?  )@Pp  /?/]]329/]]]]3/]]10%#7!#77yGq3}G$$ /@   ??229/]332210%#53%87778L_7@   ?22?229/33322210%%#553%%K77L//L77K/y"x{R *@P`_o@ /]]]102#"&5fmo|N_@=4%4%4 %     ?33229/]]]]3/]]]3/]]]1072#"54!2#"54!2#"54 #/9E@vC3338-'""o"""""=88G :$@*5p0 ??399//]]33222/]3/3]99//]]]3]]10"&532'2654&#"'"&532'2654&#""&532'2654&#"r~q?*/:;/+ F)r}q?*/:;/+Ps}p?),<9T5#f} dF3}`j'j@ ") Z  @/_%_ _??99//]3232/333223/329310!!!!!27# #53'7#5332&#"BI(zA^'ۿOzx>:; &x\J#+" H @ H H Hf****!@EH@Hi&&&&!- P  f($  ?33???/]]2]99//++33]10++++&#"327#"&54632#"&54632'254#" RDgIJlcQ'!k!!k% JBl!j@>n  pp?? "# v vu?3/?9/33229///]]]]3/32103!"&5556746323264#"urVkHyn~g53Ly(yQk&*@$$++ @(Ht V f 7 G 0H{Yi8H'$ @o*@H*, @1!K[@ H(V(f(v((a'D T  H ?33+]3]]?3+]399//]/22+99//]2210]]]+]]]+]]]]!###3353"&532'2654&#"5! C1rrx?00?@00u/d\Zysqrmnqptuk@?   z  ?333/33392/]3]]]]99//]222210##5!###33#47#/ { ell%/VWxJvRwH<@ / ?  www H ?3+?9//]322210!327#"532'&#"w{Gaۗ3,y|{){D1Qwu'{&@VgT@7  d T D  P@]]]]]5]]]]]5]]]]555?555 'u&@jgL@1(((((((!!!!!!]]5]]]]]5]]]]]555?555'=&@jgP@4"""""""]]]5]]]]]5]]]]]555?555'?&@jgP@4 k [ K 4 $  ]]5]]]]]]5]]]]]555?555yR9@F !HN PP ?3?9/22/]29/1076&#"563 ! 432&&#"32 BLŻ^(T>{Ui2J ȵ H H@D H& H)  HH X *    _o @6HG W %    P` _H[)9I?3]]]+?99/]3]]]+3/]3]]]+9=/3]3]10]]]+]]]]+]++73!&'!Jw;"-+qEoȆX1@Z Z@ H_?2?/+]]10!#!^J z@R7 &   7&8)  @ H_ _?9?9=/39/+33329/310]]]]]]]]]55!!!JGMs+r 3@ ?3/]105!.@ //]9//2/33/39/3310##5!3d( iF'_ H@2 H H H #)&   ?/]323/3992/]910++++#"&54632632#"&#"32732654&#"bvwwltvqRmAPPChVkAPT@gmծլBkOQemNOg)@  ?3?39/]3210&#"#"'5325!28:N7<8JX׮'b{3%#H@) %$" ?  P/]]33/]3223/]32105632327#"'&#"5632327#"'&#"bbJmvadIpscdGlwcdFlm@7yl?7l?8yl?83n@A     pO_o  P/]]]323/]]]323/]3299//393910#5!!5!3!!!'=yJi{;93 e@?gwhxv  @? /]]33/]]]]]29333/3229]10]]%5 5!3e5efà3 c@>hxgwy   @? /]]33/]]]]]39333/3329]10]]55!6e}VV=f=ݖ}P D@    /3?39=/3333/]329=/333/3310#3 P;L>L 11+"R@+ G $GS O!P  ?3?3?3329/]/3/3229/9210###5754632&#"32#"54#3Ùad/O@\@!j?+kŷ?KD`%erhr:9srJ1B@" GGOP ?3?3?32?/3/329/910###5754632&#"3#3Ùad/O@\@{?KD`%erh -@  _/]23/3/]10!"&'3327ì Tw”fUVJ/@G  PN ??3/]9/]91053265%5!#"u\ZΤ7're9{A @  ?3/3/107673# T=`ktN_;@  /]3/3/10673#>`CcVR!@  _/]3/3/10#54673?[IbY99 "@ 0 ??/3/]10"&5!2%2#"d+ќ97FD% V@4_ hx H    ??9/]93329/]332+]23/]310##5!533#547TyT((Vb RZI:PD\@9 @H O_p   ??39/39/]]3/]99//+3331053254#"'!!632#"Pfi4I>#=v|D'}nm998@0   ?2?9/]2/3/]92103632#"&54#"326-4 =p͓Jb]KDSmn\G~zc> IaYMDUH};b~ͺcW8P{^7%9EKQW]aeimquy}@.bze}z}L]?E %!_!o!!!%*7O _ % %`%%@dH@7P77 %77% FUUvrn]Fkg_Lthp`xlh`ll`hLG)99&~~ H`%%%&o&&&4-<B--XSXzLScG/33/3399//33/]333/]]9/33/]39/+39339///333/2222/33339///]+]]33/]3333333/3993310#".54>3232##!##"&'532655#32654&#"%!#335!#55353#35!#3#3#3#3#35!32654&#'32654&##4N66O33O66N4XX%#WK HXE+ ''^l58933985/oo0mmoo9oooommmmmmIu;&*73%!%/X@##@X67Y?##?Y75B(6&AH4MPLR),SSJKSSK0o/mom-oohi;mmz" P)9N@&))*q2P2`222****7-))--/3/33/3/3/3/33/]3/3/]]3/310 54>7>54&#"663232>54.#"fe##5##HFAB7f-52 &+ A2)  )2AVW+"!"!9>G.|!)5*($%36;$7A:/ "/ 8!&7L o %+5  + O]@7FCC;GMQQQ3F>OONN"N)))N)N)8NH8N??99//]32/]]3/]329/10.#"#".54>54&#"'6632324&'.54>323j 9M[0LV.j<@ăoV" 6%%d62E, 0N:C'QYhuKlt=XH8iR3:s7_}E(]YK-! 2D(#V]a-*J620>KxQCpR.Tߋ H @F H& 6   YT )  ZcP ;;'@ H??9+]]]33/]_]_]_]]]2]]9]]2]]++10>7>32&&#"#3dKNI.6@)"+ ! ># /:B?8K,(=' /<Sn</J @_@<V8f8G88I77!I B,I@P/771' P <1P?222?229//]]3/]9/]]9910".54>7#57!##"&'#4.'!32>55332>{OnE ## EnO^o o ## #4$$6#O?$4#DrCtnrBJPBrntCrDW[[W>rpvCCvpr>[R&)Hb9˂&Rq\u&0vdT@ &d %+5+5\s!&Pv@ '&~!$%+5+5!&$[@ '%+55+55^&D[@ 1',"%+55+55 '2\-@+`3P3@3 3333%+]]]]]55?55HM@H/H @H pH/+]+/+]+10#".54>324&#"326H#>T12R; ;R20T>$u?21?972?3Q88O33O87O45<<55<<-h0@@@  HO_/]99///+2/]10467#"&>73#-xz<9$2.;D5 08>RNsL.%'F%KOQ$ MPQ%B)'4c@7)'')& 6(  P  R( &/ N ?33?32?3322/]3333/]9/33333910!######5754>326632&&#"!!3547&&#")ˣͤdd(MpH1W"&f?4[%)E%&7$o!-#5#55A>RjT#  4W@hfsN4W@B)'4a@6,))&6( PR( &/ N?32?33?3322/]3333/]9/33339910#####5754>3266323#&&#"3!3547&&#"Hͤdd(MpH1W"&f?0.&7$-#5#55A>RjT#  4W@hfsN4W@T 43@[+[6![ /  0_&_??32/]2210#"&&546632>7332>54.#"{@ȇɁ<<ˎ|B  !1D.**$O\]N##N\]P$ݩllkZV4?I(4cWJ[鉉ۙQQۉڗQQs +5@J&H-!H  )P$P??32/]3301#".54>32>533 4&#"XJogNIoaB&+*9K1&*ם6'ՑLLՉӑKBA8HW0Er[CBc}&6@[`p%Z(Z & _??33/3/]2]10>53#".5332>5/7 0RyW-cslc.6YACV14Ma6EiKfrEA~z3WV+0ZR'WJ!!!H!@#H!G&)G  &  P??2?33/3/3/]222++]01!'##".5332>53>53 2U\+?).bi=4e`:{2Ne8FnH+!C+!vR!.@ /]9/3/3/9/3301#'>54.#"56632!-<" q%;*$242)':)o! l[}  //014632#"&=-,?'-=<77<+7#s&(CR & %+5+5Fs&CR&%+5+5H!&HC&Դ %+5+5-!&C & %+5+5R{<u@   @ H ::'>.---@" H--3/'';;8* .. 0_*::8_"?32223?32/29//]2/+]39/93/+]310%32>5#"'6632#".'#"&&532&#"3273'80C*NB7)/"X0"N^!6.+*.5!^O#0X"/)8BM+D09%+Ye %% e%fgY+J*z@.  &!""""I_I ,*@ HI& ""O?33?3393/3/+]]q33]9/]3/]]3399]10!33>733>53#&&'ᠣ" Vm?O-BqXT jJ\U38AC!`g`!hT8>u!A@!Z [#!__` ??99//32/9/3220132#!!5!3!!32654.##^7<~†+wh.[]JNBqWap=`@Fa<X'!C@"G F# PPP ??3/39/2/9/3229/01!!2#!#5353!32>54.#/eqN+7]D&!A`@לM|X/)B01B(b-V@0"@ H*/!_!! +_*' _?32???329/32/29///]+3201"!!3267#"&&'##33>32&&DrU7 )-U}RBt9;@I,xx> LpT32.#"!!3267UrF KrS"D@9/243{ \$@]=Hs0/n:{J9r4   \T(% @  @@ H H    ` _ ?33?9/322/3/9/]93+]q9}3+]q3]910##3###!'&&'p s7'%3<VJVDniKK\=J @l G  G I    H   uFVf7% HP@ H R   ?33???9/+322/]/9/]]93]]]]]]]9}3+]]]910#####.'#3ch## "" JJ\Z]VW]["b@   w  YiVf  x  Zj2) `P@ H`  ?333?39/323+]322/22/]]]]/]9/]]]]]]]93]]]9}]9]]3]]]]01##!#3!3###3'&&'/'}-( &VVlJVJTaKK[{ J@z IfTI4DjYE3   d    PP P  ?333//?39/323322//]/]qq9/]33}/]9/]]]]]]]]]q]]210#######3!3.'#l::'\ JJ9ǐV`^##^`V#&S@,$ JZ(% (&#'a$ &_?22?9/332/33/333/39/]]329011>75!#.####"!q1LrVYqJ/qq"1F66F1"q%Ub:狋8bV;H_9H9_H?` J#&u@D$f  IzYK'&#'%   R$ &P?22?9/33233/3/]333/]39/]]]]]]]32]9013>75!#.####"! w*BdPPdB,wv$/B33B0$wT=eM1 dii1Lf>P6H+ +H6-\(+s@@)   *[+Z (/ 0 "`) + ?222?9/22333/]2/]33//]3/9/]32910!667##3!5!#.####"!`8 ɦ:AS4 77 .&'- 8BQl(Vዋ8aW;I_8H8_I?Z{ J(+@[)I       u h   *HI+H`  (I* I""P) +  ?222?39/3/2/]22/]3]3/]/]3/9/]]]]]]q3]]2910!667##3!5!#.####"!sI բ =N3 II )! !) L)}T2C J9^ii1Mg>P5G, ,G5-uF'v@DbGQQMYYl9[gg,[rrxNMMMCDD"22Z"Z]@VVNC>abl2a33QGDb'` _?2/3322?3339/922/2/3/3/33/]32/3/23/93301"32>32.#"#".54>32>54.##532>54.#"'667.'53>32&&#"G[4$=/AaTQ2V^ 5N51GJ\FhP1fkYQ&8hY\a2&D_97aVO&\;f762y3b6:CK*/ !10.S}U+3[}KX_1B )$ )H`732.#"#".54>32>54&##532>54&#"'667.'53>}.\*;bE&2G+/S=$7yJY/%@06mfZ$HN $+."\lw<_zE#UnQ}U-y;oV5yOS?>vF/0-y6e8=GSRuK> .D\;2N;) +B\>CuV1&#   +EW-3ZC(+E3^[ #B6MM&""983#`8"F9$9uPT{)+@%[+$[ `$$__??9//2201#"&&5466322>7!"!.{@ȇɁ<<ˎǃ@W{O'g(P{XUzQ**OxݩllkkGzz†GCuuCsX^+@H H PPP??9//2201#".54>32 ! !XJogNIogN e#'ՑLLՉӑKKdDF?!O@/7&8  0P` a ??3??32/3/]993]3]]92/01"#3667>32&&#5*$= -* 7>32&&#"#R f5AS<, ( J?FJC9]ge%@]uC(A.?!s&vR&m1%+55+55R!&v$&8$%+55+55F'Ho@DHHH HH/(7AA')))((( (6 6677 H/ID=6(#_???3?33//]]]9/]3/]]]3/99+]10#"&&54663232>54.#"%33>73#"&'532677j8m]]m89m^]l8y .#$. -#$. d j)>X>%0/1?ݶcbee貉ۙQQۉڗQQ8=?>@<aNxS+ WcP^>@dX>h>9>I>(>%>-I666XhxIY X,I,8,,,,I-fvI %>:P3,PP???3?99/]]/]]]]]9/]]]]3/]3]]99]]]01#".54>32324&#"%33>73#"&'532677s&Gf@=dH'(Ie<9dI+{;9u<9td j)>X>%001?'݂44ݨہ33ۨ΃8=?>@<aNxS+ WcT{)?^@5% ;+50[A [/;O8_8885_+@(P(((%_ ?32/]2?32/]2/]9933333301#"&'.54>766326632>54.'#"&'{0acJ6:Gg^,,^gG:6Jca0/K6C30E7M00M7F04D5J/ݒ~>55>~}>12;~le-%'-dnnc0(&.esX!6X@/$ 5*/,H8"H5_222/QP'''$*Q ?322/]?32/]2/]9933333301#"&'.54>76632663264&'#"&'X6bV <6:;Qc85aT :9*Qd9׿ 8309 c` 9037 'u\6,,6\ts[6( $[sG&&GJ#*(GR{='f@k56?6O6_666660<(F" F"" "P5#"'6632#".'#"&&532&#"32>7Pr^&+7vg6jp}Jwz<8$2.;D}$Z30C*NB7)/"X0"N^!6.+*.5!^O#0X"/)8BM+D0-)(ˁ$*$-5#ke$*$MsL-%&F9BYe !! e%fgY .Xs;Qb@)////@ H//)I! 7dFIXR]]UU==H @- HI X`VU``HCL<==HHHU.,,Q2/&!Q?3322?3332232/]3/323/9/2/+]2/2/3//3992/+]10"&'#".54>32&#"32>732654&#"'6632#".#"#5463235654.54632>[ XIDf_.("#(/^eDJ:23#^-FmK'3Z~iPn`.+7vh>llwJxyt$2.;D# #<כۉ>>۝Չ?$+$.4lq$*$MsL)3&&FR{ J@dH; ;;;HHHHHHHH5  !L   A5II8F<<>_8;;8   8HHF_+&0?33223?3222332239//]]933333339/]]q3/]2/10#'##'##'532>5#"'6632#".'#"&&532&#"3273R#11#11#O'80C*NB7)/"X0"N^!6.+*.5!^O#0X"/)8BM+D09%gggg+Ye %% e%fgY+ 8@//#0I888&8F8V888@fH8000000)0I0Y0i040)(8 )I  *: P  I#04   )?3?3322293?3/]]933333339/]]99]]q33+]q33]01#'##'##'533>733>53#&&'R#12"21#P:ᠣ" Vm?O-BqXT jgggg\J\U38AC!`g`!hT8>#4@Z! %[!_  _/?2?32/]3/39/01"32>7##"&&546632&&k{C;vv-(%LWlON?'QډۖN bln,* .%^5@G !HQ Q?2?32?/]3/39/01".54>32&&#"3267#qđSTzZB>:O1^W5U"?Ֆۉ>"#ic0h^o @K ?O_ 0 @ P         @H @ H/33/+33/3/33/8+3/9939939999/33/33/]322/]32/93901%'%7%7%BG㴁FGJ{J;{Z}9IĤ{@  //3/301#!#"&54>3!6632+9?,08( &.08*P9),338% -12@  /23/3/]/102>32#54&#"##5Nwp:m|:.*eyUf$+$el#4.$+$5#@@ H  /]/9/+014632&&D;/2%9FI!JCAEH%F>>FI!JCAEH%F>H?GJ  IAAEI#JC5M> "?LK> "?JO>"#?NO>"#?N9O> "?NO> "?NNA #BMNA"#BM)b +6?JU>@+$ 6JD5/ !%U>OOO:/]22//22/01%#66736673#.'5&&'5667''667&&'7.'7 -Od -T d59:@>9L.u8?A<6o,?784F/q3@)m7Hw5=%$ GP2;&$! J/w6Wr#0u6?>: T d -R gi4;$&#G7;J'J5o-?:5/H5o/?:6/Hh1\@21'&& 3@  1',! _?3/?3?3322/]229/3/2210!46767##3333##".'332>7׮B٦a0]dgW*3K5,I6#9ALMF>CC JLhMwP)'OwP9H),G5%H@` H%5 H H* :   %FF F '@F %   P?3/?3?3322/^]229/3/2210]]]]]++]++!#333##".'332>7^ : {{%0]dgW*3K5,I6#jJdP}NvQ)'PwP9I)+H5/F!A@![#Z !__` ??99//32/3229/0132#!#5353!!32654.##7<~†N.[]NBqWap=`@Fa<=D!I@'F#GPO/P??99//]32/3229/]01!!!2#!#5353!2>54.#/eq98]C&!A`@!M|X/)B01B(F#l@;[% Z ` #` ??9/32/33/99399/]22/3]3/9939901'###!23267'76654&##F3R:hj},b9~<$ ;^mv<; =sfU N 9mgL"y]D^#9x@C2!3 547H;433* G   $P 2!3 3544/P ?22/32/99399???3/^]22222/3q3/9939901".'##33>32'"3267'7654&=bO= _Tkkt DDOmC DoT"jlo\+: "7+6#=-HԌEN .^_)ek7Lhd 1@Z  _ _ ??9/32/329/01!!!##53dP}J 1@  G O P??9/32/329/01!#53!!!!#q&8@"[( Z _  _??9//32/]2/01"#!!6632#".'53 4.%<H-WRv1ND< ?HwC{ \O  y~@ XJ)5@H%+FQ P Q/??9//]2/01".'532>54&#"#!!6632)A7104=&FnM( S# "N*}ДRL   0ci/J>۝և?r@@ /?iB   _??33/?339333/3/33]]]]22/]32/9/]332210!###3333#1`HAw<<<Jb@5 I I    P/33?333/9333/22/]]]3]29/3322310!##3333#àZJJt--5e}B/&2, %+55B^&/)%+55x@Mu6V Z`p W @ %   (U6$ ?3/3?393/]]]]]2]3/]]]]3/]22]]]10%3###33+76˦<BJM@, F  G   P ?3/3?393/^]22/]22/310!#333#ݴ-J_} Z@I & `p@`      ?2?99//93299?993/8]]]33/3/]9/]]33222201!###3733wwBdbceJ@K I  {`G`   ?0?3?99//]]9399399/]2/8]]]33/39/]]32210'#3733##}6}J8 ';w@J Z `pHX@`_  ?2?39/]932333/8]]]33/3]/]322999/10!###53533#73>yr`{3;z@IveVE G  H`Yv   O ??9/32?393]3]3/8]+33/3/]3229/99]]]]10353!!373###;{ V^uߘ%RZ_@. H Z  X  _?2?3933]]]3/833/83/9/299+q01!##!5!73hudN+J \@6(8 gVGG  P ?3?393/8/9/]2]]]223]3/83]]013##!5!`LJ-N@/ Z    Z_  ?3/3?39//]]2/^]q201%3##!#3!3 VhJU@5G F   O o  G P  P?3/3?39//^]2/]]]q201!!#3!33#F𶠰J9P} K@/ Z?O@H  Z_ ?2?39//^]q2/]+]210!#!#3!!!aVhJ ;@" GO  G P ?2?39//^]2/]201!#!#3!!#ǷFJ9ǚj$D@&"@ H&!0""! _#!_ ??3?9//]+9/2/106632#"&'532654&#"#!#!1b[+3^T6U+&I/sdgp3a\O  J)N@,I` I%%%+IQ PQ/22?3?9//]]9/]3/210"&'532>54.#"#!#!66329<\(*R82F,/L9-F0N]40Wy /bkkf1-PJ'9ݥׅ<TASk@=3OOL[8 888#""B[ U+[/G`p====&_O30_ _ ?332222?/]/]3/3/2/39/q9333103267#"&'#"&&546632&&#"3267.54>324.#">{6G'7#;D E>)U;~ĆFE2]&8="Q|T*.X~P%8&)PsJJsP* , , 80.js%  ,4_^ D㞝݋?6uV|<7vVY-4`TO!e|ss^P@9H4CWH 9Y9H999$##HCNNCCR,H NP>>>>>@ H>>1$'P# 1PHJP?322?329/+]q/]]2/2/39/]]q]9333106654.#""&'#".54>32&&#"3227.54>323267!+9B ,>?RA|9&S/oGCv9O -8*QuK$'MsL "4$0TrAAnR.0@%'59?4[M@-k3T: zh%LхԗS  8lgai8'PZg=^Y+'VcFwcO B&&v$ %+55B%^&F75"%+55fd J@, ZcP_ _?3/?2/]_]_]_]]]/01!#!5!!3#û^_yRJ 6@ G GPP P?3/?2/]/10!!5!!3# oo}!<RyJc@>7G&H) G{cP ??3933/]_]]_]]]9/]2]]]2]]10#33>73H  HJ Z]VV]Z o!ʹ@$ H&6hxZH)9' H@THY H{P Z    c  P   _ ?3?9/32333/]_]_]_]]]32/9/]]]2+]]2+01+]]]]]]]]+3!!#!5!53d}&ڻ'=X3RyJ@ v  @G H y HGcPO ??39/332/]_]_]_]]]3/39/]2+]]2+]]01!!#!5!33>73 H  HcJ Z]VV]Z o5@GWH h x    @ H  &  @@ H&  H  )   H )   _?3/3?39333/33]+3/3]+/3]+3/3]+933]3]01%3###333GZKNZ{/L`J@$ H   H H@ HI  H  F@3H   FP   ?3?3/93333/3/333/]33/3933301]+]]+]+_]+]+]+]333###w"#u-P3fg}L9@0Z Z _ _  ?3/2?32///]01%!33#!#5!#J?@$ G F@ G P P?3/2?3//]]/]01#!#5!#!33y}P=@"Z  Z_  _?3/?39/32/]2/]01%3###"&5332673 QT2R=MT?&G9W:JA@%GF/G  P P?3/?39/32/]/]]201!#".53326733#F'NRZ3RX/idNI% /TsDfXV-+P}FQ@& ZZ   _    ??39/333/32/22/]29/3210!###"&533336673FAm5!2R= 3q?V$ 'G9W:X $%J_@7I@Pp G GP  ?3/]33??39/333/3/]29/]3201333673##5#".5Nid}hi5e7}/RX/JfXV-F%3/TsDF+@ Z Z_ ??39/32/]20136632#4&#"#n_ĻduVk--/tt((95J;@$G G   P   ?2?9/32/^]2]01!4&#"#3>32idZT,W\e9RX/XV:9+J/ /SsDZ{-8T@/4[:#&g&w&&3[("`3"(( _._??329/32/3]22]]22/012!32>7#".'&&5467333>"!4.}]$`$KxZ/OGA!!AL\<~v< f !0WeZz†G  ]rz*B>#a[A~xuCX^,5^@81H((7 fvI0H/ @HP0 "-P'"Q(?22?9/32/+3]22]]22/01".'&&5467333>32!32>7"!4.lK 02KuZcn;L3WPM'(MQW`s 8XFǁor'?:"/1lu=Gnq ۜDqP,{0;g@97[=&)g)w))"6[,%"`6%++ _1_??33/329/32/3]22]]22/9/012!32>7#.'&&5467333>"!4.}]$`$KxZ/OGA!;CN1ZV. f !0WeZz†G  }orz*B>#a[A~xuCX^-6q@BF2H8"%f%v%%%I1H)/!@HP1!(( Q.P??33/329/32/+3]22]]22/9/102!32>7#.'&&5467333>"!4.cn;L3WPM'BNTc9 02KuVs 8X^Gnq  "uToor'?:"/1lu=DqP,,i&6R@ & %+5+5&6@ &%+5+5X%X@1  7 g w ![' Z a _?3/32?39/32/]3/223/301]]]"#3332#".'53 4.9R#5җSRw1ND<?~IvEs}<XQ휪O  w8 XJ"U@0"!!""H$ 6FF  N!Q ?3/32?39/32/]3/2]23/3]801#"&'532654.#"#33jdOJfLi/,iB4aUSKb=ϕՈ@%jb.LJ#K@* """""[Z_"a_?3/?32?/3/9/]9901%3####"&'532>7667!Ð٦ !"5OnN#J; 0@* !'+AqQgo; ?bu69:J?@! IGGPO P?3/??/3/9/9901%3##!#"&'532667!{{@_V17YE3}iuoFN@. ZZ  0_/ _/22??39/^]]/]q3/2201".'53265!#3!3X-G=68n?IJ  VhݪO -JH@* GG PQ/22??39//^]3/2]201"&'532>5!#3!3Ki/+jB;[=߶!?o %-aiJ9Ո@T@1 Z  0  [ Z_  /     ?3/3?39/^]]/32/]q201%3##!#3!3Ð٦ VhJI@)G G  G P  P?3/3?39//^]2/3201!!#3!33#B춶{{J9P}F=@ Z Z  _ _ ?3?3/9/32/]22/01%#"&5332>73##9e^\12R=-VY`8" G9W: J'%JK@-FG G   P P ?3?3/9/]/^]]22/01#".5332673##3o,W\e9RX/idYT0 /TsDfXV:9q@< l|[:J)      @XHc s T 5 E  &        _   ?3/3_]2_]?333?/]222]/]q2]2293301]]]]+]]]q]]]]]]]]]qq!##3333##46767#򚹰lJI?9Xw4=GIsJ$@$$$$$$i$Z$H$)$9$$ $UeF7&   I$$I{$$P??3/3_]3?333/]32/]293301_]]]]]]]]]]]]]]]]]]]]]]]3###.'#3>7{{{ǃ  JP}EJDy>IKJp"QK>=JN",!i&$6R&%+5+5&D6@ $&%'"%+5+5!+&$jR&%+55+55&Dj @ &&-(,%+55+55-^#i&(6R& %+5+5H&H6@ & %+5+5T{)?@$$[+%[ 0/%_ __ ?32?9//]3/]201"5>32#"&&55!.2>7!#oO&P]oCٓKIЇz7b7d0M}Z6c#Kw),  l髮hgRzGfA~xuDH^'I@+"H)#H#PP Q?22?9//^]q3/]q2102#".55!&&#"5>267!-wƏOHolx@8aYT,+SW`d Bg^J҇֔OGnq  %DqP,T{+&jR@ .&04%+55+55H&j@ ,&.2%+55+55+&jR& %+55+55&j&%+55+55/+&jR(&*. %+55+55&j%&'+%+55+55B K@)WF[ [ "  _a` ?32?9/2/]3/9/3/01]]#"&'532654&##5!5!JsGC͉nU+`cc.Ϻ{=;6df`t@"-$ Ѧ;J$F@& J$$J F&!  N!P"P ?32?9/2/]39/]3/01#"&'732>54.##5!5!HhNL{z>!Tbo;M\3;l[w=@rlnK$"0YNV{P%}횅F&MR@ &%+5+5-o&M@ & %+5+5F+&jR@ &%+55+55-&j@ & %+55+55T{+&2jR@ &%+55+55sX&Rj& %+55+55T{~sX^T{+&~jR@ .&04 %+55+55sX&j#&%) %+55+55DL+&jR&"%+55+55%&j&Ǵ! %+55+55&MR@ &%+5+5Ryo&\M@ & %+5+5+&jR&"%+55+55Ry&\j@ &! %+55+55s&SRR@ &R#%+55+55Ry!&\S^@ &^" %+55+55F+&jR@ &%+55+55%&j@ &%+55+55+d 8@# Z 0_ _?3/?/]q2/10!3##d#J .@ FGPP?3/?/]2/01!!!3##ˠJ}`m+&jR@ & %+55+55qZ&j@ &%+55+55mdH@& Z___` /??9/32//322/9/]01!!!3#"&'532655##53dPn}'?:"/1#ل 11mJH@% I GP O  P Q??9/32/32/322//9/01!#53!!!!3#"&'532655#n~&?;".2ل 115m&; %+]55`mwJ&[^P 6^ %++]555 @ H  &   H  ) @ H& H )   @ @ _  ?3?39/3322/833/83/8399//3/83933333]+]+]+]+01##!5!33!KNL{`/`jJ·@$ H   H H@ HI  H  F@7H       O ?3?39/3322/]33/3/]33/393333399//01]+]]+]+_]+]+]+]!33!!##!)"#-rs{f1L*@Z [_` ??9//]2014>333!".#"337Ϙ~=][.WqBhJ=p_33332>5#"32>5CmO\m"#za1hpRAD,XPB^<N^0<# PTe7WWO]gr<hrg3S<CkM#@!(F%22F?;F8P&-P ?322?9/?2/]9/3901%2653#"&'##"4>323&'&&53!2>754.#"XB;"DeCn"+54.#"'>323#Ż8g]\a2%D_:nK\&btHmp93[}KW^2DcA 'He=6S:C6}6)6aOIxX9  9YwHP\^/N@)$FG++/F,10P$P0--+P?3/?39/]923/3/32/3/201!4.##532>54&#"'66323#>fK>dH'/RPP-?]scq=3G+6T:+/I1'@-MM '%$HjF5Q;) ,B[>}0S@. 0 00 #&&2 _+$$_` ?32/?9/?/]99//]9901##"'532>7667!32653#".5-  0N>3';BD9*NmDCnP,tQgn: <]q6;9сww-ib.,`lJ)I@(IF))) F+ P O$ ?3323?9//]99//]10##"&'532667!32653#".5- &0:E)1%=0% 7HA:$InJGqO*ՑY'uo=}{5ib.%WhyH@*8   G__???39/3/]]2]]9/30132653#".5!#3!3>EE:%JoKKnH$ꦦww-ib.,`l-Vh{{JP@1IGW IHXIP P?2?9/?/]2]]9/]301!332653#".55!#"7H?:#FiGClL(ޢJ9=5ib.%WhJuo(I@*(( 'Z*[  _(_/_@H"_??39/+]2/]2/39/01!#"&&546632&&#"32>5!4oyҍHP؈sKH:^Vd7-YWRmBIii,*"2QډٚU7osq\^(A@#(( ?'G*H  N(P"N??39/2/]3/]39/01!#".54>32&&#"32>5!{2qȄAGЊoM=<[[\/%S^QnBJDȉGOӄҔP%'-:mb]p>-PqC@@&Z  0Z0__??9/2/]]/]]0132653#".5!5!!qR\\R2]QP_5^_ww-ib.']s)oJ<@# GG PP?2?9//]]/]015!!32653#".5)51P;i_/_`[f7AbA 5ib..bi5D;I@'1'' =Z [116a&#`', ` ?32?329/]9/]3/22/9/014>32.#"33#"3267#".54>75.:H+) O&##$7P ,P N ?2?]2?9/32?/]3/]2/2222229/01!5!##".54>323&'&&55!5!533##%2>754.#"/h=Nb>aq>>qa=bO= {OmC DoTK"=.F̆΋G,: !7+,YZ'af4! 0@_/22/3/]3/3/01#.'53#.'53d"RM@+.0e"RM?+.0SXQ"QQLSXQ"QQLD@#?Oo @ /22/33/39333/]3/01#&&'#56673'>73#+d3l46j3e3t00t3N -j7e"T00T";EE;,.4!3232673-3t00t3e3j64l3d$HE?(*\0B)&JE>(*\0B;EE;"T00T"Z"*20Q;!"+10Q;!9 -@  @  /22//3/]01>73#%#"&'332>7% -j8dn,LnGm0D+$B3",.5!7e8j-!,LnGm0D+$B3",g<!5.,7'#'6654&#"56632,LnGm0D+$B3"+#-V 8A9+' &cd7'".#"#>3232673,LnGm0C,$A3"/$HE?(*\0B)&JE>(*\0B \v \ T.Digitized data copyright 2007, Google Corporation.Droid Sans MonoRegularAscender - Droid Sans MonoVersion 1.00DroidSansMonoDroid is a trademark of Google and may be registered in certain jurisdictions.Ascender CorporationDroid Sans is a humanist sans serif typeface designed for user interfaces and electronic communication.Licensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0Digitized data copyright 2007, Google Corporation.Droid Sans MonoRegularAscender - Droid Sans MonoVersion 1.00 build 112DroidSansMonoDroid is a trademark of Google and may be registered in certain jurisdictions.Ascender CorporationDroid Sans is a humanist sans serif typeface designed for user interfaces and electronic communication.http://www.ascendercorp.com/http://www.ascendercorp.com/typedesigners.htmlLicensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0ff  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~.nulluni00AD overscoreperiodcenteredAmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflexCdotcdotDcarondcaronDcroatdcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflexGdotgdot Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonek IdotaccentIJij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflex Tcommaaccent tcommaaccentTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongs Aringacute aringacuteAEacuteaeacute Oslashacute oslashacute Scommaaccent scommaaccentmacrontonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsiuni03A9 IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonos afii10023 afii10051 afii10052 afii10053 afii10054 afii10055 afii10056 afii10057 afii10058 afii10059 afii10060 afii10061 afii10062 afii10145 afii10017 afii10018 afii10019 afii10020 afii10021 afii10022 afii10024 afii10025 afii10026 afii10027 afii10028 afii10029 afii10030 afii10031 afii10032 afii10033 afii10034 afii10035 afii10036 afii10037 afii10038 afii10039 afii10040 afii10041 afii10042 afii10043 afii10044 afii10045 afii10046 afii10047 afii10048 afii10049 afii10065 afii10066 afii10067 afii10068 afii10069 afii10070 afii10072 afii10073 afii10074 afii10075 afii10076 afii10077 afii10078 afii10079 afii10080 afii10081 afii10082 afii10083 afii10084 afii10085 afii10086 afii10087 afii10088 afii10089 afii10090 afii10091 afii10092 afii10093 afii10094 afii10095 afii10096 afii10097 afii10071 afii10099 afii10100 afii10101 afii10102 afii10103 afii10104 afii10105 afii10106 afii10107 afii10108 afii10109 afii10110 afii10193 afii10050 afii10098WgravewgraveWacutewacute Wdieresis wdieresisYgraveygrave afii00208 underscoredbl quotereversedminutesecond exclamdbl nsuperior afii08941pesetaEuro afii61248 afii61289 afii61352 estimated oneeighth threeeighths fiveeighths seveneighthsDeltauniFB01uniFB02 cyrillicbrevedotlessjcaroncommaaccent commaaccentcommaaccentrotate zerosuperior foursuperior fivesuperior sixsuperior sevensuperior eightsuperior ninesuperioruni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni200BuniFEFFuniFFFCuniFFFDuni01F0uni02BCuni03D1uni03D2uni03D6uni1E3Euni1E3Funi1E00uni1E01uni1F4Duni02F3 dasiaoxiauniFB03uniFB04OhornohornUhornuhornuni0300uni0301uni0303hookdotbelowuni0400uni040Duni0450uni045Duni0460uni0461uni0462uni0463uni0464uni0465uni0466uni0467uni0468uni0469uni046Auni046Buni046Cuni046Duni046Euni046Funi0470uni0471uni0472uni0473uni0474uni0475uni0476uni0477uni0478uni0479uni047Auni047Buni047Cuni047Duni047Euni047Funi0480uni0481uni0482uni0483uni0484uni0485uni0486uni0488uni0489uni048Auni048Buni048Cuni048Duni048Euni048Funi0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7uni04A8uni04A9uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BFuni04C0uni04C1uni04C2uni04C3uni04C4uni04C5uni04C6uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04CDuni04CEuni04CFuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04D9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni04FAuni04FBuni04FCuni04FDuni04FEuni04FFuni0500uni0501uni0502uni0503uni0504uni0505uni0506uni0507uni0508uni0509uni050Auni050Buni050Cuni050Duni050Euni050Funi0510uni0511uni0512uni0513uni1EA0uni1EA1uni1EA2uni1EA3uni1EA4uni1EA5uni1EA6uni1EA7uni1EA8uni1EA9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1EB2uni1EB3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1EBAuni1EBBuni1EBCuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1EC2uni1EC3uni1EC4uni1EC5uni1EC6uni1EC7uni1EC8uni1EC9uni1ECAuni1ECBuni1ECCuni1ECDuni1ECEuni1ECFuni1ED0uni1ED1uni1ED2uni1ED3uni1ED4uni1ED5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EDEuni1EDFuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE6uni1EE7uni1EE8uni1EE9uni1EEAuni1EEBuni1EECuni1EEDuni1EEEuni1EEFuni1EF0uni1EF1uni1EF4uni1EF5uni1EF6uni1EF7uni1EF8uni1EF9uni20ABuni030Fcircumflexacutecombcircumflexgravecombcircumflexhookcombcircumflextildecombbreveacutecombbrevegravecomb brevehookcombbrevetildecombcyrillichookleftcyrillicbighookUCcyrillicbighookLCopenimageio-1.7.17~dfsg0.orig/src/fonts/Droid_Sans_Mono/LICENSE.txt0000644000175000017500000002613613151711064023115 0ustar mfvmfv Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. openimageio-1.7.17~dfsg0.orig/src/ffmpeg.imageio/0000755000175000017500000000000013151711064017771 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/ffmpeg.imageio/CMakeLists.txt0000644000175000017500000000136313151711064022534 0ustar mfvmfv# Make the build complete for newer ffmpeg versions (3.1.1+) that have # marked m_format_context->streams[i]->codec as deprecated. # FIXME -- at some point, come back and figure out how to fix for real # before the field disappears entirely. if (NOT MSVC) set_source_files_properties (ffmpeginput.cpp PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations") endif() if (USE_FFMPEG AND FFMPEG_FOUND) add_oiio_plugin (ffmpeginput.cpp ffmpegoutput.cpp INCLUDE_DIRS ${FFMPEG_INCLUDE_DIR} LINK_LIBRARIES ${FFMPEG_LIBRARIES} ${BZIP2_LIBRARIES} DEFINITIONS "-DUSE_FFMPEG") else() message (STATUS "FFmpeg not found: ffmpeg plugin will not be built") endif() openimageio-1.7.17~dfsg0.orig/src/ffmpeg.imageio/ffmpegoutput.cpp0000644000175000017500000000614013151711064023223 0ustar mfvmfv/* Copyright 2014 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "OpenImageIO/imageio.h" OIIO_PLUGIN_NAMESPACE_BEGIN class FFmpegOutput : public ImageOutput { public: FFmpegOutput () { init (); } virtual ~FFmpegOutput () { close (); } virtual const char *format_name (void) const { return "mov"; } virtual int supports (string_view feature) const; virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode); virtual bool close (void); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); private: FILE *m_fd; std::string m_filename; void init (void) { m_fd = NULL; m_filename.clear (); } }; // Obligatory material to make this a recognizeable imageio plugin OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *ffmpeg_output_imageio_create () { return new FFmpegOutput; } OIIO_EXPORT const char *ffmpeg_output_extensions[] = { "mov", NULL }; OIIO_PLUGIN_EXPORTS_END int FFmpegOutput::supports (string_view feature) const { // Everything else, we either don't support or don't know about return false; } bool FFmpegOutput::open (const std::string &name, const ImageSpec &spec, OpenMode mode) { return true; } bool FFmpegOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { return false; } inline bool FFmpegOutput::close (void) { return true; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/ffmpeg.imageio/ffmpeginput.cpp0000644000175000017500000004400313151711064023022 0ustar mfvmfv/* Copyright 2014 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ extern "C" { // ffmpeg is a C api #include #include #include #include #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57,24,0) # include #endif } #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1) # define av_frame_alloc avcodec_alloc_frame //Ancient versions used av_freep # if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54,59,100) # define av_frame_free av_freep # else # define av_frame_free avcodec_free_frame # endif #endif // PIX_FMT was renamed to AV_PIX_FMT on this version #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51,74,100) # define AVPixelFormat PixelFormat # define AV_PIX_FMT_RGB24 PIX_FMT_RGB24 # define AV_PIX_FMT_RGB48 PIX_FMT_RGB48 # define AV_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P # define AV_PIX_FMT_YUVJ422P PIX_FMT_YUVJ422P # define AV_PIX_FMT_YUVJ440P PIX_FMT_YUVJ440P # define AV_PIX_FMT_YUVJ444P PIX_FMT_YUVJ444P # define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P # define AV_PIX_FMT_YUV422P PIX_FMT_YUV422P # define AV_PIX_FMT_YUV440P PIX_FMT_YUV440P # define AV_PIX_FMT_YUV444P PIX_FMT_YUV444P #endif // r_frame_rate deprecated in ffmpeg // see ffmpeg commit #aba232c for details #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,42,0) # define r_frame_rate avg_frame_rate #endif // Changes for ffmpeg 3.0 #define USE_FFMPEG_3_0 (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57,24,0)) #if USE_FFMPEG_3_0 # define av_free_packet av_packet_unref # define avpicture_get_size(fmt,w,h) av_image_get_buffer_size(fmt,w,h,1) inline int avpicture_fill(AVPicture *picture, uint8_t *ptr, enum AVPixelFormat pix_fmt, int width, int height) { AVFrame *frame = reinterpret_cast(picture); return av_image_fill_arrays(frame->data, frame->linesize, ptr, pix_fmt, width, height, 1); } #endif // Changes for ffmpeg 3.1 #define USE_FFMPEG_3_1 (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)) #if USE_FFMPEG_3_1 // AVStream::codec was changed to AVStream::codecpar # define stream_codec(ix) m_format_context->streams[(ix)]->codecpar // avcodec_decode_video2 was deprecated. // This now works by sending `avpkt` to the decoder, which buffers the // decoded image in `avctx`. Then `avcodec_receive_frame` will copy the // frame to `picture`. inline int receive_frame(AVCodecContext *avctx, AVFrame *picture, AVPacket *avpkt) { int ret; ret = avcodec_send_packet(avctx, avpkt); if (ret < 0) return 0; ret = avcodec_receive_frame(avctx, picture); if (ret < 0) return 0; return 1; } #else # define stream_codec(ix) m_format_context->streams[(ix)]->codec inline int receive_frame(AVCodecContext *avctx, AVFrame *picture, AVPacket *avpkt) { int ret; avcodec_decode_video2(avctx, picture, &ret, avpkt); return ret; } #endif #include #include "OpenImageIO/imageio.h" #include OIIO_PLUGIN_NAMESPACE_BEGIN class FFmpegInput : public ImageInput { public: FFmpegInput (); virtual ~FFmpegInput(); virtual const char *format_name (void) const { return "FFmpeg movie"; } virtual bool open (const std::string &name, ImageSpec &spec); virtual bool close (void); virtual int current_subimage (void) const { return m_subimage; } virtual bool seek_subimage (int subimage, int miplevel, ImageSpec &newspec); virtual bool read_native_scanline (int y, int z, void *data); void read_frame(int pos); #if 0 const char *metadata (const char * key); bool has_metadata (const char * key); #endif bool seek (int pos); double fps() const; int64_t time_stamp(int pos) const; private: std::string m_filename; int m_subimage; int64_t m_nsubimages; AVFormatContext * m_format_context; AVCodecContext * m_codec_context; AVCodec *m_codec; AVFrame *m_frame; AVFrame *m_rgb_frame; size_t m_stride; AVPixelFormat m_dst_pix_format; SwsContext *m_sws_rgb_context; AVRational m_frame_rate; std::vector m_rgb_buffer; std::vector m_video_indexes; int m_video_stream; int64_t m_frames; int m_last_search_pos; int m_last_decoded_pos; bool m_offset_time; bool m_codec_cap_delay; bool m_read_frame; int64_t m_start_time; // init to initialize state void init (void) { m_filename.clear (); m_format_context = 0; m_codec_context = 0; m_codec = 0; m_frame = 0; m_rgb_frame = 0; m_sws_rgb_context = 0; m_stride = 0; m_rgb_buffer.clear(); m_video_indexes.clear(); m_video_stream = -1; m_frames = 0; m_last_search_pos = 0; m_last_decoded_pos = 0; m_offset_time = true; m_read_frame = false; m_codec_cap_delay = false; m_subimage = 0; m_start_time = 0; } }; // Obligatory material to make this a recognizeable imageio plugin OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT int ffmpeg_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* ffmpeg_imageio_library_version () { return "FFMpeg " LIBAVFORMAT_IDENT; } OIIO_EXPORT ImageInput *ffmpeg_input_imageio_create () { return new FFmpegInput; } // FFmpeg hints: // AVI (Audio Video Interleaved) // QuickTime / MOV // raw MPEG-4 video // MPEG-1 Systems / MPEG program stream OIIO_EXPORT const char *ffmpeg_input_extensions[] = { "avi", "mov", "qt", "mp4", "m4a", "3gp", "3g2", "mj2", "m4v", "mpg", NULL }; OIIO_PLUGIN_EXPORTS_END FFmpegInput::FFmpegInput () { init(); } FFmpegInput::~FFmpegInput() { close(); } bool FFmpegInput::open (const std::string &name, ImageSpec &spec) { // Temporary workaround: refuse to open a file whose name does not // indicate that it's a movie file. This avoids the problem that ffmpeg // is willing to open tiff and other files better handled by other // plugins. The better long-term solution is to replace av_register_all // with our own function that registers only the formats that we want // this reader to handle. At some point, we will institute that superior // approach, but in the mean time, this is a quick solution that 90% // does the job. bool valid_extension = false; for (int i = 0; ffmpeg_input_extensions[i]; ++i) if (Strutil::ends_with (name, ffmpeg_input_extensions[i])) { valid_extension = true; break; } if (! valid_extension) { error ("\"%s\" could not open input", name); return false; } static boost::once_flag init_flag = BOOST_ONCE_INIT; boost::call_once (&av_register_all, init_flag); const char *file_name = name.c_str(); av_log_set_level (AV_LOG_FATAL); if (avformat_open_input (&m_format_context, file_name, NULL, NULL) != 0) // avformat_open_input allocs format_context { error ("\"%s\" could not open input", file_name); return false; } if (avformat_find_stream_info (m_format_context, NULL) < 0) { error ("\"%s\" could not find stream info", file_name); return false; } m_video_stream = -1; for (unsigned int i=0; inb_streams; i++) { if (stream_codec(i)->codec_type == AVMEDIA_TYPE_VIDEO) { if (m_video_stream < 0) { m_video_stream=i; } m_video_indexes.push_back (i); // needed for later use break; } } if (m_video_stream == -1) { error ("\"%s\" could not find a valid videostream", file_name); return false; } // codec context for videostream #if USE_FFMPEG_3_1 AVCodecParameters *par = stream_codec(m_video_stream); m_codec = avcodec_find_decoder(par->codec_id); if (!m_codec) { error ("\"%s\" can't find decoder", file_name); return false; } m_codec_context = avcodec_alloc_context3(m_codec); if (!m_codec_context) { error ("\"%s\" can't allocate decoder context", file_name); return false; } int ret; ret = avcodec_parameters_to_context(m_codec_context, par); if (ret < 0) { error ("\"%s\" unsupported codec", file_name); return false; } #else m_codec_context = stream_codec(m_video_stream); m_codec = avcodec_find_decoder (m_codec_context->codec_id); if (!m_codec) { error ("\"%s\" unsupported codec", file_name); return false; } #endif if (avcodec_open2 (m_codec_context, m_codec, NULL) < 0) { error ("\"%s\" could not open codec", file_name); return false; } if (!strcmp (m_codec_context->codec->name, "mjpeg") || !strcmp (m_codec_context->codec->name, "dvvideo")) { m_offset_time = false; } m_codec_cap_delay = (bool) (m_codec_context->codec->capabilities & CODEC_CAP_DELAY); AVStream *stream = m_format_context->streams[m_video_stream]; if (stream->r_frame_rate.num != 0 && stream->r_frame_rate.den != 0) { m_frame_rate = stream->r_frame_rate; } m_frames = stream->nb_frames; m_start_time = stream->start_time; if (!m_frames) { seek (0); AVPacket pkt; av_init_packet (&pkt); av_read_frame (m_format_context, &pkt); int64_t first_pts = pkt.pts; int64_t max_pts = 0 ; av_free_packet (&pkt); //because seek(int) uses m_format_context seek (1 << 29); av_init_packet (&pkt); //Is this needed? while (stream && av_read_frame (m_format_context, &pkt) >= 0) { int64_t current_pts = static_cast (av_q2d(stream->time_base) * (pkt.pts - first_pts) * fps()); if (current_pts > max_pts) { max_pts = current_pts +1; } av_free_packet (&pkt); //Always free before format_context usage } m_frames = max_pts; } m_frame = av_frame_alloc(); m_rgb_frame = av_frame_alloc(); AVPixelFormat src_pix_format; switch (m_codec_context->pix_fmt) { // deprecation warning for YUV formats case AV_PIX_FMT_YUVJ420P: src_pix_format = AV_PIX_FMT_YUV420P; break; case AV_PIX_FMT_YUVJ422P: src_pix_format = AV_PIX_FMT_YUV422P; break; case AV_PIX_FMT_YUVJ444P: src_pix_format = AV_PIX_FMT_YUV444P; break; case AV_PIX_FMT_YUVJ440P: src_pix_format = AV_PIX_FMT_YUV440P; break; default: src_pix_format = m_codec_context->pix_fmt; break; } m_spec = ImageSpec (m_codec_context->width, m_codec_context->height, 3); switch (src_pix_format) { // support for 10-bit and 12-bit pix_fmts case AV_PIX_FMT_YUV420P10BE: case AV_PIX_FMT_YUV420P10LE: case AV_PIX_FMT_YUV422P10BE: case AV_PIX_FMT_YUV422P10LE: case AV_PIX_FMT_YUV444P10BE: case AV_PIX_FMT_YUV444P10LE: case AV_PIX_FMT_YUV420P12BE: case AV_PIX_FMT_YUV420P12LE: case AV_PIX_FMT_YUV422P12BE: case AV_PIX_FMT_YUV422P12LE: case AV_PIX_FMT_YUV444P12BE: case AV_PIX_FMT_YUV444P12LE: m_spec.set_format (TypeDesc::UINT16); m_dst_pix_format = AV_PIX_FMT_RGB48; m_stride = (size_t) (m_spec.width * 3 * 2); break; default: m_spec.set_format (TypeDesc::UINT8); m_dst_pix_format = AV_PIX_FMT_RGB24; m_stride = (size_t) (m_spec.width * 3); break; } m_rgb_buffer.resize( avpicture_get_size (m_dst_pix_format, m_codec_context->width, m_codec_context->height), 0 ); m_sws_rgb_context = sws_getContext( m_codec_context->width, m_codec_context->height, src_pix_format, m_codec_context->width, m_codec_context->height, m_dst_pix_format, SWS_AREA, NULL, NULL, NULL ); AVDictionaryEntry *tag = NULL; while ((tag = av_dict_get (m_format_context->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { m_spec.attribute (tag->key, tag->value); } m_spec.attribute ("FramesPerSecond", m_frame_rate.num / static_cast (m_frame_rate.den)); m_spec.attribute ("oiio:Movie", true); m_spec.attribute ("oiio:BitsPerSample", m_codec_context->bits_per_raw_sample); m_nsubimages = m_frames; spec = m_spec; return true; } bool FFmpegInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec) { if (subimage < 0 || subimage >= m_nsubimages || miplevel > 0) { return false; } if (subimage == m_subimage) { newspec = m_spec; return true; } newspec = m_spec; m_subimage = subimage; m_read_frame = false; return true; } bool FFmpegInput::read_native_scanline (int y, int z, void *data) { if (!m_read_frame) { read_frame (m_subimage); } memcpy (data, m_rgb_frame->data[0] + y * m_rgb_frame->linesize[0], m_stride); return true; } bool FFmpegInput::close (void) { if (m_codec_context) avcodec_close (m_codec_context); if (m_format_context) avformat_close_input (&m_format_context); av_free (m_format_context); // will free m_codec and m_codec_context av_frame_free (&m_frame); // free after close input av_frame_free (&m_rgb_frame); sws_freeContext (m_sws_rgb_context); init (); return true; } void FFmpegInput::read_frame(int frame) { if (m_last_decoded_pos + 1 != frame ) { seek (frame); } AVPacket pkt; int finished = 0; int ret = 0; while ((ret = av_read_frame (m_format_context, &pkt)) == 0 || m_codec_cap_delay ) { if (pkt.stream_index == m_video_stream) { if (ret < 0 && m_codec_cap_delay) { pkt.data = NULL; pkt.size = 0; } finished = receive_frame(m_codec_context, m_frame, &pkt); double pts = 0; if (static_cast(m_frame->pkt_pts) != int64_t(AV_NOPTS_VALUE)) { pts = av_q2d (m_format_context->streams[m_video_stream]->time_base) * m_frame->pkt_pts; } int current_frame = int((pts-m_start_time) * fps() + 0.5f); //??? //current_frame = m_frame->display_picture_number; m_last_search_pos = current_frame; if( current_frame == frame && finished) { avpicture_fill ( reinterpret_cast(m_rgb_frame), &m_rgb_buffer[0], m_dst_pix_format, m_codec_context->width, m_codec_context->height ); sws_scale ( m_sws_rgb_context, static_cast (m_frame->data), m_frame->linesize, 0, m_codec_context->height, m_rgb_frame->data, m_rgb_frame->linesize ); m_last_decoded_pos = current_frame; av_free_packet (&pkt); break; } } av_free_packet (&pkt); } m_read_frame = true; } #if 0 const char * FFmpegInput::metadata (const char * key) { AVDictionaryEntry * entry = av_dict_get (m_format_context->metadata, key, NULL, 0); return entry ? av_strdup(entry->value) : NULL; // FIXME -- that looks suspiciously like a memory leak } bool FFmpegInput::has_metadata (const char * key) { return av_dict_get (m_format_context->metadata, key, NULL, 0); // is there a better to check exists? } #endif bool FFmpegInput::seek (int frame) { int64_t offset = time_stamp (frame); int flags = AVSEEK_FLAG_BACKWARD; avcodec_flush_buffers (m_codec_context); av_seek_frame (m_format_context, -1, offset, flags ); return true; } int64_t FFmpegInput::time_stamp(int frame) const { int64_t timestamp = static_cast((static_cast (frame) / (fps() * av_q2d(m_format_context->streams[m_video_stream]->time_base))) ); if (static_cast(m_format_context->start_time) != int64_t(AV_NOPTS_VALUE)) { timestamp += static_cast(static_cast (m_format_context->start_time)*AV_TIME_BASE/av_q2d(m_format_context->streams[m_video_stream]->time_base)); } return timestamp; } double FFmpegInput::fps() const { if (m_frame_rate.den) { return av_q2d(m_frame_rate); } return 1.0f; } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/openexr.imageio/0000755000175000017500000000000013151711064020205 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/openexr.imageio/CMakeLists.txt0000644000175000017500000000014313151711064022743 0ustar mfvmfvadd_oiio_plugin (exrinput.cpp exroutput.cpp LINK_LIBRARIES ${OPENEXR_LIBRARIES}) openimageio-1.7.17~dfsg0.orig/src/openexr.imageio/exroutput.cpp0000644000175000017500000020322413151711064022773 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include #include #include #include // The way that OpenEXR uses dynamic casting for attributes requires // temporarily suspending "hidden" symbol visibility mode. #ifdef __GNUC__ #pragma GCC visibility push(default) #endif #include #include #include #include #include #include #include #include #include #include #include #include // JUST to get symbols to figure out version! #include #include #ifdef __GNUC__ #pragma GCC visibility pop #endif #ifdef IMF_B44_COMPRESSION #define OPENEXR_VERSION_IS_1_6_OR_LATER #endif #ifdef USE_OPENEXR_VERSION2 #include #include #include #include #include #include #include #include #endif #include "OpenImageIO/dassert.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/deepdata.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/sysutil.h" #include "OpenImageIO/fmath.h" OIIO_PLUGIN_NAMESPACE_BEGIN // Custom file output stream, copying code from the class StdOFStream in // OpenEXR, which would have been used if we just provided a // filename. The difference is that this can handle UTF-8 file paths on // all platforms. class OpenEXROutputStream : public Imf::OStream { public: OpenEXROutputStream (const char *filename) : Imf::OStream(filename) { // The reason we have this class is for this line, so that we // can correctly handle UTF-8 file paths on Windows Filesystem::open (ofs, filename, std::ios_base::binary); if (!ofs) Iex::throwErrnoExc (); } virtual void write (const char c[], int n) { errno = 0; ofs.write (c, n); check_error (); } virtual Imath::Int64 tellp () { return std::streamoff (ofs.tellp ()); } virtual void seekp (Imath::Int64 pos) { ofs.seekp (pos); check_error (); } private: void check_error () { if (!ofs) { if (errno) Iex::throwErrnoExc (); throw Iex::ErrnoExc ("File output failed."); } } OIIO::ofstream ofs; }; class OpenEXROutput : public ImageOutput { public: OpenEXROutput (); virtual ~OpenEXROutput (); virtual const char * format_name (void) const { return "openexr"; } virtual int supports (string_view feature) const; virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode=Create); virtual bool open (const std::string &name, int subimages, const ImageSpec *specs); virtual bool close (); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool write_scanlines (int ybegin, int yend, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride); virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); virtual bool write_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); virtual bool write_deep_scanlines (int ybegin, int yend, int z, const DeepData &deepdata); virtual bool write_deep_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, const DeepData &deepdata); private: OpenEXROutputStream *m_output_stream; ///< Stream for output file Imf::OutputFile *m_output_scanline; ///< Input for scanline files Imf::TiledOutputFile *m_output_tiled; ///< Input for tiled files #ifdef USE_OPENEXR_VERSION2 Imf::MultiPartOutputFile *m_output_multipart; Imf::OutputPart *m_scanline_output_part; Imf::TiledOutputPart *m_tiled_output_part; Imf::DeepScanLineOutputPart *m_deep_scanline_output_part; Imf::DeepTiledOutputPart *m_deep_tiled_output_part; #else char *m_output_multipart; char *m_scanline_output_part; char *m_tiled_output_part; char *m_deep_scanline_output_part; char *m_deep_tiled_output_part; #endif int m_levelmode; ///< The level mode of the file int m_roundingmode; ///< Rounding mode of the file int m_subimage; ///< What subimage we're writing now int m_nsubimages; ///< How many subimages are there? int m_miplevel; ///< What miplevel we're writing now int m_nmiplevels; ///< How many mip levels are there? std::vector m_pixeltype; ///< Imf pixel type for each ///< channel of current subimage std::vector m_scratch; ///< Scratch space for us to use std::vector m_subimagespecs; ///< Saved subimage specs std::vector m_headers; // Initialize private members to pre-opened state void init (void) { m_output_stream = NULL; m_output_scanline = NULL; m_output_tiled = NULL; m_output_multipart = NULL; m_scanline_output_part = NULL; m_tiled_output_part = NULL; m_deep_scanline_output_part = NULL; m_deep_tiled_output_part = NULL; m_subimage = -1; m_miplevel = -1; std::vector().swap (m_subimagespecs); // clear and free std::vector().swap (m_headers); } // Set up the header based on the given spec. Also may doctor the // spec a bit. bool spec_to_header (ImageSpec &spec, int subimage, Imf::Header &header); // Fill in m_pixeltype based on the spec void compute_pixeltypes (const ImageSpec &spec); // Add a parameter to the output bool put_parameter (const std::string &name, TypeDesc type, const void *data, Imf::Header &header); // Decode the IlmImf MIP parameters from the spec. static void figure_mip (const ImageSpec &spec, int &nmiplevels, int &levelmode, int &roundingmode); // Helper: if the channel names are nonsensical, fix them to keep the // app from shooting itself in the foot. void sanity_check_channelnames (); }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput * openexr_output_imageio_create () { return new OpenEXROutput; } OIIO_EXPORT int openexr_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* openexr_imageio_library_version () { #ifdef OPENEXR_PACKAGE_STRING return OPENEXR_PACKAGE_STRING; #else return "OpenEXR 1.x"; #endif } OIIO_EXPORT const char * openexr_output_extensions[] = { "exr", "sxr", "mxr", NULL }; OIIO_PLUGIN_EXPORTS_END static std::string format_string ("openexr"); namespace pvt { void set_exr_threads (); // format-specific metadata prefixes static std::vector format_prefixes; static atomic_int format_prefixes_initialized; static spin_mutex format_prefixes_mutex; // guard } OpenEXROutput::OpenEXROutput () { pvt::set_exr_threads (); init (); } OpenEXROutput::~OpenEXROutput () { // Close, if not already done. close (); delete m_output_scanline; m_output_scanline = NULL; delete m_output_tiled; m_output_tiled = NULL; delete m_scanline_output_part; m_scanline_output_part = NULL; delete m_tiled_output_part; m_tiled_output_part = NULL; delete m_deep_scanline_output_part; m_deep_scanline_output_part = NULL; delete m_deep_tiled_output_part; m_deep_tiled_output_part = NULL; delete m_output_multipart; m_output_multipart = NULL; delete m_output_stream; m_output_stream = NULL; } int OpenEXROutput::supports (string_view feature) const { if (feature == "tiles") return true; if (feature == "mipmap") return true; if (feature == "alpha") return true; if (feature == "nchannels") return true; if (feature == "channelformats") return true; if (feature == "displaywindow") return true; if (feature == "origin") return true; if (feature == "negativeorigin") return true; if (feature == "arbitrary_metadata") return true; if (feature == "exif") // Because of arbitrary_metadata return true; if (feature == "iptc") // Because of arbitrary_metadata return true; #ifdef USE_OPENEXR_VERSION2 if (feature == "multiimage") return true; // N.B. But OpenEXR does not support "appendsubimage" if (feature == "deepdata") return true; #endif // EXR supports random write order iff lineOrder is set to 'random Y' // and it's a tiled file. if (feature == "random_access" && m_spec.tile_width != 0) { const ImageIOParameter *param = m_spec.find_attribute("openexr:lineOrder"); const char *lineorder = param ? *(char **)param->data() : NULL; return (lineorder && Strutil::iequals (lineorder, "randomY")); } // FIXME: we could support "empty" // Everything else, we either don't support or don't know about return false; } bool OpenEXROutput::open (const std::string &name, const ImageSpec &userspec, OpenMode mode) { if (mode == Create) { if (userspec.deep) // Fall back on multi-part OpenEXR for deep files return open (name, 1, &userspec); m_nsubimages = 1; m_subimage = 0; m_nmiplevels = 1; m_miplevel = 0; m_headers.resize (1); m_spec = userspec; // Stash the spec sanity_check_channelnames (); if (! spec_to_header (m_spec, m_subimage, m_headers[m_subimage])) return false; try { m_output_stream = new OpenEXROutputStream (name.c_str()); if (m_spec.tile_width) { m_output_tiled = new Imf::TiledOutputFile (*m_output_stream, m_headers[m_subimage]); } else { m_output_scanline = new Imf::OutputFile (*m_output_stream, m_headers[m_subimage]); } } catch (const std::exception &e) { error ("OpenEXR exception: %s", e.what()); m_output_scanline = NULL; m_output_tiled = NULL; return false; } catch (...) { // catch-all for edge cases or compiler bugs error ("OpenEXR exception: unknown"); m_output_scanline = NULL; m_output_tiled = NULL; return false; } if (! m_output_scanline && ! m_output_tiled) { error ("Unknown error opening EXR file"); return false; } return true; } if (mode == AppendSubimage) { #ifdef USE_OPENEXR_VERSION2 // OpenEXR 2.x supports subimages, but we only allow it to use the // open(name,subimages,specs[]) variety. if (m_subimagespecs.size() == 0 || ! m_output_multipart) { error ("%s not opened properly for subimages", format_name()); return false; } // Move on to next subimage ++m_subimage; if (m_subimage >= m_nsubimages) { error ("More subimages than originally declared."); return false; } // Close the current subimage, open the next one try { if (m_tiled_output_part) { delete m_tiled_output_part; m_tiled_output_part = new Imf::TiledOutputPart (*m_output_multipart, m_subimage); } else if (m_scanline_output_part) { delete m_scanline_output_part; m_scanline_output_part = new Imf::OutputPart (*m_output_multipart, m_subimage); } else if (m_deep_tiled_output_part) { delete m_deep_tiled_output_part; m_deep_tiled_output_part = new Imf::DeepTiledOutputPart (*m_output_multipart, m_subimage); } else if (m_deep_scanline_output_part) { delete m_deep_scanline_output_part; m_deep_scanline_output_part = new Imf::DeepScanLineOutputPart (*m_output_multipart, m_subimage); } else { error ("Called open with AppendSubimage mode, but no appropriate part is found. Application bug?"); return false; } } catch (const std::exception &e) { error ("OpenEXR exception: %s", e.what()); m_scanline_output_part = NULL; m_tiled_output_part = NULL; m_deep_scanline_output_part = NULL; m_deep_tiled_output_part = NULL; return false; } catch (...) { // catch-all for edge cases or compiler bugs error ("OpenEXR exception: unknown exception"); m_scanline_output_part = NULL; m_tiled_output_part = NULL; m_deep_scanline_output_part = NULL; m_deep_tiled_output_part = NULL; return false; } m_spec = m_subimagespecs[m_subimage]; sanity_check_channelnames (); compute_pixeltypes(m_spec); return true; #else // OpenEXR 1.x does not support subimages (multi-part) error ("%s does not support subimages", format_name()); return false; #endif } if (mode == AppendMIPLevel) { if (! m_output_scanline && ! m_output_tiled) { error ("Cannot append a MIP level if no file has been opened"); return false; } if (m_spec.tile_width && m_levelmode != Imf::ONE_LEVEL) { // OpenEXR does not support differing tile sizes on different // MIP-map levels. Reject the open() if not using the original // tile sizes. if (userspec.tile_width != m_spec.tile_width || userspec.tile_height != m_spec.tile_height) { error ("OpenEXR tiles must have the same size on all MIPmap levels"); return false; } // Copy the new mip level size. Keep everything else from the // original level. m_spec.width = userspec.width; m_spec.height = userspec.height; // N.B. do we need to copy anything else from userspec? ++m_miplevel; return true; } else { error ("Cannot add MIP level to a non-MIPmapped file"); return false; } } error ("Unknown open mode %d", int(mode)); return false; } bool OpenEXROutput::open (const std::string &name, int subimages, const ImageSpec *specs) { if (subimages < 1) { error ("OpenEXR does not support %d subimages.", subimages); return false; } #ifdef USE_OPENEXR_VERSION2 // Only one part and not deep? Write an OpenEXR 1.x file if (subimages == 1 && ! specs[0].deep) return open (name, specs[0], Create); // Copy the passed-in subimages and turn into OpenEXR headers m_nsubimages = subimages; m_subimage = 0; m_nmiplevels = 1; m_miplevel = 0; m_subimagespecs.assign (specs, specs+subimages); m_headers.resize (subimages); std::string filetype; if (specs[0].deep) filetype = specs[0].tile_width ? "tiledimage" : "deepscanlineimage"; else filetype = specs[0].tile_width ? "tiledimage" : "scanlineimage"; bool deep = false; for (int s = 0; s < subimages; ++s) { if (! spec_to_header (m_subimagespecs[s], s, m_headers[s])) return false; deep |= m_subimagespecs[s].deep; if (m_subimagespecs[s].deep != m_subimagespecs[0].deep) { error ("OpenEXR does not support mixed deep/nondeep multi-part image files"); return false; } if (subimages > 1 || deep) { bool tiled = m_subimagespecs[s].tile_width; m_headers[s].setType (deep ? (tiled ? Imf::DEEPTILE : Imf::DEEPSCANLINE) : (tiled ? Imf::TILEDIMAGE : Imf::SCANLINEIMAGE)); } } m_spec = m_subimagespecs[0]; sanity_check_channelnames (); compute_pixeltypes(m_spec); // Create an ImfMultiPartOutputFile try { // m_output_stream = new OpenEXROutputStream (name.c_str()); // m_output_multipart = new Imf::MultiPartOutputFile (*m_output_stream, // &m_headers[0], subimages); // FIXME: Oops, looks like OpenEXR 2.0 currently lacks a // MultiPartOutputFile ctr that takes an OStream, so we can't // do this quite yet. m_output_multipart = new Imf::MultiPartOutputFile (name.c_str(), &m_headers[0], subimages); } catch (const std::exception &e) { delete m_output_stream; m_output_stream = NULL; error ("OpenEXR exception: %s", e.what()); return false; } catch (...) { // catch-all for edge cases or compiler bugs delete m_output_stream; m_output_stream = NULL; error ("OpenEXR exception: unknown exception"); return false; } try { if (deep) { if (m_spec.tile_width) { m_deep_tiled_output_part = new Imf::DeepTiledOutputPart (*m_output_multipart, 0); } else { m_deep_scanline_output_part = new Imf::DeepScanLineOutputPart (*m_output_multipart, 0); } } else { if (m_spec.tile_width) { m_tiled_output_part = new Imf::TiledOutputPart (*m_output_multipart, 0); } else { m_scanline_output_part = new Imf::OutputPart (*m_output_multipart, 0); } } } catch (const std::exception &e) { error ("OpenEXR exception: %s", e.what()); delete m_output_stream; m_output_stream = NULL; m_scanline_output_part = NULL; m_tiled_output_part = NULL; m_deep_scanline_output_part = NULL; m_deep_tiled_output_part = NULL; return false; } catch (...) { // catch-all for edge cases or compiler bugs error ("OpenEXR exception: unknown exception"); delete m_output_stream; m_output_stream = NULL; m_scanline_output_part = NULL; m_tiled_output_part = NULL; m_deep_scanline_output_part = NULL; m_deep_tiled_output_part = NULL; return false; } return true; #else // No support for OpenEXR 2.x -- one subimage only if (subimages != 1) { error ("OpenEXR 1.x does not support multiple subimages."); return false; } if (specs[0].deep) { error ("OpenEXR 1.x does not support deep data."); return false; } return open (name, specs[0], Create); #endif } void OpenEXROutput::compute_pixeltypes (const ImageSpec &spec) { m_pixeltype.clear (); m_pixeltype.reserve (spec.nchannels); for (int c = 0; c < spec.nchannels; ++c) { TypeDesc format = spec.channelformat(c); Imf::PixelType ptype; switch (format.basetype) { case TypeDesc::UINT: ptype = Imf::UINT; break; case TypeDesc::FLOAT: case TypeDesc::DOUBLE: ptype = Imf::FLOAT; break; default: // Everything else defaults to half ptype = Imf::HALF; break; } m_pixeltype.push_back (ptype); } ASSERT (m_pixeltype.size() == size_t(spec.nchannels)); } bool OpenEXROutput::spec_to_header (ImageSpec &spec, int subimage, Imf::Header &header) { if (spec.width < 1 || spec.height < 1) { error ("Image resolution must be at least 1x1, you asked for %d x %d", spec.width, spec.height); return false; } if (spec.depth < 1) spec.depth = 1; if (spec.depth > 1) { error ("%s does not support volume images (depth > 1)", format_name()); return false; } if (spec.full_width <= 0) spec.full_width = spec.width; if (spec.full_height <= 0) spec.full_height = spec.height; // Force use of one of the three data types that OpenEXR supports switch (spec.format.basetype) { case TypeDesc::UINT: spec.format = TypeDesc::UINT; break; case TypeDesc::FLOAT: case TypeDesc::DOUBLE: spec.format = TypeDesc::FLOAT; break; default: // Everything else defaults to half spec.format = TypeDesc::HALF; } Imath::Box2i dataWindow (Imath::V2i (spec.x, spec.y), Imath::V2i (spec.width + spec.x - 1, spec.height + spec.y - 1)); Imath::Box2i displayWindow (Imath::V2i (spec.full_x, spec.full_y), Imath::V2i (spec.full_width+spec.full_x-1, spec.full_height+spec.full_y-1)); header = Imf::Header (displayWindow, dataWindow); // Insert channels into the header. Also give the channels names if // the user botched it. compute_pixeltypes (spec); static const char *default_chan_names[] = { "R", "G", "B", "A" }; spec.channelnames.resize (spec.nchannels); for (int c = 0; c < spec.nchannels; ++c) { if (spec.channelnames[c].empty()) spec.channelnames[c] = (c<4) ? default_chan_names[c] : Strutil::format ("unknown %d", c); #ifdef OPENEXR_VERSION_IS_1_6_OR_LATER // Hint to lossy compression methods that indicates whether // human perception of the quantity represented by this channel // is closer to linear or closer to logarithmic. Compression // methods may optimize image quality by adjusting pixel data // quantization according to this hint. // Note: This is not the same as data having come from a linear // colorspace. It is meant for data that is percieved by humans // in a linear fashion. // e.g Cb & Cr components in YCbCr images // a* & b* components in L*a*b* images // H & S components in HLS images // We ignore this for now, but we shoudl fix it if we ever commonly // work with non-perceptual/non-color image data. bool pLinear = false; header.channels().insert (spec.channelnames[c].c_str(), Imf::Channel(m_pixeltype[c], 1, 1, pLinear)); #else // Prior to OpenEXR 1.6, it didn't know about the pLinear parameter header.channels().insert (spec.channelnames[c].c_str(), Imf::Channel(m_pixeltype[c], 1, 1)); #endif } // See what compression has been requested, default to ZIP compression // if no request came with the user spec. string_view compression = spec.get_string_attribute ("compression", "zip"); // It seems that zips is the only compression that can reliably work // on deep files (but allow "none" as well) if (spec.deep && compression != "none") compression = "zips"; // Separate any appended quality from the name size_t sep = compression.find_first_of (":"); if (sep != compression.npos) { string_view qual = compression.substr (sep+1); compression = compression.substr (0, sep); if (qual.size() && Strutil::istarts_with (compression, "dwa")) { float q = Strutil::from_string(qual); q = clamp (q, 10.0f, 250000.0f); // useful range spec.attribute ("openexr:dwaCompressionLevel", q); } } spec.attribute ("compression", compression); // If compression is one of the DWA types and no compression level // was set, default to 45. if (Strutil::istarts_with (compression, "dwa") && ! spec.find_attribute("openexr:dwaCompressionLevel")) spec.attribute ("openexr:dwaCompressionLevel", 45.0f); // Default to increasingY line order if (! spec.find_attribute("openexr:lineOrder")) spec.attribute ("openexr:lineOrder", "increasingY"); // Automatically set date field if the client didn't supply it. if (! spec.find_attribute("DateTime")) { time_t now; time (&now); struct tm mytm; Sysutil::get_local_time (&now, &mytm); std::string date = Strutil::format ("%4d:%02d:%02d %2d:%02d:%02d", mytm.tm_year+1900, mytm.tm_mon+1, mytm.tm_mday, mytm.tm_hour, mytm.tm_min, mytm.tm_sec); spec.attribute ("DateTime", date); } figure_mip (spec, m_nmiplevels, m_levelmode, m_roundingmode); std::string textureformat = spec.get_string_attribute ("textureformat", ""); if (Strutil::iequals (textureformat, "CubeFace Environment")) { header.insert ("envmap", Imf::EnvmapAttribute(Imf::ENVMAP_CUBE)); } else if (Strutil::iequals (textureformat, "LatLong Environment")) { header.insert ("envmap", Imf::EnvmapAttribute(Imf::ENVMAP_LATLONG)); } // Fix up density and aspect to be consistent float aspect = spec.get_float_attribute ("PixelAspectRatio", 0.0f); float xdensity = spec.get_float_attribute ("XResolution", 0.0f); float ydensity = spec.get_float_attribute ("YResolution", 0.0f); if (! aspect && xdensity && ydensity) { // No aspect ratio. Compute it from density, if supplied. spec.attribute ("PixelAspectRatio", xdensity / ydensity); } if (xdensity && ydensity && spec.get_string_attribute("ResolutionUnit") == "cm") { // OpenEXR only supports pixels per inch, so fix the values if they // came to us in cm. spec.attribute ("XResolution", xdensity / 2.54f); spec.attribute ("YResolution", ydensity / 2.54f); } // We must setTileDescription here before the put_parameter calls below, // since put_parameter will check the header to ensure this is a tiled // image before setting lineOrder to randomY. if (spec.tile_width) header.setTileDescription ( Imf::TileDescription (spec.tile_width, spec.tile_height, Imf::LevelMode(m_levelmode), Imf::LevelRoundingMode(m_roundingmode))); // Deal with all other params for (size_t p = 0; p < spec.extra_attribs.size(); ++p) put_parameter (spec.extra_attribs[p].name().string(), spec.extra_attribs[p].type(), spec.extra_attribs[p].data(), header); #ifdef USE_OPENEXR_VERSION2 // Multi-part EXR files required to have a name. Make one up if not // supplied. if (m_nsubimages > 1 && ! header.hasName()) { std::string n = Strutil::format ("subimage%02d", subimage); header.insert ("name", Imf::StringAttribute (n)); } #endif return true; } void OpenEXROutput::figure_mip (const ImageSpec &spec, int &nmiplevels, int &levelmode, int &roundingmode) { nmiplevels = 1; levelmode = Imf::ONE_LEVEL; // Default to no MIP-mapping roundingmode = spec.get_int_attribute ("openexr:roundingmode", Imf::ROUND_DOWN); std::string textureformat = spec.get_string_attribute ("textureformat", ""); if (Strutil::iequals (textureformat, "Plain Texture")) { levelmode = spec.get_int_attribute ("openexr:levelmode", Imf::MIPMAP_LEVELS); } else if (Strutil::iequals (textureformat, "CubeFace Environment")) { levelmode = spec.get_int_attribute ("openexr:levelmode", Imf::MIPMAP_LEVELS); } else if (Strutil::iequals (textureformat, "LatLong Environment")) { levelmode = spec.get_int_attribute ("openexr:levelmode", Imf::MIPMAP_LEVELS); } else if (Strutil::iequals (textureformat, "Shadow")) { levelmode = Imf::ONE_LEVEL; // Force one level for shadow maps } if (levelmode == Imf::MIPMAP_LEVELS) { // Compute how many mip levels there will be int w = spec.width; int h = spec.height; while (w > 1 && h > 1) { if (roundingmode == Imf::ROUND_DOWN) { w = w / 2; h = h / 2; } else { w = (w + 1) / 2; h = (h + 1) / 2; } w = std::max (1, w); h = std::max (1, h); ++nmiplevels; } } } struct ExrMeta { const char *oiioname, *exrname; TypeDesc exrtype; ExrMeta (const char *oiioname=NULL, const char *exrname=NULL, TypeDesc exrtype=TypeDesc::UNKNOWN) : oiioname(oiioname), exrname(exrname), exrtype(exrtype) {} }; // Make our own type here because TypeDesc::TypeMatrix and friends may // not have been constructed yet. Initialization order fiasco, ick! static TypeDesc TypeMatrix (TypeDesc::FLOAT,TypeDesc::MATRIX44,0); static TypeDesc TypeTimeCode (TypeDesc::UINT, TypeDesc::SCALAR, TypeDesc::TIMECODE, 2); static TypeDesc TypeKeyCode (TypeDesc::INT, TypeDesc::SCALAR, TypeDesc::KEYCODE, 7); static ExrMeta exr_meta_translation[] = { // Translate OIIO standard metadata names to OpenEXR standard names ExrMeta ("worldtocamera", "worldToCamera", TypeMatrix), ExrMeta ("worldtoscreen", "worldToNDC", TypeMatrix), ExrMeta ("DateTime", "capDate", TypeDesc::STRING), ExrMeta ("ImageDescription", "comments", TypeDesc::STRING), ExrMeta ("description", "comments", TypeDesc::STRING), ExrMeta ("Copyright", "owner", TypeDesc::STRING), ExrMeta ("PixelAspectRatio", "pixelAspectRatio", TypeDesc::FLOAT), ExrMeta ("XResolution", "xDensity", TypeDesc::FLOAT), ExrMeta ("ExposureTime", "expTime", TypeDesc::FLOAT), ExrMeta ("FNumber", "aperture", TypeDesc::FLOAT), ExrMeta ("oiio:subimagename", "name", TypeDesc::STRING), ExrMeta ("openexr:dwaCompressionLevel", "dwaCompressionLevel", TypeDesc::FLOAT), ExrMeta ("smpte:TimeCode", "timeCode", TypeTimeCode), ExrMeta ("smpte:KeyCode", "keyCode", TypeKeyCode), // Empty exrname means that we silently drop this metadata. // Often this is because they have particular meaning to OpenEXR and we // don't want to mess it up by inadvertently copying it wrong from the // user or from a file we read. ExrMeta ("YResolution"), ExrMeta ("planarconfig"), ExrMeta ("type"), ExrMeta ("tiles"), ExrMeta ("version"), ExrMeta ("chunkCount"), ExrMeta ("maxSamplesPerPixel"), ExrMeta () // empty name signifies end of list }; bool OpenEXROutput::put_parameter (const std::string &name, TypeDesc type, const void *data, Imf::Header &header) { // Translate if (name.empty()) return false; std::string xname = name; TypeDesc exrtype = TypeDesc::UNKNOWN; for (int i = 0; exr_meta_translation[i].oiioname; ++i) { const ExrMeta &e(exr_meta_translation[i]); if (Strutil::iequals(xname, e.oiioname) || (e.exrname && Strutil::iequals(xname, e.exrname))) { xname = std::string(e.exrname ? e.exrname : ""); exrtype = e.exrtype; // std::cerr << "exr put '" << name << "' -> '" << xname << "'\n"; break; } } // Special cases if (Strutil::iequals(xname, "Compression") && type == TypeDesc::STRING) { const char *str = *(char **)data; header.compression() = Imf::ZIP_COMPRESSION; // Default if (str) { if (Strutil::iequals (str, "none")) header.compression() = Imf::NO_COMPRESSION; else if (Strutil::iequals (str, "deflate") || Strutil::iequals (str, "zip")) header.compression() = Imf::ZIP_COMPRESSION; else if (Strutil::iequals (str, "rle")) header.compression() = Imf::RLE_COMPRESSION; else if (Strutil::iequals (str, "zips")) header.compression() = Imf::ZIPS_COMPRESSION; else if (Strutil::iequals (str, "piz")) header.compression() = Imf::PIZ_COMPRESSION; else if (Strutil::iequals (str, "pxr24")) header.compression() = Imf::PXR24_COMPRESSION; #ifdef IMF_B44_COMPRESSION // The enum Imf::B44_COMPRESSION is not defined in older versions // of OpenEXR, and there are no explicit version numbers in the // headers. BUT this other related #define is present only in // the newer version. else if (Strutil::iequals (str, "b44")) header.compression() = Imf::B44_COMPRESSION; else if (Strutil::iequals (str, "b44a")) header.compression() = Imf::B44A_COMPRESSION; #endif #if defined(OPENEXR_VERSION_MAJOR) && \ (OPENEXR_VERSION_MAJOR*10000+OPENEXR_VERSION_MINOR*100+OPENEXR_VERSION_PATCH) >= 20200 else if (Strutil::iequals (str, "dwaa")) header.compression() = Imf::DWAA_COMPRESSION; else if (Strutil::iequals (str, "dwab")) header.compression() = Imf::DWAB_COMPRESSION; #endif } return true; } if (Strutil::iequals (xname, "openexr:lineOrder") && type == TypeDesc::STRING) { const char *str = *(char **)data; header.lineOrder() = Imf::INCREASING_Y; // Default if (str) { if (Strutil::iequals (str, "randomY") && header.hasTileDescription() /* randomY is only for tiled files */) header.lineOrder() = Imf::RANDOM_Y; else if (Strutil::iequals (str, "decreasingY")) header.lineOrder() = Imf::DECREASING_Y; } return true; } // Special handling of any remaining "oiio:*" metadata. if (Strutil::istarts_with (xname, "oiio:")) { if (Strutil::iequals (xname, "oiio:ConstantColor") || Strutil::iequals (xname, "oiio:AverageColor") || Strutil::iequals (xname, "oiio:SHA-1")) { // let these fall through and get stored as metadata } else { // Other than the listed exceptions, suppress any other custom // oiio: directives. return false; } } // Before handling general named metadata, suppress non-openexr // format-specific metadata. if (const char *colon = strchr (xname.c_str(), ':')) { std::string prefix (xname.c_str(), colon); if (! Strutil::iequals (prefix, "openexr")) { if (! pvt::format_prefixes_initialized) { // Retrieve and split the list, only the first time spin_lock lock (pvt::format_prefixes_mutex); std::string format_list; OIIO::getattribute ("format_list", format_list); Strutil::split (format_list, pvt::format_prefixes, ","); pvt::format_prefixes_initialized = true; } for (size_t i = 0, e = pvt::format_prefixes.size(); i < e; ++i) if (Strutil::iequals (prefix, pvt::format_prefixes[i])) return false; } } if (! xname.length()) return false; // Skip suppressed names // Handle some cases where the user passed a type different than what // OpenEXR expects, and we can make a good guess about how to translate. float tmpfloat; int tmpint; if (exrtype == TypeDesc::TypeFloat && type == TypeDesc::TypeInt) { tmpfloat = float (*(const int *)data); data = &tmpfloat; type = TypeDesc::TypeFloat; } else if (exrtype == TypeDesc::TypeInt && type == TypeDesc::TypeFloat) { tmpfloat = int (*(const float *)data); data = &tmpint; type = TypeDesc::TypeInt; } else if (exrtype == TypeDesc::TypeMatrix && type == TypeDesc(TypeDesc::FLOAT, 16)) { // Automatically translate float[16] to Matrix when expected type = TypeDesc::TypeMatrix; } // Now if we still don't match a specific type OpenEXR is looking for, // skip it. if (exrtype != TypeDesc() && ! exrtype.equivalent(type)) { OIIO::debugmsg ("OpenEXR output metadata \"%s\" type mismatch: expected %s, got %s\n", name, exrtype, type); return false; } // General handling of attributes try { // Scalar if (type.arraylen == 0) { if (type.aggregate == TypeDesc::SCALAR) { if (type == TypeDesc::INT || type == TypeDesc::UINT) { header.insert (xname.c_str(), Imf::IntAttribute (*(int*)data)); return true; } if (type == TypeDesc::INT16) { header.insert (xname.c_str(), Imf::IntAttribute (*(short*)data)); return true; } if (type == TypeDesc::UINT16) { header.insert (xname.c_str(), Imf::IntAttribute (*(unsigned short*)data)); return true; } if (type == TypeDesc::FLOAT) { header.insert (xname.c_str(), Imf::FloatAttribute (*(float*)data)); return true; } if (type == TypeDesc::HALF) { header.insert (xname.c_str(), Imf::FloatAttribute ((float)*(half*)data)); return true; } if (type == TypeDesc::TypeString) { header.insert (xname.c_str(), Imf::StringAttribute (*(char**)data)); return true; } #ifdef USE_OPENEXR_VERSION2 if (type == TypeDesc::DOUBLE) { header.insert (xname.c_str(), Imf::DoubleAttribute (*(double*)data)); return true; } #endif } // Single instance of aggregate type if (type.aggregate == TypeDesc::VEC2) { switch (type.basetype) { case TypeDesc::UINT: case TypeDesc::INT: // TODO could probably handle U/INT16 here too header.insert (xname.c_str(), Imf::V2iAttribute (*(Imath::V2i*)data)); return true; case TypeDesc::FLOAT: header.insert (xname.c_str(), Imf::V2fAttribute (*(Imath::V2f*)data)); return true; #ifdef USE_OPENEXR_VERSION2 case TypeDesc::DOUBLE: header.insert (xname.c_str(), Imf::V2dAttribute (*(Imath::V2d*)data)); return true; case TypeDesc::STRING: Imf::StringVector v; v.push_back(((const char **)data)[0]); v.push_back(((const char **)data)[1]); header.insert (xname.c_str(), Imf::StringVectorAttribute (v)); return true; #endif } } if (type.aggregate == TypeDesc::VEC3) { switch (type.basetype) { case TypeDesc::UINT: case TypeDesc::INT: // TODO could probably handle U/INT16 here too header.insert (xname.c_str(), Imf::V3iAttribute (*(Imath::V3i*)data)); return true; case TypeDesc::FLOAT: header.insert (xname.c_str(), Imf::V3fAttribute (*(Imath::V3f*)data)); return true; #ifdef USE_OPENEXR_VERSION2 case TypeDesc::DOUBLE: header.insert (xname.c_str(), Imf::V3dAttribute (*(Imath::V3d*)data)); return true; case TypeDesc::STRING: Imf::StringVector v; v.push_back(((const char **)data)[0]); v.push_back(((const char **)data)[1]); v.push_back(((const char **)data)[2]); header.insert (xname.c_str(), Imf::StringVectorAttribute (v)); return true; #endif } } if (type.aggregate == TypeDesc::MATRIX33) { switch (type.basetype) { case TypeDesc::FLOAT: header.insert (xname.c_str(), Imf::M33fAttribute (*(Imath::M33f*)data)); return true; #ifdef USE_OPENEXR_VERSION2 case TypeDesc::DOUBLE: header.insert (xname.c_str(), Imf::M33dAttribute (*(Imath::M33d*)data)); return true; #endif } } if (type.aggregate == TypeDesc::MATRIX44) { switch (type.basetype) { case TypeDesc::FLOAT: header.insert (xname.c_str(), Imf::M44fAttribute (*(Imath::M44f*)data)); return true; #ifdef USE_OPENEXR_VERSION2 case TypeDesc::DOUBLE: header.insert (xname.c_str(), Imf::M44dAttribute (*(Imath::M44d*)data)); return true; #endif } } } // Unknown length arrays (Don't know how to handle these yet) else if (type.arraylen < 0) { return false; } // Arrays else { // TimeCode if (type == TypeDesc::TypeTimeCode ) { header.insert(xname.c_str(), Imf::TimeCodeAttribute (*(Imf::TimeCode*)data)); return true; } // KeyCode else if (type == TypeDesc::TypeKeyCode ) { header.insert(xname.c_str(), Imf::KeyCodeAttribute (*(Imf::KeyCode*)data)); return true; } // 2 Vec2's are treated as a Box if (type.arraylen == 2 && type.aggregate == TypeDesc::VEC2) { switch (type.basetype) { case TypeDesc::UINT: case TypeDesc::INT: { int *a = (int*)data; header.insert(xname.c_str(), Imf::Box2iAttribute(Imath::Box2i(Imath::V2i(a[0],a[1]), Imath::V2i(a[2],a[3]) ))); return true; } case TypeDesc::FLOAT: { float *a = (float*)data; header.insert(xname.c_str(), Imf::Box2fAttribute(Imath::Box2f(Imath::V2f(a[0],a[1]), Imath::V2f(a[2],a[3]) ))); return true; } } } // Vec 2 if (type.arraylen == 2 && type.aggregate == TypeDesc::SCALAR) { switch (type.basetype) { case TypeDesc::UINT: case TypeDesc::INT: // TODO could probably handle U/INT16 here too header.insert (xname.c_str(), Imf::V2iAttribute (*(Imath::V2i*)data)); return true; case TypeDesc::FLOAT: header.insert (xname.c_str(), Imf::V2fAttribute (*(Imath::V2f*)data)); return true; #ifdef USE_OPENEXR_VERSION2 case TypeDesc::DOUBLE: header.insert (xname.c_str(), Imf::V2dAttribute (*(Imath::V2d*)data)); return true; #endif } } // Vec3 if (type.arraylen == 3 && type.aggregate == TypeDesc::SCALAR) { switch (type.basetype) { case TypeDesc::UINT: case TypeDesc::INT: // TODO could probably handle U/INT16 here too header.insert (xname.c_str(), Imf::V3iAttribute (*(Imath::V3i*)data)); return true; case TypeDesc::FLOAT: header.insert (xname.c_str(), Imf::V3fAttribute (*(Imath::V3f*)data)); return true; #ifdef USE_OPENEXR_VERSION2 case TypeDesc::DOUBLE: header.insert (xname.c_str(), Imf::V3dAttribute (*(Imath::V3d*)data)); return true; #endif } } // Matrix if (type.arraylen == 9 && type.aggregate == TypeDesc::SCALAR) { switch (type.basetype) { case TypeDesc::FLOAT: header.insert (xname.c_str(), Imf::M33fAttribute (*(Imath::M33f*)data)); return true; #ifdef USE_OPENEXR_VERSION2 case TypeDesc::DOUBLE: header.insert (xname.c_str(), Imf::M33dAttribute (*(Imath::M33d*)data)); return true; #endif } } if (type.arraylen == 16 && type.aggregate == TypeDesc::SCALAR) { switch (type.basetype) { case TypeDesc::FLOAT: header.insert (xname.c_str(), Imf::M44fAttribute (*(Imath::M44f*)data)); return true; #ifdef USE_OPENEXR_VERSION2 case TypeDesc::DOUBLE: header.insert (xname.c_str(), Imf::M44dAttribute (*(Imath::M44d*)data)); return true; #endif } } if (type.basetype == TypeDesc::FLOAT && type.aggregate * type.arraylen == 8 && Strutil::iequals (xname, "chromaticities")) { const float *f = (const float *)data; Imf::Chromaticities c (Imath::V2f(f[0], f[1]), Imath::V2f(f[2], f[3]), Imath::V2f(f[4], f[5]), Imath::V2f(f[6], f[7])); header.insert ("chromaticities", Imf::ChromaticitiesAttribute (c)); return true; } #ifdef USE_OPENEXR_VERSION2 // String Vector if (type.basetype == TypeDesc::STRING) { Imf::StringVector v; for (int i=0; isetFrameBuffer (frameBuffer); m_output_scanline->writePixels (1); #ifdef USE_OPENEXR_VERSION2 } else if (m_scanline_output_part) { m_scanline_output_part->setFrameBuffer (frameBuffer); m_scanline_output_part->writePixels (1); #endif } else { error ("Attempt to write scanline to a non-scanline file."); return false; } } catch (const std::exception &e) { error ("Failed OpenEXR write: %s", e.what()); return false; } catch (...) { // catch-all for edge cases or compiler bugs error ("Failed OpenEXR write: unknown exception"); return false; } // FIXME -- can we checkpoint the file? return true; } bool OpenEXROutput::write_scanlines (int ybegin, int yend, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride) { if (! (m_output_scanline || m_scanline_output_part)) { error ("called OpenEXROutput::write_scanlines without an open file"); return false; } yend = std::min (yend, spec().y+spec().height); bool native = (format == TypeDesc::UNKNOWN); imagesize_t scanlinebytes = spec().scanline_bytes(true); size_t pixel_bytes = m_spec.pixel_bytes (true); if (native && xstride == AutoStride) xstride = (stride_t) pixel_bytes; stride_t zstride = AutoStride; m_spec.auto_stride (xstride, ystride, zstride, format, m_spec.nchannels, m_spec.width, m_spec.height); const imagesize_t limit = 16*1024*1024; // Allocate 16 MB, or 1 scanline int chunk = std::max (1, int(limit / scanlinebytes)); bool ok = true; for ( ; ok && ybegin < yend; ybegin += chunk) { int y1 = std::min (ybegin+chunk, yend); int nscanlines = y1 - ybegin; const void *d = to_native_rectangle (m_spec.x, m_spec.x+m_spec.width, ybegin, y1, z, z+1, format, data, xstride, ystride, zstride, m_scratch); // Compute where OpenEXR needs to think the full buffers starts. // OpenImageIO requires that 'data' points to where client stored // the bytes to be written, but OpenEXR's frameBuffer.insert() wants // where the address of the "virtual framebuffer" for the whole // image. char *buf = (char *)d - m_spec.x * pixel_bytes - ybegin * scanlinebytes; try { Imf::FrameBuffer frameBuffer; size_t chanoffset = 0; for (int c = 0; c < m_spec.nchannels; ++c) { size_t chanbytes = m_spec.channelformat(c).size(); frameBuffer.insert (m_spec.channelnames[c].c_str(), Imf::Slice (m_pixeltype[c], buf + chanoffset, pixel_bytes, scanlinebytes)); chanoffset += chanbytes; } if (m_output_scanline) { m_output_scanline->setFrameBuffer (frameBuffer); m_output_scanline->writePixels (nscanlines); #ifdef USE_OPENEXR_VERSION2 } else if (m_scanline_output_part) { m_scanline_output_part->setFrameBuffer (frameBuffer); m_scanline_output_part->writePixels (nscanlines); #endif } else { error ("Attempt to write scanlines to a non-scanline file."); return false; } } catch (const std::exception &e) { error ("Failed OpenEXR write: %s", e.what()); return false; } catch (...) { // catch-all for edge cases or compiler bugs error ("Failed OpenEXR write: unknown exception"); return false; } data = (const char *)data + ystride*nscanlines; } // If we allocated more than 1M, free the memory. It's not wasteful, // because it means we're writing big chunks at a time, and therefore // there will be few allocations and deletions. if (m_scratch.size() > 1*1024*1024) { std::vector dummy; std::swap (m_scratch, dummy); } return true; } bool OpenEXROutput::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { bool native = (format == TypeDesc::UNKNOWN); if (native && xstride == AutoStride) xstride = (stride_t) m_spec.pixel_bytes (native); m_spec.auto_stride (xstride, ystride, zstride, format, spec().nchannels, m_spec.tile_width, m_spec.tile_height); return write_tiles (x, std::min (x+m_spec.tile_width, m_spec.x+m_spec.width), y, std::min (y+m_spec.tile_height, m_spec.y+m_spec.height), z, std::min (z+m_spec.tile_depth, m_spec.z+m_spec.depth), format, data, xstride, ystride, zstride); } bool OpenEXROutput::write_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { // std::cerr << "exr::write_tiles " << xbegin << ' ' << xend // << ' ' << ybegin << ' ' << yend << "\n"; if (! (m_output_tiled || m_tiled_output_part)) { error ("called OpenEXROutput::write_tiles without an open file"); return false; } if (! m_spec.valid_tile_range (xbegin, xend, ybegin, yend, zbegin, zend)) { error ("called OpenEXROutput::write_tiles with an invalid tile range"); return false; } // Compute where OpenEXR needs to think the full buffers starts. // OpenImageIO requires that 'data' points to where the client wants // to put the pixels being read, but OpenEXR's frameBuffer.insert() // wants where the address of the "virtual framebuffer" for the // whole image. bool native = (format == TypeDesc::UNKNOWN); size_t user_pixelbytes = m_spec.pixel_bytes (native); size_t pixelbytes = m_spec.pixel_bytes (true); if (native && xstride == AutoStride) xstride = (stride_t) user_pixelbytes; m_spec.auto_stride (xstride, ystride, zstride, format, spec().nchannels, (xend-xbegin), (yend-ybegin)); data = to_native_rectangle (xbegin, xend, ybegin, yend, zbegin, zend, format, data, xstride, ystride, zstride, m_scratch); // clamp to the image edge xend = std::min (xend, m_spec.x+m_spec.width); yend = std::min (yend, m_spec.y+m_spec.height); zend = std::min (zend, m_spec.z+m_spec.depth); int firstxtile = (xbegin-m_spec.x) / m_spec.tile_width; int firstytile = (ybegin-m_spec.y) / m_spec.tile_height; int nxtiles = (xend - xbegin + m_spec.tile_width - 1) / m_spec.tile_width; int nytiles = (yend - ybegin + m_spec.tile_height - 1) / m_spec.tile_height; std::vector padded; int width = nxtiles*m_spec.tile_width; int height = nytiles*m_spec.tile_height; stride_t widthbytes = width * pixelbytes; if (width != (xend-xbegin) || height != (yend-ybegin)) { // If the image region is not an even multiple of the tile size, // we need to copy and add padding. padded.resize (pixelbytes * width * height, 0); OIIO::copy_image (m_spec.nchannels, xend-xbegin, yend-ybegin, 1, data, pixelbytes, pixelbytes, (xend-xbegin)*pixelbytes, (xend-xbegin)*(yend-ybegin)*pixelbytes, &padded[0], pixelbytes, widthbytes, height*widthbytes); data = &padded[0]; } char *buf = (char *)data - xbegin * pixelbytes - ybegin * widthbytes; try { Imf::FrameBuffer frameBuffer; size_t chanoffset = 0; for (int c = 0; c < m_spec.nchannels; ++c) { size_t chanbytes = m_spec.channelformat(c).size(); frameBuffer.insert (m_spec.channelnames[c].c_str(), Imf::Slice (m_pixeltype[c], buf + chanoffset, pixelbytes, widthbytes)); chanoffset += chanbytes; } if (m_output_tiled) { m_output_tiled->setFrameBuffer (frameBuffer); m_output_tiled->writeTiles (firstxtile, firstxtile+nxtiles-1, firstytile, firstytile+nytiles-1, m_miplevel, m_miplevel); #ifdef USE_OPENEXR_VERSION2 } else if (m_tiled_output_part) { m_tiled_output_part->setFrameBuffer (frameBuffer); m_tiled_output_part->writeTiles (firstxtile, firstxtile+nxtiles-1, firstytile, firstytile+nytiles-1, m_miplevel, m_miplevel); #endif } else { error ("Attempt to write tiles for a non-tiled file."); return false; } } catch (const std::exception &e) { error ("Failed OpenEXR write: %s", e.what()); return false; } catch (...) { // catch-all for edge cases or compiler bugs error ("Failed OpenEXR write: unknown exception"); return false; } return true; } bool OpenEXROutput::write_deep_scanlines (int ybegin, int yend, int z, const DeepData &deepdata) { if (m_deep_scanline_output_part == NULL) { error ("called OpenEXROutput::write_deep_scanlines without an open file"); return false; } if (m_spec.width*(yend-ybegin) != deepdata.pixels() || m_spec.nchannels != deepdata.channels()) { error ("called OpenEXROutput::write_deep_scanlines with non-matching DeepData size"); return false; } #ifdef USE_OPENEXR_VERSION2 int nchans = m_spec.nchannels; try { // Set up the count and pointers arrays and the Imf framebuffer Imf::DeepFrameBuffer frameBuffer; Imf::Slice countslice (Imf::UINT, (char *)(deepdata.all_samples().data() - m_spec.x - ybegin*m_spec.width), sizeof(unsigned int), sizeof(unsigned int) * m_spec.width); frameBuffer.insertSampleCountSlice (countslice); std::vector pointerbuf; deepdata.get_pointers (pointerbuf); for (int c = 0; c < nchans; ++c) { Imf::DeepSlice slice (m_pixeltype[c], (char *)(&pointerbuf[c] - m_spec.x * nchans - ybegin*m_spec.width*nchans), sizeof(void*) * nchans, // xstride of pointer array sizeof(void*) * nchans*m_spec.width, // ystride of pointer array deepdata.samplesize()); // stride of data sample frameBuffer.insert (m_spec.channelnames[c].c_str(), slice); } m_deep_scanline_output_part->setFrameBuffer (frameBuffer); // Write the pixels m_deep_scanline_output_part->writePixels (yend-ybegin); } catch (const std::exception &e) { error ("Failed OpenEXR write: %s", e.what()); return false; } catch (...) { // catch-all for edge cases or compiler bugs error ("Failed OpenEXR write: unknown exception"); return false; } return true; #else error ("deep data not supported with OpenEXR 1.x"); return false; #endif } bool OpenEXROutput::write_deep_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, const DeepData &deepdata) { if (m_deep_tiled_output_part == NULL) { error ("called OpenEXROutput::write_deep_tiles without an open file"); return false; } if ((xend-xbegin)*(yend-ybegin)*(zend-zbegin) != deepdata.pixels() || m_spec.nchannels != deepdata.channels()) { error ("called OpenEXROutput::write_deep_tiles with non-matching DeepData size"); return false; } #ifdef USE_OPENEXR_VERSION2 int nchans = m_spec.nchannels; try { size_t width = (xend - xbegin); // Set up the count and pointers arrays and the Imf framebuffer Imf::DeepFrameBuffer frameBuffer; Imf::Slice countslice (Imf::UINT, (char *)(deepdata.all_samples().data() - xbegin - ybegin*width), sizeof(unsigned int), sizeof(unsigned int) * width); frameBuffer.insertSampleCountSlice (countslice); std::vector pointerbuf; deepdata.get_pointers (pointerbuf); for (int c = 0; c < nchans; ++c) { Imf::DeepSlice slice (m_pixeltype[c], (char *)(&pointerbuf[c] - xbegin*nchans - ybegin*width*nchans), sizeof(void*) * nchans, // xstride of pointer array sizeof(void*) * nchans*width, // ystride of pointer array deepdata.samplesize()); // stride of data sample frameBuffer.insert (m_spec.channelnames[c].c_str(), slice); } m_deep_tiled_output_part->setFrameBuffer (frameBuffer); int firstxtile = (xbegin-m_spec.x) / m_spec.tile_width; int firstytile = (ybegin-m_spec.y) / m_spec.tile_height; int xtiles = round_to_multiple (xend-xbegin, m_spec.tile_width) / m_spec.tile_width; int ytiles = round_to_multiple (yend-ybegin, m_spec.tile_height) / m_spec.tile_height; // Write the pixels m_deep_tiled_output_part->writeTiles (firstxtile, firstxtile+xtiles-1, firstytile, firstytile+ytiles-1, m_miplevel, m_miplevel); } catch (const std::exception &e) { error ("Failed OpenEXR write: %s", e.what()); return false; } catch (...) { // catch-all for edge cases or compiler bugs error ("Failed OpenEXR write: unknown exception"); return false; } return true; #else error ("deep data not supported with OpenEXR 1.x"); return false; #endif } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/openexr.imageio/exrinput.cpp0000644000175000017500000014066613151711064022604 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include #include #include #include #include // The way that OpenEXR uses dynamic casting for attributes requires // temporarily suspending "hidden" symbol visibility mode. #ifdef __GNUC__ #pragma GCC visibility push(default) #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_OPENEXR_VERSION2 #include #include #include #include #include #include #include #include #include #endif #ifdef __GNUC__ #pragma GCC visibility pop #endif #include #include "OpenImageIO/dassert.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/thread.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/imagebufalgo_util.h" #include "OpenImageIO/deepdata.h" #include "OpenImageIO/sysutil.h" #include OIIO_PLUGIN_NAMESPACE_BEGIN // Custom file input stream, copying code from the class StdIFStream in OpenEXR, // which would have been used if we just provided a filename. The difference is // that this can handle UTF-8 file paths on all platforms. class OpenEXRInputStream : public Imf::IStream { public: OpenEXRInputStream (const char *filename) : Imf::IStream (filename) { // The reason we have this class is for this line, so that we // can correctly handle UTF-8 file paths on Windows Filesystem::open (ifs, filename, std::ios_base::binary); if (!ifs) Iex::throwErrnoExc (); } virtual bool read (char c[], int n) { if (!ifs) throw Iex::InputExc ("Unexpected end of file."); errno = 0; ifs.read (c, n); return check_error (); } virtual Imath::Int64 tellg () { return std::streamoff (ifs.tellg ()); } virtual void seekg (Imath::Int64 pos) { ifs.seekg (pos); check_error (); } virtual void clear () { ifs.clear (); } private: bool check_error () { if (!ifs) { if (errno) Iex::throwErrnoExc (); return false; } return true; } OIIO::ifstream ifs; }; class OpenEXRInput : public ImageInput { public: OpenEXRInput (); virtual ~OpenEXRInput () { close(); } virtual const char * format_name (void) const { return "openexr"; } virtual int supports (string_view feature) const { return (feature == "arbitrary_metadata" || feature == "exif" // Because of arbitrary_metadata || feature == "iptc"); // Because of arbitrary_metadata } virtual bool valid_file (const std::string &filename) const; virtual bool open (const std::string &name, ImageSpec &newspec); virtual bool close (); virtual int current_subimage (void) const { return m_subimage; } virtual int current_miplevel (void) const { return m_miplevel; } virtual bool seek_subimage (int subimage, int miplevel, ImageSpec &newspec); virtual bool read_native_scanline (int y, int z, void *data); virtual bool read_native_scanlines (int ybegin, int yend, int z, void *data); virtual bool read_native_scanlines (int ybegin, int yend, int z, int chbegin, int chend, void *data); virtual bool read_native_tile (int x, int y, int z, void *data); virtual bool read_native_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, void *data); virtual bool read_native_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, void *data); virtual bool read_native_deep_scanlines (int ybegin, int yend, int z, int chbegin, int chend, DeepData &deepdata); virtual bool read_native_deep_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, DeepData &deepdata); private: struct PartInfo { bool initialized; ImageSpec spec; int topwidth; ///< Width of top mip level int topheight; ///< Height of top mip level int levelmode; ///< The level mode int roundingmode; ///< Rounding mode bool cubeface; ///< It's a cubeface environment map int nmiplevels; ///< How many MIP levels are there? Imath::Box2i top_datawindow; Imath::Box2i top_displaywindow; std::vector pixeltype; ///< Imf pixel type for each chan std::vector chanbytes; ///< Size (in bytes) of each channel PartInfo () : initialized(false) { } ~PartInfo () { } void parse_header (const Imf::Header *header); void query_channels (const Imf::Header *header); }; std::vector m_parts; ///< Image parts OpenEXRInputStream *m_input_stream; ///< Stream for input file #ifdef USE_OPENEXR_VERSION2 Imf::MultiPartInputFile *m_input_multipart; ///< Multipart input Imf::InputPart *m_scanline_input_part; Imf::TiledInputPart *m_tiled_input_part; Imf::DeepScanLineInputPart *m_deep_scanline_input_part; Imf::DeepTiledInputPart *m_deep_tiled_input_part; #else char *m_input_multipart; ///< Multipart input char *m_scanline_input_part; char *m_tiled_input_part; char *m_deep_scanline_input_part; char *m_deep_tiled_input_part; #endif Imf::InputFile *m_input_scanline; ///< Input for scanline files Imf::TiledInputFile *m_input_tiled; ///< Input for tiled files int m_subimage; ///< What subimage are we looking at? int m_nsubimages; ///< How many subimages are there? int m_miplevel; ///< What MIP level are we looking at? void init () { m_input_stream = NULL; m_input_multipart = NULL; m_scanline_input_part = NULL; m_tiled_input_part = NULL; m_deep_scanline_input_part = NULL; m_deep_tiled_input_part = NULL; m_input_scanline = NULL; m_input_tiled = NULL; m_subimage = -1; m_miplevel = -1; } }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageInput * openexr_input_imageio_create () { return new OpenEXRInput; } // OIIO_EXPORT int openexr_imageio_version = OIIO_PLUGIN_VERSION; // it's in exroutput.cpp OIIO_EXPORT const char * openexr_input_extensions[] = { "exr", "sxr", "mxr", NULL }; OIIO_PLUGIN_EXPORTS_END class StringMap { typedef std::map map_t; public: StringMap (void) { init(); } const std::string & operator[] (const std::string &s) const { map_t::const_iterator i; i = m_map.find (s); return i == m_map.end() ? s : i->second; } private: map_t m_map; void init (void) { // Ones whose name we change to our convention m_map["cameraTransform"] = "worldtocamera"; m_map["worldToCamera"] = "worldtocamera"; m_map["worldToNDC"] = "worldtoscreen"; m_map["capDate"] = "DateTime"; m_map["comments"] = "ImageDescription"; m_map["owner"] = "Copyright"; m_map["pixelAspectRatio"] = "PixelAspectRatio"; m_map["xDensity"] = "XResolution"; m_map["expTime"] = "ExposureTime"; // Ones we don't rename -- OpenEXR convention matches ours m_map["wrapmodes"] = "wrapmodes"; m_map["aperture"] = "FNumber"; // Ones to prefix with openexr: m_map["version"] = "openexr:version"; m_map["chunkCount"] = "openexr:chunkCount"; m_map["maxSamplesPerPixel"] = "openexr:maxSamplesPerPixel"; m_map["dwaCompressionLevel"] = "openexr:dwaCompressionLevel"; // Ones to skip because we handle specially m_map["channels"] = ""; m_map["compression"] = ""; m_map["dataWindow"] = ""; m_map["displayWindow"] = ""; m_map["envmap"] = ""; m_map["tiledesc"] = ""; m_map["tiles"] = ""; m_map["openexr:lineOrder"] = ""; m_map["type"] = ""; // Ones to skip because we consider them irrelevant // m_map[""] = ""; // FIXME: Things to consider in the future: // preview // screenWindowCenter // adoptedNeutral // renderingTransform, lookModTransform // utcOffset // longitude latitude altitude // focus isoSpeed } }; static StringMap exr_tag_to_oiio_std; namespace pvt { void set_exr_threads () { static int exr_threads = 0; // lives in exrinput.cpp static spin_mutex exr_threads_mutex; int oiio_threads = 1; OIIO::getattribute ("exr_threads", oiio_threads); // 0 means all threads in OIIO, but single-threaded in OpenEXR // -1 means single-threaded in OIIO if (oiio_threads == 0) { oiio_threads = Sysutil::hardware_concurrency(); } else if (oiio_threads == -1) { oiio_threads = 0; } spin_lock lock (exr_threads_mutex); if (exr_threads != oiio_threads) { exr_threads = oiio_threads; Imf::setGlobalThreadCount (exr_threads); } } } // namespace pvt OpenEXRInput::OpenEXRInput () { init (); } bool OpenEXRInput::valid_file (const std::string &filename) const { return Imf::isOpenExrFile (filename.c_str()); } bool OpenEXRInput::open (const std::string &name, ImageSpec &newspec) { // Quick check to reject non-exr files if (! Filesystem::is_regular (name)) { error ("Could not open file \"%s\"", name.c_str()); return false; } bool tiled; if (! Imf::isOpenExrFile (name.c_str(), tiled)) { error ("\"%s\" is not an OpenEXR file", name.c_str()); return false; } pvt::set_exr_threads (); m_spec = ImageSpec(); // Clear everything with default constructor try { m_input_stream = new OpenEXRInputStream (name.c_str()); } catch (const std::exception &e) { m_input_stream = NULL; error ("OpenEXR exception: %s", e.what()); return false; } catch (...) { // catch-all for edge cases or compiler bugs m_input_stream = NULL; error ("OpenEXR exception: unknown"); return false; } #ifdef USE_OPENEXR_VERSION2 try { m_input_multipart = new Imf::MultiPartInputFile (*m_input_stream); } catch (const std::exception &e) { delete m_input_stream; m_input_stream = NULL; error ("OpenEXR exception: %s", e.what()); return false; } catch (...) { // catch-all for edge cases or compiler bugs m_input_stream = NULL; error ("OpenEXR exception: unknown"); return false; } m_nsubimages = m_input_multipart->parts(); #else try { if (tiled) { m_input_tiled = new Imf::TiledInputFile (*m_input_stream); } else { m_input_scanline = new Imf::InputFile (*m_input_stream); } } catch (const std::exception &e) { delete m_input_stream; m_input_stream = NULL; error ("OpenEXR exception: %s", e.what()); return false; } catch (...) { // catch-all for edge cases or compiler bugs m_input_stream = NULL; error ("OpenEXR exception: unknown"); return false; } if (! m_input_scanline && ! m_input_tiled) { error ("Unknown error opening EXR file"); return false; } m_nsubimages = 1; // OpenEXR 1.x did not have multipart #endif m_parts.resize (m_nsubimages); m_subimage = -1; m_miplevel = -1; bool ok = seek_subimage (0, 0, newspec); if (! ok) close (); return ok; } // Count number of MIPmap levels inline int numlevels (int width, int roundingmode) { int nlevels = 1; for ( ; width > 1; ++nlevels) { if (roundingmode == Imf::ROUND_DOWN) width = width / 2; else width = (width + 1) / 2; } return nlevels; } void OpenEXRInput::PartInfo::parse_header (const Imf::Header *header) { if (initialized) return; ASSERT (header); spec = ImageSpec(); top_datawindow = header->dataWindow(); top_displaywindow = header->displayWindow(); spec.x = top_datawindow.min.x; spec.y = top_datawindow.min.y; spec.z = 0; spec.width = top_datawindow.max.x - top_datawindow.min.x + 1; spec.height = top_datawindow.max.y - top_datawindow.min.y + 1; spec.depth = 1; topwidth = spec.width; // Save top-level mipmap dimensions topheight = spec.height; spec.full_x = top_displaywindow.min.x; spec.full_y = top_displaywindow.min.y; spec.full_z = 0; spec.full_width = top_displaywindow.max.x - top_displaywindow.min.x + 1; spec.full_height = top_displaywindow.max.y - top_displaywindow.min.y + 1; spec.full_depth = 1; spec.tile_depth = 1; if (header->hasTileDescription() #if USE_OPENEXR_VERSION2 && Strutil::icontains(header->type(), "tile") #endif ) { const Imf::TileDescription &td (header->tileDescription()); spec.tile_width = td.xSize; spec.tile_height = td.ySize; levelmode = td.mode; roundingmode = td.roundingMode; if (levelmode == Imf::MIPMAP_LEVELS) nmiplevels = numlevels (std::max(topwidth,topheight), roundingmode); else if (levelmode == Imf::RIPMAP_LEVELS) nmiplevels = numlevels (std::max(topwidth,topheight), roundingmode); else nmiplevels = 1; } else { spec.tile_width = 0; spec.tile_height = 0; levelmode = Imf::ONE_LEVEL; nmiplevels = 1; } query_channels (header); // also sets format #ifdef USE_OPENEXR_VERSION2 spec.deep = Strutil::istarts_with (header->type(), "deep"); #endif // Unless otherwise specified, exr files are assumed to be linear. spec.attribute ("oiio:ColorSpace", "Linear"); if (levelmode != Imf::ONE_LEVEL) spec.attribute ("openexr:roundingmode", roundingmode); const Imf::EnvmapAttribute *envmap; envmap = header->findTypedAttribute("envmap"); if (envmap) { cubeface = (envmap->value() == Imf::ENVMAP_CUBE); spec.attribute ("textureformat", cubeface ? "CubeFace Environment" : "LatLong Environment"); // OpenEXR conventions for env maps if (! cubeface) spec.attribute ("oiio:updirection", "y"); spec.attribute ("oiio:sampleborder", 1); // FIXME - detect CubeFace Shadow? } else { cubeface = false; if (spec.tile_width && levelmode == Imf::MIPMAP_LEVELS) spec.attribute ("textureformat", "Plain Texture"); // FIXME - detect Shadow } const Imf::CompressionAttribute *compressattr; compressattr = header->findTypedAttribute("compression"); if (compressattr) { const char *comp = NULL; switch (compressattr->value()) { case Imf::NO_COMPRESSION : comp = "none"; break; case Imf::RLE_COMPRESSION : comp = "rle"; break; case Imf::ZIPS_COMPRESSION : comp = "zips"; break; case Imf::ZIP_COMPRESSION : comp = "zip"; break; case Imf::PIZ_COMPRESSION : comp = "piz"; break; case Imf::PXR24_COMPRESSION : comp = "pxr24"; break; #ifdef IMF_B44_COMPRESSION // The enum Imf::B44_COMPRESSION is not defined in older versions // of OpenEXR, and there are no explicit version numbers in the // headers. BUT this other related #define is present only in // the newer version. case Imf::B44_COMPRESSION : comp = "b44"; break; case Imf::B44A_COMPRESSION : comp = "b44a"; break; #endif #if defined(OPENEXR_VERSION_MAJOR) && \ (OPENEXR_VERSION_MAJOR*10000+OPENEXR_VERSION_MINOR*100+OPENEXR_VERSION_PATCH) >= 20200 case Imf::DWAA_COMPRESSION : comp = "dwaa"; break; case Imf::DWAB_COMPRESSION : comp = "dwab"; break; #endif default: break; } if (comp) spec.attribute ("compression", comp); } for (Imf::Header::ConstIterator hit = header->begin(); hit != header->end(); ++hit) { const Imf::IntAttribute *iattr; const Imf::FloatAttribute *fattr; const Imf::StringAttribute *sattr; const Imf::M33fAttribute *m33fattr; const Imf::M44fAttribute *m44fattr; const Imf::V3fAttribute *v3fattr; const Imf::V3iAttribute *v3iattr; const Imf::V2fAttribute *v2fattr; const Imf::V2iAttribute *v2iattr; const Imf::Box2iAttribute *b2iattr; const Imf::Box2fAttribute *b2fattr; const Imf::TimeCodeAttribute *tattr; const Imf::KeyCodeAttribute *kcattr; const Imf::ChromaticitiesAttribute *crattr; #ifdef USE_OPENEXR_VERSION2 const Imf::StringVectorAttribute *svattr; const Imf::DoubleAttribute *dattr; const Imf::V2dAttribute *v2dattr; const Imf::V3dAttribute *v3dattr; const Imf::M33dAttribute *m33dattr; const Imf::M44dAttribute *m44dattr; #endif const char *name = hit.name(); std::string oname = exr_tag_to_oiio_std[name]; if (oname.empty()) // Empty string means skip this attrib continue; // if (oname == name) // oname = std::string(format_name()) + "_" + oname; const Imf::Attribute &attrib = hit.attribute(); std::string type = attrib.typeName(); if (type == "string" && (sattr = header->findTypedAttribute (name))) spec.attribute (oname, sattr->value().c_str()); else if (type == "int" && (iattr = header->findTypedAttribute (name))) spec.attribute (oname, iattr->value()); else if (type == "float" && (fattr = header->findTypedAttribute (name))) spec.attribute (oname, fattr->value()); else if (type == "m33f" && (m33fattr = header->findTypedAttribute (name))) spec.attribute (oname, TypeDesc::TypeMatrix33, &(m33fattr->value())); else if (type == "m44f" && (m44fattr = header->findTypedAttribute (name))) spec.attribute (oname, TypeDesc::TypeMatrix44, &(m44fattr->value())); else if (type == "v3f" && (v3fattr = header->findTypedAttribute (name))) spec.attribute (oname, TypeDesc::TypeVector, &(v3fattr->value())); else if (type == "v3i" && (v3iattr = header->findTypedAttribute (name))) { TypeDesc v3 (TypeDesc::INT, TypeDesc::VEC3, TypeDesc::VECTOR); spec.attribute (oname, v3, &(v3iattr->value())); } else if (type == "v2f" && (v2fattr = header->findTypedAttribute (name))) { TypeDesc v2 (TypeDesc::FLOAT,TypeDesc::VEC2); spec.attribute (oname, v2, &(v2fattr->value())); } else if (type == "v2i" && (v2iattr = header->findTypedAttribute (name))) { TypeDesc v2 (TypeDesc::INT,TypeDesc::VEC2); spec.attribute (oname, v2, &(v2iattr->value())); } #ifdef USE_OPENEXR_VERSION2 else if (type == "stringvector" && (svattr = header->findTypedAttribute (name))) { std::vector strvec = svattr->value(); std::vector ustrvec (strvec.size()); for (size_t i = 0, e = strvec.size(); i < e; ++i) ustrvec[i] = strvec[i]; TypeDesc sv (TypeDesc::STRING, ustrvec.size()); spec.attribute(oname, sv, &ustrvec[0]); } else if (type == "double" && (dattr = header->findTypedAttribute (name))) { TypeDesc d (TypeDesc::DOUBLE); spec.attribute (oname, d, &(dattr->value())); } else if (type == "v2d" && (v2dattr = header->findTypedAttribute (name))) { TypeDesc v2 (TypeDesc::DOUBLE,TypeDesc::VEC2); spec.attribute (oname, v2, &(v2dattr->value())); } else if (type == "v3d" && (v3dattr = header->findTypedAttribute (name))) { TypeDesc v3 (TypeDesc::DOUBLE,TypeDesc::VEC3, TypeDesc::VECTOR); spec.attribute (oname, v3, &(v3dattr->value())); } else if (type == "m33d" && (m33dattr = header->findTypedAttribute (name))) { TypeDesc m33 (TypeDesc::DOUBLE, TypeDesc::MATRIX33); spec.attribute (oname, m33, &(m33dattr->value())); } else if (type == "m44d" && (m44dattr = header->findTypedAttribute (name))) { TypeDesc m44 (TypeDesc::DOUBLE, TypeDesc::MATRIX44); spec.attribute (oname, m44, &(m44dattr->value())); } #endif else if (type == "box2i" && (b2iattr = header->findTypedAttribute (name))) { TypeDesc bx (TypeDesc::INT, TypeDesc::VEC2, 2); spec.attribute (oname, bx, &b2iattr->value()); } else if (type == "box2f" && (b2fattr = header->findTypedAttribute (name))) { TypeDesc bx (TypeDesc::FLOAT, TypeDesc::VEC2, 2); spec.attribute (oname, bx, &b2fattr->value()); } else if (type == "timecode" && (tattr = header->findTypedAttribute (name))) { unsigned int timecode[2]; timecode[0] = tattr->value().timeAndFlags(Imf::TimeCode::TV60_PACKING); //TV60 returns unchanged _time timecode[1] = tattr->value().userData(); // Elevate "timeCode" to smpte:TimeCode if (oname == "timeCode") oname = "smpte:TimeCode"; spec.attribute(oname, TypeDesc::TypeTimeCode, timecode); } else if (type == "keycode" && (kcattr = header->findTypedAttribute (name))) { const Imf::KeyCode *k = &kcattr->value(); unsigned int keycode[7]; keycode[0] = k->filmMfcCode(); keycode[1] = k->filmType(); keycode[2] = k->prefix(); keycode[3] = k->count(); keycode[4] = k->perfOffset(); keycode[5] = k->perfsPerFrame(); keycode[6] = k->perfsPerCount(); // Elevate "keyCode" to smpte:KeyCode if (oname == "keyCode") oname = "smpte:KeyCode"; spec.attribute(oname, TypeDesc::TypeKeyCode, keycode); } else if (type == "chromaticities" && (crattr = header->findTypedAttribute (name))) { const Imf::Chromaticities *chroma = &crattr->value(); spec.attribute (oname, TypeDesc(TypeDesc::FLOAT,8), (const float *)chroma); } else { #if 0 std::cerr << " unknown attribute " << type << ' ' << name << "\n"; #endif } } float aspect = spec.get_float_attribute ("PixelAspectRatio", 0.0f); float xdensity = spec.get_float_attribute ("XResolution", 0.0f); if (xdensity) { // If XResolution is found, supply the YResolution and unit. spec.attribute ("YResolution", xdensity * (aspect ? aspect : 1.0f)); spec.attribute ("ResolutionUnit", "in"); // EXR is always pixels/inch } #ifdef USE_OPENEXR_VERSION2 // EXR "name" also gets passed along as "oiio:subimagename". if (header->hasName()) spec.attribute ("oiio:subimagename", header->name()); #endif initialized = true; } namespace { static TypeDesc TypeDesc_from_ImfPixelType (Imf::PixelType ptype) { switch (ptype) { case Imf::UINT : return TypeDesc::UINT; break; case Imf::HALF : return TypeDesc::HALF; break; case Imf::FLOAT : return TypeDesc::FLOAT; break; default: ASSERT_MSG (0, "Unknown Imf::PixelType %d", int(ptype)); } } // Used to hold channel information for sorting into canonical order struct ChanNameHolder { string_view fullname; int exr_channel_number; // channel index in the exr (sorted by name) string_view layer; string_view suffix; int special_index; Imf::PixelType exr_data_type; TypeDesc datatype; ChanNameHolder (string_view fullname, int n, Imf::PixelType exrtype) : fullname(fullname), exr_channel_number(n), exr_data_type(exrtype), datatype(TypeDesc_from_ImfPixelType(exrtype)) { size_t dot = fullname.find_last_of ('.'); if (dot == string_view::npos) { suffix = fullname; } else { layer = string_view (fullname.data(), dot+1); suffix = string_view (fullname.data()+dot+1, fullname.size()-dot-1); } static const char * special[] = { "R", "Red", "G", "Green", "B", "Blue", "Y", "real", "imag", "A", "Alpha", "AR", "RA", "AG", "GA", "AB", "BA", "Z", "Depth", "Zback", NULL }; special_index = 10000; for (int i = 0; special[i]; ++i) if (Strutil::iequals (suffix, special[i])) { special_index = i; break; } } static bool compare_cnh (const ChanNameHolder &a, const ChanNameHolder &b) { if (a.layer < b.layer) return true; if (a.layer > b.layer) return false; // Within the same layer if (a.special_index < b.special_index) return true; if (a.special_index > b.special_index) return false; return a.suffix < b.suffix; } }; } // anon namespace void OpenEXRInput::PartInfo::query_channels (const Imf::Header *header) { ASSERT (! initialized); spec.nchannels = 0; const Imf::ChannelList &channels (header->channels()); std::vector channelnames; // Order of channels in file std::vector cnh; int c = 0; for (Imf::ChannelList::ConstIterator ci = channels.begin(); ci != channels.end(); ++c, ++ci) { cnh.push_back (ChanNameHolder (ci.name(), c, ci.channel().type)); ++spec.nchannels; } std::sort (cnh.begin(), cnh.end(), ChanNameHolder::compare_cnh); // Now we should have cnh sorted into the order that we want to present // to the OIIO client. spec.format = TypeDesc::UNKNOWN; bool all_one_format = true; for (int c = 0; c < spec.nchannels; ++c) { spec.channelnames.push_back (cnh[c].fullname); spec.channelformats.push_back (cnh[c].datatype); spec.format = TypeDesc(ImageBufAlgo::type_merge (TypeDesc::BASETYPE(spec.format.basetype), TypeDesc::BASETYPE(cnh[c].datatype.basetype))); pixeltype.push_back (cnh[c].exr_data_type); chanbytes.push_back (cnh[c].datatype.size()); all_one_format &= (cnh[c].datatype == cnh[0].datatype); if (spec.alpha_channel < 0 && (Strutil::iequals (cnh[c].suffix, "A") || Strutil::iequals (cnh[c].suffix, "Alpha"))) spec.alpha_channel = c; if (spec.z_channel < 0 && (Strutil::iequals (cnh[c].suffix, "Z") || Strutil::iequals (cnh[c].suffix, "Depth"))) spec.z_channel = c; } ASSERT ((int)spec.channelnames.size() == spec.nchannels); ASSERT (spec.format != TypeDesc::UNKNOWN); if (all_one_format) spec.channelformats.clear(); } bool OpenEXRInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec) { if (subimage < 0 || subimage >= m_nsubimages) // out of range return false; if (subimage == m_subimage && miplevel == m_miplevel) { // no change newspec = m_spec; return true; } PartInfo &part (m_parts[subimage]); if (! part.initialized) { const Imf::Header *header = NULL; #ifdef USE_OPENEXR_VERSION2 if (m_input_multipart) header = &(m_input_multipart->header(subimage)); #else if (m_input_tiled) header = &(m_input_tiled->header()); if (m_input_scanline) header = &(m_input_scanline->header()); #endif part.parse_header (header); part.initialized = true; } #ifdef USE_OPENEXR_VERSION2 if (subimage != m_subimage) { delete m_scanline_input_part; m_scanline_input_part = NULL; delete m_tiled_input_part; m_tiled_input_part = NULL; delete m_deep_scanline_input_part; m_deep_scanline_input_part = NULL; delete m_deep_tiled_input_part; m_deep_tiled_input_part = NULL; try { if (part.spec.deep) { if (part.spec.tile_width) m_deep_tiled_input_part = new Imf::DeepTiledInputPart (*m_input_multipart, subimage); else m_deep_scanline_input_part = new Imf::DeepScanLineInputPart (*m_input_multipart, subimage); } else { if (part.spec.tile_width) m_tiled_input_part = new Imf::TiledInputPart (*m_input_multipart, subimage); else m_scanline_input_part = new Imf::InputPart (*m_input_multipart, subimage); } } catch (const std::exception &e) { error ("OpenEXR exception: %s", e.what()); m_scanline_input_part = NULL; m_tiled_input_part = NULL; m_deep_scanline_input_part = NULL; m_deep_tiled_input_part = NULL; return false; } catch (...) { // catch-all for edge cases or compiler bugs error ("OpenEXR exception: unknown"); m_scanline_input_part = NULL; m_tiled_input_part = NULL; m_deep_scanline_input_part = NULL; m_deep_tiled_input_part = NULL; return false; } } #endif m_subimage = subimage; if (miplevel < 0 || miplevel >= part.nmiplevels) // out of range return false; m_miplevel = miplevel; m_spec = part.spec; if (miplevel == 0 && part.levelmode == Imf::ONE_LEVEL) { newspec = m_spec; return true; } // Compute the resolution of the requested mip level. int w = part.topwidth, h = part.topheight; if (part.levelmode == Imf::MIPMAP_LEVELS) { while (miplevel--) { if (part.roundingmode == Imf::ROUND_DOWN) { w = w / 2; h = h / 2; } else { w = (w + 1) / 2; h = (h + 1) / 2; } w = std::max (1, w); h = std::max (1, h); } } else if (part.levelmode == Imf::RIPMAP_LEVELS) { // FIXME } else { ASSERT_MSG (0, "Unknown levelmode %d", int(part.levelmode)); } m_spec.width = w; m_spec.height = h; // N.B. OpenEXR doesn't support data and display windows per MIPmap // level. So always take from the top level. Imath::Box2i datawindow = part.top_datawindow; Imath::Box2i displaywindow = part.top_displaywindow; m_spec.x = datawindow.min.x; m_spec.y = datawindow.min.y; if (m_miplevel == 0) { m_spec.full_x = displaywindow.min.x; m_spec.full_y = displaywindow.min.y; m_spec.full_width = displaywindow.max.x - displaywindow.min.x + 1; m_spec.full_height = displaywindow.max.y - displaywindow.min.y + 1; } else { m_spec.full_x = m_spec.x; m_spec.full_y = m_spec.y; m_spec.full_width = m_spec.width; m_spec.full_height = m_spec.height; } if (part.cubeface) { m_spec.full_width = w; m_spec.full_height = w; } newspec = m_spec; return true; } bool OpenEXRInput::close () { delete m_input_multipart; delete m_scanline_input_part; delete m_tiled_input_part; delete m_deep_scanline_input_part; delete m_deep_tiled_input_part; delete m_input_scanline; delete m_input_tiled; delete m_input_stream; init (); // Reset to initial state return true; } bool OpenEXRInput::read_native_scanline (int y, int z, void *data) { return read_native_scanlines (y, y+1, z, 0, m_spec.nchannels, data); } bool OpenEXRInput::read_native_scanlines (int ybegin, int yend, int z, void *data) { return read_native_scanlines (ybegin, yend, z, 0, m_spec.nchannels, data); } bool OpenEXRInput::read_native_scanlines (int ybegin, int yend, int z, int chbegin, int chend, void *data) { chend = clamp (chend, chbegin+1, m_spec.nchannels); // std::cerr << "openexr rns " << ybegin << ' ' << yend << ", channels " // << chbegin << "-" << (chend-1) << "\n"; if (m_input_scanline == NULL && m_scanline_input_part == NULL) { error ("called OpenEXRInput::read_native_scanlines without an open file"); return false; } // Compute where OpenEXR needs to think the full buffers starts. // OpenImageIO requires that 'data' points to where the client wants // to put the pixels being read, but OpenEXR's frameBuffer.insert() // wants where the address of the "virtual framebuffer" for the // whole image. const PartInfo &part (m_parts[m_subimage]); size_t pixelbytes = m_spec.pixel_bytes (chbegin, chend, true); size_t scanlinebytes = (size_t)m_spec.width * pixelbytes; char *buf = (char *)data - m_spec.x * pixelbytes - ybegin * scanlinebytes; try { Imf::FrameBuffer frameBuffer; size_t chanoffset = 0; for (int c = chbegin; c < chend; ++c) { size_t chanbytes = m_spec.channelformat(c).size(); frameBuffer.insert (m_spec.channelnames[c].c_str(), Imf::Slice (part.pixeltype[c], buf + chanoffset, pixelbytes, scanlinebytes)); chanoffset += chanbytes; } if (m_input_scanline) { m_input_scanline->setFrameBuffer (frameBuffer); m_input_scanline->readPixels (ybegin, yend-1); #ifdef USE_OPENEXR_VERSION2 } else if (m_scanline_input_part) { m_scanline_input_part->setFrameBuffer (frameBuffer); m_scanline_input_part->readPixels (ybegin, yend-1); #endif } else { error ("Attempted to read scanline from a non-scanline file."); return false; } } catch (const std::exception &e) { error ("Failed OpenEXR read: %s", e.what()); return false; } catch (...) { // catch-all for edge cases or compiler bugs error ("Failed OpenEXR read: unknown exception"); return false; } return true; } bool OpenEXRInput::read_native_tile (int x, int y, int z, void *data) { return read_native_tiles (x, x+m_spec.tile_width, y, y+m_spec.tile_height, z, z+m_spec.tile_depth, 0, m_spec.nchannels, data); } bool OpenEXRInput::read_native_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, void *data) { return read_native_tiles (xbegin, xend, ybegin, yend, zbegin, zend, 0, m_spec.nchannels, data); } bool OpenEXRInput::read_native_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, void *data) { chend = clamp (chend, chbegin+1, m_spec.nchannels); #if 0 std::cerr << "openexr rnt " << xbegin << ' ' << xend << ' ' << ybegin << ' ' << yend << ", chans " << chbegin << "-" << (chend-1) << "\n"; #endif if (! (m_input_tiled || m_tiled_input_part) || ! m_spec.valid_tile_range (xbegin, xend, ybegin, yend, zbegin, zend)) { error ("called OpenEXRInput::read_native_tiles without an open file"); return false; } // Compute where OpenEXR needs to think the full buffers starts. // OpenImageIO requires that 'data' points to where the client wants // to put the pixels being read, but OpenEXR's frameBuffer.insert() // wants where the address of the "virtual framebuffer" for the // whole image. const PartInfo &part (m_parts[m_subimage]); size_t pixelbytes = m_spec.pixel_bytes (chbegin, chend, true); int firstxtile = (xbegin-m_spec.x) / m_spec.tile_width; int firstytile = (ybegin-m_spec.y) / m_spec.tile_height; // clamp to the image edge xend = std::min (xend, m_spec.x+m_spec.width); yend = std::min (yend, m_spec.y+m_spec.height); zend = std::min (zend, m_spec.z+m_spec.depth); // figure out how many tiles we need int nxtiles = (xend - xbegin + m_spec.tile_width - 1) / m_spec.tile_width; int nytiles = (yend - ybegin + m_spec.tile_height - 1) / m_spec.tile_height; int whole_width = nxtiles * m_spec.tile_width; int whole_height = nytiles * m_spec.tile_height; boost::scoped_array tmpbuf; void *origdata = data; if (whole_width != (xend-xbegin) || whole_height != (yend-ybegin)) { // Deal with the case of reading not a whole number of tiles -- // OpenEXR will happily overwrite user memory in this case. tmpbuf.reset (new char [nxtiles * nytiles * m_spec.tile_bytes(true)]); data = &tmpbuf[0]; } char *buf = (char *)data - xbegin * pixelbytes - ybegin * pixelbytes * m_spec.tile_width * nxtiles; try { Imf::FrameBuffer frameBuffer; size_t chanoffset = 0; for (int c = chbegin; c < chend; ++c) { size_t chanbytes = m_spec.channelformat(c).size(); frameBuffer.insert (m_spec.channelnames[c].c_str(), Imf::Slice (part.pixeltype[c], buf + chanoffset, pixelbytes, pixelbytes*m_spec.tile_width*nxtiles)); chanoffset += chanbytes; } if (m_input_tiled) { m_input_tiled->setFrameBuffer (frameBuffer); m_input_tiled->readTiles (firstxtile, firstxtile+nxtiles-1, firstytile, firstytile+nytiles-1, m_miplevel, m_miplevel); #ifdef USE_OPENEXR_VERSION2 } else if (m_tiled_input_part) { m_tiled_input_part->setFrameBuffer (frameBuffer); m_tiled_input_part->readTiles (firstxtile, firstxtile+nxtiles-1, firstytile, firstytile+nytiles-1, m_miplevel, m_miplevel); #endif } else { error ("Attempted to read tiles from a non-tiled file"); return false; } if (data != origdata) { stride_t user_scanline_bytes = (xend-xbegin) * pixelbytes; stride_t scanline_stride = nxtiles*m_spec.tile_width*pixelbytes; for (int y = ybegin; y < yend; ++y) memcpy ((char *)origdata+(y-ybegin)*scanline_stride, (char *)data+(y-ybegin)*scanline_stride, user_scanline_bytes); } } catch (const std::exception &e) { error ("Failed OpenEXR read: %s", e.what()); return false; } catch (...) { // catch-all for edge cases or compiler bugs error ("Failed OpenEXR read: unknown exception"); return false; } return true; } bool OpenEXRInput::read_native_deep_scanlines (int ybegin, int yend, int z, int chbegin, int chend, DeepData &deepdata) { if (m_deep_scanline_input_part == NULL) { error ("called OpenEXRInput::read_native_deep_scanlines without an open file"); return false; } #ifdef USE_OPENEXR_VERSION2 try { const PartInfo &part (m_parts[m_subimage]); size_t npixels = (yend - ybegin) * m_spec.width; chend = clamp (chend, chbegin+1, m_spec.nchannels); int nchans = chend - chbegin; // Set up the count and pointers arrays and the Imf framebuffer std::vector channeltypes; m_spec.get_channelformats (channeltypes); deepdata.init (npixels, nchans, array_view(&channeltypes[chbegin], chend-chbegin), spec().channelnames); std::vector all_samples (npixels); std::vector pointerbuf (npixels*nchans); Imf::DeepFrameBuffer frameBuffer; Imf::Slice countslice (Imf::UINT, (char *)(&all_samples[0] - m_spec.x - ybegin*m_spec.width), sizeof(unsigned int), sizeof(unsigned int) * m_spec.width); frameBuffer.insertSampleCountSlice (countslice); for (int c = chbegin; c < chend; ++c) { Imf::DeepSlice slice (part.pixeltype[c], (char *)(&pointerbuf[0]+(c-chbegin) - m_spec.x * nchans - ybegin*m_spec.width*nchans), sizeof(void*) * nchans, // xstride of pointer array sizeof(void*) * nchans*m_spec.width, // ystride of pointer array deepdata.samplesize()); // stride of data sample frameBuffer.insert (m_spec.channelnames[c].c_str(), slice); } m_deep_scanline_input_part->setFrameBuffer (frameBuffer); // Get the sample counts for each pixel and compute the total // number of samples and resize the data area appropriately. m_deep_scanline_input_part->readPixelSampleCounts (ybegin, yend-1); deepdata.set_all_samples (all_samples); deepdata.get_pointers (pointerbuf); // Read the pixels m_deep_scanline_input_part->readPixels (ybegin, yend-1); // deepdata.import_chansamp (pointerbuf); } catch (const std::exception &e) { error ("Failed OpenEXR read: %s", e.what()); return false; } catch (...) { // catch-all for edge cases or compiler bugs error ("Failed OpenEXR read: unknown exception"); return false; } return true; #else return false; #endif } bool OpenEXRInput::read_native_deep_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, DeepData &deepdata) { if (m_deep_tiled_input_part == NULL) { error ("called OpenEXRInput::read_native_deep_tiles without an open file"); return false; } #ifdef USE_OPENEXR_VERSION2 try { const PartInfo &part (m_parts[m_subimage]); size_t width = (xend - xbegin); size_t npixels = width * (yend - ybegin) * (zend - zbegin); chend = clamp (chend, chbegin+1, m_spec.nchannels); int nchans = chend - chbegin; // Set up the count and pointers arrays and the Imf framebuffer std::vector channeltypes; m_spec.get_channelformats (channeltypes); deepdata.init (npixels, nchans, array_view(&channeltypes[chbegin], chend-chbegin), spec().channelnames); std::vector all_samples (npixels); std::vector pointerbuf (npixels * nchans); Imf::DeepFrameBuffer frameBuffer; Imf::Slice countslice (Imf::UINT, (char *)(&all_samples[0] - xbegin - ybegin*width), sizeof(unsigned int), sizeof(unsigned int) * width); frameBuffer.insertSampleCountSlice (countslice); for (int c = chbegin; c < chend; ++c) { Imf::DeepSlice slice (part.pixeltype[c], (char *)(&pointerbuf[0]+(c-chbegin) - xbegin*nchans - ybegin*width*nchans), sizeof(void*) * nchans, // xstride of pointer array sizeof(void*) * nchans*width, // ystride of pointer array deepdata.samplesize()); // stride of data sample frameBuffer.insert (m_spec.channelnames[c].c_str(), slice); } m_deep_tiled_input_part->setFrameBuffer (frameBuffer); int xtiles = round_to_multiple (xend-xbegin, m_spec.tile_width) / m_spec.tile_width; int ytiles = round_to_multiple (yend-ybegin, m_spec.tile_height) / m_spec.tile_height; int firstxtile = (xbegin - m_spec.x) / m_spec.tile_width; int firstytile = (ybegin - m_spec.y) / m_spec.tile_height; // Get the sample counts for each pixel and compute the total // number of samples and resize the data area appropriately. m_deep_tiled_input_part->readPixelSampleCounts ( firstxtile, firstxtile+xtiles-1, firstytile, firstytile+ytiles-1); deepdata.set_all_samples (all_samples); deepdata.get_pointers (pointerbuf); // Read the pixels m_deep_tiled_input_part->readTiles ( firstxtile, firstxtile+xtiles-1, firstytile, firstytile+ytiles-1, m_miplevel, m_miplevel); } catch (const std::exception &e) { error ("Failed OpenEXR read: %s", e.what()); return false; } catch (...) { // catch-all for edge cases or compiler bugs error ("Failed OpenEXR read: unknown exception"); return false; } return true; #else return false; #endif } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/oiiotool/0000755000175000017500000000000013151711064016751 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/oiiotool/printinfo.cpp0000644000175000017500000007740313151711064021500 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #if defined(_MSC_VER) #include #endif #include #include #include #include #include "OpenImageIO/argparse.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/sysutil.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/deepdata.h" #include "OpenImageIO/hash.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/array_view.h" #include "oiiotool.h" OIIO_NAMESPACE_USING; using namespace OiioTool; using namespace ImageBufAlgo; static void print_sha1 (ImageInput *input) { SHA1 sha; const ImageSpec &spec (input->spec()); if (spec.deep) { // Special handling of deep data DeepData dd; if (! input->read_native_deep_image (dd)) { printf (" SHA-1: unable to compute, could not read image\n"); return; } // Hash both the sample counts and the data block sha.append (dd.all_samples()); sha.append (dd.all_data()); } else { imagesize_t size = input->spec().image_bytes (true /*native*/); if (size >= std::numeric_limits::max()) { printf (" SHA-1: unable to compute, image is too big\n"); return; } else if (size != 0) { boost::scoped_array buf (new char [size]); if (! input->read_image (TypeDesc::UNKNOWN /*native*/, &buf[0])) { printf (" SHA-1: unable to compute, could not read image\n"); return; } sha.append (&buf[0], size); } } printf (" SHA-1: %s\n", sha.digest().c_str()); } static void dump_data (ImageInput *input, const print_info_options &opt) { const ImageSpec &spec (input->spec()); if (spec.deep) { // Special handling of deep data DeepData dd; if (! input->read_native_deep_image (dd)) { printf (" dump data: could not read image\n"); return; } int nc = spec.nchannels; for (int z = 0, pixel = 0; z < spec.depth; ++z) { for (int y = 0; y < spec.height; ++y) { for (int x = 0; x < spec.width; ++x, ++pixel) { int nsamples = dd.samples(pixel); if (nsamples == 0 && ! opt.dumpdata_showempty) continue; std::cout << " Pixel ("; if (spec.depth > 1 || spec.z != 0) std::cout << Strutil::format("%d, %d, %d", x+spec.x, y+spec.y, z+spec.z); else std::cout << Strutil::format("%d, %d", x+spec.x, y+spec.y); std::cout << "): " << nsamples << " samples" << (nsamples ? ":" : ""); for (int s = 0; s < nsamples; ++s) { if (s) std::cout << " / "; for (int c = 0; c < nc; ++c) { std::cout << " " << spec.channelnames[c] << "="; if (dd.channeltype(c) == TypeDesc::UINT) std::cout << dd.deep_value_uint(pixel, c, s); else std::cout << dd.deep_value (pixel, c, s); } } std::cout << "\n"; } } } } else { std::vector buf(spec.image_pixels() * spec.nchannels); if (! input->read_image (TypeDesc::FLOAT, &buf[0])) { printf (" dump data: could not read image\n"); return; } const float *ptr = &buf[0]; for (int z = 0; z < spec.depth; ++z) { for (int y = 0; y < spec.height; ++y) { for (int x = 0; x < spec.width; ++x) { if (! opt.dumpdata_showempty) { bool allzero = true; for (int c = 0; c < spec.nchannels && allzero; ++c) allzero &= (ptr[c] == 0.0f); if (allzero) { ptr += spec.nchannels; continue; } } if (spec.depth > 1 || spec.z != 0) std::cout << Strutil::format(" Pixel (%d, %d, %d):", x+spec.x, y+spec.y, z+spec.z); else std::cout << Strutil::format(" Pixel (%d, %d):", x+spec.x, y+spec.y); for (int c = 0; c < spec.nchannels; ++c, ++ptr) { std::cout << ' ' << (*ptr); } std::cout << "\n"; } } } } } /////////////////////////////////////////////////////////////////////////////// // Stats static bool read_input (const std::string &filename, ImageBuf &img, int subimage=0, int miplevel=0) { if (img.subimage() >= 0 && img.subimage() == subimage) return true; if (img.init_spec (filename, subimage, miplevel)) { // Force a read now for reasonable-sized first images in the // file. This can greatly speed up the multithread case for // tiled images by not having multiple threads working on the // same image lock against each other on the file handle. // We guess that "reasonable size" is 200 MB, that's enough to // hold a 4k RGBA float image. Larger things will // simply fall back on ImageCache. bool forceread = (img.spec().image_bytes() < 200*1024*1024); return img.read (subimage, miplevel, forceread, TypeDesc::FLOAT); } return false; } static void print_stats_num (float val, int maxval, bool round) { // Ensure uniform printing of NaN and Inf on all platforms if (isnan(val)) printf ("nan"); else if (isinf(val)) printf ("inf"); else if (maxval == 0) { printf("%f",val); } else { float fval = val * static_cast(maxval); if (round) { int v = static_cast(roundf (fval)); printf ("%d", v); } else { printf ("%0.2f", fval); } } } // First check oiio:BitsPerSample int attribute. If not set, // fall back on the TypeDesc. return 0 for float types // or those that exceed the int range (long long, etc) static unsigned long long get_intsample_maxval (const ImageSpec &spec) { TypeDesc type = spec.format; int bits = spec.get_int_attribute ("oiio:BitsPerSample"); if (bits > 0) { if (type.basetype == TypeDesc::UINT8 || type.basetype == TypeDesc::UINT16 || type.basetype == TypeDesc::UINT32) return ((1LL) << bits) - 1; if (type.basetype == TypeDesc::INT8 || type.basetype == TypeDesc::INT16 || type.basetype == TypeDesc::INT32) return ((1LL) << (bits-1)) - 1; } // These correspond to all the int enums in typedesc.h <= int if (type.basetype == TypeDesc::UCHAR) return 0xff; if (type.basetype == TypeDesc::CHAR) return 0x7f; if (type.basetype == TypeDesc::USHORT) return 0xffff; if (type.basetype == TypeDesc::SHORT) return 0x7fff; if (type.basetype == TypeDesc::UINT) return 0xffffffff; if (type.basetype == TypeDesc::INT) return 0x7fffffff; return 0; } static void print_stats_footer (unsigned int maxval) { if (maxval==0) printf ("(float)"); else printf ("(of %u)", maxval); } static void print_stats (Oiiotool &ot, const std::string &filename, const ImageSpec &originalspec, int subimage=0, int miplevel=0, bool indentmip=false) { const char *indent = indentmip ? " " : " "; ImageBuf input; if (! read_input (filename, input, subimage, miplevel)) { ot.error ("stats", input.geterror()); return; } PixelStats stats; if (! computePixelStats (stats, input)) { std::string err = input.geterror(); ot.error ("stats", Strutil::format ("unable to compute: %s", err.empty() ? "unspecified error" : err.c_str())); return; } // The original spec is used, otherwise the bit depth will // be reported incorrectly (as FLOAT) unsigned int maxval = (unsigned int)get_intsample_maxval (originalspec); printf ("%sStats Min: ", indent); for (unsigned int i=0; ipixels(); size_t totalsamples = 0, emptypixels = 0; size_t maxsamples = 0, minsamples = std::numeric_limits::max(); size_t maxsamples_npixels = 0; float mindepth = std::numeric_limits::max(); float maxdepth = -std::numeric_limits::max(); Imath::V3i maxsamples_pixel(-1,-1,-1), minsamples_pixel(-1,-1,-1); Imath::V3i mindepth_pixel(-1,-1,-1), maxdepth_pixel(-1,-1,-1); Imath::V3i nonfinite_pixel(-1,-1,-1); int nonfinite_pixel_samp(-1), nonfinite_pixel_chan(-1); size_t sampoffset = 0; int nchannels = dd->channels(); int depthchannel = -1; long long nonfinites = 0; for (int c = 0; c < nchannels; ++c) if (Strutil::iequals (originalspec.channelnames[c], "Z")) depthchannel = c; int xend = originalspec.x + originalspec.width; int yend = originalspec.y + originalspec.height; int zend = originalspec.z + originalspec.depth; size_t p = 0; std::vector nsamples_histogram; for (int z = originalspec.z; z < zend; ++z) { for (int y = originalspec.y; y < yend; ++y) { for (int x = originalspec.x; x < xend; ++x, ++p) { size_t samples = input.deep_samples (x, y, z); totalsamples += samples; if (samples == maxsamples) ++maxsamples_npixels; if (samples > maxsamples) { maxsamples = samples; maxsamples_pixel.setValue (x, y, z); maxsamples_npixels = 1; } if (samples < minsamples) minsamples = samples; if (samples == 0) ++emptypixels; if (samples >= nsamples_histogram.size()) nsamples_histogram.resize (samples+1, 0); nsamples_histogram[samples] += 1; for (unsigned int s = 0; s < samples; ++s) { for (int c = 0; c < nchannels; ++c) { float d = input.deep_value (x, y, z, c, s); if (! isfinite(d)) { if (nonfinites++ == 0) { nonfinite_pixel.setValue (x, y, z); nonfinite_pixel_samp = s; nonfinite_pixel_chan = c; } } if (depthchannel == c) { if (d < mindepth) { mindepth = d; mindepth_pixel.setValue (x, y, z); } if (d > maxdepth) { maxdepth = d; maxdepth_pixel.setValue (x, y, z); } } } } sampoffset += samples; } } } printf ("%sMin deep samples in any pixel : %llu\n", indent, (unsigned long long)minsamples); printf ("%sMax deep samples in any pixel : %llu\n", indent, (unsigned long long)maxsamples); printf ("%s%llu pixel%s had the max of %llu samples, including (x=%d, y=%d)\n", indent, (unsigned long long)maxsamples_npixels, maxsamples_npixels > 1 ? "s" : "", (unsigned long long)maxsamples, maxsamples_pixel.x, maxsamples_pixel.y); printf ("%sAverage deep samples per pixel: %.2f\n", indent, double(totalsamples)/double(npixels)); printf ("%sTotal deep samples in all pixels: %llu\n", indent, (unsigned long long)totalsamples); printf ("%sPixels with deep samples : %llu\n", indent, (unsigned long long)(npixels-emptypixels)); printf ("%sPixels with no deep samples: %llu\n", indent, (unsigned long long)emptypixels); printf ("%sSamples/pixel histogram:\n", indent); size_t grandtotal = 0; for (size_t i = 0, e = nsamples_histogram.size(); i < e; ++i) grandtotal += nsamples_histogram[i]; size_t binstart = 0, bintotal = 0; for (size_t i = 0, e = nsamples_histogram.size(); i < e; ++i) { bintotal += nsamples_histogram[i]; if (i < 8 || i == (e-1) || OIIO::ispow2(i+1)) { // batch by powers of 2, unless it's a small number if (i == binstart) printf ("%s %3lld ", indent, (long long)i); else printf ("%s %3lld-%3lld", indent, (long long)binstart, (long long)i); printf (" : %8lld (%4.1f%%)\n", (long long)bintotal, (100.0*bintotal)/grandtotal); binstart = i+1; bintotal = 0; } } if (depthchannel >= 0) { printf ("%sMinimum depth was %g at (%d, %d)\n", indent, mindepth, mindepth_pixel.x, mindepth_pixel.y); printf ("%sMaximum depth was %g at (%d, %d)\n", indent, maxdepth, maxdepth_pixel.x, maxdepth_pixel.y); } if (nonfinites > 0) { printf ("%sNonfinite values: %lld, including (x=%d, y=%d, chan=%s, samp=%d)\n", indent, nonfinites, nonfinite_pixel.x, nonfinite_pixel.y, input.spec().channelnames[nonfinite_pixel_chan].c_str(), nonfinite_pixel_samp); } } else { std::vector constantValues(input.spec().nchannels); if (isConstantColor(input, &constantValues[0])) { printf ("%sConstant: Yes\n", indent); printf ("%sConstant Color: ", indent); for (unsigned int i=0; i 1) printf (", z=%d", spec.z); printf ("\n"); printed = true; } } if (spec.full_x || spec.full_y || spec.full_z || (spec.full_width != spec.width && spec.full_width != 0) || (spec.full_height != spec.height && spec.full_height != 0) || (spec.full_depth != spec.depth && spec.full_depth != 0)) { if (opt.metamatch.empty() || boost::regex_search ("full/display size", field_re)) { if (opt.filenameprefix) printf ("%s : ", filename.c_str()); printf (" full/display size: %d x %d", spec.full_width, spec.full_height); if (spec.depth > 1) printf (" x %d", spec.full_depth); printf ("\n"); printed = true; } if (opt.metamatch.empty() || boost::regex_search ("full/display origin", field_re)) { if (opt.filenameprefix) printf ("%s : ", filename.c_str()); printf (" full/display origin: %d, %d", spec.full_x, spec.full_y); if (spec.depth > 1) printf (", %d", spec.full_z); printf ("\n"); printed = true; } } if (spec.tile_width) { if (opt.metamatch.empty() || boost::regex_search ("tile", field_re)) { if (opt.filenameprefix) printf ("%s : ", filename.c_str()); printf (" tile size: %d x %d", spec.tile_width, spec.tile_height); if (spec.depth > 1) printf (" x %d", spec.tile_depth); printf ("\n"); printed = true; } } BOOST_FOREACH (const ImageIOParameter &p, spec.extra_attribs) { if (! opt.metamatch.empty() && ! boost::regex_search (p.name().c_str(), field_re)) continue; if (! opt.nometamatch.empty() && boost::regex_search (p.name().c_str(), field_exclude_re)) continue; std::string s = spec.metadata_val (p, true); if (opt.filenameprefix) printf ("%s : ", filename.c_str()); printf (" %s: ", p.name().c_str()); if (! strcmp (s.c_str(), "1.#INF")) printf ("inf"); else printf ("%s", s.c_str()); printf ("\n"); printed = true; } if (! printed && !opt.metamatch.empty()) { if (opt.filenameprefix) printf ("%s : ", filename.c_str()); printf (" %s: \n", opt.metamatch.c_str()); } } static const char * extended_format_name (TypeDesc type, int bits) { if (bits && bits < (int)type.size()*8) { // The "oiio:BitsPerSample" betrays a different bit depth in the // file than the data type we are passing. if (type == TypeDesc::UINT8 || type == TypeDesc::UINT16 || type == TypeDesc::UINT32 || type == TypeDesc::UINT64) return ustring::format("uint%d", bits).c_str(); if (type == TypeDesc::INT8 || type == TypeDesc::INT16 || type == TypeDesc::INT32 || type == TypeDesc::INT64) return ustring::format("int%d", bits).c_str(); } return type.c_str(); // use the name implied by type } static const char * brief_format_name (TypeDesc type, int bits=0) { if (! bits) bits = (int)type.size()*8; if (type.is_floating_point()) { if (type.basetype == TypeDesc::FLOAT) return "f"; if (type.basetype == TypeDesc::HALF) return "h"; return ustring::format("f%d", bits).c_str(); } else if (type.is_signed()) { return ustring::format("i%d", bits).c_str(); } else { return ustring::format("u%d", bits).c_str(); } return type.c_str(); // use the name implied by type } // prints basic info (resolution, width, height, depth, channels, data format, // and format name) about given subimage. static void print_info_subimage (Oiiotool &ot, int current_subimage, int max_subimages, ImageSpec &spec, ImageInput *input, const std::string &filename, const print_info_options &opt, boost::regex &field_re, boost::regex &field_exclude_re) { if ( ! input->seek_subimage (current_subimage, 0, spec) ) return; int nmip = 1; bool printres = opt.verbose && (opt.metamatch.empty() || boost::regex_search ("resolution, width, height, depth, channels", field_re)); if (printres && max_subimages > 1 && opt.subimages) { printf (" subimage %2d: ", current_subimage); printf ("%4d x %4d", spec.width, spec.height); if (spec.depth > 1) printf (" x %4d", spec.depth); printf (", %d channel, %s%s", spec.nchannels, spec.deep ? "deep " : "", spec.depth > 1 ? "volume " : ""); if (spec.channelformats.size()) { for (size_t c = 0; c < spec.channelformats.size(); ++c) printf ("%s%s", c ? "/" : "", spec.channelformats[c].c_str()); } else { int bits = spec.get_int_attribute ("oiio:BitsPerSample", 0); printf ("%s", extended_format_name(spec.format, bits)); } printf (" %s", input->format_name()); printf ("\n"); } // Count MIP levels ImageSpec mipspec; while (input->seek_subimage (current_subimage, nmip, mipspec)) { if (printres) { if (nmip == 1) printf (" MIP-map levels: %dx%d", spec.width, spec.height); printf (" %dx%d", mipspec.width, mipspec.height); } ++nmip; } if (printres && nmip > 1) printf ("\n"); if (opt.compute_sha1 && (opt.metamatch.empty() || boost::regex_search ("sha-1", field_re))) { if (opt.filenameprefix) printf ("%s : ", filename.c_str()); // Before sha-1, be sure to point back to the highest-res MIP level ImageSpec tmpspec; input->seek_subimage (current_subimage, 0, tmpspec); print_sha1 (input); } if (opt.verbose) print_metadata (spec, filename, opt, field_re, field_exclude_re); if (opt.dumpdata) { ImageSpec tmp; input->seek_subimage (current_subimage, 0, tmp); dump_data (input, opt); } if (opt.compute_stats && (opt.metamatch.empty() || boost::regex_search ("stats", field_re))) { for (int m = 0; m < nmip; ++m) { ImageSpec mipspec; input->seek_subimage (current_subimage, m, mipspec); if (opt.filenameprefix) printf ("%s : ", filename.c_str()); if (nmip > 1) { printf (" MIP %d of %d (%d x %d):\n", m, nmip, mipspec.width, mipspec.height); } print_stats (ot, filename, spec, current_subimage, m, nmip>1); } } if ( ! input->seek_subimage (current_subimage, 0, spec) ) return; } bool OiioTool::print_info (Oiiotool &ot, const std::string &filename, const print_info_options &opt, long long &totalsize, std::string &error) { error.clear(); ImageSpec *config = ot.input_config_set ? &ot.input_config : NULL; ImageInput *input = ImageInput::open (filename.c_str(), config); if (! input) { error = geterror(); if (error.empty()) error = Strutil::format ("Could not open \"%s\"", filename.c_str()); return false; } ImageSpec spec = input->spec(); boost::regex field_re; boost::regex field_exclude_re; if (! opt.metamatch.empty()) { try { field_re.assign (opt.metamatch, boost::regex::extended | boost::regex_constants::icase); } catch (const std::exception &e) { error = Strutil::format ("Regex error '%s' on metamatch regex \"%s\"", e.what(), opt.metamatch); return false; } } if (! opt.nometamatch.empty()) { try { field_exclude_re.assign (opt.nometamatch, boost::regex::extended | boost::regex_constants::icase); } catch (const std::exception &e) { error = Strutil::format ("Regex error '%s' on metamatch regex \"%s\"", e.what(), opt.nometamatch); return false; } } int padlen = std::max (0, (int)opt.namefieldlength - (int)filename.length()); std::string padding (padlen, ' '); // checking how many subimages and mipmap levels are stored in the file int num_of_subimages = 1; bool any_mipmapping = false; std::vector num_of_miplevels; { int nmip = 1; while (input->seek_subimage (input->current_subimage(), nmip, spec)) { ++nmip; any_mipmapping = true; } num_of_miplevels.push_back (nmip); } while (input->seek_subimage (num_of_subimages, 0, spec)) { // maybe we should do this more gently? ++num_of_subimages; int nmip = 1; while (input->seek_subimage (input->current_subimage(), nmip, spec)) { ++nmip; any_mipmapping = true; } num_of_miplevels.push_back (nmip); } input->seek_subimage (0, 0, spec); // re-seek to the first if (opt.metamatch.empty() || boost::regex_search ("resolution, width, height, depth, channels", field_re)) { printf ("%s%s : %4d x %4d", filename.c_str(), padding.c_str(), spec.width, spec.height); if (spec.depth > 1) printf (" x %4d", spec.depth); printf (", %d channel, %s%s", spec.nchannels, spec.deep ? "deep " : "", spec.depth > 1 ? "volume " : ""); if (spec.channelformats.size()) { for (size_t c = 0; c < spec.channelformats.size(); ++c) printf ("%s%s", c ? "/" : "", spec.channelformats[c].c_str()); } else { int bits = spec.get_int_attribute ("oiio:BitsPerSample", 0); printf ("%s", extended_format_name(spec.format, bits)); } printf (" %s", input->format_name()); if (opt.sum) { imagesize_t imagebytes = spec.image_bytes (true); totalsize += imagebytes; printf (" (%.2f MB)", (float)imagebytes / (1024.0*1024.0)); } // we print info about how many subimages are stored in file // only when we have more then one subimage if ( ! opt.verbose && num_of_subimages != 1) printf (" (%d subimages%s)", num_of_subimages, any_mipmapping ? " +mipmap)" : ""); if (! opt.verbose && num_of_subimages == 1 && any_mipmapping) printf (" (+mipmap)"); printf ("\n"); } int movie = spec.get_int_attribute ("oiio:Movie"); if (opt.verbose && num_of_subimages != 1) { // info about num of subimages and their resolutions printf (" %d subimages: ", num_of_subimages); for (int i = 0; i < num_of_subimages; ++i) { input->seek_subimage (i, 0, spec); int bits = spec.get_int_attribute ("oiio:BitsPerSample", spec.format.size()*8); if (i) printf (", "); if (spec.depth > 1) printf ("%dx%dx%d ", spec.width, spec.height, spec.depth); else printf ("%dx%d ", spec.width, spec.height); // printf ("["); for (int c = 0; c < spec.nchannels; ++c) printf ("%c%s", c ? ',' : '[', brief_format_name(spec.channelformat(c), bits)); printf ("]"); if (movie) break; } printf ("\n"); } // if the '-a' flag is not set we print info // about first subimage only if ( ! opt.subimages) num_of_subimages = 1; for (int i = 0; i < num_of_subimages; ++i) { print_info_subimage (ot, i, num_of_subimages, spec, input, filename, opt, field_re, field_exclude_re); } input->close (); delete input; return true; } openimageio-1.7.17~dfsg0.orig/src/oiiotool/CMakeLists.txt0000644000175000017500000000043413151711064021512 0ustar mfvmfvset (oiiotool_srcs oiiotool.cpp diff.cpp imagerec.cpp printinfo.cpp) add_executable (oiiotool ${oiiotool_srcs}) set_target_properties (oiiotool PROPERTIES FOLDER "Tools") target_link_libraries (oiiotool OpenImageIO ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) oiio_install_targets (oiiotool) openimageio-1.7.17~dfsg0.orig/src/oiiotool/diff.cpp0000644000175000017500000001737613151711064020403 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include #include #include "OpenImageIO/argparse.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/filter.h" #include "oiiotool.h" OIIO_NAMESPACE_USING using namespace OiioTool; using namespace ImageBufAlgo; // function that standarize printing NaN and Inf values on // Windows (where they are in 1.#INF, 1.#NAN format) and all // others platform inline void safe_double_print (double val) { if (OIIO::isnan (val)) std::cout << "nan"; else if (OIIO::isinf (val)) std::cout << "inf"; else std::cout << val; std::cout << '\n'; } inline void print_subimage (ImageRec &img0, int subimage, int miplevel) { if (img0.subimages() > 1) std::cout << "Subimage " << subimage << ' '; if (img0.miplevels(subimage) > 1) std::cout << " MIP level " << miplevel << ' '; if (img0.subimages() > 1 || img0.miplevels(subimage) > 1) std::cout << ": "; const ImageSpec &spec (*img0.spec(subimage)); std::cout << spec.width << " x " << spec.height; if (spec.depth > 1) std::cout << " x " << spec.depth; std::cout << ", " << spec.nchannels << " channel\n"; } int OiioTool::do_action_diff (ImageRec &ir0, ImageRec &ir1, Oiiotool &ot, int perceptual) { std::cout << "Computing " << (perceptual ? "perceptual " : "") << "diff of \"" << ir0.name() << "\" vs \"" << ir1.name() << "\"\n"; ir0.read (); ir1.read (); int ret = DiffErrOK; for (int subimage = 0; subimage < ir0.subimages(); ++subimage) { if (subimage > 0 && !ot.allsubimages) break; if (subimage >= ir1.subimages()) break; for (int m = 0; m < ir0.miplevels(subimage); ++m) { if (m > 0 && !ot.allsubimages) break; if (m > 0 && ir0.miplevels(subimage) != ir1.miplevels(subimage)) { std::cout << "Files do not match in their number of MIPmap levels\n"; ret = DiffErrDifferentSize; break; } ImageBuf &img0 (ir0(subimage,m)); ImageBuf &img1 (ir1(subimage,m)); int npels = img0.spec().width * img0.spec().height * img0.spec().depth; if (npels == 0) npels = 1; // Avoid divide by zero for 0x0 images ASSERT (img0.spec().format == TypeDesc::FLOAT); // Compare the two images. // ImageBufAlgo::CompareResults cr; int yee_failures = 0; switch (perceptual) { case 1 : yee_failures = ImageBufAlgo::compare_Yee (img0, img1, cr); break; default: ImageBufAlgo::compare (img0, img1, ot.diff_failthresh, ot.diff_warnthresh, cr); break; } if (cr.nfail > (ot.diff_failpercent/100.0 * npels) || cr.maxerror > ot.diff_hardfail || yee_failures > (ot.diff_failpercent/100.0 * npels)) { ret = DiffErrFail; } else if (cr.nwarn > (ot.diff_warnpercent/100.0 * npels) || cr.maxerror > ot.diff_hardwarn) { if (ret != DiffErrFail) ret = DiffErrWarn; } // Print the report // if (ot.verbose || ot.debug || ret != DiffErrOK) { if (ot.allsubimages) print_subimage (ir0, subimage, m); std::cout << " Mean error = "; safe_double_print (cr.meanerror); std::cout << " RMS error = "; safe_double_print (cr.rms_error); std::cout << " Peak SNR = "; safe_double_print (cr.PSNR); std::cout << " Max error = " << cr.maxerror; if (cr.maxerror != 0) { std::cout << " @ (" << cr.maxx << ", " << cr.maxy; if (img0.spec().depth > 1) std::cout << ", " << cr.maxz; if (cr.maxc < (int)img0.spec().channelnames.size()) std::cout << ", " << img0.spec().channelnames[cr.maxc] << ')'; else if (cr.maxc < (int)img1.spec().channelnames.size()) std::cout << ", " << img1.spec().channelnames[cr.maxc] << ')'; else std::cout << ", channel " << cr.maxc << ')'; } std::cout << "\n"; std::streamsize precis = std::cout.precision(); std::cout << " " << cr.nwarn << " pixels (" << std::setprecision(3) << (100.0*cr.nwarn / npels) << std::setprecision(precis) << "%) over " << ot.diff_warnthresh << "\n"; std::cout << " " << cr.nfail << " pixels (" << std::setprecision(3) << (100.0*cr.nfail / npels) << std::setprecision(precis) << "%) over " << ot.diff_failthresh << "\n"; if (perceptual == 1) std::cout << " " << yee_failures << " pixels (" << std::setprecision(3) << (100.0*yee_failures / npels) << std::setprecision(precis) << "%) failed the perceptual test\n"; } } } if (ot.allsubimages && ir0.subimages() != ir1.subimages()) { std::cout << "Images had differing numbers of subimages (" << ir0.subimages() << " vs " << ir1.subimages() << ")\n"; ret = DiffErrFail; } if (!ot.allsubimages && (ir0.subimages() > 1 || ir1.subimages() > 1)) { std::cout << "Only compared the first subimage (of " << ir0.subimages() << " and " << ir1.subimages() << ", respectively)\n"; } if (ret == DiffErrOK) std::cout << "PASS\n"; else if (ret == DiffErrWarn) std::cout << "WARNING\n"; else { std::cout << "FAILURE\n"; ot.return_value = ret; } return ret; } openimageio-1.7.17~dfsg0.orig/src/oiiotool/oiiotool.h0000644000175000017500000006441213151711064020766 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #ifndef OIIOTOOL_H #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/refcnt.h" #include "OpenImageIO/timer.h" #include "OpenImageIO/sysutil.h" OIIO_NAMESPACE_BEGIN namespace OiioTool { typedef int (*CallbackFunction)(int argc,const char*argv[]); class ImageRec; typedef shared_ptr ImageRecRef; /// Polycy hints for reading images enum ReadPolicy { ReadDefault = 0, //< Default: use cache, maybe convert to float. //< For "small" files, may bypass cache. ReadNative = 1, //< Keep in native type, use cache if it supports //< the native type, bypass if not. May still //< bypass cache for "small" images. ReadNoCache = 2, //< Bypass the cache regardless of size (beware!), //< but still subject to format conversion. ReadNativeNoCache = 3, //< No cache, no conversion. Do it all now. //< You better know what you're doing. }; class Oiiotool { public: // General options bool verbose; bool debug; bool dryrun; bool runstats; bool noclobber; bool allsubimages; bool printinfo; bool printstats; bool dumpdata; bool dumpdata_showempty; bool hash; bool updatemode; bool autoorient; bool autocc; // automatically color correct bool nativeread; // force native data type reads int cachesize; int autotile; int frame_padding; std::string full_command_line; std::string printinfo_metamatch; std::string printinfo_nometamatch; ImageSpec input_config; // configuration options for reading bool input_config_set; // Output options TypeDesc output_dataformat; std::map output_channelformats; int output_bitspersample; bool output_scanline; int output_tilewidth, output_tileheight; std::string output_compression; int output_quality; std::string output_planarconfig; bool output_adjust_time; bool output_autocrop; bool output_autotrim; bool output_dither; bool output_force_tiles; // for debugging bool metadata_nosoftwareattrib; // Options for --diff float diff_warnthresh; float diff_warnpercent; float diff_hardwarn; float diff_failthresh; float diff_failpercent; float diff_hardfail; // Internal state ImageRecRef curimg; // current image std::vector image_stack; // stack of previous images std::map image_labels; // labeled images ImageCache *imagecache; // back ptr to ImageCache int return_value; // oiiotool command return code ColorConfig colorconfig; // OCIO color config Timer total_readtime; Timer total_writetime; double total_imagecache_readtime; typedef std::map TimingMap; TimingMap function_times; bool enable_function_timing; size_t peak_memory; int num_outputs; // Count of outputs written bool printed_info; // printed info at some point int frame_number; Oiiotool (); void clear_options (); /// Force img to be read at this point. Use this wrapper, don't directly /// call img->read(), because there's extra work done here specific to /// oiiotool. bool read (ImageRecRef img, ReadPolicy readpolicy = ReadDefault); // Read the current image bool read (ReadPolicy readpolicy = ReadDefault) { if (curimg) return read (curimg, readpolicy); return true; } // If required_images are not yet on the stack, then postpone this // call by putting it on the 'pending' list and return true. // Otherwise (if enough images are on the stack), return false. bool postpone_callback (int required_images, CallbackFunction func, int argc, const char *argv[]); // Process any pending commands. void process_pending (); CallbackFunction pending_callback () const { return m_pending_callback; } const char *pending_callback_name () const { return m_pending_argv[0]; } void push (const ImageRecRef &img) { if (img) { if (curimg) image_stack.push_back (curimg); curimg = img; } } void push (ImageRec *newir) { push (ImageRecRef(newir)); } ImageRecRef pop () { ImageRecRef r = curimg; if (image_stack.size()) { // There are images on the full stack -- pop it curimg = image_stack.back (); image_stack.resize (image_stack.size()-1); } else { // Nothing on the stack, so get rid of the current image curimg = ImageRecRef(); } return r; } ImageRecRef top () { return curimg; } // How many images are on the stack? int image_stack_depth () const { return curimg ? 1+int(image_stack.size()) : 0; } // Parse geom in the form of "x,y" to retrieve a 2D integer position. bool get_position (string_view command, string_view geom, int &x, int &y); // Modify the resolution and/or offset according to what's in geom. // Valid geometries are WxH (resolution), +X+Y (offsets), WxH+X+Y // (resolution and offset). If 'allow_scaling' is true, geometries of // S% (e.g. "50%") or just S (e.g., "1.2") will be accepted to scale the // existing width and height (rounding to the nearest whole number of // pixels. bool adjust_geometry (string_view command, int &w, int &h, int &x, int &y, const char *geom, bool allow_scaling=false); // Expand substitution expressions in string str. Expressions are // enclosed in braces: {...}. An expression consists of: // * a numeric constant ("42" or "3.14") // * arbitrary math using operators +, -, *, / and parentheses // (order of operations is respected). // * IMG[n].metadata for the metadata of an image. The 'n' may be an // image name, or an integer giving stack position (for example, // "IMG[0]" is the top of the stack; also "TOP" is a synonym). The // metadata can be any of the usual named metadata from the image's // spec, such as "width", "ImageDescription", etc. string_view express (string_view str); int extract_options (std::map &options, std::string command); void error (string_view command, string_view explanation=""); void warning (string_view command, string_view explanation=""); size_t check_peak_memory () { size_t mem = Sysutil::memory_used(); peak_memory = std::max (peak_memory, mem); return mem; } private: CallbackFunction m_pending_callback; int m_pending_argc; const char *m_pending_argv[4]; void express_error (const string_view expr, const string_view s, string_view explanation); bool express_parse_atom (const string_view expr, string_view& s, std::string& result); bool express_parse_factors (const string_view expr, string_view& s, std::string& result); bool express_parse_summands (const string_view expr, string_view& s, std::string& result); std::string express_impl (string_view s); }; typedef shared_ptr ImageBufRef; class SubimageRec { public: int miplevels() const { return (int) m_miplevels.size(); } ImageBuf * operator() () { return miplevels() ? m_miplevels[0].get() : NULL; } ImageBuf * operator[] (int i) { return i < miplevels() ? m_miplevels[i].get() : NULL; } const ImageBuf * operator[] (int i) const { return i < miplevels() ? m_miplevels[i].get() : NULL; } ImageSpec * spec (int i) { return i < miplevels() ? &m_specs[i] : NULL; } const ImageSpec * spec (int i) const { return i < miplevels() ? &m_specs[i] : NULL; } private: std::vector m_miplevels; std::vector m_specs; friend class ImageRec; }; class ImageRec { public: ImageRec (const std::string &name, ImageCache *imagecache) : m_name(name), m_elaborated(false), m_metadata_modified(false), m_pixels_modified(false), m_imagecache(imagecache) { } // Initialize an ImageRec with a collection of prepared ImageSpec's. // The number of subimages is nsubimages, the number of MIP levels // for each subimages is in miplevels[0..nsubimages-1] (if miplevels // is NULL, allocate just one MIP level per subimage), and specs[] // contains the specs for all the MIP levels of subimage 0, followed // by all the specs for the MIP levels of subimage 1, and so on. // If spec == NULL, the IB's will not be fully allocated/initialized. ImageRec (const std::string &name, int nsubimages = 1, const int *miplevels = NULL, const ImageSpec *specs=NULL); // Copy an existing ImageRec. Copy just the single subimage_to_copy // if >= 0, or all subimages if <0. Copy just the single // miplevel_to_copy if >= 0, or all MIP levels if <0. If writable // is true, we expect to need to alter the pixels of the resulting // ImageRec. If copy_pixels is false, just make the new image big // enough, no need to initialize the pixel values. ImageRec (ImageRec &img, int subimage_to_copy = -1, int miplevel_to_copy = -1, bool writable = true, bool copy_pixels = true); // Create an ImageRef that consists of the ImageBuf img. Copy img // if copy_pixels==true, otherwise just take ownership of img (it's // a shared pointer). ImageRec (ImageBufRef img, bool copy_pixels = true); // Initialize an ImageRec with the given spec. ImageRec (const std::string &name, const ImageSpec &spec, ImageCache *imagecache); enum WinMerge { WinMergeUnion, WinMergeIntersection, WinMergeA, WinMergeB }; // Initialize a new ImageRec based on two exemplars. Initialize // just the single subimage_to_copy if >= 0, or all subimages if <0. // The two WinMerge parameters pixwin and fullwin, dictate the // policy for setting up the pixel data and full (display) windows, // respectively. If pixeltype not UNKNOWN, use that rather than // A's pixel type (the default behavior). ImageRec (ImageRec &imgA, ImageRec &imgB, int subimage_to_copy = -1, WinMerge pixwin = WinMergeUnion, WinMerge fullwin = WinMergeUnion, TypeDesc pixeltype = TypeDesc::UNKNOWN); // Number of subimages int subimages() const { return (int) m_subimages.size(); } // Number of MIP levels of the given subimage int miplevels (int subimage=0) const { if (subimage >= subimages()) return 0; return m_subimages[subimage].miplevels(); } // Subimage reference accessors. SubimageRec& subimage (int i) { return m_subimages[i]; } const SubimageRec& subimage (int i) const { return m_subimages[i]; } // Accessing it like an array returns a specific subimage SubimageRec& operator[] (int i) { return m_subimages[i]; } const SubimageRec& operator[] (int i) const { return m_subimages[i]; } std::string name () const { return m_name; } // Has the ImageRec been actually read or evaluated? (Until needed, // it's lazily kept as name only, without reading the file.) bool elaborated () const { return m_elaborated; } bool read (ReadPolicy readpolicy = ReadDefault); // ir(subimg,mip) references a specific MIP level of a subimage // ir(subimg) references the first MIP level of a subimage // ir() references the first MIP level of the first subimage ImageBuf& operator() (int subimg=0, int mip=0) { return *m_subimages[subimg][mip]; } const ImageBuf& operator() (int subimg=0, int mip=0) const { return *m_subimages[subimg][mip]; } ImageSpec * spec (int subimg=0, int mip=0) { return subimg < subimages() ? m_subimages[subimg].spec(mip) : NULL; } const ImageSpec * spec (int subimg=0, int mip=0) const { return subimg < subimages() ? m_subimages[subimg].spec(mip) : NULL; } bool was_output () const { return m_was_output; } void was_output (bool val) { m_was_output = val; } bool metadata_modified () const { return m_metadata_modified; } void metadata_modified (bool mod) { m_metadata_modified = mod; if (mod) was_output(false); } bool pixels_modified () const { return m_pixels_modified; } void pixels_modified (bool mod) { m_pixels_modified = mod; if (mod) was_output(false); } std::time_t time() const { return m_time; } // This should be called if for some reason the underlying // ImageBuf's spec may have been modified in place. We need to // update the outer copy held by the SubimageRec. void update_spec_from_imagebuf (int subimg=0, int mip=0) { *m_subimages[subimg].spec(mip) = m_subimages[subimg][mip]->spec(); metadata_modified (true); } // Get or set the configuration spec that will be used any time the // image is opened. const ImageSpec * configspec () const { return &m_configspec; } void configspec (const ImageSpec &spec) { m_configspec = spec; } void clear_configspec () { configspec (ImageSpec()); } /// Error reporting for ImageRec: call this with printf-like arguments. /// Note however that this is fully typesafe! /// void error (const char *format, ...) TINYFORMAT_WRAP_FORMAT (void, error, const, std::ostringstream msg;, msg, append_error(msg.str());) /// Return true if the IR has had an error and has an error message /// to retrieve via geterror(). bool has_error (void) const; /// Return the text of all error messages issued since geterror() was /// called (or an empty string if no errors are pending). This also /// clears the error message for next time if clear_error is true. std::string geterror (bool clear_error = true) const; private: std::string m_name; bool m_elaborated; bool m_metadata_modified; bool m_pixels_modified; bool m_was_output; std::vector m_subimages; std::time_t m_time; //< Modification time of the input file ImageCache *m_imagecache; mutable std::string m_err; ImageSpec m_configspec; // Add to the error message void append_error (string_view message) const; }; struct print_info_options { bool verbose; bool filenameprefix; bool sum; bool subimages; bool compute_sha1; bool compute_stats; bool dumpdata; bool dumpdata_showempty; std::string metamatch; std::string nometamatch; size_t namefieldlength; print_info_options () : verbose(false), filenameprefix(false), sum(false), subimages(false), compute_sha1(false), compute_stats(false), dumpdata(false), dumpdata_showempty(true), namefieldlength(20) {} }; // Print info about the named file to stdout, using print_info_options // opt for guidance on what to print and how to do it. The total size // of the uncompressed pixels in the file is returned in totalsize. The // return value will be true if everything is ok, or false if there is // an error (in which case the error message will be stored in 'error'). bool print_info (Oiiotool &ot, const std::string &filename, const print_info_options &opt, long long &totalsize, std::string &error); // Set an attribute of the given image. The type should be one of // TypeDesc::INT (decode the value as an int), FLOAT, STRING, or UNKNOWN // (look at the string and try to discern whether it's an int, float, or // string). If the 'value' string is empty, it will delete the // attribute. If allsubimages is true, apply the attribute to all // subimages, otherwise just the first subimage. bool set_attribute (ImageRecRef img, string_view attribname, TypeDesc type, string_view value, bool allsubimages); inline bool same_size (const ImageBuf &A, const ImageBuf &B) { const ImageSpec &a (A.spec()), &b (B.spec()); return (a.width == b.width && a.height == b.height && a.depth == b.depth && a.nchannels == b.nchannels); } enum DiffErrors { DiffErrOK = 0, ///< No errors, the images match exactly DiffErrWarn, ///< Warning: the errors differ a little DiffErrFail, ///< Failure: the errors differ a lot DiffErrDifferentSize, ///< Images aren't even the same size DiffErrFile, ///< Could not find or open input files, etc. DiffErrLast }; int do_action_diff (ImageRec &ir0, ImageRec &ir1, Oiiotool &options, int perceptual = 0); // Helper template -- perform the action on each spec in the ImageRec. // The action needs a signature like: // bool action(ImageSpec &spec, const T& t)) template bool apply_spec_mod (ImageRec &img, Action act, const Type &t, bool allsubimages) { bool ok = true; img.read (); img.metadata_modified (true); for (int s = 0, send = img.subimages(); s < send; ++s) { for (int m = 0, mend = img.miplevels(s); m < mend; ++m) { ok &= act (img(s,m).specmod(), t); if (ok) img.update_spec_from_imagebuf (s, m); if (! allsubimages) break; } if (! allsubimages) break; } return ok; } /// Base class for an Oiiotool operation/command. Rather than repeating /// code, this provides the boilerplate that nearly every op must do, /// with just a couple tiny places that need to be overridden for each op, /// generally only the impl() method. /// class OiiotoolOp { public: // The constructor records the arguments (including running them // through expression substitution) and pops the input images off the // stack. OiiotoolOp (Oiiotool &ot, string_view opname, int argc, const char *argv[], int ninputs) : ot(ot), m_opname(opname), m_nargs(argc), m_nimages(ninputs+1) { args.reserve (argc); for (int i = 0; i < argc; ++i) args.push_back (ot.express (argv[i])); ir.resize (ninputs+1); // including reserving a spot for result for (int i = 0; i < ninputs; ++i) ir[ninputs-i] = ot.pop(); } virtual ~OiiotoolOp () {} // The operator(), function-call mode, does most of the work. Although // it's virtual, in general you shouldn't need to override it. Instead, // just override impl(), and maybe option_defaults. virtual int operator() () { // Set up a timer to automatically record how much time is spent in // every class of operation. Timer timer (ot.enable_function_timing); if (ot.debug) { std::cout << "Performing '" << opname() << "'"; if (nargs() > 1) std::cout << " with args: "; for (int i = 0; i < nargs(); ++i) std::cout << (i > 0 ? ", \"" : " \"") << args[i] << "\""; std::cout << "\n"; } // Parse the options. options.clear (); options["allsubimages"] = ot.allsubimages; option_defaults (); // this can be customized to set up defaults ot.extract_options (options, args[0]); // Read all input images, and reserve (and push) the output image. int subimages = compute_subimages(); if (nimages()) { // Read the inputs for (int i = 1; i < nimages(); ++i) ot.read (ir[i]); // Initialize the output image ir[0].reset (new ImageRec (opname(), subimages)); ot.push (ir[0]); } // Give a chance for customization before we walk the subimages. // If the setup method returns false, we're done. if (! setup ()) return 0; // For each subimage, find the ImageBuf's for input and output // images, and call impl(). for (int s = 0; s < subimages; ++s) { // Get pointers for the ImageBufs for this subimage img.resize (nimages()); for (int i = 0; i < nimages(); ++i) img[i] = &((*ir[i])(std::min (s, ir[i]->subimages()-1))); // Call the impl kernel for this subimage bool ok = impl (nimages() ? &img[0] : NULL); if (! ok) ot.error (opname(), img[0]->geterror()); ir[0]->update_spec_from_imagebuf (s); } // Make sure to forward any errors missed by the impl for (int i = 0; i < nimages(); ++i) { if (img[i]->has_error()) ot.error (opname(), img[i]->geterror()); } if (ot.debug || ot.runstats) ot.check_peak_memory(); // Optional cleanup after processing all the subimages cleanup (); // Add the time we spent to the stats total for this op type. ot.function_times[opname()] += timer(); return 0; } // THIS is the method that needs to be separately overloaded for each // different op. This is called once for each subimage, generally with // img[0] the destination ImageBuf, and img[1..] as the inputs. virtual int impl (ImageBuf **img) = 0; // Extra place to inject customization before the subimages are // traversed. virtual bool setup () { return true; } // Extra place to inject customization after the subimges are traversed. virtual bool cleanup () { return true; } // Override this if the impl uses options and needs any of them set // to defaults. This will be called separate virtual void option_defaults () { } // Default subimage logic: if the global -a flag was set or if this command // had ":allsubimages=1" option set, then apply the command to all subimages // (of the first input image). Otherwise, we'll only apply the command to // the first subimage. Override this is you want another behavior. virtual int compute_subimages () { int all_subimages = Strutil::from_string(options["allsubimages"]); return all_subimages ? (nimages() > 1 ? ir[1]->subimages() : 1) : 1; } int nargs () const { return m_nargs; } int nimages () const { return m_nimages; } string_view opname () const { return m_opname; } protected: Oiiotool &ot; std::string m_opname; int m_nargs; int m_nimages; std::vector ir; std::vector img; std::vector args; std::map options; }; typedef bool (*IBAunary) (ImageBuf &dst, const ImageBuf &A, ROI roi, int nthreads); typedef bool (*IBAbinary) (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, ROI roi, int nthreads); typedef bool (*IBAbinary_img_col) (ImageBuf &dst, const ImageBuf &A, const float *B, ROI roi, int nthreads); template class OiiotoolSimpleUnaryOp : public OiiotoolOp { public: OiiotoolSimpleUnaryOp (IBLIMPL opimpl, Oiiotool &ot, string_view opname, int argc, const char *argv[], int ninputs) : OiiotoolOp (ot, opname, argc, argv, 1), opimpl(opimpl) {} virtual int impl (ImageBuf **img) { return opimpl (*img[0], *img[1], ROI(), 0); } protected: IBLIMPL opimpl; }; template class OiiotoolSimpleBinaryOp : public OiiotoolOp { public: OiiotoolSimpleBinaryOp (IBLIMPL opimpl, Oiiotool &ot, string_view opname, int argc, const char *argv[], int ninputs) : OiiotoolOp (ot, opname, argc, argv, 2), opimpl(opimpl) {} virtual int impl (ImageBuf **img) { return opimpl (*img[0], *img[1], *img[2], ROI(), 0); } protected: IBLIMPL opimpl; }; template class OiiotoolImageColorOp : public OiiotoolOp { public: OiiotoolImageColorOp (IBLIMPL opimpl, Oiiotool &ot, string_view opname, int argc, const char *argv[], int ninputs, float defaultval=0.0f) : OiiotoolOp (ot, opname, argc, argv, 1), opimpl(opimpl), defaultval(defaultval) {} virtual int impl (ImageBuf **img) { int nchans = img[1]->spec().nchannels; std::vector val (nchans, defaultval); int nvals = Strutil::extract_from_list_string (val, args[1]); val.resize (nvals); val.resize (nchans, val.size() == 1 ? val.back() : defaultval); return opimpl (*img[0], *img[1], &val[0], ROI(), 0); } protected: IBLIMPL opimpl; float defaultval; }; } // OiioTool namespace OIIO_NAMESPACE_END; #endif // OIIOTOOL_H openimageio-1.7.17~dfsg0.orig/src/oiiotool/imagerec.cpp0000644000175000017500000003022113151711064021227 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include #include #include "OpenImageIO/argparse.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/filter.h" #include "OpenImageIO/thread.h" #include "oiiotool.h" OIIO_NAMESPACE_USING using namespace OiioTool; using namespace ImageBufAlgo; ImageRec::ImageRec (const std::string &name, int nsubimages, const int *miplevels, const ImageSpec *specs) : m_name(name), m_elaborated(true), m_metadata_modified(false), m_pixels_modified(true), m_was_output(false), m_imagecache(NULL) { int specnum = 0; m_subimages.resize (nsubimages); for (int s = 0; s < nsubimages; ++s) { int nmips = miplevels ? miplevels[s] : 1; m_subimages[s].m_miplevels.resize (nmips); m_subimages[s].m_specs.resize (nmips); for (int m = 0; m < nmips; ++m) { ImageBuf *ib = specs ? new ImageBuf (specs[specnum]) : new ImageBuf (); m_subimages[s].m_miplevels[m].reset (ib); if (specs) m_subimages[s].m_specs[m] = specs[specnum]; ++specnum; } } } ImageRec::ImageRec (ImageRec &img, int subimage_to_copy, int miplevel_to_copy, bool writable, bool copy_pixels) : m_name(img.name()), m_elaborated(true), m_metadata_modified(false), m_pixels_modified(false), m_was_output(false), m_imagecache(img.m_imagecache) { img.read (); int first_subimage = std::max (0, subimage_to_copy); int subimages = (subimage_to_copy < 0) ? img.subimages() : 1; m_subimages.resize (subimages); for (int s = 0; s < subimages; ++s) { int srcsub = s + first_subimage; int first_miplevel = std::max (0, miplevel_to_copy); int miplevels = (miplevel_to_copy < 0) ? img.miplevels(srcsub) : 1; m_subimages[s].m_miplevels.resize (miplevels); m_subimages[s].m_specs.resize (miplevels); for (int m = 0; m < miplevels; ++m) { int srcmip = m + first_miplevel; const ImageBuf &srcib (img(srcsub,srcmip)); const ImageSpec &srcspec (*img.spec(srcsub,srcmip)); ImageBuf *ib = NULL; if (writable || img.pixels_modified() || !copy_pixels) { // Make our own copy of the pixels ib = new ImageBuf (srcspec); if (copy_pixels) ib->copy_pixels (srcib); } else { // The other image is not modified, and we don't need to be // writable, either. ib = new ImageBuf (img.name(), srcib.imagecache()); bool ok = ib->read (srcsub, srcmip); ASSERT (ok); } m_subimages[s].m_miplevels[m].reset (ib); m_subimages[s].m_specs[m] = srcspec; } } } ImageRec::ImageRec (ImageRec &A, ImageRec &B, int subimage_to_copy, WinMerge pixwin, WinMerge fullwin, TypeDesc pixeltype) : m_name(A.name()), m_elaborated(true), m_metadata_modified(false), m_pixels_modified(false), m_was_output(false), m_imagecache(A.m_imagecache) { A.read (); B.read (); int subimages = (subimage_to_copy < 0) ? std::min(A.subimages(), B.subimages()) : 1; int first_subimage = clamp (subimage_to_copy, 0, subimages-1); m_subimages.resize (subimages); for (int s = 0; s < subimages; ++s) { int srcsub = s + first_subimage; m_subimages[s].m_miplevels.resize (1); m_subimages[s].m_specs.resize (1); const ImageBuf &Aib (A(srcsub)); const ImageBuf &Bib (B(srcsub)); const ImageSpec &Aspec (Aib.spec()); const ImageSpec &Bspec (Bib.spec()); ImageSpec spec = Aspec; ROI Aroi = get_roi (Aspec), Aroi_full = get_roi_full (Aspec); ROI Broi = get_roi (Bspec), Broi_full = get_roi_full (Bspec); switch (pixwin) { case WinMergeUnion : set_roi (spec, roi_union (Aroi, Broi)); break; case WinMergeIntersection : set_roi (spec, roi_intersection (Aroi, Broi)); break; case WinMergeA : set_roi (spec, Aroi); break; case WinMergeB : set_roi (spec, Broi); break; } switch (fullwin) { case WinMergeUnion : set_roi_full (spec, roi_union (Aroi_full, Broi_full)); break; case WinMergeIntersection : set_roi_full (spec, roi_intersection (Aroi_full, Broi_full)); break; case WinMergeA : set_roi_full (spec, Aroi_full); break; case WinMergeB : set_roi_full (spec, Broi_full); break; } if (pixeltype != TypeDesc::UNKNOWN) spec.set_format (pixeltype); spec.nchannels = std::min (Aspec.nchannels, Bspec.nchannels); spec.channelnames.resize (spec.nchannels); spec.channelformats.clear (); ImageBuf *ib = new ImageBuf (spec); m_subimages[s].m_miplevels[0].reset (ib); m_subimages[s].m_specs[0] = spec; } } ImageRec::ImageRec (ImageBufRef img, bool copy_pixels) : m_name(img->name()), m_elaborated(true), m_metadata_modified(false), m_pixels_modified(false), m_was_output(false), m_imagecache(img->imagecache()) { m_subimages.resize (1); m_subimages[0].m_miplevels.resize (1); m_subimages[0].m_specs.push_back (img->spec()); if (copy_pixels) { m_subimages[0].m_miplevels[0].reset (new ImageBuf (*img)); } else { m_subimages[0].m_miplevels[0] = img; } } ImageRec::ImageRec (const std::string &name, const ImageSpec &spec, ImageCache *imagecache) : m_name(name), m_elaborated(true), m_metadata_modified(false), m_pixels_modified(true), m_was_output(false), m_imagecache(imagecache) { int subimages = 1; m_subimages.resize (subimages); for (int s = 0; s < subimages; ++s) { int miplevels = 1; m_subimages[s].m_miplevels.resize (miplevels); m_subimages[s].m_specs.resize (miplevels); for (int m = 0; m < miplevels; ++m) { ImageBuf *ib = new ImageBuf (spec); m_subimages[s].m_miplevels[m].reset (ib); m_subimages[s].m_specs[m] = spec; } } } bool ImageRec::read (ReadPolicy readpolicy) { if (elaborated()) return true; static ustring u_subimages("subimages"), u_miplevels("miplevels"); static boost::regex regex_sha ("SHA-1=[[:xdigit:]]*[ ]*"); int subimages = 0; ustring uname (name()); if (! m_imagecache->get_image_info (uname, 0, 0, u_subimages, TypeDesc::TypeInt, &subimages)) { error ("file not found: \"%s\"", name()); return false; // Image not found } m_subimages.resize (subimages); bool allok = true; for (int s = 0; s < subimages; ++s) { int miplevels = 0; m_imagecache->get_image_info (uname, s, 0, u_miplevels, TypeDesc::TypeInt, &miplevels); m_subimages[s].m_miplevels.resize (miplevels); m_subimages[s].m_specs.resize (miplevels); for (int m = 0; m < miplevels; ++m) { // Force a read now for reasonable-sized first images in the // file. This can greatly speed up the multithread case for // tiled images by not having multiple threads working on the // same image lock against each other on the file handle. // We guess that "reasonable size" is 50 MB, that's enough to // hold a 2048x1536 RGBA float image. Larger things will // simply fall back on ImageCache. bool forceread = (s == 0 && m == 0 && m_imagecache->imagespec(uname,s,m)->image_bytes() < 50*1024*1024); ImageBuf *ib = new ImageBuf (name(), 0, 0, m_imagecache, &m_configspec); // If we were requested to bypass the cache, force a full read. if (readpolicy & ReadNoCache) forceread = true; // Convert to float unless asked to keep native. TypeDesc convert = (readpolicy & ReadNative) ? ib->nativespec().format : TypeDesc::FLOAT; if (! forceread && convert != TypeDesc::UINT8 && convert != TypeDesc::UINT16 && convert != TypeDesc::HALF && convert != TypeDesc::FLOAT) { // If we're still trying to use the cache but it doesn't // support the native type, force a full read. forceread = true; } bool ok = ib->read (s, m, forceread, convert); if (!ok) error ("%s", ib->geterror()); allok &= ok; // Remove any existing SHA-1 hash from the spec. ib->specmod().erase_attribute ("oiio:SHA-1"); std::string desc = ib->spec().get_string_attribute ("ImageDescription"); if (desc.size()) ib->specmod().attribute ("ImageDescription", boost::regex_replace (desc, regex_sha, "")); m_subimages[s].m_miplevels[m].reset (ib); m_subimages[s].m_specs[m] = ib->spec(); // For ImageRec purposes, we need to restore a few of the // native settings. const ImageSpec &nativespec (ib->nativespec()); // m_subimages[s].m_specs[m].format = nativespec.format; m_subimages[s].m_specs[m].tile_width = nativespec.tile_width; m_subimages[s].m_specs[m].tile_height = nativespec.tile_height; m_subimages[s].m_specs[m].tile_depth = nativespec.tile_depth; } } m_time = Filesystem::last_write_time (name()); m_elaborated = true; return allok; } namespace { static spin_mutex err_mutex; } bool ImageRec::has_error () const { spin_lock lock (err_mutex); return ! m_err.empty(); } std::string ImageRec::geterror (bool clear_error) const { spin_lock lock (err_mutex); std::string e = m_err; if (clear_error) m_err.clear(); return e; } void ImageRec::append_error (string_view message) const { spin_lock lock (err_mutex); ASSERT (m_err.size() < 1024*1024*16 && "Accumulated error messages > 16MB. Try checking return codes!"); if (m_err.size() && m_err[m_err.size()-1] != '\n') m_err += '\n'; m_err += message; } openimageio-1.7.17~dfsg0.orig/src/oiiotool/oiiotool.cpp0000644000175000017500000057240413151711064021326 0ustar mfvmfv/* Copyright 2011 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "OpenImageIO/argparse.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/sysutil.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/filter.h" #include "OpenImageIO/color.h" #include "OpenImageIO/timer.h" #include "oiiotool.h" OIIO_NAMESPACE_USING using namespace OiioTool; using namespace ImageBufAlgo; static Oiiotool ot; // Macro to fully set up the "action" function that straightforwardly // calls a custom OiiotoolOp class. #define OP_CUSTOMCLASS(name,opclass,ninputs) \ static int action_##name (int argc, const char *argv[]) { \ if (ot.postpone_callback (ninputs, action_##name, argc, argv)) \ return 0; \ opclass op (ot, #name, argc, argv); \ return op(); \ } #define UNARY_IMAGE_OP(name,impl) \ static int action_##name (int argc, const char *argv[]) { \ const int nargs = 1, ninputs = 1; \ if (ot.postpone_callback (ninputs, action_##name, argc, argv)) \ return 0; \ ASSERT (argc == nargs); \ OiiotoolSimpleUnaryOp op (impl, ot, #name, \ argc, argv, ninputs); \ return op(); \ } #define BINARY_IMAGE_OP(name,impl) \ static int action_##name (int argc, const char *argv[]) { \ const int nargs = 1, ninputs = 2; \ if (ot.postpone_callback (ninputs, action_##name, argc, argv)) \ return 0; \ ASSERT (argc == nargs); \ OiiotoolSimpleBinaryOp op (impl, ot, #name, \ argc, argv, ninputs); \ return op(); \ } #define BINARY_IMAGE_COLOR_OP(name,impl,defaultval) \ static int action_##name (int argc, const char *argv[]) { \ const int nargs = 2, ninputs = 1; \ if (ot.postpone_callback (ninputs, action_##name, argc, argv)) \ return 0; \ ASSERT (argc == nargs); \ OiiotoolImageColorOp op (impl, ot, #name, \ argc, argv, ninputs); \ return op(); \ } Oiiotool::Oiiotool () : imagecache(NULL), return_value (EXIT_SUCCESS), total_readtime (Timer::DontStartNow), total_writetime (Timer::DontStartNow), total_imagecache_readtime (0.0), enable_function_timing(true), peak_memory(0), num_outputs(0), printed_info(false), frame_number(0) { clear_options (); } void Oiiotool::clear_options () { verbose = false; debug = false; dryrun = false; runstats = false; noclobber = false; allsubimages = false; printinfo = false; printstats = false; dumpdata = false; dumpdata_showempty = true; hash = false; updatemode = false; autoorient = false; autocc = false; nativeread = false; cachesize = 4096; autotile = 4096; frame_padding = 0; full_command_line.clear (); printinfo_metamatch.clear (); printinfo_nometamatch.clear (); input_config = ImageSpec(); input_config_set = false; output_dataformat = TypeDesc::UNKNOWN; output_channelformats.clear (); output_bitspersample = 0; output_scanline = false; output_tilewidth = 0; output_tileheight = 0; output_compression = ""; output_quality = -1; output_planarconfig = "default"; output_adjust_time = false; output_autocrop = true; output_autotrim = false; output_dither = false; output_force_tiles = false; metadata_nosoftwareattrib = false; diff_warnthresh = 1.0e-6f; diff_warnpercent = 0; diff_hardwarn = std::numeric_limits::max(); diff_failthresh = 1.0e-6f; diff_failpercent = 0; diff_hardfail = std::numeric_limits::max(); m_pending_callback = NULL; m_pending_argc = 0; frame_number = 0; frame_padding = 0; } std::string format_resolution (int w, int h, int x, int y) { return Strutil::format ("%dx%d%+d%+d", w, h, x, y); } std::string format_resolution (int w, int h, int d, int x, int y, int z) { return Strutil::format ("%dx%dx%d%+d%+d%+d", w, h, d, x, y, z); } // FIXME -- lots of things we skimped on so far: // FIXME: reject volume images? // FIXME: do all ops respect -a (or lack thereof?) bool Oiiotool::read (ImageRecRef img, ReadPolicy readpolicy) { // If the image is already elaborated, take an early out, both to // save time, but also because we only want to do the format and // tile adjustments below as images are read in fresh from disk. if (img->elaborated()) return true; // Cause the ImageRec to get read. Try to compute how long it took. // Subtract out ImageCache time, to avoid double-accounting it later. float pre_ic_time, post_ic_time; imagecache->getattribute ("stat:fileio_time", pre_ic_time); total_readtime.start (); if (ot.nativeread) readpolicy = ReadPolicy (readpolicy | ReadNative); bool ok = img->read (readpolicy); total_readtime.stop (); imagecache->getattribute ("stat:fileio_time", post_ic_time); total_imagecache_readtime += post_ic_time - pre_ic_time; // If this is the first tiled image we have come across, use it to // set our tile size (unless the user explicitly set a tile size, or // explicitly instructed scanline output). const ImageSpec &nspec ((*img)().nativespec()); if (nspec.tile_width && ! output_tilewidth && ! ot.output_scanline) { output_tilewidth = nspec.tile_width; output_tileheight = nspec.tile_height; } // If we do not yet have an expected output format, set it based on // this image (presumably the first one read. if (output_dataformat == TypeDesc::UNKNOWN) { output_dataformat = nspec.format; if (! output_bitspersample) output_bitspersample = nspec.get_int_attribute ("oiio:BitsPerSample"); } if (output_channelformats.empty() && nspec.channelformats.size()) { for (int c = 0; c < nspec.nchannels; ++c) { std::string chname = nspec.channelnames[c]; output_channelformats[chname] = std::string(nspec.channelformat(c).c_str()); } } if (! ok) { error ("read "+img->name(), img->geterror()); } return ok; } bool Oiiotool::postpone_callback (int required_images, CallbackFunction func, int argc, const char *argv[]) { if (image_stack_depth() < required_images) { // Not enough have inputs been specified so far, so put this // function on the "pending" list. m_pending_callback = func; m_pending_argc = argc; for (int i = 0; i < argc; ++i) m_pending_argv[i] = ustring(argv[i]).c_str(); return true; } return false; } void Oiiotool::process_pending () { // Process any pending command -- this is a case where the // command line had prefix 'oiiotool --action file1 file2' // instead of infix 'oiiotool file1 --action file2'. if (m_pending_callback) { int argc = m_pending_argc; const char *argv[4]; for (int i = 0; i < argc; ++i) argv[i] = m_pending_argv[i]; CallbackFunction callback = m_pending_callback; m_pending_callback = NULL; m_pending_argc = 0; (*callback) (argc, argv); } } void Oiiotool::error (string_view command, string_view explanation) { std::cerr << "oiiotool ERROR: " << command; if (explanation.length()) std::cerr << " : " << explanation; std::cerr << "\n"; exit (-1); } void Oiiotool::warning (string_view command, string_view explanation) { std::cerr << "oiiotool WARNING: " << command; if (explanation.length()) std::cerr << " : " << explanation; std::cerr << "\n"; } int Oiiotool::extract_options (std::map &options, std::string command) { // std::cout << "extract_options '" << command << "'\n"; int noptions = 0; size_t pos; while ((pos = command.find_first_of(":")) != std::string::npos) { command = command.substr (pos+1, std::string::npos); size_t e = command.find_first_of("="); if (e != std::string::npos) { std::string name = command.substr(0,e); std::string value = command.substr(e+1,command.find_first_of(":")-(e+1)); options[name] = value; ++noptions; // std::cout << "'" << name << "' -> '" << value << "'\n"; } } return noptions; } static int set_threads (int argc, const char *argv[]) { ASSERT (argc == 2); int nthreads = atoi(argv[1]); OIIO::attribute ("threads", nthreads); OIIO::attribute ("exr_threads", nthreads); return 0; } static int set_cachesize (int argc, const char *argv[]) { ASSERT (argc == 2); ot.cachesize = atoi(argv[1]); ot.imagecache->attribute ("max_memory_MB", float(ot.cachesize)); return 0; } static int set_autotile (int argc, const char *argv[]) { ASSERT (argc == 2); ot.autotile = atoi(argv[1]); ot.imagecache->attribute ("autotile", ot.autotile); if (ot.autotile) ot.imagecache->attribute ("autoscanline", 1); return 0; } static int set_native (int argc, const char *argv[]) { ASSERT (argc == 1); ot.nativeread = true; ot.imagecache->attribute ("forcefloat", 0); return 0; } static int set_dumpdata (int argc, const char *argv[]) { ASSERT (argc == 1); string_view command = ot.express (argv[0]); ot.dumpdata = true; std::map options; options["empty"] = "1"; ot.extract_options (options, command); ot.dumpdata_showempty = Strutil::from_string (options["empty"]); return 0; } static int set_autopremult (int argc, const char *argv[]) { ASSERT (argc == 1); ot.imagecache->attribute ("unassociatedalpha", 0); return 0; } static int unset_autopremult (int argc, const char *argv[]) { ASSERT (argc == 1); ot.imagecache->attribute ("unassociatedalpha", 1); return 0; } static int action_label (int argc, const char *argv[]) { string_view labelname = ot.express(argv[1]); ot.image_labels[labelname] = ot.curimg; return 0; } static void string_to_dataformat (const std::string &s, TypeDesc &dataformat, int &bits) { if (s == "uint8") { dataformat = TypeDesc::UINT8; bits = 0; } else if (s == "int8") { dataformat = TypeDesc::INT8; bits = 0; } else if (s == "uint10") { dataformat = TypeDesc::UINT16; bits = 10; } else if (s == "uint12") { dataformat = TypeDesc::UINT16; bits = 12; } else if (s == "uint16") { dataformat = TypeDesc::UINT16; bits = 0; } else if (s == "int16") { dataformat = TypeDesc::INT16; bits = 0; } else if (s == "uint32") { dataformat = TypeDesc::UINT32; bits = 0; } else if (s == "int32") { dataformat = TypeDesc::INT32; bits = 0; } else if (s == "half") { dataformat = TypeDesc::HALF; bits = 0; } else if (s == "float") { dataformat = TypeDesc::FLOAT; bits = 0; } else if (s == "double") { dataformat = TypeDesc::DOUBLE; bits = 0; } } inline int get_value_override (string_view localoption, int defaultval=0) { return localoption.size() ? Strutil::from_string(localoption) : defaultval; } inline float get_value_override (string_view localoption, float defaultval) { return localoption.size() ? Strutil::from_string(localoption) : defaultval; } inline string_view get_value_override (string_view localoption, string_view defaultval) { return localoption.size() ? localoption : defaultval; } static void adjust_output_options (string_view filename, ImageSpec &spec, const Oiiotool &ot, bool format_supports_tiles, std::map &fileoptions) { if (ot.output_dataformat != TypeDesc::UNKNOWN) { TypeDesc type (fileoptions["datatype"]); if (type == TypeDesc::UNKNOWN) type = ot.output_dataformat; if (type != TypeDesc::UNKNOWN) spec.format = ot.output_dataformat; int bits = get_value_override (fileoptions["bits"], ot.output_bitspersample); if (bits) spec.attribute ("oiio:BitsPerSample", ot.output_bitspersample); else spec.erase_attribute ("oiio:BitsPerSample"); } if (ot.output_channelformats.size()) { spec.channelformats.clear (); spec.channelformats.resize (spec.nchannels, spec.format); for (int c = 0; c < spec.nchannels; ++c) { if (c >= (int)spec.channelnames.size()) break; std::map::const_iterator i = ot.output_channelformats.find (spec.channelnames[c]); if (i != ot.output_channelformats.end()) { int bits = 0; string_to_dataformat (i->second, spec.channelformats[c], bits); } } bool allsame = true; if (spec.channelnames.size()) for (int c = 1; c < spec.nchannels; ++c) allsame &= (spec.channelformats[c] == spec.channelformats[0]); if (allsame) { spec.format = spec.channelformats[0]; spec.channelformats.clear(); } } else { spec.channelformats.clear (); } // If we've had tiled input and scanline was not explicitly // requested, we'll try tiled output. if (ot.output_tilewidth && !ot.output_scanline && format_supports_tiles) { spec.tile_width = ot.output_tilewidth; spec.tile_height = ot.output_tileheight; spec.tile_depth = 1; } else { spec.tile_width = spec.tile_height = spec.tile_depth = 0; } if (! ot.output_compression.empty()) spec.attribute ("compression", ot.output_compression); if (ot.output_quality > 0) spec.attribute ("CompressionQuality", ot.output_quality); if (get_value_override (fileoptions["separate"])) spec.attribute ("planarconfig", "separate"); else if (get_value_override (fileoptions["contig"])) spec.attribute ("planarconfig", "contig"); else if (ot.output_planarconfig == "contig" || ot.output_planarconfig == "separate") spec.attribute ("planarconfig", ot.output_planarconfig); // Append command to image history. Sometimes we may not want to recite the // entire command line (eg. when we have loaded it up with metadata attributes // that will make it into the header anyway). if (! ot.metadata_nosoftwareattrib) { std::string history = spec.get_string_attribute ("Exif:ImageHistory"); if (! Strutil::iends_with (history, ot.full_command_line)) { // don't add twice if (history.length() && ! Strutil::iends_with (history, "\n")) history += std::string("\n"); history += ot.full_command_line; spec.attribute ("Exif:ImageHistory", history); } std::string software = Strutil::format ("OpenImageIO %s : %s", OIIO_VERSION_STRING, ot.full_command_line); spec.attribute ("Software", software); } int dither = get_value_override (fileoptions["dither"], ot.output_dither); if (dither) { int h = (int) Strutil::strhash(filename); if (!h) h = 1; spec.attribute ("oiio:dither", h); } // Make sure we kill any special hints that maketx adds and that will // no longer be valid after whatever oiiotool operations we've done. spec.erase_attribute ("oiio:SHA-1"); spec.erase_attribute ("oiio:ConstantColor"); spec.erase_attribute ("oiio:AverageColor"); } static bool DateTime_to_time_t (const char *datetime, time_t &timet) { int year, month, day, hour, min, sec; int r = sscanf (datetime, "%d:%d:%d %d:%d:%d", &year, &month, &day, &hour, &min, &sec); // printf ("%d %d:%d:%d %d:%d:%d\n", r, year, month, day, hour, min, sec); if (r != 6) return false; struct tm tmtime; time_t now; Sysutil::get_local_time (&now, &tmtime); // fill in defaults tmtime.tm_sec = sec; tmtime.tm_min = min; tmtime.tm_hour = hour; tmtime.tm_mday = day; tmtime.tm_mon = month-1; tmtime.tm_year = year-1900; timet = mktime (&tmtime); return true; } // For a comma-separated list of channel names (e.g., "B,G,R,A"), compute // the vector of integer indices for those channels as found in the spec // (e.g., {2,1,0,3}), using -1 for any channels whose names were not found // in the spec. Return true if all named channels were found, false if one // or more were not found. static bool parse_channels (const ImageSpec &spec, string_view chanlist, std::vector &channels) { bool ok = true; channels.clear (); for (int c = 0; chanlist.length(); ++c) { int chan = -1; Strutil::skip_whitespace (chanlist); string_view name = Strutil::parse_until (chanlist, ","); if (name.size()) { for (int i = 0; i < spec.nchannels; ++i) if (spec.channelnames[i] == name) { // name of a known channel? chan = i; break; } if (chan < 0) { // Didn't find a match? Try case-insensitive. for (int i = 0; i < spec.nchannels; ++i) if (Strutil::iequals (spec.channelnames[i], name)) { chan = i; break; } } if (chan < 0) ok = false; channels.push_back (chan); } if (! Strutil::parse_char (chanlist, ',')) break; } return ok; } static int set_dataformat (int argc, const char *argv[]) { ASSERT (argc == 2); string_view command = ot.express (argv[0]); std::vector chans; Strutil::split (ot.express(argv[1]), chans, ","); if (chans.size() == 0) { return 0; // Nothing to do } if (chans.size() == 1 && !strchr(chans[0].c_str(),'=')) { // Of the form: -d uint8 (for example) // Just one default format designated, apply to all channels ot.output_dataformat = TypeDesc::UNKNOWN; ot.output_bitspersample = 0; string_to_dataformat (chans[0], ot.output_dataformat, ot.output_bitspersample); if (ot.output_dataformat == TypeDesc::UNKNOWN) ot.error (command, Strutil::format ("Unknown data format \"%s\"", chans[0])); ot.output_channelformats.clear (); return 0; // we're done } // If we make it here, the format designator was of the form // name0=type0,name1=type1,... for (size_t i = 0; i < chans.size(); ++i) { const char *eq = strchr(chans[i].c_str(),'='); if (eq) { std::string channame (chans[i], 0, eq - chans[i].c_str()); ot.output_channelformats[channame] = std::string (eq+1); } else { ot.error (command, Strutil::format ("Malformed format designator \"%s\"", chans[i])); } } return 0; } static int set_string_attribute (int argc, const char *argv[]) { ASSERT (argc == 3); if (! ot.curimg.get()) { ot.warning (argv[0], "no current image available to modify"); return 0; } set_attribute (ot.curimg, argv[1], TypeDesc::TypeString, argv[2], ot.allsubimages); // N.B. set_attribute does expression expansion on its args return 0; } static int set_any_attribute (int argc, const char *argv[]) { ASSERT (argc == 3); if (! ot.curimg.get()) { ot.warning (argv[0], "no current image available to modify"); return 0; } std::map options; ot.extract_options (options, argv[0]); TypeDesc type (options["type"]); set_attribute (ot.curimg, argv[1], type, argv[2], ot.allsubimages); // N.B. set_attribute does expression expansion on its args return 0; } static bool do_erase_attribute (ImageSpec &spec, const std::string &attribname) { spec.erase_attribute (attribname); return true; } template static bool do_set_any_attribute (ImageSpec &spec, const std::pair &x) { spec.attribute (x.first, x.second); return true; } bool Oiiotool::get_position (string_view command, string_view geom, int &x, int &y) { string_view orig_geom (geom); bool ok = Strutil::parse_int (geom, x) && Strutil::parse_char (geom, ',') && Strutil::parse_int (geom, y); if (! ok) error (command, Strutil::format ("Unrecognized position \"%s\"", orig_geom)); return ok; } bool Oiiotool::adjust_geometry (string_view command, int &w, int &h, int &x, int &y, const char *geom, bool allow_scaling) { float scaleX = 1.0f; float scaleY = 1.0f; int ww = w, hh = h; int xx = x, yy = y; int xmax, ymax; if (sscanf (geom, "%d,%d,%d,%d", &xx, &yy, &xmax, &ymax) == 4) { x = xx; y = yy; w = std::max (0, xmax-xx+1); h = std::max (0, ymax-yy+1); } else if (sscanf (geom, "%dx%d%d%d", &ww, &hh, &xx, &yy) == 4 || sscanf (geom, "%dx%d+%d+%d", &ww, &hh, &xx, &yy) == 4) { if (ww == 0 && h != 0) ww = int (hh * float(w)/float(h) + 0.5f); if (hh == 0 && w != 0) hh = int (ww * float(h)/float(w) + 0.5f); w = ww; h = hh; x = xx; y = yy; } else if (sscanf (geom, "%dx%d", &ww, &hh) == 2) { if (ww == 0 && h != 0) ww = int (hh * float(w)/float(h) + 0.5f); if (hh == 0 && w != 0) hh = int (ww * float(h)/float(w) + 0.5f); w = ww; h = hh; } else if (allow_scaling && sscanf (geom, "%f%%x%f%%", &scaleX, &scaleY) == 2) { scaleX = std::max(0.0f, scaleX*0.01f); scaleY = std::max(0.0f, scaleY*0.01f); if (scaleX == 0 && scaleY != 0) scaleX = scaleY; if (scaleY == 0 && scaleX != 0) scaleY = scaleX; w = (int)(w * scaleX + 0.5f); h = (int)(h * scaleY + 0.5f); } else if (sscanf (geom, "%d%d", &xx, &yy) == 2) { x = xx; y = yy; } else if (allow_scaling && sscanf (geom, "%f%%", &scaleX) == 1) { scaleX *= 0.01f; w = (int)(w * scaleX + 0.5f); h = (int)(h * scaleX + 0.5f); } else if (allow_scaling && sscanf (geom, "%f", &scaleX) == 1) { w = (int)(w * scaleX + 0.5f); h = (int)(h * scaleX + 0.5f); } else { error (command, Strutil::format ("Unrecognized geometry \"%s\"", geom)); return false; } // printf ("geom %dx%d, %+d%+d\n", w, h, x, y); return true; } void Oiiotool::express_error (const string_view expr, const string_view s, string_view explanation) { int offset = expr.rfind(s) + 1; error("expression", Strutil::format ("%s at char %d of `%s'", explanation, offset, expr)); } bool Oiiotool::express_parse_atom(const string_view expr, string_view& s, std::string& result) { // std::cout << " Entering express_parse_atom, s='" << s << "'\n"; string_view orig = s; string_view stringval; float floatval; Strutil::skip_whitespace(s); // handle + or - prefixes bool negative = false; while (s.size()) { if (Strutil::parse_char (s, '-')) { negative = ! negative; } else if (Strutil::parse_char (s, '+')) { // no op } else { break; } } if (Strutil::parse_char (s, '(')) { // handle parentheses if (express_parse_summands (expr, s, result)) { if (! Strutil::parse_char (s, ')')) { express_error (expr, s, "missing `)'"); result = orig; return false; } } else { result = orig; return false; } } else if (Strutil::starts_with (s,"TOP") || Strutil::starts_with (s, "IMG[")) { // metadata substitution ImageRecRef img; if (Strutil::parse_prefix (s, "TOP")) { img = curimg; } else if (Strutil::parse_prefix (s, "IMG[")) { int index = -1; if (Strutil::parse_int (s, index) && Strutil::parse_char (s, ']') && index >= 0 && index <= (int)image_stack.size()) { if (index == 0) img = curimg; else img = image_stack[image_stack.size()-index]; } else { string_view name = Strutil::parse_until (s, "]"); std::map::const_iterator found; found = ot.image_labels.find(name); if (found != ot.image_labels.end()) img = found->second; else img = ImageRecRef (new ImageRec (name, ot.imagecache)); Strutil::parse_char (s, ']'); } } if (! img.get()) { express_error (expr, s, "not a valid image"); result = orig; return false; } if (! Strutil::parse_char (s, '.')) { express_error (expr, s, "expected `.'"); result = orig; return false; } string_view metadata = Strutil::parse_identifier (s, ":", true); if (metadata.size()) { read (img); ImageIOParameter tmpparam; const ImageIOParameter *p = img->spec(0,0)->find_attribute (metadata, tmpparam); if (p) { std::string val = ImageSpec::metadata_val (*p); if (p->type().basetype == TypeDesc::STRING) { // metadata_val returns strings double quoted, strip val.erase (0, 1); val.erase (val.size()-1, 1); } result = val; } else if (metadata == "filename") result = img->name(); else if (metadata == "file_extension") result = Filesystem::extension (img->name()); else if (metadata == "file_noextension") { std::string filename = img->name(); std::string ext = Filesystem::extension (img->name()); result = filename.substr (0, filename.size()-ext.size()); } else { express_error (expr, s, Strutil::format ("unknown attribute name `%s'", metadata)); result = orig; return false; } } } else if (Strutil::parse_float (s, floatval)) { result = Strutil::format ("%g", floatval); } // Test some special identifiers else if (Strutil::parse_identifier_if (s, "FRAME_NUMBER")) { result = Strutil::format ("%d", ot.frame_number); } else if (Strutil::parse_identifier_if (s, "FRAME_NUMBER_PAD")) { std::string fmt = ot.frame_padding == 0 ? std::string("%d") : Strutil::format ("\"%%0%dd\"", ot.frame_padding); result = Strutil::format (fmt.c_str(), ot.frame_number); } else { express_error (expr, s, "syntax error"); result = orig; return false; } if (negative) result = "-" + result; // std::cout << " Exiting express_parse_atom, result='" << result << "'\n"; return true; } bool Oiiotool::express_parse_factors(const string_view expr, string_view& s, std::string& result) { // std::cout << " Entering express_parse_factors, s='" << s << "'\n"; string_view orig = s; std::string atom; float lval, rval; // parse the first factor if (! express_parse_atom (expr, s, atom)) { result = orig; return false; } if (atom.size() >= 2 && atom[0] == '\"' && atom[atom.size()-1] == '\"') { // Double quoted is string, return it result = atom; } else if (Strutil::string_is (atom)) { // lval is a number lval = Strutil::from_string (atom); while (s.size()) { char op; if (Strutil::parse_char (s, '*')) op = '*'; else if (Strutil::parse_char (s, '/')) op = '/'; else { // no more factors break; } // parse the next factor if (! express_parse_atom (expr, s, atom)) { result = orig; return false; } if (! Strutil::string_is (atom)) { express_error (expr, s, Strutil::format ("expected number but got `%s'", atom)); result = orig; return false; } // rval is a number, so we can math rval = Strutil::from_string(atom); if (op == '*') lval *= rval; else // op == '/' lval /= rval; } result = Strutil::format ("%g", lval); } else { // atom is not a number, so we're done result = atom; } // std::cout << " Exiting express_parse_factors, result='" << result << "'\n"; return true; } bool Oiiotool::express_parse_summands(const string_view expr, string_view& s, std::string& result) { // std::cout << " Entering express_parse_summands, s='" << s << "'\n"; string_view orig = s; std::string atom; float lval, rval; // parse the first summand if (! express_parse_factors(expr, s, atom)) { result = orig; return false; } if (atom.size() >= 2 && atom[0] == '\"' && atom[atom.size()-1] == '\"') { // Double quoted is string, strip it result = atom.substr (1, atom.size()-2); } else if (Strutil::string_is (atom)) { // lval is a number lval = Strutil::from_string (atom); while (s.size()) { char op; if (Strutil::parse_char (s, '+')) op = '+'; else if (Strutil::parse_char (s, '-')) op = '-'; else { // no more summands break; } // parse the next summand if (! express_parse_factors(expr, s, atom)) { result = orig; return false; } if (! Strutil::string_is (atom)) { express_error (expr, s, Strutil::format ("`%s' is not a number", atom)); result = orig; return false; } // rval is also a number, we can math rval = Strutil::from_string(atom); if (op == '+') lval += rval; else // op == '-' lval -= rval; } result = Strutil::format ("%g", lval); } else { // atom is not a number, so we're done result = atom; } // std::cout << " Exiting express_parse_summands, result='" << result << "'\n"; return true; } // Expression evaluation and substitution for a single expression std::string Oiiotool::express_impl (string_view s) { std::string result; string_view orig = s; if (! express_parse_summands(orig, s, result)) { result = orig; } return result; } // Perform expression evaluation and substitution on a string string_view Oiiotool::express (string_view str) { string_view s = str; // eg. s="ab{cde}fg" size_t openbrace = s.find('{'); if (openbrace == s.npos) return str; // No open brace found -- no expresion substitution string_view prefix = s.substr (0, openbrace); s.remove_prefix (openbrace); // eg. s="{cde}fg", prefix="ab" string_view expr = Strutil::parse_nested (s); if (expr.empty()) return str; // No corresponding close brace found -- give up // eg. prefix="ab", expr="{cde}", s="fg", prefix="ab" ASSERT (expr.front() == '{' && expr.back() == '}'); expr.remove_prefix(1); expr.remove_suffix(1); // eg. expr="cde" ustring result = ustring::format("%s%s%s", prefix, express_impl(expr), express(s)); if (ot.debug) std::cout << "Expanding expression \"" << str << "\" -> \"" << result << "\"\n"; return result; } static int set_input_attribute (int argc, const char *argv[]) { ASSERT (argc == 3); std::map options; ot.extract_options (options, argv[0]); TypeDesc type (options["type"]); string_view attribname = ot.express(argv[1]); string_view value = ot.express(argv[2]); if (! value.size()) { // If the value is the empty string, clear the attribute ot.input_config.erase_attribute (attribname); return 0; } ot.input_config_set = true; // First, handle the cases where we're told what to expect if (type.basetype == TypeDesc::FLOAT) { size_t n = type.numelements() * type.aggregate; std::vector vals (n, 0.0f); for (size_t i = 0; i < n && value.size(); ++i) { Strutil::parse_float (value, vals[i]); Strutil::parse_char (value, ','); } ot.input_config.attribute (attribname, type, &vals[0]); return 0; } if (type.basetype == TypeDesc::INT) { size_t n = type.numelements() * type.aggregate; std::vector vals (n, 0); for (size_t i = 0; i < n && value.size(); ++i) { Strutil::parse_int (value, vals[i]); Strutil::parse_char (value, ','); } ot.input_config.attribute (attribname, type, &vals[0]); return 0; } if (type.basetype == TypeDesc::STRING) { size_t n = type.numelements() * type.aggregate; std::vector vals (n, ustring()); if (n == 1) vals[0] = ustring(value); else { for (size_t i = 0; i < n && value.size(); ++i) { string_view s; Strutil::parse_string (value, s); vals[i] = ustring(s); Strutil::parse_char (value, ','); } } ot.input_config.attribute (attribname, type, &vals[0]); return 0; } // Does it seem to be an int, or did the caller explicitly request // that it be set as an int? char *p = NULL; int i = strtol (value.c_str(), &p, 10); while (*p && isspace(*p)) ++p; if ((! *p && type == TypeDesc::UNKNOWN) || type == TypeDesc::INT) { // int conversion succeeded and accounted for the whole string -- // so set an int attribute. ot.input_config.attribute (attribname, i); return 0; } // Does it seem to be a float, or did the caller explicitly request // that it be set as a float? p = NULL; float f = (float)strtod (value.c_str(), &p); while (*p && isspace(*p)) ++p; if ((! *p && type == TypeDesc::UNKNOWN) || type == TypeDesc::FLOAT) { // float conversion succeeded and accounted for the whole string -- // so set a float attribute. ot.input_config.attribute (attribname, f); return 0; } // Otherwise, set it as a string attribute ot.input_config.attribute (attribname, value); return 0; } bool OiioTool::set_attribute (ImageRecRef img, string_view attribname, TypeDesc type, string_view value, bool allsubimages) { // Expression substitution attribname = ot.express(attribname); value = ot.express(value); ot.read (img); img->metadata_modified (true); if (! value.size()) { // If the value is the empty string, clear the attribute return apply_spec_mod (*img, do_erase_attribute, attribname, allsubimages); } // First, handle the cases where we're told what to expect if (type.basetype == TypeDesc::FLOAT) { size_t n = type.numelements() * type.aggregate; std::vector vals (n, 0.0f); for (size_t i = 0; i < n && value.size(); ++i) { Strutil::parse_float (value, vals[i]); Strutil::parse_char (value, ','); } for (int s = 0, send = img->subimages(); s < send; ++s) { for (int m = 0, mend = img->miplevels(s); m < mend; ++m) { ((*img)(s,m).specmod()).attribute (attribname, type, &vals[0]); img->update_spec_from_imagebuf (s, m); if (! allsubimages) break; } if (! allsubimages) break; } return true; } if (type == TypeDesc::TypeTimeCode && value.find(':') != value.npos) { // Special case: They are specifying a TimeCode as a "HH:MM:SS:FF" // string, we need to re-encode as a uint32[2]. int hour = 0, min = 0, sec = 0, frame = 0; sscanf (value.c_str(), "%d:%d:%d:%d", &hour, &min, &sec, &frame); Imf::TimeCode tc (hour, min, sec, frame); for (int s = 0, send = img->subimages(); s < send; ++s) { for (int m = 0, mend = img->miplevels(s); m < mend; ++m) { ((*img)(s,m).specmod()).attribute (attribname, type, &tc); img->update_spec_from_imagebuf (s, m); if (! allsubimages) break; } if (! allsubimages) break; } return true; } if (type.basetype == TypeDesc::INT) { size_t n = type.numelements() * type.aggregate; std::vector vals (n, 0); for (size_t i = 0; i < n && value.size(); ++i) { Strutil::parse_int (value, vals[i]); Strutil::parse_char (value, ','); } for (int s = 0, send = img->subimages(); s < send; ++s) { for (int m = 0, mend = img->miplevels(s); m < mend; ++m) { ((*img)(s,m).specmod()).attribute (attribname, type, &vals[0]); img->update_spec_from_imagebuf (s, m); if (! allsubimages) break; } if (! allsubimages) break; } return true; } if (type.basetype == TypeDesc::STRING) { size_t n = type.numelements() * type.aggregate; std::vector vals (n, ustring()); if (n == 1) vals[0] = ustring(value); else { for (size_t i = 0; i < n && value.size(); ++i) { string_view s; Strutil::parse_string (value, s); vals[i] = ustring(s); Strutil::parse_char (value, ','); } } for (int s = 0, send = img->subimages(); s < send; ++s) { for (int m = 0, mend = img->miplevels(s); m < mend; ++m) { ((*img)(s,m).specmod()).attribute (attribname, type, &vals[0]); img->update_spec_from_imagebuf (s, m); if (! allsubimages) break; } if (! allsubimages) break; } return true; } // Does it seem to be an int, or did the caller explicitly request // that it be set as an int? char *p = NULL; int i = strtol (value.c_str(), &p, 10); while (*p && isspace(*p)) ++p; if ((! *p && type == TypeDesc::UNKNOWN) || type == TypeDesc::INT) { // int conversion succeeded and accounted for the whole string -- // so set an int attribute. return apply_spec_mod (*img, do_set_any_attribute, std::pair(attribname,i), allsubimages); } // Does it seem to be a float, or did the caller explicitly request // that it be set as a float? p = NULL; float f = (float)strtod (value.c_str(), &p); while (*p && isspace(*p)) ++p; if ((! *p && type == TypeDesc::UNKNOWN) || type == TypeDesc::FLOAT) { // float conversion succeeded and accounted for the whole string -- // so set a float attribute. return apply_spec_mod (*img, do_set_any_attribute, std::pair(attribname,f), allsubimages); } // Otherwise, set it as a string attribute return apply_spec_mod (*img, do_set_any_attribute, std::pair(attribname,value), allsubimages); } static int set_caption (int argc, const char *argv[]) { ASSERT (argc == 2); const char *newargs[3] = { argv[0], "ImageDescription", argv[1] }; return set_string_attribute (3, newargs); // N.B. set_string_attribute does expression expansion on its args } static bool do_set_keyword (ImageSpec &spec, const std::string &keyword) { std::string oldkw = spec.get_string_attribute ("Keywords"); std::vector oldkwlist; if (! oldkw.empty()) Strutil::split (oldkw, oldkwlist, ";"); bool dup = false; BOOST_FOREACH (std::string &ok, oldkwlist) { ok = Strutil::strip (ok); dup |= (ok == keyword); } if (! dup) { oldkwlist.push_back (keyword); spec.attribute ("Keywords", Strutil::join (oldkwlist, "; ")); } return true; } static int set_keyword (int argc, const char *argv[]) { ASSERT (argc == 2); if (! ot.curimg.get()) { ot.warning (argv[0], "no current image available to modify"); return 0; } std::string keyword (ot.express(argv[1])); if (keyword.size()) apply_spec_mod (*ot.curimg, do_set_keyword, keyword, ot.allsubimages); return 0; } static int clear_keywords (int argc, const char *argv[]) { ASSERT (argc == 1); const char *newargs[3]; newargs[0] = argv[0]; newargs[1] = "Keywords"; newargs[2] = ""; return set_string_attribute (3, newargs); } static int set_orientation (int argc, const char *argv[]) { ASSERT (argc == 2); if (! ot.curimg.get()) { ot.warning (argv[0], "no current image available to modify"); return 0; } return set_attribute (ot.curimg, "Orientation", TypeDesc::INT, argv[1], ot.allsubimages); // N.B. set_attribute does expression expansion on its args } static bool do_rotate_orientation (ImageSpec &spec, string_view cmd) { bool rotcw = (cmd == "--orientcw" || cmd == "-orientcw" || cmd == "--rotcw" || cmd == "-rotcw"); bool rotccw = (cmd == "--orientccw" || cmd == "-orientccw" || cmd == "--rotccw" || cmd == "-rotccw"); bool rot180 = (cmd == "--orient180" || cmd == "-orient180" || cmd == "--rot180" || cmd == "-rot180"); int orientation = spec.get_int_attribute ("Orientation", 1); if (orientation >= 1 && orientation <= 8) { static int cw[] = { 0, 6, 7, 8, 5, 2, 3, 4, 1 }; if (rotcw || rotccw || rot180) orientation = cw[orientation]; if (rotccw || rot180) orientation = cw[orientation]; if (rotccw) orientation = cw[orientation]; spec.attribute ("Orientation", orientation); } return true; } static int rotate_orientation (int argc, const char *argv[]) { ASSERT (argc == 1); string_view command = ot.express (argv[0]); if (! ot.curimg.get()) { ot.warning (command, "no current image available to modify"); return 0; } apply_spec_mod (*ot.curimg, do_rotate_orientation, command, ot.allsubimages); return 0; } static int set_origin (int argc, const char *argv[]) { if (ot.postpone_callback (1, set_origin, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); string_view origin = ot.express (argv[1]); ot.read (); ImageRecRef A = ot.curimg; for (int s = 0; s < A->subimages(); ++s) { ImageSpec &spec (*A->spec(s)); int x = spec.x, y = spec.y, z = spec.z; int w = spec.width, h = spec.height, d = spec.depth; ot.adjust_geometry (command, w, h, x, y, origin.c_str()); if (spec.width != w || spec.height != h || spec.depth != d) ot.warning (command, "can't be used to change the size, only the origin"); if (spec.x != x || spec.y != y) { ImageBuf &ib = (*A)(s); if (ib.storage() == ImageBuf::IMAGECACHE) { // If the image is cached, we will totally screw up the IB/IC // operations if we try to change the origin in place, so in // that case force a full read to convert to a local buffer, // which is safe to diddle the origin. ib.read (0, 0, true /*force*/, spec.format); } spec.x = x; spec.y = y; spec.z = z; // That updated the private spec of the ImageRec. In this case // we really need to update the underlying IB as well. ImageSpec &ibspec = ib.specmod(); ibspec.x = x; ibspec.y = y; ibspec.z = z; A->metadata_modified (true); } } ot.function_times[command] += timer(); return 0; } static int set_fullsize (int argc, const char *argv[]) { if (ot.postpone_callback (1, set_fullsize, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); string_view size = ot.express (argv[1]); ot.read (); ImageRecRef A = ot.curimg; ImageSpec &spec (*A->spec(0,0)); int x = spec.full_x, y = spec.full_y; int w = spec.full_width, h = spec.full_height; ot.adjust_geometry (argv[0], w, h, x, y, size.c_str()); if (spec.full_x != x || spec.full_y != y || spec.full_width != w || spec.full_height != h) { spec.full_x = x; spec.full_y = y; spec.full_width = w; spec.full_height = h; // That updated the private spec of the ImageRec. In this case // we really need to update the underlying IB as well. ImageSpec &ibspec = (*A)(0,0).specmod(); ibspec.full_x = x; ibspec.full_y = y; ibspec.full_width = w; ibspec.full_height = h; A->metadata_modified (true); } ot.function_times[command] += timer(); return 0; } static int set_full_to_pixels (int argc, const char *argv[]) { if (ot.postpone_callback (1, set_full_to_pixels, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); ot.read (); ImageRecRef A = ot.curimg; for (int s = 0, send = A->subimages(); s < send; ++s) { for (int m = 0, mend = A->miplevels(s); m < mend; ++m) { ImageSpec &spec = *A->spec(s,m); spec.full_x = spec.x; spec.full_y = spec.y; spec.full_z = spec.z; spec.full_width = spec.width; spec.full_height = spec.height; spec.full_depth = spec.depth; // That updated the private spec of the ImageRec. In this case // we really need to update the underlying IB as well. ImageSpec &ibspec = (*A)(s,m).specmod(); ibspec.full_x = spec.x; ibspec.full_y = spec.y; ibspec.full_z = spec.z; ibspec.full_width = spec.width; ibspec.full_height = spec.height; ibspec.full_depth = spec.depth; } } A->metadata_modified (true); ot.function_times[command] += timer(); return 0; } static int set_colorconfig (int argc, const char *argv[]) { ASSERT (argc == 2); ot.colorconfig.reset (argv[1]); return 0; } static int set_colorspace (int argc, const char *argv[]) { ASSERT (argc == 2); const char *args[3] = { argv[0], "oiio:ColorSpace", argv[1] }; return set_string_attribute (3, args); // N.B. set_string_attribute does expression expansion on its args } class OpColorConvert : public OiiotoolOp { public: OpColorConvert (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) { fromspace = args[1]; tospace = args[2]; } virtual bool setup () { if (fromspace == tospace) { // The whole thing is a no-op. Get rid of the empty result we // pushed on the stack, replace it with the original image, and // signal that we're done. ot.pop (); ot.push (ir[1]); return false; } return true; } virtual int impl (ImageBuf **img) { string_view contextkey = options["key"]; string_view contextvalue = options["value"]; return ImageBufAlgo::colorconvert (*img[0], *img[1], fromspace, tospace, false, contextkey, contextvalue, &ot.colorconfig); } string_view fromspace, tospace; }; OP_CUSTOMCLASS (colorconvert, OpColorConvert, 1); static int action_tocolorspace (int argc, const char *argv[]) { // Don't time -- let it get accounted by colorconvert ASSERT (argc == 2); if (! ot.curimg.get()) { ot.warning (argv[0], "no current image available to modify"); return 0; } const char *args[3] = { argv[0], "current", argv[1] }; return action_colorconvert (3, args); } class OpOcioLook : public OiiotoolOp { public: OpOcioLook (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) { } virtual void option_defaults () { options["from"] = "current"; options["to"] = "current"; } virtual int impl (ImageBuf **img) { string_view lookname = args[1]; string_view fromspace = options["from"]; string_view tospace = options["to"]; string_view contextkey = options["key"]; string_view contextvalue = options["value"]; bool inverse = Strutil::from_string (options["inverse"]); if (fromspace == "current" || fromspace == "") fromspace = img[1]->spec().get_string_attribute ("oiio:Colorspace", "Linear"); if (tospace == "current" || tospace == "") tospace = img[1]->spec().get_string_attribute ("oiio:Colorspace", "Linear"); return ImageBufAlgo::ociolook (*img[0], *img[1], lookname, fromspace, tospace, false, inverse, contextkey, contextvalue, &ot.colorconfig); } }; OP_CUSTOMCLASS (ociolook, OpOcioLook, 1); class OpOcioDisplay : public OiiotoolOp { public: OpOcioDisplay (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) { } virtual void option_defaults () { options["from"] = "current"; } virtual int impl (ImageBuf **img) { string_view displayname = args[1]; string_view viewname = args[2]; string_view fromspace = options["from"]; string_view contextkey = options["key"]; string_view contextvalue = options["value"]; bool override_looks = options.find("looks") != options.end(); if (fromspace == "current" || fromspace == "") fromspace = img[1]->spec().get_string_attribute ("oiio:Colorspace", "Linear"); return ImageBufAlgo::ociodisplay (*img[0], *img[1], displayname, viewname, fromspace, override_looks ? options["looks"] : std::string(""), false, contextkey, contextvalue, &ot.colorconfig); } }; OP_CUSTOMCLASS (ociodisplay, OpOcioDisplay, 1); class OpOcioFileTransform : public OiiotoolOp { public: OpOcioFileTransform (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) { } virtual void option_defaults () { } virtual int impl (ImageBuf **img) { string_view name = args[1]; bool inverse = Strutil::from_string (options["inverse"]); return ImageBufAlgo::ociofiletransform (*img[0], *img[1], name, false, inverse, &ot.colorconfig); } }; OP_CUSTOMCLASS (ociofiletransform, OpOcioFileTransform, 1); static int output_tiles (int /*argc*/, const char *argv[]) { // the ArgParse will have set the tile size, but we need this routine // to clear the scanline flag ot.output_scanline = false; return 0; } static int action_unmip (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_unmip, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); ot.read (); bool mipmapped = false; for (int s = 0, send = ot.curimg->subimages(); s < send; ++s) mipmapped |= (ot.curimg->miplevels(s) > 1); if (! mipmapped) { return 0; // --unmip on an unmipped image is a no-op } ImageRecRef newimg (new ImageRec (*ot.curimg, -1, 0, true, true)); ot.curimg = newimg; ot.function_times[command] += timer(); return 0; } static int set_channelnames (int argc, const char *argv[]) { if (ot.postpone_callback (1, set_channelnames, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); string_view channelarg = ot.express (argv[1]); ImageRecRef A = ot.curimg; ot.read (A); std::vector newchannelnames; Strutil::split (channelarg, newchannelnames, ","); for (int s = 0; s < A->subimages(); ++s) { int miplevels = A->miplevels(s); for (int m = 0; m < miplevels; ++m) { ImageSpec *spec = &(*A)(s,m).specmod(); spec->channelnames.resize (spec->nchannels); for (int c = 0; c < spec->nchannels; ++c) { if (c < (int)newchannelnames.size() && newchannelnames[c].size()) { std::string name = newchannelnames[c]; ot.output_channelformats[name] = ot.output_channelformats[spec->channelnames[c]]; spec->channelnames[c] = name; if (Strutil::iequals(name,"A") || Strutil::iends_with(name,".A") || Strutil::iequals(name,"Alpha") || Strutil::iends_with(name,".Alpha")) spec->alpha_channel = c; if (Strutil::iequals(name,"Z") || Strutil::iends_with(name,".Z") || Strutil::iequals(name,"Depth") || Strutil::iends_with(name,".Depth")) spec->z_channel = c; } } A->update_spec_from_imagebuf(s,m); } } ot.function_times[command] += timer(); return 0; } // For a given spec (which contains the channel names for an image), and // a comma separated list of channels (e.g., "B,G,R,A"), compute the // vector of integer indices for those channels (e.g., {2,1,0,3}). // A channel may be a literal assignment (e.g., "=0.5"), or a literal // assignment with channel naming (e.g., "Z=0.5"), the name of a channel // ("A"), or the name of a channel with a new name reassigned ("R=G"). // Return true for success, false for failure, including if any of the // channels were not present in the image. Upon return, channels // will be the indices of the source image channels to copy (-1 for // channels that are not filled with source data), values will hold // the value to fill un-sourced channels (defaulting to zero), and // newchannelnames will be the name of renamed or non-default-named // channels (defaulting to "" if no special name is needed). bool decode_channel_set (const ImageSpec &spec, string_view chanlist, std::vector &newchannelnames, std::vector &channels, std::vector &values) { // std::cout << "Decode_channel_set '" << chanlist << "'\n"; channels.clear (); for (int c = 0; chanlist.length(); ++c) { // It looks like: // (put old channel here, by numeric index) // oldname (put old named channel here) // newname=oldname (put old channel here, with new name) // newname= (put constant value here, with a name) // = (put constant value here, default name) std::string newname; int chan = -1; float val = 0.0f; Strutil::skip_whitespace (chanlist); if (chanlist.empty()) break; if (Strutil::parse_int (chanlist, chan) && chan >= 0 && chan < spec.nchannels) { // case: newname = spec.channelnames[chan]; } else if (Strutil::parse_char (chanlist, '=')) { // case: = Strutil::parse_float (chanlist, val); } else { string_view n = Strutil::parse_until (chanlist, "=,"); string_view oldname; if (Strutil::parse_char (chanlist, '=')) { if (Strutil::parse_float (chanlist, val)) { // case: newname=float newname = n; } else { // case: newname=oldname newname = n; oldname = Strutil::parse_until (chanlist, ","); } } else { // case: oldname oldname = n; } if (oldname.size()) { for (int i = 0; i < spec.nchannels; ++i) if (spec.channelnames[i] == oldname) { // name of a known channel? chan = i; break; } if (chan < 0) { // Didn't find a match? Try case-insensitive. for (int i = 0; i < spec.nchannels; ++i) if (Strutil::iequals (spec.channelnames[i], oldname)) { chan = i; break; } } if (newname.empty() && chan >= 0) newname = spec.channelnames[chan]; } } if (! newname.size()) { const char *RGBAZ[] = { "R", "G", "B", "A", "Z" }; if (c <= 4) newname = std::string(RGBAZ[c]); else newname = Strutil::format ("channel%d", c); } // std::cout << " Chan " << c << ": " << newname << ' ' << chan << ' ' << val << "\n"; newchannelnames.push_back (newname); channels.push_back (chan); values.push_back (val); if (! Strutil::parse_char (chanlist, ',')) break; } return true; } int action_channels (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_channels, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); string_view chanlist = ot.express (argv[1]); ImageRecRef A (ot.pop()); ot.read (A); if (chanlist == "RGB") // Fix common synonyms/mistakes chanlist = "R,G,B"; else if (chanlist == "RGBA") chanlist = "R,G,B,A"; // Decode the channel set, make the full list of ImageSpec's we'll // need to describe the new ImageRec with the altered channels. std::vector allmiplevels; std::vector allspecs; for (int s = 0, subimages = ot.allsubimages ? A->subimages() : 1; s < subimages; ++s) { std::vector newchannelnames; std::vector channels; std::vector values; bool ok = decode_channel_set (*A->spec(s,0), chanlist, newchannelnames, channels, values); if (! ok) { ot.error (command, Strutil::format("Invalid or unknown channel selection \"%s\"", chanlist)); ot.push (A); return 0; } int miplevels = ot.allsubimages ? A->miplevels(s) : 1; allmiplevels.push_back (miplevels); for (int m = 0; m < miplevels; ++m) { ImageSpec spec = *A->spec(s,m); spec.nchannels = (int)newchannelnames.size(); spec.channelformats.clear(); spec.default_channel_names (); allspecs.push_back (spec); } } // Create the replacement ImageRec ImageRecRef R (new ImageRec(A->name(), (int)allmiplevels.size(), &allmiplevels[0], &allspecs[0])); ot.push (R); // Subimage by subimage, MIP level by MIP level, copy/shuffle the // channels individually from the source image into the result. for (int s = 0, subimages = R->subimages(); s < subimages; ++s) { std::vector newchannelnames; std::vector channels; std::vector values; decode_channel_set (*A->spec(s,0), chanlist, newchannelnames, channels, values); for (int m = 0, miplevels = R->miplevels(s); m < miplevels; ++m) { // Shuffle the indexed/named channels bool ok = ImageBufAlgo::channels ((*R)(s,m), (*A)(s,m), (int)channels.size(), &channels[0], &values[0], &newchannelnames[0], false); if (! ok) ot.error (command, (*R)(s,m).geterror()); // Tricky subtlety: IBA::channels changed the underlying IB, // we may need to update the IRR's copy of the spec. R->update_spec_from_imagebuf(s,m); } } ot.function_times[command] += timer(); return 0; } static int action_chappend (int argc, const char *argv[]) { if (ot.postpone_callback (2, action_chappend, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); ImageRecRef B (ot.pop()); ImageRecRef A (ot.pop()); ot.read (A); ot.read (B); std::vector allmiplevels; for (int s = 0, subimages = ot.allsubimages ? A->subimages() : 1; s < subimages; ++s) { int miplevels = ot.allsubimages ? A->miplevels(s) : 1; allmiplevels.push_back (miplevels); } // Create the replacement ImageRec ImageRecRef R (new ImageRec(A->name(), (int)allmiplevels.size(), &allmiplevels[0])); ot.push (R); // Subimage by subimage, MIP level by MIP level, channel_append the // two images. for (int s = 0, subimages = R->subimages(); s < subimages; ++s) { for (int m = 0, miplevels = R->miplevels(s); m < miplevels; ++m) { // Shuffle the indexed/named channels bool ok = ImageBufAlgo::channel_append ((*R)(s,m), (*A)(s,m), (*B)(s,m)); if (! ok) ot.error (command, (*R)(s,m).geterror()); // Tricky subtlety: IBA::channels changed the underlying IB, // we may need to update the IRR's copy of the spec. R->update_spec_from_imagebuf(s,m); } } ot.function_times[command] += timer(); return 0; } static int action_selectmip (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_selectmip, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); int miplevel = Strutil::from_string (ot.express(argv[1])); ot.read (); bool mipmapped = false; for (int s = 0, send = ot.curimg->subimages(); s < send; ++s) mipmapped |= (ot.curimg->miplevels(s) > 1); if (! mipmapped) { return 0; // --selectmip on an unmipped image is a no-op } ImageRecRef newimg (new ImageRec (*ot.curimg, -1, miplevel, true, true)); ot.curimg = newimg; ot.function_times[command] += timer(); return 0; } static int action_select_subimage (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_select_subimage, argc, argv)) return 0; Timer timer (ot.enable_function_timing); ot.read (); string_view command = ot.express (argv[0]); int subimage = 0; std::string whichsubimage = ot.express(argv[1]); string_view w (whichsubimage); if (Strutil::parse_int (w, subimage) && w.empty()) { // Subimage specification was an integer: treat as an index if (subimage < 0 || subimage >= ot.curimg->subimages()) { ot.error (command, Strutil::format ("Invalid -subimage (%d): %s has %d subimage%s", subimage, ot.curimg->name(), ot.curimg->subimages(), ot.curimg->subimages() == 1 ? "" : "s")); return 0; } } else { // The subimage specification wasn't an integer. Assume it's a name. subimage = -1; for (int i = 0, n = ot.curimg->subimages(); i < n; ++i) { string_view siname = ot.curimg->spec(i)->get_string_attribute("oiio:subimagename"); if (siname == whichsubimage) { subimage = i; break; } } if (subimage < 0) { ot.error (command, Strutil::format ("Invalid -subimage (%s): named subimage not found", whichsubimage)); return 0; } } if (ot.curimg->subimages() == 1 && subimage == 0) return 0; // asking for the only subimage is a no-op ImageRecRef A = ot.pop(); ot.push (new ImageRec (*A, subimage)); ot.function_times[command] += timer(); return 0; } static int action_subimage_split (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_subimage_split, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); ImageRecRef A = ot.pop(); ot.read (A); // Push the individual subimages onto the stack for (int subimage = 0; subimage < A->subimages(); ++subimage) ot.push (new ImageRec (*A, subimage)); ot.function_times[command] += timer(); return 0; } static void action_subimage_append_n (int n, string_view command) { std::vector images (n); for (int i = n-1; i >= 0; --i) { images[i] = ot.pop(); ot.read (images[i]); // necessary? } // Find the MIP levels in all the subimages of both A and B std::vector allmiplevels; for (int i = 0; i < n; ++i) { ImageRecRef A = images[i]; for (int s = 0; s < A->subimages(); ++s) { int miplevels = ot.allsubimages ? A->miplevels(s) : 1; allmiplevels.push_back (miplevels); } } // Create the replacement ImageRec ImageRecRef R (new ImageRec(images[0]->name(), (int)allmiplevels.size(), &allmiplevels[0])); ot.push (R); // Subimage by subimage, MIP level by MIP level, copy int sub = 0; for (int i = 0; i < n; ++i) { ImageRecRef A = images[i]; for (int s = 0; s < A->subimages(); ++s, ++sub) { for (int m = 0; m < A->miplevels(s); ++m) { bool ok = (*R)(sub,m).copy ((*A)(s,m)); if (! ok) ot.error (command, (*R)(sub,m).geterror()); // Tricky subtlety: IBA::channels changed the underlying IB, // we may need to update the IRR's copy of the spec. R->update_spec_from_imagebuf(sub,m); } } } } static int action_subimage_append (int argc, const char *argv[]) { if (ot.postpone_callback (2, action_subimage_append, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); action_subimage_append_n (2, command); ot.function_times[command] += timer(); return 0; } static int action_subimage_append_all (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_subimage_append_all, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); action_subimage_append_n (int(ot.image_stack.size()+1), command); ot.function_times[command] += timer(); return 0; } static int action_colorcount (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_colorcount, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); string_view colorarg = ot.express (argv[1]); ot.read (); ImageBuf &Aib ((*ot.curimg)(0,0)); int nchannels = Aib.nchannels(); // We assume ';' to split, but for the sake of some command shells, // that use ';' as a command separator, also accept ":". std::vector colorvalues; std::vector colorstrings; if (colorarg.find(':') != colorarg.npos) Strutil::split (colorarg, colorstrings, ":"); else Strutil::split (colorarg, colorstrings, ";"); int ncolors = (int) colorstrings.size(); for (int col = 0; col < ncolors; ++col) { std::vector color (nchannels, 0.0f); Strutil::extract_from_list_string (color, colorstrings[col], ","); for (int c = 0; c < nchannels; ++c) colorvalues.push_back (c < (int)color.size() ? color[c] : 0.0f); } std::vector eps (nchannels, 0.001f); std::map options; ot.extract_options (options, command); Strutil::extract_from_list_string (eps, options["eps"]); imagesize_t *count = ALLOCA (imagesize_t, ncolors); bool ok = ImageBufAlgo::color_count ((*ot.curimg)(0,0), count, ncolors, &colorvalues[0], &eps[0]); if (ok) { for (int col = 0; col < ncolors; ++col) std::cout << Strutil::format("%8d %s\n", count[col], colorstrings[col]); } else { ot.error (command, (*ot.curimg)(0,0).geterror()); } ot.function_times[command] += timer(); return 0; } static int action_rangecheck (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_rangecheck, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); string_view lowarg = ot.express (argv[1]); string_view higharg = ot.express (argv[2]); ot.read (); ImageBuf &Aib ((*ot.curimg)(0,0)); int nchannels = Aib.nchannels(); std::vector low(nchannels,0.0f), high(nchannels,1.0f); Strutil::extract_from_list_string (low, lowarg, ","); Strutil::extract_from_list_string (high, higharg, ","); imagesize_t lowcount = 0, highcount = 0, inrangecount = 0; bool ok = ImageBufAlgo::color_range_check ((*ot.curimg)(0,0), &lowcount, &highcount, &inrangecount, &low[0], &high[0]); if (ok) { std::cout << Strutil::format("%8d < %s\n", lowcount, lowarg); std::cout << Strutil::format("%8d > %s\n", highcount, higharg); std::cout << Strutil::format("%8d within range\n", inrangecount); } else { ot.error (command, (*ot.curimg)(0,0).geterror()); } ot.function_times[command] += timer(); return 0; } static int action_diff (int argc, const char *argv[]) { if (ot.postpone_callback (2, action_diff, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); int ret = do_action_diff (*ot.image_stack.back(), *ot.curimg, ot); if (ret != DiffErrOK && ret != DiffErrWarn) ot.return_value = EXIT_FAILURE; if (ret != DiffErrOK && ret != DiffErrWarn && ret != DiffErrFail) ot.error (command); ot.function_times[command] += timer(); return 0; } static int action_pdiff (int argc, const char *argv[]) { if (ot.postpone_callback (2, action_pdiff, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); int ret = do_action_diff (*ot.image_stack.back(), *ot.curimg, ot, 1); if (ret != DiffErrOK && ret != DiffErrWarn) ot.return_value = EXIT_FAILURE; if (ret != DiffErrOK && ret != DiffErrWarn && ret != DiffErrFail) ot.error (command); ot.function_times[command] += timer(); return 0; } BINARY_IMAGE_OP (add, ImageBufAlgo::add); BINARY_IMAGE_OP (sub, ImageBufAlgo::sub); BINARY_IMAGE_OP (mul, ImageBufAlgo::mul); BINARY_IMAGE_OP (div, ImageBufAlgo::div); BINARY_IMAGE_OP (absdiff, ImageBufAlgo::absdiff); BINARY_IMAGE_COLOR_OP (addc, ImageBufAlgo::add, 0); BINARY_IMAGE_COLOR_OP (subc, ImageBufAlgo::sub, 0); BINARY_IMAGE_COLOR_OP (mulc, ImageBufAlgo::mul, 1); BINARY_IMAGE_COLOR_OP (divc, ImageBufAlgo::div, 1); BINARY_IMAGE_COLOR_OP (absdiffc, ImageBufAlgo::absdiff, 0); BINARY_IMAGE_COLOR_OP (powc, ImageBufAlgo::pow, 1.0f); UNARY_IMAGE_OP (abs, ImageBufAlgo::abs); UNARY_IMAGE_OP (unpremult, ImageBufAlgo::unpremult); UNARY_IMAGE_OP (premult, ImageBufAlgo::premult); class OpMad : public OiiotoolOp { public: OpMad (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 3) {} virtual int impl (ImageBuf **img) { return ImageBufAlgo::mad (*img[0], *img[1], *img[2], *img[3]); } }; OP_CUSTOMCLASS (mad, OpMad, 3); class OpInvert : public OiiotoolOp { public: OpInvert (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) {} virtual int impl (ImageBuf **img) { // invert the first three channels only, spare alpha ROI roi = img[1]->roi(); roi.chend = std::min (3, roi.chend); return ImageBufAlgo::invert (*img[0], *img[1], roi, 0); } }; OP_CUSTOMCLASS (invert, OpInvert, 1); class OpNoise : public OiiotoolOp { public: OpNoise (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) { } virtual void option_defaults () { options["type"] = "gaussian"; options["min"] = "0"; options["max"] = "0.1"; options["mean"] = "0"; options["stddev"] = "0.1"; options["portion"] = "0.01"; options["value"] = "0"; options["mono"] = "0"; options["seed"] = "0"; options["nchannels"] = "10000"; } virtual int impl (ImageBuf **img) { img[0]->copy (*img[1]); string_view type (options["type"]); float A = 0.0f, B = 0.1f; if (type == "gaussian") { A = Strutil::from_string (options["mean"]); B = Strutil::from_string (options["stddev"]); } else if (type == "uniform") { A = Strutil::from_string (options["min"]); B = Strutil::from_string (options["max"]); } else if (type == "salt") { A = Strutil::from_string (options["value"]); B = Strutil::from_string (options["portion"]); } else { ot.error (opname(), Strutil::format ("Unknown noise type \"%s\"", type)); return 0; } bool mono = Strutil::from_string (options["mono"]); int seed = Strutil::from_string (options["seed"]); int nchannels = Strutil::from_string (options["nchannels"]); ROI roi = img[0]->roi(); roi.chend = std::min (roi.chend, nchannels); return ImageBufAlgo::noise (*img[0], type, A, B, mono, seed, roi); } }; OP_CUSTOMCLASS (noise, OpNoise, 1); static int action_chsum (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_chsum, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); ImageRecRef A (ot.pop()); ot.read (A); ImageRecRef R (new ImageRec ("chsum", ot.allsubimages ? A->subimages() : 1)); ot.push (R); for (int s = 0, subimages = R->subimages(); s < subimages; ++s) { std::vector weight ((*A)(s).nchannels(), 1.0f); std::map options; ot.extract_options (options, command); Strutil::extract_from_list_string (weight, options["weight"]); ImageBuf &Rib ((*R)(s)); const ImageBuf &Aib ((*A)(s)); bool ok = ImageBufAlgo::channel_sum (Rib, Aib, &weight[0]); if (! ok) ot.error (command, Rib.geterror()); R->update_spec_from_imagebuf (s); } ot.function_times[command] += timer(); return 0; } UNARY_IMAGE_OP (flip, ImageBufAlgo::flip); UNARY_IMAGE_OP (flop, ImageBufAlgo::flop); UNARY_IMAGE_OP (rotate180, ImageBufAlgo::rotate180); UNARY_IMAGE_OP (rotate90, ImageBufAlgo::rotate90); UNARY_IMAGE_OP (rotate270, ImageBufAlgo::rotate270); UNARY_IMAGE_OP (transpose, ImageBufAlgo::transpose); int action_reorient (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_reorient, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); // Make sure time in the rotate functions is charged to reorient bool old_enable_function_timing = ot.enable_function_timing; ot.enable_function_timing = false; ImageRecRef A = ot.pop(); ot.read (A); // See if any subimages need to be reoriented bool needs_reorient = false; for (int s = 0, subimages = A->subimages(); s < subimages; ++s) { int orientation = (*A)(s).orientation(); needs_reorient |= (orientation != 1); } if (needs_reorient) { ImageRecRef R (new ImageRec ("reorient", ot.allsubimages ? A->subimages() : 1)); ot.push (R); for (int s = 0, subimages = R->subimages(); s < subimages; ++s) { ImageBufAlgo::reorient ((*R)(s), (*A)(s)); R->update_spec_from_imagebuf (s); } } else { // No subimages need modification, just leave the whole thing in // place. ot.push (A); } ot.function_times[command] += timer(); ot.enable_function_timing = old_enable_function_timing; return 0; } class OpRotate : public OiiotoolOp { public: OpRotate (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) {} virtual int impl (ImageBuf **img) { float angle = Strutil::from_string (args[1]); std::string filtername = options["filter"]; bool recompute_roi = Strutil::from_string(options["recompute_roi"]); string_view center (options["center"]); float center_x = 0.0f, center_y = 0.0f; float cx, cy; if (center.size() && Strutil::parse_float(center, center_x) && Strutil::parse_char(center, ',') && Strutil::parse_float(center, center_y)) { // center supplied cx = center_x; cy = center_y; } else { ROI src_roi_full = img[1]->roi_full(); cx = 0.5f * (src_roi_full.xbegin + src_roi_full.xend); cy = 0.5f * (src_roi_full.ybegin + src_roi_full.yend); } return ImageBufAlgo::rotate (*img[0], *img[1], angle*float(M_PI/180.0), cx, cy, filtername, 0.0f, recompute_roi); } }; OP_CUSTOMCLASS (rotate, OpRotate, 1); class OpWarp : public OiiotoolOp { public: OpWarp (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) {} virtual int impl (ImageBuf **img) { std::string filtername = options["filter"]; bool recompute_roi = Strutil::from_string(options["recompute_roi"]); std::vector M (9); if (Strutil::extract_from_list_string (M, args[1]) != 9) { ot.error (opname(), "expected 9 comma-separatd floats to form a 3x3 matrix"); return 0; } return ImageBufAlgo::warp (*img[0], *img[1], *(Imath::M33f *)&M[0], filtername, 0.0f, recompute_roi, ImageBuf::WrapDefault); } }; OP_CUSTOMCLASS (warp, OpWarp, 1); class OpCshift : public OiiotoolOp { public: OpCshift (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) {} virtual int impl (ImageBuf **img) { int x = 0, y = 0, z = 0; if (sscanf (args[1].c_str(), "%d%d%d", &x, &y, &z) < 2) { ot.error (opname(), Strutil::format ("Invalid shift offset '%s'", args[1])); return 0; } return ImageBufAlgo::circular_shift (*img[0], *img[1], x, y, z); } }; OP_CUSTOMCLASS (cshift, OpCshift, 1); static int action_pop (int argc, const char *argv[]) { ASSERT (argc == 1); ot.pop (); return 0; } static int action_dup (int argc, const char *argv[]) { ASSERT (argc == 1); ot.push (ot.curimg); return 0; } static int action_swap (int argc, const char *argv[]) { ASSERT (argc == 1); string_view command = ot.express (argv[0]); if (ot.image_stack.size() < 1) { ot.error (command, "requires at least two loaded images"); return 0; } ImageRecRef B (ot.pop()); ImageRecRef A (ot.pop()); ot.push (B); ot.push (A); return 0; } static int action_create (int argc, const char *argv[]) { ASSERT (argc == 3); Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); string_view size = ot.express (argv[1]); int nchans = Strutil::from_string (ot.express(argv[2])); if (nchans < 1 || nchans > 1024) { ot.warning (argv[0], Strutil::format ("Invalid number of channels: %d", nchans)); nchans = 3; } ImageSpec spec (64, 64, nchans, TypeDesc::FLOAT); ot.adjust_geometry (argv[0], spec.width, spec.height, spec.x, spec.y, size.c_str()); spec.full_x = spec.x; spec.full_y = spec.y; spec.full_z = spec.z; spec.full_width = spec.width; spec.full_height = spec.height; spec.full_depth = spec.depth; ImageRecRef img (new ImageRec ("new", spec, ot.imagecache)); bool ok = ImageBufAlgo::zero ((*img)()); if (! ok) ot.error (command, (*img)().geterror()); if (ot.curimg) ot.image_stack.push_back (ot.curimg); ot.curimg = img; ot.function_times[command] += timer(); return 0; } static int action_pattern (int argc, const char *argv[]) { ASSERT (argc == 4); Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); std::string pattern = ot.express (argv[1]); std::string size = ot.express (argv[2]); int nchans = Strutil::from_string (ot.express(argv[3])); if (nchans < 1 || nchans > 1024) { ot.warning (argv[0], Strutil::format ("Invalid number of channels: %d", nchans)); nchans = 3; } ImageSpec spec (64, 64, nchans, TypeDesc::FLOAT); ot.adjust_geometry (argv[0], spec.width, spec.height, spec.x, spec.y, size.c_str()); spec.full_x = spec.x; spec.full_y = spec.y; spec.full_z = spec.z; spec.full_width = spec.width; spec.full_height = spec.height; spec.full_depth = spec.depth; ImageRecRef img (new ImageRec ("new", spec, ot.imagecache)); ot.push (img); ImageBuf &ib ((*img)()); bool ok = true; if (Strutil::iequals(pattern,"black")) { ok = ImageBufAlgo::zero (ib); } else if (Strutil::istarts_with(pattern,"constant")) { std::vector fill (nchans, 1.0f); std::map options; ot.extract_options (options, pattern); Strutil::extract_from_list_string (fill, options["color"]); ok = ImageBufAlgo::fill (ib, &fill[0]); } else if (Strutil::istarts_with(pattern,"fill")) { std::vector topleft (nchans, 1.0f); std::vector topright (nchans, 1.0f); std::vector bottomleft (nchans, 1.0f); std::vector bottomright (nchans, 1.0f); std::map options; ot.extract_options (options, pattern); if (Strutil::extract_from_list_string (topleft, options["topleft"]) && Strutil::extract_from_list_string (topright, options["topright"]) && Strutil::extract_from_list_string (bottomleft, options["bottomleft"]) && Strutil::extract_from_list_string (bottomright, options["bottomright"])) { ok = ImageBufAlgo::fill (ib, &topleft[0], &topright[0], &bottomleft[0], &bottomright[0]); } else if (Strutil::extract_from_list_string (topleft, options["top"]) && Strutil::extract_from_list_string (bottomleft, options["bottom"])) { ok = ImageBufAlgo::fill (ib, &topleft[0], &bottomleft[0]); } else if (Strutil::extract_from_list_string (topleft, options["left"]) && Strutil::extract_from_list_string (topright, options["right"])) { ok = ImageBufAlgo::fill (ib, &topleft[0], &topright[0], &topleft[0], &topright[0]); } else if (Strutil::extract_from_list_string (topleft, options["color"])) { ok = ImageBufAlgo::fill (ib, &topleft[0]); } } else if (Strutil::istarts_with(pattern,"checker")) { std::map options; options["width"] = "8"; options["height"] = "8"; options["depth"] = "8"; ot.extract_options (options, pattern); int width = Strutil::from_string (options["width"]); int height = Strutil::from_string (options["height"]); int depth = Strutil::from_string (options["depth"]); std::vector color1 (nchans, 0.0f); std::vector color2 (nchans, 1.0f); Strutil::extract_from_list_string (color1, options["color1"]); Strutil::extract_from_list_string (color2, options["color2"]); ok = ImageBufAlgo::checker (ib, width, height, depth, &color1[0], &color2[0], 0, 0, 0); } else if (Strutil::istarts_with(pattern, "noise")) { std::map options; options["type"] = "gaussian"; options["min"] = "0.5"; options["max"] = "1"; options["mean"] = "0.5"; options["stddev"] = "0.1"; options["portion"] = "0.01"; options["value"] = "0"; options["mono"] = "0"; options["seed"] = "0"; ot.extract_options (options, pattern); std::string type = options["type"]; float A = 0, B = 1; bool ok = true; if (type == "gaussian") { A = Strutil::from_string (options["mean"]); B = Strutil::from_string (options["stddev"]); } else if (type == "uniform") { A = Strutil::from_string (options["min"]); B = Strutil::from_string (options["max"]); } else if (type == "salt") { A = Strutil::from_string (options["value"]); B = Strutil::from_string (options["portion"]); } else { ot.error (command, Strutil::format ("Unknown noise type \"%s\"", type)); ok = false; } bool mono = Strutil::from_string (options["mono"]); int seed = Strutil::from_string (options["seed"]); ImageBufAlgo::zero (ib); if (ok) ok = ImageBufAlgo::noise (ib, type, A, B, mono, seed); } else { ok = ImageBufAlgo::zero (ib); ot.warning (command, Strutil::format("Unknown pattern \"%s\"", pattern)); } if (! ok) ot.error (command, ib.geterror()); ot.function_times[command] += timer(); return 0; } class OpKernel : public OiiotoolOp { public: OpKernel (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 0) { } virtual int impl (ImageBuf **img) { string_view kernelname (args[1]); string_view kernelsize (args[2]); float w = 1.0f, h = 1.0f; if (sscanf (kernelsize.c_str(), "%fx%f", &w, &h) != 2) ot.error (opname(), Strutil::format ("Unknown size %s", kernelsize)); return ImageBufAlgo::make_kernel (*img[0], kernelname, w, h); } }; OP_CUSTOMCLASS (kernel, OpKernel, 0); static int action_capture (int argc, const char *argv[]) { ASSERT (argc == 1); Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); std::map options; ot.extract_options (options, command); int camera = Strutil::from_string (options["camera"]); ImageBuf ib; bool ok = ImageBufAlgo::capture_image (ib, camera, TypeDesc::FLOAT); if (! ok) ot.error (command, ib.geterror()); ImageRecRef img (new ImageRec ("capture", ib.spec(), ot.imagecache)); (*img)().copy (ib); ot.push (img); ot.function_times[command] += timer(); return 0; } int action_crop (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_crop, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); string_view size = ot.express (argv[1]); std::map options; options["allsubimages"] = Strutil::format("%d",ot.allsubimages); ot.extract_options (options, command); int crop_all_subimages = Strutil::from_string(options["allsubimages"]); ot.read (); ImageRecRef A = ot.curimg; bool crops_needed = false; int subimages = crop_all_subimages ? A->subimages() : 1; for (int s = 0; s < subimages; ++s) { ImageSpec &spec (*A->spec(s,0)); int w = spec.width, h = spec.height, d = spec.depth; int x = spec.x, y = spec.y, z = spec.z; ot.adjust_geometry (argv[0], w, h, x, y, size.c_str()); crops_needed |= (w != spec.width || h != spec.height || d != spec.depth || x != spec.x || y != spec.y || z != spec.z); } if (crops_needed) { ot.pop (); ImageRecRef R (new ImageRec (A->name(), subimages, 0)); ot.push (R); for (int s = 0; s < subimages; ++s) { ImageSpec &spec (*A->spec(s,0)); int w = spec.width, h = spec.height, d = spec.depth; int x = spec.x, y = spec.y, z = spec.z; ot.adjust_geometry (argv[0], w, h, x, y, size.c_str()); const ImageBuf &Aib ((*A)(s,0)); ImageBuf &Rib ((*R)(s,0)); ROI roi = Aib.roi(); if (w != spec.width || h != spec.height || d != spec.depth || x != spec.x || y != spec.y || z != spec.z) { roi = ROI (x, x+w, y, y+h, z, z+d); } bool ok = ImageBufAlgo::crop (Rib, Aib, roi); if (! ok) ot.error (command, Rib.geterror()); R->update_spec_from_imagebuf (s, 0); } } ot.function_times[command] += timer(); return 0; } int action_croptofull (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_croptofull, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); ot.read (); ImageRecRef A = ot.curimg; bool crops_needed = false; for (int s = 0; s < A->subimages(); ++s) { crops_needed |= ((*A)(s).roi() != (*A)(s).roi_full()); } if (crops_needed) { ot.pop (); ImageRecRef R (new ImageRec (A->name(), A->subimages(), 0)); ot.push (R); for (int s = 0; s < A->subimages(); ++s) { const ImageBuf &Aib ((*A)(s,0)); ImageBuf &Rib ((*R)(s,0)); ROI roi = (Aib.roi() != Aib.roi_full()) ? Aib.roi_full() : Aib.roi(); bool ok = ImageBufAlgo::crop (Rib, Aib, roi); if (! ok) ot.error (command, Rib.geterror()); R->update_spec_from_imagebuf (s, 0); } } ot.function_times[command] += timer(); return 0; } int action_trim (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_trim, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); ot.read (); ImageRecRef A = ot.curimg; // First, figure out shared nonzero region ROI nonzero_region; for (int s = 0; s < A->subimages(); ++s) { ROI roi = ImageBufAlgo::nonzero_region ((*A)(s)); if (roi.npixels() == 0) { // Special case -- all zero; but doctor to make it 1 zero pixel roi = (*A)(s).roi(); roi.xend = roi.xbegin+1; roi.yend = roi.ybegin+1; roi.zend = roi.zbegin+1; } nonzero_region = roi_union (nonzero_region, roi); } // Now see if any subimges need cropping bool crops_needed = false; for (int s = 0; s < A->subimages(); ++s) { crops_needed |= (nonzero_region != (*A)(s).roi()); } if (crops_needed) { ot.pop (); ImageRecRef R (new ImageRec (A->name(), A->subimages(), 0)); ot.push (R); for (int s = 0; s < A->subimages(); ++s) { const ImageBuf &Aib ((*A)(s,0)); ImageBuf &Rib ((*R)(s,0)); bool ok = ImageBufAlgo::crop (Rib, Aib, nonzero_region); if (! ok) ot.error (command, Rib.geterror()); R->update_spec_from_imagebuf (s, 0); } } ot.function_times[command] += timer(); return 0; } int action_cut (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_cut, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); string_view size = ot.express (argv[1]); ot.read (); ImageRecRef A = ot.pop(); ImageSpec &Aspec (*A->spec(0,0)); ImageSpec newspec = Aspec; ot.adjust_geometry (argv[0], newspec.width, newspec.height, newspec.x, newspec.y, size.c_str()); ImageRecRef R (new ImageRec (A->name(), newspec, ot.imagecache)); const ImageBuf &Aib ((*A)(0,0)); ImageBuf &Rib ((*R)(0,0)); ImageBufAlgo::cut (Rib, Aib, get_roi(newspec)); ImageSpec &spec (*R->spec(0,0)); set_roi (spec, Rib.roi()); set_roi_full (spec, Rib.roi()); A->metadata_modified (true); ot.push (R); ot.function_times[command] += timer(); return 0; } class OpResample : public OiiotoolOp { public: OpResample (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) { } virtual int compute_subimages () { return 1; } // just the first one virtual bool setup () { // The size argument will be the resulting display (full) window. const ImageSpec &Aspec (*ir[1]->spec(0,0)); ImageSpec newspec = Aspec; ot.adjust_geometry (args[0], newspec.full_width, newspec.full_height, newspec.full_x, newspec.full_y, args[1].c_str() /*size*/, true); if (newspec.full_width == Aspec.full_width && newspec.full_height == Aspec.full_height) { // No change -- pop the temp result and restore the original ot.pop (); ot.push (ir[1]); return false; // nothing more to do } // Compute corresponding data window. float wratio = float(newspec.full_width) / float(Aspec.full_width); float hratio = float(newspec.full_height) / float(Aspec.full_height); newspec.x = newspec.full_x + int(floorf ((Aspec.x - Aspec.full_x) * wratio)); newspec.y = newspec.full_y + int(floorf ((Aspec.y - Aspec.full_y) * hratio)); newspec.width = int(ceilf (Aspec.width * wratio)); newspec.height = int(ceilf (Aspec.height * hratio)); (*ir[0])(0,0).reset (newspec); return true; } virtual int impl (ImageBuf **img) { return ImageBufAlgo::resample (*img[0], *img[1]); } }; OP_CUSTOMCLASS (resample, OpResample, 1); class OpResize : public OiiotoolOp { public: OpResize (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) { } virtual int compute_subimages () { return 1; } // just the first one virtual bool setup () { // The size argument will be the resulting display (full) window. const ImageSpec &Aspec (*ir[1]->spec(0,0)); ImageSpec newspec = Aspec; ot.adjust_geometry (args[0], newspec.full_width, newspec.full_height, newspec.full_x, newspec.full_y, args[1].c_str() /*size*/, true); if (newspec.full_width == Aspec.full_width && newspec.full_height == Aspec.full_height) { // No change -- pop the temp result and restore the original ot.pop (); ot.push (ir[1]); return false; // nothing more to do } // Compute corresponding data window. float wratio = float(newspec.full_width) / float(Aspec.full_width); float hratio = float(newspec.full_height) / float(Aspec.full_height); newspec.x = newspec.full_x + int(floorf ((Aspec.x - Aspec.full_x) * wratio)); newspec.y = newspec.full_y + int(floorf ((Aspec.y - Aspec.full_y) * hratio)); newspec.width = int(ceilf (Aspec.width * wratio)); newspec.height = int(ceilf (Aspec.height * hratio)); (*ir[0])(0,0).reset (newspec); return true; } virtual int impl (ImageBuf **img) { string_view filtername = options["filter"]; if (ot.debug) { const ImageSpec &newspec (img[0]->spec()); const ImageSpec &Aspec (img[1]->spec()); std::cout << " Resizing " << Aspec.width << "x" << Aspec.height << " to " << newspec.width << "x" << newspec.height << " using " << (filtername.size() ? filtername.c_str() : "default") << " filter\n"; } return ImageBufAlgo::resize (*img[0], *img[1], filtername, 0.0f, img[0]->roi()); } }; OP_CUSTOMCLASS (resize, OpResize, 1); static int action_fit (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_fit, argc, argv)) return 0; Timer timer (ot.enable_function_timing); bool old_enable_function_timing = ot.enable_function_timing; ot.enable_function_timing = false; string_view command = ot.express (argv[0]); string_view size = ot.express (argv[1]); // Examine the top of stack ImageRecRef A = ot.top(); ot.read (); const ImageSpec *Aspec = A->spec(0,0); // Parse the user request for resolution to fit int fit_full_width = Aspec->full_width; int fit_full_height = Aspec->full_height; int fit_full_x = Aspec->full_x; int fit_full_y = Aspec->full_y; ot.adjust_geometry (argv[0], fit_full_width, fit_full_height, fit_full_x, fit_full_y, size.c_str(), false); std::map options; ot.extract_options (options, command); string_view padopt = options["pad"]; bool pad = padopt.size() && atoi(padopt.c_str()); string_view filtername = options["filter"]; // Compute scaling factors and use action_resize to do the heavy lifting float oldaspect = float(Aspec->full_width) / Aspec->full_height; float newaspect = float(fit_full_width) / fit_full_height; int resize_full_width = fit_full_width; int resize_full_height = fit_full_height; int xoffset = 0, yoffset = 0; if (newaspect >= oldaspect) { // same or wider than original resize_full_width = int(resize_full_height * oldaspect + 0.5f); xoffset = (fit_full_width - resize_full_width) / 2; } else { // narrower than original resize_full_height = int(resize_full_width / oldaspect + 0.5f); yoffset = (fit_full_height - resize_full_height) / 2; } if (ot.debug) { std::cout << " Fitting " << format_resolution(Aspec->full_width, Aspec->full_height, Aspec->full_x, Aspec->full_y) << " into " << format_resolution(fit_full_width, fit_full_height, fit_full_x, fit_full_y) << "\n"; } if (resize_full_width != Aspec->full_width || resize_full_height != Aspec->full_height || fit_full_x != Aspec->full_x || fit_full_y != Aspec->full_y) { std::string resize = format_resolution (resize_full_width, resize_full_height, 0, 0); if (ot.debug) std::cout << " Resizing to " << resize << "\n"; std::string command = "resize"; if (filtername.size()) command += Strutil::format (":filter=%s", filtername); const char *newargv[2] = { command.c_str(), resize.c_str() }; action_resize (2, newargv); A = ot.top (); Aspec = A->spec(0,0); // Now A,Aspec are for the NEW resized top of stack } else { if (ot.debug) std::cout << " no need to do a resize\n"; } A->spec(0,0)->full_width = (*A)(0,0).specmod().full_width = fit_full_width; A->spec(0,0)->full_height = (*A)(0,0).specmod().full_height = fit_full_height; A->spec(0,0)->full_x = (*A)(0,0).specmod().full_x = fit_full_x; A->spec(0,0)->full_y = (*A)(0,0).specmod().full_y = fit_full_y; A->spec(0,0)->x = (*A)(0,0).specmod().x = xoffset; A->spec(0,0)->y = (*A)(0,0).specmod().y = yoffset; if (pad && (fit_full_width != Aspec->width || fit_full_height != Aspec->height)) { // Needs padding if (ot.debug) std::cout << " performing a croptofull\n"; const char *argv[] = { "croptofull" }; action_croptofull (1, argv); } ot.function_times[command] += timer(); ot.enable_function_timing = old_enable_function_timing; return 0; } static int action_pixelaspect (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_pixelaspect, argc, argv)) return 0; Timer timer (ot.enable_function_timing); bool old_enable_function_timing = ot.enable_function_timing; ot.enable_function_timing = false; string_view command = ot.express (argv[0]); float new_paspect = Strutil::from_string (ot.express (argv[1])); if (new_paspect <= 0.0f) { ot.error (command, Strutil::format ("Invalid pixel aspect ratio '%g'", new_paspect)); return 0; } // Examine the top of stack ImageRecRef A = ot.top(); ot.read (); const ImageSpec *Aspec = A->spec(0,0); // Get the current pixel aspect ratio float paspect = Aspec->get_float_attribute ("PixelAspectRatio", 1.0); if (paspect <= 0.0f) { ot.error (command, Strutil::format ("Invalid pixel aspect ratio '%g' in source", paspect)); return 0; } // Get the current (if any) XResolution/YResolution attributes float xres = Aspec->get_float_attribute( "XResolution", 0.0); float yres = Aspec->get_float_attribute( "YResolution", 0.0); // Compute scaling factors and use action_resize to do the heavy lifting float scaleX = 1.0f; float scaleY = 1.0f; float factor = paspect / new_paspect; if (factor > 1.0) scaleX = factor; else if (factor < 1.0) scaleY = 1.0/factor; int scale_full_width = (int)(Aspec->full_width * scaleX + 0.5f); int scale_full_height = (int)(Aspec->full_height * scaleY + 0.5f); float scale_xres = xres * scaleX; float scale_yres = yres * scaleY; std::map options; ot.extract_options (options, command); string_view filtername = options["filter"]; if (ot.debug) { std::cout << " Scaling " << format_resolution(Aspec->full_width, Aspec->full_height, Aspec->full_x, Aspec->full_y) << " with a pixel aspect ratio of " << paspect << " to " << format_resolution(scale_full_width, scale_full_height, Aspec->full_x, Aspec->full_y) << "\n"; } if (scale_full_width != Aspec->full_width || scale_full_height != Aspec->full_height) { std::string resize = format_resolution(scale_full_width, scale_full_height, 0, 0); std::string command = "resize"; if (filtername.size()) command += Strutil::format (":filter=%s", filtername); const char *newargv[2] = { command.c_str(), resize.c_str() }; action_resize (2, newargv); A = ot.top (); Aspec = A->spec(0,0); A->spec(0,0)->full_width = (*A)(0,0).specmod().full_width = scale_full_width; A->spec(0,0)->full_height = (*A)(0,0).specmod().full_height = scale_full_height; A->spec(0,0)->attribute ("PixelAspectRatio", new_paspect); if (xres) A->spec(0,0)->attribute ("XResolution", scale_xres); if (yres) A->spec(0,0)->attribute ("YResolution", scale_yres); // Now A,Aspec are for the NEW resized top of stack } ot.function_times[command] += timer(); ot.enable_function_timing = old_enable_function_timing; return 0; } class OpConvolve : public OiiotoolOp { public: OpConvolve (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 2) {} virtual int impl (ImageBuf **img) { return ImageBufAlgo::convolve (*img[0], *img[1], *img[2]); } }; OP_CUSTOMCLASS (convolve, OpConvolve, 2); class OpBlur : public OiiotoolOp { public: OpBlur (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) {} virtual void option_defaults () { options["kernel"] = "gaussian"; }; virtual int impl (ImageBuf **img) { string_view kernopt = options["kernel"]; float w = 1.0f, h = 1.0f; if (sscanf (args[1].c_str(), "%fx%f", &w, &h) != 2) ot.error (opname(), Strutil::format ("Unknown size %s", args[1])); ImageBuf Kernel; if (! ImageBufAlgo::make_kernel (Kernel, kernopt, w, h)) ot.error (opname(), Kernel.geterror()); return ImageBufAlgo::convolve (*img[0], *img[1], Kernel); } }; OP_CUSTOMCLASS (blur, OpBlur, 1); class OpMedian : public OiiotoolOp { public: OpMedian (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) { } virtual int impl (ImageBuf **img) { string_view size (args[1]); int w = 3, h = 3; if (sscanf (size.c_str(), "%dx%d", &w, &h) != 2) ot.error (opname(), Strutil::format ("Unknown size %s", size)); return ImageBufAlgo::median_filter (*img[0], *img[1], w, h); } }; OP_CUSTOMCLASS (median, OpMedian, 1); class OpDilate : public OiiotoolOp { public: OpDilate (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) { } virtual int impl (ImageBuf **img) { string_view size (args[1]); int w = 3, h = 3; if (sscanf (size.c_str(), "%dx%d", &w, &h) != 2) ot.error (opname(), Strutil::format ("Unknown size %s", size)); return ImageBufAlgo::dilate (*img[0], *img[1], w, h); } }; OP_CUSTOMCLASS (dilate, OpDilate, 1); class OpErode : public OiiotoolOp { public: OpErode (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) { } virtual int impl (ImageBuf **img) { string_view size (args[1]); int w = 3, h = 3; if (sscanf (size.c_str(), "%dx%d", &w, &h) != 2) ot.error (opname(), Strutil::format ("Unknown size %s", size)); return ImageBufAlgo::erode (*img[0], *img[1], w, h); } }; OP_CUSTOMCLASS (erode, OpErode, 1); class OpUnsharp : public OiiotoolOp { public: OpUnsharp (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) { } virtual void option_defaults () { options["kernel"] = "gaussian"; options["width"] = "3"; options["contrast"] = "1"; options["threshold"] = "0"; } virtual int impl (ImageBuf **img) { string_view kernel = options["kernel"]; float width = Strutil::from_string (options["width"]); float contrast = Strutil::from_string (options["contrast"]); float threshold = Strutil::from_string (options["threshold"]); return ImageBufAlgo::unsharp_mask (*img[0], *img[1], kernel, width, contrast, threshold); } }; OP_CUSTOMCLASS (unsharp, OpUnsharp, 1); class OpLaplacian : public OiiotoolOp { public: OpLaplacian (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) { } virtual int impl (ImageBuf **img) { return ImageBufAlgo::laplacian (*img[0], *img[1]); } }; OP_CUSTOMCLASS (laplacian, OpLaplacian, 1); UNARY_IMAGE_OP (fft, ImageBufAlgo::fft); UNARY_IMAGE_OP (ifft, ImageBufAlgo::ifft); UNARY_IMAGE_OP (polar, ImageBufAlgo::complex_to_polar); UNARY_IMAGE_OP (unpolar, ImageBufAlgo::polar_to_complex); int action_fixnan (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_fixnan, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); string_view modename = ot.express (argv[1]); NonFiniteFixMode mode = NONFINITE_BOX3; if (modename == "black") mode = NONFINITE_BLACK; else if (modename == "box3") mode = NONFINITE_BOX3; else if (modename == "error") mode = NONFINITE_ERROR; else { ot.warning (argv[0], Strutil::format ("\"%s\" not recognized. Valid choices: black, box3, error", modename)); } ot.read (); ImageRecRef A = ot.pop(); ot.push (new ImageRec (*A, ot.allsubimages ? -1 : 0, ot.allsubimages ? -1 : 0, true, false)); int subimages = ot.curimg->subimages(); for (int s = 0; s < subimages; ++s) { int miplevels = ot.curimg->miplevels(s); for (int m = 0; m < miplevels; ++m) { const ImageBuf &Aib ((*A)(s,m)); ImageBuf &Rib ((*ot.curimg)(s,m)); bool ok = ImageBufAlgo::fixNonFinite (Rib, Aib, mode); if (! ok) ot.error (command, Rib.geterror()); } } ot.function_times[command] += timer(); return 0; } static int action_fillholes (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_fillholes, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); // Read and copy the top-of-stack image ImageRecRef A (ot.pop()); ot.read (A); ImageSpec spec = (*A)(0,0).spec(); set_roi (spec, roi_union (get_roi(spec), get_roi_full(spec))); ImageRecRef B (new ImageRec("filled", spec, ot.imagecache)); ot.push (B); ImageBuf &Rib ((*B)(0,0)); bool ok = ImageBufAlgo::fillholes_pushpull (Rib, (*A)(0,0)); if (! ok) ot.error (command, Rib.geterror()); ot.function_times[command] += timer(); return 0; } static int action_paste (int argc, const char *argv[]) { if (ot.postpone_callback (2, action_paste, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); string_view position = ot.express (argv[1]); ImageRecRef BG (ot.pop()); ImageRecRef FG (ot.pop()); ot.read (BG); ot.read (FG); int x = 0, y = 0; if (sscanf (position.c_str(), "%d%d", &x, &y) != 2) { ot.error (command, Strutil::format ("Invalid offset '%s'", position)); return 0; } ImageRecRef R (new ImageRec (*BG, 0, 0, true /* writable*/, true /* copy */)); ot.push (R); bool ok = ImageBufAlgo::paste ((*R)(), x, y, 0, 0, (*FG)()); if (! ok) ot.error (command, (*R)().geterror()); ot.function_times[command] += timer(); return 0; } static int action_mosaic (int argc, const char *argv[]) { Timer timer (ot.enable_function_timing); // Mosaic is tricky. We have to parse the argument before we know // how many images it wants to pull off the stack. string_view command = ot.express (argv[0]); string_view size = ot.express (argv[1]); int ximages = 0, yimages = 0; if (sscanf (size.c_str(), "%dx%d", &ximages, &yimages) != 2 || ximages < 1 || yimages < 1) { ot.error (command, Strutil::format ("Invalid size '%s'", size)); return 0; } int nimages = ximages * yimages; // Make the matrix complete with placeholder images ImageRecRef blank_img; while (ot.image_stack_depth() < nimages) { if (! blank_img) { ImageSpec blankspec (1, 1, 1, TypeDesc::UINT8); blank_img.reset (new ImageRec ("blank", blankspec, ot.imagecache)); ImageBufAlgo::zero ((*blank_img)()); } ot.push (blank_img); } int widest = 0, highest = 0, nchannels = 0; std::vector images (nimages); for (int i = nimages-1; i >= 0; --i) { ImageRecRef img = ot.pop(); images[i] = img; ot.read (img); widest = std::max (widest, img->spec()->full_width); highest = std::max (highest, img->spec()->full_height); nchannels = std::max (nchannels, img->spec()->nchannels); } std::map options; options["pad"] = "0"; ot.extract_options (options, command); int pad = strtol (options["pad"].c_str(), NULL, 10); ImageSpec Rspec (ximages*widest + (ximages-1)*pad, yimages*highest + (yimages-1)*pad, nchannels, TypeDesc::FLOAT); ImageRecRef R (new ImageRec ("mosaic", Rspec, ot.imagecache)); ot.push (R); ImageBufAlgo::zero ((*R)()); for (int j = 0; j < yimages; ++j) { int y = j * (highest + pad); for (int i = 0; i < ximages; ++i) { int x = i * (widest + pad); bool ok = ImageBufAlgo::paste ((*R)(), x, y, 0, 0, (*images[j*ximages+i])(0)); if (! ok) ot.error (command, (*R)().geterror()); } } ot.function_times[command] += timer(); return 0; } BINARY_IMAGE_OP (over, ImageBufAlgo::over); #if 0 // Don't enable this until we are sure we have a zover test in testsuite. class OpZover : public OiiotoolOp { public: OpZover (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) {} virtual int impl (ImageBuf **img) { bool zeroisinf = Strutil::from_string(options["zeroisinf"]); return ImageBufAlgo::zover (*img[0], *img[1], zeroisinf, ROI(), 0); } }; OP_CUSTOMCLASS (zover, OpZover, 1); #else static int action_zover (int argc, const char *argv[]) { if (ot.postpone_callback (2, action_zover, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); // Get optional flags bool z_zeroisinf = false; std::string cmd = argv[0]; size_t pos; while ((pos = cmd.find_first_of(":")) != std::string::npos) { cmd = cmd.substr (pos+1, std::string::npos); if (Strutil::istarts_with(cmd,"zeroisinf=")) z_zeroisinf = (atoi(cmd.c_str()+10) != 0); } ImageRecRef B (ot.pop()); ImageRecRef A (ot.pop()); ot.read (A); ot.read (B); const ImageBuf &Aib ((*A)()); const ImageBuf &Bib ((*B)()); const ImageSpec &specA = Aib.spec(); const ImageSpec &specB = Bib.spec(); // Create output image specification. ImageSpec specR = specA; set_roi (specR, roi_union (get_roi(specA), get_roi(specB))); set_roi_full (specR, roi_union (get_roi_full(specA), get_roi_full(specB))); ot.push (new ImageRec ("zover", specR, ot.imagecache)); ImageBuf &Rib ((*ot.curimg)()); bool ok = ImageBufAlgo::zover (Rib, Aib, Bib, z_zeroisinf); if (! ok) ot.error (command, Rib.geterror()); ot.function_times[command] += timer(); return 0; } #endif class OpDeepMerge : public OiiotoolOp { public: OpDeepMerge (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 2) {} virtual int impl (ImageBuf **img) { return ImageBufAlgo::deep_merge (*img[0], *img[1], *img[2]); } }; OP_CUSTOMCLASS (deepmerge, OpDeepMerge, 2); class OpDeepen : public OiiotoolOp { public: OpDeepen (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) {} virtual void option_defaults () { options["z"] = 1.0; } virtual int impl (ImageBuf **img) { float z = Strutil::from_string(options["z"]); return ImageBufAlgo::deepen (*img[0], *img[1], z); } }; OP_CUSTOMCLASS (deepen, OpDeepen, 1); UNARY_IMAGE_OP (flatten, ImageBufAlgo::flatten); static int action_fill (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_fill, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); string_view size = ot.express (argv[1]); // Read and copy the top-of-stack image ImageRecRef A (ot.pop()); ot.read (A); ot.push (new ImageRec (*A, 0, 0, true, true /*copy_pixels*/)); ImageBuf &Rib ((*ot.curimg)(0,0)); const ImageSpec &Rspec = Rib.spec(); int w = Rib.spec().width, h = Rib.spec().height; int x = Rib.spec().x, y = Rib.spec().y; if (! ot.adjust_geometry (argv[0], w, h, x, y, size.c_str(), true)) { return 0; } std::vector topleft (Rspec.nchannels, 1.0f); std::vector topright (Rspec.nchannels, 1.0f); std::vector bottomleft (Rspec.nchannels, 1.0f); std::vector bottomright (Rspec.nchannels, 1.0f); std::map options; ot.extract_options (options, command); bool ok = true; if (Strutil::extract_from_list_string (topleft, options["topleft"]) && Strutil::extract_from_list_string (topright, options["topright"]) && Strutil::extract_from_list_string (bottomleft, options["bottomleft"]) && Strutil::extract_from_list_string (bottomright, options["bottomright"])) { ok = ImageBufAlgo::fill (Rib, &topleft[0], &topright[0], &bottomleft[0], &bottomright[0], ROI(x, x+w, y, y+h)); } else if (Strutil::extract_from_list_string (topleft, options["top"]) && Strutil::extract_from_list_string (bottomleft, options["bottom"])) { ok = ImageBufAlgo::fill (Rib, &topleft[0], &bottomleft[0], ROI(x, x+w, y, y+h)); } else if (Strutil::extract_from_list_string (topleft, options["left"]) && Strutil::extract_from_list_string (topright, options["right"])) { ok = ImageBufAlgo::fill (Rib, &topleft[0], &topright[0], &topleft[0], &topright[0], ROI(x, x+w, y, y+h)); } else if (Strutil::extract_from_list_string (topleft, options["color"])) { ok = ImageBufAlgo::fill (Rib, &topleft[0], ROI(x, x+w, y, y+h)); } else { ot.warning (command, "No recognized fill parameters: filling with white."); ok = ImageBufAlgo::fill (Rib, &topleft[0], ROI(x, x+w, y, y+h)); } if (! ok) ot.error (command, Rib.geterror()); ot.function_times[command] += timer(); return 0; } static int action_clamp (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_clamp, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); ImageRecRef A = ot.pop(); ot.read (A); ImageRecRef R (new ImageRec (*A, ot.allsubimages ? -1 : 0, ot.allsubimages ? -1 : 0, true /*writeable*/, false /*copy_pixels*/)); ot.push (R); for (int s = 0, subimages = R->subimages(); s < subimages; ++s) { int nchans = (*R)(s,0).nchannels(); const float big = std::numeric_limits::max(); std::vector min (nchans, -big); std::vector max (nchans, big); std::map options; options["clampalpha"] = "0"; // initialize ot.extract_options (options, command); Strutil::extract_from_list_string (min, options["min"]); Strutil::extract_from_list_string (max, options["max"]); bool clampalpha01 = strtol (options["clampalpha"].c_str(), NULL, 10) != 0; for (int m = 0, miplevels=R->miplevels(s); m < miplevels; ++m) { ImageBuf &Rib ((*R)(s,m)); ImageBuf &Aib ((*A)(s,m)); bool ok = ImageBufAlgo::clamp (Rib, Aib, &min[0], &max[0], clampalpha01); if (! ok) ot.error (command, Rib.geterror()); } } ot.function_times[command] += timer(); return 0; } class OpRangeCompress : public OiiotoolOp { public: OpRangeCompress (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) {} virtual int impl (ImageBuf **img) { bool useluma = Strutil::from_string(options["luma"]); return ImageBufAlgo::rangecompress (*img[0], *img[1], useluma); } }; OP_CUSTOMCLASS (rangecompress, OpRangeCompress, 1); class OpRangeExpand : public OiiotoolOp { public: OpRangeExpand (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) {} virtual int impl (ImageBuf **img) { bool useluma = Strutil::from_string(options["luma"]); return ImageBufAlgo::rangeexpand (*img[0], *img[1], useluma); } }; OP_CUSTOMCLASS (rangeexpand, OpRangeExpand, 1); class OpBox : public OiiotoolOp { public: OpBox (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) {} virtual int impl (ImageBuf **img) { img[0]->copy (*img[1]); const ImageSpec &Rspec (img[0]->spec()); int x1, y1, x2, y2; string_view s (args[1]); if (Strutil::parse_int (s, x1) && Strutil::parse_char (s, ',') && Strutil::parse_int (s, y1) && Strutil::parse_char (s, ',') && Strutil::parse_int (s, x2) && Strutil::parse_char (s, ',') && Strutil::parse_int (s, y2)) { std::vector color (Rspec.nchannels+1, 1.0f); Strutil::extract_from_list_string (color, options["color"]); bool fill = Strutil::from_string(options["fill"]); return ImageBufAlgo::render_box (*img[0], x1, y1, x2, y2, color, fill); } return false; } }; OP_CUSTOMCLASS (box, OpBox, 1); class OpLine : public OiiotoolOp { public: OpLine (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) {} virtual int impl (ImageBuf **img) { img[0]->copy (*img[1]); const ImageSpec &Rspec (img[0]->spec()); std::vector points; Strutil::extract_from_list_string (points, args[1]); std::vector color (Rspec.nchannels+1, 1.0f); Strutil::extract_from_list_string (color, options["color"]); bool closed = (points.size() > 4 && points[0] == points[points.size()-2] && points[1] == points[points.size()-1]); for (size_t i = 0, e = points.size()-2; i < e; i += 2) ImageBufAlgo::render_line (*img[0], points[i+0], points[i+1], points[i+2], points[i+3], color, closed || i>0 /*skip_first_point*/); return true; } }; OP_CUSTOMCLASS (line, OpLine, 1); class OpText : public OiiotoolOp { public: OpText (Oiiotool &ot, string_view opname, int argc, const char *argv[]) : OiiotoolOp (ot, opname, argc, argv, 1) {} virtual int impl (ImageBuf **img) { img[0]->copy (*img[1]); const ImageSpec &Rspec (img[0]->spec()); int x = options["x"].size() ? Strutil::from_string(options["x"]) : (Rspec.x + Rspec.width/2); int y = options["y"].size() ? Strutil::from_string(options["y"]) : (Rspec.y + Rspec.height/2); int fontsize = options["size"].size() ? Strutil::from_string(options["size"]) : 16; std::string font = options["font"]; std::vector textcolor (Rspec.nchannels+1, 1.0f); Strutil::extract_from_list_string (textcolor, options["color"]); return ImageBufAlgo::render_text (*img[0], x, y, args[1], fontsize, font, &textcolor[0]); } }; OP_CUSTOMCLASS (text, OpText, 1); /// action_histogram --------------------------------------------------------- /// Usage: /// ./oiiotool in --histogram:cumulative=int 'bins'x'height' /// channel -o out /// /// in - Input image that contains the channel to be histogramed. /// cumulative - Optional argument that can take values 0 or 1. If 0, /// then each bin will contain the count of pixels having /// values in the range for that bin. If 1, then each bin /// will contain not only its count, but also the counts of /// all preceding bins. /// 'bins'x'height' - Width and height of the histogram, where width equals /// the number of bins. /// channel - The channel in the input image to be histogramed. /// out - Output image. /// /// Examples: /// - ./oiiotool in --histogram 256x256 0 -o out /// /// Save the non-cumulative histogram of channel 0 in image /// 'in', as an image with size 256x256. /// /// - ./oiiotool in --histogram:cumulative=1 256x256 0 -o out /// /// Same as the previous example, but now a cumulative /// histogram is created, instead of a regular one. /// -------------------------------------------------------------------------- static int action_histogram (int argc, const char *argv[]) { ASSERT (argc == 3); if (ot.postpone_callback (1, action_histogram, argc, argv)) return 0; Timer timer (ot.enable_function_timing); string_view command = ot.express (argv[0]); string_view size = ot.express(argv[1]); int channel = Strutil::from_string (ot.express(argv[2])); std::map options; ot.extract_options (options, command); int cumulative = Strutil::from_string (options["cumulative"]); // Input image. ot.read (); ImageRecRef A (ot.pop()); const ImageBuf &Aib ((*A)()); // Extract bins and height from size. int bins = 0, height = 0; if (sscanf (size.c_str(), "%dx%d", &bins, &height) != 2) { ot.error (command, Strutil::format ("Invalid size: %s", size)); return -1; } // Compute regular histogram. std::vector hist; bool ok = ImageBufAlgo::histogram (Aib, channel, hist, bins); if (! ok) { ot.error (command, Aib.geterror()); return 0; } // Compute cumulative histogram if specified. if (cumulative == 1) for (int i = 1; i < bins; i++) hist[i] += hist[i-1]; // Output image. ImageSpec specR (bins, height, 1, TypeDesc::FLOAT); ot.push (new ImageRec ("irec", specR, ot.imagecache)); ImageBuf &Rib ((*ot.curimg)()); ok = ImageBufAlgo::histogram_draw (Rib, hist); if (! ok) ot.error (command, Rib.geterror()); ot.function_times[command] += timer(); return 0; } static int input_file (int argc, const char *argv[]) { ot.total_readtime.start(); string_view command = ot.express (argv[0]); if (argc > 1 && Strutil::starts_with(command, "-i")) { --argc; ++argv; } else { command = "-i"; } std::map fileoptions; ot.extract_options (fileoptions, command); int printinfo = get_value_override (fileoptions["info"], int(ot.printinfo)); bool readnow = get_value_override (fileoptions["now"], int(0)); bool autocc = get_value_override (fileoptions["autocc"], int(ot.autocc)); for (int i = 0; i < argc; i++) { string_view filename = ot.express(argv[i]); std::map::const_iterator found; found = ot.image_labels.find(filename); if (found != ot.image_labels.end()) { if (ot.debug) std::cout << "Referencing labeled image " << filename << "\n"; ot.push (found->second); ot.process_pending (); break; } Timer timer (ot.enable_function_timing); int exists = 1; // ustring filename (argv[i]); if (ot.input_config_set) { // User has set some input configuration, so seed the cache with // that information. ustring fn (filename); ot.imagecache->invalidate (fn); bool ok = ot.imagecache->add_file (fn, NULL, &ot.input_config); if (!ok) { std::string err = ot.imagecache->geterror(); ot.error ("read", err.size() ? err : "(unknown error)"); exit (1); } } if (! ot.imagecache->get_image_info (ustring(filename), 0, 0, ustring("exists"), TypeDesc::TypeInt, &exists) || !exists) { // Try to get a more precise error message to report ImageInput *input = ImageInput::create (filename); bool procedural = input ? input->supports ("procedural") : false; ImageInput::destroy (input); if (! Filesystem::exists(filename) && !procedural) ot.error ("read", Strutil::format ("File does not exist: \"%s\"", filename)); else { std::string err; ImageInput *in = ImageInput::open (filename); if (in) { err = in->geterror(); in->close (); delete in; } else { err = OIIO::geterror(); } ot.error ("read", err.size() ? err : "(unknown error)"); } exit (1); } if (ot.debug || ot.verbose) std::cout << "Reading " << filename << "\n"; ot.push (ImageRecRef (new ImageRec (filename, ot.imagecache))); ot.curimg->configspec (ot.input_config); if (readnow) { ot.curimg->read (ReadNoCache); } if (printinfo || ot.printstats || ot.dumpdata || ot.hash) { OiioTool::print_info_options pio; pio.verbose = ot.verbose || printinfo > 1; pio.subimages = ot.allsubimages; pio.compute_stats = ot.printstats; pio.dumpdata = ot.dumpdata; pio.dumpdata_showempty = ot.dumpdata_showempty; pio.compute_sha1 = ot.hash; pio.metamatch = ot.printinfo_metamatch; pio.nometamatch = ot.printinfo_nometamatch; long long totalsize = 0; std::string error; bool ok = OiioTool::print_info (ot, filename, pio, totalsize, error); if (! ok) ot.error ("read", error); } ot.function_times["input"] += timer(); if (ot.autoorient) { int action_reorient (int argc, const char *argv[]); const char *argv[] = { "--reorient" }; action_reorient (1, argv); } if (autocc) { // Try to deduce the color space it's in string_view colorspace (ot.colorconfig.parseColorSpaceFromString(filename)); if (colorspace.size() && ot.debug) std::cout << " From " << filename << ", we deduce color space \"" << colorspace << "\"\n"; if (colorspace.empty()) { ot.read (); colorspace = ot.curimg->spec()->get_string_attribute ("oiio:ColorSpace"); if (ot.debug) std::cout << " Metadata of " << filename << " indicates color space \"" << colorspace << "\"\n"; } string_view linearspace = ot.colorconfig.getColorSpaceNameByRole("linear"); if (linearspace.empty()) linearspace = string_view("Linear"); if (colorspace.size() && !Strutil::iequals(colorspace,linearspace)) { const char *argv[] = { "colorconvert", colorspace.c_str(), linearspace.c_str() }; if (ot.debug) std::cout << " Converting " << filename << " from " << colorspace << " to " << linearspace << "\n"; action_colorconvert (3, argv); } } ot.process_pending (); } if (ot.input_config_set) { ot.input_config = ImageSpec(); ot.input_config_set = false; } ot.check_peak_memory (); ot.total_readtime.stop(); return 0; } static void prep_texture_config (ImageSpec &configspec, std::map &fileoptions) { configspec.tile_width = ot.output_tilewidth ? ot.output_tilewidth : 64; configspec.tile_height = ot.output_tileheight ? ot.output_tileheight : 64; configspec.tile_depth = 1; string_view wrap = get_value_override (fileoptions["wrap"], "black"); string_view swrap = get_value_override (fileoptions["swrap"], wrap); string_view twrap = get_value_override (fileoptions["twrap"], wrap); configspec.attribute ("wrapmodes", Strutil::format("%s,%s", swrap, twrap)); configspec.attribute ("maketx:verbose", ot.verbose); configspec.attribute ("maketx:runstats", ot.runstats); configspec.attribute ("maketx:resize", get_value_override(fileoptions["resize"], 0)); configspec.attribute ("maketx:nomipmap", get_value_override(fileoptions["nomipmap"], 0)); configspec.attribute ("maketx:updatemode", get_value_override(fileoptions["updatemode"], 0)); configspec.attribute ("maketx:constant_color_detect", get_value_override(fileoptions["constant_color_detect"], 0)); configspec.attribute ("maketx:monochrome_detect", get_value_override(fileoptions["monochrome_detect"], 0)); configspec.attribute ("maketx:opaque_detect", get_value_override(fileoptions["opaque_detect"], 0)); configspec.attribute ("maketx:compute_average", get_value_override(fileoptions["compute_average"], 1)); configspec.attribute ("maketx:unpremult", get_value_override(fileoptions["unpremult"], 0)); configspec.attribute ("maketx:incolorspace", get_value_override(fileoptions["incolorspace"], "")); configspec.attribute ("maketx:outcolorspace", get_value_override(fileoptions["outcolorspace"], "")); configspec.attribute ("maketx:highlightcomp", get_value_override(fileoptions["highlightcomp"], get_value_override(fileoptions["hilightcomp"], get_value_override(fileoptions["hicomp"], 0)))); configspec.attribute ("maketx:sharpen", get_value_override(fileoptions["sharpen"], 0.0f)); if (fileoptions["filter"].size() || fileoptions["filtername"].size()) configspec.attribute ("maketx:filtername", get_value_override(fileoptions["filtername"], get_value_override(fileoptions["filter"], ""))); if (fileoptions["fileformatname"].size()) configspec.attribute ("maketx:fileformatname", get_value_override(fileoptions["fileformatname"], "")); configspec.attribute ("maketx:prman_metadata", get_value_override(fileoptions["prman_metadata"], 0)); configspec.attribute ("maketx:oiio_options", get_value_override(fileoptions["oiio_options"], get_value_override(fileoptions["oiio"]))); configspec.attribute ("maketx:prman_options", get_value_override(fileoptions["prman_options"], get_value_override(fileoptions["prman"]))); // if (mipimages.size()) // configspec.attribute ("maketx:mipimages", Strutil::join(mipimages,";")); std::string software = configspec.get_string_attribute ("Software"); if (software.size()) configspec.attribute ("maketx:full_command_line", software); } static int output_file (int argc, const char *argv[]) { Timer timer (ot.enable_function_timing); ot.total_writetime.start(); string_view command = ot.express (argv[0]); string_view filename = ot.express (argv[1]); std::map fileoptions; ot.extract_options (fileoptions, command); string_view stripped_command = command; Strutil::parse_char (stripped_command, '-'); Strutil::parse_char (stripped_command, '-'); bool do_tex = Strutil::starts_with (stripped_command, "otex"); bool do_latlong = Strutil::starts_with (stripped_command, "oenv") || Strutil::starts_with (stripped_command, "olatlong"); bool do_shad = Strutil::starts_with (stripped_command, "oshad"); if (ot.debug) std::cout << "Output: " << filename << "\n"; if (! ot.curimg.get()) { ot.warning (command, Strutil::format("%s did not have any current image to output.", filename)); return 0; } if (fileoptions["all"].size()) { // Special case: if they requested outputting all images on the // stack, handle it recursively. The filename, then, is the pattern, // presumed to have a %d in it somewhere, which we will substitute // with the image index. int startnumber = Strutil::from_string(fileoptions["all"]); int nimages = 1 /*curimg*/ + int(ot.image_stack.size()); const char *new_argv[2]; // Git rid of the ":all=" part of the command so we don't infinitely // recurse. std::string newcmd = boost::regex_replace (command.str(), boost::regex(":all=[0-9]+"), ""); new_argv[0] = newcmd.c_str();; ImageRecRef saved_curimg = ot.curimg; // because we'll overwrite it for (int i = 0; i < nimages; ++i) { if (i < nimages-1) ot.curimg = ot.image_stack[i]; else ot.curimg = saved_curimg; // note: last iteration also restores it! // Use the filename as a pattern, format with the frame number new_argv[1] = ustring::format(filename.c_str(), i+startnumber).c_str(); // recurse for this file output_file (2, new_argv); } return 0; } if (ot.noclobber && Filesystem::exists(filename)) { ot.warning (command, Strutil::format("%s already exists, not overwriting.", filename)); return 0; } string_view formatname = fileoptions["fileformatname"]; if (formatname.empty()) formatname = filename; ImageOutput *out = ImageOutput::create (formatname); if (! out) { std::string err = OIIO::geterror(); ot.error (command, err.size() ? err.c_str() : "unknown error creating an ImageOutput"); return 0; } bool supports_displaywindow = out->supports ("displaywindow"); bool supports_negativeorigin = out->supports ("negativeorigin"); bool supports_tiles = out->supports ("tiles") || ot.output_force_tiles; ot.read (); ImageRecRef saveimg = ot.curimg; ImageRecRef ir (ot.curimg); TypeDesc saved_output_dataformat = ot.output_dataformat; int saved_bitspersample = ot.output_bitspersample; timer.stop(); // resume after all these auto-transforms // Automatically drop channels we can't support in output if ((ir->spec()->nchannels > 4 && ! out->supports("nchannels")) || (ir->spec()->nchannels > 3 && ! out->supports("alpha"))) { bool alpha = (ir->spec()->nchannels > 3 && out->supports("alpha")); string_view chanlist = alpha ? "R,G,B,A" : "R,G,B"; std::vector channels; bool found = parse_channels (*ir->spec(), chanlist, channels); if (! found) chanlist = alpha ? "0,1,2,3" : "0,1,2"; const char *argv[] = { "channels", chanlist.c_str() }; int action_channels (int argc, const char *argv[]); // forward decl action_channels (2, argv); ot.warning (command, Strutil::format("Can't save %d channels to %f... saving only %s", ir->spec()->nchannels, out->format_name(), chanlist.c_str())); ir = ot.curimg; } // Handle --autotrim int autotrim = get_value_override (fileoptions["autotrim"], ot.output_autotrim); if (supports_displaywindow && autotrim) { ROI origroi = get_roi(*ir->spec(0,0)); ROI roi = ImageBufAlgo::nonzero_region ((*ir)(0,0), origroi); if (roi.npixels() == 0) { // Special case -- all zero; but doctor to make it 1 zero pixel roi = origroi; roi.xend = roi.xbegin+1; roi.yend = roi.ybegin+1; roi.zend = roi.zbegin+1; } std::string crop = (ir->spec(0,0)->depth == 1) ? format_resolution (roi.width(), roi.height(), roi.xbegin, roi.ybegin) : format_resolution (roi.width(), roi.height(), roi.depth(), roi.xbegin, roi.ybegin, roi.zbegin); const char *argv[] = { "crop", crop.c_str() }; int action_crop (int argc, const char *argv[]); // forward decl action_crop (2, argv); ir = ot.curimg; } // Automatically crop/pad if outputting to a format that doesn't // support display windows, unless autocrop is disabled. int autocrop = get_value_override (fileoptions["autocrop"], ot.output_autocrop); if (! supports_displaywindow && autocrop && (ir->spec()->x != ir->spec()->full_x || ir->spec()->y != ir->spec()->full_y || ir->spec()->width != ir->spec()->full_width || ir->spec()->height != ir->spec()->full_height)) { const char *argv[] = { "croptofull" }; int action_croptofull (int argc, const char *argv[]); // forward decl action_croptofull (1, argv); ir = ot.curimg; } // See if the filename appears to contain a color space name embedded. // Automatically color convert if --autocc is used and the current // color space doesn't match that implied by the filename, and // automatically set -d based on the name if --autod is used. int autocc = get_value_override (fileoptions["autocc"], ot.autocc); string_view outcolorspace = ot.colorconfig.parseColorSpaceFromString(filename); if (autocc && outcolorspace.size()) { TypeDesc type; int bits; type = ot.colorconfig.getColorSpaceDataType(outcolorspace,&bits); if (type.basetype != TypeDesc::UNKNOWN) { if (ot.debug) std::cout << " Deduced data type " << type << " (" << bits << "bits) for output to " << filename << "\n"; if ((ot.output_dataformat && ot.output_dataformat != type) || (bits && ot.output_bitspersample && ot.output_bitspersample != bits)) { std::string msg = Strutil::format ( "Output filename colorspace \"%s\" implies %s (%d bits), overriding prior request for %s.", outcolorspace, type, bits, ot.output_dataformat); ot.warning (command, msg); } ot.output_dataformat = type; ot.output_bitspersample = bits; } } if (autocc) { string_view linearspace = ot.colorconfig.getColorSpaceNameByRole("linear"); if (linearspace.empty()) linearspace = string_view("Linear"); string_view currentspace = ir->spec()->get_string_attribute ("oiio:ColorSpace", linearspace); // Special cases where we know formats should be particular color // spaces if (outcolorspace.empty() && (Strutil::iends_with (filename, ".jpg") || Strutil::iends_with (filename, ".jpeg") || Strutil::iends_with (filename, ".gif"))) outcolorspace = string_view("sRGB"); if (outcolorspace.size() && currentspace != outcolorspace) { if (ot.debug) std::cout << " Converting from " << currentspace << " to " << outcolorspace << " for output to " << filename << "\n"; const char *argv[] = { "colorconvert", currentspace.c_str(), outcolorspace.c_str() }; action_colorconvert (3, argv); ir = ot.curimg; } } // Automatically crop out the negative areas if outputting to a format // that doesn't support negative origins. if (! supports_negativeorigin && autocrop && (ir->spec()->x < 0 || ir->spec()->y < 0 || ir->spec()->z < 0)) { ROI roi = get_roi (*ir->spec(0,0)); roi.xbegin = std::max (0, roi.xbegin); roi.ybegin = std::max (0, roi.ybegin); roi.zbegin = std::max (0, roi.zbegin); std::string crop = (ir->spec(0,0)->depth == 1) ? format_resolution (roi.width(), roi.height(), roi.xbegin, roi.ybegin) : format_resolution (roi.width(), roi.height(), roi.depth(), roi.xbegin, roi.ybegin, roi.zbegin); const char *argv[] = { "crop", crop.c_str() }; int action_crop (int argc, const char *argv[]); // forward decl action_crop (2, argv); ir = ot.curimg; } if (ot.dryrun) { ot.curimg = saveimg; ot.output_dataformat = saved_output_dataformat; ot.output_bitspersample = saved_bitspersample; return 0; } timer.start(); if (ot.debug || ot.verbose) std::cout << "Writing " << filename << "\n"; // FIXME -- the various automatic transformations above neglect to handle // MIPmaps or subimages with full generality. bool ok = true; if (do_tex || do_latlong) { ImageSpec configspec; adjust_output_options (filename, configspec, ot, supports_tiles, fileoptions); prep_texture_config (configspec, fileoptions); ImageBufAlgo::MakeTextureMode mode = ImageBufAlgo::MakeTxTexture; if (do_shad) mode = ImageBufAlgo::MakeTxShadow; if (do_latlong) mode = ImageBufAlgo::MakeTxEnvLatl; // if (lightprobemode) // mode = ImageBufAlgo::MakeTxEnvLatlFromLightProbe; ok = ImageBufAlgo::make_texture (mode, (*ir)(0,0), filename, configspec, &std::cout); if (!ok) ot.error (command, "Could not make texture"); } else { // Non-texture case std::vector subimagespecs (ir->subimages()); for (int s = 0; s < ir->subimages(); ++s) { ImageSpec spec = *ir->spec(s,0); adjust_output_options (filename, spec, ot, supports_tiles, fileoptions); // For deep files, must copy the native deep channelformats if (spec.deep) spec.channelformats = (*ir)(s,0).nativespec().channelformats; // If it's not tiled and MIP-mapped, remove any "textureformat" if (! spec.tile_pixels() || ir->miplevels(s) <= 1) spec.erase_attribute ("textureformat"); subimagespecs[s] = spec; } // Do the initial open ImageOutput::OpenMode mode = ImageOutput::Create; if (ir->subimages() > 1 && out->supports("multiimage")) { if (! out->open (filename, ir->subimages(), &subimagespecs[0])) { std::string err = out->geterror(); ot.error (command, err.size() ? err.c_str() : "unknown error"); return 0; } } else { if (! out->open (filename, subimagespecs[0], mode)) { std::string err = out->geterror(); ot.error (command, err.size() ? err.c_str() : "unknown error"); return 0; } } // Output all the subimages and MIP levels for (int s = 0, send = ir->subimages(); s < send; ++s) { for (int m = 0, mend = ir->miplevels(s); m < mend && ok; ++m) { ImageSpec spec = *ir->spec(s,m); adjust_output_options (filename, spec, ot, supports_tiles, fileoptions); if (s > 0 || m > 0) { // already opened first subimage/level if (! out->open (filename, spec, mode)) { std::string err = out->geterror(); ot.error (command, err.size() ? err.c_str() : "unknown error"); ok = false; break; } } if (! (*ir)(s,m).write (out)) { ot.error (command, (*ir)(s,m).geterror()); ok = false; break; } ot.check_peak_memory(); if (mend > 1) { if (out->supports("mipmap")) { mode = ImageOutput::AppendMIPLevel; // for next level } else if (out->supports("multiimage")) { mode = ImageOutput::AppendSubimage; } else { ot.warning (command, Strutil::format ("%s does not support MIP-maps for %s", out->format_name(), filename)); break; } } } mode = ImageOutput::AppendSubimage; // for next subimage if (send > 1 && ! out->supports("multiimage")) { ot.warning (command, Strutil::format ("%s does not support multiple subimages for %s", out->format_name(), filename)); break; } } out->close (); } delete out; if (ot.output_adjust_time && ok) { std::string metadatatime = ir->spec(0,0)->get_string_attribute ("DateTime"); std::time_t in_time = ir->time(); if (! metadatatime.empty()) DateTime_to_time_t (metadatatime.c_str(), in_time); Filesystem::last_write_time (filename, in_time); } ot.check_peak_memory(); ot.curimg = saveimg; ot.output_dataformat = saved_output_dataformat; ot.output_bitspersample = saved_bitspersample; ot.curimg->was_output (true); ot.total_writetime.stop(); ot.function_times[command] += timer(); ot.num_outputs += 1; return 0; } static int do_echo (int argc, const char *argv[]) { ASSERT (argc == 2); string_view command = ot.express (argv[0]); string_view message = ot.express (argv[1]); std::map options; options["newline"] = "1"; ot.extract_options (options, command); int newline = Strutil::from_string(options["newline"]); std::cout << message; for (int i = 0; i < newline; ++i) std::cout << '\n'; // ot.printed_info = true; return 0; } // Concatenate the command line into one string, optionally filtering out // verbose attribute commands. static std::string command_line_string (int argc, char * argv[], bool sansattrib) { std::string s; for (int i = 0; i < argc; ++i) { if (sansattrib) { // skip any filtered attributes if (!strcmp(argv[i], "--attrib") || !strcmp(argv[i], "-attrib") || !strcmp(argv[i], "--sattrib") || !strcmp(argv[i], "-sattrib")) { i += 2; // also skip the following arguments continue; } if (!strcmp(argv[i], "--sansattrib") || !strcmp(argv[i], "-sansattrib")) { continue; } } if (strchr (argv[i], ' ')) { // double quote args with spaces s += '\"'; s += argv[i]; s += '\"'; } else { s += argv[i]; } if (i < argc-1) s += ' '; } return s; } static void print_help (ArgParse &ap) { ap.usage (); std::cout << "\n"; int columns = Sysutil::terminal_columns() - 2; { std::stringstream s; s << "Image formats supported: "; std::string format_list; OIIO::getattribute ("format_list", format_list); std::vector formats; Strutil::split (format_list, formats, ","); std::sort (formats.begin(), formats.end()); format_list = Strutil::join (formats, ", "); s << format_list; std::cout << Strutil::wordwrap(s.str(), columns, 4) << "\n"; } // debugging color space names std::stringstream s; s << "Known color spaces: "; const char *linear = ot.colorconfig.getColorSpaceNameByRole("linear"); for (int i = 0, e = ot.colorconfig.getNumColorSpaces(); i < e; ++i) { const char *n = ot.colorconfig.getColorSpaceNameByIndex(i); s << "\"" << n << "\""; if (linear && !Strutil::iequals(n,"linear") && Strutil::iequals (n, linear)) s << " (linear)"; if (i < e-1) s << ", "; } std::cout << Strutil::wordwrap(s.str(), columns, 4) << "\n"; int nlooks = ot.colorconfig.getNumLooks(); if (nlooks) { std::stringstream s; s << "Known looks: "; for (int i = 0; i < nlooks; ++i) { const char *n = ot.colorconfig.getLookNameByIndex(i); s << "\"" << n << "\""; if (i < nlooks-1) s << ", "; } std::cout << Strutil::wordwrap(s.str(), columns, 4) << "\n"; } const char *default_display = ot.colorconfig.getDefaultDisplayName(); int ndisplays = ot.colorconfig.getNumDisplays(); if (ndisplays) { std::stringstream s; s << "Known displays: "; for (int i = 0; i < ndisplays; ++i) { const char *d = ot.colorconfig.getDisplayNameByIndex(i); s << "\"" << d << "\""; if (! strcmp(d, default_display)) s << "*"; const char *default_view = ot.colorconfig.getDefaultViewName(d); int nviews = ot.colorconfig.getNumViews(d); if (nviews) { s << " (views: "; for (int i = 0; i < nviews; ++i) { const char *v = ot.colorconfig.getViewNameByIndex(d, i); s << "\"" << v << "\""; if (! strcmp(v, default_view)) s << "*"; if (i < nviews-1) s << ", "; } s << ")"; } if (i < ndisplays-1) s << ", "; } s << " (* = default)"; std::cout << Strutil::wordwrap(s.str(), columns, 4) << "\n"; } if (! ot.colorconfig.supportsOpenColorIO()) std::cout << "No OpenColorIO support was enabled at build time.\n"; std::string libs = OIIO::get_string_attribute("library_list"); if (libs.size()) { std::vector libvec; Strutil::split (libs, libvec, ";"); for (size_t i = 0; i < libvec.size(); ++i) { size_t pos = libvec[i].find(':'); libvec[i].remove_prefix (pos+1); } std::cout << "Dependent libraries:\n " << Strutil::wordwrap(Strutil::join (libvec, ", "), columns, 4) << std::endl; } } static void getargs (int argc, char *argv[]) { bool help = false; bool sansattrib = false; for (int i = 0; i < argc; ++i) if (!strcmp(argv[i],"--sansattrib") || !strcmp(argv[i],"-sansattrib")) sansattrib = true; ot.full_command_line = command_line_string (argc, argv, sansattrib); ArgParse ap (argc, (const char **)argv); ap.options ("oiiotool -- simple image processing operations\n" OIIO_INTRO_STRING "\n" "Usage: oiiotool [filename,option,action]...\n", "%*", input_file, "", "", "Options (general):", "--help", &help, "Print help message", "-v", &ot.verbose, "Verbose status messages", "-q %!", &ot.verbose, "Quiet mode (turn verbose off)", "-n", &ot.dryrun, "No saved output (dry run)", "-a", &ot.allsubimages, "Do operations on all subimages/miplevels", "--debug", &ot.debug, "Debug mode", "--runstats", &ot.runstats, "Print runtime statistics", "--info", &ot.printinfo, "Print resolution and metadata on all inputs", "--echo %@ %s", do_echo, NULL, "Echo message to console (options: newline=0)", "--metamatch %s", &ot.printinfo_metamatch, "Regex: which metadata is printed with -info -v", "--no-metamatch %s", &ot.printinfo_nometamatch, "Regex: which metadata is excluded with -info -v", "--stats", &ot.printstats, "Print pixel statistics on all inputs", "--dumpdata %@", set_dumpdata, NULL, "Print all pixel data values (options: empty=0)", "--hash", &ot.hash, "Print SHA-1 hash of each input image", "--colorcount %@ %s", action_colorcount, NULL, "Count of how many pixels have the given color (argument: color;color;...) (options: eps=color)", "--rangecheck %@ %s %s", action_rangecheck, NULL, NULL, "Count of how many pixels are outside the low and high color arguments (each is a comma-separated color value list)", // "-u", &ot.updatemode, "Update mode: skip outputs when the file exists and is newer than all inputs", "--no-clobber", &ot.noclobber, "Do not overwrite existing files", "--noclobber", &ot.noclobber, "", // synonym "--threads %@ %d", set_threads, NULL, "Number of threads (default 0 == #cores)", "--frames %s", NULL, "Frame range for '#' or printf-style wildcards", "--framepadding %d", &ot.frame_padding, "Frame number padding digits (ignored when using printf-style wildcards)", "--views %s", NULL, "Views for %V/%v wildcards (comma-separated, defaults to left,right)", "--wildcardoff", NULL, "Disable numeric wildcard expansion for subsequent command line arguments", "--wildcardon", NULL, "Enable numeric wildcard expansion for subsequent command line arguments", "--no-autopremult %@", unset_autopremult, NULL, "Turn off automatic premultiplication of images with unassociated alpha", "--autopremult %@", set_autopremult, NULL, "Turn on automatic premultiplication of images with unassociated alpha", "--autoorient", &ot.autoorient, "Automatically --reorient all images upon input", "--auto-orient", &ot.autoorient, "", // symonym for --autoorient "--autocc", &ot.autocc, "Automatically color convert based on filename", "--noautocc %!", &ot.autocc, "Turn off automatic color conversion", "--native %@", set_native, &ot.nativeread, "Keep native pixel data type (bypass cache if necessary)", "--cache %@ %d", set_cachesize, &ot.cachesize, "ImageCache size (in MB: default=4096)", "--autotile %@ %d", set_autotile, &ot.autotile, "Autotile size for cached images (default=4096)", "", "Commands that read images:", "-i %@ %s", input_file, NULL, "Input file (argument: filename) (options: now=0:printinfo=0:autocc=0)", "--iconfig %@ %s %s", set_input_attribute, NULL, NULL, "Sets input config attribute (name, value) (options: type=...)", "", "Commands that write images:", "-o %@ %s", output_file, NULL, "Output the current image to the named file", "-otex %@ %s", output_file, NULL, "Output the current image as a texture", "-oenv %@ %s", output_file, NULL, "Output the current image as a latlong env map", "", "Options that affect subsequent image output:", "-d %@ %s", set_dataformat, NULL, "'-d TYPE' sets the output data format of all channels, " "'-d CHAN=TYPE' overrides a single named channel (multiple -d args are allowed). " "Data types include: uint8, sint8, uint10, uint12, uint16, sint16, uint32, sint32, half, float, double", "--scanline", &ot.output_scanline, "Output scanline images", "--tile %@ %d %d", output_tiles, &ot.output_tilewidth, &ot.output_tileheight, "Output tiled images (tilewidth, tileheight)", "--force-tiles", &ot.output_force_tiles, "", // undocumented "--compression %s", &ot.output_compression, "Set the compression method", "--quality %d", &ot.output_quality, "Set the compression quality, 1-100", "--dither", &ot.output_dither, "Add dither to 8-bit output", "--planarconfig %s", &ot.output_planarconfig, "Force planarconfig (contig, separate, default)", "--adjust-time", &ot.output_adjust_time, "Adjust file times to match DateTime metadata", "--noautocrop %!", &ot.output_autocrop, "Do not automatically crop images whose formats don't support separate pixel data and full/display windows", "--autotrim", &ot.output_autotrim, "Automatically trim black borders upon output to file formats that support separate pixel data and full/display windows", "", "Options that change current image metadata (but not pixel values):", "--attrib %@ %s %s", set_any_attribute, NULL, NULL, "Sets metadata attribute (name, value) (options: type=...)", "--sattrib %@ %s %s", set_string_attribute, NULL, NULL, "Sets string metadata attribute (name, value)", "--caption %@ %s", set_caption, NULL, "Sets caption (ImageDescription metadata)", "--keyword %@ %s", set_keyword, NULL, "Add a keyword", "--clear-keywords %@", clear_keywords, NULL, "Clear all keywords", "--nosoftwareattrib", &ot.metadata_nosoftwareattrib, "Do not write command line into Exif:ImageHistory, Software metadata attributes", "--sansattrib", &sansattrib, "Write command line into Software & ImageHistory but remove --sattrib and --attrib options", "--orientation %@ %d", set_orientation, NULL, "Set the assumed orientation", "--orientcw %@", rotate_orientation, NULL, "Rotate orientation metadata 90 deg clockwise", "--orientccw %@", rotate_orientation, NULL, "Rotate orientation metadata 90 deg counter-clockwise", "--orient180 %@", rotate_orientation, NULL, "Rotate orientation metadata 180 deg", "--rotcw %@", rotate_orientation, NULL, "", // DEPRECATED(1.5), back compatibility "--rotccw %@", rotate_orientation, NULL, "", // DEPRECATED(1.5), back compatibility "--rot180 %@", rotate_orientation, NULL, "", // DEPRECATED(1.5), back compatibility "--origin %@ %s", set_origin, NULL, "Set the pixel data window origin (e.g. +20+10)", "--fullsize %@ %s", set_fullsize, NULL, "Set the display window (e.g., 1920x1080, 1024x768+100+0, -20-30)", "--fullpixels %@", set_full_to_pixels, NULL, "Set the 'full' image range to be the pixel data window", "--chnames %@ %s", set_channelnames, NULL, "Set the channel names (comma-separated)", "", "Options that affect subsequent actions:", "--fail %g", &ot.diff_failthresh, "Failure threshold difference (0.000001)", "--failpercent %g", &ot.diff_failpercent, "Allow this percentage of failures in diff (0)", "--hardfail %g", &ot.diff_hardfail, "Fail diff if any one pixel exceeds this error (infinity)", "--warn %g", &ot.diff_warnthresh, "Warning threshold difference (0.00001)", "--warnpercent %g", &ot.diff_warnpercent, "Allow this percentage of warnings in diff (0)", "--hardwarn %g", &ot.diff_hardwarn, "Warn if any one pixel difference exceeds this error (infinity)", "", "Actions:", "--create %@ %s %d", action_create, NULL, NULL, "Create a blank image (args: geom, channels)", "--pattern %@ %s %s %d", action_pattern, NULL, NULL, NULL, "Create a patterned image (args: pattern, geom, channels). Patterns: black, fill, checker, noise", "--kernel %@ %s %s", action_kernel, NULL, NULL, "Create a centered convolution kernel (args: name, geom)", "--capture %@", action_capture, NULL, "Capture an image (options: camera=%d)", "--diff %@", action_diff, NULL, "Print report on the difference of two images (modified by --fail, --failpercent, --hardfail, --warn, --warnpercent --hardwarn)", "--pdiff %@", action_pdiff, NULL, "Print report on the perceptual difference of two images (modified by --fail, --failpercent, --hardfail, --warn, --warnpercent --hardwarn)", "--add %@", action_add, NULL, "Add two images", "--addc %s %@", action_addc, NULL, "Add to all channels a scalar or per-channel constants (e.g.: 0.5 or 1,1.25,0.5)", "--cadd %s %@", action_addc, NULL, "", // Deprecated synonym "--sub %@", action_sub, NULL, "Subtract two images", "--subc %s %@", action_subc, NULL, "Subtract from all channels a scalar or per-channel constants (e.g.: 0.5 or 1,1.25,0.5)", "--csub %s %@", action_subc, NULL, "", // Deprecated synonym "--mul %@", action_mul, NULL, "Multiply two images", "--mulc %s %@", action_mulc, NULL, "Multiply the image values by a scalar or per-channel constants (e.g.: 0.5 or 1,1.25,0.5)", "--cmul %s %@", action_mulc, NULL, "", // Deprecated synonym "--div %@", action_div, NULL, "Divide first image by second image", "--divc %s %@", action_divc, NULL, "Divide the image values by a scalar or per-channel constants (e.g.: 0.5 or 1,1.25,0.5)", "--mad %@", action_mad, NULL, "Multiply two images, add a third", "--invert %@", action_invert, NULL, "Take the color inverse (subtract from 1)", "--abs %@", action_abs, NULL, "Take the absolute value of the image pixels", "--absdiff %@", action_absdiff, NULL, "Absolute difference between two images", "--absdiffc %s %@", action_absdiffc, NULL, "Absolute difference versus a scalar or per-channel constant (e.g.: 0.5 or 1,1.25,0.5)", "--powc %s %@", action_powc, NULL, "Raise the image values to a scalar or per-channel power (e.g.: 2.2 or 2.2,2.2,2.2,1.0)", "--cpow %s %@", action_powc, NULL, "", // Depcrcated synonym "--noise %@", action_noise, NULL, "Add noise to an image (options: type=gaussian:mean=0:stddev=0.1, type=uniform:min=0:max=0.1, type=salt:value=0:portion=0.1, seed=0", "--chsum %@", action_chsum, NULL, "Turn into 1-channel image by summing channels (options: weight=r,g,...)", "--crop %@ %s", action_crop, NULL, "Set pixel data resolution and offset, cropping or padding if necessary (WxH+X+Y or xmin,ymin,xmax,ymax)", "--croptofull %@", action_croptofull, NULL, "Crop or pad to make pixel data region match the \"full\" region", "--trim %@", action_trim, NULL, "Crop to the minimal ROI containing nonzero pixel values", "--cut %@ %s", action_cut, NULL, "Cut out the ROI and reposition to the origin (WxH+X+Y or xmin,ymin,xmax,ymax)", "--paste %@ %s", action_paste, NULL, "Paste fg over bg at the given position (e.g., +100+50)", "--mosaic %@ %s", action_mosaic, NULL, "Assemble images into a mosaic (arg: WxH; options: pad=0)", "--over %@", action_over, NULL, "'Over' composite of two images", "--zover %@", action_zover, NULL, "Depth composite two images with Z channels (options: zeroisinf=%d)", "--deepmerge %@", action_deepmerge, NULL, "Merge/composite two deep images", "--histogram %@ %s %d", action_histogram, NULL, NULL, "Histogram one channel (options: cumulative=0)", "--rotate90 %@", action_rotate90, NULL, "Rotate the image 90 degrees clockwise", "--rotate180 %@", action_rotate180, NULL, "Rotate the image 180 degrees", "--flipflop %@", action_rotate180, NULL, "", // Deprecated synonym for --rotate180 "--rotate270 %@", action_rotate270, NULL, "Rotate the image 270 degrees clockwise (or 90 degrees CCW)", "--flip %@", action_flip, NULL, "Flip the image vertically (top<->bottom)", "--flop %@", action_flop, NULL, "Flop the image horizontally (left<->right)", "--reorient %@", action_reorient, NULL, "Rotate and/or flop the image to transform the pixels to match the Orientation metadata", "--transpose %@", action_transpose, NULL, "Transpose the image", "--cshift %@ %s", action_cshift, NULL, "Circular shift the image (e.g.: +20-10)", "--resample %@ %s", action_resample, NULL, "Resample (640x480, 50%)", "--resize %@ %s", action_resize, NULL, "Resize (640x480, 50%) (options: filter=%s)", "--fit %@ %s", action_fit, NULL, "Resize to fit within a window size (options: filter=%s, pad=%d)", "--pixelaspect %@ %g", action_pixelaspect, NULL, "Scale up the image's width or height to match the given pixel aspect ratio (options: filter=%s)", "--rotate %@ %g", action_rotate, NULL, "Rotate pixels (argument is degrees clockwise) around the center of the display window (options: filter=%s, center=%f,%f, recompute_roi=%d", "--warp %@ %s", action_warp, NULL, "Warp pixels (argument is a 3x3 matrix, separated by commas) (options: filter=%s, recompute_roi=%d)", "--convolve %@", action_convolve, NULL, "Convolve with a kernel", "--blur %@ %s", action_blur, NULL, "Blur the image (arg: WxH; options: kernel=name)", "--median %@ %s", action_median, NULL, "Median filter the image (arg: WxH)", "--dilate %@ %s", action_dilate, NULL, "Dilate (area maximum) the image (arg: WxH)", "--erode %@ %s", action_erode, NULL, "Erode (area minimum) the image (arg: WxH)", "--unsharp %@", action_unsharp, NULL, "Unsharp mask (options: kernel=gaussian, width=3, contrast=1, threshold=0)", "--laplacian %@", action_laplacian, NULL, "Laplacian filter the image", "--fft %@", action_fft, NULL, "Take the FFT of the image", "--ifft %@", action_ifft, NULL, "Take the inverse FFT of the image", "--polar %@", action_polar, NULL, "Convert complex (real,imag) to polar (amplitude,phase)", "--unpolar %@", action_unpolar, NULL, "Convert polar (amplitude,phase) to complex (real,imag)", "--fixnan %@ %s", action_fixnan, NULL, "Fix NaN/Inf values in the image (options: none, black, box3, error)", "--fillholes %@", action_fillholes, NULL, "Fill in holes (where alpha is not 1)", "--clamp %@", action_clamp, NULL, "Clamp values (options: min=..., max=..., clampalpha=0)", "--rangecompress %@", action_rangecompress, NULL, "Compress the range of pixel values with a log scale (options: luma=0|1)", "--rangeexpand %@", action_rangeexpand, NULL, "Un-rangecompress pixel values back to a linear scale (options: luma=0|1)", "--line %@ %s", action_line, NULL, "Render a poly-line (args: x1,y1,x2,y2... ; options: color=)", "--box %@ %s", action_box, NULL, "Render a box (args: x1,y1,x2,y2 ; options: color=)", "--fill %@ %s", action_fill, NULL, "Fill a region (options: color=)", "--text %@ %s", action_text, NULL, "Render text into the current image (options: x=, y=, size=, color=)", // "--noise_uniform %@", action_noise_uniform, NULL, "Add uniform noise to the image (options: min=, max=)", // "--noise_gaussian %@", action_noise_gaussian, NULL, "Add Gaussian noise to the image (options: mean=, stddev=)", // "--noise_salt %@", action_noise_saltpepp, NULL, "Add 'salt & pepper' noise to the image (options: min=, max=)", "", "Manipulating channels or subimages:", "--ch %@ %s", action_channels, NULL, "Select or shuffle channels (e.g., \"R,G,B\", \"B,G,R\", \"2,3,4\")", "--chappend %@", action_chappend, NULL, "Append the channels of the last two images", "--unmip %@", action_unmip, NULL, "Discard all but the top level of a MIPmap", "--selectmip %@ %d", action_selectmip, NULL, "Select just one MIP level (0 = highest res)", "--subimage %@ %s", action_select_subimage, NULL, "Select just one subimage (by index or name)", "--sisplit %@", action_subimage_split, NULL, "Split the top image's subimges into separate images", "--siappend %@", action_subimage_append, NULL, "Append the last two images into one multi-subimage image", "--siappendall %@", action_subimage_append_all, NULL, "Append all images on the stack into a single multi-subimage image", "--deepen %@", action_deepen, NULL, "Deepen normal 2D image to deep", "--flatten %@", action_flatten, NULL, "Flatten deep image to non-deep", "", "Image stack manipulation:", "--dup %@", action_dup, NULL, "Duplicate the current image (push a copy onto the stack)", "--swap %@", action_swap, NULL, "Swap the top two images on the stack.", "--pop %@", action_pop, NULL, "Throw away the current image", "--label %@ %s", action_label, NULL, "Label the top image", "", "Color management:", "--colorconfig %@ %s", set_colorconfig, NULL, "Explicitly specify an OCIO configuration file", "--iscolorspace %@ %s", set_colorspace, NULL, "Set the assumed color space (without altering pixels)", "--tocolorspace %@ %s", action_tocolorspace, NULL, "Convert the current image's pixels to a named color space", "--colorconvert %@ %s %s", action_colorconvert, NULL, NULL, "Convert pixels from 'src' to 'dst' color space (options: key=, value=)", "--ociolook %@ %s", action_ociolook, NULL, "Apply the named OCIO look (options: from=, to=, inverse=, key=, value=)", "--ociodisplay %@ %s %s", action_ociodisplay, NULL, NULL, "Apply the named OCIO display and view (options: from=, looks=, key=, value=)", "--ociofiletransform %@ %s", action_ociofiletransform, NULL, "Apply the named OCIO filetransform (options: inverse=)", "--unpremult %@", action_unpremult, NULL, "Divide all color channels of the current image by the alpha to \"un-premultiply\"", "--premult %@", action_premult, NULL, "Multiply all color channels of the current image by the alpha", NULL); if (ap.parse(argc, (const char**)argv) < 0) { std::cerr << ap.geterror() << std::endl; print_help (ap); exit (EXIT_FAILURE); } if (help) { print_help (ap); exit (EXIT_SUCCESS); } if (argc <= 1) { ap.briefusage (); std::cout << "\nFor detailed help: oiiotool --help\n"; exit (EXIT_SUCCESS); } } // Check if any of the command line arguments contains numeric ranges or // wildcards. If not, just return 'false'. But if they do, the // remainder of processing will happen here (and return 'true'). static bool handle_sequence (int argc, const char **argv) { Timer totaltime; // First, scan the original command line arguments for '#', '@', '%0Nd', // '%v' or '%V' characters. Any found indicate that there are numeric // range or wildcards to deal with. Also look for --frames, // --framepadding and --views options. #define ONERANGE_SPEC "[0-9]+(-[0-9]+((x|y)-?[0-9]+)?)?" #define MANYRANGE_SPEC ONERANGE_SPEC "(," ONERANGE_SPEC ")*" #define VIEW_SPEC "%[Vv]" #define SEQUENCE_SPEC "((" MANYRANGE_SPEC ")?" "((#|@)+|(%[0-9]*d)))" "|" "(" VIEW_SPEC ")" static boost::regex sequence_re (SEQUENCE_SPEC); std::string framespec = ""; static const char *default_views = "left,right"; std::vector views; Strutil::split (default_views, views, ","); int framepadding = 0; std::vector sequence_args; // Args with sequence numbers std::vector sequence_is_output; bool is_sequence = false; bool wildcard_on = true; for (int a = 1; a < argc; ++a) { bool is_output = false; bool is_output_all = false; if (Strutil::starts_with (argv[a], "-o") && a < argc-1) { is_output = true; if (Strutil::contains (argv[a], ":all=")) { // skip wildcard expansion for -o:all, because the name // will be a pattern for expansion of the subimage number. is_output_all = true; } a++; } std::string strarg (argv[a]); boost::match_results range_match; if (strarg == "--debug" || strarg == "-debug") ot.debug = true; else if ((strarg == "--frames" || strarg == "-frames") && a < argc-1) { framespec = argv[++a]; } else if ((strarg == "--framepadding" || strarg == "-framepadding") && a < argc-1) { int f = atoi (argv[++a]); if (f >= 1 && f < 10) framepadding = f; } else if ((strarg == "--views" || strarg == "-views") && a < argc-1) { Strutil::split (argv[++a], views, ","); } else if (strarg == "--wildcardoff" || strarg == "-wildcardoff") { wildcard_on = false; } else if (strarg == "--wildcardon" || strarg == "-wildcardon") { wildcard_on = true; } else if (wildcard_on && !is_output_all && boost::regex_search (strarg, range_match, sequence_re)) { is_sequence = true; sequence_args.push_back (a); sequence_is_output.push_back (is_output); } } // No ranges or wildcards? if (! is_sequence) return false; // For each of the arguments that contains a wildcard, get a normalized // pattern in printf style (e.g. "foo.%04d.exr"). Next, either expand the // frame pattern to a list of frame numbers and use enumerate_file_sequence // to fully elaborate all the filenames in the sequence, or if no frame // range was specified, scan the filesystem for matching frames. Output // sequences without explicit frame ranges inherit the frame numbers of // the first input sequence. It's an error if the sequences are not all // of the same length. std::vector< std::vector > filenames (argc+1); std::vector< std::vector > frame_numbers (argc+1); std::vector< std::vector > frame_views (argc+1); std::string normalized_pattern, sequence_framespec; size_t nfilenames = 0; bool result; for (size_t i = 0; i < sequence_args.size(); ++i) { int a = sequence_args[i]; result = Filesystem::parse_pattern (argv[a], framepadding, normalized_pattern, sequence_framespec); if (! result) { ot.error (Strutil::format("Could not parse pattern: %s", argv[a]), ""); return true; } if (sequence_framespec.empty()) sequence_framespec = framespec; if (! sequence_framespec.empty()) { Filesystem::enumerate_sequence (sequence_framespec.c_str(), frame_numbers[a]); Filesystem::enumerate_file_sequence (normalized_pattern, frame_numbers[a], frame_views[a], filenames[a]); } else if (sequence_is_output[i]) { // use frame numbers from first sequence Filesystem::enumerate_file_sequence (normalized_pattern, frame_numbers[sequence_args[0]], frame_views[sequence_args[0]], filenames[a]); } else if (! sequence_is_output[i]) { result = Filesystem::scan_for_matching_filenames (normalized_pattern, views, frame_numbers[a], frame_views[a], filenames[a]); if (! result) { ot.error (Strutil::format("No filenames found matching pattern: \"%s\" (did you intend to use --wildcardoff?)", argv[a]), ""); return true; } } if (i == 0) { nfilenames = filenames[a].size(); } else if (nfilenames != filenames[a].size()) { ot.error (Strutil::format("Not all sequence specifications matched: %s (%d frames) vs. %s (%d frames)", argv[sequence_args[0]], nfilenames, argv[a], filenames[a].size()), ""); return true; } } // OK, now we just call getargs once for each item in the sequences, // substituting the i-th sequence entry for its respective argument // every time. // Note: nfilenames really means, number of frame number iterations. std::vector seq_argv (argv, argv+argc+1); for (size_t i = 0; i < nfilenames; ++i) { if (ot.debug) std::cout << "SEQUENCE " << i << "\n"; for (size_t j = 0; j < sequence_args.size(); ++j) { size_t a = sequence_args[j]; seq_argv[a] = filenames[a][i].c_str(); if (ot.debug) std::cout << " " << argv[a] << " -> " << seq_argv[a] << "\n"; } ot.clear_options (); // Careful to reset all command line options! ot.frame_number = frame_numbers[sequence_args[0]][i]; getargs (argc, (char **)&seq_argv[0]); ot.process_pending (); if (ot.pending_callback()) ot.warning (Strutil::format ("pending '%s' command never executed", ot.pending_callback_name())); // Clear the stack at the end of each iteration ot.curimg.reset (); ot.image_stack.clear(); if (ot.runstats) std::cout << "End iteration " << i << ": " << Strutil::timeintervalformat(totaltime(),2) << " " << Strutil::memformat(Sysutil::memory_used()) << "\n"; if (ot.debug) std::cout << "\n"; } return true; } int main (int argc, char *argv[]) { #if OIIO_MSVS_BEFORE_2015 // When older Visual Studio is used, float values in scientific foramt // are printed with three digit exponent. We change this behaviour to // fit Linux way. _set_output_format (_TWO_DIGIT_EXPONENT); #endif Timer totaltime; ot.imagecache = ImageCache::create (false); ASSERT (ot.imagecache); ot.imagecache->attribute ("forcefloat", 1); ot.imagecache->attribute ("max_memory_MB", float(ot.cachesize)); ot.imagecache->attribute ("autotile", ot.autotile); if (ot.autotile) ot.imagecache->attribute ("autoscanline", 1); Filesystem::convert_native_arguments (argc, (const char **)argv); if (handle_sequence (argc, (const char **)argv)) { // Deal with sequence } else { // Not a sequence getargs (argc, argv); ot.process_pending (); if (ot.pending_callback()) ot.warning (Strutil::format ("pending '%s' command never executed", ot.pending_callback_name())); } if (!ot.printinfo && !ot.printstats && !ot.dumpdata && !ot.dryrun) { if (ot.curimg && !ot.curimg->was_output() && (ot.curimg->metadata_modified() || ot.curimg->pixels_modified())) ot.warning ("modified images without outputting them. Did you forget -o?"); else if (ot.num_outputs == 0) ot.warning ("oiiotool produced no output. Did you forget -o?"); } if (ot.runstats) { double total_time = totaltime(); double unaccounted = total_time; std::cout << "\n"; int threads = -1; OIIO::getattribute ("threads", threads); std::cout << "Threads: " << threads << "\n"; std::cout << "oiiotool runtime statistics:\n"; std::cout << " Total time: " << Strutil::timeintervalformat(total_time,2) << "\n"; static const char *timeformat = " %-12s : %5.2f\n"; for (Oiiotool::TimingMap::const_iterator func = ot.function_times.begin(); func != ot.function_times.end(); ++func) { double t = func->second; std::cout << Strutil::format (timeformat, func->first, t); unaccounted -= t; } std::cout << Strutil::format (timeformat, "unaccounted", std::max(unaccounted, 0.0)); ot.check_peak_memory (); std::cout << " Peak memory: " << Strutil::memformat(ot.peak_memory) << "\n"; std::cout << " Current memory: " << Strutil::memformat(Sysutil::memory_used()) << "\n"; std::cout << "\n" << ot.imagecache->getstats(2) << "\n"; } return ot.return_value; } openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/0000755000175000017500000000000013151711064017320 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/dpx.imageio/CMakeLists.txt0000644000175000017500000000046213151711064022062 0ustar mfvmfvadd_oiio_plugin (dpxinput.cpp dpxoutput.cpp libdpx/DPX.cpp libdpx/OutStream.cpp libdpx/RunLengthEncoding.cpp libdpx/Codec.cpp libdpx/Reader.cpp libdpx/Writer.cpp libdpx/DPXHeader.cpp libdpx/ElementReadStream.cpp libdpx/InStream.cpp libdpx/DPXColorConverter.cpp LINK_LIBRARIES ${OPENEXR_LIBRARIES}) openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/0000755000175000017500000000000013151711064020602 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/DPXColorConverter.h0000644000175000017500000001511613151711064024301 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _DPX_COLORCONVERTER_H #define _DPX_COLORCONVERTER_H 1 #include "DPX.h" namespace dpx { // convert between all of the various pixel formats and RGB in a controllable way /*! * \brief Query the size of the buffer necessary to hold the decoded RGB data * \param header DPX header * \param element image element (0-7) * \param block image area (used only for calculating the amount of pixels, * not an offset into the buffer) * \return zero: conversion impossible or unsupported; absolute value: size * of the buffer in bytes; sign: positive - memory needs to be allocated, * negative - allocation is optional, decoded data can replace the input */ int QueryRGBBufferSize(const Header &header, const int element, const Block &block); /*! * \brief Query the size of the buffer necessary to hold the decoded RGB data * \param header DPX header * \param element image element (0-7) * \return zero: conversion impossible or unsupported; absolute value: size * of the buffer in bytes; sign: positive - memory needs to be allocated, * negative - allocation is optional, decoded data can replace the input */ int QueryRGBBufferSize(const Header &header, const int element); /*! * \brief Convert native data from the input buffer into RGB in the output buffer * \param header DPX header * \param element image element (0-7) * \param input input buffer data; can be same as output if \ref QueryRGBBufferSize returns a negative number * \param output output buffer data; can be same as input if \ref QueryRGBBufferSize returns a negative number * \param block image area (used only for calculating the amount of pixels, * not an offset into the buffer) * \return success true/false */ bool ConvertToRGB(const Header &header, const int element, const void *input, void *output, const Block &block); /*! * \brief Convert native data from the input buffer into RGB in the output buffer * \param header DPX header * \param element image element (0-7) * \param input input buffer data; can be same as output if \ref QueryRGBBufferSize returns a negative number * \param output output buffer data; can be same as input if \ref QueryRGBBufferSize returns a negative number * \return success true/false */ bool ConvertToRGB(const Header &header, const int element, const void *input, void *output); /*! * \brief Query the size of the buffer necessary to hold the encoded native data * \param desc descriptor of the target pixel format * \param compSize component data storage data type of the target pixel format * \param block image area (used only for calculating the amount of pixels, * not an offset into the buffer) * \return zero: conversion impossible or unsupported; absolute value: size * of the buffer in bytes; sign: positive - memory needs to be allocated, * negative - allocation is optional, decoded data can replace the input */ int QueryNativeBufferSize(const Descriptor desc, const DataSize compSize, const Block &block); /*! * \brief Query the size of the buffer necessary to hold the encoded native data * \param desc descriptor of the target pixel format * \param compSize component data storage data type of the target pixel format * \return zero: conversion impossible or unsupported; absolute value: size * of the buffer in bytes; sign: positive - memory needs to be allocated, * negative - allocation is optional, decoded data can replace the input */ int QueryNativeBufferSize(const Descriptor desc, const DataSize compSize, const int width, const int height); /*! * \brief Convert RGB data from the input buffer into native format in the output buffer * \param desc descriptor of the target pixel format * \param compSize component data storage data type of the target pixel format * \param cmetr colorimetric of the target pixel format * \param input input buffer data; can be same as output if \ref QueryNativeBufferSize returns a negative number * \param output output buffer data; can be same as intput if \ref QueryNativeBufferSize returns a negative number * \param block image area (used only for calculating the amount of pixels, * not an offset into the buffer) * \return success true/false */ bool ConvertToNative(const Descriptor desc, const DataSize compSize, const Characteristic cmetr, const void *input, void *output, const Block &block); /*! * \brief Convert RGB data from the input buffer into native format in the output buffer * \param desc descriptor of the target pixel format * \param compSize component data storage data type of the target pixel format * \param cmetr colorimetric of the target pixel format * \param input input buffer data; can be same as output if \ref QueryNativeBufferSize returns a negative number * \param output output buffer data; can be same as input if \ref QueryNativeBufferSize returns a negative number * \return success true/false */ bool ConvertToNative(const Descriptor desc, const DataSize compSize, const Characteristic cmetr, const int width, const int height, const void *input, void *output); } #endif openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/Writer.cpp0000644000175000017500000003046613151711064022573 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "DPX.h" #include "DPXStream.h" #include "EndianSwap.h" #include "WriterInternal.h" dpx::Writer::Writer() : fileLoc(0) { } dpx::Writer::~Writer() { } void dpx::Writer::Start() { } void dpx::Writer::SetFileInfo(const char *fileName, const char *creationTimeDate, const char *creator, const char *project, const char *copyright, const U32 encryptKey, const bool swapEndian) { if (fileName) this->header.SetFileName(fileName); if (creationTimeDate) this->header.SetCreationTimeDate(creationTimeDate); else { time_t seconds = time(0); this->header.SetCreationTimeDate(seconds); } if (creator) this->header.SetCreator(creator); else this->header.SetCreator("OpenDPX library"); if (project) this->header.SetProject(project); if (copyright) this->header.SetCopyright(copyright); this->header.SetEncryptKey(encryptKey); if (swapEndian) this->header.magicNumber = SwapBytes(this->header.magicNumber); } void dpx::Writer::SetImageInfo(const U32 width, const U32 height) { this->header.SetImageOrientation(kLeftToRightTopToBottom); this->header.SetPixelsPerLine(width); this->header.SetLinesPerElement(height); } // returns next available or MAX_ELEMENTS if full int dpx::Writer::NextAvailElement() const { unsigned int i; for (i = 0; i < MAX_ELEMENTS; i++) { if (this->header.ImageDescriptor(i) == kUndefinedDescriptor) break; } return i; } void dpx::Writer::SetOutStream(OutStream *fd) { this->fd = fd; } bool dpx::Writer::WriteHeader() { // calculate any header info this->header.CalculateOffsets(); // seek to the beginning of the file if (!this->fd->Seek(0, OutStream::kStart)) return false; // writing the header count this->fileLoc = this->header.Size(); return this->header.Write(fd); } void dpx::Writer::SetUserData(const long size) { this->header.SetUserSize(size); } bool dpx::Writer::WriteUserData(void *data) { size_t size = this->header.UserSize(); if (fd->Write(data, size) != size) return false; this->fileLoc += size; return true; } void dpx::Writer::SetElement(const int num, const Descriptor desc, const U8 bitDepth, const Characteristic transfer, const Characteristic colorimetric, const Packing packing, const Encoding encoding, const U32 dataSign, const U32 lowData, const R32 lowQuantity, const U32 highData, const R32 highQuantity, const U32 eolnPadding, const U32 eoimPadding) { // make sure the range is good if (num < 0 || num >= MAX_ELEMENTS) return; // set values this->header.SetDataSign(num, dataSign); this->header.SetLowData(num, lowData); this->header.SetLowQuantity(num, lowQuantity); this->header.SetHighData(num, highData); this->header.SetHighQuantity(num, highQuantity); this->header.SetImageDescriptor(num, desc); this->header.SetTransfer(num, transfer); this->header.SetColorimetric(num, colorimetric); this->header.SetBitDepth(num, bitDepth); this->header.SetImagePacking(num, packing); this->header.SetImageEncoding(num, encoding); this->header.SetEndOfLinePadding(num, eolnPadding); this->header.SetEndOfImagePadding(num, eoimPadding); // determine if increases element count this->header.CalculateNumberOfElements(); } bool dpx::Writer::WritePadData(const int alignment) { int imageoffset = ((this->fileLoc + alignment - 1)/alignment)*alignment; int padsize = imageoffset - this->fileLoc; if (padsize > 0) { std::vector pad (padsize, 0xff); this->fileLoc += this->fd->Write (&pad[0], padsize); if (this->fileLoc != imageoffset) return false; } return true; } // the data is processed so write it straight through // argument count is total size in bytes of the passed data bool dpx::Writer::WriteElement(const int element, void *data, const long count) { // make sure the range is good if (element < 0 || element >= MAX_ELEMENTS) return false; // make sure the entry is valid if (this->header.ImageDescriptor(element) == kUndefinedDescriptor) return false; // The DPX spec recommends that the image data starts on a 8K boundry. if (! this->WritePadData(0x2000)) return false; // update file ptr this->header.SetDataOffset(element, this->fileLoc); this->fileLoc += count; // write return (this->fd->Write(data, count) > 0); } bool dpx::Writer::WriteElement(const int element, void *data) { // make sure the range is good if (element < 0 || element >= MAX_ELEMENTS) return false; // make sure the entry is valid if (this->header.ImageDescriptor(element) == kUndefinedDescriptor) return false; return this->WriteElement(element, data, this->header.ComponentDataSize(element)); } bool dpx::Writer::WriteElement(const int element, void *data, const DataSize size) { bool status = true; // make sure the range is good if (element < 0 || element >= MAX_ELEMENTS) return false; // make sure the entry is valid if (this->header.ImageDescriptor(element) == kUndefinedDescriptor) return false; // The DPX spec recommends that the image data starts on a 8K boundry. if (! this->WritePadData(0x2000)) return false; // mark location in headers if (element == 0) this->header.SetImageOffset(this->fileLoc); this->header.SetDataOffset(element, this->fileLoc); // reverse the order of the components bool reverse = false; // rle encoding? const bool rle = this->header.ImageEncoding(element) == kRLE; // image parameters const U32 eolnPad = this->header.EndOfLinePadding(element); const U32 eoimPad = this->header.EndOfImagePadding(element); const U8 bitDepth = this->header.BitDepth(element); const U32 width = this->header.Width(); const U32 height = this->header.Height(); const int noc = this->header.ImageElementComponentCount(element); const Packing packing = this->header.ImagePacking(element); // check width & height, just in case if (width == 0 || height == 0) return false; // sizeof a component in an image const int bytes = (bitDepth + 7) / 8; // allocate memory for use to write blank space char *blank = 0; if (eolnPad || eoimPad) { int bsize = eolnPad > eoimPad ? eolnPad : eoimPad; blank = new char[bsize]; memset(blank, 0, bsize * sizeof(char)); } // can we write the entire memory chunk at once without any additional processing if (!rle && !this->header.RequiresByteSwap() && ((bitDepth == 8 && size == dpx::kByte) || (bitDepth == 12 && size == dpx::kWord && packing == kFilledMethodA) || (bitDepth == 16 && size == dpx::kWord) || (bitDepth == 32 && size == dpx::kFloat) || (bitDepth == 64 && size == dpx::kDouble))) { status = this->WriteThrough(data, width, height, noc, bytes, eolnPad, eoimPad, blank); if (blank) delete [] blank; return status; } else { switch (bitDepth) { case 8: if (size == dpx::kByte) this->fileLoc += WriteBuffer(this->fd, size, data, width, height, noc, packing, rle, reverse, eolnPad, blank, status, this->header.RequiresByteSwap()); else this->fileLoc += WriteBuffer(this->fd, size, data, width, height, noc, packing, rle, reverse, eolnPad, blank, status, this->header.RequiresByteSwap()); break; case 10: // are the channels stored in reverse if (this->header.ImageDescriptor(element) == kRGB && this->header.DatumSwap(element) && bitDepth == 10) reverse = true; if (size == dpx::kWord) this->fileLoc += WriteBuffer(this->fd, size, data, width, height, noc, packing, rle, reverse, eolnPad, blank, status, this->header.RequiresByteSwap()); else this->fileLoc += WriteBuffer(this->fd, size, data, width, height, noc, packing, rle, reverse, eolnPad, blank, status, this->header.RequiresByteSwap()); break; case 12: if (size == dpx::kWord) this->fileLoc += WriteBuffer(this->fd, size, data, width, height, noc, packing, rle, reverse, eolnPad, blank, status, this->header.RequiresByteSwap()); else this->fileLoc += WriteBuffer(this->fd, size, data, width, height, noc, packing, rle, reverse, eolnPad, blank, status, this->header.RequiresByteSwap()); break; case 16: if (size == dpx::kWord) this->fileLoc += WriteBuffer(this->fd, size, data, width, height, noc, packing, rle, reverse, eolnPad, blank, status, this->header.RequiresByteSwap()); else this->fileLoc += WriteBuffer(this->fd, size, data, width, height, noc, packing, rle, reverse, eolnPad, blank, status, this->header.RequiresByteSwap()); break; case 32: if (size == dpx::kFloat) this->fileLoc += WriteFloatBuffer(this->fd, size, data, width, height, noc, packing, rle, eolnPad, blank, status, this->header.RequiresByteSwap()); else this->fileLoc += WriteFloatBuffer(this->fd, size, data, width, height, noc, packing, rle, eolnPad, blank, status, this->header.RequiresByteSwap()); break; case 64: if (size == dpx::kDouble) this->fileLoc += WriteFloatBuffer(this->fd, size, data, width, height, noc, packing, rle, eolnPad, blank, status, this->header.RequiresByteSwap()); else this->fileLoc += WriteFloatBuffer(this->fd, size, data, width, height, noc, packing, rle, eolnPad, blank, status, this->header.RequiresByteSwap()); break; } } // if successful if (status && eoimPad) { // end of image padding this->fileLoc += eoimPad; status = (this->fd->Write(blank, eoimPad) > 0); } // rid of memory if (blank) delete [] blank; return status; } // the passed in image buffer is written to the file untouched bool dpx::Writer::WriteThrough(void *data, const U32 width, const U32 height, const int noc, const int bytes, const U32 eolnPad, const U32 eoimPad, char *blank) { bool status = true; const int count = width * height * noc; unsigned int i; unsigned char *imageBuf = reinterpret_cast(data); // file pointer location after write this->fileLoc += bytes * count + (eolnPad * height); // write data if (eolnPad) { // loop if have end of line padding for (i = 0; i < height; i++) { // write one line if (this->fd->Write(imageBuf+(width*bytes*i), bytes * width) == false) { status = false; break; } // write end of line padding if (this->fd->Write(blank, eoimPad) == false) { status = false; break; } } } else { // write data as one chunk if (this->fd->Write(imageBuf, bytes * count) == false) { status = false; } } // end of image padding if (status && eoimPad) { this->fileLoc += eoimPad; status = (this->fd->Write(blank, eoimPad) > 0); } return status; } bool dpx::Writer::Finish() { // write the file size in the header this->header.SetFileSize(this->fileLoc); // rewrite all of the offsets in the header return this->header.WriteOffsetData(this->fd); } openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/RunLengthEncoding.h0000644000175000017500000000513113151711064024330 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _DPX_RUNLENGTHENCODING_H #define _DPX_RUNLENGTHENCODING_H 1 #include "DPX.h" #include "Codec.h" namespace dpx { /*! * \brief compress / decompress data segments, used for RLE compression */ class RunLengthEncoding : public Codec { public: /*! * \brief constructor */ RunLengthEncoding(); /*! * \brief destructor */ virtual ~RunLengthEncoding(); /*! * \brief reset instance */ virtual void Reset(); /*! * \brief read data * \param dpxHeader dpx header information * \param fd field descriptor * \param element element (0-7) * \param block image area to read * \param data buffer * \param size size of the buffer component * \return success */ virtual bool Read(const dpx::Header &dpxHeader, ElementReadStream *fd, const int element, const Block &block, void *data, const DataSize size); protected: U8 *buf; //!< intermediate buffer }; } #endif openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/Codec.cpp0000644000175000017500000000534413151711064022331 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "DPX.h" #include "Codec.h" #include "ElementReadStream.h" #include "ReaderInternal.h" dpx::Codec::Codec() : scanline(0) { } dpx::Codec::~Codec() { if (this->scanline) delete [] scanline; } void dpx::Codec::Reset() { if (this->scanline) { delete [] scanline; this->scanline = 0; } } bool dpx::Codec::Read(const Header &dpxHeader, ElementReadStream *fd, const int element, const Block &block, void *data, const DataSize size) { // scanline buffer if (this->scanline == 0) { // get the number of components for this element descriptor const int numberOfComponents = dpxHeader.ImageElementComponentCount(element); // bit depth of the image element const int bitDepth = dpxHeader.BitDepth(element); // size of the scanline buffer is image width * number of components * bytes per component int slsize = ((numberOfComponents * dpxHeader.Width() * (bitDepth / 8 + (bitDepth % 8 ? 1 : 0))) / sizeof(U32))+1; this->scanline = new U32[slsize]; } // read the image block return ReadImageBlock(dpxHeader, this->scanline, fd, element, block, data, size); } openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/DPX.cpp0000644000175000017500000000435113151711064021744 0ustar mfvmfv// vi: ts=4 /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "DPX.h" // determine byte order for current system // Intel processors are Little Endian // Power PC, MIPS and Ultra Sparc are Big Endian static unsigned long lValue = 0x12345678; static unsigned long *lPtr = &lValue; dpx::Endian dpx::systemByteOrder = (*(unsigned char*)lPtr == 0x78 ? dpx::kLittleEndian : dpx::kBigEndian); bool dpx::IdentifyFile(InStream *fp) { U32 magic; fp->Rewind(); if (fp->Read(&magic, sizeof(magic)) != sizeof(magic)) return false; return dpx::Header::ValidMagicCookie(magic); } bool dpx::IdentifyFile(const void *p) { U32 magic = *((U32 *) p); return dpx::Header::ValidMagicCookie(magic); } openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/DPX.h0000644000175000017500000002640513151711064021415 0ustar mfvmfv// vi: ts=4 /*! \file DPX.h */ /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _DPX_H #define _DPX_H 1 #include #include #include "DPXHeader.h" #include "DPXStream.h" /*! * \def OPENDPX_VERSION * \brief OpenDPX Version */ #define OPENDPX_VERSION "0.5.0" /*! * \namespace dpx * \brief OpenDPX namespace */ namespace dpx { // forward definitions class Codec; class ElementReadStream; /*! * \enum Endian * \brief DPX files can be stored in big- or little-endian byte order */ enum Endian { kLittleEndian, //!< increasing numeric significance with increasing memory kBigEndian //!< big end first }; /*! \struct Block * \brief Rectangle block definition defined by two points */ struct Block { int x1, y1, x2, y2; /*! * \brief Constructor */ inline Block(); /*! * \brief Constructor * * \param x1 upper left x coordinate * \param y1 upper left y coordinate * \param x2 lower right x coordinate * \param y2 lower right y coordinate */ Block(const int x1, const int y1, const int x2, const int y2); /*! * \brief Set the block coordinates * * \param x1 upper left x coordinate * \param y1 upper left y coordinate * \param x2 lower right x coordinate * \param y2 lower right y coordinate */ void Set(const int x1, const int y1, const int x2, const int y2); /*! * \brief Check to see if a point is within the block * * \param x x coordinate * \param y y coordinate * \return true/false if coordinates within block */ inline bool Inside(const int x, const int y) const; /*! * \brief Rearrange coordinates if necessary so the first coordinate is upper left and the second is lower right */ inline void Check(); }; // Current platform endian byte order extern Endian systemByteOrder; // namespace functions /*! * \brief determine if the image file is DPX * * \param file buffer to read and search * \return true/false if identified as DPX */ bool IdentifyFile(InStream *file); /*! * \brief determine if the image file is DPX * * \param data memory to search * \return true/false if identified as DPX */ bool IdentifyFile(const void *data); /*! * \brief returns a char * of the default DPX file extension * * \return .dpx file extenion */ inline const char *DefaultExtension(); /*! * returns a string of the highest SMPTE DPX version supported by this library * * \return SMPTE DPX version */ inline const char *Version(); /*! * \brief returns the version string for this library * * \return OpenDPX version */ inline const char *LibraryVersion(); /*! * \class Reader * \brief DPX Image Reader class */ class Reader { public: /*! * \brief DPX header */ Header header; /*! * \brief Constructor */ Reader(); /*! * \brief Destructor */ virtual ~Reader(); /*! * \brief Set the InStream object to be used to read images * * \param stream Object to use for low level reads */ void SetInStream(InStream *stream); /*! * \brief clear any caching or memory allocated specific to an image */ void Reset(); /*! * \brief Read the dpx header into the header member * * \return success true/false */ bool ReadHeader(); /*! * \brief Read an image element into a buffer * * the size of the buffer must be large enough * simple calculation would be: * width * height * num_of_components * size_of_component * * \param element element (0-7) * \param data buffer * \return success true/false */ bool ReadImage(const int element, void *data); /*! * \brief Read a rectangular image block into a buffer from the specified image element * * \param element element (0-7) * \param data buffer * \param block image area to read * \return success true/false */ bool ReadBlock(const int element, unsigned char *data, Block &block); /*! * \brief Read the user data into a buffer. * * Buffer must be large enough to hold the user data. * * \param data buffer * \return success true/false */ bool ReadUserData(unsigned char *data); protected: InStream *fd; Codec *codex[MAX_ELEMENTS]; ElementReadStream *rio; }; /*! * \class Writer * \brief DPX Image Writer class */ class Writer { public: /*! * \brief DPX Header */ Header header; /*! * \brief Constructor */ Writer(); /*! * \brief Destructor */ virtual ~Writer(); /*! * \brief Start defining the header and writing the images */ void Start(); /*! * \brief Set the basic file information about DPX * * \param fileName name of this created file (100 characters max) * \param creationTimeDate creation time and date - format is "YYYY:MM:DD:HH:MM:SSLTZ" * where HH is 24 hour time, LTZ is local time zone using either * three character notation (i.e., -04) or five character notation * representing hours and minutes offset from Greenwich Mean time * (i.e., -0700) (24 characters max) * \param creator creator (100 characters max) * \param project project name (200 characters max) * \param copyright copyright statement (200 characters max) * \param encryptKey encryption key * \param swapEndian whether to write the image header in reverse to native endianness */ void SetFileInfo(const char *fileName, const char *creationTimeDate = 0, const char *creator = 0, const char *project = 0, const char *copyright = 0, const U32 encryptKey = ~0, const bool swapEndian = false); /*! * \brief Set the Width and Height of the images * * \param width width of the image * \param height height of the image */ void SetImageInfo(const U32 width, const U32 height); /*! * \brief Get the next available element * \return next available */ int NextAvailElement() const; /*! * \brief Set the parameters on an element * * There are 8 elements maximum in an single DPX and each element used must be set before writing the header * * \param element element number (0-7) * \param desc image descriptor * \param bitDepth bit depth of image, valid values are [8,10,12,16,32,64] * \param transfer transfer characteristic * \param colorimetric colorimetric specification * \param packing packing type * \param encoding encoding type * \param dataSign * \param lowData * \param lowQuantity * \param highData * \param highQuantity * \param eolnPadding end of line padding (in bytes) * \param eoimPadding end of image padding (in bytes) */ void SetElement(const int element = 0, const Descriptor desc = kRGB, const U8 bitDepth = 10, const Characteristic transfer = kLogarithmic, const Characteristic colorimetric = kLogarithmic, const Packing packing = kFilledMethodA, const Encoding encoding = kNone, const U32 dataSign = 0, const U32 lowData = ~0, const R32 lowQuantity = std::numeric_limits::quiet_NaN(), const U32 highData = ~0, const R32 highQuantity = std::numeric_limits::quiet_NaN(), const U32 eolnPadding = 0, const U32 eoimPadding = 0); /*! * \brief Set the OutStream object will use to write the files * * \param stream OutStream object */ void SetOutStream(OutStream *stream); /*! * \brief Set the size of the user data area * * \param size size of user data */ void SetUserData(const long size); /*! * \brief Write the header * * \return success true/false */ bool WriteHeader(); /*! * \brief Write the user data * * \param data buffer - must match size set in Writer::SetUserData() * \return success true/false */ bool WriteUserData(void *data); /*! * \brief Write out some padded data to the specified boundary * * \param alignment -- Where the block boundary should be * \return success true/false */ bool WritePadData(const int alignment); /*! * \brief Write the entire element to the dpx file * * \param element element number (0-7) * \param data buffer * \param alignment -- defines the imageAlignement to make sure the image is on typically an 8K alignement. * \return success true/false */ bool WriteElement(const int element, void *data); bool WriteElement(const int element, void *data, const DataSize size); bool WriteElement(const int element, void *data, const long count); /** * \brief Finish up writing image * * \return success true/false */ bool Finish(); protected: long fileLoc; OutStream *fd; bool WriteThrough(void *, const U32, const U32, const int, const int, const U32, const U32, char *); }; } inline const char *dpx::DefaultExtension() { return "dpx"; } inline const char *dpx::Version() { return SMPTE_VERSION; } inline const char *dpx::LibraryVersion() { return OPENDPX_VERSION; } inline dpx::Block::Block() : x1(0), y1(0), x2(0), y2(0) { } inline dpx::Block::Block(const int x1, const int y1, const int x2, const int y2) : x1(x1), y1(y1), x2(x2), y2(y2) { this->Check(); } inline void dpx::Block::Set(const int x1, const int y1, const int x2, const int y2) { this->x1 = x1; this->y1 = y1; this->x2 = x2; this->y2 = y2; } // check the coordinates that x1 < x2 and y1 < y2 inline void dpx::Block::Check() { if (this->x1 > this->x2) { int t = x1; this->x1 = this->x2; this->x2 = t; } if (this->y1 > this->y2) { int t = y1; this->y1 = this->y2; this->y2 = t; } } inline bool dpx::Block::Inside(const int x, const int y) const { if (x >= this->x1 && x <= this->x2 && y >= this->y1 && y <= this->y2) return true; return false; } #endif openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/BaseTypeConverter.h0000644000175000017500000001104713151711064024362 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _DPX_BASETYPECONVERTER_H #define _DPX_BASETYPECONVERTER_H 1 namespace dpx { // convert between all of the DPX base types in a controllable way // Note: These bit depth promotions (low precision -> high) use // a combination of bitshift and the 'or' operator in order to // fully populate the output coding space // // For example, when converting 8->16 bits, the 'simple' method // (shifting 8 bits) maps 255 to 65280. This result is not ideal // (uint16 'max' of 65535 is preferable). Overall, the best conversion // is one that approximates the 'true' floating-point scale factor. // 8->16 : 65535.0 / 255.0. 10->16 : 65535.0 / 1023.0. // For performance considerations, we choose to emulate this // floating-poing scaling with pure integer math, using a trick // where we duplicate portions of the MSB in the LSB. // // For bit depth demotions, simple truncation is used. // inline void BaseTypeConverter(U8 &src, U8 &dst) { dst = src; } inline void BaseTypeConverter(U8 &src, U16 &dst) { dst = (src << 8) | src; } inline void BaseTypeConverter(U8 &src, U32 &dst) { dst = (src << 24) | (src << 16) | (src << 8) | src; } inline void BaseTypeConverter(U8 &src, R32 &dst) { dst = src; } inline void BaseTypeConverter(U8 &src, R64 &dst) { dst = src; } inline void BaseTypeConverter(U16 &src, U8 &dst) { dst = src >> 8; } inline void BaseTypeConverter(U16 &src, U16 &dst) { dst = src; } inline void BaseTypeConverter(U16 &src, U32 &dst) { dst = (src << 16) | src; } inline void BaseTypeConverter(U16 &src, R32 &dst) { dst = src; } inline void BaseTypeConverter(U16 &src, R64 &dst) { dst = src; } inline void BaseTypeConverter(U32 &src, U8 &dst) { dst = src >> 24; } inline void BaseTypeConverter(U32 &src, U16 &dst) { dst = src >> 16; } inline void BaseTypeConverter(U32 &src, U32 &dst) { dst = src; } inline void BaseTypeConverter(U32 &src, R32 &dst) { dst = src; } inline void BaseTypeConverter(U32 &src, R64 &dst) { dst = src; } inline void BaseTypeConverter(R32 &src, U8 &dst) { dst = src; } inline void BaseTypeConverter(R32 &src, U16 &dst) { dst = src; } inline void BaseTypeConverter(R32 &src, U32 &dst) { dst = src; } inline void BaseTypeConverter(R32 &src, R32 &dst) { dst = src; } inline void BaseTypeConverter(R32 &src, R64 &dst) { dst = src; } inline void BaseTypeConverter(R64 &src, U8 &dst) { dst = src; } inline void BaseTypeConverter(R64 &src, U16 &dst) { dst = src; } inline void BaseTypeConverter(R64 &src, U32 &dst) { dst = src; } inline void BaseTypeConverter(R64 &src, R32 &dst) { dst = src; } inline void BaseTypeConverter(R64 &src, R64 &dst) { dst = src; } inline void BaseTypeConvertU10ToU16(U16 &src, U16 &dst) { dst = (src << 6) | (src >> 4); } inline void BaseTypeConvertU12ToU16(U16 &src, U16 &dst) { dst = (src << 4) | (src >> 8); } } #endif openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/EndianSwap.h0000644000175000017500000000660013151711064023006 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _DPX_ENDIANSWAP_H #define _DPX_ENDIANSWAP_H 1 namespace dpx { template T SwapBytes(T& value) { unsigned char *pe, *ps = reinterpret_cast(&value); unsigned char c; size_t s = (sizeof(T)); pe = ps + s - 1; for (size_t i = s/2; i > 0; i--) { c = *ps; *ps = *pe; *pe = c; ps++; pe--; } return value; } template <> inline unsigned short SwapBytes( unsigned short& value ) { unsigned char *p = reinterpret_cast(&value); unsigned char c = p[0]; p[0] = p[1]; p[1] = c; return value; } template <> inline unsigned char SwapBytes( unsigned char& value ) { return value; } template <> inline char SwapBytes( char& value ) { return value; } template void SwapBuffer(T *buf, unsigned int len) { for (unsigned int i = 0; i < len; i++) SwapBytes(buf[i]); } template void EndianSwapImageBuffer(void *data, int length) { switch (SIZE) { case dpx::kByte: break; case dpx::kWord: SwapBuffer(reinterpret_cast(data), length); break; case dpx::kInt: SwapBuffer(reinterpret_cast(data), length); break; case dpx::kFloat: SwapBuffer(reinterpret_cast(data), length); break; case dpx::kDouble: SwapBuffer(reinterpret_cast(data), length); break; } } inline void EndianSwapImageBuffer(DataSize size, void *data, int length) { switch (size) { case dpx::kByte: break; case dpx::kWord: SwapBuffer(reinterpret_cast(data), length); break; case dpx::kInt: SwapBuffer(reinterpret_cast(data), length); break; case dpx::kFloat: SwapBuffer(reinterpret_cast(data), length); break; case dpx::kDouble: SwapBuffer(reinterpret_cast(data), length); break; } } } #endif openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/WriterInternal.h0000644000175000017500000003062713151711064023734 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _DPX_WRITERINTERNAL_H #define _DPX_WRITERINTERNAL_H 1 #include "BaseTypeConverter.h" namespace dpx { void EndianBufferSwap(int bitdepth, dpx::Packing packing, void *buf, const size_t size) { switch (bitdepth) { case 8: break; case 12: if (packing == dpx::kPacked) dpx::EndianSwapImageBuffer(buf, size / sizeof(U32)); else dpx::EndianSwapImageBuffer(buf, size / sizeof(U16)); break; case 16: dpx::EndianSwapImageBuffer(buf, size / sizeof(U16)); break; default: // 10-bit, 32-bit, 64-bit dpx::EndianSwapImageBuffer(buf, size / sizeof(U32)); } } template void MultiTypeBufferCopy(T1 *dst, T2 *src, const int len) { for (int i = 0; i < len; i++) BaseTypeConverter(src[i], dst[i]); } template void CopyWriteBuffer(DataSize src_size, unsigned char *src, IB * dst, const int len) { if (src_size == kByte) MultiTypeBufferCopy(dst, reinterpret_cast(src), len); else if (src_size == kWord) MultiTypeBufferCopy(dst, reinterpret_cast(src), len); else if (src_size == kFloat) MultiTypeBufferCopy(dst, reinterpret_cast(src), len); else if (src_size == kDouble) MultiTypeBufferCopy(dst, reinterpret_cast(src), len); } // access modifications to the buffer based on compression and packing struct BufferAccess { int offset; int length; BufferAccess() : offset(0), length(0) { } }; // \todo NOT DONE template void RleCompress(IB *src, IB *dst, const int bufsize, const int len, BufferAccess &access) { /*IB ch; int count; int i; int index = bufsize - 1; bool start = true; //bool match = true; // for each data type, have maximum length of rle datum // subtract one so it the LSBit can be used to state int maxCount; if (BITDEPTH == 8) maxCount = 0xff - 1; else if (BITDEPTH == 10) maxCount = 0x3ff - 1; else if (BITDEPTH == 12) maxCount = 0xfff - 1; else if (BITDEPTH == 16) maxCount = 0xffff - 1; else maxCount = 1000000; // high number for floats, doubles for (i = len - 1; i >= 0; i--) { if (start) { count = 1; start = false; ch = src[i]; dst[index--] = ch; } } access.offset = index; access.length = bufsize - index;*/ } template void WritePackedMethod(IB *src, IB *dst, const int len, const bool reverse, BufferAccess &access) { // pack into the same memory space U32 *dst_u32 = reinterpret_cast(dst); // bit shift count for U16 source const int shift = 16 - BITDEPTH; // bit mask U32 mask = 0; if (BITDEPTH == 10) mask = 0x03ff; else if (BITDEPTH == 12) mask = 0x0fff; else if (BITDEPTH == 8) return; int i, entry; for (i = 0; i < len; i++) { // read value and determine write location U32 value = static_cast(src[i+access.offset]) >> shift; // if reverse the order /*** XXX TODO REVERSE if (reverse) // reverse the triplets so entry would be 2,1,0,5,4,3,8,7,6,... entry = ((i / 3) * 3) + (2 - (i % 3)); else ***/ entry = i; int div = (entry * BITDEPTH) / 32; // 32 bits in a U32 int rem = (entry * BITDEPTH) % 32; // write the bits that belong in the first U32 // calculate the masked bits for the added value U32 shift_mask = mask << rem; // mask sure to mask the bits to save as part of the src_buf // so if writing bits 8-18, save the bits of the source material dst_u32[div] = (dst_u32[div] & ~shift_mask) | ((value << rem) & shift_mask); // write across multiple U16? count the carry bits int carry = BITDEPTH - (32 - rem); if (carry > 0) { U32 save = BITDEPTH - carry; dst_u32[div+1] = (dst_u32[div+1] & ~(mask >> save)) | ((value >> save) & (mask >> save)); } } // adjust offset/length access.offset = 0; access.length = (((len * BITDEPTH) / 32) + ((len * BITDEPTH) % 32 ? 1 : 0)) * 2; } // this routine expects a type of U16 template void WritePackedMethodAB_10bit(IB *src, IB *dst, const int len, const bool reverse, BufferAccess &access) { // pack into the same memory space U32 *dst_u32 = reinterpret_cast(dst); // bit shift count const U32 shift = 6; // (16 - BITDEPTH) const U32 bitdepth = 10; const U32 bitmask = 0x03ff; // shift bits over 2 if Method A const int method_shift = (METHOD == kFilledMethodA ? 2 : 0); // loop through the buffer int i; U32 value = 0; for (i = 0; i < len; i++) { int div = i / 3; // 3 10-bit values in a U32 int rem = i % 3; // write previously calculated value if (i && rem == 0) { dst_u32[div-1] = value; value = 0; } // if reverse the order if (reverse) rem = 2 - rem; // place the 10 bits in the proper place with mask U32 comp = ((static_cast(src[i+access.offset]) >> shift) << (bitdepth * rem)) << method_shift; U32 mask = (bitmask << (bitdepth * rem)) << method_shift ; // overwrite only the proper 10 bits value = (value & ~mask) | (comp & mask); } // write last dst_u32[(len+2)/3-1] = value; // adjust offset/length // multiply * 2 because it takes two U16 = U32 and this func packs into a U32 access.offset = 0; access.length = ((len / 3) + (len % 3 ? 1 : 0)) * 2; } template int WriteBuffer(OutStream *fd, DataSize src_size, void *src_buf, const U32 width, const U32 height, const int noc, const Packing packing, const bool rle, bool reverse, const int eolnPad, char *blank, bool &status, bool swapEndian) { int fileOffset = 0; // determine any impact on the max line size due to RLE // impact may be that rle is true but the data can not be compressed at all // the worst possible compression with RLE is increasing the image size by 1/3 // so we will just double the destination size if RLE int rleBufAdd = (rle ? ((width * noc / 3) + 1) : 0); // buffer access parameters BufferAccess bufaccess; bufaccess.offset = 0; bufaccess.length = width * noc; // allocate one line IB *src; IB *dst = new IB[(width * noc) + 1 + rleBufAdd]; // not exactly sure why, but the datum order is wrong when writing 4-channel images, so reverse it if (noc == 4 && BITDEPTH == 10) reverse = !reverse; // each line in the buffer for (U32 h = 0; h < height; h++) { // image buffer unsigned char *imageBuf = reinterpret_cast(src_buf); const int bytes = Header::DataSizeByteCount(src_size); // copy buffer if need to promote data types from src to destination if (SAMEBUFTYPE) { src = dst; CopyWriteBuffer(src_size, (imageBuf+(h*width*noc*bytes)+(h*eolnPad)), dst, (width*noc)); } else // not a copy, access source src = reinterpret_cast(imageBuf + (h * width * noc * bytes) + (h*eolnPad)); // if rle, compress if (rle) { RleCompress(src, dst, ((width * noc) + rleBufAdd), width * noc, bufaccess); src = dst; } // if 10 or 12 bit, pack if (BITDEPTH == 10) { if (packing == dpx::kPacked) { WritePackedMethod(src, dst, (width*noc), reverse, bufaccess); } else if (packing == kFilledMethodA) { WritePackedMethodAB_10bit(src, dst, (width*noc), reverse, bufaccess); } else // if (packing == dpx::kFilledMethodB) { WritePackedMethodAB_10bit(src, dst, (width*noc), reverse, bufaccess); } } else if (BITDEPTH == 12) { if (packing == dpx::kPacked) { WritePackedMethod(src, dst, (width*noc), reverse, bufaccess); } else if (packing == dpx::kFilledMethodB) { // shift 4 MSB down, so 0x0f00 would become 0x00f0 for (int w = 0; w < bufaccess.length; w++) dst[w] = src[bufaccess.offset+w] >> 4; bufaccess.offset = 0; } // a bitdepth of 12 by default is packed with dpx::kFilledMethodA // assumes that either a copy or rle was required // otherwise this routine should not be called with: // 12-bit Method A with the source buffer data type is kWord } // write line fileOffset += (bufaccess.length * sizeof(IB)); if (swapEndian) EndianBufferSwap(BITDEPTH, packing, dst + bufaccess.offset, bufaccess.length * sizeof(IB)); if (fd->Write(dst+bufaccess.offset, (bufaccess.length * sizeof(IB))) == false) { status = false; break; } // end of line padding if (eolnPad) { fileOffset += eolnPad; if (fd->Write(blank, eolnPad) == false) { status = false; break; } } } // done with buffer delete [] dst; return fileOffset; } template int WriteFloatBuffer(OutStream *fd, DataSize src_size, void *src_buf, const U32 width, const U32 height, const int noc, const Packing packing, const bool rle, const int eolnPad, char *blank, bool &status, bool swapEndian) { int fileOffset = 0; // determine any impact on the max line size due to RLE // impact may be that rle is true but the data can not be compressed at all // the worst possible compression with RLE is increasing the image size by 1/3 // so we will just double the destination size if RLE int rleBufAdd = (rle ? ((width * noc / 3) + 1) : 0); // buffer access parameters BufferAccess bufaccess; bufaccess.offset = 0; bufaccess.length = width * noc; // allocate one line IB *src; IB *dst = new IB[(width * noc) + rleBufAdd]; // each line in the buffer for (U32 h = 0; h < height; h++) { // image buffer unsigned char *imageBuf = reinterpret_cast(src_buf); const int bytes = Header::DataSizeByteCount(src_size); // copy buffer if need to promote data types from src to destination if (!SAMEBUFTYPE) { src = dst; CopyWriteBuffer(src_size, (imageBuf+(h*width*noc*bytes)+(h*eolnPad)), dst, (width*noc)); } else // not a copy, access source src = reinterpret_cast(imageBuf + (h * width * noc * bytes) + (h*eolnPad)); // if rle, compress if (rle) { RleCompress(src, dst, ((width * noc) + rleBufAdd), width * noc, bufaccess); src = dst; } // write line fileOffset += (bufaccess.length * sizeof(IB)); if (swapEndian) EndianBufferSwap(BITDEPTH, packing, dst + bufaccess.offset, bufaccess.length * sizeof(IB)); if (fd->Write(dst+bufaccess.offset, (bufaccess.length * sizeof(IB))) == false) { status = false; break; } // end of line padding if (eolnPad) { fileOffset += eolnPad; if (fd->Write(blank, eolnPad) == false) { status = false; break; } } } // done with buffer delete [] dst; return fileOffset; } } #endif openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/DPXHeader.h0000644000175000017500000015502113151711064022523 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /*! \file DPXHeader.h */ /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ // SMPTE DPX graphic file format v2.0 #ifndef _DPX_DPXHEADER_H #define _DPX_DPXHEADER_H 1 #include #include "OpenImageIO/strutil.h" #include "DPXStream.h" /*! * \def SMPTE_VERSION * \brief SMPTE 268M-2003 DPX Version */ #define SMPTE_VERSION "V2.0" /*! * \def MAX_ELEMENTS * \brief Maximum number of image elements */ #define MAX_ELEMENTS 8 /*! * \def MAX_COMPONENTS * \brief Maximum number of components per image element */ #define MAX_COMPONENTS 8 /*! * \def MAGIC_COOKIE * \brief HEX value of "SDPX" */ #define MAGIC_COOKIE 0x53445058 namespace dpx { // DPX data types /*! * \typedef unsigned char U8 * \brief Unsigned 8 bit integer */ typedef unsigned char U8; /*! * \typedef unsigned char U16 * \brief Unsigned 16 bit integer */ typedef unsigned short U16; /*! * \typedef unsigned char U32 * \brief Unsigned 32 bit integer */ typedef unsigned int U32; /*! * \typedef float R32 * \brief 32 bit floating point number */ typedef float R32; /*! * \typedef float R64 * \brief 64 bit floating point number */ typedef double R64; /*! * \typedef char ASCII * \brief ASCII character */ typedef char ASCII; /*! * \enum DataSize * \brief Component Data Storage Data Type */ enum DataSize { kByte, //!< 8-bit size component kWord, //!< kInt, //!< kFloat, //!< kDouble //!< }; /*! * \enum Orientation * \brief Image Orientation Code */ enum Orientation { kLeftToRightTopToBottom = 0, //!< Oriented left to right, top to bottom kRightToLeftTopToBottom = 1, //!< Oriented right to left, top to bottom kLeftToRightBottomToTop = 2, //!< Oriented left to right, bottom to top kRightToLeftBottomToTop = 3, //!< Oriented right to left, bottom to top kTopToBottomLeftToRight = 4, //!< Oriented top to bottom, left to right kTopToBottomRightToLeft = 5, //!< Oriented top to bottom, right to left kBottomToTopLeftToRight = 6, //!< Oriented bottom to top, left to right kBottomToTopRightToLeft = 7, //!< Oriented bottom to top, right to left kUndefinedOrientation = 0xffff //!< Undefined orientation }; /*! * \enum Descriptor * \brief Image element Descriptor */ enum Descriptor { kUserDefinedDescriptor = 0, //!< User defined descriptor kRed = 1, //!< Red kGreen = 2, //!< Green kBlue = 3, //!< Blue kAlpha = 4, //!< Alpha kLuma = 6, //!< Luma (Y) kColorDifference = 7, //!< Color difference kDepth = 8, //!< Depth kCompositeVideo = 9, //!< Composite video kRGB = 50, //!< R,G,B kRGBA = 51, //!< R,G,B,A kABGR = 52, //!< A,B,G,R kCbYCrY = 100, //!< Cb,Y,Cr,Y (4:2:2) kCbYACrYA = 101, //!< Cb,Y,A,Cr,Y,A (4:2:2:4) kCbYCr = 102, //!< Cb,Y,Cr (4:4:4) kCbYCrA = 103, //!< Cb,Y,Cr,A (4:4:4:4) kUserDefined2Comp = 150, //!< User defined 2 component element kUserDefined3Comp = 151, //!< User defined 3 component element kUserDefined4Comp = 152, //!< User defined 4 component element kUserDefined5Comp = 153, //!< User defined 5 component element kUserDefined6Comp = 154, //!< User defined 6 component element kUserDefined7Comp = 155, //!< User defined 7 component element kUserDefined8Comp = 156, //!< User defined 8 component element kUndefinedDescriptor = 0xff //!< Undefined descriptor }; /*! * \enum Characteristic * \brief Transfer Characteristic and Colorimetric Specification */ enum Characteristic { kUserDefined = 0, //!< User defined kPrintingDensity, //!< Printing density kLinear, //!< Linear, transfer only kLogarithmic, //!< Logarithmic, transfer only kUnspecifiedVideo, //!< Unspecified video kSMPTE274M, //!< SMPTE 274M kITUR709, //!< ITU-R 709-4 kITUR601, //!< ITU-R 601-5 system B or G kITUR602, //!< ITU-R 601-5 system M kNTSCCompositeVideo, //!< NTSC composite video kPALCompositeVideo, //!< PAL composite video kZLinear, //!< Z depth linear, transfer only kZHomogeneous, //!< Z depth homogeneous, transfer only kUndefinedCharacteristic = 0xff //!< Undefined }; /*! * \enum VideoSignal * \brief Video Signal Standard */ enum VideoSignal { kUndefined = 0, //!< Undefined kNTSC = 1, //!< NTSC kPAL = 2, //!< PAL kPAL_M = 3, //!< PAL-M kSECAM = 4, //!< SECAM k525LineInterlace43AR = 50, //!< YCbCr ITU-R 601-5 525-line, 2:1 interlace, 4:3 aspect ratio k625LineInterlace43AR = 51, //!< YCbCr ITU-R 601-5 625-line, 2:1 interlace, 4:3 aspect ratio k525LineInterlace169AR = 100, //!< YCbCr ITU-R 601-5 525-line, 2:1 interlace, 16:9 aspect ratio k625LineInterlace169AR = 101, //!< YCbCr ITU-R 601-5 625-line, 2:1 interlace, 16:9 aspect ratio k1050LineInterlace169AR = 150, //!< YCbCr 1050-line, 2:1 interlace, 16:9 aspect ratio k1125LineInterlace169AR_274 = 151, //!< YCbCr 1125-line, 2:1 interlace, 16:9 aspect ratio (SMPTE 274M) k1250LineInterlace169AR = 152, //!< YCbCr 1250-line, 2:1 interlace, 16:9 aspect ratio k1125LineInterlace169AR_240 = 153, //!< YCbCr 1125-line, 2:1 interlace, 16:9 aspect ratio (SMPTE 240M) k525LineProgressive169AR = 200, //!< YCbCr 525-line, 1:1 progressive, 16:9 aspect ratio k625LineProgressive169AR = 201, //!< YCbCr 625-line, 1:1 progressive, 16:9 aspect ratio k750LineProgressive169AR = 202, //!< YCbCr 750-line, 1:1 progressive, 16:9 aspect ratio (SMPTE 296M) k1125LineProgressive169AR = 203, //!< YCbCr 1125-line, 1:1 progressive, 16:9 aspect ratio (SMPTE 274M) k255 = 255 }; /*! * \enum Packing * \brief Component data packing method */ enum Packing { kPacked = 0, //!< Packed into 32-bit words kFilledMethodA = 1, //!< Filled to 32-bit words, method A kFilledMethodB = 2 //!< Filled to 32-bit words, method B }; /*! * \enum Encoding * \brief Component data encoding method */ enum Encoding { kNone = 0, //DetermineByteSwap(this->magicNumber); } inline const U32 Header::Size() const { return 2048; } inline U32 GenericHeader::MagicNumber() const { return this->magicNumber; } inline U32 GenericHeader::ImageOffset() const { return this->imageOffset; } inline void GenericHeader::SetImageOffset(const U32 offset) { this->imageOffset = offset; } inline void GenericHeader::Version(char *v) const { OIIO::Strutil::safe_strcpy(v, this->version, sizeof(this->version)); v[8] = '\0'; } inline void GenericHeader::SetVersion(const char * v) { OIIO::Strutil::safe_strcpy(this->version, v, sizeof(this->version)); } inline U32 GenericHeader::FileSize() const { return this->fileSize; } inline void GenericHeader::SetFileSize(const U32 fs) { this->fileSize = fs; } inline U32 GenericHeader::DittoKey() const { return this->dittoKey; } inline void GenericHeader::SetDittoKey(const U32 key) { this->dittoKey = key; } inline U32 GenericHeader::GenericSize() const { return this->genericSize; } inline U32 GenericHeader::IndustrySize() const { return this->industrySize; } inline U32 GenericHeader::UserSize() const { return this->userSize; } inline void GenericHeader::SetUserSize(const U32 size) { this->userSize = size; } inline void GenericHeader::FileName(char *fn) const { ::strncpy(fn, this->fileName, sizeof(this->fileName)); fn[100] = '\0'; } inline void GenericHeader::SetFileName(const char *fn) { ::strncpy(this->fileName, fn, sizeof(this->fileName)); } inline void GenericHeader::CreationTimeDate(char *ct) const { ::strncpy(ct, this->creationTimeDate, sizeof(this->creationTimeDate)); ct[24] = '\0'; } inline void GenericHeader::SetCreationTimeDate(const char *ct) { ::strncpy(this->creationTimeDate, ct, sizeof(this->creationTimeDate)); } inline void GenericHeader::Creator(char *creat) const { ::strncpy(creat, this->creator, sizeof(this->creator)); creat[200] = '\0'; } inline void GenericHeader::SetCreator(const char *creat) { ::strncpy(this->creator, creat, sizeof(this->creator)); } inline void GenericHeader::Project(char *prj) const { ::strncpy(prj, this->project, sizeof(this->project)); prj[200] = '\0'; } inline void GenericHeader::SetProject(const char *prj) { ::strncpy(this->project, prj, sizeof(this->project)); } inline void GenericHeader::Copyright(char *copy) const { ::strncpy(copy, this->copyright, sizeof(this->copyright)); copy[200] = '\0'; } inline void GenericHeader::SetCopyright(const char *copy) { ::strncpy(this->copyright, copy, sizeof(this->copyright)); } inline U32 GenericHeader::EncryptKey() const { return this->encryptKey; } inline void GenericHeader::SetEncryptKey(const U32 key) { this->encryptKey = key; } inline Orientation GenericHeader::ImageOrientation() const { return Orientation(this->imageOrientation); } inline void GenericHeader::SetImageOrientation(const Orientation orient) { this->imageOrientation = orient; } inline U16 GenericHeader::NumberOfElements() const { return this->numberOfElements; } inline void GenericHeader::SetNumberOfElements(const U16 num) { this->numberOfElements = num; } inline U32 GenericHeader::PixelsPerLine() const { return this->pixelsPerLine; } inline void GenericHeader::SetPixelsPerLine(const U32 ppl) { this->pixelsPerLine = ppl; } inline U32 GenericHeader::LinesPerElement() const { return this->linesPerElement; } inline void GenericHeader::SetLinesPerElement(const U32 lpe) { this->linesPerElement = lpe; } inline U32 GenericHeader::DataSign(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return 0xffffffff; return this->chan[i].dataSign; } inline void GenericHeader::SetDataSign(const int i, const U32 sign) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].dataSign = sign; } inline U32 GenericHeader::LowData(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return 0xffffffff; return this->chan[i].lowData; } inline void GenericHeader::SetLowData(const int i, const U32 data) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].lowData = data; } inline R32 GenericHeader::LowQuantity(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return 0xffffffff; return this->chan[i].lowQuantity; } inline void GenericHeader::SetLowQuantity(const int i, const R32 quant) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].lowQuantity = quant; } inline U32 GenericHeader::HighData(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return 0xffffffff; return this->chan[i].highData; } inline void GenericHeader::SetHighData(const int i, const U32 data) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].highData = data; } inline R32 GenericHeader::HighQuantity(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return 0xffffffff; return this->chan[i].highQuantity; } inline void GenericHeader::SetHighQuantity(const int i, const R32 quant) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].highQuantity = quant; } inline Descriptor GenericHeader::ImageDescriptor(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return Descriptor(0xff); return Descriptor(this->chan[i].descriptor); } inline void GenericHeader::SetImageDescriptor(const int i, const Descriptor desc) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].descriptor = desc; } inline Characteristic GenericHeader::Transfer(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return Characteristic(0xff); return Characteristic(this->chan[i].transfer); } inline void GenericHeader::SetTransfer(const int i, const Characteristic ch) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].transfer = ch; } inline Characteristic GenericHeader::Colorimetric(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return Characteristic(0xff); return Characteristic(this->chan[i].colorimetric); } inline void GenericHeader::SetColorimetric(const int i, const Characteristic c) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].colorimetric = c; } inline U8 GenericHeader::BitDepth(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return 0xff; return this->chan[i].bitDepth; } inline void GenericHeader::SetBitDepth(const int i, const U8 depth) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].bitDepth = depth; } inline Packing GenericHeader::ImagePacking(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return Packing(0xff); return Packing(this->chan[i].packing); } inline void GenericHeader::SetImagePacking(const int i, const Packing pack) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].packing = pack; } inline Encoding GenericHeader::ImageEncoding(const int i) const { Encoding e = kNone; if (i < 0 || i >= MAX_ELEMENTS) return kNone; if (this->chan[i].encoding == 1) e = kRLE; return e; } inline void GenericHeader::SetImageEncoding(const int i, const Encoding enc) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].encoding = (enc == kNone ? 0 : 1); } inline U32 GenericHeader::DataOffset(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return 0xffffffff; return this->chan[i].dataOffset; } inline void GenericHeader::SetDataOffset(const int i, const U32 offset) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].dataOffset = offset; } inline U32 GenericHeader::EndOfLinePadding(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return 0xffffffff; if (this->chan[i].endOfLinePadding == 0xffffffff) return 0; return this->chan[i].endOfLinePadding; } inline void GenericHeader::SetEndOfLinePadding(const int i, const U32 eolp) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].endOfLinePadding = eolp; } inline U32 GenericHeader::EndOfImagePadding(const int i) const { if (i < 0 || i >= MAX_ELEMENTS) return 0xffffffff; if (this->chan[i].endOfImagePadding == 0xffffffff) return 0; return this->chan[i].endOfImagePadding; } inline void GenericHeader::SetEndOfImagePadding(const int i, const U32 eoip) { if (i < 0 || i >= MAX_ELEMENTS) return; this->chan[i].endOfImagePadding = eoip; } inline void GenericHeader::Description(const int i, char *desc) const { if (i < 0 || i >= MAX_ELEMENTS) return; strncpy(desc, this->chan[i].description, 32); } inline void GenericHeader::SetDescription(const int i, const char *desc) { if (i < 0 || i >= MAX_ELEMENTS) return; ::strncpy(this->chan[i].description, desc, 32); } inline U32 GenericHeader::XOffset() const { return this->xOffset; } inline void GenericHeader::SetXOffset(const U32 offset) { this->xOffset = offset; } inline U32 GenericHeader::YOffset() const { return this->yOffset; } inline void GenericHeader::SetYOffset(const U32 offset) { this->yOffset = offset; } inline R32 GenericHeader::XCenter() const { return this->xCenter; } inline void GenericHeader::SetXCenter(const R32 center) { this->xCenter = center; } inline R32 GenericHeader::YCenter() const { return this->yCenter; } inline void GenericHeader::SetYCenter(const R32 center) { this->yCenter = center; } inline U32 GenericHeader::XOriginalSize() const { return this->xOriginalSize; } inline void GenericHeader::SetXOriginalSize(const U32 size) { this->xOriginalSize = size; } inline U32 GenericHeader::YOriginalSize() const { return this->yOriginalSize; } inline void GenericHeader::SetYOriginalSize(const U32 size) { this->yOriginalSize = size; } inline void GenericHeader::SourceImageFileName(char *fn) const { ::strncpy(fn, this->sourceImageFileName, sizeof(this->sourceImageFileName)); fn[100] = '\0'; } inline void GenericHeader::SetSourceImageFileName(const char *fn) { ::strncpy(this->sourceImageFileName, fn, sizeof(this->sourceImageFileName)); } inline void GenericHeader::SourceTimeDate(char *td) const { ::strncpy(td, this->sourceTimeDate, sizeof(this->sourceTimeDate)); td[24] = '\0'; } inline void GenericHeader::SetSourceTimeDate(const char *td) { ::strncpy(this->sourceTimeDate, td, sizeof(this->sourceTimeDate)); } inline void GenericHeader::InputDevice(char *dev) const { ::strncpy(dev, this->inputDevice, sizeof(this->inputDevice)); dev[32] = '\0'; } inline void GenericHeader::SetInputDevice(const char *dev) { ::strncpy(this->inputDevice, dev, sizeof(this->inputDevice)); } inline void GenericHeader::InputDeviceSerialNumber(char *sn) const { ::strncpy(sn, this->inputDeviceSerialNumber, sizeof(this->inputDeviceSerialNumber)); sn[32] = '\0'; } inline void GenericHeader::SetInputDeviceSerialNumber(const char *sn) { ::strncpy(this->inputDeviceSerialNumber, sn, sizeof(this->inputDeviceSerialNumber)); } inline U16 GenericHeader::Border(const int i) const { if (i < 0 || i > 3) return 0xffff; return this->border[i]; } inline void GenericHeader::SetBorder(const int i, const U16 bord) { if (i < 0 || i > 3) return; this->border[i] = bord; } inline U32 GenericHeader::AspectRatio(const int i) const { if (i != 0 && i != 1) return 0xffffffff; return this->aspectRatio[i]; } inline void GenericHeader::SetAspectRatio(const int i, const U32 ar) { if (i != 0 && i != 1) return; this->aspectRatio[i] = ar; } inline R32 GenericHeader::XScannedSize() const { return this->xScannedSize; } inline void GenericHeader::SetXScannedSize(const R32 size) { this->xScannedSize = size; } inline R32 GenericHeader::YScannedSize() const { return this->yScannedSize; } inline void GenericHeader::SetYScannedSize(const R32 size) { this->yScannedSize = size; } inline void IndustryHeader::Format(char *fmt) const { ::strncpy(fmt, this->format, sizeof(this->format)); fmt[32] = '\0'; } inline void IndustryHeader::SetFormat(const char *fmt) { ::strncpy(this->format, fmt, sizeof(this->format)); } inline U32 IndustryHeader::FramePosition() const { return this->framePosition; } inline void IndustryHeader::SetFramePosition(const U32 pos) { this->framePosition = pos; } inline U32 IndustryHeader::SequenceLength() const { return this->sequenceLength; } inline void IndustryHeader::SetSequenceLength(const U32 len) { this->sequenceLength = len; } inline U32 IndustryHeader::HeldCount() const { return this->heldCount; } inline void IndustryHeader::SetHeldCount(const U32 count) { this->heldCount = count; } inline R32 IndustryHeader::FrameRate() const { return this->frameRate; } inline void IndustryHeader::SetFrameRate(const R32 rate) { this->frameRate = rate; } inline R32 IndustryHeader::ShutterAngle() const { return this->shutterAngle; } inline void IndustryHeader::SetShutterAngle(const R32 angle) { this->shutterAngle = angle; } inline void IndustryHeader::FrameId(char *id) const { ::strncpy(id, this->frameId, sizeof(this->frameId)); id[32] = '\0'; } inline void IndustryHeader::SetFrameId(const char *id) { ::strncpy(this->frameId, id, sizeof(this->frameId)); } inline void IndustryHeader::SlateInfo(char *slate) const { ::strncpy(slate, this->slateInfo, sizeof(this->slateInfo)); slate[100] = '\0'; } inline void IndustryHeader::SetSlateInfo(const char *slate) { ::strncpy(this->slateInfo, slate, sizeof(this->slateInfo)); } inline U8 IndustryHeader::Interlace() const { return this->interlace; } inline void IndustryHeader::SetInterlace(const U8 lace) { this->interlace = lace; } inline U8 IndustryHeader::FieldNumber() const { return this->fieldNumber; } inline void IndustryHeader::SetFieldNumber(const U8 fn) { this->fieldNumber = fn; } inline VideoSignal IndustryHeader::Signal() const { return VideoSignal(this->videoSignal); } inline void IndustryHeader::SetSignal(const VideoSignal vs) { this->videoSignal = vs; } inline R32 IndustryHeader::HorizontalSampleRate() const { return this->horizontalSampleRate; } inline void IndustryHeader::SetHorizontalSampleRate(const R32 rate) { this->horizontalSampleRate = rate; } inline R32 IndustryHeader::VerticalSampleRate() const { return this->verticalSampleRate; } inline void IndustryHeader::SetVerticalSampleRate(const R32 rate) { this->verticalSampleRate = rate; } inline R32 IndustryHeader::TemporalFrameRate() const { return this->temporalFrameRate; } inline void IndustryHeader::SetTemporalFrameRate(const R32 rate) { this->temporalFrameRate = rate; } inline R32 IndustryHeader::TimeOffset() const { return this->timeOffset; } inline void IndustryHeader::SetTimeOffset(const R32 offset) { this->timeOffset = offset; } inline R32 IndustryHeader::Gamma() const { return this->gamma; } inline void IndustryHeader::SetGamma(const R32 g) { this->gamma = g; } inline R32 IndustryHeader::BlackLevel() const { return this->blackLevel; } inline void IndustryHeader::SetBlackLevel(const R32 bl) { this->blackLevel = bl; } inline R32 IndustryHeader::BlackGain() const { return this->blackGain; } inline void IndustryHeader::SetBlackGain(const R32 bg) { this->blackGain = bg; } inline R32 IndustryHeader::BreakPoint() const { return this->breakPoint; } inline void IndustryHeader::SetBreakPoint(const R32 bp) { this->breakPoint = bp; } inline R32 IndustryHeader::WhiteLevel() const { return this->whiteLevel; } inline void IndustryHeader::SetWhiteLevel(const R32 wl) { this->whiteLevel = wl; } inline R32 IndustryHeader::IntegrationTimes() const { return this->integrationTimes; } inline void IndustryHeader::SetIntegrationTimes(const R32 times) { this->integrationTimes = times; } } #endif openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/OutStream.cpp0000644000175000017500000000477413151711064023245 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include "OpenImageIO/filesystem.h" #include "DPXStream.h" OutStream::OutStream() : fp(0) { } OutStream::~OutStream() { } bool OutStream::Open(const char *f) { if (this->fp) this->Close(); if ((this->fp = OIIO::Filesystem::fopen(f, "wb")) == 0) return false; return true; } void OutStream::Close() { if (this->fp) { ::fclose(this->fp); this->fp = 0; } } size_t OutStream::Write(void *buf, const size_t size) { if (this->fp == 0) return false; return ::fwrite(buf, 1, size, this->fp); } bool OutStream::Seek(long offset, Origin origin) { int o = 0; switch (origin) { case kCurrent: o = SEEK_CUR; break; case kEnd: o = SEEK_END; break; case kStart: o = SEEK_SET; break; } if (this->fp == 0) return false; return (::fseek(this->fp, offset, o) == 0); } void OutStream::Flush() { if (this->fp) ::fflush(this->fp); } openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/ElementReadStream.h0000644000175000017500000000433013151711064024314 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _DPX_ELEMENTREADSTREAM_H #define _DPX_ELEMENTREADSTREAM_H 1 #include "DPXStream.h" namespace dpx { class ElementReadStream { public: ElementReadStream(InStream *); virtual ~ElementReadStream(); virtual void Reset(); virtual bool Read(const dpx::Header &, const int element, const long offset, void * buf, const size_t size); virtual bool ReadDirect(const dpx::Header &, const int element, const long offset, void * buf, const size_t size); protected: void EndianDataCheck(const dpx::Header &, const int element, void *, const size_t size); InStream *fd; }; } #endif openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/InStream.cpp0000644000175000017500000000525413151711064023036 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include "OpenImageIO/filesystem.h" #include "DPXStream.h" InStream::InStream() : fp(0) { } InStream::~InStream() { } bool InStream::Open(const char *f) { if (this->fp) this->Close(); if ((this->fp = OIIO::Filesystem::fopen(f, "rb")) == 0) return false; return true; } void InStream::Close() { if (this->fp) { ::fclose(this->fp); this->fp = 0; } } void InStream::Rewind() { if (this->fp) ::rewind(fp); } bool InStream::Seek(long offset, Origin origin) { int o = 0; switch (origin) { case kCurrent: o = SEEK_CUR; break; case kEnd: o = SEEK_END; break; case kStart: o = SEEK_SET; break; } if (this->fp == 0) return false; return (::fseek(this->fp, offset, o) == 0); } size_t InStream::Read(void *buf, const size_t size) { if (this->fp == 0) return 0; return ::fread(buf, 1, size, this->fp); } size_t InStream::ReadDirect(void *buf, const size_t size) { return this->Read(buf, size); } bool InStream::EndOfFile() const { if (this->fp == 0) return true; return ::feof(this->fp); } openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/Codec.h0000644000175000017500000000500013151711064021763 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _DPX_CODEC_H #define _DPX_CODEC_H 1 #include "DPX.h" namespace dpx { /*! * \brief compress / decompress data segments * base class defaults to None */ class Codec { public: /*! * \brief constructor */ Codec(); /*! * \brief destructor */ virtual ~Codec(); /*! * \brief reset instance */ virtual void Reset(); /*! * \brief read data * \param dpxHeader dpx header information * \param fd field descriptor * \param element element (0-7) * \param block image area to read * \param data buffer * \param size size of the buffer component * \return success */ virtual bool Read(const Header &dpxHeader, ElementReadStream *fd, const int element, const Block &block, void *data, const DataSize size); protected: U32 *scanline; //!< single scanline }; } #endif openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/RunLengthEncoding.cpp0000644000175000017500000001230713151711064024666 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "DPX.h" #include "RunLengthEncoding.h" #include "ElementReadStream.h" #include "ReaderInternal.h" // Basic size of a packet is the number of bytes that all data packing methods will fit into that are whole and complete #define PACKET_REPEAT (10 * sizeof(U32)) // 320 bits repeating pattern #define BUFFER_SIZE (PACKET_REPEAT * 1002) // read in temp buffer size #define EXPANDED_BUFFER_SIZE (BUFFER_SIZE + (BUFFER_SIZE / 3)) // expaded size after unpacking (max) dpx::RunLengthEncoding::RunLengthEncoding() : buf(0) { } dpx::RunLengthEncoding::~RunLengthEncoding() { if (this->buf) delete [] buf; } void dpx::RunLengthEncoding::Reset() { if (this->buf) { delete [] buf; this->buf = 0; } } bool dpx::RunLengthEncoding::Read(const Header &dpxHeader, ElementReadStream *fd, const int element, const Block &block, void *data, const DataSize size) { int i; // just check to make sure that this element is RLE if (dpxHeader.ImageEncoding(element) != kRLE) return false; // get the number of components for this element descriptor const int numberOfComponents = dpxHeader.ImageElementComponentCount(element); const int width = dpxHeader.Width(); const int height = dpxHeader.Height(); const int byteCount = dpxHeader.ComponentByteCount(element); const U32 eolnPad = dpxHeader.EndOfLinePadding(element); //DataSize srcSize = dpxHeader.ComponentDataSize(element); // has the buffer been read in and decoded? if (this->buf == 0) { // not yet // bit depth of the image element const int bitDepth = dpxHeader.BitDepth(element); // error out if the bit depth 10 or 12 and have eoln bytes // this is particularly slow to parse and eoln padding bytes // are not needed for those formats // also if end of padding makes the line length odd, error out for that as well if (bitDepth != 8 && bitDepth != 16 && eolnPad > 0) return false; else if (bitDepth == 16 && eolnPad != 2 && eolnPad != 0) return false; // error out for real types since bit operations don't really make sense if (size == kFloat || size == kDouble) return false; // find start and possible end of the element U32 startOffset = dpxHeader.DataOffset(element); U32 endOffset = 0xffffffff; U32 currentOffset = startOffset; for (i = 0; i < MAX_ELEMENTS; i++) { if (i == element) continue; U32 doff = dpxHeader.DataOffset(i); if (doff == 0xffffffff) continue; if (doff > startOffset && (endOffset == 0xffffffff || doff < endOffset)) endOffset = doff - 1; } // size of the image const size_t imageSize = width * height * numberOfComponents; const size_t imageByteSize = imageSize * byteCount; // allocate the buffer that will store the entire image this->buf = new U8[imageByteSize]; // allocate the temporary buffer that will read in the encoded image U8 *tempBuf = new U8[EXPANDED_BUFFER_SIZE]; // xpos, ypos in decoding /*int xpos = 0; int ypos = 0;*/ // read in the encoded image block at a time bool done = false; while (!done) { // read in temp buffer size_t rs = fd->ReadDirect(dpxHeader, element, (currentOffset - startOffset), tempBuf, BUFFER_SIZE); currentOffset += rs; if (rs != BUFFER_SIZE) done = true; else if (endOffset != 0xffffffff && currentOffset >= endOffset) done = true; // if 10-bit, 12-bit, unpack or unfill // step through and decode } // no longer need temp buffer delete [] tempBuf; } #ifdef RLE_WORKING // NOT COMPLETE YET // copy buffer CopyImageBlock(dpxHeader, element, buf, srcSize, data, size, dstSize, block); #endif return true; } openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/ReaderInternal.h0000644000175000017500000005306313151711064023661 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _DPX_READERINTERNAL_H #define _DPX_READERINTERNAL_H 1 #include #include "BaseTypeConverter.h" #define PADDINGBITS_10BITFILLEDMETHODA 2 #define PADDINGBITS_10BITFILLEDMETHODB 0 #define MASK_10BITPACKED 0xffc0 #define MULTIPLIER_10BITPACKED 2 #define REMAIN_10BITPACKED 4 #define REVERSE_10BITPACKED 6 #define MASK_12BITPACKED 0xfff0 #define MULTIPLIER_12BITPACKED 4 #define REMAIN_12BITPACKED 2 #define REVERSE_12BITPACKED 4 namespace dpx { // this function is called when the DataSize is 10 bit and the packing method is kFilledMethodA or kFilledMethodB template void Unfill10bitFilled(U32 *readBuf, const int x, BUF *data, int count, int bufoff, const int numberOfComponents) { // unpack the words in the buffer BUF *obuf = data + bufoff; int index = (x * sizeof(U32)) % numberOfComponents; for (int i = count - 1; i >= 0; i--) { // unpacking the buffer backwords U32 word = readBuf[(i + index) / 3 / sizeof(U32)]; U16 d1 = U16(word >> ((2 - (i + index) % 3) * 10 + PADDINGBITS) & 0x3ff); BaseTypeConvertU10ToU16(d1, d1); BaseTypeConverter(d1, obuf[i]); } #if 0 // NOTE: REVERSE -- is this something we really need to handle? // There were many dpx images that write the components backwords // because of some confusion with DPX v1 spec switch (dpxHeader.DatumSwap(element)) { case 0: // no swap for (i = count - 1; i >= 0; i--) { U32 word = readBuf[(i + index) / 3 / sizeof(U32)]; U16 d1 = U16(word >> (((i + index) % 3) * 10 + PADDINGBITS) & 0x3ff); BaseTypeConvertU10ToU16(d1, d1); BaseTypeConverter(d1, obuf[i]); } case 1: // swap the three datum around so BGR becomes RGB for (i = count - 1; i >= 0; i--) { // unpacking the buffer backwords U32 word = readBuf[(i + index) / 3 / sizeof(U32)]; U16 d1 = U16(word >> ((2 - (i + index) % 3) * 10 + PADDINGBITS) & 0x3ff); BaseTypeConvertU10ToU16(d1, d1); BaseTypeConverter(d1, obuf[i]); } // NOTE: NOT DONE case 2 case 2: // swap the second two of three datum around so YCrCb becomes YCbCr for (i = count - 1; i >= 0; i--) { // unpacking the buffer backwords U32 word = readBuf[(i + index) / 3 / sizeof(U32)]; U16 d1 = U16(word >> ((2 - (count + index) % 3) * 10 + PADDINGBITS) & 0x3ff); BaseTypeConvertU10ToU16(d1, d1); BaseTypeConverter(d1, obuf[i]); } } #endif } template bool Read10bitFilled(const Header &dpxHeader, U32 *readBuf, IR *fd, const int element, const Block &block, BUF *data) { // image height to read const int height = block.y2 - block.y1 + 1; // get the number of components for this element descriptor const int numberOfComponents = dpxHeader.ImageElementComponentCount(element); // end of line padding int eolnPad = dpxHeader.EndOfLinePadding(element); // number of datums in one row int datums = dpxHeader.Width() * numberOfComponents; // Line length in bytes rounded to 32 bits boundary int lineLength = ((datums - 1) / 3 + 1) * 4; // read in each line at a time directly into the user memory space for (int line = 0; line < height; line++) { // determine offset into image element int actline = line + block.y1; // first get line offset long offset = actline * lineLength; // add in eoln padding offset += line * eolnPad; // add in offset within the current line, rounding down so to catch any components within the word offset += block.x1 * numberOfComponents / 3 * 4; // get the read count in bytes, round to the 32-bit boundry int readSize = (block.x2 - block.x1 + 1) * numberOfComponents; readSize += readSize % 3; readSize = readSize / 3 * 4; // determine buffer offset int bufoff = line * datums; fd->Read(dpxHeader, element, offset, readBuf, readSize); // unpack the words in the buffer #if RLE_WORKING int count = (block.x2 - block.x1 + 1) * numberOfComponents; Unfill10bitFilled(readBuf, block.x1, data, count, bufoff, numberOfComponents); #else BUF *obuf = data + bufoff; int index = (block.x1 * sizeof(U32)) % numberOfComponents; for (int count = (block.x2 - block.x1 + 1) * numberOfComponents - 1; count >= 0; count--) { // unpacking the buffer backwords U16 d1 = U16(readBuf[(count + index) / 3] >> ((2 - (count + index) % 3) * 10 + PADDINGBITS) & 0x3ff); BaseTypeConvertU10ToU16(d1, d1); BaseTypeConverter(d1, obuf[count]); // work-around for 1-channel DPX images - to swap the outlying pixels, otherwise the columns are in the wrong order if (numberOfComponents == 1 && count % 3 == 0) std::swap(obuf[count], obuf[count + 2]); } #endif } return true; } template bool Read10bitFilledMethodA(const Header &dpx, U32 *readBuf, IR *fd, const int element, const Block &block, BUF *data) { // padding bits for PackedMethodA is 2 return Read10bitFilled(dpx, readBuf, fd, element, block, data); } template bool Read10bitFilledMethodB(const Header &dpx, U32 *readBuf, IR *fd, const int element, const Block &block, BUF *data) { return Read10bitFilled(dpx, readBuf, fd, element, block, data); } // 10 bit, packed data // 12 bit, packed data template void UnPackPacked(U32 *readBuf, const int bitDepth, BUF *data, int count, int bufoff) { // unpack the words in the buffer BUF *obuf = data + bufoff; for (int i = count - 1; i >= 0; i--) { // unpacking the buffer backwords // find the byte that the data starts in, read in as a 16 bits then shift and mask // the pattern with byte offset is: // 10 bits datasize rotates every 4 data elements // element 0 -> 6 bit shift to normalize at MSB (10 LSB shifted 6 bits) // element 1 -> 4 bit shift to normalize at MSB // element 2 -> 2 bit shift to normalize at MSB // element 3 -> 0 bit shift to normalize at MSB // 10 bit algorithm: (6-((count % 4)*2)) // the pattern repeats every 160 bits // 12 bits datasize rotates every 2 data elements // element 0 -> 4 bit shift to normalize at MSB // element 1 -> 0 bit shift to normalize at MSB // 12 bit algorithm: (4-((count % 2)*4)) // the pattern repeats every 96 bits // first determine the word that the data element completely resides in U16 *d1 = reinterpret_cast(reinterpret_cast(readBuf)+((i * bitDepth) / 8 /*bits*/)); // place the component in the MSB and mask it for both 10-bit and 12-bit U16 d2 = (*d1 << (REVERSE - ((i % REMAIN) * MULTIPLIER))) & MASK; // For the 10/12 bit cases, specialize the 16-bit conversion by // repacking into the LSB and using a specialized conversion if(bitDepth == 10) { d2 = d2 >> REVERSE; BaseTypeConvertU10ToU16(d2, d2); } else if(bitDepth == 12) { d2 = d2 >> REVERSE; BaseTypeConvertU12ToU16(d2, d2); } BaseTypeConverter(d2, obuf[i]); } } template bool ReadPacked(const Header &dpxHeader, U32 *readBuf, IR *fd, const int element, const Block &block, BUF *data) { // image height to read const int height = block.y2 - block.y1 + 1; // get the number of components for this element descriptor const int numberOfComponents = dpxHeader.ImageElementComponentCount(element); // end of line padding int eolnPad = dpxHeader.EndOfLinePadding(element); // data size in bits const int dataSize = dpxHeader.BitDepth(element); // number of bytes const int lineSize = (dpxHeader.Width() * numberOfComponents * dataSize + 31) / 32; // read in each line at a time directly into the user memory space for (int line = 0; line < height; line++) { // determine offset into image element long offset = (line + block.y1) * (lineSize * sizeof(U32)) + (block.x1 * numberOfComponents * dataSize / 32 * sizeof(U32)) + (line * eolnPad); // calculate read size int readSize = ((block.x2 - block.x1 + 1) * numberOfComponents * dataSize); readSize += (block.x1 * numberOfComponents * dataSize % 32); // add the bits left over from the beginning of the line readSize = ((readSize + 31) / 32) * sizeof(U32); // calculate buffer offset int bufoff = line * dpxHeader.Width() * numberOfComponents; fd->Read(dpxHeader, element, offset, readBuf, readSize); // unpack the words in the buffer int count = (block.x2 - block.x1 + 1) * numberOfComponents; UnPackPacked(readBuf, dataSize, data, count, bufoff); } return true; } template bool Read10bitPacked(const Header &dpxHeader, U32 *readBuf, IR *fd, const int element, const Block &block, BUF *data) { return ReadPacked(dpxHeader, readBuf, fd, element, block, data); } template bool Read12bitPacked(const Header &dpxHeader, U32 *readBuf, IR *fd, const int element, const Block &block, BUF *data) { return ReadPacked(dpxHeader, readBuf, fd, element, block, data); } template bool ReadBlockTypes(const Header &dpxHeader, SRC *readBuf, IR *fd, const int element, const Block &block, BUF *data) { // get the number of components for this element descriptor const int numberOfComponents = dpxHeader.ImageElementComponentCount(element); // byte count component type const int bytes = dpxHeader.ComponentByteCount(element); // image image/height to read const int width = (block.x2 - block.x1 + 1) * numberOfComponents; const int height = block.y2 - block.y1 + 1; // end of line padding int eolnPad = dpxHeader.EndOfLinePadding(element); if (eolnPad == ~0) eolnPad = 0; // image width const int imageWidth = dpxHeader.Width(); // read in each line at a time directly into the user memory space for (int line = 0; line < height; line++) { // determine offset into image element long offset = (line + block.y1) * imageWidth * numberOfComponents * bytes + block.x1 * numberOfComponents * bytes + (line * eolnPad); if (BUFTYPE == SRCTYPE) { fd->ReadDirect(dpxHeader, element, offset, reinterpret_cast(data + (width*line)), width*bytes); } else { fd->Read(dpxHeader, element, offset, readBuf, width*bytes); // convert data for (int i = 0; i < width; i++) BaseTypeConverter(readBuf[i], data[width*line+i]); } } return true; } template bool Read12bitFilledMethodB(const Header &dpxHeader, U16 *readBuf, IR *fd, const int element, const Block &block, BUF *data) { // get the number of components for this element descriptor const int numberOfComponents = dpxHeader.ImageElementComponentCount(element); // image width & height to read const int width = (block.x2 - block.x1 + 1) * numberOfComponents; const int height = block.y2 - block.y1 + 1; // width of image const int imageWidth = dpxHeader.Width(); // end of line padding (not a required data element so check for ~0) int eolnPad = dpxHeader.EndOfLinePadding(element); if (eolnPad == ~0) eolnPad = 0; // read in each line at a time directly into the user memory space for (int line = 0; line < height; line++) { // determine offset into image element long offset = (line + block.y1) * imageWidth * numberOfComponents * 2 + block.x1 * numberOfComponents * 2 + (line * eolnPad); fd->Read(dpxHeader, element, offset, readBuf, width*2); // convert data for (int i = 0; i < width; i++) { U16 d1 = readBuf[i]; BaseTypeConvertU12ToU16(d1, d1); BaseTypeConverter(d1, data[width*line+i]); } } return true; } #ifdef RLE_WORKING template void ProcessImageBlock(const Header &dpxHeader, const int element, U32 *readBuf, const int x, BUF *data, const int bufoff) { const int bitDepth = dpxHeader.BitDepth(element); const int numberOfComponents = dpxHeader.ImageElementComponentCount(element); const Packing packing = dpxHeader.ImagePacking(element); if (bitDepth == 10) { if (packing == kFilledMethodA) Read10bitFilledMethodA(dpxHeader, readBuf, fd, element, block, reinterpret_cast(data)); Unfill10bitFilled(readBuf, x, data, count, bufoff, numberOfComponents); else if (packing == kFilledMethodB) Read10bitFilledMethodB(dpxHeader, readBuf, fd, element, block, reinterpret_cast(data)); else if (packing == kPacked) Read10bitPacked(dpxHeader, readBuf, fd, element, block, reinterpret_cast(data)); UnPackPacked(readBuf, dataSize, data, count, bufoff); } else if (bitDepth == 12) { if (packing == kPacked) Read12bitPacked(dpxHeader, readBuf, fd, element, block, reinterpret_cast(data)); else if (packing == kFilledMethodB) Read12bitFilledMethodB(dpxHeader, reinterpret_cast(readBuf), fd, element, block, reinterpret_cast(data)); } } #endif template bool ReadImageBlock(const Header &dpxHeader, U32 *readBuf, IR *fd, const int element, const Block &block, BUF *data) { const int bitDepth = dpxHeader.BitDepth(element); const DataSize size = dpxHeader.ComponentDataSize(element); const Packing packing = dpxHeader.ImagePacking(element); if (bitDepth == 10) { if (packing == kFilledMethodA) return Read10bitFilledMethodA(dpxHeader, readBuf, fd, element, block, reinterpret_cast(data)); else if (packing == kFilledMethodB) return Read10bitFilledMethodB(dpxHeader, readBuf, fd, element, block, reinterpret_cast(data)); else if (packing == kPacked) return Read10bitPacked(dpxHeader, readBuf, fd, element, block, reinterpret_cast(data)); } else if (bitDepth == 12) { if (packing == kPacked) return Read12bitPacked(dpxHeader, readBuf, fd, element, block, reinterpret_cast(data)); else if (packing == kFilledMethodB) // filled method B // 12 bits fill LSB of 16 bits return Read12bitFilledMethodB(dpxHeader, reinterpret_cast(readBuf), fd, element, block, reinterpret_cast(data)); else // filled method A // 12 bits fill MSB of 16 bits return ReadBlockTypes(dpxHeader, reinterpret_cast(readBuf), fd, element, block, reinterpret_cast(data)); } else if (size == dpx::kByte) return ReadBlockTypes(dpxHeader, reinterpret_cast(readBuf), fd, element, block, reinterpret_cast(data)); else if (size == dpx::kWord) return ReadBlockTypes(dpxHeader, reinterpret_cast(readBuf), fd, element, block, reinterpret_cast(data)); else if (size == dpx::kInt) return ReadBlockTypes(dpxHeader, reinterpret_cast(readBuf), fd, element, block, reinterpret_cast(data)); else if (size == dpx::kFloat) return ReadBlockTypes(dpxHeader, reinterpret_cast(readBuf), fd, element, block, reinterpret_cast(data)); else if (size == dpx::kDouble) return ReadBlockTypes(dpxHeader, reinterpret_cast(readBuf), fd, element, block, reinterpret_cast(data)); // should not reach here return false; } template bool ReadImageBlock(const Header &dpxHeader, U32 *readBuf, IR *fd, const int element, const Block &block, void *data, const DataSize size) { if (size == dpx::kByte) return ReadImageBlock(dpxHeader, readBuf, fd, element, block, reinterpret_cast(data)); else if (size == dpx::kWord) return ReadImageBlock(dpxHeader, readBuf, fd, element, block, reinterpret_cast(data)); else if (size == dpx::kInt) return ReadImageBlock(dpxHeader, readBuf, fd, element, block, reinterpret_cast(data)); else if (size == dpx::kFloat) return ReadImageBlock(dpxHeader, readBuf, fd, element, block, reinterpret_cast(data)); else if (size == dpx::kDouble) return ReadImageBlock(dpxHeader, readBuf, fd, element, block, reinterpret_cast(data)); // should not reach here return false; } #ifdef RLE_WORKING // THIS IS PART OF THE INCOMPLETE RLE CODE // src is full image without any eoln padding template void CopyImageBlock(const Header &dpxHeader, const int element, SRC *src, DataSize srcSize, DST *dst, DataSize dstSize, const Block &block) { const int numberOfComponents = dpxHeader.ImageElementComponentCount(element); const int width = dpxHeader.Width(); const int byteCount = dpxHeader.ComponentByteCount(element); const int pixelByteCount = numberOfComponents * byteCount; int srcoff, dstoff; int x, y, nc; if (srcSize == dstSize) { int lineSize = (width * numberOfComponents * byteCount); U8 * srcU8 = reinterpret_cast(src); U8 * dstU8 = reinterpret_cast(dst); for (y = block.y1; y <= block.y2; y++) { int copySize = (block.x2 - block.x1 + 1) * numberOfComponents * byteCount; memcpy(srcU8 + (y * lineSize) + (block.x1 * numberOfComponents * byteCount), dstU8, copySize); outBuf += copySize; } return; } for (y = block.y1; y <= block.y2; y++) { dstoff = (y - block.y1) * ((block.x2 - block.x1 + 1) * numberOfComponents) - block.x1; for (x = block.x1; x <= block.x2; x++) { for (nc = 0; nc < numberOfComponents; nc++) { SRC d1 = src[(y * width * numberOfComponents) + (x * numberOfComponents) + nc]; BaseTypeConverter(d1, dst[dstoff+((x-block.x1)*numberOfComponents) + nc]); } } } } template void CopyImageBlock(const Header &dpxHeader, const int element, SRC *src, DataSize srcSize, void *dst, DataSize dstSize, const Block &block) { if (dstSize == dpx::kByte) CopyImageBlock(dpxHeader, element, src, srcSize, reinterpret_cast(dst), dstSize, block); else if (dstSize == dpx::kWord) CopyImageBlock(dpxHeader, element, src, srcSize, reinterpret_cast(dst), dstSize, block); else if (dstSize == dpx::kInt) CopyImageBlock(dpxHeader, element, src, srcSize, reinterpret_cast(dst), dstSize, block); else if (dstSize == dpx::kFloat) CopyImageBlock(dpxHeader, element, src, srcSize, reinterpret_cast(dst), dstSize, block); else if (dstSize == dpx::kDouble) CopyImageBlock(dpxHeader, element, src, srcSize, reinterpret_cast(dst), dstSize, block); } void CopyImageBlock(const Header &dpxHeader, const int element, void *src, DataSize srcSize, void *dst, DataSize dstSize, const Block &block) { if (srcSize == dpx::kByte) CopyImageBlock(dpxHeader, element, reinterpret_cast(src), srcSize, dst, dstSize, block); else if (srcSize == dpx::kWord) CopyImageBlock(dpxHeader, element, reinterpret_cast(src), srcSize, dst, dstSize, block); else if (srcSize == dpx::kInt) CopyImageBlock(dpxHeader, element, reinterpret_cast(src), srcSize, dst, dstSize, block); else if (srcSize == dpx::kFloat) CopyImageBlock(dpxHeader, element, reinterpret_cast(src), srcSize, dst, dstSize, block); else if (srcSize == dpx::kDouble) CopyImageBlock(dpxHeader, element, reinterpret_cast(src), srcSize, dst, dstSize, block); } #endif } #endif openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/ElementReadStream.cpp0000644000175000017500000000706213151711064024654 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "DPX.h" #include "EndianSwap.h" #include "ElementReadStream.h" dpx::ElementReadStream::ElementReadStream(InStream *fd) : fd(fd) { } dpx::ElementReadStream::~ElementReadStream() { } void dpx::ElementReadStream::Reset() { } bool dpx::ElementReadStream::Read(const dpx::Header &dpxHeader, const int element, const long offset, void * buf, const size_t size) { long position = dpxHeader.DataOffset(element) + offset; // seek to the memory position if (this->fd->Seek(position, InStream::kStart) == false) return false; // read in the data, calculate buffer offset if (this->fd->Read(buf, size) != size) return false; // swap the bytes if different byte order this->EndianDataCheck(dpxHeader, element, buf, size); return true; } bool dpx::ElementReadStream::ReadDirect(const dpx::Header &dpxHeader, const int element, const long offset, void * buf, const size_t size) { long position = dpxHeader.DataOffset(element) + offset; // seek to the memory position if (this->fd->Seek(position, InStream::kStart) == false) return false; // read in the data, calculate buffer offset if (this->fd->ReadDirect(buf, size) != size) return false; // swap the bytes if different byte order this->EndianDataCheck(dpxHeader, element, buf, size); return true; } void dpx::ElementReadStream::EndianDataCheck(const dpx::Header &dpxHeader, const int element, void *buf, const size_t size) { if (dpxHeader.RequiresByteSwap()) { switch (dpxHeader.BitDepth(element)) { case 8: break; case 12: if (dpxHeader.ImagePacking(element) == dpx::kPacked) dpx::EndianSwapImageBuffer(buf, size / sizeof(U32)); else dpx::EndianSwapImageBuffer(buf, size / sizeof(U16)); break; case 16: dpx::EndianSwapImageBuffer(buf, size / sizeof(U16)); break; default: // 10-bit, 32-bit, 64-bit dpx::EndianSwapImageBuffer(buf, size / sizeof(U32)); } } } openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/Reader.cpp0000644000175000017500000001353713151711064022521 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "DPX.h" #include "EndianSwap.h" #include "ReaderInternal.h" #include "ElementReadStream.h" #include "Codec.h" #include "RunLengthEncoding.h" dpx::Reader::Reader() : fd(0), rio(0) { // initialize all of the Codec* to NULL for (int i = 0; i < MAX_ELEMENTS; i++) this->codex[i] = 0; } dpx::Reader::~Reader() { this->Reset(); delete this->rio; } void dpx::Reader::Reset() { // delete all of the Codec * entries for (int i = 0; i < MAX_ELEMENTS; i++) if (this->codex[i]) { delete codex[i]; this->codex[i] = 0; } // Element Reader if (this->rio) { delete rio; this->rio = 0; } if (this->fd) this->rio = new ElementReadStream(this->fd); } void dpx::Reader::SetInStream(InStream *fd) { this->fd = fd; this->Reset(); } bool dpx::Reader::ReadHeader() { return this->header.Read(this->fd); } bool dpx::Reader::ReadImage(const int element, void *data) { Block block(0, 0, this->header.Width()-1, this->header.Height()-1); return this->ReadBlock(element, (unsigned char *)data, block); } /* implementation notes: dpx::readBlock reads in the image starting from the beginning of the channel. This can be optimized if the image isn't encoded; we can skip forward in the file to close to the start of (block.x1, block.y1) and determine exactly which bit will be the start. This certainly will save some time for cases where we are only reading ROIs (regions of interest). */ bool dpx::Reader::ReadBlock(const int element, unsigned char *data, Block &block) { // make sure the range is good if (element < 0 || element >= MAX_ELEMENTS) return false; // make sure the entry is valid if (this->header.ImageDescriptor(element) == kUndefinedDescriptor) return false; // get the number of components for this element descriptor const int numberOfComponents = this->header.ImageElementComponentCount(element); // bit depth of the image element const int bitDepth = this->header.BitDepth(element); // rle encoding? const bool rle = (this->header.ImageEncoding(element) == kRLE); // data size for the element const DataSize size = this->header.ComponentDataSize(element); // lets see if this can be done in a single fast read if (!rle && this->header.EndOfLinePadding(element) == 0 && ((bitDepth == 8 && size == dpx::kByte) || (bitDepth == 16 && size == dpx::kWord) || (bitDepth == 32 && size == dpx::kFloat) || (bitDepth == 64 && size == dpx::kDouble)) && block.x1 == 0 && block.x2 == (int)(this->header.Width()-1)) { // seek to the beginning of the image block if (this->fd->Seek((this->header.DataOffset(element) + (block.y1 * this->header.Width() * (bitDepth / 8) * numberOfComponents)), InStream::kStart) == false) return false; // size of the image const size_t imageSize = this->header.Width() * (block.y2 - block.y1 + 1) * numberOfComponents; const size_t imageByteSize = imageSize * bitDepth / 8; size_t rs = this->fd->ReadDirect(data, imageByteSize); if (rs != imageByteSize) return false; // swap the bytes if different byte order if (this->header.RequiresByteSwap()) dpx::EndianSwapImageBuffer(size, data, imageSize); return true; } // determine if the encoding system is loaded if (this->codex[element] == 0) { // this element reader has not been used if (rle) // TODO //this->codex[element] = new RunLengthEncoding; return false; else this->codex[element] = new Codec; } // read the image block return this->codex[element]->Read(this->header, this->rio, element, block, data, size); } bool dpx::Reader::ReadUserData(unsigned char *data) { // check to make sure there is some user data if (this->header.UserSize() == 0) return true; // seek to the beginning of the user data block if (this->fd->Seek(sizeof(GenericHeader) + sizeof(IndustryHeader), InStream::kStart) == false) return false; size_t rs = this->fd->ReadDirect(data, this->header.UserSize()); if (rs != this->header.UserSize()) return false; return true; } openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/DPXHeader.cpp0000644000175000017500000005000513151711064023052 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include "DPXHeader.h" #include "EndianSwap.h" // function prototypes static void EmptyString(char *, const int); char Hex(char x) { if (x >= 10) return (x-10+'A'); else return (x+'0'); } dpx::Header::Header() : GenericHeader(), IndustryHeader(), datumSwap(true) { } dpx::GenericHeader::GenericHeader() { this->Reset(); } void dpx::GenericHeader::Reset() { // File Information this->magicNumber = MAGIC_COOKIE; this->imageOffset = ~0; EmptyString(this->version, sizeof(this->version)); OIIO::Strutil::safe_strcpy(this->version, SMPTE_VERSION, sizeof(this->version)); fileSize = sizeof(dpx::Header); this->dittoKey = 1; // new // genericSize is the size of the file/image/orientation headers // sizeof(dpx::GenericHeader) won't give the correct results because // of compiler padding // file header is 768 bytes // image header is 640 bytes // orientation header 256 bytes this->genericSize = 768 + 640 + 256; // industrySize is the size of the motion picture/television headers // motion picture header is 256 bytes // television header is 128 bytes this->industrySize = 256 + 128; this->userSize = 0; EmptyString(this->fileName, sizeof(this->fileName)); EmptyString(this->creationTimeDate, sizeof(this->creationTimeDate)); EmptyString(this->creator, sizeof(this->creator)); EmptyString(this->project, sizeof(this->project)); EmptyString(this->copyright, sizeof(this->copyright)); this->encryptKey = 0xffffffff; EmptyString(this->reserved1, sizeof(this->reserved1)); // Image Information this->imageOrientation = kUndefinedOrientation; this->numberOfElements = 0xffff; this->pixelsPerLine = this->linesPerElement = 0xffffffff; EmptyString(this->reserved2, sizeof(this->reserved2)); // Image Orientation this->xOffset = this->yOffset = 0xffffffff; this->xCenter = this->yCenter = std::numeric_limits::quiet_NaN(); this->xOriginalSize = this->yOriginalSize = 0xffffffff; EmptyString(this->sourceImageFileName, sizeof(this->sourceImageFileName)); EmptyString(this->sourceTimeDate, sizeof(this->sourceTimeDate)); EmptyString(this->inputDevice, sizeof(this->inputDevice)); EmptyString(this->inputDeviceSerialNumber, sizeof(this->inputDeviceSerialNumber)); this->border[0] = this->border[1] = this->border[2] = this->border[3] = 0xffff; this->aspectRatio[0] = this->aspectRatio[1] = 0xffffffff; this->xScannedSize = this->yScannedSize = std::numeric_limits::quiet_NaN(); EmptyString(this->reserved3, sizeof(this->reserved3)); } dpx::IndustryHeader::IndustryHeader() { this->Reset(); } void dpx::IndustryHeader::Reset() { // Motion Picture Industry Specific EmptyString(this->filmManufacturingIdCode, sizeof(this->filmManufacturingIdCode)); EmptyString(this->filmType, sizeof(this->filmType)); EmptyString(this->perfsOffset, sizeof(this->perfsOffset)); EmptyString(this->prefix, sizeof(this->prefix)); EmptyString(this->count, sizeof(this->count)); EmptyString(this->format, sizeof(this->format)); this->framePosition = this->sequenceLength = this->heldCount = 0xffffffff; this->frameRate = this->shutterAngle = std::numeric_limits::quiet_NaN(); EmptyString(this->frameId, sizeof(this->frameId)); EmptyString(this->slateInfo, sizeof(this->slateInfo)); EmptyString(this->reserved4, sizeof(this->reserved4)); // Television Industry Specific this->timeCode = this->userBits = 0xffffffff; this->interlace = this->fieldNumber = 0xff; this->videoSignal = kUndefined; this->zero = 0xff; this->horizontalSampleRate = this->verticalSampleRate = this->temporalFrameRate = std::numeric_limits::quiet_NaN(); this->timeOffset = this->gamma = std::numeric_limits::quiet_NaN(); this->blackLevel = this->blackGain = std::numeric_limits::quiet_NaN(); this->breakPoint = this->whiteLevel = this->integrationTimes = std::numeric_limits::quiet_NaN(); EmptyString(this->reserved5, sizeof(this->reserved5)); } dpx::ImageElement::ImageElement() { this->dataSign = 0xffffffff; this->lowData = 0xffffffff; this->lowQuantity = 0xffffffff; this->highData = 0xffffffff; this->highQuantity = 0xffffffff; this->descriptor = kUndefinedDescriptor; this->transfer = kUndefinedCharacteristic; this->colorimetric = kUndefinedCharacteristic; this->bitDepth = 0xff; this->packing = this->encoding = 0xffff; this->dataOffset = this->endOfLinePadding = this->endOfImagePadding = 0xffffffff; EmptyString(this->description, sizeof(this->description)); } bool dpx::Header::Read(InStream *io) { // rewind file io->Rewind(); // read in the header from the file size_t r = sizeof(GenericHeader) + sizeof(IndustryHeader); if (io->Read(&(this->magicNumber), r) != r) return false; // validate return this->Validate(); } // Check to see if the compiler placed the data members in the expected memory offsets bool dpx::Header::Check() { // genericSize is the size of the file/image/orientation headers // sizeof(dpx::GenericHeader) won't give the correct results because // of compiler padding // file header is 768 bytes // image header is 640 bytes // orientation header 256 bytes if (sizeof(GenericHeader) != (768 + 640 + 256)) return false; // industrySize is the size of the motion picture/television headers // motion picture header is 256 bytes // television header is 128 bytes if (sizeof(IndustryHeader) != (256 + 128)) return false; // data size checks if (sizeof(U8) != 1 || sizeof(U16) != 2 || sizeof(U32) != 4 || sizeof(R32) != 4 || sizeof(R64) != 8) return false; return true; } bool dpx::Header::Write(OutStream *io) { // validate and byte swap, if necessary if (!this->Validate()) return false; // write the header to the file size_t r = sizeof(GenericHeader) + sizeof(IndustryHeader); if (io->Write(&(this->magicNumber), r) != r) return false; // swap back - data is in file, now we need it native again this->Validate(); return true; } bool dpx::Header::WriteOffsetData(OutStream *io) { // calculate the number of elements this->CalculateNumberOfElements(); // write the image offset const long FIELD2 = 4; // offset to image in header if (io->Seek(FIELD2, OutStream::kStart) == false) return false; if (this->RequiresByteSwap()) SwapBytes(this->imageOffset); if (io->Write(&this->imageOffset, sizeof(U32)) == false) return false; if (this->RequiresByteSwap()) SwapBytes(this->imageOffset); // write the file size const long FIELD4 = 16; // offset to total image file size in header if (io->Seek(FIELD4, OutStream::kStart) == false) return false; if (this->RequiresByteSwap()) SwapBytes(this->fileSize); if (io->Write(&this->fileSize, sizeof(U32)) == false) return false; if (this->RequiresByteSwap()) SwapBytes(this->fileSize); // write the number of elements const long FIELD19 = 770; // offset to number of image elements in header if (io->Seek(FIELD19, OutStream::kStart) == false) return false; if (this->RequiresByteSwap()) SwapBytes(this->numberOfElements); if (io->Write(&this->numberOfElements, sizeof(U16)) == false) return false; if (this->RequiresByteSwap()) SwapBytes(this->numberOfElements); // write the image offsets const long FIELD21_12 = 808; // offset to image offset in image element data structure const long IMAGE_STRUCTURE = 72; // sizeof the image data structure int i; for (i = 0; i < MAX_ELEMENTS; i++) { // only write if there is a defined image description if (this->chan[i].descriptor == kUndefinedDescriptor) continue; // seek to the image offset entry in each image element if (io->Seek((FIELD21_12 + (IMAGE_STRUCTURE * i)), OutStream::kStart) == false) return false; // write if (this->RequiresByteSwap()) SwapBytes(this->chan[i].dataOffset); if (io->Write(&this->chan[i].dataOffset, sizeof(U32)) == false) return false; if (this->RequiresByteSwap()) SwapBytes(this->chan[i].dataOffset); } return true; } bool dpx::Header::ValidMagicCookie(const U32 magic) { U32 mc = MAGIC_COOKIE; if (magic == mc) return true; else if (magic == SwapBytes(mc)) return true; else return false; } bool dpx::Header::DetermineByteSwap(const U32 magic) const { U32 mc = MAGIC_COOKIE; bool byteSwap = false; if (magic != mc) byteSwap = true; return byteSwap; } bool dpx::Header::Validate() { // check magic cookie if (!this->ValidMagicCookie(this->magicNumber)) return false; // determine if bytes needs to be swapped around if (this->DetermineByteSwap(this->magicNumber)) { // File information SwapBytes(this->imageOffset); SwapBytes(this->fileSize); SwapBytes(this->dittoKey); SwapBytes(this->genericSize); SwapBytes(this->industrySize); SwapBytes(this->userSize); SwapBytes(this->encryptKey); // Image information SwapBytes(this->imageOrientation); SwapBytes(this->numberOfElements); SwapBytes(this->pixelsPerLine); SwapBytes(this->linesPerElement); for (int i = 0; i < MAX_ELEMENTS; i++) { SwapBytes(this->chan[i].dataSign); SwapBytes(this->chan[i].lowData); SwapBytes(this->chan[i].lowQuantity); SwapBytes(this->chan[i].highData); SwapBytes(this->chan[i].highQuantity); SwapBytes(this->chan[i].descriptor); SwapBytes(this->chan[i].transfer); SwapBytes(this->chan[i].colorimetric); SwapBytes(this->chan[i].bitDepth); SwapBytes(this->chan[i].packing); SwapBytes(this->chan[i].encoding); SwapBytes(this->chan[i].dataOffset); SwapBytes(this->chan[i].endOfLinePadding); SwapBytes(this->chan[i].endOfImagePadding); } // Image Origination information SwapBytes(this->xOffset); SwapBytes(this->yOffset); SwapBytes(this->xCenter); SwapBytes(this->yCenter); SwapBytes(this->xOriginalSize); SwapBytes(this->yOriginalSize); SwapBytes(this->border[0]); SwapBytes(this->border[1]); SwapBytes(this->border[2]); SwapBytes(this->border[3]); SwapBytes(this->aspectRatio[0]); SwapBytes(this->aspectRatio[1]); // Motion Picture Industry Specific SwapBytes(this->framePosition); SwapBytes(this->sequenceLength); SwapBytes(this->heldCount); SwapBytes(this->frameRate); SwapBytes(this->shutterAngle); // Television Industry Specific SwapBytes(this->timeCode); SwapBytes(this->userBits); SwapBytes(this->interlace); SwapBytes(this->fieldNumber); SwapBytes(this->videoSignal); SwapBytes(this->zero); SwapBytes(this->horizontalSampleRate); SwapBytes(this->verticalSampleRate); SwapBytes(this->temporalFrameRate); SwapBytes(this->timeOffset); SwapBytes(this->gamma); SwapBytes(this->blackLevel); SwapBytes(this->blackGain); SwapBytes(this->breakPoint); SwapBytes(this->whiteLevel); SwapBytes(this->integrationTimes); } return true; } void dpx::Header::Reset() { GenericHeader::Reset(); IndustryHeader::Reset(); } int dpx::GenericHeader::ImageElementComponentCount(const int element) const { int count = 1; switch (this->chan[element].descriptor) { case kUserDefinedDescriptor: case kRed: case kGreen: case kBlue: case kAlpha: case kLuma: case kColorDifference: case kDepth: count = 1; break; case kCompositeVideo: count = 1; break; case kRGB: count = 3; break; case kRGBA: case kABGR: count = 4; break; case kCbYCrY: count = 2; break; case kCbYACrYA: count = 3; break; case kCbYCr: count = 3; break; case kCbYCrA: count = 4; break; case kUserDefined2Comp: count = 2; break; case kUserDefined3Comp: count = 3; break; case kUserDefined4Comp: count = 4; break; case kUserDefined5Comp: count = 5; break; case kUserDefined6Comp: count = 6; break; case kUserDefined7Comp: count = 7; break; case kUserDefined8Comp: count = 8; break; }; return count; } int dpx::GenericHeader::ImageElementCount() const { if(this->numberOfElements>0 && this->numberOfElements<=MAX_ELEMENTS) return this->numberOfElements; // If the image header does not list a valid number of elements, // count how many defined image descriptors we have... int i = 0; while (i < MAX_ELEMENTS ) { if (this->ImageDescriptor(i) == kUndefinedDescriptor) break; i++; } return i; } void dpx::GenericHeader::CalculateNumberOfElements() { this->numberOfElements = 0xffff; int i = this->ImageElementCount(); if (i == 0) this->numberOfElements = 0xffff; else this->numberOfElements = U16(i); } void dpx::Header::CalculateOffsets() { int i; for (i = 0; i < MAX_ELEMENTS; i++) { // only write if there is a defined image description if (this->chan[i].descriptor == kUndefinedDescriptor) continue; } } dpx::DataSize dpx::GenericHeader::ComponentDataSize(const int element) const { if (element < 0 || element >= MAX_ELEMENTS) return kByte; dpx::DataSize ret; switch (this->chan[element].bitDepth) { case 8: ret = kByte; break; case 10: case 12: case 16: ret = kWord; break; case 32: ret = kFloat; break; case 64: ret = kDouble; break; default: assert(0 && "Unknown bit depth"); ret = kDouble; break; } return ret; } int dpx::GenericHeader::ComponentByteCount(const int element) const { if (element < 0 || element >= MAX_ELEMENTS) return kByte; int ret; switch (this->chan[element].bitDepth) { case 8: ret = sizeof(U8); break; case 10: case 12: case 16: ret = sizeof(U16); break; case 32: ret = sizeof(R32); break; case 64: ret = sizeof(R64); break; default: assert(0 && "Unknown bit depth"); ret = sizeof(R64); break; } return ret; } int dpx::GenericHeader::DataSizeByteCount(const DataSize ds) { int ret; switch (ds) { case kByte: ret = sizeof(U8); break; case kWord: ret = sizeof(U16); break; case kInt: ret = sizeof(U32); break; case kFloat: ret = sizeof(R32); break; case kDouble: ret = sizeof(R64); break; default: assert(0 && "Unknown data size"); ret = sizeof(R64); break; } return ret; } void dpx::IndustryHeader::FilmEdgeCode(char *edge) const { edge[0] = this->filmManufacturingIdCode[0]; edge[1] = this->filmManufacturingIdCode[1]; edge[2] = this->filmType[0]; edge[3] = this->filmType[1]; edge[4] = this->perfsOffset[0]; edge[5] = this->perfsOffset[1]; edge[6] = this->prefix[0]; edge[7] = this->prefix[1]; edge[8] = this->prefix[2]; edge[9] = this->prefix[3]; edge[10] = this->prefix[4]; edge[11] = this->prefix[5]; edge[12] = this->count[0]; edge[13] = this->count[1]; edge[14] = this->count[2]; edge[15] = this->count[3]; edge[16] = '\0'; } void dpx::IndustryHeader::SetFileEdgeCode(const char *edge) { this->filmManufacturingIdCode[0] = edge[0]; this->filmManufacturingIdCode[1] = edge[1]; this->filmType[0] = edge[2]; this->filmType[1] = edge[3]; this->perfsOffset[0] = edge[4]; this->perfsOffset[1] = edge[5]; this->prefix[0] = edge[6]; this->prefix[1] = edge[7]; this->prefix[2] = edge[8]; this->prefix[3] = edge[9]; this->prefix[4] = edge[10]; this->prefix[5] = edge[11]; this->count[0] = edge[12]; this->count[1] = edge[13]; this->count[2] = edge[14]; this->count[3] = edge[15]; } void dpx::IndustryHeader::TimeCode(char *str) const { U32 tc = this->timeCode; ::sprintf(str, "%c%c:%c%c:%c%c:%c%c", Hex((tc & 0xf0000000) >> 28), Hex((tc & 0xf000000) >> 24), Hex((tc & 0xf00000) >> 20), Hex((tc & 0xf0000) >> 16), Hex((tc & 0xf000) >> 12), Hex((tc & 0xf00) >> 8), Hex((tc & 0xf0) >> 4), Hex(tc & 0xf)); } void dpx::IndustryHeader::UserBits(char *str) const { U32 ub = this->userBits; ::sprintf(str, "%c%c:%c%c:%c%c:%c%c", Hex((ub & 0xf0000000) >> 28), Hex((ub & 0xf000000) >> 24), Hex((ub & 0xf00000) >> 20), Hex((ub & 0xf0000) >> 16), Hex((ub & 0xf000) >> 12), Hex((ub & 0xf00) >> 8), Hex((ub & 0xf0) >> 4), Hex(ub & 0xf)); } dpx::U32 dpx::IndustryHeader::TCFromString(const char *str) const { // make sure the string is the correct length if (::strlen(str) != 11) return U32(~0); U32 tc = 0; int i, idx = 0; U8 ch; U32 value, mask; for (i = 0; i < 8; i++, idx++) { // determine string index skipping : idx += idx % 3 == 2 ? 1 : 0; ch = str[idx]; // error check if (ch < '0' || ch > '9') return 0xffffffff; value = U32(ch - '0') << (28 - (i*4)); mask = 0xf << (28 - (i*4)); // mask in new value tc = (tc & ~mask) | (value & mask); } return tc; } void dpx::IndustryHeader::SetTimeCode(const char *str) { U32 tc = this->TCFromString(str); if (tc != 0xffffffff) this->timeCode = tc; } void dpx::IndustryHeader::SetUserBits(const char *str) { U32 ub = this->TCFromString(str); if (ub != 0xffffffff) this->userBits = ub; } static void EmptyString(char *str, const int len) { for (int i = 0; i < len; i++) str[i] = '\0'; } void dpx::GenericHeader::SetCreationTimeDate(const long sec) { struct tm *tm_time; char str[32]; #ifdef WIN32 _tzset(); #endif const time_t t = time_t(sec); tm_time = ::localtime(&t); ::strftime(str, 32, "%Y:%m:%d:%H:%M:%S%Z", tm_time); ::strncpy(this->creationTimeDate, str, 24); } void dpx::GenericHeader::SetSourceTimeDate(const long sec) { struct tm *tm_time; char str[32]; #ifdef WIN32 _tzset(); #endif const time_t t = time_t(sec); tm_time = ::localtime(&t); ::strftime(str, 32, "%Y:%m:%d:%H:%M:%S%Z", tm_time); ::strncpy(this->sourceTimeDate, str, 24); } bool dpx::Header::DatumSwap(const int element) const { if (this->datumSwap) { if (this->ImageDescriptor(element) == kRGB || this->ImageDescriptor(element) == kCbYCrY) return true; } return false; } void dpx::Header::SetDatumSwap(const bool swap) { this->datumSwap = swap; } // Height() // this function determines the height of the image taking in account for the image orientation // if an image is 1920x1080 but is oriented top to bottom, left to right then the height stored // in the image is 1920 rather than 1080 dpx::U32 dpx::Header::Height() const { U32 h; switch (this->ImageOrientation()) { case kTopToBottomLeftToRight: case kTopToBottomRightToLeft: case kBottomToTopLeftToRight: case kBottomToTopRightToLeft: h = this->PixelsPerLine(); break; default: h = this->LinesPerElement(); break; } return h; } // Width() // this function determines the width of the image taking in account for the image orientation // if an image is 1920x1080 but is oriented top to bottom, left to right then the width stored // in the image is 1920 rather than 1080 dpx::U32 dpx::Header::Width() const { U32 w; switch (this->ImageOrientation()) { case kTopToBottomLeftToRight: case kTopToBottomRightToLeft: case kBottomToTopLeftToRight: case kBottomToTopRightToLeft: w = this->LinesPerElement(); break; default: w = this->PixelsPerLine(); break; } return w; } openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/DPXColorConverter.cpp0000644000175000017500000005400513151711064024634 0ustar mfvmfv// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "DPXColorConverter.h" #include namespace dpx { template static inline bool SwapRGBABytes(const DATA *input, DATA *output, int pixels) { // copy the data that could be destroyed to an additional buffer in case input == output DATA tmp[2]; for (int i = 0; i < pixels; i++) { memcpy(tmp, &input[i * 4], sizeof(DATA) * 2); output[i * 4 + 0] = input[i * 4 + 3]; output[i * 4 + 1] = input[i * 4 + 2]; output[i * 4 + 2] = tmp[1]; output[i * 4 + 3] = tmp[0]; } return true; } // ======================================================================== // native formats -> RGB conversion // ======================================================================== static inline const float *GetYCbCrToRGBColorMatrix(const Characteristic space) { // YCbCr -> RGB matrices static const float Rec601[9] = { // multipliers for the corresponding signals // Y' Cb Cr /* R' = */ 1.f, 0.f, 1.402f, /* G' = */ 1.f, -0.344136f, -0.714136f, /* B' = */ 1.f, -0.772f, 0.f }, Rec709[9] = { // multipliers for the corresponding signals // Y' Cb Cr /* R' = */ 1.f, 0.f, 1.5748f, /* G' = */ 1.f, -0.187324f, -0.468124f, /* B' = */ 1.f, 1.8556f, 0.f }; switch (space) { // FIXME: research those constants! //case kPrintingDensity: //case kUnspecifiedVideo: case kITUR709: case kSMPTE274M: // SMPTE 247M has the same chromaticities as Rec709 return Rec709; case kITUR601: case kITUR602: return Rec601; //case kUserDefined: //case kNTSCCompositeVideo: //case kPALCompositeVideo: default: // ??? return NULL; } } template static inline void ConvertPixelYCbCrToRGB(const DATA CbYCr[3], DATA RGB[3], const float matrix[9]) { float tmp; for (int i = 0; i < 3; i++) { // dot product of matrix row and YCbCr pixel vector // chroma must be put in the [-0.5; 0.5] range tmp = matrix[i * 3 + 0] * CbYCr[1] // Y + matrix[i * 3 + 1] * ((float)CbYCr[0] - 0.5f * (float)max) // Cb + matrix[i * 3 + 2] * ((float)CbYCr[2] - 0.5f * (float)max); // Cr // for some reason the R and B channels get swapped, put them back in the correct order // prevent overflow RGB[2 - i] = std::max((DATA)0, static_cast(std::min(tmp, (float)max))); } } // 4:4:4 template static bool ConvertCbYCrToRGB(const Characteristic space, const DATA *input, DATA *output, const int pixels) { const float *matrix = GetYCbCrToRGBColorMatrix(space); if (matrix == NULL) return false; DATA RGB[3]; for (int i = 0; i < pixels; i++) { ConvertPixelYCbCrToRGB(&input[i * 3], RGB, matrix); memcpy(&output[i * 3], RGB, sizeof(DATA) * 3); } return true; } // 4:4:4:4 template static bool ConvertCbYCrAToRGBA(const Characteristic space, const DATA *input, DATA *output, const int pixels) { const float *matrix = GetYCbCrToRGBColorMatrix(space); if (matrix == NULL) return false; DATA RGBA[4]; for (int i = 0; i < pixels; i++) { ConvertPixelYCbCrToRGB(&input[i * 4], RGBA, matrix); RGBA[3] = input[i * 4 + 3]; memcpy(&output[i * 4], RGBA, sizeof(DATA) * 4); } return true; } // 4:2:2 template static bool ConvertCbYCrYToRGB(const Characteristic space, const DATA *input, DATA *output, const int pixels) { const float *matrix = GetYCbCrToRGBColorMatrix(space); if (matrix == NULL) return false; DATA CbYCr[3]; for (int i = 0; i < pixels; i++) { // upsample to 4:4:4 // FIXME: proper interpolation CbYCr[0] = input[(i | 1) * 2]; // Cb CbYCr[1] = input[i * 2 + 1]; // Y CbYCr[2] = input[(i & ~1) * 2]; // Cr // convert to RGB; we can pass a pointer into output because input must be != output ConvertPixelYCbCrToRGB(CbYCr, &output[i * 3], matrix); } return true; } // 4:2:2:4 template static bool ConvertCbYACrYAToRGBA(const Characteristic space, const DATA *input, DATA *output, const int pixels) { const float *matrix = GetYCbCrToRGBColorMatrix(space); if (matrix == NULL) return false; DATA CbYCr[3]; for (int i = 0; i < pixels; i++) { // upsample to 4:4:4 // FIXME: proper interpolation CbYCr[0] = input[(i | 1) * 3]; // Cb CbYCr[1] = input[i * 3 + 1]; // Y CbYCr[2] = input[(i & ~1) * 3]; // Cr // convert to RGBA; we can pass a pointer into output because input must be != output ConvertPixelYCbCrToRGB(CbYCr, &output[i * 4], matrix); output[i * 4 + 3] = input[i * 3 + 2]; // A } return true; } static inline bool ConvertToRGBInternal(const Descriptor desc, const DataSize size, const Characteristic space, const void *input, void *output, const int pixels) { switch (desc) { // redundant calls case kRGB: case kRGBA: return true; // needs swapping case kABGR: switch (size) { case kByte: return SwapRGBABytes((const U8 *)input, (U8 *)output, pixels); case kWord: return SwapRGBABytes((const U16 *)input, (U16 *)output, pixels); case kInt: return SwapRGBABytes((const U32 *)input, (U32 *)output, pixels); case kFloat: return SwapRGBABytes((const R32 *)input, (R32 *)output, pixels); case kDouble: return SwapRGBABytes((const R64 *)input, (R64 *)output, pixels); } // shouldn't ever get here return false; // FIXME: can this be translated to RGB? //case kCompositeVideo: case kCbYCrY: switch (size) { case kByte: return ConvertCbYCrYToRGB(space, (const U8 *)input, (U8 *)output, pixels); case kWord: return ConvertCbYCrYToRGB(space, (const U16 *)input, (U16 *)output, pixels); case kInt: return ConvertCbYCrYToRGB(space, (const U32 *)input, (U32 *)output, pixels); case kFloat: return ConvertCbYCrYToRGB(space, (const R32 *)input, (R32 *)output, pixels); case kDouble: return ConvertCbYCrYToRGB(space, (const R64 *)input, (R64 *)output, pixels); } // shouldn't ever get here return false; case kCbYCr: switch (size) { case kByte: return ConvertCbYCrToRGB(space, (const U8 *)input, (U8 *)output, pixels); case kWord: return ConvertCbYCrToRGB(space, (const U16 *)input, (U16 *)output, pixels); case kInt: return ConvertCbYCrToRGB(space, (const U32 *)input, (U32 *)output, pixels); case kFloat: return ConvertCbYCrToRGB(space, (const R32 *)input, (R32 *)output, pixels); case kDouble: return ConvertCbYCrToRGB(space, (const R64 *)input, (R64 *)output, pixels); } // shouldn't ever get here return false; case kCbYACrYA: switch (size) { case kByte: return ConvertCbYACrYAToRGBA(space, (const U8 *)input, (U8 *)output, pixels); case kWord: return ConvertCbYACrYAToRGBA(space, (const U16 *)input, (U16 *)output, pixels); case kInt: return ConvertCbYACrYAToRGBA(space, (const U32 *)input, (U32 *)output, pixels); case kFloat: return ConvertCbYACrYAToRGBA(space, (const R32 *)input, (R32 *)output, pixels); case kDouble: return ConvertCbYACrYAToRGBA(space, (const R64 *)input, (R64 *)output, pixels); } // shouldn't ever get here return false; case kCbYCrA: switch (size) { case kByte: return ConvertCbYCrAToRGBA(space, (const U8 *)input, (U8 *)output, pixels); case kWord: return ConvertCbYCrAToRGBA(space, (const U16 *)input, (U16 *)output, pixels); case kInt: return ConvertCbYCrAToRGBA(space, (const U32 *)input, (U32 *)output, pixels); case kFloat: return ConvertCbYCrAToRGBA(space, (const R32 *)input, (R32 *)output, pixels); case kDouble: return ConvertCbYCrAToRGBA(space, (const R64 *)input, (R64 *)output, pixels); } // shouldn't ever get here return false; // all the rest is either irrelevant, invalid or unsupported /*case kUserDefinedDescriptor: case kRed: case kGreen: case kBlue: case kAlpha: case kLuma: case kColorDifference: case kDepth: case kUserDefined2Comp: case kUserDefined3Comp: case kUserDefined4Comp: case kUserDefined5Comp: case kUserDefined6Comp: case kUserDefined7Comp: case kUserDefined8Comp: case kUndefinedDescriptor:*/ default: return false; } } static inline int QueryRGBBufferSizeInternal(const Descriptor desc, const int pixels, const int bytes) { switch (desc) { //case kCompositeVideo: // FIXME: can this be translated to RGB? case kCbYCrY: // 4:2:2 -> RGB, requires allocation return pixels * 3 * bytes; case kCbYCr: // 4:4:4 -> RGB, can get away with sviweling case kRGB: // redundant return pixels * -3 * bytes; case kCbYACrYA: // 4:2:2:4 -> RGBA, requires allocation return pixels * 4 * bytes; case kCbYCrA: // 4:4:4:4 -> RGBA, can get away with sviweling case kRGBA: // redundant case kABGR: // only needs swapping return pixels * -4 * bytes; // all the rest is either irrelevant, invalid or unsupported /*case kUserDefinedDescriptor: case kRed: case kGreen: case kBlue: case kAlpha: case kLuma: case kColorDifference: case kDepth: case kUserDefined2Comp: case kUserDefined3Comp: case kUserDefined4Comp: case kUserDefined5Comp: case kUserDefined6Comp: case kUserDefined7Comp: case kUserDefined8Comp: case kUndefinedDescriptor:*/ default: return 0; } } int QueryRGBBufferSize(const Header &header, const int element, const Block &block) { return QueryRGBBufferSizeInternal(header.ImageDescriptor(element), (block.x2 - block.x1 + 1) * (block.y2 - block.y1 + 1), header.ComponentByteCount(element)); } int QueryRGBBufferSize(const Header &header, const int element) { return QueryRGBBufferSizeInternal(header.ImageDescriptor(element), header.Width() * header.Height(), header.ComponentByteCount(element)); } bool ConvertToRGB(const Header &header, const int element, const void *input, void *output, const Block &block) { return ConvertToRGBInternal(header.ImageDescriptor(element), header.ComponentDataSize(element), header.Colorimetric(element), input, output, (block.x2 - block.x1 + 1) * (block.y2 - block.y1 + 1)); } bool ConvertToRGB(const Header &header, const int element, const void *input, void *output) { return ConvertToRGBInternal(header.ImageDescriptor(element), header.ComponentDataSize(element), header.Colorimetric(element), input, output, header.Width() * header.Height()); } // ======================================================================== // RGB -> native formats conversion // ======================================================================== static inline const float *GetRGBToYCbCrColorMatrix(const Characteristic space) { // RGB -> YCbCr matrices static const float Rec601[9] = { // multipliers for the corresponding signals // R' G' B' /* Cb = */ -0.168736f, -0.331264f, 0.5f, /* Y' = */ 0.299f, 0.587f, 0.114f, /* Cr = */ 0.5f, -0.418688f, -0.081312f }, Rec709[9] = { // multipliers for the corresponding signals // R' G' B' /* Cb = */ -0.114572f, -0.385428f, 0.5f, /* Y' = */ 0.2126f, 0.7152f, 0.0722f, /* Cr = */ 0.5f, -0.454153f, -0.045847f }; switch (space) { // FIXME: research those constants! //case kPrintingDensity: //case kUnspecifiedVideo: case kITUR709: case kSMPTE274M: // SMPTE 247M has the same chromaticities as Rec709 return Rec709; case kITUR601: case kITUR602: return Rec601; //case kUserDefined: //case kNTSCCompositeVideo: //case kPALCompositeVideo: default: // ??? return NULL; } } template static inline void ConvertPixelRGBToYCbCr(const DATA RGB[3], DATA CbYCr[3], const float matrix[9]) { float tmp; for (int i = 0; i < 3; i++) { // dot product of matrix row and RGB pixel vector tmp = matrix[i * 3 + 0] * RGB[0] + matrix[i * 3 + 1] * RGB[1] + matrix[i * 3 + 2] * RGB[2]; // chroma (indices 0 and 2) must be put in the [0; 1] range if (i != 1) tmp += 0.5f * (float)max; // prevent overflow CbYCr[i] = std::max((DATA)0, static_cast(std::min(tmp, (float)max))); } } // 4:4:4 template static bool ConvertRGBToCbYCr(const Characteristic space, const DATA *input, DATA *output, const int pixels) { const float *matrix = GetRGBToYCbCrColorMatrix(space); if (matrix == NULL) return false; DATA CbYCr[3]; for (int i = 0; i < pixels; i++) { ConvertPixelRGBToYCbCr(&input[i * 3], CbYCr, matrix); memcpy(&output[i * 3], CbYCr, sizeof(DATA) * 3); } return true; } // 4:4:4:4 template static bool ConvertRGBAToCbYCrA(const Characteristic space, const DATA *input, DATA *output, const int pixels) { const float *matrix = GetRGBToYCbCrColorMatrix(space); if (matrix == NULL) return false; DATA CbYCrA[4]; for (int i = 0; i < pixels; i++) { ConvertPixelRGBToYCbCr(&input[i * 4], CbYCrA, matrix); CbYCrA[3] = input[i * 4 + 3]; memcpy(&output[i * 4], CbYCrA, sizeof(DATA) * 4); } return true; } // 4:2:2 template static bool ConvertRGBToCbYCrY(const Characteristic space, const DATA *input, DATA *output, const int pixels) { const float *matrix = GetRGBToYCbCrColorMatrix(space); if (matrix == NULL) return false; DATA CbYCr[3]; for (int i = 0; i < pixels; i++) { // convert to YCbCr ConvertPixelRGBToYCbCr(&input[i * 3], CbYCr, matrix); // downsample to 4:2:2 // FIXME: proper downsampling output[i * 2 + 0] = (i & 1) == 0 ? CbYCr[0] : CbYCr[2]; output[i * 2 + 1] = CbYCr[1]; } return true; } // 4:2:2:4 template static bool ConvertRGBAToCbYACrYA(const Characteristic space, const DATA *input, DATA *output, const int pixels) { const float *matrix = GetRGBToYCbCrColorMatrix(space); if (matrix == NULL) return false; DATA CbYCr[3]; for (int i = 0; i < pixels; i++) { // convert to YCbCr ConvertPixelRGBToYCbCr(&input[i * 4], CbYCr, matrix); // downsample to 4:2:2 // FIXME: proper downsampling output[i * 3 + 0] = (i & 1) == 0 ? CbYCr[0] : CbYCr[2]; output[i * 3 + 1] = CbYCr[1]; output[i * 3 + 3] = input[i * 4 + 3]; } return true; } static inline bool ConvertToNativeInternal(const Descriptor desc, const DataSize size, const Characteristic space, const void *input, void *output, const int pixels) { switch (desc) { // redundant calls case kRGB: case kRGBA: return true; // needs swapping case kABGR: switch (size) { case kByte: return SwapRGBABytes((const U8 *)input, (U8 *)output, pixels); case kWord: return SwapRGBABytes((const U16 *)input, (U16 *)output, pixels); case kInt: return SwapRGBABytes((const U32 *)input, (U32 *)output, pixels); case kFloat: return SwapRGBABytes((const R32 *)input, (R32 *)output, pixels); case kDouble: return SwapRGBABytes((const R64 *)input, (R64 *)output, pixels); } // shouldn't ever get here return false; case kCbYCrY: switch (size) { case kByte: return ConvertRGBToCbYCrY(space, (const U8 *)input, (U8 *)output, pixels); case kWord: return ConvertRGBToCbYCrY(space, (const U16 *)input, (U16 *)output, pixels); case kInt: return ConvertRGBToCbYCrY(space, (const U32 *)input, (U32 *)output, pixels); case kFloat: return ConvertRGBToCbYCrY(space, (const R32 *)input, (R32 *)output, pixels); case kDouble: return ConvertRGBToCbYCrY(space, (const R64 *)input, (R64 *)output, pixels); } // shouldn't ever get here return false; case kCbYCr: switch (size) { case kByte: return ConvertRGBToCbYCr(space, (const U8 *)input, (U8 *)output, pixels); case kWord: return ConvertRGBToCbYCr(space, (const U16 *)input, (U16 *)output, pixels); case kInt: return ConvertRGBToCbYCr(space, (const U32 *)input, (U32 *)output, pixels); case kFloat: return ConvertRGBToCbYCr(space, (const R32 *)input, (R32 *)output, pixels); case kDouble: return ConvertRGBToCbYCr(space, (const R64 *)input, (R64 *)output, pixels); } // shouldn't ever get here return false; case kCbYACrYA: switch (size) { case kByte: return ConvertRGBAToCbYACrYA(space, (const U8 *)input, (U8 *)output, pixels); case kWord: return ConvertRGBAToCbYACrYA(space, (const U16 *)input, (U16 *)output, pixels); case kInt: return ConvertRGBAToCbYACrYA(space, (const U32 *)input, (U32 *)output, pixels); case kFloat: return ConvertRGBAToCbYACrYA(space, (const R32 *)input, (R32 *)output, pixels); case kDouble: return ConvertRGBAToCbYACrYA(space, (const R64 *)input, (R64 *)output, pixels); } // shouldn't ever get here return false; case kCbYCrA: switch (size) { case kByte: return ConvertRGBAToCbYCrA(space, (const U8 *)input, (U8 *)output, pixels); case kWord: return ConvertRGBAToCbYCrA(space, (const U16 *)input, (U16 *)output, pixels); case kInt: return ConvertRGBAToCbYCrA(space, (const U32 *)input, (U32 *)output, pixels); case kFloat: return ConvertRGBAToCbYCrA(space, (const R32 *)input, (R32 *)output, pixels); case kDouble: return ConvertRGBAToCbYCrA(space, (const R64 *)input, (R64 *)output, pixels); } // shouldn't ever get here return false; // all the rest is either irrelevant, invalid or unsupported /*case kUserDefinedDescriptor: case kRed: case kGreen: case kBlue: case kAlpha: case kLuma: case kColorDifference: case kCompositeVideo: case kDepth: case kUserDefined2Comp: case kUserDefined3Comp: case kUserDefined4Comp: case kUserDefined5Comp: case kUserDefined6Comp: case kUserDefined7Comp: case kUserDefined8Comp: case kUndefinedDescriptor:*/ default: return false; } } static inline int QueryNativeBufferSizeInternal(const Descriptor desc, const int pixels, const DataSize compSize) { int bytes = compSize == kByte ? 1 : compSize == kWord ? 2 : compSize == kDouble ? 8 : 4; switch (desc) { case kCbYCrY: // RGB -> 4:2:2, requires allocation return pixels * 2 * bytes; case kCbYCr: // RGB -> 4:4:4, can get away with sviweling case kRGB: // redundant return pixels * -3 * bytes; case kCbYACrYA: // RGBA -> 4:2:2:4, requires allocation return pixels * 4 * bytes; case kCbYCrA: // RGBA -> 4:4:4:4, can get away with sviweling case kRGBA: // redundant case kABGR: // only needs swapping return pixels * -4 * bytes; // all the rest is either irrelevant, invalid or unsupported /*case kUserDefinedDescriptor: case kRed: case kGreen: case kBlue: case kAlpha: case kLuma: case kColorDifference: case kDepth: case kCompositeVideo: case kUserDefined2Comp: case kUserDefined3Comp: case kUserDefined4Comp: case kUserDefined5Comp: case kUserDefined6Comp: case kUserDefined7Comp: case kUserDefined8Comp: case kUndefinedDescriptor:*/ default: return 0; } } int QueryNativeBufferSize(const Descriptor desc, const DataSize compSize, const Block &block) { return QueryNativeBufferSizeInternal(desc, (block.x2 - block.x1 + 1) * (block.y2 - block.y1 + 1), compSize); } int QueryNativeBufferSize(const Descriptor desc, const DataSize compSize, const int width, const int height) { return QueryNativeBufferSizeInternal(desc, width * height, compSize); } bool ConvertToNative(const Descriptor desc, const DataSize compSize, const Characteristic cmetr, const void *input, void *output, const Block &block) { return ConvertToNativeInternal(desc, compSize, cmetr, input, output, (block.x2 - block.x1 + 1) * (block.y2 - block.y1 + 1)); } bool ConvertToNative(const Descriptor desc, const DataSize compSize, const Characteristic cmetr, const int width, const int height, const void *input, void *output) { return ConvertToNativeInternal(desc, compSize, cmetr, input, output, width * height); } } openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/libdpx/DPXStream.h0000644000175000017500000001044313151711064022564 0ustar mfvmfv/// -*- mode: C++; tab-width: 4 -*- // vi: ts=4 /*! \file DPXStream.h */ /* * Copyright (c) 2009, Patrick A. Palmer. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Patrick A. Palmer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _DPX_DPXSTREAM_H #define _DPX_DPXSTREAM_H 1 #include /*! * \class InStream * \brief Input Stream for reading files */ class InStream { public: /*! * \enum Origin * \brief file pointing positioning offset */ enum Origin { kStart, //!< beginning of the file kCurrent, //!< current file pointer kEnd //!< end of the file }; /*! * \brief Constructor */ InStream(); /*! * \brief Destructor */ virtual ~InStream(); /*! * \brief Open file * \param fn File name * \return success true/false */ virtual bool Open(const char * fn); /*! * \brief Close file */ virtual void Close(); /*! * \brief Rewind file pointer to beginning of file */ virtual void Rewind(); /*! * \brief Read data from file * \param buf data buffer * \param size bytes to read * \return number of bytes read */ virtual size_t Read(void * buf, const size_t size); /*! * \brief Read data from file without any buffering as fast as possible * \param buf data buffer * \param size bytes to read * \return number of bytes read */ virtual size_t ReadDirect(void * buf, const size_t size); /*! * \brief Query if end of file has been reached * \return end of file true/false */ virtual bool EndOfFile() const; /*! * \brief Seek to a position in the file * \param offset offset from originating position * \param origin originating position * \return success true/false */ virtual bool Seek(long offset, Origin origin); protected: FILE *fp; }; /*! * \class OutStream * \brief Output Stream for writing files */ class OutStream { public: /*! * \enum Origin * \brief file pointing positioning offset */ enum Origin { kStart, //!< beginning of the file kCurrent, //!< current file pointer kEnd //!< end of the file }; /*! * \brief Constructor */ OutStream(); /*! * \brief Destructor */ virtual ~OutStream(); /*! * \brief Open file * \param fn File name * \return success true/false */ virtual bool Open(const char *fn); /*! * \brief Close file */ virtual void Close(); /*! * \brief Write data to file * \param buf data buffer * \param size bytes to write * \return number of bytes written */ virtual size_t Write(void * buf, const size_t size); /*! * \brief Seek to a position in the file * \param offset offset from originating position * \param origin originating position * \return success true/false */ virtual bool Seek(long offset, Origin origin); /*! * \brief Flush any buffers */ virtual void Flush(); protected: FILE *fp; }; #endif openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/dpxinput.cpp0000644000175000017500000006463413151711064021714 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include "libdpx/DPX.h" #include "libdpx/DPXColorConverter.h" #include //For TimeCode support #include "OpenImageIO/typedesc.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/strutil.h" #include OIIO_PLUGIN_NAMESPACE_BEGIN class DPXInput : public ImageInput { public: DPXInput () : m_stream(NULL), m_dataPtr(NULL) { init(); } virtual ~DPXInput () { close(); } virtual const char * format_name (void) const { return "dpx"; } virtual bool valid_file (const std::string &filename) const; virtual bool open (const std::string &name, ImageSpec &newspec); virtual bool close (); virtual int current_subimage (void) const { return m_subimage; } virtual bool seek_subimage (int subimage, int miplevel, ImageSpec &newspec); virtual bool read_native_scanline (int y, int z, void *data); private: int m_subimage; InStream *m_stream; dpx::Reader m_dpx; std::vector m_userBuf; bool m_wantRaw; unsigned char *m_dataPtr; /// Reset everything to initial state /// void init () { if (m_stream) { m_stream->Close (); delete m_stream; m_stream = NULL; } delete m_dataPtr; m_dataPtr = NULL; m_userBuf.clear (); } /// Helper function - retrieve string for libdpx characteristic /// std::string get_characteristic_string (dpx::Characteristic c); /// Helper function - retrieve string for libdpx descriptor /// std::string get_descriptor_string (dpx::Descriptor c); /// Helper function - fill int array with KeyCode values /// void get_keycode_values (int *array); /// Helper function - convert Imf::TimeCode to string; /// std::string get_timecode_string (Imf::TimeCode &tc); }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageInput *dpx_input_imageio_create () { return new DPXInput; } OIIO_EXPORT int dpx_imageio_version = OIIO_PLUGIN_VERSION; OIIO_EXPORT const char* dpx_imageio_library_version () { return NULL; } OIIO_EXPORT const char * dpx_input_extensions[] = { "dpx", NULL }; OIIO_PLUGIN_EXPORTS_END bool DPXInput::valid_file (const std::string &filename) const { InStream *stream = new InStream(); if (! stream) return false; bool ok = false; if (stream->Open(filename.c_str())) { dpx::Reader dpx; dpx.SetInStream(stream); ok = dpx.ReadHeader(); stream->Close(); } delete stream; return ok; } bool DPXInput::open (const std::string &name, ImageSpec &newspec) { // open the image m_stream = new InStream(); if (! m_stream->Open(name.c_str())) { error ("Could not open file \"%s\"", name.c_str()); return false; } m_dpx.SetInStream(m_stream); if (! m_dpx.ReadHeader()) { error ("Could not read header"); return false; } bool ok = seek_subimage (0, 0, newspec); newspec = spec (); return ok; } bool DPXInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec) { if (miplevel != 0) return false; if (subimage < 0 || subimage >= m_dpx.header.ImageElementCount ()) return false; m_subimage = subimage; // check if the client asked us for raw data m_wantRaw = newspec.get_int_attribute ("dpx:RawData", 0) != 0; // create imagespec TypeDesc typedesc; switch (m_dpx.header.ComponentDataSize(subimage)) { case dpx::kByte: typedesc = m_dpx.header.DataSign (subimage) ? TypeDesc::INT8 : TypeDesc::UINT8; break; case dpx::kWord: typedesc = m_dpx.header.DataSign (subimage) ? TypeDesc::INT16 : TypeDesc::UINT16; break; case dpx::kInt: typedesc = m_dpx.header.DataSign (subimage) ? TypeDesc::INT32 : TypeDesc::UINT32; break; case dpx::kFloat: typedesc = TypeDesc::FLOAT; break; case dpx::kDouble: typedesc = TypeDesc::DOUBLE; break; default: error ("Invalid component data size"); return false; } m_spec = ImageSpec (m_dpx.header.Width(), m_dpx.header.Height(), m_dpx.header.ImageElementComponentCount(subimage), typedesc); // xOffset/yOffset are defined as unsigned 32-bit integers, but m_spec.x/y are signed // avoid casts that would result in negative values if (m_dpx.header.xOffset <= (unsigned int)std::numeric_limits::max()) m_spec.x = m_dpx.header.xOffset; if (m_dpx.header.yOffset <= (unsigned int)std::numeric_limits::max()) m_spec.y = m_dpx.header.yOffset; if ((int)m_dpx.header.xOriginalSize > 0) m_spec.full_width = m_dpx.header.xOriginalSize; if ((int)m_dpx.header.yOriginalSize > 0) m_spec.full_height = m_dpx.header.yOriginalSize; // fill channel names m_spec.channelnames.clear (); switch (m_dpx.header.ImageDescriptor(subimage)) { /*case dpx::kUserDefinedDescriptor: break;*/ case dpx::kRed: m_spec.channelnames.push_back("R"); break; case dpx::kGreen: m_spec.channelnames.push_back("G"); break; case dpx::kBlue: m_spec.channelnames.push_back("B"); break; case dpx::kAlpha: m_spec.channelnames.push_back("A"); m_spec.alpha_channel = 0; break; case dpx::kLuma: // FIXME: do we treat this as intensity or do we use Y' as per // convention to differentiate it from linear luminance? m_spec.channelnames.push_back("Y'"); break; case dpx::kDepth: m_spec.channelnames.push_back("Z"); m_spec.z_channel = 0; break; /*case dpx::kCompositeVideo: break;*/ case dpx::kRGB: case dpx::kRGBA: case dpx::kABGR: // colour converter will swap the bytes for us m_spec.default_channel_names (); break; case dpx::kCbYCrY: if (m_wantRaw) { m_spec.channelnames.push_back("CbCr"); m_spec.channelnames.push_back("Y"); } else { m_spec.nchannels = 3; m_spec.default_channel_names (); } break; case dpx::kCbYACrYA: if (m_wantRaw) { m_spec.channelnames.push_back("CbCr"); m_spec.channelnames.push_back("Y"); m_spec.channelnames.push_back("A"); m_spec.alpha_channel = 2; } else { m_spec.nchannels = 4; m_spec.default_channel_names (); } break; case dpx::kCbYCr: if (m_wantRaw) { m_spec.channelnames.push_back("Cb"); m_spec.channelnames.push_back("Y"); m_spec.channelnames.push_back("Cr"); } else m_spec.default_channel_names (); break; case dpx::kCbYCrA: if (m_wantRaw) { m_spec.channelnames.push_back("Cb"); m_spec.channelnames.push_back("Y"); m_spec.channelnames.push_back("Cr"); m_spec.channelnames.push_back("A"); m_spec.alpha_channel = 3; } else { m_spec.default_channel_names (); } break; default: { for (int i = 0; i < m_dpx.header.ImageElementComponentCount(subimage); i++) { std::string ch = Strutil::format("channel%d", i); m_spec.channelnames.push_back(ch); } } } // bits per pixel m_spec.attribute ("oiio:BitsPerSample", m_dpx.header.BitDepth(subimage)); // image orientation - see appendix B.2 of the OIIO documentation int orientation; switch (m_dpx.header.ImageOrientation ()) { case dpx::kLeftToRightTopToBottom: orientation = 1; break; case dpx::kRightToLeftTopToBottom: orientation = 2; break; case dpx::kLeftToRightBottomToTop: orientation = 4; break; case dpx::kRightToLeftBottomToTop: orientation = 3; break; case dpx::kTopToBottomLeftToRight: orientation = 5; break; case dpx::kTopToBottomRightToLeft: orientation = 6; break; case dpx::kBottomToTopLeftToRight: orientation = 8; break; case dpx::kBottomToTopRightToLeft: orientation = 7; break; default: orientation = 0; break; } m_spec.attribute ("Orientation", orientation); // image linearity switch (m_dpx.header.Transfer (subimage)) { case dpx::kLinear: m_spec.attribute ("oiio:ColorSpace", "Linear"); break; case dpx::kLogarithmic: m_spec.attribute ("oiio:ColorSpace", "KodakLog"); break; case dpx::kITUR709: m_spec.attribute ("oiio:ColorSpace", "Rec709"); break; case dpx::kUserDefined: if (! isnan (m_dpx.header.Gamma ()) && m_dpx.header.Gamma () != 0) { m_spec.attribute ("oiio:ColorSpace", "GammaCorrected"); m_spec.attribute ("oiio:Gamma", (float) m_dpx.header.Gamma ()); break; } // intentional fall-through /*case dpx::kPrintingDensity: case dpx::kUnspecifiedVideo: case dpx::kSMPTE274M: case dpx::kITUR601: case dpx::kITUR602: case dpx::kNTSCCompositeVideo: case dpx::kPALCompositeVideo: case dpx::kZLinear: case dpx::kZHomogeneous: case dpx::kUndefinedCharacteristic:*/ default: break; } m_spec.attribute ("dpx:Transfer", get_characteristic_string (m_dpx.header.Transfer (subimage))); // colorimetric characteristic m_spec.attribute ("dpx:Colorimetric", get_characteristic_string (m_dpx.header.Colorimetric (subimage))); // general metadata // some non-compliant writers will dump a field filled with 0xFF rather // than a NULL string termination on the first character, so take that // into account, too if (m_dpx.header.copyright[0] && m_dpx.header.copyright[0] != (char)0xFF) m_spec.attribute ("Copyright", m_dpx.header.copyright); if (m_dpx.header.creator[0] && m_dpx.header.creator[0] != (char)0xFF) m_spec.attribute ("Software", m_dpx.header.creator); if (m_dpx.header.project[0] && m_dpx.header.project[0] != (char)0xFF) m_spec.attribute ("DocumentName", m_dpx.header.project); if (m_dpx.header.creationTimeDate[0]) { // libdpx's date/time format is pretty close to OIIO's (libdpx uses // %Y:%m:%d:%H:%M:%S%Z) char date[24]; Strutil::safe_strcpy(date, m_dpx.header.creationTimeDate, sizeof(date)); date[10] = ' '; date[19] = 0; m_spec.attribute ("DateTime", date); } if (m_dpx.header.ImageEncoding (subimage) == dpx::kRLE) m_spec.attribute ("compression", "rle"); char buf[32 + 1]; m_dpx.header.Description (subimage, buf); if (buf[0] && buf[0] != char(-1)) m_spec.attribute ("ImageDescription", buf); m_spec.attribute ("PixelAspectRatio", m_dpx.header.AspectRatio(1) ? (m_dpx.header.AspectRatio(0) / (float)m_dpx.header.AspectRatio(1)) : 1.0f); // DPX-specific metadata m_spec.attribute ("dpx:ImageDescriptor", get_descriptor_string (m_dpx.header.ImageDescriptor (subimage))); // save some typing by using macros // "internal" macros #define DPX_SET_ATTRIB_S(x, n, s) m_spec.attribute (s, \ m_dpx.header.x (n)) #define DPX_SET_ATTRIB(x, n) DPX_SET_ATTRIB_S(x, n, "dpx:" #x) // set without checking for bogus attributes #define DPX_SET_ATTRIB_N(x) DPX_SET_ATTRIB(x, subimage) // set with checking for bogus attributes #define DPX_SET_ATTRIB_BYTE(x) if (m_dpx.header.x () != 0xFF) \ DPX_SET_ATTRIB(x, ) #define DPX_SET_ATTRIB_INT_N(x) if (m_dpx.header.x (subimage) != 0xFFFFFFFF) \ DPX_SET_ATTRIB(x, subimage) #define DPX_SET_ATTRIB_INT(x) if (m_dpx.header.x () != 0xFFFFFFFF) \ DPX_SET_ATTRIB(x, ) #define DPX_SET_ATTRIB_FLOAT_N(x) if (! isnan(m_dpx.header.x (subimage))) \ DPX_SET_ATTRIB(x, subimage) #define DPX_SET_ATTRIB_FLOAT(x) if (! isnan(m_dpx.header.x ())) \ DPX_SET_ATTRIB(x, ) // see comment above Copyright, Software and DocumentName #define DPX_SET_ATTRIB_STR(X, x) if (m_dpx.header.x[0] \ && m_dpx.header.x[0] != char(-1)) \ m_spec.attribute ("dpx:" #X, \ m_dpx.header.x) DPX_SET_ATTRIB_INT(EncryptKey); DPX_SET_ATTRIB_INT(DittoKey); DPX_SET_ATTRIB_INT_N(LowData); DPX_SET_ATTRIB_FLOAT_N(LowQuantity); DPX_SET_ATTRIB_INT_N(HighData); DPX_SET_ATTRIB_FLOAT_N(HighQuantity); DPX_SET_ATTRIB_INT_N(EndOfLinePadding); DPX_SET_ATTRIB_INT_N(EndOfImagePadding); DPX_SET_ATTRIB_FLOAT(XScannedSize); DPX_SET_ATTRIB_FLOAT(YScannedSize); DPX_SET_ATTRIB_INT(FramePosition); DPX_SET_ATTRIB_INT(SequenceLength); DPX_SET_ATTRIB_INT(HeldCount); DPX_SET_ATTRIB_FLOAT(FrameRate); DPX_SET_ATTRIB_FLOAT(ShutterAngle); DPX_SET_ATTRIB_STR(Version, version); DPX_SET_ATTRIB_STR(Format, format); DPX_SET_ATTRIB_STR(FrameId, frameId); DPX_SET_ATTRIB_STR(SlateInfo, slateInfo); DPX_SET_ATTRIB_STR(SourceImageFileName, sourceImageFileName); DPX_SET_ATTRIB_STR(InputDevice, inputDevice); DPX_SET_ATTRIB_STR(InputDeviceSerialNumber, inputDeviceSerialNumber); DPX_SET_ATTRIB_BYTE(Interlace); DPX_SET_ATTRIB_BYTE(FieldNumber); DPX_SET_ATTRIB_FLOAT(HorizontalSampleRate); DPX_SET_ATTRIB_FLOAT(VerticalSampleRate); DPX_SET_ATTRIB_FLOAT(TemporalFrameRate); DPX_SET_ATTRIB_FLOAT(TimeOffset); DPX_SET_ATTRIB_FLOAT(BlackLevel); DPX_SET_ATTRIB_FLOAT(BlackGain); DPX_SET_ATTRIB_FLOAT(BreakPoint); DPX_SET_ATTRIB_FLOAT(WhiteLevel); DPX_SET_ATTRIB_FLOAT(IntegrationTimes); #undef DPX_SET_ATTRIB_STR #undef DPX_SET_ATTRIB_FLOAT #undef DPX_SET_ATTRIB_FLOAT_N #undef DPX_SET_ATTRIB_INT #undef DPX_SET_ATTRIB_INT_N #undef DPX_SET_ATTRIB_N #undef DPX_SET_ATTRIB #undef DPX_SET_ATTRIB_S std::string tmpstr; switch (m_dpx.header.ImagePacking (subimage)) { case dpx::kPacked: tmpstr = "Packed"; break; case dpx::kFilledMethodA: tmpstr = "Filled, method A"; break; case dpx::kFilledMethodB: tmpstr = "Filled, method B"; break; } if (!tmpstr.empty ()) m_spec.attribute ("dpx:Packing", tmpstr); if (m_dpx.header.filmManufacturingIdCode[0] != 0) { int kc[7]; get_keycode_values (kc); m_spec.attribute("smpte:KeyCode", TypeDesc::TypeKeyCode, kc); } if (m_dpx.header.timeCode != 0xFFFFFFFF) { unsigned int timecode[2] = {m_dpx.header.timeCode, m_dpx.header.userBits}; m_spec.attribute("smpte:TimeCode", TypeDesc::TypeTimeCode, timecode); // This attribute is dpx specific and is left in for backwards compatability. // Users should utilise the new smpte:TimeCode attribute instead Imf::TimeCode tc(m_dpx.header.timeCode, m_dpx.header.userBits); m_spec.attribute ("dpx:TimeCode", get_timecode_string(tc)); } // This attribute is dpx specific and is left in for backwards compatability. // Users should utilise the new smpte:TimeCode attribute instead if (m_dpx.header.userBits != 0xFFFFFFFF) m_spec.attribute ("dpx:UserBits", m_dpx.header.userBits); if (m_dpx.header.sourceTimeDate[0]) { // libdpx's date/time format is pretty close to OIIO's (libdpx uses // %Y:%m:%d:%H:%M:%S%Z) char date[24]; Strutil::safe_strcpy(date, m_dpx.header.sourceTimeDate, sizeof(date)); date[10] = ' '; date[19] = 0; m_spec.attribute ("dpx:SourceDateTime", date); } m_dpx.header.FilmEdgeCode(buf); if (buf[0]) m_spec.attribute ("dpx:FilmEdgeCode", buf); tmpstr.clear (); switch (m_dpx.header.Signal ()) { case dpx::kUndefined: tmpstr = "Undefined"; break; case dpx::kNTSC: tmpstr = "NTSC"; break; case dpx::kPAL: tmpstr = "PAL"; break; case dpx::kPAL_M: tmpstr = "PAL-M"; break; case dpx::kSECAM: tmpstr = "SECAM"; break; case dpx::k525LineInterlace43AR: tmpstr = "YCbCr ITU-R 601-5 525i, 4:3"; break; case dpx::k625LineInterlace43AR: tmpstr = "YCbCr ITU-R 601-5 625i, 4:3"; break; case dpx::k525LineInterlace169AR: tmpstr = "YCbCr ITU-R 601-5 525i, 16:9"; break; case dpx::k625LineInterlace169AR: tmpstr = "YCbCr ITU-R 601-5 625i, 16:9"; break; case dpx::k1050LineInterlace169AR: tmpstr = "YCbCr 1050i, 16:9"; break; case dpx::k1125LineInterlace169AR_274: tmpstr = "YCbCr 1125i, 16:9 (SMPTE 274M)"; break; case dpx::k1250LineInterlace169AR: tmpstr = "YCbCr 1250i, 16:9"; break; case dpx::k1125LineInterlace169AR_240: tmpstr = "YCbCr 1125i, 16:9 (SMPTE 240M)"; break; case dpx::k525LineProgressive169AR: tmpstr = "YCbCr 525p, 16:9"; break; case dpx::k625LineProgressive169AR: tmpstr = "YCbCr 625p, 16:9"; break; case dpx::k750LineProgressive169AR: tmpstr = "YCbCr 750p, 16:9 (SMPTE 296M)"; break; case dpx::k1125LineProgressive169AR: tmpstr = "YCbCr 1125p, 16:9 (SMPTE 274M)"; break; case dpx::k255: // don't set the attribute at all break; default: tmpstr = Strutil::format ("Undefined %d", (int)m_dpx.header.Signal ()); break; } if (!tmpstr.empty ()) m_spec.attribute ("dpx:Signal", tmpstr); // read in user data; don't bother if the buffer is already filled (user // data is per-file, not per-element) if (m_userBuf.empty () && m_dpx.header.UserSize () != 0 && m_dpx.header.UserSize () != 0xFFFFFFFF) { m_userBuf.resize (m_dpx.header.UserSize ()); m_dpx.ReadUserData (&m_userBuf[0]); } if (!m_userBuf.empty ()) m_spec.attribute ("dpx:UserData", TypeDesc (TypeDesc::UCHAR, m_dpx.header.UserSize ()), &m_userBuf[0]); dpx::Block block(0, 0, m_dpx.header.Width () - 1, 0); int bufsize = dpx::QueryRGBBufferSize (m_dpx.header, subimage, block); if (bufsize == 0 && !m_wantRaw) { error ("Unable to deliver RGB data from source data"); return false; } else if (!m_wantRaw && bufsize > 0) m_dataPtr = new unsigned char[bufsize]; else // no need to allocate another buffer m_dataPtr = NULL; newspec = m_spec; return true; } bool DPXInput::close () { init(); // Reset to initial state return true; } bool DPXInput::read_native_scanline (int y, int z, void *data) { dpx::Block block(0, y-m_spec.y, m_dpx.header.Width () - 1, y-m_spec.y); if (m_wantRaw) { // fast path - just read the scanline in if (!m_dpx.ReadBlock (m_subimage, (unsigned char *)data, block)) return false; } else { // read the scanline and convert to RGB void *ptr = m_dataPtr == NULL ? data : (void *)m_dataPtr; if (!m_dpx.ReadBlock (m_subimage, (unsigned char *)ptr, block)) return false; if (!dpx::ConvertToRGB (m_dpx.header, m_subimage, ptr, data, block)) return false; } return true; } std::string DPXInput::get_characteristic_string (dpx::Characteristic c) { switch (c) { case dpx::kUserDefined: return "User defined"; case dpx::kPrintingDensity: return "Printing density"; case dpx::kLinear: return "Linear"; case dpx::kLogarithmic: return "Logarithmic"; case dpx::kUnspecifiedVideo: return "Unspecified video"; case dpx::kSMPTE274M: return "SMPTE 274M"; case dpx::kITUR709: return "ITU-R 709-4"; case dpx::kITUR601: return "ITU-R 601-5 system B or G"; case dpx::kITUR602: return "ITU-R 601-5 system M"; case dpx::kNTSCCompositeVideo: return "NTSC composite video"; case dpx::kPALCompositeVideo: return "PAL composite video"; case dpx::kZLinear: return "Z depth linear"; case dpx::kZHomogeneous: return "Z depth homogeneous"; case dpx::kUndefinedCharacteristic: default: return "Undefined"; } } std::string DPXInput::get_descriptor_string (dpx::Descriptor c) { switch (c) { case dpx::kUserDefinedDescriptor: case dpx::kUserDefined2Comp: case dpx::kUserDefined3Comp: case dpx::kUserDefined4Comp: case dpx::kUserDefined5Comp: case dpx::kUserDefined6Comp: case dpx::kUserDefined7Comp: case dpx::kUserDefined8Comp: return "User defined"; case dpx::kRed: return "Red"; case dpx::kGreen: return "Green"; case dpx::kBlue: return "Blue"; case dpx::kAlpha: return "Alpha"; case dpx::kLuma: return "Luma"; case dpx::kColorDifference: return "Color difference"; case dpx::kDepth: return "Depth"; case dpx::kCompositeVideo: return "Composite video"; case dpx::kRGB: return "RGB"; case dpx::kRGBA: return "RGBA"; case dpx::kABGR: return "ABGR"; case dpx::kCbYCrY: return "CbYCrY"; case dpx::kCbYACrYA: return "CbYACrYA"; case dpx::kCbYCr: return "CbYCr"; case dpx::kCbYCrA: return "CbYCrA"; //case dpx::kUndefinedDescriptor: default: return "Undefined"; } } void DPXInput::get_keycode_values (int *array) { std::stringstream ss; // Manufacturer code ss << std::string(m_dpx.header.filmManufacturingIdCode, 2); ss >> array[0]; ss.clear(); ss.str(""); // Film type ss << std::string(m_dpx.header.filmType, 2); ss >> array[1]; ss.clear(); ss.str(""); // Prefix ss << std::string(m_dpx.header.prefix, 6); ss >> array[2]; ss.clear(); ss.str(""); // Count ss << std::string(m_dpx.header.count, 4); ss >> array[3]; ss.clear(); ss.str(""); // Perforation Offset ss << std::string(m_dpx.header.perfsOffset, 2); ss >> array[4]; ss.clear(); ss.str(""); // Format std::string format(m_dpx.header.format, 32); int &perfsPerFrame = array[5]; int &perfsPerCount = array[6]; // default values perfsPerFrame = 4; perfsPerCount = 64; if ( format == "8kimax" ) { perfsPerFrame = 15; perfsPerCount = 120; } else if ( format.substr(0,4) == "2kvv" || format.substr(0,4) == "4kvv" ) { perfsPerFrame = 8; } else if ( format == "VistaVision" ) { perfsPerFrame = 8; } else if ( format.substr(0,4) == "2k35" || format.substr(0,4) == "4k35") { perfsPerFrame = 4; } else if ( format == "Full Aperture" ) { perfsPerFrame = 4; } else if ( format == "Academy" ) { perfsPerFrame = 4; } else if ( format.substr(0,7) == "2k3perf" || format.substr(0,7) == "4k3perf" ) { perfsPerFrame = 3; } else if ( format == "3perf" ) { perfsPerFrame = 3; } } std::string DPXInput::get_timecode_string (Imf::TimeCode &tc) { int values[] = {tc.hours(), tc.minutes(), tc.seconds(), tc.frame()}; std::stringstream ss; for (int i=0; i<4; i++) { std::ostringstream padded; padded << std::setw(2) << std::setfill('0') << values[i]; ss << padded.str(); if (i != 3) { if (i == 2) { tc.dropFrame() ? ss << ';' : ss << ':'; } else { ss << ':'; } } } return ss.str(); } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/dpx.imageio/dpxoutput.cpp0000644000175000017500000006524213151711064022111 0ustar mfvmfv/* Copyright 2010 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include "libdpx/DPX.h" #include "libdpx/DPXColorConverter.h" #include "OpenImageIO/typedesc.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/strutil.h" OIIO_PLUGIN_NAMESPACE_BEGIN static const int MAX_DPX_IMAGE_ELEMENTS = 8; // max subimages in DPX spec class DPXOutput : public ImageOutput { public: DPXOutput (); virtual ~DPXOutput (); virtual const char * format_name (void) const { return "dpx"; } virtual int supports (string_view feature) const { if (feature == "multiimage" || feature == "alpha" || feature == "nchannels" || feature == "random_access" || feature == "rewrite" || feature == "displaywindow" || feature == "origin") return true; return false; } virtual bool open (const std::string &name, const ImageSpec &spec, OpenMode mode=Create); virtual bool open (const std::string &name, int subimages, const ImageSpec *specs); virtual bool close (); virtual bool write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride); virtual bool write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride); private: OutStream *m_stream; dpx::Writer m_dpx; std::vector m_buf; std::vector m_scratch; dpx::DataSize m_datasize; dpx::Descriptor m_desc; dpx::Characteristic m_cmetr; dpx::Characteristic m_transfer; dpx::Packing m_packing; int m_bitdepth; bool m_wantRaw, m_wantSwap; int m_bytes; int m_subimage; int m_subimages_to_write; std::vector m_subimage_specs; bool m_write_pending; // subimage buffer needs to be written unsigned int m_dither; std::vector m_tilebuffer; // Initialize private members to pre-opened state void init (void) { if (m_stream) { m_stream->Close (); delete m_stream; m_stream = NULL; } m_buf.clear (); m_subimage = 0; m_subimages_to_write = 0; m_subimage_specs.clear (); m_write_pending = false; } // Is the output file currently opened? bool is_opened () const { return (m_stream != NULL); } // flush the pending buffer bool write_buffer (); bool prep_subimage (int s, bool allocate); /// Helper function - retrieve libdpx descriptor for string /// dpx::Characteristic get_characteristic_from_string (const std::string &str); /// Helper function - retrieve libdpx descriptor given nchannels and /// the channel names. dpx::Descriptor get_image_descriptor (); /// Helper function - set keycode values from int array /// void set_keycode_values (int *array); }; // Obligatory material to make this a recognizeable imageio plugin: OIIO_PLUGIN_EXPORTS_BEGIN OIIO_EXPORT ImageOutput *dpx_output_imageio_create () { return new DPXOutput; } // OIIO_EXPORT int dpx_imageio_version = OIIO_PLUGIN_VERSION; // it's in dpxinput.cpp OIIO_EXPORT const char * dpx_output_extensions[] = { "dpx", NULL }; OIIO_PLUGIN_EXPORTS_END DPXOutput::DPXOutput () : m_stream(NULL) { init (); } DPXOutput::~DPXOutput () { // Close, if not already done. close (); } bool DPXOutput::open (const std::string &name, int subimages, const ImageSpec *specs) { if (subimages > MAX_DPX_IMAGE_ELEMENTS) { error ("DPX does not support more than %d subimages", MAX_DPX_IMAGE_ELEMENTS); return false; }; m_subimages_to_write = subimages; m_subimage_specs.clear (); m_subimage_specs.insert (m_subimage_specs.begin(), specs, specs+subimages); return open (name, m_subimage_specs[0], Create); } bool DPXOutput::open (const std::string &name, const ImageSpec &userspec, OpenMode mode) { if (mode == Create) { m_subimage = 0; if (m_subimage_specs.size() < 1) { m_subimage_specs.resize (1); m_subimage_specs[0] = userspec; m_subimages_to_write = 1; } } else if (mode == AppendSubimage) { if (m_write_pending) write_buffer (); ++m_subimage; if (m_subimage >= m_subimages_to_write) { error ("Exceeded the pre-declared number of subimages (%d)", m_subimages_to_write); return false; } return prep_subimage (m_subimage, true); // Nothing else to do, the header taken care of when we opened with // Create. } else if (mode == AppendMIPLevel) { error ("DPX does not support MIP-maps"); return false; } // From here out, all the heavy lifting is done for Create ASSERT (mode == Create); if (is_opened()) close (); // Close any already-opened file m_stream = new OutStream(); if (! m_stream->Open(name.c_str ())) { error ("Could not open file \"%s\"", name.c_str ()); return false; } m_dpx.SetOutStream (m_stream); m_dpx.Start (); m_subimage = 0; ImageSpec &m_spec (m_subimage_specs[m_subimage]); // alias the spec // Check for things this format doesn't support if (m_spec.width < 1 || m_spec.height < 1) { error ("Image resolution must be at least 1x1, you asked for %d x %d", m_spec.width, m_spec.height); return false; } if (m_spec.depth < 1) m_spec.depth = 1; else if (m_spec.depth > 1) { error ("DPX does not support volume images (depth > 1)"); return false; } // some metadata std::string software = m_spec.get_string_attribute ("Software", ""); std::string project = m_spec.get_string_attribute ("DocumentName", ""); std::string copyright = m_spec.get_string_attribute ("Copyright", ""); std::string datestr = m_spec.get_string_attribute ("DateTime", ""); if (datestr.size () >= 19) { // libdpx's date/time format is pretty close to OIIO's (libdpx uses // %Y:%m:%d:%H:%M:%S%Z) // NOTE: the following code relies on the DateTime attribute being properly // formatted! // assume UTC for simplicity's sake, fix it if someone complains datestr[10] = ':'; datestr.replace (19, -1, "Z"); } // check if the client wants endianness reverse to native // assume big endian per Jeremy's request, unless little endian is // explicitly specified std::string endian = m_spec.get_string_attribute ("oiio:Endian", littleendian() ? "little" : "big"); m_wantSwap = (littleendian() != Strutil::iequals (endian, "little")); m_dpx.SetFileInfo (name.c_str (), // filename datestr.c_str (), // cr. date software.empty () ? OIIO_INTRO_STRING : software.c_str (), // creator project.empty () ? NULL : project.c_str (), // project copyright.empty () ? NULL : copyright.c_str (), // copyright m_spec.get_int_attribute ("dpx:EncryptKey", ~0), // encryption key m_wantSwap); // image info m_dpx.SetImageInfo (m_spec.width, m_spec.height); for (int s = 0; s < m_subimages_to_write; ++s) { prep_subimage (s, false); m_dpx.header.SetBitDepth (s, m_bitdepth); ImageSpec &spec (m_subimage_specs[s]); bool datasign = (spec.format == TypeDesc::INT8 || spec.format == TypeDesc::INT16); m_dpx.SetElement (s, m_desc, m_bitdepth, m_transfer, m_cmetr, m_packing, dpx::kNone, datasign, spec.get_int_attribute ("dpx:LowData", 0xFFFFFFFF), spec.get_float_attribute ("dpx:LowQuantity", std::numeric_limits::quiet_NaN()), spec.get_int_attribute ("dpx:HighData", 0xFFFFFFFF), spec.get_float_attribute ("dpx:HighQuantity", std::numeric_limits::quiet_NaN()), spec.get_int_attribute ("dpx:EndOfLinePadding", 0), spec.get_int_attribute ("dpx:EndOfImagePadding", 0)); std::string desc = spec.get_string_attribute ("ImageDescription", ""); m_dpx.header.SetDescription (s, desc.c_str()); } m_dpx.header.SetXScannedSize (m_spec.get_float_attribute ("dpx:XScannedSize", std::numeric_limits::quiet_NaN())); m_dpx.header.SetYScannedSize (m_spec.get_float_attribute ("dpx:YScannedSize", std::numeric_limits::quiet_NaN())); m_dpx.header.SetFramePosition (m_spec.get_int_attribute ("dpx:FramePosition", 0xFFFFFFFF)); m_dpx.header.SetSequenceLength (m_spec.get_int_attribute ("dpx:SequenceLength", 0xFFFFFFFF)); m_dpx.header.SetHeldCount (m_spec.get_int_attribute ("dpx:HeldCount", 0xFFFFFFFF)); m_dpx.header.SetFrameRate (m_spec.get_float_attribute ("dpx:FrameRate", std::numeric_limits::quiet_NaN())); m_dpx.header.SetShutterAngle (m_spec.get_float_attribute ("dpx:ShutterAngle", std::numeric_limits::quiet_NaN())); // FIXME: should we write the input version through or always default to 2.0? /*tmpstr = m_spec.get_string_attribute ("dpx:Version", ""); if (tmpstr.size () > 0) m_dpx.header.SetVersion (tmpstr.c_str ());*/ std::string tmpstr; tmpstr = m_spec.get_string_attribute ("dpx:FrameId", ""); if (tmpstr.size () > 0) m_dpx.header.SetFrameId (tmpstr.c_str ()); tmpstr = m_spec.get_string_attribute ("dpx:SlateInfo", ""); if (tmpstr.size () > 0) m_dpx.header.SetSlateInfo (tmpstr.c_str ()); tmpstr = m_spec.get_string_attribute ("dpx:SourceImageFileName", ""); if (tmpstr.size () > 0) m_dpx.header.SetSourceImageFileName (tmpstr.c_str ()); tmpstr = m_spec.get_string_attribute ("dpx:InputDevice", ""); if (tmpstr.size () > 0) m_dpx.header.SetInputDevice (tmpstr.c_str ()); tmpstr = m_spec.get_string_attribute ("dpx:InputDeviceSerialNumber", ""); if (tmpstr.size () > 0) m_dpx.header.SetInputDeviceSerialNumber (tmpstr.c_str ()); m_dpx.header.SetInterlace (m_spec.get_int_attribute ("dpx:Interlace", 0xFF)); m_dpx.header.SetFieldNumber (m_spec.get_int_attribute ("dpx:FieldNumber", 0xFF)); m_dpx.header.SetHorizontalSampleRate (m_spec.get_float_attribute ("dpx:HorizontalSampleRate", std::numeric_limits::quiet_NaN())); m_dpx.header.SetVerticalSampleRate (m_spec.get_float_attribute ("dpx:VerticalSampleRate", std::numeric_limits::quiet_NaN())); m_dpx.header.SetTemporalFrameRate (m_spec.get_float_attribute ("dpx:TemporalFrameRate", std::numeric_limits::quiet_NaN())); m_dpx.header.SetTimeOffset (m_spec.get_float_attribute ("dpx:TimeOffset", std::numeric_limits::quiet_NaN())); m_dpx.header.SetBlackLevel (m_spec.get_float_attribute ("dpx:BlackLevel", std::numeric_limits::quiet_NaN())); m_dpx.header.SetBlackGain (m_spec.get_float_attribute ("dpx:BlackGain", std::numeric_limits::quiet_NaN())); m_dpx.header.SetBreakPoint (m_spec.get_float_attribute ("dpx:BreakPoint", std::numeric_limits::quiet_NaN())); m_dpx.header.SetWhiteLevel (m_spec.get_float_attribute ("dpx:WhiteLevel", std::numeric_limits::quiet_NaN())); m_dpx.header.SetIntegrationTimes (m_spec.get_float_attribute ("dpx:IntegrationTimes", std::numeric_limits::quiet_NaN())); float aspect = m_spec.get_float_attribute ("PixelAspectRatio", 1.0f); int aspect_num, aspect_den; float_to_rational (aspect, aspect_num, aspect_den); m_dpx.header.SetAspectRatio (0, aspect_num); m_dpx.header.SetAspectRatio (1, aspect_den); m_dpx.header.SetXOffset ((unsigned int)std::max (0, m_spec.x)); m_dpx.header.SetYOffset ((unsigned int)std::max (0, m_spec.y)); m_dpx.header.SetXOriginalSize ((unsigned int)m_spec.full_width); m_dpx.header.SetYOriginalSize ((unsigned int)m_spec.full_height); static int DpxOrientations[] = { 0, dpx::kLeftToRightTopToBottom, dpx::kRightToLeftTopToBottom, dpx::kLeftToRightBottomToTop, dpx::kRightToLeftBottomToTop, dpx::kTopToBottomLeftToRight, dpx::kTopToBottomRightToLeft, dpx::kBottomToTopLeftToRight, dpx::kBottomToTopRightToLeft }; int orient = m_spec.get_int_attribute ("Orientation", 0); orient = DpxOrientations[clamp (orient, 0, 8)]; m_dpx.header.SetImageOrientation ((dpx::Orientation)orient); ImageIOParameter *tc = m_spec.find_attribute("smpte:TimeCode", TypeDesc::TypeTimeCode, false); if (tc) { unsigned int *timecode = (unsigned int*) tc->data(); m_dpx.header.timeCode = timecode[0]; m_dpx.header.userBits = timecode[1]; } else { std::string timecode = m_spec.get_string_attribute ("dpx:TimeCode", ""); int tmpint = m_spec.get_int_attribute ("dpx:TimeCode", ~0); if (timecode.size () > 0) m_dpx.header.SetTimeCode (timecode.c_str ()); else if (tmpint != ~0) m_dpx.header.timeCode = tmpint; m_dpx.header.userBits = m_spec.get_int_attribute ("dpx:UserBits", ~0); } ImageIOParameter *kc = m_spec.find_attribute("smpte:KeyCode", TypeDesc::TypeKeyCode, false); if (kc) { int *array = (int*) kc->data(); set_keycode_values(array); // See if there is an overloaded dpx:Format std::string format = m_spec.get_string_attribute ("dpx:Format", ""); if (format.size () > 0) m_dpx.header.SetFormat (format.c_str ()); } std::string srcdate = m_spec.get_string_attribute ("dpx:SourceDateTime", ""); if (srcdate.size () >= 19) { // libdpx's date/time format is pretty close to OIIO's (libdpx uses // %Y:%m:%d:%H:%M:%S%Z) // NOTE: the following code relies on the DateTime attribute being properly // formatted! // assume UTC for simplicity's sake, fix it if someone complains srcdate[10] = ':'; srcdate.replace (19, -1, "Z"); m_dpx.header.SetSourceTimeDate (srcdate.c_str ()); } // set the user data size ImageIOParameter *user = m_spec.find_attribute ("dpx:UserData"); if (user && user->datasize () > 0 && user->datasize () <= 1024 * 1024) { m_dpx.SetUserData (user->datasize ()); } // commit! if (!m_dpx.WriteHeader ()) { error ("Failed to write DPX header"); return false; } // write the user data if (user && user->datasize () > 0 && user->datasize() <= 1024 * 1024) { if (!m_dpx.WriteUserData ((void *)user->data ())) { error ("Failed to write user data"); return false; } } m_dither = (m_spec.format == TypeDesc::UINT8) ? m_spec.get_int_attribute ("oiio:dither", 0) : 0; // If user asked for tiles -- which this format doesn't support, emulate // it by buffering the whole image. if (m_spec.tile_width && m_spec.tile_height) m_tilebuffer.resize (m_spec.image_bytes()); return prep_subimage (m_subimage, true); } bool DPXOutput::prep_subimage (int s, bool allocate) { m_spec = m_subimage_specs[s]; // stash the spec // determine descriptor m_desc = get_image_descriptor(); // transfer function std::string colorspace = m_spec.get_string_attribute ("oiio:ColorSpace", ""); if (Strutil::iequals (colorspace, "Linear")) m_transfer = dpx::kLinear; else if (Strutil::iequals (colorspace, "GammaCorrected")) m_transfer = dpx::kUserDefined; else if (Strutil::iequals (colorspace, "Rec709")) m_transfer = dpx::kITUR709; else if (Strutil::iequals (colorspace, "KodakLog")) m_transfer = dpx::kLogarithmic; else { std::string dpxtransfer = m_spec.get_string_attribute ("dpx:Transfer", ""); m_transfer = get_characteristic_from_string (dpxtransfer); } // colorimetric m_cmetr = get_characteristic_from_string (m_spec.get_string_attribute ("dpx:Colorimetric", "User defined")); // select packing method std::string pck = m_spec.get_string_attribute ("dpx:Packing", "Filled, method A"); if (Strutil::iequals (pck, "Packed")) m_packing = dpx::kPacked; else if (Strutil::iequals (pck, "Filled, method B")) m_packing = dpx::kFilledMethodB; else m_packing = dpx::kFilledMethodA; switch (m_spec.format.basetype) { case TypeDesc::UINT8 : case TypeDesc::UINT16 : case TypeDesc::FLOAT : case TypeDesc::DOUBLE : // supported, fine break; case TypeDesc::HALF : // Turn half into float m_spec.format.basetype = TypeDesc::FLOAT; break; default: // Turn everything else into UINT16 m_spec.format.basetype = TypeDesc::UINT16; break; } // calculate target bit depth m_bitdepth = m_spec.format.size () * 8; if (m_spec.format == TypeDesc::UINT16) { m_bitdepth = m_spec.get_int_attribute ("oiio:BitsPerSample", 16); if (m_bitdepth != 10 && m_bitdepth != 12 && m_bitdepth != 16) { error ("Unsupported bit depth %d", m_bitdepth); return false; } } // Bug workaround: libDPX doesn't appear to correctly support // "filled method A" for 12 bit data. Does anybody care what // packing/filling we use? Punt and just use "packed". if (m_bitdepth == 12) m_packing = dpx::kPacked; if (m_spec.format == TypeDesc::UINT8 || m_spec.format == TypeDesc::INT8) m_datasize = dpx::kByte; else if (m_spec.format == TypeDesc::UINT16 || m_spec.format == TypeDesc::INT16) m_datasize = dpx::kWord; else if (m_spec.format == TypeDesc::FLOAT || m_spec.format == TypeDesc::HALF) { m_spec.format = TypeDesc::FLOAT; m_datasize = dpx::kFloat; } else if (m_spec.format == TypeDesc::DOUBLE) m_datasize = dpx::kDouble; else { // use 16-bit unsigned integers as a failsafe m_spec.set_format (TypeDesc::UINT16); m_datasize = dpx::kWord; } // check if the client is giving us raw data to write m_wantRaw = m_spec.get_int_attribute ("dpx:RawData", 0) != 0; // see if we'll need to convert or not if (m_desc == dpx::kRGB || m_desc == dpx::kRGBA) { // shortcut for RGB(A) that gets the job done m_bytes = m_spec.scanline_bytes (); m_wantRaw = true; } else { m_bytes = dpx::QueryNativeBufferSize (m_desc, m_datasize, m_spec.width, 1); if (m_bytes == 0 && !m_wantRaw) { error ("Unable to deliver native format data from source data"); return false; } else if (m_bytes < 0) { // no need to allocate another buffer if (!m_wantRaw) m_bytes = m_spec.scanline_bytes (); else m_bytes = -m_bytes; } } if (m_bytes < 0) m_bytes = -m_bytes; // allocate space for the image data buffer if (allocate) m_buf.resize (m_bytes * m_spec.height); return true; } bool DPXOutput::write_buffer () { if (m_write_pending) { m_dpx.WriteElement (m_subimage, &m_buf[0], m_datasize); m_write_pending = false; } return true; } bool DPXOutput::close () { if (! m_stream) { // already closed init (); return true; } bool ok = true; if (m_spec.tile_width) { // Handle tile emulation -- output the buffered pixels ASSERT (m_tilebuffer.size()); ok &= write_scanlines (m_spec.y, m_spec.y+m_spec.height, 0, m_spec.format, &m_tilebuffer[0]); std::vector().swap (m_tilebuffer); } ok &= write_buffer (); m_dpx.Finish (); init(); // Reset to initial state return ok; } bool DPXOutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { m_write_pending = true; m_spec.auto_stride (xstride, format, m_spec.nchannels); const void *origdata = data; data = to_native_scanline (format, data, xstride, m_scratch, m_dither, y, z); if (data == origdata) { m_scratch.assign ((unsigned char *)data, (unsigned char *)data+m_spec.scanline_bytes()); data = &m_scratch[0]; } unsigned char *dst = &m_buf[(y-m_spec.y) * m_bytes]; if (m_wantRaw) // fast path - just dump the scanline into the buffer memcpy (dst, data, m_spec.scanline_bytes ()); else if (!dpx::ConvertToNative (m_desc, m_datasize, m_cmetr, m_spec.width, 1, data, dst)) return false; return true; } bool DPXOutput::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { // Emulate tiles by buffering the whole image return copy_tile_to_image_buffer (x, y, z, format, data, xstride, ystride, zstride, &m_tilebuffer[0]); } dpx::Characteristic DPXOutput::get_characteristic_from_string (const std::string &str) { if (Strutil::iequals (str, "User defined")) return dpx::kUserDefined; else if (Strutil::iequals (str, "Printing density")) return dpx::kPrintingDensity; else if (Strutil::iequals (str, "Linear")) return dpx::kLinear; else if (Strutil::iequals (str, "Logarithmic")) return dpx::kLogarithmic; else if (Strutil::iequals (str, "Unspecified video")) return dpx::kUnspecifiedVideo; else if (Strutil::iequals (str, "SMPTE 274M")) return dpx::kSMPTE274M; else if (Strutil::iequals (str, "ITU-R 709-4")) return dpx::kITUR709; else if (Strutil::iequals (str, "ITU-R 601-5 system B or G")) return dpx::kITUR601; else if (Strutil::iequals (str, "ITU-R 601-5 system M")) return dpx::kITUR602; else if (Strutil::iequals (str, "NTSC composite video")) return dpx::kNTSCCompositeVideo; else if (Strutil::iequals (str, "PAL composite video")) return dpx::kPALCompositeVideo; else if (Strutil::iequals (str, "Z depth linear")) return dpx::kZLinear; else if (Strutil::iequals (str, "Z depth homogeneous")) return dpx::kZHomogeneous; else return dpx::kUndefinedCharacteristic; } dpx::Descriptor DPXOutput::get_image_descriptor () { switch (m_spec.nchannels) { case 1: { std::string name = m_spec.channelnames.size() ? m_spec.channelnames[0] : ""; if (m_spec.z_channel == 0 || name == "Z") return dpx::kDepth; else if (m_spec.alpha_channel == 0 || name == "A") return dpx::kAlpha; else if (name == "R") return dpx::kRed; else if (name == "B") return dpx::kBlue; else if (name == "G") return dpx::kGreen; else return dpx::kLuma; } case 3: return dpx::kRGB; case 4: return dpx::kRGBA; default: if (m_spec.nchannels <= 8) return (dpx::Descriptor)((int)dpx::kUserDefined2Comp + m_spec.nchannels - 2); return dpx::kUndefinedDescriptor; } } void DPXOutput::set_keycode_values (int *array) { // Manufacturer code { std::stringstream ss; ss << std::setfill('0'); ss << std::setw(2) << array[0]; memcpy(m_dpx.header.filmManufacturingIdCode, ss.str().c_str(), 2); } // Film type { std::stringstream ss; ss << std::setfill('0'); ss << std::setw(2) << array[1]; memcpy(m_dpx.header.filmType, ss.str().c_str(), 2); } // Prefix { std::stringstream ss; ss << std::setfill('0'); ss << std::setw(6) << array[2]; memcpy(m_dpx.header.prefix, ss.str().c_str(), 6); } // Count { std::stringstream ss; ss << std::setfill('0'); ss << std::setw(4) << array[3]; memcpy(m_dpx.header.count, ss.str().c_str(), 4); } // Perforation Offset { std::stringstream ss; ss << std::setfill('0'); ss << std::setw(2) << array[4]; memcpy(m_dpx.header.perfsOffset, ss.str().c_str(), 2); } // Format int &perfsPerFrame = array[5]; int &perfsPerCount = array[6]; if (perfsPerFrame == 15 && perfsPerCount == 120) { Strutil::safe_strcpy(m_dpx.header.format, "8kimax", sizeof(m_dpx.header.format)); } else if (perfsPerFrame == 8 && perfsPerCount == 64) { Strutil::safe_strcpy(m_dpx.header.format, "VistaVision", sizeof(m_dpx.header.format)); } else if (perfsPerFrame == 4 && perfsPerCount == 64) { Strutil::safe_strcpy(m_dpx.header.format, "Full Aperture", sizeof(m_dpx.header.format)); } else if (perfsPerFrame == 3 && perfsPerCount == 64) { Strutil::safe_strcpy(m_dpx.header.format, "3perf", sizeof(m_dpx.header.format)); } else { Strutil::safe_strcpy(m_dpx.header.format, "Unknown", sizeof(m_dpx.header.format)); } } OIIO_PLUGIN_NAMESPACE_END openimageio-1.7.17~dfsg0.orig/src/testtex/0000755000175000017500000000000013151711064016614 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/src/testtex/CMakeLists.txt0000644000175000017500000000032213151711064021351 0ustar mfvmfvset (testtex_srcs testtex.cpp) add_executable (testtex ${testtex_srcs}) set_target_properties (testtex PROPERTIES FOLDER "Tools") target_link_libraries (testtex OpenImageIO ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) openimageio-1.7.17~dfsg0.orig/src/testtex/testtex.cpp0000644000175000017500000013567613151711064021042 0ustar mfvmfv/* Copyright 2008 Larry Gritz and the other authors and contributors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the software's owners nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This is the Modified BSD License) */ #include #include #include #include #include #include #include #include #include #include #include "OpenImageIO/argparse.h" #include "OpenImageIO/imageio.h" #include "OpenImageIO/ustring.h" #include "OpenImageIO/imagebuf.h" #include "OpenImageIO/imagebufalgo.h" #include "OpenImageIO/imagebufalgo_util.h" #include "OpenImageIO/texture.h" #include "OpenImageIO/fmath.h" #include "OpenImageIO/filesystem.h" #include "OpenImageIO/sysutil.h" #include "OpenImageIO/strutil.h" #include "OpenImageIO/timer.h" #include "../libtexture/imagecache_pvt.h" OIIO_NAMESPACE_USING #if OIIO_CPLUSPLUS_VERSION >= 11 using OIIO::_1; #endif static std::vector filenames; static std::string output_filename = "out.exr"; static bool verbose = false; static int nthreads = 0; static int threadtimes = 0; static int output_xres = 512, output_yres = 512; static int nchannels_override = 0; static std::string dataformatname = "half"; static float sscale = 1, tscale = 1; static float sblur = 0, tblur = -1; static float width = 1; static std::string wrapmodes ("periodic"); static int anisotropic = -1; static int iters = 1; static int autotile = 0; static bool automip = false; static bool dedup = true; static bool test_construction = false; static bool test_gettexels = false; static bool test_getimagespec = false; static bool filtertest = false; static TextureSystem *texsys = NULL; static std::string searchpath; static int blocksize = 1; static bool nowarp = false; static bool tube = false; static bool use_handle = false; static float cachesize = -1; static int maxfiles = -1; static int mipmode = TextureOpt::MipModeDefault; static int interpmode = TextureOpt::InterpSmartBicubic; static float missing[4] = {-1, 0, 0, 1}; static float fill = -1; // -1 signifies unset static float scalefactor = 1.0f; static Imath::V3f texoffset (0,0,0); static bool nountiled = false; static bool nounmipped = false; static bool gray_to_rgb = false; static bool flip_t = false; static bool resetstats = false; static bool testhash = false; static bool wedge = false; static int ntrials = 1; static int testicwrite = 0; static bool test_derivs = false; static bool test_statquery = false; static Imath::M33f xform; static mutex error_mutex; void *dummyptr; typedef void (*Mapping2D)(int,int,float&,float&,float&,float&,float&,float&); typedef void (*Mapping3D)(int,int,Imath::V3f&,Imath::V3f&,Imath::V3f&,Imath::V3f &); static int parse_files (int argc, const char *argv[]) { for (int i = 0; i < argc; i++) filenames.push_back (ustring(argv[i])); return 0; } static void getargs (int argc, const char *argv[]) { TextureOptions opt; // to figure out defaults anisotropic = opt.anisotropic; bool help = false; ArgParse ap; ap.options ("Usage: testtex [options] inputfile", "%*", parse_files, "", "--help", &help, "Print help message", "-v", &verbose, "Verbose status messages", "-o %s", &output_filename, "Output test image", "-d %s", &dataformatname, "Set the output data format to one of:" "uint8, sint8, uint10, uint12, uint16, sint16, half, float, double", "--res %d %d", &output_xres, &output_yres, "Resolution of output test image", "--nchannels %d", &nchannels_override, "Force number of channels to look up", "--iters %d", &iters, "Iterations for time trials", "--threads %d", &nthreads, "Number of threads (default 0 = #cores)", "-t %d", &nthreads, "", // synonym "--blur %f", &sblur, "Add blur to texture lookup", "--stblur %f %f", &sblur, &tblur, "Add blur (s, t) to texture lookup", "--width %f", &width, "Multiply filter width of texture lookup", "--fill %f", &fill, "Set fill value for missing channels", "--wrap %s", &wrapmodes, "Set wrap mode (default, black, clamp, periodic, mirror, overscan)", "--aniso %d", &anisotropic, Strutil::format("Set max anisotropy (default: %d)", anisotropic).c_str(), "--mipmode %d", &mipmode, "Set mip mode (default: 0 = aniso)", "--interpmode %d", &interpmode, "Set interp mode (default: 3 = smart bicubic)", "--missing %f %f %f", &missing[0], &missing[1], &missing[2], "Specify missing texture color", "--autotile %d", &autotile, "Set auto-tile size for the image cache", "--automip", &automip, "Set auto-MIPmap for the image cache", "--blocksize %d", &blocksize, "Set blocksize (n x n) for batches", "--handle", &use_handle, "Use texture handle rather than name lookup", "--searchpath %s", &searchpath, "Search path for files", "--filtertest", &filtertest, "Test the filter sizes", "--nowarp", &nowarp, "Do not warp the image->texture mapping", "--tube", &tube, "Make a tube projection", "--ctr", &test_construction, "Test TextureOpt construction time", "--gettexels", &test_gettexels, "Test TextureSystem::get_texels", "--getimagespec", &test_getimagespec, "Test TextureSystem::get_imagespec", "--offset %f %f %f", &texoffset[0], &texoffset[1], &texoffset[2], "Offset texture coordinates", "--scalest %f %f", &sscale, &tscale, "Scale texture lookups (s, t)", "--cachesize %f", &cachesize, "Set cache size, in MB", "--nodedup %!", &dedup, "Turn off de-duplication", "--scale %f", &scalefactor, "Scale intensities", "--maxfiles %d", &maxfiles, "Set maximum open files", "--nountiled", &nountiled, "Reject untiled images", "--nounmipped", &nounmipped, "Reject unmipped images", "--graytorgb", &gray_to_rgb, "Convert gratscale textures to RGB", "--flipt", &flip_t, "Flip direction of t coordinate", "--derivs", &test_derivs, "Test returning derivatives of texture lookups", "--resetstats", &resetstats, "Print and reset statistics on each iteration", "--testhash", &testhash, "Test the tile hashing function", "--threadtimes %d", &threadtimes, "Do thread timings (arg = workload profile)", "--trials %d", &ntrials, "Number of trials for timings", "--wedge", &wedge, "Wedge test", "--testicwrite %d", &testicwrite, "Test ImageCache write ability (1=seeded, 2=generated)", "--teststatquery", &test_statquery, "Test queries of statistics", NULL); if (ap.parse (argc, argv) < 0) { std::cerr << ap.geterror() << std::endl; ap.usage (); exit (EXIT_FAILURE); } if (help) { ap.usage (); exit (EXIT_FAILURE); } if (filenames.size() < 1 && !test_construction && !test_getimagespec && !testhash) { std::cerr << "testtex: Must have at least one input file\n"; ap.usage(); exit (EXIT_FAILURE); } } static void initialize_opt (TextureOpt &opt, int nchannels) { opt.sblur = sblur; opt.tblur = tblur >= 0.0f ? tblur : sblur; opt.rblur = sblur; opt.swidth = width; opt.twidth = width; opt.rwidth = width; // opt.nchannels = nchannels; opt.fill = (fill >= 0.0f) ? fill : 1.0f; if (missing[0] >= 0) opt.missingcolor = (float *)&missing; TextureOpt::parse_wrapmodes (wrapmodes.c_str(), opt.swrap, opt.twrap); opt.rwrap = opt.swrap; opt.anisotropic = anisotropic; opt.mipmode = (TextureOpt::MipMode) mipmode; opt.interpmode = (TextureOpt::InterpMode) interpmode; } static void test_gettextureinfo (ustring filename) { bool ok; int res[2] = {0}; ok = texsys->get_texture_info (filename, 0, ustring("resolution"), TypeDesc(TypeDesc::INT,2), res); std::cout << "Result of get_texture_info resolution = " << ok << ' ' << res[0] << 'x' << res[1] << "\n"; int chan = 0; ok = texsys->get_texture_info (filename, 0, ustring("channels"), TypeDesc::INT, &chan); std::cout << "Result of get_texture_info channels = " << ok << ' ' << chan << "\n"; float fchan = 0; ok = texsys->get_texture_info (filename, 0, ustring("channels"), TypeDesc::FLOAT, &fchan); std::cout << "Result of get_texture_info channels = " << ok << ' ' << fchan << "\n"; int dataformat = 0; ok = texsys->get_texture_info (filename, 0, ustring("format"), TypeDesc::INT, &dataformat); std::cout << "Result of get_texture_info data format = " << ok << ' ' << TypeDesc((TypeDesc::BASETYPE)dataformat).c_str() << "\n"; const char *datetime = NULL; ok = texsys->get_texture_info (filename, 0, ustring("DateTime"), TypeDesc::STRING, &datetime); std::cout << "Result of get_texture_info datetime = " << ok << ' ' << (datetime ? datetime : "") << "\n"; float avg[4]; ok = texsys->get_texture_info (filename, 0, ustring("averagecolor"), TypeDesc(TypeDesc::FLOAT,4), avg); std::cout << "Result of get_texture_info averagecolor = " << (ok?"yes":"no\n"); if (ok) std::cout << " " << avg[0] << ' ' << avg[1] << ' ' << avg[2] << ' ' << avg[3] << "\n"; ok = texsys->get_texture_info (filename, 0, ustring("averagealpha"), TypeDesc::TypeFloat, avg); std::cout << "Result of get_texture_info averagealpha = " << (ok?"yes":"no\n"); if (ok) std::cout << " " << avg[0] << "\n"; ok = texsys->get_texture_info (filename, 0, ustring("constantcolor"), TypeDesc(TypeDesc::FLOAT,4), avg); std::cout << "Result of get_texture_info constantcolor = " << (ok?"yes":"no\n"); if (ok) std::cout << " " << avg[0] << ' ' << avg[1] << ' ' << avg[2] << ' ' << avg[3] << "\n"; const char *texturetype = NULL; ok = texsys->get_texture_info (filename, 0, ustring("textureformat"), TypeDesc::STRING, &texturetype); std::cout << "Texture type is " << ok << ' ' << (texturetype ? texturetype : "") << "\n"; std::cout << "\n"; } static void adjust_spec (ImageSpec &outspec, const std::string &dataformatname) { if (! dataformatname.empty()) { if (dataformatname == "uint8") outspec.set_format (TypeDesc::UINT8); else if (dataformatname == "int8") outspec.set_format (TypeDesc::INT8); else if (dataformatname == "uint10") { outspec.attribute ("oiio:BitsPerSample", 10); outspec.set_format (TypeDesc::UINT16); } else if (dataformatname == "uint12") { outspec.attribute ("oiio:BitsPerSample", 12); outspec.set_format (TypeDesc::UINT16); } else if (dataformatname == "uint16") outspec.set_format (TypeDesc::UINT16); else if (dataformatname == "int16") outspec.set_format (TypeDesc::INT16); else if (dataformatname == "half") outspec.set_format (TypeDesc::HALF); else if (dataformatname == "float") outspec.set_format (TypeDesc::FLOAT); else if (dataformatname == "double") outspec.set_format (TypeDesc::DOUBLE); outspec.channelformats.clear (); } } inline Imath::V2f warp (float x, float y, const Imath::M33f &xform) { Imath::V2f coord (x, y); xform.multVecMatrix (coord, coord); return coord; } inline Imath::V3f warp (float x, float y, float z, const Imath::M33f &xform) { Imath::V3f coord (x, y, z); coord *= xform; return coord; } inline Imath::V2f warp_coord (float x, float y) { Imath::V2f coord = warp (x/output_xres, y/output_yres, xform); coord.x *= sscale; coord.y *= tscale; coord += Imath::V2f(texoffset.x, texoffset.y); return coord; } // Just map pixels to [0,1] st space static void map_default (int x, int y, float &s, float &t, float &dsdx, float &dtdx, float &dsdy, float &dtdy) { s = float(x+0.5f)/output_xres * sscale + texoffset[0]; t = float(y+0.5f)/output_yres * tscale + texoffset[1]; dsdx = 1.0f/output_xres * sscale; dtdx = 0.0f; dsdy = 0.0f; dtdy = 1.0f/output_yres * tscale; } static void map_warp (int x, int y, float &s, float &t, float &dsdx, float &dtdx, float &dsdy, float &dtdy) { const Imath::V2f coord = warp_coord (x+0.5f, y+0.5f); const Imath::V2f coordx = warp_coord (x+1.5f, y+0.5f); const Imath::V2f coordy = warp_coord (x+0.5f, y+1.5f); s = coord[0]; t = coord[1]; dsdx = coordx[0] - coord[0]; dtdx = coordx[1] - coord[1]; dsdy = coordy[0] - coord[0]; dtdy = coordy[1] - coord[1]; } static void map_tube (int x, int y, float &s, float &t, float &dsdx, float &dtdx, float &dsdy, float &dtdy) { float xt = float(x+0.5f)/output_xres - 0.5f; float dxt_dx = 1.0f/output_xres; float yt = float(y+0.5f)/output_yres - 0.5f; float dyt_dy = 1.0f/output_yres; float theta = atan2f (yt, xt); // See OSL's Dual2 for partial derivs of // atan2, hypot, and 1/x double denom = 1.0 / (xt*xt + yt*yt); double dtheta_dx = yt*dxt_dx * denom; double dtheta_dy = -xt*dyt_dy * denom; s = float(4.0 * theta / (2.0 * M_PI)); dsdx = float(4.0 * dtheta_dx / (2.0 * M_PI)); dsdy = float(4.0 * dtheta_dy / (2.0 * M_PI)); double h = hypot(xt,yt); double dh_dx = xt*dxt_dx / h; double dh_dy = yt*dyt_dy / h; h *= M_SQRT2; dh_dx *= M_SQRT2; dh_dy *= M_SQRT2; double hinv = 1.0 / h; t = float(hinv); dtdx = float(hinv * (-hinv * dh_dx)); dtdy = float(hinv * (-hinv * dh_dy)); } // To test filters, we always sample at the center of the image, and // keep the minor axis of the filter at 1/256, but we vary the // eccentricity (i.e. major axis length) as we go left (1) to right // (32), and vary the angle as we go top (0) to bottom (2pi). // // If filtering is correct, all pixels should sample from the same MIP // level because they have the same minor axis (1/256), regardless of // eccentricity or angle. If we specify a texture that has a // distinctive color at the 256-res level, and something totally // different at the 512 and 128 levels, it should be easy to verify that // we aren't over-filtering or under-filtering by selecting the wrong // MIP level. (Though of course, there are other kinds of mistakes we // could be making, such as computing the wrong eccentricity or angle.) static void map_filtertest (int x, int y, float &s, float &t, float &dsdx, float &dtdx, float &dsdy, float &dtdy) { float minoraxis = 1.0f/256; float majoraxis = minoraxis * lerp (1.0f, 32.0f, (float)x/(output_xres-1)); float angle = (float)(2.0 * M_PI * (double)y/(output_yres-1)); float sinangle, cosangle; sincos (angle, &sinangle, &cosangle); s = 0.5f; t = 0.5f; dsdx = minoraxis * cosangle; dtdx = minoraxis * sinangle; dsdy = -majoraxis * sinangle; dtdy = majoraxis * cosangle; } void map_default_3D (int x, int y, Imath::V3f &P, Imath::V3f &dPdx, Imath::V3f &dPdy, Imath::V3f &dPdz) { P[0] = (float)(x+0.5f)/output_xres * sscale; P[1] = (float)(y+0.5f)/output_yres * tscale; P[2] = 0.5f * sscale; P += texoffset; dPdx[0] = 1.0f/output_xres * sscale; dPdx[1] = 0; dPdx[2] = 0; dPdy[0] = 0; dPdy[1] = 1.0f/output_yres * tscale; dPdy[2] = 0; dPdz.setValue (0,0,0); } void map_warp_3D (int x, int y, Imath::V3f &P, Imath::V3f &dPdx, Imath::V3f &dPdy, Imath::V3f &dPdz) { Imath::V3f coord = warp ((float)x/output_xres, (float)y/output_yres, 0.5, xform); coord.x *= sscale; coord.y *= tscale; coord += texoffset; Imath::V3f coordx = warp ((float)(x+1)/output_xres, (float)y/output_yres, 0.5, xform); coordx.x *= sscale; coordx.y *= tscale; coordx += texoffset; Imath::V3f coordy = warp ((float)x/output_xres, (float)(y+1)/output_yres, 0.5, xform); coordy.x *= sscale; coordy.y *= tscale; coordy += texoffset; P = coord; dPdx = coordx - coord; dPdy = coordy - coord; dPdz.setValue (0,0,0); } void plain_tex_region (ImageBuf &image, ustring filename, Mapping2D mapping, ImageBuf *image_ds, ImageBuf *image_dt, ROI roi) { TextureSystem::Perthread *perthread_info = texsys->get_perthread_info (); TextureSystem::TextureHandle *texture_handle = texsys->get_texture_handle (filename); int nchannels = nchannels_override ? nchannels_override : image.nchannels(); TextureOpt opt; initialize_opt (opt, nchannels); float *result = ALLOCA (float, std::max (3, nchannels)); float *dresultds = test_derivs ? ALLOCA (float, nchannels) : NULL; float *dresultdt = test_derivs ? ALLOCA (float, nchannels) : NULL; for (ImageBuf::Iterator p (image, roi); ! p.done(); ++p) { float s, t, dsdx, dtdx, dsdy, dtdy; mapping (p.x(), p.y(), s, t, dsdx, dtdx, dsdy, dtdy); // Call the texture system to do the filtering. bool ok; if (use_handle) ok = texsys->texture (texture_handle, perthread_info, opt, s, t, dsdx, dtdx, dsdy, dtdy, nchannels, result, dresultds, dresultdt); else ok = texsys->texture (filename, opt, s, t, dsdx, dtdx, dsdy, dtdy, nchannels, result, dresultds, dresultdt); if (! ok) { std::string e = texsys->geterror (); if (! e.empty()) { lock_guard lock (error_mutex); std::cerr << "ERROR: " << e << "\n"; } } // Save filtered pixels back to the image. for (int i = 0; i < nchannels; ++i) result[i] *= scalefactor; image.setpixel (p.x(), p.y(), result); if (test_derivs) { image_ds->setpixel (p.x(), p.y(), dresultds); image_dt->setpixel (p.x(), p.y(), dresultdt); } } } void test_plain_texture (Mapping2D mapping) { std::cout << "Testing 2d texture " << filenames[0] << ", output = " << output_filename << "\n"; const int nchannels = 4; ImageSpec outspec (output_xres, output_yres, nchannels, TypeDesc::HALF); adjust_spec (outspec, dataformatname); ImageBuf image (outspec); OIIO::ImageBufAlgo::zero (image); ImageBuf image_ds, image_dt; if (test_derivs) { image_ds.reset (outspec); OIIO::ImageBufAlgo::zero (image_ds); image_dt.reset (outspec); OIIO::ImageBufAlgo::zero (image_dt); } ustring filename = filenames[0]; for (int iter = 0; iter < iters; ++iter) { if (iters > 1 && filenames.size() > 1) { // Use a different filename for each iteration int texid = std::min (iter, (int)filenames.size()-1); filename = (filenames[texid]); std::cout << "iter " << iter << " file " << filename << "\n"; } OIIO::ImageBufAlgo::parallel_image (OIIO::bind(plain_tex_region, OIIO::ref(image), filename, mapping, test_derivs ? &image_ds : NULL, test_derivs ? &image_dt : NULL, _1), get_roi(image.spec()), nthreads); if (resetstats) { std::cout << texsys->getstats(2) << "\n"; texsys->reset_stats (); } } if (! image.write (output_filename)) std::cerr << "Error writing " << output_filename << " : " << image.geterror() << "\n"; if (test_derivs) { if (! image_ds.write (output_filename+"-ds.exr")) std::cerr << "Error writing " << (output_filename+"-ds.exr") << " : " << image_ds.geterror() << "\n"; if (! image_dt.write (output_filename+"-dt.exr")) std::cerr << "Error writing " << (output_filename+"-dt.exr") << " : " << image_dt.geterror() << "\n"; } } void tex3d_region (ImageBuf &image, ustring filename, Mapping3D mapping, ROI roi) { TextureSystem::Perthread *perthread_info = texsys->get_perthread_info (); TextureSystem::TextureHandle *texture_handle = texsys->get_texture_handle (filename); int nchannels = nchannels_override ? nchannels_override : image.nchannels(); TextureOpt opt; initialize_opt (opt, nchannels); opt.fill = (fill >= 0.0f) ? fill : 0.0f; // opt.swrap = opt.twrap = opt.rwrap = TextureOpt::WrapPeriodic; float *result = ALLOCA (float, nchannels); float *dresultds = test_derivs ? ALLOCA (float, nchannels) : NULL; float *dresultdt = test_derivs ? ALLOCA (float, nchannels) : NULL; float *dresultdr = test_derivs ? ALLOCA (float, nchannels) : NULL; for (ImageBuf::Iterator p (image, roi); ! p.done(); ++p) { Imath::V3f P, dPdx, dPdy, dPdz; mapping (p.x(), p.y(), P, dPdx, dPdy, dPdz); // Call the texture system to do the filtering. bool ok = texsys->texture3d (texture_handle, perthread_info, opt, P, dPdx, dPdy, dPdz, nchannels, result, dresultds, dresultdt, dresultdr); if (! ok) { std::string e = texsys->geterror (); if (! e.empty()) { lock_guard lock (error_mutex); std::cerr << "ERROR: " << e << "\n"; } } // Save filtered pixels back to the image. for (int i = 0; i < nchannels; ++i) result[i] *= scalefactor; image.setpixel (p.x(), p.y(), result); } } void test_texture3d (ustring filename, Mapping3D mapping) { std::cout << "Testing 3d texture " << filename << ", output = " << output_filename << "\n"; int nchannels = nchannels_override ? nchannels_override : 4; ImageSpec outspec (output_xres, output_yres, nchannels, TypeDesc::HALF); adjust_spec (outspec, dataformatname); ImageBuf image (outspec); OIIO::ImageBufAlgo::zero (image); for (int iter = 0; iter < iters; ++iter) { // Trick: switch to second texture, if given, for second iteration if (iter && filenames.size() > 1) filename = filenames[1]; OIIO::ImageBufAlgo::parallel_image (OIIO::bind(tex3d_region, OIIO::ref(image), filename, mapping, _1), get_roi(image.spec()), nthreads); } if (! image.write (output_filename)) std::cerr << "Error writing " << output_filename << " : " << image.geterror() << "\n"; } static void test_shadow (ustring filename) { } static void test_environment (ustring filename) { } static void test_getimagespec_gettexels (ustring filename) { ImageSpec spec; int miplevel = 0; if (! texsys->get_imagespec (filename, 0, spec)) { std::cerr << "Could not get spec for " << filename << "\n"; std::string e = texsys->geterror (); if (! e.empty()) { lock_guard lock (error_mutex); std::cerr << "ERROR: " << e << "\n"; } return; } if (! test_gettexels) return; int w = std::min (spec.width, output_xres); int h = std::min (spec.height, output_yres); int nchannels = nchannels_override ? nchannels_override : spec.nchannels; ImageSpec postagespec (w, h, nchannels, TypeDesc::FLOAT); ImageBuf buf (postagespec); TextureOpt opt; initialize_opt (opt, nchannels); std::vector tmp (w*h*nchannels); int x = spec.x + spec.width/2 - w/2; int y = spec.y + spec.height/2 - h/2; for (int i = 0; i < iters; ++i) { bool ok = texsys->get_texels (filename, opt, miplevel, x, x+w, y, y+h, 0, 1, 0, nchannels, postagespec.format, &tmp[0]); if (! ok) std::cerr << texsys->geterror() << "\n"; } for (int y = 0; y < h; ++y) for (int x = 0; x < w; ++x) { imagesize_t texoffset = (y*w + x) * spec.nchannels; buf.setpixel (x, y, &tmp[texoffset]); } TypeDesc fmt (dataformatname); if (fmt != TypeDesc::UNKNOWN) buf.set_write_format (fmt); buf.write (output_filename); } static void test_hash () { std::vector fourbits (1<<4, 0); std::vector eightbits (1<<8, 0); std::vector sixteenbits (1<<16, 0); std::vector highereightbits (1<<8, 0); const size_t iters = 1000000; const int res = 4*1024; // Simulate tiles from a 4k image const int tilesize = 64; const int nfiles = iters / ((res/tilesize)*(res/tilesize)); std::cout << "Testing hashing with " << nfiles << " files of " << res << 'x' << res << " with " << tilesize << 'x' << tilesize << " tiles:\n"; ImageCache *imagecache = ImageCache::create (); // Set up the ImageCacheFiles outside of the timing loop using OIIO::pvt::ImageCacheImpl; using OIIO::pvt::ImageCacheFile; using OIIO::pvt::ImageCacheFileRef; std::vector icf; for (int f = 0; f < nfiles; ++f) { ustring filename = ustring::format ("%06d.tif", f); icf.push_back (new ImageCacheFile(*(ImageCacheImpl *)imagecache, NULL, filename)); } // First, just try to do raw timings of the hash Timer timer; size_t i = 0, hh = 0; for (int f = 0; f < nfiles; ++f) { for (int y = 0; y < res; y += tilesize) { for (int x = 0; x < res; x += tilesize, ++i) { OIIO::pvt::TileID id (*icf[f], 0, 0, x, y, 0); size_t h = id.hash(); hh += h; } } } std::cout << "hh = " << hh << "\n"; double time = timer(); double rate = (i/1.0e6) / time; std::cout << "Hashing rate: " << Strutil::format ("%3.2f", rate) << " Mhashes/sec\n"; // Now, check the quality of the hash by looking at the low 4, 8, and // 16 bits and making sure that they divide into hash buckets fairly // evenly. i = 0; for (int f = 0; f < nfiles; ++f) { for (int y = 0; y < res; y += tilesize) { for (int x = 0; x < res; x += tilesize, ++i) { OIIO::pvt::TileID id (*icf[f], 0, 0, x, y, 0); size_t h = id.hash(); ++ fourbits[h & 0xf]; ++ eightbits[h & 0xff]; ++ highereightbits[(h>>24) & 0xff]; ++ sixteenbits[h & 0xffff]; // if (i < 16) std::cout << Strutil::format("%llx\n", h); } } } size_t min, max; min = std::numeric_limits::max(); max = 0; for (int i = 0; i < 16; ++i) { if (fourbits[i] < min) min = fourbits[i]; if (fourbits[i] > max) max = fourbits[i]; } std::cout << "4-bit hash buckets range from " << min << " to " << max << "\n"; min = std::numeric_limits::max(); max = 0; for (int i = 0; i < 256; ++i) { if (eightbits[i] < min) min = eightbits[i]; if (eightbits[i] > max) max = eightbits[i]; } std::cout << "8-bit hash buckets range from " << min << " to " << max << "\n"; min = std::numeric_limits::max(); max = 0; for (int i = 0; i < 256; ++i) { if (highereightbits[i] < min) min = highereightbits[i]; if (highereightbits[i] > max) max = highereightbits[i]; } std::cout << "higher 8-bit hash buckets range from " << min << " to " << max << "\n"; min = std::numeric_limits::max(); max = 0; for (int i = 0; i < (1<<16); ++i) { if (sixteenbits[i] < min) min = sixteenbits[i]; if (sixteenbits[i] > max) max = sixteenbits[i]; } std::cout << "16-bit hash buckets range from " << min << " to " << max << "\n"; std::cout << "\n"; ImageCache::destroy (imagecache); } static const char *workload_names[] = { /*0*/ "None", /*1*/ "Everybody accesses the same spot in one file (handles)", /*2*/ "Everybody accesses the same spot in one file", /*3*/ "Coherent access, one file, each thread in similar spots", /*4*/ "Coherent access, one file, each thread in different spots", /*5*/ "Coherent access, many files, each thread in similar spots", /*6*/ "Coherent access, many files, each thread in different spots", /*7*/ "Coherent access, many files, partially overlapping texture sets", /*8*/ "Coherent access, many files, partially overlapping texture sets, no extra busy work", NULL }; void do_tex_thread_workout (int iterations, int mythread) { int nfiles = (int) filenames.size(); float s = 0.1f, t = 0.1f; int nchannels = nchannels_override ? nchannels_override : 3; float *result = ALLOCA (float, nchannels); TextureOpt opt; initialize_opt (opt, nchannels); float *dresultds = test_derivs ? ALLOCA (float, nchannels) : NULL; float *dresultdt = test_derivs ? ALLOCA (float, nchannels) : NULL; TextureSystem::Perthread *perthread_info = texsys->get_perthread_info (); TextureSystem::TextureHandle *texture_handle = texsys->get_texture_handle (filenames[0]); int pixel, whichfile = 0; ImageSpec spec0; texsys->get_imagespec (filenames[0], 0, spec0); // Compute a filter size that's between the first and second MIP levels. float fw = (1.0f / spec0.width) * 1.5f; float fh = (1.0f / spec0.height) * 1.5f; float dsdx = fw, dtdx = 0.0f, dsdy = 0.0f, dtdy = fh; for (int i = 0; i < iterations; ++i) { pixel = i; bool ok = false; // Several different texture access patterns switch (threadtimes) { case 1: // Workload 1: Speed of light: Static texture access (same // texture coordinates all the time, one file), with handles // and per-thread data already queried only once rather than // per-call. ok = texsys->texture (texture_handle, perthread_info, opt, s, t, dsdx, dtdx, dsdy, dtdy, nchannels, result, dresultds, dresultdt); break; case 2: // Workload 2: Static texture access, with filenames. ok = texsys->texture (filenames[0], opt, s, t, dsdx, dtdx, dsdy, dtdy, nchannels, result, dresultds, dresultdt); break; case 3: case 4: // Workload 3: One file, coherent texture coordinates. // // Workload 4: Each thread starts with a different texture // coordinate offset, so likely are not simultaneously // accessing the very same tile as the other threads. if (threadtimes == 4) pixel += 57557*mythread; break; case 5: case 6: // Workload 5: Coherent texture coordinates, but access // a series of textures at each coordinate. // // Workload 6: Each thread starts with a different texture // coordinate offset, so likely are not simultaneously // accessing the very same tile as the other threads. whichfile = i % nfiles; pixel = i / nfiles; if (threadtimes == 6) pixel += 57557*mythread; break; case 7: case 8: // Workload 7: Coherent texture coordinates, but access // a series of textures at each coordinate, which partially // overlap with other threads. { int file = i % 8; if (file < 2) // everybody accesses the first 2 files whichfile = std::min (file, nfiles-1); else // and a slowly changing set of 6 others whichfile = (file+11*mythread+i/1000) % nfiles; pixel = i / nfiles; pixel += 57557*mythread; } break; default: ASSERT_MSG (0, "Unkonwn thread work pattern %d", threadtimes); } if (! ok) { s = (((2*pixel) % spec0.width) + 0.5f) / spec0.width; t = (((2*((2*pixel) / spec0.width)) % spec0.height) + 0.5f) / spec0.height; ok = texsys->texture (filenames[whichfile], opt, s, t, dsdx, dtdx, dsdy, dtdy, nchannels, result, dresultds, dresultdt); } if (! ok) { lock_guard lock (error_mutex); std::cerr << "Unexpected error: " << texsys->geterror() << "\n"; return; } // Do some pointless work, to simulate that in a real app, there // would be operations interspersed with texture accesses. if (threadtimes != 8 /* skip on this test */) { for (int j = 0; j < 30; ++j) for (int c = 0; c < nchannels; ++c) result[c] = cosf (result[c]); } } // Force the compiler to not optimize away the "other work" for (int c = 0; c < nchannels; ++c) ASSERT (! isnan(result[c])); } // Launch numthreads threads each of which performs a workout of texture // accesses. void launch_tex_threads (int numthreads, int iterations) { texsys->invalidate_all (true); OIIO::thread_group threads; for (int i = 0; i < numthreads; ++i) { threads.create_thread (OIIO::bind(do_tex_thread_workout,iterations,i)); } ASSERT ((int)threads.size() == numthreads); threads.join_all (); } class GridImageInput : public ImageInput { public: GridImageInput () : m_miplevel(-1) { } virtual ~GridImageInput () { close(); } virtual const char * format_name (void) const { return "grid"; } virtual bool valid_file (const std::string &filename) const { return true; } virtual bool open (const std::string &name, ImageSpec &newspec) { return seek_subimage (0, 0, newspec); } virtual bool close () { return true; } virtual int current_miplevel (void) const { return m_miplevel; } virtual bool seek_subimage (int subimage, int miplevel, ImageSpec &newspec) { if (subimage > 0) return false; if (miplevel > 0 && automip /* if automip is on, don't generate MIP */) return false; if (miplevel == m_miplevel) return true; int res = 512; res >>= miplevel; if (res == 0) return false; m_spec = ImageSpec (res, res, 3, TypeDesc::FLOAT); m_spec.tile_width = std::min (64, res); m_spec.tile_height = std::min (64, res); m_spec.tile_depth = 1; newspec = m_spec; m_miplevel = miplevel; return true; } virtual bool read_native_scanline (int y, int z, void *data) { return false; } virtual bool read_native_tile (int xbegin, int ybegin, int zbegin, void *data) { float *tile = (float *)data; for (int z = zbegin, zend = z+m_spec.tile_depth; z < zend; ++z) for (int y = ybegin, yend = y+m_spec.tile_height; y < yend; ++y) for (int x = xbegin, xend = x+m_spec.tile_width; x < xend; ++x) { tile[0] = float(x)/m_spec.width; tile[2] = float(y)/m_spec.height; tile[1] = (((x/16)&1) == ((y/16)&1)) ? 1.0f/(m_miplevel+1) : 0.05f; tile += m_spec.nchannels; } return true; } private: int m_miplevel; }; ImageInput *make_grid_input () { return new GridImageInput; } void test_icwrite (int testicwrite) { std::cout << "Testing IC write, mode " << testicwrite << "\n"; // The global "shared" ImageCache will be the same one the // TextureSystem uses. ImageCache *ic = ImageCache::create (); // Set up the fake file ane add it int tw = 64, th = 64; // tile width and height int nc = nchannels_override ? nchannels_override : 3; // channels ImageSpec spec (512, 512, nc, TypeDesc::FLOAT); spec.depth = 1; spec.tile_width = tw; spec.tile_height = th; spec.tile_depth = 1; ustring filename (filenames[0]); bool ok = ic->add_file (filename, make_grid_input); if (! ok) std::cout << "ic->add_file error: " << ic->geterror() << "\n"; ASSERT (ok); // Now add all the tiles if it's a seeded map // testicwrite == 1 means to seed the first MIP level using add_tile. // testicwrite == 2 does not use add_tile, but instead will rely on // the make_grid_input custom ImageInput that constructs a pattern // procedurally. if (testicwrite == 1) { std::vector tile (spec.tile_pixels() * spec.nchannels); for (int ty = 0; ty < spec.height; ty += th) { for (int tx = 0; tx < spec.width; tx += tw) { // Construct a tile for (int y = 0; y < th; ++y) for (int x = 0; x < tw; ++x) { int index = (y*tw + x) * nc; int xx = x+tx, yy = y+ty; tile[index+0] = float(xx)/spec.width; tile[index+1] = float(yy)/spec.height; tile[index+2] = (!(xx%10) || !(yy%10)) ? 1.0f : 0.0f; } bool ok = ic->add_tile (filename, 0, 0, tx, ty, 0, 0, -1, TypeDesc::FLOAT, &tile[0]); if (! ok) { lock_guard lock (error_mutex); std::cout << "ic->add_tile error: " << ic->geterror() << "\n"; } ASSERT (ok); } } } } int main (int argc, const char *argv[]) { Filesystem::convert_native_arguments (argc, argv); getargs (argc, argv); OIIO::attribute ("threads", nthreads); texsys = TextureSystem::create (); std::cout << "Created texture system\n"; texsys->attribute ("statistics:level", 2); texsys->attribute ("autotile", autotile); texsys->attribute ("automip", (int)automip); texsys->attribute ("deduplicate", (int)dedup); if (cachesize >= 0) texsys->attribute ("max_memory_MB", cachesize); else texsys->getattribute ("max_memory_MB", TypeDesc::TypeFloat, &cachesize); if (maxfiles >= 0) texsys->attribute ("max_open_files", maxfiles); if (searchpath.length()) texsys->attribute ("searchpath", searchpath); if (nountiled) texsys->attribute ("accept_untiled", 0); if (nounmipped) texsys->attribute ("accept_unmipped", 0); texsys->attribute ("gray_to_rgb", gray_to_rgb); texsys->attribute ("flip_t", flip_t); if (test_construction) { Timer t; for (int i = 0; i < 1000000000; ++i) { TextureOpt opt; dummyptr = &opt; // This forces the optimizer to keep the loop } std::cout << "TextureOpt construction: " << t() << " ns\n"; TextureOpt canonical, copy; t.reset(); t.start(); for (int i = 0; i < 1000000000; ++i) { memcpy (©, &canonical, sizeof(TextureOpt)); dummyptr = © // This forces the optimizer to keep the loop } std::cout << "TextureOpt memcpy: " << t() << " ns\n"; } if (testicwrite && filenames.size()) { test_icwrite (testicwrite); } if (test_getimagespec) { ImageSpec spec; for (int i = 0; i < iters; ++i) { texsys->get_imagespec (filenames[0], 0, spec); } iters = 0; } if (test_gettexels) { test_getimagespec_gettexels (filenames[0]); iters = 0; } if (testhash) { test_hash (); } Imath::M33f scale; scale.scale (Imath::V2f (0.3, 0.3)); Imath::M33f rot; rot.rotate (radians(25.0f)); Imath::M33f trans; trans.translate (Imath::V2f (0.75f, 0.25f)); Imath::M33f persp (2, 0, 0, 0, 0.8, -0.55, 0, 0, 1); xform = persp * rot * trans * scale; xform.invert(); if (threadtimes) { // If the --iters flag was used, do that number of iterations total // (divided among the threads). If not supplied (iters will be 1), // then use a large constant *per thread*. const int iterations = iters>1 ? iters : 2000000; std::cout << "Workload: " << workload_names[threadtimes] << "\n"; std::cout << "texture cache size = " << cachesize << " MB\n"; std::cout << "hw threads = " << Sysutil::hardware_concurrency() << "\n"; std::cout << "times are best of " << ntrials << " trials\n\n"; std::cout << "threads time (s) efficiency\n"; std::cout << "-------- -------- ----------\n"; if (nthreads == 0) nthreads = Sysutil::hardware_concurrency(); static int threadcounts[] = { 1, 2, 4, 8, 12, 16, 24, 32, 64, 128, 1024, 1<<30 }; float single_thread_time = 0.0f; for (int i = 0; threadcounts[i] <= nthreads; ++i) { int nt = wedge ? threadcounts[i] : nthreads; int its = iters>1 ? (std::max (1, iters/nt)) : iterations; // / nt; double range; double t = time_trial (OIIO::bind(launch_tex_threads,nt,its), ntrials, &range); if (nt == 1) single_thread_time = (float)t; float efficiency = (single_thread_time /*/nt*/) / (float)t; std::cout << Strutil::format ("%2d %8.2f %6.1f%% range %.2f\t(%d iters/thread)\n", nt, t, efficiency*100.0f, range, its); if (! wedge) break; // don't loop if we're not wedging } std::cout << "\n"; } else if (iters > 0 && filenames.size()) { ustring filename (filenames[0]); test_gettextureinfo (filenames[0]); const char *texturetype = "Plain Texture"; texsys->get_texture_info (filename, 0, ustring("texturetype"), TypeDesc::STRING, &texturetype); Timer timer; if (! strcmp (texturetype, "Plain Texture")) { if (nowarp) test_plain_texture (map_default); else if (tube) test_plain_texture (map_tube); else if (filtertest) test_plain_texture (map_filtertest); else test_plain_texture (map_warp); } if (! strcmp (texturetype, "Volume Texture")) { if (nowarp) test_texture3d (filename, map_default_3D); else test_texture3d (filename, map_warp_3D); } if (! strcmp (texturetype, "Shadow")) { test_shadow (filename); } if (! strcmp (texturetype, "Environment")) { test_environment (filename); } test_getimagespec_gettexels (filename); std::cout << "Time: " << Strutil::timeintervalformat (timer()) << "\n"; } if (test_statquery) { std::cout << "Testing statistics queries:\n"; int total_files = 0; texsys->getattribute ("total_files", total_files); std::cout << " Total files: " << total_files << "\n"; std::vector all_filenames (total_files); std::cout << TypeDesc(TypeDesc::STRING,total_files) << "\n"; texsys->getattribute ("all_filenames", TypeDesc(TypeDesc::STRING,total_files), &all_filenames[0]); for (int i = 0; i < total_files; ++i) { int timesopened = 0; int64_t bytesread = 0; float iotime = 0.0f; int64_t data_size = 0, file_size = 0; texsys->get_texture_info (all_filenames[i], 0, ustring("stat:timesopened"), TypeDesc::INT, ×opened); texsys->get_texture_info (all_filenames[i], 0, ustring("stat:bytesread"), TypeDesc::INT64, &bytesread); texsys->get_texture_info (all_filenames[i], 0, ustring("stat:iotime"), TypeDesc::FLOAT, &iotime); texsys->get_texture_info (all_filenames[i], 0, ustring("stat:image_size"), TypeDesc::INT64, &data_size); texsys->get_texture_info (all_filenames[i], 0, ustring("stat:file_size"), TypeDesc::INT64, &file_size); std::cout << Strutil::format (" %d: %s opens=%d, read=%s, time=%s, data=%s, file=%s\n", i, all_filenames[i], timesopened, Strutil::memformat(bytesread), Strutil::timeintervalformat(iotime,2), Strutil::memformat(data_size), Strutil::memformat(file_size)); } } std::cout << "Memory use: " << Strutil::memformat (Sysutil::memory_used(true)) << "\n"; TextureSystem::destroy (texsys); std::cout << "\nustrings: " << ustring::getstats(false) << "\n\n"; return 0; } openimageio-1.7.17~dfsg0.orig/CMakeLists.txt0000644000175000017500000007324713151711064017102 0ustar mfvmfvproject (OpenImageIO) # Release version of the library set (OIIO_VERSION_MAJOR 1) set (OIIO_VERSION_MINOR 7) set (OIIO_VERSION_PATCH 17) set (OIIO_VERSION_RELEASE_TYPE "") # "dev", "betaX", "RCY", "" cmake_minimum_required (VERSION 2.6) if (NOT CMAKE_VERSION VERSION_LESS 2.8.4) cmake_policy (SET CMP0017 NEW) endif () if (NOT CMAKE_VERSION VERSION_LESS 3.0) cmake_policy (SET CMP0025 NEW) # Detect AppleClang for new CMake endif () if (NOT CMAKE_VERSION VERSION_LESS 3.2.2) cmake_policy (SET CMP0014 OLD) cmake_policy (SET CMP0042 OLD) cmake_policy (SET CMP0046 OLD) endif () set (CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS TRUE) # N.B./FIXME - after CMake 3.0 is our minimum, we should change many of the # add_definitions to add_compile_options. In newer cmake versions, the # former is designated for definitions (-Dblah) and the latter is for # compile flags (-Wno-foo). if (VERBOSE) message (STATUS "Project source dir = ${PROJECT_SOURCE_DIR}") endif () message (STATUS "Project build dir = ${CMAKE_BINARY_DIR}") if ("${PROJECT_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") message (FATAL_ERROR "Not allowed to run in-source build!") endif () if (NOT CMAKE_BUILD_TYPE) set (CMAKE_BUILD_TYPE "Release") endif () if (CMAKE_BUILD_TYPE STREQUAL "Debug") set (DEBUGMODE ON) endif () if (CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") # cmake bug workaround -- on some platforms, cmake doesn't set # NDEBUG for RelWithDebInfo mode add_definitions ("-DNDEBUG") endif () option (CMAKE_USE_FOLDERS "Use the FOLDER target property to organize targets into folders." ON) mark_as_advanced (CMAKE_USE_FOLDERS) if (CMAKE_USE_FOLDERS) set_property (GLOBAL PROPERTY USE_FOLDERS ON) endif () ## turn on more detailed warnings and consider warnings as errors set (STOP_ON_WARNING ON CACHE BOOL "Stop building if there are any compiler warnings") if (NOT MSVC) add_definitions ("-Wall") if (STOP_ON_WARNING) add_definitions ("-Werror") endif () endif () message (STATUS "CMAKE_CXX_COMPILER is ${CMAKE_CXX_COMPILER}") message (STATUS "CMAKE_CXX_COMPILER_ID is ${CMAKE_CXX_COMPILER_ID}") # Figure out which compiler we're using if (CMAKE_COMPILER_IS_GNUCC) execute_process (COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) if (VERBOSE) message (STATUS "Using gcc ${GCC_VERSION} as the compiler") endif () endif () # Figure out which compiler we're using, for tricky cases if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER MATCHES "[Cc]lang") # If using any flavor of clang, set CMAKE_COMPILER_IS_CLANG. If it's # Apple's variety, set CMAKE_COMPILER_IS_APPLECLANG and # APPLECLANG_VERSION_STRING, otherwise for generic clang set # CLANG_VERSION_STRING. set (CMAKE_COMPILER_IS_CLANG 1) EXECUTE_PROCESS( COMMAND ${CMAKE_CXX_COMPILER} --version OUTPUT_VARIABLE clang_full_version_string ) if (clang_full_version_string MATCHES "Apple") set (CMAKE_CXX_COMPILER_ID "AppleClang") set (CMAKE_COMPILER_IS_APPLECLANG 1) string (REGEX REPLACE ".* version ([0-9]+\\.[0-9]+).*" "\\1" APPLECLANG_VERSION_STRING ${clang_full_version_string}) if (VERBOSE) message (STATUS "The compiler is Clang: ${CMAKE_CXX_COMPILER_ID} version ${APPLECLANG_VERSION_STRING}") endif () else () string (REGEX REPLACE ".* version ([0-9]+\\.[0-9]+).*" "\\1" CLANG_VERSION_STRING ${clang_full_version_string}) if (VERBOSE) message (STATUS "The compiler is Clang: ${CMAKE_CXX_COMPILER_ID} version ${CLANG_VERSION_STRING}") endif () endif () elseif (CMAKE_CXX_COMPILER_ID MATCHES "Intel") set (CMAKE_COMPILER_IS_INTEL 1) if (VERBOSE) message (STATUS "Using Intel as the compiler") endif () endif () # Options common to gcc and clang if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG) # CMake doesn't automatically know what do do with # include_directories(SYSTEM...) when using clang or gcc. set (CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-isystem ") # Ensure this macro is set for stdint.h add_definitions ("-D__STDC_LIMIT_MACROS") add_definitions ("-D__STDC_CONSTANT_MACROS") # this allows native instructions to be used for sqrtf instead of a function call add_definitions ("-fno-math-errno") if (HIDE_SYMBOLS AND NOT DEBUGMODE) # Turn default symbol visibility to hidden set (VISIBILITY_COMMAND "-fvisibility=hidden -fvisibility-inlines-hidden") add_definitions (${VISIBILITY_COMMAND}) if (CMAKE_SYSTEM_NAME MATCHES "Linux|kFreeBSD" OR CMAKE_SYSTEM_NAME STREQUAL "GNU") # Linux/FreeBSD/Hurd: also hide all the symbols of dependent # libraries to prevent clashes if an app using OIIO is linked # against other verions of our dependencies. if (NOT VISIBILITY_MAP_FILE) set (VISIBILITY_MAP_FILE "${PROJECT_SOURCE_DIR}/src/libOpenImageIO/libOpenImageIO.map") endif () set (VISIBILITY_MAP_COMMAND "-Wl,--version-script=${VISIBILITY_MAP_FILE}") endif () endif () if (VERBOSE) message (STATUS "VISIBILITY_MAP_COMMAND = ${VISIBILITY_MAP_COMMAND}") endif () endif () # Clang-specific options if (CMAKE_COMPILER_IS_CLANG OR CMAKE_COMPILER_IS_APPLECLANG) # Disable some warnings for Clang, for some things that are too awkward # to change just for the sake of having no warnings. add_definitions ("-Wno-unused-function") add_definitions ("-Wno-overloaded-virtual") add_definitions ("-Wno-unneeded-internal-declaration") add_definitions ("-Wno-unused-private-field") add_definitions ("-Wno-tautological-compare") # disable warning about unused command line arguments add_definitions ("-Qunused-arguments") # Don't warn if we ask it not to warn about warnings it doesn't know add_definitions ("-Wunknown-warning-option") if (CLANG_VERSION_STRING VERSION_GREATER 3.5 OR APPLECLANG_VERSION_STRING VERSION_GREATER 6.1) add_definitions ("-Wno-unused-local-typedefs") endif () if (CLANG_VERSION_STRING VERSION_EQUAL 3.9 OR CLANG_VERSION_STRING VERSION_GREATER 3.9) # Don't warn about using unknown preprocessor symbols in #if'set add_definitions ("-Wno-expansion-to-defined") endif () endif () # gcc specific options if (CMAKE_COMPILER_IS_GNUCC AND NOT (CMAKE_COMPILER_IS_CLANG OR CMAKE_COMPILER_IS_APPLECLANG)) if (NOT ${GCC_VERSION} VERSION_LESS 4.8) # suppress a warning that Boost::Python hits in g++ 4.8 add_definitions ("-Wno-error=unused-local-typedefs") add_definitions ("-Wno-unused-local-typedefs") endif () if (NOT ${GCC_VERSION} VERSION_LESS 4.5) add_definitions ("-Wno-unused-result") endif () if (NOT ${GCC_VERSION} VERSION_LESS 6.0) add_definitions ("-Wno-error=misleading-indentation") endif () endif () set (VERBOSE OFF CACHE BOOL "Print lots of messages while compiling") set (EMBEDPLUGINS ON CACHE BOOL "Embed format plugins in libOpenImageIO") set (OIIO_BUILD_TOOLS ON CACHE BOOL "Build the command-line tools") set (OIIO_BUILD_TESTS ON CACHE BOOL "Build the unit tests") set (BUILDSTATIC OFF CACHE BOOL "Build static library instead of shared") set (LINKSTATIC OFF CACHE BOOL "Link with static external libraries when possible") set (HIDE_SYMBOLS OFF CACHE BOOL "Hide symbols not in the public API") set (USE_OPENGL ON CACHE BOOL "Include OpenGL support") set (USE_QT ON CACHE BOOL "Include Qt support") set (FORCE_OPENGL_1 OFF CACHE BOOL "Force iv to use OpenGL's fixed pipeline") set (USE_PYTHON ON CACHE BOOL "Build the Python bindings") set (USE_PYTHON3 OFF CACHE BOOL "Build the Python3 bindings") set (PYTHON_VERSION 2.6) set (PYTHON3_VERSION 3.2) set (PYLIB_INCLUDE_SONAME OFF CACHE BOOL "If ON, soname/soversion will be set for Python module library") set (PYLIB_LIB_PREFIX OFF CACHE BOOL "If ON, prefix the Python module with 'lib'") set (USE_FIELD3D ON CACHE BOOL "Use Field3D if found") set (USE_FFMPEG ON CACHE BOOL "Use FFmpeg if found") set (JPEG_PATH "" CACHE STRING "Custom JPEG path") set (USE_JPEGTURBO ON CACHE BOOL "Use JPEG-Turbo if found") set (JPEGTURBO_PATH "" CACHE STRING "Custom JPEG-Turbo path") set (USE_OPENJPEG ON CACHE BOOL "Use OpenJpeg if found") set (USE_OCIO ON CACHE BOOL "Use OpenColorIO for color management if found") set (USE_OPENCV ON CACHE BOOL "Use OpenCV if found") set (USE_OPENSSL OFF CACHE BOOL "Use OpenSSL if found (for faster SHA-1)") set (USE_FREETYPE ON CACHE BOOL "Use Freetype if found") set (USE_GIF ON CACHE BOOL "Use GIF if found") set (USE_PTEX ON CACHE BOOL "Use PTex if found") set (USE_LIBRAW ON CACHE BOOL "Use LibRaw if found") set (LIBRAW_PATH "" CACHE STRING "Custom LibRaw path") set (NOTHREADS OFF CACHE BOOL "Compile with no threads or locking") set (OIIO_THREAD_ALLOW_DCLP ON CACHE BOOL "OIIO threads may use DCLP for speed") set (USE_NUKE ON CACHE BOOL "Build Nuke plugins, if Nuke is found") set (Nuke_ROOT "" CACHE STRING "Where to find Nuke installation") set (NUKE_VERSION 7.0) set (USE_EXTERNAL_PUGIXML OFF CACHE BOOL "Use an externally built shared library version of the pugixml library") set (PUGIXML_HOME "" CACHE STRING "Hint about where to find external PugiXML library") set (SOVERSION ${OIIO_VERSION_MAJOR}.${OIIO_VERSION_MINOR} CACHE STRING "Set the SO version in the SO name of the output library") set (BUILD_OIIOUTIL_ONLY OFF CACHE BOOL "If ON, will build *only* libOpenImageIO_Util") set (USE_CPP11 ON CACHE BOOL "Compile in C++11 mode") set (USE_CPP14 OFF CACHE BOOL "Compile in C++14 mode") set (USE_LIBCPLUSPLUS OFF CACHE BOOL "Compile with clang libc++") set (EXTRA_CPP_ARGS "" CACHE STRING "Extra C++ command line definitions") set (EXTRA_DSO_LINK_ARGS "" CACHE STRING "Extra command line definitions when building DSOs") set (USE_SIMD "" CACHE STRING "Use SIMD directives (0, sse2, sse3, ssse3, sse4.1, sse4.2, avx, avx2, avx512f, f16c)") set (USE_CCACHE ON CACHE BOOL "Use ccache if found") set (CODECOV OFF CACHE BOOL "Build code coverage tests") # Use ccache if found find_program (CCACHE_FOUND ccache) if (CCACHE_FOUND AND USE_CCACHE) if (CMAKE_COMPILER_IS_CLANG AND USE_QT AND (NOT DEFINED ENV{CCACHE_CPP2})) message (STATUS "Ignoring ccache because clang + Qt + env CCACHE_CPP2 is not set") else () set_property (GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) set_property (GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) endif () endif () if (NOTHREADS) message (STATUS "NO THREADS!") add_definitions ("-DNOTHREADS=1") else () if (VERBOSE) message (STATUS "Building with thread support") if (NOT OIIO_THREAD_ALLOW_DCLP) add_definitions ("-DOIIO_THREAD_ALLOW_DCLP=0") endif () endif () endif () if (EXTRA_CPP_ARGS) message (STATUS "Extra C++ args: ${EXTRA_CPP_ARGS}") add_definitions ("${EXTRA_CPP_ARGS}") endif() if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG OR CMAKE_COMPILER_IS_INTEL) if (USE_CPP11 OR USE_CPP14) if (USE_CPP14) message (STATUS "Building for C++14") add_definitions ("-std=c++14") elseif (USE_CPP11) message (STATUS "Building for C++11") add_definitions ("-std=c++11") endif () if (CMAKE_COMPILER_IS_CLANG) # C++ >= 11 doesn't like 'register' keyword, which is in Qt headers add_definitions ("-Wno-deprecated-register") endif () else () message (STATUS "Building for C++03!") endif () endif () if (USE_LIBCPLUSPLUS AND CMAKE_COMPILER_IS_CLANG) message (STATUS "Using libc++") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") endif () set (USE_fPIC OFF CACHE BOOL "Build with -fPIC") if (USE_fPIC) add_definitions ("-fPIC") endif () # Options common to gcc and clang if (CODECOV AND (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG)) message (STATUS "Compiling for code coverage analysis") add_definitions ("-ftest-coverage -fprofile-arcs -O0 -DOIIO_CODE_COVERAGE=1") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -ftest-coverage -fprofile-arcs") set (CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -ftest-coverage -fprofile-arcs") set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -ftest-coverage -fprofile-arcs") endif () if (DEFINED ENV{TRAVIS} OR DEFINED ENV{APPVEYOR} OR DEFINED ENV{CI}) add_definitions ("-DOIIO_CI=1") endif () if (NOT USE_SIMD STREQUAL "") message (STATUS "Compiling with SIMD level ${USE_SIMD}") if (USE_SIMD STREQUAL "0") add_definitions ("-DOIIO_NO_SSE=1") else () string (REPLACE "," ";" SIMD_FEATURE_LIST ${USE_SIMD}) foreach (feature ${SIMD_FEATURE_LIST}) if (VERBOSE) message (STATUS "SIMD feature: ${feature}") endif () if (MSVC OR CMAKE_COMPILER_IS_INTEL) add_definitions ("/arch:${feature}") else () add_definitions ("-m${feature}") endif () if (feature STREQUAL "fma" AND (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG)) # If fma is requested, for numerical accuracy sake, turn it # off by default except when we explicitly use madd. At some # future time, we should look at this again carefully and # see if we want to use it more widely by ffp-contract=fast. add_definitions ("-ffp-contract=off") endif () endforeach() endif () endif () # Set the default namespace if (NOT OIIO_NAMESPACE) set (OIIO_NAMESPACE OpenImageIO CACHE STRING "Specify the master OpenImageIO C++ namespace: Options include OpenImageIO OpenImageIO_ etc." FORCE) endif () message(STATUS "Setting Namespace to: ${OIIO_NAMESPACE}") if (BUILDSTATIC) message (STATUS "Building static libraries") add_definitions(-DOIIO_STATIC_BUILD=1) if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") # On Linux, the lack of -fPIC when building static libraries seems # incompatible with the dynamic library needed for the Python bindings. set (USE_PYTHON OFF) set (USE_PYTHON3 OFF) endif () if (WIN32) set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") endif () endif() # Use .a files if LINKSTATIC is enabled if (LINKSTATIC) set (_orig_link_suffixes ${CMAKE_FIND_LIBRARY_SUFFIXES}) message (STATUS "Statically linking external libraries") if (WIN32) set (CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) else () # set (CMAKE_FIND_LIBRARY_SUFFIXES .a) set (CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) endif () add_definitions (-DBoost_USE_STATIC_LIBS=1) set (Boost_USE_STATIC_LIBS 1) endif () set (CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/src/cmake/modules" "${PROJECT_SOURCE_DIR}/src/cmake") include (util_macros) include (oiio_macros) include (platform) include (externalpackages) include_directories ( BEFORE "${CMAKE_SOURCE_DIR}/src/include" "${CMAKE_BINARY_DIR}/include/OpenImageIO" ) ########################################################################### # Paths for install tree customization. Note that relative paths are relative # to CMAKE_INSTALL_PREFIX. set (DEFAULT_BIN_INSTALL_DIR "bin") set (DEFAULT_LIB_INSTALL_DIR "lib") set (DEFAULT_INCLUDE_INSTALL_DIR "include/OpenImageIO") if (UNIX AND NOT SELF_CONTAINED_INSTALL_TREE) # Try to be well-behaved and install into reasonable places according to # the "standard" unix directory heirarchy # TODO: Figure out how to get the correct python directory set (DEFAULT_PYLIB_INSTALL_DIR "lib/python/site-packages") set (DEFAULT_PYLIB3_INSTALL_DIR "lib/python3/site-packages") set (DEFAULT_DOC_INSTALL_DIR "share/doc/OpenImageIO") set (DEFAULT_MAN_INSTALL_DIR "share/man/man1") set (DEFAULT_FONTS_INSTALL_DIR "share/fonts/oiio") else () # Here is the "self-contained install tree" case: the expectation here is # that everything OIIO related will go into its own directory, not into # some standard system heirarchy. set (DEFAULT_PYLIB_INSTALL_DIR "python") set (DEFAULT_PYLIB3_INSTALL_DIR "python3") set (DEFAULT_DOC_INSTALL_DIR "doc") set (DEFAULT_MAN_INSTALL_DIR "doc/man") set (DEFAULT_FONTS_INSTALL_DIR "fonts/oiio") endif () if (EXEC_INSTALL_PREFIX) # Tack on an extra prefix to support multi-arch builds. set (DEFAULT_BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/${DEFAULT_BIN_INSTALL_DIR}") set (DEFAULT_LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/${DEFAULT_LIB_INSTALL_DIR}") set (DEFAULT_PYLIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/${DEFAULT_PYLIB_INSTALL_DIR}") set (DEFAULT_PYLIB3_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/${DEFAULT_PYLIB3_INSTALL_DIR}") set (DEFAULT_FONTS_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/${DEFAULT_FONTS_INSTALL_DIR}") endif () # Set up cmake cache variables corresponding to the defaults deduced above, so # that the user can override them as desired: set (BIN_INSTALL_DIR ${DEFAULT_BIN_INSTALL_DIR} CACHE STRING "Install location for binaries (relative to CMAKE_INSTALL_PREFIX or absolute)") set (LIB_INSTALL_DIR ${DEFAULT_LIB_INSTALL_DIR} CACHE STRING "Install location for libraries (relative to CMAKE_INSTALL_PREFIX or absolute)") set (PYLIB_INSTALL_DIR ${DEFAULT_PYLIB_INSTALL_DIR} CACHE STRING "Install location for python libraries (relative to CMAKE_INSTALL_PREFIX or absolute)") set (PYLIB3_INSTALL_DIR ${DEFAULT_PYLIB3_INSTALL_DIR} CACHE STRING "Install location for python3 libraries (relative to CMAKE_INSTALL_PREFIX or absolute)") set (INCLUDE_INSTALL_DIR ${DEFAULT_INCLUDE_INSTALL_DIR} CACHE STRING "Install location of header files (relative to CMAKE_INSTALL_PREFIX or absolute)") set (DOC_INSTALL_DIR ${DEFAULT_DOC_INSTALL_DIR} CACHE STRING "Install location for documentation (relative to CMAKE_INSTALL_PREFIX or absolute)") set (FONTS_INSTALL_DIR ${DEFAULT_FONTS_INSTALL_DIR} CACHE STRING "Install location for fonts (relative to CMAKE_INSTALL_PREFIX or absolute)") if (UNIX) set (MAN_INSTALL_DIR ${DEFAULT_MAN_INSTALL_DIR} CACHE STRING "Install location for manual pages (relative to CMAKE_INSTALL_PREFIX or absolute)") endif() set (PLUGIN_SEARCH_PATH "" CACHE STRING "Default plugin search path") set (INSTALL_DOCS ON CACHE BOOL "Install documentation") set (INSTALL_FONTS ON CACHE BOOL "Install default fonts") ########################################################################### # Rpath handling. if (CMAKE_SKIP_RPATH) # We need to disallow the user from truly setting CMAKE_SKIP_RPATH, since # we want to run the generated executables from the build tree in order to # generate the manual page documentation. However, we make sure the # install rpath is unset so that the install tree is still free of rpaths # for linux packaging purposes. set (CMAKE_SKIP_RPATH FALSE) unset (CMAKE_INSTALL_RPATH) else () set (CMAKE_INSTALL_RPATH "${LIB_INSTALL_DIR}") if (NOT IS_ABSOLUTE ${CMAKE_INSTALL_RPATH}) set (CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}") endif () set (CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) endif () set (MACOSX_RPATH ON) if (MSVC) add_definitions (/W1) add_definitions (-D_CRT_SECURE_NO_DEPRECATE) add_definitions (-D_CRT_SECURE_NO_WARNINGS) add_definitions (-D_CRT_NONSTDC_NO_WARNINGS) add_definitions (-D_SCL_SECURE_NO_WARNINGS) add_definitions (-DJAS_WIN_MSVC_BUILD) add_definitions (-DOPENEXR_DLL) if (LINKSTATIC) add_definitions (-DBoost_USE_STATIC_LIBS=1) else () add_definitions (-DBOOST_ALL_DYN_LINK) endif () endif (MSVC) # We want CTest for testing # N.B. This needs to be added before any of the subdirectories, or # their add_test commands will not register. include (CTest) # Tell CMake to process the sub-directories add_subdirectory (src/libutil) # Add IO plugin directories -- if we are embedding plugins, we need to visit # these directories BEFORE the OpenImageIO target is established (in # src/libOpenImageIO). For each plugin, we append to the lists of source # files, format libs, include directories, and definitions, all of which # will be used by src/libOpenImageIO. set (libOpenImageIO_srcs "") set (format_plugin_libs "") set (format_plugin_include_dirs "") set (format_plugin_definitions "") file (GLOB all_format_plugin_dirs src/*.imageio) if ("${OIIO_SITE}" STREQUAL "SPI") # SPI only -- because of a workaround for a very weird linkage issue # specific to our system, we need to be sure libtiff is referenced first. file (GLOB tiff_format_plugin_dir src/tiff.imageio) list (REMOVE_ITEM all_format_plugin_dirs ${tiff_format_plugin_dir}) list (INSERT all_format_plugin_dirs 0 ${tiff_format_plugin_dir}) endif () if (EMBEDPLUGINS AND NOT BUILD_OIIOUTIL_ONLY) foreach (plugin_dir ${all_format_plugin_dirs}) add_subdirectory (${plugin_dir}) endforeach () endif () if (NOT BUILD_OIIOUTIL_ONLY) add_subdirectory (src/libOpenImageIO) endif () if (OIIO_BUILD_TOOLS AND NOT BUILD_OIIOUTIL_ONLY) add_subdirectory (src/iconvert) add_subdirectory (src/idiff) add_subdirectory (src/igrep) add_subdirectory (src/iinfo) add_subdirectory (src/maketx) add_subdirectory (src/oiiotool) add_subdirectory (src/testtex) add_subdirectory (src/iv) endif () # Add IO plugin directories -- if we are not embedding plugins, we need to # do it AFTER the OpenImageIO target is established (in src/libOpenImageIO), # since each plugin needs libOpenImageIO to be a dependency. if (NOT EMBEDPLUGINS AND NOT BUILD_OIIOUTIL_ONLY) foreach (plugin_dir ${all_format_plugin_dirs}) add_subdirectory (${plugin_dir}) endforeach () endif () if (USE_PYTHON AND boost_PYTHON_FOUND AND NOT BUILD_OIIOUTIL_ONLY) add_subdirectory (src/python) endif () if (USE_PYTHON3 AND boost_PYTHON_FOUND AND NOT BUILD_OIIOUTIL_ONLY) #build the python3 module in a different binary directory since it will #have the same name as the python2 module (e.g. OpenImageIO.so) add_subdirectory (src/python src/python3) endif () add_subdirectory (src/include) add_subdirectory (src/doc) add_subdirectory (src/fonts) add_subdirectory (src/nuke) ######################################################################### # Testing # # Just call oiio_add_tests(testname...) for each test. Additional # optional arguments include: # FOUNDVAR specifies the name of a CMAKE variable; if not defined, # the test will not be added for 'make test' (helpful # for excluding tests for libraries not found). # IMAGEDIR specifies a directory for test images, one level higher # than where the oiio top level source lives -- a # message will be printed if not found. # URL URL where the test images can be found, will be # incorporated into the error message if the test # image directory is not found. # LABEL If set to "broken", will designate the test as one # that is known to be broken, so will only be run # for "make testall", but not "make test". # # Make a build/platform/testsuite directory, and copy the master runtest.py # there. The rest is up to the tests themselves. file (MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/testsuite") add_custom_command (OUTPUT "${CMAKE_BINARY_DIR}/testsuite/runtest.py" COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/testsuite/runtest.py" "${CMAKE_BINARY_DIR}/testsuite/runtest.py" MAIN_DEPENDENCY "${CMAKE_SOURCE_DIR}/testsuite/runtest.py") add_custom_target ( CopyFiles ALL DEPENDS "${CMAKE_BINARY_DIR}/testsuite/runtest.py" ) # Basic tests that apply even to continuous integration tests: oiio_add_tests ( gpsread misnamed-file nonwhole-tiles oiiotool oiiotool-composite oiiotool-deep oiiotool-fixnan oiiotool-pattern oiiotool-readerror oiiotool-subimage oiiotool-text maketx oiiotool-maketx dither dup-channels dpx ico iff png psd psd-colormodes rla sgi python-typedesc python-imagespec python-roi python-deep python-imageinput python-imageoutput python-imagebuf python-imagebufalgo texture-interp-bicubic texture-blurtube texture-crop texture-cropover texture-derivs texture-fill texture-filtersize texture-flipt texture-gettexels texture-gray texture-mip-nomip texture-mip-trilinear texture-overscan texture-pointsample texture-uint8 texture-width0blur texture-fat texture-skinny texture-wrapfill texture-missing texture-res texture-udim texture-udim2 ) # Advanced tests that are done by hand and for releases: # FIXME -- at some point, try to fix these or provide new ref images if (NOT DEFINED ENV{TRAVIS}) oiio_add_tests ( texture-half texture-uint16 texture-interp-bilinear texture-interp-closest texture-mip-onelevel texture-icwrite ) endif () # List testsuites which need special external reference images from the web # here: oiio_add_tests (bmp IMAGEDIR bmpsuite URL http://entropymine.com/jason/bmpsuite/bmpsuite.zip) oiio_add_tests (tiff-suite tiff-depths tiff-misc IMAGEDIR libtiffpic URL http://www.remotesensing.org/libtiff/images.html) oiio_add_tests (openexr-suite openexr-multires openexr-chroma openexr-v2 perchannel IMAGEDIR openexr-images URL http://www.openexr.com/downloads.html) oiio_add_tests (gif FOUNDVAR GIF_FOUND IMAGEDIR oiio-images URL "Recent checkout of oiio-images") oiio_add_tests (jpeg2000 FOUNDVAR OPENJPEG_FOUND IMAGEDIR j2kp4files_v1_5 URL http://www.itu.int/net/ITU-T/sigdb/speimage/ImageForm-s.aspx?val=10100803) oiio_add_tests (pnm IMAGEDIR oiio-images/pnm URL "Recent checkout of oiio-images") oiio_add_tests (targa-tgautils IMAGEDIR TGAUTILS URL http://tgautils.inequation.org/) oiio_add_tests (fits IMAGEDIR fits-images URL http://www.cv.nrao.edu/fits/data/tests/) oiio_add_tests (webp FOUNDVAR WEBP_FOUND IMAGEDIR webp-images URL http://code.google.com/speed/webp/gallery.html) oiio_add_tests (ptex FOUNDVAR PTEX_FOUND) if (NOT DEFINED ENV{TRAVIS}) oiio_add_tests (texture-field3d field3d FOUNDVAR FIELD3D_FOUND) endif () if (SPI_TESTS) oiio_add_tests (oiiotool-spi FOUNDVAR SPI_TESTS IMAGEDIR spi-oiio-tests URL "noplace -- it's SPI specific tests") endif () ######################################################################### # Packaging set (CPACK_PACKAGE_VERSION_MAJOR ${OIIO_VERSION_MAJOR}) set (CPACK_PACKAGE_VERSION_MINOR ${OIIO_VERSION_MINOR}) set (CPACK_PACKAGE_VERSION_PATCH ${OIIO_VERSION_PATCH}) # "Vendor" is only used in copyright notices, so we use the same thing that # the rest of the copyright notices say. set (CPACK_PACKAGE_VENDOR "Larry Gritz et al.") set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "OpenImageIO is an open source library for reading and writing image file formats, a nice format-agnostic image viewer, and other image-related classes and utilities.") set (CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/src/doc/Description.txt") set (CPACK_PACKAGE_FILE_NAME OpenImageIO-${OIIO_VERSION_MAJOR}.${OIIO_VERSION_MINOR}.${OIIO_VERSION_PATCH}-${platform}) #SET (CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_SOURCE_DIR}") file (MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/cpack") file (COPY "${PROJECT_SOURCE_DIR}/LICENSE" DESTINATION "${CMAKE_BINARY_DIR}/cpack") file (RENAME "${CMAKE_BINARY_DIR}/cpack/LICENSE" "${CMAKE_BINARY_DIR}/cpack/License.txt") set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_BINARY_DIR}/cpack/License.txt") file (COPY "${PROJECT_SOURCE_DIR}/README.md" DESTINATION "${CMAKE_BINARY_DIR}/cpack") file (RENAME "${CMAKE_BINARY_DIR}/cpack/README.md" "${CMAKE_BINARY_DIR}/cpack/Readme.md") set (CPACK_RESOURCE_FILE_README "${CMAKE_BINARY_DIR}/cpack/Readme.md") set (CPACK_RESOURCE_FILE_WELCOME "${PROJECT_SOURCE_DIR}/src/doc/Welcome.txt") #SET (CPACK_STRIP_FILES Do we need this?) if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set (CPACK_GENERATOR "TGZ;STGZ;RPM;DEB") set (CPACK_SOURCE_GENERATOR "TGZ") endif () if (APPLE) set (CPACK_GENERATOR "TGZ;STGZ;PackageMaker") set (CPACK_SOURCE_GENERATOR "TGZ") endif () if (WIN32) set (CPACK_GENERATOR "NSIS") set(CPACK_PACKAGE_EXECUTABLES "iv" "iv - Image Viewer") # set(CPACK_CREATE_DESCTOP_LINKS "iv" "iv - Image Viewer") set(CPACK_NSIS_MODIFY_PATH ON) include (InstallRequiredSystemLibraries) endif () set (CPACK_SOURCE_PACKAGE_FILE_NAME OpenImageIO-${OIIO_VERSION_MAJOR}.${OIIO_VERSION_MINOR}.${OIIO_VERSION_PATCH}-source) #set (CPACK_SOURCE_STRIP_FILES Do we need this?) set (CPACK_SOURCE_IGNORE_FILES ".*~") set (CPACK_COMPONENT_UNSPECIFIED_HIDDEN TRUE) set (CPACK_COMPONENT_UNSPECIFIED_REQUIRED TRUE) set (CPACK_COMPONENTS_ALL user developer documentation Unspecified) set (CPACK_COMPONENT_USER_DISPLAY_NAME "Applications") set (CPACK_COMPONENT_DEVELOPER_DISPLAY_NAME "Developer files") set (CPACK_COMPONENT_DOCUMENTATION_DISPLAY_NAME "Documentation") set (CPACK_COMPONENT_USER_DESCRIPTION "Applications: iv, iinfo, iconvert, idiff, igrep, maketx and libraries") set (CPACK_COMPONENT_DEVELOPER_DESCRIPTION "Include files") set (CPACK_COMPONENT_DOCUMENTATION_DESCRIPTION "OpenImageIO documentation") set (CPACK_COMPONENT_DEVELOPER_DEPENDS user) include (CPack) # TODO: equivalents of the old: # * make doxygen # * BOOST_DYNAMIC # Do TIFF, JPEG, PNG actually look in external? openimageio-1.7.17~dfsg0.orig/CHANGES.md0000644000175000017500000065026213151711064015732 0ustar mfvmfvRelease 1.7.17 (1 Sep 2017) -- compared to 1.7.16 ------------------------------------------------- * Repair build breaks against Boost 1.65. #1753 * Fix a subtle static initialization order problem. #1757 * Build: Improved finding LibRaw. #1749 Release 1.7.16 (1 Aug 2017) -- compared to 1.7.15 ------------------------------------------------- * OpenEXR: fix problem with 2-channel images putting the channels in the wrong order. #1717 * TIFF: images with fewer than 4 channels, but one of those channels was alpha, were not correctly marking their spec.alpha_channel. #1718 * Several minor updates to simd.h backported from mater. Release 1.7.15 (1 Jun 2017) -- compared to 1.7.14 ------------------------------------------------- * Add "raw:user_sat" configuration attribute to the reader. #1666 * Better exception safety for `Filesystem::searchpath_find()`. #1680 * Improved I/O of color separation images, particularly those with custom InkSet attributes. #1658 * Big TextureSystem performance improvement on Windows with MSVC by removing certain empty destructors in simd.h that prevented MSVC from fully inlining the class. #1685 * Fix rare case TextureSystem crash. #1685 Release 1.7.14 (1 May 2017) -- compared to 1.7.13 ------------------------------------------------- * oiiotool expression substitution now recognizes FRAME_NUMBER and FRAME_NUMBER_PAD. #1648 * oiiotool -resize and -resample now have more intuitive behavior for images where the display and pixel data windows are not the same, especially if the data window had a nonzero origin (such as with crop or overscan). #1667 * oiiotool --resample and ImageBufAlgo::resample() have been extended to work for "deep" images. #1668 * ImageCache::get_image_info() will now return a proper error (and not hit an assertion) if asked for information about a subimage or MIP level that does not exist in the file. #1672 * ImageBuf copy constructor from another ImageBuf was previously broken for IB's that wrap application buffers. #1665 * TextureSystem::get_texels fixes crashing behavior. #1669 * Fixes to OSX rpath behavior of linked binaries. #1671 * OpenEXR file input: fixed problem with sorting order of spectral alpha channels (RA, GA, BA, or AR, AG, AB). #1674 * ImageBufAlgo::deep_merge() (and oiiotool --deepmerge) now will give a useful error message if you try to merge two deep images that do not have the same number of channels. #1675 * ImageCache/TextureSystem statistics no longer list as "BROKEN" files which were invalidated, or files that were initialized with an ImageCacheFile but never opened. #1655 * Fix Windows warnings about SIMD types as function args, and about int vs bool. #1659 Release 1.7.13 (1 Apr 2017) -- compared to 1.7.12 ---------------------------------------------- * TIFF, JPEG, others: Improved reading Exif meatdata from XMP blocks, now it does a better job of discerning the proper data types. #1627 * RAW: The default value for missing "raw:use_camera_matrix" has been changed to 1 (not 0) to better match default behavior of libraw/dcraw. #1629 * RAW: Add support for selecting new demosaicing algorithms: "DMT" (mode 11) and "AAHD" (mode 12). Renamed the "Modified AHD" to "AHD-Mod" to simplify and match libraw terminology. Made matching of demosaicing algorithms case-insensitive. #1629 * RAW: Support "ACES" color space for direct conversion while reading RAW images (supported in libraw 0.18 or newer). #1626 * oiiotool: Improved error reporting of file open errors when -iconfig is used. #1626 * oiiotool `--echo STRING` prints the string to the console. This can contain expressions! So you can do something like oiiotool file.exr -echo "Size is {TOP.width}x{TOP.height}" #1633 * JPEG: Be more reslient to malformed Exif data blocks with bogus offsets. #1585, #1639 * TIFF output omitted setting the "Make" and "Model" metadata tags. #1642 * webp: Several new sanity checks prevent the webp reader from spending too much I/O time and memory reading bogus files (malformed, corrupted, or not a webp after all). #1640 * PSD: Support has been added for "cmyk", "multichannel", and "grayscale" color modes. And support was fixed for rgb and grayscale 32 bit per sample bit depth. #1641 Release 1.7.12 (1 Mar 2017) -- compared to 1.7.11 ---------------------------------------------- * BMP: add support for version 5 headers. #1616 * TIFF: Fix typo that prevented correct reading of some Exif fields. #1625 * ImageBuf: Fix broken threads(n) method, which didn't correctly pass the right number of threads along. #1622. * Fix build warnings about undefined OIIO_MSVS_AT_LEAST_2013 symbol. Release 1.7.11 (1 Feb 2017) -- compared to 1.7.10 ---------------------------------------------- * maketx fix: two textures that had identical source pixels but differed in whether they resized with "highlight compensation" incorrectly ended up with identical hashes, and thus could be confused by the TextureSystem at runtime into thinking they were duplicates. The hash is now fixed. (1599) * OpenEXR: Allow compression "none" for deep exr files. * Fix unimplemented python ImageBufAlgo.computePixelStats. (1596) * IBA::draw_rectangle (and oiiotool --box) wasn't drawing properly for the degenerate case of a rectangle that takes up just one pixel. (1601) * Fixed several (so far unnoticed) buffer overruns and memory leaks. (1591) * ImageInput::read_tiles when only a channel subset is read, fixed case with certain data sizes where the copy to user buffer got mangled. (1595) * oiiotool -iconfig fixed, did not in all cases correctly propagate the input config attribute to the file read. (1605) * TIFF: now has a way to read raw pixel values from CMYK files, without the automatic conversion to RGB (pass configuration attribute "oiio:RawColor" set to nonzero). (1605) * imageio.h: Fix incorrect declaration of declare_imageio_format(). (1609) * `oiiotool --crop` did not properly honor the `-a` flag and apply the crop to all subimages. ((1613) Release 1.7.10 (1 Jan 2017) -- compared to 1.7.9 ---------------------------------------------- * Fix "exr_threads" value, -1 should disable IlmImf's thread pools. #1582 * Be more reslient to malformed Exif data blocks with bogus offsets. #1585 * Build: Fix regarding iv man page. * Build: Fix compiler warning on Fedora aarch64. #1592 * Docs: improve docs about deep IBA functions. * Docs: fix 'Building OIIO on Windows' link. #1590 Release 1.7.9 (1 Dec 2016) -- compared to 1.7.8 ---------------------------------------------- * Make sure that sRGB<->linear color transform still work (in the obvious way) even when OpenColorIO is present but that its configuration for some reason doesn't know about "sRGB" space. #1554 * ImageCache: make robust to changes in autotile after opening and reading from untiled files. #1566 * ImageCache: fix initialization bug that made the reported stats output nonsensical in the numbers it gave for "redundant reads". #1567 * IC/TS get_image_info queries of "displaywindow" and "datawindow" did not correctly return a 'true' value when the data was found. #1574 * oiiotool -d giving per-channel formats is no longer confused by channel renaming with --chnames. #1563 * Added implementation of ImageBufAlgo::to_IplImage(). #1461 Release 1.7.8 (1 Nov 2016) -- compared to 1.7.7 ---------------------------------------------- * Fix gcc warnings when compiling for AVX2. #1511 * Fix a variety of Windows warnings and breaks. #1512, #1523 * Added support for new API introduced with ffmpeg 3.1. #1515 * Improve oiiotool --mosaic, wasn't reliably clearing the blank spaces for missing images. * Smarter channel_append channel renaming: try to resolve redundant channel names by using the subimage name, if available. #1498 * Texture: get_image_info queries for "channels" on UDIM file patterns now succeeds, returning the value for the first matching file it finds. (Relies on all textures within the same UDIM set having the same nchannels.) #1502, #1519 * Bug fix to possible crashes when adding dither to tiled file output (buffer size miscalculation). #1518 * maketx: multiple simultaneous maketx process trying to create the same texture will no longer clobber each other's output. #1525 * Build no longer gets confused about include files from older installations of OIIO elsewhere on the system. #1524 * Improvements in finding OpenJPEG. #1520 * Sysutil::Term formatting now works properly in Windows (though is only functional for Windows 10 or above). #1527 * Fix RLA reading and writing with certain channel orders and mixded data formats. #1499 * Improved finding of OCIO headers. #1528 * Better recognition of C++11 features in MSVS. * Fix compile warnings with Clang 3.9. #1529 * Texture: Fix UDIM channels query. #1530 * nuke: Fix txReader to properly restore saved mip level knob value (#1531) * Fix warnings on some 32 bit platforms #1539 * Exit oiiotool with non-zero status when command-line args fail to parse properly. #1540 * Fix typo in fmath bitcast_to_float declaration #1543 * Allow multiple key/value pairs in OCIO wrappers. #1542 * colorconvert API extended to take context key/values #1542 * Fix to TIFF handling of certain unusual tags, which also affected raw files. #1547 * fmath.h: templatize round_to_multiple so it works with other types (like size_t). #1548 * Fix IFF output that didn't correctly save the "Author" and "Date" metadata. #1549 * Handle 'oiiotool --colorconvert X X' (transform from and to spaces that are the same) without considering it an error. #1550 Release 1.7 (1 Oct 2016) -- compared to 1.6.x ---------------------------------------------- Major new features and improvements: * New oiiotool commands: * `-otex` and `-oenv` allow oiiotool to directly output proper texture maps, and it can now do everything that maketx can do. #1351 (1.7.2) * `--line` can draw polylines into an image. #1319 (1.7.1) * `--box` can draw a filled or unfilled box into an image. #1319 (1.7.1) * `--laplacian` computes the Laplacian. #1332 (1.7.1) * `--deep_merge` does a full merge/composite of deep images. #1388 (1.7.2) * `-i` inputs a file. Options `autocc=`, `now=`, `info=` control aspects of reading that one file. #1389 (1.7.2) * `--dilate` and `--erode` perform the basic morphological operations of dilation and erosion. #1486 (1.7.5) * New ImageBufAlgo functions: render_point(), render_line(), render_box() #1319 (1.7.1); laplacian() #1332 (1.7.2); copy() #1388 (1.7.2); deep_merge() #1388,1393 (1.7.2); dilate() and erode() (1.7.5). * UDIM support for textures: filenames like `"tex_.exr"` will automatically be resolved to the correct UTIM tile based on the s,t coordinates. #1426 (1.7.3) * Behavior change: When reading files without explicit channel names, single channel images now name their channel "Y" (no longer "A", which was confusing to algorithms that treat alpha in special ways). Similarly, 2-channel images name their channels "R" and "G". #1434 (1.7.3) Public API changes: * DeepData internals and API overhaul: struct internals hidden, now you must use the API; DeepData declaration is now in deepdata.h, not in imageio.h; DD methods now allow insertion and erasure of individual samples. #1289 (1.7.0) New DeepData methods: split, sort, merge_overlaps, merge_deep_pixels, occlusion_cull. #1388,1393 (1.7.2) * imageio.h: Removed items deprecated since 1.4: a version of convert_types() that took alpha and z channel indices (but never used them). #1291 * fmath.h: Removed safe_sqrtf, safe_acosf, fast_expf, which have been deprecated since 1.5. (1.7.0) #1291 * Removed ImageBufAlgo::flipflop(), which was deprecated since 1.5 and is now called rotate180. #1291 (1.7.0) * Several varieties of ImageCache and TextureSystem getattribute methods were noticed to not be properly declared 'const'. This was fixed. #1300 (1.7.0/1.6.9) * For API calls that are deprecated but not yet removed, we now mark them with deprecated attributes for compilers that support it, meaning that you will get compile warnings and explanations when you use deprecated OIIO API functions. #1313,#1318 (1.7.1) * ImageBuf::contains_roi() reveals whether an ROI is completely contained in the data region of the IB. #1310 (1.7.1) * TypeDesc::is_signed() return true of the TypeDesc returns a type that can represent negative values. #1320 * Python: improve: reading with request for type UNKNOWN returns native data in an unsigned char array. Also, requesting HALF returns the half bits in an unsigned short array (since there is no 'half' type in Python). #1362 (1.7.2/1.6.11) * Deprecate ImageCache/TextureSystem clear() methods, which never did anything useful. #1347 (1.7.3) * ImageSpec::set_format() clears any per-channel format information. #1446 (1.7.4) * OIIO::getattribute("library_list", ...) can retrieve a list of all the dependent libraries used when OIIO was built. #1458 (1.7.5) * simd::mask4 class has been renamed simd::bool4 (though the old name is still typedef'ed to work for now). #1484 (1.7.5) * Python bindings for ImageBuf.reset() now properly understands the argument names and default values. #1492 (1.7.6) Fixes, minor enhancements, and performance improvements: * oiiotool: * oiiotool --subimage now takes as an argument either the subimage numeric index, or a subimage name. #1287 (1.7.0) * oiiotool's image cache was smaller than intended because of typo. (1.7.0/1.6.9) * Allow command-line expression metadata names to contain ':'. #1321 (1.7.1/1.6.10) * --info more clearly prints info about subimage formats. #1320 (1.7.1) * --ch: when the channels were only renamed, not reordered, the renaming didn't happen properly. #1326 (1.7.1/1.6.10) * Improved error message propagation from the underlying IBA functions and from errors encounered during --stats. #1338 (1.7.2) * --dumpdata:empty=0 now does something for non-deep files: skips reporting of pixels where all channels are black. Also fixed errors for dumpdata of deep, but non-float, files. #1355 (1.7.2/1.6.11) * '--attrib:type=t name value' lets you explicitly name the top of the attribute you're seting. This helps for ambiguous cases, and also lets you create aggregate types (such as 'matrix' or 'int[2]' -- the value can be a comma-separated list in those cases). #1351 (1.7.2) * --fixnan can now take option "error", meaning that upon finding a NaN, the program considers it an error (rather than fixing). #1351 (1.7.2) * -o now takes optional arguments that control the output of just that one file, including :datatype=, :bits=, :dither=, :autocc=, :autocrop=, :autotrim=, :separate=, :contig=. #1351 (1.7.2) * --resize and --fit sped up by approximately 2x. #1372 (1.7.2) * --runstats not tracks and reports max memory usage. #1385 (1.7.2) * New --cache and --autotile options let you set the ImageCache size and autotile options. #1385 (1.7.2) * --native results in slightly different behavior: does not force float, but still uses the ImageCache as backing if the native data type is one of the types directly supported by IC. #1385 (1.7.2) * --fixnan now works with deep images. #1397 (1.7.3) * --stats when used with deep images, if it encounters any nonfinite values, will print the location of a representative one. #1397 (1.7.3) * --add, --sub, and --absdiff, when its two image operands have differing number of channels, now try to do the "correct" thing and have the result have the larger number of channels, treating any "missing" channels in either input as if they were 0-valued. #1402 (1.7.3) * --attrib:type-timecode lets you set timecodes in "human-readable" form: oiiotool in.exr -attrib:type=timecode TimeCode 11:34:03:00 -o out.exr #1415 (1.7.3) * -info -v will print timecodes in human-readable form. #1415 (1.7.3) * Bug fix: --fullsize didn't work properly when followed by --attrib. #1418 (1.7.3/1.6.14) * Easily understandable warnings for common mistakes: no output specified, an image that's modified but never output, or when -autocc causes output to be a data format that overrides a previous -d directive. #1419 (1.7.3) * --origin, --croptofull, and --trim all do their thing to all subimages, if the image at the top of the stack has multiple subimages. #1440 (1.7.3) * --crop operates on all subimages if either the -a flag was used, or if the specific optinoal override `--crop:allsubimages=1` is used. #1440 (1.7.3) * --trim will trim all subimages to the same region, containing the union of all nonzero pixels in all subimages. #1440 (1.7.3) * --help now prints all of the dependent libraries for individual formats. #1458 (1.7.5) * Bug fix: make sure to propagate per-channel formats from input to output. #1491 (1.7.6) * -o:all=n will output all images currently on the stack, and the filename argument will be assumed to be a pattern containing a %d, which will be substituted with the index of the image (beginning with n). For example, to take a multi-image TIFF and extract all the subimages separately, oiiotool multi.tif -sisplit -o:all=1 sub%04d.tif will output the subimges as sub0001.tif, sub0002.tif, and so on. #1494 (1.7.6) * --mosaic MxN would fail mysteriously if the number of images on the stack was less then M*N. This has been fixed to handle too-few images gracefully and just leave blank spaces as needed. #1501 (1.7.7) * ImageBuf: * ImageBuf::iterator performance is improved -- roughly cutting in half the overhead of iterating over pixels. #1308 (1.7.1/1.6.10) * ImageBuf reads from disk have been improved substantially (in some cases cutting read time in half) in many cases. #1328 (1.7.1) * ImageBuf::copy_pixels() has been sped up. #1358 (1.7.2) * ImageBufAlgo: * The varieties of add(), sub(), mul(), and div() that take an image operand and a per-channel constant operand have all been modified to work properly for "deep" images. #1297 (1.7.0/1.6.10) * mad() is sped up significantly (10x) for the common case of float images already stored in memory (not cached). #1310 (1.6.1) * render_point(), render_line(), render_box() can be used to render points, lines, and boxes into an image. #1319 (1.7.1) * channels(): when the channels were only renamed, not reordered, the renaming didn't happen properly. #1326 (1.7.1/1.6.10) * computePixelStats: Improved numerical accuracy to avoid getting NaN values from imprecision. #1333 (1.7.2/1.6.11) * laplacian() computes the laplacian of an image. #1332 (1.7.2) * fixNonFinite() takes a new option: NONFINITE_ERROR, which will return an error if nonfinite values are encountered. #1351 (1.7.2) * convolve() and unsharp_mask() have been sped up by about 35% for common cases. #1357 (1.7.2) * IBA::resize sped up by approximately 2x. #1372 (1.7.2) * IBA::fixNonFinite() now works with deep images. #1397 (1.7.3) * dilate() and erode() perform basic morphological operations. #1486 (1.7.5) * ImageCache / TextureSystem: * Less unnecessary pausing after read errors when falure_retries == 0. #1336 (1.7.2/1.6.11) * Texture: slight improvement in texture sharpness. #1369 (1.7.2/1.6.11) * New statistics related to redundant tile reads. #1417 (1.7.3) * TextureSystem option "flip_t", if nonzero, will flip the vertical direction of all texture lookups. Use this for renderers that adhere to the convention that the t=0 texture coordinate is the visible "bottom" of the texture. #1428 (1.7.3) #1462 (1.7.5) * UDIM support for textures: filenames like `"tex_.exr"` will automatically be resolved to the correct UTIM tile based on the s,t coordinates. #1426 (1.7.3) * Avoid repeated broken texture error messages. #1423 (1.7.3) * New IC/TS attribute: "max_errors_per_file" limits how many error messages are printed for each file. #1423 (1.7.3) * Improved statistics: for the "top 3" stats, print the top 3 that aren't broken. Also print a count & list of broken/invalid files. #1433 (1.7.3/1.6.15) * Add ability to retrieve various per-file statistics. #1438 (1.7.3/1.6.15) * IC will clamp the max_open_files to the maximum allowed by the system, so you can no longer crash a program by incorrectly setting this limit too high. #1457 (1.7.5) * IC/TS statistics now report separately the total size of images referenced, in terms of in-cache data size, as well as on-disk size (the latter may be compressed). #1481 (1.7.5) * maketx: * maketx -u now remakes the file if command line arguments or OIIO version changes, even if the files' dates appear to match. #1281 (1.7.0) * Remove long-obsolete and non-functional command line options: --new, --old, --hash. #1351 (1.7.2) * iinfo: * More clearly prints info about subimage formats. #1320 (1.7.1) * Print timecodes in human-readable form. #1415 (1.7.3) * ImageOutput: fix cases with native data but non-contiguous strides. #1416 (1.7.3/1.6.15) * Cineon: * Improved deduction/setting of color space info. #1466 (1.7.5) * GIF: * GIF reader failed to set spec full_width, full_height. #1348 (1.7.2/1.6.11) * JPEG: * Fix bad memory access crash when reading specific JPEG files that were written without their comment field including a null character to end the string. #1365 (1.7.2/1.6.11) * Change in behavior writing JPEG files when XResolution & YResolution are not provided, but a PixelAspectRatio is requested. Previously, we obeyed the JPEG/JFIF spec exactly, but it turns out that popular apps including PhotoShop and Nuke use the field differently than the spec dictates. So now we conform to how these apps work, rather than to the letter of the spec. #1412 (1.7.3/1.6.15) * OpenEXR: * Fix broken multipart output when parts had different pixel data types. #1306,#1316 (1.7.1/1.6.10) * Improved error reporting for bad tile reads. #1338 (1.7.2/1.6.11) * Fix errors reading tiles for mixed-format EXR files. #1352 (1.7.2/1.6.11) * The global OIIO::attribute("exr_threads") has been modified so that 0 means to use full available hardware, -1 disables the OpenEXR thread pool and execute in the caller thread. #1381 (1.7.2) * When writing EXR, double check that there are no repeated channel names, and if so, rename them so the data is not lost (since the underlying libIlmImf will silently drop channels with repeated names). #1435 (1.7.3) * More robust detected of when OpenEXR is tiled (for weird files). #1441 (1.7.3/1.6.15) (and a fix in #1448/1.7.4) * Fixed minor bug with OpenEXR output with correctly setting PixelAspectRatio based on the "XResolution" and "YResolution" attributes. #1453 (Fixes #1214) (1.7.4/1.6.16) * Fix setting "chromaticity" metadata in EXR files. #1487 (1.7.5) * When writing OpenEXR, accept compression requests with quality numbers appended to the compression algorithm name, such as "dwaa:200" to mean dwaa compression with a dwaCompressionLevel set to 200. #1493 (1.7.6) * PNG: * Per the PNG spec, name 2-channel images Y,A. #1435 (1.7.3) * Enforce that alpha premultiplication on output MUST consider alpha to be the last channel of 2 or 4 channel images, no other cases (as dictated by the PNG spec). #1435 (1.7.3) * PNM: * Fixed byte swapping when reading 16 but PNM files. #1352 (1.7.2/1.6.11) * RAW: * Changes to how we instruct libraw to process images when reading: Now, by default, auto-bright adjustment is off, camera white balance is on, and maximum threshoding is set to 0. There are "open with config" overrides for all of these, for anybody who doesn't like the default. #1490 (1.7.6) * RLA: * Fixes for both reading and writing of RLA images that are cropped (i.e., data window is a subset of display window). #1224 (1.7.0/1.6.10) * TIFF: * When outputting a TIFF file, a special attribute "tiff:half", if set to nonzero, will enable writing of 16-bit float pixel data (obviously, only if the spec.format is HALF). #1283 (1.7.0) * TIFF input: erase redundant IPTC:Caption and IPTC:OriginatingProgram if they are identical to existing ImageDescription and Software metadata, respectively. (1.7.0/1.6.9) * Output: "tiff:zipquality" attribute controls time-vs-quality for ZIP compression (1-9, defualt 6, higher means more compression). #1295 (1.7.1) * Fix typo that made TIFF files incorrectly name color space metadata "oiio::ColorSpace" instead of "oiio:ColorSpace". #1394 (1.7.2) * More robust handling of non-zero origin of full/display window. #1414 (1.6.14/1.7.3) * Video formats: * The ffmpeg-based reader had a variety of fixes. #1288 (1.7.0) * Support for reading 10-bit and 12-bit movies. #1430 (1.7.5) * Improved accuracy of "lanczos3" filter; speed up blackman-harris filter. #1379 (1.7.2) * Speed up linear<->sRGB color conversions (as used by any of the IBA color conversion functions as well as oiiotool --colorconvert and friends), approximately doubling the speed when no OpenColorIO config is found. #1383 (1.7.2) * ImageInput::create() and ImageOutput::create() will now gracefully handle unexpected exceptions inside an ImageInput or ImageOutput constructor -- return an error rather than crashing. #1456 (1.7.4/1.6.16) * Nuke txWriter adds UI to let you choose which type of texture you are building (ordinary 2D texture, latlong env map, etc). #1488 (1.7.6) Build/test system improvements: * Default build is now C++11! #1344 (1.7.2) You can still (for now) build for C++03 using 'make USE_CPP11=0' or 'cmake -DOIIO_BUID_CPP11=0', but some time soon we will be C++11 minimum. * Fix build break against Boost 1.60. #1299,#1300 (1.7.0/1.6.9/1.5.23) * filesystem_test now much more comprehensively tests the contents of Filesystem. #1302 (1.7.0) * fmath_test adds benchmarks for various data conversions. #1305 (1.7.0) * Travis: add DEBUG builds to the matrix to fix any warnings or failures that only show up for DEBUG builds. #1309 (1.7.1/1.6.10) * Fix build issues on some platforms for SHA1.h, by adding proper include of ``. #1298,#1311,#1312 (1.7.1/1.6.10) * Cleanup of include logic in simd.h that fixed build problems for gcc < 4.4. #1314 (1.7.1/1.6.10) * Fix build breaks for certain 32 bit platforms. #1315,#1322 (1.7.1/1.6.10) * imagespeed_test can not specify the data conversion type for reads, can optionally allow skipping the IB iteration tests, and can set the IC tile cache size. #1323 (1.7.1) * Fix build breaks for gcc 6. #1339 (1.7.2/1.6.11) #1436 (1.7.3/1.6.15) * Fix errors in finding the correct locaiton of pugixml.hpp when using USE_EXTERNAL_PUGIXML=1. #1339 (1.7.2/1.6.11) * Rewrite of FindOpenEXR.cmake. Solves many problems and is simpler. No more FindIlmbase.cmake at all. #1346 (1.7.2/1.6.11) * 'make CODECOV=1; make CODECOV=1 test' can build and test in a way that provides a code coverage report. #1356 (1.7.2) * Fix Filesystem::open() issues with UTF-8 filenames on MinGW. #1353,#1357 (1.7.2/1.6.11) * Allow build against a wider range of ffmpeg versions. #1359 (1.7.2) * Build correctly against FFMPEG 3.0. #1374 (1.7.2) * If found, libjpeg-turbo is used rather than libjpeg; this gives about a 2x speed improvement for reading & writing JPEG files. #1390 (1.7.2/1.6.13) * USE_CPP11=... and USE_CPP14=... are the build flags for both the Make wrapper and the CMake scripts. (Before, it was confusing to have USE_CPP11 for make but OIIO_BUILD_CPP11 for CMake. Now they are one.) #1391 (1.7.2) * Improved LINKSTATIC=1, now mostly works. #1395 (1.7.3) * Big CMake refactor, got rid of redundancies. #1395 (1.7.3) * Remove old embedded Ptex, now must find Ptex externally. Also modified the build scripts to correctly handle newer versions of Ptex. #1400 (1.7.3/1.6.13) * Got Appveyor building OIIO. This is a continuous integration service much like Travis, but it's for Windows. Hopefully this means that it will be much harder for us to make changes that inadvertently break the build on Windows. #1399 (1.7.3) * Make FindOpenEXR.cmake more robust when the version number is embedded in the library name. #1401 (1.7.3/1.6.15) * Clear up some inconsistencies in the CMake files and the Makefile wrapper: the flag to compile with libc++ is now always called USE_LIBCPLUSPLUS, not sometimes OIIO_BUILD_LIBCPLUSPLUS. #1404 (1.7.3) * Overhaul OpenCV dependency finding and make it work with OpenCV 3.x. #1409 (1.7.3/1.6.13) * Allow custom JPEG_PATH to hint location of JPEG library. #1411 (1.7.3/1.6.13) * Windows UTF-8 filename safety fixes. #1420 (1.7.3/1.6.14) * Various Windows compilation & warning fixes. #1443 (1.7.3/1.6.15) * Now builds correctly against OpenJPEG 2.x, it previously only supported OpenJPEG 1.x. #1452 (Fixes #957, #1449) (1.7.4/1.6.16) * Fix Filesystem::searchpath_find on Windows with UTF-8 paths. #1469 (1.7.51.6.17) * Improved the way OpenEXR installations are found. #1464 (1.7.5) Developer goodies / internals: * thread.h has had all the atomic operations split into a separate atomic.h. #1443 (1.7.3) * atomic.h: add atomic and, or, and xor. #1417 (1.7.2/1.6.14); * parallel_image has been improved in several ways: can choose split direction; raised minimum chunk size to prevent thread fan-out for images too small to benefit; uses the calling thread as part of the pool. #1303 (1.7.0) * timer.h: DoNotOptimize() and clobber_all_memory() help to disable certain optimizations that would interfere with micro-benchmarks. #1305 (1.7.0) * simd.h improvements: select(); round(); float4::store(half*), int4::store(unsigned short*), int4::store(unsigned char*). #1305 (1.7.0) Define insert, extract, and ^ (xor), and ~ (bit complement) for mask4, and add ~ for int4. #1331 (1.7.2); madd, msub, nmadd, nmsub, rint, andnot #1377 (1.7.2); exp, log #1384 (1.7.2); simd::float3 is like float4, but only loads and stores 3 components, it's a good Vec3f replacement (but padded) #1473 (1.7.5); matrix44 4x4 matrix class #1473 (1.7.5); mask4 renamed to bool4, and addition of float8, int8, bool8 classes for 8-wide AVX/AVX2 SIMD #1484 (1.7.5). * fmath.h: convert_types has new special cases that vastly speed up float <-> uint16, uint8, and half buffer conversions #1305 (1.7.0); ifloor (1.7.2); SIMD versions of fast_log2, fast_log, fast_exp2, fast_exp, fast_pow_pos #1384 (1.7.2); fix sign of expm1 for small arguments #1482 (1.7.5); added fast_log1p #1483 (1.75). * Fix pesky precision discrepancy in internal convert_type<> that used slightly different math when converting one value at a time, versus converting whole arrays. #1350 (1.7.2) * thread.h: add mutex_pool #1425 (1.7.3/1.6.15) * compute_test: new unit test can be used to benchmark computation times. #1310 (1.7.1) * filesystem.h: Filesystem::file_size() returns file size in bytes; Filesystem::read_bytes() reads the first n (or all) bytes from a file into a buffer. #1451 (1.7.4/1.6.16) * strutil.h: Strutil::extract_from_list_string is more flixible by allowing the vals list to start empty, in which case it will add as many values as it finds rather than only replacing existing values #1319 (1.7.1); Strutil::replace #1422 (1.7.3/1.6.15); utf_to_unicode now takes a string_view rather than a std::string& #1450 (1.7.4); add Strutil::base64_encode() #1450 (1.7.4). * sysutil.h: Sysutil::getenv() safely gets an env variable as a string_view #1451 (1.7.4/1.6.16); terminal_columns() now has a correct implementation on Windows #1460 (1.7.5); max_open_files() retrieves the maximum number of files the process may open simultaneously #1457 (1.7.5). * platform.h: better distinguishing beteen Apple and Generic clang, separately set OIIO_CLANG_VERSION and OIIO_APPLE_CLANG_VERSION. Also change OIIO_GNUC_VERSION to 0 for clang, only nonzero for true gcc. #1380 (1.7.2) * ImageCache: remove unused shadow matrix fields, save space. #1424 (1.7.3) * Many documentation files (such as README, CHANGES, LICENSE, CREDITS, and INSTALL) have been changed from plain text to MarkDown. #1442 (1.7.3) * Sysutil::Term class makes it easy to use color output on the terminal. #1479 (1.7.5) Release 1.6.18 (released 1 Nov 2016 -- compared to 1.6.17) ------------------------------------------------ * Fix setting "chromaticity" metadata in EXR files. #1487 * maketx: multiple simultaneous maketx process trying to create the same texture will no longer clobber each other's output. #1525 * Fix compile warnings with Clang 3.9. #1529 * Fix IFF output that didn't correctly save the "Author" and "Date" metadata. #1549 Release 1.6.17 (released 1 Sep 2016 -- compared to 1.6.16) ------------------------------------------------ * Fix build for newer ffmpeg release that deprecated functions. * Improved finding of OCIO installations. #1467 * Fixed Sysutil::terminal_columns() for WIndows. #1460 * Fix build break in Windows when roundf function not found. #1468 * Fix Filesystem::searchpath_find on Windows with UTF-8 paths. #1469 Release 1.6.16 (released 1 Aug 2016 -- compared to 1.6.15) ------------------------------------------------ * Fix EXR tile logic for OpenEXR 1.x (fixes a break introduced in 1.6.15, is not an issue for exr 2.x). #1448 * Now builds correctly against OpenJPEG 2.x, it previously only supported OpenJPEG 1.x. #1452 (Fixes #957, #1449) * New utility functions: Sysutil::getenv(), Filesystem::file_size(), FileSystem::read_bytes(). #1451 * Fixed minor bug with OpenEXR output with correctly setting PixelAspectRatio based on the "XResolution" and "YResolution" attributes. #1453 (Fixes #1214) * Gracefully handle unexpected exceptions inside an ImageInput or ImageOutput constructor -- return an error rather than crashing. #1456 Release 1.6.15 (released 1 Jul 2016 -- compared to 1.6.14) ------------------------------------------------ * Improved statistics: for the "top 3" stats, print the top 3 that aren't broken. Also print a count & list of broken/invalid files. #1433 * Change in behavior writing JPEG files when XResolution & YResolution are not provided, but a PixelAspectRatio is requested. Previously, we obeyed the JPEG/JFIF spec exactly, but it turns out that popular apps including PhotoShop and Nuke use the field differently than the spec dictates. So now we conform to how these apps work, rather than to the letter of the spec. #1412 * IC/TS: add ability to retrieve various per-file statistics. #1438 * Windows UTF-8 filename safety fixes. #1420 * ImageOutput: fix cases with native data but non-contiguous strides. #1416 * Make FindOpenEXR.cmake more robust when the version number is embedded in the library name. #1401 * Fix build breaks for gcc 6. #1339 (1.7.2/1.6.11) #1436 * More robust detected of when OpenEXR is tiled (for weird files). #1441 * Various Windows compilation and warning fixes. * strutil.h: added replace(). #1422 * thread.h: added mutex_pool. #1425 Release 1.6.14 (released 1 Jun 2016 -- compared to 1.6.13) ------------------------------------------------ * More robust handling of TIFF I/O when non-zero origin of full/display window. (#1414) * oiiotool --fullsize didn't work properly when followed by --attrib. (#1418) Release 1.6.13 (released 1 May 2016 -- compared to 1.6.12) ------------------------------------------------ * Use libjpeg-turbo if found. It's a drop-in replacement for libjpeg, but is around 2x faster. #1390 * Fix some Windows compiler warnings and errors. * Remove old embedded Ptex, now must find Ptex externally. Also modified the build scripts to correctly handle newer versions of Ptex. #1400 * Overhaul OpenCV dependency finding and make it work with OpenCV 3.x. #1409 * Allow custom JPEG_PATH to hint location of JPEG library. #1411 Release 1.6.12 (released 1 Apr 2016 -- compared to 1.6.11) ------------------------------------------------ * Build correctly against FFMPEG 3.0. #1374 * The global OIIO::attribute("exr_threads") has been modified so that 0 means to use full available hardware, -1 disables the OpenEXR thread pool and execute in the caller thread. #1381 * Thread-pool counts initialized to hardware_concurrency, not physical_concurrency (i.e., they will include hyperthread cores by default). #1378 * oiiotool --autocc bug fixed. * Miscellaneous improvements to simd.h ported from master. * Fix typo that made TIFF files incorrectly name color space metadata "oiio::ColorSpace" instead of "oiio:ColorSpace". #1394 Release 1.6.11 (released 1 Mar 2016 -- compared to 1.6.10) ------------------------------------------------ * Fix potential of IBA::computePixelStats (including oiiotool --stats) to end up with NaNs due to numerical imprecision. #1333 * Less unnecessary pausing after read errors when falure_retries == 0. #1336 * Fix errors in finding the correct locaiton of pugixml.hpp when using USE_EXTERNAL_PUGIXML=1. #1339 * Fix build breaks for gcc 6. #1339 * GIF reader failed to set spec full_width, full_height. #1348 * PNM: Fixed byte swapping when reading 16 but PNM files. #1352 * OpenEXR: Improved error reporting for bad tile reads. #1338 * OpenEXR: Fix errors reading tiles for mixed-format EXR files. #1352 * oiiotool --dumpdata:empty=0 now does something for non-deep files: skips reporting of pixels where all channels are black. Also fixed errors for dumpdata of deep, but non-float, files. #1355 * Fix Filesystem::open() issues with UTF-8 filenames on MinGW. #1353 * Rewrite of FindOpenEXR.cmake. Solves many problems and is simpler. No more FindIlmbase.cmake at all. #1346 * Fix build break for older gcc < 4.4 that didn't have immintrin.h. * Fix bad memory access crash when reading specific JPEG files that were written without their comment field including a null character to end the string. #1365 * The ffmpeg-based reader had a variety of fixes. #1288 * Python: improve: reading with request for type UNKNOWN returns native data in an unsigned char array. Also, requesting HALF returns the half bits in an unsigned short array (since there is no 'half' type in Python). #1362 * Texture: slight improvement in texture sharpness. #1369 * Update webp testsuite references for new webp version. Release 1.6.10 (released 1 Feb 2016 -- compared to 1.6.9) ------------------------------------------------ * ImageBufAlgo add, sub, mul, and div, for the varieties that combine an image with a (per-channel) constant, now work for "deep" images. #1257 * ImageBuf::iterator performance is improved -- roughly cutting in half the overhead of iterating over pixels. #1308 * OpenEXR: Fix broken multipart output when parts had different pixel data types. #1306,#1316 * Allow oiiotool command-line expression metadata names to contain ':'. * Fix oiiotool --ch (or IBA::channels) when the channels were only renamed, not reordered, the renaming didn't happen properly. #1326 * Fixes for both reading and writing of RLA images that are cropped (i.e., data window is a subset of display window). #1224 * Fix build issues on some platforms for SHA1.h, by adding proper include of ``. #1298,#1311,#1312 * Cleanup of include logic in simd.h that fixed build problems for gcc < 4.4. #1314 * Fix build breaks for certain 32 bit platforms. #1315,#1322 Release 1.6.9 (released 5 Jan 2016 -- compared to 1.6.8) ------------------------------------------------ * Several varieties of ImageCache and TextureSystem getattribute methods were noticed to not be properly declared 'const'. This was fixed. #1300 (1.6.9) * Fix build break against Boost 1.60. #1299,#1300 (1.6.9/1.5.23) * The Python bindings for ImageCache was overhauled after several of the methods were found to be horribly broken. #1300 (1.6.9) * oiiotool --subimage now allows a subimage name as argument, as well as the numeric index. #1271,#1287 (1.6.9) * TIFF input: erase redundant IPTC:Caption and IPTC:OriginatingProgram if they are identical to existing ImageDescription and Software metadata, respectively. (1.6.9) * Fix oiiotool image cache smaller than intended because of typo. (1.6.9) Release 1.6 (1.6.8 released Dec 21, 2015 -- compared to 1.5.x) ---------------------------------------------- Major new features and improvements: * New oiiotool functionality: * Expression evaluation/substitution on the oiiotool command line. Anything enclosed in braces { } in a command line argument will be substituted by the evaluation of the enclosed expression. Expressions may be numbers, simple arithmetic (like 'expr+expr'), or retrieving image metadata from named images or images on the stack. Please see the PDF documentation, Section 12.1 for details and examples. * --absdiff, --absdiffc compute the absolute difference (abs(A-B)) of two images, or between an image and a constant color. #1029 (1.6.0) * --abs computes the absolute value of an image. #1029 (1.6.0) * --div, divc divide one image by another (pixel by pixel), or divides the pixels of an image by a constant color. #1029 (1.6.0) * --addc, --subc, --mulc, --powc are the new names for --cadd, --csub, --cmul, and --cpow. The old ones will continue to work but are considered depcrected. #1030 (1.6.0) * --pattern supports new patterns: "fill" makes a solid, vertical or horizontal gradient, or four-corner interpolated image (just like the --fill commmand) (1.6.0); "noise" can generate uniform, gaussian, or salt & pepper noise (1.6.2). * --fill, in addition to taking optional parameter color=... to give a solid color for the fill region, now also takes top=...:bottom=... to make a vertical gradient, left=...:right=... to make a horizontal gradient, and topleft=...:topright=...:bottomleft=...:bottomright=... to make a 4-corner gradient. (1.6.0) * --noise adds noise to the current image: additive uniform or gaussian noise, or making "salt & pepper" noise. (1.6.2) * --trim crops the image to the minimal rectangle containing all the non-0 pixels. (1.6.3) * --autocc : when turned on, automatic color conversion of input files into a scene_linear space, and conversion to an appropriate space and pixel type upon output. It infers the color spaces based on metadata and filenames (looking for OCIO-recognized color space names as substrings of the filenames). #1120 (1.6.3) * --mad takes three image arguments, multiplies the first two and then adds the third to it. #1125 (1.6.3) * --invert computes the color inverse (1-value) for color channels. #1125 (1.6.3) * --colorconfig allows you to specify a custom OCIO configuration file (rather than strictly relying on the $OCIO env variable). #1129 (1.6.3) * --deepen converts flat images to "deep". #1130 (1.6.3) * -n (no saved output) performs all calculations (including timing and stats) but does not write any output files to disk. #1134 (1.6.3) * --debug prints debugging information, this is now separate from -v which just makes more verbose (non-debugging) output. #1134 (1.6.3) * --pixelaspect rescales the image to have the given pixel aspect ratio. #1146 (1.6.5) * --ociofiletransform() implements OpenColorIO "file" transforms. #1213 (1.6.5) * New ImageBufAlgo functions: * absdiff() computes the absolute difference (abs(A-B)) of two images, or between an image and a constant color. #1029 (1.6.0) * abs() computes the absolute value of an image. #1029 (1.6.0) * div() divides one image by another (pixel by pixel), or divides all the pixels of an image by a constant color. #1029 (1.6.0) * fill() has been extended with new varieties that take 2 colors (making a vertical gradient) and 4 colors (one for each ROI corner, for a bilinearly interpolated gradient). (1.6.0) * noise() injects noise into an image -- uniform, gaussian/normal, or salt & pepper noise. (1.6.2) * mad() multiplies the first two arguments and then adds the third to it. #1125 (1.6.3) * invert() computes 1-val. #1125 (1.6.3) * deepen() turns a flat RGBA (and optional Z) image into a "deep" image. #1130 (1.6.3) * ociofiletransform() implements OpenColorIO "file" transforms. #1213 (1.6.5) * Some open source fonts are now distributed with OIIO (DroidSans, DroidSans-Bold, DroidSerif, DroidSerif-Bold, DroidSerif-Italic, DroidSerif-BoldItalic, and DroidSansMono), and so those are always available to ImageBufAlgo::render_text() and oiiotool --text, on all platforms and even if you don't have any other installed fonts on your system. DroidSans is now the default font. #1132 (1.6.3) * GIF output support (including writing animated GIF images, just write it as a multi-subimage file). For example, this works: oiiotool foo*.jpg -siappendall -attrib FramesPerSecond 10.0 -o anim.gif #1193 (1.6.4) Public API changes: * TypeDesc: * New helper methods: is_array(), is_unsized_array(), is_sized_array(). #1136 (1.6.3) * New constructor and fromstring of a string_view, in addition to the old versions that took char*. #1159 (1.6.4/1.5.16) * New aggregate type: MATRIX33. #1265,#1267 (1.6.6) * ImageSpec: * ImageSpec::metadata_val() is now static, rather than simply const, since it doesn't need access to *this at all. #1063 (1.6.1) * Added a new variety of find_attribute that takes a temporary ImageIOParameter as scratch space. The advantage of this call is that it can retrieve items from the named ImageSpec fields, such as "width", "full_x", etc. Also, the get_int_attribute, get_float_attribute, and get_string_attribute can now retrieve these fixed fields as well. #1063 (1.6.1) * ImageInput & ImageOutput: * New ImageOutput::supports() tags: supports("alpha") should be true for image formats that support an alpha channel, supports("nchannels") should be true for output formats that support an arbitrary number of output channels. (1.6.2/1.5.13) * ImageInput and ImageOutput supports() method has been changed to accept a string_view (rather than a const std::string&), and return an int (rather than a bool). (1.6.2) * ImageInput and ImageOutput have added destroy() static methods. They are just wrappers around 'delete', but can help you to ensure that II and IO objects are deleted on the same side of a DLL boundary as where they were created. (Helps with using OIIO from DLL-based plugins on Windows.) (1.6.3) * New ImageInput query: "procedural" -- returns 1 if the ImageInput may not correspond to an actual file. #1154 (1.6.4/1.5.16) * ImageInput and ImageOutput's error() method is changed from protected to public, making it easier for an app to set an error on a reader or writer. (1.6.4) * ImageOutput::copy_to_image_buffer is a helper function that generalizes the existing copy_tile_to_image_buffer, but for any rectangle. #1193 (1.6.4) * ImageInput::read_image() variant that takes a channel range to read just a subset of the channels present. #1222 (1.6.5) * ImageInput and ImageOutput now have new method threads(n) that sets the thread "fan-out" for the ImageInput or ImageOutput individually, overriding any global attribute("threads"). #1259 (1.6.6) * ImageBuf: * Add make_writeable(), which forces ImageCache-backed read-only ImageBuf to read into locally allocated pixels so they can be subsequently altered. #1087 (1.6.2) * ImageBuf::Iterator has added set_deep_samples() and set_deep_value() methods. (1.6.3) * ImageBuf::set_pixels() now provides a way to set an arbitrary rectancle of an ImageBuf from raw values. #1167 (1.6.4) * ImageBuf::get_pixels() now has a variety that takes an ROI to describe the rectangle of pixels being requested. #1167 (1.6.4) * ImageBuf now has new method threads(n) that sets the thread "fan-out" for the ImageInput or ImageOutput individually, overriding any global attribute("threads"). #1259 (1.6.6) * ImageCache/TextureSystem: * Clarified in the docs that TextureSystem::get_texture_info and ImageCache::get_image_info "exists" queries should return true, and place in *data the value 1 or 0 depending on whether the image exists and can be read. (1.6.0/1.5.10) * Added handle-based versions of TextureSystem get_texture_info(), get_imagespec(), imagespec(), and get_texels(), in addition to the existing name-based versions of those methods. Note that texture(), environment(), and texture3d() already had both name-based and handle-based varieties. #1057 (1.6.1) #1083 (1.6.2) * Add create_thread_info() and destroy_thread_info() methods that allow an app to manage the per-thread records needed by the IC. #1080 (1.6.2) * Added ImageCache get_perthread_info() and get_image_handle() to return opaque perthread and file handle pointers, much like TextureSystem already had, and added handle-based versions of get_image_info(), get_imagespec(), imagespec(), get_pixels(), and get_tile(), in addition to the existing name-based versions of those methods. #1057 (1.6.1) * ImageCache get_tile and get_pixels have new varieties that let you request channel begin/end range. This allows you to control which channel ranges are in the cache, and thus be much more efficient with cache storage when only a few channels are needed from a file with many channels. #1226 (1.6.5) * ImageBufAlgo: * New ImageBufAlgo functions: abs, absdiff, div, fill, noise, mad, invert, deepen, ociofiletransform. * nchannels() now takes an 'nthreads' parameters, just like all the other ImageBufAlgo functions. #1261 (1.6.6) * Python bindings: * Added previously-M.I.A. ImageSpec::erase_attribute(). #1063 (1.6.1) * ImageSpec.set_channel_formats() now works when the channel type lists are either TypeDesc, in addition to the existing support for BASETYPE. #1113 (1.6.3/1.5.13) * Added Python bindings for DeepData and deep reads (ImageInput) and writes (ImageOutput), as well as additional DeepData and ImageBuf methods to fully match the C++ API. #1113 #1122 (1.6.3/1.5.13) * ImageBuf.set_pixels, and ImageBuf.get_pixels with ROI. #1167,1179 (1.6.4) * Change Python ImageOutput bindings to simplify the write_* methods. They no longer take both a TypeDesc and an array; it can figure out the type from the array itself. Also get rid of the stride parameters, which weren't useful in a Python context. #1184 (1.6.4) * ImageBufAlgo colorconvert, ociolook, and ociodisplay now take an optional string colorconfig argument. #1187 (1.6.4) * Fix missing Python bindings for global OIIO::getattribute(). #1290 (1.6.8) * The ColorConfig wrapper for OCIO functionality has been extended to parse color names from filename strings, and to report the recommended pixel data type for a color space. #1129 (1.6.3) * C++11 definitions: oiioversion.h defines OIIO_BUILD_CPP11 as nonzero if OIIO itself was built in C++11 (or later) mode, and platform.h defines OIIO_USING_CPP11 as nonzero if at this moment C++11 (or later) mode is detected. Note that these can differ if one set of compiler flags was used to build OIIO, and a different set is used to build a project that uses OIIO headers. #1148 (1.6.4) * Renamed the "fps" standard metadata to "FramesPerSecond. #1193 (1.6.4) * Removed deprecated header "string_ref.h" (use string_view.h). (1.6.1) * oiioversion.h: Renamed the namespace macros OIIO_NAMESPACE_ENTER/EXIT to OIIO_NAMESPACE_BEGIN/END, and roll the braces into it. #1196 (1.6.4) * array_view.h: Refactor array_view to be more in line with what is slated for C++17, in particular it is now templated on Rank and so can be a view to a multi-dimensional array. Also change array_view_strided to have strides measured in units of sizeof(T), not bytes (to keep with C++17). This also adds coordinate.h to give definitions for the offset<>, bounds<>, and bounds_iterator<> templates used by array_view. #1205 (1.6.4) * Add top-level OIIO::get_int_attribute(), get_float_attribute(), and get_string_attribute() helpers, similar to how they work in many of the classes. #1283 (1.6.7) Fixes, minor enhancements, and performance improvements: * oiiotool * Bug fix for frame sequences -- could crash in Windows. #1060 (1.6.1) * Gracefully handle requests to save an image with more channels than the output file format can handle. Instead of being a fatal error, now it's just a warning, and extra channels are dropped. It tries to to find R, G, B, and A channels, saving them. If those names are not found, it just saves the first 3 (or 4) channels. #1058 (1.6.1) * Improve error messages when files can't be read. It is now easier to to distinguish files that don't exist from those that are an unknown format from those that are corrupted or have read errors. #1065 (1.6.1) * Flag errors properly when -d specifies an unknown data format name. #1077 (1.6.2/1.5.13) * oiiotool numeric wildcard improvement: allow more digits to match. #1082 (1.6.2/1.5.13) * Bug fix: input file data format didn't always end up in the output. (1.6.3) * --channels bugs were fixed when dealing with "deep" images. (1.6.3) * All the color space conversion operations run much faster now, since the underlying IBA::colorconvert() has been parallelized. (1.6.3) * --crop logic bug fixed in cases where the crop region was the same size as the original pixel data window. #1128 (1.6.3) * oiiotool now gives proper error messages when asked to perform unsupported operations on deep images. (1.6.3) * Bug fix: --frames incorrectly overrode explicit frame sequence wildcards on the command line. #1133 (1.6.3) * --crop, --trim, and --autotrim have been extended to work on "deep" images. #1137 (1.6.3) * For "procedural" ImageInputs, don't give "file doesn't exist" errors. (1.6.4) * Suppress output/copying of "textureformat" metadata inherited from input if it's not plausibly still a valid texture (i.e., if it's no longer tiled or MIPmapped). #1206 (1.6.4) * oiiotool's full help message lists all supported formats. #1210 (1.6.5) * oiiotool --help prints a briefer help screen. Use --help -v for the full-detail help. #1214 (1.6.5) * Bug fix in --fit when the image didn't need to be resized. #1227 (1.6.5/1.5.21) * Bug fix in --ch for "deep" files when the channel reordering is the same as it already was. #1286 (1.6.7) * ImageBufAlgo: * compare() (and therefore oiiotool -diff and idiff) did not notice image differences when the pixels that differed had NaN or NaN or Inf values! Now it is right. #1109 (1.6.3/1.5.13) * channels() bugs were fixed when dealing with "deep" images. (1.6.3) * colorconvert() has been parallelized, and thus on most systems will now run much faster. (1.6.3) * render_text() handles UTF-8 input. #1121 (1.6.3) * colorconvert(), ociodisplay(), and ociolook() have new varities that accept an optional ColorConfig, rather than having no choice but to construct a new one internally. (1.6.3) * nonempty_region() and crop() have been extended to handle "deep" images. #1137 (1.6.3) * Fix bug in fft() -- was not always zeroing out the imaginary channel. #1171 (1.6.4/1.5.17) * Fixed uninitialized variable bugs with rangecompress() and rangeexpand() when using luma. #1180 (1.6.4) * The lanczos3, radial-lanczos, and catrom filters have been change from fixed-width to fully scalable. This fixes artifacts that occur when using them as upsizing filters. #1228,#1232 (1.6.5/1.5.21) * maketx, TextureSystem, and ImageCache: * TextureSystem/IC now directly stores uint16 and half pixel data in the cache rather than converting internally to float for tile storage, thus effectively doubling the cache capacity for files of those formats. (1.6.3) * Fix broken bicubic texture sampling with non-power-of-two sized tiles. #1035 (1.6.0/1.5.10) * maketx: when the source image was a crop (data window != display window), and the sharpening filters were used, it would incorrectly issue an "unknown filter name" error. #1059 (1.6.1/1.5.12) * maketx: Flag errors properly when -d specifies an unknown data format name. #1077 (1.5.13) * maketx now writes to a temporary file, then moving it to the final requested output filename only when the write completed without error. This prevents situations where maketx crashes or is killed and leaves behind a file that looks correct but is actually corrupted or truncated. #1072 (1.6.2/1.5.13) * TextureSystem bug fix that occasionally resulted in NaN in the alpha channel result when looking up from 3-channel images. #1108 (1.6.3/1.5.13) * maketx --runstats prints runtime staticstics (deprecating --stats). #1152 (1.6.4) * Fixed trilinear MIPmap texture lookups that gave invalid alpha fill. #1163 (1.6.4/1.5.16) * The lanczos3, radial-lanczos, and catrom filters have been change from fixed-width to fully scalable. This fixes artifacts that occur when using them as upsizing filters. #1228,#1232 (1.6.5) * Texture cache memory efficiency is much better for the special case of accessing just a few channels from a texture file with large numbers of channels. #1226 (1.6.5) * Eliminate spurious ImageCache invalidation just because the shared cache is requested again. #1157 (1.6.4/1.5.16) * Statistics output also shows all the option setting values. #1226 (1.6.5) * Data copy error in ImageCache::get_pixels for partial-channel-set copies. #1246 (1.6.5) * maketx -u now remakes the file if command line arguments or OIIO version changes, even if the files' dates appear to match. #1281 (1.6.8) * GIF: * Write support! #1193 (1.6.4) * On input, renamed "fps" metadata to "FramesPerSecond". #1193 (1.6.4) * IFF: * Fix botched output of 16 bit uncompressed data. #1234 (1.6.5/1.5.21) * Make "rle" compression the default. #1234 (1.6.5/1.5.21) * JPEG: * Now properly read/write xdensity and ydensity (what OIIO and TIFF call "XResolution" and "YResolution" and, therefore, "PixelAspectRatio". #1042 #1066 (1.6.0, 1.6.1) * Support JPEG files encoded as CMYK (by converting to RGB upon read) #1044 (1.6.1) * Fix misdeclared supports() which would make the JPEG plugin appear to not support exif or iptc. #1192 (1.6.4) * JPEG-2000: * Fix handling of un-premultiplied alpha (which is dictated by the JPEG-2000 spec). (1.6.3) * Fix reading of YUV-encoded files. (1.6.3) * Read and write the ICC profile, if present. (1.6.3) * Handle all bit depth precisions properly (previously only 8, 10, 12, and 16 were right). (1.6.3) * Set the full/display window correctly. (1.6.3) * Deal with differing per-channel data windows and sampling rates. (1.6.3) * OpenEXR: * Improved handling of density and aspect ratio. #1042 (1.6.0) * Fix read_deep_tiles() error when not starting at the image origin. #1040 (1.6.0/1.5.10) * Fix output of multi-part exr file when some parts are tiled and others aren't. #1040 (1.6.0/1.5.10) * write_tile() with AutoStride calculated the wrong default strides for "edge" tiles when the image width or length was not an integer multiple of the tile size. Also clarified the PDF and imageio.h docs in how they explain strides for this case. #1055 (1.6.1/1.5.12) * Fix bugs in reading deep OpenEXR images with mixed channel types. #1113 (1.6.3/1.5.13) * OpenEXR output supports("deepdata") now correctly returns 'true'. #1238 (1.6.5/1.5.21) * A separate global OIIO::attribute("exr_threads") sets the thread pool size for OpenEXR's libIlmImf, independent of the OIIO thread fan-out attribute OIIO::attribute("threads"). #1244 (1.6.5) * Correctly read and write Matrix33 and double (scalar, 2d, 3d, m33, m44) metadata. #1265,#1267 (1.6.6) * Recognize AR/AG/AB channel names in addition to the old RA/RG/RB #1277 (1.6.6) * PNG: * Writing PNG files now honors the PixelAspectRatio metadata. #1142 (1.6.3) * PFM: * PFM (float extension of PNM) was incorrectly flipped top to bottom. Now fixed. #1230 (1.6.5) * PSD: * Better error handling for files lacking "global layer mask info" or "additional layer info". #1147 (1.6.4/1.5.18) * Additional PSD signatures for global additional layer info. #1147 (1.6.4/1.5.18) * Better error handling when dealing with an empty layer mask. #1147 (1.6.4/1.5.18) * TIFF: * Improved handling of density and aspect ratio. #1042 (1.6.0) * Improved proper handling of the interplay between "XResolution", "YResolution", and "PixelAspectRatio". #1042 (1.6.0) * TIFF output: recognize special "tiff:write_exif" metadata, which when present and set to 0, will skip writing the Exif directory into the TIFF file. This can be helpful when you expect the resulting TIFF file to be read with very old versions of libtiff. #1185 (1.6.4/1.5.18) * Correct read and write of JPEG-compressed TIFF. #1207 (1.6.4) * Correct support for reading LAB, LOG, YCbCr, subsampled chroma. #1207 (1.6.4) * Make robust to strange TIFF files that have unexpected MIP level-to-MIP level changes in planarconfig, photometric, palette, extrasamples, etc. #1220,1221 (1.6.5/1.5.20) * Support output of 2, 4, 10, and 12 bit unsigned ints into TIFF files. #1216 (1.6.5) * Make TIFF reading more robust to certain subimage-to-subimage changes that were thought to be invariant. #1221 (1.6.5) * CMYK is properly read and written. Upon read, CMYK is auto-converted to RGB (and the "tiff:ColorSpace" metadata is set to "CMYK"). For output, if "tiff:ColorSpace" metadata is set and nonzero, the RGB passed in will be auto-converted to CMYK upon writing. #1233 #1245 (1.6.5) * Recognize Exif tags in the main directory, not only the special Exif directory. #1250 (1.6.5) * Fix bug in read_scanlines when reading TIFF files with UNassociated alpha and unusual ystride values. #1278 (1.6.6) * ImageBuf iterator constructors with 0-size ranges or ROIs have been fixed to look like they are immediately done(). #1141 (1.6.3) * Fix bug in internal convert_image() that could corrupt certain image copying of non-contiguous data layouts. #1144 (1.6.3) * Also search for OIIO plugins in [DY]LD_LIBRARY_PATH. #1153 (1.6.4/1.5.16) * Nuke plugin: don't crash with NULL Knob* in TxReaderFormat::setMipLabels. #1212 (1.6.5/1.5.20) * idiff -q results in quiet mode -- output nothing for success, only minimal errors to stderr for failure. #1231 (1.6.5) Build/test system improvements: * Python plugin is now build as a cmake "module" rather than "library", which fixes some things on OSX. #1043 (1.6.0/1.5.10) * Various build fixes for Windows. #1052 #1054 (1.6.1) * New CMake build-time option to specify the default plugin search path. #1056 (1.6.1/1.5.12) * Fix build breaks for very old versions of Ilmbase (1.6 and earlier) that lack a definition of V4f used by our simd.h. #1048 (1.6.1/1.5.11) * Fix signed/unsigned warning on 32 bit platforms in jpeginput.cpp. #1049 (1.6.1/1.5.11) * New CMake build-time option to specify the default plugin search path. #1056 (1.6.1/1.5.12) * Fix gcc 5.0 compiler warning in PtexHalf.cpp. (1.6.1/1.5.12) * Remove dependency of OpenSSL by default. #1086 (1.6.2/1.5.13) * Fix warnings when compiling with C++11. (1.6.3/1.5.13) * Dont link Python framework on OSX. #1099 (1.6.3/1.5.13) * Changed the way testtex warps the image to give faux perspective to test texture mapping. (1.6.3) * Build-time USE_SIMD=... has been changed from accepting a single tag to a comma-separated list of feature options. So you can, for example, do make USE_SIMD=avx,f16c ... (1.6.3) * make USE_NINJA=1 causes CMake to build Ninja build files instead of Makefiles (they execute much faster, espectially for incremental builds). #1158 (1.6.4) * PSD & JPEG plugins fixes for Win32 compilation. #1150 (1.6.4/1.5.16) * Fix Nuke plugin build files to not do anything if USE_NUKE=0. #1156 (1.6.4/1.5.16) * Builds now produce much less console output by default (use VERBOSE=1 to get all the details, most of which is only useful when debugging broken builds). #1162 (1.6.4) * Fix support for older ffmpeg version on Ubuntu 14.04. #1168 (1.6.4/1.5.17) * Build-time fixes for Nocona CPUs that have SSE3 without SSSE3. #1175 (1.6.4/1.5.17) * ustring internals fixes for gcc 5.x changs to std::string ABI. #1176 (1.6.4) * Fixes for clean build with clang 3.6. #1182,1183 (1.6.4) * Fix signed/unsigned comparison error. #1186 (1.6.4) * Top-level Makefile option USE_OPENCV=0 to turn off even searching for OpenCV components. #1194 (1.6.4/1.5.18) * If a system-installed (external) PTex implementation is found, use it. Only use the "bundled" version if no other is found. Also add a top-level USE_PTEX=0 that will skip PTex support, even if the library is found. #1195,1197 (1.6.4) * Fix compiler warnings about int vs size_t mismatches. 1199 (1.6.4) * Improve C++11 and C++14 readiness. #1200 * Fix build break with certain new versions of libraw. #1204 (1.6.4/1.5.19) * Fix build warnings for new Apple tools release that upgrades the standard clang release. #1218 (1.6.5/1.5.20) * When compiling in C++11 mode, std::unordered_map, mutex, recursive_mutex, lock_guard, bind, ref, cref, thread, shared_ptr will be used rather than boost equivalents, and our own thread_group and intrusive_ptr are now used rather than the boost equivalents. We believe that this completely removes all Boost headers and types from the OIIO public APIs when in C++11 mode. (Though internals still use Boost in some cases.) #1262 #1266 (1.6.6) * We are now set up to use Travis-CI (https://travis-ci.org) for continuous integration / automatic builds of all merges and pull requests. #1268, #1269, #1273 (1.6.6) * Don't install fonts if USE_FREETYPE is disabled. #1275 (1.6.6) * Use ccache for builds when detected and safe (unless USE_CCACHE=0). #1274,#1285 (1.6.7) * Failed tests now print their non-matching text output to the console when doing 'make test'. This makes it much easier to spot most errors. #1284 (1.6.7) Developer goodies / internals: * Strutil additions: parse_until, parse_nested (1.6.1), repeat (#1272/1.6.6/1.5.21). * Give Strutil::parse_string an option to not strip surrounding quotes. (1.6.4) * Made TypeDesc::equivalent accept comparisons of arrays of unspecified length with ones of definite length. #1072 (1.6.2/1.5.13) * Add Filesystem::rename() utility. #1070 (1.6.2/1.5.13) * New SIMD methods: insert<>, xyz0, vreduce_add, dot, dot3, vdot, vdot3, AxBxCxDx, blend0not (1.6.2) * array_view enhancements that let you initialize an `array_view` from a const `std::vector&`. #1084 (1.6.2/1.5.14) * hash.h contains several new hashes in namespaces 'OIIO::xxhash' and 'OIIO::farmhash'. Also, Strutil::strhash now uses farmhash rather than the Jenkins one-at-a-time hash, bringing big speed improvements (including ustring creation). Beware that the strhash value returned will be different than they were before. #1090 (1.6.3) * fmath: safe_fast_pow improves the precision of its results for special cases of pow(x,1) and pow(x,2). #1094 (1.6.3/1.5.13) * Added TypeDesc::TypeHalf(). #1113 (1.6.3/1.5.13) * thread.h: our atomic types have had their API adjusted somewhat to more closely conform to C++11's std::atomic. (1.6.3) * ustring's internals and underlying hash table have been overhauled, yielding much higher performance, especially when many threads are simultaneously creating ustrings. (1.6.3) * ROI improvement: make intersection & union robust to uninitialized ROIs as arguments. (1.6.3) * osdep.h is deprecated. Use platform.h instead. (1.6.3) * The DISPATCH_TYPES utility macros used internally by IBA have been improved, and in particular the DISPATCH_COMMON_TYPES now handle ALL types ("uncommon" ones are silently converted to float). (1.6.3) * platform.h moves the endian functions into the OIIO namespace. (1.6.3) * platform.h adds functions for runtime query of CPU capabilities. (1.6.3) * simd.h: float4 and int4 can now construct and load from unsigned short*, short*, unsigned char*, char*, and 'half'. (1.6.3) * Strutil::utf8_to_unicode (1.6.3) * Filesystem::current_path(). #1124 (1.6.3/1.5.21) * Filesystem enumerate_file_sequence and scan_for_matching_filenames have been modified to clear their result vectors rather than simply assume they are empty. #1124 (1.6.3) * oiiotool internals have been refactored to be class-oriented and move a lot of boilerplate repeated in each op to be part of the base class. #1127 (1.6.3) * timer.h: Timer and ScopedTimer have changed slightly. This isn't used in any public OIIO APIs, but may affect 3rd party programs that like to use OIIO's timer.h for convenience. #1201 (1.6.4/1.5.19) * dassert.h: added OIIO_STATIC_ASSERT macros for static assertion. Doesn't affect existing OIIO apps since they are new additions, but feel free to use them! #1202 (1.6.4/1.5.19) * New unit test for imagecache. #1246 (1.6.5) * Sysutil::hardware_concurrency() and physical_concurrency(). #1263 (1.6.6/1.5.21) Release 1.5.24 (1 Mar 2016) -- compared to 1.5.23) --------------------------------------------------- * Fix oiiotool --dumpdata, didn't work properly for non-float files. * Fix broken OpenEXR multi-part output when parts have different pixel types. * Update webp testsuite references for new webp version. Release 1.5.23 (28 Dec 2015) -- compared to 1.5.22) --------------------------------------------------- * Fix build break against Boost 1.60. #1299,#1300 Release 1.5.22 (16 Dec 2015) -- compared to 1.5.21) --------------------------------------------------- * Deep OpenEXR: recognize the newer AR/AG/AB channel name convention. #1277 * Fix ffmpeg plugin compilation in some configurations. #1288 * Bug fix: TIFF read_scanlines of files with unassociated alpha didn't honor the 'ystride' parameter and could run off the end of the buffer for nonstandard stride arranagements. #1278 * Fix missing Python bindings for global OIIO::getattribute(). #1290 Release 1.5.21 (1 Dec 2015) -- compared to 1.5.20) --------------------------------------------------- * Bug fix in --fit when the image didn't need to be resized. #1227 * IFF: Fix botched output of 16 bit uncompressed data. #1234 * IFF: Make "rle" compression the default for output. #1234 * OpenEXR output supports("deepdata") now correctly returns 'true'. #1238 * The lanczos3, radial-lanczos, and catrom filters have been changed from fixed-width to fully scalable. This fixes artifacts that occur when using them as upsizing filters. #1228,#1232 * Filesystem::current_path(). #1124 * Sysutil::hardware_concurrency() and physical_concurrency(). #1263 * Strutil::repeat() #1272 Release 1.5.20 (28 Sep 2015) -- compared to 1.5.19) --------------------------------------------------- * Nuke plugin: don't crash with NULL Knob* in TxReaderFormat::setMipLabels. #1212 * Fix build warnings for new Apple tools release that upgrades the standard clang release. #1218 * Make TIFF reader robust to strange TIFF files that have unexpected MIP level-to-MIP level changes in planarconfig, photometric, palette, extrasamples, etc. We previously assumed these things would never vary between MIP levels of the same file, and Murphy called our bluff. #1220,1221 Release 1.5.19 (8 Sep 2015) -- compared to 1.5.18) -------------------------------------------------- * Fix compile warnings on some platforms/compilers. * Fix build break with certain new versions of libraw. #1204 * Internals: Timer and ScopedTimer have changed slightly. This isn't used in any public OIIO APIs, but may affect 3rd party programs that like to use OIIO's timer.h for convenience. #1201 * Internals: dassert.h has added OIIO_STATIC_ASSERT macros for static assertion. Doesn't affect existing OIIO apps since they are new additions, but feel free to use them! #1202 Release 1.5.18 (4 Aug 2015) -- compared to 1.5.17) --------------------------------------------------- * PSD input improvements: better error handling for files lacking "global layer mask info" or "additional layer info"; additional PSD signatures for global additional layer info; better error handling when dealing with an empty layer mask. #1147 * TIFF output: recognize special "tiff:write_exif" metadata, which when present and set to 0, will skip writing the Exif directory into the TIFF file. This can be helpful when you expect the resulting TIFF file to be read with very old versions of libtiff. #1185 * Top-level Makefile option USE_OPENCV=0 to turn off even searching for OpenCV components. #1194 Release 1.5.17 (13 Jul 2015) -- compared to 1.5.16) --------------------------------------------------- * Fix support for older ffmpeg version on Ubuntu 14.04. #1168 * Fix bug in fft -- was not always zeroing out the imaginary channel. #1171 * Build-time fixes for Nocona CPUs that have SSE3 without SSSE3. #1175 * ustring fixes for new gcc (5.1+) and new std::string ABI. #1176 * Fixes for unit test timer_test for new OSX versions with timer coalescing. #1181 * Fix bugs with rangecompress and rangeexpand when using luma. #1180 * Fixes for clean build when using clang 3.6. #1182 Release 1.5.16 (11 Jun 2015) -- compared to 1.5.15) --------------------------------------------------- * PNG writes now honor PixelAspectRatio attribute. #1142 * Build fixes for Visual Studio 2010 #1140 * PSD & JPEG plugins fixes for Win32 compilation. * Also search for OIIO plugins in [DY]LD_LIBRARY_PATH. #1153 * Give Strutil::parse_string an option to not strip surrounding quotes. * Fix Nuke plugin build files to not do anything if USE_NUKE=0 #1156 * New ImageInput query: "procedural" -- returns 1 if the ImageInput may not correspond to an actual file. #1154 * TypeDesc has a new constructor and fromstring of a string_view, in addition to the old versions that took char*. #1159 * Eliminate spurious ImageCache invalidation just because the shared cache is requested again. #1157 * Fixed trilinear MIPmap texture lookups that gave invalid alpha fill. #1163 * Filesystem: sequence matching should clear results arrays upon start. Release 1.5.15 (11 May 2015) -- compared to 1.5.14) --------------------------------------------------- * Bug fix with IBA::channels() with deep data with UINT channels. * Fix TypeDesc compatibility with OSL. * Misc WIN32 / VS2010 fixes. * Fix incorrect logic in convert_image with certain channel types and strides. #1144 Release 1.5.14 (10 April 2015) -- compared to 1.5.13) ---------------------------------------------- * fmath: save_fast_pow improves the precision of its results for special cases of pow(x,1) and pow(x,2). #1094 (1.5.13) * Fix warnings when compiling with C++11. (1.5.13) * Dont link Python framework on OSX. #1099 (1.5.13) * Improve IBA::compare() (and therefore oiiotool -diff and idiff) when the images being compared have NaN or Inf values. #1109 (1.5.13) * TextureSystem bug fix that occasionally resulted in NaN in the alpha channel result when looking up from 3-channel images. #1108 (1.5.13) * Added TypeDesc::TypeHalf(). #1113 (1.5.13) * Fix IBA::channels() bugs when dealing with "deep" images. #1113 (1.5.13) * Python ImageSpec.set_channel_formats() now works when the channel type lists are either TypeDesc, in addition to the existing support for BASETYPE. #1113 (1.5.13) * Added Python bindings for DeepData and deep reads (ImageInput) and writes (ImageOutput). #1113 (1.5.13) * Fix bugs in reading deep OpenEXR images with mixed channel types. #1113 (1.5.13) * Fix bug in IBA::convolve() for the case when the kernel image passed is not a float image. #1116 (1.5.13) Release 1.5.13 (10 Mar 2015) -- compared to 1.5.12) ---------------------------------------------- * oiiotool: Bug fix for frame sequences -- could crash in Windows. #1060 * New ImageOutput::supports() tags: supports("alpha") should be true for image formats that support an alpha channel, supports("nchannels") should be true for output formats that support an arbitrary number of output channels. #1058 * oiiotool: Gracefully handle requests to save an image with more channels than the output file format can handle. Instead of being a fatal error, now it's just a warning, and extra channels are dropped. It tries to to find R, G, B, and A channels, saving them. If those names are not found, it just saves the first 3 (or 4) channels. #1058 * Improved handling of "PixelAspectRatio" for JPEG, TIFF, and OpenEXR. #1042 #1066 * oiiotool: Improve error messages when files can't be read. It is now easier to to distinguish files that don't exist from those that are an unknown format from those that are corrupted or have read errors. #1065 * maketx now writes to a temporary file, then moving it to the final requested output filename only when the write completed without error. This prevents situations where maketx crashes or is killed and leaves behind a file that looks correct but is actually corrupted or truncated. #1072 * Python: added previously-M.I.A. ImageSpec.erase_attribute(). #1063 * Add Filesystem::rename() utility. #1070 * Made TypeDesc::equivalent accept comparisons of arrays of unspecified length with ones of definite length. #1072 * oiiotool & maketx have improved error message when unknown data format names are requested with "-d". #1077 * oiiotool numeric wildcard improvement: allow more digits to match. #1082 * Remove dependency of OpenSSL by default. #1086 Release 1.5.12 (11 Feb 2015) -- compared to 1.5.11) ---------------------------------------------- * Various build fixes for Windows. #1052 #1054 * New CMake build-time option to specify the default plugin search path. #1056 (1.5.12) * OpenEXR: fixed write_tile() with AutoStride calculated the wrong default strides for "edge" tiles when the image width or length was not an integer multiple of the tile size. Also clarified the PDF and imageio.h docs in how they explain strides for this case. #1055 (1.5.12) * maketx: when the source image was a crop (data window != display window), and the sharpening filters were used, it would incorrectly issue an "unknown filter name" error. #1059 (1.5.12) * Fix gcc 5.0 compiler warning in PtexHalf.cpp. (1.5.12) Release 1.5.11 (28 Jan 2015) -- compared to 1.5.10) ---------------------------------------------- * Fix build breaks for very old versions of Ilmbase (1.6 and earlier) that lack a definition of V4f used by our simd.h. #1048 * Fix signed/unsigned warning on 32 bit platforms in jpeginput.cpp. #1049 Release 1.5 (26 Jan 2015) -- compared to 1.4.x ---------------------------------------------- Major new features and improvements: * New oiiotool functionality/commands: * --rotate90, --rotate180, --rotate270 rotate the image in 90 degree axially-aligned increments with no filtering. (1.5.2) * --reorient will perform whatever series of rotations or flips are necessary to move the pixels to match the "Orientation" metadata that describes the desired display orientation. (1.5.2) * --autoorient will automatically do the equivalent of --reorient on every image as it is read in, if it has a nonstandard orientation. (This is generally a good idea to use if you are using oiiotool to combine images that may have different orientations.) (1.5.2) * --rotate rotates an image by arbitrary angle and center point, with high-quality filtering. #932 (1.5.3) * --warp transforms an image using a 3x3 matrix, with high-quality filtering. #932 (1.5.3) * --median performs a median filter. (1.5.4) * New ImageBufAlgo functions: * rotate90(), rotate180(), rotate270() rotate the image in 90 degree axially-aligned increments with no filtering. (1.5.2) * reorient() will perform whatever series of rotations or flips are necessary to move the pixels to match the "Orientation" metadata that describes the desired display orientation. (1.5.2) * rotate() performs rotation with arbitrary angle and center point, with high-quality filtering. #932 (1.5.3) * warp() transforms an image by a 3x3 matrix, with high-quality filtering. #932 (1.5.3) * median_filter performs a median filter. (1.5.4) * Significant internal speedups by utilizing SIMD instructions (SSE) in the TextureSystem (1.5.5 / #948, 1.5.6 / #990). To use this to its fullest extent, build OIIO with the make/cmake option USE_SIMD=arch, where arch is sse2, ssse3, sse4.1, sse4.2, depending on what machines you'll be deploying to. (Note that x86_64 automatically implies at least sse2.) We're finding that this has approximately doubled the speed of the math part of texture mapping (it doesn't speed up the disk I/O, of course). (1.5.5) * Basic support for many movie files via a plugin using 'ffmpeg'. Works with avi, mov, qt, mp4, m4a, 3gp, 3g2, mj2, m4v, mpg, and more. Movie files simply look like multi-image files to OIIO. There isn't really support for audio yet, and although this lets you retrieve and process individual frames of a movie file, OIIO is still not meant to be a video-processing library. Currently, these formats can be read, but there is no write support (maybe coming soon). #928 #1010 (1.5.5) * Nuke plugins -- a txReader plugins that will read OIIO texture files, and a txWriter that will output proper (tiled & mip-mapped) texture files from Nuke. Contributed by Nathan Rusch / Luma Pictures. #973 (1.5.6) Public API changes: * TextureSystem API calls have been modified: the nchannels, dresultds, dresultdt fields have been removed from TextureOpt and are now passed explicitly as arguments to texture(), environment(), and texture3d(). Some long-deprecated methods of TextureSystem have been removed, and also some new API calls have been added that provide multi-point texturing given a TextureHandle/PerThread rather than a ustring filename. (1.5.5) (#948) * New filters available to direct users of filter.{h,cpp} and for ImageBufAlgo, oiiotoo, and maketx: "cubic", "keys", "simon", "rifman". (1.5.0/1.4.9) (#874) * Global attribute "read_chunk" can set the default number of scanlines read at a time internally by read_image() calls. #925 * The Python ImageBuf class did not expose an equivalent to the C++ ImageBuf::get_pixels. This is exposed now. #931 (1.5.3) * The Filter API now uses string_view. (1.5.3) * ImageBuf and ImageBufAlgo now use string_view extensively. (1.5.4) * ImageInput::supports() and ImageOutput::supports() now accept new tags: "arbitrary_metadata" reveals if the format allows arbitrarily-named metadata; "exif" if the format can store camera Exif data; "iptc" if the format can store IPTC metadata. #1001 (1.5.7) * Removed ImageBuf and ImageBufAlgo functions that have been deprecated since OIIO 1.3. #1016 (1.5.8) * ImageCache::add_file has been extended to allow an ImageSpec pointer as "configuration", which will be passed along to the underlying ImageInput::open() when the file is first opened. This allows you to use the same kind of configuration options/hints that you would with a raw ImageInput. (1.5.8) * ImageBuf constructor and reset methods have been extended to allow an ImageSpec pointer as "configuration", which will be passed along to the underlying ImageCache and/or ImageInput::open() when the file is first opened. This allows you to use the same kind of configuration options/hints that you would with a raw ImageInput. (1.5.8) * The DeepData helper structure now has methods to set (already allocated) deep values, as well as to retrieve uint values. (1.5.8) * ImageBuf methods to get and set deep values and to allocate the DeepData structure. (1.5.8) * ImageBuf::interppixel_NDC has been changed to consider the coordinates relate to full (aka display) window, rather than data window. (1.5.8) * ImageBuf::interppixel_bicubic and interppixel_bicubic_NDC added to sample an image with B-spline bicubic interpolation. (1.5.8) * Clarified in the docs that TextureSystem::get_texture_info and ImageCache::get_image_info "exists" queries should return true, and place in *data the value 1 or 0 depending on whether the image exists and can be read. (1.5.10) * Clarified in the docs that TextureSystem::get_texture_info and ImageCache::get_image_info "exists" queries should return true, and place in *data the value 1 or 0 depending on whether the image exists and can be read. (1.5.10) Fixes, minor enhancements, and performance improvements: * ImageBufAlgo: * flip(), flop(), flipflop() have been rewritten to work more sensibly for cropped images. In particular, the transformation now happens with respect to the display (full) window, rather than simply flipping or flopping within the data window. (1.5.2) * channels() now works properly with "deep" images. (1.5.8) * oiiotool: * oiiotool --nosoftwareattrib suppresses the altering of Software and ImageHistory fields upon output. (1.5.1) * oiiotool --ch now will search for channel names case-insensitively, if the channel names are not first found with case-sensitive compares. #897 (1.5.1/1.4.11) * oiiotool --wildcardoff/--wildcardon can selectively disable and re-enable the frame sequence numeric wildcards (useful if you have a filename or other argument that must actually contain '#' or '@' characters). #892 (1.5.1/1.4.11) * oiiotool --no-autopremult/--autopremult can disable and re-enable the automatic premultiplication of color by opacity, in files where they are stored not-premultiplied and you wish to preserve the original un-premultiplied values. #906 (1.5.1/1.4.11) * oiiotool --sansattrib omits "-attrib" and "-sattrib" and their arguments from the command line that is put in the Software and ImageHistory metadata (makes it less pointlessly verbose). (1.5.1) * Now get helpful error messages if input images are corrupt or incomplete. (1.5.1) * oiiotool --flip, --flop, --flipflop, and --transpose now have more sensible behavior for cropped images. (1.5.2) * oiiotool --orientcw, --orientccw, and --orient180 are the new names for --rotcw, --rotccw, --rot180. The old names were confusing, and now it is more clear that these merely adust the Orientation metadata that suggests viewing orientation, rather than actually rotating the image data. (1.5.2) * oiiotool color conversion ops crashed when inputs were files with MIPmap levels (such as textures). #930 (1.5.3/1.4.13) * More graceful handling when outputting an image with negative pixel window origin to a file format that doesn't support this feature. (1.5.3) * oiiotool --unsharp_mask:kernel=median will perform a median filter for the blurring step (rather than a gaussian or other convolution filter), which tends to sharpen small details but not over-sharpen long edges as the traditional unsharp mask often does. (1.5.4) * oiiotool's help message now says explicitly if it was compiled without any support for OpenColorIO. (1.5.5/1.4.15) * oiiotool -stats, on deep files, now prints a histogram of the number of samples per pixel. #992 (1.5.6) * oiiotool --dump of "deep" files improves output with int32 channels (1.5.6). * oiiotool --stats of "deep" files prints a histogram of samples/pixel (1.5.6). * oiiotool -subimage has better error detection and reporting for requests for nonexistant subimages. #1005 (1.5.7) * oiiotool --ch is a bit more flexible in its channel-shuffling syntax: you are now able to say newchannel=oldchannel, both shuffling and renaming channels simultaneously, thus removing a frequent necessity of a --chnames action following a --ch. (1.5.8) * oiiotool -d now supports uint32, sint32. #1013 (1.5.8) * oiiotool -native forces native data format read in cases where going through the ImageCache would have sacrificed precision or range. #1014 (1.5.8) * oiiotool --resize (and similar, such as --fix, --resample, etc.) size specifiers now allow percentage values that are not the same in each direction, such as "200%x50%", which would double horizontal resolution while cutting vertical resolution in half. #1017 (1.5.8) * oiiotool --ch now works properly with "deep" images. (1.5.8) * maketx & TextureSystem: * texture lookups have roughly doubled in performance because of SSE optimization. (1.5.5 / #948, 1.5.6 / #990). * maketx: Fix case typo for LatLong env map creation when in 'prman' mode. #877. (1.5.1/1.4.10) * maketx --attrib, --sattrib, and --sansattrib now work like oiiotool; in other words, you can use command line arguments to add or alter metadata in the process of creating a texture. #901 (1.5.1) * `maketx --sharpen ` adds slight sharpening and emphasis of higher frequencies when creating MIP-maps. #958 (1.5.5) * Fix crash when using maketx --checknan (1.5.5) * maketx now embeds metadata hints ("oiio:ConstantColor") for constant textures (all pixels are identical). The texture system notices this hint and will automatically replace texture lookups (as long as wrap mode is not black) with this constant value, about 10x faster than a real texture lookup would have been. This sounds silly, but in production sometimes you end up with uniform textures and this helps. Also, you can retrieve the constant color via get_texture_info of "constantcolor" and "constantalpha" (though the retrieval will only succeed if the metadata is present, even if the texture is actually constant). #1006 (1.5.7) * maketx now embeds metadata hints ("oiio:AverageColor") containing the average value of the texture. This can be retrieved with get_texture_info of "averagecolor" and "averagealpha". #1006 (1.5.7) * Python: * Bug fix: the Python binding of ImageInput.read_scanlines did not properly honor any channel subset specified. (1.5.5/1.4.15) * All of the expensive functions in the Python bindings now release the Python GIL (global interpreter lock) as much as possible. This allows other Python threads to make progress even when OIIO is doing a lengthy operation such as file I/O or an expensive image processing operation. (1.5.5) * Bug fix in ImageInput: could crash if calling ImageInput.read_* functions without specifying a data format. #998 (1.5.6) * Bug fix in ImageOutput: could crash if calling ImageInput.write_* functions if the buffer format did not match the file data format. #999 (1.5.6) * OpenEXR: * Improve the quality of lossy b44 compression by more correctly using the pLinear value of channels (we were incorrectly using the flag to indicate linear channels, but it's really for channels that are perceptually linear). (#867) (1.5.0/1.4.9) * Fix potential build breaks due to incorrect use of Imf::isDeepData() which apparently was not intended to be an externally-visible function. (1.5.0/1.4.9) (#875) * Fix crashes/exceptions when passing string array attributes for OpenEXR output. #907 (1.5.1/1.4.11) * Improved ordering of channel names for OpenEXR files with many "layers". #904 (1.5.1/1.4.11) * Fixed issue where a stringvec of 16 items was mishandled as a matrix. #929 (1.5.3/1.4.13) * OpenEXR 2.2 support, including the new DWAA/DWAB compression modes. (1.5.4) * Fix bug that prevented proper saving/reporting of subimage name for multi-subimage OpenEXR files. (1.5.5). * Fix read_deep_tiles() error when not starting at the image origin. #1040 (1.6.0/1.5.10) * Fix output of multi-part exr file when some parts are tiled and others aren't. #1040 (1.6.0/1.5.10) * JPEG: * Fix broken recognition of .jfi extension. (#876) * Read and write ICC profiles as attribute "ICCProfile". (#911) * Fix seek_subimage for JPEG input -- did not properly return the spec. (1.5.2/1.4.12) * Support for reporting and controlling chroma-subsampling value, via the "jpeg:subsampling" attribute. #978 (1.5.5) * PNG: * Read and write ICC profiles as attribute "ICCProfile". (#911) * TIFF: * Read and write ICC profiles as attribute "ICCProfile". (#911) * Graceful handling of output of images with negative data window origin. (1.5.3) * Improved precision when auto-premultiplying 8 bit unassociated alpha images (if the user is reading them into a higher-precision buffer). #960, #962 (1.5.2) * Change default compression (if otherwise unspecified) to "zip". (1.5.6) * Robust to Exif blocks with corrupted GPS data. #1008 (1.5.8/1.4.16) * Fixes to allow proper output of TIFF files with uint32 or int32 pixels (1.5.8) * RAW: * Fix for portrait-orientation image reads. (#878) (1.5.1/1.4.10) * Fix bug with "open with config" options. #996 (1.5.6) * RLA: Fix bug that caused RLA output to not respect requests for 10 bit output (would force it to 16 in that case). #899 (1.5.1) * ImageSpec::get_float_attribute() is now better about retrieving values that were stored as integers, even though you are querying them as floats. (#862) (1.5.0/1.4.9) * ImageCache fix for when files are not found, then subsequently the searchpath is changed. #913 (1.4.12/1.5.2) * Fixed some alloca calls that did not get the right amount of memory. (#866) (1.5.0/1.4.9) * Fix an off-by-one loop in IBA::resize that would not get the wrong image result, but might trigger debuggers to flag it as touching the wrong memory. (#868) (1.5.0/1.4.9) * Better exception safety in Filesystem::scan_for_matching_filenames(). #902 (1.5.1/1.4.11) * Python: ImageBuf.get_pixels() is now implemented, was previously omitted. (1.5.2) * TextureSystem: anisotropic texture lookups are slightly sped up (~7%) by using some approximate math (but not visibly different) #953. (1.5.5) * Better exception safety in Filesystem::exists() and similar functions. #1026 (1.5.8/1.4.16) Build/test system improvements: * Fix several compiler warnings and build breakages for a variety of platforms and compiler versions. (#858, #861) (1.5.0/1.4.8) * Move around some cpack-related components. (#863) (1.5.0/1.4.9) * Allow in-source build (not recommended, but necessary for Macports). (#863) (1.5.0/1.4.9) * Fixes to docs-building makefiles. (#873) (1.5.0/1.4.9) * More robust build when OpenEXR and IlmBase have installed their respective header files in different directories rather than the expected behavior of being installed together. (#861) (1.5.0/1.4.9) * Fix build break in DEBUG compiles for ustring internals. (#869) (1.5.0/1.4.9) * Fix warnings about potentially uninitialized variables. (#871) (1.5.0/1.4.9) * Make thread.h use more modern gcc intrinsics when gcc >= 4.8, this allows correct thread.h operations for PPC and MIPS platforms that were't working before. (#865) (1.5.0/1.4.9) * Fix Windows build when OIIO_STATIC_BUILD is used. (#872) (1.5.0/1.4.9) * Fixes to get a clean compile on Windows + MSVC 9. (#859) (1.5.0/1.4.8) * Fixes to get a clean compile on Windows + MSVC 9. (#872) (1.5.0/1.4.9) * Make 3.0 compatibility fixes on OSX. (1.5.1/1.4.10) * Fix segfaults on 32 bit systems with gcc 4.2. #889 (1.5.1/1.4.11) * Fixes to Filesystem internals to work with older Boost releases older than 1.45. #891 (1.5.1/1.4.11) * Fixes to find libraw properly with Visual Studio 2010. #895 (1.5.1/1.4.11) * Fix bad casts in thread.h that broke some platforms. #896 (1.5.1/1.4.11) * Several fixes for Windows build, including properly finding OpenEXR libraries when OpenEXR 2.1 is used. (1.5.1/1.4.11) * Fixes to fmath.h to work with MSVC 2013 (some long-omitted math ops that we'd always needed to define for Windows only are finally supported in MSVC 2013). #912 (1.4.12/1.5.2) #927 (1.4.13/1.5.3) * Fix for Linux + Boost >= 1.55 combination: need to link with -lrt. #914 (1.4.12/1.5.2) * Fix Ptex + static linkage. (1.4.12/1.5.2) * Rudimentary support for C++11: building with USE_CPP11=1 tries to set compiler flags (for gcc and clang, anyway) to use C++11 mode, and also building with USE_LIBCPLUSPLUS=1 tries to link with libc++ if you are using clang. (1.5.2) * Fixes for Boost Filesystem 1.46-1.49. (1.5.2/1.4.12) * testtex new options: --nchannels (forces num channels to look up, rather than always doing what's in the file), --derivs (force the kind of texture calls that return derivs of the texture lookups. (1.5.4) * Make it able to compile against giflib >= 5.1. #975 (1.4.14/1.5.5) * Add an option to link against static Ilmbase and OpenEXR libraries (1.5.5). * Add testtex options -interpmode and -mipmode. #988 (1.5.6) * Build against Python 3 if the make/cmake build flag USE_PYTHON3=1 is used. (1.5.6) * grid.tx and checker.tx have been moved from ../oiio/images to testsuite/common/texture (allow it to be versioned along with any changes to maketx. (1.5.7) * Support for freetype 2.5.4. #1012 (1.5.8/1.4.16) * Python plugin is now build as a cmake "module" rather than "library", which fixes some things on OSX. #1043 (1.6.0/1.5.10) Developer goodies / internals: * New Strutil string comparison functions: starts_with, ends_with. (1.5.1/1.4.10) * New Strutil simple parsing functions: skip_whitespace, parse_char, parse_until_char, parse_prefix, parse_int, parse_float, parse_string, parse_word, parse_identifier, parse_until. (1.5.1/1.4.10, 1.5.2/1.4.11) * New Filesystem functions: create_directory, copy, remove, remove_all, temp_directory_path, unique_path. (1.5.1/1.4.10) * thread.h: add atomic_exchange() function. #898 (1.5.1/1.4.11) * Improved error propagation through ImageCache, ImageBuf, and oiiotool. (1.5.1) * Moved certain platform-dependent macros from sysutil.h to platform.h (1.5.4/1.4.16) * ustring: add comparisons between ustrings versus char* and string_view. (1.5.4) * New simd.h exposes float4, int4, and mask4 classes that utilize SSE instructions for big speedups. (1.5.5 / #948, #954, #955) * Big reorganization of fmath.h, moved stuff around, organized, added lots of safe_blah() functions that clamp to valid ranges and fast_blah() functions that are approximations that may differ slightly several decimal places in but are much faster than the full precision libm/IEEE versions. #953, #956 (1.5.5) * New utility function: premult(), which can premultiply non-alpha, non-z channels by alpha, for a whole buffer (with strides, any data type). #962 (1.5.5) * Timer API now has queries for ticks as well as seconds. (1.5.5) * platform.h now has macros to mark functions as pure, const, nothrow, or unused. (1.5.5) * Filesystem::read_text_file reads a whole text file into a string efficiently. (1.5.8) Release 1.4.16 (19 Jan 2015 -- compared to 1.4.15) -------------------------------------------------- * Fix gcc 4.9 warnings. * Improved error propagation through ImageCache, ImageBuf, and oiiotool. * TIFF more robust to Exif blocks with corrupted GPS data. #1008 * Support for freetype 2.5.4. #1012 * Better exception safety in Filesystem::exists() and similar functions. #1026 Release 1.4.15 (24 Nov 2014 -- compared to 1.4.14) -------------------------------------------------- * OpenEXR: fix botched writing of subimage name. * 'oiiotool --help' now says explicitly if it was built without OpenColorIO enabled. * Python read_scanlines() did not honor the channel subset. * RAW file reading: the open-with-config variety had the wrong function signature, and therefore did not properly override the base class, causing configuration hints to be ignored. * Bug fix in Python ImageInput: could crash if calling ImageInput.read_* functions without specifying a data format. #998 * Bug fix in Python ImageOutput: could crash if calling ImageInput.write_* functions if the buffer format did not match the file data format. #999 * RAW: Fix bug with "open with config" options. #996 Release 1.4.14 (20 Oct 2014 -- compared to 1.4.13) -------------------------------------------------- * GIF: Fix to make it able to compile against giflib >= 5.1. #975 * JPEG support for reporting and controlling chroma-subsampling value, via the "jpeg:subsampling" attribute. #978 Release 1.4.13 (12 Sep 2014 -- compared to 1.4.12) -------------------------------------------------- * Now builds against OpenEXR 2.2, including support for the new DWAA and DWAB compression modes. * OpenEXR: fixed issue where a stringvec of 16 items was mishandled as a matrix. * Fix fmath.h to move the 'using' statements around properly. * Fix oiiotool color conversion bug that crashed when the input image was a file with MIP levels. * TIFF output now gracefully handles negative origins without hitting an assertion. * Developer details: platform macros moved from sysutil.h to platform.h. (But sysutil.h now includes platform.h, so you shouldn't notice.) * Developer details: ustring now has comparisons between ustrings versus char* and string_view. Release 1.4.12 (31 Jul 2014 -- compared to 1.4.11) -------------------------------------------------- * ImageCache fix for when files are not found, then subsequently the searchpath is changed. #913 (1.4.12/1.5.2) * Fixes to fmath.h to work with MSVC 2013 (some long-omitted math ops that we'd always needed to define for Windows only are finally supported in MSVC 2013). #912 (1.4.12/1.5.2) * Fix for Linux + Boost >= 1.55 combination: need to link with -lrt. #914 (1.4.12/1.5.2) * Fix Ptex + static linkage. (1.4.12/1.5.2) Release 1.4.11 (9 Jul 2014 -- compared to 1.4.10) ------------------------------------------------- * OpenEXR output fix for crashes/exceptions when passing string array attributes. #907 * Improved ordering of channel names for OpenEXR files with many "layers". #904 * oiiotool --ch now will search for channel names case-insensitively, if the channel names are not first found with case-sensitive compares. #897 * oiiotool --wildcardoff/--wildcardon can selectively disable and re-enable the frame sequence numeric wildcards (useful if you have a filename or other argument that must actually contain '#' or '@' characters). #892 * oiiotool --no-autopremult/--autopremult can disable and re-enable the automatic premultiplication of color by opacity, in files where they are stored not-premultiplied and you wish to preserve the original un-premultiplied values. #906 * Fix segfaults on 32 bit systems with gcc 4.2. #889 * Fixes to Filesystem internals to work with older Boost releases < 1.45. #891 * Fixes to find libraw properly with Visual Studio 2010. #895 * Fix bad casts in thread.h that broke some platforms. #896 * Strutil: add another variety of parse_identifier with an expanded character set. * thread.h: add atomic_exchange() function. #898 * Several fixes for Windows build, including properly finding OpenEXR libraries when OpenEXR 2.1 is used. * Better exception safety in Filesystem::scan_for_matching_filenames(). #902 Release 1.4.10 (20 Jun 2014 -- compared to 1.4.9) ------------------------------------------------- * Fix for portrait-orientation RAW image reads. (#878) * maketx: Fix case typo for LatLong env map creation when in 'prman' mode (#877). * New Strutil string comparison functions: starts_with, ends_with. * Make 3.0 compatibility fixes on OSX. * New Strutil simple parsing functions: skip_whitespace, parse_char, parse_until_char, parse_prefix, parse_int, parse_float, parse_string, parse_word, parse_identifier, parse_until. * New Filesystem functions: create_directory, copy, remove, remove_all, temp_directory_path, unique_path. Release 1.4.9 (6 Jun 2014 -- compared to 1.4.8) ----------------------------------------------- * Allow in-source build (not recommended, but necessary for MacPorts). (#863) * CPack improvements. (#863) * Fixes to docs-building makefiles. (#873) * Make ImageSpec::get_float_attribute correctly convert many integer types. (#862) * Fixed some alloca calls that did not get the right amount of memory. (#866) * OpenEXR: Improve the quality of lossy b44 compression by more correctly using the pLinear value of channels (we were incorrectly using the flag to indicate linear channels, but it's really for channels that are perceptually linear). (#867) * More robust build when OpenEXR and IlmBase have installed their respective header files in different directories rather than the expected behavior of being installed together. (#861) * Fix an off-by-one loop in IBA::resize that would not get the wrong image result, but might trigger debuggers to flag it as touching the wrong memory. (#868) * Fix build break in DEBUG compiles for ustring internals. (#869) * Fix warnings about potentially uninitialized variables. (#871) * Make thread.h use more modern gcc intrinsics when gcc >= 4.8, this allows correct thread.h operations for PPC and MIPS platforms that were't working before. (#865) * Fix Windows build when OIIO_STATIC_BUILD is used. (#872) * Fixes to get a clean compile on Windows + MSVC 9. (#872) * New filters available to direct users of filter.{h,cpp} and for ImageBufAlgo, oiiotoo, and maketx: "cubic", "keys", "simon", "rifman". (#874) * OpenEXR: Fix potential build breaks due to incorrect use of Imf::isDeepData() which apparently was not intended to be an externally-visible function. (#875) * JPEG: Fix broken recognition of .jfi extension. (#876) Release 1.4.8 (23 May 2014 -- compared to 1.4.7) ------------------------------------------------ * Fix several compiler warnings and build breakages for a variety of platforms and compiler versions. No new feature or true bug fixes. #857, #858, #859 Release 1.4 (19 May 2014) -- compared to 1.3.x ---------------------------------------------- Major new features and improvements: * The PNM reader now supports "PFM" files, which are the floating point extension to PNM. (1.4.1) * Preliminary support for reading a wide variety of digital camera "RAW" files. (1.4.1) * New oiiotool commands: * `--cpow` : raise pixel values to a power (1.4.1) * `--label` : give a name to the top-of-stack image, can be referred to later in the command line (1.4.1) * `--cut` : combine --crop, --origin +0+0, and --fullpixels. (1.4.3) * `--pdiff` : perceptual diff (#815) (1.4.4) * `--polar`, `--unpolar` : complex <-> polar conversion. (#831) (1.4.5) * oiiotool --resize and --fit, and also maketx when using "good" filters for downsizing, have been significantly sped up. When downsizing with large filters, we have seen up to 3x improvement. (#808) (1.4.3) Public API changes: * New ImageBufAlgo functions: - pow() raises pixel values to a power. (1.4.1) - cut() cuts a region of pixels and moves it to the origin (combines crop, reset origin, and set full res = data resolution). (1.4.3) - complex_to_polar() and polar_to_complex() convert from (real,imag) to (amplitude,phase) and back. (#831) (1.4.5) * New string_view class (in string_view.h) describes a non-owning reference to a string. The string_view is now used in many places throughout OIIO's APIs that used to pass parameters or return values as char* or std::string&. Read string_view.h for an explanation of why this is good. (1.4.2, 1.4.3) (N.B. this was called string_ref until 1.4.6, when it was renamed string_view to conform to C++17 draft nomenclature.) * New array_view<>, array_view_strided<>, strided_ptr<>, and image_view<> templates are great utility for passing bounded and strided arrays. (1.4.3) * Removed deprecated PT_* definitions from typedesc.h. * Removed the quantization-related fields from ImageSpec. (1.4.3) * Dither: If ImageOutput::open() is passed an ImageSpec containing the attribute "oiio:dither" and it is nonzero, then any write_*() calls that convert a floating point buffer to UINT8 output in the file will have a hashed dither added to the pixel values prior to quantization in order to prevent the appearance of visible banding. The specific nonzero value passed for the attribute will serve as a hash seed so that the pattern is repeatable (or not). (1.4.3) Fixes, minor enhancements, and performance improvements: * Improved oiiotool features: * --stats on deep files now prints additional info, such as the minimum and maximum depth and on which pixels they were encountered, as well as which pixel had the maximum number of depth samples. (1.4.1) * --resize and --resample allow WIDTHx0 or 0xHEIGHT, where the '0' value will be interpreted as automatically computing the missing dimension to preserve the aspect ratio of the source image. (#797, #807) (1.4.3) * Fixed possible crash when using --origin with tiled, cached images. (1.3.12/1.4.2) * --pdiff does a perceptual diff (like 'idiff -p'). (#815) (1.4.4) * --dumpdata takes a noptional modifier empty=0 that will cause empty deep pixels to not produce any output. (#821) (1.4.5) * --dumpdata correctly prints values of uint32 channels. #989 (1.5.6) deep pixels to not produce any output. (#821) (1.4.5) * --polar, --unpolar convert from complex (real,imag) to polar (amplitude, phase) and vice versa. (#831) (1.4.5) * View wildcards: similar to frame range wildcards, "%V" is replaced by new names, "%v" by the first letter of each view. The view list is {"left","right"} by default, but may be set with the --views argument. (1.4.5) * --over and --zover set the resulting display/full window to the union of those of the inputs; previously it set the display window to that of the foreground image, which is often a poor default. (1.4.7) * ImageCache/TextureSystem: - The multi-point version of environment() was broken. (1.3.9/1.4.1) - Don't honor the SHA-1 fingerprint found in a file if the "Software" metadata doesn't indicate that the file was written by maketx or oiiotool. (1.4.3) * OpenEXR: - Multi-part EXR (2.0) didn't write the required "name" attribute for each part. (1.3.10/1.4.1) - Fix crashing bug when reading stringvector attributes in the file. (1.3.11/1.4.2) - Add .sxr and .mxr as possible filename extensions (1.3.12/1.4.2) - Smarter channel ordering of input of files with ZBack, RA, GA, or BA channels (#822) (1.4.5). - Adhere to the misunderstood limitation that OpenEXR library doesn't allow random writes to scanline files. (1.4.6) - More robust with certain malformed metadata. (#841) (1.4.6) * TIFF: Give a more explicit error message for unsupported tile sizes (1.4.4) * GIF: Fixes to subimage generation; GIF frames are treated as sequential windows to be drawn on canvas rather than as independent images; respect "disposal" method; initial canvas is now transparent and all GIFs are presented as 4-channel images. (#828) (1.4.5) * iconvert: properly handle multi-image files for formats that can't append subimages. (1.3.10/1.4.1) * iv info window should print native file info, not translated ImageBuf/ImageCache info. (1.3.10/1.4.1) * Fix ImageCache::get_pixels() for the chbegin != 0 case, when cache and output buffer types were not identical. (1.3.10/1.4.1) * DPX: - Fixed several places in the where it could have had buffer overruns when processing certain malformed string fields. (1.4.1) - Fixed inappropriate use of "dpx_ImageDescriptor" could make invalid DPX files (especially when reading metadata from one DPX file, changing the number of channels, then writing out again as a DPX file). (1.3.10/1.4.1) - For output, honor the "Software" metadata attribute passed in. (1.3.11/1.4.2) - Ignore negative image origin values, which are not allowed by the DPX spec which states they are unsigned. (#813) (1.4.4) - Fix improper handling of unsupported pixel data types. (#818) (1.4.5) - Accept pixel ratio (x/0) to mean 1.0, not NaN. (#834) (1.4.5/1.3.13) - Pad subimages to 8k boundaries, as suggested by the DPX spec (1.4.7) - Properly write "userdata" field to DPX files if set. (1.4.7) * PNG: - add "png:compressionlevel" and "compression" strategy attributes. (1.3.12/1.4.2) - output properly responds to "oiio:UnassociatedAlpha"=1 to indicate that the buffer is unassociated (not premultiplied) and therefore it should not automatically unpremultiply it. (1.4.5) * Make ImageBuf iterators return valid black pixel data for missing tiles. (1.3.12/1.4.2) * Make the ImageOutput implementations for all non-tiled file formats emulate tiles by accepting write_tile() calls and buffering the image until the close() call, at which point the scanlines will be output. (1.4.3) * All ImageBufAlgo functions, and oiiotool, strip any "oiio:SHA-1" hash values in the metadata in order not to confuse the TextureSystem. (1.4.3) * IFF: accept write_scanline, even though IFF is tile only. (1.4.3) * The implementation of the Lanczos filter (and any operations using it) have been sped up by using an approximate fast_sinpi instead of the more expensive sin() (1.4.3). * Speed up iinfo --hash / oiiotool --hash by about 20%. (#809) (1.4.4) * All format writer plugins: ensure that calling close() twice is safe. (#810) (1.4.4) * oiiotool --info and iinfo output have been altered slightly to make them match and be consistent. Also, oiiotool didn't say that deep files were deep (1.4.4). * Fixed bad bugs in IBA::flatten() and oiiotool --flatten. (#819) (1.4.5) * Fix Parameter neglect of properly copying the m_interp field for assignment and copy construction. (#829) (1.4.5/1.3.13) * Fix ImageBufAlgo::circular_shift (and oiiotool --cshift) that did not wrap correctly for negative shifts. (#832) (1.4.5/1.3.13) * The "gaussian" flter incorrectly had default width 2 (correct = 3), and the "mitchell" filter incorrect had default width 3 (correct = 4). These were bugs/typos, the new way is correct. If you were using those filters in ways that used the default width value, appearance may change slightly. (1.4.6) Build/test system improvements: * libOpenImageIO_Util is now built that only has the utility functions in libutil (in addition to the libOpenImageIO, which contains everything). This is handy for apps that want to use OIIO's utility functions (such as ustring or Filesystem) but doesn't really need any of the image stuff. A build flag BUILD_OIIOUTIL_ONLY=1 will cause only the util library to be built. (1.4.1) * New build option OIIO_THREAD_ALLOW_DCLP=0 will turn off an optimization in thread.h, resulting in possibly worse spin lock performance during heavy thread contention, but will no longer get false positive errors from Thread Sanitizer. The default is the old way, with full optimization! (1.4.1) * More robust detection of OpenEXR library filenames. (1.4.1) * Always reference OpenEXR and Imath headers as `` rather than ``. (1.4.1) * Unit test strutil_test now comprehensively tests Strutil. (1.4.1) * Fix broken build when EMBEDPLUGINS=0. (1.4.3/1.3.13) * Fix broken build against OpenEXR 1.x. (1.4.3/1.3.13) * version.h has been renamed oiioversion.h. For back compatibility, there is still a version.h, but it merely includes oiioversion.h. (#811) (1.4.4) * Moved all the public header files from src/include to src/include/OpenImageIO, so that the src/include area more closely matches the layout of an OIIO install area. (#817) (1.4.4) * Fix compilation problems for PowerPC (#825). (1.4.5/1.3.13) * Fixes for OpenBSD compilation. (#826/#830) (1.4.5/1.3.13) * Fixes for Linux compilation when building with BUILDSTATIC=1. (1.4.6) * Fixes for compilation against IlmBase/OpenEXR 2.1. (1.4.6) * Improve finding of Freetype on some systems (1.4.6). * Add to top level Makefile the option STOP_ON_WARNING=0 to let it cleanly compile code that generates compiler warnings, without stopping the build. (1.4.7) Developer goodies / internals: * TBB has been removed completely. (1.4.2) * Slightly faster timer queries in timer.h for OSX and Windows. (1.4.1) * Strutil : - safe_strcpy() -- like strncpy, but copies the terminating 0 char. (1.4.1) - split() fixes bug when maxsplit is not the default value. (1.3.10/1.4.1) * ParamValue/ParamValueList : - ParamValue now allows get/set of the internal 'interp' field. (1.3.9/1.4.1) - ParamValueList::push_back is not properly const-ified. (1.4.1) - New PVL::find() lets you search on the PVL. (1.4.6) * fmath.h : - New fast_sin, fast_cos, fast_sinpi, fast_cospi are much faster polynomial approximations (with max absolute error of ~0.001). (1.4.3) - round_to_multiple_of_pow2 - a faster version of the existing round_to_multiple(), but only works when the multiple is known to be a power of 2. (1.4.6) * TypeDesc now has operator<, which makes it easier to use STL data structures and algorithms that expect an ordering, using TypeDesc as a key. (1.4.6) * thread.h - Slight thread.h portability tweaks. (1.4.1) - spin_rw_lock now has more standard lock()/unlock() as synonym for for exclusive/write lock, and lock_shared()/unlock_shared() as synonym for "read" locks. (1.4.6) * ustring : - new ustringLess and ustringHashIsLess functors make it easier to use ustring as keys in STL data structures and algorithms that require an ordering function. (1.4.6) - improve thread performance significantly by using an unordered_map_concurrent internally for the ustring table. (1.4.6) * unordered_map_concurrent.h : - Allow umc template to specify a different underlying map for the bins. (1.4.6) - Add retrieve() method that's slightly faster than find() when you just need a value, not an iterator. (1.4.6) - Align bins to cache lines for improved thread performance. (1.4.6) * ImageBuf iterators have a new rerange() method that resets the iteration range, without changing images or constructing a new iterator. (1.4.6) Release 1.3.14 (19 May 2014 -- compared to 1.3.13) -------------------------------------------------- * OpenEXR output: More robust with certain malformed metadata. (#841) (1.4.6) * Rename the string_ref class to string_view. (This is unused in OIIO, it is for compatibility with OSL.) * Build fixes on Linux when using BUILDSTATIC=1. * Add round_to_multiple_of_pow2 to fmath.h * Add STOP_ON_WARNING option to the top level Makeile wrapper. * Add documentation on the Python binding for IBA::cut. * oiiotool --over and --zover now set the output image's display window to the union of the inputs' display window, rather than to the foreground. Release 1.3.13 (2 Apr 2014 -- compared to 1.3.12) ------------------------------------------------- * Bug fix to string_ref::c_str(). * Make ImageBuf iterators return valid black pixel data for missing tiles. * Fix broken build when EMBEDPLUGINS=0. * Fix broken build when building against OpenEXR 1.x. * Fix bad bugs in IBA::flatten() and oiiotool --flatten. (#819) * Fix DPX handling of unsupported pixel types. (#818) * Fix compilation problems for PowerPC. * Fix Parameter neglect of proerly copying the m_interp field for assignment and copy construction. (#829) * Fixes for OpenBSD compilation. (#826/#830) * DPX: accept pixel ratio (x/0) to mean 1.0, not NaN. (#834) * Fix ImageBufAlgo::circular_shift (and oiiotool --cshift) that did not wrap correctly for negative shifts. (#832) Release 1.3.12 (25 Jan 2014 -- compared to 1.3.11) -------------------------------------------------- * Add .sxr and .mxr as possible filename extensions for OpenEXR. * PNG: add "png:compressionlevel" and "compression" strategy attributes. * Fix recent build break where OIIO would no longer compile properly against OpenEXR <= 1.6. * oiiotool --origin could crash with certain large ImageCache-backed images. Release 1.3.11 (8 Jan 2014 -- compared to 1.3.10) ------------------------------------------------- * DPX output: honor the "Software" metadata attribute passed in. * OpenEXR: fix crashing bug when reading stringvector attributes in the file. * Fix build breaks when building against OpenEXR 1.x. * Fix warnings with Boost Python + gcc 4.8. Release 1.3.10 (2 Jan 2014 -- compared to 1.3.9) ------------------------------------------------ * OpenEXR fix: multi-part EXR (2.0) didn't write the required "name" attribute for each part. * iconvert: properly handle multi-image files for formats that can't append subimages. * iv info window should print native file info, not translated ImageBuf/ImageCache info. * Improved strutil_test now much more comprehensively unit tests Strutil. * Strutil::split() fixes bug when maxsplit is not the default value. * Fix ImageCache::get_pixels() for the chbegin != 0 case, when cache and output buffer types were not identical. * DPX bug fix -- inappropriate use of "dpx_ImageDescriptor" could make invalid DPX files (especially when reading metadata from one DPX file, changing the number of channels, then writing out again as a DPX file). Release 1.3 (2 Dec 2013 -- compared to 1.2.x) ---------------------------------------------- Major new features and improvements: * Huge overhaul of the Python bindings: TypeDesc, ImageSpec (1.3.2), ImageInput, ImageOutput (1.3.3), ROI, ImageBuf (1.3.4), ImageBufAlgo (1.3.6). The Python bindings were pretty rusty, badly tested, undocumented, and had not kept up with recent changes in the C++ APIs. That's all fixed now, the Python APIs are finally first-class citizens (including full functionality, unit tests, and docs), and we intend to keep it that way. * The ability for an application to supply custom ImageInput and associate them with a file extension. Those II's can do anything, including generate image data procedurally. * GIF reader Public API changes: * Large overhaul of the Python bindings. See the (finally existing!) docs. * ImageBufAlgo: * New functions: nonzero_region(); ociodisplay(), resize() variety that lets you specify the filter by name; 2-argument (non-in-place) versions of add, sub, mul, rangecompress, rangeexpand, unpremult, premult, clamp fixNonFinite; sub() varieties that take float or float* operands. * Removed several IBA functions that have been deprecated since 1.2. * Deprecated the single-image in-place versions of add, sub, mul, rangecompress, rangeexpand, unpremult, premult, clamp fixNonFinite. * ImageBuf: * read() and init_spec() are no longer required, somewhat simplifying application code that uses ImageBuf. All ImageBuf API calls automatically read the spec and/or pixels from their named file if they are needed, if it has not already been done. (1.3.4) * save() is deprecated, and new ImageBuf::write() is now preferred (naming symmetry). (1.3.4) * New set_write_format() and IB::set_write_tiles() allow override of default choices for data format and tile size for subsequent calls to ImageBuf::write(). (1.3.4) * ImageCache / TextureSystem: * ImageCache::add_file() lets you seed the ImageCache with a "virtual file" that will read from a custom ImageInput. This lets you add "procedural images" to the IC. * ImageCache::add_tile() lets you add tiles to the ImageCache. The caller can initialize those tiles with any pixel values it chooses. * A new variety of IC/TS::destroy() takes a 'bool teardown' parameter that, when true, does a complete teardown of the underlying ImageCache, even if it's the "shared" one. (1.3.7) * OIIO::declare_imageio_format() exposes a way to give OIIO a custom ImageInput and/or ImageOutput (via factory functions) and associate them with particular file extensions. This makes it especially easy for an app to make a procedural image generator that looks to the entire rest of OIIO like a regular image file. (1.3.2) * TypeDesc::VECSEMANTICS now have additional enum tags for TIMECODE and KEYCODE to indicate that the data represents an SMPTE timecode or SMPTE keycode, respectively. (1.3.7) Fixes, minor enhancements, and performance improvements: * oiiotool improvements: * --autotrim Shrinks pixel data window upon output to trim black edges. (1.3.2) * --siappend Appends subimages of top two images on the stack. (1.2.2) * --siappendall Appends ALL images on the stack into a single image with multiple subimages. #1178 (1.6.4) * --sisplit Splits the top multi-image into separate images on the stack for each subimage. #1178 (1.6.4) * --datadump will print all pixel values of an image (debugging tool) (1.3.6) * --flatten turns a "deep" image into a flat one by depth-compositing within each pixel (1.3.6). * --ociodisplay applies an OpenColorIO display transformation. (1.3.7) * Fix memory leak when processing frame range. (1.2.1/1.3.2) * --help now returns a success error code, not a failure. (1.2.1/1.3.2) * Fix incorrect help message about --ociolook. (1.2.1/1.3.2) * Fix typo in "oiio:Colorspace" attribute name that interfered with correct color space conversion in --colorconvert. (1.2.1) * Many fixes and improvements to XMP & IPTC metadata handling. (1.2.2) * Multithread speed improvement when opening files by reducing how much time ImageInput::create and/or ImageOutput::create hold a global mutex. * oiiotool --origin and --fullpixels, when operating on cropped or overscanned image, could sometimes do the wrong thing. (1.2.2/1.3.3) * oiiotool --colorconvert did not work properly when the color transformation was detected to be a no-op. (1.2.2/1.3.3) * oiiotool --fit did not handle padding or offsets properly. (1.2.2/1.3.3) * Changed/improved the behavior of --rangecompress/--rangeexpand. (1.3.3) * 'oiiotool --pattern checker' was incorrect when nonzero offsets were used. (1.2.3/1.3.4) * oiiotool --runstats prints the total time/memory on every iteration when doing file sequence wildcard iteration. (1.3.4) * Eliminated a particular situation that might hit an ASSERT. Instead, bubble up a real error message. (1.3.4) * oiiotool --resize and --resample fixed for overscan images (1.3.5) * --ociolook applies OCIO looks. (1.3.6) * Supports printf-style frame range wildcards ('%04d') in addition to the '#' style, and scan for matching frames if no explicit framespec is provided. (1.3.6) * ImageBufAlgo improvements: * colorconvert() did not work properly when the color transformation was detected to be a no-op. * colorconvert(): added a variety that specifies color spaces by name. * New ociolook() function applies OCIO "looks." (1.3.6) * checker() was incorrect when nonzero offsets were used. * checker() now has default values of 0 for the 'offset' parameters (and so may be omitted if you want 0 offsets). (1.3.4) * unsharp_mask() bug when src and dst were different data formats. (1.2.3/1.3.4) * Better dealing with cases of IBA functions detecting and issuing errors when inputs that must be initialized are not. (1.3.4) * We changed the behavior of rangecompress/rangeexpand. We swear the new way is better. (1.3.3) * New nonzero_region() returns the shrink-wrapped nonzero pixel data window. (1.3.2) * resize() has a new variety that lets you specify the filter by name (rather than allocating ans passing a Filter2D*). * resize() and resample() fixed to more robustly handle overscan images. (1.3.5) * over()/zover() are no longer restricted to float images. (1.3.7) * ImageBuf: * ImageBuf::write() writes untiled images by default, fixing some tricky issues when IB's start thinking they're tiled because of interaction with the ImageCache (which makes everything look tiled). * ImageBuf::file_format_name() never worked properly, now is fixed (1.3.4) * Fixed bug that caused incorrect ImageBuf::copy_pixels() when the two IB's had different data types. (1.3.4/1.2.3) * Improved iterator's handling of how overscanned pixels interact with wrap modes. (1.3.6) * Fixed a bug with black wrap mode not working correctly. (1.3.7/1.2.4) * ImageCache/TextureSystem: * More careful with texture de-duplication -- texture value lookups use de-duplication, but metadata lookups (e.g., get_texture_info) uses the metadata from the original file. * get_image_info/get_texture_info queries for "datawindow" and "displaywindow". (1.3.6) * The multi-point version of environment() was broken. (1.3.9/1.4.1) * maketx: --hicomp uses the new range compression/expansion formula. (1.3.3) * DPX: * support multi-image (often used for stereo frames). * Fixed DPX input that didn't recognized offset/cropped images. (1.2.2/1.3.3, another fix in 1.3.4) * Fixed DPX output crash with cropped images. (1.2.2/1.3.3) * Now correctly get and set "smpte:TimeCode" and "smpte:KeyCode" metadata. (1.3.7). * OpenEXR: * Fixed write_scanlines handling of per-channel data types (1.3.6) * Several OpenEXR 2.0 deep file fixes: only some compression types supported, write_tiles passed wrong parameters, must suppress some attribute names. (1.2.3/1.3.6) * Now correctly get and set "smpte:TimeCode" and "smpte:KeyCode" metadata. (1.3.7). * JPEG: fixed that some JPEG files were not being recognized because of magic number issues. * TGA: Correctly unassociate alpha if it's from an unasociated file; also, always write unassociated data because so few Targa readers in the wild seem to properly handle associated alpha. * PNG: More correct handling of unassociated alpha. * TIFF: More correct handling of unassociated alpha. * PSD: fix handling of associated vs unassociated alpha. (1.2.3) * maketx fixed to handle inputs that are a mixture of cropped and overscanned. (1.3.5) * Fix segfault if OCIO is set to a non-existant file. (1.3.6) * Slight performance increase when writing images to disk (1.3.6) * Many fixes to make OIIO compile with libc++ (clang's new C++ library, and the default on OSX Mavericks). (1.2.3/1.3.6, 1.3.7) * Fixed several potential buffer overflow errors from unsafe strcpy. (1.3.8) Build/test system improvements: * Fix broken tests under Windows. (1.3.2) * Many fixes for compiler warnings on various platforms: fmath_test.cpp, field3dinput.cpp, sysutil.cpp, argparse.cpp, oiiotool.cpp. (1.2.1/1.3.2) * Fixes problems on little-endian architecture with texture3d.cpp. (1.2.1/1.3.2) * Fix compilation problems on architectures with gcc, but no 'pause' instruction. (1.2.1/1.3.2) * Fix build search path for correctly finding libopenjpeg 1.5. (1.2.1) * Work around bug in older MSVC versions wherein Filesystem::open needed to explicitly seek to the beginning of a file. (1.2.1/1.3.2) * Build fixes for FreeBSD. (1.2.1/1.3.2, 1.2.4/1.3.6) * Fix testsuite/oiiotool on Windows -- windows shell doesn't expand wildcards. (1.2.1/1.3.2) * Fix warnings for new GCC 4.8 compiler. * Always search for and use the release HDF5 libraries, not the debugging ones, even when building debug OIIO (this fixes errors when a system does not have the debugging HDF5 libraries installed). (1.2.2/1.3.3) * Extensive unit tests in the testsuite for the Python bindings. * Fix compiler error on MIPS platform. (1.2.2/1.3.3) * Add FIELD3D_HOME description to 'make help' (1.2.2/1.3.3) * Add cmake variables ILMBASE_CUSTOM_INCLUDE_DIR, ILMBASE_CUSTOM_LIB_DIR, OPENEXR_CUSTOM_INCLUDE_DIR, and OPENEXR_CUSTOM_LIB_DIR to make it easier to have site-specific hints for these packages' locations. (1.3.4) * Add BOOST_HOME and OCIO_HOME controls from the top-level Makefile wrapper. (1.3.4/1.2.3) * Accommodate new cmake release that slightly changes the HDF5 library naming. (1.3.6) * Various fixes to make the code compile properly with libc++ (clang's rewrite of the C++ standard library). (1.3.6) * Updated PugiXML (partly to help compilation with libc++) (1.3.6) * Better support for NOTHREADS (for some legacy systems) (1.3.6) * Fix to __attribute__(visibility) for gcc < 4.1.2 (1.3.6) * Improve the CMake build files to fully quote path constructions to make it more robust for builds with paths containing spaces. (1.3.7) * Moved the main CMakeLists.txt file to the top level directory, per usual CMake conventions. (1.3.7) * Fixed timer_test to allow for timing slop of newer OSX versions that have "timer coalescing." (1.6.4) Developer goodies: * Docs improvement: full documentation of ImageBufAlgo. (1.2.1/1.3.2) * Merge improved "Tinyformat" that fixes a bug in some old glibc versions (1.3.2). * Now each command line tools explicitly converts to UTF native arguments, rather than relying on it happening in ArgParse (which no longer does so). (1.3.2) * Strutil::contains() and icontains(). (1.2.2/1.3.3) * Updatd "Tinyformat" to the latest release (1.3.6) * Sysutil::physical_memory() tries to figure out the total physical memory on the machine. (1.3.6) * Strutil::safe_strcpy (1.3.8) * ParamValue now allows get/set of the hidden 'interp' field. (1.3.9/1.4.1) Release 1.2.3 (1 Nov 2013) -------------------------- * 'oiiotool --pattern checker' (and ImageBufAlgo::checker) was incorrect when nonzero offsets were used. * ImageBufAlgo::unsharp_mask() bug when src and dst were different data formats. * PSD: fix handling of associated vs unassociated alpha. * Fixed bug that caused incorrect ImageBuf::copy_pixels() when the two IB's had different data types. * Add BOOST_HOME and OCIO_HOME controls from the top-level Makefile wrapper. * Several OpenEXR 2.0 deep file fixes: only some compression types supported, write_tiles passed wrong parameters, must suppress some attribute names. * DPX - several fixes to properly handle images with nonzero origins. * Fixes for recent cmake not finding HDF5 properly. * Many fixes to make OIIO compile with libc++ (clang's new C++ library, and the default on OSX Mavericks). * Fix OpenEXR write_scanlines handling of per-channel data types. * Upgraded PugiXML to a more modern version (necessary for clean compile with libc++). Release 1.2.2 (1 Oct 2013) -------------------------- * New features: * New oiiotool --siappend : append subimages of top two images on stack. * Utilities: added Strutil::contains() and icontains(). * Fixes: * Fixes in handling XMP & IPTC metadata. * oiiotool --origin and --fullpixels did not correctly propagate their changes to the output images. * oiiotool --colorconvert (and the underlying ImageBufAlgo::colorconvert) could crash if given a color conversion recognized as a no-op. * DPX output could crash when writing crop images. * DPX input was not recognizing the proper image offset or originalsize. * oiiotool --fit wasn't padding correctly or modifying offsets properly. * Build fixes: * Fix compiler error on MIPS platform. * Add FIELD3D_HOME description to 'make help' * Always use the HDF5 release libraries (for Field3D), not the debug ones. Release 1.2.1 (5 Aug 2013) --------------------------- * oiiotool: Fix memory leak when processing frame range. * Docs improvement: full documentation of ImageBufAlgo. * oiiotool --help now returns a success error code, not a failure. * oiiotool: fix incorrect help message about --ociolook. * oiiotool: Fix typo in "oiio:Colorspace" attribute name that interfered with correct color space conversion in --colorconvert. * Many fixes for compiler warnings on various platforms: fmath_test.cpp, field3dinput.cpp, sysutil.cpp, argparse.cpp, oiiotool.cpp. * Fixes problems on little-endian architecture with texture3d.cpp. * Fix compilation problems on architectures with gcc, but no 'pause' instruction. * Fix build search path for correctly finding libopenjpeg 1.5. * Work around bug in older MSVC versions wherein Filesystem::open needed to explicitly seek to the beginning of a file. * Build fixes for FreeBSD. * Fix testsuite/oiiotool on Windows -- windows shell doesn't expand wildcards. Release 1.2 (8 July 2013) ------------------------- Major new features and improvements: * New oiiotool commands: * `--swap` Exchanges the top two items on the image stack. * `--fit` Resize image to fit into a given resolution (keeping aspect). * `--ch` Select/cull/reorder/add channels within an image. * `--chappend` Merge two images by appending their color channels. * `--chnames` Rename some or all of the color channels in an image. * `--zover` Depth compositing * `--cadd` Add constant per-channel values to all pixels * `--cmul` Multiply an image by a scalar or per-channel constant. * `--fillholes` Smoothly interpolate for hole filling. * `--resample` Similar to `--resize`, but just uses closest pixel lookup. * `--clamp` Clamp pixel values * `--rangeexpand` Expand range for certain HDR processing * `--rangecompress` Compress range for certain HDR processing * `--unpremult` Divide colors by alpha (un-premultiply). * `--premult` Multiply colors by alpha. * `--kernel` Make a convolution kernel using a filter name. * `--convolve` Convolve two images. * `--blur` Blur an image. * `--unsharp` Sharpen an image using an unsharp mask. * `--paste` Paste one image on another. * `--mosaic` Create a rectilinear image mosaic. * `--transpose` Transpose an image (flip along the diagonal axis) * `--chsum` Sum all channels in each pixel * `--cshift` Circular shift an image pixels * `--fft` `--ifft` Forward and inverse Fourier transform * `--colorcount` Counts how many pixels are one of a list of colors. * `--rangecheck` Counts how many pixels fall outside the given range. * `--ociolook` Apply OpenColorIO "looks" * oiiotool can loop over entire numeric frame ranges by specifying wildcard filenames such as "foo.#.tif" or "bar.1-10#.exr". * oiiotool --frames and --framepadding give more explicit control over frame range wildcards. * Significant performance improvements when reading and writing images using the ImageBuf::read and ImageCache::get_pixels interfaces, and in some cases also when using regular ImageInput. This also translates to improved performance and memory use for oiiotool and maketx. * At least doubled the performance of maketx for large images when run on multi-core machines. * Significant performance improvements when using ImageBuf::Iterator or ConstIterator to traverse the pixels in an ImageBuf, and the iterators now support "wrap" modes (black, clamp, periodic, mirror). * maketx --hicomp does "highlight compensation" by compressing the HDR value range prior to inter-MIP resizes, then re-expanding the range. * Field3D writer (it could read f3d files before, but not write them). * idiff can now compare that are not the same size (treating pixels beyond the pixel data window is being 0 valued). * maketx --lightprobe turns a "lightprobe" iamge into a latlong environment map. * Significant improvements and fixes to EXIF, IPTC, and XMP metadata reading and writing. * Significant thread scalability improvements to TextureSystem and ImageCache. * Huge overhaul of functionality, style, and performance of the entire ImageBufAlgo set of functions (see the "Public API changes" section below, and the imagebufalgo.h file for details). Public API changes: * ImageOutput semantics change: If the spec passed to open() has spec.format set fo UNKNOWN, then select a default data format for the output file that is "most likely to be able to be read" and/or "most typical for files of that format in the wild." Also, ImageOutput::open() will never fail because a requested data format is unavailable; if the requested format is not supported, a reasonable alternate will always be chosen. * ImageBuf has been changed to a "PIMPL" idiom, wherein all the internals are no longer exposed in the public API. This allows us to change ImageBuf internals in the future without breaking API or link compatibility (and thus giving us more freedom to backport important improvements to prior releases). * Overhaul of ImageBufAlgo functions: they all take an ROI parameter; use the DISPATCH macros to make them work with all pixel data types where practical (previously, many supported float only); use Iterator rather than getpixel/setpixel, leading to huge speed improvements; multithread when operating on enough pixels, leading to huge speed improvements; work on 3D (volume) images where applicable; always gracefully handle uninitialized dest image or undefined ROI. * New ImageBufAlgo functions: channels(), channel_append(), mul(), paste(), zover(), add() and mul() varieties that that add/multiply a constant amount to all pixels, fillholes_pp(), resample(), clamp(), rangecompress(), rangeexpand(), make_kernel(), unsharp_mask(), transpose(), channel_sum(), circular_shift(), fft(), ifft(), color_count(), color_range_check(). [look in imagebufalgo.h for documentation.] * ImageBufAlgo::make_texture() allows you to do the same thing that maketx does, but from inside an application and without launching a shell invocation of maketx. Two varieties exist: one that takes a filename and reads from disk, another that takes an ImageBuf already in memory. * ImageBuf Iterator/ConstIterator now take "wrap" mode parameters that allow out-of-range iterators to be able to retrieve valid data. Supported wrap modes include black, clamp, periodic, and mirror. This simplifies a lot of algorithms using IB::Iterator, they can now be written to rely on wrap behavior rather than being cluttered with checks for "if (it.exits())" clauses. * ImageBufAlgo::computePixelHashSHA1 has been refactored to take ROI, a block size, and thread count, and thus can be parallelized with threads. The block size means that rather than computing a single SHA-1 for all the pixels, it computes separate (parallel) SHA-1 for each group of blocksize scanlines, then returns the SHA-1 of all the individual SHA-1 hashed blocks. This is just as strong a hash as before, thought the value is different than doing the whole thing at once, but by breaking it into blocks the computation can be multithreaded. * ImageBuf::swap() makes it easy to swap two ImageBuf's. * ImageSpec::get_channelformats is now const (and always should have been). Fixes, minor enhancements, and performance improvements: * TextureSystem improvements: * Make sure "black" wrap wins out over "fill" value when they conflict (looking up an out-of-range channel beyond the pixel data window). * "mirror" wrap mode was slightly incorrect and has been fixed. * oiiotool improvements: * oiiotool -v breaks down timing by individual function. * oiiotool has been sped up by forcing read of the whole image up front for reasonably-sized files (instead of relying on ImageCache). * oiiotool does not write output images if fatal errors have occurred. * oiiotool --diff: Better error handling, better error printing, and now it can compare images with differing data windows or channel numbers ("missing" channels or pixels are presumed to be 0 for the purposes of comparing). * oiiotool --resize (and --fit): properly handle the case of resizing to the same size as the original image. * oiiotool -d CHAN=TYPE can set the output for just one channel. * ImageBufAlgo improvements: * Internal overhaul of IBA::resize to greatly speed it up. * Improve IBA::resize to handle the image edge better -- instead of clamping, just don't consider nonexistant pixels. * More careful selection of filter when resizing (IBA::resize, oiiotool --resize and --fit, and maketx). * Fix IBA::paste() error when the foreground image runs off the end of the background image. * Bug fix when computing SHA-1 hash of 0-sized images. * Image format support improvements: * Bug fix where some format readers (PNM, SGI, and IFF) would leave the file handle opened if the ImageInput was destroyed without calling close() first. Now we guarantee that destroying the II always causes the file to close(). * DPX: output allocation bug fix; properly set pixel aspect ratio for DPX write. * IFF: bug fix for endian swap for IFF file input. * JPEG2000: fix warnings, make sure event manager transfer object remains valid. * OpenEXR: when reading, was botching the ordering of per-channel data formats. * SGI write: bug fix for the case of 15 bpp RLE encoding, was double-swapping bytes. * Targa: more robust check for presence of alpha channels; bug fix where R and B channels were reversed for certain kinds of palette images. * TIFF: Store the orientation flag properly when outputting a TIFF file. * maketx improvements: * maketx --chnames allows you to rename the channels when you create a texture. * maketx bug fixes: incorrect weighting when resizing MIP levels for latlong environment map images that could make visible artifacts on some intermediate MIP levels. * encode_exif() didn't copy the right number of bytes. * Python bindings: ImageSpec extra_attribs now properly responds to iterator calls. * Fix bug in sRGB -> linear conversion. * iv: make pixelview display coordinates & color even when outside the data window. Build/test system improvements: * Many fixes to improve builds and eliminate warnings on Windows and MinGW. * Fix missing InterlockedExchangeAdd64 for Windows XP. * New make/cmake boags: OIIO_BUILD_TOOLS=0 will exclude building of the command line tools (just build libraries), OIIO_BUILD_TESTS=0 will exclude building of unit test binaries. * Improved matching of testsuite reference images on different platforms. * Lots of fixes to compiler warnings on newer gcc and clang releases. * Unit tests for Timer class. * libOpenImageio/imagespeed_test benchmarks various methods of reading and writing files and iterating image pixels (to help us know what to optimize). * If OpenSSL is available at build time, OIIO will use its SHA-1 implementation instead of our own (theirs is faster). We still fall back on ours if OpenSSL is not available or when OIIO is built with USE_OPENSSL=0. * Allow default the shared library suffix to be overridden with the CMake variable OVERRIDE_SHARED_LIBRARY_SUFFIX. * Eliminated all uses of the custom DEBUG symbol, and instead use the more standard idiom "#ifndef NDEBUG". * Compatibility fixes for Python3. * MSVC 2008: Prevent a redefinition error when using boost::shared_ptr. * Fixes for compatibility with libtiff 4.0. * Fixes for MSVC debug mode having out-of-bound exceptions. * Fixes for libjpeg 9.x. * Compile to treat warnings as errors (you can disable this with STOP_ON_WARNING=0). * New filter: "sharp-gaussian". * Fix various Windows build errors. * Improvements to the build when finding IlmBase/OpenEXR. * Various fixes to compile on ARM architecture. * Fixes to compile on ESA/390 mainframe (!). * testtex --threadtimes, --trials, --iters, --nodup, --wedge. These are helpful in using testtext to benchmark the texture system. * Improvements to make more tests work properly on Windows. Developer goodies: * Improved ASSERT and DASSERT macros to not generate warning for certain debug compiles; key their debug behavior by the absence of the standard NDEBUG idiom rather than presence of a custom DEBUG symbol; rename the message variants ASSERT_MSG and DASSERT_MSG. * Change the default for Sysutil::memory_used to report resident memory rather than virtual process size. * Multithread/parallel version of utility function convert_image(). * imagebufalgo.h improvements and expansion of the various DISPATCH_* macros. * New Filesystem utilities: parent_path(), get_directory_entries(). * New Strutil utilities: extract_from_list_string * spinlock tweaks make it faster than TBB's spin locks! * By default, we no longer build or use TBB (it's considered deprecated, but in 1.2 can still be turned on with USE_TBB=1). * In fmath.h, added definitions for safe_inversesqrt, safelog, safe_log2, safe_log10, safe_logb. * In typedesc.h, added TypeDesc::tostring() function. * unordered_map_concurrent.h contains a template for a thread-safe unordered_map that is very efficient even for large number of threads simultaneously accessing it. * Documentation: Finally, a chapter in the PDF docs that fully describes the ImageBuf class. Release 1.1.13 (24 Jun 2013) ---------------------------- * Texture: make sure wrap mode "black" wins over "fill" value when they conflict. Release 1.1.12 (20 Jun 2013) ---------------------------- * Fix oiiotool '#' wildcard, was broken on Windows. * Fix an overflow problem that plagued 'maketx' when running on input larger than 32k x 32k (among other possible failures). Release 1.1.11 (29 May 2013) ---------------------------- * IFF input: bug in endian swap of 16 bit IFF files. * oiiotool: fix a minor bug where tiled files were output inappropriately. (Had been patched in master some time ago.) * fmath.h additions: safe_inversesqrt, safe_log, safe_log2, safe_log10, safe_logb. These are versions that clamp their inputs so that they can't throw exceptions or return Inf or NaN. * Fix to not incorrectly print ImageCache stats for certain broken files. Release 1.1.10 (13 Apr 2013) ---------------------------- * IBA::fillholes() and oiiotool --fillholes can smoothly fill in alpha holes with nearby colors. Great for extrapolating the empty areas of texture atlas images so that filtered texture lookups pull in a plausible color at part edges. * IBA::clamp and oiiotool --clamp clamp pixel values to a scalar or per-channel min and/or max, or clamp alpha to [0,1]. * IBA::rangecompress()/rangeexpand(), and oiiotool --rangecompress / --rangeexpand compress the excess >1 values of HDR images to a log scale (leaving the <= 1 part linear), and re-expand to the usual linear scale. This is very helpful to reduce ringing artifacts that can happen when an HDR image is resized with a good filter with negative lobes (such as lanczos3), by doing a range compression, then the resize, then range expansion. It's not mathematically correct and loses energy, but it often makes a much more pleasing result. * maketx --hicomp does highlight compression -- automatically doing a range compress before each high-quality resize step, and then a range expansion and clamp-to-zero (squash negative pixels) after each resize. * DPX - when writing DPX files, properly set the pixel aspect ratio. Release 1.1.9 (2 Apr 2013) -------------------------- * IBA::resize and oiiotool --resize/--fit: Bug fixes to resize filter size selection fix artifacts wherein extreme zooms could end up with black stripes in places where the filters fell entirely between samples. * oiiotool --fit: fix subtle bugs with aspect ratio preservation for images with differing data and display windows; and allow "filter=..." to override the default filter used for fit. * Resize improvement: fix potential artifacts at the image edges resulting from odd clamping behavior. * Even more frame range wildcard flexibility with oiiotool --frames and --framepadding options. * oiiotool --resize and --fit (and the underlying IBA::resize()) have been sped up significantly and are now also multithreaded. Release 1.1.8 (15 Mar 2013) --------------------------- * oiiotool --chappend (and ImageBufAlgo::channel_append() underneath) allow you to take two files and concatenate their color channels. * oiiotool --chnames allows you to rename some or all of a file's color channels. * oiiotool can loop over entire frame ranges by specifying wildcard filenames such as "foo.#.tif" or "bar.1-10#.exr". * Cmake: OVERRIDE_SHARED_LIBRARY_SUFFIX allows the shared library suffix to be overridden (e.g., if you need to force .so names on OSX rather than the usual default of .dylib). Release 1.1.7 (21 Feb 2013) --------------------------- * Back out dangerous change to thread.h that was in 1.1.6, which could cause performance problems. * Compile fix for WIN32 in strutil.cpp * Compile fix for Windows XP - add implementation of InterlockedExchangeAdd64 Release 1.1.6 (11 Feb 2013) --------------------------- * Fix bug that could generate NaNs or other bad values near the poles of very blurry environment lookups specifically from OpenEXR latlong env maps. * Fix bug in oiiotool --crop where it could mis-parse the geometric parameter. * Fix bug in ImageCache::invalidate() where it did not properly delete the fingerprint of an invalidated file. * Cleanup and fixes in the oiiotool command line help messages. * New function ImageBufAlgo::paste() copies a region from one IB to another. * oiiotool --fit resizes an image to fit into a given resolution (keeping the original aspect ratio and padding with black if needed). * ImageBufAlgo::channels() and "oiiotool --ch" have been extended to allow new channels (specified to be filled with numeric constants) to also be named. * New function ImageBufAlgo::mul() and "oiiotool --cmul" let you multiply an image by a scalar constant (or per-channel constant). * Important maketx bug fix: when creating latlong environment maps as OpenEXR files, it was adding energy near the poles, making low-res MIP levels too bright near the poles. * Fix to "oiiotool --text" and "oiiotool --fill" -- both were incorrectly making the base image black rather than drawing overtop of the previous image. * Fix FreeBSD compile when not using TBB. * New oiiotool --swap exchanges the top two items on the image stack. Release 1.1.5 (29 Jan 2013) --------------------------- * Bug fix in ImageBufAlgo::parallel_image utility template -- care when not enough work chunks to dole out to all the threads (was previously sending work to threads with nonsensical ROI's, now we just stop when all the regions have been doled out). * Additional optional argument to IBA::zover that, when nonzero, will treat z=0 pixels as infinitely far away, not super close. You can turn this on from oiiotool with: oiiotool --zover:zeroisinf=1 ... Release 1.1.4 (27 Jan 2013) --------------------------- * ImageBufAlgo::make_texture() allows you to do the same thing that maketx does, but from inside an application and without launching a shell invocation of maketx. * oiiotool now recognizes --metamatch and --nometamatch arguments which cause metadata names matching (or only info NOT matching) the given regular expression to be printed with --info. * oiiotool --zover does z (depth) composites (it's like a regular "over", but uses the z depth at each pixel to determine which of the two images is the foreground and which is the background). * ImageBufAlgo::zover() performs z compositing (same as oiiotool --zover). * ImageBufAlgo::fixNonFinite didn't work properly with 'half' image buffers. * Performance improvements when reading and writing images. * Fix error when writing tiled 'float' TIFF images, corrupted output. (Could easily happen when using 'maketx' to convert float images into TIFF textures.) * Eliminate warnings when compiling with Clang 3.2. * New CMake variable "USE_EXTERNAL_TBB" can optionally be set to force use of an external TBB library rather than the embedded one. * Additional testsuite tests (doesn't affect users, but makes bugs easier to catch). * Fix build problem with SHA1.cpp on some platforms. Release 1.1.3 (9 Jan 2013) --------------------------- * Build fix: incorrectly named OpenEXR 2.x files. * Bug fix in oiiotool --croptofull on OSX * Build fixes for MinGW on Windows. * maketx --fullpixels option ignores any origin or display window in the source image, pretending the pixel data is the entire 0-1 image range starting at the origin (useful when the source image is created by an application that incorrectly writes it out as if it were a crop window). * maketx no longer will clobber existing ImageDescription metadata when it adds SHA-1 hash or other info as it creates the texture. * Many additional Exif and IPTC tags are correctly recognized. * maketx and oiiotool recognize and take advantage of IPTC:ImageHistory metadata. Release 1.1.2 (5 Dec 2012) -------------------------- * maketx fixes -- was botching creation of textures from source images that were crop windows (pixel window smaller than display window). * Minor bug fix to Timer when repeatedly starting and restopping (Apple only). * Bug fix in ustring:find_last_not_of. Release 1.1.1 (16 Nov 2012) --------------------------- * Altered the ImageInput::read_scanlines, read_tiles, read_native_scanlines, read_native_tiles, read_native_deep_scanlines, read_native_deep_tiles, and the channel-subset version of ImageSpec::pixel_bytes, so that instead of specifying channel subsets as (firstchan, nchans), they are specified as [chbegin, chend), to match how spatial extents are done, as well as how channel ranges already were specified in ROI and ImageBuf. We hate changing API meanings, but we really think this is better and more consistent. Note that the two common uses of channel subsets were firstchan=0,nchans=nchannels (select all channels) and firstchan=foo,nchans=1, and we have rigged it so that [chbegin,chend) returns the same channels in both of these cases (in the latter case, because we retrieve a minimum of 1 channel), so we believe this is unlikely to break working code in the vast majority of cases. * OpenEXR: support reading and writing V2f attributes. * OIIO::getattribute("extension_list") returns a list of all formats supported, and all extensions for each format, in the form: "formatA:ext1,ext2,ext3;formatB:ext4,ext5;..." * The new ImageCache per-file stats that list numbers of tiles read per MIPmap level have been reformatted slightly, and now print only for files that are actually MIP-mapped. * New ImageCache::get_pixels() variety that can retrieve a subset of channels. * Substantial speedup of ImageCache::get_pixels, used to be about 50% more expensive to call IC::get_pixels compared to a direct call to ImageInput::read_image; now is only about 15% more expensive to use the cache. Release 1.1 (9 Nov 2012) ------------------------ Major new features and improvements: * Support for reading and writing "deep" images (including OpenEXR 2.0). * Big ImageCache/TextureSystem improvements: - Improved accuracy of anisotropic texture filtering, especially when combined with "blur." - Improve performance in cases with high numbers of threads using the TS simultaneously (mostly due to use of reader-writer locks on the tile cache rather than unique locks). * New ImageBufAlgo functions: * `fromIplImage()` : converts/copies an OpenCV image to an ImageBuf. * `capture_image()` : captures from a camera device (only if OpenCV is found) * `over()` : Porter/Duff "over" compositing operation * `render_text()` : render text into an image * `histogram()` : compute value histogram information for an image * `histogram_draw()` : compute an image containing a graph of the histogram of another image * `channels()` : select, shuffle, truncate, or extend channels of an image. * New oiiotool commands: * `--capture` : captures from a camera device (only if OpenCV is found) * `--pattern` constant : creates a constant-color image * `--over` : Porter/Duff "over" compositing operation * `--text` : render text into an image. * `--histogram` : computes an image containing a graph of the histogram of the input image. * `--fill` : fills a region with a solid color * `--ch` : select, shuffle, truncate, or extend channels API changes: * A new static ImageInput::open(filename [,config]) combines the old create-and-open idiom into a single call, which is also much more efficient because it won't needlessly open and close the file multiple times. This is now the preferred method for reading a file, though the old-style create() and open() still work as always. * Deep image support: ImageInput adds read_native_deep_scanlines, read_native_deep_tiles, read_native_deep_image, and ImageOutput adds write_deep_scanlines, write_deep_tiles, write_deep_image, as well as a supports("deepdata") query. Also, a 'deep' field has been added to ImageSpec, and some deep data access functions have been added to ImageBuf. * Altered the ImageInput::read_scanlines, read_tiles, read_native_scanlines, read_native_tiles, read_native_deep_scanlines, read_native_deep_tiles so that instead of specifying channel subsets as (firstchan, nchans), they are specified as [chbegin, chend), to match how spatial extents are done, as well as how channel ranges already were specified in ROI and ImageBuf. We hate changing API meanings, but we really think this is better and more consistent. Note that the two common uses of channel subsets were firstchan=0,nchans=nchannels (select all channels) and firstchan=foo,nchans=1, and we have rigged it so that [chbegin,chend) returns the same channels in both of these cases (in the latter case, because we retrieve a minimum of 1 channel), so we believe this is unlikely to break working code in the vast majority of cases. * ImageInput plugins now may supply a valid_file(filename) method which detects whether a given file is in the right format, less expensively than doing a full open() and checking for errors. (It's probably the same cost as before when the file is not the right time, but when it is, it's less expensive because it can stop as soon as it knows it's the right type, without needing to do a full header read and ImageSpec setup.) * New ImageCache::get_pixels() method that can retrieve a subset of channels. * Removed various error_message() functions that had been deprecated for a long time (in favor of newer getmessage() functions). * Define a namespace alias 'OIIO' that gets you past all the custom namespacesin a convenient way. * TextureOpt now contains a 'subimagename' field that allows subimages to be addressed by name as well as by index (only for multi-image textures, of course). * ImageBuf improvements: - A new constructor allows an ImageBuf to "wrap" an existing buffer memory owned by the calling application without allocating/copying. - Renamed the old ImageBuf::copy_pixels -> get_pixels, and it now works for 3D (volumetric) buffers. - New ImageBuf::copy(), and eliminated operator= which was confusing. - New ImageBuf methods: reres(), copy_metadata(), copy_pixels(), get_pixel_channels(). - ImageBuf::specmod() allows writable access to the ImageSpec (caution!). - Better error reporting mechanism. - get_pixels and get_pixel_channels take optional strides. * ImageBufAlgo changes: - Many ImageBufAlgo functions now take a 'ROI' that restricts the operation to a particular range of pixels within the image (usually defaulting to the whole image), and for some operations a range of channels. - zero() and fill() take ROI arguments. - ImageBufAlgo::CompareResults struct changed the failure and warning counts to imagesize_t so they can't overflow int for large images. * OIIO::getattribute("format_list") now can retrieve the comma-separated list of all known image file formats. * OIIO::getattribute("extension_list") returns a list of all formats supported, and all extensions for each format, in the form: "formatA:ext1,ext2,ext3;formatB:ext4,ext5;..." Fixes, minor enhancements, and performance improvements: * ImageCache/TextureSystem: - Anisotropic texture lookups are more robust when the derivatives are tiny. - Attribute "deduplicate" controls whether the identical-image deduplication is enabled (on by default). - Attribute "substitute_image" lets you force all texture references to a single image (helpful for debugging). - Texture files are no longer limited to having tile sizes that are powers of 2. - Much faster TIFF texture access (by speeding up switching of MIPmap levels). - More graceful handling of the inability to free handles or tiles under extreme conditions. Rather than assert when we can't free enough to stay within limits, just issue an error and allow the limits to be exceeded (hopefully only by a little, and temporarily). - Detailed per-file stats now track the number of tile reads per MIPmap level. - Attribute "unassociatedalpha" (when nonzero) requests that IC images not convert unassociated alpha image to associated alpha. - Substantial speedup of ImageCache::get_pixels, used to be about 50% more expensive to call IC::get_pixels compared to a direct call to ImageInput::read_image; now is only about 15% more expensive to use the cache. * iconvert handles the int32 and uint32 cases. * Bug fix in to_native_rectangle, which could lead to errors in certain data format conversions. * iv improvements: - better behavior after closing the last image of the sequence. - file load/save dialogs can filter to show just certain image file types. - remember last open dialog directory - "About" dialog has a link to the OIIO home page * Improve ::create to more robustly handle files whose extensions don't match their actual formats. * OpenImageIO::geterror() is now thread-specific, so separate threads will no longer clobber each others' error messages. * OpenEXR: support for building with OpenEXR 2.x, including use of multi-part EXR and "deep" data. * Fix reading bugs in DPX and Cineon. * DPX: fix endianness problem for 15 bit DPX output. * PNG: fix handling of gamma for sRGB images. * oiiotool fixes: print MIP messages correctly (it was only printing for the first MIP level); make sure stray "oiio:BitsPerSample" in an input file doesn't mess up the -d flags. * Field3D fixes: properly catch exceptions thrown by the Field3D open(); conform metadata to Field3D conventions; multi-layer f3d files will present as a multi-image file with the "oiio:subimagename" giving a unique name for each layer subimage; * OpenEXR: suppress format-specific metadata from other formats. * OpenEXR: support reading and writing V2f attributes. * Targa: fix several bugs that were preventing certain metadata from being written properly. * TIFF: recognize the SAMPLEFORMAT_IEEEFP/bitspersample=16 as an image composed of "half" pixels; enable PREDICTOR_FLOATINGPOINT to give slightly better compression of float images. * Handle UTF-8 paths properly on Windows. * Removed the obsolete "iprocess" utility. * Fix allocation and stride bugs when dealing with images having different data formats per channel, and tiled images with partially filled border tiles. * Field3D: Bug fix when reading vector f3d files. * Significant performance improvements of our atomics and spin locks when compiling with USE_TBB=0. * Fix quantize() to properly round rather than truncate. * ImageBufAlgo functions now by convention will save error messages into the error state of their output ImageBuf parameter. * Improve I/O error checking -- many file reads/writes did not previously have their result status checked. * Fixed missing OpenEXR open() error message. * Clean up error reporting in iconvert. * Fixes to handle Windows utf8 filenames properly. * ImageBufAlgo::compare() gives a sensible error (rather than an assertion) if the images being compared are not float. * maketx: - Better error messages for a variety of things that could go wrong when reading or writing image files. - Fixes for bug preventing certain ImageCache efficiencies. - new option --ignore-unassoc leaves unassociated alpha data as it is (no auto-conversion to associated alpha) and/or ignores the tags for an input file that is associated but incorrectly tagged as unassociated alpha. - Option --monochrome-detect was buggy for images with alpha. - Option --constant-color-detect didn't do anything; now it works. - New option: --compression allows you to override the default compresion. * oiiotool & info: the --hash command had a bug wherein when applied to images there were MIP-mapped, would hash the lowest-res MIP level rather than the highest-res. This could result in two different images, if they happened to have the same average color, to incorrectly report the same SHA-1 hash. Note that this did NOT affect non-MIPmapped images, nor did it affect the SHA-1 hashing that occurred in maketx to allow the TextureSystem to detect duplicate textures. Build/test system improvements: * Various Windows build fixes, including fixes for Windows 7, and improvements to running the testsuite on Windows. * Testsuite additions and improvements: png fmath_test * Compilation fixes on FreeBSD. * Compilation fixes on GNU Hurd platform. * Compilation and warning fixes for Clang 3.1. * Add FIELD3D_HOME build variable to allow explicit path to Field3D implementation. * Remove support for Boost < 1.40. * Improved unit tests for atomics, spin locks, and rw locks. * Avoid generating iv man pages when USE_QT=0 * New testtex options: --aniso, --stblur * CMake option 'EXTRA_CPP_DEFINITIONS' lets custom builds inject site-specific compiler flags. * Make/cmake option: HIDE_SYMBOLS=1 will try to restrict symbol visibility so that only symbols intended to be part of the public APIs will be visible in the library when linked. * The old DLLPUBLIC and LLEXPORT macros, which could clash with other packages, have been renamed to OIIO_API and OIIO_EXPORT. * Greatly reduced output when building with cmake; by default, most non-error status messages only are printed when VERBOSE=1 compilation is requested. Developer goodies: * Strutil new utilities: iequals, istarts_with, iends_with, to_lower, to_upper, strip, join. * Use Chris Foster's 'tinyformat' for type-safe printf-like formatting, and this now forms the basis of Strutil::format, ustring::format, and many of the classes' error() methods. * TypeDesc::equivalent() tests for type equality but allows triples with different' vector semantics to match. * In timer.h, a new time_trial() template that makes multiple trial benchmarks easy. * Macros for memory and cache alignment (in sysutil.h). * Extend Filesystem::searchpath_find() to be able to search recursively. * Strutil::strip() strips whitespace (or other specified character sets) from the beginning or ending of strings. * Change threads.h to set USE_TBB=0 if undefined as a compiler flag; this makes it easier to use threads.h in other applications without worrying about TBB at all. * Windows utf8 filename utilities path_to_windows_native and path_from_windows_native. Release 1.0.10 (5 Nov 2012) --------------------------- * ImageCache: more graceful handling of the inability to free handles or tiles under extreme conditions. Rather than assert when we can't free enough to stay within limits, just issue an error and allow the limits to be exceeded (hopefully only by a little, and temporarily). * ImageCache: Detailed per-file stats now track the number of tile reads per MIPmap level. * ImageCache attribute "unassociatedalpha" (when nonzero) requests that IC images not convert unassociated alpha image to associated alpha. * maketx option --ignore-unassoc leaves unassociated alpha data as it is (no auto-conversion to associated alpha) and/or ignores the tags for an input file that is associated but incorrectly tagged as unassociated alpha. * oiiotool & info: the --hash command had a bug wherein when applied to images there were MIP-mapped, would hash the lowest-res MIP level rather than the highest-res. This could result in two different images, if they happened to have the same average color, to incorrectly report the same SHA-1 hash. Note that this did NOT affect non-MIPmapped images, nor did it affect the SHA-1 hashing that occurred in maketx to allow the TextureSystem to detect duplicate textures. Release 1.0.9 (4 Sep 2012) ---------------------------- * Improve error messages when trying to open an OpenEXR image that doesn't exist or is not a valid OpenEXR file. * Make the TextureSystem work properly with MIPmapped images whose tile size is not a power of 2 (mostly back-ported from master, but with additional fixes). Release 1.0.8 (17 July 2012) ---------------------------- * Fix quantization/truncation bug that sometimes left tiny alpha holes in 8 bit images (making some alpha value that should be 255, instead 254). * TextureSystem: fix fill_channels for monochrome+alpha images to properly expand to "RRRA." Release 1.0.7 (8 July 2012) --------------------------- * Bug fix when reading vector Field3D files. * Fix input of tiled images with per-channel formats. * Add testsuite/nonwhole-tiles and testsuite/perchannel. * Bug fix when reading binary PNM files. Release 1.0.6 (12 Jun 2012) --------------------------- * Fix allocation and stride bugs in that could overrun a buffer when reading tiled images whose resolution was not a whole number of tiles. * Fix stride bugs when reading scanline images with differing data types per channel. * Fixes for FreeBSD compilation. Release 1.0.5 (3 Jun 2012) -------------------------- * Various fixes for FreeBSD/kFreeBSD systems. * Various fixes to compile with Clang 3.1 without warnings. * Fixed some DPX and Cineon bugs related to channel names. * Fixed some mangled text in the PDF documentation. * Developer goodie: TypeDesc::equivalent() tests two TypeDesc's for equality, but allows 'triples' with differing vector semantics to match. Release 1.0.4 (2 May 2012) -------------------------- * DPX fixes for 12 bit DPX and packing methods. * Cineon fixes: remove buggy 32 and 64 bit output, which wasn't needed; fix for 10 bit -> 16 bit promotion. * bmp fix: wasn't setting oiio:BitsPerSample correctly. * oiiotool fixes: improved argument help and add man page generation; print data format info correctly for non-byte bit depths; better inference of output tile size and data format from the inputs (when not explicitly requested); --resize n% was broken; print data format info correctly for non-byte bit depths. * iinfo fixes: make --stats print correctly; print data format info correctly for non-byte bit depths. * Fix roundoff error when converting from float buffers to int image files. * More precise filter normalization in ImageBufAlgo::resize (and therefore oiiotool --resize). Release 1.0.3 (16 Apr 2012) --------------------------- * Fix reading bugs in DPX and Cineon. * iconvert handles the int32 and uint32 cases. * Bug fix in to_native_rectangle, which could lead to errors in certain data format conversions. * Various Windows build fixes, including fixes for Windows 7. * Compilation fixes on FreeBSD. Release 1.0.2 (19 Mar 2012) ---------------------------- * Fixed TARGA reader bug where for 16-bpp, 4-channel images, we weren't reading the alpha properly. * Fix ill-formed default output names for maketx (and in the process, add Filesystem::replace_extension utility). * Threading performance improvement in the texture system as a result of wrapping various internal "iequals" calls to pass a static locale rather than relying on their default behavior that would use a mutex underneath to access a global locale. Release 1.0.1 (13 Mar 2012, compared to 1.0.0) ---------------------------------------------- Fixes, minor enhancements, and performance improvements: * Improvements in anisotropic texture filtering quality. * oiiotool --hash prints the SHA-1 hash of each input image. * oiiotool: properly print error message and exit when an input file cannot be opened. * Changed the default behavior of idiff and "oiiotool --diff" to print the pixel difference report only for failures (not for successful matches), unless in verbose (-v) mode. Developer goodies: * dassert.h: New ASSERTMSG and DASSERTMSG allow even more flexible assertion messages with full printf argument generality. * Windows compilation fixes. * Major testsuite overhaul: All tests are copied and run in the build/ARCH/testsuite directory, no longer leaving any clutter in the "source" testsuite area. The testing scripts have been cleaned up and greatly simplified. An individual test can be run using "make test TEST=name" (also works with regular expressions). The usual "make test" will exclude tests that are expected to be broken (such as tests for portions of the system that were not built because their required libraries were not found), but "make testall" will run all tests including nominally "broken" ones. Release 1.0 (25 Feb 2012, compared to 0.10.5) --------------------------------------------- Major new features and improvements: * New ImageInput & ImageOutput methods that can efficiently read/write multiple scanlines or tiles at a time. * New ImageInput methods that can read a subset of channels from an image. * WebP format reader/writer. * PSD (Adobe Photoshop) format reader. * RLA (Wavefront) format reader/writer. * Cineon support is re-enabled after various bug fixes. * New utility: oiiotool. This is still a work in progress, but largely subsumes the functionality of iprocess, iinfo, iconvert, idiff. * Use OpenColorIO (www.opencolorio.org) for color space conversion, if detected at build time and a valid OCIO configuration is found at runtime. Color conversion commands have been added to oiiotool and maketx. API changes: * New ImageInput & ImageOutput methods that can efficiently read/write multiple scanlines or tiles at a time: read_scanlines, read_tiles, write_scanlines, write_tiles. * New ImageInput methods that can read a subset of channels from an image. * Change the last couple functions that took min/max pixel range specifications to conform to our usual [begin,end) convention -- write_rectangle and to_native_rectangle. * exif_encode, exif_decode now available as general utilities (previously were private to the JPEG plugin). * New ImageOutput::supports() queries: "displaywindow" queries whether the file format is able to handle differing display ("full") and pixel data windows, "negativeorigin" queries whether data origin or full/display origin may be negative. * TextureSystem and ImageCache now accept attribute "options", that is a comma-separated list of name=value setings (e.g. "max_memory_MB=256,max_files=1000"). Also, upon startup, the environment variables OPENIMAGEIO_TEXTURE_OPTIONS and OPENIMAGEIO_IMAGECACHE_OPTIONS are parsed for these startup values. * TextureSystem/ImageCache: add a separate "plugin_searchpath" attribute separate from the "searchpath" for images. Fixes, minor enhancements, and performance improvements: * ImageBufAlgo new algorithms: compare, compare_Yee, isConstantChannel, fixNonFinite. * TextureOpt: add ustring-aware versions of the decode_wrapmode utility. * TypeDesc: allow stream << output. * iv: raised maximum ImageCache size from 2 GB to 8 GB. * PNM: fix bug where file exceptions could go uncaught. * Properly create coefficients for Kodak color transform. * iprocess: Fix bug calling read. * maketx new options: --opaque-detect omits alpha from texture whose input images had alpha=1 everywhere; --mipimage option allows custom MIP levels to be assembled; --fixnan repairs NaN & Inf values in the inputs. * Fixed bugs in sinc and Blackman-Harris filters. * ImageCache/TextureSystem -- new reset_stats() method resets all the statistics back to zero. * TIFF: better handling of unexpected bitsperpixel combinations; support the nonstandard use of IEEEFP/16bit as "half"; fix many small bugs related to unusual depth depths and contig/separate conversions. * JPEG-2000 plugin rewritten to use OpenJpeg library instead of Jasper. * DPX: various bug fixes. * RLA plugin overhauled and now has good support for non-8-bit depths. * oiiotool improvements: --pop, --dup, --selectmip, --origin, --incolorspace, --tocolorspace, --colorconvert. * TextureSystem supports textures with "overscan" (including proper maketx support for input images with overscan). * TS/IC invalidate_all previously cleared all fingerprint info, but now only clears fingerprints for individual files that are invalidated (this makes for better duplicate detection). Build system improvements: * Support compilation on FreeBSD. * Improved custom detection of boost-python on Windows. * Easier to compile OIIO without using TBB. Developer goodies: * ArgParse enhancements: make %! indicate a bool that's set to false if the option is found, %@ indicates an immediate callback, allow callbacks for bool options, option matching ignores characters after ':' in the option, wrap lines at word breaks when printing usage help. * Generate man pages for the command-line tools. * Strutil additions: escape_chars, unescape_chars, word_wrap. * Filesystem additions: filename(), extension(). * Sysutil additions: terminal_columns() * Use github.com/OpenImageIO/oiio-images project for test images that are too big to fit in testsuite. * Fixed bugs in Timer::lap(). * Aded 'invert' algorithm to fmath.h. * Clarify Timer docs and fix Apple-specific bug. * testtex improvements: --wrap Release 0.10.5 (20 Feb 2012) ---------------------------- * Improvements to anisotropic texture filtering: (1) fix for degenerate derivatives that could corrupt the filter footpring calculations, resulting in an infinitely long major axis; (2) more efficient subpixel filtering for very narrow anisotropic footprints when on the highest-res MIP level. Release 0.10.4 (November 20, 2011) ---------------------------------- * Important texture bug fix: Improve robustness of texture lookups with very small derivatives. The previous bug/misunderstanding had the result of some filter footprints with very small (but valid) derivatives inappropriately using the highest-resolution MIPmap level and maximum anisotropy, resulting in terrible performance, alising, and in some cases visible seams on the boundary between where this happened and where it didn't. Be aware that the fixed code will make some areas of texture look less sharp, but that's only because it was aliasing before and using a totally incorrect MIPmap level. Release 0.10.3 (November 5, 2011) --------------------------------- * New ImageCache/TextureSystem option: "autoscanline", which, when autotile is turned on, causes the virtual tiles to be the full width of the image scanlines, rather than square. This improves performance for some apps. * Bug fix: PNG files with both associated alpha and gamma correction lost precision when converting. * Bug fix: ICO and Targa did not properly force requested (but unsupported) UINT16 output to be UINT8. * maketx (and Filter classes): fixes to sinc, blackman-harris filters. * Minor Python binding bug fixes. * Allow stream << of TypeDesc. * Fix minor Timer::lap() bug. Release 0.10.2 (August 6, 2011) ------------------------------- * Improve the performance of ustring constructor when highly multithread. * Remove old out-of-date Doxygen html pages. Release 0.10.1 (August 2, 2011) ------------------------------- * Fix TextureSystem::get_texture_info(file,"exists") (and the equivalent for ImageCache), it was previously incorrectly giving an error if the file didn't exist. * Fixed an error where we were losing the error message if ImageInput::create failed. * maketx: --hash is deprecated, the SHA-1 hash is always computed; the hash takes into account upstream image changes, such as resizing; the --filter command line argument only takes the filter name, the width is now automatically computed. * Add static methods to Filter classes allowing queries about the names and vital info about all available filters. * New Filesystem::is_regular() wraps the boost is_regular and catches exceptions. * iv: raise the maximum ImageCache settable in the UI from 2GB to 8GB. * Bug fixes with per-channel data formats. * Add Strutil::escape_chars() and unescape_chars() utility functions. * TextureOpt: add ustring-aware versions of the decode_wrapmode() utility. Release 0.10 (June 9 2010) -------------------------- Major new features and improvements: * TextureSystem: fix longstanding texture quality issues: underestimation of anisotropic filter footprint, improved metric for determining when to switch to bicubic filtering, better MIP level selection for non-square images. * maketx --filter allows you to specify the filter for resizing and downsizing to generate MIPmap levels (this lets you choose a filter that is better than the default "box"). * TextureSystem option "gray_to_rgb", when set to nonzero, promotes grayscale (single channel) texture lookups to RGB (rather than using the fill color for missing channel. * IFF (Maya) support from Mikael Sundell. API changes: * TextureSystem has additional API entry points for apps that want to retrieve an opaque texture handle and per-thread info and then pass it back to texture lookups, saving some name resolution and per-thread retrieval time. (The old routines that do this all automatically still work just fine.) * New ImageBufAlgo utilities: setNumChannels, isConstantColor, isMonochrome, computePixelHashSHA1, transform. Fixes, minor enhancements, and performance improvements: * ImageCache/TextuerSystem: - option "accept_untiled" wasn't properly recognized (0.9.1); new attribute "accept_unmipped" (default to 1), when set to zero, will treat any un-MIPmapped file as an error (analogous to the existing "accept_untiled") (0.9.2); - fix deadlock when file handles are exhausted (0.9.3); invalidate_all() no longer closes all files unconditionally, only the ones actually invalidated; - fix longstanding problem where multiple threads could redundantly open and read the same file if they need it simultaneously and it isn't in cache already; - get_pixels issues a single error from a corrupt file, rather than reporting error after error on the same file. * Texture: Fixes to make latlong environment maps more correct for OpenEXR files, which have some particular unique conventions. (0.9.1); bug fix to TextureOpt default initializer that could screw up texture lookups. (0.9.1) * maketx fixes: the -oiio command line option also enables hash generation; resize properly if any of the dimensions change (previously only did if ALL dimensions changed) (0.9.3); --nchannels lets you set the number of output channels. * Added ImageBufAlgo::transform to allow for 'flip' & 'flop' in iprocess. (0.9.1) * DPX: fix file reading when number of channels not equal to 3 (0.9.3); support for endianness specification, fix lots of problems writing metadata. * BMP: RGB-to-BGR conversion fixed, force UINT8 output; scanline size was incorrect when copying to temporary buffers. * JPEG: reader is more robust to corrupted files and other problems. * JPEG-2000: support files with more than 8 bits per channel. * Targa: properly expand 5 bit per channel to full bytes. * Fixed incorrectly set "ResolutionUnit" and "BitsPerSample" usage in several format plugins. * Improved handling of file formats that allow unassociated alpha. * iv: display non-Latin filenames properly. * iconvert --noclobber option ensures that existing files aren't overwritten. * iinfo: fixes to properly print subimage and mipmap statistics. For developers / build issues: * Fix USE_TBB macro on Windows build. (0.9.1) * Fixes required for Windows compile in conjunction with OSL. (0.9.1) * Removed some pointless debugging output to the console. (0.9.1) * Fix subtle bug in convert_type utility function that was causing a slight incorrect rounding when converting float to a signed integer type. (0.9.3) * Fix to compile properly against Boost 1.46. (0.9.3) * Update pugixml from 0.5 to 1.0. * Remove boost::test and gtest as dependencies, use our own macros. * Fixes to allow use of libtiff 4.0. * make USE_JASPER=0 USE_FIELD3D=0 make it easy to disable Jasper and Field3D as dependencies. * Various fixes to make cleaner compiles with clang. * ustring: Added find* methods that match those of std::string, expose make_unique, is_unique, and from_unique helper functions. * Add Filesystem::exists and Filesystem::is_directory. Release 0.9 (Dec 9 2010, updated Feb 23, 2011) ---------------------------------------------- Major new features: * New format plugin: DPX * New format plugin: Cineon (currently read only) (r1599,1600,1601,1617) * New format plugin: Ptex (currently read only) (r1655,1664). * New format plugin: Field3D (currently read only) (r1659,1666,1669) * Support for files that are simultaneously multi-image and where each subimage may also be mipmapped (these concepts were previously comingled). This mainly effects ImageInput::seek_subimage and ImageOutput::open, as well as some minor methods of ImageCache and ImageBuf. (r1655,1656,1664,1671) * Support for per-channel data formats via the new ImageSpec::channelformats vector and interpreting read_foo/write_foo format parameter of UKNOWN as a request for the true native format. (r1674) * Full support of TextureSystem environment() for lat-long maps. API changes: * Single-point texture lookup struct (TextureOpt) and additional single-point texture lookup entry points. (r1679) * Filter{1D,2D} class now has a destroy() method to match its create(), and create() accepts "bspline" and "catrom" as synonyms for the existing "b-spline" and "catmull-rom" fileters. (r1542) * Add methods to ImageSpec to read/write an XML representation of the ImageSpec (r1574). * Finally put all the helper classes (ustring, TypeDesc, etc.) that were in the main OpenImageIO namespace, as well as centralized version numbering and custom namespace control. * ParamList now has a method to remove attributes. * Color handling change: color space now is a metadata string, "oiio:ColorSpace", not 'linearity' data member of ImageSpec; remnants of bad 'dither' ideas have been removed; "BitsPerSample" metadata has been renamed "oiio:BitsPerSample" and several bugs have been fixed related to it in some of the image plugins. * Moved some ImageBuf methods into functions in imagebufalgo.h. Fixes, minor enhancements, and performance improvements: * OpenEXR: Allow read/write with different data formats per channel (r1674). * SGI: add support for files with any number of channels (r1630). * PNG: improve PNG write speed by 4x by adjusting compression tradeoffs (r1677) * JPEG: assume sRGB unless EXIF says otherwise (r1693); fix broken JPEG 4-channel to 3-channel conversion (r1696). * PNM: monochrome data was output incorrectly in both binary & ascii forms; adopt the Netbpm convention for endianness in the 16 bit case; open binary image files in binary mode to avoid newline mangling (r1709). * TIFF: more sensible checkpointing logic greatly reduces header rewriting. * iinfo: add --stats option (r1618) * iv: Now can sort the image list by file date, metadata date, name, or file path (r1514). * ImageCache: fixed bug that allowed the max_open_files limit to be exceeded (r1657); raise the default IC cache size to 256 MB (r1663); automip unmipped files even if they are tiled (r1670); fix bug wherein an invalidated and modified file would continue to flush in subsequent invalidations, even if the file was not modified again (r1712/0.8.8). * New ImageBuf algorithm: computePixelStats (r1618) * Fixes in ImageCache and ImageBuf to allow correct handling of 3D (volumetric) files. (r1659,1660) * ImageCache fixed to ensure that multiple threads don't try to concurrently open the same file. * Properly append error messages; ASSERT if the error message buffer exceeds 16 MB (which means somebody is failing to call geterror) (1672) * Fix subtle Strutil::format and ustring::format crasher bugs with long strings (r1654 - 0.8.8). * Print the OIIO version in the ImageCache stats so we don't guess when somebody sends us a log file with complaints. * ImageCache::getattribute can retrieve its interesting internal statistics individually by name. (r1721) * idiff and iv increased their IC cache size. (r1722) * idiff bug fixes: (1) files with different number of MIPmap levels immediately failed, whereas they should have compared their top levels, and only fail if the "-a" flag was used; (2) some failure modes incorrectly printed a "PASS" message despite actually failing. (r1722) * Changed the environment variable that contains the plugin search path from IMAGEIO_LIBRARY_PATH to OPENIMAGEIO_LIBRARY_PATH. (r1723) * Bug fix to ImageInput::read_image -- could crash due to an internal buffer allocated as the wrong size. (r1724) * Bug fixes to write_image(), related to subtle stride errors. * Improved strhash, fewer ustring hash collisions. * New maketx functionality: --constant-color-detect, --monochrome-detect, --prman, --oiio (look in docs for explanation). For developers / build issues: * testtex: print memory use (r1522) * Embedded plugins are now built within the OIIO namespace, if defined (r1559). * Fixed implementation of TypeDesc construction from a string. (r1562) * Incorporate PugiXML for XML parsing/writing needs (r1569). * In-progress socket I/O plugin is in the code base, but not yet fully supported. * Disable python support if boost_python is not found. (r1701) Release 0.8 (May 26 2010) ------------------------- Major new features: * Python bindings for the ImageInput, ImageOutput, ImageSpec, ImageBuf, and ImageCache classes. * New format plugin: SGI image file * New format plugin: PNM/PPM/PGM/PBM * New format plugin: DDS (currently reading only) * New format plugin: Softimage PIC (currently reading only) API changes: * New "linearity" tags include AdobeRGB, Rec709, and KodakLog. * ColorTransfer helper class can convert among the linearity types, and may be optionally passed to convert_image and convert_types. * Added to fmath.h: sincos, exp2f, log2f * Renamed ErrHandler::ErrCode enums with EH_ prefix (to fix conflicts with some Windows headers). * ustring now has getstats() and memory() methods. Fixes, minor enhancements, and performance improvements: * ImageInput::create() error messages are more helpful. * Fixed some error messages in FITS output, iconvert. * maketx: Console flushes in status messages to that a calling process will get status messages right away. * Fix subtle ImageCache bug with invalidate(). * ImageCache/TextureSystem have improved multithreading performance when large untiled files are needed simultaneously by many threads. * TextureSystem: new 'missingcolor' texture option that, when provided, can specify a color that will be used for missing textures rather than generating errors. (If not supplied, missing tex is still an error.) * BMP plugin enhancements. * TIFF: support 64-bit float pixels, proper random scanline access emulation for all appropriate compression types, handle incorrectly set-to-zero image_full_width and image_full_height. (r1515 - 0.8.1) * PNG: properly handle palette images, unassociated alpha, gamma correction, endianness for 16-bit files, and has vastly better memory consumption due to reading scanlines individually rather than buffering the whole image (r1523 - 0.8.1); fix clamping/wrapping problem for certain values when alpha > color. (r1605 - 0.8.3) * iv fixes: fix improper recentering after image reload; fix crash when image info window opened without any image files loaded; better status window message when image reads fail; iv goes into background properly in Windows; "slide show" mode; pixel view display moves if you need to look at pixels underneath it; * ImageCache bug: previously couldn't designate a cache > 2GB (because of integer overflow issues). * ImageCache::get_image_info and TextureSystem::get_texture_info now respond to a new "exists" query that merely tests for existance of the file. (0.8.1) * ImageCache/TextureSystem fix for a threading logic bug that could potentially lead to a deadlock (and definitely led to hitting a DASSERT when compiled for DEBUG mode). (0.8.1) * maketx performance improvements: --noresize is now the default (use --resize if you really want power-of-two resizing); much better performance because it doesn't use ImageCache unless the image being converted is very large; takes advantage of multiple cores by automatically multithreading (the number of threads can be controlled by the "-t" option, with the default to use the same number of threads as hardware cores). (r1546 - 0.8.2) * Fix potential crash in read_tile for files with tiles so big that they would not fit on the stack (heap allocation used instead). (0.8.2) * OpenEXR: add support for vector metadata attributes. (r1554 - 0.8.2) * Improve TIFF open error messages. (r1570 - 0.8.3) * Make ImageCache::get_pixels() and TextureSystem::get_texels() safe for crop windows -- fill with zero outside the valid pixel data area. (r1579 - 0.8.3) * In ImageCache::attribute (and by extension, TS::attribute), only invalidate the cache if the attributes actually CHANGED. (r1582 - 0.8.3) * maketx: --checknan option double checks that no source image pixels are NaN or Inf (r1584 - 0.8.3). * Fixed crash that could result from certain XML strings embedded in TIFF headers (uncaught exception). (0.8.5) * Fixed ImageCache deadlock when using autotile. (r1631 - 0.8.6) * Fixed a longstanding performance issue with ImageCache automip, wherein an unmipped file that is larger than the cache size leads to pathological thrashing. The solution is to automatically raise the cache size to be large enough to automip the file without danger of thrashing. (r1657 - 0.8.7) For developers / build issues: * EMBEDPLUGINS=1 is now the default. This means that all the format plugins that come with OIIO are compiled into the main library, so there's no reason for users to set $IMAGEIO_LIBRARY_PATH unless they need custom format plugins not supplied with the main distribution. * Fix compiler warnings (mostly under Windows): TBB stuff, ustring, windows.h. * Option to build static libraries (with 'make LINKSTATIC=1'). * Fixes to make clean compilation with gcc-4.4.2. * Allow custom 'platform' designation in the build. * Allow custom installation destination ('make INSTALLDIR=...'). * ustring now takes half the memory (by no longer redundantly storing the characters on Linux and OS X). * Always use TBB (better performance on Windows for atomics). [0.8.2] Release 0.7 (Nov 26 2009) -------------------------- Major new features: * New format plugin: JPEG-2000 (r1050) * New format plugin: FITS (r1287 et al) * TextureSystem: two new entries to TextureOptions which allow the texture system to return the derivatives in s and t of the texture. (r1308) API changes: * Added imagespec() method to ImageCache and TextureSystem that returns a reference to the internal ImageSpec of the image. This is much more efficient than get_imagespec, but beware, the pointer is only valid until somebody calls invalidate() on the file. (r1266) * TextureOptions: eliminated the 'alpha' field. Added the dresultds and dresultdt fields. * Extend TypeDesc to include INT64 and UINT64. (r1145) Fixes, minor enhancements, and performance improvements: * Make EMBEDPLUGINS=1 the default. (0.7.1) * Improvements to the Targa plugin, bringing it into compliance with TGA 2.0 (r1163, r1297) * Fixed PNG-related crashes on 64 bit machines. (r1336) * iv improvements: support for multichannel images and different color modes (r1129), support auto use mipmap level based on zooming (r1093), correct pixelview for rotated images (r1092), fix off-by-one error with some zoom levels (r1089). * maketx: fixed problem where it was sometimes not setting the output data format to match the input data format correctly (r1290), fixed problems with writing EXR files with --nomipmap (r1286), fixed cases where data window was not the same as display window (i.e. crop or overscan). * ImageCache/TextureSystem: various threading and performance improvements. (r1188, r1211, r1288, r1299) * TS: fixed incorrect "texturetype" results of get_texture_info. (r1314) * IC/TS: fixed crasher bugs when doing get_pixels of images that had non-zero data window origin. (r1313) * IC/TS: better error messages and recovery from spooky open and read_tile failures. (r1321) * When IC/TS reads and entire (untiled) image, the file is closed afterwards. This is especially helpful on Windows where open files are locked to writing by other processes. (r1298) * HUGE speedup of ImageCache::get_image_info (and TS::get_texture_info) b replacing strcmp's with ustring == (r1281). * IC: fixed various subtle logic errors with broken files and invalidate/invalidate_all. (r1252, r1279) * IC/TS: fixed memory leak of per-thread imagecache data and subtle race conditions. (r1057, r1216, r1222, r1238) * TS: fixed problem where missing or broken textures weren't using the right fill color. (r1268) * IC: Clamp cache size to a reasonable lower limit (r1256) * TS: improvements to filter estimation (1134) and bicubic interpolation numerical stability and speed (r1166, r1179, r1333). * IC: when autotile=0 but automip=1, fixed bug that was wasting HUGE amounts of memory by using the wrong resolution for mip levels! (r1147) * IC: fix an edge case where tiles could leak. (r1044) * Fixed some hairy static initialization problems with ustring (r1280) * Use a spin lock rather than block in ustring constructor gives HUGE speedup especially on Windows. (r1167) * TS: Make everything work for textures whose image origin is not (0,0) or whose pixel data window doesn't match the image window (i.e. crop windows or overscan). (r1332) * IC/TS: Correctly invalidate files afected by recently changed "automip" setting. (r1337) * IC/TS: fix crash that could occur with non-existant textures in combination with invalidate_all(). (r1338) * Make create() error messages more helpful. (0.7.1) For developers: * Build more easily when older OpenEXR versions are found. (r1082) * HTML Doxygen documentation on the public APIs. (r1311, r1312, et al) * Sysutil::this_program_path finds the full path of the running program. (r1304) * Better compiler-side error checking of printf-like functions (r1302) * A new site/... area where important users with local build customization needs can check in (reasonably sized) custom makefiles or other helpful things. (r1284) * New ErrorHandler class, currently unused by OIIO itself, but very handy. (r1265) * Fixed lots of compiler warnings. * Upgraded to a more recent TBB, which fixed some atomic problems. (r1211) * ustring: make string comparison safe for empty strings. (r1330) * Include file fixes for gcc 4.4. (r1331) * Regularize all #include references to Imath and Openexr to ``. (r1335) Release 0.6 (Jul 20, 2009) -------------------------- Major new features: * Everything has been ported to Windows. * iv: handle older cards or versions of OpenGL, including lack of GLSL, non-pow2 textures, etc. Generally should now be usable (if slightly degraded functionality) for most OpenGL 1.x implementations. (r764) * ImageBuf that only reads images is now automatically backed by ImageCache. In the process, add Iterator and ConstIterator as "safe" and efficient ways to visit all the pixels within a region of the image, and eliminate the unsafe pixeladdr() method. Also added ImageCache::attribute("forcefloat") to conveniently force all ImageCache internal storage to be float. (r770,771,772,775,803,805) * iv can now support "big" images, in particular larger than the OpenGL texture limit (4k), and also very big images via the use of ImageCache (r912). * Truevision Targa (TGA) support. (r776,792) API changes: * In a variety of places that specified pixel rectangles (including ImageCache::get_pixels and TextureSystem::get_texels), specify regions as (xbegin,xend,ybegin,yend) rather than (xmin, ymin, xmax, ymax). Note that 'end' is, like STL, one past the last pixel. (r771) * All classes now query error messages using geterror(). Previously some used geterror() and others used error_message(). The old error_message is deprecated and will be removed in a future release (r957). Fixes and minor enhancements: * OpenEXR plugin improvements: don't set "textureformat" attribute unless it really is a mip-mapped texture. Preserve the mipmap rounding mode when copying OpenEXR files, by using the "openexr:roundingmode" metadata (r801). Properly mark the alpha and z channels in the ImageSpec (r885). * TIFF plugin improvements: handle 2 bpp images, properly name channels when reading palette images (r802), no longer uses the PREDICTOR_FLOATINGPOINT, since older versions of libtiff can't read those files (r752). Properly set the Exif sRGB marker (r888). * BMP plugin improvements: allows top-down scanlines as well as bottom-up, correctly reads 4-, 8- and 24-bit images that have scanlines padded to 4-byte boundaries. * ImageBuf algorithms: crop, add (r892). * EXPERIMENTAL: 'iprocess' utility that lets you do some simple image processing operations (r892). * ImageCache additional statistics: file open time (r743), alert if mip-mapped images are accessed at only their highest-res level (r743). Properly emulates random access reads of LZW-compressed files (r920). * iv: fix problems displaying images whose width was not a multiple of 4 bytes (r767), when loading small images, the window starts out a usable minimum size, iv always raises the window upon first opening, fix pixelview of alpha in RGB images (r939). * iv: Fix off-by-one error that drew the last scanline incorrectly sometimes (r1089). Give feedback when doing a slow repaint (r1089). * iv improvements: fix skew-like problems with Intel cards, fix non-GLSL texture mapping, limit texture size to 4096^2 to keep GL memory use reasonable make "Reload" work again after breaking a few patches ago (r1090). * maketx: in the case where the input texture was already float and needed no pow2 rounding, we didn't get the tiling or other metadata right (r824) * ImageCache and TextureSystem do a better job of reporting low-level ImageInput errors up the chain (r945). * ImageCache: new option "accept_untiled", when set to zero, will reject untiled images (r979). * 'maketx --hash' embeds a SHA-1 fingerprint of the source image's pixels in the texture file header's "ImageDescription" field. ImageCache (and TextureSystem) will recognize these and eliminate redundant I/O when it finds multiple files with identical pixels. (r741,742) * iinfo: eliminate --md5 in favor of --hash (computing SHA-1). (r749) * Fix ImageCache and TextureSystem to have thread-specific error reporting. (r1045) * TextureSystem: fixed subtle bug in destruction order that could double-free per-thread data. (r1057) * ImageCache: now get_image_info("format") returns teh native data format of the file. (r1058) * maketx: properly handle input files where the data window is not the same as the display window or if the image offset was nonzero. The correct semantics are that the DISPLAY window is what maps to [0,1] in texture space. (r1059) For developers: * Lots of fixes for Windows compilation (r754, r860) * A build option for whether or not to use TBB for atomics. (r780) * New test suite entries: tiff-suite, tiff-depths (r787,788), openexr-suite, openexr-multires, openexr-chroma (r789,790,791). * New unit tests for ImageBuf::zero, ImageBuf::fill, ImageBufAlgo::crop (r891). * Reorganization of unit tests. * Improvements to ArgParse internals and interface. * All the macros that prevent double-inclusion of header files have had their names changed from FILENAME_H to OPENIMAGEIO_FILENAME_H so that they don't conflict with other package (r967). * Reorganized test suite hierarchy. * Optionally allow the entire library to be enclosed in a versioned namespace (via 'make NAMESPACE=foo') . * Upgraded to a more recent version of TBB that seems to have fixed some bugs with atomic counters. (r1027) Release 0.5 (31 May 2009) ------------------------- Features: * New image format plugins: zfile (r529), ICO (r579,585,588,619,637), BMP (reads only) (r580,584,614,625) * Support for multiple subimages in iinfo (r607), iconvert, idiff (r631), * ImageCache and TextureSystem: better stats (r528, r717), bug fixes for large untiled images (r558,561), anisotropic improvements, stats improvements, thread safety improvements (r566), invalidate/invalidate_all (r591), better error reporting (r606), thread safety fixes (r650), fix problem when filter size was precisely at a mipmap level it blurred to higher level (r687), avoid problems when blur > 1 and there is no 1x1 mip level (r687). * maketx: --shadow (r530), --nomipmap (r531), big speedups (r699). * idiff: add RMS error and PSNR (r622). * OpenEXR plugin: support "openexr:lineOrder" attribute so random-tile-order files may be written (r569). * API: better handling of huge images that could have sizes > 32 bits (r575) Fixes and minor enhancements: * iinfo: fix - lack of help message when no files specified (r513). * maketx: make -u work properly (r517), wasn't honoring --separate (r532). * iconvert: add --separate and --contig (r535). * TIFF plugin: work around error in old versions of libtiff for IPTC packets (r674). * JPEG plugin: if linearity is sRGB, set Exif:ColorSpace correctly (r536) * iv: more robust to certain OpenGL versions (r550), support for OpenGL versions that don't support non-pow2 textures (r563), correct texture mapping when GL_NV_texture_rectangle is the best texture mapping extension we can find (r572). * idiff: refactored to use ImageBuf internally (r541) For developers: * Switch to CMake for builds. * Build enhancements: 'make USE_OPENGL=0' (r512), better handling of certain system OpenGL headers (r512), more robust with Qt location (r542), handle older Boost 1.35 (r574). * Tests: test_libOpenImageIO (r581), ico (r621), * More work towards clean windows compilation (r659,672). Release 0.4 (15 Mar 2009 - not formally released) ------------------------------------------------- (compared to the 'initial' developer release of 1 Sep 2008) Features: * Lots of work on docs. * API changes: - Replaced ParamBaseType/ParamType with TypeDesc. - ImageSpec: add full_{x,y,z} fields. - Changed ImageInput/ImageOutput create(), open(), and suports() to take std::string instead of char* (r297) - Added ImageOutput::copy_image (r428) - TypeDesc - distinguish COLOR from NOXFORM. (r466) - ImageInput:open(name,newspec,config). (r482) * igrep utility searches metadata of images (r447,455,499) * iconvert: add --caption, --keyword, --clear-keywords, --adjust-time --attrib, --inplace (r484,488,491), --compression (r354), --quality (r362). * iv: put into background after launch unless -F arg (r240), alt-leftmouse zooms, handle sRGB correctly, GAMMA env variable, full HDR for half and float (r243), honor full/display window versus data window (r300), better view re-centering behavior (r355), fix orientation bugs (r363,380,381). * TextureSystem: single point texture lookups (r247), have all routines return a bool giving error status, rename gettextureinfo -> get_texture_info, add get_imagespec, get_texels, geterror (r252,265), replace hard-coded get/set routines with generic attribute/getattribute (r321), accept non-tiled and non-mipped textures (r317,319,388,389,390), separate the image cache into a separate ImageCache class that may be used independently of TextureSystem (r326,327,393), better statistics including per-file stats (r333,360,375,429), invalidate method (r460). * TIFF plugin: read/write EXIF, IPTC IIM, and IPTC XPM/XML data (r406,407,456,458) * JPEG plugin: read/write IPTC IIM, XMP, and GPS metadata (r408,411,458,461), implement ImageOutput::copy_data() can copy images without altering pixel values (r483). Fixes and minor enhancements: * ImageBuf: add option to read() that allows data format conversion (r244), add oriented{x,y} and oriented_full_{width,height} methods (r296). * TextureSystem: fix bicubic filetering (r309), big memory savings by not having libtiff use memory mapping (r332), lots of performance tuning (r351), anisotropic texture improvements (r364), bug fixes for search paths (r459). * iinfo: print color space and file format info (r241), better printing of matrix metadata (r365), new options -f, -m (r501). * idiff: bug fix - not producing difference image (r402) * maketx: deduce default file format from extension (r275). * All format plugins: better error detection in open() for senseless resolutions (r294,295) * OpenEXR plugin: handle float as well as half data, fixes when image origin is not (0,0) (r291), fix leak of exr writer (r292), conform to common conventions for camera matrix naming (r367), regularize capitalization of metadata (r412) * TIFF plugin: bug fix for combination of tile + separate (r304), fixes to retrieval of matrix tags (r366) * HDR plugin: emulate random access of scanlines (r387), better error reporting (r451). * JPEG plugin: respect "CompressionQuality" (r361), emulate random access of scanlines (r387), properly read & write JPEG_COM marker for comments (r403), assume sRGB unless the metadata say otherwise (r414). For developers: * Preliminary work on Windows port (r398,399) * Include all the needed .h files in the dist area (r251) * Handle older gcc (r273), older boost (r301,431), older OpenEXR (r301), older libtiff (r432). * 'make EMBEDPLUGINS=1' compiles the bundled plugins right into main library (r302). * Put header files in dist/ARCH/include/OpenImageIO (r303), rename src/libimageio -> src/libOpenImageIO (r382). Initial developer release 0.1 (1 Sep 2008): --------------------------------------- * ImageInput, ImageOutput, TextureSystem APIs pretty mature * Plugins: TIFF, JPEG/JFIF, OpenEXR, PNG, HDR/rgbe * iv - basic display, multiple images, menus, status bar, info window, basic prefs window, pixel view tool, zoom, pan, open, open recent, close, save as, view subimages, view channels, gamma, exposure, fit window to image, fit image to window, full screen. * iconvert * idiff * maketx * API docs for ImageInput, ImageOutput, writing plugins * Linux and OSX openimageio-1.7.17~dfsg0.orig/site/0000755000175000017500000000000013151711064015271 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/site/spi/0000755000175000017500000000000013151711064016064 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/site/spi/Makefile-bits-arnold0000644000175000017500000002327013151711064021744 0ustar mfvmfv# SPI-specific settings ifneq (${VERBOSE},) $(info Including spi/Makefile-bits-arnold) endif # READ THIS FIRST # # If you are building for Arnold, just make. # Default namespace NAMESPACE ?= 'OpenImageIO_Arnold' MY_CMAKE_FLAGS += -DEXTRA_CPP_ARGS:STRING="-DOIIO_SPI=1" -DOIIO_SITE:STRING=SPI SPCOMP2_SHOTTREE_LOCATION = /shots/spi/home/lib/SpComp2 INSTALL_SPCOMP2_LOCATION = /shots/spi/home/lib/SpComp2 NUKE_VERSION ?= 10.0v4 CPPSTDSUFFIX= ## Detect which SPI platform and set $platform, $COMPILER, $SPCOMP2_COMPILER, ## and PYVER. Lots of other decisions are based on these. ifeq ($(SP_OS), rhel7) # Rhel7 (current) platform := rhel7 SPCOMP2_COMPILER=gcc48m64 else ifeq (${SP_OS}, lion) # Official OS X build machines with special conventions platform := lion COMPILER ?= clang SPCOMP2_COMPILER ?= clang else ifeq (${platform}, macosx) # Generic OSX machines (including LG's laptop) COMPILER ?= clang SPCOMP2_COMPILER ?= clang PYVER ?= 2.6 else $(error Unknown SP_OS) endif # endif $(SP_OS) PYVER ?= 2.7 OCIO_SPCOMP_VERSION ?= 2 ## Rhel7 (current) ifeq ($(SP_OS), rhel7) USE_CPP11 ?= 1 USE_SIMD = sse4.1 USE_NINJA ?= 1 NINJA ?= ninja CMAKE ?= cmake ifeq ($(USE_NINJA),1) MY_CMAKE_FLAGS += -DCMAKE_MAKE_PROGRAM=${NINJA} endif BOOSTVERS ?= 1.55 SPCOMP2_USE_BOOSTVERS ?= 1 OCIO_PATH ?= "${SPCOMP2_SHOTTREE_LOCATION}/OpenColorIO/${SP_OS}-${SPCOMP2_COMPILER}/v${OCIO_SPCOMP_VERSION}" FIELD3D_HOME ?= "${INSTALL_SPCOMP2_LOCATION}/Field3D/${SP_OS}-${SPCOMP2_COMPILER}-boost155sp/v408" ## If not overridden, here is our preferred LLVM installation ## (may be changed as new versions are rolled out to the facility) LLVM_DIRECTORY ?= /shots/spi/home/lib/arnold/spinux1/llvm_3.4.2 # A variety of tags can be used to try specific versions of gcc or # clang from the site-specific places we have installed them. ifeq (${COMPILER}, clang342) MY_CMAKE_FLAGS += \ -DCMAKE_C_COMPILER=/shots/spi/home/lib/arnold/spinux1/llvm_3.4.2/bin/clang \ -DCMAKE_CXX_COMPILER=/shots/spi/home/lib/arnold/spinux1/llvm_3.4.2/bin/clang++ else ifeq (${COMPILER}, gcc472) MY_CMAKE_FLAGS += \ -DCMAKE_C_COMPILER=/net/soft_scratch/apps/arnold/tools/gcc-4.7.2-test/bin/gcc \ -DCMAKE_CXX_COMPILER=/net/soft_scratch/apps/arnold/tools/gcc-4.7.2-test/bin/g++ else ifeq (${COMPILER}, gcc490) MY_CMAKE_FLAGS += \ -DCMAKE_C_COMPILER=/net/soft_scratch/apps/arnold/tools/gcc-4.9-20130512-test/bin/gcc \ -DCMAKE_CXX_COMPILER=/net/soft_scratch/apps/arnold/tools/gcc-4.9-20130512-test/bin/g++ else ifeq (${COMPILER},clang) MY_CMAKE_FLAGS += -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ else ifeq (${COMPILER},gcc) MY_CMAKE_FLAGS += -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ else ifeq (${COMPILER},) # default compiler is clang, taken from the LLVM directory MY_CMAKE_FLAGS += \ -DCMAKE_C_COMPILER=${LLVM_DIRECTORY}/bin/clang \ -DCMAKE_CXX_COMPILER=${LLVM_DIRECTORY}/bin/clang++ endif MY_CMAKE_FLAGS += \ -DOPENEXR_CUSTOM_INCLUDE_DIR=/usr/include/OpenEXR2 \ -DOPENEXR_CUSTOM_LIB_DIR=/usr/lib64/OpenEXR2 \ -DOCIO_PATH=${OCIO_PATH} \ -DFIELD3D_HOME=${FIELD3D_HOME} \ -DHDF5_CUSTOM=1 \ -DHDF5_LIBRARIES=/usr/lib64/libhdf5.so \ -DNuke_ROOT=/net/apps/rhel7/foundry/nuke${NUKE_VERSION} BOOSTVERSSP=${BOOSTVERS}sp BOOSTVERS_SUFFIX = -${shell echo ${BOOSTVERS} | sed "s/\\./_/"} BOOSTVERS_PREFIX = ${shell echo ${BOOSTVERS} | sed "s/\\./_/"}_0 CONSTRUCTED_BOOSTVERS = ${shell echo ${BOOSTVERS} | sed "s/\\./0/"}00 MY_CMAKE_FLAGS += \ -DBOOST_CUSTOM=1 \ -DBoost_VERSION=${CONSTRUCTED_BOOSTVERS} \ -DBoost_INCLUDE_DIRS=/usr/include/boost_${BOOSTVERSSP} \ -DBoost_LIBRARY_DIRS=/usr/lib64/boost_${BOOSTVERSSP} \ -Dboost_PYTHON_FOUND=1 -DPYTHONLIBS_FOUND=1 \ -DBoost_LIBRARIES:STRING="/usr/lib64/boost_${BOOSTVERSSP}/libspboost_${BOOSTVERS_PREFIX}_filesystem-gcc48-mt${BOOSTVERS_SUFFIX}.so;/usr/lib64/boost_${BOOSTVERSSP}/libspboost_${BOOSTVERS_PREFIX}_filesystem-gcc48-mt${BOOSTVERS_SUFFIX}.so;/usr/lib64/boost_${BOOSTVERSSP}/libspboost_${BOOSTVERS_PREFIX}_regex-gcc48-mt${BOOSTVERS_SUFFIX}.so;/usr/lib64/boost_${BOOSTVERSSP}/libspboost_${BOOSTVERS_PREFIX}_system-gcc48-mt${BOOSTVERS_SUFFIX}.so;/usr/lib64/boost_${BOOSTVERSSP}/libspboost_${BOOSTVERS_PREFIX}_thread-gcc48-mt${BOOSTVERS_SUFFIX}.so" \ -DBoost_PYTHON_LIBRARIES:STRING="/usr/lib64/boost_${BOOSTVERSSP}/libspboost_${BOOSTVERS_PREFIX}_python-gcc48-mt${BOOSTVERS_SUFFIX}.so" \ -DPYTHON_LIBRARIES:STRING="/usr/lib64/libpython${PYVER}.so" # FIXME: Force use of libtiff 3.9.4 because libtiff symbols are not # namespaced, and if we compile OIIO against the system default of # 4.0.x, but it's used by a Maya plugin and Maya links against the old # libtiff.so.3, we get crashes. Hopefully we can come back to this and # upgrade to libtiff 4.x later. # MY_CMAKE_FLAGS += -DTIFF_INCLUDE_DIR:STRING=/usr/include/libtiff-3.9.4 \ # -DTIFF_LIBRARIES:STRING=/usr/lib64/libtiff.so.3 # No, for now, just use libtiff I built myself, static, and try to # hide its symbols. MY_CMAKE_FLAGS += -DTIFF_INCLUDE_DIR:STRING=/usr/include \ -DTIFF_LIBRARIES:STRING=/net/soft_scratch/users/lg/tiff-4.0.3/rhel7/lib/libtiff.a MY_CMAKE_FLAGS += -DHIDE_SYMBOLS=1 MY_CMAKE_FLAGS += -DVISIBILITY_MAP_FILE:STRING="${working_dir}/site/spi/libOpenImageIO.map" MY_CMAKE_FLAGS += -DEXTRA_DSO_LINK_ARGS:STRING="-Wl,--exclude-libs,libtiff.a" # end rhel7 ## Official OS X build machines with special conventions else ifeq ($(SP_OS), lion) USE_CPP11 ?= 0 USE_SIMD = sse4.2 MACPORTS_PREFIX=/opt/local-10.7-2012-08 OCIO_PATH ?= "${SPCOMP2_SHOTTREE_LOCATION}/OpenColorIO/${SP_OS}-${SPCOMP2_COMPILER}/v${OCIO_SPCOMP_VERSION}" FIELD3D_HOME ?= "${INSTALL_SPCOMP2_LOCATION}/Field3D/${SP_OS}-${SPCOMP2_COMPILER}-boost151/v306" # CMAKE_CXX_COMPILER: # otherwise g++ is used and SPI standardized on clang. # CMAKE_INSTALL_NAME_DIR: # without libraries and applications linking against this # library will not be able to find it at run time; prepends # "@rpath/" to the internal ID # OPENJPEG_HOME: # Setting THIRD_PARTY_TOOLS_HOME isn't sufficient # OVERRIDE_SHARED_LIBRARY_SUFFIX: # Set to .so so that shared libraries end in .so to be consistent # with Linux SpComp2 shared libraries # PYTHON_EXECUTABLE: # PYTHON_INCLUDE_DIR: # PYTHON_LIBRARY: # Use MacPorts' Python # THIRD_PARTY_TOOLS_HOME: # Find everything in SPI's MacPorts. # ZLIB_ROOT: # Needed to use SPI's MacPorts' zlib. MY_CMAKE_FLAGS += \ -DOCIO_PATH="${OCIO_PATH}" \ -DFIELD3D_HOME=${FIELD3D_HOME} \ -DOPENJPEG_HOME=${MACPORTS_PREFIX} \ -DOVERRIDE_SHARED_LIBRARY_SUFFIX=.so \ -DPYTHON_EXECUTABLE=${MACPORTS_PREFIX}/bin/python${PYVER} \ -DPYTHON_INCLUDE_DIR=${MACPORTS_PREFIX}/Library/Frameworks/Python.framework/Versions/2.7/Headers \ -DPYTHON_LIBRARIES=${MACPORTS_PREFIX}/Library/Frameworks/Python.framework/Versions/2.7/Python \ -DTHIRD_PARTY_TOOLS_HOME=${MACPORTS_PREFIX} \ -DZLIB_ROOT=${MACPORTS_PREFIX} MY_CMAKE_FLAGS += \ -DCMAKE_INSTALL_NAME_DIR="${working_dir}/dist/${SP_OS}${variant}/lib" ifeq (${SPCOMP2_USE_BOOSTVERS}, 1) BOOSTVERS_UNDERSCORE = ${shell echo ${BOOSTVERS} | sed "s/\\./_/"} MY_CMAKE_FLAGS += \ -DBOOST_CUSTOM=1 \ -DBoost_INCLUDE_DIRS=/usr/include/boost_${BOOSTVERS} \ -DBoost_LIBRARY_DIRS=/usr/lib64/boost_${BOOSTVERS} \ -DBoost_LIBRARIES=optimized;/usr/lib64/boost_${BOOSTVERS}/libboost_filesystem-gcc44-mt-${BOOSTVERS_UNDERSCORE}.so;optimized;/usr/lib64/boost_${BOOSTVERS}/libboost_filesystem-gcc44-mt-${BOOSTVERS_UNDERSCORE}.so;optimized;/usr/lib64/boost_${BOOSTVERS}/libboost_regex-gcc44-mt-${BOOSTVERS_UNDERSCORE}.so;optimized;/usr/lib64/boost_${BOOSTVERS}/libboost_system-gcc44-mt-${BOOSTVERS_UNDERSCORE}.so;optimized;/usr/lib64/boost_${BOOSTVERS}/libboost_thread-gcc44-mt-${BOOSTVERS_UNDERSCORE}.so endif # end SPI lion ## Generic OSX machines (including LG's laptop) else ifeq (${platform}, macosx) USE_SIMD = sse4.2 MY_CMAKE_FLAGS += \ -DCMAKE_INSTALL_NAME_DIR="${working_dir}/dist/${platform}${variant}/lib" # don't need this? -DBUILD_WITH_INSTALL_RPATH=1 # A variety of tags can be used to try specific versions of gcc or # clang from the site-specific places we have installed them. ifeq (${COMPILER}, gcc5) MY_CMAKE_FLAGS += \ -DCMAKE_C_COMPILER=/usr/local/bin/gcc-5 -DCMAKE_CXX_COMPILER=/usr/local/bin/g++-5 else ifeq (${COMPILER}, gcc6) MY_CMAKE_FLAGS += \ -DCMAKE_C_COMPILER=/usr/local/bin/gcc-6 -DCMAKE_CXX_COMPILER=/usr/local/bin/g++-6 else ifeq (${COMPILER},clang) MY_CMAKE_FLAGS += -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ else ifeq (${COMPILER},gcc) MY_CMAKE_FLAGS += -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ else ifeq (${COMPILER},) # default compiler is clang, taken from the LLVM directory MY_CMAKE_FLAGS += \ -DCMAKE_C_COMPILER=${LLVM_DIRECTORY}/bin/clang \ -DCMAKE_CXX_COMPILER=${LLVM_DIRECTORY}/bin/clang++ endif # end generic OSX else $(error Unknown SP_OS) endif # endif $(SP_OS) SPARCH=$(SP_OS)-$(SPCOMP2_COMPILER) ifdef SPCOMP2_USE_BOOSTVERS ifneq ($(BOOSTVERS),1.42) SPARCH=$(SP_OS)-$(SPCOMP2_COMPILER)-boost$(subst .,,$(BOOSTVERS)) endif endif PYVERNOSPACE=${shell echo ${PYVER} | sed "s/\\.//"} SPPYARCH=$(SPARCH)-py$(PYVERNOSPACE) all: dist .PHONY: spcomp2_install spcomp2_install_local clean all comma:= , empty:= space:= $(empty) $(empty) local: dist openimageio-1.7.17~dfsg0.orig/site/spi/libOpenImageIO.map0000644000175000017500000000006613151711064021350 0ustar mfvmfv{ global: *; local: TIFF*; _TIFF*; _tiff*; }; openimageio-1.7.17~dfsg0.orig/site/spi/Makefile-bits0000644000175000017500000000123313151711064020462 0ustar mfvmfv# SPI-specific settings ifneq (${VERBOSE},) $(info Including spi/Makefile-bits) endif use_spcomp2_bits := 0 # Is the string literal 'spcomp2' in the command goals? # If so, use the spcomp2 makefile instead of arnold. ifneq (,$(findstring spcomp2,$(MAKECMDGOALS))) use_spcomp2_bits := 1 # Export a variable that can be checked for in sub-makes to know # to stay in spcomp2 mode. export TARGETING_SPCOMP2 = 1 endif ifeq (1,$(TARGETING_SPCOMP2)) use_spcomp2_bits := 1 endif ifeq (1,$(use_spcomp2_bits)) include ${working_dir}/site/spi/Makefile-bits-spcomp2 else include ${working_dir}/site/spi/Makefile-bits-arnold endif MY_CMAKE_FLAGS += -DSPI_TESTS=1 openimageio-1.7.17~dfsg0.orig/site/spi/Makefile-bits-spcomp20000644000175000017500000003763413151711064022061 0ustar mfvmfv# SPI-specific settings ifneq (${VERBOSE},) $(info Including spi/Makefile-bits-spcomp2) endif # READ THIS FIRST # # If you are building for installation as an SpComp2, make like this: # 1) On Linux, you must be setshot, for patchelf to be found; on # Mac OS X, no need to be setshot, just make sure SP_OS=lion and # COMPILER=clang. # 2) make spcomp2_install_local # or 2) make spcomp2_install # # Other notes about the SpComp2: # - it also tries to install OIIO's binaries alongside the libs. It would # be really nice if somebody would either appwrap those binaries or # symlink them to an spi show path on each install. For the moment # though, they are just there for people who know. # - it would also be nice if we had a convention for tagging SpComp2 # release versions on the svn server. # OPENIMAGEIO_SPCOMP2_VERSION=41 # Default namespace NAMESPACE ?= 'OpenImageIO_SPI' MY_CMAKE_FLAGS += -DEXTRA_CPP_ARGS:STRING="-DOIIO_SPI=1" -DOIIO_SITE:STRING=SPI SOVERSION ?= ${OPENIMAGEIO_SPCOMP2_VERSION} SPCOMP2_SHOTTREE_LOCATION = /shots/spi/home/lib/SpComp2 INSTALL_SPCOMP2_LOCATION = /shots/spi/home/lib/SpComp2 NUKE_VERSION ?= 10.0v4 CPPSTDSUFFIX= ## Detect which SPI platform and set $platform, $COMPILER, $SPCOMP2_COMPILER, ## and PYVER. Lots of other decisions are based on these. ifeq ($(SP_OS), rhel7) # Rhel7 (current) platform := rhel7 SPCOMP2_COMPILER ?= gcc48m64 # On RHEL7, we need to make a variant that disables C++11 to link # cleanly with Maya 2015, which is built against gcc 4.1, ugh. ifeq ($(USE_CPP11),0) CPPSTDSUFFIX=-c++03 endif else ifeq (${SP_OS}, lion) # Official OS X build machines with special conventions platform := lion COMPILER ?= clang SPCOMP2_COMPILER ?= clang else ifeq (${platform}, macosx) # Generic OSX machines (including LG's laptop) COMPILER ?= clang SPCOMP2_COMPILER ?= clang PYVER ?= 2.6 else $(error Unknown SP_OS) endif # endif $(SP_OS) PYVER ?= 2.7 OCIO_SPCOMP_VERSION ?= 2 OIIO_SPCOMP2_PATH ?= ${SPCOMP2_SHOTTREE_LOCATION}/OpenImageIO/${SP_OS}-${SPCOMP2_COMPILER}$(CPPSTDSUFFIX)/v${OPENIMAGEIO_SPCOMP2_VERSION} ## Rhel7 (current) ifeq ($(SP_OS), rhel7) USE_CPP11 ?= 1 USE_SIMD = sse4.1 USE_NINJA ?= 1 NINJA ?= ninja CMAKE ?= cmake ifeq ($(USE_NINJA),1) MY_CMAKE_FLAGS += -DCMAKE_MAKE_PROGRAM=${NINJA} endif BOOSTVERS ?= 1.55 SPCOMP2_USE_BOOSTVERS ?= 1 BOOSTSPSUFFIX ?= sp BOOSTVERSSP=${BOOSTVERS}${BOOSTSPSUFFIX} BOOSTVERS_SUFFIX = -${shell echo ${BOOSTVERS} | sed "s/\\./_/"} BOOSTVERS_PREFIX = ${shell echo ${BOOSTVERS} | sed "s/\\./_/"}_0 CONSTRUCTED_BOOSTVERS = ${shell echo ${BOOSTVERS} | sed "s/\\./0/"}00 SPCOMP2_BOOSTVERS_SUFFIX = ${shell echo ${BOOSTVERSSP} | sed "s/\\.//"} # $(info BOOSTVERSSP ${BOOSTVERSSP}) # $(info BOOSTVERS_SUFFIX ${BOOSTVERS_SUFFIX}) # $(info BOOSTVERS_PREFIX ${BOOSTVERS_PREFIX}) # $(info CONSTRUCTED_BOOSTVERS ${CONSTRUCTED_BOOSTVERS}) # $(info SPCOMP2_BOOSTVERS_SUFFIX ${SPCOMP2_BOOSTVERS_SUFFIX}) OCIO_PATH ?= ${SPCOMP2_SHOTTREE_LOCATION}/OpenColorIO/${SP_OS}-${SPCOMP2_COMPILER}/v${OCIO_SPCOMP_VERSION} # FIELD3D_HOME ?= ${SPCOMP2_SHOTTREE_LOCATION}/Field3D/${SP_OS}-${SPCOMP2_COMPILER}-boost155${BOOSTSPSUFFIX}/v408 OIIO_SPCOMP2_PATH := ${SPCOMP2_SHOTTREE_LOCATION}/OpenImageIO/${SP_OS}-${SPCOMP2_COMPILER}$(CPPSTDSUFFIX)-boost${SPCOMP2_BOOSTVERS_SUFFIX}/v${OPENIMAGEIO_SPCOMP2_VERSION} $(info New rhel7 OIIO_SPCOMP2_PATH is ${OIIO_SPCOMP2_PATH}) USE_OPENCV ?= 0 ## If not overridden, here is our preferred LLVM installation ## (may be changed as new versions are rolled out to the facility) LLVM_DIRECTORY ?= /shots/spi/home/lib/arnold/spinux1/llvm_3.4.2 # A variety of tags can be used to try specific versions of gcc or # clang from the site-specific places we have installed them. ifeq (${COMPILER}, clang342) MY_CMAKE_FLAGS += \ -DCMAKE_C_COMPILER=/shots/spi/home/lib/arnold/spinux1/llvm_3.4.2/bin/clang \ -DCMAKE_CXX_COMPILER=/shots/spi/home/lib/arnold/spinux1/llvm_3.4.2/bin/clang++ else ifeq (${COMPILER}, gcc472) MY_CMAKE_FLAGS += \ -DCMAKE_C_COMPILER=/net/soft_scratch/apps/arnold/tools/gcc-4.7.2-test/bin/gcc \ -DCMAKE_CXX_COMPILER=/net/soft_scratch/apps/arnold/tools/gcc-4.7.2-test/bin/g++ else ifeq (${COMPILER}, gcc490) MY_CMAKE_FLAGS += \ -DCMAKE_C_COMPILER=/net/soft_scratch/apps/arnold/tools/gcc-4.9-20130512-test/bin/gcc \ -DCMAKE_CXX_COMPILER=/net/soft_scratch/apps/arnold/tools/gcc-4.9-20130512-test/bin/g++ else ifeq (${COMPILER},clang) MY_CMAKE_FLAGS += -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ else ifeq (${COMPILER},gcc) MY_CMAKE_FLAGS += -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ else ifeq (${COMPILER},) # default compiler is clang, taken from the LLVM directory MY_CMAKE_FLAGS += \ -DCMAKE_C_COMPILER=${LLVM_DIRECTORY}/bin/clang \ -DCMAKE_CXX_COMPILER=${LLVM_DIRECTORY}/bin/clang++ endif MY_CMAKE_FLAGS += \ -DOPENEXR_CUSTOM_INCLUDE_DIR=/usr/include/OpenEXR2 \ -DOPENEXR_CUSTOM_LIB_DIR=/usr/lib64/OpenEXR2 \ -DOCIO_PATH=${OCIO_PATH} \ -DFIELD3D_HOME=${FIELD3D_HOME} \ -DHDF5_CUSTOM=1 \ -DHDF5_LIBRARIES=/usr/lib64/libhdf5.so \ -DNuke_ROOT=/net/apps/rhel7/foundry/nuke${NUKE_VERSION} MY_CMAKE_FLAGS += \ -DBOOST_CUSTOM=1 \ -DBoost_VERSION=${CONSTRUCTED_BOOSTVERS} \ -DBoost_INCLUDE_DIRS=/usr/include/boost_${BOOSTVERSSP} \ -DBoost_LIBRARY_DIRS=/usr/lib64/boost_${BOOSTVERSSP} \ -Dboost_PYTHON_FOUND=1 -DPYTHONLIBS_FOUND=1 \ -DPYTHON_LIBRARIES:STRING="/usr/lib64/libpython${PYVER}.so" ifeq (${BOOSTSPSUFFIX}, sp) MY_CMAKE_FLAGS += \ -DBoost_LIBRARIES:STRING="/usr/lib64/boost_${BOOSTVERSSP}/lib${BOOSTSPSUFFIX}boost_${BOOSTVERS_PREFIX}_filesystem-gcc48-mt${BOOSTVERS_SUFFIX}.so;/usr/lib64/boost_${BOOSTVERSSP}/lib${BOOSTSPSUFFIX}boost_${BOOSTVERS_PREFIX}_filesystem-gcc48-mt${BOOSTVERS_SUFFIX}.so;/usr/lib64/boost_${BOOSTVERSSP}/lib${BOOSTSPSUFFIX}boost_${BOOSTVERS_PREFIX}_regex-gcc48-mt${BOOSTVERS_SUFFIX}.so;/usr/lib64/boost_${BOOSTVERSSP}/lib${BOOSTSPSUFFIX}boost_${BOOSTVERS_PREFIX}_system-gcc48-mt${BOOSTVERS_SUFFIX}.so;/usr/lib64/boost_${BOOSTVERSSP}/lib${BOOSTSPSUFFIX}boost_${BOOSTVERS_PREFIX}_thread-gcc48-mt${BOOSTVERS_SUFFIX}.so" \ -DBoost_PYTHON_LIBRARIES:STRING="/usr/lib64/boost_${BOOSTVERSSP}/lib${BOOSTSPSUFFIX}boost_${BOOSTVERS_PREFIX}_python-gcc48-mt${BOOSTVERS_SUFFIX}.so" else MY_CMAKE_FLAGS += \ -DBoost_LIBRARIES:STRING="/usr/lib64/boost_${BOOSTVERSSP}/lib${BOOSTSPSUFFIX}boost_filesystem-gcc48-mt${BOOSTVERS_SUFFIX}.so;/usr/lib64/boost_${BOOSTVERSSP}/lib${BOOSTSPSUFFIX}boost_filesystem-gcc48-mt${BOOSTVERS_SUFFIX}.so;/usr/lib64/boost_${BOOSTVERSSP}/lib${BOOSTSPSUFFIX}boost_regex-gcc48-mt${BOOSTVERS_SUFFIX}.so;/usr/lib64/boost_${BOOSTVERSSP}/lib${BOOSTSPSUFFIX}boost_system-gcc48-mt${BOOSTVERS_SUFFIX}.so;/usr/lib64/boost_${BOOSTVERSSP}/lib${BOOSTSPSUFFIX}boost_thread-gcc48-mt${BOOSTVERS_SUFFIX}.so" \ -DBoost_PYTHON_LIBRARIES:STRING="/usr/lib64/boost_${BOOSTVERSSP}/lib${BOOSTSPSUFFIX}boost_python-gcc48-mt${BOOSTVERS_SUFFIX}.so" endif ifneq (${USE_TIFF3},) # FIXME: for the sake of Maya 2015 (and 2016, and 2017, ugh!), we # need to link against old libtiff 3.9.4, not the system default # 4.0.x, to avoid runtime problems when the SpComp2 OIIO is used # by Maya plugins. We only need to do this for the set of libs we # build with BOOSTSPSUFFIX == "sp", since that's the only one used # from within Maya plugins. MY_CMAKE_FLAGS += -DTIFF_INCLUDE_DIR:STRING=/usr/include/libtiff-3.9.4 \ -DTIFF_LIBRARIES:STRING=/usr/lib64/libtiff.so.3 endif # No, for now, just use libtiff I built myself, static, and try to # hide its symbols. MY_CMAKE_FLAGS += -DTIFF_INCLUDE_DIR:STRING=/usr/include \ -DTIFF_LIBRARIES:STRING=/net/soft_scratch/users/lg/tiff-4.0.3/rhel7/lib/libtiff.a MY_CMAKE_FLAGS += -DHIDE_SYMBOLS=1 MY_CMAKE_FLAGS += -DVISIBILITY_MAP_FILE:STRING="${working_dir}/site/spi/libOpenImageIO.map" MY_CMAKE_FLAGS += -DEXTRA_DSO_LINK_ARGS:STRING="-Wl,--exclude-libs,libtiff.a" # end rhel7 ## Official OS X build machines with special conventions else ifeq ($(SP_OS), lion) USE_CPP11 ?= 0 USE_SIMD = sse4.2 MACPORTS_PREFIX=/opt/local-10.7-2012-08 OCIO_PATH ?= "${SPCOMP2_SHOTTREE_LOCATION}/OpenColorIO/${SP_OS}-${SPCOMP2_COMPILER}/v${OCIO_SPCOMP_VERSION}" FIELD3D_HOME ?= "${INSTALL_SPCOMP2_LOCATION}/Field3D/${SP_OS}-${SPCOMP2_COMPILER}-boost151/v306" # CMAKE_CXX_COMPILER: # otherwise g++ is used and SPI standardized on clang. # CMAKE_INSTALL_NAME_DIR: # without libraries and applications linking against this # library will not be able to find it at run time; prepends # "@rpath/" to the internal ID # OPENJPEG_HOME: # Setting THIRD_PARTY_TOOLS_HOME isn't sufficient # OVERRIDE_SHARED_LIBRARY_SUFFIX: # Set to .so so that shared libraries end in .so to be consistent # with Linux SpComp2 shared libraries # PYTHON_EXECUTABLE: # PYTHON_INCLUDE_DIR: # PYTHON_LIBRARY: # Use MacPorts' Python # THIRD_PARTY_TOOLS_HOME: # Find everything in SPI's MacPorts. # ZLIB_ROOT: # Needed to use SPI's MacPorts' zlib. MY_CMAKE_FLAGS += \ -DOCIO_PATH="${OCIO_PATH}" \ -DFIELD3D_HOME=${FIELD3D_HOME} \ -DOPENJPEG_HOME=${MACPORTS_PREFIX} \ -DOVERRIDE_SHARED_LIBRARY_SUFFIX=.so \ -DPYTHON_EXECUTABLE=${MACPORTS_PREFIX}/bin/python${PYVER} \ -DPYTHON_INCLUDE_DIR=${MACPORTS_PREFIX}/Library/Frameworks/Python.framework/Versions/2.7/Headers \ -DPYTHON_LIBRARIES=${MACPORTS_PREFIX}/Library/Frameworks/Python.framework/Versions/2.7/Python \ -DTHIRD_PARTY_TOOLS_HOME=${MACPORTS_PREFIX} \ -DZLIB_ROOT=${MACPORTS_PREFIX} MY_CMAKE_FLAGS += \ -DCMAKE_CXX_COMPILER=/usr/bin/clang++ \ -DCMAKE_INSTALL_NAME_DIR=@rpath ifeq (${SPCOMP2_USE_BOOSTVERS}, 1) BOOSTVERS_UNDERSCORE = ${shell echo ${BOOSTVERS} | sed "s/\\./_/"} MY_CMAKE_FLAGS += \ -DBOOST_CUSTOM=1 \ -DBoost_INCLUDE_DIRS=/usr/include/boost_${BOOSTVERS} \ -DBoost_LIBRARY_DIRS=/usr/lib64/boost_${BOOSTVERS} \ -DBoost_LIBRARIES=optimized;/usr/lib64/boost_${BOOSTVERS}/libboost_filesystem-gcc44-mt-${BOOSTVERS_UNDERSCORE}.so;optimized;/usr/lib64/boost_${BOOSTVERS}/libboost_filesystem-gcc44-mt-${BOOSTVERS_UNDERSCORE}.so;optimized;/usr/lib64/boost_${BOOSTVERS}/libboost_regex-gcc44-mt-${BOOSTVERS_UNDERSCORE}.so;optimized;/usr/lib64/boost_${BOOSTVERS}/libboost_system-gcc44-mt-${BOOSTVERS_UNDERSCORE}.so;optimized;/usr/lib64/boost_${BOOSTVERS}/libboost_thread-gcc44-mt-${BOOSTVERS_UNDERSCORE}.so endif # end SPI lion ## Generic OSX machines (including LG's laptop) else ifeq (${platform}, macosx) USE_SIMD = sse4.2 MY_CMAKE_FLAGS += \ -DCMAKE_INSTALL_NAME_DIR="${working_dir}/dist/${platform}${variant}/lib" # don't need this? -DBUILD_WITH_INSTALL_RPATH=1 # A variety of tags can be used to try specific versions of gcc or # clang from the site-specific places we have installed them. ifeq (${COMPILER}, gcc5) MY_CMAKE_FLAGS += \ -DCMAKE_C_COMPILER=/usr/local/bin/gcc-5 -DCMAKE_CXX_COMPILER=/usr/local/bin/g++-5 else ifeq (${COMPILER}, gcc6) MY_CMAKE_FLAGS += \ -DCMAKE_C_COMPILER=/usr/local/bin/gcc-6 -DCMAKE_CXX_COMPILER=/usr/local/bin/g++-6 else ifeq (${COMPILER},clang) MY_CMAKE_FLAGS += -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ else ifeq (${COMPILER},gcc) MY_CMAKE_FLAGS += -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ else ifeq (${COMPILER},) # default compiler is clang, taken from the LLVM directory MY_CMAKE_FLAGS += \ -DCMAKE_C_COMPILER=${LLVM_DIRECTORY}/bin/clang \ -DCMAKE_CXX_COMPILER=${LLVM_DIRECTORY}/bin/clang++ endif # end generic OSX else $(error Unknown SP_OS) endif # endif $(SP_OS) SPARCH=$(SP_OS)-$(SPCOMP2_COMPILER)$(CPPSTDSUFFIX) BOOSTVERSSP?=${BOOSTVERS}${BOOSTSPSUFFIX} ifdef SPCOMP2_USE_BOOSTVERS ifneq ($(BOOSTVERS),1.42) SPARCH=$(SP_OS)-$(SPCOMP2_COMPILER)$(CPPSTDSUFFIX)-boost$(subst .,,$(BOOSTVERSSP)) endif endif PYVERNOSPACE=${shell echo ${PYVER} | sed "s/\\.//"} SPPYARCH=$(SPARCH)-py$(PYVERNOSPACE) all: dist .PHONY: spcomp2_install spcomp2_install_local clean all comma:= , empty:= space:= $(empty) $(empty) INSTALL_BIN_LOCATION = /shots/spi/home/bin/$(SP_OS) INSTALL_SPCOMP2_CURRENT = $(INSTALL_SPCOMP2_LOCATION)/OpenImageIO/$(SPARCH)/v$(OPENIMAGEIO_SPCOMP2_VERSION) SPCOMP2_RPATH_OPT ?= ${OCIO_PATH}/lib:${FIELD3D_HOME}/lib SPCOMP2_RPATH_DEBUG ?= ${OCIO_PATH}/lib/debug:${FIELD3D_HOME}/lib/debug PYSPCOMP2_RPATH_OPT ?= ${SPCOMP2_RPATH_OPT}:${OIIO_SPCOMP2_PATH}/lib PYSPCOMP2_RPATH_DEBUG ?= ${SPCOMP2_RPATH_DEBUG}:${OIIO_SPCOMP2_PATH}/lib/debug spcomp2_install_local: INSTALL_SPCOMP2_LOCATION = $(SPCOMP2_LOCAL_PATH) spcomp2_install_local: INSTALL_BIN_LOCATION = $(INSTALL_SPCOMP2_CURRENT)/bin spcomp2_install_local: spcomp2_install local: dist spcomp2: MY_CMAKE_FLAGS += \ -DCMAKE_SKIP_BUILD_RPATH:BOOL=ON \ -DCMAKE_INSTALL_RPATH=$(INSTALL_SPCOMP2_LOCATION)/OpenImageIO/$(SPARCH)/v$(OPENIMAGEIO_SPCOMP2_VERSION)/lib \ -DCMAKE_INSTALL_RPATH_USE_LINK_PATH:BOOL=ON \ -DPYLIB_INCLUDE_SONAME:BOOL=ON \ -DPYLIB_LIB_PREFIX:BOOL=ON spcomp2: dist spcomp2_debug: MY_CMAKE_FLAGS += \ -DCMAKE_SKIP_BUILD_RPATH:BOOL=ON \ -DCMAKE_INSTALL_RPATH=$(INSTALL_SPCOMP2_LOCATION)/OpenImageIO/$(SPARCH)/v$(OPENIMAGEIO_SPCOMP2_VERSION)/lib \ -DCMAKE_INSTALL_RPATH_USE_LINK_PATH:BOOL=ON \ -DPYLIB_INCLUDE_SONAME:BOOL=ON \ -DPYLIB_LIB_PREFIX:BOOL=ON spcomp2_debug: debug spcomp2_install_fixup_lion: spcomp2 spcomp2_debug echo $(INSTALL_SPCOMP2_LOCATION) install_name_tool -add_rpath ${OCIO_PATH}/lib ${dist_dir}/python/libPyOpenImageIO.so install_name_tool -add_rpath ${OIIO_SPCOMP2_PATH}/lib ${dist_dir}/python/libPyOpenImageIO.so install_name_tool -add_rpath ${OCIO_PATH}/lib/debug ${dist_dir}.debug/python/libPyOpenImageIO.so install_name_tool -add_rpath ${OIIO_SPCOMP2_PATH}/lib/debug ${dist_dir}.debug/python/libPyOpenImageIO.so spcomp2_install_fixup_spinux1: spcomp2 spcomp2_debug echo $(INSTALL_SPCOMP2_LOCATION) patchelf --set-rpath ${SPCOMP2_RPATH_OPT} ${dist_dir}/lib/libOpenImageIO.so patchelf --set-rpath ${SPCOMP2_RPATH_DEBUG} ${dist_dir}.debug/lib/libOpenImageIO.so patchelf --set-rpath ${PYSPCOMP2_RPATH_OPT} ${dist_dir}/python/libPyOpenImageIO.so patchelf --set-rpath ${PYSPCOMP2_RPATH_DEBUG} ${dist_dir}.debug/python/libPyOpenImageIO.so spcomp2_install_fixup_rhel7: spcomp2 spcomp2_debug echo $(INSTALL_SPCOMP2_LOCATION) patchelf --set-rpath ${SPCOMP2_RPATH_OPT} ${dist_dir}/lib/libOpenImageIO.so patchelf --set-rpath ${SPCOMP2_RPATH_DEBUG} ${dist_dir}.debug/lib/libOpenImageIO.so patchelf --set-rpath ${PYSPCOMP2_RPATH_OPT} ${dist_dir}/python/libPyOpenImageIO.so patchelf --set-rpath ${PYSPCOMP2_RPATH_DEBUG} ${dist_dir}.debug/python/libPyOpenImageIO.so # This goal can't start with 'install' because something elsewhere picks # it up and starts doing bad things spcomp2_install: spcomp2_install_fixup_$(SP_OS) perl -I/usr/local/spi/lib/make /usr/local/spi/bin/spcomp_install.pl -m installhost \ --project=OpenImageIO \ --version=$(OPENIMAGEIO_SPCOMP2_VERSION) \ --root=$(INSTALL_SPCOMP2_LOCATION) \ --arch=$(SPARCH) \ --headers=$(subst $(space),$(comma),$(wildcard ${dist_dir}/include/OpenImageIO/*)) \ --cflags= --static_lflags="$(LINK_BOOST) $(LINK_OPENEXR) -lpthread" \ --namespace=${NAMESPACE} \ --srcdir=${dist_dir}/lib \ --builddir_o=${dist_dir}/lib \ --builddir_d=${dist_dir}.debug/lib perl -I/usr/local/spi/lib/make /usr/local/spi/bin/spcomp_install.pl -m installhost \ --project=PyOpenImageIO \ --version=$(OPENIMAGEIO_SPCOMP2_VERSION) \ --root=$(INSTALL_SPCOMP2_LOCATION) \ --arch=$(SPPYARCH) \ --cflags= --static_lflags="$(LINK_BOOST) $(LINK_OPENEXR) -lpthread"\ --namespace=${NAMESPACE} \ --srcdir=${dist_dir}/python \ --builddir_o=${dist_dir}/python \ --builddir_d=${dist_dir}.debug/python openimageio-1.7.17~dfsg0.orig/CONTRIBUTING.md0000644000175000017500000002767513151711064016577 0ustar mfvmfvContributing to OpenImageIO =========================== Code contributions to OpenImageIO are always welcome. That's a big part of why it's an open source project. Please review this document to get a briefing on our process. Mail Lists ---------- There are two mail lists associated with OpenImageIO development: * [oiio-dev](http://lists.openimageio.org/listinfo.cgi/oiio-dev-openimageio.org) For developers of the OpenImageIO code itself, or users who are really interested in the OIIO internals. This is where we discuss the code (including bug reports). * [oiio-announce](http://lists.openimageio.org/listinfo.cgi/oiio-announce-openimageio.org) For announcements about major OIIO releases or other important news. You can sign up for these mail lists on your own using the links above. Bug Reports and Issue Tracking ------------------------------ We use GitHub's issue tracking system for bugs and enhancements: https://github.com/OpenImageIO/oiio/issues **If you are merely asking a question ("how do I...")**, please do not file an issue, but instead ask the question on the [OIIO developer mail list](http://lists.openimageio.org/listinfo.cgi/oiio-dev-openimageio.org). If you are submitting a bug report, please be sure to note which version of OIIO you are using, on what platform (OS/version, which compiler you used, and any special build flags or other unusual environmental issues). Please give an account of * what you tried * what happened * what you expected to happen instead with enough detail that others can reproduce the problem. Contributor License Agreement (CLA) and Intellectual Property ------------------------------------------------------------- To protect the project -- and the contributors! -- we do require a Contributor License Agreement (CLA) for anybody submitting substantial changes. Trivial changes (such as an alteration to the Makefiles, a one-line bug fix, etc.) may be accepted without a CLA, at the sole discretion of the project leader, but anything complex needs a CLA. This is for your own safety. * If you are an individual writing the code on your own time and you're SURE you are the sole owner of any intellectual property you contribute, use the [Individual CLA](https://github.com/OpenImageIO/oiio/blob/master/src/doc/CLA-INDIVIDUAL). * If you are writing the code as part of your job, or if there is any possibility that your employers might think they own any intellectual property you create, then you should use the [Corporate CLA](https://github.com/OpenImageIO/oiio/blob/master/src/doc/CLA-CORPORATE). Download the appropriate CLA from the links above (or find them in the src/doc directory of the software distribution), print, sign, and rescan it (or just add a digital signature directly), and email it back to us (info@openimageio.org). Our CLA's are identical to those used by Apache and many other open source projects. Pull Requests and Code Review ----------------------------- The best way to submit changes is via GitHub Pull Request. GitHub has a [Pull Request Howto](https://help.github.com/articles/using-pull-requests/). All code must be formally reviewed before being merged into the official repository. The protocol is like this: 1. Get a GitHub account, fork OpenImageIO/oiio to create your own repository on GitHub, and then clone it to get a repository on your local machine. 2. Edit, compile, and test your changes. 3. Push your changes to your fork (each unrelated pull request to a separate "topic branch", please). 4. Make a "pull request" on GitHub for your patch. 5. If your patch will induce a major compatibility break, or has a design component that deserves extended discussion or debate among the wider OIIO community, then it may be prudent to email oiio-dev pointing everybody to the pull request URL and discussing any issues you think are important. 6. The reviewer will look over the code and critique on the "comments" area, or discuss in email. Reviewers may ask for changes, explain problems they found, congratulate the author on a clever solution, etc. But until somebody says "LGTM" (looks good to me), the code should not be committed. Sometimes this takes a few rounds of give and take. Please don't take it hard if your first try is not accepted. It happens to all of us. 7. After approval, one of the senior developers (with commit approval to the official main repository) will merge your fixes into the master branch. Coding Style ------------ There are two overarching rules: 1. When making changes, conform to the style and conventions of the surrounding code. 2. Strive for clarity, even if that means occasionally breaking the guidelines. Use your head and ask for advice if your common sense seems to disagree with the conventions. Below we try to enumerate the guidelines embodied in the code. #### File conventions C++ implementation should be named `*.cpp`. Headers should be named `.h`. All headers should contain #pragma once All source files should begin with the copyright and license, which can be found in the LICENSE file (or cut and pasted from any other other source file). Two notes on this: * For NEW source files, please change the copyright year to the present. DO NOT edit existing files only to update the copyright year, it just creates pointless deltas and offers no increased protection. * One or two files also credit NVIDIA. Do NOT copy that line to any new files, it really only applies to the small number of files where it already appears. #### Formatting NEVER alter somebody else's code to reformat just because you found something that violates the rules. Let the group/author/leader know, and resist the temptation to change it yourself. Each line of text in your code should be at most 80 characters long. Exceptions are allowed for those rare cases where letting a line be longer (and wrapping on an 80-character window) is actually a better and clearer alternative than trying to split it into two lines. Sometimes this happens, but it's extremely rare. Indent 4 spaces at a time, and use actual spaces, not tabs. For files that must use tabs for some reason, tabs should always be on 8's. Most editors have a setting that forces indentations to be spaces. With emacs, you can do this: (setq c-default-style "bsd") (setq-default indent-tabs-mode nil) Opening brace on the same line as the condition or loop. One statement per line, except in rare cases where violating this rule makes the code more clear. Three (3) consecutive empty lines between function or class method implementations, one blank line between method declarations within a class definition. Put a single blank line within a function if it helps to visually separate different sequential tasks. Don't put multiple blank lines in a row within a function, or blank lines right after an opening brace or right before a closing brace. The goal is to use just enough white space to help developers visually parse the code (for example, spotting at a glance where new functions begin), but not so much as to spread it out or be confusing. For if, for, while, etc., put a space before the paren, but NOT inside the parens. For example: if (foo) // Yes if(foo) // No if ( foo ) // No Function calls should have a space between the function name and the opening parenthesis, NO space inside the parenthesis, except for a single blank space between each argument. For example: x = foo (a, b); // Yes, this is always ok x = foo ( a, b ); // No x = foo (a,b); // No x = foo(a, b); // No x = foo(a); // Occasionally, this just looks better, when the function name is short, // and there's just one very short argument. What can I say, I do it too. Function declarations: function names should begin at column 0 for a full function definition. (It's ok to violate this rule for very short inline functions within class definitions.) Here is a short code fragment that shows some of these rules in action: static int function (int a, int b) { int x = a + b; if (a == 0 || b == 0) { x += 1; x *= 4; } else { x -= 3; } for (int i = 0; i < 3; ++i) { x += a * i; x *= foo (i); // function call } return x; } Don't ever reformat, re-indent, change whitespace, or make any other such changes to working code. If you're adding just a few lines or fixing a bug in existing code, stick to the style of the surrounding code. In very rare circumstances, and with consensus of the group, reformatting is sometimes helpful to improve code readability, but it should be done as a separate formatting-only checkin. #### Identifiers In general, classes and templates should start with upper case and capitalize new words: class CustomerList; In general, local variables should start with lower case. If your class is extremely similar to, or modeled after, something in the standard library, Boost, or something else we interoperate with, it's ok to use their naming conventions. For example, very general utility classes and templates (the kind of thing you would normally find in std or boost) should be lower case with underscores separating words, as they would be if they were standards. template shared_ptr; class scoped_mutex; Macros should be ALL_CAPS, if used at all. Names of data should generally be nouns. Functions/methods are trickier: a the name of a function that returns a value but has no side effects should be a noun, but a procedure that performs an action should be a verb. #### Class structure Try to avoid public data members, although there are some classes that serve a role similar to a simple C struct -- a very straightforward collection of data members. In these, it's fine to make the data members public and have clients set and get them directly. Private member data should be named m_foo (alternately, it's ok to use the common practice of member data foo_, but don't mix the conventions within a class). Private member data that needs public accessors should use the convention: void foo (const T& newfoo) { m_foo = newfoo; } const T& foo () const { return m_foo; } Avoid multiple inheritance. Namespaces: yes, use them! #### Third-party libraries Prefer C++11 `std` rather than Boost, where both can do the same task. Feel free to use Boost classes you already see in the code base, but don't use any Boost you don't see us already using, without first checking with the project leader. Please do use IlmBase vector, matrix, and utility classes where applicable. Don't write your own vector or matrix code! Use these libraries for OIIO internals, but please DO NOT require them in any of our main external APIs unless it's been thoroughly discussed and approved by the group. #### Comments and Doxygen Comment philosophy: try to be clear, try to help teach the reader what's going on in your code. Prefer C++ comments (starting line with `//`) rather than C comments (`/* ... */`). For any function that may be used by other programmers (e.g., public or protected members of classes), please use Doxygen-style comments. They looks like this: /// Explanation of a class. Note THREE slashes! /// Also, you need at least two lines like this. If you don't have enough /// for two lines, make one line blank like this: /// class myclass { .... float foo; ///< Doxygen comments on same line look like this } If you know Doxygen well, please feel free to use the various other markups. But don't go so crazy with Doxygen markups that the comment itself, in an ordinary editor, is not as readable as possible. The Doxygen-generated pages are nice, but the place that needs to be most readable is in the code. #### Miscellaneous Macros should be used only rarely -- prefer inline functions, templates, enums, or "const" values wherever possible. #### Bottom Line When in doubt, look elsewhere in the code base for examples of similar structures and try to format your code in the same manner. openimageio-1.7.17~dfsg0.orig/Makefile0000644000175000017500000003753413151711064016001 0ustar mfvmfv######################################################################### # # This is the master makefile. # Here we put all the top-level make targets, platform-independent # rules, etc. # # Run 'make help' to list helpful targets. # ######################################################################### .PHONY: all debug profile clean realclean nuke doxygen working_dir := ${shell pwd} INSTALLDIR =${working_dir} # Figure out which architecture we're on include ${working_dir}/src/make/detectplatform.mk # Presence of make variables DEBUG and PROFILE cause us to make special # builds, which we put in their own areas. ifdef DEBUG variant +=.debug endif ifdef PROFILE variant +=.profile endif MY_MAKE_FLAGS ?= MY_NINJA_FLAGS ?= MY_CMAKE_FLAGS += -g3 -DSELF_CONTAINED_INSTALL_TREE:BOOL=TRUE BUILDSENTINEL ?= Makefile NINJA ?= ninja CMAKE ?= cmake # Site-specific build instructions ifndef OPENIMAGEIO_SITE OPENIMAGEIO_SITE := ${shell uname -n} endif ifneq (${shell echo ${OPENIMAGEIO_SITE} | grep imageworks},) include ${working_dir}/site/spi/Makefile-bits endif # Set up variables holding the names of platform-dependent directories -- # set these after evaluating site-specific instructions top_build_dir := build build_dir := ${top_build_dir}/${platform}${variant} top_dist_dir := dist dist_dir := ${top_dist_dir}/${platform}${variant} VERBOSE ?= ${SHOWCOMMANDS} ifneq (${VERBOSE},) MY_MAKE_FLAGS += VERBOSE=${VERBOSE} MY_CMAKE_FLAGS += -DVERBOSE:BOOL=${VERBOSE} ifneq (${VERBOSE},0) MY_NINJA_FLAGS += -v TEST_FLAGS += -V endif $(info OPENIMAGEIO_SITE = ${OPENIMAGEIO_SITE}) $(info dist_dir = ${dist_dir}) $(info INSTALLDIR = ${INSTALLDIR}) endif ifneq (${EMBEDPLUGINS},) MY_CMAKE_FLAGS += -DEMBEDPLUGINS:BOOL=${EMBEDPLUGINS} endif ifneq (${USE_OPENGL},) MY_CMAKE_FLAGS += -DUSE_OPENGL:BOOL=${USE_OPENGL} endif ifneq (${USE_QT},) MY_CMAKE_FLAGS += -DUSE_QT:BOOL=${USE_QT} endif ifneq (${FORCE_OPENGL_1},) MY_CMAKE_FLAGS += -DFORCE_OPENGL_1:BOOL=${FORCE_OPENGL_1} endif ifneq (${NOTHREADS},) MY_CMAKE_FLAGS += -DNOTHREADS:BOOL=${NOTHREADS} endif ifneq (${OIIO_THREAD_ALLOW_DCLP},) MY_CMAKE_FLAGS += -DOIIO_THREAD_ALLOW_DCLP:BOOL=${OIIO_THREAD_ALLOW_DCLP} endif ifneq (${NAMESPACE},) MY_CMAKE_FLAGS += -DOIIO_NAMESPACE:STRING=${NAMESPACE} endif ifneq (${HIDE_SYMBOLS},) MY_CMAKE_FLAGS += -DHIDE_SYMBOLS:BOOL=${HIDE_SYMBOLS} endif ifneq (${USE_PYTHON},) MY_CMAKE_FLAGS += -DUSE_PYTHON:BOOL=${USE_PYTHON} endif ifneq (${USE_PYTHON3},) MY_CMAKE_FLAGS += -DUSE_PYTHON3:BOOL=${USE_PYTHON3} endif ifneq (${PYTHON_VERSION},) MY_CMAKE_FLAGS += -DPYTHON_VERSION:STRING=${PYTHON_VERSION} endif ifneq (${PYLIB_LIB_PREFIX},) MY_CMAKE_FLAGS += -DPYLIB_LIB_PREFIX:BOOL=${PYLIB_LIB_PREFIX} endif ifneq (${PYLIB_INCLUDE_SONAME},) MY_CMAKE_FLAGS += -DPYLIB_INCLUDE_SONAME:BOOL=${PYLIB_INCLUDE_SONAME} endif ifneq (${USE_FFMPEG},) MY_CMAKE_FLAGS += -DUSE_FFMPEG:BOOL=${USE_FFMPEG} endif ifneq (${USE_FIELD3D},) MY_CMAKE_FLAGS += -DUSE_FIELD3D:BOOL=${USE_FIELD3D} endif ifneq (${FIELD3D_HOME},) MY_CMAKE_FLAGS += -DFIELD3D_HOME:STRING=${FIELD3D_HOME} endif ifneq (${USE_OPENJPEG},) MY_CMAKE_FLAGS += -DUSE_OPENJPEG:BOOL=${USE_OPENJPEG} endif ifneq (${OPENJPEG_HOME},) MY_CMAKE_FLAGS += -DOPENJPEG_HOME:STRING=${OPENJPEG_HOME} endif ifneq (${USE_JPEGTURBO},) MY_CMAKE_FLAGS += -DUSE_JPEGTURBO:BOOL=${USE_JPEGTURBO} endif ifneq (${JPEGTURBO_PATH},) MY_CMAKE_FLAGS += -DJPEGTURBO_PATH:STRING=${JPEGTURBO_PATH} endif ifneq (${USE_GIF},) MY_CMAKE_FLAGS += -DUSE_GIF:BOOL=${USE_GIF} endif ifneq (${GIF_DIR},) MY_CMAKE_FLAGS += -DGIF_DIR:STRING=${GIF_DIR} endif ifneq (${USE_OCIO},) MY_CMAKE_FLAGS += -DUSE_OCIO:BOOL=${USE_OCIO} endif ifneq (${USE_NUKE},) MY_CMAKE_FLAGS += -DUSE_NUKE:BOOL=${USE_NUKE} endif ifneq (${USE_OPENSSL},) MY_CMAKE_FLAGS += -DUSE_OPENSSL:BOOL=${USE_OPENSSL} endif ifneq (${USE_OPENCV},) MY_CMAKE_FLAGS += -DUSE_OPENCV:BOOL=${USE_OPENCV} endif ifneq (${USE_LIBRAW},) MY_CMAKE_FLAGS += -DUSE_LIBRAW:BOOL=${USE_LIBRAW} endif ifneq (${LIBRAW_PATH},) MY_CMAKE_FLAGS += -DLIBRAW_PATH:STRING=${LIBRAW_PATH} endif ifneq (${USE_PTEX},) MY_CMAKE_FLAGS += -DUSE_PTEX:BOOL=${USE_PTEX} endif ifneq (${USE_EXTERNAL_PUGIXML},) MY_CMAKE_FLAGS += -DUSE_EXTERNAL_PUGIXML:BOOL=${USE_EXTERNAL_PUGIXML} -DPUGIXML_HOME=${PUGIXML_HOME} endif ifneq (${OPENEXR_HOME},) MY_CMAKE_FLAGS += -DOPENEXR_HOME:STRING=${OPENEXR_HOME} endif ifneq (${ILMBASE_HOME},) MY_CMAKE_FLAGS += -DILMBASE_HOME:STRING=${ILMBASE_HOME} endif ifneq (${OCIO_HOME},) MY_CMAKE_FLAGS += -DOCIO_PATH:STRING=${OCIO_HOME} endif ifneq (${BOOST_HOME},) MY_CMAKE_FLAGS += -DBOOST_ROOT:STRING=${BOOST_HOME} endif ifneq (${NUKE_HOME},) MY_CMAKE_FLAGS += -DNuke_ROOT:STRING=${NUKE_HOME} endif ifneq (${NUKE_VERSION},) MY_CMAKE_FLAGS += -DNUKE_VERSION:STRING=${NUKE_VERSION} endif ifneq (${STOP_ON_WARNING},) MY_CMAKE_FLAGS += -DSTOP_ON_WARNING:BOOL=${STOP_ON_WARNING} endif ifneq (${BUILDSTATIC},) MY_CMAKE_FLAGS += -DBUILDSTATIC:BOOL=${BUILDSTATIC} endif ifneq (${LINKSTATIC},) MY_CMAKE_FLAGS += -DLINKSTATIC:BOOL=${LINKSTATIC} endif ifneq (${OIIO_BUILD_TOOLS},) MY_CMAKE_FLAGS += -DOIIO_BUILD_TOOLS:BOOL=${OIIO_BUILD_TOOLS} endif ifneq (${OIIO_BUILD_TESTS},) MY_CMAKE_FLAGS += -DOIIO_BUILD_TESTS:BOOL=${OIIO_BUILD_TESTS} endif ifneq (${SOVERSION},) MY_CMAKE_FLAGS += -DSOVERSION:STRING=${SOVERSION} endif ifneq (${BUILD_OIIOUTIL_ONLY},) MY_CMAKE_FLAGS += -DBUILD_OIIOUTIL_ONLY:BOOL=${BUILD_OIIOUTIL_ONLY} endif ifdef DEBUG MY_CMAKE_FLAGS += -DCMAKE_BUILD_TYPE:STRING=Debug endif ifdef PROFILE MY_CMAKE_FLAGS += -DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo endif ifneq (${MYCC},) MY_CMAKE_FLAGS += -DCMAKE_C_COMPILER:STRING="${MYCC}" endif ifneq (${MYCXX},) MY_CMAKE_FLAGS += -DCMAKE_CXX_COMPILER:STRING="${MYCXX}" endif ifneq (${USE_CPP11},) MY_CMAKE_FLAGS += -DUSE_CPP11:BOOL=${USE_CPP11} endif ifneq (${USE_CPP14},) MY_CMAKE_FLAGS += -DUSE_CPP14:BOOL=${USE_CPP14} endif ifneq (${USE_LIBCPLUSPLUS},) MY_CMAKE_FLAGS += -DUSE_LIBCPLUSPLUS:BOOL=${USE_LIBCPLUSPLUS} endif ifneq (${EXTRA_CPP_ARGS},) MY_CMAKE_FLAGS += -DEXTRA_CPP_ARGS:STRING="${EXTRA_CPP_ARGS}" endif ifneq (${USE_SIMD},) MY_CMAKE_FLAGS += -DUSE_SIMD:STRING="${USE_SIMD}" endif ifneq (${TEST},) TEST_FLAGS += -R ${TEST} endif ifneq (${USE_CCACHE},) MY_CMAKE_FLAGS += -DUSE_CCACHE:BOOL=${USE_CCACHE} endif ifeq (${USE_NINJA},1) MY_CMAKE_FLAGS += -G Ninja BUILDSENTINEL := build.ninja endif ifneq (${CODECOV},) MY_CMAKE_FLAGS += -DCMAKE_BUILD_TYPE:STRING=Debug -DCODECOV:BOOL=${CODECOV} endif ifneq (${USE_FREETYPE},) MY_CMAKE_FLAGS += -DUSE_FREETYPE:BOOL=${USE_FREETYPE} endif #$(info MY_CMAKE_FLAGS = ${MY_CMAKE_FLAGS}) #$(info MY_MAKE_FLAGS = ${MY_MAKE_FLAGS}) ######################################################################### ######################################################################### # Top-level documented targets all: dist # 'make debug' is implemented via recursive make setting DEBUG debug: ${MAKE} DEBUG=1 --no-print-directory # 'make profile' is implemented via recursive make setting PROFILE profile: ${MAKE} PROFILE=1 --no-print-directory # 'make cmakesetup' constructs the build directory and runs 'cmake' there, # generating makefiles to build the project. For speed, it only does this when # ${build_dir}/Makefile doesn't already exist, in which case we rely on the # cmake generated makefiles to regenerate themselves when necessary. cmakesetup: @ (if [ ! -e ${build_dir}/${BUILDSENTINEL} ] ; then \ ${CMAKE} -E make_directory ${build_dir} ; \ cd ${build_dir} ; \ ${CMAKE} -DCMAKE_INSTALL_PREFIX=${INSTALLDIR}/${dist_dir} \ ${MY_CMAKE_FLAGS} -DBOOST_ROOT=${BOOST_HOME} \ ../.. ; \ fi) ifeq (${USE_NINJA},1) # 'make cmake' does a basic build (after first setting it up) cmake: cmakesetup @ ( cd ${build_dir} ; ${NINJA} ${MY_NINJA_FLAGS} ) # 'make cmakeinstall' builds everthing and installs it in 'dist'. # Suppress pointless output from docs installation. cmakeinstall: cmake @ ( cd ${build_dir} ; ${NINJA} ${MY_NINJA_FLAGS} install | grep -v '^-- \(Installing\|Up-to-date\|Set runtime path\)' ) # 'make package' builds everything and then makes an installable package # (platform dependent -- may be .tar.gz, .sh, .dmg, .rpm, .deb. .exe) package: cmakeinstall @ ( cd ${build_dir} ; ${NINJA} ${MY_NINJA_FLAGS} package ) # 'make package_source' makes an installable source package # (platform dependent -- may be .tar.gz, .sh, .dmg, .rpm, .deb. .exe) package_source: cmakeinstall @ ( cd ${build_dir} ; ${NINJA} ${MY_NINJA_FLAGS} package_source ) else # 'make cmake' does a basic build (after first setting it up) cmake: cmakesetup @ ( cd ${build_dir} ; ${MAKE} ${MY_MAKE_FLAGS} ) # 'make cmakeinstall' builds everthing and installs it in 'dist'. # Suppress pointless output from docs installation. cmakeinstall: cmake @ ( cd ${build_dir} ; ${MAKE} ${MY_MAKE_FLAGS} install | grep -v '^-- \(Installing\|Up-to-date\|Set runtime path\)' ) # 'make package' builds everything and then makes an installable package # (platform dependent -- may be .tar.gz, .sh, .dmg, .rpm, .deb. .exe) package: cmakeinstall @ ( cd ${build_dir} ; ${MAKE} ${MY_MAKE_FLAGS} package ) # 'make package_source' makes an installable source package # (platform dependent -- may be .tar.gz, .sh, .dmg, .rpm, .deb. .exe) package_source: cmakeinstall @ ( cd ${build_dir} ; ${MAKE} ${MY_MAKE_FLAGS} package_source ) endif # 'make dist' is just a synonym for 'make cmakeinstall' dist : cmakeinstall TEST_FLAGS += --force-new-ctest-process --output-on-failure # 'make test' does a full build and then runs all tests test: cmake @ ${CMAKE} -E cmake_echo_color --switch=$(COLOR) --cyan "Running tests ${TEST_FLAGS}..." @ # if [ "${CODECOV}" == "1" ] ; then lcov -b ${build_dir} -d ${build_dir} -z ; rm -rf ${build_dir}/cov ; fi @ ( cd ${build_dir} ; PYTHONPATH=${PWD}/${build_dir}/src/python ctest -E broken ${TEST_FLAGS} ) @ ( if [ "${CODECOV}" == "1" ] ; then \ cd ${build_dir} ; \ lcov -b . -d . -c -o cov.info ; \ lcov --remove cov.info "/usr*" -o cov.info ; \ genhtml -o ./cov -t "OIIO test coverage" --num-spaces 4 cov.info ; \ fi ) # 'make testall' does a full build and then runs all tests (even the ones # that are expected to fail on some platforms) testall: cmake ${CMAKE} -E cmake_echo_color --switch=$(COLOR) --cyan "Running all tests ${TEST_FLAGS}..." ( cd ${build_dir} ; PYTHONPATH=${PWD}/${build_dir}/src/python ctest ${TEST_FLAGS} ) # 'make clean' clears out the build directory for this platform clean: ${CMAKE} -E remove_directory ${build_dir} # 'make realclean' clears out both build and dist directories for this platform realclean: clean ${CMAKE} -E remove_directory ${dist_dir} # 'make nuke' blows away the build and dist areas for all platforms nuke: ${CMAKE} -E remove_directory ${top_build_dir} ${CMAKE} -E remove_directory ${top_dist_dir} doxygen: doxygen src/doc/Doxyfile ######################################################################### # 'make help' prints important make targets help: @echo "Targets:" @echo " make Build optimized binaries and libraries in ${dist_dir}," @echo " temporary build files in ${build_dir}" @echo " make debug Build unoptimized with symbols in ${dist_dir}.debug," @echo " temporary build files in ${build_dir}.debug" @echo " make profile Build for profiling in ${dist_dir}.profile," @echo " temporary build files in ${build_dir}.profile" @echo " make clean Remove the temporary files in ${build_dir}" @echo " make realclean Remove both ${build_dir} AND ${dist_dir}" @echo " make nuke Remove ALL of build and dist (not just ${platform})" @echo " make test Run tests" @echo " make testall Run all tests, even broken ones" @echo " make doxygen Build the Doxygen docs in ${top_build_dir}/doxygen" @echo "" @echo "Helpful modifiers:" @echo " C++ compiler and build process:" @echo " VERBOSE=1 Show all compilation commands" @echo " STOP_ON_WARNING=0 Do not stop building if compiler warns" @echo " OPENIMAGEIO_SITE=xx Use custom site build mods" @echo " MYCC=xx MYCXX=yy Use custom compilers" @echo " USE_CPP11=0 Compile in C++11 mode (=0 means use CPP03!)" @echo " USE_CPP14=1 Compile in C++14 mode" @echo " USE_LIBCPLUSPLUS=1 Use clang libc++" @echo " EXTRA_CPP_ARGS= Additional args to the C++ command" @echo " USE_NINJA=1 Set up Ninja build (instead of make)" @echo " USE_CCACHE=0 Disable ccache (even if available)" @echo " CODECOV=1 Enable code coverage tests" @echo " Linking and libraries:" @echo " HIDE_SYMBOLS=1 Hide symbols not in the public API" @echo " SOVERSION=nn Include the specifed major version number " @echo " in the shared object metadata" @echo " BUILDSTATIC=1 Build static library instead of shared" @echo " LINKSTATIC=1 Link with static external libs when possible" @echo " Finding and Using Dependencies:" @echo " BOOST_HOME=path Custom Boost installation" @echo " OPENEXR_HOME=path Custom OpenEXR installation" @echo " ILMBASE_HOME=path Custom IlmBase installation" @echo " USE_EXTERNAL_PUGIXML=1 Use the system PugiXML, not the one in OIIO" @echo " USE_QT=0 Skip anything that needs Qt" @echo " USE_OPENGL=0 Skip anything that needs OpenGL" @echo " FORCE_OPENGL_1=1 Force iv to use OpenGL's fixed pipeline" @echo " USE_PYTHON=0 Don't build the Python binding" @echo " USE_PYTHON3=1 If 1, try to build against Python3, not 2.x" @echo " PYTHON_VERSION=2.6 Specify the Python version" @echo " USE_FIELD3D=0 Don't build the Field3D plugin" @echo " FIELD3D_HOME=path Custom Field3D installation" @echo " USE_FFMPEG=0 Don't build the FFmpeg plugin" @echo " USE_JPEGTURBO=0 Don't build the JPEG-Turbo even if found" @echo " JPEGTURBO_PATH=path Custom path for JPEG-Turbo" @echo " USE_OPENJPEG=0 Don't build the JPEG-2000 plugin" @echo " USE_GIF=0 Don't build the GIF plugin" @echo " GIF_DIR=path Custom GIFLIB installation" @echo " USE_OCIO=0 Don't use OpenColorIO even if found" @echo " OCIO_HOME=path Custom OpenColorIO installation" @echo " USE_NUKE=0 Don't build Nuke plugins" @echo " NUKE_HOME=path Custom Nuke installation" @echo " NUKE_VERSION=ver Custom Nuke version" # @echo " USE_OPENSSL=1 Use OpenSSL's SHA-1 implementation" @echo " USE_LIBRAW=0 Don't use LibRaw, even if found" @echo " LIBRAW_PATH=path Custom LibRaw installation" @echo " USE_OPENCV=0 Skip anything that needs OpenCV" @echo " USE_PTEX=0 Skip anything that needs PTex" @echo " USE_FREETYPE=0 Skip anything that needs Freetype" @echo " OIIO build-time options:" @echo " NAMESPACE=name Wrap everything in another namespace" @echo " EMBEDPLUGINS=0 Don't compile the plugins into libOpenImageIO" @echo " NOTHREADS=1 Build with threading support turned off" @echo " OIIO_THREAD_ALLOW_DCLP=0 Don't allow threads.h to use DCLP" @echo " OIIO_BUILD_TOOLS=0 Skip building the command-line tools" @echo " OIIO_BUILD_TESTS=0 Skip building the unit tests" @echo " BUILD_OIIOUTIL_ONLY=1 Build *only* libOpenImageIO_Util" @echo " USE_SIMD=arch Build with SIMD support (choices: 0, sse2, sse3," @echo " ssse3, sse4.1, sse4.2, f16c, avx, avx2" @echo " comma-separated ok)" @echo " make test, extra options:" @echo " TEST=regex Run only tests matching the regex" @echo "" openimageio-1.7.17~dfsg0.orig/testsuite/0000755000175000017500000000000013151711064016356 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/testsuite/texture-cropover/0000755000175000017500000000000013151711064021713 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/testsuite/texture-cropover/run.py0000755000175000017500000000065213151711064023077 0ustar mfvmfv#!/usr/bin/env python # Tests input image which is partial crop, partial overscan! command += oiiotool(parent + "/oiio-images/grid.tif " + "--crop 500x1000+250+0 --fullsize 1000x800+0+100 -o grid-cropover.exr") command += maketx_command ("grid-cropover.exr", "grid-cropover.tx.exr") command += testtex_command ("grid-cropover.tx.exr", "--wrap black") outputs = [ "out.exr" ] openimageio-1.7.17~dfsg0.orig/testsuite/texture-cropover/ref/0000755000175000017500000000000013151711064022467 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/testsuite/texture-cropover/ref/out.exr0000644000175000017500000074751513151711064024041 0ustar mfvmfvv/1capDatestring2016:02:25 9:51:47channelschlistIABGRcompressioncompressiondataWindowbox2idisplayWindowbox2ilineOrderlineOrderpixelAspectRatiofloat?screenWindowCenterv2fscreenWindowWidthfloat?q+@%,?:]}{K"b6sZ^|8t,RyGpȔUx;#:4Ux;#:4 Ux;#:40xhuq4$D P2W@5ŘvԹLNo?nw{n?O#jNNJV.t tŽ@D_BDg|?7|gwm sZׂ b凾T'@(HqR]>{ouTngX֊AwY_;9sKN9ϫ;v_ ;Qun.Œ̹σtд^=c杝 n^l~d${䅧U0^gɢK],-{W/Z:Ǜ}uK buK ; ٧'NuY*o o*hؿCm N^u4n &h]` '. `%[_?\ح.@k}US Лq#-0vz_,.u ;\?qDj6IS 2 VHS voMQ'XS]6&ϝhFa N{hmDnއXtK ۟wvjoԻ"kyG4M^C8R]@ݎޗvu 8{hOw;&vN6w;@ x XLyp]gwiE,QB"-J+B.Eݦ9ElSmQ*bbԺEXukkBڵd33(̜}43<zlYd&F$ͱHnNp)A lg_rgQ_7滤z⳨:[SkZ7D쐵4,~^Zj֣SM[u6|Pfly)9Vm εfv\l@ 醊nr6\bFE(ԟ2i7\>{ {pQs8sd3iZ@-b^ЉD0Hvfr"oުk9JRꞑo 17).uH4Pf{c`Q졞J ?՚,y`}4*+zPozϐӑ/ ;`3+c)͎>qVcy͢1*ͻcgPgyڮ߹Ae {v̓'aSviB(sFo~qQUp̝:NG-XZZ1s*p}'|ǩQ'gjmue,&A_v|mAno7~ne@,"}IoBV;huUښ>-XD}o-c*jW l83%$wF+3vk]:I`wbrMglVKKn;tީ],~']˒fxP֦"{Vk6vij-Pg ]$S v_? xXM-n&4+ShkzF}orko`\~ꪽ!)JQU4guۿ˹t3 VJ;>Tgꌂe3 ˗W*>'NU{Aԉ"AͲyZ['8-*11|Xp&o=ɾ[N ޗ}8x~r fi;Y.6wmAGy_Uwr;]On׬vKrEߩP g}Z-4lV(5쳟ilXdw-:=lÒBP dʧiʈXDQ[ivDjexe 6 GIe~VVOR1P\&zW7@AV9vZ:6_BSU7.\k1}l/(bevϨcI_|ߪ6ڊG{GDж;2>&Iýk=~1 #G;l6\<#y%cW,4viv<֫?^g(yy֢SنZǛ_j}up8n(nH<ꋂG ǜ@PfK"0>B9szdƼ# WBS?B3ۡXj/?Y>MO5!ҝHK 7<a,@?eɆ?aPgʗ:("~cC,%|׹'^L#Â`;J3/7K/G𸸰\URF-R Η @A-m&K68o_ S0PA6OYb>y_<tzŹp ?eFQM@$/]l0H_:-PlT=1y3<^?T$T ԟ?2?Q o PR*&[{ǿQ|nW8']VxPf$>Ǩs^Z'^*zE2/ӤC'N:DYAMv_4_+ (?5@a^O`; &oq|,2$YO abHPfgKAP|3@hlTG6EQ]6{1't4.h<2<nW PfOyc n$緯 pa;.>Px >hEmrtn.$"^c.w]EH6tYT S~Jh6~ޅ x]wݘݺ|une1FKf0vt$H (9jk¤KEmMޟiQ{&;L}sB0ូ/G$ɩ' *j+<7 MY]i據hut%>@׵Qwۓ;"TqZբs5 َZҚ.NS媑bI-+̛lָjk+5&zX0DxvqtIXHh @( :B#=69ʍSmW : !rœ?YV'9z#+e-~]#~t!3^~qc1YiipV1ԫ(`v]]τ܆'m8}Vd(>HYVqn_G|uN3SG6h;i\LZx(g7S m ?g b_f])Y+IYZnetYzo&i Vu c²1XgjэBb"^V Y Pywz$lP4QXQ3l]fs2A$GYjUW VQgBs![v=Ɵ+W1lmpX;C $/D=,۶QtgpXAn{ibn&I!Qj?F!Q1IY$1He#N\u/RUXF+U˅mnO^ep3t(-c;iz-@TE1fZ7(0dXym=c_ #@`R<`wMp[s9lb1q s/$۸)Ф*Dt|F&%)Q7vJG.]azi5{YB9j I& ^ NT<ٟٶUAnτ+w6aS`ٗE9>TC޸Z}ebR!G=;o kXGꆨ #HiH.>Ofyga6Ǟ*>3%de&8j,_{/&\T|D"Θ6 mM[h)RVϴFw >pRKnǪ,KXw%:l{J1+bkG+sNY>iׂVu+ -9{k=ؓ>P4?$|==ay j-5xq(B~uob8]o)+7eݞ{ͷ^ڟyA5;4N[(sbjĥYҜݞ`Wg+f|1-∍I }Kh,x^9`bQ9ߤ3x"Bùv Dء^A*+7LUJ+n_4yzR-hc\jkuBO?.+iI1-%yߡ*e6ZZeqc NI[[zxsSu7 MԳ&(_汷qѭ yF*\/T S{zqq)jn \(sjWs9@V]u _Bzڱc/SJ+J䞲{o`=UdBǖJYXȻTw9Ȣx4#ިqlI9U5>- /FeϭIu3>OJj^uF JFƛi1·/=)m<ۨew򥻷~X⚵F%](2mߜ{v*n=O2_hG\[}/^}8i0TNgm\uxE }AoDiW[r_׍W0ҿ~®ҡYdmLXOQӺmyaIJeߋ3,S _T7%K@]7@_6j?ϻ,1rzU {Ɵؠ!aL0b9 # ͡` LeL!*G 2g~R? 9>*v>0ϏF;{*G^c z9seI<smL2>L t sA[z#&^0VRFi.g#u?.AuhА!6$s(՟G:dLON f`#ia.ƟD9ԇЧ? &Y@|IE!0A!+I93R@8eBF(u DǨ.$˚@P. jdK2 $__7T DA&Xca" ?f1 x%u {y750{xW:`D:ßmP5??pB"#r&2VNĠ.:=BA `|>NB`dv@ߟbذaohG a_O0v@|L=Ke*Ls`vg?K" ?DsB(p!AJ!l>퀸#..GG)fd @SP>@|r`Ydv} p<L 8+)߀>`;>H J֟ pa#I&7 % LƘ0`` ' ڵ3/*0a7̧Q~TA`#I0}dD ` Aca:9y~ԟ}o @"qiR?HP?a]7F} Á÷aI`3J?:22); NH{?KmœyAֽZ`x <[ETJEQJU )"ywflc_ZhdTH[Rh$Q)[;hߺw̘=;3;~s<<(ipze7I(q bCw!>X%uq:ڔ[*; u>/ג FoӍDŽhOnՁ[$cdkc UK\ 1(G{ώʭ;fťDzI{_9Ḅ@u"oI^6OI5O?Li^WtfʕWI|3gΕNP~c*auL<]wQ?'Fo=Eͻ2O_~q\각Elӹ:bcK½E|}"QZ70C=.? H>2iev?.NPfSI|S_#{!,oϋ'^@AYl~՞;5]CåaU+Ϩ4E0;q㄃}>*戀?/yT3g}^n].u ;ԆlcKȉ , L<щop_{&vHq:gGZ-^qB=ԋOuW/|~+{K5ygg\N,aai$0M[[x<:Z\_iVݕi%*6ZWQwkNWaMޛRk$%=\/ E*Uǜ}?H{a.B=jSj?Lڮ}OE)ӳm$kl1ԣm.lZPy_2זˏNVu7mV%(N<Wmͪ//aw5?i/k2LtNSêgsnO=e˘j~`Oםe]nmʤʍ`YEQIS1i;*rDo8qFWIgr6?籼lp~a/OhUq}|U#:+Kjj,KI=,CnB#7v%&g5|k-b"g>kؠ5/+Ev( JDKn[w-R>:&|鯯~ ™-6SmG`چf7hphyuOg~_cމ<4/;e溿KN%o49OjVe% X_sNV}bA1x^jm&,omş6FHXEWe8tE{44,X6 >Ne{^b]n|v޽u_»cHAbQϏ>/9jRU|&pzgыisvY>k׳s|C*b]UwR|WhRԯ2d8U1m~V{k rb)_pIʦb[_鉨IY1'X) :" [Mkg#YRW_9y.Vּ!jr~=_x$My%5ML=2=im³LMNhidZ̒6Bc/6Y=vQ ~ ecwe< =qeQA_]OQRexg<^'+ 0Ŧ)} d"pݱ!kR |{PN$~oԉN5i$k ׇ<%jr1FxGLâ?ɾq&1ʺk7v&.-jкĻ0om-,ωg=JH,Y-}a/\K_dsvtӮNZ' `װ0OY_6p- IU^ .+]h䭩bէ.v0&3f_- 0j7/[o+ƪ!IB("_Y~p)ļ"R= ~YhQmѥn?rNֹe&z{4OpdԒy=6܋1$cK,xS7:Nqjg^v-uM-)?gnn{^(Qα#_=*0zةT{_Cؒ+r,19@["yl%Bh7(/^H%sqBkvHQw"'THӜ[uVA\a9D$d~C6ozfܞKF%˺n~EZ~Ayue st;Æ5mvw?Ĵ>%)a}ógz{Vg/jj[v< MNmŠ:V4k\["yϥ;N*;LEvS#韹#1ܟƤkIMK1χ6@+7%vQYU& E_2Sh+iy[J:Iv6/h;Atm`uQAbF^8RX\")u,.CJ O՟s}{ v7%\c96(<0;~.KqLrŘce:?F_ #mXǗ:Jx˧Kh/Wb+䈁y+YS1#a72!cu ig ie=>kBM^^q iSu>5Fi;$ S0侣WNyLLOv멇`,2[4tC+f @N#; 07gyq'e#R)W]3] wNw2f %Hh*8:/"LXXť)DIOvM-ǚ|q_5{No*%e^:EB;$%kԇy0Q}/+f;<\ˣ>EvjXϘZ٤"?M9y8Yu.N$<_ַzJY6Li;Gެ&P)Px~TuySe~VfqiC69 l(A*6 3.x5Ylߴ/;ŬڦZ-lqY⢶Vl(Bnv3 GDHrަfh;[ 7#ЖNڽuAKOC?|TMzNM4U L?鬘i%n}-|!{iL.^63{_t$[PGsOT[H.pi|-Q]7,nsgHOIU-xZqZGT̲V5* 5@NcIU s[ 0ru]a^$,!s6əv7c\I}WlP;OsQbi3Mly.-nէț^RӌN$g.8>t1+n`du{sЍ^zLf6r?j8(N7c٘3V܅O)G0N|YIyy&8ݕ yRZ"7f/Y1RX5!8l ځ3>W钞 -\+pd:.l|7Ė~AGWs&Fh1ŧT6FAnqΐ=թ!lL*?cHNV>+ HfMlۑ^Z3ڬKCye=.%kųHz]vK"蠙(&uzk2_'/DgMpM5[+N-*SzVRއ#\M/ƸcG? w#twclY[HacOџ"⓯B!Lk˨WПUWd8 +G?S?Y6a{1xƷ`_KјQg=an^3V, 9ᏧS@??4m`dсɧ8 8<8ș0ad 0 %1tGZç R%s75` FޞNE08^icP=@h~0?<%Odžu'0x?/8" 9fy|9 =a!042 q @6Li?u`hmp gɎ5sA^o),( w-C` ) 83fzx!36w0Ou rOyp?UUq>Ŀ@dN3WOџt"P87=CrpgIA_!.Г!CЃ iasΓh*P'ܳN d?y$zQM;9G)# hO56?Q]}Gj 5†Ee~K!0_\vNm됛?apP@3T.{YV1O<]HD뮄z՟|#X? )khOAԌg uskGDSQw!<͛G~7 `jDA_C c,Y[?yyХ ;ad]A0H$xx_0Kgi}sXh_,,7$@biSxu>ԧXogiG#*Z0fЦ a 4`TƱ_xy:`#@"|3'5aXHHi#06sXi)@NF,.C(ۿ 6 /++ iG,x = l <Y^M`}C!7D'rDjm-zO'łG7П8jOYbB#$2;'7W zPzGmRf 8 hF^QT3Ǣ `@ &n1AhdiC x"DAB7Пӈ#S\XDy⇸D7ghGG `O{#tOMY^W0S7}sL nx%O1C@P}`?O7`#}!0LQ?*X!`IDzVAAz r3w8Eq}lDp&Dj,QO=pZϸvs,l 0O`p0a`9W&xGG@ofF]F;5?P_7p_΁u5|XXq<@T ^CxC8_d+ A^v~3P۽;px]y!d2gV({sF JHwԷL>@kn9><벵QM˚tbhާtE&E<{b*}>5핿R^:68Ɖȗyv9}GLم֣p=c.O!sþx#ˈ绢C ݚL󵳩5ȟ|kռ)6gt26~du(?rjZ'0+-L~rvi"SsZoP,:uɕܥj/GBDn :D|fR#FlfIl+ˆvun?GiRv-KFsjY$rÆ<_b/9fav=]:)|BDލI?;%[]2fnL?wln'w~;$nP[Q:z7z ~liŒQԩ]uA"_г3ib^v4ŴXwȏ{G7I; d1nfQd_W/KgΥQ^MOk?mQhQً@Oo( 15{stWT&^fFz1IR-mFv~ 'F;B> [\lCno1`qquwVD@N{m{/(=Cy]Xؤ湖={ȿgnw`8d01GFBEw:v˞}K^6B M LJeO_2lNDݔ k]2ǘnBw?16s!މ)|(-,2udkOUZ_-HWa+n?o(㖔a)y֠?hȬY;i!KܚE6`/Xx.2k|R=9ӆ{=:ӂ6>vcU _+nAȫEp"8U?D9HV|w^J h~S'Y{lv )8EC&Km5ȿ0s|g.D+qw*ϜrWkrlGerQ[ /kMS3'|gn oȵ(/ܭaM&7 Xl#kdТ{Jt%uCL, 'ʪ#ܞ>YכSNϼHh$5JG6WFoKۘ /1bC<;"Y.׶Z1 =p@7lExE 6roi$;hPbJ#B2;Lڲdl){yw"A^ds5D\bѭ+ #лwsoK mޙDOWܭA[#Թc txMk<=nrOd׹X׈oڂ*b®KSP(6ڲ֠?]Th5 sq~xiD)yUj[?[3Ѝ8uǚQ홿_l}]4K7pXm@wмtݿ0r;Y3礶]5Q'6 5wn_t}0Gdֻ2N&Y.w,_&[߇L=dQoѾlAYkUw9_׬VJe?+i=|1kswξjN0']qY!1sbPӲ;j ƦW=-I2(tϋ;Ϝi<*T͠] ws'otpKfӪ-Zļl"W|PON;̅ɻKkǗO#fJ=V;Y~b LU;Hd^թ!L4ⳇU 6zm0?޴_WaڮY8,VYeX˙bZӶsցisLgS~hOwh /]'Xx@[Ef6{niᷢ |ct*\ʓiL8W_fsq_ﻷn 78PF^^u k* p7ahf};XߞI3&d /6ǝ 7l-{cm6T1uݱ[s`?M3GOHu-an5:!n w jvqjcpoE(67יO#[^/6Z*Ńa5kq4$3pi#.&L#ɸ9BH_~]'b0TG s&"?n9{F6G9?4C#Am6Fc"e<BbEj+wEH+оu}r<1a"9O3<6|/>!Pa/?U_A J2Z*kMP~IMN]jtM1]ε-W wVC\g𿐀V dv lro t0%lqb&]gZ"~;ߩ}ۊon1Xl頾sX{N`݁K޸1۵v L_ߛ_^eTl6<rq$^…OJ5T1]3Ap7az.y띺ۨV-1| Ȼv/oLƷ뀗r6mUK*-zq+ #t~wڣ$k+8d97>OTb+7'*ۧW:¡d*N'J޽ mWVeX飗?kf𿀀 w0%w \9ZF_QK^sȭ5{.~6:8s?P`t^Qhsݪ̢lO LyC]~B`bųq͊"k9 _I@OCS$X_n )IS`Oа+LOnIp}agU;ڨw?ͺ׆& %˭]S+ j}Z)]e˙W{4nBaIݷ -~ӆ;$N]"9qo~qOkwpUw)| !xpktY*׷uIc𿀐ʮ wh6 w v-J"T8c(jKu?Sٷ;x䚤6E/W*&9}DZ197U=95W=\?λųܢA͇UHyњ gFͤRE ]wvӏ&T?)4ث{`5y2 JV(HT7JW@RRp}a,'|A-njg#E^5zI6;D~΃߫W;r5/Zu贼`#' <Ư`<Ma~GR',4G`XK,H'/4ʋA.^:x XCza]W(<z g`nGIoqx#t/GeH &PCh;0NQv0(bWFHC`АSF^1` 20_(uW Qz  : um%O`q#nC> ?j1#qCB(A2` I$ \-@ߘS]|)sZHQ# 8 (1h4K)!&+^ ]Dfpׁ5ܽBDs0h3w?h#??ɒ/09@4qHGa8&m K?YV` 3@ko .o$bIɾ~`,. O(GJן"1n:pWUXGQC»Xȟ%vm4"LDA~  Fd1c5.#FC4&`\K uм!IsQWǒuؼA!-?]0,3rBEEi(@<h#(s4yKΎ!@BÀzʁo &C}$cɮ},&Qd4 @c_b𿠐 w*W%Y 8?P>KLDDQ ([0Ut Mb;wJ,C2hp9#ؼ#% -S~mZe!^My2_PI ΟoI &*hxf]=}PP$/PxP0Cx0o5? &AXNׇe_H c u |jBԔh䝢&d+%їLFVZ&FSTB@ !~㨎Z@GH+)A#\RO!}䟑dA(/0~'0>Ѧ ` 1`GMmvx,Y%$ @cR)lǒ\߳r H0s!a%"I^}ޕ|~M=R0HJY9HzgzA x]y޸#b>^ 5[89Ql \@/9nC(y (I*7GRUc4=۞\ ~FP|ͺ;Rn=9RђLg _[j8 壳Έ zy(\r,=pwU]ex[ܭ_rCoR:`y牑S[l] -f8>i>rvh&'zVr]u55d3+o֏4ijX.Rͺ.}4zǴh?\i6 +V^]6[zЬE"y;&gv[k7ˏ>z9/;`ufˀe#OFֹ<[{Y&גw( s/;.i|5"*wv>2X<[] jM*Lgҷ[jyHo Wyj,`?oN Ui܍heAn( t'ޖ ئj~nv(:REweA+qH.[4yun@wM(Ã, ZUx֝N>a$qO f ] WW1`ۨ.#wu9f!j~<9ږDT_|"_9@P}H8>ܮy]l؍%,פ/ܺ_I;;1뮲i+X~=m;rв|O6 /eZ4L.39ȕd[jxjM8rkk58s xxy#.{>>k;@ۡt7uqڞ Z&Hfls1c{AW-E@ֻߞ[i{4 6بK>xJq '?k[zexl]=,CIm_z*ۂڹR^=e*Swu懎Z.֦w0zǝ\nfvݱV=0i1M/y=.ή:Sd-82W-[HtϊSJY=&c\{q2LAϥPm&khJҏڥrB7Zvgp/.4d\-J<۹5Gi/Fe.$p8 Xuǂ:e8KbVQĪGfR6Zgzh|;~^(xC?j{7s)<˺oٯ,8#f ,uDbm-mһO /q_dkf![?nqF9sXT\Se˖vg;v"]/7zHW z,$L휑u&3K][)8&{1e`൵C7Wl*iieZ;(=vF*1"C\WΏu/Vm%?K~ŃܭFκr*y%[lxc,> u4veU!]wV3 wʱF5uA9.7|\Uz `7ѹ~q鍏J6y[v0o=pwC[ zBK<ɥ}b^N޽8j+7Z˖vSj ?U [sF&˼/5H8ݤagUdeN,s FbMVbLg j-THe/W.FjҍRyuԂh}tl~ͷlCҚseDU ^R*_vdw5Brjiܥ<+w ?In\4y!Y'Q?#,s6=`~>(|Q:mKz `OV4_}d˿\|bNF\daH$l85U!Pqrq!z[z uE#E,*;4yמn6׼QPĄg?qՂ{.&W)'m.$Xd \+z٭odv ] ?l\zBoûrlNJvĴDmT;k:4sT]\USѺoY[!(wOV':qNQz\sPN-H;!d3T W&3-کhcz0"M(yċRz5*L2D[b3o1UץWH ]yY)&ԎPʛ']CЍ/;6 5[\3 .Zpm|[5)J]`ˏ*JuQBnaL1̄xh kݼ/ڄ R7}em(XcqsLN|ۦ3 d=Odi%Kna\]Ν<ԽY}cmvUxbNvؗ5LѬL|3`Ut-|]i7?tIb?SJGz;2-Le= I;V\y^rǫ*g,eq_C5zm) A7Y/xIKν^4Pd*٭\/rҷ.i Bg's7u?IM:Zaxl瑡+=Q6otljGqҜso( v ,Uy4Eۢ2j%8zVf>/yXUJ -b'_tJG,Rtq)l[NnW)=;S.5we`7$ܫtF2C!l3L6̖*#|{Nͻ8S1lil}\#JnE@KJ|K:a29?1=: ģx[nak[[_D^@0gΚ:Qɒzw2^L!6 _Sc.GYk[eѱC[msg?S2tVe`~ԍVe *_[3W <ϟ/.ض,lۊ[-q &Wр*QmƄ^b- w: \1ty&·$WaM`>yJF3&1cQf0maϿX(N2 e1lƽjoW{حb|y xBW_Q:uLx z/=j o{_wmiT]!QXzc?y<f6 gO>N}uM"uUwp}Q`>{C$m>n˽U,fYu6<[p"])f"-+ gt,N|JF^FF2s*pP+5\k ;A0K gۖ҉mF]bzt5l!kwvr۔ټ@WFˉ+}-1}:Czw `a-:"_+&EY_vane"{M%.yQ5D/ϓ}-2\/ [[3_MuO4" ZbM(m1Ez ofKT<;:p.Lߛչ(HMeY/܎lPTn웷9-9m2~vHybt)E SɂK? eʋ]hUC%ysK)v_7]s?~sGZ|aOL ט\e^GiRc/0`E W2a:quC[n>Zr$ 0D#=B: rrIkztJ9&VX2 yf? Tnzx#MxF%El0^>1sIG@!-FLzd- D?rwVA2{f4!: ]P, A@1]ǒ$Crsxd |`NH?:_`z/2AToȏ+y60,ڑQqqE@@)BjpRxGO>`dG$0OR +b`Os>b?C?e{20Cԡ^xLzRTs}-%2LjƙYB3\ p/\U_J% HI'@$Iu(Ms wA|h_ap9G^?&x//X=ԲaÏЄe(H a}nh1 |xMRN9 ]08bBqV1 ǟbF1@b)B*|I%Lg :q`n@+ON^Dw8JQ>G^HM5hQFA$R WSHv4|0{M&L $3vD}֌H }l?J% Zq z' /ߦAYN+ć&wxfG{cѨm"NLh9I ?8( ue?Swmcfol/  Mx<0"8A[OR*m&t?Q@(@3ǩU yXKc.ɰI>GXĂT??vпk`<ɃKy_i o#Q7<<5sNrƒ倓TH 0s a:wSl_xjG<4U 8; $` !xҾ?IS6%G mIp пkSA'㿤, ./7 QO$U#>drroPJp%)~w`ax07)Z\N'?O3:B `!D?y|^8C@6HaI# `?Sm@ai,s[O0!N9dp Kz: uvlP[@ ' CzOt8u8cs(?0 EB<zz2!K ?q>HoPh2 "] Q @T?H5ٓ)?IsÙAMM|2`途O򶁰U6I`#,SNy:a@DCIxHAA*jtSF'l;,Տa?#X!x]y9 "zUu>W5,?5!1(wEP_У|L|,}3y e= ~e Vj?^3[UEfW>]>K㱴+JO1ͼ.pzRib&w*N,jհ¿Hb,_V3~ [4;:aߛfxƣo]J?mMr窘Ն>1#Usժa ܙ%'"Fț[bAo5;f/(_إRjazIN֖Om~--1T!L./,2.zW/jqnj! i Y-[`S(LJ7ӵn ѐWv+O^0Q{YKw("S#~ţD%X.)RKEy^5\!G*j+^pɊ2_ Mgv5!ɑV [9Rj7K(GxF춷/?9ʓ9YY9VmQOp˚5q7g j^!V [`쾄ēGfۼ{ඉY(thnЇ6%ɳ:+ғ6"6sCƔY]ŧqg[DkI \S㛊whQdl'ܱZ`dqNK.1,j|soVq*y7@Xmv %1H†k_:ӮN=Jds G[Lxѷ /f,S2'IN+r}a_Ys~Ay{؜&Fa3^UmM'~A$;s\f՜2djհ79zF7rтon%M䟵LAqwպ*t^I:w]%Ɋ|G}lWm;xp{oY-`ܹ2nF_<8{o]vۊkO,}QLqoi`Ƌ%:TIW^`'M[{ñ\bN+]]-N툼--ܾN鴏Ƴbm)}I"hP*6WOIk7fn?NeD_] ɉare DJxGTfP;g6}^{HKBd T#Z0#G iEvZ7쀸34>K( We]?E1MBqw>wi:Odmٹc3SJFt]Zw\]]1e@1I%w*'5\I&heYD'i7l |;ʒ e 3~:HnGgo~f;Y#'kkg૰U>zB |ꦴ=">ܬz׵I[-7'7AVyJ]4=&51 Hŝo2ҷnw*$l\%r}8PiJ~ßV>jݰOSkjb&0$йv嫸@|[Nfo'-2fuѬ \hD¾֢-Ihxf$jCq:49 l,ߔj~VֲZ6@`4̓9\3U"[~ѪYQ %n]g+w^8:,>{Wͯ%QVlYl-uԭ% ۯBz\)ӐS`}W yuF y8&!k"x?vD)Dm9zia0)]"R][jמЧCМ`F6.G&5ʼ<,"bi7o\g;o brGQ>3$iqcgk@&aG"7' j=LXW+!W) ė-yrףggTp4d U tNdhޑs{ ~U)>pScQ^wd?`U-%LޫxwOQ+rגYKV&vh6{-=ij+; ͭʖ-njoQu\۔)JFV`@f^V+ /t&o5 :cCry(w''@e?ei-n]f,J{5 ,]VUЯ]qH~p׶\+c;[n S;򒑅 ^N;/-1} eTtn Fh5>nyům8(zrB{:/f E^&2^p\ٱGW;&VJFKfT}pSL\wJUbM@T3)|#X\B^Nܽ0%ɻuY8Әkd99Hȟ7Լܪr]us{Zmf A;d5Z& Rq̇;.DDuYۭӯqGp EK^t [Z73j:{ale^wIT\pdci‡f)qZr8j!Y-Dlj zJks٠JYV0,wYbֳܕ?:CMՒtN;u5y!ړϭxWT瓾4b!:㺾nΛnH.N҆Q&2} ^C*js}S $'7e TG*>I2˓^mVkWKU>bVkRL|ddf!)%y|AGF=Х/Da>w!mygLt;r]lL^ J"4iw};o:ci >wJá澮3=uJ)Z6Sl1?qlV80O"LtHȈgv{uX빾綣iZئ<{eܫ}UiY2?hfMVȢ!9_g&]=b}]],BR9؛yXNqЫ{ݫ3l;[,h{֗dK^iHũ-9ڛ)Ժv.uI`bό]?x_%iאRˑmKx_J -y͚%һ0O^]STP\-Dm<ː9׊qws kW6R&3)(%H~Qfls_fxwGɗ.H>&e}AX&>ZĦoaSț.þS΃,lMto*ˊ z? GR6']24]E'yi7+w ,aͽVr轔ݎb-]7q@ 9>FoEǾ>'l5?9|y'44cJBmpŪd|/f:o -j\(0ĝ7}5i;373Y4ppt#,qڬVnTdU'A>u=1"+rN_by@p5 *79RHHЙW*Jfs~02'<EX-GFݺ["ܾg Ok>ؚ#}ťS/^g+߷eߝi?3oM]ڙLf~ ,qkĎe(xsvv!禬~u؎O_l0>U/L>99C!i\Bׯj,V2!9 `O ѓ$}}sNq27q}B ݺ4&d'ؒ@u >u01nM<UҪTZT\yvmӕ?C]>IME\Z+ XܻgaR! ""8X*eoT&Z 6Lktv6\ɧyJNA@s0?D8 Nϼžhrun0NQ%r6;cުIʫEo8guGF,W ;Vٟ*wyZmv1B^:rN~[m=g8_9CKwJ}yְ_ֵVi3+?Vo`fN~qky˽eMmmomߝcjMpDN Ď2"m{3eK [zϯ٥f56iﲽM묦LubX})EF/yxk~Lx8Z` lp?qc)G??eo8;KH\ !At|1RRvQ,xtK}aJ 3Kvvt뇇C'ôO'@=fg8An 6Af4hy%}z١t)uԔf 289`<8$BOxZYalpLmxG7O"N ?C $ Ç(@xpb(Ompsz/+q=q.,vJ8,L}KQg {hv8\ ; e`$iA!:,+?6oO~F|DG@SS &3`ǡ ޗHBKI F;)3HAX?#B rhoREKѶJsrH'tt΅_j%4Oi`З0)H' j[90&C 0&q` `\0DOSǕ3p@ntߏK@]=5+ 0AJE?iE?>~R"p@[KVH 'hw4CF3_*h`d`S=-D- eqHԁV /bN L)B+h^}xuͱ? $!ZِF)^? a1.p2b lۏj$coR 9g0Xg@O`) Gɑ2O-&=|d2ĿG%a@Z7j) =!?'Sp cH#R]=RHi~Xm ?=@j8j"pDJraDVSOh`0HvQm9:DdBO})?i=83L-;uX옳-y0z;PZlp< w%4L?_>HHw@\ 5[9eB <rLrh]?mȫG0PD 1RO0CO`l~@ `U xbi=5PCG0&#~k} Ԯ,#`''(^ TMw(a n qs0Di Z]-v)C j& X58A_ۣa##SiF L wGLA@_6|lWE@>}9@wE!wh182ãi2Z!l$R '7Em^u j?:c ǥH_9K0 QDZ= ю>K#?φ8#@+Fu2?0 ag`ѣ ;ރq0`;"´e(dc0$#S!J@kO+Eh #(=8a.#a_Ea0c  zF  FP?Z!C st=|XOx5Hr.zxg)}D пzl6n#x] W?K-~MM||s>}++J}<|ٳ-x{:~V] O0?n^A!إ舘d~wH3υYj92 b_[[7{y;[аcWޕh~0;Q_uYc-tӱ+fW6GJo5uƊ2}Q׊> 9ݧ|Mhw%#AF%Knxw嫕5lp >׹fUbܐd\r֩E^{fè:y+lz nZ1d0cH|\n_GuV"oE\XML}c8m0~a ]m{ΏC*Ө{e5'ם |\xAFR.8!@R$ve"Vss˷ohᢲMu<`}D5I yȧɣ=oO8~xrK~1[[N;J㭇v{Ӫ"?Ctl^wAa/wc3$nRf9i}'rZn26N]pfB7bkVh^oRI@M*ٸz%.j9ʮӨӜbDr†MW[V9\ ~~ b̳'Q:sv@7;$C^mVw=K=hk=Q00m'1q&{z2W>'Ae](wc NsbԐrLx[S)rgN![.yj;3^ #v}:zktzH9ܭYgm-OsYEfe(Ss馺) ]cPTvli"_aWrAY]T?Y8^wgX.>02<5bK.wv|؊mѧroEJuTlWRwjxb:Z Qnl|kq,R&YUNѣLygz=i9I^FӦ̩ɶLt@%zW`ȟMU?c^5:cϢ [it׃$bY`]Dݯ_%iUSR.P+~oljwkk$?fpʢvo76wo_0XPUVpFߒUl<]d)W|X!2W,4B3_>fML6饢3bV5Jۦ5:dN ]?睚iAwߦZͥ^X" 93IYĊ2Tը7]hʂ'>11YRDڝDϔY ,17jIg{0mAib[is~$]9ɔ`&g-M'住֮moLB_d,k/urkA`|':2kGvتXzEܳ=%kk`՚Y~tNp;+ܴ3ʹCol9Cd91BIEx݇؝dOܳ*EKkldH h zWaCjWN,_x]MvbjWM 3ar'fStÃC&½> f ]~cSbLhrN7,Iؿ!@Bd4)X.:5I{GV(phq4'eS+ !)mɛ.bD3g#5 Ǻ]HkI,||wVKvݣ tU|&=s yE #X$ף$*I\-n-lHw|ݍ6gwPvhnu7 hdi?N$6zPuvrߪIy&^~{w>C?C$}9EÆ?ɥVsD ŏi{6%ܬ$U8#:N p4yśؼs*]4ewj薔jy T8Rb;1'u48_pɁ3/1rmtɼAG} :soUyz޺Sܷd^3ף$q*1>#Um%UpMjG~E^Zf3o*VC靴 ^[~N9y<.7(7:cO?ז4z8ٻl{~¢EGR}NQyrn/g&# NkdE%Ng"yI.#Qfi>a5>.lձ'bw^ݠԪ%~֗e# Վ."MZ0{CƸ:˧4}>qUZ^n>]924C{FȎ]a_?NE$-MxpHNDYV:"7ī#SS$uJmo0gnyR5 GIp1^Ӌ쎀~iAg}~Lf/<%[~u{-%+9Gӛ;;?Gj&|YzueNjQ\PM{<7SB1"2 b%op)sԛP gUNA*3.W5hv%UlЮIuk=y#KB_\tw߹xWe Ew*ݨKQUK]5sm%6co^?$&ZҢO^RQу+d/GzmBCц6[]Ș5lMu?Ѽ!cyOjv1.(mקz"rONt>Y{actFhST3 kn:?8|(R!$9͊2wnD&'Y '! 1_ 䌳0F{/gB`ή[ꏍyhgP5N=QUr_t)JګC~o~(!2~v  9_Ƥ];ّerO~p'ѧ3#ﶊ31wk*GCǫDW qTDbg? wƨɊֆGaTG+<7=G-FfD֋^(ݝ`~v֞;nrˠ*Qh{Wm|WE6Iz.wԓ>.J{ԧuۚp@U6@$Fqum#C82I7j/;굗 oDʐy݉Bet0 @ݛ6+B-"ڽګT)ڪQ0K飵'z/kɘm_kەykM>O|Զ53 l|0噄3 D$L̕FgyOwdJbM\À~UVs6>_V$%u9qLٮ&>/W<<$w;J.lӖ.ʁIS^cݫ==Ɗ|~1x`lC[g_ *z:B&wƝ枽-:RK% ])$ڞdUlMqV T}aOw\qkbN+_\3ٻtFsBnH_.^A@U~vfsǿ|P1QʖqJuMJ VBS%͇qOe{ct]TEɞ̈́5w$|Dža6m4f6x̦c[,TMpw%$ix&e\rty-ݳoww0J@atpeW }{ |RZQrVv^:ۻ`H,%!\1ol n@I<3oY5f+G'4!t4TlpU`Phz.SUϕcu 8fI?*'*cKnQ֡ >X,?y%</savG<;W@Z>(LWk`$BʹOBR~-h< /g=8|%~Rw>zJMC]x)k)b=G]I״NIVSw<{isl0`ZJbºas&a_߬ޟcviH۷|| 3h82sGow<4k(ťy*6 b7'Y_[Uf,pT5f׮+Gw,4 sBT9W褼X4Pi 5L<# բInNQHˢ3qk[j]%mPKd.=W}j}3)ip<_\Iv' 2'҅Bu_bv^A0Ey4Nk &b NX)O"`T(//Ĕkmoj)L; DBoeV|-oDWvEg@1 oex!ߴ3%cyJp7+?D]R-Ըk9}/]:R2}TXd8 w`9O?`h4Wr{KEx,[`84֞F`aDR|"U#H RL:MhϹ L`Se?c8gO AĂt/`9u2Ȉs= ߮w @4tCdY!X0bol iv~-AD|`pMc50Eph%(<ˈh=LL_I,/D`ЏD!|X2.g^A7&дK/Eq=` KCI?\gH  &2\ zh)>dQA^G (< `$O@]& g{(Oa[b4j*?18O=J&HO.?u8 {$[;GU4I5av?68nOW2ڣ(fV?t0#8s^T'E0xb90&ؐHb(pf7 Uqln^F 9mio0 (T4_O0N` bv+{8SR E w&c|xžD"Gӭ4 % `\J9Z=Zzm 86n`Fd'RaceK?K`N;?[C4sրQDΧoF\FIw9 HӂD l0WC!$~ >.%Cl @;0I*c!·Ɓ-dpګ -Y Đ :-hLW:n|9R?Nc !_h8N687J \3~qTcFL e{-)\31qE` 6$'13`0ORpjGܯ &[SGX>LsFSȌY,C=*>GZt2 q[{AC/ *<^ ƄGa0:eL gm(`2wtnDXeN8ekV!0#̲?iFDRa,{`v0H?colܿv8f6=LBDr D4/C!۳>'k a4m{@c9Oe>Eɳl1+/1GD=*!n {CNb-s< ?K8<+Gcm#X[>CЙ9GȊ ?',Sra5AF!a(q-܌`H !9f$*p8 @pao\43 R}`4eY?{;*eO`Z1$,ibd2lMd! oa(ݓ}L'FbQ[َCD|XĎcx}oKBc^YS8?+2>GlNb%%ka^rR 6MښG2~Ɋ[$bG#ܳd)'ͺmV XvNzjqnE/$6bη/ڔ՜^)kkS WmPىC<5m}\{svF4UdznK%O\nUpcX橹R"4֞TP} q1Fev.Z6 W;C}H38A2*n<-=̗'w]XtH>AqݔIcGV=-w} zzX**95@I6~rcd5Ao^MnNhX~}HcHԇ&5y{-=IP{EZ#誷=Fg9CNkhl'.ioӒrg =uYgƻ>6^9OSw*^|?WS#AszE:a߆;ԧ¥y>ڑNwFkidzt%¾m, N4ץxT>WY%.}5Ec s;7.Wbmܤzd վҍNrs2|zny䎜5:KJl?:N?U#"g+V;~2jEULbH/,IHyVotqZP#9=ყWºOn-[Dl;yrj3muTGvrމF,kg.vox`ac+/㬏D֨WMs[rR'M%G#/+IA5 n5 _e)4{thqlzkF3}5{ţdī8T\ 7΢'m5pS7^z#?m2'xym߳C8[yxdNi,n7|e,Zo1A /%y=3p:Ӊ8x0eQ97n2Rq%~X ƫz؊b5^cf$hIDAW}u,ݝo˲)2_xhv}9ƍsz\ o7{'I@>T~V=w܊*BBg(k{$Vpb"-16 +MBYG+~n,;;z~>rPy­K<"|]/KS|g>]+Tv 7nHapϹ2;k*4/_M[}*;MTڦ͜^*^ ^6#/}¾֛:^y`x/,װn\jGb/rꎣY&SƑf_?)}6l}gr7~׬M+r& ?J0[]!+R qqr=.[)'x'%bSz{iw ^U x$Z\/ӓ+9p!rK)*;w㎬JZO,J͟~[ ; oz]ဘ:iSw'ۯ/$;Ի(nίypv̢m.ݶ&(*(M-~e=u֩G z޿^v!M]'X_^}=-Em6VH5bq!F CrM]kW_}06ϫnnH%>WQZ3Jwd|6)[ X**VvMY渂tS-5)]3'[#]g3V )zu®N^y4qa/g '3%ξk"y{t ]ftÍ]Zf 5УIp IilaIo%QkI'xdٯIZ"<"] cO.X-yg(,(]b%PL3^3wߺ7Er&VM.q/bq%9Ξʓ\,X8>FU1Xoa@ 嫣:OKq5(8mjǬ#s; cg]ϧ] -[q-8_ =vg]MXs˜^,.@@ّ^qS( tVgyvݦULN=|L]}ɟ<;zMt)>-YEյ_qһ'n havy /Э|)SRnn PRY^Z7U!vaw_mZ; ?UTNOG@g9p!\=5iw {ON<z%[JϜ 9inZl~@=S`Tnɲ]NK!j>_gꗪ3',zsUC"W~b] !աlI9Sj~̾c}4ztPm坋G8X\љa+ʑqUsxArY=eՕe[AMk睯,{ue~rUoQ̗ͩR8GHߖ.9[ͷM O>j- ǒ7-HhRJ6[*N۴Y2[NHmR9X\!pO{kqѹՊk\XJG^ی{# y`lQ@$~@#}>km+"{KzO5.Th];:kӐD57Cm9~c[@кo˅wUQMǥiXܷ0xq:T9K'mA=-M]'4Tpy\@Dqߦo8jkj &ݵ;cF#O8Z܇*nIfȂlqr|p`)@9Qf:)i*U?}1NfjvF$؜~ߑݪ(%봚LIKti84?|~˅ǝ0&] jqg,F.v)x@¾%Z͟w:T'dCO{v9y^-íU-ё:c7Սv]S*4j==ͭUZם#T͔/n+iIAMԣNn񛹄ڳrxkOnMRS`,E\b_6W7:LUYnrW/!C* "vR#nݿJZNOaF.g셧MTJONc`]LkRCQ| \Ri$h:vћzqiPx9i 2YeW vm^'zm>ĹJK˔ԟw3!:s+ߢ&gRW,%$66t䋐ۊc'rqM ڡ-j%}!)3$M5;;04䦮B2b98M]fqJ1qgnbEzmgmTV +{jf4A.tH|H*'AV k恙;R'wx-Í '̫}zƅ7hl]*x-n#pz?%NρPmX熜DdNl<q{;ޑ'|ˉOxX*~DK[6miܘ{&Jv|ԁ;2t-5=umkgSqJBn跱ms1.:{mcrosw(ߦauөυd\p{EúSK/}:h~bO0R{Ҩ2`m+a]7fPN'B?s<<->|jX|uSt=nPYELEW5sy9#jD&]sњ0vڤЭ"S)5:Rpq+n3w:-uѧgS"[5 7bDf[N?!usQn9p. ŸF$gg,~sW*f㛰Z.˳kIep(tN4Rt4FyufsTIs Oeز/E+7/,QD5뉢w߻Z..<`;˙\4_d99yXI^L[ ?<Yj.V?a!r?R{oc+Qu&dK"$G 'jHQ{̷}[#؇U=];tRolnͣ{=՜iwfÆZ3Q=D U[}>U_}؂m_p$#{ +JMn_?rqv;sQ;s6ea@d_<'Zx<18{s7嗌&[+[zprX8;&uЧp(d*y28~D !!ZIӳF;s:9,q^%Džcd?zaiDUϧx徊7vZ=w۪{^/n}`M`[X65fJ"[S^I,~K퀱uݢ4ݐW_ݨ]W;#&%S}[wmD=r-:^YS7%u}gE;b`b-?ҕMcNbF8^܆.rKR$6IOk>7ȈGk8* qh#t=Dw;]k&k.%JT*nq-mGOS5nqdoqjLxk0){ƾ C 39p >LG[8'FB4@vخ0AF?q %?j9p%Ғe"}^8\cC ޿a;z")˸C @r tAp1p?2)B90UNA#7js" Y}0sX Zv&coQSO`;?ƶ @wlOU"zшN>v",BŤ #lOAX 8p14 >['@AD-A |]9~B΍SňϠd@?#ψ;eOX? h)8`O}m?)x|$y  tKϨa$ꘑ0#I ?“f oI}>H $I$2ch@6,cA``ĠpVj" 0њĭXD-[r;gw$37?k >6m0%^pEe!Lx o X1΃naX5XߏDc:w}loYat(@矗v=G@ο7)8 -!qPcЫ@#i!絀2PNqn!@q0!Eē(/  x`"H7?g҆XuDkNw#9>nF?7 gz0g(OaU| r0],o@ сv]Ѩ@$:ǒ@ Ap`P{ 7aGrR03+3a0 پCa]F@$> /8ð3/^ph"ND8/c6!1ma:Kڟ!n #;xl@ ٹ|AG ʿ& HÎt V?Y@Cb(MoOȿGp8"( ># \PafaTywH5$:6Ì ǖdHY8]>@ݡ(TxESÏJgc5~' '"Ƞz@'k.W")e]teYX ;GhN|HwpA^ ?-:ȸcP73- ",21eEd1yIa5qHrx>lm]C"hzC|jX zFHD&cfXP9/'- C|S!)6p+?Qhqp ` _xËh6(1J#?d160;o?a 0^ٖs(S:sd&AT 3Sa]j<>k8(x}T[Ԯ   "UTzH3&łT]AP o& E\ o0/߂df2k>{}Nz)Qa ra ZMhs,< F7?k[z,))ɝk!0o6F+̴muwyٖ̍>z~QG_L$J^qVʖQidxjjtW%J˃6j)Td޾&/L Z+r^]t}[C\U}ϬCmI֘hkx 0-jNgvzDb{.4=[K-Hr|TaL,[YCs遝K9e-rn%E;KN|(z (q`79tL  ;Ggo 2cq.rӄ*_.tШ Ffϖ}>f 2Fܙ2֨|TM}e3ҷ<:t )#*zIz&zD #^AWMyz&ʗ/Ea",f8ҙ.qg ^qIdc~{tҫWt~#lc0pXGd{J9%M]{}1=˅-utFe-<\vV̹hIK[%f}݈\>]Fm5vR0GTl#>5c4V=97'='V;*Вk}*l&ztR{ڟs&ܪ9aa WzѨ6䱞1lw|}ԦyenU&^>q=7y,9qRakLaR=J4 K\prwW-]ĝӻ$_t qd 105HYmbޏ+6& { -3r1hjF%S<4?\&enw.q2u"Щɫ"sg}5VY3akLЭ4+[b.|M7{?-v=a Zv^Lj{@_/ ۧn6$~]7#I:{*VGe|9un g>?&D$ӞlQkV2Q6%T[T5nQO8!emp!Mr!ֻnhbzK;HvjUTdmb@f+1Uk} ia[.\`PٰW +WWa˴Lmw˾ݓ'AYgvb6hf~م^6LZ;QYO߳B=o9N^/q|[KaS7 V~T%!%;|n+WF-tCPu'dMV,7q||1=OG [߂Pa RXepI-6kW[j zNNwPy U\5&*\Rǜv\Io-W/[gZ}Sȫ{씦XýVb9NfN_v* JW 6TK'HeJ[Əv6pIBQNw 2Ck>LeɈٔcm0BԤR;o L~ eL 8,rC2ldD"n'C^T}1mwm??xnyO\٘W\KRыuw£k|r0۠ k[iah<9X(朥7{ZDm~ ĭ>{7㆔.1=-o!@a RȸlX/Վ: SB_ߝG9?nG*F.v(W XCzkħ;n~r31j)W$jhHчˡVzfi|Nqo' xK`y9}⵰U&R_vNyo+&IT쿪`#b߻y l |n/ûrWMvXU?<+wvl_O]nAw`i0;1Fe":+.o1rMU?e9~$s颕>#lSe6uOrI䯖[n.b$V*ZO^1"kF;zMv>Ik<.?-l4%r~Ǔ^Xt53HYVA$fϢI":Y n.f)%ci7B}n׫S0_sř/׫2n>bpeǻ+M]Qn|]tC[}hUtJe.7]_Mz{),zk9U[^9)'91g0{o؜`@Gz<"}sokӼ^5.O48f7M'0X$lDϧwgу}/WtA7Z{OYx X>$ x~zvf)Ɏ=9^\T+z Z*j83`L^%'|Fү%k^ו1t+蕷nͯpiG^z5JېCwao}7)KC[H5DiJ{k[ԍ~;pencܦ?5l&:U̵u 濇¨H.e [T}>;,r[U}Qs$TgPԮvpi0z)b^GM]6 4Xmy>VvzE5>K{:lEޣ[U^<-}(p$_>oP trҔiY4Hj[7?nRVrxr ZR?gV?:b{(}c)Tv–Ad5Hyy'tnjSvg JKkM2<ʒT|^ћr#R͹*O6s od|4gڂ2B\?&1d˶} ;5΍yqA`Z/״9fKy+Y讨i.n22HN"Ӽ,@j0VJ>2?*.9 8RV ُSke{ Fu.Us.|feˇ +.LVO)I2}g0X-a{Lͱq銴k2?7{ikyUdM]tk5d.7~J]TuUӕTi3F>^!jMi յV?Ps W-:Ǜ;ޢ0Tz{aPLr9o:(%^N٬wI2ޑ) Tyt.*T3]Mp'4gy4w,5oUmeUY>OKLLm \ tu:һΎ5*2}8(3=ݢ榓0)1nɪFb NjDaK )ZoIeMSc[{5S"t86Lnxj؉_qզIp&ݒJ0{*{8ܔ4k1r4vKi$SwW͹n欸d=37c:#7Qeb{&Y [߇_@tG'nj @橂#mдU Ƥ?~y@C*?~a{K&H*OWf^F?ÌMrYbCۙɉɠj|1wQK=|ʼn'r.jI2_^;s +,m&3{t8Sb@5 CT_ﭻY27*l ~̸ @u?Wl^~3?8Gm{ M¸A>}O~I3kIb3jmҗmte`ykn+۴.uXU=1-ãذe!gw[d'G=W5(i`}?!2:7(Y"l ; .Nw+ء{p7࿁l g{or[\wcBB˜[з0ML(B?9%oAlfa0~ ?[@\az#uw+xG" <meŵ߱0s@ȁ6oJ؉Dw=l%xjP_!f#LX1.Aa&D?9svMIb-ӓQ(d [: [1 b0=OT"\wItL#xŤpILЩC'mKb0TmJHo7*(t `1݊x5QB8xOtx9 |A'~Ϫ+n Ξ<_ oa:G]zIT?%9b{,1aK{@[6 [QB֞ :?c:̛.87n% KR?@y:`THӂB߭(dcDO0 HAA?P1NnW\ð@ݽx8 -R>t{`_j22t_ ~(z/必c!"-d av)欈0WFu2i:Rcw7޴C zw֚)./=x}4oUe%`~VڇI%SF+ Fhoo^nF7sVDȤCkӛ~Mp{[v#*03N\ʹVR7䲓4_^7/[sz%͖Ttkm8~^_ZBa kC~2sECQƶ=_8*~ ލMzj[F^89fז /S^}ݫejC\!/jn9~kjl/ٯ`ESL+|T;tt})Ng֝cV֜d?)- r$-OpSn$ L`zʺ >/,{+ރ \SڊYa:L(W1\p!VԳ>xQ9.}ꑳeӗn٦svwƧwy=Ro=mHCGx6]^޿biJ[l`}`;ѭ9@.~B6p ѵm`Z䔁iLCGybd̕ &[PiS?m7 o7˥3-KX4ݬ.9$p6zKAc>MKT}^~8z];|G.JdgtsraQ;M_Bٚw0gճ܂o+9adAǬnGNKRp.kAjA|;nI2 : SD;UDw>^>܇Pt5h8vdߞ1Oi#~ M *):.dbl?e{wSخ9}/we[=Fَ-~kuOm& 䎬*3>k\U}KN鸆D$ؿ@%)x'e}ĜaB 6Az)6Yҭ:2 @~3bg-ɩ)K^+'\x]f.[qo&LQk<=n톬2ElLV>pQU ]k 5Y:g~߱jOdP6ve]1|iwM6l0UL!dM&jVD&BУuvܲAwbo;j}p;C3/ĝͩ9Eru+fp"f=8{pXݷ(IYSwl1 z =,B_v3Wu Yf-y%=V>$E~Nk:F ߖs`KM]D[mjXej 5:Judݶ'UT~?guU~w|~śW˶[u$kp\Fjbf NyoFz 2}ڇp}uw<gn9g^Dm{}Kog~XqCm샥V2*;m]5Xft cɖk\{hYoek4otN"@KoP[÷8EZ\Me@<9TR‚-.C?X0v<3~[r-(6j([;Z#x駾+$%x uƺ߹%gC\Vq Ouk嗨 .wȎIepTomqgλO:gݞ8cwB_*A3Xԯ摮Nnk'T-2WƑ1/80Ya$3╁:V<_yYa͛]5Ǎ V;WS[w_ @/tKs`?N Ct> &\8tzXq G=>rV_ؿtv,L qYsNԛ >Aj^0G>`ψ:=>+"AgTPilھeeӬ S6~RR@N0]~gK@lRitK`ĥ[ 倖ka__9>Դ0kc&श=ũD8#<"pe0-!h05NYXKk Ҕ+_u?ޡ~UjkqsyOozrgޤbkCR56\4fCNh{$y@R_+GyU4M$e-s`'fÉFQs-O?]@ 7pA0G]c*~qǵvMY lSugx{6Ю3^:xc5lkȑ3o٢y wSB-9kW(\gN{֢G6% )Y\pUlUuk>{1~%ʓx)A' 7q$ UWNtFuZHmZxCPkH< 7c UEfjI$ AD'O?Q3p\K-˜iBwN]zݰUΩ<]fFx@zWG$-g2zحᖯ`JO@H3ޕ*k_Lqk#TL5WF䘘m΁JY010{j+PFjNY;Ngh4wVߧ, ׉/=uS2۔_rn{U^h!e.G]{ѕ=׼Mc%}]*jW^N) ;ѷ*"$Wӱж'{РbYn1~KEcFᣕV૝yk)=GfcGf܅  ayE#ۦ+vg ev/2m6`y:t%ɠ *3{oϿ76Mm`Nj]HP_c7y-</;~3/&~)]xi}P//Oz.NT,Gc:ѱ~LR7mN(<~}_wq;T~\qv5?cgTBߔvE/T^ҋ̻fgӟ}G'K#W?7/t| ױ7_1&iENcR_Ƭ`@~iT^,Nyg\/)rUk TpV%7/^"M9t3g7#B^~z;~ϊ^U|bϫ/V{^~u9aUy wWf]>,*_LZ=ZK15޼tKp$w{~N rV,`YmV:v@[ǵЮ^0UqqtU!G;qU!P?`]=hd-yruk3Cqj |D,YzΫ4,l|UP2v,h/c7[*.>r8m8GF>۾̺b(elX;KJ܆sD_D\wP/ (]ې'Tp"V917D GA[#YBxpy((1%Krc}/D *  /E(;P4Lڿƙn$$Rd lT(!ΛQW0]/h-΋d,*DpBm۲7Q@}B4%QB|;1& ޙa#UL^vP ( bp .pA?.=NH'.z n a|Bx$> MKR;?N]\J$ Koao(wt[F,L4-ߌ-i C?2 Gلkl^)`G}^D]<3XA: C~~.}CY.I p0LlC,&l (zq1b&Nkx ,S O`\|\!6GFE> %T :AD .2@rp.7!2\?y~j Lǩ+ [#7w1`  P^S^PGACN>Ih)-)tKS@A"`ph9؟)rQ4H#7n)ć0aGOFv8.r /~H>L['%yH c/c6dP6J.>5…8j( Զ19Mi~=kxT?'iH+d }.tL@Ko!H[&~fCQ}3 8A\C?6 Vz;x‘@3Ӂz!|QzPoh#I\ }wamu%Hɸ.pYQ}_|E⟼. P1D1G 8a:BB 1"~evMȧf#sQ DsiԚ6"C?};2HR^*G>**a|$6&z!a QDqe䀩d P7G2cxM r4 걂_J#E}'=x~S 䟬g&pjKp!60YIO9W 꾚?7՜dFJE96A*l7&;okrD3Hŀ(gan懡\M@vKv$OzTG5O,! g 8*$)*Ga5;Ca.a8F (C\?qO4@8)>8;](^mJ7 ՛hSVO;[(j'MSCF FgCX%woх'7\P!| XvbCNGLPDs@G3qj:h#GPH߁ !&*Gťo$ # 1r%)>8W @$c0I+)"G? k!Zk^vCٿ/P`¢<` |mlMPGg m9$! D!#(#WFC0q'ڍ>I 2b׿\~!X0q>_!|~?%@w_a`Ŀ/nDGD})8xړNI)tU.CZKCj%/kZ`o\@S 4 oA=>\? L#<qT;HDPq`J `O4~l??4Aq1@'GE' ?FmPq ngqݦ#\'%~{#0ZuO0EYQeh& &6.܅z_jra` M~6p1!µp("<*Xo'irVܤ2i8 5Gą(#7ĵLOQ.#k@lʼnX.0zp7NIF}Œ6I ZK "[o2[VQ?) ASͫOz#>~Ԓ@fR@(3 ~gYHUlip9O,/и<E?B%[[1$p5ez5`ZxӨ9@ )4 1].M*bEln4aSR)f| v4z'w9^8\Jn! > cQ}B$?U (r1gRˤ3@q!߹9\ ? dru_6_@%!P?"ul߇/n_ 1h謖Ig*ZKtK@=L#_{elbۏB68\.5Yq _Fb եS~F5UPxcu@5 w=QS<G4CLXN$-8Kz$@c܂s>ٛLM!"`h,X\arYs` P.Rn:OiA@/^" 8kuIR}=7pB#Pa8?U E-,\ BR C9F4jN2_R|#x}  AH곆~2A;zαD1~m9+ 58(^=q̭۾ك}?ϥxg3SU ^Fbv9" ~c.wZ<.O z}y=NU=; +ϝS wF׹(~SzãeFs{M|*9kl, .Ue]k 4G4Fv^tu.}g岣Uê>ؼ)@'_qGi@H{e3L26'4W6侼8[a nAգ)4[% 7[K :%ls*)=en,FW`xmJSL?+&r0cX`8SqNޟ$-SPG5Jz˦ZsX,"\[ un VZ8>ф8seHdU| .F괌[q.kX܅qz1O7Ng+v`ѫf_xC0'AZ+?/9tuw"a7gvJÌv.E6S^94+!~MH# LkverǙe8 "a}G}0r=8V;/-٠Q>,7x|Ѻ+6oMzsӟ+wb`AN+қ'3Of<57gS^ʇ\< 4hzwrY_ŒwixdS&^̏n4[ngW^xYeX׾Ҟ@fJZ VY͛@IvFݝRmʼnğB'$87С݌vPPYT U쫒^;v zZx9_&Aw81XS6^!5MYjmjΏYo4q:uWOa KzcΌQдsF?Lw_g~qsX'6fI{ e^#ǜ=FW t§ǺGP[%SW>DLb}ٿdS1?vMˬ;Ь} ՎҐ.5ͣ|U_SOw*5/lGŕNy32ŵQyq ((Y_|ͳzӺ=7"9.+ tQ >RGucC6\#mpxHخP Xi aLz90xMcJuZ"97sX|'s 5o9XWS/vZ;@ \}W230pw|ջz>s]WAȹӵL@=2_^P,%Hh,tiqا3}e1N;ظ8~H@KY3a7"ϞZ{>L/4]a@IsUZ6Cy`:ܦU2? /|3dI~G>[O+?.e߻}= 1PUw>0uaDhc݈]kVej^ _"469yLd}Fz^0ď?N#q&7Lq|O'{|% )55l"۾669^p~3LUo$(4^A, , ~fﻏpx~ҞCY,ȿP{%2 ݺmX0iRI7CMYšxiy܆`YQ,tʫ,N,IE`Ӕ?sw_7 *lL/U_v}kmwbSsX'_yr /\h/zt~ ize.ey<3]Xn8v<#k^(W\fVټ~O//:o|Ik.oLYs#`ӳPv~# ]b]]sY S܌8:@M%!4ЎM ǽkgw|DrynR1sW J;{O}D7>!f2dC( +H#H{2Z=ao4ģP81}؞IY;=wn]x 7PcMu Rjr Y7\W‡qTvXv`Aݎѐll'5_;8q"E?rg,K۩1]÷L65k Y4gI~;۽+W$0.؍\p3!`IW>v*lU2)NyW>=xDtc8/'AA0[]""BegfQ0-V>9L$?`Q4r'Q!;/G`%i@b"0>^ht$2LI~g<вOo?꾹uCzv9 ҴSOp{)8s| 5SMv;#k`뻆qn>x3|ğ~"uhVQ\:lvfA<34L1vLf'u+ջ{:g4Di>do+Vwvc]ST}5 Zoxw jQgGML Z[~#[z| O>J%ipTsSysZ}\Ygc3nvU՞]thͺ v蜐2|Uu[͓EAiiJJo(vW @˶nRw+]]L-=M=1cs`GEǫ4M/^գ2?jIdJP]$:_sG}Nӭ2%MU4K6Q?Cե=2 92$j/!,3.@ډΙܸP;z嚢)`yhiah}z%X1.:e˜`RuC1N},148C/Ǝ/oG;e$@Hz"͜wŶSiVѨk|ûIN'r_(&@}N౦+ ;c<.)sYpa[Ҵ#2O4,Sb 'I@U}xj'Of. kPk'?>ZaB㜴+svTSݴUf9 4+QiÞfgMݷOG;+!]%?rSe]vGl|vQf!s/_ iArdIt[oG\ՍǞå[`gowp4xǚ֚<ڵOLji]m*i89𝹌騻auJ36{4(Μzc1FʕՍe2߰=9b޴M>oGr]x{Ѳgl7lڞysh 9ߍx7|_/~>R .*)΂z]˪'\5@psdL4Lb +wʻ) I7ۏaooigZ(ޚD16,kk"[DXl|B#T#Ĥ}3(L R1HoHܔ,m*,h>Q 4LS}e}ZGI+?q6 7 آa=|࿵4L"/G"K HtliAqHPhi``6À#!'(<[۟gGF3v162BA&BT 8Mx0Q\#/2dV#h V]eHOQ]BP1 0N( VBȂqK{&7%_{(mQ?(F#ł0Y|N+zgrE> omr(3CmLKތȯK0ca}Xu NQ~2@ l , e3e%T(m0_(1qaX.OJِ9? `;J{#doPGt$A Ab`d _1L# 7#T+bvP\䫼$8m#0O hmE@]d3&4_@!N?z"r d 4MCa Zxt.UmF)ǁZB*&3q )|aXTm'40SjeXt0a sr%A@̧QGPQ/5qEjh3J.mZ2#.Bm%>~/@,NLCv_lW}?rx\%Ci<*熶T@KOq" jcS @Q̐Am6Z|a]Y!*XL9/Ju& i|ai@ ?_G)FH ~@DAlB 7,B4@WEJP٫e>,y,ɿ1nC]$!.b46hJi|_,6? ,Zj bl:uCp 7b (Lm= lzhu ?rqqqaxB ㊢?-[@? N"a {l:#6 OnC肶N%.,.dLؔI{ .!,d8@CE>A4$ ocl 7QTq{~)%lmQ6H'M`y"z?C.H0C_6 V "DM-@ 8 h |Q ^Ȟ7E#q+% %-Y<¤9%1[ a`dp\ NXk7 p2H{18oznFyl6?p)ןtH/hU?-?$ 囦Tuapc?m/D]`` Y*C!x] XMϘBT2)B%SB!JIH4O3GC$%HTD ɐLʘ2S|{{nz[$?a#q l齌lݼYO@A#][fHU<عaJJ>ERXrhf-nHAGߴ6XzȏlߨhLߺ*cYu8OnSq1ZeT:VɷE4xS8Թ&Jr]1_4Th)- 0qJg/JRGAPN11n@6KԜbU: HrЋ[W`X F NIHW,_t9AQoLȞ}rc[hs'tM/poGId꿔1■5%Tt5nDܺ(}dYN|G.G-)*jaYRF&em~~Yo. *cc<;\h6aݬ䇾_x+3P;^ڐ`YiGanYe;m7} FnYU.D5i:\ٱ=ɚ,{%ݓ.ЪpE f.BF}규w/MHmςtFf̿OCo__]ZWKrޤ#l)G"V UWBQb!/uf%h ̼!n$nsmi_O+]gaKxNLbd*jfֳצN).W;0C;筓 \^.Wum)osojV>S;IY{̋7`)-!428?zV2>;VY|1F~HCB9uR]IUO?@J( ܠrm yeQZSj9>1lcA!7챽J? `nvT܆2WM 'sRi$Lx6贌=DTm_Umj_Yy{&<@J($Wf/AB|C&(+e >6(LV8~Vq4N6T1n+ wω\'UxL,0XS?>(xhZ0 A;,eɘ XiYvТ b6R˅?u0Н,y2ݯqc;17%VEv^U~rvwKo)=?mΜ!CTz|5/gB] fĆ-YUӾଙw=[ЛoipGs?2̤_G:3+iE &8.#:RqB_?GúqK '+}uZJ.brS, 46tK8QN1gqB cˇ啉KSrޘQ9꿦8 NmP+}fgSH{GCwwI\upqp/찛NfiLpYÁn{^j31RSOJot}NrR>7+;[95٣RMBNA˓>q^f}uYc!Pv76n<﫥7Vu׸iFL,|K]8=o*cs4+PHK8?E {&[DqR9?_ _BxWM`읫k_0(+ߚ@8U.'Xj5Rq뽞m0tGQRgY'C_HIy9ܪC~e݂!_}!P9\ǗjmH7~]o2.L`A^vq+*v UbzuEKn)1u^e8L>sx``Źu/v]r>jos9vYȂd{LV_Oe.qdB_?¶-B1:!n$wË"jWx`eLSݳƌE&U|%.S% |k /=\@VAap427j`\RԇGb{#1sc 3=q4;z&~ޞgDFMe4*꿺vjRƼmv\zsɂ׋}h&<;4uR"?g7uW]]oR(KXrϪߑ;W{0g麨!7[Π/jKߛð>UJX1"f0oG~˃{'Xf\C)h /c.nZ[7[DlNʚq?J>}uxRNOJF+~0SN>O\k=|l@+S\#Mtax,{%e!>.˓v8WtlUssfϧ}ě0umچ%ĔEDz/U-f[x]Y5rtzXYq$HY~4ӧ=2 (3MIuk? =wپ^j us )q^Clwށu/ 'WY䶱͇)_F5@Xx)g+PX'uపT` 7ruBܿwLں\.*ԿsFބ';A$Q2 u[jii8Z4}n*JU{*Sx{S /}^OL?i?>V9oU]ھn V<1~OԴxy7+̏+sk;L DXt ="\QiAs'T!*J7]_[mBr{"b௸pXN\cu.'P$Tu:}<RnPK:g?b6q={c+7Tllqm`#F}̖]pۡ$A3Ō< =pGSo+/1lKMۋA紌Nqlx.79iKOahlh6.U5)gc^%h5lyW"Hr=cƚ&lḡU?e^j!"=j^N\Qa_fWV_PubXhezƁh 0tn }v@OS`j~J/37_?\y.;+o7hlxL7S⛌ARHS■ g2J"H<՛Ym}T{-ޔq}lL'|iiթ2.l݀ZSuRPWO-KjF%*v1z }Q,>*i'@e+!:29SwdE#%!85$?8TsZV펺T3f0(N#n ~,)4w2. I>g]}t5TcU>Eo Ô3JoG:cJylwHw[,ám`n!n$n[@'`(!~Fފ!UDtBsݏt6Àm(' (0@ |XU8SnP#()"5`A8`",l@~?H@Asďy/"@ܱ҇x$GY[f(SGn%1dHKo- ?cqK H ,0"h~BQ lz";Bx7u\Xt+;鄊A(B 5a8E:6i}i .FZ X(Mk#8#Jfl&'|1Hg^*QW  Cɇ.% 醀X(T:j-"=b<.F*2+Tۤ "ԛ(Y߂`4 xx<ɿ0 " eP?z((7_  \#w芜< ?bZGcYfpy؟Y_@$t&9 cR8Q!/5p/n ZEPdC =\"`{Œ@ɛ[ЁO% EB̓nsp(t\"Tu2}H%AZ ¤ӂɰIG z³3}Q>YP 'Oe`M6'ԑߊHoiedϑqK 8ƒf<1yFz7B@qfufƁ㢄/ 51' @v@a|o)eG?YJ, "O @LF),v`z@^Vu97@z֪{sRB!/G A2qܤ4R ?'|Mj?X1s s MGJ&+>N`_d:5 6갵dQيtdMK$Gr>BQ6 AM1Q#B@҉MwmmD = )mcD&Y4*×KOB y)S6>6j& ( 4-K" #必8*k3/ j(G4Q%@?3(@pL*p1Y*zK[{$%m@Qۆ|qK NńJܤm|J+r-J?AA~(Wa6A22p&`a bb@@ @h-%>!A-!(DMDZ¦A0 lA=2LT'm[Aa?E%J1Ah@)l XJ"ڊKoۈBqK H%'L1}Cf`hl0`G Ԃp!TI&`&%ZЦ K0A0nDeo[[Hr"G.#HDm>('LA'HL)l1/Don+mf^e/"5D8wN _y=Sy7e E3TuOa0b8LL n"):n;0K 'H %]98( 401j+F[455 ?/ѤFP` &o`xXa,+-  @ṕK-'E~qrqf1.d-CF Y' LF05B'O>< ArW@bBAL ۙR#HF%h50-?$Pv6/Rכ>A}Y30\)FlC(Fo9st\@:۲Պv(Oْ.&q (3n~GOTł͜G7bȨMbGޘ<.s\F;wK[l_'(vӫ;'zN}lаw̐~#j׫)pgsut ?qsgQXX]z4pVC1 ea:y$ Tll_^861^Tvqm:u(+йwwd'[O˷OW\LVmq 7?g9m18D*,ao7`s{EK"!Zn1]_0ǘVYN&25%2F'QvX058]voČ#pw&;Ψ5UWqZ?^r^3$=0Ь!_@-wG) myĀ8GeGh 49QKrfʵWJ@e{cpvCT#no齗4)+;hHc}=[Nz_}K6.À0nfֽ|;~>(D ^ 6֥Nne]|-va1̍10~%(FL˼guw ,I馨n⾺Y wnmlQ0gsP "ҧr$$'҉Z vUvb3/->CLU7[ .R'rT5W]_pͼt &ة@tn{+_趫DNcU/\Yf=گ s@S2?mثPCtHZ]G]uz}UÕvLqp9+w@dȳ++L`ב΀JO≽'T_˴ =HV郛ԓPkZ(myG0 }R9q;bg&_Yb}%Q%S*ޔ4W7Ƣq]^7 a&׃"qU46I  pz`<>BhJ>lQKЦfq9b = 7zLhwO.5Jryp87050e#3gg^n EV vo^Ƃe/=biSrK\]>q&8/ysś:n Bq* [74|Gh ZgyNAFq)f[WQh— r:5Iҧ5oSzH,/ ?0^8ru)f*\08$qhv[ntd}ǪAv| lT^f%14o{璹nƿfo/`s:V-ΊZ+k_`ߓ^z9-.|6o;) ˖ٞ {?h}QX6&%)ChfR08qϚcAs]d8ֹ_]Iokr66:>nKZgmj#L[=-Fr˟ٟ3DOPNYk)%.j~]mQ7{gc?j[Gj6%ytyOO=\Qg4l=4bFJ`ư&'Zu:s# ~:l6pÍ7sL&7%ynv!}Q6<6lwi wnTm DQСpxVC4Xbۑ~Tw~z Q6yocs3d'](-;(l[kf7sנ[-'-S) _'S16ݓmuuk5g-BKTa o3%3zf?pX>K-`o/`st%h[h͐ Juc.1jTCT>|!<~=8@f'LqQpHoPRCGvPX3\(+Uh|TpΤV\~53ZSP}{%6ǫ_WԳysc_[2@?jm垻:?%d{VIitBn])/=#&B.&`T13!`O`컘Fe7wOu3C)M3V5D(twrAV)jl-ITRnä%c爏۷.Ut´N1}MD-B[t$(K'{5M Â]Gg P$#6ul@j>c'h2Eq"bY_]CUMqyZ3[M>6\% mO|hMb ;R]oۊQCAf/r޼ӻxsDthXa]Ɨd ᱙[KЗ]5yԚ5 ]Wӎ=ڽ $^Xjڻ6樼Аhh{ A5sr@yIH3/¼bM. eOOI24i}S*_C'nkߔ%n@Ajn ozO x8c5Q>þ$]R׳zlQ:oMAgP?^dxOM7R[MyhQ[i)ջ$b_Xx՟O \節4-cvXY3m 4L"t$L|qg]%/z0lʤ;g 5ui`yYٚ/ `-~ PfĉG{mfk8hC6)㻥}jI_crYG}|4#C *JB }a_%L'=%aΦa#PTK%!V6}-<i+_zݔj\!Gξ{ vskJAfBMAlXMQ 9!jAr(Hh=:Dv琊4>إn%.Cf#/2X.Mao'`S`QHgK.qGA@12>sT˰ Uw%I+ŃsScO VGGէ8P~U7i&'a-/csk{ˎx/u+A[&g%\ׁKVK_ջsJW~ZW= vэGG&DXbxRٹHWvmZsp ɺ+gSb9dM9I/o}Fi 8%}ܟ gn74.;Rq8t-Gv̎iI :^%Y8t0D>;1rdN@qڜ2Z1`cuL~hT@D詑}Xx}YWGoĠ9;oq%rofW5fs"E(00߹P=&ޠh vAﯨ—0e>tϪ^uq7ƛl̾z~u8LOf7fc>Jl9"l G#,bz; o^㢷ϟG:emly1>p[5؞ٵgJ(>{BQwS4$HʯR;.5e=ZP1o6HCԳqp7Du?Lo?p PrۍQ@;$34aa7aq3h4LjuVOȡm!tʓEDtЋ ^n︭lGt)f7:]eȻ7+Xu|yq~_bKck*Ycr{$ډQ'^G-7M,7n{v8{6x7wt'wCʾG~|L Q,؟n- zCb">e&XI wrP7SQ(F\ } /1 !\ܗG}_PEG\x@OՅzrCy(Ca}x[8z.#`9|,nrhWᛊc8% %Y`󣮀bu0r#yC(JQQd#%ޥ(#CɯN> `10߹'u.qRI: ԋ04+;zqxVJNOoܧ[M]8v!p mN9^>֓8/JC $ } 0%_h@~fJtJpjU?"\0 >;@0dVޑt2{p^I?QKЦ@]E-BB?Db?? Cs:rxh;+HyeqaِI!#\qQ_A94-O|lMܿu(f"5V.NW*GP>#(N7x{FvTtT`teo/`s#W-_BTk`@?`"|Xspz}?xy0AC(KK|5W,\6ճFb% z@O@>`t9' 1`?cd+V85ӝ´΍S|QKжp i>_nA?:s'P=~'XOdo$7 Frw]BGi<䓫NY­KT@_CG-$G(kfOBl=Q]T @ ZD"t(⿟K"nV[[w ^\_p]C[7m "QF:zAZyt0]D 5 V V鏁pALHLJA_A?9(wk(N PE5zs  9C`'UެkU0^l4zߺjGpKW0jQ")ȢG[ˌ ?{p>]cE" ݥ'h\v?J{szo`_xrI\A7jئA#bz}΍ GVZ0oiNPC\u Z?|0?PR ыń@<0Ch8YPƅO ,VG3Mڽ @, mn X^T38? Xl vA wnD-B6$E>wˎ?j;6 7}!F(0)¢_f33\CX`Nosӟ/1 MTB@lDZ?`پ@:{\!絴7NBZ00߹q,w5DE-B_?vZy[z`_ijVNj N„[|doQ` !q/ ZG Ú{AO.yGK?is[o y]d0@C<8L"t 9o𹎑G豭g_kK~6v0n#>i Q6ˏ f.+$JqlD"+Vzs}?=Sµx <7}c8ێ}΍tQK`1 ~)PZϱ.-v~`;I"HNp'N|At]_eQ|/O?"`Զ G@~ N@:"# 2z}֛ D' @|9ݎ!l\'j: R[_6^zbq#?oy0@ K{4{ͿF8B:-A&aC>}9B\~k vaPAT@].)&,"x]yРSQwPߵa-l ~-AVq03n{ƒf0/.]6v|4c}ds,ʿ7iq}v7XlT4S ΟL: `wƙM4XrI4\dn7)-w\̞6r^Ls@z MNw-,}ȝF Sʪz[ykYICnoQtM4<WF*O#}[; |_.) 3^jL2TZLc%B\̹+叁`#0,`۾#`sΎi.n:M/97{-zO!Vvg\ܟÂd,#1u53ҽC؟x7#Oi |_•KOl]p;SUoiqpO9Vܽ9iÒ~ˬfŰME&vol'AA;GeEM Êy$jL[ }jnSJ, 0n5T1\\"&ycBuk'Ozo>Z,]#5w At@ɶ'[oK.]+3[ '*U3&L~pUs 8=-ʬ٧sfT-@ٜe!^z r5ViFfvH҈hY[ƲXIIobyHVxv{D4y RA,K̲Sfz&RB+NvOOv,:ƺZ¾[(qT%BڡeP_xՙFlK `tĢ}08igb`=f5gur6Cp/_Y8:Mˢ ȻoNڑp)ka;0rE`}*>)vkE\{y cjeY0jCw,`5cO}' 0Mze74&?*餢Ss/J4D #2[3X:?%٫WhG~M7,y Rm[cτ^.=P7?'`,wJzqKo\3(ºQ?l:|?@Oڿv&ЗuchvKa]<6" g߃{1qF/%R$\y/Q\b=Qq<}~/ClQWuZcR);(dsX" 8-l:za0zWz3IP˗shL=g?2kZ>4tMV -cE.[WRDhP5#rЊIC `eg45onah~ڰL'3si7фnSa!ł9Ko=!ڻS'WhN7w跴o ^OènT)bwꎬ-KP^{CYиYC,OKer5z<>=r 怜+z7lVT[IJ܊04iʅjh+\silE9ѳ J㖫>O0adsc筥k#K"|!l :}7JzSqjf8@Z밷`+}Kq\(Z\e{3[1i@\rNip'``yAC^jB&[C zx 5rwTVusAb"ȩeP} &sGD1ٹB} GԮ*(uq]UXoܡuv"`'G5V[$;9~}ˣ0ák{m.Hi8kpGY /,x0yq&',n'I и qK:rdJ'CMk1X=C L&#PwPߵ [߂9S`Ӯq"n\21CxCUC܆tV;S|>nղV]0`1g„c{dhsoV$giKd0"Os~&M?qulAcȮE2:ӳSSkd*a-rqhI᱑.[oS\e?%"JD޿){,[)gŋo]{h'q'a?94\KcsgvLśҌ6v!Wzys7=zlq:~@403ZPWN$|}9x.sF$~_rESݑEӱ舧wm`%M0F"t4VT#utye >8*זқ0slxdy?+{fl8\°{Yk)q+8wP鿗aX;7YѣD@?~o=Fۄhbth;(ſz_>w󃛼y屟we`_{+o(-s8Z,!zZH) S"EP4bߔbmL 㼜TaG+ke+iϏí^O? lo).2PjZBƚˑo c:j*4\zV6ᚏ~J6> EM-Bg@Tg5 +aBHV3+nX6Tn%L< Y]SU]Cllprl; ;Kq3#զc:X^OӮ }yL-c9xPx(wA@^WfGwd}W򴨹}aB@q\IGv_Z@Qy޾9nku,/ cj }rq'WC]~׉% tKyAqH~0~Dٗ/2*(pry{6>;lEۈdisr#1!0צA&Gk)og% eZh0AYw\&7VC͚bF)4w Vqc-]K7 5t{FՑAcFe,))8!% z-x95%QWV]dVh}¥y7-mJmSuvͩKgކOOP(߬>P3uE~ \1멅j TvnQ`e-Ϟ4ovɓQS 9r+v40b4DsО؛ӷ}t[soO~/JSip&q׫)J4-Wfa2ca4{︨&ywzѰxH uOZ [=u ;N}Yz U{ʹw ɋ=Y|R}جـ^}kKʅE+5gZ*\m]9,$Dkeq٣sUAI6Ѥig.ysޫz}yZ\~{[7RJW |3'$OfA U!٢[ŰJ^6MQ`QSFǧXvmLzF/ݝ\{nXaXX*vn8NK{OeN-m)q":o:26D~e y'�aoң96us+O89gb]ׯ=y ӢHsfGtN.#.3榍]8k(c-GNZO (}. [ m X=9OQl2}vɗ,F`x⠯p Meߢ: 31O| uBG%Cr5/@/tPߵ1p{Em*l :>RBzӃ2 ߯/553$)4 @c[yმa3e](o*~ݲaPC;iPHˇQc ٔ]4) 3m{U 9oU`vz"_xP w!75.^۬{{;ک9(ݻOv%cFDL.} ρ&6 nZ .qJEf2J ff ݛLL6gʭ`[=uλiO|F}zA[%7/&[w&ſAFAEmp48)l:<6g,yG!F4uT5't]{,[}MztݣR#N /بnIZƯf`4 ; rNNcvv絢C^X0NXhWm(7\SL񕛟ƣK  ^mϛyd^e9Wo~p@Ll؋ϝr(f}#N;1Yd0F2Aϕlof9x Ǚ'p3}fvBdV"3L28dIxhĄZ.1Ai^,6aDPwPߵq+l ~PS UPW?kˁr>tW/5(8, BMS ܚ$1p D~b9r@` u BpM`(3EdC\(;(!@mK8D5POSA-' $H7ltOk#}0~UX $A\ bn>¯'( 9^1B>0Ż.@[|q]n rr! [RwPߵGni l:r_^!Oߍk3 ۻ|;`ˁ#< Jlu?rvjڪpin_kIufB[_ Btd5H@ڞXjmF4ovۂ`bPj / w&_)i_r@"i_.ެv Q'P wie@̬-BLJwJ>"vC]XF /1ln|ۖ unE?Q"[␭?X@) LNo6]S@[myč1D w4kſAFܞ?#<WT-Nx6Z]MjK> /Q'fK4v; #A_\!Yu&~4h&Od:}Gкij@<%c9 px_8' ;an R wma?IAu aR*=G(?ZN`>hE4.L+ g#[N x!qk ˈTS  qcE=H >e0E?g8M#ù6Q7&CvN$sNB]G쩇@ fmM>Gv0, ַe~aw Bݍa^mXφiY=WO dٮA7_L "-T?d?cg6q9 Jø=0Z[ ϣ_yQ 4#x}y\L ".I'%B"T(YRH"L3s푔"%kd{VBD'-7?~ w;>{:u^NM0%ۄNnހ =b~ 0U!^PbQ7a/ ؐ^="{^%6y:YS[60aEJޖUi`: ?77O&:O|׺Y\;Vl k/=WFzF_0(y@~Jˎ#62hq!d?NgսWY4 % ;u:dQJu*{WmJ;[x| TtQI`ew;,lt3!\!0V6۽LNC{88#l'1C{d[Ё("ۄΎNf%qgd'ħ= ެV %7Z^퇢J|Awcy\ ,gŧWn^[Om 3R^y _c]/> 0uA}w^?cwK6B*H}l;m 6O^/>lGlL\ds.xwSԎ} Fu T`͔A _`Ű^R}_]]g)'L3p%w/o^t˄)@ φq_=#y*4t[ ' v?/d=1dC}ɶє{l:;6TVؙӴ @ze ]<P:oHz!kr[㪑ǍE) \kzS7]a7)ٗq <񤂁DʧJYDl3`aѧvN-Asݮ0Zn[ sN.9B&]>xY =oS=g~\9F+<^]Xmt:3B~=1GӴ\0 { HCJѸ1fh`^4k4g?]ځszҡ/,HV Km+`u~}Ȁsǝ|s# _fD!N%d[i:<n\ Xi8+4Z Wo@S@^8oh6]@_F^Gaʮ,Y?'gӰw#!1uL=!0yN 3dhρ5n#*TΝh:i>,Y~zY>3)R73;0oI0}w{rB}'\ԖsݥW|;;ԗAj۲Nwm{ .~nЇ{]>fTtQgۨ%- AEkߠ4u}ex^Ĺ5sE>_ il5hm92٥Q?혩>݂ekGYHã%~sk7^+MyF>ʿQ;>:tKx/bM_yKUzVK4U)-TVh0ȯon=T},,N@ +%mh4u1dC. ΒCdБXvz3&tnf*N@*`ӂU3P ؤ۲hU+]'^-sM&p;+E6U`|l*HSgA|?F%yV:Z|lO1Wz=nc2WR3J)+*E\dYPEIE:2zJ=ZOϿiWT O%4.}-2i\w>xnU\s54JG#ƁY q5W^$5YEN?kc9dСjC NVͅ7zNsFiGț)}fZUZo]0v3^?kN6l:إdЙ{0&Rh89sjh֥ n҄kU`IA6}C#rgHu]%[kE) fpk=Y&hY n͋~) U(OHpuZG 626<׷*uVT 8eKn8g֢fޚƧvXz_AMMꅁܨi ʏ\\J38a|ziL`lW:vXGW~L,0ꞩ=\w`ic&y} m:69jB o+S92+1URx1W?k\&t,*ؓmBgƖ57_CxS^U5%,Te;8Ao3XM&9mq˷K]Mfe [&M,2>4'= %ξiV y{AӳeSSɭK.xmRl!)yqڽzl :3vw?g7Xko3R7t0rUn   oLkzĺ^!/Bٓ ҁqMul?egc0/NवM's譲zK1ϕQXclgDr‚;q>,TO.Hr ۂI Яa!\pqBA#Q o{ץ] O I ]2U%i"<8Y [ש_W `]bAK~צÔ6)6J%G43- YPџAopՙ/p4tW{mmԹxilE.saiAkF-Sb;ǰ풡%TNyQ޽lV@1}s=e%b{r Ov̼w5RdcH>sk pzLlF҂Gਭ\xYPVد 7Vj`aGip֐kWxvzz'f*UO.ώl5J Fv#M,Hr+JԼKݿxͱw {`뷼>Æ -mKl0WMP;5LXTޮ*s}gZ00;=/q/v,Lw:1,{h/|wϔ{Kndnp5qOd$]RCJݶ!#qӰ{\Zg`v#o>_p%(-p(WMG\U)$s J&ƒeݧ/>`;|zฤcTHf/r,`UW`ڳYV6ݴibIX]U;6qv&tZ<[I?TJy&gktx"6SX5#Y)Oosh3M?zpD3}5e ͧb/ C3aVy{υͪCMy1,5{ >Rk/D_ϱDVĐ5X}scs`r<0IW_nIXp`؎}f.;'U NWư ٧z{Ff4,̎Izq>_M{/N]7 Ϫؑ# 7w{U\bYs;[0HM@yhdlUxOsB˃4Nկ ^U/whbh-ѣg|jOT+n-RF)2wr|#]?k#1&t8>#ۄNȊ΃&IUBj} b hMB_P·qm.qRRY<>i;e￝~$w>ٍV~<..WK]cde^!sS8p/E#"fjn/CY\:\}݂yw"?b٘F"^ל7V_ҫ0j9NnRlr6rWԬ 1wZDol@vf'p̼Sc~pLjީKM!t}F>MϾjH W]%KvDs F?O?e~dǬi}7AՋ6fx5<wQÓbɄX]LM0 +MH࠿K L!c5_`0cD aP]!4ͤ[y0X &nAKcpY9L:~=z aWSl&It+IO)9C( t ?DbO埅=o=\31(QqY"|$J5Kpz+~',FBp8L!Fn t }6c'f^t@l ̟A h왂CǴ=U1+2S@}t?`0xXPl4Op}A<#Q`8@pa"주`> /!Fl :bMXlX4'@߲).~ eOxaJA0 ca9"{,Bc1K>LBųM" W1L%`sY ͣ3,AG}*12[";g m>B eɶOpW ]?kl :T -ep7lU6ا G?EnDa;#D1>,Z`9<Qc4`'" &XYu0\] '%_ QxtϨ [|C;Y@qFrQwtNO6?cJM8E0(<.SX9C}R}`\F[xW3bu׋Q)cii-(Q z B)B,;`0!#=J/aAy\칣c H~1aE~| }.!F6&t$?qo:JT `s1mł@ſR=3th2| @;o # . cC >DN ~xL! AO`.Bg >ƿ gV@>Q6_A<3 '?F?k#vߟ?͜l :7"3͓籄{T4)շ53-x sAX$T4$ @,h3:Pf`f@C3b\&c7B/g [2@`n&+ jn'bwmcE@x}~>A \7c)\`1|1x7 ŧ%S@'"=G/Xl b@ Q]_>*` 6ɶ0l :3qu2!~k!7O@mܶtJ cxwRYb?f`4`=Ab#?u-\J1"WQOZ&7#5 v þʋ(+ 6sɶزl:1?Bm:}s0 vۅ'|ygc,*syr 0q݃pҏ(#\CD ؚs uxd'cGh RbË,M -Lm1dB?{ |MM}o6yCYL+sx'~+Ͱu"1Nq8DoLOX_amO1C)B씘(%'GMtv n՟ aP8|At }&MO*`A[@%ۄ΋HM =y|>%` O5vy?14Lp 2Y͗F%D< e=_KL &b2iw \}l4 (=<>!XS?+ .A&1 (Tߥ)WHaP6zy}l uշ)_O?'l g M*p=Y}U@L@La=B xDjI|?Ī?<9F މ!Xض bɄXN08$x]y\LBeM"K )KYBd+Q i{ϝi_ B*IDHOv l-.3{z={9sg:AFڢhI^IKna} n|C`Sl?pzQAnQ0;,p8Qt ^ _TVs~2U}~62nˬn it1<ӄoq/K`ly0AOsj'CZU%wmS7&~x.0/Ma蝳uXw0ziI !㽣1X[Wx6eNږG;7IgXOM6??xDqbA;ǽOW rK5j#؈)#s٠@Y96ov+;zs i&+}`tU*Sɯt0Jkh[WПM/IHcC˦E#NJ"WJOo|A {z3 ܸÃ^ql ϔkN * 0dM#X뚉 Hw˨ln9?ۛ.3ɧ\#\QX$jk[;~s0Е6(zpO}د2Eה@ĝͻ9;"(bA;`0.ƵsLXyk7[gzݠ%>'8]]G]\%]i^֓Rʿ$!u/BkzP[\ds7~âJ7^ȳj~ ؼ8yFK\[JO-6Zv% O8jk aWןufsG H*=zl''b[i٦4sk_g3O'GAa\0 o35uUTDϓCMX})mLV,m'~ڸ%Sʗ]x}Q2zܮ/x2O\Y% %)|Д`Zqw"9\Mܯ޺J#Gd&|6K8΋~QS9/+?{6/Nؐ8|#NFMֆ-g[vgzVZ B-]0IS"SUs4*n#c)ealwNZM][^JW8ڕ=@h]EOwQ6ߴ{}zT<(t~V~ISW=oou4`&ɁkvtFr{ǣ*#lfLavb߄=kg2槆5e>3z*XeuJ%?oO VrHASl) ^>??Wtv?`ȿVa|TR`htd!<)@ 5gn[:h(bc;i@p8o$70nP t6V2o.V/-tUfUT- (U8 {X6#;+jO۪rSþN/AHcÒ$-B̥D[>n&Vg^ гoM^ p|۾MO[<ܢ U\B܁\^5Fͧ S~aOvv&vx8Volt>VY6Ӳ4j(U4z,|p;f`ԩ5A8a+2ܴߍmubUC߯4i}uvGg'RKut/}횶yI8 ;`o9| 0Q[נyH3-`*E$6~q†WgvpE]~ކW\'49 [ I9mX BHZ(IK.Ynx93iױC_Ŀ MJ.n5x.~3g1@a_һqrapqדeЬ0&kifgvl?G^w_H Cӆ] qOeo5{d\gFV sWXW.!  =cGQ!c<$scxK*r~#=pf]0E|m6?w_X 4Tޙ{?`ͬE9~lt3F8k]pYk޻YfQ&hqҖRE1=9{3 {H߱ѲE;x'i#݌8;1$M)3J㍑ ڴm{g5jEL?p=!F\.'j[>ctKP9.n&m| +U@}2@>d2)&oz0"'hσj=guB=8i؏}m}gWht] `6|Kc9ÚOn>ޅ<oQKV_6tb12km%#=ظiE^iO]d#,N',#,5*pj?;4zA+۷z ,g9JZdԚֆ9'*}Jn(M|;&S¹/R+I߱dA#;IZ xa8>i|蕝_rG7mbD#=t^⟁kw*;'Ɇ59%MFErzVm{!tk壓_(gzf5Yދ34};f\w-nz4 b5{>݁MLF+ 4v ?GOG[ ךcuKؔ5 Paqjۭ!^,xX`LϤe*Wʮ[F%ϻ57R{҄W*wt^\X=+jF%@'?ujfm\}WWE~Aݵ@ ~f*?^ ޲K߱2EKP;(i!FP ?&pz\kc^gR=ߎž/J"+֟2 N?z-y<*Fҟ@iv4fshz!2J 0#4drVr+E9iw;+p$ -Y\Gؕ eʭ^Cs}go>mmR|mJ*b<[h:uosa]gT9-zoR%.ٽ+@>jpf.62]F(nwMΔ-yq ];4[3*ՐF%AG[ g/1c.$|mk3=Os,j9 w/߽V.H߱1(V"%XEU۫'s48Wvri\+vbйMM] T J̓we| ū6NwF-k/>]["Np1:伳vM |{u[__X͏d=Q>ZY:(*u;U5vdy\PJYu4uu[Hon^PY|AeHzƗO\jΫP->/®x?9REH!6Jo.845²tn~0s ^UT\0B5M ){Sh\7fzŴL0xY# Jyog“olvKg~ M>w$s'ЇΛqq}`nYE7y 1 ~H;rUU3F{ 2Я[%!!! ~z\;G;&QU7T,QO5$@mៀc-AϘK%v~܀%ʛj LϹ\ ϵtV/KT~h)T;6^+KZUՒac=mY]Ǭ>'7w>@kƭ[J;mH}{%GЬCve^ o}dzvpP-ٽpL;ȣ=vֲYu1Ї%ݤQug5!|ᬁ2[tϹu_<OG=Xfp3&X4!U5[7FLIt|X'je{'M\p؛o+FHi?u_ϙw?}ǬLJEX)^;/Ls ]yy5#^޴UlWX>9Ǔ?iZOp1~ٯß5n^/ԥJykR":6h{@%_byCIKנޕsFᯓ;'jvd*XlrScA[/erʽs\7XRa =1:yfx9x`gOLg};Aa ϟo׆dXu)~6Ϫ<`*G%TǗAEJA4JyFs#Lw^z 89JhFE'N4sucd3U<2LNz)(u` 4+yí;ʙ ~+M W hW9F0ykHMB|`wEgMTd:Uɸ jÚ|G\X}0CօxBY)Dj7Qғ ,<08l= BN/'r w8& I$߱q0R"WdD^>ar b: bSzE3iЪA X|3OAA)8gD`@]Hhy-`b< )BVqoMi|qCLlc|yOwBFs gS0B)T;6rC;f0yzfSgs a-vH3L(ORLYD?R h6,`PME~rL` Kb5KD?Y xEi8FD܆an@0A0_H[@dl1RCÈH9"g8OpyAB-  6@X"2 BN)!KR8$i PEh8̇_WDE=W$[| UWn;Gx\$M ! T1߃\kO>W"s*,,sBD~)F 9QRZ 0̤KRЈ_{fasx@ sA_'[, "(lƀ`rYD@ hV!n X1r!iB?il9,XK!LP/Z:(#AQQv@2 ZS)T;6;?="~P۬Qպ9Vh qַR~qTAp.`Gig,M JN ? q[wynQ`bz? "we}~DӠp2Жhb%j!m~Œ="i[SKu89+f˄p D@m 7kBذM`jb1sقDpuP*(-,[k3$7Hb]|jgx KBmz?,ˡ B.ԗϣ"6W' "A9_"CEamz~۔Dc a63~ADIih a6uS}F %Dv "\B 0 Cl)EF4&l#)0EV?lԕ)<s0S6]v Q6"I8 :*M(X$dBP`Fq[8RF}к BOP]V3K??YG-5TˀO-"9K/1Hc#/8ںך(,I6=砭Aq񅗵.  DXl(Ldp/7 X4+|(!lAC)>s6ȐAl'w쉗R}@z/8328t@ʿ$ %k`WbM@Hhc+v@:k38 ն2kL t!(4 |.QX~l>evGM@"OTSv-'߁áD>FzecGX8gDŽ={TB*dQH Vm[7tb J4H߱Qȓ Eh8L8#q\aR 0||.[` /m[a c[%F. Ah@"ALn&(FB &J7 b~Qhb dk-1fX 3Lu+`h{@e{K RO~@4%x}ykAt]^kϰ@T!Αb[W:R_a_kZm9!{+Λ lǬ\kE]vvspr yEEχEãkyQ4_-+K$Ih5_o7m:.45Ks dWb~QSeVeʢM5ʎ5o^88?阣IA&s$-BaJpEjhv)??@~E;? `8Lwǿ~|`=* p1l/h[ڙ[:V/7 ]09e]fj޵LxЫ%2Pȇ\.֤~\/PcڕQ IkVF{ֵFg4ޖJIpUs T&qȶWSo+BzJ Ui<59pŇJ3 B+g/_v|Cx?pO 6^eR%wo̼.i:azKZ.ey Ц>}̾Sn9{u~:nd,Z^0f8N2K󔳂n@|^pIiFQLץVߑY/`Ҁ*#ߛ5oL~S-tE>k1ls9r2-~۔!_bt ~ѥrEעzl(evnrC{kmL,;d_k#R9^=wñ/X|ke/̴*/Ű}$r\ne5 QnH۴DAk>??.l֘ ek/0y1ʺӑ(y=t`R6إy_t7 ϡ;niS/!H{#ED,] vkZ͊p*x@Y+=@;TGYyaC$mqٰn,n>.宿p`ڢ}'* +}{F'%ǰ!]5o}L,gߕ-WK/=Sجs-3u˝iቩՑpf33<Ё&ӒiuՕndg·< _b:nbfbaFi {oۇn z!Ő{N}usIwࣀF^?q  G  {>Xa.6K @8l0bqDJ_Ǻ=v_kkM+1i38&sMuPI9x)Η/!H{cKZ.Kߜ=[c:n'} ߶䁀Lz 0Jq?Ynj@{y~߫Ike]{DɯC} n֖|uEٻz+Lo 쁟e~'ǂvVEYRHXm\i{pH^(̭DiSwl[s=-O;=;?I.zz >8`Yn()pwqVPeTYcx:`Tq*I&GHf߇۸ 6 );$wylpWt¤sT6G_2 ַ 'EqЁkٺ{7v8aqVs <9CPfv-9Dʿd #+B$-Bg"]Iеqnpub@ڐ'ӽKJ;LBW`%ؒpǸp}lP Q2-93ݺJGwزW},#SE.V]UǍFsÆÊ1_zG6S.xj$|:4}NMlW#f8}QOPLwS^Ôonn8$uԌ&7fg8JcWb~9ە cl:>7f[m٫TU|cmO ɖy㋸Xs lC\0~y ׍ PUCF!;OV%m0y;NڡR%wozʒ _}χ-<=v} Qg,G; 0dj{WjlPp{j'DQ)#e\\X=dƆsq?CedhŭgAkW(l]7C_~^em7 9~ e8e;]B#_k׀fن>&St ?ڻ>Ϧ~^pm @M֣P=)pˌ  z[kq^N nُ9ȈsZNy% ^t.+ >KmX +S3>2#I ;N!>`GxIOSB?N>u\|>E6},[t95_n^I2*v7WR\D5Vjum*8p$s_s'Ҭ>C7=C|^WD͝5:I/`Pm3;OLwkY,?\ܲKRؕ!i:kOKZ;mG>c|r] v_xo.z뙻x:D0{]:K_<ӯELR=D2V#2D&v*11v􇊾X*M5ohBcjl[[+BTgI߽![+i: M%-BB+Ʃ1\3 mw 1hJfptK5emxMC;}^eoy?rHu~7nFa9O~$)լ>\Oe 6!%qǛw K[#*{j>8goC.21wv9\`W7:><;ٿ>ds9p?]+#m\ BEK=H{'..ԻŎٿF;T 9?E:=/]ʖЛ*>jxX>8فvOTyd#?xK Tm3hCpn' [JKj}ǕLg~}-{-_6}x8N;_ʿ$ >ZEBM@UL]]?!:2*Bye+n g'4o؀6}У @sy횒`0y7'Q"'OHUn+n0_s7DZS%}o9?Y/>xP#0iwO{F3#Nir;سjAOx  O2q \~Ky%_Ƣ/%-BgU<8r6/u}VcG\-wԶj,SVw[)-g#M8Ƒl_#~5M/~0ԭ ?rO#89vSܒsHo'8 zV!7\ lOƆ&1~u+TuLjꖱ CVG3\hzݟ~yԧ}d6< nooMBP+v@e掝)t/abοMmX şZu75n$6GtM1Xw-y@`YG4 ƛaF:Y7$^1ldۆp~Tf߂k{ln()T7!O%-Bg3}E:+( ;*'ʰvfxq,crhK*bO&>W(^j3}˚a ȮLO6AZdpKSU|X8Yxx̑PYquЭ%Պ5^\<箪C_W suƦl+2ԉ;0Xi PT8v<$gLzE2ڶ8\gi"#jxP%}׻Tyg_ zZy;xx=5@0g~+X43Vv;d<3j4n^W~jʮ}lzdc4Gql0]FC񨯊>fk zkKRؙ i:m]Yxi*`ߠigE^?v,Z3vg$8?Ԕ13Gra*厶:v2eie7PcCkNspO͔:ƽaĭ qWiz}+X~/;/5N  eD,i9?d.@įٵ.zc_9 ({A݌] \e#%­s0s̔qIU rd!)`a>GwR1dg1;Q06]j#lb&dY͚,u -m=hP[^+_wG>GtIZ.b+&_Qk;$Υv046 /b(`D/, ?",@P_|/E1}B t~ QFB3qYƿU,)\> qIC` lIAM&" a0ql\X(aa֣Q,ʦ#;8R%wkgKZV"t5psDI@]=4$ATV@6A 6K^i U#"{`qe2\Pr ;IegFGං¤Ή@NC0 Cm _f 0F$6?c(_dω6  AI(}Ejςvzb-ڦaA B;]&L!2BK!mF;C%}C$d^'5l3 sč Pa PZyT|s## c*񛎰1cOpBL8ga(?*D_HW/!H{#-w] ɉ1}Զ>GLzb>|;܍ ;h= ).%H! $S& Ï-F B_E -~<~eػKwe$pZ0ȱ'..L0@oK$H PI߽ql7?jIp0|M&(ÃvAC,cq9G&/];x̕',*ؑ=*qc e1"%M1a%|Lh (8ƈa#r&-OzR8/N?|ᖗ:KtG qYGs,^@-,Lʿ n5tdq( n\RßxEcgkBY¬2k!C{,B &c TlG,|XbRIDS-jpk!F}S%X@?X@&>! 1ڋJx '8Tm" v@K"t&%-BBfXxsA oJozL K,m(9X bJaJH~=& 2/WIYE L :Qj!P;ԓ._2i%\0VHZ.l)  NR +.&v?G JҼ:YTP.`zSžT/&29<`0Z0KT6,H"Rq|AE?Fn~dW0aEe($DQʀJ#6??GI{ޖG0yH ߽ѝ!xJZ.?@?m>rA@}ⲄbC &,(Gv  - b?d;ѥN\6U!,],|bG?QOUQv3nArR@"e@ |sQxŨBo3)T5Xg?K@h#wR .qUݟeP Dl2V *cס5\a \|"<.4/T0=$lvQy+?ҮCҵX HNPbAjNAӵLʿd nn6sEB}tXS8G<"^G(t,"M 3x%|'Gn} <"8ЮI,~"@m|矴8\>Gv v=]kSKRؗԝ!z[JZ.dr'( H {6]nۣt/xOZ(خfH;xlP}bo" #ZUA9Gt6{(/=d쯢(+LB` NP>d]?Jg񅥀tA?P* / H{##OC Đ-*ii6}Aw!mqx~'πhY#tGʹk"A|Xm W (eDže@&l0CpQ­> Qlc/G>D>& ~Hpgb8<0Dt/ H{#+ۍu =\|ώQ@GA B IWӅZ._Ɖn7~+%-Bq'= hV{ V AhA<%2!ip&ZƨځEg~ *UAx؂mrB_NX(l>Hی3%> @|_Ai L爞LD5 ) hP%x]gT]W( D,X)*XQ$2C)`A6,EXDNIBo%lg~:\>Pj o<V?wkl[&\@}w鷎w]q5mw֮)vMByWW mo`g/c/XE_WWM赴d_hDw,݆(~"$@|tғ`*>} PyobAxl N"V0-\;ilYG:ſ2@FAh+e kn#ypzہpg'ks0VW;A!ʉc==`"Wϸ?~7j_>CR&Y׸l pȃHMz4,Zqg9#rǦBG޼omɍb5?c0+K[+ < u`2l2ws}d|Pq4\GVaVˤye֑6 ޕR7v]V{qM ˻AhM'3ħSS;&b1&h- tSn+o3m)UMad1}) 1ՍDκ`]b$ZM ]W(ox9H#hyR?fEs쭭Vn7 `Y^ih; &rTl1=ù,5*Kwc;ס쫶 I3cŇ0=;Htb;ʾ,"IŌcD4''Ѣ;1YxnpvSs!S4qk@c /;Mk4zh~n ſ&i;1ˁcRߜ2|;0@]Q7bڛ>.6 v]q!'^ւ=G]wM%JeOrkݴcrtKhͤW(o]!(kVUZ >0iKl7J\3`1Mw6՜LmgՁb yGq6b=ɬ[vy>x~ᯟ>gU +2R^w)AƹX]eA!9{W-NU31+e1T 6_rjOGor\NR sU}ҡle FXNt q &qjRU}ʘ `/:,Ѳ2:iS(㥛kڽ1oX[ˍ:9D0ncq0)ol0ӱ1MVA拳,u[vdO]S}] u_LN,adʪʾ[V=ViX+f6$)qhz &,TmB-q,J5*rP}B46^#ة _}τ-Tn^cFV%k'J>Pz"0r|9˃EC=e`~_eu=]|W +7'Lq6}鈸=4zy_O;: Ҏ>~F#"h6_jDf5apfmnJڰȰ. w/U_jb}*?l[ }e,u#vXА޺:ñ#u]ji'DM ̿tuhX$н!lhA6BF`3/v_x䲤E'Ѣ[+R|[zy=ZOZT xMIxD'4z,R jQk%zx{Ѻ!nu[:oy0º>hiɦj؟okve/ֆDmĿ2Beu46m)$fe젫gJlNx\(+;fRJĝ|`kd`)(cn0L蚤 hճg>yE}TnUn-q?W gylŕ͌jBwU -3U0^0vz_|?B<jm~SnytdEx$TRW/״=h ?Q%e@ \gQ0t ջvge0vqi g: 'Dmx?* G3v?aT* ھ窜hڭk{]qלnϊ[JmmieA)H! D{3η| ~ Ï f3.yA!D#l^#1rGV} @4t[MY@#'?&S^" `Cs_Uf|{ߟ Q>BoىߎA:j31,ֻR7>l(jyG0? +o$%nn`ۊ}u]@HH4?s?3 kķ7^8swW{1N90f ;awb9߹ *<кmX_d}3,o |kM]7b>L5nmϯo1Žxaم{dC!sJk-J6T#=E/eU D<ݽnbouXO0B.I9ڬb-gNߪ IF.=YA>ݣ*fZ-<%̾-|y]Xͪ ClAQ,bky1[KeVA7=w¥%KV/ϛkhlȫئv~mhHdFEwa*~7xz.6\xeS"aG˙ރuohPkF?UCu慶z׏Jd~әӰ_'3+ |% G/=~o^jx)޷k>$UUH+ˮ7>U9Uv-J6LN=QGCh:FePR| TrbȈ wr A*OxXP^tf|P{L{fߪQ:pY]wx%lxqKX5ʦG|]~n^fvNa ,笔sRr~ns'Z_(7|41k\Έu;ZhO8&>Pk`-zөuNH;Kg¯_:ž9׬PoHYIhUVB0#}IJaz>%b;Ӕ^YcOCw\4V ?x4t^o[bzJ@GlZQ?F/O8m%|ZIm7e@I83 {ЯD:vX.5aځi7`&~Ҍ.uQľ|e/ X0ScVP$=ymQvshL}XJa뫝AތwF.;-&WOɍ ,jd{k‘vT7ӡ!C=7M+;).FO|mϹ2ޙ;7h`yJ_kZm6G׾5`6?yb1u`s 5dy1: yݑp/3&ذ`{_ ?vMcY{꼬 @uS!{vAѰ&ǥSO}s<_>hF-J6NX*{J=ր;`?b^Vz4B'?r2C;xU4<=.| R܍5]zf^컿`MwZԼ="1bε%~頿@׮ÿKG>Yxݛ{uaʗ>]c2 [m?J?.iunGbW W;yj}ٗUC `bɍ+aP(Flhhr4A"?#ݱgL#FgR^@l`/1B^jppζoK ɲ89y@Ƌ^֩ gg&,FqޭOUOz`c,F}~AFÐM<|6 j#fk T_jB/ߪQ){%nhLh-mi|3puix>-=q~@c"]0vb0adAGB,I[[!x!UT7a}na5}NU M.Sx:PiCpSY=P,ڦ0[vv8{9uېpz5|3 }L~F:̈"x\R4dNH=}Q ;>A3^oE 7P0(oXpM.AP!(X> uGgUo#47LKo8 }MT!>ŎB{5Lna"gx/xn}O#C/N;?G|R{ׅ;P,1pMW_T-^qspz#tne;b2Qk#(v6AP{jM40}0 . q _ ԩ>S:Gop57Uy̾Kbfz!; ~gb m#ݎy: %'{=nБ^nO?XgV9=۱{kA wA7=wyΊnCFWw=' 珔=ekMCP>ΞZ:k3c8t"vO3/3 |`mlwș޶RF: 3| 7Y;656֭J"6 /1 x՗`/tx eỷ kׁ pS-? 009ӏ3jOdvG'\s#iV`݅d+2~Mmh,a\el!iwv""ʾ^绨>CVPwgM: 6?9N]wz;q'^v_$x/^;jZҮ^㝵8pɋ:7~lbA467[gQ7`(nBP0(o8)({-jpXPW@<(WA6F?QLp`,:K,J 9YQa[pP &Pg ?a؇8.. `Yg`o 5̥WP6'^c,(~O7Fx+88s,K, x86W(o8%RZJe C,FH7kOTTA  dZΦ1fu1&PcxMbvpՄ&7kp@: .;v81֜p(۟J%Q0fImm"ſ@F@#hy%B1bGviL~M: !\2iױw( M 8C~H.k* "8d:/ < T(lsRqBw*X2|`xX聿Or®1 ʳf'qO`c \.np_DŽQ7|pd~0BI!ɬllP+m+{J^Ch-8.Q Y@\o;hx\|9@.fE|吝Çg qGΈրJCMKZ B7"#x^ d00[R@^Z 3q+>MS,&4O ODͳArO0KÇFPߦ-Ҭ=V}OK㊺۬GG$y8!e3i|1';Dd' @RGt !Q)CX2LBB`c0hKܪ_DLd/sE1Iː|Y 9\4W6}d19t>Oxdd/N̓AFr s1!$RMEg d?&{to xs{@tpJ2ێ=yJ#Ϡ b4ídepD @GY?d!aD%̍ROdzy$-3J l.?*H;xFw ˃=m#B@rsru#ФO}t/z* Q&,G:BX099'%SuԱ4G !& q?Y+w?X% fxsRH?n|qO  C%Q P,iOFs4׏H -bbj0Q<(o8BR '|/Z7kzMh"Q`<ج'?3qvS,Ƀڤ0.2TÛbBe/T͊̕E ( 9@Obo?qd':sA\GFZHS g'X ٚ-Q(O҆KoqP߶&?\=VĔQ+9ӂLV \s\A"GMdkP$ڟ'[K,"FMˁHoŕf16 idw++!80]qrN?a=7u}Ą IaXH (KDYf DA GAd LV YQeK"nd;\9 it\l0TR*dԲXZ)!W̞BjoǾ4K @#8ˑYA^38ޤ+trSF\B~#0T!Q%ʿ½}[^+T/ @,0(foFV4=D y`_ALC;(A-|rW5i+yx8_לm0Wo7N(H// b`Bo<D %?a`&x]y+mM}v3s>){޲d%=C<']/vNU31'`5vGfVDmRxxwb]tn b}]aI-aV[:87yڄ30?~u׸Ws@hξhE{_0}} :uE'O+2e\iS^|a WvDq@kT5?g 濽H~ 8DMuqלp?^w5>Dm踅Nթ/ǕF ם?]+-W;!Bx4?&5vR< \_N>i࿝WOei:!PM=>B0q\וeEWxߓNY6s.7@X2׸gC-l9mo_s_0>sO+t7.k9 z891;yxMOp ws`+ޤ#UZP}){kě'2#Acջ?}|m[lA31G?{@Wd[B[6<3>BkL)Vkr w/K%>;{iP<;!pó6[N W4B=Uxt<]@SPȿ|jDC u˩И<2~^%5׺g ;lEFW{.WBhRݟU^@csz~i-ӳj1.۲HYw}u6y:mH ̷O365&ynҝϼ.p6^Ɂv,su@_d>.EUzPi(xo!Z:*H=_+q 7GsvppklOoR3'L'9ClF5-8tHI>0.XӻMo :࿝W@CC ' İ 8{8p(tM=L<;Ճ k$$}(0~ǁתK2v0X .3 ueǚ[mS" N#6tZ o8޷G8e:+S;ww~͛oQջ[=ގ>Ol3oTu\y4ک/}0fP3w]ǐGKQ^Y<4TLS߾Pȿ|"N@5nuKD{}{-Նf>e0p`OO]'DZ`[3u5̆!O.ѰO#>,I,8csh홿}dBh̀\8q[ ;݉e^?n 520v%`-3~:~5~] /fp_b+jNV8XƗ!e`AFNT蔒]nk& =:V|Bk;)Ϝ*\PU:kĪ}[(k0ޔ]9kv1c=dc7B sC/G|SjU2lܔwdH]1VlRPZx9|&`Wj4IΟ{%j~YP߾Pȿ|wG@%,_Q=}Z[Ӑ<RwXc%E?a4^ngP\8{'ugxcWuB2^J)mC܎ ;'R.]|ڈB#N\GWn.zƮ֎U0; p{ک_<̈́_\7 d;oJ.eaÑ \%KK;twTif1um.n)\ 緫oL;a_?\0U |~rP )w 1uMw lQxRη_X.Yci1@Q̦aO l՛¡qWu{¡9GٞP@{B!)H.7SqA J r!W ߲ˁƇβ)CKɋ6fQ*GVa1*³8Zvsf~][~ ^ T<l s>,yռ__K<5[aʤLwfUYrο޹yv֔l*.A8V 0CJ[3~NxUڋc+F55,|I+g<5|mi]sz鄚m>(#* ww|dS tP[m|<<~6T{B!ySTZj6zTl8{I5iUP]G0'QQؠK\L'$A/֛1݁mK&pBN:h/4QҏDjZ^6iL X &omt~{5cuO}%tTtqF; Ն*+1cBRpQWzTboBGW_FN5>~? _q#]=wչo<˫Q)vqz\7 3㷃կ 'z(;B]оbc<0үA4V<;) OzҰOz+5v*y2!Նtauugg1',56eIP"0j2b8W;c1q|֙ `ͭGfAجo'!G+A)7bWˎogDU'7YsŇ^>3D sɃYu<$7;l5 9  o7%r8|!PƪHZ.2,_&!fUE:̤ׅnM Ypg:hnjJ{jtzڧݢ^2-u(.>?xpqXs`މU>2$q߭A')Pb?n#A~|p@DZl*"uW. u^ЦT]Bs~SzqUSMЭwC;s Ŀ5Ugj/L YۖNfi]>ֹK?t˚n+!ڻ> SF]6lDQU¥Gn\+y3 iE"}5ɀͨG_ذ@ZgmqC]𾬲zvB7h[H(3>a9heonU@4b$֜{ p>[=(!x>m З̪Ub8Oȿug:;9Tq4B\^X0*_s32ރ*W;Ƿj8Sg_jJ۵OCׇ^Z/He$Q ?~szIsx{= ޱ;_w^I7`_1Sk4vs#*Lh͋1}57XiG5 rcgX1:=`jG^<=XDzO 0o'DL^Sf^6g#Vtrxh]'Z(ؘaqέ 8D!vKvpQs/?1Ϗ> x:^{B!rl@5rRt]| MMy,I5wkZ$Cz!&_`IחPڇ^_0GTkB/b1}4rZ45(ӡ\_krW7/v'\t\Fu'̴y]>fPdVoij}4ˡ:?va{e4~BYJ!s|6ۿWSJkjpDmTvXi%lD[X'fWX;yo<;@nTʦ/M~A 0;1[b箢AEkqLU @Z6c{5nYlH뮩Y9 FL~-&=@΃O  o$ wg@9rYa8Dqko8.yj< DE KA痾$\Q\r(+xuv З3Ѱ[: gA [,`o  7<~ΎOc.wKA$,` /⟎kGl5/na8yt<" 6aewT""ĄCvBbcTr%Y$2po<DbFhh=DOlU2R> xaɍdւ0$@ :),X2"h\,.!LR5HEH! i'TQ_@dYKH];Ld9gFf8Kl ŋl< AD'D7.~'3I~PPȿ|$J?]~-v Ad2|o>FҠNbQ(Ar>I_Zт8KbHcELu%BL2p(Ri[$ FbPP+"O"G#'?3yW2gs,\a%j8f࿝F)O~?0}CLUq/A4nb$$[nr̵q,A,8&6i}DQ&b2cY)AiaRaT3`(7ㄑ_^+-IאE|hXL$4gK?aI@t[0IC'ʸʋ$$E\` vBEʱ–R=/ 7L’݃$U=59#+ H32 y(EE%Ƃ' 1L+9" Ŋ V( ,bS_K(bOi~)b~ J*Zd3Љ:#eK"VDı$_@f=Y  o:O@1v mRZ%>/4T_,YYL(U'KlDla҉Ck(6{'pL(匣b_Z#@ʀ"Sd8Ő{,Ob{.24d/1/ ]lQg (gr0QڿM/NEI+  Q߾Pȿ\{C`C(FɒT(@#w|EͶ=>=^b"sBiO߲!"10>KK 0w Q#3a\GXtN^vȊDJ,4Y FI?"'5gȜB. طmm$[[//HΦzTz".ĕ:I5iGK&\hw_~o&@do>lR0ܺXFx[~8vlbY)A2”EӁ(#Hg[3`?A/ e*F0T\t1 OjaQҀ#KKҹ. 1Ŏ9K Ӆ/4`lvz"-gDW,I#7iiL݁3)4 KESqq=FY%n) sYhnu *HEf'0%>A"T,t_k@FC0S yrrxыb j3&Yx?)0:؀9j-q08͏6]V? $LNk@K*X4֥eDL*'+^5dY&V vye"u 8xl=X\_h7 YBF2f@d5|In+Hq}X0&$/dz`Y6g5%@d2gjn=B!іP= Q`G'IJ }W_cI =8T$v7|L:OG'rNuW\wܙƗB{v?R`Uk2.ȹ=y6T:$]`B 8h\`t)=1{wI؅ fO ~1Qy]S2av=l!wLcϜKqIױ3Ys U4[>HE8b`QdvnΎ-y~\gogn0;ėgh}Ĵˀ!co-Vx9ZU]wdNe?lH s@/wo☵HO"ٸ㚡#6nxlEuO2Z;?3hũEvUI,jМIͼL]hc b.Q}L7/! Ž{X]|QSj%ðˁ!"sN8[Z~ߛ/Fim|\ciV3;ai}y}kEO5mm;lqh:[ܻ*6h=0n޵/h7߸GUևT6ʢaFwy0uCa6k:eGE~6%RL,Wk`bN嫟\!E>B30JYl` =s nhL];>pX?6ΗlǓ,K[Q|?j jt[,4ڽO&ӫ x WmuoF8 ;qUfkLCTb=afL!gЬnAYE2pͦK9:d$F)E@Z/[ '(.]ƃv)>#7Y4]'<͉/Y em$<2i]e*oLVc}fiL`UNHٷ~LzҊnU7! /dW7M ;x{nm9F^@A?pS$eg'S]nl3= m]^k:+u}DņزF{&jg Q6 lD܌aӁ׏#&ǟy3c0͆ ^S?-,gBɈ-yMC716WTIt\*GZjqe58АY=mk%؃v4l%.}ˊӹouOUҏXr)3g i(,L+NƎ@՘}CoGܙAoW<=:BZ#k2탃IC}xހ~y1[*M?!նoPu})|@uHW/FQok 1rS(C._c9"V] 9Jx@$Q 'D*~E\Eg sU8>;`:,ʦr@Q*OQlnO,lCaĨrt3OL&A\5r挦`ͻmv#t=g:zg i7۶0gL܊ÅZvD(5؍>3H(U0qEOv0uZZޣAWki)~+}~yQG8nNgxoĴ KCbsa3-<sC^;%` tO 4#(v﬿<_2UpT ]I1,öuM!EOBEœz i}wΣ`FUwuG>2@r?I^,fsˍܮ iqW̰U!g۾]61[FlcF9ޝ"}f#6*?Z*РᄌMCn[)`ƕQl3Z xkXxۚӻ%g|` Mԝ?}{Qu^XΉSuf02ێm##J1tN5z}b)a빝C7M7ndD> GݲTQ{1a@FҼZfbۇK/T:DLIz}-JhʵtQ/teA7o6R˿J_ZwF,dlBdž{ڿx/BZk!zdP!Z/j}{xltк{xcό43|>MiqiHH>~G^#Y-/YWgy,a@|ۋ@Z7/ZK{}f]({ؐfK~&8$EV-,2v{MO¦/M|"NY{\s73!mw_[3rNap'y z.>l(B '.{ie(oͅ}KXÙx݌k\{o.pv1çm2.m@Eʏ ҟtKruF4?p6Y`6!'+Fu3:/gz"6J*? lA4R`nݜ,C X_3Dxd}*ЪM펎_7yL`25Q5D&NJ͟zrǿE1έph P#I\t+ SWgDž.Kw(t*}w"50BSxsYlA1~6;nVx-k}:lk esU{Kt[܀ٖcF9HiU6 =_{##(Vz滗p%79cr(¡`0M }Y~ͣ|pw_p`۔qJGֽp0{eV";c.ڱe姎o!La1^ל]a۩jjl :hP6 ʠO{fFcu#-lk3`:phgn7P3W/A eqɬ--ޯ\ocdhi=UA`'{?Sb>fyЏU-YpYeE,v uĬ"Mi%#aqlGLz9}(tK(gu2e̷.8%~pQ^7>̚y\|2BX09GܕcER|F&ǧ- v@wےXec ,-EO2޻n5`Ί(V?=5nU[9册b}f"#xC{}w^ݎ]bmԿ6,Xo.=Js FVHӇ߻@UqE`%Z7u OGƚ#=no&R˿j=[l lAQğS4{f]1wPɍ"{4rT3ÎPR<3?1өi.Gakf`{:˔{6]ma> nLrZHA,1h9ruoMѸ!aoܞ٧~vIQRZn Մ'3++.stֳeY'S; ko;ﷳBXrhb>?'V4vqY*+=ƫOf8n8Gk.]ݪ^BK ';b¿s3Ay{CzK6g f& y ^4?jb+)򒉣1[<ޡ -domD?OXu.r#= ZL5DjWmz(?J*%P6rsncsh9k_me|жy6 XkVH}sPzh0O,F+G{I:dO%"Gԣ!?ic>Uy `iO~|dq<>u&b glў3v'pՖi{UV'} ۯ\=N끻uL֞  '{yŸ]ϼ0EXĀj݇abZıĺ%}Udܣ{,Grղd3QΦwYeCg.uWNn7z aժNc_ r%<}\8'дNݡӣ+oKxئ\/~x\5DjWm?8l :dhcP nƕBGH쫫>GTm*Sw E"Xh[rsBe<;hpJE\|yuR3%"JOĤjĴQGK7V"?blFapn ;,L<鮕g =1+װѪ+j x}f-I{jF'ө=Z~n|ײǝ7==>;5,_ ź|QpESC WFlprЉsy(zJ!c~8.'v`uF[1Cp{=\_|.p\;i%x[X\}g֭LcƄfBFa1L*` Bp:sh2Ts{] 5h.}ͬ>U0丐8w">|k= 3֔z}]E6v+<8;}̏m#{!)c}]U56h;sڽJ_vpxZjh3# a8O*SnP~*57+wi%XⓈIStdӘ6ݷ <U= n6V]Z_@{F-0|;un֌BIqaO[uwÕ&'5J3ZUW6 J'eYPNGG6K o-  B>H(84n%pxD 8)G1}9b C?%6?:m}hl ɕ HF&/B0 fנ'ԜH'L3GIE2 ҸH?C |R咊Kt(>vey ʞ>#u{o>R˿j3Z/p\͂(9\IYbyb) ZheE&kBAe`M I"v悔lh8J!^!2yLrL / d0qB56{FYs!¥4l!Td/$2]"i(N"PXqQ\Gn /^nR|o>w¼l͂(=`]k%eIDF'W׼ cabAIE&saN! KY #TS*AIi`Z)uB 7A|ߐq :;Mƪ<7)=R@F+J6d쁅!>>b%YWp2)N!.,}N6TJˎƿH-`e+:0SpҭnzXs!9v?>f: #GJ<'eb2@^K&, W. 0E!. yt8L>Ȧ=UOg(Y8`Qw=TA>UUpZ'1=OZURwje$ R]$MHo Rk J!~ U|Ўzcf2}'`stNN62Q6k0 g"JO4:CeBT[E#8A4DQ^BcF\X c~ %YKcE p5FjWmjfAI(}C /JE| W# HK*&*BACŠeC|+ͣ[@T/#|DP hj2=A.F*_`=ad2z2%(TD-A(Tx< v[kaYq\5FjWm}8 A9_L9BaG&|.ӛ'yM=:~Ш4~UFc+/NExd3,?Xke>U'wq o @n:api ' k0,T?Ÿh2GvAQ=7_*?0wS(&gxpdB 4U gρ`߭LZ *ɚIf2¤Y jL+Xw8RUs 㣇,RMoh:OPQ%ʴ|8@(dZ7_#P6 ͂RH5lݡPZXѤp4,MW|iAnhnh{s 0+̭"Y(L~z`A2_ 2UsX)%Q d5iF@G^B_(xBЯG гTqy8s"6I(8nT6 ʠdet%sl./ 55 +~XT\`|Xkʒ8hLp#Ŕt]P6Dwe}A02YyE)bK (NvT#t^_HXh:/h q:W+sA4k5EjWmbP{/V6 ʠ8 c<8αld"ZdH * \,ܘVŒ< ۀ)7֌F@>ן(S|#RrMX<@D 4(1x,㌱}$t?D($&H5DjWm?E8{YPF4?Th-q\cRtG, _`rtLP(5=ƶbM{S4l?GѸAR>N85&"~B% ?mvD4˫Osv[]m[@3ZU}!YCbl@B 3ߤtMI;[m}>a N}BbV$OX6`@f )8'7 7F UL~h!cMWQ2OA0%0]c %{*7~=q_`^~۴,F'@!ʣjg?MYP %A:_:xM~ ^(eqgDǦ!d}7m˸r{d0٦V +TC_lT(Oɻ|iW~Hm*h㎿ c|>zΚ  /6O@-(ƿH-TmeYl RܐSlw$8K gRݦ>dj%?G? # \(y_v_Y+5d \_L"%5 ? 2a0rR裰ydy_$w_(b3/OEɊoP%8R 5<5Djo&.(x}wX"*(^;*bEaEQTl H{vC(*a Xr~_7yeϞ:gzGRDCSȱSC@{f>)g*6lqqb\`nY?2 ֳI}cZ;^;1M~g!hW PKziD_P:U.v&փ~;(Mųm#]5"fJg>o]x-L>UixYT>rDT5՘#&*~)ѡEtôîk Ӊ.:yr} cL4̣ʤ5{6̅s(j8n n5bɼIޮ A%Z4V7\xB F+'b̴HkՎQyP2EH/R3)I`~ 5f,@l#;ށm۞)=Դg/; ImNl-R|=?Gz)-d="6wV.H^ߜ4A,b 0nSA  ͵3jrc~{*F J`7Q_W4⻯vcgn ddV6خIC0z 0j{R[^<ѵu3ӫ$myz z#4b}"V_gή?B[瘥64)8gǸuIawfb3R1WBp2_~c/w׹ٔofr3wnj-J;2-LvNh3qfƥz5Czi7'=?Yh{' 8.傴"}5D0bC,cƲ!A0> t`sݎo]}dv鄖yȈ<**r0lDž>}al8|$aų8>Alnލzw|C[i=t²FWϲeXtٌs]* |H@++^K7@IO OM춻\GEg?>%+ƣ/=(/-?EvU`O 2+#J9fmC_=.d{ ?~IV8밖vG_mro!huzI_Ə_y篾Ñx5ojɾ0g<g͸t`(geuRXY< ˄4|6cimGy}N;̙z;UE 8F]z|XF{p$l҂0߹bZ.>w}6-\Qln# ,Ҽs :_Bj$`?~aOw7ģ,>62%uOlq.4,ojm2zIøm=c/7aK&~5Ph^+ >O:JOYjt ޺F_z׫Ǡa}]FP7fD\9W$5XS1%#E9ɪ 8 x{;| `'cL=Zu:ګp F11&ƵO5KFFŞ%⿡ځ7eӒH #icjJ<ƚR*azbd ÿ[3 pT;^ɹ׻; Vlɷp繷B>]P`8'/;$"ZB+^K7^3{Д/A;-*ka1]ɮL]י=,? xrHi(_Y>tDcQӢqLZgKbY>`sM,{ /ްy7ɟ?q2q&A@ ⤵oAxڼv]{VL~OCU G.'v~ OO޻>>񵦨AgH +t~ p*~K$ÿxqEGxp}x[2GzsY( Qλ״0 Gᗸ X[_v9";6nsiFI>2H41p[8kG 7)C;<EAFH/<јmAÔ7/]9/t SKbި;spI>;vȝܥ Κh;guy3}+; oӞHY'zU䤚Ygv%Ol%:~!zZiڳ9Wed>oޑZ87m,WcS`u,fUx$Ū}&5wsd̀ g1˵'Oa?no`եI)cHC]V/WLXu߲+SѮ˽t|km.-MmlXXoy|݇Q?<֊b5_o?jd/'F?ș %ށRC'PHŋ䍜R=l>x nDw"pj˿nz'Y}~|p8[K.cNtݼaKhX ԰6 Ȝ1w/w8=_+ _O{9kUε`3fYT!̖q67 $|¬3CH놌 =xf#˰Q|߃Ǟ*a%”9y}Ny`8Yn-e z37imz$5<o"عzjQn-+eƭ ۶#d-ٸfO_ cNȀWb)q- 8砟&ŬѧJvSe#21"a!=l;$ϗ{{7qʼ`o8Wso؜KJ]s}M = 4_X1QXIGu|f ,z1]4 =xAww^ :-|!Xʦ!>HAy_^(+'_RԞП怅nv X.O"ۯ[9.qyx_,as5b3x& |N 1qfMiZ#0b` xj~lKE(D.8v38`8mr]I ,ٝ<2FmH~E߿W*Cx?1m/>8]s{;s*-hm{h l6_rPBbeWc^q3.,0`-CͶ xOP ]'- mQ}q"` 4(`XWH/?L!(2S!b*?}}=c-^CDϫ]mWK M7Xda6$`gK8 GHB1N^f@U!kƣk??W = }~ccK/\n1R\_OhGr.\/Ξ)trɵwiwYG)zF\'fHp^?=,oP"/g >N )ЀqClu,kZF^0pȚ!3wsyK[ˊXLNZ7+mBx{+~Π^:_)gdUM0=)O?bV$2#я:$8*zȬsW'4}Ke?&uhЯ/!z<} p,h5T{<(xk DJ 3]lq]=c@ڬ}m#;秕*M\H{]Cv,iS&: fgph~|hy%fwhkH ,챨#yrke>Uޅk8/}c%-z#==vC  uE9|%ZZ[Aeq6&ԙp:w"疛]0.o"UK%UƗ0'D-ߓ^uŸFhg.U2;gN.+}I(p-Gl18;woy x]YoBoh \g}{ *p1IP19Gz61/f̽=x,FdbnXp̊IAzxBf\sdžFmrzFIkfsr}ɛzC|o7zv. J9\grzuWv=!&`{Sš.W}9Ôo',p?I @Dm9Z:]Z'6ϷhS7B e;Li^|uR]Er;fLd D׫n䩳 , Q6psFd r~$"}|5O2"wcu[C0ߓ^u"}9i'~q1lXԅǎ`GIEӏR܀$Rš>Nv'Pyj_A?ȝ5q7Ơh<@&&.&AqFD|eW#HaTg)8}}\ LF2瑠<Zv0>ŒsIԋPhRyl3_ދBp|RB3 LRGA \ϣ &qGKL9Sk _A8KC ŴSDY.xZZ\\yHT*$jZ!j翚Ok@+uʠoDZ0Á0)ʦžPr.7'v1'Ȗ8Ô r5RNLL(}n? ewG ' ?X$Ԥ2Y> DJVdeDBR Y0>Wr'Q.˿N/)x>=M]D]JiAZX*e7 8RT`WJm;PWm?"OS޻I ǃ)CƗǨT(ɌKpU+D1bHZQ1r:YOFv)=ҕ+ 3 BpMӦ_$Utg ]Ai+H'!"CJJ7+ ˀTxP _q._KԲr^e/6t5rY eN8 XD\EP:"ibzy|֧RSyhx Z0hp6Ÿ !@(bh \C,@ U4ko^Sxh{u rO!j2ƏK9ݬ0B*Y@PxRS'F pePm尶k(eV@{~orE*Jj 0i IeH@-QC !0# $k:D]G*$E|>cjWxmrGRK4!/|b1 Wކb1!@|^umԵ qʊ_eg1V87iNtIŪ'% ށ;}K%ʬ0ӎTFk&%8Ɠ%d,/ MoHb:]*^syV*DsJD #XԬx4JOix %<\-0PdkƹSkFqj{u S!")-X\4TΗPRQ' (/]E I*TdJhH R.K5=n$[&+ wq.H`[⿝?6+=E.q P{O >p"|*Ղ*WUF}@bkᡳ=Y\" Ã%Tg!͉JE|x$!PVO*6GʗĿlmDN~ HHPl@x] rM P;bK+yƉ B\cS]O* pA@W=h!$H&H/:@Oyj{$rX)WɿZ10#bV(JV dGX)}0ʕȏP$U2 f#6W%GE횖)7[bO-YS lvX&$kw1Y1cT P_،Ez5@zmzNxLmA-:Ҁt2^^R~P~XtYqD>u (s0EXY;NkRa?pP4cU|;2cౘ|))MCQzCfmE$Y!je@}ۯN2 =Ÿ*T{lUcIc˿n6M_¹na)9neOG!ژ_Guy,*XU FfRQ,`s93Vi>_O I<]V"T%10h!>B-0p.HO PmeE?z_`>?=r1YQ%~ 0vYv3}S ]#u^uViuzYWZ.xCNeyKbV>Ce{ڇTPb(O*2r[ B ?3Yy& EῲO(y# #[e/pb>j!IP9_.I/?uIPNDY[\"W}e،(rkHP ˊߞC3j/*cgTYKpRv.G1 IW?U.mY-RJQݔbe@ei}5)eFY W v ;Yz{˿n5Vo#(7^M`x)n`ap_ P",rig¿}[ƅ^}9(io8. /H/wp][{.J9|Xg#5oxRɿr;棢EϡYt"cq9 e}ps!֥WO4/ymK' >ec6FBcx{Vc8xq7 ؋;{{k_rfV\=197.̞殠CLoCy,'U":o/ax4f7Ӟ-8a$pH76G"܍RZmHT?k*7aVG^ó.vVZ@r}s#V<<[*\a~x7~fNw\ic!]d `/֮c寃3܅kX뤳 '+7y_GYBǏ8\t89.09bgkNz9>.;OWbA4.K+99s`t$j#a}&w=6qv'ggwKW%+zbsoȸ(4r P&gZA{ n =7i`?ӐrM7tQ9S'Ϲ 3ͪ5u޷ aVw϶2ܬۻvvqoguqw9s' kfz(+JC%ڴFn&3ϿH2sX]!gAԯx7G,]j}gdn;vΨǓ~pjCYW0R&-]Y=շ5p֡擄cZLzfp_opRɿR9zѐ& ="9ʦ[+loc'rbt}h:#_տ<<(z YO \=w/y|ܸ%k=@v{izHݟ@72sFu3j&ui&#/޹fqф= "{C}7{q@^>9~tP|'V\'v[gch][.ʄ?F6@[77^;¿_&A>\8ۼ+ur]O͌{Зk z^%Ilxc %GczT8$fn{<bХ}c*T+zy6>C#G39Q[ټ>x7{iw;>Ξp+, |YSvg)9HK[t8g·h=p^3C}'ܧ,ݭTTssiuU]uiw_6a6f}5̻_s>*]dzo~Y˶x[dߪ͈a'L ᰍIi c`_ͳ!ʏsbM~wۯ}5&^|׋tЪ`{LK-Yod߷s+7_"Jx]|6O* xq$|nrڸxمMj"!FdBRS_([)48 Go="&;K0hrd@E[!Q,[a~N7pUu[[~82l=WKޜ`% َ?6܉煡T٩vB M*WnnVFEShJ$*AFu68`Eay#o=k -^bW&xس0X@ɿq,+ɰz)rw^mQ-T:_Z&tl_r,2(w?Qn[Eߧx{x܀O1oy=Mkۯy#tϮtj+Nqީd֣_c;qnӋl&=[]ڛg1sǤ>;sg=7.H0G4Unwzw.۩KT~@=_= )h.WƝT[)8cfG}vb) S̘Ź!D_hd?ؿo[r_", V,| &+77SFz1VShNԸ5|'݃:n\q)9 OUvd̙ԇY ؏\"%윰o^k&h O5 PZ;/H]k2og rFͦGDJ'pνK[ [{ k>r_O#g Vt;';.i% #6-e_ @̑ $Sҍ}GNN:c%aqf`dѫon*awSI+Ƣy9CXGG~IOEg=_=b|y lJ[Lp=rvNW:@ǵ|ۡԇۦ%=% 4NZzO/ R~)MhZsh~xUjmuzv)/^=:kx0o67k ՉkQ^XUe#X_9;9Mِfo'Rt:֣+ψX9xs{"х4wm%[ax0ee3^ Ց_EaB_7NMPusYɭ잏Jzx3ձDakWz+/wgQ-9H|Yٙ67z#VQ"(jsM^>j M;1t6֎F{x _O ݊C\pvMf>]T:Zeb􂟽_?`ݗ*p57g}5:+:v3&{]m &+7S(z %}^)4 pkINQ{D S΢g;&H9Z?#3K(;:"§lep}7Yi+S~{Lf}fx-*Lg/Sdv<-{n Ɯ._ю{,}҃!TbVOϒ3vXUqq+⢙Ɂs. Gj7=pֺ_ɛ$Ǯ8|j?zoW]G&پ R)أ=Sz=3ZָO.)sdN%!n_@^6-D[W UWK0ԷAWN[I,Oq*#V0aTW(fXP5Y4z'J~!aICƼyK{%;lavP]qcNKOo& tqjYm p=k\~ۘÈQ, ymI"zbu L#Q?\jm=^@MZ£p|^B >WNCŋcNFV6kr!9ۆF>PCXƛCR/ j9Z}NW_`".-K_Bb ¿aI%ʍI>=Fi6=AnT5-]\l3KƵ|qSdfڥf0f_v@Ϳ{Wk75㡋e_={pܩi+Ό`w|KAF&sn86˙U \;-}1Xi [`~4|G {Sgb &B]ڬq`Ս tEXSQuӡuw`rw"\G׾s['/#d=6iƋMcPS~٠O~3K{pxitg] x.޼]f^6@2 `0PeapTt؛dZJut80MT>:]bxA{l#klrK>(sS_S>wLڝI'DXtoXRɿre|wj+B#P3 `i+֗# c x=s|'r7mR -m5,o{X[*mraz. j񬹆Ix&rb&{ZxDӏ28qZ-IS& %?WX6]rA: %+7KsIE/[~QUO԰ZX,~⺻SBEKb }H@g\0#ϽW%6TnSc:wSnu0cYHHmt{_}ZC0#?""sk=t ?a05W/CWGVw.wkΟECsz3VzmH:ԡ(YQ9N t|eu;ғϋ`̗f:o k.~Lܛ|iI`i4Kc/=;??lYH11c;MZgsyǟ:(N |ȝq9MUE˦vYn 'MXZ; l| `_~Na:U\݌>sfKhDJń?_Sh<ʊb8!ipO[^QppQ|^_DO@Ƒd`>*ppǘ$r0ѧ >zsé ^;3=0@ކQTt8F=}(Gy8 '1 = v\L?܇^ec9x% >3*TPzs-=ƣzOsWQ7d eSր>3wOAI92-@Lb%P %}F!%cd4" +p<8VP\(WڈS'48ҥ^P $)%0c,͋ hAW&#HʷIzzKTGSyWk{XZ7Sc(PhJi Mr8*T T?*z DkB^ IrD>O^F;:F4|p +:\$;H t c/ޫ7c2hOҰ1I1Xe'u)$<i"Gn%࿁v~Уτ%}?cI%3i~m;>Ap9, RHCޫT74_ߜ$PB9J>ID_=') L ?.g2,f0`<_TXcdQ@ .N9rQ?^\*仲T*HLb"kI=!Rr @P8 ̾))IBq|f@|$`7 0z+t\@Lʠ8ohRɿr *EOqhcnF0cב!!aa^w`a?;Rz jqZkS#*A]BT eBC]<LJ<~tȅpdǒu`vнsQBz< `'hСY:˝YJф7ΛVGו97[GyP?4X'Xv~aԲ0>Ǐ k *V'(/Lrt~Y\|t)IbCcEID#1n \H * quPwУҽS@q0:}d8ْ0 P!j\@j![¿I%ʍHak(z B;e#8%!u3sBQ'48'G EJp=J"=24\7G `W,d!f$} !( BJ?RtiF?- S@ngROWbd!~jύA3(>H@Hwfh¿I%ʍpABЮ0~|:~W7IS_ #eFXq0'+U %ƒzGƩ:1I`Oz8%h INQ@`-: "D/JxAqjמD1>IJ'd@fAå$`XY`8Bvb *Vi4P+U70_?$办Bcp&Y 8~1s ۩?)_;D.'N$&" =WLM\"Q>]z8%t`OrtF-d=!!jvw/:GI"tiO6 HSh×u %A045wjǯpJHזD)No Pa{~Qk NT,ŸPeRǜ` ZZ]H i=̝¿AI%ʍdUA|O%$3忤Ui&&2IE?uB$8QUH ^(2L <]TRpyI}kdcS%?R/QHz%5¤p9qf1a1{GK?[u}ٯ`,Ÿ MG+W.T7()x]wXIO"f9A;&P 3Q@rݝ] d"ED8 NS_.{[?v~SU]U Ñ瓬R'ׯQ~MaR|$SM|%+"7zY&HwT?#@m.b==#/Gj:Qq;@/Ys~9-q4swu S:)@!!Na zчen?Q6,]u~u9+9}RivlƎc0%X9j4 3^Pd?gv&ՅZ[!NSy|(趿i)uwtX#;\j^,jp)>bʹm,)0ǒj0L:xקXwx)@,?O5r*\`jx|T#((LKwr~ :f ;mۡqI9OK/UƿqgQYAtl ~E;s:rPZז9$b}e]atlT2L4 bg =7 s)czMG/iLuZ|cS(H]g5p44`y7XddRqS 8lm١A/g `3No"F{sl<_cpZ wđ=B;kx}w\_f} -[;4d{09kȾ /Ufk+^[ۍ'yɧ977 &櫣pwC#A 0r#-Lu\}$Cd%koOZkVAgg#ag(x:}GMJƵ(=,dA;)*"֏U֌Ţ a ,;N~S\n7;U^뜶:Sŵ.wsF^km[?{ojfόyյ.MkT93RY;?7c1v_U\ʊռ>G᩺w*׏gCEԫ 1da*V!ξ;im0'mK}fDEA'*mT_C6|.F9lD!qeAq0xr NYTOc<̝u1Lq9285کGKO=gbh5#}EiF-wn+=|~G8^_{#>B?`zŝ(LD}gPwFqukPaN7/^\i\,+>j {VqK˺_!ϑ\x&ՠ'{OrPbUa?=  r(ŽY;X_UuĦpp쯃 r} >B.JۯUJgvw5h47n#ORtMU-Q/+KZݯ,Q==ῢsƎz7`E «*#K߶߯r^CqhYAde54T [V3W.2"k/":M$&Kҵe-@MȤLYD8/Kf`5L5͍Sb"su7ޘI5<?u{QIE_|\25aL.?P:߿hNzB{];`ѧa:x.1neM|=|:sw]$wIO3ΝKyGv^vV$j_@r/4G?<^r7IOFzUx?ZHTn 2Yy|uF !6kC͍wKن<ȸ{}9wLs!ޟG3L}w2A|-;b[v:J{=,/M?ÏK ~K }z8b) ѩQ[:IEҚ3:.*D:|kz7{S5n؞IAМ:r|냱ԱMoi.4NS#U~ 8jӴ^-Qi :8kZ%~Yw}Zn{8 `۷ӼAâԏ[\.P:Vك?\jWbߜc+PKK:hM- `?5[{D`ۼvەjo?MӰм|d.aO JQt5{FGf)\6'Le=Gd=Gl.F {`ct rק_٬3ȩpYj$Z_#{G5Kv0/"^v{-y=/ /O}MPpWM)ו%CHg/Z[q9# k;vfw< ^LYl9fRb ,k~p_OSa˙WwfYQLlfҕ㤓J+Mz|{T2,ё>m42H -{tf@ݔ^Cz5M6+yDte\)1?bދkw;;ŵ:@M|o iאB8@_dzs|лaBN]S0,&4"m㟿o@p&YA{l&L/3}"84q'^E ܔ#:wTI׆)Eݦ@Qs.y_V/^Y YUG*NLoUe;*Jp?^mXNúP;t=tqw4<_C׆:AV=~◗H5N?k4Yс蕶_("x՝Vsr7;e`:,S eɥ{Qc;="l.}3{uuߧOxksq@q{Q6G?}wCrr7 iG.*W*b+c,Ygp]o{kO3_q`Zn?6vv>';hIo,Q.?gL2nm-9"_$YLc*q:w@ .N]>u1W^ūE5%Q251Cvp< L_*x/B??ؠ^!YuYK^iM#>6?9ݵW5KSkLWF=Lޮ~5Sq%n[~͹FC. e y0~ONsDl} b7 k򈛂S+1h@/Ez]M/zR,O/p6uzBxv47ϚBjx4)F޾/=]βMVVzRKFM-ɤ6:">TsuW=\ U~6o .8TE9W~]lD޻ZJ‘Un?}^a.:= Op<9Kn߳dix;.I>z]o? huJ{o"^[˶846>y{Ղav0KUCsi+O1ju.u ₑAnxϟSp';ү>U CwܠXN;lvHY3/S]VE^B<+\gql+*ʃ}-5οWTt-kB51'eSš7uaCy=!KH.maMw}FzҤ.)ʿ&⤣W^9s4k&j⁇;cj$y>?"REmy.:u.Ę)3Ec`E#tPsˮ~g`F}M$j^nLX*>/bL|W4P;ЅbUBT11nJe6̓Lmt7 _K*eUJҀޏΎ`wLy}yw,UW m10wݠݗyFۮ*k͂wSmN.x{.~,NzUH;N:Z(#濎A\ z~ [Q踀}HU qsvs$)N]'fNo%G;?y*QT`/bNN0`x! #'m. Czqxxy-=뗼4(%ޢ,Gͱғ#^w4 acXz; 1}}2EHT8zj7:X52(QC/lR1Kv=a8kw> *!(oi!.V cKu-Gk3}R`PO9#i-z ?8UEC͌U`imx zTF}>F}HQtCNHJR1gFzǃ+G8ޅDe?>SrN\F8 =KcQ9HO}ěY^l~oV?`F1!l#B_}Je7s'/yy;Sy|}K ެEcO]\]guSi^o'T,U>xbiɹgbxw0<,bXYz|7we ͲtIPv,.\XG#y[:Ui K<yg8e^z.w,FV8!cTι]|=ScC5R6!'ly]M-A?] w=y6k@ou/iW|bq\6;7se=H6H)~rPR0L% AK;bf <shN𠢂y)s'm~Htn9ǧ$E3Cbs#ѹe(>F1A"bw$pL<p.~'b4 | .C EA4zZg$#3'w9R%mҲJ${ uʈGJ8ń0Z` !ǭxYH)96HuC\h݇wld8XNi+Y|DBMba `$< )E B(+|6&ЪcJrh=98Ƅb38H/46ŨigD=L:E,<!H (0 K߶ω"} u-)yV63`uGv\ߊ [at83IǶ SBr'IY\ ^<Ɗ)B F%WyǦY3Z҆ C\VbϘqƎ ?9O4){EIGjfDGF`EQitDTI.m@C:HXaC4@( PKr[xdBӔ-"TcLqP9OH{Pl"0?Xyb 'b&v+, v$?6% c^ 9E;Fa#tMK߶ $Ά)+372̱Ƅ$%3(sܢy\ۑ$,#"~bAz(%k L qfE`fQ^94a4@Y\Ɔ ??I)xid3-?\ 0/},70 t%bSK߶?,IYAڔZ?% $2*6/V[G/nDn80Hl?`Zvl$pu`+$2bB9+\ $-c2ZNIL) & b)'Lϑ$Hl8ŸYcKd5bb/\|>t2bzD\`aWY<9$i#?d-dz %^V`>,g`3M2#,Yh-€>-Kk=?~D0W|;b^>b<qGqjOH08 jMIfaKd .BK˔6%^v0aUI/\p'%sTV$f|$?A21QEI8h'[nh/_$T7q\-e=RLJp+5 X}87_|d`_N,a'`<(>2H|_hG\_Zش$?&!@(ݗQ* PAhDL`4-c1bMWB@P6 q2 D)@"g$HAHA`AAN})\6۸rD-K[oWa@ tH(Vj5ObNꇖ!q%9 YDXXMOt=FI [eӂeD;$Kz0~efuKa&[h(AC ǐ 閨)i8ـ0ѡH!dpb/-8'$HV`(HI_~e p"NkOB1x5_yP꽨O+LlI aK߶ώe=T)3_ 96~ ĔD]Z "-Plx$÷ʃ`G$hqe_~Xt/pAaq) b8.($+HR/twM`v^!{B+Cً%h[<Z"L?)A-wK BSrGroFJ54iwdD﵉'[2[$D%#,S2E *WX!m#Zd*7@|cKL 0#玠Jc."?QOK 3^AhYEq fF w/=_AbYA7U/)Lneʁio[4lm<^%D C$$<Ӳ~,1{vHEx I` (Jf0D$~$)L "}ͤeO{[H0KQp?f?tJJH?D 7AoAH.m_d[z Ro埑Vb+Pn5'l~gp w[pq؅}Ԗ:Ax( #-v%yBAT, "<%D \%g#PND?2z>\u?RGZP'Xv>Ac/ [BGM4Rm #-[9#i"ܖz#*'D*29HO`@{f~,H"_&vk-:'Hh;3iU$XJ AQ|n~rC2v? C :7nNIڿT{/Bg<4Jsf1)\om$EѝND:uX}e68Υ(8(kxLC^ƭd'cnc>P,|MܶգH~s~u X5hUtyXйǜ[x]UE4~)yRѨ9z}k \h_2H1?>~.|Bͦ4A><].߁ i=3uY\e@@l'Ɔ&e#Q9r=)mࣃ)<$J# QW `E:RY߇G_FH.MAyÿ͓Fn$,RcW5s. W#G W-6N6Lxu0a~9%9mA Gl?vGU^ jL(يΫ~RL@p(<͊'ۆxϐcqؽMzt`X}#jݞwXPS\t ˭)*)^g %'qݯÜ^}.ɲK,&zѯ3`M|ylIPr]Fb_67rg)\zu>T2y %JyO5 @ڕ-A,ی+Q8WG:% dLsvε@7f?j2C.Ќ# CG>%T2w(}B+ru[`յ49"7mzzr-1H"kܘTR6ݘ j|n̵l\.F&ALB[Ca6}f?5mM^"gO2{2/hy[ߎUr QFOw]bYYtz7-5mZl} v7/r䷷=7앺z8ok.߸W.D(+ϼGb Z bZꖆsNJU}~5 O" 7q\EN8/%Wl4$ӍǞpy(ůr)ݗV=gTAo7oLr!_Z$+U¿$zҢOG 93ba>ٯp"ex]Hk;$xSx䘑[X#&0}fiR^`=g/bv3#ѠcmBK{u(&=4Fwt;kly@F=#NV3yb!1';ӌ^~q[^|7益Uqh)3wO&hl@(wI:0uȃTk ߚsi'd Kn;w7L僋#?- *oIŃ|>ߖ5Rygqթ9) ӑ1YYN7& ]W>w'j.wU#xP)”c\;od+'ZX5{r'Tf}½Xsɼaz< F,3L׷(CGw.Gt-¸ wjpϘvop׻5GpKՈHN˛ck#4ۿ\gS+T&ep 9^MA/6Ľ[m9mPaw9"7m/z~bMDTDx] >[۱FŪmƜ㦸!C U4.EY)BGcQygE#cpAlh}NYp >-~>foAfZgǽwt&sͼ!Ǫ*jK(_zMXe7xPohx+yl  S-VgU%f/ z`tb7gdg^ 55j.Ml׾}wW8s,qz>.:~.rv#+RWoMH;mښ/c9fyǃCGD .(s|Aᤂ^|xJ/%zz+Y?@* e=)ѣӸ)=|RxUՎQ'm׉=[(Fh{/+̘Ã"ExMc٥n=|`P'݆GDla:4wߥn:/_O&y*cZ׬,sp g3wb3ꃫ?g{$vG3} G;"5qBM-ĭy7 wPpW2b^ qsi^=l (~^6u[k[)ΜfNn%:#;J_Z l.\nw=䠣[cGe?\ZqU~MȒ,ZXݣUYT)۔xˏ^s**HYMɇezu~)^GŎC/oz G(u1 {\CoOR9R"7i*.z 2п#!Hh4XCR[[t]p6C(bnZ7e ,\Kꉺn:t-oܱpn8b΀KngZk Ơ[),iOXb3G*hϫfZ;q/n2yDih^r;>ͅag}ԭjN|,[gw0k?8z$*jH~\ df{k- ~6[!>iVC#JGl(_L8kѭ}O]֣-3) JVSiǨzsu^=@y}]捙2tUseC'Y]}Kk={;!pyP^^S`z,z! "RpI.w0& `g&#,1T̫9sc[.sysQ}"㣌lFW>ss=(= 's1=ß Jѡ{QCv8[[ s{:.9#ws%O8>'fMucT<@ۜs=9suXQXGSҺSպ`+{*dX,s8=&v\Hl^tH.MN'&1H,kC*%L^75#V\#OîVnpY\Hp)C9̘=)z?7R"}~F|A8vÇu3y~e sdYiz[{ҮKFfR[ޠ^#O*X4.Olaw'=Nka-4;y8G-4Faf8U{X )3 u潳CORq*ZXE(z|ۭ4<8y4>`㠀ku-\[Ǘ3g_:>eC.Η&#kl8^~{ZvF(u׏BçZ=Mg#?{>}OEXv 3uvv]KߴmDz |,!Hfi?*6[R7f(I[QdwwcX\fڗ~{ ћP9^fH?V a c^:y7ة1G;Su-?F^wPL6B&mwSl9eRl+21emV඿SƤiHS9.7_ۙ1_+kש)[vR ^V|$!G_\oc]~h]$p(oyS5mL벝{X-/Z]ѵ/2y}PVb,%58>q:iA.xߗE?'R3%N*smdvr-ݘǦVya ܺ^1BNET3Z۾N)rBro/O#RɗAKGҫQef`8EN]mOT^~Y@4ytqsL6u 2gLsBZ(1t7L+j jCHX?y=8[8TU&GXuoTEk O^N]#?.)M¿*R~аk~9أt9vz`omWDY0'W6wo=UwBP9@"j{ fbWnL[؋zQHR$`Q54:W.jA%Enۄ_=ViYB:뤺1 '2p]KǧQڛ`=l ʻD@#Nڐ'j%3AVd/6# /~xhEswFVtx %jϏ/ƿmYAײʬUc[ɇu!.C 7qlpfN_y:W6ҩ )ph%^͌rߴO>+F½N3ц677?-[=mI;z,N wQbrIEr;TL>? ._: 97C,6?7"# dW-oIl}Ee Jccyox-$=4Db=xRϊI]V+ #cz,2ހ5S=sɂC#9In.gF^- k{=^| dC^wa{qD80ߠ*~zoݺrBroU#DYA fYMkV9jJr>1gwՆ.8{u7ދJY3k9W pCS'T 1jԵgu( ũ)Icskl;q>sU|f$/:lO/`ÁE@.2b oZ뽧 s4ɯxr-jur2|lrD cO[F r|>+_*$?lN֓G@R+@2_aبGD80(Z3yCͣ(Q7擠0OCǢNj8 \%3ExcA< (?1G$xgGb7Wx}lv0͹$*zG ~)px42/OaI?n ~-$b޹/ n7:@0w Cb02O'\E`/-kOnׯFq[@LeV$CPk2]%y6ȓvmG!+~2t,q-!EĖ .'4#)I2Bg$=}-V>O aǯ (~@v] bhA@d޶OzNM)P ͱ9"7mw"@C%1r)u Dn0 ȵS+-ڤKph_B!6yp KO^@E^2^\Ӧ!ҋgOF?>Rx4baTZ"Xw]SbAshߌ9spF9"7mwd==KHYAZìk!<:6&!$+?9j?ɿ 2*? !,4 D t1) DG5(0GQ"y2m Gdҳ%\~ dO0VNT ,ttW< E0N;ֶ,vhL@h9nKq9.b$_Z$H'YAZfbjuᑰa'@I7w^p#KY2A_VHp) QLLc$Q@<0zqF7Ǝ( (pJ3~F|5]Y憌'` \p v]<`@ '&H#=K 0K1q$><%<8LrI8rj=+eq7_'H&㣫Dz'FiXOoU,yq?r.`b)\4irck.!HwH&@ȿm,xYtS~?/uኊr~+Q@IAJ,Ar  5 $ 9 jBY,HoOX?AS? [GB<[̂[4WT4EDĖ@\6I|Dz ~Iѡ99'v Fc9mf])XG#HpIѢ1 "&$fE|1>fa@Z?@"/m0_"DHÎ= E;Hd<Ƌ KߴOoOdmA ~ⱺF9#l-~]۔_Ҁ6$9 b`+.V̇*b`bu?%F2LʎŢ_ qA( HJ>wBFl AϬ?ƥ$?l$ ,#)觔0^ P߆473,+rBro?#} ȧu5'}oEGbqo[J;`\L>I?H'HG==+^$O/NGC ,)6!Ɵ>9mѻޘ8 r)ɟ":& M;!uAQ1 ]FS HTH.?0(x}y@LT""{Pd  -Ei>3ӴJ%HNq5e%%"*=ϙ{_h̙󜥧:y/3bߒ/&݅Y[?E}hџt~FQcGX[Bz^IWe0f -Ev6v1O/Z z\7JCOk(:3q}t󱧇%լLN,ꧦK ~g,0mq4י[5 _أs6+]4K}Nr}s/s9ʰ@m+jIžId<_C@˜*=c{\㖖Ǯ&mn&$Ui4˚uSVfg h$|JФ~:Å7=@FJ}#Cq-%ѱS^t'P6-tRm}œIƎdp kF%xe)/ ~%${{PDlN){\zUȡK'¿mD0*aE~!.]s5s¼#r#!}F+r?FjWfCw^`)صa&jlZ81S-oQg'/2r1}\0gHȸzs%ƣ&YL@oHUy [7GKJ'jhh_]V^.A_搃 ,ꅜl\O;5\b'lqO-@bI&@WVZ~60}٣[+&}_̝4t:m_V:/!9<:jճD5ˮde@>:OԸ6ʡVz-w؊2#-n7G#>Vfbm$A9#{ 6ȪMZ1@`嚙Gfeo+;P"/݅6-e{tû9et(UvB+c;:|,Hպ"ET4u9#nr'1wF鉚aūF_\Cm2s~2y؛cw/ՄH#7}kiSnM)kCmU;Ew5?`p_5G$p- ` ˵ R5zs$ @*"f ldYU azɏ'2_UqNAO.iޯMw}uN_TU3lC¼5A%\\{|Rpr oG,#MO?jif|L&j_c$65Klgս{]I [Wf\o`ʏl,6Q£bֳ贔l` [> О^Uڧt+}JSeXԖWVzf1\\ kC-]:VNS:y /]nPdN߉>ĿX̀A+z1at/{}>{tq=.>ϸ&>.OILV|GN?,c[X?w3;>%&Ѥh_ ϛ"[fZlR khpf:X%ɿ7?u >$k|+߅urᤚ{X͚;ґ'hos|PfyrN挂oL[QchOF8of +)]S:V9|r7mĥ*>-:3Ta(ك"XDx3_y; L݅6:=_L9=]O/ĺ,mqR6.څ~w3G5ơj@_R Z:Λ6e{:lޏN}9 ywD;XPTȤ^S"zO8hT'&LSTLL۵UXG BuʸnEdfc >kpy)8La=Ν|(揖1lُgcH!F/X8S7mcF?9q#9ߊ^Q8Pik_`hy"elGIVإbS"_՟}UJJTY'-idm)Y=cC.#n?oK#2ί,!a?0/Eivpޢ)yD?u;"'?,%~X[E}h[I;SP;KVlE@ElI_JLj&φZ_cMrHۚe8[r ՗3o]s@|q+$TV#@ <TmNO5-Y֧` J6:^0=T0=}L"Ctf^?>Kʘ̈́t90jHeo}m^{K @].b\56NSc.Ϗ|p;>~ru̜ PKjߪ%fΒ;H]WNwwgӀh Q:&^%f쿭2 YbNo>+~umzQV y{鳃[ U.:xeäm}"G? - ^B݅műݐ=[/:՘FDvՏvz0Uӣ 3S>d 3{\6nzd(o0⺺KZ8X?vWc>+а=OҔk1fȅ|]@Y-kJG%),Al䐬ޗA;&~ O( >ƨJ*Q6Z]y&p_Xb'%{zq 1|!t^_?Š6Y߲0$k/j:@DN.+qYhBbM-Hտ0U\ub;+t^ Cʰ?B{^f"PjUgуb>is&IҍCwq2 yQ)sGkۣ3%MY"G_ϔq2ݏv\1=zTd*-n^ZL Gifjz ?2ڬ:2]Xq#jGF[ҷ(R1i]%m]p%y8Ƽ\`v0PyN4n$`L) 'NfsrɯlˀG͞!FDfmj1J`~C$˝:rOl,{-,ۺSx|\V&Qv)Yqʮ/@G<#qpW2ͻvb$HlM9Nnͮ+P\b P{ayuBG}?b@l#Vsߥ-gmg-{:m,zWt*;+v;",}24G #iS{Wp$9M={`Lr| j/: 3gx.?\|tJS.-i 2ɟpM냖xRܵ&<`P#f%]8uE"OבIqkM N=N}$bŝ#C{np ryZ%$\e,j*2.)!PGySs=;q] ؼplf [[aUqL}'b y֯\,[8U"lS\6↑S,j }gW&5!BsO]uyWa`@s߆ÁZn3قS"ֺ9$¿DsO2ݑtQ\).8=yR߯+No FFU.OPn;Шq!5<)xəVj,Ɩ"w1O%=ش4lYY7A mEt_NФK `t}ShiM#b|2oUSgF{ 0n4x 7ҒR#;v~6id! @޲[ mbLSZ EVs/-+٥xIFOws\'_8gwz/ы6h㮾?IRXW^N?`zB&̍qlx d2x=wa@r|tdV+yRfizƔsY ."ðѣTJo?q%42[J`Za$FS<'Ƞ\rvYa_G:wۡmih(4:57{^qFKzf2أwHӻtȓv&N x8'YvҚEx#2 %aC9ߧ=.`TXL_XRt׬z}h$ lؠs1Xnex3NJ7bGogw{qPZyjABeh TMTi~l_o,S'ڑɹ;J_Wz9'Tx(KSrFͩoT-۸&M:8M6]> ψT<+ pyOTs-̽ѻ"OMαl0>obϝ*¿=E[<2Ln23RYf7/6K`iyXy<6RkÚ9#hb7)x62.hF/@=:n>,ȧNU[EU'{ KGeC3=$]{mT-YW dOP.=7x%Ja쉁37P(^L9*k3"ߨh_ѾYzvB3Jw젩K\17}~5Btwly?>*z%'u& g;=dםYh``L 8 w3E?\ˆMS B݅v5ws/F<{%hT $t=0LJyj9CPE^L`߱RlKZK -u.\cK3uν7XUjpAA/D Y} ^iݦ;fƬ8+&?rTZY@¯S#7Cv9/eoYeX|eAKjK:nȮ,G#s|8伟ƬqA(x8fOE>L;Ld޹ߢr:3$؀\H 3\d|&`mGm39:~xF8ahw$m>D)7duO5xƢ+/ƥ3FM{w+wFiM.{-5bUn)¿EsW6`mԅ݅F} {NmK ӨaTYe.ZxvAY>QRrڣ*xŘ %=y_ÿWcŒvV| &cnt2ZvB\H 78 ]ԿB!jBji$"g&ߧraȐu!ƎCQ8дR2i iJ`ј[A~|QS]iH(۩fk oU3#w}=Z!hpi]:qz_H%b. zmm8/Zmah}51dĪhBJvu1h& &hC*."wD-Lm6d.aw}a3B>9b e3_3kxYTȓ MG!8hpn^~b]KMZy3t_hZe~73.+Q(zt6XX we="ikJXrOrhc#$l˾fxyŴismXś o=H䑽ߧ$u1슢Ω0oL85CvOPtxAZZE26.6̜{mp)!{W] 8P?(4 'C!#-3C'-\ ƆYa^^҉xZqGg*PS}Jt^[;q=;4׮ƘoQ>r$yr!5DF '. ^V]sL`otOEF66H6䝢[@B ;zD[:A o06`pO#o t`PgQ-wqA <™$w.aBMMgHqt&wIƠSXAӘa `#݈piiCd3bzu^K)wO^ Xۈ#!"/`!.,+t$ L ?566d稐lǗ3[(;5@tU5=88;6l>"<3]!|:@ Id(sg~W$C߸ ')?(bI|sKKps3bFP!?d|[_[lK!݅6e@39ࣺ׹xA¯<_[BtcxD#  h )"9Gʁpn?V gg:/z+ 9;ށ8!: ȍx%95=kПlΓ2G6#&$ bSN*_KS@"yoN p$sbC,q?ŸbSOp2GLDK11Dr8/SrzEOE#h/٭vVb,Js(MyҢ+#߀/s_4Ϳqi@QB+EQgkqW=-;28N=|N+~hVH= %`gYbc"8j8E[pJ37 nF NϱA2+ (M.$oi(¿EVaFFB[KȖӁ"cx\o.yƒ-G~?בF1C"Ze Q@S@\wEG??$G4@RA-iZGcV`?u_-Hh3՜w=hV"ODᅠD#f^}h'qqt0@<3Rg5G>Kïr~ee0ڡy9&ZYK G@Mf`@SEA$;HV? &jB$o -mr?Xp1ߞ"G?#B4VBbcCXAY Z€q>iv ,E=8~&=d0Az.ӜBpRI0 H.AAKʇ; !L<hҩV K , HR+-OI`6/÷N@KΟS4߾"G_4w_@P""pQ12~^;\*umoj/RwpW ) zRro:#"$$i?5{gـPEu(cb#[xϦƙ}z~=Wrsk<@;tОBaSi7KpNKtMl&? 8&[a-Q@sx[@Dnaljv/Z ܣV:#$nr I8s='P`9Kl"Gğ;P4Y/aw}tJ(! Hw~;H<@"}fKdbp=:߫/ Q 4Xp6ɛ̝2$_+1)헀Vr>m g9 SM $Bsl90@rr/+$xy\M?n(<!Cfz$*! nu9m'C2DB)dBL#dsXOs~y^g}NyZkoK>(}3]ǥ# tuYeGlM{#g摵VڗZQ7`j -5 a} +~ޣ"'w56ƧU$`uMm?jgT;tvwy(:6S>!Ky,+ޝ9+=-< ~j18!fX'HS}d]{T09,Fފa.^xQצi5Q߻< bYI;ݸy!5-pȃ 캮Tq{0WQ^7Uht?w{=*ǽ`AW=}IbP{EΣSol/ #8?DL^utϜ 7M0/x]}GwGbKֺW͸lGvy֣CRGTͮ#׾30֊WF *M05OP\͓F'> l.W\s٪Lb˟`=?q:zi`{[PGx3JEION]ݿ^]ߘy'~'7 _h4ۓA0,kMY48y`6vۯf KekP1)VjA !zǍG!<9ooP{keTeyqtXj2"ýǡ3};t;ǞW;pam# UN&-F4v‘"`uAYQQQ* x&eܜE#2<#bvTݑv1 tw^zP:S"o Roɺw67 pH*ԫ]Rt}SX/p: NccWޢv^#CGmrBn<&: ;X(؆DBc9\_u'2' otaVg?;/=OyΕ% '6gVߋvqo%`v'; E;gyuFO;قAuC?mࢾ튬҃S+>#fw{ GHmYiu}RiŪCR+=uFwXf!gSic%V\_%8hUwǷP;%oai˜,}~c4*S{;'IOA&yu>#`?w'섋1ߺ;P8*=T[N=o"e]rQsZ[ TߵK{jh.sYۑ #ݹ=Mpn'@cЕ*'cg {xk7!x gYc[y95] hDxA%z|k :q;c#;zRTu\HBIYZ)+˿G!`"_y|g>cOo~QFC]bT68Ǿ:e9.ڔn8:9*_8PsHhH,Oz8͏5ݷ:Syx& v>q6^`s1Tp`/{΅!."`oφwo|Mk~:wu.B2|ܧf(/>1k9+y};|ʭm uӟ :$>O!-56{9mOц_]n[#3pLϛ#S6S^֨k ѶKņn3otnƣ:{fUXۨƣZGt~ݼ&ytprh9:}ݾ2Y9u2 2נ}O\xәHguE5}8 ;*4ZsoA 9 wO_o6`?`\0G'sq۴ *=k\ XgTvyo8:O!joʷjN >#Mۚ.կ]AUGͧ{45.nE)lfviTsB-n(?>a=ڀʨPe'VxCOoG>gs8lJ<7%/>5RuC@4g"ۓlZ&YO`@'c5+sS69 2 3KrV<_ ?mY Mj2ȴ^̎z~c%]FLEC7}́nd*'`QG6;m=bCaY6fp]Gr;R'q Py @+,]X[[wW[}X /jzjr_`01zOɺS99P>2Cv27OUg־HubU*Y%w!|=vBDBT8&w^ U6)um>bʇ<Ԏr Ǘ Sc_ . "3NUx #=3}KFt`^]bL1F6)o4n9J+mIw~k\8n7Top7^4URJ9J_D'b4{|콶cG&iÞf&sP& Nq' ttcR= 4)TejK/\{XMS91cG/+>_fQtkC3woUsn xFS[ i}";iCh\I~dN'{ģdP`yW 쯛u;c9:cn*kBH`7˷ކȵ8`,Vw+@ӃZSQ1;Bى.Ѝg3vLUG!C9ߋ,RU \Ѽn<%k^K\(}^2RN_N95 &)x5)7SKa.%A,A$7h]Y!ģ3{M;}IŇQma P[ڋ.U @H!1Fs=?!7kJ'c}8!^Kw #6߂ F~OU!pn4S5w3ն{X_R)*4UHTήM-/瓨A+Ba-PB c ;k jgߴ[iv]NtMffZ~a?ce  Tz6H;B3Q `WSm-s~Z$SLQ%+7{W9{3 @|_mB Wߢ6 ˰r>c *|ʛ^&jp6s~EP~PӘUMCvYyqCEŅ 4 ᦌ%k =Z6:oDaϐpt@R˴ 5 x~?ˤ$v~8xe9x*+Լ^ZJy@Ӥi}G'M0txo@ /$^2;3Lf <- +L?/sFv;3vY?N Ŗ:yg{LA}f¦5/30/URUGZe?F7-C̮Ofm؀v  ~gd>A%jW%&9}SӮ/ kN}ϣO⛻dU9cK58)K'XKlx-'hs-oڵƨ76r"s뇟xBroVygI>ZC7' ۯIѱ`9Eٟ[Ν$ܻg (moBw]E~_f`S9J6>dl3mPtn(>}"%ɬ$i][rO˟DYD [:Ckw ɷ+vj$rv1#0/}:ޯ. L׻U5d vjL]igTkCr jq58Z:]󯭘}Ws.rn|6mQ\o=`C^4tum4攷6Opں/;:% myEVR`ZX'Tlgz$T@.o6 { [0mrHgW]Ju0} |j,dbnusW38&uzJ[Y 8s P1K *өٜGF}swdQ`ߗf/[>d$qmC^5zt)Qiq5`xe`8j8^^?KFbŁW/r ǜ[u?Na &1Oڂ.ܩw@꺴gMOܼ plO3M`2XثZջib#`IK2G-WvJ.|ʭ >ʡ_2F$Y+!`U 6Y֓_DWYhu2u\>,nh6_r_5B9TTzdif%/mCsƠNɓR r}!PŖ}qEvp5VGev9atw܆\{OjÚwɟUa<\B=f8O[k@jdxc'{G&`tie< 4(̱~L+sK9}fwt<x;;`h<-EgŬ>}Fa߬ov*ʜۈ(}@̈́,@+ 0cti:;ҿUzM$9&)8=zΚ/IkO~S7sŢlCmxl7NLLjV ׎4%"&foS'lifIGWkЀYN4Q?5ӌ+@Zku|?VvFeơsNFŽ Ojs1G¦kƖERÀI՜ŜO=~8u&7 W9'K㲖MT/]6.onHf.0fTsO nX>Hbr^p}pBꀣJu!`ULa*E o!u'Ep=l[b\[\ #~!F}P]IgDↅ?O]>0˕ 5d0oZ[y'aCAa ?!xd5Nv@IUMŕ02-CQɇP{d"!|hю>Ђ J6Fya}RȖ&vi@PI9~H#$Jz\-' "m"-A\Cc7?B$dґ!ضz؀pĿy $UճXטN0/Ec'5@ 9mC Psv`t_[F>|64@&5Gi*Lֈj<8l ĤHp8`~pQIE RyoH2d*) xJPQ@>~qb^DZG(Nh1 FeqOwG8.k}FQ9'pm5d cζ!Hr|HF+/xx + ~,^T0$ ו <>Ȏh1oO4`^!RXh;6/HI2Sz\5@ċ1ȿ,,1 bGT(+4[ <)ĭbP)(VŎ(<,*bB@:*lQPGPdeRyDZ?-*zJ>B>=<( BeiasA( p00/378zJ DZYJ(ߒģZޤԂ[ILC1"S]GXM6_oNϕJx6/Eo\Qjrd*)0jI ֱ\?z<a1@Y98PkAb OsBp*PÌ.Ѝ_ Zɿ 7pƃVB iʅ*+'r [F%pv2EA/NMQv8,Î JYB'y?XGp<,nA&_)oK4 TJ$&h9  Bˀñ Q~ 6fo/`_,- #!6Li [i{oPaA-@H[$oQ~@vœL*X+Y\jP^KMb(4IEE>G` o0l %S T HvI),_: (8 $C껿E<#4d~9 lCw©`a@oqL|b;!p7Դ'Ƹj/jq剮40yE򃤣g r!sԸ( ?d Զl$BwtB&¥a! qeoG`_,r*. PˁAZž)!XTm=[TEPH)%SK I~ &I d >0( ?*)HwV2ON/ةm~$ bZ(E._`o`_,,=C/%V Bcs  5m((nWp28hm&!0r$I LeIHkFtUO,74{D=7S>c snCe:5voOIlT~le}^byI{zZǩU)ϑp4c+wܠaXZ7Q/waU5}TɁZ̍7ݵPgM*b\0fE% Q=72dne* ᵗ{Po9s h_~_}-k}<@LB;-C/m1Mfw( rL<)zfewR0Tt}qSG<7s@9 5L }΢HB,?T@^M;" }*.Hʷ7;nbz,Gͻ1A4J>?Z ~}R",Gd2ʹ5)ߍ(@ 4tu\vjy lU{ǻ+!7\ CA2 6c}F=gӐ7 k˪HzN``,4RrzFIq+{u ֬KgӇx;EuͫX L“|Ŵ0<]kτI;٧1o dׁ*_\.zxE#}i6ʹL7}c|Mf-龍M\oK(hK#j:LVVBw$ {xC?.tx%U zrQՉ;HaKV]]QV9v/D7Q/4H?Bסwgh *)Gȏx:S/u].KYg$Lgw,wF3\a~ix7m {Wl+4ƿ\ۖ(u+\YA<^S:R%aØBX?1s'>nyauO .mt}DnCPy5]xhvP`U 0U??|Wyz|1dש@kK>)_SGr\h%jWL)~jV Wywv~ x8@P?e)]_)g6q憜ctƌ> 5=0;48E%~9X<гCԪ4~Qr{}e7tvC<<卂OL?iKk 1),Tz, U6LD~w<H"fUzA#}tb&ۄ/v ݻڏ[9XSM፛׸'#+c*Z\ ʌEv=*4Ε=amxOlEd*әr+6ox-ô7mp!s{{* ,mlroM+l;J`Y4{~N3n!K͞:^Cx׉Kv*M*[1JWGGβ*ˊ ӗ;.?w;"dC^Urp<򯫝C@~p}mSo zo={U ҴWpVUluݶ:! r # :1/C=Web6!oB6-yc&agAVN[N t+knW^ޞEhO}:!&ـ$B8Li>G];k*#>H.\}91ANJ8 P85 /s_檫.@OhD Lww#kuO6*{P6` J',>[򮊧 2[ wxlصWf_p +v̘%JSɃ*=fc} yyo0%&+͚'ZZqN.xN3i=jvG47u?IsQ<W0 vS|vZ҈M6qlf yaW[Su+uƒ!:GC^vJ{ucrZ(˅/Laθ eSs1GD5?s֞WU,]HzH4*#e>۬9C߂ӎ3'yw|f>_&آ/E#l5 ܰ0Ssߟr:*8e3ά1 l"TVuw+'~AvQ>6vqյY;S$s _3ӛ6zӕ|,,9:M;x@x`QU[w_^0ub;IJpD Z_?6l>̛*F.=ֿ4be+\qw /q{Κ{#>?wzb [(۸<뇫v貟8#*-eR5Z>77cҚα-MEvģGp^E2rw)SdOE:<fiWz.>T/}k Bς={l#꿐ge2Q{Ƥ'<B#z?w,pFK#_@#|~&2P,pH# \ýs^W+*sQql`y,w\ 85zgok-CDZԶi@NJ94 .fy]s;Yi9ȼ1}QG<0{9u&gXVVƥ>ߎ>AGp<0(\GYL %7]K^&0 lJm")D]wGwжx#>pdRO征QGe^*ЭBDF\&ϋxv y(lM^ NQnYY\-kKispꈢo7=g rb$4`m ǧ_%Gj{>s37-RZ0iP 2D˩eαU*QseCCGk-I>293L_/mJq[N럣c _/G[V˘ M˦pocPleBA_I?'։@6 ]w$ s-e-U2t{Eg!_L:o_N0JM{Àm="#ְ$ ==j'6@/0>!߭dh/@'Karp_A"($Y5WFwg6=4lbtv_ީ%eq}=6( ՆA&j[0,&X,g"F&4ҳ\]pٔK")??lm$lc1ٖXa2Yk[Hfc 7 Cp{80|9ȖOuXK 0s,%a`_ 2̂eaֿ4"6Z:^Fc—0cSxs}RH0`[׀G'2[N)?"^K 0;[7QID-g~bAl%p/:O d;# AzHt(}hh=C?+BHv@<`@n"k[y?SہߕA>1џ'X#{xpd`+)H=5Z(EAk?$I_?2)XԸ5¸H+6MK_U;)D`l0~g;[w60gAPaA*?mplK.&\CZ*>\#X|/74"4#8dROjow\$hhK!퀸N,z!? ],  C|rXH%iMi.RDd`dF}BYlGA_"62chauS* `B 4Cx]TW(X{^X"j,(DAD) -3wѻ ("VPDAQ;DlDcTTĂ.,?1΁e߼ޙ7>rrOvIǿܣvǠ,Msֺ~ `.;Esi)|{}LqvGAqM_evO y'K{f^m7ekI?,u@1zmDB fYQ36jl_gn_zx^}30A{,Q) !hP۷[^W `#{Eȿ%`TK18ɕL nlAGb8Sg?~2jusde>dge~F0qq6Ae+]X5E?j38(}37 }N v5wFn;^8pq1n֮&D%)mN3 ՞OsL]V'rcc7ל^;w^U*<9C7 C.vyӯGJ~ ÿuUz8((z :*zJ_g#3YHPEN+a~n⃎[he!SV $o8碅AI]9IluGlt~ESډWDޠvɿKd{JC?[.p,k PC$|}Sq=>&ok`Wo3 겁\c CXuYU 4?Qn=mESL*?Gov}^8R{.| (0`A6iծI'JJ-NU7S ֧rUi[]͎u{\< yqO-gPP2kV!qr;l\z]CPB|4t͓xKZ{3X74r8NloqpTm .޽ڮ~&x}E+j]R 0=k3m<_AmHѪϯ=qӡXȽhT8pׁ! `tȺ#59=l3@;{Fc@3ӎeg~3jĕJ3Joɞ'o𵳽2ɇ-DRDk6>޲ӎILEs Q[csP 6XeB!'ݑg8k\Kx`iNȓ etom( C*nwQ/' @]W8t<~qYo[,ڛ#ya]okeQ$3,:cW0ؐ[`h ,SqqYB?;zmu6Hr{Ys*dB62}wsK[9ne]E^2a9W#Dž߄Ű/5 εt p ՚-ӣ4x@yvԴѻCJݞ IڳG.g~0ݯ;HﴓS,kN7\B@u4-v e7B'K=vZnOU#{}pl*SXrqfnj¦"#cϥwnGTK[W,{:Ƒ-`+=;?k6{[Ư+K vj 0XK @8kM6Vf|G9ZrL. [o\;cGm3Ӱo2T`=oa#[(Bbpo?ۜ`śyΖ u ];1cm6-p;.:k"#ꈊiSv\qTEל W7])ecV/g1cxRWm%߾:䐡w6ٟ|{pdDnba{K!X_ƻF:BGk>Sa+M"2B!= 8<_Uȿ:BKt Srtw0Wa[`cj?y929 eV{,CV/v}ctskϴ"!p(e ޝZ];z,JHhkO^כ;k{^%Zl3 ,jZ W̪ p~8s\f7*llǩ9"֜mzkI>W^] _ 3ࠇ+ÿꀑuYGM*QOECPn;3xY>1~X˱D9#hy$pػk* t82?RVU7m9Wpt 7< K1%'+֣欎N cyak'~5)[ȫZUvҀP.-W]jo pMeW)0X 值Gv(z JGOޒE~1߰%>@5Y-9WũiAmmFFf|3T/6rupN*|=3Ȯ/60n1yeu5ϽBgO}b!tuNɘm֐ AcHn"fa-uo8<оYb>ҵu/af,P8&M>g$}l!VDwvR]x->w!idq?]Nk#9%1b;IŊl#q)7 HeOv'u3d`_&VY ,QD_*H(QF9 u M6?ّ/T> BSv<#|J;kTnED=Z; 6`iq8l)i@(eF1 FBPI k!(6Dd֠T"oW#\,1ұ=R؁QRԎ嘲!)B/ J)BvE/FbGJ{IŠc:SS0rG,}(%_r2+1WCJ"B*ZxlX&ӛ?d\ ~:ga@Knx|Rة]E?I6 {7 hhtn YYXT x*76" !Pw&]1D_Ǽ!R#CUy_hzIHד$nLʷQ_xQԆȘ1yK;:ʗ7QV>e!n$#6,p\20y, (KaY'4y1%A( >:)) KjjmLQ F*W2 h*)@E)JIC  \V+Kʊz~Zd`-zrBȍk^n$X!F$AG`|Fg2WGI=JamM˜sn841*fQe2/5foM@C ˭Tɸ"Fy R6i$?uF8*`_Z倘"EA>:L 70l6_(zЦ:_YI/25T 6e9ƲK2lq}MZAHo0HJ"rFeW!0Pr@ /'Ӌh/E-!r0j0ڕr[x+W3EBJopenimageio-1.7.17~dfsg0.orig/testsuite/maketx/0000755000175000017500000000000013151711064017647 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/testsuite/maketx/run.py0000755000175000017500000001313213151711064021030 0ustar mfvmfv#!/usr/bin/env python # location of oiio-images directory oiio_images = parent + "/oiio-images/" # Just for simplicity, make a checkerboard with a solid alpha command += oiiotool (" --pattern checker 128x128 4 --ch R,G,B,=1.0" + " -d uint8 -o " + oiio_relpath("checker.tif") ) # Basic test - recreate the grid texture command += maketx_command (oiio_images + "grid.tif", "grid.tx", showinfo=True) # Test --resize (to power of 2) with the grid, which is 1000x1000 command += maketx_command (oiio_images + "grid.tif", "grid-resize.tx", "--resize", showinfo=True) # Test -d to set output data type command += maketx_command ("checker.tif", "checker-uint16.tx", "-d uint16", showinfo=True) # Test --nchannels to restrict the number of channels command += maketx_command ("checker.tif", "checker-1chan.tx", "--nchannels 1", showinfo=True) # Test --tiles to set a non-default tile size command += maketx_command ("checker.tif", "checker-16x32tile.tx", "--tile 16 32", showinfo=True) # Test --separate and --compression command += maketx_command ("checker.tif", "checker-seplzw.tx", "--separate --compression lzw", showinfo=True) # Test --wrap command += maketx_command ("checker.tif", "checker-clamp.tx", "--wrap clamp", showinfo=True) # Test --swrap and --twrap command += maketx_command ("checker.tif", "checker-permir.tx", "--swrap periodic --twrap mirror", showinfo=True) # Test --nomipmap command += maketx_command ("checker.tif", "checker-nomip.tx", "--nomipmap", showinfo=True) # Test --Mcamera, --Mscreen command += maketx_command ("checker.tif", "checker-camera.tx", "--Mcamera 1 0 0 0 0 2 0 0 0 0 1 0 0 0 0 1 --Mscreen 3 0 0 0 0 3 0 0 0 0 3 0 1 2 3 1", showinfo=True) # Test --opaque-detect (should drop the alpha channel) command += maketx_command ("checker.tif", "checker-opaque.tx", "--opaque-detect", showinfo=True) # Test --monochrome-detect (first create a monochrome image) command += oiiotool (" --pattern constant:color=.25,.25,.25 256x256 3 " + " -d uint8 -o " + oiio_relpath("gray.tif")) command += maketx_command ("gray.tif", "gray-mono.tx", "--monochrome-detect", showinfo=True) # Test --monochrome-detect on something that is NOT monochrome command += oiiotool (" --pattern constant:color=.25,.2,.15 256x256 3 " + " -d uint8 -o " + oiio_relpath("pink.tif")) command += maketx_command ("pink.tif", "pink-mono.tx", "--monochrome-detect", showinfo=True) # Test --prman : should save 'separate' planarconfig, and funny 64x32 tiles # since we are specifying 16 bits, and it should save as 'int16' even though # we asked for unsigned. command += maketx_command ("checker.tif", "checker-prman.tx", "-d uint16 --prman", showinfo=True) # Test --fixnan : take advantage of the bad.exr images in # testsuite/oiiotool-fixnan. (Use --nomipmap to cut down on stats output) # FIXME: would also like to test --checknan, but the problem with that is # that is actually FAILS if there's a nan. command += maketx_command ("../oiiotool-fixnan/src/bad.exr", "nan.exr", "--fixnan box3 --nomipmap", showinfo=True, showinfo_extra="--stats") # Test --format to force exr even though it can't be deduced from the name. command += maketx_command ("checker.tif", "checker-exr.pdq", "--format exr", showinfo=True) # Test that we cleanly replace any existing SHA-1 hash and ConstantColor # hint in the ImageDescription of the input file. command += oiiotool (" --pattern constant:color=1,0,0 64x64 3 " + " --caption \"foo SHA-1=1234abcd ConstantColor=[0.0,0,-0.0] bar\"" + " -d uint8 -o " + oiio_relpath("small.tif") ) command += info_command ("small.tif", safematch=1); command += maketx_command ("small.tif", "small.tx", "--oiio --constant-color-detect", showinfo=True) # Test that the oiio:SHA-1 hash is stable, and that that changing filter and # using -hicomp result in different images and different hashes. command += maketx_command (oiio_images + "grid.tif", "grid-lanczos3.tx", extraargs = "-filter lanczos3") command += maketx_command (oiio_images + "grid.tif", "grid-lanczos3-hicomp.tx", extraargs = "-filter lanczos3 -hicomp") command += info_command ("grid.tx", extraargs="--metamatch oiio:SHA-1") command += info_command ("grid-lanczos3.tx", extraargs="--metamatch oiio:SHA-1") command += info_command ("grid-lanczos3-hicomp.tx", extraargs="--metamatch oiio:SHA-1") # Regression test -- at one point, we had a bug where we were botching # the poles of OpenEXR env maps, adding energy. Check it by creating an # all-white image, turning it into an env map, and calculating its # statistics (should be 1.0 everywhere). command += oiiotool (" --pattern constant:color=1,1,1 4x2 3 " + " -d half -o " + oiio_relpath("white.exr")) command += maketx_command ("white.exr", "whiteenv.exr", "--envlatl") command += oiiotool ("--stats whiteenv.exr") outputs = [ "out.txt" ] # To do: --filter --checknan --fullpixels # --prman-metadata --ignore-unassoc # --mipimage # --envlatl TIFF, --envlatl EXR # --colorconvert --unpremult -u --fovcot openimageio-1.7.17~dfsg0.orig/testsuite/maketx/ref/0000755000175000017500000000000013151711064020423 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/testsuite/maketx/ref/out-alt-tiff4.txt0000644000175000017500000003202213151711064023562 0ustar mfvmfvReading grid.tx grid.tx : 1000 x 1000, 4 channel, uint8 tiff MIP-map levels: 1000x1000 500x500 250x250 125x125 62x62 31x31 15x15 7x7 3x3 1x1 SHA-1: 97D6548FF9E9BFD21424B773B1D84FACDF0C1797 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) XResolution: 72 YResolution: 72 ResolutionUnit: "in" DocumentName: "g.tif" textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" PixelAspectRatio: 1 oiio:AverageColor: "0.608983,0.608434,0.608728,1" oiio:SHA-1: "9B24D4CC05313A43973AC384718D81D19183B691" Reading grid-resize.tx grid-resize.tx : 1024 x 1024, 4 channel, uint8 tiff MIP-map levels: 1024x1024 512x512 256x256 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 1DAB274639828223059FDBC0E30BD569C07B66C0 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) XResolution: 72 YResolution: 72 ResolutionUnit: "in" DocumentName: "g.tif" textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" PixelAspectRatio: 1 oiio:AverageColor: "0.608983,0.608434,0.608728,1" oiio:SHA-1: "2E23E9B4C6E707427A73B5433B23104D4183724E" Reading checker-uint16.tx checker-uint16.tx : 128 x 128, 4 channel, uint16 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 75BCACCE72A4275AD11FAF4A5D328C624DFF69AB channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 16 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-1chan.tx checker-1chan.tx : 128 x 128, 1 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: F7DAC8772B7C6BFABDA884D61ABC84844C949AF1 channel list: Y tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 1 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" oiio:AverageColor: "0.5" oiio:SHA-1: "1D0A2254373A69C054C08672041FC7DE3BAE5179" Reading checker-16x32tile.tx checker-16x32tile.tx : 128 x 128, 4 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6 channel list: R, G, B, A tile size: 16 x 32 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-seplzw.tx checker-seplzw.tx : 128 x 128, 4 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 2 planarconfig: "separate" tiff:Compression: 5 compression: "lzw" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-clamp.tx checker-clamp.tx : 128 x 128, 4 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "clamp,clamp" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-permir.tx checker-permir.tx : 128 x 128, 4 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "periodic,mirror" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-nomip.tx checker-nomip.tx : 128 x 128, 4 channel, uint8 tiff SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-camera.tx checker-camera.tx : 128 x 128, 4 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" worldtocamera: 1 0 0 0 0 2 0 0 0 0 1 0 0 0 0 1 worldtoscreen: 3 0 0 0 0 3 0 0 0 0 3 0 1 2 3 1 oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-opaque.tx checker-opaque.tx : 128 x 128, 3 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 6827D9ED3A5674C1FAB56F96E5D7D06796D43144 channel list: R, G, B tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" oiio:AverageColor: "0.5,0.5,0.5" oiio:SHA-1: "2BE3743A64D4309645F94CA7B37D0CD08AF0B38E" Reading gray-mono.tx gray-mono.tx : 256 x 256, 1 channel, uint8 tiff MIP-map levels: 256x256 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: F5C1F9FA963006D501B14542F5E941AEDB4C5907 channel list: Y tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 1 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" oiio:ConstantColor: "0.25098" oiio:AverageColor: "0.25098" oiio:SHA-1: "24049CDF337F17E162291C7B626E0588A9D71160" Reading pink-mono.tx pink-mono.tx : 256 x 256, 3 channel, uint8 tiff MIP-map levels: 256x256 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: D781FABF7CF958AE9D9429255C8591C77BAA9917 channel list: R, G, B tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" oiio:ConstantColor: "0.25098,0.2,0.14902" oiio:AverageColor: "0.25098,0.2,0.14902" oiio:SHA-1: "41B1A0D1C1EBB4CEEBC7BCEE2CAB2AAAB4139724" Reading checker-prman.tx checker-prman.tx : 128 x 128, 4 channel, int16 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: DE42A8468161DA333BABF1BEB60811EA0FEF6534 channel list: R, G, B, A tile size: 64 x 32 oiio:BitsPerSample: 16 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 2 planarconfig: "separate" tiff:Compression: 8 compression: "zip" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading nan.exr nan.exr : 64 x 64, 3 channel, half openexr SHA-1: 47A8E8F3E8B2C3B6B032FCC8C39D3C5FC1AAA390 channel list: R, G, B tile size: 64 x 64 oiio:ColorSpace: "Linear" compression: "zip" fovcot: 1 oiio:AverageColor: "0.5,0.5,0.5" oiio:SHA-1: "44B96A7C3AFBF8D7621E7C6453266E5DD1962D36" openexr:levelmode: 0 PixelAspectRatio: 1 screenWindowCenter: 0 0 screenWindowWidth: 1 textureformat: "Plain Texture" wrapmodes: "black,black" Stats Min: 0.000000 0.000000 0.000000 (float) Stats Max: 1.000000 1.000000 1.000000 (float) Stats Avg: 0.500000 0.500000 0.500000 (float) Stats StdDev: 0.500000 0.500000 0.500000 (float) Stats NanCount: 0 0 0 Stats InfCount: 0 0 0 Stats FiniteCount: 4096 4096 4096 Constant: No Monochrome: Yes Reading checker-exr.pdq checker-exr.pdq : 128 x 128, 4 channel, half openexr MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: FA921281D5C8AF14FC50885DD54E2E17509202EC channel list: R, G, B, A tile size: 64 x 64 oiio:ColorSpace: "Linear" openexr:roundingmode: 0 textureformat: "Plain Texture" compression: "zip" Orientation: 1 (normal) fovcot: 1 oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" PixelAspectRatio: 1 screenWindowCenter: 0 0 screenWindowWidth: 1 wrapmodes: "black,black" Reading small.tif small.tif : 64 x 64, 3 channel, uint8 tiff SHA-1: BE1D5EA24E907A4C4B3FB3C28EAC872A20F5B414 channel list: R, G, B oiio:BitsPerSample: 8 ImageDescription: "foo ConstantColor=[0.0,0,-0.0] bar" Orientation: 1 (normal) tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" tiff:RowsPerStrip: 32 oiio:SHA-1: "1234abcd ConstantColor=[0.0,0,-0.0] bar" Reading small.tx small.tx : 64 x 64, 3 channel, uint8 tiff MIP-map levels: 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: BE1D5EA24E907A4C4B3FB3C28EAC872A20F5B414 channel list: R, G, B tile size: 64 x 64 oiio:BitsPerSample: 8 ImageDescription: "foo bar " Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" oiio:ConstantColor: "1,0,0" oiio:AverageColor: "1,0,0" oiio:SHA-1: "1DD1CB3C76F9A491B115F85A6DAC1B1A8DD2D3CB" Reading grid.tx oiio:SHA-1: "9B24D4CC05313A43973AC384718D81D19183B691" Reading grid-lanczos3.tx oiio:SHA-1: "D6C3DEE1A567726FB962FE39F0FE94A3E94530CF" Reading grid-lanczos3-hicomp.tx oiio:SHA-1: "3994E2C39E040A372BA1894E805382E5D470790F" whiteenv.exr : 4 x 2, 3 channel, half openexr (+mipmap) MIP 0 of 3 (4 x 2): Stats Min: 1.000000 1.000000 1.000000 (float) Stats Max: 1.000000 1.000000 1.000000 (float) Stats Avg: 1.000000 1.000000 1.000000 (float) Stats StdDev: 0.000000 0.000000 0.000000 (float) Stats NanCount: 0 0 0 Stats InfCount: 0 0 0 Stats FiniteCount: 8 8 8 Constant: Yes Constant Color: 1.000000 1.000000 1.000000 (float) Monochrome: Yes MIP 1 of 3 (2 x 1): Stats Min: 1.000000 1.000000 1.000000 (float) Stats Max: 1.000000 1.000000 1.000000 (float) Stats Avg: 1.000000 1.000000 1.000000 (float) Stats StdDev: 0.000000 0.000000 0.000000 (float) Stats NanCount: 0 0 0 Stats InfCount: 0 0 0 Stats FiniteCount: 2 2 2 Constant: Yes Constant Color: 1.000000 1.000000 1.000000 (float) Monochrome: Yes MIP 2 of 3 (1 x 1): Stats Min: 1.000000 1.000000 1.000000 (float) Stats Max: 1.000000 1.000000 1.000000 (float) Stats Avg: 1.000000 1.000000 1.000000 (float) Stats StdDev: 0.000000 0.000000 0.000000 (float) Stats NanCount: 0 0 0 Stats InfCount: 0 0 0 Stats FiniteCount: 1 1 1 Constant: Yes Constant Color: 1.000000 1.000000 1.000000 (float) Monochrome: Yes openimageio-1.7.17~dfsg0.orig/testsuite/maketx/ref/out-alt-tiff4-2.txt0000644000175000017500000003217213151711064023727 0ustar mfvmfvReading grid.tx grid.tx : 1000 x 1000, 4 channel, uint8 tiff MIP-map levels: 1000x1000 500x500 250x250 125x125 62x62 31x31 15x15 7x7 3x3 1x1 SHA-1: 97D6548FF9E9BFD21424B773B1D84FACDF0C1797 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) XResolution: 72 YResolution: 72 ResolutionUnit: "in" DocumentName: "g.tif" textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" PixelAspectRatio: 1 oiio:AverageColor: "0.608983,0.608434,0.608728,1" oiio:SHA-1: "9CA964417213269190944948273FB1F00A4A8393" Reading grid-resize.tx grid-resize.tx : 1024 x 1024, 4 channel, uint8 tiff MIP-map levels: 1024x1024 512x512 256x256 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 1DAB274639828223059FDBC0E30BD569C07B66C0 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) XResolution: 72 YResolution: 72 ResolutionUnit: "in" DocumentName: "g.tif" textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" PixelAspectRatio: 1 oiio:AverageColor: "0.608983,0.608434,0.608728,1" oiio:SHA-1: "C4BE40DC3B3534F30ACB906602F2FF80A246C01E" Reading checker-uint16.tx checker-uint16.tx : 128 x 128, 4 channel, uint16 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 75BCACCE72A4275AD11FAF4A5D328C624DFF69AB channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 16 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-1chan.tx checker-1chan.tx : 128 x 128, 1 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: F7DAC8772B7C6BFABDA884D61ABC84844C949AF1 channel list: Y tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 1 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" oiio:AverageColor: "0.5" oiio:SHA-1: "1D0A2254373A69C054C08672041FC7DE3BAE5179" Reading checker-16x32tile.tx checker-16x32tile.tx : 128 x 128, 4 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6 channel list: R, G, B, A tile size: 16 x 32 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-seplzw.tx checker-seplzw.tx : 128 x 128, 4 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 2 planarconfig: "separate" tiff:Compression: 5 compression: "lzw" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-clamp.tx checker-clamp.tx : 128 x 128, 4 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "clamp,clamp" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-permir.tx checker-permir.tx : 128 x 128, 4 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "periodic,mirror" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-nomip.tx checker-nomip.tx : 128 x 128, 4 channel, uint8 tiff SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-camera.tx checker-camera.tx : 128 x 128, 4 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" worldtocamera: 1 0 0 0 0 2 0 0 0 0 1 0 0 0 0 1 worldtoscreen: 3 0 0 0 0 3 0 0 0 0 3 0 1 2 3 1 oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-opaque.tx checker-opaque.tx : 128 x 128, 3 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 6827D9ED3A5674C1FAB56F96E5D7D06796D43144 channel list: R, G, B tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" oiio:AverageColor: "0.5,0.5,0.5" oiio:SHA-1: "2BE3743A64D4309645F94CA7B37D0CD08AF0B38E" Reading gray-mono.tx gray-mono.tx : 256 x 256, 1 channel, uint8 tiff MIP-map levels: 256x256 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: F5C1F9FA963006D501B14542F5E941AEDB4C5907 channel list: Y tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 1 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" oiio:ConstantColor: "0.25098" oiio:AverageColor: "0.25098" oiio:SHA-1: "24049CDF337F17E162291C7B626E0588A9D71160" Reading pink-mono.tx pink-mono.tx : 256 x 256, 3 channel, uint8 tiff MIP-map levels: 256x256 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: D781FABF7CF958AE9D9429255C8591C77BAA9917 channel list: R, G, B tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" oiio:ConstantColor: "0.25098,0.2,0.14902" oiio:AverageColor: "0.25098,0.2,0.14902" oiio:SHA-1: "D5F1500992EFBDA77B89E946A9F53F89D332B6F4" Reading checker-prman.tx checker-prman.tx : 128 x 128, 4 channel, int16 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: DE42A8468161DA333BABF1BEB60811EA0FEF6534 channel list: R, G, B, A tile size: 64 x 32 oiio:BitsPerSample: 16 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 2 planarconfig: "separate" tiff:Compression: 8 compression: "zip" IPTC:Caption: "oiio:SHA-1=D924CA144A02479D1507F5910F5FC8F51EF78765 oiio:AverageColor=0.5,0.5,0.5,1" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading nan.exr nan.exr : 64 x 64, 3 channel, half openexr SHA-1: 47A8E8F3E8B2C3B6B032FCC8C39D3C5FC1AAA390 channel list: R, G, B tile size: 64 x 64 oiio:ColorSpace: "Linear" compression: "zip" fovcot: 1 oiio:AverageColor: "0.5,0.5,0.5" oiio:SHA-1: "44B96A7C3AFBF8D7621E7C6453266E5DD1962D36" openexr:levelmode: 0 PixelAspectRatio: 1 screenWindowCenter: 0 0 screenWindowWidth: 1 textureformat: "Plain Texture" wrapmodes: "black,black" Stats Min: 0.000000 0.000000 0.000000 (float) Stats Max: 1.000000 1.000000 1.000000 (float) Stats Avg: 0.500000 0.500000 0.500000 (float) Stats StdDev: 0.500000 0.500000 0.500000 (float) Stats NanCount: 0 0 0 Stats InfCount: 0 0 0 Stats FiniteCount: 4096 4096 4096 Constant: No Monochrome: Yes Reading checker-exr.pdq checker-exr.pdq : 128 x 128, 4 channel, half openexr MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: FA921281D5C8AF14FC50885DD54E2E17509202EC channel list: R, G, B, A tile size: 64 x 64 oiio:ColorSpace: "Linear" openexr:roundingmode: 0 textureformat: "Plain Texture" compression: "zip" Orientation: 1 (normal) fovcot: 1 oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" PixelAspectRatio: 1 screenWindowCenter: 0 0 screenWindowWidth: 1 wrapmodes: "black,black" Reading small.tif small.tif : 64 x 64, 3 channel, uint8 tiff SHA-1: BE1D5EA24E907A4C4B3FB3C28EAC872A20F5B414 channel list: R, G, B oiio:BitsPerSample: 8 ImageDescription: "foo ConstantColor=[0.0,0,-0.0] bar" Orientation: 1 (normal) tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" tiff:RowsPerStrip: 32 oiio:SHA-1: "1234abcd ConstantColor=[0.0,0,-0.0] bar" Reading small.tx small.tx : 64 x 64, 3 channel, uint8 tiff MIP-map levels: 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: BE1D5EA24E907A4C4B3FB3C28EAC872A20F5B414 channel list: R, G, B tile size: 64 x 64 oiio:BitsPerSample: 8 ImageDescription: "foo bar " Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" oiio:ConstantColor: "1,0,0" oiio:AverageColor: "1,0,0" oiio:SHA-1: "1DD1CB3C76F9A491B115F85A6DAC1B1A8DD2D3CB" Reading grid.tx oiio:SHA-1: "9B24D4CC05313A43973AC384718D81D19183B691" Reading grid-lanczos3.tx oiio:SHA-1: "D6C3DEE1A567726FB962FE39F0FE94A3E94530CF" Reading grid-lanczos3-hicomp.tx oiio:SHA-1: "3994E2C39E040A372BA1894E805382E5D470790F" whiteenv.exr : 4 x 2, 3 channel, half openexr (+mipmap) MIP 0 of 3 (4 x 2): Stats Min: 1.000000 1.000000 1.000000 (float) Stats Max: 1.000000 1.000000 1.000000 (float) Stats Avg: 1.000000 1.000000 1.000000 (float) Stats StdDev: 0.000000 0.000000 0.000000 (float) Stats NanCount: 0 0 0 Stats InfCount: 0 0 0 Stats FiniteCount: 8 8 8 Constant: Yes Constant Color: 1.000000 1.000000 1.000000 (float) Monochrome: Yes MIP 1 of 3 (2 x 1): Stats Min: 1.000000 1.000000 1.000000 (float) Stats Max: 1.000000 1.000000 1.000000 (float) Stats Avg: 1.000000 1.000000 1.000000 (float) Stats StdDev: 0.000000 0.000000 0.000000 (float) Stats NanCount: 0 0 0 Stats InfCount: 0 0 0 Stats FiniteCount: 2 2 2 Constant: Yes Constant Color: 1.000000 1.000000 1.000000 (float) Monochrome: Yes MIP 2 of 3 (1 x 1): Stats Min: 1.000000 1.000000 1.000000 (float) Stats Max: 1.000000 1.000000 1.000000 (float) Stats Avg: 1.000000 1.000000 1.000000 (float) Stats StdDev: 0.000000 0.000000 0.000000 (float) Stats NanCount: 0 0 0 Stats InfCount: 0 0 0 Stats FiniteCount: 1 1 1 Constant: Yes Constant Color: 1.000000 1.000000 1.000000 (float) Monochrome: Yes openimageio-1.7.17~dfsg0.orig/testsuite/maketx/ref/out.txt0000644000175000017500000003533513151711064022004 0ustar mfvmfvReading grid.tx grid.tx : 1000 x 1000, 4 channel, uint8 tiff MIP-map levels: 1000x1000 500x500 250x250 125x125 62x62 31x31 15x15 7x7 3x3 1x1 SHA-1: 97D6548FF9E9BFD21424B773B1D84FACDF0C1797 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) XResolution: 72 YResolution: 72 ResolutionUnit: "in" DocumentName: "g.tif" textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" PixelAspectRatio: 1 IPTC:Caption: "oiio:SHA-1=9CA964417213269190944948273FB1F00A4A8393 oiio:AverageColor=0.608983,0.608434,0.608728,1" oiio:AverageColor: "0.608983,0.608434,0.608728,1" oiio:SHA-1: "9CA964417213269190944948273FB1F00A4A8393" Reading grid-resize.tx grid-resize.tx : 1024 x 1024, 4 channel, uint8 tiff MIP-map levels: 1024x1024 512x512 256x256 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 1DAB274639828223059FDBC0E30BD569C07B66C0 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) XResolution: 72 YResolution: 72 ResolutionUnit: "in" DocumentName: "g.tif" textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" PixelAspectRatio: 1 IPTC:Caption: "oiio:SHA-1=C4BE40DC3B3534F30ACB906602F2FF80A246C01E oiio:AverageColor=0.608983,0.608434,0.608728,1" oiio:AverageColor: "0.608983,0.608434,0.608728,1" oiio:SHA-1: "C4BE40DC3B3534F30ACB906602F2FF80A246C01E" Reading checker-uint16.tx checker-uint16.tx : 128 x 128, 4 channel, uint16 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 75BCACCE72A4275AD11FAF4A5D328C624DFF69AB channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 16 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" IPTC:Caption: "oiio:SHA-1=D924CA144A02479D1507F5910F5FC8F51EF78765 oiio:AverageColor=0.5,0.5,0.5,1" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-1chan.tx checker-1chan.tx : 128 x 128, 1 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: F7DAC8772B7C6BFABDA884D61ABC84844C949AF1 channel list: Y tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 1 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" IPTC:Caption: "oiio:SHA-1=1D0A2254373A69C054C08672041FC7DE3BAE5179 oiio:AverageColor=0.5" oiio:AverageColor: "0.5" oiio:SHA-1: "1D0A2254373A69C054C08672041FC7DE3BAE5179" Reading checker-16x32tile.tx checker-16x32tile.tx : 128 x 128, 4 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6 channel list: R, G, B, A tile size: 16 x 32 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" IPTC:Caption: "oiio:SHA-1=D924CA144A02479D1507F5910F5FC8F51EF78765 oiio:AverageColor=0.5,0.5,0.5,1" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-seplzw.tx checker-seplzw.tx : 128 x 128, 4 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 2 planarconfig: "separate" tiff:Compression: 5 compression: "lzw" IPTC:Caption: "oiio:SHA-1=D924CA144A02479D1507F5910F5FC8F51EF78765 oiio:AverageColor=0.5,0.5,0.5,1" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-clamp.tx checker-clamp.tx : 128 x 128, 4 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "clamp,clamp" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" IPTC:Caption: "oiio:SHA-1=D924CA144A02479D1507F5910F5FC8F51EF78765 oiio:AverageColor=0.5,0.5,0.5,1" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-permir.tx checker-permir.tx : 128 x 128, 4 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "periodic,mirror" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" IPTC:Caption: "oiio:SHA-1=D924CA144A02479D1507F5910F5FC8F51EF78765 oiio:AverageColor=0.5,0.5,0.5,1" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-nomip.tx checker-nomip.tx : 128 x 128, 4 channel, uint8 tiff SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" IPTC:Caption: "oiio:SHA-1=D924CA144A02479D1507F5910F5FC8F51EF78765 oiio:AverageColor=0.5,0.5,0.5,1" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-camera.tx checker-camera.tx : 128 x 128, 4 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 1C1A7D35CCED212B768B31F002B442EA947D89A6 channel list: R, G, B, A tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" worldtocamera: 1 0 0 0 0 2 0 0 0 0 1 0 0 0 0 1 worldtoscreen: 3 0 0 0 0 3 0 0 0 0 3 0 1 2 3 1 IPTC:Caption: "oiio:SHA-1=D924CA144A02479D1507F5910F5FC8F51EF78765 oiio:AverageColor=0.5,0.5,0.5,1" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading checker-opaque.tx checker-opaque.tx : 128 x 128, 3 channel, uint8 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: 6827D9ED3A5674C1FAB56F96E5D7D06796D43144 channel list: R, G, B tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" IPTC:Caption: "oiio:SHA-1=2BE3743A64D4309645F94CA7B37D0CD08AF0B38E oiio:AverageColor=0.5,0.5,0.5" oiio:AverageColor: "0.5,0.5,0.5" oiio:SHA-1: "2BE3743A64D4309645F94CA7B37D0CD08AF0B38E" Reading gray-mono.tx gray-mono.tx : 256 x 256, 1 channel, uint8 tiff MIP-map levels: 256x256 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: F5C1F9FA963006D501B14542F5E941AEDB4C5907 channel list: Y tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 1 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" IPTC:Caption: "oiio:SHA-1=24049CDF337F17E162291C7B626E0588A9D71160 oiio:ConstantColor=0.25098 oiio:AverageColor=0.25098" oiio:ConstantColor: "0.25098" oiio:AverageColor: "0.25098" oiio:SHA-1: "24049CDF337F17E162291C7B626E0588A9D71160" Reading pink-mono.tx pink-mono.tx : 256 x 256, 3 channel, uint8 tiff MIP-map levels: 256x256 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: D781FABF7CF958AE9D9429255C8591C77BAA9917 channel list: R, G, B tile size: 64 x 64 oiio:BitsPerSample: 8 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" IPTC:Caption: "oiio:SHA-1=D5F1500992EFBDA77B89E946A9F53F89D332B6F4 oiio:ConstantColor=0.25098,0.2,0.14902 oiio:AverageColor=0.25098,0.2,0.14902" oiio:ConstantColor: "0.25098,0.2,0.14902" oiio:AverageColor: "0.25098,0.2,0.14902" oiio:SHA-1: "D5F1500992EFBDA77B89E946A9F53F89D332B6F4" Reading checker-prman.tx checker-prman.tx : 128 x 128, 4 channel, int16 tiff MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: DE42A8468161DA333BABF1BEB60811EA0FEF6534 channel list: R, G, B, A tile size: 64 x 32 oiio:BitsPerSample: 16 Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 2 planarconfig: "separate" tiff:Compression: 8 compression: "zip" IPTC:Caption: "oiio:SHA-1=D924CA144A02479D1507F5910F5FC8F51EF78765 oiio:AverageColor=0.5,0.5,0.5,1" oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" Reading nan.exr nan.exr : 64 x 64, 3 channel, half openexr SHA-1: 47A8E8F3E8B2C3B6B032FCC8C39D3C5FC1AAA390 channel list: R, G, B tile size: 64 x 64 oiio:ColorSpace: "Linear" compression: "zip" fovcot: 1 oiio:AverageColor: "0.5,0.5,0.5" oiio:SHA-1: "44B96A7C3AFBF8D7621E7C6453266E5DD1962D36" openexr:levelmode: 0 PixelAspectRatio: 1 screenWindowCenter: 0 0 screenWindowWidth: 1 textureformat: "Plain Texture" wrapmodes: "black,black" Stats Min: 0.000000 0.000000 0.000000 (float) Stats Max: 1.000000 1.000000 1.000000 (float) Stats Avg: 0.500000 0.500000 0.500000 (float) Stats StdDev: 0.500000 0.500000 0.500000 (float) Stats NanCount: 0 0 0 Stats InfCount: 0 0 0 Stats FiniteCount: 4096 4096 4096 Constant: No Monochrome: Yes Reading checker-exr.pdq checker-exr.pdq : 128 x 128, 4 channel, half openexr MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: FA921281D5C8AF14FC50885DD54E2E17509202EC channel list: R, G, B, A tile size: 64 x 64 oiio:ColorSpace: "Linear" openexr:roundingmode: 0 textureformat: "Plain Texture" compression: "zip" Orientation: 1 (normal) fovcot: 1 oiio:AverageColor: "0.5,0.5,0.5,1" oiio:SHA-1: "D924CA144A02479D1507F5910F5FC8F51EF78765" PixelAspectRatio: 1 screenWindowCenter: 0 0 screenWindowWidth: 1 wrapmodes: "black,black" Reading small.tif small.tif : 64 x 64, 3 channel, uint8 tiff SHA-1: BE1D5EA24E907A4C4B3FB3C28EAC872A20F5B414 channel list: R, G, B oiio:BitsPerSample: 8 ImageDescription: "foo ConstantColor=[0.0,0,-0.0] bar" Orientation: 1 (normal) tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" tiff:RowsPerStrip: 32 IPTC:Caption: "foo SHA-1=1234abcd ConstantColor=[0.0,0,-0.0] bar" oiio:SHA-1: "1234abcd ConstantColor=[0.0,0,-0.0] bar" Reading small.tx small.tx : 64 x 64, 3 channel, uint8 tiff MIP-map levels: 64x64 32x32 16x16 8x8 4x4 2x2 1x1 SHA-1: BE1D5EA24E907A4C4B3FB3C28EAC872A20F5B414 channel list: R, G, B tile size: 64 x 64 oiio:BitsPerSample: 8 ImageDescription: "foo bar " Orientation: 1 (normal) textureformat: "Plain Texture" wrapmodes: "black,black" fovcot: 1 tiff:PhotometricInterpretation: 2 tiff:PlanarConfiguration: 1 planarconfig: "contig" tiff:Compression: 8 compression: "zip" IPTC:Caption: "foo bar oiio:SHA-1=1DD1CB3C76F9A491B115F85A6DAC1B1A8DD2D3CB oiio:ConstantColor=1,0,0 oiio:AverageColor=1,0,0" oiio:ConstantColor: "1,0,0" oiio:AverageColor: "1,0,0" oiio:SHA-1: "1DD1CB3C76F9A491B115F85A6DAC1B1A8DD2D3CB" Reading grid.tx oiio:SHA-1: "9B24D4CC05313A43973AC384718D81D19183B691" Reading grid-lanczos3.tx oiio:SHA-1: "D6C3DEE1A567726FB962FE39F0FE94A3E94530CF" Reading grid-lanczos3-hicomp.tx oiio:SHA-1: "3994E2C39E040A372BA1894E805382E5D470790F" whiteenv.exr : 4 x 2, 3 channel, half openexr (+mipmap) MIP 0 of 3 (4 x 2): Stats Min: 1.000000 1.000000 1.000000 (float) Stats Max: 1.000000 1.000000 1.000000 (float) Stats Avg: 1.000000 1.000000 1.000000 (float) Stats StdDev: 0.000000 0.000000 0.000000 (float) Stats NanCount: 0 0 0 Stats InfCount: 0 0 0 Stats FiniteCount: 8 8 8 Constant: Yes Constant Color: 1.000000 1.000000 1.000000 (float) Monochrome: Yes MIP 1 of 3 (2 x 1): Stats Min: 1.000000 1.000000 1.000000 (float) Stats Max: 1.000000 1.000000 1.000000 (float) Stats Avg: 1.000000 1.000000 1.000000 (float) Stats StdDev: 0.000000 0.000000 0.000000 (float) Stats NanCount: 0 0 0 Stats InfCount: 0 0 0 Stats FiniteCount: 2 2 2 Constant: Yes Constant Color: 1.000000 1.000000 1.000000 (float) Monochrome: Yes MIP 2 of 3 (1 x 1): Stats Min: 1.000000 1.000000 1.000000 (float) Stats Max: 1.000000 1.000000 1.000000 (float) Stats Avg: 1.000000 1.000000 1.000000 (float) Stats StdDev: 0.000000 0.000000 0.000000 (float) Stats NanCount: 0 0 0 Stats InfCount: 0 0 0 Stats FiniteCount: 1 1 1 Constant: Yes Constant Color: 1.000000 1.000000 1.000000 (float) Monochrome: Yes openimageio-1.7.17~dfsg0.orig/testsuite/texture-mip-trilinear/0000755000175000017500000000000013151711064022630 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/testsuite/texture-mip-trilinear/run.py0000755000175000017500000000026213151711064024011 0ustar mfvmfv#!/usr/bin/env python command = testtex_command ("../common/textures/grid.tx", extraargs = "-mipmode 3 -d uint8 -o out.tif") outputs = [ "out.tif" ] openimageio-1.7.17~dfsg0.orig/testsuite/texture-mip-trilinear/ref/0000755000175000017500000000000013151711064023404 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/testsuite/texture-mip-trilinear/ref/out.tif0000644000175000017500000213674713151711064024743 0ustar mfvmfvII*> 2=RS~ׅϋnŎ"Syƅ s W*{Zr $n/2016:02:25 9:51:48 x׎$Kv%B눌Z簛= |;`O/@| .(8JΈ |fU}=Hwswm{-w^j+A,G29өk!ώ~ =$#z9'ǀ|,Z-}nQߠfM-mi~959^G(IA9s3~yy]=>88\.T*vzϋ ylxx###Om|>nFCϙ6μ\.^000 }Wɉ"v=onnݞw:$ǒH $\ >Ҧpmuu{=C=l93)"[^ϴtp#wX .RI9L"H (YO_wne?{d\nRڍJ^!K?_omgFS!9Ow%)L_OzhZh[fe9L{Ә! J[y<{B}tNAd2r=^{lCwبUL5 `0,yL!3x|(˵y#rMHoluK a>paL^RAU>;P9yƼq3/]soQ`Mwevyf'@F޽+`ohC'}J4_Nariоyq8jêokќX|,}FIxB%Du̜8ʓ6<+Qyh=/fy*<ۨK]`][.gs郶/8FqEyiwpU[y3ۏB&`"81\n bg+*%(Ƴ;Q^s9<01{8BBϻh>pk 8۩c"7fۘ^Eys1 >>GckGɎ!h_{mm~5{?eu9)YW[K>+ m5{pYm&(.U h?BT@L=~ܴ2kys+i##HpZC[D_J0@3>e ǀrGƁS@)n jK1BXg)8(|j҃_&DØ|4Rw&/:ѥ 2ߣQʵ{xB8&|.K G=dg8C7أN<< !<;{AԽ8#h>g9$rP:FrzMu "upy#"G\i˳=`uՂGHԱ IdZ W?9[aZxTFP}fݡ<"@%X!x <0z@lEYz38bx!7_ UjRol?&ƫ2/B8ZפALw t+ ukMoJp`Eqn+Nѫ 旺8vg1t;[q0&7'p.8E1]F98IjaFјB`}BG)O?xp8+woNFωc?EGJX +VG}a<^̖|Oq!`i,9p&c_ow=o6~7~Ci#Nvyϓ~Wh}n7}jjJn: ?W8`ttoFMt4]|~*p} #wooocm{ {=PՐbys"!۠/kٻmyFao/r#g y=Mxe;D)|UgSH  t3='gz!`yGHhGQ2eeȖQCB[Yk)-h);+/#:Kq^m[&㽉1xݡ?Z^uï*'ϝl~w Xxw888PҒ̫ N\<ݏO>l{rrRe9;/S/oy6h'ƠN}W[][:JJmf tO۟&Jo/x#1d"QԧÃ5cPSJXe溺O!,KN"; wg|=rJ|$^y-(x ϧ^Z#&AB :@~ꖍRI6F0:44m<~:' 8ZFO|݀|!5K|Bah_'ͅ 4:~G"ۺT\@|ľP#JC[xEAp:gm/vƕG]8sS{⎌2>)) @ޤ;1Ḏ|ɶzX~'⮌%o Eҫ9iYt¶,/fz3WEO =cg7)t+}1 +s:-F"q8}pQ>4 s-&dIWIpQh@JNuCFdAw./4s8JKJaP&O}y.N1 ヤ];)lWcIa|| ˶{ Adbk8 ڱ5FLq9'AK9GCBLG:y:6ݑ6^W.$Q;i 1mTu pzJې $+TTO{c0M{̋"XR]v1in=w FL#G 18;阿9Gmp.&Έt9B8m z̥4I /J ~k+OϾ2GAz\Pj,XPZǃRv{Jĕ}ٱbUWkD<|8`=ѕax,ׯz"'E>{nC~,(P⧢*(r b&0H-i1м]3DF50]GWp=bES~Hgrnaz7NCpEǑIx UMox^/;*(<5cy00.BAp%ZnauOuØETpѸ(F':x}.K0M 巛pCi"xS9>yaTw߃"w=Ml_$D,@Ydt5eqVw(Nt{^MOo|?ex/2{W3FmeL 9b}1'tw1x] _apR*XƄ(1O8@X>{]58XkCsٜ,9ǖځ6nPVW'yvG'<_ ݝr871@-F?b}-iΟLO,|ĻvY,S?㖲Om/8b?*szLl o1@j(eSԦ6Xo[Q;A,綪-4M nИ፝zG1@b1\ oIw].5cξ`coWBak'qs4wh/h@U:RVp:QFC;8;qх2O1m/&i !mE/t1183^a!ƫ 8hZZj|3gc3A9$mȳ'oBs %}O2z=Q@ 8yۄ+={}BGhx1Rpp/ZKq.[jwxV!Kx?id!:]7]bZpHo_E:d'vhw(}x,N‹(tPw?:`lp%1 k(N}.i<(D_x`nڧAM_r.f_q׆n[oe~%Vi_)-63v.\.}={¿օ9GjxE_9b}_QgQrw14Nߞ865qlߵvx>3mkkk+ɓik}7~6 / '9> k*&FW;g`ty `=0!+osy'@Y.:,jW"˶JdG|߷5>/:meޓq ƸQ[;?tGuy1I՟*֯@A}RuLb)}qq. 罘kF"67`Sy#w2TkH 0.לTo C! EĢ1-["*UN4\ 46tO<鈘.+e|m'IiuT^q$]q#x^PѬ_c2t'3̇ϑˈB֮KGǮОs)ԲLˏ8R7B:\8ij:#Igv >\jݶh&ZNkqt$9=f@XiO_2LKw̫26܆GSL,h+ 4cM}/7axgq[gW{|1& //`Y4D6 4Jp5]aBH AlBm BSW]D-3"F0y&&woq1A  .9@>5T-t~&o'Z,3} &](6͙*s_f FG|% .m9!@{Jwr=R{W_}շ;i~LݘrwP>vJhKE:?;|cK?{L_#_O,wYXXY FCNe:U2y0m; C:\NuEE(#4"|cwe㱗E1mq{gk秋gS iiB֯p8F}2.mCؤn)2{ƔQn3e~2ր~ x\?4B`CTp3;k2֋1qE$+ILuY@A1*8<$S1?HT\˖Kjx^[H@p@Uxv9.RfwP;Gz'l0ӵo Qv%OH@qԯ>#zO#;uU@lmK[u$Q ʅG[-TVȌ2k7w=}vw<ݮ,v0|/8m Z ~ $0%x9ʒZo`v.ND=\7'dpA,@*)}(jv&[ ~B|Bp$j;#X^0Cv/w|Xqoc ,>P)h ʌ`m O>1}._TvwO?s9pu)թ?OM@}ƿXo#@_Vp`jx{h: Ll/Su›~* ͦʯ(s8ܩ󟞞QsSGRfO9Msx&0X;_sGg4F9/ADߏk+(6^hDScce?1okPg~m.Ɛ&l^'ھCޛ8 kc=+Sk|[ZM8R>ZܼoaM.:ƋO]oR#1Bw)S\(}n:oal r\ spJx̐M+"p@>IQBF#  YyNƳ Wذ>b/ᵧ"/>b*x!xVbYn%p>2h|o[g `'oI wQ5w\. >?`ja cXK/}fO) h 3q2!;\~n}\"jcg>8""DcܸD7ݟw+ЫcSv*R sϏ`""|vyFC?@ 66X`WVE~p4 >8A}N`axfpy5&7k Y7؋^u +wxU\Ƈw`{}k':\o0ژ@0'8 ]U_oaq^StpўTsnqFlkn _^}g*bu{}r~GǙ/hbvR (/TO+y7qQ P@Fm~-p jq<~wWm꿮VP2;yoќx<ѣGK=\-ԃh'/\lN~::k f> |^ /xlO|~|\) 1@f*2ҸkhUZOc{W$ ;H{o/JpK男1=Sm`Npkɼw-4bYZ*ScI[7(br8Z£CX.`DD\&gm3ݖqz& ƯϹh}Al.b8R <ŽzCu =4aAp@slX{#̴NFԎ憫IF[k8p.zNhblK%EGyt?SeoodӶP]&/9&. ﰁ4uj%#ubRh|ּvl/6R s],|'AX-ޔzԑ׋ec]`,`a.Čȁ M_OhK;=pd`7Ә70O0N"#?SQJ<h+sBW! q wa6x=à Sdu d0b'hm.Goz8 ,ɛM#O&4w~}d/8iZDXc)>8fQ=*Lɯvq0qg랱{06&/Sׇ1xg `wRո|''"wozHxCIkH7[i2yEikAA 0ɂP\Ҷhw`͡C}'nxx URgcO$?N~`H=^DO_mٸV]Ï-m.~e}Ox88$l3٫xs 覂B7"z#[1DzDfEA5x`I{c__4LzitC:u7[ƹbԞlnKw1nѨ%sݰȿrn8K8z F$|pD+vܢVwp0.7}ḩ!>⊒f[煎S*3울ى<%q~xcEdFKt4Fk++l^P[yrȉ9&2[A-!|H:)OК_܏}XCWM,`jD(Ay#o;8q4&%1|{-t[Wp: 0GaPd"q9uwX`YJ`f͋0+՜̏!]LR9gXX1qÇ2obA!p@_0X+1DCMiiS_|l;8^0V<%8 <Վy[d)8j7XZADp*LpiD[qԫ|*ʓXd3YHb_mXo6N~T>SWg +u+)'&ިl@uL|k..J3;x>p6c|\,pol|BToF +!1<56DGTO?t<'J;?~X>1%rT[_^z<b\ va_//A|bqwg&vX^jWkJ#?g~qT1~/؉jk0w7{7z|p~Dcov=z+Mދb19Џգl垶]>?4Iߓ>ae_׭]]^ļׇ]7 !kx3t~g[O[ ҏk;l "ߤ$:Iw@mQKO6Jr!z, " fHA w'9EK?sݧNy]m!^RT /NvQ1[Wzlvhgxhw?Tq~Q[}օsk/PiVp!OgxXdŸ>"e&Ɲֱeرsoah)tU /8O~'t{TRֽ:ce_]I7< LNv!sPnrcXzD,> ]ST BoG g~K,X}B[B$1Tk rIQ|v X x$21yy &8_ip /][la<\nbt?82<O^N14wHnca$OxeGpr??);oNkZ̵ׇh f_ZMAcg;&o?mm K1Eq_P*+N|c|:/~cHpB@ιdj7FAޏxz\C_}P#fk~ߏgܴeEk7 -ϑ;~fG0u\5o+xb,g]!Ҽz}z],e^~ԮlKޱ~cHv-<~ok:;$gƚƓ#6H[[u1~,v:J77|PʎAS,hF؝`Ćҡ uyN\_Ǒ7urz>p9nn&{&vӛq >W'8`Yg_7_q}&[Rjݨ>MIZ_.|z?T@`rTT>;5L>FZEkH{£:a\|0zDmE0v/\:(Hgb855w+cɸ/ҿQ\Br7D*g,=w x??Cui$srmS]L9mx]Xw:Ҙ錮Ayq0:wy=6|^V=8;@q?o8{`?m !x0s:o4/[oo1L;DZy>}O>_WqyMy@ekQB>-Cm^GhY]_}{}8.P9:B;cЍO 5L< YN٭rIpƽІDv1>bh"uըv24b^u;\A\ ?}\Nq~4r^pw U$uZg|pY],u3}A_r es> q~`|Ozɨ)b_L!nZӹTx c1gNr:ƾpǭez Wa闌ͭnc#X~ [m|79d@2ڻ-vq9F$ noӚI6o(yAji7{HcpSf7oyjy^3w<  *t5o G#kOW=z)zu ɋ"Gq=2iehl智O=LnuP(ã ޾c  8 +TSnxuX[FrC + clu{F3ε1jxA0ϪWo3x"T"@M]@|kycc_cppA|e8FjBsodn=k9/N⃆'ىWkk:6 s&CpML u6>ylCwy;H?ڗ߹ssi%/__H7GN{:.368_] gR3I})#?>յ1^|[pݻw42toLN{ǻ缏ӟO8<8[4uO i18k\]u%sI0ni=[Qg j`WJ|^_'>π 4m\>y}zNWW{ 0vt2Zre};:w1TUN朣;Nup!ں+:w1@:lƜ:[É\]sW?_;ӯt 8QkP?{szd, SUnc9S)dYģqMfC͓á}\*-8 Sjo5k,x/`j-\J^ .p=5z0ݲURv\Lxn1@4\ouo f-_k!{0jm _uiLt&mMki>'}hc'uo-.Ay߽aa1L7j#X-#`E `م ^®}Xzm6 =[3`s͹ ޽"%r8`*8i@ucY#VOhd>N6Y'"sBasSM!<{l8y k̭x3x=/垛yuGa-ͿYHtJ {SBXϟz3e/_<\bֱr ^B#n+VeL/}\k qd2o6,] 9>\nRk'4!G_8YC`Wxc_V|ug#cԭ)~Nu$:{Pb}\{?_:=E-c߯&ٲS}_m^?71 HN~l6PaqRz.vz_x.Ut]o:,avYf9LH?1?َd?uoכ3s]A;򞩈bf$EBnOc5_{b!X<3p)nyA,@?_PN*'2Q2+[< byUZO<;uuE ݭdޯt|v5ZuE?j9suގ3lj9ђq5}DWe Q<7#d{ It9{򟟴!^lrJx+;HK_Yć8zW'>ƪyc:7:Vk`s(GwO@W7&`)!3ζ٬]frxذg'cmRHh6>ќW?p>zW?54HS07ڻx;XC)^shun>h `81»M?9^ī`kBU5_`s^oao񄉙y&Օp'&fZϬ]SHu633?R?C]97zGyξnnm\aZG,`佩׳!(C{@V{J?QgaB閛-4k45 L?}|敗WhbLlv3n`c:\~~_U9G__p~r>}lb<,o}Ie[| jcEuN~^? >󃇾 }ϼAKk>6ڪZCd'Z~Hݟg=쑄C$8z3:xubЏ cS&} bЦlcm['mRdr$ n"@*` wJ?s+mܔm bYDvX`1tb2cbpps)S;jZ{3j}n #'s1DŒ:}{Q[XX96RykSFCW<(wӨ0JAlz aC}eߋ̑DÝWfo{m+k(yPܴ^E&ӗE6:BM[7A0 f-1 cXuVQr&s&z D71k^_o麃ƪq_зNʊ ??*}nymt?%r.;H8=̿}Iw58m߿0[P1wЏD>O+?dϽؿvWS۠|5mW^!C6(ˉ4~kkyAA?m0>IL8q}#x8kŚZ`3rcҖ82[0es9\rtJ" eg]b'v7!R7mi2U/vz@|Xx&1@KL-sH࿏h\@"[<~[,j WG;lkx>WQzipcjrql.O.ކ\e1i_J>lNいhvulc?c6j`dx \=j6Z[׊4U봱[Q qPk<,$40K>6ˡp.Vy&f97c{]/dh6 l,Zr 욛8>=z.ٯρl]Cf{ \pu1sr6ДԖ ) S椒u.P8Bِ6eYY7sXٔیsu΍!ү}yP Nr s2gv}鳠aX*C([&c.v;+g?ӱKm_'{8AE̷f !ZC`?jؾFn7a<H%kN{!}uy F֗9q\ ߼YV$1@֘s56x>m{\VQFqYqx djP46ݎՓh/]6^:<ܙN4~viq/ <0<x𰍹ٚ`SZ5c+B+{8}dgFٓ "b^ATs?\6NV1w%уbv Xy֣( a,_3}T6=b-ý#(?8+^aztqBǍunoЉ-Ɵ.Nd[pPwṷN^،Xȭޯd}5MSKw%ؗ57W }'^]jib1 -"f$?XſF3VHȣ-ݮV*nU7;y2Y ̓'On6+ȋt`& 9CNa򀚉&_oMi8`$zr9 % +..n:NWؒD}zȧ0M&(ۓY2-J"|@Qs 3iϱnT Smx~ZGcD}{t?z@p#4`Sp?wA.} Yfrsf{ rMiz糑,]~V Wq7]/d">,VHôI-ں{Np킶1C:B:<N>xw? |s2¡lUEƾK}⨛V5:oɬDn@˓@fxw)uo|4Azkq.Vi!J]WLWô8AS稫MrVW=Os/. Z6lB@A W,|La(6B⣏Wh{| (i@asZ_Zs MpzIkn}x.DWpqqÈmX=f[駍Z^5:{{C]sv؞\iv;t*P4f2jبniTꍦ|Е V.XOh88m fi nq1}sQhvG.q0ئϰ] u\æ}kZ5cOr8ہ\&=8QRů`[B-xcqUq_g 6= \%oz+Ȁūepktr${1:yTZ]5*M|"kA>@r| j&Ǵ{Le/?p IH ME44S"I]-6{xd*ѽ5~PKy~F4gq@o#/d9EV`(%߿Ӱv~$?=9ۓ т_LJ%9|6/*_RnͤW9}4>ߐ{/W59{x)дiKB= C~/ {~ײGfg¼{ʋi:ͶD޼'<qޟ~| ~{.<' R/0/ܢw?_@'OH]ž\;8zpW_e?v'+~Hi6zﱞv$~@ho*i󀷇ych"_qu! |:o~率? ;VO٬rc @A+|G!zRM٤M.z.bIzf.+5b 7^v-EBs@:ZkMӮ?F1xZ/5>Ş a7kNV7{ `Lg{Ep196wu j:(AWM{tp1Ѿ|&tp;&2}uR:7V O4oRWkz 8'h:&-ws]sЌDkfh#QMzCb. Z~Nru8:/ E*{4^M:iy, G_[q@B=]JTNP+91)ƭEH__ݫWDzGWTcB?ZiqU@>+B)0 Gٳ0I$^b|(ZeK4{jbg,AOh6|{roByROZ_יnhS}nK)c./oîIk☆n\>/3AmO6MP*MĀǝs:/JB 8vqhʰκya'v<91w#eݜDDkҘ>Z7ۋ;e5$1ZQrȃiM D׽}Vy1 {?ԉ|[•'kꢬs?s>8{X <`vZ^f};h}s|?4W4rJ]W2ێmil("ͼl ' $ud]]ϷP}o3*=q·C#TꪉE:R9{V*IzFGG@DMW5e!Zl;Wjl/񸴬 ZIg4)s^] ƃh:DSZ]E>ˡ۠7y Ɣk/dmdδgh44*5o82C"3N4V'[rϧh\ta;d.Ic9P=P?5nJNIwJyM9Wt@F<\١!>ͽgq9c i `}϶NSmzuWK(Q#P[@]}{N1^n7'^c߆y>R/ܦ3{[??ϼ''$]@RbN|~LQWE kN=RҙX=`Ðw0䱬a|ɱz/^eR喤v:=#/~AG.Ϟ=ꦦ>0+{'?|?f !Woy}pp p?7 #x=߼/E`ǽo>o4ON;?:x|?|?f_fJ_<+1'iǻK Jv 3Ga> hb`/ -GA$yCeR4_4|:yXQVmy9\0pp~@nP%TLj+.=;H$ 7 p=-t $zct}-X{ZtV*8-؄\%R,JܢO-_`| o=˪C<7g{JFx&9z^Jg.Z6+~ZঈI,-_nѫ'~c~m6mMH _.D`y8 \4H=r0ƫPKelq۹ >Cb3:AoWTy?E:c _+^(sDU#䆚ڇ0K4Mc`8:uS4=UBo˴Q[UzWUX8ǩ);; 5 ~K]_NuO ~;P7]ݧJyR c#_GZ;O85.yaβqz̀2,{LAb; ~x+ml_ @<@T% U _K SMmG ܀jYgJnߴ?.#  _n3 1~ZbcT {ểH^pq3,[`;[ӗ~; B_/5ܿ8`uq!7 x%n'3cSǕO;{9;vl-.q9կ_hOw0= oyn>ߝx.3xm}~m` %r[ߡV,Gsau&滠`=pw;M;%Ϡ<2( cׁ3w#bUjxr>.6sWpǎG[ޥy򺵷kUR/bzyfwtcP>;0 Z0@eF~nPGjN+t!=SӚzeIZkQ|?(W+> }0mN + XIt V},r?И`LY;{BsmZS8Z}V`Z[\N{sҧOT <:g[>Ọ9ǀ.o:02Ctrd-g Ѓ:s|U-IOiwKpbk"~fO(E;uثK?T*fUhvH#:ݧjlT{)g|c>U#tGKу*(g>0ibEWZk`DE%)?K#=8 3Q>Z=|ps⢫\}hj.q;HB[|ytrɱOhw_8XĮJ6 6 X'_fh "od}7>+?w5`OQTL>b#p;JKzlV9կ跿G2G6Oz}7@"&ַi_tLՆj4xo#F7.muplCpW/$}R_s=A|l #w`s.7\AgxHYYcoh c´q'?vwbz{F3`>[;B:;>,wC߯F-10YhiY%6vFra T+He~,^+wapF}8&~8x̳1ERdxw(˨NHn0OP(\=t%ah+eDINįo\tV_ыQjod2ji :.g&++q&Q$ecۊz[tM Yt߿_eX14. x8`x>>O·H\;b~5x Pr\.ҹ88jGhOu2鑟AZ?.txD⣚̨>?:T= lJe\t[./_DžgIiiIfB'@<}͘:4N(כ\"G^l] y!0 jSX{ҋ;Ҿ 5t).>3T>p Oc |H |/w3NЋ<[jWͶgbgUSi3vl:B'f\#sϡ/tq{ҟ?cidџl`)3p@ͨP?8ݢ?l]/k3 Ms, gQpI翓̭QpzIZCطVE(袪E{NΧoC|X+&E *(wCi^ |n5͝K6DZ49IG Tn*O X]m|SIS4)G't{(BCfnXҙ%ZVhߥMhJ Zo_8݄Poos1o 9O^ChK}`tPzD't?#޻;P6;8t߶uW=@ިQ~-3fv ڦ7c>(^cLj Ϗz?;ǿ/o^|r[gkFڹUH簡pv,h` p_| ?諯9߰u#!5b_m<}s܎o/a_ u^}!aVnd|.?r-GmΏ?VA8rWrx]0"F \!𨔢ތumvi N l:pŔ+huj ו~K huI|czJzEwwh"%2o۝3=75is}.<9콡^J36tk> 8Φ-(߆un$h8@)Gi=L9{p9 HQ%k1:ҜccD<"|bh 95K4〇TN\rY ]Zx T0۫%cc??cއG./WFTKyL|=|kz(Q?܏ ΛHJ|j_] #x~,; L5˼߷5.)1fF'l`=uf\hY.Klj.:?e^I'zeOd〹 ML0lo1oM mFqq~u=?A/h`+>۫1Hs# 盎 h8Nji^riЫ6=" LJECxAV\C|6l'W<%i @=<絰]j l{9&Gb&[ST|wka'^PV֨2hUTw\7jl16z1=%F. O{}(JA8ayxWy{x|T>|>p草?TVˑ'uu>#yZ+Gp?%bW :K -Y_wv0#6]P ,ό<@9k۷$3gf>,"zϿ_,\*o dr5}!3Q?~S<=G?!fvp'/i}]8&RF. z;O TG/Sm۾݀D\> l쟬{vko3r+9<{~>)vs 'g{ p7s?hR.^h~I>Alw͉t?pz658֫@Khg9&Zdp!h5&>q ߝN9pc?[{Cp,qQ7WP@K@y<puΘkwޡm}Ӂmm&&b|ԭO> { oCi{: AJTրuθ57#%pkmk4hB4u$;'mrcGJ0a_')8.km$ Զqw8 F\AK^ or8 b)\T[MiqgcZC9;~iF]*&GIwuhapjw[xgdy2{ RI+bcJ! }وWDj)  tWWy}sOVvO#ٓ7=syַ6J @*Sy-#Bx zP*k`Fwz~Lo䳠3.ǖGrnB(y)&w_yDw|V Y_>笴SBVߵQ \BrSy9x-[!oꓧ+&P}!_\OACrl~|w9?`o}tvN":./7i"XwKY9NOJ_6 ],,,Upss#u`zzZG=J~!=z rj=5tMtc\ QkZo//q~Dom=m9$2 4.^x|>m)!}F1]D0D(B=h*e$#?s8[Þy/k2=ζ'mg9/|^V!wC댡.yI!=l?=ѿk.bl7#Mv!(.^]u# >8N ߙbg,RGդVb:mL—yB(9TF!)s sk0Ow3>\o]_XZ$k]~qi)R)odNmm2W\fܠmBB/d9\.'3&F=1LFצc_B 5qq}lڌp.Qgc9WF9_s3cqrk.jFak <8a,,Vtz?rrRI@Xh ͠x=#͇YWuGG׵0H`g+\5AdG7ɤȲȯ8u9GVJ+ǎ91Džrt+UleV1]k+>$A^J5}^{$Tb2qObʵaMMs|&C6|ɎA̺2eyi&?h11Tr%(\_D[%GE4:\gYT2gzx.f^UWs5cCCUN^)9/^_6y%Ѧsݲ^yHKI.}`7Z: +O>6We|W)cbK"@"%_ͣG ~'!ewM Ϝ{N#!XEYYI9y b1F۴.`0ܥ\\-s]|oxeFWz 2*2ʋH$͹D܁`܏=?g]\o쩜e0??zggGߡtOCXbW$ir$ o&v`2\(+D 5αP:W//78],8ljCכׇ6 s& |1#\qi"/F6< 3f-LT?~ztyGOOW< 2}dtH'KUrEfq8ggkrWcjꋊ?`/B<' :V>]*mɶմ̇v]}^ڻlmr ~^.Gp|Tp{vNɗ[AdkyPSfۉ% mXP"Sw6JRe`0RV{yѣrK{{p\>ӧY0yCto~S?6EWT)o˱}Adگ"q"r5,5c؍o7|EUܖ)Ȗ'X{Rhgah ףM2 crYqN值5) 8 ӥ2 E7R .`?#$Vn܎+2,c\]$KuYl$s9n^d_OmXuu})6Cjgqbr\ ݛ!8%z?`sߚz'Jujʙei6R\ޝ,:RpM |޹ɘVDWpE6 >g8kP}KoY{rǯX94:z aP[@җD&lC4M>9r~YEGWBoPk7a>}t0{^;.VH+k~V]b"NOUR־>F1ќs[aG{*7n|V5{) g0>[݁.oKi+R*6 +3 pOd2"o~KD򅱱_pOp8)kFk̵wܿXhsH$Dۅ _ؕ?~~ԇo~*|>[(tcC1zw4ӥS쾷'SuK>ԝ녛Uoceq {DtIY/X[[Š~@m`mXFFOmDdq1ҩ4B߰O@x_5/rK dGqY@PѹFB@eMI3bc YGzMm8>ÿ]$2VIwe |> =_IdP=ݭvk#ywc5n=aI]\+ӔCʛxr_>]iÜQ1ȠXh`s j( ^\A\VL dSihgr1UBЁRڰaK=PݸT:'32뢿%c@myEJƔp-[Tq8[b2 !,vz8WO5q wdޗP GeP &>O.blnyns5KhxЎ@BD^rsqI$8pPg''x㷥wUQ5k1{?|<FlbN;|[P)N'CYNʟX%,`W0ΨQcא= T[ߛATb.&[Sv+ 5ΫZyqځĸ k32a> C$# GC]Vg15U/e,p,9Ss>9ɼH7X I6PS~!i}Ň"''}#bgE)}P=E2y[ﻍ?Ӿi˜8W\|[.uqۇuTyqک0~F8Z>|Y CAL|r3 ;(vٶxՆ_s.3Ǝ5FuxA|$L9G0v w;_pޒ#FJ_h>wcRqz"\jퟺc:5c^rqܹN1^ my,{@9-|Zr־H ߘF|bv񺈕Xʟ j,=`H&1lLq]mWe]4@W{vv$tmxzXJqX6.eP"G<[ 3 Ooo^7c @0pZ<Hq!{+'8IY>q0c ŁEs/E~~lLۢ&}&>0|;Sr_cն>i^>:+$iJCM''L?͸b>Q~ga6 z8iW͕y{Gm _[$*ƸXxW\Ma\lY0r[i .߇ xs/HnƟu:4" L+`b?S\0b3<8D@~K:vy@'m_acѼDa3Ўuf*y$qgՇ5 Q(ИƄG\#1*U!XźpthDeYpmŬV >e2Ȭ  ϦEe(;#t#s\0}QДI:qs'ױkj`q<E\_ccbtx=gTO,ZX3Ǐ8B{S^d C_W'2rkڸB~L|eBmggx_b(FAD'zz=э>8vW  o<%ƻ㊧>naS<,IWǧ<F{qX'"h{EKU_dd~ ߓ.L s*W YH{6G^mS&?~_j cϫxc?>G?5~e~^ʝ3X.uaI8}NJф-D aXs9n]wXz2/=yb66HGA>Xܒ|~iҟQ2rΰȡK޴ɘ@eL <{Lsgh# F:;M140r@*sM}Vd 8~Ԑ>*k8ȵ>b_煱-=%?8(gd p0vˍY\dFG*ogz|FBncqD]+ $<`a@*@kы3߻ѐ㎻,=w3DY )2yCp8=9ӸT{(HUc94ˏ I-oF%ߌ3)mN.s3x~`$2bG>ƟxtLߜx˜9 qyRkk6/ܤ,/|:6vC0\bN\oo|K)6|!qb*XCmRx\\_;*Mߋ?՜-NL|Nc#Sx8>~G"/Jx=b[}pH#~9tva>Uk0Q쿿O'8~z{ܸ"A`콿Xߜ7qc 5wKiWYir]? ?ԧzU8дڧ|/51^mP=?Ehg3^}A?kn6O}n~G@lj~uώ鱌K'E<> #Qz{1:X@)>+wkL>cV#mb3gv9ڵ 4<R0^hc)2\p5s\.ؖEiڼA1Gi'dn>OO5WI]tjIjn<>BB0vrhz50ҕi=EC(n 673`T\_Fmiϝy|xv32kcEg! ^)9H7p_LM8Vs/ȺXp}agwkBO>Լ8牯)#>1Yǚ(֗n/훾O<ۚW)|\;4e7@qv|:|?+/<;@ k1&9чXZ0-c0UvѾc:Vk^3!Os>'iQFD"le+dޱCPo?֘>jWT9廗T쥾϶/>` ڴ~`ȇqrrw\if?w!?8AscXT98ׇ"{qRHuyq6aKԹYy|;T[zb=+1ArbZw;^<9#7?7>r]>5.m4gA[}77+#S>٫-T~q SÊ%tvv1m AjG#FvOզD;q&X/@}I12O njhTJ[BkeEM,ǗXB`V}m4ޏ& 9s2q=uz]-o8ۘ׼%V׎1z<|l faXAUh/_⑨1~^N4ݜKυn^EPT#>GXZ#[tUv2Z;Hǜx0Ϲn)'Q^(qv#L?> <L^~E1[:dMإ\H9G(QGg?[H|-. { /<  ߌ3d>`kҿN3(V:.h ߈}d!wH}|5.zT05L!V8ϕp.*R %"dMUϾ+O`(?x8{Iwz4N:il_]G*sqwLv$YcQ0to9׉#g!z4=[TlfluseZEاUSM;e,iT h=_ [ب[o]e<'sZK/\kL_4RC͟`̄|Ƨ߂ڗ&6zZ.z{/W[)l1")ITA%q}o"UyBz:>ljy&my]b>o[v5lrfb?y.o)v_ЀO4vA1U|ވҿF>Ϛ#\'\W##okgߒq'6/RR.6n6psboQmoowb;y}sT׼#>l;1_ MoU/WLmVs& 'Ѥ?@ZcGCS~!sn61|]GiỈP'70Ƅ2.p8Os̢ӫOQ4cɽm!ԬhlIeS_b/A o/X,VWSNX-cbhOx2K}EtĵT!u^[ty>[jcS]=.khO/W~ZdPC^|VuwQ-c58㾵Sj҃˺BKnnj 5Hq쏱Q[e;_gc049jrߤ[S_3 r.04е` z>JhLRA)65>z[m כ^܁OyL_߬N$A}j e~HOZbkddؿƏ/\EϜ7f;+Hsw~Yd\l\`+ ꔱr[3UW{yӧ>dm=ҦOڦ6OMb5Ts{o?e ;okk'a#ț#N|_g+Sꂂm-hXm47]y: کi >moo+f/]:zu6綍9'y.uuZƾp~yEG.~ym #9 ڽDӆ (8@dמ:;!*x}К7+"7Y:1:5~'u63ށ{ƻ]][Ĉpr,uzXN_}lN}ƿxK>⨩\UU2 3ޏ1!knXZp w-Ke%k>a1I#0 K^.dg1޵: c^|d˙2yє^eBْH?p0I#u+T+Pd,i}HbQ/vvS`}%hGnМ~sXm-\@,`rnM*紎[ѣ͞n MѪ^{ks?AG[ ]/wp0y[K`4_1 `է-inux ^zyq=`?95cAyoe\ɉ Ȝd&nvop!ƺ0ڍr;r)l5FD /75>5bpgFOz5f?:c[bncL,4}ɴqp 1ėGy~+lj Ԭdj[k ߩpccCW׌W&鮮zzXOv^3~ڣu2$*cYñs,|XWoThXTko}MM;5W%$wHky B1YC@YʩO|dVbj}*W#X,v)8n5i#iU/es;<4j]8 *_=!GFȋl6 c-ɯv/^MG#l7߇/S u~D)`j3`R(Dn>35v͵+zѷGdNg~l xVB94 R3:5"iQn 4h۸=>YrqwѺ6g/)B}U8ͩۍ40;b5J=xHekkMywg26oO}5|#ŸhtL݋XC\nKj4.ߎ~ϊ.˚y'L˚k|t]32xO{I05X@Gkx|Hcg k}61t%W󛚥j Љ)GY{.79x,9o({{Wa A3k1ԑ('EKu69#*lh Vĸ/MIZ;ʁ0#q10: m5WuMr}0i K\rX_LS??Y:QNI}^Ol6 L?1CyS?^Zv7Lw?Ͼ e3?O2}XJo58'[6ϣ5[j%oIT~1_6q8͡7dл$;ec}]'G=ɯ-1qf՛@Wjk]rsK[3^.Ml77bj;>jz`ɫ=[`yo Wv\oܔ7 _i`$V_֖ 8~uk /{9FgD<}d6\s9vS. f=3XߕʽϨ)V^au/ng0A]eEw_+h<,6:444kV梉2A/⾭ AGh4ct~:znˌOk `n@&ј"[%o}ZwI ǕɦbZk ѷyxQb+ t!3F _>cb)*"X<.-jQ{ְ<'7gw4wD=Rx5mWq?k-b/0F`GWQG+,1*tuEi}n+U<ǽOrL|sﭻhtX`5ɽ|+G8X3|ڻj71΍upr]&ǂ2~K̥<8Obq'm lu{߯{Pg~S˄rTGiEqD#`],:~P6@ސti#aB:[>֌+4y AovxLf_yh5(˘{q= Ӹr$e= ֚1Ɔ&se2gQ2g+[LJ0kD#z6휬6V71ODVQ=X5tNwjݡM~};Jָd;b?//6w:vsAshzQlMc Xu=铴!{~Cx;.);:W*q, 2E1gC(+:\fv햰Dk I=1$ZlNֲH݊S?k8@62>ن1#^0D ʽ`A;`[g*dzOjLXG)ss]g7o w%1Ja-kq#r Ȅ5r^Sx'*?r7>g7ymmW,k gݗP\sV_t5Bכp^rhn1:τ6VPy ϫ߉wsSPce \=XÅ_ 巸|bU.7u}ں'Hl',XX j.{9fyTsP fF =><o%֕`<1՟zy40x Xw])mf`3~8aQ XS_oOY7yGּe7UYEᦶSG7' u u>+T7:喽K>uLlcFS,-ސ:Ey`r P,6S>op bRqLӒ~|^(L62ܲ*kS]~hVfg+ssJ$t #z"3`T6Yq{[Hxҏ%ԽfHTms42Frt8_:y=DGm׸8uoHw #_&vsb%k-P)3[qb:h=<@}/[ϫ~Fo#(-z'UZݱy.))Snth_yɏ82œ'Y1ہ0F⣂{+w)zk*iân{GGNq^]oOLi,ӷvyy/_6~[S%2q~O̧a6w6 \,Ar5&%q%0W ]xryjq0v}j8K<o<':WEEԖ}[8@֌*yx ^ҧϸMVuuX-cݷ.RCAX7ljC[?95'_0];-`p X'5(E{1+DžEpJGҳEյv׋Cw10P6uƓhL@#eGR_qGU\̭X3`Ma98w Bja-L@?=FLm \}Wj5_NQbc^޹/%ԳK=~Π |Oܵ5hYMNZ>Jx_3& b#ߌ75Κ5|WZǀ(yL6Oa2ޯZ쇔\hX_<+h VUu۽ٶ.B G/`C{ SblgZύ̫Gk/=؃Gs_i~6\uȀKYo GjWv^xtz'X| _ЛCܼ_3qк!' -d2XG wu5E[2Ӵ !'qS]*c3EWq (G#|[5DgO;G޾~^ASdrkNot5>mu~G$6Ϣ5>]m_xj LMGtژ+~̿%2>cY{wvob(qǘZ⿹X\~LkWnmIa\RCE3Ul z\ٱ6?Qm"(9b]qdDzt7%9wM$~qOrm>I3@ r 3==LpӜBy '1:o666/Yo7;8~|x9Z/;y^cr}E_QlFHLEE7XFSN{+OTYGshiCCu%4xiFۊma#6-v5ohj- +Tnϯ;Xc8Te΀A!TdlO!~V 6^L,CY|U>D&o:W@xӲzf~DY OO++x}xÇxGݵpTg2 yFtNvͅ-qy3sO(1pH}cSh zWw64fo8CƃquJZ&Z5䜌#}nw=Zy 5Z}FO+ZN/4'౶^!xf׫53ONW|o4$.963N"u 3Aݿ>+_/`b:B-׼gb)S+yA}r&Z?ٳYז ̞ۺuЍQwu˜[irRk& <;9Tܚ>r2#kl c;h }^Ak-뢱E7m|p95.5R g+G;eaT-?ϣHY_mKBouO|Yqx>L}RC0x("< T[76YP;i!9{him\ rN}n?,7q>t6tJ] 6j-uá8l ƳxNش̣^3dyulնXEy_}&7xЁ}wso={q? `<`iC.0ݫ '8S)5z)1ֿ=xMzfov87y{w?bzU^Hq.rߧd-!/u/S{I4#[sJ&7ь_1P9o?k&6ݚ|Dxk:s{{y= zc#k_h1NadcM'29f_dc,W~懩Sc[Y6ct O{0FB,.*?-_:Hc>W&'QL}GCOwl9\ʼI߇i8EY P2n`!͸61ꮴP5@BW \CۭTf.ͧ6Cy7ϝG{ ~B=yM뛊,'1լLI+j1 RgFRsRQʕˢ?ۏޛ^Dv˽O~)GlR(xʹyu~_P\'f;M&^f>Loj_UӖmij TqyWjUm|ocx,&07{UW5q8A]k\ϬWb+m|voLY@ F^h9ݦ1vK=ӐL(fck`[?v`gb81[9Ec3f}ඞsEy o:6Û؍j1H> yD\S7u Qȍ$0'/zQ2zvd} w19AY[4Y}I4QA%C}}Ԏʽ#?ock(6&"е*u^-SYYfE^hb8Tî0ӏy<jlП=CXצf,m76mq+a~i㻢3ァ}54șKs" <:;Ǎ즪1bBupєֲ{qO{ Y۵"kpYjg+Ub?] ܿf`+;-mk—01lqV6~}_ vu!WP>g8Vb6ޡՠRwPcdܴ^19}ёTYeߧ `@kG?ݻwed}pq!'ѧlu/^|O;e:q 㧌]Eo:BfoHe~K~Ecmr "oӢcȹ DvE=ތGE8w1Hr* '!梀=yTλ>m;(VP tx&nA?\SIMW3FWgh9ӟ!k;yir'w{ ! zAE795WyJI6ĵ{㬻/qWo jo磭m7xKۡfrM=je2@E]s-g[<{{ʓKtL|Y{#&ρ[6/aQaE(8cз:4IAM9!j`Mx< ?]hb Zkš`I[AarN҇/=|RKҸ~b=>{j'H C?Lʱ) 薶:y.oF`+1y4CpSzٔXpz[3\;Ư@ܱ~Əm!nЮ W.?uFyM# 1. M κʍ<"3CÌ\ir ?wL̺og ^e/Hk! i7^H>hbM`۳<祽o?sF񪈣seݗDP ch䳢{w\jhm@KrL܇}ߙgf [}޾<6ucϜj/HqP pM{{=dDhe4شp,8v ȷ(Ց[% o70nG2~,wP71z1Z_ц>)c;ɫ|p0Ck"wN@M~[By }uĄsQnqq:M H?ꘝn-j>K.cgMWo;GX56w739i^.CZkgx-pe2t~<:~A} D}j32Bav_/.kXkqoߨOk6{a s]##_մM/q9W'LC%ἱWeGNo oS-Ϝ/P:#N?,1 ޤSWjs^qe@d_}{T6񅜁W[y7}]̦v?Ϯ`?[ؚ4t ,,uݑ}./.Z?~9\^^ǻɓYHNiy@+f+&%TJF^mcLo楲 #}6)6Wdٳ3>:N廑=E[z[㻌ݾU䚾`09}|:O>Q6pY rژ{*͉)\Q_Kͫ܀gc1{82_2>_Fv %ÛfXv7$[N U˞-+ [hNhw놆\3,tu\_Ѭ{Qr+|,JǸo˓FcTPNxla k&T}~'&\–ԷM*@f>r:W'8ؙ=Ǚ^4N ÑG(#T*+eWRQU\p2Cn&T"ver,9*)F!gp7bkl9us0{<kl3ۈ-t?mw?!+˷e{vu9iz>\O x􂰷\\Mq]b g~?3V6[? n85'O~j}z76^/Ye>!ZԆ80@^ld^F2ݑ~BLk|4ڲj7y>5dV $4@Fyl-歷~V[~Sjq_LNZLIsc~B}]HU{VЬµ Zݵ}l<ؐ\kη55Ը?k>-syL^_G oN/ΕS{ !}8[*y`Ms_5}e#5nK_e\E6&K24)&ƮݷjuI??wyz=)?G?]]+ޱ?rPTo痼Xu@ AUIk6'4QdTXy2c^py|1>Ҩ8czN1S9XU3)xX U;n֢ybJ|Oz@@F >]axTbVkP >žE/*.yԆ2R\I܁4\X6ϱ&1Qs)fX!o̵[B>gÎŅ ĺS&);-˙9˛G~d5 `j(Fi )ꃳ-Xٶa|ssi+6s+K![ur^^ KΡ]Kȫ˵> r`.ƆϺSkv;/)L㌬5yĞ#I pYE3ԖXl[V-kP_5ӟvivGFFF-?{gO6߷۵g2mfo2zq¨Σnς]I+:Bƛ z$aP;G7s_2~{hjGpԞ+o4tݲ~U+?R Х[UO~kK> `fgX6ÂbG~k;i=zo?=8|&rհo>4ꧫr|`\{6rVD?uw4]C{M4*?"9v~[O#jpJ%WAA/qSk_gW {t>R:G:c  <'Oƍ:i\1}vf@ve2|kk. ^oW_/k;*䎽ߐk`}48/>j&Q,oUs$׫g|NrƣR gPA>PT]}ø 'i }3-`q4Դ稣.T$Ԝ`~E0\æןВ޻']]ԺJ5kq6[qFM8 sR5Y`,זl1lZgGρn\?Bq@_Nd}llG/'33rWm=qwVԠ߼xѸ--fjA/_ۻ-ll4|`FƆ$u.32qĞ|b…[ f󛮣F`cON^ >~93l< 8b䚮$MN s5^@{c'8%"H34MshK8 9#(w$voOLkz/M[]T*QQܶ"5p{#Y }uNHkk_nDݠNL롭n/Xay|H|qTu>th:l3`Ƅs&{\|?x` ou {F+t~"W䵺y9?ްM_Ӡ1ӖiǃgQڜ$#/^bN<r1[]?zC#[!&}W!և䵦U皤TM鄇kyM MM2.Ċ^Ss:Σ,LjqBM@)KdO,&Άj0% #e5>]@r1k1ᠹV73|q[GɧS,oGtƐ?+<|y#N-/pT63YS}5P9IpqÔ8SߪmXHò̕L'\j3OcRj>Ǐ3ZZZ'k27 R|&1Xx֛ Q';8@w޾>W,/s]w^qMzQ'3^y\ %M?^wň-"F=o>z~ ugA!/ω>u9?x,UǫzvKn6I5S }p:\W:YsO f{_5. -㝆 GHhG]Ǫ,OYlG3:0){KJ^\>,K!ҡxmsG7gP{`.kB<9qL{mO1oX4֣"uuםi8 V_%kf哋2T–tUmܒJOHg߻Ҙ <@;嚝v5? wj$=&9٭ٵC"b9K1+,ɩ~^Z)!kPAg2v}^ϧZӂLM^ƾgpbyv66uMcGֈww_e~ z=pgpb'5|ɯ6%dMX<GGj>|26s5{o+ o>z}~K~zal?H׍{m[%ffbk?E< s;/3?GrL7'X?>~dlhωLo3|3}y ^!d=2b7}ON`鳓RТyXG\%9$h lX,>k ݨ_٣8W 1<|DhAbN|)kwפ1am|=N2}ˠ?^UVW#䆤ÇuB?4׭ \c/W D]!Dc@pxa7WxL*m<O酢(+BG$>w<;ow kD_zrF=%m:;Dz}oK c&uIS'9{]=+5cPQkШ?Uqvŵ c$'Qs.l=ڗ wOf_Zߖ )lfL?yv3|M+P::Y4 xGo[-zz^3\y'vptlzC>?)7llc=7pjK:YYLvvޏ|Sz{ 󏘂J'Q2xEͫE5CN:(C)Xƴ,n=fGuu͒m))ZWĸ1oӵtfu!Dk}\@(zpNtV|`W{s/+ gC_/jۊK\b'ѲLT#׹wɸ:[1\47> \Ցٺn5{;yü`ܨĜ>$U;RÍ'F~hȻ8O-v}O6Զe~RzZz46˚ͣmytYwu'$Z ΗA灷O)z-Id,5+Sϼ dh'rm8D@C)k-Xr+cF&p<b =ޑF/IMij͒kL禹y5%7PexGu-Bz`jvp=E[405Rzu0/8 w,`I=!>}7߰HfZ7-ɣGI}˫ %y])uD7Q>$?K?`.l~R]#Ǐݦ/zA2//9}'W}$#^3 ?W‚}fo N/7>0.{]ym`u9Mω!}T{ =yynܱvj[zZʗe{m=۲V3h5vd&M}4%]OXlQց` x*Ǟ}1p %>nWr}saS yWW+ץ> Xi:8£e[z 'kJ6 ea!`xhQO*ێǼk!.y4s[Wcatpl5j"grfNr68S g7/z݉ܺx,F`GNUxOO6 9+~[_bU:lل؏s+,dpH^mU1u+ֹuy簴M–E{1Ovǘ SUUOoG05'r4%#2k9Nٖm4p-f7mXҞlҟcbxzt(y8I̹ԯKJw(Fj98@mk&Vߠx.]#oK5+?-srXk\GY|~{u"}# :?$K['Q4:t]kkơ]Y^c5~heWW\]_'دž xE|_|i0ԹC/9WG>?i@=K}}7uܿgC386|>8w۾> =1] :I;91_Sr 7`bﱾc[ a5`eFL1jb۱"!W"0.{}wR0~}뻲;+C!ܐvϫ~c0_ R#Pp\"vxdF]ibnc\}kZ~sԾ yOhWx|ξ\0 &a>sv1j@Bg8أl=Ud3ϤzjEZ;yY{]ǵ^|*x  uU D.NŞy[_k1j#ݣM\;㌄#W6ZLaFFGGFF1˜xv`V\ک:nUɋ5{v^C,1X n^=)?Q۩*$cQeT-ɐHEu`?0;f;@'jkcIb tdgcO1,-a-?R9p"1Q.c;0kgmɅҪ\Grܹ%wINM(FA| sˬ\7zWTT_etx8c\tV-> <ػuݘ7j gv}Zķ'.{D^_ypUg3;rri!?߭1)11{&-eXkTXb-kzw~{3VoD):#R'wԶSǯȆu<=\]t&G/^]燚7u[LX;55tVtG?8,_ZuOu=C?zwR'OjnbtϞIoԮ~e])`ޟw7dzΠ~3]噏sQ&ۂߨ6c4vt>ac܎Gl<5h[N :5I&=8ۊے6}+]&ױxsy0/7e>Ϊv'HR%QϦukۉ=gO,x1TZ||7cB`}MbN4~ضè t~aI`Zm{iin{OPqсfcx ]I~}cQuʽ]TgD.#0=-=&cW}yq,뎵eY:dVnTߐB)YY^W ܤw@nqͼPZ'w23Kݤ3[K} rttD]9*/&I\bq_<W%Ube=jT}ǟ}}E6tvfp_$cߑKMt_&Ջƛ{e19E]"K<܂ DwnSgqU|ueH~vb{m. #Ω)XPk>?w z c]\ ⒌=\\nK}dC&&᫓ۦ8Dה:7nؽņR|@IylzKkUh 3qtXY pu? lcgiBz[}}b?Or`hQ󈜴r_D.DL*Q7ll>gO]![ }n/-l}c|KzZng^>hߓ&fF3M/!yJ,8dd7e;0_Q;X%D|}ޅ!f>.D9o;-g2:(ƌG7H)g I~n*ٽbͼ8dqOl2^[Gd:R;}˝ZBuFcO1N1S=+(S\P&kky&=Aӱ1~ 罞YWzY^?~v$NH_qZp`3Ys Ek{&@_^xWU3K,_ut$螁aefì^~7/={fPBW̬ބȘ眨T]]q+23;f۞߯GDr*zTNORI,.xZݕh4,.ߗ$Hk2<wOȹs_czcvI&;ZR*mr޳cwQw29mkJzފ~J[۔?_cRzmoޫ(cmW^gR۴-?k}Y]}O\2^Y_PّTU==kҾZ ??>999^ܓg4п9}S;VE]Qzϳ}C3-r;#JE. ʫCC2*''ڝuȩW2k[ZD_~.ohTs/sQIbxcU.7<~ږLFr9^A۳zt$ E 33wܑ~HzAmg&&2/WWoݒ[3׮ɏ˾ޫmhK&%^98Ϟ>1ɔ}qN4'-z}_mv딈M;j~蟖HDcQ)dіl?ޖj*e3qsZg/Ү$ ƣ3#CrZ;u{>>?RiTM9%|]cK[ͯжx,1IigY>kRV~TNdyPT4F103XbIjlX,ń]|G"Q;\ܓ]y~C2e>ZebؿHUkRWo[?I~cJn=EJS2UAjێtvecH_>7U۳환ϼx^cVޓu9+%-mq7y*OFj%$ӛ1˚AKL1fWJ t^?+{u=hߖu,^R}#XS\nDFF^i}#-Ŵ!߱ʊyRiMPǸ]PXswRiٸA:=eΡёH0Z=r(`u4cT*vu X7kct}T>=ҿ_NJ*(wR&dR28ssz#=}mozTsiVy]XÊz9 }M1gjAne"|N>3etbs;8yëؿ*/:8(a5b]u3I,k^#FdƵ "H+ Y眿rsEҳ#|S86ce_?u fg+.{\.so^ǪϮk>uMO5>#$+Wtfk3/?:4xuNuaIn|Nڃp>276ܡDx{q9]gr.#mH*m6RӦ>^A_k3nv;/wp!93g]5C9H\a6sVx߭O[6ph4fj'&TmO*{l ]尛Wb2>ȻL]Edq.Δd{ W#b}/3Xw[4R鄜?9/k:%TmQE[O{0Oɷ%]]=.UswD5)9+z.+OZcWtZ}b;81|K͞bwr6ma3<~~P9Di0}]n;m9XЮHۀzrn91CE`B9vM7e3J3=][=#(]ޕ#:u;+ޜ|z^gP]AGeoxخ=:F:~ \)ǏehTOM_?twufc{Eo |ֱm쵚x{ֻU<͛xnE=/<5sy/eNy1zczQ'owLZz\ :y*s >M[R<(ʉ662 y5o=/CCnL#Ξc/blg`~76 x2uaA^׵-W{7%]~&sҡ2]`XE6?ܾøʞq~.ot>CxARA 2dN^:5#wim~C}O3W8BUtd#}~dWVL?|qfFfU/ 'WC\WY2oMW;m?Of}kd!6zmgmuW`ҞnJD~-; ;&#lgUj>`:9ɛ\=m2b-(?VdL'd&2#hʈrǠ'5dma3?,z~!/0%tm$dmmSlmOk58kWt lGGT s^FG Jh_tX̮r}&=`h`r-ߓ9 ~*8# =uى,5&婬4ѻu;;vtpխ~ܿu7um .1.:}z;\F/GFlpyb b>}k;r׍T*z GC.mD;Aޮ'[U6;ACH$f@b{X^O11I󗠢b8 X ϶'K]q^\ͨ|ɿ[Xc˯:HWOC|̭a;'{ u}j žy;\&}I1A[*^`ۘLmm`ӑNM zc]>c6Spٯ\[O((V΄RHR!1/{` rPؖE2YT=3f~Vm2Z4N}NIps]/pI|ף5oB.ZkʭYH\d{f?2s&b2qv%(ooݼpP `\Z NѺ*iRmtdTR{{kvMȄf`qqY9@M }=`vQq X.l!wp:>r5,qPgze_?auػ5}Q{g$#aqt}O& w*IRfzbwovvrð.xN<?N> A/ 6oH `NLUW>3qڷkkk֧ m 4mpe;O<x>00`z_sa3۞}v=?SՅ6T usrx_ .\>Fg!pӽ&c_&>!3l>]CDtvlibK,?K^eN`ikװe>VFҮ<}ߏΊ= `qf:26sUlxaCbxC_|ߥ e4+=CtٶS1طØ:gOr~[rvN?~]l8^3Ɩߢ!je~d>Kl45﫡O _,c7XuI`$)δTdq*SB?7}m>LLqTlJ-~q^t*լí=Yxt3'zb;zW.ȥKl89?z.7e\O#Mf';_L>wT?cQgxCᴋOHwlvlv\9φ!Y?dCe2A罤zkoUnKa*-s?4'7m:F,sxv2qWJ%N'Wz\ `Go ;sHnրQ:(Ixi#>V|ASKb=azrfD79|!/?91i2(l;qTA\wwC"7_1ȇ>U(o@è:dW ) >'ڦU;!kSc=1! @пBp " (coGzrXCǘ`;(xy]TǃW r2ޟ}Nlþ3R|tOw0,:qܗEg~01a ep,xibSlWd:-:/2Y-s@rٜ?bRh'<}Z7?q<6hpsvo}k &,s\c;ZZ+-wd^b v`AǠCL-=uuH]#*Zm]C㷰Zr|].^ʄZ-~*v tX{]w7.~4x.sŬSMyMޓqy|'fy]qup{L?wmӷ,}]g瀌~mr]4q 7w 6rb1߉/{a:vt鞷MzΩ}/2jH68H< jfG&E՟n&ggbS}E`@Wq9G/}0[7~[GTo<7miXOlI0X@WצO{?8xON|opxF': 7 ~x뚞D81l!>fKSs b |k{EeC0?/`rC3:79L<^^_<%<8l{π=mV39\{=S/xsLBv@c뼂=9=|M]]u6;Qyl goo>v9;|)ns˺7V̆/M^l`|#(aXּORv6uIߕk%9> ޣS"wK^d=10`pNГjO`^5 v0 ^AV1/;Kb\tv;b=4TֈCe\A%msL' UrvI228#{šֶV{ 6otsyƚ<ƛqg#ZX55{ r.'dcFr]>`J(]Vp9F>s(೏B7BA!2>e&Q]of=kZfi=|}^.YՑCD=_C!9qEkxQx9tg-[f?"[5ܬ;OCuM$SSoݽg{k93,Okrc:vf@M}NΩ1M1Os͉_;w~מʕㆴL;B"Ѧ޷rfo'.ۺ=3*3~ͮwOZb4Zch3m`NaW]mZ9E }n@{Ktt̘oɓoy77/=Iu ݱ? MɈ!W eN]6 k3xo8lʩ-rIu5[#CVZ]z l̜5pgU6WL._}6 6?P9*IT|m 7msU 8`/P]8a?x=ĸ@x@s-kz7ɇ#~n=-8Ck g#!7;L }v% 8KN1',g bK?ZcG{ͱXSs[lA7֖b'7t|?b&\y5cS@>!\#i><Wޜc=vyZ4@͌}òƶ /e]fc>U/:-0Kd&;#CCf 0pLC0=s|O36NZR]tGn7*.wfe?qms>ז+2q=?C^@*׶Ð)y[yg*oM6 ,"luc!2++қ핸>b d/_+':U.<(BnF-iY q,Sg2Vcc:tt ݔ=vrػ9ؾ={#X'fWcч3pك睞?5XDp};7u֎3_j,jѮLfu%6jYAg<3P0 @@P9v];88xxV߂S2>e]wodL_òqZ؏k`u<,8y9~ݻ?<8(Ӫ-ͣeE!~Zy;1INa ? !_cG8EbB$Fl0br^Bb/ˠr03b3nsRm^Vyos< rv~ _MK/ӈD''3#mm/pf-!(9<`oyTAG@GbH 88xfgyGldqRf#Wd`HtҸX=+=}}tw99>^!Ѭ4 E\XAt}CL ۚP*UޕwO?]V>|)܈жP_L@qPbq\#E]` *JrY.VFBs9:n<6o N:sxZQ3얊,[yJv8gX5Q {+6q>wuZL|*n&SSP>gqd>r88. bQ#X^VWǏϬdӱYD`6XeC^eq>/_&:kп]w,kS%1Q\_@/\ۇASy,y5 KGkzȘ=O۶m{gXByxr88-}H>-s^q:Ͽ+|~s 럅o$=1i]rp|] .o¾'y8Ggc?<8%75s|/WR_iʥK?#CC׬>mvyu_ʭ[e0{_*`3//Ɋ ."'x!_@ݽ[S9k10pچ퀚Qo;/ɪ\m|l?ipKӏzdbJujji-_?~/to2ak &\BG |mr{Q~_]WN0k1oOæL} >om%V8މKߧG Y\8wWZ\]FS*p9SSq^ܖ';w' Ӧ<W_b @I`1̔gk̔8f>l7d2::vzr"0ݭV=oZ6`/5ib{\0 v4 L-/|]GڦtCM8︐OߟzB.#N<Ř e1/S F~NJzMfc喏(<y}@*SGi.uyBlw r,k{b~|z$?ŁtLwY~i#\k $/ɜx{9xz CCr-~MfGfřD^F:|w 9FϏ85g}ro8Rijs1.o.lt3r宼~cvc/}E%a`? TEn_I 4b/vr[[ Sog19";Wg9L]lza+;  c5v%G'~N:}T*Q/th<9ya(>vq0KS>pl xbcOMP?z橱r|lc;B0Ynz ?+==SHZb;?Kb?:]޶m_8@r#f[ .+,,stdvOYE o'&KY>?9uy6u>";w[AE|p5}V_9w*wN|59ӝfs< w'fjĬ>{.N8=q .b2ME0La".dS~.8/:q.l͖Cɽ'Co 9ߴ['6yLɸ|1ǟmw!kac.SSS)B Thc"` YolU}ńmHw)3%C<9yz{1XDmXa~}ޒ[ gax_s;lhR u.oh yrcWup>WZC&Gbwzei*" Y~;1q??bNE=\<^$3c36/` 3 BݪPS{^o|BG~dc~fK8W 8[l|'`r?<9IwuNmU'\l-FB9G|j Dc,1r>|j:;~`{+>}r[_ܕoN2oXT2#<uy>=;~L?-LٹީcLJC6|H>fK@s?_ ^x9+H?jWX?S~B!|ClVcE迤>Ƽv;Syoɻm)Ee `35/=?@"~;kN'WS:v/+:k5w"fy D#r1+'>Elm~`'Q ֭[fprpoٿdnnlV|6lUw|[*7畇6jиJӿή?~bqb9 #<4!/Tڷmt\.o ;;t>v [gӾ}ﳹ`?]8E\M4?x>!gp$'E뚜6Wy> ;[;g%BNLaF264f+~D1.뇹EĠ]9gsM!rO/*w ӆ? VmS$dP9ô@N^X26XE@&Ǽ;D>PH-rnbB..?mX1%ƉGKF,)š NJ2-Ֆ7oeթkz/yegl>>^4C'N].~iW6;eH|uq=b`b7[z>D}ʰz:1𝫥7lc7z{ŋKXS_sk~;|>L؜!?u|ptl Сs|l`o|~'|5o9=~"m Đ ;+acgg:ngm7o**H^U=~`_9ܼ++w*#O,H-yŧP1_ / O}A:[.qh  j|bbBZŗ{ag} / vq!kkžD,()~L~ft$BIXnތ+ CLX6L~A7p &Ŋ?mx| _ё7c:|R 7ܹs~b@?lK(|/0xǍ7j{X'&!k ='q{FY)_ swlVt~sȢ~}֛{f=j 9?mkw#-L6Kcswnm!ǝIJ9\_d/TFҙ ~ zk1v-z{,nCG>y剡G%;Y>W ]~];.}7-fǠXVmY_۩j8`LyΊ .]T~9`pC6/NͅC{JbW,n7 vq afgQhS[۬6V ~bȳDɓw$k2ўSg~VUouxgưgbk)yAz쀯TN;@^06G# z<:0z 5s}B5L>?]AGgy{D}./Cqbmgr 88Q͉aMOz~ Ea5Z+ߋˆN/ZCk׾CVgQb!ujYMArSnfn b{%roEZ\flbAFG1,7pvbйSBr _+?~a }d=-?t6/ V[-fn^Ⱦ=Ky9>}rćJ}!t]`#lkҚeesY>¾3ؘx~E z#cmǖpꏏz=-o}z`A,7 HIdUg`>~~s3 B-\B~3/tH_w{0.K C`6x`.0'8o1Vw\vĔ,s7S t{Wjٸz:zGM3S7@]Z&JwΓ8SȧEѷ)u`.rY,{ٳȻC}?-3X,M{E:lǧÏb9W#pw.~p|j―`v}rh ~JՠCǏOp5x3pl +t<]#,OCރo%j>n*+Rlns9]'VW ?1o)KZ~80;bhbz$1 *Oϓׇk{i)>P 8tf D^DpM9pۿyM-#Ӧ;O 00s=YÞf[||>na\W>/V=Gْ}~AE#L~˦mlEǺ`w@<'SKڸ!S)^y' \nԽ,/MY BoΆ捬Ƨs>}8ga@?e1?'fOoJl< ']P mSG,= z3W&4y5)cmΆmyLZܞB y^jk|p&t0Gڜ{j{f!/s.w0Iyc&' =9n|N0y)螰؜e)x2`y `=`L~P\7]˗cAXNiOvZ\M<5~H7XYG8|Guwf9~=QoOr ]NP9X\Dmr;%YL }Or5o/)鏕 wĬt//uͮɍ^db5xz5qp迮Nj@p(ܞ9O?2Iutc_lXs湚n/q rOr2;#X1:E?2cnWLf)V~^[a;:M|SĢRq< %.|EKdC&ڒ?vʾ|:gzZǽjgz#fua^r̵{VbᘹW}?Ӧv֘zdx0>{ 5O6hSmmgoe<}=om̌?Sh64Ҿ|Ul"!v9 ':ǂOk+CC^;r/UOXlOf>uyi@#~gcs'ro(l /]"omO#[`.vl/7qd4@? Xs`n 4B|_2=`D\"Bq-+XTqܹS6s&W w 5 WB͝}AGr DL&pS"kɧrbcr>Dy;^-,w;Sqc!dG׭ '`{ <-ne4&mBcW~cE}\Nj=]#=86ئ >|̉O1*.cfmXbk|&D#>c-ޖ5 5'K@-H8s CmMŶgrw^$ӛl_r*;E,21.}=}ot>Pc[}1T ʺ.#__gΝO~'{WpZ27iK8M]INLrTg Xx|qjF#Xf 7p9y/3* ѣ/^_R:%n?agpC!,&Q az=AwD6-bp9n?Bi6}-O.c|ijT~kL-\ASLkG' yG !o]!GVǏ-O?tI'ۦߋۧ< 'tʐK;L҉C7ToL$.CkzX>gnvFAf̾2LN31cAJ 9IϯõBA]M8 k="'{NqG֫XR\ƎS#2t;I ?co<5e9w9]SAj~C!gچӾ+1ǻdž1+P9ьga/l\>9wNO\_힍o\ B_ux` elkd :}TuG{ 5/9xAKeoLܕ2Cl8j] ^FsK>^5_/9@pp/Z)6ڐ~P/׳3VI%J"0X}g1-`-uwHvKܝg=.q3 st%G[@xb<Ϟm W\Z85Xޠ >s[Fv8@> z:ܭTHB9V|]n^PY8:M=Շ:| ,7bh5ŧ:AMal?-սol˂翡`;dN {^j[c+_=OO)|?iz@kU+2tkݶAb!L~ g=j__ӟ+W]'k׾_ڇ.|V&'Y.kp{TzG SѝKgf#Gw`MxXoa;^nmΟI]wu*v9m#z/8ځ i\Sgw]p5"cpO̖Xŕ,O2Ƈ,gq?b]1] IGF(#tGUsy {n|c#~>|Ľ6񹓻׳:yL̀}G#{郞>ł-_{Z>+/yAUnޮ_[3%_Ot)I]P{!ͣc~=r{G'A|2~u;XX_*O!؏@?xߨjΈx5R0|~wW e//js , uϏ9 ~ &''_A=296!\/  &g8@btPwyX %%6(贺C-wx@a8<\X6dzG^K\Ga?4pP @Yg؎# m}9M|9`yF5 &8g0ڳ||(hߗ{56 X|k"+"Ӆi2|Efg%\vl6pD8@ͼc?Nf9 !n fDC?GN-wZ2<q57\tQ5SS7no*5,l657Bl'rfin9;muiO[nXm-Q;(oJTlFR:sN }< jtm5n@߽{_s;a`- z=OܞU<~*g^l൫7{?b/ϐާ1B͛rߐH~A&Tkd^|S=W<^z>Թ =&WtwyKxM5F|{}=9`K=33xuu֍u~[$}}X1uB-5l{z8>=E|Sl͞Ȳ$: 5(Z.o9ޛ~A1( <าozB <OlOOo47.o2N50>O|U#%>ƾE7p'kA ?,=Lϧ ѭ>ٞ>yL!'Yc˽%D]{a5!&xٵs^o< p @7מ~˛"jI!j3Lz|89#z`1RX3F۱%}sUyW~6#!~>qγD"/8ǚ5κ |!/q-=ߕܞ>.{rau+=hRovf0CKQ(تX,]6k'p;^50c\wo؇ƥcmY~Y.Myu68@<^`\cNU5>?00yҔ7 {ti߬%oȥ%˭d sN&RE]wCڨEȿc{ǯʿ?UlK'Oio i5}]_T\Mw?5b l./e9{՘rו\EL'FjG#l~PwwInM=C.^YQ yikkކ|yCL{ݧ ?!T+ |po2:alz{Ⱍ~7WWm"ze{s"OXϿ3~/~P#hx?tںˇ?_eCD}2v3?n=%>ou;K잏C~Ƙ)`p!r|o r'qbi7z^n1Vk{ؿA; '+z_IspOEΙ!nYi5 jVp6m/𓐧gr'}"?8k (\odC_;؛A8D5m`r{=5t1W!w 6clLFF<9 7n4ځ yAV=~6>6A|x>d^ :~OzW[E1qoW{ܼYU3b9c~oAM 9a{gN=.oZ{ȄzE5؊'.F >G,ͶxkX+|!~@!D.c*g>s6A%'`0( n?>7b|^a@h\7jP Vw* 1Y[K_T9+{r)lAOx x@lyTbxJd} t#nn'* z;;Ąa8:\=' {` ?Q;q <:&0A͞ۓkt#ٓ ҒCǎo{xI[CoOuc6=moS}߻gu3)!aX-c6ru 9O3 l {z~6Y}aG(<*vo)3oUPK0mڃY3Y"p#%ݢVo>˹`@|>Ÿ^\|El ~T;9Vaߠvm`Cr,7i~5Z'tz\l=|7{4-lpW$`]-ߔ؆tNC,@곺=ڽ_k>o#8WÚ ᨖu[Z^ ,Xu^fP{HIU>wb`t*XۭTf777rECh+!wC@~Ǟ?@g5( y q-Ʌ2@gr:| !ʁ\ܕW+KDcaށq±!O|@[}@Ρ=luӱ^x(|]Zy˗cTl{ttv>k6 >E }Ѿ\{{G. ]K#]i;,Yr_ք1-%-SCK3'#ox7 urK|`MRvw0 3:^U'&5N>+5bQɉ9wX'\Ymo8 M#kSh{ΐˤb<t|,"$c-Lu?48 mc K>j}q{l!ű=㢺 8 {=Ot/6etݯTm17RyL|rB⹸Fmotw[nP :Pz<{daR^W_Uω3vmfdj7_BUr5ݣ{ndҍ7 +lh>e9L< 6F !_]]SN-Օk{ :+~-K=y=,cՏ 5~}\ =HǙ>f{oUM8Bp@,XM9v_ W6p%YH1M[e\, a3F5>8z]݆89jv.-zR|T!w 9|>6Z\\[*Φ{&ғ98u߉񀒏jU3PX>R531d }a h3nu^e_e䅋216nVc`~u~:b ʌ} 35ΚXbkvp-|oR LNt,N/vq.@~{3@y>b{ywuLs27] bS衦c;9n/W'ا(,YXl@>s߰Oz^ OPr]?k5[Z[ɘy?6}<?랯7v{:/q>{}~zvtXt.Aiԇq=g6| P%S1;PmZPY{~?Po[5v}St^= 5A`>2a°́- :8ب@[ q1S%5&ZI|f55n+܊H2ڐTJك_wj<>VV,&s9ym36$ER\%B6KaF@i 4@Q/6E&HQm4E[8uJeɲJBZHe;9+vG8ss}ȑcG VcBߠ߈~駶 O˞Cg`oFf=mL +7])fy_ +I`oy`zݻVw[v<BG1{ٓi\ˮ&؄^/r/urÇO3W#/0fCE'.bތ_!<L>t y3p{L=5kSN? ;JMNc8->0 +_-ʍ69_-]ɓ~Cn A wb?28K|G_yk1?ڦ!1XSSr?Y2=gVӠKîGF%H|-/qn<53?>2i~~p4`>ù,'r{j`W%#6l6q:SfO7\!⻨7V>Ldչ/̧I>?1]RS}MYߟ{ܷN!b KJyZp_C-X`"eK 2UƂOCaf>C6.`8/OMɩ)Tx* ObοʚC2oS0]<(oa2_%cyG_3ryME{vzUO"r{'.<77k~N&+WV m#OS90!>Όw{?E,ak/:45kWo|+um؝˺jo,M9 |~g|}UFv^zr=22(~ֻ-qd9Y^YpK#sBȎ r5x'#!88dwqɪt ȼmq?/hsIm.dGvkEik3+,%X:@7Ju[ Û884q>cA]~Xp`g ~J.^߲hB~d2 _UauNHy8/O bn>v_hy Dhjf:܈(g>+y6ڔ9br(G}̅ l<>K m_rz&w볠㟇MKx=M<y${:62kdn >Ge7"[8tM.[7&p^q|lOA g-{-;hu^վo܁#s= yV[66ׇl_)'`Z!uyqD=}2r]|yr.ՠyl_*6?}=0ey2\oo7s976${?rSX@ 7?o9iaWpA'DM󎱥=A/ |MKU'amYt7ͳny{۲ctvt>uN xy1F |MGc ;EV G2mz͖Iu[8+ӷ嵱:ٱ(-%U6T./Y `G?bo/q_vĵՉsdOJ!W[juy5dB'9uݖ3~Am9%D^}A:1Dʑsycw0& _z5KKSr68%.S^_xl6M ̸epZ_"?k>\3@mǏ^ay1A^+ڵrKw(6ɟ=,:Sy ߂۱[#Θ-N}iߦ1M.pfM 9C{Ot\ݺ0.b8&s ~Ig4ԿϪ0{^Kk'U=k.'(mj5͎G28gi ]B?-Xs+zO߼iu`>914v ^B_^ß%kb/X'Jr~YfwVw\|B;u0f8`r{d͂O7n-b[ynj_b#.-ez@ Z0751n ^އ^BȰķ±oN`cO.^z>Mڷ+CڀCYP{Xi[39n<.͝<"m}~Lv~,6#=pZ^;'aqj}sgbFQ39UT&7u_oȖl'|9v*r;;6G?d,U]_L̹8r zEvބcǼe T՟RTxҏkbEs/p@4x1VS1!Wy,b V\~EjHmEj]s*oGln>g?=F}v?}844d{2:}}?22b}o ף7c?&}mmU=`WO T2w;z]bd3D&~ə>.o5?)ggb{ظx^_߫0> +u~g_x˖:NN^9j.G[Gլ WE 78/ uoRoC^mLYՑ'to-]W?tb;cO ={C9o5  ;Mp.=׎{?+/m!S}džyMpGL"0KZIq?|!*RWmN Vҿwi6l~~1Rﶾդ:U>~ˎe`G~_>pj *~QgqXĕ 20GoGz`h M)dqVgȐu@]d6װm>]C۱'\O~Ͼ5~؏}ázurڿUi@<+yq u5'l˕b9=NyNVew9~0\Ɂ#?#˃+rЂCr݇=O,΁);y9B7gyOVyntsd.9vlHmgE~#!cꦁDFf_ 1|3\=ߕy{f%޾U1b_P5~BX7muL?5R_ sp>9l8>_1Cuc&y^;3x k)Kkyߓ?qO0 6x@)5{M`&!שl1cAkѵtvfRΫpsÒ}gQ zs\xڟM?x^ٓrB/diD.P6.~ Lr_={D8 ]yez@ؗC'xL{~lǐ xO1/Α;27xRh/r6Rȡ`FMxx&/_zYz2?sN֍іÊ!U.?Kdr>>z@qw;/Ƚ~ eί5lr*aٰWαY.!qlp7/EOUBUgG:w.Y% g/.V> Jߣq I#dHd<k(ॢ!W9|\;rL4MHmSMM.ei[*kW'c\Ǣ"ۀ>2=WElU>pھC!o{jr t'쐢::ɬ *7d#!2T03E>%SϮyVet]֩>5n;prVS/Vo腘}ܠgyuuҗ-\9{k+ CQAɜۻG2)4ȑYv 0t1cp;-K  hsBbI:RNʅ.HpQUIcO>ay߶8?A ׈Z-~nԍ`\h,IOfT7tΥzbqMWȱ-\yYQ>qЍc-jE 734>>jX3go V]r_O?P}:7? ̶O/o۟Z| [+q| M~`--n+/,oX>Vwk"7Xddg^N,Abj{ΐCĨϞr+1,wOcȋg.657/7V:RfWJ3uCڕn36߳8RE`#O xq97 p>#, ~\wqڻfMj\[p:'溲*; +Qnջj̯KǧcLㄍᲞڍI~|sj4'˹}[;*yxu{z` gD",KZT^{e*nUeIMDeZ$$A {zzy3hAm~'?{sҥ%ɭ[d``@R.evvV&&&dllL2l۶MZ[[r%IӲ~fR(>~%LԔ]_YYuI{{,..J%I$v_&6ܐ~[˯KU咲<&|FvzBI˛{J>?$ⒶcTWg~}VNM7Sz^Ki]Z,- j[nR[ۥ}.ٵbq^Dbzf+pdvh}){N T>719}r9٘m uR(ɉM^˫[TWKmMLc>>c{G]īܜ$E﯋eXk N_Zq-'eRNNkUZ;LIE)itN667K[:332mii't W(6 j=תsM7_^mg{GȫU־[^\[+ZΊצ߹#MMrͶ>XU.x,kge|~^]W_/mZ.C=uh7z\ҿg7x3NqY4u'kMߕuO5\6m&Z7צZg+/꼌\(l3mܪhύraB6m͛յC:::l pYima=X3s/Mb]K}Nm3336/|YE_sJ3,g(dP7׹GndoX$qѽ\r$yi< ǯ'd{uwA[KzkѾ1fWߘ)m0钗oJQ ur|HdĪ5]%.18f_3FiƎȃ1^p{2+x 4匏ܷYi;kFYe :3ŪX,u/b=_U1罸اu zؠ撜:HlMǕ><+/({R zѹLt- o6u7i qn٩{Cu^퍍ң}/\|Xa]8N]@V3Z~Un`Y[Ӳni೶킮SZ?A]f/p:T>+Odi(7u:&7Qr>jѱOfFpe~ԂoI瘮~]׳~\seO^ ȜQעW74;a}ATJ돃H@*Ѓ^Kf|yZJeS;e}rsl!8(6KWK8.ܢ*ƦGxx0vp%;t ]./+}ؤ]f|ƍF#{1't B?cU*O1FZcIzE=-KT5ʺz]/UzoԦReD"۠'WKЧTz-{'u ZeݱΪB?zw&}s/}Mߨ93K9s}r9Oj;%nft}LɆ SfTn8<&p͜3Ό7sə{om{l|b]a~gzYWo_ӋeiL벊fjb^aݟ3>PWkdrgF ;_C1n$8h+X 9fsrXĻJ~ٷϭ[Z1yLpV0^ ޠs׶9{ɮemL/MvGo:ڶ<轛y-d~O$괽}nK6}T\9"ׯD֥ǎJV=2jc\͊)J+!]|""'ZyޤkM`R׮* ?c~5h_A0tnzT>mivImZ65϶n;s۠|Dy3sޢu ?#ˣWhѿ.cR۱^/#_үea>NẮ+ØG[xT:|0;/ wS'c F!h{fpIO㜮{ۡ | 􏱥ksE{%}_uk[mo{;.v#D`~1v"<{Ā/Mքt=%`jR52~{\/KdcG쇮UW+W۾٧F];N#C@?N7t>ٚ%h.]f\< :]۩{kzx-zЯvm5b.ם@g/ےKVNSP^: +&Yqxp>3QoufcA?1 ]Y_|Q?ٱc|_~KJ߶=׏ӾEvxm2y`CH^cD9 Tjg 7]3fn5(_'oE)/\LBU]}PTzLU*wzA VΎ՗ z@އl=?^7^=:{k'.GЦgמt.輗ρ.2rex~cqS >` 1z-P laӬAdv /@_n~ߦB߁>QY*~ΡCdDSce_w''//xѾlUZ]kZ>OKa1~Ɵ ҡ7t/T L_pPXM=}CN~١ج`OeLO fB~Y}zaˡ.QOS>%lRFAr|q9xklr9g-[_]'|x6~ WڲGbu(o䐫WfMԵKwVO')М.tXO- ~Ac?@nXGqgz-$R^r rJȦF 3u #X_wp1%%h X?;<gL]r]5`#X |{;6;p! mRdlL%AimUp#lܜ~ё;nk=GW۱{61k>{wl |:o,֐vw}d ; XxtctQ1/ɑdƐ@KY16U^JD'r2~oA?&'O,/ 5v|&!ϬuGwq/Ǧ<ƙRxcl᧨??Y;>~GikkyO$/:mJw>}آ=5jzm6͡4w{-o؎.ݑH@z}xR`zXLy[kaQbJΞgϲ6%+td{?eH7eAw.7d;ߐ#ћ{`IFCagxײΧunxl9['[t^&Pw WtyDMzRylcJ#y7BHuh?k_u}hy |4 o{X罽WFK|̞N*'zG6Vi~ܧXSbg#l3<A7Ns}%C=44:JK*h{#` / YBT|@žؐ]~YxLLJrAmOQ|oew/Y\cC %`Zd{*W_z7+e{kyJ큦:qߡ_lG>iρ |!0NK=t/`NݳY)RX*$rx,-;m->@"{蜳 Ǯ2 |)Lޏwpr۱]Mix%l x`Xg]Č:rf44nr 3㇕XYYV=n>شQ`rWο:oq\WZ,撷 G!㺜̼6!==ҟ闚z9gʷF7?dDReҿ({K?.ʲw[\ƴA:aיo>9ɔХE)lbn>lN}쾀 ~y찖7b_퉌/<8|}_v|{ ~(ה&躯کm?=ݨodfU |2Zuھ@ǔA9y%S19[=ŕWǮ˿)=g5'L] "aVgll>uz5]0/E9~HۭW*s{?Qv^y~l2 jvoAǀvna+u|>{#[ ^a!ƺ_Фv}nNy& G==K?k \]gi}"/"KRZ`wS0,\̷2GAcl=mGrE:M9AWuR7(]ww?GGtWHH-wɑ Gd=ҹSrK2S{+>x sЃJ{ zd p }^I幟:-GK)ҟ^m^0ϳ&Ro@Wv1Ӟ3gȹs2ޮFN[ZRQ&e륢Hɮfu#S_1qp_xcT`W=v~wŹ`HyW zꈅ5o |l%S^n3ojMX9;$Wu[h}R׺W Ar^l2td{/(f]Fڕ] ^ /2YA =s, 7S;B /疥X0Sq1wݙK,uj 4"bfr:76oM_0;:Yf{~&'pfiY^2g[m r`֓PfoZ| &//o`2٦k?Stx" 9;sǎ-;tܱcF/ތNNwCLGF5};xph8 e#Ӛ!lmj|Zp^ 2Hwϝ3JoV뜶G@ޏA˄`oc@Qy rk:諜YV:2ׯ}! 1& l+ _]frH^_l&Afc?:u2S|3A6Z KIk;'GHy`VB &^R^. k몂 |˜¾I=6W gtn^zz~tf*|H6l`{~q/ʿ^0zBxu] 3|-g-Va@. ͪߋp-;m8])r,Vk_n>2cpo:_<ܮ-*v6.ēKB7_u6K&0A@؃_bu%~g]@XЂ?Wdʩ)X\.eLgt /SAկ~Uq[);Xﱁ>&@3ϱ]ގr+-}ys1{g/N~y8{^ݎahgoU_9yMwcwGl=,, Ql`m1vr~[Z5U[_Y1J;XX]iӴY}{ >hc>|֬iݦlv 2oIꌎ~X?%bGV_d9+>+:F]{7#x2yzrс0A? t; mvztn%;:[_PQ<~ub!|MW#! Z1ޤk{W&'mz_>==,^zw"]A7ݷZ4^z{mr0rr -6o/~lh}~0 ƻe}^,eO'o-íR^j5R2=Fa&{Q^d4e3&5xLj.#vd|&|N\!݀TXE !^lǧĵY;rQYFjgLܖ-vF9JO|LڈbN9c.]v ʺ`~ּuX X;H3r-{Mk# uqjQJR )b:|x9i: G{kdg.G&= 6tR}=cqpႭic&s5ZAd| 8ϰ'ė$w ?O_llˌe%s<P/y h>P}駟gw)aٷI}vL|dFrmF[_z~fieen#C=r̨u9ui9"S1 Eީ [kw#kγAFݪc8o*c?cwmgnCoqjz!o{EyBś[Fn]`7Ydoh%[<) qb eQ-M`nM!ƦPMܽ;wfB W9$X66l. _:SZyek邮Fc "_?7;f2gC\otߴ5U?]OtO5q j ]~K}NK|Fft^'CUrfNT, 9ٷ<`2qX."ѫC )}ﱃ\X>T\]#>2OX3@{mU7u5r@e74:eDK$ei4noDd^P+3%/7H)h'-$ocv2=l+ќrP z-6WYWحLdRrg . z!No[ b1cf 39.uZذtQ1UǖXIūi@D1ܱ,ma 0_ȱstr^Vedrݤ$&WsYZ)Yi٩먳U: :܊H띢-!;N Wg%U5Rab*o>[0G3Fn'a/gݾºw>Y|L<{+X=_l~`?&6q/t<擹>* Rs=wnSpLvzXܢf}aXY!is.- |db1(bF C&_1O?,:N#ゥ}Tۉ,>dʆ_n7jÎC'96ֹ~eCPΎ:&-@g <- n_Co ÇPnbۊ 0kL_xDȧR[Q݁7c۩~ <@升1OX; X7 T}@ͬ ᓊ[ȳ5v\? ޴!5_m"%e`bTNO'\q-OLwKˇ?ayosrsq: @7=γކpU*yfC/îڅ>z*~+~+7_Ο.>(Zo.KA*?ov}5;eu{$ِ4|0_b^&z7FpnB_!kqTbDJΗ\+{ V E\YOła.bxĢQjL,w(FLɀwB,-ߓ:D$b[G1̩˨'֚=</`."z)w/1RoBb@ HէmޜY Ol.]O%j"aEM!]y~V^go{?x<~u={ M`C$/2;|g+_Eyc3x~0`--d=v;85E.~pixGq)H?1Œ\tlkv{tFyv! *G^>lU/,G `qyutuY\~MG:6!,}D"Ӧt㿷}7TaqIHfBL*Xe~!G z-z~úvh/1_߼i&@77 91s9+97`=Z:AZ?%y| `:ר?ǁ/7)x ƫ;!XM_7eN8+ۮk8x+LUxֱnlqs*MkC9y+Q6?%6>ݛo|TG>y'VmJy2ͱuu= @Vw[ Kϣuz/J4sxwe#C\^,~y>h}t=>#ENա&hd ޸*\眴< 8U>@1n,kl^N=XÎv td!m<\^9yy XΐC +BZͣ;2yKpN)X ( 妭-49ҁ߲DX9\iذ|?-%Y>G3!b^Y5Ue-E|']~YH_R-c)sqzQ*/t*-N.Z(0fP;&GjF%-fR4(c_G?2|FC~KFOo=Fz_^|0{][%2䩧??OgB:'`ya[kkXg?i(hлScrݶٙG.!^~uo}&ySF(IPN_x ʗk9y1`"ȁ<8\OuM:Fu\/^7x K%`1w(r\elML؟aC:ܠۏN,ؠYW_; Ѝ#ٮe`/&x$AmE]SgtϬ׽ɽ{@GL~cp>SqЙLX`:b] 6~;9Nk,줜7;%t6fQ(~M߄K|q*Zv_*:ov^l`8N'+f*+ԕ /ݷeqdg%PiKVT^2*SFHJZhZ;ؾ;V>)2C@f-zwa^|wƟ}><+<Ǹ~g{e679lܔ{d;1l~OI5&Q^mgӺte=fSX[׋$A޶jnIU*z>Ӕ1O,-:տW%$*/Ҹ {/S-J}( Delul?s!5 0߮'p;~ⱐ]O/~6hyx#+ρYʽsS&p mݷog|Y[uW>QݳKZ*܀Z'yo*5ϾA?ǮȢQ%;9}fԇx9~CllA׏:&p:1P|\Nt +*r ar%Oz32'`iiVn^mߵqS1hDqVo8||=x ƻZ/~BzDߝ>XX:y碜>(ƒ̯ͅ(JXW6K:&>yomS|“?y3=g]~w+Efq?`/x9tk.cSPK}a=G/d~::ioM|Q_rVٿoܰTCbz\nrp.ȩSCرMM7y}4xtݙ@1EGvLƀ`%t]ȎC7:&P^G3:NǕ|sZ oWYMxrz,/v [~v}`e!W+uJQ\4zN:n!ϳʱ Թ=kGG͟9rDş!'gc6t?:xdl#yDg0l`7<nwr<[t< 9*svpRs:r<Pr' G~ˠ?w/w|{c6~˭/xٯ|K r2Bu]>(1U4!yyIn'ȶIGe-7hjK/A ^E66B<bO t_w⨟#s<3 'r: -<su׽ xAo3\v CXJZy?+ʁ^AU(k&u$ϛ/`%/Ɣ4ojLs42Ҡ/716Im*i>Us{<ܒ&˛y}HpW <@ll -ԛC^@u8ul cgnWyXQ>vYytǎH5;u._'G iV\i3`J0>~MU{PRo1 <#=֝s}x圂eg>xduv[endҋwW'AZn1:z*MX!khQ֝_:+V2Dovv~_|P?.G>9}]g?2~+ x 85~g\sF7q6S xwrAG7Nw>mގξ}^A7 q?n fA7p_1<\6ǐ[׵kG:wO)A;wyGvΧ nCOO1Uе_C'b}p {@6g>XCga&SD?sUar8^xC+#+ nbX`Yk Я蠼2q .#iNOh3!zz{ztݖcǺAؓKàs/c! ep]*ueCw2pm;qޫgY+T-'Oe|ݸ;&bo`zbdC!Wg7'Z] ;н z'$ WyTO<˾OG'>'Y530f`\at:{B񵸦btqS2X5\s,A\R$~=~4r~~-ww_y9LCWA0^Yy|#֑{r/ԐC J, JE]`@ce|+z=ܰ:Nm`Cu>)A_׹{cqb;iȳ;7v!;X ľ;=0`lzDwkȋw[k$յ*ʯ;ӃV=]5_E-s2ȸ`C0 ~zbҋ?1OpJ<;ԕ`%`7CBG0k|l*Tyr= b9o=bGLf Ӫg,!& xzQ7hq>ρ0߇o(-l[vV'g&bzM$r^ضN|I@k`r{ߔOȿ9y_xx CU*{Upرwr/>7=>J}ž?7}ei@;ҼY&LLUȗ?4fr2=0m1- B[fδ$(P#JR!i2JAG;vK8C1ߕRݾLW;q cQ_3>x`fJzrA|@Z%cwBG~>~}4Mqi07L>Ooo?~y?Szh '?PXo|C.-amStw?H8e>qԾ}Zs8y˼KS_g|UJv\+ W ~x|mKGoZUZB~.0jC bN;SW9b#G> cL}o@K>ya\7>E6f=Үɠg0?Uhq67x~ƷEqOH%'aŕˡ}Ci;O[uS'b.ׇB\A9J>qC4ģ|&WE9!@zUޮ;HD2h˽0۱d~BW\u/=@"SNtkzCx> x|]h3[ߒޖi>&)='6l~l76F|@yxrlԶV(xnI!ppV75[*a*n4B]~C8c=?mt ̅|%GGg3?4B8YGwd[8oq 3\,6dyFEg ߕ}҉s{iY<"MG9[yXȟ`xITm.ؐ1ۂN slܷ>1C&5]z/=6guQ?sޠLl_&`?H@׵C ~ ^-AEmxocimTȇ еs]3(x cj٣cǔp&?;<,O霐+ Z TXFr{m6$,hhLeZyW|$?a}X 's0!0lp.n|~ݗX@K˲B/^=}{ku|{Pq|kZ[o{yE3:QYam>{^Wyn50h4: 9d_K)mij1Yr!C)01ooдtqoK<7AGFk:>H{huߝ0]3E}T~\(uI:%GIu $i(N0gFg^fBb~Ul5y 5庝&?sM, kvk*OcM@|;kUO7%7"n9SQx̮ѲeFSIisϜ}3^ٚ5rYyC<77?3AVoύO~Ga"t>\>n?\C?A*0?<==*qcO/'>V~GȦȳ[ _+ХsXg1HY tM k"OyCCHuzXpbʼn+/~Ƣ5~\G 25z x|eYCA?@ bقtyA6N|Be]Kו$Wğd|||.`0m7p_-yv\Lj:lGte)="6燮a<Ϸ]9ܤfWʳ%r+=5<2sLB;/*:F6PDkpa^fЈ4(M捇iV̬2'7vDOi 60:zN˸n11|@W䕳`FaA_t]90- MSp<p*v77\՜@GᙪW'|[pV|@</ɛ+V5gC66J)>B\@1|JJ~wkk3[+k|1ik۱g O?LOe!'F*}Q>-ΡO!)k|]Z6Θ:rރu!~b18v۴qOhZu|S{X`φXw;rXp9:wp@ ?9;wP ;A]?&xRyV>xSy rYAUt^볶H>ҳH׶hxA+Rt;I !9h 9XI,^M(ܴG~=Y+mqimײ<55uؼn](:nTW'tO[ſt_ݐ'lNY._M g5 ng|n7\hBA!D@|޼żx3܎ߖ q&  snHB:<ON:U?$~Aw}`,\|;a7|'cO;=}pk =|B1{cn=6ƛM9@K_<޵l)ș`r1߉; zW\'`Oc3fV wc!S37ȌVlNﯝ>m8~5R~z :osv 8[vq_ >~p&p~ xjϡ{65^׹'NH]qL9D/FyI˹A!9EB,59m!&S~樖եs?/uA?6\B&2OHQj)x؆u>~sRX`'2_><vosxQLS_B,7cD\~Qg&W5rIȖK{.ig3Yoef@O1^ <It8{)#O~򓶷|Os{@'UWޮG;X^kyhyoky{s?K_gCo);0ЪϿl:ެԭg8߷:T[7&yt*x{@qiţ=Ò}cFvh muqy|+Ѝ X _h a%!yyޤ>m/{HyK)y|DJ{n;@o[@#3ExCMj=*esǚ7DeN?Y3U>(nIOMܨ! @okG?w-vP1{;<ked|05t=5d|0x:sxw' pپOE9!L-~N&{sbq³<3 2Kzf/>< x^r}$tcjKtSDU>gݱEn-]w,/ŠCZlKn~^H8[~QZ>o<=ox+6.|!wy`tώ}/(ovA;&LXk횾PVtv(xYI?wPJn}|GX.^rߞ p&:_u>ɅA ȫ\1Ϯ1g|VZ0Ax\ v5|V~oк;#j~|\ȑJ9L/a2r{)e]fI&2l"~UUiE< zR^0k1 /2KwvvD)ӗV7.'NG}Ie|\cC)<  x{*m6QxJlw=Axo 9&Щ{*uY/=rqmCXoe*diO =uu\aY8pyÒ|4ʴJs]6쩲|< xݕ_ǁ\~ۂ-ft}ϱ<ן/k?__j.F9e0_y$n8~uO{1\My~~swH]w'o^>9#bIJney뙳9X.'vwǏs^/ML X@h<؇l {w<_T;8ϓO58Y^{Mzt9%zr u(ڧ;:߽xm!~8c9LpBȧ[b4,^yG8:޽:oꚣ ;~lPg@?.FR=P | yo)8(׍/979l}`@JZKuYR3G'.}^Ϥ<۠\O@| zt-jvJ6}^3={:$/ L "]~_vԩ?x =o5f2~+Xq<'S(龜P:ph+zyx˺T>0=! .^:u?w%spDHxAۇ%'! L[NA>眠1GF\ճ+ǩ D߸R$]OȡrG.'Bj,zF;(θNYl9*w ; \{|+lH%1Y9"ݱ$m3.> *^_1Grtnb + D*]by+-/闉5"09϶p ; G|u]zi2: XRnN9ˡpY ~c}GpoC#;FJ(_GOydWayo@>g,-w];w̦ȏc%QۆO.]2ŃJǡQw8Ã$`Ig?`w.3ʅ`_'(vSVh y:=l`uwD4zY݃@u|.ssn9S.ӿ-Jm'm]<|$h{}ѡi;#;l/ ^ߍV <P8gE;;9Y\Ħ#ʏU~ݣ3^Ա@GOgS)@1OD):&8qP?a8nȋi5OUF~5nlm?G>y aYtN}M>, n^WaѽLְ!c"?>y:z!I#O30*=WשyɜŌ*?ZFvdp̅eM6}[ߒo|gPr?я,<| _A==~f1{cؿ;;O:]{|#c_6FQȞ&y{ToL]œ]Uo^)LdҿL4l25ɱ rpf߉cYs'>,6V h?C>>|ҡ}{ygsĔy\p^q:؉)s$6jsCl> Z7 \g<7?9;ǹ4:>1!.RuDL㹅=_ ||8̍u<?z8+WdXt\G9W,-[BB{nr ]vh1 bK@w>|G3 rv_^T^,ub2fÝSMѱM& yj:p7XԕyGqLظx8=畞^պǕx C{g-m.Gc+uC~'Yz}m'kl/KAg56޷cs9zK>n/޿k\5Q)T$ѹ5+6:}^w/*OKJfGZ|1"C?9!Ӳ3lC;dN3|Ϩ#?-O\ϩe--Ew76clCMgw3ksN㹐C 뗩s |nLw9Wȿ` k|CX{_P0s<Ƕ !@~OBK?sw1"u OpjXi&|qNNWrN9뮵pTSzt5iYX@T_+%uvVrd3e%`?N`|AK{<쳆Ww<:aWb3aOd/<@8H[+eaYMc G 3{ w%k~j i_令rYLd6f G4xjw'?9.cCpf@93e-OH?>zPS|o!x9& ~}X& ~ y(OWGXgÊs6z\ UzJI^(x2oziקKsF-Q3wlI5hGdyDjy>}Ga(UC8Pw*9SkwI:qĤX,[|WW6oE<؟/u?xN ǣ#JPa)s=es=66zZGm]pZln]jʹsۘ\4gn̘o? }[T=<*9A`6v=k<6|4]{;1z+p-0~@or8`>׀pgzncYqn^G^:/Y8s xo귿2۾>9!i.g:pżB(VZ%JK.W7G_ j5ERbXv5djyaywȫjւǖ:x!W>)?+AKW0plƼ5ĺ̂ ̇| j7:wyEt0ArY'?ϵx|&߷xŐkU]8k팯L@kR.ȿ߮{ a-sf A|ys$y] ns>@qFa1Јz%7D5;:^[Q{}b2Wo~V\_tHK&`DB^ݴit*O&=3(,Ο͎:l1xj \Ckxu?=罡{V ^ {7zN K`x{gKC;{\yn|H8ϧ}*WHiKI?޴n~Aɡ736c5aċϜ_2z#/7,S[jxp9k \}$Mi C3;J^.2"Āu^{QclOPN1599:+b;v5jě"=&b|"?¼i`~} Vm}86@K-_&,޽E/ڝWՖ+vxYCB׺nJjǧ?=+{2m]/w]BE5Za3| :6 ύ~1~# c_/<?a`~beB;N=^h;\kp _=goŠ~G<]- kاRbb_\!/XGm YC=7D3`"h^; E3G˲9r\GbC~C1ZP;x܀ubkCϟaKʝbA{0s~߶w449pmIa;,i|qxMdduKWG=ӶW_ZwnOV׽f47cCg,)tvvBY]{ۂ*:]zl1h 9M?,Q̡zZ M?駟6\Yk JzߩdoSpn) v5=~z+}z<acqIJ[?Lnnxzז.gxO| _pk8ȵ9ϼD#æ?u眇sNs{{c–ZB҃/[?"/pĺua^vY73?^g ."|¸|M9#HH^)KM_sbl>5K%˹HmVgv@7%oeڜI+gg'e1#o2-}RmjR{_j`cN6??.{D.>SzbsqhςGWL0};|gs֦G$_ھx >;u 6=\Fۑ =7,9Q=Qo"'z}Oȳ \{=#GB#P?R'AL9zxb>똚C?uk=X-%B}'ϣO\6.\ =MK0q'=飿x^q㘎ure4ɍ2^9&O+ɟ9{[ 'oɑ_f@P7GE]]`_?P( f2o5/ (`ƁOxzSS_Z c7 )kVڔ–zU,da[$B~J_wkGx=] tˋ=ßʅ<'[iW=w;g~|x| ``F{wݞ䕁#??|۟8ř}(GoHL\ݼ|Z-dzBH_"8W`v8)KE9?]GD:'Di|gh0G}bfG}%^kk( {Obu\Tk/*vlZ- `զ-\\_&PV o- UR8؇X O%>sޞgO^31`>|ɱz6> 69ﱶ=uB9aNax.͘( Qp>zp)"O!yw"_<"тbnkzNjƅ[z[>,}HWNֽ\N&$9iW+QL m%['jc%o|'g2&Ur`&AYnOLY[oo<{l_#Rm'Wѓn>wl_׃=Ȅf[!~vM.A2[/jغwB@׍z:ŷ ŹOtH?;C uK?z] Oߎ[Bk~m9hw!MW:)upPV k4)h4=7ocr}<4GҒj3 V5 Gvyldr"(z [H3:ݺu[QN6Rn0.8lEB7eb񀎎yT=^Kho{n>z`}ߐ5 !?k 9= LZ-?}`,H GhĻmncY>~5$:Gw3ÓN[5m0c֫bœoFdH/w}MCxPVM!r @4$Bc\u}:KIkZ =;{ǰ>9%E*ZXk7แQH8[@'p@b}br7Oy](MgoV=k~"t'ʥ%p8e*>.MMM'~%⢜;wN>ZȈLLLXk)۷BѨuҏ2۞J$IKK;;7HmDz{~ iػ"Y!++))R*d5*UY/KUwTD+$s-c턛KemfV%u2%eeҰNɰx䔼%9V*喦v){;$/l6+z=a L}OI83z":uqz*t[kXPykѿC{Lh>/zN>HQUK%}hgG~tN}R+6y9υ{+=ڧ1ODӢϧQg$*'^cڧJ=~PVu=oWϜo}qI{_[v뵿!zuLh5J~UcQoضMz=ymZrsnڬqsڎ1GM-L1ac/MǑepP*8,&Пz +RАv߮s(~cA-5۹3HԞt&%SU#oDAR,,I(y}tۧUVtTJl6ʑF}R^^!xNO,;&uuz& {a=> %{GsEkw~ee^HQEH9swZ__~\Qs}3b,+8ﺞod˴>mi/2 gC7iÿ5%}&K:{sN{l/~ȣn<\O<.NB&]Zp5)KwK1\:}iZwVXeqr&),gҒ]J!s1 R+7vh"hlsr g'EKZKH_m]k.ܘQYԠZ%%kF=dt,eR^ND݀bFԢx.K{Mo컲Rߙ dxeeD eQWף)ik۫zLqumӹXmg2R[7ۖ`g{W z_WV+^GYUY-1~Ka9qKy`GھlfY08LjkkzoW9 >7;v3O8a~߶O900`ϸ`?,q֭vN:*g9vͭ/__|n^b-1r"^!żTV))W kiH5.,_Xyخ<7x^)U^.ټq~mҊgYD\;1./|e)<'ۥZkwζ9>^̇;rD{ީNFVq}`B\PWܯk\kT]{vw11s: ߴr6ϑߧ pΨ&sM^}$B*o6}~ Sz) oQ7vtFmG6oѱwA ~E;q'[ܧQn5}>7-/s\_܀g0^4轺ǗkұN]z_ Nq?kiب\F=ZϵWUƹ W/pDRRzI.{tV|* }L{+,4Dju34ǮJ{-{d^>ѹ|\}VmGMy}+p9}raFsBlOXH- >ԭWg=_egs%Y&3kXw F;X1`<pxC7ʐ;1]ff)W_gR1 :ko'c;ml-b<^Jjgs EC޺ק{m*7+`2ʸ?דѭ(|_q9'`8X{ye p9i mͨ~oOԩS/|{^ygٳ L<#Џ??'yGMMpvAE=X+ᶰawa j*%W2+RʨZQU)HsDy-a)NP}HQbQҗfHLʺR@a`dOBd9zeZȁX"EG_n >^ u9(hFaCzTTd|Fv޿:.m叓zYmw%vz8(678^8H[ƴMˏޭFum<öRmq shnxEI)g9{D"^(/dRإqynAvIITUKuSœam(4|/+ N.NJs*#Lou[Lη ۷eɷ+YInʊ6әh ~p=WVV@P(\EԷ剗euFZJ&`^%﹥t~·_U_%55[3\;vƾFM}߫lӱp5{W|;T}4+Mr($۪%Ulq+9f? $ [n28ϬUcR6ˊh:}MMl(zz*5iX68D"l(w oz8\vUߟ6.J۩kF*7K(r>w^fU,$VP,$x>z=qXg{^dGVqrG9b=Ž'-&chnö}YgϞ9#zzsL79>`bpӦ)' H ӛ\`3>Afq@_M|i:&ތJ|c+UV%'t<:[e66|}vNl#WGq}N6vRL?IDz LRs  _7Kz?u P=gѤ+^K-IRuG/\o%]Ѿ[9@*58*t-X~Wose}pA`B!krjiid=* o8h@;6]qgbo l#O++1=}P4~#r-׿^oyu}^YlCf0_\C e 2֜x$vqIM}<['><91Y_mf}M:-J7 nh6f lF25c\^+a8ݷa߉R!]W$y2#\핵RF6lus>J#sg\e:Iu4jv 9nqbt8w`.,ה>itUխ"^s1o/LOb6p>jrw}kzޗ.}ø~Q]ݩVܚW^h<,=OK:|s[Gݭcq |ſoK%x'3^K_zHOOCb^!-`6vڣ]>0q\\M6~  '7c~X{9mo{ko8 8 -~//pCxC7K~ZOd9z;}v(+I &zZJْk;%Up, בyQF*h~ō_GWm\{S΢H-v蝷KoetAl]z߮+` f}`U M%6Wбeo{.|E 4m/woS|Ga)?00 c/kt\=52"(/Wb!u>[o;+gGg1[,P鹁xLv*PY<|[ɲWx] &F5L\Mzh9qg\FBp.xW_/#:ƿ<:73wc? ПZ߇,9^cJuoG~WpK!4nǗ&_pI3i{lo=i5TZ9 HF`pMÁ ns 9Z\sO-'}ŀb5Əہa,Փϛ ͎=hgFrܻ,gSff2ϯ#8vx>`.xr>'L?sgTþ+ׯ_7olg"w?ޱᫀ&s#s- ) !>oU]a|Lh_ѩQ֩=rGp,,Jj LVX˯ŽVT)X2/!SEYmeV$Ci`?q3(_ ,ul{r jy*/ҝUVšv YT:s<0I2[ߛwp͉cw>>}=|k^Weߔ>3z߿-PD]'vL(ft._뵴'xO\! _ףO;;}> Lov辝z_2>Ax]iV u΢Lh._ƨţ:rz ns6gp_R.N`@YG´O8!_kW|c'ዀk9x^\K1dt^Y܄g3<9.Ybtl>*O*Mo;*q=p 3v<>Ba02i ְzd77}~yyl.=j̞k~ODŽ5N=!fZ9 @sج7>v2RoI:nmq 'LH1[4=8dbêOn]&ty0.ZY!S0NOA;S9>=|*oO^[$6Уލ=m9]9q6m~z9%WWb.m+HJЩ ~cY`z˪h0n={ڶ8 [;(\.7g &&^1~F["'ew$ٞRuq͸']p2c͉oY׏~lm\ vl\`u1 Lg~|9C;l?G׷9ُM~C?n~u]_a|򓟔O}S KӞ&CJǁ-V@upھ*(RRfuΘ?3?[vo|FۣRYWilߪ-URզڲΤ$'>sp2yyx܈tòcZ;V\-gYVV͎߬GG~/v`? ź% .ۦǣ} I?M']\>撳%Kz3:wH> ô:GP} wSw2&Wֿ~XBw-z߿R׸KqXl5q<`50W4!qqQ''8Ծ>XA %`7kv1zu^扛x& gsG}m9F7]g45*>o?x'TVX,l+oG16<`< ~𾮫θP /m{}WKy \OiM/ Q3(C ,g!&#}JրqW oP_kmΜQ%.ۓz#GU '8Dz2x|+_W.}.Ṭa'cc`lӱxtyx7sy/!n][_Wmy睲w^ߔ%]?K;-Vra>HK5$BQ­* Ǐ}WZVt=gmqڥ3K`{¶%m!Tn?0Bɐ [w^΄d?`;߷CtwG="9jMkli`f>K^w1{pf1ޟRMߺ <@zX\БoSؠǓK0Ѕ OKIL @106#ܟS9}XzX8tq7:{я.xS1VC: .Fb_o70=q~\ l&A{E7X1mk_v(>}jn0}d`ͺx I9},>`QGe 1L/ˣOJ"ʔ˹>蚚>)+fMs*F1a//Qc+ vG"MBL2i7).[s<ޤv66πKFL xOܡO8XxAu}Cere)9rpF,F ,AF&OǎېS8O+[pd$2B^vOr `'s,FIg?YyuUm[[sn py.f%^7_x<|#w)XkaЬZB{89\]\3C3l?mʶ "ݗהDjYVtdz5r0,;)|[-rO.MWW]6`X]UUg|yClvb "*llA`+bRO9@pxҪqq%Gr8b $WdxşY0v8e[xօMq6?~_g`2ߴ>pOtn8s6 [{@/t2߈9}N[n1lW~^a{n;mmd>[V\#xg{{ܕb\lp/cc~,;kTyq]4)6b8]N'SQSr;q:9f Lr*VNl 3+Eޕ{ z\.ienO+]\TeY`;X*+~ᄜM.-=߼f>ZbLGv? &&=$>a.<ع]9@n|LfIQ=~]::j2,7ZVs1!+W=-*c:,b f@Ʊp]?_"}#Cb-oyJy/r#| @3$l>|s\dos+"OG(Jfi1昆zN_6l5 k7n^QӷWZ(~E/hOg3@; ;૆p&oXŠn+ٓR}dQ¯djqYlU}?_f}^:GB?(ŒKsexM>,<577nTrCϜ!Lޜ;8o`<M`cnz??@A =P-,O"{K y8h8/J?T~J.؍Wn?љѥNR9[3/p->>Ά@}sy0s7lV yC跏Ň5|=\O7!قrݦ_1˙_Kuha6ze'{mF fWbhg6s5ccVC(31Vml#9̕?8"mRU+95VsZ>,ur[Yu^ }CR?ӡ_b?GwWt]bj] ;S|tU%wKÃ't? 07]vzQKC*Gԏ+_S1A,C:f\ a }.r{tLs=%Wǧ"X8qcz "HNkg4 G!pQ۸.Ϸ*׸z̪tRM~u. v < |@c vXM2F&O[uvNa'"QL#% #.S*_ot~uf{<ަM5z$Ǒxbicrn gE #Oo&ַ̗ɛ[SI?:L9G<z9@q,2nqf?뱯>&ObǢ4 4Hj*eo=o <S˲4dz0}T;(/ۈ^bsl}#.?1n_[4`~op\y*KgU?vlMͲ LG:-`H rb:#ϖKD|M_9y> cj cy`>Öܾ7qPhL3 [ hsApd>^X2{p}[<׉]x?|\߳q.es1}N z>ly;Mv9}g;/t/vVf~߷[%K pps'wp=\ܓ O. c[xw pyxl `G/ Y|ac%ɀ'.+/S>1_2ݟ3ditI޹U6K`-s-**YyeK2ۢ]rkoL.8ΘܭόZ7Br^Uq>eվ97\ˊ䦓guBׇomy).]yp>zR~7=j3I p}â}_f"r6=tqw&#WOxZ:qo1L uüO=''H%>/]?A x^ 1(WϜ'&c> }wYNߧsJuGl&VVf(_uqVO^z߀|0O< )nyW1k~s͒hū& _ oteVͱM52eLUvis:E>~J7 w2;e[׿^}C0~Kl=:>??|C>cƃo⦱-/M.!}CΡx;&T ~s yf~\@oO+sWf;A@<3zIխW-'z$}8ue<syQk?B_ bw6@>>lUh3#VOg&@I>mlmZ dgs?G+-RݼkVwEoO+}26Eoi>WzPKs-V7 f-6>D6;c:uUlUkk=zT_b0~,o0vzѹ>9~tx^)|| q fLӞA`Sڣ)#gށ)A;x \}]8yۻ풆9=#;cgaa`1WO],xf'7PߗZA~??7~?!іpbX !ʲG@q_.ҸQ>cXkzr 5W/| ّTZ|⨜y_VkuW9}25tw0y);MB 83AWVVu*c7]<W9߻sW$tgNp\է !fܫ|rWP6SlUNowh_)]GoHm^т/-㮞nK΀ޓ+uwsчV{m]'>0c5F1< prKx\ZE9z<=r:ysjv\~I%Slw+\^܈]`}/Xur- R:o6&&8Ѓa3}t V0lA- ,D~..N://v`xd<ӟڸg>>>'^0>J3ه1 C`:061.GxmWk_Xy鷷N*߷Vm?{Ɠ/ղNI]Nv3VHkl~nV+ʘk†W$T2 <>>ǀ jk$S_:%xZzn;bKf|kŵ ?c J3!q* _Z8 mNmVs?;An2u:qg9?>Z[]w؛׶G\=~^uXLJ΋>Zs3j>\0uS)OSU>MMr^Ǎw"]0j)`Py04=._NP٠磇/.;yK$gu7m> |Яwlu 4<ʕ=+LFⳬq,)K^ܼp"X}br#jrCv9 ȦxJ]nAx cU*o9 k{\}py~;iz'FOt,d)5qGn+!U^^!68NFWɦI)T|3ȣ_[2[^>:pbGM' ׿p`6@~PqSn 6a:3gѺCM&?\}\}?/M*c$&{*Oڢi]+u@1_=2 wH]]qurnclFG< ~p i) qA_g`yk76>7x5yAlp ^A.!kfϮǘ.xp=K_ն_i*6vtiovbW|i}]8 .f^'xs=r1_χsq,\ݍŗ^ t]rK$7KR^_.'C~KtwgSƅ1%c/ DD39^  `[E]r 34+GO?>Sc];1Cق@mbɱOShz8kES^?wB+djmiw%52hHrgr =4}0fD1ĵ~S/{VeEL#f=Zm[4ujݾpPoXۇ p Olz>vN͐/+'u{[eeW[j8˥b$CG;p 8&3-``F¸@Ξu>a 18l1]Dƴ[g{:pI^0,V,YGg\Ud奮vrr&,Ho<ۭ2Eu^ Y jך}? bPxنy{p."kzʋh=*mՆ;&Orxx@&ߎ@lW`kX>Pf!# [,ΏͷO=^F,rz_2xÏqs;y ˓}3Y9Sy[dse=Z&'3:;zϷ64J_]\#koTLW łzVѷ5{jͷV'N+szvW"M /_1-8~$VQK~;cS '6EX'':>xm[y9a}}#|cfa ,<3 ǭM8?E jXia EVˊIʡ:${߷W2cA=*L~˅.H9&߳[vY3Amj:w"> %f>2uWfC .l9ٝ 6ƘNɋߋV#`vJMWDY>lsO$<\&U]URF~|_Ht$/pS9Ud*trcw`h_Tl™3(Æ"9ج]v_໭S `ԁ5+f+9rޣ|`T+[PJu|uj1KS'n<1Ţ˻j͆-ǝ7?;sQ>`0c2c|ӵ]sP$X'i/?ذQU(,ٸ&g`~^eQeVKY-ak TjDzoX_W8 g_Yxڏ~Rycw := -m7zk͹8lEFFewE:u9ka^N\c*w8<j1ᶍCeYhqa96f7>׀r; Ɖy7 bk'_;u*woEAu=WQ=yܵ[J?N_k,\9>'\ϱcۇ3b6ogkH .#3X #-پ췲R6Y57'>03@o7[1*ofFVڢMus揇 k/OnM<w|\x{{[O@<>Ķ͎Q-"y惭#La#XG(u!e5{jLo~u7W!yKHnik5'k 7r1c}0k vroPϸ4Sk5~-^Oe{U[wpzn7.iܭz0|ءڶļ]UYm$2ʳ8Om9W6Nߡ1ħNKV[xq*rb.C[ O {]8XKe>ʎNgҶkpIJ+!_ykָbɇ}vtjyƫzBuV71Y+pf梭ںkkiGGaA4V{ϠJ 9???dM`廇T֜<]q9 ^Boҽ* qZ#tfG6)s/&~G~PX*b"?я 1|=">lEgN_Kh-8s3@z?/oc ֓c?@>|-lzw\=mg*6=gu [vر'@?+[/zFmru޻E*e˗%7zYZe/Vx 0dq .cc`(XV>5ƲoriƤ=|^~·\tw>ǀo>q>8z_'_``t)meϻZvleeVLǖN.?`zّ_govoPw`OTHid\]U>@nuWUaXzMT`K :F^Wdq{,'ReM\{$ΰP jl5;|E/"'~8FtJ{RW+g;Zdgoܥ<eӁ/ X:haUӧNɃ̞gn5#XC3o'/xO.>6~6KM"bѿ%Pw;7uro;gtc}X#UVt\QUUg3ʗBU_vf)w8B!g1T> ˃C~q#7z%nE9~]`` Bz{ӶX%xy b"Ά^p5V7KrƄd &bv{ c$O9NPn$O>Iӑ9t*#\X5 y&ZG{T.\ONnțmk+Dv`,5 0?9{ǃ(}q9f $'?X>^rbNf<COn!' }oXCK''xIDzKɖz_qdVB>YH1en2~x#a]|m FG[~ ':< 9  )fی19Mw.׽b[s %\~NOq6./Cp oq:܀#[l~=]r/%wv#|xS"5ZO))޹MYմ&rV~7[qa;zź.65a2\ɘoP7PgﷹP]#ZNUuT+ X,da b[cF1M> TJ?|J'r0 o۵C^[\2RYDŷؘW;-/^U.cWG~@liӢߣcP@/y.^f^qqp8q)]uϞ5;&!6~[mdN$h?=5w~.wyV' .Qqk—^N_IDoH߻!n1x<+33r].,Z>F}!w(nŕ?,O=[~55;MMG*`"~>?im7>B33C}3 O5O1E6c}gM^!>,??Ky?eSsDbK@8Ư;u&;/>-SSܝ57twjx/f[<~|kr׮7inE&NXBpr{68yh5fAS } ܂x EYKh~$f“i`#7օwe66yPp>=/xp wH3.٩@7ߎN8w5m<Ϟ'd+w=~tm@ ˆN']gO[{`16ݲ-IJdga;iq/?+ D\P~-)Ty">17Q_gd_YZ9ܷUomj V#[¼1_xfmB Ύ6 ϸ<x@.yb \ %`y~EWڡC":%,uMaD9i~B_\u%Ol5:Zqer_V6*n'Q!DGi\qonޣs9 ˖5>~Ǝ! .3>zC`nP_`IھA> iX77:_Cc^Ok;L%mǬ8?UA_hu}|@@O'T^WI{ ]ma`Y٨˅B"gyt5= ymmS8LJd"< ,2 x+ȹ9Iu,kV<;yzRmI['qwc`ͩ D 1}vW/bN?ڀ^]V? _8 ggf۵зo[Z.+WlaYrRܒ vz0|~mƜu- 0ƀAޡav}덚 W'.rbOئIɯK]?0c Xea-~x 0nٟm?+u~p,<#NA-1qk8~i{G<΃ɽKO>S|[j8m|>UU_AVeQ6?9(Z[ ,hqw]I/{qs;|=n|k[ _:crdvI{,;zee6ǭ㟈vD OD߷該܆ų#נ /`':?+$J^q9}eWyo\7zC' v|jL~ _ ΎQ;P[7܇y6b> bF8e*[>6`M\)\W2M #ЇM,OOlL|?tL%fY}jpߗ6!G~Y@tGT> VGr {7|RS9u؟PU(ݧox}A)PQq-ZL7Cw|TAi?XD<ʫ~f9"6 ŕ~msTٱ#FS'e{~ 8>o6F f ~p> 5np;8#1cW~W,:gus> >62i~0s>pޯA } Xy?qP;g?Rl vƻfgJJY-?\;~4;s9c1t9 yYh1&C I`|Nio~xnb2xtpr PSvom EI_H:b4ܗVl{6_YQnkQu|tʩlZPYW|Ėqjц]DSU[\bMQ_c_8! ٹZ-Z{w)Ĥ9{ME'NKw!N.SWI}̮lubupFb81Q@-=:&z}U:СkXWWDXC |ڡxݞp |]^? r\|"cx9݇k|qQC;ZZdޭj.;?  g~].SY[z洿n-7ζ/@>J>'}|}&*oij3{w OV @瞖6O\ Pcrx9}My:wZ|>N``O-:|bgp.ޅ>6ٜ߶*#?Q Y#*B]M,:NPtmG347}sȚԇ |md2e_~ !!Ӽo=/}M 9Ez Dvׅm=L뿑~=torћ}LCT;?n3k8F`QBpk \8~s] C?,a?7W;@ sd c;$T M27#GgOkkoqڸE ܙ_S0LaVx>ĒF@auu%%eΧ%v`3X 6ЏKkp~cѳѯy:[O=|#׺xeiÇ= 9_1ץ ~%_OM\t}sg׸Qn[}[#O\lޫfvpbK[A,uyT.?/nA  oR2Zf\jS# NF-p9vkl{6OͿ-~vWm;vsڛ1.\|kW$ƀkObP+-bD|5rȰQ&R i;l>`=K7Q F-~ T2D|Gn\IB${lwrmnRFwb/PlAچd6dIud)/(rnqع(>mɕ7f  [sfo,ߴ~kp_@/鹴ųו7j ^2_>6DO8o^Ww:=esw鿝]2|S[+1| S.fޤi|e29Z&qЋD<~ޥʍ/|_׀c{G/[%3Ϩfx{-Ɗ.=ߐe?˭k&MZuVJv';{~󭸺Xq5~sIf0^_tEu<Hץ}65b'NO|"Xr|q܅/H'Vjwelޮ{'/*59y1S=}\;: zDܬ/Chv0K0r *3rLNP^^B//;>+'裏~VJLfJClOΊ'XoY s#O>cCm/<F'Cs7]m_^eU7ږ:{^qԴbXi'\ 3yg+Y`!Nbķv1Gba{_z7x"wSsnB&>co8gF<yZ~rh<&19เGr߽gd?{w~8+28kgi2c9P[P?.n6M?`۷<I3xoZ7=W#''gE t~/}ss3;yeYӺfPe|d:ނ"= /{^:W^}}'5'}Ŋ&L q| `8]oZC~7{"::ޕ⼷,ߛ][i[c|y$&'?[-W<1 q9YpZ^ oi ~mRi쪃N}^(| YEqp}Ԓy-džw$|79{y P\oCyl#F {|$G2xfP{KZ~vcGej=0ǧ㐯FG>pӿ<zÓA #cm*+bo`9hMYBvN `̀p<x<̰pVrJm|+z x 759DsHmx 7>v]A[ M~9'dz,D7bSi^uvtCKwJ?sjxSbgNд V 5`-0O^ijx>C}}XZk-o^.;=5*W90j`{s:,QsYYz/L+icL~bhFz^ /z}_o[,/"ۖUno`Բz}mVm]q|/~$V> X3<>;21:Z8G-VF7@.!ӏkߛ:}K\Fq3W@O}\\G/ʹȸ81䗀@Fx?{#ГV\GDȈf+1ih1s^i/]޿P <B5K1'},=^:у'<vԋ~O=W y€y !~PTG7\{9kjy'yBy 9>}|_GAdˉfV ]g}_}gԠÅ 8Ӫ?G)2<|?vjRx{S5.㨇}_Wl8'vb$G@'>w^|uwڲeVVW&_=QqᨴY>?,,&.C'g,$9{\u0#= r}<|My'˖x ݡ!"^P@M|bpL>\j[fX~[򇛚a}V:-upTɧoM_rH.W/N;XzQ!uu[2 w^)p>?x$կ~U~1pXEA$'Y{䤈C0+ </b}4<DZM^xw2~?{'w׾5gpl۴F }-% %M[Ōt_"%&F_ 멅kL\W?u|n+ȏ?53e:\fg!9m[K5+mv_NY&[YR ssou8p]aIbTӀ3Wf4@טJJ4xgff 6ޣO23S;M>~ 3S)k}UO;ۚQjdVΆm]UĤپn{e 䁿iK_==_jG߳ڳP7o]`dkT ^bE?Y7D,R~̰x@2 fG{\OlfUF!z]G0:sD}Ἴ"?i= y0s8S} ˬrS93W OM>iE:BFHMs k@xt1w{HtSI=?S9^q(0tOrK{_ZWN5zܼ?OK>+;+=6O^tz-xߜK~ݩI>:>OC?&^y :~Wp) 9Ν+"I!?͇~7m:+-w~G1Uu~9v,yUb'^џ|\| KEJ fݸ7g+@ M :xC!p!S+‹y_Tzp|&W㉹}&6\|<=ki;}YŒSC 'sm@2Ϗ\.2 =_cU-UO[anmgR9~%O-|qpL LWF,$&7CsW ޺\zjçz˪Vk s4=p}]_ڥެsEٶ2+-ks612%ꊤ9O~T/H۠N1<ș#ȱ .cʝ.-jG/~a~dX^uI;p-6ZxJ {$w0877x*F̎wq::}tv&wӧO|㥃Qh}n`.? اu[]wٴY?m|95MM6@ y&_2=I8C?$3Qs+Gy f2sjQ=O?JAP[bS ouDRĿ=.o啗l{z㫃hy:01sG-cPېjX䷉Z敡S`Fk]> P<'KL o,=5!@'#2oB~D8qڍs7jUW/~ [Sgk?̟8/88~uC[gn;0|`#gxga﷔+oZ} bE?+1Y#>INj0c9`9\qńw_Uؼ?I ýE>?I,sl>N5qc^_u00~nFˉѿ*S6vlL4Oiy-JW'&'/X=x\kHA+o^f@]=yswZjlxpP?ڄ| k{ws\:Wڬjs?S^hicjϻvlLkY,]+W T=cRׄ|ݕq8 |}nygo'?M,3_S߷vr8R`/~89xbދ3eǙ{U:Y[Owq' ?N_ܱCF1GW@^ kys3q0N}_8wF&[Fhu.TYӎ/ZU: f '^p:R '9yF>iNu pNzD{A\M1CP\=F˰~jc.T w } \Bb)yRϪQh$y/yz{zN;7J6ӬUvȯur'zԩڸU|y?A~' ){#Ux{'[Zw[zekk[Ec,HacK4]8ղ[j~ݼ:BS / =̂!/,ƃ Yqwmk\9̻{KOtDc ;z-MZa-޷)ioiq.x߬|WXd ^%E}>ף&o18JGͳ nsAJ9,M\Uҷ' OE?p}s &Z@2},9kbC8of/v^Tν Y*塔x]-70@XxF-'/R>@b}rӃ3/P>%p/ɑ9kW iS])8>`G;u/NY#إYywH,7!/}ngF_'"O5 ?ށ<pµz<|'?V~83!ϘϜ^=aS >E.}m土elEXe?z,.Gm[c2Cc` }M~m .7*^ԍ^9 I-QG4;m6] G.?B3 _?(˵ {㙒Mgt0}gbpx8<`r˫T |Cd}VW?k|򽴸>T_'I,s!ylS|~~8j3ջ|)pC_Rn.K#OW>CW 3 _/,d3^jc^5Kz⾾cvwyRXX9 wW{~ _ V?v?}S)Na$@*q?h=h_Wreߘoju(%vrۃU>[ݚ#fw) 53ثYxGREӟF%83snK.Aʢ@:wpKo\$߁VR~΋Pz#5w҈UU۞=y '׎??ss=<%[=ܶY39[>5P?BGm 54I #9'm+.((=xo~Sq?./_mo_ށ|h?K^ <<DZó>9>ϱ>6|++:4A,9 p?Ucr8|Ǯ/f>>Oh~6죅#v+V"T1~!fݓsg)Zp4Ky2Qg\U]Cbt : yt/SouFϏڮ?e;aeʤ#g~=<5G#ܟh9p'Ch NwLڿ i @}spOOTkz=+G+硱L]xӎ>͟3Jk*Ol{74X1l @:wcFt筭7w '1q9_81>5"b"=(SȬX?sdYٚkqm92{㢽5akj0aťR]x_u_R6h>!xҷoo=PX'^hb/H?g_ _{ӞI΁~>L^>|A+x@~Xd Nn[Qoك É7 '<.9ģY4O|^wE-|?΅cX7W-oj}{&L#w_j`D63q9s9Op<WwnG')3F@~9WaGr }Zh ;*Xcx{-elZ>bWi!8܁uٓg') se`y͡cCX$ YL%C#{fM Mع?g]ow)Ëita?\{)S6Fmu7…:?lbΓ}9ǎZ{k/~Kܗ軹48 ;x>UUit j &'CߏM#$_9ml}VVZ&Z7?p0~7WkZ~g|u^8 HH4~Ff q?ԏ ?5Jɏbq`7zSp4YCq xL>1& xbEq6Ou\Swp.S:uU/ go~c=lw5;*܄0GpϞ?1< A=OLy𝎎w];HOܔh 7ǟx 麉:|pܿjThucV\8/apxm$>0} )1sڴsj߳_5˺ |> :5bY<\2ݻ  D 8@΅vԉMxdgyvssfFYml}ً뽏׬^52F$$M =L眪r:=}~|}÷o߾#<"$JI,,,HFFɖ-[$++K%믿.dR:;;XduuU._,z%=::jmTTTX333rŮbE"ɱ͒'nݲk~3y饗Qw͛ .Xx<.+gϟLo쫑!OxE#qdI- +RRXrre-&++2=/y5yHNqdffJzHd=.+˒x6y$ wynY7Ò){J% 鵦}_OZ<;/1 G~}kwJˣ-˖|RdfpF/KZZ+5j$W$1եUɫ_֗%1Xܞ/ KFfdS+)_Or9o@>=p\rJr٤S~^e ʭoI^iOJ^U-2g]-Քhd$:t{exV.>uYdʑ2(':2E$W?W;:uL*xdUr($>$ y xmqz\`P.w#:Gb1k=?_B:fBm+c=:R A^c@_jhRߋg#SOj;ǞI͛U^.rezZݻP 鳞,vן^/ ӟ<^Q'֤No--IOy~f95͑yy熼2+TZLtl{ezΎ\@L9.KKwt,kkqmw~g8m#L? Y5ҥ-ẑ5~;cǫ3V,=I׋{/oLL\>uKJJz[d}_ՄE 99^96Vh?Z11ac+ѱ泱vISSkӏK.fLMMɁtnM3nн{nĄ3\k>OG΅gl</W^k7.>36iw~wݸä%%%֏/}KSO<#<"ܔ/ cKJvQ8bo8O&J$n<I%Rx\oΗ\%Y^!\%$,G≸ 4(}듪]Ud4hlL`8 !beIN'\Y^\6NUPg<IZl ^cklPdJ Jrr|sibS1;jp՞ -HD2r2)3%CA>3/<#g$KiOc͛VElV\^RwƸb^DcҠc𰮏Go(^+wY|` u<+זּ;7+)TlsW^OhIqܠN_9C΃^c\g#jY}\5hCZml99[NTϿ<V|ƫXZ'ƴm&ʉ7-5憽6f]u~m9:/çuXi./S,7,o33s9冩++a' |pf/C6Uȵk?sswb]CR69֖u,5<OJ,&sAx0$ok^>_g$uꂮe7d׮Z_2tpy<%lbzk)wEΥop3znPm&]h_s,}?$Oqݻ?kw> %vc=oRmٮE:z,x|Q>/,ٳgϚ1ο/ʫ_tpBz|ٴm߽.ѐ$cI[#Lݘd8) $<|Vbu.vފ)33G,,Jisxau~eL;x3&B`x7M#p΃ )8)`jɸBUg;]{sq^>cB_F/J`8 +˃>#7;p~`;m-0x|Mw~g~ikpqp3'c,_/"u}]vFHm"]_YY)ccc`M,>7gz7 K >/^a5I&ٴ}*X Dd5jzFަ<;?9p{Y{ٲ]1l Y]X5ߒo ^Ȟ%۞vqY^+߼"uH}lDniM|IgX.܅cC1 \ׄ_;-g[3HVi(xrYFGQUvNwaO/(:XVYϳuLS`OpY}PLu9C^g8φWevD9i]6+7wAm{GFF.S>`#s ZѶ˻.[oYOt= kkf;3s}F|r'pP|匳:ZP_ .yLl~r337[~8Ɲ;3؄{״}G[JK>m;byoT۽Y{jk<x<3jl@Tߗ_~Y^9UI qrO?<.-Hvnqc6m.3{̚։M|o t62.UOc?K,`pMއ⇿zb~fƋf3=R7+xFٻ'8hСCe> O>o@pMcy8~} "p8@h;܀_xcxIm>0bG!-EO͛YHٰhk5Yx}Arro S1&ED m : }a5^3|FC o\;Zq-t0[5a^/ ˳=f'|G΍ɾO3NpTVJ[l\W5(Oc>!7 DDl# RPW`Ow3X~'Z6L7w L[@ ] yeE2|rX|5ٳғ0Oz}:7紏>a=?[^yΜZ=O|^‘!ٽZDb\ 'oš7Kim>fcS//9>H\ϺwɱcN zS/^o 7ЮK:ƍ 7̶+R)GK˫3,__sow^oYOvՠp8~nnjkwTk!Xu'bz['g45g16ޏu;/xPSnsrbH$Ƌi.δOu{ٙYLN^>v&Զc1UTW{=lq05앿x4|B%ʩ{g=|{;?%~xM.|ai8`X=4?+s:߹y#`{l[pte-eWw-Čk$k"S@\)ꪲ6fÿ\1+Ol\_]H}sݶc˲peAu>%'I] N9׮2<LgƂ\¹B N~`pʽѯđ`/7v_~|ڠ]l||\{۶m~'7|o߶{ ܄xE/+|ߗO]%RQac85=[6l`x&MV-2mg[?'/&~x5+>-,\l}ht҈Dk 7+G]ϐ׆tKak/sbqBWC'`vcrye=p1-%t~Sh_Ommr Iztkb#;O >9Ad4"٨3uS%,m5o|aACӸfn&ɾÑ=_;4LWp l]sK19n|yHd!<.[kN(lݫek:ڮcV(d6zιEc:ucccO nJ۫z\KW60E1d"7.)G)zdfqN2 ?]c?tf-8`k\F=65ʕg#E6-k >.x`s[ܷx u3S]?0΃ς8F|kk+Lt7Hڈ90(oudcұI/Uq]~8mjC6^{u~Tp1cSw0?ǸI4zMGwzV4?iꖫO_ӴR<#{ݝ2Gu^0[5+]{mX֥˥v,M1#{6pkB6Uov{]9mt=ֵ`&rGO awMSJp\M|bt.c?p0>x\ڵ:MO**9UdZRwooܵk8>q|mN'n_}/M8{{viB@#ǂ b _\aPm8ٟq1,ѹG%{9[B+x>( -w5\xҍ%_xo|E=n`+b8 'ɴ@\ Fxr>9srn"o5|lf]''9}E-Y|X.̵W/5j))`,<ƿo):u5'3˦wς$4_8+ހWa39q'ݱ1@kAcOٸ&Q7zKÛ %>=+#?ꗝR$R Z}˫:6tT+~?jrRtu Dڦ&8|zk34烹7tn(~M?`9}y]f(sGя(?YJݵ ptl"1+kЇQFGe(7[ 1c79<)*,l7y8]$?K*M&qbVVu'&kn-&vm|@a˃aeAAi:`q Ǧ/Ia ;z6Fڃ[`0?ŷum5溾_EמVO~^]?p}pa;qfk/-M}uP ="Զ|fu@-Vat1mjC;>Sj}Amzzܲ}@鋋7~沬4#mʤ\+ZnG#[v.^es{\%?X/>uHK ;/ϗ<n,qY2WTHAE\<<>zq$ʚ67q 7%̟=;OL"1 ~滐7q9ʗe=9 a˂vzz&<\s?\3t7GXWzXq{Bm, X]UBNE,%elgϚo~׶/͎} Vccyyg>>7gn{_7_4?_ppx"p|gi`;?)=/ܐL ^M&ߌ=ט`{258_| 'f(g51tj o׉|ero7=\DشģO|Tj,߀x=x lu/th㱑C"`ˠg8gdԈ4?,'Zg []J8x<\ۙL۷'Oϻ+++4$4lmfqO|)N \yS 1NE^ x#h\ 𜉷[Lo\sxAcX\%E>7a٤L~6~_^o|(joH^γ ) oA(u޾S+즏'߯Fݯ<:#^64,3:7.}N̐_wm6?5Bא~\gUIfdgX,"W|Shdž`J;C mM#s &f_^;kuW_犮Ehl ï!Vvr_x@㊸Pd8 C7Y㧈cVL_!qu{n]1 Oz>_Ϥ} )W[~y?Mm_1<4twI,/aqq3,-[:g$bYZU>G&,Ƴ7/⟼h:fo d@}.X-IggLw5F|4rt-p஭ -:|bh `79~7ͼ.a_\ib#PՓrtQgMa.Vǎ*_{])NȨ~}ryR^dcrԦNgG9: r}FpcGq p|b nXfOx52rgNdи-=&shkg|!KK2}}Z<}E~hN[;B7CN}Z'g N_Yyp{ KwxG0+b/lZ76tMɯ Ȫυo]sH""+#ʖzӯ㽣$/WE9f\)r\Me*cgcÙu `tb0q`=?twMeFz>9z5zE[ө;a_ Tq|Ⱥ!X ?HkN!Ӷ8+NV]cLV5vH]/m?ܧd};9 V}`5/tRZ;0=3C'Vbee􀹹[*K&cg\>W{}R- +zCו앣G?#KKr2ý ?$坕րlk_xMjȁ<`+;*eGwo77'KiKc] ˅$6F@oq~n~fKm?8 f\^P%d[θ++ǂ@['X`cD)A7Nt_r.I|8&5Ւ棎z:8]Nr/pcI|N]1Tkm.`<>wՌتk }:h o=pkk[:5qc ?}Z<Ms7c9] w>"}c20k͏m/[h،ŝŲxgQ_闛ܴ<w@j՚NM {ZΰKj`~}tuSG/0WfVdҢ=텆CI0ĿX}}6',wP^e?:Zӡ&cV;*̓ECY㰓kox؜ &ڼ~oWY)ɰ1*"S'j:X c&\ eGr_ɞ_#-w@}g@Spψ1$s =g` wLzr)ͱ~poZ|(7D@Z?~F.~GYrHGۥR6WKb9)Sјaa\|hoC%^/͕ yz99'W[]m9_}_͛?R𛺾Opdpg~V%`ߗ_e׺x)Y!%v~R6?Yn?˟J ND% ΝV3`'ު)ss˱ű؉G#m՛:ީ{x닠5N!GL~3WM&dodî.] n'F?yC~xO;S`|/~jMMWwˁe[`wBb-=3e9(sgd TX^ԺI2oM/:Pdp\=?hhqp@s?SgϰY}v tZy.%VKO22N >Gƒ"G.żL&LC`bc[cckC$lr &5u-x>pb£aD#PӏmO?8}0~ z=U螐ß>,%M%o./,3 8H|^<u,wc19F !]犎Ii]N,f>w z<>lt.?^s:C|p6YA_tZ_nj٧_/ x^rtNQQa,TA;5NS79;>] `}%}0M0&޻}A)557Öi,˽fPFZ(Nj+ͯA~a"ȱ8q V-`~~X^zu|@}n_ݿ}~(,g7{O<Pi_VĐWG^RxTN3G6[.260kPy{ Zğ>!W_*Cgn#O0|zz\&Zo99 `Up"h~|0o5pc e 6 Յyûu1!Cޤ@L mQ^*zLt1*qٛ+xȴ7.nl!8 Kr?߽u \-`fEwGN>cwc.4O=ncÓS]~燶?v>|<}}W[+>237c1uֱ@Ėb6%0NE)=P*2Û2qfBvwؽr f 62Īc+S/}bwn5E'%:091jg~ZcjTI l o>4x9p m`*g',{Hf݉{ MϘ]}6Z,."'kiVZ.ޏ^6]G7[ "W7sD!|ɢ"FwKIklb,/ɫ_~Mf{29bc+rkaw(Om"_+9#cN5@Ƿ)N,~~]9w1ߦO?ms;L®'7a4Ϋ*rwѼ_sӵEɿܜ\YY K?_Hk1< &X9 i+O N x/6|=ʀ?-[~^M3 [x'mŰ!5CK:w:G[C2, |M.˃@?{bc5z3<>%\ 7] '](i|77r,F#鸆 x[oP SS7,WpZdɿkӗզz]j@&]l ih;*BhZUrZ|dFvDJWK l fI_;/Sdeqj[?sۭnS=)/#wxLݹ9;Gڠ601r_3?6/#{d<,}Ͻ`*{<+y}9\cukկ~Uzd/ѥjNZ |enEn|qxt|݋l"7^Z-Qn.9u;h&?\S'dwBurҴI~=o6ro5AE˻-6~,5³a9m{6ؘ\gwfA\dڽPhɝ 2ʺ+7!hp:H|0J|!s ,('W^;Raw֖H2h$&g$/ J޼2y-N_q][@ӯ6/犹h v8gwB!_?}\R{J*][[:`:_]Z:ok*WqN۞'6eg%'nXt!'99_`z>|>ߺ{5M'#]ǯ$@*gi9E؅$X>?5 um#,e`N}C--#iu Eͦ &SߗAyɑSӦn-S{2=&5]b}V[״tGMEǿ*Dk1v bӶ?@ս}&آa-ZcY-l}t YAEp&-B1tߴˑ#n9azBr7hc|{Kbƭz [2CW`:\GGpĉ ݂VGp.2p2VG^9&\~r]~5c?#2j8 G٨=ZXqc E"M5q7r dP**eo쒲2+N~ u,+^{[`2Xl8Eɯ}:v'ks3p, ^ J|)hso8:v1~rjYrsW!Z˰8BIY }\l}8>~.3bGQ9峲Ͳgpq5l?i١4~oh&9өDnqF"P[,A7- 5Ⱦg\agbn6Z5*+L~;pmwr '>0߂7T_lпpci ,_{qm{.~b4zqTZ[X1ilntqwk/u[~tqۃ/r Ƿ`/]}j'O4|8Alw{7̇68v}0>p@!@@O('?~|_^Ilm<$VS^>~o08bʱEɹ Iel o˝Q)V.{~}^3[ k4t||ؠG\(xM\i-zm-EVSE/Y{,Y}Mbhjlڱ0cֱ͆ UeڅY=`v2~ ߳ &~̀{M٠i~Gfg'Vsoݷ.clϡJi\Y]ׇ+}Nc1mt{| `s n@1YN}\*n+gYv/ ~!5S=ZLC 23%:ti_k-3~rsұV SzIt1O~cwWyr- ɿOk R\'}V?{-yVRoe܋SS؀I= P:z~'`I9tbnZ vzA ۬veCA>p5rl[Ӑ[@GUn]·@? ^AL$~^[;]@y=l%NYN\xOqfV;$sTzJOMς.>Xm:\>\ϣ^5uz{&SVcXKoHIko7C7؟iK,~׮3N?:B49w00-—ajc쬎?f}㈜} D燥rG4o߸*uyHEv9\#p+^'/?;c!9@샶O95Qϴ ?VY[M˽ IѦ"^'yy][& L&OϲؾO_G eOm okW0WCu^ö{Ï%8@ |%NDɁOԱɋNnSC |4j-h#Z_35 wںkl r5ywBn35?z|efSk9xKK7mkrXO.hz'P2]'N V ;ݛ/]]u/}fg=`v[ish`l ^)[X)=Nחۺ=#w\ѬtMȯs|N]uVw̶||[_\L&ۋWFkAup4=3'wp4 g_= kӇeK9kN!Ⱦss7/+kfGvܼBtu_iߑ9c͕ҡOʈ||g3-SKKrzrT.۞M7z-t7N qs5Niѝm$hgRNI9b?'_uj,=}}G~5g@6{jؾ RTSdLk'? @NWEڜ /}},'NnꞞOwco;H7oc,;mawyS 8Gb6}[ ?{#}k}CrU㈕+o?|֡K!71U1񶗬b d{Q!)=G@öxU̓gM`ݭOn懚tKY+ _=JLUy~QsP7Ɓq p5LF S9u2=ugXk|dxsԳ7c\t.gZ>Z߼gŻF[x NMӵ+ -M ;{,O_j:~"]?ˮ$L tg3w~v>`<:۩ߟ{`99+Y fjRނq37 Honjx>A?gDWBffi&?c kXƮNɯ9ٕU,+eF֠qSxMߚ[lx~@^-UصKjtrj Ywm?_Է)8rXj3?a |:_pM ĀS#߼-߹'R.sjw<hT"Y]]4nNjo;f@?`px0eYWOr۷_5@EE7L\if?vuL.G_pUUA<4gqق t+Oo\.;5%/gu9qN?^6 ӛHԾ!4hDQ tNM{pDAxM*e g6?G =%#!޵U{r֌rTg :n7ǘ}@m:'o'>̉HّZ)o+}Y|O-8?9#>=QÚZDZEEVdg@o(7~f"sڤi힮#??uzv8I-a/ד3/onz.nOb4f9L t=Rϒ=10^Թ)󽝲C-.f Xx@A%υ ~h!|gbdqY}5g ]>|=Vn+2D6^p-] VX4h6α4ds:k< =ߍO2yp<4Mm?Ⱦ߾$?ε|١v{o]2 ɂ.1؏ĜR+OX}rW!ޫ:W~~ @, ^1eץANOb d5)y ȡOJڍĿa:{dmY1e^S 7Z$ ٷ`SgN @Fz :)yg יڽllgdP@<եt 8gIw' |@j+:5Vl_WP^U9H\Qn@eDk rӅ G5_^X( *;7K\Q[|lFfVlɞ/5MGόʱ?<&mj9I ؊?4nb5 ?<G ޚ/`n'N}w owFM`'}hӵޖCxen]$-[H󛃽546lp?c[# =y; ;˶}H%Yr%N%`@m t@f.,eOg:h)kH %! IFx_UcK,]4U=߽>~hw]wwoޫ@`p\!~O8>NnBZE:xw~fŹn䆞{oٽU`rma 5~L}^ikC2xܢ#Ӭ3r ʋo|ƚ6[N\pee [`=kјmS?/O.ڽ|-ߵ\xǟ}V[KuxL`d48pz}Sge$@b;ۼ'18܁|fO㇐.S90eM:nk8Wz@znS@wgqMCq8OĹ8X,I| ?6Z|ϼA j{sOVbɚ%tRqxK ԌW<]xSG{O[KJrnUi}vkGuV1ۚ,V^b^[iCLmkZEuʹ6m鉩O;/5@fz w"z*hpt?y̞`|:; +-}[H_ߩ9:t& z  v~ƛ7>XA:qty[6qZi<$tkjӯ>~cɣ4;VްJFǢ~.8Ź<{{E錨m+ 1 X26>Ѫ?Q-Xh鳫f}ߙ{ݟ4k]55~{N̋~8\!U{v\镯xIN/f /5kn޵gUf)^P@]y׻jط 7f[8Ṕ֣aC]q8rj03oK! Y0|<չp z'xZzɩhQ/!B )~?҃pTi " ZBG^6t[oڝ; #+#yȩe:/E S+'ϿGo7 |eʕ;ʢ<2#@OAUvN?[)vP4syHn?xSf]G6ќ?<{͌YbGi<&'@lzK3K\m68X23?8ceٺXgɩ"vҡV}g"#T޻oJ.8HODa&Z)yP#f4ʽ 82^|FgUuAq=90(me]VTOlUĹu5 bt@0pa@~NLb Iy1ۈ87N>[yJ+S4p~~K8|\<uȕлz_(ށܳ`CwىXHmlOܸ=R?4y_t.^ s|Rۛ>Ygo:ZWo|}0 | %y(}skW7b{ Puv{,~?NM[& `:S(;hbV0<=/)a$x~ի$#ʅ/c T7_O-O4Fݛ[QqB% >V_yi>qXi?!( BVԓ7Et.6ܝ}ȷ'ɰ5kxњOKoN?;{y([zySQ͡Sxĵ8yH,91p '9-ǘa[%s6;l^bǩ15Ǐ_V`'9_:>Ǜ5:~}o/.߼Y9- ]%]7L; M>&?J%OtɮUw~nlxj;xhߨܹV>: krs~,Z3-q*Tg@jOr5vP}|l|?7<砫oΉN>=h|r{9qg?Y}t/r _byb}yOr<7^_ ] #+H܏G/D}Yn[o)Eg9~#|nX8&[I~Ks'`hQ;\~͏b=k:65}Lk>fրejվV}2_rmy5OɯݚaH^riwHG~6;B%K;:3 IOx s.Q*BXO֚ ;Z;4aw.̳KW,':FF?tNq!8>ki7u(Fvxs߷~MF!W8ɑ&5˷I/!:fJ=p;Wgw%#,5ů1GR阧u#Ѷ_] hgfv)J7xG_`<|רmE3? ql뇇C`/]…7|M*{8SGuz4xWX__b1 (9PS 5L3Mg &Ov0; r]]GfWF.A$8h%wf&E '/P ba?>ȃg܇p0 _8\*bzln>fo6&w֗j;;/^|V{Yk^}+jǛVRG۷W@3n7nO5!'?]nXj*8aU[ekM'v'@Oc|c;k\BѦ0xB?sל Hhgmqb۹tjmOgX/<`+ ϝ诛!W`%N&y7M;xr<ӟ4~8'@O$f?909<#7A|O|?د슭+4'%S tUiiZ񳥮ySXVFOX}uĭn{MN[Na&y4jњn TbHpbc_G_Wcpy4G-i*|'{;HGs-\ħ,Ϙm T7g9p( +)8:-t^פg'_8th.僄²Ti}GGwvۥ^R7֋ÏY74w|XE<3&Bsü`R3Sx]+mKSe*S66_~-i1c:'5?6 3b{|)[fIb?=//DVWʰmyKloem(Zbkvn`w.P dz^,V1|sc uHyyh ;ި >\g :{gٛ VcVV7O+wK,Kkּ1qkЮS3[iG>2$'-RZbbrrBvC/¤ <9knؿkh~{czs;;kN/ 7l۶Gy;+p|'Ǹ! W- $?w1h,ǝFZ5mׯa[LzzY7gpV:yGϓǣ=R?>s5~%p/>I?Q9xlt|k8`gq?̓ɷ^z}֟;{ ңZ>|}/09zt\z?ψWP 'ak 8 ?O5'286xmz&nw8ܸUYvԡy<ā`O_;Hz27e Zs.ubAxub`c]|[YsS~:xG̷+vԩ2ogz1|W,:=x>)&nNÀfpjp/j^f'@cYT;O>~q&_cxM;ɇaG('_9'))̔G5,srŮɵ"1T_εS}af ĜDth~]|{o^ p^dg]W[+MD9~.C&k ~y{p_hkb#3휰j#6t6&dݾFdlĖSm1:pߟ^ƍ6=&u.:nodi~SO'O?C4^sV6<7mĩcB|[öi_\>9mp9<Xiuu8>} Kz>NBp3@<^*>A^=Nu9^ZN_VHsz;l7ݣ~Oxq*:vjjek׾KyɑWQrKqE,0? )慑 l=;˗o7y jAOA<>~rrcJ_1'7!B_tGة|>EEwKg$2ul Q 냯qF|? ljݓ!>~3w5\}fE^/w:$sy ǘ 8_@7H ©lk֯ccV` )Rl@4_ltw~t|6{s}qjb 4XvJ7M<`.? @?B=m/gfU fp^ϛ s5ehx оpc[㼞pr77 x.#y''+`'/r5 l rpnۗeԺ^TsY'L}#6`fn+-:Kqrw)@\&.ܢD;~|ĉ)RSfAG[UkXzO/JҼ5O :l[e?ܓA y{ 0W mM16a] X#?ߖѠw,S-!#C!!Ǣybuj`@9?nj#1+8W0 m]zω >Q3g_"$_EDM}cor90~unH[f*,wY \y)mS,y~- #$h43*=|q:3#> &1|J 1yێ?qnd  /uw%+y_E=vU0CC37O_޾<.#1Dyh |Mkl +cZ7 -վ+?Ij3h!Җ)fAX<1Z;~fY9bNu;15p(>.(Xk1x_  v)C\r޲)-)a7xՇ@WWǻ}Wk֝ʣ3o±fa4 G`Bp=xk!QaQ;$Ug3-[/E@$x.xy3 )3@lLc#@?C_ofvtF9Eʛ=|/kBfD)sN zIyq%)v +vHᡫWcVUي(ǐTV#Mh sǻvH=+]~=78^gnP]7`Ջڣ_! _=ݹ W kÿH ݟ oV0lS Z›qzhN '׊7_Wf!4uyz|`נ;m|O˃q;x~ĉr@>x0/  &/~~ y.M=m+61>|5#B?-`gi- s{CqWW޻xb +C[nϜ;e?ou/v$'//, tω^R<`ŊݶfCP4u¼ȋ}ZrFwJ,;zSO !G%,gP+ބz㟈|B`mj}_nIBz&f5yi< n/'0n| x(fe3+*8Q0 N`AWf4#|vHGod5̈|kQfdd*~E} ngX7A 9>݅?EG6V\nd4!Р:fsǥ|`j+kc ѭ[7o 3<ܧ@]@g6-xN#p?UnxF@Ή//m '=`?w\_x_3# 6@>#P7̶y>NN!O=x󟟝ƀ?@j~3}g{AmS}<vf%& O[S0@/(_k7wxÑG'/=8*}b5jŽGFv|9sxh rJkfhffN|Qݜ7`O+tgR Eӈ \{ >38>18oaHσ7Z1?afX]j@Ӏ<^#]e6, /B' 9v >XOszOr >6=f^fW"wtԠ5lל%s? 2O4کNYZNHn]*nG#$#%;z@8 .NGy -x,[~@ p֚E_sz\_!gcY}t6۵vuh/s0k j}?tMc~_Z=fG-D _r&?K'zC|nsa_jXC!C#7ұ EX7ߟ[dGEEU;&olS8kޣj~"U=Z>jq=< 5׻7曎'0rxptL#m9?xUͅ=~@sy<:0a[ qszԷf3 y6'. 1Fmε{۷>'h.}vȹwϠO}x~X@k@+#| G?$W<z٘#-WR?f8wN_;ڧ>Ol΅D=@3`z߼ؾ Ȭ zMGNFrœ}f5yz|ȿF6KO=?1)Y7$.IGAΦKHO03^Wڇ*LNIgxQ$<< zKyzbIp]aO~=0x D/oݔ#.jhx9C-UAz2c30 @ ^=_ZZ>I>CK}sc֫vk͘=??8XO֬c=F-S,Qj0xqP$,SS"_z=p A+gϾ?^p \P|j!_87w7EG^ӊ0,A>#hޡ סgڡFOM~I|"3nJG ULejj@#ngn+_lC-vvZr4dWI ~_`4k9WXgm< ~uD9Bhe^|a blUT#ӟ}yz aB`;޽[|6y\.xN-Qgg}^|E;{x ?()֖!3ijs>Rll2y9ޣrp.Mx:x|OXg}s##/h *m㺢L޷f/屛Po^5 7 +{_/=DziKҬdv_^ڧkW[8P|8د>zAdY WK{ U::88}gv?q,(}d?5+Zl>Mp r"x:hy_N8qlQ|^#$f3_†o{JU7@B=~.]Xc5Rc l{7hM B^Dps@ |=*>eO||Y*y0x4hK#oZ[Ԝ[ǔSذ],~NOurtxGThC}"Ul#sw.Nmf&cF_^[?$qZǏ@ ϧR!3Lz!JއCf-:w~PrC=[nR_h/]z*DȗPjk;@ee{s0'iPG=Xc8 s"aȝ+VZ@А]t^iK=GquL^JKc!l,*RO )x+3;O81~2ߊك;o/z+d ;ż+GzY?!'_8o[k{-AWȜ;qzPzGp@,Ѷ6XU}m鲥 ~Ux`{`wy}g ^x QC_-O<'?}r#~<WW5[lm&q&hf,*K 9Tb,xG]5^[y_|n=K  O x (8&[:MI۞4 O~;GtlVRjr OqstzOnE630#}:Gq7׉[OhnH7X 29`O8zE <@:փvچG6G*_GFN^#I$mix ˫cvz&Gɳ'kA+5g@/" #B];zM\ (>92WdZF;a! ]ƃ ;{.OmJ:ϑJ/X37iqOy:u:n>AguFl2Nxd:o ѲBUYnVvK\\'eι򏯽fWgv_u|hlԾR۴\!^{E1{wdudδzbiiQ }x+W~X<Ztf5c{h芅Eccu<+1zt+sqat8׽1'knb\hEE[^eX^\y$@<4 4Js?d)97<%wnVJ?:B_{::S^6q xB_G6oٗo8/VGo.^T-[,׿c*ZvܹX]Ґ)U]_޽8j;ȜY“1Ĺ7yym?+zX`߻_ 1v7*x||\u1D# {/hQŖdE%ĩN8/q\'rK&[ը H 2~Z7$~̜>[*K_͛T;wN<('sssrSn*555)^`0(/RTT$]]]/~3z\~]~ayGIg`pP]D"!ْ&_2ҤlKxi}seatAVW$[T䈤iqjQ@de'i/r-"ˉemIFVDoD%>x=]LO,/,۱7²Y:ddK?GB]!YY^pOXeuiUܕn7e%}EV+J'&z%͜;7'[ ط.9s6i}Cdy$3'S炲"OG"eJv [z_eؐ4,޾A%hB˲[47%>j}""D{!;%͝&NvӇzK%kCB0 y$0KdԀ)iUntY/I;]r9NN/}ph_׾ZYc IJb:aȹ?1YZmS#E2'ڶݣĊ$fȐr{W$2ַJ d´d2%+/KFʥ^-to |2q|B2|RIONĆf2:Z|0r#"3h HnY{Xc[+!O$< .JVȀKZVɵ9pd@^싲{$ɯ?kY.H~mE6qn*92wlLZZW-*$ cјxyς>[~ 9֯}p/g˧N͙H/:7<.*+gc Y-/w$g$7R\YZ I<'99eqQEᆮ.i?d]#MYX6S%g?`۬rXl?6xcZ^i$6W r/ulinΓcNnY[Srq%#e?1##G?>KOw[8nwz=R[C?_Ѷ kyZۏUf}1Kڟ}={'[_Xߖԋ[6(7uz]쟙3k6Mڹwi)j}5c]u?5+hoZń 3xү81cMw%^wIΏ˓ W$Kݹv쩮MzŭUbcWHl~^ٸQkighYᥗl\˂OZYs J:yPigTj8q1\a^zWvaɯ̗ڀddf<6?csP?W^H'Ǚtxb1 r' u߼LGdnhNx#^ٓG}z%#=ö\K9^Fcx[VVf`?lt7˚5kX`ddDn޼cNjz>illf}]ϟ|;vQy.<2lKa#g,-/IFn^>)h)ȼn$|%,:.{%wmݫHg5 u$m%__rJrY]ӆ~ÞL wX5.ýHsdfK9 K+:gdw<4G\e.O^m?[k\jO=W9[ܥn>'c'dâ_$[uwsIywϞ9"s:-[$frw/*LMpyO|9z= oڽE).aQ 5<yAqp thU9mzx|@?o0 j.RǚmE)LJ[C:W;uy:u&o;1`slZ.pc6??js/kۃv6XݒrRgq1u^yyٿ0,-s8z|>(R]&qgP3م7m6g0O鹎ڵmT_0mpD?W 73ӫm$QTE_(4s=aim}99'*>`sBk3*;L@z|zlxۼHdL,Jޤv:ϊbĒp,&7uL.gJJē+Q7;xZ*O[}!/ݸ!RHǔ(=ˍi9ȦMR߃|p>ynDN̍gS>*kw-m (]׺ j d۷?/ !qufFb҇s}1=9+9e99O2Ƒf{j=sY=Y9LZ*CiY-6iKFbf'vI_Ҋc k2;8+'r?+\,)OYyJ}N ^ uj?Ts^{<|SJKmjv:j|z6].X-Q.$sdU#gM}>wvN3͖>(7;69#~pA"S9RVbK^9:3e~f^\.IM7{ ?8p 8ܒi,vG%=wǛaª=;k̍Iӽ24 M7<suNJKrsl)Ho[5[cw06<v ,r?1Y噭~ゼߌg$Lz S`kx,hgF~^xaySOUŰ27wIkG%M4*88!W~zE3-@v7 )P/j+uMNJ>]jC׹/˯>[U%ة?搄2+o]<.GǚI2'Uc5:'urU$;7wJ7jܭs]^Ǭa6ءP=/ZyYKovA{wЮ)UTm,.]rB>_]J +-\EI-%ϸmȲ}5s?:g+4.WҲ)5N@g_GYÅI/8s%}Oo^o:[( >P1]7<`R`\qerlF1}T?'R ŋVuGÏu^TmK_~Ic|R)ebP?Ԟ ^ _leF|VxoS=S'.Ied^`^` ~ڵ!?OIg>O?-(vZ"޹M|j@&L[/l9)ś੠DFޕ/ ʙMg̬dgxk6cbOC8ӗeҜ;Ææ }΃;8[.U9;c6w޶Pn\ٸMYPVw/xMX8Y'K'%ZMmLGDԆ \4/ߪޘdL@OL$\Tڙh'Om~ě>-Ht**޴AjՊv̇qn{n/.<)<]6E1fsw۞uGG1}w8SfLd""sR4w[NI9OG^{dGv pr$˅_u _m+yRwN_,!}37Q.+α?QۊoذAֹ5;#SOtC1.ߠ6^/sݮr4oWkjwkJ{p `lpD =jW+:nffHii#st~}Aſ"El 6M a#;--]2Zm=2ty}}Ǵ vF휈5 m[؉~q}]DqbJmv=^Ǟ2Lu}4SN<:,tPw1>Q# i9g#G>oUWVnU98p,q ,,Dڹ7G <~_m*n*L+.(tUI٧;֬R4Ld^gCyw@&o=+]g.:^4?۶3.ay]Sܫ8S )7/0$ia fKÝ uf-flM,aq幤tvI%d,/%ۛ-Y. Ih,$U[>A6t=<, u޲Sۤ~x?~_~NN hh{K11~{s|yyޏ=p]vپ)ݟ}z) \9ST$߷|fQ,t3/ΈeϘy=`Oc ]Ȝ<Ҵ ^W bKEnF̞bf~e7@9bbL4_y`G4^[l܂\W86~+ۘ_8.}6[c8at:tSw\/pً.hlV^\\zJݦu>T!h[WKkܦgc8 ]ܟ,_<#je׻w&>7C#FEr >v=m+]hz bwӇ`riq.EBPX}<#=;52y}x 4@{8{nچ2θ+'>|M>e0~yqV~?b}=lUϪòfb7)㋒6_vs4F??-~Vx7ȱ/\i_K\yչ1鞞<}n?Ƃ9+;~ܦbK˷)'o)*Ӄӧʼn4ٹ/,s_6{Ʒ_hvfNN@^.H$ 2۬{O|pS4xerkTzi2=jlT%{ ϰ_HfIRs2?*~(mů`9i~ ?co3vi׎ו>c%IHʗיB1g_|).WnvPvvoNN>_UFF.1C1jj?nkU1hcͷ`~ rKkuFiġ/ptpyEzGtϗJ'Q'M}Vw?=gxCgQߞ۪-[HBjce&ozvd9~4$>^K2T,:;6&Xl/ 0]5-k_w8@oo?<\*V|~0Cq>9,[]p$lvǎF`QWW*]o?xcOMMh 躺:wJ}5M?@LczbAgÆ>:m8=-LI e'ćޚ>4(ύvasONg`'Mq0.~ۇڭzNd~{3pʰR>v6~j0l\l ;`:ñl:2D'@t6u~nY1^ŋI 0Lc?;W懇+UUs欍 }?ɭʵA_@ǾY\ZW]= ~R\'2`BWiY;h| ³%Y,/E㉦7 Z<$8:_=.Edjt8Ot$*/ \e[b&^K'N:JrgdUdt=j'(w?NY̿['xrD<&?}T~2*k^/;C 97ׯ8_qvwNY]5I}G" ?;6v-,sbv/&&`x44g>+Wcs̆ omH¸.7zA}܅a%ljFmp8 K꼵i#q@!Xܴ79z55;fjQۖNcEi[ >,kG;pb< `4LSh;g紭j߈qbxq x }w]wl9n~Bf*öGkMXZz|mƝXlG`㛧s!*2]ϟ+AY%e왻|oi=5,!`>^㝖Cq-j =E!:3m(rz.6C >}Ϟ!"tC~6.A砟8.1m3UPSr˧:DhngML=7JcId\MtV#R;e9y򛊟:w)mz@ PTir lGl{oj~~#=.v.ہز׻Fư=.~@糍Dpu|I]:M7+}<{_m ?8xY xb_rq|>)o89E_fQ GBwN[)jsꜹW݊7p tǯOJfKˎO$ Np~0==d@U6!dc㯸q%ۭwtn$^Y8y ff+7{\ "ݭURvbJ?:e@;_߅99{Eq7+̨ڭݪcsiL۹]X+WU[&mut}MM'&KiP]jz=.i֮Q-u׉Q+ӓ899fWlp4W 8nS /k.|xJy*Ǖ##]#}=[vxh$j,gϞ5?z9qel|p<H ہڵkO=! y<,ysI <PM~)ǏB\w*ųio{-&DF`6tl٢[v9Z<8{< [rw;zQŤLv,'ڵ'ԾQ+>v ̶{ 7Hl|4lolm|v?76|=/b߉HKKtǀ՛5orͱO<9(ѹp/_Ign"1j~[E`pNi(khuΔ' 6[UvOz\/*lk*k%\RF;F7ؕ1˹h{[.Ïu7j/|f3r BpeǗM%o ζcup8V'oʃKf13_5,Rb[z9 >˖ٞY<:({޿Gftd¬ŃW<t#7$XbԼ㟲|[tǮ8?6b&7dV+խRqx B, ~T~f+rI2sn~cpp?ΫJ|i4} ux@{ p6r1z|}VSKwRqAQ^N~ dO$" |SFΎHݾ:)Z[dyn>Ki\56~&?㷍вpx,&R|4'x}~gpc?&+AlWKQ9_ˎwl<pt$Ah]߹h#x5IYbTmϓM'_o-|6rZ|jɛ'z \qC^ztQ|ȳط{-n,##ӴyB߹d]{O$fv͇>3s0g\N坭_+-V;my=mCd +9p/b>xo͎u:f~ff˰3s+?Ve"Ά?N_q8v<>j%G߫2W=#/(kۯY jkX>Z> mɫZ#u[v'0=%v=/d]&mWDbtê Ī}+v48 qNEpxB5LZk KW~%c{c 0n)@`h/}-]p4祉HkQ.Ĺ^رCnS;<bX)$gaW^qUyt>[`}c؇SQ<,۪-&1ߩzxDׄ 0@aՎWi+ݮH4N6$xtz+_o@x''>Xsxv?OvzGLa* KH^>81!Y sV*]%x>ٰ]/wˑV77ʚ{X<s({;=8O3[q޴bw^tUj0Znzwyb#X:̵s gؗ~A?-ěaˣrߎK:`n4`F=0r';6[!ly_291ӳe۔OvU^+eӻ6Y 9< }A㖗?3G{%) >}8m l|qk+< ƵsLnMHѮ"_C0y~@^@;{o@v=AqZl]YK~N4#V|pHFWhݵRGGç_im}m?hN Di wpn`pT :wjթF~Crt 0ɡH꧙ }`aa`H2zIb5ii&+&c)fũ_LMmYҤEa5G l<12rNi$O"r ߉I-SMޒ 7`>yQsU`x/ 'UԾLy ;+*ĥ]PK'&JK}r!Ũm3B rvcz.e/U_lęǘZBKarɈ%\~qx@F@Ϥ 1[C[CR^Qnx`OSj(7 Zmf8 t GW8hh@G`?(?jr_8ۈ=No5oG'~~IޜO.c:6Uػ`مncˢoW4y fngWͯm2S`1L&~s׸'-W}{cbN9CۥQcn'ώ<^䉃y,a#@>>ow{bŚ2.,ƜuE, og>qaBFFW=W'mo_ 9:|ssć{e[YУ:vi4jZٿa)'G]LڀN?0T;}'k3&3 Y,0wU,-)2/ɗ63h7}FIt.*?햁cRuJ6H٦2 i|*h Ǡd5ə[~O{2~hIeh7co W7wHVBzB9y9g09sY{ꥨHFYVss{4pʂi{S&鯞ql;w\([6HQ-?:wV\;>(7?"DpoʵkOT[ٲRW01 =%f[8:vТ8INߤsS/7 3[i 4>6>csqRmyRȷ8GZk$cyHv+vsby}l%1v@Dsw~8A]H>}3jOR =5m)XURVfAGgm`  JP\CstjX.evm=]w;~Lܼ\1~S>+9A*Wy ; G ;)X ]X  <846~u5\bOL-[S0p}b9?lܸS5-p>Sg>c~W n0 ~#q4_r]^kwOfn̈_v~`deHV~gL 8y6A[؊h6}MW0bhȭε>Ķ_pfI>+<?:/WwU~`T4n˓بZfΓSӖKJK$s5o7# N?$[l}VӇߛvs=Źu>p}'}n1ɸ>8ˏoN@bڎ߀}Y!ZQΕe{ڏgz%JdvtV~&b|_ wK|:nqi:\U!ZFֿnq6 qЉRЗo:h#jP+ sz3Tx/w'KN=h9?|]^\bu4Hޚ<れi-X!|k|{lS_>e" HHjҴH<, yR{c$V +!dٚ5cW[]:.#}G~k[x_ d> nyꩿ3,Chbsp3/X1ik{?-j87̑6L `r\ RZڠ2{ôv8 qwB6Gwڿ/8B;m?=Z|08"'O~(ۭ& @0xӸ9FжT/pq(xpx~S#7z>Z2qd>CN?eUc?tT}@4Kg.P>o? ;Sh &Ujs6WqԺL*1s=䡦&LesU}L!S{0%Yz{jXآ+JJ&'BX<;GFGE:EvZWɿ{eL!71ͷ㫧v/q Ǿ=>r7 #`sя~Tyv96ob.w0l;|hY:W\UL^4dϧX'\vntZ^ÿ/ᾰ";s2o5,(=X*r9bQWZ<$Ȋo7Eǟ9&pA?cˡ{jɱV+N=<3!fo׉Ml .MwrOxFIF?'v~4׭U|>Ys;]rѳVqӻ7;>7M ,W!K1Wnտ#t7m4}8<F8zb|7Vǀ<,X}U'W3<\Ăl|F)l.4X﹥[}2>5$O [% ˍkЯј':d1h+|NsxV)A;#o|E@c|8~Y_.M-}|S#GZ `Mix~9k}pLʑ:)<*&x r;[T, ={kyfxnxN&E-1ق gm &7`(x[e8&(:[-FXvbZ f;u,,ž*Sg-~=jÖOrz[/łV 8ݻ?/%cWa8840e|.~=ȶkt8Gbx~%u Zrj[^*.\ fȩ_<$IO[<&5h{HݸM6|;xT&#ݍ6$Iz~P?- bHU~+7[_ ks9.3ϟA Xkn sQL3?E,ZN?@PuMYYYlך~k^|UCl9SsLt;)Xx@;7z1a^ ނrk'O,?M^*/_=}l{MxۥA|f_-6wl<u5 /=4ezlcdՖ .Y,|h^v}|4lخY}'KcLK_Z]2[]<2JTj`; غ$B/>Ye6.ЍGFėOH;m_a-1:!q7>[}=}Dl[3+N-ghx,ύkZzxJxP08T8Lv7::G;FsrWe{j ̬Lb>),+tjV "_Bߧ%Ҭ~/pk䵱 {ʸчpj:Џ;z{H]`SFR#y-yKWV+5,qpd쐢"-y ?UC ?>}nB7:dלĖbn=u~PG4{8(pg ?&s oCt'J%!ƀI|&q"VH ?kaiG-ݿVʶɺd5jfM93V xg&_X@hTR,fMf}~gye\Rߣ-7d|Wfgvɜ@ [Nŵ|meLO̩73s9K֨Tl+z7n&bӝ$ {r _O!]ɺ3 f̌(YΟ׊O]KGn޴:˥]UwkE{H ZH/8z}xevɅgڭʱ7)ǾYg`7κ1DGZyͳpyr)$ :%crۺ*qw9~cނC/ >@]T~u3o}tcxMe˃[]U׏d5P[uiUoxW1ݔ/jw<25-߰Qv|bihYY2uf[>@KfZ߂)+N XxoZ\>oWtltyYlT} lj4WNɅ\Ckm@+WLnjAXgV[/c~oHtT#68e;N^ipA  NAp݉+n?y_귚?olf~)n-6xvp-vOl턂lg0-joz Ne|/p /~jhYΝO 3ep||S_;e}D]Duln'?Gtb3Rh !((Eb@wdqE O;e5U|3:r_&ʭn1<@oly&F:cpn`'R)+#` ][9X#ߺY۫\} <;(=RJʟVu8/t.P\ڣxӿt;22aQ'zk첚x7~ƍoCOOOak_ԤsK:؝KK  hĹ96|~2ϩ/5 nGˎo-νn N{':2m-詪iqN bl|g&@g V1˟7>!>_:uu;M/!n`[wp͚O;;5"ah+K_L ]1I `|O$YbqZyY7c᠞{jw#57JbO1/8ydPY 'Dڢ"'t[rXS#~Vzr+fZp%}.P$sғysY#:-zq9Sʋ 6Q9t}[,>rƐ\uNPq]S@:# xkASj<kyn3v*wLJOСCccǎMR6 @^PӇS|paޭ=dAKMy Nq,|p9=ֺM웞\hӝrNdߤ̍YIG,; )}b+PG.5-ofQ-~1l=:jW{lg0z𯱋c6׹;%+e` '[;3N}꺘] |%UK3j5]YNZisg3HVoOP^/ц3z jV]G듎wH/%]/?cqx8<r6x%j◱5CRbІV[3Yb@7Ko7ހ?ًLL^-onRq[#ҩ5o #'G䙿~Fvwݾy.WnS [Щ ~KŎژd\W+Rd?}G_<)/ۼQS5,jtHj=85 +,v:m0N~`-!d|C?} V3_uX\\cZ:K05ujo5M;'"(1>_Z*@@{f73fW91˷j* \ qhp+чb{Y/{IcqoKjLg`? Z2kLN/H7mM,cT:{8:͛-9x?sp̦~~|p} ؆/S?i7Ӧpّk1&hsf+27q| ; .Z^Wsrsk&'eX?O-~)]&OM|Yp^{rwǮCOg'S/% \>UgsYcz^Sd~b?|CXb(axhWf-qk6 `,fD]lDsLZ:`}| e٥z_}MP̈dlxp5ilѳcG!^~`2z3TOPo?\T_2?  J2wmή\.I5!4.l=A^kΏ[Я>j2)9tK&oL=U+|0&fnc$nj2ƣqy=ղ8V嬴+^.!f{ p".ZCFAm xmmSWtM+84jMnZu,w+gϞ|JGvzyOsug-a=lZ)|qmC.Oܱcrmsry"|a#O;y Cfg[qq b;5CGmfٸq%?==9pࣆV]c-wچ~%|)[Nj!'\t;}{߰!ۇ+p\EKkjtYǩ!3IYB~t8D֧|-&yDrM1AXcux8@4j{DfF>풩_OKzkZ^@0HRݖ@wO/1g RZj9# Q|w15pi)ާ-_cawS3|Ep].sVJ 뽶Zw03EuS7رXbRuh;s)1/v* 'o~O>|d@S=|/!b8g@'xӛd\yp]bu~~#kuĪ17ڕݣVk̩ϗگԗ!n+W:~!]ued핂#*jac?IxFraiy5ų,Wr0.貜YY9}2Ly2pp;p^elBZJC왇ѨFKKH s0%1!CcLcߩ| Y;4bԗG#&&?}9߰V^2=E߀??xa]rD!lcX3im,3>2 :v1qt '+gW'CCr;bol7bu$'V?ȇ|lmX bL OZ~rT8hd(bMP^ZU!pץjvBXSۖAqrД5&Nlxdt c{ #2oXg!OʢçxϽĸG<3_+%sSj36P[Z`wOҙ%y׃K/y<_ث]_C-֭Qv{_AG~Q{d߾T{='˟PWK~., ͙C!l1EC`g`~H21Tg*ov;kqH2ҷ)?~xpXkptI !OK呆 [WgwكGn_^JMa+?\Cݐn[v[\޴94_xMLa<6 @򅨾S@@Y gVx |d} P77@9 h)@ VSCrayVf5 uGzkq[+J688J<>w1qz=Ç' 6?}x\,/#}K<ȑ#9$w} _m-:V[/(J_\ `HfY3s=?f~|uKk l%BS+@=>BeryY5sl:ks>bMs\ʶuYgl}h]#99֯LߴqXu  GW KiҶ35mNȼղ/tr0謭ϟ\7EbXjsSfs7vLk!ϯIӝI KK=NNq;hXdf|&M2?3xJ $NNF<8jZ/  5 Ӭ?X }mAӏJ#5f/=`樯oPj)3?g}sK`-GjA}WzJIk4$N~ٺE:~ͯ5)өh0?xA/س`@=u7֙85 8/c/ΦC: C]ۥ|SDg,cA{/~t=|GMC36]`z:bznby&k`3SO-?3"gRQq86s1ycj̿lMּ]Hu[\L˵4}Q74h[Mvmۅtax}}z]ejrOظ= k9[z|ĩS\Ը<*3j4EӛQ 4*F1؀!vܒS\vvmp:!$ıYY PAH#ihz}^Iv?)߽<:;{4_oLZVP}Ύk"YO&XzzLII nDb"0#M 8W@r f\'b$Z];v> o K0smmsZh_XC F~Kql/%8DOsw›1{<n p:} `U]}Mpwqp:~߳g|'~}8f~|#?b7xþjݠGyWۖ/lɴI塭oJ5O8o0z)yNBp o. .}~xIU}v zqVz|yzmMVq[ռ48w6r}եkK\]-|X]x.׃Xwv}Zz^!O|O~\q3/9On}1? WB/%xk\<3_:3f Ce=/E{y-&^ÇX!{5^kϵɉKgb`Lo y[d/Bm'߳Ev]>8}z9ǂ׍/ b@pMGMĈg{>'ߙ3 @obǩ,SMz,FBk糰/wumݟ0"C>[~N |!=NFq4=đp#F{ϑ-PPgD|=u{zso8CvVRa_YVfֵ`x[vzƒ[S hѫ֏v?jq07T]a&-T<Z5zZ s⹹!N/y9Z69-y/>-FS<]]0`ʔ3h@bisE&zmyi؎1CfC _[[OO>08 Oש2wd_ X@h^}Z+k^A8*F L4sdfVEjGM*|DCm E\ŚU&Jϧ><VxzAss䫯zlG|;>x`|A 甿>wU^oh>6ZM~SWc|%# tߤտTfZ>POF/ݲŦΝ9x` L@ 掷[+UVzZ{#WWis6Α 3ty$sxqQ0>x\jzm6y?o+dK1&-9.}xk^LWAsbs_IHz<+Gv'Pν*7x:|ӕ_N :Jyv[&Gl%L1`Gf Q=X+}*HNM-cB{{COl{k RtL&x7G)-׆kFC?܊<ě㙨AsH}D+&p/q,hT/ 9?sGqg,н^~]{Qzŷ-ҥ奅yK17oam|`f( Ƴ=zCxx߃$+>\m0BTV5jұo{`ZiB={j_{ZFf~x'Fp@)sx=ti#}`&uJzQ.;niL- MFshTخ5.?q8p`.Oh9FO>@/ 6χ @<ۢG\wmUgt?L9MUB 4-A*gMwe+(hrR񴬳y9+b%։_DZ;jx <zfdYv;qk>zWܵ*\5#x_a̲\^84P /T'z:TKtNw~|"`cP*7u|t!~q,\m604`g?#//!z_>Kű>!=&n& =Vmc*Ր^΄^Ufrq\G?I4z}}#+m﯑'I{okOBZąycµ DXgVWwȚYq ;p>̝JQA<=:@}};=Pg鯧;7Ǵ4`65xi?W> %Qf#{]GDmב&'{Gpx=\_o_<7]z>zbǠ' L0; ǘ?簋23+HyT4}~D~>AXAjU޵lZ>~Iĵ#>ݡ,h=v$xN=; y Vr<?cxwx{8S&"=ALj<5|)]`%SGQ߸ho}}%U'uVg?G?RZ||v}+|{~Ǿ|f}خ]{}?@'OǑ] rwbsTND_'U yi/q·|m]"O++^\0ZAI>:t]GYr7ﳲv8Y#a>P_rkN/3pv?4NVy?}}u܃_H: t}}gsT & 3CN~YY{r8F8Ƃh[酓GʱhV $qqE@=;|NßPs }Z'/gE>uhlT/ xtǾ{H8L?qBw4p΀V.YC;;пkkmzbzyemb{ W_[tq~Lf39E:G5[WTO;:ypcXf8q|x ͺe\B`oAe#Ͷk5]#thF0x7M[5pByZf<7q4CKtOoKj-j-N-Rm0Lz5x'Oކ<Յ[ p'3T&0rCI]l] _/@1h>Ko~4h4\W-)H(AvjMb>=8鿬HB/ gOoҧۥwB\r%kidݟtAhXGbSO=^_~}vݳ^xy91czר wK^+Us}jEE+mϞHCXV±*_{Psy`^K-: *լS9Tzz|r}Ss אyC<-Yr^xsVUS.]H7:yӛ惝'7/($}ɾo<=/VJWߣb0x|4h 9sK{WW54PXH=Ξ}C)8 'J|T="! ܬsOgK^8xV9sϼ,?ۀ9Ѭ^8wkUUy^w?i h9IIJG0 OysqG`8gX;i.x -]%A|ѷ6NNz0C7̖mosmK̰>^H//O9/σ#{`6^r #vA_/?>SGGl/HݟzPϣrR=XC aknqpy8ٶ^5JTOl&CTO}ecUoI7_!z±g?J?/CN7X3(6}síG[zg4zC6}t/ukD:y37K}#G@q9Ddž7<Ђj>AT<=E{%r+sC?c;}G|!HX$- 9.?*}Za7ڒۖh.א\7<؂|c5#IGerN=wjժ֍- Kz bCj:?v_bjǺWA.} ֋ۻ{;r͗0<]:q >b" <΂N}"6ו<{X@=~n¯ ׃s3K=vyey3o<+[~O{^x)ke3^ico41ģu09ƽޫh! &׾zϞ:8fȇnɒ[3/lǭ i Ưϯ8VZT;y[ޡW"m_bQyC.<7@ux㙯zp><ԍ]!rÿpғG~O؍/@v:7bRGz@b1§>)qk\|b[i^9)ɉn.,2G'wcX5s2έ&W<S_}h⥕cpz4r2p֜ij]yaMàj1k{ɖ?Vp5_:zI|@fQzKJ\8վ[k|iʯ:+Vn OM:K̾WP+jw/cfFXzcGVap y)p xr Ɣ3#iD[h,pb24ݵvߎ[f{CYgg ;!dߚ,M{T+!ރg O ɐo3o~K:daxg[|-B/xT^8 ]-4GWܓ\ V,hs #K婘pm?z^i(5bqy nyp2V?_mɟw'v'Uk:u <GW-Xs}m;iG_+.^.y]4S猯/*^pϼxjy=8jz_f2Sʕ~+m܍~Cjvgɿyc$D~fa> դ8?q٪3s1OpE4.>ڡ=,x!8V]_;5d?cӅVۉ7 K<̴~jpO+_1ӗ?9:J`O-QȃxZqw&F.8Z;D|`'uhV^+ e/cfb'J׀d|^BY dz`ߵr%3c/QOݶ`srx1HNC^$8t^rZ*m3)+Eu\Ipy=zt,`/59+ujнOU <'QgnuhfNIת*kjSMs-X~-C~&`fddd.'g` 폴mӦFˑD Z/.x OfV8>~E˂7c #B=x烹}m'²e\~sa^K׎\=x< o߱8s^+11[y5WnbKX<>[:1kh8f',?z=1KTϱr_ƕ 4(>C=رZR^š ڼ:z;~ yP wJ=c1omWQ_G˗ #>^sAϛ7ſbi~I-zH[1F5h!".0;E~Q@ VŏyC4w*ͳmC෼%ç7:~3y?9>1 O>GG5xf?u_yxn拹KWrsC{>3_wYU[U7 5P=d,l?6f;;io7d_䣬@^f8ljx}þqji4T* k¤"'8O](s;?μ}?v/&ÏmVt3ʕþ6|JUX >=w!؃]A ölK>f& Ӄ?C⠾v{jn;͘߇ZBO`|IhL__s#]+y뫶2gؼr7+GGї:7 ;>^xz&y> ^ 4iG';GZ=9uf?Bf xqG'84$-\M4wP|{z # X3LS7`\k$7#8hUdLJm~wj7Dor;0G{*vBO,S'+ pghz HMYFq=>\?Yo(XF1΃C o- 1 wh˨fʲK'J{{XcYɅ,:fkm \z{ut+U@+XbgUvi%V;dQxƍ5ߩN $VKsl*P!5jZx;nkP&j`97cwZǞ䎝;5KR^{lY录`h#=vจ_~*qs꛸/_3xv_ϝVƊ?X9`qp>m>R^H*HҌlɝKOMN3so8.i3 >kH<w: m_T9pZU}ޢ~vG4*y/d_on̼Lv~ ]ӫ{Zz& o_h%sKpeP1ZCb;i34:ļ|Nv *O *XjW[mcll[sSr|=u@ ~__]|#^`# 8Zgxo},\xm%,3+vx ySg' zt`SOiS-tU9^p?[qt엙?aV|bo\ lߥ;tA+v]tgof=ߨzHW܅G;֭Eu*&Ən QjPm|Ĝ ?:/gά [fϮOF|{98=PCvr3[ƍ}-!czW55_RRbsl _>\1jx$FgGs~D=!N wd~?AKO9?<C~"o ? ,ijx=℉H4g p)8+Try .* k׮0)\buxz؆z?Mx 3 0ހpx~K>K/_hiiќ?{VZE[Ox\cSBkgBjQC{RsOv_z. @>/)) ފQM:|X񠇿8<08vq 'LOHCO !@Wg7*K:u 56myT9+P3 |]+p v䟜 5r}r7nC^+G9ң\=a= <镨~]'>"jʫ\_5<~ѳ/8iTSYom+ ycZ[,97αEVTEDGy'>eUsY]U]@~ÞW]z=FNʐa7Kj(`=lC΍.-~!6Ȋ){{O Oҥw zM >=h$b0|t.ڽC}|J07xΟGo/*Znǎ\>fU:\ l'v y'z{E3[ߟ6m<靄:hUaaKwGӧի? ;k~;XZJ=pf!"ܙA&t-Xω=س+6/ݾFњ8ރS#mf9 N˷e coz>鑟pDz8z &=􈛙 f=?6߮Ï^&ƢAS(>A 9+3 8xր" 0#ɯ1g#>KutE)I5yE{hsy =9>rfG[3t~ϸ4[y x@[e=v`u^+gꏮZS7*%wD{GMj+k \Ʌ~ O$N h%߃eMwK$p1 ^㸅m~^eǟ:n>הWT0;-2ypH: pҼO%7W}n{ԛ 6ȣ~t[60y^F|%zj޸`69(׺Nu`ѴзKEN( U+}Vxxlgԋȷ9W'z{Ԭ) z>?~;ak?1?pa )EOgB \~GKmDJ]zpm81:?g}ee8O_~|k׮#G؃>ϫf\n\)^ʫvv qQ.̍ YױCx]ŊQ S] 7xմkA_}Ѭ`[@0Oۅqɩk'~yڪoݨ<ߪ~Qeg^8*bMlyZY[Uǟz=^ .==y5q^91GUMz >f hfDS+{fB|?ckz|<0G: 3* =ze=Ew+FFL_vw᥉$5cSG@C%y35jv^>J|EB=0ُx1VƤ_GZ urz4pL#5&y MY<'KE:)%ow#]Z<ek]bܻ:쵿xVjPǯ-׉ v15c\ߚ_،3X\xIy6{Ol AlirFj_X 4G^8b|ϷW|>Sd=7vǏ~1߯p#)k1S]w}ط;k.?=}'~ʇwO_3+E+u0g=ra8OsС1r+i>$Œ~^qEulizz֬C^v4;(˗S`Vt Hקtc4alWm޿҉:M/#f[%.yUc1 ||> ;v|8 ;+t ѤDs5/@:"c`05 h}LC_`pPͬ\WxOFo1"_z!U>z|_""xL|u.`,z# {DQVubˊۉ'zI8I((c["&P$Vw`Cb ?3_ GbwΝ;wL&HKKR)Ih4* .ٳgK )zr|LOO۹W^YfI}}%-2yw{t:-i)Y.|ledbbBR)?=.U;_$$%!,E5EOV+l6+I Jb<`Z-16JW/y&Sz)_iI)(yzIHv*+KHKcq1ћɨ&+:=K)32r`Dj$ˈ' RJbbұCׄ%ϗ'y2d2ofLIQE= I^y2l3=9-dvDrBvvo,od:/%?'hs0ٟO>k㙘bJ^m9k ]3O/S=%EE5R\ܤzƸ]q}`E{/vJ }.ѵvϳzlQ9#3q~{]Ѿ~ h[Uy^G|QG:Sݡ 6I}nG䖙aȲR Js_RmgJco+ȬPHzt>gw=&snbWΟPaXN'{zdsS4UU"zLcmO}r_盗竘t{\ؔ9stbzmDt@c8H"]ω%r~xXfKmI\cJٳҪUY)\}B~p}'Xd3ڙ5)1XL,]###/Dt.磣rVҹ9a{;nλu,}4koWغRTgڰ? 1ZJxX+VHaP ;s?8 çeKe掙( HCyrƀgJCC_/ى=wzjڞ <#ݟܐ=;mȨ(gG? 6ϒ}Ir-G|d\3SRC)=O Nس/.Q]:KxRqxNXξuV>vP<<qFva:kx c_V=bY=g 9|iuudܹ񆧟~Z^x9z3]c?O̙3r͛e?^'~X5j~vLѤ:XM '&uM\jիEuϕ ur̐akI\}W>!/Qn_Lf)((Ҷ_ubXYTT'$]5[^25ʩS#jc=c7CsHBfh f-Ҿ^Jz\~ qq8[n\)ߩ^ݟ[qk$W~JlL.^|O+DTO+\UW'GW6hKtnKvQ?m A/|b7ͼ%ڿگJх־"Cz >nA=g~_wǧנx Jq?kEǔ}B9@)ٳO}Sc{/ox6"(,)ssw~ϻ xDžgn5 lO\H&/w0;ex 2(F㶾.*5Ƕ B9(ًa)3 ͖ö)/ոٱS)3RڦT^pSvߎO[;?S)=;j76NmD.G.wJEBq;'ٕ4lYٌ5˓:./H$2m@J[VHLJj0eOL,^֟0/19.鞴^V]PlIɷg0zlTFHխUQ<Е!m5cs^5mVD5~{ʚdpL)WiP, 94 þSx>*oxY؞dRZR~ul`{R?7i߃͛77ްYog>#/6MltzR1C~_(_8,UںFZguR۷hn8єatQQ~~Zܺxi:GWj]# Yz̠v0ϟӺvumSz/Æt ltO?j[z̸.ŋjߠ^S풎Zz΄{4 Zzb{33m V)^4nRXX!=*+a>3qm?6&soIŖ4͘B"ɘg?+zB:C%7R6zMN7۷WPڝ1PxuE!过88~W8S˰u 5{{erF~ml5Ru{_S!FjŽԴzoD.bFϢcc;4rf{7,^k׮sx490ٲe8:3yk6If;(~)|'p.7ώKʃfӢƎd¸]+zBtMx{Ђvp[omUۅ׿0 tyR0W{ܸ鶊57׈k8'Hv:urPKo{iM R\|.kx\a1n'eKdĀ|^ߕ2tzHݿL}yB^q V=;"lHe,3` ы:82>aCŽaEMEwOv}sh,odu epϠ] nJo̵8kcv6;~{B :Zk؏< OPm+q_ܦ#CCrwg%3üf7`qOE"O:({cw}L/'mdpuk/^! 0:x?suСCjM֯_o~.qwJg"gyJ>Lŷ:Y^:mSq_qr~7i6-xfݎ_}6{5I\TT*x١^JW>ZY~g+=h;=f@xi Lv阶WbϜyR^#slk+E>~Fk2ށv\">xG3^[x8ϱH\W^JrŀNI%>ßzKҥƑpzN݋S^]et`qb;:W  u] itpܽj֨͏ƞR<Y[Z[PS#swN;lckG=tޮ.Yk#`s\c zSnY,s'7pSJ>|" `8(pI}jC!l,'0&{oGG;a7*G§F.7os~xW˒?]"7ϛ[s7-%k  gh-Ɵf!!lpe*:ev2zl>Y =YC4b=8zWRt[pk4*O΁;qӲG{dgJqCLM4n u^|7i C{dW69ϙcxpZ7>sۆ{ W_cb[~,YVb~}[l˜ҹS6FiM^,vg ȊOYJif0656Y/0x`zb;?ųmfd̀y4vY\#"q4^J "˼̅%fа]"cX؍C/¯ge)۽=z[-ksqhqDl,UvGyV23iNJD*ҴI7u3%KZ 99Jޝcm N\ ?_رcgŦc{`gٗv ĺBѩSbh?|/AMKx7w5-i?ׂ/TW嗿|B}%ŪYg:]n*]4âbU^z K[G@Co^^\1I]+.?#Yx0z?Zq<^ގNG/˵3 k~;߉Q(cF/la>ϓmbGjÆ`>|$rDH6: xG l<#j.b拀k>K۔gƔ%d2IW(7}q>}w;{l^ڎ%?7olEw,抧#f vJJ_Ndxװ˖:k>u]7PA0-Xl:k`sxl‚Bn^r ~p{5K{[:xfO]#Tn4MXؕyҰ)v:n;o򛯙x3~mᎷ:ҞK2|~XOU5s^; k;q FvHՍUw ]G͎sS)2pl@޾T5+}6'FbhtRzxEֱMP *VWH^q҃p#@}Ϥ3oZrȄ:Pˣ/H;f~Mje-v3X_a^,!i'g(3?3wCÍX71(*qgF3f *\ ߾n<Ưh\URvLA_S ~=1Y0iX 1@{W'0g)k.@ ؐIG=*gxVfg?>RYX>k >ElmNobv[@ŋNAf o/YĸǷ-2 ˱d8v˓OZ^}%J|3MKokۦ}d*quw1u<~~[9D*oX 5]Сu nRw 1 tFi??colxBEZpdbo*hR_Do|&^a} A\X԰􇫎h~l6m ti,]btbBԍg,s} p| 妯o Ҫe3e1$:?[$tN5\@+DkKc'yt~tﱀ3uEgs>Ecz )_V03:gJbz\Ger s x~T>US-1`7 Wm 91c`B%փ/.7FƲc`4޶w^/`w-[l{C@cbq!ce׉Tƥ!7֊o|ش\#;b k_R:b93)lTgkbx]؉ҵnz|Z Y;ؖōjb!7p;'269v+8_JyހQ{o˵G5y?mѮqScf[o>Xŧ8=-ޛ5^+{jJYR=ll*k'k1asqSų-~ri1\Öc< =,+;}YT~7 ozp(52h 7p?gf$^oVE, A˱[$ O;[w-KG/ŷ.JHV}~ԭO,eyh'g+~rb=%j3y%da4^jy9gǻ'.˸u^ L&g+|i;sƔw+]Vj񤩁 HD.qAUK -2g##\[)vȑ|b6϶ʕ+-4G~0LV@GW$֏c̙c:ϿsloNG;h{,_\VibmbM'hʬY-D~':ʫ ۴sur*ϛ-\Zfk"i󗃟>_X;i}*87;v>>~i๣ɇpBM!x5ْ799h<1vT ̓={tXt/k|T۝6 ~sPVl5=gg 09x,'q]>?B?n=´a|.fy8zB}/#?bxsL= twU[n^|`Xk M N-|MS\L1d7 PnR1|4xNE C:'j:8J, ?b5M^)qkn9EGG~aDMt />?j 6w\ۊ(%; }B^'?_YW>]~_ٟ߿iK٫l$C)v-)Sl+6ૃRyck1>ccΖo*wr.&&_} .)5tv057xA7-+,ƐcU.1J{?l~6-|W\]{,2Y~z٦oZ1^hʯ~Y)*WV }-m=G]R5Є@ m{l=op1 Jr3ttٴW/ [+LCྉ?g|%o 6X',0C,`5٭~{j6.ĹSP{?/'!7^ʉNH]{(,tq5x8:.,/9fr ~7Q# msgTlk!,gڸ9J_G{ׄo?h+i#GF/B,+v:f~i&:G=;jNB 'XwHR1wpYrrGߣi&YSX'g|ppLwt|cl lslb sa͢M:<_i';o+bO)jš<䓿R,=M69Ԑy n 瘍7X˻̙;cx݀I=^Vb`|!" 6Ws[v/ {?or>rx+Z}3!ސ .O"NW>o: ~ ? 4"ܭBRS@[\:7Q_X ]9z39Фu#n^.lr{"z,&bR%a=EO+psɇ^bslҗt|=v5XF2VAkӗ)0fûgtx.?5 ޸q燮_Mxkqj'.y`lyp%52?>.;3hhĠ?a =h6k6>v-/9UYV&f|jk]wBN;C-mfّ=#¼B'G~yD?w\2{liOpb,M1mvAش aMWCL/o˩NA_,ۛ܁ ,kDc;\\A?|*_}U3W֯m7c?gHOvF{)[Xf9V?Ag`/\6=[iЈ=G4/ol١YH&Otb1vwaڿi w +_yE+e6[ sC]>qY_h1Y2Ćs WOCo9sAa>`1)p@4_ ڏxe-R:{|F5chq:;q!b'ׄss7f8qh-|O`E;7Ӳ?{/;$' |/?k666?ͺ֠h}:D ڧm|ٵk]=]!x}a444Z/?X?rۦ-3-6fe'  /oʰawYKKWؿISGBsސڛXd2*}X0?1 l9 7@3 ~ )`V}}~wjvL Pc1,J~&/\M2 >5 >~;.M?7-Uypmэ$^^ &:g^ :G?Jڠ4O1|94oll=wFc0|] t4.Ww+ܘׇpݴ9m9ZoVwڗbtރ@&]N1 Ys]ns۞ ,燘jn_#v۶mw\}k]X+\dyV|)*4ZY3:cb*n0d}`¦8BXǰ%aS__:?h:F,f~jC{CY5nA-@ί\#Š,6oBjo4ꕽz6]-ꥠb)_##Hz`}r-.W @;*񾸬uҲ| [k7I\"_ņusƍC *Tظ_xoϬ-7@Ο͵J\Ŧ 9hh*T$evq. F ~p}!8#(Z[G( j|f/eA/0?bwj#y/JyklF8#~ϝ;dҐ}pŰdGAMll\͎k8 cb{6x;jNYqoGvTn)WןY+X7}vbCDY|ƯǮ89<|#su>4KڷL2]39-~9SÇRYIM5NkfGaC=xL͛Ծ7Luw g-^gDp j`)K,vJQ3JǪ\ }ga֛s }L[@BG-QT}0.20pUq?{H[f;+̶X].Y"1 >1Ig^zYvn.1bu>5pb[>|!|Bg]`+~w|hs`/{uȠC<g{@:贞rG]Cݤqͻ'lyTL`k|O9l/tc3c@8D|%}Onw] &A߫.kSY|6WYp'gFo=#: 99a\joPCoT{б} err[<~RGjܚc~+%Jz1I3[|/LL;c& qt> 6k.7iGGNwFc؟o9u*W:rɎ;dwJ3kēX]+0rG_"}%mz?v)5o葃_[v-rNky&42Ǹg˯5ut4 ^GjP/1IIkvs~[FcnO>k XM6vp,s\u =5I+fsXw=||0i'Ns /!Z( #K8rIOSM[v=ںM|˗q]?ˑ#?Z@Ӊ98C8. 1v`זN ۏDNՆ~?u uܞ̒5k>|k&.ozPΞʽgĮO~'fAfkbbb@2}x :@,~'b!NBIc_ӹs::u|Mտ}n} QGuߔ!t ]Jsp1S ۥ:oMg+lHx>?6SnN#qhAK=o $f?pQv+3XFT-ZOxxox.]z 믿nj= 볶.U]%>II7<8H|5s\`w`6knFaoJZJ$r2b:(Xg5tKI'b!,Zu\ -jg˯g󸵞N$ FIVD1w/0[7F}:pjs #DD>eDGSHb ͖;t u S GS0w\z`??1$yGA+zE7F f<5!yH˶l|q7zaOuKzye9i9#v̒/_цgrrvXxχFɽ@s@00sO7\+~Yl2u֭[gFq.Zfh#KG]CO~wbJKg~^qYC5WhO8y^غZVÏ8XsL&m0^ϿCyo6 !I=-oO踭ݻoXܼZ5[@ z8ykUfLwY?q]Y <; #8~  :c3r3NlN6翇á3faǃbxOx'xyy~wY7mm_U[@>7mtGcO}/au!8q3uX1_he<ɱ_XmX3ѯ|'kvj#oⷉ2]U}Ѭ Rj!D*3V͐L &E`}Cv?r#q^^Y'wb~6Vrvle}H˪?]%u[##*zB})J+9dƎf/r?S J$z6vFT<_;2\"5yVSIGв)7`GY>IQ+J 1GiIEŀZ]OLz){>+]!sKSᳱQ&730]GN?w\ilp,=Mb1|'˘7k"G6}X@r8~]G(>x5 qt%b* 3M<E²Rnp#+}wpf5#<Y+I9 >)m s `/vFί߼o@{tZ[p@8x\CrYW8 '}u..\fk3yUC-n1'iz·l[Ž3N[P|ٻzAh`+pjEǕ?ͷx<|hp@pc<y>ˮ.@N놖Ǝ9IuެGLn:w+|{ &_/<<= vw[^ w`|7!G_@/s#u#$z;PU|zl_H|Ab8,,TT+Y>Ȧ\ 1q/}8 Ww_|^-KW:R `)]kFS<&u少KY~Uۅ8q{G>hĴ3RόgIIۇ ]z}N6]N|FN/5ħע"B6IZ@:5#G,lMit܈sæC5Ӊ7 ut+VH(k}$ni[3ܘZĘo[ɣuTνpNb1^T-뿲^*Z+od"N;e3[htvmD)[ѕ25h-jyzo}3_M6'~`PLeCp9\.-]`=;m9j8u} k׿d^^5:wku3z?Y  ##e0F .jwH[mz|:!.@\ P\f~^a [Cn W_lm8^0=Y82mry8#.L| |߉/ƿyz9 8y7o*ϖpc癘0.n~d|}Nn ~Hұ3TGf,_x0-I'~ ;}z,$o]>S  YQѨ 1hfNW_~]n-6 ĖZ؂V# T#*vHujް`ߙu#>vcn*'"vQgoKn9P h8Ew^6BbLN[< 7VA ٩mu%I!Nlʸi`?qV[XWjAC;@̕reyw%ݑ~g3ةvtc_JPn|a`2?8g+E7 q9r 5? y~su;D_ 07 3}=./c3)YezEEyrl,>0<-)i8S@#'F/cq߻/:ުXfz%%K,~mٗc9LP'/X9=>#νlE>YlD+)e5Z,`OQ{w-WsW8ϭ V*p&5GFY*/ϳqtt|#Ϋsf#/ znNްWG=n~#o kHE.ίw4G:~ +A-EL_n~_ nbrxߓnL?\Ǜa[@;u _@\\|nNS.G1{x<n 7p,߷³¶NmV91}V[ xMe'WIS7?7+`w|ɦzUsGS?/k|vzk=Z |9p=rt PgN9No 'h2۪ Cv3Ę]L'҅i9ۣV]ҰW{߸ Z:1n`1n`lhAz.6gbpc&;0^dl°dY'wʇ#3|)e,Ts@_2xbа=:1]%# tʡYݻ}k!g-,/x ӧ88JN~+WZ}"|*8Vs[V y^S C0!g1|FC3Bҕ?^no[kqc `;^BZ[eotȩߝT"e0Ctuu .s‰Ql<'.P#~| 2 !>'~pLe}Anh,͚cOG,\l2ma09?K G q]6![{vyb | ts':)!fr 4ݽS~GGgTj$׹ >j双;rNiq0Ld64p@l<>k5qm:kߩWf:h;:zLf<[.((g$mխ8!r}/\xCdL. Ts m$XZxS8cu8 o+VE4B,!1eg5n!)1 * r_ejSSXI9sz1G}ƽkܽz!7vO>r5|ZߏzO]rwP06rtwW+_-ҾP-  g@]ud|us;O>`[4n=cV70;92w\9,G~sDJY~ri"^&A3KpM}=X;: +9uKcCm|w(-I@s|6]ɿpb]8ז^41iVg_>qa<$ed?lٕG`YN<B~j3_Kc8yYj/`S MrIӣ[|߭ښJ?KSX_oIN7)=KO][V"0L WtbW^y}boq8XM >6ag0 nL|C؟\=uv9؟yi7G@ @s>r p.X7 =wO)yW\]k@{e7p8\!gބsutIZ>G{jﵴlRn~q߉cGD_ p|Isj [MzCH^_.嗿|#گ{8 :SzC) D'6n^Pwr!Wuk~(ΛBKs }Fl?rVc& 9x\{/.h].ܡ=<5 7;r\,ehژ߬τc|tpt6F'oqM,l~hCdk.*B{|3@!,kZ-Gm=|yǩm:Ğ7Cz[|sK2k~rr-'r,bpɜӨV3; ;1{Gg"[FA jWĺ.~UͭVŞ'-GM7?>5qo,|Wxq. ǡ?,0 D|_.ktԄ;Vx_ʙ")r,փi /r:焜pj_>Y9gO^q_y<t!Pܧ{KX]Brt?CvxaEM,( jLaL.Ш,~nߠF4u-_1n@>1`j{]r\sU}t? .Ӷ7rJ8Ms{:n|=_m-b낿]N;@sJ7i0V{Z$8%GcPw gcZ&҆Ǿ+b?;k <}GofAW' I.\ެj/`P=s!G9wɼ߹|Hտh{+]Un`7؎Mv96?S:}N@6SS槇PR2[VGm{ k-9?;~zG/`31EE3'H9[{,_1Wq`pKfSY]2΂ ޳Gc\XUjױ~פ1oX&abCc7\8\ {٥lG}b&vVp8brg|;vL>8oTDo/_g`{܍ rc4mTh{u>Ϟ~ǾV;+t>سZ~uy}s 7wn"C/969w{.o_ᾼ_|tPg+oX Z&K>j׳`C_Gl\9#/_haYrYrÁi5טdkQGF-/߬$Zaػh}RP'buG3X(][sK&uFGmn19bY'~uB=Rۤ|̩k'B7:{RrY@OxbA X\LZZ>9y hG%)7VWww\+H&i^,:p"r1PV{X_z4arcsgRN {_ 1w_D nxw;WAۯNvйH?˞oo| `. ns{ylxs~E,xLb (lwM6 gٹzs.9(58|~\Ԝ@{N}hg^l|լ?01Fv}V'9jj1z?~NC~w"i?c!X>>~ 3靵 :KAANu,n弸o*6o}^Ͷu/,*jp!;qpB v䖺7PZJȐR/ y!'}Snn/ܰp//[A>sAgz}G~V@Lߐkr<ߣkH u\]=sJwaku c=N@݀~O} *-Um95ee8 zۏ۷g/Zn|xryЯ?95/O=7hK|{`h`6ſC3)$f#Mԏ%OlNc-=`؀^<f-]J8C.o^@?6>P'S[͡Dž͙jY  ʻ];o(skM`}Ũl1<7MKZ@C?]ld)?I @-Nz44hԫGwKKɁ#GqQK2pj@#Iy  > M<# r҆Ep"Qlqg??Ѹ*<}~pנ ndq %^k+1!ܜn`5G{We)<:ȉTo=ޖ@V^au} 'C04WΞ~ӟz)Vz\%pH4z1N \~2>{S>v>6?H;=Pq0v.ӄ6 1|)?G%!gb_׿u ~'7uY]W^ c:$$N'83I&.ydgr'?΍&:.g7B6%]]B$z}A>{}[Zkݺ?#]>Uy/><<6.d,|͝Cv^CKWZ:{N8c/{VX!r^>>B>Bx 9~js;mܨhPpnl=kM\pp4ph߱p{|M7EwkBa]1vz#>>kv[Mxc">Cq|?߳G}yf{o/[fH7Ԯ'Gbl?'؂ÁX< ֡ j#%y#x? 5q/}iȿe:74\qN0u^+W]asok}C}ؙ:a/B˧-E ܙsݡ.Y_euɕ fO'&`W!ʃ <5jVvUxl(1nX5s%N_iU?&ml=/\85dKU_Η|EԨSn;̰߆'GGzNЊ!'㣡?+K7bsо=䃺]B#1&jz *xzҸ ʚZBfvKM1K{[Ye %%9'~(Ѹ-.xL}皰Ypi9rccwŽ2%s::hY0|1yl:9{?c9oFcӰO?u96;k9!ռ{ =lX5uCCz)xC5k~OǗkVQOSQ~r>oSU Փ; ]a2dprB^?ǨT "ʗ>?GAE5{vZU9d&džS8pB &|>8*چ1ADAZ~GvuE*\cf׏|ԨW0v|rУ`OSzqQ]le1&dž>87Z@l(֎] t-44Xc/;O:N|0/uP37F-eW?[_ugUUGwyGmgߨo~ t..x8!45}ḷ=_뎭a}1>c7kk75 gOռ\cu7jj,PGD;%I1r `YL[Uq/uq0>9v< 7row0!>j}ݷ*UXzJTyD[D?/SCCt|>} ϸ02GȺ@gB'_ݟcˈ;c8 tKdvu/%>5Tl- >~4?cS kܸqm޼Y~79pgbAwcg9'>CYpޘKIxb>EMx_8:v1} 8=#g"^0GSN/W=zr7r=-<0N v`su|"4"C8VpbFYconðy%1u0vyJc74_#m~}}dF}?#ÿj =rf#ongca53\i?o<tMKS("X'k|s 'Fo zlX[%(Hsˆxbl"\("U%A:R=xrǢ1Y9&_5٦P㼿O:4աW}_CN7Ւxu_T{'>j]$Lqej4/PZ*d7W(f?>myȕB*uggbIGM[yg''es숴!ukjЀW`8{|QRgw !_yv-RlxARb^%یVyZ>i5.MX=Am']PtyQw( 7PzIV!k|mjqMk^U+81b$`]-v`khƬc‰ 9 رZ}&,/c⽳'.v>0ҥNAkmmgeHBwG1u'-v;xeeUhn91ypZCDI;G+/+ԩ830L z"Mm~D~='zd꼝6=[Rc_Z9qЂR/;ko"5 |zWg&8zVp>/^s_߱k>>@OGz_ʹGӗ(ܶxOAc tp  m1u?KmlT@?ƛ~Y<z%?:)w!O -<`Zc<{W) -?Ã9瓙jlooYó,}|b)g[3|?OdyhıStpFՏW~-8hnm. $}(}K`jV4ɉ*:~qDŽz?0% 'ۤLE\dyKkxA26aJ 伩,+XV9O?5M {1}.mFOKUQ:2+w\9[|b5ip uqmo?aGe[xB'yWŕe>:] nׇ#@qm|dulPϐrcn>@NnC1jt+˥b^Oz ǚ;{Vv^:QQ,0DaC \~{x~fl36.-8gIg8g{ pO}яɝ+vcWl;M'!] vĽ?Oٞ''~93"{{O={XA{4-N<#w&NqlL`ݧgDp@~TT~K*f ƹYmV{?uX"Ǒ:{a_#<-MeP/r f(>:+>\<z u^q:7\6?p/&ONuk|.T=$b .t1[E?'l @^8vz:j_[i7\ݿɏuD">z8t{g#,p /uۿv_~~~?O]34FZ@|t<}w ~K:SuHDjС+qi6WágVc)r??ځmI%SJl-sllzqS3kv΃3ٳbwgqdJUޟ+2tb{ġm/fWLSowZ,gz/ URVouX26=%5EV/.B%җ]WJKyS|lz j+;A/0GE >/kZثwj9|yU,z&Sx'njm%XLHlٶM<mj/r,rzʇ9β@lm~x.b 5([; Sz1X.QP04wn? תAJAgNJù9,?ZƚFYn]s49;5Gx^XO!@kaQȫ+:d%z?X7ܛY5ŵ}U?RmEEӏY3eKʱI?K?~+ұ?1w' j&`ރuy?GA\>uu976o|x|4` t b ̃gL\/p lC~ȑ|>mO>V]q|,|1u勣#o/ N0ԉڈ++W+>I?뀏ri../xo"Am @|}PmqXUO\wxNWڴi7\iPA#zc9|uF{9\e V;h\{uk!3g< Zkڹ 7WD:;{6QLmWw|WG?8yܨ_Xz8>F>`wY ~#3⚅~,pGhcog71/y2R ;<86:W &'lڴz&xv߷UVB~7*F{rFYVeVi ɱhw./%S߾-VZkmU?Qme#33lWآ,&XكPM;^HOV m= }#kG R _g[݇u{jiiتի9kN_ Lj_bVg. Od@։`?#v:_cO='?? y^ =t.=|8AŞs^47= !Mek0&b>{U<qq} 5yy ްW_}CIGs532X#_3osp>߯v_wMTc] ~7z û Abx~ xd!}Uv~a)\%|T|C͠3 8V|O0*9lg?gw͘`wzKG<߃O ~mhzT[<fW4ukEщ3E5|~A^?'Rĝ_tE_(ăv\Tg qȵ-9? Lrj}M/z lݺu?|~x>؀_/Yl Pu!Y(K;嫞C!#)p@b'{79y C5~X\W{#6xf.3wOksGqRq+JB{Ƭw4jp|㻢yn0v)#5o V6L9CóN S+ :{٣eGz靐^Cn5Yet v?|/msLr Cd~džb!Ëp;Ɓ=0s/nA5knzɖ?ܖݽL53ot£7o=xV+35m9&n|je dS3}*rQ }3jG^>3?[S4A&6U?©&͆I3; {J JӂP_KJ(֏0>r:5u4ėXSξ͡( r`m}ljgeg~gicy.<_h7H6)#>;~BaAr+}+c ø9 "Ꭸ ~Kru{y}];9{;-wbGjYdl({Gwxt}Zl{N |ރ}IH <7x2Ü]V_<8 ̂> [ /Bq~/|% ɱ: 1w߭ڙwK[b9j5 7|%p @->o\&O9p}H~86 :៽w\ˇblyV'qBx"O惶+ OP7E?I+]?^62vG7G>#>/?zjYˍh.Duykw'`HO@s }| _4_: F (֏F `cӅQx~d<^wSfyG*x:OkV̮/?i 4  8Ÿje7ifo}A~'ug7z{抯Ap5J=׉K&V6RSu-rO|GGM])weK*߆'G0|7u[vZJiucT~j_5{\ƍ!:]~gv wxjbE CM&@v a[#&@l^u-lﶟR?6O5{C~ }j-R+]\j%klq)摸=T<З@O[W~/O5}:~ƦTMOpd}xt!~ yhomk<9}lXQ>wypP8';/C#Rz-藧ۅ)Zh,Q4kRU}ac;/6~ $'Ξ5(6\}d}_c3h8>>&G38' 9hk)!#;9 h_O}`l5Ҭ#}R|kk6 zm={,Uh,~擺 q4",QftԺ)̃ix % 0 ,;$p{:b2Oh?X7a|1A{O|f־W7j-/"+SdUkG kMfmN%}Hރ&})}^˚/t9~︞vCtR{?*;A.v}GmU_Pٓ'l,Zb? 6;g}Q( f'`c9x_8(y8?\h' ?+'1^g, n[f͚5o}K9` KbeMM͎ F5z [r`Q~Y>fO+pq|{lܸ~MUܠl2Dm!t~!dj>'ʚu4{ uC@iuc3e&׹b~h('kiM#9?\' 5Zc@(>*nw6OW}r}h`b&lKɯ;L#~RÎ[X&{!ۃQ?8g >&ڀ!4>}-{k_',F8.svEp - NSs&f9]kw޽C`m0)I  b=s=ʩ}W&]5I~8+.{0} ^L>IL\>|=rb;k+Ǩl^b`!Un1Ojb`~۬Ͳ|rd8bry[~n_wl(hv>x[U6r%+BŎ/2dς!мYjw[7!kwI!V N,؆\8={[GHOmGM|1sPvCY0kmngWGR{a\LQ/'j= OW-czfxG=XKXjVO ޡS`>qlŰT_uTX?<\l|qDZ-zxp;IMj/Hq q{^ Cb!8 "i0nڂW.ӈ1~L O8 yr" ؀]MY .7oo&>7&GA s`Q'Yk75ip p龷GdE]cc1Sjuq ||l4(GxIIY~?S]<~Frpz3^P?g=Mc"6p1- {||e}veEOR^|FULjb<(FN฿oX ;}%J_#«gBW.?ަ asQ>NG'm03 [x #ݑ>!A=-D_zLJE W{fW'|2Su1*|y-2qėUO+ Mo4).>*Oxo[~k?koo8.3Zrϣ*x= %up]38!RrYͺiKq踰gG+O%!v5w߲Pw^-gN4yʟjx!YTatSs }:6 ݀+&}\1 ;N{^7҇BOǪ'#{;^)Ŷu0‰ۀvGM# }bLԹ~ [6n8Yg\p6+4}<ܯ-6EWQi>BWA\jmxxL)~z ljDb9';97(;gxxű:eKރ'q76XER}P{w i:r'`Ҡu6 633wbj_m6l t.IcP 4 zR(wTjMW pQ-Au&]]NZbI]L\TSlle/T->5 khЩ>= [MGq vyhñs n{n8?)؅k;u߹GT55R\;8Lõ1V9Q&{gP߷?ORm㯱~ "B߸(p*yu1?s`tǞS!5|L{|zdu [FN^mQApHJ s[^F= iqy@kH.$۶mO?vy69hD5{A[47#{Oę"g%Qُ?N#c{9>4 Lc Zn}2?m`Q0`o$kp=7_@=e5k +Y3Xʳڄgݲn&kbw3uTwϋw+oqd^! aOaR^1oogk'ԡol[lz)lJ Bd4G8mkۨ^3J7%Kv ޜ4EG} u(q[Z?|d{ٛ|BEl-8?mdPϏ_KqqLw+sm +P|}왴o!Q~c1툕oОOǠ{uc9qJ+G~30q?\ MS}\ϧ. ?o ~CzP<Ӌhլ\%kIߥxb7N>XUg5ah5I=eV 7`#꟫,kcNPw j4oו@\?hF\摵 B[&SŲe(NXAy>_\6zhRW ^r%gv;鐝>ehd%sK\oqt\5:P[ά$~A ><@ #0~-ȰOԣW}6Eۋ- IG?*^>s<|el5 0籗|.+~z#sh8XqqlB| ދc3~0\+sqoMƆ aS5Ow :s'eC.@г==%#쭪KN|X?jy|AbE뗔Enx5Tu?6,ӱ&Nj}*{ST)3~Ymx^[Rksv(.?ڏ9~/9.F1|[%vFX$M;U;oynWӿKcY8c3lx >K\{ #b8c \{? xT|\f4fF^d[qI,i i iK6!&$@I(lq0-K˨4*2=wwc̽o:u*^^^.g)((萬,6m ֭[S H< )_U.SMEE19CIOGB B2<<,$ K_@πoc>&+dhhHz=v{#d鷗['HPdHZJƸ %A-IJIAOKhJHbC1+6Jz[%W^4 dd丑=%[RR闖Hrjx<2?,9rYv~H\dI%cl~k[`B@|#|«d IpFPRd{@@ȓ OdȰ#"qJZ${%{j$컡[MK?MZ?/i4:%51K6}}RrdMΒ -J'E"g"6iIIN>T%ۿ]e,yd$%99YIjvdΒކ^kaԀ/t`tP<Ǭ2799v]k>,\ZkZZL},"ԠP"1id>hx 2<0,:H`f@rʰֿ %sKd6JǕIJ!I/N$  Pאau$}dfJ^ɞc#9cgu7')>>bք,ILKdδya%kl)!75`gIBY]"tUv+X\|>XRhTn}RLf~fdddHoc#YpO{Ò"G2Gfں5Ombl5\3l{R0@~ G;=,~QeҥKRYY)f͒QFIggD"ɱIgϖ$ijjy6KƎk{UWWKZZLimmVΟ?oeq}yݻeƍf BVO].&Mliiim;}aĈ6 2uTk/}r_}}^cz-lC: rsrmktl*S,7}&vl›bI+HvG^cRtKINhy4khKp:9%YBC!EEE2HplP|e_/O=gbTcLۏ}{CT/vș#-ZK"t{hz`xcU%Q JA{~G}i|*Ȳ>O"q_\2K3Dd͐t3ɩzU$cL7g,YvIJzdOʖXT^:BI/qhe?\MUa/+OH'[ElWִ,I %œegPϐAj^Yikk&Yla ?vAW:saGZp4E1c~uq s̱2س(,~1={V G?ɓ宻2Dx8qBfΜiaڵΘ1 F/p>͔gs &Fm_rSy=OIy6wdZmOpI~4:%yޯHsՠ_:񀔔}PS8ު>4ޑ rKUAS<~Fu^ǤrdkTyj-ҡ5C }Nyh]YzO,Ng:i:pxC3N~@ˉ躽DM|L1z(CWH=ed'E*]+uQLH~רUutCZNm:wN(g*#^9}>~PAy0o2);u Z˙yvԩS7X8_Wl3f2ɲ۫$R0;ʌf(EIȥͷ6z_m?Y/n`(EDNlzI o+|vI79(m /״:VKFY `aoVG˺ JJ Ez.31'MLN+eM256{Akkƭ&"2~~RZV>R&.([=WU&w_ӓγ6t;20؁^ sTؐ>C7;^9=OyQ0E""ֆd'OzB*_h$jkccK$JS^<߰ٸ_i~$9+^ƩX'-[d,n 뿷^+)vV9f^,˘FONۓyᗬ#6oY+kb LN0>OEf 8z[ p4_4aͦdH`{dwf-ee3A;]L)_^J\i&UUɹWI`L@eOȗKg}m^n**Ooc:GO!sx2<-/_["OI[7"r_ebȧG)S,V8#` 9t et>fTdk=,\=Ñs6md[o59p6P>8^ >s-?Ѵ5rx?|8FUU.~+|Ɔ(qٛTW^_:AY… cD۷n}Ky zs?1qju =e9*A 9ݫU\u#3񂔔wQ+8W_p-k?tY|GOƔrכW׀ FmmV>ئ-.S9Ǖ_ ]+oKmQ"CԭOm5:?}@8[Jt Q]_ Y={@~ WQ5wHyzE]A˯"whYZ:::WM aΧkzI9߶JRחhp ֮6ʒ:]#]O$\όmgOtTpիWKaaO:OūpFn&Ƀ28N4(7۞~]R2Tޛm^dw7.md{K1jXevBLw9R8PZڞQa{0:Nu̞.(y(5Q lH.ftґ 6tJ奉A9!{~HTmx2ydi{$՛j-91<Ĕ>6SFqoǵydѲe5#A1:pkx']m: /4dgKQ㿌ky}f7=JCJ x c>S̼LykoWAS:@k@^GBRnٱο}x;Kb 4}ö&ހ8IpRt CCviѷn#K/#<"'3FFi2+L 悁?286"oG!#98 ނ`6{QAff?o~#_׬ȴҗ}>NV~6Nf켬'k{lm[mH`p/z4)Nts}><0<ܯ8[Ajj!ƍ[״!=@ׂrQ_SEOf@6Af(+ l>cm?uomHSoXlD170AH:'ݺ'!zo^߄A򀎩O?G?Zww t w@Ҳju+o+uٶ7bW!Ziyt9_\][ \FݳgOܵᔬs_;|q`}ϥcdݏIR9u*''mej?nr"6kw.JW]>7wBl=aĻ&+3]k8)w`6{&uDX|S}&;\xGz%wlZ:vJv3d'c{>[Lv_om:.{PT&(5Lf߯3Z;'[*n`ːHmx#weL`u'"IZL ."{ >v"Gƚc jatF>dyb?F nE@'. 4dv >IK:yhN,{lL-0>YK!&]WIhZHS +?\d/|lSV.ߋ;.ʙHŏ-[%5-|)LW] r,k oNKx]&6I=Lu sk ,}ə6N4dnc{`h~|2/üK'a_`i2@߀շ;r<@p̆r/.D»CXBttlEݝ Ed%-f~ G,ʱ2X3Q#/qEO!K[\}g ?я^20.8nGJVVl{M ًi'ihL×.]j gqDgLh}7ʡ[;Nsl~rdx˛.rpR5Ydq^ߡ@{++Go.#'% OǓ8-G>+֧}Sϵ2dfT6or >HAtuU>sj4z>sbI_?EǪR*/ȝѥKLkʧsuY >(]oC,9vN>]ull[1ZkЦ^+1=zx t͔)}׏mV躨y +`p eJ&u}>\2y?dk>!Uߣ@n0y3[nqQ+>)qs^<@JGކI+u8?='9m쎵/֚8\uvY)^Ƭ#Bݣf]+!yqC$K$V9U`E2zh)Y(WE{)A+鬱ÂxVkg?#UU6'b|rG]5|l/_Odeގy9sl |A1lԸˌGDZQyHJs1=~]fk66KhNdxpkog?+S6L+!%fb(Wh2} HK#fLQ6?h]:h}pxb1ÙhY(n}qIL7|^d'kgS6aLk,纖-־y7%~]do?&뜿##6\WY[Iqyo"xm;uciRN1>ԗ$&Gk+Y#̯x'gJ6=AN?g8\h^U^5ץd,)_,Mj?nO۰;փP_U/~]ɈdCYz~|Eؙ;G្Ћ#O#*GGО y=y)wߗʿ!OB.2ͧ۾[5:"]s{<}V];\;/~%wEPv^_zǟupw曍y2YR@䛭l׭deCB?Env]56;/>x:`՚_X-/y8 zo|Cz{ mu[h'? ?|=;l|W@Hs7NMj~9sLkgB+0WtKBfŖqeM+eƹqlRHڇ;K=3`ç k\;(3?>S|q Rژ3l&YOc$##ɾd ySVBov|,{Na"b~u,μK&DraWwV7R}N{zH<,X 5ĺlSc׏\8/;eeǧ7ظ<[١HbrwUZcߖGa >~uoQO6|oѵu`9x2е⯇=A]Y,]?@ͮebWqַe3MOŧlz*p)Bٌ 8b`,h 'c;0>p~_,;vյ6Z5[띤߫Xm`pײfh5,3?}%lkS^[nyZ1A 0+ >~s S6)]t(HMRP*<&So7Y9iX];>[g&_sVm`~u `޷_vd{_¯l{klkzI΢ZFdgExs%>2B۲ZP9e1~چdߎփ ~(@@s $)Ȏ;lhlH|}<7Ly{ e[ɕ3Wdww>&Z f('."iu|TFbB칤x!<%׎^.G?g`:0{prJ4L&>@PYr.̕"GvĿD^~ס1!}l0ن`$zd&TY}v5)^ȉHW;?^`o.@[o) Lg,; ;V_É9i9aYo+ds/u'I5v-vq0=>2(6dfIosɡ7Xo9emM ¸v[lEANu&/:>`#~zU=LZZjL8A"?,ȔH\z.)_Q. Zh:y@"+Y̦fg6=IepڰS7z 1&ΆЙ`a\͝ Oy`}G$(C阜yܽ0;w#Xgx} 5C'mC// &suRsFF%ʘO$ qO-7'}cnS?ܡFwHp@0}''Uؓ\<5~sL S`'?o7ϣ\/09XRoذi\ꂇ:9y%PyJa|]܏~:~_y{9xքaȱ#y. FmD0fQrnFHܱo/h &? # ᫇?xZylV]|Y8i~e'=n`>j9"~dp *t^ȗt]_4pө&if!W23som:qq|=xciFg+WϚ܃M`촱22}':˾0|&>@-Vd(E5rdJpLPæ;& }4~` rX{`z/Pq ,>:~ثo75?,o6JmN\AETϵgB |>˃}懈O=vlxM[`U~{p\v"cO5d߶ϹCh&gr0;wYɫg2o[ruU|Y7_}22C$`7[,T*!kJdtlE}5mizm y, 86mMLg>6?,dt bN>{+߫4}]%s^[#q%wI36kωYT5Ӹ9.(޺ #eɿ/_⑸+mX녋 /'GvT*sW_z |cygIdxMCCf ##6L =x1<Џ5˗g  C %-ob~j*P7$\.A۹ Ͽowm|W+7bxnŞ$}Øv2&/l6bTBOOx_gG뚠}]׮|ݦr|%˗5BrtMT.U}b b0A$rfHM(n8 =}-0R?gqo?" {M:FtM[0NyeX`i,_;g}j^/!l.w8꼁/G"Xz'CP:Hy]ҏ4/ @.>~C`cΝX׿ ?+?K~f,zxa:G12r52&LmI׉.I.--_0[DF~2L0! 3QhXj9[v?[*^.{6@}&LY?|ӛNˮ'wO?9_6kfO lo1;Xlk+(X`ץ&JWkCIkl0=|$*4 D?ؑ$,hy9S"߼"Xk> OadLPNI9.]bvy,Ǟ=& i̺iju4y&\R ϙ37BRVr>h7p88LBa>6 3ߦ3<#.~oގ}'?X!c֌gnH,$_=8)9,@M|#հ~ѻӁbĆM_Ο1_ovǯKz(]<8q=E$G<#U9S-~p@fΐ__/v2#7n]5,vql$1ԽbxSC/0{2DfOjvpڃ ΁ \l,\'O4y =>\96p6N O2?`:߻kz;߹sn|"}p6r2{X|3YT?Z[zY@F:7)$D^in]wMy226|Uh+t--SΙkKɕ%k5'^^ 9gPz/38#,iW* $cRŐ o7HѭE0i}=O)JKFZhc4{fW}N9)oƬx;)W&3DVIpyб]+;ˇ3@.U_Z%s 6O"0i;9ӱWBO >wT<،R\&lyVr.vt:2lL#/~O~? @vk|8tׯ{>^tȀ#˸y[ksY-wyÃҝ/IR}lIOx2&ocn|{M 7Ã&GS>eGio3E;'$136xådOv/.gJEpfh|Y7[xo>)ij'?$$k _xm4=y,Gd.$+y뾽r2}aתd/ItK-1brC]{0=ݮùȸ賑S0 E@y||We͖߄==,.p2Ww.O}܃61hr9u1`-ׯ^nl!}#xO]K_P}}n챌0N SJ;?,~/vϢE n"|7nssΑ^{Ygtc :*꼒Kƣ!b-+>h HJJ7c+Z2lb kݵ&c' 8q\ +/+3r};n }еx@[T {9 '@p;'k7$bbw:u-Q7K?tJ^avMl9~]ȘfO';{m'_3edʚ9_ntH!WR1b@''p–/ˌ̐ywγ*=(^#Ϛgxz\ud^KZ"ees5XNDQ #dhN7嚎 6""'_8i9c^ɌYtϑUQ37O9"Cf{Op|xЙ>~T"& /|>;J7DϘkVtG3CN-͎?PhznsхŎOʷa3öQHbؐ0>`FBQkmĖ@L%9xdؼ>1BOr*)C_m<(=,26h5 [֫oނo6- c#Ffq Z,[8c-xS>\FNϜz\wE"M[+yyY;S'omImOGyT ,]7bY`%s yM`ud|Ya(70s|}|;Ul68nԕ3v݃*倕n:ֳǺ:{rcZEz7{V0&pCcvǀE\ 2;cGV Q6?+x<>;{i+<5c`L6`R)Ǹx=FyB)/_G%i(5j=|mS dd;)gX+==c^c1yy+O܏;ĸF4 Ǐ?/ ސϙ&T."#^&GKaS%k?WW~ ]oϿH G/@Ν?<(kˏ'l!bIc1b}(-F#Ĭ&&| +uϗW4t 'XkgE?l+tX8!5/UW[?2PxH^3dWÏ/u&; Я9 l%on&Ȕ-HE:>rqXNyRrc~q,xhb>,~8-ANea{Xf$6]8klɞmcǡmt}sܥ&G -%f `iɧޚ9aذ{ n@.dwaw>mWњ"ۚy?.le\Ɯe90.[w ="[EV}o,B7kv p@`M΍?>SѽEGcwFނ<=/lp665^O(bsG(uKW}\d/uz-˗dəc7`'Ϳsj: NA08Gy<&/`{fε-1'|f1}P '2r9"m`Ǧ/Ϸ#'1Fb䃲sŽg|1`Yp|}cmh1Ί&}lj0@ZzTmﰸo7eM/Cp&LD @tb=X.a(,!߸q?z7>[7 Y\nl>ݔ˞-kK/deR9 pxS F'6J4mUQz-'ƛ Gq3&n⧞ztxgzW;'ĉ#s}6OC1}bm:|== @:OXT(_ ,C`Oz"VFfxUW}@^y.SnSd|𰍃1jy W/1n~V|^o h^X=Wmkdd#SN>) [hƐ_16t Z}ߤ哯^mߧ#{*66Zso!+% _|8υٟ{ON_[iƦGֽPo|@l3xX}IO& a P,R .lZ)aa$쯱=Y}?Gd䔑WlrxKg1,-Z1#or 喟"S>2K$WRmӒn?lwN##Sl>O]۰%5cu@3ʜ̱?$z9vrE#.&k\:-:CnDnjPuʀkG&''G Y+OgT9DV3\ m``!wv~rv_yXÎ/1ֆ4/%6xI7 ,7/ݰͰW3>?z IRsFV~YtQ}/,Y- {uFc ˲5A>7-+eBΛgk3b ࿀"v4[(3lZ|;0Ӗ_Ce(XNƔ3L/f~K囕zb)AV'-\CwQ;.F. P6V:Do]`3<#/L;ܴ~v6{5 lVF7΀7'u_1^}7nB0I8;kk7!s7pyHr؍g<:wG` =E&W_ukfK 4l9jL j8wuzЇY ȹ#O K,Σ~g*:|9&or d }3xO)O~|Fo|T9*y9k:'FO1p 3`_-y>Eۣs<^kFoxKXRmy>/Qr$btpsG,+9crLfij-Kk`MmA/ȝKs˲kn便's foB[r_3 om0ŋ'`Idg0+㧏_z %\vTޙOsdn09^J`2ЄLo}M,_Jt5[.b0+pt.ȇN.ti<(9(qѰ?/?;_tɬEl}|r~\%kKd0i[dȰdM2/w qfp)rOb LnӾ+F}ln,4mʮC2R6L ƍȕCGGV9#&tCÆȺƁ}b>`v|;~o6|x7i$ʜ˙@\<#V&˿\JJz9ii,*/>5tYU@rRbo:1`"ȕO!,Աo0tŶyӹȫ9d^GA(_פxz,Y֘%97|;1O9 5:%:Cv bvكsG\k1W<#I >RrLΠ=vm39umᅬ>y='.V̕@t k [sSΞp/I7#g{ٚ2n΀1T`,%v>U+M &W'VI~0.We/4\Vٗ ;X7|CUqǧٽqKcgF@Ȕ]bb(A 2)`CN3Sꕚj GҞKE'[M;Zan+vkt'>FQy]bp"1'74n| o(h7lˤxji3[3r1M|'g u07wh¦pGzr('x$'W  @ "<%bb㻍ˆNJgc]vNyGK!b:qǙM3L{ٳ[ϵJAy?B.s& sD_ωT.0f+[c a?]MáБBX6 &NO,sɚx%T2nκgf7(p|sI-"v[ 7k899mn;f:bǦwچkkBzc|{&{sԡ열4[ `@FEtज़<|F 0f,gѱ>xx7G_#9.>O`8`7[ <}9?Ofw6 ܂qo 3 gϠ.l$)]PC_zAp?pĬpwm +œ1%=Yo[.hL07d4;Xp7+/o#pb CHb>w轍CCz)>4X~y||V>ӝՏ1ףcߥZ Z@U+g³X笽ރ$9:v G!tL)$&}y"/$%lLg&5^&9G9 ܁|@2nQZ3J۬k/nA/wr2~x/3 oggl~`!篼QoÛe翻9TO+{`0.ӐCVy9;{6Rq72vn[_gi&C4{iL!/}-@LÝO) Fɕn-ۛ~>Oo@b| ɱc9g!'_ˑ9b6y+T<)Y<߰?~ɗ`؁~Yvb&~lL֖?n\8[an ŀsfzz兛^KTr'4/ /cbcn?sRM &lYO*SbWL y |tD{KLe 9u-o6}bol!]Œ[9_8$g/x􅣦jlYRˁ{Y"G?틝x̩+;< a`]7 jc3θ}R{1[6wSp|3]P;~vPcM7ԵI|,79aXy1ek'-sG6ʑ lsN< +L&b9p!vW@nʀWNsc[ ^9QLN$0w/pnw(_E>>q ml_SϠ݌7pu3X1.%AO@7r_W^^CDaMXtT8.7~`+fz|0=^\B7 xx،%_rE^+(Rɣ;y]zCVice`RUdKrkog֚VyWob6xLs˯'}J̗ m&9Rc1`"߻Y9]ZN6qz<ȠK? -0õ=/KwC#w=ySMBSƢ8xK aU!tYtsoGspSߏ#9HrO)|ذuc'G/8>s%K,ov\|;;Yyi& jعA[ksyo}yg9Ofna-Ќc#Yy\o<C\Ǐ?, .0'h lI\DG?Q!& R5`M\:m#ޝ;󆜕>0r/>-`q9gY{I*#c2Or?[?Xn"ƞLsιc<jM|[tb9养_0W3 yy-e ׄ<V̏Rmyp]MoȚZND~֙7x /?=!yP8/%_rd| g˚o4/ 61Eȫ"=Ii:$[jv{| |G-%;Spc$Wmȍ^Bw kȶwpdyl? Y LGw4dZVNd aa'iAt_1V毠%g.d98?#玴D/C'Ɗ_'vZA~-g4zk3vF'+3)=I6?j$4ӛ>l2^'3l{ Sk]uENtBZ+[Mk AƠx-8 bKA 1vp'zO8d- ~Lx=%\ǯ}5ˋq/5F18pr|wx+!Ƃ/m2 Ap.`OOv}ʆu<4?r)Az;CCW5y83po)ݮr-8ʳ`(߻gRX΃ZddW0Us p LZpm76x4;{X=[n{% i+}cvEp \{Nds"5X;aԷm6>} `;[gh͵ļ&z7q=D;I6sYFGxml9RRZu;GxɺfgOkCNEyr7p} Pakl|F{{c:w1el sy| 3]%?Q1Ǜ:m:7\kCA_P<{|Rz93‰9cC "GL%ǗZo(?au \'r'r9ŹS?zY%r!ų?XAZrgG v ,y97}lm3ξgMdv٫ٓ?Zlz~dkoc~Y|U%OUhkes fqT*N.˱o˃s1',1T݋u'kKKKkNr_Z[YM]&wb8~[:yAӕ#sy3&Oa%,02`i~wHZ*[e;'9<&-rOQ{H_mg|J2LŶp R{V*jxmON@Z)I&6$/R.& bLWCK.[]ܡ8a˛t|ϛ_%r 7..rvHL&;7|Vy~Mp6 > 5(/3Ϫ:d"$d) 3(SAADAkPCkVўVZb_3>3N¸}CB_blKȱ<;7wqPOx;s}ucmKyKW}`Q,|emŦ(?.ףv`-0Nܹ[/XdGPkڤ4#vLJ|h?>i §h=?_uh~]D0 Xˡ٩hv>sʚQ/]zakI+֐+q֖-s=buB|%t3LjYm ?T:t|\T"h8r.&*ncaCLjTU^:Pe3a.{b%䐇MpzC9ɉ!38{c٪΋+\G[2lh|+* 4+[U&/ܖe䮃mC\Ź+,%3>>W5Q֍`8>ڝNʰi {-!b SP[No;+&-iiQ.+3Dm)u~ P']Q*+!9tekpC~<_-Vlt%;\IjoG'x5$Ĭ;<MjႪ XPB̟g^|Fcn'uk u`j{:jkm/n9دVQYaKƞ_hp3?ytW;zо !t8 ]۝8?]M^=>c:17xy&! ¶ ~urno8w#O}9܁0G^/** }fp>$=JCdl죌Vsm>1>cŧO,^½') @!6 <58^|Q3zN:>ᄏʏu^c|lkpsN-t_q\~0<\}~AO@^:sy8@~)5xul&fOu>X=u&*:c~{،@l)Vې^縝`m{tyvooLT _4Q~%lv?Vf}Y9ۼR>NXS:/>ÞzZŶ91SǷl2tr{SZgsO̵nO|ʕv>xj)[||`g>p"g'gB=Cez:>=8)XAܗ=<֯Zjd_{,lvT%ۿW^ɃžO[{.WYv#wrZ 6Ԧ4ُ:>u5}{91ra+ >6zdbh:aM +_3#?,_ B+ _ =9s 9pY _KkH ڟ7+]Y39 5n֜,$V3qe rF5\cg[gM*l}/|B,GLb[+=hmvuxDk;&f*FK_zCRz1L̩5e}vg>|ܖ,%pO9@^!p2xxHD  E)h :3֥SAB[TN/G?A lAlnĖ'';Scؓ11rLƧ߸ y?`״iӄY`I y?68 ?DZA Xb؁͌yd 4_6ҧsa7p7*_bɒ%#v69;xc fpPc|k $Xc ȜA M7ݨ@_j_λ=S %$X)n~_=dz'?뜽i/.Yl=sc~p VWg.'v 8&?4Ϗ>jVaA9O?щ7DO`bB`89$\}b %Üo{~O!KHmtEu`MoMdi֡nW MP.y\J\;)"`iكyW՛gnQweŞ~O|-Yok~JwF9x ޸fqUKрWZkXu6tzJS 5f?Ck*}Hr"!rl(qd sbOgt?GD1IvvGċr."^@?ut3(BBՆ*z 8hb=~ֶC݆&|Bq1`FD9wnb۳LPN;=НO>@Bȃqg'._R+~e S5Yg&>c lɓKMU8'Ik K@ڿ>6\;QӰA=$?s;V"yk?Ӯ{y_kp\i&Vm %?\ |C(ᴛ~I'p%&%Qs/QQS!_ *{Lz A)&p9pZMN|%Ǻ_1j-ѐ"f+>z#Mʵ'zf̚a5]>clR-`;'Q#`rxc+ػ`Xиvc1Bl߱E+e4ӧs0`[ nZV0g</|x?b<!Gko`nuD }}$By3!1p pNW`\`6~ݣʍ<'B%\`}{ڇ8:_p|=AO`B~@iΝJQPk/Ǔk^:9t'HPY{II9y ΞzB8=93sݲ_2~tu58'w8{_j8r`L=}x58@s̿/yΝ9.ku!?x^'E-/܀=~ S@ ;oEҟ7:rڇ_xag+JGdB;k;OwxDxD xd4髋_#jd6l]܁sU|M1g˶Kӭl?_l[ڪv͉݃tOzYtMB#[u naO ^؈M)zVbtN39r곌lÞۮNJrD= k~Wu_0܆LOoK*=oC]y'?ŪT5J@r>Q|^McfeΒۑ< y2?rX?pm9Z<@xQeȊz*9#gVj=x \2oIrHK;![s=-&]#iRK7dOl񭖐*uk<c2bŨvtn)B fl&޷vpAk:ۤ|iLgȶ$.!Ԟəd ēmĚǶgٮWvق^`yJݩ;Ƿ 76Wߟ.?_)`6{HcKr\;cƠkiΙc=GXKwL'O`LwNm~<L߇WVAz̸ŸU·-a|}{mb~ z(@^L=WvﶇgszDbh!ONԸAÎFsg檾 {&ǏiHg=u?Yg[ڢ={jw&]Q<|q rugP}pj/+P!6umo>?gKV;];7)MͲsO0p|N5Tޕ&D'~ 7خ7wia =_x@ő/(]<jО'|k^[U=Y()6ŲGg+n@>>,]G^Dq~ߥ{3#WEb o*B}S;<{6YT|_X'/qHٹ YM8 I<"_uWqTX_@1 Qކj+T+^AْXG|%&[jYc+M C~s̓حlo*i*;VeыGoTlWƃJ~Vx=ЅVso<ƶbs~:|5a门xBD:,pg/GWD<$8|@PQN&.C TsP]VmZ|nmX6X>z>0sS9jwq;GsKbqyN?hGw 뽃i1;m{f?9}|o81 zEF .~X3aE?(aKE6mHĊfX+7HODx>)E7=d ?cKV'!I8z^Nw}׎o:.][h^2r˨^䵝i[.?6;*A~ʤRLj,3yH1墿cލ?;mܦ"W?peL{&#ۜ户Ӗ.nZƽKַۛԛaLlOԺximfk=ߪHC_NYtKL슗^&71ΤN+]yiL$v! 獿:~^q+0[p[:;?[{\r|J\5jd[Ov7A^ ^F 9BH/#Ax^(z ڌoϰNiiGԩ\9b$<JVbzh'R?[>T]\^]w>si FSA7έk/)iQ> }%4._P"ꁫlԷF鞪? 4I ބ["?4{zjy{9PJK= s䷠΅1o\u뢼֔nʑP j{n9ODo-rk9b?,l&||MR0+LS+W^Wz! >R'{1\ A؟9:p |{X7|{0bU`xyB_ [y8y ~=D}x>lUpqKA3i|)p\cd'C0Z`r1kAۃϘs`!Zs=xoիW:)65Fk E)i!DE%H^{ߵ %ӧd')l6=&>9~)1,3GѻE~f@AT*b#)OQ̻{vwC/M&NG]zS?%һKMWKl76gqZgbSa/vjߩô4-/7O88$T̾BxJN'7WʮMaB3 GΈO\8sNyJ71ú*VUHӎ-U{oׄ^VUZeGq_]yQ=4BGQX\crUw ]\օK^񡴡ipA.8ƿc <;,&BN_¼L"5c,iо& ­m:uPq6|p[Vs&=Y@;Q>Gt9tS2"uYTO ZJx9Mz'#Gyւt%+9}#:Te_-n\4%u6;6?9<F_:lh؍`q ށ/6<O=`5wԼ5k7[뇼v`8nBlw`#ap s^֐J ?W3wփktWf|p~ӛ6s { \&>^2Óۙqސ@ 裏jXK84J(.>3L^@jXn@|,G9tW=5Νm3˖n q~nA1-A+_h޷߶os?+G=cΟ}f}L \,Dgر>tH 1Zoz^%6,<{#~Y٧庑HxST?֊kb7t5@4>M|#wFzEqK,#5Cs_p yOGm#k|wyj7>yeOV g7yةuԧ&UsFu\V=@$y;Ȟm~~5z% K'ЫZʨ\ =x,wD:>ĺ!T.Z7'Ýtus~++~ؾ+psˮ76@[$'&Dkpë8!ìBKCb?bcք,kkoH&7Q=b<E<ďUm8:srzjl司*߹ۡ)=<9oB=U+˖\f>q l]cZ}7ꌷ]md̊>Q'5}~RCא.Dd:5pSo֗G۔xV[֙7&@ m)>ƈE#l&SrfR%W|lmmZW%~Q1||Sd}kO>.O]!y~|.䋐&;%yQ=dk[kم'~*1 y+xր`BS`(x1`X ;zcг 8p5 c| w+<w^M`[3Ӈ{!׈V)Ww8C_?pr0qQ|r;%X# /a #z >G>/^,_I4k}` h_Z@Dc,O0 s5`ޭ~prỹ ?1kǺAx5f_d8'_|{v9?tsΏ}ON 9!<ރj q%/`) &b{P?MT)t?s0Ov he#ͫ*>tmv;ѨO|}vx5\>rԊ.Y6h ˹2GrLGX>}S6٧C}Ob-",fC[&,v@ol< mjԧ7.({!> 'Ou6vUx~4w o"Onꩰ]'|uar_cˑOwWguN:u$`m`pg% j?ڀIl[zt墡sfҀ:@9Aϝ<{[;:9 ;nK W'\9a%r-v;k 컸,M *\ y၌-cz4x:ֿN|z6>F2dvk4rS:)j縷s[#<E4;Ǣy₮Jif^&4̓vJ//+M5!61I\FxzYuԇ{ؠlΎ;UE|ֈѠ)H#qxj[M9LTBdViQ#n3ρW1KIO&nEC5:۩dcISMZ{Ϩ>@wkIF^>R%0 G=d[{za]Fg虑GN<Gڜ5&X팿ip s_`00=x08{i\S/+`5`%Ws!nuLCރzVj !@nBm$<[kc48 6j/yC A'cpꦩmKD)ywjj_uN4yB/k VD=E-sҌ^_8yَ~b}8fc-|vN=q{zy͛m~kgh6ƟEbW'wDn"Xe#c3׳0ʳ Xߏ<̡4+W&u%|___F[¯\B?r_2g,gݔn_{MVP :ʹs<:n-|ia7!+KoluŇ9wuӟn3_nYT?8 x_Vm^E8p?ʾ G #9oi z甝,=i6j۠@mQlڱBi UusJKQ;N _oi3GAUWϟQal(׍ԏ6>Si39-ؚ仓[ɜяψO]#!0{̼П_'?k#Tsxp-#Mu|pV?چNjmYʧ<tE\_)C59եS0lZ4cν"N;z ~|V́SO:8Upz*_~U[l'E+u kv\YY 8 :mQ5&Am+k2|M;#.ٺԒd?~o}Î=.jp|O?"C`0N `']V|?1.ko@EĘrؤUfι䨾+=n@L;emĎo_5a MZ֋!kCcpNƺ/z\~59sX*BrlԾ˫?_z^Zx;t탥g E_>s{i9J.!$ -_(g ?8)uˉ&_K.髽.{>y9`]/F|SkOَvH[L3"G]:R[ǯ @XA>\Bzly~|v-}a .u*Y_ekϞ &[]:=!`3l*nK/_y_?$@Nqr9qY. ;srngQ4^۰A,*v4| xz GxH#0nj~rA&W_89k``O۸o+jF|E}8xoWڃ[l\Y)[XK7/3~ܺJxH)q%N=zZn_ZFy++A=slrw7-%-x'xnȇş[kX教Q9 a[:x|XX>WE<8XpAq q :p jl|Ag0!䉇Þ.6Z'y`!~`zDb؜|O^>8 #O7fO7õSO[Nü@㡇<979>pPyr`#x w`n)[i x4f̘|ւr>lxf߆}]6EI 9psޕ;@ Lq$ExUF)=ewoO@ 5n?9^p zI^On;+:I`r9mC:- ذh$ HP0R\#Z]{OLx=J!CY_r$^~fkDo.3}m9`)M`E?yyTp}?N5.*upNxvYP?珝ޓvCV{$7q6ʛLKozQ?wn=zN2_:R'X/uYlZ\v<fYCe}O{Gګuٚ` >xuL{8>pgF}\wlIq`9`)?=[|C\s7 cwp8abcSEk،}\ǂWyu9~1wA\  = 8k霃!XÌ.`p# 78 fyπ/1m֝7y\ax4~Rş+cm 5ѯ?گ;ş/Ow-D]ء+l!||&~>obcS&ˣ._/ N@z_9&9es>ng{s_PK@]A2z9;wCO͟٣^?8%^GeWu3?x|^չ}L]$@[F{=Pj@-mO9H$(!Jl2N9;7\3߽^Yk~t#ժhjRcg"ُ^;1;ѿ^;VYYʟ ЮS m )elz;{|Ynk[j:Ӥ.NՇ%XDګUmRJ(HPDLбKR3Rg يIQñ%IVtVv/߭OBBsGhf(gZ -SLVz_Ddz:{T^memJ(JPF%JcbUv'+n`"GFDH?VdR"":.U#ԭu*;UuS͉JKNӂ({XR(ih^ڮÍJġQTT7i7k_+2*R33ChoQ{Erz==j9ݢu5lT6ED+*.ż_([Oߨ ]9y*6V(:5ZS֣֊Vu5vvK){F ۣ6jQLJE-jۨޮ^eMrttzi-iUf土׶ sƫ翭WLkQӡ&%OTfe_hon["#}͟7+21RGծvi5j֫G==>T1{f::Jqyq~x_7u[TpiV)JRlFbbTFfEF@v)a`bc<Х& IQ\B=PuutO\2QzzImԩWs_z;t/WLvwz_k1눹ꬷusi^X&&ډ+Sb^6d +6;6,u.h57\(|574kۓ۴;`ݻ6effjܸq겱>sNegg,Yϥ:s‡~;---8p;v])))yWkkFZzwUYY|4aɓ~9眣e=c2enOyyTWW2 4HÇk8y03gx߆ ٷoׯUUU֮]G~/ ׯ_͛7릛nrlܸe̜ \17{ߢ" 0tr1"wϴWXb;dxǏcc&_ȋ~E.g̘1z+/ڜ۵y&vb 56W6Qz󔭐~{хx s3]` N~ c5m\v ~]alj+ʹcY}Gz{&gd#&gq6z,>>`i*߸6ڬyv]]nYaTczbVWܬ&{dPħ+6>V# OP\%e%>VҦ/T$ePBa;C}L)ХTy{.{wZuv+%H򽚯3[_Rsys֧)~p 6He;GFgǴvxncLB/9?LUVwsbscqsZc|ư~Wr|5 0naY5ָfM]Jꗤ}5:Imbܜ\]J)::U1j9Ӣ)(uvī'oWՉ*%f%jK5p@5uIi2v^Q١ȄHDžˊs܄kNIuEũYO~Bխ/s;:WxSd!7v8]ҹZ|A;y`I&Ť(mTQnݵj:ܤ؜XeDž9MFKWo^XqNRF\#ݣەy^zUˍJs [_jo^޶iպ'G5 ;FHSKy4M11ʽ(y3TǖNBWȳKC[MBƟ$_)2/g`m#95ӟHE<W Q\j6'78/sW+q@;u3ETd?!a@XU!>eD5}h1Y[ao|bs"Rl:㛎kj܀qzOjӦMsbo5k,'Gxaݻ9kӧ{`j}}c0X u?rJmٲEwo9kz饗,lp >6q & .矻7A>`K+";mСCGЧ={83Mp 38 l=}KΝt- V?s0,k\>?ykuegWۻw 8 \1_^r#+E'r m1áC\~7܃NMM7'n6qdkgq6ǟ;m~CKh;hj :lf>6l$ó8dع'׎usdc6909>ck2:avem7ڹѣzTagq9kd|[7ٱRi7o5و6l5 t®ks[[0p" /i';tǨb^RMa},G?k(iЪW)ff~cR RݥhuTur~{TWZ΂O#sdcuZ{m>sSWkUJ>61Mq9%GUJ (&3agS6QBy=?m !ҫTLvpܡV\LF%/.Qt^B {Zr,+pD-%-j=ت[j}7ܹ@+弅ӯn?{?7x:[:SƤ(e|bcgjܧ49o_>=29zTs94ZJ[|}twx'mu p$)SԩP.\[T*}z:j:rq9FvNZwOSW;2}|4*&'FXu:aMl ƣ͌u9t(5/ы.VcȻ(O=O6rcbbe);R=~ʏ*1#5>+^4^ǃa:_F)>/^ݮ VۦYߞߛ_DG50mVȷ'yvhmH9ZlE[ vL"wSǤJr]Ɵo{}.{,)ƆㆫVdV߳ZIc_hTKs>3fk`K7ip k-2`Bw/ŕW^a_>C rL4i}\DwЧ /ѣG3gt 7+`пo?~0fl # z.ZƼ~?`8_o@Gv\]hgc\rs58X g>w9I`ȃqÉu4GO~/mssWȆ1r's?8 yc1K/M=_b׌{Mo=/<_V<`X6abc3EOGosSm-zveO>wӦZ?MkBonw۽XF۱Zk [u=ZYO{v!?6GX+m7cUfl}vwʔgxkɦE'E+m\뽕W*Y l&.6lTš Z( GIqIu,ͺo2`$:~zP ,Iľ|Y#Tɧ%JOֲ-SH@IÒxBn;Ӧ;n'KVB~eGuOչ_?W[-G7ÊK7=(EoLV{})qpRv1C\~0cc ?>yWsC.X\>kZʿ$˹賡 !e_kuVW3s:9xNց1xN)Z{="_W p>}[:`0oę\M0s\Z{ꩧܮ?ܓg ݛk#1!S2@f`4=q1|Ge.] ;ͅ^ |~"g?`2Vk'+ }+_7|~9:A~@ca {mr_yȻar~B!v&vl4pSY?7^b8ڎqa`Nטdnt]:ƾ]hQ;mWY {_g.7ԇ U}6e{&s_ԇ8&_ۯ~c?q'uw~ ƿmjl;>DǙΞq9v4M.R֠,|cRƜ uչ6!mt\W~+ڷj|tc |>Ȅ_s~\yiT?=%]~H#n0_ `%-g{Fwrы׿#qţt7Wl56kԨ"EFG/87mt|qZ-x`8M=q=n,TɆc 99̈TWgbT1]5|||<ӱ~V wt5 V*<_X lNv; ܬz{g1eDJ( B6uѠDJ,y;O#/>qV'>_Ys0$<159ADJ_\(¥._i8Рw#NY]Eq{NMnWv<8=;:6Ʀڿ\;OĆ )k`| Ns< {66]K cux IʄuTg.2f(>'>O?65E}Epw h?êSUzwTR~V|JNI֙3;53g?<_ގNn>j@gkV_3fY=xOù.ao1=]v9&Ι3ǯ>ˀ׀Zuӟ]p~.3]Wbϑp-aYqoUx .5?/iD'2WAN`8ǐrs>%xכo@^0nd8!pvh~0OGG{{/V/؆)>o d36v|`4vfcOa`dcސf6v7va6\.FkWl[?|g? ;A6j[<1I6/v[o":n+>)pix3Mƈݣ<'lv80USo+9՞gTtm;/`wNlٻeYn{ ~luVbSco>Ǣ:sl{N|f;D))4gc 7}MaKw]J^*QDҧ_mjPmקY7q:={?~laOL2fTUU>Ⱦ}dUjn%,Ѡd[Yo~[IMʿ"?쏨UZmxdtp#cr[آϞq˘+Ƹ_ }X?W`1vh={BɄ0cޝ?Y74p̓dNK# Gx|. ?$Nv;Wl~iB :Gk!KTr wݕ6y |-+o@]ѕEXKݞjw _'Ld{J?(8`֪V% Nr?nR.l3Ht۶i[t/W|EuDi[65)0E1=Ρ+T\^bpj|fc&iHRƥJ v|e՚*s/U\8`OcU|T56koktU7Ӯ-`1 ^$˹c |u4X^܃.ȹq?ۻч W\qc؅n̽Xs$n1p+~%}kvt~.q-8vq/<6mMlxMdނ\h y:1\& c% _җ%S[ <E?a8s8 5GU5woq 6~g,9ra`p L[ȅ1_<}Ξ{)=&^ q-JSc[=nOO܈鋵1T[lppv 퀟q[cldik턵;"%|g05 w<%1֯*;6O?["[_~=d|w_[`]w8l[392No{'ߨF߸~zP'XNb~ #RÒM l!N }.3yw*&6F} }jav֦VK}_?v~qrM^ǰg/~aTWg윗=b=#H׏#s{Mb[uv]3ĜDn\>F9r}3;@zst^ˬY7D`.ItDp.yAGB&eOkۺѣ~0m[^آMŚJOHwퟹcOd/y3ʚi1v&> YGǵjknTib|6G~5Ki~MgqOBS_VBئB{pݖ^R}5l+q9Ot]p#VUy6r;۟k`gdͿz-|R肟\ v\c䧡'd]M'-j,kTlbrj² Ijlvӱ{6~ cS{؉ vel1?8{+4#~sb#v"CW9y_qvn}b1#y`3|@pSRR&@<ĄؿOw>"Vj]p<6n2/eel*i 0xbyLχI8\Ԩ+Fy|D|F<uƤXX3prY=5ix2gd絬}3>q /{O>'Tidӟ)S8vb'Ν}/{… Xsj`'z+>uTf_|~ ܱso~8 Mu` (?ʏq[.2 lEs8 X4}~غx<k1` V裏:A_~X&Gg#׀p?x}f A"2o7:\ O|J` nueA9>s ->!;0_#a.8OAa{Otm{ Ȝ>1v uD®nK&WGav^&DehFF.;Jut.c] Pb}anwb}Fiy^yE7?<81v Xdmc`cˇ1 }VrLUw~^| &=lce2ɳvV_ksa} |~mMnkdɓ8Cbds^~ٸQYf)(9\(?&bUm =V8~3}O4izаmr瑟rtQ,Qr La r$Ug@?&9yWGsk<`[m#R,},z뭺/^cϜW`7f $U;5ؗFp/tSs|_< 0[<< ܟ68m`/n/7ME}x^|A ?Al$A`*1Fޚ]gjpo c? .>oOl<.Oy˖-9`Lȁ9i~b:3 cEF+qO7_ x2Gg0/\=@?sF 6uZyi?oz֞ԸLMK!uqӵu'0nѮԨp> z{>&.`$"rjחja^m#>A@K]WG;X[_'Da'O*c9OZfϑ.{]| ;lbobKU&K[b [{rT0@#԰Ô=0m`iź ag-?̝ݗJcLF+YQ%EudJԄAŒ!yqpYnK *@֜ r\9OyZҎvczq ׼qス>N=_8tA0aӱ18cީOmG{.}t23VGrYa9^.vCW'9xgLCMZV >b-@,0PSv1v:[ ߯V10cmq WqOmvϥb~&_ټ>av7&>AVn< ك⟥. 6^WrJCr?Vׂ ԦݨZ}?4)OVNG%>I<({m诞I[tk6?YS!pL:>YlMؽds:#ǰ^+%۽w]n?v/=8GXGi .t=.uG;_[uOӴ;k~cnsAlDI'6,MMMM:[|B6MSyk;.~prp%g^NZӭu w_Vt-}h"?}չ.<N /]}tr rCz&|V*gn ^6$}oc ~65"}ax Zu(wygαN'tGʝv?Gv>SU%UW4/A JK {8v2<[p-|<}?yj=;&~ՙҩ~*V~ӟ|7{=ĝs`{XK;a7f2M'g̺'{@3uhG\/~~pCl` z0FG_VY `1x39:z|"{ vj0fq/ c?<.! V.ȽC-O 9'=+X!ׁ@ {|\GЭ{[f3Y-kY>@om~`s_gkQ/v%e')sxW.ߟf?\b񁓫ﰕRs/ NO6ܾKyo}-}5ͻs&}m7BEW?k?7]89a|)n E:@*Wfk{}Fd_`g?sSsr-ORrw8]˥xԜEhǿXWk{<lXםpW``VLT,"_&6<#Xp'"ok}w8MzP8?sV@53>Ml?L`k澩R]f k{ Ǖ^~tsb'z-v!b&mK& ra0k>ZZU압oUz^ q֓HH}|Uc=6l.BC^=+kBCw6$\b}բ-r[j2'豯3}]My{|\!ܟU ?W {O[UZmrxp7Z_^aw?Pm G<0srJ7jשbtMCq؏::s.x΀` {2Xs ߃6%Ip^qLw{E_y̤~۱^O~<gAg$~ ?~{6 q>_`Ǖؙӟ6}v𠠆[VcO#؏.upM} `~'x ?z: 2<kAsI~BxId|ɐS\prI&Fg|) 뉿i`Mqv]ȃ9=.{oFl81EL'P .-hN -40a@%.Tܢ[ |\?Rk:_F@ l h/zL)}z~#u-&͞C31YRzf=CԳj`\pyvpʞl~'džDn<Š$*1-Q+od^XÜ]u~>=O;&}KW:VaďK5Z]nxb6aO?eLuNzGb'q{55Z1OiUF|;"+mS3`8r8vCmm nTt1c1v|*`|(mVnBw&I7 OU5gz>!3܀^o>˞^=VqprIY#W".%NҸKyz[W؎<=GZQy7B6]K&j;zM޾ZxXEӋ:O|?uޢ5r^[굙#^yLEC׆'z/p9{7ǜOATׯ/69bQ>1!W|uu= $΁N\k<[5'6>|Z^#k*ԜQlKDlϗv[m^w6\*x5*6'܌#̝9[U[٪CzU^V{>`ؾ/ԩzlpF^O4C-oi{Dя~bjp&cx嗽91p_7},B U10 B? :P+]|z?c\yGp }}, pMV9NevDȗ;|ccd8kwqYsd?B;tzƉM%Gy:3uPs{wNnz&ymxES R]g)Ny:hJ1~O]>f<o[L.clz PcCl'%@~:D | }q|"k2"g08{H=]ؾC v 1 klH&PÛ7k{uuXA_a yL9SbKmRⷽ>5AqXyY}ٟێjW~~fɷOE]^!Oу.}T98/`_:I~o~z|JC-h9YQVjc"w 93]: j0Wy3W_,?b’v//b<~>Ԥg/zVS^3S^R[>bc4䘉O.gȜ̸pd;'yk߶x1#.IMw;g![MDaM'5umN?gLpNU |D.1#;fg}oNbOvc8P.[4/Z+Rtc8x5F>Fӑ&SS>+¦nr@?5 %ƅ#;7Y~r%$_ P/sZN,?oPÂ5RV\XGL쨫GyޠWZqȞp]/`;x8Sϕ77k<^}Xbo2|G' |j ۏus>x`\ٮn0 Gb8l+00!臬W`[KOwtOlprp}Ex:(mmqp c,qAMbM߃1!{xG !Ӄ:A]%p,0_so@K[ufA^;e uM|vbݱnJYP3 AkeΈ-~x;Pů{?~j^ZWkOӦj==$ Nptcܗγ-ڿzj=ׄ&(%-b1k;;\cD3\|G8?0R&:믎$I@ݕ: =a=0~([G#N9#_(K+q1~Wa^"䍒 w`&~ 8y&b#bUN64||7fp :EN//wIi %l`;``Mu\n x~}ƃ ^0]w?v=E+u jk\q01Cfc lcK1Ok%x@{w%11Z9mrFW,x.ɟ1K#lĆg#O83 >Dȗ_r%|]~yXlInjNӔp`AN7x닳Y Gںru rkj W]'LP㼏ƺ{`sۗ@^5?cq Cͳ{46g^hź?x|ƭ&uϚak0kV(k;75 |1V(ZTBAgg[cv?i4EQϞL;_ W*oDBp;T⠈ +yDʻ"1&cF-:?!QӖLӬg)mFJ|D8?N-tSyM]Ëجqjs_}ݵݮ+{MScGV{91GIIn;'0j6S? XPs+3psXE5g A-j5l139[wmX טm}hw;!yG Ahjy ?5?Í o዆K|I-Q-w{lbMܟZ I!8&.1Y\?@ݑc \Y6dP=F5=V:C ?@ ksjcs׉J2:!Tzc7vTS^C{ <і5  fPX[:tzifJUv9:S#?5ߣϡң1.k"L_Gw({g }wg.xOzb=a{ 9`3YC\3ǷMS!{;p-5%9Qؽ'nprv3'xg1.Lw 0CK? |ςxߌUwZG) ~>y|7 stv8%Kx_õwzcpv;) nF 70XYWA} /nޟ\&}b~ͫ5+5i +-A#> = 2!5ޞ3^y1.;Z&u+R?=WkvW陙:fX:f@#!nםG {M k}%R\W ?[>Զ.5G¾:A '40"enw;.g5~m. WFQ[;H>u[o쭞#WOIF>qtWvC{Jwsg35q lA_lnОgh謡{Xg9|U^[My7)51U_4#?yȻȫOz3m ۇqH|_~]_;~14]~hZE \w3g&/)_s! -}[S|uزs>syM0+Mq|aw}C^G7. Lޘ^{WY9 'g^C^Z{-z%/Qd~;"?2l wj+PkP_l\`;$6? ^3ЧPod|u?;Ͷ !o9_8mMd4EjQ_8o&ZP{.5196ٽ#;+oթi7jT2.QIj>{z&un>S?WsߓϷULoڥ};[_Wͽt aBl1v1ic&LtoՁ*Ms⺾ˊ*B0}㶆.OsjշkGti聪Ubjpob,xnP~ 9`};6 1A[lܟsK ~/dM}C=sM #n 1}a,"''}0,`Jjx.ݥn/j KzMP{oX;,-;iϘ\ o8r1^Q=X3jsKn/ϔ |۝FMmnzaC1#LP/_u->\a+Nj77}#^$bm-Oz]_c[j~!*c;lKރ݃5I>7 ρǍ6/"rB%LX)[ՅcGT$>2 M{ۓӒ535h e %M|4.wԜћxc}<}mMkܢqQU|&H/+W;N8 i*_ԙg5}D9g ;F5_fT8p>\z}.|^u7}qڀ4ݲ5omv,] a-mK ^5[bgqmTq38?|ؘ::g8u Нf6Y< \] ;w"7|w{\>7LƑPq&.@:x,>9OGק?gȼ$&;_ͧ_q~B!y<~}epfு dLy@NX]w;H'rz$[U9J.}.!$0v|=g>l vz(8ĵ. 󀁿/x3߂ٴn;L!0} 7b _7j 2]fșg^"A=E}j6SMAd?' ީrVSڵ}yΉ!F9&NFCp6@F|wػL6vwDټ~eLN^O+8d/vYN75 "3*`cj{x~O?kS5x`=7W_ۭ3=QxBKG yO9UZf5y! }~|yN%z]zgv5g?Sgkځuyϟx?|S'~~nk;G n.W=qSGoI`Ƅ uuyO+}p=Hud bK+r2bξP>Q#sKµsD]2ё}7g`=4!tc!Ӊ离̑tD/ssL=gN; >qw*`R?:6Ç}`7;ZTz D:xɧn#O ??p"֪k{{|µ9;ֱ)stۿ=΀5p os^CP߉PJ^O9KVZ΅u_nj|YߘMK|Et O>4vX=C1sB!}s߲ˢG`XR7:5>۲O?x9A8 jְ`rj7c4p88Mp dN`92yg] B{w 3!= b|pN0~69?xc-:{33A >Z4|-[g ]iÓ[TPY1Yk'MPfbvWaz&|]Bu Uy/y$9's00 Ab b[i9vi^[UVUZE"3@HH p!y]'++'{?{_!CWy5=O{Eo2~_N4^qb{E/-^Gww1.Ex!,w[<Z|B n{1O?8N>;y\iޞnw/1Q2V6أN[j_jwYGK%M^kb+>dx9+];LnMnhpS[tgN8Diݓdس-S?q9-%|n^up^eZwͼX;{3G1XcN~cP}glz=וߣso$rs5Om䌑Ů7vwlwu/61oSܺ%~]dÒ#wx; 3&&ҷIwɋJ[d"NA?2: ݘ^@Wr%4<É' w@)p,/+;So}rܻKQ.>}1Ɩ|+eE ~՞5a|ֳ=yzY"g*k7 ="pM\g;}f՘L_s DXK/+Wy>o~~qʗ 8K. Ͽo5^[lʩ;+aϓ-u wsdLV/.AƮE7 ˚:,x`U.B׫d*w]ik Y{LkdKZXϿ`<_Б<ȶWQu~䰞-={xgRt$~m2}@ye屛vczy/sN=;w_r| o(dz4P.!8,@S 08_髡 $}[6~Ĺ^eP|oa셿1Sgĸrp =r=֒⭱ֳ VǸkVʚ߉{VWK:ˌg) UO9Nig9/RM;}gS3hGk-WYek_Xϖ`پv{Lzä2ҙeڛy7c0o?5ǝ\-qCG-ݔ∙#2NΟؿ8߹< סwЕKO-gWؘr ?M wuѓI߈}9/ӷ[pڟd<±Yػ_%7.)s?{ңsƟ}Bw?(4*Fm=S=N>c4V%=0@ w6${c ~;_V~uy'JQ|lN9jQy 2<1_\?{gw||:񗟛m0v-WX6#n峁\ׁߞqCulj8?{Q<axya~ `es [SN=y D9G?J,Rᄚ|+ ^߁tʛw_X A0~c1gyKp H{t/:D 0jO/A dc,w: k^/~1݇kSyF7g}'j/ʷd ^91oKv|W2Q?|{h;lgm?n*^@Yuh>tTy;V9!| n%Iުx @O!oew+>|Oy\@zmSFRnl3d=kB8U(P[CfwަFQ'ǽ_Riڐ}S{wu~rwg|O { ;qȓ|O}卟~c9|~ =挭`QKcc<`q͊>uT^6yB`'׍[eBxoHʵ[y b\wawW<'/28s`sMH!70!zY>zȸ*WzC@ Sspc߸kF뒣EA}GPgʩ=L=sjؚj|Ln,6ʙA HܲGu5f\,c*xtܷL?~XYusY3ˤs+(cmx b ᦾxƇ g_쉟r=otõwpk=^kև~L {dn<3/{#f`C<ǘ[\B?=+31N~sO%p[͡}cھ%6 ФS')3G.9*y7)=mz 9r1W^[mYgvyyv쥲˲zn1fD9'qF5\聯GfTv?>UO*cj ʣdlؾf{a"w}ܯ[=زkǮşae¯;Б9mzc 5=pfwۀLJl|8sB/ 9tZ3s5҇4y<߫:~232_~W%7,s;l]}t[Ki~;v! 5/yrCa[&-wnɺ|C韫t 92zÜO|mEX%-ۍ v{鵖^ڼ'kn#\MA^C^Vy&=$Ȏjy ޓ?X`g;tÙsWcΎs;[Sen@ΖKwϜ? sngO@w{]eʖ#~ e#~j򖫃ޞ/|Gm녱F+c w'޷/'֡=tgȐ/^N2??&sV<#8{Ip5c{}q ?2~8t.!s @2ߋ5JKqO3ʚ嵵-e1edG}WYV? 5[֕1>j혚&1[>Q-pbC]__8sm1G/';L`j+8Wğ>u*|_~SΝpT?:a|ˉ!敾Ç'ʮm߼6v13>2snk &4H1mLLܿM|y.My_NkҽG\C5? ҇F3G?Ou?wyq)K:߬f;SE}iL{7$>Y/ko"҆~z[To۾\kq~Idl%mtxog@P=MNpj!G#5@/ǽ/qїeMɿߵ?{Id^Ô2uT晈kҵwvL߈9^0{CY:3[s/ c4~Psog }G 7~̆6e?]VlzzSHExܳ[Nȶuplrꃮؖ74_r W^/}Ǧ<8 o^!Lsm=!GMCj#`Ě+񐅘tO8Bp^ka-ؼlٚ_!rcN8WcBW=yֺ5]H́Ϝ`/YбU6 k?ס 56S@ѾE؈/}X\܍;|Ʉnb]jt=ƞ1'?2ʙO=VpYwwͺMp_uzgxݼ_7=`Q{G}`6e'!d7KocͲ;0h_6կ}oa%?O,A\A,{_wYluY~ҵ`|x^a UX[`ݷwUՇU6qsCچ2띳sG75w?[ؠp^,XO&M嬻Z=2rɗ8?ƿ×M3[ =V\!2e̯Qo5M>ɉt=Ѝ3Nf@ڿ,˟W\ϞSNeަ1Vdiq'ј |rCzѩyeKC҇NFt=坴D_?&kF9*V_)lzD~ŠXQ >|S2剟?QdIٴtS>hx7,n\X{o^s~Թ,'9.573j;_g6,_yklT ]?qr'0 ׀1˖15kڰ08ώ_y~a'?g~sTf5~ w5ҝ`&Χͯkɢa{ݘ|{U2_ѫ*W>\|z}~BK/ſ C}fֵ'#:Uk&YmCzxv- }9*y׬֢Mg)y/EeYOd1oozm녳M.p˜Mކ=1/\[wZQ^w`HQپU={gC̘sCbFW>v!/dz=CK9j dF\WЃb?˾}[BУHe~˂ߨKbok,T=(Z= ^EqؿߊcU<7|[|(f`ȸ!yMc>` 5?Y8nƢzT׏ 4q-K*Gpr-9՟{rR~xW1G~ s0;}\v*sn:Ý ~KoYk=6tAǶc\D2]kF>wq sSum {IK]݁L_s><\'W<7r+1z>+gorۏ)gϞ^f]2+uywbrJ%c5VnE钛̷,ǞvlQƷ?M^}2'ɉv<83U-s9Y zsz%'?g`9z QG8V8}SҞؖ5bCܫO[VgO<0{B1ڽ/q|l|moHO'8ofp-3F܌~޻O\`6fx M؟yt5>A^zrݒWM}8ĈY#rl}F*9-֋o WIg˾v]>J~qouKG2W>loOoOy8q}6\{}齫wy_-liԻZ_ pv2UclaXSs\ kz}OP k7wvm3; FgWqZ7=lJ*.}>b+_yAѣ%ydQy`.Sr͏n`. @<]lӍGmMj="{c jYZCϪ=W8t>k}RXSGOCi</_߱68\|3pj-j(W?A6>o}}rx{.2rԕE>a$kj|+^/ww&X#mȱo.)cMݔ_,K-M79 7̛Rm~i;M䲳+|}yrr,3Y^]jӿ>lK6阋$~lC5aԪh:6@,1wgϹ>{Y}?{+v.c[^ycL韶k#ɡ?ԑLC\cԼQ{|X/t* jy;m/mK}hqZ?m+}ia]\p9fm}r:vEe[&=뗻f)ux;@'WZy!/#ى l]oBoRx7_;69\lXy5J×^xșOP>1\FC<Χ:7sHml9Z/n-:s/*ʺA﻽d}ȇ>R>M{0g'ܒ-Nxky Kpc ρ[lHxX3a>\= ළ#z18ՠ\}z]Kص0moCz|_XkNo\ p F?̓l\+9>zK.Ů~ʯKϨLKSOZ⁗?1b}rV^M_X·b_&;^GiqkhBk{B!鳕(c>1q5 k}6Xc5zqJ\wY|Icy\xqSxm7ھI'M*yX9d^wK'á#9d] zV jfwvxyGybؕẕ=Gdȋ7X&ubƛᅽHZ{gcC3޵;ʪ{VXP:免goée7.\kXӗէhmG=*s`n_ٝ1o}𼖾-{J0EN{&ƛ>Mo=8O/^GJX.<4gk7-zCnx ew2˔/۟/ʖU[P>5/Y eo+vHxdCW {B/._נ{$ObɅ 4檧2sig]s˃K!/_83Wus`O^ݮ.^QjZho8=k!,0/q9Yp$dC̞X۲/&6{{eoަ ]¢\QMZgW>{\tw 63b }YupM^{ |g;a$9=v4߳{gO>:$,d?w͊U{f;^k0NྵwryZi|'guVe3|ktU#~8b y3)9k\{Cgwݜ5) LL;ygt̋H]=7}7s4;}G\vDʵ1T!& a+]qzsܷy_)o/~N{ݷ7mn~&~C?t.{V)74q+{v7/t'9kLɫ%rI'7 MzfG ]?_=܁a'kx6y>8NXo֏l=-o]^Yge^I'vw;lbHl;†0j'v |am Wj a}}ّgV>̅9p߽氹q!rWcp_: 6^xU]wuiϒ ezg?Ybe,V־y'FPzݗko=jvBr0ڣ!FC6_9}^V|5ȤpM>7cW t-\J\A'_Yxu$7D>eߙYqUk2q73kjol9K#.ْ^.Ϯ[L~Zuy;zPwqid _L83t=BV~_^ɱ_!͍}F,8 HWػ3ځCSx>8?\u谏}vW]{!Cj8\덧 wǮX3㽎СqUᅡ]Q[sRSV}rG?KۄPWpL齷wr=g٬G˕I:wiqn:ŤՙPW(+~"}5_WqWNIJwކ`@߼t3?@+=,'-{?l_~`{Ypמ]ɳ#N Տa(>21M߁מ^vÆWw7~qGȓaFriv_uzЦ~qHå7O]>289 saS&HZy}w opq瓝?n,~Ѳ2aH̼df(ik0x{o">8cv/c03uI ?mo@5O+M }Aew.6w2og[|\n cOySؾ:|:˿orD+kB73y!%;?X=Wݯ|su9rD9aCU.wf}6v ,:bv:a!n Ka3`L,c\8ɾ~{]Ԙ}[9z+?0[+8D}8~{Al˗yxakZv`s/r={|>7nyW5nƵ8 }Xu5*_z?=jGȚY?5Z!5Ŕ̿ D!~gD[{7b匿=#{na{{ɝ>sdƄd,ţ$n4S 󕱁A=^.3Mmq DFxtյaf@zoҫ"H1FXbĂ5(6TDEޑ"UL9l03.kw?*{n)qNYv47 CCB$->^:UPX2"#e'ɝ]Hth[yyynf)oj+2#۽/ {ZG~.uZ?^_^Z[<-UfuW'5}=='Grp>$nֺJ[{MKH\9^_/;jjIO yyyQ>mOhW9#%%Ɇmۤm-?īhQ'Ycx${Kk]T쨐})Kth 0ikU!R%,N?mhC&ʁh[V'FF˨{FIIJ~5IF)ܮə#{j.loJ%wQv5DZZp[=m/<~OДPITv$Kp=$4D7J7!R%sDFJSqTX%}%:?ZǼ]*|UbM( $*#JZ[jSvҁ|Y`#a)!a!RaOJp巕ɩe욈I(|Y4 63hxD% -=⥽]£d{l~~Z{H""%:%Z'w8/:u޶6JE8$Qڛڭ11|YbJy1|ӹW^N6HLil֚V͌Ils!Ю+._¥@D&F\΍*qq֜dOܰi-l_"S#]2gH{GxTHJə#׎&ɾ<[aSYRNRǦJ݁:ir!1b?BR|wg0`***^nݺI>uuuj*ڵ5J䧟~f]l}W}t6lqQ6O ޺u7UN:Ɋ+G_Vvcc2p@{GMאAIll?СC%^׽'NHttظq5tUYYi{?}IېIw=H]|CɴiomڴIRSSM>9~VUUI߾}/e׮]+ӧOΝ;L?mB+0a6S?ŊÇ{ȑ#SOɂ dΜ9߻>|XƎkrF>)))gEcƌ]FF\wu&A9Ǐoeر|'2zhxq 2C^pݻS( p$wOKHiDٺ[Rbl ?WghݓIXxS=,Hg εvtāձ:vzwfΔD wNNi}ӹoS9-˗ˏ:F}!AIH:v}N(cYuh%*y}[eסq_2C6tײ3Dzeä.-C:; +Y%iaϚ[ᙴp\ܷFJJ۬Hψ7;u|$ H2,m>,ɃmxN.˲ky^ëˇ?o<_\1D $aPfJZì( =>g3MgV9-^ ʥ醁5jB92%<9\Im+k${N@}EQ*(+?g?+Re֣Vb¤xH@S!m6 G{`l}eCDdtM=0-&RBrJs]46VNjo0v-%-6ѪWIڔ4Ib|(P$H\8MNgNʔΑ޻=)R ~z$tM0ٔ_.5;j$sj$M4R㱱J2&gD}Ƙ}P$eJ|x T}OM7Yp]F}>ToOKXT4i4larziioh7(=V+1-Tngjio/y[BC?>(sk׮<11paͼ^۷~ ?X_5z2^I8> 2 08>gP =!pQVVff?w;qDs uw0rɆb/!֭޺ƳSe:k\AX_},N9A`# ,ܢ m<T]x2r?ȃ1B^=z??83O>G9>s)Z/¸?lcaByQ܀9w|)>[q{O)i]wՊ)-ǺdCdåX13N_ZZM ])Ƹ35  KhTa@s  (YJ~pI1`@`};fCʍn~ԱAίފUSz{君txybT/cYRw~?H\)p_c>oϟsg~_Wf`R/""<gCcX˟kcyr:"U>qRX٨#|={@Lj vt]EDHzZ!I+F&(T+2$SMm*o [jP72^udp}Uo%o}3}iD75.&Z cvqa9ZZd?'7ʤ&M%C3l1-"R+[\~.OYS$[ux ?7#>44KriQgIʨqU3L--RcW. r%_hL+UxApOIl+$[N~2[L~$RPoϔoO"S"%,f` t.C%cHɌgV"_Jhl)yם1)fA--Rij GL,%ϋ@r7W\Lg-H._?}kd476JOO6FcY#a&lU>י};M闥ƫ̱@]@*wUX uПNן7E&|%QzmRsFV*)T^yluzk*_veހNdM7Yg^+ p 2s8:{k:z*Anxn3\aGp2k,> ̡1tG)R] ,>`"80 zΝ;/nv^sC.]jvxM7d#]'}ch721ۻwa(e"ʥߴ ]y x|رcvg)>Od2En?u߫JnV&|Cu8G%KL3fס 6O=]H{&7KXh1y1ƫRs[pd?Mg†kByqp(yR~\1\Ct-8\#33w_ ٌ8P󨫎OzP]>?x S޷.eҹOe>t^s@]~9sf!z:gjmhY Bxoj*.x}"u 9'v'{t~Џ۔X0W.S>w3ڮWyݠsPAB_#ђ?0_0#Q Q">kVĞkTt)\->: qfoM,}.#݇uu5+tLj!!Z*%ߔH+;>37ۥ]|tt.l }l㮸kX{B?=]z]l~)\Vh Dՙ$*;J{ś ;kM80_tz4io^_Whz:*hoiU-aIav-󭽶 ve$wp]513ڋϺfv&9H:AK ȕ/\ipMj+/Kژ4 I8Mrd>Ym\.gHw˗QVr^w82˚eV5 >s}=:wOj{\WYm^.kFN:!rɿ(߸ 6t0e5CE}6cv2":p6lu${a7P)^Y,RMzG7g> |b ?7M#OqMR]'6C5%_GeES婧qx&ageu@Di_y5pcwSXYXk=8 փ[9c^g{7oa8B`$XogK סo9N6=܀Smѵ-G|IG2.k 4IQQ:_ e}[u 'X m[7zPy:\e}ʎӱ>G)AD9徨sۧso>?#u$ kVТK^ 3V}cA۔:\zNNμ./>7(שRݣbu>vlqiqmZ7ӱ3{dJlrm+ƣfqc%.'N*OV^KN?*_'_8nΊ9цIv3fؚ i9J3?<2?Rݰ/_.كu-j{6)Hҧ|ZWe<[@(X8 u?8ɱ+oLйQ:1氍([]f_|i򘘳(7r 4m8:cZ= tK tMdo?._ib'ߑ]yKn MJ0<6uGDEoO}XEv} {D ]4b3ZT_Wjz2XHLHKy{b?xřΘ_:8BɊ{ę~+{+O$!!&=2b[O7ڃg،]rOy/0*2'қ;){>q+Jt;IHk<&)6ءM{]-QU6=IF !ꞳX vpgu0>z LgKu&ư::+EXu.2,ꫯ̎ p ^zf`:.ɴ~NoESL>b˦.&pܺ h3h3ӱ!O~i7|\ _H}̤e˖/05xY uP>[ij|b>1 #܇#&~d߂>aG`_J~ؘR6m>܇qM-7 (w1 \ #P_LjtIZ[ k֚e:V[1;j;jGgHK8J=`=qIݒbOCzRS\#^/n&,yTvU:n9:/Ư]@ǸQbu:>wXaxlU6 $}_2')H@^ VoʱUƯ_/:/\ۺ=O賅>&MLqU֧J׺Hy6-Ω_EGY:Ei}z vܢa:1A@H/UYA<@M1y2!V&2 W{"sc% %0|-Dg[Ѧ3m`6 ^.EIނk/fg^LJ|)ڗh/Y Ti BYWd2p`{NIHl sc[]GryWSN2oM^\Vf.e[ˤf{DFYʹ.`c}p,1_MRn Y8DYfdH/BUӱ&\aϋ+%T O 7Mdx)M>y!{p=$.D""-Nq-2Qs|Oi8 I(槈m6e#Zz锁t9+{ qG5bI_bI>?Y¥@]ۤI&O{ҹO*gJ#Z6a9꠬uW{1%b3u*š I`6rtxY:4<{ ׀`^ `ve>:bh 7x~<2rH+zx ꄴ8&7I#ˇP&m{ ߃{^|'>'|Ҹ #L}pLxO_D S28 GC(`(vmgc2L<%rAs=[2DN.6@?cӟdp9ߨ9 Sތ!Gqai##Gh׫*xrɟ.ugc~NWں.snfA?'lTdb;!;i1ڽ/M.NUyʾYWڨK|VaKu.Թ!t]7Ry}˿*,:{8DE<ػZ땋ԟ`p:unQj̖8X}Tf}ϱ\I!pX׽|n^$[JL޷UǕo9Ӻ|y}&hUFsΗ Dp l?1gS:Ș?1,JO6@u$lX?Fk?g?aOq ŹW皭8#o]tY^5V:_Yqq`! LgW:81]$ItFmc$O{^Z Z aA%6(K[l5{j,.11[lם,+#b&)5jl..&~;Ͱ؋(_=?~"^&j(IjqSѲ-ra>2򮑆ᭁVS+iXAN]%f+ֹB6n40IB;BekeeCe=#Z#[m ]G$<?z==9C^ x 0gMf77byzg+N8xglX۸¡ ﲪJY&C˂ +W NOӣ9/qɵ`%8g`"op^Ϝ9`;l`>&M2X&cra*X?9rA <ßcKGFCp"E,6 N^|W*pl w$B86b _`$ӳm 7!;p& 7xϟ?}rRm` [ワ\6;_YP>i0~y=2|؆rC.p]șDḮϱ pKpM0?XVo~X7Z^M4?{u=Ύ|Olk,11YnȚIk\?O+em SYtq~XK s|:khbulTnX=hV}pᅺbT˻]OB6h]u,۴ tl)gwqJs_׹6I#&1:,Y{7|}Apqp-h?RUL>PjxGΗM'.S;o{XzdM$~W[\1s~ӱcg/tQp|`!eƻX@b>t+3(z18,#,sݝ V1pzh'2`g}fm6RFx{8=׻Iʄe\@.C~_n63N&,ZlίsEh|DS'.ȸ&x92_~ ׹=c-cvpȂ6 dƚ|73^B0{ƌfr{H9pBi.o=v4Ȱ+ac ܷblV>W5Fy%1bUn%:',;:V>{%K ҧTc)ӟ %nU~Qjv;_`Õ{t CF|1Adҵ`@۱Ƹ1 wVsru:gOaC9g0􆡒.lM1Lag:>߻Ru]gXY-/sd-r_.Asyv&/`Ϙa1~IEo!)^- ƍU~۳yۋW{qhgxۓ­o'n~$ 9`Wʋ2<"n9;5l"#NགI!gN~R>?\ehp7[=Zw-l\@G4f#:g>ʛA}W{Ob/йgguc/2DFEZL(s{ͷRjb%2g bhǾ9&I]M0]~=t4t '摕ya# G8WΜklÁ9'pAJ.1([7|cboGO ߈mc{ T8\kq]Uɻ+%KdE̳XYǬοzN?Xz >pc?cg X믿ķ΀}p ㌡/w!o}}؁ܠ A8b'mCx |E?Gw Ls-z?݀,{xcOuvo`*~Gw兏{P'&6#M>{2w#z=c20.Ȉ1O<+1uoc {AOcQ/}t9 E$eȗz;fS eaq1)-.cWc}%_ eoZh1ıagXb/LL1}fl?*<_'e!٨iڮ{uCz7spBDt/Ѿ@LeNl`;W^)ı>UL1?2pvRZv뱑(~rWu|Ͽ)) Z9FP.zsK< 8}XN̈3uk#α׳?@wWtDT91S׊9 :#P?ת\ҵM>|=_ ``H}%kji1mg{Eƞ>5ƾ~o/b\O}H\nk|3!=@c#lO1;pK{|!!]: ]9wt1=8 M X |{PT|[a}& D `yt{#g6tzQlXWx /_ɠ- ƿ@L&,Ď@P΂z|x~Fsȱ6oݙ8.[_*MM7> ]8TrދO $yxqAß̿aBo&GKj SU"e~|5GVIڸ4p5H GHqnO){i5`>acO{Cd/[=U=x!#8sAV߿Z@]袬`,XwyO\SvXw)df0;s6دWץ}ԋ]3p꩏is/ɋ7%S`}Yi 7 f= >P?oq09lK?}||ON.W-r ೡmp\{ ?%%ثq_s)|DP ރ<(3~~"W>;8#gk ŋ۸gn)th7s9Sv ;x1GˀgBث|"?wyFY+'6+ºnsn~8X[zqtuH`S&`N`;s%ャeʋnYYY`e H6M8YT{0:W6ؐ h^Os0RUNKm y{SEK(>O[kz]>e\u?`o%Dsz7 G{Q9yZW`έ^3ZroUUS}'<5L5EI85wO:?:mqw+-^i`;'$wjԝ3~| n[fbs%-fs*oN[.8l ߍ7wop&ZڋKhL13># ^XG9A3^tvlX&ǁ&p\9è+Ś<7nx s. uw:?:or!8IGچO_)X=gBs))ΛroÛ\@ Lׂ]'r^dJRPク 82~# >uhz6m~G@ t{*yuȂr r #>Ayi`_܀>p/ma ?x|#8_oC_c喤^{,*,1u\E;kG:":5B5$&MbW\}P0 yeDu~rr:Dz`xWxbh9:B26Gha+wUF٩Թ{gqu<SީnB>2CAf!Zx~׵}d&xp.bًUǻLnjo#/GpB(LBeElqt.0WN(2A)o9\ZUP|=g-1t'G[&'='$<W֗V3_/%OŲ+_Rn3q_m/lbKLqIWGG_Mf6_pLtEqU?brp x@[w/!p mozKB8aI'أG<+]wɚʠI$,-@BdH0LîߺPdŘ"q=R:`j/K$ePaˏ\D`qE~MqM$*QurzP@G~lx9s-:d>ge-wuc-~p<:&.)\%x?O>;6q~K'p Sbcb+u50 {4/C( wÉ(gH`\3>w tq~\ ƣC[V#@^CȘvWs£IO7y=rܡ7qQŸt{g@:+wʡkpd|ߌdD?->8P7s^`l?Bߏ9@?;g^8c˾&嚷_?ԝ3>k0ܙllxPYKYRG*bl1qXT<pGm;uCi[<}Q:9zWLkqv{rJYZxEr&ϓU3طOV}/TN'~r@l=F+G#{mπZ6*vdokײ|!/j?f Miaxk+7g\yKt/.ۧXZ=[U[$}bg[J^CrC[xY|]8–gƧ7ʘ_ O}Q;A&vl#}تWW+NoC-xG?%ǡ*Stdr6UG'l9lzr]a/{3\}Baްk]%%tݵ Y?焥IeyqG,/1? 1>_Ud4=x;iɢ{3D&[,ƣS|p1 ꝻXLXE.~r͵^k.gmƦW:7Knn_85ncGs۲7 5_`[8[=%KLk>XG[h`2`ӧo]p/ :]LvqM]. `YUr?upPS>0;NKE{'b\_ .tn/\q/K_cFM gpqƙ ΂<܏-.ĵ#~'N!EY8c|gp\@s6 ǜMLs\U?lwd̔Nc:YnZ{e;7E+S">π\`=V#ly,bMxV{VYmPN-{-n]|*Gѱ6:<~oW?SS.9] 'A~K?~'tn^Xu>u'xn!AF?Jqʏ>@@>uӾu1~_s,&xsK$s-}s:3`rI vv^H^}[phv\d9yΝC>Au;}eZ'KӒԧ$kB,Ǡg|||<7T鳟d-M- wfcL Dz71C\ 1`2qk4*[][\=8g%v^\8?Y3$,9 !馽sOX˲B/2MI4}wY';^!/cI;¥Rao`߁5PG ~BBy56MLq__qϱg,r=gQCd_aC3U:b$+X pvY)k]6[>]/x;s|7xi/e1. 8up0] F?w8:<pI^:vW1{ >=j>zO&|~'VCCmj"Xp_ h f|f._cX-ZgrlxK߿ηd]3@ {GvWUMwΖZqX}NVYos Xcc-/$9)Y,rlYlJۯIG o[5m*:>{So?O4|_󇵟2.rݨG_bzvV@k`Kɧ%6٫oӼZ 0ժCN{ڋ]TB?~+p'g$ۓb @Ǝ >Y:>yHijkgxzlı`>X-tvV۞} 3?oo㫾Y2wԜ[V"q=Cp9~GNay{7W}C|t~1V>b] !voK$gZ0)brrYɺoc? XN$tfq.f'eIBY` )4l90 \bN0 Y, fw\<3l/oM ԁ k&oM賎=΍]̾'/o٭GS.5O߃XgC\J_\| Cty{^ˏϻ]_ bBwz>溼GX𡀙Ph/sl`)CǞg[`uo8eRtF~49 d쩃{?e.0;A~5r'gAy;]ٯSxW-'ФG&Ɂ=4 1["pzl5}"/@M ] /i[?j 7}Qh?sHۋ΍fmk%_U|L$׫\{_ߩcޠߩN8 ZyqtN̶ d3O֧t ^G9~϶8oֵUفKI8mG>_;t~ի 1Oj3ɷeπL!S#/>34 Wy_@[(/Wyy˹Gy#F%x6e[0)b,^pԽ.g_[G|m^D_~zۧ|ntM&o;Og٘\Jfٙ+=~%OL9 a{:4ggya#7cȾ+rmfsι2%=y7ye s#nl}2/eUCMG&Y<}$p@l>zgWBx'gXn0;#?>^RWjmZ)yWΓ'.k/bT =luW[!. *8 ;+?1lnϸX)tw+|3p _l?p 7YYzyQ&vuAh}z>^9VP&}+<uga{_b偳_Scp1,`e;Hȓ~P>H]ԍ<d}@^.}dl'qpW7lș\ƕx/ʡ~ggSA|7z/ 92n2'Pb{p/B`>10̇2ܹ\s23"GJH}KȀDhgE|b#~;;`>2Ι#9*;=ߢ_u[82Eģ:v'F_o5t\8w̮=9bH8'^' Y`f;cs`eOnai K+Z񡵤#p;~}b,Wzo^dIG:%Wo=;r+qppJ?]}9}?L|c*$cFaU}픦&iI?hk׼/6o-yBJv%* g dc0&T]q C֏_΅(^^lkovmhmaDRsd^lul|bL3lzܠQKz` ޻ ^3jq0ّf>`:@r|\/E`q`$4rKr8L?2*b] }ÿs}xOȔug%S|/< oM{ѕY^w@癆P{h#̸IÝ=1 l+`9<K䋝;'[^"?[`|` WM<|@p&m!|.>wL-Ȋ76 ǮOqgpF~~o=C.6xs\O.4܀! s{*]9ζFsG |vS}She t|;ʧbeczxk--߷^>"5Gjd5Dž=̾Kny;; KS<#3fXNs@9 3UV1ج*{6zvf_0LrO!BѾ%Hu<?I|RX{ tuJ̰g>GRܞ6 Z4HzdYuG͋ޔyr]JL'I#aVt/dstu%91^΢x *np}rd;[+N>X|w˳=} ܏30 X `ʜ靑qr?بF.tTi&Fã,BS;c[ 6Geq7'o8ϋ%{v9{7 >ג=NN M.ݕCA7$FEˎwOvKrj}2̷RXf>9S{6v} /8<9eP\ƃ޹ʥ+_)]fk¹{jma*>Þ]][;2/ʴ3(黝q~I2dZ^* ȺZ͋ܩg}u1`:*z cs 8qs]xI;3p \ `dXJ{0Cg% .6 A[]<|_p#w@Fpr#d!@]\}EouCt~ʆ||ȝXlgQ7xL]"MɌ<6s-'5\eƃۯ muK.ѝ ::#OO rs1q-ti{lo̢%|5dR] TT-?$:gn;2ַ5^>h!ޙv){u݌P~̙tɓO~kοTq4 p1`yzt]cqLmZ\k]<-Wbۃ~GGiD!=~ց=ʍBR]O||Ba }_߳G>W=,{ʕ7C7e-d-c-6Lok]y/KnZ ݺؓ؛h6]˿-?ĝ)#.6fB*&w!a7N<)]Y*9X$oyrNG p/3:|p.r$ @9{S}l~u_{>a2!MM? 8FlϚeCȺ$yTUld?8 ;Pp7|rNɋD'8dpW%Rl~{0r(.wx-bXQZxqʶP4p#!f36sgb;͚M; w3ʥ.p' qvz0[Cs*ĵq lϞk|Ί,y'} w&"Zg\K'|lF-.F rb|h3u󉬸2]`'~=|(|Od.g}qxjp:?m=NxJuɠde -F}0ۼ/?8܀/Qy ~kw{iT&]M ~ m Jl߁ [ )`onUDovJafDF4T5{ߓrwʣ>j͟lun9:1xHM1;ffqv^G~w08E` j _pq`x:,1.'>eS;K]tMw.Í9uaWG]CBܾ+ o7Ys ݹC?] 킇!/gOݾJGVםчcpe_!e,z18N_z%]l͝}N^(di+܃h#G. }K`;x NA@K] s//l<`;mLs< By6w65iy ,x–zڈz~Cs݃RrDF)+]/jKٶ2y+lcE[>{ l ɫ;P}?#;-x&1!V7'G׾39oA8q엻1k4V}ob g|rsȕt__ ~u<[9J|Ϫw/_{q A{??)SPC~:G) pRٮs-zY\*;%Ev<I8! 0=~#%rOk=gǹ`뺯OnŎuIugLd!9,ckb{]?9{19;a`|K2^.`*O<8uό\ǀ/g+rs-k-k285 5KXj8C Cm"b Z5q`!xά;:r${ #ea̡r߅x?bI/vI6rpV(xJfù7)-{;hq>bںp-2_Ya mLLRCQ O(Y]]SsnjlN eH3S@( r\ ,}}ٯr=<]}eWWv<#c0^{j? P$G[փ`p]uL5GTb6^αڸpcR3swa ^ Wbm#Wl;؃O.1~r 0W~x́%O3oXJU|:ghc[ç^g/xn'nlj@X7YA-| dXh\C#k+Dԯs<_j|Jv<և5|d,ȘGV)Qc(毡[g ̙vuZ5y2׽;{{x~mmuanAg_F7ܴ351Ml2)ۮ}Sb þ> O>`3U峞ط`Μ2:3cO]q2qoqd#S9>[2?Sb0}zYk~v'~n~̛:=a331o{Di2)䒛?+֏x,1yO\'<7H1❪ K!;l!ORK,rů }8;~u{HGrՏĜ<>֤b /=/5XLj <|=Ov;Rid#>C?zrr~nw+nW)F[ũG@KǦ\?& O'.x6rUl iwN@s2`ub +#~0k\Ŀ۷Ovg|2a~D{=|䈌ԣlyb[l"?3d'yx߲ϳCg>s%cOuq d#{g?~ps',P `j noL } h']3õL W9h?@{x  1klvYC1ytCm~rѣ5F{6\EgV\2w_[36{X_Nxk9?}*?kg{م c}Y_5՚O`\rWƝM\z~WyS%_Y*=l}jkܰC&O;zߜ,۞ߖzVHnqSǕO,mmY$jG CrMїԵ[kqsx?:s8<~ycޠ|#1 K[Cw&g1vpq}\xh+΀Izyl|Qk7 |@ȸO}7r .5cqV^?׌~kBxw;bgv[y4ޅGŚ}ફ>na{u5"OG}nѢ2<0EcK^oF?5cXoĺ3P9ĸ;-gPPypf#ƔS))#+8䯁KbI2d?Ys"MG%Nߘ~otg-ߑ3Gfn^$S\if#!ɭrgnxcdg_[֓8 < F)_/n*G/371 bnAϗ1;Zqreg-{G_4:~n>}:+Z5`6_??S6upivQX󐞉ƱՑK.|M}yW~Jysie3Z(,KoZZկW2)}{{K$>0*r91$zSJo?d`wψ @׷wp\+#3vx_E?n|]tEjj <wܑm߭5>9{%n-,/o^n߯{9n:C1䓀pvFxa=<k'W>;Oȑ#`G{i6YOk{}O5Qkn7 Zn=l6 ƞ|7N`L #lpֆjMZWÜ7n"ն@7ƹֆ6!_lemڐa+U$-YT2Gz@zɺZ%|um+Zݪ3+ӏ\}u^^1S8ŤcxۣL7Q1^vYYA}w8>21'tbv,Jcmbs=Y-?3c_c̏ vPę1ǚxG Ys|=pq1?4F<52!6{np/{D/d{+ֹ\r T<bUX8~\f PwFg%נ1 fhAp-{XGx ^u N?q;y&^ݕܺJX&αÅ}G]3/~}h{ ܼ-;SF5WrASÖN.a {T_|%P1oj|^=\`lUfrLp3h 둷%l%5Yf{Ԝ}8T~er2ajmC 58G BVLm б]sݣrotnV>,Ҳ51t䁞zԲYc 1뿽7u}aAǸٵ'9|aw ׉a]$e;~Zk IJS',k 4j%;iQu/=ƀoa-~EqZXS֏{̛7/qη9|J}ޯ_A?sA#=]͇!=J̹oWs1*kkV9F9ye̩d&﷣M_0hO,0/.|WĚ[9{qzΟ_{;x'*ޛ!Az!G:؎󃷐c}#'{!!{=Ž_~~ɒ.d]2}f~u7up 9󿽳=yl2~H=fވ#.;%;ٙeSsFC,s-L׊uWO|[zSG9Zu{5Cأ-s˰N~^czLA8\z9K[zr0^;ZG8q9rȝxs别ZBG{qg,d '@Xz|SZ(Յ]]w]&>V^$k̬ j]Z/_6 }Eq/W_\u5OӸt21kL621ԏ*Oj1'tuw>{VK3K~;H&PS=bϑkㅟM /Gδv|x5-$Ϗ~Aar3m`!&hhl_/6{cuLcvȗF?eOqr41rq#y.luj?M^Blk=|16-_]'ޛ!x.9{r8^swSu)3/ϡsazY$ުg ={h ^dmu?|K{.\vbkdA<-^<71ik߽uw#'=pS)V,Ncsa'\8]sZ?~k{3PH\?d(18S$Ah̿l~xß(]/whDyvχ:7Q}Y^Oe)VɿH![=oR\/Wd.e) ]-dq<^ >Kˇp 4v<8/w{=6á5[j{.ۣÚsړa,I>jU,@o׾KójN?,p^L=s]Oվ*:Dcb,; S*?}sZ9`8KX<26Az|yScmn7a|U>AV=wd k@E;68*!]D=$k _c!/E#'VY|i~Vvj-Jv͵3@Fql0kĜ3cV9|×S/s67}kvA{B& wNB.~[+W)wΐw1cqqޫ?7XXOX{69bjb)Wn{6b-yRׯֲew=xȢ>63qb su0;>QaaqƜ~{Á{8ẉ_ xz"6ǺIg(;Q-&G\I|_b T~`:Z"}bLKcG\اV\{N<1k05qCx? }b#/bvKX}x1_&7h?a7w͞] kPc[!<X6}{_@uO{R#d㆕A J;t KMqǵ{^F@v3AFjyֳlM+ !`Kɼ)\tKٱzG*m[5/࠷ANj ~~gYzf܅؇/8L>/M{ː3ConkxKY&Gi֞{a)e7C\֙d#"XMyL0#헱e7(Lvʴ̩߳C*~X8hN쥵6>yA'e@OSptTt:{?pߋ=W!) k&?0|Rav 1mx`s*OWRsYFz05 c_G*nP.kM^ac9l3knk6Ta\ Ϯ}t}s_ S}kv_xMFAo69'QYG֜9c˨}̋%ivck. oPs"klkٲ&9NieǪw͉OűyWP3?:"l .(C;jЧg_$Al9N\xcƻq…Wۑ;Ҥ佅Gr_csioe;|p}>9OfSb:C86z| '?E͉{ޏgvA~BUq <~]*<ԏܔx3}C$΋l#~[Kdl\wdؐxc.9eܴts*wװ/'A?zum'חy+Gst[7EˣS;A6n}l}nk7_iО 5snĹ#dC`ߊ&L r3u3c٧vK!{l̳ybq&_@Fotro/[Wm-_gn[oyrK> ׏CYU;Fv,e #^cOfmĽ[vf7?9dNץd𱃓TBLmeyw=_eȱ{uܫQsϥ/U>6|z0~x8+W>itZS^lwm߆u|&qPs%` k#_&-ΡSǿ b۴fX5c[1p8+KC&C 뻟~"zKaUN`AYge`%y.W^sY_ 8ȆΒیF`|Őx>1G3ɢ0ݘU LJl'\>8oA1A+ Xf< ?(cu38t{Ngv>W^Bo@gϞbڧ\ʥbU sX鱾O~RD5{~g,#ߋC{V=!Gk_nځ&rc$z9։}45~NuDŽͫvP>lTX'7ڛKXc {ߕWqߞ''^251rq"{oI1-XP^W}rc0w~Z9,/~v|<䯿9ӿ_"Szca2>se[?"ç8,T]saѫ-dN>wŸvp66mjbx9{,mv|pgey˰#emI;?p樅[ݻ.9r3^myoBrlݓ^}zV8oxѺ;cِo7t1sƤL V,YQe&nٳ/?-_zr z+9wٸwG] Nu Ϋ'\5bPāZ~qL<ѯMNṘ| |.@#?'kPW\>=QܣK\7<>&I^|<3/G51mr.i2֪xwiя z=!xnlNH\òx 0^R.+ oF)[8ꜣʑY{NG C︺#eӃRF<"Ȝ綥ͮNf`'+_и<[~yb#䥲eSʌ<v. ݜ ^QJ{G@,opl;[}8:[uL[Y\CnK=6(w^ugY2YeYcRbOHgCȶޟ<; *ͧ!)l?}nOW΃[p6^߱[{{匯qg΅ 5N8OWkʡkkO.>-f9Zk9|}W`n9^'11tPWn]c_0F^fr{Lc/X \? %)V#WB^m7;{O9sT_ʏI}o- tˬ2x|}c7@r m>fxlVյ #]@Pl("Gc)fܘxhr5{.XQ J=M}Ώkﳟ#cSKYbB,I=w'%B{Idgl bIFvJ=x}h{,Km4uvZKwϤL{lJKׯ{n޾.muOkӵE~Ucmvіw=wGeeْݟ3=9َ3<,+Rz.Yj1W?ijc6LmhmODÇuuwl~Q N`-Zdj?!K~EaNy%r}^[m竟e'l8Ѫ,zD߿Ϊ1S_o5yUFM..U{CXeMk?}E{OldkwZZlGG妦Z~fYLExA.z~Im{XB|m'1KK Ze33ƖY^eu5vYGuoȳyVfII`oG[Lo+1!;i}}fũ`9$zzz,YtD`yd^fmֵSmҍ/ّWi nX` ݖ`}}VṉA|˦obV4Ȳd[_OuYEZ*{k뭻7Ȳf]a/,cZ;-65`Z\K:lYVrzߡÇ-q Xٖ{DzvZVj='>~yYLK-I/:,8rX@N'm;6Z>kqRKSiC Z2Ȓ m@ky_Uػims`V0_1ݦ7ۭgޘuw[-η!;:ײeY뗭ֱOkoL-s :9Td}1ۿq}ʯ-bzS3~?>z{{9ٯdߏ?iIzj;Yۂ &[g}]vevXym? 7VUUDU{qC>iӞ?D{RÇ[iiwߵڀ__|͝;?~3Zv>]߶m5<{>Vg***?2eoܸ73л|roG}-\&hO?A푴O#Gt 1c :hgҢ_|Ç#F8/X8b뮻^[Y<Ɩ޹Ԛ6oVtTj\ ݖ!XŖ{\ /Xznmbmob`r~Z~N|>wn@ &Es 8x7b8'.9~$>犆T}~=i}\">=ӫů¨FhL^']=m'}{Mmu΄" ?KϷJ>OIY'1@&Gc`賚Jg+VX#O4$YgB=;tiܭu 魷wkX azn?$+C?,砿QnОU7f?䍘7yvJgZ$_5$=ğ'~hm“F+szW 9BJ\HLl^a/nK-gP}d.DɆ-0?* ϐ_`zFo3wRuvHLӃZb]SS>` u[uVwwo`3a9s~~CL.̡+:{,/e_kly>оkl;%ڍ7{,ꫯ:;x v_r%} `ڴi|} W^ymW7x#}_>ro,[zϒµ믿 gaziv>?fiog:< =` ς36ЏL#k̟?28ݥ=섧&2 Q`r:7`4o+onnvs1NxOC;`/σq</C1"/0>dMQ5Iڋ&Mcoh`d*vte AƄlZM_K/9Oկ/Ko;d k 1S8)#pCsN[X|qہ;OqY- t X'(߭!in &e'yra͟KNIDw]Zgj|gi-#rm ='eexIv9.|="LԚ|{:XcGoCbx[e)^t^vl]Cf\sܡ{,}WJaǍZo>ca7WRs9ڧTsz|ɸ`a}7G5s5[gwJ]<ͳ*"{fN4#'{>}]~ Z;7zW.$znbѓO@>p~ЫZ'zo1Dˊ띁jYݿV}o iee"YN]T+ָW4j]Pd86-)L4>=ލk:JdzD׵؁,$-[k fr@;A+?qDom[6I6k,Kz'mM̳A.GY]5ߪYz||KHwvuǬ90Zr*;xmFǸI. mkνVel &nimmj, *UXbnk7f<meS UVGs kba $ Xbrec\&CoH-K4B^[G;fSm[ͶֈW;o s"хtX;Dk?G`!Lk[5=rkO?mYA&Ay~FekO|o\׻ϣW}+^<7@{<{VE <Mt;]ܔUIg۷jL>+4[-}X_z=`2v _z>^myO~9\}oҵ4w6Wz>ֳW Di_EN§>\m|.vAwsdYEY]m3?ӆj#]y3e&K\Qc9r,sBX5`wQcXݼJO+u6mׯ6޽юclƲiֶٕJ+~ottJY6IFxr;jgjJ3zzk☞54s}Nn<`s?;Kב[`xaЅVA; }<߸|UXFB2} s~z#׀SZ0w6`'2x7+W6l3&gn %0u~;o1 h6 G}34;t?!Y?3_ڼ̳1'3XFewI+tt[8-?y [znzJf6^&Z,cѢqؼ[iYkS3ⱺTSºYp5>[Kw/GķB|z7K&dpz.!z{/3Ws#5Ou?o+Cja|{Fk?ޑ[%>?<v7K}_)Fb͢uh|Wk!_ߟ-ߵZ4o6O\_ oIN$Ӧ\r&8&&[Wm X ռ<}uمpϪW[厭}==VF6{:]Ώ>_75 m ?cHL q:?MOmM/nr2Vzsmֽ畻Le5}63,}H%%97h)9) 74:=-<-.Ŵ~ީ,9Kzπd[նewɌ;ʆ!y`W貴4k#:IcPyJgۏ~#rpX;:!{'6+Vމ}߁)`ޏKp[q>x}Y>t@COh_{7{9ρnKch&8N{`2OO0lp`ˆ`lu&w `.0 m~F?l,1|ǘGd ?c2`$36ڐ~y_7?Ƌ`\eT9G`i b7~Lh+&ZzA jY֨,c?:?C]Wr,;"R SV|wm_wړlWgj]u[bj߬uLCL+Nsl' s+SĤDy /tnz/]S۶Yv{S RxçRнj_}]VzJ h;l .LIHV߼Ol?ݱeDKJNry˔KH}M=}qC^?"9?Ҭq}/.4߻ / *O;6#l֕l C} 7tN/ΖN`OR|\eUfYOf66nxk} Nt`P}\e:6>`=}G`XO`}]=r ` 桷;4\+}: >&8.C`)<+^q mcG>vi?~l!wcah@B|N<s ZLE}HomԴuhg36x #SEj?VڑCk?}W8s=}=sq`D\3@L k8 q3k Xo6{qR٣]$rc6lW,^Mr=C}ՉG7NpqxDcD?Fnďa|zPH#{[yk`}jgi|`*pGW-:/"M_'ߺ껋nv4_#~ddK|_(vN'ƮP>֊51#dm`,oZ7w2 Ϸng ]gуfIi3f,D=<}yY9iyd4tc\IɟjIc n}gxߪ^V#? M'S%';ȵR^0@,AomUV9"w}?k3:c+wNs?#ઃ.hp:|܉d۱bJRes=ZwZ9n'he)X׋vtg4矷Zwu~O,v0_FWʥB{eYk5cu9ӧ>u=Mn)_fGUM>hۚ& 0v3Vq}o=Gu /-^8kt@>p}`oexߕ=9|!9pݐ\>kc <`W]uAXEhh AnoV6_0"2y& 6md ^ڂ=qK[Td 0B06Vp?74p?s Dߑ.0 yg7xMY(Ќ1#1/S'|{w>zGߌy&^y39eaȃGXc<:.]s#;N#r 1 :33n9G>w=?3 2,$ˆ;&/leˬyCz:z:IcCo2d.seۧmkصb]1%۪߭] vN)Mޔ^סyG?nE'֭neEs<ꧫ]f6SÐ^7)c|^=`V:!f>v~GA!-gbN\B .4rz ́c^) :VJϰ+:d,^5 |8q*t% 岃c!&$m$XǎmVyS"}6Yfy \Q2HV{75ۭ~?}}} cP>{w5]!f?A  0^" /<S͝|4ڄ51^Ҿrf< !_:d'{ICi wvW#h@pCf`-cħBdЉLh 1{B`SS;;|`䄜{.1&B\; |Kr$s{ 65ڇCn&_<َXQze<ٞx"3pdTmy>؀<9ʧ{_j~"Ӛ[$E7fkcEmUkeĞwg~bd@~WϓׇM}~gmޝN?iQLx9X13]:qs~^?qzhnR(B.iz DWz?%^֞|-ИW,%C 6=B Gl Zz}V*>5!.GW1_k"B Z-hC^Ejob}ld~Zh, m9?{n z괿`p$6ujxIr[W5tXk)'ͲJbJ <&}c.n׵ޭs|.9IJFd+]ilRSqx-vz~+؄,$kROM=9"`J $Xz+;1kH>+;q͉c'6aEC'Ǡ`Nc ) ﵃ZEk@ mx9M}-4.rKNxvہ,)+)?9qDrFWCXԽ\Cl"O]Uq`a ,cD4(V|t{ `kY~/;93.aSMu~6lipHޘ-xG{` C~=p:>qqob|b C8oxB<}#.ǹ%KطmPp1rBnCcȿg'mx ƜA7/ޡ2!F '_02d Ǝb=| ;)CD :hy kgB^Fg@7we β%Xb=o (kbwiE5QKܲoc'f׵+ӠG/෋Kt`# '/ըwX'D`Zgj?S䟅SBy%P#ض5_? &S /=-]~l `&/0G}a9Eqo+c?i~s,#&X_5o:ď'7Lb-#8i-xfsEX̆~-K>ҽ(S=OLk0132aשwPm# +&D`帏8 פwnn9hq \:9MOP|J/>r܆@!5Ը\Q#!ߧ.ugw;Wi-w*(nɮߓ&?4{m_`+)9s=9M<})\.ZB݇u޴{m4xDyH}gڊ+VذA< 5{%=03pg gٟWO_n!b?F&~bgE~WSO=j 7I+#!_fvtd0:<u})Й+|0AeNO =B3VBlE< X _1G|@K OB#א)/&9x M|W Zc}wx}ЏBЅ>Đk2@9ذP Yq={{׹ReN.^S ##vc^9[}Պ";b>]s-qw?KZ4ϋcJwok<7qqFNպM>Ysu=_~vEa:#U鑭bf6 w[OϡZI%.CܧO =uD%}cry5r*#5w-?ƽ^gW.|sl#3Zm뎺:t~4' +Wک#y;N*䭢C7uQVN|&F1Pakյ564,hyŧ@"|-><1.~Aa/8ݠGW˳-HMd =run鴪ӫ^gRaO3>[6toW#F KJ=TAz+9mڽqV{ҪҼתU+\?~dkrmg=}E' /-6=2ŮO{ 8#om _t9MW[_Zy+aAVW 'T?VmK*Fk}ˀ=YN9}}V^>׈LOqןrHwk}g矖澆Gc9Faf:~N. ΄|!v؃#ѿC\'{<6?zlvc?w|VG9!o ON}1580:b@. |0Bz %f0N' >2&GF6O-!pg;;)7ʲcbYiQwaxrx6J!XϱM)5^|o9p@<$ۢ7;;7{XcRk91.Nݙ4V݋=ʉ=aԈġG=5@kz{z`K%V>Zϋ2auN.Z]?VtDhq¾.`LO䳎5oK5Q>xBkZ+/>X#;SV޵ 750vbZ9_{/%O.O~@4=!>_ -';Kvw{xogʊ@&ѸF|H~A By?c^ϵm; :zߜ)ϱ̢L2vMt+Wj)ht )WyA}. ,E6֛koc&|b OK ns~y@g]h[;vux}[Sωy F0(?rq';Ơ rmڻ .CH/PZLr}7!/n?izߟ=<#WplG>W@` 9yG m S{2 r595H:}T̩no`6qЩYWk?[_XaOfOG m6`߃:j^Ʊ':kCk}{HL7zWl V 1 r :J԰C : >EsgGdL)~{` | @_>d`@.d tQro'/ <ۼotRwCI}'1W x˸GG..Br cC"_':2ߡgENC` j7Y:ѻt0 }OSQH roC*]I|Y-ܰވ'%flbX<O]e _p=u^ x b>]QݻLK?=N|Dx+q a$b|;jxVimf?G|( L:9S4u'zo;G?ت߲v}!^R6|h%+/uZoh~Jkl4o/\tMh \C|iRP>}{?5 ,Y vIBыL܃Sqz T(ެr>5E;P5u=^&O.X2ӕz3f0NtԿ`砽s[ZrN$V+H "bQOye^q!`;~5&l;υ۰x =qKZzBz1ScZ٘2v8޺USR߱=H_g Po oluvzzbѓӒ]84ghgX垏Hu۰= ^6GϧNߠyt I)xACM]hA?OWl cF`OG(x»39 9`Ú&cى?|LPsvB b 6JZn({pY0ԔBHlu<w8[[gr}ot[Zu 1NPaxM %,91o%P.u=HR1.uj^'nRR;Y37i΃ 917J!34^VZHϏBIVNj{FZ_WrVuǛºNW#s/vgD>rSk}Q"5q1Wr޿uuqQǏgCǷ0S ߭6Ϻ~Lh{Sv +@J<#`=ˀg^@COn0 n]dɪzC b`wl3ЏB&j,)U`;Ys3|_}#}=<K,N 9!= A3C=~ z ag{a[;8Kzrfp 1|G=NM{!XxFĥ5]9lB!w@'5<x "o&MB<C|h/?fA/Wxj1߅^ AO7kj@B'ڠk:`0^h S0y~eAvPbٲe.W!ǰh' g<&d8 w07A , 9)ȫ\3o˭@/= ?q7U5T.F ZyҜSkd;|gJyF2Nw*:O<bL\G-!яۿ&c{ja~Bd_./%96˼˩v?wF6Aw%vm_chq>gΕ|]ja*9,?1mZ a-#"kYgB4 I.+% 3~j| ܮQFN<}8[4?kƃ<~>^Z$Lr8E܃-瘻2 C#c=D]ح9zFkfwaAoP/6*"({n^=iM`O#Ő8 r;<>@=X3uu%bL]G<&ใYVTQd3鱂 ~/쯧!rZ8*t.6;#Chî@ 6'N sdRYɅYr? Z+r6ď^8A."yju}ᙯ?c|C$ds.䒁?`{$ sLeFO3ki\ g԰cُٟ.̡[.: s Oi}Ydm@8gÙs`{ cn!]M}4P?[ ~S`"6|/r.۰0KjP gnc+xL{97ΣP>8#1/<9C@FcuA7&&>ЁL݌U?f {x @Z78l|W"g ȳlo/x= ;k9q;a[?']{m,a>%'TT9MLV>I8Sqm,yz=cݭE؈N0 SVϜ(9p'}^^,#?!'|46jT;CC5Gӻ@,~{cC>"^}qԳ!FX^ZDb#~o_SH 2>'i0Dj #?Q%3oոXx@u6bfPp/:GqQoC^,{@');`.GR/[\'oayGu g-;Z\V;(~_,'=gtkbC.q79\u~/"_ 9y9ssӭfuMy^H2<1 [x\2kli-"6XrS(  N(r" zؠmō.} koUeUnK]9bpn,>cap@g_f/LB\p,xr٫1 D ,-6rv`(8? jԚa~69Ƌ.?y%_$|9_{܏DM]ut"NJo5G-}]l)'I%0f~/ϗ ~P6q n_7bWHgcW/z}aIB>5eluEݟ BYarNzX)Y[rzzSG@Ék[ۙxƹ_/&O_,^K:jSQ _lj rpT$ԇI yHfԊw Vr{OR_hGU[mq[_g'a,=k;yxL:>s+j^Y+kG:g{1Q.oփ`o5p,b~=>P&5N.Xw! W C@rNkHj";7zdlwdAgA hC%9m~"'p V8+ߴw`?ag> j:[hAwo4| 83l""KBm) ȖX"! M8rz085)A+ {rsǚa<]l혯aӤr>Ouޗ?WԔh.6`d |RC`@3񃼻.ǵf;'F:{POX G0G$C[oy\_sye"IVGAk#߁$0D_Wvrol69K.zTyG%nL;Z[-~:v;`#z:zwy v3Vg%?=3h&7@Qeg>n_ADd Fw:dAn"6ԌTSnMr溝$Y5|y$c&@\!^wwYģR{) W8i٫ٻIde_bǸ7L5݋= օsn|& d&{ٓQ{ wo۹ށC=z'A&3'Xohu L"L`!xI~=p ن'oYY[{ȋD! >A3r ^1~N0Y`VtPx̺Z)?w< :{g:j X'y饗z?u2 cCENA6 %d$ ט? :D *xD_m }xM;XBK3_jcjOF?Cx^Nw)>- &Prů#٥Z[sůq73a^![7o FLmu!qz7Ks-߻95KFxLl[R$`V> Q ˴NV⯨)q!0Gкm4o,UE!F})k(,KwU\J̥fA{ ] ڛ$'Gk ~'Kxu 9Z?8/sBu >f>ߏE#gD>~b#O PCa/> G|C{u˂y/jL~56)58d鵄Бy?};vާ< r?z~0hTMd ?n(/Z?bLXFn,2ͽ^@Vsy]J։q_?'ِ Qr(ؾ<3pES#_zGlz7KKL\kMtM7k#O)~m{]QY~Nh ~u=q _9Dhv('_8-^Gz3{y#['߅Z|^8?CX&ΰGBMaWg. Oq&`8Sqn7 Gwws7TA>y6%I`4|[ĄG m<p| uB`|*2X|q;"71اߌz%聟Pː7r a+Eیg5,|Xo7m猵w.փY$xs~Ya_8c.Y$ZEaѕO)~Z# 4s&Ou5i+9Z:bN$s#kA.н,$7OeDmlG!hak=_}cM _{f^rMˉד?W2u~OWx)q j &%]=^$FSNZ_jj#ǖ;C7qkп>liL}ErktpzT"!q'TmgP[Jk~r+yi͈ƞUk2%^Gspvqu@t"8C֥%bkZCbqzO]'n1߅Æ`;D=CwaXVyeeۘcbX(ƹM\Ţ דχ s秾0ys_{cvՖ73 b|? ڿ|Lx f8Ol -C>rDG . 385^;.hq Ykp`bo6lk|k7xc!uk@X=}X_\9AodokK)L>nN}EltkP(.-r=x9%ļ#3Ul`Nh^6S<_ЕCm=킻=9衴 Sz>vj܇ιnl)D`[8{~Nx~ BCp tߑ+E;K`592 y9+%k ;%|;nz"&!Q&s8Ϟ1 @79܃uY5!3½?!YyyP8 [5E޶F6vߐئW:ZMM $ߎy~ޗ!G~ru=F2c ~tqQ<&śĈk_nĤC^}vFYN9{%G abB=!j 8WװW?Ӛy3;"c̙v)1 # v/"jR[~"g{MC9)mЧySlŮNk[WGurd(YZ󴎉߮5E5gwpvGy⋽;Z |Xu :Dr!X s{{|Y#!ΏD/j|')rB#!`!gy1$?n8GH~%%ӢD=sgT{OoH/y&@`Qef#+ZJ)? z?? cqRχx9?x?5{#Ё&nȲ!ƀjr9$~ڹK-0_lix66=T.@ɥii٫7j#OigLy>17#qp g zy3`'AD'h̠S=FB\INgH^A"s . "9=9.hz^޼ݼƌ k݁C!2vlj`c7;~6w`!6xAd _ڠ_pP=b3;t @gn@8rpp@h5|ơG[XFlP7L1~MۡN?Xf3+`W` 9mj@/2 -l `} 5 H;@ lA v}dddpV0߅xI>č"pЋԀW!?N7vCsźYT5E6`g-@ s)2 }Eyr/~ {Grf3^ӓsbݢ8&ᲿSC}j\ԱKtUwưNc!c-SēDaqcDfL M&8ZΛOsCoIZ4fs $l(gfZJ5=P+k/:_lkB?t@W4j۬ ( 1yfu~kl5[ί | ^u$ߐF-{XQ~W|P44wvOMwp!%E-yzQܿh,7F܎xC"?g<%݃eQDg%w'ZB5R#8r(Q?_V gUf|ȾBjTE9 Đ`X9sxMdEm x9V8lcnXeS+mK'ϱz+[ ߏ@p1$v'k<#*ӭla5^5 \.nM]vxhقN2vdl.s.-)'s?y{l~6-vP0=; n{7lѽџj! 6Φ u!]􏬂bdxv^ {73r+ @?0p> =p/: 5Cp}<1b+ PcFu#?O+9~3< ~ux7 k}w=,wƍK \.P7B!d7=ؚy|#4 N>6? m19tyƆOEĨ0/!.Yq>XO\h`[Ej%\ϲ}_HȞ@Q@dq#5۪3c( 0H$ !E P$X pszñ/׹r}>7Tup_>yY{Y\x~LJ^jwdk܋<[>_K [̚O>g'z?y.b5dorŌij lbgq\P󆧈ρ-jh5zv|Dr,(GPh̩M^37|7ps1]:Y>J e{id)WsL{(<cQY1_4}5?3/uTg!{b-,d] 1}ϐ NPg6И?uy6o Q'{oᣱ^WepGxFa|1M8*ߝwj!FU΀1_;XuG kc&iMh}69m^SRo}}m'|'o/y|+| lήc͊;>d{ʺ #Ư{r]۲aKY5톏ݐ]p nA\{#L݁|j6;$8wd'k{drqL:lRlW>xI<|Yh͎ mE6̹|Js9V]gj8Q]Uߎ|tC }olř]S _Nc\Wam`Kq45$G@^ 9ƈMrGro8p \{Wg90 o ,׹\? slĵY\[7#Gܗ5+L(߯ĸ>sD ֮;9ڼۑƃbMwǸ[|SNL;H?5,l0^WEDV}#XW[*!A3387{]FW[B!F|>C胡-0H|Q'3E' )Ӭ`/}ZKrqb7w9}WsT#~~}y˙|A<'%cb'By]X|v Q~uS;yz\r yկ~ծ}.s;O:PŕߕolD|{w0xBwɼS?[mS뗷Yճ[U?p Yw;_j3Κu ᙃkc7=)m pLF:D _6.[rY<'SXȺl_\'l='oiџyArS6& wU;܎Sa{7\?)2W'6*ގ1/Bik.\nmVvʉ7d֎52f`lH;/g\U#v*'~/})2"Ȳw4&r6oL3Wu YF?GͿk?׮nz5#c@v2_,>ҼRlc4vx|3sg}ZsLGП}+~^ڏW_|XI=W,xfhbFY{b1b@ivZ1`ʍgRo8{է|'szGZr<;aBV|Kk[v=ޥ:"g7^bg}KkKwF_sh.P_wodu/Rq/}b`/r/dr?:眶>/g@ .=uWv|Wf{,.=6{:8߀|{r9Oq{8ǽ猆09\:ϰsZVrGq8d;'g*Lפ{=emhrAٺ`v_V=2_~_j5G +lC(~A{yNo=y~a~`<+C~iX5N`ܳ'E`=ڝ[e~R1ӓO6 ~q_i߹ලt9 WV؟-[`->/>]ݭ/'^Ğ:=4va~jrp%Z?jĞļ= g;;5!k7?"^SMkc؋Ÿ1glʇ#%|Ymxpq݃1F. |E|]gWpz۱W̝yPwh>|Aq.ڕ/s1w8 ԌW`۰|tpvlj'Z #}63c #Ȝ2~j͍}b1O񸆭D Y}#7>pa{$ϧQGm uq^b\.mrM=Lml{֥Z:XF϶m֙(޾W6.ߘyw5ájɪRV{?:k_^T赥e\q૟~BQ=7'F7.yAl n[V RA3M- :s'`s/F9bvߕy;{nYtˡ\l)Wb+۹H6pN»kGh8HwHl'Ugt&;a WwW^yeb0īX2J'W'Dq ExO0g0Vك,c.4Xt?77dsřg'4]}>_ysHb(>{vͽc?!g ȁ0a+(.\SXd }o#9jKbՐoݾ2U͹ys_X,XaX.aa٪u*7}o}K΄cu[_A3,}}>2lrb_.=8zx-%ם>>k酸 ͕EϋbzpvG/z+x^Kb.StН:F.q6|/Ottz}F<؋s>z{7 f _>|'D^yk.D\WLak68/8&ctWOo-cԎ/S,}7s-nĽ϶~'v6nlbyq{lz%P~ųtQid4qx9 ^ўx ؂)OI8s ϳڢ&zM+?ix [hi>tKY,?30sƸ8>&}j%Pb&WvE[p‚;G/ xA㿦#zy~2Gbeg)+}&8[k0뜗;E{gq;c}gT:] Q K)mƇ/X|pýw.\&1}Osƶ>h̝rEo̝g/aU{uȋ 3'WT-^x [}_c*B\Zѝ2"}4Oh{w}1w\S/q9tj{ybN NZ@~Qok:W#藹ю}0}o}(yjQgټ:㠖{'>Sxlq₇AS`ހ8x NA]zo8d~F ګj3ocF;Kcx,>`I 9-.sR9pw9;70_N%ʼn{v}%8+~pm=|A8k]_x\Q!W\ƻ:ٌlW!+Lx&<趫\Jz|1Ep!,7?ŭ1VvKY&GW?03Kl^l=dzxvx['X7X­]?|={^Ы1ĞݞXcq UgqWxQ5,X~k ?~G 4@[{vceZצ gq ;8 Vp%4=3ځ=gF ݰ,1M}AuzFN:=?gm+ڮ5u|YxW n]%ujw~^zvַj{֋ڷ5!6Vݵ*vq0m/r(\|Ag x_/1$-k\d CYA֖Z-cGNrAm}H8IiشuS\\fw8+69 ܆ pd10b0 ^>t_[{Ě|XRqpM9_\uꋯ@^|uY?Rz50.;90`kJŬ51F7P1}>߻|̓Q;ڦwq+'9^|Uf]_G;b g*ξ`.g.[}^uͻ'|U{@&3k-J!b߰hlA6n GA^ Qٸ̿=_\UցP9#?r;W\/>V/|+g}uv|k>{Z0:TOX}{&;<<[x*g}80v)>m;ɜwFp;ʓloq 䘘7xR/g.9:Zl!;@Iחo`p{+6pʥTSo1km8~!1zظhr㎎9S/J_LRZ>5[niYc7}Oό>x,/_Ĥ.a[Ç "&\](G1S9g{І軼= _؋bz*n_g N9dpyfOn9䝈)#1| ْJ0}/$q{n?sG˸{MΜnܦ1-(`,læ 6/`ۺmG_ĺOx焌7b!_@ 7Gln_<ӎQm~sҗ@H^8rpoM9<;^ks?67C9z?`\P38sz6h̠ ls!g\6vX[T̝Ao|uy;l{ײj _05Ns/AyW$˞%a./U]ފX{ \k mڻd0h|`M|F޳w/yXZGs>_/^H|~9?.֍5÷e#;w{;=TOvevQ7#RDնOݱb!U)gkMwǺ2(q|eFXĞ!.,gɿ,f#tXSOV1q̟7_gާt:|l'){xwt ֘V#7e;rW5ǛKƑLWb<q\#3 OḁO:] ; ]Bukc෤5Pr b iq8WPr#"~C)!J|~!On3#L M6>ȉ0N1ru }OgcZOzC>&̙f>mֻgמz^:q8<惶 j/b b#fRؚ:?/zbSRtt/~+lo+~"c %9>5N6DqbV]f7؂]߻k>ybM[x¶eÏHrn˗[pw0aՋٹ Gg3Ώ!/}\~v`lbt}[] 6>dr<ޯ:g/^ju^\CV;d rHʭ g˿0 ވxchy6m=3&N,Gny9iʮoݺ2,m W*j2,m頬ߋ׮-kq3g'M*ŋիa3fQ\[2וH"s:2L6,8~\+(~衲hŊJc{?ue /&#F-+nY`.y5*{gM^>6ܞO#Cq?=>kozi|,]`Am1jT߁ɨn?lc}[{ks/-e-+kKz2yeVʺuex~_e=eז,ڨ|T2;P=_L|?_z֖g/xzLYܪ2bƈ2zѥtKeʑS <{eߨ!UϮ*}#JО2e˘9cʺʚkʼ+9M;̳hxư|{GDx#eet@t{GQ~('pBwUknGuTr-u ]wy5^}edx~]co}h7޸<}Ǖ>C[>tPgk{}Bs0{h^{L/75|tg?;0]_quC|sEWWZ -KN~qUl-ӷ:kӬ߁<s͵Ý|<2a{ oъV =8<0lQYBFlu-}&}-] 崳*}ݕ5pv}}3C5Y#4z)_ow!]gn܇F0h'>kx~?/~:kH /t˒\s~6ݴ|C9lCϴ"sIYic\פdGίXztͿrs߮+Sӟ f7v2fFx[غ <3P69t.+K^RFn:,hyr2|A ][̻|^x2znźݒ..#f(wXRLSn Zv{nO%{nmOYIJ2e+זacQ*C5/r~;{%حǖ! rƲv}e &nRzGVL_;lTx@yKwT&1! c/U 7ZѸanu s5w߽' #Ɯ_{v 9_l.h`|JK~7W-?{]s] jc-}p뭷VL'+[k}XRא}c!R% O>:>l[\/pєg\o߾׮gٟcma>zybM^Yo{gfYBe|yFv6^ϣ;:˽7? ly=0+!nȴYy~ۏige,|2?㢧muWW} HY0\3; ȳiƳ0{`ޖFOW678w|o@?m9Nz*N/$CF=yߛن:_Lγ&}=^oDx&4.s}Gِ1eMϸ:ߗ1K?]81mAזV'l`.2ͳwЪBotwͽղ|7{ ?Ni\Gvc2uڸM kO^t&EgٯH3*_}i d}*g26ahn5gCBLفGoyhx{p: _9ze̱e]޾K2~rʲ_=]8~L1eD2:-]\ޞ޲+/& +GtlyMgT;nrW̹  ,fAg~auY#cf=U^Yf}Ytݢ2jQn*wjG!۰;1l1L}Bŷ-yn?cXRA~P)/+^p?i0SW^yes=+=doO/Ծviri?/f߽u^Ǧo_ve~_٣poa[xĵUѰ`C:W\Qa"v s.,h=~&?;N׿^T*6ٽq95M7T =- S}G~ҏ8|.<.`n_??V_ɂBr7ڢǚ]{} hnӼh`~ Ǟ.@>G?:O}SUG+UDos7OKm1iDյ֖y`́wB5P)M)ç/kv-&[~E-XkeysM_Wחqۏ߯__: 䞈@ {N(æ ++_Y}kG6L9`JWQ ,BI]+V>L;lZgn}fYdey_/W^G6'?ţM8߭1=[X|_Ŀak }͝Vi<6C>h}@?n(yn3n6j2ᑩ?WEwנ[?05tiD=׸k^Ẋlﳹd';GN7,|}k^SV5&ߓ6yQ:|?4f# SLo~2p\ƛ'Po ɀ[;87n=`ܞ5_ߡs3y C4O{|klϘWwa.}72?rͰ t3oxHxAiOph_:oʗ/-`}wDڰu|Ǟ*wo3|0KBU6 7zMlqh(i86.<<+cfؚ.|Iƹ)i!c5n6s?ɾ:;r|0nrzD#fվ&;Zg`c` x(ʯ2扙L>^n]G gc]w|g_{gO|=>s>3 TO{llO3gCEقߴQ.W!.2sN9lߑ6=MT_H'ie2lհّ͸5eѽq a3yݒueuGێ[|Ur׏/C -'*4l\|=/U9c/֘vz70jeor/*_^xDa{:vzrϰ Ϯ'+ 5wtNY_ц>"ǿ?D8jSt2f<-Xhl1Ycpxatæ+ca *g'}iϮW8:gkì#|1YF[_t6%]kO@_2YCW κ}m ڶGQzExh1mk}OO~tgCw{A` ںki6&Hddf_l3r*FElAJDv;%3Vlӛ<__G4cx({E<]2WCs|pߪCwU}yӷwZ- MCdz/dA%ݻ3􇴵S::=~J ρl>DښKg !3'˟6B'?3=k7b{͢3Xi='rϥ+b ;!Yo$|-{] qd !XvPڄl?D03;7/z͟ vַ&s?z.xs-y6 ?sیܓ6/#;šř/MH|"_^n_`Q\{E~&t#"m{B=Q.zEe/W,_SNȳ<~XY2iIe#*!_25.+\YzOj<it]'Ը1WWίێ-#(.W^FY+\Q>~ X|m:~hIJfXee-b] e. 74aw:2>sʹ[qG;a4M7ּ|E3G؀F}mMil@߷/]'.Цyt@koo.`̰OMj~fx'@epИ^ ]?tv.֍lhMv1B"{uC#8jWc\1f{󞺏i d<@B{}mo>v. lͯ/n}KduxiV 9gOO1yoߑ>O]vYn3D6:{ua;zz^ij=g[7';>=msHd#qf|ٿOۏƯO8+l{Yy {_Z#wZ ډE?>=k(nY ün;|~;?PZ9d>wfX;  awu3Ϡ7v|C3Io˿" }u㬱ov/&׽쐱ߚ>ŗ<{'R} 26sֶ ٽMno -_N}#~䏚,1ʵwV|Z_?Ɩ [L(ۿn^[)_v2j`V]~?[r?\PFMU~ˊWO- ;M~q}5#25nݪuU_;^,.#g>|eeVgO:`Rg+XGl2)lk)/R^mG,{+YYc, ֬XSuoZ6IyV:H -F X,> Ur^<^kei ܀[x?f=~ >sUXxqAs-56"s'sM;x>󄹰8w_`0gs7Gf&]2W| 0 m,phHF?}k6-Ưms/5_5ЉmD"By okL."sO?ҷ MIMa/wl5>ߓ wck&moٞ0yg79~McK!+;Y dΘ^ԧV|Ge淡bPʠ/~Y?7tvE:iƾsr 6;aG>=G8-Yq] M@w%,^!]>/}o"<91Frv@Z6Kg7ʘ4Hw[?V@yl{K^s}WY}C7 #F٪б_IK+cmts3٧h/Lvv?gn~x)e=EmZ5v[;GxT ٪sG̃?#B.>=ym<|6ʷO;Ih_;V\ .GyN ?ښT&c:k {(rϡn)N*[eN|? vCyٶ˵wZ. ^3CK"gc5#3ގ?wfbzg|y:u9=ls6eʌ)e^3č'/.K_ZuYפ:|hYp~^]c 0{f^|2l:zkO/G{i]6iXj/_?[=u7|]YhMYx7lF5n:vf]]tKeKKߨ0v`|֭^W=̜6iUx}mx?/fA<݋ӑ{wadjvt~ #]<0sZ6ۿX=<["c{S~C";oHB+t:A_g߸'+bH7tѳ"#0}v_e:a{'cqܿ.Fqޫ_@XڬNO pM#oL'n6Jڅ̑{x6ՄXb{:{;<?A.od_N&k-s@3)K<ҥ=2|r!b/~n\u1c;K )cC^~1 ~gzw6a/oK-D성]<{p هE~Vr@;8cl9vdloؾaV5qTY`Uyj\#&8 V2dҐjW__nXmc^ KWѪEϕYϪ-/gI z{=B\3Pg'kQ* \P cS爷VD~ћk׭-so[=ah'>Y4#ӑt~/ox\=Ћ~#:.[[tŧ[v`x|wqf{ W#d6iج .@X8ۛ>ik xM9"vm"qDa~Yoa.b\Gjւ=|F{kj#} !??T Zl=y }2Wld`@N2-Ђ=D_dK |oX|Ìsb_ |sx9X2>vZyGge;{X{3:dx9dgMk du_̞f>3Ug_f<^'Mb-Y'Gw5=7g/ݗ*21IyVG10#~YoK_tC1?Ͼ?g/M[F$'} oaxgh9YW_K! 6! OWG}S>}<|!{-.tnfۄۅ=/* k%O@rx:; j 7=19J٫|l$h<_IL|{l.5DnV :7{Wҷw䙛 ;TM 5Ո;?k_l}cndqO`hv r(Kf]^7gobF`.yll(j7M`_9*כkK;S6z \ 0lr&e)], 3 ޾ }w_Xwyڿ.Δۍq|neYוe+yz5my{yiY|2r5-aԴ` jX69ҲUY `+F M(CoYU?,tyYtjCiu._X\̲r |\<oo8bz)_tWw?{ͯIHmXn|֋ l .x;*zx{klK7Z ́?dxщa*1mlA_ֺZ\9kiV[Gty:!`bhml-ޚ,a Fd"cnM=BkunKƴq`25A^k1m{Ӻ CP|ȿFwci6}ZVfa\*Yj;0+@Ow_<*4߇u1ká :߬ߡ^_?P#cԟ\}`ʗ= #kw>̅f>KYwr+b0]bos2^I]|9(kݖmy`wi=/|X9 C_{F9v6K{2zh99{n= :c䮡]|?=0~rhFlW3 [q5sir<~qn5\#~we}e=1} ,:߀OW[ħ$6UYNjo <滍X]D5 ;{g$mV:}1CZѯj]sT:=9KS3cS ad绸9GL|wvx-A8f=le´ esʚ%k*/rٿ\V-\VNie+~Ϭ.S^5X=PV_U^+P]|Cr֖| A ׯ\_ ';}g{M頵.]7f_zz64cV,`jٗZajf?׼3ž;Bեׇ"o!,/M|٨\5i\ٴy}$t5ږ;_??j`0oqa3#A_CZM1(m#?{K(xdC:dԹw| B3Cwz.9{F>/f/cw1eN§eA:Iͮ=,m잹npԍ0mџd鏬D"cF>/o1ۗGt>/z.&,J ;>;;ړ:{H%x{ξSGւ,Fv jΫjA6CRj'fLPr^yE wi ;b֊p|w0"c4mz1Ou||-Pl^6!Ulr[<]c0'2@0.苾q4j5wWN|9j|;vrկ~+ESsF/orsӱú5GwR_ '6y/:|cV%mG t[k}5myU$>?V^l{|4`6Zp{h -n|Z-{ŵ~oa 7Y`b#j}?5:r_ސy<=#I#|bA#:lj˖۟~> Y~ۻ^>++pegu; oՉtfk/ffj;c"cvH=NMGNd \1㘗:\T 9`pv>ڌMX_r/EgGEφ5/tuĹ>g>!D_<}{;ܩtyq.讖xWG5OWGyF~+g`טõ.ޮW,::2W\xL>҆}jX<3W o=4ef !ovjM2yi;LwXnW,kq]< x2w¶33wu !/i u0vt!{ǵ5ך[h qЏ Dmr.ƶ]X߶ 'pSI_r RW6vۖ9)W} O|\3qLXwXόcf|֟:yՆ9@ߑFIT}>uSefU8Ow-/PFb*[ UO*K]Rޑe+jnv qZP+^Q{E0Z\k˝?̚9uCo 1;v{x!>IûvX?x'jSc7a, o5`m4[.q; _/blb $\ur@-akQt=R?1^ Z+ajqGD2 :Nn/A- t8w21v/Yh7cl5':k̉֗'1Ζb59yȞd vs+vVlGVh%Cv|tk[*>-y.2puR2~8yvW/t[ k;i;ozg#CdzAx*yOmFu{0^,omMv.NZzݙϕY+Wb7ZJ==;?A>|^'w8ohǮ_ =r Zf e>/7#t&ܑ>OϫSOu::baStސylcԵ;A]=6]7?OG5 51> w6dfy|S<9 we)sѯ!&m!u]2wdӍBq]~m52>zPXJy3r '){}۬.ƾpw5.v!E`߶f䛆tHd@Ga0Fґ t7 \[g<\Cng  Cv {9y櫏V VKl5wح xlxڵ mן'~ڹAhE?&S6 E'6krja >8 7ro˚uل\Kfe-noj>ל}X_.>=ӹ#-Wjؼ*{xXg`vSY+zWDέlV$˫nثV?Eﳳڥ T&ju܋c}(sgoxI_A]l2Ċ`v2:tUiy/Xv3Wx s{BuZ~8NiDqwb N[Gui_ݿ78͡ok2OƧ'~s_g`8.Y9]b6sB#1?!>}g/OֱI~th`<˻xfe:V_'UǒB]:gh ˳M@Fl$1)z !g>ԝ'\rfxwnnyFwqj\W"_DE/(axl zVTm<_8ٯ;s {gG[X"{3Wnc zߛuXa`)/6zXӭڪFwѳ5N=@Z\5 ZhkugSW/3NQ+ Pm/gCo_ [﫶Gnus"3Z}Vd]g>Sb/j5y* f;}Gs4V6 n|>xM]1vy .wskl-΂}ha-؞ m~F~gq9YѕA&[u͚KiΙask겵i~zߗgef7mFwU'Xvޚ; A<O,6ٰ x5{Vߐ&oplFh,!8.{U}Iˌ45w{F#g< VcK dɟFQ`BW+fCx|7|"eeY2i8{@88^<_\e]O? :_GU؊=~~9z A B SZk}׶UV-[Uw<?5?7<Z-״ZpJufmftDz Tzֆۼa r̀#֋%øO 8Ʈtos!hbU_7?x2 <%VqWؙmaI˗'l;xFv![vq}[v O`0'k2 {6}kzَd!tig+֗ `<=leơMEe+-gss]?P[оN?jXv޲vȧ r_L%:LߡyMȗdP6ȘؾKgݚkޛ> h:w3{Rx>7#꺋#5f:yZg}a5Q#O>*cŌ?5W,cwv\7\s.6z?s>-N k]";xQB]sN:/{ysMuw&BC:=usIGg7xGov<1rN@ o _؝7/[qx(mg}yr_b){Bڹ0Q yR Gg~Úm &(wxC_\#tx}?6/p_`xW?@Vcc32MWƗGwƻo~G;[G>mq#|?tZ:+]PݣonW/)A!w;@-OPk} -mIEݦKzj{|'-n_ͷd30|gpѼ;o̠ :ZckOcwr۹gLa-yda,֜M =tF'}P[ d ucZmhgN=Uj#Zt\yZbajHfFl7}3Y/~#:_iڑVw>7ȃN;5gj%׽ a'ЬSm_l{.cnքT]V6Xq_cp˺3s"A}u$#fonMiv(@}ELw?Tr;<'gw{ozEy\fT֌ 9EGԾcmubwGptq]>d'V{pKWg/Cmck)j@]r-^,8)`Ni*`mmWK}q7/]]1YT|C;Hi]Tڜ]fV.r Uz~Sɇl {@I-JGOA^rjp;gMN!ӱqiR?39ӺYa/|71g t#0b2fҘ2ceSvXԹOU^/o>g >ivd@xߝݧ>``~1_x2)55Й #8^=+-tЪg]k˓7?Y/_Ceubg׺2youx=vixAW}[Yaϵұ\uߑC`=F`4trs-\YY֝V|e/.6̡ e`]կȥ&ukz}0ޓeIFO?Qw=,P_2f9~.ËZ-᧚3:W 3{3Ee2p/dnrZ['\Og|lΜ#Xk^-T#OZ Al `˔miN+Y_&6@ !l#$ u八_9_{zT[!CjLgWxp +kی7޿Lmb3tm{n^ϓ|赮DhuQK{c5|0Û~8\nr 0ŧaZ/@~o8K4ݞ O؜7WO][[iOK_)nuF[<&>j`mo\w|#|֮6z;Ԝ69;m4E@S W #+5>2թEU]'^x|oZYľ7_VE`[N ]Fuxo 6^~Jysƽ[ZgW]'M:jJ5[*L>zb帽*ƚٯgL<{7VtoyMr$zG#;ySΜI3菽5>_@M"Mg3_w|ݶ8'0w3xSHQ|PCRo7y}'s7jEڹFu2Eɓ'V_?.σ7#O>/g tuqh3ѫZOȺUds9?ͯzsWK//_l?_~C̍Bۑp"=6f\_?oɸ۹܌ ,]@?r|r g5 ?\3X+'y^>w;wpfˌ3ʶoضlfޯE-iL+#f2UN>25gĠ!@)GNtz_WϬzV_z*Svy+KwTz+Mҹ03tXA_@ Z9 o~ ;_A>}_sKi^x0^KGKx뚟忓1Ķ?Oyt eo-u>ݮЁoW zpՅC'?u}և/?ה쭖@XΪI\~'[wy>`;geVňZKr`CG)NOW 75>U ?%x*vn578oh^8>쥣"mŇQk~EӺv/ {.ߡvU6~;3<;37]2<`}esktޮk8KaX@x۹&zBۛݷņ C[o/2j :ST(ڢY79㏆/r'a)$K]wHbZ揾b6жXSeG"0ZkZ:O.b#ОG5:6>̱`hE7|g`O&&4z8Fc16s&|kn= pUˎo#93!>@3E'vg@;uRG͞yC|]}Y6$G&smř#'Tqc w=u}sύe{k~NNjwVxvv^> yuve!Xaʹ4ٻ|f?qq睜(->:n|ldIxL,{cC7~ay}6䖳&_3|6}!]l&vrTz2z"#'y]rA|%ӿx:>|NrW,*\o')L8{߫98rbN %i\{T/p ^c| n[P0t߰!}/-6U 1 vEWs3sնuj0a=܀C9\UY\CrrL#G '}x XllV1Ks-3f`6pIO&'aro׳ș4E }m[ 0s0خI|qFĺgj n= lnựco/uз1hfN's:dG0mZ-kjAN;{oy~ܯ-א[|v-ȳ&> =dۙIfoj/|aOjj;? 5YvJJ{?X#[gsY+هߏ4'?Ύʟ94a<}y.y0y}YsV#Xwr m5qiȼ~u| |Iïb ]圁 ?wKj Z֋m_n ݮ3W:9˽P{qxG??Kl=uBτ_,N9r<Gw`3,?A}Iʠ}}k"9c].,]|̮`h]G@@t#I Գ|NFc<|6!wRrM8Zo׾ݩ?{ٞkO~ku5~vJWlC~xrO~8=N7#"^:7hhs2{J+![ Y>wyygˤ&Q[r/Y_]=~z!e]Ytw5#`n93u5VqӫjอU?ڛ~Z3h͐5*_:ƻ[-uExk57Κ\B2\-ZwZD7.:})مMi5__-8+gD|[gΩsν 5Nqg,Npw߅w>c;[y?;Sl}Np{ܬvٿz}\- 0ټեvp3kV(fA|/@PwyG9o#<?< ; w =ɌktWϠ3Ӂ/ qB>sqtm~V'4}!gֳ ;9yQ{ˇ%'}2n_Zu::UJU{q,®60Y{iG{]X7xsRM;67'w\94fTwƟ<+3w;;07lUdvuQSu&Ylj}rjnlldA) ^^U8z}ġ'ohzrN.5i2tFZ䒲gQ 3v4qOӵ|+1ǔެluVɛL.2bڈb2tj:|hy7Ϯ[ˊVێ P_HI{L*C'zy+k-a9b$p}k=Q[Ӈ>rYy>{Sx>;^'VO>5^~kxv߆? v [\@ygƻ)խƛ>>VL'#ƿ.ɦkܰZ̅~Hw0فYNSb[>m! }G|-364nze=ncZ`z4^Eswl F ɟ֢l|A>hD*h>6dyfϨ]!ㆵv5=i=yӯ^Pden:pb36ׇMG[S;CfQg8=mLM;u^΅a{wo w|VLobꝉCNj[xu ~]VibPw-/{MbiNaN{/,~Ὼ/~%5磿~Y{8\T{|}S&> =IAmK}Y>kX SVʛ{䴬󬱾y"'v}*jSo=,ځ(eslrǔ#bm^`}b]Oil^Yso{YӉ3TcU2'OM_uaCʸM{ru'd=qEov0;:X'4ab߿'{-3 w}{6\]Ӌo?x9a԰\gpm7/_ u/C!ZlKq~k5.z.nm.ikXH 3Z Bjh~|1gyfwM Nȋ=#G;~}35Njsx,qq\{(egmXFg>G%7WXKO_a. ]BwϏ0?Ϊbq}{ύgsw#mp׳'ȃGjȗ6yӥ nCG]?2´o㓏:!b"3b R_)y ?ϑK6b\ǢbcI0 'onh|A|1n7x/> ʡy΁zQCoPK ȍ/Ɯ g$I׈7Md|@>SdCJgwgU&<|G~okY5GV;=r:D *Wl8y̞1  e5̱e--#:K/xz{%ΜNwظ{t;ZOћG[rrD:].:s3ٝxX N3NNH˚0pԸqf䈣s?yʸ黹u΅o[a$yL"ƽߞGߊqMkx;RPfο=1+!xieLy~0Qc#&\`娏.񵱇k8=v^|+؝4s1l5 ,:A<2awA>$5^&3=OX=ytUvpq}[0b`e=FnaK!N7ch" gxCsV~_q7/{}5iRkV,P;=uю}\q/%`l.ς(9\#B\r3uV 8>}q\jӯv_o)[RG~TRS|.!Zr=kZSҽ;*>`g7m磳8f:oStCÖwVôE>]xNM vhQ s饾v H\qd{ :"c+$~\$sf ݀|rWrO13о>sdvS%bbXSc.ꚴ $ oI[N9VK'<)ā >w>CC ggd ۯH:o^1z֘V]X ?S'd=S_LߜĮYsY\r+M'\. Yiکgbxz葸Ylj:qv eI|6/K$SsG¨U}Zםxc}#Z:dmhO<ϝGr=a[qcLx+oTuL ejɒ8Zž{sak˵? \>%2kmw% $/ڸſ1^lF:L7 Mf\PÚ-/ھ-O_*?ޞ}c&AY7(Dͯ`/QWÑ#:?^95Lt9Ƴ?qmygqGdO\ ]ˁx2֎ &J<>d' ,{|OĞcT{WVym%y&IY`u.fy+02;E/ gGN-X!{q_x>o+[orʧNT3vL?e|sgc,0{dfS`1w:^!sOYn@w-^a*%C$XU`.׸̹=:~_{cp51Vu{-So.]؏8ȡ>.iͻ={Gl亮C@̙9ć}kvvz7/~bWR'? .;|q}ql_~ v֞#+ 9@~8[4Z;ٹeա~}1e?:㏏ʯOwE 1NƙM/ڕCb%3wm.qcx?]o ZڲXv={џC~f% ɝ}|jҜU3Y^'d d5\c% M~9o|Ffy7K|!|3'VD,yVEpWٱ_6E|ʷrlf6>`\)}8۪/_BSh ۿl믪j;Z,Y[y~-_^|&/ ϳ{~_}8R $dxS edw?d^!nQ,_v=af㏨q/YO0sf9_n5U\g<׷Z gHi/z/`C&1 ߳+wpr9W<;uV>=[C7Xӗc[a eDf8h2sJπeӖ ʻ+-3OY9S90޲B+-tq5Xw:Sw'?SLlwqp¹ r$ { 8373|4؋nH.8L]/vz-Jy͗7K/NA]1;;ˤX!V4?2(.SR7Vg_gK/Z7Kp,Ld,. 5 ΠZs&\|&?^g2_~rU=?$/ m ҸO^Fw\8kN8\]O_16c_Ere08E\%?m|cO߁K:|AYU{lL^GnaI9 w%{\}{s-2_L6sέ~ sm Y@3@1"pG6i1iWs.~45'y$/lL0\uasD>1^^?U2aOʥm$WL7G3r~ :%ӫ8k{}=tZI`kXYx^EqiO<:@^K(.Wbpϒ؝ W'Gx'g~݃ѿ3w:BNxCұ yoɯj/ ][y|>Έ1Oyp\\LrOW8 f<~>8T:n^l4j%r߈6S橶h__. C?5rd qha\O.-M7[]ƭf*u5^BgN˘XC"C%|7p53?aP/?fd]&?7]_ѧ /3=ui|>uDmȹv{eyueefoxeU&M\ ɧkO;۲53W& tK0|<{u+2\~^&Xs ?LoBOCҶe7[~']ڶZxK,=`CmerNv/Mbɹ!3W2;=g22R֛m<Lu.s= {xAgO#2N~WCQq_<#5Mۙ"Eg }66Ǣf=sG>_gsb`r:8s>~żs(uOsDtşvѱ\Ȧk#Ō=s'WWظskSl69裘|~3X_8Dݷyvr񻈥fq屖CT/^zXZ P+b鳗?~dUwc!+Oe+ށ]~{X ۃ@8whI!938Y;|9@)~}e0[n^r0΃k?0Ӽ87Ng'_vs:{" KzO>^ 4?UL^n!HĘ F@PӨyhը9)8IW86/I쑧bΊs<W>9]W dբxF{6[,&./_?jб wp丑eL2){Zw9DX* ;`X׋۴jS4 p GyhgϔѣFk\q uXaltvM/=y^m1'rcgu :t/ `&IE?C3`}֖I!I[c^0zq-61]N=sS}\o'>dG1gMn,5gpU2ս;B<1OSb`ϔ|{֓̈́]E_3.cٜg,{fac9{;c \'xW6!K?239jkj=3s}~I6klj꺬7ΐ50/$xt]=L<$!HKi "*u/TS -VJK +R݃{ d[V'yr朽|k|rHutvwֳv,D7ǬYC^~MEF>7Enj;rϞ:xaiw=_;3sgN?;宻 'Oynbz2}{?݈;7o.L͘ 2J;Ǻo.Tחv{?\k3gvynVۭ1aB7Եƒxnzva7|nF?M6sή/}1}JgW١\W\O˽o^ĉѣCǑ~Ͻ9㩧9G6 n>qaod]h9y,ϫ7_7%}.ݎd1q2iRYa;ɬ?3GsEGf74C>]Pz\vJOCǏNnqU zMosmLO;[oujFh@෉?}͘MBlXk~٣9ne6F~{j~}[oI?[3w.{ni^׻2e2# ˜:'ޞsWZ||'͙;!Xd?B݈JCE YGlF&t|)c[(#Hw~:c#tZ3s;y/y/Gθtn1YsbX]ߡit?яsc2 .[aBg!=SK}5>}z7n{nh茆u]Ms2wh>#4pz*y晧{1{[l2׷X^Ȟ[/ oyM onm-cz0.SN]w^ ?F}~ oYK.diC˄ʚy3yml-آC?p/_Z/ϒ{'5\mv]{{wwmg?Yhe\~-?O 3/נ~ښϼJ?߭ 0&}5ꪫ ݇91Nj.[4mt 'q7)tQ4_uUKzkiw+}(V=㘙g{_km $[?}>Zڳ uwQZky]>~uO{s7Gzo|:a5uYCf0saqY×n:fi˙>2abژz<^uP`W=scsiwh5[)|x?SsϯCB1OiEifyV%_U+d\!42s}*|4cQeYUNgW=2'v$̄1~e?_ϡ/ó253e93t?xCu{Zw=y{^K[,nYne ty&Ӟx6뿷5禭?̚Lֆ|w`w\^/>O@E3y<˅Df*Md_+R|~C.\}μy.炡<[F3U/w}2_Si:du=c*?3g fgsOes`m~'#Kog>:X/c$;W\!ω/Zս_0cZfaܻLw2YmnʘNksO?ۺoq}cm2/,N 3_1usrr;5m?u4sNf>\!jCY?<'O|̳wDo7nqĥ'vX7svKT;{~{᷻}\ޟ~]u#э262PduN9{w }/<N=˘3а޺{=ˡ4lx{\{vod+b7:z77pC7g67,x/e oy߆f1c G?k_Z&̀'z__^zi|ysҷ1&\x[d=idFx>F okQ3wWF/d?ݱ[懦d]wݵ:F7 a,X1Zߖ="i<O,495$[ج):kКPt3/?Y@cS{'Uz_> P_u#8FC3kyIg>wƟ ]&em*G=Lnd>|+\d OVnt73^+ωw'Cؼ[uMΠ{/2 m]kg?qndO;ysOe_7Z랬;q6%#Yqaܻ9h-ȍp\9/4|*{q7ߕ5܃M __6v[nn4Jhrky:FkenT}^l;o{a5)_,r}LՆksd=)ٽ%Wyn'ЫL!C<}mhylD&*r[hq2}*nЪyav6c1{X߿nìY2;CWr ycp-}=]9޾Dw~GpoV͘3{T{BWr/=:q\>cdg*w.ONl*&?%tL{y+&dgu2 2wv'gZ nroe^{?uƖF2*PxNN3#eهe73!Cts,kL7e)ݒ,MZnR7oxmwJ7|ݨGu wxo7 sֻ݉kؽFn};)<]'ceOx_^p0 c`72eJcckjG~_, Sa{`ўx@pXg}v_p ϧC\ȵ0=܄/^po,򉹚x7,GV1Vk}߭ 8Otvr %L?&r=dkocEגa|aMr?"{听gAXZhɦM/ 1v݉}V6|yE>AM˿3dOd=~252N$X<4K=UbgǠx~a^cӻy]&价U7d|!~=B6]P~_/2|dߌ\ߟ68<9"<튬k<+|KgLfl_&Xܒ~yLb7jlu7{| :[<<^|(tJqZ z+k:o@5|z֐r֗~}XZ~E;G;ts(~٧nٯã9f'ڱ ;#ܝ{3ϡ59-聼7L3t~K7do~[g٣UOr3y'm>3k`ηo6(B%2~ ߝuU|B_G铝nr>59U"C2êg_Kc^@C32}y>}W3> Tb O&o8[r% GwynwӞ֍D<l>O7tdiӻw9{ǺVYկ~Ux5gDt^Sw;GaKq?;7ֈPW{OlSڜP5>ƃK> gχMlq7GUbk}?@G'g _\_ ::Uśٗп̽d^A[B7'7wEVN?7O`bجсm>3^`,gٍ }.;nlݿ)>%ky?ZbCx'2ؒ҆{$y1KZ~5WO7cۍ<Im^r~[^f͘ͽ]˻ws"v?O]wٵo>6) &びj`<=G Esa  {eƫ]K;`8`a?O fTUwyRK,jv }m55d%౷ayy̡jucB:A 3?5hA0Nr{.ݵ sєmŸA{ڡ[;,Gc}(߱c2e@'/6:@FM.&ڹ 6]ow@oN֝LzA>޼^$ꏼb,'9aڡ5Gf֌^FSmFZ0/ceٙ3$1tAH߰U>;>klsTю<}8; І5 I(15+y>lG{+{)^C^l1gRgov76ϪbN^\8;VɛmEXzRQy%|U8T޲n[2߫NޓU[u_Z%'sCv[&rY۳\[J"kyh~]m'W7sNF1,Um2moI/?/Jg~ WʚX&GoV}bG=Qط`GG?=2<} 3m>tӬ3f&&0<㽵Q=l9o7-ܽ]ptHh$n[YqȨy="kǾy0s_<4̾nq~sۜ@C,9[|X3_O[}vC\>15!†uWyοb/vj31b?5Ҏ : |`bKl mdp2s){mkPet=qt7f1-r/0iI݋Mpj7W_7bz.}M7}3- .l4cW|v\&-enjwHh`^8g$ޛ/ݜȃ_o@##|l*~hN_} nqB6ѧkѸ+铌` bQ2ZuQE6a^GWG}myY{s0#xFn+.͸`(\xWM'/60t*;IY﮹n_Yؾ1Ewg>S6"}۾l}f0kK2ݜ wEW;,~ex9Y|O%C=k|6ū3gvAs2 >;"ΟzO3/{H}y 85k8M;\. ߸"{m=,џgb؍o Ì볾fUV[B˫ŻƢ],dc2;Y~SO=*x3G4Ϟw ٯ}c cy"Mȁy֭u,9ߙ6{f/G 7f̽ukTzWyx rk~Y5Op!_DB×G=!|]|z޻t٫4NZI`WY!*mx% G2i}>dޕdQ;q /sxu Mh2U1vD7a <-0lnz ]p%NDg?,lcg=rݑ?p-l dXð-+N6tz~~׸gf}:cv1{tJ2=%Av1f['vxl֊=0m,b!1-}ћO~L},~֜)iVgۚ5;kItѶ޼m}l7~ ݃"rY yCvP2.7K_:9?}=EC }5DG2>X|+_)d&ٟ,M}iϾџuیh/q6SKBCXϖH{*7iۙ7 h]\ݴȰ+e b~^S8'寈4π^GG: xaC_<^ȟ8y x /\K?;U,9lÙ X:<6rʧ/G;fߏ O.{kb"5k/L=r`ܿ%bۣ;Q >r*,i{xE+>F㋹2ϡG&-H([7!Oo9k\'. M~C32BG6ۓI\Κ^ggc WGtx;lxyiu7qXq2?ߓ^㫟%[>Yh@'M@iq` K<ҥI}c<{qc\5ڰu_'J3Ϝd곧3v߽[OW<t-8g -K?%o|l?O >^8mglq֮̾$;[ WrpG? tG;w/nDse60Mqd1̑^m~{cF_=ۜڰxE7:=AP#,ԏ{ YJ hy|mMuC_{|\Ͼ֢s;9C>裋\b/8G`m}o?ԷϬz읡yBq&ȧX/خQՖo#\7 }_*6߼[z[ Zm2EXrFxso1f[z~9?cŇ;U^(s(s<;wPx;?zoLg}"wmd/@J;g.;bؾ|_1\NyrvV@rJDz@+g mgD"gNJbСVYϒWͼOb=|]xey. x;yxqHO˸P}=ml,/pq}څK~ogߊh5!@7?Cok^:ScKI>ӏg# ]htSg:Y*^3}\{>~|}Th]r3Nr9O=kcs^llMu ^GGXA1ھk1 /%WK _-6C`7n6{xKn~Z~;ËZH?a1!;z8El~{1p9ǸfQw6 -Ѐ~dK.V{;́Xor/oфlpꩧg\b]k^Hp^Kfkv vՋ϶: ʷ +7~6{C7-fA'g5C_&.e^[2ʾ⛡X/MӼaռP @zjO|&l8~ Y~O~\rv\2K᳿>N ]䴭YqKk3!5{xWwsͯ.EdLqID$ +5o+>.nu*@4nOt{7O,ndȺbZ \z1J}wC8O׺?o P5r9kγ.m^-lV>q"o44Ce'Y[Zm -]w>ue0U˧ӷ^qNaXEc^9\&au<_ã3UG2依sEt_,'nq<ke-JY<&~MN_KoUɑ \'{PxAk Nd?{ !g@lWoNxO7cӿMozq}бCcDcv|Jޞ=ŗOis;m=6[3w$ܳ@ i%eo=m?zAx10*>wnu녾r8~8\V^.0EW~ 1b7= zրl,&c#E-(43CъoM\OhA&[<<Ob_grg_M8nSUW^mG*A^EGa^tWz r/^/?wlvt|f\-4<] [=\{l;9mωu->N'tRp!E-~gntttҘ-WOwqǢǶ\4N0~s#{E`5hjࠩo b.{Oo.nqwt=ܳw}gDYмZ܅Foa{@ U^m2}bJ!ޒ.T-p9k*s1e>-ł6b1ZgduqhM9l|tv 2uƒ2?< R?ah?Y+9R'“x=܆trfCt>=G.& >Yԝ3׌ZJ+^q?u\vM׈6>D8:g*]aUj嚝C7S`ƉDS*er\wa5/ϵ6|k-y>S-ƾ V>'卬c˘rlCoȕ5;EyJ׿\M{{jYO2WM곤̧'so[J_nUGH&#j:fL9d?u)LƿXh{d=з8?{=_ƻ5*Ϭ}$Cۗl>: _.l66%5[Z%$y;{iſ8? K&M'!o<77>a, ;@b}Z "v[:};.t7aHt顇vX]g4V8G'/y5pk]\̳߀y`mۜE^u23?Y]Cc3nG~C/wIo}٣gmcЎ,Dn#kXK"cك-wO3~GVSAQZ=r6I{E{տڠW}F͇>a "9Z0\EuswCoe͆f/;xiq 61俿ia,Cj Mlů^e ~L"뱼ќa1U 8hF+ ̏s%&~'kC_Wvv+c .B7xQ!M|S|_,r5sV#&t=:CfVn[S1g\9|y"QyyK'k>q _HxBWv.=5n^y_\#C!wr Umbr:mv˝1sΎs{uuMccofY'6,u#36{ҏ]i>nqNK_'SRtyIk[:ʔ%/c ]~|Gr0&b%Զd!k:Cÿ N 1A4gRSd\ڀCRywj*mI,1-fg F&X9{ʳ~E'GІ_JKo̽^;8Pf 'zv_gǡx<~ `;(ޫr/È lv|t18ž[^/2A_} NT{1p '6xKhq^ yA,r!XwkInZ~Zfp=A&#%C #6C6͇A>6KFo]=ڰ ^< !D1!5[(LG#cB٧ڐci.lGbqa~ohu\w?L>lH{>#菑Ouo5[YyZ 7'Lؚn~\aW\Q;?H缩sG/ r Z Y0VMb/S C'[eþ%{O=65Nlީt\'W.KfY7FhvVZw7P"A_7gay5\ھ"f]]@2@l/?d{B,rZy[@w@]#T}*bJ&< s6Ϭ25́,!=a",;+/W jkr9x|&{xĕζoq f@" l5D(aBy6:qb\CZ}2gܛ_l<|qBR[ VoѰgշu ?^gZTx{Z>xaLluxkd铬V4ݬ] _BCj6_s)@üC?Gkmw7c~`|(3~zt~'@_' ZưhFa>ZvI9c쳋o=Of_Ac/m[39BŅGw+"'~u6}%0OOVCC{:X/2B3E?)d2keOXIȃKFa[1_i:ۿ/oz!>{v85saxLENTs#džotaVjYk0`|ۧ/m0[uwn_ eVj>8|6{XjiA:"mg_P64UvQsPw_9@rK`d

Zjy5wv}ߍ_\/45~Scgyuǐ`uU5Gįl ͮ93+ʮ~sуj6M'<.m5Ųn-NM!7>b6ݑX>"🝯i|6~5`=NЫVo<QO'%Ziu8ܲНɄ۾*eNF/smk# /=Pj4[ka~ξ T  SU-ޯ;g;cFM}&8>[r}v.W0v߱4,v 9U%0ehyNY$qljuёI0^)|EvpZ9/Xej 6wV-~ P27rS;Ⱥ ϸ[Mvd'gaV!{ZA围h~[=I@1&;XLJ&|֎]d=NF֛tvgܮ)m+2{Có?/,¾3/5!m rx-Ͼϫ]y{ՇLMWӈz ]s{f96mїr^RvoCzZ< ﳡ)UozD]f1S+^/kE/`]Y*_zȳԦ^klyOU\ƬʻElYO1֬˿oOpXmqo~[yig ;#!:Eqd6|q܃܏kw>.Xs [|@fD/߉064sX&3œ5/alQ .(k޴]dY_OL_^&yd3j\)s,95.:i֡7^rr=WuarB5V9L=627QS;qg=+^8r?ly<<&ΏۘYDfvb?x 9zVԐgoAg1Tllj5s_кg?M}Lׅ~C+{z΀5 "c$,sa&9nbTS-S -[٘O''O*g.M3V=8wt7lĞo~Jx}l d!6'kAh9hjO~oruw1~Y-?`8E8k!pQ{cy{뿶7FZ|Vgy|+ƗkqybW^]rFռf~NqOO=w6fV])x;{F> sn<峷9*6?1\_'ֺ@OC~O6QE!g/\ѷuZ|q҇ȜHЕm8?lg܀z{i:g?j'x~SOB-կǷa3VC˕*Y]ӷuggEMsʄO Efs6MA[j 9v쳅ӬY~6j|gȏĹհܳ5B89x\XuEZd_Nr嘚ô|K,쏵ejlI\7@WbI' {-Ym]]Ggq󳗰ӹA-nƶ[s0'~@+Dy8[g-5lxJζB#Y`Рɸ|Ma8:i\1Ưoaqґͣ}OFR%sd6I0vN1guliA~mm?#s5t>zNnqtc1layd -jD{ !oNﬓ5j xX#Z&9oBw}Cg{|[9'.cw\8f\+ni ,}r8kh@ծrK(g2<\Nsט{:::#P-ow|r.ؿz.u+?kxQBn 187q&*]qϬ /m' ߪ qo7m6bg1 )۟|rP7KbGd}iFs+qqr`SM^|r[kge5.oïnpG:yy.H[g8rI_-JZڗSy$~ ,hNmb~Cd*3UaY#K|϶߾ew/s.3"9 L-z*;g@(߸XJ27*ٴnڳC/qwφ[=ԉ-_{GawrWQ|:k"Y\|? f8p9@[`ٜRe2/ΐl(GthMA sHͅ(.G 8//aeB{Lڀj`;WbKVCyγCи6[qsqn9wT7|ĸׂo4Y@t5xw^En^o0ǻSҖZYѕ xΈiM5`|;pڢ'%F́0Xn#֫p@1nss=i2i5~Gi8aXk$kUd=}E2y7yo=wK/F; 27r9t\ybk3\+NG^n|d すd{]3b}|nڂ04h$0ظ:h ͙ MeX;Ρ^1vk'nbߕ!Q:r2^>Uv-'j#2~z*v@FG=h-2_~u3nv[jJF4![B,{uşZ`݇gmVowRAWx&6̐!s[ $џܟ(w7fU99?s7Dva(y6|uM;! gS>Cys>&J_9[D~?fli|歼O[ƒ[mS_+Шc\:r55𸴇~%l%cVfRv-{V1xϿ T_/3&b8W9r>I+O9b4a.Ga4;d{sdQqQ-;]c'xL/cyoyݥA5UZ&<ua90Ob 4&{ZoGgX2sgLҨ.+Q~=ƨ3W N,8oeן_HsSVBg7r)!?!FE=1u@ I"eﱕ 't}a^zaIo>#0/ #iّr V lbp'Ns`-6MNG/[~e;6'׿.뮛'5;>C[fo}ڙLF'ӏqhzZ󲟝sGp)ggg̫~wv =+$]!tYǵ31%j)${ylJy}qU;wg/]բ/v*!;_Kkճ>tRg#| y~%&>'#7!^TL*,;oŮ@;{MYg^;йf" yu P@q.J}=u4H.[3Ϟxnr|ST9!8~7~~:2NMnZnnCP>SszD֬u ӑLޕ5]]OAgh٫]=V֚\c />`޵-6F `8ðplxI+w]aM-ά\|F?~v<]|"zx?:~2vdBv_3YbD}|[-ͮ%pǼỗ1S5o~s7|ڤ+CkZm2VtXݼ|sdr;&/]X]Gsntwtcivkohho2fa/X&wyhVk.ĞY >hv0v'd6ag[.Lu!U݀YUl E:zϯe<C/?n~tA槎lu3jA15>սu+_/%Ovv`KhsW~?܀fYQ`D Q'm.iN51j 3TjOt18@|0c;4:!gtOe/Zc,1dXGL?:E$v /uzY%v2XH0 L%$]o86r# cb^k>~^\%E2ynQSOMFӜi4|f<8ٲWo,'#SWcضFfϩήp^kT5LVc_N3@^tq1!Rx@b檷6`oi{r5~E&{=ϬEl_F<,j9خ[nY˽]3˧^O.!3eVrƥ]cw*K rqb. -=Ù2qXJWf7C0.exH,d M!c z4\T+Jo9iL>뛼b|0&E{2֤_-6vEjSM!3o?3Dyт ՚Z,"[Wo/_Jأ"wV6>CwQۆ=4c4RG5׾>=bHO߾PLZ!kNkG\]iV㛴ųmƋ|92~\սNaܶhA`fgfg=a3| ~ _ԼMƸAagUY87f+ֳ|ONxb$ju~P9c!s|s2>2MA]"|]Ua*GcefRsുOg?~9㞫)]ڿ+=Уqo_ ,0VֳO1'w±ch C0v^2{?YԊ\CC7"S෱7;1:6Y-#=ߺ6_o,}˥p>ZR2mLhtBGdVɔ| r\?v9ߔ|Br>\3/~ثG;ĸ6˙i9^f Fo'v5wKߌ ânyN%V+{d/y.vlꎊ_<ik@lK|<ɁՇ^Qo{709/Vk#jh}꜇T+?) v-wZC?wN[rSkb+kKqU>w rdd6]9{lrI֭zjY';-XYLo~4Զ6|zﶬծּv<]sp6Lza'ߠ],:sU=Ϸ8m5~7g0t {` 5vnݕ}0g͚#_b7yWM1yV:nBeֵ4`ɰ7`Imv:q|=/0(Ưvg>'ЈZN8o^yK,Y*]>?8 d/otEg=s¥9.9nZsžwZa] mMp80F_mr&_ZjK6bW s,?] `;L21 l9_8Zy/W\4{@Mϒ>VVYƌ 5 o/{NVVJ!Λk1 x7{/v^Y,:cWyʅoxЋ|>l T{WẀ^9rܵ&/r=܆Ya5ҞG=䞰'Q{,ϒ\8Zk9;bɷ\YOE7|%=({I戴m7q~:ҰxW29H=K-G6Bf@2ٚMI  olG6#s`A1GUUgA-KN>oэ5[g}՚US 2|P~Z;o v|`#}$8h3 >L߃`m|Ko9&`~ysiMƤK 3$};6Lq-ѫ[L`3/kיY{|򰉌\lVa˩Kskohcn;g^hH׵Z}S_v&P;М'\72?>\sGI{^~ǖ@.8hFV[p;G??٢ihΡ2'E,3z -r~O7jw}1=;>8ǭ \vgZ?'$kuҿ+ֹ:c+d2P!ty.Z{U_&gܜyUGhDw䶍q*,6ttۇ2Ζ3dP #-tX5[C75BQ;:6fO`6Xgk|dĎycTN N=}oWWo}p~}m L=Q`kh C*+b#fh="׋r&G {G}[%re 75;вYgC-GMp)?3NթNS:2}q3w:]rN7ȟj(뇝C ~yՔbr~8O8RpmW/^WxM6v2#/=S)w^z <%a\Ű6"Ojz}`;8Co5x/38Va@nߠi^؝v] \΍ Зslr3vVgNJ*bzkgn9+9rjue 8."O:˥ ;6_]k*/Y|VSi;oqn>5<|FtyrV?P.魲cpM/[33sgoMlا?^m1~ZVQ7V߰3-$rߝOlrSzmuRw&NHA*r٘1Q5|_j?g/徽R{{?gkW+e3djoq13ÿ mU[+ [k![sw8]5ɦfV|W]{a_<5xjk7F|&s[w[Po7 K]S~}n5;^->-|zlAlzM/\o~ >ubOqts[9  ٣%Zm^XGԉo 7 8}2 >`|Ѫlw۰#1o#9hunw{!/ ߻o?7DKٖ ȍ#h̩7___^@kb;UYd>qgXy8qJSkrɞiTtT1\Viiuj<|z&ޔYg ;+/JM?.382?M}1ۥc+P%gW?<; U;Ze-DU#q>c:|Ojqnþ]v 1_ٷ>6LJ|LOQw³sMJf_xQ}|nV3βyk h~ g"O5vЈE)j zWq! ԚjO(9K,upyU ߽%|BKY E᳋ԩ]mzx/0mڋ'w R;k%|ook׺p3wk&<ĮPD?ځ&̹CMc7vvn5K_z?O o,L>u`?D<$uZ5[Dd"߮}h's+l3.d~R7{BGe{>j %'g{m1[C+.(Ǟ79{kxفsY ߶h cL0heXovy:[fয়zz'XwSS4p}wY^-|J[o/1 bVr&9hXwe_W`2/gk_]]7}RbT(g 2_.06!sv[;EJ_U|@ 3K0O}I'ԜW9YRDcl/rzX 'r>w1;cPl yr@ԯX'_5|onw3g ??sǂaY"n&Q-2a= G ?":훂Eީ+ Z΀0ar|vv.zvw1q0"꼵s`YCghh pދ+hyZ~vmVޑ ] `&Z[p&Px-dYpƞ֘9 h "`j5 nqrDߗ+AFb_`O*z/[W=Ekׯyzl9ڵlh†'7kxi: 82ڶs ̍ @V)~~COUV~'~[5kTLvx`?69_,=W#ň]UsPŜ5Oڳp9_m>~ݚYkƺrMkۊqpa79/cۊk {3Μ >sLUuVo7z!O󆌑pq^y4V䮌on9^5k`tVS1ѧyϬ%o,96k0u1woi_ 809A]lcYR[2Cd,ji-78f@^q2YXS\W{t;IO<=p]]xQ0|W+d yij5lCW$c!&|k DnrJ}]Y4Fl͜a;AٽƉ{!+Yagf3\H&^.=h;qo+ӂc$Ùsr,9.Ǝ+@b-O p]5}䍕~v͛/U,E;%i뉪?L=){ ?_mאa|֚,h^8moħzguȚU̮9bU-5+?uNx~˭ӽO|w:WFJ["R0`,>tP7xxY|1.]\1gw5_=i:d1j]eqs`+eL3a:>sޱK# η~QN7:W@|ᥓ;߆T:iC2ӯ7ozdm۶97m4~v}gOX~fq7>gz,;Ϝ/]ka: ߜ|4ݜK2گFr=aznx?N0NqYkE "cG?#/Nj[ݺȉ9Spkߛ>yjb?ur>(!~GFک%\i:XFWl6}[vUds<=xGS5oF^u؇_̵3_?֗{oD,Z/e`ƭ\ٞk8n)p;o˕v]6qۧGn6|g 3r \{CqIɲk7Q#HZ.}#/4_f_ƎX_w5ٚn~"cLM>T8ӵS.Z{ͯ; z6m`8;׹r3򿾘"2rgךxgnUgȝNp4:}t߳G'?|þ.8Sߘ._`SͻOqp|z/tg8;g?4S={|e0jCY_dMi oNӇrrr|+}!W}j/|si5:{r *nj▒۪9=kwAz&C~FҜIl}}DDoBWsxR_/gq9yYvg[ah[w׿4`76G:l}`7ojW9GctH zIj8 S|9i+`:CO mC-:"|tDr{:;;nzs8:֭ZKXN0W Z7?X/XƋ/x-y^Ϡȼs]icd߹89z.C6?w].,oOS3~ӛNΪ'NSP.P܉yc2䖯 WBW{]8 ;lKu}Ir.͞p` |#`6IK=կ3 l(?cN _ ,=Lw?Kflu}|`UbbطgkH<]mṼܭ"]_6xdk;s15W5o*Y;(oaȓ>Rx-IdWs%6\ɯ3x/*syOV.mx~7|ikΎi9vI/lR9s"N قyu\E{}L.N`dЙ7g*=6)ٿǴ$6z{3{/<k_=| `j;hh9 W0nt{W?'0?/^m+rrM `ᖶ }?F8t=Z/ټNT tx|wpg#99}z!յnUE6GË =cSrym.Y};I[$kC^'xMa0r1wWs̡r5+m5橹|G 3xŜ^{='d#ok$^Ց{Vx;_.9G\BiX>˦/N"7\V-#ư;)GG{]1ݵkTsa>߁90|98 o.tί5ju$ة tĻsqS{W>? \M#~ĉ̺%Faus7=xU#Tsߖ}#orٕ8w]J?ى-Mu~?Ko{'='r!ErgXC7\<c]4r8F}\j5Ϛ q/ \&'cbkA4-³?m<8߳Sa )Of5}cZo<\9M{ح9bDط?:O-9OY|B0PmyQ{|:5pB癜EcOtWسl ^ɜj=3v8G" W3t? GxoCz1}k[ iO)Õ7bD 8%|xgrȞ_͚ۅyq ~[L^/oCG}+21cP.ehZ:3]8Xn  n9Xag3d?~דAo `)ي͜L'B :;l\m_Wd+9Í7x:2|2S|ˇ<;Ox9 c5ˬ-9=g9g0插`#BE~ 5xl]յ>uBBݽ[B [ooZn ŭ$9gi~ZSƜk缣>o^N\nz~wÆu{+wN==cF¬Y Gw-t/nqcܳ~!i{Pmg~oon?EGvZhnpI3+?v3q.y2$ /msAݏ~z7th7^楯M{,D~+ om1n?z[%og=ugNygd\cɸ>9o6me#eXd{^7h;xpw/w͙fӻ{_{; Aio}ruѹghùw ;ךoo7^ͺmE&]Xxno&;svJwmt|괿Mr 1ǘ`XA=|ޘ1Ǘk;k^xr+ݑqݑx"@r6oÙǫ;/L{nrrvogcTZ}nݴju[s{t[oem|\'尿#N×][*W㙓''M ={8;gv#Ų}[e醺o-~lƾF<똱y6 Uigu웍 o4vݷ/[>c\jf6!?{Μ ״/bݤhv~f}l6vl7&z5s{`ʔnSyg/ 䒅f3ƣ=k!@sUxNߞ<#w.cB3Y~-{쇕3e|n]秼-ݞ{MA7eԩ߾[tݺc Yeƌǻӟ&Nܵ <{#-ΘT7wnԨei^xnE3yݰaCݼyuf^{'^~f| 4NKdo~fm6ɞx7Bo~|`k?ߞ㈌zwWd܃ Ɔ.N7:4^{o}!͕횗Ë}n\Zk-}a?x<[uUG y%<^K-Tnf n,m9Y3@1el|1tԧ}3{okz =,mVN^=Po+yKsƬ݉x7'hn>Z/a׆eOnnL/lL]6| ^ gyiu|?[o9GًݐKC*|C?v>8g,{γ^GGx0~x YAow=ˬY[6v/}ߕ1?=<;`_ - M}|WeOCk[]3nɚ~0wZi' dg^Y5NC*?DK;[3/3eCr퐺?ndDy쭿9o2icu}!@76a݅;_ Z,\|Ev|kIOmY#2{ޟea0,{֬z{1YGLq"zzy#{gqJ=vR#79l팇yE3_=/co!;3}饗z |5L/}a7DM7ݴ@dn=`v6dr/ǴG>B_s;#ۮOZp:>'XG2WBo [l6ھ;}zrA9_b-^2^m̜k>ts~?Ntu3Um< ewX}_-on{#c54^d ,^mǡ]m@Ü'gݜ\z3e2_C*haNa 魺;!xŲ_mN/>(k~0j^C0 %W<ŴH;e}Cc|s~X'k9rdڤÁB[i6 _+BdeWUhpk1#[4c,y o!{e2Y˅ ǵ7}b.'yŜd4D4o]p|/}*(u^Sy=˦>7yry̿gz/ͭ=@HBO"x v^~4'5@{gF$|ap噰cCOMO?%o1|C5n&܂5[d_祝?e|kU*ڜtIz(W0llyx k-oWrcY "}FO}K&ߙ+ݕW.86c>6Gxߊ#m\1OL3Cn`l1N_~S V=|H yoi|G]d:~앃_p{ևG?q:.6ڐ`Ԋcn١m.|Ϭ`2ӺVCuCsg<[f0ٷYwggȑR'?y] B#c9lɾoR.y@)3b*F޻2#:>nπMBWk辩ikzih\x1;!gUlPo)U'yalOA>5e?(Xyܛ}|_- ,Qe*{/{fٟ T ?Y|7nz6+ݶ}#tV4'G\lyGG4ra˲!ѐ6%tqC# |Ōυ;5}v᭻fM3:>Ahr@ ; , Q Xp_G3AU_6/E:;wp@ίr1u}9\3 6h]rhUˆ^燭L;{H/^~sz)3W[[G'2uMd#u3Vq~~㋸=Zm5~ӵ35lrkݵë=n2AU>*ɍګ䍲.ko}Цnmɍg/9Zm%-'^|x@uBO QOJe^g?:m= .g{+f^b`G;a4k ALo}N#,ûtkτ|5^i] M?4^ =ܷa栟\/{+s?=ݟ KLd?wdrqxwd? ݺٌ'8}pn | ܯ믿 ;"Q}{//X*:#-|s=}lJjr onlE K`>b /= =ۉ*bfo0nѣayӬ1ڀ!/=~#'Y+ld6Q5G_ƺ{=}LΘ}z~N&_)cڽv_z~4\㫯~{}$Ĉػ|Y` ~fR쬽5&__wZ}ON+tg׾Uw/~zhO ٣=$Zm 2|V|?8M d a[<廮}J|Ƚ _'N$>@?݆RYpb/:,Zv Bd+|//BC9Cr=_!͆voʽ~7]SNrulB=! Xڽ:v6qx#|OGg9rmB+b3Xr댴g30.iȵ0v0/TVگګ#=NZvBw;W*?}pkٗoe{?{>k Y䂾X7XvIFisbVphΜUwodcr%1'ãE޽}%F`W??8c9眂 2x[\ɢ7Ë#HOu>0wpt˯] 9k N  |/}SHyyj@_l-l` }vYwOց}.?͉b.On{}NZr9ٜkZN&d9# =zyM~~A7۳Ho}32|ުTonx!']9n*rV{nѓC$9jz !Y.r*f d\hk^X? .i煪/zғ̞>b`̟m3 d~“Yldz5oCx]yϏֆ-+v7xGF_tt[f}Ϟ//=9 ȫ?Y&c&l^XEv^ 6*y' W=:^HxOY_ emO`n^x+0|AQdN9c%*Gk/7ety jn0ic쉝}Tr}mo9Bnu5V?>:7w_ww֏7n[v^-Kh70B2>r󹙫Gv5O37d y&?7[}⬯{śoߜZv#k|İj_;cvW]#otv_koW & Asnq-y`;Wo|SN)rQً{Dn]"ctQ1w|c+.zܛ]pw\Nm=2_AOykrכ l|0L)_wx7i-w04aKl{*K>ӻBo$ׅ%~aȘdap?؜=/o A/ 2#ekEVdgq&䙏q5*˘[lo :j)y/$Ax~˻h6DY=`X}lVOݱ.//r*16xo+oyrبpϋNׄ^Gpσ0nOW5,pNڄm0I냙>zz~_Ň}e:Ɗ]}1Xhfn;/A46f ga'?5~&4 l.l*ܭ%;F[Β*9Hx]PsuEYᷟlד~[]e&怜Q|uT/3?s Y.-n+gωy"BL ,R8rc:5{bLܭEvYwgzP=!xGӇf^zrJ뙹]`nX|bg&ך-dNy"{Nl>&̮9:'7 }; $/9b>q sj39fN@S5db;VyfWW|=AVd[7흲~~Gk ?LBv2 G-o^|vܹooA ˗yCe7̟?-~>1?&<2ڪ<==ѡo ?_ ?t ld-֌-p?Ϣ6{9&]a _v` wSZ!Im}M6l-FxwAɓQĵ882~_c}Go2y'#u6o>W6?t9=o q ƥ|槁k-nvErJEӶ|˚pYkDZ0tj;7x*r Qf%ǰsZ.E_,?9%勯u3\} m"th5(w&(]m / V|\ O޾\Gkt#t?ȳ3ƈ@+o}9Wۯdo?Cr\a845o>=儠Z#nǬ?=aoq-_J! iVk\tf}IzjYSs;d9䒫E39j<gy^{h2qrvyQ,?yRY 9[m¶mthu(yAɾN+=HitOgr]cy7x}',pTa| 2OE/:jmKUǿe'/'"sWwBg1a| x@]k%<'{E:-|{z'琏pRh71j4z䦵^圚f"dq⤂w{~FsXs3"ϼ}] Ort=l==˶UGm1cV ]=mF.8'Tp񍢿/f0}nx3[uK۳gX/0zr Pj f֬« nvD^y5}==:峹若xiZ̅5tG(#tBpf "An`/g~ܰ,զ&ӅL@'6F~qNJn ;3Z8h,-|#q(\@m@v2|ds`hr٤͙<9ļ"|a#h_#6nyh]m -b841טq㗗H&3>kЭWdlYտM&ۋ'ͱgO溏V})==Oeϑwe60J\_:soPc$oFqty6emzQ5}*6?{)w׾Z ^v=Zg5uzm;RR~♮gDl=/F\"4۰:"mw#޲_Ex|4̐Z&v7c~N_:XG@Wcl4-OiR}cr^'9Lr yL--ܲmB۾?,>!:Ъ%. &|(rP̟?=zC &Tftp8vЮ/g: ThEw-!C? ~%mSr lgL,2dL961;\@M怙&űPCx@ڇ-ׁ}6䂦'_ _d0&S2[lhB''Giذݸt|b ۘ|&/Zy6#O~"˸@C#~aGӃ%wfnu{K_}wBhnƸ_x'آz&Ϸ{zsmS6~lis)Z>޼b1+ӡa'7"%v5gg@~s}6c7?oߏ9,XSb =ݜv䤏X0/8~S˸N/ {{fuJ]W2T/_b}&#ܻ>?>^. -C\y/լ!ja߳gtE*Y˿X=?1ůκ`lOwt;EV~Dֽc#q~9bWu6ə΋y[? oʜF?Z|0}XjBg[5jJA\5?<}2Yt]z1,_j3pD~?]匱D/ߓa`;V6xv>駰 o7Ncۙl | (tfxNo|M:ҿ$z=Z&WUӱOf`7wr9E;G}t_jsgl l?y!֍OЪdC1E"'=~hz'5u`'qj,_ ,5γONn|{M#zq+IKWeȕj[NGwhƧ-w0?_1bbK<z?LA20wF m)n6ʸ7ti\n['Y-~‡A>ӆغke85mN:T[X9#or~/Кyp᫯en Gu7Z.<3=jٚ;X&ݐ\v@ڌAnz܁V= ;e=/o/9bg}7uaxD1Ç/^ck}GǟW~'V4l曆&j>mYT`-~sL\r!r\wB{u|ggt5ϳӪvkG#W8U{Sj΀5BGj\WgWJ-  ]YsA"3wrqrp陟5.F;Aydu>`Gɯ0f{T>~[y{tEd+[+73~3l&?aÛ_mgtSğrjw+/w Y,鷻57l8=%V}v^7|oW;C.6Q_`q0@}1<:GhTu+2ܹ}j?Ly{oJi(rʢXy޼ˣwk{n˫/^0lؘ\'goez48ܢ!a pƋ]k9浳k`u dhw Ն4a8z{󿱘%'n(?ݺ[qR/Zׅ^Xb[D уMg_'[c=Cނhe-\mdf[h^:6xoguVwBtGs=r[=m|qMz mgkTm" z} .r6I?k_啕 TU}GC5>Y]{{_wlke]د-><2t@ƉO6ses\Վ?d|W.yl*O]mt`r7{wjtzDhlx%cfxƮK/W{⭚.Od^O,׻.{JhlbfA9ҭ])ZɯW>HŹk *œ.oP@.С/go@,1tiGBd/`m['2;>>2o G9Tl;yW|&9Gn:j,Pk{UkΙ<6:\6Uj_:C^"72]6t`O%3xsfx~ +/W||2YF>jsu?^H䅬XoX1 ,R[s_51+>قM5i%pŋ\CD7~3u\7y'5}5}Ϸ Vɡk1a03̂idr97mvqx;oa4|bC`[ϐөռ'KhgOktf0~yhmjoh05^9/LE;߇̿mNwɶO/~Qa.y}B2wa!o~Az2zYs򗿔ڙEO6vnaM^9-1ܖg3<ફBou]vz VV ԙUmVɦD}䔞jy}jf/9Oi-&sПs%*_Z۴Kn?_&ǀU1KT:x%$>>rZ}guh@Ssdh8,} 3blUd?ɭa:ʞ!7I{~(>ښ)&sTUάȏ#O{_eM WYbKȏ5Kmq=l~ Co(6*^'^, YL~\SxzK欞@;U4tQooY?2Hٛ#Y<9Gq*`XC=;yz3潈`;3D6Zy0+c^yjWk aQ&y7r;@ow}6[ws7ho/vyU<ܸ*=bjxjGX/:c>11uꃥ>PyFA_'-ڇN;w^5YA8}E0aMؼzs?j?,-Xqu{/-;H/IE䰥pgvlXN a!,) Y=XI& h> >@8 [:C/+ Gc^p]Λ1[U jm5R}ilm̓L`͟Eo7F4&>*Nh^,&N^dtW- -44vL )ւ<}wVAg;{碌@-N*o~ݞ{v{;o֬25ZmH糳~W[Vg] s{_?ڲhsk:D1:Úٽ.ߩ3j#SrDlUߘ5zMwt亽"?Cn{>O!'홞&@Ss?t ޜ!]Xw.a~_Vb̕/gs#?u9d-Glj/$QZ:<{LbgF7H!3Խ6vx:~j5EOx[-FbCe@Sr#oȒ;u🽹_?# btQmZdG>5jɩ{I:6k{€X!_ 1ss5:%uƙiMNuŖS\l&7_`"~ ˑGll'l dtg },[yuz8:l\cBe#?O 'hTt%^< ƶ`/_SőX/䉶4͈SAU%}SOwuVk/cWm?Tkl\q~ڴ'v}쨧jv˻i`ݛ-_nPWcpFQK8\by̝;Nu ;C'W<θkc9[?[oû7T[0s ]ϯ3h,@wٵ[^<7FOcMXXlMdfWr?#?yMN sh :>yx{szf]<xGC/sl6p:2ܸKnMsc!@v|`Lm x1tbK!7#X`>?яb>,?&@ahկJ{dzɤI99ϒUX-lݲv |JOqFw3ƟGNYC|~ۛq)yLu,lYs:䪞^UwPm_kg4o}bL0f.xGùf}~=c>:13sqNCs[,c"O #;g3'7"Vѥ9n9ͣ޷zX?z>Tc׌5&}S2 }p(mGj ׫^^r|_ gU*&ȡ[,7«?4_ y,~~@_#GOϢW-T${4Wg[e>?aFޙ5_LU)K {+\Lf)̒m0nXo9d{nwifnRXTv}xqy4-v15l.M*[O56̧.aFH~2U{VKogLdy^|چW0Dq6;QN;S90s%WȾΛ)ky95&:sU{lj$3WQk2Wrz\y#sV6ԜY.Pc});@21%X31ٜ̫Ad1dtv 2BuCF{qnku{^8Kh llAn /d/X&LDs ajhf\b&ܸ θHS ^h?+ds+Jdd4dp9Y57W<Ό,p}d x3EǦ"9p r]*#Il}*IZ[+H/VUW9:trvMED;6'2>5v^??Rl>8E|ΞSd;SP ,qu_w>V%w0wbd^ye7.q{w:FЄ*fͣoZR:o(ӫهpb/nm嫑_` l,߅ظj`J qۉDfU~*g2;Ϻ;sP &d52v^53m\pN̈́IݑyV vNfj/Ԋ VB/Wv = _"l  |*s'!h WBڙ/u>ߨS38o_mFvEC˹De3>qKgC>v8;|ҹVjm<ܿ>ϽG$Y}VȾWODν Hj,R'c=/A3Oc-15fњl_>c=]t썙gu ҫ' n޹X {1LtW^$xDwYl}ufaE)rDW"P7;:w! {J̀>3.Q|>蛧FV0փJ](;w|-Ga=A'#߻}GydVU?>Gv"{)dK169~n㋣d糰l Ihg7̼%Sn /\jڽ5\ѹl)uLsYO{k!4%Ɖ}1'>O'$\/OsvZl@9-9+F{ט.A->@ !^X!o=}exSVPajQ27{r`8wtە2rJ!uJ?KnwO_ :6S#[}ꢋوĪsluTxz 9W ğ̞gPX~;9 Yk/AoS<4 kepLx޹ w-:l9u 7,\k{9:Gv:gQ|s튝m|?8=lh: M>ZKR%g|9{n|vql~>T0WvrqSxֳ OPs7̚l~'F<ץNƇweټelyl_qF ցv[;sT.[{=/5pcOO9r\n\#59WNQ1bE}8.ݫgLbd;X3wb]30ƋgXnQXj][|>bBxwÇ+4;Rq^//`֬K~Kn/|qx>x㦢ӳߏT>/Tgx :`#B mk*vٳ_/9'nY*]l o}[d3Ҷusptҭ[Dj5P{hĝBaԪ$->=Mo)s-y:,W >I{8>]c}GMj,ZsAb=آػXN-\kYG9;%_ F"gywda'f'`h> u(3CwvZ=MF ;wC7}9 Yix8z Ə~1y}iߛmA\Do0Ǒv}=/pEEo PXz_Rmg-kVmir˥]vg1wݿ'˷|W|ZY#ĭHG]~2j%љ(lߌr:XoA]Ȗt/wTVr;*2o#ﭒ]tweϫy!ps^ŧd`8u֨B[=bϕįt|>yEtdkM__8R=Ӊl;C|x6rBg:9Aa7r#Q1UPN\zu#j\odz_|KyG+^LӶD,FL<{ƙ0D?j׏ib `|9StF/ؠH kNKd9Mb>glxP 5wP25Ⱦj@?.ת L?ݙWR휯G#N++vwR}0{A=Αz/QKvvמGka5cmUccZ;os:6r1ɐ2c,?`z{FT\fb4YT">ޡ>C~u?1A Mdʅ^dVo淀d,~j -Kϻ-ƫv KRJ;d|@g[o6!;MhKytUKfv~ osnČS>v/_{f>69ړZ~qCߝwwWe/?]_Yk~ZC?^~رu1]sz3g7 Vx<P?tlt[ >_t6Nv2],WG9wL)2;;@y#QhC֌-OGM#0qxI6Kwڪ[؅'25%ODM)u  kġ/P;?6sOW9\~~!-UϬQSlt|Qf[e=⼡jpCv" m|j=V[vS X s?rRs:?t}}ҵaS;khu&7{ @fb?!r+a7}y'M;rp`ΜWlsJ>5njY-2B+>X ,j9 /nn>|RCg֟k'Lؠ`}/8Kh)Sn+5'p^SoAn=.8/ 6g_~3r%FAm3_>Y8"眃#]7,Ly?>``]$z1|[pa,LݽM'yn\jJn5}i]t>v?VMPZkpE]N&j92c1?oc5VGC>m-d qW{#yXC9Adkf苼Di1hEހ n CWm\|w<~Q95v#?o3B~swSs j Ϊg˙@Ɨ{o}g t}Yl|_N_^6k{Bx!r+l? s^sm~cy՞t\˃Tf|E:<șxXٸIudlku*cXQ~zVheo(OAΟ_ٞn>[G#8h{}/\⬻Ek]sr/\.gũ$j3ip9>lGE_8t/k]鼐atLsvu6[ܨf#<`ߡtVЊ,!v;_ME S˞νK΀m_?9s@U=g౿^ki g׷٦z}p_7CRx5|it^{ze؞Έ@[55uш 8`o3H&KxިrqUV{*[sXbnݎ ~O(==}->%ooxų?m9a^RrM{`}>3e//_A~}3TcH_ZBF-Yj:C_͚ 3, 5-C+>k'v/gyN3E!zȑKX9cy2Fqt=wgd{_//4=MӍ;9|5θmrrٯc)0_ /NorćfOqnlch5ỗ^;Z~ >RNCx6j[DahlD]ЫRkdK /*97N gWVoA}<;:G,yVZמx+xVh`爕܋cWk13C/<~_usCTsSw4K/=_%w-g?|z G͛fg9GMwOwsɌӴhaigfm?30~ vטru/Mo4&AXNm2Gjl1읛#c| |ê?g(ׄ1w9S*#^ҚåOmK=Z__'J<emCin-曭]9vKw y#|:Wm ||b뗬括v9_}%%EWd|cTVPnjGlf K:X/31>gKx^^xRumyk풷XF}݈K99z==͋*?(߉><7W˼VlDKȗ\1q64zمw&>|n%%И3rvgv`Ӧv Oplp5xxH/ΎϨ1  Z &/8o%?T} zJ!{M_N%ۀs [7s5{~%~qEwwx0xn-NirVb^(YjxZl SryRK`ʔW%?sd:3 uN~V/ivf|v] @ t= ,m~ 8]Έ0ϿXKU?CM'Ck.9kH&+6O[V/d$K!|0O~C6a?p-yS$A~=~D s"cǹGY[pt~+9L״-yy~o{#/vܱN;u滳#{@n7?f˹ /yyˆTuv7?9vfb{zs 1ki_QM߬6|y*Iz_sђv< 8\3O$ NvU]EfoWQm2>x|6[{q㦡ٷj|8?λlz꧖o@:+s=j\X%s1 vߘn,gdoɋiʇ(䱮_߹'}ie"WóU2_9V!hKFZ+}ŋ[\Ó;7Bh7!u eA00@%RY)XR1 GEZZljԌRsi:FBj*(Q25@qٿ=~6kZwy}}{}t܃ݫBp tysTYkFG8AaCy{ΈGd̏_uȮYcTYtq %6Ǧ/l9)0zL18+nϿ v)TA^#p繁olv8ޒݨ6l|rg7sy3'Ԝ)"s/o~x7AvL~|o~ҥ͏>6%Ya]vjV6JBxGv{ Q󑏌@o=qN煽ZZ7ki.im1ށ7c~+ך}<1iJ|::xU*[~EOS]>%:fvkfgؓgZ¯} x>%G/s!~ZaZKǸaܲ{q_: jڕydǕ h9Gk кesl={xA;x޻&ФtUvfAWL'a]Ny|>5L'&{[^?P,ŞV{gx8qQJ͡G{'̓3]a{͋;kc0=emE_c]^:qbs'>}}=歳ϳ rNhV< 熸:5&T/-㞚O5*|||4sg1l|9aϝr-@%֗(^ږq7I ݚFف]nӉ4׋f䌝(?0?V]=|Z&?baӷcGkݶ_۶⾭ÿl3g?zqEϐBxY|~t̙wܹƃYǠߑ|?ۄyX~I+cny6~>~SW*Ƽ2αh b`_.cmOj |?{qwj|kÃ{.8C@R<ߞ ˫"=>||F+Nߍ4A lWg袋8dk(~ȐQ_vĢGؿ<WWo[_4"ش鹬`tu7˖}W#ߩaalܸ"jp ݖ{wNarGP b<ȑq#{Yyq-z1b%%Xr|J9pewvy9SҗCNq:~^͝cc\lWZj_|xcN?Y~~A=r=yߎc /Zǣ%te|la/}$/-`/8c`aS':A?690)=ipwq K[Gy lS@R{`>q)'"n]4V>ث+|jxʪM]̺.*r܉ݖמP9ukR*Ę2v6?_ iba ;蘗9篊c^Xs xЯ90Z;sF]ufp/վȇܰՆCA^x!l/6aC{;?,wuCx*ioj|Dž'7s4b_~ygԩ͑z_ij'Vޒp>LeӅD9ptw)u3/*z7x7KsE/]|Dx]89̭:bOʕc79Z*'PKn~ǧŭgn؀>G=v<caq_9鬚ri'VkǴ]k 禾1 ̹]P8: KGCǔ$`ͫ?cUzhwΕ~uq}{S{>~]/۶U+ w׮}:MKxGѫy)0iIY'ɵ27^_y::z[PX:Brj[vĦcUuE`jj5s¾AcG'p\;5}ݢٕ}XO~`'3aԨؾq] z+G/<|g]8}Xk1'С_ xK\}'$mE_\ΰѪ~74=l}b|Y;2 ~g tV<8{I̩˥̟}\8bCw[bMkئS15uo:(! K?(cpi'mk3`x1WsMƘ#cN._מ5p[aS};l{`X҆Qw@:GJwZw k$vʑ z{obN1~#|ؿg\#vwO؇ u_K︌-/es( #KW~hMJMx@-رp~x_Ryv`{w's0nɁ4(^gXƎ‡|lkSkF*J <(cr-J⋿{jqP~z;oGyiރ0& 5|7S;. >cלvA*pp Ə+{6O3pC,~[MN`OT}D1n}mQu/br?~)ō8W]uU OoBc?K턹um͛m;y1ޠGk>w?nM*qggPc]&Yr䷵؀8'_[8qnT8im.;!J -!m[ yU.i^qпПP@Cl 1?ǬY]s ֗3֢p,v¿sQWoZƟ'c {75˯{쒳5i(ܶ?*=dTScYbɹN禔ɦ-!GO/M)g q;W=?7Am}MO 3cFsT+^)\ދjZ=,q+~..ZP1Q/zz1r8Uu7^%vg}WfoHޡg/Լ:9I5 |ᖂ{+x2gW]΄zYhx΍cL(r4}sƖ@+qz=%DڝŜ7[}/1=!1q);R)Fc3U; OR.K? gI :P8v铵'caU|kmwrl&L.oժZz8bsp`իgժ'Vp+:| o~r'oӦx߳mkg66pw`'=6oM)zzLB,񗿼y|ewyGwyo[zaIԪ]J^g|-mdll?F}$5ZqMY IaԨݲ: ؉q1; |wj^4h:~7\lmSql?zny͏@XU5Cz6^?c5᜸xvK5-*5>Nߓ WӘ88q#GDOx{1?t -Mc!)d#سl XoXDpѮ%7&zk3&ӧ7Q(}.C/\[9c~ԸUnl-XIhZ;(TgB\6)sNү1VA9O`xcKƝ?xh6f޶yCbb|=mZ;/8{vOӸ{]x pDm94.+w}gzj\[f/ѻQ'Q fOG{hkBki2gS>]4jijjh+}:rǺkV/b<=n WOg{ .y1m6kK/buWso^b} 8U/bCSC˜=٠>'8.%9kK|>#ɡ!v5|&u?Y'6|\3y?5{uPuߝ~-8?5,[Μ|wW?83362?tZs%s+9ѓ>ͻLX4}Rih;f^\vjj;Ϳ|Dvzu떆rLHE[g"-I߸"呼5.ⴋ:9q7mq ;`i`\c%.|Yb0g}f,|~̞B~aVeb& =r>;O@=8s|焟.NWmAZ@w >kO[w?h/}ogc7jϚ5rK.YUؿNJvLchKF`wxdU3 UzW`^FM&`1QSLL=*HJUޤwf3L[gwps}ww}q萻IW+-͹XU:*}}_Q~3}n6/~ٿsgθ+.)ɭ>yw_xQYRyC=S*t{׳n]uN7ezӺLkQԲ}{W7'fed13$dF.U&{9ϦE*]?շ1gr]Ì 2;5VS4[mkys>׽zuY s~?{ojZ{ٕvԶ[zV[v z u,]k27Ϳ>vҏ3eennщyz \O .*vkw \/ej{]s5^7GjOzژTPy~W꘥{W=|RE[jk =dz23]jլվzOԻȅQZw6oUE}V()q}M4]~']<{ۤIӛnr=.*U/g#Gܠz\WZ]Xk6x5Ҹ gzq+Sم;6u5/1?9]/lxJ+7Mu2ֽ|y>*YXڶ+Xou ]3!שj;VS9V.:KV9U_wE>ߺZe D}=֢V-5DSUa^iN4latιuS\_u^1uڛhVc\uU_<]`<~\k,UL4S2n~h j=Vy9Z_uզj?cF7VY9/f7nt鹮oG\^q%%ܹs\Wo+KW+.ޢgv:uu`MН]]yw^OIU9 .7KK{1=N\\vv+=SUTEzR&KNP{:ZuJKc湼.5/S9Ֆĉ%ljhr?U<UWθÇ gՇk?uly՗vTy=Tnk=Tu&.=ʊ4]Dzs?ΕXZjc۷o;p`+(XRۺbmqh/ر<˗=Ve<7w\ץKW_T2-[kLUg,mիwV{{|:udz[z\у3ZgwT O~'뮳vm\bQ= >+~_E٬Y3wWcn5k\\ΝEWG̙|Ikm~Gݞ={77|ϦM\hy$}+پi3m]wi ';&&Ek[rնF$o_ Wh=ٶ<p<|1yLټٽGzo:Y|* B=øҗNkڴqWښ</CDb.ʮT_f7j < H@xKk<=T]Uoy ~ߡB]ql}F=};;7vu?*j6Ѽ1ӎ7gs>:.; ~OFW`5k9Y_dw ThF߇toj&?v՘4һGUnM}~*Uٯ~oNSDcrC>-z!(^,`l,̫1(Qkwa (-=,;W8zv&n ۭk𱓰!9Y#/d7!0^!;FYYptʫ[ #G>]$ +kݍ7NT{.pϗd=|JJ-Opjr̼@g_l)̾KUhjƛ}JNh<={ ;tA&?zZuiY'kꚬfnwLm+UQFkRu!Rw# jvv6AK|]kVi>NFּhgWA=Ȇ n 'jpuDDܯVsYy';B4RkږeǛ'Ovm#N:wD -z1cYM${u U[h.^2&qDZjG#.dl]~_[m]Q ӕhIμI"!Vx,[1jsa!xQik X' L|n~T_'ݚ,p5\X0 9E2=Br|VG׻Dc[Sd-NTwր~Fm{Aǽ [M}}M{=åOƘhZZk"9n,5fkՎ+|ZoS/r3zdGO\w>z/j QT]?8ѝҸ]%VpUSjH>2վ4>}MFzc;^XY?x1ޝ>{EX`6s\m(R]^LjI<:%p[.ZL^H߬A^C7w]Iݹ2$[r%}<8ծW8cXYGJ׸u.RCi8XYyNXV LpppjX|~|UNM?:  vL_Z S[{$UC*Cbb f 7蹆*s|ۺu%~s*;yrhVC̰2Ξ=vb4Ԇ WTz.#3Iy1aݷzmmܹ䷔$m~j۠1&=+MN]@]nL?~o~yi\%&Q6^&σF&Ol#Af O)?ohO7xઽN/o6ѾFZe}- .‡>ȞCлwo~WV.u)|hʕ+xgxꦛb_I&}M<3:迉wO^t |ot5smԯ=J{KO^ 3 Wix ViO)~}h6"M):t=[O>ʿV;y٤Dzu&[hzj?v ut+5w ۀz=[m$xY쌾i\(e3`#:W3Ԯk}KCXU} p x_ o>cD ժ }H/9K?ԸߤL}^g۫46ijKZۏ` ,͘>/uPZd3s^F /9Jkh NvoS}0n'm}u}mvkz!:4UŻx9X9-c\9i}ջh j|<~^<5D_PU"^[KM(hѷ`WC?ӼQ?R\߿V[>4S}Ae"+U6i1a;Ƨ&i$$(R}X[{9kdHDF]\=!=z+%%Om1)RxtH {5}+lָPx,?n"ټ,lÃj^Jt@9dk jm-yTzvlTxZ7n4:4n8_h3Ew_;">Uڵk <ľ}mcƌ1;mZhmw_lzBa?~LzF7Y`6|bѨuh]¦AjM'}nx@ԃGb?VTٟTp+7˾CDVFeg?QcPz{;ۃ-<|k| aL @ ^1 ܗC3Lc5=ԘfjiY穭u&Z%,a8amu`|XCA|p*رguZ-?sou6ApH{mגS>[Dn-ĠG:zEfC^0Z]knh1F@?g$y58!۩4v`K=`UŮ~+Kc5!_cm;5zL %/1{.FFN6_kkC{}Cg1O[5_xNd;V4*A~3}Nm}>79|}=7SCko#G1ⁿ\/>JcY<@=>cQ/gMQϝU|ԚDƞ 6|9 (0/`<SJmEBܪP|`<Я=z~6n*u6h;rZQt\WXPaYP0Mt銊]ŒQ]M3,NM3NIA&ܠ#xp{tB_(}L5>OWM;zz>\贴FfS@܎BWzB+´Qj:%tEs]ϞBTOCLwAy@sr:~8%%ۅQMȸ@6ߒ|&}ةsij\Ie8qbdѦ8vl0rxzDۡ:vX$x _`(cG~ `7.t`0x>x78 f+0rWcx={OA`G}'N| W{=_ʰi;70>5 =>h̙3 xtG?=y?ڣ}[vT=-YdCaD{yoL۝; }^+9n zk '(\ft#N S5)BLrGz{t4FMF#|ђh.5 |  E cBD=Jp'6a0RYYBeKk[}Qk9h%L!'[5&iKV!W}Kh_DZeߨ>lbt={dd=S ١wi?ި=c'GǁDv_Ϥ߽a+'u뮹|DAegI2{߾}MOn>υL|| #OVlO֩͏Tk >;zy?NDwde2l |*j<CGw{eʔ)ݣ?>V|,tMw 6SO>g*ԖDw{$7߸ 0xС}籃r4oWd}=v>>{RC!!\mo?܁FNRyS֭s՟*COA{͛[v`'19J ~Bou ?` [xt*hUWnzDCŸyx/@o=U[O2ni/?޷-nX})~G栍0s#lWpGcoLӚyHaq _/E5xN{kLQ9' poǯ~._+B>|?L+~t6ΪW_~Q\ .9k7Vk+ neW-Ū3^ )կ5n`2u[wgW5&س[xo-!*wGԟyjKgzsE}nEWGBk5~ >-=s~ko>,qhk/ǿxY'/%(:80ؖKA#i_zo{V{ٓzS9ۿw)YkۄW/+6 6o;;p^aWRrKJvƍǚ{dH3ѣ_C OZo{P=yf5%ypGzf;OL2y]:4m;ikbhnlMr_|q/],6FA׆_"m1E~@އUږlr)xdғi7Lfԧ}feV7rr ֩=kܑ߫#4vG.#[W,7gϞmGt8}0>9Ӕ#+ϻI|Fw>4h58o< ( >_8q/i|@7< 6 #Fnǁ(׿+c7eg?]BgSWUH=[؆\8Sc/؂/7# I?#|%ϏA.}O2[Sk} IoaWm\w\、%ﲞb*b1~}U4AWgث6] rqO?֜ ތ};T0nѺj?o $vgB΂:6_|S83| ;߮SbAX}A͌_ƪGiNcyx-Zף0ޏ5:3l7#h|[QϿ,6#7HX2xkޟ+/ݯD?.EwظC\gMg|e}lxe[`zvO/|=~o_gSkwdjzjT68џ'X=skֻԮ5#DiiWC@`3̥^8\DKo c`$AB\.4CҞDžEl"ްpQݪ?eALɪ̷u0d(//MA'̌mp, u!vv=>8>:{yY=䑫)|GLXR?v|z'c@"0O}ך_>2:{|WSSO=e 6/7}$O(g~Pohjс߈~}UV][k#6 nV 1LKE/W=ANzl%8;w1;2O2YYv{DƭyuR欦L|c}Idq?"*=&m%Iwƙs|q[`G1:c>&+<5E0$^J'3K.׎}&^tQN}CJI^[4x yZdUY}=)=i./BõvcjuBp~N~=W\ fg_`cW}6yyw]+o8zto?ozc櫾L9V]f&?PPUn2uyy0gڣw_ή+v~A>1~G;O:~pFM;scSΞ+C=I!z|_6^7lS7EG;O?mB,M|?AW鷿a{g}&R}D{l^;X=گ{bP>~:M7XGm*?E_ocTwZ^ќqg7m2t|G |9 =@[Щ Wj(zO.!x/0>3>EgsO^ͅl/8t Uqn91e4w<<ۅyuz6})yY%8P_G0tPO"Ok  X0[[uf\C;KDCnrٳѷ9ɗ7"_hv.q}+F{ֿG6<2H!6 IDkN7`:|=&i=vX Qv 3GGO2C~m3f0ބ>[>~!>X瞋^.hQ Ս?W]Q`g.UC$N:9"dgȏC{Srhi?S]/hi'|5?E,Lx#Ü=rp5T}r D6zـ! ~])i~Ykl؍[jn>vb|~ު~լi7 [6{m_lt|" xӒ  C(G?W;6@9w⓭v4 ozw]KXg$s>\_^cb5eqn_Tl08IY6Г$$Tw xU 0 TXc=é]Abd׺#%Gcx ٳ_E-r@\cxFU[^6?؏TT"LUfgѪZ=/Ugi>zͽߟ͇ݏ/' /äy5ȞK|C瑘tb^Ϗ뚿Iݫ`}cOc8}u|.tͻ]} %3O7AZt\~ZL.G8}zժuܢ`/8qbבga/d mE]ph'6sSVZ]e'WQFWc0 RRe{7s_q70Q3Eq1/o셜=j: dz ;/sC_W79yr Qğ`+CPX5lx>mvVTaXbP޿zד=7. LTmt4Z/Ķ -oW#`JC׼^?|3d>vxjm ϑG b?u݃a~A^uNC%|A.}U8?[F\(mBO,|w#yλQnЗ_/DQj˳G~Q6#Ν6c:v1Eoh Kݙ3{,+,&F>}yh$秦V7,NN1>\>>`Յ_=e۷Oq3gRɫj?u4zF[8qb>: t<5kvT PX'@xx[n932"?ʘ3\ Og4Hbb5{~ؼyK+? 4w|wtA^#g_0Ec(2?ekHA n]7ʂOV壳'Md〽^0$p^' &@'`3ΪU9s~۵s;v4!f' K`[ 7 ݻGoE;NoGdrdeb?Mӗj% ~0_cl-7[D.͵ȹCN2=11ETILو3):FAtؓAGFW#%{'-rOΛ.ho.~DydCz8~wrv@:>gйC8>eq/Txo\rr+};wWjt#@Yŧ-r:L%Cȿj ?Tk4FgO:o_"C^|b'mOZ:؈cׂ21FFZG}?}\:ϏƩzp]5l<O܉rl+EwM>AO6Oׅϋ.Nb†slS}݁WSXM}+>`>#gg}vHW>hސy\ƣH>8k=!cc2_Ts3{G<{/s|jy "q +4߀Z1u㻏:u3:ɾDŽfpnu=|#I'lpC ˷aëU]ax8so-@߾c%7o&LJl>)m\Eïouo@.|gm7@|H,FC|v[jhM8ӏH-0r'S:ͮQ[>M_ 1׵n߾m/o,} > ]>z}gr "" lǶ& /\t\(x5j(kq"G^f&x㍦g5ہ4IANԩSCt9YmyhdxA|1ʉ#FNCwY1mun{xt!96y~x7ov}DW蹟gb'/TtnR}J]ՖS*AG@]Djk j7n>R_^6ew NBàe!?c~.+?g.7wo ;Xw) fb/.O4inO['!ԟ?A>p=دKq ?9D P7n'׮}~=ܫu:6M~7x?x7[|ұI BCZ r<ϒ yuvMoBlԉo yYӴzK1>^5dwr]}2e{EZ_E}4>C⹡1G~ XgAN0&?Snr \-Z3bÿ\|"h}<ɒnpï{[CIU[¬S”'.99tpXE0;z}#Ol.=-7|3PVv^ퟤs|; ߷߾",na0Zk!Upt?:(l cƃ{srZFg0?77dz (.^/h $/a~T?YxǿջvvW_Fnذq:hݮ]%~[.cǶXd#|_6bOA;{l` }x,eC f:79 yC|m?Ofk#h^9E/y_ANd5=$̂>c;- !>179WKS}Q9J^r?SwCn#nYXϝ:Y<.UEn/?!υ]vN7lz: @)f>8|/9fNcCY~bQ-n1w]!vY5INn߭q\t #wWRcAK[?2K♳} 'm8X_6u_J_cVgy4gsRSLή(? /:cG +b;>-2mE ίJou=WǞq!]o O ayߏ-hP{ pq/\Cm~Ӝ‡=F\*&g"/a1L.(w0땵YK3\8 X1xK]Zna1χ@/OGyk[O|;@/%A T[d9FluA/O$E^~ clSɫ4^|~+\=kuy-5Nc;#&8톗`>o9;8:t$a#{A{s|ϱ`~qk{| yG^v?я߇|R>g{?lq-X">sԾA'_mz?|i!tW< G|9`Ds< 7HHIt;*~Y\lRs&"K=>BbhW>ٹq19zL`')7أoh:w~T,2r(A KL70\ZWռ#| b'#b26~A젧 8.-jhU?oB;L^,|6z=EW?R-\c  b9C;vm= [Loxe"]uu7t7m^Skgp_BBk 99k.sl͸/n|۩u!v=9m!nMVR+1f.|p`>yC^0y's?BfߞsӬq<*oZx&Н' 駖x&?Ϋ9/~$h"C7L>Iv2gq[hF7Ht*^D |TMaIBB3%VډGF`$visvynĸqsw| p rp~xxG^(/O&F?'|Dڵ/IIM-s|rkjw饏X2tEСO gd43}'#hlJ!'b5h+\Nf3>Mӯfn@Ȟ &JF>٩ :tփCpwakOwכ|r==2n<  6AA׏vY+_ÀUMs^b^U<ؕIrMfݛ*  Cr`,a{EcO9s:{09u3fCU9|ț}|?Lջn?1.aϒ_Y_a;>sr槡a] 1yĺ '`@|Y γO^ SoԳZgi2ۧ'ϻ0ڹHF|byl _;dPp9zy謹;ȅ.;i=Ax~܀fFه(y@Ɗe&SjgÊnMs8[.t>R<6*xD9K=؀ZU|#~bYIy"֯Xp , 4sg6t-`> $=>zXtz2rR'1| [sA>; =OX0ۑ.?} |+qx ȹ5dvboB<]/}֪kϹ T&r>nô\L #9s ϊsakΒW7h%g]hToCrT{VVTjj>eTn=N}agaGܹ $<ʨ0X ՗ L^&' ڗo@͚ $a2@ro5,ohv 3~g7f;2v}t x562>'WY]!~{?|B9dLy7yyygDL~li Ɨ 1 Pz?r6"l,Vmqs||s0q_{Cڀ=;<|9˟z*rj죏 XmԖ|ο[ĿA8z[NnU=>8sg/sƆ`9O_*U36Y|0<% wtm 3crqNSsa\)ZLͩAWp."|ρ>!7 t:9"fO/v5EO_jwVb.@Rs?Iqcc\Uw݂Q#UG.! z vhx!KB|_Gdbkb>G7\#%u#} ]vϕZrF>Dg_;Qv{55 ktr qe=P. \|~|l $EЗc"lU@4Fql%DOK)*q3 c=[-ׂkm'M)P&:Y։7 YLn~|C'S +me C{H{g޽Ƴ;]#fSEg8W>c|NQ؆ &&!sO?UaW&0?$/.P?z۪2ǹm/7:?<{smA\\ȍ^܇# 'O.ezċ.7;>c'=@\>XL(E`/_@}x֖Sxٲ7o[oRmj!$'yjgxn wirzQ;'ǑN\c>)*ىhg'1U%'8%%%;V{7زK < ?Tnfoef`Lf8rd3@[bpk[-?#vBxl8p`?Z,`8z/ /p/PÙ!F _V6 ?@ .!h*,ls'9^e>#Bbh+J ~x> 7y'^r KT]ȭc?A'3;BM|=g~MG.%{d't`o+VՀ\+\/yQÏSU`G'@Ivϵh&>i&3ijqg j 6ub;WO9';;X4#!x[9+r_roka>|@]ABk3r Kx|Xň =<3 3w|>}=,law%03r.~ 0yy9?Gv|b/xǻ'x*/: E'F@|  L=|'fsM_1rTkK Y}~t_~s.1褉oĶo [c{B!WN>WOs>de#X.%T5m`9Pù {VWg عzfxB3F85ifQ7tvQ _K5_cx{ݍs` zNI{j8.*j< *v0<1gc9؉Sxr3ykM㗁rnj60W^?yM]#η!߇ ~!?yP{/?1^~/H1\>?roV1F5hb3=yݰ%r7h++Lms[wbkڴ k8'(lb* /@`W"?+޹[6H3(TYEr 1F g]hm| _ 9pC a3@Bd%q3Χ$'v :{/G< ]'\y>𿊷Kr _| 5X~5J<\bǫ-w|[ՀxX~r2G`=ߤuRݟs8#ҽM(ԺbxWrs>vN&z]ts'j__6pVhh~k;D]H&MX'ِ}s|hg?'ЏoT,@7}xrT=Ekߓ\C^'/>yj9t:S3ut!75XΜ٧:/198z\Wgzwlցge5iGvo`GXZa}B>ĉmȧݐ! {~u'[l6Ԇ.:B;_k.3ٙx~ǎͱܾߏ>2ѣKotQ # 0GdݺCsO _ tdyg2:t yyC⹝; nsÆ*LIuWH{|d9i?vx L+']pxoѵZغ:38 ݓc.}8||xlND3ۧn89>r/H ~E?@<oΦoMNq. G56OyCTqu ٧WJ>S)ڦq|յ{n`3_ȵe1l*(!:kc?6nA< i sr:tpu=5;do{U la51xinS]/z0~݆>PFfj,ڈO?<8eIl>Fws?g0wK / ҏ|}ۖG{s-.E?_\ f;LtAG?sl"MϷ;>09!A2u4n0:Sow\,}H6'kZ|K8]3mR]"?bYߡ_$ШL oO˱X E^I[=׽+wS+RjOw ׅ_`"> N?0~8K"'\vevnwD+ *!5ٷȿg|əק6r/>~`iz`8 >gg1lDc,mrRa>U1/ԮO_HG>Ұ;9-YO|ܘ1^|"yiZC\c0܌f8ztUxjҁη;iA$b3_kq,̼_s꜁@Ωǃ9_`@?^6m%w ݿ{̷oxc,o5~a'] `@wx>ۮ}|9S'a\{WTg'Fg[Ib7.u/=Sbz_b縸 J~El9Q]4I w&h$Ob,x{7p/E};/&!_eHx? R{R}bTr̀s9Z# qk Y=ߣqטOY[_\¿Ye#yOYRt Y)[ll YK܄?}+]z4OAt4j?EfAlsE0ކϸ ܡG ݋&vM vͮ7¹g>?`wkSsчp䫉;ϑ'./1ހWŻHGJZкd > Y1a.$tZ8 ?]jov _LfLAh/gN]l*/01 6==@~"+vGVpQN*)X.o`6*-TJI6b砶GbՇ%b`qsȕ*s.P[;ߪhFz]x:ss{xUW%%[]A"Cdyr٧~ް2^\=r߀}ˇ/ oVV[)]ف# bK|$[3!ù=M%?0!9S zVX5fxF3CQ:SGyx;P2=8/; CO\b؋xm#y޵Edfᬡ ^xs 7 9秦CxIY'7b{X#>`Y1]+|jAtsoHqdr 3.!G|+ex(Dr >WH% uM׾-d 9oW= |WQ߫Mtb~U>^<، ۣcw Yxrc?h >Mց*FwB&<ĻTx1qQ6S|3l&Q&KFRtߊ?i`m/o`xw端pn̯ȇė][]D >7SȮgk~8_&s=Kra*|{,oS k|vuQ 0p^l7ph8>!v~$z<kOaP 4|Dl=os3 :oG?tt{֢Ӽɍ'9-2+hk#7I#Al%G ~?,!F'&=zkM'dzAe]k5&KKqmzg~Hx>O'ȣ~rݐ!/.]nOnש36?my׿dlrWl =` D[y~c/u tՕ͋ 1 "VE|QhD|Vu̬U[[ERvM+:ZSp T* $>ʚZwr?9_m?5:˟cG1a**FY@j_=wOk3\XXkYx;^{@}>l61bVs3 ox[A\o'滠@L ~36&OsP!z]uh~~>|࿐#ihh %{o8|?{6ag) /o5#AYcYv|od@PkOpxh#^+ )$.&oG.\4{+W>`0g:M7Zn~#N"^%[4C7O Wi1lNTtoWI;-G,!Ae4mdrxMGo _cb {ŋC7gZwCtz183[m[ѣ?@oȇ7뇘s+8?XIdWAKTp}>;^cFrh ]<Y+I-_'y_wf; y?g\K^#0I{Kf$nf!:~{LQgkBo.\{u,0/5pM?a>z ,7as.]7yd!X\ g_ƍa3X:b:lyb\7.Xsth $oO@khl^<ȞtruepoļE?Wf6[([0b鎎V Xo+\xî 7A_Æܳ篆-O-&z˟GAklv{E+'O@MwY;0~#Ÿbql09ph`U/|O'W'w7SÀ1c~bzDo߇VÇ[Sks0v>sVOg;>7lݺD׺8--;i'`a ǞSHz@>'l >\9sX>o4y[o5>kݢ8p tQNxa8\=z X~^ftIyb~5Ysr$uڰVw=vmj6R> `8sNtoHBxpL4h{ zq^md1y6^YE'oMt/%%CG~[PY9u"n)*aXlvյvv}?73G=A}wa}0${0|zj՜lٴ0yٛr=⫠K>- ao߁_5 ^9Y󱣱w`ɧ7 Ч,'qh,gB`u0pٹߦpw+ORlpy(X5Y]np!O@;@*'?-xO\#ys4wʞi$ZI%=@_9\pc0 F989Of?~e|=]MޣuosICkM{oW [w78w͹oHvz)SBWL̸^NlHHnZ~(n0lܭ-Dwm35hb#4ÊoR~Bqαpe jq$P0ng[k`Ϧ//h,Nivtڲe1}jҤ0Xu$` ܇%y!ϙ=׸aM n\|U[k56n߂DCrywK668 ;&!5ջ"i4_hYXԞ+s~K9%=w ⋁D{ ܟyN'g-J:Ӓ|oH5X׶&, Limb oM: +c41z %s4ac+hR|;4Lb"Upu~1ظv^.n bq P#_ ΍ .ĩ4?:GG:DzUY]V+ 0v;t>1m޵ki"mSdki>ڵq ҲwhW6hGMz-FB|#i0^b浖W 7r 8]cb,{oa^u>m*žcuPqZacpO$G R &HІzj5 HrI:yZ~W'.~a C#@//Ah_ϡ9>ԗ^ Kۮާ5z<7\}=rTكjLO |> ~i={}l=yNz{L>~ZXWBNdb^,sDcI'bcq&㜅N\ῡ~xQmMϹ)=J(}.J7%ǵ =Us?GbW=%MC#=Gx 6o.=jg%SHS뽠-AA=^k b~@}q;j~5%v\mюnTy.M2RLe?;<^~#@{l 9onӼُ[Aw_pKI?~s7qXsT!gBw}6ϻ'_Ru GƎ1[N$Z#s^yw2SƊ|'9!SxuV5>O|ak06xAZ%xNQY_d?p~Z{UO u960Ϲ1:3LGA{gw7u<8<ǹzsx2 'oN4G~@CK܎ 6~zNl6=)lwwo[Lm<9y}ps<nľُZCw~Ԝ \;p pOm6|\ ywr \ 1/65g.axȷ3@?s=O͏fyKۉMvۏ0Ji陦Ny#78.rݕ-/mn:_(ƾ̟^_3ƱEd,(ȷ{晱cF1cѽg}V61Q[7zp ?{36yLTG\Ǐ==^ |'~>j=N70g϶Eh 'sukjLf QK3]Z쑷 sCM [BB=ny\ң7=cxStG(wC9{':'i0K7j_+[pc5aKKM'<} C ܩų'8YGRO#?BMn̗ 1O>-;x8`x=S}LK#>1xQ:)>YtwO.gB|.:MA{i>CthܠxO_Fc;Av$Ǔ瞣^ҳ@MLwD@c;trp'X Q~'zg֒cP8P٤w 1d͸?J r؁Mе/w7n'z])t5i~s߬'^5?]fl#=G70pt/)x3|qP{܉ޣ&tZ:pxtLrk }Q7{5u õW>59֞޻OFN?j}}C֟==_bºuؾ=lӢ"N΍p>>'+|8YCc{cl<8DxerCLԳXba/oj/ ܷ=fwz1bK9R74!{Rz}}SQ5aGօ+5},wsuޮfew.nK> ~ 8<:~{Nރ`MPZ:ĵ 3kjz)0`A{a/f࿀Ö߹E]ad,_eK/4CG.o3alj @{>EJkI|,D}kKKScXvǦ#lAn+W[{x  ӧ)S;|` Hj7@/ >:bDIa_/q!OGjLvaoZ}~E'SwN;4olk⥞͵y^g';M%8͎آ%ہi*zJ7q]ni<7׺'O><X cQ6=yCC{5s/[5ݤiz^UߟtiX}/_o9_+ʴoR~ y.^L ^* GlcܹOQ5:o+mN?/k!cYֳunWO M]CS_\Ɵ(^F܈Vx:~>SHpRMsykZ]'omXK}؝w6_nK}c-=V8[q.5m`z!;}OdN/g1?k D{E6@}qz)kihu2ƾͻwf!%Jߜev;+t݋Q^xT>}#K'ݻЧxWG\\?Vz,șV!끡omd1n߉}9`oɝS'wg#A.<ƨ4EEa<c63@Oj%ہMطզϓraqlXrs?.V}a(8SC-@3; o6'"st4vjxz< ZB)'@]._X?Ao.@n;GW@LO?1f+Ƥn::\~N{mKGU uaz Ջ=|􀡖@C}tgӣ>z1~-̖-o"7T'F>X.]ǚ0i=z;u2x₧4~h~7p`%/M2Zp:N"O[ SN[Œ%ᗊJa^~<8h XU4M5uny+=b 3nysdЦCS>yf}q4"k?|澿^j?\Xc!0]m0n[6q͊}?ip tbB= =|Ѻ Xe"oTHג_.uM?v u+t ׁ,jrGw;'>zy9.7`D_c:/X|^җ<)n->6y{{|5||/a,zo?j~}}|[O/[V?V{J }'_G%FӃXM=mr#,"?hr|IoNm㕯|e~U5s_1 1 g෠uGڷ3: hTi<p@SyK/C>]@CguX]M:_=j :}"ՙ'BÆ=B{YQQ=j2<|ȡy64j>ujcsmSkH [_丣?r(l0waذ[Yg#..i{z;ȷ/١Fa#=>Ӈ_Ϟs֚%!x2xZ - u./K/o W$wlmVAAΦ5>L2OSYYcS|u]56% xPvx'l4|:{ E] Wpl7xq ۃƟp  YΚ5` 0" <txst__z@,~<Щ#PlmOwWtܷ+^}ۋ!VpLhts^4]$i.@N^0Q ~ǚg{G}z~O/{5=$-.0WlX?r"&2q7j|;UV?= Xz|`G:52GB=_5 W8wg' QyaLLo4ucZ}M\f&C˹J4NxVhO|VRGa~s04ޥySLJg vz۶ '\ml_O=x`55l1a`yrj hc{rrl+|~0b޽bﺬla'cut`㻘+&[k]m Ϥ]POL<9s=c-HxA#g Wwmx@~/`zopenimageio-1.7.17~dfsg0.orig/testsuite/texture-blurtube/0000755000175000017500000000000013151711064021700 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/testsuite/texture-blurtube/run.py0000755000175000017500000000250213151711064023060 0ustar mfvmfv#!/usr/bin/env python # This test renders the "tube" pattern with a checker pattern (i.e., # showing all angles and a wide range of anisotropy), at several # different blur levels. grid = "../common/textures/grid.tx" checker = "../common/textures/checker.tx" command += testtex_command (grid, "--tube --blur 0.0 -d uint8 -o grid-0.00.tif") command += testtex_command (grid, "--tube --blur 0.02 -d uint8 -o grid-0.02.tif") command += testtex_command (grid, "--tube --blur 0.05 -d uint8 -o grid-0.05.tif") command += testtex_command (grid, "--tube --blur 0.1 -d uint8 -o grid-0.10.tif") command += testtex_command (grid, "--tube --blur 0.2 -d uint8 -o grid-0.20.tif") outputs = [ "grid-0.00.tif", "grid-0.02.tif", "grid-0.05.tif", "grid-0.10.tif", "grid-0.20.tif" ] command += testtex_command (checker, "--tube --blur 0.0 -d uint8 -o checker-0.00.tif") command += testtex_command (checker, "--tube --blur 0.02 -d uint8 -o checker-0.02.tif") command += testtex_command (checker, "--tube --blur 0.05 -d uint8 -o checker-0.05.tif") command += testtex_command (checker, "--tube --blur 0.1 -d uint8 -o checker-0.10.tif") command += testtex_command (checker, "--tube --blur 0.2 -d uint8 -o checker-0.20.tif") outputs += [ "checker-0.00.tif", "checker-0.02.tif", "checker-0.05.tif", "checker-0.10.tif", "checker-0.20.tif" ] openimageio-1.7.17~dfsg0.orig/testsuite/texture-blurtube/ref/0000755000175000017500000000000013151711064022454 5ustar mfvmfvopenimageio-1.7.17~dfsg0.orig/testsuite/texture-blurtube/ref/grid-0.05.tif0000644000175000017500000102257313151711064024477 0ustar mfvmfvII*> 2=RS~&%( 16rCN\KllE\OC71("%?(TP` dJsb*nfY2016:02:25 9:51:38 x ,QCȋ#< B !! Ujsn3]*{'8=x׽=Lt?mqL=tӲ-|c[zcu#/W\!Fu?0bE]*zՁ{m]]/p|h?-,tpc-|O?5?Qzc/N;`"*;)ZǓզ$+?ic&}X5 ?^N꧌ܦ=Z[,[s3^<7,{n]Ky/{cG۾ov OJ>{>/: qF, uouG 59E+fIsZn]h \EަS(ǧ1PĢXoK J#Yc3=0  xu,.yCZk@6Sn q?_qL(b:;8_V/u53t"e,ڠ;t*X%_щj:K>*ڑ}s_p!.hWܛ+5Pz ;2>_Cω#12|a`bq 0G.e]Jǭ(uB$[.#GD#)st0.|I\"֖es^Z7+;ꀃo8o~? cu>c`b+n?PxB|Ī9jz_k߿b?~G8U;8_zMg]p̮Ni c}x] n&k`lXT9cp2,뱇TͣKf饆K{5]V'z?\3Ty P;{Q8VTNHz u:ކ|Or꼅dԃF{h#2?@uaF){ ܀۞YA׹(M֟{߳ TjQ6jC%cdG19{/|SO>:7/19fG85;kw >uV5BzlϯٿV_ߏd~h,fr{x~pa`5( -G|?ycѡ?݁+i?Th^"W]j)CLk]@7oH&M.4oya3TpYD~1+ߧ͌B1v^߭(ˣ}#?Qc~-pײX_5@o [B|q(4@s9uP-4#L*3q(NxkϜm~< oOGdbOʼ}gZ|:`.V`CoMG u Z@o}/FML5c~^29i?t(`k`e9&¬td9[/;1|9?8Y]}AD+ +,F@]ޛߍoe9e-_}73/b)bF 75쵑I>0_:~e:j2?=g?벏?TYey>=OGgeXV4s-;0ڛK|U#_ߌ[}}G |b ]q۫`l^-VdI-)|7]>nvC8 cߏia>nw s`.`h w~}{~{ (t pײYaƏԱˑ+KbCBfio4,Or/ɝGvi`iyMTܟipDc7?64n 褾\@xM{;ڣqU􀡼9t Y=Rȡ=Ukr|d0j, e|eX އyɰ?=g>~3'"[&];p,} >M x~>ݴ!9ooO[Zd?g5S?~XW꿡lCZ?eGzд1>B>>{+|m>oス-֟i% 9c|!{s?z L48C/tlKhbw8$ᓪ!B&[^n{.LX|5 J܀D[sqL#+7_|8o0gkh_k?_,/-ϖ(x_,[cž~r5]3H{ k֏W_7֕aG ޷5< Wxgr2}4@j>M{F}xp)53?c\m!Oٓ7`zWoA{ըm58?ke׾ |%|0͏Ya {x|'s3 0(oou|6B Xcq87쟌D.~~r'd?6$4[dd}|},s1,o:{ۍ.#5Ğup|o 0 G~,]D?O[~3fc mkSsqWygZ ngumov_zZDk؋$#Q QŦtպ#0tK}< h4o,U_8;_]ߞOn.q~k80Gf=3RS{UYc* _¶kgh3f3?}~?&Pߌzэ!@ãe]!,oU@OϦlmotT7 Aڄ>p:`&[k\z_F@685n tP?.7dHbTͿsȚ?K`_X/ϓi~DkM ??k Py-:tsʕQݧv֟!KE'Cs׼gygl,{"F ]oFྰ9.|?=  w{9 q@࿏,㇋H|ȣF9Hq-zب3lKcyBۣ&0Edlu~;:wVKnj_OzuN{~5Xj֢p ڑuak1}.hB+cNRgNs[~}^<׼G3}v{sUX@7$6c2^0z&xn?^=`Vp͘4x1oCd]86X }?>~S?8?Obn~~K~A`\x4y v[^wo<͢#֯Y>2eGxu=~~qI;|~̗7?_C~;{]HI ]n>yh>.PhdN{u#&\\x;U9`cȑZ\RqgV9IG ueC3bn߻P?՗k8Z||o#i tLzg3ӌ\Bcq͐?W=sNIfp"ʯG}>3˽ʻz~qSyx `Ff?, >m]=ܮh?EPZtq4}ˣF-@w 胖5~KuieGǃ6<|wTM NJ :Nz 63}ܿ1ou~ݧPwu/\%Fd{>eMۤ"k7[dnE,l,~dsG\ݼ7g`p$^Ӟ>盍{{|g̗=? k,Vy>}q@wrr:m͝[,`l?ucP Sq-`Q X}6i{̂kzr PڝL촕|{c1k:ƛ{/7/}fۚ?97=q~_/ýĹ>Ogvܗk o͝?7:>cxoc˨aMj֘'V^bl4{[ϙ~uy^L:y Lo1}_0uX;8'Ī?` n7;'M@4?b ;&C|:@440S]&* [o5y!af}粘aZ@t@_bᅌ4?Mez, ^lh:`o-152w_X?0m&jj|='GsR|jD{~^i}iZ xb_I*ae\|dg}a~~_#Sm-4Ng{?Poz4'#9Flfv~7Qf _5AdدM<]?q`  0Ú:g/Hsg&mσ&?qv0o-]Iw)2Q}y)c}s6jN✿2*89L2[H4@dA}7@bL|{ਟiڶ$5B5#Ũuc~~=5c|#$mn=qgG4>2_xٿ?1 SϞ'L$y>Osws_5׾ZoD}$~Xx}]/ߥ-pq^5]B0<+H!㦎$5:՜so&' ű;}ci9~ncp,1?= '9lbʐ!/7 9:{g KA `m,ۣ !}.1ߟt:~>Ŀ~:c>vz]J@nIi(nS MsI\uR'w~d=0ٯ:-Fp#q8>A|@Mk\\w㜀+uՂñl^K :P!#ɞ;f9`;긿q׋@sޯ?2r{V>ټpoGXXi5OfԼM5˫&v./#cw}j͟15Ưyuo7~|ߘ~X@ƺZ\_|nF/3F\ִJʠz*$g3MД41tnT C&ezV`#ub&?oك5@\wm GΩ͖L/ξyr𛹏=r~͏78}` 1 r`FJs=3rώz(;K[ /| 5NX?3;!}]=}d|{gk"84rO"R~L"{J7';hk~_j?C=-L=x 1)1AYT5c|?耆35#K#_~d~̿ `6NR@q@3Ǥoq o0oz@ g1aL4pGh{T۰{h׼kKY9pٽ?ƕq}<߀{+|k}s):vM561{iM;@\B:VR yp .בc2U4Z -`o^4i@skDNpFРLEwfQN^5 [lEC?rlԲbk<{UtKH|< /^G5KuuOR+C&PikP5<PDI˸mo5V ~W{{_y*~1o5˯_eE;-6ѻ{~kyGo޿Aڱ_iYy_oMM](V׃_&*6=|o`goԵ[YW_~zByO{|o##Yq6z_|{HR;AՅy[q\|MLz-yTo&|,7,ka9չ01o;Fs(ՁyeϯR^Ͽoxߐ2BNϸJ+2i 29lLPdt[BŶ/G_ooSW,Ws /35GG{KiW){ºD G[c~++ L JߞDM 9jgQvF+]+Tl5AU 0 Pmt)'.!'\sr\xg]Mg~OD\߿ z> Y?ק}w;TF3,b ~}?|O>pv9/Are2]q"ˢLw]*C^l(z`ϲ-qA7ο3Wk~VxMaB}a~z/9uA\q9;7{>0Bn9TDW406\Ca_h8pEs <МTO\kR^ҏXxiI8qT< 귀o#mMzUl{ V:o'Xf]P&K8'P# Vx?㾣-ϋ]w)#/01vx{mއc^^R} ߱M>׹oT>^V|פ>˷q1ϧI[>C؟3`;XjY|@Xr2BT9lV8:o<6|f 9Cі\mx0v)DW#j~Gԛ:JQwm#E]yîw2 &x v{ H}eee] k/o}n:w r$)Gr =%㨳zɲO6S)< FSƺ~}c=k綷p\'z~5a U]sOi?KK @|:gx5qm}:Z'И (vOy@ $WcQ)ؒ1 7ٮm )x,΃;m;א_W%Ml xʻUt;No`tu؇CM)cT]JzR5;_q;)l~}oBF S~?^uE vx_C( ys r\<]czAP80'P `#X' <р4.H $|X63qY\䀹qYF93}v=o /`MN <wra'*;w}32?> iR ˃\Dݷ"yklA:ylm~n?c=E}:F~2.F%?Y׻2b|HQϤe ԿpSgqFޡѥ`9~i ߎtP|!(b>&?{^t$f`]s]z(sW/?u҈Pџ 63~ⲁ:Yao;ﶖ{<T^|G4?.EV91+zb}u=o=ז>Uޯ:?CnO4e}5{]*ؤi8~C?b`&xƜa,T87S{20``~j>&y8/z0@vILetcZx}u8c~Ju]lmTyz^_@ݏ}e/T.9+ʣїoίDq< `WiCH>(f+Iy pSת@"O=&28ŐDod<:-x vm7l0x|UK](3gj 'ܷb09@|s?)Ne-B!Wt g9o5HU~M1Om qD dllW)-KcyX >֡ O8@?-y3༉Zuܟ|l+51+]Z&CO==\ lWNyB53p=dlvǴ @/DU tkRH$㱍~%~47Ӯ+BWZz6 R@.r4gFV)c?棿"pėqgi _@KdNU-Kp!gΏi$:a? VdrktYl9M.6v>m}H@?-弬ivVcP a1Ӱ2AB}$?=r}czcmP_ k9g?0~_8)X@Mߵ|֜Oi Ds+@wfmԠ6Mp%6K meއ߻Y~ƫbe6ORlnc?}w[K#9'J8@~o9'$}Y(7W#bMk[}Ay7`)`1G}?c>5y# DVq_:~emYa I"?-԰RwGs1!FK@emnG썮zx<t.ЦM`#XOP[9g`0V%kgn(I%:J7Ƕ93.骪N;ttvjQNM'Lb/I[=d2F< +sP Kٲu̱$ӛ?yCȻW؟q+?IYɑ uca/oٿ؟c=sݾniͱڞ}!tqߝ RJ+gB~Ƹ2 ~L kmL}G_{J?~ot\@ `>Qߤ/3΂چ'rZnI[V@#<L An˙-`yXsc?NN{]MXT Ozą:|e ǝ6@`mĉ\x޺rӐwP.VFITլ9WiG0ɚ| ׳o\F{0gO8|/TkW M`7,m'z.Op دzh3GdoMdN/ %ϡ\l%r ':bʈ|po L)Ԟ[6 ~ LV@/M]0H\>e^\4] 3%;>`4 ^ߢ__N>[Y(`}`:ܮ +o;;  l  ߣM`+h)o Bl~r2yx'gY1Yqo P1pfK1?]>[A-doԎmPxw_r{z^KRy_7 Y&W>jm1~| x_c X_5~Q]Kf3uMq c݅mI/3E!oo$|g#+Jq=؛*a,&ڝ"0`>M0/m<r(WV8Np۽=4]m>?,g~}|؟pB3MmĘg.~9Ó r3CHh]eg/$?φZv  vgT| FJgdT!fEqu~~~UJGl?߇a>[GP˹qieRv-d@>﫼#\G~-K\Ķ uW.9y_$1:8IP8!Aޛ |Cm(Ю~a^;op;=ܧNxKII–_Cq~?!Ǽ (7~pdGVcYG>R @EI}6[.G9aP Tab~5.0#~sOt~c5b"Ѥ>}(U}7j x;\ =pW5'/'е?l)[MH`K N:ڌ^u' rW.-R-حa#w&[G E݇R{Łn g7U v _2?3K1CZI0g?{ӱؓ1מkyc'.6a%9z=g,_F ;r.L1!R{엖~u(2|λ `?巸~o??;f)L8߫Wo]`I0+X x-T7Xa~@:S]o~] y !"ⵎlv,XrҤ5N-IDyT_`~c5h? 6|m[mjQ9@ƥ}SܟVK9@3?ۙm}Yb[(6/nje"y7ϲ1Gzy4'[딹bmsͱs6~`ԤS6i*71aV/k|ߍsC{MXGcc|~K7F~&gD[!)G:ۻ^ 01 gy*w3$Yܦ[_Y֔1mVy?];;>"Q, sRde2{B9P{{r>.i_qtK.9࿮0_\O߮om{|{Gb,6AOv)p,ӭs=>:؆O?mT[z<߇`}my~>?gwA~S9/ڶ#+o=|(~鉌cG0o :2>bt? m6'e]2l9 cv +ި_/~/ { oC߆߿[-l.ߘSJwOcO=M?o1&Lcg}KM=-VF>m[kє2šH:G~gB0m?|fo==p߹DNcl)50d?Հm9< y1ֿ62{/^IJ!Sϙͨu b=[~|`M^࿮k V~[qia rIzxYǢyz\g߰ [ot:tUͮXS_;7WXW{G k>t _rV(u'$lK?%2x dm>CӶd\t5}eBe 8-` Uha? 8"z6~ 71BD\_Bu^/J,S9ˌqQѩhܦufXe߯Npj6}?Y[;ˈh53 ?k}>/dz}Ld϶=J_}+ 3&PDDc?Cb_vO>^'wqo~gcg6*%oW?;~o`;_c&;t/%pvu셨_¿dtΌ8~򙯀-uOo!:?눾uEԅu>рxlڙ4kŋ~j1W:_j\x>7um}/%oFGZ"wA_#{C+|3y8^ / Y~^j??~PXG9޷_W;Uvߴ#YK;:w|:8Wsrpt s[6H`<`rseNX%{q1֯X >/KAeFF:zڪ#,x61oq]/y@s}鱁i_[܎?WmT8@txJcjw6l6u~i{{ 7cn@O7R|W㫿/pvW} kGkk6OP?(? Gw8=Q5.k۸&aA|0B;@svP/967vGa@?At,Oy, NJGϸ:?QGvY`_Λwu=oڝ8O_1PvЭWk9?@oa N1n\0}ww}͞5?oC*41^p 1{Vݍ϶0;s ߢ}+ݿ_wt~;.u3rv*6%K6 ČL_w?x0?m޾i :& 0O;Xb~N\=0 |oOl/(t*xP " ʰ[gd쟶H9 k9YqFPי_yem6KXGF#6Yq~8x~|1b~׿;wgM@θݸ@PUGݟ/lWm3?чO#&// e@/OuQNk\h#m`v~-@90o0=p6uw6g:^&׈<2tL_04ݗYqx몎v~UI16$DZ.H_ZT)ɯha%[lp+ߧ +s7M˼SZwX5^sے~[ci8_k!=GڿƷvm}/e?Z5W0]y]ssdT=_>*,T904|}bǗ1o~~ f2Wemv>?;OJϫE^*oV+O(^˰rbۥ2lP~m.K¾]}mrٵ6Cl+^YhxWzջQӫocWt;+?ꚏmo׉\Q+[_8i?ٍ }nr_ܤKd={r~So~,@ȵ:SNNa⫟ZnÎZ8W<2 YjT7NhU<3η=D7&|dg~C_տV]SG?_c' _Hv+͎-cx5SݑKvy%q[/`} ?aiɺd]d+j.n{*P~ñl3Ok]tOR i =Extwp?Uo'k?B:`䝵@O,^aЗ~3;}>q}*udQFT}n>cu<'46O.2MjZ@K;yr>h;z.>";}aoR*bW>co&ُj~WxWh4>ogahK^_H+( fvEF-4RR i9lAN~ ]\L[AdcuHr;WyM5c}Ԧ5@W_o'Ww>WvvG&zEE{O0j`8\'%7dl{Mٯ>g[]:Wc>jl~^1~3Z&o˼wG <넔}EbV~'u>|.,t`C^5yKˤ x*!n<w>Dkpu ےd $85 p<k?`c @hu7x/%~=x;Ss'`6.6Mu}sˮmAoWuA޻fzx>1?2:N3? &un62>ñ:+*~WF~ױ~a?vlsNOhC:\זl}\~O}Yud3 @'_=?)7kX~*QGpd{ ? 0Nr_ٴ}W6x9vVkx,/ґP :7 Xα xnn];ly/hG7<uUiX%6'Da(k.gW vd^]9x_X:)}5.ij_W ЊbߘUؿݿK[x?0ֿoh8ϖbwZӫ}c+dmX, ]*TWYD?xoG\-53F u`,oA$pUw\o>O0mhflWz? ? ~/& 5 ~'0ꀝ};zmyw>;Q680.(Y7@_ыs+b8ŕ^x&@ Squg~E 4XvZ Y= x)Um 'a{i'83/;kkYsAhC_M/qO*~yNVe1o$x6o*/ݠWߏAd|a++ToltI3[QFjXW ZXlc+Č-iw. `̋MIҡUw3=۸`5$l)x^ss1buQc6|x_u#!/p~2<5/6f6?:S5Kj}uu3;{jyף^u{%lg~i@Ozf|8lA@,@|"1ۦASdc0޵׾py9%͘WԿG} aKhx И:6X$m|(ecA)2>pmn-ږb22A~U{}+>2)mZ\ NFõ';}?NE/tn}{|g~1 1u_`>5a`fc.Ѱs-׏-ʵ:'v|simkOG,694S0?>[3Y@=r| 1'OGBzo?VF6P_[Cl'j( v(SHLdOM<.|Tb7KS F>i˻cvgCoUexи4> ǽ' ppS4?O0َ3K3 Uj[>A_)vt fڼZmg;S9,eekVm2wo{ng~Xf0iSt_ovߜϟؿ8MO?W 56WQ}^q;_]OeXo&Չp߯Ot_{{/B|/Syn?ȳYl=̗u!d\7T |Jh8 Fm~qtU^fv]i|r^17٩sE47WC~^<&`p8 й/sa_@S-p0׏4jLB~|`U4}XiZo[#ݦ2 od.̷'$CQ$e >oUW?ؿo?x6Wd&{l}I϶A>~KFtOC;nwO~~d|)ˋ^g_} |IsX#mC||%-P{t*j~O}o?'7]weY*b ;`h ޲~b^؜1=\c^.fۣ$pߝWp3pxL0v`wt ?9' @:`o& xq v~`}b9e55Bfq/?+C;Us(XsO,o^~L@ly:g@㰚4mqjM?'vq{6l#}`ǔPh~QqYm/lq9~f~Ϸ ̑N7boF_~fOUPWo:`೩ol72)ݴ u vZ(a;vI4J {1[Bm}Zth @ol4Xzc=O*^X;}uV'jT?!mx<+VOv ~h~ Njy;\k~+!O漤 Hb~Jelom:/},>Cg6? mdr"I&׭z <2_y{o0-L jHlqvbyyj;\)_=䃗)ڀp1bPC~{szԴ>؍3Y@L $]vW#sm= &'/8dq$~|h@vp#_Q?? ?}p :`fs4bOT^&Q'?ىnڍ'?E]c4g`,sc7_O]:Oeհ5{"b0=1uU]UW_k{Y>9']j/=2yR!y 2W?~f[Ĵw4ysfο(2GʬE=E-X|J -׵`!fMu8Nim,]m5Ck&~̷Z? fx ~+sZ|] Tl/Uu ܦ)\!.̌V@\d Jl5al6d|<(K/x6w+6QaVx gc)xjO=q۫W8}Y*f}Ͷ?>}<< /cJ[o,xxOCHd~oNؼ5}pYm^ h\@@S0w?;K|c ӡ2nzYSl>]p>ċVW@n_yF>x@CZ/`]h,iy2&`<7^K1XhLH"@ȴʰ 4A࿮=< Io<>>hhwxxN. Z#|ͱ+tZ?*߳a>>y>:v{ qw;i$ug>֟?j#^Kl<ֿ ;?/:7^)Ӗ[Q1i5: S.uKv|&׎b~<WE{~mq_Y1}Ӡͯ^_ |f{ #>f1/.zVr:O6f5cwf~@ *on 7f`{VQb[̦K[5ר?>Zs1^t=#5˾BAqn}_o5Vζ%IsWD~&iQ xIsF'w,v4_ユmNk-&cv }fu v_=jew`Iv?dzύ;hٯb`6pŊAKcb~WsL?Q~ɦ7vc};l.%L3br=x}Jy.^4߿4KJq=ÇO/3_J1*g߾f_n߿ >OXKj UMXq?ָ+MfrǺСqs_l{^Wqqc]X|#eo!?o?t&ߎac D@0WL'|B:`(/qb>~QdMD -xB٦K|pznPIoߠn7cG2st/@v|NdX lfK~28eLm{7Gj y&7= ǜnkܛ5Ko}Asc-g7${%_$e3|V_ǘ`?،~c?J눏Nlw1{6V[lۇ|d^zL-oc}˗4h&k:)==g陨zP)f=XǬ*Ĝ2h^%`XNØ~ ] |Q] &8jcUY|}&CG]ڭC㒪)!Zk?+K^h,qg*T '/`4 P`>)P/}/8@yq^+7@%cE`[?YVX(S س-f_dٿ>b:\GE벪bZs|$߯'J6~Ilma_3EcogU1 㟲幘WvSKI )WJj>v{f7gw#gK?28}KT4e-r[u-2ZH Gפd=G}}LOSP8Tku20w_j XǑJ@jkׇJ n3 ۘ}_;XI_i`M d/@>zNxB*F)ƙ$EvIZ'k=Gcj=vqoB3۟4{w>Sv_=Kc|_翱1O;+1X 㟲]=O}VOi3??}}?Y~p/xܪKZU 478ޞlK߸U2:  (H5'=Ƶ(O ~'p7S c E rmJ&䶬6S`:Ƕ^A Pa:.}јz/_cy>g[J'Kzor%/߾CpzC؎a٘N T6JR]' Wge>2]8o/ַms *eF;l-GK#8vP .74]V`[Ug-'0tU_1N 4@VyiGQm8?s4YQGg=xi< k`PCk[:  8~Ov[6aiVMd5WOmx4`2 ?*ṿ8C^MS''v|7ߟfx7oh03MT۝(p5Q^IA[!;&?kg34AQ|q3W(zVWLX4 L4@X > x.-08.}?S Ko=6M/8߰9_:`IMyԷ9F>zO~1¶?{aq_g>fݜV? w׺6?_ѷljl%j [~~9H{t^6{>c?ξWk;r>N'Vh:zgm{^T K`xXлZUNQn`~A;c[P~# `ku)殓DyZh?ކjI]JdX 1ދN؀S5v^o{ylHH,O}fWLQ!LxlM6vYG-pє` ~aLOYk#Tv/a9lm v}ky> %."˳}n?hx?_5@]Jc!7hS\Ē?MEO?̯}}qjƦ|Ak^?/W?hc[Q\P+CU]L |'U;E{5x?xj~iL"}_Qu.JE-%Z jFC{[Ni[Y&zߥ'CESy$5NH(.k̗k=w P2AR^Luff0ev -Hz)bjn0aC>-O^FNl7E~d[ jsɤ$|ُn+^c pߔ_!E wb?~43Ih;bgyEuuؼ`u@;8'Z_6 y|ޯ>_ڻF ko 4ИAy6qۣ(]75s.(ٔ耩 ׶60%oMgx'u4 ?@8i gҳiN"iq.RZ@5k%ڔ_?Ns[S _l_M ~j?|\_gd~ol$3w`.5F Z,|ݹcǖwm K]S?oSv.֗qx|~ {ݓ튶c=t?|B6IyĊ'gTTr1~U_P  ٙ4?wqW%/鋸nS4*O~C4]M5/ñPjX.iyj/״,[7|hYsy}ܟ7uϗNTf2F .e:kSL1Eq_77fb2mlwoloZ2v:(hG?/oe[l>o?ݽz}1[_ggs§ܷ_ٓ g{O+54mr_?b j'_]&pl}s uϗu}?f;耯YHA#J*c3 HH4u)`/gZ6_}k$? t@S @ x890k{GjEprqȻG`wxw߶P$oW}BISjX/?뫸8²ؿv~}a~Q{m'p3gx-ŵz+y |o`1_ j;phdL`y@-ikKYEK'1rZx @.ǟMwl~J[ڋӔ{qdma`7y(|cxb=qO~+o*|M1VP-ZK q]VAE~ ٳs7~]|n'xx=zxsܷB?'a[b-g1w{~g}?+5+9:eIJ~/ԍOD_9{7k%թu崺o)st@mIyFA:xO@X@`;4D=p`W7Fby]IOqx J-S>?WǍk3Ŵ}fe-ed?g_|?l/Ytw2 ޏ<=7of ٯ<¬C'ϏW?'{ޱ+~~cZcU~[tDۧbl +K&~{彁;Kh=Σ'ϵ/kŖt E,k_0ߵsAcl6_P2v:Ch^mdtX 0_u_'!a45< `_<+|X?xOb]:Uj { ɸ8k~Wdiw~hx}b %)9dn[:I|KH9vPVlJ@ԿVUS?[쑖 ޺:gK-g;Ѵ4!{P)\{c+/-UuRn/\JyQVJ/u/e/˥\z/z)rQM6mT?WZoտn/iUnյ84=xܛ^ߏG)K+_V>?}饖ۭ@6*5 -So^xn߽kڛc{[l˥RnҥʍzoѿK?F[REhj l]uYO~ؓ~ӱk bڴūr뺼댻鄭..GoRl*Vk׭ulAЯP>\b^zI  28>z _6?y9PK]zy\M1!EK~cT9P'<@>2~<ݼt/l"w.+-\Zw'&bl~ m4Vg~~KnRAւ>{SN[lX)O}(X:W3A nߠSlm8JM,z7j@Y0/?;` /co_7^v꠷(AoP uYۙEӱ*k. _r_8ny?`3e&'29Kiz8>8b3W\[_ƺK3?r~:; +('p yR]xbKp}=8O[ccWUh)ǣE6~smӶ U} ۿowxc)ݑXtjg }v+1D؟m~RPׯ61܏qTE/T:CGNhg@Î1Ϗi8G07_3&O`㤋Ob٫(s+̎AmFy宾4 W)w{c;Þ܎Y(҂vն>M@8dB~E2iu[ml O~G֥!A>/_|#>"wnb c{m`>b qx;qd~5g0Mm~?z;Onkiho5D.O1UιCж[>?~1f-ǰqa  _]rNp \ߊ:< 0bfZ3~,O$ rK{at국?S9p;]?pw?rۂx.~S<`ew}`'ݑ1?|odsJWB !YIl| .&~o}-q3V؏aWϟ~W??oFq$_f˿UɧocxƱ8N%kiW SJpljE6;8n0tbÉYޢ yAV6?|:Ʊ=s֯~ˬc pcoK3L¸ _⍉c8 qW28@5_C|TP<yH^|$S(d[0׸67pWn0Λ< ykj 5O:H_"v1[ro__c^d9,qxn/dz}7_Vlj瀐gca9o#9?W|rMӇ굲OL(F=ws@'f7Kmxئ !y~y~U %/` `ןG/hW_<ۿl8GM; pEx6hlldܟ{.oD)'Ɛ!~UlҏqN[ɖ8w#+_昿߿B_/fSο/bqe|oSy6WƱc<0?cRO~/2^7WBm~}*s`?JbQxvw9p&nG@MFӘ4m~}ᛞ9̗ f 49 `?F|cXr\!~1@>@9O7Meֳ#3b}ll |!_s?#&@X7 <70Wkxރȅ:_\Z/ pV~7C ye/h;FVWzh_K.eϊz;Oo{i4&^agwY[?r̿?~v?a~QE__l;XN^)lK,O]?K+c\W뗵{>>*>6edܒd .9R'.8c&=!fĪDzc'yq ,`[8=~*kN8B{kKݼ҈9"bcaǷb9>$&8 *|Vi kӺft$jDFМ??&0b@),|g[pwE ?|mѥn?Nts_q?A !߱ss/*ku%/;~2.[ MF[Ӳo꺬MC<־ ί?|毶v垞-ms WA v}=-ŠaP CpX1x!{8v7/{R͞_k̦!c1ns{23ųg%΀tco/krd}M9KMmð)a5۟p?UG^qMu,M19Ҫ`z@1._1Yncs:XOq~~Q-; + 7| _?fyX5$+Mp5O`W\@XȌ2}ۦVG}n1b>\;.w!ot y4/|MSQoqa''`|'?jQ?Vy"Cc)N1U u%oWs<r׵{50 0_l"B3gi\WmEa<{3s@~ݦڱkV6>k5szwʕKl+7m l&RAu?x-أȚO8TaWrꮶ+ḉÍMnT8Q9d N~W<08V RFIK"m c@_]V/i뢁1RMС4B,spԗ'`qGWeM˗a˿~}~/?173}O/ȱ߰{/?5rd:6?ZkYc?} 8@g9@^O!sa%_r{`ڽ1l,H1nfRf̏Y#o ~\q1׳=a|[~Sz߹!eu5_8Tsu/}Dzq8 RsڱB~[,]jѸ&rdeV;[,r+ rwzC/u;v /(-n@r2 㜤4}9FAYW.q1O$@w s&0~~W6EW:n+o~Ocs'<?`?ʚ7ů](+`? x=W-`5Qۿ<~j:nzGsmWq<'#y8-.F}XK~sA;_HߓJrx~_qc71߱_1]^q a^_.ඖMx? 1JUY0Q_<>,Z&4z%,<[x}z'췺rO5:xEmSXLRm}q2JԣؚA#/@#p3(_зaHym %_ 8ad>&dny3(yNp6qId$,powWl2Z/Iǂ!uϒgF| ˵\uԺQoLjfUn*S ^^ĦI#Wg̃ $[\??eߧ̱w~/bܯllb?|Q kma9<Z|{qDl/_۔}/?=pvw?2|bb@ m8>G-_-Oe?6 7+h\ޏhۍqU:@@׻p~7+r/`^OAb qR^o ƶprpxNX]^a@[sY_YBABYB;-) rs퍐_E1u=coXu[k=Ҏy[}c=<`|МZ#~pۉAFlѱ-3+Ă2ch_|oi'2#F:E pl'_svS?fZl~P<\\XoSǥi>O ,W{0y}_~͏ky?6ڌn.@s1c˯{Q^{qvW/Uc,Х}u;c  пQocSYLla n?7WWٖ/w[М/4 +$ {qyW(ˑ.t>#7S =?r: _sa6zE}?7zs#/&9Ic"8<_9@8O&E Iq G02n pʄ#?_C&KYQΜͱ^*O/oNJjsrtuv?aom5wf,ŹEV@8"e͕.ul*+m?uֹ:j|U8$ŋz4ED6b7 `8vnPWe۰[s)G'xp/*]ܫhC(-z/d,h;{F{H9bug  <0r:hϔPn"}O5~|J? }|ʵP]S= `_s8.#s6[4[`,b pItԹ {O?oƒ7ݷˇ KA; !+a'`M1_\u<Wy]7 qx}6b~ cu{c<7 }Vtmww ^2?Z÷ze>"}Gg>^(l%̯ 3t)?'`hy?c7nVnɕ&$q\,t_0Jc K<}.Mh8-϶|Y>/os_/!xx]\ZOes,yG Kۀ g9:<1G6f1qqyBb_hBףL eN:|/>)kaʅ/|_%ggjv\bj9s\u 96k1W/O?G/G X@6@WW s_ T]_K8bP_ x "W>8?;朵@܀}nq6obX<aS'=tE vIq.up.e1]W7K5x?=Ϥ.rA}[{>?žu%9\o0 1׀w=xwd[:kd7ؿb4cՐ|fגD p % <*#_~}|`p:Py@y-lTb)<_}VWMuLHw~\l`eCs =FrO. p@8?yVY_gT_/89ӟAv mr6]X^p+p,-xX?si.%NZ CLq&k5cr[>H @?a͘c ?bd滮qX8XϺ BEEOcb_w}<:1''ܧRh]/k|\?.ϸsmJ~3?V }K^6ǩg?kn,Շlۄ "#6)A @\? aY,܂ܭϐ ˔k1sȍs=%#Bv(PǺ_˪8_>8vgyR@۷S~QCO؂@y'a0h5-2s2 h`?r2r?| :}ƗôkJ>G?V?}alt⎧:"qglWY7kuNsh8u+tN_0Kx~VKXN ^#'J^ g/}pF3z`p?C ?)uN+C&o>@߮c|Q b1W,35qg`tzx3`.9v'_ˤţFWq_3p_oEm/Eŋ.4^+mN8*+Ӽ?] ; ?.yi<p_q̶ ~f$ߌd{;W Wexd(b_iInC `뻱c#h`&yٵ(a\( <`„?gy؄'3vV#<@ Oհ?s X_IB0]eL'9@S? p1[2}smp^o8@^S<'as ʉo:AA,@:P_XeM졌@ : UZd_?~>5b4:R$=2~|1*{gf~*Wmbkd:+L-[da[qhór.J.;3w{Y@/cex}c+'Pg[3ƈ]chؑ%c>>=c>ǸsK8Fy&/%"! 2yoԧR@8_9 cQO}]I>xdO+|E+װ" >~6s !sBySmscW7ywK^v}zb9Y[A|nܛD9@'nZ[@΅2P%ʜ:r3Yc3_ ?5P?68_p0M,֟>|%?8z9 _xK?\sOc&pm,G(9vKe{Lqy/kwۣGk0VD=YÅ#D8GF%0[,Q7ȅ6q=S8btPmS!/gu[ {vO h/e쯲%>Sp lZv d1jcx+~+~7دpd a[ K^?.\?ca M_~Ku)hqΕ/vY{:o~dbQOy42Q?l>f| ?19'>0~ /sguT tе;x*[S(!3^D@zWhǜX%\J1AO<`߻v6yt[w{/vu^MWi-ч>[KNDs^=`A>=9bwx};] M#suUt ̧kS6 QErjY&-_?o-_ɶ) ޾rw)\g,C+n/1i8n2-Wo,x\y{3;!x|gJazFs߳hr̺SƏV qy y qs.\F/=,cYYcX%Zg{\ꎿIZI-1kR)#8.Anxx`O3DŽ1 zp^kˀRQJ{qyz[ss wWlwg8_>Rٞ/yk-isx^k+?7dMjez/>4B:\\2Lg22e#aA99$؝l.(#x Eϥ:be-%=&jS{~J?o|_WT.4y(=p;'w3 #3AtnY$<+{+u_3ɿTΣuޑK⑱zXW*,tB|9!t{L}߱|\,XX2,m@iHE-O7$ٞ;3ǃm~~-64Cx+TԳ*ᅰ;قz4a.*\cecΕrA:u1+WJF\Uv?L]A@y˸[|q!O'bbN?A # ',ӨOebODlrU<ʅ?}Ja#Sf~:pIq?9zCebu[0?kܯ2pUj^s1+>V[!?|dp?}zO?] ^:잫~D blè?)Y]|ڱ,{}2a^53#''3OoP+D:ڼo[>[oɂ״5,bO=pJ5w@oEy7jFxSK2ޜ0^y@ ޔ|@X0a"cg1Q%g |B/;5%V`{OZ\ӯ b?v[@^۶qv'Ƽ{>o 9į(>=NC_7{ߜumw?bի|w?BW?-l9o?QT}.yFsd})7 ;7?B؏8N~eoط'++4;2ͿPQxL .^|Y5a^TZSOjB.Ey0{Jw u>`66BFw3Z1v zgFU>~~=m[ ğJS#}>BG>X܉ տ5P)q,b7Yb< a5jnm,o½~ ¥ɟV`^yϡӖ}x0=ݹ`DN=`Rp?m[]\_m-k=} 8^/PQ?~ s{_C߰#_OR;Yn _׶ؼ+ۿ`?=?zl~ܯcuh-WLه{]vr:ھB>0@ؿPoaWσ-бA<'3(dO QmwGM_`+qXRȚW >u/h9XY0yb1V|pƣN^yC0W~Hlo8n6ncCXge9@mԏ7@B#Cg$Y}/+X9$U7K} r!8Ay?\pYϰ~ڒOﯾ~|!Mν獯ฝ緱W}7}Q ~W`FN?b_/vد6oK^#ou oo|l~}T\|//'[Fb9=ǭunvL}7zoܿS]Up)Glg,k%4`p429@Q۝RkUTܼ9.irַ|~rǂ}I8 5&(wPO1>r" {$7~|΃#ClPNv?#0pXg5X=kc7uyr\F墳5}9@@Y8X.@19@܏WS(ߵدP^tXsE@뷎m1&繋&[;Gs]Oht,9OzXcmSL2VUK/!m'~'e,ԭz;=QOӕf #~GA)rhMq~|Զ )l+V,ڗ˙  ?C:DžCy22R.Ir \}ä0Nܩ^kc)k kXGK Vrt yE|?}p^KöLm̌9G|?ǥbkeO`:u(3O@ہb `^-q풶]V\0heW(+y{K x:/%8 (jcW,tK ~ZFЭm\?s?? Uܾf֜?O?1=߱2O^hAvO%^=MZݴ|#^录F%~_&QOh"gp]_ ͪO/}-7P&Z>[2PMCe+lw rtj9ox}1X~=/糕EE=a6}AVŵCLn8Ae2f4_I%YLmq5Xt|刟Sm- $?2)\<ک#ָ:FN+ܝspz}'w u?C'cjs9~Zk*\=X+q(A9 m0bQnZc2&``]ת꿍嫘Y*_~e~>Yq]G h\<u.jN~6۽&h8?])./Ôٰ~o~;c7ՠWGu 5oXc[:e(Pl4O%sT_%1@ Of lLVߢϹDG;5 cv?<Mk|Ůw;^poDozx\}_* f&feGfL^I9@>YkYc7i~4gguG%\ _iχﯸ]Цt.r cL ۹gTZ7֌tr{rP< r#roV dliόΧ_ks?/=:~Trvr 9p mf{<X[;ȼ1uxC'cZPXpw@+-\e{OjMs˯5r< 8~WΠ5xu&i7;~,5?,FAP85P/ ѽQ>rR?9QOMI3eG|@f.8/">7]anUmUL꾘 =7q -}Dn`#*= >Č}x^  {y~3]X แ%Q_/Y(/ i/c8eؿ7nc'~V̗YIm}U4x|+I$gIpf]y, sr\Ϡy(L11怸r@|9>Mx0WR[FT >& (~6]9Z3u`x WP_<x@Ú/ Yı~ 5Gy:j8fWc I&Ԓq2nvBe]?Colb?n'$/:6/ 9s\?/?~xcF nb(+a )'9 brEH`~z m|Q=T |>%x>p"!^U }jWm?=@Œx Kol`ʎ 2:v\å|UN؏2~~M~q*9ƲOrB_N 9FDMN`ҹK@,.c^_'x!1vb^q`UسՆZOh0?ϟ6d>^zׅˈNq?_}MO9~Lx(:@2zހDFWfz2Nyxr%dcy Fz@yCQ7y>HײiZ#]KX{͏ s=10xBeg0M%Nf83<)gϱ:sdNҢrX%&YOo[L2fRs`z5GmY>9ĭ5ඖ<urs58@c_oZf##?^zJAth^V/qј<`n<;d qjܯ7cVNv~2?;z 1=bQ@7fwxp*:zhI(Q!1KW~ģ4"l.2Xw|ETIuh=hm܏ml?|qxAz 6rcI{}_+iv,n]'4)QykDeQsoxb[T9G10pUeKNB/匟Ox^f~)C6[~Z&0kƹHFݺ,~L ɟ/>~h~JOs\[_b2ßdo&ob&wEV;!ttTAEGiP.1{`$Џu꺋}Xt^#?r.Wp8bY%2ǿ~\n5m1Y^B쐾ٿ&>zhSM#SYbl˺c8Pq?y#8S||5~wW_̱/[GA-?|Ku=AF/_G-,op]~hꨱjn'>Fg\wt(I7|w>d\=4ߡ91ihϧ`ap{w\o&'sӰ{ćfه}nAw\roڹ:9<X2H|xtOx|ldmRb4?q߉w9  ֭:Qm-Wgb_/~ƏS/6z^ƍ~ݭo铞upQG<(ϰ_1OkF_gP]_؀d[2fQ68bJ\g{!{S`eVJo}>(qEv;tz}[py{9yHq!k~@~/B.4hb cFt6ޭݻ r}  `G?~>6rG9._|A>;m7X X!!Q<09#lao}s{f/cލy8@cw]['[kr6ظ6)'cp/<6&ı5ľ¢/s{;sZ]YG|=۟Re_B? O\{8h8Mtmp[_c3:dC1~uʚԃ"WSs#9|`zQ8W a }$_h#x2×/{GxyFܣ~ư኿ټ+\M~6 ܧ<~}}σ)>ۢn?•kǎĭZ)\ w/W1jzo$PpxjTj3|*<EƢ? cS ZMEK^q~`=+/Ӽ /vEml4fK\̾F7͵Qk趿uɃqǙ8v2>v􁋵u a(u| f}%|0tS?v'to7sEғ:S~l[Ui~_nG`dSwndUE}Uzp7x8?+_ha!O &#K-O]sӁJN !?"iJ2l>G_O{ho;s[ _ӱ8?_iDÝ.l耟Tb=s!qYj{>ks+9>CՑӬ;a/J?lnOpqs89@^y6Z~y% ]o7eW5/EQ^:C x q2uzyVby =Ȃ~Q l;WinU?Wmϫsg~nkp#yZV6aPk'"Wc 1L[廈}LQ)oerW[6glp[OD%̈́< *|\ЎF|14^ cn}xfű2OTη ?)`*Z8Ɵ ~R_sߏ'\ur/15GWt3leiňG)rk1#KiN1 8DU}DWOMב*1{vֵ i }O#/&!X^Kv?Zg%9|vr9@B0!3l;ئ'O'ю~x:aGlr͒' r䨧j傕l5iggO'{ QCaG%_7?rq>W`|Nr5'P;n ?@ynPo;>q.߳k?[ 'Mu3ԜYc!oosq>p_\7}VX$\PY{1~m[X?q $Dc0rtK3b@mp\pwpDrqAhDe4aМm4֭֝IaK e_lv]@g ߧyh!RL_!x9/\_|9F?mП*+({ p*< >>!쑫=<=c[>0`؟A ~:&ϸa r#,2[@ydnQ 8 Ծqx۞/\-w'Q.8e/ \{j[}r jN~|WV*o+Ob_?y^a~0/`}2. j+,ͱ C/u}c;~)[quo9@߂ཱྀ2=!g۟Ж,GFţGآ///h+ `;o'WJ׋d|>n큨J$%^N~7}E,qۼ!\%k 6+/{yN6Gh;ϲ&m}KBAJ~7AќL 46:? rC>) ֶg2#7cPY6sঝ>5z1Rb yE)__2߿-|!;clm]$?D )9Έϰ| ? 9`'%s]ma+Rij:%pD$yl칀 i^k u4ܽ畞nusx{q(oBkXR ;yC6p[{orϔ*u|~oM8چ~vslxvGh*x>;k|Q!$9c/~$W-Pgm~UǕ^7!&'P!<r72 m~ǂeT& Ayɱ!^Ny?~d_u?/UDa CdǾc?6WVX[!x?ɚ-͵_.wH "O|_.\)Z51Tыĝx * 9I)*_f-6 ky6]NWoe,%&q!GY H^[@SnjO`s9-t=nt.oN_`y!!0^Ў~v~q0b B7|:קJc\Z>KK6@䎹v}`5Ϧ`bk< ϮQ[IVMGs뵁9ojp&˶~kl}7s90$onUkr2/_,_#}6n?w:6 O96qR]aېEF {Oa*_%O|h^#lbOl b_v`n 2aH1 iN&n{}J}n7Rc_s5!A%[ӦR_p2)RZOSt5iE7 _򉱞p /N,gjG `q]}RXvƫ<иW9`>=Z٭0ЇMnc̠j?0!rHL n3 Sv1+p?׏ȱqQ$Up+F/@FeH`DzyAٖٚ*|||W=@yxϩH,]0[SwA~V3GH&@mc{m Dv_q+ǚ6**Z[5quUw뫃Po͵;rH^_8@'\3\xarTXeY֪ &y<g="'|xauk,l  k Nob=lTBW5q`?4f$E'D<9m'$vP%Un6HqXq 0\|A 6TGksro?^=`?TE0@ @8+sǹl<`ypwЍ֯?\U(Nxc4o?cl;gh[ܽŷ!ո>_S_?`2t^J+(W{V@g60rq_ D be;ѱ~*eN$k)[{6E9ej7 h,O/;}_ O{6qjIx:P`̧5Gw< 9NP>cyo>k00Kt;^d.P6g[WUmV y78V.{4{zkz6`[ Л P~#v˘?<Ioyxgn˷:h#o/`\t|҇ _}qfgXAe=ZF[!9Ӗ4G[3_Ͽla%?lgBz~Ip<ԆX 5>Hq7659|B[Tcz.-Ov'^_a%ks^Iz1+3NO KNĺUޮKX巊_@eq%.UShV&ߦM[m}kE&si 9xd(b3~Hsg! {%9̅9BE47 GxG?!b y qZ/@_֖X]?./iaѡQ~<Esy@%>_E!Pl9=2xо]09@u'Hͺpu>?CI}L|咿Z'9AXcOu՘?Z㯌vm6+g\j9=`=qSWɩ5,rxomvZa)>Q;( e1c?i߷؏a;`#}A|$y'ݗ';hA E5q"7oEg??rʼjj=!ϼJ1|L iKXnȷ*Eb39nU1еMR>yߧU'99*Bᾫ><u3r/Qzy&~TCjeH|?Hx;x >g|~'ðCA']R+`aD}_$~ևw-p>>kqy3NE*9=z>rx}g㸲^TV0igoݾgg'uJH (T}lrBoo}'y/_߲\:߽n@;|o+@36Xa6Nv젫isT]ϒn{/}7oAڗqu1*vx` ppɋ㕃 d`1إӺpɥmw^tm~}wdö`ݵm j <ϱ/O+cf09t>X67} N O {W?jVa9Gkk *nfA]*t2oyvI' vӍF({8?8;+eFY}XOSeg̿5u26ns;h=^e8}FP#CsqF8f g4qLv;i3ㄿXy+  ZM]Ww:*X*>,,ӨXQ'Nd~2?ぎs-)KoͲ^V;(?B9j ǖ1"r]F9Hs6-ȁr>><S2f ǁ]TϝaC̾M{lPGjswU/m=nc܆6 F:>d:O3 gbDwxb?Hzm1]z8z%w@XCLA}Pu O[~v>M=.*Tj#'-<yX`:C}?a1c7oDX1I o =0_m%؏幃; kX-+[(Gx]gR?㸲z NXN 9ax;V8-̾̾3ve\e?pG8r)X/rOWe|(^k^n^wNmzԗ+kǶ@QnGk.~ ϰ?;&ȫS{<"3dԄh.*6mƝDTəH؞p3rRb?n2y O8 ,Q_"mSԕgh{N>Av8 uߌ!gQgm4)}(Xl'7Ʊ?1QkP\Pm.>Iǝxppt$OrsO '+o~nk?l7؟hwH4ut[ {|;!htcԕȫp{ nc#ŐdoKT}b'ȉ-%ꥇ?];X"86rH}e ϴ@?F{8`?vE8 9e99Gw35ߌupZU ?@9H/|}GG>r`MU6>YK_Z*?bnnsFޫ?8m O`ܿ?3b8H /`3`U~|۟_m.J?zlgț+1o؂2,=93sG;p#>DՆwʱ?o:-~q so=w'7g5x 4G. ucDcF~4E}Q;/lG okxySWI69N*tNm'}m.!t*M!^ܧX-ϙ8n]w;vgnY?$'۟6cֳ خw\ۙo7s5>cs ;{Zoa|9#A# P_ZyogFdP'=9p?YaK} X b m ͚899^`若5J|9_%an%l =r<=h 9r6&*Q: o+CiG>+\BcCm3 8$s'#@țb՛.}*Wqj~.΢Oz& 1.Krp,!$߮nsK^" s P?-+`is3Oe8E;Ğ5ˎ_Q!'?g_zl_{&l~w#?ah w-<"/ΰ(8=ǍROXqQ׉=OvRXO:r?%",^*xV4ۊ\j$?dkO$=ƽ:v']6w?Gܿө}s{|Eoݮ-⃼wgN~c: G^`wXok<7ÍuCDmzGBN @AE߀}q4ʧ1@0Arf8n=\ xpp| 8Rıy2@;Q sw96ҸfYCjkoKA~1OWG; s >@ĭJ<P\@예1vazCZul Yv2Vhm6 yUy%- 7?^(kg jޟGY"?d6JOT`1].Z+v o/:r^ğyV?̰_m[0cctw+#bBFAr,bh WG#l܁ sg~֓PH]QgNNkjؾV@+Вo1)p4޽hWQw ؎~,${/+oel㛦e|bybCH⍻g~@dz"i[ld)ok qJC@>6!mLu1v!s+⁄# ',mDsޟpqQg]>/s;Fip`*!X\;F'w\6\c[++cTs_v,׈ .LEҊJOe--LjΏXsO81B>~.$@hnl?Cq>,/9[oDoqׂ6 u8`}y*slZzs{lsg|'yWO?7&ogm* p~_z{}E% =>L7~˪ ~_GG*WXob5tp4b'}O~`ު{d*[/OWq(}^^*XSfb;~4Nu +`Mv˄߿ܾYP;g5`2v>mG'lvh#`gH /hxo=YlKܟ p7ȧ%&/@9@'Hg9#,G˨E_ YT< G^_[cNm sb*HZW?;킌hH.X x||e$%?27~0cx@;syI8 )@rK@Qev8{!k/20s!8@?C01,cRݼ1?@3OҜ?ߛ?<[ou{{ȀW `jw9|i~ꀛ`Yͤ\lڸYiGjtYd/s??cSvc_:MbY) 6<ei\ʱ;~QwX@\Ze w?{\%Ղ(OӜ?'9Ŝ>\;'<>,baAyІ:h $?~h%c\.1z2:Hnk bSy)r`ۍׂ ~nsfdn N^Q zDG[nG}h>GC~?5x4pz?+IN2zd旸OͦE|oh" }Ay u[_}`F;uaψnþ ԪtVSJ,wj oѶ[#X"\7&PIV6ʗŃͅ16F4{n1<.Ҏ7mߢX8X5kۓ)rsUCus-+/?yw8@C|q+q'a;/5Se >Se %+@\r 01 O[hc9s ˹9O})A4R:ܿN|\m3*,N8>[(ß,Sr7c|?r}"jvܩ"Ϭ\W<?!o~נ2]8)cQX'Hr!G؛! Ooj_~NavA#Y*FQG{iN?z\]vc S89mR~}_h_otq|ۇu8rׄr~n=+]ttR+6yFDŽ^@1f>@b8s,O8[ 1ڨLogկ1}4>Tgȥowpû;g fƐ|$U_J`ˏХ%;TҪhoG#g!hߡ "Nk@򭵬3CY്𼎆C@yzü@ n5Kj1?ioka%@Kc\ ߚC>+e|mKR b2+z.8[zCy>WDaZpzMQΟWs=nyZxNg-mҟT!]~0kK?%)??QON*:04r}=qg۩ħZx!#&ޞxx<ㆸO}й Rv'M#uM@U@}:]s:΋Ǎgm;O&pq1E4A>L0Б<:dS7~}_⽽Žo:r9Zʛ֌hʉ_'r(S/kH[(dc#D EHܯM&;{,j80%9X{ڠ6l~eRcz*h|ͬjތP683T3C X! *^G_?[PE[#;xxD4hNF6J`0 O۳)N(~縟@s^8&rm7.罼<.n#nm\cIwv?>3!/yBSQ5#_1>{]~\yʸF)_qRaD]>So٣h۔/A7C~6|+x^xjpRn(Gmj*Q`T/פK=A /u4@2_@})=^m >|8'h%-7 5I>qѡ&yguf#cPb![OC<\!ʦO;.)(r9yrc1܌U˯ z@9-"1r稘^J9 ߟL6(M*l=8r0? %?2ϯyX~_JXT% \c\آ9x\3Aq T2e~O_\ײV&А!<ݿ<d^;s@y풇g?-'iMMv?Bd}6$Sy͝1h9Z?#U]F>mN@iE @1*v?r6KljߞzysZKN7,+G||sqoTp}1ո;7e"=L:h7 엺x ^r ./[N`vz?6ʌGp_| #9Z.Ǘy-/' ;@е >ieo}I;B9)텇K6c~ϱv@x˯| 1>,̹6'˛[ 1֏2" <:}'Nȗ4b_s*Աꤧ)@O9:x|P.rKyfe.@1&`**:(D(yyF^mv:W[ld>[|< ks}%O_G9g|]z8uc߿elK|1~࿹ D*|E g=#CXeeq}.zsԃ_>l)`+0x$yД!n\~=[mIN1m}r1և: ~D_|OC?C"*x.Cq7qz[@ܯ)GsT'z'Ir7/;KyoE[wk#[;^Ƈ|j]~Wy zv13yTc ۑKHn|/ BOʝ:z~iޣG M2x#y@v |M % y1rփ .*8q'91篵 V{ m>ǔ<`\8&j$?ԏO5#z)SX tWN˟aܿ'cP3>J><PZH 鎴<P<b`3qTrb?:wG `xvܾ232>KyNO9s Zȶ 2~SxS5x nO;C:?]LC!18VFwčP'_Cy7u1 [mCub5M6tw@sibmK| Q/tGu ZϹ>/ }f)bOO>%&6NؼNU5ǣzVaT Y3Ӷǭ۠ Ea ZI<Ƕ\cC\+>ulY<؟y%béۿx>*e1"e6-t7 |»))r wAߏ͟P xG"ac.Q.̏FmY>>g+8$//;@~'\9ok@*6rl|-: |@\@ p42khq'l^+`1^e%kY `uM]⿮B-O~˵!y9[_bo1\Tr{0zYm_:ibv\fv@ǹϟ|bɻNk55C_# b :ńC n\1L'a+y:%#j`YGkpfNi ODVاjkqT]h/=@ KP=Y#C)%ͨo]黭Ԫ[IJdoEM%#9;3\:_\󹱿 b&p \~eW$oaVQP8x_kM{#ŕpQ|qY_u KWܫ |f'3gKr9+r Ѐl!SYĹ>rY5TK?֎{|;%wwU-Z@%<9%OU=4:>`]G|&__vd>B;C6q\kȖ_( 敽n-K^"R]0syu3Z_VMFqiGEr-ѷt͊x{eb=7~rv7njo~u( @] oh(Etk[ Pߠ?&O뿰^)pǺv.^:HMCP!8Ocxr1BeU $&ioOx+%F_8:R:69~q|8Sj J*yՔy&$T֘K/9|>4bj;<\\Y`kn sHOoM`yZZ@I3gHz׵f&@@Aɾ̰9vN¿8Л,{lr=g1?{w7:Z!CGfj\uzֶ|ҎQbltr?#U0n*`3ԇl\O<犹Tv4(ԻD_n(/0?χh9BH7q GXH=Gn*LJ>Ȩb&}=՞H CX3v?J?vz+Ź=:~rgV}8.Vw󁰖մ:~wFU26  y Rn~\{XNW!GPRo.ĬfFؾޮ/-\=᪛czŜk tws9A;{z5۸ݯ~X@I9毯-hi%64?BX숲e|#X\->PwP<YSSN@~.=co~wgHȺ]e=`tZ@} HGx?VL ?bVX/]Q/ $fa C>x<  +?n_JU]̙~eeߩݯ6b?]'̫#ݹArz8ou\q6bmZ [820|_#ϝ~z ib-ױ7uW8rd.\IhN+s M wxx¶j&PLZ[7aOA``w#`'d| s?p9Q1Q3sJuN#MOڒg#smjG[?_˪|*S=״6dI5%V&cϱ1UhAkS> ЏP׹*t}`iOczߞ N$s!/*z˂ݲI@ bݔ _Y|oӢc.~ĉg?>Ph~,mUCk@ $_jxlߪW"y)WȮl]@m~d>yox[9knϜw>7辬c)ONؿR)|SkCķ^hgT}L뻀C`s.?a٘vn<xMp%ɷbw1sMנuUk&`e{Xgӻ'A ꁡk{=:G1[|X;ʚ?61{ȇn~=A?b.!R۠y3kŸk.ɓ^_ޣ7=~PR Yr8j-bӅU-E;"? X{/3l˟]Rmu=fR."TDze> P@qTg<x&|Fʿr 䳣|\}#u֛<\glL9GNZc1owҸLV Wﷰg/u.쾠>F<dLP(Լu mK#woQ~ķ5G |;w]Ӛɯ-r5ltn9IwG+O؅|&uW0ض>~ɿSgnU`9sRϭ2>9^P]i8^EPa aQ\~ ~ce'4x:cNjij= >s+<k9&Aӆk5c$N2*A>/8gy_5!ka?دO0/}?fdwmF,olPbOi9#6j+f9~mȶ?PȽNn qNR[(ýzH/dĂ'h| [ֱ,z[XGkw!'puPXzhY=ڴ;#_Ϗ`|We]QPmDn.o ^xbÚµg?Gu}uOh0@,Nާ4Zy!wk^rֈh*[$4y]fo5ߠ%_`QlV(0|NsW=YV_y'k1,6?Nנd9pU\ǑE%450_ƓsC<֝ ~]*5'Fc~^ug紦4՝"W[וW[#}5OPu 䟢yO-݆8sulK{>8@h[NeW<W ҵm s&n _ԾU7((* w9>c<Z~_Oz?&/kFrI:qr{;5U$%{3Om?{Y/eA p9U#9H:siͱ>[+ɠ~tMO Ɵ,!nlpč&(״>'$@A.iYźl_ U7KQ=v|'p0: ;^-a<PӸL60Aql>}Ұ'}Z7Fk=;2> ~X<@4+@ \".I>#H2+[QAseeުơ_XwR~YsBd֜.^`ݬa_(3up0}B䢄|YQnfg0r;OZx9G5[}@]'MYwB֙a>@qӸ+W>H>+ CDY0>)_ U3|hu~?x_yoʣK0g2ăC;ruKJuZzlP<<O:cOo\=: &$SorhV7Oh[O<pA~C~7akaMҘ1<ܣC}xp\ s:{߳j ^_;WWkk;wMӠ^آ~hxjr˥;qCY˷;\?`wI$\f"yR=Jց­<7]z{chx9qr}rĮbCc.r|KvLu|3ں2LV> к ru/ xk ̐'(D-%> İXt.%jǵ(>| >(nq.b~?Aצ \v\7jS%]2-&~K}|shxqlD\@xsͤK@=L(e8wc}R; ?@Я0&HNQty? L9Z+j}IA"/9p.֞~zاz?0kL3Nctml]VA|aw9ߟQW9t=՘joi 3JbZxcAC\{ v{ >)'?a?^S75Y8ڃ$[sw|q{vIk)Ȯ K->_^vM A[^uhPK}gkz#yZӽ +SYԯ)4v|ki|V 0-ri?#8|-? $fS|(>dqo,*vA P Ov־CI6~H/ [xT0l?=S#Ab'C߁wosZCs[>ܯ㭭f_Fo^v9|6nn3ʃGN:Z<9Td?^bqCzӮl)+g9)mBs VQO&;N8 O\lwp]˾`G0Pno'+~lKo;W 77 ! XY7Q,:}}q>OU ?lڮ.C}r$.]9]r l /kb?mۄ -B\W}״wEn{Qۋ ^.q`yQ"5>0C/˼Џ.iȜQ0c#+FWwn_BNZV!+/wH>&S7p ;/a4s@JdSUFcvμ{{O ;8?1arRbod-pZm5186Qܳ9@v\@>TX{=_\/R;ϊi`(H=`y4 uc+x!]zk k͟U/?o_b2-;]S<F9jeЗ}ۛlG?KHPjDrO~G_<世<t}Ө騞 vv: Tmz}ԍXNg:^mD[[71-~tQNZ08F{n~X?;zt6a} ?Jl} 48? ϶V҅,?b 2k4{/Z G5n#=|l,}Q~lpym&?twԲu [~B/i|T(6-<ݾ͗1s ^@r$p$NO%Oi.?]g}j1D)p7 x <P|ص,ʘ{tHP˹%-`~/Cq>??d=Z7(zp~76ޟü?/jƚ?vޟb{-֞ώ1'`XB[ ٫ɤrErWly%CEBʏ̹h ?FOd&5S?b^y;~wf_hwp@sv?߅>Q?FƑg p5>#15nGR:u!¸}52Du\{8@⢃ϟLju8+dߋt}Z3Pj {J1}/ 'l4uc7]!;EO#5$~|^NqO]'ջ_-S㘚#w|w\̉GKb۾9f\=c(D''Ȉuuxy_p 15'6py J+aڌ-FRڞ>@\)9!`{Ua<鈡ޗ巑B; @<~Ӈc9 kzQ\<@|Y?o[g$25$_'l{/=/gy_ks}:-/(h<>бC:w,}/VE~?p.:]~z6[>Zh[~>1c x܋瞧߇|vL pWYj WR |@_m1*Hr.~ws|"nPZ eº/%A\CQ7 ~^Gy~ O``0ٻ.!?!}4T쁮|c=ƋN.Vh;sp`3a2hF5;0<|Md S-@(q?0<`ϛ>٥!Xޏsu_ XV۵=v߇z Q{ί!A?8y'ȭYnW:V,ϵ3 qOQw<އZ5]j5'ԕw,~/R_XZ~v7` _^|A&Ǩ'42׏G{1=3aҵ:>3m\njΞ׿^WZ[ >}ŧƐ]u]q}+ZLfO y<^G)@mD,RHIH˚@6ǍVZg>c$ \5z@ u?c}@k1,b0>Og4-g,S|O3OǩO-=z;;e)lvaW' [4bvy  ~ ygl@ޮYs"6XV@ӥy3O7=7cNy>ڹRн "_px8@~K94}u! ݛxbȑ-z@YzOv7ۻ1Fv|oWƵwl˦/_(Edd (g{Sd$ĉ.?'dQg$$ƍ0l1A:-a5{1]'^/\r4KO{w sMpf\_~f@H@{cX=`o0JdF;t:1͹׽s ϨY.g6226Gk9ui׋/Q.n/9+G9k# )qAc)'/7DQlFr/d i)ژ051A ib7轁1<4!N.=ŭ[-\.$ &Zuea_>zӔ\iEiZ_Ne`mMenנEk.1"׈[Y3dtP ߀뛾}dFҿg&΀cB Կ32y^w47 & pGi"E`3\?;m<)̾XNѠo`qc7ٵ-,LhFM4hc/1?v>0"29R>&9F#9F#> ƴxlSׄ%3- :Zׄ1/ (=(Uw,Q<tq?O-Z㴖>oGtV#+kSwzTW(KHygv7lFh%l=29,6HN0 mLq?h{pb |4+n"_>R‰$gGҼ ,*; oJ.|r+-9w7+ӹ,r+ ИmlМ 4V^6'[g|9}lSǸ߽}2i%z9>>pj86Pҋx^* tβ#t7)qerܥ~kXĎ-c㸅2a~M,6ЈA\KW_a4ـttSui\Zc76"/WP]X.Ju+<ޱƦY(<<Y'==%@"&!)U\ѵn0zW,_zc:fF\_t>`nGvs_9 U 1*?Gx𙪸t`Jʻ٬b O&UAv$m?O>/ir3?fO#?vܟyjkpT<l6sK@WǴ>s\AjgYOG',i~2gH֚4KQu!f?[갍zD~p*pd$<%dP'*}rqĘd57\=,S߇SXM{^~S Iz2K~5GKi#I"l8Y|BbB-cD~ϱwUar-Gytx> t9j++9یI"_ϊ YF?K1ͦec ?fI4| O: Q.Sc&]`DG3j;ɮ8Ժ>mJ&5` 7݋~ RFX|:ƕF XMv&|ǚ7a1n4D?7ln 78qF$y<~K5"k32cu:۱ng:@N? LlfkC"c*̟o ⿷(OCYw mZu6K~4̸-2[jsKYCly:jw'x>*j?e+=;?G{, Wtd& h ߦ%9u7!cƊ[Z#Sbck+bLW9NP>خ׎ట.Y OjtkYRBϥ}JdUW :' f-s?`7@ԽÝ{D.xt]0r(޿7$S:$.~S<ܧ<_o=g%YOJG:"}`HzAOz])hfĵ&lѳ6J}%x\ ]=ʕwv`/;t `/۹UpO&:XZ`x 1z^Yj47'XZ&ng2nH6Opr2$=o7 =cD*&~&vf4fz~[6Ngnt6OOg:c?20^")<skXX9F67}_}zn՞֔M`c^m`Jc`_K?1 ?B^ܿN0UWy*U +,ee.r/uwf/`om3﫶:| ۯb9EH?R$]s#mЛTe&λ17$OI,P?qR<1GS<n"yDy,b ĉws8Hb|g- Y9_%#g` ly|oU k|T>xu*W..ss:_:>Ç*T WUc+(?ܿPO\wMکtb?. B s8R_H9$Lp{ձ>_Ax) ,8.l8õXu:uzW%?a?HᴙVYI<_ygpYDDO+7, ~Ɖ a;ܺG2xfG|7L Ns<{f#r^sϰc_pmqL槄9'צ8>٘t:Z-<4i#wUB hm2.t#k-?bcehf72H-< ]l`6X LȜn\Cs>aMzJMտϘt5ZoWhw">#X{OӔ?%>iZtjdW eט.Hm,zl:C@kJis 2O3Ǧ-+{lU=4uFC| @}ͦ?갿(B_jc`߅iVg K9v^brC(7e\*=!} *CmEm7)$G2iyC_؅92El߼xEƑ6_b%6@r{ܺX18ݎpG:t8 p Ts񃵇@+{[٩|LH|qdI~8?CJ#g9Xlj:1/9]''lk"l>vt]WHL3ky~J:@LA/Rmoe6cJ|e~ntUY BYsfĵr|A1d_1]]&N$ݍLJ)x|1+0?qwr a۷Mh\eSؾ F,OOS~z}1R_m(0Up\_q6"llĤ;:΢r lU?(+푸~s3-ae6uPwoUlmP?g'UuU7Ἤi?n|v1| zj_r=_le[祑/5!^/9N?_iV0360`w91+֬1^g˃sf ?qqɯNCTi_]s»wޓ,':W#<8LaH>'jww6 ~6.by=qKg_E6.dcY6l,K:&0lnm,EDĻ^|36\aR_qvDXRϒX?tYz:?Hr9Vc,j6IiLa.<'<nN$Er1-$s8o氤KQs! s {x{sMyfdןeЀ077tCŢsҟ^%kDk*IG8z|sڤčENܓ1dqtx|5]'vns f2{f4Ϟq[um%q3 y6qy_:+ٙ;?kDȲr82zn~Ou St.sg.$Q :p]_Y՗_3K}=1Y|늦 qJlο QV_#|/nmT /J—qj\gS[6FmNlodb(_X#NmS|,r6K|aΟeN_wX;'@lM+3rց8/•6) gU=&|3 !bۗq}PT@VnL& x|l*C8/;ssܼ>x:@CoP^ ?U@>jj-/?O_ֵi[>O}\{Z޾C~ӥP0k1K~??˽ g" H\_Am9K࿾`ķBVn`LG4;]y sBVLѯIʹu$ &`u\/G.-;%y ޚĎYr.R[ϟ0cL GAz0q7{_~%gQmmI"~d'!O!\&q7%ZI`| `g(-:\hcX_@._'>\E|V|o.~{{L?"vxp1a8xxyskl'ܼMH,lQa6cJKǨ3:iOx^Hǻh66Y8!/֝IaGkBx@ '^5uؼTgȤƗm>ؖ#6&P<|yn\O$Rޙ(Ǫg x6?sy H͜ -A9U(,npfVey<:s_ƻ:3kWeܳX]kjTcf5O_!e|/l ]fptEP| U~߾S_B;|?`݅3<ìjߝ~P6΁g}jۯz[6[htzjl.Ě c\ߊPߛl}H @b@䎸U?@uo [M߉@6u<t|_i ĎhcbzkBN=p3y$h$:ޛhK{1({ [qMyظ~U髹g#ܟK5MN_G^ߟDJh%u_sߒ؍LENd.S;7ͻDyu= 5x|mރ.Z{$OONGc2cSm >t0CޠuXl̆,Ǹwo ݢnޞ>#g ڇ,e pM ˶h{RtE.\g< FJSdl@'9٧H1%zq,Hz \rM.\gÒ4u;Ya[@n+"WJS|'C~ uwh6Vcs]anפ'l{bg8pg.C}EhCNӼcʭ[&޽Tg3n1Y_.ɋ ol-Ӎ5|⶷p9 xyZ̋8@7W}m'PXeo66Kl뎰?ve8~W[/_h}?xki޲.vA_W}[wutϟQ23]bϒ+E9*Ak 8_W%؟SMH'[b lq)Rd|(࿭O\dW,:ma7T}opQfhz^S߇րΫ+֠ytNh+ԎOH ` ̦x_Y@Ӕ=ܾA6,OXDO-;mіzrNpH{讞]ܹE2#{]9w9>e[Klkkښ:s?uθHm^ pVƶNj*m3"6p9y`D=;.@cR1E-±7b7KW]PZOnNX%|J 'Hp>Y=O,b@c'2y0*⿷9]| `gIj#׿῾Ws9uw"1^U/\fwg}SttJmպ?毫!Ό1;C/!gB9_A OgϸGj}:K}KߜJ.|q[w:{@_@%\=@oȝsK`DUMY<mb`8Ԏg\T= OF[&%S\r16H^,v]s|G3Oh,4͹)ؼ2"ˇ1v~ls[m{18>q.NQ@=^`ߪA52q_k)1)SkcNZ;ܱm"6՜E0ّn^Y6>$ig1vb inu`=}177ڿx-:8c{AmlD2֓5[glFchqoXݤJ: !)q%z…_*K`7bvy<ߣ5 OVS@f9}y]-f~=6Ÿ%ϯ>@0Hl. }gBoU@9=b6 ]u}sr>~>}],/ ? 1כEu\޿vSc1`ۃ}k?=T Lwj]CGZfsRƿ$žr ; ?Dؾ!!>֋_OǴjTh_9n$>qo#ajbykxCX>[zm3YE_YbCܔyivRpOQh#Yj<]Ö"1ڛf->G [CnC Z^葱~TqY1&vM7' }i;?2@l3NmYfitƽldS41} FvfG+ωH49{s#>bnkJqeY=p6J\,G?ˑ+Hg5v>MdOppkmEZNkf+ n|K@#՘5ebWέoxZr#_wO& bbNkU1mS Rxa=j`~5Myu^g y>z P6se{ jZ@_|u6R$s:@1uy@ݿ~=s0{ui*{|V?y  _~ki&3O+3Mkg!/ŨˀSw_혈o;/Gv潃s}=CeID\\̋<@IS"èܿ3bf=RoGvcd2Vƶsxs=$:xjwW SacMmE~G:Te } %+ߏhޭayeƣeG"&JbcoBݎc9?&YBz$Lbի[\0|.œ'j94qj{DP:%,T_дhqeyX6bP=UmZf.y?D[D%c~1_xe:<.Ju|-`Z@(}Q3ؿWoKź?"_%}Zt;w6y1gb(EB7eZC_n$qỤ.H?lmYoS]Nbds߿\C/wbmw&{hͽp=ο,43Ƭq/ƫ?qA*e`Frj {(>~-uu/!+pıM2eo \_>%<ѿS9}'}fԤ-:1ғ\c90c5O'?uؽWex3`<}e|}z=%kpF2M0|3?mbyϢl ä։qt{F^`8vx'O&xI2$G֥nܘ  [6 /s<DVPT"y{^57RKj>@}%_c0^apGƞ /0r\/ph(?ϵS2{=@b_]c8(sFQ|<,sl3r 1uu.(U/0RrT6*/sJ?_יe]* a*]''M/57fxt5ɶ =߭gaBiYjhx[bhkδO7/:s,IgؙH_hŅ͊ё18; :c>oo[`qQp9N111N{d˜a]`0;mΐy_U:[ }.Nm{K}@}NwFz}? k) đK+Rsz@#;Fucs0?_9n!]s/EvL')>~ѣoJlG2\n} 집X@''xa;3>fNl h\M eǗ0F@p1 `k+qp}8n߯xUPp-cZ@}ow(v|c}a*wc?<^{P]:^@\>0?_|Pu__cJu=c\޿9[7<ktu , _?sC??\n+}_ J_, 9s/oJ-[m]%35vnz?Zî~ᳪmݮuȜ`kߟs܏was9M}_oҍI>@ t _<~l>C=3|3!?b6IbS6&>-`nw2)Ccm }>}}nlHN\p}Φ8=}4?Cg' F6A'ظ%ۈ$ðY?RvHRBN}dl`ɬ/n[BuH)Jr F\Yfw >~Dz1=y}cyɖ13gqck4Mo`15MOml-~Jҧe\ǁs22(sHkHv๮v\ARLsl% v>+{?םWeT^}8K>^a@A,ͨ?%Eqڒ/b.- \k+PY`ۿIƲݙ'q5# Q;t( @k%3EW^=j^=Şc>J[*+mvF9 Ot5.?h)ɶcT2xϊ#?#=Nd tq:C\}/ڥw X)5>ŧ0dS'*eCҺz֯ZJ!罃~>} _ d{1WkN/xL\'\xrBRaXm>FVӱ~j!MVsb+hY[M`ׁ263,ˉw{4kϜeO"\/г5&M]kMMB~)l,)|?DdIck07iŦ}C}Ϟ17}8"EyY,} @V q 6p.Obbh6X\TX*uˁq'. U>U/|> _X)|P=c"0:?w?מ**A.{J1 l_ _tϸȱI/ߢZ࿋ Tk>GPˋ=܄f|_so~/J"_5Im19{~x>iμ$.5 %EJ_wpt[<%}i@}z_~`Abt#ÏgSL5Ftx&WHdHa t x! L=ǽ7Ѕ$W̳̱PY:7ovY0/<y-x/cҒ#ENa͛Sp&DLiiM/M*pQͭLa#ji-xJ)~n fXu[?J Bm]#uI7Ϗߧ_b?\W2^ܹoi.֣xϞ_~7ؗ=W ՈJ=g{g8'0\ <0]Sd_ \.|i<|r"y\׋m`X Hy?e/:@1<} @0(їx}t/L:==ܲgo q?E+د/}sqsagqhv7X7jt xW9@М9s/2AQǸe{nhdkql;,Wu3WdʊZeX8BP[?ps+ vo9~>ח./{9pl~4V+?ɥ}-]2m5pMbsӉ|J/p]ܽ d5͕O%' d I3s=m( \ާ}qcVWy:+K5R'jNqB8bj/St39nyzt}[9=[]/Eރ)e ĄV'O aN؏V7ڴq9YyiH|mg;A-xfkC޳O9bYR^K\;jW1B@p36!ġ[R]AK𵀌RerrlN~yFF\9+b H> x X@eX9ߗGwޗ=ysl _S*E$V-FV_I cдqu ^nrg[S[Or$]`q8)T<4x{.Ǐ{:yw5IP;;nyYWa_9d? FuY/R7'˱pbǏr'VGj<?v6=9<:zh4:`_uou?b]7= mW-K}/@S:_6n='/|7WᅧM*;Ss/ɒ~;kPg噟|%5g$ٷ6z/]Dy^[C}62pL}diؽ}b?ǭG>=jI gy1;h',o>?.a+{v=Sh3;(1endԘќ$6%#tSt%,q{hnqǓlG8kJkp-p{sMj>`k9Y{=ŝI`xu/- OigRo&Ys,%6Wl^k?E{-/#ܨ,^3W,;+OMj_׬4TU)+ z^Ԣ?ہ?"<'_h%+o^WyATcm]0}=YI,綆OwIo#+g P{ϯ6I]Ο,ANR&ӻ$71jy\YF/%=Z|c~\\kؽź-C ~׏ }~mp>)ɜ0%f?7vdzGht ؜?}>8uw [̓ &\/:Ohzӄ&Yg6mZv_8{/ kcGص% xytxסm}?R=ha)_'No:h/um5 |~z7/ټ0fa?Ks08uε[8x1d_~/a~"𪪖S}|^f$FslC vi\bNq_>#5z9YSF0v:3z4{u:qQoP5t>@/Ǻ~ '1RZ_bfYt"g_WK<:9tzeW:\C/u?\Qco^/x_Pf_S{w /UO&u4p-hW8[9Z;CHxaNs'mps7_|{p.֡?x'7mtͿK..nؾ?bkcڱ>s`ի;=e~5g9d>_}QY&rc>̲s4N10s izB1F)JE_L&u6\˘Œpk45C|YO?xIzԟ[FW:JC:,g6NrРN-҅F8xt'?Ęڶy>oX>p)rZ?wxdA},8$~9cCY56? ΣZ="WS=\O~7oRkr^ΏS|6(&2s--.&[ڞ:@Z y3;ߎZ_Bqyϟpuh|*;uWx%\p?pT|\ok SQѺ:>:.o=:*0;iφh쟷?O@\MExܿڅ HzR]޿]Ss_2Z>z<6rLg纭.aN{&5!z:Vʱ>3ϛ7w%1y oR6aǝ}o NdxaDr׈~¹5o_'?ϚC| %4[-k?`D86,w-lXG4Izk"p^|:^?33c:-±lS`K2 La 'hE}5suIrk:$݇q36I O2\'s4O2QfX"`"m0$qoXZlj`FkϧxSdIl~_NTvŘ585We=#ǚ*eB;`NqؠqU]2]?|HsQN߾b$W+G=Z-;M}( >0@|}J>1e+lm@L^E.`|ϵ! _u\jo?P@XZ@g@(* %ߩ5վ 1?cv)Fxܯe4Osߝd_c7ƩU+k$YA@QssݏRj~4Z࿳ !w~ K'9~y\pM9Vd Kc?X+pqQ}tJS^ =/׮ V_ ʩ\΁`?k)>+a[?%)) uĤ}xִ'mx0ƍM4>x6{M.q-8ͭq8{rn ugt BwBzHL_}8,ƒ1wg->/ugJ<: "U@un31ʾ^S=|]ύ5G@1(ʊz/ ߿mXoZ8U f>i-@l L2\ Vur[I?=_W}9?s̿Ӭc"GaBGjoZG͵xK?g|DQ9, kxCqNK}6+Z+_`;K?o+G?}4N cgX' }~ͷ C/zkz7y R۶췼pڟ5 wOy}ۮ|eް/aUbGh=5M}RVu- ysҰ .aI  t/NuqGmֈ]-u0!Kd~lv[Ͼ \?qm`f$G'.c ϨShwǢ߲Aꍭ6&,#y?>ŭ3y@'kl1-oS]D0 M]LL1ocH)1~gCo#|H] rn7or 3 ?s֪[[ɟnKDXx\os_|I}y"wL r^k(1uC[ldԆfq'@6YԼ@c.?=*1pX^XTk})`RR:20O@y.\_GUP?~@0@8)KuE_E?MŽErqmW乥y=8_"_ɞ?_Ϭs'ϋY]`Gv?Ysu5?oM`?l͋yk 8AEivHt{.翨t0]ǶƦq ճ2P}YNsN<tؘm_~alܦЍuM_ĖVN?c5 K1)ďC׼I|@\)%x?:ŸֱFcv1"`oh?!\xJ8^6׮:a_'(UM8}qs3xqxr_I>.pY .:m<"%@c|zBeK.Gw9.pӧ!qq:/vs 66_Uw0-g=Ƃ=9fyM]@H=z8}{Y'1 x=2WP|'֞[ 4`B>olS[3rifGjoZoN3-uJ @~U[v=^9jrS k;/Mxg$W%""}^ʛ[;"yHA?A> 3󚞬*_[ק9'f(y+3q{ݹdS.g"8QNhURjumywjz9 .H"^H)>G?OS=}11{pI ͧ{<%?4*AJPJP_O1{hr䰒4D~]CDveG7~odc3ddvC@(smvh}s[$趺m~}`x#@22FiF[3п-af6dKM11=;^>zzO)nvsm}JH5maeyƃ|9NPH/%X8Xqk[೫\(]`]ܘachlU1qob0>.уo߆z8t(#!FC{<{Lg05egi)|<&@.kVOQP*˜\>C,[GEUyYy s> RXecz^')9^IqqCOV7ۃ!>bQx&p h^?y;"- 1%3.~$pZZ6a*Μ[y<5ЬldOl9߾Mp჌e?[ܺPJkW'OJgBsiqiA?3>ac# 7'}z|>1"v3hTP]g)#uC'[t&n99ӣ؈H?܊MNJޓ;hKbyw]m'?4٥Q9 Tm14ȧȦȦzY~+1%H7}w` .}oހ/]vY䐘w*}/+6V+֖~~Q@89jf=aݮGEPNwEvJn/TQ@ 11C_y:;s:@/ҁVM}_dHhb?%ν5uy=PKuT͖i`Am,D%} gMYwz#K)̟-k,,a,>m>Kb+sb~,cAs&=NҼC@x}Zpj%P=}ͪN#a~=o.Qt]T0+\Fѡ}`kȟ,_`j)F93 9X5}$s(w߯c ^?)~W1@"}A9Ng'i4Ӄ},חl}Lˊ| @'5o?W7<;==C:[߷q\ 4?:pmLZl_]߰OK3`pj`ZYsVh{Bo'8yz1z[=t7E"Z9?!ה0$b🶍ē)nMr̍W;±Y; {t\:1+ >=寬e-%߸OL#kz%8kיA#)~x_)3\?̱\ _<38ymY ݙn~S_C"UB^q3SLwX^0.<*xC¿wxoH[_' {2l8f"B;-ʢԃE40QNBI^gKFkr9`bӿ^z?޳O>^Q鿝)8t'w5l6d M@ A$8ulTW{!>ܜ<*fϞ͹Ǐ1~5˗lT9b32cM*\9ˢc B}C*0k5bOtѝ 8cxx??}YJ";r,- 9, &>g.S5bg*V .*b6b 'D(2&}y]sKm3»f d~ؽ8 :$U=+Hdac:DVEDCH-\~ZYi>,8`sJLގ)>}!zOOpCm{ߺؽUpD;#Ob2$rYn}>ΆJ2l?fz *|>:#{'Ğ5-} :G[{:s}: oNIsGmt!_CO -/Qu+#?vn3Pi3a;orST#;tw׿ngM-V:+CER߿g㿎w2 ~q??m2X>~}/\auEwe(1ܿ?{\ٟQG8?*2}~?N>>d|7{:~gdMmzuᏻމ\y`HhPPư`(lk׻6֦A}pi$#/76Aou>p½N= 1&44}wkwNxBLmH cY삲,\Aӿn֥͟"Lt2OuQ}\gh5?_3U&xvJ_9KW=j%qpwꨋ|;M1sv)}Pų'e1w_ `5Ka~bX8&y\=Ɇ r&':|';.\?$y3种N6EWzrx7`ǫGx~*H7Bܮbc&xtC0ɰ_;wj]'kn,+ n v]ܮqR'&ϟf`\>aN;Ç)ݣTR| phOuP`0 } k2`}# zGh Νx+J>W1{#SXX01KK7#-=r|ZZ7>?UzX?}= {3=n&o3NXrm/y-ζ⿃O7 Mt?˹QjXfML_k?׵Z}zG}C}W-m*Kڛ+ f. uXA *K]~5& hTt}stg{C=]g27q< ť[(<=M܀CĹRA6o"zqN 7T;zw'풬 פd|da %5x*+^ Ύa:{Cxڄw:^<+T08|r.P.7Ɓ?VVR^g+-)?EJX@eZ2$?JbJ?E=3OO[u?!!K9?c^ 72 Tޥć#M GqJc8pYV;[W/Dq䨌ϥ@e~WoO|ӧ#4 I?/ǽxǏUNw2n7!78جib#[W 8 ~Dֻc"|]<*1LLD/%r=@ƕ Ew";a|џy/1([N1f ՎCR8^ Î5;=Ş+}G=/ن?cc}=se߳; f7gWuWsEH{:@ =I*< z?cv9f=e]?oP|¨?Df->F^>S7pm.jYCS!\bt>| /io<)Y?&o^j+w'`Bf|Z{8.|r\GB@0 )ruyD8jY˔?ʷg5%Λ}LpyBqRe<+z³<ؗb\;X}\G}Gas2C}Sxn"⅏AyZ jG!eH*\z`A@q)6F"sQ(zȦܣi?/+_{2S1TSE8ZGP6" {5:N_' ޵qmSp!ũsz@0Ӣ]bu,]7o6/báCԅrO ({.bR; h}[P޷=j/.x޾T.8\92:HcCu62Rv︶32$>$60b8܏!|ۀ97 @9? `^_e>\ wԋQS)1;oxg`c x;_rK8n⿳۪Ovyrмuarg#>^)ko{3oc#?$.?k\cF1*r6g s7&c?"} cڴ{0#Illce™w5/tGXT'ԁ /CrWf1uZW%{s~%Ôpf}J&g@".Oq8<<;_J amkXYF(4k`U&+xťTG9hly:6NMHeE<0Vj yJk5~Tc'T>ߝbh9zÌ#ZvShž3uV7"|x?="\L_#19|)}n2׻ ~پS(Q'gng|e/b0ٵ ؏?'?ܽZ[>wSDRu~++ ?f~c7~ܒ&5at4Ν]=wM/8L4$V硝ds"1N愶o?Fm`b]ek`c^i & t q8>8Ε(u/I}y4(S]ۿ΅oOBo}SJW'Xـ&JX"<]~߷/1>'ok:?+{Y`|Ӿ}07cd8(9"cwL<"f?YÎw) 4)Đ~rl.?c`G;c9߿/<|q<ګm6=IpDtjME[{v1ĝq |Vƴ:6|5Lx=wp c? y@?^˽K| mW0̟~:QAEF{Uyv-pճCѾ3vO5ШӲrC_|1F/(٫:: (x˿߈>IoIO*^c#"3? P0Ƈ]1a)v Oʁ tNE%|%8NpL ﻸ[$8RDZ2b%[G2& '9KqT8qnܐ1mp`X1xڌtv;Q{;J峗]9cq`o>4n Gvw:2>]099$(<:10p]mr`:>^oOkێ?r\h^]cWV"P7k}Ot19609EcuOaM }^?b0g?j$EۿCo9GbܿAZ"w[' s[B1/֙!o ?^qO`V&1j̭}aQAnγ_vw(<<xE,-|;2F#}>{[{M H@Dpz`j*G]6幇ck08Bgc1[[0?@wm. D^M"|?;Ӂr NL ?2+@3_8wo&6iEwk^ɧ|Aa{fgÀݕ q>[^![u5oG44ab߫m#2慛/ ƦGvqޔ&_Km&}3 )Dxfź? }3VDOQ߿aagy]0U}8J'y7d.=?&?@6|Chu܋|`}4KގMK״6ku3#σr ŘpEpK`̣7yB;)\\]nlq^tFGoVbt GbCeDIy,=e!_y&>R*/u}uJ "D|~uv]p [GݩCs+#MQU,B9˴6+)'1Mӗԇ{?yhNߦӆoH1kh攠䱹ΐmE㡋tclky @b?? 9]6?a3mc[5;?@w0to?e02yl?EYތDe/-Ϝ&{d?o;!z񝷵S&>v3Ը$8|ֿj-Wc[ּ$s )zbll|"Q\6?[o>[D{&p1kZCu~s(1l__vKmÅ&ê>Lԣ~s vέRסŀ1 WLbi>wp`"0'_fDUo0ū+gw3tj+[!*ggJG_0 ax&l"5i`\chSfl#ÙأG&H9f{ߩ^ly9dj3z>A>ѽd^ձc0z0yb\2ēۂ}Mf^pۛ(iM_"+kE8q"!qu]I3S7 ߡ?{6`g}]!FG+nI }7d_0 q0+ǣ1"9N1|(˗ɻX#}9b?u1Νei{Cƿ^x?w10q4uE=8q=;dHLg09og>ePot#29~p5>?`Y~=gM 2@_,s@xٗؿ^k/_s?gA0Tk9; ^ϮXNOk)q#ޓk <˄l_vxbSJ.ݒuaTޏ!.:4fS,`LJVejuemu2Dǔ8&#}Smx'D۪m <l-4Zơ5lxtC>*m_M\s kw/m|￧`si`[e|~0;{aّ&_^2ƯgC}رcSswlnkq>y÷+-in0==*mV{.9 C[a$U/0:Ǻb#uQHZmq9|Lg`@X&z }a3x'i@g @hbMޫ\og.'s~H\)<2D3l_ث~/SŒ~ӽl_V_rn/ffOpmO0uX'׶$ ,37Yc +wϏ 9~I)1HnVlAf7~b>~Bc{~b9d`a[Oe8WPy{5]azw?ka"gBᐲ٧Z/'S:gYd m5 ]S$ؼ>Ñ=|.;sfq\z?C\`&VOO&X9yݤ'Fuv&%=N<0w5M1yq%hHTh( 0~P.>2J%CɩX&Mwj< ++_\59 <1tN{M\̜}#ɑwݬNjr`SN' 1Xŋ1fgGOd͛7B+xtG0`Ϟ. G4)@ֱMf߿P``0Frb?k]X@=ss]ɋspoj8 x_Pb?_b~3<0?کr^ k';7/fyjc[l6Mgf:c2ލG9~"}R[i4φ~\ʛN?DLyh &-#kv#_sicZUm!4>nؿԍ m/ߠoʣEgk} 5 lo|2-Lmҝ&8I9}뤼/|qjN Gfm!'O)N>_0ourkB #RocLWwX6Z}:W^|&x|IQw,\;ƤcҕWxp=9בy ctO~[DBfTfv"hX]JF5ǔqThF LJ[^<1>V} 5~^8ܻf8aqrTy\19}Ǹ_W~3^brocgL*Tn 7zl0ytɓ7h~@c$gmWs33 1or/=wy3`n0lg9lۀ}VW.vmrtgo:@&֍P't_Ux8>~_tObsŜ;uI.ρ?g67ݲj0)w 8@_~=~;A۲DZGo?fk9R/E-2Y;E|3mݾ? ?36js책Iqnl[\ZX=^5ZbM?4'[ |߿75_ؿRT.MͿMiz6z^>{'|Yxܑ.`N^Pǿ؛M?VK{>T?2?b.х> 47.7Tk >8^B7c\̏qcQ.i^ATqY6Vl`\\O.pQ*uaK<&aޗAl:p|`oH3!CFE4D1XEk#sb iA-_?1&:ӧ}1K"H_h8AC'xrZm3 nf]5I̾)r? S/5us?F"u%.yy,1܇`TsLPv$9q9H>A_]19sD7&DW曥q99[[T 'F9`-w:`)v{~_z/Q7z8@#%|;+@}fOs3@wkyd.ZA6z{g? {6/''7m_bb]ݟY`^@lE?#5lg[ 8}(ݩg=B𿹜`;2!UayQW9]? عȽ{-L>XxVӱ!W}xuu5l4 6N|py}m;e,5=@F_ߔHK×v z)]Mqn~>:`kN`? g]?a|i_=mїd|WF0 A{̆53pRM֨ Sn}ɦH1#߳mɲQJavB;>Ǔl~EG?ӇSOr{h?}K"{x'1Νkɻǒ3?!Wq |TpM:{؜AKrS>~̡-Y,b cꘘ<|dn݃'b:gç1uyoͺ-f$Y`yȺ^[d>܆N^Dw5?kd/P? F0g A9}}3\t]ԱuaMFb?kǿi~KGL=῭ qjbvU_黾],)7{;}EfVǷ6?{K t1Dmvְd)*vS{`Fa1&X\e r Ox|Ͽ<8ia2=ogFI[u7UsR^N5_ԉ;kE7m70UoE> J1ՙġZm6VLb~Jgқ;~I=|pp.Rk).??`lS {?=Oѻwr2GL>#u #k*A3G`69>K-@Bnw> K0e  bcC ~}?om L/` an<@]SǛk|'-wo6f6 d{o?j|M ]K]  O~peW#t9_m+\?Fc.ZǜPhq5x䡍H.w7)[TH0LG1ob9PB< 0uz+2߷QCCL'Lvy_l?{-o{ƽ ݓP0{-u;'Dwu>Xo{16&Sُ[6 cn/_@kc<9x7wu•N@r@)`ꈉxKdp䳎-/nb W6Fb5ү|,DZQ^nkK?,|)/#.u eݖR 4/}WHydauT=nY?ILk./36 7Ү3vb*C). >||^8kMM (A(?kgqQE,YkVvk9݋8>ƉrH17+ %WQ*,]Ž#rh_Oq,o6yuFCu:& n qV o}*&b+Oj?Lz"sx '2r:] ^Gs+z. ??EUʿ9|S9NR߶xlҬ{eX)ozagz,t:c@ʬ]FOvnrT*~^խl p}'dgC?c͆5FX{/8[Kb8MSz]Wb<PJ>w{[l Mx()-0G|;A99>W6]s:Їf#،f~_`p:@7+oZ/޵W~߼{Ykj|(nϿ\k%wۣ?s,\T6il.aus[>lv{D>5MPSkB\g&Oqq4ظBFzx_ZBm[?aTØLBOx|<}Ԗn{mPz0CCխsȻ[1^aU v\d uGőg~ò>'k{,v-3/`C |_=Xg$&*"a)9vȻXid锡l#\Fv,eLiWqЩ{mr~Y^lmܗ6Ǐ!ߟZ<.8r~oUC9 &޼I|G;z q801 ۸paZmNgK@^&nqbSsb-E>ݻPY֊|anjPp/],hs3W+%>@ΖZHvǺWhUp1x5ĖŅ@m|dgFpl?0uSW_5N m"/jA 9/ۀ9{5{}6=E>_4^XS1#xcR?oα ϳGB:ˀ'kh=^dܿO@U{Z+.W.=>6-gƛ)OX`}Bƽa_Kk}~݌ktݲ|{3}l}(|9=eO'9>e{& ]64g?tI0v2u^547r(;qj>T1T;g9Yia |]m-?|acR v t |'V}?~%kg/JUmf~Hph.|γϮ 03G7c~bqA1dtOݮ{X01K2Qeɛ8 Xzi^Խ~8Klb YIZq KHNujkUsGcLU!_Lf>UImތ )g%Z]Q&s"9oXÇ uvv:K#lN1ܽ@&p˭E9~4,uW09 -V/A_>W1@gLad4NqueYUDbIsok'y?Yt@P{d6u2] J]-}3qLܤ6}m7̔rso[߱OgWao;ۿp`mu %ߩ?S/-fo_X-Aa>} uwt2wMHgD~N_plY`߂E㡌@Qu?卶sw݂#k}6 ed+DG?/|HL_G^vq{ZCuvOo[~_D|8RǎF3;@>^4螧=ٿoMLx k&..o k~7-}?v}^TTõ p`Ǎ9 @v Y劙{u->P P3d?ø\eVd'NtEmOI?OK?;c>ZWm?oL`3k7,sO((5?3=@[wW,̈|!(^}oel,YYonMS 3ތ5uw3cN p1}J/PK71xEGصQƌ,d 4<^ǹk)Z?oj'OEp{>ZǩwpimHu4OJtX9&/5~'cyqu Yڞb]?ލ1. %;-KEh:|=sj!ȜD^ E^:V`bMZW5 jF@Os+>?-9,.ץl,z~ .Xdpgz8{vH?*߽-iQ?xU|pݸڼ|M#Gt>?3@b!&2lcO?X{I)hh/G=4&˂|˻wr8e=%nQ:&@/yN\ %75x?wnB^3h?:key5mUѝCHK!.08~{p{o#05J=z.ܨR%T_r>힀y:,ŘW1}pۊؘEE,:oɺmah/R81] J {u+rϊ^&}렬} 0qj?k(oraW2L TXۿ_ߧ䚘ϣ|>エ}gO"IY-Zzggw9g}m7DLY?c\C}|*Za߿ŶVw+78|o5~.ñgduMl𳣽)xG\lRsޓU\Np3}Aې9ۚ틚?9.kov W>4E4_DRSb<˸y%FWk9YbE@:T(sO͇~#Y=mSf,EONi]>W/>U> "l8Oƻd>)NʘC7MHeZ''o9s+gOݮ\aۺ`p'ONoScܺ`kb>~aeFCk[n1\2Op഑0^bֲ`.}) x7K]d5;[j- ޶0{$Ł*>@`w.-CI.pKw0!sejWo@_d6o;kc, /ωnFo lE,u DŽb{kE,ue,my/mc1OV_}m6ccE/s렀YMxOWLa?{ >:}D?eN oA?R6߷vd>?/F,O  u4sk^G}|ilcSQc}'=\G⿩!Fћ]1^:]7?naSbi].~jo;½W'ܯa; [[5\{)yl? 9tD8 JWec?wQcCptϵqup~uH W.778mQtcE .f#<#8Vo_k:/Kk}z& 4Ct]rYUCMF( =I'ya8v$%ֳNKX3:ܫ$b{aZx<4tպ>ܟo7%ܹ?!ר*sߝE߿Vt-7E.klO$ƎE{\Gg8:"OoHptZ!\)بD#99*:Jp̘)@V@1~u(ݞFpO-]_căp``bG)ѓ3kc)FN+GAZdXl}΁gw=k=>'WW2W/:нSs̱֘wֳ({>r8oL셽co}B!c`^`77иw":u׍iܸ `01ϯ8Fr;'Fwa~Ž?Z?x/9v/?|C|Ƕ tc5|u{_V12=LwP{7DWd=l.2ܳK65z#꺯h Y怌B}#;~+3#iϻ9}pXO3K$ }TxanXVEǎp:uOeUhSYs뾈?={F1!QUj\=S'{%{\\;9x{9tOO`|biVܻ)"T9*yssay{f;\Ű;'_+7 /-}F8~nVa&/o7>iG/Řc`h h{ Կ~$y]~U-mьls~$=c*R2>_EO.f#7N/Ԃ~33cB-YHLbmcu$^Y_c=ƬsGu6'tOHv=osm?~Sgl}k_\68[ 8Ρ WKd{ .'{#jG_1.؜FetNrb?&`2g/d9?$ޗ!&'_JdFlxƇ54no>v|E8 P(a2bo%;+mMS ̿O?c2&O뗁;)v~`W\vt+zS#{7z v۔}4mRsM-Umڿ#%g!''h$ X'PY:!M{ǽrXb䬝y~3uu<'+x3eTk[8_)aeEp:[7;OsccΜ͟4ύ>;]{8}~z; Nڽy:3㘜7W]Yϧhؒ>6`=˘$ZdQ&/Io乖^ƫ~[K@l4_'!:MpUmJ@gCBIcM]{ῳsv~ˌ&~L5 Ԍ?/SwwIKIp}8lNV~ w\߆W+ͱ;?馊|Lau08/vz`q4JP~) O>kbm,N$B 9NWlL :(KG"ʊ0o.%ʹ|.:ӟănUdȟ-GCg~-fq?g 6HT\&I7㿝OQ=B 9n|)?OS*H|NZp3om7VMpAx#93t#ߩ`Ϻʹ߽s\{i .>v&Ofҝ5\9n4pwO75(ǭop{p*L)lh1Duhb;cέ-ɒ-\µ`1\Ȣt$k}CFt  `KmFL\fdi!όfΜs~>_Rv8})A363ij2hFc00o4kCf4ӕ~VsE1HI"lt$KrxbElz^]\S>eF֙oaz'j'䞷kC,k˶x[nM\|ORSY6 }ʅkַ{j վ>GgCSCcqN¶SZ^Ӕb92JHq/ەukv^e07!K@G//ERgQhJgJl+~6B?1 +'0b&|jSȊ 8>,W|r''/AiGk_:#g/kEچ3>H?N3/2qV؃1oΕWy.c[W&{7~ks6f1C9ؗ~/9N:x2_ `د*ƙ̅%^#3J{῔~'|ܿ^l{(mخ39 o}Zbe(},Bk}:=.ʓ|Lazy:¦̛:F`]cOr8X9 k1vN{n7qQk|i:XfULƑn0{M\#sJXKlV"~VWn,<q_Ky<[ q!vRMNvf'Vsid_eiT9"e.N.7.q!x]pb*~p^bYQvjz?sg._9$kH.ҢgN8Sooqz/ x PoTq\onɵN=]>hV,_XP~S#{^hl5:o{:&X3$p/g+[?Y9[mA']nz&v0$+3يQpue8l;k#tݔVۃm6vG;a0m9_ֈOl щB&u9Z7y4giV g 39"agr OUO)so̠aӱ:_5OU}_wtrb%/?_wyq/2Nubw2}oϐ[cd__啜W{sܸ豕%~6K7e1-O1a?v] JEu[npHS'PgG2F:M]^uUVRX%~r~[%],=c/dLh?pa\/"]o%O]nݍ6fa zt:;+mօ"itVȺcN~:xGlN&$?R,/s2*~·^r(P*ŋ/BEh\yѹ߫_Pـ<*LSI_*Yt⡵E}zS5/xSG8T/zsC!Y> j`Br'./7߲:UgUlkCޣqWPMwܧ'v8%pb}NiGwj@7y khZ<_%g**Z;V6ͺ%mo56m"Og&a->r?ǨǪ3l6%cɁ~S?˜ֱ!Lدl%۪'C:eO˾%Oh}/wI'g[N^]q?d+엊+3Z/lH|Ʌ/_y,lp|2'~9T俧Ө wޛ#n1_GSH 6o8k??36H{'f-*_?]?"#?&=&,=vl_{WkC]"0Dv#|E?s{cg}s_N<,fhd <ϴCԡ+JzV76QCL1?CV[ۘ{,u~CF*3f.F_IϗCp"B-w1j* %M# VWmi|͏Z>*=s3"+DS5PBkg W3sx =|G}?ag~%9T*?g>_C#6{Iv(QݡNCZz~ ([,ɹv}=~_|¤CTSD6^Ro6U/Qw]E{ _g:ڪvih_Qy}Lk eץu)}bv\鷪S}/LSf~ԧVn3T(̇Oek]@~˓|3~cg>o궩gW(*`NR߉~3R~(Or<9 c0|i`}Nu "ڠK?ϛmdN.3eAFsWy}S>|)~ц9A@?@Ν_ЗO;cv-ۇ\^ٶto3Ys7{+ XR]6&<=0QW=&Y ='ol3|~Xx0L7Ockok>_ώg,nv 6ZE|* `.e~>+}ʍN^j8כ]ߨu2UK> =FNừ͔^h~Z/ 4\_ chl6ej=>Ns+EcGo,mQ> /]{{gVEE3=hnidB~heFhm+krwm{g[9K><_{}_~gS1s_l;} ФAkQ(GJ94 );Rg ~} ]2zX}g_\QH@iHr\\5ؐ/Uanaǚ!Kb/(;ox [=G}3Sk'>PJ?3/῝S9T/!伙)V)2ve4>d!3 _l?*H>hٟ3wmvݞ;}Om`{^MzWɎU=t|ja1(_ {_UFO J<y<zgphK&v6\lؗ% #SEK5s03ss|?u_Bji2v3>pAiWi° w6Z6#n.}}1zvC`"~'*+Vv)8.=ΧS/hn};Y wMaOT=#w=Z`sG*`h&-Ɋ\]1FRG8\L?& -?gmg`maMC<_vS>[ 伲':?Zު혏Qr016?Ex"nã\2s3dda_Glk;)I{=dž^Oa?ځB~,#;s/ _u[t+_vo^Ͼ@y$/7`7ӶgkR~E3Qq*^GD #\?Lm7y %߾7 uW?.d O^p4Mw|ba܁e->0Ǐ`_,eقڧڜG / X k5?8; O<dϦf Fz.;0W4N?3b{ƧҰh/gpW ,OXZN!{]ħy?aL˵=-/mn?ڼYE2'+tuVtGnͫ80}#nе= tn]QT};ѡ#yZ\PGGCb}v}p6 ӿaY,熴Nm ߅ɄWO%72f&F[^~BsE.U)L@^D,ѩ97!]~Rc}OT-雿m;tщ}r+SCT suT>} %9`8&;եQ* tov3'nxTlIiv ۿ8 ;?3$y}؇|id2z~oLo ޜx1b/`,77>6J|c9*T bߝ+94ᡙ3_ "_K|lRvf}?X7oJ{c0^d t|f5;)evlǨ/Th--pbϾ~E ~(lE@~,WM{w}~ d%g-bw>֮+Of>Ǝ~Oq~]_, S?9keחo:hhkwx{=f j ִacn-Tp~<l گ K}uԧ!h~֎#n" /zLGb:|֧ꈟu]Y^a<ƜNA+W~?pל5l<=|8 =Oy^߁-To/]& O*D#yFCӑ|l_~"עH._ӽKb <G+P#f3Y׷ssJ%}_Il#W`51ض:~'4Z)3<8ڀ%5q}\wEl|H/XORߧVA#m^%w=z|֣KCZuՅPw=5/ q,`=mР8Vix;j٣wuW>]qA]Eg«> x~QWtq3lwz4wk8zLِg#3uЧW Z0 :!kJL~im|k_;k̮gCӡ:ڢ߆<C?_yl->[Y$ƳSuĊTS\d\%4ѸvZGr` vU}?-6@\LNG|}l`vA '%_|+F3l3d+k9> o$m2g+ lx?`[+оGYTImW?e8v"}szu^4 G>}ڠiz\BAs[pjC?+zE?{T񻴱PD1`W$oϡ9OѝvmEw-+E˼6=;/?.|+>6;l]ҋc?x=@m\. y߸xYF{MtBR=z,7q޳=Z=zHm._^tB* [VpXO g{4ǿz4ItI ?Qus7q?^6%3#MsW|NJKk~yB婊0"'Y=mo?oO/K1-g+T- )1;\rLaϴ6gAΩ_7o(Z X\Wg"3$=r3*5`8@c-Ǽ!/=P`XmbroE';sg;gwKl-k~9!~_s$s@y sО]o;e`;! ˾:Ӏj~;`cSQ|k;_ `'}?Kd̦C/k,[*K_3'W2xN[$o[TU`7P`hPw Kl}c}:MߙעVcӻט/_딨\H|n֡Oܣ ?e zd ső>Ϟ<әs8xH׮/Dуccn\x dFR&O/vAy/i] qX~EKK66Qzv`x49 X_~+O&cm1`dF{+> uCL tM\({o9\Ǻ!k:|7ҙ벶gdrx%zN{s2ѧiQk:GΩ<}gzC><}wIUFT~ }i^ѽ:{tv~ gpI/';-}z6zVa?6W턎?p,I`JЩ>9 FO`]L1:-/ѻ7zvJ +2s̎΂Sڏ Un–xbn¹uǩTKڜ?{hL. Lsvtbs0_ x܆ߤOht4OLD=eNa_IA5/];Χ#Gl=E$z6y&~/A-zuCGW!Etx>^(W7=:8_ŏ%ڱ K]iӿ/Q>7A?D >k?}l}uiC k_>Q 5a~5/;s;>VLVa1h߳wG`'GӸ!,` e*;ghY՚zH ;&1zlv0_܁ 0 _EUv{gh hq^6O9Sg=d~v3'wjk7gߵ6f7oV8@.<\KsȲnkXhsmEɱ~aKf]O&a[?9nEo]?4σ͕<x:[\&Gt-y l d?+W[ﴴod?@ snGZ?^?~!3% r )Xp1qik'v矙n0/΁mƣs/O?_@}_hR;h^G?n+̍ϽGѱpktWtoy&RCѕkDyf<.z^lWT)~ggs곢Jg:.bAs$+њ3#1{vk07kb`5/y߲6Fo9R8gel 4mEĆ=6^`ct)#WTj?@?J^>kXKҋD)ӷnp[Q^3N ZXϹ40><]|lYq_C7>Z_lp@ġ9t^27[cA%l)c1Z?{d?A0U$109j?LoÞ߬Y62_ucB[TO|iN?\Z&GNX( CR9_|.3>ѧ,OALՖ1ԄVZ]Y9&rfXt1aYHÇeqG++*'խ>;wAÍVW77Gq#~?@?7Ns 6Ŕ\Ç@.~nFxU`y޼٤_}V( pgYW3poa}D_+''c5z}R1 kg9Z]#tO׽g'qWp:%dkcu7'=͸L{F6O lKV 9n0SzXDǎcwb phe9 3 c:su$W ss+?ؕ'd>iVV_/*>^h ^e'ddfca0_8'Dr G\ "\SQuUIM- fiey߱ 1Wì߄eX`mi9a!\֔=_>g2!?r6_%_3wBsm~y&.u"ޚY+/Jδ_lt-\?kJnem?%o1;.y?cǬQAmѾI|c'|נTeJ `o!~7'Nb _M3m9pfS܄1́k|伱qߜf9蟕>;hܬa}p{t0omll}5zxc($vrrL'Ob1>U]]'E~T*]7oϜ…@ھ5F{8N]Z^ Ob#+Ч۷a1N8W!$zll5m?Bl 0)9ο .,bۏf@)S:_1Da|c)56nb,apF^أsrS~NI &pzcD>4Qhװ h[Ř>xG{Mlzǖ=lѭ?`_C-.D=2?vkhCh;ʮCix8c]trlj96K}QiA6gIO_} lГ'ys,s> cObg1~Rsf-W>\ ha举'}o5eh󐾁f-yG0ρ'ȫW96޽}C~ |~ ]O|:Ϲ>kpݦ&| 2;(# Pұz^8G3CK8QP `5Z<&*}A_u 5xåjChZ t{>o~N=K-_XHa+|mq7w}챢nԒؿ٪h9?ͯ?jt+:?@>#O]Qv#=qÉcwr~.mz#7GВ;TC;Αz)wD-[lƍ0Ksgov w>T6BGhʄƖv39΁qx}v鋯EׁdlfNrf5@^g'64kUh9V[^z]ӷ@[rM3kN'ܢ!߿/g[C>  ?4a@hb}kO86rKۭ? {oG[MO/>:o?oAv2ǫ?2]~CSsxtC{z5;7 4_ZCe?/Z[>~:}в~ :uM0Ox(=gP+;77757*ILuxL/a>3/]|vB91gM# p⨜ѣ1}ySXa[(}6M$ Go~S昝e?_Pִ PHXtvi}QYWMʱ߾}~~O=_0>Sc9s e l> b;;1m`R^9AQƾbk8Qw}yA[%#/㰏?ӥDrUDG \?-%+Jl߭8}$sz+w$9*vC?Pwn#,HqK=un*:}K3s6O,nz}<H2~m_`LLI/cYV4a9EV:sefm׹|n]Smlc݂? V{[[ۤ,Fg&Obk9&dl/q_LOOm?g9^9^%} ?A<]$ע5O5럧X0L4`k6 q63ut`^/WS`[c}(@ǚ1iONǮŧOY>O@E5<1W?KcZ*?|l]&|ف&߼*i7>]t tZ~72~Bnqݰ:ݻ#optZ}A|77 Uklva(Z\㘔oٷ-}5k|%̟QA= %CHc/䬄!;:`W)bE)\WQs{t)^SCp<<C m/~ ;Nte- (SX L׀6@aNS/Oy4 }KD3o߈#{Zѽ8L`7,&hO~́ۻy#>Q{ߠݯAGtX&Htdyդ&ݿSt"^R^8ǵ>csrm%y@w0~6$x O^[`a[fx XiqBLsOr]ܗKmx_$'k1d|eoN}m=AMf-K5E;4!Ԧri?/ʈ9º`-X35p92?2k+aGoɹOה宗9f2]%3E`m {ntvv8Lҟ+uX]UM)o9,s`@kk_,}bbhȗR} \VkC],ug}Y]l5EE;wF=9 =x߫v 6@}'5Y/~|sa=6T%Ф{6&c8Ϗ}}~ǜFr,1I]ޖ`O(Nok??OX8q@!Vb;| >u?qh{T7]nSk6}eaych{]yR)tcwQ~ ̝ubl8ְ{iGu{1=6)缀_i;rqMDG]oh1xAJlo:޼>?ݰA8k̜y,WOhbr|?~5o6D^5[L>v|Ա3+zuJ/zns^Z]\ǾZw;:/F'zn6W؇c~:6= >-eaw=\׭PT e `o;oe;z2MuOwvPgK t_RC2ds\\%7LgW45H@ɹ ``,{qX^H-P8בcpkn?S`t_`[j;゙I8OkkKR ӗ8 xަoK8qck6ԕ+1>l}: ~DZ C_볝S^is3/y/lɓ˗=y+oΝSb'|Fz4^qy60q,`39H|^:ym8.DУ1N&vogcA.u8a_k?JujV4c='zuH;8yn\F[1ǟ8tGCl(Wo^$6OqnO?K[.הfLi]?ڗk 1,K-Z5{oc AF]|+EgPZf&rEF(}`xQٵr>$oE}s >n><؈utLw01~O|VsLaE% /`uVo?'sY搵Kq8c _AKFKCSޗ_eKOfP S^L_6??2f?]X 4URfON3o`W7/HiooN @[Xy^uXh/9_M'7$oll`)]kB!ce_b. s|g g-s J[ 9Zr ?m 8٧7TGvuLG9l{peE`?Rۊ:=yRǏy}{I׶Oкێ|u;yu;x@CMNtOOsk}8}8?P\e>S}gGǴ._qMF s7s׫Q_ڈ6F7g?( }d9}8V^ Ba8ޛ?R1 9/Z}"*Wxz?hGݼ ݽ}C8¾ ~{;1/xv?b3'#5싲,t9ִN<НoRSi=ϡ'8$tTL'T,z]8>| S-nCXkhצ>I]XQd[M~? 4N3f`kI?s_&y3yh(_ 5Mnz׈ם+?)9W럞 ~J?t(ךجju(X?4D;}3٪)ߝ=,w_ .6\ɟn$p%N)䕣I]B[mp51%}ܵMb^)[de-`-k: 1y}=3vg:? ×ϱ[訩z_mC>ojs=фl߰/lgѣ\'?5k߄Vf|-oۇg'ec 2&<)ݹ23:^VCu`3{쇰ޏ^9 7ox}\ Ob>tIqu'J.ލivROvhm5:Ma Gɿ1!Oe^Dr>9g:|^̬伿)kڗRd{779J[sakǴ'h-Pt{ycrQ8v[7[۵1ꋉ+p_N}qg9LzL_þ ?8g/?[w8E}zIΛfjV?? v-1Z߳RiK=w2%`bіd~明?jslLKpb%>k7cwScn^/w:M=3;}'?kljS m|Wsfؿ }-Ck߿_26zzlh[v_tSǙT_En)k =W:Nl+ݖdb[Okh_3o&аu0*{\~A3jsr9[܎2lLdz-g _[k}y0ٗ3i/ {}}T1:Ĝ4P|\|67kA_G-%}pZ~ 58na㮒6k={8޽㜽:oy\뇿~mM}sSѯrs\[h^~? zstG^uQ'm*VkTlRvʯQ]j) cW}{е/{%_Ɠ6gt(.w<]o}}xucc.ahtKz5ήS~F.wp|OOk=>>h+xz$/pчho1s*Pgua'7lܧ_ֵ7υ~0I.=}\=GqSsm0N{MZ;j7ͱdO;p=1{tO#GJ3_Sg7=jpiuƧ?h Á[_c#w.D3KkSMYo$dֱn#>Y\_-KퟌC"叽_\wOmJ.$,q<?ark]?_m(=^~?wl6M Ju_bޤ6v팝'gmk+*_53ߗS0 im 'h'spkA?xR:N%mjnkssHc:rBѲE7\1V$~~>9Hv19++`pZc m>?}## sj-Z |AsBE8g3q-olt%>Ç7X{=x 9֑1O(8G|O=ڱ#t\fw6Rn[1<]pt -) [SmbECeN<|} [+uCr})GGi s9_ji;5l=u+はUlIEm*lPgeJuE5:hʣh.hLyI"r=s>=>Nt^xSyQѳ8{9 e9sf:X#A2za,1VXCw< N{*|Ei\G/)7Yu/5!<G}1aj5#}zy\6gׇ1/U4徿N|sa1I|='a?d/1*2Hk{fN\JypER`x=y0~_&oͷUznն)]GlwjR[prkIkX65uVɹnfWl30{iདྷ5lyELwS[(75#Mքױ{=Nc67[ؑ:<++9Lc+r~.kwp;mO}mqYsyݞc^ cCI4b7-?3yӦws#haϞXrl"z8g}F(1H6|\68E9'NY}sVN(zqK0u(x mR}C+a61L38VA!/F3UG0Nakօ~Z|;m$?u5nⵗվ O4imFFGTiaJ^RH\DsS?>=:JI jӣw{}`a -ӗ~}ڟm'zM]'0=?IHW~S7ʅb_uPʼn3ڿm^Ŝ[q lsy_xa0ȍ5J_Wc17/N*KQc b/_KcHG#+:s'ڎM?2~Ko>)%_ekwK'R\ܬgmO[oez `^3!/i?۵_9TbܿR[q_wr9-.}}}?gOZC?Ճ`% ۚre_Zf}pmmnj7|GebK6ImW&?ղ_كoB#YHCw_@濽o[{"q}uB>䘽~ϑ;0YǞ:cl!?85ӧ&*]Oss{#y vںݻcpL!pL?u9};}Gfsa>hvYw@̿ӇC!dV[ص'|ܵg6%U Z?m=er:o|I mI?sr?9>^}h޿>06@Z߉Gb-56nO[ 䴛io\?ѹXiKOs4Ow,$3jm s_??M_?Nve`f[grOH1 `1?svN}8?s뿳vusmoc@{ov 4Fs@\"|n v ߯=5Cخ*Jnd'OƲ^*e𶼞}n Ǐ&'޿N]NMq ^uDw*zؗ)Nj?gEhec!{tߠp:tG;a&c$I}wu77uܥK C9/2Lq6lrMLqd cbܣ;{֥4<hEK|A=mӇ-jî;vOC߃󿤤NW}uۣI\'cwzzn{x}}pr=@3h׊>.r f0יWb߫18cN=.O[c9oץa:a``}Y;1cy|hCѾo}'ڿ̾9\;NE+Gt_ Zsr=o?9{ĹLw|Վ#}g>͊g[}:fz h gMGǔ1f?0_r_9o?PkӾ߮pGwo:5={4 Lsm9s=νkdj[ʬ:.*l: 2o/vOU.{n 7} Y ~0&@1h}:67JzV\ŹPvǹ}Gu G~P5`{t@8cUwq<I^H羾`S0ٗrsF$u^Mp! {%^桭 &"sg(5?aA׍nJ/ŜԢ=kl<{Tuj̬<<>}KϑO3C{&9D=";Ĺd˶9vYy%8˗Cqc Y˘Wa~88~Mt(8ݠ8GÇ[b_@{7 1} sOjZ E1۟!Mv:iqo`3LvsbL_kNE3>\;Y֓ov9o5a= :։nY {tDώf 徻[|dƋxIݿm/YsRi3&& {UK01?rNK,;%Npck:z \? 徿,Xso[k%',X:-XIs~%m7e[|% v?%еO=a=߭9Sc?; ^mcᎃTSVK 3?4cG{ Yrn9A`s䤛}?^][^u6` fdvO|xtS5z4DʹϞqwn/!yQL܀K>2M._`{MwP.]:9/_ўu:sާ\+g9c~6>-o Z @edOb{8Ǟkg;$n]ݟșmlVp?p8'޴')?t 56vgOtZ?0*u c>9s@ דu8'K1)nκsB{q`@:@8rclkU!^_~"-Kj\uY{AdBlb)p߫8vl  L@wy^qЃ=ң'rO,uKصEqd=E@@0(5_%sDcvD,"ρy[]]=Iꪮs{ϼnK_Wp;K9jk+h/a[\[m,}P{[7 >g]/#Oz5P<} )L_N#+zm`Z33fn؟qWѵ>v)>[Z8p#:AA kM4j5̿lwLiAl ~׭W-z>x6?yh 2"0{z̺ W0?=:|{/mz$PyԛSͭq~=Vu~oz:{C{R(OQEڋbg۔|ke4֒}g -KcuR|DI`mb'r>=bɉ[^kC%/| pu6FnR_akQJNu?}g9}'>Zdlp͠qn~Lya& drY:}}]-!s˛M\|`)%?4cI<5` yAO9xC@ۃqp]޺e؋/8˗gvߺ};R>%l ϞM{{>3ar?{\Ln_GX^1 w V0HpC}QK2n^`;l_z K+RK#߸OHH( 0.3PBk{yMXHܧ9du{fu5­]cU GWQ[uzwS"<}X6q}?xQ)O=>.%Tlz>u>=ܺ(qv:9yqލ~W>ʱK_)^8clhzv; QsunoPMb5q1PuOO^7L0'ߓj/X-➋SKZ'c>{a6A>ڿ<>7zvC5'o8NG}f^dY_~#-^ 05l/77mgcm3yLFJ}F25}.[@75}`u}縍p]u+mP90k[0O~x\8?lm;SPv~{~,z<6Y`c<Ώ{VOF{X;ׁ `fyv'OΏ)Fbg^-6! >$M41v^|;bop ?OO?~LEͽN. sXo^xh$` M#/M }SB!]/]hԻ܁nYgR^q}he41{O}cSC\?:%?>LWԊ.:#ϋ/PsW>x9Ğ1YK׎L 3{h}k.k_ޥEQ# Kzw21;T 3o~̂Iz' ?O'tБI߷qR\uy8?@7nNw 1nTG9 Y_>?ln (`r9Y͏4۪uk8EqQuF.2| ON:łƌHXy^&cMs <~fdyjypSWdz1~ pr!>ȏYI ݵiM,<'C6޴pF]p3kh=u4N _:ؾ'߽^~z˻P`_Ҡm]58I]sE7 @1DS Fk(0kjqFyqhbby-3q|l0 rHS,sb ^_L&{ηB\߫~Q7L1 g@.@`D.~kpj#eeȭ!O7wc«P[x)+Ǫ]|guIˍ?ObEj|o.֞a5F?g q&Ng-P)0oۧsI_{wO6OWRLKc߫Sk ~g9!q=xc#ɑ.e&ۑ8I#}Fc4ޱ齁)*/"#2ݡ P&Md|?o0c'['sbQhh6-Z-ݶt@Ϡ  cP:еr]7r,j-p)ŸԨ :H; f'A,_ſ%}~3~xl n7õk9 _O0?_eIcW2p ROkiBs4-Gn+/m^A6,nݚ3nS4m8?%uƝOJq#G- _L7}K͌ݻ9 ]_&]gn޼k4]F?y,g9jyX{|[™\>C\&xhoh|ilZzBca/t9" z͝X?zeeLoX@SKq}GρoX$ڀ)Qhe (ǫ'>MR=wq;Hc ,X<}N4G֦y69_* 3&&'ߧ.1ZEÝ4/)ӭޡ5"HOo_Q{m|晾O2?Is~L;9fBqDhDyB|db0!1 ̹YDCIx1gw=AG?y O4Iyދ)wC8_6TGb0+5Z۵1JJ]Mi.G3o靮t6`||Lmܻ?R̀U<_3taU`cwxe0q??I (?;x +T?*vN8o`q@$Is GiMg'c4퐰j|>ҋ!>ahzc$ߪCk;kGwQ5,̥X6!F>Ώp^f ٠m:cnW_71=ſka.yZQh\<'ĈRn?H̠8] t=z>zƵk{ dl 5g_S?h1g x{{@伵,{+3CFK|ފ{Uz>m~>-yzmr`&y9[ z~`NkaFN`G<1e4/x@s49ߚN LhsPIV&?إ]<+ q>Nhn:|  z_:q~ *ޯ2"jS*|c}xu#zo࿞YgG#/IH OS8L _/*k@'q7իe\Yk6s˙;Ym{c׍XecSjǤ[F#ߘt}j$ %tGY/|sg}.ss9>r54?*;}o fCWLdy_v=c aЄz'3P'tǵ /2ؽGcG|nx7wIߟ?'|}"`=yk{9 ,N2QǸcH|1%=RCnի49&k*/F#ٰ=uxd˵I| Z4&<$}}0(ҜekeH{lRdq"kh4jߒOVt&jt.wD=OYZxm32O};XHWTSYb|d\dwi.nHpNIOZ$7 @G롱 (eVuҹK]gwc| (G0ͱ}T7K We[z'?s? Ur14'|_~EPA/? eOZcG/WxpZxl${>=0?=`g'!~̚~wcR/87)"tLbmao-?OH1u{3oXF[I2ŧM-!88~I~x?}* }{,׿ Ͼ7K1g?1~5DgQƘh0kaD9qbWpFr("'Z7'w...&X^;_l%;}Bpxg$WQ_>@~&_eNk6 ^WWۍ>FÐ9KIkb(8۸ӻ d4w%~k=)+F-_rl3Vy -e緿xd۪)h+~Է%_R{M++DssgIt+De}il'm-46b'yVKclLkD$[vH)#&J|Fm[R#;.P&Cϟs+?cJ|2ɜ$mMI>C=}!ǘ JrQqEg+#X繋~KW8t~Wg?_cVeQ?fPS*N'%@F2?< %xb#U+rLU ةs?*11]Y<90~*W8 cpq1yk4&Lwz~y=saq1Ó'uD*'LK>Czgt2=Y/+KND',3Lv5@u=aZXvHg8Ɠ?fXK]<@5N,9uu+$P<}/xzs~I[$]?̶9NF/KC.Y| -\C;YF>NMGafKB|rOe]0Y}2cm^'ڣIе-t]#1qx |Z/$fN<7qe)~}ugb3uMGTWui r2w,Iv*&h:7?Z<{ \?Mw:D2Ofdly2!Ҹ KtctolZLb zK|=\WyxB-?H|%Q}1n1C zLٹīޓ 0pa?x+>.5} mJzathҚ^4/rI>'-Mbs#+˗`&MSgg9ƒqǘY --i q=AKl$_rd @l l>/$ mlH#foH y.8}y3! ?01-uc!?7$^p a<Ҫ^#Vr,Npdq&r|~zk*猉urlxAm&oP{7|&U#wvOiL7WNC3|WWb>{]lO84n?cD?iWߏA.vX/(0#CB_bwZ_jsf?wrK43æX1Kf?'[>0 _b8=q6=b_+~P-%bwF:Rye?bljW_eS`@0?oƌ߼YwL1$Nc&[~93˞<`C^~⧟D"{ψd&_3fx+]+$8\cعX3G]aZvI^Ngox݆g^d ^_qNߤ`㝄wHsz'=m i-:I€,oWX #>hL:ظqMhkxʊqպ ukadXJuK/BZi8paEy3֞ύ y伋knli=v2:s:Ҷ_MX9H lb9UƏq? IräxzAc9xytI.~f|OYh^_#:~Fϸ}8.qx ,/FQCD. 󱇅;d-X T-Q;? BNr\ߣ^>z߿טD=b-*;oIEw-χ@ ?cҷ[8&!nNq1T|tXI޹FZMmBb<"r{ؘ Ia̩9ۿk{ágb~O}?b8p|gofwtPgcy(bgcZS8O5gw0MܧX1W_c$Ocg&eV1qW{1l5l`<@T#5 yWtdN!hy9w>0+ `̉'YNXX惱%Lm᫯Gt\CgDNSqcƪ:f||)I;s|Sϯ ҵ>1>ͼ1w.rۣW߳/8pϼ4g\fͲzC3_v.,]C&Qv;Lbߺ ?1!=qq}F#ex=ZY{Fb.>޾Np5;DCNOc/]se_/_xvWxA"q$1&Ѐ5_O#̚)~7O\&o&;~i?G |E|[8Kkɰ+ˁm辉/{c| y+g}yJ߿1*zշ1s(al>>ƾ pUߔNvFP4~[f{E1Ϻ>￐\@u~d*b/W,z}PoW40m8>z/?Xwb,U?|ѹ2YZq],R`cو>H~^UVk\8!qjΨP?p io6XFMk\ aLp9Ջ<'߮0#9ZFƝsaڋ3|ŷ] %b;?<<5QtĻrS"B\duG R_9Ccȹ\[T / zwrlq(1#}Om+Ov,Ym] LGz3Ke8w.ϺQ[|s>+Ͼucܿ~Y91LWٖ~J^R-TTXA?k?9c_ O_iG 9VSxWR{@ {y2e9?bB  uU/󕿯bt^`n.#:m{0`ssu}צw}ִ<ggsgfĴ [Xõ]@Z!bhɻ&+fgm&g[j` LsRg#\1s.-#,]ojr?&\<3E>l,x5ƶj^?^upR~:i6뤷HZ~ё/fs4-sÑ3Ӹȵ1I<ʼ1[[wp5#z 补(l} M};rZ#nP\f7ߟ\o E#^N]T &>p_}Ŀm )xXڼBG`K`4N.q?w/gpIs}=ÛS}]Em;_!hJ5}NA (D߫orH߿sHT?j6}Z Xy`pх<&:\(s> }h]>#^k&0/-M `l4{6ĺ%Noy 6lX6簬Qslᩲ6UI?Xm ?!fHO[;q 9Y'`@jz0@!V6St& }5Sl.Iï EsNs[4\?>lX4ξ{2#g93,f# IMpq= ;cɽ_9C4:'fD[57\i`aΎ!d}o-Ǥ߹C^iz/iiHf1#]ai]&i!sԤLxw.n66'ȼe.D#^+˟.XZ(ۇl{6BHxztSIWuaBΟP [?ǯ ) akE럧mm%?NIDZcnKSz\.P{/l^)7f#Kb Dz,[{{ `(3x۱$|cfWc}Z\XsbMT8( > @z}|Mx11?>z)(0_L ̯<{2@0e\kj'`]`Lr88mkK]C@^AEIϯOaEr>Tyc&~\5#t%|m##gwhи4v=]J1O?Ĝe3Z)6wqϤsޝ'4?#dK'/M\^`xd840Z:ξ~9oHjyjpX>CkCK.p|7,|>|v$4F_B־\KI@Пqa6]4~9 8g[Ws!7DGwjh!6m2po$p!?!ze,&}V+2rĿTG˙^,q\`08Ygb#;~W/|8omHMkv4!]t"/j?_YtWݿwx3Ku Ʀ#O6g*Hv}W4j?KSY9 @_bz+r{0 ypj8bO*&=GڛTU9~eE9V(~߸z<6 @X|Tl}[mfP_dv{j!/c;wF.Z,x/i?ϝ9~IN8n"Y_zm~belbtJ)69C$]sI ݍ ^x.9NA㟹vq"69oM,Xdpl<Ч)5pV @#( |_N2a?@OsρR^ma?r8@is&:Å )Nevbc#ƺ%8& FOzeO$ݴbm8ߛs_F<%\n٦9-.pI2pݿt?~Ms|Zeǯ,ma-HM귫;w?3E]\hև\a!Vp58ֿbk6N$r A>uE㷮ݻ":@Xgpt>;kw5p͆g8Gk.Jcq9ħ[VۤkpMCkZ=bM)ZNXwu֝A';~oSk'#tk r8;6jWao\ιǶ~&p'Tm*Lhl7OGz8=R3V)69y3}*wŒ{az ߿;_W2@[ }Wox`M^?oW.  T?J5~OT_|& HVlˏvoeA_} BvYPow?ww3g߯;t @۩6`6kڛ_Npn܄8.Y#us+ʺ /f>^/_lpt빋;F{*b){ d~ǘH͢4 SW1~ ?m!y'5}ö] zL#,a{+)bi6ΐ=e>(W\ <ʟg)3˦}AϠ/ŷuZ:|!r~مD6zmyW}Oy@X" 2coYvT `}]d~ m w{UhEsIu'O6滅Τ9>&8&~$ݒ^5ً&F2xD݋hù~[pzZ7,.2?lW>^a)!dz>׹˶c'lnSú6ӔkZLF6ҤG]GJc=y@4cn3%sH1#@ݘGR8ޒ)_<:G`Z?AD軻к?eӈ?SZ"[}"zɜ}ܟo;=uj??$.]pܿocݿxƿ|G2@q5ߕ~O]|s\iOt"H#HX^dʘ[8/}?8B|PJeWXTyD(D Qϟ m}Y1A(?2s-\1&~ Tm˓vWh\`9VR9N =3eaQ8YF#=z@|Ջ\]P1<#.~l°UcLן/mK _|]ݳN!} %5/oͨ_1R 2t `5 .QcJG V46RL_ 0yP6b_LP-h1sۙP6aklx8AxO h[>i{(}r%yoHni5C;u<#_OӖ%@l@>­ۗx򬋛l7'vr͇!>2pL 鞓sZħoh Ыg}<|rk׸zpNkux[yZh O&hNУ;s#~(:r֋6fy.s}|+z>u'DO\'*u/52v-ܸa];E_(x//xzɂSؤթM.HQ gH)K\> :ޕcRs=.@,K2׼'po+AH׵w,d^ypFz-eoÚ)qܿq+,s͟Ę;/*Vhjv[wq?VWܯP?HTp#LBV0ѽUD n랿U/j4_7CW~Si|@ֱK2[`}R>#/ߡPl`@ vr @Yj;PL y@MTjUq}c8m Xs^W#-, wknq!5d8%}Q(Ke(ug7@ciJ}}<ݬѸzTIR/pqh}ݒ X?*r?{ܺZ[p}H6p'w{D\uy裠J,=6^p59֤M42 rKjԢ?BnAl:gs8⵭/{|?M*#:glWG )U_cؿôX `~1_ K:jGR/acL*wGg Ių,@;wq쓪 :zlH.-٢{9HPg?kY6#cw }8ك6g76f]jļ?Wp~@K.Pn4yFs"XtqD\O_ `X5ds4|H9dQXe-pg`ah[ ϾJDc "G.rM@F;r <޾ᘚybM[gs1ڍX`Ua&G=C90xvh{h?2[v-T,x }cI~L^|&/Zχ/ɥUxSt' cYQZE]`+|+xQl'DST?yאַ^UV~Fs-~mDo~Ͽ2@8q_A⹜[9?r" ]?o W:_Fv g/i uTϏÙ ߹R-`D>0e@|w>}+`J{K=`Mo@p=Vls 1 mB} Azܽ`yI}ro۬|妧i!y3Š;y?%,ͻ8[9[ _?ony/&[<)HX{MzO&8j$Fw2aâ@X?Y<XA]5d_@/*QM=)IVWC\1 mR~V露U }-1sL^ֻOR+xaCM H[8nd\cct)-Ƶ~X;&o >:SeaRsuw9v}Ҿ~>O,#ZW}uG+ V1~9/jpՆ r 6\/?yO}GmriM1{MyE<6<࿩Q kh@Z}h? 1 `V @`9?gX,׹*{ `+%9'U0 WZ\'c&+>c>ʿtm[Rۭ}곔:F\ 7y/Z-QyT{P {ϐ\"ϖ[\+ 1 yH\[Pk o߫!KfLu=Rۄ']Gom>+%ںZǺѰ+k[u:ɩ'h d WKj>S~B` g`gʞNc&ݷndʼnܹ;S&Ȉi9BY^c??ŋ,1Ɲ&s{Mg\ˇ9>5RC 3c\߅O~ h1ѷl `Ь:Z50 Z:1Dm53׿/Qԝ8,Ѹ<(mGhʌ҇>&\.ak\{9*%#rSf@ T&ĺƛ7n޳ck.~}s?6>1.O^mھ=Yk %&_zI7Ghс:{=.x*{Z 8Oj{}Fn=&G yGO}7knO'`[>rX9-ӯd :@~ܸK;8Zҕ: v19sq`st9/sN2+5)K.obQsbGUv4{R|."X#~0H4C#Lua\ۯ9=dDYJ/ɬ?Aنq].${$Qܟ^Wk_c@o_w.'uO||_Mp%W@ }þ(Ѹ5 ;#<&P?O=z)tXV+&=y.e9- >4k/wbs,>/=@y;[ DzqRhy<2+cq fF ``Vllnr@}v?-ݨci|I5QM]jQ,[4'}'{^O 0`6kH߽ɇ=k?^W:|?^C$/~-`9Ԥ;ˬK9=@mW/y?co/1y}=,6y_,~'y񁰖92se9b#~=qc>ְzQsYt{j96 皏k?ן~XSYD^72ݟs ,c wToi| U`.u-y/  ޷۠{j޿ȣbCIl!kS· `tUu_hZϟ|+>2.l`ggq- gP x6rUUQL .qJ=rq69/{qQR0~΁{оmƄp3ܺp5xO/KxIJvwH#]lo`<="Ǒ5,#Npnwdk-XqWNϪs |~g31)\:پa>q̼P 8%o/~uB.)_+M_G.XS*6We2m|1{?|W~xDLW:Wn%> <S-ܗ҇H-ϲ/}nN {%z2}ue{A,' qM@\Xcؾjo'_:"?$kδ_HKlED; 0ʛ in>e8&ޖYk7kl1i @D9@vG|mlbq{k-7E@ޒ~y[ M's\kdxc쬳/kucsBs_p-pNc=1~w8Kc9Os|u V%Nb5Tk<{X=zT +%cUu]H\` 5ӎDž)zS:}Y? =+-'4j?k_ŞYa;ڹQs1@%?[7W|]%>د>Ŝ1 }6m,4K{1ojJ= k:e?j@ u.n6MJ{F@dCT'JϚM mJnKWԊzeTX/"n 8cgM=&!81D6>۽S<>qz7B)=|9FRJ״Muî1W蛿̳\se2rg{J}FF nҘp. o .f3sŏ ד?fc&77Exi/˧Ԃ +zXǛy;Da*I&od ۗeƒb]yг<|6h,v[u6>¬{Չ:>\1X#mD=hE~nJ|U?}s [Y9nUqڎ7˳lR_\ϱ'58.#tR˧tF):ֶN]Un W_% J}흩k@~eP:>i\߫~N_=iC,TSu>l[J~?1 1fNE^ֲE.  s;]4.w_e0~O~}do8 mKaݓ3kKlO&\8qzX DMaw9FԾdkد䴮OJ2%'Խ]IO: PԊK$7}\e}ͩR{D.k]j=bUΉ_N y1#!??ѳ9vEhl{'4m4MMZ_-X?,si]Km= sm tp yZؔW9-+?+hDN =뗇~?Fޟax LQkL0X&ڸ7=<[1xAK&x1es$kq|ݺ1ʞ(vOwEW ɄB)tc2EVor(po*8W孑uןusaݞת__YxL, z,A.OBaLͪS/5o_,/?{bcPb*06vj/2cG y6βW!/]?u^!Pm 87~~%I'\cf7 }5!6@g^|X\ݻWLEtxĄ{ 0^bd1 >ds} GeCL.پ:Mn1JL̿<߽I}θ\ JU|kq `o 8]|'cѦux?|0>HayDv\P/CG;i4H OBMiY˼?Lkq= OO%mYE4ײ9.eTgwp E}ㅞ.}loR Ю%㷂, ([2sc){@ټ~5)e!ĵ/`\̌9:wVeU8X.XZ6=@k/a_??Wkϲy S|Aeo?:|mºTBMį %?ն|{ݻQ;ך!4EJ};ɲ26e< {T{/(>ҳ P8>Puc@٢h8?Zk Β=&=s>X>(:_l d_e8S@Tߏ9>\S 6 ClQ[ pfZ.<>kӻ|t2I)Rt- uaƎyþdne8~@vd>c"/痎{O_>j,4 ܞKbt 'd c@Od[]9W%4ID+Ci1L\5o>> ܹcqB+b^l+,3\^ }7/]$GC__;$89,пE~>2h=ߏB؟&g&nLdak&k;)SC $qu~~[TkxEm(t ۪ӱL<7DLF\1Hї FL|]l\:n/qL.N ~+6vsҌm'`(nuccyZ1oQ_?_?[Œ^[i<g:e/ st0 kcׅ>zI@0,@[n!hz@e >՚z3}0I{,\! {G{ b9'ާ~ƍ66Zq0lK}h:߅}w3穓.G}!ٜuFy>懹frwq;!>f{Pp3_wWi) Ιײ鉷wH@4U-HAb| tZO\xno꠹\( q{7w ݂Rׂʻpx^Ȳ_y=S7 &͛d:Qj2Ue8ynmH̓>v1!oC .|H8I$3C B)}`2nj;C\0ý:m55fL5e}wF%/A/|"BF>?grz?NWW[”~,@aj;жGh?/%.fB_Zx3d Z ,u$ԟUi옴 ~/ y;BiC} w_/e9%o^T00[`y {O*؏*e?ܴN$u;|vla[´(>pLx cLi̵W/sql75v}0bmA'!ƥ}TB^A+?ujyeqTA CY }=[dg~7' uϻ}w}m;kjcD9{7ރx 0|ngF.Ec;u(VPN(?Jg(0{l;|`FU?tñǷz>wNm_ÞYj8`w'~zMO:ΡګzoclxwоCBۆjIOn-Hғ 8g -zo>]GY~pjoŢ./ XJ`ߔ\;؜rz_Zy57ׯ%< '悔ڤ"ϰ*(B;7j"(M+>LG{Xx ޡ|8 GB<@E؏=\>?^gpSȯ~e|."蘖lu+:YTw|SOy-[P T$! ~mOF|73gջ;IQF^'CQOݯ=#?@n0CCxw)O~ uo-ruԔf< [q_XnkS}Z es9݇C.6 @OdyiLWv`7ȯ ]t!uv-G [EԝL[x4sY;ȯ_"mXj?R{|NN`pN8.E$m;bk:/_\]؋-|U ܳm}CWnaz+?}^cfP |%O<G774ATw}8.~ `2$']sl{cɏ1쾿+R 1|kaj5YÇw#?/`.aRAw>gE_ɘvmx xS?{-ʯp\!n:]0e!OxkGN/0]N!gv;򃦩|>}Ax"qWT& C! 6hwGӇlOO??ڏO @+rl~x$}wg !`mP fG ?T )^Xz?[m1'H A=C^1kLϟ}=8i (`S<68Kw5)F^7p>Ec(t$%c/B wj!;m_zˍ!+M\H)lԯ؞x+X?5Lc0vM~ls<"xk ButG!DA;6 lh~<+j-(Zp-rN~6}Ow<NNX`VO6x:_f98@ ݶjQV__i?(\p#pQKB9`K gs a}rM]WQlav?68F ܢM&B78|N9#ԭhW? y!U[%߇7t/O0 GlH'!6ɭg;_0DDžmfa%~0~WSL>a> po~`<ǥ]:Vpq pzRb)"%\o} ڀ:d>@WhԱgz4qLn j'\h ~ⶆJ;Q~oj.2~wמ͹z&hcyׇ^ -ZXxA>n$GGiirJ@.k?40Írxo[f +<[g b:n5ޟ/)*_Mui.Fw9>BLqc'8'-iQ>> qC`82H9ސ}hx~9xچy!(/$9y_i \ܕhdG1LJjg \djx{lQ/8o97C~h{F)^}5F0ŗgdߥgǹel{}l ;X8߂/yb?N|(@L~2c Y=.q P!=lSbK9WgbzCm]xm⿶=Mq]~wv\@>ڔgqhG#"99g{&WuwO>fSčQsޏy&V9@h#Ȯ8@Eud~%ӉNe 7l/a~99Mzk~7 ?k~,(1ڟl bssGzj <>FmQJdcv.@{"!>@ K (_;GxO3ƧGG {s2`[_@sQ-xo'b!1Ţ)otcz#N&C'O qݎ O!_|YfRo>7񱇏[cpzm@ynOC 9戾0?r Q'xO!؏B% U" }v<u [r8 qM3d/Ovv[8i^hk(ni WѶt8+;ċ Syd3F?^aFyhփ -Wϫ"yBc `b~?oܱChX_p[6SHEsla9,C?d[l?|C1:gR ?uno}K9;->yݱѝ:=bC{]ʜFX ;xju$eqM?ky]s|p>? 9) /tY~-Fosgp0zXc@R\@י s`hp>_dِdWo3`:k856X> v\mu~y[#ŋm~`(4OڮE\@Oظw0s?0B*3{2;mŦ@Dt9TNǶ~ lj?y) |ynn$|E7Ֆ+`7اtC? m7x2}hlQBF/T'3_-zwG>*A;p!HZ)D9EZ>Z'r^+'F'uh,@?-DHin!yV>+;f##]%e _>ܟ#-$->W}0#HVT晊O~VȦ$6b{t%L :xO9{C[￞9m m;$W}]lvޜWDhS\߳%7Rۆ"uHgP1~ 6ɉ Ah3' >ְ|#_#hK8/d؏6+P6er9B?S4(cFFkƏz!\W(|˱u^gqh{$k@9@#}8k`??ճ|ol5M{ z]*ˮ#>rxz>y;3O9VzҼ?[ؿa(kw91<_yFofcR=3P0c5Cr ',k }lGjsפgT r.2:r6y 9Ry&دJMjÞ9C?J|G99F+ 6ڈVWv@۟j5P`mdt-;w'(i;s`K)8TX>7Sڴ&Mp~O:c [|`wwNc@j [^ j .v@ w~/p_+8, uaqϜjEJ!\~4|ק c/GjS =\/G <Ĝ?&jS o8ë=bO COsPMܟQflP4ARNv!w:.^43gbRǦOq/#oGMg_)s)&`k1q`>0~~C>kb]?Ṁz@.S .s^[/|?mL|'`|p. hw}ezlsmml @<5zP؇F+xYr~;3Yۋ^;/>RޟRƲԡIZЕ4[bbu 5N ߇:"Kq}OXZB^o?\ẏXtn@(5%U痚F?+fNn_E6wȔ5,6x{ʲؿc~*Ba@\pn㺅o[N~sOdbXGw:?~^O?wp)`^K.B:QQ;oOs_֫9KK_9F+Af<-W؟^j$K~B??,\ j7)G"y -R's1Z ?ߐң? q5lR=`?_j5*~yhӚAR "'Y;*%\O)?9FHsiҸw8Q.;x~@yk|l P\QΔgƬ5$J㪡ym.sF[S_ϥ:(_ЅY{XZu hs|leK//oO di.Ds~P?׿OnAm״SRёruiw\%~<; gy^o (kٿ*x>}؊DcT#j{xj^ڽn>A~eQdm}h]_xv` a-5\ nT!nJkQ!qLVcHT}? /Lh=?A46+%G:& Sa Mr 29Kx)l>{"AkUК5J?/dr~npw(ψ?rSIuTN$.)p^ldpd qZкcn.񱅇Kl˥svl7S,7mڷ׹m'C[^Cc5sS# Ҹ=j]ggЯo^9'{IMEd'V߱~|c;W:O\Omn }&+`^ t8}~<,c= }6>>Uq<w18o(dQ6XG<<EoNRS?13આN7O)Ink|߅|l p,Vjzߦ rWP|Csw4W!t QW^t/9hՃQh%ǭ$vR}:k ȧJuT)@Dk! dD)3ǣi[i 6Uu![,dKFA5˟+W"b?H^Mu!ӼT=3浯@0H _>T/q947!T\5& 9!Pq ܟ#eG{wE~U޲Xƕ c|(+1ynaM~ 8})aY6$LA[B *چ}>q߹2VkP6~ߠ;_o7ۇl~P<,?V55y3P'?}³Cz`VSKuHϨM+y=;~V.i8m>ߪ>@6;H,; $3]w&"3r kuWWy;tgo=W7^X{ssJk@r^!~5b쿺zAZWQ9"p}I*\tDkx>C.G~ķzB\- S7&f< 4^ ;Z/di` 9m#Ϛ`˂v/&8{}t=SmqCo|cM?z?>5<;sʸU=l>OO/ <`ś#G~wDh.x"y:_zp|>>9$0P&61ׄ+Gum}'ݢ9X\m7 ߣ|DZ^Q vWz1&_5li]6kֶ g8s`М!ܪL!dup~bˍWaֺ_#Yj[^gkl߹33Jw] K ?}gq?ᤧ BMkL5k'{XsLӚZk2O|Py >]?B`2N p;f;l=K Ӈ$m} 1ِ^ڊNcljj\_ =cE1V? `k~+=^qyz߭^cltH݇>G7?<9{C1T jfM` (|V> Q9gQ|; 6Ҷ%l>TH&P\C<'rZd+Ƨ =>(:e=LF>Z==-yM7T, ^k âmacݹ`oxv>vlll`ܣ=EZ \eCC(m%> p$긁g#h8qg1)Ed|hڼ~[s۲o0/] zװ2ieQղ5NN$Tj `.mu`Ua?Tpw_۷+O^FZP?՜L-H28rg]/pԧ{xy2|ez[ĚP]`*b 5Cظ:T8})l[ª.ak:z #}x\r|Q!>Yꜿam}ڿ0t~gDgzkdyzkBY3xo+6"7!yCO>>׿c|tay(Ӛq.pf.>rl;K4Fysv%}~Bprh]WZM8\M9@+ :{#K+kZ9?+W&*Nr㺀 _f@MPy5pl{Oh"hV"&')s =Bq6GN˝#=hfWsfz[?}c5 4 Ue  ]xtќ=wz2U?5Oy3#~vPP(υ1K}0[G? @=5?Ѷ8wLo~~ꉌ6CMWa?ˮ3iA<܈ =]=p7'>~}dzOy=Wۿ_5z3]+= =ӡų_(k @ (z1Z6~>wuQ·g90}>C>׻|@xy\0 д̙LMdku8~h׃mceZ-,(m䓓1l8@)ysl? :#!WI:l<\mz9@```\2 ccǞZ7H~(Us??9xqAnnT_揮}b_^I6{*A!c0| Iڃ?Jl'peI[*A8_i)kM&O6ծMm,k 5ȯks6״0bbC$i~c)/}Ќ ~%GMsR2`txz{0WuIZk7ƀs?%9`օ.j=}_J8A]Nw.\S3[LFPy3mfH.wa_{ytDqy`@%]}sٳziOC_-krM?R_ Zj=M۟^㳽:mZ@Z@5%we0A & \@ yi.dʧ6~#8Q[췝<x| 3Z}<24χ5.JlaMqCҺ ̷ Le l2pt t=vS{ʡPV7n=\/#bb"#G]WtE|-=}l9okyO봶0Cmz.x(ynZ'}_*u廅}|U'|dS)'2$Ŏ6|ߗ1(Jknᡂoߥ'cKkaiΉy%ş'hOw0F?ii^o )B 4y3T#Xj߲ݏm=Xݵgz)t?u;bw~iiWZ2 gFx!u"uuuua0Kqpc@ޙ҉[#z#^y?/i͟VbR﷋+S|~;t >1q; ǽag:z$К5Rm=1qW6c8ϡ#a{SMa`>#?8r-uc?[G@^c5)6C:UGh3/hr'POEcZ4KM~Q ״^볾C=2W:12߇[)Ixix9Ƴ_$t  1h:P|>yOCT ca|$U4_DǸ|%"dma1k vǞ}5fJ='lۚIC[""qQ]#fv#8B_]دʫzqcg%gTThO~Oq"=uxҾk1{3?^'Yg?Cyk<}n|trs Ck:= ˦m0Ȏ ` &!@u-\KeS}A щQ8 h`8@stIױ'(J~o/=SNZ8A;yJ1 V. s͓3T?'IWIuɮ.;9ͥ֞G}0$ct޶ ݮ.tq<MyB+!6&$'gaN߅?/9ᦲrriN ?װB_#.Q `?(1 kQ1"FFx`v|8ןN1x`PqX] Tgz $냘goyf9Kk1!=W,3MWǧ yΟz?kLޟT?N\|~c'a`u%&Н hcvM@P@Mwyml<=03.tܶv_  k]`Y \. "vj?Lm>Hxɷǽh 8:\㓉pum;,U!3}bRTg%KS Kί(`0vO>W' 9|?S$k_|.1zv}f;Fz$//!`?^ֵZؿY58O$[ڱ]Ce|~k/.k;Y'?9gb7s KZ82o@kP)䖆삜dOro߻sq@u5|Y;k{Â8lX18ӎϟ^clKޠ@E!&n[@p`MZx6/~"6KQ66W&8NѾDvX2.RS`,j wWot 񿤭-o73 D VqV\"1 ,VʋZ?o޿+~Or|nj"̇1 u 'V梆N/kX#OЎBmJvXƼ|bli^ 5lXT犜P}Wc9b|ҜtqD_u9ƈuɘ;kW{2(9&?NJ)u7|ɗ=g~!avSۿܬmZϰ:߽_c=Tx'xWq>h|&U ߇7a9f".z+o@%g͂oCM@ZG%8@}zV?1vf˹#1ߚ9(1Ǟ94pr TG2#{|>%߄Y 7Kc!md w\6pz^eG( ͈vd;{\fB@5b#LjT1/֓y ;SO(xHąs>(|ќ5h:߯ si'W>kȰ?B aYίYWZq:%)Qqm4Ю=#y@t5|G)~(> ,Z*>m_y^8N0G08&kcy~&6Csh5b Too@$cDmrڵp5}/Xr}i5$> (>^j;A0l(V=r4AӾ:Bl٢~zI`E}2, LP0I^DH43jмsi͝G/pI`u_oix}ˮi-S$@9ͷK aZzEhec?V9~9+,|7^WO_ƘOb>ʛ~>:>'pw'y2vݬt2?];Vy6lr٥19H)(v96eh#չ1Uǣ_AW# K2WKxfwj>bUKW1㣋c!t!RrJkw$! s7 ~s!;yy s}F;s@1;ri.XZ*+q/sGC}|-d- ˱yv>r.@6SzNPy6~N~Bܢ^"@۾O4L:91' Ēϛ5IZR֏ʱ"V 󤉋a{ٍ^SfGyOBwD=Fkswpw.94_g-d쫺oPxgEoGOxg`K6?iOvrq7-wZ3T=[y}Okeu~v lt _GLfےdnֱt\wP@d*lZxAb Kc%SToVc!7<-~U^_inaky4=Ys&7oCvg&zr vu~aۿ[oO~v;`?zj^p"o;rmC>}}+_stNwRțSMR@sut]v\']k8D!zSvmqgh XL}`h6s{vNŵ~A_׸بj\S[rzvp F3볼ˮÐb$zAww p"Ͷo0ωǩL/v).Gx? 0}Fn.S1''oNmN,y'11ƒ}ER0{.pdA.8֏ijT :UsVƺymd/\w;!- TopY;b +fc~,qce mњºOp$Hmzd\0ߏcdkgwQ~|~-bs؟v1>:?g߮?9e zI;{~3x@xb8 sZK\̯=]%)wpHUT*qD πO95C:2 tx06)]>-v+eCco֥'~]}.kGl;/^Sc|Vpe\8||$8^LSe"k6»f=9v>?c%a.}z\g?c>`?O_|x`:,03W-7~)M0Y#d~>U(8ZGm;fx''7lǧ3f𷦖B0R'yN-PX,cYu'oO~~ G9?IűߓU~5J߀2???mt ^ z?&ˀLc|\ް~&.cB ϫ2nϨKp ._3߾z`MU8-8n?<Ωoe_mq ~q /(j/#|[}:nqa7`Tƍ 7>g0]. a7lme1 u!c*uA2e1ulOgj22yZtТzhsEt@7;K;͂}%siUJ>VmWhK8gl`K@?HX>S2uJ? qgkohA5.T=$ a/`/<7cE8ܐ\'s~s?`|2ӗ Qu- de$@e0Hw=;@?˲TK- oWO^N8 DžHWy!FMw Gx !r/INı>oUs p3˩ Ϙ =c?8ghq;4T$fA |?//0b?y籩Ƶ`?oٰ"].p!_NNp~; N_ < ;4n`ؿ Hp2*~J]wϡ͒4ccI5X??N&uoy@l}#r=l7PZPQ3A`.Xp!9 uĞ *Yˍ peo0-S 3ax|G]`9жr?};e`W$~KrD8 OxwlXqSU6וkkf֝tc]j8v}Xw4[h#@qP;z׺sF͐Q P%$'Ӈse<@AyY``9@xXF;͋sSoN~b d#;x?"#oC3?\DFrEm8FO˯/??pY\KOm_ᱽqhC+PjAN g,pd+ml`c @_qhmů v?Kl_9^rmO :b]zԱiqO|3TWj?N(K>dyt9@>@8`E,\: 94~{gq/3v߷Xv!008,T7ƘVPQ6(Sop^.,G:Ve4< =-5>%l%z 3e/>ֽl$4 /g,>JȊlXi}ym/qi1(>~Mk%ܭ9` 6?;8Im?q_9_l.on?/؎/_JؿA![lV\ڗcXIx1ɟe~s1-oߠ?#by߽޳ij%zG_$%?h'}8y\#k0BO?oS.Ys86lbP%i:iiQyX>8 氹Q5ߔ8qA ;SݪzuIX^mtY:ϵP \? o u8ggć( GX좞lBA@`|9'0?m=_Jf=o x]/udtP)A @N"=)qXTO}A$]nT(9Vsw:(W!0ͣx!!MO~t|M  e"y'4ޮWק#rSgᗼfh^'WAy=Q>v dL6ogFS*וx$;|NWi'8#C >(G3ڎ $#a7\MtRwSoGsZ$''[yL>ukpӵյyS.܇-b3fǼ?~hr /~{ޟok0|~I:dY vRo_1@,Mp @ Ʀ5tatl{>sMusrM[eHqr9vJH.GS.͋*qX!arSG)S̷: @?NKNL6xu 9z!}CKeJMدȹ!O֯v_w4G]b _ZGO4gׁt,GN1"'a5Anh,HW!Lr'y2bu[,{.(mXG\˂ / ^7fi՛ 'm';z4o^YgsR!`C6?G./doD@g+, |nv-8şcvIwO?  9*σc㑱@(\.cY$hE-֖g:دܔT"ߔ#o qؖd5 51`֜I61oeOo9n7yN0.R(M~ݥyu} mns sZ/y[:^@Fџ1w?kk@;b>eGgp.؇pI8\m,5HwI0K2J_6=A?ub|9!!p+ rxF]p -xٰ`b\r]6m8J音`B8g{Z6l{9e>o'0rlخ/b֮A*H4O(;ϟo;h_Q(7 yTvz Q_,sH>eIvO['lV\V~Y4'K9=w|3+ Z}45h'7 2築sοK!9C*!}wCkeN9@]"7si;oWoq!5XcP*Zq ly6Oz `9S9@l?V9&(Dw5S8&D<`4\Q&.hpޜ6r9(9}8 `jeh> eIFiOćiWlNj+w/Xu>`Ug{fGmAnXh!owpF?#SBs,SX"S= ~}zƄ 1~%//4p0xQ$ iK~(]ʰl2 _ ~>/3,gMm1~+wa Õtccv.z,_Jw:?r|s13/Ǭs䡣'ѹ8\\|࿝ߤ4Hssb)_]B$v~ߪk/SOia?,Ju^?`y<lz.wi0o9w/, y^q+cTyfќ'O/~NKŚ֤%_#Gԟm]]E>"(O^8B suە_^mhz|+^r @> b,y`X/V9))b|d!nHe* #pCp^NpD9'8#~'|Cm 豀4w1ʸnHe37ȷ8?WcJϷw^T  #D%?b'.OzfANֱ jڏжd7){}Ep__?̪i2sulpRk{tq%DnpT?pyq#0%oQ LRyc syN@] moEq6׏S?߿O"]oVS苎U&}u7nUN',G?֏$9[G[`*ul{DRCb___nm9=;< X.|w[F|^+bSri{1x ˃4|ᮿX㭔H.8 <'kߴ %ho*NrpS?oKWZd938lnpx6+ |Bi9жuO:<Ƌ )G_ހ9% <@x^\X}?R>w;B7z_Ym=s<υ ϙ? Tz~-)+eX7E9%;d)~b?nW~Ehu+?,p'x{?O~Ǐn((9[=sre Gp?kYN@.r 'R:}8?]19SśH+ehkIߪg[m-[}.Wq}9o*sxRb `//RW8]*;ῷO|JK~+.o?<< Jk/ף뀿Gןe8[뒎-m6`{e_8@X0r!ns>g/zbqbP_|s&IM T?L3srH?g^`6B'^欲ߟ}8֏g)B?o˻[@GCz[Ƕ^U : Y\K\5\{%ߟ֬\窔-m+,?n?JVnkjN70C{ic {gN >&}@ 7` ã7 G:׬ wM<'"3^ pY06Q"f{r'M1T6蟍>4'`9N`E;eW'p@=rBpOc當7ֶO{,uزd瓏oKw|(?,szhO9TP`swZ|G]5< ~7_ky?htDDKx,ؘ0,LଭM_gٚ3tYy}`YDq,RT_ |3޺ 'tUO3pcM0=ie=~hϳ3# xF]%+>9n |i˖s-ZSqbv_tvW9 yO/Nx$O9乃@~,=.PCw Npa*Z'HEtZF.y2d]ruAu{dQ|l}}#Ml|p*ݖj\Eދ$_|G(_ז4&PeGQG.&3o b[lI.ژ@PZG$]3XO 3q  w ^Q~!`H՜^ݵ3B1?]\]Z y?O;Dp=@kyi~Y/\ uuocPr =m< fgw%keD}/yV8OO6>a>m;\5Mald-ݿ(Oma?|kĶi]FGѱ8FJ ,Fgstuo>C-[~sK̟<ȡ,V=]{_[ۛ uvgEh &|ZOr9gv+rpV>v[=X@+_9@tto@*@ 8@*Я;׵\'.K]+H&[~!q֕fģ Nx($O s|hӤd!ka9@o{:85w̼ӻ=E? D:,R>F ~ŽZ~`jYz߶g?Ocp?g>Fs%/akG8\Ŀ/H8/y|Im}Zw߮7Se1bjOv?uLvuiXC!o-rs, 9)0!eǠ/s?Kx4y\y"g=?Q7"hynt|?и/ ?kS^k훗Ͽߛq!^[yހ< Ե !HU=&wϦmܞ +U՘$ H?]uEflG>~m/nY}1d?>O>g;v~ûW{~]<䱧`ۅu'kx_0296?ϧ\nB~NՒhklyA6غd//~CkR۾>ܴدWؿ (uKv]ck|Opb8m1߹Y"ؿw_ M_֟~ko̿f߷t_{ #߱?r̶|`ByMAV|@Z`ZtbXۺo1`sV1G;dAB`# a3ÇxB>|FB5{URxMqjI0~`[`z `0lWе2Hj̏~RE7;^I+7RH?CYB^ ,W8o4~| KyYg [O7`֋t:]Qܰ,,3Jy0o ەx,s#>|MYo9xɅ:? 9>sa?ؿ){RՙC{FY۽ו6mqO_[g7֏7?#@XAio uê f!'!|o ͚~^W@ߡ,P7Kk~B @~]ty \%|?l/x_ÚoyFUhf;cbv]܆xOI8a/_~'.EnMc~`lm0]_@O]qv/Gup H 8F@#fv bAܠɾ9(F5w`tɂ6@=y6N/@1Te]i}X`ݟtZ|o< Sm"y9 JȻ(?>/ P/y85?|ܐXb.n2:XNB~1W؏w\3 U|Õ̅>~Om}?~5\?B?9jV~P{Fmox~r:6jc|0!Z6u $l|mt[>nݔu-6Wse3:.gk<_}s1|7mO{.{2dteAv8\N"&3P ~ݗI?6moMjTojm[#Wh7}TO|`'`{k%}"Q5^i]Ȧ#d, v̹4'8 @Ȝ{zE>7op=4y~e&Y~Q_]CH|αAtv1{2\g##?xID[lvf^n;|{~0¿[AE}܄ d= , 8wX̞u5`Iq'ЍuȆҖs\ےoLN G/k/rK8:)~oto'jU%$rڙr)~ο -ًJF7Lxo}9q-\re~}7p9~Gy~c[+U]\`C~2o5Ŝ~ZƠ=Z qQNq gt=!x.P:{zh%ױug?>ry[Ћ!-qi9|&$(\?/$zd\{E|xcT :Ewf 輕h~:e7ȺcC*nL  ۪{]0X?h8`!XgbGO 04@ҜdUi=T*~xJ hn&^.@lnBގz9t0y4˚~|-X8@kEʾfؚCf{ U~VW'Z{򑟄/ry/Եٵj=b熾x>c,syľ'f@ :$^)&eDIb?4Cya;a!Yu3kqS}c =ļ=;]ax{n,KY88?sn?>Osn .djTدȌF{q9l/*'8y %qlg<3eZ8e,ZcCue;KUcK;|v.F@߬F{H;]Fl[& Ecsf@yi cu_JL h̟/?l씵- nb`@iG`: y?ҮCJ Q8[w-} h-N|,PX4 '´5}Ycd6+b-l`eMi>槏F[gݏMk~XǾoOX!#w"4Vߜ}*'~Da\|]SsM]>>/s$l+P_<؏zv\7_IW_Vwz5=tsOjo:;}~ۺ>-eC>*X_q}SXsE4k&P} oZXlo䶁qƟu@%:9yX8ߑ2y*QSt9G/G.Iags^<@c=[ocU7m'>uu l}ם)g{[>{l~Zo{vHz \Tqc_gv`sv-Xٱu;lmq23 YDv昿a?4G;ߌh+O_bn9JWjZxQmdWss/c_/ k-6´k~^FlzŚ@Ybb'#h9@u9gFಧ'8qK"?8 -Wa D98ob/~@`k5|?~ P~  e|mfzQAW49I9x]rKrb?[f@/`?l+qjvt,OܼGnO+v?eZ1kb>w`|VිN/%Pm՚]. s/dQẅW byGSvZh4w?^Ԉp<=#Ρ|?=~1^WVﷸ=㿝m})Wxԫ)_kkُ՜"wq>]X%X6SZ,/9X+?= W@{КdW,g<34@f@0NU;ZyH%lzMa`x^ja4q\r>{'^9sؓ\>T/m͘s:}-y:tbowQ{ҍq:]=;wpjg3]6֘,N)M$+qkk|^bqq`o3(u/vJ,&츟'gA-m$P }}: \2w-fW! WWb4Mnp9͌7,?t6/K_E)#F*umĴ !?o6ܐe8(8e{9fra51"_z/~5;ߺʡr)W>' 8Pw~+߻ƪ{y5ǰ!`旱%i_$ZZ뿑*[kgQ.ُ\W5b)6쭑<^_H1?1jqzv#Y;Eo8]?Z3ױAֶv)wR9mKM.cr~]_jk.CIhCn]dJ.`)G1$P% _E駮C>>vcz309s3?$``K`x0b/u !71n9X2P_μW)e,~%ȶgAo* |D;'_}* x?j?H%`+r䴪`n*C%ϾK=5y Cr{|x?-W6N5+oC',uz^?(SaAOFF<)؏@ cl?`è檏rCMh䫺= GW(P?Z?q_Tߩka?օ6ykټu 8֝x [AxErJ9_+xhhGދC"Mgϴ^,.c|c0gGZ֡f sY %e]P茿8@Pqx<y>f=ӤxZgpi>ʳ=6˳ǣ?(~׳\>z}?{z^eqDI{Sǵu@wU&!iGUvt.#./U߽|/ggگmFv$K!202m)%ʦ[kzNߗv)Wzk7}wyzb8YlO&r?DP`EtQ.Ͻq8h__]&V>?kV.{*l-{_tG:㱏~EyJ==\t{^>(7{u}qz_K\AqK?W,W5~ 0unStH(zz.#{).'ώ'\>j/e۽+ssm}J-d$DPC[sMn{ elU GuΪ9~%>WcVT[R]dgp]?s+=C? .18C89@c}]'^]\NWԁZ>"\~ Y/;wKG zBmamYe~*:Oe0yG]Vs߯{j?l,-q`#aAni6v6W0P9SQjsͺ8kJ;$[q2T~qSqg寿?kowp AYEsrSlM \91:ݱ#\pSr~^;:]_']ؿPۤ7thK}3o~lUe`tTNwKk}*'}ڔϡo$a.G?}y,6zq>}Ë$@ܕxa?qP9t>W+;:`Rk'+ ;m6<@?|K똿rcϽܨ|w^Ll+pm9Hw~_5gtֆo~t??eerb1{' z FYCl$>˧~O52'ytǎk?9vtP0xϘO~~j S ;ȽVM7hlԨw8Qa?vyp{?[dܯ]OV'3l~+NW۽[H>c+^-aL}Wg/,7~*;}^Е~-n@D?Ft%^oDmN~'%6~a?x747C q+6<69H c?\bH2 a ^7;;{wǽ],߻]J~>6\A8^O}0@?Enx?xy~㭖[Ǐ./O{-vڱ 3q{QQ_-В+!j8RV::}W7cӌOu.pׯ6 Gg{KZG2_76Z{G(cOpo2۟p_r{ t?w?Nvd_~A{ %#o^9;sd,R-+>WowUc'q%y}7a?"qk9f&ʹP8Bh>ʄ_5u&=q<ƷzVt?Jx8gu7s(Po9Grs*pك CAPV*x@ǂ.+n ?).}N}g<@w 2 dO`b&ctx@8+P|hZhjj~~};q$s׬N6qsi^9O2rիO6fУ\ߩtq{c7^b?*W@%-v?ʕ #l;`M(.(v)mO߱&v?q3 spQXb-a?ߋAYh 80F 8zMxE< 9@/3p>ɶd]gl\8@yvUbX wrxԓ_oa ?+γ7~ˋzEq1F߷{;ݰ} yIĮhnP½:ې?ډa16p_Tnn߷%Ov_rj~KHD~?06oZ#?Z`@}8/}<`\6ʡmN ">`}X%<"( 05C`p^,}ލW[8?^)bS|% o>}I߯8&I~x: .&\@G?:0\ hr-cDyS.*a?l60?+(pL\Pwg-ێ{/g'󩐏?>r)h;X.X6Ny?C} 793G4( Aׅy+Mu|//b?dOOOﯶIb֚+\ ]i,j[Dž1Ė-s1 @ro xnഥXp9ҟYˤ Uq.XCuRc?'к2l9>}zO~?e1\q];h-{Et93 H;ѱNou 1osQ|Hlym (ʊJ-o# hq?[?_k ;:]863֫o_ O~:l|;?~䨀?9\<@v+@n~6tIDۧm/o?6x$Gv?$P8{mQ#z3rإ/W3}:P_b9[7KWe +%z>b5GL[ x u+䴯8@+R7O9c qق$K,8w>^|\ PqrWԞ/qrr4PvE983*$Gc- h(îXKXC6 ?q'\`'. q&\ )?nIJϸAua`+`VTh~fw&;xϘ}/]{>׳?9VzkA{ qmi{ZA {c>pG)m}_@/z *9)|_7۟~`Sl})7~y|[ԁq $'-ۿ>"o "k6piOM0X\:y/NX$z4/؟93̍)>>d9ƽ7;.)XhpXbf#P73< #p}2 0x X8'u8gOs ޯ+0My{Hu{? `?:V$SeKM̝#8M"imYW > y8&`+1PMפC^O|@{؏;Ol鱆؇ z5]pK¾ǘ γͯq}gp+ի4m2c$mk,O>SOv>cv[m6tM5\a!ɑNLl~+;S~!za?~?~a_SO/?^YemzCz _/cl5 _vl|@|d(9j`y5N)\tn+u)no<~w8rw'`锅^s9O /e>x}~sa>pжS Z>!S"6) paGoDGzE.\CG{͹\<*l:=߲_J^oIJ|% Ni1=/όQl'm~S_ϟOg6VA(O/5GU8?Fo#~ xz0p9@(bm5Elf_wvlq[ [l_sNCxxg_Hl3_$ e![ _ۥp 3<+b?;[ƞN^/ao 𜼼] uu~USp79jsjxPm~~v`ϴ%l ӇvCSSg8|DSrZ(y[ӱ%g#_`?{~}KߡGhEmug?kf?~!GZxʈ 34'869 17@˶:|p6 Wp>1C?1'x`1. +' {ju;צ6;_&b?կq}ݾzb>?`r`{O؟l#' } t ǍM(r'쿲l71Y1U0nY蜕Dx6r8fsAqobs?8~i~rO9~~5&5kn-_ >gl/lU3-{ ^ӫ8@q,1Ժ\~`xF\9+\a<.Jy*]@osXg5'29lԃR[2 i`=S'άx(Rџv~v}x>uCi3Qomo'sBY :@y`xnl ae~zK`p/C]G)T|FkoV^9bysNXe{`^o =zQ%pug0y~YXoy + ?ފ`c$o0h,@;#uO`)1w@IϟʠRc$KY,$Y5y:DQ4mk<}>`p2p&@zS|+.ۘsU=toq{uOq vIMxl 92|]lS @c}ma:oO'p6 (\sg \C!JOzJx@nt8KiKs {&߀е[Mfcb?+nZ?oou;e/9B;Wm=Brin4O)gv?@T73eAlO2*1tOxp)E U<#OEm~Ԉ9ﷳr7 ^,bmPJI](ÒOA߶y֯Py?8x#\p,yJu+J1g}{3bNNi|\,b%B|~^68a> ͠xX38JŸ8sPǼ*eGgThlu1\<0SΠ2U+!Zw-Er1ѱ.el7`[g9hئSZ\jXu|}MayX;YZOs<.6AH:i^a!b5')<_<"^Э1HEɁ/ߎ9vj+}c+jm9y k,ԑ\ 8ۈ90֔)>d"ΆccnZ, @x,̌m,Ϝg_8־w )gd>p3P77Eba?? ~׽< |vlEg4ѧ︗x#z!VXh}Z|ؖ6\\Wس{}wg}Iy~NaDN vvx&>#Op_I6~G?ya>acMRCߺAꕏk- j;5z܅Sly\>|ˣ8}6\.iN`0(p-g?uqͱzgk)+hIEC<5,P@{;~_~s(XW`zmDžK cXpp݋ iܱ6O|SA_Ic_<ߔ >oX_>*FsWy2bnnY×rNV V6[--؟O8N߸& }Ȕ5n" s3;?dm`?=όo\7+_f9>˽'wI|e[>Y3qW>wC!jy\r.ϑP gֿV)Gc6aN".mܶ};s`$7pp'Hʻ|ȳ5ds ΢$-@y3rRָĄ~_pC〗tW~lVgw?ep?1l cYk=qy~vU3Fws~ f *4`=Ǐooe9~+}=O/KlƦg~ sy} /`驶?c_W o6C? 0QXYyx|@U̷ rjha-pc_vNm*eO0֘/@p_s #0u'pk!V_϶:3ߞ b phx>6n\|n336>nq{}OǹGM>?bO1Q/{0,9~}q Ak.sRuV[s_r[.7r븞v#YQ"mjv]lZ+S~Y>Og)qߌ|]) eO؛OfkI6Oq~psA+*֢Ͻkom/ނldѣ0Kq VxO>u~vY߿:?j1E?5 / 1SuZ^ބӼۇ/WvBS[q~| W8&Esx@`^x6p| pup94>O> WS1ɱ9~;goE?`>y&՗;\`b}߿Ooy(#v J;]\RF*Ҏ/kݟm~c{lzIX~$9}Wb2e>g=>q ?6ߏ_r9e+7W~p>~~Hz}#ɇ?w2:[[3Vy823GnT2?% ` , ? B#`X q~هO4q?o_3;;CJcd_%.k/ބʕX`8WVLvW<`5 Wc;vt0aզ1=5ۿ 7ۆc''E0_^c&;9`~V/㧘q~v{xG_O>W i~ rhJ`ˠrZ&k 5'bs`l;|(So1O__yw3rm!r&}~\~lm'><_t^xe_WsI? Pak> pJuw/l>_h^_`4/Is6DF[gq~!||O@M*1&%Hrtϻʗ$' qsKl4'yza1 g)8ͮ>9GQ>NY܎0"ԫkk o9dO6WlNc?xO.ߟy ??@~9WinSJ w''X)|#?:D;¥uqq!gv@T@e.9/k׭(?.ɩp>&Z۶6p\<@x#P=@8@XWc\|9g|h~_o kBL,rq? \O(M뿕wrm+#lϺLUPK7ܿfd'21ֽ njx?8J"?nYG?s۰_͏8sR>v0.YglŴI6r^Zp zΟ{g۟rO +u~[=hst rKPsӑ2xe#n|p ʱx0Qj >qN(''pN`<'YG7_"3ɣ19@)e:?e/loz7l  k .@9xX@1Qyt4G?VF%Jѵ4FG=l@6o@?3xqu78Or׭Zn3r_)")Ư6? 5NbKyp 'jovV~6qYQ'Q_9 [N\0;مεb^*ϟ0'k.|Y/ 6(;;c?Z/h>ۜ]Džz ox@Mx8O`2zL,w`_c|w%rnOQ'aT_@Aßɉ Mx@Z<qA?^nA'_7Z*xs}5'c*WEp5{6+3z /2(f" *c y#.j&Wkzggܧ&`t5~%4ϊ`4_4LL5>,i3`/[ʆA$,oW9$PgV6ppwyp ~j:kQǩR$&@a:~#9Z^HF@0y@kl_<uϟ:ǣoXl& sם믬Q+}sXdqkY޸i#6vlTt~e-{Lsk+GOs 8S}-}Yg<џ?9ӟRNg{Kytɹq뙖s[X8pSf2\+WT-ߦmCV↴?}p}_lNΞ(CU,1ukV=_{<P _=Wˑ*Y6 LC({F>$}N`yZRk~?vkD_P .O\hLL3rQK?GjgcC5PA],Gc>h[4;}vTG׳au?) {z>̖9-!~EZ4-6b{#_ﲿ|Cw?=me92/'gTcfħO\^TV[>[_oI/yQ>sX-%jme?XRgU `4@_@ܯ.4iN{3MsuNX0iF~jS:k>?t@8b'T.[@ PCy֝ily?X``՞0 d}LclދGbϸm77 d}aϏpL~ϗWu~k9jAD+m -y7-?ʇ=װ7F[l?tѵlbdaw-UNw&~l]9ݷoMXo@,׊;` 7E|0x/Dra  1('?XŮXXvT=D2\諦exF];:4p'ǎqMTm rU[! c"h6f`7;r'tړsܚ\R>g>c|ot_}b0_b;_1?Ԗn&N/=b q"?8:ճYRr-+M[ '՟؟@O$GN,z\oW]y8x xx he;ui4D!?@ :c@.nj@A౑O@]I=j`[LS)&':u}1]ky-)$ygs?~g7ˢV >~kȓO7=[Sx[Wf}ݿOܯ}כ2sg$mu: }zq5};7&$V?߉Wymd:G޹=5w'۟__j_ΥtsP[C;:xy򬔿>lj/jN俶KTpr;☧dq,? N}9ؿ5@ 8OXX,xhྲTziߠ}2c68-un||qqM7A@耮81~M?`A+3 P0x=_cr؏>Lx-oV`p]F>{x^PJ).Ux3!|?Ѷ0"26D>~~ 7,¬J:6ok֏Pww:R! 3`; ?/l3߉b_(~ea/|.P_sOAзu}Xϒ97 e'B NSzQg;m6"e}ؽ1w9W/ۈ@4i>/ ۟s.k^_@Kq?ŽǒI5N^_2- J@ F0`u@5?@c LPOe,aYyw|`;U3|?=w?+ w ߎ?|Y\5eO6t˽`1b1b8z :H}0ylz& 63i&ޠ&ᡞU>BSӓ:Y 4v>=o4XAK]C, }\1 U䶜kσ_ nIlo)cݻc%&8o&ЙQO|VY]uy.l6jas';{˾H}_?l#0'a/ϷdC2jʮ҉u@Id?mqS.p+_d=GӼAɵx/֩~漖 ؋y2뀨*?+@k:o:?@Wd?h=g٫Crn_)~3\$QSĘf7cە%hM?!7_r9^\2=d?r@OT,)bPAsYa|>W|ؚjLZo_-N|`77o00OC%=__V,/>/^W}W/>G?sQ\+6em_7 TzuH;Gl_cˍ6J3[TO&+s~9Iߍ샜gdnC pwѸ홦U-P^nS`h8h#qZ8mC(aGϹ{^0\k.paAX`En&Ѭ=oc;A Վ:3Z/`VM 𦘣rI@Q@giWKGռ17C3҆j4"=ϸ5S?zeW \?ZO?bisMmNs~`?Xϓ7WbfU-c@L+]οht vf¬ZJX82@l[|v/@465t^cpi4>/xwWmIùbp2^NXٶ~W>3h L3d,w8jʓίV+h=k.@k1Mr c$& bCct|8t~zh?}I\wf~`3}Oq~ow~sjŊ|M}5_ʹÊRĝ"J2oWdӠ6Rt6su^DV!p8vq;'鳂ĖIxCY7T%<~g jchZxt?ymt ۟B`_ ˯;}OflXF>MvIJ6+m}ͮf?caX6.`m곊HE$*Usv<+4@e>u[4P@<?>P0LND/h_K0. MpbmjOˣ94F3lU.^`:`٤lc W\0w."x*^ICsRٳ`lYWۮ%=zgy9=~?c";?7;Gum62.nn: f7?F9yM>7I|sn#[׮v<4@:ȞrtwѲ}& n&j\/د['8m[>nԳ(hF_ @38{ϽG pxh@/ 3-|5q{ȼrFVqPyy)g16.cE#@a_+1Lxy'Wv9z2i}}NV{~xO-X~]yמ)ߏ~[yrbJdЏ-i)|\gd-ku>7n`x?o,!_7>wt=?=7?F_ Gg\p"߰NSqAy^iְݙ82 LyES9=f$T90?i;i+Oo(257 OxfF6l}+t r.Ԇe耶K~8=O(w 3dC4bGZ`z?Q G P}]=føΟʣ{1 raq %[Ѿs=,gq_'️ںʷܟƗ ur?W?r{&7fvki;%_Y4 +[!o sa!c?l׋g[߬- k5}ʿ~9_D5N/ 8bi> t?X@, p~<ёR%~OžF֘M>`.?@5@ YlT @\˕@ z?=$JSS@~_,8{Zd\A"W2㱜ryUkȍf/_->;q/eH ?m,ޗm8xcY}Ƕ띲:fa/*?~fKYlCNYx/u@Og,qji3Wkɸ~; 4l3~X 8 8Cy6@7N z/QKH iR yd-TM y1¹1qZK)դ[ ̾~}-Z˫k hכ!:GϨPa?;'w4ocsOw[Oog{نK??_sa?{Qz| ~G>>wrDJ muD?}5=7oKڳۓ[(7ؗYk0{fo1݁4kzM/d?ibWp4=Gx=0J%J"U8,3?>Nw({n=`ely _>:B)y(drQ6y)%\0^/Xl@ŭ| >f_ggc?];Lcc͚O~wLsfלbo?(I =j>ϿXBgDէh0selh'-jn˳x2š2wn`tR21%NAG=eNyLTTo/{@s DbXlw؞]YX qoj wF^"Gˬ׬>%4^_e k[/5@\-h7mg낢*k+3m%_s󇦢ߎhb|C2jxLFM oѶ)IS2&Mx\ A]u/G۟##[w;?lb?%cGa,༳KYu56uSզJҲcv-:1zY ZԾ'̗7wyvI б]J/d'侰[W7ϭVt,tŔQ(ǔA29(VUY'>~+gtи5!>ڍθ=4ɇufc6^ }PNqEOg I<`$?3 4@Q5@엒vB[KI-DPv-r+ZWLdaXLa,'D˴h~~Oٿ{{_LbpWLy?ާVё/￱ó|w̏'mO쯈:%O/t|z7zڙfK{l}~o1>ߑ_p~b;?tyH+k7ة"kIZǺn)'g}R dWn8 \%)cL '3|4 ?yDF xk'_t@zx5ePD `?3"`vw}oh(b8e'ʠPa)ܻZ=YW+Wry(?.U+c5o7oOOs܂?h Rl_ؿi6H'܏?{8WnO h{y1pLe|[Fʋ{+XyVNxL}j $v}ϽWbws0wZnv'o?LO9CΣ@M !iF:`''*hu|/-Q *܏Sut9N l;4@3BQGLpk15 Sf Yp4A] PO>[~7[7Ğ[{mka󣾝Wc/l=7߯R.~Hj}-co~ʟ^+kyξP j`=-1Y~Kk=ܿDg>ßWcsu}K/逮 pj70|-hX_?C_f 627y[W}ܶn[e7Dz_/~9$60Fks:^d`u@ 0s'C?(w&⾶G=ߙپ6??c_>Ud2ui+0>SwL6"e1Z7lš\?թv #tžC磂1yo/b43oz4N (,H4yt?ZFLZdLi  P1NyG;?)4S"1>|e)=FgW%ۺ8Ih Lc}SG%/l0 @47/;|j{Yn_cQ؇p]W/kl'_pv%г*Z];kO=Y?wˢn|Xw cu|W`@/%:y[sKlsU'?6lq>P=ڎ~֯1q=SX~:@|icζ~`'`u/" vT4@ll>X47T|9rnGbl%?81_~L\-sJ~㿕VhI8_yѾ [[[ffٺ1wmӱ_X.Ix95?ߟ/lc4ί HߞQ_==p׋鋱G `vͻ. _04~}znBt j2j/:?\x⽕}:?)v׋fkhh'&D&@1I 8!z`H~<&M< @P], zηn~nm3Q I<1FxֻdOǀ;9\Wa@]?r>?%w?s?7[g;ٔm:6)7?ؿ7c?EUa_߷6^MbufUKzN9epJE'뗿n3ˣ'j;h_1ٜ?R mI`CSeuKc7澎I,<.Pb~LvrF~b:Lj&'m,6l( Ƒk5$q.Ffۻ!nJ8_ra:y淧t@3k.Vy˸{gugo ?ۿ3߹Ogiv;~y6u ) Vdl8/v/||>~,$9hm+P{ MEq|Kc}{e5/,7_ Yv؃@p;@ >?to<ø@ H/eG?sM賿C:$ 48z/#~0R?p?ixw: ˯{*MӶهOi>bk~;7?jI箬bkɇpxg=3JK}r\sgq9qx]8n$n.?rwi[%@RrF3jR-R*+[#}|J%coJ*?ҏk^z*7o ua]zkCMaR뭅^I5H~syc*6+R~Zov-v/ǿWko?9Ƿ^~8~?ˤ|Wge~]MW8K1ϗwIޮ=OXkW>s;cߟ,?v}l<^c5=_cѾo1t|5Ї/tjp1w{/)O;.<.e}Ų_i81a+0 arX<)C_&ׅp=Fס2Yo*J3q??a˧9-_yp .c ڿ$P>n ;@SP%-::IO9чi;g ='']7ݒkY=݈2Q\q%=tW{~mY{5q,pxUv|gc=xVպ=BQJx( bw_tw߆q?O>ZKא;9k*lz`-mu+pS;CQ\zGެze~uɝ}ï计Reovx)v#?/`~Mq[!g@~` 0x<_r2 `/Uxfݕ\ԏdռE-w@'6#_?wQ/RDF^o=~E5oֆw8Ƽ>]ESG=1;];wg} YXߕU_XG1]y?@|~ꍆ hh]`bH-qL?B?pDfzt(9k8@uiԂ~؟ o y]eM0xqo иO}7v6:x̹Ю|O8z2RPF @q~_O`7ky|/WmQyF;ͺ06E g_O_ͮ?>aWȫuVuC˳NfP-|Q1հbb۸@0m%K$V}36*֒c~?c:q_t=}<:(\l(dgAq6|&ꅦV x=1ƍ:bAڧ8>MO>nZC#L/ᨅcOz{)cվ xE.}71" NEK 9<@80x6;lbŧ.z{i=swIμh[O5iXtod8cb8A>*+m'/eWXvW]xhMy>Cv`D~osv[ףW7=yⳒCW, ';|Ņ0xT'mQdL}3zI?_ͺ{ִ;ѓqP"`No/blgdױiL~mcڇE>Ov<8S|0bdW;@\6{ vY\u_~&i-mՖo;['yc@>w8@rlj$q:3}' gy{/Vem7x)_Gz^H~g`s~~wݿc!;mF7^Z3ۿG3Prl_,6Z[|}2[04 zj9QyR`>89<,lrxәu\5=hN>2ޠYxqJo` ; Oe6b x K:z &r[ثxzZMz|v 6((#.0ic@IC":=&W=Z=v\iKqh_9okK;___~ ^H_?|Ϟ> 0=vK{0Pq11c XvG0}T'mmی'7.2Bc(qS܁z8D}|-b?߭c u8B jpr~ݏw s7:%x|`6 (Iʑc- tL|cX`/*0xu֤s4s oKd /12_pW5quxMVꗫ6q,Wy7uDw :~u};o ;ծ+\ pm?qWlVX_3l\cbӮ5rbDh#k6DAtj<{tDk2*/a67u5}>_Lrwl'|4/P0['pd)?f~>xj{q߬\Z %;-z`sv~]^WxvKB<47/ q%X2m3ӊm[E8cv&7Ǐ|!cļ#U`o|yܣ>#'g-ۘ?se*ISc}k- p[ T#`/k<<ƒ `lOIe  98v a~{O2 G7+翺#3[/?ns7mqwy\ e1!K,^JEzZLwQ {Cΰp&`ƌv/`l>FaK8nsV@]?? AgP?}0}BE=/#͌7m.kئ 䓩뿁cRZX{C\~Hhp#<-@Oa%|C[8 (ƕT=(ED5|]\ܦl}?4ߝ[/3g\F6Ѩ0?7C+we6KAi&' .?~erfo| Xۂo縋yNfh;A35[༖(Y>,D]8d1!`9엲6jkO9'y@>9bo3:='o+)qGqb%n,T;`[!0[Od|1{P"ʞB̟nCjč!~= YG:%=׋u|6o?}g8kQKE!;>@>y ek]7|Tkҫ֐wz.'eGq(W){Wuվט s"Z9' p`p/4WP-qe 8GFqYS 77y3HgNyR-;|㾟1>0.qR8|Οl-Ql@vObvyծ60x`}kƯ: 曾o>!|MĤ18Y1p^dF/Zn85F?qW5wr9pdz6^{"#7Hy|2}8mC'__eCCboa7SB7]qݼ++Ċ(:|8aq^0.:@-M}r=2nD%+!ͣcoh8cÒq?-G~7K|K1?Y1?{Q:uvli#C;ߚ>mc}o}_d'_& 곹* t 8fv]Aׯ3?  crv|  ߢ-`lk bHZFyIt!(R, 5AnA'@M3'4\_{ka `O+ڼ鶶D<0?pK;c*z}s/x_x6~Az<>ϹvʴYq2z%H<?+\+jT;5k>8s;5⽗KCGo%3 Tgg_%Kq]L_o1u'Y˞t48q _4 )'׷+ᗶE8|_v+Q=- q? 0 lOqisuK8up,dylpm pF\F.o?Z Xoc}[{oe4oVN*_(k ~~&./wx.n_ ?p~/x5e\C}~j10^P&3+~iX9eb!ϯsoU-q \wEىw|kI~\W0%[+Sĭus2ֿgC~HgF,>۰~Źe [lSWη䙸4V"k9c_ v@I\pri~wc1`?>w3R55trp7f,~U_mCuiq8-8w;~9|fJm?eP[|f\ qR7⿥3}w 8:u$]5!yO?_59 0Le^/EW. B}swC_I}6~Lْ {j < k =C7/M&Kz')/TW~ho;Py zQL9Rms?_8'_?5t+&|{WCFz'䵆ؘtLU6ۤLlۢOK~#Nz-;%>n;3{{QE;0ɯ_l-@|/{N c%h_xrY`| m`o>`yπ Mp !y@ 80bc68>Pz00 Eq/ه\]Aa_YW)^W/k7 5: vyd->Rz;ng}_0| X|ThsgǼk%3w,tǥ3#Oc&ۮ۶ 861}+}y7[ /o@Rq62Y>8]9'P ǰ:@Ryْ=8KK,@c)of?pTpwl|zKjqoS ˯9-f:^*8/w:gXS皴-KPe϶}K۹s]7-\ٻر`]?2Yx Q$@$ B!!B@ {\UVUu;O?mVU}&Z;֮񷰷hým#~7 CFێҺ1yL,׎Cvtyk֌GR0Abb t>g# 0 NNeҍtN(_26IXG(mߎ=|<|/cz=8<nZVOCk,{wR>vI qV6`=eݸ> kY0}hםқTp|;W38sY5ۚrNBx܍o ^wak|~Rp^濴Qڃ:ݽG=H%hc/knEw`;uÒPsϘ]8~u QgQ,g&x';_`c2=2_3yz;=<~-7_[{5Ӕ?zù =gP T3<+Mx)=Yݺ'O8?ݍo|?-5wd<;s6MVYYܯ]@^ -`>0=gNkcR;\;O`@=Ppo#p>wMo(NtդCqYBXi˷G= -7 %܆$I-۫l{X}wK`V`~Z?vog \>u/ˌt*@~9o&8~n[+#v/$rC-ٷ|#6^՟[+ ֗.*\Rߣ'7[xyg_v#I$?`c_@':@~:`)#6~iV}q c =$25t"_fTȫqY_1_l}=ųgw(s|W74a,**{ec:nQSLP|u\6,@Ps1c /~^?ÃhӘ"ۿ-G7õ1BlW+3zv g&k{Ruk( ~™H|g PNE|~l~ClFH8w O ߀j(Q6E4Ap,@식M*cp18??(g\&1|}f~׶N t=rGGqmTle}SPgۿ#Qq},W]:컡b@c+NG8ci0ˏז~O׻@>/%} 6^Wg۟@>G(7P@@5!uNvԭSۡ=ßi %ѧ (̜Oq40o`a<w RI זPu1[[i 4a6żױc'\GtNtl{L8{;l}h#+WPNexe3k#O%G<ϩv9]J[Z?[\ې\d?; NFT8'?}͵q_?p'u]3Ms\SSa:5@ʛ,9ݿlm21 f,}C<0W/ t5T;F:\vݺ;z=a6kv@P%+/4ַܜ=o~W=_OcV>o>`VWa,_1溄}BWWc<91Aϰ٘?dӨ.c,pOəEci {W;_GGw ?y_b0}GWp/\~їkYk X=AtCm# 8ӵvyw跓>j0:c6D&V:3kMO`P}@أx5BXuΠ@i #ݽѴ idio _æ+rh=*u xZ1j^4|?E󳿿K:N3޹# ۅw˙z>8TZ4'؄Y+90 gg߀4Mx`Վ/Z 3`2}{@ߞMcಸ~;^ 罰fwf?rzWB4뤲ہ?qYk3Ρ+ǿ8F,|0Cwy|}J BkVofW-vIXs1.p)2{Ώ &z:}_E@ g> "u."?^9FP (?Z4wjoh `qk~a ?a|1}v:g>ۉc%s|< ys=մ,g<,sY>4[iw:/gKZk_g1 \~Gjecla4wf*{ ;w^wb짛Rh߸-7ۂRNfdAT4eaob()/ q>n-ix\>o? gLL i;~,n&夘kdWV^q=\.4ls[t9eQH:cO:lr^W@Z/x_ V DZ(E6wCgxic?jތcU #78%p~rw~ ,[{sh7ٯn?=3=;ܗnm5G ?s{/xۄ;̛6?;j~{y~Ql޿+D?>o 7 į ~V\IEkyY^gD }y6Y&2z¶{Lyxy^WQGSe,yq˥_Y?fr:u^1[Oh` o0.*@w;t!  n@ȿs0~=~wwޛ=ϫ0甚RnP3;_6m.$[&E9)◿cWUO|oڇoEG^g=* 9{A q[@[wf,W b.!mq\cu2j7}6zy(R(&"5~7 B=<϶k/:BL9>^Њ>ytU-]i=֤࿲߹O1bnhm0o:<<t7*A`"g ލ[.씑OPga [YY{~`?H7to np;?`2ftEz7#H߳0%=|d6scgifQZl۬8ݕ!(yV:=n$d⺬+췿3Њ;,om}g{+[7w{ﯾٟX/H.e뜉`_C<fZ_)&h1ROdI)^z̎׹[ħP]N:r, CD!_`1z8og|- y=鼊0b{\ok'1?xEp[ZQ֪eY_Ƌߊ1/ۺ'$[\3-<}O -Qx̧|`7?i?S 4C`^:`_7O$i~VXe QY]BgsXO`$ W ش-WI'3T|;=6?&p~ܷqn. 77;>(`[:c9ߦy}{d, ..j*˳lmW~ Ė$d% *UsY&gM}ki5HV,{8y7V. ߰_:%}`fߔhh#.q H`4@z5K>Sqv Lۊ2XFΝls /E@C azo/_qC+Ʒk|=/c=wb۔WPcy+n־2;=þ{^z}<ǹ6zz\a|jI,cúݔY 'ByVc1=+N7aqdPYj%o8|{F~~Gc.`1 l0_\V}-sZ QoŧK[kCm|;'U_p,J^B5ѹ./x;,! *B9} /W/.q7sE0:f`Foqg`UK5 i(?pXe[?k)\r3 *Wܟ*>qb_d._j;?l :}q(nb@A[}f8c@݁;+;:f\@큤>i_Eir[MfShke)>Ԅ2ry*NFM߇ű32췪{x̟1oR|fl㝯?(J=Q YM8tCEm׳iȾM܀_-|fqG_c a;c?+sz޷%ŏXqƥm_$DY"-f֟+9c?U%5GNtdOCq<uaC;6n1 `l ߙ|z>?%ȝ>-p3j# 2G>iC'?L+rr1nVu,ϜJy>PNxfT_C9?)-Ex~8>CF߿࿘߷Cwr?>1_6kK~F߀>;kV5j/*O:w)1/3XT6SM`A?cNi{{Ow(~.F{`O4elL`:j ={ck%OܢM: /1AZ 趹Ӱ?qHb)Y˦Z(J-@GXsJ &߻˛2=[/4?oiHmm#|[{>017[FC^_ub{t]}E^x֊ZAQ0Ï mI{Qhm5|~`BZjQ6 吝tV1.gq/+U]-[aV  ?~7 -}-#mjⒷEn΅gz`lG )?ې'̷1Ϝ˼,a(4?ojWgxxo| [?cߑz#a֧1`>~v}Wu[U%/Kg skF%Qľ"[hl;iW%kۙv圉Yj:{_#fU}m/>ujk: Z,jL~zߝzn\|W؟ˉV/1k=P+D[>Թ5{('s?w%[nss6^pWk?Ӛ;hny6Br^!Ϙf1#}j_zȈe`^l(3Ϙ6VxzO[}: `kOrʹa~ZMҋT{/u@ycDpv?}?*yWJhC~yn*۹e=/ٲT)ƠN:)ʢs?iUʫ:z]˘P>ORxi+rN׌dhsc ~'k5o}U \x/}#9)xEq9xIҷO+ řϭirT Rj!1W3M#YSmϕxDy^f[qOPZ0scϚP :ްW o!^^lFكϳPWf3.9p6?ev_+Vpεʌ2ľ)k\gt~^: |ԕzݷlpN`W?WlQ͏!rc0fuK_V8- >b5fET$F_D]9gVuL EKe߫BΕC:a߶o䧙/`8>Fc>^ZJW >[]ŧVU)OLgA'6QHLz] THQG +_<'UusI{Vϻ|שeYS_2)%IOe,&Yk%Se~~Qi%]xc.|}?xopenimageio-1.7.17~dfsg0.orig/testsuite/texture-blurtube/ref/grid-0.20.tif0000644000175000017500000066240213151711064024474 0ustar mfvmfvII*> 2=RS~^~ ,&)E0<iSr__S^=0*_& w"B!iMgRB@:fi$zE2016:02:25 9:51:39 x]6n~L<=_>}EBm?':G(%HJf6^G)RAoc0ov 抑K7?]3Dȧ ҏ#>hǸ+v?g$C%]IKQuØ:QQ|%ڷNgn q~k\6` pUcO,~;Yaٍ߳-7x0:!F3A:s"Sp])%z< kqD?yH_8d7[/O> ȗ| qC篸xM[ՍwWF2|43z+n}@1>w$_ɗz͹W9m}O!ɫ|u<'Hs)$~t'}v:NmOKWGz( Q8u=яY<Tm;ᆡ=|\B?,}ĵJ}X/1;̎1F}370|2x+UxU7| DWh=p/{SGʨ`3 tk|$#Xq;WY8?ce/ tr١>T-L;食|DȰe +O{I).Of>b=)Vk UϷӸ~z*W?-çW>ʹ@xs/9%[Rr)\w>q!Mi9G϶o+bџίs, 2\@~|n粬Z_y5&`IAcI{_`;Fkrġq .FY!{gG^nr^w01>Y:}'p@Oɗoxp~1 wonlp=7f8?ѧԡ'J>Nj~aY.Qus,O@{~2_y@p('ԕ N<ԷrF,Qu5{Zk7m~ŹpE>^ă8Xl6[n;a;#wn X_w%q}yJ|}.@ cwMs;ƭ{&0651Rtbzr;UGynG(fkNf>N(;Gҧ+n1? hk!{WGnOa~Or< EܴWsr-`𬇠Ҷν<:tpvڇ!&8eȃ.s:=c7 C`y> E{n!;Cwu𴒏s9&yQ|xAܤۂ NܴfL/|"shwaPaXwFTA\ \q3?л syy| 9lg+ 6xMQ0ns)WK8nZMK*a8ЎW֧5:5NynĊ11/7 ķfwYOzjOu}bDz^?aw!KNWOrG?{/\A70\a_xtl^'v{35_Dz0ԑ=k.n Bm)w1NB*r-]o,3NԽnv]<7bʞ>ڟAޡ[W}a]ݺx~vZ*WF1b$q'n(Quۻ~Fuxs$ UQTap^yC8Q~b 7_!w ۫t/_{q5~8bY]͟}bvvlǢuպ@#PQMx;x܆lH9t%N5܎SS0k/t~_w_І\˧| R)QWqEg2W>)M8y%~0>d]BC݀l'.Aqw? յz0y? q9֕=rfx3/sJ.1^|yt51ԽFe:wcw[ @_~T_ea<]/^Ti&t-L~u${/u~>󃊿0ї/X^*Vpnp*F֍n~}@m`|w8?}D\/6'>`y>q7~#"f ƝtiK: 2/~>~]g_rQnus#6Svi|6:_2T^Gdc r ܃o^P?0'Yq/yu ɺWcջ'rn7t/Ge5)Gŋn(PwšIGev#GFAi8W 0[fe ;vJ7Qa\(VoQjc vA`ca sy2^`~AwkN/Է\AW7+uvw |߷Jĭ kdq|øQ|pXs oqotG.@y*?~.s,'^`Sm6}H 8,oi^`7Y~Ś[Ϻ@;dā1J{﫞ˬxfb~Y~0/;a[ ڧ#a㹶'?K9)}WwԹ}ԝ`ϲSoNsY 7~{9\O[;Y?r]~~6OPq%o{ znվyk=;?(zD΀(y e\$Zxs $+ O ?bS|q 8cEl/$,O{s?w<wo,GJ CqƍϘn> fݯϗoQ?<PZC`{9OrfqSLw?Qr~Nsݪ0hsx8w"v\ډ;+{~4HO}hێ?%7 %3wơ|_4me=c]ݧǸbqot uK;8?vҽ_«.}Vuu莓x/~?OrJ/]U3f\so+ܟ#bݛ_ugndG96հV&[/Sn 1k|?T'a^~]Tfn&D}[?_lt~`x05֡"}PۀCO@5y8+pH~O^z|7(Yog_dl.@ٯ~ǰ? ߻\6%؞ux!b T .ٽܗCO>3J r+߳=d (cC}A>{ oo|;d9%ٓ_1b>@Ă 6cb`%ĄKe7N'Er } c@XM(Jʅ}sЕot%;CsCCb?S;;ޥ~}?ayZT~o:?!@9:}kb|1u7C+qg#V\6~AG {y%mGOUHsiyޒޟ KGym1/\Әڷö_5g; >,C)WFxo1rhUf9?|9zG;H<~&f_t_ǹAW_Ǿp9g[v-y\s8np8I {|:/vevr/{;a7yA?`(qKآ˗զt<FOq օD;X1'}@>|ȭ[%9bR,9/`<_eǽLhbnY'` WN'9qtQ[-{xk|_6ytvBsHk/ .l$ɞ[^߂ӫ}5(]m^g2a$[w1C2nu9 bOtҋ{]I^Uȥ9kTW8}W-89@~{7[J_?ú=G{j]'3[ge#; ̓q6q?T[cA]D\EܑHhs%υȉp y7ٙߝ'z3c݀KUN\%~n= {Mǵ0YtF֤ eɽp;͊{;=;+&U?9pm˺װs% ?w= κΞGΗE=)z,;ѦA:N12?q;|?Qcs?eNW_ni_ub.rL reswsjmט`Lwno3k|ٔ !)p9@y#ɞs1?~ [=u.s1y{A1*'9W &mu߀գ >o(W[z9H>Sxv^,߲:A㲓,񺤽ޏz/ilWڿ[/0rW?6{|iɳchC9> 9^.tt Wݓ܂ K5~r1wfio8@_::Q\N=Tdbp=ÂKAfr?Ў׿'#9kPWJ-;u;֭ ^o ~Nb$;n1h\~_$foÄy^pݞV1um6\C|O80Ip~)9 \oKd'*O#L\-twsx~& '8 GBja>G~%_ۏS׏|ޕ RݳkpWv#9Ġ\b>be@r?ξ*^ǠxQ&8:0q#G57xE^#;FzV1擔.AeHn_qShu/ٛ=OlNv3qF֩\5jI>?xđs&ŃĄ):&|Gpi߽8}pm^ɀO&³rGxD1e;QֱߺwAy?s|HkB>y{po1㇉v(dk+9/V|1Cp7"ܯng|یtpƹY?'nn #.I]=$Ac}{pKz@A~ۺ>9yĤ\l V?k^1oۗ~)|` l(רެ`}]~w{]r}}.}\…< smdjCx~KXoGGcmйa)o^] |Xu ʤ;+ԗޗl]x=a+k]jscn01eԃ,۱37f~pN[~WxoMGGx_6k]B?nM@WIOX{~i w ofx͝m~j;n.'cرsNkfG"AI=TIdT_ r3? 嫽Ȏ뚍&DzŴ{]ok~(a2ePv^dNofEy:;v,);iM>.c/c7@yh7.uV<W/WUώqWQ-:qW]Hp,ۨkikGlG>ܬ#1ov )|ԅ9$Lh3=Aݚ!u|'yiNה-&=3@ȯ91g~oliK2%9RW4I?p^uv<{CK5S{e9x^mɯ?)_x=su'_kd,kWԧX _6>,|nq$'amȇm~s X.tw(.c+Rh(zud^ZC_} 7a>A}Yg 6 37 (X &۠.|U1 ~*O=Wv;{GuǶp>0&8|1Qy{/б=,?㲤&o .-1Ys+2XQ(#FټD!S>.ɀ" |p{W(߯׮cPo`R$[? eeaG6T/K|碸Ϡ\ȧԬõB7Cy,d##pW]Xgq4T+cn貒w]/'L+hkc9f{}g|F OndC(Qw'6 Ҧ&`Qa~@mhӨw\sYgcs #ci|H $)Uρ\25cn.3_ߵy(sua=l2~` e]J6;+d\x{њkgS{<ʘ$64Ux`s 0<卷߰s,6u|uJ|@a2en'<>7h|1(E PiL6"=cz/swʭo1ORcE7 _ P/5>k 8ytoKe+(q NDy;e=5n几~L_BYX-=J6r42=r9fQg[?2ۏx^ R29qҩ~~r ˼{& 1>'ݒ=\^ P^f?0m9q/xyQ'|`ʯm;m_Gb|`f+.KY^>!V_T"> gZ_fpD^ @hX3XOdVp9 M9n{brs=)ǢשI>X((WT?p90ws ,ɼ_6}̯>p 'tNCa~~J'ٿ?ʭ;\gfy`Y!It{d0FFz)˿řN+Ȼ^V[ v1jG\.ɖN:)1phqX>ea$4(4>gfHKbw,Z P tfΫ?Zܕrq]ܞRp{x =1\HUZ܄"xr 'u{"P0)u1@ 1;<$SWE{K/ TǑ!K?fΗh;H7 eϼ*˫owb9Jq\2Q<az֡fYH%ZH=Ay?1:TzcdGd2HmW/h[qn[_g;)},+ٙu:۠4+gZF*gZ{bGz_g{2dW0|ar~рc7{7~ز?nm~ؖp6xOOw=UcbD>eN\b @`WwCavQ"7&3gzȩ?\> -X|_)݇́$y-?[_XWk ʰ'#Oa2)'`(G;@eVziv{3w{ݷvܿ5O$? 9 9(p`3 :xP~qR4EƗ\eyv{;YwPG*_?D[Bu#Ȉjaza vz}V?8U' ?y:`[qD%_O!*vCnARl/+U `} ~9d&Xu+OP tqL)F1_h 25=hwvk`? h/r|o%ز{G^l{+E)G.]Xu;gF7.P诖2vڿKW=;#sbj>{1()rh(̓Y<oտ}+6ۺRgu49u?uf~kbߝom޺L1!>|28L9(=6Ɏc$h<[qe[WWk3ݱUj"c@$C% `{ vna?לCj㡮;K쨋se^b \>m`}ԯ |$YO Wq|,Yv?܏i@?wbg?ǹ+o_kzҚ`Zcc}ȃVQ."/C?  bm'|EZ(L1յYOṫҨV<.L0>o]嶣e7=1?4 ƣM^@xz?<6±g@à%<[^^we;XF @YcEJFa,_wfgf>'>$X[-([\ʼT2Kkl/c]VK˪'Y4zoD;ʌd2 EfJ#E Ώw_Qc{7h*t"~XmLBn>?ƒ%:?;dJvkgd]L]Hy jO_Hy>9'&߫<ۚ??. J<1m |0l#8=>/ߖ;ZWv>gWů|:KFW>w{[Ff-=5:Mg^XH9k`{c'쯬j[=.?Z\0*}}pW1@Y=sR~魻 ]y2 y$+_kUߑK?l^}w"2;>Ҕ88Q.d=(v}:-|Z=ːAexknADE\ e2<y,K?ggT.h6"պXx`GǙV=^Gz-ø'.΁e^ǔeٱ\>h hmde0搯.\Lv$K51?u;.1@}=tCyu S!i*ه WMv-q?˵?Rg/;^mԌgugtvi_Kq .E`|D(1XVNYo~lTn~X!3߸>X.w3$E都] T,_яibPy06w.{/X .]bmRU @<ܨj Pxo荲(c?YggJ+t:Z}(,l<geƲ{}='Wkf_0NiTv̼B^R~8'UO;gruQQs8Ecp_hUcǕt'mC  ^e P̋ ?W_-sibFy*O``pIG'hmWZ4Bz J>tLT(sdNu?Q 6_ = .'Hs}ԥMΑu P1(gଏ -Dzp>p壳1A3b2^zz~b%CKwyL.x, K3^J&&mt9Xڽ|W=ЧGQSc0^Rck"Ѥ}VS'_#A7|ـ~zV {$\Mbcv;c; l4X=/%}vƦ{A{C|_i哎Yy-Ud? وKXtR3_ig 1vhSW194am΍Q~~T66uLcN7 TLYjp R-qI4h mFo!G{[IW :Ϋˆױje *~R}!{g3>?v kO)syb6&#DK1 3ynkwy4G81}2Wzgا}9o*bt+~`v}`9ehCqS>-U#^ޝ"{z1<<{]:' X_=.`CwڿcOA pkV1͜4p9M[w=?g yۃ.#?'/`? IﲿfQ7C[bv;s0U O] `:>x}2e;KYSTEER'F20@3G !σ~giuud}W8aT2&Iz~R^W{ʽ<6@MW={K|iXO*@ߌb,@h/'=BM PkwqCLkUdW9vMN'OO3νQ,Vٿһ1F ߘ9; |}S=Wɼmnw_}?&eKSqK^?lg/Rګ.> GP+J瀿 gI~Y}B +WtT23K3SLc*{;^Ov8JжL3WKe!梬jK$}szS-c1 d:_9&VS+ʃ J:xO~Ѐc,n#ا%MbdV쇼vu8@/Lry/ }r }*0vV%$+16(~8/; !O2|Khs; jH>FDH,@K^lυ;Rgag~޿R? ـ z)b?h#2ZW̿[ᾀ E\ $ۈ~:Qb*6 'x] $m?j%eSN$U=4qeV$#z,~[ɟj.}euE~cVŹc(~]y%k I?[#tj+mrӏG˹vjW*&u؈>eߓKlys{Թ˷{[y^g-NU\Ldm!BAzgܾa^Ęv+nw c] onG: @?N}kx~>/c? 6~|CQ=|CpbG}8kW_=:#Iz_OEy=7`_B/h_eڴo#q^a+69uu'@G ^TG젣L:|\~iᣞ؟n/~Nq6/96^agn0q>ţ9q2Vv8z^2paP3||.αqzq, =c_ z+7H!@Yˑ /P>Pj}_"{UVmM%rqن8SX>A<>l#Xލk6F?7~x= C|z :'쏸#\θ!GLG`+x|A]|9濨ko8@a8@dz{l# ·&ѱ`Op|.h8}LrbF#Ob뚿nr~?} @$fܟ̌V8.2#W[s:r#w1p,S{'uЯZLG:ؚ]|_4 hyk7>>_=*/̏L;>R?{ nt5Zȉv3CR߀e[+Zl`zr#'@| y7Nvs &u͠P. 3__S;Vq >SԚ?KHDݩ {ׂlÜ_ Xj@q_(79YsG^P>9c9fvō1bws? yMWז\N<1γ{D4ئ~&8~\v'|PH[Řa4/{ s`j͗?FܿB_ﭰQW`UC|-Uic`?@Nbdr LWns+y 8q_'n_;wt=z2+l燇>8ql&(qK}s~g|姚?M[}ϑor`;sźj?[bs~Xrmq- 8Ǒ-lz>pTGq6 }ާH|.?7n18c}Σ,yXDXO'kB~=d{j~y@({7Y"5[i}vX.?1~o}j/cn󠯕`~)G=ɫⰾ߰?&8_3nP}S-v8S5_N*c >Eg'ޠ1'lo^8/0&sr{Z#8@N5W^K:[ﺖ_68RHD9_? Nǵ{\{y{j`ݎxr9}(K ̿n < Qag?$k'weך>kqa8ӱsEӚ Uξu 4^1@9Y5XZcub(g)1f~S!_0qyzwl{'vl3Uu&[.yA`}8>1zUZQst}Vp=a[3_ϥ5OoS_E=@]9@K>u\+?p;Ȑo !'=%-t[pEN/x1- l Ae-:Do7t{3?x19qu|' t/uօ:.yGK +/[Pqs-`-N|ؾ0 ut/- ?@|gw8͹ L05_|mۼRg l7#t}|[G/7oLsr`<~-s.ԔMzGto\}ju*9rb.@`lg>`>f/?~|>q?d~>'OKD>cmǺOXn>3Wߣz5 rq+}+Z#&q<|d=>;|^6;Gd}' ^jP}+/έq6史}Gg7ش+-\SNjK Po6< O`{>lj7C Oq?GPiȄ_qĵo>gI>(#n^3WĤA_c}~F~3\-?\|lCO:5}i kj?#||հ>b־. P?qh[|]#kc97v(?ƚ>4:>|5 ;7λ4EtH)QPẓL1@x;`? bos̹!e&zT\踾ϗ _}>`[xlZc3\WzcaLbmuLF@ߏy^/LouoOuC~^-\ n3瘈A晪:tܱM0'=`m笛׿[}v\ΫN}،w׵LxOotm`?X< L_WCGR/{ܷp:c^/x9[v`7D<`sޟ@|,+w c4_| 3Z0η2 zs,|D0~~/+"'O,q/HVl+y|2"h3YoWgps;< Llb3'@.W_fnG_]&Ƿ&5O/:P^[3=Mv\%_x@Hm8`~9Bo/}8z\]vˁWs! daX  Fr[^x{z}#7p:q1En|-8i(bbB|;b}3 {\# k0c`TG18i"sq}<+%@u6:׸`Q>'r83hx?`=`<`[$0/? =/tVk?W-`0I_kg 7'm'n ?{wK'Њ $f|q=@~hr\m6#k M Xh=jca _#gW]@xM`c½ {1O9)>!:g~Ձ9I.(9wG9kw~x#`P~?<1w#B=HpzɃoȱ:/r F?q1̹,>/5U<`Fy 4N`#'۶bmiqg{>7oQaauUNZ#u'#7p8pg[{{: B.@<*n+70 ;#6kktܥ>_gEGlzMwqޠk'fG\Svбg3l^!GN6+x0O55NXAgJc'O}\ 0O{Lr~֫Zp>7VvW6Sk l.C7 Ǥ{n/q>ssNM^NÇw{EXS.?7y?x~0c?طo۞+2O:oq\<_M|> X~wz_;6clav 뼼@/`?zbgw0==} \t ?`{% /Ԭ@rpx`ƿVgq﷿ݤ Fo[`8f:&P햲X'X {ʧ_LJؾ V'؁p_p/אpxe 8 ~gt:G^[rO.]T^oJ@m?0ve^3Ygt}/b_7;σ_c:?csPa~>p{` ?s=uuRw-०&uk0װc[6Ġ?}Y 츧??opq }Ru>a%x@9;\;~_˭Xn6o}\w?qmXbS]lS=.p7g<뱢-yh2^cO}T w ,{@Cdb)cLc]0} x?/^>\>ncl?/<=_O77//=L˗:L =>W~qK}`Kd+Mڒ97҉<5/0ku3C _б׭6@>챿躔l;0xA+{/ۓ%\y[WwZ;aN<us7|~}w<@9CŰN~?w_ O?}ɟ};!֭a9?v߱/\ |+؟o tyx@Xm;j̡nGx| ǫ/_ rƐr.o\qcqw@ kw~-[  k/#&d5x>+O_͙׸&xk[C \cs<tx59~uOs^Q^Ox/` oY=8dXbC}}=1ƟI#xzkX>`~TnjR߶|(W/+y翅=k6 ս > |  (D>`57b?Y?lwiA_#fr,;]8*ob}':,)V- 'ckVq\cb߯i3 OZC@K?)W@um {e=?d1. d1~`S͟nq[c\jggcYVcۿ}ޯ#.Ρ'\|}˱5 s\ss|\ : >r?cncr<ĿkRWHLS-@ɥu9Tck?kQoĖ=~5 bMx@fVݓ$f59{{Mpߧyp~`uAǘ8/sR?g;U̿ݬ o5=gU:"?B/R8P0;sY'#v _?Ɉ^3rͫtz+,#k켥8FSp:Z;x>m>.pC3ΣqiM!M9E1>߫ha0jި_ +?2xQfT׿G锇?q ϧ|S`Y_^79?S& <`8wsZhF9`vv,acyuȄ4 زrq7؏rp" >?#t@.}m/ `lvB9ȇZ}jwo em7ؽGګy\ voGɺ0aGlj'5c<~oO6 WMjXw^'ʱ+̿˃ 9#xO2ޕKDZǘWN`1?ͼp_FuM㷥\ik4b\U g7|;0%׿gƿ<~b36&A&۹^h?sw&?;, ȇցsꆹd~?N h1i-?S9p;W{ؖؖw>0lO,X XdsSsҀdCvϜh@ʀ xm vxO?$OPǸA~>}_m7~CHM)M_+އ-3? }q^sZc\k[,_:!ֵ)ckv׺?8#(*lFݡL-]DD~ i=!~u2\m9۸ǨXox02{~LW]ZxV\8bTh|NcF=`St)G@vc.?x㸍o'|KFm ܓu e- ߿ϲxZk]˷Skïy\ѭ|Kk?o|lc>m] _9ӣ9z6 ow .7_p|]vw]/}/ #7֛6q?ϊQ}߉^7W޿|MvʙG٢#Gqξ߿m9Dŏ*&W`Wsvo0y>OѶv̶yΖ{.. q%.d|X>`~9yo/Ϙb;Ĺ?bSw{ljXım۱+m#11urq]>t;/~^>ϗ$91o5`>k`Ot@Q [sb `_G@ybCr~#G1ɰ09/5E{3?o3S_힗wssO.\.~ΩMڇ}OQ][6(#bs NZG04-PDI h}q)GhG[\t@i:}T HM`_`CN`6}C/a'+qg!=t˗> w'_MGdu~S G5~=lM' =XpدWc>^ۨ<ފwf'>xُ+ vs>ڱrӷ55o{(ǰOCǍG{Z'B=^t3:q>`C bɉK __c֣NHmu=ư8xk=?0(=aOd2ԟC6Wq2}ܿ`R/k|Ӝ5\ߡ=~#?%8oEsQ4'_@1oچyuZ_avԉ]uc>4|/s9;kiF3z0o{Ϣ!qߒ+sD ,Zu@%C `'| 6#^yNvT?i1`j*FAZpߌoL]cY CP`:gf}y |sv>qY[=k/mQülާO4ٰ?d??t`I/﷘fuĻ~,W*O";0jcः+o._'z`hŌoW/x47ςzd?T{5Z-@%y}')Gup}_~o->]y'=@4)A#0!Z?VљOpQ^Gng?ϦIsߘC\6l@T[`boߏ0Kɢ9`F/8#_hw|3nz[1u#saʹjzUmcA=#g4!yw`}NZdO/056u?SBk0Ve?ē^ p}?' ADnpK9Y/"~`¿~/ٍST86>x;y 2&K_m*iVDg$H#Osaxٞ9j?}/?c 㱶s;3_Ϲ^dL>.g =G- oFm a?lncgx=g={O<I U 51m/>Y`Y@EcqX#k|<֚ 0Mf&mbæ3}!qbM5b<#Mq-8_#!#.  #{뗴fw?kxا*v#;Ss_u90ё:$_x緽5c]`r͓%3:_6OTc2xC56y}m/ Vu O?癵̜\>\k:6{jY ]cD3CXHsxC:,iabVĒjct#@>8#ua]@?/lGo{w낲)?p߶O_+&ۣc2w}j-|{\ G0Ns TtNg^vpr~.7۳5'd{G 98w5ڿ]3̙?si>f;ߐ8?aƆ Om~ƙ xitrs->_\_G?7I|_7 o ВqbQZt@NJ@5d'es9dm@Xul.߱C?:ٴ'a~(B+@Tlt}ܚ?z݀8Ǻ Z}|~ i\5Ư}0?Os8g j5Wa/ljS/쿏'c_b;3p͠xs }1s,3o'<y2?5xm?-{[M.O77Nq5@?XXFu[DZ?:úd :MG1^Zp<~;ޠ`]@XZ| NCIPÕ}@'nͷ' nPuv}m2~S4&`Gkuki73g< EL 6+/,jia?_4}o kǒtڧ\cj qQ^{Kx' rk:l;FC/~c?Y^kvd~i:7S}YqbkN9gQ8Ǭ(FPK3Vq~+~y}ٶU#e?H&thl3 c9tpߌT5qyjm965W v8jm6IS ~uq/hνVz8/}>83M=6>4q~+0P׏&762}Co~i3Pm>G{O?hwfy՞z(Ǘu 3 r?9/F۾?ZSM.hs~=LlG[.u3k="m쿇ӱj0N6㰍gu|/o݆9~?a.?47ur?oqXgOUOok9/tߔ}ϧsɫ~`yb~ViY2~uHe&{#)? qd6YO06=Okyec'Ē}:8>ز`P3>OiU@hme?>/bp? WرKk!燚?Cw ?2h~јp沍FБZ?7}U;B#XF= @y37~l:طdZ7H>i)ֹ5"ޝp}gطP6Pqj16Ğ7(?(&PomYXhCozuqvƦV Wji}U8sm lb|_Mz@FW{orD}@#?5VX| uU xo$Fi{!?짜9ϱzzԣ1>nGWGވu>}hj?ۮ} O'5r(ƽnR_C~n o#S/ ;yxנh9q {PRDSv4|{^:'ka('=Ky_۷p_;" ۋpyM+Mȷ?A?%Ͻ'}5_u۷ jG ?RԹF& 0].4@OMU.qm>eZVrhl:)(f{V.K T\jۖ@1\˯CI@2Xh:0OZO>n6:7jZogއvdKz`ZS3=Z4u)O.rME`Ӽ߄pE>r_~6qN~V7hGl4@>+p{y>zb~τfztg>HW?V q@}Y#zu&m[ c2cWsw sUzX//`?|6j̐+}[A u^p?>k.'6КꁷŠZ :`7G-D?]/iA~5_g棟v_=~?pϨe>H,l^:r K ^5U79`㩀.v^C΁5`}~ y"+|0 ?O {VfISxo#|kN-0rO|zbO|a-@?!Q~`;if4j^@ \h/IJ%vi:`ۥ``W10p\ҁƨz\?>|K$߉׵p-r8kd>})c8osi?rQ}۬o5g4g|пO99F:Gw})sף|6X%j / P9Q~'YW_^=kKMPnM G˷^뿔> :oAj3 pN:p}>Pg 3~-DL?uJ*KRu]Zxm4f_uNܦ5d6@Zjzݳ~o8Ϭ?=[;}ޅ|>SyZ KלlG;Y?ϳW ~g_OZ7sɝ*nR8IMy>ⶏIx!XO39s}kz?˗g[`ߣE6hkv?ڨʞjcRKu~jPm9N|`8뀵n#G[u/j}/kZ[d `ǫڙh}_ d_߯y,/>pdr?D[0bThy_>"i{8+' `}؟ˇ5f`?.ϼ؟ b1Ј5Gg/Z@.x<-A,/ k Vm|sL~AOpOtL}~iOkO{*ח>>_Q?C7 p>ڨ 2YOk4@jr-z~+ra0kldCu/xO?#GSǼL`|dN,?p~9f`4E:[;x&p &6U2y a\p?ڰPpX:W~i[|šƶ9G(Ic5H8tOOJz\] Hmre|_|ڼ}Kyo~`]dW^e~|NFuw$-kS??i-#~~kޏCz}/ 5k_q=:3oܗQhz}vqe;֯5k~Tx^ju8.x- x̯K0/Z:`0Tٖs܏15`Rp@}Pqn.Ӯ>FbRi/ HJ/Z :%k] =~y肘 c'l3OMO?6=WGs072z?j }p?,u0>}1=iUے[+ho}ǘ[w2qr؉}+_߁HJCOܯ[V!7TZNM]?1:WNZJD BuK-{K8_ˇQ P-[oV6^ Zthٿ.fGX `{mQez#8|:Z@g.4j{6IS|Cw @kqq|k7-Yy%o+ǶY4_c0b]ZgC{{}g_9ׇ:^5ZZؿ_U|0زgw &ߟ??ޔ?WKa{ u>hy[35Y`c-ak`* >#k MZ~IwH7 j#Pbx/EJ;gcSs z2n/ms?3,(%F 7OeGOy~?c}䳏~/Sj߼7bEs{r|G| m6^󠰃ϊ~}/b-W?{ s?v߳^NCZ~?xw_tOOkkh\ tiOi>^VB|yg50Z\@pߢ& 5#?k' .\Z h} `KiY}Zkxony?JK*ױ"JZ@YorlGuuhؙ(/pmߟhe=C/5*=D k8??oc_o>?7>+֯reMݨ?I j??xa^<ÿ&}k\];Iu35@] TA|ۺ'k>^Tjuĵ^ǽ}muy-\([1j~yi?(]?p}eTx&VW؞?Ml1u~6/J!ePۺGySǮў'E{c]7ޗ_C|<^j18Q_緱.wi-`}nsyo񽰞5Ab[c6w#`}mks;>΍>uςNmi;3vk6s^g qN Vbux|icTXclZ} [_QOcqGW@JxWz4D-[a?k} gG/tﻤ:)W4by3رuEb,@Y{i+o`y?r;XMd5 h~_~n=r~f\ H ŹKq-̇06ٿj\S;0xRgW->]6^gIf8;s9O#'wX"_o=b T/ yw]5#>GJõr8<}T#` p8e/4;kmǜgп~g$^ 隆2/濐ށg?x|2 B\e~vK<9oT e>y5M'U{S|=%o6nx>J=zx[\?'< <iFHg2h9Y_# Jg4ߕ\矼j#>{>m!]^Ci].ߒ@`i8k /F}k<01 mÜRE=>@=o?5@3fU}?>"w4^߯t~\e?#˫)|1l] Yx>@?~;;_q- S;iS^4JKAP?M>@^'/=kkpݡ@e\ݼ@h@׾IbS/0F(P[qgY9M~=4r'XEgA~腺?l8L?§?q.oF}u^ %{4?m;`p=Z?a?i6s.[g8MK^&?4OIv1m>{=}b?~x0xeAå [+ڜxo<[&%rBљc6H۱HTp@: 3^@# PbLi]"g9B s/uU^:bMv *Į] p)\d*s>yߧ=&73_8 'aC2ЉiDLNߕ; iK>fx^Y_}?1s|ħ@pyOgmg/Z?_(痜x)v^+ɱ̘: 8'qiJ;eޯ#>!D%6)>v3zp=| 8ou^u\X. ?\u*}5wŒEwט<@|<цY D^I09-ncP\/JHBk7-pf=@^ ?굌s/˿q'-{7)m7Xq{5@eiN O]~KCo_ZjO܏^Xp׿KiLe- R멭ܟ?4Aę`(I"bgHoO_)ޣuiBWjkxb8(8W܇GPI9&ѕqղ/9_Wt@7t@0J*i7U -{rT6RX(Fdm>L k4{ǘu9 hbNĺ `gtVX[jNB/%g87q_rOH?+qO=p\w"kN1J=ooҹǢ^} >)wB[_u:?i;ICTCuGH9?gEޯPΏ犉kӿ(?y$#i2f8xy'KgŜڗ7~yG?%]&Λ$``WGec be[ƜfdZpCǥZс55M^X,_4&i>ʚh_Η փc_ ("|i=6/>+0ϟ/kRL ayUuq9&B 7Yu+ 6pϙV/ 4(Q?rgԹ7?r4߱ d߽?%b-/>a|89?ץߟs(XARu8=X/Ϲd[Ώ?Cm_;?eݧ_p7 *ގ(Y p`_IdM~@]ǧkcSs?`|= qx>R11R0m~kq|OqK]C%rǜ}f?LX='0|H:h }:_jv'\5@WkVsgҐ"q{A9k'$] s-ߤGzcZ=W[|bvšcthc7Ly?؏|?#秸~eČ%sq_o}?47s3?dN6>WiSg7^ 41Ww>|#Ӛ}5 ^to@ Lw`s?08@TX+Qi+?Sq_?g׶z`z,~k3+o[ P6Oyi~OϿ렿?Q5:C?)_q㍖cGybMws5`%w|lO^[3߸o1g~֢#_cb_?89|"4t<~ǃ_{/"ߗ_ya%3؏^0x `%85ʨk \Obn q̓ZǗ hhOto94u~YT|[*3P5aoU Lf/ưWuG.zofXA hx%B;jkY0k[mk-h e Ép 6&/GsIྜྷ0I GfWC<OG5WYv?g 51sW ⶤcs0i?__șluќʒuuz?G$qLWS Kߒ9K" ~hQ(OO@sGq#z<#3x K9nhҀ6w3s?ξ{jns?ob.=: ^Ah~fM0yn=k>5'ke. 玸'N>>P}RuژMw6h?ii,`ƀ63s?r_JGx_^7ǁ:`VIo}~@:S|nk|qgr Ъ7xu_,s`SRN]~_<p&=yW?x>nef?ZimIab1Gkٿ@o*Mfڿ% ws:?ŸY7I7%}O=}_'2y?k+c #k  WOyQZƿx; ҵ!1qB3uywnW|L]ۯ%sȿZۙt@en.! AS/+I 4i9lt@9{)5yi K4]?15_ pOj0?k:&2<@ZW=hZi._ksBpN壧&;64y`v@ڢ7[ߢiI {X~1EKTQY:@GN@kZdz~y#gFC|]ؘy=o9/~_?e{x^C\u_S_nxevZhIś6őg_hѯ.G5  Ě3/\C8(Rp2M0?xI{~ rd7.f.@I83:r5!ZbC]Ӿ}5{\q?6b '0υWy-0hb~~P~wϟ??:sTCm 9=Y5rb' *hgT*9J엞KcAs#JSnǵkQW,|ZOe: "5^S]p_J qek>K+oAi; 줤S㆕>'p<E} /YdPwk|7& p_<bcu|`BqO=vn!K8\KU_}Xm̿o^ <9hQwo+?j_Jd㸔VMmJQnXG4F|yh:_~XޱC񃳶EOq|篿,X'qd͸( Lܿ$._ 9p-%8/T3534rC3q_zN<:Ou43\?x`?p+Q!-sW\M< E{ 忌x3}46x7\\4'P>c^K^qu~zb/k 2kB =2|4*.{OtɋXkeqܤ{@>YZ<'{1=y;dz~i?~Y=ף?5@ga>{}\>..63-~MX;>$u7mavom 5}|/oF޿NoclD0rš\Nim>P P:R P+U&6|oS >@ Vqz6bR4<~=N<_[ʁtϳt{ho;xxb޴6~Zg9X5wt٬C^_7nѮK?r[~~|Y؟,.Gy~Û(8j~?~ܿ9?u~9?K85}h:)}%r5_aV}wz`wsk+y?ӧ"5c;n̿r02k xvz48&6AxW?GȽ FIXԙGg x>_h1-ycuY`s=er>O^-5p|K~~?t>N*qHZ*Wk~5#G @D8k*<"8k}|!q6f>[ck#+d|_%C4rǽ6槜ٯ)39P!V2 =:#?j#^<lzD5q@#xxmuߤns>J$^M̾@9o1'ijrj_>1oT՞@7JhFh7F;k\!Kq<>cy~'@ϡ9A lyB{[} ~d*RQ*72߿ן[0_㘕:ZZ:{a3~Z:b'? 1%bd{ia~yS_G篑w [Ɩ3z?4q#E}IT*g͘tITm}g_h 1JG8q_u35kB1.J{/~b2_oThǨx<̬Gݍ{űo-?/ϼ?Wx~~1]5珼WDpoUgx~ ?r?{buFS~g<{0a$ur{}&[L|zmZ,>ie@:~o>w/-\`ם=AdpoǶz {@~S9v/|XxI/o~O逢":f+:@N:7"u= BthqS;TY߫K w: q} ʟ |0_^Xճ#[_'>/:kT)]L鄅H;$ k+pxڷ`0[\s_bd.1߿.r#?1}s#So?sǘ:}8E8U6:g1ϖu '7g.(=YuV5~ mk]+ƖHtg { ek^}#..=w@^\h(Gz@' ac{;IĽp_zy }<#(dH]ǂ}vkV|o~gݰ}]㼟:?e{giЩwԙ9ES:X9|?׏rߤZbgJsOy4V-__re;yd:.2/='?7#>'CXK݂(%49GLZguD^in[y mM @aʝ0'w~| ׸ p `ceZZ!93߫wyf.Faqtχ;iJ|8_?e[.ь@:t52ռZw|UxO,'jCbD7>O8xe qoEYl|֙OZ #0_}ۂ*Z@hF~@#? HWޣzc"b]gxX ڕE>t@x^hM%cN@ooޠhV>/ # z58g0hT|}?H)h )Ns}#۟|7kw4Y:`qrWP[rW?6^#ǗyFHmcODC'H1?ѧ|Ŗ~߂L׿ m @_؟x1:BMRإ9OyL\GBٹ/}+-JUQ#> h|$-0hG?oL F]xﴀXc  x(~B kٜ&O~*'4@ hzh2`v},j%iߋ|Z@~h 5HXmAkxV?ʏu; <@?;d?#K(T)#w y:0ێy>?ycQ$ b#Xo'Do'JWG$<)BK,V/_lO:}<}z6okto.cEE;@:/\u484Z19|<|׮>r59?X 8vi\|i(J9.>_x}zܸ"wߗLf{l3yn["?@$$,r/$jnXxu+_(qc,;}^O_/l{JeuRZǭ\֜;;ʖR׳v oglle+9ߢ/Ҏ$UK;NZ/R,Vnd9?[, tA.=ԞIm,uIk|}oo˅J{^NϧUx;жH).=gprm뫝{-Ҙr4VVW}kwoHθnv\7?:3[eGH/XRO8_SMCo}/U>hYP{wvޤl^Z;kSVޝ4D mYm-oIKm>\z[~P[ܦA@([>?^RI I6}?qGWB_H}NK\V/s:dYO*Wk|Bs#v8\>D?U^3v~p4눷%p4;׀\7rK4:|[Uôсdʂ;8}_Q췱/ &<9?] y7_p?+2۸zguL/2^¸Oca ߞ}5Kpxh_Ǽdc>~"8QcɎ֠1?[;׽{sT5@>^wldjss.}$e/}u%x8`>{Fkt=ϊEypw\8 , ߯׀l=A0`6VLp"8rZ"} H ;-8)3L6?㐐Ov\=k]٨VLxvj^!8H8?12X-sݯ@uh},u߫N Ok M(ؿ+O~'دoX=~>1375i}l؟xSUwUXqύ ld9|Vϊ8lWw~@p6?rsp-1r Č(8\( Hk_>n?O}m5?XJ`c~ă\&9W/5s3܇5BEW}xwv5ЈsT;V쯠+~%@1KϤCHjͺ4.0r.>6r=g@@z<"|cE허0P {qs@bZIRݯ @p;x] 0#8XO,\f>@ %kk0'?x_z w~>ܟ9Wxr g 1`Gv6!9au?o=Gt 7wݿ{X8x:.Ǹʳt]s\a`Wxrth}7>?h"|5-E]W{ 'x? ؼj9&̯j=V/z\?[Ꞝ'O*OrtV^q 0/ 6񀽵_͛P܌fW}$C`W`|>@7_u pj?xLǣ<27|16|?|vqqtR?p/sNG>^ϰUS}0ze^9?{aiɿy}#G3=p?Ck-o>ߖ]'BSO%YOCO~w}}-mG$ܿۿr`-濰ɠsaNi)'|y[٬v z߰|]E@_}F JΫW+`HZs_cZ7P".WS2dGbM} <8@y"x΋>@|br @]nj8@rˀȁ$+_ /&rb y$*s5_S$e9~N<q;}fL?fy;O?\k폍~)s?^U>?]ٲ#}iI䌷0q?{uWg?]c,~>6NkjZ~J?L+?z;~q)eˆW~V3k~x;-CSXZmnjk|o;6XdKUlcgtd&:@_>|@G8An}s/M@D/H6FU'F@ @%>@R _y `eCd9T ?c-@ _q 鼘.p`e.,N9(irX'? zG  w?o}@`r3p_à3`a>eU {^7rb t|F]㪁ɹ%5rQs|A2N37τT[_@_tؿ?I?Bcb?}{ۨ>*Cr==S`c}U{^ȬwͫI3η08Y? _W'cu{.06`8[ۃ xV͑`'pS쿫pϊ8@>rHT>9@r?4b_O`d ~.<eRľU`G?y3: =g t?207qkC[B)$+18^)bvmmwRy𜧺_}kMwا{xQݟYgWܯ1p1ﺿ85g_v >6v}Z0<*?v./|1?1 Gg^$ax/n8 `_`O@IɮM|-)GHr3b[牝)Ra̫C[>x x[g49Eĸί~84`?3~P`x,`qx=F_?"o/܇k8+y_5k|>|}1zf]#y;uhV|_!ٰk:N7 ?^?k|~+Ijgܿ+/%0@oo WxKV? 2.TY+scgؿ|' >^ X\KG#Vd  yw#O?57ou޾ծlEv<=p;@#`/M/?{s:z_}b4&g_7qދT}InbbMFk]R^ k150Tѯ %/AAuU7ܗ5x~ojyUc;/Z;ԣ_F8D^/qe:n@_7Зé 4/c8ڻI#tNbrp %8G?x݁]b؏/k>o؟{wE9:~5'kߟ~Ȯi~~#[_5M0߰m\3Ub_a1VmK,G`=b3#ț~hmznt6| l&0l}z{@J{'97TJx#Tв4^H JIx-l~p `@>vyLPV^zX<ˀH_>ug\ 8?N8zpvj~4k?~m^uFsn9?zx/S?=CWoG8M⒳_:>q?O/c],g+չJ@ؿ_@w}].Nc\\gY4ֿ/;/g~5|~}57}{hoK΋b8.؟m:/n5Wi9oZcp}ڟL?p%&BÉS]4h?/Cݽvl_ k~.0>9ɑ5-ј7,]^yg3쯨uNϪi|.Z,tϳdA?Q-1k9ٌoSMp_|*C!_{jl~*0U=^=_/U0$._lm3?wmzZ$d%7g0<(|iy:@Cbx9q]zN@vV!j>b]ڟE}/< <7E߸Ul `N¶(7) c i!'9dy] q'c%yu>+#@sޓ{׌pkCߠ4|~?gsqOt?brUݟ@[)_l}RH3?T[{_Go˺c0>^"ϯ ű?wUZu 1>ٽ`ךBO^'x/j)vLxoaֆOǼ/,I5^+K/b2' _^?]mrd xoMwMg ڝd޵QQh"@<.@/ޗ:M߸M֍|2p5#WI .*{?%hԱk~?rXco7!1u= =OUX̿f];xWUnw߱?T<,`e~IþF_m|Uq{~<G1_Gt~C׀ O@n ={s)9 8,\@xUb[YHGս/=,]۞ 92W '??UM> ?%<<'jL'^f>]wqUrQ  8s̮ـ `br{<8_?\\$0nwxS`| kW?n#޿_맏ktowvn85b䗽yӸ?Mc[x='clŹ;]$ϵ5TS7c!j27+W~i؏s 7~Oϊ\CKO*ϿU!]{Wy{J;rh}R1}nwT'-z_XU/(lޚw[96u_ [ ﯺ_x@zk:~%5_9O@1Mj)CxiybϹE<({jdEiH6Q/t>c[,E &Xm-οco~^+Xf_yOX8^5p3/A`{hZg>z?pc5ϴKܟ|~y305W~Ί2y_/;@vlZK6m-<קlĚT}>98S|Zo* H 8rdErx y)[X5=jY.8s Oy@qcC=xmx ^K.׌qH'dWy@رqXؽ/b9c_/|<~3xG\cwSש6zTyWVwy~ɱbmpOEb|O`؟7~6/m }d7s{?Ej :~1N0޷/>x6W+XOy;oT7ԕy_+ˮj Jn5V޴{_+sfJpo\S (\@֛?HrzHyq}04\m d :O\'lS8Aijt|bh3Y _8gi-/?@?Ot7b|?*L~v?y W4_\ϖ7W;sObCܿDmh͓kw["bws2\#7wl{)Xyn6ilEMt{'ihOۮ؟ˀs`?\'a}b?#{iE}~Sx{ Xl~ܷ]&VUuXj#(<<_zsnU:ϩO.Nf}<|ϙ+@1~S&{&ݣ 9=M J}^k!>@A~r{!9y`W lߟz0U/tz p| zX&PpSYS+4l. 6c u_q: }sC 07P{o|t1 zSd|*L 0!O9ع6N<po>9"_`kbyEPqoК| rك*<ߔx*qBkQ:slB=4N%]:ɞq6n.5qN rmCc~"l- +zV30[j^GGx%EΈOk-^Fxy߃ ޥ\th `{Zp<X1qzus4x@*;Yhimc1'pPZؾSop˚a0O:MXax>)p z IрrP;^{ISB %\d7~<r̟E|Q;4?kW?99{Dt\D#p?r;OPC[5?w@Ӝx'f3̊og`+~^Hmܿ B='O=@7)31%>`,j;k2vNXFȾŠ~[y/`V>ޥW=ZuWܛgѽ{%x%x~a|EYʚ#pmǼ?|>yKN'Z}G7RQsch;O9~Poɱ_<wHn7ml?%Oy4VX.i~#7!@{ gju_?$em/!k}v_^r~Z7zU}}] {x~ԴnOOd}oAjw\x/:0/RО sw>o>% 7.4Pd`y2=㋀o.$}ǽ.N!Z.?نu2xjz`'TO.[ chQ.x<Ot-kI罇aϽ%y 8@:Եy8}c?9!ﯚ>Ls?疏0ayV_ _slyMֹgȉw__\~g3*T%u{ƮoDE\)U8kފ>4P?oF.~7_h¸h0 ~(۾4V8h&s {{!π"ǻOasUn[^o+{My(xhbr#gߪPNqU}~ߔ'R,ࢱs@&6 i,|<]ש@qbqc&s3 >xc1!`1&@Rq c~kQw|e`<%+|jW ܝ >߉xޗlcykr?0|.#xS7Ly͟x RN?kУg9[i@~pMobչ~O>/(VOc;axiD_ Ew Oy~j{ WOcq0}[@ nUmq"'g%XMUf8A?(S?\w&ϮUY\{~4V.'p]@JNn=]~}y>@;h5}J-+(o0r&@+r⇫+>(ta@p$Ex!8t#X @PްK.@8d`J?Nןp[иq.^z3\ @`<>3|{p1q|?2|'t𰫻Y'yMGGGO:X:XgU#M'_slCs~|Z.O  B~ѕPg8@ٖ<_}(}Vj{o"kDQ,>:AU s_|X5NDqvc<`z]s8~Bp@qk?'7mBfט>@[c @QY[_AG<D91p|/pQןq9{:$p".$ob?$@D kk?,V~ )qny͟h^o:o?"J@3/yfy%_mnכb7O1Uq߱?֜ /Ӓ>[_p$J~G04ߊ4l?kVv c+ k4_Y5u_^ HjaeoǮ˚95ilro4oτkg .?𞽋V\y hm]m.u: (tq0p8Oy"Otr8P,sEy o-9ܠk!c>DXPkluv q\7z?-&}y_:^j¼S}Ǿ>uh hyN1q:M:wH~2&?ؿؿ+|r8>c?j00'#"~yLW ǎ`n -6c^* c Vua/3 ׿Ϗm^q;Ȍ܇s̅+Ł %p"{~i9 |<8 ?DDCp4d7M4}{\HWp nyn}ϋ8U}|!ؒp|Fps]<+X-v-xz#rVE ߁w4͒ɑtRsNq=#\Oe/l[(֓  X>^.a7f&5sώ#W&t" v^VGdX"p h(>O|8߀`L3}q)W p/^аM\ }pp;+V;}>tL8vFOH}Il`$ҸO8KO>:|;x.%vѦ?Y@x }.qhk[;/|&9W?DGyOk}~:W ?i 􍻠!#/V#b#|s5po~Wz,:W+:%49o絝Nl;ܞ=_itp4 Hht;:쳋EkpNyz/b܌>@{.WϺ%X&!jB?aLiw/-9c<7q:K&X@|H@9@P1 >Y#8@P;_>9+[+@Ƴd kLtp[`03ߧ8Wv|?X_Aیq9@_W<3[P`x`mgx~; &10_%'0sAoW!q`?{5_'_?ݔ焝}O3s;o|#ύ9Ws~uhL؋_~^ث q wkƅX&<oh/wϊ bcv{/0.cO{:[lhmL H 5~_{}?~";W?{p}~O7wh} 70d[~18<7i /[pN877t3#o"_7O|ۮ#=h< kj1oAc1&s5v rRlb|hegI9>k?3qy>mG,u|$ ڛZ8x=>Oq9(85͉US+g|-Va泉`wMmߴ_9-Nb ܯZk` *c m@ Ckߞ_}CZce]9xhǭƸ?!Ϝd13+.GK &iUOsc~Ƈ~X?SF`/`/%voM짿o`>G[裯_'8ǯGܿb_\F܏уo7vp[Mp 1})5ߠ;ףb;Nm3"P!n`NK ǥ?Ru?ӽB_o}?P/3Ƴ/`}wSM#k^8&E?c2hL~7!V?9{NP(Zj]l}mĹXh]T+ qs4Uous>C$?@No~7$ s_|?c8@ Z @xWQkb-؞>g䳚;PSlomXR5~Ͼ?Kzܿ*.q?3lohC뿦KqUf7)1talcl >C]/&!yxO߫?vבl,AęL1Љ!1b?րp {~1cCԅ w߶c|3)[l/^ u'ܿz/|{^^О`KO8'1hIS >b?N#o@zG=x7s؃ pl6vWՎF!Zݮ9N{Q##c?W=]z+o˓ةtl $_m@ c6o#[?X`Kv0FX@(3?$?d-OnZ%LHp1־ػ Jo0g<]݌sՁM/mOE `y2Nf@`7gu5b 9濎/1o qt |R4/_;}^AsGyF'?a#M} _SO8}db 1o')~}nOk\/"j9Wq"oi㼀9|a?_~ˋS[w꿤2tuKnݱ5/7!vE(v|vW0mqN W8q4? H?\9=9q '+?䍸Kp]=P~7?4Qx^8Ox. i?y3~$0a?"~$1^XG6sw{T?v..u"', sL .`ؿIXQs]kMx~2o9@OhccLr3ն}?Ƹ;W?c)+O*7>Ob_ݿد>Y.::1 /: 8fqaҷp\_'߈?z؀-"6^)=hoZwɺAR3[Eq_l_=|Vk2͉R|[`iQm2ٜZb?ro7Ha_w8!O@<:p3kRN#?QmGX'r8BЇ{p#Z.%y|)wУ_ o8%?Rm5~E&蒝%Z@7}Z !mT_|H5#1Mrp6`WrVX8y.Nxg`$0Ӛ ܩ)uZU+0Tc}=iLY?rIʷ0ri, +NW㾾x?kד8 ?L>rc57MW}79鷖sYo [):p.(T a˨Z@?+c՜U`U\t;u t" d]DE>d\-~\/)0̼N15L0vïzzM#Xwm׾\KNQ+|>Mͧ/%fJK~-{sſ3cC;_9I+XsZCYm/v3JO)"/~'v#cM:D%}ڏWQֈG+~6ع0^?v0?D9/ѯac뷰e^3pg`g 8GqnG_\_@Ap>y?hَ0Pp.T}jίoUD Z?{ }Ri[ᄒU 5ncwD 9Io+rUh.#*'r#xo7r!& h$fs$b×8jNغXco:O{OTyF`ߞ6gozN+^X[֘+&ɬmMhF &z@K-X9N5\\.^u|ouZ'.݆M~lofi| -A,y.aUQ/ڿo&b~3g^?L? wUDos+/Wʾ'ۈ1SnM~?jqr}S`/eZ.ׂmZw_=z'+ߏ[G?j`?}_/IW/n[ļ c}=|==|SăF:;z[6LuXp1on{:'$F߃cItA $/2[u]l4}ZgSͿSgi_jy݆9}/ `mz^?bV9q kFob~&esp~c:K~ B}\i&q9K|89TeLu>'-`XlVqv>~X ZeZ|.D(\]# lp {vڵ04ZиI.`gDmxg|;W`lw L`aW'5"6&6Lݞ9o;J 9 ; kF=zؚ?yk9߿/?8@#kG[[/?d7.}sf.v6bq~AlƼF_ܿnw.a:6s9e~w &w#[ @AV }İk״;^%E@_U?AaYs5pXz8/:YHrlz: J8KC sQsln➵k~ˠz(S.Hܾ/1o3h$H_=݃]㩑&y䐬}+pnӁ,Z9\$hX@8JXoƞcѢZP#i`Q%ovh?9/1sc iR>|,O| 2$6 5By&^9w-'Od>mY@k} E@R}o%||KL80Bj!k,^<wFWqxe?L_ >[M>غk쟳KlOv9Y7ct^a4z.Y;E/u;K"/q)ig{UIO-kԽ9u5v)ots?0į[]kkO +g,wV`WW~9auiJNW%mKbu7^j xXh n<'}Z;V`ZOG.c ǘ̹M.D'$9XH|{K<9_q=ϊ/3&p\n> o$O4fe]_p%oZx0?w9@źhsW8@9@)@ߚ5#_D.t9'0ERV釩`+9ZgGր8uou`]jsߐ405i-2al<P>6CT?A ׄaH;g~Mrkkzxެq[ذ~vZ#`H~H!GL!NIA|  D@ܘm:KQ c&vqΙ@r?9ڟ{}'ѱx13ݏо>޲_#y8@ с;uϝ~5,Ж8^.ie#ضn'Ǹ?wN9U,hq:O:nd (̒X.>/|~k yJl>Ss#E\-]`6X 'S6dHd}3wnnsouEAu>TLS/؁ų1_3Ũ0#8v FyϱLjO`_3yQɫg]Z?mv7JtG|_˚Rp0f\/"R/l *N:?i};ÞyZ֋sZn}!^:2~ls ت+`? ;ٷ=0ؿaR)[3l] FI6u7{ؿ ڳK]ǐs,~a$b^h؇j#nvK0︾I`s ;= ]91/99?y|v}k;O+Gr9 bn=۠/8.>IH}_7aB8@~K͍w+pn: 8ٷ4xcqhif5@쿎qelٮ>)n&fc z#zЋtv`1纷f| S=H*2~E X@"`Z OkQg `3(]@egy6=&5?L`럟Z7)Ο߻u*]>E0c>Z!FGﱲO>h?bcgsjB3~g}UcY=wUi:l:ɂQb˽QkY^3xN1wN\wFQg-q`>YvpMNrIv `M_/Z'C+5vg ̚RsʉC޼k)V ^ x'g%睶b`Y/ 99!k1n珒{b_*o|?]Hޟr,';/u9l qܛ_uYu~Z 818F1Z:`C8p {`rƋ} qAp˚'"qTdY!phFS=ϻP-%};brmD{3)@Ne^ Zv1D?BQB߯V~7t!g7jЗ3a#qHc-1}Y?o}SEy1Y9ufh} jky3g@q__[5?q]\g7b~[%W|\wYۿ\z3o9lO.LtCSv-THs4o#ϣcuߟ iŶ;sjPsƜ/H^F ufyªk/ \/~[/_lUO8sFܺ-56u ^HQi'ss*~ 2Wz\p|t['04E'7r `>|V[~u|SǃN[_{[CpZ?_0vm;cn;no߃; o0+͝u}8_53rs'7C_I |6\.`*Iv1rF`*i3'`&8}| .`yPc9?78Ol `k~3z8N9+ M?q?ߘ4~˚J-32oOu>^|M/YoxOScY 䊵B_O-cȧE3.9k0QהeMy鵉\{z4L`+o^ ^ ^ qb?==׹ K$䙱&0ks~^_öo۵9;'~㾀w|@9| ܡ.۫ y!؃Kp8j}vHUUO_Q}\&K یq?WsM?(ΟK/}Ww\?B'p=8`=i5~E?>7 ,T8e &ky3Jq3c|@'E}ۭ 8d8?"Pk@ptvm@ctvM' @Q71,ׅ} 0z-X,1Y#>0}RS3}k/O:yLq~Y|g%>M:@6 /ǣIK%ݿ;Mjp[ao>_+s)=Ͼp4%iKqt-O}϶qkcWN_-OOH=1LO_+y Gεͷhݹ[`'柩Y\.- wۅb &~~Ӹ6c[60C.:Q]_ 9́7r y/s^Ϗ{zԜs܀k|?s&9!nhXk48P{ѵ?}8wQFu[`;\w=Z>jtQ߿"g_ ~@_ҁ:0ܾ&rY׊>;BYzDG|| r sz8/UOu9u\g1ƙT`~l6 {]s7\~mc\!P췹I1i[zK=_bM >l~(qrqn>jjoj:@k5\~>rk LC-.^0??gOF P\`~V.CP Gyo &~gkԝ?a\W4aT_S(}64~3V^ :N\ki~!mk;8bk8T~Et9F^<9uWˁE7&y–#fj'hǾl DXg~ {w^t'ڿ_q<7_.`L;U ErQZ;-A4k2QDGN%1Fk>1@G7d\|~ǚNg~ŸD 9*`ד\p?ߢ׍0gp 8IksoW߃;p|>AS{^Tjugen߬Z@g>}zNFvs }΍ku\50d@ZWƸw6imZrzCo+Z}3!ۿ4mo_Ws[M}WW:_S1y@\ǹ}owl1qySmy_(m߶BɃD`m]0?ʜx\h_}|=bmX_Q}<=DOԦG۟u׍ZC?{.`v;9΋Ɲ\X'x)wY'[ 3­ak+@ssyE_p_?Q:sM(k\.9ۨ&4zA:w|F?Qu}WA{06oɧZ]#{/k5,Eܾk78Cx.'؀?wm47;O^P;Pk->Pkc]c\/3):*nC18};źlL `qS>G ;c>jci=a?&D;>\jo-AR 5 C V1CRz z #7>?hq~jg̼]blq_'gX,YXs}'y|ǵ|c-1z}b2k1hm!.棭OE ` pxq pLXw KX"{Ţ@zz@J^ěb"tE-@8B[b<@'7R9[78x_GwF 0_Cwv;ߵLb"7mqla#54>E @*XWL_PIyߏ6_dN?a|L DݿCg?D3L߼eabwwܨ/F~o_Sl˃Siد&o_&O`g]UzjGW __cW֯T9Om5ouPͿoc$݁Wc׃VQA8?~9Vch}bذ{}: 0/Yȃ.Kl:?/?BMK={syN59'l^P /;n% @GvyFuxۯ%/@/wAK'r{p5vwSǸ c?5cNLԇ;8GUt>_~>3pkﱏ17Zߏ~z>sY[z!u{#>_ZI0 >bhR>ܓѷ.1 Bm5G@uu>y3Qg}@6Џ1nZi8u6(465lIƄ@cd*Our͜\J7ާuf0xN?[׷Prw?}m~꜁j 54 a1j=XSܿdn?'贰iߎvOwr͟J{M~ɺxX'Z̘$_oX"Oɺd7E[k[k^]יsnshRc-~/ֹ/~֤uf[N\6> {@2{ؿoa!X\c6ۅ[°;>+q8I=H=lw7orfMOj@~&e֬ 0`}ՋYdap<-:{mz/w}!/W{soa_oX}AkRB_3 l wߣ~c'G?U֭?{ŷϚIQk=Qwϟ#|~|y,ZH1GuZoQ&9@\s{3Vk'2CLp>pƶQ_"k9,|·*vjQdPiP&5/fHj @\ȷ#ul z ~vBN*j?2y `z7KՂ7g|}6oձ_7Lf\`7^F{?>/܀~pqwNϷ>qcVj.:KAi'- xSP@uT K/58L,} -ɧ^L :BD{bMZ@Q&VkҎb @1`1l'0l.(\I:&4ovjv^fJ5>*}˟|/\04.y `x 84:O(> d2|`TWs=_Nv lk+Mú@~!{YGC{1RwCn!퀹P7ˣw-xc>'xYڿO8/4ߟ5~ 8{5wb,N<鑶p}ݵG:8 N8=8[]'$?7_$KXO5{iZ ڎ9@*re@ɧ >.b]E}z0~XH=PXG @h.&xQc:F6=3yj:>o[߼qY]oxΟc[9k ,XT1mq)G0 7pqݛ'[")Βbj%Δzȁ "`Ⱥuo _#s:ϼuP?+\;fb?Zi!˶?Kh Ϋ܁-߱̓ y` _p.vF=l%^smy-x?6]hQnu wnνK^X;`p{ ,W/ycė~< Wq\v}8E".(G?p? [n?:g-VsهaIQ6OO,OZ~?koqlG"egnZ|ǣ kp~W燧1oCzNqM#q V'jW|T|u?f9k/i@\x<'4+K- Y0c.>t @'C/tyaLa7xXc~?˾sr*lL&PhFͿrx$?J?ajGcT}lmOcۚO5`$u .gX"cCe }O)l(~SOm];WhzL9~6PǾ0Gm y|3|"i?b >ZLB]W`?~~ Թ-G y_ºs]@nj:_i~R`} 0np5˓q`kzr-ǵqp'ZחYc+u>?{XG}S'u}C?Dy?9_M.sӳg1/Z9װ359Z3ǟ׵F{q\X xqO~y8D_`Ś@WW^xnc=@^tpo@1Th1Q,bY\czb `7cd0$| s&SnR`j"Y3YϿOP߬<ʿ<} ɿ~>? [7g cf.bm_;R30#'UͿn˚FHyؿKkz/RȺF?IΧ;{kz?=Hy'LKԧ1_m]܌\i?qgȥK>w^hԸvehw +`ծunwV Xft?w3 v~O]6o{g PdE_[}f ݞk6Gu׷H:%X+gg`G>Ø: @N8W`}t ̓'= ѻzo>=_:} >8[| bf[<; xtA`YNs81ՈYI⡳1k-1<5,)5aq cW1 #ZY`}3؟fb4f.Nltio 0gj0/0k8@ww/{LxHw&LzWtL?/jWj5Lϴ-oXL:' @&{n0dϹFhDHduyauuc~uuT_oZr;7&}}߭^_XxTsc5ں/` ES\!gKYݱNxE.b^IdH:Hb-XoŢT˖i;$XH2;lg;2x{Jfnܟj96.-+l)Y󄶌ѴR>9$8 "+Vp]7!#\w'8ֳ3z8#R\FjLi203+29mc_\_ o\Og 5sƿeNa[eߞtRʥ=F'~m/Anga.< /.8}tg.}h[֢N}^/8Nt,*̅6qzcb7eγ|lGl=pR=]tGa=k]1EǼ[=Z7^~-k-p/B=p+GuA`:fmSTʱ:\x꼄"e5@(<_'tOVqٯJ"y'^TpiUo;bc=Wc?2sղߝE7nj~)Bվֆd?_@aⵎ=N6sж7쟶}ߙG۞pA{I'9z}po/c=%Ef]bu|0J SoC|o|Z`ÁϜDn+ü.t};JɛXrl0N$m?mzq T4}$ f鋏һÅYr\bB $+|C==ۿR8!GRUU@1|,G}2__A\_YM0[S`"mWu]xI24 .1^㷘/ %: fWtJ `S5luy=}mPj!,j@ןWU[6]nڴubj1LSXYؿ=*GpBqJN)Fxz*XM}\ A{@)04=&? OFC 7t?97w$Θ"Ϟ[p/0p~d v?eyx ޟ_?o1c#iԁClx,\g" |*>vM EH D>Ͼ"W~q%iҒ.wђ؇G-jK^۰y&h']Ƴ[ʰ:9.L7Nd& Eo?=~8t`(^&nmgJtx||;Z$gc"E+\nWX/biDwRO$vX> w[^B|mqy`>}lJx=8uCeYd7}k 1zl8Sá1[607g}3ɮ'q3y'#2N?G bDm`^~9@χ1xbC*15~s-P&P)Ȁ'8{V%^j_W{҈ >0pKꘟظ8*cN4 [Vg~q| |ke}/̸!0z]iu}| ĿNN3ah @2e _R/LxNؿ͏vOdmؿ2z}R+ cܿ?]FchA9&Ow-c%]b #-umk.,*ߞ9meځ^sw VML8w$iXnȫg0uCNXs5@: JTY9S༿+Q!m56z~؈{\@nd˿uXW1G>~1~t] CRiz?x|@b{_MWڿS/k(s{Ya#l0\, K sq>[KM|/5 %wTk.NDbC }b ؿ[b29aL8u9x ;q-0|?=\pp>d7x}3$2\/Rd6GLN|#$r {O+gF~1|兖^s!oC`=ϥk\8*0d/jjɩK'ecSx4 }F*<#e ` fu,=wm lϏss\XOALN?OYxd]͋&?5]ZLbX1I$M 09WM `, $c U1Ȳ+ca `VsIhcb^(<*b(Gc^Z@}"Jտ1o54{My:qb/蒹h1LG8PP| FWr\zm/<G*y wYWrA)*31|σW33]q[ܳyzO]-ۯfmC[߾<ӲAd 6Xr箘d=>cm

|D,r~4.Ќ /mGXKC -z_gxoEj o޺>U7(l)</*U-##o5_ tCwm'NvwZ?w`_f? svs֒]pLds5siY?uؿ9ؿ2qj .z?ZZ_;}mjw6mcWv܃_оd)ǂ/i{1}1,?2qg2$W _}zH~NM@1k(*\05>aD1AZ @z _q}75Q(*Uk`,ϮÛ |4s0X P8nRfl Ey7$k)ȶoC=}cH| ohsC +X挂62 |? QQ^@yRӵ!KؿKkj4maq̽ 0"XQ0u~%9og4Jnna>?tU?=1}`sfݑt%La=+{6?wau۞mE?/lL:{nMSWQ[YYUrOmGH:ޝ l>I"e_X'9O<58?mAg1!V<(4m[rvo ez.2׌em_,Jx*]7s[ฉ}cc3ge{qh!E?crGp-{'ҿhXVG?B۽xXFr3{(%ZC5e/ϵlG2pa$U]e4=<?:@̢-} U~l6l(sLvS36:F7rJao {bhlߌ5DnLb`F|=9` @+G.@qV(7ӟW0&ؿ5Okf g!_GпGbw>U7>'1Z+|?hu=1oo.A_ 碬5 b}zroW:ȍS.?qABTs?6qQ.`rg}u}ec  p^_<镶+`]۶s?CͰ_yLu`y, l>P{G?^]x%5O"-ocY8\Lf"?DFDں-`=v(b6Xޒe*9F~ז3¾Sk[(3O% x=t[.MT"hUp 69ĺ&#Dd}j^cgcuoq8bhW sr82}zocd,,zD׼483s7M:k.' 3V??vװڌf?*N kB%y~Du{..2Z e_ASp̂f ghD4`?Qn AU zyЦcbḈ<};oj|{#G04c$H(F`Iӛ潢zOz}i3 `_Qq37ܲ9rў)}sg?ok\b3f0UR+Pz#Kx%DX{-D-329Xq[[6d:m'h׉,r癝p>s[ m2߼f<^\Gbbݮ=3l޶|䬩f[bed2rS7v2&N\f.@R;ǁyAik| 3@5Q翩h9P9`}h7?cYJ!׾?5v\.sM?__WkD0 mw[W[.⽿wcz6~BѨ_O[_9m;?ajXwxm?}js3\_N qeݿ1yd/DuOg{eMxg^1Qco9W&zͱIx>kٿL;~3{hO=x xYWAȽ?58֏`)S=oU[W<o^[oN^2^,,YǓی\71 k>f\@gn+2Hv19ܞ8y;1xQ9N9o | ajRΫ>-EhGF1lW;HVcG<6z2l?3/|G@ͤFcÐa ?}c44voS]~h21cpG~#9Z[?)cno1!8 ˺&};c- #W_[?q"p'A7yAkG;Ǎ#‡kSǷr/ٵaܓFcb^_[R'3`,1ESzDnycu pBO<^ e^Ʋ Yy<ƜnDz-.S-O8J}.-Ibpmn?U_4'2$pYts1(> ubZ Φ+lb4ZK%;XFG'.sSY.ڃ?m!cZ^~k3"/G8/2}Vzcs}mN_Xbnrhgy1sGa 9BVXC{{5WV?X69bεTq3tlwa= v(S2hh7}}_@v=zsmkLrllGRKp s3^@ݚ8he_lo@ mc,=&ojrSqLFf~V;.&0z]} xI?.@? $D<p<ʹ|2wun11a$~ rWjQu{:9On࿸s^!oMܘ=oOtҧxw`3k_T׏Fퟪ\!`_ 0KMXr ?aۗ t7r|^/qH>/> 5ueϹzgdk"U%;,# {v9j׵mX}OJx|cN -[.x!ļcx'0mooW|"/>g"yLjǧ,'0C{4n`& ~5y>C"D V @\$8=*˱3X0 _;vNE9o4s8Efϭ*#hy.s6G\9kO{6Vmײ3HZLZWf 7m=:&n`jc^׉139Wj;b/@/:&M ?s-~1I/*u^q|t%~!k[R]QPJԽ]W {alYRm׭}_fX ~#`u2zFo^ou_ I6j6zܭL_xryb ki?W+<-_|eL͵s)̜ʟ,_NvcG.yoZ3?]7s}p],ڱsϕR.y=`~7-x:#Fx2V[Cpad=Gֶ'? ly9[?8x7?@#yew|#vrIos^e ^bF wq"Pټ+5`Sᅨ&h@6 ={q-Fߞ#$S;Mb5@1hIk6X39Kֽodvd;%hg?}kلW?؜ "31s2fs\ȃ8Wv#8{0?˝L,ɗv/ຠ`<!??9d-|'65'O puFf`^ߏxvoc.>jz&|i_{[_]Q?`5r `42`e C3V醐KtɂcPF5 ?hơ zf<@?>~E9Icjs_d-BB_/۰\}쿺ԕ4~[A NX_ q!u YXWϙݜă6L*?f_L/߷-~᭍ hӇ[Ol]{%3ousnfN`i؊%LgZv?gD̛%sK_ gjC:y3S4b5`-t5k}S ֟} \ `4_ 翏SX m]B-o5@@J?^V0}bpRHe4r*i;q/}[OW-U_|^hD 8zFިoj2\݀4U翔ո7?CR=1c%}  y q\yA ࿪ڪq|,*_7s8J+Qҵy_xw>Lg~hn>O?7fM-/˳emt-$K,kNm ~xscMxYAqhf3c [/h5'@;}smy𣖇/x\6l c[o?~ &s]xI^(150(v>k2uw҆#뢍?f39>/kmmF:~x?֭Slo&SPpsyl"S_Dz~|<,O} m>w6şolG`S诟cp0qXݗ?]e} 1`2Km]gv;ٱG{6֞gvd:b.b< 6\ĵyvdADNцE%fL+zH@|\ ߻ksEYSS7lp1T1MtkŰ 1B(ݡ_@Yr0w#~ؖ* pc Sy7o#޾1^hkW^9A;7%~m9i@˘@KPxؿC9yq [:?ZF`,ZMq9ŜͯOy&%pb9[=s\XWz̾G?~w`<۰; ~dA@eH:cw?ٽB{bbWwb5Ë'v*Wv,+y|:XÃ6Ef]ޢy\QT ~llގ LL<=6sr>+s|筋k3?1:Q>9dhSkČcd sp#朄<ɇhp\;8 ǏvY`_6珼o_!}i%߾\VƝx>\Vrv^^ |}i&l|,J< ~`{ ױu- p@Fs"1z`oGA wm .$!x `_@'r/J۷4ߊ=psāpq`j(觥jE7@!Z`j˘Xx/u7_c3t}{cH5cyG?`<@PY~8hj_?/(~-Ќx'{>_w9߷~ke<=p̓'5q`Im_;/+oOy[-~!vui r5mMXJ=>ncEXkL΃E͜}8=>_9?ߣϥws%i%o3Ư tq h<۝P![1ǯ7?s'ox7)7z+ #6Z<090:@E0k܌+ 3s\W8f 6SgCWHv:e4/l3 z ;C ɘ#j븀sy߃š܆[^O y?BmhS$Isf <4WdT.\_?G\:OΡM".[h1ז/:r7@\ `ӱG}^:PŶ]?]_x(}c*75޿IZ*z=NbN'_W >Z~a`"Սi_5e}3c?y=q{+u_9*\\:)dn}g%_oߛB{9̃՛/pt1r5[/sY}Uxw/T>XxCj-߹?|= Eh"%o7psL7yeV365CH8@.A*۬ճ1H1GF ,}t ܶV-u1 p_g:s\ޛ(dʳs])s?"n@}Wk ߾҆k-ڄryY$Wm <>z&9y#sv^mcl3D>fv3CY_V'І 8=m#ߞdbS3V\FOɖiײTpwju1^&'%u51~ `r^>E}(BZ|:t&_ Pjw?<78^7XcVc|C4u?ֵsO={#:C7ypEo}>s?An7RT.}r߼:˺Rئnl>cK2:Z?k;6nϱ &|õ6[-D./bQs_Cvxox?xгoöOGW;2qV+`I.  :y.dfYxᅲO7b<Qൖ[Gy} `Xh&KO9(Omm5X}4L\;^M-54`*sG":"/0s(ScySW6Ōo=w6-e/X5R&~Gh?hk &8Ɨ?Ok <~$o?yv&[O"Wgn`y vͽPV@݋ej.112aq}vm ?LfW YXYL]cJ169E俋۱U΋[Ĭ΂?tcc;r ʹq~vmֲvmkZJzF9x^4)޷zVH;oZ:sܐ9g71x>Vb^S4otq/oc5Ctż&kmG_jBꚲ6$<F#T*w/A H0_.2a_hB]m]96[܅8[Z{G/1ҷ#wl"Q?=4g,{yMm;N,}7xSl.SWl^]҆79&9}jrI׶#oP_|W?jYۏMu.c:?( _\A绿EhsҲ0cNsbkh_;'蜏:)tśc:v0T@^Yg?iuuB&Wӳb1{_/4"y7y&_|JWL"^*y#뼇r}0"o4tWAd@7sYs}&kߏ}+s.w_/p/3?p H1.L6~Y>F?y?3}D,o|֗"^$3~G[Wc7Zn+3sIRdWk [֣-?eA"cYƣ1IJJڬgja(OE68P ?Fq4Z&|}*I+a1_1C\E3+tbjC2yzwk-,t#ļrMްHN,?y|]~ڌg 1h{7xvuEO/Ѫp T `C:T-P=XTPߟ 2xiVݹG_w%FKOhm:)Y_n}z J'O_0P_ ?seܯiow>_{3봊[ݠ/ߘ?:NWjΙ~)FeVDUk?v qc3\ m>6zQ?k { N[PWpD!~{>pŖ@~M!g=e0|`چ h?2 :d Z#,K-s˽Ww܇Fx+%gSvv1WSvs Vpn<˱v{&t>ɟq\,/? |m`(w?zꏬʝ/8N*xA-]PCoMMΠCs%PV8Wjjr~ý3{\je^dqtd=z>K7ײ( k:c}qƼ}̺1s0<~w|s(=scu<=YN+M[?oNozxɮ{3F7XS>c~N!jyy|s@f@o~hGhW(d)I\- c9xɻo _]sDރ;PnR9ہA1Ɂ,=eA'Gd5PڋC|Fdn#V'1ch9|vPZ{kZWqw@FxONFk7arc<o1av uu[ yl oYߴwM:rA_*;Ԑ+~ &`gCUR54hi *!zMvczc1&_XaL\qvšF )5Hz~q(5g0U?fysw],e6z,?kX(+kJ9ɧ\ ]U!J)ko~j[ͺk/ή2oggB/\``2I?d| ]xT|N_f2"7t`-%Ϡ#Ve_b.wL~u̜)3֠59 H/44drF̜|̺~xwcǽnŸw1{ؾ=mlr? M_N {8o̭e _E9;WHd[%%c\Dr_B1)2/GO2x7/MZf7SiO%F8W`6#o~>!e4jh;ޝ:bczPظ_Y+tپ& mB76`9&61lw9~xsw=9@#_才c~Hu|ijW:,5@?_@k?S< 9=yD7ߠƭvNvڿz Ap4ͭǼx\OsU%dG OŔ׍7Ѿ/mv5=~6?:.=qW׎Ῐt4ڍM4{S?:j+} @V0I xptrOfl>o:D==?uZ?8>=$= m*9{n( ~\>1`(`I"eCYr"{O#ɍ=o]^=e,"4+,y8ϩ 5CaV0&@Ƌ>..}>hbf^7ݏ5ϝˌkk=B,Er7 d|,WY+-IҒ}a>_C??3ՂH~٦cHv&sOTtO5 롯'ҹeNbT5Mkdb5HXoAFg/L=C_MA#.2Uޚ1΅ꡯaKðtRsHۿGT5CGB cXX?@e67F` od48ߔ0(d/rN׿qW_Vcbܿʥcf^  - Wm `=K%l@#` \ ޟa&=bM?rEA3' WJƚx_1W+LIrqKn7k+oK?|7g2/͘^d<|`m0>x( aG[3z35T*xc8?uR9ײ,L>Xu+V3Ǯ*i-(3LW8_OOmMh-kޗ<.2l@pXw;N0hGqsC+h8mn_{59Z_2TK/S0#~Px̯-~ ?c03w{Kv<Szn^ҧdT_Zt&ҚMRlvm=5$3IV-s9X FcE QFcdZvU_;3'/WK7,._;?pU_Bc-Tsz/^7WQM޿]7KayKKqC9_F??ͼMc)_O-!ͣ.g"RG/K׾_+y~W'iP` Y﵀7j0ϯ"8+/y+?L;MжOSc9M%c,O+`zBϯĀ>0OSJ[XPLZo#Z !?͹#0 H'߁}yWCm$ >x{ͤr@j9[t2zaF,)3el^W c4> ]i;Y]r9OWLo-òoex:(%sFXo3#}SI%<#|$>/>M&xLJVKo"}m>˥k3?x堼L<s~$0}秖Yg 1ΥvM?rW%~~* #+;oZW>a==JMez-,ik??=39 j};5 ?<J}v+Gq1Kk'5w%^d x/^xy;SBx ߣHD3\D+x&mq׀usME8Bxc߁<9ؒ5߾ gxQ/' %G&e׍y;}_ZAs*$_pd=eoO R 3i? o3}-1_څ79Fm U< ]J_H+0)XmuEUprܹKPW"mSu۟gRҮ=_῏7hxwcJw@2dmfgF_p5ծv{Gts},zp:'n@&s?(%1F{-[)[{+޿Nߕƾ,(B_K>뜯 {~F ޾fKSm,^{lꚍݛv}&fwqs{u”jk/HV )Gx}M^.Q!~>F命Ψ았ƞ],^Ϲm 3ZH[e(2aXH~KqcMǟ,XҞ(p\Lm4,$O$"[vQTWבt.G%\#|&-dJx+Nܑ.9A? t[4PwT#.u$SXÎd]Jw3|>!x-`z]Vq9v~n}θN_›^dm{ ׾vS#=i^؟ɩ}ڠF7",Xc%xEzlLl] 76řPFރq|Ex`VK38 =;c #oĞ>zKv%>ujrߺcجuL?^|6 :}_Vc_Pv"vlѽ:3C7U=VST{76|_Lio/y_L9{ OzTrJU+ \{ 6 {up_5 }:iߴ_:WG[/.kx$c'jg+Lfm]w=s#?XG+Vرe&a.)_`lNk#KnS,zI09Ec41cl./b ?;x;ߛ̟a<D2> wLό>42|.^$O"߅qm,";y>WkM?Ն*1; &-.s%:|_t7#?G>*e0c&ٟ]7nN.]dR ߹LFyzMyù<7E|roQ|wyؑ븈M& ўΰh/gڧsl]/%8N9q}Zؑ9;ߜB=--1ܲi_8- /Si]㿙=FYkzSb羔%?62?R֗e`Hmtg4ِ 6w?p38{5o>#t'x+/Xŵm+COuo2>sOM_W8/阮T}kwg7p>3U'R;"a}Maɶg+` YM[cPZcw1fb.QgȞ/W'1hQ퍚0~oxvQOVm0=2:>k+g,TΠ`.tv$ r9{5+s`R>lWx>㿎d&~{?]RWzs9~lda$(s9y)I/r}H>hb4}3:pӔuV 5B@k~߰å!Ws2}bs/:3']u~#N[/KL=_FW柣N2S9 w'-nQ  🶆9V_ `;5A 6߅ه _l3IӦE <9=)iw9t ]&4?`Ue3&@ꖚ|:yso7s?FNO[߲~߽n ReΆ{JKm'f3 ԧJ?mw}-'>,a{y?Jׯ~ܾڿRo&-&y}۩l{Pg?TP2a#WV1m7Fi Π3Չ7#p?7`xLCwhьˆw IQS_D5n'~hї3po9Ъ19{BNަr=,}gn*⮬Fl,;`++аۿϥ½\v4r/B,)!A=Rs$->׺ceDm¾'\zSrpb 9*FlB9h=Ν!] u2ֱ~J2E!k̿*e+#kpL~hLa'%Jes SMO 9fr5w#Xî y|3/K aE]@Q up>>u'=>C' GvO89Aw5?ݘBg2f~8oGKz?v*T /6p~-Ųd,/ޟܒynl4gלNW |6&w2_r Kv >zWW5B;ST[ gWM~ ?iȔDuUܗ,j+E~q_ s\q+P/Yu+ckV㒆/l,KnetW6EZX)BgG4o=r|6dM-51'Mwf2,S0{ҙAqgTB 6\Ʋu?eR.S0+7 }77X߀?lt=ixjI+=X1V3bH{P>lTa[p}p>:B[}Rkjۖ9Bp8UL&z̢]vOr9o1"wDnJ [{8hoP7:28ꭿu܁}ھw#/fB&'z7}}=-2 wX5rb\.GD}?}4:] ǧB Bx2&簡wP7?mXmVU;0o|$xTm k-XMX/?{M}!ڏu` -y;*WܟM}6'Bo`w8^h?))T)L_ܿ e++3VŪ<QۺmKXS.Ƌ/64A+,S$$66?S?kl,K8@cfCpZ~' ?;N0aCgЅ1e ]|s,3tߛw`#=UҮ̭G 9Ndx߉ej۷NrOGW2yrޕjd=4v"~cdt? v Мt,";u<=117,Rd%=Ogd=TܪmwCwB|\ k36o`d0@[.2. ٿ˽?lrq3m}籬roD3wz=X%9;'?d{GhDF_fxq'>w?7mAo?_D6(Fdt6j_xB`c_8y\2ǎ1_Y[&,s=lp+??\1ULݼI[5l;/mDw:R|T@FgesYsoTF6^m_YSBOe6@h\}Uu7RkyEv@M5}n6/u*ԩcdi$ۢo1VC|jD,?kt6լ^@/*G[mD?q|2-C*F'(1y[wy!wtf#Y-r>M!"wU/W"hw%ʻJ9f"k~}Br r9g|8a%v`׺~ulJmIj}g;G=F1]d+˒A&O%/ Np~3鬤.qL>Fs1v-=7`0lG7\kr,wQ_/?AfyZ.ˍ7~(d:nv;0z| Y2W??NOduw-3p9( |g6>2͓ 㰣?γfg LpO`/̀=H&4D?'g[\?al?ս?>rڏ7߀v@Ϻgi_|DAjˀ2OEZ?r1(dl\&yj_(?:<~ @۹oܿ+/ &aM_&ۺ=CU=UkS2ui[{ڿ rܸ:n%?WӨ yU6QF Z @P@h˥FQ+(FTF2:tFV6H24=?Xl&ץg}|"v9yV?~=ˆ;yM&˩~ >+NEKfi7%Ɛ} f`:bxlw2uZܺD[̓"onЮk y's-~?L,WVV6삯#?)h.`3ۻxNneM7ز3`y. {x=mͼ9/ƽAso:,&G}?l1?,]~mAwM??Epe?· 21o*g42 dMO'õ\ ECsރm@|0`,} >EϧM0Srߧ>i? 4_Qmu7!F݇ʉes+ohm9wPM?O.=mp'`{ +2c< ]/qdFoI3׫:{?^CIs+mLhNw@Ca3z7?`:.xM .3#]pC,OEN3yH6n%r~!?ǹyg?lފNjK/v9Zv1i@a7kbO"T=d2Mτ05 +Q f o+ج3(0@_Dl,HNcڻRek湕F~v_+װ7>Dr{6+;_$5܀|Q]n&#Y"dwY0|w[yK~+??_b?sX3RZ|?|Buumἄ3g\Y*#-YslF*/_$t\?55[[?"> /rbrTױ Њ4/ @5`!<ȕ ?կL~o\a4iv@5x}y$_6w`vᝧ"_m\ޣ·`{F6ܞX6dЗiG}:=42Z;-3#7z#Oع I9kf:mޤ?n,r8ߐ0/oT1Sg0OhmC4`ҭUVx[7l<@MBKX7Bw - >)?oiϿwБ~קܽR}!C@# -{;;Kyu{qcU_AavR;WCgVeY8py,gco[s23c=?<187x&s0:1=<>0r|sZB5Q, }/b|g*71߱o<YIfrzD07Mm ݑwsyw ghXH 2w<ˇ;¦\ҵ zC#"`쑻:0WlmV19Bc}m_(S)/8.QգEDX3gjt}^͹c,Q:oc G(G>9;[axrf-NM{_mm _CA+@]Hc<+4MΦ_c`#_&C[ طm)i+W]}mT;]O=fXCFTUവWkҲLr)ַ ж)+ߓ; 9 (nIug `Kg77H픸ęNO>7A-9o\qkc?څ̞a0y~OMoR8;ŵ-ta {}Rj~Sk7᷋\{RcՍ{~ CqEx gQZ1Ne>{k :jCh;ۜN 0K4ʎX[:>j~87$e2;Vۖs1` Ϝx~-)kcc^Gc*űR3ͳ_7pwm > q 7lq@}QΟc]-^m=6Πw˓eד2~DModu\;`tVtpDz7NV/M&oE 0L]!xLG|q7*_in7m߁W"LJ`!/eaj x7̠)@,p=%{Dsmy/8۞P06M`v ne(B ؉}ٹ~?^zNQ=QH~t_kQ`Mg?A!o?Z_Ӈ@F} y;_<2r녑ud!+І'7bOpBcn|~L 9ݶ:썥Xq}G4(-O۶Jpj_̢/ÆX2H߹{cZiffna~fwTwG?I{+ձoK/m_r|A ; GSk݈em:/h_ 6uSk_ tZZ q߿Rvt`:'?g gel>U64Bho\a4ڷ﫺&[*_oSq ;@PO/ZoQby[oԟm-j3&>1(?T>zj~VgI?W27o 8QcB 7Vσ)`l20Jl>߃E6'q:ι68Qv?pe9]Ws1oOٹ1hqm{[GS /W| @ǹ\*z-S:/uZ_*{ڿcgx}_^>l:ǔ*C:ܯJc9L_퀢7<ݴXA;}M-0?hs<rE1 k;NUL8?T=iWa_ V~'_osti۲f0KW 7@sӑ?@zm7⺝,Ma|#,@e#sKz|;7ިN<kĸ[`A]sۧ t9f/w|9hw2v9'GzZ[ꆱ'Ev"Y]Q6Ky,u_9.Y^ y (p|#}.ۜk^cF_.~n\ӑ+M)n(\Y|}Nl=DZd$~>tpΉ=c Ayf1x=6:Z. }sKtPwD? pvz6 ]-Vۿ]GF."u \I^$3ox1>:d귧q~ qM}6^vCL;;`PkC"yCx?1?Yo/b%odL퀞翟W521]_NTYqjObjx'Z# ;@Ge/Iޗy5t~8ֶ ?bS}m 8?59lmcoH?v@([?w>o8g4P]'7 bᖫ~sN[o:9 ws379d2-<7FF[?P@$Ne6bN%]@.N#~X}'y^/Y>-q_.svNb=K-zc;+6Ç{xC({Cq-E$clg̓}RȫИg?u]0_-M#xw}6Op~7OJePV.s]pqزaq}wtggdͳ41Qt#¶9 /_#[[lqfpy)k Y/W@׿tX7Q_FnyeJyOͷ"od{>\hFnEszכ"wЗ^M_ ؿFo5{8>/`?q:#pvsN=q%(TSsDǛ9CG^n?Qo4֣TZwk%Uf~ci]I6PT_X?}Oj1Sl`k}<$d~ibB_i;E]dMxBx_V &l>@"kO_*΅쀼~_4bmׯ>(Lh<h\.] \>=tԗ]?llT^ `?c# lYU=Dk95FߛQ̩^N4?ø }|zhxK7UN^??n\F ~csܜp̡1@@y#::0ΕC#x慑~yw\+b}FO`#\'숭< e?Mp @:xB~];V/ ._z'i*3rde5O|@?!~NqtWP='(z†v@wc gи3a/7Z~{k Z@~E}ވE< ~XNQw_~]ZpD6_,A\7 |]v-_gc?#spg?h -x=s yuas^~ _vT2D|&9ls69TX޽LF39 _5Tvdg?)fPg}N |Cerأv] {jy1khLn[ڸI?l : Oն`laiVy^GrZgVw? Yo2M=I4-=ǧ'c OyaJ|o._'0acr>cIa _+<O>'#qC[G/bV}4k9h3|k0߾=z?9F;^p{{:_O8Z9oM)߈]Lw?岰"l&Nn T2Ago짨4{m`56YF\oMg`8Α (uג|$ 3Jo ra&$3RfӧNQߗr/5{yP`3{+,xq;mp&w wz#[dvČok_.܄5jv͗b3X7x<]2q6(}?uOϝ?za? G.݀Ȫ8s BDb @|W?<#B>8wuߒeoOe_?`4M?~6ͧO{߯&_ٟm|E@@1N_o~S{?{ß"X?䜸s[wsf&SgY /qe!)?7#g@_}νuy3\oIO> r./ 99ں}o Owb|6Cl=K_z.Z㎖if ~s~sŏxM!F?/ "ow9L9u\Nm]  9yS̮50S͖vh{~f?]׀ ø>Ϡod3Pra*Xs#ͤ؀-7c8㳱`cdy&sVz0%g?_|UpuG?Voo&9Y\D6>} :}M+w_+pMc#PoW?KNnBbc ;bYӸwb+}_kx𿙃6ɵ&_̤z?ʿO$83e0ڴ}_j\lMj;+r> p>kxFV#mDOI^q[,~s:<P:#:@NW 9]10 u.0:w;β1S?-ݜCgeڼ=G'ǹ1p=0@n+4:8Fk u]w޾~{УxۻO mE5?%oXQ&׿Lx$_0 K+;n4t1Yf;Sӕss`Z*1R`>f}fHqL̪|oy1pN "03xlmX)Gd]r$A(?~*7,ku?>)ڙ9l/+{v} .P} j|䚱6'P˓_lE0k_טBιt^4KtvKuyr?kHug2T9Y8Sq[7?!N[k"m~6Fߩ_TsqBG5|z.yuuJk~*_}^6Y 6~)L? Z5by81 7M?@Xk/93tݙvN,.G,fvbe6ٛ9WuG61ډS3'\ `n^?--:tY>K{$- we;m~qb}/83tM0"hx\ ހ\\PwAGrܮp#t:.m :{a$j.=|r9MmйЙ/vɨvM`lX_gJ&Nh8I<<[~nA rihM 3sw+_gAfx?feHa}T3~?9o&! mſuRvf~ M]8J._eo3S>o>~]NbؗBvjaۆ5,@.078Yl;Z&6`F9ϝ9=7sN'Uy[a0PmNL?b͵:_[rC=v3빶Zyk8%);zm[ЬӰmh\UIa5Ɨ,4rßԟ>L.R;B?e(W ;ԁ=">?}cl1 Hhio_ 3lL.٥ζ>3P}JMqm#392YTfރ-\zDžF\"/y/w_[9 uyk !%Vu Ssϳu]je;$gp3'|q.{uX06/su<{.OoQSwcxvj&s>NG-k:mVoZߨ޷.mxK׃1?}YE|`?M7cW^ڶoBOD?rBM;hkA_ij?a`>M\_ r.|}M|ZO ` Gaihk4ASX62fu8krfx96L`@L'sz<u [sN`K8m/y̝s p 4uys s#{Yȝb<~]oleؘB^{cYO^y9x?Fa#|2AviO'.ݿ( u oBS؏=Ԧ76_@; gt bIȘ"T?Hr ;$E91 F?,X;_Cp,?v}`ghsdEa]\]U=s[/?_~w7T 2[?HM\w-ΗN}?Qnq|p|)7'9ɺryYoFy"qe }ിOڄi;cYbA_a_ Lq~!7?e_m@' x6_Sl+lGJkW\:v?]'~ m_k|r?Mk9ް)rsM&jԒ@ ?9˨o*Eϛuz2z69Gڌe:!s`bH}^D2|Z6ִgs1}ښVinA1}VQw0{grq1u< x[B* Y.}g[FU!1=|KHϹv/t08RafR9]Wavldu@u81_D?%]}9:0ג n|׊z\r9?yqT.r*n<,J$.]nr>5.s»L6?E߻@;Ӝ p~z zRVwՏΜ{G黧_S~UdsLvh6O`}:gcVra'?X=Xoy,cOWYԱIC& rBwB}W?G 0?+y 9N+]7\ 6ӹ?-/'x?iTc3oPs&i?/?ws筭lA b5}kgt:m'U@99=F!C06Ns0:h`9v_'ON1Wq=7j0ǀyK> ;U @ .Np> Oׯw|p}=!kr orl0֯mh`vޏ7F21'cɮ eYn 3遱cQ?s5%4yiG 1sO}'ޟ{`P >s6۞zh||՛tdr,lPH.%/nFy\w:Ig/g98P Σ_. ]pyk\ƻj3.q F8YV&M7\:R7$}g9f{-u]G?f?إ%|0O[ko.:M3/WsCӼ`N` \~/kbrTT>+7m8Iÿ?FlC̏gzmj@m^W?4B}KI>)9@{?5]({}+˫oUVq࿻?Hs q+?IHS\ 3?pىT>ncRpEXstszRY;]uﯮ7ԎM;[x~m`]Y-\mti2v3e>6[,c]!sIM&Ϡ߁ 2ޥ`c{&w`O@k.׸漎Q#d0.t=*B`?ʼ۫zI 9w9̕d y'+7VsQ.䒡a,Lae(fa.2)p k~sk frcGp7}d'G9˙GOn\ChpmJn-:?͗ȱ%,:iq2S?7c [fib|~-7Yn&l_Bv@33T,?h>]\ȃ*/ _ |qڣ:un]f*&}6o6s=NO58DK-Cr8t;x}7`| lYژ&K]5n;L?>޽6^<3m}E> f3Y5|C#{Эϡ2i2Cco$>*ki@A&kԼ\]pŽi82Ǽ;#lDqGLֳ~_?/u@\rv#Unrko˥X_b`oCHG2z=渎]x,\'=tQvd uZl@~>GXW=DյAod~>o ?gg oc'ʴ_C(/d__vysX[?gS]w'Wz?3;.~SS@98-omY?kS]Ws_ +{ /7l:e7*ss*7}F5~lf_+&nϳߴl _ǿ<j}oDkM;`Z@0z/E%5˦k,6@:h@}c"[9w8[vZ#%XHf?K@:֜xP`9.Y oٜGw|1 oo6m `y<|Ԝ^Xqz=-xJ},[@v^=HHd ]d~. yegςwGe&\ h9˰ q*72ٻSsm լkiOO=I*XF 8e+\^baKD@?J ;"vmY/\}븶Q59no\_ޠ/`$g`y DN*)%6Oe}pSnvzYu9ͱ[ios  .އqSY|2o}G9K|u??M{/H?}ޏk`p+rȮ'7vmy7{ }3%s?*hsؿEؿӉaF쿎t߿ߢJJsYpVi<-5/:|y qoZ>>6(ÏM3!v@40W~2&pW9oht36@h9}"1_;ZoskΗ-pj`/??obIެ pEmP/6Gs8̜/>s?ѕuff]?za2 ;;vu| W6rXTO-<@>{bt]кQ@ia> %\Ĭ^ eλ2 7Ns,]kk^ǩ9bcZ:U&X.`b.jR֡29e6CXBכ?`^XvsS<]8d`Hߟe0`c85#^a eH6:[`$G3Q>ۻvfct_7f6IvSYzmCݧbm94VZy ε fn߹vü;́Џf&v5?r>x~8_txg{#5{^|/[{YhsyzF?3203w>AsW_V7b5?+ ͊4c}|Q+?du߳4X/6uL7z<~PWfg0=>?`վ鱀)ysn?5a_1`.U9Ey?}z-_U%յߊt>7?p-_k( Pcv4,zf |~sxv {/R{Ȯ4 917^/m8⚽Nܾk7inswI3zo3c {/$̤2-]F ])8_qRX hvi}ί3B?/O6 څ8C;](x>Y{I8b .1ַԗ$Iw]L,'Ǻ]čm|wCTQ|668  ͤs3cobpSrqvakڗ>}_IMS 9q`5I8e[]g7P;3 ?hrtNOx=965.u5llɭ5 }yŞ'|n߅~}?ί;߿Slsgyݼs5o*ȡi6+2]S=46 ?\?~_ax{څҼ 7 o?|I۠}pvo9wU^p߄BB!6`?4nM yy0M;'h: *5S?|mm*Hu9ER,GL?1gBmva~#j._9KskMO[?ǕIcms e{H:3=5V7H?OKKյF3iȱ>nr2D?9/d ?4|E]|Le\>hJe1#>xȱ$Vr|O5ծ䥕7$g8sݱ`Je˷Ġ @EDfdٙkVe{l|]UfZuum?md\M}>= {{czlCdwi{{7{\\8xN"T8;cX44 ˙M~W7kb1q=λm99LYs:̚Ź?|*?dz\~1fkp>yyvњc^_9F>w|ژc{=tt>1GcF ,ya0`-cXϙ/s4ź3w?8swcg<4P{?ޜֽqެ?b<<=ۥzޙ+\WKtL/p\Xǂ޽>lyo:a^1O/pԸWGcpw8z񖫏<:qbo{p֡q=^m5?`Nz=yF=>|8v6yV\03W1֝e ld[bk}˿d^ sq?t^49|;Ϲ9w|5n6߄j5?IcQ4)}l1OkG0_ -N4M>鵎\_u{=8S`p S|/}mWƘRįޔ8@+x@,QL'\~ٽ|ldkN'-qWe4Kcqߑ'̃~rs.:3An8K9j9rQ|s A5^_y~܊ M8\YŊ `ͭ ?O^!asYs};0r¹nUʼT渭5naĶ2 o, H05Nڼ2Zt 'qܼoWg:뱫%Ankx65z^µ"ޓ7{K b0 Hc>< : #وh?!4xy~ygS|Kx]87Jq0@C C|ǦkP-\/G/^5[Ab/RP3.`%$1̹ۭ~ /p0 8??j IL!0F10cg'H9}}]y |X5z_+ V==aF  -;~~xD0305}lq~Cg0G}w<=p~ ^qs` gk/+<~xj0 ސpWk w-[U2W.#@*'0k[8Z L ڂwq\ 0Icw8O`G3'`Kb?`؛=  nSg078&|ːcZ`?a,7ԆT^b"c%1~nqC@,=Jĥq/W78nlxO ڟz~?&M]>eH"qm{acU&v8Ohħ*ӝۄK< E~6/;Spq}y@c~1Iu$8x@ 0>do /\ ?u9\ypAul؂L}6W\δr805}m~8!;0/8R?֖c9y0ƼD'3p_#<906 -E 9U}jxyRs,@b.fl1f1'=+4<9 D[zs7po2Q'|={T\ʊ^{?t)p>wx7g5kp <# `j\{`{wю{Y_%cOYfy'Xt \c`23/9 7LbӘu G |1<y_lEKþx w7+x7_U+v-Y xNod\c`^k&Ӫɔ>@2csWX|%(BxqH 닏w8A}mMma`;sKXJߴ 3`>6 kÐbny~+c1DgrNƑy{hp>4=.˗< 0~d/}kG%kƽ0?^v>GM xzq+1N1Q'qQ3YqNMMB4?j9V4/ׄA/KisO+iUj/m"Ob}?am/??8>+Ls 3_޿>>s<!cc43|c.ޔgY `(A#Pg<{q2fp^[|Ps?X\^z 1\u@W^` 7u Nx1Rs$cxo}1|`9 ߟdؿ} c2tܵqhw |xȰ۠+__pR bԋެvB[/bj|9?c 1n`N>T֠[ա7!R/@ mH9~C,םvZc?AcsN9[5GWlUaYw?B.: {gn8oQj>ɣpv\`ݙV'bg s&1ͪ6umVX^:Sx4ɻy>6;pcW=8S~cm!=6`k`ml:Qg<O';y2\AÛ<=uw|c\\G=McOk0yY}wGϴXt9Qc}82Ğgc]Nbk3lN.O=gUeq_doȽU ,|L8mOϽ;bÈ>G0p[x"ƏVv'yr<&֜Ü0#@xze?hTPccf8G;{=M< $-=U~9@1uң$5Y\/'=nz?JӸïx/:1*fzۙa|]<UE{F8t ``]> v}O0<ԇ<~W;؁ԥNG t-'p?3gr1_7и0qVsS ^Y,p`[4 JgV8Ϗ?Z8>wt< Koқ'p=k%u9B~_x쯁 x? Ё. p+s:KNǓ';oH0+%zJj3P6&^aS>/bߢ aQƏM;badz5ri,b9^s?l\beߖ85=2/svE?\ojL `9y@9g{.0X0|,#ߖ9KLƱ:(yPٔ(s @7}?4YRK`@[b->Es1Ayp7$ީ x!zn>G#8=|XG?M ߛ-8k-=yL`~߼tx.=A;зNuh[p 7@@2WzzMq&4\~iוB =}51ذқ `1:uc[ާ\t2f>n{ZwD:'h/9I{1{]ϿBڿV̡oڟt㲼O.bk͌_S? CO5)ڿ]|>*kҪ Gj_kq?E2upcUv7O߆ؿsy(M3>rcfK\ ,1ϕcSe1jUR `o?ƙ/h\G_)\d1ϯQFF)3Dw/}v/zN -Ncbc@Kf¹*Fy?|C:&w @b['18_>{1#+'رX93p ZxO]<#0k,9@gm+5y% Z׀_{h|5p8@ ׯN~Of6g??J֟~ů^SA%{[ld0Z\Sp sk=Nxc?S0r{gelO^~`0O8^:z2p n-Dz ?<\{|@blTx\}M\'ڟQOߑK 1OaݥhAqK*ř>P\yխᾗ:E|i9ߟ|A~X/2ʱ'{3WRB7ҞsxNq}/,_cO <(zB b#8Y;?"0G8\\`_`?#o~=sZ~| y -`L_s1bky^?UG WggXa~?AtzFr!;[ fߧj¨>XWxn*pJ9^ ^UvOOU)4T/O?B#ha; 0ײbn>+ƼWܐ_qM/N+iᚐgwyu?mb߂k5hgTROeF%,|޿a(yQ>A~ugsyckS{GLgz-?qwm7sK` ? @\9@}cG\,O%G`r:&9s`.? qI?a[c5 x@-q(<00z zx| mv%9V9`9h\!p;b;rsK׾6T 8\Ns?c@$F$a^C/kB\5@nO^~٧yeOjי=6x l&pr_q5 p-y~?}ރ`졀7NVg=}z7H<<f }5sƏ`vZS7 _Jz:g&xsHyA˔5v7s?mEN;<rq??/B?ߘ9KʼijƼho^Kq?޿ØI(| 07b %g#}:˔9SP=F? Pfm! (7=8d|+ 9uxP0Vpg~~/'|fݤZ5&KZ@kV{;mީtŘ\=ߏk\=9 ;[ל~7gQg_*%IvmC_Pnlo׍n; ~^O;`,7N-Koeyx]c܀cxw_\k=[\k:j5vk~5z'W䴍[ja/7Hh5^~r_ҏ qp}QͿhL>盓fY?9yw!_:$ns8PYϋ6pTqF6_yYS9 S_L=E, w0?y/s=c[o71D`1t8o2N?_Wc8 X =h}/:lMMk~w qzP 𢼀uy/=>BݒsoHVtAp'`k @&+lq08pXudxgkz5Tztѥm];zp. sJ"'Vq 4=~tG# 42 ;\Aqk/?ZkZb90ph}g`G3@n\k/mxm~cE?&-i't/ދο7Fxz+}x{mϸo/OM/O`|}W\sA!{ 0:Wheg/k!矴)󼿉kcR?Mɱ&@Hue쿨 ([ [I908mcg\b& &9L~j5vKD|m9R80W8r<l<X| />iύ=|O}|z@9ӛ58>R[󪥕F>`0^ׯ8p4*8s> 3O2858y=]n0ԥt.( uiG3OrZW,umU|Rcm?S>km15G㺏^_^K^^+#.' Xp<[+a_X]q c~ `?kƏ w~CŋÌ^_h̙ \]Agθv^{Ժ bre۲w7{4'-qџ?ЉbV_f?_z>p%y1x^+Szrk.xC_Q߿Pb&:bnByGkNy ^XOp?p/0NogVz 0eq-@ 837uOZ+{E6-]8rΏj5tO5B s.~9V5 ` ^{_< kKA4scs;'/Qo?9@ՙ8r/cf o3[%{ zg$Oui>Ԫ׏ާcް\xg=d|`_ @Y>~7x?ާ :ay7q>jEjPF{ƽu[8)揽c7_s<3r?m\ /X3>cn<E-`QN,9suA z@ @ZGKc;_ Ӛ@&@S`uޗϥ^@ H1{0~"_fW5z0~~R˿{ @_A-j'7nr>{_qA> 5[8w立S֏.GΧ,XpJ|NeG_WϬhʃnڱ6=4D 5QMk{aę :pFb>\Oqw֚J-r3ğjxm[u NOk1HNf>?|3ĵy췬[zݏh_{Y#hIƨ0&C/۵w%ZGy|M3 >_jߌg8^yYGyVT?Q?S޿e?hu1flGk9Yso.i!׿WэvLNp"Or#/5{(Ík3s 9h$orzoG1|-#(|Y}\⁖=^@1ږuU_9I&P 㿮 _@>*,mDgyb}5П4/~/םx#~tlվ~\[^59V9@>ƞ Ǐr &tכ;Q:#5C7YZWz40(`I~*v^!~<=2:Y{a_Q;e6^Oc!u؏Qpr'}{ĵ?ssE}F?yZ%\Ķ?qhKc%|Q؟5ڿNDǦo}|=____33>03עeDQ*;1'nxx=muŸrϵgĴQ;^y?X ع/ߖߛf9bM ='$4]s_F:^9801~@>WIuǭ^<=`~a_6pS?"@_abՀVqlRCP$K-|yQpY8bx6}.ZXxwf~c[\OwNkJ{ca;aăڟ k0ɵ^8>=)ڟ6k16 ^:IzyBuKL~k]!-C =ٹNδ=m5?O$c}s?AW}:9_ٜ/C$ڿE[1?3sxF~3o/<侀K^qo3.Nk</X+)ǽ 1S`xr!a~?ݸ L|)|c߅S<cy$+b-s?c _`F1[u< _C/qI4|\@ң9@ a) }~|xs<_ӘOZ?Iyr:M{Up|b/hg:?9@ اZb5]dY=䚣rJ~e<:}_؃h}{/|_^71yqic;y?xu5tu{c% ^=nR8@زcMA9P8-/r;xqNnveoWfP4x%߿B?kcZaҟށ&0}~\; ߪC9r a~w|z/zS__&˄9'Ϙ2u羿ou÷bͿNuE~CO.@$%$/oes4g~u:9=)W7 i_9Ͽ7bN8@B@?jC@S(k^@y `\ ?L/ =1 kb^: @6^BpPpbd$/sV{ XRKbO\`<`\XrV9>i֒mr9@ۮXgxҞ;7IJ.}GA4R{E.kl}ŭƿnt #=DkGxy/Vki$s Aj66Yfyo;`7e޻9 y-5~-=~}~|=^pS}D>ɵσدitd*{=hX+sy?==9Dǵ~򚿀y9 qk9߿»c=8T\Cn%}8 DI/9G^jKnsC #@mZ2sN*[銧nkױ=jZz ]w_oYm_P;~v{Ab'lacI!LO /9k =0Av&q*񛴿-5dO "yзvQep?5?!@uk~r^`?k[a@:{OjcYGVn~w>þMj W kJF/g5"u~{ ?5܏p64c}S,s oTo8Lll@f#1x=S7ۉ]7z|6&x.qo9(Ps]?`R8~-r9ױ-~~ Sy|MkB Z&boX>kɡE/~: 9sVy@FY^V{):`c6{#9CnVFs׃p^o݃f>5}9.j<)Kg}*瞯)X@|@<+qٌn8XfRn&Yx^vZ HT}`JQ\Ҙ_% uzO w퍓@گWK^:s>O0E:X 8Xk_~?i zģ}7D~)k?ױ߫O8Q}I-=.wbϏJ߆>usybwT9wIbI7l|k/kzwȃ51'R)`? O–?xN.pxp0JnQ~1Mz&Tc?#Sc`mL@;>6# 'ݤ'@f-,O.g^@#@LAoJz-`5kUEߩNc:Z/q@':O<ݮl#s䍋>p恉gChCm2{wZ/5h\fMF>}V@uW˾k leШn맖zׂn\=eCl75? ^}qn/k~rz*a?{%}RiCe@|;0`y :!i-%_tYO /kRg?ź[O[qg}M0%['?LfХ\Q,`tb,5fusM~Q/70s0feH1J/f"ؙoS űp7p@S^BǕK 1^_,yt!ytQR`P?`8u~R`d=t!NqA1?h ac='P`k Ag+kˋg>pz잵&|yu$/{&x#`\`5ZN}85M@hl{}uUZ{f ϦIUX=[.ٵ\. F7`|gkt- ĜUR__u?fd͠Qz\~z>D㾩!/Oz qMrtg/=C~(y3t<kw1ÔQO>>*= _s_9J@Q⿉TM9yc<\ȱ?CFC0 1e"<xo?De|{Mgi/=}e`,8c̄-ntJ.v1"PI<&=n2z3?ΊAky/%b<@ؒN =_Xl!sf[C0gK#{;YׁWU\ՉP|~=⤷/k?X譳ƄwК~[/5_=^?֯wnepXJk7|}Wgߪ`}|j_C~Ho&}fy*}\G?K5ḅ.wb?Gf5,}"Kܟ^Oz~j(k_:ιzݯk%1>k뛴.XICqEOߩk2/zKoh_ {Whr7?bH^I?Jx37cǸ?&Ɵ[~;#.o} 19~o㿿3$z^^!y`0@5L*hz1N@[1q~@g$BoJRs@ԾZ:K y 5 3 aՈ8ч|X߯3 b#brC )F|[|ճeuM5 ͓\~ 7WOx7߷Wvnz9ݬ4 柭h}YheݧI~h}GHvgߏj_`WZ< lϪO?;!]#|rIH*7~sk3?.VS sX?ʼQ}T'_Y<`dy7{c_Z`nۉ$kkNs{tzI +nC\oѲHO G`VRzS?g'=w|9VZ]jV֚kM뭦G+5QiJ?-;9C j{XU]Ѷk{پ޶{r{@]PIsi/~ޮ~{]myvw>|/HPI飝ǭݗK'N?x[sR[ܗ֮.9={y+yv-~ig.knL:i+X3`[?|d*ԭܰSORjWn7H߾kJG{׏.ݵgCm<{kK{GҞݣ=G筽rkGK=Nn.ND{piukc k B@mJ{k{v׶}gamо&-RJk{ly}|}a?oX: mx?W83\VU}'[np-?Ո\u3^Y 8[M}Y}|`v {Ysg+9@< o2T%(;leQЮ%1 r;d[i~ComX`iז#`wΡ6Nã'φa[;#dAQo`y2V Do d-=dmd79VR ps9Q:7gҲ6>[F[A 76m-)}q]w{F=KJӲ}Fޞuힷ{6aM/ W@mbBdǂdOEIl9 ! {p^9|eY"$X鰟k뇄}wůSԙX}cuK`W _sg~S'>xou ngx㞻c3)Gs|8o}_o`rYoL9Ɉimà@}u vB\ȮyןRϵ]Pg;3<=g q!:q~r$?x-ϫ{lϕW)dՉgTfSrÐK^Ѷ7P pQHç/=?ا5t9쏭p0PxX}-l ]Di(TXBjn8J͸r0 jWڞ[n9ͨZivڬ<1=,;lcpwok? GٰGOV?n wG\gW3"7؟wvmE5#gL;?'n~?FOmce|'??֏vyvDgjm1+/DlXpĴ&A4Zx r׷^:g96~0w>J=p X=>~ aDZ~f|%v(Oic+b2`E'51tm&Mz&دXN1Ǻ@,~SSFl;j򗈿<(lvAz Qr v4{^~CH9}4Uf> |OdѶq쓰,QCn~oׇ!7k]wviAk/ފՈIp?v4bLosK@B_`ܨX8ҳ k9VYӺ Gl>?aP}; }$؏?b~?b[UDB'H/boM_t~Ļ*pc?sẂJhVwoigڿgO_0 < ϯ8@W<{o>?t_c9#f_a{_{m ƍl;9G4,2޳RXK:p?,>Ix8/xO/4^@ su 9@c78M;;mMG_ O `;(:@S vo<sDbqm5s}pL kzj4⽤* 8Iqp:r%mS ]\JB9暩]oYp?Sg}|Ɩ,*}~> FVD~?i]7il#OWpk+ƊC?XYB̟|Ԇ׋> TwטnI705v~nY?Wx⊁09lx HO^ޟ?y-uw0 9g 0|fz~? =cc: w9ADC5! 3mt0v\!^ܧp^vz 8 ͘pa: Hqu0>E 5WxY@r+tIZφqO׆=߰T_='Ӕ9b?qضΈCt[Y!(ZabB-6$O~`>c~+a{i8cz{ߺfZΈuwCƊR5LgoW9-_gEǶaϚ[߰ @;qV1ߏU?}g cW5f5"ںP8`ٕKk1ޯۛk?#.Fkq\1./Gaz>j(ϰ?a?־}ds|ψ>p~ÐDuw '@z?HR«WG<c<0T=cOO9^{Ftzc#Xe,e c -!GcE @2aTbO @h۩|'yFu|!"?\plW߳ ~^dW3/%\}e<͞(ظ,O 2oKA@ޘ0Xкb h#u!_/^.Zx¾>h /C+~~?X _dR 9sD3r jOw~+nT0t Ώn_IC9 {aG_qAr~|=i'c*& ]v#5qڿ%uv緰_qg?S|^#Pf%e|?>NQt繝j]>q,Oc1 cxX5.` 44f=, `8࿎+S-'پDHklI 8| p~Vۍp<@VWlɟApi29M'9se| [M:(?Ly@q<}oX@|/Zߵn\`Fk 2`>9/`؏pOC(>]Kk{71O~{] Qa~zgǸ<96Fy UE $ݿ+kΊyi.Xx.?8H5;˼yua.jO9PJϰE] %y>H,ϝ? {x|Gx@]ˤ=6W`6h<4 ! ]\ȳ;; LAt<ts,[{@cM@ 1pM!%/DH(V|V7 % Jk[s9} tWLYY vܬ $Xl'D@}^| ` K 5e=dhP q]q=hYqƄ:?d^?y[u~ۖ|LR+Vi6~{7w4ޯFЪ>>{w~X`c9b:D!y8iߘ`7Gߟ{x_h'|`<`Wz/Ct}\{ fq:<@Sp @OkGЍt)\tJl+fi|^s@{GS2H׸?H| E1r cHX@p@pR}8}@x(M'~N*9:@/ +pNJ \ 43}[sQ Zu w |rMC<=ba8ܕ31 4&'pͿ=c!?` _:UW?/sQ>\:qt%YrPpC{2pLpJ!bc,`(,c9"`w(p`#gx :xM&<0?bb;|_jkx#J4`t= 8O pUuK,C @PwU x$x<<!.r(7߭afk@wUy_r9/hLdq\}8.|Kvpɢ{̩ %9G_^v \ES6+9m#2jY}~9|Wߟr98l~X[eo~&~?b!C`<N{엱* W o<b+kE:oޯ\Lvz'.6|.Lrv=ypqQuJ?PXI̴5?I,)5JF>?8P\4p@:E$m_q9b+"ou5W W9Utu* x* u>?x^ \pەDK!#5?d_06 X3Zk~s.܁dXg(*xUO5yM68b>R0o߰_yU_C{i~ơe%9_]~5쯂{;_%oRtW B͟M3ahx ao7O,Cߣ0{>L+?h{G<򀮜pTpt1 , 8=sұvےDM͒'p1˜~.l ;> 8PR<)XuL4 wPhDHDѷ10pqffo` [>]tBVdp9z[$G,K|ܙ? mBb>ͦ hp~-Ew/_$V)_@0?L {v__؞\W3~mL6.OQr5кA`bz~{Ta&_gKW(4HkoR_x?B!={ÕwFQ6tsuΟs8NpG?;GnlYULwχ|+~_=f1'BG'@Gy_'ЛI@&6o3MQHQs`@2"t:& `WP!O8WdPwy]WTusU uV"+;9- a.qMΟKea?^jm<;,pՂY1?qABGx:o;VgsxoJxiIY<*'t' Z=Ok۱z[I.a(>L\3?ɈGKlw8﫸;9Ka{:\?+Ͱ?3+W8Ke@?P_sdK]c_p!6=Lϲo 9"</1Fk X0Qjt`?W 8,b1%{sLcUp]9b,}2$_qқ}HZo::{c:,6'd+4:9,@9@ kM/:C ?=c`~sϕަmSz>%?x Alzޙ 3Y ul ~Hvhp_kV 5@Wb.`79( ;^s)P @ H1<r򀀙. O9 @X@6,aX\6Xy@(O09̾6P}jrkV 5dN ;;R&1_\3{{m_ ?;ea3sL7g~uw_7֟ }ߨ|a|] K׈>aת QZs}kp(bXG0ޫ#GOʹG n͈?_'OLyAN3yzұvԁgh0 c=5kߏB9h-pb۱y Pg <5'mkU2饼,%x^ZHv6Ǿ|l6H%6p{(Wx@RtNq6jٞjF0 - V[-)˚{;5#wk q;~쏡#Ϗk/W5;U[ /||q{ZŽ>7痪=; }H_?#5l>O5mL5IZ0G <sƹ|?̵ NG܏ctp*f0r#M8^,а~p _f_@\2 |4Q 9$h. (P9!T[5Xi}xm:rPN=YNE|ww>? < rsrJR.>'-]7~_9~u _db?ϒϟh-`O^rO1V8t}X_sJ 'xq$x*@ly/\@u|נsT=}~*kCQ7?є7k?h5wyx׺PFzx!ُ8֣=_aψ>^?_p)X@o0^Ƕ|^ju>wysL1y]֜W{ dM+XϗZ>HN _?~S ~mB@#@;̱QH2/;zUR㸬,7<'+sTx@=FKcǹxt,]Vݴx[k,7pṆ/_8!ޏ ^ xNoc̿wej-+9Z?}+8s#e3=ke=<`Ox'F׺s}{9o32` Hᜇk54?Mȱc @Q:ju7,0 tl۔U<4~_x q, G8dg[HcĖ`Z5zD pMx@h)ctQm@d2Oc?+[sZ5QpMl j˗`?Dܗ\04,#008k> 'r\[X)?GN-~qH}~\wZdܿ !o2㽭a_Ϫ\%b>!`4bf#LHvw?o9}9Y_$G?:]s2T~~p%KׇRG߿[2CN^Lk)!pY,pq0 دykXC518/ql:~8@ {\@4Z@RR1\X7pյX#H6lR44U~E-z6)<<@K_08+P鸿k`1\+65XI'?qjW y]ۣ?f^5 ?󼦐4$bCbYHD}^> /\ˍ9@R @qR;_ך֞?G%5K/'~?e 1Tgb!y?>_SϞvN~ďHkD#F+?c ܟb4 )s_#b@)Zt]N?g<Kv9'<30QEKryw8X\n\sj?D %ڶ%Hz.ܡ&+X) ڼ̎\3 4P+Pc_T @%d,@r`<&X SAz)qbb8g1%oq]VrTuת-dm^WMU|pI~|uaO?~|2_:imEKNh[\e\0_s .kǘԦw\h/8G /`;q/wO:o4 Vn?ֳ9&s0tD+/X5ֹ1gPdLx~6R=gw:b?zx aX&oBe9/@ʕhxU]D 8/DS.֥ 6·@x:c\H 8@6Rgp4w`Ҏ;""NPDχz~^GXy}yߡR8X <@u{e'ri_}<0/?avtk%q1o?SM~řz;5Vy6(s:_cӜG`,tE&3jߟ\δ3_+q0oH][95jk26<`YX.byؾڲYN`?'0k<0 (yFh 4Fxo:@40 zA`}&EX$>TDyr _x+? kjq>]0@w9PQФ 4o.e ַJ/b;_B-xcxB-­k؟WUT;1ib~տ*1s$}kp Z41_;ϟ_ci_nr;s?w70"b}kj ys_nO?뷆q] u;i}ۦ2| e;3>?2?^[>шagZY4/G<4FO=c4_O&z!8@t^ӬrbI @Nn_1Ȣ#q_c<C]J^E8DcFkw{_}~#%^2/kVOe]Bm2k'Lؖ41bDhN7~q++깤λ݃16v<`o'Mc{p0/5?]r?+!oo"Jkϯ}l!du.d}C@a<o)9]"x.d`D+t0Lt 2G,~񟸀q5< 83'ny0c桽E.dٵ6&0K͸/5b?-oeM˚Kv<1~]0xD]_4~]/F`?V_>֪ T?qzw||oEFGc/J[x Kts(!_`~_?X?0>w~π07<ۯt_wi㹛?ޖNsDÜucQT<4: x!s59@\|<8衯t4h<㡃kyXG- VO9x^8X@Ge.z0]qȺͼ(<` 9?}R]8ۙF">r}'9d4.6ź,bWo_ߏ F6o~Iu1k_Jld[WsDD PD#k?M)Uݿx߈91 g?!` =;ؙ"9ωM^amIo (-؟~±,{y.S;;??7nclLb,>drL*]?tP#'K_ [pz⿍Tsu~ w~%֯~c>k  !ޞF?n?t12/#w͉9KtOS'cw{bN9˃gp[hƹkڑZSJށsw3 `;y@_L Y%h:uS(ɵW:NqԀ<>- CE'oqn<:E.SxͲ?E:~ 8>q˴"'1#c]qM ~Eb0=,^i5Ο/ׂkr?HvrI~OO׺,o?_Jb3{3cξ~rx?8?|? ZJGK?lF z '>+p];\x b8A9mI" (͌$L+(_!k\/ǰmgW6;[.߃Y_~]I}Gs'QW<<~nj%zEzچcv|k~߱[{"k/Zw8aǨcfjݾq=}=Ͼ|yP{=>'=(u>w{_z_9s;vnTߣlܮ8|{?Cȯsamy|hg|ݻ^c;>g{~Y? }1^cD듐8Z賷-ރzt,?odnߣ]G+smz68?Ӷ_y?=/'+1ǹc뛶?D8w9@R\9m>c;ސc|G+g{8{7˦ seo?_~OEq1?:0%/ྵ`;Y\Nv;v~תߋ5~=v?x̸pqt@oNhg{_^g|<'W+S<'vCPX>^4P-ˆuh`x8,Z{'GFUܳSާm:h9&*=#s7)WH''#N'r>d9;__0v3wӯW 'u}abc &=jߙ/|wߵ3YLVY6K ab vS*5>;׶n/>iؿhyTOX0Lw 1Tg#^!5IZt`p>}ݦHZa|#<1r6>}1ٯ͎acdqzr?' Ub-t-%XrsΟ]~N/7__;__|1 K{% &MJ^]Oܷߟ{ֈ [y\&1ZmQyYt ,Ip#%T75ymncZPa)4٨`tTs.K`j%  O@pْttiYO?ZZ+zFtbX<H:X- Z,:` "g;ިv{^ <@wZcK*og#6|Z60p-k-rO;#wxƯJ/y󿲿'U_!M˸'Oށ< ո]tϼLpeoٿ5߿R^F:7R\ٟ/`Huа߻ OC쏶pZX\ Q޺@///0y\t}?7ءf~?|KxT6X-|&;t Z0l}`~30=p1p?35<'_cv/}1 y |Q矧?}8O??LuAJZ=Ge5o?4^?/~xU/;{珗)|F '⵮;לg=Z?(?j <z\ikЏ-y\G;ϴ٘:_O5ؠ3Ƕ~m t@}YσLY␦8|:w?Z|! `޶cC Njfz14(b]g= -F^o`/`M^sQ58YzL _`kJ3gO@OGmE s]9okQjN~_<Ղl~~W6gb`_CB hܟZ_|nרc-_"˚?쪠Ĺ~XȜ\x5gf_[k*gwrY5 x}o_ kkƾ˯o=紾mV:>xr=_'Pi׹ð^ZxtÕ \6DJb>뀁>O ԫ}P=1X/z^ pq4@ziM|ph#s7 Z70></F61- [KNSwe57c&ߡwv Zd\X^#gϚ?/2׺ק1(Q5s'js{b~׵Pz{B:y9_Ϲt9<{ =_~Cu_eiR{󏾾 ۵7_*o&Sr|<~o'׭Z5pB~=EqV~fƥ}zf-Nu(g%<6G@ 1`r w$jg@`kx+ 㿍oZ:62w.B:afzh 5_&=Y'@1K3t=@|LCʧ1>5ř+̿?5~ߔ;gyɥ>jiߞٿ;3\?﷜kο994<װl.؏kqw^W4xo|k%Z:w kbsn|@ZO逓O<[j]s@=uwaz+ #P=8utotkY:|qK8_h@Y<>i\ 5@:)43k/ HY K^ Mi#~5_ߡx;NpG^cWf6uEo`=4~/̟x}vKQ3xp'&Ƽq=ׯ7:z?l5찱1Tf*PF\b?yߞȾR9IӺYs(RZ'[ҋm)׏l F+GsRm_gT{4bYPC 5= tkZ-@@ZP{ؚ@hYKp <zhL5~l`?_<_j~-?sZ?WKy;j!2 noy_ƢBp+m5b}.vp_b:@ }a)V[ Cоyjcu.sz' 0rn@~k{\@b1Z%S C -:59B?X ``kEX5Tw @ bE($LLBk&`g?O8/MohEfhS^Xߦ>pvlyutoqlv"އP Dt1e7o>j36@5u~5j3%{ y͍E\G?J Lx>60 *$Nu ϱsg7o~>pccbnyw/z@:xotH'O1{jc؟tN~xM`|5t-  ƬB(h%.-`=BZyCD 9n>0o{z46@^hR; s_{IQJ}f~i4jU4Kiy?;`ue-kӳ['W?t,g ib={|-]e?ISw⿨Ϗ~!v.b=r3L51#u$ ~O;X-n.W̵ 6z#(bO`K:@ }e/ &Qj_%4@zmGY[YbI ]'~'0}~u~N,vcZkuSڸ>_ҽv;G_"M<(51/=ui?`.`[|[s0F {M˸bMϿ\kp-حg0.'m7h1?ΉZ9Cv?k<30+Oa5`GqOmZ@]6~{1i=`CP3 ^a/ x%`Zڟ;@f׈@ۣ }- 6i  eGcC^}^rߟ?BCD=0>:va+yϘO1~߱3Rbz{)p_}rٿ_,yFK{~ Yh_6N[:v~1}ּƵ UU74:d 1(:i`E=he=k@u5/|;{M""t%߿?&)|g Qߏ#^'1F_R)'_79 _ue?Y;c͟N/?qR<}g>5Yg{}a]6?y&> S_⛘uߵeiwe{d/uEK,Ou5zk?\׈1꧖Ƣa7N$ Mp~= :j{/05^k\<:&!@#5Y` 'HugzN(_Ex or?;jȚ6ҜeO|=%q=ka1&7cc9?|UY?+_kF|NYk'MΚEZ?Կ^!iƜfub׼=-K%]^}{5?-y<ze$J:[x/:`y٭cV0oyI'1sIؒCT {GF 0Ɣ48~Pix` ~0'^`!¹U0A4iM xAs}0ە_/Zlscܛ<.܏~ј{e~0}nG)_3~~8G:H\75NkVxzwy?Zy >GSόOYa~=d?<]?kb~Z[|5Y5O4)|(6=k_~=>(c)w_E;w?BĤow6ѦZu?o-NjyISχcnμ+_DZ械4Ok E "}/oW`=yYgгn1Kݿ pXkܐO',$Gc{-~muH co5|qMV_tEo'ܔ&{5C"fP7] '.&7QMSK^'PyOp8I?q1,gWpQקc$ O›sV3{]~RߘO'ynj\56"~ J1 yI~m@sp' QrxWEw )s|?DIchܖYVGX tܷ_/غ.x{=wOT-0$fYt>i"-k{;iX}>R Y< G8 %bХw9h|Zs>O')Wa,&[^;ܟ^pVy15jFi f\xο]<缿\2OVﭺ[]?~>x/y@ݿxu?~hh/YGXq4X.sڸPk߲4 LU}z~8 ͝6ޮ795gUp]n 44||1 ӭ4Iu Tk\ `@YP_(\Th6h9^vƽ~kgޠ q1_kt>|>ߺ<7P4A QHoC {/Om|'Br?ZQOho k ҜTp_Ho_ܗK\۷߿H~2\[rHza 9{O@R eq_OtӺƿϜ/[s0}Ю-֧D_/H~ */i>-^My?xk/#Okj_s?YK=1(ۺURwGpÛg뎉ؘϵ=rw&w6yϟ\?`!v} YS$}b#4BCH 22|?&Xo1OG>58ڬ\/$˟rN 8fc~wZ`_Ze ۵CZ`o3Yۯ5! 9L: /`?] {4Hݏ$0vw^m^ Issf?O2}GoqMjKnk"b\|_&XlA_<oz 3Ф#͵a`>?G/O. TSO79)3K_O~繿sgnQ|tjO$MN+u[9ߓ_t|>&[(մRq:wKA_њ]@E3{l^Y$ѹXOz>hĺ@@Y P/ȲĽ4t@-<VS1Z@`/xs}_j"&)~%@odo dO3l pC l2)x[ 6Wޏ4o|{|5-G)R|_anM3q޿h|)_sc?RN}ZyTţu uW_ow-̓V H<_,xu />@xr UHy|#0g|#\TZrq?t 5\`Z@H\s?r|I&0M2]'*S?O <s{q\'ۇ?xߵIlbC= q~Vus#o H,i!hXܷy~%j~~k˱_)֩+?_}#Oy6E{ 5/1q5@G 3۵]70PsE x5h,hvz۾b?~:~0׵@E:k6R8EظM^T M<ǸHS-`5XHkmc=k6ɵ7H%/#Zab>O~@g2sV  6>u|ou/OE z|Gyo|,rrb%>j+Fo`:?g+4@}}es/"w|WF\)5)ҸKK?hނ|b?zkYho`~;6L;_0ѕ6Yr\z`h[>%iH@(:A/7_uWx?V _k4q:4иskڀ=;е_`N$zLRr'iVؿ{<>x$Y \5 45IOp>yy ;weN5xAz*s_s:wy?_=ZR_|7M0 k@tmau置^k7h[/)܈MwOe-@~=@29E+.h5||ڏv ZszCM| x&ާI}?: @Z]W`O`HW95wa\' xp͹=Px/ mY xtq<|K쥘t9K//u|ߍ^#gq@:)>mT=~F_Wji}s#@`}70B?+kZ`|⿤Z}f~?rf  .$ot?n_as낤 8q`_ O@edž~p+>i6Yte @YL;1@tl轀_zmڎ}X-\zfkb(5>pwL/b&g*=~Ǚ`=_ ,)cߴ¾|?7yyw`Kk9-~ pCRS7 yw^^ϿoUpyS_/Q XY/ey]]uV'^׵{H@cs~ P] -TXhM/t `|Vx6kp,#{a3+umP` 0;I7J}If?|}pe~}ly|@?w{ޗ~W6o)Q'U0m_KJg-J9?iA/Xos}}Yr~#ks/:~Ɂ;ž)jir p]?{?zg5haUxi#Ax=}Ypb1@GU  RUZX֖kyzr]S}<?]uq}?eym5C{ivK4m?ßm?mS>w{P?>h`?<3_m}|:ojDy_^R+ K-uq?V/]C.O]BEKh{,r[.[CjZuki_n4˯CP~<^^POS5p[Mw}߿S>o+f<^, ^{s:5ۮ嶕_Q _.ܖǞg?kKc|-.KV5^ m+gMw>/&evm맼n{?wxRw*Fd 6wPr5)!(|@_6k[BMfzZw>u=/=}a Vh5{sh6ӣ}:սnP m͆{_X |P?7'׾ _M:֔įB4?#o-V`-2_w, >궜l}p3{7<5V3= :ؙϼ׊hG y/g_Y ]}_Vw߅7@sh&`+W774@8wҐ>l;Ch_@yގU\@>~1:腁az/jU |Ͷ9@h%l|@"*Oi^4Er7H %>,I=Prz)a!'pu56g~mHuqڙ]x"Nf⯫=<_ļ{'ζ|0>@ c oLR}E Pc >p?2u@>y0{5cz7xFH ^k<@g~|yJ.kWAq{ `5M)d-()͚-j匂X[-^(<{n~?gXy_BOg1u|^G>iܗ//io{/^|"_|_k~.uؤ0^Z}A9|?۝6!?͹p7W9TnoZ[ݒzC|omn=ў& |f}P@!lEux3`P{bu@wd?ٰڹ@zk?W&`1t*6Zǘ~U' {^|@x]q p1Bۨ \ g΋s=_k_Eh>n>fsWC?|%߸O)\Bާۋw;_lKcNW{;YP֪ێ^دJ^?VC1KOWvc> n[7$ן.xZ^{s^ C_ۈZۛgk` @ <wt:`܇| ]}/eMU &-ИE]TO9=la@@@>85jzb~ @Oc.@P("m[M`g(w 01y S~$1ES$m->NYwhglw0SmQU53~GSe 1\C(WR!Duw<o؆stlŏzX/9k۞]wgÇ@hNZ<@ad$4?g.eĚ@"dȠIm P 0rs~؟?'|j: B$]4nԆZ#)߯.;yvp`f= ۿo z MCS>N Q@\|y~ ??2ϼp?k (m<3_*Kݞ\knY8O9O12k@HWc.†9-_?<}t2?_Ȥ@S 8c0_aڝ }{)? lR'Q#GMP/yN FPQrhks rjZuk?O| 9%,.I&PuK`jzo?&1_ ;v~%oɫ>k S>4_t?yZp6_Q DSrWם/}_ߟ G/Khe$mϵ/7:` q>5X?CC~@-㰍qV0sE (Z|ls9:P6Vw !|qQ3O-8\G^٠A/%se8/@~C xةwA MPΔ3ji57g_u-룞+1Q \h7 T 7Kd=3?f:sgO|[Ct |hտ]Xpxcq_K_2>i繿/5 ؠwI:`c~C D\3O<@`S`AIm+sg/`6 B¦yU0kct %?̇:2? @ɑ^a=Z J}>̇<@h`/Rɜr5Yϵ_u@-܀G_?pb ,W8uOr}}¦Rc/qۃZ~hyrx%1g0K s\[ Xu  t{/4(1+q>7ם؟=֯xTj1/ ˘1ha Ϸ5ڻyqY=0Ps 7Q@8YTTR֕ȉTS#ُ =>[t c<@_vZ@ @ 8{\rZu@)?lm+_+x']ԉrkh, J_`<|?K̿2;1 PUa_Uc6`O?+9ϟs}3kߟ5pU`_Gmcߒ_2sB}LMg0&= x?` 3G{ e'Zh=Ijju֓{~ 컂1_;/ g\ %-`>NZ`# )3zcy@:WjNsRپ:[m+v\_8+?|??x1쫬Ǻd{0T`}\;|XB?d@zru3?u5_Yu1?ӿjֲs_ml#[ӷq'mpkm:Z` 8? Ưl.~lw&䛭lG4Ӳފvnk+֏Zpe&h\׈=bE9/ۼm5Q5@]?]4v׿/e┵x>)1?Sc!?X~x:Ǣ4+@_}a3!c>ء 4` 1g^2~ɺXH}9qF9;̿i?p=/ǰ6,wk@pٵq'*Okz^Mۘ׌x'Dz.w }% E+=E-Hj`Ƞy}}_3oqs= g 5Y~v|?i2wa(zhF]704"ɛ&1֦A留qsƸQF6)^/eNy8v1qi~gǼ.g?p)w63wGg_F 漿ܟbeH;6H t@LEu2OOwߜc,Rߘug^yv>@ 4D|9r?`QF}VgXGuTx-`C9=.e6G,$d?+mls8OR&1VJzSn2b~'i_=:"-w- B@Ҷ)/}V-0Y6hy~hl"d=_@= i`=%9)??<2&HP~7ps,}-ufziu@W&n}9C+{zouQp|]@2?r6i~}={i._|Q(yIsC]ALLys' kD^uyZ*/B("~#C+nΓԵԉHfz{ˡ.#}ZF$ޯti2WVi ߻|ڀX&~M*_~>Ӟk_-Zu?}y_ډ4yr|nI%0;g);.F@=~y?e1[: S^g: =onsb~pVXYMP+-0 }3ȭyT~.czͽAP>>l.{>q:<^~^?14ALRr;yǸ_aµw-E66n5ޗlMK?>ܟg7p?D1F~r;/CX?c|MMgO5;і9|yϪ.?p<}wuÉ٧4^ uh+ǍZQ&M~ -Z Ա]M2'PB__de@ڳgZ?_+K\?Ld\@@s= ϵúc+O:^c y+w]lKjWYPb~-̏J_$]8bx?}IL3s0oqЧ^xu+#}|K徟~eFm"y4@?rc`R: c{ Y$9߯4A+w~GO6u}z?t4Ծv ޤjm D8we9rO7_|/>#GV$VʵvDU1/x?i<~&q?`<~`; 0?q-@{6lwmo8.IKk5eajoam듴@Y_OJ _{u"&ӚQmY-{[1?bƘz`8n׼?2?ס-Zrl? i簷/ vl]j&gB $#c??zG7hE`'K>vy]|_1NkW9yMvHk,c::˩/ǽ`~濴Ǟ/!]_c'n cXoZS܉b~ tﯡHZ؟aڳa?8;o:+^ޟ 5𿲟ʋ y3届_h`5@~w<ʚr|{8E>RC??ȱS6q]aߟ>IsG7uK]&ދ9hzoKd>>dsG e,cLs?Sϫ/yYe<tO|5c} 51w&#GMk p? R>ߨ*0is^\jG`Z95νu5-SIa4|]nq Lvې V/Sϼk0w cV﹇v}T ۧ~4{9-4˞%KrO1jۤJ_$6 zzJz@C{q/b}9~F?C:"苘9sMuѯt6O茶Ich6fjZv(cM& idf\O>t<@/%a=>|5:U\$ @/: u_X_t:Ѣ=Ga:QxV ·c%5's>Ŷ|4P!' Rt_S b?pkd%s^Gֿq#?Jf?/` |QjԦd9}f{ʀniy矘o~OR~oI3 ?ǞVPX߂]b`Z mIv-' =/ Y+'JpJuoZgu؏{ h*9<^n}hN+ܯ?? a=r1mE ɸuIa:vzz$Z(;㽍_r ?~6}D-*+0|__X >Ha }j1?}rg۷&1U,OsZE5U92t<x\ ] 5 p~3 '#m]A߲?ֳf?ײ9\f{1Yy)kg׶w[տLtpHzR\>oMOR L?2's{[=~[[,b)'5}%+EO}ȿ-Z~Ut@;k ]Judsyp{?r:3?1pud<;YК8hGo h޳cFpRr%{Jbϑ^-*g~cPC/q}\=5u_ُW\~67q t %f>0_u3 ]A:46X"%ku9J|~h-{Y׳ٱb*h-E<6 kmhg@=6y[֟fs5Zx}S0 ̿ߥ׾6CkkMť/>|{w4/5d~a [ /{oBM5V.ݙ6k)}0M}:O'nX]mss~Xi'k{{ioLPH.?4@%=pcI(W}v=.XhRhM\5p@c^"Yuӓdk_W"h+Y؅2?mO@g"zt )_C14b;W9p-GhSF+ԔKK?nj?v%1뵶Y05@>otlwﶯOl~*>A}ؔް5?6y@sM wMP*gpu޻X_'nE.9Ƽ$(-OIg*b~d}k͘/{εKMRm6=lfLG@XiVERi?yѳMm _h7(' zld_ Ù?[PlsC?4A 9kkM໘ؿ po*^"yhZ?q>Jdk{@? b?;4IWs y?_{J̟1;'Wr~ R-Y>톽\܆3|3uO\41:=/uhK~ljt?싟B[u\ ԭ_t1Z_lm[aWȏW4?-r}~Ⱦ?O}^i,qg:9e{g l]R==Ѫ#=R?昵mq9"gj:C'y4uEsyɺ=gCeWҿ/_jcnopiT!cLQj>h8&/wrx:Pɵ/ո[^p3}]y|"#?ݏU7###؝vk6i^Ⅳc` >"oG_w}i.1יm۳{=bcd>H~6yrcr k3,-C^$39o-~0ta_Q`%?bs+\':fo #{ `sوƺI^nB༽V1qۓD8sñvg7//u>%BnPpތ9M*Ɵb AzZH^enWqq>H->?12bs`|_y5Xvf I޽lp>q?8wk] @>Ӛ>oB|T cbEbO1_^s k|Tt:̃߂4c>>"@pJ?ad#nc-@s'o29iY`~S~LxaFp#;̟~dt1sVy!/o>1Nr\w< q ko`'o^Coа1'ga^[j8p8=?l쯸-ZZQ-nom13ޅ}~!K=|9sA˯;LW_<p^NZrs^Vc( q̏؏sh?)~Z#$9@;m pzʖ< nuxyDٻ[l |6c{lx~zPW_̳}ܯwȉY'W=p֧}6#`[Q+|U(B|`CLz\Wp%=(c=.y4w[3XCw.sH֘G''b[ {yk{nh҆5|?kC|?U> p>zLX1o䈮㤧[.l`_lz]6׃/r|WzzX0|3 boeM9B.Wp ߡ>p|hs쏹coO$^~lds ^Ǹ_e 1K܋chV3sI~&?_'z~|#|o=*|o׾v 9֌^ z? ל~}\^A0X<6;0ʇ|{\[%0-d dFqJ@?cj6~ӟ˘"P  c{`zN%Ca= ྲྀ;zgAxW_{[M@3qj_^zzdUHN5S.z/skԵd}wA[=t,>0pu0^[~J~bׯ+jy~1|/q}u?}^ANׇ1O)x1ra\Gi-w~p ǿ` `?$ ?ZOrJ pz= ʆk 'Wَ<`1]=z9^yppءްf>R٩/ٮЉ<~$[#oq,#O;|虷 EGcZ/=;s/b 0@kk f2;!GYX؃nϼt[ 7`e=1?^S̈q\}JUǤ?>` ?p;.#/xߜ]8|E%'S}?\ qǮa &v 95n:2O;70p;>pg;῁ f8i}8?#o@r|@y~ L޾wPsOK 4*7߻|~{[I\k͟Ў9 Ӛ#=k'؏ݿ<8x0B OY*3YX (OXUcha]RV| Zm'ǽZXo]%(^iWx \ LZ 2s7`}c7uAs q߀ļd@ew72;zx^x2M;q;5#n='@z K/9~6>9;qW9Gqkσ9>G=Ѷ1%ܿΣ+q#ogv}W~zc]j{~js mʀuMof ^ \U9bxO@iv\`1ah,'w.uҳM\}k>Y+w/>zI{L &E\W{(#o=?Ň'[oeOcGa3)k0f߯uܾ:/̤F qເ? |sj\S`gf?+=Y>-%c=%7>;Z.:~_`QoxqS/x_3M ~[^b__O^1~;X? r|}Ax~qr{n{\mb&W?P w`b^\}5 NFn@ suxFgګoǿ)q=nwl(~ mxNA=ϰu^؏\`5/^Z_e>w?Oy~9kVq&= GFlߒ_1MƟ y]8U06 mǽ1 c]p>{=1~x}( #+Fz~ &g(NB;J] xMҍ,8x@vY~]pKϵ}YHXp.:f@4-[`v^ `q17{u_1"?<_i=ƺy@O/8oZLz)q{\"%{O ϵ}~ b,jr#.=~1$ Mq"kv 9U(E7[43(g>`|zkگr!9~y8WXSks8fې,=Zr T-`GǷu\r>[tz/#2<yaz ZzvJԩsa8HcӗxX'n?b~`$ƿqlaɦcNK7Ix!oy:ƸG Xc$Klbؑr}=^ <c(8^Kku%}m 6Xi .mƯ5oCo /~1/yXO?+=7w 5犍;p|鷳%lg S`ZO[50j#X:cF1gc?W=M^kn>G=kf?->'5AM^ 8Zzo G<+'߾8O`0CwC^pW?.xc=pzM@e6y#1}U/ ]yQOy!cֱ >6:9_iitXt:SΏ*ktн=,xYn_w7cN$׹7^(CuNC8]coq1Vþ0\C[1#rI>+Ԁ|.OSt?ՔϾHr=~%?/x?8G; Ik lq .ȇn_Q6qΟ7axǴ(GYTa a wIf\GO2JƘ|N1{`:??k/7^"z?|}+6S۾dz|kvlP9wta6>. Xt {}jZ( vo_ӿS?~+f8ouLN=Ѯo#sk=_O~7uei- m{l8@[c=ga}Ά0bCm}o\/&t^z5 Yk5{ z/}ס&?qq}dZx:/@,^P^5<> nWmx ox,8 9O6Xu4:aOqt&%_~nc rws\!UgĴ}y~{Xo5ON箘5$=ls78X'xIw7|\:n_ki7~ŐMo`/Ii*?|+#g^2<KW۪-γ1 mP+=g QGn=؏of' G8X|>BX}k nHrx}\ڽL}w9 dϟ}lOKf6{v uS^k37c>asEqu lSZ@7:a~=1^|ec 'G EOI|^dŢ/;Wƪ~j8r63<q"Ǿ@K_ FP's &`Ll)C?AJmԹQtZ;5!/q1S\GL6}^ub̒cffܞX b.P1ߴ`, geuɾA)x{vCO>ױ|_)79[Gu 6_'Mw紊SG_\C^nM^nW?`g>q򴺷98B_ CKNRc 9@C)6{u b,}3[ym@;7d6np㼸@V[tq?m3.\`wS~pa2V0[1v̀GZ1?09Arĩ;uQ @lߔA 0nhSOYpXX5g~x@~ѹ1 X7wˋ${kqqC/աsh `)7x?L>c?C<sY}}[V+@zʂ6YH \y̎&Zv 9v1?0 nRq?xA^Fw)hؿ15|ZۑnS_qf/\NrGN眿`gGj.z>oXO; ?l7?L,'{s {5kN-3y}s)l#7(ԳEIZah?@O @iq&%q|͇{O`+nA<`<-"ea;77ޣ|n`\'WOXˇx3{^oK9%C|zMxm!?b k%j-` yb)O"m+EE:vkm-M( z[K/?CοzL.@>-pWaο16:`O}@}T{Lz9׈u|-Iq<aAo ڞm[6p;ܟ|0?cop(ʘ;m;%'hҍo|ٻ[(YxgF?G+rmz+05~5}?|xY>b˧+_yx͝b$mD8s9}_qbpM5CU#iH+@N=ƻ0ec 3?3 >CR;gC};؇g>K*o)o<7a޿-/-mF} Xe=5otmSob~IOu<񴌢[J,Yp z#*߿!Q|HRn|k"?zP7=K>elQFm@Nz80<|y2;ٖyǝ|?yޖͫϛ<7ѽ@7Sxu,fn7IuIv̀tI7;m <ޖ!؅ϓMV_߻g_&1ϳG'}oqAuGފԔ-q1>}~8`OuBA3y`}7]cuW!AF͋m gH 9D 0-˛hl,vs 0 =C~bPWņyd2 іu<}*/Rd#϶7I-x3x9#>m2kHwq@ަw9i&?*U~i=YS0:Mos}4܇}@=?9~'w7ajֳ~QXgMCqtƷz~xFc.*a-ӟ8mkvV+o5ȶxvHZ7uŃxssky`;8{'=`>/ "_m~oQ.i~a*zzwNi`[YEYnUBk(?5͍9@F*\b});;'-~_Lu&{F؏18BWK;ȩϻ!;~ylK@K@ݐj5Q>cm М8\ܦo\vUS>Nolټ+1YnOikn~o;ko^$eNdo,fgb?b<X ,?uk 53Wbޯ q7b CQޤ8`s>j660ʧ ֭v{3'[>H\G9t_4˝l0Lx ⥍}z~[c~7a`|Cu؟2q[_ a?6Toq@ÄUّpL1~gκ uFs}o5en{.r4.ڊ=#-@{-nN-QiGYKg!ʒϏq^ Uߞ x~6/وʾ ȃwoe1W;0bk-dkY}h׵,e%h[$8hHx{MM.h)l -LЌ./v[qw zmly@OGWFu3vkУ5_q?Qg[N?1A>o61@>9>g =3ןcC<<;.fZeh'nŮ:43l ^/qJ;7{-kn:ٖT)0?ُھ߀܀ꁺ XUҖeb8bsaM2_yQ3f_rѕdWi,?׺[9?c4v~/a7G%,Z_^{G:&<dgYGv(w*W. Ɏ\Qyg]R*ߡ/>6۾__>F17|͛v<˧8ʾ3c`b+,/{iجC~}94xQ/r+fv{s_}ȧX ]x@IZW^:#oR/t? Z ށ PƎ0a}Rs.=2m؟k͎|>Vf5,|*u ]#&yݫig|W1p~ 0`z0x|XYwkܷ~i{}0%Ѵv1Зi~žc/1S1F55_?'8sz^o]5m_y(k嫹 UHKSnG18o1??x3ҝ%x&]uhofYe ;]2ٿ^[A p7Yy95/ 5gﶿunZ3,Cv{p<6cuҽ@!gXX u&,c,B1?[(m',S9+yӼ4mSyC<Q꩗R,bO~gpzcӺݑڋjЋ<jg⤫i2t%~r2oy-/[3]91 w~/ky:_/:Ò7,[0*}괿8 RY>&t= >h2(s΋=^u| G7_YCSL|l?Z[% [B+1oN_o46qOsI_ T9z.窃q ȗWΣtvit/ЬO@z`1g N7;oɤs=?_;?~5/L\x7%ͭgmCӞnN%,v|ƯT`^^ 'zz:g/QR:hi_chUo֎(q$ot֑^>oݳ(*KدXWꝄq,GSdʴjY_}5zwoϊ !\g81茘ĺ)ϱBǗ{WY2ĝOΣh}EEO޿&51钁1@Wb}6Od[N1@5uO>#῞ ׮q/~fͺ+9LI:Qgρ:;}jokN俲}%w9|gv)߻qIo .Y-HxZW;`H`_D&6:н aEV(uʓmaQ8APp/`uT~3eٓG+gw~Nr}1}%'7|=օ_xt1YG_b؞ʯ<:CK>CLgY} t{XY*NRLa <a߉mݝ:+Ot"x~7HI|C1c-w;y zEפVgۙݳ|^Oo&zKli  ާ2U|*=N}.k]{k}}7Y>`G,S!BuGtl@~ks=f/wju?d~geؗq %M/?bxX}&2ޏY߿kb1^˸N˛Wz=~57._{/`Nۨ7c[椐SIؓ ;^ʤ/?3`I>,%?9z/ vVzǚsK7姎̏W\q0>P]:cjj(-=hِ'ߌ !ZF_}Sz_d?r ` G}|> y9@`1@c kc:>.h\\?Ʊ@l<0־? M{"bPd~!"?XcyX~B1~0+r@x(Wz7c=|idLHo5.W1@G?_ ! u0a3)lW+ѧ1:vc/}q7}Do.Q}~9 >tÐ|FϪrff75ݻ[~~6`;ǼkV} 0BvT[?h7}uw:[g\8uji{9`0ІLASVؼ!e|XϛgAHs mIS ~@pڣʊ&@,#%|"ǻ=RL)j]U^Ly,>j/xfb~_f)‡xL}؍uG9VUnz9Ʒy?Qlf`7ꌘ 8 R0:g=y_8nClY3j.fC=h5Ȳ姎: Yg;[ILߝ 71n2vvA<} \eqeؿiYt]?_ km>4@Ў{[w_܏t1ԑVR]1eqgާ\u?؜߳/>Fh7R` 5>y%'ֵuܴs6}vW;]k0]2G900Y3P\ݾSߤ3I'OoWM^g;NPU/PDչiWҗ~1q/l7G_?_']qtinOzK>HbLyN*esiq0a]/7lmqLV_X&GDA~|Jgg]F]Oxv_ &+GK}\|@=߅i{s9h;M䥯ٟebh,m3~g?#C3cS|\hX_M\0S/Let-hy gk|`eInNYp9~~Mx$qWSo ׸@"r C9ȳ?Wx{?\'3ihz`LLWgPyo2$YYC_:]ۋ=K6}QG: '@)ͣ 4-K8+ l/<1->ט0cc [? 8~E{PƗN n?X+9g=m4cvӆ0gm1c|N&oOL9>:6$)N6۟@_ͺ?چy;h5Vm8jý0`ۧXG_^OSx!<~ڟ:?nr z=va[V?zG^+}~N7 !OΞ?6>&@Q=~!ϲijGֱ^ROl|1=KƛrYqf>4=pLk./z{:Ly8EVSg~krn[GYmؿi*#60P< < 'a-x e Ypˍu <3;z#BZ_߾=`}u'ѽ|lTo0:1~3Пۜ7Um^yڡ|YOW.?GƯ<ƒx X㫽b2 vJo3&X}q#qsK.[}mں5=o/9:uO}r{=إak^eʯ2#yO7Se1Vh\|ӿ.yA7Nz7k}4|s쪧irW|Pwolg{vxui;`;7);89N3{5Mmʎ*q56=a!eTֱAvB9~uy[>׳#q%f@=9zn95Mp_q#p]ztߤ>q wgf.cM9i{ Dߘz=m1a=K}|g_@XQ3z 2)|>8< +~pp}vwkFjcY7=f̎7@Nq.yӬe9佯ckOuM]vJ0uGl9&-]yY_ѕy`dž3ԑ߲r~t?eZ1x]mӉs^w[$Q((8_C(DHJ?ac.ݮ_3z٧?b523(w{xo*vg/0}=0c=lOUӾb&m#\Qα:>y>EY]2IK? m).W}gϫ)y >W?s\p0.,e5=F]p9YEcl^yzwi;N>öYvk7{wMߐ%`޷147p3g^'yh ~q*҇nl4 zh;wf}N A܏k'#I9x_&aqM`:6s)@ƀ@N39PzƨL<` Zغ=g6*cܟ87׍MDE(pWY/{׺~y%}uOsG?`m}axHy-~a}(?q]+6޶S_.:+_8?xb"we9/μ)'@$_ =u[1~U_ t}'Fq=[j.}9s_mP u͚B C:vKp>Sc:OxoC^ue_<Xm~*X,*꯲p=q,锼 u!uptA };}_s9BN`suIF0@K̹I^`䂗l9vͥ]Vg{3?I[%3RGQ?,s: (mYo06.:ϽvqQg[s۶쳮!8.F SJ)'셛+g[o'xcnݻgO@ ~YO̷ϼuG~nx2OqX믲^`BݶO56?>[|0͡Ok9mt?g| Eax._AT9Ș^;({E?mBG~p*/ɐ7t;m͟`#`pa*=ӳ5'7m< 5~ls+9m`oenKȥ1>| >ұ)j^gA/yѩ,r;a;ek9dU,}*Io]ׁxWr@Ժ~nc1  g$V3/Q]r0`/(={7<\8'TM|ifOK)'m9- xd>E?> 絋43q籎|=|>>oT H)F9h@Y!߁Oo/)ٟf|Cpw_/?cx?ݕ4(g|;wgc qKӣ*>oGR G\q}3}jKeAgˎ\³UO>j!S\O~q<.++G\`LkezU9{b]|t '~ܴ?#] XZ/[{e)r|O)Fe\r''(ק}bvR[=q7*=Y~xD>oq?>_6جO2[/eME%]ԷgLc( [Y'?^Ky]{*6u3rw`2n(šVy~yq5|ǜ/ƾh<{1`[8Lwm]~@ݎi=}>CIv[ {>ӵؾŮD)9^ߑp*_s+]i0<=6w87xg$Vwz4.n26Su]/zLqg$⽀k(HOX0z.r1{cX&[_IWM$F ]L<܃bV:l]LiƑzӖ-~Fg϶}̣)zg*)@*.9&K)MZn^ss:?kw<X)s~|M6v<}0^ lW9L'83Spjƺ[}L<, O7~j÷:սi9caLpcƕ/:J9v mOf6{1i*ɍnhݯv3ЉwD?T|w}';䄔NFsֹ͏:8忻:}7_嶯cЗ98nǹxo1>:'Ηs[ XcBV9^!nm07 B'¸tCHd8vVH(}V 18}3Sshc)p>m;|9@i韎tB'd4F\gH >vNsY.kV}l@EySkty+û'#ζOI[~9aWn#Yi_ sXd/lanoǓ=fۂ>t6Vp.QY@ .^q8=U:v#~xۖqy< _e?r3xVۗI;G<(N'+A^?3 !`'Rpq-`g-j{'H kW<ɻJ#;O lj]֏»ߠ,ͻŶX$}c0iml _>Q0El#مNr$y}*:RQY3~OX;/þ[~3{{ms,ɗ0q}uc:Clz> =QzBwx^K,'0ogټG(vĒysgyǽw1&Ɩ y?y`E꒭D܀jK>)fT=(fQ۝s>0O*ҍbfkmGWW?L 4y <}ߕ>H~Е}b>5i_*/C;8ս"m =ד.8ؠ m9wLƢ PsrKqwb vv0_~Op7}87qƍg0[y]N;=ǃRd-~JZz~ӈ#ss^}+9qfqwz-`y|?u:rYwsK~pnv]`܌c:cdSXOAC ,GY~!U`N9F? \7ƤIYh/?͇c'*n'IIXƞ΄/;9A:xWN?wlcSI;rjJ@ryDdnJnFkmkvb,yu,K5{jp;Bǘ4apf< (TާNoK8N@7nc,r=dndLZS; 8.cE%^]W8m.үs}a&ud/|[|>9쁇kk{Pf ^qF Q*a?~}ɘp]a:ߩt1Х>4s^\xFF/xoN߬>lt3t)>e, #\CPbE=V>r(zG]O-gb^OؗeA~3*ͶI'[Tij|1o]]0P0yO}"]pbٖʜn}e8+rI#c _qhT^v;u?7Wn1wwUXWۀcs / yr;ϞqֈB܀|~_Q"8ʾ6W ߋn.VR_xvNme{CijM3Iu;b_^8ԊۃT6}s m9?>&F$G+sZֳNݯL?8uCkz/r+v_~oIOsխnM0pi/e xmWmc?{|\ku^qcM]uxsmx֙Z!&%N_j8l Cm N|^{~ti^uF}B-WOs4R%!tUΔtp*mZ'7+s 95zxvXh?n?f9 ̾)lOq=)WXvvo؆%|c2C'n>az{cVnGx^;}Wimo1s}L8^03]MLkT?< 8[6y9fq5}YD=4s, 3#3XEW( |t#^6[6s;fu8q8Pnn]E)~@ҹYrL!tK$+:y@zdX9F\}շbW|zx'6˱6Jp'/n1PVi }R 'B'sBQyK輕uȥڇ;m(ofe}D,Cc[OF)!c1nbwBb$^lٙ1:< ۤyyc7x(}J$7^#ɀ4ҖqوQ1?k{l੝XG]ݾ̞9 :u~2;ʉcev_#:KuyWooٟQ4m?n|+}rxeU<}n`xZƣ`wy}@H~%~5\m&'H~Kp%>=νn? i_踶=@Bu,s;swخ~by/} l3/p8m .A8p1\y fHs񹽓_>kYt'{l `iT:ͫM;w{FR|w.T_X?A?r\z'o#fik|8ZV)x_2ʟ,Yf.,ѲmǨw0dBA!?{2cEڏu616FH#q=ے!q+Ё o}/yd}Aل/+Ĥwlg QqIt_}l4b/v缤=cd$T]řuxcu#Oܑ!iwMR 1x.P@}VWoގs#^~)goJ鳎g]}C-,8eDco_;btB>e!+ =~PZ,G^oѮеCƛc'$s;yڃ`z.Of/OkLMWc19ߙogW.ݞ%{qKdlB^R6I~q,]q?mS:!qAr럚K}f ]< P{Pfu(g~\@y"H?? Ywzޖf;p =<  %zQ{-`+E>2g@`9{S? c{ˎ2fn`9k>}s?'8I6gi.x3l_ n^[f#Ycw T;wRyrgSzxxo<s4(}hoJ3^ˣ>r{KƘ'G&'O/ݒs;& j}5p;gy@?>|/>h3:9|֥$Qs;9%뿂3~a1NN/}D G7c|{ByM5 c2ݚOF |_HdwxpUoFe\ݕgͳʱ7lCY:NW%lw=O׷g~}%3ۺ铜8`p6l bY/_4+>><@{9%?X5gVu.|w8xĂ{7y;j/K$QZxR0WؖM Uk@=^Wopenimageio-1.7.17~dfsg0.orig/testsuite/texture-blurtube/ref/grid-0.02.tif0000644000175000017500000124311413151711064024470 0ustar mfvmfvII*> 2=RS~2.9AHLWaRnwwnaX=MH&935n [2,٣!D^:2016:02:25 9:51:38 x}K,;ҕU4GBbˆ31cĄ $$$CUipj_:qcgN;ٯ4q餒>r>rR[RZ_8ORIN:DkRdX\M^Mp9ck,TrnߏpH\˶tkۧÞWίdu'zJvX}}|YZieb?էMdt|PϢUɇ d{Yg.sYgXϯpG~K _~\#|3ܖn1-qEv/ wm%'=?$grYGK. Jšng$y)]YV]7Z=ε}ad&?,sdڐT`GN>KsxnWjjֆMb]y2<5I^NVu 46Ozj3\?_Jg+\p?'ȘN e}ƕDsϻa~}[@ϫf5{ޛ.o4|>.mV~¶Hs"cbf\_+OVrHî#_:% ?$\{gW ٮ̧<-a x#TT/sM)|Sǩ`8o s9 O3~r4 ov܆ 1`A=&ms?⍌3|>k|^fX5._ z,b{>χ1 `5\7ؿPI?s&+Sl!2xdolCM^^WOd[[8oΩAk|[/mx'O{ X9q0ײr[=ݡ5˦<:<k{?c$V#!t6yt֥Ċ{~. :o 9h/_RTJj_ 2rf@? Iͨ2_$oާGj=8a| 9NLb<8濢j/owGrlyGZ9q^=ؚcV?cPb؟gO_a϶GR?,foBbD3V8}>[I| 2j]ɕ-yk[m8K=q'`+\mc1=mKMc 6e$m|q&dܢv=;f}80$ G W%7{l[}kq|};.u?X0srHN/]/u,'^}Qz޾7#ksc>.8/&=yt]"'v}ne^=k9y m} 7CX|} Fq)>gqf[y~ 囸WfA^EiIнcN ?3h!F\vv+<3?ؤssφapjr54cy"1?{o2ΟA~gPOmu^3)Y} Q_8+c Tl=_5?W qWQ5##;2'ӟ7Ƥq.g<2އ01 st\wyo,9b"(g7ȉ&*p}~&ֲ.-ĄRn"cVq=~5#k' w\ٶ5gG.98@{~P9?0s3$?9Q]?"o[> rcd`1GٿK6W3^qfB|jF1:809G$΂FQ~`/?!9V%-/bA&mb*x/6N&JPc5_sBcirdzȧI.8:ɿ]pMptT?ꗣkF=}|dz)'ֻw1@ 9D11z_;S^gn0?]_q>ߖ9V'Xzj蒱߳:.V.8@=$-#\ԏ{o{?`n(g 0scƿ7!K.ȻӋS }{cOFu6,O2=ca~sL9_L/ l[FQJϥ)/|-P8\RJI[A;N~+rybS\6כ7=~}{9,vߎθb}wk+', `?q_N|+ߟΊo5{h>7:P|r$>oyԷG56Q.Cng7 Hi,M׮r~kq+|8±O~5G!A.[~9SU-eG2M|3oHV~̧@+ u 1mX.tϘ˘o1zsXP :1>_G{lD*c~|Kk89_-q9um `9@h@)Νx<}ޘ•9㋲qɼθ1_'=?.F߿p٣n6srYWzBȹEC7k1sc ewY'7_cK8Tor/5ut~}oG,^蓽 Ͳyg1 =!5yy],+()ݏ?mwi{'v\,r՟U:5G oi&ZtQ J&🠩LW*&2D)X$UB}XcqT)`>+\~u C ׯU#oRzGw?~b=¿{s43I6\׌:^9{R# ?qY(?w3ǹr9m ۹[kx9i78YF#~:N+Y\ݶ cGm|"^e&x=״8@ 0 c)7?t{TX?AY[kGaʫۥ5:S= ʌ=,⹼c7{yMs?sg=j[̷+`ܲ/llqOVVg?[# 0ڀ\}(i ĵ?*cqyFlV~e?5^Oa>AgX,6j?okܵE6|3?Y\)! /#ދ>ٖZ6.U `rfk+c޼?KshsRs.9OdC+R>KY^d(Z@>log쿕Rai\BК˿I1`S'G#|ȕ+5ckcNTy0|{=5|^Vc-gOR[8,sk*K>Jmm-ۺR,g  <{į<~|v.?2ཇc Z yot/'9Bi1Zd{zmXǶm0Gh< :'tcxq|G]2uATs`^=?ۏom-=Ku;Al |ҺG\Hc&+k {b{b%/`$fݯ'=}_o̺|+7' @, MCԽ:Gp[U9[bYc~oݖ=l,HZlP/c5nk##vΞ0ߏk5b-^~%% qq_ c{;f?zY&P`?+@ $vVֆx~43[W=k 7sVO=&kn˳BC#̥p]?5p&gP5ԟ/j#@.@of3&=ga]?/P{0G}?!Y%˵__cK@(`F?6%8/:GErN^9_X,Yq꘾,lӷϤOg\~%Ұ_8%eH^s|fg%c)_%;Ā'8=ّ [-K `\Tϓ_H{3$.7$`ˁuF\$@&XE`1!L,`2/N1ֿy3BO^F+9Q^ \==jiqyO[}|<ޞ۳+?(t̗@xk|$ߠ}րy R{9A5{+t7~ر'oiO1Db?`uOY>7qR~g0[pr@_Cd:yyزӇ-mQkzoۖȖOTB-F/ס4o_ [n1)ϙG^lG-gc}=A+[We(p\#_#O.r\T7a|/u{_k:5s R%\sYcc$Pw^cA|xp X&L *w{7 <X?۵v`+<&$@VߴC8@ W0sW/~p$lx@twfۿϊ?_\?m]'6*_&z9 &XCp܀#7P'.}P ,k^xt8ǿOcqkbûo֧j ¾A0Ir:xxk1=Z<Μǃ:??@2xRr\ g֚G;X{L~ ^K߿Ϝ,奘S|Oz\>yAZ3}fP Zs`wf$>rΎWt]S}< 1j^ɇ!8+3P/OY6[ޔ\bl/w.w϶7tXr8O#=B#Ը/u<9;?~e߇V99< I&1820(i}y'-`|R +?? 1+^)"~JG-x,Nw'j8af*k<_ Xmc v]_["s}G2 j'DxF}ye?ؗp<]~.KN6ZϾ"4b[NbGKp$ًi)֍jiO߲"i߷Cup4^ΑCkG^#/>qCg82)3}$'lW?rr &s `6I2%8u?1#Fu\:* (uGo(}A+ Zh!p?=d+yNiizʶb}cf6+B 0@c /mUp 4"Os퉟`G҆W'_foler *grky&" ˫f N8 cT`ƿC|8Vm&r-WU?>.]D~}=d݂ͷӼʷ])y{oc>[zru~_wKCoH[<22%N~//uW_ٓz͆FS~J@z`]#+_N2RRB X+Q@>RB -RۺÆ[ĽZø=緆`ox9ڶ>6vz.Oczv}>~)7\cnl-_~ϧȉ\RVg3輾nqTR_ Sr03fg]z}O_q*U}<Adx ,N SE=/.Aqm-n`^<q& 5.Gqoג9l60Z&;Ƙîyī3Ը1}lwlX'fVNQ] dPiXϥ=\o̙*u]`.b?scE!9ISAZ(?W>޸BA~t\t6Խ\Qm!M\mY T>PxWթr n}/"cEUt)_ڑS{ߑhDF}* c~+ />}hZG+e }Wu~:O@_iW~<&nzO^_};@N z""COZ0,lNA|૔>ϰqZ-N𱷜ڋKYބ%7x`8~K*d[7%ùokUt<Zh oag<ϭkw߉+ϟ8<;W9@}<:H_ qXj_tHX_U#sY)qXўr}bx\ްtsv~YMmhM`}@9u>?L~g{a-GoXk<-7 : ϧI@׽LÃ:+a hX[.9>~/%9OqBÉ;aS 5s|6> G~y\#ZƾCVK&Yqe\~ 7S=PXrzFNoV (ˏr0_Cl}m?YGm^{YCǹsqutd5K?vANja8w_|vq{~-R]_<ٳ@X4F޺zCOrZ9:7/{w?jt_oNEwa~EO`]I{d[b Ϗ8@|i~&Cg`\_h+91 b/64|m~~L|(W~bߗ}g>㷘VK-{y<Ǹjh}g~+$k3?K5#w8eЕ.<;ܿ Iw-au<%A>%gԺpHm­O}Vy,Xe6Ew+> g+xr~sږ1,"{cd?Ql\`U~~!d )?ѽu>~w? dv,{r VbKohS_)ńw1sٷXWT} NKyUg_vYn`lJZCI?9Y#˜EmuEx*>,#_{]jG][`W?q^k1ok%nmQ?CI>\0u(/;_0>*8i ?]}n(Gqm'S>nAuO~̺d.`?ބ*gu'#_ ' ^PxGg@w(6>0 Qg>qpvm.n/%x1'ߎ~n%AX_!1 }| ,݄z`"|/#0R$d_Ys8='=s0e}k4{^Ad4~,5҃=\s\⾵y Wr6Kua|`Gad̟@ J[WbRyF^|;o.}6P=19>GЯǃ?._~]<:K?n@>DrmC$Ew e/r} %)9' ؂pB/ulvߟK o"#6dT?"JGHz's Sy$|_? @(~'<Ӡgn9#XHJw>"O}jU͔:bW#c<'9FnƸb<;gG{}{|c;`yZ={OnQVɷg_j3~}`_ߙ0חػEsn㿓>$9Et(l $0ew*7>T\ze3yCY/r.,/w_^.@g۟XN9{nAAݏwWMEgx8'0ߺ=7?֟lߊ5@IAMm x}8r%@J̪j78}yteeJ"acY  ?ꫴQ*|^J߶f+{g'L[޷e~|+~|K纝xO]|N[O,zkR\-sYr^h[;#p*Ӯ'ۖ}[>֮,kݟSn%Jk`h+C,\/is=&c-=nRg[o=m˲u}岧璞RRoN&sa;;ܾm]os_ڄckwkvMl{?M/2ɱ 7‹b綿m-mo#(?k-ϖt]ֶzҺmx=#% >zwSk7{ڮm[Z;+ -ezn~u3+ZbYޖsb]Vk%J:tQ[_9=-iRsje+W5}ʶ`0yu>G a68 (׬leRV=;>a^rzp+1o!++)] c_ ۶/Ue{sڸa3%9{A۫г&Rf\r6zԭmңzx/D#p}<~-5z;6'Py(rMGe~UE"D[^k:@nc{_6..ᘛ_`:?=τ i[O c7փ|3=ð.l< 2IyZYfUkwW8zK[m%ڴsO:v`Wmck V 9~Ο <`tH[y<Ҿwzpm[.u{zcжwwqL?jXghۉn-4yɒC;$/<@876`Mcީo?5gLZ!Ro0*_E2P̸PptyP[,XYyW4ɓ}!3[[o+ΆeZasԜw8:ߌ9gX%{X=Em}nOˈfoSݰVpgc @0|=n~U?#/}}'D}$/E#K(ot,T#z2azhKk ^H  ˏD}˕&p*(P}#N a<-^9'^ߨ&,Zueԏφ[۟q6~/{a|`WlatÇbvjz׊m*_ʃ:c /{s9HE=OAbOۣޯ؟׼(Ob%3>3]l2zw5!o_0id˵J"pxr:ˋ//c>ڷdo:24jGYW 9cQMa6:2#GD)*+o#ѫx^uۈcp.w< I( ֞gS84w <5P9@LHtpfiMEu:\O7v<7k3պ {rW |5ZB#?`;\tq{wm\sIvn_p~u cRұmE?>?d[.s4'|N u{{֭ʱJZ/ˉD4豇:a }:vTzA2| XZ@gb$KM0˗E?1:G-h7_p B#)f9p;#P/2#::f#M"V#:-u^NKXvr$";\GJwV \XK\OEy@rlk#i 6 yƩxy@8ő?0lwf$lxܱۦ8_{_ Uݸk92 G1>S^[2~' 鶣/`tb̟K<`WHؿq8uޓ྇>1'7~F1KmI8dїLLW~=7f/OZ߯M' D; ZҰ#Xxեߝiw!:环y ,&|;罋c- "+MVMAMJ/ϱ?8.a tPܼ+'h\.  ,)v|\tOX,c= D~3N0HsGЦzOq'W'ܿ ]c|;oq۞4mSm!Kko! !M)eV?loKVYu:L+1 `H @o}=켊$eKV_/ -KC,}0Οq_xsWKY؟`6G8ցRV6?<>}X}nc)|q}-_buUy9wz:W?w|7#r#?LHҗ'wiogh>cgydz6>zS#Prsd׋C8.I|躋ra>Ǖ|.V+nj x^,uv?WpnrwݿūUDv6bq _p6R~97 c j_?ǵ^^KP.sm=iL 㵏gfL5܊ݺdCbu_4_'K~:q^[NߍEtGzpYƋ[=V1%ܿj=k{maM nRqiuD9Sgƣ\?<?٩֮u#c41-AK=Iry-5x~rZظX>γmnggx1pf5q~m_Rnf6< ~ 2ApNsۦ|@3I1_y$'X=% R-`#- Z^嶤{٫ 9@ZW:4v y{Ǿ?v^Q0$'q:cltk7 b`V*' Wv_:h[Ł7+QPŢ{ob?GN e|GG?ʌ9:b^Oy@g04'Y#S%OC?1N}wMX&t$w bhx-\gS-qӭa9's? Xg/džy~_>m8`cy>9'e#_ãk9`<5Oۚ?=2 4:Lc WzY L&֝\ |/ '"eZO᲏e<kʾ`^'x{N*z_m<>gOuL Ӹ2y.Oڧ 1>K`7{R_;Lqu1pz E9@ӪooUSV Zxo?aU+gCMZr]%#bƧhO*tTRT8kW|rR?<@C(\lW~Gϒ\j.@X%yOϏcߊVo'#{|߻4WrƏ˃u5\gv掹"vD_G/vq2w?k;`ݯ|^µmLS?5/pO1(#?9.΅XOpۚIo-\VwIX<@/0@pw6?ՏtdP:DI\0kalԆQpj'ԬUwuΦUgЩWUةV؏jTv3仲ӌ|-<(ZmA-EtGsIY;|xӌ<ʻT~O;4`/J^gZw) T4^-dNaB}lg>j Gww݅7='/1߿[ m4{m_sF n͆~kx.9}^Gv _09Iq>6`[iog_\}?8xdc{֎W12S{Os@qL(&9dKOH 0 T :5krN~/ے qs $%B>~!F/ܸؼhGgϴ! _btܡ9sU775~jY5>V'U[d`'(&}qV7/DoIyGyrT^* Rj]7W1+c*.8"2fvRn;ġVLCq_0l e?µ4'7~}_cN~ٛME?qG&݋͈<`_>U'đFq }y>oc3- k?jaf[paF @yG R(0ϥ_4@E|\4' | y͡J[+^+xe.x@<+rъ@GXϕ-i<C>3cgduԟ$6 k`%F}80GM5͚u2UOa_3}KM;ШX_$c̥\39Oc*%TRQ3&`|rŌ0&~ jķ> ZAi~mh#A !o˅hͿwq}_9oϪ'OܷZV[b}6fN1c|Gx?gh ?%P^mt?w va1#ci?q Ƣ/W F"qa\) عv}\ߦ?x@ZO4\ cyFuf=1Wg7#qx?3  mXd?e_Ǐ^Eۇ\s'?3N :`Z_/8>CS}K4g|Ds(cv?ھ6 [J _ce:@z3Gǥ;;CrR5:[:U#c'li1ʯkrN7)rL~ vfe+ ӎ?_'OQU } /eݠVz y1iqX{hTd;681c\?.h(K>~C{rYL'z0~!b^melLp>n:ٹg{$3̏oݹcfq Xģ,B_Us+;:?6i1Lt܃>˫ "}w#G@cL#M@+ݒ|bsS~WPh_hl9F 9sdx7N8c{a-l5Ms s$F_!OB5O媗\9G?bgjj(1Z*3k)=~VbCiVN$0miO{4zYɵ+R`SɲVk%Uvm}A!j~oԀ=C'p~=κs.c|rIӣ6?g?;ߑ>#y3<(#?a3 @P9\8 rMюG{ugPnޫ^цrIX&Oto x< o?,6Y<Zytnl.,g,qm Q0;\\ʂHf! $^}t36ئ֩ؿ6_pIDpwɵ8XTnѺQ37&humALlc( `Lq̵dYV9Yli1/57}ms{iR3h߶oZˋ~-p糹лX%|L/̜<<#'3Lbr8_8'ΙdP}X>`A {)9+\<^e̐Ib'4VK|tn}PQܻOksִy@m;\s>>osU$q]X1ִFb& #ldg6͕ץ5LkԷ{W[ c䞕qqӽX:Ѡ7K,@}"1ߍl 8m"DIn@z- '5emn/˒Yp_\w/f5)״p?{ʹXGs~QL`Qcf8[-hcG"MǼ`^lmgsޭ69Ԍ2(2af/9Á\Gbi;GyI<@&d_)w"(g^96|}]sw?ӿcmq =` }9P/ŚZFmgzy3em;3YܹK/؅xJG3@0i%-_@C?'|̷gy^nm+ /?ɰsM#'nf sc3l~jB45bi^ck 8,['{?w6ͳܟQq h8>w,7+['vm9`ͯ}> 3&} ֵv wk(<=5&?w+A~1Q#*yѺA:"HُAʩX7dMnhz@+p5H>b>$lJҎbbfhcb̄lC4јly]734,>f@VQvCHkOښx-W=rNq2^}-:ʒX|R6n&]Z> J6ͬ k}132/he |/q/p=ȚS7DŽbں+Mi\Qk`s{;amض`̿܏ȀE[8?|C<*S4~dc ?}?8/?3|?CM h (`4fUo-\I9JAzyn?ˇ0Fh|iZvn5onZ@ۚOF9.;3-RrXp$V8GP|5`&?l5`Zgyumؿ֏6?aC6ICqߚ|o+q!G5yDQwa?c>`1/ ؿhk/v㢟[K[[ϴ|Z)n-yP/̃DLV!Vˍ8I (6wlq Tl Wp.X'[ ( $L6 Xl~C~Tvx^6׌盵%g.sۿ|_-޼2-a~#OCy /ߒl@Hta۶)/GZZ0{h-D2>s=w$6(r˦=j~[g7xR/~ל3hPD:P6q H `ۭtIv#k 7-G?SNk]rN1=UCCauqif%UY'ز'S?Vb}N&c\iY/E}cI3gE0';598Sm*ږ?+'t1𼸀I8d[ߖbpqv]|.}]f7駵n~_bPF>+>y4a&+YIʧC. P-"w|cӗ?R..Zl G(Cb}b%? ޅI'ôq\/!`yfkoEל1W"<_:>n9n 3asƒ\s٦{g#F˼azruUb~0f̴\6/np۞,dS`iǸfPreG+}XOIpx$ 8k uY X~-=4׺]+r: h9=72,h+"NH>:q*6|[+h'5fKzh9Zˏb$,/#kse_^߯3[t 1}W_ ז %??ߔ z o;ߋֿ>2a˨ Hk7v[ؿ5;_d&Y;xfh\6GスN6>}#߶smZDs~S?{+'@ Fy۴[c| w>km_oDyGl> sE1hށs^5_O#? pe0i4wrcWaNj ,@pKT/w1̐1 hS9_יm OC x-/֘u%_\[&ӘߢΜe9;6G{¯*>k]Ϗ/!>8Ӱ'^&wL5߶ޮ};;Xese9W0ǒXzįDcnmx@2a~]+ޟ+?n_*0a޹zQk{ٯ3lL) |dulq,kЬ.EN!2Ә(rf;k'O o| 7\qSx훔;k.6r3yq( % Ƨ248/P,٨|}~M˿:4M{[k0KTǷ/u{>F\< {@Yv-4.0/@Mk79@Gҋom&[c dK81q.l}9EɢG;D!;_Yq?Muv(7]u n0cc:zȂA)8?PljqQb 2_J}&SK_kK1b9_'/5mhk"cyrGq%x^"<3N3g5+CG4|=)ui_Tg ^O'{g3OlY+G rĶه蹢0]9 ^ksㇿG_!f9 P:9 s?pM״-NR?znR犅_*,T9$o۱鳠?@pl-JfsYsmذvb o#cvOs xLn? :~XQ??˿(U}Ecg #rS1go1 9w<+] RpNk9_g7ov9}q~o6-}crϭƸz9byg gvL/^}qXYb{RџqT;i }'Vc_NSSX\wum sV91ssOu n2ﱍE~!)iyZ\$Gi9z+ۏ~ӽrR9@]{xݻ/q:V~tO= P=;Vj @98OL>l/s!6m?ydz^ _4Na=.7?Gz/ DchQTmm_m 8n-/?Lߍw:B=+񚮍[~W9oiyk ?_Sr_iձJKw^*xβⷱȑPi>=w]goS=0!@tϳ^ݕ CdžY;EhT'c?l:!+۩W|m~2ɒpdnkgIs:ʱlȼh' me'mq[oo9qkN.Xwfvp"& `ayx #^9׌ĺמqGסgG{+>6q1:qsw+\9/.v8`"ǝ1G{2L.G="!&;sJ ;N؆x(s?W5ҭst{鏷tj[[N:U9pLqz/l~psƷׂ hgC$O䈻h8]t8;;/#</س201L8< c$ =8x}#;] "Yvg?sw5+sA M:(  o0fi7 9hnㆷ`نۂmm[Cۆߧc#;k̫n5|`+[]`q̸{a69l7{ 65˅X+]O id:?W>2c[&uxwWWw7h;taox|~<<=`Aw]j-Æj, o4mѿ=bLCcycR+VC7s{FZ~v9Qc{l=vYcFǘ?ݧ;\+\+|Ww;>tw ֙kuf+ޗsqx[N}Zx]yp,nރ#:kV\ʗxzp @ϝ?M9p+97^lHfzM^$gݻc;L?p^z]os}s8C_ϟe8-4]{X>[ynqq; LބӜ߆y[uf3fFipx=\{z7΁xu @g'ogs6q6~?#_37Ji[;NMy3/#Н*=;p5 1϶dz1E!2ܲ%SYx8ܘq+dqW1w wq,[W[܏p Zدkj[Ejr5KW=~>5"Ǽc?΂lxNϸ#`(XSlS T,!P\s0E\(9xcǶ&|Vt?"1"f >[g_.c3ZWeqsKm~C30K _^p=|;`Gq.B#C.07]h-a'Yȍ <ܼ0Kc[yo axtz4`Ҙ5 x/ze HswTeP~} !>bڮ9V^c~8>[7]>6/OC4>rV=sOx?X>Xrt hHԚ%`=@OwYxg?Np<lx)ޗvYiݟ1_qyǡS KdsJޟG3|}MqG_ vwE>ts=?\}b\o˚1^M dZo!չB?Q*K}n3CO{}iU.Z_69N#o_>P܏#Ն@'?tu EgKZP! h Rؐ }qϸaE򸊡cԗ ?[!k*7]14jw熨?hg?!Fo(97 v!mwy`|gc;8jϯ5;:{<`0ZΌ#6 x!ڈ`DpkitS}sh}|F|L{ޫI_wg^^iVWM Мp Z[Ƿ8}:&=켞Mc_u|7{b`jC뱬ϸm;.g )#>!|7/۞$b4"HD3cI ɮo1#p8,ZvXv8؟Fk%k?Z#ݽ>C[~Mc?+jzx -ۿǹ=*[-A2()J~U݄KǞWɽVnv?a?񀛿1/בysu9cwm1Y6-ԟ>}F`9xsD3"`l,N&%r=gc: t.cB\I`ó\Hg?bwtO8v8nNhM~xPڞIڦR{5{9k jmZ|.1f{o1/3xIaGi#F_n($h?{^|ne&/Ew#0[Ҁ8&aWKS.vI}%FuSѰ^ݿK[ޏb;(6xiK[M"~}/V1q{9Po3/}œ&~৕q,1pywq4غ9.r hj>ޙP9e ݣr;vnXo8W ߨ$s f`Kl9cO!<زcvgHdb %ޠ3z鷎U#b)pq5lw/ m[n>6v^}lo5>49/1XߞX[[l oЮ'=Q`Oz %&Q@uLw(`Ok(y# K~QkvPj%fHcGzO_Kuid,';'_)(O j6ųh.^Y_$r ھg7fn3k7 sY߮iڧJ+mupw' =.b%lD.(|dI1(=3Y@1(+^~wW+n_W>3yP\ rZ_)9`{n?8?1Va]EW[ s&[\y@i8f\$Ɯ}< tp<"]qIg>d[}r;)H^د vtU8D `_p{ [ӊ=v=s 'Rq|,}j7.7S||lZ[` (o۳/~}6|> Cxoz,WoEiYK-f˸G#_/g~O/{6;q5jtJ}I'_Z%!fPniko`rlr}/jl5YW a}]+F<~1?[ 2}-*xֺ vgyqf':k:#Wpߚ\xľ>9q|<+z cMW>Vb(7GX`c}aEВVH8@x\@xr# /8W;|8:_/8W[#`a.6Z.Dq(Kbй2~?zP&}qjJ+ݏng5; .ep%3o3;%vy-sPA=i~5دUhcrD/>+6~jaZ?Q^߻!jF3B؏s4fm&-~oUG~>_yi@|'ۜQ !_|CĨ8+v-K3_ڱ> `~zOj?omu?gxM/׈^C[lݮ/C,<v)1@s}~r 11}y@`1M_!h}m[mh[ݎ5\~o_})+Rpdۜ[uwU P1̆~}oQ %]c\_xv&S!4K!k_ GюGCЏB~ iShۏWNgOU ?Cs;O}]r/`_ƨP.;G9b65aSv[ks6* .ڹZ۟cXd|!wыΓ͞sVݤMAGr\Sߤm|[ےy0 v[ùz'vn7 +UM9p40jɥU4_h `}֊Ic?1g$zg5_'\#-k?Z?*.^\G%Cp;F:I}#~Ngp/{f~pc{y}bK)~8HϩԞ~\?Wr5]W5;_Ͻcmϴ =v{e/Xm]}oy`~;GW 5s|`)/Ib[y|]}.:'x⏢=G+bq>f?/t75<ds} ?d"Vp:A}=םM+ ȟ< )). |wX}짊}Z[(9@GMSҗX:D .Lg8D?Vu U0qáq@[o+ ak.+tw|عC,>p9'8v:5G-kOE3mmgklFԋ}/b#MfuOe@8^D@EpϥtNHs}إͯ~-]``:`hfz&qϿ}%\)~GUXSq$$~d'#1N<52pG̿O?O9ط?? C-#%?W?8 m.9vqnujߑ}9,,>Ԝ8Cس08j7d=yƬ',-q୶m߉/o ~ xo  mI5K܁lSC7$]y~PYDb#ְ60 fguy1PG6>k鼩.E x1rhXіtr5hDb. ?b>iOÎA׊NqVrm&>[qۭm'o*!\V,Sgm,Nmtnv_ ynes/;6st9[!-s]~+hh, בwnpgH==?w vq0Y֐#6 _ψOO3~alYi[~^j5^_guzu?.5b~Yb)|zC=-O}0{Vn=Y*Mc?\ڈzڊNL|^F] Gw'0b5pfdh -|blv&<_򽐺)^G_IxNz63vnwJQګ-Z_[ޟV5xJ]EGH>mrt:r L6#(. ][]Zb4Ӻ(.XAғ?I 8*s`khKw[С`3u84}iEGl?&\,1!4phk%zoB{w⼮Ǡᘀ(|~A3?!Լ#zH1'X}` l}w|2C5kH6oTsp"[ rrC}.`?j`sj7Z5Q.t{\7>Ub)~XGod͠ԑlc- +~vyp}qO~yZ#RY$g9'|}8Zex~_K(.j{k52_{ q(ۦ|7j)KM[vv9mzynߥ57K^lm}5׫hGm] ]Wh~~K? ׵](ށJ=N0~tnuV SXghCA+Mߪ[ט,Gϥ8wjvEF0nZ &ƾmbnS-WyXk~l|vK*xElR5HLm7,p p_?c?a8H_Z>K\GjXinrSvhL`hX?koSN H]N>9\!,uc+nGKMf\֐35>P j5(&;q 'w&nYom:/[lwK\w> W~ v.sBc^  jf~fgncJ_#D k(Ͽ711Bǜ (yd ;v Zzy #R7b #R+RׇZ 30]J8plF lueJgtE;F#_{iu!-n9cYa?0w&p<\'-i#:@ oPzS?^_-`'h+{m޿F%v5ٮou_X_qMT~{O ]Wz#y pB0"#G>xh-~Ks!xl?0:1XCֵ|R^c#L5ћ5"M7g~ X:u`Q+az_R=Z_ ߅Aek?sPG5~}+D?;1rޤ5$ظ dŧ@'SY<K U8Hs8QۄzB~?)GLyT kد}\wwzv/z2b|;k%3aYnCye>Bzފ۟v6c[9jwSEc`sг@eM#`z+:~G;z_d8v(Z3͂ :XևgF9\!\{1B9 kg|/? bǶxvr=rK< :m}r>PJYm[Soot:֢u:g ^itXQmE|.A>;b?W޲/bqR:c =r+}C/~cl7 6="CZ'uZk'QZ}%`])^u5mN)++9?ygVWU(l~};WXC=c?W'-ʺdGm/yAkz4V^ƕk‘lupmg~Cwp>r엹t&_a uul]W>}*GQ̝*B#svf1plIx| g?VeH6Ĺ{' ~-;}r4H9~@&?BQrVX8|NxC|$O63S@ ]3= bz;L|Ht,ښE-8;Km]\klc=YM-"&yB>Eޱ)ékD@Xb.yQ0##_!ڑ@`~hw^[=|GpN~ uYO_ͫf53'|t! qφ {>AW86 |M6&oMtzB?\{kq`p=3;ۻflZkkC>ߗq FӈN-V0_۸8c[纓:#;ڡ-,2S,Xm|37 -^[_k8҇P˹ SK{Y(ۥ1)VG|Ώ>].w[m_YK 1텟ta @plk1:X -hNAWS]OnLQ8~+twYyf0hC[K} ?CPۍ5w+|=ܑO8?y91|C%gov- ]ْw,jY.!s62! elÜbKOD I=;_{k $|wq!h3.fkh- ϠNCgݼQn^icr!#{57=g?XOwAm g8,Ncr ޺j玟SJc l`]oRR) qڂqL}̡^#f$}Q2u|n?w{C~_ ~z.geYyWNW,Yߗ|[~˺=_r5>mg^ȸQ]_1 h1gh[^{=~ 1>``|r.dgYImjsfH }㳌=TYРm߬7Kb3sե"$t 67ZϦ0Klވ0pl!bB/G9w<yy8 /8;IH>^Ӻ_Y+v(Ϡ?P_qyUl@ayqu>7{9O.p8@=)Kj>M0Nǣ\PXlj<ӳTncwyܭ4H̏ucxz ﳼin[ܷ_l͜u mcex2ۣ.$.wzeݟq>aܿwĉؿ 'XV؏S^3NHO ɧOz:—7.}"̕&B@cWmӸ6neݯ#3li%/4YMGM!n7{jw~ďwđ>iD@˛MlL0=~^Q۾muژm8?d zGB^? >ᩙa:4 Ny> j ) +y;|7_ ~l9s-:>\‚͟Oxx)n@ +(4pk^gj-_A\Y0ڽ\eIv:)u&plsu =5!D:Iw|#\#@1S)\ \h,ܳ}q=uu|iǜ4[DlV<xbv>k-ޗ{F;ړr!cS2^~׃BS7 &_/t⿵~B9VNFmD -S#?]-m6+{_}?m~=er PĨ.[Kt}k۸KulcM ~6Soo36ϘOΗ<ջ/ƗRAʝ/ I;biH׭f o_836;5#v? +#3=-8js1h-1оOf)"wu#xX^#F0Is=kxG@9Hx?|xg|g<.N>Uk۝*͓S5^vMX.M9/x@u _9/I8cX_T|/kg~-z5WPynfgΞIPW^h!+b1riٯzZcW J,fL+Fk>Yx% r휴7YBI;`373`%'78gf9wKzcK%oEm/@۾X^>h! O=yg|M?[>Vx 5.oq~,&D>ؤz3CT_:Pɕ Qcd pvpr 2K C9]~wpq'h8ǯMcռN.+}_&bH<\H[_=<P[?-YQMWq&aqaF)Qm~ C'uD$?OߟIAjVI? |yF?=3sMkP^Q^4>}hZnysY&5wJ\M?zkuʘj뿿#w{W|/˷,hSRGl|3Xvaܸq3Ul<{k: ~\XҜj\d{+n; vO fP\^;{6XD;u,:KE}IL1R?|wV_sAB!_\ֲc`,j⪳u n.m0֬&?g=e/tͿle>>|;XujkM8j;?~?pRl#xtݓ 7ْ.SK=mcΝc5ZmHco5X 4P?f}x>>ޑo#wy qIdo~c&wOu~_}rؿ%?"n>ԫ$=bqqK~9Kjp?4i,=c6؟v&`zwW^ٵml^#~ΞCHHo"&ɯl9 xK} ,y<[gk|Ơu!Goct<"I6C^%_fT#qh D[~]s[kkwZ.ja^|I/W;ZTӺo{nu(G%jn5L2Ư+k{36g9v%+&W낸q/kYlkl5>5?fqDI^XWWcjc{?*WB) }% /},[Zg, -./ y׳9ҵ1ao`r`o;[PRz#x~3jY.q߮g_k\I,a=ʽ'̟X{krq{[3+sO զ|9G~/'z;Dgk^n_1˵Cu,WLvUX%6OI_&qjCw naO+@7O-jghڛu̚1rPp]o7|K/x^v1靖_,$*zȔx qY "X)`XeDܿ;#~cHP `wϟdO#׮Jz)6gh:ij HH\޶_z9%cZ۟jU?^wl5mm-7ߪ_r[eRrZ\`N#IZlľv?pK?rS=_ 7;niLHʱ~kg/Pnlj9-~4V|[M|6V͕1h]wx HŶF[S>6z%nM8M`9O \$o~O1 Q;G_}8XO/[h+`OL!_`ԼԚ(b58ǁt5hw 8v3ޟ%^Г淗!|XZ['ʵj?y?>"p!tΡ/ZAM̴^/`5hu+>nL\HfZ?xopyv^dž}yTk8~crT?SG[=qM]Ow{_[4HP݀o̺͟pYK'_lmr2W@m~kv lA~[Xh,l4=oyOO\]߆zӬ~*&R\je#ޟ~~/.uO@Cupl;_DKk =@Lm#x?Ny@#f}c6@<` 2W?aߑA_ |g֮1fC2Cst/lY ulۚr_pq~G_"_nWwh M)|s)8Z>8ۉ6l?-l,~y{,[3?' sOA(fyI;-\GgMA)Oj1/5.9kd#XtwZk8׿嘿ay#nwu|-RgsAq}YQsٮ^vY?wt[jZb-1'>Y(0#Q>{_q)'ixsw[]Nek [.hs53=nڛ_pU7nOe+|ڃX:mn𣹥ΘgRRis3ʚBv*e\Ce~VPX[n1\vS(y# mS dҊuU͋?pla?Ͽ[_vl1Vbu9rgΩr>OvF>~l/9e8VՎ3K_Lq)߿3J?۵;.rQjSg.;ePGøIl |W['9_(ä8;jZcV^6]:xg /4LkpzV ˸D[3 Pg@v %f At?c(ޏ=}(V0gwx{ ?Hq@Tx9`xa0994ۉ;ͭON QC}om"2(X<3.y6c_-(٨zv-a{786\gɷjmK>Ky(nj<8?~ ވDu~Ogp;7-77@9.׽"%i<6_vYG.$Gԇ@/jB~/8޿}'_q}ߧN:+g5XkM9S[gAwf\~kospcvO]vmyy؇W1/go.#oxoYs\~,:@5[7h`G9oF6B5^#8%ߎ |G7TgXk̵7fkL h\ƶ+M|AY8GX}B:Xx"~͂hsb]?ӹysE&^߸0|\4ZcUtĵ%ޒ5G'J o%o9~>uZc|_eZWqx}g$9r^ M63#ϓl[}=jX|w''ܧA>?7 W%A-v!Rd zPЮ\8aP*Eue..$E]^ؔc,y2oCoɗ%gP]^sXD|<.E5Fݠ|BS~b:|'7žl%bZ8ns/ @ w|CãJ8dKOm*| {י ~ȟ$ěy`~-PsKb|l/mx O8FÁdw_j~:o.+i&Ozme~/%(@zCƢ+9{_pr__%otFysROF۟0A%9S,C|6f(Ւ87&asjv GEd˳ 2a9v٢A<m+xԍѦ~| د1 +O`c̽3'WW[dW::K@ByV}n#\$rfC(+YS.Am9q>G2]}uzgi-@gam s>Lbydkqz%N(;|K=|}Ę3 .]ߵ`:ㄱ,as ؇.@|DIF `wwuѯgދOM}vP u3hMgX @AÑ9ĞcGAE]S~I q\rH|B%+x6 Ik~F.>Ͽ]ͭx7ML_u9ܟ @9ØrpC<PyC 1c_p{. |F{'ڽc-)D߫ӄø%n yc*#a&k+Т<ǔИ>ۧ|l ?c8 .2jcFY0x^Uxpr-cżR'!t fQ/B^_Ȱk 91mc/qgk8[6QN otFxx;vAlDv }BRň슏@Hbf2<=g1?-+`9bo`}(9׿~#e\U.8݋<Ƹ71%u0ǎqlEv[;-iУy)g?mg˷3ގcf^rXh O6&L?C9(/m~2}߁]-[?y.y=s~ J}}EK~:T) &2Go;"?/ @LOsOgp>jfQ38"Ya| s?`= chzBm #|D؃~=|A?]ϖbC/Dcz<%:3Pי aAt yk&k뜿\-CeqM6!M':ߍ>tfu\"h&]@9Gt#5swX|T dP$@lryOift@ 1<4&2K.Xpڙa}{ 9~suŶO1S1#?P q!֯!ߣ?A4nb6z?u̬bUK92ady2ym̑ZAYa0SRms]Zg\O[Wi,vhovwvK<*琜]wf@c-vu_r9 ߷9S}y9[ {|~ΦIMȇW58??,?vJ7: j~sy>st` 7ޗ8&l%bԂl_+ϋ^$벃oab(F~ 9} 2M\did}wOJdćD8׌55yLGMn-9!5ޢy1ϐ| WeM^>$ҁE0rq @umA](6*y`*)F^nrxJG8O늾܋%^~_,<+޿syV+uA~6߹@#H8ɿ nqVЭscx]S?ul~mZ's8ZʑZ͗aB}LH,S~(Ov,"+6(#,~za? (#>\w%3V\=~>:=JLZ|O2geDlߟm@.e\~Sޟ91_L?f9 S?p!U%YJ 5﷬+㈻P`9Qq !vpl{ܲn%hcѵ~6P@vpg빾퀇?U Sk]> (G!:S@M`b#4;(Aϱy7l=PTd*0?"e4 Q澉O˟_oˉ W}cc*|Fe\A1#$~KA)GG>'ԋ3`C dߟds/o_?0W!>u\'ÆȠʲ@=.2UAnN4,E9$KC3l/} u>?Y mf2b77^ X[>5kqNޢ-@*Z@0VWݣ\>D.WYC ߧN~>C-snWIg}z<*D\ 2 (U @:027УA:G}톓|@ߢn 1VoTt5wP[ l}r4W\9(֖06f=)Nz|Eiw3: sp}5jnQQU(OS?*Wz1N~&;QUl2W!K뷵mL}M@=(OM?IjNg #_cSż@[ j_1x PE3q?AϲޒeOSTߗ*  {|m;r ըW [ԓe3xu dtX!@\[cZ?˜1ÅߩsR7dqܿc;;WsZoem:{]#TG~?}&_-_[guI!ӌ G){VG>Kcl%W h~~V#XOsPew5@[A9[7}~m<^"Wg &zZcsfs<-}fF.9.˳hslޔOǷK[?o||qʟȹI[xw>lӼ ,X`ҳ47ܠ} jt.Zkx^$ڏXիa8QP#u{侠wPc}Eko[=g\0kSYBh]bYH s'j8PAv'yy!{Ndj/crkI|@J]`flkJ?` 6G7ܞz?5켿~]>RW?zuq1C9оAy[ t6G~?M2;ű56R.p|ZԨ.FkdP.-R~m-4O84'Fɏ@YA_O}~韫S"$s//{x|<>> '9kd,(ږz%," ò^{K?3B|?^\\@xAV_=/ ;ȭØ_dVe-o2?r5n 9O9ҹl1.VBIhKw9Ck౾_(/mN/do]C4^^G!އr}{^jvlPO5KP(_-W^߂l>e\h'`ǹ##Hso OmKg8`:d R [#hUPy*ceo5ל7n,g,7vklnĖy;_.h@̺qpFgCېuM{*{cE~xO69˘ =T4v}{|:I†1O67=Ź}57lk/Gc?6v547H1/&ixPb)3R{>z_~?6\=Q}/9~S}_\"ndJyF3{_JCE xOu~T[>ǹyPY>̹(o7[^msg#9S-.݃\FYq6ľ+S~g~kw7n|~;и%`@Η5~w3E/r9seK{9\ORK>;ȸE~<@5aX;I6g/ZrZ} Ӿ_sUa,DH9gy+싲_^$^2-Ay+ ;4~ Гkm mkr48/q0/&//MvK \R)؛A.R JG`L/mkp/zPmfh4QƌlQ?-s_ob{#Snm ^+5NC83͝rm1Ƽm}~<;x锣d v"ym$AMz3q4mE Ylev0=;ȹ+7Rjx wm𕵃 [w.T Wf漼֙.şfzǴ~'5Ȅc6/ W_q ^a e^IϠ]O89GxI=~8\"$i.\< HH${$'>=e.iºAoo2zAޓO=&@jy>ٵDfKb{cR@9\kJJO?ZɱÅ1a} YM)?P}Ͷ-eL̟}16c<6CmrbZhm8vhS 6 o7~VxEFҶ7\ \좜)ԹFI~I ybQh[])J1;S@{xaa>9@`ސUe .Tp44D?{X\zVX\ؠFk`wA> =sedeW?k֏Xa^ @Pٷx7)έS uh[X]ӆBj}Gp~ZrN+<.3|X RFZ;$^m{GHUZ~>ыH!/Iue媫8O6KP<}jPm 'ǃx|p tCml/AMcӸ&,~EN)^_83i89y?3 =XcH/#gΩy^"~mߩbi]C}?Go|ؚj,sg쿩9=!rXw?P跱]lfqõ'kmok4Om!{_V\G}}~.Ig}Գ7yn`>p]GH|swkt>kAˋ3wv4O("K{=^ǀP8p##j-z8DsFoAiw:]h##7U;8 m5 p ه"~>x*Au+'*ZVk7]7p9Lֿ {u r/9kX\s JU6"\7693`8&Gam}ؿ/w|I,L?ΉXyo=sz=#?s߯}tVD7ȹ9OE9z9rToY`>?Yje];X~WwZ< $PkwKSlqo=>u?eߓܖJ=[^]{o[.gP٘_|.p隷_P="E5^#G~Mm/r_r>Ml^ Tٲj6Xjl86zn}~d{m_qdS N^XR[CϘ s~_=?ygH~Zi8 yo6n.>Sm/̼מNSAϧ88޳l,kN}yl/LyUpx>zaA\X,\'\'r/QҺ}#. k #۵Je޴dz0#sK#Gn' #.[XPm}'1ðu?-*eao6`Zcc>`ٓTL@c }~l,ߗ:SS $jWynkYe> Ze\po.yXk_DŽ3>{)#Z gy0fohߐJSY26@Q E,bZ'_q=;墓z~`??r4OG@~&mb '@rMA,SlSb^^(GRG3:!>./a9YckSM@8u%Lg?d_h+}רh{*?yg#7hkҨp{~ñ{养WQֲUsŌyJO̒\!;{{JD׮ qakeM?{i ՗ᜮ#{v6;v=JG86p<T3X.M0y r aڤ1Okk =.pr2LRu>תϫ/?]+t[ߓ=fY߯ƺ/N6WFk<smb1X햠S'/ҙk__*|Bf.$>]k c܄P=ce,g@E6Q>?D`+\˳}1zYW[μ27~/{~vN\~<{{z3b?O3O훞Zy ǾZM-Onaso4_@HM(FϤ?M-NXod˓lju8z^1Oy~?|XQz_ŧU6ki~_Sx霚H>]wZl`[تCs)WxޱsƱcGryNY{蜴~Wr!nJY?M7Ҽe}lZ~AdZR- qh_{X yA{@U)d㌏ n9J|/`N:fYpZs јʞ%ٖ :Z…@|;h3@sPqX{ulӯ߈b_uI,z1e/ke_Q ;6OM;j(uQ^:Ӆm:A]mw^ખOG9Vq&߲3~G>\ȟ bGV"g`|əXYgv<]n=eOBeOY1ꡕv ڛ=5Z6V>S+ kXgV݋*B^չ(k}eC]rZc~}G?vok ҸͭW*?v/ɩ_|?&V]tTޟϨ/񾁻B?zKo[JFyR=Wc̗⳵;sc욗bmkq~KH tX_#.]n<<+p~ha)`TWP%=t\ .k>NPO[|Nc#1zoi>(G6@P^Q}E?:'ı_Q^ ېhӾ8Gs7Z 3Gpԛl^+|Gxk\rOl',0N"@`صIrK9ˤ?0\8yx{Ec=̤'/e.l~-RO(ۘ`krw>NVSĴ&߇m}}>n{r!B!B5ya~XߧX>57mѸED|RWż\,`'@?o՛mVϯ~^GY;{)N0@saE;~{ 5  l9Z~I5'?]F3 ua{Ar'h/ab m75g~.  iןj P蒞)s+e'Z"H jr UE!qp>ⷵ8+NˏFi哝e~/x}hthR>e9'/sR>lkJA˯;“}BMu:O㚞W|; ]H`f }2"x{x ߏp`@ kY>-\E_gsV:ڀ) w\#zz .#kq>8i2 =u+jO%L1ncm_ynob}~t\N cn{|xfCq|b^O+W>=;c%`fsQ~K窮ABbI.X.ǐ- V ܽx[ 'l! u 0b|SBT/@gg ÑgO/ dH3khΠX"o-ZO5ETS\"fMs,?5`#agƹdO1rr+.rMt ( h {HBQIƹ\T< 8wSNYZ4_dk?9P\{AdWT1'% yng}o+??8 )X5Kћ&O6陑ѽ+6z<+|Vgo{pNjj@^{@wv\C6>Z6x#|K7:cFh/ضm-)ٹ+,4tTߡ/AGC6@˾Knx+StRou2&C%^&6>~ B]55jŵ*G9[Fmh+ĊR6=y{IJ(}jzEbm}[i_|pi>^U 6<&͟q|@]+3&w H_Ǟ"7@?2krڣWlrOINuO>cky־z}^yUnFa]̩{ l5p:-P@{J>JBo8@ X@ >S61 ?mfZ?PzYR6~m?ycWԁu+x];sn[x;o\k >^[-V)gu읭kn]ݵoZ'k_yo}A>pե}:*5*F;<~9W^&8//ȽKv͙y'КP/P*\߮;X[.@Dܩ>ww=b>h%u1_p2f>ɷrllXrvZ%|w?GGQ.[VF&{wY.@e<Th>Fq s ^F<` uf|1i ڋ^'z?c4W;@Tco5&F,Fx!`?'pč{7qp_Q#v:?oڼHVzK,R[7u md-OWE\!4|-5G\$[p/{=eB\kk\EQ{{SG;O%1@=|r@}?5;qw 4EùϏ5n}-?1:{:5Kwݙk$ޮϛֶMW/Ns=b&6Ɩ A0§Om?$O5UYrb=ο>Ӹ&ϣo?_L̄񥬨߯1.5Y< tPF>֧E,d18ӣˉs!5TO~xN`KJsk3z͙P?Wjf[~Z=PBQż߷\gOoszi./N.oog94\Y\IcYͭ361tlu;ԁʱ]J@!"W~<-yɱazY_^ˣvE.ٹ'=Suq9gz'pQ׸hu~mGw9k;ؼ$uZ]W窫6m߶h @a7'~Sc.h)w.5­xȱ >V^Yogf/c7_@S݀?`~}%5M HU䧍*(ѫSl\œ|Wxso_|KLceE ?1=&M봝5j;ɟֵ6!es~5pRbu azE5!?Y2L{N ~Kg2?[GK=+:xк˜%Ї-8O Z>u rX1O^z k2wZUWSy5S Ng)V}P+>$x>zoC=OgG'cf1VcbbOwӔxGFi/p?/`u}5/5[߿K~s ܧAgTɁq:u|H78gyޛztĵ}O>=:q;[_[ <\4O<חG?ozy!$5O>Z{ok59$Qx~~@觟ZܮG Zp/V觌uOs?cX~whWl_Yzo3DvFs,{sL }&؞ؚfC壗 V_;yh^j^\Gq}5|= |^vi]@'UBs;H9!^<`4=nn+{5ϿJ~?+H>?)Nu3Įb9hz~|*U 8@9-`eFBi,`.7Nm |y 84@?GdXޓ<y)VN5L9ZmھNjۺR94?w\Xi}'9%"ڄ|?88~a^^>_T F[b>B@ƟyJ|L2&=Ԁkx&u. >504Wtg\4z"&{<6@c${=Q&^;X|saW}ouVg~7)&ds85s/lq@aޯ(0]RNq{ g|ϟo=>_%A* C^YV\ Y-~,(KS8=_hv,=m8Ci.`gxz\G}B+`tb}oS_[oU9k^]9yMb?۵NSWG۶CߟZ #څ|wqKkZ.c|R?̚l3Zt+ГP2R G::\__і}{@Fw[??I$:6-]Ir85r'OymL^O"m1/~ZL`.9`9‡cW&ym*g<^c/ܿpv^ 5z#GAkoZo%.SBqlaO()!cytNAt76?0 TυϜ sK\#׬[YB~xlnFa?ޡL| eq1ߧ Om_{|ǵu?vޗ|[?68|@zK.$@6z_P (:Ngy[F9 o[x$I#eUeeq\rf0YD\Lww/[6HpKr ܲ,,I -?gǁY>s]?+fYss +C뤅Ǣyg<'P/rHÐګI]}A~ﺃ`HR<Ȼ:ӵxlk~Ofާ2s#ߙϦ23gyJfu`kj[<涃o%Osza̠M&ЧcFxKrR^#\#~ux oq7Ou-n5G|ۈI3[-_yh_x=L&;8;2J=#Z|sY{,i9os״mkh7:|?( ,:(9EF畍Ik7kgwW>O5T9>g |įqBYwrC?5dse)kGknY",OL|KkY/YL v6,pm:pq3 vvL5oQ~lF  >Cz/Uz/Zo2aDkO"G緟q3.rkТைJp6drxĿ=+ ^p..`1øèw*͹Hsdmgxhk^Yi x2 }ۉNj u/?5=Mr,3^`驂:O`*oYҕDa9w0&ҁF3֓fK9+r>K9[~N ӑ{ CdeKB{fwx y&V7#l?r\MEp?E:=ڪ`(?wBsDb-?o"]UdfI-Nj}m>^~we8ʚkT~^WJs-guukk,ZSŦ]_Rx~G3+WcqNb#)x?׈辮c;M'=:/9* X/Ξ;c KCy3TiIq?Oԅx5n=]_FV=4^^| ?x}EV5z6>AS쳠} }k NV%~5NR.|F/1w;\5~ͦ ۀ@~A%O[ >)m;co#ۙg;_O3&z76_ש7qܯ?_i=멵]^ܽܞ4'ϡmmsy )ƫm?ye Q`cwS쮋t,*wж_SZtVu 8 di M(. } kN?pq?~L5nFP`~#Њݖ}g:r|\NdʖLE֭}xi-ms]6hq~^NN(\wpMl[tl.6[֗GMv}>wͶº^o69 ;ZhYw~rN9!7}Ș}c{uw';}c' .=j1 b#:8/-OrF?U?9تIH\@Ibb _tYlEWZsbǠ3`ZECKgh cr~xja,F63IBykB?{9؁/ɐ= 0%uWz]3)tA@}0X:luZE_J0ctɮ;@*Xyf`&1n> F}Tu cxȿlG_ߤR4伃(3oN&'k}%9/~]:qs95-sbu^-?D([:/?Puݚ<==60ER=DmE暵}~.CM=o*?+8}l| {%>}kZx:5{pFbs\ cca8-?9v_Vڤ0ŋs||>1~UUV>|b\];f^+:'z{{y}c?lQlI ttaogٺ/װ#q<awwqբpkUO Gf=SxV4 d|i17=L9C`O=M`7PohM%Nױ]!`r2,P[,1ש907A_8=m:*N(7B<I g-\ Kf7r<8{.2$k[cNouMkdq<2ˁނӉmαϖ<qZzBNU5<@ynj]Gzx_O6D1iy |*1>Gs/v]O eGr 6(dxNCA?>Tؽܒ3`cpο[=xF.K-Kl稍<}I똺U=!m/w%L'= /7 n+mt}ma Đ(7`ziF|s\3o^OjJnyIBRcPq:ׯpz::닭>לC40-=*yGa^AƫqK- A~TT.9R6*]j@s{2's]Is͍]hg%ZTĚ (8o[Oa}) i3 r,w |2 7ݣxh`W(K2rLpZQCzĘQ]C44qSn 4 3hI9ߡl%Q9ezvvbl?q|ѧfBLx(G?Pk|aS"!ƴD>D&xNKwk7JL,~`5<=-q`B]Y:w|=409C wDqVb]_1{p[AH?e"?'ok_[1G?1w'O|OX Npk\VOzɁnϕ\ǘ[(z~?J d+{Wh3$B.U #7).?\ɼ@ug碼퇁}~;Yh‹= lؿ&ܟbYp}Qҁ֐*)aN&;5fmE%_%O=GP\z!Enrx)A ה3D Uo0ޯډ)o ctkĔEY.>Dqo}^p 5;,;^5&Xq }c?jc~!cv;ט}"+qZiyqy8oby:7}xw<[fohH_jl|ÐZ(7ܚ Yn}~l=M|ä'a |@>@6c M~ v H&dLrm7o˶{6p>u ,SW{u!DZk/iK9>ɽ\inc99-g)~V&@q3 pI"h>&fAHyJ1!<SG*1Fw6qxNto._$50}'ޏ99Yǵhg%6[,;Ti>xY&kϾpyJ~Is$u|>72=N?ko%wGR9L dO׸5d w0_ƳԨjQM@N@knhE#[}g1秘e)|9O9|8@shq?yK:7@bqAiԗd5)F f|4ո`dKXIs|m|hgBLGFӂ6ߩF>nD}?=K\K ^f=߲B _@n#r/XYR<ɛ9K@fȲuŗN~E?c/V,Y", w~{O4Z)a/\GF`F9OOpzL9יJv<|/^:Ovz. O?jTd7& nϯ?G -j t@l4R. `;^0{ @p@%X@ Ωzg?=rx;/r_"5bA#5ggl‚k==\"LD[/Cǧ֫󟂏fu C YHdx]-@;N=vC?[`N0tᵐ Gu\>S5KP[5sl|[ ;8?&QfqKlZ,XtXu>Flu1Yl?4uy{s^Ob->C,܏{1#pR?=j'S~*d:Jcclq#yAy4 z'ǽڔI^宊O\y \O lG~;{*TPO9uԂ fƺ0},H\HxVn1/DOȾ[8#٦smAIep9fs3#['3{KvbTi?`~;na>>avz zf$o^[[K7fma«L>t̼4O[L_r m7b :٢ceTq؞q=0^yOwO3w|BsukRmO@gpv_WNIf;x$ߜ^-_B^eO̡c5ȭG]H~K4}7/NW an<7{%Kx6#tWRGܑ g!~!lV(ؤS9 t5:ś{}c1 h}$ @/\O aUED(H:6\U },WYU+#NBz~O20JZ\{pd12 6;Zjww2}2%ׯzd[rj~ І7c|e@p;YߩPGbw?8(: ώS ;k_60y߀E>d,F7J hMOׯ7\=e3AY6W%3h:~Y+_03b ˓ \%'͕kEwWk܁ooc:.ן5]`~! a0}M= + ^/`n1s>Nz/`}9$k]隗dosp_׺toZcclֶLևs;kOɧ+rϏ'C^G=V󗜽OT_o7Ћ'jyG=?p9 xp6<׵v-3m0qQBMP+#y9sldS r7FlE8]r/jLw5=ï [=r+8?'?q<#%$\4GSԱ w['8z$Ȧ9&Ys/8DW<=;a_ Mcz)lÈ)j3qiϕxbg} yqv&X+m1y?d-T{!d_QV)7l/nπv2Whْ0;c(!,&AF[\y!>!4|SZ:e [8UUu1W9Ɯυ{b3ƅb7kgpa6}¼]MO׿˗(ӠwovW ~Ѱ,qmdgg[x}59\/gKlT{ThYj=چ]O9u~Nr6Wl9{3@yL= Cqkؽh^cy3?b_'<.(:^k @baQ~. 1{Y'qbz9?xD]8)L_H݅j_g^8W=w>Y :Hdts6>}dg[v*kHmG_SWר{jw矙la{ Q&Hp'e/0qMqk+؂{.Mml1pM}`gP̿ Ʉr9K^'Nb;oH qOkGb Ժlf8m@>6G=Ave{mo,.blzRl~;cMp00a9X< -> }ݯ+_6p1f3|lVXH锟1/0gL6=|\ SK[;ru9O_U#>eo \^ٜ{&Joy\1^W^qkN_Wz (\B0Vp8g7 0c[5P/b ?,+b9E?Q3~&=^{u.yw$z~Gc{@lC3Dsб"5@hˑo@i/>M:pI3=E9F[ols0y-v/w`L}nz'c'@<1/0QŖ{4Ojzz}5^>o3OovZ:/Ӻ'C~@'@=}C[7*m?9jo8̓>`|?&_o$؁t7{`m7}=ʏն_㉺_?rٗӜS$X'G\-C]3s٢`ʼz/"y"uݦ>0詾 4=}?MVy^zaSzQU=~b=3Ѭ N;?R,LrNx@, .狅Ck @SD~ۦyGfXB8P?K1VMwh{vSe;kys|R/4V/Z|lQdOD$XϿ4Ǘ)/,9p}'KsigIs_?+iLK>xM~I9rrB+3L$Y\Q__fF TX[rnsBmugfoI?xxֆ}/*xߦ*{aOp㜾$v{}b 1&>1 @|!^cq/C97}= DH-@tC~׋!%y =ka"%zױ{$ rTuy'Yj:eC 8B. n (}x >(fLQoo,>6K?J$? :\75ägRg2qYi )nO_#W !8W `aلx64[7 宆^pzp/,X]~C0F#cyi q{.8j[s45> 78^[QKkK\Xybsziw$/5px򂚈K@'a}w9M;z ^p/aGqK ׃[º"a=dls/Q|At#:q߱[8}x5w),q]#8O`j9dy3gt}#=~39TԧbqѵPr/ 5qN^s~H nxjt u!qn>pqMu.-6O LtPz9^_|ec>Zt8g'>syK {zߎy<HZc& 䁾8 EBY+bݯ{0?}~>$g`V>+Gn)G~3[2S\.WdNx>V>c|Cgv/Bll'{BO q_j@񹆫O~#|Ҁ'3\lm{ʎs7/ݴ ^J `9RqHq-BAg't w{ ow&a>~~hO,bE6uͽD?@〺7  TsN el<o v~q8P| &%g{( 7cSeV6$_ȽRH/畗$~`Mk 2OpWy_' |:w^`̽_? >۪אWyxN~:w=u.[Yzte+/k>uph /&3X%YjCzH}j3}V]OO~~O\-qN$z=<E>M6j5}^'pu5\>X3ux\lsb >Kb @вa40SZ37M ab XB?y]F':ULI?X'ZXC˾>;E_={o6_aܣ(ْy~#6Tstm`o:R&xo9'3?v]zFNϽ #a^'x 4vpKkig߈# ?-7,X vop e>~R2' 6}p-&-|I_?Fe}W}$/T/9fo>oS*8Aj<&S@F=9sw<-Y(os>{#< 3ۋ+> l NJ6O儰4)@ cv/_.GL}]O+U#鏩cXs̓2'?K<׳:vhEeVr~qb8`lλ鞔Ʌ*pzU ĵ'&ӑ-<-(8K`w+~<0sR=C9AhM:a $'x~4+?+K|O p]V_sx)u=pc%+f-/痃9glIϴkD=GgK4_XL|+!_իpyył?}Ef^Cg\/!0 3=z㿹ߩ`y?8ayyv_ Y/P Wɦ{p!?L&'cu/L SeOkmooE/jH~˄{JPc356?,.Mx_^3H~uex_7PO[X|)5׋x~3off< 4~S.bY$fg˽MU!V3^%)c[$W|z8 Nr%s-46WH{k1X3Ts}A)H\bz6̢cc?"8cp>3L_?}.gϧouN3~˗~cN|H?$? '`a /k H\ֳy:Pa {z|Ou>*o)#uEf]Z)kZ u%>ysKL7]+|WF7A\6G{ozlG, M ֦K _ TפqOڞ~ 9f-8<0oFwaדPV`$>`>q(k{ZdN̵=׽t ϱ\s\/>Fw@f>`2 Sss;sknN3y&Kc? >y@m1kx eƽBϸ;\&fX}0¾5.W>.(=[gW\S8Fdk~Jeh :):2{Dnup_pX$ޣ]ݨ|}-{Y A̢ "0yB-9t-QD8ox<n`vW3¨\,SOgRljsܓ|_G0}%$'~?ոD%ӧNgg;~S3/l,ugGhNxL5'{s5_Aj =V7Z x%oW`:g<p|6>9ߜk瓛~S fR?-Z<HqGY;է?Qp"Ac ]in!b)ޭPf>S ?|zkzH_g?sgA_-{Fd/;3 /^P-f^ 60{vX0dk-sÏofә6<ٶ rA>@~""<#+p#@rOo\Gt )kw=|KJc{^Vt0Z Y?뫈-%ݬ?ig' 8z#;/{Xa%?* k9!\g FZ!ޜ׏ʬҦ1sDK|^+p `[<&pNr,gX|krIU?~~CK|ך?Qűtmopʳ،,鷣51pUy=-?h Wќ~0 Zac !0.?^ߡ'88E?1Ͻc5Oj\$YjH4i9-s?b73&e?g|>Aϸpohr_<]B!0kukx)wOuO%ς<ܷ{*cxMݠ!>A`Uh3N=(ff-i_z$9}s w޾lΟ|H55,>{{ʹ%k(o^~-;#3}ћNBdTlcft/s~^ՅsBZcpmA$O.qe|OLԕϏtbA%T*Bs  '<cLз%39(𢾜'5)*% ;X.pu۷s;+p}R<^z^3,}}L_sNb>` g@2`t=0:W}Q#7Kv^?R{O=E_(I\q_h@bpBwWA^ ?˺0|/KT*7YpQzA%YKFd5fgu\]{cf}9z~'{X`̟帶@eq =~,֘︹c9mdpPcu-iȭ8>\ |wPxƳkȧB-N jPh>޼I8%.\bz':SsygG@A0pwjEs5ho>1P< __OCܣ>ԣ'pv|q/xz[.[n)?5?;1?҃Y{{{%I\+[ 4^/X $(㉄a2S[?:kчx]}1ɛˤ&#z~:]M?km m0[1sN \m>mqqmg^`y xw޽~'܌aD50ٺ~[ߏlK\϶[%@EOX?>P-3ऒ?f:59*w`@VVz;й O^hfn\觍Vڢuc]8ϴg{{k rcg{wPLşk\U~rkk3hx>`f&if?Y>{6^`u#)~{m:CN9B/(Wui/Ю[|]x{Aq)fϢ< Ԧ=8$`}Rб-kpbW.t5*.%pQM޽+  \"z&$Q3s؜cyy纷$_l|o9 `1$j+ߛ }0ziDZ̙1WabBc3/R2x>Wan^?$~}{5_S}8Q/#=J~( ;q|bL0?~U2_:i9ˬn{d}|$ߎY<59[e\Q+v??Q0 %l'P ۷<3EzF}`W !}U py^[h.:nm'N7^OРҡޤ>֯z߅Ӻ- \pJ=J#9ݿl"9m%yaVÿ^uꗥ5PM7+ Nz]\Izd}+֙>U=4h/o=Lyl?r>6blٹ& g~֖Q hc]kb<3 &C蘎}|crz<@k!u=|Kz"}x9+ȘA11Z@30F 082[\7D"=ùM qkxXw|'apwgf7p~_ap}ڽOP4sObL*Q*Y]7 N8n#cUP~+eStLYY[<>S Ws2Rzo*fRLeyk9UZ~ÿ?S b8W/_Wp:FM b2s&l3\}g|oR9Hm>@~㷯8Ҙ]<Ƙ?}]}lFv~n_xczk[89 m 9&'UyFm{j pD[ٯirDY;G]bo'8ۤ/m[?V`ZK~:^R$R-W?;7O OkP>*gD'z[[K2hn~vO[xm7QNN?xrA'ɾc9M=ړ}!sdtsoLIϢ,wEϪ&h(s\[xf@{hZk08y8;^3 pTmfQ-<<Pp@|_W1iju `:hޟu? ?7e]ksL4~nx4?ܐ}݈.07`y3̣>|? #",)\^]%,GK38l4q1~0.| d]B=}ٕ{Z=%_(s0=M̯qzǛc{͎T_;.9/z4ǐOF#..ĵqQGhJFf1C^|WfN)./Г_W^CcV} b;_|%ڷoeB9΍;pzbF7܁TA7.ݮc|]S@/p+_a AO+W-|z@Esʰ'$/>{Pgy`TwHP->t d yC~G|><7 q=k=(Gu_I537 H,^yDtr]myh{Z&_81P= >bh @z6Yuo5_st?ow+HO@90CLqv{<߿?!쁚fg? {3pؿo]1]|?XdnAk.=_p3lSo{}z?A=bѧ5)W4ӷZD}=!w#7{PO\hd gMEߓyfO;*ƭ N9^´¨Cޔ0'(0s|E'[=A~IvM t/o'( gwF/#Gv dDb0㤏y9}8y/_+pz:l\5ؤcs &NJ]U5z j3O.8 Nv-d-6pw ph_LLE ?W[[H<gYV/<Bo IE5i`uvb["__8'(K&.a~*qZq7y>y6`uϿO} Owߠw0fr k\<7;f]-&OE^:GL!ƞ͌X9tgZ' y1Oc@,/q}_yN'Cf\S @=E~0o{nbw\->@9?s o{9r&^ӌ?sf<\&?sߏ4m eWH-ͺV GLuһgl \0 W,~3Jb?n۵)Z?/ 6j A_}3Cwc~p{=8 u%~nz[ Xd 3}(Gq9f]I߷>kEi!>9~}ޡ|TcHuYf jgE]T7b߰`k2FQđn9{Q=4fadc.\RCNrkw3ǘcY]vf} g;nQlzߣt]/Pƀ[%j]7Vq??T^a9>_g5?b,r |?05"l@s}k%O<ϊ7_oן~_>?fE\drζn7 rq~_hJp(%EC|[==ќ03޼-,>gGsZ1yO\>t`ـa^^Z{4l ~3;xX{h\y X`mysd9z ckIo/A A_[FR|_EphZ?!=88LX OXqOCp>+> ǟ6=9'`G9/ |/'׏)^X. Ē9}30vfo~>F\ϏΖ4/Ȓ>q[⮚< ^2(t=1.``C}zx<H+;0>{: 4T 1pZ>^Y>4Bxg#I&TAJUq9WwZbl_\9ڞ\7mODUh],q fU<u#>e Kt8+mZnmyίx|vO|?|⿇I=sXo7}9OUs9L:][$K)xu4)dUisaқZo I5:Q YA$x!߿s)Nsx>y_7,!^(J#^{~7^*WIۚ}Zgw/`<^5ܺV >ޣxt,5cWd#ȭYV.O:VLd˾{ٮc^Kop\a}G%gLP%=Q2̲oSCmZ :ʸ|\|kӺA4P vp4iyLSkdOtk$$zi-YVt-3YZ/[}]IMAdtv㗳N*)L^K?s\uk1 5 z7r_›7oÇ]8/| : 7a0?Lł1IzSenƿ쓬zHɭ\8uuϯ\Z!٧uck q͛%ܹ3 ylMQ&ֻeUv ,=[,$t/2~^fz%#ߩѝl '' 8<\^g ?hCDNн4TӪ}5i/-;} 7Z'b]KV%:°q%QWomoL v?!b:$l:~goX?Ƕ? C}C>җ+{ʊ> ֧HÍ |da?C4^6__Y;6k'4|}J-~D7[DGb/>!=*a[n0Y6[qWK~ojEk!u d;+x\fսle?S?k:dϓƟ% I<~ӇIr1@e_3K{;s݃>ׯG({m-HEe=^s4,d0e߶4~9tv^6'c{CM@75}㚘uk&!n%)6]dtfQJk%O [Ҵk-v|Kb;%fb|b8E6}y`? zx.ܖ7l+1HO'{fAO^C?jڌ>&n:8 ]Ύ[NpEn9P}? d9D](ϱ?_ ukjy9×9/Nv4h"p/dgfm\FJ?b @a֗ae'=Jޜl-%87WJr 1 `K_DO?I_xh`Ⱦ71 ! ֜s>O3%|t:m"ލ>,񵹏Օįi1vaQW+w/Az}C89rEILBzKSy٧ tu+Mq_k"73F7KZ5015BԷ'%>pM 7X:hvZw{F1&S{x? xqϏbeFG~/JㆁYiMxg(f> Hq ǾʴOZm`>م9EhGz([#_lV%М(mq .3 dۚR'8AlNc7pxׯ/Ν>\Ǜ:czj[&_r鶧5#kû?ij`XRo N pp3ptD] # kOϷGٶdb04>?/jVX8TqBf ōq`A7#hn3'~ Kb{s];7X~oܼ2:E܎ph9dp#ã )Z˜m˟ |_uyaTZp҃tL FPY^v3s8a\5||ufWX/͏Du Br z Nʌd}l3FgE>?/d,֋Y> =ilk՝k%8Av.QGʕލiErD^x̿[,A:7 zyujgӿNSs(1^[.1]N7weu=Ʌv/7kyZC7ְ6pfmlNYa'^^RuQ= x~wBXD݋HI^2`{q~Guύ`Ę?賤;H>c:2pxxvDeYouYIM q2N>@טǿ6>Kȟ-%nc|s8c\cX䶋Г^5@>ϗ\^7|_ }(ms+Qwpz*9q1]u}4GGVׅV.6u nn ߣ3+tSue-4u)Nf;gPcQ'?iaZN( >;%9w{Y?[6fA}fs5˲os1@A~@ kKa W_"][o?M|+5F{v~yt凸J'o8aXq?Pl}h}kLrDF e}fh?Jx=~165]&.uCp1b}ƻ4smOX&gYć пܹ͛0LVŦKXmh[vLږw1㌵9.0n,x1Q"?9Ioc\ v_͟o4oYra:ߔbTþ[ߏOr A yZ[dS$qqڶkϔ]>Mm}ԛr|-^"OX Ičk8h%x<݆:"nROs#[< 46;b_'| \c[\ËZX^TEQ.`:Rb#Er!޽WWl%7`%)IPB/߽[O`00a"q;"ߓݵ VLD]-۩brY``qϚމ6{~ UqM1 qi+.2ϣoKFxeypͿk|q͍l0^i} *c%@oY99X:as p(|[Ž*I.eT}a?m./pbi۰˯`[5=xY3ȿe{g1d<!s71'(W8zB+=g6|شk6>/VtΣig \4]m\_lt^8}7Xeŏbg4Pfm>bMx)09 B.N<1m.~okIܹOyb6ߎ97P?,em_ f-6WbrGT[gk芺Kw_%_ .!:Ml@?+ kx~q'tSayϑS{&y}4E_%ʶKOv|Ŷ;$ˑ"\unG0wS,mˇo$7,E}!;0kHр}ɑ'rlĘoukiǜYaeIAօ{O88}0Qw޽ mӓ5G8\79uqBA&z ß;1ho$}SUMJ }oG֜ud߀rX&M?qmV=.|N ̇ yTÌC:6нgO'Z] >Ȳ-ћns +tee-0>U@l=Fk#g} ٖ0c&Kx{h;4ZYnè³k0Ln+0x?q[ZGL8:bOXa󸼯`q&y/5!B0Gߗ :cJhRZ(\u}lcs֖d|@هYCQW^;tH5RpkܖK6/*x/N5󺉓Mt^_!ZM+q3N`zpMǵuˑ$!۠wkF̲ 粏u!?@|l\_&~wKZ}{%zU\żVD>|_=!Yݐɩ_~yz5 ]Ze-R7+|^dK9h nΝ{+bFu^^rm5@dj<]н2~꜃LZ@GxNĵ]; 4@0Oì~x__rΒw]u mWx';uc&dē;{"~gP+rj {ՊW u:y{h!(ğp n )X,\==={D3U %A]7kA;Sm^Ύ vcrg\ҿ K)eՖ0)kgJj+E9;g `ME3_r[:ҁ䨟Iĺj0<um8=K_J'h V!F1;ܣsMvq,AB0Q(W+|+3~'7|1UB9 a9l#g5 ~yKlIOɉ6]@ @;rz+kV  Y0|q>A7r[gx~&k<M}I[3hKI\nb*9FӾw |k#u]g2|^?_4YY P9:_˅9؟_:@ pAx%۸vq V9mf}lrǏll /gQ.j}>:{ao Jx%{s :Zn<^ќpd?;$`=R/sEo',*Cݛ%!!0kIK~RY&+9GEq|p ʶijd㞳/{̉u4hekJg\a2jC{8]ҟH.h_N&']AxѥF_ WAXl}Og:OйUS>3XG? 6&(^ ]c1S]jSZ'i= mz^G8[1py?Eh}{,%nL1Woִ%'p bk"d _׫5Xl0|X³faڠaؗ\|s|Hi>?dž׎ϥﳎ0I֔n6?"p~A0;kWիc d]*X;NRߎS4squ?.9<@y cDoov>PX`_g-}u%'w M!JOTV7;^:b!vU/g林98j<S~]8t j eY)GG)\0 –~gDݝ4:.*gany:>Rkuo/?|KYK.-ϐ0~0pba(y\)0//m2vpy$0W6ˊQΟjW; &-6c}>-Kk9z]@pwwz^eDdU|;q0<%tyts.<_l= u. o~εgu/7W=c`V+K Rޟ=viO[[_Cr/e[s|BDj 4p P`쿎B-PIR0Zҳ s?sq4f* _ @؋pf.y-ڼI H$8&x9@Eu{ 0~;p 0S((Ws@ E'CrbL}`hu~m7,K\됺8p+iL'Ѓû <ڄ[0 {KeM8]nR ,xxtNZ|n G>K'7(?#``t0qf')[y/<.JavL1*3O^Oڵ |Xپ5ZOm5 6eGX$x?и~XJi9o.߷LVp{]J wkݾ j_mm'\;Cs={OQ]gp"Ac7^82mڦn?,hspc)\5Uw-̫6my9I99jz\ϣ)ߵ$c{NAkc3ӵZ0ˁoWp"8bY@uA%z|zNzM>1 ޘ,s J?Gc@j6v<nNd[ H̯%$O%lON 9>#\ /.1֝vQupE斿._K*oDk=5>Ufг[}ܫfn5px }IdVO܁ڶ o|wN#=X>7c|jI 735x}U'(gelh `kW\^؟s#g!6KzIs3N3p_f$6\0%+pyvI w`8>G%6c0; `}}Zm5ߎyIxJv9/d ka]$vK|$(9r?@ @A0n?du@| gsrnVKX_e5PhyTTp02 MS5}{>5]Ǽ?{\7'4=FZ~]д ћfW?vGOp%^wN(WG~8OڔbxU8t7HMla} xÃ#rJ<Ϋp}Q:E(]C]t}~zFU z,0`f }w];6]b{C.zYi.KQlMX蜝 Sֿbwo)9O#p%"C?'kZklek˗ܟ]dO >`$Z ܬϧ[a e.Gb ?GdYϩcYlG œYNӴNi2A,e> ll`GdɎFv$/x 蜿C^'Y\\)v ޥhO6p_#˷/?u3f`ƏH1:~1 _dwv]hWM“>9nxF9X |nrQi&R[U9<Kc~ge-{= _?C?1WϤ?0=:6Vbx\/s5#SSyږXԹKX|l@꧆z>\Bc(W~:yKXac\+}F~A4G8ʿـzv ݳ)tn!-BIRG~w&:l$}rx2'T/EVM<"puVe4er# WKlocx K^ֹE*aKY*{>|fDRkh]压3sD˺7V=m7i6?o{$.L- wj|5dzuȱCף_G^^]/!{\7bnHuk301a1L~^=x`{6Ħu!\?W1erD[ &_}t˜d4(_̩,ا\z[ g2ҾpD>=}}>0qk|b+C96Fjc>AX_YS1>}]Jbx}WKlr1~<@s=.۟f 4<@#a>`> g^@ ^@:_x7wzXOZw: u!~6ϼnߜj`2Utf.1_)(R7WcO3~6ޕK )5׏d˜r۷\˯vW ݣ؁'@x {_7p gq-_\ h.A>nvZdx|M {ՑɵQM^p1 XFuԽ6'0k)}~bKW\ۇp^ Q\g}"[nç.=iM2_~9ޟN`zUog_#UHS>A=HhSG((3xO/ ?k@ۀ*-.HwIs*Lݚ+ !BJy#-h"y|F?5|yZ%&P4OΜ.E,.pKRv>Ϣ\?D3",1gNJËQGAVlߞZvqHtLOE} rs:*SxrۮљK>@ˠ =}VHL&` l5}s-;=~}h]h$>_߈~_U/Owߡ{`w쯱1<@ ':^{tVp 9Vۉ׼RO& pzEj-ϳ~l Yg5{iV%`kNu/y)c[%oE^a??y?Գc*a֕Om/VlýK,OS OU@-/ͅMNQ/#WSόl?R)E{pOi.@=}CXDVD0gV%Ế2/97u(v5r`Q6ʅH-|>Ԯ׮KבҶ/琾Ý<:Ij 0}rV&(6'0S)|]TSB_6~<FYW'Df޼N[vuc;1XyV9yAmӿ;Z*w'}\͠qe]lfAC5'~U_xG guCTQwkkCr:Ǡ{ж]=ouo@|m;3*ִ{myYbH=eF x|zQnoc3:ޫ)ӄqfp75;]?K|RF6OA ""٧hv}ÝcIdMl|%b$%>.6~3|9G4&,5cO2\9~aγ)_bjleΠ b\sk0pnn078U6ZuUO@˒\}PkDc|<98=5=) 3=/Z\\<6):oú-N6g`bpc\A̬nveuts]\^ޟs$PSq wYc.z?5Ϝb#-yeudO_wI_iEꪤ&0\=wH^8z\AË+9M[dmP=^i[v>9~(oy3.v d6uS,uqUY':,k>1^pvN^hnom`ey'ħcJÇS_gY.gEz N/~҃•6(Dv`;Jǘ{dbzѳQGn 1/?me\`H=m}Iz jX߫ak;ۀ&K*w1F`tNY Nv>muRH2)O)<>+pm4ƵQR=ƝxpȘ@Iy)W̐8֭3d<70iz?/A@l#vRgdVaz}_p@aKO5s+ST4#c=osg|sYպ^c_t r v{]MAa%9]q7_eX?69p@K=?X P;)ye<_s55+N͟ ?7~.d>ۢM?p{fJoe[zRk+z3}#HRڳ؀gQ\5zcem /5p0z;_Rw%?,I߯4y^ }?qs  Kz  w)@ðޏͼ%4О2_U1!16T#H/ S뿹w ׮^3=iW9w<~aⴘ{wSޡ2큙o(="ڋ*y|sðBɄz7İ8``9bEt{(b|OooevE#^'ϝ{O"\uy"jUcHߴg9,?/)Ukvdnb9sv|Y~~n7Q%g.5O>˘I~2#N8tTm }ׇ|adr9?bZsիsg5Ƹb {` [8t<e@ֽ5']n g/޽(OrY@|]tk鯰e_ @/|^RoOj@:f:SY|!`Sp9Kp?/yU֜/=[iw\bg*g'gXNϷ#ܭҟdsamSVd#=(o!^Y\磟Üsӊ/סpE쿴nv Aٻ:"1Ioe wv0y՜dεOM߽.EIt'fr:,q ЃӺ9Y !o~wwpu>cޔ5+awXfD]Zۄ//L?6`Yr=W+|6fx{ĀSc-?պ]*}X#w{\W`^;!8`mk7=#a%w0kxrۑSlkS\\g8Gf3?AoptLn:!:Q/Q^%4?7N%<}:t1|gnj:2_<?uWfO{_$k:)<Ģ"w/$t~Y B5hsҍ@s1pR|;zH| q tPkcw -;д4x_L9 $Fn`¿%-/cԹ#Cɠ>/5EczRϟA5c͘@ؿkL\ZN &^ѽtqh/qW`bc7&? 1v^R˯rN.T~4-8gt~'Yo Dg Ի4/*srmy '"qp8Oٷu &sEON{з\WS,MbZJq4F@pe@e=W7lb7uUZ7''9}:^&~fI]NhKujB~ݹd6^' 1amHwG#gO}@ط_81lgz5Bevukc!_J?Cϕ<; > `K  -<8VkCM>@sv4XLdh>(\?ݲ|̢\=7 LW3sa|\GRx6OsSy5oݻ3x CXR0]Lݨ=]ם(eo| |]s xf ڵu}'*;+>wynǽ_a/901i;Onhc{g[~0O>Py>rw :cl p{N!ڸdsqjmIp@,`{fୱo4<+/OC p棣^ѶFv}=3@GxT;IGK7_x+k f[G!-gx]pnkcDX[c#YCn@Es<@C_?A|T74דzyGG4@fJrN[еs  } >^o^ +rߚR?x6%8:&u珶i#xs۱_IO!bH0|G#xpf2ƭ1ڸ|}^=‡gl3}C=$Ϻ% _/жTޅ=b%/̾OiRWx;Ϝ+Ǒм1}}UW{]\?&}b7XEOqgol(ᏧptooO >L` wg> s3kucdl:^wj'5[9&W/Oz$I&BF¶?ѱtlùKT"S|zO̽諟 uE5TsWsꁙRsb:HɣK(Y5[[Upm ;o0kdTT]?(佘B'H}Y+ >MyckO1 Z6p%Q=L8P`2&[ߠSI  mi=}=H̯Qۛ7Oo!Bm=ۖOy={&|9ۏ%LoGKxqOq(}|?K}q"aQ:_,wpkك4] 9!FN=YMnێUGF wWWa6z0I'?o 1lm ll>yS\?Ȭf0# >\ }-Mk883j!`*' h\b=S%z -DM%" {l/ >uLg& 7:mԎk8]k}&v'|v @ý<.Ѝ=7Lx'bWEv]9o7,@|*FO3W^BieP汋C8CkG{{rx>A-0OW`O`M_a`}y`d\`t>0N9wkduw%뚆Cs?E/h>U$}tr2u7q`߄V ckqqfnoDS tCxY`liq{K]O3kMm:_z{)OEp 2$Y=|W`?(2MOKx[}ZÇuX[EH?\@2¼1\֗/gs \c Ay>N9o57O0X\@P쬭s< }}inPd?qkmu.mPi_bpȜ1X~f&&@{&`o3A,g?[?kTؿf?ƲgQRml+b+%(!Umro]ޟO/ -5>p K:}E.Ha<||N"9?ZǷguu? ]U'd[y!G~m1͸_^ ַqgu0:{.Hm@Mc+v7Qee厝|!=@е{9_{1Tm>-{m7Y'x/h.7p6x_?ã>˟V< p?Ѩ@Uƒ!2~Gft{~eμCϐ d|̜goūpص⛝/ u(Px{v3FtBuaj_jkyczp︱~:uy-/̺M\.pCZ1em_Xc׷R m9*a6J=?#]_wSk06m U\Ybo/fK/sݛf֏p[dxǢ{zO"_Y]o]:wKüM3ss]J}pg(gnqDw+/|[ͭY1SXj޸o}?dG/Kx)\B҉/9 S" cQM~to{^ce=WTwj| -O1u+ts?ڳ é(0 V|9/Nt??$mo 3x3/1hh+ tZ~b/"h^Wx7  c+߭khyR"c|>O7B :Wu#H '/,@IzHrܸϯQ E 3?$2e3d?lk5r/᧟O]X[?ccA@yGqbKam`e A1!%/M\@]/6v鳄3)xQ'dm]? yWH}>].\:y.}7 p37?u1vyw $3%B!yMk:'u63x-Ǔ ޴5ޏp^`ba?ـdڃ)}uXT,_U3=o7assYά\l uze52AݔŇ]|jz ^+y&`qhT ZxfS76sS$0&5cBy$Gԉұ|<{FuWizIo|3{z?1Oڻ)7^3@Ԃ{wgS MdvĢzp51ҳomـT@; Nf6pfU<)L& POy>wczڎo:k>g؎%w,rL`].%g}nм~SV_u'kSWIPF WӼ2(ew_ٯ?! / h{ Y?ؿZzcx I-{`ݺWO8V `c,7^OT@ᚢ\hz(At?E./mk;Ïr#k &/W6|B7XZsXV$_Ÿ# #Y>'"ZTR/zGz蛕?` ,u53Ayfr|mk{-<۷ﻍWsrx`r}PAYqLj*eͲHs,v(?j_89ezYCE3FƋ6y`fr~b \iU:M+i:AY| ,3\̨| [l!ݳt/#E4?a MA/~Ë|o| 0u2+s*O% r/8ϨG1 mRXiOND^-<"9yfIFiR:s €( /plnO>$C'*Yq/\k|dzm<{l5k{[ݘ:V5%7ý2~bqLo= jӷ$}/9)Ϟ=Qtyま\x%)EHID^мXҕ)XQ' 7<~?h'8IO|O^X# {J解C}ܡօY Mh ~I aՆ|lQ>Ω;>Q<u%aEIr#noF{>/S<ޠ5z;,v;9~}d]QJ++<Sz稏5r o3X+wD~b5χtH@>zjq#\30'IyvpɐvF;:}M״0r3)#zǓ[IUnNsJ}.QdԺtLz7VOxO['xwD6lP9!']:~{LDԤ d?1o)yK}OBy- 4'b򜵞ҫh>yy݇)O,?:934`uV_xL.#g#U:qK?OGKT:[13̈́q?gψ IXKmCs]m?ݝNC#^YoujWYu_jh?GKysOe!z>1鷋PiSz>-2+U෩n{Y,kI\ Ltetbt꣰~25+ iׇzô0>;g4YF2,;G#G:@ֈ3Ğ+kj;<:4cڪ1u"YW1OBְb3WMOخ*{'lm}&֪/>V}$<\~ZҳW$c^?/grOmv,hc#s N$l_okc.\;A?g̈_%TLf&2ЁTbKqkg} cHqN/Dԩy: $CmͭgX{x}ow<HՙZ@0?:_hz)wF|̀I]0&4i8hSz4yXnKt)?<6lxb>'ANO۶[j?]|B21r=g 9,?S2F  Oh3Dc|žf\q9F{-sGQ] mߢF'Z[9e=ܹYNPS1  }e͵BUѵG#W^0>gϺtq_hQ /"bhMwטqUY'}mEBpii_gV&d~eKr/cf\{J%*t ;]*c9M:|3>"s81fuK5r̥g;'C<?f7`o>^[l{8yn#'Ѵ_s?| (r&JS& mV6N}֓_ K;UZʪKM?~~#m@u `*bջl,.fS|՞>t́?WW|_p\`kDgo*-$79>4`}`g+_;ny/Gߡ\j`tM`NE}} g)lO/1bȇꟲ_|޷6O^E,b1X6h;өSc?=vq]_~&>ǿ#FH8ΥiζK{wFy덈!Tn|m WUO_׿Ɓ!attċVY'q+=99m1+ߓ{Yxvl55yՇl翗Xk(2D-sΘn|QZ-c{>睝]y=yOKm[`5dcG򷱇N`̃vo[^s}coIж)ViGznsOw ^ eNJKm2ޅCzC!*_zcvyO~j:ԯn kT=xa4p=?1Uaʄ@UDz s:f]l|4>c4]R}߮I9Y[#1dı#8K i7bۿ?o4찾Vu q}ve-eY-G b)r:omy$>O|>I {^ =q}׷`Cr1]Z lF9"=Ϭ>"7b-Zt5`%&0vIJkc1^BC˶sk+^O#7}.>>~c_uGg:Gm.d Sdfǡ][|kC>ˊڣ:JOo[!-B7 nWnXS`;_xǫ-vXOxH0G_|-.,(k^m~Ǐ+ⷃ^Fd|ňgtD^ {R幱|? XNxbixh7vx{+dmϋ8i󛿁PXIҥƜ﫽`z*OH'Ou~ c|/i`w+Ļ!TYJ}GttCbC'mtY阾yrk7 \[sMpc&`{c kF, &ؿM7oᏥ)u5?^sv$ /@vʎ_aCDNL^n8)|j| }Rc`jNIm:g9iyJ7*t< 9㦸9?`rt(ϱnBtĘ_5vM˸_rV>k+? .dlF3 reN G^Џ=1˵*Ld?8CiKh"kx>ɧC 4VO8}9ġc#>vX?ZZX2r{)i>"tNv⼑l!aET*#k@c*98 ?h}}2"aޔ oȆTKh1Mǽ5:PRLR ;gr/g5}6 !#-}[v?9:տ=33}ݏg~Zσ(l=xu#C'||ogF|;)4Ӱn=͞`T=?޶nf#{-kY:!kk]ǘfǠ&ozfv&cleum"|X);b/vcKIvcI=݊X+PDcBmy}rDg^ztaBGXW0ΰm_VA1>}ZM\>ফ7,6trN;Yδ7qqqyҙA]ѦrԿQb`gsٞ}+Әpڢ1}ŝffG$1␶ =m҉Ч~H /qP8h9l1ݺ94 Sc)=\3}<6-Ưnj18, 0>e~q)wiJˌ35}%8UՀmZN/{,y<]|UW;s}uuu^?*^ձ/>U U iPOO?lϊ7`#0́ʯ3\FO >z}\NRk]5><7c뇅^<_? ҃d=]R'q|x}ׇ}`tA!Ey_o, 8.Ix#x~}χqat՞PB6ߍK$5 nj,w\ga_rKoEG'l & &|Nۈ#o&!_7cؿy G_p_"ɉ_%{h?^J3&5vuQ'#*oPsyy/$:'mLwG˭UFxr-,EY\Ѓ#kFשMqǣp7[G<>[27 _>ղ]g?m߷/{Y CxdcYwo?>>c/ nGSGtG-oef+E3DwrB1ɎG;7wh5j\"/a;F0jJƯ;  J󓹶9tr3ݼ٥[zc"ƯaL=okf{/G;">].ļ]@׉2/@;`s|OoCAc?}i|hSYj A#*73_k2 Lpc91p2@v?3<@{\%i:nF+E?7yK߿g78Tl"ϫ[~gx6reѼ67稐c1kCZzѥ`;rԞQl+x_tMxߖ] 2Ƕ O~[F/k#u;"~[MD~sʱZ7F뺪XNmYƥbu!#V(~]/ i|$*r @P?|0+?o,ؾLm|!6m wbרCgn\smu{Ϟ/q۷sD)Oc0b/lO4Ws{77}ZY'+< &sy)l7(m^Y)غF8Xzh}^ill&dxq@Wy>kS^fڮW0Q'7>Lu2*гvHm|&]LY2gC;hO~3:7>C M?d=hF_Uh/rb藎aq}76Zn40{76:h 'rXTY{?3Ly7oJ̙OhGguY?M,_`ya|"l/I8y+|#LG.u g'Ty5/=:[E8!WgbW ߿Y[Kt Ά^Y}79F,+}uS?y?_|5Ayx]x "q`e@Wnft}?bSX~]w, ׿5Eyg{+#mWߐ+Rgdl]w+4?2zGr 0^wl, Z?XW+tӓiv7i6ߡNء˿_>[>=}Z8{L2z݈ΟR뒨\S bi}>Ju#z< y339?އ$,wǻ:{M*m[,܌%tZKD!=|#jvy 9!P%֖y&v/?ٲиaN}V >Sh]1c~H'N̊Q>׼6 1kh|^<}ei _>ppJ?غF^^|'.8%޹1Q10z4;{@'O<m^b9{Gu\@XhNQղ7bo\au%S:u*h/Ϲ ֜N}"?;{je|+:[D?6>yi К]e"_~7M׍ uFWzt]Czo]> [<@ LeFnzlMozgdsO|2:Ty?<p3:Z[Gh4ױrkNcUp+p9g Q0uBKu]rc_!t!g{5J͘xe?2_Dáᚫl򧋼?'ߍl* n̷qz)|p']Z(M%54CVVicC)pY {Νb|n+&yTbF)wP?}`B m~Yrs]ydlƂI6 ZhߢA040kč^yd4G7Za֟q>(+g|GYN[5GtΈ= "/;~@l$Gu/=y1cE3_'j|=bcvf1NK`1=ǽQ09+J}`+M}!Ӂ ?=.aͳG ܖ} lA@q iBZ; nx_XPH~Ds5`/%GMu5]/\"u6[&uPC'ɺ5/L❎2_|x6_pmsY/5=SZxO1WL+.nWhn>)k\vYI)©u?n|m_!B7ayqwmB`ba m_'~~T17P=thګ|{+,~/kU?U5&5>s?E`L`ŧ>}saDOg2E}=%Ox&l%'uj5)5K{lv(X7zN^{MWdtˠg?z_x37ѱc>E 7>>LۢuBFCJ;T^SD~P?:$M6?;' _jxLV/AX%:Bٱ'nv^ Գ0i?>{ߣY,Y@yDhxMs 4P;l _\ &IOtxvEjO&Ilsn^6PX`wsNժ,3ys~&O\ yeh,*^S]ιM,ĵTmb/˼A%z6,.6r[Ǡdx|}K-k^&$GHy0{0>IHqN@ tp 1DUs*՟+r֦2p?+@TJ͟cOXD/5&~ޭX^Ʈiؿد NV#u>w+?A/v~R<~'KFWR\<^Е`F4(ڤuY__]mRpP56~dIh2D_A D-L}p$u=bdߦryPMlySg%#? 6G4K=om.uxff6ƍ*]l뫔5Lc YM.c9Gtt@gDTެGjlֿw#Xa̘<_-JRvUhռKGt[5} M?xÀ1]a^+e<Qz|sc[賌=~lsy^,gtBF/N:Bx@GIn*ujYk2/:~brPj:X zf^X`}Us{ 8j飏bޙB?WSk3\Z[6+oxf6,7ap,g}QMR!&_xMuY+9OwtYO1?,U`A:ZɵiX +Q1ɞ bLyAh>c:6[76= ` u2g@4ŰIsP"Cވ{~xs5:lݟt>Gvۼ ,_ʚ6_7b~Ű9?"A~ɓGzG|e7žx&׼6?0S?u.+Y{Y{ ߿JgFtk&ck5ޟPsP4so@bW&n~cs濫 l#?({q|c{m#J:qZ46( cY]ZC9EAt{^ը6i'о`VtīQoEg' )3gNP&e*8pLUiC晝žA `Q͵!Cz},L-pL V^o}}|E9t5:形ut]׃Ph(g|13pPKLD*~nSzx.3Z_?ط٤ZG~\jOthS96^!?/^?5MX}R^'|E(vURyʳS\sg~zA`˳0?&W.иyNfW^:WpcΟ6M'\rϞS5u/;߿pخjٳpA~O"l3:ŭ c{7qrk6ַ?jr/ظ[Ww@QK[6wwEL84P H.$QgO#>y}M|^m;:Fx_OYfWڽzȁik@柛1>˶>grQh}Rlߐ֕pmF7see9`sC#{/Е+{twk1ݽ폱 dr[}~*gΠ'1Ք gZʻ3 O`#9l{.U5jcǼxg>qEhw٧;cU4-ueޯG}:! :/=?NLoqL^wizwCKeG-=bpnS>4ŅVr3ݿeb<>#~T9FW-Ќի=&nd5B]swE{{uj>Hy.r\w?1R/Z2uO++ c1Nя,#+#UbXdPp&?˙KKcjjKcfikNAq\[z8b4:@,Sp o7-bd|1_ZLʙ?<{rlzkס|<nN7c$^s>`]v֦Gte~g/bK!~ټCSg&bA`xzZ`C[yAVns5 C-I${XH(>=YkuX|O8z6W=Yќ^rg}YxQN'yc͹ݛ D%WĜ+m?ReNm.Qyme0l8\1NT kr/5~5Nظ5~KdeZK׭ W<س>ݤOnUZm27.snҥYڗj+il_yZ;Xh<[c;߻O-3XE>>Tj`}}F|^/wXW~iaj PeEy03 A*_Bk;4pw@۽y@qSd?e]2: = ЁJ^/^癕rcthdec[o7LhC9J?W f?_^s|㰼`Whk>_Qa_^.` w%Tn/r镍 3oXd汩't'6Mdcr -Wm=QYKPj;)=w}꟩IQ|y듾"yz>Wo;S?'UZL0trпrsm~z)|bxDI'5sk.!W|Ux ZcS2i׋Oz8q#o]_x>zĞ}9OX+ԩt:}K/xgpb`_PS.[ 6~F/_zٳ[b,.@`hn4hVUzῐ %{n\,9ħ?H ? |)'s\OhgtaJeKbL`9il$S"<ƿh̀*[~b[p0yV睄>P 2\}~y<#ԻZ͘4\=otd~{QnyW gP_oꕩ \7hru8Is}:K|ofkS~`5VYydYʭLՕ˧oU F8sƑu*3%7u6^MP&03mH356=;ͻu q8o0?S؇&#uP_X8*bO(9)Ik7q-bBw"?*Q_1i58o5/9_^F' ƱY̓=(cZEgWȾ? |ALw%]2~eYptL??+9~j/<Ѝs{23c*ߡ*_P:c6?LibĞ޷!}G*vB3CVS?sOXOm9sMr>%7maӭ=~OϏٓh>FΠ /GYi]! Й3"/@vWL/^{:&IY!0,ꝗP{C C`]"g:aIo&|18u_)?X_ľ)5w ؖ/})>+ m?<6+=c*^$rGc6]׫lC}WC'ňOÝԚf0RSVMcmfqNǴ2+L{?oTkm>]ޱTc{u7=~ {q]w[#ha1L= Wځ5O)Q@ϬNs&J|z&.2iǛZ#3&kXG?D h@?x4"bA5Uz[pmyi{4n0]h]>,9?MrO6;PTucWù?]5̙%ϭOl왩Wcu㩹.O|D̀ :7y fT" V,~Y~5؞;MЬ/l9-F}+R^>z' z;bϫK)rP<IO}ΘNDs:rkHߍĨZ ZO/@j(Gt;0O\G>2+[M Xyb&/?h9ׇg>*\~ӿvhZ8DE3 ysR+0kod-^0棶\mfQmQ,{3WuhqF-/O ,͆3;;ސW<% 6q-0W<_onV6\,iu*ye.[n=?L`s`!?c(n9/yG Y&.U9,Fra3pL_0g8cYcY/8VPr§D]l8]כ+!>?[״u\/OqVx yv"yޛ-_7JΌQg.b ZWJbz43WYF_ PIw놾I43*51}vA++yVlƶr6ʈqJ[YYs>9wG^gowR_tqkGWx՜ksP·>^g _gv٧5j:j3T9Zc{2aL#er t ydM[TpJ\e;ɓ@j#O_kvDQBG =+k3AĬ+.@~=qrF8Xp)٥| :lQǏ#<À~J'zw93};yg]c8"Y/X&xY7};q/5Wsq>vԦ*ﻌSZ[(^Q9' Ϫ1pνy2ZsS :w ;qַyZAk/3?@Gh}OE} x N  *#ռ?\@7U|?ݻ09|JQ/ZkyY/wp/r#Y cJ<]4upu4p= h,@J?YV,< شֿn^>ֺ}W̙2Hisrp߬!}w̩ͯ?~ui@Q9s}>D޻ELBϾO8 ؞Zk۬gƂֻ5/ Pɟ< n?/l ]Kc?pڷ1>:gqŔ)_u=C{ok[t~o>'7]sXغ#[X}ylcW<:_JNh]hNo<:_}_`#90~LB?qny'VFL]aݳg07>E7F5SoP"ui~Ҡmȩܤ%q=1?^ e~y.܀? h"@b{ܦJܷ-)j33kKG+?znD݃<6u-V+Nۚrfm W_'<ƖsSz#pt1Gog7b;;C!͈?v9&*l(v?C~ ]tXh+23@dOw~+6L~@\_7ǰ+W}] g4] hG@ti"0u?\|J~gLeuCӛD7oUIM,}m^m ?uz̿gMB ƌ?>ΣgZHݔcC*!Mך4>L5?ɫʐ~=:넆Gokb+c>,_K11}JG*M֐1$6Oc[39 ƟSAZlJ裛Շf0}8_`f'yMt*՞i'oe裏tIr[MĶzl=[Zki]HjyH& '1j?p_r/Θv;W+ܣhSan.;)cֹ/٦uF^_ͣ#C@O=|Ǔ=v}5CcvYrM>lѱVQOkܾmj9s`%2Vx+sc~N|1}'9fbs[>pl.]:3j'j\JQHs oP 0]^OߢVVbծpx=yzh_+,L?M` m~H-@{b"-ʥ@L<7ZVsH4Cj: ~p˳_4gn Op77`d?xqD.uL_Kh󀼝ESO,OrY|N9gM}7/:1;8x:WVr?f;%Ebfk/gy u{C*ߡH |k7%j@Gwک_yҝwrgYb}RP'9_^ߞ $i>02"$n t>}:s|L_DjA77?hò/ ՘~5^TpW/5nݻ ޣqq5-G.#Ϯ^c[ka"2\S\{?N(D9+z>q2kuGRV~r.\ʕ5 u^Hv6bF:D,ЗBF5T|qhLl0&۸uƹ:3M)oe9/ԌABJϏrʣuxt~F'TnK"3Dʸɭ.-}ӉW8B*/׍>~zE\rL1N\N=NUyJD?b}"sd=hzNg@p@=O][ӟ<湜9o| {j3.lÀKY mP~=;HhOQ/jN Xvb&S6<>Sy^ L)(h= imT|ͦ'u.UӚ 333R @_mtWfWBk:@. :@opv-`&f"֡~FsI}[?pw+ m3Z %Ӳug*'M['5B"w/h&EJQZ??]G̗A/v~PK;luVNY y=*r:!]r،D,ѣ9c*~}zp'`gqCR 8KMmι5]B?,NMl}GFs=_tck-@NlgЋ.k?2^b/Nu;ş<+grlm3~O0ʕ}i[j)=H~<1FH8cO}kM اkFtbL]Ο$P$~W͌/`srJk~`L}?rDh8!g2RVüdx HO6ǘK+е]΀p0(-χH)r'"93g-^m3NdrSeh{֕Qkrh"ľ F fuިф1]ؿ-;2r>3~pˁ#.~mbr^ p83z? \h%v]<: PAo1rvL+Y,D+h$zֱm }4.{ˀ1}Fk"+OZo5Hmճֈwi{ߕ/5o}uL_)SQQ7I5>Kmؿ|Ys3xvFs1\|lV/ 9[D72myh:w"-r&m0jѳ5sƍ}Z^:`A.W0IٷxOg΄ h3[77!ǒ_,Ϊ@~`y 1;ԄGn -C;GRc)g^;u6V3: ׯeuԮ1~=1|9#\=3SH7ϡJ4Pʗ䣁?ztxN}{ӧ]+3.ʑ=(:٧g)=L 9b>=n3ЭPR1ҳ<@8|4F4:"B.Z mH/<:d!b3Eޫ}NWypkRA*>cuBempE6>m,"?Ki5W>[7:|Fd* w3zDM+Ph=7ίmUs}&虹h4in.@yKEg_qFkL?56?]rחu'ή1pp1o 5u,9yEcSxg7G}plRs0 E_*ZY9 >w}/X}.O xǠ !zl ϞI([7bN\>2uB˛^_nSD>@Zoh_A59'<,"_Wl-U'lC</g y.bN@'{đ+}X.UIOڹfxZ[@Eb~/8_9g+kN"[Ϭ*gY؟C_wLM%AӍrZйBm `Ľg?ztl] wk~%X]MXf'1~O x :jGG{1c@R9b?8k`H:Ọ۔&?f{urPگi[ʑv/4j2>\3?Px tNxzc:KLW7 euk 43/< ˵7gY7:c2ln)ݥަ~G[gZ瓳|@xd.?4Qw觟#;ճhc/, |?ڡG=u 5~ngyZΜi 㠜7Yys)bl8gec:k@B{=qG`wI1f/@~X?9 g0=v L4]xz9)ǤV|>~|n,w5}: QQ Vp_0zzǝ=HxY5 #7Os1q['jbgwDŽ] .= bw?9VIz+MVȼ[%`5.ҏ5.-k1HfjJ?>ķa &'{c;klfZ?;H^J[n}&ppѝ\-µtO}Rl`%}r-3'֣1`2Vo,b:M= ~skf=B^M|a`vu9~y.q ZJ΀<+٥{ J̹=O16wmy'',O0' }Dt'~%QkO\(?[> >h5xcmu&3Ǩ5z*߶~8E/jw8uIT3ǧuX ` ŪW(V)gw1y~\Fg [>v>}"@p+7E֚[ ~WA;!D KhƷmfKc_gY p疇fI^gIَ@h=_ ʃ mU(Qʁǵ~7E:''y=g)6 `^j5IuVz;S42G&-̋*p]Sܧv&3x?Eq4 c/uK>X<~6":Gu8g⼗gWK 2NJ9z/{Y\6 xx &N~Of( P!z7x@FƆO@=2szƧh]ܼbgpy7olrj:M^^!z].XY5Euo}3љb}X<hSq3ϧ 3#6op mX3(=].~oywsƒ^?^wNoPnzm?50/ư@5cʷARW :=Z8!O$^MssrܟIZY9K߿#:~ .EH<F_Xܿqܿj<6>ߠg0+#3~2~1kDT̓w",ʍ|^_^q&ſu)#ӓY~s"<їsӎYx^ x4s^L4W% ϭbZFLڰxA@ׯ{ Rg4֢+C|L1|#mK\{\xK>Zy>F˞<#C\<1{E{=ї|Fs [1^Ӿrcl.{8pυfrԊ5d9zǫ*=S'+!־g{{b;e8v''ܻg&VN $fW љ))g_`'5GR{^YioK1 h=\Nwp\W'yt͏/90rr.gtqEG5Ryr Ak>'_R z R??h+2!t{T?}V{d >ǢY@Wk?rc9zwPsR/;8{H<{ '˨]qYvxvْ% OERD2R[gLt{=佷$zqNd& 筇*,P@2vpQs]yP+nP@ejzާVGCY Q% E\$ !N˧.e=r=Wul"9ɞsZx9'-v?o0 @Pg=pnWr,)qnrݠPܤF2>^jj5-4 c{r9,(cڛS1%q"5|e2T:vvtm_QXpo2Ҷv(c}=k3ؤ`@͡13ayzz~Ѕ+}f׷Qh9dh^#9th:=LÃ}uH}HB5w(G#_ha!ofil|ЦNPmƗJ1|Ko nWw5lh:G1QeWW46Y>>N;Է3hvQƺ֭.ߖ-Oy/EuA|ntB̕;}}>& Wd,pL>/9-S^!g`wu?Żu}8}7ô~щ呜o3:l1I:)t<_cc^>[/dtCƒb9* ,4Ⱥ]уpNw9 GfL 2 y{:VW[ D{wV7[ԻͲ{-G"`8n61;=0{xOh\ZN͟EVl9!7AG dpx5:#늿C_8u[߬Ϝ0,t92;Q+V͂ۄ6>^ X{ʳ]g<+E]oZO 'Y~{}w>xUz~>L<ǂ)`MH?A_ubbH~cnmd|.k075:*B:~rX&x~b;Ss?!>m>?ly;6M T]7/isۥs~b<˲]w -z.byyX;,,[" @=XڂD6&=2M0/|&` {LUG724floF ΰ7r:>cy ȇ?3QKEZy1:, i6&ݼ.+x<cy##UbLw+?*lW_Z7o|zuѺbXZ"^jџz|_ ?tyɞ=eYa_U63ormw`0 nV08j~3|ܦJer @DxN vAOqw8:V, b6+e`28z0 J r\\ u\wGFEd}=| A/ >5we2xv3T[uZ[k $b'WǼt {_'Z-j_biHFa2./3A-/aqLm]:F1ֶ1Q{BtnyM[DG tZk_Ź"n^2)|$*;?xk9>x1Ϧ'Ϋx΋<;Vu Mzx4 `q;?^y0K3X/zk\c!3D.:4m!cs mZqtp\uk{;mE7C׌~Pi5/n^y{]N3` Kј_vD箆tZHG53ͼ̇nl#'r{e(2M7ΰ^{й.;t#Eim?Vbvĩei@O]yطϡ#G`uBzrIww w>2Q @2|:%rPyY ׌Wk,N= i4.=69>mڻ2!C4w7env 'pS]ƶGr2msS֏-]N`&p#b=Mσy1ZDڢŜNc< ukwpp4{Re=Z8 D' 7\c7 :uIrcxV T>ߢG[{ Fmߣ/n"b?A̽B֕q 1!f"?3a1x]K>(W+cnq qqlϐ܇bO9"D.\sӭ?KBn1g|Zaa6<`A? ?QXA|H@7W#c}۸Ej5+赅25BunަCGW 0`~;ܟ<Ὡ@Wz ہu}gLƸޓ{ mFopi@m3 0Nx jT_<M`=Ҥ!3,SFQ`5lf/kSmx vf-چW[E-ey~crdLҶEСt4cc:*VBooP:v!?.0k}7}-^#u0'ü>ț2{<<#O2f^ ѹɼ:l9{F3nN޽> ]F_礡9ow&^+*!۷sh7@UE9|g{j_[F _aIY?rnq>a)6./K_pb JӇס_MB1Pk ?4>eY~Nx]t#/vFe8ovܯis]Զp^h[0Wnmzq=7E_VY8? Wp6;},WV1i8tGoZgW`}h#XFήR.~Qe/˴]FlE\'X_g5qfDȟI, x8N=.𜖜Wn{:Yݜ>2_KkSv%`?w6}7O{zD_οc4O{> >x>Bo^hx#J^欐5!}oX_by&Ga⵻Dm{~FDy^htj>0_Xߝ]mmf21Ϝ{a?Qc,Ї,k=~8DS7:F'Naemz ~ò]l~p3CKBW:/ͧ@q/_&Sc8sv#׋OzVN$Y|- {Cwq [y&p 'S^-4fpVl^WɈu* o||\8O-ǿ$V(}>~ܖ@gL {ժx/oH?;ks57ڿn2os0C<=1똫 3n.iW.1L~%qg%D65Ua3y~ID뿊_ް# ehEߣ0i9.>Xy*k\fg?.L7:'쮂Yk2k F^m  `3Ot9Xe-6}y>;b}Ycr;b6nNc{nӞW1iу6- HEdbԧY}:A&kbTx,qrʟ`ePgHRm oby 5agDtcaȯtzyhڸ+c0'b+bC#?wND#n5v}n9ƠO ZMoC}}ѳì~-I<zT!1n5һUBZ^Fl0ޥ'lfltuo6;w`Zy.\$>a@W`<J#z|Wy "/SgNx.p ;s8;آ _Fo H1`vƼt|@QfL 0c9woŶ:bN@Z'иaWW+A޾E C8D{؛8GsTItcWv}6kmЭcc>=~oh>:>;wTW,W?[Լ T.e,/emN0x./_I܏:?+!??x-?eHPy*vly"[? x&q`2ئ>n]!}l֊ݒ_g, Ոf=_Qݥf1?\ ?rzR8sZ߿o}ڟʝ)Ӈcgm~s-z#,O:L~+Cu⥱/m&5Oy6T"a←1MEqʁ=2y{vbdqj5hCD07e|'Xg˧Z8nbҶ$+w-F%T0# %kmzwW|O!]>Oj:c3uTzg#ˑ̐/aFHb,:xڧ{O=9:Gz7Oǧh0;KR ?a` uXgv]d8PFYmN fw*wᏳbzz,><\'xz\}i)V}?9Qg=0Ugi.:~j6ݻD9}:`ݽa.úﲎ ޿;?䲑_G '>`z&^wx)˷>bn+&=ekr;`)pp`Ux0oԔ'w9j4cҥ2E?_ϓkᚘ?~"n+p[kuJ47E;P / %R _!t|83 ߴ?aL} pP7tm^;-,p{ށox~r.LWG+ibK4۾8/, \F~/iט? <-WǸ2rrYGYb -vڥ?g>~n#م& :`J ԙ i]IC:\m2P^o%1??۴-ѧ<,ѷi',KF`/A: _ۙ6-*{]~tRw~ W"±cү?>w#%q݈=b^Inl[8 Ɉ~c%ύ#V0S3,)b,G-8|h(rLltNV(nT݌'ǵ ]%6?/k\;oajG4s$3HC^h mlӑ:~¡mi~:IÇ׌茞ס17p|5.P֊=(@A1_y{^_LO5}E'@r0f'_-7Sզ8[Wa)[0Dztآ߿1&6COa&G46>9Ϣ:p4|>٦?($kFϮD{W2Kc]˥e^/=CD0,6fDgtFqa x\#{}@DO9灾źǻC{)>1NC+~=:IYzex.jՓ>c)&@LƎj )dq+䰌{m3N6rg zwͣx: ¼-,zMNgEǫ%_Ml1W\201׍݁$`(8"F i7\y_&Q#^02乮I b~Bs6yՍ31/]kg3T75RheEkd@-t `du;R/ܡ_<_|{pи]G2Vg~}[Ƶv*E;>UstSV|sᶽ>czx}62}tB-׍Rױ6+v%f[D6ORu+ lY::^i ݷ5:ԥjMq`OƏ>h޿cug=jе3=-~?nS~MnRȈ/#jw<ΕcmzuF8nC}~ms'b]uh7Y7YkBgDCk^y޻T%9oCIMQcj4 ֥gk;CEfh nKKa1lƦZ0sB!xgcj,Q'm+RDĈ&&wA h1c"+lzi712wC+<}Tϟt8&}Hl7޾52u`ߟX5yscbΝޕ 8|D'ywG6$`Ϟ|/2\QaAl O;|x^>]ߧmA_1`׺3巄8$~ 1t@}4)'92>^Pĸ"."os/qzT<s'tY%L7'ѹPuɏ;C6?kyg?cT?Q'^Bj/Zw> 5=hԢ? ̍2tS4_ #.@o*/߲}[I*lG-Do@0Һƛp9ٮI$^g>[l_u##uQaN߼K\є~Z: 8gj Dm7Wnf#{ N@ACY-ƘGGǍܷ4w!}o׽WNuJtGBl?iƍSI*[5`4, cur"H'?@&!mob3D`6 vu32ؐKJ(<~<~|c̰P@DuK̻g9G'xtd3]nG+M3j=}I{ϜC"uįeĶ\s^M;B"(S_\5a9w+oH>{VAbFG]ֳvk? BH:?}P.S ̉ފzt]`Sb0VWR#tcd Qԭ{O;mߧUֱ%[wڰ;\-F9;`SV#Gbށn6yE'CU*-8&2g-[yψ:6/{yR.ynS-[[hbm-zwXNxpd¥#y~'(kfacN#Zt-cv$vv8-o}-ju~/nқ fF3Ljm/nSc%\IS=y֯ZEcotZ(yh]VrV1{tg&|?^qij~D~tM:ȁ9oϑC=!}s9wDQ#WMWͯK=` G<~ғ,k:ءOֵ{]Y#?F!lv0r2C\'+~׍o]W Z9mj1#VQNl- Z,m<).{;H&rˈYsZ{0~bNz.sq:'g8rĮŹsCR\\vi7wyR׭\&r @PopЌ?tC=A]M޿%5^"ɘ# ll~^|K 8.>1/0mz9uZطnlh쁭9ߥ _NSSh~i\(Uw:@>UOИy\Y1hqM gCth`!yH1?Ɔ`ɻ3{z$abjζ腪:ȅ@?THۥ}D+իk t@ NG Z-G5ck&$f\FM?*ffȎZk%$/??M{ t m$Խϸ{XM3s %[S o?vSqM= ^Oۢ&B5:Ժ ll̛/r+& ; u4O7zxMgYg?W9U,'yZ2f<V6ڜ_Êr$?0gL^7&&/~]wm:J!6wA6uNoj\X^I ՆA5Aᅧqiߐ@iOt *7O㺶.+ZOX~7#5`:85KQ7=qqϑXD,6O>boƋ"toZۀ$. jh1נ?+czA`qzG޳~Q5y7o;KlS7maڦ?W"es^\PbZf!|'ܢSHm@jzzܧGd%&Rmn1k4Zi?:c4{"/1Fg |&C$$-yw `u¡.tuU֥fC~sh:FQ _y  O'i/ RQ"+bá`yV?= >CN;p{>uM]!"ßS {= ~7^Γg6H]8׍cXcq4mӋyư}x?@3>@<3Nu_ĵMeV5vj0ϯZtv`>fO,,Jjj m3kkgح``OhI @FC9_S'NZ/XstpHfb4At'H/ܢso+þ?q]y+T&B_ML|gc,׵vNwMN_e9S'H?jz!~`ͱA7tU.].WnvxT Jqm!]b 2zly^ ̙ۈ,=8v JD13 hr1on<ݯ]H`z3z6P_ӥ"c{yȥ>92F4rl\:I.Mїtu[<n3[k@Rwͼ`[ y m[sg(u#yX~_>K!5|w/v3g|ٯ޽ =} / ɞ}}}r\ 61y%ֽ>|P(b7`#ٳ@b}?JDssU1Wz@|3WX1w.!5?}<>C֏ݎs/@ qmx0c0\cu;W1FFmOᧀy?6u(?*A3A19s}?8o򚨊 v4F56jN5Nߍ߽_65;kit{5޿;J4316ǘjBx4kkϋu+&uŵ$~-7V!8)0ZͮxsMW(ݵd>uo.㰿0AU#V>Aw֦Ȗ1CbFVs2Ջs| fxfX zMI>:&|YKt"Tyӕ 2c<;`G!H{℉ >ߋq~ۑ-4vVk|bR@I48'^K9"c~͑㾾"{7HT_@z_@f{ 6>n#k޿7Jo-1 >p/嬸gw]>M8;հжh21TxfjbӸAS1u<@1slsWKWOc]@톊in%]A?'5?O#w:^3MS?֡ Ӡg"'og+ڿ2N; M`kfa_7qM ~ᾰw'~=Ynd+x sW8I}50#GH YAN0ه}8_<>Ϋ.Ӻ zOcYj YX%C~_j_S}y{!Cݠ,|?̹^p:6}~}0Y'+Qu!R?/]vʇ2Oaj9f1M:q۷XyYXF_D16Ͳ2GeǺ·$9\%Z Ɉv.pk&67|th7{7xی9j4Yl0β1ju @bߵv6Pr$6y=Jn-&d'߈Bw=y\0KU7O^y71' 'V*˗eB/GwFAܫW?_|CHp!b>7`?(ӏ?^ wkoωXV`?$|> i~z& D7+ ?R{I9]ZpA"TK *f-@9pXwE2q7Suc( 6|s: ~ {4mfl#M]XؖڌسX25~>LOhqGa᱑ n?##UCa?  ~`[G;qzf|j͠/VܣemFd 6foSطxND/>fTߴ۰o}.:ܼܳUK1|CpO?*ߴݦZ).ƞx.>8|b2/㱞2I W vū=$9z,_ |=;rj+ kL ˺0KAj:x13CHym3o8?Y|D3b}:Y9 kL+ޫRGP_5D|{8<o{o/0~E +g 7BW%5 0w:Gss"o+I\^/?P}b1z5`G&G>&0oUIؠ,17kc3t_`MZܿ~K{kGBw9tmC%Wtg>@jǫ{@sOo` P ͅX~ ",qj7H?~׷M|w82RњR)Qۍ{7tSW,_lg#M>w@vLX= \bouj(?N{)n H6,{G?Ng%.XNMz{ OR{m\X/{}G&Gd(#ڷZQ-1Z<yS8uWgT>,FcL\秹q V?:mٔ1@>T-+is+&Z?o\Oi{Mxi qWj~7! Ro@m@yc/H)ySB.Dvi|Ϗ%{BJ-:w%oJlcM۝,eľqצt)j11/sx?qtW0?\u_׬[.nC+ِ&qm#xdb&B3K L˞#4?AW,m1y!Ȟ[mĹS˙=FpL@eR|kkƿ=я1_~{Ilϛv7=p K/_.ʾsU^ᡏ#MlCЙ3uSGC\XP >vl*x=x.똯"^Sq_\OIGp?{Vx!j4; G3 03};cOɚ&[77ߊ}-`?>@xb=!]9%a)P{Nf`S(@~>7o#؏JKzrU{)Vq;s~_܁}]nT=9mr @-}Tea'=7tI nuRڴ܏C˘\J?5F5?ژ, ~[^.o-D 9c.9KkN6.$ߵN/&mJ8fЉk<ՠxާ̾Ҹ58^m}7Xۨ 6ZKڋZ<_Q\?~u^+0o[;ZF h-b[u]6^oަoo/dO/N$6X7բ[tuB1,.@qoqY][7[m'F٤Ɗ=OD\nfHྀ_ ='v6HphK]\zv0=&J!lan^r(<N*1?x,.86_s 7qeJ2wjI O!hc0o[,r$I38Ü;Ǐヷ;RwsгgDu<'+~ ?hq1x>o .B͛Pyg g`ߠюׯt#u}U0[tEm_(t~J̿E+LO繯YhjWBkj\ pѣ-/X'9^jt֔&ؘk?j_i{<5o{I>1~M B+|`N4ժc cWӕ=h \jL,ta:ZiϦG_kJ,Vd̻l{g=`n{9i`aGv<?:TfbqW7rpI.8/O?ǥ.K?};?e7W/~׽s%s2o3S,˪6t_vmdM5Mߟ`u>o/5[o$5ocTx!96=x\k%Qla:7Gk I_xfɞϼ/!`i}3~7GWwb/'Yu1Cf<J߯|$:mpG[MbH tvucC\zzu$v\P{T6&XL=0 졊g q$S!oƦ a#[c8` d\7\?SD/^=w<H6lmp!x>>8M?& YヒСq-;e>ӧmӑ#;(@o)dGnA#1jz``N>s4Y @,`7Z7Y?,{^fb_4t vg ln4q^8=-=@?:;Vs -NG;,6y%qCT_I?=s}?ڿ){ 48ܗs>,`_$?|2 zC焌ecL\s,k}|.O5t[:/sLޞ4x=nq]gM-R?gx;چ9Vi nX9'{htT*Ww )&QyT-:s#읰1\wv5o}g=+%c'X6ǹM:KW{dٯ~犞L?dD79KT.4W{Zؤџ_4ا%.\;ԞZu%n{}'˽#_[7aCa}QP`y:o[ws=72|65yQN8'%WZ&c|3u=aOaGjV ss$<׮y~y.l58Wn>G$oMutttLpҥPl M3ǵ[eݣx|:D#VV:V*S/chg+X\lF/*sߟIpfggkܟ`yz,Z/>S#l}- |@ywN }}*ݻZZ?oYvd}M(Uм4ovp$X *]C/F[M`k*c9 95wk.gؑ=]YN{wEp=;Jũ+ƿ6M97?]('Ow%>ڿr<ދۦ9G@hkg\5Se1'Fokkl6o7 >z =-z0tM?5LҾ~|!r09^󟲴y$}Z+Z_jk,g-5ϟ]O̾?jL|7=:yq  i?Qq>ޤs:%s_ 䒵Pc3ouV#P[k!sp":]~UA^b4 )=Z:[Ze@ʹ`nI>ktA^?E]P˂Oa~\e% )8DGH ʅ^᳌/~C,#?)9(\21:<|P;\IB=7ߴiaSPwXrL]>دo {O>o337X_x1ڨxwl?Q 1s }آ12wMN wVa.?kc[miϖΜqs{n2f믁g0?}I c@7ߗKܞ000HbsA";O`u3.},j~~޼-VmGGBG `v ۱E ֧x H"cϿoujk?<=@|ַ9K|"O_k/6dG&~fPYVk)%]8h ĝoX_۽]ԟɹ?ۅq.s6Aq$[;0k*_eIg6"Cwٚ)?̟xϦkol.VmK&ۦ'?ŵ7 9֏ E`aGcy~x1C{_@7CW}N1 g?9{`h0Z1ʳ׎ȪOPgYv{?Fbuðgؚi1 yC/#>z"9}f9RezM+|D'CQϞ;)u}Ʌ'^X,_Ѿ˗C5#t=}mr0]k0QgyΜ}sR{^q%^Y/b38o1}s YmX߁|O aez%ϚI (_ehQ~9l*efE(+=20OTix[JetXߋK=&؁6K{NQ30uaW{u*f0kcS7q#Ss]7xN|*ͼQ2{{:_<2;㿭=J_L`3u}ɋ7̳(}k&!kC:|x3Ճ;8%{Ѯ7-GFMxPά{ӵ|Z:[LZWڈ98su*:Ps=̽?(t] HMInÑg72?ڵ%{=:|77OYVfbk:8u7nRa2?bE/ 42Z7ȸFkM:[Nlhy d$?׿ҭDЁ,]aEgy9f~E, hC#3/a=?/yؙyXկ2'fzX3..j_[og c\X6n`O齠1zGAg2atj u?ڿo<+ cm{+Җc]` Yӯ}*yl[ cTH/>In?4ᗬ PŃ}Zݍ{.}$LF85_OԃsK9?pRuUYç.L-Klrqhg>`!Mm hƴsc?0'ώz=ʿ7䳧gHYg7XufbU1בDVoUP9ٗ/>4V/o x<߇k䓂ֲgM߇WDž&`c׸yT?7e50b^B'NU٩9W\B7kVǎh޼{9wg̮]=ɿM!`jjRo{P?:gYֺ/>oQSU.jP7T_UKs֍b{ c yc:@cI.rrcTxNAGdr!;\Kc@Ocx;(k\W/#Z2l ۦraf{8  )k8>,A²<5dYϼy pMސJkR{WO7{{#)]tP {Yyi~ק>ۢV@ nz`0fC"ehmȆ3|?}9zUwkl2w<+ bQvXֱu /;~z}ȯɺEw0QYsZ 1? bX[yx8E.&@}cr8p3Ƅ뽲^W>7[qx7ZG&.^]W/?/Vq臕 [g9 ~<)ZMrT)_5.\ǹK}07?l;*8280{ 'cC0C1VvԶT{zF~d|"{"U 8?pgd׮S8)br`oD{όl5߅64%?e bѢBx3bbzs'YW5'urkaYzWTׅԛ~z'9Ξe~/M?T9׮ta?kKkuv"_jrn[^2e<|PLmflq^:8^s ?y翵Gz|zhQ}{m"d<gM{zسx=b;mzS k5&{gal?/W?2VFkL@[A8ל\R*L ֹo)0/Ocdvx!k'` '\m/k35?{7cv+l4nl[S.qn"}Iw8]4n{/L&׷ylsw. #gfp1_mu\D{0~ZEiv_AYÇKѣu?녯|WpKn\gχ'xNdOSO3s~ML:׼fsIےBZIݻ]\뵸ez1ql !/eYǸ27dL1-ujzR"+l.q2^<rы~Ə`d7[re3b잗zܳbٰ;H/z?ձt/@/t|q7y{@`=)hbF:;ctU& sTmcՂM'_SjߪŨ 6|sz"oc0|g"Vv<'O#s ,?$#7R}M"xBuTW%HuIF:>[<\f@Yǰ۫8wM&OG'_ׅ[Kl_cYŖ&\Zߟz& _/bwegzR'#M@ڝ_ n{Ld%d~cߵ?1י#njYYʾb_]i0wD_y=)J}zN]|&uj\M3o{hNc価>>㜶+5|$Q2zˬw،pwFx7p̽0>`_'qC_=YrDX6N]V(ζ6nyx YW'<%煡l˟=}@z9[׋y.ٓo;ҰOʖp덞kH`׌m@ܞ2@cm5Ǝym ^<~7;pL |pyGq?\jOq/<$2j$l߶ظ￳eCr.4qԴ}-s_ FGݑp{j` Km_I]9zZ(]"O8훩¤V 뚯%C^ðqrܞwc*}SL{9|s}d}ӱq3]QSd楻uUtXX_>@?SG{C>Hm|{i!Gޗ 1ʈ'j7N{6b>r;߶-@jg?U$fe[77B4Wɫ"FdDֆs,v|M~C:.ʙ"%oCd)I&|-u%k񟟻>>Y:??Aez31 iؠ]l;WokmxmZ%z&9a@V~AO;^:y1JZ/&1/{G3'ՇU~?Yۼӷ ZyW o!gkom~r_e1&Ab ;Y9]Eףr z]bjZז)N6}tRy 4f퟽_8?+? _z'X<);b gkV[q[pkoV.X'OPZt[Ӷ:FW]ڣyavam@1/gR=5}ywR\@yd@Nrg0٬ATNg. jA SV_E/=Vӥ:No7q.h'[\ǻ'xȄ7/&xS*RXU'a5v. wb?/F颜w kP{+ctl$;{w9+՟m6]l;!2\Vړ-%mT5<|725i=s!oj f}iхM^!+ )/Y~i _DWY LY:J9m]'2=6u o`b ڳuݵîL YNfd*2V5b~?hZ Y_m[-m6SBV6,VUbVG%QVd_?s7=?[MGo??XsDEg-kr30כl;>b A9-VP5=>y__upmTCО+L)c}GxY.#Y񿸉R 99ށ2>4q}z~vgw"$A{Oz-sܞBuݖv߸ FU"J ´4No; (%w#8B! Q ޮm#Җ"SHt_ r|%蔚vE^Eqw]bIs{T.}]k˵:MTD7FBEF],Eĉ<$kd^BGro[;UEP9xM|&VVJߐUJUmic~F?"ɢMl+^Wk;rBT5/ȞWJP? TY[w]\En (AC{̾f~gU@yG3r3wWи^D1z?PM\ Q(bYKp%"u0HsSV![POY ab=:a#<)nc[/~߽}eL+`⊇&xeaS]c҆h}#sHIkQ_<=?{jOa_T~eM;f[^ /.\/g}KlƟMt}4>iAFVv P=WfbJ2:=|si/1 fB>\K9W(ﮌ57?k˶ރ'gy!E=bQ,5}8fu^yE+2o=n4P/^WۡTu`Aǵ'xOHjy:OYnDZoe}XYdwb ybѴ僙M{?s8܆czU/Աic S=^AaPҾfiǞ]r2.r>Z"=)}Ad1)v )>ux t:G_OǸ},Lt>}d.} QUge-z^\CTR:F͕>y˘ V-*qg5ݧJ:g2^iX )M1#Oƣxs"8j{[$n_Sޞe8Tăq N*_@[wpax'|oO#}G!KT7Ӯ'q{1eMwr@ݐg aOwoU \c~,}GvSsd>s9D!ШdmcOe3[kdH\`竲<OxY93N#CaG97B]<'1qme 3Q(oڸX>~+lzr[IJB}nsޗsV yYOeY#Gf9g\_USI]-jx(<EYRv2|{*zVxF~7m~9%aȘ{|q{V?% 󹹂|Uv-Xӈ{r/x RSzغgU9{![t_X7@toʡU~ӽroc#K, _O?Ⱦ@97d6mi3qWʅa;,.-#OA_I(D}~5 S侺k7Ve!^`r0Rt|QU +X?ul甅K;f/q7GYq$Շ +@CKT'HuLq?VԦNnl rVaO~pq>^HY|jWdZk| ;GN{][6*}@goZ.u^*Wgx" צl#v28tϖ<S.V2X*}CϨ~-9EwVN`r;V:{uVsU^1z.PO e:_CZ@Fdlnn0<]>Ox;>k K8ə|}o-@g\?S>{>m˗_\VNLp?}KCؼ977?u 8rTxr&'pD GpP~WֱcGS |yAz]dN6MhԾ\Bo mxw[%w[FRrҟ:U.GS.P-80rQKF^wiYtEn{aȫ"m:ę{pA䩑zaqiy ><.%▇g*~8!QVo;x ʘ"!T&gJ6uz%]oŸ??EOWqPGJ2b#;h8v!agOeny6^ jFߎ>C[{OySn\$eՑCNcC[l(Wx+2=25i}Ō:CIǖ?QrA~.0[{"`xKTR:O wH{EwOO swIX)>DY#tkC?e߇T6[uz?Y"mm_9zoҔ(z\!𒏺_>{/ۿeo.|֟F2adBdՒ忑Y>6:zy9t?cI/8P1~hk*DmŘ}܇ ڍ1UT9"WEW"m ޼yI*cTI$s=eLaæ%CM-9|c5R/Ǚ<}D50E;;[ʝ}.GanHIp.Mp@}G~(ݙZ_~㫯x\Ozo7}`j*Coŋ7`> '3q;U!+b6kɃ#ܹb۶\ :}Ft/mm|_,T.PycC?k?Rʻ"cۄWiAO-9ϑs YӾ`u>H%%:")ʢKJ{/5rx*AULqO~RqjB?6<~ky5|})Łb?L0 3d%èWuu#%gƕnNo^Mqp#l_w{#'qE,3EoQ8dl 2щ cWDKG|@RbjnILQfCi{syc?$>/;/G!6 C9|/;{ޏ[E_YՒy-s(GpETD\agzX$2Nj|뼛8ӝ}p/ '=S}19|:l9`T?,Oj`?[5ap? ˳0:QJv6/?3=f8=W-ufSy㗊 >Fy_}&~e9G[D/EdXz!#W,aVLc&gc!vwP]xR646V[nv>c~ExCcuIWn"#SŠʅaw^k^bdt\&g33x8}v*wgqcAy6 2WE>-}=z4̺;//2>8u{a7xdB7*zᇮ/ rS ?vԆmU?ޕ_)}3VO@['|r3n}>s117G-7k N\o%/2$&TOb( 6oɻm!{7eҤٴJ'\P{GkrfhNWmʜc38^O۸$eQ/@OtLח{ sWys杜{wto};qo5FoICGY?6 G2o8;,?NP.O02ZQY3X~oyyG:Ll>Id-U~"ga_*L̪5NQ۷[WՆ&M}+U-,9Z!,,L;Eg> k/s9fy]-:\XUOKX&+o|Ew:{Sr˗֭{Ly0;;2B!ŋ]|YsykdN@ڻwC|uI䕶sDwy^8 nO[&z8ܧ`y&Hd/l|^"m/>xʩM?2a˟wCc> /ig|uw<َPfhր1QnLٻj=t6qRG8SS=<=5w7PbZX rD [^Pkm_xl {)aӐrRؘ:OI8v5y} Gn1_Gi#͘X͟؏=Qr,M;tr} _«9NkCse6U:d1߲~Q򒌜NNx/3|ykS5T*FJ! ǎՊxvBtp))4e6zm~[0kpI]+d=Ӑ}i6ˌs䘼JYmKG"<Ҵt?~Jv93蝎ZDf7nkd PZKW-'򿀒ya/|0Kj-,ab6Dk_*6_blxlck[w3ٖ'6[MO5s)>~.WE&>uRqrfVhh|}?^F]T;uYԅU]}b{,x_߬>=qW4g5*ELMU]㿋sݻw"stE 'Tͧ@|޽7ӏU`Htp?/^W:7\ͱy\ľ4TE}WdCtP?9˶-<|XáCSU9FM~,Ç);6DSh6>{=3F@Y^cpqjoWҏdx $m<&Զy- hYdgG+70`bhr`L[{Z*+ҿ:h' )m`wͷ:p6nG~xU݉piOi9|u!ƵmVJSo.|~_n8X7?7 ˸~N8g$cSRyFx Wd pbO+wJý$4'G,=ǛU"T}$磍G6L^!kEaX"#<[#S|uSYEY12,ƷryȎ E7m0w܅򧏢<(4pthsFf2.8W|y8cr$6 P TvqVjOw:3Ob.d-5O{<<}+o6QSΨγ2H=m |l[ouqEq!LuXcKZ 4GjSف>R&iGV-@x9GRRheƣ:vv6JZicӺɛqܶ1$:HWSM'fl[ӿR3O]5q0q9o'˵:4T_#G_&\IjنX ۩ݎ33C27Wd.Zeev]Tݿ (G4FSsK9?o$X=9dߺGkȚ2O֟?;S[N/,,{u%Q٦1^s2=/|#jVWG}{]sw|.c^|:qS_yw79m-[ Y{eJ97U+> SS=דl~j6[ ZMs53?m<{LO i{Kd@ϛ&5'%Feƒ#2d3>#M؁ƹo2GZ`_q~̧enW=tjD>(غ?Υx;`c1*?N^jCBk-"3M%|sJ?]|ux5ñ.ON`#gߩ5p{яM|Ɇ7704wYk[;x􍌁~.hu [fi?*d1@΁' o2,V# GLhfrM@8Gk/:z]ۛ%s̤~|<YvR1hOyx4Xgd(mIA΃Ns63vX"&AHb}|oK|ڻ_`'z5?[?\;c k_qN}ÿZkgpߥ >9+!cs_O7hILhås-_-_*yW?2,>oН[m5hc(>km<.yS~3NJ/.lZ]S)E7 G?Q40>fԫVqc]_yݎ|9/<Ἔ5u,Swn6о|!?MR#FD}#~Ԕ\S-/`qqNع'@wpVÇٯ}-P}[YӓTAst96';f]=G'dr q"+8?ǡl60']}剭ݩ2'£!hl\Sg7:=cN>nܭ忙Í1m*gVk W%aаڊ ;ju~owW7G=~%jP5{-s8xpDmgX~OV믩ћۚrV=yȑ-k𼁇|l%ыߥ@s ֧e| &.nG77M -W1*d<\#s0S0=xBz.ŝ#PLI/iD+u_;gBLjIqmTt?OnxU~?]MM r2.9Vͩj'(wvO2c½)d=mߎpF9kO&ϊ Vd~9 sxc;TUr6r 妉{m|%x0'A9v5kcF1epl{g`c?cН_ptV"=L/ ?g:`H&ob[wuRmme^JذO9giPf򯱭ٴk n{2/L_cӋ]> yl2dk6o&?Ow%\dA 4%ײmۆr]: CSYZ*pmUV'E߮1 6Q[eKo 2umCG͵|@XXXQ˗3uv7cf^l4.Fx nޔ1ٙCG}zwߊm](s շoMS87KVAMӧ iC 9_KPmϟC !gaX m9I?cZ+(ݟsYmGdzsm&6^gR]!ku%hY(mYTUlX%:(KN($Kn/},l1?vvyx\8(O_HPn8!:}<:SRi(ͳ 'GE6Qd] )<5bzGFLc܍oZ+uLJ/*j/NGsϵ}Y:*mr MF^ד1nZg6kb6~loG8{rZ5n,}-K}{'5fh|z8C/}b.ˆ_ 5kۆY/'yzC7dslN~y>O#*ͼg5zlFHpℯ}{rM_t/ƘqYzZ-_ےckԆu(ȜB\0=jX?|Bֺ?RX})l45yCی8rFR~Qy]}h-=.%rϕTU{=lc/4 | Hd_{Kq)M&(rQCwJC=,c\<)ނ;ٽ_Zؿtη)7|q;>dBG Kh*+ܜ‰bf뀹ʟ3!>ܝJVܑg.s'?%׻|?rĞyVv;G%kw&NNir'/kܹk8cAb?Szv00 J3yfbd}*<.Tjj!~⣷_m2N7_cwu|/ Z3cXv/?OO?v>Q•7{O=MR/- }y#t] ǭW峖k+ g#ZbL[6f[rp-kEOh-W k|4ῙS?cOgd'c([σ>%9~s@ch:F-9ہw ޾hX8,,ڢ&@7]VkIe8{..Ϭ/d6nsqTju"/|OLD~?Ϻv}<|NY錪>uk?Ӻz?/YO:7cl>mi>}Gg>)c} <wp떯9d e";K;'8y2B Atݞ^uu 1gIߕ_!F-1>X¶ȸh]}{V?,wrO&Ks߁6V+Ҝv7x/Q. ?zF(tM}`2_zxIX&:+>%7{xxlj[]f{.qZdϞXMFRܘ篅qs?fM-mF1ӷw)ՆυM?~/z?} j_kw^?ך5sVm1֑IlokL]@7T.,hm^mN=.ug{zq _Ɲ{;nuwvsw8=td}r]}3_9WbdFgX?yA`KO$:luy;٪V /nC QQ mTF?lpIϬg`A`hiݸl=(4Vv}{i"94*Kخz" ϼ0`'"K0pTeT{O8ݗ#yG9̓ @/k R g߽{q_@uӧigU*rCHW] Esw='dήF`cލ-ʸ}4xOJY'1%^݌5<=tO4ŷ0JYy}e{)OAYN0av wn{b}_.*UEv&O}?lK57*|Caӗt2k/q|'wu=[{ W`rŷQ]q{{' SVޟk_XQSjĻ``a)"1x̒?_ nA?cǍ=FNU53ZzbV'5W`7cQD}jͯxv݀+Ȯ{c>a|`m`j1O--gTwqDyI,3HmOW7QJ2/aDGK±=runrm<Z|"@ـㇹX{8R!l]D?4ovտ |CqNwNWr`]N;[ FiaODKvPz&qqjlOWwv;!SBly~ €R=}Yصv?OqW;Da߻-N p衇G{= X.IZd3\IpA~clSE ~cGM'˘3K>CΈ7?U;9̯lIyreOx o+?~96\۷ocZ!VMe&6P.3mՆ͗cp19o.ޑg̿lzZzOSKt ޿;wXݨbRXw2.^񵬽Ϛ>[vh\'WZtӧcl]Tu5i+ܻr2ۍk#_nbiaQ wE=Y[Iѐ8aj]ZPd2gZ2"ߟqH?Q=~d8lW-۴jȎDxt40YO}ug~=- ~2Sy W]6"uK"{OOqsff;%'17tEݛ9ynSln嚀\˻FusVqprxs]N M5~aeE6yRߑ Y_5`SSaY.I_j҉(#,'РVͭ<#)Yܻk dm0jښZԃymY_= [FqYb)8%wTZ@-mgmNfT#3:3 ?e ѕ( j'[NA]^ހ=?ǧ|>4q*+[CY\<Y4vwY9wYa/JZ{] &0k}]Q] ~ի>|`#?ΟO3/^mw.fKa_:GSk'4ĉTzߏt-!]XkTѕ+>51'Nu2:D_~Fv#rFv[x!< 2'Cwu31w |{*ӃyZ'7hƸg#@5y/7C'j/vuw_s2N_tkp]Z'͵Pks.~# N\3zw$Җ&^|kRjj|Zگavv< =lbElk15 6~^[Oڢi66wpnh`\˱o4uSE<@Mak8fh @eS+ḷԨyLʔ.@kvNoW9_&2٤/:mg~?/akq j_7 <ο:dM^x0nYtr̊y@x.3N02 +ޭk]-"wq|"W4\GOu\~˗Tffgb+ ۸0H7/F&aW (Ņ l'#slJ[?B~ϱ;wx*$:]{w{xq}bȽ}xQe?_|Kֻ +뢒boEheyWs,E9 >y/:|uؾd-\>0ڃ_ ,̉\{ƆgXJ{)qWC}~Q)'/_%x,~Wۀg<4F}+u)KlLy`yԌ/:Smj ~gqM1#-?Qu';qJt>_Iښs&gʱ?I,@ȝ3&j0eK/25w6VB%6u_w~]vvnS+ۛDE1W&olHIko'2ɯc.{ q믱7(\wG"} >C{%,5,WcagM Š׊]yEgpGV(rQK&a8s⨎UAr:<&e3͞]ԶvHo춛(̣1K/p5#bdz-O}7N]5ۅrů =5 %j|k_7eԶ?1j>ޖhs F`nJN,微HCmbX'1'u 10W?P2;sq~2nom.+X?ҎeųK]A{#lvz^[>]@XZ|Ů;~,j s2e8aүk^e(keާ(cl_xc\ Ej=џO?3\緘s1d㿫?o?_]oe6"4kr?Ԟ^1  N;ۿ>)2Yht @7#T,y_==k{k6_xn7֚Mo'g,߰}w5_c75 gsoGk: 3Z ;;۾;[._+)|gXX g=^6ǖΟ`xccjf.w}CD? c}9lC` OcN(㹼/:jvl,0}CCFtk^ Us݌9Ny0!)޾ܜC]pסuoiP7oDh,`9Py)GE`t$@UF}zԥ9gk~>/KMtXN 6!YcKDr,9ڹy|)E.t9gY_"t>l͚<'_wn %P(TWr:|OsiZź6g:2|*h飍=,TdNOLm`݃q7#fukҳjoj luov_79cY]{}c3譟n)GG#YrϯiZgc&Ffp c@Y \}|LoO:[YaW*>jfuo2ܯ1kk5|g;59_]яvX^_|p9ƃ۹voaQ_ŻwUߦ^_6<XlG :@ʪUϞ}~|`~GiMN.^LMP:&z73S2jk9#>~'c=>ذՏUwwi߿߬s1;v$#02B{7L's /iw} כ8xR)ߘ)ZfP)u3х^oL46uSi E\L^osbxVy&Q2$: i3︎0ǽ*e[kj@zv6y{0U^ƻcmbJݓ`c [_x ]I/Dg<ǟXݿI ]|h/~R}~or'M_xX2݇<zӎ/7>|*Έߟ*v./3?&e5'\c>}yTɒK}S%7X_q [ZE]G9W캿:@܂0ѭ<'M1]6 M[@W*dw94۪>[|R35yҼ(:}ŮK;W3\5x3ڿ"KZ㴧i_i:,amujN(b rr~-\EfЬYoZq??ow}fjջxAn?kNNzk 4HcJ׫;Tֿ55neW \+GS r@QOwH1r$Z3M<"s}U!СDnܮ:C#-}EǖPaW\^ؽu;]scl@sU]W1Bפԡ/y"D=!}uJZhrTV5rhT&xӵБt"jAo1|-5pPdgGT^w5pBio~!G sɿ(n'ZBy.0zNKSs0oMȾ䃛\=-c;&!▉L~Ny@q?$;5Yg{;c_%E?)kfuo_^I?:ѧ>x7n7kq 8_^y]Sܬ{_|zL_/չ]خ]l"mQ>i /YUI.Rs| /ߵMK}M,Y@hSwq@]c01[\^_n`3s͕T_\~ 'u zik/:u1pX=?c߾5wPЮZ@0Ϣ_ׯGv.,ӑrϋ}|:cK]uj85v%lm\YJ*-H1xߝqNNřI'Mf5;E֪{{IFߧ8RUꝻ{6#%_Mx?YWK"kxFŁE:Yפ+>e;拟]hÀ:}ݫ$/Dͣ.o(ipq+ӤhKwv=uTdž)Z]C8r[L8ǎE*M ƥkU9an G COIMDd%hlB^t9jp O/9<6`))NJ~D+#q5? ۗ~׽ֆ}į G;:COh0iA?# i4c?B\SWƊ_5'U~ߟM_7P@^RNh'r?]?<(@팀V.6vlJ>NWWz)g{xsg-lA%^f(ۑ}8qux []=-$Dy:՚k]koK_`(O;;5ChO[Z?Zq9HzXN80zyf@f\k/j_5_9 ʵZ;'>!ݼA&O`_5cV-΋{6<,PN!ƽER`KnT|O}}'2{ 54z@3ƚ@1ZQαrԻ bRؚ\6jϳgkQF44q+.ǧt r w:?>Z2orkfu<B h^ߏCX>N i)͍:c]VFNQU*Pb&Vx;9iYH7\(>,;f]*'Y"7 /yMapJHa 0 M ^YЭ-s ?Tc1CC9m<_r@.nMil&1-դ0&͝$8\GMҟGWOyc4იzػ@fe]f}o'wĚ ܃|s̮ܿ7-fy]|rqדB`0r=w揢k_tK~6Nکt5^]9Wc9)D)6K^ʢn[fT{uX@3]M^Ԟ;?hb3_9ئ>[r0+Нp:(#^+hc"yW0:96$>?9W/@r҇6~z]9<|t`X^s<N{{':I:sf.}|}I:}ڋׯCb@^ }/_ {(Ko^թI+C!9&7U#F?r?Pϣ˜C] SSҰ'SSfj>,cJe8N EJ("#t|?Y㵌x{%ƙ3<Пi&H8efo, x \{ޤsޏ9ܾɸU!ϱi!_!o}+=xP)tSJ<|Bti!SL<0W}W}ꮒ2Qx?EOͰn׋50=J.XZ3j5?|;%w|7Uzz6&nҵpΕWدv#Uøa=mn:tT#<?t-s5\cc,\}@R#] TEj#i)=L?$S#)los .S}J+x6vVi:u,Pm( `KG}?n]8>o_'D :04IwmF|;1|H$⠍鵢x \K>^AX?$LHn/2UFj74Q&lC:<zی'^{ޟ38; Db=@ILNL&|wKߎi}E}q{. `8F-ةںD15_^X96{VH=%u.(s]ߣm{N(~/>m7j_3[<4^v~E3Zqjg&f g4{k?t54>@&\w98/뛡 rp.vvv A?go?=;/ab/3dL=l(a{N#۞t  esj?|YQL<[";]/s?{PaBff+#]]Gx({a!A))a%ۧWwh:MmS-h6!_>G>͏r~Kйِ|89wZnR硉Zsk(trv)J{wĈm&%3/> 7 OhX@xq"&Eu@o&imN'uiJ(':̝7Qkxf?HҧqLw'|=4f߷Oͦ"@ԃoJ ۇ_[vOF;AolG~ԏqbW~HtoX|%V_Gqyq[I?j}rT s{ J,%a!t4GM_uEocM5dG03T?f#~?W g@g:UOX ;|)T]_v:ItrnXLޣgfUkkgQm?'#&Z[]||OKIX|_:8F? ]t;ZsBT0^r5@?gp|WUk>ӫΐ_gwVPE]g?Vy1o4|]_%w} V@w'g¨`؏<9.6 \wgpL@ \ 1VboFu &W(w@se:36ܤ9>3KDcDg9h|@84e~J} ӤRޗu6S J=T]aq1"ĉ$(CVtxh5./)pCX^npE_ܧh>U23Əs8j+f>[-믭gLS?y&&G*hx@O8&mk??]IFدd ϩ&I:V:}{~FKI]vȬLE_) /oS9I5A2-Բ?Mwv+V]?G#P/䌝#Dj?TgwΈhޮx%V j]OPDWlU?(wTڴv9Vsg޿Q/Sߪqf>.#6yhp0s9_:bzkՖK3X,5 wtʬṣo5|Sgm[?ϜoC71$r^o"uq~WG>87ggR /g^8FSHE;ݫsRuf |YL~ҥKU˳V6i=oMۇs >h ^K~}_/ky{ ŋdم']tTӛU{s@]ڢ;]I8o887]-&itq<}* !hh*cITҢA6':Lk7OP~eX}̅ǿ/sosp? U=&sp(iԋ+\D# gFjNo'(ڥq,I>l-ptYe˄jc8^;a 9:hl;߿i}Ycv({ty;cR[c: Itzr2Zl_o.E|j}5rx;}ïlRM>;0KWYl9s,6^k40ߟ>F[{_uk|Ik\x=]gF߁yRW`h.;^S`qS4C?]N_|}'d;Xeb&&B3~yF\&__Z[L`̓Cx5 i>ا%hVZj5h6i:3ۀ>0K{C:;ϥh,?OxԻMԳ|3gLI[W%{De7.oӧT)r}XWun&M +>=`/6 \@(  GF +5nγ~zNo: zϏuo$+fb>/|:3۹d5g|ܳYɖ|nYԚKKz='Ǜss)%IBQaȿ)Q;܇6qqh48okz8Ro{,pNm+vP3`}Ƙŵ|}kN\DѐR5wӎXC?>soeDmzէߟj#}8=}Z` C_my87oqcp|%0Wڂ1t~SbݓscZn|]6?,:tb?5@{?g5qZD'Nel_y~dܧ ҹs'k!#zp L36ޡ6wiKs٣Nu>͌ѣS&s|JhݘeX㺸f?g5Wgbz5Pn:Wl\cӏ  #($JIJ/qv< o<<u:vu^qOh7`@[CMʼeo}@>nߧ38OVrL{[ 4W{P ^s>rhƚX dv Y16AȽU%K'\ܸ.Mewh do|6d/F_<bNhJ;qHM 8d {9u+Ple\k 0#C--eaJ);e? !U+z*+To88^(PǹqNECvXsǠ;ws5̘D,;t|L.TUZ&su^' ouݹYܱ$2ۀ>GT(30=~a-XTaaH?Ob4z9\Ә_KOsK;n׌'7shLOX:h|xݾѯr OlQ탌}:({<yڌUVO` z-2?=2uDGx<^-~f9SfR=^q"yV>D<\*5j |)9E!VyS;tVCy>}SrJ7yޤSDt̀P7ܣ :hLx=u߸n_1c1}L5 B5u]zq.~;)}#coUS擄[c"wg:D_9[,^.0 *1.GhNNU^;|` }ssVF%PëX{]nB<{Q$e|Bs݂+8U %m z1,ջ>g^v-|Uj;&_no ԳˣخcQe)"q-myfJݞ܃+ <,8,,. OTs˹/=zwVg+~y_rV%X<݈Js)G+6a=P=,S7ddC6gğXk"-VYt=y/GeCNbsѤ^?jPSuN3.,fho<[rV9'.cc:Q<nt3B0;lʕAXzC+w|g7_3{7d;2_0wpxj#o|A۵ܮ2B[U.vq)o }d|՝2GtbI۝:QF?26<}>6c~u5/٦\] >|嗄zD1_}u%Ƹ\׏/#zpbԣt}ۥElFg]haunՈ>q--gLc>mǸsȣ!˅XαD5i}1d-neϲk̳ r0˜s?o|xѣ =˗)ON\ZIcz{)Uq 3z!w__SvKY.=*WFrJw>bn~D?x;մb50+{i )}ё /\j:ʏ7@/Ƽ{B_E>3s)##PYl{h9 aD=,ScŲ> \ 65l1o !@l?ӭ l3\zIldSm[C.]" k[#IGXtฯ25C=,X<Ѡ ew6p&-*ѣC緫ˈ.gU$j39~0v}q,TA?lY cQJ^'?ܻx=|>?Y?i4',۾ۼ1Fs!ˆ1;}Ɲ Ua;gW.6GMN:ON=> iwweZM0i0솜9'>`8 JVX΃1@P_C]֡>m6o>p?t4VT'8N1oDoO2D$|_X}^6rKcxt_m16;Ɓa|G:4ۓ<:Ot|l˫S#0w9gRP}mv-q8$ߣ7:yϺ.-.u5=8OTZ4~S:{;FVO/b=!fl}k]k+B8vijU16ˠ.l_aؾ؋}LY_ jڍsZZ:4LX s;%>}SgC܇g=S?A[.l`ĞL mnDܿWc ??c[lc rxLk}:71 _5׆\KcD[&c2Ŋqy\駄~,|Gmя?̙0)Aw>Td 1umvs{rڣ`L;^O/?(QkuJgetG3/lG\'#[>Ͷٖ9`;i٣Ρlg|xY1+b YƋ4^p5CBOT*̱mV 11ۼCIpVlD^Hgq`[hctaJ_2iN;b^w%[k PuLj[,,E?x4\ 9^7,aO= ?C5oO?4l#~ë _d;K sIVڵe^h|$ X6wP.^fl_p<3bmێ 4 ؘX~|5>NbѥhLϏdbp 9p#_G63smġظ!# 1W=:z" fF&9h.{PZ t"L\,uDAׁM~)q0FF^!>N> ~F?QѱmG=¯1}9F)bҏ謗UD_GwMۧrJ*UskXet}?{X&?mW8o\/"X,:.pT0 >r)[O/}3OMׂ.XY}JN9 :|S^'{#z j7!ֆW>}h|_cKl&bX zjR'GRZoF4R cOC>r?GF{wqT Q`j8an$S7:FeZwjf%1vCbO͉~?A~N Y& \OU^U[brߊ7Sޠ~ (]9jlZtybTpo83n0z wL@rJ>~`ILn +밬 `Lc$v>4.0FߩN ,.mRng94U}f33+6x::{\W9gu/pэ~u Df-ØqدA۷#駀c:vE|RfQEt6cu-7wifը\ :ҥG>5fUu_~p<Njl˾Ȩ5=zs"A66dfB)͵l+G` 3fۯs :D˹Y>\HW,RidX{?`[,:t~1={3~73~ѩS(0?> A /7ӧG!%.oӓi}1l 2\+~Yxqa%>eĄ ~'7X62UINȋ0Cz1ѷb>\,Uc }x:%z[1~/{><{ МM@h]?9WcaZn#3U`'#?-ӡe _b }8x_w +$3q!C!=Zi9{QpIȳZpmz2=;~y'bPNcg6,{o^nf_*8Qu}';O P|1Hy?sڡj#brզ#&7`YۡWaMr;uk}x(y1یo퍇#6̝"0@Ic..\gGb~\ku1x{ƑIr |jѿrDS }wt1}}B?qCgϢ1nLݔ}fC ND 烫`@#3~ FI8Gp`wwzFytpx͏s?rFJVf.^ :{27~ԧ>e tҌ?fjv,,v,vؚ+pQOiEO8٘qt}DfG{Gk;#7tS .};c_w}Y#x,1ւkJL{3^1e?\E4 -D׼Ӱ)s+%[[È\'u 'V1Y?r")-0v|p2W0`3r?r7IOGT@d_Ӿxh>tC_ . j]9w Q^7om-%IW'_`4Z?im.}qUDlY1^cIMP h YAhc>]`{rVsn 闟"zu:>-jf6?eܾ$b~{3:q}+>}y:@ϙܪtEF'=:7)mͥ.u>G?1(kB<.Y+LQ_ _ 8*dp* ROxG#~Bq1GA.P޽=CxL c>`8ӝѩVh.51:oT9~ig7YyJAE`{dn >Xp=,L~?bH,+O\$-y~N;@l7<ЅݪwLj]] 9%-/YV^Ѝ YYyl?@sq 89ǾxH~Lx֤ '_4bv=yऌؿo&ES> ?-1Ck)=[J[ND^4?\i\O-o|/_֏/vFE h}1kNj9yt"*"?eolkr+9 @ԇ Zg1iPN?s{qg'@ `Dbrj@Éq16 LM ymv(ԩ??#quyPKh{qsϴnw(ܭG3˘/x_l PbJ!]:+ʵ ui?ܧf{OktH1b;S~nӍl=;o|s'' ֈu]zK;4т:6=f.+R:; ckLwkXF3i. fٞ]zO7&X#/`uO X[Λk2>4\M3ǁ|B@K:sd6q?=hDC|]*OCݦk?tm8kj8mPr-Wа/dV^$o=ZjiWaQ?f?Jr,F(' 9ܞC O7JĭWL-[r?D?˫i洙:3~Yޞ@7l>*fnCܪwqZ([Kbaw9;x-W,Sӡ`Aྀۚ@DGRLdQ'uzX,DN\߿,l&t^+ry}ӒY6ǂ>\M8?1o7u_ԗ>ځ~(h[^[çJ4pZD/Nmt ZmnPp+z~x(9/; Ԣ-ۡ3vlˤ6R ~{#ːK<78ȓ'8ˇ1uːќyI}V3gʺY n+6_[L7ztG 5CB4ZyLRggl+08c 1y-mXOhuB|Yܴ^z:]Te2% ,|~ Ggl?E…pa\9^B,kF&koXe^#/Ym8*?怮_1;]??"++:a<Kq/x\OG\z`@!ˋGj ><9 rۏ"5ok1ffcS5rt~C^`rWV4?_Tlup~K[9.Ocrk7h~$C3ǘ>S7fm"\/x~o,t,po:d͵Ŷ:q)\ۧ> ԿJL$<ܚfK>cj$Sqw_ܣrZ^zKl71{?Z\1f9N|RCAPG?d&ӑ7QWc%kKџ/5۔fmθ彭KGǟZWD u>:Z_?2V>lU|]k|okt n}Kt_9q{!~$N} 7fc}y`N`E0Kޏ75}[W x~lxڔXL }*_f^mi?+OInn͆}3\m?☎mPfZHcC'yw.ؤٿs~cܲ+j'PPm..9n?te\+kM4h䘯0u"S$t>^Xy`k6_k0UeD?\dtJ^=mX/aib87h<zB<0~#Zz0|Cd8ӉK[ؿN*o@wغ?][_{c&˱x[׏$cc'y~huE;~;E?ZG Ʋb,TQÍu=ij0[q,1S'q |L|ޑ:;O7F' !(f㼷nͺ525";=೗r?[З:>ĸЯ>25P(c9|jݥc:,UWs4aI].<<.jo$|l?{܎׊fұG }Aof"Qoޗڧj/7/m8>1cRxV_ˎ6Z*P8տ@pwwI80Z{Q>~$̙5tΘȕ]F+loNj3|x@->d|B:]#*߈ܩf =gX{tz* +大VP? ͕JI}o`҃pD_jBy=O;<WaIrS4015y啩A}89Ma'ƝpsDDtqLIIx?;>~W>|R9E7j5֫:-jCGdb&у^-`= X}5yZ\|  kd MZ!>+$̅K:?D_2o_-@?7* `6<؟C~W=}Ls][{b~w4?}5j \fĊww}N1n|*vPZ'=p4Gr6uhC/L]V<9+\ق8 iDc)Q=vƍ.=~ܴ<]艾o9=oqhmXLGp ^;6u26}[o8l/o-G~8gyE]@;^q T'O/ӗ #^~y#/=:gY&EO?Hh-3.O^PlS˶h)^q: chm0{"6$ LfStu5c2 (JwƴW !s.\ES8NbW0n'N6DTGm~I#=q1crk:!q泒_Z?xn|5 x/86_bWktLj>|Uj䶿R'o;诧FCfͪQsL7?`Zw {6=NW"#_x+־׼?ɿiT΀Zc+Q{+l-oj=*Qp9k ^i耺?Gq N|o!_aR#\ҮhIS낌ﱲcNlrqBIpY_ _L}uZ"]}Pxq1w9E`z kwt `bN 8h/`̓Wx^CRQQ`Fr7vkyzƎ/j蔉H?`$uZsWW]~r|x/|jBz<@_xY="?}/goo ľ;wnN|F8^ϯO?}i4hМߣ?{b#]k/|-GomvHәdc9(|2olz3|iSLlM_k7xs_|:<,%5l?0ƒϲ~s@?zޙyj){:u]E:@9G!]zӑa$/vRv[ϊjC|߷{ -pd}}0p̷|!lCM7h3?V*jZk]G%SWxIWM`"VS_m߶r˿m[h\O&Oɉw6_3iA{OTZx(uu]\8nM_ݫ]9q^.@^֡u:z|04pS˯J0s8/( RzsWt3ӕ+uM;$7o iotvHދhtbL}8 <ӼH.}eC0t~8r1NئlnGgN-j1} ]踠Wacz)c>a^]m<^tUYj \9CgȬ\euLJcnЊؚZF95`}H9 ě%pOeS:UG]BBoЬ95\~;\u|Ìpi,Ͽ7#+5릦{ .cEf q/qo߽|S{Ko?u"jl'E_ϱsٱ#O֟_Lkֻ?8}Vm"P %zeu(8o.8 >u'Y^Kϐ&':@.z9F0|I앺I@_:7dkU}X \ApW;_m`gk Y=1^`?YZy"S{_?SޅO{wnž{v2ʟ{'>-+ed_ħn碵^B3,obHhnhvS3 1U1V0>XvRt^(`%0uM='5M$kΑCeYF%_1~hD׹k Ʉ)j 9 XĹh=jOSj(ew,G;t.qB)9}CwWW W?,֟Ɣ%>ǝ!}O+pb/?YSIz&u|#;^B'|VяWj]`YP }}mZ6?]0i<0xHngtUz ^gں>pe Ft#K^ j}I?G{1l% ?((VgK AV|W ?} `$uWWÜ8Y8Ny7q9oT7~ݸh;Yisu:Ԣ)ɾ>:p[vz5 r .8E.t[[Q2uX{ڍh?O D߬矇!/8hM/{r>_}Uc]4Oۭpw-%1yr=ݯXQؿC_|ql9Q^ԣWF]Z[3D=1lCʯ5 +_]x J k^_Nw^>ӛ|c=~NIlY䔗"Ē7nm<7Z}JUz0myhgnrK ֧R R(b}>~U֛R&oE.&g" O2+^p3 ެ7ZT) p0Vh,SG[}yrLooÍ-~sey5M{vޞ ӵ& d>r.osW] pl hVp}cuf#gQV hۥox&v ]yK 5~]߿Ȩ{*v}$lCP襶Y}!~ؒrFgpL@xثBW_Їߡ py޿W\vTrďX y-0i?!/~VP_J6>LPpUra$@Kp HM\뺾87pP+[q/v'qUvjۢ{ ?g`/ W1†R^"]z)A\]cW59ʗp˻ݾ[a}aƎѺ& rk<=>%^ҵk΢vTG=n=PKÄ>7R?VP'_Ԧ>;vlO⼮xghoc;tt]9XSu[.9Qb c~YjjHݽ!&ocb^$13i3$/p`sjþDm iaAR>uz4mJNQ\cxTslwAOU䓲䙿gUlUQBօ:/cʍc s:¥s8WOcڽ̶#N#)4w`I`yۜW#:[L7ʴ_hBhn٫?Pky}<~dS%lώEdmU9۷W☞8\)/>j}^ ;\R2n?(0X;Em>{N30:p0y= ?IIe{S8unfSMPʊ[,g{qSݭ.)~P95.\@9=D.|Xfk%cRN9u_ʹASн^wBJgJ<1ܛޗ~߻5^ܧ7ƴ1/YLü6.,2c9qoК@ :^hv~!8@yAhb+"/7EeO?;4+X8d(ygM-/ҩ;c:IT%Ͳ{'tTL[++5 /ёg){f^QGS֗`=` ?ˡDf cyReD-{+ T-W7L0\67c佽PSu-ȑ O?^ww;tNW0f*mXt j\Pԣg~mCSXr1d<^ߡOpIijCkERS9kc+qאM|9gmT=ƸrL~ٽsz}[᎝@hO֡!u^|Ӽ^?س6|ˉMnj7Ct.|aoϹ(4h~?OTz=/]T_hOBI:ޟmH\X}w$?v)&kOlNo\7Yس)LQmW,?Ms_*p5=E|8p{u832eFNlޭ4]pP8xu9^1=l83dS^oSfߛ>S 81(t"=6xLPx2V~[OqUc.5>ȃl1>36}{W}p tLm9{a4{">_ KN~G@Ϟ_Yoر mqm%Gܠsl}h6hdٓwYӕ[WS]L$->c =p"AOhyB'ƣ|_.Co}n:@QE:C!/S\\LVd^O&lLs#zbQ<,ꅟ]x|١[S1j0LQ<=2T]S7R+i~f R,u>uF5܆)1.'ȋ1QJe\jVlޟ^?:>X=4M=Ynhk\BĿfs \,帎u%&a5!V^Jk2ϐ5tyVcߖ[+FoghY^$OS hZ$1Ge1.Wy7?_ztLs߿1dkȊɞq7{o㿗SL?`Y&yؿMs~O8ˌ ^) WXJ@Sd/@pMp0&[8wpz^GylC)@ Nr(vZxczʓ<$c57N1ѭl|h1\2xkTE- %ዚ.p> @}OS&d }FVwZ^@dy;uvBNuuc}ΧUX p~O篔go~#6)%[nRzSQ!˔G56#7=)֩龳nx_gAtr=c>- ehlkZeLnϑ7R}3?,'jh|,ב8=L|4:O6.j ޸yUh@혁(ubٓzeW}x&8~[ UIW;6`. Nr3[7ߪ05zvsW^+e8ha/e9'] sEHݳ{kڅ"GHt!><*(V?/@?,1k${>}C h~5Sy>,_}ё >xAEMr'ܹuZSXr4x W#PkܢNڡt6;#lӿc\j_YeOQ1dyֺpV;8| SwzO_ƽC[k``ߨ2FsJ#ϯMqLQ'ۋhJmRy5gjݛ t55Qw?l_qy]&G:5Ef >0O9O?pNO d:ޜݔkۭq .p|rYKieu|m?w,GV:>on`pL p{H~iwpTgp7؂l[aI@<8;;ĸ5ˍs g8k+7(Źgʯľ7ǎ:<†jlyNjo޴^uQп0~\ a [u Οҵk,k!*Ad t1#ˎ73u!%1ܷQi}X9cə!m{ ]6_vnv隃sc%g92 gw2::'k]ө+3H߮qݧ^oWfNl%J-lv9W595/H?>X6ۇhHcc '5|O#5r[DВ~??}3pDZþA z5ݾy7`,l,`_z@ƶok#ڽu1F^lc{lC9/i))>w=s]ӗ?/~ߡnU!^#UP|Z~ݺ@ymc%~-n _Ӳ+I X6#u5@߿'G֚t?xn\|k1uNp/Ue2/޼PNi߿ gi^íD~a78?b[ۙM'ם2'uP,.4ums uP 젽߅n~4#x8re/P9Νn N-z,VX[dYgH7)wa5h\~W' tI,lL ?g]q|{  (SL]@CW&@(nƏ?L/#aޥa1g(ƶgT^b\pM|29%tVF7Y{/>|8= ۃ@Kq{׎157c?s6e4 j @ô4O2_Z&G-h6~;w}`{> Xg'効fJZ谸FFWtdʓbݤb㘜 |zGtxVSu2cI=ވ9 OÅ13cƻgk\9UKؙG#wzP#-[-v/d&E'? vΕ)<[޷W~gND GtG'{U28rؗk}#^@寱`D hfˢo͍u콹%ocjO \W=/ -9N/ ߼=yoȞ?o*d>HձL<2W}]-2Hb%:] ֨{*f hک"|h4\]rmAgxk|zw}LZ9jp zw*WV.k@17jW{x&΍70 ]֓=ž@oԮwcO r]k]ױF06~+;k\!ے%e<1۸iF N>`S31}?Ke-jRf5:J^PZ^*ZO_\8>DAW{_}W١G6s2SUٖs2w\ _vyX5ԧ6cyjBy1ۡO"ƫU!OPׅ#QL;=?+q2_L휈XX]-K] ,;7mZ ߣ#-}Ⱥ4WJ=ʺ\MFr/vϖu?X{?r}ot])w`mlMA*3@(k|xHXMV-UvE1~ݏqpd}5+m.*JRR|<q$!NqoUo}˺%vߵQL_ Nˡ1:Is.zDj>˧pY}?5&bp6MycG-?O3̩oL0Lu}?E }>Ȳn7@%  ]W[9nALzYZnhq(loYi'NmS?5y>.7y a$vz 6]HlG70<@[zw/wz<ܜoW}A)3}{Eo/|jn_or?@+Г E׮Z+&+zGFlK6UT%YeCi[%Yfr>==TD0hf}{DFCV+d|<=)p^'/!t)]3a~nH#zqXL'DҹPq8 r{&f^O@b@K| |_k<}2'_(/A+| WtcN"Z?AD9rJ_YIp&֐j0;YV̛Dq{{BǏc8׬j w5cQf$&<8<~FϟbI&8/]p׶-1FAAywBw4 3 \QSv[#:|H?.Oy5Ml-@fz_ғ;)u6E)4s\[H$7ܓ'~D?n#3'jh{/}d5 BoEnƬV0/):s84?W~[*,~;9޳nƞwmOAν_5O妈1O@h{LZ$TmMhݟ ~{u#%?UHɬG^{Z_>xxT޹gTi ϼ>Է}?g3:t:_%j 5>4{ V<>ZԡcF8]Σ,('ӼΩ? M~ '$C<>131a|d3XkQ3s"5 ˣ8>< ^U0c{9oy[F 9uҰOnd)9p|'X3?c1Lb3Tr 5@=8TގĉYa55WdFeN`TT4TҊ Ghb};q1>2=d{m~Bz[!^t:ER'` 3 ]|OT_"C4E>{C 0|̿g@8x\ʯC~_:mh2Kwpףc,嚿:g}ܟ/4)^}$c{ǦojUxv`t[?l/ͣGo2=@P|`bu Hpb/;S7y9)" z~5`:Ho-g/_ڌyc0؞sL|V&8| BG28DW>\;A8ȳy 2(}w4ڻ?j&&n`dc; ϘpNNgϲx~hV.YSs<4KG<ƅ@p˸ЦFy~G,iوɿ6=UVkN+]CT|?z!?7g&șFI9SoS= |y~gO}>/>E>"A)-Q-Q3mҹk\^Z6:~u;[Io8oʌz`p L__^?j\R#\]͛ p =zV^Uj2~SvԷ)5.u48/z1}z¢~W^;z >>:z;JTUL m[V_zt5-CCo~)l=u f sqs^_u>oXK;#Źc3q>?#6}l̓y7Xze:WCy,~{jf|ٙ0wJak :iϳ`N{p K>w.]{y] ˅?TO)0A@X@ĹZ_yZfkt| F4C׬5cB@8 vI'sm|{kjqّ!T,\\K_UgС+WtVMP9{f :>b=zNt/y?~dyc X˯M8kԾ'KKU#HQ^>mS>--sJ3R.Me㪙}ڥ #:WBaHYiIBگ' ]]X6޾Y>&zS̺YZx1̈́V 5~y"f z %o%:;o$2gSE~)^ `4A >ǥg}lcPۋS]^f7e?^x>_ߎ/^(SsךryBa*lPhp?,O`lwzҥ;X)α?JVw9}nz9q^rK=th-ЯF@:oɗ9{X_ŋraE=i(^/1@yQBtD-:{GTUve/L2>mZCc[_UMD溚Kze/3ٺ< &u~Hw1V bŬ\sXyܷW &x+0ƴvV$|`ܗ>Owfu\o?8cpHoV*K^?k߬/_߇7]O> z?| dM/&ss2RcR&ډ?~DVszxzI%j!SA+jYj1? N׾oE(A22c;Ln1oyTTU%9uEa= ;Q@ͭvAK߿0CMikk(=߽o˲<)2;cJ(EQgY9=Y} ^Gi]ߏ;#rӻwgقXl9߂{ɏ%#+ۜ^/[ɵ0^kqOzw%uΟM+~5K_\0cp~:[<0Fx\.oq{W|9U_)Drr+l%=^Q XZ]\0ޒgFBP\8꣜Zӏ>}ǚj w7_ȘK,^ !s_[{xgKƓ ~ǻWy\WTV9b}?,{J(HjߏG_qp2Ww Va`mЀ-X·NCmV y-%\\yw9Q%Gm0Sќ ?㿗+,x"?_cҩ_|:-k/1(v}9[n n6֬9T_|?~Ћczkj?ÇO׮]s6ܗc3Yd>eU)1--f~XXubp [{5%>_+ޗ!U;0n|BNb\:FΎR}8pM^`rdڱ] N- K8+9N D},ߔU6bZ3]}a9;H'~Kf '}o_-6h}=ؿ1߈]:v ?nƯuwc ?kܫF6<x!LtxxxWЇ/~鏳5)+67|3nEF1pI"9c퇃%G7o5VJ)t%(p̑X31ǁ9@bĬ ]%^t%OW7~+-]X߈[ ޖ?OpxP,\{ٿ& wox%ǒZƹ Ggǃ`#mS3*`ִ%q|]b%]_a*7p){ ::ӳgSzx!97+)W_?4Fع., W5c޽ Z[S`|(࿎x8oܔg񼎓m6[/L{97sQE.kÇR|o`yl.V]7^_ǿڞ6✦)eQF9d'b%0ݍr'l ` .jaV/Ԯ+[=b\yEBQOa{Ğ%4[ހv+khcǙ+ܴ+c=?Agq/.4ܧ,ޡ2q$vn]9A3~s>^ÜB&]ξ .3nYIctv\qnxH9,+K<-pB[тm׼uH9^Z|w/}M=W=KYncxw?__ѷu_ZZ|`M$G ﵾ܌mjcO4ic+w: Ex`-]|Fo2`w? CC7&=[[Mu= 7K^+58IߎrɚZ~U<|G{_zvfSm^'#*1i-=kk4^)-wC.4_dI&pW1vw'\ushn2w%Wj] j^V޻;xqJ/_c;i+]lA7_bj^ǀ1MGtpu)a?#bx폓^ }݌?ر2xa7*] gs=Sg Vtz#zE@;{;l3BEbBtI`*~zcD CNjhBђ1>?,G8;xG?ʷh=עu)$/T\+ $.M|8 L+/wsz>v]<xbWo~lf_7:Z՟2zAknw_6z>jԿ5yT>k51cΊ^ 76Dv]Oόm6WR_y˓6.נ0)͇=ޠND _/2|m>heD5rK-L4߂߅<=;ӊhhظDI ot'x|wWtPUr>|9y6ⲷ\ZPT4=һ{Mѷc|? /rCͅ!-NbTYj^AӨ2zn1֥=͟sV6 $7^@8tI_\1f<?-2ßrM]co~KM:$܉xɱk9Cwm :HG]^?v@Q\}_6_pV} `-oY?_#ڣ؜hIHyB%AФW/;7|o4[#jxVm(.v*>Y 0fxC_υt>Q]̒+(8WD+Vy__PHHþǵ^G9=/d$n3P[n@kH8h*x}Ȭ]xqMV@+K^S|N*X4Z FK=+TИ>`VA Y&z jrYۃAk:Z-{ss81>]Sןjg35C+Ƽn8.gg 0s>%ݼ9$f.WmѢX\q9q1N`<o_QlHϕtgs9^bpn% y\%En?"0ziQbZ 1C4&ż4O\쏼nE7~:<ؿ|<~PܾO֖eV3Sϗ6G-}VdsET]sAW-Qty6/\x.BqtIv1jg~C^Z>]a;^%73[^f}^ߛk? hoVЈc^9}^ 끨kcF̧} DR9: cPJ`g' fL0::_\ڒO95o]j/i:_8>gwеs ~>}['.`|`hMӡҗ_V|;Okpùkd)DC0&AhZ"J'ǏNÝ ]*=⹼ G'wc1H|T~Uy`燜~_/7  6gZU;8VĜW<]Ӈe2CvD-XؼE!|= W1 g5L9^-y|L'<)=*!iWtJ{׌G7vg4B>?V|F'̕nN[##g1# Dl"P V0,Ν>mv0dnq6] 9M;'1WjǪJK BIR z?{#Jg3׽%^Ad4y{9u寕P_ \ (xI_=δG93KCOֵE WU6ۚ?_"v9xc(Z ;ܿ[ ]_}*h&ߍ&ЭS\mq?؈k/z?8hQ?4_ٸhMl+|^Z[QobvpHr#M~=tW|Jƶ †+/}9`לD|j:Ї;1#f=&mM j%[FB+^N9fߓX$8? d=?wsFqY167G'Xmk>W_VƬ>gyy\=d-y둌*Y~u+gKz`ߗRUihZVKヅ?c3z{>j=٢a$( 1 ntU|D&P4<yNڒ?y.'&aOrrqǗ+#v&_XlFMk?NW!n춚f65Ƭ^cӳAjAE'p7 ogYJ6u όuAnxmơܩ q+[cV7>tS:4'l*m#:&@'B\xw3ƕV]_yb4yuf~j~/ş˹hՅ/_T"_\Kt:{~5R'uOm]UQ6}]ha6_e)U([Z<~rP?`NMn?p+)'el6 ? 5^>y^!+o[&?E=ưf'u8߮e?Xf''3?莌 ߡW~+1PBtiպ^?'/rx/`̖39zC= >n[klKZ9>QZ[_`Nh 0'٣:jp[8o֎9BjU'21Orç!\hPG-~~H\L)gYf/#M}2ރz8AIo/e[C דSBacgc [??W{b)htI{ye$42 `|bx&ڕ#r\6Z?>B~́ӎm.{LZ63uS[ 8@i$ﵟ'(=!x|*KP@i]Puuz_=| ׁԱW[bh `M!OqEħO_"P`~G 5ۍ(?c6~uF瓗t8}I﷧n* T\S8[l}OQ{g%߿J ʬ{ ^Z 寻H<4+:ZӛDgw97&_tꥂ 6M:(h?ڧ6 Em 諿s]绬J{5$u}}>txXӻw!Ljk;luzt{A"]nޜG;ԾDSUM9'ѯ0ͻoWT kF1#q=lB#o۲n4 ѷ?XjOF\_F:<-昁Zj2DkX?WI6yYcU_{:7lnKv~}b }q|c%2^I>$6r,WEx&=?t`/"X1`a-#68ċD "} Z2 wkھqi@ڮ_hx^D8_. 3#FȜsmO%}1H3 Ur_E# B![5,g=M`:0Ǵ/rxjU:<+'/h=Qftj罊n1.T~ 6}\,g}e3>'~xsu5A3<9QwhaS^71~_]M? SG4,f'nQb~s5p9+6H5-ZB9euq;KohczAE&k118峂d߰.[}4)@.ZwP 8osK@s(: {{Zګ 8s 'ma-j}miE}&C@fl 1lm9mEKsL.ǼpO'=X6h{4E$6FOluDE9Ff5mŴ zg{~~U/jY͓jAmF!c6@m3@ּucb¦,aWfR&=mGsul2;5ecZ{?W4)Iv6zn?όR}}j|]?տE521~?wl@5c r+/WϞ /~&V[?3>5hSED짳uXߌwV}ݺ?_k6@[ _yt9@/P~Vy>ڗq~5:@w?~`@Jx}3>c]B31~w}-:YM@8'.qo@Ҟ2kW$@&㏅*˞YèiX`.c=UǹOKZeb1 iNq Jui}E}XУGzsb#;]-0b(p/蚕߶^|^{rאnL C'w%bﭿ <XJk:t͚ζG s ^y9Ӻ d?7 /MMdt 7`\1Q75LBdަG\mE|O9t̿sWzHLvv2{RK1je.ţG^?xI3%Q4؍y<y^550i- 形VM`dG9>Cn^~Ǭ>AKtĽ:gX݊ b["×1ȌOkS=[Ҧ?XE} ^ ּP޿Aj5~9VÌ\-NbblwFGnf5>h{K.gx~F?0/ylrPݟ9˜su>xojq1R:p ITr7}}/Ժ<.^VP}}ߤ\D?WtxHl3l{OhNMEC׼:G(eo@;0P{a{+psՐݑD^Pށ|`)yY|$ϟ` zOxϨW.+ bV`gb6'Kz%w4&vܹ [&Er}t${ω Qs48ZjWtщ׭C"~/i~p{|^oȠTl K()w=>~,e_8f?<=KȞei\;~7O5:x%zNs-iܒ)ů_U ={vu a/?hM[QR/id3g;mFhQ |_~d4_Àfwힾs*kg>bۯSVT>jYF_>!lҧ $cOج{(ne$n4nbX^~?Nx̎7z,1uy}t)#2zJ[L/ݞ^ 1>^`Zߥ~_ =!V/{?.Gs \:P6g_{_. /`s&{Pm^Wo[9O췔7~wS @KE P=fjL෎_417o2>B#h]R]|f1]68W`;iUk7٦bIjދuB~!:;[ȳZ? ? ZQ`v;Ei{|3?_ z|)uyV\5kkrTC3~է 'v>]_X{ H r?˥R3a36\@h׭%~j#r\jd~69+k,*0>ftQE_}Hi깽c*OP]i _ϗy}|ؿ~B;⁉_2mc_]TW˫K}hs|o-9|4G]E޿ԍ4w7t흥Ţ{<^G'|{{`6{L~POn@5m]s^}bb\pׁ[Yp6=u|V`6V&['z~3?G9+< lMc g}iSU뇜1k.;(}˓Ɍ<8K2ApZQ_]kZgZ4V.+%Y|F =~E(u{|ůwDDӪIEk]LVv_]】 ?lWMvquoxs&sgG -"o}: (g)!ځႚ:t]?dοjlT{vE2O:ӕ+Ky3[sO[sw1ҹsXٜKzxB{i0#[[u]qwvNAaO׌jsZM JxqsE|Y6M;Y#mWy`|[o1眵6E5ufy_ {+8+lTWcɥZנ~Md}7uSM_s7:_{ܿk5E٪1e=}AWnoo.r<c;i{xZcUCp!s,-]~i tj|9_czc*5~Z.uMDtY?@|T,<\|lX8c`EGGu{+h?~hzԭ3Z\S|<`pJosqg$}Ҵ^~`F7} k,;=GczĄoJ b6_%Jbۍ!֟e`ۜCnUjI`JmJn;uEךrz#bW8z_e?ޛ4*B;@Ӝ댼TTu;]:ߏ2?pޣFuJ-:&GolAm 9cpܜづ7n LujW~*6Cc~yBo˟E18|x`/`l$D->5!Kz9||[9]W㴧Z}fOڟRre"g"/_FbHۡudA_ۃǼQoO$Rw\~@/9g 7yp4DUZWߪ/ڹht'ݿ$N߮KԤc?iDy${gqt?Zuܡz3$U~wS$R~uxWOwo gQحj(lrxzBoބl-4m_J9fodzN Ki7SSh^? lcM^nOٚyԽ}4[QZ]!b|p|FrJG^3֦uǦ]8}}R^l6~IKƕoRzv(6,V Pgo\nID[Kޒ\TeAnzo,~n@ޏL&lQ+*ڦ:H$H7|:V̼o10;2Pw9|t]~} X>::MpxM9Y9]5 ȅfṯpRDzNhׂ9x>h.iX]s}-zl,1Z }%yA鈮_E|؇y =pz-Q>k]zeGڜutc`z8h}]{U{hq)z_kK\5 Vݿr6GHsb:߿~;k~l]&79u?N^@ ~@1T-`>o"uʋ{2`_ Ohgu~4ZgÛ 2V@zm4< Ŕ1c`ݶ^+>߯>@/qnb-˅"qS>nOpcx-]? )}XcW_zlU8~a/pZQAW^t}@;+g=Tl|5SQ߭O}> Wo3?Ǫ^^!(<>j~fl-jegj}i/Jb \8_׍+$\'j~p؇)}1xlJOkcu fjS<4)Sំa&>MΌw&쫄CGB[/:}ld{|-@p=X}\Pu8n<myi~/AoNӇ-~}>wsSk{AQF/+}/վ9au<=uC.9%5#iq,@isE^XM=_qV[f{ꑫV;|zS?iݯ\$A.@z3.Wa%M)}89SZ;ꢦAXPh<,Dm-ˋE`o\zBqT^~.ۤ5ϛ Ϧk9;anɜJ^MMOVp]WVvPyV̀6F%#UG>Ӊ3-]I9%C^Vz еk!]gk{'9%4^W͞}_ho 9 `!95 -_;@=Q)SpkL}i.{ f؟)^}_ZD^jEhF{pҡ'hA?+nyw}Vjy}=~ؿG j.~'Fz[5& Z}xvb&ڼK/il1+jGlr=^G=ϭ׵O w+o~4gm޺:.WO^~[uj.u>Ƿ1=p[GN޷Qr^{%n|j<>Q`c,R<о@M~a{KWTt>ogc1^1lJ|j`jNDcc~OƧ8^Fx)GNQ*Loo5%aq1}mzV.I+޷>_KbmS4=jYEź޻>!?P{sxnjwL}vxRJoB' MlkOYLoևج[}?*oAUPٞ=5+ zd{ϟ@/VjZT]n][uUqNӐX/kj圈<9̭)<@azQcaתsJoip%۷E9+T }p5,ZM T9Eso~JSqg3qz6i#I7ߟgw5x] M=%Hf;; &>= &_|~ׯbl<¥=ݟ׺ o| TǺ_U7:ˍY@;˘6_[ߪS_2<떆Eh DbSpw{~ a0Z/uk#/:@9\G~@?1|FM^( qx@~6#}k=v7G`> ֭>ݿ5ܥ~챚y>ooKWp\ܟt8y=,86]Oi>? <5;yQ ]:텢{a{ (!Mn'TOm&jdc+g+1W@s4伤YMk `Oj9V3,zG)k [sa\] }>w-_.E?&FzVԞ@ 6~j[7> /wgۙ!-lݼOWؾ'kvTm`{`b&؏yWJ^gcZ *| *+چEH+ZLԼrp˰Qaz;ڇOel=Y"ZTAf|釛GC)Zp{[ Z`>ڂƌsYA6֛o:ВD|39^iZ>\JӴxzTt ͽu17T[ UvoI&0iɱ̘nYе샌ZVKg33{l?L<@M|&>@<?ty'dAwS*glE7>FYN6iR`u؜=z%V,?obR,uEZ}~}䫱N8J>^ j$?Ềuݪk=|@cϦ]NGt~2ݟ_c2ܿi~>#,b9q>W1=G(-975fW.:Xt ~0.thly(ݟx^K1ݷ]c ?`A_ѫW%?v"?Y6.ml5`'^궴@y|n8<ߺݻ|hwgH\AXWxgEVHMO8>/Bb'i5i1,93 {J `%?|UE>mn`5.Qo3ݛⰝ͟kik4&^?Sޤg a6NYY:w|'h ؅8jt->o;_atǞoF/c\7> ۻY+Ay?oo5Qi HסWL/DF@' S:@O?w:|ws9_P9nS?(]?#}pM̶k]F `5X>-e mmtnB׮wvhkkG }PK,c,&4Yq8*n%Y_k|Juuz{Yb9L׃#P.RM=g=靚N XK?ܳ=@qqؑUjTC|+S &>@a f?yM> Dn1p>^ |jVvWKr,֭D'@җ[`H''C~/t-/ḓ}2h{bF' <3f-Âv'tnpc1p9cR*_e z<؇B]@ղ.Pjk-lik󊮌# `89=x׊m6&Vke ص$>u=ytsftsEϮW4vi 4y_W׍}Mx$7r]>܄ gГ׏~[kȱo9ܻW˸?^}Z79_x1P{F^>r:@L-߮sy-Ss=}݁QuH]nƗ*KOzCZ.MO K<Rk@q^o_a;rZ1y 6rX2y{ݹȞCߣuYϾ^B~"/w|6/%WT,=($WP3}LmSƻaFCv .Yz}QyuJ3A];@x\f; Iؚ:@(6;JZ{/en=hէ( 1Jd0{7m[YmZFix]zl|~=u@X07|ͺ*i:~ZKz*IYxߨH2{ߋ #ϱÞ\U6L%z|Jg3ѱ"'`hE ^o|`bIE7^TtCH;#B$1BҼ6/UKs_ Z߶?|͟!M(Wt|Qڧ_nWVyXP\$Wׇ]Z_g|M緣dt;k=3jc/1?=._U."0_^`sL0_yjqkcyż1e?{jR_9L3 tx-``BO [jlnj8n ׯC+jP?|%mkz1jK?vW]="%~=xڙC X*M{ 7Y fs ͞CuGO9]:gOxޯIR2|'%c"5f V:n^ [FsCyaG3N48+kGE"61glnkM v8@mQ];Z:==v+H]ln>9-zM/] #2gt}?QjuiaghOl[g>hiop:u!՜>}Es.?}==@MwYXJ?F/Rbrm^t?sи^1P炽޿?/?pfu~="4@,Z}y~Ah/GX]f 3ZVݒ㡒wt'OLRصy>t9Q-$ϭx~Q/# )͵[!ziEWtIA^t} ] #aj~{eTCT򰦳AEokz{7鍄 ДHюŝb.Ji3 kF J9Y֋uکvh'ܡt5(n=n6_+Vw]>9;}J zVI\|pXsQ(ni-r 0]][~90$2Fx7$!"ހvC`_}X_3jo0AZ{ql}nc,RӋъ~V٭==rR};2߷f1Yv/h -_篭r/kt/~tt1W.UV.s7x5h칼ߞ DqNt5-5E;gZs|)Z.vyğ`u[M{hN Wz>Sf\T]d>9(bՔ}l'MǹrenݾMCG)C# ^4sKKPz|Tir_\Ӝ>{IJ^[ebSj  jNQ%۸Wқ݊^⿯ǔm3F@P{1 o뗫ƆC5<ckck4f7hmvy޽CW^ޠ'ϥ7_FEV<Gfz;O+x8WWFaqd4YL3j /Gn ?pSiR;kt|#nD ;z4Nj>^*ϯj[nq/m{'2lqC¦f]ۉʂ>c_8wB^=~?xtO,)5'212=Z7k؞}ҽ~FCӫr[~[Q˱}hpG^<vkh?7}0>3ar[$r\xqjU(n5>z]Im@Wvj4sJW]8|ܿG4:~K5Ct-t_ |F@_ؚ?}*N?_)@G~:07x}w$In&Җk6Lή]ӝݻ;d˦dTWR!p>L$^wzyb cZhƭ4íp?3KpsUF=1t΄E^Zi?( 2=>FoUU|,:6s<}lrmFF#(˒shڗ>GO/.]g`—/CWWcnx nw<玟CjIϋi ?߈~ƚ4y;]m w~'X.^^LOz 2mn{&k`}[wkض[hi ?Ik3e] A}a7/3802 nm}R<_Y:iы{{Z+ EgI,h ڡi7> y 9-}t,s ,δuM1|,)e) "[}q#8; 8)O`^~{5ofg{Zr@v5}S;S \^Go=,k~aקz kꉆ79o"#Mg]fϗ@ 77%|fcfzU]9介ӳ͋Yְ,alple`c6,Mk}Q-/=?,y/3n#=dKwM|x]oQO_ֶmUxS>c>Ok=JB}Lz _'<&5|{ YA&?ήisZi7{_ְO6;uڊ5Դ,(/Sr^G(Yޛ'Y3lo* }[s7,p}Oh  e6(sĖؾczK29~}Eѹhx̟s-kv;> b{ZB>KփbD5 ts߻:UϋOz(>'}zY߿Lg,(ߠcHngxxg%z}~G8@~]z]XV5XY=|-'@Kş~X2GCIv ]GCd=t`|G#٣}I5{D\a7p5zi f>at1X_6>17ESs ɩ%kNkֽ^ 4վf>~z!omf3p`mx6EѺSVFI/ɚ}阶{?B|c\[?Vh[ \\|sΠk?^~%Nˢd¶?kW`YDKїF۸>Sr%_p-?|‹~J_O^Ol2KM cZwAZҵӺch͒ mGřONVIЏCYCM=N#ti1`m%_7ZdEul O.Df:?Q66 Zn ;Am5HA%UL^%w 88wI -?FdyY翉Fϲq19Ox^s~2V&~G+ŭLf&Idݞy+{/' C$"{$a6;1ņc8)p!]HGqDrGx~ _GcŐ$q7j:l7,.LྴRH F>J 3,ON^wo ߽ŋUBWxlͨ.ws$chY9>r+ %c}a?$#xsJ'G/WTl=sﳾL8l)qp6})ng.R]О`IRam׋wD+"z0o˺,=3ؗ}Y벎^>oɷlj[;\ &@A/K>];W|u (KG^t] i A)O6 ?SF]Iy&ϡ@6ifc?l;xBp؋/ ڂb[ :swϟß{W%<>GPDX^~wg1|} |:)s|V1, |l=wvO,x7cFֻt,2Tnx_\͜7{şf_Lx?73pǕ*Bp~dN lJOswg)^?ΠblWҀ pıf}?ӾQ͂g?YlkZ13mɗ* nok?AYH٧6ߒ"gmu듟 H%;c{hBx41~{=8jo+x5}~ [QNlrtsw77OO0i%k֮Mǜ=mU[pQsIykZXҨg9U;"vߌ pwߢ_X?yYk+ӌ;[w>T/>m^v 8{ˀ؟z> ?( q%pؿYQ,cc{b~^.%wkuX|M0\1 =} `¸A# JZza|ID|kr:` 4/| >@kN O=JHb: z/zTb9wc#2{~<ӓ vԄ z-;`^s;bifK~X::b`,P:|6Zr7/EFO1=AG㋏CrNK@ψe0l vfWp}'(ph99_0&ο@ιr*%C~}l[{)EKn+?Gh &E?;ܿz׊~?'F|v߱9}%_8(bſhu5=ot[3?Smg (!?#O@B>A{_bAMrLz{0C`r0}遊o' <,Ӻxh6f @cھmYAlsl0ޞ C\@c)=>pd{Wlׯ+^0H|_G4^qX}T"Iׄ㾨3|~->Bh }s.2lʸ?aڜIΟ*?%f /.\_8|Gj@:Kn t)z\Y pQ@296 ||YuQXd5}Js?!n_Ъ%|IouMdc[1J9_BM8Zt_4d.`&ޟrWOocu=x?wnE<_-7ʻe;L*_ prEֳS=y=p%Fxg\@6=P`߉j:jmOu;։6{8`r,Hlz;/x}9__6W^Ckq^ O#} uɻBjC\ eX?s NnP޽7]{_pg ;T<^\/~@,bƂ[-rT3STSU40.mFT/#aܝIz GO RO8Nx8 7&kzw"}xj9ٟ/@Ο$ uc~oǨsc ѹ!؇EvYU.p:n]6(+ߵpzpm2׏St#c瑊 pge)|I$~bgg@ j̎1%g_5/r}b˅C}kL$G( $^~ .G &a.u.9 ?( j:kǯmMse<Ϫ' .};Phwϰ^}x 31&8 ־Q\|IkMP&2>Iy? Y~]~K7і&Ѫt9yYwN`#|$V-?eռ* }.mo:O{Ȇwy$u[[$\g{/?GOHRC="z~[*gu@wSl3"s萗ʬĶ2 2M6b=p+<ޮ`fpp1w10}ҕ(#|s ˧bC='9᥊_ ǏRGl!nsF¹Vm~YßPƿ|@cKǑS >W[.~/NVluځ T by${\ʇc/?epJ?9ulqjgi_?jt-`q`cIcv&cst=˙q^_ \-bCt;ܳ<>Wx ͞>~\e(rQgs_ɽcb| |FG~. /!0N; {( V[x\>Ws8Ogp?ȿߗ #bYx0;CW5L.kxfzÄ )cz^.?m 9a!h (`'ć6.CnPq۠MlGp-Μ>;.[IǤ=.)? pOآ@ו!{__r. p,k0E1_O|?8pKmߨy۟;"9^νZ|no?F~Od fx9NaV,Wi 9En-F؀T)̎i?>Ekut!a~jnwM)gbe_jZ:$ǜ$11Pyi;\|oY[\O L%'?m3i$o~8W$5YΥni˿Fx{N1 :>f814(=NO0 @b!}}m%՜ݘ?駗~0O@ 1pu8C?@>z-qgVg5P`?G];Mv0 \nۜo@;f _>śK",].q 1ƄU?: xw)wlmL$_+wO'lkܝ՘`tf ?|Kן^Lp:`Vv"r)\,P-׊|^m<>+zZ9B'- 'z_sz\ey!w/W8b&5쿪GxD<;Sꥅ.zZ(1(/ b_r2Pg(EwZ@bI3p; % 8[~9-}u!еk(9C=8 Өa>7 vb%sA;VtAovgHa=ۋZZƵjyhnY[A>骣e T7|\]aGq'9Ktw=„3:9q%"C>c ]ϝܢ𱆋O˸OXNlȦ`o:>q[%o*vsO[ȷ!;J">dJ3J/?gY_T^8g;^ s{*5~$hi e\~>g~}t\K^(2,{DWo6X>1+/F(Wy)ZE\am%|ӴoNW/=S[×&Io91e~X75)+Y _$мAz:9~>sޘjUϠX̯P?/$r|^o.p|b-%>N|a_bIVIl_b(v{c?o cR[9n, Ih<@=n츎5 %vZпw/9t h@8;/\R=oFkkJ?]AgHۚo rS;kꨢۢZ[]<Q,#¦קGn=:~7ǰS(VK;]'C1I_H4XA=p^, I^;ο Wdy' t<@/Ca ȯ39];?:O,(=+KAךh;!n=_,*>B rTG9W };b?*lNAz8Z5&'zE^| lz /_޿[´f0!0M!0˜?ez}n}aJ9ཤ\[gm6í8^k\e X.5 >`}+ޝ.pH6s@KϾu/~Ðq5t&F lRe_b0pP?ocoea[sy(]-@reOc^\ã}pM} ~ ݠqmso,H#R'l`3i\çsZΑ~?#` =ƖRj1׳rǥs]_і\P˺ྜྷ9y~Jֱ{_p׺ljL 3 z $'#'>'o`hvk 9wHy 苸_?zVk^ 8(gX,6h[xt:v"h|N~]+.Xua\qT`~-_KPQWSxo`1 r6f%{ԣi;F9eqs'q;Dd1w$ FkωZn*墁\3/F\GdWyx.\Ru*_u|!-vK87(-8 'ܣ{~BDӶC7 WؿQ귿o_e빗nP{y}= `aF hqP!}wKc# įQ貿xns. N}ϟ[ F6}: +C5aߙjw$|(\k?NBY^z۱^5^*b'QF$o7p~%eo2 x|>z${FƯ˻uļoc0 =Cd)sS5~M}sPe &?ీfLL1[<;pd-;yc Jv<)TD/;ߙcG..:YC [YT'韷~D,WZh<@bmea% -˱+i~'?]r>-}\qeuCZr}:󹾏'Xm 'v$~uKl%|6NKb8dnMMbIX-B ^^%&9Pzt/Q⡒nءNիc~"Zz}:F4_-:7 簽S%<<x=)L01>ǽg7@]畡>y-kdžOF"7M2C;]`E;4R6]u0GApI9xZ%$GPN:]m\jQ7ǯKauGM3@x?Lj}ԹA)j_4w} l W Z돺 v^k@A[%x\S3+`FOH]^:X1]JO*=lΝ:)=񟟗 ݧ#[Ӿ{1?pPPv= \Hn*haa =9~?FZO:۲M<[9ƚTۈF ~ǧ7,me o }O*- ʙIc~ʧnfGpHցWp{I?i+r鹘AYCVznC(c >OO5wR8F}0Øl 2Ws8b{\ q"A_s!X1\ن^k_gd7+7PO` ,)@v@klO09B [3 (DKtIn p}DE"@!^wo][ىq 6r B@ƌ~9=)?($k. `^$h6.n?s$a}"[{ce׳\x8&:t|0`^NԱ87nΗ󐼯$>y',ݬpTR G~r|kc9iMvn} >/3W4Or3Wt1VbzN8 @#\g@ۇz|;{W,P9=Ѩ_ϕ"{Kr q/_n<2tumښ[@؂.xxxV=<dzNAf6똼tIKm8j\Qm.f(SN\*Pj9:WMA:b{q9 @su{}4X-hz;b>z=l}ZM[g#SB&[H)`lƌrc|?'~ݬ$7ۯz&>O~՞kjx12wP : !/% #F/9߷ |2#5nm<:?|LUկ lL {Ǹ(-LG'Omwj}v]Ü 7MAץHܑ('O~%׫L^1XGx?:w2@ct=\^^EI ~1NRmR{<cĹ1.&0aNA9a(9CHLS })^<҇Oec[0ßiFA;K<-ի _>'yl. 5{C=coй}ߨkB5 }\%,OXs ~N(Oajriqu~cu-늤b ar{4?XFso,jbDFEH>7!7x_7h.b1q=R_0$Z/[X`(0И)pՅyRC_<2xsygg;;%_ھ>ӷ޾ A uoJX8ra ֚ $}CǨg3N>#}Jk*Gޠn>>.^LlG6&;kp5/pOum"CqMp$O:V\!q5oe77-1/&nHVb[Ûj=7rP& }k̠/ͱii\1-o_fs/3zIrrssv I1>&{l?*=7xo\_S'w>?s}~[cQ1B iR}HcLB s{!mH~-\QkWqxCbgg1ɧz=sy;X#*<žtFya,`\~PM? 1J 5״noC˜ }1})8ˈW3oqFAԌwI:d!IIqv[ܭcFs?`Fg{uQ1=G˸z-Qz;q3xYe*V'kE_' $0^ v UmslOR+ ėܙ?cF_t ~SɎ]#}bgQ|Ʉ6rLyQ<wj}6Y9ZeP 3D9'<޿ \_7ܧ3{;ǵ7e}(>f8sמC>@٭Hpg9& d7Y1: hq>`k @=-P0qc>K 4lސCt?aKHcT(owKi_. o0&ZĊA5d6^'N|F;Qduˊ9B2^gt V:2Fz5{whT>j> ={.Rh~BJ+:XJΟEHBnDy `Cx3zX@|/8n9n Ǎ%Ҿ9f@. 0RM [K037Gߏ1?&B_g'qׇ챹lw:z۷Fbal:5w^B=:9׏Η7o+[9tmݍQNG œ[Cy{E|Br m-u4pU?3?cHFk]й[p;{8\__PgpY!`!j "?i (w3(@ -) Irkz/>,;iqBlЏ׃":AhlP{tc @qr= LDq?l $ma<`|kcpN09ǵ9+xQP6\,ӧ5\^e䚀)Po f&`۲ݷv؂~y#+To\G|c7 55GvN  o뢆30\D p>wF/G$}-?cSz~Ap-`Vvn&/0B`{_:R|P/ʥtA8}<ɺr98&#/͜#:i#!O;3bxۂmݥRѬ]66?цs9<+Ils}8u h} i]8!,s/7h1<>.`Glօ+im 9őn4"53ӧR.@rνmM@v0?^9$wW*<suczv7ʿ6.a}~9~sMo~m8M 3u?ؓH緲?}Fw ?z`בgSWb~c_ ?g& 9cXXrz5B;C`u F <\p br]1 8z:Cq1:69R(g(q<@)R2g1?x~mM9lQ_;[߳n˾odo(elQzxʽ}?&pegq\;k_O;xtP'O /\^NM 6^+a+xWG zf _]z%G?A>1/0FO4|ibNsrnܮ0lM6m 6$ `,Ȳڻh3^--ygɹ;O&kվiύ1xM}{ c@N[/d H}+8cX0#qkk %CN8d~#VבyYjp’l5y{=uA}ߥg:^oK W=x9*s51SÆ_ :?]p`Y.@̡V#}GThk |L}s_&c,LPs{tX~(!oggq7sPɄF~z:]|(yoqF_dG38o4WF0wrI]g_g_g$;nBbb>Cj:m0Cx:Fyb4S0?KwY({"?nq]`?wヨw~ _dY!h>Y@suMs@CfĹ&.WA>@^FJ(Ǐ\—Ϩn(?#2S# 5Ǝ4ry:#.Jl-t5kGZ@~+_ cKePD%18,@V+c~*G)1'XY{Dj}Rl+h!=HZ}xVu=l?t=nP|0cMpaT80E?md Sg揾Ʉ03'kUZvi!񶞧gqiLgZD.xs>0e܂%cݎ|DS?VKdA{Ǫ@,{ցKO03lQGgdY,{KKԭ_Π:? 5r94_Ҟ_f+{s}{bրEg/2uz?zh|&=.$V5s//Bd~(8vm+B xpfh}~6av>B Zgɭk x]2~[|XH}C.PW5!%[~/Kw%6c\|-@/n1<shzagGvtH_{]N7^8}ToLv^963"qE6 Pr>Y WoQɡ=8lr?r =IXU^?rP<0uc|!!2St}aO)/!{ C2{#q\`[B^,6xIyC.˽X^ ͛>rk8<( m;cww>]Gl,w@z UK?.˧ts Sq`z+z[N@qnv?mPmoR 鸟7Y _'|@Ծk>)g)?+Ilg;5Fk+}?!w]/D¾pҷdd辅#eOαgD.f1W*>BobJ* ~]oVkYy =k8>s}={%{&q}m}':5Ƚuuc{xD?moԖ@?hSlB`.꺳q U[@}iq/4 so'FJ-aҟ u nޤ}Cȸ/xzΗkDg-r>~IQ!]{0װ :ӯ=[maNQ_@C%6jd1|ڿu:@10\`-O<~73z";3qz(Ͻ5.@ڋuld?S~6C/ׂٙ"ҷ< \`ؾ]ۅz3\^|2%mݸO/j m(\gԵzf׹o\?/Ky?װO4N󜿘/ ,l篟70ߓ~&Royz5!y>!z'O??- ֶ>c @2+$>1)TRz@i>}\y<7 8v3딆jbqsP_j=u-XY^vϟe wtfs 2w bWKdRa3>>MC4ˏpX.K< c L%~ZkIob;_Xn ?$91cYq{[W9ٶ^8ݿky1b> 9~}~gzߣ i/5*G94Z=7 2$>%'lZ7&ܟ'bq3ocC֣kΡ^iڏZ|*A~w?{~;%>coVfs?V*۟~4Ppp ݹ~2ThX2scGuaubb2֡z]6mI>Zoi8)mY s}֛-`{״Kw0޾ Y`y4 8zsw>͒O#[!(*zT0vkPO2{']aLs3I _xzCu>OXfm3^<3K-(w)ף?i9Dqoѡ6'-۠ c8((Kbwqo1AgyO{#'(񱂯_+\z8k#q?;uM9`9$ܳ'u/eyYk&TP@ RHjo6-ܞ_Fv%rʆQ~eijj5WuFH-K;[Wq\sr!V~=x[=~^Dz~j[}_38cWh n@\@;sy`C\@r]oOs:L㊂X>/kQ.5kzRȸEiУ4{k^>u[@dC5A/b,aWg@z,QkweZ~L=G*25pkY7Y -3KvW<x>Qݟ=W&=O\~r8 IA]Pϛy/Lʉ.XZu~y\ Ŀ꿭J#oήoob mͶa2Htz|g%J*ٟ:/Z^発y?Ó/}uY? Nt 9^Ԓ|n_峞Ak9dx#naoV%0?x_3ø(_8 Lǯa&8/q ~w1k[h^{_6YW?Nέ?}oFUKW+7@h u/9ePz0<b|PНCM?4CϻhWXcr=P7["KC_Wʟ־`jkW+GRN3I)9L>ͽј(b}]jm ~~ X֞&x#^?oX7ZW[BuK{^x矢oLh̖&1^&Y0tuoDClg_&ȟ)隆p؝7ְ?d5pDpWz?#_~]G;ѩ~}+9m?OS>x}$;n%&}khFH)iWk/ggf8.$Ȉ~ڍ:љ]>\x։i'TUk uw*=}w.zq9hߪUC%zzv}.tps35-4^gyM։׾:Q5--zqJ}Wmt[{tng^q@v 9pt͗#>Sf!}F{pp+|^~~^<|%p]- =3ѹWM +|gu"h ?;Bi޳l=\uJO[ym[Ρq]z--2\{gOcS?g<}eqm>TYmu04װp3õC{gAGYtq4~Ңrcǽxʍ}B1i1n]mvtnd?:xy9ï^G߷Dݖɽ>\eJ+ yat]+5J-wЏ;:vӬX7&)=1? |+~ܷn^>;o Mkky\d݆^}u65IuvPoD^5cdKY\:qUzU_^>qk LpsS;E|Dx^$x3^"7X155.uQ=:t!P9.vz&1goeQmkv ~%xm]et%S\>NXhh PM[6(hAN!j u!I >?2ވH6.n;:3z!d͞7r#?όъ 'Wx~|W}}6@6nj1_IOv@Svkе3Y2@:;l(}3NCDFƹcĵrxBq_?<ntvsKz!O:T*/mؒ"N[Y֡KcYK[}8Ɏ /ˍk|s_my#Y9,;> >*s샤-aC -ȚbImqr~Qſ(ܒpe?W=^.6?@f}t\_ t#Km5Z_%-d/6 ^=o~<+(_O:>ڏ\7'eyu)a?(knq08bod6`nx9Uno +eqZ;_G~ңnцш$P7zTT3czksG(>j{6@y_E?4ף7 #ܺ[Fif68~g>zvj/Ӳg/ :G<9;xz:G?]4t$g/ǘ"36sA5uOq \FU~pN8$Y9)I6ʾ/;Cx/.Յ׮BBYu= )C0ثQ6`ƪ4P>guG݌8F~G7ߣ>!Y!oZ5蘢!%p/?~!xxᾫeP3?~h|RetK> ?xoq3_Y؟ޯus˩"0[\wHq6S`8G} TX,9Eে|r8$]|r>Z/ϒCqΜ?IJ|~?j~S[68k<0~¿GCǽ|vgL2KNکzAM%vzixc[<\x`tj &8.pxSݗvn^>8p Y߉Yy,}}z~^՞'*0u)[Y77߭v3?ܡLvXݷb,6ݛ`z+:-Lۭ bz~!J1 M|66hFojov%D6ͳ ;n_3K@GA@%cC25;_;KK_Yr㗺r'(G~+3_qޣOO_VG_mS]X>rUug*HzQ!>Ia=HJǧH52FBH/7p}+[T*c@ p>)7}zG{:fޢ|~+c}Nwnw%φ~02btn9kc28+oTp5pdu\ԉV/F@BC3}J*6EmBvχ} yV+%ǿCB>?ڡ п17];sG۟{)>z5{x~}i;=)i_$櫎X AG󬊼R65 n@xgh=Qn_і|F[P!;,|*vkUVj~1.'Lϑd š_ѓ _sl1K>M~O6^-p}Z! Pzds;1IFJ#O|[:Awk%g2Pm= Rcͥ ?{ߧX?gx.\'\ } |g|7( !xOmx zA?xz A B֨~?8U(g`?ϚDZ+wLeXJ&ۗ>36C;pMxz61f?}Rܿ^g(FFʘ8-9'7ʡyv `sC X/<3r89H4С? psJ@ ́PGf^/!-6 Pr1_g)AAPl'9&x^3+lǏe~yȱW<@SwoFyJkhY 04b7(vf.2I;c6G \wɟǁ(y_osp>:h ) At3S[Ak3D-q R$ 'P}Vص;Xյ}8Y|O^ri%!9{? oe+|'W_<;w~ em<4c6F!;=TOs-8Dϥ~|OpsZh;Lc >'^_/gq3;B]kYw'>.9*VE;}1"Ce N!<6jsT>ɘ&#bn 1 ~2sg;\Nlw~Vɾ!Pwߍxd[X`~mCDX}A# 6PPUnSn?t.k\k14A>bRQ,Yԭj? 9G"9ck,8=<Xm:W.Ԇ"鹇 4~Ꮿ-|Zc^}{9U3Cbc'v񪰍L)@ТdbbvjLlA,.G5;ϨG0덃_x] 1O}4^najTZZ^? s[}ugx9?_b{_pgohZ0 [CNCc=V_q_kSP]_QZ'w]ԁY;k:Qkʚ$OgӜ`x=Cq̄c}y2C /'⼡) \! W#c?bӸlsoV3ikތrA0K\+ K`w1vі~ɳᭆxv _}c=3[|]sl7VUZos[gOwOIK@e\S@Lw p>I;!N pD8B,1kXns3L.&kcj~0wƑ}h?C'=Y~ Hf+'VU*x΁=_:8uZ[طp@ڢ[6s?ʹ[~Vuk&$⽛gck_8_ ṅQ1uB*;| ^:8{u<= ^8T-:Lq4\>S߬lsM[.{1^9<9e݋ⱹܣ>=T7wm5z$O S8&ks}EGK\&6~uޖ~6nr{|?9gX\arRHri\$fV^EukFZL%)m$F'߽Т]yx:?q}Z3*'<ק<$y˻՜?7,7HGC dƪT+X.q'oh}ec)0R_'P͏44RO,!侞8p]5L! =\P60l[.N=j{>WI󚎋\zy.s[cT8eSu-'OY_3A]ЧC5=h!!3W'E8!oÇ#>kݳ崴gq71S0!+S(Hk'EФQB(`ϰ?yƝC41 nZ?Ooqݿ7=#m-~Nr/{HPd(~fn;~N}د2S|p OnT*}+3 [3Hק$T8'|ߢ {+^K=K&RZYZ4@{ @f=18ba>?*C=: ^+\RfyIm žp/O?m3G2ಱU{˧2_۾Uc#` `k 8Jz[YX%9dw~uڣ?=tc 9ԛمB>_^&\h 8 e&)/TY"Sg}{'=OԒ9͂S=6=6e17kcWXm ց wn |<<-]KS8)WD|'%@=g(9bPqd'P`ui#9ܜ&0XqoZH^isGىN"ď ?Z[x (Ky/:fi!g&%$8H"\o`hM@\ u8XSTFݧ?J.<|c| R=9Qvo=Am>+׬Ь`] n9*.c[}$_QE1'qOj _ɿ@%rOo㿞#ѩ/P9>^OSW\_o=Go/ G>n6_=@..e6u9OqRס>GE(vhw[ĽYj^(/P5M { ħwoYǻڱjjo ^oRYeDCii;5\’Ƙt4ަeXrc[\sM$ ?O.)УoE܏xZ&gI>Őb|GC.oJY5^OXr{Y{g1ahpA5G_-?cF80{Zxc^K;tHy߯i4|3\83vr.Se%Q:GNzm2Qua?)6!}ñE3VИ>8~u_LAH=U̜ s!c W;ClU߿Kok+y[.3.\m^Svm5[ zxWbCi݅ǕϕiHIt~>̙m68 ͑~[ֿzUm1cgDȻH,X@,dlCq1?9?gƇiatz ϧhd>w5|}IFr,Mg=iBie^ė4XH=8E+߫!9G7ڧ\赗F\xˋ߯stߟzW~\h^_;ܜ+ZC–|$l;KRu n+ݥE_tg}Ϸ[eμ )p[ɧRSn/қ x0@s;mJ,5ϴDX. -}&Eq8XNүw|@jj88|1~+1~if9<+~Rnǣև\YjǦͷE}q/^@UȆVW[e Ʒf:|bLm >;L vCNc ' z*qozq<7m׺ka scX; u^~R*KX=z! (x+=/?}zEsn`G9PL5wUaOvg Ld~[%uKO(y~Uʲ)~k7$?p] 4USq%=~; ̩;~?&°?_?9Y~5-k[W_[ 9ɭ=zjoVG=]\^+Y̫Wzq佯\2pFwon'[ ?j?1<,~J R.@ا[R\]-GuElJr&wKu:kmkg) /(K]^Nճ~eR8Ӻsڲz~-qb=ڥ:ܷi( Hx d1Կ ~܃FS5#ǏCi ~ 'ψCu@}wFqER <(ם Gb6vw-/(-jJO[͓<mϰw>/#8'/Dy)pyϴҷ }L}Sjlko5&gkOj'Hpo\kyE|ใi[I W#GDx}~ /kr^W;׷y qlxW8<^>'I ;,o{~_lm{'Z{W4 >S.R>t<K8#ƾ!?#>_  1f1\TaZ h>f|եq-əaqfdqKwfnQymHe4f{sW^{\=oa=}r9^Xڙq^j-Q֡Yʱ?Xv[%q27`yeEs}v\ &fbuGMQ׽ ?M;xF\o;wTȍ#c4 '>K]ȕyoUU8Eؼ ^3XOyC؇7o 1NG+ri3Pm-t>~Y9m\?M4"cQ'Ao"]D~}`Hz ¼ԛL$OG^ҹfs |G5v=Ow?;U7u3s!/+899(?u5lk^Ok6;=(^_xB<@sݭ0o[=.Xޯ6lX}>Lppfmko~˷2 Nb_˭վ $'?<< e.(mj,`ײ'αߊncx [|.{z.+T:U\E J.#GVL\穂7g'_ ?CO\R.|CRp3O|X1:~:/WC=~CHm҂FNVU)$Y~ĸS[?rv/sUرZ湀fc,h-m\+KP ^z@ 8 S?媅5چ\.d.v\p/椯O911V6I$WdG3KMG6Rmcl3@fq ۴ bj!u:P\iUN鱂?6[oܙ i1J>';"'bڜ[V.lM{o4F\W|^s:7fb#'aՏs=cdl#T?z?R9YlR̹2O? s<W49^d6&o-}3^^eO8A;[sn>%OVG9UR^&~kkYG_|$%"i^$1':ڀz-$uQ-c[ǜ?' *q߳`c.p kV)xfvo\}>b97s1魽 X1Zί+sTCc^s%Ľ(pߢz\!+gJCq[ O<.{4m@,w2_Bm.J2y62$ٜsԩGk )f֣ӎI2rmu{S 2*Ο QSsW}:I8Q6K9Q.@m`|]zC͘@7<5c\埅_-^_h%]J? 7 /1ajCMm[8rfIܼզlIru{}\ݞOBqJ=y-LP.0u鞔z$[Gf,H @=b= v(#l,/Y^O)1s_&֡Er)O9_=>wf1#>%qzQMp1^]0Y3_} Kܿ+:.8@?3,gy/sv]xC%,kss&pfqp<а}{ 72OpB=% j˚ثOm/6֩F9zQו⦾>gcr56M%^n!Nj?k?fZ/̀0=h>shJ}˕8) ï|~t𻗆kPn ^t#sdc> lϯq1/r~w71ho!5\5{sbk :2o{Ȏ/=Ug gw@]G{r+ 933[R锸䥜Qz(hYqmI[[)ߪE :WJqÁpSrGt# n.)/qb/'4~dv=@TȆPR?^)GG%yqwR ~Ϝ%۱(ߏ~gAχ }3z_q}o;4KP&68߿? Vά/[T~-9Q} tAk7g)o-y8lo\yjsÉq[ 8q. ?զAp[_̑P d娔%#Wm8>o|e̎6K;|J@kAçkS0 X}da;>w{`e+|j~@C;`38pf rZsOȷvP?-om_E$4Eޫe~xF>s(rE{"3k9ooB @$4y<=[[:2/'6.Uk^ECHsז||sd)Xn>pt FGawQ|m|?&Mz0שZ>$sJW2o5cGqto#;klm{C};hxCҞ=l^'8U΢M#PFmȣ6Orjx&pik{dya$~^1\mca-6|N%/ w6v.^ /=ӣ^<7crk| W3+qk.%T%,/qmnN/l 5c^!>8OI3CXIcX1}eO#_#,7)6m΁D~j-`Y16 >? SL-+p/ͷD>?$A[ 4X+ 7yK}@╔l#>$7}W8f+#; IRG`' r~맾>qu}7=+ oc|eP'kL}?|aU2 V\z⿍[Wo$Or 1Vd纣 ^,fu6ЋcJ''̿\5zEZɏ'\]e1)g9w?x/?_yTBm~?v%>VgHNrA ЅM>d(x/>?Uhak@- %ȱy'g} s\c[KYRg|uR͠.y687L^!q'?9o}u,S\ӓHX[ٱ/I~Kv_y<" s{05:hG iޞӜu"#f򌖰g0abb\iv,}u>r>p?azh] 2\%U[eYvS+~K!:GܯhJbT53 yk7hof6ϗyM3WVU[#~s7,(c^19٭ev۹P)Gw^&] om#ټ\<2'پ_Q~8=0]01G@ő\@/jq:Sj8WrtMe@ێ2C6 >ᒽL>?k-r6g甍9.gS3,G@7fiO'$?WY{ _{:‡kxnvh`F@2f28U|xW 7.DS޼*ZA-_e.=cOmO!h ]aBI{ҏ+}co_7?q u5B9J%uG07'&hN P嘜_|7]n⨪Tooq>VA_?LI6Ҽ\ymWiT9m'Q<6x \\ 6vOemv#ed c^{Q}ʊ _\Қ36haG;K3n6:Oj Z!s× >>:\lj<LJqܴxYPq}Xc'%~Qd}Wqs0-Ws Y;#5@~(R9_&2<+6^˛/ Y!ɰ on|k 6 Kx =J}?<ԸR]߫Xa'~Zg>#[s8rvrLr_}~e\{$<*g׀p:·iQgnG1zȉ.5UxF[Z*`?5t5_~c[|&wm圁f^%f*;=f.kF Yw=Vn y5lk'x5'C2\N־=*SufjÓI@u A^LKqXCԴ<VSoWq0 _=|zyk#Vh栉>%Y2nk?(}ZZqu=~f(y?˹?lpb?ῗW/~o.՞}B_!qL6zn;0%\1:&뱓e_`~kpzs=ھZ_= Dǽ{iΞ<<>tI6o &Ư-W Z\U5&D@T0u)ɛ@+~o9*-'>:Ckޥ->Uv]4ϩ ꑾ _ [NuHOGycV0mdtZ+|L,ȧk>}Ooy%/UqR>`.+I&-66@gPt8lN4?lv('dPMuVc$^b?Γ饿 NΧVE~r{b/fw RYIq9 vDU%6<Ҝ<S͗> .:F5c%+9#eo9'Ts6GeR5mҮȬ{->L[x=]8ov`h&;l?K鹯1+9Z|9_Ho:C%qDݶQq8VX|=[< ?Cз,bWl-vops--`9+ WDM_"[9t>tޫ_Q4O[,mO )Iw,"}lNW qzYq 1<|ʬ\_-|Toj؜ǙKq|1b3n{B'"Yfz H&_EY>#c<ǺC 1S uZGS>.)} ?YᦇaO10(쯌 =?V*> ɖK ޲qž&Ym`td}G@}8։ϟ =1ϯτ|4P?+u-mk3ǏL̻\g29TLݎ2ߧWny dȢ4x}$r] ]Wr3'J:2OhMtPD"Q=9Jpo08Giy[6ۗ{u][j.x:oaͅ~MǠ}ҾSפ|2cx.#tOco-|pcgƅs_-8FglC7-]Oc ?/D4>h,r<|ɒ9z}}{a5`5}W֔4 !r=j\qbcu#AǠ\ư5~7\9 0xW߾/G ׄ# Gg1Ƌ+#4&۲,븵m}F'MmևJ[vS/Owf7xy5=}~Zv[%go! Xp6P+FWctK0!9/ܥjIbͼOyt>+<y@:3r_N/x@y@yR;o%r"UweWXz+^L3W8=m[o4ȐP Ow@7 ?#Z%q8v5ߖ>}^{,|rzM8_qYBOA 6c?w 6^^AaC;17M[_~| 2Vw{xZ}bd]b(ϧp24OD쟂CXϘxb[k '\+ m][b/^dM62V5՗@}'WWM*vVT-ϴa?NqFO֫y5F)c H<'9k ߌh#l#`$rί9_9޶k^j*"A yEqm(73b= =G{@ \c׵ަ'c@6DGX-6Ẁ?Mqy_ ԰̉_W?C48m0D'3~tep?/c<.!MK/h |G<;kg&i \v At7B-zi:Wki|yDw/J-@kov~pesS#cs{S bn?90#?-'ߺcǘKMq9AZ>qY#_,y܊~4Fqh܍vm;\ۺ]\@8C}x cO#/ؘH^A.PkoGк%Zmo>EoNq?n#-]7` t?|I.x{ziE~ K_/dGh- &k >Ggנ0/ mέ?[C>k 7p_rv|  #av[x5`~8W>/u;}| \3HOh=B/\ m'E;<`FnkY@x@8M%YS5VZj_@ <ޯ'oljm"l_ YoAwQ'o"`;j׃o!53u&c?a/MQp{̭{3XCgFļ?&{?g o1j5_S=^7/M4U59 KX;-z?qdRN.d37!9?D!3W 8p|>劶J4Š;/<8\/z=?9-k_^N/|*[\2\/G'^aUhd>MS+ZkꚘob_V4xyMkF 85WSCF6'_p; `d B!DL?[/;س?@㐆?}Bg7?`yNܘD9 PdOH|^h_d?@8@Qx"},/$@xB/ZO+DPcbCKPՉ< pxIkmOr=Lr߅?g@o~P J2pypeelޚh/k-~O$kKv-_6M<~D|9eְO}פf\ֱ1vu{dVbuLa?rܞf܁7ΓwCOz}6x|\Y7@p'g ~e88x{/z?#%}c矱_wvϹ v*~ODO~x~_h?S:xpsƌ<-xM `g+=U[{Hv >Dv0<]qn]~cƹtqvB%8?qx?4yB,i{3}K6v<ŽUd\ >!X?rK!P`s~!|YH~ߓ޼m1\ZX<=Vꢿ#}T_Sh_HXOOGpq\ Ed\?w8!VN+ f 8Ä/=ߧ8?Vyml㌳uݨ1 +"@Ƨͯߘ{xtMsib|\8󉺆~{U:08KtAX3Pm @>p|-ڜ;G h뷇)pZko/C}.Q{&;8'uyIs?~p6Ѷ)K/9yѴ9݊L*q 8743 ?<|=!@<|n @|ta*vAQk2R+cS~nz??#Ǜ-P'S'sei~y]1Txk:/΅ֻ%v)jd;i}I~uݴU4/~׺@%hMR ){X_DO%OmuIp0Lxt4m4ad!Gg? rd;3S|C|zGx5x;q}/+& >ϵGy7?d?w<M{ɿ<,?A~P98KXE@ {G co1# l~_=GTkh&q|3 Pm" l#(Z'@}#ΑqP9Ek ifWlx8.-.Owlc /ZK.pc!DKSs `e-׵? פ#6>i5@͹)tΏ\-sW߯υ0>CeH}_8gf qG8~){Z{'ܧV:O~ѵO?'kL7Hu"[|ub.Pf;+n_nqbeG?a?yz1sW~%~?W`7KeqOyӚ}5:~i|*8ڦ}/c <==Q3-5Rgkqy>bI=ba\>a/3?n(6if\GpX׏9J|( 9uݷr+ >~Y/}X֞:Nb.Oהvwݮk$-@2}Dk5ΓϣKTh3֨ AX zw#%hx5j#dۥ8:d%{!Q\6KkP7U'?djy&~mG>l'b+㇛Fld _g'l])o?k[yyW5k{]^;gOۧ5a cmnƭ}T5R^cx{׷q@pjg\w88CC$ʝ}S-zg[1Ͽb?zq-xtlmge_? |ٕ#jޮq]` K_xV/s5=u5 FW 8ssP <=rR?2<6P0*ۺ~9]6Ϙ(xp ,zi?@jh &[QHuU͠T\_L~X@;63?@L@mx><_<g\Wf-&p'{#kh[]w(XYe=V^h@EޡBJG. "4>ۦ4?kZ H%qk`~ h篸\c}d7k{W_YfJN*Ls ?Kh> !nRW<x_1q]bf^~@~9npy wex?q ޫ=#bv(_fy!-Cn_e\V+ouPh[0ޒP^1/JY&~׳6~W3侴@'$*/ ]eK*8F#"ރ̧6 ! 8jQ=@1ꂸtW pغѥ9B8B !oob[~n//'\xoϰ_Zxڻ}˘~B?'6v~痮 cW|||ﯿ?!֟) p}_H"=c?xwx'<ޗ]'I/K/wwCm}{? vOG '7tyCj xs pOK@,o?+k#p:z#>8~9I!J8k O7?#/@v<;P#=rXw9&<r&8r`.0<   tu}ߦу\_ V ߪ Ccvc}4Q$ `?@5-_隍ڕDbYiqZx p <" ~w?(~}!]\W8V@z]-Os ]_F;;?`;9ar=& iKi7y%q=4}=nR_^zY|f:Ol8Ov>wN'C'~?wW%OX\NMqZ:=GM|PP_gz~6/zhӟN'bߑ / ?jF_Vs #R؏sש^9A˺liyeB_۷t5)~V-QQ\co6Οk"[J<ƠK/d^sio Xja Ɣ}O[1{+]ŒO%7& vDcXl/b S7d w\KG:t_F?ohgC?ɿeW';XQ{ xj3dm}|;X"&P 52Yq0 \gto[Lza.pĶp͠ºSg̱ǜkӘ5%Sń>mb31?P[o 𢹸v-/h ?+f##oY/W(.rd]ypk;ln8f>с ޷-i}?s,|y%51Jތu5=3XbCh1T3GدqZ Dۨ1 ٚC TuW8}rE##S 0OrupL /{|/h7}AƎy:7Bs)ٻrbW~EZ`{ Ӻs^1;6 obhcm3JP(5\PET:5< gg##̸28!f#A OT9FuGG' mH yJjOle6/X8~>b{WbIoc<5czA$So'}xY%H/9>3W(xwp0i{n|8Du5bDmb6?$FqzD'@Q;ChOnX`1F\g)V` Y$[WC{0~? O=̵ |5+ ͩ{u݊[Y}lLΖ˹u'm? >Skk8B~:Sj.ה۟kJC|P1Z *_VQp)7PK~?mG5xX @9,alrrCof ܑ,ȵ@oE(8Q<~3@7lq6mM"^dLlz}m /n` Bh*>?[oXm)js!j -<Sho'5ٿ@_&% 'p%?)w/Agokd_ۓpS`m0:&b#I4@#_sW֛9j#G$:/3  M~m[5}m=;},GZsڗP5mT `O=:?zzo?mw{N>BO玊UC,q1֓#6s'[u_܁,9iHb^/OsBkuIW3t"e#_+mtmmk8NVmͷ@A(F̲~T}{\!xK}+c '܎!h+I =`:s sg^/!߹r6-=IMT`yo}GC \1ӓQw.a|>`/yf.bW?[{μEk+>mcb{{6RCŵd[L\ E77g'$ \`7:~F |8jbbb R,tIdѵsXsowOu?1DmBd|XKy9/]hTbKއ0sD 75{yi=?GܻϞ&! ?}>Z_++-yW_%AI';47UbuM3u\ƶخ庤HUYQP{( V+&🼠{qݾ3@O9J*+ո";[Cϗxezv?go9cZ ΦkmyT7R6{5۽/4{E|5Hl-Ęv[[_o5nk\ϩ2OPױ/qr(a?d c?+=Ð\}W̍cqoq="s#_Y}Ǟj#1tsb!>Iَ[P,!=Dosl>jS{/ժ9`gqkuȵ{5dr{_e[7sHiE0f{q?@|~l((C=bw6@CO*CݪPq܂8 uoI`Eo^pX 0߳߱]@Su_Lhp.ݫ$so?%1 :Q=VE>/>xTnS,!DͿ?Y4l#V0bc.P! ظ|#fsLXka~=8/Ņ|#?o)o?<˅EX;tn>``ر )Q~|}lA'.= v %yNyj?gmoc-O=6?>%1nOZI{}g?郸#}?-=:l=HveqAwFcmWтK ~h&0%/`cGE_I.}j&|N8p=|ۮbvDݩbfA }Xg~|Qr}ol08B&.9;<{wrgW =guD.NGck&,[ QK@xv 5DA\ֳ6D,>q}=qya7grbSM=Ɠ9>n[?MpkN?1=r]%S˽okإq`ׇ_o=:>Nt| \MbB3O= p¹~oN~A T$q4l}emF2g΅9)v`~7o?-P, 6ѽGR?{b f=K0~߽~13p1`o!sHb|_%(5c㚼[s jQ4Lj7\O=s%sLYW8`ik үha#R-Mi8KTz[x~M[oB!c9i/|i:6A[LaMYqj)ϡu~ +s& gNN5b>q%L]}9c8@ ZܐrÑ`D_gXw1G?tk;s<ؕ6?c}-_]6U~ #]_c~ 1F^˷?5B~&8hcuK;76??άQC.~ v ~dĎbGlQ8Pq_G;u 3@cǘW jQuų:6{-9FK|Ss1-k߽3c[}6H&sD;2֗uHl6Lרq?84XWڝss>bZC[1#/1teږ__9Ε&GoAAoP/8@ L(sWqob,ng Bk^ }#:ͯk_p-Uyɹ?y/b'$$ TlК5t @ؾyTCۿCtNjUJ>຋K-Ƹ<q&9CP`<Ϟ^b~Og'^A NcxG1Z˷cquӱ6[ux|Bksq"%/ex%bx~a{{g_VOc:~4}S\vn!o6T5HZ߹74]9bB|~~^kw动oo^(>Țࢶu2/@|_L 2n NzWwG >fS%C!~D>W+XA[W^mY Plqw{n{ y\rIeZ#N.?~[Fה#z05^[uնO63]ǚmxYh׾x}MV*?QuWp us~?woaǘ1/{uzq=Zp{|`dvi#:A8\h`^| ~5S?kf lv~g6G@Crβx^4yµ \ }>Vwq@[V?X&<2#+ן 'GƐ[fb?qR, L]4`!ˆ`:4H} 6G#{#r'ML`QM|]?y .IL`|S52]ܱgU 5> } a1dEi8g>S ~6%4~!=qsp;1 9=sDZ،x𱭸hshGn)[ˢm20_t{jR +N[d62/;?|kj?a|e~槰~uA˻l?~ߒ_|2tKM󿽏w0 gU)3"?p␼'n\b]Һ}ub)?PH4tMZc ձ_/*]ڦ{%~05@OWn;;ku),Mlx牃$ =Kߵ~m3ZhH!ǯU~#1 547?/wj)q1_!}i`"  k:@ tU ~x&&L<`Sw!P㯨߯mz Ë51z5_𒦯#.BH@Ɯh`E?OWr2bD&Șl.[c $'a\rܚ>98ڗ-V-Z?M>G{n\?'č[|Yޟ'xؿ߽'.r.V^ _[}ͪt~x@=s_ X+.%+^߭D_1xAdM`yr_h&CSJ9(My(>j;8)\L8^ M XA-*5٧`[6k} N}G.9wsl暪{ V?},еӾҼR1 : X?ϩ_a8Znȟ"?#?;5Rp[6pn=MlG5]s k;}ίo'X5|iUl`Mg,ySB/,9`cq/14qwOm~m΋OuIӅ?5V)n{qsR} D>X+b,@<dmu} 0rn/vϴTuJ׶&$fQkejE/o1vLe-^/5_k$y O:CD֔d"r9FSf43hbMƇ</rHw4@-\`&Ǒ緹$s5(@4..3bshsr]}d?1:&5~ c(=>j븲quEe5ok /tQ5=AVo>r*GaSIr>Ck-?+]ZBh7WJZsFX/VElG}έnMC}W~]43Ŕr[_=לH_o/xOؿB5|o7\θ>><\.+g f8c>rEzCA p@4\ZMu[嶜X (kmF \c@(xm5Q%.ږ;/Ư`ݻ?/Z@{o`8 pXbza'I @\s|+܏&~ČaRv#X4\p:s9/Gׇ Z@Q56!<{?9zxqpq Wh?9 L9ݾ}^mZ{>8"@lKNǯPm*\ZxӸu^eN%D Pu2Je#>w4\,bHCP#q;8/փ5'|.8HC5zO?yYٚ_~:l~oi,N[痚>(WpD9-~ſ7 @8ۮMCWs?n'"n%6̱¹ۜS\)w{`:}Fq3&`X'H1ϋ?shg Y@~׿!Q9@x΂;87II+a{8u8N)&!wO,"/ЕYm—.d[_`k. _7Vq56!}p`f+NֶبU_ͅynk?CkKjq6!OVu >_8rȦ_[p{\>61"7\_UmX8>Sօ흟 nHh9[t̠Uiρaj)OJ7Ɉ 8lϟS7V-[ikk(R'gUş3Hqpp]m'+{?Kv< 'Lc]HS]Б5 FiT+! 3"h)w8E? 9)?h)NaK|⇵7Z;Ɏ۪1C/U#S[ P8pfMT#ҷ@&Rkv\B/CSYGی4~Aθl3?9pl ZIbCaT {ls ?kc5޵\;OPahs?9g>+(/`kr )W;#NheN:+^lBysI h tSX'l 4z޻X+~הoS~pe_صgyGm,fۿ4_ݗ_gP>a᥹r=qXStyss8.EۿO} Eq4yLpm.E:vcX85 Wvns|bw#j/óⳃ 1f/[4&ilֹeۙ[>qU$V|:< R.y!F `92sĿ#uz7@<X(Y rs ҄ ֤_ȱ j\5P?Lf;;\{Gg/; P\kYk< c+zVw/M0/<@7{4b?IalwlO%*-+;2Qa~k\FO)` |&} ?å=)lmϰ3ܵӸUgG qG ?mZey ğ-j;)OOgz/}q<AumxO)kȘtn|Iա bC}9 cR"&gﵲ;|}mL&+?5m̓m;U?Dx}+q%IZߴ5m+g|vf|[~ Eb;_Ll{F ?גsN9/)љ!ыKOyOV[\v ˲{or˗*zAǭFk=_y\cM{:-=n|g:.~~gOLSHici,ԻRߥizyܞܮڂ\#eลK>z~ۮzTS-K\KJo~{Nw/ۧtV{^rǩZۻVi[VtzeYڵuu.и,jc_eҐy1ezekK,GeglV&m֦[73huHxT^i#t^|K/ti=r$os-Qݵ?ض͟v{]݇^z=:}茇W_ө+㨾8i=>ZM zl_>J\7zz>Ezh}0{ŗ6CyvyN=v:*\+Q1TS34W s;'Qs-勇ߩg:Uߧ#֪(}'AuϭgYRʙqe йؙјVx,sWr {<}|u_j*W_k_3-=WVܗ?-zԮ6Q=wL)k-8/I?oo[[[z]햾.\˷Ӗ>R|iMcp:w9ֹE0Kq%3ʥc-u@\;֝OkÆŊc8x%Y$K?JJ4p+r%8@^_T_qf>=󩎩.y6a!aaDg Myѡ6uVzNz?rڮ=5ka܁s(79ۿgusvy!V^*^>Op;u" ٦dNcXHRj  ߙ43sg:+ N9WfT~:<5 WK9t~V_Dа?apwZLpQ,ۙvgh''O ?ۙ8l}ߎ`33,E{3gWdonD:@Ix֎Kg}?Tvmou\~J|&Ts[q+8>kfg+*4&fC[M^ L`w6p~+ˤIjqE8UvNOSz_7k_ϯs?cf6~k.?3.wzG{c]g ubʥKE^]??\?bZSRL8ڰ|f?^ϵ,Ep Q9@I 8)(XG=H`~i6#6Zp8~&~o՟GvfN`o}!}Ok_ lףbk{{\O;-p7WyoM >oW88ot`)ֿ\7{apE`[&]svf6s@Sf"V5lw;>1 'v#,'"vOʂu2>=vuo?3?N0nu E0^#^lgT'tn Ȃ ܯ֯3%g!O#[v-PڏdoƃW6:˕t\lT8)4Oj/|xI.\+78$ 6rqy':+:<A/0;\}9 u83[4:Gg zU{#}_ݯoxM~yo-e<`=&8q3Pbۉ#lÆq|w؟7:K0ﶿ}f5xr}cm'c?:WvKXWOxķ}[}:qgqYAo:`c1cQ^'w#k&řŮ1D%1?O|r~Rb ς84p{ݩ!7؂} FuڻI'bp8@uRiҧICS׷^RBRL$v$>gq*osh<`m~r>?/ _Wb}Z| 1+-l{ޛm?An8ë]o0=h@!`>%1gk7UwA7&өxhymܳ_}e^G<$b飶2b/:jhyf< ZO>KzrCi[ $q*6Yb*qx;ؿn c`sfo+>uUA_žh,9>D_cx5oﰿ+ _a7^g[LPϾ&jű_lq\-AOM's4C:{.7hxi_*ALfY]4nusE_#1+&(~FGZ˹ה>~O"x4087f,acVmgS\ {3`YGnتqc[{fpok)`iSerE@-O_j I?w`u3̋vo^>%A>7,>{q>m/4/駭{\7祴Lmi>ljߋ}|mńq"f+Z1+9ZX;Z0 o8@'W\P$˳يu6Gے1xڪ3ů 7>0avqYx\q5h- J} hҎԵ]PR}W_ :FDW^ׅcޟJzYg3}rEb\Dh:1[I&wloV۞4k`>Նxϲo |jOȽ>!o{P;ΉY뿤k; :IMеݽg`,$SpM|yqV٘{A? 3ڧ#ˈJ ρи7DY,g[!s;$X-^9~rpGk_:^c;a\A9Wт|`{o/%?0M XA ϴ\`%cQtqVY|[hYOʶŪ?sv?_ _O< g؇zAЏRs~=`y_RYDܧ9:?_{ؿOO?߿]`iFHWOtv>c7iN}'~cn>a7#ȥm|1gj<rŀ-n@,wj2Ʀ?s׫?c4\Is{M'_0ZGwCd9slq:K8 >^ |[ik%ځZ+[Y92TO`P9@ooD .3pWoպwm+&~rkW="F9M"Ӳ -Qh裘D=a1ߍ]tSgy0vm;CNq2a͏?;o?ig4=[?<wg:Cm+=Ep\BGBnj,߷/ w~ߗsOӺ>Ůs9q6Ih7smy|c %O0 ~ƈh\-DO&(X߳3,zhmXm~=>To3YcWNC:a~NӋ?e`sF_t0E}7w8@q~:@9@a~J/b&&pW@ ?pMq8Om6/x@^ۣ@ p{|`zĀ+iƍNQgc='Gv2>h{k{G磵vz|E~3GGm${JN1[Gza?>#_pl~~QS@{?32_и'o={5fo*0輯 ƨ_0 |ۘf+PcΤ}maأPX2t"|6#k&ù>v~nFZ~q߾_Df}tc-_cKk`\B C{{=v-k,|Ϧ6tTO@.?P|`mY9rɖba-k }㈿Sp\޻kph8 ĺ/Ck_5 )|eG6.5##)^K1#׏Glãkfy,Yrmd]oo[6?C6?>~=#~ F}1^@a6f7HtmLcl{LP;s5cg+blujWQۿ_K힃?92RίfiLPu[*zh2e3mdq^p˨F2 ٣:>jgDh~?c Ȉq ¼-xv+sz|Tp}^-$-/bLOaŜ<[ &7L2?D,W[yvɴ훁Q2h{a_y8{^K5“s/,BF .;+xU-r .J>%(>0ҀBy@ 1VP1 a "ή-{: U ?h:b[}`/^8m|xHI38#0Sဗ:bzz~>}>DL=a߷}w~aw~{qof{uOk )KnkGmP1/}f'\kxn3c,9}|_'d:}FdWt}[c֢9{xm2L?~iD}]'吏A`TUh`6e{Gcv 3|oZ@ 4-:+('2ׅcy~MٳWgnc zr)F;>Aـ_tcp͘g/.Yf5/t[Z_8@CC߯胏w3 F<~C!rq?6+czCc!?L7vcq\>.pe}_O};Xv xs×n}eee{r_91fJt-疽/1g{vcFc<ޜ_Zx癱91v'Ns5:>:ڮO`c@Z:wTҥrr:/(1`K\er0b` |9 uw觘j3AXgl=sq_i}Yg#Gm푺\n"CfnS+o Ȣ+#!; ݏ~fYs~EcXn<}=W;s}U0kM>ݯyfk֘]+8!nXf?jht bD)2$KgٛIHN: jkx̸O<gvw_۬>7hDܐҼ:/{0Ug@ b 9 zGc$[*}%g|ox<8M Jц]d|Lֵ6m?xͭsc)l%ƿ; 4;l5|禮 a(P&)8yޟZl9mp'׻xG3WݑG`_?5 um$%}׸,U맽zz?eo9G0fgpyJon9v]KFNC޸ϜΕ8c~|s~3NΚg6IYr> _د~0FZ~}{dcۛd?XLos2L 1?ZeH>rq?_߮Iʿ { \0B3hki /S%s:'Yh j{ ޖ4󙍽qwl?b ~B1"6p_].r@Xg-=G<{:^VG\E/Z|If+OOkǯ>jpf_hmuĐ-~[} I]p`Y;e+F`:=fy9c$qjgO=XϲbsY/Ͱ짶p-UU}|he{yO +qg{doeEQ~ xOr<_n=?ܧH|/P ⣨ (u;\Tyza.^9'񀗜g hkuH>k?%X~W w{6ͬ4j3_/͟?+9 I9>5RchGbg o?r{ɋ+['ؗY=G7ۧG}~nb'~|3 H opZerg7l8~_A;GL }/O){ hٶl}m%ufai߸r;c@[/~~Yk`m|cuޭgُx+ #R|RZ'y3{wA,S|= |Op6Ϥ\^5Fcv<t=صn6;^`@Ow'8_5f{[یf諮:Vb!l뮉FE,q??˷m//Wp.־9wxzo?յa,{c=c =o/({ 6:@r=G<` ؙ fcx}'t ȓ9izzԕae(P})/mQ?=g`^1RAY󽲙M`q^>=p_~3$IˈA}#?^{Ɨ@5{͏3@OpFuOmiqjklTժ.i Yq %xʨϰ~/מn3KQGMm~ e/kb*Q@98cNh/OVQdء2RGZyZ{X$@whkԺ>|?/J9mABzU >vj ˸#:ϗ$FHvǽWOWgkgke2ӡxgk}>#')AHݧg4͟.OqYnSԗ3KmPpق@AQ;peٝGg)>O2#;g<<YbG'Iif Nb7Q9oP5cum KJ˷lqί`1}o_I\VX}Iu~@P08~k'Aʵ'3qZHk.-pb:/=/r(o-z1ŘVy>py}59kaIJuد10$1t6b?h[ wl r^GPa=_.Yq~֟k=SotEmx,b'p_il7havۣ؏kq/=gE9c<`sqM&4. e-<ےnm_[@| yl9<?{k\|mv h} uvM:u_/71_5~Gm1=~0;:>8I8gu>Mp#>i~\G?/T7}9½Gpk qpT9}7#w縿 <_ixlOx@vfyB^^î]/õ4wP|M^=vpjO_㊿gz>{^q9gQz,WE$Ó{AO~}ѧ (G/&6?{}5?;̸s->vG_?.?-ǐ^'C0?uwXv2 ߷5m lRY8v}!g-Q^u 蔑j\-7ړKq8}_L:bg/zU<龊޾?0rqV_͍ X6 yh/@Vw24ǜ3 0<OZ =}+0M@@:X(ЩIPK .n=vssw6e~߁iurN}u!b5OO:V>o혼"ܨuڧ-#z;I=Os96PR:k5xq739Vswzr2%X8^'mCW_ (P|TP휧:='^3 Z^a_@y91c(~>}hcO3GaGkbx!1&Ln?gwD; 1X>X՛Dzrӽx5Ooik 5}縿u]f? }g>}UMgQf4ӝym 푰uy֣u߭ƶ vڸwmk-!S`_ د$?>̅ʠ` h*/͛gv}u{n6j{@'-[tjt5ok'meL蠌+Fi3F.DKZ|ޮ!qvaǢ{S f& // l`4%Z>EsiXjq߾@9΅|b9`U}$ٳ>}=y<<_@s~X/lʃ.s%@KL<>RHj"0IeUs?a|9V36r o>_Ƙ x*/ˡo s㤇:KU 4@z@[;X 8gvkI ٶ`u6~̹y0yy}Cpw {8Ci8w&n_OObqy>& ~#zǜ;7 f:QL׏1~OX㘾p_s_:[8qq?Oqn+PZ^= Ƕ#ܖr^F6I_oezO:^$c6qyز1M/Pw[|c{_v7no{#vx}=49TqwcYXwe#𰰰pp0 Q+t~1c\z?v6՛j ;LqSs/n-{8>OY)~񥾻~n{>.kP 52EIe.}rzis\z^cyCƜ(սhe^q`ZAKJ?Z+V:+>8K=ǥZtێqH_mU]2HV~廤ߦKms+o mE*qLl}~IeG24vmy/K {ؗ+5*DfA010'i撊msKrǎzƲ:Z3:W]7Gi/EyV\;B@{g̿#6Zs/دe3[x\P)q4V'jU=;r\/"GwEփ9RXF$x>C .3 Q^Y 40@7.|PYyK:HBM+؏?)S̈́мMnp;) N(.[&gyT/?ώ׏qHPw96 v9O?PƼa]zBOjw1,4.S}s/K}Vr&wU(OW2mȢWm|8Gn{ov2eeh9 <@Yc]݇2h':|?j~>wL`G';a~o8O/vugN`n7D?WcLߙ_%^~7#'E{@oŴ?pn2v1,  >+/w _m (Dl PS,_‡Z?kM|L䷸l}`T߳~uF?طq&9gg$kZcpM/Bd+`n.r 6b<ʷ9yrRFl`g@ 0<_o~eW7-RjI\ #i 0d@38lXr`=-]_OV6s߱#{P0_w> Z>Gl}2(E0|V~V >ĸx/6fߋ㾷WE /\@ogٶF_ K# y˱*YqyI䛘k`32!Ncy860:!>T?=bp\8Eꌍ o \v q \c#I;28e- Xi[E+ \G]c2l$ .˼{8]/SJX7\[(x G_R'[* 2?i~In ߶ v1fct 0ekpˆ#۝dWd/r"d(8̩q࿑g'4w/{ 4.UՏb3;rq:Wj7@q=OzztZ׋!<D sd0hKL/y `Zsӈ7H̟p/Ǔ}?3拝MWS\>F>s b_1rwѵ / ёuiLbls')/ ´l}~᷸<`p m9utq88@ǖ16>ٮ5~Wζk@~]\"?Emʭ:<*Օ9֓yCx@@ Xt]KGV9wT/6趞79#&s {2|B2Ǝ9-LB1p1Ğl7d!r#_ǁ%ybG|Η>T7?~ݟݿT7LEW} %>ѦjvV̟OQIQ[? Or/(^^aPܷTl+9k<C?Z5r/%'5c fj"wmXn!:Th_yq1Tch oGmÕ~D9qy4CsΜWyC@פ# ,'DQhqHz–?߾ @πx1}|}YG~}*k's/'ܗ9pHWл9j1t8p.%rBk;d}L2Y߿](skv}sĸP3k_,ɸ9 ӕN[y`Oi(ٓi";s$ цoe<?pȭ>liZxYٷbˎ qZfp ]oifiW8Π΍Ê`<;*vimLI~UC"e>|ǐ xA沥.qfy|9B{?x0(A2JA>0DضvR71Afe!0؟g??o- %5߭k<KzɮW>q|φ+ޫ![$n86ҳpp2[Nc,`Ȏ `~1#V)xpm`=ϧB6>ƟJɧ&[WVyio=v`GZ1c@ל}rdl֧<$ծ 4`4e?,}؎Vz7y?vx jkuXm Y!wW=pc/2.ZqϹk;sh~,${RJ8@KY[?Yn!Ips5ӎ~n[trw0<9*r`=A) piZYc|8͖畸@ !<簋۽߶4ERd!×@??wkkcW/I)KEWݮ~{:F8oWqkGiOze{ڧ"#xW8_؏(k{y}\~6D2*XM5/|c&ȡKsC 9tNgQϟ8/E뱿/BcpMYBgY ccb _982G+ԟ8[.9;ֺwx@g}uCGIKJKvא(wzE{p>dXWl<c#.BxU 9ļqck q潈6ڋ~ ~+#9P.v6~g{`.߂A 'O 3žؗ/xO)GNlϧcqfdG>ng.#1ɞ9p=Xw?F<DZBH{g?!geǰ/?hZִCՃri5tw=(v߼W瓉i&W?^!ex8&NcOuVn׻?=W{k5xtLq{ 7vwĮZp]1_gWH_6Na8sXp{̾+һ/2Gy mrs|ӆOl;EwbI T]/Q p!{Cl}%}}Npc<3o?gg_{}sa|Ž{烞)l0lPCs7X0FƎf|T.!G_Yc$0[%0FF`"NJ" w$J`F֮q"\Gg!C9E=4.p0'.-gI~s0VCa >?!ͼO{Zks$@9?R<~ݯPryNc??H@$7|`{i[ko`¸#Oz7 _ Dt}plb٭?]}BgNxބ}E=/R.i,k]5qLN\G>*^.Ϫjo{-7׮&i\#k5׳ |^#8ݲ~ߎřyB{g3c`q2G.`4~ˇdlM~]/^_ooO1*,۪v3{g5 >;rY:vds7>&;=0OW}nOBr2g {Ϭ!v&S2X\o9[u{EȽkF̐ k?@11iͯ3dd\H\A kpr"x`v~EϬ!Qѣɜ\ `o<7SqbAGvg&ouxyUeLΤʯv'X;S?bY(ydt\@ _hkz.+q4x[UWٜy,_0+23@v/1"`D'4%2l(p=|GE(n#_ nNyW8nY?+>*?*?-}rLϊGxF ga'">^KX76f֬ۓA~~rzgocWg\iۜ P~l?|_9Gj;,Ydz^g8_e1)$޻9f~{!=:&g?:7C?'S1O9a-jR{ߵ`xv?[F|>9mOx~7&8J #|q8쮳"y ENo1Y'h rM>"mc8wV~TGV+gT'@mg9@ިj'e7+E9vFXgn;nv~}!{%n~G{sU^*j6},}GG~ gy+,e? Pl 6\4N{WXp?_ڵ6<=}k5:~|rU%?1s<>2w`Q s8._ĉ| qQlȮ<S|Ӛ=/{`fzGwrxWc4OXDz{[g0w׿븠%TmHzt?_ǥb5Zm_+Z>G):6E׶8ƦpV~׆[۞VC7nY/^O#?]sI}0o -{;q?Nr`KF0f?ĎǎձBq;1!'_m>@{'xi}e?wcxU *.E~4jYXs'l뼙sn[ڠxGxEx\3\^n 990~]$]8[h +"ݣΟ%TY+51?g|GyMصړY1f,q)f*}v0|+u/ޙTsǾr=-Ym}Gs``%2lKߥM{^<D7zN>ߖ/yE ˞^]e  X\ף8tnb|w8zj;l#,v4>`1߆ɿŬ{An>w%v@'ʧ6a5c@Hn_W4?on~T/H/6gqke[1wGy{gX߻f=wFcQ" ق09j}B}/ڐ6?~ 2}y.py3񵦜=S:rО_C)ܲkj|7p\zE{89[om5'O))MϚev?~wodzL1%L9'k38Nd5/9v&er,Q?''~O?Vx"sYm(LKϞa~Up<+;v,Nloc6܆Gq ԗz?خx񫰝<'@pL xo_yˇq+ӻ Xž~ÿ. 5(1gh~ٽ8r,*\z6˾s$Oo庾.ӝ|y0>n6 ,۵_<L~ 'Q< DorMG{QX+l{h]ȗb/g?4n۷m/o'w+@J1iKu2'?uXܟ9Iq̫"~-/93abMֆ Fpm|BS2E19Zqגv9Rp.`{$Gr#:?BTdW[0+8rJ_Oowd ,Ev*=RNlϟ13>GmPkl0GqRe ߎ~'D|^{e!/SDqYD.OA5/s"c>~K 6ާ`ˊLI/3[]n}=K }n8oqbbG|c_k%RWFj #@^\Mw+ou'.#7Ga/p,om*Ez=;蚮S#s.xGĄH6';[%z#=ܦ>W2̏2"\] S?[O1 }f{4Gߴ2q=ԏg(cpoɬ֕s<6=m=?{owG]cۻ vux.䷿Dj_!⿴qa? ,NvvXt/:k5l%D h;z pȐHU?coek#gok~k;(c?59VMYc 1+`ʆp[jBcAA,}8Lf K%ٓ?:4fL FW:̫K;'v}1:Pl]wn`%X?|V /)W?'?Z:y?:sy(r08a7,v4' -ǐκ:ΓXak+`uh_# ]qeꗗUq?:*-yؔ'L?q۴84?c7а?]?-usZU;c1_in_)g",0̘_C|v{3WiK=g?ܡo:'=aY\tOtw؎G> [dkQ{ίt'yc??|d WR`~?W?TN^: 5.y\5oXfwkn~=''2zdu5̧XDW]Im"lqsԨVt6k[E~ځ!X3⮅:V\} 0gVyMe_"1+ Etq4cw9Zb^C_z<(S{~ձGopenimageio-1.7.17~dfsg0.orig/testsuite/texture-blurtube/ref/checker-0.10.tif0000644000175000017500000044244213151711064025152 0ustar mfvmfvII*> 2=RS~E'<#/S9H7V79/#9^+EC`RL#ZvF22016:02:25 9:51:39 x\kE~hT" A$($Q]sټÜ͞zngɓ'[:?u~|'O_g}gݿyk}wԛTuwu:}Υ>s yeV գN ,bBaPqF Í)&JpˡT?i`8}ppVIkG^0^ sŋޛ'?{WL)W8sfS쮫^íSG-@4(ݪvr{](K}_լrOݓqk‹;'2A>^?wT_pjf\K45% zQfsGN93s_w~N{w83skgޭ}AT?;ޯtZS{]J#p}@>wN?#K'+/ ?id JS,4啫y]~=Hu&a߈s0&ϴMfu7 ywzpJQݪfUʩV>0l8}VLs^u 82矞 KyQ]޿`kW'BO3Aa@aӒuUsZS<8s⋇;% '䵄IO~yug߃q}F׌.&kԣӽDֵ֓] \vLsOvsGFB:)G|@GaП'!Lќ|:+D_a"S [~+)-TSK0ts/ÔB,(Q{G{ʂJ힍]>} :zOw?fj>Orj&L"'Tժ3Oű$ō'W9?iJ2XSL&>gLzOw֤k`U{]U\r.]Wp9a{3]ׯ?z$P{r،R(ê Nj; MRGW{j3R'9]J',siS=ﳛo={{OTO0gtzS*Gpg(~L}_-_ oH,d|dd\Tz{rj~t$k]?hω*@5'\w)w+tQ& Yo:z5wx/E7|cNw3گd.C6IτH$\yz]=OZSc9.qP@čėTi)Or8'}3ppG{XN]氵&t,{}Ϟ=%ޏcݙliN|?!ôQޙsG7ƺ{{+a~S믿M2`I?7OX\.\>;8g%ay~Φe%֓,oN–d8ٹ8r'1%WgelMf滩~v3EC{t~]Gז{'{2,&/LvEsvޏ4Ng{XS GMN`~ur^ ;{{j& kޑ\)1 ϓvUy:&9{?'~9g8|WiNw}G?z:1R 1J_ɸjEu9&k mڹ:We<|}B=KOӏ{Q{4!TY0P<)t/"^Z'%imX^|,AWgh{'{ ~͡?+|?"!4kNjLLCLj<`ޏjvh7)} =uIH'n'Y=.pƔӿ@8йt'IؼՌ]W52rn' Qk^;ŋ7Y©'9{]Yn1 H=TO=oDZnlM1,\{Sma:T?,'3{fZkFsV|V7qi}Sz \:}b{u4gTk6wfdzLJYpt˗}%:{uzd_՟֞K7Y+d~X?% 3 ḫ?{҃IjƮtwgbai0:TQlu+.(:jP6O?Qfv?ko,Wޘ_kWsSk>d9%TƩTxNlRgD;2zgU38ceVr":/&>稳L3N`>hsN9^;?f?zxt9N0\9.^#>3M>ccb-9.WN :f$y.KfU;9Qϝ&ɰtD՛a% V;8GV5egNMMR²ë#Ejʏ3z1oիWؽ\5 4SQ{d8CYczFI^g늴:7l3Ot <yrz}_WVPZ}iQUznNs@_O}uyjn8}]{VF%>V=F?3S}8"t^z];3@}_; WIƼ^w!ܾdoiF.)OLjKtnDzꜜ/ln1e5Rד?so׫jex8HPMS^06(%`WoG ;kjfOmXy;bM gi 4imn/-cxs Ӭ:L37ߞX&pPO{0r}ֻ8Ad, bݱvQ&kSq.ˏLս́aiOɾ׮?N<"MtrGL̝ޯ0 wڒ=Djݻz]MxPT-<:].ܕPm'OKէRI} f7?ԓf_~5̡tK:k :Xv_]\>SؚNWt?сOW;-Lp˸xGX@˼iQ+]`Q Puߓ,fc=ѰdVyN?cS]~\$k8=P:х]O׍37SmD=u;Lu"zomwVIMסqu}~Jޏ-Ѹ?#BzxLzȲ~*K>&,>:<2xiZg>[6OjF,רƝuMP4iq9_,>gF֚i(=D7Sޠ;o39cR_^VSi?7TZ(l96|Χ5g'ϲ~ljQw>3prIRr9aqae gi'\]Fff3L- orKծl<תcHgEG8S9ѴZ'y?3+U=וy?զ ieݓ?[(gM25zVajQfN2T׺$T~ZRD5|f:2{4#miX~S0>)6|YT_2~':Z#Gœ[+3H4q%wq]Y.v^Y[[ޟ'%ˬ'U{$q>iIZePNF4ng M2Dלn#ߥLXsgXO۷o=Խ^LӾuT4XJyjϬdUSLh2fe:ց|v+0MPYqڽVW3)'޻|rI`ɉW 0zӃ^~ϔzv2 ^Yߺ̏jN3>[:߽{tĽj]=Lzz|B3\Qkd|gR]g)dQ5dZ3*,Ó~sΞK>P`Na-@zNpk7 ĉ%m +'ޯyx]FD AADj&aQVժ'ކ8sӧYY۷oz=uǝ;wO]x1׿:15jwz\Ō1buteFW,6Vƃ{`]X}XU<..Nקx^waUOzkZC5'{4 ֲÐz\yQq$ǝ~%<3ѫud}LWwuc5M>O;Nwqw\|k ƑN2c1ڏxye)wZS^s']x`ܣ'`10/;kV:Y4)A?k̊:t5o O%DIy|lD{]tyuqg¸9 Ϝ(P-O,bxvMW/3dV;]s١pg?b؉Cŀ銫S3f25 vX=5),z9($qxnRbF|]o{/bS1ތXt3a{wq(Lw>@_Y<)T`T?b0Nw*(j|n̋9Oj8BaΣ~t)Ɣ|Ngk:Lu{.].-v5Kŧz5u>_$ڟ/6z#:RyL?n:}Q>=I݋U|¸*-YΗ)xՊΝF֘c敕qƇ1&U*Nݒy/@G1_,!;j8֬fG穧3.kjKJP>p}c⥔r8)nOtaaWIqÅ L'/4ᤔTuz8 UMymxR}T,މHeɰ㜇;!x5Δvb;H4Oe~cJd`C&>?-&;^OW!y3yTs356A7c{'p´rSܮ5;Չq<ĸ[e,~\py3>]F`|1L?1 ;/Ú8]r(%گ7U3 _+2?/CY8(Ŕ73k[W.A<^OЄOp>-BqƂZQ4[V//Zk%&E(>4ӘwO]0w58K1X r{Nzn#uϩw4_׎0~,aMz!wGiiCAI%z3ilNյ {Weշ,'?qۿ\-]&Lt% ;x5ogn`fqN?>|N;f>SRyP;a L߉8NYLny3-uOݻӌuZ5LsAL$V_7vԶ'zr{WXmS/Ǹk_o[Y;.Q8cǕq$Z $3397~żGu__/Mz;N!]/u?6GLf Ņ)yۊW'97;K5wIR?m[x8Q=wEʟ5Ibj_Mzj}Gq;ߙc}G+~My;909j-p%`&߽QT;gRiY^1fi>P>I~IaTTY#b?jgE~lS|Z8Ot~cO&{vM#٬kGp)4,nfxe 1zO' uc'yd#n|xiN'uݻw7Szxkż)sR*ᜩ9X&oױtshwss^㚊5LS~_WܘhTnW~ *O:)&<:8#*x> >+rHgk޺";nLaxeG۷o#OoRC%9D}T<7#wtyY%[si"YY-w 8Gp1ݯCDGו땮17ևF/lŏ]/v39LNtygt_zޛ7ow9Kp,s:k`ܬ^)&]3|cgO)lt_kzHd/cOuqf/ge|3革NS{Uk:_0tZ ʿt̮\\ܗza3YVi_~}z痑 Xw6sLEiNkvd)^]gy<~NXoao#$'Gz]G2幾Ƥ u/f6gH$br8}}:02~z' n5J37M6MIx\xw{ nxwq<s,۷p|}Ӈng|bzelGŋzn&c~XwxAx拾?[ڃpGc%w3^P8[qw8za , agW͇G%UXmukyd{ =w=oTL:L;H0hY-;N˦}ϟ?MwqQky@ڷgj8սaZ;?ఛeK}n)?}3/ mn*1|$q*o*f)YjLSv> 57,_Y`]swuv]ݻ?ռzqlFTWpݜ\*N#3C<:o˼Oc\9<@ ~Sfs:;M?˸fZtTϜgup[ӧ7(Ϥx/:c2 qf:T}?,{xac?$U6t34b tN嶔߰g+uJ/)Md!s%Do|ŭbZX0XZnUy.kECXţȱ~ơW0]`>3z1IT\oϷ^֪o= l&)~6# u [8aIK7Cv|m ϙc5aF::Wnqr>| *Wx A)|+<ꋵ3;=s>&o/'&bxR󁪷ԩt_'i~`kd\#Nf0L5Nx Sg =p '`w)8\{O? 3SZmr=`XW9d8'6ަ;3őӋ;~`ߝ;nWg򁫵=[QcNSMu>c_w8b,/.9S_;ڧr8ANV}k0~*o}),;޿xR~:x֫lI+w9ܫ\4'S:?u~ZqN5uAtkS[`/ McmEdUuV4{7(_W;;0l9+)PQ5#׽ܬr9i>[wOzk=A;V=g<ưWwp {!k~>p jςV@5ߦ3ng߿o߫g9I}CƨSWI<=}@ľWW'RJr괟);?_qخ=j-1g Jg?v= Pk650/ŰٟgIυ$\ɴ3Ն`[}6\BIx DE" 0"2g܆yʌGO?~t?9;Ϗw?kg |֟x5Ɠ__~>uCO{8i;קk=E?Zzh׵׳1oWT5~zEMZ(kFV5&s sŃƫ};aǥ{8J8J lj[n[ /4Nt|'/{|4')'oL|zzzq{)q,aӴGP5I߱?O?1af(rSgT <>![}Ý 4@MӸ#m5x5uCwj4Ũz57|V1մc׬py4Sj=.TSyecqĕɃP6L9y,=a陶[o:%řfӆ< ~7t^O|x>my^%MښzI֨?GpWC߃Uo5S}ov/MvMx~I&~HAr KzGZAC-SuޟWZOMq]? zio^q5\L{$FuMr MENm~i깩:_5$]{?78lkΓx>1vYK0 `S۞4k?|I=F QOwdk&N55?ޟz& (^^ )䋒Hܸޫx1 n߂W7=Sjy~;}䭈怴mg[im%]CQ/:7@\858yԇsII\4\6q^ v@>x>ƇL NuoīW<Ʃ۞9.}RoOO}|ƹj@qй(y-R"cj-wVOm"KS6ئy}KK>;ݩUzO:M)gһiS]^Iͼ>ν/7?e{o!u\Kny%'74 `msq?iMsI֯lVץtl;q8yL=pPsk)aƦ=G'kZIc2[⻦hz`9 qVg/=δo[_zY @1M={\Źg͉k<|ی.iܤ' ]O%OڇJ⊦=ƩGH'9Yu=i 6ߣGG>#'r5E>՝nZ^#llR,hIhr>iSlf'5 lzTjxU7ua `}ito38uO;Y=Mӥ#obTkv5i-5k.Sa͓fF08j-i%} z]7cOyK97y7q_g%8N曏w\&֜=c˄734w{{x?S=*iuS m?A5+7=4~ϩr:*oM/zMްag_7?=3uO[L1%f}Atqۤ?B(QSO(~R9R|m^_/ȷNָߵy. o7D5R~zp#ayt=0 ic߿FLiv&nC4@⬔IKyrkO8r Ǘ;R.Tl?o=?-y] ' qH6H^[olj/<g ?>y1Z7x0~6L8j˽:7ēRL}B1]~\;6%t`mraICM7%PP74@Q1i~[ @X1'~oznk\N87q+i|Oӌ"AY^mvコO#ng[~5Tk7~4y-- ¤3o̓+ L&6-M6`kxoĹ4;I8+gQ^o 67鸤W&߼Tg;z/MOzFO3O?}mgz}lpnoi'O|&^h<'gWOvPL#zgMm N$=C(Ok~{yg'MC|֘6{6Pk=t38\omS<:6;Q߻S|9_ӞN=7J}y09STk@ӡn%y:h}M_7cAL \$>ULޟxaGRۿk|!4kuHRk>ζ޿Gúw^f&n;͇4Mz&o;\C>z^K6?͙.f%7s 7*^A?yKhħ:y9S3ҝ^g7MuO|=I0b:vӱԱ}՚LXxߵ&4;X6Ilj_9uꛆwn<%O619^֍뀦HVP&G-x\M587j^㬼'/n9O?I֗F1_y:kX5qV!|O}9 ~NX٧zg}ЄQk9 tNJF5yOxAZ YiV 843=f$7ss-yjlq0yjq~9P$HuS=?u/⋗6mo'M.-ws 'lĹ6']L9L{HcA3%.j*p} BTQPv-0='}N`=J潷x0ߵnkȵ֭kY77fXO =$隚h?pF$^<׿Ҿzmo^v;70ḵ0wW'xiuXٸ_9w+WTS?q'=zz5|0d&[Hc[lIp[׍njy7& EƄ=4{9SZ,7g4@5/2X_Kה:&;iIc&Ͻvc+9ok"ͼ#=_>/ȉ;<M̏iFi^-{j~~u\Ұ,lyF:P]mz|ѽz%ltIϮ\Fnsǃ\_}-OkyI?zISkjM `ҍCqt9S#\HI]OhNt_W\c[#uUSh->ԟs-VqR-~zNS)y+O =u~bs[sR%!zRH `7ج>kix2g?t}_$m4MݧQQfM_:99y7sާd¤I&hsc >h>#&7) ayto_RS.Әڛ}v/fo~8l8HmpQk1O޲JYix$G.,dӬ(հƒ[~y `ÓT~|Gc3H[ƙNؼyq[)%O8kvǜs~^ing<[Y` P_7s#F&o4DӱzL3ޫ&L^nhckv_[|O7DXxXRmtkWc=}9fɋn!i ӽ摧# psN .o0տס{S?YkSe: hpCOXݫTIR'ϹѴ;HS<~xuk=!zZ߄' STwkow99gotii48N뚔t? o8nS=h(FhN?$Nz0a6s\Mq?^ZkKrϴ&oږj݋NNҲz4Stڼ^`*]N:qM MOGQ'^;zmXJ0?&m0 p ƵMGMZILq4? +S?l˳ڕxG+Nv xmյE+"" A !5?ѺvZs`Qeթck?^~7N+|wz7<{W~<^O?׏?y303z~ws=?->k>_wr-c}Wc~xϏk~˱^y88ܯm_f?f޹v޹yCuj$O[_Xy'^>b7ek{ww^o^[ӛ3 ?s,'' mmr}'ߙ3kZƙg̳M/[_pluu~ncm~fANӿɞ}k{m߸z5k7 K7W#k2{c![a_翧9}KNn5I^>S?79x @#~Y +r'lJ{y Ùl:=K" ow/^Cf.Y9'Y=li<֦[Ll}]COrY;͖l^xf܏O융O,f]NQ4|`~;휩iYS34?Ӳ Snsr{VOO3\y_T۹Z;̬{4@ ˆ[ñ[.VΞ6ozMl`۳O@ r.[nxe0OLӎi1oe>2b8}F;Nۼm7PY6<}' gnjY p;/'>}wHyz 0ú}f/녚=#tѶ<32ܓhjdznȕK~~?߸4m\srˌNߦYiu3-oz޸?}w1/l2'N%99s9ka橈Mn8xd,36[o?9ix~6 >bm9a)xs3룆YYntiU?[/oX[7zdu[5Lio>aEr>ŜrlV߲4rÉ&ى2q1q2 <[cͷ'^3ˆ39t`i86.ɛh:=M^"'kl9Z󟹟7}ue=̎z4O82of<[K̵zj~O~5}noX7~7'n-mo&'"v߲ )mG-'e?#/Ӿp,pgoy/;;7_tN~Qbq~t:v/jĂv&in4Gݑ9Ή''kZZ_jj<Hb39ukxmEoyqQ׸Ɨeykƭצimvqͭ>88יy5eӯ24eӌj/SX&fh\`a:Fi6d:vߓ .9G`e3|o{ArC_k3ީ[f]澙o?_?mYyy5a7ڄui79yM7g[loٙKr?۽o|8>5qw~悖9=S1Y _ғߴId& }61}W*ۣ7 ssۋ-w0}c9@L3ִaS&d~]8isƓ'7}95ΫiMט֖=npӆVvviOp2+4h^ē9﹮y/ 9fG{NdH~Wgě6v~EӢV-K?sԣYo־iS-fnFl9-j8wkgp-67/:,Oq\7~_6/=r^7oɸ7i{얝gky,r~vVlyoÿ|y0W2~m9/k3kY?sOe0kZx+25@VSM4=}`gIg\hb øݲƺ kw~8xqdy_Ÿ77y״u ӆ5ɬfML.֧_[ ۿ+L f27?89 Ɩ/l\E|eϙ~nxkl\cCOzf^ԈmmrsƗO۞ 9fd4MzǕ<Óɳ7?4@46/ӁodffX31pVrr 82Κ\/!7gl<1ᄫ}97^3/>[hӵӸ7ghطo#wmf9v,+O zt糞oj^Ys׉m' ޘ{$[XN7o,&%?nfcJ޸o?3GIvn2jÚoYP'׌=F25>9q?=rzrٗE<Ï[1s8)ؼ|5mB#'7uשMk=au,Ljl^8NX́lc׼ω9 6hƏ[N߲/M+7L>ݰ9b9'}~#Xvk7~4J^l8``^<|a>hNyٿq|v2DF[ʞj=4?I^=sSoS3õ}ݞrY6ڵߢ/z!Hm} kg[A~5}><>~xgY}7:q\_^=" cWvj6se0p7s}} yxƟ=gbsݚ72ęԋ':93sG17|4!3y?qW_}Ҹ|9?7_h.z)bTKfyFa|\JMeg{..5xbNÝ=b5ך1O;'sfvٳyΛ3gjՍ6gM8e><ɰ~x{ϬS7Lv`kϧLn{53"n~\3#yٲu0ڐj8k+YE&M7$?Lmg5OB |ټXα1xfn}|=žLl}?{ͿWgk[Nnڟ .73\|h˛O8I}ANO|ӆO3جgN[vmntmMj`VgR{2Su'}y4? MoMF#o''m^Mw/|^oM7ͭi˥Nuq?r>r<&W"n3q&??y^&\a\v^?1g-+ox%{2-$.S&n5kkl_5c꬝N>iMh۳zS7L5֘19\{H]sJ0<4Z_yܘZpvY9&ˀ\u5>8ezք>_4<@!6\9i,ϛOl2])ëSM'՞M\,q)bcfr9\_2}?q8!Y yy>o lOb XgI3oXf0nSP6-kyˇNič\7L?RϜ4Ir oSgg~L2MB}h-mix%͟nٟ+?+2ۦNkkn鮓:\!7=j'Ǽyοy5c|a>}xe\[Y_^MK szBLe?ܛ?Wxkt|=,8<ɫz:g/;p(r[OxӴ-}ocul<`G-s|+/s{!6m'rW1#qNsĆg˅we7x]J.ߙנGڟfR-o9VWvn5k^b2ޛK]} ٫7-so>!j3?$4kE|rɍ#7}7J]C!qnIERPmXGbiPج?%jc!랾3ui|cb澳nHў5iY(U4Iooزmb^r/ܸ6yk;\,{{i5tD_prgFyvu|h^?t/u{_Y+m5ޥlc]fo'M)E-OZNN<x8ytkm|Qmr\;ݖ%OvyN__񓏙y9/Ss'$f"Ng| 3Er?rv/HYc1[?61 lfc߲%97gћh<{Qr4'g ig7ql'gX=v-3bHԦn5=+ag2% `; 5y6bm'OqЉ'0"oY'| :r-oF-sq9Z12m9؍ rX_<0m?sJSܖo5loL2uʖ|/\7f7??6Y'f97Vο)kx,?~^kE.`M5,*̸rLwsMfk 'j{|[+I1Wk{mfVC_3vD 3gk'uyUy359zob3g7{ޓY &z0qvZJx({\|n?cXwָ֑9Wu~΋msϜz9ϟ?ױ_==?szy6^gߚ{^z{c}ne޿ۣc:Wl,s\C\]Y9s]s=\9ngfڴci7/Mc54m>s r)4ѿ:r> 3=Fdu3=lv8/ħ9f~5>cUtcR֛kM߾O<4.\Ǵ  v8p׺^ ޶y\)6|\w)~i=|9%#b|p~D|o:+۾n3A8 ~[1s6NN\Gqy3V[_j*&Ѷg-&>pCɁEhuWr \kSx N747Xy͚0-fg\SNe͓>N-n3I۳-Zi$5)> }=S^8ݰJAI& >9i Grm]IO)oevb=abk"yXNjcO>ߚ?9Iz:cޚ][>X uL/k̉O&fevj֋\Iier_&l<`mo{0'>嘭&55:fsס֐MҖ7l3',㷘=3o\Ju5SʼOuS|l5cQ -Rt; oN;'_4fհ`s7Հrk-6,nZէ'av3ÚI=Mɵ:Jݸo쏯6o}ѴglMo\Iǖ]}9|4m}:3l8>mM5Mؚ9QO /&jf]LC]XS>>պvSk& N}mSm_7<MZN5vZ:^6b/Οv:\4>}i ]No,n45TZ^1Ni=kIXeVۼs6Nu^4s^9x;q kT]MnZ7''hMh~q3?ڂK˙\c,}']nik?cƂ.|K-/t cEQ"ӫ5)osm8͹+"&|ŭk^7Jz+kwefAꫯ>߉hcRlSdC0tԃ`?Xcض)M +h?h惴nkkNu> 9@ݓsn[37.A?1)Sw͹O\_MhgkZ-7%6?ۗ^O~[vZs7,mUvg1^㶾rEY/:;}qye͜jaoc.fhWJ֣c.%Kw~3~eBb0Vp5G8iŶ]h|-i<չ"sXϟq3{M=XeMp5olwGqarٖ+xzjaUkj͞SW1Yp-ǞT_c.rljb K' valf?c]O޷uzw{l6՛&^Niei?aGE^?sj܏]s:=3uX9۾v2kG]l 9N^>)s϶j\؞|6zLׅ4^v`<|?i=wy>c>gj}.ŮfgN:~9cb3-WlvmNWӉtUL|M14Lmmz4r~ 륻}o9ʩt|t,rMb./2蘟8wcp^aIOoFI' Fc5yc6Ex?mE-znro<'u7Sw3Xk{logGAs㿹#OwxΦ.֍kڷ=8Fc[h>S֑pɦ3|Fpklȼ;a1Yp9s xbӽZ}~дUʮFK[["9;-u}+Wsc~Bԭq\iIp g8u"yd5dˮ}["оX+Qxvwc㹩5 02Fx6-g7]vl N5n `^'nVMo)mGܟ?>q-'o{n״:˶|ʹT˩yMߜy;a\luhcSb7ן7z7nKDMy.5ƌO޶9_ӷsҾ'Jc cgCК ^մ\ǵI+k'pV9j%L|7;8+kUXq=<6ָMpL=cNזڳs3ݗ4hcFw9 ݧϞ`uچT.1qúI Kw8j~h k[̝&ܩ\Y8#p;p 4VL? {o'j's_kI{&'x0]x|?`͓1|ʁx$:k&:^c˹Lͺ7 ]7r vGbq]ٯ98eWSrOk=npk{ ~f 7\a?a4vmoIL.GZפ/&F ĝ]~04t&L=aByЯvx徇)_jw'|%%o #;mqs]S8w7m<_Ɓ\p-˹f|v}ms/״G3NI^k=W>I_TN.q?}~6s]L>cZRΓ8wɣv 2?r=;žWt}0v;ij\yTTgȉZ {( D L$۔2N^ɗZF[sdݛmϽz/zZ|ȜbmoO]SLvx] [`opo駟V7x9s6/-ʽmoLo`fg1:W gyӫ?kWN٭b`#s>5b믿~ 33xG4\?q̘uݵ+LԵ`!a+V;M烚VT\ ~nA2{=*kE?ּ/ Nu9桱%d}rƳ6lha"Zmx[g9~wF>v]־'n&q{;iVﹴzTɚX[m7y:x1.ߵcƈIMxa3ӊr[?x~31+YCԚ^$v鿭麽5O!rV r?*A5hmӹXo.EN>-\5UQ(``~.~Dm k~}sxt"sSr\/}T-^6u)6>B^ŽmNC?iŮGLߴ c'\7?>zչ&Ʒ;ImYژcmy_|#v=$w,}33-r^-Zkq+ח5}Ws^*S5{ \%N$޺n{ Tp垮3r YO>&`\2ǝo=ߺ{ЃVXu!Z"v{V376~א[G;PVO=/Y_OEbsK[/5{bK@~fzĎͭ7= FOb"t{[r^hwV1bkv3mJ?s9# K?v-p-rQ/\=FmŖG|ehdp emzSȺ$5Nb[kĖ\kvGvO[{0qj5~llju{5\y˛S4ϓ.1vkގrgKf_C8W\hێGZ=1Pkffć+Yo^+-9R7V>ηlO݂-6Sᘴ+DfcI,p`[4SԜMyuz_jc^Sg/s,;]sF/<)A\sk|?R Zӿ۽,%BnqSnlx/m-?k]w9,~pʫVߴss]_D~Ԯ-UOh-k^?id8`2_jF}[JlmS]r9-~ӽ-y/ 4p$ _Ⱥd>H.a|9w龱Ü@D=b\&sR}ord9@5ڷ~iEȺMlwzrbT7Vj+k)GYh|}8M߸/nj Oknܟ0믿~Z9?s~miHtSM{?5fWq?6ZO0glEO͝ڠ㌟c=xM΃+:ckONNU_& HkNY 1SZ5'S)D_g-bW}J>q͹xon4i:glvlp5p,둵W47ωZ@+s^b:oZbo_p\0kYRg3x_W oֶ>k*.s_902tlei~N7Gz 5:OPahe ȼv@L\ys~rp>G\u1!7o漾/|u>Z3v;5-o X=jߖ3e+aquISr [܇LI)MiH ?=QS"]1:g->?i>&&Xj&r,`S &1s&'ȺD'`ZcoxLO\}~Rw\}m!16x{32Ǭal5`ګ{+&Mjbi9>8r'm!<0PlkM}3| |sm!!=/ 5)ypZYg52]Z]ܴnJtQ`o 3vkٰ|m{WYo䟌Ϭ=1)̩2/9ּ緀W's;ws9_pu~BΓk,w+[%(牎ͬE?ުAclUp#q '<= Owfr4q'9\'=\/sɝ-5.IonK6nO\zI+9[OtoTr z܏ #'_G5^4|v{2\k? J6Avܗd޶>:]{K19V޹uȿBy~T.6upBijkbL܆?INȼ=f[n%S>fZdacuUս|͋J_wkg^{՟,ps}]w7x馛N?vQy?__O/r=wɳiWޥ~L}r?o0[l6Ү[o+?tOHRWJn+wqOKYRVϸo{RcS?Lzw^u]6}ݕo?pz?ey.m}Εd\Rv\y:rOud]GƟ?[\ԗz~~" {d1 7zmC>m=eqM;g9!u~|Ly6\d^RF<\2iC.S'Hۘ9dRe}٬ԟ6럹@7B^-v:urij=ncWN78vGMߍ[omj ==?f9]ɐo*ke}uƄY[dպcZs,gyk5].gEwDw^tP^ Y7Xr7u\rMS3o N9hϦr mO!"r6R<{3BD`7/ 6$gu`X@a H0 eg390&Wʷ$y6eg7≯ߘ6`۽;|4|;rgܑ~zlzyμOrԅ Z6dx7߆ܴ|뗵nۻ}mr?[e:u)rEy͡Z2-&`lگj̰m}aބ+4~w,w.z|an52-깣m;;1wݾ?f,>d-Ʋm z6xӤqy:;ݑ7|d)؏2 }xI?]9-jJ R>sjߟ1?xc@æ!WNz_/{,Ȱc?eᵁn8Aϥg> ;,jyG~8[s J]3ܶ 4 ul10XXF6PAeaے`dK/~ c[:u*:щGԉWÐu_qlgk4w}݋ :w&>v3+{m#6@_^9߿usc۬KS|{m7?bB۸͍LyRt$z^p^W[޼D/&.{=~N91 n;]F w,rк,rҎ5wdNGs@lt'6mBh:Ya%<`al;?b ʔ+mpkdαbau5rǍ\Ŷu\vzMmKvyIG*V W|d֎/_a~.&?b/c&q387&Ubq3'߶uvL=}j;:a΋5Ƹ;ꀓG繴/{= t"}1ǭҦ1mN| xb{ ΢c ߁CO'\l=4!;Z@qD?Maԓ2l70ЎEP'e 9֐w=\ֱc;k6y 9ϔmpKp nGM9 Sw\vOm[c:=Ckct_adӳvbݧ~uvYwsȧF6*o5ݾlĬOo6ϓc&Gg漝D gޱXr:|Sxnw#NNǹ(ß͋w?q~Ooh =p= {Xp1O2Yc:g2hͩ[?/a ^ҫ`sXZW~dO~'Wr+_g~[_Sz'9:G?y,ߝgyW_;g|Xւm1x9o0mnv_;\z_Xx a;G_杌m wsN܀`s䵁ZNyn=Od8O?&kcnй--?/Xۮ![rGq^˶L9pGN"/i7q$ۓȻ5re?)bOnωף> zz#G7\':2u.:_>I] Hcք;u\ 'WE=u9<_saS,=V'\:WO|Ä'X6e}3qq(+~:ڶz[F}Όca}7<Ӷoj}k}D=>'=GKѱAcy8xIiSt_~yjmxѯZiKt!mH.G>̅kx%IZӞiעѡ>[g'Q C,';m#[v,9 mAs\7ryܤ)3mmnpΐd7Rs|Ʊٮ6 Mչco?K4M2{\ ӡ[O86eNMu7pl?{?g?OS.jlWzpU&:^&e9gOv0k mھ O׻orN:y|j'mNPk~3B'Em'rv# 75=}Iy t sN7 \!su7לueA_u^y^;xrg\5奬M'MJ{s5ˮa''+.X#_%0lGM~8O9y/' cm_tN܅9}h0^}^U2rO<|5;2;\,9Ӹ֫>GTؓ=Ч¾Oc٧|sk߶=;Kѳ𲝓K߭Í?Ĕl );』p6l0_ok p1fp}HȦ9SLyv~<ۛ6|y`&b\ p<>u\gI `d?=b9mAyg]l\quBg9AF0.>m0`70=S;ƔƦ#O~N8߹G<';iM6@oilm+`5Sd''89aesn8o޸{l;hނ06x,,ygCy/s6T>biz8F\`5v })#g~s fsqq/Lps'[SďR2n^l'D [.7yy¿~!M츔.COȝ!sw-8Kgkh[KvOˁAys:eڳo> zg:R9Lzݺlr.)¨sX89 OLnwZ:s \ʧE{FmRu5ּ۟=btM.W^kbW5/:׀Xf.2o NE~z{n|ƴ!%,Wc5|4բS_|qO?7^u}e?38ud ^a=Lzdr#y {,|~3XMόkƗ<B.M{?Ϯd. >wd^$smWLM#\s2>ýp`VmPA@O](/j|ZmU `0=w&ȑqqcٻv}I4^=}r98}/s :h>L4VS^)/[Yr39Rfį3^ORl3~1/'9qʳ|f>mZN{]mf"~8a|1픃H=r<>s96+O3pD,Wp@a;QbS)<661 is m_ZEG:sگUWȪ^@qd~˔ > WxA:VBEflj2^?WM*?>M6;\nn~vsgGfғ=d1G|'sG4Ws>1>'ݗghXkNJÏ: O|>?ug;!]d]::s/Lcu.|I95cN~Ft4Fͫ7g#-A945[GXbkr 8yo/vqcX#|9q쾭t&6Y*6q;nb?g4Eg_ꀟLJCt^t/:/偝;mS7uyRn?ϧy.gi-\4mOٌ'1y`d݊BMNwC~hU&21HO7GE4^3Myl*lc|Lk#`D.YIƬa)>-PGT9{9߿Q/+2֭ʩ7l#prӇngcJܶkM6xIg\M@o^3 5.:csƙ|,K>ΧY}+萻DY/;X1eŜ9Odz;xǏ+m7sg2NҞK{衇N9Q1ܸ}A<}O}y}ͳu|^S:8=OǟaYa;^ w{1p".a_eSaD zRF;<@E"ؽٗqssK#}ހ@D < 65}kp{ؒs:ܷ1-'y\m~<;/| 5I8:vQoo7iئ1_ӼM;sl1X?w7i(>Y܎jC捽ߜuIym;{ħoz/b`ڙrt5qWwyprPR?| -G[s9&@36s Enw>K_zo206p~Gx :xz#}'G$oN<7q 6=R"/`p%.o>lGž'EXK.CcDL7I'"kw9=n]Nm;ɵhǕF6+_a%s_Wqiƾ؜DmK}O3E_Rnl#l۽siY`94|.IrS;ϧ혢y {l~"ZEGvNUl{^tq#$ϒ ۂiљ彼Iyyn }s>1l%vcw%˔n,g\Up3!iSdӎ/ys2y%<#>q=O]<^KM˅!?+!;7}ٹYu$s~r0wmcWߺ\Kt,MbE?WǍ]ք &\2~'W.2k]{f=%~w?Om=Ǟ 'ceLsrc9lV|\`?;zt~r'pĒo~Ϲ8b>z_|>6:1\=)ypl?Ѵ-7p<>Sӧc(uco!g? Vw??7'@<2ʸx1v)+ԋ=X q2- 9g.3/'l͕q" ~r/l 9kq1^rl?=2vA90 xНK:}F<7bى?e{C8~ X1mȕ2ϙ{'D>@}<=سG^S~7r͒_`+;sZVY{ݞi(ڇC/[`c9}6D&?{ZO%cG;q$^~_y啫S~;11gixFZ>I';;8mcF~L㼮\b?;|n671xnc!y, 9i؟;}xEN~1#:&bϤ{|֔2A*#ק-؆K㴝'W3k;v;wA󽺩3ʸe+8S ֓2Ҟ/I2y?= :sw\Y> 6oo+d;ܸy3WAF/lw^έGWnﵱ%0l;|x47ys^~~嗷VUy {. ;O,QOlZ^ږiVmGoa;g?i>;_u׭qf8vsqѯp>N ?;Փ2RG|8Ii }-fޏ]|!晟ל0딣`z[۰~w28=\:O;e2n3np)<3_{rc:8?+g+K=Ӵ3}- 6Du:^v_sNK!w бg:3պ <+csm)^=„M¦)7mM;tv9 _z.elӬl:ynWcnj,}Or2eKi|is}j9#ߟxY8\w<й;t (؛3;O+e憳GǓ)/X>yzyޔ?;>ٔ Fao_' y9`ѵ`vm9& -u$KoJ9K+=3=E,yʳy/yLS&C?#y}cLoYM+% c]c0_ho5'eRgn+Y+m8sti^:b];p.1m.\,WF<;: ?y{sc#0) Gk䁁+gpOƩgSW2!uJ~ᩎ<#FrsnlqL}8lθ#;OfLOge޴%8_ڃGG F0&[HȳdS2Ǽ˱d 8σ[p\sV|o I}bI>8߇1aͫ|udo`P 8 xN5ֱ\&{ZOiapY&v9 /;Ƞ)3*ܿ{DOsEmb+I>WGls>6oyg9Nq8`3[mL[w G/^=}O]#~|:Dwlp(- _x_y.fڞK9i|g.G л]k|'s&pl&;W$6@ +8幌[/V|t.ge)SN1yM;gi3gHrgE#y%[$~S><5q;'s8gmr;gԸ'y.Ӿ.G127ps%&ռgcy?iߟ[-+:׏v[}Gl-:cY9,6Ҭ=<8kgʐw Tl;D8\9Jb7z`WrNG,t\F<Ķc`E>l|'N\O mR.G}tz/f>^<;|=3#xaqS|y=mϙ[b\8#eg2^>y;[/z^ KN#7_Mg"a(y.'3'[.&u8&u$B,@8z9u,6q3hc/n=[Ga|/9`g50qnvV9Sل=܅1?Oʔ1+[<.mۊ{W,3=ĠskLqO0jy-v#NO{꽼?Ηhߟ'9EC禵/c=`NCJ=ly\Y|D-ms1Ql%no)?c3u55nY'ZN\fo494^OgL1ps`sL}٫6fOdǶ/=;)`oh;sWyS,0q#鳲ȻAw7|m/|vxz}>>++%҆ߗ|tprΔ6q8˅\dls)7uŷNcwuxi/1$6?Ϙ~z#9p ?T<Օ>f-vb}C6-]F?'عU'|ԉ0z$`Ǽy02/G0#s۶䄵ImX8@s]=6oor7z|G9`uu*cرu3G)'gOx@㏟|/m#6_\)?}l8W9ʮͫؾ<-GmǹNUٙ]-=-s++8nx}&g]s Vq:W>&_1WMҶ_ዣFq|>sL.N9OyO=IOwS/z5FttG1y>ƢL8 {uy֓`.Pmy? ycp)EK|Hg^2?y9o ̹@cWRo*EΈC̿􁳌ɧUoz0bk_\ ;rn!gHY䔲1 | sz}uIڷ윲)'G8oh?9ƈhXGxV{vod'{,5xuIрǜs9CuUQDD%5sv>,ϖwf=p;鮮}y_7?o?zғt<)O|ԧx3.{|'_>\X_כSOxYz}vg.;6c;r_8&lfOƘ8.n}W>ϼy}?qǛϟ\<=/~K^\,_W7.mh;Wӟ./Üpq9.|*#?ow-ȫs,~ ^o~c2o> O`%n|wЅߡ}14O]3x^'_|?2gOe K &+)^8׵uXq3Eg+ Z; 5S$-?}N>U3*S*2Ot-IO(Sӷ:cv쩫;¢|-nmNkOO̙:X=잺XJWKSq#-$S˧.\;mZ8A`W >gYCV0/DN 1%`0;qgKƥOe||)_W\m?E3G׽կ~=pp ?Ќ<Ǽ? y27whf~ŅKsҤvcԥ)TkK0m!y>Y͢ǧz<]v GP^_z@yxFZS<mq o،ڀ|iSGZVZ֚-u3!3́yJ2G`<џ:__VrOR6NJY_٪V_86F±] _UQ_oӡSwG a84'OtIpYkge[8Ӯ㖮G;ƘcW;KhkLx?t;t> zA7O][?_0Da̋q+y?p\A؃;ҕE޸hhzc}6 mkK[dw&ʟn|-WWO]\hܴ6|U}UUW_u 6f8Zf''_v0g,E,-km\!W\<3m`l̤Wq dD} eryw~c.ȟ h9|%]T#4?c#7 iF>mO;_c"c|:|]?EО3pw\Q_sch0--+p_[ete䧮ޯM< Nצ.~N0 7!̛7yS'ghk|wC]t^xF_W5^ q~ofJn?gls2ˮkil.S،&.D.Ҟ{Ą#9Z8d!W7`z<7/IXW! ;KjA.3fcҟuZq9}ʁS`L?ƅo g\<=s>Ocn.RH ؙ [#|'p:chXt}K3rA2reƋNLsSG6?_?e:y"4?2OMrިLz87k$Vg7~?VS-_1sa_\OQTO7FqXNQ[":vkoAt͵dJ!Wh~qYB`;&eu-GuXi=@|4w1<~ ^L#d:L|a0> 5s16}(|(_c.@ .>ҳZuuVl֯ զoԧ~77 ;c} |?֝v0nLH\{RGӷ4pYGڹg[{'s1LdʸʓMOq VsͭتyUḙVƝ͏ިX9Mur<ѭrg}KWa L Pg%ħxK{oj;nzvkﭿ:jsƟ6N`sw/2 ձ)dDӚ6[?i#5=곬 GFv'~'  q]cU;@ւ篵T|Kf Z_i,YԹ?4}K6ӌ?گ>ֺ)/bz7$.ɠ9[7}zCwO//{='if}Z?Wxگږxo'3PrS?[j&-cROSuni_w~ڞ׌>]sh=yY}됛GV kk)1^ /k~u{oܔ{gǟ#G̳yČ9;CX~yg\5_t Gȼ|V\5|i'N}^nMzYq~||ӏ{tX!?Y1^|Aߴ_X_YiɋOw٭-c׎e}NX/ج0^qƺ 'NAU4FW:詧O9@Mvv͇il>V[p77ݿj `[ p0Wұ-h謍s-?utyysɛϺmYZ^\auC R)Sܼ?294jN@p yyFpd<8do}[k 5ǬeeAN3>z1n`TO18bݷ1ak-_yYhk躛뺵[o_}ywchwVh沖]76= G}ڠf@gAO>˯,)]U7=g<'> |o}rUzV13>j?㪓70yR/zt@[e2ܐ1) 3PN1Z[x-n]zS=5=:o6P?u?k6@NsӤ7yqƼΘ7Kݸgn~[O0olB?sҵ5w65&d1=}r6`┍6Vkx h|%F ٳ35aN<> x9}+\ޖ?}{h뜍/&- >+7m?tVV[se=}r~u5QҜ|\Mո[-hu}9gn^[ `SsXl/g e㒍qm4QNڥƥw^VfS>s}IۙCԽppG_ַ[sz>rLy|G~gv6m.]=#=#x/7?裗\(s@}4*/0Ax.)ԍצo_؉9omMrM!=秺\+/l2'$bad$>#G |=?}3/zclc+*{aMHK=LF}5Xnuܭc_h޸jꩩGӓu_#ܞ sFjݓTgFg_~V<^}knk:qțChn"g.M;\96f|CFǬo gel|&u}y=Pµe}c1sy~~b~'{.<ͳ<;3uco=xDLh>rNls\c斁u џcs{{/~m)AO }z3>w0cm8Ņq:c_qkۼpЇ3-.݋ӽ SΗ*jg^2ʞT_MÜ2);S֪m}1xi`SOUnzxEN?u5]fM{̺Neqͥ7oܗ2RO6)Wcڹں.Z]溱z鞄;oR*g\>nލe1Efk<X=Y,`W(Ճ?.u>˜"k_Ft_R˹/뭴E) ch;> mq`m>ojv͙5X786[ϚroWf3[^g wl53כ~C:t|+܅o sY}n_i9MCn݌__پ̡nklΫu&\L^c[l{O?1W13 9.جQs]dcߧ-c׿~#n|@bdGwGt |vכzRɛ'jY5sWަ/xAy`ZP6alZ~|'h mLlh4׉kuk/X|y`"d^{}(Y/+{v|\ y G6sf,8go~X=Sw3/ݠm5"KakNQYl yimrW5>* ڙ<V`~`<~|qGsH<>r=B,J; g7+\{?ߗk3S/|3 [2<7u!֊jovGam%2Ne|XԸӬߚ5 v!_'_t]:{^}>}w~Nc>x,Ni:LkEggluܘylmfgi g| ~ήnq5PuG?w,$WD`R~`|G3.:9'"}sOFϫw/ Dw?BSm.mk9˜|+/߶1\\qm2N]"ZnXu|'.[nxt?mC`ͽS`L|' t;}߸kA ߁p?ݓ=>6_wKz svǿ>8>Tk{?o.DQlrDh׳HccT[{akZ=]77~MUMo]:>?O~ƘhrtDi&Z'ʨOʋۙݏ:YR{yַkqevw}yk3לoj_7Ƶ@+#p9@n's>d}ޓ֮3mAy2`>̫y*/Ou le[?xlcЉ yc@_L];15Ժ{f|M{>eLcc7m>=7r)gM|o1'1ߙVgI#e*Grw2.o0| 73sli;HGt<`CЋˁ:Rm1 MG??>6̋g}!ցO[>kk[oa=qs9#enE\j3呶Ls|5.ڕV[=QnӗSgmú޶D]c1~w9cs~'4{| ^먭szvSs7\='jKs.̷yk񲾇VeⴹǚlL0+ڱF= x)g\cn5}'AyVp1msݞo/G#ϑmJ#?ɼI-`S~{& r;ҏ5Zw{j1{oз{ɬ+i_28Z?٦mpjyF[ܗ~_y<wovv}->Љ{Ѝ~g=i?Za<1ơƤ_w 7!_|YPkMUuGIŻ:Vgt:zy|c{LW7α77pol˶6@8uV> }7?ϊvϢ~ٹfjY=T]Ǹsluns}74`@[OhLR[^8%a hN}XZ)Ӻm.00김 ?ߑcN=ƪs`|g¹*_|'I{pO:G{M^b~iݧ׆k_8;yyjg'کژ;z=Г1|(OB;ݖ7/;}$'| ·\35y$8q.9y#]֨֗)|gٞm.FxbqX_z.z@}Ekr}uMw]l.bh<5`9Dp;m8i׫_^߭F|PψlPv1(.m߿2&Y_=k׏JͺȘ69{1]my^Vvc %5)#= \f`y[zL}2vƹ#'4Wmm78d0yF2}b'1|cP{31CKZ?nmߧ-^۵$z>/xz8~?π~>ɽ:BO7/Nz7<_;3Kc5_e@]Zq7b}͕T6W;=[ Pd2x259ucs?rc^{>2_$׋Û8&iO\+_7zҿ~'>q{kt[]7<18ws 0lڮTcL!s \sk ڽm{m~\2egW:KsXݚcо9 *ͧc0i}՞ȵbeoƥFeyNByB9r|E yϓ<<[}G-x>4zx^t|Ѡ]+݃?x/Ɠ' 7CcFr~1>64u8s9\5e`|1{S= (#U?p<{O_\d76t޼6.{l'?ɫ5^Qga#c[(="\P5g\oOmoG9_cBm|cL N{DW&0:I=&g؍:ϋq|cL.GoО^+_Wmoj=e}4`bL[i{{l,2/SOOquM\Mu^'W0o:pOm5T1=h }KݠzSMa~/; ?2gLkZO9%2m~c(<^۲Y{s}9kr{Ho.IUNUd>r/˧}O:k{0u.{ ǧ>jmӦ8⣹ 7f\dǔI8fc\WypO=Fg ,oExsIwVz*}Md>eϿ>D[?e3ݣS(󀛶)2cY;~}>?cZSvC9t2z[G=KRfs Ü|M_ͼ+vf󛶂w͜MF쳵-iGlv̛[^XHm'^__xg0W1 Þy\ڐ|?W}_ ‡#}ʣuuK-,Ճ`ջ3pW_>]N5vW lνZ;SFv߿gu>2ܿC#?)=ןv%cVܞʾ?y;[9'nmK_v8/30?0'z8܂J3GACkߕsC$$9L[A۠2w'<7пO>z ?tA  5BoA~/{ Oes?h|urQ><м699gd]>Ğ:;ik[h spZs$kM^6pӗ3'n:~[nv(TixFas^wݗ/_zfŇ7 3aZ/ɜt9x.xR u5"J@&j/<?mc<:>/~_x' T]3y9>ڙ1u@{m[`1zҞzO }w5ݭmnϽyX2v/8֯~y^5Y[켼vMϜ >3oyGی]lswrM}6apxfS7ziN}.:A&blql{e52ϲ޺ƚC\gl)k>>_oں9)3,㷴Lqgox7>H߳fxͅxAS8oȱG}1Z-7_\R y^vZ/6Ϝ>~Pr\}sӌVO?aR7oٳh뻡Jq??No: hfnAs6-&r3qp8Rs?oU&ҀZwu}/v虁=lV*c|#GܻMFYS^~5}Dt=߸gP&z~<1\%Kx9]`@3깘. Eki+ |Ccznӓ==s3SϿ|~I_oև?hgmekC{N ~\B_ ;?x~o. ںwG g,~ºd\:Ʀߺozni_}{b'/|v 6{ӱqMu-0ɛsnWPژS^mR{t/F01wQDu݋?d2w2Jq<81?mkiG7Ƨvsy \5E[?!ϱ.7s`Yzk߳))RilVo->ayb~<;^=SqFqs5[{&x?"x 4EՆGEPE}$!&ĐchpCP}\"7wNuLfSu:KU_xի^uk+^uqqq}^xk<xɷSVv+_ʭkϫ_u]|x&lM a3}׹ͽsg3k~~oد偙15qug}v>)s 77nk_ Ng9=?[]z_zvz;ޱա߿w~z+t-n zN|SO=u/yI|fku{|V 0f`gLwmm<[Yx6뭷n}W:|8_lwooV9r7M7e(#Ľ9hoL>[:ߤ(,n9}'ؖr,oaqx#ssWaIm''x|wP?tH;$opP/@)K_г|A;;tP>_6#Cڄ/O ܔWgb#ԡOcB>(+2˜7I)Q,qr-IZ>姿}&o=ɟIٟ}O==sԟ}N?:?,4\}oaP?vӼL0"?ܛQP/iL\ɵU)k0Ųc330"(BN4sl_}ȕ[=Fv!?nyD=G a^rw{{|CV3t#d}ro@dkƭ!p0~`{mc^~ӟ\c0fo#7)m/muc~O?=~Z=_=Z(W! nפ':ͷʂ/#z׻Ao1#ypAAy^) 1qw /H9s3F/ڥ| j~b^?c KAq OjK9p擤#?4Mz9G#VvvT1d ylch B wk+O]22 -`{7#rvF+*c{qZ־M9r[D22SA{0m{qBp7S_&5=5t/eC1=88=yB_Akgq<B_ |7)^)7x=EWRVGS蕲ЯM_7t.}C=/3v-Џ`(R>^v ܴ8*zu2g%ڏt6ɽ䝴ڮIrmҡ9uNIgZפI=vY7\\ҽY׿i=bwnO_}^O@;{;q4i>5$7ZaZ#'//o )8'hgv吏'>/Ll,!9}#Kod?}c}6}?d򔶨EPr<_qLg =8 [9@gh16y::λ:nwO6˦o}^WScX3v`ٽy,]1?x >+uL\<7t3 ?3:KڤJ٤cΩ l?|?A =yvɸ' 2'嚧$/K߾}>ӧ-y~[~ZNݴO(Lg{O)6*}}߸h!5ԷH'yxdHcΔ/qoaZ_Rr4%ln?erd 0F\A0W $FI/]El2W?@[G@^~C&BraKa! H1.i<_(V!gٴ ȸiCE=v;N>ɶWz?m||'/N>qg|/7[P|7'}Pxxv=‡>v'} Awt=:^[:k:``^q#W'f ;c5cI1ƦMTVM>ɗ'C{2 m(SOIIߐ~qJo\% ?ǽ^uw8yi?dUM׀MR;nw7){;wd!46+a>}+e?sd쑋b\F6#?)<#6l"o zhя~t(.myG2 |(236~|O| ׺i3te:^+CauԦ|Ve4Zõ+x_Wʂ_.mczP&H+ c t:zh:CBCϮ%O=g/g98el6xθ1=~Kʚ6=b_RL{p.ܓ{g2utfu:@l6fo&o1bͭi:ou~nI.׏~-' Y!G}06?{ҏrZLVP֜3ӭ'gs^H~سW넽}S_vh;>;)SͫP+rԾ]؆r4J?洉VG>cm5d4)ny}53=?s!VIE'IO|4m5}e<\>/I۶«)/%9ҷ92 y7sUw.(@/ 3 C>Sn_> fA1iK~VN2ܹMVO+uyQSWr+MGYmdOel)ɓOٟlʹ7[7|)*2s?I_fNюp2__|#S&Җt>{<@[!F\¤@{)i\;Gf_A^W>8 \ټeƅLWD&ҖȯK 6"ٟ'omO_r:bE~> dΒ ?W`zڜܤW-3{m`8aj^g/B ^{FA@/ AO}z?@% R^0Aе1%w>('(c%ɷi]ŕ>~w-uz۳?>l+gOZD,Ll-2IZ#q[w9Ug/韛?LHHDzeh3g;N/ݶOߗvIC߹mm?v-0 \?ks?+۟|vy=}|Ì)4P e`L\CI]I;n;f1Me9}Kc=vye~g̿{2f|W 7=z7t@Gt}GpA%0A+t_׻'<Ջmd] W~0P?m{(̗r ǜ3?ܡ}0*CL1Pur'y=WFݛ#2~˼pͿk, o":w?LM>ә i> /}e_5fYooLzVx=8㣎o'R׳}=ߋga cѷ2X Rgk ̘'rg6u=(mkEo +v=?}x\wO5_t/c>}:*mU:99i7#? Fzag)mcׄπ'LvϦsٳ*?`B7ԃg?tz%0AЫ|zͅQÚwrN䝴QmI]ڡvy¨IqOvxT+e|N?9sR9kz@PxJo c޽o_'W:}m̉0%>&O&dpoFe.m&vmS]?cn<;33s<ۋr>Ўz~9EŇʆ\wp&e4|*&m'm0Q|/rrf7w9y֔C{r99wM~ CiΟcQNILZ}+{m0 س{3긞n={xxg{A;`>tsuVW`om)g_(7y\ r@ )CD~SgCD?1೥<|Ǝ7lɶbΕ}WުN_G~cUfHҧ͕>y| =#s ~wi:pig C{e/CwtH3q$nQ{kIu| s }:J_ mC}l|))VѲ4tv+,N&twn8|^iS{i>}_K;aOtE_N4޶]S+=7}]x=:i sL{>}.gk# |&zx-3y6XyM`@v'׎0V(y.s@ҔR?Yϲ>~fE/Z9+w'sIc0!Ş4ovc{Vw+i8z{2Ay{xo`oտshzwhSxkdeO4 @9ڣ2vCɜS3e<׆ewc1ܟ0{i\.߷ 1to}ԡ-џʦ^{mWm]&:yM~nS ^<} WavI_sә^UtTiK|V,_We1ީ Mv|iOZ}ږ7d =?E?i\EϮvҘHccM6|GUOybK~Č#:(r*?recmCZjKSvy*;kgfY-w}N[iNq>Y׾vߩ^dNj3ϋs_,>q1!q:!}?{_g5i6f'2_RtwGSs1ʖ#v=Oʛl윶Ӕ![*csu'mC+C>w̋qq"HCq9 }о~Xw[vkIG|+ݘ,ݛ1$LS5NHSK~3 ;_L{ٿ/˳.i{R:6GΡw_HgK7g<\W0)~)I]?G&QUW,~R-?" {Vwde;<6i,i3UQ E].a^Զ2nz\9HCH?ږc|9[H} :ȧ1s~^zc1?ó )[\SK~tyx、 <-H\_A#>pO5ᙖ?RO=GvcTchϽ05w "O<4a.s2Xȼv3!$%[FWg&h)m:&ut_^>uG"l_W^b'O|~; =' XH#22Ϯ|1o){gC-iVhWH\}?1[t?{6Hy䗶:QlpO5eL972rNJS`PBK\򰐃yʕMAC>=nEnm(d0ynJ缛#|ɳ:}PeVpN?2@{3Od[ȧpמ5*a)gz|^yꮆ}EAOa:Э93@ߔ?C_,|x ?R 'O}lȁ =/|]0?V~+?g.QhV7g̦πIO;ϜbΩwz[oSL_{?/g~GI;V&˵X>f!a{/\_YϬ3ؗ\QkoK\/?Ơcu(G(C[qAyvs9?ʼ,^>s 3W3ژ#ڦ XgP|E93Z\d|7f;ɓl1{>#m=uSy{k$ʀoB'C=|y Π7.zn_AB;_ _8! {廵=2 v-|+׼s5j.>E!Rt@w|W_-ŕqIr_rϮ{xf ~}>{>p |?2r8.E5mbc AD.6gTOD61ޣs!ޯʸ8{  cLT6rE$.EЧ9Or-0W=+? wY.uJמlu^9U ˵@ʧt>yRsA/ cnt}Agt]B) BO9s{x|LJw+07L 3$f^Af>A%pj}g3vgv|Fg(r=\<Π=Δi>m Ð\16(ƈlBv%"iϧ㜶lue-~%Ii?cUhGB}a@'so̻vygǗ>(kN0p{M|))O˘o8Գ"ol"d/\?!C;_;s|;[=3߾BCM[z. 0W4ڽ|Qz|4q{qWރ/ ^q_\(x'w =@ԅNjCO}  B=tmw@dl O\WW̅} y={}˳A({Ğ%09s>=d>W"o Tv^xځ R']Fo7;I%I^b>w߅cO!:.aG|Cǯo>YWw;B\9N"A m ő-#c_dž9!¯.\p|)'Ί]?^|ׯ|q8".0K]恶Ev+qOLIʊޥ䫼V}?z\~t3iа$gvOCY~W 7x]'zn'7?wC+mpgΡw`/3>0?X|jӷqOiyy `3^mZ{vWu-[~ѷhs}gu9״nsWo$ݦwKz9zxG>w%F0,2!!7aʛVs4WqLSgI}6{;wM gqRpo!mS3=#|! F[)=Q"#}L}߽.d*riSV  ._`\ 27#癯5}ܽ|;G[Q,iMX{Ա$=dzzʹ(c\L×{Ag{vӧ 2}?l?G3pZ:(T뼶{wIOzx'=<|Q[gtL} z>z^&L~A^)&OպJw>yy'Ϡr˳-Yxgs^>ԣm+x 9Cd\!.C.rK\W ,T>>c>i2q\reM_[|'2ӳcįss3I׼q}Zj w0S̄u[ g: C' -]A_ݹz BrӶycl7{̞g~s=q<,T3IEv'̽5qYG {R_ljOv~`-mm;w^*~\td`ҿŗ| Oߟ Ssh?@|~Y#r\fߔ_ʌ%ISs#s;}֡)u;p@[g!偹YD? ]#_y!rO"`."=v;= O2 YE^3~P9}t6v犛 6v7[}WJөj夅hc jou\8joZ$/ruF_̿/'ӽ%|w}Qt}@'{>t=Qr С?>/9w' ^?GnzV2L̻)R{fNev UmokWI>OqwN?iudoSߴ?⹮V~}&{{k0N ^}?HstA]ijo+ :l:=s=Ks>*0Nykkgx+_=-\ח(>/6LX[NzN:ړӽ޳;ͤ/C7\w}6ϱLtnϹxuߴ8g}1rr=Ѷa;%׵sorgKBCsA=s ep !6FCjsϳ~YjN13;d}N9VsԍԽ+VZ\?~k \@@84~.upOk ƲngBICi:L`oi]9w㷴%PϘ3N?q{i˟\푶EC km~Nܯ餟'}~`=Guzgg/t]wuڙ`;\xV^1zWs:slklcEs+޴viS]r5y\٣wI3Gzz^UG% NNmM0dl[Z8WZӆN:r! \ߌ\yLM!kټ5|HɓsPo|{$So+=0]/GtWͫ?{~\xuEՆ_PJK5B1&Ę% ]Q|vܮg?/ɷsSj~'.⊓2~껿{~3t麫zW^yy׶r|wY>6e,es>>ӛ޴Kϸχo~=ꫯn(=~s2m㪫}hq/q$8+:m~7rM044:mK s;iS:nEr! \_E.Io?+DZs,^9`=%SoOr~z#z7uD7}kOs i]syW<0m}!'g~ǝ+@%\eOu1=u\/Lʼ+ӿ</Ekµ^{I[x[޲}Xַu /\x饗v?eG> 7ܰC=mo~sgs׿Gsڦ]% 6߮_&'s#㥕ko 2_Vk=n'i>[O|~g>+.rϡ!臶3vG۴]=ek~K\Qq{?slD$S~˰c-RM=y:Cnz=/GLKZhgկZsƙzk\yi~nڔFS?ٗWGeyux|*hopQGERos\BQ6xDFiC#אW"ՍJ=d<:eCM[ dC&m_wuۇ[[4BR~hې+?  ^|?>+.>>t=QΠ7莲!HuG=SzgL?0?|]70̵52NGc0xӟ~7~Oy5 P<溁zaVvPB'k׭i_N{k5o][p}_>;=76/OK|ϫ^&+*׹LtO{x'N=K]qo9QPO"e|#7E3!C|27{Mr򗿼?aKwV{d~m>nQyM|m<̅կ3`M`Dr/c0| ZI3W|ydm63N1|\S’e&<\{#Mnw߽O?h}{߆#`7#3Sցv}ߙ>O@&ݿ%_˺o's`M\ޗ'Sk2ao ||W 7?t=@t@7}@OAoB%}C+tKXs_//7??9a0gs`Nq:eCap|'; 7_2(ôa\?( eh;婶PT-ۧ5i_кF[08[bSx{+]<=/~l9Ls9yOz%y%ޥi]v]Jȵ5׽2^i;kgԅ=G_?dG<}B]?u#_iSs||Mdr>Mlcȟ- M2$i @!)}.#S命ý'[ߟ'7}?oJX~]vemfNo9ʷ3޼潉Ï\{ew`gG1S,3&1[xo O<7~;}?Skfoy92W(C W2[a~Jڇ^K5eƧ:V1\oo[O;[n;6VGN{ Kf0nGv cKo~(C Mi{wn2&l3+D"g{glo{cdէ? ۼS>h\==6R!_sx\RoF?@t^ tůoT_翟[ r'@ŕ^ON<]P:^ ]A_܇޸?9mAЩ>Qvk`Ρw> >_`c 1>QބO[7|N;x'hTmP'so`{4SKHjFV=?}ꁤ&}k=]q>Y)&>^= iL%lkO3'9;+?_:5uoڂ捷%;3vw/r quO<=@=mWd2 ٢mOKۅr éRNBOO}|#x*P >KL3rGwyC27^d/8Ї> e5m!=As{OxJg%?'fGWWz [[YXhMw'xϡu}w\# =&0ɸ} 3nW\2.2NWҫ߸3sn|!{\@y|M{G9|/&d *ㄏ/()g|8%Ido@a1/Z0K9KE>߿d;%,2 r 9̢opH l%ߔ1NjN|ީ?SNes5Dҹ{y_FT>b?=Wzy浀_ ;A B}:+ :ޠ;M#t }3n_zvo t>$ މGr)K/mϑ{ 9<09G Jɸߙ;"g<&}S-O7hiBϤ<;:cSA t$>)Z?:i__Rd}uAIKAp376:=3n B/ AOAoz.Ozn_ڧ9ƹ{|al 8g71{iKrqӦ{ %̱Fi$2wPS#!%p-gOU~a灵ݹSJ"O{!s{^Ewf>^ʃL\tS'}O}WX<϶W^L9|! )Cqr9IR/0 |^spL9My^kd"ҿO۔p:90FMskiO9eڜC},z0t޿:ߧ{^?#0ͻ\ Lc .څ^m/ z?%-K9t>h9px#?3w\V+ 5M?s^B?r_APOIzy꺯L?sSY@)ЯyR紾t?lm y8G7}S)?äuH'}M8}ճNJݓ^'VR??{=zSS2|3 b'=7rNlx6ϙ+!+s"E=藲 e<dz&qJ㏞7lND(g^yG{@zm=דuΣ'ʆo Ns6z3x?ϵ4f/c\5ƘgztAwt=+鹂ƹcx}g/9|xyN:'7ևH` x__{ [seKϕ{\{&2fi)3<[%J[3Hzo?"{GΔs0Uw_ &\t^^z^wgNI^U1g2kxų+qK~{NW _%}]>G6Plڠ*?Wd1G̷F>R+m>1Nid\.=>+l Bܣ=ps\Q mxlQ';0pxyNmc]{+97|?—5cRSk p#9``ӯ`̱{{R/qɳ0́2O6ZƀsVʳVɌ/Y?}+}{܃~[~g}@:z?GE`EK^~2W~zxM/'yVs]^~{v#8]y>Gy!:~hng=Ԕ !ӎE9\Amx|Gu׷Ѿ=G93,n%0N`,Զ>j귌w[פϫ+keoŋe Z|/r GIy s1)KЉkp6\=iѸ9-iO=e N.:5:mz|]_>4Gw(Gm0f?f^0假6dzOgSk>9gV9ݲt~*G6`{ mՙp[3][fmqy衇Gg+C=L{o\Lg<<{k?o+UkwK}R^zO_#a|?k?d<03=K'ĜSs=zqC${f+]߉7$Qr6=8<9d+< NSڒͭ+/pe>N=oν%%0ʘs}y>cLiכw~A۸ px.e7}"'hý{\T Sw_2ɿTHY_0S6ܿ0_{V= i}=)y0c3Wx狃70VYpҮWӗsevpa Ѷ9vd jN"6W^Я^mPЏqUmҌ{O{}{M1u~^ٮ7m:ho^<j=3%ӁO*z=%@/AGRåSfҼuy@晪޺|Wk>x-3g`h]9H%yH'培z)KS9B)k2Uߤ_ŀ]7ڃcdo7xsyOȳ{8oJ߂z-_!`Zv7㖾ۙwyΗ:\m}W=KOf[0G<ɳMr)sŭ=r+~SgRQ,~;'3oEWo-1&)l UUY)+i2|7 ^/ i:0v;x@?=QAiL1qXyn7.>˜b&,|/xciXz;.L\_g+ '?7雴{3<>s`Og~{Ko_SW;xc"=25~1G/p]g3g쾌YٮmsZ\P=obS3S"g9 9;|l>:FN;K к.}u'ݙDk/_r{ p}7} ~3_óx @GWnfv<iqKDY!勲T?`RyҊ9??;_׾_zUwu[뻕O|ryHwzbL8.)ΖN|26;7ۮ% `3@?%{rk\[oRggNs2}OyOj#ipXyaBr~ mp gaU;s=<碼<jwl[?i;ǴUǥn|tJoދ#XD73umL:OLy)~vlN PwPFt=B- Ks炩ÚwrNrrG6vʨ)'5>7O?1w9sģ/?׎Ggڟ>ƹnuMWʻ(OSO[ZO^O[wwֿWKX;3OH|cL{RstPwu|w9{ i%lAs|ʏ7{|hziӑSͩsO|vtmW۾_)7R?s0;uYB.h:^:+?A~'tJy薶wi t.udeyci%ߪC8.ʃ-n4b/Y/m}K}ݲmgds7ߙsOרrS$s;?k+?htN7'ig>}}Z#/u}kx l"]So.G:23}G&9GV"9ywl_rј}{o Fַ6WBcyV./辱]q9X:nfl zZu?ϿG`[Lvٖ/+_~ѧϼ2s;5gğ@ЅLK:?t}AgtA#t }BB1L{> }gLr];O2z\gz1} )wJ?bȾ3F,S>?+}:p/y 󓺦L6yl){G7G$+@}C/)c_HR+u "=^Ug[M܋k\Wڷ]3k 5ؘ{LJޱŨOyb]d0Qm!_x+ F~gR9fn\}̭gjsJƨg| >h֯;sӖ6Jv{o'iy4mOmb/x<^:+ :ޠ;:r)=ؠgsi1sX=38̅q>Sg@ꞟ ˔)+'*+s26QO]y@yyz>)?&:7㿓mͤ#nS{Mp馁?G6=_X4Me΢t3&c؎x'jzKNs~e{~ޣ7vW-l5d.="Nܙjx&I?\;(Ag zH~~m|eɖ^ay[$mO:aؿrϛQ[u~eͿKDm7Mw>s"zMk+a46lO?g&jO273i/z˔@;&,,|g/`?/ϑdvߋFֺ<~`no..g"35Rޘ~k}`7nd341OΝtxVFO'\/Q>cwj 'Ey{z\ @7Џ+˳;ڇ=7 Fs}}וw=D<<{q;.eߦ|iYRdNt%Vk<-esǁ s йZ[oǺOk6i?O\$o=9'Ӛ($^;bI:k^cL92N|fhg6mNzmoHM7,/S<]ygӶ?E__ʮƽ/|P|>7_}8gBWmBorqt B1 ]C9#x|o4~{/w7}_ȳ!uS'y>´ ۿi?{ԝ M9 yQ-TKS[}׭}Go.}mu°Ft.z^:lh߷OF};N9inU6rkڞN~5vQy"W=3r9OMS|g6L=(7駟Af3[8,}"gBڢ.~T e)X3uC OޗrY8 {9oؘ¥^sSN:w6/:ڱHY'͛>m;m<:u(x]O{1qϠ+Ϡ?z.ir+ek:.A@[/<g[ɗ)ZvS~ӿY5x?Ô9fg:o9pW~5j뜤}ϏN83ޤiV5ƝW-zWShr|&d>rq!}?r_^.zf ; G}3|_"^ꓧ_2c`|'$2}|w֍+]ϳ6_G ΄!uΛ{;>x]xrفoi>zq.>teY)C=+)JYW/:|πX'څ7H_#9}7MoL}˧M;劵2I|{;i#'mԖ˩;Og%=i]z&Jwn?ޕ'G}O?Pot{&V1Odܛ޶zl%^[e7׼yO{lhRY2](2}UF9`0vJ9`>>Po@]&2g>@:#1PG߮<6p0~``"yk1 O욊o }Ǟ2t*5[3hx n~a^m.I7'xCЃe5fӟvLyO >M?;t 8 >_.7/'|9\ƻܣ?盶]I6MrL\Ouwr}hT;'[SϜ'9) ҽYz1ZS5/t=S:Oh~vyr4ג9so Ց_3ʕC Җ 5Hr(=?Xh _&'"J99>Cnۇ<Ƥ=nfm.}!Þ (1G`)`2כ< mN׶<:~{ ѱi?}'?Wf^?V <2’,x=W~3:t߃'Mb6]B+/\Bh>//``>;>Mo|6׻I=] r] d0FL_xmhTOMqi|nJo;3)=s^s_ٹOKǒ?}Aj|xtKp9眯?)("Wt9w=,ꭞ909{wWWUW}{syד'?ɗS򔻧>wO{.??x3~>myg:c׿uo?ܣM/㸅I3W>ߏxC[Ǜ8௟9??Y|zٞW _poYzf_rӟte/{K_w_z_?o~K[}+^q׾%/y?w?/|3yW ox݋^s?~\Ͽ5|_߽/̓\`^s9? Coy[.wsi\?_'sxyv yA^=w>q0OޛѮ}ҏ4a\{Y? |7'x;xA<]/t_Ў ~5)#7| <<_wz`]0ab}hج7gyud7s`Y|Vҧk9܀2W/O]-MKc>1SϔNz@zrw^}({~:Hy9u\?;I]ӽջ'+{8w66\N_\$k~9l/Wㄧpm1'5:7."^\>|/Gn {.م,b,!Kӟ^ ;C*~͏~?/?se:e..p6sR!GF"ǀv~_ݽu~{[zA0򕯼̉g+ctsx__0ӎgL>̟rV~<}"rx꽩n}FLަg:faX'wgX:d r نAn"xh‡q~eߴ?|sAF} |)|/`VO-vߙ,EU;· yӛt+y胋#/}<ޗwS[P/.:)6X9u|>rug/_QLm|< xQ~>s=@'}u,:Coߘ | p3&(j;|0ƺ^an'/c]̇v[OxXw-sfnܐgq=N&ߔ':lncVlz{=6{1>p5? '`]OŸȦ7/XIMl6B|7ϱ6Y[a>1}pŚfm"cymz3sBߴEn; SdJ mvQ?܇g y$Oߙy >s'}_>۞; iklu/%x<>?^:A7v ]/tН?#f,rL;~ρg]Bw1m]ipXɺuݱظgW m*_#m[D?uW))z[}mMoy+lznCǾwg{T|PVXeo-3'||6_cwҾs;b7噮 փm׈rY58&뙿O3rf d rƳ>&ȇWVc^·/Iok WO];wSIϼ'[w͹ʁ}CK۶ԳN~0''Hm7ҿl YOiOf86fmTcq){yu,aS!k+ ֶi7`3Eh̢r_e b }16&üŝqV=e$͝o|/E{1bsO=⼌?8i\w#EV^_c/ߙC}|gyIS; .ucmZwxg\0TckN0G5~G_GN\}UL[k(zo|+V(OyصΡ n7G>qN9,iW?Q6{햍uݳ6Fi֧~r3wKںu|`֧:yeTf>瀇~ݿalwu;m/yf +9V&Y㥌Y7jڨoۗBk_2]zׅw/8 8GG0Fb _s~1А~JsUoV@ʁ_xXO>}Zx'9}>a1 Q_ ;@ZXyK]>_.# q~C>QkCYCYGL^5VֱmY8жvnskK j#vʶu[k#_[3_y?#urSoy=G?`7ٲziT"FmSgҴ-SO=AvO6>/} י1 Ϩ<,r}#1t?2Ce{Ѻo~9 efUնQ^/Yzjo7q2q[pB{ġH2L|?t#́p)yӷS*7?-փchڏ^/h+Z{y3ॶS1Ѓ1Cv/HGڐo‡U]|=y޵` |vkx{g3gۺ3?~#\v䏾krvVpuk @tMO=:us8Mnwέp|c38XgnܦM5k7؝6V6Ul-84FGY`Z`~:)ckz a{A hk^eU]uo3—$uXC 7͇̅?G{궔c.x>~_]7?X`,k󷶘v3|rό7XϥMq?e{<-o-G>uIoLxMm:ΛglbQ'VݭH5xcgt>#So[k-.`T/(ͳ\k|XdkUl U\OϳUd׶@מ7ݯG=!dKX,28w䄵W?c뜀 ?BgBd2\ +k̖9xC~;_s7q\37 Ž!c/q2[~j!0Xry:~uiNݸ*8ѷivc ۷ֺO ~#s=/|0m;K ͻ_A?3r߈(OҷZogBO; ~LA~oGlk'ZZܰns7=7k5n : S4vG8 flo@g}Rl*O;qڠӢ-8kX_go7ؼiW[|;o:tBy5{ݫ9*3T`7 0n|?{iu=gD61_N5Wln:sƳ.@hblgU5+6^{r}|k^;fah1!PsH77*xb\fJڳgiC}@G ]rzUm1&W|G}^oaޞi>~dgM7wqAkSP^UcІ3Yy44ʴcq2N{_uS}5tIWwo՗ԧlco9}|su6[cΩ!Lrydի|5/={*jϮ1gz$G]s˕V}y^7v$kw+YҦ9gs=r_>&ת*~!FN!kOC=9DCރs9 }q__j=e_HᑟƍyOep7|خ:s;֥:asEs=cZĵeD[ך1G,cz@CЋ:ry7~+ch'?|\#?j?§ܣ?>s{y7ݷNF|VjJ~2xHY}37hX_y<ف%g6yϫ'3uI''&^vo<>O?s53q+qҭng,ƦMvo@ezˋoKҟn @ר:57mۺ/Ƥ ~s_x9Qs1kVX\ͺK[EYhN򶾎g\sε4Mu}mN=OM{{*.z3] :A?COƀЙ`ƆK|O8{܃CƦ= ÿڌ}NM2Q?ܵX_1ޡ7nhӰ˦Gtɵ=[bɋUcY+S{x:{69ǜ6PAEBꐑǹL&>j ي>1WdY {}Dm2O1>0.]́s^h`NQܗ-OCOx(ũvWIP/q_]ofsmu68We!W/rlYR7}hgu_kC. #4_doio5-j}86@u_c3+\1hҼdcʡ|њfQ}gR,e2vPM@Ʀ7v6g?SOpM_q[~lӾ?d#c<\mǕ;wo{[9`;tq|uQ}WGs#lܾgs͏)Co%0W#Rޛبhe';^𻈭2 R~Rf~5짞 3&:Xۍ6fLͷkӇqT2a#WDH']rV=jeScﭷ})V5>lQ6Z?s256\kaw?nc;gzj/ODw^O:{^})O(-;;۬ iwNMgoAUg٧=wcknkiO{y5#jkm֭}ɞ){8sOQ97c LX1,7qɘ$];a'F[붹?}yqaֽ|gAx*^]ɼ=s47g2jx2k1ztIy@j#å *>զq!^kו:яAG3:sR,kq;ϩ. ~oO?75 kKpߝ ]JOk[!faeЌ%vߟ̹\)o] Kܦ<>]y'_6UZzҝ'>?8ݾOOlWo{6O6εFoҰ,]GkQG{U]g}GFv>;cf5k \as]:t)w^ݼm=eνVYnn˽{=P=h}pq.=9QM8sOg3`.F@V{,89,ݧsQOZ[Ѻ|8Y/-oNwcb.q'o:Y~׮S q|zw kk+ >|.׳{#?S~x8[Y=ZX;^ўo>Ek楷2|Bir͜=2A^ص#ak͵7]BS7S!Ɛ]3mN:t[t[>cf Io> Ӿ&槵//jt1E=ڳ[<׬sY/U&4\(+6Ȼ(E-^?@ sHЦg:TJ>_uQXD_[rLRN>TygsSOtǦᦷS Un67/|v}tl_ڤ=6Z4t"ml6icRImg.s];-՘Gq8ihm§,l^Z־SVboƊvm ox.ez c#{?Ĉx.lIS͸}qC 3﹪vFrټmkQ{vǎHLc&osގgg>κy`q=y6OO|П>5D;c>6u@1&c}'k̚йou/}^ίL ֙8cYsXks+7?ziXgW{MOӧl/~w=~eFLfm⮟vcoJҰy֊nP, ialg cs_AƷ:]~^l]9c|aۑΌ(>կZs PϩoMEhg̀Wt@τc<`6.sz"OtؿiPnC2j<ݸOcDZmg7oΗ\wv\wM9[+Z,(>={7A?5ْ31|Џm;ec|VB<ϜWm_gգƾf Pc"fYkߔ5뚌3W4޲,ml߿w~&njF<[9=><3E3=r1m6MϹ6>=$;vo1P{5f=sˌDFDsMlgcXyZ> 3gF#gy=,#a.;wUVK3 {7Ƈ6n5=g^i{|LӮÃW=J=J&6f|_?W7+S7:KlϩKG皞5u'ߏ0f `+`3-pvVtk׸֦c/u1̚ʏiμ]Kg<|Ziaog|}A2 L{?ӕiukWXR+-_=&~yrw sGG&;X]'?i"~g{EXۮ=ltvZkOe\3O퓫LyK[u1_\=~?g5r[ }}y๽_'_'=m@1dVf4FhX9s{6Wx&GLu\ϛrЩYǚ2_nbƍkl'N׺c՟|O P=x[0m,7OO܆R*&S ;8isRM5})ڤoQ0mquU5ch]qDk}7}ry{dr *׬gG2/LR~ *7<-8 =xUd-4.Qq1<_=EIK:wϲ5a\gw方M{ 6;sSr#m1UM}0vKe'|[iWOoxl`n^˴Y=Q|n>osӖ Py-P=]qwڙLϾf_'=4LЮ^37yϵ~"[ulg򧾫>siK4zʸVw8FexQfbLYkrdl5foz{ 0s|EM56}_?kݺ~3qN@iYbS.l|t]kGe@3)kskN?uo?rd f w3k_PR;Gy[o90͙kPkx~so-YS"C?Xϙ'f>1gȼz["/?3[Sռάݞ~~.Nu{} N?]Zgg@@/ͥe=< ' ?2.qi‡[F2!.W6y <1kN)zF|SгwjSn)VWUn5Zؼ|cW9\ fc9kSWNқ^zrd}.[ ԑO1k?[ gGm6jצ֞"o/f qkua8[滾=eYꇸpcN;uLPp =νbCE.'i|䈁0p}[0pn?uOt=('{MZXf~.2GPY' |$6 >ܿ;gbͿkoQ}3Roӝk{~qhNiF4c>uGm @eݖo\u/3FՋ4]:ruZa\=xm{icwWm@x&X]?9qnn\ϫ7=ۇyg 99;s'}WnG4o+~w Hwd81O3.}>k,cqH4F;+W3n/踥~0|q_?7F_[Ӽ]|ݒ=f:`<5<{27c5Ҥq[Gɺ ςyڻXk7 .垽7IOI=}ֆm+nq:nq>m efm-_{C}Qq 7c3MN? n6-s ){f_f4m\icSʰ~J͆908\?mqZR{yAwmO= 㦌i}Z}z `!ɛ- _Z?cC}9ZG=OчE?شtqsyF3{x6wh<ߦUغ(L?z1I+hNy}0vߤv#>7W=+=>IXH{D6&'ԱsMsn Yڙc&/*<ѧuvK)*+k{7v?ͱW_lW_m鯶o7:u?{E 1.lO-UvV?cf[V_YS;vlC`U[v9dEl `Fg}EzE,s\}r`왂Kx-+V>z.v$Yk臺0d5n_O7\ե[w>_lY3sLWX}>8涙?/|H'V;t/Ɣi5Üۚrn 37a^\;Zy^Y%Ns[gw 8?w y>~cwgl>vcn.`ƊOzhUWo1WgToW{NOƅܔʝmKi_ͻiCL~狟~{r\rw\u]w}-mIw}w__|7?yn;s_s=~|^w뭷vfL}J/r?ڞ+xTgK>?\G.Sy&1M9wyi2\qɳ_~c>Oy_|qj>xCe RoOӮ_d-{W_>gl d\?'y̳i͕qEkdeky5zBS~<Lݶvy6e<)?g2y'qxQf1qx!f^2?ygY]HېOK9KҮ|9g4}H9\9rc3^k&m쵙;cԙ:F6gҶ^}S_e}+Khu%>c6ޝ :<1F F0fLX!הYkiNVn0Yϳns/?w+e7}nʢlQavL}Gm;Guz,<#e[V=?{ 2e虬׬}mڅމzKt:#:0#sGW y7"`=vO>ԗ<O>O?SyH)Ϧ w*?wM?SNډܦ,mL{3d6β^?%*[?INdgҟ}찌_1 N9|d^2?y6yK{RG3gS@4L`f;W̸}ԝD.#y.J?SFN}aG3|)'*}Ƣ k2mþMR/|\.Wz mH##g| i+g&=h XtňiLu'ذּNo=+hwyg\s>qv܄خ*ˎm;/v|D38]kFzq}!|g'+LyiS|l|Ŵ%mCvߘO1dk;wס dW.|HqvI?3S g<L3mk_<4g\zd ɥ1 ǾMzS8axe^s[\uqnF;nn\7~2}~> Ӷ_q5bvg)>]Rvn] cGOsf>Cg,ßx^[4#eSW|ش8:87\zچO0nQΤI f|vSNKچ7 |qД7;~]ϔOL$eۗfx}ٮp}˜Q6,ʹ7Ϲ*' G%C<'=딟2=!2!23i/Su iJ"˔9ۉgux6}|n~ θ#GS%7ɾ̑}?76}ҟizC' mƲG<8} 3l|#jzzmt oBL}1|\?ߎx|%x~sSC=5f+޿@ '>l_!`Tȳ'>!CGwn~fLӼIo?ݘqq9&F9(XCIm/픑=)vV8f<[p -WoCٍwA n-7ogvSi8M.S^e:eGiD\">{/k9r<.93ڐsA>mvSNߎϜo.1ڔ;FGl|`E$/>'qKc|ĔGL?s1Oۂ\-ʧO}{nIsN^kt(#&eKʸd|2Ny?;Hse2pWN9g5^̼y h)0nr~糴7spm!~:G6gu`?k\rh:qrY!ԝA)KMjk~^Ή7w|P_cyoqO6@Cs4G9 g\WN׸<Ī ͉2jlXETѵ.xX0ytp|4lsv Ϋ*َĿN{?N.LRO/駟|@p 6Xbϔ_ ȕr\Ǿ42qݭ[~sKްmOؤx|ϚQ[r]V_on1XaW9j9ŧ'?w|a~|ue_G/:cmjC(=AP_ۦ+l{:ܹȻI+X{C߱&&v8Lq z{ňO])eע+ɍ_GO,#ΟruA/™>Zy7.Sn^I3S.r2Gy6eb_o/9蠴 ?9(p:b.:N58mR<sO6gSq;3^;e ȯy6<ټ<)3y3"k5bN<\d[Vq3kl/~6񵔋 |i]M5 g&_y`G>w@nΩk/'=5;nm +txԆ#b+?ݶ^{mvcdM*>tlǛy2/τ|L9m:As> G 1^˦UudLqjSV{65ooA4^N*M𫯾z5167lkql8 Y3=Ue[&T[2bΕ|) Z.N`?l[W`P66w nNȻN~O;ό%?- ܭ)'#θ %2RnK'qʠpޯ{m/0.ژLOqq]Ofoz2׌o.2hGſM{?vҿ3sc?Bks)yȣ\<,MyD9cZq,9W P6؏> ?pޏٱY혝>]۰Yv'\̣ygnY{W^qGLSvMfۋuոb1փ{9ͭz?ԹR`DZ7ˀ1)ytyϹi6c/ݐ?>=J[7J[Dw`S76r`8L{:Rgvbw wWpw,;Aq˄#sv6@p_ƅ _qi?Nřw?/|6w_v9+,erwﱦ}٘mj_=ZV\ڿ]h뵳:{^?d/oszk1:go;џy~^nDE#?pÕ,p`y`_ '& ~#uuɧs m2'@aN6e =#_Ѿcڏ󢜯1.̝rK~MypEu/6~w,}\#A>s& ܉7HtRb}w Oܿ@هbJ['_q?'{ Øp/+ωwy/2]OS,pSo[s;ُY+="sɧ<ö'/sh~MnXMzg)sJo8( \fbо Ws+[={>ﱇQ|388y/'yz<,cCob k>ޠύi0egllʶe83qA}6 r2n;GL /C.>OiS?&,;4ٹxIuq ֶ֊uM M?ÄYD~M>*=n]޼.`c.<'^O +/__xᅫ4#7Si> =ܹiq^;ˎrG&GZbе9Num qcl0/؀>op3P ιisJ#ql_&e-  0~s~pvql|>?Y"++{e88s/RVn0yOv$'#G܃\c 26U/sKȕ1+a~v~[BqBlIosG}W5&Ny\|γ.7V4Ʃ&xN:oʩzܿ)ɦi\43L+`5Ύ6rG3꾵<ڏb,Lzlx|ɧ<~\֦ؖp;K>RrI`;}q%| }˞?^rVR 2y|J̚}O `ߧ`G;o9/׏MءyN畴/*369\y9ߞiyƓ|̔,dp~_ 8yד,g^Kn"usv~>爫mg>Fzb5:vy>t;SZb+}n\ `Ҋ?6.NؿI\q嫜9s=w^'f)~My6SNr;G7{@)7듛@O㦝xs[9""@|sq~ڔr˘8gvS'\u?e>a 性JgNCV&'.y!vl&[R.o0rblx/l`~g`Ϝ9f:ɟ&Jngqp򳉹UVOyP$N Y]ΕՎ77 cW7Gve`i§Em`?u Q~O,s82<ev3IP,Wy;6Xe, yRm{.k7XqL 2j}8o?[Ʋn~1qˍ}Ëxtʚom\駯lFs/ѿ/4Ǝad;OnIR{18}\mh>Lm7Ok~;}!.y}޳؏E?$謴qp$ͷbwVl⸶';b?l%%<{zД`}W&1Yw: L3Nc^7bDˎG18JV2<c|6Ymq>0bdcZ/ɯ͑#t!f];٬svxa̰l=I)׸Fc'G)0{x 1ǣ<̓LWoyWw L ?{i9 eoA|r|#<ԁ~Fm?lngRsgy/U4/ON&rc,~6䡓͹5n9purO4ٞsNY'l8(O]?m|D܄=y8O nܱUN¹$}޳ dnhX7'>b|y<9#Eώulo¥#}`|S ǎ_\(Z϶[57?ŰW6kQ07vO=ԕrv6@GNnW;9LL ymg`M{mK8GmnnIXQKN# i}Em3W9:W qǓcfc>#[{,W.ב!2c283{;e#Gl|ƹ}d:^  v\ec|m}u؏O2.(楚ep~V'}>uύ+\|ZO\ǹxOO>3v^h֫芮^湿8i{ .q&g$O w?3C c\8nm8_$Y#) 9bma+[}5mobwL{oݷ?{O_Ν2?+2N})NzO'feL~4+aǶm'ٲb]ٲi`&`QO޿q_vz37;mp,|~sOh.Hˌyp;Ǻ7–~!+b6P}+;?r>_ySjǬ|cJF;/}+8/<=O$#2yh?mmsN?ky _x5h<{~V2{skZ[f)|i85ձWu;o2Ū}|ոqu'uN -͙8kqch0 #@ZOat.1w:>%b{8u8ߞRvزϊ.)2}irȈ OGlǶ6yɹfo1Nxk=5jYr,=jGA`Yinlqr7xN3yN񷽏#K~<~6p^`&uAmWcN%M[LƉK~s WDzG|7n:9\O=}g'wXa9zg~tIΦB^9ږm`۾S <FSƶc ?tmf^;U8@yʎkCm~dx\)&8g ͳisawq×o͙c_xL̫` 8 vzbL'nnc<@}5g~%vsF9;K8tǁ,W`cI{cgHN 5D}ކelW˔ ^$W@w?-?״o=i8~둎6'NO{-&:qowt ޘ5aW??s;蘑s vCYikOop]֏slb>zmm7ۿ,ofƆ7?9ERt} f5w&s M3 8?3dR.c>ۯ?^5`=|XvnƵ;w6'fYc`6g"_ F"9y)Ic?rq=N@%ky_y$Ʀx==ڋ>~l^;:C¾1}֜ĤWm@Nzվ?:gVcȹ|eƬ]O6GLWp_V}]s#gZF'cyĎ vޞ`yc׬qn8m2'a6ۏ smF\~ck8~1A\r\ClǛ߇Or?m'i ]&8={̅ǖ᜼azs-CONs.hG;o}WYC AG۽w~v%O&pGo/k?vO`9oL\ŋؗ<pm΅:ͷ[Ovoj^~jڀ?gc;}Mޗ'#m<vWnS>5}N|HesfO'8iۧ;|Nև~z :LS`7n3X\} z#/ ;8D;v*p=!9vZs+2pyؒmpxQYS'^kmtQ0>uK;ʋj i8&o,;wv>2ȇmg_=~sv6Ō,;,W[fۇr}f<<:Ngk\긣}>̱dǃؾ =e]f{8&etv*g1.zO>+6m)"Í7'2Kٽ>6Ы%ZX7Esz\{QX͙ۖz{sru\k%c5|)l5nj@pUsNEǙZΩ꼿?)Csql׮i==Eܶ}1]ݶT4wg8j۬SmW;˚~^UD>׾2zҹw'j߱|ʹc}~l}N+8mQÎ:0 d|#3KlBsK_Ow˛j]ْnKے.?cg/^^'9'5yQ1|*3/-<ގMx?sϑj߿9i/I+}+vuj8wyGwč 67OXg> r<<'3ܵԹoD{KȎ?3a{{_}7'?2bw Gl7?vp{z zsp!cH@I*yۄ+zwVj}lS83,7+ݽFj ss ?W86 #|U&e c6nmy'ܴd*X/ZfYc+Wkv i;]x [}6VzcKP?8r\O~'>/:1~ /c5ߛ+^{Gy~?mk3mM;~kڛ9e^3{,k2N/f{i믿~_>Yۙ|w1Or;g=x3selϬ37||vڞ~1Nd^mi~q͸s{<ɾ~eo^7qeccW{>;v~f~'{=;ߛw_l>됱GNYf'M[ُbIq(|sak?;gNvܶxًn$>7K Cqxj,}(K',׿;~ǐqٿ\]l({dcOD8?h1Oklk~oYb]51 x3 O'%N^'{ƒ+sʚeM >7W_=|?eKLa{kڞ>Ϭ_i<% 1~w9b^y3wm,mkۜpa~ Wُj+>٣3s 륿p#_3ϸOgƓ딏0CN/mh`h 5 k8fиNK(Z4۬/>a9s/ lO{<'l뭷sb?ǻ-o5$$i q?#]iز8<ui檙'7Xw':$;E歉/[>1qm}7~ob7ϐǘS5-}˓0}%cnD;dkcOE2֚37LYemssݙc|/s%iهсᓋ7g.1M_b?kt5o:{yu35/b364}6O=&kM[ţƵĆ'R;b?cV+Oucϳcp7-ao gw>N)FiyCmS>wjxȸh41俌ELLLYCʩg3GpLL,J.23'r|,~y֥K1aĵb~K_YguN>*ݍU׮QK]z&w' xMu+c?><#CyF#i#%N5:B U@_O;&y'?\6溿kvrFň'3mZv!NVH[wpwym9Gܿa[?r^gӓ|mvմVIC2qdm٘Ĝ q@׭20^ Og}Lfl>3|68D`?xqDO WdIɱ֯P [l\`َc'gl3j5Ҷw/'76FNhonE^Ӯ[?לg?NQ#?7𞌴qɵ-9n߉e5iq,+jFݰ᪵u0o{*3 W<[8bkIS9g żXbfpiHRgq9cCk|cc=`o|`nxׇ{oy?ǔqrI[;j(g[ {"Vq]ZZZ=Țj k̹߰!uk༟;[ݳk7?0cxcSȳsLj.V6ߣNZkBg$g^,}sdgʂDk7`ԑ5sViCqo܇A5oiKA^[ 33bCGcC>s{7`3)팝ȸcukjv^Egm[l)6;wz!qg60'^5^ٍ|koMzlWz!~;N'Yhywv ֙|qmmgxLZRehgI \{ƑzJu:1s|礙/~\Gǎ̟{rXt~cakk$1G>Ifٴrp@}\p(99\c}Y6 8g?ݻq76>y&Kmc4ufzCs$Q _:5 `~cǂ|c[mhC7mq8Iy羴Sks8΋F9zoj-9`Xbaxsc>_ʺ<qʵrMngȳO?Ҟ2RlX9ܖ+k>x[ vOF!W$ QnU=1؟s'nϠn6Ma`|ޚ=D;cg㕧|i[Nk&ܿ;sƫ] _2^c>8+n]UXu۰9ʉCMndb-gns G4>t\cp~-k'#' 27퍾Fw]#_-n9 y`zMyUh}lYæ/]׳LјmԺbWޗmpNpԶn5Ay3=4?{ܰ~۸rkjlO>7Oi =-~Oq)lD̝Z-N-{l6qto؟6q>aa?mxM9䙄|FAjD휐5Oec?@ϘӇ|>5Vmvhnm"l,ذ?.2o?i [7\=r?~M yMxH `[׭^i!RH[]}헧g?ϭUlclhHfsH 3yȓZllqqq-uUל]{Z|qTqtϦWmcD u7^3emNtbv֑[ͦ7oG+uvƱg1[.Logõq۹y#ϚI*>T'Vq7Vp{lk}6^A-y7gB[#lOPP{曧3\W އe|>$!9lΕ9<؇9q=ͼ2k]8wkqɚT7퓾kn?ec' 8j<9 :4׉oc{sЎhAPK迶h \ƵѸ5_Iq~`Z/ܴX?Mt?ԇg=qk[li'ƭ6ݧ=ci߹}8cdSԒ7 7~a^?Fl7'x>+kY;gf'ch༖m|kݴVtm)A3җvmqύ_s]|?ma?XPwXmC2WW;g{ Bq;~Fs#`ݟ9}Ϸίl2X[۱c@TkF}U^ܿ]LV|_׊\6Ncڸi}?>-w dk-7vz=ךx:[j*+zSd -:@jY>H[;9Keb{%[Og^l.*wOj鳛ƺ[\۹Kw[`5M!W#fyLmnWK?mpϒۘ<#{ܴ㊵EE/-^6 xr@k[OOC0y? o@{O̯ö1E9y6i4kx>ߺ}t[\iX;8uhmϳ6aMg[NhgL޻ 4ݪoyVl=mQ!/-ÚOgqLǍ Skmn䊧ujuSqɶxxus9&Oeq7o|5Xͥ6]n55>:Ӧ4~IǵV  pΦo0ΐmʖ9vRY\w\3Ҟ'Fə'5-nmz9@*-Vẏ {ɣ~Eڇ׏y%<f*Nm{GnC~B}Z^26#; ״&_RV&!|9cϠ|kfG:.ڴscr[Nv{N3ny\{ N8i'8_-1g+:KyƳ 4__E?r6ߵ:m9Yƫo[tc:ZPlywNF]uMhx8-s{kԾc䉷Ӿ[3v[wh ^oj8pCly?iL\7b}ߺjQX[4;gYg79>9mvc9SȘe͒Z4i8{jg^81siϺԶTKx˶NS^`i,[7t%~Eеk7yZƟ6^85yS΍Z3.4oc[lmTL3nγn~#Eqŗ4MNcJܧ [gOnŶ6;6Bׂs[~F'yoӾ[viVܛSb~qڕ~iцGyI7nl_mx?s¶װŖ%viZKWyVu:\ߍ':ijn8G=g[ͿG+ K]2_ͭk탹)uxdՕv_4~?7-6 :n1@}ߧߎ\njo-&iKf\&}5u kTi3عCwXStŔ_:q˙3_wjZf7-:cʡ7g a(qs8w1f{o;:ٖ;淼gi0.8oDҺǟOuOcb lݜb }ӷAh. .;LxƜvo?uӤϘC7!?B?l>C&\<\SO_Mo`ߞϖӚ0/3vmX'߰JnxMkE8̼6_μpU_lb-WM?ٴ2Ocۿ1`CMoo|^lݶVyͿ?|a}{ϵuxo3bNuvo s`iroػ[yꙌ ɸ˳։Mh-=Wcw>(˦ p?ۻ}~ϘOlj:ӴM;vzbgc4ZܻMܟʖl{!u qaOt9'?\}繾⋯|=|\s߹<1؟qk?cϟ^9מ>s??zwތٙ7'{ny?}|ݬ3g?>|9s3^9ٗ|Y5zxgݟzܟןì]\wK="PټN^į\ԑċM?wNm{znxۧRe55z9{&M= fve/հ߬m-l 'rmgSsgp݈džéOO qԘPMkj_fMgoZ:&R;j\,~o3-*2uoPbvYQۧn_5g};mچ G G5gzM{=ظ:km7k}^ktN:ju|C7۹r9SrfYcY_酒ÆKo,o*3 yf+ԱkYGCxY~+.q03YԳSVON={ܿy/S2˘&4?qM,ܿyji_Z'sj-tfvp5e;g j|}rJijNIWǶx1Ο{eK}rqCn{}=@x;kZJ=?s`~eZΰ/- gnz@-3?ƛ5VI^>5?m-[9|pSټ={g5LnW~fPŖs_Wviiᵍ?y5qt|s%.͚^/i L|&h½_}>[?{֎^xɬx^3ם:zϳvezrzh}+aw!5I11˽}>a1$8J'5xrY_|ӟf(nLp{uop~ZeϚH|#72f`vl4'N 9<ͳ>Ouz 3sֆ|&+O+8bL3e&.-:{Δdxp\j:챛3:u6Mfe^bg^nnr;kyǷvbʵ3,ksGSǛh9Xf|g m6}:9nWiSemM7 sڞS$L/xǾ5m<5v&g5x,1 31[Ӽg˷sl,i\KXH[Nrl-m:r#Md}WW6q35or6]bcXacv{/C χ BX 浚ΰ]{͖3=>sV|m>nmxf~tsY5L''1Ag<0`r.ONH.:Ƒ{[||79#,370\8SbL'XŚ>fO2ilünΓC3uќ׉G|1璚^qW^ש3n| W#&כۼMʝ\MK2N=e Sԝl+X;Om?i'??Lonx`{c[5-Ɩn:;tI7>"m9\tC?N~5]b?kBn?׿T8G׽q6qd.\[{b.LX]n}$jdwWr2{q*KKP{y^'J<7?ɨR6`Z96}jM_S3q[mݶjV7Bb}'gٖٓƖNXN|5ooYee[%mc"l?_}28e[@='Owԥ\ʞliy:29 '[֐x\S'C?"ZpٲsfޘW9?n}J-S1n5ܯM}nrNZlg]@MgZMcX?|Nfnq܏kv䧬|F`ZXvƺ2gէ-n\{?n\I~d&c`g|_;c3yٜso^'3Mo5e-Z6>)hٔiMky=12| wRg>7. $r̙7L7>9>n;$|׿/nqK93vV 'M_ifYfMo6 ~fO&g?/{3O;[ސ0?ڝt@{kÌ쟙sˊ^LnzZjM~f7aq;vnӏl_XMtG 55M>coܟjgCM_gR6mx6xlX}j}8m~g7ʸ?̲{߈y4MnDj8@N~oYm{1̫y9's"qXk +일v/ޯa-#;>3}:"W:) ,y1?i|F{B)׬piޟKlcI|g0=6>8clj-yg_b`.ye71"~ܮ$%vK\fP'K_ž1L`ϐÚH̡eg<6/kMW1Oɞo>匟ƚ7 *s70$_l7n}4}Xݛho3GlBrUڷbo&_|/4r=So;O?qtnml-N>J|'q\CnGoɷ61c1g^8ÖIY;.ڠw{˰zgzC#NP+{Mnk5sO{ %PfMɻYC;pM58[\d`{(=O=s g۽˞t+z{;5`r]̇/Y^Z/ï ޘyqn:vfM+ލ8t7s{%dx1E~ιo9/h|=A06uCXk $ƜX˟[mڼ>-w˜(3oswYy9O46'<ߋez7z4eĴX2-=eu7~5<:1bj-#׻&ϝzS&ZV# U6L g>-=?Ͼol>1zz/v@7oyCWn@U _;,ϸ^y5쬢c.hjٙ?h0;}SQ煶}Y'_D :xa'>,=O|˺ɵƿ _۟o9 =lXmr̞r.yޙioo5k=d=eW33Lwē-1<Ը(Ǿeد'$.O7=s i3 t"rִhY3/擬rͬwBULdMp?ZVGmNJ|L 3hN[QwZtAfLɋ'7\ |>?ǿ鞓#r-w8q?~O"%Q#gxCK\czZq3s7?Dfֹg&7 G̣q6=0u˚/?yev1s'ɿlX[&l惾p[_c>aJ{9&5;8;kźv[/oX~scflW^y]arO6?'eN&VFeﴺaXMDTN읍w7Ҽy[j6Ϳ`Vg3@lgR7xcl_oz=7l}1ro/mayz3zso/kW~Y/sa!MZ%^2ikqz{atu1o?Ǻ\?3?9KM6μqj-Kksams?[bizS.G%~oZq&0g_\N˱9f_O:lVSvo̫?=߷^oͯXd͞:lg?7͜kE"Wn=KF7EO?>/` R~Oiy,*n鸛0MrbΰqÇSoci~*N6=\bW2)?߮ͅtLÃ~^7jwXmۺ6m}emvze]zj8=͖l9ꍇS@?3?55}_KmYϦwWyqN-#<+Mnvva7e~>3o&x]$+uՖZ% #"t6U3ӕ'Of<{f?{u5q]vs_oט:o|ߞyy`?x{~<7_5LYׂkĘ&?syx_u|Go/^i:x&'7/s?=q}sx9;:smrN>~T3!>TW0oy]WnW0W+ӫ\sߩEU(bB Ƈԓ 3b suȸXA{m/rΆXG\1b/3g+>O>߇n0o3w)ڰc\>T-%Wyvն|L09}:_g>H\㸅}bH{4jWq.+Vxgk+LR۟N#o1O܏u:1SCnjUxVƽx[=8Z=J%kS977z{ dȿ[駟J? iOs>l/Ti\m7=\ˬ3k̸0Y9xO0rIsUYod oY:QሺZS*c p KWiU1(|pO>S~?j0?6bR_#Vxͽʚ}[*|zSM|g7_aZ'i?R8񷚶oY*im}Ϻć=Ba=b i߸<)y{7nnjo'͞f@G>8=w7/ywWҢ܏R?l4o>`åi>Y6r<9S^s|ɽ7=1wqgO;7tQN-'W>- {gni?Ϭ!IG+|nՄلr|R+]0SչOmZWEy;7q_nC=QM6$܏k]an5q?c9^E=*O~⋚k0ֳ߬q?O$58լQ_c֏Q7ab gixEIE6ymME `;wsy'SW5iNkibKC)yӺL~]2aKqHH~_6cvs]8%]؝Kkf- SZ_q?n0tO5ǺRuDoU6 Áoámڍ}@5us ͊,`MN>ddTsEǝh}Xdz19r?˗{꾭yU'<0>8,b]pQթ1/[~Q:өul2w_}}}\OU*t37JWVVJ=X̍5`׸UE *?~쉆jfy )=x/MLoK3\nZgO>ޟoshxn<CgH|Or?*l:^|yGkJ_2; sq5+Ww YuFJskuY+ns S8,bRlx_FW˩?U3Lz[qݞ:ϏuҬo%>G^!۶@ȕviͤ/q0^>P+2&_br8O~7JַpӸ*}ָEC6T=Aյ?q?|nYEIG&R?7Xh5 aˣnT]ՒNc=xb:O m}infM|Jy6߭4$ҬESlI%MwZn{s1'_{^0yp ui[gM>R-?q;+ӎSR,S/^o?k kfO֤$0z:'cf{`S+8 {<[So+/|rx{i>2u͒Z<8o'5ti?r=~wq`,s*nb7nRX^gU[39 _vv7I8wM: iT:r=׌Nw*UرޘX1 5uwqr_8tUV̱#ܾoour.Slw)V3Jqx* B3 #% @^3S3h<ܻ ٛi{Ū֔~U9\&g?$6om*F幔6u\6٦9S\ Taó34Ŵţbrs}Qum4cA5/1Qi#n~z5\CaĸMuއBy6p["+zM lM =z bwḜT1=&4Vg {%8AY OfsUz]{wzZyO4^ҟW(_LF~|1ǣ?:_,gaAcҞrx>ƌMjJ4'>uZO6p91^'ߞp[C|9mP/i?Osk1nW:~sV-ߦTވkT{_9qQ5ڷ{f;Ni97W3^(H6 q57dV0ɖOd?7)dxH>>U{z۞ra'NaTOT9A.n߶};~}g|?bzW٦S&VN<* qOLܿ~WMϩZQ>Q7H\kr]=u?+ëcOz;iݪV6tpAOZrut;ƙftwޢ䣷(^ut~k=N^In?Ԧ֛y=8洹T[6ye'suX_y~']6JwZHaﶷn_7]YLm/q4c/οmUq5=ЉH{y={ ؛zOá䭔OU= 6 m-江ч'5TL K{V'uUZ![̅l؊yeLukqjv6&,˜͌ZeU`Puqp#74uV=?N՞18f ??ucqi{CV>v {Z'w'fޱ?91tݳYY7{2>f:;֌e<9xx`y.)3 gM"GPڀu^Ӹvݸ~qMĔEa?j'/3lSܯj{~fr~q8c>~*~b֍P8z饚>`0p~{mz_ XfnY/0b_wpCUG?y*rz3zXi`3#L\:Ǟ*3'WcL;YUmn [M}4C1b瞍j>{k,z{ aLjι^g;_W> X*gGirf/i򕛫+prf ?mfX5#6!LWVX_c^_kGxu/b|f?ƓM]w8_yT5(|YE_q@⁻3k;!Vϯ\u2 K|(/0Ťm6yc( OS_zg }Oi6m{%NgyA5om8i0 IY]~z)G SZtufd{)]Nps@n~mbP}fΙv^I^eY5>upW]gn(`bgߋbϋkC' SἺy,-VX>묓kNR<%ߟyObzi"IUgYΗb17~맘WtNʴg+|ۯL>>{WO?_͘ 3N;]\7&e\z<)-M[Y3aq[dxdF/~k18y܏F ߬_7S>-|>sĺ3kNϰM;ϯxᅲ^p׍c7{9h)}-\fT/ſw=ZnϞ溑41= /(ov&z,n 3֒ł1ڿ{b5buQ8aVj=}2L"^YyO,a ʏa-Zn.P@?z@' jWoy7$y(Mb5q^577NOgw,E1Mqs)PguG;ywXīh0t4>c5i|kSI&5n8z3ެcw[{_Y7%^nf"\9sw0ٿ9_)fd=[SX;+F*K<2#*xPB MLU\l}Zw;C7:b9}׺쯰fgވa]jo>4$w3v8zp|={7\_!WQyg>zzZ}ԡ5`T_9ywg=+c3*[/zf7w<3vޝ9h1eu$-u~l_P1q6|ܼUmU퓆n55q$,9g3;(n>ygxgTz:0==`<=}xs*o?a`ư;֟*gJSk3-,/yxԀ`>3I{vSޒojs8\qcg8^H{ '\ o1u&6^#ӣ;fPXq::UN73;DH7z3'T1lf;3wqe@i >>abC^V>sh{a yq*%KR#n_6t>v-lfpg``ko^K*nL}4˴pكu+ko8awi̟#}1}lZ^bq8cxk>1E[79/JG|`]8n_ xhxF S8ѪgH?cW||\QVwi c{"4fƉ-/2n~IOw4 PsV;}]?ӗ&G w.owx7\z]aq ƣ3n1>N8ڧwo90Sɋ?Mʓ?TmsO+gmn~vsJ'\#[ O̓ajOQ|f3O>䥹tC.SE;̟g¸?zӬbUy\33 q֠IX_mzj~5Wԃ;IԙBkxw:~~gw|bx0md}Pi󘌯0_Iם' {T?~a%avo`sFkyěK=50GLsfܬ\~\TܬK\>`s7mo_izb-lߢ9U\ ŏ ZHzq&Infm]|@w+^?鏻[ LѻobKJ{ pQTs|0;{c}Y'\pY=qLq}*N']ٽVtqw?x>o߇qW]LgG6^P7t`m$Q@/c>IW1gnP7"q06:ᴏ;\jYobqrYv^p<@ 'a@œr6γ1Gm|i.nR{<ɰ}-]q=ذY.8xÖ0Nc{s`[&;>_ap/0[1 \wx‰G&I)UgkTtG7܌| /9=TZ~_Uũ[_f:#iNPtb^;%yPuil>gކ*sg=P`~z1Vb7 ?^JKwG0F_/ ?5;wZ?M>?4ck|[;Q{mi&xń5S}|y; 3N[s8Wyėx8mǃ=[W,C[}+, 3f ;h? O(<&8q% 3-SjjA O<9ω6|9׭QtFC V)J3wx06Y.kfL}=zF7zk[C3[ngй2pŒ k0ǨX;jxbLߘ_;1'.x] Vh"*HC'􅾔Unн鮪uo98׃?xq}w]9}?<<9 kϣjkE3s/4s^>3TW^TCZ ͥ>{VWU~z~Cόjk o+SxcXC^kS?K)QTGڦHI]ԙarayPyNOTY>s?YYqu.+;玙 vEi-4c2?qڟy2lkJIu-YפYfe͆4ոNeYGqվ3{)]@Cio]_?kTUGp8?tM>3@KHٺGyï9zVcԧ߇>r|G8z-j?]NHa[i^v2+oOkO>^?m^wɚΘiASs[y+N 33㴾I>N?e2o NL3YӤkSV4WYJ;mAGIVXb^9TK !\쯼eiVNLi6+y6[̉7)w/dx4~閮+,@*&N$[O!\1?:f4w>?}%=D}d\gw>y]XTyGՊ2 Քtyއ[ǰjrwz/ɭU&E\gyŊ?WL_=Tr$_1|1UvI4Nۤ.kޡ:WVW=kOuAe錻L3.)=d)+ޯrҵ31Y>vxaɬh6 wOWzWo&Y}JcgOt`'.NMԅxN`9%}YT'$ ˾IQS<}@z/1. @DK+u>]ռ'w+I .3 p\w).H&3z>uw #g~#zեrMin^UG>~?}'m~dIR@31ƣ_Jj*$Bs j;6hnڰyς3]AbX'*lZg@5My}Sod3Lgǰ<TxׯΏvޯ3a8z׽j]P$멎FcV}_RP3@RQ=XmwXarۘ)PHvZ9ʹ?IG_השwz6f^cӨEͭk-ʛR:Yώ}_vzϟ'L&>=[7RN1oQzGptos51mTYGR_\Ήϡ߳PVyG#q֗wsӰ#^suWfV%),+bxU~(=.zq_{Z_LVPcޟF~?:' zp%߰y&\rPpf,[l]fvzxb4b'sqqOt*Y=iI"'ZNV/rHٞ~}U@NJ3t3V2~\STs f$ӥy@T36y >]~I&:p3xޯq.x?ӨNaaYW CS 8~W_? SmI՟r9*ViZw;,ݺBьݼ{LY箅wgv_̪iP?]yʳ.>1ؗйtgem4Oܮ''u'ܳO˗/CO)S&X4iŤ֤^Kd͚չXjAԞ$93]{Nv /MCliW2+jWaq0>SiqI> ?:Ua˼?kE}xus\x_ &kZUTgWY+ eu*AXu|EםgH2@b>sWyqU<^9#6 S?<ris:W}UoZYj?>.T+U>}q7u3CwO+yä,%@ߥn7HWSMeEiLj-9ẻwΠ^qVLv|?]0vMp?YJPe" wi֙-9;73}Zeڇ7oތ'8f1NP'Q)V2@=Z<9LK'{lV5WT'+BKX~ ߯dv9y7.BA8s]J߇a0݇d\td5ƧV3'e㯿ip@8$$zϊ27j aTxX~Y:V9>)gXb#/nɭѱSSx`SzʩΥD_SO6|mɻw|qw:btOz7v/u{[1~A)&g:CiNSeJ5i>TOsU/7vM~~?yIaxT<پtYQx`5+N2)O'9G^Jy]H?kǔyTwC-DWe3)VZg9OrO~VjvaݳP ;|<A~ߟA=?54&ޟx. (ɩ}ѦeZi1[͘՛dDsb4?zN#VgAIRY'4_hK7on]fxf(n qzGOcY6SCMh[aVq4ʧ$$rv WryM|Uy}luQ]ަ}N=a'2[5W ]ek^€DYj?qe@ՋWr?h &r2kVvga78":J5ek3T7ykkEՇDWrzΡPi`?Ox00ưjO>:Uw~xt^k+xIڃTgҾ8F]sןYfHs"iHngfa^HtevVto?]g'L=+ y`gʋT\`r&q]}a?}߁845я+_ *gܸJ_v=ӧOL'ssySq͍y0>=YS&gSU~e҇Υ:DF,z ]LL6 [sCS,M0fGu3M _\݊3z1Wp>- lopenimageio-1.7.17~dfsg0.orig/testsuite/texture-blurtube/ref/checker-0.00.tif0000644000175000017500000051033013151711064025141 0ustar mfvmfvII*> 2=RS~ )8I@=M=T@8*-!,.Hid[ iI )Ke|2016:02:25 9:51:39 x̖cw :1Ś vbw`aDg({;Ax\ss{Ȑ!矇Vsvm0Ӧ'O_~e8ꨣ­ԿL1a9 {G4hPXlr &'x"SN9ec9BϞ=&l6pð2˄Yg5ڮ+a- Fix's,?]ŁxeūϦ z@hu+27n_[}ЫW0|ZkeOxØ1cq8aw zhXn 4y#8"_G/|l¦nmS޷~1_Vz-q!>ĉ?+,D??4 !Nċ?H<#S'x,SEhhĒ?_|Qcf)}ּnu?#W{sL|sZ`^xan:93=zD۷oXb%O<#;~?;OK?H<%򀼑?,u4g ɲ7'ͪn: t:r&~1S=zt[曩׭Ԫ]tѸ},R\gof_g9u]7a 6;o{~xç~dV~~J|3&7lhҶě湏n:BOδ}rIU+.Ұ+ ǚʐ!Cw}5ɽ=3枸,'pk]w w_ji8Nq٭<1iҤ[~:M'ĝQ\Ftw(+C(5ėx"=+UDW^eok?яc>@SO=u{;c,r[\}a9(p<j.qY"o*úmȑ*5b(.ŧ8h|7C/'< ?ğe8gCO }ҙ#o.0~̟YاsN־Tkp /s}<ˆ;._{YO> lMxWjzaf .8JM?q*^ŭm>%_ ~SORǗx9]{BgtwL=À{dફXH&פƖv}1_>Sk 3Qef:ZAٟ8į8Z|wy"_M$s os67, 7tUz>(,RSZmL%[b?Zɞ=;Ë/NwvQ {,H묙a}Yя0ZAٟ>,ij\wDț"_x9>ċO ;X6ԣ'^{?E|n 9S'jY/kkM7_..[wPSKY~³>[4V燼5q.Žo? O䋼:g]Un[Ǔ;ju-stH[~ 1bD8;n}N8!9.k=y睑kW\qX_{Ϫju_y!?|i1|ku@=*fI~=Z9Ї˝ГO>97:'tRj 5%>xFkwjK΋=BU_vg~:䃼D9|T tѥxQmG4'>4}ꅺK/>q{q.Jk~x#~ϓ?Ϟ5sHT]ٝ /{7xc烫<0wws =X㟯 Z`+3zg.7ǏڪmO{?/5xj׏}~V?ۏEYٝ~V !O@y$zh7xA|XG0&DUhV487sXrvs7F\ 駟<syb^g+1diAw^Z`|Թ1蝱J9D+}sS摒q>/|'yY=7I;ٝ /G;H>+%Ϫ$UWY NC%},`klŶ`z+u_9+J+Uz߆k6rzeWMog=^w=ΣjK/t\-a,熵3H88Ǹ8׸z؝fL  K7yWU;iLJx??nefbkD%Uum/CSN="YA.:j}Ǐhx=uFּmgWOEܝ_gu a` _G<ٛٿ E=.囼U{g{.x?ɪi e,t,MC ط~Ӟ?gqFS}>(rOw>/t#gZGg-*={Ƶ-+k_2W`7cGdWegfV8'䃼OGyYv$5!|_OV8H=K4z.6>wط{bhy:2w>J{pi;=HsNU{*3Y.n,:-ʮ!R>"ER~Ӳ";Iěw1zFך,ji?$g.;a„y]:?Yw4\W]_z~eӐw}7#ד$3:<ҍ=N}_F =\|'yG}4j'`ovg~~~/~+Ŀ<i܋T2Xwx;*5ֈ)[ϞCuL̩w=xϩֿ|1>R&'!?xl?CgUCٽCrkzx_h3qucsA:Uøqj`ovɯ_$Ŀ<ȊڝsOySW«rs} z=qmQ3o|v#TUٙٽQ78aI ^˃!vgc W>n^oυU9pJsֽUk;BXye7;}ﹿ7ݗzKQH,3w>{ud}Ə?> Gr)$K}o &Hfi䠃Jfؿ{ɥ^ :4nVC8裓}݌.QW\1W~=f/;1ugj?)|U3exgm.h9'9묳¿;Bc袋駲  > 9hР.cy |RK%[2nܸ6xdɄNZ믿&?sw}?o!^>C>u1DŽwuO}|nGhߩgve_vnG߷~3x ?)Eq|^KD#<tI]]bO<1ˢJ,M7S^l=v[una2SvmǷ#\p>(h/a>_yѣG'cƌ ,le7s$/rxovNl|1/;#5f+8y+=؅}j=;/;3ߕ@< Gx!|S k`"ъƉzJW,Gp 9~;w}_|1j-g̣N?&ПkX}a^oغl_;bo H a>lޱq}C `a'b7cGgWQ_ą'ͬ!^Ow<,Ues=}f51v?'Ab~ѣGri%gyf[hsΙ~/gگ)&,uYú99;3| DL_ ǎɮ̯8/F<~{7git_٬ڝ?a|nTkgm `-;H=Ж۝7&dgqFȭaYAnF믇u~dE ZٓO>y2dȐ8AS'/;7~%Ƶ~̟u#7GŚ+>K O᫘y{VY6 dT|Y4_vk HNM-3<W\6~!'&yG$;S26 805t Yio6vdW~E'Uv.q ~̟q$Ug{gE\wuASeoYzKw[E&[mUاyOssNg+[N;cr'3<oX0}?,3zs;Kn0\wu^;Y|UW]5yrnemB;dWK??,1nJ|3V6CWx u6 :@7R] ZgzK/Rj!;OCMz=M泸$e0ճ_`ٳgX{x#psWW:S>餓fw\&'Iٓ]~]<[ˏ=υoz3+]t)/nvWcOnB4' ~W!j5WBSpNܨ}9V{~޺&뭷^8+aly^EQonGދ{TF:mȞEL7~ܚ[[4ĩx"FWK=gs,o &Zc)6'PS>~4^x'Y|:[3Eّ=cg?l_q,['xd`u##bNzP_|[>k%jjݱi?XeU:B -ZuGuw'zji:묳jL8ǭFr%<"13,ΧyDz7~Ȟ?~vVhgq-y'x}z >ċ] GG ]X?3vPkHP眉Wj<q'o߾";CC 吨#~^EY :W^:ͱvj^'W%55U/y`^μ}>h%vI?»9?nY>;K^~y|3cǬj O+'⾫X~dZ=+EʘpZL=7w:\'[闋/8B7[r|UW]m喁b%Z X8Ǻ`]\ aC##{[/'[L5T[K.+$WW6~k+V]Urk;O>^\務 H+R;;;6c?vlc_/ڬZon'%ğeTj?m3&uMyGHqlQY+mt?G2Ɔm]8Yf[n%Գr^[n!W]1>s\Y:[l'(AW {^Ő5q;'/;Y{^KYK~Ϳ9^xB[( xѲtw 26wǧƊ):ꯢrVvn\x!O9h=R|O׵䎎΂S+rW_ 8ʢC;vlңG#؍*}ٙٝCѵ:]k;1Ae Q|+->GtJVw]x[ۓW~g1>5.A#bv etVw]w۹<[}Ccw1"vg7,/G?[_z֙!1 |7'(>ūVF=BwG:}OZ\1Ku1f?Ý޵l^:|g{VrCbأ1bD?&\2sq{56rSѣG~6G^ ZuOdmɩ!vg7kG?/cY B|d}ƾs(vW45{JS g=2g> ֖[Fwq0[Kon ~ԟUf6#lC2<<uX}eC1wuuUSƫE>Z(m:thf{pO[fY;q'YêcDn[gq,ֈqE 2>+14ijf9hE+.ѧ t1cƌXo|zh50wv+,5?;G{晗/̵5ycޠGT.=¥Quk+Yًݪ(+ohl~Ꚕ k:gyf՚v;A1u;ܳ>sW^y%܍eNeOD1@_b"rhԻaőxW+9;gM{xVf NѫXL2xtg>Zez|ɅIc;z&+r:- zU|]^^Jn+[X0fgF1 >:'?~XĕgY S&/6L*[:~cN:)<.i.祖sza5>~c]jFጒ3Wk!^u)|Xm"aΘ/\ʯW| b2o?Oq?,LVx4 ?jsӶhWVϰį?ouG܉?j?h]<͟ڏs=E[p#C]߽9OaK1> /yK\Ǝ׿Ÿ~0|C{KaJҦ*/fϞVZ5w=1 n͚5a…+_JW_=-\x?q{mO;k nmϐƍm֯_~߆_Wo ypwa˖vsXhvivjvkg~~v 9 I#={ ]b>u'kŏgb?=+W }C>B'D8|ܹѶԷ~=?iӦhѢdɒh0gΜ0}0]tg&L~rÆ q puׅn!H ?ƍ o/Oo' y5koxxM>KIO{{_!N5anEU_'1_9Hɓ'/| Iˣ75vw}w8û/>o޼a/ D{جN6-qSOm]/{:>_so8M"r m!r"/r#oxx|\:'=bD=b))~q`k&|_F #wy2eJVb{۞g5f^ׄ.(3c>[ ZlS6O,ʺ'%ƌvuF&,|;Αo9W1w"G$W%&gn.xu zJ_-m=>:|S }xno?a֬YўU3g +VԲx@OO}S3{S(z G Hn{MȫZ̯ț8[7+/7zK1}M{j[WS1Cvf&mߍ}[uktϯ<0szFկ~5U ӟ<co~׿,no㎫T%8b|RYU_6xHs~@nM9=9c!:qҤI}{_#,9G=*p cŋ۳.ĸsK)@vSE_>q3~xa_x6(s>kM{Ngc]Sl8{ήMg{_}7\r%ᑏ|dSM}ߍ\mO9掓m6xEx{ؘ5ʇ?#$>(GgX8onoc+6+N=/RٞviO{=H2(rӸ_޷'??\s5I=Ru0NU.8x|~Ũ#1۳?T^Wz@E cz/vKQ+}ey=2=߫r0w>yXӾorjVOT9_|q1cF]>sI z;uc:xfG7+^8^N<[W|9O_]0xx)R R> QFKs;tZ{ՙ0=7 z k;Tn};3򕯌gU儶9 S5:[-[%:p\=aԩC_7hZ<&mΫ׿ݲoh #;c~OgԢ)~Y>Wy~Ҿ i i^KymOxmЗ:Fwkf؅ ~m4{@N_l'fS6|[ڞΉoq?̼yd 讻;:*G*~8∡|Xڣ]ڧ[n~u/B>MC_o rz3L]azKq CZ;Am3Aɀ1kΣsj|׳FuW >_{nۚݠ^~ky+'tRq}+ nu2 ̚'NϜ{A?g@X*' GWzKN;ڙag~?h7?ujrxM-x.s1ٷs9'[ Qo悳8x?O}ڒ/j/}M5OWq珳_4 9w2$Ϝy2#917?A{x;Yyn/!Ϟ|:@n^DIKtʝ;Nѷv{ydEOk`?$Gvtx,@vM'K}˰NHQm_L; ci:vgڞ ,wrR%V/AkϚ8iӹO@Uəɝnl<1#*aڵCE~_ײpI,KvWw/UT4G3C6nXoW_^~=|;q9hI{ٚ( A`q1>Ʃ*GoLTʠwGϤu'lv}ɱ7^Zj$M3ems{K ߄K.͚GtXC/qnLJ2)sgyf iK,vuZ-⊨֙up1wzӰ^ߕb/ȃ\1N˸?8+CH/gJ/Ls^(jizj.UŹ-~4|="شvۿq"w}2}+k><PJm3~t>ERB=%g5k*13V.lvp-gNPNߜw=uyK0~xתuooS GzI?Sqΰ79j59>G5O~?s˖-"#ffP2eJoS5ܱ+'S1@{I)])YW8NGS<@z˗/-WCNNgdhQ1G-f1:͛c^{{;wK~Y{|-_] {s08w2xx"OGzI?i8{?)'ɭb_sݙ?dOa [8[CqgqrS`oe?1WPG|#% i3eo/f[.d17w c\_@r_gAıI.[mڴ)O{/>SNr=w~)QI[{"^6Ze/xS'sƋ۔g$R?[KykwV'MOA ^A~?9e:aX ̜5Gk6\2skӧOm(xb/7`=#xX[X"fݺuC}W7|{ߋ|sdurj dž N% kEu[#ui_1W_e} h/O%~-)Gu*†+;>NG:xobϯxߓq燹9P5`.Ɲ!MA\ϙB^mpb-:wk/@Tv]`}_zŪւ1a'szI CMٙ3gc{^$Ǔw~~}r &~ [{ѿnaKs<]bE9ߺ~2&,{L+KS+?[_§&s,8._Ԩ7w5닃:(8úuLu{bMgyS{uȍ)A?#|J|@/G@=ObXeƅes__n)Qܡ߶ŤdF}e (կ~u4uO7yN.2̙~9I7wC7n&w3J.8o|]Nuc֬Y1.)K@Ur1S^rϡU ɓ\ɗɻn} hW+ R=V Q {^.T/;kHY-ny_[ѫO9 Ti9 f'cnީ}8Psc>SO7K:{8֩Ϗ甇1ԭ^>ۯK/=a^&ja:??~|ߵQ+nM8/AV97q,%^m~zz^~ѳ^NS+vMw}.2 4?-3?m_FEA 0YzuٻR5/1Vok><2͛W[2p9@TvbhPǶ XO4)pqгږ:Ѵ~oq0Atg?zOف`د^{v01 4}|b_5oE,|yW_miӦ73,[f}Νro| g:u5ݰvΝ;7~''V{#ܽ˫ Ԧy |W\b+2Y~|>N?9cq3~Ux|{;(}Wκj`R1]Vn;8Fh/%/WYo"`(W]syDÉZÈo3f{΂ _Ej}OIt͘1c_qՅ vC9$ԩSֱO7'^*_X2ǎ?%~<PA;|y~|L{2߸?h<^ 2KLQ>+Uw`؅wAcV^ {#U?P3@W'0)?kvx wC#BkKER"e,%HHTHEɖ,٩JYڈ$YCQyNy8;gfzsf^3s̙3g=x[R|[*^y{73q{\1l}d/<ŧ>Dgw yW<Lm׏O~׏OŮUs=;Pviٟbo;Sq!d{B -T-rbK~~(`y)^W_c5X =]7袋<)e_}s^^wOz'?xғn?.zvhvivjv^?1mJL@[E*WĮ~bk?W}?v~~c6* :]tr\pv.or\ 7PlV~;Q ^?^?is6lSMWR__b+^Wd}G|#)9昒+^K,D5Ys=w1s n'}'v쌽 bo9Lnk~{ !|bӀO*}W95x?X}ՋkK\dm-&aC~Zy _|_C^{ 2o~~( ~xwQr>K9眳x[ZK/8餓JLrtCqM7^{m׍^s^ܸۊũZ\yW_],Sw]ڤw[ouVκ gk/kЪ_02noHwzA?IngzM6?v^ ~~-8^Ox*š5?~mZWctWtvǗ_Wc FG?}so?;P~_9yx4XÈ5eO{&_.wq8o}g?<9?K`|_t .183f//;.;,ԧ&׿u~wS\uUō7Xʠ }Cice<5۸ɿiI_ G9A;M`/td9Ot|C/~|:W1k~|7gۚ Owau<#%'m(Ǿy>jOpW,c`9{8a,&s)+9M9Zk91yǻK~Q?9cuQ%vXy8wv6fP[B앎mw,q7@M wCʙ+A?;`씽b>?~0u< cVU\|#ėx㉧ɪ˼~< 1kX{s&>$\r%幥 e?I>1qvzscxxbWO>P}+" =Xk?яY]wuť^Z{O~l9 =Ěئ{[N? s\{a >U/{3^/zk@{y섽Sno.+K]}YPu} +EORuM|MOק3ƪ%7O,bQr[O8c1=f`?>?< ~|3Y]xĚoz+_u}\Ҙ\o~elĘ 9pbm>8 pr}ŭ}U9}LZ;#x|>j{O;͵ܿ r"/rk# or @.;a/`t]"8u=: FE&ز8gP`J=`ީFĴ\&tS>ؑ=P'|ʋI9|'5MMxFC$[I>9)㾁 7O9~~gɑ<ɕ|əsCH/S.؇簗D1{f׹w&.0$9y~ /h1oq1>~6Թ%g&؜:#.v~r5f߹?!'I< eCiX⻰6l~{˜'476HH+_WϪڗ%g=\s9-gaH?YbX}Kur@}V{nb-\s,H<k\z-ǒK.YioϙM6dncMwPc˜? KMOr՗ٓbH`<G9?7:rǨoxn5;yoisOc?DIpWLx ـ9w=:7Moʶ?a O&WI`|4ryj?`ߖ_̑.|@j[vu+[k^Sgŷ`7)_|Q=`܌q4B4rA ҏN;`m{{bW9j5skMs[)w:>kt/H_q|TߺWd>:v]p"Tٜv~;jk j1eQԸ7SH/| > r_S|MIj;brāQ#lgW>zϖUVYkgs<{gYk;7DY}08oǗ+B/=/O{L}娉=8z_]OGQݸ36P;\z_s9gO= *KYIXy9Jrg؃^R|fOO>pcy2jq5:ku!;/;iLWٯ}k5w.8f/&g}>k >[K繞k sԵ6Ɵc ȓ\׻'7]79Ωz/}<0'}5 Su/OJG!Ven[!jq,s^W?#(Wa}Hޏ ra޳bB_퉞y晏2zG${zhC3Kk!G=P1T;V [sC_US싟Nq4j;n o tWCny<-xy=>yB^. msIsTh y S}^@uAo=O;i'{7vcs>h=?_P<rO+9ڀ{UOͥs;MK E0zMm&8_llx|%m)cl/p׹J3[>u쾤:,guVY_I<:ց~˜3~ָ}dipP勬gqim}NOn˝㫝ګگ_C999z6o_ΤG^uM;o;5 ݱq.s)_!B} ]i/9|76:~K'^[3T)6"՝SCg?Ys˺a~d8R,'O=_l&X+jԍ~ߘm]Pxr}Mj y& Yq}?5#ykJ_,5K}jc~zb\WW~v{^O5x媯ڄ|؇?1qԥ yGƭrTrJ9?Lw5919yg(] bux\ʙFS;sWY^gMT*$HĮs5\Sr*Kߏu_5XwjUxcrȑ<ɕ|S}w19}߳R5^1 ?;wRu5a%~{_5:}<'Co\4907w6O^.b9n١e!g_>s{H{wRQkmT+@>P;ϜRW-xի^L> NcW>ӓ2錁_oT7$G$W%gT }W۱.b volŏg@vm:U<ѥ?xhښ6`W?3򕯔w/}أ~گ$[6Dzhh/}Ke/bZˊo[B<)޵]y,l'qF+gY9gdѪ-kg¹2 ?K~;ǴVX!j/LwΥ kk~svfelӳrW곊Q纇H yͱvvW=T_<}.ڠwyGhi[W7m܍?9D~8 ^z;>.snb^mjqڒFSƊ |7g ~3΢=<~.xƌ3sՑ sq5V]u6'geذ"rsÚy5'KO$[vFWo@M߾Q~D<`x1GOcqcuNiQ̘_^469Xs8/'{MјyӠګگ_9cq@B>a/ȯ94AO SJo/gz4Aݲ_c;$uȯo]vA{c7'x%#UA|8=U?{ n#'d:bo|-7 rN另is,5f},>޿!i[g} cSN6^֩}aoWskE8ʱ'nL2˙#Ww<4GO?W88C.CNEn#B_ G1Ǭ aGgQ/;f}P ?7~a}z'Ԝ6A|jM8Ñ\h9OsnڠL@>+yPvk]c5_)@?j9cl]_s=kqQqgnX`G̓qƐ5;1Nk"7$;=O//=g{2N1`^yWRR;Tr. ?H;֛GjM_r.4ثb3Êm?U3|8=\wR9+Ɣjqyp9dLci|8Y~1V䦛nun+؇6=ۥXr۴}u1{d֟'[ZNrih3-Ըd񋩇D^F~H[>+EϺކ{bWf̮$L GȖU?s_o >4ϱ9n\Ӯ5#{z_}tñrĸϦĈ+|5> w'mÝ~6xz~C[w?h;H29;~hOJl>TWG<9b]Q9(319a~G9 vrqt#ӛ+[=ԼyNrG{7f|\_ϗ)˯qH~;о|,gC=uqINEccQXtnN"p^U+wvi_P_?Gr '_!'򚖟COCy0_z;1Ubbm윽koI:?]kxL۳+eq19C|_<0v>Yfu|{_fL1U*]!2:nbKW^9ko{zE,ego?im|-5}8zng ?_N}' !' z@3>vڳ?;ݱ&Cm;?ȁ }tS3|s|%DB>D^C~#cH/'=9<;`K0CKޙp`>šD.-/6{ߋ ^7"/ک?Q2INr]f]2<я^ oxwp?}oCQ/Џ~?c}-;J%'>)Oyʮw/' _:=pK_z8Y:<щNtɷWpWrx;9\Wu=?/>}nam'?ɇ/}Kά! GO ??Oc-x{kU:ŗ[Qw_W-~=qAzpC]\W qc'?s zֳwckn7|#>~#ֈ#`o|𲗽l?xGq# X|dx=Ʒ| Eɻ~퟿~64~7=?_'_ufx?Nk*9=ϛ>oMc(<'Jމ_-B>]A~H%Bo=j0KIO+=>؉y{dEs[.,%xztr81Nvr;yx{=6 XE/z1Nw}NOԥ9X~wxk^3/}OΧ񗞝#}a0qCK~wq?F|կ~5.Sw<{{8Ǹ3׸'oɏ LJ%C<^֑>KIO}_zC섽v>iJ6|XJpdQp^nA\[CK;B&ɏv$I&9"Xf]ۻ8=cCXv;aov }cϔխf@ox}/~ˉv '< p#oy[׻֜ s^ =թN5y Gȟ'-m62Þ{9u.-foךX_Oȗɛܣd~M8$}s> a?=D씽Ω N,%[1\Cp~3<ߚg;fv!8޵^{5|3G^jOrOkHܢdi~%"y`?nȑ79Eܽ}1i/[}U׺ֵu|(MHTL1g1Xx }tᴧ=h;:^]9yyyyo|r riՈɝA=o.c=z^_T#v~{-bu˷r{XW N5psi-9\ߒ^"~4ZvJdMG=jgҷ׾bZ >1Y5&/?|{[}5^j}cb<7ؓ7=Ğ 4﵇Z# [1(VO%?׹5y^kpCuNa -0lWkt6ܙ5o%kr r!A>D|<;G=UB7=aG'^-n,!8N9\ߪ0”QtGgב:grmXf-?C}u{(_Z8ƈp ]hF$=y3yݶwHG:pmK=Uc9~7:on~^}/{?Zqy])b\gk[?=oF]]|%{p~j*JW ~'o%.d}I,T#~?Z{VFj 64]QڻWC/s4m=:h'kIy9u9R {f՘/!Mxn8սo>CQG^Puej_{zo}[c_X=r~81g?Cbcs?;TSo{+,K22>4^6~0%~1{O~PF~HQܧ'П93FGzI?zOA;kۙ,;f캗N ~3OHm,|7-{Cd/t-VM}#u"gzl}MsJ֚ϳkbj{ȁ~/3Ϛȵ56ڊdVr0466jwkgԜzS:| ߜyFb8'|o}W:]r}.hK#6~0̷/>7?9b/$WRCJ:GT%=B)J쌽luϾ\"|3%kY=qD woO|gmAۻUuc߱O~pxgG9FT\ب_Ó%g*ws>?!y w[׸[/O=|r r#g&w9O~3V]hv>^|1W_51\l[?6%x%9![ cϑ5%$FV+#VzcEA?ڡ0߻orm'=ؔ-QO@-}>XVR껚#1`bB~ F yJ=?)`|rꑣ3qz~Zx{FSgشE?_{G}CN"r'zO=oޒc|iv=ءބ:]v{ |٫\[CH&+]K޳dwϿyOm}k$/%MrK]|jGK.9F|prF||~.G&^ #}AGzs|CŽ|xT^/gZini!|O|_|]"zA?ɼO1}W7zG̩E^G{v~rlΙ7`vouFp|s\#܄ptpvs #w&?.mlZ֛EI2k=Vs$#EkIR*s| 18'{7/=wsFd| w#[bSZS9U>rmޒجDKgqP9ɝuX-Ғ IOY _˜'Aji"dƺL X)E|ks7[5 0}6KksB#&k-M쉪N?{L}v:2Ӵϧ5CZx{F?>1ȣȕ|ɹzC}*ՕozN[#A+N#9Dp#pzx]=Nn[寖JF: wӝ<ծHpdOD&ROM,_ e^:ڻw}j_᳜[)5(Ǟ.Zzu5p\"g+b@1A%l;{2Nk91σ =w%x9"=AJOGW wV+i-%}[|U:e:ܳ6rgzrN{{I|R\%D{xO{x%8~[{xv+]i Zت.H|ek=zo+Y;$7.; rteN,;~Cw1ܵ<"l޺v/qqyy_. ?9gsB>D^MOJD}W3IOk+AIĮD`}xz{pg?Џhm\S7^_"~]yngg9s˞A!6y83$їFݭ^0y\\;ik arka̻Ms{3sFju[)Z"/bH^Fo}+ńs;W/뷼NS ~?O@95c1/3O5H_ի|ȯ!z@ s ~ѳSJo[>C{2oeNzY@zWzw908[P}ǼS%hW'ȏ[gĻ|~3VWzCGnH6E^=ٯFp"rf:Fx kwM_0-zK/?{H[Z@6egh/:t˳Sr.mٛ\, d~C&HlyPw`3 쩗!1 Xy[w=m)wp/px>;N!"G$yЛ3Fj~2c\#.} 95vZDgoN}!x"|Hg2 /qM񚻆?jz>GHf<gp'G>Z5Kw(LW"EKÛ3kj @cVKvgԞWO}P;kEΦ}DtFJ_r/Z0~Z,~ݽ^ W"3p$\迸eqf,3e>'o+j|+kF%6rfN}Td8J8o|ima>Y>_WyKg+s-CO (wϸjӈ>#v>IZGOmh;"3=SL 787<\s R`YV-b7,N~>}?=k8y]1/3Om_7Gג?&r!r*٧3y=/rYX1L?NZ99bwxjĞQų{ * \J}\l,yH3?ۻF{U%<+ίӵׁ+ɽMoAο%ukQ="&G[+?靯K)uemӧB`HZVe= ħGζT ɕg_Kvy3}o:>kmPuϥ=괵fꧭ&6zwk/YEDtb;#ӹd9;m_NxZʉ`:̴n:39q~ӝ>"죖w|y|͹rgv%,|f Ɩ3;Cwsx9Ƈ[9v ۝#;U;_yz,ũ!~02>4^n_g^ۂ%9}c%*y>[Mܯ aanOHy~~VkJp ^-"|(} ~c+y%;[kܘΗrY'n /O3G0VIq |!~׳ߢHoQRSVYTKu`)9k57V8QjoVj5;\lֻ^MROQF=4ظXo_}ڇ%}پtϏ،/;zɸ/|vN _ /w~C)C~%L9J|v~SZ"zN}?a?F=윽G#%8Vo,dg80GYF行ƿmI/?s8Q _f[ckI>~c yO9v7DږߌRuLr-^R.o~ąyU+M,TtVT8iooMK,%GZ-O=fJk2rΞz Irwsv̇M{+sh]I?bA dg~{M};X22?4Z,_9ԛgKk#$W/A'%Av暞ZOKN?>1aG3.g{^H>[%NND~Vįџ\"a_{=m[M{1iG>qi;_`Nַad=Y'^i΁ª^Kn"mSZ;kE%sw|Jg}^=~XJmsŔr1L@)ΗjEM9=GQ{nj.\6?amut}ĸK_d;qz9"r!rȗ;=zA? }}w%WzKKcZ?ώSMa=ts^(zv9\)㆓䱴'k͓DʿNkez?7~uAMO]z~kiY);R/ !}ޣ&BEխ hlS&sEs+3ҙ..l})COCVo#V7fN&t;NwK-419N%.{þ\8 ۢ 8Ǹ8Ә4||/>wn/ȉ-[LޞM`^O_M֗;GsdrqF/ɔ=]쓝FWu>8dҺ7Ks~d+5~?W6*{G?;kՁ~%}V 8n/5K=jx(v.rS\a>zGb&Ŗާ8)^UVӍ>/o:#sΩѕb]tދ{$c1i<'W1lRo^y|qx|J?_>7\NG{sdOr?K?zX 06zLuޗg=;ve<{? Ad jw/8zȳf+Q":֑9"j&MH?-)wx,I>۶=۶mسm۶g۶gsOGEn̼7UfeVVp^җ׿\?:!9Lgx+WӞv׸5׼5?E7MArswxѽ=aw.pU|+~g=<<1t#?]xu/&N|x3K]Rk }CG>ÿ09p}6=fӟo9=A~_W6^W4qd-o9\~ԧ1y7>~7կ կGZb_쌽M{5n;Gp@ߖ|39ckknO8 m +$o~t :`|#bp]m8y;Oq-ڄ]zpы^tG?qj 7g?{}OyS;?7^7}L%@2h]24|Fφ{qcXÇ?8ŪE.r?{|;/5+_g=s+<8!ƸEzJ`8<^{[3Z>?yG wG>#mG }sfܙU>'/>_r!r"JJJ3=//zVzI?i+߂׿hWTc^zc=bYw\+2/pRg}8 O7! [!~q>?:2aߞ%}羻$zl.7|;sD?v׽uUrpM ?!Y}y-oyy{q-x39=ypް!B7z8D^n~g|OWcq 9Q'>cLn׽?yf>eN_gϐc5F%/Ĺy)\+夵w'?iz07oG|෵+d[_t~A~w~<ڦ~OoN:1.3Ζ'e_|o|oC^Z!g& zS }w'=y >O5E^MTH=\-[oYJkApt<>^qx '.mwhk5dYz8dz.w'?O^'W=muLo|G]\kx#3_7Eoۣ{V+®{OtȳzónRa1i|>bSG]ȚA-v8von\>EwiO/OmK8׸b >>VLl|rȍ1t|əɝ[T k3tcM;-'v~z;"|dz|vGI%?51i Q˗.r i 9{:mM۱ڣ9~p/YKXKtZ҆:7amʿmClo}iKHlp;q=r6=EN|:#㶷툍Sts{lm8Y|_mn ʹF^œ0K0 a,RwV}{[}Orְ zJuh|]:=q-c=cr3}xkԧxbbag|?YoX|{s~^E~~~3~||y&>'ּyK$?r$Or%_r"z@*CZGUzzK1-5k%zի137^J:khэnzS4ortꠦ]jcNazqRr9ET[k<[ӕ c[aPdM:&:Wz4{PVׇ ˇs0qS&O|bR<;[tuOk|ew1q1!^*w=1\>{y}?-r}+gw>W&WZAB>n <ɕ|zQk=O<u |^<[47F=Gس1L-~9?ph~wkU b/>/uLU?֪!gc ߄m0ESZt |ǎ)brr&a.F glg]"9[QcVո3ŗR%)Ħ>yZr ;/ghUXx aQzQ/=KH{վ~~~o}qi8׸?~*gӑ9WIKu}}W-Cnުs6Г!;bOuQcr*ϮwkNKj{)9ՃMu /L'ժeA6j]ДWZ[әKMY3馵6kySbWK-%{Z&$i7l}zyYҎܓoۣ}`bpn$}_~Y{i ??z0$JgR=\]W7Wčf7lo~[~l]3#9bb~ӝt{̃ |ű^>5kzGT,TSOU}>*?%7:ǽxvrn~~qx??#~w'qCn羜Ln^/zV>KgOw1}׭vEkNgU/o\}f^#8q)bI;nB߷!s %Zx lzVGwIlz_S`XAɼx0:\L)9kz&32ʽtQ Ķ[ؿ}Ӕ^Wp{߻[{ƧgGF [{6-쟶 wOt{mk^s Oz# _#noB}Ѕ.4u3c@<'^{68Q/ǧ)(O;ksQo\W?)>'o?9 9gIoIU~ѳKIOi{`Ax ?bXpii5{k1nBpOK(;?\r&=5:K7!6;kc)麜 krt죰m9SPS}q#o豼a/^3*&Ain K鲹wMPCk?U02r. }:ey@&oi_C$^wk 'Vm+~|\'TP5bq}G^9[Ѿ~~~~g2.3N5,||/|6; O9'f&w5/zCjM=ov^O9Uz^ozު?ѳ1f_Sq{z<;g>Up~,=KnY=Z\m'?)gi#:=wZߥd-Ѫ%ބػ9/06_(Lڄ69JK+曭3i+_mQػgݪC^ Jyq_1^{UQkLm͵>pIOzҨӁrⱺ.OqwO y_kx/Tg}MZ{|'R539y^ikjb5s>P#Nzze:>O-h >9>7׺w!'"7#G$W%L>zCQOFC9< zN[Ы/}SZc!';g,{@YKk܃53G;_?W?߭<&?vISsE[Cbn>?ƹ~%+lMȼ]:fnw(#x#o\]IVS̢)AbxcGx{$22lŲbK9Sks~n[2jXOkK5 \ŗ'ţƖs8[o}yL|&q/A|Y~Z8Jw:5o8gĢbs{6>xu"ϏvqZg ~ j k;{)O~yDLޙ#ίOY^{c&=7z:?{k}QccoWv~}sxH|SK 5g{|# s* wEg[͖W#g:d[bİKHNሳaSge.ՉUs5djMuXi|;ͭbΞcê@$#'P |lxRnJn7cP-{qLO,^שfx>ľrB7Osug$cCc1e7]u>9m7E;e=K!u" \8 0m\8/F_#O?ah\gkܑ|>WkkOA.S$/r#?rUL^ ={5$M[z } D Vm;co쮗{fO{}ݻf^9<+sZB]lBs=l.7R#r=El?oAle5k}389dk^}$I3E $f;'sn]+s;cg/F k΂{aNӆ[/Nd|E%9Y\_(:LrWrNT]:#5^hLN/gv\u>gZ=}ï& q; r_˧\=}9jZ|qܹsk3=~qWF5n+xϘ.>wIB>u5Gc&w@zB_DzESmzܪKTb/7cb-re=c1w^ <+KkɁ{D&gyx3 Dj\Zu sDvzzkI_ Kζ/昝:CsޥHYSlWWrÍo|<.XkIA`{V,Ztqy9e9.j={K}ݷEƾk֝byxWn渐?/鳵lmqyg0Pǿsp\Yu ^ykO7T;ǵ վ~D}Ƹx3߃/s>gۋy?y<y+f=&= z:Ͼoզc:;9 ck>D[K΂/Kʴv!Of>?zk<#"sZsк)Mv8#֒'Fkn<m=z\. !.nZz$f?=<ު~C8 ۝%$ | y%{E7N㥸Nŭb {ՙkY! ;x?m{AŹCG9D>3 6X!zfQ?|hOko0>3_+:>'o{;yvȉ-/9=>Գ {}o~9(zI?[V^{h7;ꝳa ^){e];A,W6)kMww{흿8EsD7%~lJ%ů5 Avٞsf#g6)[Skdɾ%u:V G^\*9Qu`9WN\pvo{ ]J䣦`_s]mAA,uKĬɑzR^kҟSh_԰_˞<n+ΰsQ+-?L7kqΞ|:oZq~uI?>jG{վ~Ĺoc\1ny>_܇1  :*r#?rk;mzA?|CS&u;)5y<ޙ=Έq/;d){ˎTw/+Fnrx-{dpx)w8O>p휣S!7%~\7YK:d_W-9*Πߖ`lJ^Rrfr*Nٿ,!KVسyv5YOdgfFL!#Chl봰AO ztѼ󁾷CRs.&w~3r)\VGo1~d߯ {b:׻y՞vv[KS[}7e|im{|G>7-r9{:y{O~Г#>~5zGِԳm&<~z2;bO"v%Ŀ#S̮{yڂ3Kb^׾7 ґ)<ك [DM? ~ϔ>CG{ۼݞZ%֢l#"+Wjc| }yҚbM%{qJjoqoA{(m]TWe,c/\67K鳸1PĊ4'ոϴLoc^k=Y 5'˜E 6aį!zEj歅p9`:*ιxϋ|>чot|_Sўv_W_023n~Z,>WGW1;y|ȉb6ɓ\7 dOZE5?zXT{zuV}==}ju3{3PCgۂZrV)";\iWސC] Z܄G~rwr}Y[ײ޴7l}W9ZJ1?֚]=54gYKڀbyNd`l7p=KgfR^;]UB ͺGJ7U{INOr>Ng] 'k~,Ϛ׌!1n{ŎOz~C~Y $V5n}$oyz|_|hO& "\IOCȒzsknǺ+sުzb_yc]Ϟ}kݫWs|//84{r,flp^;C}ʚw OVԶD=7IeO7o}_{Co_Ka")Ys~!=l~YYRދgHO{hU=w O+1FҶ6y0nD=Jg5k?y5Z6| #Ogyl9o9[:l~Qc\xJgԩ5,qr\lgoR~Ѹ:V'N2Fyh>מucװXX ~Fƿ}繞iW?a'=ss'}m4bgi3rf{o~<"s~;3pҚ|lRC]Siކ|Py$6m~kOXgnJx/@[g)Z{^<ܒ1BgsP{d}:ֹjaz;RLVϕz%v2y>R]9 [_g֗|VKF.6w3곘Y:-#\PdO_3vnއ3 X>&{zgto}w@hC{~_G~ ~c?·$!'BG="'D?sJս~泃c=x{NK~}/{kOcՐov>8ν"zwSe)\޳%?m\%+6$XO`z4N<#_햞q'aے’Y9ف0ùsZ>뷺\CZTW^ojmaC泳;g,Yq>u5T'91ckq>X)f}P|Z"11ˏq1ά*e>C]׊Kb݊1g>K^XpIؿ=@5q~>ўv_5 a |w1s+־f)W/"rTsgN9#lZLpY{;8b|61ϵ7jbi={wj N \u1pZ/ K\Ἲ댉Zc>'^`qiry9'Df&5~7z6\g_9pkj8@<3w8U^\,KXW%r~;?~X|hO/Ooc2NǏx/>>g̟Ɂ<ȅ|y:!j<#=/l=jgf;3橙3ߙr}=xBk){[ٹZ xS߉բW;g[ 2[K?XmK6Geg%ժn`6ͤlCH$bnjZ\ .Zr>K?x53ֺ> ^Z ('^SKTߟJ_pAbOdRmq_`o~g<[5uCt/avx֘819KWwC[l 6|N~&drjs-<;sccA>#o-F᫵a< ~w]ܓkLўv_2҆ix? -9~+;G- 96|əc=yoy#_OOWozk)}k]{n؏S{ +mywv߳yx7bg33Gn>S:^ש&VZ>lBt8ςvdWsLHޒ=lKhu&"?ZK{hкTݞ!#)9g2yM4Ȼa"^aOp4\\_7W GC[<4v]o9sw3jqຜ.lL-9Tq#"nՎ5a lmk gk-\\V\ܞ q%'_'JÉ}c_UO|j>׻Q๞iW?a#8;)rcB"jFG_ELC=u~ʸooz^}n;bO7;׫c{{p~3+Ys[]DodU=xUN/}Hkh)&>;mov12^{R#zOm`9e3s|L[;9WM3Sh=pXJoئU'a>[%e5H9_ NYA19M^e8Ďr70wi|vu˄!c3HLkbZxml[wߘW#| >@>*繞iW?y<~ ~#~+3~-B>>0r#8?\ɗɟ:I\p~w1OV/.𛹾t15 m~yMI=KHޱCnSve:n+ zDY bcj|Zy]^ bY'ފClkMk_=vw G΄s#ʳxؾc'&>ᅺl_'gFxO(ˬu˻;1f]oJy5nq+ X5ָMw `|~+E_?G/a㷾㿾9FMo]ҋ8;GN)LJE"4c\!{`생bWyq/?Gn#lvvn͙蚽=Zsd|6j[r܄OvkwLk4Ea~nz!{z$*t.u_9Jb޼psH[z kZ> \$&^>/Ԟm{b>$k*o F6Ͽg\c%_ ֕>| O`3?~OK⑾M>2Z{ykG{վ~O-j5[x+޸3 ?5o?999j\<5<^ѯ|&%yJOkg}kߞG]L!v^jN?]-awɕz G\n.]|sKʻӥ&g!}cv E?Zkx4K޻m۶m۶m۶m۶mۻqyj{盛tWW%ެʪg}}6r 燓;iwmo{ Wҕ_QzT<9nv?a3<rc_'D3?8/po=wCc|nxC2#wsӝ4|s?׻vG_CPx}[| _Gw}K_o?(g?xo g9Y&Õ|屽?Yzկ~ux^6%.q8w{x8k8=Lg ?Oӟ9qӜ4ÿWƩO>ǾӝÏ?~q ;=i\>kG{վx<_?GO?W8Ǹ8? Q{@~r"/c ?rGrwr&~x}~=O8} CH/=کD1=Tb/Tb_*G`=bGpa\Op*DN-!8ok^Z -SS^K'>&~g>'v6}8s[tg<޶tskx{߻ƭ!Wjl-t[t>O ]B\p]0O~o}pg Cztox_=vKpW|~cO}S%!}Mo=Av?Ox]7ao=|{;xgҗ43_|#G<`8|5yWcS5}|8< o~9y~<^vx ^0SWcXûw38>o<t }c|"kd'NxG?a;v G9Q#HwܫM~ :׻ў9繞կOmqqqq?>>i_]KA.CNEn}$W$㼏}õѧ/z;@>}7D>wFd5^ggd%̮w.L<+d=E^-#xgKv3GO?_16ĮwAlz_:k n0 &}Cz)17<)}>k^cl Yp{ӛ48&x|e/|oG8OwӞv]ӟF_cl{2_(|X5w=jsD^wrto>{=/#DB=ɡ 4x5|b]Tb7G+%{KN"DF쓝]Smn\Op[KѳMG)\]JnDtc[bG k鮉ϜswAlVػݶ$wя~n"y䐗XEl@+fL;`y<G8(r=:q;J6yZ)rI5~A~'ep͉~խƘO}Gğ]W3WrlBv_:> _6soy[ƘPZNNs댹l:bL]zhoy\Gلȥx+=y_pv1g㳟;9IN25~TMG~w~hO/aD^F~HON>ЋOl^/d\woz#_={0&QL{}Sjwl%|9TWxZp-!xڮ!n"MG֒{ɹW.<Įo~XߚҳFOz$O~rKhmG>WY:v92sMFG;apP1wj.gaBN,"ۭ 3U(vp>b8C^Ĕ0g>YyeF:"e}Wz}ou~hwy~ ~?=˽B>D^F~Hr&or'z@~G ;9,zJ_~sov;j>Ozxv:Uþg>Lŭ_ {P/_xy.!xJt`)mOƶ4[BΟfsǴsO',X+ׯɿdm^~|R.7α~9sdNm]kQlv[5_S5w=6Z b48RTm])7dEaֵu1~`|!L$6K:/q5j^fw}ZE.r1沦)>pyqwxXv6lF'^"{>Qoོ<8JuOz}foP/ >T\>-jss؜ᬃQa(Gz7;kͰ:.qc{3;? [/ĩڔc_9(}e/;⻵Zo}7W&iBC"_ʗɗܴ~ٽO=a|=y"9p1,Ò')7׹}׎}>'/>7?9{ɇȋs/oj2ȝ避SM'zE1uo5owHދ{_w\'vUݱ^~)s#>_Lag%\-rKpum>~D۶7ܞ5ğs˵kgk~T_rgj|#kw,!1P{hɜBN/Xj-౽0rm=u-JSɚ,_?xmuARWtK]j\?yD` apV03k|΋3yFquX4ָ% FTS[SgDnED|_|1C;ji3|>y>:uT57. ]{m9>q5n]:'okoy|~~8Ǹa1nm~ /{ȃ\G_+=<|əɝ}y?>nѷ$=~;}gzMC^ٳ댹EĮ ?l[ےݷ^`k3'-9Z#BlKt ;NU];mnu[}7.%1Jcڧ=!kL*uƕe|IX{0 kXL,[ #>cSܫ <ܯ8LnO+N7YHui8-/m-m@n8{ĹlXQ,/p_Խf3KĹWf<ЦJLXl@+9U Ar|g9)#dwCNʿe O6suw_jZ{yWS[e[} jv_g'%5H}_ ~^;^C x3'[o%pv)o8XCҹI-k)mSöv᭽b߿}Fg/k(rwyj٩ZKg:Mujpɯh  s`=ְ%1Âm-XymWńbZ'+> Xc?$v3|8q=x9|ŇTuH@yVm[WF-~8N|)Ob!xJ;{O`amz}YX~W< oSkŚ:}<3LW=|3sv]{/Oo Ifky'me />799i<ɕ|əɝ}g/2o?N}?zHM?Ww==DvS]b!pcͦp5Ex&2-%8}K5ŏS~1㱷]X%kp↶f HYKiI#k#N|sQ{lW>?[Q ɶ>TkM,u-^Åz>uX5E!Wm}}%Tow>krbMx7]<'_++qGFֹ#`Oyalp8bU H`kvb&OuS1uַF@߭߅|C|y[xF>]>kG{}\/Oo7~ ~6?ȃ\|ȏɓ\ɗɛɟl]1.zDgH_j{k @yM{ vSs}չb챭c췇?~r ͝ IN6F?\$Np6uKh~r}W+mNvZ_KJ|úJꛗ#+陷ъT[svp8kKỺ)[s3מ?*kӐ6)֜8K;x $Τg֏3̵fzF{6E]v=g^ŊPo$_$~Ծ\"rY|βMmq^ nbk>Mw>8D~{uўv;G~7~h ?@B'+9r$Or%_r&orw =s }I =>_̸1ծ/=y{z^6fPm}jqbkٹ=o h9L#6-xd ,Ϲk?s^=rZ[W }Xz fnR799֛hIԠOڤ_Tԫ{t,gɊ~ފ*M뺿^=;|>|۷J|atT92p@~ [ckड़ Wȧk񷼵/Pl_]\xϱU;3_Ǐ9϶_ ߲F#_vg#;yYWgd['~Zu$YO/Oz䇵]<ݣ?a<ƕܲ~ ~#~Ɂs~3Ӫl1/`'oM~Jp 6_'i⹺NT>kM&/tDlk=_NW.Jnz\-lcjĘY Öcb7a/QgO`g, !-1 _+.'|=Rz_*<<۩;gz6]zӮ= b73ϯve[dL=Z~? ?yOVKYxl۳%MŏyݶĶ׵{5 OEs ͗ \˔!UX]KfkizŒMKiT<724?W[y앖K%^z9^,GuaboP%>I}Aagyߌ0KHkp_u ۑSߥb:qLB_=]dq&{\x?O|~KگK}/ԯ]?n|5%#*^|9߾rOҞv9繞/Oo7ixg|p _׽6z!'"7#G$W&w7@/GOk=r\zF]>Ę5>%F}Τ@}ԘGb ;}{7v!#mv=}@b?sBxr6!\\r^5u]?m:?֞ݰ OwF[w|={s.o{g]jÞHm˞&I,CQ8YBp]Ms΋_BrWm;Eb=l; 댕ԵwUl,A.Ր͛klv؟MyN1xœCj<߸:0C+qI{.g#ʋZߕ˴ x.u՞NM=G\U!x79̅֏Z8'/|"ģ㫳f@= JmVui#q~w_2~#~+3~㻸܀<ȅ|){SȏKM䟳-'zD~.N=#Ϥ^׽<^[y<`GY==ZO8N{=P[ܙ!|vO\K.YC s^ye9O6׾<3R(Nܮ}+?A1 \/~|Vg0YLf\{ߧv߀9KZ:>z_/?@~/'wzZ\Qch~/G,4>4^6~||7>>'|r r\r"/r#?א'/97?=3 G)gR3F=LN~z.^9o5GAvRcUG}Y1]ךP-{hmABp 7qy/nz@WK{=Ϟ9bO%?Q__MqR2`yucaBoNN`6x0gs!Rkp6{SKu$hɾMy7uNmN>FSr&~%X:!܃݋;=X#WĶFZfMnl`ּ+e,XM~noz@9?s˾{cw쯭;b\T<ق!82wuH>1$Dx w{) ҥď$߳ cY}Ll"O&Y^އWiW٠-uuN`n6-s\}b1J)ZZZZH-snu5=OyH%|d`VWC/ YogqyԺ&hW~6}O0"ljߑY*b?ͶargEXN=?U g+> Qw >K{&/ټ>;:oj'.ߚ 3 'z91xe; ĥi#k>>1}ўFr oKͼg?>j_ռ<{\GB>D^䦝*Oא3y;@/=/LQά_eMǜUFOkdoz^qV2b'ձ'vžrc;eԫ&8bSkp5 m)lњ=vO&G37Fg7:{lˁm~"caGxad|N>Ƚ_3g@ul pg]~5{s],v:4_څiǽ}w׹}׎2ܬ!VNvSwl|iƝ_܃/O:{k|o|r\ȇG/KMO~z(s_X]8}"y' = C={k\!`){mϼe컷x2UwxM5ߺh|ϻW֐_ƯolϮ=yv][O45j0psk)r5g=Qgo!sMro+iS50qXuo v r6P6=7}26UKa1]kc{` \Sp1WX:mXOgi?[a Әϧ@ ~;^O'k֮KźZ6Ԫ鿸J ̻:Atpq_V%>ދ ?L>oxB.ݏ/|0p1kP#1T?s}w%~hOsk35q͸/Oo?>ȃ\ENEnGIK=ڨA~}W7ӿ+̹OBofFur]g}g7ub_5g=Vb웝'pe!Vi"|:7)*%{[O7~z_y-}l%WڅgrXwM%k#ۧ#7s9sHsMNv־kxWd>ĞjAIvܫUS[~ 0]׈|YM=FCXv r ͍ќ۳d`?\T$6[bCmXXxĩpRįkʺ_~>xN\-HGߓ}|9/,N^⎬+&>O˝k76>f-sv]Ns=_?GO?}fb\3n=~ ~ᛜ>'z6>7?9ȇȋȏɓ\ɗɛɟzA?b#D={#v5^{؃gճ~7gD^} ;T ?å-[]BK}'/~3BGw |m%gh/f97U`94Oz:lY[Bkfi8<^Lӵ;{†}٧Tb;ǁ*u)[`̣?܃]7)gU'>6wO]XBLF}]#K.*&ӹ38ƤbG<(>kR%)ܵ g!j3,z>QmT-ybuʎS3w;ubz+|e|x}ߑ7}p-R:׻ўv;ykC+ͻݗ=(m) %GKgw' !'"7K(/97 =~Bog&@C\709IOߠ)5Aﵑp}&g;3?Tbm}bG[CLS1>¥iwi%8ٞw4Ekyy~A O%N6UY}'XKDn3Ꙛ$1|2zf^ܳ Js}5Mw3uS4) Oc-O)ᔚ"/>:&?Mqq6{{ _;Ӯy?>qq_}/k?wǼ {,Ͼwy y#yf~PM> AO ?>ѫSX;aUD?i|vͩ~}֗]Z߾k'{o[nos;_7mz^q>.F_w&a*Gϯ{P=o朴.k*=R뜯mips9ְ]kl9vz٬4w^/ؔ[j[?{ao&~hk=5Ćsj]3Ksق}ʙGf]=^7yd$oeE`u[]o^- fO}uX`M / ļlhyo#^眩Z>߉<K$]zty?˷ߜ|y=js']K7!'"7{$W3y;@/=q?('7W}o5o:ԑgUО;KМW]cv~^, >}p >~ܙB!<{t}_;zVmv7~.%`^=z=ڶӺ%rjL9ywږ6# O]՞gW.1S}GOHԛƧ"q\w ϧ?3| Skق9pf@;yXPݕgZ*g1x09ޜ&YNy._]/Oo*~s&k_:cOȗMjlh>gP}'zs|?%GO?W8S >`7#~z3~;y y#y&'gA16zB_ ;*3wGzI?3[;كF]؇g'?7B]#;?l' L3\OsyΥE;{' yton6[J!RyMCS$kF X2u%&Nm'\5SG ̙z\~~j^sa.5BbG5JuFw&.q)9H{rfx.ZC|? > &,g>>@.}V[czϵj_vsp}b٬/[!ĥyGF7WaorY+{G?i%C[ڳj5^!k:49ٛOo?9kȋȏɓ\ɗɛɟzA? }?#eGzI?)}\DsAIߑ~>IkD񈽱*+vپ㶦V '{:*ͭ/>Jmڳ/@C 0NϹ58a~{kےn"+Ey|q- k8gNoO<'5%9{lZ!̽0mSf*zk kZm]%[nzgsu5]8mL\F)GK>|Vwj3]@X#g'yb.+)t86OZ+'/W?\9{e21ɞ5kL4wfO=X>&]>x H峏}O4m<#g_1r%!/m*}@B>D^F~HJ9Ñɟ^zB_ Gߧ_;{H/'=/}Ξ]L;yv S읱~q[}/@pT/sLj$;l`-ܥgkϋ}&"K~nC9sɻb6]eOkSףy;umI]Ho!1YvKׂkpzYv ={yx3.k;<[%CvEp^3O,y]]k"x 7өs0f;8r#7SP~'S_mw\AO<{~ yOvOiS[gfz6>/+uB>D^#?r$Oru_%w^zb9O}ڣWy/}wUkgNgz؏F] v^@{){cw5׎3;4׊wN|zXK:%'%Ldy_,k>,y-,!v{Vx-I^۶mcֶm۶gm۶m۳ޝmENmx{o*Oʪ:vw%/y].G'vw7<==Aǯi|oկ#5Or 9бԧ]bݟk7^7Uڵ>нo.p /#}U!yȁG?z׼Ѕ.{^ݣ=w>O7A7|_{?7׽n};q۽E/{^>x Np9usu81fݷIOzRw߼=y*WJ'??}w?o~s׻z'< +^|g/}z׻vx;z=7.uK0Nzғv_wSG~z_?ϻd׾ԧ>uw9q?O!yka{ؾr_ׁ_ycv}{|Ѓ;;!oqw]~x.9=ڡ=ڥ}zO\#}~ԟUgߝOA/~'7蘾yp~q \s.NnM9va/&žT{dUgv]ݳVUy;X*Kݵ\_O|E.Es1 Nāg{?zg//׀n1{^sH~y~_X{ $n.<;v6"9O/ {K\/1ϙ|}gڧ=i?}ݳi=FЇ>wXup8|+Ν/zfz2\?sMPͷ;@ݵaw|/11=}}=yws]{kߵKv~q-ޮc^\KK|< aɛ~](c0Y){Rg? q]x% UsM ğ唬C/v6 ]w]nw|p;t:x;s-lGM ^&71o{KƆ\?QJp"W1$vne( pWG_%."z;igx}c-s/!/| [|/~;Os>׿~.>[=LLx߿/g>NJ8'Ccus'ys>?q?O&Oí8 M_/{8ѻe'|X}wZ9yx.ϗ=Iڭ~q\{^g^臞O'/=ӷc.Nn 3.Nn ~.*aW}٩2& )BڌgcApWSޔM^ //|OW/ nm`aWww;g?oݷ45rSѥK999Y}9qJpN|?q@NN*҉#|-?P>.svWeh\1y #HS`| =|aly'?;wyϹpt}}^-X~1~?sr>;>7;{$9=oijwyw?opԯ[HB?D_FGLN~p/88'/7?&6+j3 oݰvD< bgSUfkgx _x~z#WycV<28$M O<:'./kg{q_v~Nv wl{2U` B"R{׻޵xKF4'gծvu{/&G<$rǫ$㌕+qq7yY'.{|*~=ߩzի^ֻXn^0Dq|syΌ>xG.~-X tqq1(7vJu}yTG"6csiVg\S-~ogw'y_1x S;/TJ=p֓Cp\;>w%v5s\3K \sz^Ή>tvk~E~t~տKwOA/COEoGIKM'\k\''ϒ=|9=݀]&b_,aG){9f̮z^_ lT7xgH_Mm|WdL2~^*ڍ,3ᴵ9nWwa1-ޭ.-)8'ja8~xOG?ѓLjs} _=fojRZGE .e>8L퐚 lfrY.gޕ1=,b@9]}Ý#?>9UzcOt\1q@$nyb85cb>9Bqa|q>ϊ#\CV9>O}jyK=jfrݸoLK`F_YψWW-F?c?$vmyooѥc3>$?߉_vLyMI drکڭ?m%~տYwOA/7='/=7ӿ| o2A}8TS`-U*5 tixGLb9uo|f'ؽ<뼠>%a_g;Ϲ8Ēb"b>>Df/Upu3V9ũZk1ؕOqSP,q3?Ė2fz߅SߑXsWἚppﳿk<81 p3j-S{[Oϡ~s~L^臞^闞 '7><|{μ+/sj P8[[_;YqSn=;fþ9\eA[A+xCd9\|LV?nWaKt.o?D5SsR^/u55W7$8{;B!:?Pg0'b%Ob8= e;/ {E|,y[jEjĵKR?ꏌk'ҿYwO'7GWg?8FfEY .]T~>\+;^;5q v{ǰWv~{fx`X[P;c߹{O㟿kM5v/H_$ox>g݂C xNisڕZWC&~_M%7@#cW\z?ztz_z.\N\n \y >Ij;J~}3>9)vhGk+p#D_୩z.JØO<1)skT3V`uo;7bJvlG}sD]bNp9 rKeKl[l8&sur͐ νxw6NUEO]`uqΉ$~2 _{2Iٰ\+5ٕմgCqM|9!ot|bx-!9N.n=%߀|ecb+8>rzR'g?k${C|W!k%\#<O=ːqd|A7~L mN^臞^闞/\ OjM2g9*-qއ[eA;v~1ǻ5&,;[>jU\L#Csrcd((Tk g(3FWGqN6Í,[m5+.Pcb)\Xl peul>Zxb;Z8&. A,Gg-۬z<|luǧ-kTYs8g\WO]/~5w_c#Du:`O'{wΌ%H?k &p鉾>~陾}N \n|?Y{pp p3 ߹!uH섽dN?;r;co5_XиoV#w2Hisy+kɚx?/]@~`3͒8pH9Ufde}Hɩ"jPLjWcvCcY^CJCBuޛ֪a ꤌQVz{k##k+5Ɗ٪ܝN_%/p1)'.3q 51~T%fUύ[P%\.9j8z|_T1?7&Ο2V3x5F;O>>ܯfܿzwԹ}y6ϙ @&Q۝~1 >~)>>~陾N88r=/8K !<%|)z.cxk/oCζ#ڙ9 p(?˾y?3B2n2&ځu[#<O9g9av]kٺRc)7bxK-aC^8|5KWM]P\֮EioãgO8W_8v}o8wԵ1 {8vĞV-;co5= fc<24FwXuj BNT ~^+WuL~gdaK凷[?;%yv%2 5k很u62׶Qt:w=mkyow?B'o1{Nhk y.f}|;[w"K6J本oqKd߿!IN1{W^-o˼S|/k{e;r.o$>ˍ-⺬%/;wގp"/J<2?]^r=ܭخ[3A[sly&}r|/3?{bqc]Oel1usOI?>vퟺ'<~}'qƁ${/cDKA/COEoG~Wgwx 7GWg.Nfk1<#ϔ\?zv_>bO5hfuv웝k}#!YkhY}fRo"} }S1lj-֎5۳Y:ׂ۟ln~陾>^~s3x;Zwp p p N}d}=e|[ov;6'>?4Fw\v㿬}4&xf)Y;'OOɮs)_*/?u.VϦc[d׵Lv)jJGۯ72" }5j3j_TԨϺ}<8Ž:^uO]oɳgI]/5,֜2g6:jϟ3Ku:!ə:m-3)s]Y{KO?w:?s[OW_;sO_SV1_ʹ}!#ܵY0~;ӏSf ɓd@zz/z?zOz_Hy 1G_Sk-x5 CRߩoA]"S}Vգl;T#Kr+YKvձ1k=[+s{ 5@c{EO3m:>D9H}\2fC5?W*霬x-{wU-~b)$+RkOTc$Mu_}dmI#}+3}{\ $wTqwa֩O8W/3\7;?;G"9an2gco/oN`CkeLk#Z5stDӺݓg5BW[ DBo?"["sC|u戚)z/&okZ[NWkzL{ U;б.c1byԶ^Kֹk9ĮJ.οJb;mE=cǒ`d*%{Q:]kû&?2'MV{@"/?ynϟg׮qڭsstuOY>%='/=7 p/p? Op_p'k±doM8w}\k;MĮ2_:]*J""[ZkxZ3fӊv>%J1bɚKvyujSƝtjoDv?x*uHmҮe^k<> 'PIE%+{1]1gh|{3m Zی_[eΫ61{Z?f5Ϝˋ?M^Qkgd]W5ίs[>&Cs2נDj bRڣ]ڗ\B%JO|֏S_=3*>5^闞.7G;N5ceϙo?s뜝vό6]Zp(?{X-GC<4&sssCKS!OX՘Fs߭Y ?u^s.̭!Պ1Eq%/Ck¾ֆo֞={(_^n#"b/ϫ6J; y]U~ܱuYS˴@ct|~ןg 5~h޶#q|XW/Łɨv jq\<ƁeNW7i6sܲ6~~zf9׹xߞ%1%R13cW1i[O_3_~艾ͳd z_zoz888'/ op~ O8py Rvuﴋ]%:O;7 9{oㅡu>ϜolÃsu䞷O?+/[Pwow0$KP*Nq%{*=vYS-SkSP?x g2?\>gW߀[_I\ܔ(1vX1F]!\'ZG7wrz>CܜnܨFM3ŏJg~/2NIωcٿZZk96|r;1o[_纥ޞd͝v*[P}v^ea[?d Y30{g=G)3G#LNpp#x+3>^>%,Y9`3aWyGnvW#;1u5F9{og~m+x W;kO?뗎g<]FnwR Vv]ʖVx,6}oh݊)1?{qE|)Y^1 sBmrC ڤf^&嫰it}y0I<iovOڣ,RL&36+6˵P$NT_k\ojuC`r{%?%G=y7姴3)1Y+ <5|C~9nvS?5ڼpPoc2Vf:/!knB??;E|L^É>~陾]>^~op3Ş.S_siu^"}/W[~k>k-<;gmSm 'C\:i ckh_Ãu!Y xz麴#owkǬDŽnsY*[L\ ;ٺ-YS#ηv?%΃0&;|Hθ\jk)dO>[E"b1ԵC̄##Esf/Sm97S !1. ;swjxgWfl1 c/ϯJ^Ye4~ߨ;/)w#>+KsrٛxJ+sKn=4Ie;7zߌI} x~乵[nX Os҃B?D_FHJ~oz888' wY}W_m~zv>oձ'vUso!s>/;{;967\)Cc~Oqx=>3i=ڽx9-kgOsk叉W㓽痊1Az[ߊ#Zb܏An>C9$,1ܧ('պEw |v39WmMgx64O { yU5q+fζ1`yp<>~7>-|_Sq I.kSS[mǯ]woHO}}$$Y@;kC~;ҟѿY>=='/=7ӿ.Nn βtz;>¯竵u?gvZU3&uv>k>g);\*xahc6A̧_\GsL$~jɌ-g{.Y?}˗%9b眺&N]k' mVirݺP=6ϕ](}}ڙ6?'}>~տY-5B?D_FHJKNpp8'/87׀?8GO8u /3\8ONS.\7q6ɻ~slvcˎ+9{^#^{ 7AyKW}?!ѽNxq?K__=chG;խn=:ґ}#O~򓻻{?tCoy{޽o~]WuZ׺s윟k<α?ݍo|3α|;ݿMoz>Omw߼'?=ٟgN|ݭ-nq-oyKasF7Q׾==a^Wuγ}{_w[ߺ=t;ux`g<җ=я{= Oe/۟ozӛ|'=i;zԣvw?gxG?zwC׿?O(G~\׸>9g9!V ty)>ܾC{M>kGx}ڙ6!| ]|'r!r"/r#?r$Or%_%or'8 \{#\':n?=@} 7sW}_^OzzN}ܝϞ+|v=j_=Ʋ {c=eWm}&NG?Ɇ}>7TZ;獽Gi)կ޽r:9Ϲ/;O}{Ouc!"L[Ɯ=}bHق ^{FOkM ?.uKu8!vr\xmns)Oy7 5q"=_U}C?}{m׽u{=.ֽe/w6կ~uoSG/^'oӟ{=^җX`v]WE.}NqSt?up#?tc?eok??ƿvwt{w~=|rk'v[j9mGڡ=E#W{s;<ȅ|ȉȍȑ<ɕ|əɝ]pp/p?pOp_p;ܹ'+w1D/gW{*sN+ ;žW v=!bAp]#8`% Xkw޾O~?H^Wjݒ>o<rg{{WBH aoƦ[K⇋^>~ۈI*WA}ş WBoCg>;7Xl,oAzPw޷̖]gЅowCo[Y[я~~k^{S o}[ݽuiO{Zo'|ޟ][Ob>N; NN&v */*AN|v𕮁Ƨx|G껡ڗp#~G|?O|_%Ʌ|ȉȍȑ<ɕ|NNppp/p?pOp781^~yO ћ?}W+_Be%v}haOC髶ٱ1bӈo3x#mz $[/t9iV]'wӝn=puc{\F_}@^g:әFyӟM|]7m|S7vN.vO{n-ҕԽ]9=vu_噈-Y$/zMnr/|νַv׿3 }_2m=Gwc+<;cv ~BNev_G>ro;Gxt"͓ؗԾş&x?4`;X?R?s@+iSڭ\##~+3~' !'"7#G$W%g&wwx x#x+p}Gp ڟ<k^Џ$UWo+DjSC7 s󪫕dVweر1bA"]#g%M0! ㍴G}ݐ?MײAtr '?}C}!b_=칯{~w}g;Fϑls!Ʈj/+-՘"޸OoAIΕO1>6>ǀwOGq[\ogAySsq/?%ӌk͸Aj|vgH<"9RY:sy|ǝ_?|O|E,r \r"/#?r$Or%_r&or'8p= \w ?Gۻ;zHECnzZ\K!zNgUbGؓ+;4/<#bSvugvkw^K"wx#mPkQvCw{ӵpO|bҏ#bSNt͞KM?#[_>ԧzSob7olK,mdoj?\'v3׋ `5yMVyg[5Nu%,wEF3;9ƀrWv?籯6#ȞZ2'>q.$&e[r~]~3s;%66T[~٧js s>Ǵ[}+ \^@>D^]ȑ<ɕ|əɝ>^~ 3!<|)-V_}':I$]*}zq~|^u'J~5.-#IX _?vks>ө'5;c9}A.qݐ\Vҿ_KQˢߧg ;fK Frxj\}q1k7ّVjRK^-!Et5>J~ x|"%gȍt\*la MlWeJSo4|<b$Yz񁭏}cxv[s#SƧ;|ydu\y76w}#oGߓ_~85/ܧj ԾBkl]F_h(^H-m%zNS8s!m"تukE̾û%l:׹[hingԧ!zHrzK̑|,mv|>&һ!zz>:FHRe^7~7avJ]J<lܮ1@qT"ʏ㲇*GiV'/!cOuSq?>{ͪLV]g;J4U\N1y`۞rR0rڑ:{O-c^|?pm߮w8$VsX|+~}{~{vi{ivk݇?ȼt-z&~f 3B>D^F~HJLNp> {\%Ά?8GO8W_8v9'|nzy]^/o=@ms|J{2w/CqHc5(}, 7[shR㍴'ؚDV26wE1{jH|Y%C1FnnK |9Z:m9B` vqxo|c?/%NBj,ձ<`Y 󑼇xHV;ewIM؍|Praگn#El[hVL'>c qvn_KMFދ.v1)q(q/N^X9ϟC#yk܏;K@mސo[o7?999'o}3\\[j>;-1<5|{/x~䵐k Am^ѻZD/3644gh\ĎUgء!"/k#)bGӡzgYZG@\BDG:O}S6Bn.-aϾ,FJ?tܓy8CdLP1Fl獭Q0mfl}veйiNvz"cWwtqqJM<MnEs:Zd~~moorb9jmVe;_l Fcݏcq6VZL¦=pmXb9cLuj>SYo (FL{й'9u{$Gc>~luퟺ&QI^?ORڭg#/|>'ϙOB>D^F~HJLNp Sgwr>\:~ ׹DJNC+ mc zN+W_[W;~SĎ[yRrO~`,ݮ#uVax#mtúPsuKhvR+ZJl[bKt#{mʺ"̃jX:OsCRӍHȊ`dj&xcMS1M̕~}[5gJjkǓ;+r{d#6'?%^|3'uŖ'۳P Ǐq|Zj='>4}ygO \xè^/y>Ky]'5~کu_“3c>i?g>&_E^F~HJL^q9"p/p?pTqZ2G1,sx{xD//&JNѻ:Ͻ+DxA1Dz[Cx Ďd ϖr~uE[S钱ZyM-?U=F4B䆿F埌IR{m!fY;f w\یx^ Uc;kIC4/6~C}|6HL}bZX\]e}l1NZ6_xM7.jSx)g~/dfbF؍?m%YGq^1_ʚ2Zr?ի6v[_f-v]r{ZSkkh&O&wv~d8>/>w}B>zr#?r$Or%_r&or'888'53_.3^~/7}p/6gO]n[wh(7 CKS3Bc5ȝuM꘶P p_7&os7T^T|jS[ZϠ89)[-OC5{:Zj#CjM=l6C0\n0u1޵$mGdۙ|ky~tp,L=;ǰb<Gܦ}Փi]|:6q}V]ɟ9n>yߘZZ9gz04G ϙ]\Ic^O}'#~kӇ" !'"7#G$W~χ8I~} ޲vOƣ2ny?^dx^B]Wɢ7ck%]ZG٩X 3~MҮ12D({dֵ~ynM{,jvC/?d]3j$90[hΏ-`ӀHo:AccPAuOm.=y;^kVlۙ|cU܆=CUR5ș>g/bثswun Z#bq?b޶稛u9_lܭ| JGU[l=@Դ ChWHn;o=}3?@xbBtrwsM1>|uaǏ^_;R߯}ɼd w|_|N;ȃ\ȇ+49'/97?\ O|;\y87eƋn'95Wч9ڹ7_P׽z#^!u-|M{%6. Oպ;;4Dծ3P[+eOFc>Kij)Zo"n/y]mZ٩ sZf)+g=5`j%Ud1??@ۻUA-(~Ni2y];ުQxn ӹxx7:k&rόd3?_5\?Hm`.y|ɓd-QDǝ_Z% !'"7#G+w'wVx 7'/8pK[uo8gf>O[gԜiccRާojV5[Qk/F1b85o5' _M~mo|f|{9|fOM|IƧ?yxu,'ߓ #r<2Fk}]'}a$k3ЮqY)/|3o/9B>D^F~<;3y;<| ?pOpe<ScXDz GH?d~xo&zN?F=roc;_$`ڱDɺycn]JݳtR˱nݬy_凷CmpNfz8s<߷Z##}Щ,*{+_]Z–B6k{37%\J֌ukZ3-9P ߚ5~ԣ'UĶ;9-=}x8}/Yj?32' !'"7#G$W%g&wx &st/gi3|)-±5|ùsOο'%k#5}sX:{0OԼ`ayCD;8ES>/چ9oj%=ךCJ/?ux6o@t>{QJ]K[b46w)%%sh Y?d?qGy;kk44F6Pm:z=l!;ysJ_Q\@k6V)vwONQ=]eFY{\GyYclyrlMƪ3̖5ʿ˅e=6Sn%o& g,oj霛57\Kj6~g}1\juus6ίOo3-5ڗg'7G׹flo% 7ɏɓ\ɗ3<| |Y#\'«_8X|s'-}HЗ#}u}^V[o`G2g.o*AccGs;als>dv!Zm oߵk0>X?t`OnjMJtYRk1Eb$6e s/cc+c{oʅWrkk3dMul:m6'fpsCf]gy]jj#m#"u[M|(Nؐ>,1&_7>ez񥜯99W׳wY.1pr_wm|,ۛsMre >s۽>úR'Ru˞$Q=ouyڃݙZmn 湵zӗ#v}%5`?, v9nn}%7KE5!}~k#.k?O}K䮮eh- A%<56&ۺتJTέ5Y[-"s8ض:W喧{b5V.6cjܳI-WeX4V{#.su5q;[ja1uƅM`ܗfuͺuF{' ne;qze-Ը {ۧ>0~3sSߗ~]Nd_=3VQ c2{^yɵhw#<>_QL>@B>D^F~HJLNp:~ .fN:nO^SfzDB{DV0kT+ڂ+Mͻ&;vkjC蒽fey.Z|os~kiܷBڹcV:QSĆK%dLK:n[-~e5'yKcs}NuFu-LclCm39Cz(룅Irf3[g66U^yo` K_b)R}vҹr?^!&b<s̺b??2rl98zSΜZ?7ڈRXZag+gߌ?.{d~@bI#Hg:܂'_9;K.^|K ϘAژ;~ 1$͕<ȅ|ȉȍȑ<ɕ|ə=.Nn'y쌧yC>p~p p p -=Y[h?޿:!zH657/w؏Bc9Nb('MOd}Nu}̶?߬oWsr[i ݼ-i"U?іvGm}Krq%Lj:>$&1og&A-vmkܜv܁ePQ滘4w Nl]懊MS4~cvrx^TeMʖ[0}f7ه8_>nI|%3fU-qOs[|qV\칥Į0p!>@gٛyu{ׯuu]εo52>J3o]G2V>O!P#mk Oo}g3~;g/Rr!r"/r#?#Or%_r&oLppp/\W0cg>éw:p pw8,ӃM?\X?LS3zH,44n5[_u<1Tؾ577dGӾd>u%vZ`v;ύY@ڵnh<=w[b_Ί/)Vb|shvLmh׽rKRrYV3Qb~c(8G~ɟؑjsp^Eff9P1)+6{)c݋-v}~jJ|_>čYxI9򉩿|q8/9O]ꜿO^>'ȺzGg]C9?r\B})$561~|||/|@9Yȃ\ȇ\KnGCK<{ <e 1k)­{i;<.ЃC?Ir@?g1eSc!zM[{m؏؃awjݚ"ne?%򹽈[:|ɞSϭݯnI3A|zVk)>j-{xnJs{_#vM}=*C?5׮D]m@%k줹C?['9&ػk6Nթ{@uzYD^-lz&oKnrXT+c }ts]P6Z.ؚ!bO &we;~xkifԺ#?\zWԚ~%$SdL]'쫗|rܿ uc,C{S_ ߜ"?~;y|ȉȍȑ<Ó3yLppd) .^5^4u7zC"Ej:zA/w}3߭Of뵕aw2緥v`ݛ۷l/%{ TںPj sk(#GOfƠY:2E`lյК5ءwR5>CKrY8[%Wc/z|fj=k%9猥O!gms~Bvzɲ2@- ~ѱڣ#{,?~rϗ1ƂJr_{QrS>W,(Sή#9'1nrdl@jS#W A@z201O]'u݂ױ̃N@{[=?{~sb?#9)r"/r#?r$Or%_r&or'8.Nn|#x+3x;Kt/8W_8gFpqo|zvŇ?zX?s;%O=g i:(S{9Fމݛovt ]Sa) t,,h7^d>Rq*x\Kka6Fkє&C{h7Zb/۩(ilP,jk"k9]KSݮ<[aMryuONCe ]$͏v_rcbsF;fcm_z+t ?$Fw#{X ̯_~ښ%{?ZObbO hj%n#?w3F.vRG16XFknusS}9!9z~~|ɘ6rO|_|o|r r!EnGIKMNn;/JNpp [mhk3_5Y/ܷ#j,Z ׺_nG= }=m%jc`e!a3y ՜g֬ou+i:;Zǃ>ҹ-=XZJk֖FlO|-@oʿé9[ahn֗d+IT]v{ Ɣj> gG|Đ\qͬ-֮jў؟<- {*JP,Vq/Ͷ s⵬$wdC5p1{Eak_=S} ]brmb?>F,)j>8UqK?51sġ o3?{㿘X,'nN<ɵLJ'~SEf>kg.u/2 {zeH{kv:_#u~|p pZc[?c~d v zE"uzJ_k}9}Z`? v6*M;иcdSҹh--2?u[|[B.,2bdrs:d7fn\eڱՒscOaWvNyI L-n!|G~3zl"z9OyMظ+=dQ+1HnMMvw=oA;űb>vqEq!{Ϙd~gڟ .`XV"~}"G1mriky߬ Y=5t:gw\qVj<76y8vA=kd=O^~s Rw|o|HB>D^F~䘹&KM7x ς 7GWw<&x[|s:; zA?7'BYȽa:''v2qX ;;^{Bp~N#B1\,]~5S nimF`reb׎m1c27EBc:ESsC01؏/o #6غl;T]SԹj%vGd P]*{xo٘g{OtDLfl9lqUeIǦ1=֍q."Nʞ/c{qp![Se^^w@bt6/tHLA! w Ӈ:u]z:|}!hGDt>jvg?DSj/w |^PA.CNu<<ɕ|əKppp/p?pp_po18ړ g='~k!zkcuӌ\7:Q\;2~ؠا%Mb7{O vO)bjq~[֐s [{oq1d*钾~RCVm`s;RcԮj9UA'q374>C qIkGQbL]9o)q2@FU g]%K.5s-7\rޅ=5?mzl>bŅϽ>-<~+{tx}(׳y|ȁo>6~џ{Y6yW^6y=k#~ҹW9G&<%O]cqq?udM}\s#qrGM'z>>7?9'9eג+3ypp>p/p?pOppVqٽSp pdO/_}u<۹G@w =msY!rUb/؍ؒԦC}+vIbػv .=Nn 1ޕ=U틵G?Ak>EjP'%D't`)26!lIrSTuAO4][W\v Vka1Rl3$c"GjP]<+Zcw6_Hswװ9~]'+&>,L^=ce3.~ηceyY?{%Ϛ샼08_̫cȺp7'y}QI;\E f[YG8sڡ=ڥ}کSC;d9;9_Ʃȕ|.??\dn <|%vCxKmSxud]캾6}Ǡ"kzCxK;WQ ]L*M+?{FN{W&9;-#[|)Q}+-CghWKSo\M mOњ6O WgbvoN=֗?׏obm]иb/eOGn>v,&{d I~z7\j}ၸ5lŁރ]a+łl\%lIݲe簥~ǧ^)X\n\޴jQˍ!g5&>`,5b8N.xz][)(NW7A3?g5]>g>M bWkhi{ս ;?|O|_|LA.~#'"7$Șɝup~q\k.NnY ]>6}v^D1:hȖcܮM"vx^U=c9kkBkքY.khȖ-VK͏ֶy&-;w/Xx3vg:әۯ'>s=ihG>t>ӟ.vuwS{{oIOz>_t׿ծ/ٹvK\{+^/vu~w~_^t?wʹuֻ.;}?Onw?s}{ݫ׿޽/Mn}}խ(3'=iwܦ2Lw;ݩ퓟P:Ts?_ݡ}Nu8!?;<:Ё;GkD#v@섽U;crBʞ[bhI[ |[x5Fpޅ`E8 /&#py){?)oܶN]wKGo/s/9i~=ά/| Yzǖa{D_/}K=^}p;=EK>}k+]JE/zZo~-/{8\閷>/!x7_k]Z;xW;9/~@e/1G}nǰz׻^o0 r#?r$Or%_r&or'z@'E9C_L H/'=_D蹺b=_쁽dΎ]o| K%v̞+!g- x7Z3 -n5pu0k'}21懣,.✟=qi0/~q?s.X ?齮qlc.|cnC|{>>){?_x'L_u' IMnGIKMOC/=/#D=oN%_ωeb'%ĎS)}Vb=Wb'<#}ScMt#Wk38\Fs#ZCdɿM_ߒچ*{ZV߶ƌ&k,`y{ޝ2~Ӟd;.l=$8F0-[jjH]1{0|~^O>/|awp>_Wcl/y&MoڷVo|{.rY9eWz׻v|#mB)^Usp\|!_ڡ>{? oq[\BsbC)FG 7 }'`&yv~kvb7 #`7N!/9RCkbxrUVi_On^9S=>5.NS[գOoOA."7#G$W%g&w^zB_O9>+gGzI?)}U/^L>Iyz? 򬱿r¥.uޞ;0t;]<#_a<_<;c|`?+vɺnYNf_67D_'?{sv㍼6vxˏf~?{'3\Q_7不L^oぺn'm/z}\G}Uvhvivjv~dB?~=_|O/>7?999g߮?=P.7zB_ GާWgG=~j7}gK odn{VPwxÎSo[{dm<~qKݷ4_.'85F )N~'p^g-Mי)v1DC֍1O&dvSjsgsĶa_g8C$ ;NOWK⊡}H{`%/y)9qm*;;F;Q($~ =f6  ਱9-E0= \^1X'y^q\E؎X%\i ̕'ęKڧ/|<Ӛ'M'WmY =k,~_ӹ'uƗTW)W63GZ>?ū^u ])WQzկڣ]ڧګگ.Ϭ;T>7GW<~;y9" OruɛɟzA? G^eLW}LfmuO5ZW|VW 􅝄;s,{duM)emݷ3}pEYcsp >M&)OmsD0<:&tӇkP;VzSo]?}߿m]i15ۮ'l?o3ʓWɟr<1}{ag0x7qr`|r֘-bA+փl3GP.Y_us0[y<9G&q;k||o EeDـ{G~~)WNN_ڑڗ?yO:կL'Kg_]gCB>D^F~+ W&z@'zDgʡwx@#4k跾wzObCy!@?-?.+[ˎ+sJN "ˡiwQ~c~D['o'Zx _l|#]p2sRy~A Oz)kC9j\Myx, _1$_9>Kkϔy`Qx#'~#{M/O>< &[~yC|,z<զO<<=+'(_=SC{KoV~g-~/Oo|,O|U7>7?999'/97?=~V G^/zFܧڃRwߜ@7.RaG!їJ=l^m]Gȋk2Dx \C%6к1px)%Xws99c !9Jȟ>0Zcz\%:!/OځoǯkC@8ȍȑ<ɕ|əG@U?Go'zE}s^% 37u.]v+U䌍+m1fC{\EK]Gc>ɩ>z .ȃ\ȇȋȏɓ\ɗɛ=GzR'z$S=^f*}^cuG=$?N؋=} )oS>'@tCڄCg9=~({W1#.KXia홦-qm [?Ɵ.%~%/ծ?YJưbcXL)'֐AKƏ-ݺԼǵb ?ڭ؏1Zؿ,ހ&?Dӕk3%(dYa ?.}KLrfdf,7ھpe[==gCGa&|xW(S̶WijQCo}k}1ͷjvg>*s?>517囕+vI9g.u=yy_9xA=ـYJ9ګ(7~?>x_s4Cor!r"/ɏS[ȗɛɟh=R_L_]G'=}NNAPd/ƻ9]JuAv;^< CGؙ=1gpm*ǥgh35#vͯ!_Ưoljt ۜЮs!l|/B2<_cT~ͭQ=s!3*hI K0D#f^ G)j׾yx&\%'s)!y88 oګE_ų9CI~n^Xhleݗ\5c>{̇?#r"1k|q֓Cs=kpSF-V!|0>V**5s|</g>Upxg^{97e|'?qI!93/e$^~|OU'/O7GW_.=G9ÑȋܔSr&or'z@'ETg?zH3GO+^_zLug!uv^>ĮWA!v^y|v;3Qi ChلC4MCT _Bp>({wr^lwܘe kϸ]rƱ;@NzqۢOCIl޺˵q׺zR_3ύ-liNؠ:;BmCs1O]gn" s}x}ŇC8X̳ &ʵ Kc}M0Zrd96[[oܳxP6]p!^,Uw K"ɚI>G(o̓W|%O/lHQ_]Oa39/H_W-&\ͳzH_9SnZ~xG;W_?GO?W_  u?999{K%wחL zDf#Oz2qozNQ;u7{d bgsv^[U<wU&]1gsspr\5ď'Sφ96c^Ȇ\H[c^תrys#FkԒ<&kVqĒ39=sSDpbhn(co^)8sv`OrmNauVHb */Yj\Ok*{s/2_Ssb9x} ,wͅZ'yyO6+SܴcX˧?!x9kĻq[f0Q`xE>h?LI7d=g '6Ϛw=9sSU+hW^~?_||7>>'~'! yy<ɕ|əɝ}~CS3E=Ln~7{8ok=KDAq생54Cgi6?Orљrg9|Z/4|WaJKue-Yd^]oJmn04?w|Y=$ wcebO~s!7,9"^~C\og#8N~˷a=M1"ܜQ?^<;)[Z f\|9CKsT|ᓸ~2Zw$|^mO5?Ǚ|C8aoz>3?Yg9u|y&WӞ#Nn?_ty yF~Horv/{}#sJ~); K^O߉W[ {S@]`u}S;?CuMbKpl7s^KsN~ki9/C䛈sodo]ڢn$7ߔ4gLcJG۞Wi9-7Y9=!2k)Yt`92'X!˻M',#kD<%&'jEʆmu|_c,X2s$rsկtN (+4VDK*f ~#PY|XETs:ax^^669CxH\7-1;w9d_+z_ڳ%>Q{|`ŧ(e(|)ڣ]ڧګگ_~ _*A.CNے_Ϋ73y;@/GMo=OMԃC}Խxpsޫ׺?|loWI띛Dd\9S=ҶWg0̮ ;'S2} œ}$f70 4N}Ooל Z][`u5SkԬ7;ʱXWs˅Mpd!xa~uu. em ]o:8r&&ߐghϵ.b)gbE=g`Lo=8ڃf/'76'wL`ib+woϲ;abNușd:{?s׳_9c=w2<*?*P3.Nn)Oo| _׻\<y#yLNA } GޣWgE#}}YO}=]_b ;sܐO֌v=CK 'O]ؒ1x6EKG#K^\J?}Ώi_ȅNKį =r K}s}kX|%ki?-fہ0N=7VM"yRu/:ڵO1I\ˬ1qxV~0 gPsrY3oMxD>1f[ຼM':'s'ĕRB֪w؋rV+9ZqMk=qumlF 탻M\g[| +w9||9W׹2ϟs{v_yUzԧGO;=y>>>w/>+3~;MiCNEnGIK!wW}>AOIo=u/zF]2+>WQ]ԵJLn#dgv]z3+X $9ph_YxS1]({6%#_5Ԏ ^s7~Ӹ0&-!ir"cx9s0h^w ђBr!Mx'>SƫƋEK$o r5;Ц]#2mF ,ok1] |7w [S '\?Z ?6'-ʡ0 }9TO8;{[EYo KKMw/?gka6׌ϰ/LM&+Z&gc.\ST=ڥ}کڭ?y_||G7u?5|o|r zsh'7#G|əɝ}C ?YH=o?!}tf%=vo b v~^|ӟ|G;=_c^aVp͵4vh5{fВ} ScV__7=7o"NǗ~*$dÈOڍ2E-85{FRrޞ߸ 6Ʀ&3sm,!*.9eʯyFx 3`A91=35j-GߍzO~Z{V Q ؐ0L!i͕bFrω1.PNY^X3-:<lrd/nD:E.0? =`iy<産|57k7Gu9+ _=OA.g?yH97{z^zB_N)^/zщ\.K޺v]>jkݴf_, {n㺞3ɹoGrs\#rc뚇gso;gki.>[Cs_܄vZW1Xײf(di 17k|4,tK+յ.I$ W"G r V5ֈ=Rᄗ֎\mꗽb|F)y9^>O<9|=o msXkW|QC$n"ϒ{ q&n` q$Q^bBr?0?>|K~3Zԫ~>^~~~{'7e#~k@B>ido#ykț߳x<C2G>;һ4?|Ov3B\'ĮW{);dݲ!z^>3g'J}E'o_8LsGea*{<2[I>^[#[--kƿmxS6_9X5%.~_~8F,XZ O˹&5Wv};/<*_=[vi_vk:GO?W_}'ei~+3~;<ȅ|x5)<ɕ|əɝ}'{?gd!=CF!}T6 x~o\ Oߡ {g߁p3G7)[K^=g!/mmzmJ)M.bvO}o-mMޖskHNX,\ίDAN=)֢CSCI{\ָvЎj焗u\1gok9e ܫg?x^ v3W&V<\sb/yQ#/˶,9ENzi?֯k<..3nUaukBt:(ͳI1ͫauhQ3ʪ2^@yUzrC{}کڭ?_|||/|?|O|_|oIA.CN q=9f.|9ƻ> AOw'zw7z3F#>qJo#5F[_R.k}9vdC֐{Z ^u{pL'[;%4w.JK.YB6Я+Gv»Ak=D\f]3ʮ-%B)59{Ha9EKlɳC WC }yh_d ~ņ X>K;-YO \ܬQlY?jʩob7#ݳJ|ȯZjP.'?>~C7moz>㉉+#O'䚵AL(փ؞O,=ϷzueY#6b@3p}y{~!座~șkjgCKV>Oo}OA.-Eno86/97?="wCS3vJo>]nzz&y=vZ^-mI\Bp#ٸ 8~-5Ihhr6_Sg:7y 9oz7=/b1oW g7G9m -!ۚ#sd?&*'x )Vcw,[#c; ˭V&3\~/yDk&23Paza08;9wϋE< &_x|| & bʔϥ_[QĤb]w+U->\7<揽yܪsũֵ_fe^l/ͷ*{:Pyr\G}Uvhvivjo~z^/OoO]!'"|'g/9OO$ʤ/ڀ}Wy>>fGKr^Z_mS~kht<9_`l8s6;07s\)gv,!9zɟɗg`pXbMӌ:KNΖF,g$.2IiS|"W^ Y,pLvg> _/op_(7_Xo౹78_&pN~sѵ|dz qWֈQo|yK@?Tl]1?orӵ;gSrWڡ=ivjvk~~~~+_D]n|r r!r"/rS9#_r&or'z@;7Gfӳ|_zL8k}֯ @-7C|l>qLQ_vW=c2jl~KAڽs_w6!r%m֮õ-/< y 5yn ވ/6;193']w%KT? l7 HNoJ쿯D'm-fɝ!uBu- |c9\(Z,^r%7f؝Yrr$KxlNusyh\ŗģ| *HtO̱mR>X*&3<#'%K,FWŨWn.?ߙ?%z6[Wzկڣ]ڧګ9CKS[? <.99f~L MMOd1&<=C}{ꢧz\ޥއ;j烗x2}='-i#|3xCu06.i M/P k{%9 ߴ= }Oc_Bt.}Gh/5w)jܹ6?;k{ꉶG5/;c]փԩ~«31< sv( kWkhYYŏ56wV#θMku3#_*dcNYř7ȗŧWb~:7~?0>Nosg|y ؄d]7A9Sգ>ԟhij~ ~z?Ɂ<ȅ|ȉȍȑ<ON>ЋzNUs-M/Y}2U)O;C߫] tcrvu,>iĕȚ}&ϵ×?|Gp >%X&kI6=Omݠ?9Ϛ&{c,߄֜WIn 瓝,9 H~n"dΘm;ڱ\&}<(,m6JYK/[ɾ/qD.Z؞fǵ9ɵG3;|;d=8$bqw)s_[sٻk͟l9xXĔ(X^Ş|^VU,ܷVNF(^`l)gZNJ|Ow]{6t__?'}O||¯GWgw' !>9z\ɗ7~g=}&zDjy'w^ֱ<ٗC7=`]Tb7ٳP /[{ϱ=kzh̀~'W6DמeSq.'8ն%x(<]s K*=_%v3wZ8&y&sqI 95oG;b 8dq:2)Gз:Bsv57p8~h#6[R _̋fgopRT&\c\˷pńu<<SP+i-ukxs+Y1 lǚwůr#+?T',;!19j6eWdfj+I5|پ~`y{W\S~Β]T~ r ?ݳ@B>D^F~'OrU'9;z@,}7ݣO9ÒPCXcgJozM盹djhg;Άk.؞=[hin-9!O *n̂!4%i'2dz S՚oȏR6#~Ξ5-%kڵҶPk)}kY{yDk>oG™!k̇;X2u[bcgL]6aHmc-m>y`~߂ ?"|^*x%s7wK>۾5? W췼]q9dۈylr2|;`uV|55d{>ߐΝ[qg*_3?W'Gzm;0tֽ?10_"Θ-)WQ{.NnѯZ/OoWgwyɉQUə7}Y=Jm31FRLx-;ol۶7m6ضm۶^U77Nou9gNRWRI~L9AmmnsiO{V~M;nw)Oy}cOpӅ/|~DE/zQӓg}򓟜.wMկsF7Q'Ur򜚎p#L/| +^{}G>׿g{|~3wegӟ<1Mnr'8M-?;юV nӝ|oϮ{N׿ԣu׽U8>|~;q:ґ47'NO}SӉO|ݍo|p;\_WM n򗿜^җN~7MWk^~׸t\f:?7_|ӥ.u@|3~~_>ϖ}kxsғt:O>owv5`x#a{鐇+>3~:\g>S0O1 ^/Oe;bsŧ^Vc`'Ƭ=ߟlg>N3zֳN<9o]|./^||flϿ{;׹}kG{վ~~_xx?_BB>D^F~#O97y O}AC5gcD?v>؉qĮWMeMx2.%"xZ2>Ȼ5ۄOvmG?\:0ovw̦ZKe7ͫ1e-:)NQ׻J?`=o@&x{+]Z`쳞b-0V\ֺW,՚|[*ѧ>=>ۙ[q{׿|.6Mƞɵb FNw*C, ŷ}or?7\לNx:׹N+G*Ї>T|.v WBi'>Qγr{<_t;]-m߇??}:ֱ5(m|Pq\-uw=<=i_?GO?W8Ǹ8׸C[OGW&~{y>y#y3ykNBoB蓶CAR[4;w`Lɘ|_;dc"z?#Dt}p~5Zќm'~jV/bWl o.rh>W׾vG0 %%6A򘈍7,cmv'?}P#Xή^t17TS?d0ԧ>^߽/.$$/}K{\o~/%/MRu/>!))IOzED||F]+f|]-X>*d,/ :G g>%O -{^W֘)y"pG?zmɓ?<ϾxJE"0=>}繞iW?y<~~ ~#~+3~㻿7 "7\DKKO =9%zF]>:Mq!z]z]vbA=; †/;ngpG 78 C&Dpz|6;d, ~^ R.fb#l *Y9\X`-=+WDw݊}^Z*vt3{AÈe|_ 3G0.o~bO>?[[$zc<{Aq8.a@='k~{ַ}OyG=Qm\w|֞b- ]W/?^@qY=eW ܔyX[ GYzsస?<9ts _ ـ?,?։i?>s߻s3=_;n~~~qqqq?> _ȃ\ȇȋ'z."orwMZ ?&7Bг]d\ozL.;a/#;leϚؽb?b.#8\QRk>- ل2Ocnk2ϗ062Z SF279B533%KYZAsju7=N)>ʱw&c?k}q 6?`b^ s~^7|.K>E~nbP\j:xgP$[F|]z钗d{^Y;||5n1G;0?&QȯiuX3};Qbf{aVn#./=iNS7 Jǎ7(G)~OO_n~<|hO/Oo71.3N5n'7G׸Ɂ<ȅ|ȉ #yF,EM~hg+:H/+7=+E`G%ݱnWvȋg۬ n\ ._'C[~?=9]/Oo׆ix? -j_=ߣv y1nQB}1_1ODM!Wz^oz Ch=4|cw1G7{g=pr\ Y"x 7O)\<]>oG ?]7CGIxWOl[v6d]X%[ֺjKaܡu~ޜ؃6Bljv?HNƺdA:ּI'*W[[ zDl _ ժ1ԞhLt=3lx1O#gZ)&3\G$n?ěA^r{?#.v#(m҉/Kg3uXV^8KwcutO|5;#/N˷?LWo<׻๞iW?x 3T@"7)j<Ї zB_7'z<ѿOL^qVnÎؓ񄎱7vu씽uL\{ymMnp%gKJ&xY1wtұ gNbTK緕C/}A|"`\~-#5+7%:mGD8`%17g+{$GwH RkC3ՀD5Hu8{y^%]kna-Ϊz"l5<,k_To1/&~~~qqqq?>봁oϑ;}%wkK~ȳ5Y&w򏽴~Г-QoY2g:ڣ+Le koN{5s]LMp^Nε\B ZρJ1\k0 F4X"z> |kAmXki,s{H.ΩO3ѩc9O#c'(i!Fd3Z `!̑olUicTRy89Q{۬ /gp[$N9X'e-gyK˂\,r7^kMMy8HOgo\g-ܺ)ǿKهz58)w~cbg?j#+bި0.9w8G;Vw\X<ύ6~~Eoc\gkƏ/Ouȃ\ȇb?3#y+2.&h^Џ\B#=o3zI?s:9zmvS2^Ak7;ǃ:gzwFñ54DCm6cX6p'.Tg{&՝ EmhM]%y-o1Rg̯XZOv¾%Z"yPGQV뼐LHzoV '[<¼RU'w:_/`Y]wk/^g!mk^Oa{}C-ͺ=AWKbƐWo#ČBÉ33֘š'q)qg]ˁ?y?/3X^gdNJM;$W?2 q羏=~\׎} _88׸_B3{|o|r r!8Çȏc 3yG|iL^>zC1t~ѳ^g'=Rg}r식7vM2 `AxʾyX;ÿޙB[:-|s0 Gk??PC!~(j|A;=pw~>n6GvcĦ{׎:;=:Wy߸=aq`"iCj){206;}0࠽J9/o(/ZWGҚ$/+Յ{Έˤ6jRxJWB#9 #3nj_o]ԠzR 7ĺ]|qLn|.mm~1X?3'b%91>uw=|j_?GO?W8'x3?"_cWrv^/%/rgv+Ӄ'}1|2k!}[[I{>:}cW+?v_}]ot|g=#F~N!mDix kZDWFF 螇5D=Mh'qu=A#{GHPNp'su%vv=:7J$95*;)X:;H;>gO|hOѯطQ`\yk;'Zq|O|#?9{ɇ+Ώe5']_?zX[ZkvV {bWyO٣{';]%[ gK1BXozC ᇶu?Ofg=p@CQ⬚}An#DPĮՕLbD0KbD\#1fud߱3AFȜes[UΑu*9ҹX.2U'kK܎KrxPρJu+7+CJsnUnrNL9O>Q5 Jb!3`)9BS?TNzs߻sx^;ڋ6x=?q>~>gc|o|}hbȑ+F_]FO=ޛ:}ĎS/.g]Oc|)< K5Gڂ3s [k4kftd6<=p}N RTZ͈لؼmǩ 챑ӳum6y7x挮LF*stv,R/#+`/gٳqmF}u&򬽜F+Ϭ|\Տ !}g: e*\ ̄jɢ?bED= WFa2%~M?>_l<t+krv]\02q?>Gףo~}Q#yKt9F5kw$У>Jdg?=m>[dG]^v٥S繽]., _+5 vdM!ww8O>pn_?ȝ?x.։_'5/V-C&+&y7%M^蛽n)nn8ZS(9{yGIp L{>C<3/{kR[,ʎD1`k2ɗ:k8ڃd=VNS^a{5j]J[u~?`'\f|UwKG8;x#b*@gIӎ؂YOuJbt%o(Gjb?8/(@>\׎<6~|||¯[|r r9e[ W%7ι#}^Ϻfgzu+;㌛v; \{e x9K2x[zb 7݃pH.dEq_-~qnnoNi|>FWbG&FY}GFs3ksk `^SRAkMQW[9|.lk[#wZs+`Ldݍjb-4>̺mX!w12Wun=RZ|g>O0:y >-nK: ~4cՁYîcݏ8_>32iW?qqqw8~ D^k]ACNc$WzMzPЛzvgYW>׵־ v~Zuݱ"Qn/;f^w0h7D_k4𰮕XMqE΂959y-z{%[u!٬ o:kW98bkyx`taٽs;H Dv:Lx},|`栰R&ƌ?DbRx7ߙk`;}Ӯuyk0U;󚖰yўvsy4~|#~r]|XAA.CNEnGɛ?oЗ8/ ^ѯ.˚{ߪc/7cj [ˎ3f9=w>x<+#5™]{Epu}'R݂z_K!=hk ";~A{)]f|N86yNr9#DGWLo=%w%b{@z$h k⥥3Q9Fl"CcU˗Aic/{ZUwuol: wyXȼ YN*(U-7'ˌX\ ,Np֨3bLa'iL_ taQۮfg;g=kYSc{N~nqw 3|¯?9jGȃ\"GA^F~ə=0zRs>ѫ:֦< Joo~;o;˘؋{[{{cw{e7y}ݻÓ'8UGGF Ÿ:|"zJ?]lv 3KDN8-q.FE{{nvBrRk8G(s6XbP5z%#f.Z]["y5kFtEԵpMئCWh=Мs lW[,K}|79?$fɱ>/Qn=pA!繲E1<0,rN{\<&8"^Gy/́}@0?~YNY/"C.jg<_;ӮCKPĻ3q?2__s?99/c*s=ק~`CSw{c&=!{o;\[o\{;gK8RW6'֞ gm\K]~?W"'<%syF Ej _ KCeۤv5͑x6rrZsjY; NI{@0;콵y3݈ zB{/G1^{ZZڃWV)VK^ gKO%~>Yۅgu_īYq'ufNc])m!-Woź)m{;ˍ\ ȟsx'd_ƧcRgk1m~O8x@s=_;֮sMgdom*oy2>G6CCNEnq5y+{|}kD G~UFb4Y_0~sc}a'?ΈUxv䜏~$uq|XG}pӆزkww+{{A?QX ¸8׸\ -6;υȃ\'2#ykwxЃ?}Ki*@o3kM[^sN[ 9{gp~=hm u5kMGޗ6G7tiW7?>s\ӰU Rӻi\s%27?c>Jj=#ksL-y]Al;{W3y;4;>3gV`?~ĉy7~ZuVp߾z0[g乷}&mu`xf_{=9b$Z ΞYb*z 9y\׎P??Exr>x/0o^r r~ȏ3$y7/zֲ?zI?y 1^-ɔ]Ϲ|sXA`A5\}:{=tC?=#N'DMJr׼~ ty;=Hٺy ۳8{Kɜ4hL~QH; n^9?\\pfRV-ܵY,r~Q9f+ٯ%[W g{OO5oiόZ9)kc:B>CrO|Jz=1?:'Q}~_k׸_>g-O5{3i]'>RDWkIcLW .ZyvĞU/o٥qfr.^]=gnG8Jks-_:ٰ_ќv ٔuD_ΑKlRap?)ܒ~X8JpU|S?G2Br?}Z8wIs9bm?Uu p; 5\^85ki >@l~igkL_^\᭙{Eݟ|Ο}П8P?~qi8׸=vOoWYqvbyu&$W%:N~?zDUk.׎9_A[q'g'ùwE#;˖]_ψz`8/Z[dEp|y<;}5';FS܄m-tșuBsVQF`^%Z󞾚|ZiSo_^&Ļ}*Z[ ZXX-e3_isՈդV읮s$b[֚sй nsaj%ku<|Z:~Qv{g''6wF y&WW˝ߧuu3=_;ѦCZz02>}~ S>hW8 y ΍"Or%ߺzQzD=5^9v;##vr+'3z[CnZ{[}_FzB;6`&o / L|Vm௺ v.srHLah=}5!־B^O=#ﮌ\6w/Ȩv\R]U2|HXGMX>x?{u[5cvd#׷8Mklr$zO{xKshQ{fKuE^3;378M:W֞kȚg] mn]☊f5;"5r:t\|zs6W>nGbMșT¼9 j5Zk-1U!<3Hm.]:cBa׭&V\DIlϜX8ooOko6ko־C}s&=Z;{֥gMփ>} lJ}Zgny8zؙ~93t_S\ɻF]%c|im5~c OuLF^F~AL^nz6i,ϥwyH~_zLC9}<>w;R]3\~$܁?σk-cQYw?73GoнlBlI\|om/ps5 [uPg6[ܴ!⽵rt5_-r&5BF[a)%춟<=ٗRr3Čbgֺج~Zíϭ.j[P`,\<'gLbK\^_^!}_=q0 `[|b?Mcz߻q`蹞_;504.3V7>i03~{V!/rkșɽ%􄾴>o'=97-{/{R_U\Hx)p>lYrm;gR}br>h-%o_a mcY=ɾ ~{lZd~:[ќNHag)$`o)^\k-jMstؓj/^KfJC~ -a1[53nWHzm5rϐZ+>ro=3ŊΠO֣:8$_kxF]5iyC#_~;{<6?uo=gП? :>~+s Sk(w]􃞴x>g\xLI`1ױA쌽3-'\+2/p(3]O&Cۈc;~!Ӽ5buL-nB&D_^o=y mgnBb{[n!=2霛HSv,h>;/U뼴~c`?-&{?k9)g9rb={񬅯Hmz>O9_sߞ)W٧Ɯy>[t߭}a[nFV/57G܏ȃ\Z1˕y6IFU~D%l̓3_K`'suO쌽nx0xfhm^3hsU6nѬ!~?i&D<"~1G_nZ@WڹF69>z:=Z.-0a; y-]{Z{ZŴ(;wؐTb'g w[{uȞh8Z5HMz97_G]9[WqZ6:)ӭ:4>33;#Ջuq/lzV+wYGzt<˸8׸[ u?9Gϑ9g]B;П8?U>A5}xV;k?2W]@.8oNj{K716j~uVĦG~7y{ x`Rff攙Rnܔ233r3(g{:ֱ·pz݁~_~o|c?~_N"HG>s4_D?>'>g?pdwk\.zыmKxtiO{Z?[VZ89^O5|K_ w2?.O~ԇAxv>χ?o{9qt='8 w{#9و򕯄% oxCO~˥.upk_;\ W8g:ә\2?=3|ߝK_W?=;,g9K׿>?_mzOhG>?u<'>^~?ȁ\ȇȋȏS3yr6FC=;H/maG)cƎ319 {go[Vx;L6ᰇ=l?.p t^w}s?,/{_}{`s\_o||OlG~ٿЇ<1_ܫ/=~>Gя~tg}5bĽ}p ^`.| Ony[ʶÇ]bWz/NO=y'>89~Y75qB<"5';ɺ>m"ȃ\'7#+3yEOEo9ۢgvog1{a7';A?/;umc⿘N_5O~_[+: O4co!lMz!*_m8=O?υpΜ3Lxӛ4_\׼5/| >Oӝto2P Wҕnwp\%x]zK>>Õ|d{ysizֳt)OypS*'bﲗ~z#= K<&^3174s]//Sٞ2{#w{845}<%IyCxMozӉGַN\5|ntiqs+{wS|}C8]O~2?-=A߳lS<sm}cO?ڜR߽t#Mr|̧~=sS;ЮO EnG}~rOz_z= NKjv^mO;g~I~ԊӞ/g7+]BL^x+3µ9| G`✳|a{ؔ*Ś)y{v]k|{;߹k| l̫^)j+9YV^ZJ%X}c;qeiL:^^ 㷿w5ye/{tsJ?8]kce9O='ͽJ1F>F]ڧګگo}99g v}@y;`}*dv}HG G5C/{kO+3IdO>2K^tY,&m Weyp|Q'emZ׺TSr_D쏐zֳ5q)?ֿ79p3 >_q=#r9r2Rugru dW'r'=IiNs)Zn^~xTOׂx'֥s=_;GO;W' 򙯓#y+3y{I~EoG%{vvm}}웝:i:07UcOyZ$#x _-Sx?%Ox!nlL\@<ɚמ1c͍5شqmo{ۃ1w/9tH#5_L93x+Ni^1~a񐺰cjz>OL1OY~!B,S gNZߓO.Kk#%ﮛp?uIZ\_AIKΥ14}[F?]3baG==ڼ7;/݋eS?kT5.yc&?XLx b^](/khaqBX:aid/cqأx-nxe~!1?t|2\[ڞq+cs&#ym\z\0!\>90=yx{ϑܔؿOt鉯 '{5ܘ6z8 \ekDq|e/{I:XwTG> kd%hX/}קSlt?r~~_jv]/Og 7#G$R>>~NMKN a?!{{fמY'E9wd]wn/́t_\q /J gq8&і_su8ru}6>:Zڄ]rM-_2aWPǪe+!5rka>p{ks\:_So`t/|Ђ*ytc?fp@+}lcYyѧ}յBM/>P83jn#o|]@S{%&|目­9XNnѯ|ȉȍX\:/=w 섽/cUs38V/sJe]=c̯{d3^Ooxn.h >k_lWm\)8/ދ?68Z3#)C\ dt"=grڸlKMR<{ǘQzT5L :KȺXi9ڳ쉋"ooSDk0_+aH7/e5R^+^)7\рVjǼ4 _f.q\ON͑wIޚyڑK;  !\%/;#}km.생t(;cokqkM? H!iQhOXj|S xN\Ҙˆqkl NlK[+ZaVȇGLʹZص`w61>ZF-cT_= VY=0z39=Gl3FV8º2Y;>4Mɑvg{ej X`.&.Su1<noXgykk`}b7;conof0Sj9W{{ײkrmG\?ċ# <lV\Z-!RL5p-ĢFbl shk>Ɩ\s6GU=+L94G^{J kgE׶j~CǮ4Fߜk_w;'ak+7.##z}{)ǝKڡ=ڥ}ک=H.CNEns9={^6zozo !#TG\g1j<ҵ:GI澖k?KF^swwp-ثxP;s_>ZkE1hٖsΡ] Hs s5;ܘڕ}K:G7s}odoRZߣ~pќ WQق0_6J-;3O1^/'oYlOԓ)~oHpG'ZGO\#snw1y3w#w_7[כ]{9s>ޞ]' i#g&whQ/=^ aGt]x}s޳Qm2`Ӟ[#g{^|[gGx_ƵM a╸+3qtnI/`ְvyD# w6k}=aC;%o<-Yxw;=>泹-k#w!7?nngdY! |MО8I=}+ooq>1ƭ)>=bO-jޗЛ?&_r&orY{GV]nb_Sn-Cؒ f5~yk]h 9y;Kw5-rxnq;־38E֮ k"˛@%󵶄|zߵgtOO#GSGan?j§0e~?K?By{X盛X(TsgO}3_\=k~rH~^AD^F~{\c]ϣz;вKvž{k -e=|Ozu?4uIh+,H,Ż{-Ԡ]@|x w͙8-xpĖPKS?c52/I}sz>F<畴j36NaMYԞߞqu=sʃG5Ϙ56s= 4U;Ϯ驳7{߭_e]u6އ*7,87󮸯?d?ܟxN~!W[j' !wl{g KM3gO* Wv~{~?z_çYQ~WZKU{%-1$N d n-?#yQ؇Գ.<|G==(/ڣu0rb΅zkC9;:j){90(s~)z27շs0k+35]{9Ƴ_KJ^ES[a(KJkfKݓc?=i+bgs,n{c9;}`MgkoJVx%^y|u O*laތ]>9g/!e/Z1(oL{}98 :ZvFGay+{LRC'{}|Jbۘ.{WGs6U\AQ[$V u.hl2R F1y>͉oSG %[CoTJLWWs aGqM){cwͱKW``$.9%_Ku{mR}swH.]'Kk-:!][Y 9K>gȷy'G?Kwhϑp瞼z"sh={TLknɗCmrX99k䈉:"{yd!s??0Q$FC{-5a׾ ڣ]ڧګگdF~eJi!nEoG=`'{cw#dvGΜ_U,?zdGQ|C|2>Sg˰'h== 9l!w03~s~j5yUsB{RKdۂyd9اLN[W^Xit}r[qs&B=DQ0p&])[^ܾ19wN#5qQ&pSm^5[yY-ڥ}کڭk C^F~Xէ==_:`줕bo{읇 {g={#e sF7Ҙגq_ģfz%ݱ?v{)ֱW#g5tl |7+zXN^6?{m3\`P]k kjJEchc]zwpo >cSw 95]#ݟ[MnO;5bՠ;;|[n3vunvk to' %p>~FƫCٍcwf#t~ğF%+H x_%g3Ǜs 1|>55Y9?[<8*Svy&q?E%ge,>9r oR\oՂK>m>ȟFײ{`_y}.|sUqE ƹbٛΡuĎwy\亭:/=ZnZ~|:xsrgZy[}:G\+?_AN@һO{Ͻc=Z;fzd6?|d ^ROx _ͥ_m}o Fm!֬bW9Y%t/K onα7\^#uʣ?A+өN@3}|ۿ| :KO5}̇?s:Ѻ-٩-ͽ7~_F8KY;/Śd^YǬA-sH(vV< YkANR7wquƵK,kiw>*箎{B2.}!))k1_,.{G\|Qw$7,v=I?zZDg{g#x9tiFLS{Ge>sd1[?̡}px]zKLscJ׏qg9N/G9r&;{a7:dݎ}?} =ﲙ:kY'xu@q߭ϠcKYo=k O܍%crD9ko[1c'k ٧K{O?/W6%??/wถvOHB>D^Kj釞o|v^MkSOv>_3d!䗽>,Gkq==x~GcR3^f.`ԃX'壌jk0\\˥a:c}zo_ >RSFy)3z@/ԑ\{ Rsuۣf$9?2WԖͯ%Ϸo}ikv%0o7oz?99,zzѺv> {1{d쓝޵'~p #cK"!^ď[,vzx*']xj> x6EQPQN1TlE;QlPQ TLQ;A@z?~9\˾{;ݙ3g9aÆ af|a_g}.?p3qo׿/xAo/v}s?nsۄ7UAӷ-zֳOz͆G:*|+g<wڿ3?am _Wwi'>1 g8C;g?p!wYn'>_2|GqD?~_%?c 89[op+\*WbfmA^W=;'tRyNw_+< c9$SJnE}03vaj!g` hƿ3sDS~snlX>p<tmo{[}'vawCx[t3jxw_Uϡ7ys==Oln~v=Ɛsᱏ}lxusn|ogK }CpK\"뽞qG7 }{:޳=O9>u_W_qaM6 l '~}J~e|ima>e~@gLJ]'Z;k #/%c Ӄ\7zGrAh rSd[}f/¿9rAIr ,遵uj1.x n;[MxK_ڭ[)OyJxW{^ᕯ|e|͓~L??Ϝo<Y٢k\9ԧ>nrt3&Of'?7uGp >ߝ,gnnɏ|hR /~q8Uuv~߆?knvdJSxzxzq87<<7 twO<43uh?*|a.yK;.i-.t?5?G>:N׾ϾsspƘd1g>#O~7Eρ}s|k} 1o[D9KA< \3DDW 3al¾3[v6&=fZ#Ihp:7:'?=|px+^ѝԞA_̍v-nѝւ# ߻ӝ^ \Ec{ғL ȇ3G=QEq\2;E.R41bc {Wz?g~K_$n>TE+h-7ü]j~g㪳)rDU./9't*=:lR+;ޱ{-r+krqI,u\碳ְh6nw[vxcd/"J{䠘cysܰڷqyZʁx<"OyNlj&l g?wLaIo>e~im"_ 伏CJ@n/9.9o@CV:Lc`OؕZgع8as#/ SIM:!4NYko(mf7Ywj1#<{ߴ9dZӑݔ3U8Tk/ _.g npnuveN/~֋oA ]BY6@!^Ӟnڃ϶=<_84^6~02?l'7|BnJ| 9%y&ש/&_)A{RdطVåod?WÖ1?;e_n+s-뮧"辽GINt+ugNq13rV gU{ge/R ZMavبE9{^t7qWu[*lf;3{կ~pӛ޴XCU7ڇshT>r}UN78&3N_{1/kFw?ca@ '{IIN֙\)R3@Bov}kv/1Ɠ?X/U?E7їciȵ&.a6zgj6%usc0Fg%5,}[6悞Z_yں|_-rtq>DᖇmZܼbzkr eJVɹ=G?{gwxֳۗ`宛<sw^zB_JAOk`? njNCs~i ΓCH.@t>ЋZ` D~^/|x,@N_Fz{Q+bםS=cYƾ5EZo:醎5|O|_|rGao#4z@j#5@#v"Xe[~cY+{;s*OԋrBፁ^[l0n10r* k,4|;ߙbGŒρS=sUGjcߦXӓ<5g_|VNUqu'4z eϧC ow K2]R3R[I+ӓsNɿgы> ê35~]unWr?&C\S{iwR}"w~7ğ;jcPzk^sms)Ѫ-N|@y+9z|QڼK`'^E1@_׼5]ވ|uks9s.uKz~7%'WqU_0/3ѡ7Gך3NrEYiNkcW{.:bZyaWWvv"=X?ߵc> s0K'@?i=%Tw]5Vm"9W ΰ9^Q=Tַ.uwsއ=avU,LK&vы^jL짬qs~|t{y{۝N9abw1rbAswyyyK  s7<M;9ꃾ;Wۛ؃Ժ)NW9uS`OUϝP/)j,sw~?|y,ΐǺ=Z\Ebs;(ZҐNWǸK9\ ȍdQWU0u'p O{׻ޕ_CjsX8Q\`Bھ;sg{E2'sٜsT^7űd~-k /OWMu&7Mw<2N򟻮g<^lNUJi˻Vq>~ⷆ+eRc`ݕ[.:>OρMNWga]jMz={0r_Gּ{#g8X==Rn~ *nz:7jx] xZf)SBW}|?&}ts \uoЇߧ;:'T]rC~j\r}/zFjAi,th%)[aTy0  ՞?GzFq-5BjZM>!Ws@Ya˚p)ZЇ*Þ7%;lW> nɎΕyyG_kYۛ2H[s lFoF}a R[r![U֪oح5Qka/UvJ)bjs#!v#dOTQw3Vza1ȁw_w!i_d,3.`tC?tDOtE|_|y +%SaU =OEzN} C#ÚZgV`9?^Κ&Eވaƫ%r #;stNz҅!CE>Я͕?W[o`k>9.ZBnf{FoG+y!&Ύ}8ޱg]r̻9=.NfWzwZ6&|SSD rAy+CX߀RfcS`}nc9r{w+l $s$g֣0}U@%Gѭ }>Tr{-DsI-w)*=t^}2@d+w'yԩwŻnWwZ>lCgDl#3~{ZKH.gl П ׵}`w؟dU\eI?=?7/4}^n_W\(#5"oi}2<9Έ[bnw*'f^1SjyggES(\Xj;/㲣SY"%\yyyM[CX[O/З>VXewjN76v_?6=oXO\bzcqQs̸.ûjΕ=Yz= Z+\Ԝ:Ɣ޽-| ZF!)Us[Uŀ>ys[ 愼;wF'm5rN*Zx'qF_ok2?49jo0G#7;*^z_z~9v1'c>w_b͋ת<- [k~Ǭ}Y{x`w斲+2D-&xg'>h^{%w/M_4b5;bbzzOglr%XՇE߸s"J=b}]7+>˯||>x/ qS7._9#oc-~Гs¾xT}xHq\`u}f[UC~'k~;!ֱa1  r`.jAt1eK@Lk{utPgZobJϪ1=긜W䄜>RV;]^˲}ɭzړV;{|Yw;~}}}2OmkZ k=أ~d|ɚ5R7?>GnO|/#DZ!=A_Vóז`O+7/eʖ`iq]_o?G~,'?WI>A.3 @(a>t-w5X)[no[5ai$k,yum=nN?/e`B)υw~'/:~*Q !?-zA;_o zI?iK#Ii-:\gϗOW%tF$ğvۭ녽nm/ŵJc}X#]*7ΩXG[y-֣.SΈbʝlUiݏwu׮_OL㶄g^Mnz䌯7_^e ][_;ANK˻\or⣽LոC5nHm7?29GS~u&Ʊ.e8=wDtAH+tkv߻ s}Bu~Пu=c=l/2謹`ؽ9Ϟ8_jj~?WWboK9sex~MTBA=j)P޷S)}ȹrKy ߺp]tI]׽u.ג}=bǽn!N0YN18􉳿C~]y-n)p|q{>|=%̴ųzFZ>-{b%z?Wb,X]go9ZDݦ 9c?:Tcz(+F@h}?zzBޱ5>dQr+ӴXxf%y/7jC8+6WuKƪ_9{ct.4V9.Ya~{f{_{93=ի{PDMT=)>_SO=_o]R#a)(.SThS\ve&lR#_}o~o>⭷*8/&L50d{L2I׿U}>bjڀgrJq/g}C=?cW\o. }Axgg߼+R/?|_~;>/RO;p,]빮뻏hWxnIԯx1,/OWK4 q@<ć!gEB\wCQ.Ugc<VƕEYn^{׿BO\s5B -힧vZ/})a1lo|#c_[lQuQL35ۏvXF5v2nO..䒠?u>` V?pE/k ?]8 O{~vh G?/n?%mlg}nG?ϋ^Ѷw݁gA`Ove_vf/O:Ox_x ^F6Lv}j_+qhw rP,w_qnɰ&:CoRg[[]t^\;s~c9[mUKfW_=ۘw5Te뺵^9oyOZ뮻..lƸ_k~[=Sz!7'|]?}ٓpg2؍9r'G_~2w)ǜ8%^ 1~c/oh' }3 W\\ݦr%qO~2ssG<#1.2˕[.M.uq 30C4mYȁ _gi{<=?;Gq?_R20w9e}x$.u[wGS⭸ CWK[Vfn:tK.`۪gK/t9'#{Qm+msυCw ོNXqÜך}ռ?qoX`b_|qd/jd7k9 >r-796Hڧګ<|m@>vI1)[;ޏڟ-)!GMqyc*m.z#mS7uu1@c]gw:k]馛&I]}ukFq8n:صI^{:Ԕ?蠃BMiyӟ4Ԛ65;O$bUVZݷ\ԽDW@/Xzꩃ/c^eڽu5]}=?jűJZRv`\\[<Mksvj_-MqGģљCW~A?=i⌢aAOcxW xǞp#PsnP÷R6O {gsu? Bj+c/o"uPjRsfh>ϛZÑw.=ףr9gFEn3W:mn~}3#yn_x=׉;GjW=w.|&ם?Suokܲ{:mS:*b.'|r1A:'pBؗ֞@0qFk;.cx_1J&Yc )4Of8'rn6G~3guV83@=찵xUa^y[ӟ Q;a^.kf@ z؞nϔ~}ݳ =SUg,\ZDV!ME]U<<)[:C<2-b}dHP-|7[/5j//>^`vؑ=ٕ}ٙSֶGO0E]_7/~ߺͩ؊& ~#S%>eTy@خM^;O`Cjj۬-e0 5Ź8Oz?(02?tV\wBϲ[`ʱ6 s{k^N 99bmA~ܜ.`a'bhCyW>Eҹנ S:j!~#9 .䠽M]9wIz#vgԦ|ͭ>sց8ٳ^3&$jO\K]Sv*Yr{AgT6 Vȹրov~6:ߕ8[V?^y_Bv؃]؇k%GWg9Ə g BOt?Yu=s-@\,k:@B{"}7ʥ`5}U9[nCv6Q#nu8Nݱ3:C[3e!^_6|0j8Ԓ1qOKn1@agKuMiO?2j~i]vpUwJߔMu<駟m_}U5g.*>?k\_gŤ|}]O3۽U /R9Ơ9@*s]qޠ1@_5NMӋex]X/S.sMC[qa?ÜWz?k;~|_ .ΧU=GBۼcɺa۰׸To:a`# {wr>=%^s9Y ׿59t}/KK s2^_>s:k]5Ah2G~ԟM φ~9}.g~Ϳ9 L\r7-w+9lzG_61%ޝug-~b?zY&AܰꫯZb0z_zbh}`}i?I7$ go< ̯wڛ^3q-缦<gzw#Vt/tC{mrc"Oy2Ag7vꀝ|Qor'|rV͍jw/`NկQ,r&Kڿ*PfM3:Q_?RYU_ؗm)kLsY i>,` Ӟ8sc_9 Hq獎{w{űӘx3!>%~i[[kuI7q\O<(֧=km0n9R*Ԑxǧu6Yagpx*1˼Ǻ;ݜ`N㭹֢.j{gZk3'҇/^{ 3U[=s{~v3vuvmT?h3s?KY%=o P|ΈӞۻN1zFLnNAһS Fۇ#m%uQXWXaeL*-/x8YKe^~ީT_;N?Ly7mi|֩:y:D{4>^~{?I&xd]vIvyok~)c= /0믿rmS'oaÆ%k2ꨣfzguVkVr衇&s9g\w5Lnd1H[ld6K L1n`n0c'?~xM6dI߾}ËN5TɄN'` :Xc%>z2hkQF w%,HNy9>-ڕQ{{[,O3_U*[eCTOBV t~1z#FF:#ÿ@믿&CMN9?ȥMY$ÇO&|Jj_~ `~ēO>,ҡOjzРA/38|*3v鋯/:}饗߯)nU~ğ?~X&ęxw<:{\ EyiWFƊ/g*ow\raUYg59%\y뭷u]7yskSO 2$̏w޷k䤓Ns1G:W3ϓ[_n+7|3| neg? q%ęx xB7xT qXc:EV ts64a=X+ȼ֖w?[o:B㏙'1j? i˼ΛxkQ?ְ\psZү_R|N^}?SĬne̝v!, H<+U^h_ z] _<~l@jg*y߷oߑ7tSX|M曇=NExvm|W]_Xi3<3iz+2z뭻^o&p@طq3}ʅ^88p`+{.@_X'eaI&I^56_]ufͣiqSGEW^= <D#);5"駟x1@y'niY1S&'pB/pz뭗3?6oE]4η +$=PeMu >{svp-_^ЯW?o^?yq P?#g-3}DT̠kI믿~ȟzM#FhiK}ZD'h+_,8gOxGs)= qX{Sk?lYf s-^ -Pk~xX?_7QWw?[q"^Z'ұ #xY|[f Gt> Nh=kMUHu7x#eG}l}y{zjꪫ=m6+k3axm}<.[o>pK,u]WP m(u~Yt /jx۳? |7W<[$:M]ta]|!5fK/[>ʚ7{g8WùClң>Zusî{}M1UklN;m7[~C~\me_.#vg7kA>e'[<xsG)^ůe ;ѣ}[=i&w1{`*pcM:B4n%wyg;кbڲ?3ϜL4DGǮ~;!{k'ިmӲҵ~IP/Xÿ.Vn`L5͟zd6 pΣ\s56Vg6x㰧g]8:*}Yem(pUVqK}ْ[A]5Ո!9;S?TݼtInǎɮ1QK_o~cz|V?ğxztqz.agPV-`'x"dM> o=+cŲdfKprT5CB-;ΗR[{:-U/;w]N{ Sj3c~?k=cU ڬ>w o,e? cC!=X4_ ql_v~'.M9Ujĭ1wxo6Oy{/ TפNyh"v|sӿMz|n?.s|~Ȟ1?KOk⼓rQk|o=}$u=ͺ_|۸$u㣾q|15ͽ#3U3<並c g>3_fSoY\g>+%/=alЏГZu @t=r7«BvWrm/q1!68#Rɺ3ȿƻv[e.sS=óo5V=#8"䐔YSJbcfEr=% LMp};W;=`w[uO\3OӧO z#{? 7ݬ[[K)No| A_ zA7Ac:L}z795zb_"Y#q N>pY'9m1׺3O4(z];ڍVHn"TX,緯Y^I+#{k?<Lj/e|w>k-~gNt疇Fe6azqM&S#BKeӮjencL!x- g[A^ؗkT8s5-px|seN*+mW-z{k[~Pys~qwODzm'3+]wxt*@w 3PWkaOꎎv!꺆o۞9|uV'GLL|+ӊNOZk̍pIMϞQWwu/?yA7qW9]|wՆ[ UyJ+z@Twt| 7ܐ{"SUo Gi)/U| jwq83t]9nwz{A+n&y_P~&&Nvm7T7g5z[+%bٍwO; _1tUc19ar%rv(~R~U͝ۅ|Yj[Z߉'x*]✹4hPέm?jYTk#&W`x(u8+ؓ]_~_-3~̟9#$, xǺ}CBLe@?{QYzKws@.ZqXۻDRUkuU9]:]sϫzjO>yA(^KFRW9N {1zBodWegfwCĠ_wq#~đx#Tw[gW_T.X+vYzr 9pP ^WeVn|;oNCF z~nC=4E[କ3t/B3τ:jW'_Rg?vdOve_vfθ̯ہ8/&ƺxC/᧘f!g/M#Ĥ.Nnǎ~ή3nv'⤌:y!|bҺ>x~E mi݅۝k0YKQYJWb 6^k<&R!p#Zר{I'sWuE][楖Z*QA}9H~ꬸub⦌44..E<>==;oǛ?{t8":e >K)uF7'n)]-s,{dذa-cI 9!j) +gomZXc Δw#MQə/a_i?ggC{KS{k<̒oŮν_dȐp'MSh:b9 n00-.sd_K>sͱ Ζ;Tg͓ɋ5_ߚsG0SsL;}my^~'uf]7kNXsz/x/' \.hnq˂7\u3Z"eqst=7!/;&c̯7?>+4~Sgx>zI7g5j,`sx]EPPũx-ķ8e<jY?i|ˌNKc%rgG\ve/b~ 7pC8#L5Tq r駗vw߅-";[`¶nvuװK)2ukFϋ~QEg_vf@Gq)>iW]ܗx_ _x,5^x*uOHN[\^q}Y^_9Y0~_|ϛuYG<0L3aё7~.Raw[mUXh o3&l&uE}ٙxwOZ\2/l&ĉx7G+Ž/Y{e9c>fZ|o=Uݣt&yY+q:4{?129~_}.lSk,]ag8#rG駑c{LG}^sWoySZ5Ћֿ8*fxCĽ3n<>k_ *=G#8{IG3D&hw/6́*B 5\qKCGݿ;9眱g׾dp7]~[+RZGZ 5ҋ馛Vn/L ;w Y܋y5䳼OKw.yyt~>XGl>O{gr~OģVutѹz?Ru%sC=wc r-vN/3ᆲ2h[,v[Ÿ|q{.6;F:BCV[m#2K~!駟.QFٝ?+Ŀ<iOy*_m` 4u=k?'s})ߤB7|;S;{( w8w5|cul0rGU3nܿ}WR||`=sUV K/tmA]kvh~.6z]i /bYѩqmņzh;SC}W\qE˖yS+%k3N7>}Oei)#{+3{;W@>ȋz!|eGkƏx_*~1zg0h2D=z[G=[x tPZ{93~|pǻ:)}9^~ꫯzs2{Kb#b7ٝ[A^Ժ)x=ËO޻A5a/ϐ'xע vJGY wc 6k+{ݿ;6l=B<@o6yW>z`cJ۬mc>ط~߹,kB{“qOim{ٿkDB~t%䛼*}!^ďOzEWHQdh\OG}}#Wm~=H_]wu 1h5Nr?'|'oaP~~Y]U-|K>;.=I-yVxҎsvgϪQ >Ty$*;AO|òh%&)ip '#'#U嵲ږs*=p/,|z{_>'Y]t#VMt9b\'flO~ j]裏?{~V?ۏMEٟZB~$}F#{F{CX嵿1':E@ Z\W ; .h.1g@.UW]z G⎓o1;r޳;S{jPg = /ޗB 6~ZavX\ugw燪C> !OKwx >ċjD]*ךwɷ~{+;W'*sUbN9X lZ;IN:oW^YU8G7`gUPs |CȗVgx  ćx?@=*ܱLkD"h9|ɰ{gc&Ե_;+oD/,h^Ȫ»SSn }c{oøqJ{N##=:E?Jhg~@wq/A[;c u>>ݡ?t,h@jz+>رcK^3~$E-|6?OOQ.x=C;??P}3uhw,ڟ@M}`.寳;qVN,iu~^:wN(=ZIV6~QS*no~-/FTx ?)|Z>,:SN"?NJW:1?\PYgŻ}o'\\nr7Bͺ(${V(R+ģT6S/޻%@RK,ėx,З*ROK+Y?nVOݿ+Mغ)M}4I I}ўTMod} s=\{^;Ye(ğ8i;@ҫ^Jc>ğE$Wn4}RW)K5{E׬Eτ6Z ̺MyFZ&YgxާwՋ="?x[bg]kﳺ7?/d(ė8o.v#ʄd wJ>ģEv#Y58=_+h5zILb4]'9Kd_Y܋=ll^swwO81N3&9bN}孢Yq$|gy@yE|wk'xYO 9JXo[pЏ?8ԓ̖X{KK{oCGէOȗK,D!.svgW2K6V~~iCq#~QQy$>+WGi^gmEhhԪǏ uYթLݪo߾֭˼UAy{!>ٲ[gy<~.lRtITRo%glŃҶ?H>1lO ~s?(>ūYq:A/ZmCڟ;J b~ו;4|W^aԨQ۷LnM0!j?7ިi>iC+?Kwr78e=O#䓼,Md@}dYvopenimageio-1.7.17~dfsg0.orig/testsuite/texture-blurtube/ref/out.exr0000644000175000017500000121617113151711064024014 0ustar mfvmfvv/1capDatestring2012:07:26 11:15:56channelschlistIABGRcompressioncompressiondataWindowbox2idisplayWindowbox2ilineOrderlineOrderpixelAspectRatiofloat?screenWindowCenterv2fscreenWindowWidthfloat?q>`82(S >~ 8Efur/wCq[!RJrO8Nx]G,qnttE:,$DJnT{}x? Bn҅(B:]g>`ngʬ|""#28VQը2: \V$R?<>={quu}u}yyq~R)T" ^nzdnnvvn^.Uh 7MJ˫VVK\6MJ{NdiJ\.yəM`uxx:[,Wv{{k{{{j+B.뱙VVTJZ36Et6_Tf*٩{{y}sUojHz$ 1Uk4jvuyuSҙ.DXiw˕jVjj{4IƢ y\Nj1MFVL&b.^d?5/|s9z SL_ؔ*`9p,=<_^]hd<:5dƫ\3a'2js{Mި`t*q-f\.B-lzmHg Ÿnkt< 8{`; ,yd2\Ti:`8LH [;{{( ?$M;Tuq8 `رڬPeT&O,K6 :CR=zPRJckn(XePINԌA,,K#AZH 696l;].rV ?ʦT*Wz h"/՚[0V777Uv >7:&$^Wb| ``/GAe u0$2dC4@*Xy UH6e2$M6DŽD?Y(%H8 v~&D,@z ^E"+N >)Y]xk,N_8!!|6aУ1sjsGse(wxzvS#։wC/$][ۭV35Vtc.N?SSS%~Q ԗ6+F`@,U $wtBbU8Vn`:*V Kk0Z `3dID?:;)pB_R:ЄrBn:Ep 0b6I!c f(,OrF?<<ZŮ@<o8h=A~Q)e7^V"1h"!UW/_` @bs"\;8 T$DUpd]FUiV&sQm%_]YN"r20H"vB f8JOO?_P=Y !0T`lg0`^\߽{w&' 2`ղѪuʆT5\pqr+M,,..nã|}"1`if&߼Fg6qnz_,#ɠ_ g%+H98>EݷD)"`8{ll a1vS IOO? De Y$Jdo)ɞ`Ѿ͗m4] PÑx0ЂCԘ3l|w üS&KBQ@H?&jUF'v0DZ>3VV7Js2M7Ev *G '3j pahmNs;_xgfljvqE[grc nnߡ^_=9(dу@vsءkVf f1dTmpl61X.cx>hB'h&S &wGV̰@T*uߴ$o R?dky ]lJ.PU"0T`2 f\.TB?1̅^7.@w/cȮ r1 Nхs8aΑR*&gfk2'/T(߽:r \'b0Ξ>aАKhj\[4=5א,8z} wẉ[d5IWg߼zTm p{uq~t]%^aU <8ݠ(U<0X }I:$f0MffdÇ`Ap?Б4U9.Q(UZ@50g'Rf8FThFX<e0w e8ݘG,qZL҉D<˦;٭d~aկRPNNMyֹBE<H<J`7v,L*E*A!&^:?&EMJgB`3-p-vZJ!ƅfbceanrKBތO-4F;ˀk{xz~u|{zU/ec!n*k+ $5\eqjA7P-6-@]„N"ϩGV{~Q"'NLp?0ɿ3ݷZ,??`pw uhoe:Œ㴁ߠQ+ricD |%#;T,O2B@(v,ⳙ,+\J&q33 4666>6195 ِu q[lnNQ-en7f'q @K +Zh`Y-dbAìW7V$s3\ȏajkӇ?tSdeje YmTTT#a? UhC8V[ 3?f:ivOwDHD@?v0+h7;?FˤzxfdrRD$q`?_3}hB:9=s2F˗<߄qL>_,uL@Y0ө4@>`"8]C"7oƑh?f(Wã#`VKEIX_L&SH33oO}`\߿8=jpe5jkK x a\Tq{l6$s z,<" ??_2T3k'C5r\a9׸q'XVdW;,gMV7vFF :2)}؅lW0dN@T.~#ֿZI/@5f9 bsAу`9Q  @>DD" p8x&KԊɉ1?0bB9W5ۻ''LJ;z92Q#XL^z39-؂MTv>+$ۛspZB::lmy?I.8)Z;ρX*n"kqfotbZՕYl *Cx][oq{k/Mo&eN%i2tZ ^L&hʝ9,Y}OVwHT:{Rޖd^+A2k=lڬ-_,.O }+&˓T6ԕMc2FX$A/X$`4[GC$Ton5+T @-__g?5x؂OU/?) wn.i1e+Sdq&;+N=|xwwnB2Lvw;qS zvk6-??~Qȃ3whFU_psH'g_]˦Q:!Y8 wz#?YhEk1n𒅿&7M;cX**yE08-pc)a}`8d*ݝː(_26A}D"HK`Y p 1 IdqvH>.Ix{~zMx 4SL/*́txvvIWa;ۆӇ `:ȶdQ\XW!:5^I"|w,QSS94)nQ ^&Of 5S-~Gqܧd_tA' ;d ̽7;ѿjK'qO1);:ĮGi~Z?Gv w'$B1ƿ?DbLS{E#p(p}O~   "aBhTg eM:\;3>4+uf'Nf~GNV jkx!d7+15:z)y&RNXِk[ U)[8x7W/fZ-kSeDPSSt *C[,7JY0].X7Ox^tî_)sd, a?;J=fq.4nwcWk G?~٠$/ 'LT:~?X>y@023<p*Ρ٬5ŝ(Nវ$ Dr{xR.ߞ@n^*{LLK^pbIe Z'~r~zAރ3J.,zl.:S3pUT -\?|.zxx=D.O7;993;&UjǟPSS~QxsQzsϩwꀂ5p-k?$T7z ބXq^G`x' 6F'ąW%CQs yxxR9bX}0#ܳE_7G [hDgwG|`xPU.\տe|;(/h: AϫSS-t\T.RPD=A҃OzS ~>y_Ή>F/x!N/Se9c[Da_ˋ%9*Rt Z_P| ~y?*pspeO]},7c`'S_RTF`PAx †xoſ_$%-൸\꿷^_Qh 瘦cm6P~aGΤ2:y?IA? )%X*" \tAʅ('HB7{JnOі 3YO0 (WA F5@\\n?=nQQgP ]G oY?]]AlE4܃`5ρ RV㵘U?S-V$TB^Plі?Dw71qT<5*_߅zJ/Cbn#K\j B?@WH?49VmK@jxLC_ ſ{.,rS?)TT_'hKmw_LF c(+ ¿g *$T=SVU?}8;9گloT" >t:6x``p*=1)SjV ko7ru!ᯒݢ :\>8sY\ByT,i0d1Հ~J8\0OfA;@8;K'")Ы"ɕj-ij @o8r-Z F vMh$bh1C!@1`v9]N@i5h6[@n/v T:l#0lR4q# ѱ DA KOמ_\^x~qv|P\YHavX0:iw/l\uEg׶7vN/W6WsD4k!otx<tqc%P1r9`w{sm)Jư]p;?2ʟZڟ?k[lVnbIWp`:>C/ŀaq!K'a]vɀW(r9hBD: ?(/sI1)Qh'@ff~ic{˗/_\3I a1 ]mO>iቤI̯lVj޳t*wYu*?:4ur%:g$uvebl-/Mǣ*ߩ$?Xeqڟ?k[k՛XQY @ ?98U/:88BgRq*rLR,6 RiC43 kx.O(טh࿵ZF,//."j^Wo{h_"?[f \T!j1A^?;T&OϠLs*xVT[*MFG*Pj8><=/_ r;w +-S"חKL}'08 %2lǬYMw@ܨ8VaO2182uW: se@?LgӘӚ"-KmZ!>tm~A>! H,CԽ]@|$v&!k34 )W +.o_x۷_-!agq%RJY"pryyAxMBqP %"8_©|iic˯|*grx4sۡ;?x `T VjC[ JBz>y 0ŝԻ"Ӌ'UNNN+[+ <,M:Z=``J6ڟ?k[k?Tj\PTJ,YKdO87)K݄LmtB<¨Y8aL*\63!E -O:z[/_ gr$YL_LR%NcÉtPZXgff L"_StV+:@:XU|H 38XZ\Z]z^J >d5k>Yp\Zެ]zU$@*x!J&{?jiJq0%S$M5Hɵf?.̯llP]. ywO Xvnir|w޽!\5l 8YW'2hsBT~YfE Ά7V9<87.* % W.W7mW*ݝ|BX3:˱ j#'M\] `n[!AdIxB |rE<|tv~~q~v ::@yݵٙl21O&@ZwrFbDjTK2Z*{≔?_AÇo`{j6l7:244<<2WFNd y>vf3%b2Lj,@BKpFo4?Oɨ>=nEZO&@U.b \k.a^-6@Vq6n&{t*#Gb1en>#{|iBd  y_|yH 3@ ,Mgѿ5p_OW'n ҃<}koobF_o_D;C|\mrx#Ī1j ,|Gm/=m )Jyv\Y$6rx݃=|7rz>g;kB<46c#Cd+LE͖Wqsr2PMĢP`j5~ b#mT{+Ug?_{30"+?+mn6pl.1t"5j^oWgӎȘ`R gonbF)FdOH QҟǔY$t *6'/_<`l.cY-t>iypLiy~ˎQ;|_կ>{PH=6=5<?_\$c/_뗗gKf?d=>D .X ɼ 4|ӳ GR{KH $o?#)Թë8H!!รDM6;g%cJ- SqQs?&HdV7A6׫3ɸoiU"?iw/g;.)Xgk/nbFK9+1T&j ᵪ_ooF ?ij~gV 2֝( 59AXSf``$Vnh ^ fΌ~~9'4f`&X:uJ~vT3@51 fYڟvlGm~JSy>EƇ\?gR/h7 `YIbeJb D(Jj4q?7~MA|=m45&6ZY3ڟj[jg ޠ6&np0@GK\~#72tj_Y ~6?X3ڟZ>;klY=q'z_৵gЪƬg8`S~3ʠR)g2{3(=2ڿQ?kf1۟o\t_$ӝ?soFrk5c7B&@V3]@ڟ7%ڟo4)5kChU@2\S?OZkiuJT,:QzY3ڟ鈵ӂ?xVn\_5~4CknfAR_G[ Qk)͹8śok䧢v`n?p ڿڟo\74r}ol@dF?60;ugҙ^Qƚ7+2=fS;%\~<:?kQ?lcψf "uMaIk~FR & iL{!>(#F Rff^U,"Y^;b`O=gr}YPׄd$ 41̈ *k#fWA(7|ASϦ]ƙח@?YڟIXRk3H,d29^ `kJU:`p8 ;hR@ZZ(ZGgw?ɠ1@1q˘~T)qX!RRJfNAYP֙ `jzzvFB" կ蟓_P\ZY}ұKOOngv1xۨeU} ̍~ _U+\~h4Jk39w߽p)!9-+sG tV{{{; qZ<Ϡ\*EjJjZFw^ʩc |˯>xу{7v7vBmoR]V\y=(<[9w]\[^B?cx>rrJ5htZvTR)UJRJ \ G1J:cfL<.Ǹb2h0fuwA!T{G'8!0Y])gS>n <&GJ&|3,FZ.  Ii @ "hBp;-VQ7!3 srsrr ˫[h83LV?#QimEeuMF[ ӘrGf2 yAfo8-W.]_vAY REۧ+[;7nWU5[|[Z КWXZ 1D1ZRDB>wJMt… ٿ?Oʯ7m퍥Yä[̤tD?COn{Ϟ>~xֵKhR[VI-mGM6dp迱4?35ᶛCehEN__oo__s=4,HPk &Dsk|1L&V)T\" Т!aO rZscݨB`R>;CaT&MMMNOMNF$Bb@MV;СIQĂavZ)3#3 @QIe͕]s?1-sj[[r K~Bkv [;+F#ZTKG\jO) /^prBJ:Bvr.\:s-u5YiYEɬ'~O?8ܛGNDjtaZjzfNAi ޿ldLN迱<|^- l4`t2{|ῐ)R^Mi<=n  $<0?F(l)5dZG4:.L3s`s 763$mtc!L& PrLl[q Fnjn/o^rh(i iG!E'YY9X@^$''?Ϸ ?F@GcVn904ԙ酵G_4dž:iyYiɉ/]'e%^”f=:f1DC)F"p=ўJs~i5k,훋PL0N Vqx#2 @ƭh pSZ1ShQ}"ls'n Wɏu奧!ip[Yw@4?fjH3Kk[{7n7?b!yݵi) /%$$f ɽb~qGܿ{_̣bn_Gs]EQ.)ym 94jr6>y⣏>zi/ џҘW\@bdco`vaemc#4735jTQA[hށA.oD{4Ngq٭V3HI#B?=`z22Zm\n1n5a6'ه?on&e啀 I݃ԅiE? ={ѣ@FPhd R bCl]b45ɡwHhYXհ8s?<,y4;ImAɇ^JOOn; ԾI֊l}39,V٥{7otYUrH Ud$%&$$&f|.w ͻ/W~O=F`7ēj:xrcrqsç|~97y{/_-0*9#ŠVF`ֺa;K$nolik?ZU;t[X./-CG?qfv8%5@#6sB$.2c,{ M5!˗3r:J+}cP奅9\1q9 UhPg!R{^*$#5DEAь ]X1fo'! M< ?$$Y9h/ragjrQkjjMNVշQw_:fZ&:ۚ+K r2RLѿ8v/<ٯ~O?{ֲۨf(h-/_\k,뻇WW?F!oAE] iY--N4: :6M.\@oOQ\\\RVYUFg r1 8"M8k :ed%U~mL,o]]Ž" ylf5KsLY=| k+˘D3\f wc3fTͣCL `ޮ4Ɉ ~gH jNn^VC8y͊Fbr K+GOOnuDؙ4DHLNU]Ak c"n}/l*oN%U摰}iiw?63ϾѳټOleHyEWZ:YB՘{zx~4?lcZ袷6566-eu2]Zx=ҩCp= tyy~aQiEu=FuLgfA`hfjR. й tc|hs!@gl{FKtzD*:1-|{w{>jcS9ՠA7e-Fk(], ,?78?73MИ( ъ-آj0^c`D,Sߜ @lPhi:= 8ln"%$$$P/[Ϻ"LWv[Nr91)53ȵfofi}{۷oL<h#We22о?QR!Wb/_|g}Oy]6lkfs _? ,m߼OWWVWg>xsokiB'])f_z.;B+\r8v.Z}EA;rMST(N6p6f4rvxC,iܼ&jsX2X]G^cd4f&'\x'RI l>uޓ?~՗o, @m*,2,s{9lr밙Bk`P( HdJƹw=NA6rOa&K܂3;["MBGxebAsF:+x+m>?ˊsILR6 ]±Σg=ӗ_|O^NC˵5 ?4E/npZ~s@e^%Oi!7\J |bz]}bV/b;ŌN1AփvH*`= }hӢ Fh4OP@!> 呲BfasE "ŵׯ\][62M)MO;?yR !__޾O?՗_~nҡQQt[کmp[ں"3spqiuLЇF>;F{RہL_,W7v7?8l3 @Ppˉh7FOOnO83'x67K/^D-*ml_ۺ~wo 5r1Am)/)g>'?bvu;O7y?zW_//M!G+˶+Eu P5Yݽ}{aiemmuu%4,fѫ:(͍ We~6oD֏a\&51vSBZLWitz _?> DՁ MF+Bď,pĩh}>vȩܨ Лk/br Աv>ի?`5u">-BXTP^D)PXuޓWW֖g|q :6(&h3 G%ii}g> OvH.A;ߘ[LOOn/?5 4za_2:1z*GdoIX4;(ITeg<$Wow=YsTx8ᆇpj'$*q?nuO7M ;KŸN,jorĐR#-C T{8脻!u?ǩ iOǿ$YbѰѤK#I\|;NzYoUR7x+$|O OLJ/%o.D-L3z:B0$Q,2[x1ڥ8.${X!28tQ:O[.NJr ßϷ.)$D(@tS%g#Fc /zy,~SBǦƽ!9jO@_noDGS@n[2i4O?/GTNe?Cb#oF{1RPɓҟ" V3QZ  á(o>̱lDѩ+w:%/R{G$_fE,;95~& } ?\#'_?~Ey|X~|o>RKUeo%DyEFt"z()'?FE+fDeA!J c3F_?Z8%?-Ŵx@ U(C'# c>qMpJEfc/!^rS,'-7kEJɱB~-_2$xG tqqI{#.AD 8onݶ;s{/<~ҝ{ܼ~qnbb-2AG9>V]cmE&-edKKYYPo%Q͠kn*/(y9N&##JjdǞG:1117,a&$[8vwXH$KT5ͭщs LAm*ժeАа*-S)3vx闏O vuM5E*Y.)=BM9Ye^ભ(?>O2p𯸢Z($">EHMR(d 9\@ *Ra}D+ |>4dfy\TҲ"B.p$T>@B"bbӄS{驙sgN650qwn/?ܴ͏"?~ߗn߹sgO54bLha8$OfeŵѩWo=|'t[j񤥥,v6_'yqhjGj*J @%@%i$aZu5u@s.{=|FYBN;|… g,sN)Lqaq lzڝ>`OU% HHL$3ؼ\R[Vb4w[:DamV''b"bb T/il1̭fiU2Iͤ[O$?c?O 2PFSV*Rs9l6aӨ8AgfeÏ*`eŅ*]FrXpx U[:G'-\8?7=1 @$=サ彭O~Ĝʞҭ۷^<71ԥJytr|q/σ|rHY:g}_/ t M*i l.? eEՐM6sk$ARxQG{C~N9A-jյ,֗._Y0?`F)^%KHJsŵs7=l36Ud"n&-$J*-(4Uu3GU( b>𳓖%<"*G$G$f%fKnn54WZI'R| & Y#xpoI@/de2 :3(qqpQlw}!oZx|fǟ~o>?Sf؝*Ӫy">'I2ht5C4UC6X @`YY%󽇛0117,_sfb"wm|BphR5xjrfaիW//7?qSɉؘ8< +ͽg_ө'{- B= !1FgDR_]2өD|lTxxhXxDtO(!++i0:`u:G: -=K /,*-weBJ!G3. <:&, jx!' jmiEmC Rgl,,P%F1@Yb >p굫滚HE\f9|{z76D4'׮_rqvbآP==zbYSמ|_}}ڟR*ؙL;'WP72y[\WWYw13 |+߰Zu!n;wX9M4X{GƧ/^v֓F]zp%o&=)i,DUR;'N -WYYgz*HpBIot$TVL$b"BCBB÷y O ŀc.l.puiֱ? wH,8_[^llkרV,76g ZRtTdddDddTL\|h3P,Uj+8uuu+KB\>7EйyΡ Wݸ~4'Jnyƛlu;7E0 7{}nիNKL*.,~7ĞxӇxNqۙ[{/O-+R)s,Џa0`auyS[L??0UjU)$"dhyy/z7@"7= N`"eSS/_[ZtJ2 l8WH)txp[gOX7?MsmM\vjؑ;m۶xGr?x{>/~䂥 %a?ELeUes@L"k3*2   z7@NL:'Uu^8pҭ۷o.͘MlDG1WƦ/\jE>P_]@ z/0,. ޚJ]׉K-ʖzs^f:%!.*,dۿ뫯H"]_1065;7njlKW]dg|?ڴݟ$;?7P>O2'璀03 LHշ]VPJcR` s@a߱ fjg<}|y8 M{wom3k*r L!~HΆ`-'&fFX! UH  EDƁ;hlX.ih1.%L.DGnūo#! upbwo/1rY4`G" q5ӓS݆ഗj UpO$FY,&\D$&$ DbL6W+UWԵ:{FA|##G몗]ow0 RRRO@HL" Okb.3D ;zg.π$FchljfnpD.b}_?pӎcIGO>{=@9c;0(7uN-^Xz{,]m&[Jӥ;?>wٺ%]nǞy85c#3 Woܾ{3n!WB"c߼~6 L6\ym=Y4TSYT(OLVϣ"pT@QTg>z!ݽ=F벭B8`JK.Gk jzr u.*jyvKo}уݝ5eZ,f%@% I)<Zzzz[UJ @\lTDxXxdt,H#r k f`hi_9>62lW6=~y,x;>sQ]F}p2GeKԠ 5tjrfna-޿F/.%\ /\!ہhtqZgY,N6D=fS'z|-_|-8MrW_pݾ~y܆#"!:SôˉɹK7ꛁaS)p0B4??ſ{ {vr?e΃O>yr ~ɺ cWY䫋ʭ7wtZ,]]~HoY2NJkwgؿ6eWXl-X;->e;ǦN `}̾e؝ewi80c6Yu` M}*Rk+/s^V?Ml ,NPS)9 rgc4 ){u1 fk{>?3TUpk8P.鿶Z\?!N.*TѺs 36YVCؿQ6w 1cr/Eض#kT5B붳 j3؏]G9(]`sTp΋l5ZFBz?{u2eGEfmWT4lȕgk;z وӫ9cp|ʛw|P<] ՀNb*s]N$ aV&E-rT~0w Xmw:ϱZi5[Sȵ)B+\8]ȋB|QP+Ek+n%KX$eaE11 S_c6۸oԢYgd+Ű""uZ:+[1L AD! RhE (KQ]pU26$o{w+XTg'>\GTP,, .fW hn_Dbcw&FwHHzio?d.HZnC}tv"|R;J31áD |h-j:TvIXS11qmRT[Nh}tbK-:ZWùr|Ѯ};eJ̉2Nwqt5p죊IlOB wΟ:w]8_ *@&x]w@SVmֽ 7!{I @!$ CDqκ8n z⶜W@}3l{wkkjjhhjighnc q{N]*h/Z|۱j;Fq>ۂ޹~@k%hڶmwge7@k<|бᑑGwUxlFr)2<8(0KNPMN`p ؐef2i)$4%R\< У  }}|Q$2%y>$T%BPaUQ.f H5C)6:*2"""<@\59*(+.ZvzE'gp J뛁67T(1!O,emmwr|dPĊ`keaY_]|OlF ~O96\S J=߶u}K'<1:)@3tµۣ;4WWq2S(䘈`'AGG ){@K{~gdZaޡ.߼;.:^F*$ w@LIu'@PZʊRX)'Ɔ}1&FFjɚF(|p<v6qLo^r5{3kw_殻o{7.3b .Vo޸q6M]3Gج֞?z耨u!@ǹ|C"cɌ|Aieum]cCcccCCf%ȈpnKLJ+(G%!XYLzBlX+ gp ;[ꄥt),Z9bp^! l^X2 ++ȥ'ToƌgY||cVǩ;/>>t$/35 .͛6oUgj%DPyC'_v BBhK /XV?mל )K{w޵k^8ژ:$+::H"v?B^v:=1&<BAKciR*C/*)+Fl*wq762P1s΂,]1'No)JsVݴv՚u__\{x[}bwwG[sCMeIH A;:g/ӒQ#`3f_f.G7_zEyS33Bxdxȱ|.;#ggciaiecHgrxWY]]п0it5Dq$2%5㋜J si8hhow׵p 4kj:ۛkmOvur+ bXlb +/(VWW Y`NrC314P-ZE?8&<<8RS^N #]wl^vͪ`!Ng j;a`EF&!6"4Zù{!##O[WMؔZ=:{74vzyX%GO_vw鳇b~. d~nG{vODZ+ 1^8O>/شS{>\b񁫧N~{ܙCrNZ, euӆ|lK|[?rΓׯ_=>PUJDcv\vˎh߈O:u8VZRIVF&6xpRb?7FUmCcKK[&<.ǐdi舰%ٝGF`%tU q8ڡlmR J`6ݽ{70D}c\AUhp?{1TW]&fb@dAٛjY`|R+\ 5B!\bh_-]%0˯_poGug'Eb,kmݼe֕ʣ.٫o߼: %T@6kl[u3gK ⊣Bo<Z[8bxL»ssjjk9\<kHM8~ gW-m=|d_MH"G0(6v Tt >zBtuvrtrzEl^Q;-1:q6767_7swY:'3ۻ{{E]-Bo]T/e+X^ESL5:pɋ>z:,Zk<;=Jw CGG=bSjU72qpK+m:uڝO/)-]rdemmecc҅Ư-$EElVj9*u63駟cּ%k_@!:??ҵ.)d%x:TUQQ6@9{Ы;|4OHOgK}eI89XoTk-o9|Lc]M??ݝ⢵[  g )W<+';L ٩g-t =gNHH.ݰ~~\[-:vΓWu4ז2P'M fr k*w qv=S__Fe! /mh(AdʥK,_QU`N[bJYSG]/@5UyY+x-iR4ؿo~}CF%eT4t;ݍ{lNcAYXhr ֊Nds9L*!,:f|?f]4sPy=W^}ֽh3HD7[CԵ lh#o=~_>(,f$]Q([{=iյuuҢtGnȻg8c\8ycAdg x\N`23Y22i)@0im.~ƆIe+DOpw~ժk6iD;q~z;p_\WQN %x9]< ܂a]cKkkKc8J424402^M;tޡ95`5q ] wu\|7 /[Y}͆qwnO|XWg.%y9,F2tPDGG#jC}`{3Q2`)XЩ?/O`-zBlJh222D9a,^Imk > ?T*t^fk6kGPō}je,316<fϚ5{ޢ+W_j{* ?{%6djjZz.ĄCxzڛlokmmkE&dUu u5ЭIdT=^6N8%Yl{VDRH$29!d1@|XPSٲUE]KeZ*:8+W._8}DP$[Q۰rna^:q×?ۿt4Ք@ΎqvyPy+(W況ZlVoʚz XeQbC֋;wE7&#|BAיGB۷ߏx-HA}饏?m_0bSj"###cS kVAQYt[="hB C5*ڄ47irN;pjNissWeT9Z|ڥ rg*4ֿ|7?yK0Zj{MѾ1 N^y068ppp_Xm(++mJ*;R|次pw/O`$ 0ӡ|?\H$H1ѐC#ch]̬\.'L>Vƺ;TUj_k;=z|ׯ_>{I@w6Q]|kWz.YW!o?n1l:`Z1 {:ۻ{zۛK씄HMML,41I,a>qI~6J U__jۏaÌo>/vEueI/L^^^v%S=!6vOV6B[ODR>2O)`ZFɜ\d32qo[k-zKo hֲW?o^<t0ur"FƧJ -mMu%TٙЙ5''g+?26f'{$Q:"Bg{=NNhA"c)t}Cbs#62L(҉, l:**Hz3Dry/M>ž&%g:drLdy )p l0&&"9~?e#+O?2OodCx {kl԰(e5L"dOs/K9)4NSNɼ.JU: iD T>H'72o!˹2O%WQb lXdH-J0Gho ¨BMnaҲcsC]m}?21#ά(O !MtTd%eUuL!i|:XJN !;Xhijj[` N̄䔔dJb|LdXp/\kZZb,1XK[=NL.()bE=2bGڱ}C2/^D rT߷aby^w Nݬ{㊃_{] ũ8]y{wѲ̪hrxj[{[+8*e$'k3C uU5uM +90N7YZ\@tcUeNLcL%k~Zn<|2г2FQ~v:%>:4ȗ`gb!vOZOLڄ07]|"2/\s ;L5Ud$.9}bJFxbxZ߽/3h~rvغmFjxڜ?wt}Ui~F|cc`feD'gTTT29)Q^C %Y) rʪGR5e u)ADW;Op|FAYIHKI)x8;XC2PUVRTWPTRUefi'xc3 J*kO7bRBNvRwnۺu>J6ޑUmon}pv|f͘9wqշE⛟߱٫W/޹RMڻkrxĢ}?zT @R5(Zbpx;k}gADӓHnx̉_~2랼AܘZ{=z|m Z~vFrBtX/FppYb?OZ&zBmB턲2HUMCS+{Wb`$%V~zee%btTe$EwtUJ:T f`je %rdceׅ/]h^g{kSS@[54q OˡVTUU0#Ȯ6f:jzC\L͇eSJoEAdTm<c3y؈@_ Ѿggmccmc3 Ckn={R' W{MUzDR51Ւ̤p?7™SΘʃٻtnX=I vnݱspZٙoXplg{KcMi5\C9uhK?^[WQ<396d]uEY GMpՒe`x={VxmqX+P7OZM'zBmB g FSM [+*+X̴LOSqc^׶R`JQൾO?}Ġ夶 6ʲǏ9tHPdqW]ٳmJrSbF:`F[O`d5 #>ꈦ"" +eT|ѓZ&=?#12gfzd杇D,Hi/t_(HIxNN6vx93s%: QnqUcK]EEy9pHN6=-uU[v JcSzW3 IadS9F:9#k^hSpO+97mںWṱ}2w!pdZ\mXX:xM՝+@U.g1S5N;i|z1cԚWܸ=ɓxEkFGG;MڄZk@'gѫ:.]-)c&Q\AH2gɺ'um#}z?}c0Osc Ď=*&z€{쭧G,\rlǙSե UU5ux7 $&%,+K rJ9zTkvZW =+HS`]+[%2:{z{{7?"$fn..g!əCTĘCqx{h,ll;D+. s0ok6YEv~ U7Bxdei5! e~y+6^.nz{o_>S *߶aݺMkFJ9ft՗,^6\gʒwr˪lvY\D]0Zrbv+6<^O.cpg7{K @ x@(((PPTTԵu bcIiYyyyYiIQ~&쟆1!G[Q%mΞ/߼}@wmeYb2*6^TfkσgΟ\,̈qj(*(hX9xBY7QVZio&B> [EHG?}ue$z[޸n_lq5RdFI]{;wܸܚF #xy{xzy@$x@p wt$E&R [ % @%FVzC]YIIYMG^џBkbTTT22B509K75&t+J p2U?uꕫWF9{?~`=ʡA q[Yb6?$vuSn5Q:~Lxś#\ k/vj$qV8z'??ſt' &֖jZzBQ1 Ħ:pD K#mUyi #^:U"o_w;_`YQ&%-\O]AFJJVY58tSܺr0Woi$'++gj ]}_(z1z'OsoRs)9s7u7|] El^7޷[>rwo\ȟ'xzBwtpvq7"EDDFID//w;H.LZIFN>AJ"XpRM/  FeMM JtƂbG?}tAY#BXNmͻ7/630'lY|oVH:\Q܅K-j?.2{vX%5> ՁrFAyRdqao>|NJ+WG:?G޽}YiOCGGߜ  89:/^( ^&}囏f.kŇ.7x?<2tjպc,/+mo3/=⪫3oI%u=sbpLJNjgy9XI }V6{(/7.7'v_bM+6"u195ĨȨȈ#2\] gDbLB 5.JJR(5QWSPR2عUu5Ź(‚||ayȼ 7޾^Ӗ[z1薓[xpPJpb0蚤Z6dJ+뫙ܲP` o>}m+l:a ]zuf_;7d? ?kOZG'zBmB c53 BZ1QZRL+Ȇ;Kc5y"BxytβG:ޏ|ÙBjB(t웉WXZɩK}?mm< ઠٿ Eu5=o"V@%*$o>|m-oz-%_̑VX36+8%7Y2x`ns!cfbbb78,z5'Fh2h@ϫ]ܼIQ ԼPiqQ^%*DWTIQ^QYS7<1YU__fi1~n6ƚ 2oն*>Sݷ^`&]񺇷Z8k蔙9/8fuuV9)s/\(0B}HtqZbqsDUSN57WfD{9Zk@'{nd{׭:U98~Zo_;z.ee$'?i:jjOA[iqo-% s3SI6Z'OC/ߏLcŧ{|.,ȔiS<.q2TUSQ0tϭl6Jsk[G{rZf|L[QGTBZa!4(n ]A<ˇ>xTswC͙? AO+#N̢׮s?& 2޽=[i1V::z&xOR`XtB::Y^ ]KNNINII$%n.Nx[kk[*@VYVZR玗TQWP3u#G$eљ`f^[I6fJbGE+H+kz6-'%:@i_?B3GЖzetl3 V _OtQe KC--Mu%yq!G{%oڻ]s=oߺyق ?G'#U:IDO@M} D[CkKxQ 94z1HQqaDW{+VSicBx۬EoKȔgvWAZj(ijy^nhnim=sO=O`*F^*- {kb h*Y] &EqA&rG l:s'Qb@;q"ǎW3q OPU][[WS .'@"z8,L 5N[bNJ++*]lԔN*FPZKrJ8),.Ep} >'O_~]z&ݑѩ_? ݿL$m,_ICЬsw>w4T3 2$ЭVBoyitZ؆R͝^s25)>*4LpurVUVRVVۋ?iϜ  -XcM# kG¢丈@I Q%C/|2H$Vwه9Ο{879 d{`\vYn X]N\о0uѿ0df7zcDv?Ǐ{>l.0)ﻋ2bUt`ײ5[J:Wtzc{gggJznet%>r_g-Z]PZHg69w+/=S@nWVTTTVOZ;&zBmB@K=273'jjbLhEvx9c K,z ~q6%jmpRv?4 5eԸ@[#uYq{vZw-nwb,wxӟ3f)݂zk:WUegxr["2+ۮ^|񶭺QI4/,ҟI*dQ"cc,"C|.6fZ6muPLAzIۏ^<.(NvĂ +话 ѴޡWmu)Fr%1QE f)=+1o q7w K.mgu620.XQ#6U켂ۻ.v_R=e_ VEQA^AQ9l' &Ԥ %HSw%E'gј_/Oa^zU?%֩LpȬV)R_A˄ōT;ZCOJ),fVTWV0 3bw1r7S-=~xg-s_>KԫzKVY4mo(-%bWlA޻Uuy aX]MURʺx7 0<&>LS(DJB\ Hװ@?PW|úz@L?wkhȯ` C.p҅smiD3MiI*:I f3T@fo.|czBo?rzFVBܶ gt qKϣSϟ:{Whe$sȮϣo+sM/,oh8BWg۩RJl䕺QQQ'-&zBmBM?h:GpP K22srs؈ P[Yت߾2!Uv(`+ow+5T<)-yd߷}CҲi2fYqAF|vl>֌0u 1*GY2J]S2x %ho!/uBJ^ZPْ@?\ZQ[WʤCC14Դ4Lm>a1q$Jb|ltDP/ }====}dp^`? kW\փɥ~Ӿj"tX͚|҃ϳ}\d0:J'4Đ zf VOQ=;ܸݣ3.6pޝTө^WO+x6a3+MvŮNpE*u5UǠW~]r929Qs칳-]mqr2r PQQ'-=6E"_/ /ۍ<\Wb`;Md3+x} p큨#ҏ.g{ o{Ilxb;ZPM@'qPa?5i,&h[]z^?Fp7){-{%t\t 4j/}N| .qFGm8a1<,pY$x<mbL>BȰ0O2Qv!$k8|;?~pJЈ'@"{gط(%ֆ۠>3.Ô $O |8;)VdJ#ANQ\Ff D>Nq`X\d]HcqcBك((C,6o~@?2vl9ڏH⸍ [~Y.O[rXnǗu#;4 po名˯GH䴙 ?,"c7"6#Gb($DQY=jļyG``p2H^CTF%ҰFy$r8o wkl_∧,Ǒ8"Qd.F0Cj$tɐt"Sl'/Q=b3\k<I#jSO7y+A!}.'yV~ DQc9e =~?MA _ͅuş^?7'ys&$ǑnO;v^Y]~ҕK *Y@B3OBG'Gmc?A'|$/p>!ɐ.,,"s "t7f}K"! p;(Q7GxeSe#,yyqGmz["pɍ/? /=XzB/9C6ؕ;?3MUOٛV>_R>dIٍO};7((PHBm2[Ȉ?Yp^r’'& {t @07$YLBglA!I"S>w[Ÿ~l2/[-((<;NKa?+CFpgx OaDVY.~|ߕ0\Ev-@Hnr !opNM2k( y30Ȟ HP+r~`3ux|Y +ae@F*[~Ow+nyQ7>?wX!pq\>[Y&a1@'ֲ????0??|buyg+̜dDdzh"ǃspNf˓zt$N;Ȉdv"Zʡ,lOZ4ڱaHEϳF`F)x}u\vThKhqwwww.Ip݊i)Ur+]HI] 33h٬lQfccgb,oD!s3S#& |߿玹oTWMOKCzԙ3'F;/T%GZi*JK$VTY)Irvѻmwh$_νnOv&)?DNk=9rChNLT&k)IK(i;fdW7wuw7f|tUTu lL-}a1Ii9 q1aphn n^m!؄ĄpX('#cGS2~{^^w?`~> &@K{bA٭W}KAIsgpׅ(_{Cu%E-#[o[}403~ۭG?r/͛ WZ*s w"=J-gSqy^4Xّ 1SUޱfA9kи⺖FY(xH=??_z 6\l,mu, aqiyME%%Eq~V:jRd_>~}ڱd|VR.$')NO7f'}uU$$RGƧdWVWac*%swnH!Q[QnR=JŭX;z3gg._kȉ t4֐V0%d6wtwwW\p6F_IIYM?$<:>199%90hHŌO@hDlRZV~AqqqQ^fRd0;cG?1j?%BӉ~ȅ ][?ѾѧQP30,^PgooA{`Biz J9Xv/}i əC]%F AvEL8v=OݝAw_[]i "HKYV%Bl;N1h[ǤV7v ]8{!'#51.h,/+-#-'i75?kKK KKkMM}3;W_hLJNquc34V?/}'ZPuSyOR.NO\k)̈ v5T>@-@mJ {E8nXzY\pH;c$$۾sRc6 .p%cLmNN]i*@FYJ\\R^ SV׊Nk2ɶ&:ʊ Jz&6䴌̬DtR`o6;;CUee%EZhޱqp E'UT5Vf%xgƚs`TdձxH-eLzqjs4WƇ:Qy~ ]Z;422T8;+#WIrǦ&Fz[ {~| Jv>y|nvpwsUgF\js&.*",*v9)Msg~Yqhh +#91& RߴnچJUM;4*)0Rv|uH.mP?}tfQTd,JFE8 ǧf "u$O/^$l~}J 6#wxx!36͊,i6Y+8{}3s3S MeŅEe m9qi2;VOQێ0{6 tvRqf ~k78 tOP:ui992rR⢢"d'%M#[;{QߴnچZWPDbE̔Ę`W[}ϖ5Ck 9"?C{n/`G}IV|0C Ɓ2܂ԘBwoܼ?X~i X3-Js0>qN'҇ɉ+3W&*"ŅD=B*:zz; ҢQ_W]QLBK=@OGKCMU+(nB_dsO-jZ~9Q>ulu0o;#FL.vܵkW.# ٹguˮnj^+HzX* xǭ~a2K|=G)oN v\O t5Q;I%_8"+?/_<8bc(*ZH,⟲ hjq&*jvܒ ]訉 BFvI軥Lڡu_Ig~zmPׅwk}ei!>nN^G"ʆn/;e!LX}Xǖ49O_\kk^T98695>^n.%ȍ 3:^-3ϟw6fk)ܳ6$4gƽO6Ug/} pGGԨ}@xꍥrdztw3Z"DMw|Lq(챣.J6`9ysbN߽qx_cabJx8xE)1X%ַw:Sül$I_<~I%|k8m?t{[jZ2`>h񓖖S<%fA󿻧0?7;=%15~^N֖B| ^@P516B/4&9h4f57ҡ?Fd>3gy4_oADBeE89l?ΡW=zgxQ^3 [6 FnAa_קG Ԕ4 m=BsʛGL ӕ>BQv?wj(Ɉ pS پm'3߄[O^-y񠻢(7-1:<$ EYHHbgkckȭlhjh/OtVҐ9~M3zmU9~v|7n?m@i-rlSMYAVTGaIF;wޚh.B9H (e7Pkg,WO cqF.6 myɑ&jJr2A啵L Ț־ѡHwS5I>vf'_3C痷l8Xd&Coۖ-iXw?}qo'/#):,${z@ܼ|1 D\|\lltT 4$8RZF<nl(-).%"(P=zkPGuv{;>I<`>@ۋuAc3-{(=o/\j.Iv2RE)cpRA]x_'j:THݺϪx* d`f}>ebOs{Qj{hi(/''nWV]SJON x@l-Lĸ89ySAAA7- 747PP׷t Dd dNVZRLx;DÇ)Y ['u='qswPjd2@YR'>S>O1Pg:}`߿:_xUd`}pюLc@TFICGp{yZ7+ݮB?"h/mA:[l's{+#~XgUF$,'***HG@zZ*4&r /ww77wOOohBJz~YmSGwoow[CySg|FicK>V2L OƧ|{?Eq_on$2#ݹgkȅ=/,у7[`&')..r vHdŽC]9qAN&zvat\N2ҡokIp3PQRPTVՒ4s X{ȅ• >kKS#%la F/@m<[@\NUUT?W`m*@ygqKIx\깿~ہS_j(Ɍw4Qbebd~ )Ip^̓[I^8)QS;?٣DQo͏ud]$Eeu-]Cs*ZJ9HFg'lFkok\4زmϏ,_#͉ʌ(?'KCM-m}S+GwhtbzNAH{GWgG{[e^^^~~~Y "aoOW'GGgwߠ򺖮{te%%4!Qeͽ#cMʼnrL4'}"{קG3L)vwsy%?{`K % f>~\iK u3ה>~mf޷>M~dvz|`i.$mVPye ݜM P}  hjz::l"*:fAhW7 gg #~6|4gN:Cw^HU7|]g_~mvdNtZjˋr12qL6%7^sPLJ7s Isg)PXftj"8'\̈́;ˋ K+x jzדb:rqxlXCE:;?L%:q޷mC>0$sˇ{[|yurOEMXI)u O)*Egd&'Ƣb;<9Yqgp,`(-.)nhU7:91Ud([`U*{˅Ŏoޖa j,(݅MEA&J*:w7F; R6Fzsw^>ʡP; a]C1vz T hj?}F'{ S5m)NI;ҦP'C1ёfm{yO< IN*sJ+ga?o}iwaF,Ɛ<'gH"YZY[ 50Ձ ,2RVNyBP 10V4p IiiC mo&+.!oU:8>}er$JM޳xtKRe ŶO?^*,k} ȸcm m}3kgWۯXk<헉uKuI>MH}ur*0PGїPѷ&dԵ4䚓oWG[K3#]5s̬ ^@PҔg:-$mb熾{pҥᖼhx'=-=#}`Ri7I)]mȎ r6UdgY[ZqZ/GN9Zb!w_)y)(ٓ'&{ӂlĸXX8$yy4SFzګ"<-df'&.O#Ma9AZY1+%Cgۖ[ʲގ:̔tj>삒*Ɔ*dZFFVNN}FzzzZ "!&joa!M- f7rkph,'ˑTsI<;?;9ؘ/@GN:~y[_/-0>2IZc*"xbiΚh{c]]=c+{7_o^AIJGM^Bщ٥+};U=s;rԥ/gkS---]=i5#wpb?䂾H_Gq??şu 6䵴44hY%܃"SPaqlbb|d ?' b+b M읾v3sk V: \'=H ;yCt4?LlE9#*ǟl9*LPb=vji~вo$9zqsʼw̞bl_(HӓAVe؊/myq6%LO]IQ,#K`"T:\XT\ScR|))Iqa~n:,="U8]CmuE>}5{go,.Oդۨr2cd?ës{ q6QuإI]۪-"EdeoO yژ:yY?tfDsiZ'3#gʅ~c1P&;AMGKyg_C|],tu4"SUMťee%+OrEPOGK Si߿ hjZ@``P4NCV6w MN_<~8739."քKHF=<[n]~e?HEũg?~yLO b}LT%Lo'9u҃wMt'z2R9CIrCO_LUDx+1}~|QS}fy<¢W]Yaϫ4Le ,{{:J= eγr :喷t7y[=,S]C:˚veHA )Q#]uWkSc {q_:]1Taa~qg4dOQSsi(#`ebgh,c^TS\V"dNzRlD*M uu4ՙ(h@AA7-6zmC- N[YG`_ֆAÀeknE?+0Ba A Ƶ.u?ؾ@v7) ;vVaضkUpf p:;w@",W^֝Q'Zwm[_OBGa@GpOkPpp[mp:.bP+u SIP(t 5}? ??A$x tL<bp\ bG< _Oq2`R?XGF=$[<R[# {pBm\#ްv?onO(PL-a \PjhwRtx"0 @RǯE4T uq/8vN8?8{b+?`>0R8#(-?H C ܉.ZxX j;QCVw_R[BLyڄB9P<ǀr̬@1'EY:x8 fܬc'' N@RDM?f?Me? i$@$by]$TC8\?|=x-☌#|JTFLSPL!75ǵ8&3F}@0#qp9j$8n??T/VBO'?+Ҋ( -P%C_(xvX۰qWSb[Е=?iؚl#Ywaqjos`F &k8F jO=Ct l)Ma8 oP'0vN*#a)k gdXA ??onC@c2Z8.PN,ת zk ]%Р_{ { }}CGWX@HdlRZƽ _Og N9;gѓ'KfF+ʐя'ؗg ?(K00s +;@#ҊJ|5D9/+/)u B󣳱,+6HМ,a_g?jΎu0P`BF9|1{ǭzB ,%ɱnXkkhhhɋȨ;xe5efg&DCnݴ41PSR'"&ߵwڎ%RJ +Z܄g>黗uXKPJ90ztau@`Wkmɽh?3 )~fZ ➦3dkBl\,{z }l5%yӅeMt=zh:=NU3յ}I<ʦX{;^rnWCAτϚJ,Z1cG;~ȟUN2?zF"Y8 _˅瓽u١,ҚV-mM& LD's˓yrՈKa`p"7) hnhP_ïL=~Zn!L}{ jQÇ9pᶺ0_GkS]-MM->Qy-аĜƎ‚ܬ_*)˒_ߵڎ9YD$Rֳr񹓔W586;A0xKk%WX]~Pi IxblX7(o+uI>N2 M{ õ }ccCUi6WIϟUR(.oL7KP03=2j8/)^>ٕ=Tgp'5:9u3|S!8w|{V~ɺǹ7;?;=Um%DM~hkbg^I<#c<m̌x%U]"ӋkJJJ 3b&h*\k~Y׸eԌh.O Q ̄{eyA?E>,--ΌTf*`V^d%w]U-–ѧ%Y1agGxoeiecbjnaem('-^gb I,4/=涇ope,KF?;k)Ftϛ#fi|+xUE7F{KIjxGdTNU'{\&8}|=>ڥ)uG_ʸa͗cTvqmHY~,,M ?(O q3QAGABD6l/ 뇏[~f!iN^iUC[ƮҒ\8!^n7 u7YYiV#]^@Q:wGDN|jvatIAvZBdFh Iokl{PUl! ґ2Bw\" %9 3/" K/ilj(K Sd %V-v*&(/݅Pw8p_ZNe&|" .0V:r|?;sU]Qoj* 2#C|=ݜn;8ptrvuu05cbgdauMuKŹYJ qӼ_yC%eqٳ{R|}6 iJP\NAf;:B7Z&O 7eH;S^`3ߖZo۽>Cxw˹ME ~|dĄZz&_#Tћo{c4/54\u9xEdԌ=+ZƧ+*+ˊ 3lژjn<& ߵ$f|8I)*h*Zfw0k-^o+Kr6Qg|[|97ٞd)J~ޅ_{&;2FEO,Zc}qSe.@AJtmcT5?=\uSCas`vT)wW!.F B$D.qk}_g'C\-YMʯi__ɥ'Dy8Xk++JKIߵڎ*s/Q3 J3z%Վv Oͽ^$/39N .Uնsk~INRUʱƦCvy<38T%>(,WH-IUXe,|RifFi.ACf4/դ+ [zͺ5:5=֒f = saãDҰ Ԥ^9zK&o/<}O+]%Y)u=~aJ-u)iiV rs033sN+imiO e.%$ev7}db>f&CGߔYgew=O' j~Nd(GEJSNm\K)5~(W1'M-p$.$Tٳvqz8^[  o1XV<{݋ on)02\cT1tK.{(_$oxH]EQnO ^@Q?Gp2-+UPvtSW?} 3%&<`i'nq+,}0#.XEG[˃'7/劉|^zd!/X"ϥn/2uy7>Ԙ7ly~5/ZA_Z'ot,Յ3?$f׍QQRS1l4ỰggT5?9tkv ;;|c M)*R~/3:11>6*<$lgac`f|;2(/ ʎGV \ԗΜHȪXv8m_R՘=ԢZZ?~r>Ew|N xOCIU#[0,xWi"NOIJJer'.uCؒyLqF>?UfQ%} w?UCcC]ueiAvZ|D7]N/@}=w! % 9sdгO^,}׻ƺeY)w] 6ޒU6qT2T^Qy$75*@IADg^2FBJ:F_NtVبБ?O4rQڷ[j<Zk;Qݘ7_uvyi8ZM@VfPjyۖlGԆy0oJul5Ҙ7/?e0_2|-t%ɉI)dtl= K**++4T&&e%$&$D {j,/ţ`P6Pz/ b!+x;~H]-M:3ǎ+YzCsiE}s ff*YyU, }}9&/xtru=ݑ5)>dEMƘcJ(j ~\y3;Ql 'BCANFZ. _Oy:Z0RQRӳ z6L.}6X[]^br-UAAAw-<;{"9!$qqets6b`cn(ٹ3ƾ8uy:=Hގ6> )6>|~ZH1 *ɪm 4f"8/^!U!Ov#ڄ䇹WT_'.>)hJ\ûtOoxBzv~aaA~N]~vFjr̝Ȩؘ =59ID * J,8ŵMMVܵf&9uؽdN[Nv#]_|8aC^ʕwKrOZӽ)L+f׺6S{/Zf!?AhGKvZF_>j.ђfL3{=ޞ{@WR=8ɹmiiK 9Zk++؂Z)vzmG-_a"o߽08Ż?~kk./N p2RG2 OnMpqiiq޽?WK]ܗ&{{Θj{ҋʬ~\(K6fLwoZOxTL] Xu9jpaOsEAtd$TLJ^E]y6S~TI GHTR+;F<)L\MT د%Lοg.E >N6: =??w vԜ/^$&ce̫S5s;8P[JrRRr6ް̪BxPuԁ'/kJVcr~[]~5_h}qY҆D׻W_nMVu`\o6g/3RQn~Z+2Y!N;Kbo 3pHz}hm|rb&'b)/%($m{7%;7&:2<$fgkgkhm Kˏ s5'0A]z^f z`§8 ?,dpԟo&{ҽtsk87ߘ%H)9!BߢZA"]Y3F[KoJ\JzS6 mprz%7#1 q1Q%M_/4Vg&ń{o>TSRk h;jH.S3pP_3Ioy6hw_S^ u5VbQd#O8=jCsg ?|;;Z)HGw EaIAWk3ځVg+19qLYzo#gP=|+,v)}]#G6ZB,U<#bb#C7 P';kCCc"‚i7n'dUV_=ޚ׌cdr'hhHOqտrNx4|X)zsI5o7F:i3%FG5?3 XGk)qfr|zȢbHǷ/fPFOGrSZ>(IKz;Yi)Q=OL!e ήzG{Ը -G*J5 ;F]haߋ+?h% ii(-.*r]*4wl '#>w(g{'$s'ӹyuyqwE8#)ÿK7)uprQ=zDZv[8Ӗ(Is}GZfqy.Eyg2d#GwuɮJ)DNvDs9B$gf% ru2751 ͞$!!-ebU34Vc"Bsq˖& d>pv"]ֳ@}JB9SϘ@S &S>!or$~xI{?_%)*k;xsYJQoI$IZP.z6: r\c`b!V'gƇC=- YN9|9rFAYԪw_X[YUxXWSUYQ^z v3 =dӑl:Cf:}`e0z y@A  忌?nD+Gc  X(݌e .ql TAl#GA ۱Ӷ+yQ!G L 8Zǀ8$@6 6G6-7Zǰ݄?nDڎ RU(*o2ȪFe% c@BXy2V9ะ8b:qE PBi#Q &T@KG-)$ X5cmW'2,- 5E8/`@XbQfc'@ W7XC_e`GL(ڡyA l` O=oZaw'+A;T-q*F#rc16J!PP(܆W|6G m"a}`(b abDCrug(l] o1+MHzy?1(c$QonYP:a艂ArEn>P7:)B0vC[à?B%7b b] #E4EgxDFևZq0nւ-C( Go+rlh7;1n֩ q)&B ?bvQ5D??Uws6OtZ =(:ba+aE_NJ>| I A܂_EEi#aB] f0J[lCkz68 :J`7-`2ƣ W0 rk"Hn?Iԙ#?h0Ǫ;m@ mzdA:=(oC[ BeHE@=soH*Z6.N °^E?s[ F@J`l0֭JyUȰ"8u+P0!h"!w1bA|#?{$PLCPv 2/7`Gy}ב~0{('1@CQ!血c+mH H?K$Qo@aW0@B>0 !nPT /渷~0T͍6oAl@@1ZE=ztG̫[~^ 6T`0A6ԄBQ a$w,Agb Y![uDQ&-P&c*F=D\Eό%[msU<ѕ 9UCA⿫mȏΠ#!ݸ >PL@{C$@@ހ T.o`]:te =S!n$ ֮ Ds;@=΢ƈ& I?ǘ( p2Pt$ m2!og^>Imd`o^(~dY@Jӹa? PJllP$DrxS۠@aPeS,/@As_\w#@O[ APbqD7- ")P o(($-(̧%=hQ =&ٹ7 ( >JCF[`{^?nsG "fKQ@@Ynw|LV޼鍊hR30.as6lb?@A00 88&kȀqz%P71Ezg(c m%Ik 39`+x]w1AC}muYQNj\x a!aCYtݟgU4woHxTK^|%Yi aEŹɡ2WR{[е(\#Jڅ&_U*7* S12XDYUΫ,n-zw6V%ODq<ϹOk O$ %SW0'-!Y7䁥=m5eEy9E=㿞@p ;%IC/&$o*+J sw0U-)*$( x<̊5 QA^Nfw7f~.gSBgq~j&-F>\w.G>&II߷aN+}%| ז\:k2Si5ks5-;'|ܵlN?uIH.(]oGEkXRqf\./̈ ww4objjfniaekkoej㣧t玢gɕԔpOsU ~C\! +'YƛT=ȕrѰs&]nHvUbfkWӘQ#hhb2s\wFkQ]Ux&¥ KW@t.x2WoK[?W8/#)6/@WCU鎂< ??ſ_O@Stҕk7oݱ hʯm01GBҸWeInF%,UIMkYUmC/z*^_^w蒬MX(i^Ss3}Mya."G pZd4?jw53xWb)sYCb /h,=]x<8mD(z^b!V!-eՏk z+#L-N&Gx+/ Çn.Tl)o[*jKS] 䅮\`~TMt8t w@Hԋx˂ܤǠ'|< NvVf&FFZʦ6!gmJ5K *?wGDWy?[Xn٩ʗn.΄if$&WK_@2Zi:)؎ ŪH 5.kh}/LXhʋ ^x馶cpګܼԸGj"߿/S#1x >~ [j^hejzW4???_O@S+z*GTlfySDM u9i/a&ڊҢ\W8 q)pk<+lמ0?47Q0_5=39]Z1+<(Eb븮HS8Ҋ;Z3T9,xm_oz`ݒluwk?^'!\;UͭYddǏ~oո%/|KY_Tl\:/.!)**2<䉿7q{cLSSaxrqgzҼL 9Lbw~iѻB ?wf1E]ƺ u 80]f ~xUPǝw7p7Uq9o("%"j*#ݍ%)!6J!~=ڮs͠K|NL3ƨ={Ubr EIaζfFzZj*ʊ {=O-eN^! rZfWpI^Wd'G?qު-Ņ8.ߤ8)e\f̧F"mչDrXs^ţxa.Qbިg;xs &tn\!x041]Kou.:ʺ4o藊586$ pt o4ݙ v|su9;x(C6ѷ-}~/I00 ktPcg8qQSWȤ’Wq.ϣcG=ݪܖWU0(l͋6x]H6(yh0վOVpe1C4^h'^G;mC97=i*?y9hSƓYDB}<:7?8ge[ J=eiaj%eQy%%=suOcSK8_bۖMHzK]MEq؈G&:j*wAAA,zZ:;']=KG;k+K_&Dz>0Q`yvci[:y/k{gN~DKW< '5xoSg.q>Q8@j7('>:J ?jjz\|3cM6gKu.qIz(<=Rvoe#A;Wd!ꆶ.Ÿݢ~[#u !N6Vƾgd aUCW~^K r4Q}$3e`~Ojccݯ3Մ.Б88X|PS> &X \4%Jņ³Y%/o<;?eO2%"#RX[3Lۦ:~1-5hq$ }sjWO0T;}IHQbEX}euUEqvR H/ M,h9&*!9pͫ҂}`Ntժ@AA,-OPTJQGPlFiCWYIσ-TdD.2 lPJT|Y!"X"gS|y=K? o-q7:CM6'V8_}c1] )PNi^DZnFi <fgZB4h>khOcѷ>OhyXyœ M=+&)/@ qܢ^L qwszddly3]Sgπ܂'>. gG>Qqꪘ$n_Sa \GEɩ单kYИoqlȺ&ύ7qJ IodN,}Q8w]~sn:}Oaitߏo# HzݔGhYMPTSy5d9DӪOԽ)/HMt0єfcz?2B<4Dc8~ric1)'>twq45To߳{JET,*Ʈɥʒ܌p;<vt ld0gh!<@8+!J_펜 &n9ǹCUiOD.O?\9+G9_b*D1azb) D~9J sTTWyv;cg6G&?q?!M4ъ0bimT?7^qMV=02.)!.cosc#}}_5 -~`[ ,]"ӊjj ^x_J:ixMLwl[fj>si!%&:si$oޡb+mON7NJG=J>_DCDn=QkGcm`uJ2%Ab^xU7?)_͈{4-Hs\ZO3?~kj.Nu6Vfp|w2]O$VqH_W[oo[Mi Ck7슦oK^׾ʖԘgnjrb׸Xں?Y!q+UNvIMYNR#Y)ֳR&~z5D{c!mwuClUu ,DK !3)۪6fG=>EɀwC8S$Hzm"/Kwnc=E^@Snht]y$%f2ʦA 9Y)!&zz!ZB\%oqnIo&t?tutes0- DW!| GN(89RQoQm\$H,f])bK*A4 ߽̈́DjhQ`Hb}lsm3e!mKgt|%ct̷0/;C5y^cn:4,komkU^w0_[ |Kxvrԏu}fq05= g?MI9em;_XbNEV,.TTULzh{WJcwvAtuy[W;qI;uM绖NzWRz~gBy *(59‡Wg3ǘn]^})2ۖJY"N鹙" 8UNWW_Gz.t KϽzw%ʀ|иO /1>s~(~lvp?_41XZj骫(JKޒQ4f(KS;u\FBXf-k&1Ӝ{ <&0jO&n՝ 7{vKXFEkK-Oُўb彥fykWcZ$W&^( {D$$Yy yrWjۤjzcI!e0;#1&< lgu@O' -I(9'nBoUUʭb1E?XSA: L Mmk~g00ksC {=O#r*UVVnѿ8e`WK}5ٛ׮\:O_7fқ'H; R#\-oPwWTW2BYoO>&}@Bu4,Som)|6 #)P:6nLL}8P"7.o)H}x3AVqI:G~?t޺x0ˉo4 +t̴k}\D% `1I1gOaNvFzԵB ^j~Se,}z=k A8G4o-Zn0䶏Mv9m[vs:9*WԮq]0!Ǚl;CL^bIPjos S#n,Ϧ%p΋@<>\CC;:}O{cwnB'9w&WHOS^7޼@?>bI~vZbLl-+Y?r*:VNI %9itonՏ|R;9]mW;[ R}M4do \8~U!|`UajOe>m 7"^z :knbud14$z$# |e '|Db0ovIY]Mj ֨<,q>Y@Nzk F~3˗N|1|]=I%*ۚS1/bŸy**ݳϬxYl%w aëlvUc1Vښ}/_7,Η=TA@'e|yu]S,6.+4!X|SW6Tb{(ePJFq3G,u*•ѺHJ7YT2;íόw1U;Ar⪢Mpn$5K#}J r];{aIXvAjv3 ˫*ˊr3RFDEǼ r6׾#)*,}%u`gMYZ^e ~3R7*gG)(e2yacoX'#=>7}uT%~sp]z7q#To yo~:5jFp""-sٞ4ɩ>m0+{d-G:ܢ[Yؠb40ǡΏU9OZr"ܗ/02n9D{F5sm9[Y71~gghԜaP-#_PP u2aP( < 1ΩНN;G 0)py;W ;>>_LX@qbBB\uc~ p؎ 2;yO׆P!1w +X`4H0 NJx^ m/30ve "YP<#&܃d/h>P ?B`(M(70lR@E@}L\{ b#ʹw`)%0 'Z"C# ȫAۋxE??? ;9rXQ`?vgtTC@#JETxN_he(]* 6Thv!Mz$ č|B(Hqah ?:oc!o``7*=w.#IPTa[/w8h6! ) ؿ^H9PL耘Q%G`b'~an{8f_QAAAa{pۆw2 HM('p4I AS{g g8 i05;+nCQZe`S s":4@#RwG ukhJc=,4 {/)03(Ћ*j9@0ۣZofG^A5>0,m~Cs`+NàXʃj1s/mB}a0 o06TtC |R-= (wEA>2DtA(Za/ Wro3vB0jKTqP8`LR#w%=;&Q}!ZcuE_8V;r(=l  %96%Ԉ ahBa|c<(E `ر{vNc' rL'! )~M6PJǰ[&` 8خCڰCA3$`إD2>4w‡/ Ek1:ǬLvح @@a  p? {U%rXPR`5Vs dgK AVS0(H 43\E/0[h;foDnx D F]2Z ӆ^Z `B"]YBo@6g4A`t??= 0fn/ta?~8B\|X DM#$V8pUTt }I @R^{ &p_G-o3 08莨rĩPV_ŌbIzCi@aNDnA; YDp[oM:KD* p6X@Bo gmΘl6,?#0d *7q'!A 1_8Zk[#C 4 1&0o@`\tD;-ۿcn_{ )(Z`9c ZAAA ?pW਌DTON E%>7qH Xcsr+l`l(&(8ZvGt\w,_k#? G\v (vB0 (rCa 61%T5`t { p۶5c꣓X#z"H>4Qv~ ˰HvO,0,Cw6֐ P~}5@0XMpiPz!!? /  @0? jخ n(85l {p] U%YU DJrda?y$(*ਿ(Ƒ" ("1:0a0"*ߺF P~ sT:-{L=h |aTP(FP+??9P,x}y<_~(PTJR*K}wʾFK}߲B,!IPd_*Ki_{g>?׼͝9g99gM b/kZ8eܮ_v޽_w{3sl?[^ߋiݣ?|ZMPbgJ3#vJb<0X<PYR94'594MMp=gLwM Gɛq=vJ}X0[~l[zrzLVr\~dMsEC_I>2|-lBrqQY-u鶊dD^o}@JayEEYQnZBTh _ 踄ذZ| sKxq?ɞ(Eg_ep^E3Qh&pG*2%iN,VR!u2r]iQz`+ݼВ=tbDR 2ȷ}iA GK> ɍs6SXB+dnC}ޑ'WM/J r020|zU&œsg& sғ"}ўם쬭,!##oXZ% 3KG7ش[^6)+[^VRv1Tfcjy98;dWK]M]\m˖wK҂lw0pidUY^ͣ‚#5B/N\r$ؕ ԛ"?H4Lm_XR3c_o^WI^ i3-㕮l3'o S?p.C0PhV Dom ' cDvyUNblx /7'{ ׌L.[]I+{fbW{ZY i>ߍ=zM+{sO4i]a,L}{ZY脓AX\G%,֝p:vtQagqgn9mp~y"-Ŧ{Y17U$YƗ+~c4vQIj96'h;<.lXr՛鉷iI1A;X[###oXغZut`tJ^Yuc{EQIiٝIQ% A.ϟw?X >pO[VہUNFo>rFno oj'vo5򾼗rRP7y% rϿyVJ%ֈ}Ϗ-՞^pT㟞Zxtg''.m'.lD3L?oˉu.Qcvص~@C&+zIvK6Ԝe?EGFZBBJNU=$yLiOJ=4xկXgy(:?v뮽d w_\Di㜾zˀ]دW-M2ԺΎ/y[2.$r)=7 YϏ (zC k\VBm:'L{3zVgD vvCE2q'1OZBBsZLʩh{;,7+%!:,\濓'??şy 6kY9I[ KoNt27P .:=8ڱG`YMT_cbIEN4h!zh]"".JsБZa͸nr1 _2 MSCH> I (nk)sq;: kںϓ YQ q8#I/F!G+w{w uu[BTRFXŻsܵ#&Ȟ~^?ǞgOl[:9:[_R]ÊvlTj0Q~vV;]If!Ϛܬgefhc,OZ^\ϳ6=T UːaCJg?7f.~?PDX]yܖOW&%jk-r:x0yL9h;]yavƆǭUul}KBQpf~<+J[+pwsխxoG#Uial٨Pw*{ySIrBTX/ogawZuK}#skGW?ҒԐcC;2 ٶ -'8uȦzs*貁_h 5.< d>DS+E|j6*f %?=7tȎǨy)Wa'R3QMM4y!վc%s;= }~an뱃dFO&-^Tn|nF`k"}kWS|:֞!I M͍05g';+}E z@=44b ǧ'gތ = 羽mp`ŸbL"J:{޾}A^ -_ LœE h+M9QEMsi"Pu.*?C'vH64q9t*uʧk.N~AV͋]>l\zI]vnrYȱ^(= uekДѷ4%Qf,n&5k*ĆxoE'$F8,|I=B47VsMN f%|tӧJ3oCW5uLN`p-oW1Q}/ NZ{GI0)FO3~ܽ}73֣ e*8Mf Y^o[S;Aw Eߛ\)j^?+q^OԛʼX_g3 yq!>nn^Ο;0XgT=~x/7:"xwu`og abjm檻w`TrfF|͔ /gzRl>.|BEsgRP{?8<-QW=^76/QAY]O$߰ՑD!`?~'I?aJ=I6X^UϑO,0/;wI}խN=rʼr_L,_#7=2&uxǝra{]ݽs:ֿ_W'˲;(f_4flͲikki{,Hq1yпנ;+o좿 KI]OM\:Kt'豎+4Ezjڝ,w%';Ϝ>UXsJM:l}K4Jÿ -M|&/2;-SCC/G?RTlz'暞,ϧ?TzO}?{|?y[\jQr#O om}Pn,#*' w!Ay]+UD<ݮ]u@GG߰غ&v.7Bcof'&'''Gx]6PM;RJfpJQ~}04Wf9LsGbj%Uw6ipl"FelşNW q}oN3Tm~@޹ΫoӕEHsY3rrQ}[0TDRɂڟh#ŽySZcDvU!àO6mTAU->>㥧9.k 䔗Ί,M MʹĄD/{7O=+d)֖ץ~g#w0ӚmAN}0lq ?ufo7 OIѶ g׷24/.zf,|A]bs鍍'xp>{Ha͊XT"%vN|e\=EJ35;\f(p|[dlfeS#=8pU%ޙѾ5%iɥ B"ltlSVޯ[R _svBGG߰غv[k+k#S˕gSbbB}ٙiFxcr@ګbd.e%ni=S+Vr/o~,aJ,T"5U%Z9Xhʊ Ik8DT/n:`[D3UᡷaÐM]p sjƃ=Z*EPw{J+jP:^vhc6o҃8ˡJ_-BwD%y|=c1 C8:Y}X`nӍB5Dg{GuCjfrͿ}(RY 9icxBAU7{{t|hh}o-ˊb"+!&*&.CL?*%₄0쟳 z@274rrCE&D`i.lvRçEEl;#C53=>8Z]0mQUSZRZ斧Zx+02ʠJU[uJ_fA_MHQBxn;53}|Km>xU&zO;ݥw~\KlE/{ǿ*>y}-_l(>J>Ef⯉bt7?v>bh?H+z&֮耐`?9A%c;'_=+0>2yF|MuC޶)R'L/ s"*s~f.O#gHۨ 5xGpWҧCD_>JK8("~Wٰ!>!YUc/^R;#fb_TRRt+)<8뺋??ſz 8m,---t /8{D'gFGݜ/k Gs[+^:nF_;׶Ay #H=Q*NN_fZ=ŘҾQ lX"i{z?OSfhrțA ס49wY3Xȱdw<љҮcOHk]e%{|G~]QIJHn |r9L75O8w\@GF%(^UuսĤ))an:^wۆ't8)>.:4KT^2}_YAt}ёc;^X1!$nc*ciN(9# oOM6٪ۚ/:e껏_>Ϸy|{2(h{d+T=&^6IůnTL\n NZ f,bJ I!a9+N`PͬۅC}рEUeUuD z@I[Y\r ]Csk_XlrFN\lttdX߲clgn}\Za;ہ')Zg(wl J(ܫLwőʜtTKfHdvʥ+1M< ? )j>>\I\O5tny=;)~\Ϲ &ҋbNIz$5=SWӼ5*2Wܱ 3~ {kh}qŝ´̜̤p9q LjҎR=i6}Vɖsb(UܐdLEERtu}0/=[a⪒"~'hSՙa*R٦{S ~Xhs|Ľ">W|{|<4xSTX3N7OÆυȫ?2^xڷ7122^4T޺v4PU+owVNTDh`czObjZMLͮh[:#hefr c@Þ9ڳÃ=ϫcsUaCd$ai'%мAe>XPsnoL# E,ŅaϭKn{ؓĎ R`H VY0$;m@lv,)73BΐJxh#wmqdfhB:BgMݝGKs,̑ H67U.3?7(<6)=+;-.H_n?~-.c'Q#h1Sf G?mXq^O*EZ_Pǒ7׶AT韋{zec7)YpͣJcPrdZy?L3R~lackOv-K< $Xd%Lʥn繙W&giɷo~v&zkb* R"n\1չ_yNV/"!-+/?9*2,8Z^_H7,6=!+ ~`P(_()¦_uX䵒i%68 8 }F-7S`/r "ByZWҢPPU:ɲk˷An J/Jp'6 tx\IגK @^"Jb۩`cIKWpYAJ_9ǤY?F!: FoWZU}h DUEu_]z\0y JIoLb"hXQ1] ZCl??7QpL& N0GÄ;,D'MI7*>8ְE +WY&pP$ #*]W~!Ak-kNPU/GGG߰#ƶOo'_]cW{PNL.MH 2'b7VRaK=UaҬҥպWmyPuP:(VfqU'—28='kj#:??7c>!ub9Rk:χ}Qc15b9sWb؋`V8nHp*[P "+1Y0(lj??7y`>W;~M;A} ~cK§ cߍ+2^+._ì^ ,Iaa#('4?6pۿJ7 7ŏ!t9a+%k_Cc(3;\_q=jol 9Fb!B;O@nMQd#wڕ{* 2.k&>q?>n`EZҰ>p5Y:t^`W[o\3"z~zrpS]c@]c)֢{vre\)K ss1RUVPTյm~؝maiZ&{1+j=q]`a6CL2`d3CyBb0-k֮ßVgm+~_ԕ?Іbz6xχ^?Ew!Ʊǝ g9E%TvNs:dV̾[&ʾm_eoKitNPlek(bh O-( a=\ $%dUN/[UBmIDANE M+*)͊^r>%/xBJ+24LS?nHu90p*_>}5+gVac+#Z;}HBAEʢq6c^Q\|lW4o 7![Gn/Sq"?WS!ńI4;f^OtƊ$}ׄO_YcQL\}[ 0-to|l.aSКhfc`ba$+!*xWj럩ىH9rA޷wJ^}w_i>MGog '2l>7n63d#Y񀀳#?vKf|;[2('ҦNM5Oxh^ yz"A#mū)^z3d{o-?'cbXgj5k$ɽsքފqv{m\mM uu t]|cR3/FE9qqq IYEseRw@--̌ ΝW3s J+Rv .*<qNC ѓgm|"~3 Z]{#}FD|N`HiCe+ Rf\9;*gOI Cuqrw;qhmϽMFcٹ̲o 1Vt75##M='wh]hL ttaOlfO:yyeRw@-Ei<+ih샏/)*/ |ܜΊVַȮjfwZ*3ܕNqfˬrdߣaj,v9ۂꦑ{gh9MYoa~ޤȭyKnOHKFojL Nf{#y'V߷seV]*izI[ԝN*~i§N+]<?:Ys^/ k/}B7&JvBbrR|t0@SITj`l#Fƾ/6_z5;XWuw]jc֜0!A==560Qi;󼕹6M~I^Zy>s= ϯ+Ѳ3Qd.bH֭gd^Y5'q֋Lӟ)77s\n'R/~tkk_&Im\z3F\ kY}:joKJ/,.KJKJk8]n|65$AnWYzw48$`ʝ6SXDzo {3F5kź'}늛1 6YUKn5QvaWW{۽XZƥXi)VCKJ Is>e~ӒvS5ّ;!6B7-lfiR?Ia5 eIQ~NRop\Z^VRJJJrB''zgDOK/[KBmI-@OGKKKZ&v1*>6"Gh3.(Z{%in~27k}8n>tiGo_S+?Zb p5\B_2߽6zgi:{lPօ{~LtBS}_4B_ \,:lRrzs'WC}Dȕ[!s)I|*jQÊrz)*o-UE4`'cK߯ym{n7fĆ LH+,.Huw?D]WѼwd6>b-[6,____jN꫽6W_@fgGBوǻt9L,=eN}7>Y8U{R.H\WowU(0AQs">%OӮ9\0ewba2OdZ37.C .Mۚ?9ͽjvOUYxwG[k +-=sG/+)iii)I/}(((P[R0TV1sNQbCs͑HLibhZ[jL/w'' RdoaJr9KVLzSn w5?`z|U/]1Gv s+^VYu47W #eMieo梾 yRdv_V_W(JD|Z3t-ػZ#Lz!=7 x0 r4/zMyKs#y" ?FZZwFjg);"﹑a$ QtN(j:9tݙStu+>=k[ 46igr߿ņޮ[y9W*2 &F&:ڊ\"bC(((P[R2PSS<'|FZEWzVG{nr|T(1doccVv"?)J O % Knd:g%c8Ep9E+ƻ |?7n89T ^`wXdz[-M͔F5t}q+X^oU_YќJ)6vK4EVI(IL`U#C653l°]8J|G9xO`^ޛ8̇<<6}(UX\8}|ozlKfu8]vwݙ>h75jȇ9&ȯ*u&&Kvd7=q0nHR,H󌚺4]YBstz'Rw|&lIkm/Uo'~y1q+\$mX`hNؚz/%\2}=gfvMcFP8p_dIc,{Vt84ygD! W{Mu]sK04"*<((vՕҶLyDd>c?GАR 3Izゕ7y:IC ޴VJ7_p@Б:Z'#_#uƘF53Y9=F>3aBm*>uD?߿QvN>s :.`TFi}{gc%WGk K}Mc{’O+S"n;QuנV &ڊ|(k-Yw#Uw*ʞ>!PՏ[6N>3]&ޮ8g'.o=ebe֛/~N_R vkF^?Js.0q[(G7kUѬk$W7$=onTcĞDzWrֆ;"TM0w'd{ yq :^M^/'=^h߲|Jb}PiY5J< /}FCg~ϩ !6Q':J5З͓=aS4Ƙn 7uodSĕ8ڳbu9]s{?6-9*b<4O!4jǏwdŇy8+\PT׳p .z /+5!2vq4PU>g#GQQQ-KLh JJ {w?{.6V'O{R"A/7G ss }-3glTFYaJdpVtdNnJ&Z6}{7pJւVt~Vm/]W~xߐdҴlK^gPR5u ϼz]1|ďf_OdYUhjFݞ]0s\6*ҹ[O^ UIcPyk 02GyS34UBjyX3'&7vx< -1*,*.)4풑Y4%ˬz9_@xs ַ\~yxfU猖k(Ih)Ѡ3CR95S`pwCtbirM;9f(\CoyA[}`ۯd#^o^H+:Ȟ~.Оqg7\uprί)U}feBtp颽+RTJYiIAnzbL!<4VWQݹkAcwQQQ-5KvYKMYI^b}x嵭kں*-MK}MLLu,r*rrӓUv0[ʳdx3R>TǢ؞^p?A*ԫQku=wwD -yOZڠḾhTn7Yn#R푨Ӥõ.'8W6YD).n+cOIb嶥>6=qlx㞒/{[[{Sm umSҌ s񧕟޻uҔ J3{#ݫ;osĪ1= iF n=孇'\쮴Q8iSf'sWi)|{ M^>7ϻgyø1172Q(H:s') ~Z5,ڍu@ `*-qlfYӭOoTTgĆ|7#`uafeRw@-k*)\cf~TH\^38j^y;vdž}nWHR~eAF&A}/k_F-|Pb(Fݝ9ߞ Y{GǺ[v [u5 C k#윢M~`Ztp8[D_F}bYUD:6N۲OQ7+XGwr/VyTo/ z60P8esaJ.['4^lͷetlŶch󮛼tLl嚔r~dxtrZ^*9zy1YӔ>-z%$kV~k`V^ښiW6vDWW&ņj,h>i8Y@ᎬKJG7W{dMkW>MR8OB3zCiLkֲQy" w1ARo}R5B|wgw| 5et^.J?{da-k:I6Ji;0n/8;KtuUu͍!A%B:6{Ts慙آΗeٿJ&E3/)Yff+^F+YE~跼l`|>\gv9LDgE>D3% wvXmQmo['VvU&o~ȩk##sM9|7yK0IRqJf2RR`gMzErOف+6'_ 1;%>2\&Ni g'|z.53$ei'6q ̓n,vf j`m.yoS_bhExjEޥ78anrj` ֝B ~TjaZV^qQ~j%UuiiM{狀N?WdլuWn)[lW׉2?qlH7n>R6?3meuWfϱQ[)n?Un!/*/cVRSC\ NrqrH(%7ut0+9&hiB9Ym;r@GG_.uڒFf% 4cJAFB8P[MEYEFD5\I&v&l^:س[8f]tӖwk/龹h1gw_`֌:ζ sʼn^̼1.3f'/qWHK93E\hҏg_R9/6_^p|{w2|+xjn'OKgin+XetU$VfDr2k~Xjuy$^ezB!4+xfFU㟻+w^fjt?P^|UCⲡre[$S]'^)u5_;Ikl~˗Rt^<9m{o='_p TyamepԗmWߨJUQPQ;iWddGa<(߷SJ N `AH:t9U0Xb ppDBIe  +MUm??m$j20u!N$#&) #}Eb8$x2#[H@MH5 !XFbTXjTR/D_ `ZNp XXʠQHC z cC?7An*/e?/o&ᔿf?R;_ڃ 0|QD1BICA6#r &O%"T*pA9GcpPeBGP 9 C$B bQ=8b9Y)8,Hٹ@QQQhdC_?b(8p<@: bAʐ>6#KeÃKAL a90X {jԗ1 "]pAL-@dtB1$B0dj 3#pqI???Q;ɱ@3@#ija_).e4la>1J0PA@d#YF*\D{rZc}`q NP!EB@ПJH#jF H""%#A 0Q y #/??,[:6eh~/9yGbˤFH???GFrI,W8 9XJV,ld0_M uR$ȳKVp0ߡ9KX$R Lq ʤ)!,x"paŨ\ K9|Gmp]̄>HQ Y,HSp5 4`q0qGh" @ÎxԵBj e@8\#X{0T! 4QQEE-, ./BSrS???2-?AN%BfjÙ. BC!}|f$m ÓjX,0 ɡ7"\@qp(Uw$JA N*/4# DXI cSi!@d7z"1 R'7zM???-) gQZA?)DW0&%(18ˇCX%JS!a8P؉"DJ((XTE,% qx$Q)R bB)Aӆp”,I)YEG.ʿ9.@_,䭐/KX^ȋxP@< z9FZDZEaq) CqAN*q6?@Čο !,;'?a $=D8H%/x%uqF; O(((_G([P__›1p/ 50mH3 Br)7G#A\2b\\0P.4{b2$@$%i@*'Kl%x4"T}P/SN:J@?/oC,Z `ʭIjP Ô'EAGȒZ2 ǓvqNN (GaH e$"#$Uv"RD2YLP,F!'V{.W#dQQQ-hmQO՞zTt([=S~ as|x|`|2 % &-%)'@bS# @lYhHz'/EH/$4"_x*uF4Q44T@/[ypce-hr.'|&"M D88+p2g@W!T'-"|z,60 I $X2Ary$WfAh㈞NCM#[X%/{@''* Z3䕧)$,] 9XG>6_j(b9@iv vg.v1H| ccZ,m4Zx CCCn+:"a/ Kf2NCT^%_ a )`mS d~IZCgE"+d0a\_().xyޜ뀲Wȇ۝'^{S?)U@9U/F=խ=Q;Þ>~ -BKEᐒ[dьKùeirI>;{|h:>qNOaTqr/;9cRr~2n"I F.wL"QESqES:ysN]R݂nfU49(?~B@3'-Miki߶m???_u XVUL u4nca+*f{NNk*g'Şs13PSf9u1.ٳ ?w'uI ee֯4mo߶g];+Ʋ_Cw<[osO&Uw]]$|OfS)ýթܼ]}ϞВdgwl=MYG-T| [X"Y3o:gU &N̸x^pb/FGkGY}_g-p6?Ԕ?&ϏdOb3im_/ ,*ZGl+.a~: <↉ڪĘ+(/G+#uGixevesU 1te4=myT%2\ᧉo6KcxQ1uIDv6}4[_=[RwEa{&~oGK6bO?ܜ~(-h%%y&F« wvCzZr|c{#DT[DA92mOX)t0gR]R^p'*RWF܇³{@x*ѴCͣ7=QOR{f<]lw&y!G߿YZW!gy|␸$cZ%<M@ȮY-}c?^VXɢzzgmhgzr{``lMĝwq8amiኽa0qC-UʍT ;et*_,m誩*/{|%9fFxkf f߲ ,*:Zb[2PжtEG?}qz'5x'D_Fۛk=rXd==kPW}=-#/!- -h{SP_{ ):EsNG4{mCSecu*ʂ ߰w>I웾 z{QϢ2(bPD4wnߴgUs㒙[kF\uP1l{ޖۇh=5hKmA?45;oNyػ鹏b/і6dS:5˾ec.mЊ_wۇ[\f¾u\Yق6MS)PÛ Q>vvU l~e'zjN]λ#@8ucC;e'6k )ץ K=O6r΍Eo<Oڨ W4/;7ApïEʭ'|IA$+9nG]CCi'g??j/@hq1nc%t@QgPlfŋM('-!WmÇS2 2rxӗѷY)qan'BU?-(LFkKr\.+yA>]s照ZM2j `8ޛ!ߍgۻRnF~.רVml+RiH*%"/j n=ƆcXʅ {4Ѓ?)aKZ/M}BߵOh_d.J`t@KM3xQ2`xܮhEQ#OfijIlnjl]]N8dN&ة߶0 'gNk)}75a3-?SS?*{sw@W[SC ??j/@G䠲95m_S/6ՖG?metHZVcWjߎ~})@BtN}v㜔G}Y1)%T?7m1ơ#R~i_̦ Ev٦Mgrړ%>[~QzqbATݍ'Y]mޡMO v.gi}ŸX/~r[U nQiװlaŨ4YUiqZվHD;7u8lg$ʱB]9Ϲ)G'T %ܳJ)Ǟ3s6UYκP:e~rr0w̅f#Q\E 8h91cq;EG:*5zuݒWƓVc{sM b@mn\y;- VWZoxjEЋ򒂬`3ֆJrB_NۺGݢޯ?7п(͸yPOGSSi'1??ſx XVUuog+*fз4הܻr0SQ> qb[ZMߕ܋ E{:Zk%fegܽr6Q{\2*u/'IF-v}$6oRyj%÷>'nӁ(ej辇S%Ng_)6Wdh3ߔ6>nVOS|;ٸUg4C< [D9:Pjwa{7ܽzHhL@CRwyӽ!-'U}/ݘ<\?шn+afSr߂j~Z1;mVSQoؖ;ŠA'S={^iC $|}E4c[.{Cvo= ħb?-vX/ܵ_ۗjnUbg3^T0>w1q19]{DlnSPmmUi^j g KOjVqM{킧cx3V?Hq boefg???j/@,H03r H(hZ8"5cÔP8OZNYEoƖ6ocl|t8nos3,,#51*Zms7AZjuS;Y(SOp~kϟ\W1R0}ю Ï<{0}3IX/>yh=>C:e5oWpaK !%('w %n E35ÖvhNP\*0нxREާB/^ y/Ä #G= &?RߨsP;j!m&廃7Bi-jgu kҸk{a%/kއR.\Rm_35}k-՘ USsbz;ܵk:2DXenYSLnξsLdyEd.Vh{u=/5=.̌w2P?(N?}_LNû/i(T>ʺw;2ny5?b XVU& xw;WXRI;.& MSc]uYAfR%?gkCuzFfhHퟣe`n~R01ak."oͩiLrh(b`#;g!j[?{A!olMP13]قuS4Cf2t[r`scF$e8̖t oc؋#å%%L5ߑѭc}Ws+DE2=e(%.k|<_'|QLsS^P_M|-qEJ"^~R]%`|tĔ#{[ FUq H:1wwgqsIg3Z( M\^u:6-%l] FUJB_#2y&SAy0ƸS@&;Վ-IPg T1w #gͧu [,'Db}ݝl-L uur_XUCݻXٸEeT Oz_UevLSC]Ui~FBdyzrD_8MM`Ƌ ;xi^6U&E\-Tdwr _5+:̆z!KRs=[ )Vh`݋)DJ?Xvyww۶f!^)JqR uƣrLLI?vJ.„=/9'ND0_C/-C,#uC_f8%h7C3WBQ)`u)Z]ͽl(}73ܽOV*mԐ`&uczaQzQinW˽ &z}YZ{'jq!rDfѐuŻ9c}&iވ|xHj7\d&諬hk7)s %=I{BizZփ戤ӯ۞>Nƹ:O%=*ڇ~,Qo鯯)+NKPOWW}O2??_i XVU:)s/n o|n=`cÓܴ0^ڊ]3ʎaofE%g_5W'_ p2QWQ-i|V LL.V|ᘚoyBxpۺH~:?j1rKW -RX-uo?{s$=K+|xMnI>}y_i]g6|ۺx]Džz\?iE.9ݺ@ɮ~l0O7-ag)F/ʲbR] <)~>p||[_]r S;k6Ll0S^/{HVoٌ~;$rt_G2?X4˼i 86;H5aWp4/UU 7SRݖK]̴u63+No Bz>O(XY:Kn^3_Dp>5LBыo 6orӓb#N79f'f[Օz#9...}-wkii-/̾wfk~XJTp{l N#o[j%)Jmc:`HxaսsZ\{L|.d̿鳆KU][Rv)>*FxEi|3-3іkcv{ \}v]rW0Rhy1H̪kKO3}[m# 6OGϩsH ܔ9D^r} ,CΑ/Sb0g]NYwd64dḁv*oo |I[]|twG~%+YS,F]=Nur&}gۻa$j+׹ozOzG.:ɖSqWRYaS}4T;dW)ӕ]3xU}r+wYCX!!e&\Cגgv֓Mbr}=_T `6vt§h#0A͈K󮎶fF d `aa,W{eUx?^~a 9nȔA]_H%\sJo}&tG@WۓG!^>S ^Iȿ{ fc$(~ܻ}AʊN*nҚ++bsG*oS4VO3G[bL_!|McfO X4Z./HWdHSކT_-n| ȗi}H7 η%}&blٞY:uulO ׋Fc2Ϧ hy*/Jx>H&c{) y|hOɾG݆ϛZ^>C; rt电N#s{6iZFA1!WY5OţaDuYaރޮvMMLL>YW{eU妶5Kg Ǧk*J Ņ\M/X)hZ:]7 M>P0n?GSEi CoGl.\L`,x@AmYǬxx8oqkv)>UN.L?rsg=wQyնj4Uf?PHp+vi/uBb.>፠v>핂p=Z|8W̹i(Qc=(gt̢Ƙ7 ;G޼/K9JJʬuHtSoG}AR1Y !}L*6y >}n0 96fJ`aa,㫽/ X¿Xw ?O./Ϡ(4 ,aX½(?X p,p! s($ځWB Bb#A0̀~͖DlUcX1 }+ O IotE8,<À}D/!<h(VC|.Ƥ&tKp"MHQCp000kXֲ$״Y3V),EtGӖlFPp A@#QLKPG D`p@m 3vy_FU 0P?=X +\ {Po(\#H>'';'ɠA})FO2DTk$-O2Ӎ%5[qV 6Q 'ːL?F@P[bcEjL$h eDcE4`1A!! 8>%aAvK6?r-IN1pŐaCD$?(rP,m<"o ~7e{MnJW.BN}@N"(`()U"HUA@qT!v(jF륗;ђOFAP k[қ̴"8 nEh󂥺8Ī1{$ ociEYPL!B6 d?;2G%?`AoKt@Pw`jq`pPw`@ -X/E @,-g27 +?Ox000k` ,kYskʠWXXb_0+'\& 26/cnoDLA>H$U4x @u4̠ix5d#x$c2 %M99!40E;>"m 7AUĞbѸ?* l%>,ɃCQeEaaa?m{_6$ yf RGI#ح\EKA?)`RE(lyQ xsi!` ۀp Ac `17K+B^"QN#A[>@,!0 RIrU׀~cbYؕ j+`SM_+2[ag4K_!JPT$^}Dcл1 g=|FP)Dnm B M-H#*`fCf*,/+%4!  H0D ɲa@kWdsk[DN9h̊sǖ DA(l@8! ̆2j(vwj}0A$" 5@2\T؆A@R]X]7qB[ 6(#x  A P]^d`.9$c1d1Y 3Ym˿hÒ&(viHtZNj}K>h1B/`Z >M vSķAdLa!]sPN J-+& %}b@TDfpc" "hR.b=Đ\ c(G?mA`?oM+r~OBz v7۠P- 0|! HsCL B*ҡ:aHo"XrG 8I`ӎp/F@l-I RqP8qX&=deDp2$ pO?mA&%hd$cY$3]1D> 0b/ P,k( q8@:w6w! !v?'`&i9hB3Iǐ|c8BQ)?,A)8/x}gTK" AEDD HD$$93{9JA$)1AńD;=3 ߻qxzvkWDe>K64bß?ެVZq2U=,&~O\J'w >}\+#%idLU]Wڊ҇HZFVte;lj,kC;mȼn;`ngE Måܨ?&hF{mg9-ޓ]=@WKl.T_,"Ǧy+9/k1֍]rk6V_;|ig6m906?auqZ|znah,LAQK_:9UxI+χOL,]"X̾*PX l[,Yi}[1ˡNnˊ:5yپh֖;jW8LJL-RҴ1L^r-M쥨lO/p ׄذm6' R܌~M,;N({|?o>4^ v͑^2vwC|^t߻;"#"?2jwTA}oS?޽~RAvZlxC(((̿=W%XI!Ie-}K7|xJAeckU%i('UĄVu#&sj1}֑7͕Y; m\lOrom30ȫbYd%[r=ȏØKPH + B-vLN)食XNjW=zT{:'b>1*d) nXMN2q|X)kw( XȤlSOǃ6gtš23X_D)}U RxW$&9IHg3-8Z%DGRa\00򤾨樮֗Q@47 rlüxיȯn2m}슩-Y5Pn&WdOA91XkmQNw%'?zTvUGbj֫.4 ;#_>.L :Q?,.gس?8w蹄j~:dGeY)qY[K3c##cdyPBTS8vLXjk#^F:*z_}ZExg Hj莽},gvd7<6"O_vYEmMgsNr%c$MyiE:Vl ̵v_,TҗC/M\cM""V=XLkP^''}rz:K]ܓT]TaB^+E&)]:l*Ba˫vtY9$hjN\򩞝yRsJj]IfpP9exESy9M%رZ~iڿTIJu_O).ky=t+Z}ͺ㊎_Bn?\tSuGS[.q/kڰ%u']Ƀʫ1Qɩ '#?9rYCRU|ҽemڲ]YNe`4Hl1tpxzӹ4e|V۲[zʛ{ ʮ"&!IhTjs^݈h]f%#E[We pyKC&LKq&ګ:RWXo~Iσ&^#Z2*x4>p2Fݾk,#v@pˋ|Z6@bI}G.fF0֖(((PvLEEUI˳>_Tx!-6fsF[}_ ~>uw6ڼ0' ,=y.:&( ] ߾%W{Fykgr~Jh-"] A_SbuSTqpQz2Jrl`WiKee"|Ǧņ[׫.ˉdؤ]voa UZ[5FfS#K׋\,m5$ =g.>jsfG;6$[sս{6YS~ae/m)NnśZ'c&%'jyӕ=d+ELsv5.4ImvU~aۅ%dTFJgvl|p%QC'6iZҫZ_ V_gj`FKXdN ?s-' kZMȁ?OL\~篇d$FnN֖VV(((=WE[KCSI}*ȑ)u?*-|(?;9:j}FGE^ Mċ>#khd&˱DqE>+)܍ Iײ\m|駰tˍ4RC~ՋǷ^WUsѪwƀqVl79~Z;/tN iPxGƌKB 6X= ddKdqCN1%aT#rCv>1do ۫ 67Xs4W}GꥌĘu4u3ƳjDHyEwܓ'3hE;n{ ƥSjKdE ޗu+rY7՘oI2>0nvalѶ^n5?m|}ׯks[LhZej|Oj t/Lb&YMg:0Qj t5]ϋ#8kY4>OT;<햞g/^g'Ƅ.glllmlQQQ-{B寊ө'tN22wŐ#ΗTk/.).As29(bd]JV!_W~JȾuq1׊Y9kl"|wNIjYxoq+gw.+x2bHkOكS*HD^Fr;B}%ﻞ'VstIF%l~}wl$ 䯿:w2K+#Z{5 p.9skTIoT$ڭF~rNl9ۘNvr;knp18B`9]LuVts~o~/!!@G.b\pj#{!͸&J'bEq[Bo=k.Kg|Lzk[ehf빴lא҉u- xx2mKP㳵͚뭽]¦ߔ2U#ԅZ|>7ѪNWR2qN-~+b[Ek?۳n߫-V\B~{Bwf:H Sjfw,a5~@sXcԙ}cJѻ> w)߰b2Fi˯+!wl;T+g0F:eXVT3v!\}Ԅ TZ[Y@y6.^RHtrʚ2R㣂@gX/[~.r.zķq-9O\_4rs5 ^VR6OK}i\_MsK#>sU!y?J_3/z ͢+^=ݲ ۇW9ί ^ʟAڵGS?0{SY-.Z^SQ% k ZIxyG。d1!L"zY%-lgcq֏@&⬏h.b`B"yܫhK2){S򕅶C1Jd3v[}eφ!|kmH]mék:(޻nrwKzW9ߪuvJ^nvdN #u`E߅}kg3mEGr֨Hk%VzY즾mᓐۣ!5C;De%UdF4Usm'a Fe_mhx+35)6:<*Wmmml\0ԜҲ؈ εDY'$g&k)%$V:M+z6(4TɸqAwɶ1]iuOY5m{⮜4{훵p$|y͒m~~+<꫅d>y2c]#i%YYKBYOo'O߼Ne1Gͤ ,Q?5\|n gsʽVNp\йo,ї_u~2pOTj~H WY#;=ŤĆGn/zst)$ICnVv\lo9z|p9fin+=ձ{Sdr}G-qݻ{{M|dWw.y-`uÖ{̄e9tfw-M%YwkuUew<|_ZQ m疙w."4La|===<<=QQQ1kx;;9:;::{)))ax_W{ S{֮]uCЅ[}_7.ls#/DEd+׻؋Z~$ n`(FxZA^/̶^³B6¼7s =)T"k0eIu&YF"UיzO<՛'ܮ<֦ai9 #ꤊ8vWm_`ZJ@ƙ3eO&ԕ2cQډke1;.!11.d@O`mJB|dY3~}YVу|kZ2lgU˽x>Rӻk:##U}ۦ'+]h~tų\jg{:qԫXGhM0+5m;8Wzs%J-Nm@2.?7<mQ83zid 7w4ԃjGnWŤTI͊_M d#UQo|D|npE {BŲ7~.*  >^%a^]Q?L:&?X{Q^e  of)=sCwdɧ\eF9zR /j\)#: *bF]W#Uo\-֫ o޼m̘?G3:j{ʩ[Bb3r K._.)J>@,; TtsuuAaRSDE?g;KӂʨzE|7|8%kGպG3aku[>Zl/C܊ׅg~ } x|u @aûcDs6u J)nw8hdk7{5ƫEUYnQ\y9oK 36{6cwoT޶W04qlHv<]ȡAd2L!zW.W [z&ix{芞`Tif#k2㷧ܝi~رxö hEZkvQw 32j'Tϔ\a?adDWMj.$ݜM8U9P6/9뎄i.x"'DkoU+4f6 ʵvEͫH[>mh:u!b~^N\DXP ~T㋭DGG_*UR@j`<=|Ш(j!n6EEĤPTccCռxY#Tu](j<7UVk8Z;Ioxqtى7Yyl. qt*QIحvڷX9棗'?Z#,|JĨæŠ>'nG%?(d[9ފdlt+,I$Yau2Q>!Ycw?>4_hDsʆo<*uߕ5oyL(s]yT-tn#OO~oj [v0mgؔCvN (kU֭Xck/UK(;OXՕ{J{~%,L,U3X].cpxfe'>@CE%M}sG/ (:!-3;;+#-"$(D'[*9DG7Ásaa!Ad挂M{L}^ެ,Ɏ9+ VV?xϑ; 5%YiNǕ _+F>8ZvwAWÛ^R6?:I< 8xb5K3t5%2>&V%㗻@ Cb{rTv>TRy=ch/d- K ]wL?0V44 򐖥xw3/>,D 8_+̪|ʰ̑cEuU,2=/oq5?a f:L$F{yjc;j >+iA4']*[W,42Vy\LsnM{FI-j$u5CKZ?.Twr51po[.Љ^VKZyͼ=mbǾ~tBʹ !(2.1͍XSko@U H0 O: H .Ax0[7<ǃD"8}W@3ÀH PHDIT= Q`qddHD=.z P+AjQ"Շ@x2 ZSbd<O*O4ZYjc SO +ql| \qx|,}+ X*D&SP^IP)=X &C^, &uٟh ζ((([?3iV0:?B8}g :pYXް'k@#p ! :Q="luV 6UvQ@ITD- A$֑Z,T+<Ȱo!PZRkM5 =X i(A@R!?mg(l G=GGG ?&F@?!˰G3_K?A ҄'z !&@*e)0ىDS؉œp쇈bp D ?fY!5e4@Z).9 : ~i1I; Dࡐ i`z(U`.͑X+M3f]1@:iZ$f%8@GGo?*J?),Rt-lGe4 ?X-44 l[dl>SPHe([0h@" H雭RCzlPCVAXbTGT@_!E@DGL&4, 431}#Pb(k[À UᏎ[0ʬ ?) SF(0xf`@q@@c !>Ս!D(kAr' (h1s Pm}+ S ! B"%D4퇨40&7iqO;A;PDf5y:QQQ'?*dl?"IAg}z n#7'6tM%9%@3X,`ivY@HuO5A"s rLnG)H7u. #:F"f@I/i!|_ǃ_DࡆCHf8D3$d( +"(ҶC'L'@ms5*Lk` ioO@dY}v?G-X'S~ (18a*V[-#EPZIyH \gBiP̏@2w(; } !BL>e@8(pBCy@86hDe~whkED\) .|8$ &<:&;(((tߌn2C$qw6E+`+H4󟾟#@V3h# ?(j / [2IZ _+ly?'dkգ2\>ABӡ hA:i/v9T2Y1,8: y?<(-vEGM3oi~.;QX ^r 8 lcHT*BiTޑ5(+H5k_{2Z^Tx.JB"OHT*= w äe:I~HmY>H m*? FPQQ? w ǞQp.i?1qC\ H2| $X @!BV2xD- hfYp-ljR "t# ƃ6&5TfۈGt"zd#ۍx"~@[Ak{h ٨Y4aN Ĺ5ah4H遹 Mo¯FhghsϨ~Ꮞ[9*C M&s35Ԯ }.:ߑL-@FАtGb\]WlȌtBrNY<t0zC9 >;GD <6 Жc2AHPOIP`އHFVȞ`"25 `j\T{ (((t߂=dRRsIsF!fcT0aHoP:4X*`kAjCE@Y6?65G5Ar H$btbHXQ^N\ ,5nXڞQFs/^}Iȓ`szZ0ģBn*$VieSj޶/߼9[]]]]ݬ- &>99%)Q ͤP(A97;"zB?N3,(@@ƃ&=t YVw⁚Ue/k}e.x>XuەQHhCeƁ5ruZ}rB՛س}ߤ%گ;O>&J Jnjk\S7I?^-pKz|xéY+j'?~'Ҽ#V:y4o,s I}s0䓭+ӄ܈0zd @oȶ@7?ɺR{̬縌-^7/?`ժٵ3ƍwՒYrټ1n @H%͖'ܯ3s6I#ބ4k*]3ظxÂRc߭%~svcA^E/S ^l. ܸ\QA Mܿ +Y2}=,8<*&.>!^ʃ!IQC/"*F; "j:,@ Y!t-JRl%@聈Osd Tf'1l\ы5_l4QJ]cm,5gx6}FZd? SKV:`q禫&m,WrFru~ٲODik%hw FI%Eenᱍ)>NY\u&O/n[gbJ5? :6у}i4[/ܓٔ|D98)>x$kfvLҿʜw՞ NKm 7mw;.}ZF[Tk'۱wR:5^6}t:׎U&0H˗)~2Cݜ",j8qŖmQ|LxxQhlnX(&VC"}iL/z5 EN Pn Z炜w Ib Gs_>5_Y~}n/K՚%ƄzUC.7gתG^oU}Kw(3k3d~2\%=%0!8Q/V447QiAGHAD8B<Ճ@e¸p>row@Dt?FK"xҐ#KJNMIE#IGR! .-/9%5%9. tZ3u*>wJ롢PF V|;wX>M3'}_:'v)ڊ:  ?hS 뼹OlW7Vt<xWZ&駆%T Z2,99¯ɟe}tpXaTttT$A 98y\ALbb>ԫU(:" 0~>dokk{'0,:!%c3 tNa¤xQ$Cw'i)*(2xSvenGVشq 5N~]F}M%۝T=W\|$p>[6Q~[44YzyѼ؄q$!aVXպ_]'-9J:db=߮:|c_SZ_Ӛoxe+ .wo?w'zlӀGja^r7̕UƋ\Yױmo(wS}ņ S=Eg_(;-}fU(b_B&e^kv?/{tY›|{gY ze Y.|vM7~jɘ֓sߩfʍt@M`߳aS8 )4rZC;fxbV~AAnRd8Q},04*6)I#F x[ӗEťd ''vD^M_c0@/ß "a^AA~#!-q̄#D"Q fyl\m'?x#˓ *F=_ϙ3qpY17fWٍk•7#J\CrǼeSFMBw/N-|>;3₁eϟ4:\PocCfRnxE_ݙk}&rG0;q MҐ0vKj;IN.5]ȔY#ZcEpAYqsKS .mnvr [A:&嬔*yw|^Z饅ܵgdG̋hnڞ X]:kVjuip~i s~uT K09beY_s{y7sTGf:m[#4eW>H ~<'O'HƶT(*>-#35&!ƿK1pQBZvqv\Ex{o1!Q i9 ,W8Q$Z 9*J^ZV+ GHjpP`?"" O2600%fѸgOu~,IƋ2^yd?=5("M۱t Z|ul2e𯗔sm- 5 kSNDafԆ\2 j7ҏ7~Mv+aQɛ߽y}r 0\W-)9)1.&*aRx= L-]x3sGY{"RI""mŅIԊǤn-JANBDàP(n s$[k;8`ʊh9nl]RyT|&NNѶy[_,fejM!Uk  \~%Xd7ٶ? e_k}#hj03Rk]ʑBozg.ˢBaҌIf#GXg[̸tMO#*nhڴ1;>7b|%+#3L/pp왣> k }K>o88*xASɴ+d?W87۽Ԭ}p)=Y׿s.[}n:E^W5C|w 8K˘$)jwvcUsEJ=zlV#ԴfxCu%/Rrl~f p#c"#i9ؓ\̂ҪԔX! oEgW /LFN'''gvD^M0OtsqvZcdnfSW_'!߇` &E!?$<67?o[rF×x??݃c6 UNs;S{{A2>y RcnYrfIЗq'h ׶R[,Ϯ^Jjz#<ʋf@EZ,'mIMvIs[Wo~P3|n0S|0?O9]ǹY;YyC!dǼC#S|3mPIз k^ҷ!z# >'z2VsV5¿vf<> +tedk+GeLf*U?V\>l䃋ju\&[nX=eY`|xѡa02n[ͷZ)˳33rA~Ύ7lbI0r>uow@D4;%1&:f Չ`foHh|= oCQLc>VSkg2 JMࡳиԴԸ6Ru̜ ׇ)߻s~WAMB)-6oǤ>Jb!Oܧ| +jleӦ,';w٤#hCLP-E~!ieCtO"n1#}#ydMq:מKu:߶*mlx6D)`mH"v8x6f) ]K6?_a{\ }n4MiiKߩn{1rlʵنƧ׹mF[s3nL>٠;EeIa4phPk1fy`ŢgJB3+F;O`]H!5~r76~mry~}uQ1ܺWU&0LtuW(m&WVӂؔĸ84~ h>^zF.Ĝrs3SEПy چ Smp%''HSci ^nNvkiZ:%j8R (NN.vdJnIEEiBld(8lR\nB;NlbRe| {`)EWzO7-T8KN2ż5G>11G<꺧rCf3-n |;ȇG m~\y ;<>pۮ~Ǝo^Agz}a|sb}$_rb_))t`;Mk8>opҼ?.wg ^(iS {~&~|'ƨJUxs+Ö{9/0L}cOA.ZoG4<dRשKo}k4q#Kn}~w\:F1?OWZ͘}0egaa֏ 2i~482)(%%9)6:`xZ۰ˊL-s؁HFpqtQ_ȻoOS\Y/vD^MKSi 6Zj+56l%D$W=|0o[Z0z:il6fF#A]]zJ" ?o2 JJFA =e[dۮo{$ rբρ5OkO~O*H3 nMBZ>vfqٺy~ՠKLlE]r~ ׹WGY!϶a٩o燿v| 5l_X i3.ʏ2jzyX~a[:7HG?1u<3bO@ moGrp"wefe&C9TO+5Yŵ N^Tg(d7:]k[90KS ,{"RU8YT m ֚KV5#0E;;}ByqaNf(Lv#Z[iZ3+e `Fٟ-HHL4gk+ ACUUEI,gնsoWd4!Kjy~D~G=čTI߳hYck7\u7{SC}9e-rXyc1ye]1[g7A>%Iq!0{3ُ-}[my%3P3ON\SvMݬT5YA|U}a*dF&yMS7|:|&C|Rr@P}soIxfVҲz}F(zoWʆΒf(;szk&|jN^yGjۃǻ0+W=qӉL/cYmx >1q[lcOL{p+mZYiQ OhUUr .Li+n%xʟ=1)p2_aow@DJv= Ò I4 ̅a.r i3ko8~;96@6\6`(d!\9 "G@C JO6nG+?8<H ;9c'sA跩|HyaA"G|B:bH mgIjW\^t'؁GuIp_FOOߗԗ(? Xś#>ƌve\V ygE_ӇF菐 l2K CCl J ~ uwwX BN~6͛ !LTr@HHS )00d/*إ+@*$U$[?/]/ o s/(:еbƾؖś2\.'@0NPlI3Ab~c?GlnF~>ln#Dsw1X|g yg&ol!^q7PAPq`CA^ @B1Sp |,?QGHqRbIGaN(7up??ßvW?w[k;"VpGSƍ[d3v;ԠCP:Gf.=Cas#Td6BkL$bґ݈/P p^F{b҃Ge)d6?"HRTqA>AA!.Z:`vssepiAX ` ~[Pܻ['7 D߷Mi$-bVܔm+n  c-$Â!f?^Ca{\.FF-r>`%,sQ<]U,N@Mtp0blTeaq6,a?Dap?` 0oSqdsK/=NC D߷ӿ{~? Camuغ0 64¸;4(F7Gl>#9XŷH+` <E9`"pI Rd#0d񸟘ېx Ic@PRxbxt2 ۿˑNyOOOOşvVwKB$M[lj_Xc ͊($?fClt-b|GP"R`;ȅNfa!q} $h`Tswi$V;[n@qB8"}O7?wUۻPoeqvwJBQ81a ?oal`>[_/ᓁQFLm@v9.6!& <![`NAc,!k(9~< @> ĖG#?>V*@#إsqO%:>'|!? Zq?$+JR AN׍pOgt+moǬU պ{.jcB<6!|b/:/^YLt6sprQVv:_l^.`\`!z=~Qq1AkĂ;0 ~9[|Q8j7qMu'q{<"yP lGPHlbMasCP o!80 ,a:jEjcDt sqA.F}Ҙ<?*+\,g䆆Y4@g|Ip%w$AAosy%3~p?HPs(Kÿ:NvZOOO'?wÝQ+awl3T!#ĎjBC86 e$RG9 r0w1wu?yJ,kAt-hVbJ0qUNa"A4iIQ(0Ouwwl@K~_Ɵv:@kw!*(ndDPlv> [=-0b>j0A?ɚ_ACGD I~GG%+K`$;.~,g|\]Թ#*OhyŖ?4˓[ O<TROxP_f]dY |?KwmaƂdb vB SllE@<dw }6󗋅0'#Y [H6Z84Tf#? P"9BR~3ɸa~G )ÏpXP:sp 3bK?.:+ BM,xNsC\t$raN_#^$Y#$^Ij $ ܎ O};a;[uif7lx;<  l 80 tV @c\(C%~xl2LC:3Je/!fx%Ǘ%x0]c>a_Z\ 3![A!dٝ4w.]꾡 *8(?-x}@Tڮ v*A+(" H̞=Cw4 *( }`za/bvzYjV`JRl)E';+sC=]>Дڶ{]+λbcfO@Gdn}g2"-uH[zgĆ/9cck %o2ԝx>'x*'nK)m C*y-Ʌ g_+lq:g*mK[]ŢԦ͜XoSGܣAdN׬8%yA2;xEf'+k2vSXסT[+ػi{[_~Oz6n|gS:xT*nhHmgp}lXXEuXkg=UfJjw;0t0{2tw`ދ ۮ[_ưElkGaqU@gpR^^JJbTZykWScQ.Թ9 T;zv̔p%g[+Sc}=R&6+/7###/[t?Wbއm-͌ tu?*!irk9WMt5T%wS6)l~Hyh[x^0_\jTQAJ:n~[miּˣN{4Ix3IqUI*Ʈ2*TPGGg|f|AJq e{h؛ Xܽq+Y*AΫBA0)k9|Jt@c&1nО%}С#3ozZlf ضv DݍƢ_ł+ ^y&RLz7Xow(vXc#3UoyI|էG˅&̛"ZMdW抶7$Q\(#}>&+!0? txp{sY޾hcI y8XJrk~x(;)XSU@/wm_m%=beø稼ᅀԒcˮ\xXh8r]A&4jYee.GSxZbt?󂭥q%|"o挍Ϊhx(?d* [vp +__xPU k)gPq4ny^r%+ CRe7Dy_pr >vh _C y^ X(Q-دh1IJ jl/EX [&wڱG_B5/N]xcnrœcI\jl3Xvџ>TP0EU'qrnMګөf3\+5H6v#Sqtcخ@o|/ܲ>_/u7Re57]a/ky~ҔZ=3ѨmޱV'fݶǕ|dxTTT&y9Zg]IO E9r *8DW,L ?#a>hcMYnr8PCEBzN)%F^d$Ea=] @OG[hncg1!e ?]!鏦P? Z))ݠ',!y]ug`խʲ<:Fv!y JÉ_\ˌd%wlUpIiW_f&/p@BJ㡪hY̸`3@;6V]iyGR+;6|&= cǞvO.9FO{n|Zqv7Š+F}Nl_{.Hz&ViW}g:s/'6Fv?޻2r,ڒ=6EXKNW|]=X$Y]inf#OY[;o`#ս_l_ ţlsM5jh+buf[QQVTy#c _vݒpsJ;7mmi,Qc1lg�onO.J+Ƿa;+o2щǕŹY̴*8! $p9kofFYßW|o^](+L [0ڍѩʲkycq^,cՔ63/"ny $싺lcn$ϻa gL%t>j%>p1:#'~+_n'uwn֗fFו8q¥_w&3^CRduGa Ӕ`NJx?[Ľv@+M}:{5Ć>5! m{&_;ubצOW3ZyIMݔP{WiDbՓ3DfI\w) 2jZgO,4HоOSh3-KumǦOLчj~B_n]3qW(O,4ïDG=9Mase{4k%z8>.NN#/.mPy} m nTXQvMjj*w3RNϩ{wv}gkYQW?8uow0J1配T=[Gh׶]ˌ3PS9fj֘ǦƪʯgĆ\Mԕy!R7q(lhM ƣ.9?k,Ϲi>InOz8r8J\XKYVz5VVae[so6fDxYiJ|CæS6j,?keуWϲk+ ;mTmLyqC"CO+8ܶv\61tIi 'L@p z Vjѷ _*9)fG{Aϫr_]YrgRs[Kz~gUnHYs;a?>rGyygEjgf]- Ww[E1o?KI?ȼOUN37$^5Kx=rukm sƣK}9S w3}؆ͥNj⿦6(W/%;&Ў񞛥sRGXåzsdnS͆8_sj'W}L W S R|\l4!R3I/nBGG_#N1qA8ysuӶ]\RA)O_UW/3T\IJC=ΪK944 X;ggwC&,?@kip^NkNYgZ>+8ۤ<.[׳6m}x5|27YTaFSzNoAs GbQ 0sV9~;-|qMAc#Wep-93 oܳM٦?DO20t}0Lʣ~h\00*r< >Xat-I,n-'yL,*8bSgMV||/b/0KyuɈ/8q3mqPX"ULpa<~Oz=$o 5IKܡ>5w!85{h+ǻ!v/VeWAF&Sqۭ ũ&B4:s,"ԧs,5+:OoĽŀUw =+1쬨H3^M~u"j[VKd_˴T=m[P> $c>&0uB]oXlWrJ. ; 'aYTUFֱvOS/gkF <^bHcg꨹ߟ/Yu+uή6ٻ黅9q,LAaf=G?}B#*wLw<5h-hgx>>5:Rf`KQn?1>M3Qqb\H*' %ԄdfcGQg;:rxntnYXu՛u\O3V;ͲCX-<ƣyՃ͍UEYɑV:*J"Tl ?]!&\|t?gt5TwbP4 Ϩx25U?/{S r718iXp纗-5IN ₴'=X)>:MMxBoY_$Y U MkBr,.fCVxȷB-ؽ^\8[e5\Xd+ `2RѮTxCz.f -ԗ ~y˼hVN׫٧F4.NbYm"¼ֱꔰ؟%k`-ԺZl"0tߟG8ݨ{".%q݃C}fyVGZ8Wꋪ3<ϼ>![{B%קY*vx๯+5ͭsӹ I[ޱoӎbԂb ͚gyS$'SK˽J09As^/fJ(\GE^cC=/g~PI?񜉞 #!_-?]!&,ƺZrYx$c"2*:_}:t,'%DCQއ/>|Y7[_[}݊X'A7E4jE[>ȴҳ>B&;LI5nKk,sq"w}$㫀G}|sФApnT|a2[zsci{Gb Ԫ|G`[{$čjb_lajc=S$li/ʋ\#e$t;;rJ ;94 WE5^;<-mCeqf1 H⻈_ѡݓ*F@}șIUuZuxhoEkz a2fl>77SwN F!-r׍l4+?sB0Y=1-؏:eus 撓*'+aQi5 ?]!&׸`?l,5dYY!P YhkW#vr'DW]*aĔRpƊX^_V1U\b/oF:((O_NMW^WqMY*tR+MxㅋG3sGh%vaݮɯ}SR+u϶HPo2yvUHjԗr߮>O|fQ%cEq/ga[kSeAJ]GSuinv Hц$xpgs#]Mud!!qW-t?lcÃкZҜ\d5]և/^(L ]3Ӕ?)FFS،WiK;} ٩;$/VjgM6 LDձ8nM>DMy\?7%$O zjO-Szlv *Z=V{ͤ3c&9籿zt<&Lo160UlvߪgsBIYⵯWdrVȸ$Z*VF-v^H%hRVǛOZB;32<9{ [}4FNh~45}K#Yy_׵},ے*/uWy-{L Gϸl)|-&o]; LbVU&H`Xf /o_qQ<Oр/h]0q:D|Xh'Iȏ ?|I: $\> Z@㡣0h_03ÃZPF< ^nzQ½EvoX%9 6o@G؅CGG_c7 *ٌxu)\dd.1Q xf/DfF,@4 Ca}a @_,0C&eb. $݀IG'H(),á@<#pK%Ax 8Xxa  (Q@DTBI%eJFA䝐|-X$-焦,bD<.92!Pc'xx"|a Db@a/Le,O5@+H ,M9@? E |A<vx 03O|>Du,LJ6c.IG8GerE g###/+y'A4> ,'F UDV TsA0Qx 0hXHq~Ou A 'V~,a!oV(ݒ| /HqE/{oԹ;2KNGZ;d %{OG)W^<$C bq?T _/{ývH? Jrk\=%ؿK0Vr ݀6BA(@Bw$Obkߟ*w(8H:.JUGGG_.#N[_?a蘂TW].p*%E@!4a*\FC MDp-"a$.‘A7!XG?8V|Pb1xؐxr(G(8B%!*5cHn?@Rd;ߨ'%6rS!EmK`wb###/ y'3b$ Vj/`h;Á3?\p<C[CC$IAq KD D8(%2vA@Г$Xƀ?e"RS 1I>,j/eGGG)w?ԦJ127&$^Bj>U$@肣Ik((rcI#sI @i#`cl)! ֓LE !"*r~…Cp<x<@;+ C$pQ N2ہ?YfQ*X1SGGG)wg'{ub8@e^O<C P.7a4Z;x3@5Ćiɞ?@B$BQ * O$Xbd,qD_###/7yZRhKOP"#n9avQ ab-B[bE 9/}w/%tMb?U:v-!:" de dɗ%@bfQOή#NG**S $A2#ebo{ɿ6便%UdRZ&$  C~V@L"O%F% ^(XwǣOO7 y]j 7"XvDA/$sjKfpQɿ܆C0\<G=U GŁ>ĚDN3) %%t C} NFS@Lr ڶ$G[A/w$RؿҶO 0d8CEqĉ-E HF UKރ!%H (?i040(8 ԑ ;7S(m/o{k?R/" hrGJA8Be^L3: :ZoI>?X⽩}y,9Hn,U#fI|.#GwDsD $%y\ROԿlO _!##/[Y(()x@TYXBTPR$s!C ]R (*bk e;9s;!Tyy, z8,QS_'S|.M6[uЛ0Wϟ@đL7k3dsڛKx&+v5#eۻGFe[upcخ؍wo-]ؐzU]G^P,iVݙ^سþlIU{MZï|XX?w+ G<_UH/Xm`,J|5v'7=6"Yu^S'wZKDzgJ;>)Ӽr-sR[ӯK*ZYPwkfNvdĕⷍvv_v!-^4|rݵl ijp,Gdx]|AGVwss륹h )Por6cnd;']#o1lys9o':oD(ks&_q&/mo)7n=[_[Uˣa~v6ZVbbAƧΆC!n Pruv0XFK_tyt(;153mզNy̱=$_7fڷS/n*`k^hyurH%co9c<]Ύ=p~= &'/!?{k.e7M?uny~Oƨv;Eb=^/[ح+nMܐִu|my}xTc;GCحcpff J<2z\I {6=evv&rh[С>a~yKO.o][M]F .]ݶ&n΀(%Jti|*WԪiXP/w3axC6vB1Ϊ%tdzaGn)F0}3+~:k^Zbm}2zjCչB:@o1ӖnӒʚ\N]uyqvJ,F@\7\of sR@!A@7@ DqAÂ]4jn2r @1b+N^qjWAFr076zԤyA&>q&'sӪ'4VXQ*1uֆ\څ߮$Mսl%2n__}I#AMw|uaE*=+)w0y\:*;pCۍg-kUOߨ.(ԻݬJ i>niY~z ζ)OOhn6]g@qW ou(G{98U.߉_oUQt.GH[Pfx뷂vkjt XPzM%]jbgҹgvʷOQ6M:uo1Ҽg'& t1 |dT+M&T׷&5}fuծ¬$PĈ@`GZ ;69Zl@M7&Ctw5n0b[گܨ*+MrhvqUtoF i=23 ofa?[t/GۑLK/u @?3Agѽi;N*/qG)&fUڷu~:'uhW%'u UV>fr}^nDRɽG<+>ο~cItk_ =+x3#Gƪ퓧dv3Z oJG` -jbcA!-黠4s獛'z1n1(eO'Wzx݇/CƾZv[H\:mw[B Zt/B9ߨW>-:xW~W{kEv 'UК,Cqkɯ7\\xlD`ԴwuSwnXvfȔoG+]`[OG-gyfʍTgGgƬVqjJ}ju7. _cYլ6.>vl 75W͗_`560\Dhx0f2_j3A[8tہqkX=c?hAN/2b˜oeWUI՞rXhݲo~3حtdm:fMsՋ1K:~q`oYV r2>}Mc'8%6󗯔'p$txpݭ6q,ABZA6c)DLD(<{6#3g3&LIqanFb ws65OM5~Յ)\\&sx哵i$)W?xb6tѰKG ;Wj<ֵ]nQI㪌T%~y`iYJM85tTzťv] }jm~mmQ֜ik}ˎb A`}+1[v(SX.òGe˄G3ciB˼>;4bȧ5/J48tqQm{`Mo WYjrCt!e4:q=5ɬʝQO4Í^uybHcV0ڐcԜ|z}rh5?ܮr,|7pb_ę,pge/.bYs&|CtNc8lIieX\թ;?k)ɈcC|\m--,XA(?zl¼xN"A>^^v[[ٻIHa tـ2ǢD,*  265gFW:v0_T64n-{h4 Q[D5BдyO܋c+޾;aEr/]8,]V%y6xbN9 r 3ogڭdsQN\̗] _}̇Xepk ՜j#k }6d֐5W oӵ>z3mgy[v nesf~l1;bnfXxqݛJgK1JUjzg ;}"nmӄ*Cw0-I p^b;VTtDI^vz0C'@l+0Cbc _8 dj#x\& i '2I95 rsҒXl8r՚6^#+,\NSj~ ݲDH6՜uLkad]4&oԁW<A}>gJj (T TNg_rI'O(.uzFYxo?1I/?q;p j~Ra(ej#Ӯ F*0fmjh-Zs4diO&ا$lGl]1h AH\O,m-O;̷Ig݈sVɩYIsD<$k6~m淯*B~8mqTnk]P83TOrU;GQ]r\2ga-ϔQWZd)fagЅؾdV7څE;uxܔh vsrZklB'Ui:\ B"=,l|a20Zn P{e'1`?EdF%dLKN3auS Vԅc%yi1,B7յ0~YۃO|-7/|:dKaKz7VN|7j#}CA<߯1kEÙl043Cԟ}qӾ~6<+5 _=Lʿt#(Wh7gw 5g~jԶ{ԸBU҂4>Rru~QBWfHs.ƈGxx38촧ӔF֋ރ[㗐 KS#?] ͘8+FRZˣ)p UJwg R9 櫻Bb/wRbyFP*89H_SF[kZygZJntjިκ\ y *Oϛ Yg+esVlu f׶_9 +I$@8nn[u H= yIh>Nc#0W%38n??h tـ}ꟀE  Ő”ԔXIB}zEuO4e% P_W;E+؅\{RW) Q-~:lxcv~\ 2]o[3oSX馳֫[=٘ wٮ7y2 TˏoUƳ0O ˴L"e5dG1I0[ ĉK-JN" E]ܽCPx2SC-A6vd(d"@xxzC^LbznAIabB|\t$[%a$"(}o(S@UNf0Av5YmhP^'\-.28mq$c.4lIťSDSSxCԯyo~ց{'?MM->U7lzfշW=J͠ z֢Ƙ{SwKi}W=OsY75 Q_ko>d] zĠl4I`X)9NUm7; 2Tr?j\ Gy0sXAηKX;wصQ&56=?bU޴fBn+3/뜂@*b斏:^52j}pι7oTgP6`JzBy=?wg(VbACYI|&Z:{^N+*ݕ st EE=|a!|8 +&EEAx 0 *C0`s@!A4;MN$~Hdx k5qX 2 Ša0E''D "9, p 'bECR`wk',?|OvJg)&{vϭC-oO]g _f||a\LENC}{`__sf߳q21xQ  (2TrHK\Ȯ]Y܈_r0h[힬$<%4%(br鑎i+s;mF3˖ {z;2chbX`CՀ˥|?>_*|0v[@TUK]v<e ['=)I@:۹ ܄좢(.C{# t^lbZFRtâA0xhH &N."!Å##hHht&w??h6%i4*@P D,NjdgIjAEFqoRD/"7LH 'rb 2:,J*O0q!T bɭޟt`w0kߐFpLl.eiOeÞtQ=gT즵gG#qOFz$D}#2yJpcT7HXdy!К#B5.e/s<>LFR80:o MrسyT܅co7LrSs~lg-\ftmnW[ I>;cK)I]ø(:s3sbEz842"3ɑ"O A'Ea4HѠAǀ#\_҃ PX"4=ɜ.G!Ќx> `d,%`J dp4?6#Hs|#8x:"9XYD(YX&1<#VAS8tEȑb$r]B6r"iD*l1g T&B!42XDQ@ǃאYDG&1}R>0x&J xh 'G`R\ !2Rxd(80%+p΋2!|wz~$C9HBO٤To{ȼh**Я%HVbQD|02'Ri9|d\Çt,V{o$!D2DT@n$YS%T&+!Y V '@BlC!p[?m?ӿlC.H2ꑄXd(>;'M" r9WSz5$OErz.N@ӿCX %_GmIHGӿI&ۨTpdQ8≶>ʩ#yTi`/N0MQPY ߃[?O*ǻCv?? Loh_Vy׿bӿXJ/'lN1I R~B>9( i{ Tj4(]y$/'@??ߣسOd?@lIC>F+&Teߛ P8N {2'@җ-~o8>%Dy E_C!eppn=Pc`9Uvh m)e\̚tO_8iA%(?&eu~пъ>_>dN%=~7ğ WCw ]G@B .ߣdG$W?%Ѷ<8_>wP֙4@!%ҴYE)-??p??VE*hgOz0ݧp ϋl_7?C?4c'n'%ɇL2 僆Tܷ7?l;~M_?o?__r)(x@kǍsc`w bX(tw-;3,ݝ" -Gֻsw.;ò;;;=o l$Ad2BȐBH$H H NB(8(F$ D4 LA @T)Z d(Ha9t} Ah&"bJ8M0A~Xv,A "Ң9T\p 5$D@ @E=B J&&x>X: ABҸ4)&(HQǀpTaX:X?"an., "a$R82Ȁ+&HH&4 '$aIX/2E{hoB@c($7>AcCN.*lKL8OhLJ!CHcx"Qht:]T" D IbHlEPȐh3@ L$XbdDa&}`aa{ bžCʢQ"b)b"}džg st( ( FōGY A(\AB,$rv 2i<)b偉*|=5ɑ};}[ c06F\X:i5gWYm^K[ ^~UhŪ{}#B 2L.DaC!=$~(W~=q75,fāmI=aI@KG1qvxsn-L #rQ)HQ 6Qo}S蘀f9Xn4_-w\+8y˵E5m Vks㛢:6g/L\tڡ2"›('㥋iMWkshoG;lCó`_'}~XVLFXMa7[Gw?$‰MJMFlQtDⅅ!>^>~! љ܈( f x$"&??OT;M5J8$a1i""<F R2cxPemdjHdו$ yt<~ݶsq,\^kx'=/EO'r6o3Ц/M{|(dm5 7GZ}&0]?Eq7qi8S7Kcl;60&Cά4+U?ѪQ-UC/}1/°+YEٞO 'D%@^/܌C 3[n 3 aOn;>b<{z|hPd1kQC?Py7 ./FRiGI?f]+1+% )_cέt˅OwV,}n=_Bϫ3߶c^5%{wuxwWmg>{*q?)A/~x2* @' bz&RgM'i6@;B`j [Twn:iAݶa-iMb5o.%Zv)MSYwyC^6̛V,KO iɃ/K n켮91rţB>mgc7#RN )#, .j.xÚXgw[Zl +iƧf% 1pL¡B^z{l\ļ#.u߿} &"Ż+Ks9x?;C݉fnvV駶Ƣh]ctgg\bx্gR]5x頮l?'#i7(tmyŦ$}|/o;2&vs^LwP3/d/݃v9Ļo}啱g2UzV,f:2&$S[jQO&=i F N<VF_3w]!z::vA\R-m,uT߀2$ޝ[; Y5|ƴK-l4ĥj<+9>gmD\V7M;I3.֫^l 61bKhfўmTL\s]&V4ʜ?3>#6(:?{W=-1\dnasbboȱ3.gQas;LkO)LsK%I"4ns`S޲zCܜ@P2CBGmA~)hg]iC}!A6;ʎݍQ6Sf<]M{OYV6U]/ 3q̰l%vz!q+'unqXu=Z uYyQLQ`mU)4noYaJy^u 3GژRغ N*;q|YNF0EƇz:;7rEh”e ㄂(IA ~5; ʎK)*;T' gEx8ZoظE3(m<}BIa^z|$ign2}!Q%?:~<#e}Z.ꨵpԎwQ4v*>a=$l~-T4#{}Go/J+yx̴L>8#VgVm/O o}k֩a3;&;w]'в=c9ƿʹپTÊr mn>ٴy\MAj>̚rdT7V~Qn7K˽*_n ƹ;ϪPzVdwܘs :<+Y6:<mE WݵcGl YZ} ui4_3ӎsbX's#uAsˏ_v0;->B\6on`B`Dg)MJ#,hT+??տ+ նz#TNtBFA# |J .d_s-!~V 7oe%K6[5lue^ v:'O[K=Ӎ~t`"vۊ?ܜZ5l/1,@i*tnO ;0 jwyȸ~I9;7l J7w~yّ&mSB)z϶!Ur9jHJpQN' 7̢|I{ʇ[ۭǟ6D;!\G^Raݭ-,X{&>xFoP0:HZmjR^_KwHSf< ›mnW'k7,_1agհsͪ"oL H=Ϥn7.J:bsh[}U+{sǞS=/z\Clg#&^mΏcbLv;KSUy~UI~fB4 sw^kbL`F'Wn8/l䯊$X_M)7)`]Ҍ p\t冝AdAnMU X@W=;5> aYqʄm $^4[`Lû˧k=mm9kGTKƥSwiF|d՞QT wbޛ"Z^oe%!濧oC\EFĝYRƞRDg҂G7?= ;ee^]S*+KWpzܴb?cAz*hIjhZ4,O͸ =f xDS O\2Cj،W\ǢR~ˬVWA~w)wTLS>u -6|c;^Fi/Uõ:ll?1=v^3W?-Y-aɓRu zν[kx=M|d{ӳ=3 .}<jώ-c~Lw_{ˋlboeit^+ "K_^pPFjJ0:E'q($+ F(ܘꆦCYi(. uw0SS[jɫ?wɫB6buwy*}rq]'?0c7P3iݎ,fӂߪo#viLzʷ9Z45ΞqGTVa4Ŭc #c}l1GQW:kMsjkߩ2478]%zvLobUgɂ,J_F{A>R뚚c-'cY,虦{zp S3*T05Z焉 X{Hۢ-q,tfWyZ/mnjL~b6 բ|kS;G`錹4.Qx~oͯ֐NR[vԡdhϯF-kn<_نF}#(i~PinbN֖& hjYŒC ͭm5":D†X_홳O.L˯kj=S.wvMMV]A)hxx.lsgCs+oF~uAkcݤ/jZD>vܞIț|_zBQc&\t\_ѾR54M_D[R$xR aPPې7;>+MT=FW,,247}ԣFcGgfr 9W1_lR rt3+z'׳VU,TAtUMl|E\m[roN Z$lxx9ϯtiY=8xX]⤃|Psh3q\Ah8=vT\i&P3?̽4A'vlu?EYQ1ܞœ?_C0Zsō3D'+?y9 7?@q6޾a7Uupemx"?5YlK]ZwL{XHC˜`uXXXUW@]q-aFaeC˙ R<o 9Sg-\k  ^ynD d}FS6:+ {$km͔+Fz1o뗛JgH;>§suwv޳pWӣ#?d/mk] 1w=N_;Bi&˵RwrͿ1W{@&{yiGWZ:z)+k7P-{`"sFw~ii-wŁPuѻ+b8@is'lh=q>?'+5QFw[1; 9<6[ r2 lmjd0[eʼ:f^DAѱkO PU:ոvy3牝'ʓ~:QRUw 55C~qy 7sS[.ۨ3(?Zt-{9m cH2bPZf- S}ѲB{|9j'ЎB6K>YCv3?7'cFs멿e?W1өp}Qwn8♋L5zar|b)kS_7нIk(?pq~7s$Ƌ7WIY 7K+S ɣ8}:Ҟ, (%+vXXXoa?_̘»|Uz#>IdK%I/$Ǔq@+ I; O|)yd)OI䙀 Pu~S}{/"}6z{yd[/ MH(>O?_F!.eL@ie YAy*E{u;$I~s)שi|P] *zGjh~Ͽ9ۇJʁTnW2*'I8D8ˑO)0U5Ȣb{MJg/ʑ%^/)'d^Q&] (o<`?|ӿ1P`_ƿ> I9Pοl+YJn/=N";7pJZ^XXX?\l~`_z+|O@%HA%JA'Bq6i[`/7䀲*eA9UGڶCc bQI2IODٕ {aO7-FXX?\lS?{Kf_HA'_ $Y_țݒq8.r#+[ (nUFI%ʓBY?LO %_s(Cև?V_ `Qz{$?dy "xd`@H҈h 5E+fNʿ/ShRJ"KTFyA"D$O}s.9}1\bY^I Ĺc!YU!/G=-mG!6Kzt /ɥJ$ $C|/$ɢ|Kwm>mrdNN; Ϟ|,}$;& 3׻("Ҧ4PP$/@z/+AeTYʃ@aaap?2?niR2{U P_$(# T<_#d&D'%+fz5Ay'P4I>?_ry8=$V @7S^uK\ݧc igz}ڏ 70N Zpox{/ 1vl"P? {FŽ}P>ǀ#s@*[Jƕ5A aRd1B#}'<|U ʁeCKCe`7P>\xE?P$kKW&= |TxHv2E HgAHܔWK+O:*Z嫕}--@/XXX?\l;zП/lQ^e`X-&33-<*#% V7#bOMv$1*{ 12Yg&(8xe7BL,@ǿBDx??,9+-x}\_-a("JtK#% ]; J""" v3[,~O|>?sٙ3{ ,ņ؋Y%uw:=.-.L |MԕYS)jj溒HDMbE {>0=}==AKfu̓#߽:㙶ƫ&Jmy~dYիVp~8&XsAԉ/n7uѽDL's(dY~,#v5ZH7*);Hk#0\r{mSu7uuW!Y+C{2%FUJbɘqJfDA~LU[h`(zaùU[vDW13 $ ;d'S}E􉌣{wwDedgSzJgR9yStgjӻ홓{= p;4v$=D[UANpNCj`dZI}[ϓ܌AC ???a DT蝳/^*o}2 ;-1* ˶nνǔ}ZF6y,#FRWEFbuvVg]p>,Ocϋ轢)WU |e+ErŮ4p5 ]\s9+AST#;g Ub֌40cz9ksBԘ95rsc/}F/뜦 W"fB|^!Mshߗ RcՊ.oL#ߊFGZEl޼#*AIi xNeO5Vjʝ\T]MKp;34as(u~HBZf6y_q:L"^`'?Qspk✽n buAڭ,L\7?/-JM{###/[#BdI%L_`p|(/-)XGE^cպQwF2|輋˿sSሖn lzDJf6x!/85x$]Z֘h)_uVE}?*v);I~xu[4[u#<)g+]7aPۿc͊7{lpd.Q.u,6+& 2 U"9̛}tGŹe{ː(8l:(T%w|x@%תCY?g1X*D۽L`(ɇ8e^f0f󈫩;ቊ.k{*5bC\ž'rXƼ'dx_ݐݟ:Lo8]>/cz0ώ;%u~ʹ&7m|Jl̚e5><{N}if\)uw񊪚b㲮5w M++.Ⱥ.  "K*(-#w =~$?JL⿶)߫7>rRA̗ixK=%iэ{gu|g_w!?]_G'wh^y[Frj9YfƲG1Xe}3g|o]c&5}|P !;WQCUݳNuM|E[K{n;m}ƀkZъJ W$߶al6-/5cY^Ժ6H^D5XD0Tl -Xg\1f9aՙ;uϝ,%4ޙl6`}s-Crtf(W0"684k(^ 5 P㴶Ái/hg);gK!bn` KȾvUE٩qx?Wkc-Ӓ|3ۅΑ7܀,+>ZO( 䝓[hX>h*5޳ z7S{9ܚ~iW1-/n_XMudWz;mS>儶"1E{6so~W],g9`r9@~6 !{ҭѯI7t"#F8: 2*ݔ:w|@6M8U\]:Zm2uXϗuL| bVpYfՑ'~ɱY=^;!ʜ/wU4Xc+}NB`k~tn]->Z鿮f8Q6ۯҜ@_|m%tiv򈪞I*lw]qw>lX%SU} Yך:MoVs5!jc,#)$sHzudo4Tf'Hq咱Oh~fs_gSib_>4ճW]X]T,=+\xY.aT(* 7t52ۦifcLwt~ Y7LUulDy.zyyv:D`p0!l`jD:Ɠx/]&M]0c WEV4:xTudKQZw> Jnk?|,S)7<]3Z- w}4z{~Y3[e"+j FʮziȊY"wP,%.6baM=C/}*/0u?8Ȫnhʝܬǝʓsk2j]lUtv b2r.bnk{^kCq SU <<ل>}8*7CK18P=q6g|ûu^:|O'»Wpny^o,'/L]wK]M5d%c;X9~gc}eqVRnC'n Z7çʊܫ/Q^.2nYRS2 ,9񿿶,?#)R`i񟕑-6ɫO?6T^ G9>G!aZ3 Ɂ:2*\8z̽P[u 3o݌dd#q:޹53; x۰HЖfvIf? l+<]r@߯Jo>Ѿs~bж+v ~a3v?(=UfiIt;MЗf#CӸezEf:6]{l/R|`kܲfImqM??;{ò7gѴGo?\40]M=2y8~nM5-)eӳW|62TiEw np#|pFRB9}',4{ v&޷;KMO}v 56o+b!wطM\I]g>5]+L =h0UԷq_k*r.,-K!b w;(& rKh/&gU _kLBf:r/ʹ%֬)aX=bXK}IJıL'{>t==I_QK5iwV{TZ\T&n仞UX|H22\Ie&ȍf~͛d.Iy7 7Uײ老R/,6.hZР( fZwb1{3]s]vD\6&gGO;j^c~.}iABuܚr!_ZYcyZ3TAUl_Wl1>cJOhkbYg[6_|S >SpS*6/VϾ;F|0/0Uf9FQ|pꢬH_ M4\9Re"yϭB5Cifښr&R?l9eN{m0#" I&}GĔ Pŵ쫉QO'[KYe?_b,al녏N+ilf6RVqEM}oUB]TdݺgM5!n%7pvH=UU73MaI#8o'ܬˀG7+eTw=,sOnxkb<Οk>GϺ1;l2R;9*)+}#w;qnqT䓈^GNkAG y׌Ϩwr0컹!c{|/ f`;w}>o^՝;4զ:~l%{a },!mw#V#ԇ)?=7wPwk,ܖ']gL^J gJbh&( ^7q v2U: eyp/sMsl5_5חgŀN&gc>${L2^WUVnkrbIe#tՒږξ/ f&Fb=lU-K b)nd㉍ZTk|xPGCM`KƮW\/IĺY+fg/iJozfYݵPO N#*N5Ci1xks^9*+y'_wek#YeS司>uf.+m4W-˳\9a,ltr].ݽ׸ ZtBVgvqb7g~E"Ϭ>  {[);O*Lo(oلYky!o 㿦:1/buw YЭs=ogx#Nk%Ǖm_GIUNv9V^RSe]t]w՚4{{v\Z{e9KA.Nۄ4=-o+}5ML죮L~o*N8i+.dKzzI^jl(@[]MWPJ J/k{=PUQ6VQ/c\3>=r$+!:q쐔{T֦H=q1p;j|epGSRbdݏZy.yH} :+Z;yO\#?s4YJRj?^γ&C>YtZ͜ۻm>:1~*cd')3b E.nu'"8<$z8'}09z+h##E6_¥J;mCj~ӁS^et3+k<&߼zRtlj>}lBGkV^xfjπm)+[lpFM6(975 3 ΫLu0L9mu[ey^r'mҪxQYQP[SMm/ 6:vGߣ'ey#!yeR7@,ʨZKkow=xTT QX_;s#=mM)5ͽ3<y1x +)mg*[H7Hqnjl+謒KA(/W ;K)iᾖ꛿oV%C'T`H]_Ԯ(~4l Nf$gΆVw.) /5=ϥ'*;2Zp> 'v5z"~Gb*苞v1 8*:P̣t}[>0^dzR rvXomw.e<^E5\A ȵ{nek_Ջ lq4`w *.6=s7WN} `͆("z(=Ι,C^!ܞ!yy1 GXݓjbBP) ѡp8OQi}Ʈ҂a(gq Nl{<fsP?'gιb2 ^DžY) 1?wGs^R7@,˨ \L,nj}Pufgib ϰ巺 _&n2sF_LAC{wgvZbDWH\jzLco,7]G}ۄ`OR]Pd?qˀr덗p ŝ*x 9) u$8=jĎuw_y8O1 S/Շ؊X)_eW<@mGh1ޕI3i Lǰqdf_跚X܅dUF.W{xELm뭻sR#C._ &Wn=H:M᥮ХwGƯ^W4Q#>wU&Z1@ʝluPM)[&βZW2^LpMstc ȵ Uzh (7{g *>R.P^A-4~a^Vj֡E \BS5<)-MO w5?u ږ{}=9i`}. ߴ "K*~@H0W Scש`(gS AG',|L8@8 7.(p/%Ād/Kz6RA]| ]#"EcIY@/(EcXB& ɈO&@PJ(;p)!FCl 2PObHi0A4?? ŏlj,?)WOQ?~DPOa>vpvh<Ǡ&1DK 3|Jݑx?XR)}N=apdD~ )eI';d.o?0⏴[ ^Or'P@CTƠ@,70lOG0!ŀY "1< Ŵ{gyB@/w~Ѥ@B  XlӠ;P Z >j???i{'ĝ$1Hl/Bz/1X,dp l` O8^d }cI C,{@ %~H B]8a1X!oY蹐 !RG"IC'G ##/[q"Y(?zPC$ )"XDғP$ސ >Fa!oÃƐGOdM$H N2 D l; w$ e6Gh%(? 2 +@c'+blo]@lu???i(.fA pX5}FxFpyedQ9O8)q eŎ2cА? ]k L26B\\j A@JO#Ⱦ>QRSB" &FNKA+t (88|F3ce/#J (7E30JP(<mA@yvQVfyHÀTE%\i88+*G|@ b H8" |&Ok^ G!KR;_??UQ???ri,jԏ%*Iߟ'J@?i0=CCG`a v oA\5@:( iq"tP"ē K|'w/'X1I@ldb,d #.xe/TTEb]/?%&YᏴ[_\0 Fc!͋EC7T4racqFȅ&`$&Al<E'24&IBH怘!4!$v- Az ij u& 3WBWEh!,ou ,΂OO> [Twi???\y߶rUCYtb?O 6`L "<498e ~<ib^`1p2 ݍ6ll0YGa'2JCLs*$mE ~')6R{_e?/owI)^ՃpO R 9Ӱ! a7<DfB4qj I:Zl 85<H w ts_0@lʾ_8"t8d )BB1(4 R"T5 .CuG']_oTJFI.UI7GIXh :Ž?K@> CE([(3Gc*}zQ*]2C:&FzÓ~L( Gg&F bMQB?(@B-aW<ң>d%HoʹXͿ d0?@L>y:`H d ё/$t,aA> e䏼̐;ĉFͿиx ` oQ NjzOo(_\d߁࿌G-E X({ԷVk# H!s;<>FX1"g4e 0HHr?#%w8 y(q*D pjJc@J0Gth,IĜ-aB/0DyM-̤!G}򟺦#w6he\/49 .xXT6j`݅b!!-5 L`chED@0[ANs^{K5{^z}ߊ?AdJn33ҒBL?֒k6X{sjIObٚh+SiUNG@_Wgk7 2"HzZPV;:q,}Ҷґ y?:t<;F#H]K(*N:ƒth- f5UC _yj`_- ]mʘ7n^SDv[5g2t<VySTa=$6_# uv c()?;H /L.ڽ2@3%-0Mq7'5Mzܖ[g;y4ַųv]yN1&@I[˄3;-ذꌵE{j?0tct:tvnmni`IFG cB U7'qO\ht2=l٪ l9)yk\JOIA_oCnԧi'Sr43=5).J(`R\,M V+lݣo]~)7]Ȣz8XoU6sc'e) 4dxxX?Rm5]?F>CۜڨɳUۆ}9rro]=5_^?J]j7iI&W| ^,T.4͗qJ7kERR˯Ls&)ާRrҽ k.9[qOKYE.F.揢zqi9eF0m %*k\'d(=naދwG~42?-^)JEN- 8M/- zZVyu)R+e.9q:6dfj4w;9eP jG[ (d}a*Dֶ]ĬD>Nvu!Bb#l776>;3]z:Y[XvFdN(ٺՙi|DMrw&''H}fhX{2Y'^+G+H\Ϣy[ܸa;2âDH.VtM}8QZj$ :zBQjBb\(Zw@iܺl+"soվlְ;G>a^1ޔ9zɻrV(]8NԎz/RldܸqD_ӛ|C=tbɏ⃓ٛǥ.1Ro5g~ӺxR~ArAs8D=qaQs~)|H>*;dAKA33-ue:ѮWW <5og w9β޿c|4!&8yȐА@.LvsUSۣqևVpT]tȯSܺ[ԑ.:\r|]UFZJr|Lx0߁ "iZeQPVU} 1B>hkinjc=&>0Qnɱ2 T@8:8Y:>ؘZ1BӏE݌d>&62Z7yE ELEo[8de\]޸^2O~Sc75ObY-nM~xCƷ}Yf62L}SJ cNF#swZ6>gjː v/IWT܉n1gv, gmPIe'.V*}zCA/e?N92A:ncI6n?r#cn~6Ԓkek8;9U"W\We4L{P[rvWC, ٠]כ j6KdO7Ww'2cTw}s0!4))16RGLGAi>= g?SgjKR.,bz;־nԧiC$'$.-3GEɉQH=--T0pB2JS=ܜ@>I'{jkz6](//HhoZzT$NSގRyQ[m3WU^5Wq^$f&)PqB -w2)0SJBrN< U)Cq_gyyf~[9'J .=nue]d }+l7hBtEJB6~M_sCxf>Unvݩ+^ݱx(oq Wd9!#6-Xmv ʤ;y6\rʽyBYߘPxRi<W 5oٍ>;AȢZQ9SMͭA~Dbfظ`Lp>u XR2y?u&?EEOOOo 4i[:x8Q'NV"A*ɠx"Rw=S[7?0Vy8p\H}~>$/;{کlڼS5(5Dzg&T|4[Y>}ֵ4XpzZ;cFIoj(߶8ͦsWl}ٟ;1ѻ+W1CgL;/4"Q/+7ߒ 9$ݠ# ߦifbMv{_iK"sfs^K1G-N0}1~Im䳮!ko=žCٶ-T>TNSCaM*B&9w'6,=`.zcN~3yK -I 䃯^padH9nSLtwǮeԍ M3G)}007J38CfN! @;)!L:Z_Um]w\ދ*Nf%'&EGpXAOOo 4YX;h젨̼c 1QB1|I.v&ZEN^tNpT(5E" ݇/ ɮ֦/]QX~p=c "*[qK]^ܾ Brl<0mRy>JKv<ᇭxs2=w}ۚ p*ܼrJ5]䱛+M I?8= 0=boAFQÊOZx>aax64¸Jȇo$.麘48,yYhoc ǧexLJvrvB|at04D,@fNF:=hhQV~q1QB#z#[uD>M^Tv`dBjN~QIYYlLtxhnNN̬<}!npxL\\L(d|}]<) Rԩ.X{ s|1^S{ 9fydJl`k2uEʀ-iӷpOe^<K%#Ztvl߆]GvK\|cPf6nk̕j'_^YL lEGN6.x5ޣ23Dvi,rVH}}eu_)΍0m}{ԵŏV6onjʨeЖϺ6r#O8maF3-qz|FgVm>3e}}XP6<9 (t(N\RqκzI^pgG")AW7j ?hq6U+ooPu7ܳGyFukj(b6If07,@ݝ-tZ9z"F$eE#aT$\>[uD>MA&v.4V@x(#'HaaLdx0dž>$7WWW;K;O_EDs9LF񰷶s٢Q|j{׮K|>o5 LO-Wu04as]=WV"B+OT60,K?.1suԊ,zl5חە8Ȼ㼅HmYϺ^nA,L9ee%O?xmmdVml3}n;C5'DA p=zMf{MV OPkنTW>T:zi%+]SʔF~Єly<%7V\4<G}7:Ӛy&ih!)X=1o hO;Z.llŒ-$s 8 I3Ԍ5quUnd)94l@%{yzz{RN!ٙu&QĴ計0aLrwDOOo 4[9yY褔Qa|Ԉ#{<<=<ܽ[&rXٗV|I/4K@/ޒr&]K4뭘6e}1 vZa9ywMUg0Im3Sw$7ݺrOPYX,Ë) Wq-­ZgǜzfʺSKbճ.tK-/ {Nˬ 2^W+ 9tuJ&:SeWf_RD0L)BmGz;;H-mZfi Q[D!&hwg'(xl&ȠRm,l\ Ȉp_*m??|_7@Dt = ?$*.9%-##=\`G&{,^ b\"%QHnuL} k s4GՍS.7ύ~8j5*+n) y$jL=UȏB HuUr[Ԝ-?i5|thE#K& o\]۝FlRlLޢ?Jc#lo(vǂEg(ՙ6T3 oSry~{WZ@/Wګ.2٢UcUh| O '4 JRT >}+ jon1>f^;"rW;D4^?+øA9!]3Hd͏wxm4-#N6˴yp )QQ=9:ಔhԜEkF}p^V:m#h['F.yz_ɫ<>Ħ{9:;8ٹ0,,a#Q)Nn$_ !'B`HOOo 458 ` #b? 4_ هLB70 @a.Aw,̈t{cmɏ-$ZR}mOƪ+2&U=.otU'9=]>r`y,CkKݯXmLvRrެ`^uz^lֱSCKYsaַ~t8ɤsׇnV֑cfEށ+6ij4.jwvYE9[k5X^LTnS~A{UaϘ0"]NwG ;[Υ2AAw;(2.!)!.τ4?_/7wO/2p0<2(0(0x.T_8~nԧۗsA!ᑑQ\R7Տ`Q1Pay2QXl#0͗.V.ڛO:.:nsʹ|CUt4DQ0h!1o}Hje KUێ< hmѾbZ{+nӿUaա/VR>mm*dr[zU6E󆈶(ۛ9p̒Tn>g73'xkuq:3ΙVK5%U.}90uTh<4*= $[}sjsd:N)н:CUsssOG?I"D{AYîdct9Jʼ*4~X.Vv1.4n`8rܹIg%?t륗:vGۀNڎJutN9[/L;#NLi8雇/}\t0c?s46RةiHf$|`ACh7/h!F -["R: CBCC6T?/&'EFEEGq!?Y.Wt ί1xZGKA ݀ߒE-5jā1+_~g5"9WeΦt6)vtjED!3,e??cȢج +-|nq;~bQ' 6^r+Or׈i-;7s3h27&ϔ2mh>>62ۙh;vݶΑFjk:[c4o[5.(|2̩Wgi,k]nO]CWhi )XxiɧX]h̵\cw~~[uqzXddJu󊹊j3iI7:=5 }p<":{FS+)V^EGed ġyQdr@B8Iq7vm.PP"˾%ۿJ֨09wwڧy- 竾Ͳ 2vJڧf~mKo09^}eu=juu)X"|fyXǕKmT>~KY_6}] Lv5Kȕ/ؚrNH/Z4\EV4dmwhXx2Dy@>>>^d/D"GBOOoH}P?|o`&vgŇ@G}9Az ad ̄``Б :}GCHmE^sh0b؂,`AtALYt9Og2 cO gLr zq1ќ1tnA1ND?\~Q! f4 |-! 0X0L&= < - $eK ]^6/g ]0 ''@"uUW9$᪂y0vBz 1C @DA5= $QNg1?Da1AT@ A1A\t&5\3UO bnLgBQAl#'4r4 χr=*yaE)#RL#@s $?ßhwB}?-SmbN48!`0 j*#|g63q!< bCn{@4`6:f#11ƒ-?v%<"$(ZkA1AG5  b00X *Ah>hlLb'4̠Za a#2]R20~-Xak;??܇ɯο aC]+( 1{؀$G f tc1Qp,}i 6?:91K{P!`+@hG5@ @|졐g`D-<䉫nad/#ʼn  }1m{Я_b׊98 ѐ_7M Ƹi+a%*̢7bvQL#zur;DZwaTca,b3]m1)B8Ü0y A]!,( ļ.IogOOO;O[čVf>R:a=~z.ᤗ L#!tT Ġ,&q8b4F@&ƺQa@P:[1k`F H&90i’ȎSKAb)!C<Ht;N{ ?sN_.}7=مpV(Q ;Ȕta4bt*adüD `YI1=JO&c0h{#~>^ сD(LQ"bL<):* Pv_ψglw/%{*OOO??Ի{ Պz`<1{@Gq?!!/LǸȄ1r@4&F!菙$S@l;c+A$a:9 DBlHlSat] l/0~sA: >|,0׋$G{?DoNŝ@=f+ 5 .t15G!5080%"Pu΢tg) ( j nK+!@t/7&; cu5AݱKP`/?*qSb)VvmŖO;Q XKW 1x Cg&+ w@:Nebv:HOf"FtS{Ʀtg8_~ , v_`F1`O: 2aYb$^%&8{l_ yHuEOOO'# D1*#(^lUu( Á90/Y GPjb:9%BP;W@\&PGLlty)V3;6Lߒ{,MA/2ܠ?SQB}!L.Ͽ~NX,_RXbqC߉ku}K@ I=Gk-nbx(6! Aa5 ÀQJ,@l1 rrKD#f9Ce=P{=DqX@&>]&@0@$g G IZ' d`()5̏~v߉?yý{J-=nxľfaFR{sL3ØC!^ܻI~!Ub]:ň.02lOt=g?;GCxX7Ñ (5e{/??ßhwOBJ=zI+Z݊UeX/y<ЅrWW2ѱ4ϲ7X썊–ᠳ!g TDADf/}%gz%sPx0 [x GULGK5MPi[QQn5d<0ԋ@w)5Ѝmnī!^œ},x( /qۻCĉaF3t/d —G e,6:+z[hzC2 ~t?t> F' !_!{H{~ (؃UP@"=H\wI/3؍ΏC D߿ӯ?K.xpKlKJ΃60 w4:ln+!Š#? u&\x/?QSc` -[ &T ]@LA3!$݂|9RwꎣJJ_B'''_/90@/x} 8T_J҂ЮDv*-R)[H}ɾrN5KEH(Kl!v-Z_T;ѯw?<{zfΝ{9rDB.2~xO$xUPZubzrlY㻙/3/?)!4Yge:ܶ8Gbq/EJ^5^>~窖Ekԥ~52}ܯfαGou3LbXڼF G<`З:^Wŧ5&\j3:;1U,NJ|r'J?>&(3WV^ᨠqvc9Fjm@xD$p0bfaZK=5+ݢ"oycIMkZ:zSQe%;zT<44vҽ{Tu>uQ&텑,읇k,-46~l] vL1Wwly{gf}Dȫ #-_֩w;0wHw.0?*NUT(nاv?!NC} psssp?XXu((( 2@  xYZ9@q5r"NGLO/٥X7*/J:7Pxi>ߝ KUg;qK횧-ʧr {"+}W $r [TT2ν9;V . r[݆aKk)T.SMUr[ ݰ37ݔȮ9BLl][MW .D:8" A5ϬMZ˿}zVn"bZ"hx1Ȋ Z5cV*oTd/OycS!a(KIͤe}_w:[jB>R1bbbO1ysUL,_7}c-9zYE/µ0+%6ai6P > Iax xbÈ~t鸬\("y˪w OI L &71]|A6)U5uT?u~V4C?i[=~Siaޝnŵ1|8Ȏ8Wq N _Yke;}̡Ko%M߉{ \x#CM %[:Y g#9}pʧnEYi헖e=bzF}}c!y+K>x(DFEE((( 2.$,((88,$y{:uJKы_^QyqqQ@?'SmU}?^{}JcݯLp=!j?M-T%ȵ mӈn,IC.BgWG-ˌ`~mg޹ĥwYhx]~X V %+*9sn 6^i譩@quԏR}4IƊ)a;XHAd VIT2/?!WW}ϩe|1iKB;/Y<ϡ]/:H}w+7DC~.)3kv*xF^{UqAy_<104":&&6&*"9ZxKH0 .Nhz3ݾFy+YX'S={}׹ƑMFZC\hIqL:igt+( Z ko6W0=dǴE⟲#F]/ņjzc6Ob$p,Z{4ZE Y},dydʲnЩ{'WV;4EXM"OH=Z᲋UVPwcmm%{6Mw%[^5B#v3ï:+ n|>EM_}Q =͵>gdn쉁B#bbcB\QQQ,2%$,,<<",,$ȟ`}=;ؘ(QTVԷr#Fg]sهUU7R"g-e)GvW؃?o HFy~ %5 ?S@*+5Ry |\P|܃SdȒpv9DM kb,OZ|r]QY&#dl &/m=u~˺u:9M)L*_x7CW.{=`c+ܪ\$ć8Yٟ!k{0a9˹i.<3_dmJxꤱiH{8y&]yئeq?ΟLJh݈8;~GYz" .;ҁhndvQ'lI]5_3NǬ?Jkk'{л.MG"u hz߮w,.eߎ֒? 1$k~PBp_Y͉9>|9~z~VrL0boifrckg/htBrZzFFzjJ???h' k dgif,+gϾCJ'7ܩ(ʉ0V'IUCw_<`urdWUAuwx[1,HX|ዜ[eEDk?5ś߯ӝkݳV<.}V⇴&W~6O&2l+.9jVRjRi;ndUU/ώy.<99esLA{}LL@a2Rƥfi7Jkh`wHQ?,6)bvNNvVf???b' k p!..66*"4tv C{T`o=4TrJ~mew:dEjjKK]Q+,i"@uQ~~ZlOI?eۋݍy96 ۽bZ}>cQƍ>~s\( \phW7Q$:zE?BJmE+w{=b|rҦTEn{87W*٫޸aXAP\Ʀ8?z^~CKyOUu|ZޭWZ:VG J z~-z-i"uӽMV\hETܝg,K:&,^TbD]"7e̶ތذU3'/;NKkG7K9'js 6HsL_Llw^`z\v^G^r o~a:~>FIo\M {8XKRQ3sGĥ^#P -Zbww4={F^p~Y7_d\Wq5#c_ZiS#Y r&m`xy-zowt\R[rs ^IYnU ߥ<n\Quf2IkX]@k1AvƙpoV5U_vX= #޹Z(%G9ݝ۵6r̍SXwRgﱶg]t@a5Q.kV2% W-w!/BdlH(\VkFv\ .m־i:v\H>Bvn6ܒ-BZ ~hzl#Sև[Z/!l{lz_A+/[)(ǩn7Sl/J3猲wj2^zOR6&vX51i L9D]<$2[4=B\+7\ߛjnFbl͌hkiKW:gJ̹Rx+((( 2%(19)991BtD dknķev!C'C2;1lk] 8-+iÖcv;'-\S69<*Uܹi+vLěac6pX[z/9U1ɹo⠓2ٻ{D͙m8RIク=GXqk ))"/ߺQaٚ})|DW<#jѤU\Jфݴm.ynv.6Nf-û>^hA#]@| }+\+*+oǠ/X{B˼"4#-%).*qs259sݦRZPrI[wfέ{ ӂw>SƥP^j2k]kyo r6<*8$ O1X1g_t4~J\aFkB˃,Z&e#m=ܮV[Zx{:Pګ3{%^ ;$4cRdza=VGհJ?h&'[yl$tV +b⺧ZXʮ%9s):ȤȀUg{m UN\gW=x˧k>]K4,}j7]eQ'yf`bîbg.zR9IKNt+G/T<e&PYyؙkiP;ĻMX3WJʫkjo/X{B˼LvfVVffzrBlx`i}vkȨcZ^tݶZj_< c={9Ϲ'=jAzyU־67bFEzh.nյa517n4{4W8ש>*gUVo>(:rEʁkm7ae/},)c{YBѠ-zF*tDhGHhZ>(Yu18U!Fa`tTSכì#l26Ixؐh&qEpX^Avs?;hq@x:²׿^͂"m E9 xWsU,(%F?sVm}CC}/???Z8PIKou+5;UyF4>Yn;ǣG=mwn\*ܴe?,;{䗗EQ5vo^8voC{q aIື3Uj):=PF1r+SAq;&Sjoz&.ÔsI>F͑$\PIR=*ݭ9D]5&O sd XU3wج|Gbj>$ ^JocC8prD/o|$Ďe:]xPθ̿ۆ˾enslvaty)dhzgD嬝+H1aʗe=H/}q_Zjӷ}{x;)2}x`xZZ6MbVA>!WdV_r2t91,6rM"ׯ}p:~=W0- UU׮痔Uճr'DT6ܽ\m` -ZLo]Bsw0;}؁%KXsns ɫ{򕙋_+DgcrXRЋx3Bk7hlpH+;s_N|wKgC:[ub>nev^x>x:b&_&[l=q~k ^7ٻ})8~yuRgobt\Z^i)NlNʊ|5.tuKFnfC_ca\^-!ywmN]{T^q0<ov6gvK`I@ 1F@dͶ4Ti 2KYA `2)^bh#C0B8c.(J'P][HB38S# T$G H 4Y9CZ[8v4.욀Ի`!RYd'K,F'? 6}HQcT OQ:5'  UPQQ /j~ )O5q oZƏs{p h0p$QlXNaV>@l 8JjA [`.QbsDD yӇ8?l pDC6q~$&!$V K "EȢA P57!~]FA>??vx 8Z.1Hicj~,xz3lD$\G=;^Sg(lȜ#9`;LOx4 90,g2|zb$=?8 1@X( l#%U%D!*H:HAs70G]HOМ)#J -vEP:m,.fy x؜G<z}<,d [da`gJY=G208A$g@%C 3$ 8U4h Oq98G *Lt9-8?.AAfIh}F#0eHR@\01_@b#88,50 *ܧ*|' 6n}ӏ  $?  ;Hd` "Dg8APJ9?Ծ R] a1285?2nSG]π\B0լ 笁 R/J{Y O$/ @KF?m,SK]tKj`nLae8 *%ȝ_`>32~DyQӉBv>d;|$D?Q)y?1 %3EH_*F`D> @GG_/Bg ͼg.sDNJү0f1Oͤ!iQy-/KqQPw𶛈su&ʟi($dXe`1DFH"|eMdOB@h4fE? j@?:/_4#_'!Mhr3bÜ'PՀp,_#QHFyc!Y;A˛eor )DD"A;!$E_ĹŰ,E= &ib1O *:*@>.x}ysoSYMX˲:مt[i~@i}MUEyY)jVjF\2+k_6577FGG__XԔGq!~7PW Pc9OIw@,~Aoftm/d_ҕܾ(hYTYW:';_37D^1خ߼I]?^UyШ5k^ uɬxV:}nhat ^V딡.USPNf_۰/iY/i+"@H\cߏ[6 s=Di+Z"#C|q-y7 \ZPMsY BMLqsO]M>5(pvrwU>zW1Q|.ˈmO{9#wݡ(w+y`oU2k0yoN穗z׹4Vr Ao6'sJ`n}K֖;$c}.+#!iMZ¡ه:jk]~ ^aW7;<7?沕M6nɹOj^6yӚo"+*iiIaޘ tԕx~ίe.x54apnn- Os`.(]X=ǥE{X`wse}O3#u){4/Է.U ?#oq2ڵHVf<f=;/0;uhςkWnܘ;"(G*{)1VDIWnP~4^s12b>Eaɷ kWHKuyҾy}k%{[i%i(^)a*"?yU$;񞯇QiZء>o~M,??J@l(y8-Q܃{7KM ՕdEOQ2 5pOع SR";){8&wqLGUYMHUj&_jE|{ݹyhi4{)gӮGBmy*uoЋ{d9|-VRsғoMDTټ{(I"I?XkȤOצ4 Wb~_]Ybm\({꫼wYgM׳A>~aӢ=4mx03:5\;{>p0s[&m{~2 MH6.&yfs{s[n&ZϯF VQNo!qRTTCmkh֝a`Ӓ4v^޳C3YFv=|.(==6w3՜-ܝ]-n}R=rD/0+֝"߾~$3.Q)5ױp4ufV7yUUJ@||r wI EY鹩B׺x#s|-_~Uy匎܁=?9~2Ho*Qcgf$lt3Rk68Xn/4mWSzW-JE,8[ߛxXguz!p?rQ/SӀ,G٢G]W[6]o[ugӫv Q>ܛk֙}.ҶV| _ޖ O4F}o~mw{9Q{9J";.(l'i9v0?+J b}JOCIFZR&'ϰļ;{{{3W-R+=!&;+;q꣇a~m̍t5d֏I%63nV_wp0Q J`Ϛxq}nQ~r(RUx^`‡UB 3v)PL`c7ŁV);ߞKuOBޡ,ĦO3e coz4<Ɵ! |.pf *[=9XTm聯w3O ͮpLtۏwemh8j*.}ROnkm1Ɖ0 ; Q{\>~}0u:h]P?ϸ7~5z+y4\~=q&|6]znܽrn!13ݵE^ʹkh陠߭Q5M!(<"/%{QI ߴ,tϺDUj{;08Po DVTdg'?x|XOCYVg|rr=ka/jJ1Ah;c2D0|5Lz14{a4'񾏻)f蘪WOʳ%eNyG%'+jCѿz:d/S)Eњg;|Ը~6jߩǙl<aBMA]e! EԱYzO|;Ar>R2NyԙБ> SWI`G90p(w׫n!w2rSdc+|?lk,\I͓ͮu>g:GZD )d ձzFgu_vޟӨfK.W5UwPDl9Rٝ O6u;#~f$^y :\?2)\(> \=|%8凚l Q`,Q-KC-yiIqb =(my38Cv??ş{ DVT =HI YT>m2'SOei~YUww\GIخϳ,+:>}Iys>cpHF!0mW]c}I,T.m x!K̯&Bgޑꀏ:=2j+$$u?(<Г*/76v>9,唪nmァo4mŽ3vNn~82}/+c?ެ 0oNh Bw+^C1''SU4DZ)bM O|>I0xgPŬb]zMW3&61UEi1 գGiyd,=g(#.9qOZB|f}AC[tÜ7>|ܕyϼ"+*}s3Ӓbϝ?"Tbb7杒 y(^TdƇp2А:"NfUG!nD(E׌NvצzKsR랸C)5Yђ#O6T[}}!e[%x;}DȔJ"B|64uoЉ-|"%GL*q ӫ߱=qK!^xSd׸0Wq|t˹~L\.9g01o"W[;O-L-O=,O9}CןP GGYkRVp)n[Ǵ]67J}hR84׸5gm1aqEWfɈ3? V؞Jan8$&le(VO>xCSϒ/0i o{h57w"w -L M_fd? :YSuͭԚul\SM 51A.6')J MQߺK\.,ghxd17O"+*?/JO _w`zRKMAFubf~sب6mYQfݛlLUed8ulW[.>A/!Au7.Ov?1JqnT2+/M/>K \v'U(H;\VcaKg U4,J R"Ӻ;b:{90gy[˺H\│7r>nZ\q˗ʜɕYQQ4ZXP}7ԞI7T?Yæmqw}5UpS4q~ZWyX} w^z_^Q?;Hsㅃ+tlõg[fwdl}՚~mm!"YrCN2 ܃ߛã3{x^bm?lZNףgl<-i_:ݟ}4 @N谀[a1Yyw 2}kKh\X/sg^7Ma/%JDu:N8tyoVnoMSeƁ6J>gr?zQǵ^ہ i[̈́u9@e#Pg6SϽfOa?%9ؒgmѤULmMV%SczRv7c*2c"Y]ϾKjo랗e߻r2UWvյ[Qe/ZGǾf!##ZWzBdEeOߧ‚+zǔJGL唋k6|xV/񬱮cJBR<r˫RcB|<7a1.a1;֙穷sH8\^X_ohNg;CY-B]cKZPh׍Jn^# \UnbvaK@BSjL9Y4z'\c5LG{.O>}IQlK,jLx3uv#"CkpH{Oq`˼5W^]`&9<^ (l~oZ! z,)"j׾b͖>Ъ*(Q~ g\ _2.mr(hXits,Z~;*w y(ez$J'o64w!/ og~DkpREØ`.[Tθ?RYI^jlM7Kc]uEYC?)(x<̮xaX/XUYQYO{aa~nfw|WNR%sXx+]yXmjDyqvJL/I==#j>7:D}G(0Y1??ſf DVT{?~ ?/+=admKNjk( SҳW8i̭}ib\I~ƣI'/bB 2B;눍r¯OO3TwޖqK/|Wܿщuy>õ'Y_>`4e'%`qڳߏ2 䩰wb_sd3]\ٛ.t )Cqλ͝*g\&Oo3 z8?0˿]4EžMN{,6'!YK+kS-u%U=eoS &?"&OyDhnp.c^O{C8nm|Ru~Hv;Ut4mco5ķם=Hk%CڽspfG`V,og^g}iT-KS|3@nuGԜn"_+iʚv߿WG%/G<)-M] _Y^yk׽ꦮ/Ɵd !##Z WzBdEMX9ُy;Y1TQfdUз?}xUvJ@oYӧLuNX]{AtDOscxO\ظa@Cz[dm7bYtk,j`0m073966墅1eEY** [_V\n`뷑 UJ@ Wl-Ok(*le*p@At-;>K.H8۝;c~XFdfVRzZLlMs ?>-g yMo0Ig`K z| Aku2֥UF3]rĠ@6]_4% g d5p3yT#N^9nmCµT3FAPbLk^m<ÑpݐmCpzĈظ0Wǎq{?Ϟە:EYE+15}6 <~9*f8$tn{ޤ(W:9rFϏAkpHv"yk<"2ƪ"kN貲>4䚪۴+a2Fv@%џ7(Hbey5Q.^;5*tv4eh#+q'/;srA_ha|BSM5  C~яKZuegEGG_wȊ; ,@?H `H0/x =%I:7фC,@x CF x 8 /O]_0^8&#鉃JpP;“",)YoC&4 XȎ^ Zh ' j<Zīh 7uƒ\:OzRK/b Y###Wc݁,d_vB_r#xvNvĉG( :?HbWǃsF`> cO^F*gb!Lp!F 4+"hB6(XbڃDc=~ @22.Ho18DT4$$ Sxd=a0D g1iG.!4 Wx놅V:a 4G=y.p'qX(CJfbb`KOԖ_ =5⏌[ᾲ&;KJD߲[ d*wƒ^7qg Њ!lMd1͆;j,OUm X\˃yb1 $Qp"+x2M@P @^Chggg9AJvF &Պ?2n?Y@_b rv A P8< E9C960  :88G4X(KkLemvxGz%>(p0)TiKҺ&,n49Gj p94-?!Rm7tj?< & Xj[)`v@̃Ley?` 7sC4KA:C]z[Z Pm! j MKĒ-IZP]u`Bܧ_> >,X-)eK$pYrm/c?T jG-nykI. 3vt,|\`q}9g n2z;JEIANj\EDW]Qٽ+׳q;jy)GϞ?kH $y8X)h[Q׋K+sKr]N(9uz)ӭRi7r1Ktߺ(酏{boExkIT^xLf]Q.ӱ:ueJdY.Rd%zFƹU_";J5My %WF97q2_5/8sC?/a9(APQUՋ@tї?ɽCT(ji!&5[nYI[nFi||r͋7W\&ٳ"Wrf.U\Կݹ:z`ebϣ1TqB_zvtt.fZϼ%sP r-v\}LnGhqVϛCV]Aa.>̃i5 [Vxv-NжukMl{0PNRWpbAK'kc]U^y}FT\o'd}Hy\@ͩy 3fu%i C|<ՔdYq IkZz%ljoH =/kKSҾDXt<󒧱 QY EZo^2/˓n=. FvR8{%c%eu'i̬;\A$K+jsG7~G4j+9g #꼭 nQtI+-ߟ\Z]Aaxݕ }ÜrK-?S;2u_`w :j^,IuOls;ڟtF^=ܝݠ𴤜Hw5A]! lZ^A6&WoSG2yeqλZfXV-Gۓqj&&`_ΈqtZN$R̭҆דհ TW\l_{s߯m(cwuYe__4`^|#SW_gMg ScEyuRDZՂP?S= s6l-pV?&{^]UYZ?o95'/GzȮ`IAvZBdy%y9Eu=sݼs,.2BQT윲jmۮw[ f 7\.$7 zۺ3,-ُҖ/7]d֓fg۽wJ9>tǩonRd!!okk=wjexh|o;lӚT}y}_MS?h#6-(eSҞa[ݶ;Y V. 9/ж?;Eje$ [ t5ז%fe٨=uRӋ<}~nY-|=l|qS! <(ltHzů>ctcպQ@BA08=Gw~EQiF26c}i.xu7qݹ7Xr×#:NX˜~#n 6/_,5k&]Knoibhm]Bl UNnݶsRqUM_vØk[zmNM?*+ˊ #ؚh(+*& C[kR"Cv2gNU2t ixTg|sò(/$M-w0'yy G=o~rZ8Si/L޶?F~H\t%θ*sƠG! % 첐߷>trt.Ak:'>@= Ğa6W*mJ0Rv I+*K 1P;_̹fۻ){Dt입7,r7SZbfvRC_op*X_>{Us%",Gjzcx"Üļsx稥Ongˎ4s~c/*0Q.K,-}fP~=k]Mکgg8V/ k¡Xa#%iQzJz6^! EIIq!~Yt5TvspӶ ϫjj}ƪr2888 s=6&w)/-&yC"rf`h|VIUm]MvԥcG "Z_f{>'AԞ K:w:n۵mdO9wOԡ+ Bϝ.=K1oUm6\yszYj`VkZqJ5nL6i}>U1]nƦ'o8xO?BVtnûR$"UkgZ>uCTUT ,l\,ME/e4p8Uր 5U*m7>}AheIv*Eu:Ej6/e|"١@i(Ϊd svڞFFrsc/]Ęr[V[b*iP TfchNŽeŧ{^Ʃ!y?O8r2+=T1r“c/6f:|{;)oK I̭~W/*K s[zmNMg_6BVJBd?hej!poxbvQŕʬ /gk<Y=dؓ',ۿq锳wJE`5 ^ʲP#V In Kq~ߒl'ozr |K;Ҳn_nf JqW?Tl ۓ9^ 284y|5!Ңc;3)B}r;4cXjR˪Jˣ},u M4D(x6*$8RT@PK{W_T0Bq(cC{æ\EV^Gé'ZPa;`USߊ-{5޿$=M}C6&f=9ɼKur/Y WxJ}ڷZ~Kߔ8{,hiي 4XrGhE_-12;!Vun|[@TZAVBb\Ћ$orfs!>AaiU# ,!ZK=e%\888pSnҢ܈-mQ -Sb`dJnQyEbL9[}(M2e-m7/4F/P.^xfq=@e潌nYKE [&K #,)Ħ"S==/++(hHB2 b"/)Hk?vSN`XBN}(--*fۜe']z6&m+,$~Ղw m%Yi9"*&.~Srb"BIӢR:Ph|Viݍ* s2qqq-s=6&v@E^nfjBlD0.vf&d3Q:g EJ:n&:2k/[yy`i߂u"ғvzX3эE>VYzL=( 6i:R=뾯ʝyŦI+zkWm,^ y8*cW^)ϊOHJJ̴UΨk50N`O뢷ީd;^hiʗ)wĻI[-Ypu0`a"xEp)[v~q5xDᘣ{SUALKֿt?aW'GY;9Gh'x'mٸ|A1?@YФ{- AMͽ^/s6V8+.)wH@Jǖ]X^Qy)t6:'&""&)oeU75 ,888ӗZYrf._k$KOXbV/ b {Yj֎s/v5XY]FbO.}kZRE_5jkX9yv5R}FT|G WT$Dݝl%%%e4 ]ws3xqqq-s=6u=;#51g|AO+s333 J/!3'!)9x9XHmfE#y$5WM~}^~iz"%ѧ I><49qїbaG !ս*au}deLq o8[^ VO%ԂBhMV~b &qgxvXuj;,4WDFpXzI \ֵ\GBDa]CU} TG]]SBGAJXA^A)cZ+;SD,Rk.XxbڣI^WtX?s5wR~n,)sO{ 7Utq{w^٦3CQ9m<<fB:׆W+mlIo%{oۜ`tO[fxI֮f_#}/喞1:Rj&VZT{[K9!xφ='\c2V&_dmddkZ/'\V"?o95ƛ-wZof%GG\!okeiaaimDvCb3RRR.xؙj+H7T 6\--׳L3/>fW$1r94Vu`SuL̽+㆘{{*ƪ@Q#ۭ̮\:'TON'U&^Ht{9.[WV:r+fg E@ox+U+mr#L\K7XwHAE-񡐻y%E5;߄;}Y1 ozM".|]+iվhnzaU_3g{V0_K 87ؙFPVWeEz<~_aɷN9[J~n e.:k5'w~66mJkŞme!f c24Ř|u3AE߿Z-chc:`CEG`^㨴_LVYUq!`FKYAQY /<.$My\@ͩyV_| <26t Ddgfw[u91O=}g3 bږ+~|{V|m;(u:M-6gtvFض烷rJ{:"p:7VSҭ;vOe&Q7|:wyw-?*AǫeCLO HrQoBEݹ'Ɔǘ)ϵƃgttuݪ y;iZغiՕiii)II888pS+-ohˌJv66ZZ&Ġ‚p?2UN1ik"=_iɯ[ncYK\2Ŝ#$h`vz~lLI%KG,6fDbS2V♌=*Գ5>wOpsω1΍mc6R|n̓=.jsy%ϻM^,l;zKrLbY K:TTœO[cb!7^l3ge2uOGn5(|OꮩD 2V^-fv[tdv^T-p6^21Pukw,K<';h(l)=Q=sWv^zahwGJc]UM9eo`Y>SD7nH Qf`땴@'{998LJsi;VAoǭd=wq`m`r'82)()9%%9)!??ſo >''S6y>YODPn\rJ4rrчC $P$P ׀r:D).4p'aUGۅX仐hm@@Tذ:[xB>?oq?toSYDv0}(4HӔN$K`"T#&x})Ka5, |/!.RyVJ _T3(LX $ `r{Cd)\'$jaCˆB?PUo@DOB ?t`PqOzC?>out84hG(խ4*%hFF|ie @caw/UFHEV/Ip@"4v],f hr{"漣|YdLuo$Vy0hO"_p(APрa+888lt}~;S;Y~ù<_'QqCȆ8pG2f:r GxG\_'Pr:DbTtt*(W"23Q} !8ч)!'RabL,n?V84`16+mTM⏏?K#h$@`6{5+ 'aDQl>|D& %@hZftLQrqL㟢6ΔQd4|@'"Gn&?6IBtOFӗDLSJٿh)Ǟ 93~}gmv ![r > c%kFf^;H/!8 ACDb?[Ԃ:F , O8 ʣ tLVj$Q 0PO)U( &Whΐ>AU 7`e|Sv%Ii1z辰888 |t` 5z*.NC4pc ekx /Ej$i ӖܠF4x "!]fqZ+cn@?Rn a-[W` D]De( w#«Y@bxΒD{&)XB;Q[L.}OYOy?>ose4CEH[z:5@sh$,$@fд8wN:Q$:^E#rZT [ aw(e$ԧ%҄7@T A,һ JP??>oߍE@JsU!jFEګ}Mi"!줲9XehO#HB LqF_J(2=UIpfD5$&dh^eZP-888mv[ŞXWN*t -;RJ^J:HUbADio:t):^4yG bs|6(h*BX@CUC~*(3B_ m@[ +x#ꆼHx9Q+t?Qs6PᏏ<)?] lKz+}O_l} [k P_v{` Nгx5 -@ }E~Z4$owރ2t}]`bgrG>pt֚VhD%_pqq)Fgy'ކD @:ҎDAy7GP@y 'F [ @WA;m2N6vA*T Kz\yGbgINhFALW5_@Z ?+Fg~{a9`U,x}uXT]()"4ҡJ )L 4Hw)H(" b̜}rփgs̳^Ҋʪꊇia>.v&~IU-yNmo.'{vs4ѹk7/4&bp" ~Y|'0=:\g,{۵/cB'ɉhi.W~!Qb 첼%s9В\vxf`9Aߔ#ՋM7"^8sqG:iëg"0(B:#PGá^.ֆjBB*KD5Wro%>ivݭ6 IYo"MQ6Psx]ɡ8!ܳ?xေœtrVb ʵwcqr28u>oM>/:D[{U|"H`eK7q$]T/MI_Z~03,.ϿhJg!= 'Xf財֢x4#==U"Kz95#&t'Ƅ"|<l , a)V@AA,|81*,$V M=c+'eE7dxXTW7t[5lt*cח):A3cZJM@U2X^t騠g1o_2IbLA}WIpF1M)gs5e51FiUw_[N jFþhAM#C$RͯnnI8hm|JrD]5Mi=sI>իHѻJw7sRv%xˬӋ>m܄^Yrx1J6>)pj5~ExI)m'tο8[V<}2) ]x OWoTIx&J|KV!B 3=5DJVAAvRxMS322WՍ]2{FG2Zd3MxVԺ>Tksqd̺ #;*N1ԆPqp0=۹5i^ p( 5iR^M|*{ċ hIsUk2ʚ 3~s'9Ma3bpEwO̭tdzhcѐbߊx1MW_?z`uE)jJʺީUbΫfg2C`ޮNv6VVVf6>~Q iY1qqQf {^@U/(,(K  z8PUVV1q O.}ZY[W_WUv?-ꖛ5qfEUm!md9W ]׾/L?ʹct8޳yK'yXڋ<.e%p(!Oֈˢ8ILϛj-$#}D'N(1|Ӛr,7+q-;$0:=>9=3^ȧ p/Y:xW >^ܓ^LK_/F}S_֓n%G?y|\1mYMo(ylB?~W%^.''y^V֗f̶g@yϞ\˛8lb<VZ\ Y}[ߗf }T$/1RWIƫl{'ʏ cn#--,- \!!1i1111 {7搕pq4WWQWPclq+2q`C]CcSc}UqV|$/3c>ٟƈ;,(Իxs:-+iS~ =_q*S/T yRvZ+Փ#GKrcyYפԦ%kyԢ;[.闥RrʟO8I O_ıOg0L}Nl"J/?SbJW_u ]5YֆĔ@ Y qvůF-.3;O1X/ V:Rpͤä#Dr:j"qWz=?_N t0} uk^҂'w3U֖ϴzk ѯ3K`cAUAܴfF<_'3ٹIOLS rX}mEw>nNVffֆؤԨ {|N]k*F6љe-CSCͭmMu' Xhɺ? | ёճ~:-JQbvxB%qo9B#'\pOc}eEQʜI/8|KO[o剽.)K1i4<%bp/kB E9IjyBGO-+L F:X43uu7*>95rAAA,gw{mWmzffrx dF Rʺb+?Pe)+~1QWg6-60[>)) nKĨ ?o[˛FƦИ{i { Ɵ|hkzIRDHHXLZ^/:ꑏ}}]-5p{kb\' wWk|R>EI"lx .X_OO覹 2E/ܫ]8u9ĉ{eA=u;{͵bʗG,.̬ž<;u8$ʇ3H=PR+ rUo\8 %~{)H*3aC#r_?LNu'G?c^싾pϛʒddy/Gtq"Xlkx kO'3#]]]=c=mckĔp|=n/@'GD ;[)Kn̪z>؉O:v| -qsayX}…ϗX$~x9Va(MET5T.B/g:EgY^y1XqZdOe\oY%› 缾xr,WtUju؛TjΙ_ԨNYNxĥW8Bw5+:߿?=d8.~y '-.䦡e)KW7:W802ܑwK֗5/ )>/rʇ';i/?)kNnRtȩˣOyKTjsϠ;v7X%嗏O ƿO.P\3WLN5${+DϧWZ?caP[YRȹT?2 g42uJ S i*7y`ik)gy Pht|rjFz6??y vB"""Bm&|\8x.(ZeTt>G17tqOkMQj !vzʓ_ a\4O' T+3XUPzpqZڙ@'=ywGٔ=:0 };5[cv%Yc^ihAiftgXul?!6PWK M-=+ʚ7]}F%gfYv{mW-'22<<$8P sW2T[+" 6E餬ۖf> U9cf6$sxnA17>!~3m+gOB uYu4.tD8iH C14~FRLxH`ЭȘ{?pfL-M=18OIL'{rn+fӑq[\BȲW*iw|9MbIK?zlW۟,rP4GYV55SKR?D{l;ֈt:18V3)eyMߌt<^?ZrPQr߃t)Y %Sp"af;QEagZjj5[8z!cS22AAA,}odky@ ʆη[G?£dyZSvSU<)"_/}{>G/,𳿹<3j$#)$һu!z_LS3)L; 3?`bp.8獎73]~_?4֨Bqls:t:ֳ_ q/c2yAzZ2oޓ"vIecČbwSkZ[j#R3Ӓ{χjc`6rtTMC9=깟ٹh7!~^:7(i_Q1|8&1%3+G g vF"7~0/7Gsc]hhhYDmOgWR0<{>t,#ZKN 㲵g8I{AlU.1˻g>hS&@`YGrh#ʚ d3l@?Gt&Az=Z XTspTz]7io*a -|JM)#'(/L*3piyC256,5L]F򪜮9o].вr}k/ w5Ӹ*-"ilg]O1Qx:XhH"':}_꺲[zI7\r&GzZ*cn9)_g#=E{#\t253p]qJUa|Jnܑu©oG\ =rCz`.’{mjt+_(W7޶f50d^n:5""vleql之Cc!b˥hs: o*D6xѯuMDлMQ׸gʆ]+?S%}_akvS隚ݘ;2BnVf2:%]=##I^ڒT d.3MpMgCmZJ1uu20;@)hh=PwyKy>qj6 epq8IbRWj>hV=Mqck.ljՅ/yڤ.]m,T$fhqT1i(|P.bQ˃Ocl4/9*li M^AIkV.;AAA,{DGm@[MCG"iwrI#uzYhȊұ2:5ln?$m75Tuabҕj>]U{T[%}uCQ/5˛\(D4v7Cn.KYg%x">;itYL%Y[ gnP䍇= 8=zv' )DWp&rLM-m\ܼ!>>^N~{x"֊d/yiz&OnwEmg)q#-aWz<S2DԶci!0Q~-2Y !+a- b({O)zҌX5G(o},Сѡ[^fz*W]S`fo[߼ hj^0mmvT nTn@jbEmnU@!( G K7k@P QQ<;|/U{dg #6<7/>n] +e-hRP(|ˀ6B6#6J@ 7" 6!ٶ׀F=B0 aFs`7@((lk|AAA*;=e^є@9P G[b-Q&qQՠQ5鷮QC!8 )n@aX@PC3`BmEUCĦ>~aF@6Qhot1B2npÁ&a gmCAAA qA^Q 5#Q|[$k\p@MmUGr)#3Q *;НY8svuTO  &6Z K" f:%feK6jG ſM{ۼ3m;GXS¤0)5o(Z@/}ol:pL|z>kpTz& 8,: N"6 ;Df(Rl14fbR(jr4|߳6/DLG ZN4PN92a*C 'K6͠n3 h;apˍTZ 0 9nЍ.a BZ:_-@~t&9X,LtB K,5X UYo7~?)LK$ƐT Vb 7d3ocCsjlcNY#7v: Rnh3Vj)6NV:u9RLPMcc,ߣ6/c-9Nb(4OBeU巕VC(d_N/9 K0dD+TlA] )DqGwbG>TPacPPaCG S??m\q F?P(FG ­4!8Z@PM: þ몀Rm X@ /nE Ȋt rpgHX{ D&pN&lB ݃t0ld;~Ç??^\ypw{AP16P(fKn@w I `##o!7#Cci?[( M~K3P(F$݃`VK,"v@6PٿrK5`;o%q߃6olCY~c*aM;("03uP0 [ߧ|A_aUp l(EtJ A7 )Z(~ٍzz `mD"Uk+ D=@Tpk01Lj,6^gm>; %9&1i'# Ô#Ka0tP4, . c]=)a QG{0pBjTA@VR75܆"i6=O6P€1Zws'@@15𿵳=eaGIaͺmxX~2*۶! c= Cy6 E?dF3 PC1CpÍ خpX8=(2 {Xw,mWjp̑,߶0Æuydۻż%o^Ѯ"g+y~o!Djñ` - K` pK@}OB0P~s;9D#%XEnmߡHh?MAAAmh E?Y&?P`Gws#Cr/"@͚`?,w)O%ñ b;904]0~Jn*~JO#,ڦKc6OY t'Ohr}u7ѯc$_Kq`#ޘN"kljMG/+3 9=5-MuwrB|ݜ.X;]5VuRUQPӷYpɪ·]~/ߴ!+ ԂcE˯]!ʂ`k=߸4|zCJkK+??XH0蚹u輡-S]Jkrh)zjICS1TZ<86X|Y@ s9hrb Ǜpe"M~|2EGԭҲ`?!fr2:om}rIcIϟ%v1f{Tb%\)>99ZW^o$C+Mq4s~%{pؓB+/^ϙ ϖ~:)Cc 車~沋EGGv]-\T{3nl-Mw2RcS/}Q{/cui!^O_Md>jᘎ b_u=)J`a F+&cj]\VYs_O@SHKu#{k3C ebH/&Cu`~fpR̋+ocmDigk>*&K9S)!v"}(GN` ;| ZI Sw]VEi { 1$,dio;t蓚_s՝s nef&DB}<\r[+Z;y@# + cqMO9Mk~j)Y28,3_K-L|0U%&!"Klg:r$*aY42^a*aXR{2-g7~o9vaƢWh>h.;(!"q\:} O{#uaŁ _>GH/">G 3₼].PQ#;#"mj?,!={% =OM13=-9!&<[+#aɦ|GL9M-@862"/1nG|[)͊kPNN'u)э/Onmj9U^Pl=Ut9>:WD)'NI<'*NQ= ^am(AiʓǪ)2yc+) 1T/(M z{y^ F%LNMM^x^,k!쑉5,L'ϙw\Xzń=;Qj|":2b^Myʱ9=31g/~Qպlb'IVsdm)'EFv/E-:%RO#kTq/ۮjϼ֔u킺Ya.z13HV󫗍MUŷFbFJK8,EJtS@d峞y)0 j[$=-,epYX^]O' feތ n)X& qz{ٱ]Յiᾗ̄%Q,0h8e=kUɭ(gS5c]s`͓ˆByϲS 7t,gLoݹwAuAZ+r22Ӓ"]lu54]J۟ ? V8rZ>>9RlOqi"\mtOMmfq7H8.Te,~%[Q򦢨sw߲ \`&&\5[mq ^;fw`H$u˥j>G lҳ~9Z_k$-.""AI~R\rXn}돃wSý G{oJWВr^g=ec)ͅdW0䯪Lx OϬ`a-yy-i`"ኑH7x5m|^]lm ~}\=bHh4n)5 +=l ""RvAqk5vwTzed$D߀\dkinji]nzI2;ƊG$i!Xy,Tǀ+WF_!$LLdxZd$xZ6m› w/o=$T6_:3%>k̄^40!$A$gEvrJ+'?\q?x^ sQ^4#^ƃ}ei> )_7$ߎ 3zz"*#[kn1ˆM@rɁ) 0e<9h}BQ&Hv 2&5ZUqVD\nԕeƇx;_W;ExKɭk|`xYo{SuV% |qiYxŔ\o$6uv[u?5‚[)QPowg[ #]u%Y)U,|qS)~bбp>ik[`g"/#-'+ov-ƚҼ-+|Pʳ_6=D]ܛҒoLۣ32W?H$#*_3OBJ1HqszrZȾ"!rM#J_SNK}p,DG(=)1~KQ#.'vΐ;g>'Xtu_А@_VEG߶w7xZ( st Lr$N5t? ۄDuAZ\% =vtYxeH).W+)oe$U8\O_s2g1 I^ g[_xZw!. d'Xj8@@".M} ctp| $#-ysbbI>bhJ+[Do' 9d%܀z\4PX9KBQV)N\yx0#&@SYA^QY+;ݫ~A_Q Kv%W V|{+ ThX`K0`)DIE=0ڐafIPSfVoCheǥn W3ОҾ]68:y;PAOaRKCUQzL PO[S]CõYu}gJteX+8uZpo*r|H6~ I1QQV=M;B?Xgo+cfa!lO})qiUC[7+2ىP[STSܽ"eM__+Xqu<+:Cܚ ]#ӽeW.&8DN%y^3$^߻_8ǰ_ 7Քf's7?$'E q%}}ʻw 3#>W/;]lai'+%kjTcO ?M598!tqs ͜Gg  !3wB|7ᆼTDLyz LXü݅瞙*&?1[s^4S/dC%Yz|Cu >Rk vޘ)7AS藡"^]'~O\7;Y܆sؤtv6Tlk6F 2D6 8e/+o{Bo)+).LM \ws15RU>'677Oy&N@(2. ѣ[uw3VWSWs ueÃoE8+r3u5K*7l2v\m/a%Ĺ2{'P%5^O;814 HpH:HHͺjpguH빫lS7nB SC2DNJ?<62UtHQ\0g;[P3sKs*چ茒{-mt)|\H_`>C?QyzOI\:؛K:Ҝ1;DMuIX]3ޢ Y) ^qt!Pi34 $>p s|VGl.,j HKAuYCS?ld`\GhUvnxIoW[SMYNr񂡶ʁCT\" zViEUM ߷o3Sz(?;=)6l+3v~HQVRr:)Q gI()q;rknFzZZ.a9hzXw5W9W_QJtbP[l sZy񖏱(#1 &;?E&3b|sL|XS!ЖHAyuUvjlZ9M A9UEqP |T3>P+歯b=+& @cpۥfrfqe˓jdX 5 Q/Q/0U)[Z L?ONG#G9ԯe6%_9k}kcXs]P?q}dF"/,\۷.Ycd8~򌤶'? t7oI+[+*JCLsW\@!4^kw*}?mzwJK r3S#=/[+IIZK|eDc XDćԖo>V&::BJ-/_5w֖zZkIf,| {/ױ;|G~({xNQIRT}qfJ#R֜h@1?%~1 kj"J -tL"Mwih`, 1ڑOZ+҃]$Ը[|(VTXp 䆏W=A᱉q 7o&Eۛh-~#M|<\O۪nj spJd vǧwX_.Y8  b&Tttn9Z_;/.pc7:QR2J)^OU9IL jo&|]km`s^ڮwUϿo""\\bczS@FGL^+?;:[הFzmRsqQѥ'%5Dct޽2??mJ[P1he }N6 ?)ߢg~,c=BM $x,@MI GEsR80*I13<)T5ؗoSg2!!޾or^ܰSzzE3A#R܌ԤX%}x\+[9DAMq''1H$;EW /Zg%x:Y)l~a!Sr|Җiuk[3o'hD++J sn%݀_17-f>-ɮx9W_Vl4UƄ@ofx^Ҏ_?/Ib*yv#=5Q5uy\eks_^>*ug&?[*q8SBq(|Qo: aRB&y O^a"ĞEd7GtU:ɋ_}|Ӓt՚i9̨Kh0riXc J+kV;ܯ)LK)I Qa0+5x=*'RYWP5:dxXI Dq#?;oM-;,f!=9Y 6Jv݋5nܧ/Hsv/0[]4quE~Z2b겖957+xT I7[׽U>{SKk˃_wK#mUEiIqu\"f1AnUЂ mO{ _O@Sym@EĿ݃0( AUCT8} m : `f!-mTܮ] ໵0yPV[Bd 0{P d0d?@v]~ =~svpm#GTAՁ#=@`0tȆ"BvwΆ 1 M#@@4a"U{om{pvx C @deC @H ]1X,Bxm6`@*Qe!]ae >PuO0w wA]w] )Z0CwC9g@vbuDŽ`y((0bq8??IEE='rA ?.aÒcÑؽ#H Ќ*AQt2gEAPTҌ6fJseT Bp(Bt5D\ (UPހS+H#5QD?@=@r fb;[hQA:K@ %3F2 zaT"Bvy8 yq:\(bwn"I(`](7ZOFWO??F%QW? 'C dw ]Z rZR^ :1m"h:=;GCb ț@#UqwW2@'އ ҘPzf0rhFt 4`FPԍ; P@ {;+`sP(@Bd(` io j9(`G vTfK{Bg!( G  fV(AoK!Ҁ Et/} -wF3 u*G}!ŠlOrf:(ps `P&*c4PPD @TQ^G? (uG @D>>(B Z761  P@@c{Gy"HQʋTc_/ 8e a(F{U; p?{OԐB편~\spNhAօ!]@.PڹIl08f^&` (B~gC%vsE- 2#G+ _m9Z ,Uw~ 3 }C (*·WE| A:sndo$@`k 棚R|lA@R| W&νD#Qho,a aPB/?C) k2w(=4 58S A6,B(P4 vr7/F:F^@\t ۦ4>/h@G޵  Mw/\p߆a -Q-! e  l:hgOa0`]`J0({0F<kB@:P蟊Eo"9 *詝/3ݓCvr@(Zy=9 pDس! xI;Qt`jXHY0tg c yP{FT1`G>k" ;H~6K P d;2 YA~ 8cIt ] g2Jĥ{Dq3%Bnc???e۞-{F ja fC91vX+cF=@ " RqJ@ tWOُ* |l+C{ARD n } J gЁEx 2=m '(A*x\uXTTABN.ABzf()n;%$DE1n& 络Xk}gϰ n6丈`G]uEiqa~^_9MF--eX^<<>Ą@ 4U0Fvy\b,p`:6!R̨omk;=q }enO/ ҕf%?uӗ+AYGO7 C5 .J> e ϧ;R]/82,q>9';问H}c&gG_ ,Ӫ' $8q!XCPV7<1Ƹ$55#vbbB\txH-S}-u5M=Аu}=~b4ѐJ y`t@x*?ի{WU h}绉#>> jyEt&o73]剝r;ű72Q#i e=<2hUuGx}JܷuRnkPZP6G5|N&+IKH+iڹ߽[ )996&*<47AAÞʬH('u.z6֙'mFN'Q+S)tӿEء"?.r'T̸ŜT=xsN;ö'?)U?*_yXsv ?Vsߦ ].w 5%Ò@fz*J⴬0ؼښʲĈ@ ;Mv撀gRɖɧ ϞKZKwzmGmEYYqQ^VzRLX/o\89,~r6?#12]*PQRop[x<1x2|@^B|FyROG챩2zFlՅYɆyߋ,kyA}w\~hλ|[Uhw}_8V߭vz]!?qde{פiK{6Áճ'-x'rjxWO2=pYQ;;,%z_z~tkV_>,pc"";^>yƢOAzkG:7SWVTe҆xEfvWUUƅt2R^oPs蹉,۟M/xq h;jk%9qN6I 1=zCT9 a Si aKT <] ߎ2=9XpN[RI}++-:jqṊl{ԔGNxOG@#O#ꂬ{f]yUa?oH PbLt&㘞Ѿ 'yaNFw{/ޯ{ОĈͭN.w ^>>>0PWKCC[+$lى {u򅎪㪏2&͌e:#㊚XɎ;"W k59õ gV[&~uui_e  伺Q5фN씌O p5QSWdPKl(,-+/+Jus0B9p$NQ/&.<< k7 )\u0V8s2je#'лI/\!x?c˱ kOg&;k"=͕.'81 7=P  $Q:tjumcs3-zWN[}?-GK~V#:1|]'wϰ|`e-fJ2 j6qGE10k-!.fF}i!5#KT8;Z[ZZXZYZZݶ42T PUQVV3TÚD /?'ԄOЛhŃfQWW6UT_e ;퀤X6r{<" INHN翡ODzYSPFRL:5s$궁Y-ӟ\4=7;31R/CC~DsfqD]A:BJs= 6b&!ecf/=_oϾk(xܽσb zG(H>o!3r:_?]kɾk$@EE}SNHFU{@W]N$ZG^WO50'-><鶵iЍLiiieM#+g߈ S]#߯~?;tgN' $8lǧdde= :Y[Dii)pqݔSзpfԷ4WdjSS'-FّiI> (h ,uC DYhxeng6u u4VPַr [-kh}}Ce1aa"u]#+{wqi%UE>Ng;k.YɗxBʥcD5?Ҙ纩u'&$C_"%̵\t=l.VV2e“PC k2u}HO+3~RigpS\TLRm[ܜHj)HK^DpGV;nwxrًׯWAAAw-;ZSIanVjbLhMI qIR*F^ UcФ1a0'g#M'~Wav|_A2b5w,8)lmPgWUn.Kq3㑋6ξ{4Rh,/+,b ׭А>PwWgk+gY8BދJHN t%:sm ޛSJWpI(Jv1[_;+ )Dg}Y+Wn*[E5UX* 2gfy(dolja*5NWAVQ]ž7qө֒?[-qN ɜv_rŜ>BFz2]TŅťh$lliKqVvVF2\aueN>KrM@GVpNkiMk; ^@Q(+)JK ܱ57VWySOJʆ޾л''C y⣸{¿)1=gK3#u16bϞoy}躢GOZ ,m4%8>}GWv)Mϟ^W8R'&ry%qG_?̍0W<\W]1_R/w39B6flP@_GCiZ?/iiɉ^nwy  `iΏϿȬT=:9spku~b+wb1/''.VŹjR\ v ku9T^Aӣ kzs*e J=|'/))mdsrHWMv+5*e*$z61O': CL5%EE/ZZzO.iLKDWCI,!9 ܢ Z.Q%痖_ ҁZwzmGhy"݅F]ME9YiiIJZ6Ms'߰y =E٥IbHI/'lr&#MnډZe<PnnKG;}7i`A&:cun p릖_X샴) pw 3?"<|M[EFcT\Jsl_ow[}YF4?Ml[G𓧳&lT$DDS_LWHs#y9~ep9!a YU=s{ GGzKSBWV6 j:&/+*03Zf,W|lLf+:236`cQ'&!e忩jlJ.k^us]N/@Z_pQ~NFhcf /+'%,m_] ] <,,d;2/1--giâDG}Td$$L}>zLm])*C~x|-PRH ӻu$eul"ZR}YVxLWQW^gzv|}OO+8T .}=wCMa^K=5y$i9TLQiyU,+.KKH [i(H_P zdaYV|8/c`RIs{v*|Ą'W\W l~= 8of35}UjZ]YxZj(+sCsIpen(tۯjg|CP\ܓoGS Y1nd JfA ݵEx;݂&yKXNOxR^Ecף˯FZYwzmGųRx@O'; c]MUeEy%a^qE=׀ԂƎ̴!fb~oϣlvE/];@B%Dlvr*+FS|Sck?1r{`a%=koOפ0sJkZy4vWx+3Qϯhso;{0 ʣ??~eޮ~?x`meA =_X\Jv~qiT{kKC}m^RRrJjI\\LtDjgeqS?$+.JKZWT! a`[IMj "3uP*79tbK;ڋ;d8Yor|(P_LN?MOkM~$=Næާ>o9W&cؿ831Vn$)y^N%0>gi) a@h_|25z6> %})MC3 K/k@AAw-vxmg-dEw󿜌i q2QWUVRTVT1I/iiZ?]Jaon*#v7kV/U;x2 g ӏ{JC\͔Ei().RNgR.^w%1P+3>b hP2|ja*\A[PJ%0)&JI )ѡYGO^ 9IU85O~~nHg=w3ᵽդ;Ɉ T0qE$f65<,| %55( ߹mmnlohjWҺ@W+mY|F ]jot>,k\8̐rɚrp||EFK2138fue&I;91FjQbOFJR#|Lԥឰs]V4qLȩl\?KI򶥱'͵W阹dԌo{FV5ohy]^@Q3\\-/"|\l̍tUUU4xe4LJ۲ۄ8-˕z3Z罤P~S@tu`{uN?uT5hJr\X]˩ѡC]׽:dU5?,M$z) H9EgW6 6:hS_$9u|0OYc+mhӟm_,fΉ<׻پ0mmUE%e ]ckwT>e'$$<ݜ?dnaec O*ihko˪̌ pԑT5ϫk-IY] (mLxtəԞEgpZG&.˹pg=I.kW1s%a4J,olkΎ4Sa37ل+3GrKcqaf|*'>JGA/ KPb^UsW@WZRlI.\ thih9nk9BŦ֬z ^@Q[\j--.07'[Kcmhh )h;y&V4f >]zHUVҳ7_Jf%;p8/2q q,=i(I Ҕer~oDZh sOܩ_\NA(cA1E}[x +$SOЫ ?}x2/?|9ˡۋe^?_MuDx8;Y|BTSlhXh+?_/ݭ[fnwc3 *[Z $9xm=in,ZjZ]C.В:{MܑO'p|rif1羣8'-W|S>c $??n\w@,b7,^oCq+(Tq1y2R*;@P`ۀmiNDi&*ٷ1,1(uHbϰ9v^P`5@ ,@ ?x"@?[ST:YpcoVaj2l\<=(W$q(v0 aˀq A(y"G 5FHQ@"0mnl?ݬAfF||ַ9=LYmc#8ԇ1)U0, @H{m;v, S(QM4"Aa4̋! C[!(r y}cni Q["+@'&-md`kH:l ,,l>^#[oo 9׷xq'&!jPdW̚_:EBAAA!-~?\wa L0G`HvcL/Ot P< chmpZȴ)dKAlC0t% =!i"2]PUQm/@CtCA?XZܠ2E Br{'ڂ8K]CJwP@P( pm: p+"(3@(:7u:+ +R{[@?@Aw b> l;@L>d/ x gb %"( H&lnbl v{ EĖo F"=:3-mCv=`A@@ܶ˃ @6 Bi" |7/2XVzm!a(`?(o-McH #88X =B>X]K0QچBH}Ǹ r1w+rbs#F7@91c(:-FwY1a|oi6C(*F}@0(S{{bP^aAm $(joO ]Sȍ5 n9 Gm P a* g8b#;C$L@W;gFR-Emv=@"aicaPDs{T0TU r[ [܆tK ?H*6)x}u\ݍʭ UJ+w/ܓ)Z:mRI& 2rܜ=guLfnB@hA^vVZr|tX,8ĕ]˪S233Sczrw??y}x"zIaרﱝn,M wP碿u-Ĝa )I1Nj܌T:j#򈴳٨;k-84-*[ztV+ғ_H|y#|k*wґ&qSD2?~ia:31*ncafL--m.nNPW[]MEU] Z2T\ZUSSUld##@xyZQ#OTe9)0jkjNH(Y~03{GJdO{ v1ǏgǺ*Mho^pꓧ_\pKlk/JOTa=514_%gv~޶鉡pwk]\h()n]]}6I]{PO'; c]M{twP3s ɨXE&շOV*CCCoXyl}4r"fg%PG]UQAN^^AKXZ#41 53+'+3%>2a&#rŒ,(ޤǼeb27@AMg4c}BSӓ}Yɮ TUT7J."nmj!x.# &% 29Du^nN/ z/@=/JOM vwr63RWQF*BfoTFq}FFN~A~nvJ|LKZē7uݿ_BACG2$#ZO^ϣT֮1nsR,Dio^9eH,ϙ /{~ Y/ؘ!\3;6?2)k`ANNNv???z GF I q[濾2x,cʚ r3=:Bho~]Kvufzj,-AO]PN32,/))/%w_VMPO/9:UU5 nNvV&q)ye-s{W̢ں8,睋Gwo}qw?_S޾f$rĮ" z{G{[*2#\9$UM~љHҵw4Udu%8ho\>wDYUЋ5BƈϿ.?!i?/Φh]5~? 4UrL5(n޸·=.0SK[L>~HgBt`kVgaafR1sH.j,߰ٺo$C%20}mMuߗQ5r ϩl-)(*)).Iq2Tf2<z񶤘0?OG; } %vV6vn~Qiխg +3+3#= djIq1^˧`:j**y m܂krJK3#LDRSMN=؛?ͦEqC =Rb%+|wpLrVQyuCC]e!R_:{m˃G?">cM&Wo<8INUzYkq1ݽ)gS__[Y)'+,!odxxy:!m`zRRҲ ʭ0 [W`hTlBRB|T+VO쑽;w_j}b(ەϋ?70ٖc-yKWM|POsYzBvIUMuQJ\W*k[mK>yٺeOF;,5~n;xZH+їƐ^W7V`{&3/%s$U7L5$dž{2RdgQց:e׷vեgdd9@CCoXAfۘ [hJr kۺ$vvUTVŅzz|Bhn[ԼxY6kG]zKkmQj}v:qUЌ֦T1!r…WN;w>yD[A'߿=oTE2*cC96Ro3Hg7W6Ք'ۣ~BNRR0;`oO7g' #U`mI~}{r?N _7mq:1F_e߯/D+>:M8>>2YWo-. fl[RQQ(R]X_1e_w丠 }~/͏wńx9Zt-v H'~}ہ gKd:hn xmϙכo8x;7/fKR"<L u4TEعx׆Y;yĦ6u䥥BCCoXm{l]Ͱ#5!&*423B\|B 0;Фº¬(W[c ~Q^zd%{xlMM2fry%鑞y :VtNT7vvu|zűg_ylOY0?m~A&z )u"ԕEZlL^b2M|"4H]NLW@EPuw`hDTtTdxhp Au`omj,ua(y&]̸ 7=EQK?6 *xȃ抬H7SvF:z&.a9-sG0SUq.Fz\*q9ߑ۾M hkAtA"=}}U)vFjl T7nl~aGYÛ^x/_NdGn25PSG_ZI F*puCcr*R5 !!7,Aњጰ45Za -Ghra}þʚښP/ =T)|t۟%<뗮3ǗJ3} x|b*f/:{K\QL#'v'w3 .xp}4U^|dY.N ӗ.Pj%-@yۡ e]>Uf&!O$DbSֳ@G*&%EGxy#Y!s`a(#.rԙi9s$7%ROA JA}[ $J1Qг woUXZi#'~ CK"DO={ЎҜHgVfyȊWgz:[kJ2b̵s3&yjd/&JHsF|>.&z8X#ʍ:R4DF&eV'%$%BCCoX-{l]ͨ,&:<nc5(k=CijꊢW[#u* +cs><|Q]o;z8?XUHJ_J!8aŗ=Hu4eYq޽Mv&9ѭ'ȸՁgc0u?|d>$y` $ㇶt2>oN"bZ!-캁ٗ gKrR%8^'-cfQRT`cn$+%.F{_rY|_MJCry8ظ]ڻzېbr5m5x.vĐNH~Ge]|POyv zͷΖP[#5)AvF[7<[D}^x+{ӏϯrVԝj|" n III Z^ [Wolnnn Bcnb.#$!zgXu}Cc}]uiA. '#-'f?l>|eIGK[;UbZ$?+[\iϣl,ˌԔaMyU" c('!˭o<r\3,B/5s,%{lA`җ^$ڙhRߡcdP6sIJȢ,/+.NQ  tA]m$''+'.$cX_R&93!@UZWXV/&"'NG6%mZwE>M5P`޶nI/ɹ2^vʂ(?g+CMUeUM]X5kJG-+N:b,wP}z8xt Ǽ͗6}e_rHW u!,OXlZnQEBBBbbbB< o dj5uHNVƆz8[hJqɫۺ'L<_lojjj,FVnB򢼬w(~=GKiuϾ]o]-,DyXDT,2F_J):1-ԘOl¨OXJIW1$"947u=ɜԔϦq"#vzW]e^ <4WN{[;/Q:xMè4f8[JIJ+ L-skkJRc5d0y$R,IKcDe%DŽohlrfNDtt Ң& !!7,Af+y98.p8 j--/߲#oQ;>GԦΖ`uW=6@GNq#L,:`%b1J~0l &;1_O0Pl> w^#9mN*ҟkU/*W'#AU,M5n}Sim3v5DLm pd{aqr_3?AJ Achǧ6ی7Gתs (!hqI 8W,#衭]\S°E>uY k#8A|$A!~ a \VtH1,O(6na =?NDLs/@(h o0E!8?x lJaJ2Y} tyuĄj- R51=cRG`6WPn 9c\_Cb@YVx7sA>7{̸p6#P1A*srBf hs]M9o4??olA9!o ghGLD;ZpTW@GC _788,lLh1g4l|5o`y#+%V"8 !Q6ao̼Lb<'9ZةKpm*!b-:4<[KhkjLpl+D[{pPQG:CEh85>;1SLWh X)O@^K/$ ZWwKv tv!w 0P^נ?l`0NBPM7/p\Al?Nkpf ؏~2ZlСb;^_K,U[[!q!$@HT(x}u\KvAuJ[hPh)5$4Dp!ww(^H]nҖ[o/u6P݄eOHٳsf9ϙ3e,7ZZZ[i) qQ@_Wa)ic(S/ёlگ,×~NX ˗,y!GO=}|`^fzR|$)48? =0O  @o7'#78[[YQX_UaoFȫ I]U)$_{c]Fz4~֊?%^ >{:F <^dߟ~S=UyԨkc= cbiѫS+FjGe?{}i/?V[H r t(c+GܲM9$/[#me8Ra)=k}m*H$ژ[rSHb^}﹛=~x0NMZꌳ:pN.>AXJVi}ޚu0-I"uEyÒR~.\+&tK>ɫ}R"͑JR_ɿt޿Ϻ{b B۶-;dwSe.%D -XK p4:͋/xf]_,m۷nɏ,$.o_}ퟯ Vmz|s=AX#=muUu ]$l"ߓMe9qx0Zw,sZvFYO< Xj܌`?w';1 fVf:hs;wBLrFNQyɠӭaaag,g{eZE8ts:"up=>0&&eW_˼%N>5XYmV=*/---h:6Aɥgo;.Kp :eyLe$emřs4 6u &+ۺ{{J6FH*U1M*;q80gO#ݲƸ8ziSҢzjr|4 V$8G''g,mѽbnaesLwܥK%ԄP_[ Cի Aん [N|͋kֆKf}z~{sJ!/cϞȵz[wI#Hx /iOL5(&54$`f|?,~q}QE46U7Te'E]l,MM L,MMm]¢ҳ *@g`aag,{eZŊU_FMN(h-')W|=5X;ԬҖȳpӧN2Jr ~(kcm5- 4 9 hmz`ycED%,K/3Ξf&B[RկZ(Qv(-奟>|@iCRŕd}`J|F*92j M}BR%t:3_i꺺TjsOX %}pخT ]cKNtw^EgNu6TГQy#G7 l=fRWDY=BcӲ,Fen LJzz;X[SS]MM]CK-;@/nhim=TSFi&'!*y&!Qke z_?#wwSBk[xR`툀\ŋx>#]mԷاq Ϭ;y郞CN w6UR]cG)KJ cA)k*L t5AjI|uM>wѺGĖ2wU XK#l,L0h:ANWg32h aaag,s{eZJI"ǎouwq1ڿGlJX1Z_ًV|8sjD{SMqNjUT_G H\Gf\MMgQQanz~Srg/6u6 , }(3o1۷ӳ u^|pxhD;[ՕThKGo|ljNIUCSsc]eq;%&3FH=Rcji\#Ő)i驔x}_:gG=;8" .~Dg畔UVVf$E[c4o޲Ex%~Î?xϳǷtQCP⛖/ϷW1[56^ϭ& ga-cG.h70XC pWܻJpKh|fQU]]MiMeaFbx1Rs?fixť Gd?Ã]+Ӓ@o___mhlkhOVOWi54ZH$x89X[߱mBIU>xwtҕO 33"C-Zʊǎ Iø2j>xR#z۷?jo9kewۧNR>ΈvHc!ì!vc EO u_UUU ahG*olnl+KOY PX "bSq1QD|HP@3 XC coxɩ-95:zxVA! +9$6yͣgO^?VJ +\d# E7^iey{ۛ kdBcNaƶֺ씈g W=*'-%-+ϿxcM5ys h"Gϛ=w?Nw>O< W<͌5eJ!8k cHcWX⼂Hq)95#`aag,=2S]S8c"!>X ;m#B`-1Χ$!nŲUv7)iӜu-aPmLH s."1='3) uՀ_R Vmݯv,ohk.N Y /^tmnSsIi5p[ީTrD(377F#AY%-#<0003t@L ?e_?+޲i;v/j]x~/3e'Ot4וSb\ 1z:jZ{!d ʠćz:Xb }^ EbO=yx\O]nbau3Gza{/?Yo0#GRBjP{=lG@AU^*3RS)X\ D"IȨ蘸X&cc# >.8[K};lݶSBVMڋW9|΃G޽d-mêE3n=÷ޮ`w[S`R1u J+klj(w6B+HJJ~}u&qy;2Dgk}y~F">ىu+\+,.-jb[w~.y=NXcwc.(~D6 k7^h"9K _z{?Eˮ|޴o<~};\]M q2߳aCJ^']uRV c֖hRj_{r33ߺܴts ADr<{9;`-L(`nAimʒF9"Ðy]]!)mo_mh)I{;Ytts F%(LY7>~[)v7^| D3àD֯($KXbnmOɓ}=me.B2{onaYem]CC}]t@L3JI&F}=X`Pr׮XfWV#r~5g޼]mM5eytJ,!=">C%啕UI!ݫJ 2K,Z-r@)R2pʕC]u¾mWXµk]2/c/](LUڼi29-+ݼyLjb )g:xTQY]['K ,mhljnnjl-b.22gk$%DC=\mVXK@xRVYcwmYyU@ݢ\:5`jh E]`B~@m}K{WwwGkCU!#%2P7t'ƤdU0zȿoŏ<"Zr[GbP6n"[L$Q z/}뫾m-uo, ]eUWoj_?c;Ȥiiy0oeY#jm_LS3oǑWc_ΞkR1(`/g[  DHl3@+**+IW/4[Y\V+2m勧{Ki~v*bV/[tE5)(؇]IJB+ܵu|aqYm3wBrnu[ߙKW_rTo[%%!P7uc.+ojnin~ Rb^.N8BHBFV~q)*Jӓ[=`M+mlmlom-K'37&mjN2ʁF&;?+>}e R[o-sk{[kEyyuOdzqse *(诩e!yU]c"̼FI`V _O9:I]""[2_1"q$"/kl LHdU}m@(y +ܟr'k * ɦ+AOnE@?2^ί~%w&u¸Z6V񏘪Ѱ /ay*sPCQgY,N,U(0?aOV;9b3'9~|Dp՟a xf axi\MC~Ɣ0 : 3[Gp? {0Z/<ɲҘ  UZ\-8geOi:Wϝq+@=  +ï\Ȁ 0aax4'P A ;8"5W:5TaT&ϠM&Y>bD!ƃk7j*IX{:E? Ɵ ?#-ASџxP:=UHn͢879+iA=ԓ/vMb1:/! gG1x3ټ.:9""k.JAAE@c0LgLi O:N;53@R 4pjĦ+SqxO]$;fyU8"gZc#1E7 Zp0fII?d_ZlB>r d;Zӹ=K~shq~,zCo'C7P{z9rt㯲 JQ(.,u-eOpUbOg0.\77cMDVWl{fxj?x`'!N.U!e&3X+Of%pFV q jl@^r\GXlaUu:jj3<&D._oj??$waA*~K!8\f. "WR'H|GM9D3g SPSRBc1DPwS9N4R KOg_0]F.@)E16!>RcdӀ= p*-!@AG=\'s5쟪ST'Rl7!#`sCL$.-?Y6AEAi%|xw&# x pT`=<^?-^#:huVCyXmGivCton<70oaa .?JJ&x]w\SWo߷m Qg]PDFa 0I{3VAqmuj}޾s$/> s=|b6?/7?7/ =vVfXTT}xٚOY-u9 o}om5ba^FBFts57bWJbF$seUZI +)"~ƢKҲ N(t ;ۖ͢,lYp/fz{?k s6x⧗?ܿ:QCǛiٴf%7[-޳#F v5U IAT5I2BQEFRrsrr4ݸpN(3NRDC$SCâҲr%ee¢śUXZUP[Qdmk`b` I*,/DŅo?=XMHmˬ_޾+ϜhFܞaXva}πnYs~>ر¬v kf݇t.>!QyQeM]cSSS2jSjrs2Ӓ#X * 6m7wʼ+R=LcM 312Np27Q8zG$K˫jk .!=!6hkj8ux퇍}#2K 7r3Â^6TUf̜?zÞiw>f?Υh +U^=sGtk/ h$G2(wWg''g7/ "6aqnD$((efffDèp6? IH͒N %462G$d4455UQ<,t4j`wiҢJ~nz<'dcjO>o~( -BJ7s֮޹>:x8-1:<$Rw+nݣa#[.}zпRMO'{[[c0]KV۶0'2XYBqumCssKKc??T@M/_yYɉ1lfcsUz5[~+?7?b'EO {=ɌDYc3@WQmvʶ޳U"aav 4psªO>1W9qߨ,nngGi޴vUnNJY83|POG}0'-. 3^>$ ʉܒ_7%%5%%%9)!>.: a2i@?&@P,ŢAaNZbd(,m)΅D O/Kk*J 39 ga#OXV][__+ bC}\K{ZG45Tn}+n910jSj  @zJbL;A%z9G`5v]aH'+Jr@:L%l-Ll\}YqY; ATo[a]<)ШČ\^ $M0Vҫ̹c˄jܾw塡 %4j7ov뎽N>eΏ;TR@g0htz@@@Ppp03 |ʬ ^9$"0)쨄^YuSG5Hb0A)H SK6V68';kF L$UHjkk*ErU9isr$j[._x?>b!_oyjmXXMdT˛@OHtZP[ t"'-!"덗1RYrvS;7"*:uFGGNڔ"WTLM,bocifl9.]W^t'?wiu,N6 4tqt' @;ikhoH S]ArjŚ{qiB(.qSbX Q_|5=^$^7ܾ}K:'yXܶy˶40f4vWP|w`ܹjAQ>$DpXЅ=߿ϏFD٥OH$e k[NtDNMb^n8[k++k[XW`LNYk$5E8@X_O1A{"+_7t~loG03: guo֫t%/\_uoCMe)?R,EX3"J\744J*D<H`2faj]fޢkݱ_(L)4vt;~X??~ ԦԞ| `KhbaΜy \MȮ:N5gtDwJW2;XYIћ|\_]&M3<.Н3;/ =-RB@6hmj9zTk+6 ݽx U"H"Awܶ}> ];7 3:ɾ3B~Q^H 4 "䓈$2hooggkkg_\^sXXH%ylw9kbJ[]eu9j Nt41=?Jemꆶv̢2Qё3=-a4wC m׭Y_A+ro<|_biDx{x> 6쭪TP]qwYCat,.R6v J.,mn:}X???nԦ~|WXHVC8kKcC}̢9* vCCVq%]=n߽wֵ x>FGGϕK-k>{owuv5Aƅpw#PXQEee%>l&uu֨ݮn@ 5_}QB \q66ڻ1va%dŀ'Z~Yc3_ ɇ@LJjcmeiinnfnniemÛؤ/'5 ]´157{/]cl~ϝ }.^0wAJq?捛=kGÏ^{㫷oڛk+D$鉃<= @Zr*p&w޾h̝hj`L$:;6-c='N9~o1jSjx@H#]l-ͱFz'ٴ6v_vJxyIQAZ<90*?T%E)@ *kk-WVV@ %@OGGWGL'.TWdJqZ%.*3*/4B5'U@,fPA!xyyJk5611{ kZ@hXtRz>?7;#%L@x{۽kמ}l\)/޸}L tصHe֬Y* WlUG\#JOKW/_m3cj;mnG]o|O': RbCT+H-p8$1LMeuDRU!B%Ex?=̒ TYy灣xZtNaAqe]sDZ'{OCGGLm@ePDŽ[-L'H|h٪;jd0c3#ݸqYst(51816O&e k:/~տ~h,Lf1|lmqNxO-8,&93_R^YU]Y.#<oo m5RxޟUU^%nfR;Џw ;:Mm\|CS%\`9ٙ)IlG'H5655??3,*!5 TZZz㥧_mރGLXIy枳߼yb#u߲vk~yo*mׯ_i./L ™ڻkN5u]3Gf~5SIEq7->240w1G *t3?+%>"RDy_/^j6C:XS+Mb՜:udO??_} Ԧ. b`lbݠGͱ+^tՆmjG ,}#S D/_z`1/?3wb(lҵLh3gmC^rk8PóZj+j;8utߩS'PQQ-zBmJm@lVYzJtMF: -]~so:'+t ]tia ㅶ=Y+vG>rW}T_]^\L#z:ڃō@ǥfD55ՕeX`@tYa ,}鹼r(KL7@qN.x?zphXTL|bRJjZjjrrbB\LtDvB"$,,-̭moB y<8h1ݯVV4 |ʥs}K166Yde_|A\p~3rν;/ lgD, 4;mlAHI.|O;MīD,'&,ȜA w(9WX\TNK젧?,Tfm{ui&JV7u;u` ԦԎ %%%@ @fZbl$'Gp^D۲[]+o:902z.wc9/gS}-jvܳ+w]}۹WWK<\e^$Z;: zJRWW}^H!7Jvu"d53u D$.yjJ+` `osppru*@P;<*6.>!)111!? 9ARd2HrA7q@9=}(g!lnNn^nNvDNڕTosV=s;Fk,OyL0W?jhεC{V \fH^E8^D -ɉ mLRV*(LdBNf.\l:(;#6Sg=?mc Ԧj%-`,4П Ml,̰@ 7-]nX;7J0Mm/ӓc8O'@n_}4{ q3]Ǘ?h*0 T P4[c2RR[_}^H /'-.dke346 q$C"Ҹ2L$= MX)$cA0YX qqё1=G-ŻA@2 g@Xa!CϞ;~xD{MIvl0gj>ۍ}PqǏݺ2t05>"cuuM=sGB@T:ջ[Zz*d%6ֶ`ʄ*Ɏ+-@O'-fV[/V]b|ψ˯zZ2  SwR?x֝SZoZw}cSssc1g7'2y/? toĘ0 *DƪKXa H1[vYi M ;PRcqd_Xcnwp_`" gɾYq`N;vRa (E AZwu7Ş87u6<9pDZ߱_a{7]e7+Ts( _?OڊڇoJY*H[}bpL"0wma ٗ\`1r!}ٯ8~,)ҟ}ly1D?CGGmc]<$O~-{U*a+*dŶh=Q'OJeb{Y K$Ww B6JF Dyiz$㿌,yt߹E`Ar?xPNʽ~K Ԝl`c5~B d\*8Hi5EAgZ1J s&ex'v$?]Pa.qztWFxLXn<ΆICT>,؝qpYm%aJN7 rȒQ1G_ߐ.(ʡ0?a qjgre]Nk -ĉ_Y3'xSLh k*GqBPH1V9,%]$w//?(r4?m_Q2&? R9PSV8ܔPGF @@g?݄3Nb,?"+ࠂrP~Hm .t;xZA]9 nT??J;vn} ?q\Zۿ>?ӇMZ$73%F;CfͽWn  v57Di*2IKHLc5&fm4 *LM'PHDB'$%$y"I\d\x\`p|\X%D*qX\%~afŝ=Þ>'Cc)lUkk[﹩nݼ>;=ӬpiQAvnްa_XNdG}tbVpKaP}CJkt֕aX}-nY%LPDq$ -)5+U_ϟoookt5* ʠc#s v O,7vM M_zܭqqq,np[UlKfcS}m\,sb!:""<<6gch%Hushҵ[z|Mjᲊs=ھm?- ^>G TJ~iavZ2Q_S|mp&3s褘xjRF+tsKYV YNaP)$` Md数 ZQrT )^YYQQ^Q! bTZ'fee8tXHRbv "5zG'g^~}vfb\+dӣOuߵu[>7?1jj+,Itj5t8)9+YzǦ:zz-\+Lbcg%*]rvllЪENI^f [ڼmC>w,MN\5{~Y??ş njc60{oj"<1H@z%PӋZSū7/kT".N;s[7oڸy }?^sk^[ҺG?ӏ?|qF^2N=z,LTBZ w@X:McA?$2:DKs"Ԡ75[ZZƺZe[Z;- @& 'FRr Y\ Z}NhjjTJD"Je2L.$7Ja_bd$2xxx?$&!5U!m}#f\rizߠTpyپyӦ{<N j}wz;MZ<~NF2JM%&%Fkb鴍k"AiA{sCy01>QN%вJ+զ>x[ݶ&^In*K"đC lveMwln47 M@dUPғiT (? 1zZv!+Pn2M&d2  {J2B*Dr]RĠ>t&2YX&Rԙǧgf/_8TK^rԑ};^pJ6c{_=;4fTtZBUPȮ+kޮsVScJ*Ȇ|&D^X8x 9)W)gt$Et 1gۖ[ؽ{P\F?0>wOнGCiB]&g\5\O??*?ݽzsiTU|H ń{gj^H;2uy{ H*y,{&%boz^{͍[_HD<~~S#ݖ:9RI ;[&Zzlw䋯 tk9EX!BUH掁щ oȥR@ԔDF@``Ą"\$]gWWgWgGݠj:_ F _ 7;#-%9 'jModl #굪'+oR`o.V*V"O]/o th! NJ-153d5:;:m-FB <-1!>?4$44,2zaߠxzF!Z VdScZ.d$)$7 mAgBOĦeީk=Ccӗ; S-l5np[Ukr} #TU%}jB%z$'Am֨52*p ֽ*]->\iۏܹkZ$biGx 'UԵ}˯n_7 b]H ƺ;l,=\3zONMOOښujE)-A{*%38 #5+.Vet:]Nk_'…Ԕd`Ш2#-/R ֮>#EG?/+A!ń;FJ-.WǮ,ᑡSZVɣdCɔzRzv[.zsk[GGWG_))/+MOcCBBΆ۹(RrW6 @ϭ9.WUpK rғ Qrs{(ji,ݚ[j_np[U+~k2 Z@$Q* @ll\|OHbR. ޖaJCgLZXEyYbLԩ^}W^{s?; m}߿ojD[&1T}= 0Kʫk'&'F:[u21"NiI6,D&ʐJhڻ{uw5`}2S&\6?/';;+++;;7?+#Bh0R$?+T'Oe 6jx N)ylZm04p3F*3 `"z68w;rt$Kbe^Ӫ`/dq}pc(1lm M=sfmtYr+ܘ:l64-jZjO$COG'f1jjJ]fwH1Q!G^˯V«}ԥk7n޸zibҠ(I :q`=yg⋔~?r_~'=|g~v^]m.xЁ=} FKGv\8 DѨTzbrjFv^!+Ek0ZmZN@*ZZuB疱KK r󋊙2>jZSWߨ?\w$TK!Z\ֵ,ΞkqKy1U51-+EbZ[gyB8G  PGCceB 5j뾲oOKfyv࠻ױJm]SsG,ۀwڡ'YVmU닳o߶xV4 GjQ)$"H"QbD'f 5 i*; Jg a!g_zu7{LtHL0;2uʵkW.MX5Ue=u۹kɬ /~~wܜ9c }<j}dX#ؾ=8r~*agHV %i)))i9y,TѻlimmMjW]V)Qea2K JZ~dM _ p9@)T))DГrrNowvt*'7=J_SВsJdniim1ԲrN](aqyi;?Jr. ֎Ei/CP?q!O_SJrV1Rx wžy=??d V&/\zsna}iђU S4*L" "WIep~}pjuq*W_k\FwnjfԹv̤ń=m玝nf*Ľ~q~{o_0iֈSȑ^G{x?HH@nc@l¸¼8|WP,% 56 07ౠdPL*WUV yef RGb@t 7.g$PHdHȆzRf_jBt+\#IOb ф8R#%3#fҼx)\d>^^Ʀ{=|ż*Ut-,E%Ja<|q1=wܙ;qqq,np[UStZ;j+$:\ NB~\)ʕj ۏ%CS`bl4~}GNGS O ;?u.KRM&7lc=|@ Ծjxpl6_N%xڳk֭^~pdf/~//ݽ;n_46`Tq R{iFPk[l@.XdRy\]\XX\B/axrJ"drJ$W _ᕕq fAa_Z>ed6 M 4A)EY#G4zRjfnQ)`:Nc@Q#촤r!::**&@O-.K M]l6DTɌŽ?ֽ:AgN uz;zG΍ vXbuE4?_oOO/AԔbn7<6}.,<9=_ njӰ0w{f[z@HMbM'U ^ a4khM\#pJax ᡡ:^R=^8=qhcfG}-w÷/nȨ}{}gڅ}֦QY~-6<8qCtx=0:6uafⅩsC}]x.nᕁxDlvy)ᔱ9@HyrArKpOE>$)  6m}dNzxLdq?NOLN+bqb٬͆#sғh$<" SIldҢoҩY.$wGΝ;nd֬SDӒ>>GAiyL~Bop[ 3f+888kj7@ ]ؠUwqY9:@jB12p,@%WYyi\aw8v*ÂvO]<ר(pL^f [l޲clwD:8xo{o]yezڨ*+HG 8q*8@+74>y%c#KRTK@(׮, d[J jߜl+ L6RF7Y;zZFg~H("؟W jZp8JGl?Q?H ?C(Hjm=CF]Y@Z7(Hɍׯ߸ءE妁93:6>11~~tf1kdb0/3=9v` CI,`!UtNޚ {o_nke/ z/>D#_-ٿO?^gst4\x-y8wzB^/_Oı|` ]pSǧ?uCcO^88xk+QV$dubYA:=5qI5!%b{41G]R%go=-sx~ #tϽ"5 >>q{> |t 6>拿x-~ [蔱4 X";ٙPv2dV6SfJY+vhÖ#1?O-TW?J? _A3Mw"p]Iݲ,|%Tl^~ ;D:ZPw.`9~L>p)eE|l4_wA&l|(ˊo%g]мZ$p qz.E4v~eﷂY'rON"J0]:L/#Os4s?m8U9'pUV]wpS2. 萴 !NNt`eyY{ 2B`B.rw*Lw8?6;0?ȁ' &ˎ`]=+IjDɥF!W kŬa)+w`9r Q coo׶k8<&jbOk|.T3C]1Ę:_McoV(ws5$P.hA" kme:.!A**-W8W`)]Ll4P=TD~vY*0xGz9I R?:J]Vbs[P.@G!+;%88N^x"f/W4t~RV?^c<\!ZPÖ%e~0?as|Ŋn|;`E/ :G?pq='66c.ګҊG-3~bKzjm6 @_ݞ]R(f wųcS.ǤKuX{  pmlt]_Vg%CU?GaQa{ǜ]Gv4.sWS>&Ǻ; Gix8]_mhjy/W4;> 2]nFet\8 WTnuu%SWtQx-]SfecU'?6JVL'&`]., "r"|G[Ώ,1'hą+]|#O`%/y.wi8w <㶖*+ҝ`sdhy'g\T ?t1a|\%[ؓz448mjbV^RWPtVjnJcq{'fuuuBCzZF!%E{oޙO]?Z&Mi]y^YZ~x䴛9#֍o%ݽS\5zO[S}}=]^SœH;Q۟YXY:?ⲣjkԲ*!dAԜ{iY-5:࿭ wt)qF-dPʊ rsr ]KN+cIj̎QPɍ+RF\xٗ?gKӏQJ sݿ?5-# @37x..WVË@r4حH\.dR0x b.aXL&dJ ;{O-vwu¥4uh\de0l._(6w[5 E@_x^f1]25:GYV;YlFތԳy|ɹ/\NTYZ ᥅ѡ߬;I7oL +)u20п?hZ9Ap[{O>mKwoj߬J:FR理q-f;^==@ bШB蹔‚|JJjv ]}O:κ"w}ǟ^~7&Pmg[FA 5iYet:wkgD06G"N[S$˪011t JFWIP޿?9;8"p}^WC]-G.`q(,y#PNVQ%`#YHoVheZŠQV 9̒+>>𓳟_zyn^[X_,Nw#PY[nݾ]PJTz)38:GP_%''?ui'kuOP?2UJ)tp64*νJuCvuBe*J K(TFN!+Z\݃$͞F4(E}~p)Y aX{|.3=--=#+"hs{F& ~v5۬fA|X,`"01Q*ht& Zc0]Mm=CSs OU8]?++/\7|vkbGե񡾮&Ո%$'ݾW\Ȫ6#4465zYwWg{$$$:' ˫hm:FZ$qXPnD&Hdjmzq 4l*$(+-W$UjZlxC%.yy٥kw)mYZ{qowZ4B };6+;I:|M|pO >/zaaA~>Q RU*nPkot5vtOuANWLJQ7[V? z:Z#hXp7S2|9fo^Zڝ>0hRP9l, eXm lj&L@H?/"|Za?pLzU2G!bb1aj*P[PRQh|vy}kwͥEМ(\!A-+A.\YHC7<yrtY9_|ҥ+W~?:{#. ->w&6q6ڭf^Q&nͻ}`}4[$ךܭޡqD$` ??g'=vrx}ckk{kZo#/7…2sBY]]]޾>|9 '|^xZL .=ZQF,@"W댖:oG !h衻]b"⢂{WSK*w<FAFOw)"j%|RP$bx՜Vz~ ?x&#[SRdHhx|PjD*TZ3xBtg4m%1j  7'+3++' U՘=8>3DDD>杻x%)%ʑV׻'s˫kىSC-NKIq׮[ɵSsΏtmkg͵&n1t5Di]t+zOkg0yu1 ?l@OOjI@(Xoy`HYTINg0\Dn|721^@NPwdL0}Ô *e*4nn !ni(`I+c;hz9=]hې14I& hE*R1v9]m _BFI1n%eWkZ4讨Q |yJiQ~NvVVVvN~qŗ 6Wkbb;"~ }zg/]M# Nl<鿱0=6w0E)߾u7W W;O=togsu1քi5j%ZTUunJZFNQ9+Qjk=m}K-P_&.??H;Yv0H6WG.Yo4:MT:Z֏'@F)4ZJ\Ckj-h?'!Hh)ʼB ?ᷬ-M&A[Kĸs|H 0cfkhhtnZFTeUUbH^h/( RZ45~𓑇m Cey~|?lΖSkh]cj=>*R^ps_zȠ)lk_=]Z]X]?h¬6{׾E\Mcplş|xcHEIZ 2r( Dmu&{{@-$''?C> ܌b'0+?Xc~F{cU zmtoС]6Z!,F7rѴ PDZ]H JZEyiiIIIYy#sk{÷x (Fo|j6<} XR!I VYdqh:mjjnnjhZĮS[[(6؜ DKh_*Zr5 mܾy҅_tz&Mf ^X/L M)QZ|'.g*mC 6ŋϟmE$ Mb1c:M5r#ͪR*yU* ?5Ӆ? =$7ğğyi'j$`+C8M. D̲ t|u\3Mξ቙4ݠ uB;*#bq@8jZğu]@8KIp4*_C_[;dj-F[pq B`6[6fV+f$@Ïd ȫ7m/ ~ht:]n4l_T0##BY п#4<6=1 d -Z)V|K_\yw6B!^Xnu1Crz;.뷳iRSsjg/^|(b+>g5^jt =D*ķԘ O1;ğğ~i'j㉩pxeeqߋO 6AVTPhwT1X&׷Г ڢft P ` $2U f68h0"-.,ТᲲr*U)kТam6v=+ 1|.eL`1(BP0G?VKRk2 zNS`.2V}0{X0VЈ6kkMF(E|6b~NVYy@G$N_G#jiyeyqnj|Ço߼vK/_ٛtx+@xipoaE̴{wKM+ Xwp=/= kq;3p­J&/2hݯ !jkinZNz"Dؓ9s8 ˮVybZg;G91{S@5{5 \*@N@$UVÍmBD!@5 P4B} QW= d1Y!PkF jkH0FV)UJM No V{]ᬳXe55iC0̌гr K(\BcG>6>5;8739(ԁsY,JtWHz;ov+!%]853H\JJJjZfn}G-EՃoͿ 'bɁ)K,`u6?9(Nt&Ztw=_XZZ^Z\kfN#)+94x x4 M}1rJ`̶n䰗KzeU. PЮ!W Eiji ;mHj%T"p9x1#1@P` .PWgC3A@0=|&3pbiCp߂hG/0Air J+\Bks6FLNN v77Zt 佗|+WL=PXKOw?_Y fu6J̥dB9i9 Pn¿0`ЈxD,LN6#>˙JOOj'n6 n?䗮F%4 MQ &W$UiMx72>^bl`` -1|.r/JdRhO˃EC}HE6г"W-m-蛽"V@xl6Le%9RakZ|c=oI#%Rgd/thlz1vםf-?MEbD"*$= S8hw9 HIIO- NԪ pяIϘ3`/߰? 0TWoQU0K8Ұc~K8s5Ƨ^G5Qg^a9ƄK 1 56|$,ykN)18v,x)w,&AGQ/cœ'>Nqj}d:OjIwx%K*y̵Rƞ#7De$Oc:Ho"0FKQ6E꼤1>!KQRjjaZ˶qE\txpT^WϘLP7KIx8 1%A ^m'Tj='mꗽ"P?bۛ @ 2 Ñ+z |HH@ܣ/̄rXJE& K#,W'" VL4$MMOğOū }6! ΎI!&Xԅ #pϕD]_cL˜닑DkJ-8'T%R*Wk?9nSvFulHˣ~%8K tڱgO,[*e=̇9b<1?lHx+1Q,\?OԱ(7qϋ}eXLDǟ9OG_&NAiN Zx?GOZĖ:Oԯz? #9cLP;2q ƘDubLjLFố5XZG?*ˡ2HeoJ ?AUI g o;-j?Nbh1 Cz  &j[q *G6ec0.A QS '?<$'\|_t0ɘ okZBaQvTqGpx/>&GLşx:)O9H b Nu$鶗Kر Rŕ"e12DHI<+zE(o5_16ױCOOO}M'mo2Gwxq55.oʌ?Q+:x2BB }I0>_L)?^Gbщ^$|Q$ -ßO% $(#&x$o7;%(omj틤rRygw`OR̝xpέ_n}Ͽeɍo_O]~ݻ.TVsqE7'`wu<~q{w[$[=D~m_VWKL* nlԩUr)4T<;+Kd2Rb҅F*fsl&J$؟?ck#*K/oVONNOpзtR!"'ƞODs* +%ߗl* M B1;©t4 ŕe$|.| *8쩩?+ktfýED"Lcp]p"hK6bq;zGǧ9ZgNH$'x,EgċH^^q zFI%"`m?x(f+neoog\*!A%Mu/~ŭ;wt|񋻽,3~?]zW޾yyqz_8r']w &/.^|UZCN NL!E@9\`bT: OP52gwbZ?FZy{ӓ wLV YS'Slެ\mCJJ[/߼|ù:x@:+p<wdfWʹ4@7Υ pըAAaC^_B`7F$Vbw O#(Hpc( Oҙ B0IS0>~!]_l VUB!=l6R𿳳6V̛|Wo}ŗ}3߼:2<4880&@7-ow}mDdi#fy.opl %k,c@X ԙ25blDNl4G1ePi9ށщR7YL._*Te)` X@[vl4IX䣇A}#,X!gk[[ۛ"#]}[m}#S| % 탧Cų37ɸʥbx7\Z۪_z qf& Yjr/\h NrՍl2IXA3gcbZBxx|||tXٌ_jDLO psJ다'BWG⑐|YAR9;{3 3/\~ri51c3d72:}h,# @D3! pdlJd*ld0Jgd0?T( `8T fQUfΎށ'Sr?/z o߼{2|$\cu?) >{R\^;]͓ԬH J ƳKk[`{* cou?V1lqãÃJ9F3QNHtp~%SX=x۷o޼|2DE7s;3rՒYd d`zJS@S3'識j.ӱ(XH|DWWwOo?4/CFߺH" +k%"kkp @ ǹ?+7ͩty+BTOFZ! cmޑIXmrwN7)S

$'\[-oV._zՋSrv24Y3|`_%]޼\Md9@fZ.jg !:r~y^@S.݂_5 G9|D,D:/.76K,DQ ߷fA?2 @- pb`qzD:_X)6R1`$-&D粻s'8J?)7o߽-j"!L: oS$ӚH2Z bP_: O;lN$a  'MJd[KT*_< TcX5?^CqLnldvmF) ,Ez?)PZ}V/.?=I) ӽށ P6;|/q 6 8mH&vf&;D4Fy٨Ӫ D42244<<xvU"㇘=>Mx4gYX^ׯ`6@ï=x7<HTB\aeu [Z[)Fn<FL)<\

ة?9?=>l/@ N%jgG^e{B h_x5J 9 H5i{܅`*͵rK˫ku+r`g`N-pvS,:"k2\X c@Bqeᵶ0gxP_Bq+RT;jH2Js/~U Ƀ 57;@3a386##s|x'1 c Y0$9\kv,Pr|.WmvuxVxd0 !N:12T coqFWB ,-+*J<Gn6h2\;9624 08va8OaZO^^mXjPJǏ;:{G&q @g/^|qM'ɸfF E|{꛻w{g`|av1P9<n9Vɨ32~kB`՛#y do(Db [[ @!Q(JZiy.?P?4ܑLọ̈̄GihpՕ KFKmd/qHvB7Z vqߑLDq/4Y3' NgDSp"D:e؟~1r8;{ߑW5tuvvtttu |y}pROp?𿺔9>>X+d!ìUyޮ dLX=Kgϟ_\?=$#A?hK0avt π4ODI$xpB&1;dN*+TRf=^_K6d>?|~Ali2t:@VpzҮI'EIoQKM}b0^yq*Un"'ө$_KPR8]@4K46D2J3g@\L{hZ)mln,,3s"l])G< H|pq\x_&".^5'`O==E@8eOϞ]Ãr #fk0=l-7;{p0_ЙjcryNna2 \/BK q`;&"Dw7G`Ѝ:N D`0$? GpBO-f#q$pxD ~!ۥ"7/!ñޭ.EGpdR1TI"$P_Rʕ:,zݞSF_u؟?cku?^LWmwb!90j=5>Jx$782>0!TZⅭ+@ÃFKFnQ#LJ x?n`N]jX=8zzv~pG d?Kwf|d~\ +Sm7,U&H\Z[-,UaiWJ!JN,,`0 BnXv9pvb1tB^7FR|p! F$ב[+>+*Ę\ C=Hl_*7Wu%R\^N,w1v'qI$(KeB %b/K$R؟?clFn݋hL*w X>xbtx,yz]ãC]3bݟ\٩V6 JegOyN!& rtw>=:*-EY+fGphb(ge!Vћ[[Jq) s0X*Vg0l}w:votzpAY]/d1}ȪL'Y<|p,&G=qƓAIgE|~ |>rq/ෟ&dNunN_?9T%. -,!4Ŀs~?c7_k O77 %4VLyJSM75 V򣱜W=ֺw?X?VSw~֛i?֝QOoaؿDr~|ӗ O<}^bs?6 z=i)=)©?7nu}77(R{6@{?cjif:U}7^<.lVכ{'jnPX8-} ,sNcOChi>?c0؟ZOfRon#}ǧs?Rtni[hT{R)%ӕHgo^Jѡmyݾլ؟*[~g7[4  ͔hmё[7TDD4lm:O]qxT_4OZZiiT&cZgߺt~Rg7[4TRhStgnlRijy:EeBoth.B# leZi∎_]0c0oVQڟylѾЄ ZcЗ;iZZee4oD+ՏnQmU@qo$+k-\C5Co(Q*A۟ylyE#oU}j$>]h:5V*zZڢL9BKW]{݉-.g75L=GC1cŵ8u i[~g7[uhu6uKJX"U`R>-s] Bo[4e"+ZK3/_0gO=1ٟyl=CT4e:ןʅ [%Ju>]3)cn>)gOjk.P؟?chS?R}׃ZZ҄f^Y̍?ꥑeh"Z@4oRojjM7ܢWJ~OS E1gؿYI?3-߀+](?ip zS[M/t%r5ߩ^|'QPWcO3o]f >#-_ץا뜴@n`* >h }\ER,$) 6}hzEet  c00-LVJ\NeBeƎp&}peVBVC>-!lhi VrԨe% t-+!۟yl}X4:-؄Bs9\-4Vd M['?GEuMDItÚlk5RJdO/燻f%&7fJ8]n/%b*Je|X(|.I%pa]v٠SrT"K$RB6bt&_, j\g (aV^R%H$2R՛,vd: mK4eRX$0٨B2X EG$K X-vEIlf>g3d" |JOO??De/X2[]4onb? n[FNVn.LNf_/o*䲹y^;3i+kKHŕ̪XDK]s\W@-..-,.m DR`}xX_^=@77WL"v: 6+vt9]aXG;6b6=xk]TPV(K\鰚 zZ4drJGh<J ࿺_cဗqL('J  Ck4Y!-`Hxi8 ,* 9˄=\3Y7B |_qO}_[_z2RP=@$],FvO[LةU `jzv~quc[h4;=XP9<%p{w?;/g`یj0㧐&;/Ƿ? =_7+T w6ז_/,,^\UxH[7t<> \]g"p``ճo%Bx΀ b1F|^ F/|zp\v8g!]#?\P5z,T rZ.?uOX.m8 s-6'"N Mi.4%RTћN$‹_  lQ@TF*Fb(QO(i@ fB$p~8)3/~uxvX';D+KKK˫[D1X"_\nkXs0-?,v]hGj7dXVOGhDIh?CqS* 9_`$u2ڴf14JwR3vH@  >B!j#d6Mx`FѫU*R All Fߙ\| FP*`[?xR.'!JS`5X @Mx?BTt?YC_&W`F@? ϤHygEs ˫=\37KopuDV<HOd\;C?⟛ 2NpLFrD>[\^5@(&8B~)XI0 L_\ܘ\QZ|xo߿Gxp{U?9,k3jRkduyNϐ лc1Ƈ7HhTdRKI{`k_'o`/o6i^:!G x}0X`U2FGó \iKr Ca@og Ӛp `0AQ E+S@[;"B7I)tᏚ+QSS9He"k`.qa q".895=zimcG(Sp:.̖GWz\HC^UXBCم]ʞH\kׄp )NݤS{`-HPM'jEb!!@983|dOW= sgqN3Z`;G$wR@4$V"<;' M?EEp/qX++Ux6.'ObQП'MgۚF\?zW K] hhp P\өd\z2R H$?ЕD%@ =Rc1`,G/޸iOl"q;:dw{mS&fN86OJٸjj\͍X-bW7s[pz+}?cG>"Fp[{MKe *N^V|H8Rh7Zf#\ J #' xʚ?IQobA0I?KBDʶ+Mjmcs YS("+e6R|>?QSS5HEKP8'1.8; Ǡf^zH>Ƶ\6.kbQ07=5'\mH*_=:k?#,׈v6w)5 9 <\ ƣ$ U81BhIHVkȢ]Zo/#&pppHK!Rk5:)1JL_`3&<0<[H? Wl.o0'1?8b6_&g0OzO,Si%|ֱ/?V3H|3`Pis42 Ygjb pL3_(B7#W \J` Tuj\{4Ӈw bB#8I'lЉRP$8OO<:E63#>̗>7ރk*JE,uZln vv\*h3DbLX?]^_#6 hD*$p-Io \M(!<(IUdU+Lh7MUq^ٿJW-,Iٿ8G?}T8s1ĿV_Dec\> ? Vд*iRՄL3Iӝ#* $F蹫b שl`jjznDáxR}x{|MZW`2tUXJrC~ pv^/hs/N NFc/אзsb񅾃+ q qq ęx$ֿͅT ȕ:AT=>[`~_$5]x`/Z+HT5洄$K#$n߁8V3\W7@S+ `q*^=ߢ[ *#/K47@'p 0L$Ef 5qu 񿴂rqCIc({r2dq nSI2U#ރH6ùIݛ|6|&+1x׊$J'1mE78:9Ng2Y03T2ˀa1#9:5.H5 n6f*'ggg5\_@)] ғV&Av(0C=կ8WyB_"C=~WOU *< Rڠi}^4ؚHVN.KW(UVEGdQnQ&]7{KOO?Q@TF*G`@?MId. k #cOciWxdv8'*>VQQ+2zSӸzF;C ޞH* 6bIjHJh&pRuLC$?,&len:&o@on vDRB7p&Vϕã">nQp4;I PZ+86g31'*\O"^!nPԙ/ǘE -x!;Fppz dfc+@hsɿ|D?@pps4T'TTTcox2Z?q/d'Ln%{[֖dX",,-o pv.FUz+vJwI_Jַv=]p=IS%H z&$:^/)ZLӑspI$Dd35h@ %Sj _;o7!JVl,&".gW( ; ۢ7wo|ϥSxS+2#@rENO Jfϒ?hɈs+-{ߐ2=3 `G9C@"?ylT| jb9ےB"[lr;@9ֹhه#;UPFES$Qe8x\k\ݿy@P^tA<&tzIzI7\$ SC"$le?r ~'Rý8gW*;ɴbP A #KN@jo+Mc۽ھ︩*xTz go σeuH{ ss3Pz࿼?= t R)H|?|lQ@TF*7?x@(ĥOi@CG];= zeT&+6kT:5(nUs _IB$٪RdĪV7Rd쟜\)^t@c^˹:`4q#o! tc"1|i$B_Cdl: %dN$ FLsov9Ej\" war ۷uڎb no{کO?xQKGe c<^s0ޯM'VWwT߰TRU F:py_ƻ+&%/C?ѿ-mjP5} qA[j\mP8꟥?[amKN[`xLG ` CQ?3wVVMSSCWj^MO2NOGrz_l<쨮 +Ci~m3vAэ>2som t,^>~:sͰ]vQma[M?[lxW-m]C2}Z|6>Qw?F/TnaP>fGuSDr?b'ga@2Nz+w;O|U AOO'r+q?}d%RF+o+BSUto!!cب 罈A=}0 ʼn/࿯?TO U0 2=RS~#+7`EM~KKME8+#<`` V+;'2016:02:25 9:51:39 x;U?z"```b`bbbbbb`b`` ""uX9kUi^zeڻvKU+w>?<_?=KO]3gk\kSO=r=ӏ]<}Gs=z^/£_|⪿<\yyrUz՚Zq=\'1ku]~\ o=q믏wk tm5\k]u.u]cuIcz&ܹ!G:/ï/ϼ+_4GҋĘu=ttt]Ӛ:zֺuk͗Z.>^4w~b]q^ϮZj+;O:zgp5n]]t9g֊z-6/Wk_ןYQ{ra2_uњ51oN.%sOcլ?fk~U#3R goƯ;wx'_Y@D]c_W?O_9}Z?tyryDOCN@NΑKH7uҍF::?Nއ$Nw5/^{;}Qoj8:'q}S߂:^8|R;?sW:ϲN\׺{:i;Zbo\k~ 'N񥳡دo<qf pܲSxj_n\۷GIHOǦ>in2Sܛs̼tΧd-u^G;=p;u6O9w~'N-Lڟد{xz]u}4OX|j-ǩYWuLZS^oS/|7x T;OZֹ7_瘸wsfP.o;Lp$a?{i:J.{S.TO䊃f6+[?Τ)qmֹz_n?؋OxgOyS~7|'NΌLw\/Ф9:z$<(MP_ћoy75& :_ϢiJ\'ӃFO[C9*_Cӌ UG7iZSwp~o$q8t{{OOҭkqh՝ o@]Oe ^w{'N^k#9Xg5vzmnZw~[{d&Oz:SCNS0՝z kqؿݟ Ks}I{QykQϸ;ͿkFs޿+ZS\ϟ'#ml=~$=/<$O??狱j4U:OݬO4O9kҜNw﹞lY(W>li̩naִh<錩txS<.j?ES/`:_[,dM뉷4 T'ऎ|]z[9p}}Po{オs sw\h92һg}Nد59a5fz8M4ץٿNs:a?񆵸6=Nv}>|qʼn[yI:` :z.VN?䟥N{ѓ<GOxIל<;_^sFh o%/bߟ,a}Og 'Gfy¤};&oӝoϟMqĺyKx۞vi,0xS/Z&|o0qS/8?I<cOe}s5oj?c8\1ߦz/f ?U&\k!ͺgq Ztw\I*.9?':h\i^n< <nj/p~GW>s7 :K~xU&59O5=M&_D>6x>{Oqn<jR/[Þ=.cq>OONg׬#:q[ݿۯZ; uӞ^q'\tk9<Ϯӹs]O^[ry[-Z~^@K`D`5',$裏uOзПӿeZ?D'ML|tzzN|sb,ps6Z8bl˩=~Ywy0 ?kM&S|n~BsL^{>ZN'&u?xW_"(zS/w\yݾEX:41g\Xυ|lL[ :r;WN>V^)^@W;;,v>u!j>1w'q8Iu7 ?s)yqOO9o5`otE”ˮ6׮g3aS&NuOMj)kvVn'O`ϩVR+MbN#;`תץ.v3ߝ`|<޺pt 7؟ց^~k&Okr5Lw5t;_q^'-H}K͛FTR;XT<{iց5jcz>8nN},ഫi]󤿷}n ._|q"0!9ox;H_?n}/L禋|RrX^ߩد{[z鳛s\]?|Oo=xU.gs3at=u.N>@#o֓ 򀎻Kbqմgz&P{wjhK3W$Fwo8З_~yOyiO}&s'y[?]#a][izt;\tڟxu,un?7_cTgTґ6̇\p?{YӜw9_ӜvgڝcɧtzMy9aܻom{Wym .o7KW^GW_=g-s>*FQ&<6Oryfp&BU#1ivZ5տo5}s~z^Rlħ>)o/K ќ-w_uU?SOUBZWIOn1\tsL Hzy!#pZ8ߦ9\_ Ήoy۩jکNڟ_uZNiV&~ G4ֆOS7sqq9GRz>8]?'5j_XUPLu}TYX"6NRoT\ί_,Ɠz9λ;p{W[ͽ7D_ϋǕ]՚O}>\'Լi?K97; u7`Sk067'V;U\#OhXK;O[_7CǼ6y`})_X׶=\OyaZæSg5,y]>vd~i7Zgm=o+!wUa;:yT'y{n kuyZ\?\ωg6roN"ITkфKz9OYׯ"o.'/u:giɳ=U`>;WBLR< ?pniޭI==X79xHyi@agN3[u=}ݝN)KzJ*nJcUf$T/T.1oG͟ o ;ssO-i+e^yTlex7ET-oq;?Sg}c0IuK`E]^kC'~rgu>eH>kG_Q]_ q+'p.o#rLV}`,y%a_ch]NuPʚweI}kٝ?}ދvOw CuXIO|/yI8_?[c}iN4וtrLpq3N|~׿y2&߆kugIs]MqȵITRO^7}I/|ob7⼀nHssﻺ67t^Vo:O}l'ZO?ޒNqwgg\{TOvΌ;3ȕ3K4 kiv7bf'ti?v'w|KZ y:[f-k=&>5a~58nbcRistm=ֽ>t5}&gYS?=%x;eYѴaabbbbaa``` !{}n)G>$6M\N:{yq*|޿;Y&[2/5y`3kj1C^$+goo cㄕf?[m33>c<})~5q}Ye^N !u|,yeSϼs [>a~wFiؙq:xu8#ȼ4 zSHN3%[jyͶ<\wk<[Hֈ 7 {^?5q2q_?n)7䍃-?'offw&7g&geZ/IZl\fY^ 'y>YOs/sgc6.mǾj2s\e,Ox7큱gaŖE knfe df}jധ'0xLk:oզݰ>^80x˛V7 \OƧq1s{K41\Ƶofy/nqbo:Fy?S3 Oyb%s-uY{M1-77jqOv>њF$cs7'礗A)V3а5>n-9+Wx@ܸ7Lx@@yVzjI[t5nm\:܌͓K "a^e~瑘gcsB7 ` s kgN?xɽ?ߟtS̐|| [y_>kjS1A?6'r!c&coMl͜iaM8᤭3q_+G2VCmtLCOf2 /1r){.궞bsnih^ M_Fssy_xOrϛk}7_nsV/=y cΩi[?Ք̷晌r&z+6|›޵Y?%<ތ~ SC!gGoξ6g]z6/yrgozy?7gfWR N&ehK1y{TRnuM7:sW/Pw"94+j.9u<X7y[fs0xZ{pF֍77?cm<'s7-#|79}jK iƫf&>7-[H5ko5=4N80>YM/?c\=?6{nPrѶOv%[?C1ϧPrLifޱi3mV1~9ՃsܲN~r.;m6` oU^韓j5/|;ysN๲Va+se6}w=i?k{w#1MϾ~߽uZ]&M}hPlkbK?aOl>1m^bDLiaɼfi/V7Y?3&޲g/Y64[O{VO9HlO9ϺkpO~4 |<۸iT` ca2lظX1,ƌpZ7i׭w5=iO] FNs}}~4o6}\?8qM/Ϟؿ͉Kq^nJ1^x^WXGG~Y9icKMJS0b㗳7gcyM[ͲlgOaּjsiWbV3Q9WhMnm[pΛ&c<3^r?C4wI "qowm-n8ko~ҴײaҰU(c}y/L7ɺbMOgz'o12bxKkuj-?_g|I7F-K}Wᄟ͟=Ir.~i3mOfY`ԴǦ1<'Lg[zX5'ߜۯ< ׆MϾl9G/m=X>4-o=wZu~_|[/%Oy[zK&fsOhsgp@MK R{4Ѵ-pgdn:׳r@-g[0rqox42|926~mo9\fR! 6{|6osgzal5tSәXNs'?#Y 137Os=isj\ycXnokW7wʆ79BoϿk>7.m߼Gxe^uA;ȼ{̃՚9/{osh5=՜{֕@:ZyԦ[9Lro?O߶Zޯ R6Z9c2r7ئock_`s匑3$77tarl}ȉ7Hjj7k k3ulo֝%I .yw\uE4_{ ^?9oP! Hs%e}o hVl8쟚wzPursqv[~̉mgoF_8O~7esv[eR'R;R`MփsPuzm<}MϘߟ5ļ~ɻ[5췺1W 5y8m3fN=6k8zcGM9b~Ϟ}6H=o.m:ՙM{ILz[9r77| nqS:z~?csw?E&^>W̳oc4bLO\[LIPgwD߿aa6n3l;YL7XYd/j>oƅ'ƚwQhcLáXf$g^cϼ$t O9ns 6EJY+W"g masNY>7YG?zcos7۳X$yg`~l5c:t~&sf>3g9czW1^b?GƱ_ mzmo[iubWWO3i7on4MktW~1b9:@KYo4Ԍm66h'YΚgz/2'G6{id=Tl:j'bæ5,6_Ll{Yf= {Q3] 8zW'Uy֜-}5=+bu9cp}~rzW%_kˉs'ҞU`\kisAیhFjOLbh}Y?̥6c?Eizl;OFr\o(0׍@.x@]4 bE.0x?MhW2., G:ar8ĺz%.n{fS Z6*smyW{Rs+6:3'׻9O\m4:}\6;ox8:{gִĚ1sF!j=6SNϸbiB, ׶+Rم ?Nv33ߞQe^-uBg/j-ͫf 6y5|:@ƷfsL60kę@>heQ>{OShs>7oe];=|/{>gZ`>Wcc{ͯ"߳sN?|?~nb+]a}Yy4~{ձe$&zVoFaslsyh~Xy϶Ϟ&6ogmff,&4z}[ӧx3Jo<UW=-P(kqb¦Y8}!`<٤YćRH'?9Ձ7oZ`ЦsֳIUmfY~?ͣ` kWְaOb~Y58oZ=~YNAkeB ٰ?8\Pݴ81:OXOx@ٟ(~z8GL02׍Cӫ䌝W_Ai7иVo,Lx.{li>S{yַF>ZN`9(]ؼeӨs`j)>67B0bӈ8kż=lόj i:i},\7߶YӼmY`> s4sQqhnumj{<'4@ќ]JcUntf\d|gb=mu~_ٴUcۿmf3mYP/#s/Yxy|jE7qj5iijOghMܼ?7dlS{M[YY?o}!n+mec1|hs@{`{&ƛDoW'77\Ek>ڼaܨ f 3-Yfn{~z޿[[h~in5y2gO8Yory_Mߑq6.fi9i:Mhz6ScZ@jU/φ Sg^e=G/tS~͚O" l3y'oϾf=\߉Լ2msO'ޟI#K?ݴsy=yfuӴz9~gMM_==]>uUӧz`y.qoe<6wEj5g:7{mPwf<@SYn:@ԛZ\N|sj[09K$d|^1a3W5_̈́5ڐ*75<91x9om>>K[6gkm߹s_~_~?n?믿o|s=7׽{{WwȚkZxkuYk^Zk&5ufk=>{?\???ֿ{{Yw>+k_c}߹~Zӊ1I8Fueߌy]Y{ޭ(׌gqct^o7{Xp d0^_'o֜+6?bv}O{ɿ{N,dY~f מ9Xa$7kk-m ]Y+:e] 9k߹po׬ӵ`ki){x` 8fk[_oև=e<`Zƽb-ZXyOH+qq}o55{5Lӄ+WІm5q*qs~'ւGQ(kmyE$.;̭N㺓2:~i>g\?)ST~Eοƫ?vߑiʽzSre/\7[huuԈĬb.ևSFcTH ֚O QQX̻7>kY߉W ]'\˒kψkm19s#SKM{rFs}=KEokĄ\kPr4Ⱦr/Yo;t7j9/{@sևqM g2W-->1ef׵֭jkliSNfopX~qG*\urǻcimY7m/F imw q.Zqxw^{N=Ǯ4ex}Y_./!Ϯ7&63w|Z^¥Qr*yn'Kl{oهiNMouȤA{Śp9&G:5s?$k3nsVt2z ܯMǝ.Ѽ7l'-we?c=bO2myfx~piM}vXOb|M:u}[_\*g5zVpfVbiB0.u']ޱif.`57gg75@9<|yz饗wQ:tF7bÙ:d#푸yu&w[{F'.v$we+Y_OM{6s8d|Z4ԿVk>YyW&Fkm~ZK9sggn ׍$.~pkzlؾGg< M?4[\1a|M6$g 3tN`Mf]: 505kS4Suڏj;짾5fmvOr =7~;25i=!ٝKbr-:S&%0b/k1εXW$xOf;ߐSfm+kq]M?w ek [|͇3okަs6'{@wAj?1vsG-w9>~@jsz@K﹉HYSH7ڎz٫h 'otcjo-a5{mx'5r֜Ei`K;>5exq͗88昌u% 0LxKb'#Wc l^p|8wܛsY NLzicp{7^9z ?{zCjds\ܗϞg_L?51h9%?ӷm>:nO9-/9796?Á3Rmh7Xptٚ?_kbo=2fٻ=b{}gWaZ}$Wbl.ܼ'&m=h0 NgkgަY/..|}G=߻1{/^;1:MU}?#cﵵW>SGmgey^vWW?{o%lDŽdScg~|yg :kX<Ώ]}>k>yIls̩֜`HL0sǔkpI [Ms?Y7vNҚp(69ۺg7ƂvyB8MxoG?gjMf7o~#?'kQݻfϹXr\c>xh|yrW2y&N9|ϡyNsYum\vuGFnGk>uɳFL!{Gş%ojq_79 ۬:sar->@n@]l7k?yKգemkX檱:{g{|y;os+ƞWuڞYsNk 9'{}ߞbג7[O_ǹmNbnlOvz}{۰NדOy\k>=O9wS󙴵LZ194Nqj2=cYO뭍Mhi klnmvLm, 9@r ^45qy&_x&[INNW=0S[xSZzgrMeYjb[tl#kO}{ޓyĜih7]x[|\ 5<ь}! 2rϛ'(oXj5ş9@;O9AaZ?vf405|6&=iM/1y[o_|kog&:weZ칢V4s7?'91-ɷSOdݼNgkHߜiyVBeGpMSo4o{E"}RߛZyشN`;g-k|?|v紌Eh۞xSt< 8@:57]K:}>aÓ@_ a_ [?3XknfF^;|O;?7W>鹇gz]/źڹC4M; gM ~fYOkS?g4_{I;$cR?Y̧>YWܵqeE/7-g *6ͫyj9`$f0wgQXc\S۝;S;co=;v~]ksa/}g8 lؿWGn4=o~{ϛ.;w\L'iY}ZlMXI9{:Oznd4gy&_OoyG+a}jaY1x\sq=R7г<-G0c>z۴3$h: g#]r{yyyJU8c۹WrϨSdZ]vFޓ/7_g;; iq;a';S?J]shgYֹ̭ϙe{~˚<r֯Θp_1ht'쭚~?xzZC!k!SOul5%9d'_ȋѐ_K;o杻4;=}c}ٳԸ,X8z99wLs؝&d-O9@;c/*5Q~3gn=4GixZNL=skq=hV3c%ouqj:[OJ ,<[g 4&SޫMԩi|s~ѝޟޟs歟blw}w{f6֒e^j1־`j̉#?$ {v_鳾>:fr3ͣxuS=fgύ2vn}}agG9uKдO5ބɟz|$ē ƪ5ݼ5y]LrKf;G4\ӤrfՃ{w7yGco~?w:'{g-oJՒ^W'_Tcs+`Y]zE^36_?a?kggm/PigD.bL!8>Xssgrѩ4w4-yOǬuγ+3+g#{g}yM8@<`=jS4oQݣ3u5bG;[bm~o~L-O뉏|sqK0Fy=KjZ;?7F~75y ؿZ9֋76Wt窭4ͫԟ[OhLaWS7j:}=s:9V;cu 1=qֿ^< `ub|;ŚϜI$LNb{Ԍ/p7%O5|`+EꏭLj8^N0n`>OILÙ3W;ݿ'ݟsMoWʜj>G:3;<}2濧8uA;s&Q^|/qz8 ec cx<#7ٽvsYTgz?p3өM?j}W򌼷5b 5???!'?~IK}d6o`?5F~[m'>>7Ob6&Z#rfpZmi3Vc8_po|ӽcE?|&5[ HgE&5τs;nk@`5`hƚLڦ~~V5>ˬix:h>[8>X\r_ 7هKz6}ߘ\iyMQʺ+S&M}I;f{_sG6=Z{^˸Ͽ{f>'s'6Rol1Y,4X :dMB? SuS7#;;#o-KE|_Ƙ QVqSJo.|gp3}'߸~NM~S[-j:GНabD~f,M`Mmh:ޛEq6b{#f&Xy-W9snZɷ}f:8O% Xߏ.J Ԏ=?kx_'[ulOY;|[F|gXK\O5}'|r=y2?lb>>۽gdvs}r1tT[D?-5mv'-ǥgqG_#Se}vd?믿WMX&\6kf#=Y<7csaBC;iO45}ۓFLt lYmZc8atoel~?[V6}7;W;oyԛ5\תEZiļiٞLukȉ聜vg|[ 5kM3{veNN{9#ͫ|S|>\0ܓ:F 8uJ=+qx |Lkyosw$ו<uSWٗƅ@vxNgqm_y};LGFi?v7̿w\OǙc<.GKϖZøf6u&=@w}g'guyO ^M%Vѧj$3?sAzuqwqOw8Nx7-]Eキ$F{aPF[{Ԛgfڜ.k׮s=|;0_/^W<|+^W=~c~n~o1_s_׿OLJO~c???{{~^tps}x^7Moz[򖇷o{۞7o|Ͻ5y]}1bX9޸9XY9'׿ڲ~\/z׿~կ~_ᗿӟ>kZFw9k34k07?7Ϲ9x~?;v9ggw<|}rbaցk!םgk5=^6/>xss] |ig>Ki=awsMr9>Xܟc=^֪~<}l6;gbM:<\k1v9i{%^p`{̿s]|>86>4b&L\0x17cή=/)6;>Yb4z7>Fy~1!=Cb>\blc=Txmccʶ.+ء 9پ7q=KϡzVsk-`^.7iԚeSW PyxۼnK6{+fNx*߫?pj?{ޣd{l|둍ǪUxCMsw4fW{q16> Bbm817;?s+7iѬACi)){kCtiw(?q/EA^snPþG=a㚬>|l We5؟M?l9?uex5ctZk _u]6Nglζ1I>Mm~S'us#O|+}qelu4W癚o*O5ʛ6[mr֢VĭF%as+/k7])O5o4rOSwomm6zQ|sKe6}N7~Q7^XŜYxp`{ghNĆvMn8!땷xXO6k[kxq޻=i}m\C {s-^os¹~~8ggGIZmT^Z/֔5n zPv-(Ăm+]@1)O_Xy5pbX?aCkxƿދNdMϸXؽn.s-jAy]uN3p"kޫ=0c?Ww^wo><W cϕ8(XXO͈nPi-:9КazS@"Ozѹq<;ߜ?~ao{fYj97}~~_lmCkfVS3:Wh׵նOIzAlMO_]]5z)'Ij)ŻѾT b?04b]vHG|b=[P:W\Q>|YZlcӶsشOScoǪ?Q.uH{ƛu>YKO>mkͽ̋sBg6˜ 0g^A1>Do붟 ׍kMlޑgsRb{+>4v#]hmj=hk-sN3srWǭ_vAN#c+9}Ыo,X o9w]uF7;^W9CYyW kG{ϲOH'>-Ydva~dzookw~j,,sU mckSO,hwBlZWbc۶?y^Zgd3VyDx=۬) +16zb~fk6h^~|ԾVmN,o4Wy}+_ʣZ bc=i3[o{Us_s"]So}ϭα.x~4c<g6yF:\}3~i_g8Ot(~i7wZb+u gmUr3olߜDKGh)W㔟ݳ/oM|n^|uncGg(ٷ.< Jϼc`.g^><50oޛb>:_x; =b?ug;Seݷ19G]\{oNŕ{6we=)_~,>_zo!α!ֶc;=:O:<]k_;u~6I(tO~ӟ>ţy\ob9^9Nbܧ >W w>Ԝ\Xi_[xuӨndӋn:?zqU^s>vo|m︷C̘k7v5?`ﶩ+>~s;bgmg}to/|Ֆ10.5^4ajI]vQ뾋|>o5·'``_җ҆{z_ۜsmzϛԚ~fNxOs5I%+\q9ף 8yOO~?19?[7A bjw7v4ِMG>WS7m6DLơ΍nzʯڧVNH.q|~ss>wa [?lq>~;.>:OdW=>9j@i]UšjK#[)_; _暓fWؽ/Noj{wΧl?3gۄ6'Yqi}s a7^~ 'z\k]6w昘3ߩ;7ipϘgMO1pL1Z5 ǺG}k΅ǿ ysQNV3s0hGluwCNڀK1u_j0v7w<0יk޷B܁80qs=$2_V Yڹ(>;v ^ƂjEjxoq[w=dNMc7>#49gZ!zo^ًwS7=սENqw螿sS7U?t?1<5, k{83jӟ7zWT 8Tc|އs):n@?A.ش|>'ZakQnzS8(pQCzSͮIMav>~q'ΨA y^쇹~t[=2:I^?z1_^B?/pBd)6u޿QեoV:dž{t^:[}mzcPg`; @߯g:ymg[q5˓gWKe^D3'{r_ks7>3,!m6Tf=ks~jnq8ft-z}-?}G+wzHg>uWW7m'`>Kq9ΥkġZ?xl>o3}u+GVO-RSx/sͳu|]#9g:m>?>":yp͈ly±os`7;?wz+Q98W:\{ભz]{_NM=M=_sڈe~7x p k[u|?>[^\|ɶ513q l8jvߙ:?kusyNV6%Rp?ia6ߩ^iv/d.h^+S/{WXgG1`|[[Y#~9?:s{ssk gvM'yY'ȘpF~7w~ޯ^ql}IVw[8w{buąFqoػ^1q5xM߸Arh7goc}kAkzns{s .g n{pt?۹us.t7?Zt# &p?9y\X3detj2/{ڏ{XW{rO{']>:֧'`nO}S^_:w7֙\{O3[߲B!z,oRZbj9(̍=F,.=; ~m?NPjfs-d^6 ߞ~)~TF{+Z"[Qݟ`mok @tF:_/6r<O7a7<8ٓ?O6[Y}uzZbkE?KjwkU]0YXCc}{sW=+{,j?{sÛ3ke&N}CzU][wY$p35|!X&2w9oΌ<  foz:V;7Xfb9<@nG|؃ť}s֨33ֽI:=Y{ ]9m{@oj {_yн$ݘ#uz_11ko˪g#/4f7"lug]#3cqlo`ѕ?xU}(7[V_8Wi'k=߅ݽzmLJ`xsa=bhseckP|3Fh'~ɣhOO!6s ̈g{f<;)dw +'s:1ӱs'n/==- +=q>u' =K/FF۽Mo@57~yW,oܳ>`3m1k, M+ͥ:F86&3?}4Ls}Cb[_o.8iPֵ۫ZZ=_~\9[:@ˍ>xctq,o4|8 -5)m< {ZWq_/Ylga=}9g:O35OmBx-|,7KTͽYpmƪ ^j?γ sι7p眭!l[9_ǹ3bnZlW3oMAfE I<۹=_kHw}1:y_~_أM5@|5^YͺU=aV0܇σsCyn 0aۗkуaOAE9B<9=9Rø 8Zv>=jN N{d>@gPG?ƛˮf[[g^Zkw(v̺MSu߰ߦsu\ٴ_ , z3qؾ)oCΝúj17nԛٳ&Y?h-ej~(9ggCm5séCK\77'+W`g?F=fN{WwoJ+.MY?[o}Fho}Ʊ{&cc:u܍ujk6fNdj=ܷIcakMw:x$bg@uX7 tz97[7WSur뤛7_NG>xo3q8M뤝7tޫhܢc·qsoYe=>Q]7IbF %y Č?|T#w u 30k?뎖j$)vN u51m[Ny6VrNԽsim2nNJh]/mrc^38/:;lbc\Anش@殊]\chUw&=K8f Nrz{5IGNZf֬cj.[:x9n[U7""Hcե(nBDdFFhhHHHJHhFFFb/@AzQN92wZ{kfѼc=䓏~=?_/~_ݮ{ѣݮ?x3n=ó??}xs=/x??}ó>|?=y<˱0?}v'̇ycyc|1w|߻f]+0W^=/uw'۵Ν<};÷ow1<t_<ùV_̇2fWxm.=́ud_կ|x&3|ֱ*G|g}>?}g\ż]wzիn} _x/֎ս\a |FfY{X'➬żϘgy3~ǼʫgzfXdu\Ώ߫+]sW;;Sxkz&3Yg[>/12~)#{7ܓq9@>aKyf=OY9d>10ob={9Ƚ㺗KV{ڹ369a ;{71^#u=-=Uz;Ě.)'D U}܍ _W߫([6>ƃ3k7ןEV3{oݛ^aGձyRmϾ:98a29f5'~G㧵K7) "Eb]ęq|Yγ>YIxZ}&<wk'W~k9~>#G\Μx1?kνq*S1 :Xs_qs?֛1s^3x^uwf~a?ǫU(ϡ>6[@edR=胲>ź~#x|c{oi3bkg+ (,rOE]_y{N8 s9܏y7~|z^/W({9m:(kȾ0W1}}Xzb_?rtV6gwsc揳2vschw:+g5ןX <ᘍa1L^̰и3S2:Zgcb%ճyW1.ʶО/3_-'G1.bB#c.^Dt_'_o1cjuPg+dMg4y>oܩo0f1kslag[_<[z>٧XXn*ggN1jcjO<@0,uº~ėKG ¾Ÿj?}:fsrŽCb \g^<¹0yǎs}q<{==O(RlR,6^B?Kl|I 網]L.9^G>ؕ<>ѵG'Y 4n?⃮;G'*_yW cc[wǻb{9/eaXL}^cr@_}+mӞ7ܚ:}s{|P]&.#c2VdV>/Ku6F_@bgXWuO12]_W { V;#楝YƉ3zʙw)7Wk~\N`Kl5Va30Veocϊ-ڥ$P&(~XYLY{/cԿ~bI?ñ>:g@\.@~.qyQ 't,#cNƧ҇gT<3\#藚3/nueh.OnCC/KQP?XW}U}Ynr]>zpWqmO~1Snxs<+Wvq_aqOߋ˵Xݲe~}\u~빾IŚ*g!qY9ddm?7ϱʐgnA?SŚkk̡\X F~o+or3%/Qg.c+9$VgsOyڣC׏P0^ۘA}\qnK}\e]ߕ7zQKȇG̵{6攚k r?_Ys}%/ghck_Ssv%g\xŦWǧ\H1g1{wu1C}񗣄~2g#w6y)L5W/;ckc+Z?83Z|~n1ϵ x'__/7>9k7-߽?*) A*p9_@\u3/#5rZ~_i9#[+p壴vWcWzӵ^SwN5u}^u<= <:Ys+p:Wu qocYl8ڦ[/qN7-4TirqR92z9fX(^Ud=f;kZ])&z1Ns֧q|-r>ƒM4/tc,FҾ Z,=>x0s=tN]aWu9N[<_Luv{rN8˩RPk*'iߗ\/V_צ]jQY=o)GVyomB DGoָ.=O߬zAhOu0ZW\<I!_+4._:֡/ua7@ 9)/KC&W;:qTPPz\͞[P߽1յ.zsk[kٔg[XrZOkOYY%hbk{IJ֛_t=+xL~|W2Fu~ukkX=x6ss'b֋pEFP~c+g뛈e>Jz~}˟vI}ոe{Z p~4Fўi='++ڡoB;ɗ1aY[lnb16Un}312Oq8gi#(#9 PkX~HU}6O_g+->xi'Kԫyp0vo8OYuƬ8nSo]H+}Y_]rk^=`^5m~+N?<]\U%_1_eݚuԸӹ^a=J{_˧ku{Mw-m?p_&ḃ5%1wGBF?lkw}+rݾ={# 圶vc6X[V{7N^{;~^e{s?$wւ\y&ueܫ(EFjK3C>RhޔPYhmWk ؜ ס<+^6cKkg[MӮ0\k@ۺ|r{/Ggqc7nyO>KyB~n#`5N|\_rfuc{VjNg=.:E__[Fclֶ=u`u /}閏el9 }bZOq/"p{Ӈšc.c!ܜ|ˁ6˝7ھշ=W[_}&[ޫc]Eh';FRg{he=uZ/Zkmgݣw+r"nj'y`<=s>xNy:eG뉯b u>h1E}7Bc%e|~Z֫;Cm/uw뢍Mak6PH@p\~9+ `p{O>Hi~lW5tb{,Ϣ\3i/WN/7+9/aq~0t21F,{Xgk/r^sm?8eXyڟU;uύ/};gcʾN;BXQ,-@_>sinm=KGUAuum?wȵ=mOܾkΗ7*\|rWG1k`}v(#5XmkN]~nM|9e/d ˩` d>z1;Vuy1M`uf{ݜcAZkLax5k ?NvS09j,m{ާk}7O:'m_k]]3^bLyVn=~y*7L׾w:~\c iuŎ9<)Wgg?{ә苾SM]$7wY'o(+#փn׬OsDk^0֒5__Fua^)WK,>bvW =呚sa~s7_~7*OM0b~>krZo=ɔ5 =Ɗ\F%eb}r Nذc2 ڷ\\U|sߚ~'<^vY5ԗTߩXrR=Q[?jn-Vuke39OWNbonOT7),oi/Ͼˠ=+.7Ty4~'<hh^Ҙ~޼D뇷<}tq1^S?K`׊g2>Ykcgiܼ<4Xb_gk X }z#-dy}y`z=Dq獻}gʭgck.\[g nۺ>YeWN ?s/*W<ǘݚx&r#4ۿC]Gf۩Ul*[ε>10?,Wcre/Goʉ ֘F_vekN@yj0M{<8k}gXJS)W~m_;;V5wk"U}\]z{S7nxiĚ3\{੫} oxĺ[헏G7wZ\IsyѬ(nMZ堫[k?Odo:l<.LzO~ֿ֘.f\*T,k{+Uuڻrk7Q9kn1ir-.hwyN1=ϭ\'xu}X{ {s)dߡއbK{:7/.(\\uhdufcSLYڃ}bژ܋+ ` v'?|lz3Z_|d5'k_V|)\}rq=:SMBk=?~ߝֺSS_mlϋ8X$(s(6Y(ϔkXy8ovƤ'7ELd}RF-?X1Q[A,?XaMy-y<_Ǽ]Ctn3fں1/H{kW+q(G:s/Nf.^k< Of6'̈w|D>g%>|#ʼ>H1nbC竜np osxz &KW~WyzL] ^Zg~)ioqjN=j7Njk0p/>qj7wNc;Wu\?'u{zz̮rm3(O=ڛӵvs.缵z-Y̷zH|s-5斣eGm|gc@ oqGǴirhoGm?k3Y }}_׺ ̋5mn^s.cִb)bC ,d[m_}kk%>?wר=Rgn͹}\~UA׈ϕCo+}V^Ƣcƣqseւ+yBK"g*S޷`͹gz. q6APwkأRNRi&:r”3Ń6fY^֫- xbƯ?}ojsݛY}k{([^KqmR\Th_۾˅+__gc 䁟oYeoulb39E^[ح {o} \=C{oƕkNBwmk{OvxnSWic:m,ϲ+gmbs [P5,^Uʳ[xnNϟﮑąoOE:Ǟj~7 hOT?(vʿYkg[3;oPs;@}=XoAUܡx{&oRy\o(eqk`)(ͅϭ`.A'Wr{UFzt9el>>Ϯpb#ɾ:U.#7>tߙ\B{Ta g"s}q{r8j5/eN0ίgYάri|z"[C12j?m}U&-p/U+@;QQ?ƇZl'cc`뛬_@Hk{jwO ܳЇϮr5/Rs({;T~craZNBX'չnYVN>,'}aXnj{ox y ^W&<-iw[:7<[: O3m|\A9̉\5rzNG 3wBL{-4֕l^j``UNQZ].g1Y'Nlb"|]+1$ӧ+D1+>3xKcpw~!ȡwP y@}{ 'eZ?ZNש?֧s_αg\^k2^Pgt=Wc諶JCj Y9닪3]|Z U~1b6a֎v\j#k%>ڱ{lbW| ks_ s&+/_ryv\;r.;Ij#S~rc}#^AGȱgzsח׆۵riLmYg$sԧ>oo=ek{t4#Fl36:P/'wb&}?ښm S=uGg;bmtccYkV:Oڡʗ0}=>c?u{}fw7}n2 @GCGk{kWP!E軱Ng[8%>tMj扯0}gcrbŔ6-`Sߘdw_w\o?} ZTqvMN5/ﯘ{mrcxkǴe˳V;]~:Ȍ#|f9P92[]#-6n >@7Z$/>A'؟_]gzKsMc<-KL{ZK,>s'o7s}jsG1~kKPc}L{PWoOr0#a|)e N Y}:5':;buO1_t\ߥf}Wx5>1Cy&*qs : ~%^ֱѰ63* L}/ ;@̊,VKX]}f-8s0Wlkq셰=E;֎~o Sbhv5>t֡Ƿz3nyƷ|gkES?+ʽ+ >r!w>9 'r"eαo~:aȞrblC#R~Jlw}vڟ}b-ur])7˅AOZlvu1Vk)짡x{H/f[儵/[:qvOWd`b "fS+_a}smojOؿ|T׼>}?aN>ƕzv_mEڀ(c_b{TE۷ݢO}S,W]bָf-470\(3]~/+y1ksz|u0}A6v b[C&fj/fׇUOyVyN.7Ѿ׺7j+e]Şk72S[$FCb_I~y"Bs)⻃ ggBy&|yEn_ƫ+zFl #(ќ}Ŏ?Fߕs{4SK.*\I8&[S\ᤏꓮ.S6'5~7Rgd{bjھ+N/F97pk'_~ώ/v>ϿgCyw1XR_i}Ӊpo%W}16.cK:9}j@W3q{Xik` boL#טҜ?c`gNs{ɭVBa Y_3'x=)tyQQM[aNտך|oAc1BS>Sm{$g˚ qApXhV۷TXTW;rڿ^գϩʟܱGucd_b}: ?>yC?#@B1rr{Db2v\_Źz?jUq:*8#żasWV9/޴{1u0gQkZPڐ@mko_-~[sQ~_m܍S 8nrnOi'iʒIյޘȰrܶgYwh[q[L!?|"]><[?>~\6oSp wq{ַϺ}'rny։3~}.9]Yx_z1gD]3KoAkxnU^kr ȳ\ Lg̃Nϒ[ ~gXg֛uW&y{l-{>oƞϑ9ȓ</")`^rl(5 1~_^؜y*y:F.zi{کWh1ݺê;zk(i1rӦ~h7.Q[y%>}f9dC;t>xǓmQ{{`DAL0a„& $ w:=񣾵\#v>kUeefeVz_|???o}7'O<ٛÛ?[[[>w9xmi~sz&8soovoNN^^˻;;ޞl0qc77gǷ>_ߞwm}}>??>>}}]oςa,kn޿x?||[n}۷}l<ο˿C>C?\<&cw~w~~n>>?/ó{phC^ ޳^?c??o}k7oo<_˿\h/|z<{>nG|mNs3X>~~6x~a7zOx|hA4c>G{}7xsc<68wncǻe}imO7G{]̇n臎 xek\h/#d.sX#4',{iF^ c  A٧aڿ捷bO .πH&[1}y+>?X8ЗK$^y 6|B+<{k. =WOt3o{־CG''7%'ɴp=ǻ|<+ 5y9ج{x0Ybֹq9矿>rq;}qqcn2g/x7gn=@f7sx[u{{h}9~??xԳ·h=&{pg>ϓJߞsmgy|f~>7Nk~p311m\0q/ޙ/o^W]gn.tDOwXٚ'e鎟yl_O|eIzا>a/pO`~8Ì^cطپٜh;_u^\;pu6x䐯+y/[YrfgǞ׹ ϒxNWxsGwg3z}xcG__gSϼ1?ӼOݽ7\ǯ}OH/ε:.k;}%LB/r+ݲ~u^P?'5llKwvܸVcExԜx6-y[ܳ{.-Z;['8;d`˞;k!;>??>>e^߾f6KђII7Yc_u{ٟه߼]?{`Ȯ+xEdM1>;wwo0N.p >`19;3i.;/G=[?{w*YٵNwH~u#?xzb}C#]mM#|+3|;zK4B/7l}>e _ރ|jşԻ`\Wgc/w,h)9@Zs`}-qʠzO,팹|uOgeiGٰtssz3٧wsß>| p.86?;3?rNgVHll=g+Ur>Iz洿֚ochm`0e&m N;Dr] ,͟FU^(@!^ Fg_?=OӱSݾ{;o<>YۿCʔ?qOW|(x#ŗ~9=l;ҙ՞<Ϭ_? :L\1O#_去{;XZ682|~|O菾Ɏb43.|/2~l~{zxG#]n<ϓYu[4:t2 |=:{?3>&7O6({1YI(݅wѹ1]H`cʏy RNn~9Zp&;z;dX<|<`o_Y6Ggþ# 70';;_R9Kɓ>'K!9W7}ҽ8ԕku=׎[9~̏gw7WnQ{wN9|y_dyzצ_Ik| w/{bQ ed<әqmo2_yuRŗuFzu 9cv/DMln2b~iU|:7->{\?gtb'eį؇cm~1_Gl:e}Kd ֹ;%07_u%Wd*$Sq˻( [nei#C|彦O{~K];}uD7 ]deu[cʤ.}x9I'K~0ݜqo_/!D^ݼsm~c (fxI^dpKsɮpWδ;pY6~+*pϪ>%[s>ʆvKng?F28%XMmd,3_7cٜ(_ߐsڼ{i8}Nśo?m_([So6uw_,M ]Ы:6v>Y5ٸn,|H|{3zbՍ5}P>=f}UB*\ٷᄊٺ+$43^zy.tA||aTpm `sO{YYYSWO>fߺz|O?ď/<@ߙž?'g\봯/p u8iMgϒOpk/>%?N%;ɹd^ՈnӋZ_ klt+R9rV,nO<};O>7{Y;1l)Tit[Sn,歅^M7lobk7d9^1?6ש7PNuk^<(yׯ}kor^c_llBx̎7BPpfUenG+_ʛ>/wmc/N>o(}9cïʯ`;{pUa6q 8]C}{Z=\l~뫦`*>tpg/uZor@S F0У8|ևk%Q A|_ |`g[ٺm!/_`_t@ 4}eZyHv>[^yxƛGW>\ɽ_=#װ1=>Ł8cV}:}[e1lY3wV'yߟz~x~l}x]Oq*rzKyGY[g{ˮ?^Ʀ9{UUO}^spy9~^asamwX/:zC3ɼXwjNd) _l{K^rS_qy1Nr'T0vz-Yz$[Ҙ?3?s|ށSD7[οƷj?hધDϞSs``4f:/sgoU6OP^^}g7>en}G/5a݊lrM6Wa:x%#ۚ" gʩ$_tU>?s{RO*n}UvtVnswf._w:=?~g9SܿwN_\pm38^[KQhF[|pT>?⬥{#Rwri{hU}";<һ17?W W9~N[}+d0yQG<*?HƔǎˏ[\WVL^E OwU}y{^/rѧ+d>]mc/t5rKNOܸ̎>Z]9_˕8'fًᡳCXos*ݯFjMOCQ|?+2fguջ/jɂ!k NI>U/j&* Sպo_{g칳^ =I}˕O&=#5J,ۻ~<[n[[\ϺYpꢭ.^ sXu<IՁ/>uܿ~JRnϑ{O^ r:clljr3,7GV!->dzp}ˍIկVg7*Tvz|j4_=S|!`v-Ӻyy7YaL?fsGyXxc咓mwqs[X5tZŦ,/k͋N=뽽bv(Ϫr,[W ౚ gw=lޞN _0x??+6/oV1;W>_%]A>?To)vNr`'>b7q7ͧvx{֍n@>=g %oOXn|?_^\~`sZ}sm^G3^JW5gO@o>ycgSO~s$DΤ[سx)ZL6ITK5|d ʞ~aO$K[BBkA#6Rqҵ|/~[kk^Tw'O=;WhM6ken=`+6Z.`:h\3/<p͝9?ͯ;kHiª9sc|Yug]]fyc.=Z`k'__Ny-~O[" ۙ?Wk[I"a7fg}ZO[/qncﶩX]n(y._9UbW>+9y#bWPۇ!?Ʃ]=nwY[`6s={`Z3m~7ɞ"Km)Ȗ+BrW(pm$t8Xh3lln6OOw˛S'PO6x|U%e7XW78<_mFeyMУ;;ׇ~ƅr E螓-( XMd9g/ӟ6|v;C~gϿ㠻6܇a]߽X܋Ó7\{qG:8u[5+CotG|y|a|_Mg W/QpqDzds"X}cCu:}˞_^9x+/(5g= o>bQS#@r&) Hgg3ΰ9pڮxkO={ٜ>ڦc0xՏmWkLKϦܿ5 ݣWVG5:9{gl˓k/'zªKN>ͫaߐ*]묾|vŖ]q~ڞS!䂵- ^Ja>rY_şj3/κ+f{NvyypE/~1?x}뮷|=kgmr]֗=Tw ;sѫݞQxsfjVv+G&nwDvuZu#7? ><kbM ^!~ >Ƈ?  ?6狫>EXm >)Q>k =:Qgrȶovou9,d]=ޢ ;9E^yp{vvMߨ~fsׇ~e1{^VW8*/d)֟5_U#n`y{G|ƪ_}ܚsvk-ZT|}G'Z|=[ݘ|歶:sko/uʍO>x^qnzUcSo? TӁo>Oۋ )ֆ5oCgHk_>`qZOͧT=\y:[OrT+7[~NK+%S/=CXɯ ~̾y,]=z}uWUO >{mtGTJW,Y}y5qrn8{:yJo4aCԓ>!>/7]wKnm߯9\AuW׵E3ȧxCwjuG'Ɵ1wW//g2%ky[}eWezcv+y)~臞}%$OЂ.'s>;;r{CԷEm:9^BHr ?TDuK}jR\oi\k[yg>׿Ǻ[>{3#rcgu|_|3rb/}ksk>i]iiz{v#ljg{mjμ!Q:moߗΆ^=U K'?{57OϴZ79[|O~ޟ韾j}ѺcXv)?WΩ >cD.o?;eDƱF-;#ꋾ苞V>r %[ ?>*Wɧ+Fsف1s^2_|tV V?8d؀po]'_\[]([l0w;1z^oqtn) ) a=UZ o zVSRI0uNK&k Aw}},?ϝ<rse+5_=͉=WGc_-Y~>x]}O덐}_ƅoX9R>5\>EKM~Dm~UMyw{ yg?O8{˷KSy{mruH=}_69y/˟Q~@?ykd>;>۳Bpf=/+<32GKg<\&9[*(쿵ShvO$WjY\toxY_>['7GW=#D'B7wCWEg;K<6x(ž>#,T)7;7g?[Q=@jsЦ=?ˡ|9H>X`m o2ܥ0?F[Mn<2{eÝxGJX[zW_OsUXtey7|/̕#{{o ~ g굯z?2g[eWldONUh{O)?,[R>*fsr:S.*{իwOxȻ!|^9O{";{U{ &0m<{| ',$woS)ڿfHwtΫ/`n>p^w\t)}:w$7cdpmſl~ΘCkр^}Ã9xf X׬}0}9oSᲣ1]gſ? ;[0ž 3<:ڏ+Q_ƀowvB=7VNN(8-y{֧bkӥb22g~_OOgG{sO=[^x}r)]`E|wFښk={.[gu}n_Yn>b}Xʙ;1hO~[cO&WggCvmٻs/Y{cxb vo!c ֳ`!+T5pqbCG&c t"KfkId)kdzGЈۗL3G . =:eW`twwaj'Ob"Oj͟=~=˝rN?Ac寪A'ogB>hj,'7G,:Ωٛ>=9?>߀O)uVofSc _ך\>we}o}V&Sߚ򇪻j 8Mk+t.%>rf}{h@V(OosT>)o{HgkUyꃫ|M|N*N7MXʷ/; שOU|䱺+g,S϶g,Yyꌵ}yNR{;S#κ͏>m{#gqφ[GZ=4nX{d[Ч~b`*׺'r7/L.䫪[?]jL8\l%n//Om>ٞx^& XXe.Zd˿kcŃa]x;=]l`*o|ُti_/Q?b/3pXzfcc{[oreJ7Gz;瀏dx?xOxnE.Z ]׺_zNmNI=x5Sgs!=vx9 şS1; ϿpGg5)o0f9p@^ѫZ|Msswo۲s{%}r_uOΞo9֯ik;vs7=חk_^*f^}߫o'Wׇ󥴖39fQ=cC=[|{[{_tj gz^o?x3_J;r#嫥o?{I.Uؚq1l?QcYRLݶ }kNqՅ^}=Cn"ƋS,KK+&g𭒯p'Ǫ|u6gwO?X=]GtjªC/Q|SO%al8x~hx9呑Cp7u}>^ҝvr{nh;;ǭw{E'G\yg]֗Mc2|W >\g z]=cNtg_/7|a'>ů;{0w9=+85Kϗ1GlnSP.Z`r)ocs t'QwOK[w3{ųz+=yV֙K{Vis66}lt꭫>[xoocuW/,SԞe֗aWgz]XT'[tr#\a<2ȗ",_7{`$O9Q,;ۜk͵]ƧBe pV~0uYu7?_9=]$8˜_B/t߃w};3bXw.4A~sʥ~EA'26#z':Y'g">ď֍?~.gHioN ƫ&ף wx;G p_ w0lgOJ='o^UL{oUީ;^)_fcx9ެK9:^|˷<_ʱ;Q[ϸHW7_[3Տ9hp,y)gݜ\˯^l߻!QwLqV_oZ0 &a߼dG9qWBΑ1~共꫾փ~ {/ pe/\3\N jDS6٢xʯ}ZblM|A>flk/@}и^-Ѫ|r:]'yO5{'i\zJb5պ?_Z|}zNn艮Fg;#;7:7?|hl|?_-y>B_^_X^mwb }: 9`.c F:#/RqGK~J'3Y̱e+7z+N={ĭZXi);::*q߸cWw܋o'|P/lumܿy,zzk9x\mzٞ%щ$۸^obwV?!O¯V]T}xb 'x={&m̱orj1@Ek0o~ˑ8 nXG9ܚ^~wtE_tFot/>7^.F=kg\|9Ï[s~@C+7F5ݧW.a{<\M1l:k9[7T{V/r6^H+<=VQ\r7i[qWu{gr6>}8se;u\ MMO^ύnUʇgc?ֽ܊~ޞ%'mzlh.\ޔgo >Yw{n`N_A[ pr Gkc3;[~elCz q=ªs܅ .Ev=vO 7yexkkoz>ۯI1[+s,xG}#1n&W4>!_ p~8n ܍bXW-7:߽{\~zYO pC.zW='7G&fxΩ>^4:'/:!9kh rSo%ħ+1?W=z͙&3ʼ:w7} q+{f[S~/yO'?I'WIyX-W'ϗ`{[|z+=jNq{i:*]qQ(p/ ]|p\;?Y[9}{Lջ:_o{GjnMi{XTuAYO9(>#9xO繞پƱ^~<_k2Ro@zz+3z{(<N0{~y _-g'>~8f~rS?TW0VǛ-_}h?ڗőj?׽/9ԝ!˹oO_//2ïW]}p6V@dvv}|\ ը_ܹog O9?øۻ ωWS~qhruz懏?7[٣nro[]Cmyl7c:MY wϤZOT|éߓ5Ck_Y'u_u7bn:%#S Xb.`nkhOK64Z'vgc`S#G %Scspc9]ZϨնc|R^T9ԧ 0;W&^?[?<)|-gN-Rf: ǣ'}քE@7%_C/O͍o𳵔+You`z>/ |5g o;0o8~U' p|Ur5[bm]w|U՛]}4[#y[=FKz~'F{8q߿/uK~?Ç9WǷWe{~~pK\bkKПр<|ӟ>|ޟԧhD.yK^D~p_pk\S?S^~G :РWr?я~t{;2O_;xpUre.)omtx׿2W^s-];BY_:9\=O.>.??~.w}mnsoM~G>9sx[rx޷с7hޗMnz׻~!+/ npo~>;68zy>OlOg?~~ t}h1zр/| p?t}~w~h׏t]C'zэ~|_~9х3y;>N?D_?zO/=ӷ\  Sx[c5ދ Stv2&W7z#ed/g2!_מ~?ݡ_ 9'G x;O>,D^cѮCvwk QWɪ1#|FG~.>^NxПW}c XU2j c(#z-/Md>'N]tOƣ~Ƕ/l !yj7z4d_)'!/6vW!^'pnXBGh5?c>51y&~c:r3Gs=Oċ芟W6G_~c|;7]ʿK׻E^|ع瞻]| ~V?KwݛO6W~eOnٹq_t脟ol} {wӝ6]ߦB?~~Lox/~6YJ0w{ 3a<$' v{zg^~w<~~+ѫ ?>oy|ȉȍȑ<ɕ|əɝ>~艾>~q'Nn gwkvE"=o.؇ a?dN\.NɚgvMx%?O@oo~yyX|l>= {t6e>`" g͓xz4_%/<͏1mwyaxG.`lbX<=d1Gojdrꩧn?C|ؿWcm2D7 9,O朊Ū^Lv׻u A&5O/oe+?kOh"l}w׹}wMsiG?:&]/>o{9ȓ\xMt 胾KJx<opGOël]?a?hf_ z%[X.3o~%m:o#:h|h_4f+3k\M\5?Gc%[(=Ss,-0]N93?* y l[4z,/ΧMzgsbb:͏5L3}y1- /1fJdp#F.쮹]4_FGo鏝x}b/~s"y__(r!9Uע5)ߒ ӛ[l:cw͟w__nhyl+I0_6|'С?}d w!wyc7 Oc34/b[&.ﭧNsU؞>)?5Gw_>U{~jAs\_|ȃ\ȇDno&3y;} }czl6^~Op_pop.S[1~őgod7vA͋]~ZbWZOvZ@;~g<9ɶu2 wtɼ؎'6%v_+VoO6h|}?ʟ+:g͹+Z$ߍrjQXy\Zsks\nƯlgW7쭍֬_,0$&C֗Ra䵯}6F/Gj"Տ1̘WJۭw#%@w΃}u$X(7,ьga9Q1/o?.wtm] /oh-n+?"_ב7^Ѓ7Gql888*W_hp~OK$.xg7;(;!+a?=+;uG-F ?Q C/d诜6{|&5M-jwyUGUk :0NjIUиA똵5Ko,[Gߋ3kSf?woP_5 ܸ;kkbc̵,}77m:a-osb4&Xj-r)5 t?I_^LY{Uc^cxov]oXYo]]>y{OdL|8֬+<ȹ5\ѧkˍ>Zc-?kkF}OoLEʭ۵O@'r_G$Or%_r&oro->~ mVlZ LƒNnGDFgw;Zpp_] {hm E`G Ȑe{}Sں^y{7 C5O/N?S_39a vJ9[E#*ݒou)XL>~O_[ <uͺYGxnX1sn u]?O>1utS0{c ?UY>AhٵҮkkRlY=]h?:Ѓ.o>6t-OA.h6yiWH%_}7?==NO?.'?\Nn>`pp* '4}ohb'EdΎ“YNkύ){ku5v^h}Ox/))OZجr?⿊Ж,Z|lfo*&vmc5zbY5s5n%u[^56拞S/nxqs]_[(,w(僣yܺC߭掳ܿ9NpS*|?|k~fiߜ/Oi)%,ո<+uRc}dF&h.gWҁ$yAjO()?jч^LWɺejЁ\Rd<` sgldI}~ C2y\^_w/}`ڸcL;񎛮az1իʯ^л9{{Zc59^;jK?k}詞Wkg>oߓyxO'O~!Or%_r&or'zzzr>~^>x#x+>3go8)!Sv>Z_b7GeW@7, a훡G\,:79ۻ0yFuP^5mC_l&?V _XC{{Z_1kߚk[{#k>@nK{okah<[ߋ5Y0kg?˭5{uh1xQǢo]3O0g?qo 9ě]SL^gGFsrv]k@N'4?jr'rhm _\b:4JWO u~oq:~=t)[,TGkŗFxOO|-:wUǿ Ƚ(|J& 5r{XW}m]uԦg|?Y&ʡ^~x]6gam^NS_}&+׎b /}G>Kc6y[IKMh)=5lm+s'mg|98\ 'ْt}m:rp, /v;i|s?f [|{:b4jk3fo*{uP/k`aUnjT[d\uY_|ss(o4&[Wu֍:9V5G߬yҽؿY-Jkk?+̣{ٺg-guM#pPyĦ\/>,s >yɋjN~f[ɗz<>Oih=d&f7U?E>]/mٟuLgIG|o-u$~F|W-~%#K^V2lO|Ї'6|ն'{;Ҹo~TV??=\lkoc5n^yTSy]? iܜ;Gu0՚>:S?9GpMnEJL!^x/#}ҫ>>z_p/x8'/8k_%3O8?/ #C|ס}n}uz;dԯȏj3ڼfQqԌf/B6.~,R`3 b|yq8_/`kshOvm_{{?}lLG7&f:5{o;:k4ϲܟwB{?[CIoo3?=Xs܄?\fK&s^x/7Ux] y9̯o_Lw6HCDAco~Ϊp]9v|x]~O=U3m_b(>_}:yV>AN/coTw-³_\z0] [96Oo5Zv_?>om~;7ȋCIKMڡ߽vM;IKM:x h~ Op9waQ<1<õ{`:v3s}YC.]׋s8s|gù/;x6gYѬjuo],l~{ |\_}7=5wUM>I܆o2@;Lڤ\f5쁿~ի^ټ>ޙmyX&}qHv^͋ fF+̟'^ԘGG}ϗʛ36}hG>7oW?FX#`>}FYwqR92 {%%6w&|YsT.nzsjסt6E?>/ÿȃ\|&'א<ɕ|əɝ>;4W7GWgwG<>^F?pOx#~OSxu/1< Ghg} ]W,;.أ>(>y x/+;?~~5CVc*V<1Cz,gV'_MFaooZ}Վh>rݻ#f[^y:kq3t{ػk @1~m_\g^kx^hb|y;;xSk1bFs?~jS_iG}f=SQ͓T|cx7?wƺxHgۙ^{F.ߕ+Ol=a76|k]d.ݽbYK&?^|O7 c>I9c&]ڦ{^6V?>p{N5Gnr_ tU#y~?|Z99\ɗ䯍l{z/=)3};<>~t~56RN&FMmd]T 'tκdWmP]!e3F[l,mYu\m^g~#dWb|>l> e?|+?-\xZ^ ;n0~3X5r$ծ=?q=k5OOw'mgYc2%g7/IggOv.͜{a.{ѫ-18@~mmI܎'a~jSglЙ i||'<@)i?z??h ӦԅOej/JOd_lv_dooKA5V@cvҙdZyYi)0 nKln.64X]}5,ufZܧ=M{φکEzLu >/5<1ȋȏɓ\1y=8z/zKKM><^;pZ <,^<pEϔb/Fﭭ[AOc{5ޝ7s|c޻ | 爡rFto_Ykss軘|B9kks׫ܴP ݳ1d1L_s`S/N쿮u3o50ksn=go^Z{7(wS4kw5)ب~>ǟcrg9<)q)c{g^z::׆_Ϗyngi QO@jـI[km/nWkC~S,$tټ%/ 6usu`]=uяnr/C}{,~||}Wh3_z|Mn:ra=3b$bmx4 kjp=\Y4G̽ŏlfvVz~ݳA.K^{r$Or%_r&or'cC5rCOEo7IWz8?KvGBs؃xKS4-<5|>Z`}cgͺr,m0~3H\Od9I5d3 s'uX{U ? OMO?Ϻ!;sE6Kڿv}pRxZWm)yn~-uj6+ /;Oh&ڜ3羃μ{d[C{zi@{QMz>J}=͓-/`ѹ%/v'-c~WNGluV^DrA V3n֊\L3,YhK˹_N>=UYkCDߚyQ'U><y;yk6צּ@1?@4m k_׮<:Qcxs}ssbkEq5na=ӎN<AsLpw^D;GrDE[yasF;fbgYJ/gE;kx፳}lT1McǺa{3G`"[눝 ^AyE(OMM&:ν' SOנx n_K^/굫" w&~ȘKc5ֈ_W@;%W_ygብq:&ks|]>acj'?p]Fo͹y?/۝l<\{<|uG&9t#y#yj3}hZ~7>~陾wp]_}j_TLT=5\GŒc{tz75u{Ư1O65>駟9 Ϻ~mKV4%;xΡʃufbϠng7L4Ӷn9koG1s(=_8亪<֞ݫ>|ߦj­zkfs\[фvigWS^'gk:q4&GFtBn2_fj [%*f>Ncߙb54KKhџ1~WK%3~U{=}c7>wk瑏|E{}/,/zvؖ:4 :;;>groogk^nIk1}OyЉ^t% r$EnGIlF@?D_?zlϩ{陾x8AvGWgMT p ڃo8w?;_hd+ha?OlOy/N'=bW3ȊGZbI>'N~ɠ|f5~ϳykw|<5@}>kh!ҳW;˾u5w7.cZg_9&y~gksLn;X=1cG?kLܫcXǝIO3wәI'{z^DmYsFyjQ)ϞI>2gd?:%[q?Lx 1{ng9ܭUaw\5חsvʟ5N9|b3vsھwky;~_3T~y@zki~Yb+N1 kXP.*+L5_qѡ> Osя~/b1>,E|"}]gX8μD08kniokwyAbYh>ݏr rq]{ɍȑ<ɵzCr&w'74#}j~Y_8fcϭ]\.S? cxkst>{hkcaR1@5d=/˻=]jasqPLrK?Ÿ+s-q/?~h>z& R0/jzP=c;h> h'L9r{~)sUs4|B %Kty}W b&ڟi19N=~ŪGQڲnm‹>Ĉrgg9r%qA[>Ԗz1:Mj侎N#[[rsy6vs`fo=3{ȥ y#yjww'*gDI-{!h| <| p !<3\kgoXz>o/h ٻ뵃'|jW< !]Lhf <˴sKؘ)؋uU6QLR`{ഉymyδhg͏㧝{{uqk~a]WrYǍs91kZmDG^k:Z98aΧ]u1{gx?<|ih(p g&C8&:{sNKT=;kN|"-{6yov޻ S~}d[6C9!3f\ßu :WTOҞd2'y6ڌ:3{vt\ם{?Þ{bǣ׶=q{uRgٽqsp\?gyE77Ϙc5>r?9 ڣeOޓyzКg{E:n>2>^Y6ԫszͧX2Щ~ֽ2ƴZogɃ%(荞lm2ǯf=|uNs{㟟s]6<}ﭥB&kz.c[\*},햃՗f.+>=5ns1G _2A9]?yt'>q['}_J|mχ%nϚ1ΰ_km<ܖ{ ߍs<_fnkZҾPs ;Tlߝ3gא[#Tz?y>~艾 HJAx?OW]x;=>^~;>x{!{C씽[ߵf=,iG~39mNRL?fj(d\x|׹qWV>uwzN}Gk]m?* 1{9:Ĝ׬kzzM{)F|Y0<˚|q8j3j# 9vp4n[{\|'^癿lTӢ-׷^羖seZ_GErxw/軺jq~'I/>\[ ƻ]ﳸZ!M *#/}JI'#z==g9\z'O#x+?:p M͇n/?F{lݎ^+egzcd{nD~h(Rj-}#P4ypsy=|lڼY/bu=s9NigƼsOߪ#suQbrk^AO _7h^==s̳3[k*[eT=^វ|ʧ5uA{R\;W~/L%k~mkL^õm]^)ʇ7_.7g6ɾ8>v/--Kv׉ u+}zCYYL?>NR[WjV-&_n 2uXW?x: țݘt;w-ZNS8[ϰ^Lx:kzroT6S^ze+/m|K.: gѐ/97?== b~/}L<| hC?pOp_poppO8ţ~gū52}|Ƞ%igv/;3j9^ɻ9#@^_A?>#~roy4;~ٺ"|e{tϟťk^zgΚ9<4w>̿O>>8{?74tw9}?VzaγɣiGg6p[H{{j={{f=[p[wmnhg נ:44+b?l>in>$,a7o.K-Om5MqsΈyx ÞU?dCj;cEnUoXOrd#n#e/y}}Qw~?$u`x{{Q=U* \/ў-}{87#r_~?VG>yTnn7?i܋\=Y#YN@S>ՔTNL&w} }ћѬgww<>^জ<ܫ?8Gg5ӹoOCYc=#'2BKΊYg{vPӇTg\ܛ ~vQׇ~Oy)Ѓ.ӪKs] ,&9v~3y;} }w\;Goѿk.NnwWgd8Qپ@q/nf]<r;;bO=/Od.YQ=왼7;gά7ؙb}/I_QVz.67~6Xiׇ޵NjQ7ko}x:׹5ϜQcny>nAc{bpÚo^M7795 g9 dԳ*|>[Y<&`}By^mGstY{g@VwClZ}L|AS>;󯘢vɮz5bWAizwfhb/:#k>_T|<(ȿ_gC'ls=lh?| ?hоxɺ0w[Ά:n~ymjĂG[?AS +e':xPVb,n|Ⱦ5֏;3n/;{'*hqo `o_T,?s=.R_>Cs:k4ycmo^pSF:J.su1y@S1C{|,jt@6=OZX0(nR$h_u3vA#1k C]>Kl&}hZiOo;ZFS>.Srs_#ˇry=0?~) WPK_JFsXKxiWXjojϞcSY(=ZFW(\ɗɛ]O1C_AIgw| ~ poh?8D#\j2 L6=_쁌'a/~ؑ~#k.';Cc;g/d_l/x@'?S `s8_j^ǹi[37׍= ИqgOwg舖έy2{9kMJӺc!lˬ\r!9תY;ON:2sYs\Nݾ5ll/kDFZğ z,3r=۫.q\#s =|z"I_ɕÞ-Q'Nc(GjGk|e3L2y8γ:ۧWGa;gCQ-ޛC^N,mkcW:F_u{^~|7$ۉƀrEny%_r&or'h/?=VIKM}x x#|]Eip p=.z{;A{bWL7vȝ<%dd-ˎ5?=?.7<gZ_l~W3Ss&Dsד|!?ڭu6::=sN2ƹZk{nz|.ͬs;_}wg=kN6'ړ8?Ժxu(?d.ɨ9Xdoyv܃0wn>6W54 kmcmͺɹޝW~gws|\M/F[O7ȁV M}|^1gqgxߋAFImE޳ٴ%?ěZ+y^ޫ=d~F>?]<8>_LywF3-z)lXVW\W||mMrF|]G&|vb~f/U' U[cq׷N[yFE͕Ly|] ȉz39'/97WcCOEoGIKMp- r=`} ~;{.On] ;cogc~Ϟ\z /Ƚ3_ |P|Tu.{:73>_G~k9ϳ\fȟun@9O|ƙrG ?-ib|j#yU1 YX~LhյH,#FA_o/1 ~P~V0/cxFy,+KN'=I[Lc,SO=}$Y&ڝ[V1"#şF- 7m2yUVWY''"7#GmL:IzE_FDKM'7GWW)pd8/Zф?x{.⃽܁"dW#ꃝWd=yǴe=`70Pm<93[}\=8ɇ^m=~K|xܽns 4;N6q3[ǭkx T+ J*)4IEZZ!*c3ՠ  H)h2)cwyZ|s>{}}ы^t2ۥ.u==K\Ϲs?s{;߹molozӛ^n~׼5ۿۿmox{oKۗ%~p>?pU>>{{EW4E^4˿;-@k4x^ߗ/Wާq]n?;񎽽7^}FC7>6>c1hϏggϔ{mLxַuse/׼>cПd/7y3׺ֵ&|<7Xpk\co.Wۯo+/iNwv[b/'>q{򓟼Uڮvm__}Nn??=ۼխnwp_u?'';~?ƄvcC0>?|{_=Y>C?tV~xGG//wݶg>__aKph WҕvG|ۘW:<³߾wDy<}pxބn}鿿?W71.;C_6h&~r r!r"/r#?4'FM'7Ev:>p p p p@˜ }7'zE}w=ڤSo/=:!zO GύaWgix.OlZ6.Yv0i?Q6hdF܆31Lc<8 0 <c࿌?|\/xV;g:t+Z0 =Z)=K;7y'=>h??#Y?/L4>-x+_yl`~Gs}_Wܮzի:otnWy׏qL{y^eoK_oY_!.}?wFmMk<oI7L''ow؏iq{{{;>>j]ﺷ~'r xӞnv>v~N7wۏ'N<~?#?r/xe_eu{1y̎oo^mOxޱG?zO>ö__n|4~<#c%e#7^;[Lz g,xasp7z&gXf$ 5Mўv%S["xѐ~sGWr!r"/r#?r$Or%_r&or'88}O6 gwG3<%|)~ؘ]~'}7X_xI3~H/CO+#ѧOkcA?Cr!gmLoslY6*xd˦ΖM_]_~gU촶WO _Ɵߊ1iӁ|:sSV_GyN;:ZOxA~& 9qm18y\?A ~y9xygot/mCƫ :}vosbB8rm3R5t!z_}f#DkmnY0n? g~gn__eo.sl/y~v~`՟/3>3vl=OpqAA{qȘ^;y◸=ғ~;'/{vZYY;] 9Ȝ2tW;pbh>9&],Dosqg#_t9N|r r!r"/r#?r$Or%_r&or'8[6FA 58W8ksc{@/zIoC2&4=,D?)}yc&r7^{ig޸gQKt{Nn3ٌfLޜO`CcM:mgZgF:Yc}=ߌCK?\&r0g?L' y' '}kP (?dp6LN}1C^ۀ}MJu-6&ۃh^ۊVs^~d|{`b'.i>vhysg?;}'O)/__1l =Oq5ͯ˿{]rl Xot`|-<@O:dg ^j}'~7)Oy,ۭor5k O~[#?g 1}*ٕC*߯#G ק}<3gΤ?-2j\|)3g^gw' 1"7#GԆəwp`88'/87NnY O@3=zCc~3v9@9raН_&^dTml+?6k>rk3OoXɦߏη5n.U}Xrs#g?^?^njg~|?kk>~ǣL`7#| [a2,NY2&we?kڄ'|EQ[K4^O7}9l2)V@ 42CGs6=dhݑܤGlbۿ@Awh<"ϒ|=f3VԯXy]klZ*|!ӯXȺ0:'pg_A{b|͆q/)){V>Z^s]U}WG)lzvև{wy̹G~s)Ԭw^L1k_6'['^kw;y yȕ|O]panWYx;Cx|)-1<5|k5z,=ze:!}?^#ِ-{S[~6ߍﭭgqz=oQk#';zV]&;]&^7ۯ47h]!??zu{p׸?Z g]?\;?_k"]W?"fP -O?䋗?5_טV?tVN׶Don]{/mS.o!jGtujwg IǛ/s]h0_Eǖ:0i{%;Fn]'Gk__W^؏ؾEٺG=Q{A}r[WRlfVC|Hj۸pyƒ؄35]J6uCOOC/Z?ƫj[oM.Vv+z&=k܃Rٚs/W4^|cq|o1G+3~;y 9N._x#x+3x;CxKD_u.c5zލ}p/'\g]?1Ozj\g%Y 嚶k`nvBM {6,MwkajcZlg ԉYggZk blkTXvjOug >>۹9qEḟ5Y"&Oz<~Es.3k@Z{%0@ɟa`ڿaSeNbkEӜ]GnRǛoy/y:Ӹ+;UsM:sݧ<~U՝?OE'+vO^kG&ߪ^oed^-*γio O~}jQɳu2X;d鲹a햛[zqGsmnO͙[6v]qvn\uO[=ϖElqE[iŰ>Yc0vi7?9GuD^<A!wx xq 5?8Gmç} n\O'z> EϚ+M=,>Z.eZW՗GvdGT~j}894{xW;^'耓Y'Z m`Vޓ4ՙZw`Y8ԙ}QwzDry8p??{9QE5oI֕$JWB0pPZgyx[1\S+Ae]5'۳?8n~N#_X]hj_ klxjg5F;d7]N q6bۥ}f֧閷6[\-|۸ݖΛxey˿|_G~˹b;]_7?99Wyj7<| <|!<%|)~۳SH? }7XMZo~z::GzQ|ܽ{ҜWTbدr>kBj6뗬CVgfxw36(lYob5ogͬ5/=uk;YyOT7߽?pGͅf?99'xbGn93d=sYIfa:s3Sf9j9&y T>\ɗɛ?,^~Hp_popa .Nn]{G}vQ{#≟}zIyy}U^lg=3 yZloιG?^({ʮ:Sfv_9sj6{ؙ{ Ym}k5a|oon׽S0W:g}3u?u=fM59@dN&r2Ck6jØ>|ĭm8d^:!hG=o?_4!JϚ7˃5vfg 6ͬu)4g[ }5kosu6Á1Y[g|+|;9G[@;&oo~C>uqݧ}ڧm??E5{fװ>EonSG0vz[Օ%μ#SugړIӯͧu_wt*[7IWk0"ӆ٦O|j->U=Uȫ}uH}r&o}?\ h }}Sx[cxkF; }+h|ˁX&NM[O?`Gؓdgw:W~-˳dڜ޸8 銣.P Zp4+u?9zLuZZ7uӯ[_g!3>g?uP|Q'3?̠2k2fUO֨qU:A.{BLJ/V~1Si A#ZLVklZ'>h6Aw=Kۓ:"C3v9;Ϙ Wwe+7jx 7/."tos Nj]^Weܾ?՞HM]5}rrljk6;;w\>!TUzq΋:hL犿^|8-8ћO՞Gq4B>D^F~HJLN>'7G+3xC !<%Z^p Ͼo8/m~􃞔^>_9[[]>ג0VtFXawvyڍ,U 1UgA<0XKG>dYen=c+h >LGkf~Z?=sN^o7khL9xw79羄&oww|ȹ'rC|oyT]j-oY״ˮ gjWsgL|k LszeϘ9@ov6xfAvF7;h+='3/k0c:.^ݕs^p仾vZշn{{k!b1=]pFv=wld7i ɧlʝ̼ţy}a=bG4=ݗon<|u ʇ59lZOX $SQϳ٧3nɃ\ȇEnGIKMFx#x+3x;CxKt) ~'嚫1G/3ˑ}kM#.=3zHv3؇Ύ@7;O@> ;{\g/.mck^_*8kQ2aWY}6^45Gk^y!h|wu^:`'1x |S3qMGL5?y=dso ޵td[G;6]1qõ6|c$wS.wq e״j4C0=4voOYgit xZ/B/~t5 W0=Ǝ ^fZs,lNU͌Z1@=5˳j`Hݶ=a-nq=_?|wݮñ:(~+w_g;[Q:װޖs̮׭ZqɣGϹv͵r/s}](5s>3oqt@tc;칙hOLmZ|Us$OO_o>yzf|{+7|WS= ڬ`?ʉ_vawvHOʳhbǪY@/;ޱ{;?Do=ŇRO7-Y3:U+xw͚ZϺ ;zY(w_a){XfvY1KK՗OrVpknYjaqϋ'cMЉ_U3_{93Lmxa䏗.;sr?8D[h<+FtTwAζ-:eg~iPIM磣ǻ8=]ֳs[+벏|#TU=lY_sX.Nqx7k/ks.Q=|Y߹o/a;=os<;c;k-:gwkӾPw@+Ƀ\ȧɍȑ<ɕ|əɝ>p?pOp_popphG?;|&}tYz^zڤxB1:D?ƃv<37f Ԧ_cq=3g|3'qܟR4?YGE3ߕsxRްwac|s2F/~ɉ <h&,Khe~,ܭ1:_{T7>tvRkJ͋ tsgM̩}g3W?}^?fb1wc&Vgα̜C9|`"!'"j9ȓ\ɗɛux x< :7[mh lOp=c~t>1=Ow ds%t}'A?;} #xn2fg?P{]w4gرxƌYTk Rw*9;|6vgj-gygkxT?u|Zw~gYgϳN?;1Ggs}{K\s/ȡ֬euۗ;YCF9Vga9}֗a-`g+yz牳V 졲WژЮ ԝ&׺A;v;s:Ggc [Ơm#/ùlj'6#Ky ;xY oggtp5s:'w[>Yg~uQG|ckއx<$?9OɍȑKz G@7xq0t3>_ݦ?f)Pszs蝈szLryzfZ _e_L43wDxլYio^o~{ >tֳv|k[]l $c{gq6'wZ_6ƞ*4!;c' {]Pw6c1 Ѡ}cQ7N7|!aݕ !]}N%{<5}\9^rӷsY|g.ͺ>k>~pvV;kn9AZØ6yN99'/97څ0􉦗.8GO8W58F}}ڢ'h7]^7?zhG3\hx;w&#gW/clʁ!{n_s2~wV#YK=ksH0ٓBp>ցysf3|t|Zﶾu@x;xEg`O\{(?hQu.4y1g~?~s`_ocsy}5S,o-k30}G;V vo,cS8Y&+Ȼ|kqf`z?~u3:ђZǽ+ާ't\Ԭwz擟gv܋y3_\>u}ӆ9l9=ϸ:+|w' !'"7y+v{Z(gۚ6Vt6p/_8go87FzMzA?Cүh<[cGNѡ^n_'Jfo#vI{{;ٳ sv/}KR YݹOk X83ykx};@䢏bֻݏr_,\;g|@]Ca8/c>(?0sgg{gegVgŬp/nEe7O tVQ^zc0fѝj˻yܵ^:*5Uf#uj=KgɻOj6{̼G՚-MO|w=SŞW>ObS)/bAugTo~ 9g 4 B;ƄjI-XO<;Oۓ:zק-:wf`ιkmsqV^bpl_vWCJB>D^G6ȓ\7{ڏYOg~ȳp_poppڄO8WE3\7 _ڣzcL>+3F~G/'=5NzK1}wzo{0jB^]ig3Nib:w^{yygwd//kz/gz\gYҭ܇_>p5xG{yaOsWaܰm>4yqj.,{bg1dOz#B۵sK-@;-`;Mc_C@>=CZ睲eormI/uVry9cE o[}x9lv^oΊ'*yQq.<0V'hguw}_:\}PG̽skGz5fobwbS>X}g%du;/Ӛp֙JɧUߓ3y65| <|ppp p p g^5z=O~:s~%4Jo/=.F~=o]]`:p?;žs ;]BKgȄc:by0;Sњ9p<π#pyY}Y>6xg='}9+_?sҏ5FHnɋ|̳޿*cǘYS,G .w̳V?/wV2vվܗyϖ?[bk{]ӗeL-6Q# dg*g܋hb:fl3R2oe 8KV<{cyk:{ϯ<ݿw5 ?6.ϳrTNǘF69ItZCeSЂ/dybcb7ZlKf6\<@w5ܶY_]̽S}7SO{Z:739Αēɧb|<[úgBGȗwnx#x+3x;CxKSx[cx <=@ ЏzC] @3vH/'=5vzK1}/Mύޓ;^v_ 7.t#gL?__5k_?ٞeOޥ٧g.%|h)_F~F_zpMϮy_swrh_5zYْGΘY::?39:ܯߏ|=ZNj.h+ڤ쁘xj;kxFOQ>m1Cl{z箘HDg&)/jp<9Aypb AOclw }C7^=ux>zN=:]@ ;ّb %{d#ߵio 3==v2gbg,|5};{>ϛ;󝷭Qﺫ5a}M?<}q=Y:7yr={񡟹ߡw@~1xȢwG_?H>MWdn%p~+9+)<ϫ׽hk54潝7?g,5c0fIGz ~kw躏M~qv'aw]OF3좜!=M ߰vyO6NEx';y{{{6o5vSR{G S{N< 3̬4=sp\mw)'s0w|f]?nzOmӗ;kw N>MϤ D.CNUɓ\ɗJppp/ GWgw <OcA>O{w19>k}޷^t菝@g#Iߧln_3_^3Pz{Yyˎo9Ѧ6@Ї| zͬCgmKst1O?k^=뀦9߹u_;éͮz΂ŇY1k$Z,}\{09\>-uog yr)TJ*/`_ ue};3yLso9 g3æ$zvgS{JoϏܩhƙ{L:(7z:S^-+a#Btw=F}۷=9-˶wܨ_߱uxdI;g?#\'+/3\7;?=~BoL2~};cz~9}=v=y+F9m+u~Y2[Ts~rbOWNc=~lI7;}ceMWFw'SZ̒ޅt(p缠9`=CdS{sYsg38;t>ZؘөlŤ7[.tɅ|ȉ/97?\ : K_wa>^~o8w 4zDj _Әަ7=v=`zWu^z];P9/%; r8;5ָTsNkus}oSa o|Zk @9[Oǧ<)guzYќc_s=@y:H6a@Ƴ>Ob@S?ou/{%R[ڜg i*sUm~g)i-ofݮ耥dc-!&pNW;C>ٱr_?e$[ˇSx*qb0rfC4 ZԶSt}7}ӎ}r?\{{NwPai3{3l:d||Ϝ}{ڏQ;~ԛSGϜZ#X?f?IϏ.)ZSȭ;Ct u=y+3y;7=x.`GzBcءrᔽ2=j&'vZ5<8j\3,?3^yέs?k+/{ֻ5#2[GkS<.ϭks}NY9x:7y֙k$Y7Yskf`ʿ)١rKoWɸ3h:'\U[ZhA[y h<vΗq=/ȿ-{g}5XW^qܞE(z_\o}s h_q/j}Ƥ|w;?s/>wywnfTkf$c?}3&Yg?XCs_gͳw Mv}sOAXmj6O{>Ar#Wױփv?O~q[,?]ՠzSkz3ƟݼbN](>݉/IWvvI9W3;O|יxҙF9N3y;랁x#x+3!<%|)~Sm|ùkp;z';K{Mz7$Ԯ{_zL~w~-raձ+Mͱ:r}V5V|sw~}̘jӚYg8>kw>p>ua־M>Zm3< W^>=G{̾y39C4'ƒt<#Y4S9uSxܫueʼyњ_ Lzp=}b>Z A'ͻ;t,Wskm,zjNv/{`n޹cbom%$l'ثhg\ӆ}Q^}t ;UFckq-V.ZF]gK,6saی ߍɸKqmdZ~|>[.sq'LL{yU7quG}nHd\̛z}]@B>D~HJLNppp/y ௺Pl ^;SCVmk^uf}w~Kiޢv=O<؃'wofݽkB~Z_qPU7>cg~~8 Yo@9yZw>9Sf9m泳/t}yN?Hg]g8U4yΉyʃw|J뻍j5\LWc%&OG-:s-AfMaӯioSQnT'ojgg+?Oٯ/\=%::Ab6vK kdWw^ccc35nXoXUTll>{|.杹‰0ֻ}a.901k׮zk_CkЌm;S-ʫ9̥ٿȁ3\O͍z5ӗ5&}Zm>}MSΏhg%;ڸտ:oeeO6KG 3mSk5WiSO#;ݵh}՛9gVs|ziSx ԿTP* S|(Sɔ!+Qq2REQV4`! &!h@)27JE[9Zz{5{_{_:\υ.tӿ{ֿ:uG^:9uk?s,{s _}h]"9lv>n?ښcs.zыnm|spl|?g}}O==\bvkц]s]״qK\p_|>l|'};]m)m{߾OmOmɟ D7cod߮O~ĵOO&V_lsO.8g{4s_Oz c\9wx > 6|omgw6fxo8w)N61ysM?صlq>y^tx>LM?3NMtԩ{xv`j{V[0[mI6g&̶ڻ޳'}=SmjÎѼ>&N[77Ӵg՞XOxG{h q~Խ{r8mlS{ڢp6j3e=F|x{߻luԥ.u}__Ooxәxx{sZ^=|g|6mhpny[zx׻޵3?3kwc:|g??.}K|+om o99??6_w݇׽u_\*۵9\//k^[:\jW;rFKt6lo|]]טap = كH"6LOƑLt- pxr=U7g􁾳~>'.'#x+3Ҝ^~| <ȅk䄼F~Iys%Zh3̭Hk z-[DϠ3{Gkt\? {4:uU!O=i5M;0ϴ~W[ټiVw-vvs_>sgyJgs]+-oc>u&'>Q?څQiÓ_cŽO~5?L]!חe6">z|&9WWC[H mýy|qͦ܇Uzo8 pә􄹼59-oٮ苶ӯiKK׸5hӫ5_5pKK׻o~:kӁ/~y晇;7h󒗼pxk-߲׽u/x 猇n7oOt7tcȖ3INw2wLc15ݓ0>٦3ly4n~]?_Ƅppp/y .Nwk 6 FUTFڡ $]8>rN?z~p/} GieGwzHk^s:>q|,N1jSS&&0[9oM_0tL۟m3'}i~St;UWۯn?k!ӷ1uhu8c('NjEo}&LU4p>\'Vyu\gy?݋|,:|g<ʯʍڥSw__?_oo[[??Mn__mo{m>O7y>W==~pnÅx;n?y~l)l ǰ_ZIֳ7J_i{>גy>/*{>{qEa9 ӧOL_F~ 6+>7<88'/8#\'«~x} ȁdd|i;g\j׽hCn/9&hFɹz }@/Gs!>z}6c7=Oу42/z/4c XzZ{sob>gklN``=?+N0}>Ӟ>j[2 ^a揶~gܥÙgg]|? upYck,I_t|xm7㷾ybX 0p-ksknk);blZwAdCG=Mo9a__o0?7.LC\o-~qm_oommz׻nt{}w~;x蚝F34_/͢ lbMٳ-}ڰ5jG?}_ϱھ~w+)7ħR9,~U{k-&|/>'/>7?\xNnWgڂ;CxK-jy 䃜rC~̇<+Eț_)rI>=vh:9Ƛwrf6q/ЗGА^)Gu?JlBgcϦ,> lПKҫCNڣFb W?Ƃ̽5/y3=I_/C -ϱ7Ox[\/2/l)^#S̡[꫾jO/|ᕯ|F3O[ɰ~kkn,zl4g=Y7}7mV|2o:?Y^zSzxMl}~6]?[)Oy~߆?_GoOỿ7>g}x#_y) '<̽Og?=H?>ZvO~'9?'C3gmZރk^^_z ~+3~;<| <|>^~ z AN iϋ<+ܑ?r܌ -%Y䛜wrOS?=rZc;=DO8̃Z3&=h|x^=jN*!Sg7Ov@h6_${78awsOxg9G ׼>S->nth^C }wme=|u,\g'`!:Lx~x܋<(7JLK9u{TYܶVy2UU_~kK\_mOOLP{hsBozq\;7dT̐䓯7N3xN|z=JɭnuMnO_u_L͓>Da>??}H?n=iǽ׽qѳ~5|5m??؏mzC:??-<`noy[n}=1[EsFczUp2۳^cM[κH׾:an|׼L0Dz-c+ />7?\ q\ $|)- ppmȅ1Bn9"O|?y#wrI>]m2Lс|sN?syhz~6;hLGƁVA_[=ogA~fμb_>ܪtGGW=g̽fv&=,y63_CvwkcyuY/0W~껵{}Ah3;Nh,?)W5\Oy{S+< 5R1%c7 ֬h]ZX[hc|rɞdgb{Z{gɵ#Is}A߉iJE/z/֯9_ȶ+8Fڣ=ٟ٦6VQ#$ {h_nYӡ񖮥~W>v6އ>ؿ/} S`Y>K~K}?C!}1HxE^k H{=yzA'>JҳL{2Eo0@mcչiϙa;w^0̽g\^X x~j٧9p󬆾cg<@5@Ώ}~H _Gʋo_?^L :;sZgXW-ȉfS%po+z53pq3/6 g/Rӽ/Oc^bYwwnr$a>/1<4틺(ުW[;[t#8mt_~#y˿-^OCoPUG<N-|hϏoq[l\s{lrGwvœ̃OD't&tϷ_~c;cf`SS->iZ`cܿ_ۺwa^{5oΜ̘\'t:?_X;3~;<| 3<|!<%|©y-1<5|9'@.9!/y+NDȟGrI>--?G;#WGý(A'b (EcY,Gz^OGSz~\U9'9قT?z_8}N}urkټ5}̿ЏM;qTǧhu.h=Ooygwx x#x+3x;}pj^_8NZ»q?9 |dy"W ]țqKs#!g4t9'8^0zBh~zܲxI;ii>Y{^:<ֲҟSkg/+*zZ6=r{zpJࢺ&66|طrШ]p{3Ovxڿu/~sk]Ժν5J~y{g'-\>DߵpgpݙΑg?׸>ۺajsk]gz:{ߺ;f dؗ$ou6=CXCx_~/vv9EO<1n{F2WC!C_W,~'?yQtGk}Ky˼|7 <|!<%|)m#xksxɁ y!7I#N%$Nn/9&Eɹ9j^@z+_>lGkK_.Q{AWw>fƝ.sNgL`]|dns~uʟW}ϝG\ \w >xksx{ߓ@.F!7#Dz'#$A^-%z\?Ѐ^ t7O'_3FK+z|OGa,/GK{x˫^y1a_V @s}Ͻb{vx>ś~~:{}gd{k-eF6`ɜyz\Wxs2ϼtLH8 V='{SFlu pl_`yW0uԼᅫok,Z -^}c~+?Ikn< WgwMSx[cxksc{צ 'ܘ9"OJ{䌼\OrjcL79'ޜ^@o߽I{zAMoe Gуa_;KC:Q}-rY{=ycA7#j=CSε{r׽ޏbWozAL{xڿGyzO@}:5nzU;E y3îh?{7{!w~1ܿ_re!{?sah [!}V?o_3wg[V}-gXkPAb|~ }B8]D7Љxb|WauHׯ=TcBC=t{Б=9V#~4vcW{:=ߛG5Oя^V8A{po@CylHxJcYGSXӧ'-mLMɶN{U8Yҫ\3F0 =5g9;~/3:LGth3zO>>'/>7㿿څ{H/+}S>+#\V ^X}p< mc'7Ǹ2&rFCȡy|Eё\N΍ܣ1=FB扦Ah^iEC{BАV:>{p@ i(}J{&=g/#_yg{ܿ?p9s?o|jgٿ={0k=-G95<P1a/:NXsS)03'ܮYk='4HtN?>Tҵ_|o|8hnѽ|"ƧbL CO>G^-#gܑ?k\3tc7msv@?GoNCϡ1Ǫ93P]i 穪Oh<?mOSf?;dgjg_c ~s>;5'4"]Q]ʺ?=c:bƌ9hcM۳ƃ\m~qD{xB 4O<+co-fu9r]t7Gٞةv5g>a};Wڞ܃טk5gڿHj03;s ͟vϼ9i3׺\a10tG|||/|?|O|_|o|.Nn GW#ίY1<^{7\crDzWZ{Wo}λeѦuIrg??K晃Ō=d܃ jzh=-f.tiޏd{__u-/jz'a=|gAkh2d=mk]:C>Ng<_|Yq;t4ZX/;>NwТ =m{ OqVDv"-|Sd k8*/Kn6Oy qC+v?%bM9Ux ݶ_Dy`rGKZNhz7{ m ya?s-~9]0]K)΃?_ppp~8'/87%|N8ZGlymr ȓ臜7r׻ɣ{=%K}G79f,^8#uA{oZ#~Pr%z8^[.O28ɗ:O/gZ>Awoe;ys|CgΦ_W{w~7sXSs_{8fg_hǺo4e1C|_Oq|Yk.Y=jfls@w}ϖCCЪdrS{SFJu\[0OrD\g#`B9yY^fk/zp?}~ޡ!}G+ޠCj\fS5Ыg3TZÖq $:擓Ydͽ8]5AF9tښOu0{,}AYZ~ֶb;zZ{?ZqYU6T_j?>+UoM{|owOQ=_Ǟ0}􊱤}gpͼ5޵__?3?c=7 jc>[۳0v̚sC9O_O.Ǧ{ ~ cpp/p?p/87c?8G4V8Wc_8gw5ᾳY|Ώ9^u${A~s|b Aa;Og-ڿZ3ţYKU?3==k{ր73?3ךsseͩI=8Xctp=޽=̇hmk=40D/~lztOwKiKRrKa)#gVYd /EU Oghuېkpkfn:s>%~wYnim^|gM0fL{O_~=W ez`? {3>O?3p'ߧ?gֽcOq?tN=?Y=mS_x xm<|p p p p 8yG}oN)#s'>gyg~}߆1\t;[;= gCm5}3[I ilqz-&_agS]k^13ژqߟxEUZ\L̳.}=ګ.wB/Sl}W|_|o|x#m|%O8W_8go8w 8ȃ 'mCMoI.rm9φ=@S/ jz.Ooy+3yz;~=Z|cY!LOq:3{fjdk:o^ɬӻoқO9.t߳Z'L{Cޭ^ i绐g4:fz /3ڣuuup`/%½uuskO~٬'0ݷ}sg=-sMҜ|j)$}? ǽ+_˪ϖt+daw>jkfb^=[﮵:?Zg._Si[Ui$^Lc,'cy@u:C1&m[/1/]v_/~\ [G90Ku{ {al>s#{,[/~{ةwi5Kyt8N;o+WW>3Jx}-ފv޷"nNvp>y۽5>_Q цP>$G jΛO{/N6M_eL|k]fluܬAT>ll6wZYlz+}kހyi>Z4=5icCotG||I[Y;><| M܅'/8s Q>xu1<5|9}F> y!7:XAYy~S[;#(5eyy8f|Ug7AA)٪ާ1}M_қ'ѧ??L?[{_Ku.H,|֛_ug{ݧ?7!{1/;<@?=uMl<}  _L&;z_ԧk}Kn ;+i:Xoo|Z{&_uf>E{ATɎO{<-G矪}׫ }׼:>겉7 w󝷘{{/0]?-eV{zxۑo~q8bm^x&l]2Xg:!2>z`+_xT|)oΟ{Ik+^21: ZWk)L>yw =iSеw5?]ew<>^~wTܙ#\§_cõ93^AN>Re]c~_ݻ;{!ɇi?<_%ٟ~5]g"V_-i<`wgYa֬?)vU}rKgݪk knby"Jg6b2~T`X_ |.V~i<V~H{p_7?8G'+/3\÷6FN]%GA̓;1Nrh>HNksXYXݞ>1w/v4fú:&t;{3r{1߼;l`5,o3Dϼpv&hhk[QB"^<|{j57;G1n CoiT__4fa-]Ӂ?guNQw qCg~GgqA5;Pug>__YՊAxs]ѥa~лsEXON:\[w~b|&φ3:zg3yϡnHgϽ/co ^V7]Sޝx/87NnY O0fr6+U`rG!|S|+*W<*Gc)W8n]g>?w.uGgnjK/9r:c9̳j/hg_'5`?\P^!ݑ.Wa#/'@뚬C@=?skG֌zVlGw(;_3E0xy1n<.>jћRdWZb%۳7^"9Ǎ^)zy{F\W>7ɘBOp7.C|632DϘ{z5B'^;1e{]+^}8OkK/_#_rEONE6&ݵ/'7Gko|8cMJ;Cx̊n o8wr@s!''7'r7E,F;g~< 67ޙ_̝kZ>гgHnYiu9߽w{I=t+}gfyn wϾ{EsA46krޙ|VրlγV+Yc|Wok 3UU쿘bn|ڡcOQ,uU gCf!]$h%Ji7qS}:*bt.S_Sml{9=s6ܪ\|m6 zȞIGr[d՗^̥:I{g{偵/Tl;Lo ow."Yq~|S|W1bM}?>>>'/>7?\K>rÙ>^plNp pprA>Гc\jO;gjj#n|DK!|6}Ao/Ptq+{lt;wN;9ao-o\7 ֹd̝ɽ<}ÊZ;?Տ]hwT#tO~>O,6r~378gG9`_ƌ>7?\ ܸn,Hx[cxk6x{'&LN !?<+rGܣMrI>O^цcL/~׺ֵm%7Z|_=E/:/=|t]p׻uo~x3k/ppxӟ6'?k_ڇ\2>Gy{n2hodL׿~<9yC>JWᱏ}Az&Gq8׹u~xk_t|M%~ō?l3yH/dxzc%[>?=5wE&p3l׸w~w8Kf+]p9}NI 3s4|~#~+3~;@.9!/#Dȗ:?rH`%dj}3^s+ΰ7d?̓?W.g#'GDuO4ۚ]t-Zy1WW`|o}s61qk^p_p _xo/yK~9𙝸nwծɠ7n7{v\ezֳp+yO~pbRFk^G03fk~׽e/;=7^tDOtE_tFot7V0>>'/>7?9 p-9!/^<+Ef#C~H5&{@/OBo^ =oয়ƣMOl|w;v@wM3dwfL]FWVC{7촋oqմW8z"̝O10I7ȪߚoΕ7.:g:03;1?nk =?]y}?t1pp>`zaMFFWV7ޠE0럹`|6ߌ̕xJV/B=̆|$pmo1G1 %^@;cӋ<[}o|7q#WE ao_Wlq]ڛf[,fÅ-oƲeltu{-+[<1N>lڈ_mMn_x3ȿۿmp> Ow6>IOl=ym`a;omqu?)f7+_F׾`E.'pW,}65[̙"_!oh6 cL'N?=~wDNFڡE1}xJϧ޳B?![a~op+ΰ7lgڰS!y\_c^d3ODw<1hkwgc#&L{׏'O8',?L<,o=FȆ7uY5&'6_3>ßJ_&kxLg=Npbݘ[_t=5G/1/oc5r巘h62i-GQկ~~] "bc\-lqeJ^a6M/8_WȅnbGv>Wqus'Wwݶ{ԧo;ΛM}>p9>n˫e|Ɠ'/\`Ecx` )Ύ'hhÎɿ']~Xd;k~_Ds\kk]C @/tC?t]͏1_w5|NC/9#o`$2܂g^^=/H葹|JCkH/'=5?܌C3]wN?;`\v}@ceG p3MװG٩Ϟ}懗eWOޏ&O`4[k w2{81 ~+^'50a?Zg >O$-k Flx|/'wy'=W9*nk{o>p/:G|X=ck &4t-0xGwh''ۚxc|9FLl Vc`0O n_'{r_"~Jh-Gj &pe{m~>=/qKlzb,ApAw6_sw}>E%ol@y)N{At=@/t3?:'/:7?> ̍oGWgw'\rB^ 1y"W'gܑ?t!})y`2>9'p%Ơ7>=/hE}2 zG%t؜Vzps=>nMv /vȵ?lؽ#;Yi"塡?=~}2Xf^X9<{>ev`7"_W sGgM ?co\uדּ#xjl=i^,/oNFZ|x533s4YWIЮD|ݠnj<G÷rjz`&gW^ {`m/?6drDp[z9:>Oܭj&6jBOU7@|W툱%$ۭ'yYoyu}7Oqa{~gTڹF= =}?_f~0_u䃜rFrDGk~rG!y $/'nwO:zA? z%G_ 5}5W5<]}9>`7Bvflϳnd*_;"|Ds u\ϵ< CϺi?z]}$:NYYkHq=C2kƼisdɁqgi<ס5doνe/ktcv^{[-G-r#::׼5'sHθt>m1N 䄼<<+E#<|S8܁ūXr`շ8'O=3wL/'=p^G>haO3C]^]bk>ߵxvΘ7]WZe+gsͳXkڳ`oYsγ~gXo*7[c5c֐ϼVNA878Ĉt oY^~?WŸLۡOusՓ)KqY>鴍ًi_}g[cfU_co>@A'*>0z? ?۫g;@.9!/$?<+FAɣ'95GK3&hH}7+GwzCQgC|:{@_-u->Frg`hxv?^`;`n ѶQgEc _yИ';?\xgÎ?֟ӿ^U%]{{otkaͷC!oO?Ewj;f~Z_k57: C,zG}`?٫S#&Xi<:B?ɷuO\` ?kXm:6M/xzJ5 ('*;:W{hU]hYu\r;qWzd!>ۙ-s^[α\O}K^rqn?ǝ3|cGO[y>apsl,OTa3?KϺ2q:3ߖ oz>>:V3z/OoGW-Yu@.ONȋɏ"_%o|SJn/zgr Wwrw6sKlI>:zJ_;S7={m0{ЋaO89.>oo^:sQQvsrKxޒ,sٹir37sg|~3ӿ9_cf[yݏ87͎}po}ͧTQM3HᶷΊ9uMkIアKYs:11't^:

դ[dй%'7tzrA>I=ȏ"_&or/썽ƣk^zݧ=jz?#gŭo}cG;juFk}b~L|V4B'YIX~=l\֔Asg7;9drmڗMO쳱}|j,ʽG:S1c/W[OUjSuŽ<"Ѥܞ\9AW3|zUq bnh 5Mn8KnQ`UM4b3ƖNΆٙg0\7?p1[w;iˉ:g0",>l/k,wb?'{XNm^Upg ʹkzN?ҵ l@6)|\go:oU=CA'B7CGǏ1;'7GW5 FcrB^ !G\/rFy4$>sN?=݃ {:iri^Éww={؉;>v}jPv? |KBWgpkU?;y?_G_Oh`%km܌!9*?:/ud\tC?tDOtEg/?>O Lgsڸ_g~S~rEy#w <ܚgrM98Ƚ9}=WsRb4Nn^JoQnw*S O'Jvǽy1]cfVh vҸ3F*F{oֶ_^r>KGy4\|50{K~rf X!:ϳ{5`΅̞͋w:>~=T{sdLcuǘlrМ"W5]VY&S1eOGc v]LIk>x2/GojNy򎝿6v*۾^8O -{6U,[ۗs}NЬ;&YM#X.bDvetZDoEIx^ p ;9"O#g͘p!\rF^gorNG:wTi('`o>kGuA==`06Zn'٢1@2/oƨ>xٚ${Ypk]8.} ݜ{?Q__9uz/>llډƵ`b͓O}}cGwǯʽZǙ yBkLAOfПrku($t6wUW/θS<{^1@/>'N?544qѱ.br]}P2ھ\wsM0`YgOj[(pYocz zcU_pXw>'/>7\8'p/r.9$|SJn/9?`{OC=Q/GtOhX}@{٣|j}Ouw~Z|֢]OEe:_,B/>1|97Nvo/ í-yxϥy=[yr{{qԿ|k^š{0c{ {ϸa[:}ߘ'-e6EG;F5mg.cXWPΎ>TCP[Aj;ab^#4XJqH"M% pa"3Kg˽rwxvdĽhevg`AfcVׅڎ`7-0<~?(\-%]8Dd,q'\ϽnkK3T_ r=N:=p/xNhO=Х zk=}q94'7GWu~;@.X-khdyCCÏ|Sc[K_+z`,t􄾠q{WWK‡t<~C x{fGmް;pAeR>geu^fs_pUÛW>r#=>?kf\>}>lg]QNwO1&jrkcx ِy6t}|trX3VX @u(1nHZ'6f2.5Ϸvi^Y=stD\S/@r:j[-XZg,y=+˃Jgwзj|dmn\x.GZ+glzN>.zCxީ-C$c9{p匽 uRlZsqEDovoLvxZ/`EKrBuժ'0u2M;sTZ/^'?fގ) NOgY_סz ~舞芾n^e,||//GWgw'%O{Ƈ"gp$wGrI>)M~1y&歷IкgdГ?>+U,AZk٣}X<=>A5i!dWZw!i=??޲]Ի\ѣJNovܘ ?gߊg??^q_\g?xy>?9jm1_s y1ggzWpo8rB\tŜdΚ\{|eg{ {բcZ]G/3}a8;o]L>}gml5={˻XV?c󩇒 tfc?7cMqCl>_:_?;-v%^=j ;,,znmyOǂĵa'|F{[e g/Պgʣ\ E\*<{e#ݺgdɳny٬IAS==իk||kat1:]{F.}|/|ÿOg;@.ONKz֙N/0'{'|S0[V\wr^z#>t)g.NvK؝)nwkОaKT~ {6죱z)3W,ݞ<߳ʣC.Gb/ڼfߡ3ŋg_էٟT=䉣hGASm_cgO`G{ޱ<-qzh]br15^d@][7ڑ {78S5WlzV?zE-TR1Ikgi_>p{Y5ojQxw4@ף::'FcFw/|h?3|G+3~;' | 7éΥ_]?B ܢ9&V3HNh_zd>|_隱=f 'x7:Ӵs{^Aup!+xZÎU\Oxi-2ؚ՚GƮ93X>g(χL_gӇ|æ1tt=Ե_>}=X~l|:a%;+Y >gg~37fOY>' |sxNY 8Ӎ@g%vN`1P3t;Q?p栗p ~/uOk!tC?t=/:7ѳ Lra'Gish5Sj|xd|s6c@/p'W4=s3?=_x{H_n۝# 8ءEiصfoꢌ7}egoә+0[oYWrQ 3{{k_O2ϥ??=loX{p=1Z ><')o}"Wg؎]u =:ce\0S֯I7uroɒ׬_޷ZCyj4oY\l3*.u,IIәXE{$սjE9yo9<(}ACׁ ̸hNU/Ϙ9>t]mAKMvNz}z׽tcǪorh׹(|DGv=CxEp@{g?9|kk=s3qűVG=X{}ko/왴o\'gs mo`n([NvQ֌=rF(+'\fG YKumouyoynu'qzT=d} yн~މqx+ڨbK{leL^\#武g3p#Vmal,oՈy ;Vx&~\1:tތm/m?u/>.q9cq^3aǖ_K5=6׌Yzfiu׫3pT?'%7<я>Vg% +{{#^^+OOxXPkD|(r=#,YΨ>LzRu)(&k @u:˚Yt19+lyS̞{9Y׏`|s|؞kc|p?f@.SL'`D[שwG Mޙ7]gxeCu)sܳ2x}6M=s)C;3F D\ >5]| z*.#<s} dԬk0bǻҞM}G4^-o1x>y1xm:.> 艮z̺Wgc;w\r~rclLcg~Щgˑ[\|hA}8艹gO\GWoG+Gۺ|w~Lof\d0Q*4xgST%{zlutq]Z˵;oIg]]黦Ͽ ̧O{ܫȟ_u%D<1ǪQvWze0ՠW=g`U_Qm7y'h3wٹ|L|{{"[ u|fPn=.{[c0>). һ kOQߙٞBr4pu^ FWgP]Y>b9h{bd\8y ;$>c~sI'p M{|Pr TkξGa{+cs&'C莎lzœrh_!XL>eΞ.\F>u{^cc{ 3[.{8g5mLks!xM'xۋ^'| } +z+3z;~ SϬO|כ{w#D:a{2 x|N\Gr^-,Ls/_`y>R}Q?ֱz}#OgG[4^.uxh7{fo;N~gSrsYnv6 [ָQ\>7ߕߪ޲g A˼~mb/];g_?ZR΢yhw>6trQ:pHn#qn+4$Inw]cv6ۻ2ys\{%f֫\l4³ju:xHV)_{^ڳf+][y癈5j-S08^n9iYqՏ')1ؖz#,tlޘj_|tSx5cl1/2de z` K5~36/'Tff,d:]D{7k~pt|/^Zx]2d tDO__9zܚrOrB^ !G7?ș9 yv>^#\,0$uMG^-_p &;K<ϟZ#_6}tl tq:Ş}eg_>~]]{+V1Վg_^#ߢ{/7VQ޿w,{ߟ{3'l pT30-$Kk 庇~-,S35?;_~f$Ƿ\;9a ^ၷBxQF;ē7.V˶ [λ 5 :sK_*QyjWVޞb9Ź>hxGc<8{vg/N5zDWEgFw'볙60.y ''c/rF#kϦFg=ArN=7N5G 茆/ݛ^W :P->yG}%`1Xfٓ=k<<7=ݹgYƌvm=3zNI.֩eڟ땓w#W}FlC{KW;e<<X9g撇Qg_֊e־= h2ϠMS7ͺrcdz;S=gsMi gss\ܤ:Fss'-l{^>EL(f[lo\듊V.Nqd|hTZƛٟt`6s3-84.|U?YFtA|Bg ͇^:Fw7vK<>s=rA> y!7y+䲾4^=/GCEݽF?>+󂟾;ހ|Q.Y/>3+ai e:cY/*kgBXf3E3dYYϞWG7їCNj?v|ʷQyU`pn/pԹRf9MNv}d;lAs|9jŢ䜏3?%lCkzA24jqW;+V?%wAw>>rH@O ۃnmZ4=-Hw(/*l12Z^=_+g?X^ng?:: ЯD_tFj?e֔c=mMޞU ܐ'rEy#w(@.ɧz&6?B/У#_nҷY{3)}@~=y@/W̖å|C˟"S+76w_S/rVz&*]F⥹uuE{ůޮg]ڜ;j=w>[^qoPgg,k5 x{w` `r@k4̝{{g9So 9{G u=;;p:#:]@fy~٭Fc9]טCLsY|*bVm,@6VKlW岧ٹXZU'Rh~s.\>="c3ЗpnNg#ի[~h&_І}TswAftԉoj4WGn/O97ξ=pBo_t6ٱ;k}szan6z~8&\P1O»\ z zЋz ??~3xwW|,܋6OP}Ӵ+!;9:?{3e՞a4_i9zƬ1k V{KW9=?:s铮kǣjfa{ر9v{53W蹉xZ5zjfY9Fs={Tti>p> `;FU23q,o;3X/34?\l>=Y Ts+zպGXGgZ?~Ϻo 3v߭&|=Sj1?]=zӏN?g__>zFQ=n)f!c{ӬlMlQ5+_9`t,OUaޑ={y{1E[;]Z3߫Kjz7(N=/jg`5KN[(4O9^qgg rdeol2;vN&J=ڙA;?{2'SM/Ï U{V|*ќ J^mE_5|Q'O{9_1V#\|Yٛm7sE3_=g:c^󃣾xRgv^3Og?o>@A'B7c}=S ??_?|O/>ۡzAN !?>|MA!yk"u9FOi=[h<3VExǹ?czL}{h5xVv爵~#yT5ϽMna3<{TYr{=;|͞us=g=O_5sWsCfz#xGϼQ!!c`3&Xذaņ lxCB 2&眳9gяxFaꮮ_oۼo7{7雾ooFot{7|ٳgnq?oyn|?? cɛ}޷|˷۾onv{|.r38 __qOOۿGG߻w筵^u{}wzw^Xƴ??;'x=~u[;;g^~W~ۿw͚>>W{??韾>7G|G>> wZXǿ˿?>C?s|ȇ|}k1^|;N!Ͻu/o?c?v廾>>E_Eo=s_:e/{}ƞ//~Gns?soo_W>>;MZ?kww?G}~w<AAwqw/pߜx 'oVou3~~oW7N{1yu^p:ƴN|^8 .NB7CGDW3z=''|Ç_ gkV|wliu+kُ?~5u%& ABz :@|e-L.'/k9eYrֳ+cgπ~Of/݁|^yNpYd6Z =w<VO$n$WOй=^c;$x̟mO ]ƒW:| / Gz0}30vx{&#ާh .nn[؃`f_xc8]|F[߉/mO~~;?i{lo~6 ߳^{9ճ}{+_yۻ w`'_ɵگڝƦ?o)r;oe kNy=x6]dY%_%wYI~7|]=rs>snٟ}|?wõ`c'>^W߾ v@}G~}`2&\?ZO/w\>~ N0 joS߁# ^}mo7ys}%k|ϼx>pXz ?_>?xOxM~826>>Gs}` `Ljp/A{?v̍x~vc3{ A؄ܑ}koyyz_e_vMMym-qOpmN0wKEv|d}۷wbF4d<0.f_{k;l+8S5'3]jc>?Mgt_ْu0}|1.uXuZ| {7G+<>CtB/s]X֕cx#57|WSj>vo3M<pf_>;>c:½gC3r2#rH';'WLO' OS"[_鰅 <Ʒ6>SpwtE|WIW+_5_s`<^E3igy ݳUWYv&;gw_ 64^ڳ㌛7~csߝw;:mٻwn?-`'?+|6l-U2 @vgɱzBs;C9lFv֊F{q_wN׾?lv3/x/ |<g~gtb'__}\ѹMO¯Ɔ};GcOlN7I F_@~%y̗_'3:wܾcc8sX| wƄ+KO} stE_tFot/>H'Q|_ ?+q~ė+ſ?ks r){!^ yx^>l۟p95tW!?!z.L[yj>ܕZ[q}Wwu|x MOxٍ ?I<6~1G_/<0/7k sHo\9J30u''/w+vQ|Oxٞ%;Ǖ ab4{_Y=u O8E\5:dWLfW>0xC3sXy 7Z':آ|f`Y֕J7^-Zir;?;ݕ}X|z p?lSGWO9Qm+Sσ)~zAApHW- >bpxbA|{p6P^Tt@Ybi̙_:v.G;y3ٕOgIۿ#y%Q|(V? Yr&k,V ^?w| _w3;,G ^Npd ÷qϳAc9ϑo`>ď֍?~s|Ñ`_$73 Zٟp`<{o~/omW4!7Hgrv \?}`k{?)JKώ]i{{>V/纾G}>{{e}OέqWWeGw޷W~W>KHFx?Øo܊38N匭_bq hrw~o$_ڔ7Z_9 īPl{po,>C1Cr2 pjZqrs]܏|v􆛍e\b\Ƴyl4;0Nn|m-7>4odOW]]w[Ӌ'?ِ/]^07^B#ԳPx*J.=glQ!=c5Ag&zK~{r˓n^CSNOy++WoQ7V\ /o-&[^SxO1gX:՘ټn臎B_tb+XNe>:ϕK!~,&O+F|}s8pi!מ3nx3Ucگk=980q_A~# rZ!o_85VNnMԕ|rٯV]Wl8Pܿ_M_z1jҙܿ_O]_C?{Zcl.U@ux[P,3ڎG؜jNbP7,o.\jAg6 $rv@G>߳&vv'0l*r<<W^nX>~zTX1MG;|)^,7NطUB;-'Osc0=n:3.|<2:KrqwbYs_|~?8j nοt~Ϟj ʿ?j<<빭/ 벾 Xx/c<'o8Ytfĺ ̏芾~s?>\?n|g||<Ç։/'>ů8-~,VQf^ F)<[۾7i{ -s#8վ_Fr'*[ڟu0Oŧ76._mڙYf_Ug|v7:k}y/gb13p}۳i:1L=6*p:s;>OnNʾ7T*/e\sd岹A-_>d^2Ny~Ƴ;>Y|/eyJӜqJ6\y3a yr=c t"n%Na!bW/]-' . (ױZ|bUΗ1 (>Na~s6x̟&G5{&/zέE*ςp֑OsYba,'7+^c|XM\9q0#zk59.':ل/'>}8?kZsoT+c"̫)_O~w_m^غ7Fr)c;);mzgWSY;`u6 X_Dqy0`;wÇ˗֚c}LI=ųvj9K'AT +'ȍ#eF,R6{dSJn|tSgkrIO|.=xؘ-6_ggŮ%=پ{WuWk7W1Z\ӥgggoq?N#KY\q65]]䤿zz{fofUJ֭9}-&X?ݾO9tv>oct\%۪u@F|uc-t[#|+3|ׯj:Oyu[ftG|`π1C/X/>5s j"~1Ϫ7 x_Ӯ'k_}yP0{3`+/zOϕlϯzn,cgYڮzWuxɭ6oi}k`{mwVoC<ݪ:f{SU/Ofc7+`;|ٳɳ|08gg?}xXRyOXϲQwZcxo#|+ѡ`Og=ѵ~Bd^ߏ#bisJć|߁Y?`8./Ͼ?<}xLjlᰞa^3G9PΑ5k[_1L<xY򯑓3>걚~IGN^G6l} c>b?}{H=~9gg3?=*7ޡ>@oeP`*b3z go~b3n6VQ2ٛpf;a+wf{gwoOH>mmq㑯jλ\gWڼ~AfkZL&w';L<Ժט7oLseS+gha] %3>_}OS_$M}WU\ PmAx'<{EgYWxjh}N_|}u˳WzaqP>Pso O nf]WjZG77_~TQT} QEgFw7Fw6|}P>!_E;ZWg]kŧƨ?6>sONTo?ܝUѺ;E^3zJ CwL1laZsY<"* c}G7C椭.ڜfUmd{gޣY;m鳧[x3v}k֯Sy%vtWyN\h9{ꟲS~Qʾ䢟$zֶ]Gq9_#ռʳftWgcTCO>{%ݯR_8vnZ/Oؒ7x|l|.c擭&L.6T_:L.ƌdeDP5=W~v5GF\k*>wT[A=;kQ?lͭꝿo>~y?Nz+{!z+:C5jR/ /3>^]yc0k],I+]-_l~+?gKڅ׳~|r1D2quUE[Y~yoyu/Rzl_qSGf#_#򌗟u||+m}:G}z?k7VosYuxWߘDG#[r_Pyvr=MnU_}e~|Ȼbh[?N֐#gqTn1ckE˩HFJC}_?Vy|S ֩oB_ ;zbJO;:`%;ѩq_wĀ{Z'9YhSW13ے623աѥX_KW@wu uʃS}[Y ;"k㖛G WИn@G `_qz6`xx _2|;׿ʏF :W |Q~7}ܙ >'> ?k]x>߿>ߴ:GGenvWJ.46ȑVЅ)(ۿCKUէL&ڹ"[.Rޏϯ)wGg?VrκK;:YT/WiSWvժm~g?,eq`>yz?^vv};Ἴr]%Gɪbg@\Ʃ' P~xjRmo9B6_d\|ZhWjݳՇ,br=WKޕT7Ϛ}]V_Pn~,}{[ߩrW77]/b6&]ڽO2N맆vot/iw׿aoW;sVeW=ŏ_J*&P*yYΐwRև]|A9|3gߔQmb[?&Vax>ںݱ4|nN[lZlr{9QOj1Va#g3qrssk^qzc3W6+{X]?WR5Xovُ;}ydK6fz9 WvxXyYNtS 3"xt֠'΃~#JuP=(~,@&?}>jh>|j·n/>2|G mrS؁gkf-Q΀yJnawWP='_g'#Œ)/gɫ"z\Z1nEK/e~%`w0J?|g|UטO؜kTs´HƳf<CZGe4W;cÓ{^wU3px4uiB  30'YsX=;?}c]³[>z?n,_Vl'ϑ/ސmQ)W3uZ|Ѳ\Ψ罂+׊om_3~go_Oyq[NU>SWu{/gWw?srɇX~qr駣Iӝ'7xTݽwu ;]W^(>WLx㼍VՏu7 3t-[I{m~QN? }zj@ļ=+T{gxcωG~_XkFrV?)Zk9\u4gթfDS'o۟}seO:g[YucmdTt< Ʊ몷e1aUg*Q߁OxrG>d/Fo&3Fw6.O̟G _uGPJW&Wmt8>Ba7Owfw|}lSdf|llzvyRfƄ<ڸ'lKɭHvPkqS9WgMf\=nobgw;cϷg'=J廬J?Usw>œo%]9[_HO/om|փ>cX߷FbE:G}}U>Z=z;OFޔ _=>IGh;[C^xӘgLwp' '}S8[:HGso/X9Xuyb_R.Fy6VpimBUT.7N \Czr9޵bN=o2;~T9Z؜?9V7xt^g.܂rꋇ?{k$Cٿd#[Ozԛ+?F9)UcP `kNOsƴںs~sy}˞%H?~nz嵟=:ۆWcId\nrʐ+4nr'Zm]=ŦLvڌpR.f|.,\\ r 8:,x?yRNZ :+6fu{ukbܷ놇jݒ(Z|9I с]qﮄhjcY|Egj!cF #)Ya0Zs?øp`gpڪ}j7(s7{neѥjԫ۳/e'z%kF?Y`\k#osmgXxxg|{?s7_In-y7(?;#Y8<]]U칓(/Σ~Rq/ͯIZOq}s/Vw>a~[6cnUX1wrY8/O}Dz~tA3LT3~uXk1&c:;Uו϶[#r{Ey^%,/^nK=Ч*} (S/S#/Z8jt ߾?Jez/ڟ=w׹zM9Ԙf0|)0>OtSbZlwyvk'Ӆ3څi8kO?#XUߕNMgn;ϐ}[wǥ߻/LG7^:ck(6Zu[|^@O6$p~t'wjz|Vs["/kOmxѰ_Mu/*y ˕KKw{ؑ:ó:zV?5jqn͇u:sfc7ljK{Tb?{XF67v~}<{T.jJ }W7a|1ޣ=g~~|a}S/pzMIvtϵug_rs\Is+>.'|OgE>|/g/G!;edH93Wor\^ޞ|g)B>I:1S 9jtԙ+ڏɸŤCW8~Zn+n/Uu>'\,׊ V.uk_Sɩlj: _geW '\@yGljī97&Ίb5}x'zS_e*7"_nPk8&K^[Q76\g]lzYgL1OxPڂ h/l %̋? ~1bWݫSaqjSWo}Н{xm\봯/pOլؗ多P\Nug?wY}^>qI$#v=9Vz$_ok=z>ktJ]͹ꩫgpw{bgUqO9ns׏}wy(Lt:{\r_-=R$CSW>ͯ8sϓ5;5rm-73ʇ.'קۚsf՟mG0_noO~ ż6o<ۋ)vtW/'7W^ջd3|;o`KSx;VN>~0&'G _y&2L_6U\ռwo}iCmc}'-Ce{oπz|;m9W=)_>L3St.WƼk( 7x1}58ujzoi~>?}km<}py^;}){X\of;'ögf~|ŧΨcgSWS=_v5T/*MޘDƒ7 +vtTԺzKK7 x >T,aݽWIWQ\G9"#+p럫\K&pO7V?k'.+>v/Yo9{NZQzuŵU ۣzWu5٭%>zuW._,q\({w9A]΋gpuN]@gxvE=:nL=0ʱ*ַ=[}q%˱WM^x=թ\K֟5}Mv5 >d{9Zmg򨭑57:}βzZ{9`ݏf 鮯|W3Q?ƹzr6YF9SMqiM;v(g|7ħֹ7y6?A5{W>-L:)+~?ϽgnׯLݼ-sw/v}|{g;^fn;Wn#UwWe{s +/bSU_?SyOyg謥3#gy]̸>ѝ;_Y<kbY=oN;/n⿍/W!{Q|`gxD%{/6+ _(?m>jWح+ޑ}wd `v7`髵N[$oXUz9[8<ϵ*JjM'=2hه֟|V} 6"Gεşz^ȥ{1wk6U_9/wmjN&9z_ξ<;|G\լ][bϸ^_`o+)!ܭ?ײ:嬥{go. d{R2Y}ƒ? Gy˯crbovN=}jw/_G]ɗ`w րt?[ll>;=sկ6g+U?UK;w`k ־߮׏8;uл}'z[7;˫&/`ԏ=O<<ڿ`tG@Tk{~=9\r0Ozny/J5o-Wۓ[yuT=ȞZyv\r'rճ[.;歗{gݕ_9?juٞv3qzyAsT<g y^ W=b 'ۢNQ}Mv#@})1Syxzג;*;:?Qq+^^dyrtVZb5Q|_v|+ό߹ZН~ O]=Fk/u8P8}l?ח3OT|eKlOlA+__y[ +Kq^j繲]GkUwGg+]?W^-g_;jkwz5ȇˉ{s7b[;³ndϬ YoˋM}=T}?svl՝m~B>V.#. Y&ct'zC5%6뮷gUG+O;u1ؖ|yzetw@yq\.ʶOޞ3-dϸk:᩻OSq}led{^+<9FVc^]g^}zz}?`'ң;~n|t~>s<>b\A>{p;/m~:3_m(sy]z%CN%ڼb9Fx19Z՘brot|im~US^i??7lhu3_W=[ks@_Wy{T>(Omӝd,>(P|3׋+n%Z|h~]nb'o]o"Gil<&?em_1RWW2Ve1WՓ$۬3=|j? ([9Wk(]+G {n :]^Z>q }tᱽ;;a_Tlֻw#^>a5a=|ދTWHuoj9O9_g k|/7T.K1x\v6W)G>+0O+ٷ{hy@r~#m~7o=O|/_W7?On~^?ogO~o<ϸxa#v{1{IOz=y?wя~to}x_O܇կyp'={7q|Xc5eΙ߳_^܋敯|ퟬkZ_g>g]K˺e/yx׾Yz8X3dYBwp{};߹SY_K_۽b ̅yÚ2./x|xwX{ӟvo_bd|3o˸r FYT7ͽx3Qvyu_b|r/<b~s5a}X'֋g~#a]z$t<ޱ5b{L 1r Ga\yq_9DKֈA^OG=c8}0s8_3{wz5Ư`oK _}|qULJ]zpֆϫ?YW$Sw";)C?CdzX?}fkG@wzJg{Ϲyk:W]v3_>Kϥm[zqY5;6{s>klwOl# {oCluasSd}qʰk8ԝzO91Yq f=#=oCdyF|_=w܏r?_W7ys܏]OZĚ2yOMY{nu=`ެ-cG~s{zK%zիn/w+c~q's1։|x1r䓹C5#>zbO }b=kX??)q+/{#ܟ26ߞ>WmgGF}UVz.k]?ĺ5cd_b>s-rSs9 os#9ET?5e\}l֍ĹYeGsɳ8W֓aº2v:yuxHyaϕU|A$h[#rraY{~jr_^?ri?{cܯ_\ik\RmY_[gkk?ѷmko}|Wo?k /^[_ /"{Ȱ7rsTʪOMCǣ\(G-؇g: .FOqg9t~5:k_ͷSW.y_| ;+ܓ{ޭN1rMdq߳WcƍBq?Z~|]a?Ys_{?~ǼC7fz쫶uW}kÌ_3x_[r}WOb ɿČzf|<{2.΅ݞQcAcՎwe̟u~^;3q}aا#g_y\Y, / ssș2 1wj/\bȮ?e8O+Η~:{r^=ʓXqs9ܳr5bNh˔͵%WʸMhoէ؟ALq|.;mR1tʮyFG{veGtݵ^o+'ۿ--ON2{Wߥ8p8#bT1IDz$g 3s|FVCm bd効Lրd_}_TS #VӴ+?MXg~^ߨf4tA'yv?qs43c;?~/69#ʽޕq"'  #_,\")ܺ+k}Q.kſ={S_\L)sg9ߜs;b<錙u.=Y{V'NzI%E!7Wo-ޞbk|ޙr󲎧)~?ۺyk3wc'8-?1" gl ~C7rQ0/|É7Aw;1uc8}1%v8x97tuu&ybm*u>=7Ɛ}5bXްά7h~䁰#)>5U0f~īs!oG,r|"+r˚kߘ^ O=ks9G ] gr>9 ]c'PrȆHt9dqsOZsS=:k^]-v%8ƱkӴgiG9q֮a]|=;>d캝"׮K%2U.I1x>rYF6TO).V,BR_g +\򲚣5d\XwW.x )T>>\tt yQT|nI94sbU/8,:I@|W[}qŌYej ԋr Ty!D-6ҎOe cA8O9a-y.<aym1ߒYS7$xdu<eaoPFZPg+ϮE\k(#pYZL=S~9'|qC.)ȹ4&[ùsR|=n3xrz%(K/7ųki-mJqʉm39a/6W+_ BKq,_ Wɞm]ekgkwO>q]~?MWS_qs5[ ~8Ч+[g,1%r^z޼e#zAA}ln,+Mb' jGw8Qpľ/?3O{1]b-ts9x^~cw̡5,ל}s;a;0Ʈ|:[WHux1Gubq]Ȍ͝k;ra䯔wqS,<c?䧰OcbsQan͚er7<6WwS 7bkl(θy1/gܺo O5׵W{so5/c[wߚ"\7׮q} P9F}'G/S>1J\#]GzŐ; X1z䅳N`T(7og`mzK] pk{óWSXNG9_LI|֞/>[\̛gm_X ~:qB-_yimش|ƉKTQ?9[Wgc9sʬq/\|V׊gߩW&=?c0e|k9GX[rfWC^k8_yIjs˱2{,H֩5{9\]qsu=E?oz+rt[Z1epsC;U/_h_=uWqrشAڤKӎCW68.W{{wS:8aor%Kr~'\G{k\xәnmrV>S{,Fbg #kH (2=Տ3 l0XwC`͗6G28[s̏g3tbQ_q⑍8Ni`<ܨJh3Ħ?0^ǮN^ɳPyr+wNu:&.S\omx2jMkc յc{sx啰o`?W{կ3Ǣ\0:]˸C>5?b ֊ 3r-D]?Z:CdhւGkܦՏڒƶ)T))O|5/F؎5mˁ<۫]_} w{4ecx6oDi$(6uZ bM쿆qm`9I\H=}3?%c=#܋`$1 zF.Ơq:ܐ b,j<-vKL])ӺD1{a>jkC&1Ew$,+XWnl} kEuqY/ϪzO΀0C{ioy=tiM!3VmAqw7~b'c3p.yi?"ǵM΁88VPb˳9_1YN{YvXz eH_Rj}Q{)Gql t}1zo8Ds5U^y|Uo̞mk= cS3lR`_ eCߤ|i˜W)zx=OۧLI?Jg~4o?2c{}n{.!^Yַu<[O=A_Q홺=Oڟ|kP4S';a 裖xUSƐ ւߕp [/^#?UwkӋK6׾OQZD+ hA `kyo['Fs۷e{`]?mL{ù֎]UhQ?ZcYu9OמGEsW_#ckGO6VOcӺ-[nVsPbv}^mkd&b1?r ǰ~0 ymO>LesG,FSN=w 7rnC[֏` ~֦} b2gDۼkn[p[kno}r_6*/>m r6=̫j:'|yw!o>О8خc[wa[t؞[+ic]l͍y{EZbk9t=ƚe]c;Y#{qLuŽ;-fݳ$gOO굸Yz1 ۫f91+\qkZ|9}Ȍ-MRomz8I>1M v򥊻^&g'M}^?jm{Nfc >Ɩtj\%9-.FΕy䟵7Xܬ5R;jϪnp o9jC ܸ%< [ݰ}M곴\x,V.]v}TN]qW.WuMn뱶_{\ɓoЇ.ϾwlIqs-=/e'=9b8ytŪvgꖕ;oἓ}3dTsϋ[Gx&Rqٷ\Q2'Q.DZ~Z;{XXD-r/3Z[|>sm-.nZ~u'5^Io{X蕯c}X'D 뮜ٿe>Ph(_egߗ3+.N/ce76燱[؞r0XSߟSsc1Gs|?y PK_<}=o?71֮,=hR6e1557׷V&'\v_owy`)k 8Q>< Нjgy֗-*yC.p{+μ=^N8+=em쏧DEߣ'EmhXWQG3{F0~zZXry}{n>c]k㉏^q7z;Ƕxso!{r(G< {G/rc~k6S}g܍A߲:P>G.jD,TWQק.G"'*K}Ǫ7ݚrv{i]WW;_Nck)>Wb'kx!vLZ}'\ `(mg1[ XN=G,rr}<7h՞xV\7Wٽ:ar(.z`O@gk >iY{Ι/w7ɾ*b$U{șOj/&Cn$9+[þڧ}lY난cLr}/֩֯rnYʹ5Wf?ףu?bPk.O5g۷Qo>w[mv/?[895fM,r]y^Ioe[[W<1yu\|މ+ >UWxwWG9˺|=up,eYx{YWh~ǙCWp&kx&m=:zCbOs94v06{러|Yk'˅kn .VO6oMy|D`pcu6jrQԞ$y8!qb-}ׯhkxQ;Zko容1y:Z1qu/R}[bNIހ2wVvͽު];9j}#ȷc׾.΋s}8Fs=߂L6Y929Gjg:Vu8`/AvK3[|STAς`}nsKNymکb%Nmߟbpg9_bcCOsVybꖭڞz)#'eϰ<Ƕ_zVuo֟k,N}3=4|r[i?{.^=76Z^oj.dk˶6s;ƾnOFd l1{.uZ joRmkYښrFn}Uun?;5)Y9z,WʤT[hM>q^?gc~}W&[㪯|RoX[ZgbknͳڅۈlϜXĞ3=:Fi%*.dzu>넗V6gZYn]׫7z_l`y Wڨ[jk[7^]~=0x{b%OSR}?utJFk16<}o*'kXll϶1,}pa՞帯qLrW\}s};:P86 ~$ON`y|VwIz.[q;}htQk}Q+{$ Qmb}?{k{1}r Z!RrW//9Ϋmϛ8RlZ)Vc[m>$ױ1F뤯=|k봑X3K k 3g-·/ڐd=kW1ƫcyFf 1-)dO}mo~*^n}v{oKez¨kIXn]uNu 8ӵ~C^aW=NYo麶uSTZW|bm,hqPbrsi4wwC5U8G]Ǹ<:ֺ>ƃw.]o_m('5#2}oš4-j`}|bU,o/Yw؞A{9U1>(j^_?v3wg}#Oq\TնGؘռIͷGջG'_Dokvl*<=a#zE_c̚]H˯b3mעTy'Yce|0jKW>nzڦUwW?ޟ6ͱx:'>{͙O/&W `v1]Uzkq-횪[+k'߸y̧\܉=Den꫃ٗøq򶻦3U~\9Nܗ|q#1)9ﮅğ꿋m erƋWܾ_'m_k>ՑyN1y{{Wy8eGc`{vOrp!lΙ/tJ.u$_J^ym]þ|֘(8o.Wqt{i_?hӻbu5ԫqN'/kG8ΏrO<=O<ͭԿq'>U 6&w]{\S}-ӉKsmqVu@;_Rތ9Qϻ>@ߙo0]ް^[|i:uZOpz7E쓸B<֘[@hy Aw?ϳcxr??6ty Gg}1_j>v{kg{XT|J]^۩Jˡm|eb:_ۏ?/{p癷n=pkKɵCڽVrar̡|qάqlW<]=Fscqk ([PB݅{w[~ekOxbV͓yW}j_-="M7U/Z}ӟW {9^⛫8+pj{`zf7?ߺʃZTL' ~@z3;WYk{ʷYhù7)0VnS۷=vŮ^]~':QNbL`y1 ݜL;wL7sgsnZ_^7/Ӝ|XN=<{;Mi-Fmz+fj{T'kn=6C8r#vYX3>)oRܚVpmxt9S.znjr>SZ5}ljiϔ|ZG|XjcO1֠y֏rOsԘj{ؚgXm?o?'G߇h\e\~v}}ѝRO}$8{5/Wp'8rۓy4޾CŴgݸ8P|=4{~͝wW`1m}{OU;_VkjꟵ~<"^`/7!O+ƽ=b@7V'^kGorh[NɎn޿WvtN^a8WO:r<7[Z> jO<3}~,i>mĺsέgi?-O- V_|~;Vn][qw\|7jH@*}7^ۛ?Y#aߝvS jSd )RHsWͿnXOn~T9g+kb'l 3ET&]#Q{slMk#Q\AL}9or޼)Eu+m^l9Sۗ]l>޾K篯-k6_˥۾_7}06o '}5bZܤ׸wL>JZ>m{xczw)~Ǵ/we a']f9ּ|g:}k#'cԇo<-1N_?oo/ }Ds'<`qruGt듞Oճi^NϪoǩfx~¾c[jhж߀cFneIm/[V.|fb)i]V#geSGC'2Y\@bW>x'yW+31N4+W 8Ou%WN9:g߂ʂ:ѳz#P>Յ5P :tV0qM=k|u1ܚoO4Qgkr8Y|_Oykoc_yW{lO5?_Wq9azQIcO˝؞`;;qڱ|hkSZC`Nbc:וWfKQG ַf81yn^7<{tc(S7|.۹5܏_<NC}NuNJj.]un]2ޫZ#8{]Ͻq*wM^/X5rW~.39:q/c}CbƋ8X}[,`?$꿷BLYg Z7νr{},XLh{yQkc{mzS,y-m\\#ACg!O }x{w/OJnYϠvSN.Ekк?>W+=ۋ=[|/ۯb+}:jszO>:Wy(ńN<jǜ8 eW_NCb?'nt}|omwqb^k*\w58֋n8KF=)_1#8;XXbqW`uN7'`vy\/xfŏZ/fXpq79xy}v承|1Ofm+7.w=;cmljˠ~4>,Q֞Z+ǽ򼶿rh<]+Ѹ~޾{9Sn/u}|egaL/?Y~R[n^es{ 6>9qw |R4 x75YF{o0 ""222BBBB2B222""{Y_>gHs)S>-ooo o}^^W?_zիnyzzz?_?o?nnoӟnn_o_7׿u|/=Ϛk?׌u{5cΘgo{۞כ0{Xswf^<{3ϙoxk6ŚVs8׿򗿼?xˬ׬k1}<~5~׻>{s<}ᚿ8gf3/u{2巿~?_3fNn3||#~O#G3ƿ2ϛ?Ϙ{k9qϘ;޹׬OO~Wxg-ww3w/|g;c\LX^3Ιe>Efs}>{3k:3fYtg>2w>k7?udz?b?z>8X_ b烍sYo<=g }sdbcdq|-3ι288G^Yki2yu=}{xg37z86x{l%=6k[`l]ߟq|fY\ˍame0>Gi/6?g``?`>w⣘;Ϟ ߆`_ za}v5~dGld>#mN|wÚ3&sekd~g}}1|. c3׊37o~;?l9sƆ͘ 4{;69.ތ؂yZ֡ٿ9?^bf.mw}C~`-qhcyrZ#tc8w&Wn}ki\ǜ0ux^3'b2]΃@}g5_{ L\{={H>q۬x~ 0qaMMr&s#y1}bя~g{41sdg0fs(\<¹03/b|IT}o܇Xo 38's1ϜYa&t<٘|os4WqYG|>Kx枳;;mV]ֹ/~y3.=G>͛o|?2;k0c=s?ya/``ec7kAo $|9 7E#5?[,U_<cf`Εgm1s>n`ܫh\ӹͼkƙ6 Ⱥ}1ik7~a>?#-l l>܉vh rl<~>m|zJcOf>-d3g/3Ϲɖ6&3ɥ?w`S3^p?p. 1&y xkrTj`"Ȗ1L孞ߟٶT[Ca@l%/c^w`K*>$?c]k E ^&߬CٛekbY's䝺sUUbѣ/lݢVQaD<@k#Y˨q_un1бqJd یr,eK{ ^S|%j󙮃';G.N~Oؿ%c|}r@]o8Vl_g!k&cslug3&9_$Wb#}3ئMS% }S1b]f3SKwrA10=s\yk#k[ m"3u؁w,ax¸k|`جk65e3?~{Y}6gl>5+sR _Eޏ}=9_;qo!vB"]߫;F6~U"E_G7op zi?ɮk}*/\wr&2b[9_:݇֞]sr=x1GÎ8xG4p3V?c/&OKǎyjmr*m1uG6|s͟ eܓ82\:hMvxc朓 >q1kM[~Z{X LcH<`91Ymb` 7Ӷޟ~buq~l}9[=36޸b'8#nI?5O4lʡ|U྇c;˿kG:GmM=s5ַ>{Vpޱm{&>}^828_q 4F `=h {Y pbX}5/uVoq: 79Z 0g3 vؔr̫%GN`36><Ūfp.s%0-&vx}-nn5:ל;/nW [$xf}7j_0||~ʶ9{/e~ROv}?u/ԫoIћzkW:ڿ{e|շ[2Vw=-nve1-vgrSsugıxs{=5rmxƟ{\-o*Ơj }s=u lzZXN9X{9'}/v;֘z;+8KCt\fLꌌǔ{Nu go,+Ovro_jk 7ZZe+w]~j\ȡvt^{; x}pӑm։8v}r^?Xdk \ l߿|Fp[W>{cn=`}.p?cp˧y2wk_Kv-qcY5z{n[f9{=zoʍ켋#XV'`_3-}%̗>-(̳ᛙ_O.mCWf.7$z'}l\>> #_O>lZ7/W?iV7m[\T7nsl=l~9kZ>F{0rBڃƳyU1FqmwN~4`5 SnQRsIpq}aK3:o???կ>p։yEo_5'[ZkJq.`.ok:p"n: Cga!7cMŰ-SdXtE<<)0<)ƓN\7jrDܶh(*NT'OsuW[yZ,{7v^:WVjY7xp77FeTlgmhbsg؅<]⍞~c >$sxZ;$E/}xo>wU?|w=<ka;X6o3ZYle2'>׮[Oςjҵ1~%vnFW mqf,e]a>gDZ{#;sil|I; ]sΏ4?Y^ 3_AZ_Esf5 j}]oV2&;qoq>c|U {:{Ww!_Zh @U8rs0mc]4iu\xgj? ur6tdMKLS `?39Ϛoԯч}<⌮3:q(?/aImVsxdnpV=3ǥjl!nuMh4m*^x}krN1s'9,DTߏWwa=޹g_쿽ގk*׊ܿ[uC=s|rmo<n>K_S}ept9>3OܿdxVWW@ɮOnp\\u)Vv7D3VXB#x6`=cA8>c=|No6l֎l{[4_haMcYqWzj4w8T~~Zw>:#~{Yƒkڧḧe1d9roy'ߏlWBY?5Fo}[46!|VgaDaZ_m^TrVոB.Jep `h[ŧ0K\j{ڋ'|t R|͞[5gi;`;;86W{Xo'^$6fù$\zbs3؜nsY,@~GJk[O]g^`P?c=| j!q pyxmW{Oغi;_njጛ~C^ϲ}Ә' dU,nm&G'wg?iZpM>}θߘsrG{6jC[7\7@u ?du{_ w6>Щxlǖl~>oEw P,omRly1L[gr{\8z:Jь=[ y{;ﻖzΞZƅqj=Sn}sov〞b,8<ѳ=ᐵbm*'n@|xJ3c[kkwZLֹ|#ܟ c.~s[qkN=g[r3kupQΦO>?zm-.j*>g ?=Ԟ`7=8v3ܯ|/sߜCX&Yc~]).dYm=| 6\8>\1ɾܜqiX[c^,ۭօ+vb\ѱٮ/9K< [N†b=?x+>O= yEu ,@F؍)9P'o/C yp~?ces999|][ck.zT5ﱿ6p5`=soNۺezz9Sl TxO큱o<: 6t^)k\NnupE|1|X{qޯ~́9I93iO|>q/38ʻ^nX ؛w,eg:!{sٜQŢ6:Opoæ|xlqtUkMSo.:'LV&ᶿ8ũ|R_9<g:f9lWh i㕷?{e<{{:ÿsp `slf+.c=׭ϱ̛g1㑫~>|bxƃ6_xAX6L[3A>Q>;Xt_UnnJ}yC'ܳq7ovf/G|s=r-['sڜ6KN~ /~A+{7#Xl7sl`BA\=uƆ{)8ʾcϙ7gcMb 0gc =$p{rr1\.'pV߭/[ۏv~[潧Ž7↑{1G9s[uiSȟٰ?ל|8Fգ 6<ƫE׹KzO˔a6*i-Ź?ٮ0m6Y\_7xȓ­fg'|Ϗc{*~3N߽]m>c`]b-nFwnޓq25rNgfxMܷk]DdžNhw-m2NnN{-E9esa[V(G`movt1 }OqG{⺓97=ט=BZg|ڴ}9tk4>0l/5t7=}W;lW)Pm8=gT"mXnl~sr<+{rmRl˅Ot^f}lV:/*\ 7^ ms3v#xvFȌc8ɟvs;{}PXj7?iG~i|{ο~i 9f^ O9w><7t~;Ĺs5w|V|= :@n {ܣOWui;ۋwo2x1yvmvϳ jۊf7k홻H-/Vo7in+^'nU2ky*'n( pUxPyf/М/q}\p^0@6ls93_ȶ=t;ԛe{uh]=^T]jN^Jƨ_Nɮpncƥ]g[?sb{}_;[6LYe߹K 0i-W5ixh p([OW:aֶV9=o\r'OjĖŘ;c?#GЪ;Ϛ}cs]2cNsjβ^S_U_=DR~Q\rjT8(wC'iz6api,ƤzFV'z?9JQ-n91CUοY9I?|6V6V3ש@G<ȇYp [-c-\O<3//]d\8_5ۙCxik^l=\~Hg'mmIϟO߾ϰm9t u7f U7~cpٮ%7]r<}/W2Jr;Mr?OַCH _w<ǭOc>X6x{;ǽ[-m#jh:n~>s쯷kSW|Gt)n6v5-0t/;6clC_֡6Y}whk:_f|`NGg6~Z[ܺ;x.5rlZ6@Z24sv<:Zmmc#_nf1Ɨ}c4$s|[~=4΍cq!c?3gYo]ʛo_̪$>Ԧ"  ۩|]on2y«]~gq6VWl Me/ۏgz*췷P0Yx7-7{ э0 ;O}G#ӽLZս_xᅛ\;{{\}?/?u]ֵǺG_y?~y'.O>婧s{ˣ>zz;cn_k}pov_.?~]_kwϺk%c+Ƿu~_o~}ֵ̽9wu5gkN=3{߾{55-fkݛ۬Zs+ϟyd\ߞo_~]_g^dgse;83YuϹYfk bǚo^k,IlhO73Fĺ|oc_n,zE|d[bP|v>n^z~7g[ KbYQ`|s5XgN1aŋZ>7ZZZcZac~yOމEYk!h${eI,m.f^Yc\ƒ9XϹ֋qu}M|Mٿ|3Z[?[Ʒn}7o`/sI"kXK pHwGk%cʼ^;vW1gsh9pe!&eΡ06b=ܟܻư>ֽ}i}=q-9rOb)'&ZdL¸E w|dNJf|6ki}Irκb=&LLMޤMy-uK]iBƗ ~91ׁ`Cl8'w1X}kq}'&:-{==߯zg<~eMemg<1'< 0g-GXl'c`\8_bױXzğY0v j\]yu-g]Y޷9VOϟg75s/b={L0'=MߜH=džs+.b[\I惱Ts Zlfݟ8  vZ\H85z2+f}X:sIsku?u9v5'x3/1mXߵ.N9u?gbIsآs)'&qXg3y:߯+~arWKbʼ0.7nv[Ou܎ݞ7-4ܾ]O}>b.W^ye[\ 8'oY0q0#K>=Gע;L?qra? 9Z# {^ 1G M-y^ĹG8&0p&fo`gm3Sux0ʙ18|mj{k7ܟgcQٺ"1}lZ3*׃</ֽwqW8} h}G7m;5>CSwγ! G|uGbw 9(y2m ɋHɕqp,wܓk]bit:c'Z-dͽ7Ź=3:\^熻7߹ԳnڄI0xzSoyb.q5RY #zcgzc/9ݖ3; 믟[i,B|=Ŧ9k@dŘoZ|ϸ]MKdPsD1"Xn oA`|暛'{3/MļM\uᾄ# `\F.B30@mcaNr/k4DBg'dMbMy:cz$x<ٯ<9֥oS7  99'\LEG(ڹ툽~#'7~hM<{Mrw~j ֩rQoϹO5ӝ>e67c4{U~&3R|Xs7cmrƻ; 摹8q-$.GG=q}+sĸd `u?i\{OkXF\XgL C&/]gn֮ssf '6zgdr1wG߉g/'on)0WN>c 0Y`zr1e"cm'1Om0q7Ǧo(k)7-$5.{| q/ȞZ4-F=>%׊u.㥯 6mxa ?;?<[mr z#$MvV a=|kOj;xH S G|In)NvU →'=G%8i[o+kBkoZ@s4;k~j\148s-coo<y)~34WkM51q!x8$weݑsaO7߶.~{OysSj8| nc3ljkmӺյq~}Ӝ[ ̭]!^(}Nt~o\V?OӑϾ:qo⺂vw?ϛgpg-@w>knqJfܓd6F,_$_0t{X'?xVZEQD]$K5cN&iKS._[|n6`MΘ^_bAh5$9{9i=V og ~ưcۢպvF,[&mi}cOVr?kuո6Nqnqx5yj=+OBLq;lyS_CG}h}3`wFGCl:?Nv3}73s6\5c/5O8׵&f2c'֏Mo} AX7} k7W{V L`?clxpTnc)m>'2I4 Vk<{ٓ ~l{Ř}.G$So>5CD?65m.;θoyir/)&_\Gow9j4ﭯ1qYzk}=z11e?OtU?FyRwn]g{N {%i=] 8ΞM >3h:7(89Jws;ib_9Yl&wNCݮϜOg#3I9Ŷ7&mF-ư?tåϓ]oklGغ tBżv{rv=u{cw?k剃u_4kv@a;gq˜4.yaoy9<#pM0S0 sjasM3oEƮ mK!kݯ^v} i8_ ׸VbsXuj?l4Ӛx9n׏#`뷍NgôrNI|~#7o<ۯ.yv΅5mem=T3gt'h[uƭf>dfo֭[oɗ<h5fM<91Viҩ>!48܏'1̹nu\;m|sZî1cj<c +#s:މ]_1x1C{V4\9w{[7'sf_?;qMuK狝O74ww>3vogycw>~įy95[j#gpNS8|}xo?} p]}=4=ٚqaSޠ}mڱ:̘ܗh207c5(kD)״\gu-V0@CC{7ͧwI,xk2뗆e+ck:m<)̫yFsgl6VZڟ MpT7޿>g?|/O5:~c`9l{[f?42#lRm5Jo|ظ=!5MNƘg!⼶/ج8wMPǸ>QhM0w\`nLL[aQڡVcQṲ^-fGMFq75|Lיvg\|=ǔcwf77z mrh1GȎmiaZ^1^z;`]}Xv&n{8&k}ʩ1kvvht59H?Xr{X09z_1{>6&obuMg11M\xI0h2=H}-EDq5ܑyqngt(SdMx<k:b89^r9<'7{X ps[8Z#Os@&{moěR5iK -'vMA~qb::Ϛ Қ#j 8;6-Xw~q4V⨌5 jaMi|rؔX=W\z<#GP)1;}uƉ>-6v,`- MZ gα1`jO)>Now}O5cq4ܟ1o~u|"5Mvfy'O#b}H^'fmXZֱzjwv8۴qe:vG3>]Y#ΌKnM#8D~Cvʱ6"_f͎:b'ֲ;ݮmx=h0L,EI\lqY߉qS߽3ڟ sZk l[y MgѸ3?h/3Fb:wg0O46k kMv9 sFs5H_hk'1n'^:Gq ^[>kͷK[;:]|>Գk4O'n}:7I&.ktm/d:sYYuRfYLܰ]bF{~۴XV88Ζ]8'p.}\/o;uh8i\r\#{#l@rkbϙlgL9b7M燴bjeuq@0@;G$W3WXӴN48<Ξv @aFk}9u.N.d<9#x2bv5 q5i;jBz5ݩ1 'ntTG/1GⳖyn/yk]2Zcz&Xr&8om c5c˸hhN'#[ϟX|Npiz.Oӣڿ<kj)c#~'u{p/`{hml]7>4?wܘ9w_;1RxO3}ikZuxy{9FⓩO4>3)6< qO._cЙZFh~o}c;cc歧{btBs6_&xP41xĺzrW;3N嚹_`[?=Io͎< }s088h6 w\Y[oג=̻?:9< hY` z0"8YrdSilg@ qsZ7]& @]kۦjZSNl5m8tlzkK/^j5sCͶ~~[j59oʽM?qf>ۼ}3jevo ٔ?=v۵ܿ?a8p{;bNޭ"kx1z5 `/i'sAP'+Ff(F S`ovjkѽ[XiaU OkޑFƜk|5qW8bhy/ݤLjcZSe͝'_bxM5Τ3ҴKW.kkxĜ~gs̱7vbo-[xobRS?vs?O?>pOĵ91Y{a<;_ğMa@,F'0δ;\F\j en~' Pӄ>ڷzɞ@_9LڣvLiq8љhv_8GR~s-mu^kWbrG9w 9Y7_8bV3$;;luwEZ1hŗqk6iMcinW99Oܯ}8Zu83:sznͥ}&V7vu-|2sگ1ם6q+b۩nܿqɡ8NH֦[s3yډoͮ[qCp`O둝 1v>kgFL?6'lonIM6o_l/>c?o{e};|?SOcaf˓ OdSmyϝ3^^9V{=WGf.v<w76s#~j-Ẁc3&2,Ǐph{8#5n!{bVopm4gGkkƻ9F޺u} ߭=V֣<_:i;;|M{ >.{U#e\xڽmۮݯhzW67y~Z#.|Ook0s„vV7X4$0d0 ftmD\Tv c1X8V#4f`wxfK/"&&&&&&(ڟHy{"ޙQt[*3#2"O/}K_zw}_|w} _sGg]g]?>O}󟏯_G>__|y~sf}gsxogy{k:gms?|}keޞy^/Ys=gW򕏾կ~}?~9<9럽kqדY;svgfm>o~^y's=[{ӳ\gu&emx4o☭3c#qr2qr2qa`{?8|s<ǬYoLgϬv&?[̽:vq+csb2S)Y{ާ6e9rSk07qwr-khMzX^3v2N26f\ocare`٫Kv-'>xep3~6{}N'7i{>5>)fmyY6Nf^ gaNe5~ӯb Kwm#<|g1\g8imE\6fk=)Jo?cCt<[Mh/q[j4oo\MLN?les*\6I/1:|rۙKyy6O?;Z\٪S_971M^E|ӊ5K7yӑ&q'f s6lq>3jB#g6ͽG-7"k6Ӱ֟l7q?g@ڳ 죉3 NA?\gOVcm=լ59 ^lޏ) 6fy}!?ʆUh8gZ~_Wff?t>9@X3ϳ?.I=1ߝ69@y4/4_38'D֦0k7?j`鷶yvuƷ84{o3n7s#6un>뻙%>٬dx7g984=3;9Z??g-22ӥM/[mfh7}6 r=(kkc#yr73z=WI]5qΰ}4Y7|կ~vys4 M`:?{;6LMm|眇N&W` ^vY<-o:c5Wg4ݙ|n8@\7~zrML57-6oۚW}Vog~M^>_vv0_})-s g]Řc++;zTOǦؾ8/}g+uUӄr*ɏ7@bðz*yrmz_o6&[]Ƚ<rVnv?Y4`㫩Zi.YU-^QgOs iͳaL}Eemւ 4O?yb6cdBM~avNs|\4=s`7Yp6y7ImToMsɯ:Ih׆C1  BW9Kc22'_V0fs9l>|O0i`·9?S'6޵W'4Oe|eZoƆTj?'{($ 2[]{gìnX\?{n| ?!߳7m.kB[ 3 hq-7~u-utSOϵߦ8*Lw<~[&@={N0y#}5ls^.sryo-s'SMxsЭ>oϛX]i3չԥ[_W&~P6?aD@72ݿW4[ݟ؟g?ٛlH[`ko w6@m԰c7y7?s<}ujϫ|z˻6@s󋛎I޲6qk6߅y/#1|gL`6;3\VZnuy_vԾ2~Zl4u~>y+baF36쿙r<{֙6mbf9Cm9e3A홾9˟o7؞h{fV@z;Y`9wo^@{n՞ i憞76bϨM5O K7$ށ^l1@/ 74lA“gh}`ʧ3[mkټ<띭&lŝ=@P'mN\͏VѰz%n秶<κN7]qa`6˟m Li?[lvԬ^ nx7 @c+3CVG?jhSr;|" 9gmNWjdžSWOO^bS2=f i仭e\ c# _7_&Ͱ'LndzzEw~\In>3̴ >' ُl:FSͿ7>?3ifZ~NtFdr^]i0hEs`0YW/Y!,vS5kO@Mٛ^@/yf̓Sk17?nkcF؃nk[|1_6猟y-l]Jp'}5r5y ;NXdWWm=79bߴ֯'_{я~f&yKd]kmb8y'?6Mwިfٛ[.x͒gM.dnZ^z7r[7<_fnfgL5/@ ޳>/'۰ߞ=ZfYqGϘ{;ao9M}#Xks+\'7Ff#sz֞|H];cp«7˩9_AV3[=[ sog1ͻbZ?SK}k?7l0g8q{M0fZ&x߯l&Z>~zbEoi䙷aس'6;y;y'di(hs#G-6n-oƜ9jsfY>z9Cɺ\nf5nNsN_yK%g>f~8@V\y_3rӱ 짓n}_m49@֚5^M6'o~u<Ɯoy&ͷ4ui)7oc9Y3>cjc5.{ҁ̩3s(}tՓ.+o%gMosƍ{n}r~s 5O67Ұĥ|ٓ}{om=hW0{K#2YW85 n3?j}tgl:#f_r6\@W@2L8G3M_Gn~7|aMy9OCh=ɇmssjN6ͷR[6~fմ5qy2װl|}#OޒI '? @ɉLM1/ k6XMfOF~iF7sugl}{F~%gִ|R{>o8yƫnlk|uh pgƹ%mX˹?s7fZ $lq`?yټMenЇ{{kީ0sFA{Ŧ?cv>;%Ϥ0\[B-Vc:iqmlzV~9fSe1}{4m1>yNzi7s/͓kEۜBֹӬ gMx_fJ+?M`@56╾?kͷ7e%?m7h/?.l[SY o%I-.v&+$wU$W1x1r7?͓4}gOrcYg|[z8@Q1QCm)s3lsZfSlOgny:g@j_4Ooy>'_ŷ6@mxxMm~rqMr6bsfOؿiMk̙31q?3ryn s Bۜ=mcj\ ~"{K_LL9}G~iׯ Mmkc>7_^{kiHu;Ga3{ظa'^G%ɏ93W ʨG>ii/ΣiOԌ6O|a ?@#F~ؿ14|s3 ̆3M#bh̭̉9Y߼ !m&V}4Mqv͌gf̱`޿qWoa?X3t }5Ӭ8c߼:g8@/MǗʼn4xL#lz@orOú\~ltN1;emǒ`Ɖ%曮uz>|R3?niK6S~/jd}7_7_kJz f9q+7>Ňw5B{hN)fٕX3yO1Ѵ k//ΓL_Ro8Ogcz26=_fZn8sb-YOn|ۙza8yӘojsYfZؼ;3pv!{lg{87ؿNj3{̳c̓9:] %NnsY7Jۿ3/2FNms.zN֫S_fs<>3{=6`Tg1~gd;$L ǩMяj^xfhi7o(wsƕ9/N61ٴr-f^~9~3OyV3<j0 k8E a?߿মC?Yˆ'#6GԸԎ٫XKoXqmð͙ ayPQcϹ"~!9#b<Ӓoqm^~W+;79Gw^Ff{y;Cӛ۬2=w3rlbㄏ>{;cs+qMq\S9'VyM/?7m:Za%䚗=x{ԦpϺ﷿=i+<Ɩdku hVz}GgzH6 }񱌧V6oXyȦ}fK^.a.?Wos֟p 6 =?2x;U v{>z5kU߁>sfk׺TuW_}zO?q=3g}{?~L׿_׃?>?<9/ݣ.1V_xG/>x饗]/W^y?w{/QwG>]̗?x?Vש*˥uyWX*UU\G=r1il.ˣI<KÇk~UvҟuL޿[u5zoT>hpǺ. w)=C]S=c g S`LõoKK\ZR\SzU/urnU<{S}jgnΏ۫|k+u8吏ֵ֓g^׬~y&zN:7͚q=11qʑ7xH;{r?itkx1y-^o?F.y7~F/wggb+q;/լW,b7p)iΗv-Ht^M7 2}nTRqnu֯b_8z.=+NOI39T6~Ʀwj.i4s&HUn1;-w[o;~j˟7q\>6&~t͵Q'L_Y|r>I3zN]lG>9m )?G4NuЯZ>$?Q3zM޿,ӬE';.xoϺ˗Zm9 WlzOFs}Ҙ?'lsf/[kZܟkENlUlM puyƸ]w%bIssIL?>N׸:Y|h^$Jrzt2r3#._?+wڪJ{Ob?Ϻ4P3W\?{4<~O~|ܟQ779\gV ~ϒOn~Nn'''J}kFK9@э~^F?wzoύʚ;jfins־}Xȏ]vS;vZ7y~9|+no\:~<I3wy.Փ[+z| N9s~N鸳Rܧ&yϦ8ø{VW<\Ww˚fo&^fvqw#E,p:P]t}뚥5*lIy| sGQplY1:QOYO/s$cN٦OwߵE:jUҽ@&-b6< ;W|0?}kY;1ks?>CWIO؟0LދS]ю~&`SE_c?Ή?;K}>Lop 8=>r>!KM.-Vz=Ҋ|n5Tǻ9?ͅ'6N~.5> ;{O~*?rA:=0͏tt348up>S쮇>gJ4=SN'P+ުF FN'9z9OW맹4k>zN e_O:ʭg~;aqn1vI|_8{ijXkLYa?ׯ:wwy:~mf_Ww;uO?&-EmԮn%^M`;?úP+:=8 :1}=n}4#HǬu;*ﴷ[?y]EZU3}.^6hL2iW7]o&:O:|ݿSjtmWq'tTw'5\~ N|;:vM `nj~#;`i1Co5׶Z/(M\riZƣ:`:Z~w >lSPOs~){O^SC)k'kǩs8/7:.>X`VO*v=r_vW߮GrCp)pح'}b;Pyraz :+gGv1q+&߭8R|n+̉N^+|䓺:I}8@Pw.OziCCݿ~['嵮[(U[g?(i Lywjcׇ8]~q\xNuWI'xN'?W_i/8#Dp^=' aHg.idb)wAw}ƘҳQ;w_t@N9-߫p=JUg9p8y&q78wu6NAN'w5a"f ts)I'$OᾋŕbO{jtE 5?unz&H [7}#s=bIڬ8鄽 駟޹p/8@P'@5_Fiݨytn6Ũ^$]E^s՞:l!q=˕]Ņ=&/ 1Z']L4.j}~N7:1&Ns=_Xyx'LL=-79vvjO'sP;g}f}j*v1sU~rnƔcz.Jyvܾޱrž.ΩWَ!u=UQy"2w5v q߻uw7E=IG_D]kIp+k× cK}7$q]Q~5&j?\;o[ͿU5Y8\ܟӿvZyF~LqtX:K[@/8 WwؐֆpKO< -PR/8Ot;nNl8;vH Slnoly?L?|[c9)NL);=rߝzzwybL 3ou`Z>GҞϿà;Nju9ߐ?YѶg:X /NZkF7/< \l<;=:\`<t9_zsߓ;&'#\N9ϲ[䗉k|q̳·Icu_7õr,kNx8갟9:s]c|ioyM8Od_.gO1܏tCpidRүΗl8?`uZ/`t.upa@=;wSҭ-Vxnn谟u8գz뿧oZ7\'$>6/n8HܨlԇR)1o\m'ޗ4q}NC0 ɟG4ěO+rl5?>{Es>U`9|-^k?ri{ $a Ϗ~zd-vد^-&ߎgiCytɗ|WǨݬT@,Lg-jR{(>j̬S6m'7nwxFw?ձx)ti̱ l>'a>oit7agMl4cJ?騮qi7s2 S4kM81-{'z*'~5xk3L8_Dg~;qx/|6 )ueݼf:OeyM5bY٥s:8W~ʉk-&i 7˙z՞nDryS^1'5_r+팩N'\NCtb/ēV?;K뮯AC@Skj'xܵ} o[_?6sS'4bW7>y ^zݮ^',KZ91-'>*][wm:®?zkzgr|2q'mK7͘t뫱Ls~M?u4y=O?ð'6S_sˣr<|=}}mN1n^vzìO'}+.pܦ3,]_y*?}Ir]RqkZ+-'S}^F='# ;liOb\B:hN4KI5$ՒOz^wiSGc[s 'N\kEL/?)openimageio-1.7.17~dfsg0.orig/testsuite/texture-blurtube/ref/grid-0.10.tif0000644000175000017500000071304713151711064024475 0ustar mfvmfvII*> 2=RS~&*5CX&iciXaD6*&9/ >e b5&3Zx2016:02:25 9:51:38 xݝ $9qC16kˀ}oJ^ {fp5 BJRߓf_V>լkOGz^mTͰFsfĚ͸afЎyxePl϶q~`3/.rnkΧ6(׾^G:*F$=2+#Q#_n~(;#}*`x*vp#^CWw=f:F!bn(O|'[U>q=i'+s.ɮac~ߌvj[סx~?Jêm˺F-Pr{cyht@T^1?'Ò<ot{u5S]8:3vWq|k;bp'm0˟Q th]̠)ki-^wA]~gʣIW%.w`"|+?*#+/Y @!k~a5Vc#Nqxl9tl++ޖgqYQc!'2<}7 r 0| J5r_o)=G, iZmq`xO>pܝaw(<>tGK2RJyn/L=LZ[ߕz@kwxz+ww2]e3b 2p}uox<4_m[v )_޾G[] P?洀Fz@9O~Vպ7{ ~AĚ_*1Ǟcco{WZcRU)źj .x@\G=utΚ':[W8#?޶ 6ؿXpO[ oXUܳku@ ^:F&2_aY~:@Gz05Ow~<>> _k_Ms%f҆TmAw$#|h?0^#Olw~ OfggG̽#Fs7/>xi㼝KGO=d|a/50Տw ?yO;NǙA{_7;B&uWb9o ~D*ȸ>ou`_V~W4@? ^_OkW>{V~ [PrP?K_V_ lhLWq:>ul>};J wpg?is@> }\gZ \%<یr1֏Zc뾆Nјomn▼yws,S^(mM6'{G{ih,(Y=ɿ_]ez %/OumU:wXG&(c3 @3`ϭk ~ z `{|x{{oBI&}"]q|Tsv2?˺w1sҞnTN@0X ˏq~/?4`7޻\> Z7@Ly~dq!nޢyOݐ|S-Qta'_ͣ9c Ovg>*t6o fZ} ?_qxJs>O +}z<rvb ~mJqY<'^ l|=b3,oA\i˪c<<SXX=|BkK巵@9ܡXycӺL'dZ];9c;jz`==Ż|H x;|/u0>6{8Mu}eeK0~*{~3?c/SX| {l=Yl.Έ^.}nyCODۂm*}'`p\9%_Tt9w `:/yXh]bn{R+j|"{}6K,vOWo6{=@3#0өv55p0rXW5B_V]2n|u2-PEL'gw8z'*kVhXu0Dpf} |5 oi>xU~sm^}N42"1o~?57a|G8ro-_9q9}p>ؿ} ʇv5>_>vbYW#!E֏>cbt@?.7;5lVZ@b}LA|?}WUz5e2X?Pzmrc8]VPvý}E=p~\wo oE_;] s>x1m?P Ãǭ*v>luW\?ՊߊN#dZuJ+##esoN ˧nQ:rC(.0z?Yg:roCk*|I[gGlG|3=o~o`aDN<߫{ǎc~La7/8>| HT΃dT޻x|4]1JH#܆ϬTᛟwkfe<yn6~h1̥k >bz`joV X򾝕k: D*m3׉yV:;ìa<, {4b[=,C=]oF~ՏQŭ49ޏ!zxUz&qwg<>~}lW =8L;b>c}ukJ򊱮~u=~%ޟ݀AL+I޻/ } uL'h(?[[JzU6Pz,fu fZ_Xzܗf%v=Ķ؝"g|qyn=cbYJ0X ='bS Ou2rS g!oYy3ʛk+?􀯋scA'N|y{0ާ?Dx?s=dg?_w P7|SL=ОҦ,Yw߼3};_~UO0wwt@|x,ukii~e~Ow؏v`<<(Λ_N?^xo Z@ŸbߏFx=#ᯡ4}cd }WX28y~r?EY{Vo3y{[_Y\ۓhkڵƽ \Mٜct;/~ˍKw+3( c91ݙmf31d5gpJQyv,叿_x@ +;|5*7 EYOx/M?.xss_*a+gks}{T kCmܷ1wfj_ |.}m>쵽Wܿ%VE-~3=Q[Wbn_|V巍㔸S]}`/zq9+sK؏S^O3%Os~E `{ } ov|'O `}}gY\  /[Fn8[/{a߸Οi_]%w6mhKgw'[`>ˡn>vYa^uzn)-ؿ*{#=-MAIiLrU6:byNU#?:D{^9?'w# ?*к9L6`K/uF}e73^ qhϕ8ղu=p{P6E|~pهf n0jZNq>L끖ȗ;GZ5&xep 'l7% ;3FlY~5?g寱? ^l hgӰo8SKU}U l4^<]Ƅ yc*>m;ݎ#omo#@OR쬒/O~sTsMy1>5LNQO;?ǞL$a;s1~ǘhr[`64|fQvk::?t\{/cO = q{ a;q̪l/,/->-_ \6O:9eOww E Um$~6iq`1d;S:x~@ה\?0vI``|_Lx>~%Y2S:`, E,gϑ5yg(,ُTỷ}>qu=kMɭG_3'_XgegF9 ɴ3~8{:("?싼Gk_])0)[;u?ovQ?4P{MX|?}}]aQzL `=Au9K ]ڧ'#jʴΜXBz 7c߂~n-~+6u@1~? 쿪+xq,iogctoG,G`ܧ5>5Q`U{翍kto=Q?Gf+ ?OtI6yבEr΃j:h?m l#]*O3εǎRǿhSSXVwd;:9oGb'}sO>aG~ Y(Wzqɾ?+ !\%r6OG*ߒ9wuYX#> lOg?%8U :?}|=>@{j֡6?~|qv6(a$!?Ԡ? y\em-Q_>gvx~`ͯ_ׯ0||WеkwE؜;Gm[15MoD{<ou[(W?яi"k*ZF_Կ'e Lj~3ubǣ >+1.FqGj4=&_96=Z& #51}>31MyDGcaWk y#5?7ioa;W4t.M7i?=BNN0ϟ{JZ_̗KvL_2ќ & 91bL䊹~T+7q=;ZO{DZ1UhW>'_}xm/(.3=VNuE&B)_e!VGg]w?0ݢ|%jOq$/=gPkwMOU}P^{[z.jnB|}Kt<&Vc'fgko [?l{>Y;74Vn3?YR{爫X+Xf[i6&w}*>> _sm_*>Rϻ9Vž w psVx_o?92nB ^G<. װ,B}}Ϗ:Ʉr_1r֪Ԧ=ٖ3#i^ݼhAM9qgZ3*x՝mA&6MyMҴic$=[keYd} ~S)R(̆Gcm}.7n`S;{^hQ?a[2}EUlq^N澼|Dn|yki~[Q>e~ؾ\5|Zmյf߻sjc {(lձnž\62k[-?ʬ_Q>}r߿u6}e}΋#9mi\EKh 6]_u kڏlfp^e{N!YsE2,i?sc>QDH?XYϷx~sП9 uN:zS2@k[q5>ޏN9U+Gw`sɹoi~]vNN[ ۵0_y 0 :`yOߨJD^a sIzrA2L ̮W+s|myE_l+r^6S.Z޻_@+ p]r||<-!D??'w6]>L)wPm ,wMv'gl߱Y?}{eYE P,7WbnT/k o5~G܆P3\{X>7L vLl{3{&kycPv r?Zh)q;x_@X柮9࿲_cq */ пc̷۾y30_3˚#2sۘȤeB9| `iGZԋq==>P ʳ\ <нh 0\U58mefsR׶_[%[`aPֹiIo4S09W@~C?µ]Q pY]hCGއrg\ʸ\(VH_æokM_ϰj|eX.)v.G9:kOUk|tA~]V2Dy ` q?hfBA~wY;b=*zu%>߈%j=kyLH:-T|@ghg>v |9G5ooFXn3?ۣO?3#ݞof^B@SU8g9q<Ѯ|د$)X'A\ټy?7g}랹,8}>헄f~j3cR\Jm,{ A_3C>z@J% !0,ЮAYf;?'>RB2LobĴ{=~UgyluB)F_3}gǁoiV?)IM氖~v=ve/|#˸LV_Xܲc?@u?Mɖ]\顭[1sZN5Oc0p& W`7_E5@o"e|e.j9vXd{]uvMYI3*vs|Zg8\4_g?]ݺge;59 5};1VЅx|?s~}?r<_JcӹNz./Ųgsg _U閴-v>?G+]9~A 9n :iohsEy9翽>fG(O̖yϚke"!>ctE?e~}kX}rMMYXc.Y?N]4܇_ ӷ43`bϏ~ ݲ{M^̌%.Z i/<6S`ꁸR ѽ*u~c` ۲G\e2֏hL۬R=T'Q;C{]\: U_Θcʅ+0t?c.h^C=:_?=לޮe,06p?2ߏ4Y|u~>fKy?iiJu>_X5@@\ךSp<_A=NȪɊŪt#c-SpTc.ꀴo1i}hUvo팏b͓{vK=l_"L khrܷ>EfM&퍙&wuvGw s@XۿSWvJ,mJU-wS޿/'s8=t)]fjKZ}i=g<;CN@G4Qz7] GٟB}9_Zsۃ2dZl]_r2d?R\ؓcS {k,@ P/vjBy?Ħ p'<Dz? uȿ_"9M4- WGA h1?>c ok~M}ܯ.zMƸx+-w3EL[EB5j82Xxⶈc!3|}ݏ1r aq<|S{Vs+] c@Wx~~1Jb+/fN{( psݖV!> ϴ+oJt_5lD>b43M04~^e"6U" <xN?hxLטXr?4mb8hG 0z _;FC(=P<|y93d _P.B|A46?}qVm Z"˸ۮh1yD<+0*t?^6xk'>C NُvfvW0懊hǦ῏#-5۾eOJv,aOܿ)]-iXqob{^1-WE̚ж1e19SK`9oJҮIq=t^#-c:z|8;۩w`/Q I3-6?c3?`c{~`y=m'rַ͎?6l,:7f>?MCܟ&Obi?׸ӓ>U@;ه$PwGgJSo_NE{9H@p:| w=p o.LY?fey`??_~v;gi/_Pkϊ9&z߉F 0~] 0xg"n߆GjOי_xǘX֎ŘE@}rõsOs{fzq y}d @jxab bVGhjCg }@c{]4`=9dgxsjlk[lXZֱxPmw|V SAdU[jMI/$L\s`(7q=%ꈛ'6??jbvN;3tշz1 u$?9Xnk݅+5mvu@u5GrVTq,==;\8r&~ij4}lS7D3Vfƛe++}GmkX p-o)3ۜ7'O>~oc?=_ ?j0k#ÏaD(W@+n\Ȼp;x{oWLiGxPm/U?@WPC_|^ 򼠞 ((xU>u'`{c~=`3q6!eQ<|uu]o 3lbc2űf >lQjp>7J7k3mg[fL ev`owg0/{`1^*[Ӌs5n->ot@~k>H4!J y n#W(s nn-lFΘz[1:ƹklhje5 ]lbrH,wTs38cYG0_-Юҕ;Z8IexL74Q,1/ɵfMᓯ*ژc[_\@ ,𜀑 ptuGf'?`c9VtXJo~n:;fZ /3?]y>_%,{/_~cxE}F&Nt(^t}:35m6׏wL\KGQ@c^wu'h|z\ e8Xi`g|M  < p p\3rct#c%ˬ3M]윲i<{^2'[g+)O5@['#42n 2\/Bs7vj+aY?C?c= ̜rœa~ק1픔WߏW' CP>\߿2d!@8VgJ<[Z,گLe(ۓf?=_BF^Ou|3eM&q -P0֛M_ Ͽ4ܷ1\d pKsWἽ/cPocG@~Ûo܃q{zxLW3o>>}28SI}0B擾 vo@1 jy^q?`,Ol1e'~`x-gDx͝:E%6Fǰ:V`_-<칳dl[] }l;vv6?ptOv~vvAXsbiooju;x~N2k:bǸЮW6}n#v>^_/) >*lnO1>\w.˿\7lOͺ_KckW+: >6_ǽU0'ܦ-omvڢHqލSl e}^]b=Mg|&\Ao;`?} /[Ћ%O+/'կ\xpK N/֡.@GK:V~྾|a1x@耓i>0{gܗrSfЊO؟G\0ʷqj?:Op3{{\;.Xk~ѿ}?oaW4c;33o^#6#}:dwޱ1 7?>'G3. v:>0:50 c~"{I`Կ{-z9.i~V-Ї gpEh. Oh; `7u{1O?N*ۂ}9/ͣ﮵{VmP@YOOן(kʫKrg+~~[?68Z>-ﰾ`,?==+cel/}|``~w}{ZKJq?c#">1lha#ݺS*6/xӛ ۉ.E ,цg?Me`voH3v6pkϬ"݆j_]~u_놻uN`|2B~ͳ?9:iέg"cOScz]Ϳ_6}c_?~=l`LfZ~2>>HZe`8'X 0~i Hj':(+ ؏}/j ͵@vՅsx`ڳ ?''&0j=K Мu` r?fǐ59gHzi^W}~XإjifC /k:GrCzagq5q;f/|E87y=hg. gX/Ƙ.+ vN`jŽQVk μ l5C_j4 Ѯ^ѕs3r)#_~Q'; ;?w*i\G;>O "WU?ˍWP؟gb|俟4o9fZ`Zy}C\*p|߆>: C|֭89`9g~?ю`>>y.V/QXz=vЍ.?7=ekw-Z@ (j s13r^T95g{?uӊ * }t}ϱk56~+qrS H̥8l?B̯m9&u1x$7ߴoh1E }xdT#;7x[}] #%kg5j{'Y?2_-㴽>]׿7~;Ei}QL?U@\v^u Z82?zl|vqq}!YI쿷j@g'V㾏Z[T?yaZoWsfk<>g/ٟ{!࣎돴Mu&Pklhwm'!X߷=rp?m4(تDž/b&֝O߮~MK/K{hjc=;_wuֺ`.9cޞ Q(r?}߱~Q𼊺Gڀt_e*gKC+=k}{~GK?j*q?+5o}vܯyOdso:!p5  e75?"?"!=<#p8G-9ߩ{O޿͞>؟DhOf l3N:xʸ{O?"7@^#`[u};E?PhF/ݡI<@5ﻜc7'`yaN"W 0ȹ?P s픡]S~}YkOss<D2o/޶{Wmk6u,cZۜ~~|?= ]7e#/}/g#=kecE_/k Z4p(]0 d~mTb ߽8k,7mmᾉGP4iݟ>^ _ZR_~4h5ܧy hFf u@ `V Dx-Doy-=kv?ᕲ.wx?ϓO7\JlG_[sU d>M%e_qX{K}-Ѯ=h/r_z~Пe~urEw}`/'ݤ4@#\[IM@C=6۲-`vp2}{aB@b?'kh̼jz? `m0u4P_؟} 4_~އ??o (_eٿ>1BJ!?c~Xsp_4 Pz[wq0{[7ݞ֩>ZӗX|G&yﴁ^`ޠ: Os ߸Vߛ}t@/Z>8hAw_tf#hv{o^JL.ۨ^ }mKTsF*-K?>k?}>`{o|6b _k>__i=ۧ{_o1?mec_nD^i<Ͽs@iDؘ俛aOwMA 8akN/֍PO j~~:ߥ}}Wu޳by Zc@nN`'rR\ZV"v{+8ƹk٩[Wq?ߖN? }# 5ecJQH`UOWsGem9  }`o޳5cuꑘ_>0&* `ZQXX?/}̽+JnM|8< Q&8pN ],4?SRO}@T/ uX^Q3tYua^3W<@s<@Y`Py2(yN j5 _}~{|Oɮl M1j˱=ꃊ+??C1ǝ~Ƙ]W\h{/쯙s5C}^Қ~^! q0=oAc@Ӗb3_y?Ep>`u0TǸ-05d~|Qס Z@ن:ޓP Lu99gy~ Vm!PGwئ |/|.؆:LPfO9;aܩ|?>=lb#6oG;Ze1swǸi Iyv'l|39W7|D_쏼;wO0f?YG>Yp܇`~o[`~n.`AE3}NP`-& pZ!Nj4v? SI )뵯1x;7?i/xv<O=uC-<PD @\@ Cu`W *S|YYl><_};?~}J*6:]RO BriǸT- G%ߦaQ.PF[cqes ip17BNXK[~y:`QQU_m \ 'd?(1/O}\hZ@|'ڴ^_P@m+4@'|J9 4'NPX?逘`4&><0>&S\'l}GYo# K##93,W;2V4bO}r>^ TZṯ\@UK5FeZ;=C[x߷zd>ц? 5}<}6Yܷ&g=ڷt ݄wƱwPMk^^ bE9eltJ,\ v ʧq>O,4b<=s:j_saVS'\5X{NE_|zw逺FKD2~h}ųr7uҘ5~ p1-o~oY,_Y\Ч8k_Ty>^IoHfbW!G!wf=.awۓf5`vu^^oͿ`Že}t=hDi x{@hAr unn>-5: 5@aN@L9d]{߭:;gi]uBiZ}s~7ٟlc祂5gn<h+sQ@n@<"[`7{&Pa'9EM&` f6G{5o c~hqPLS <8/5@1#09L8-г{\5GO֯߻ WgЋmb`9i#-Ce~lSk]~i9Qq o=}75IO^R\: БSIO1?ׯ5a*ǯk?i!pQ}1˘os-k-n}#}Ȥ5 lgEu{~Cn@N!i/;- ɹY@b}osdNf  {R @!Я<@gt?u1=9׊~Gpmr?Y  ,ؿd?ÿ==w!|ްI5@øߠ ?>cd<`35XYo!yz{}sn>O@c}*>|zUQ?k5 m@*ʇ+4@諅sNou[@N@U ?~W|il#^m?\?R`vr&5@Ь}N>2Zsz=pޑ?z,?\{;-QǑßCge_giu+[PvE翏B-Ee|3[?fsoaHLd9؟:Twc_?.IR(/jB ذc脤PA8pE6w'l`90scj8`t׽ٵϐ Q4k% W s ڿWڀ} ?m= _{ogI~9/pǥ|?xĕ{b+q]ew~-^d?dǃXib';EX: y{^|1ghяi<1nرy^c=C/c;l߭rW]`<pFy~9L`};=۟mTKu,b=~ed~S j䰘t@S ꀨ\Y]:0L*r4ANyp+I {c*t P81oA|xQ0\ I+{wyٵ(S䷂(#g-f`^B=6ߴ=M'].e6k8+X}Os_5祐cw|}|D_5^r0-/Tb~Öa>oP<@ 90?'geI۝ݯYr*ޏ~Km _r('|퐿JW sv]y?v{».@rsPcMSjS3". W~lMh/h ,VtY@uO)e>h:}_>zZ.i;1{awfH9x~~SDx默Nz!k߱3/7O퓼'5ϯP{-Pj%w5O`ډR~9LZr;ؿ#g{K5~d?2wJ^Hy~*KHӋϓ.,wP(uyWW~Un_kr."ɝ8_\+vo|);"F[5MCͿ\crÜ8ܿ>yC?/>nٿ JNsC>ݾQoرtWS݆kf%%z5/9ǺVGw[8~ezj{{ZkyzQ%c̶:F?=u?}(;JZZ6?E6㋒Qʶm^Vqv<柳N͝D/xVNϭ Ph#?\)<=w9{yp9r{VxʯEP{/feL>gYKܳSɺ}x<1/sYnmo9(ɿf{{O|޻-c:fm-y em<4LG=v eQoR^mOԽF.w,߽t?GގKm=FQ)EptkPmŷK/k. {cpgkö3-_=wGo?>4w؏/|P_h=KBߕw ǝaa? 3=3ŏO6@YLoWcQs 6K} q# 3K؝sv~NQwU{_F S p%*Q(q<1KY/2/@3ĒL2A#wB 9|PoZcR7j: g/-+#W; qk{c/c}f9iv?;ƚ@lQ 7~s耙fW_PG]'t'_PC7ֹM3LmAkVb?{T-{l#7z>{ayw{e 'o?n? Ou`g{ @ 8;~E}+`d~_Y(M&ak|S &W@1?@q@vs9<iY,?{/v?%mWo+߿~>~$G#G?߁~?hQ4|h>)yG_dܧSs??w?O=m{Ϗp{QXL \Pxk("x?H3E6Rs9c/hvB,s_514A}qXԋqf?@)(X 5rz=}qc!1?~S"%?o%?a5 }ͿՏr?{//m=:y?_8[sf6ۿ|R/d?@ 2ꁗ2Px@Uq?ܯmv:-r_Xtu@4J|X:ۿ}9mԜ~fKo%?+Y O_~h[.4@Q&#4'OK?C#ml|hܿOϟ!5:}S{Mi[>k->#} 9dJ>8#P @!&@h:`x{ wL 8[sKlW@X6mcFux=8+R?pb~w? SO8??r?2?*bb?O.@C~<4~d>gg1~K笏1=7>6h濷Ilgg=>rܺ7vD v:\=jniBd:XI nGgSzDy$'vcn7ދ8<Ǚj`UAvY~)~ԜGP|5=_gDzܿE#=D>ޏE5 m~ޯ}rXc>O40?r_h߿ _mln{E'l1/}R~soEMQ@>Ndho8@g yHbm~3ss;{iPs6Ǹ106`^5A L:Ph/o0OgWkgϪ_yec=EkH}먿ίu9ꃻcWRS~/ھ$ oklГ/}\6qNsqc?aPWkODhx _O3\ 1{L&f tDYP}!/;t@Gn@^u]/˛G/e]D!0i&y<`Or?}. ύqHjd_mM~Pcs϶'O}{̟l(R'^c r%oWw{ڔO|6}SI<#ƥoG[yLӦ-ݓbTs48?c>h6Mokhٖ~n]$q[,4dy'񁍵G$ k~ on3?-nb+h w~jS/ڒ~bU97;+ו' \Ǐжo6?ߧ% 5@5e4A?~>k-~ֶyM 4/lм@ ޣ愠fbw4g@܃R(3 }9DdR1})Mnuߓ&?cq+@k|?lų_Z~l:{߭P?q,pF~/6?\G(1]C~h7*'Mmq9y0 ' =z hL)|'U4K%L̿ }? Mߦ4_k10&@f$#/5 "[ wsEǹk; mp}6 >f W"ߏ#?N>Ɛkq5Wu_zWSbeݏu5Z5}l E ~o[}Y._A9W};OO8~B}>/ ٿMZou(W@Δj|.Ϟ?~Ɏ.wGYjk:z`Ls}Wgȁ[yc|?OoR[4nM}RoCV ,wbs?}AS:yεtS{͎/ h</5{Tg˜9)ٜ=DZ=6N L h%__I7O^侔h gvb n e_|='9Yg|WbOo{:2u?/b~Zۥ͸nڛw)Zy_ظzak'x۬m역kƱu/_B[a&P=P+]PiH_`6V@ 1}Ns < x lbGh>c:"mK?_^l`dM;8 P.4(і_;O?}_p_!`'k[ؚB޹jMo?~cW_?X^+nU׵ _M_V?͗ү:E؅E7G~67R֚q/r|Gޯ)ëO@:-@4'@4|:9 l ֫!O5@<hK;-b@sG @[xE[ K\w23%/ZGqq{s8"WA; /'VlKݸ5}O'2'n/} l|ٮF[KK}lGflcK}g7iz۴M.:5{cM08O-G!1c >ȴN2?xEhL4}N z/pؼTVWG>G'LO5 2s!'`& y<6wW'OfO؟wҟ(qV}o9 ?Y u|Yָ?x;iE%2Oؿ-Ce}& 7 `2\ܗqy3^}*&>M?3slxoxX%G?Or :GwePc9\"vސ?G؊VP`^|'}es幹?X@~{ky/t 06;:(w)nj|aMRsn'aW { 0t/8 (yɼ) hbQ v l+}m\S_(e8ssqo/ls? w~Y ػc3j hVwI;loqy]F'{}G.5u߼G>?A2PY;G wy+ߘs;ۋa뾀}{9_Չc@_qoa?ԝ6f= >4n` i﹜ o7y_@)nmuݟN`mi^0_~`&_hTua_L8 PP?`k9olj킹Jig0= W>gq%Mb 9=1﯈Hoc6`?2ߕqyYc]'T} Ό~u& O_z8o} ~7žr|m 3?񾨭6;cqB;ka뛫. _@F &y|r?b 6v'` 8gC>9}^ :p9/wv}wmrk_4P{rc;2V0rvv>hl-48pL@#^}ⲯ΁޼J纕k?k]G?!6-stkr-|ESWtVۜXl м@;a,F>7a>8~D#]ff'NQh # @'Xl #c`:g޹ݷ*'_w/^K l\zϮ\_5@Ͻ6\M؟rFq2iޟ soogmgN{qDm\bN:ޝfm9h<OGKO c~ G2l_/S4A`^v ns|y=|y}W~rӮ]G]$@߽۟Z&n5 ̏?Y7bj?3k6g [KS&?|z߉/`eO$u5ߘ~`[qkc}Ji{6>?0_j7e3cMir;of3ٛ@WW}]~b}IMfZ}P< uN`{.nή|V>YV@pn 9?|/8v`[ZZ~DQ r4q ` 8Vw, uj&+sXP8yuѮ&8E_q_c?nTTq/>}?|?Ͷh3}ql]R~2cj ]!_-eE0\_1bc?+ 0EmK1_|/{~l}/*%l W@>`+Ohm7*17<@{ߦ=0|t+_@N7H-N%֩y~KX9 qR;^tW?=g P' ؏=vt~V@Ιa=Q8 \s \v39g|u}#i#12J6J}ے?>e:wWkr})37P bph/?1f1R{"<ă"?Vp\0_ Dغ?C9qg,G cOtRe&W&۟[0@v`uI/] hNiX ~O(AR?̘>NumA0}4r_/y/A_97X|X/`Į q?l2wrU9/\#q=p5ycΞlQd}}r7;π:l\M-V>{U{? ~χsy? C)L%p-1&WLK5kZ88_4 @ıE pp87Ke;\~w|}#QRMu.7(zmg`s/.K ?pLkrcn.C#͍bA?V 5F/[bC}Oy׉9&PX7? 4nW*cq<8 P  P@KDTPq~cQR|-+<)lw!`2:K7-w412p~>|t ZDW΁cor|W[Ǚ)/ef@_/gz?ŭsMs)|K}J OW 1oU .PJEPtڱ-ֳNq}ov}{ ?׷Gb@ʿ}1sz=ViU05>iٔRj| mZ@6@-`}_G/ַ~lW2}=d @~ ?@KC s'@'b43* Nhi />\ezvy։(3D-!SxלgV/kkWgrA$7,4[3]P>㿎3`w+0B|/5aw__5055@E[WHLJ\7aRܧK?hfO~)C_˽}1D0n> ,bšB+X~9fg3; P`{Z`?] PY?|c94iڥ4Ur|7έWƤv#qX |@b> ^Y,@9_GIZ ){`:W~~O|?3۟}eO߂_Jooo 8޿(zۿo>|[ZZn}קκ@bv^y.O_G}(Zy`gGG%0%ؿI\|/yojyohr/sdd~sC|P5o8@1_w򺀣X$*9s rV5 fK,@lx|`NI}\wStNvk׸x9ot 󗸿zc;>j}]}fmU_Ho?rw _5oU-La^l8y` =_dz>:m~`\oެ[L%[o/E+;Ld/fS2izuͭX@x?` :Q k35lK)d@SxڷCT c<޿)^BUp. 06(] M dkH>_|9IglOfas,?4T#wu_ 5@mmj3U it9rjif5?WGU{l+97es~(˚g: ΃0: 1>^Mܗkԇߺše`U ^;OC=wO/@f# ^PXy?gxS@a}U|F^@_/?X@g?P_y]5 qO{=}!*mE. Jea.H[#U( |6Y5O_Fx͝b󨒭 $;=:]]vwu[qҽA;$[uAB_⾶={JoD^nq[]zKj,G+9QK/VkSJVCM&v'dknMumvERx;S'T0d.+[VnJK{+z{9sD.{u#ͅB`68༩u-Be%ײkz%(o^?%~_}w,]!uM߇7^S'L)G=~-?~OvI:wb}~|g+g|p{{kվU_PJ}nci{NDZwX>1ᅷyɸugIԵ9sZ{~_K=/$Z66rω6KO-?KS'B>vd_mrB}ڽ^ۇPw׵k?͓}&["߳}xz챫ijHgc~u;dgoo /=K;U ~LXwkۭaF?=^d/]4@_y_?l~ҹ+62MubS.gucwc~c~!a| GҘߵB6q_~wSJ&C/|f;69Y񿶍)_0-j;4'?=Z3e(:h,oE5@+EgPKcs Z`s_Kq߷@_UZ狰_uW ZA|߷_i= ֙oEu?;j4}~o1 ZI? þ.m%FOW>~}EY.3~Nh/[o[{CmCkh/nnد}jBkfۼ07徳yOn+`[I~|aR'ouHݵS՝lq_ _lm^S{u+/'Z@9Dl ~BO p8&'b7_J+K򿾷`}5@RYY`Vwc4t^&dC|}D?Cmyj:{kۿo>CWyiQ뉢ݯۇo5sͯ:g$?  kҏ[O@<ퟖuQM؟'aS#{_ee|s̃ӹqCT<޿̿"13}~ w}?Jv+C/W_?0Wh//_o*8O`?gj{` 1Ѥ6@0OdJƂ}l[|Zj4΢6l;j>~㶶 {{zG)w=X8>{iq q/gNNe?K`?2?~g~y A3꿤 [߸ueV?:e2'{N:c5s >1i[~y {S7_8}_S+T~9%&1m`+#VU]eĉ (W;W_>?j1F ܯD_R|_WS7_/w~?>b, IxC:%6p&E?;@xT PH$mKcu8ⱴm$ۮҲ=kQA2/6X  ( >ྀ:@4@/C`q@G0X@X@i`K5+\4ۦeh,r i |#t;2T+;w1P<*&0t]@frUhOwl|/ھ3s~]7>phdz{h ̟4@/ĩ-ן}?u]rl2遘0-mgcUyfY_o/w[8Վ8|k>J h ƨO~h5au:&ίH9`h7nl ІP֖$Ȣ6Yp}-?<$k|槠}-@) (.I]XR4u:-lqkZG6kg- y<0} d~xh~S/?P<`3y r`;)OUbn#Iql `qC|G sF~Qʣ W[nϷX7ߑur?,mweyȯ3G[_L;~(REO$%W׹T>$-M=Lp_D_R[ͮ5k=g=}]#&H/@^dlP?qYC$ ` W}|oZ82zݬʞiG[~ As ºH `دCQB|C?|4]N<Ƚ H!p/a -*'Ŏw`g껟sK%s?e;>žg^<C$ߟ߯u34dghOR1>~@?䩐7d]r?9ON~)ѾP;G.۷R'`?"/EMwz&kڠZ-<2CuzחvkbzK) Ua[3hy5"6 |h HdӪF;§v徼X 99i$ qhl\d̏hoMuiB6he`snw?|1p8 m;i_v??W~0`^?yDưss1wIZ8q"OC_$>[xg*> +_@U-Ɨ =T]8ǟm~{g[f}9$CQeV-oV&K}dFs%E?3S-;+]{-5~u.rT k *?nZ@Jı hg:`=Ok/`y :ா?smSP9] 3y S1kXDŽq/Pb\dhk_9kcB7ڙf͍cD BfOcg>'_z 1F}p _`ߗVk/f㚽ux,mb 7`X1ܲ?b\+()Y~@)t_vc?SkO$m>2Judy+fwLm{}aj$%/jg~鷜b>/nߕGն|?I|sCp2 M65B+E e'^yv5Κ8z:o>t4 hMs?Ƣ.Z)h&[$Asl^:@r]P\`T p6^+l4ى"1 0z ;"g%O_Q'y/GOOncLI23W-l~5joc-U擱~J~d~߰̇ߦT h7_ٿݟ?cU^ŹO`\ՈשAF@.uyZm4_} !>9ql;֭^еКk|rx`Q;9>sj9f5@js̘O=_߹9\}߄r/2;B˹fqO4em}߿韟}nSgrߙ/vM)n:fۿܿT0SqD++a?0Q̿4R9:W0^Q+q >蝸rI7u@䱹v_ P }_7FG4@A 7֜lR_jdާtٞ% ufJKc `Xqh0ݾKQZy5,?/~(WgI?0l9j,u4E*iտ ;soߺ߸ʟCq~rFY9}z73Efk>+_UwN{9[|of?&ߟ_{GA `ԓm~nh >5VV|j <'^kx K'?cK1 >hx`(k Y}1lxl,Ce< bFZ[0 (>NxӯNoekI!ck:N"~)sKb^r~.g%e~g돾c(# [鲳ݿ??5ZsAߧ_psFC'닡kpXVy+`@\&+e~(n.3m./S˻h\=O@},>p%9_\Lp>Etu^^7{107h) `}t<@Hku &{zw?&pz6=#ht@rIIר~WmsQ?O-`p}9Ɔ\OTػOGvoR 囯$y>^"'_,kg|-Ă߱ obWV߿ix!N=Xxߏh>1m;:.))yzUFό!?gn!@T= |Aϳ$h/o1_5ǻk/yYm_ 9;~*@ Hrػ "2`p.hE{aknG?@|63NIrZo73qYu>5`X璙S/߸)w_3[`w ptY_?/6a_Oָ"B Ƕ /دwwOr`ٟovpzeXYrRv"e) ^ lis7wKQ-<_q,O^&-84#gJ>~lу>*SLJ#\UhC@ 305&ǻOu+sǾ2K~X/ ?@__ujNl[G?`7S p~d 1Mޟ%v?`]o")|~-?u3<6`,)4?atg~WlW~#پkAO{!I('ZgD_9IbLy|FOkG?].ߦ$=|]:v?)k/뫤1q kǴR cIZW/F!`T~K>-"q~yŜC+:?Dr% $TK< '<+st hCXk7 p L|g Q! ld~$-DDjV߽.L 47oP_lm˱D`1`g?]?o5oe8pO5~wָM/Z@s{#ebl&#Q4Ɵwψf7拿[D~n3/%Aqc\'Wއ\!ut5}6} 9m`#B1@OTg,Z V؍u[ξfк}I1^bxn8'೰/@55FY4@t+@ _sק%r<7E=m34?gVl[z0Y^O=}oe{ةJeqQ_6>+6?Z#Y}/ #I_]=ߦiܯ?]|bW#cru`?zjWu}Lmd-}O1['p xFOX_?|v:: _@//:rn0[EB-p;?Ub }rHNQˣOy?_;^>ӷgr\V.;9CHi|6~Ї_<Kk|]`$^/^??Lmh#Wl鹾g pOc o?{lmWTm7`.~c!(ҢɃ+>_|eGN!Om}f0wc?Dմk, N5@:xm !5Ǽ<_}Y4x ssB>;٧qWLգGPu 4>|^''xmeo3MR޾w?se?u? ל?~(mk?1fC{U㰎xߗA׃m_?8oN!_ЖwXCd~}Susߞƥ܂{$v0? W3?C3=ϯfqg1h\7 X|k!~6>l33i.R`ˤ`{ z|`!~{1L?>x_[ɾ5@/>En{.X >3 ϟ$@\=]ǿA`r^=0jѐ @/;{k`[|2q}FI﫶%2xjgmuO4$'{ܰ::Ǹ~W'aCd;2߿^h!?R^-]߅nunHEٿzd aʸmޒ?6>~gki]"̑ߴH&&u~o29rEnVW/ZK9uc~-[yh]K﷭.X[06&-nx=G逮nP9 *,Wt_Jg]|}i^>hVgh;l/Q}okpm.C?5~_ 0j `#|>m}׶lA̓?!r_je(s׹E~x__m4&@95K״s܄c~;*~`!y&7\^G+~&+ՙN>ZT])q2IrBܤHpFiu ':$zx-zmգ&5J_e`^ rW8 ry'5Q-4@MױkROk>^4~gsIE xT#p߅\@-O)h`=>h=:K^ۑB!'<>6\zo`+ +G9#7ú[?hJ|g.)TKmNZ/~_c]>,>]?H8`dg '{$~b7wf? w`ro7E?DXu}_pOן㳹k*-cөs tY?8Rğ:`ۅ-и/V<*$wjm{( k"^\J/z֯>\W_5 -&z.j-GsM =q")o[9 XWTjHb4ixv`rӂ85K-q|828 Ы 1yi5yN+>#}?% ~ml q]3l`pu^3}X'9_}b`˓zlE`?E/aQ~j&쿼_}ZNJVk6\lOf6[6>C`T5ڇ| Υ׸} [vQظ?7[vz>$@hڴe:֠@ͯxf5P{dҵd1 H ,i55ls}eNϠu@ PT/] M|@{f@I 0/.Yk`7D@  MK;x -: `@Ekb_R1 gi׏c[_6C)ǵ~wg)?$cmy(wP0oxGh{n?Q~grkWxcN>pCa5~~e9ammS3&[?Qg}Ϝ/e=c$B;T>qm?~ߛ&훩rW-$ ,߯eи˺~ثo`/]uB=RX/@}nW@ @?SVbcE?ZP5/ PX|{ ?|+;mWJ \xϽ.mר o? w9='|W ;]1y w406 b#G?gޫ~yQ|x_eNDs~+~Bz~V>+m%쿿_ٟxIٿAtG=/潱7>wc?o ܷ|}5?ϑ#6Gbt qR~N?_ U8Qht?[`Bj_Izo]浀~MZ/z Ⱦo[@t ukvL\! ? _lXW+khjC5@AN 8i1,c`8E 8W&އp+C7R ph,. @w➀' ࿻">Wt Yy0ox|ni?k4>?~H y}Zo/ܼ?:Azx^7JI;o^3c綿7U}~Ƙ'Y;\m}lڇwJ_ۯVV{gl moo__;-u{_Jː<+o~}lf\0ߔyIkX~QLGw)%~/Pc/]DT: h%3n|M}>y׹YI<'k7t 5SYH1h{=/wwϹZ|!`a?;>? -X }so?T;{,&e[ ?[oq _lx}z8&K̬ݷܞ}ٙLx` )YGdY" ?NWp_ׂ^}:rnצXkn CcZ\o{&3=]=9Wy-^_cˏwn 7^}Vhm]wqnu8zx_FOAw|:Xhn /gs ׷ Ц]¶vծqզuͦqM׺i\]׮jn^㾂eZ {ۥXk]y]ca y8J ܂%=S?a}<' M@}%렒ubN30޴vA罌ȭ 7ʊzQ #0{`.yhSyl8fRlNC0Is}Op'=\An '' Bx_<1!UO:q pO\غ@s~#s_?{ׁ~oׂ_+_x\\.1A^TB&XxIÐϟ{yHnM|^Ϗ_S0WcMZU7`0m+9=a(拌"yدrg|QmD_D ~$3͗yֶྶGK/[NX[1y-<` nA;_Ό#qy}ʱLrH=!=&:1c ]k󓜋߳0c,xF| f#3A8яs&op [ ,\@D#<RyY`Ɯ/S#Y0yi3y.qigqSE9-O) _8@?rr/Ɨr r8ޏ6'B{Pf- r5 Ow øÜ|R.?*43%|cuU~A֓1@ZDW3_zy_<Tsj@Y''G=G(gP>7]w9Or{0iN-` =E_qW+KIaOa#_PFčSe_am쭼~úFt}}m[:9uJeȵfaC amǾA/g@ w=u6_U+xы*ml dBN ѧ5,ܜ2AmmOh y\wԂ8b X~8\4 Å8gpA;@q0Qqϟ{Q16P|)x@Zf}6wF? pl0%ϛ!& 4< W(.mKm07bgܷQTWePbGtVq;7ץ "U ܏d+'N / 9;w 紺۬?0*D쇓?Ռml'Em@!wзl~k']}c'@yL>b?rWnG8 /=KMlKjvw _}dS,M\ߊ&{hDŽab'1? +'( j13gٻkM)CH۬X‹}tB8<ry@b [/8@k8@@G<?q*j%?X? X,&?ʥaxbv]\ml.u9Wm![_Pxu|_Њxf^"SO9Ï9Lk"8qY/<b C{+ςǎQt~XDmt?jFܟ'&G֌qN!~{{\ޟ4gBܯY!ƣwHbdT_vͧP}X8m^~!>({׋?k 1q?aE{sCJPڍi}x<-P`I6ɱ9pO`\[%U?p.4PX I @H$z’ސ^J|>EՅ @22?l?_\ fb\68~p |} q9v[mlGoB`8:Y_goNPK̟/|;iXllZ 'aݞldr̟ۚ_e!`_#Or+ ~`o9&OJ|(Z[k>#w-=Ř>YL\f|l1?1Gl@NLSǾc~ȼڄm %~@31{~h5LSk{#jƾS,:E>@7H`yV׍^ xY 8Y'uys``0-pNh  pާ9_?bΈ@d8ӈvSm9f0#1Um!((mj6_r=, `W/m, ߃Yo_b-P~0+g_?V!n f1{xg1\<3oIxcH>iQG?#Dm1. d`P(=k6ѵ`%'1\__OqMQ9< lmE߿Wߓ}a%Gd]Og_? hAj~3?~aD<` mkZ<)?Ѻ{3[= ڪ9'b$\O ؀7`"3N<>SpD;Nu]^vb`0ܷ67rG9O] pV;@!h3(eC11z9?^i, `t$?hb2 )?IqWtcG>[]E ㏒H/Dʘ?[o`(1vf0}[ LkzΉ)T@bk._A9 3ց( P< .' "Dp.c2'~bf6,PXm-̫6VV$b ^ﳧ{WHm G?Ec@ߵU)󣣌+8e 6Z>z#c@)׬ / 3O)u`?>p,\?Y.Əx?1fM36GlG}#zan " $=8\m  ;?:K.?qL%Z`@r9̕9rF\ۮQx'@\ca:3PE;_=`dT19/3w g+j8!P_@_'&;@H@s_x_5e>8{4(_j>r?~ )U@ hl 8@|TR Wx;7}O~U/K3Z^~,LZ?eVlSr 0ޥ1IG?{1:HS?C#+ /E=5`ƿD/h?Ę'Crro_Z1ϑGWru~;{\kG_ͰߌW N `IpC 8^~*MW>3d6@12H? ֹqΜ`ael8bj,@P#oݫ0FT{qN6#~O vME#̗1Af _鈪U9(ڃ` Z?LG,09@f~'uRm뱀rn`dpk`g%WRJo}/-%ۿ[ t_uL_cb4/7~Ӳx9+$',gM`_3`M7a>綬ˡDxQ'٨线Ez_2`Wiiz?#kQ_DZ6bQGt~ob7~~!Rl|h?X{ qHz\0x[*dGR/񿋾A| 42.$BPŎN%+ `=?-P@ o q9>=-8T Θsj &0p}}5p00!}Q"\7tRp;Ecb0@Jra*#=/X?@~ZY&&P>9l3wo5\.z@ sH0 ԣ9rsY_e9c#Lz;0_sSZg(CH-!~(z3IAdJ:!y 䑘K\ ;%v. S~n8@4p_;U;b @M.oXPwԋy@`]`0#DW 'bu,}'H 8.<-kt`P\<`-0[ to"!&_=S_\}kcS:ʟXݟjx/R-/n=?zϩ<~c}|=c? cSst}?51ϰߗ?O?H Xʂss ygW|L%7QY.?0a,:gܽ8Tn{S2=aPo'hCxJ|dp;1<Gq#@c(D6guM Ǧ?.c/ .r?8Qb>^h|]-6U?ޯO5g[ʉa[ϒo?'G;",ҥΰ?cb'\?~g~k5=qڀghwf?CbgEv`P uww-a/וn0bGGdžraP&I߻p}S SSU:l>|Si y*<{'W=?G% .&׼?-9koZKvKr,?{8찷>Wq둛z_4盧F#{` @br,+9Fϥ!|(_e b,' u?V 8,sx s [܂zu ZWl#n@ޟn->㣏&濳_X˧}1r}?7zxi5]dG%Lc'io0> t3 + -6x~M?N}QoSPh;C a]دWQ ]vQGz~S]\ _VMqo$a2V sXc}}5#Ze3b+sZ'݃7Ĺ|~'jdp{=hZC?֧yЏkvސ4aԆO9Nj ny>(<|>ϪTأ~& pQpy9I=1 @K,sb>@5.`h= yO|݊ `%0ⷩs=`nS0w?nM2so넽S/Kz @rS @1{ԘuW=$߯/%ފ'WϸcD106%?Qc[q-SO\-d@߁<:S@ 4LAB"{wPO-`Gؿvk5F+&Ǽ1XGLyyfkο^lO.aIpbNAWCGuF{ NaF'ڃ^< u*C6k_{i2OU  0P{L'7N_f;WNBp#WZ'ܫl@H5jO7rF(,qd  ong>3Һ_mΥqZu:vvj6'1굆O{6k6Njjs-Oolq"lXSZ-%dXzŭ>%-[l1a=/S>'wEWʖP[_sVo1t9S K>7C0OKߙIǚ,Xg|5,Of Eղwׂ- !=per#\p 9@皩%0 HXq i>3m;r VȞkDDp8$;.@>vg|ɻ<9F\`X7Ph}m kgG;%0v,J-@z5J}3 O_>I/cg9U~>Λ mt O)gqTg짷g1kQ?ZK1%Z('Y q/IO^TRG~o99%Hޒ1*w)ZWa-x !#~ ؿY 7FK&yOk"^ '}d ſ/7m,=_B06}͕f(~!YЇ6;3Q"cpA= ( kE>.q @b۝xon0-+3/Liks䚻<Qy86@;2$mfZݰ!l1h _=q̍0kF{lhJX*̽c.p>b). OF`$Ε 6<!U8\ɞk6ߕ:LZ ̮csk.[ ߙs2j|H|Y>o,M_i;\wZ\ޥ:mt,ט?EMy1=?+2_rݑtG{%)]͟i&,Ȕ.?KA߀B~S>DCȮIG쏶~#4sIzQ(,PôNTZU׊<3ƟA~eY蓤֟k.$^ZBX ϽԶu'c+ 8 OU[,TS N8'fp!l {asbC-ބ12+q`F;@vsOʩ#Oh]5BGHN0Tb_e{iP[V?{ ހU \Qq`n| V8z}?Iۿsste GLw?_O)oY_9]x4v11xԫ8?;joc˚#)y&jzH/O/!M0hFk g `׃1؛/ـݑw6zهU&YgØ;oom$G-V=ʌ+,׀m5* #z;8#c?D~,FvA~tF%!\Zw Z|{klx?Qw͹/+h|J~}PgYbI{\C` _600Idj)[n8oy'wIqO 7υTkS,ک0u @XKo! k$eI 5O^0tcN_(fc\}[t0P1Spn_X^mEͿYߒ_f/eZXÄ iJ?ҨqeEܯUZi`'b LRq_vϱ~>bqϕ%!O{IJ?vѱHUKY[Pܾ>F=؏>)2Oc3L8A9am0?}{^07?A ސX^],'p=`=>ݣuϰ4aͶB%??{g>l/d<&ޏc_ߔ3?#X207_@V^s3EGWݟR\; _Nx`@ߋ<6!25b_t~b~6[Zɿm'8A=_DwR[3ᰱ|(PÃ'}b٤z.b: !OFCxD!яS/=j<Gh@<X6L}x8@pNkUOJ>Ȣ~w㎜O}{OiK5oޕ+(p\DA L+rTIT?;HN>`8Υ~&`UeIYZN~kx9a)6)6}~BӶS 8>O ~MΟo1ZWbLwb71t!^LxS=V'r{~62*>d?r .JvO)Xw? π#`ؿ!q]z?l54[¹={~v2s4-VW,P_a?)EpwnV")?0΃ _.аBC? [ FKr% |_lTD?4Rclz/>ܧdymOkx?O *a?`ޓ0'C 0>`aXN`pТQ sAl?lrn6ڠ5`BU$_Ym_Nf^[=15x!{~7߲1Y.@7p wW|\Kkspn=zS}kX{6Ϫp|ce-I)/M_h}73G{=vɔ*M1￑xVƥN6 *BF!'CqdZ7byFb~×)`ؿ :ݎ~~O~locӟ*wx,A3:~[ 7 !# ;kOC;` {CqoX#'XׇjԨlaE@yA94=dt/9!$5Sr(fԶ2?`+蹞@<=zc@;|s8N 0>?7 xZ~Aj/r$`ݛ=PN\ \[ 0H܆GZjMP7kh?nzӽoW} ^KؿWq_{eftI?C9@+9VO~r()gS/~{'ۿL|I5UZ؏Ϗs_:;9#;5y gz``t/P0# 7*~Up*G.Cr͞sѦ}}G_/wVy`\a;zQG# 6a%07@h@& :@a=_@VDp;Gw  MŒ5|#b Ov""ޞhkހw {ܳF.9@e@sq.aKv8|[UVIL_~n=(̑̾d'N 6aoo ptO,ⷓqtZPlK}|7 s%@ǒ91Z@s8qy8=K-/K^?e^0{ntL+hXS9eS"󁇹~OIGjtH@+ZAϪ8݁(0sPF[LM:$OD_dgv+bu 7F'_ q7[%1~R3}Rͱm@}?ǿU߭}U[r[}@}}D{\jw'v#bH\wnxqghx̽ 6hdu8ebne{F` <=cpݴ_DcsGXKp)!g̝84د2z߀|箃99 ip FLaCsBOđ⥖1 h#U>q]]X=3Q; d?}(1`1ʃE79T:z@r d86>,|0z \0ߒXiswo1cWӿq-Fݟpbm߶GLfo#huE\gM3(xKnA)C컭Ap5> s ju&5sz?v}cO_@7Ao7m ؏?6 埵{{`Mu/|iwYWO+]f9[hG~x7;2c"+<1֯%TH4?a>c?]o2p6HmGŹc=Ⱇ}}o1[w1?K+_ s1^rrn;Πxp?pyh2sbp^Fwzp2C¸e2D%?C_{e~ͣ(T7`0xt\aI>&`*3qE jZ\&|o`?Y.OQCl@1&׻>@JQ[qY_'!u_. E?}~8)dqy~RΟe,؏:QcG;Y3Bㅶ/b1W [?O{ ߇ DϬ?LX{>`vc?.^.'Aq_kkj|V0vrc zVk9齮Jt_͔Dݿ&>]06ׯE2wN|8ǰ6#894!OƦ yEлp5|%\ڲ"|@9!x1Xk>5Yl|~%ebNjH%y. k/^\YYdhVgsr@%AHDȟk"xqPrs# bʚE[ ?Mߋ%NE > QcR `q臉9WOC7?ӕG.S.c??>>pk{_kٸb _Z.u/|Avwv߾ՠ7 j߅gq7qy{ /\)<=q߿@zk g x'4HOeTA䈸@$cu{FW;Sb\. >ܷO Ԃ!(O^8G /nD/CdCr6S~a>p{Wn9;V//~ƍvRqmmIZ9r5pRM|2!U| (Za#JWr38koб[ [QdدR S+ -a_N_mZG}6 q;zX.,\QCe`u;,GsOyGO0%aGj#β/]i/=?<}l~h]ؿmXS="+T {xz/ؐ $>R]r|\ݍ~r k q|4/u0?=80(.lهSlQAv=oZ [o.p]g߅0 d~1^?HZgyrjIe Ez `eu&2[;`}|1/zRvP<{~ޓy@ޟ?C34||iK;LJƼ(wO;4W8W-sDag/C6~/xօmo6ִ[%ٺTcM xl&ox}yVmR 8 `}y6d]ZI5n G>w+P}su" ~~0QdO~x?AdznўX{{kGߧ3d?18G?^מ@=k ݂OȚ oXly}5I6bH_}@Q}܀iO\~PXT#(sw;q;i-k)gۃ_l d ໻srs_kw y v~ 8-a40w& VPsit/?{y8 m׎8{H<ZwGŹMPq7'5Ի w#9 z@!dH>%` gy >LpNwݮ zv }"Xqߩ9 DFLȌIc ^ꑡ?:DdZd ǥ[l>0~y5ǒ>xu)rdr͎6&83$餒$ C1El I*K&u&,xϖxz \`A*1٠]+{3yחFWdmAt N,;+FNDq17S4 GvghfgexXE~_1t0'1&X7; \f+~{-9@s\uVpc992Zr_;ŎȸL}`xƙi)"glL:/b t=G1Sw9:Tl?V&YsI$ˢ]ŢŴ\n/ @0&wq#%`yaF7+1~ƍ%uk )Ƹ3&ķW%Ƒs-Gs+SuϹ?%S~-9ڞDo6'#s}af.2tsǾ><׃̓}w=c~2_WkW Lc"(=+_S:'lZ7S5⾮=~׻<[?׵J}sa֏)֖a|]0.s ڦ 58ѽ۟xlh@`?JCW6 b?0؟x%fOа,^%g_9|@sK q3ekȣ]}4Ѹ'~~1U,uMB9_Oո$gYkm&T' &/ swpu.s;w8?oM.c ?J(x#ظ5^^ Ijxxɾ6)4B)r]=$t &K 8rN;wNϸ$7tmFcx|9ϗ+8ܣ3ri㌩h 7{](t1#U@~1&G4pWb< VnJ'sz1c8@ci}eZ- ;0~ߴ\= `~wq(Fu_bSf|'?945@1?R[ cSBt??7Uľ &~n օA)i;h_\4NCQ$5;wܿNdg`.gy |n3  'ۏ>t ?/դHgЅ=gFNNxU;Āb6p0xx2S[__ qY??K1@D-q=V' /\c?o9':<9~9=-pp3^%హƒ K:ɠt58qj=G~08@3p}rI<䤴Rf@`SMSwoc ԏӿq z7qZƬM:~߶\iP<߉ ?ThU6pmjxk?|/9@ΘvⱩq9J"oWv4pƤcem{wkPw렷o|+Wa縏k5}0Jk?hOT|N}>Zvn밟 |+r9vDʑbZp?I"|Pς$2_Rt:*s\/,d0a= iIk{#F+/z::Q&[˒x2_zsrh<>_c$x/Z/#d{3ףszrRv16+b߄5ɕ{ƷKh oBSοfC_v1TCSc_})y!#w 0{۴Bv*Cp1]#З8^ |g1+0օ^ p 9}YTyh|W\y mޣ'vgPl0_"o<;N hD>`6?RB꾰+mzoĜvU߽@ocWlTq\+.I=ue_߫,G^'Cϻ+֗+9?ԀFiitpr\9K`V"9q\4t@.*_k d 7[gx$[؟QgNrUB3]~dS_k=1#G#^wkZm(<ρ?kv#=f%tj@he%)8Z $ ?<0gp]x<@wu $xNh=?{_xYyq>C W7=g$Q} 9%G&s﨔Q"!h8@9@y% N"1Gl3v#d9FefGw\.2c*Rw26 ~ `sV~`ؿn K\j΀gs؏Ğ"738V} >™trU>`VZ>=|Mm`*ݿ,Hd_< 47t4?`mkߋe[?_~H&;8ߋbNa>Zqߐ<-?!)jl|߅x1;Wn*~st=zݿ CS4քAYǵwFN L8z~ si? ];d$;cA}A|d& 19/а bj;?q#=#T/^lwkhN77:Lm?H5HOxZ؃=<,0 C/ ?Hhw/a{& NPu;FiNqo77U[SE 6?1g}9ܐn8>Pq< y7<1r6c>0bLb]J?18@w1jPw$j cNup w{61.}pg9fbj)^OL'?G8E_8FױE66 8 1mhߺlEp:;?~ro+7.`m_g4Ąeo[sWmQ@V?_s&7V?MtMf8o8b-J_u?ot 4m^ϨlB_z|s? qs1[ekY׹l׌W=G?>߯PGcJs㞧lHsIs8i4)_]"q>K"B'p~8?od2>m];kR9-tq*E뽕:!񸿿@%ަː_'}ɗs뎫+:/QhGIOx̥kxL=کb?p`1I%agnY܌K +3RK_Γ8Q+c8]`1E͌L~pS`ab@&2\Ҏq!1qk@0Z>` co%CqsIZ< 4?k8@q؇y7Q֐|}*j`/ڶ:IMe8@[M31,W̿[7i ~!zi7s~k**Ls*G\5naz 0(eq"R S+t1453KS0?-պkRGa331)E9,D0G19|nj~,{MԬ@r )_s=`X\M#NJv#pMs<s٧-ΗY]o%r>|lN2h/?vsp8mPN;y_cf,Fc12?*?cWz^'ŭMri Glop_?51-s3l?Z*zQ @|mHk5V/>2kqQH5ePaB8^oMӶ1EK?_ojGU`A[g0/qu?K݈Tg}lkteBxj /0t{\~:5?yl=wj ~0]J?>zt_k_o ܖqbemlg:Z~h 0r,f@Y|>kpϩ̯pC؞rSs6m Yok~zpd,$j4OS_oZ>VM oڏKt:@A 8<|l`Ejlwla\A?ru yy@N7U``]C_`4zǿ WgjX`\baJK߬:f 2^_*& iOblE_؟y?<["z֯K1k<U ͸Pb%kvd]uX~d%MPOkx߇NC@~=~q.zShGCut:Aǝd2ґ]{05ԖT4s'pϺۓji@Wpt$勑kvZqAȚ@\B 9 WdbV1c_kH)+=?n~?~Tq| ?q2pCGn`J'R|O LyPR?xȾrN%yw6Kpx-nBc0C?`\i L1EjM q~wku 0Dz9F./ o(jVk=pq&q[h~`(<!P)&>$ ))\8Pb8X:le[sIF+khbC/8@tEH[:}g`oHw"|/_}w"y8@UǘFSK?;kmdZK^.1.+˲?4&Ԙ?M밟1X3Z}UDu]?GFMJx~8Nvy #\(wetӝNй?T2?K=Viggb?kVzߨG/sy5ϔ& _<g'3 Ws7 ~c?.< M?+3 ~Hp~pg<oO\G^vdc41> |:@f @. ?z`cYv`P?6|l @YFl<@X=5kە(QWZo̠cij~r*]?3%pLzAֿq|w_CHQO;@|5q4OϘxնWlVߕ?~cƑ?%^Wtk6I6c1Iq~$? #gw|2[?/^aI}u@'.\ۨ7 ,XXs ǥuĺK0Լ ȅ Y9CjqLYl>w6p允Wc=߻`OϴtDK1BK>8hNЯ3)1>=pp{A1v>:Rοte~?F2jEzLǂ5 zmQyu]^jL9|bH&*F㠸8xW`ݜ1݊| |՞] \Ֆ~/eq`+cj@<@I+ J`c~uv17jz϶0y_Q _5k.mk5q7|HC֐C_S>Kju>ZF >u6IuJujz?|3_]밑>c+\rhb蚓=>k}Bۨ)\ ιgEsvǺF\ _Dh1p6Ugi*T8lQ퉎2oۇVٓGG1t5uD9ALm‰W'q@&iǩ8 k(]^0O\y'ǘs~8Ev3t9aJܺ8]̱f_S|z*r>5lXۇG{|.dwړl \=܏A/FCL{T2.\|r-eW|Q üc:}/<`W<q$̲JW{?k2qIk6:c f?þ8!D6kq{t+'kq!,ı.&͋XYT ]fdjU "R%hmیz/FyE5`s{7mC_sb|:]_ _X۠m~?b#ۿ lFF}xRWTuz-GF ?iP!uN_߼1=[>~ثmQXi_QOv'<5M2n!Su 9Nk!:e#Icg 8gt-p&3osв;{!fV:k-F.!/G}Q7 }lb]ظco/q=pF3|崔c+Nbj=J-y c9dOp}8| I ]"XcѺzVk3LC;6^'F <ԘSp#G ioq)#h}X{p Gw"/=LOiz |̼T6_2K}EV $= 9=7垁DƘxv0RT:z ?9XWl uP3{|]i1n?p{q- sC+ۿ9REq|kZ(|Bs?6oqUOEZQڹ[W29ODn9˛Ro9e`ELo<]`' [z;ae.CF|763h 8k_A)2ǩYw6K#!KY~X2;;d9,P3p)>1: r2W|o~׸kk_4y〣٘$GY7q1G,Ƭ|^sIޢ_ҙ =܋q#Ldc76h}{ns8ΑwE hS&>|:?pτx:/!0X@\!;kd Aow@a`PQ]n/pzMkM?~낋$) F <ZmZ -krm+neV*p}8`oks[ؿ ?M_^ji[|_OiLU~/O&SzF*O>0q{Rz?C_῕-n7>ȹT{嵥cΞ4=7р{\ٸO<6?8U v.y:{2v_ ew( :{~2/kFss)[;<~f3nՙK'f|h]` p5ƅq /@:.Ϙsqv.S`N56 S,(S_xw3zB}G_qـ1`\}(_KY#"> XB;4|+s>Wׇ0(~<H:8@qqݭDv08x;q9wc"r9.Q܊9g8@rf~ZC>u k/jX_x^``~Qz 3!Ьll'1^F0ox? pV> 1 I|d tW aZc so\/4 \7Uub{E=V}f֏2{"Th| s?s>ob:0n:?81wc{Ծb쨖{SZ7uW?W[rSΉ\V%F Uk99נv #w{!FVk7mb}>.[ўOۗv\n4Ҧ޿Ws鳹-eS)9~sq p!nNp=A_~`o!Ӷ ym}yi;csg7 nt_- ҅k=uC9w/:2z9֨9 0v zLNKsd wVLeܩd27zkQ7V&7ovκ}tGk;`MZo8L}Fcܳ'/ǥ|xo,s1h=>*`NG"ƻtfeeo0~~` ºbp`\@40( 6f[h*zjȿY8֟T1+$jhn'YHV6j8<`Dumob]j5\h:45bmp xolK{D Ǻ?uߜU+Gf m/ã~ z3;?0hlSNdO oh>cM-]_k{s_hΟ5_1MZ8iW>XGgawX ֍R~{qa=m ]o;9'JubΡ +ٓG3`G#L>vd=_ʝM٫>tZFs| 8̛_ HyYwo|ĽC\O 7 {^ibNϦ_|6"nɥg.o&2t`-@Q2Cށ ܌\/澿EWmߌàM>X98\R8`+N_}Z}*W}37 2W/#Akcd<@q<7G'C)n̍;x>̥~` kro\>},RuMh%'֍Ic$5k4IW@` Pz@zY'Sܲ,_A>q(@o8r-8쨽'K (9n@n]Zs_" yШ㲲p@˖rWkoK~ܿ[I+h`~ )l2W/qzWEy-_b|[YkQװgIpte1U3^8#_ek{j𦺻wՊg5^׷}+/_za =G=yyGG}WF\b$Wߕ윣_-XN0_zc~rp{,/k=y uWo}cz~k>f~k< 2sXggXQm$[׹vzSWyq.:7Z7Z4/ M4ie@SiUf=k06ϒبnykF&zٚWq^>@sr[_q_-ߎG=rQ& FyY_yۿc=2!g(fr< l0ro^|!umGqy,C]5HsZXz ؐEjƦZyx%nPN콑tQ OA0>r:ًd}W#2I's`Gݻ_>B"<8u=9g軡\czr1pE^8 4S#o>p>\[!o<J X, p0)~zJ 9c98ksXwk\ i\nGy \<#6>V]u7g84c2zXo0c;]. }XPR德}n|qow j&u<8 q/B= i?_3jglն~U n_+rԌ3 ە?]6i=S/,g*9=GԱeSWy 5?j "-V>P?װ/1˶j8֮9XcuNky4x).kz ]9OK!}Z֊K{n3?==y]:_W%yH:πoSSދMF.r!{&Oc&ֵݻ$N^:Ha,`LmJ1 ,L>p*TD=چL0' YrW7Ǽ)pPз2vuu|̟].7ICl'8v_Zg@m0ˉ8,wOu{q 2Ɓ6|`kvx@ a [<Ƀ>zAv?o] 4?h|@ơ 1v-ӕCidq)T@u@~&Tq_N/0vӱK!{5DҺK| Mۿݲe>/YgdW,RE(>1j+=ZI~fS봿K gϚ=/Dւy%} -}v=Fl:;XxC5MgW ֠Q.^^s.ch}[P'r;(SOv^lh8r{Ҕ75wC?y+ۛ]N1fyog3>h|bQyi+%Ǎ{;6@9|1z"+ҵS{owU`Y{|v;qQj3΃h= 0ր Z۟u)oKyk"{Wt\D>9c0%%2du*?: Gr}6own ZGym׶[ *N&|_yN\mfg {{h,XSn}ihr{}h,:.Ev9Q}n_ɶ\}B r_aw"?v?gz4V>p{o0*/~?;>~oOT_>CLׯ|!oغ ?s'̷.֎ >&o5=*޹qwͼtMyK&&uBbk94G6]uGK7-zR><8鳞\ 7F~M{qK)ГMC`.76玜ڕ ‚9i)SG>`'r |xs3/(io~83kҔ/''e)@ fSH3^:n:nr']Q`ì3FrqFK6u?&{^ˬH? !k >C)R KOzEz}ُ?o{,g9@s8l3Son]i!g'sW+ǟh?ki}!vc&wܽ,m\+77_{, *yo Cug.\/_/@=h(1eNx|E `r{>]qfZ?_cppKk{^Þoq ,WD19@Pz^z }/VIwQ 6?":}<-f_fmO+?,Z_SkI7WűUܟbmUbcױڈv3L?V^]~XWL[ihfػr7I>X#־u~i=~\߶Zz^4Ԫa|8& { V<!. 00>5n;BǮK?M:ѻg?kN&)\~JB.#wo2tm.dY! F1_J\:uuU\6qNb0QKL͝L3{2}w KhX0ד>oS#{j}8LNk۽A}|1'.޿OO!CGJgKcx p};vy%h[-q؟'EhM"?>4.g.IŒwgV޼eE'.sZ3㳹A@R፧q,.4o KW-_18XjǶrM9.yT|%G  \sS|O.M6Y 0nRjT h@ῩuT=z xi/BZm VL@bp-M?/_gWjؿUu´1? _m>NlTGThj2jcjڹڐaF35~wYqOr[Yh;74l74Nk՗}vC&pqm?9gV^7Џ{9߄1V?cӘWX LpNꝸ `[X_lח,68ߍ㵜?r4yyХ07Esy$;nT CP0`gv^S:|, C96)cf3wL9?p')]O伦mҺZ <A.@y Zc1ˬWjDz{\3JfOBKRe87> KW0\~Ner9p}?a%nϭR~{.~/\}A]׹1O\g.{g\^CYMzˬ8pjhrͱz@+mpSpܗ'8VA@E<@Vm`Ef_BI y@$bjR__Xs@p`PD6v@qUo6ܱ_glAGm_eX5bNV B4i/+*Gcթx*?SePϚ]O폶ۺ|"U[q%!K[@?xYm.^e+k;hOyo~A+N[t/ /կa[`7/Jy{jmoh;k'𧭅| @o* `kW)?_H=&z, T~k&g_RW?.dO4/=޴n. W󞋆'bc=J2o"rX%i ;?1n=_ =TNyl'e)~H w~KG[œy~]/LƸo{g;/hO19s ya6#.vq{?1n/ F?|%`hc#R@d ݻ{)":qVqJK½ hu>Zy`32n18|' ?ju1+sZ?~ec?IE}݈C`c/ ~K5V1/?۠+U7,٦o/km ]Ҿt眷w[| ox;Re낕+n4>;߿u yu Lsu C̵hk .| ߿Z+* {gF|6X?ǂsi7~[P݉{c1kq'~  tGh$|} w2xVu!G+IwտMe߁(_s)-h1VWdr܊_4uwZq D3kƮ.> bp&l3zxD$b9$1Kro|`30cA{?9y["']8;|s&׬rKM$7LdLR˹|9OPSNvod}8.s_<<\/Ԣ姽/^`X.6[}g2ۺM}}z'K1T:rOkq񆧧,6@qu(8@F# `U=hb|f~ҪXXכ񿱮_K_}Ўkm殷nV5TT< ~8VڿЪ (q_bL#@߁atykK+"_y˲ vu9ʵ֍ _w\*i3ݷ\p  q#oFW#gjp:k^?ɤ7IS9+ێo@vesG.֕kR^'y&%.yw>f}׵xO[5c iXWھ ;Y9)0buq13 {EގKdpGkdq)O {/w9SGt`grT>-LW'3yR>A?oG"3ֿZNzsSȻ+0|~~n7~OڰN Xm @cӉA.@mCK"@X}/RBj(>q#hH#@s܊Isړ?~ bf>=mպҬ"ծIY>:xG9]́|d_jAq^\r[k`o@UNJؿC_oٶG%2\e OClvtjq.s6ۨa#ۿcyfiZX9Cp~xOc}_m[7\H'xG[Nwas'CHMK15p a ` csxZ?gzމǘ? ֩uuʰg?j|ָErź?˞CwOM i!oӮ$D^?aWO䴗-ِt7{2킯a gyl"wOD5ӕGGQW=O40k}p&wqB,t~ZLn5?źx/3ɨpu1Rs=h?<g"Oich׿c"y7w`!8.MH,O=R\C 2rHo+WK"O^L~wO},</8y^cɍ~D6c$:9]n8[b {_ʳg)lokL*@XZVh]8ճsg}|4nT>cF_Xg뿟(VWr21Y d:cb:IF*NyYz)z-d(KۊF @=W4e}=p='/xo$׶viH'prPI#s  0 Æað 8|$MDNԹߵCծbs{7Bfwuծ]YJǿF R?D}#gw~>QؓIm [Kks+D3[8g8wBmII9{j؇}b^Jo&zth擏4NЅ[`Ƕ%ϗS\SeAND%*kg=:vqF {ҫ*)?%hl@cm{왠w1-.MMo)mo-I6v2 %;*0yMO"m(w)ҍ$XR'IۓM57}o'z~Z`1F'KON< i}ާ]o$={Fߨ<RB*;)ֳ%I;Ob;/IVy>50Ϟx5y|ۘ;/_ aS炧_֮)|挤DZo8׾Osӑ~ ):o`uo\g1]+iw-0簮$4f1*e U"\͔'_E!摏1?>~# 5hGsXogK]_.:9͎+Y8.܅QJbqGQ?>_~ssy|!dž|C92c}lqgO|Xx17k}#5Nl?kxl 6Sfd>U9'N>Iqds<>3.? ُ9&!#ϊ! KSʵ{,`ߏ`~]* Ю㵚PCc_ƚ54T=a=sL]S~e;e{|%FN!fV0{z=W.=lqI/K/]#qp Db+Vp/ [x#} p\/[Ghitm^6,Ŵq)$|Q|.i(5* j=h^= >-!_ [`*L0S8W{fQ? ) xJ0N##>+0cz}gJ:؇)im 6KRiŞ;k7FkX)z ~n~5<0X-_p#m=ϘY/09 ]E{ev98FT>_E|k< <>堡ߞ~hhpo0~'YǡZj./l`OO XGKBYv=:)RXuGАo5k53O]=^]gE8*sQObzI@ɱ*Ei@O| f/-ifР =>wZrfhw-g=z^1D3<%>_enkvJ}zç6E`ρl F$b5Z'aOzMXzItNAOn{1SBПا=z_Hv.9[Q>ģy!\ ِ6l L"سg1/ag^7bzU5PX-/jۇRs+Icmz it|綮a:-̙o=`[Df F3]mWBε[8Y*Ĝd*Mmtq=<8V> H14uA`+idxEVw2\3;P5Ug]Zwߍ0l gTlw Y@Őt׋?[9:}CLVga Y ٭b|YEgNYIӪ׎0yfI/+=fvW}XO !~Z,A~Ns.`}Vg:s5VSf\8(Cw hi 8B4հa3agClfmn}|I r$%}N!&ShAa{U_ycrc;/ظ;`P1~s|.GgЭm}-ڟyg{>1~() "XMtO71 2AO hzy&E#ܯbǶ`]ao^> Z-| '5͢y<9y]ۓ&F0^i\ L38!i6p\׏ v6BK+x)bv schO4`\a T ԧ'xy|ѧ}߁96kTs6qXbON@ɓ|Ǹ91}FtfN Ve斎}~oYW?*ffM72m }7;;&T @᩻u>Ɓ|=ۚsF ͮJ]gЎvOyϱ:s0.m\XV#ixtNZgg0~hB|¾1f{LW}K`m} 8xãw>\>FZTգjyT'8z'.4VDVN<~ֺ̏5=GF9%X R|v1c[}aڔ]L1=Q@5I澶Agu^$ɰnx쌧o xP'وϳ ]—M&E#), ļO?hz֣>Փz|?c**.5 }Rٯ'O/( >uJ*`rBghk᷐o?7YOԪZ si 1`@^GlPh>>b kTF{;1m[赜 :swm sjFpblZb+q8 [3=]XcSw?OobaC@fyL97C'[g#?90(,8Ų%߿e]/#E+a~a>'r;kqޟ&??,ޔ̜/oDdj߿?5ecT_Ef^S>~ I+,|61,7ѽNRZX϶SZ`*xۣ.MA6VBamTǨ?m*옓%}&hrSax~s}Q{܋ݷs]&Z]9l^xHtE<=<} x>P@`<>9ӛO`Ɍhv%;!OG`8B=0-@7iyD_75 ׾7R1kwW*2}^+9G,1i.?Ak Ǧ1ոON38:흮R;x!AS4{&NBOoAl#:quݝJ%9 Db/U+Bi7oRg{kk9RӧNnnW\3OFFZ>vয$&puU66b^71T!LL8G!8wO-=Xw=}67Im ,P-g9Xzz*rATT@n`6L}[_*~*NM;NCU?~v/{?/ q9"G9Lg\B?(70ˤ o%i!?)W0r=PIyV8׹iBcn_ \ojol3y_/ޠ/2>&S#;cHSX)c{Yzm:}B-PW9{zJK:TWߛE_7Xo-s2򠉰ONi;>nk9l`fqB̼ŏuv*Ъgkw0G =Xt&-h\z|J?֞RjФ*/y]܊K5 I Y5`{CZ Az8̃TJ.3<8wh_Ы4kL{Lޝy4v/Zu VH* gku@nZS̥]`?-雘no~ff%}97.>9.Xtk$+EAJS:tk-<7D/j6}tr?cB_1]{]#̉VUҭ+)\ys`g?~#[B-GS_Fxj.w"44y7"o%cUJ2J+*/`\HcUZtԴ74Zy[<}߮/^iXԩ 6kBh htOO;؅ؿ!:mC'\ʙM\h'v7ir14 Ә+8A׾ |/xJ]X?}qs 59><9<X_v {'T瓓19{ JBv{1WlC/n>7cZTuF#ba&򶙡\gF*ug'wN2yTcu4op " M.9nw)r*C+Ln^:rnoa]gSoj'[9kdϮ cWdix_3owGò K7C__/-l?*go/)Ԝc/㷹-߱Q~ yC0󉘘{jbڪb:5+ 㟘|vu"ˏ;-[1am?>)dFY}>Mr{XFm;Sng^x49jm>o2Qc`)]c >xQ Kt|΋EI7>Y:k7Kw..xG&_?{UP|J]UVQ/`MB$}:^OY;qX2_?Hm1ԵclGY֒c/|aD˜ *&#֝z#4yJ [KMtGW>aw1`?-\\FʧЍDo`l)HuV>{&K8Ocrn$Bo+=(]<k@k꒖5*r4kU]yV:]?%lRNT 49m͂5t#CsCj-o`@ly:PD&akpTn@Zd?@ⰽcXKYE;%ϾW%2/> ^+ev-~oU7W??q~<S*=:ǏZ濊3}]f_:sc;/M`=̷O?ܞ*^̱tQpI=Z6 41!ly-Ûߞ2yv\Gc[Mգ b`c|8oRzm|-9^Zmh:t'Јg? z=_H>`hvxmJz ݮ^fǰ\妎WwP=ic? ?U?k_s.a;@J{}t1#9htCsףprs5x t\ OU5`XRwA'?i|}4%TM oCgRׯ'ORuK(5.B_q! \BWNX>< Z5<~*07BO!Puoac:O4;B!쁊ȦZU}nn>%~<$ 7H9W@}̃=c}8wOOL KP1 G2Q}P\gO)Kjioj 9Bg=nlÎKkOto4P9H APgk2˾?_;j: UAu@31Lؿ0SgK?j)K_fSovSuUSoC9š35uϾ;kɮTzo|WUm?yJ 9OOky~t9@vbwc}s̷}q |}\W~~.42s+vto|7xA&1aM@߿ʣ?u)\IMSKehg'~\;x!4w664v׳׶@XZ hd$R96-69/^C:=^oeDfCpC(}}dmmTi@=S`lL ָ%& _+h߳a֔r#X$GP_\?t/@? Q}UPW}?a!^9sPڿLZ߿Y_R_M?[^7zj} _zA__?u;_G:o%/;s6پ?g-nXz=[Mݿbt!}C;`;wuϣ >ߜڟ59l>_5+)}LGm'gu4~́zBς7ook* :.ݕ}A>xspso꜀g$MЧ7΃?g$DЧ? jaݐ<0ݕ~* cA*GTRUŚ^8uO)̭jUڴ;<;$IqB㈯uqD}mr-&>Wv#g j\=lTN]hGwB Ϲ>l_е}ݸ>v_pfpܼ)sSW}ŦgR{Wz|s<|i)VrZ{g:O'Oƴ6 lݫ?g? lX0bjlU_΍u+*UDjs`sN oݢoi@}{`?Bya̞SibnosCwj200C-w7/e[@Ϧpo 6@Ϙ/uAIg3Gi}d)HoP}7y6&?v#wlŞQl.f=g?_q_ms?U0T]K9 67!I>mNtۧ1lU?c,s YWhc –htqJW;1i97y.9sxĹ>haW g?/&|T+_/oapn%=ν?)t>ѣӒ]J@S к#tN%XB`.Ur3f0`y`F$I> >ά`5T ?:*Ň7/:* #iR7D\..\RS9. A_K{?Js3=u΋<<y]8'3;"ϟ]_c9i\R=|M؟¢lFs{>{`}8p:wLس +*sGT ?,Q[1*P?S:= g/ZeX 45QwjR w4w}T|oJP wl7PnퟓGx>GIe?O׵OWOsYפ5U_{y ݱb?o?F9~V^y7 7ngtn>.?z?=X-4 hT`m_Q:Ew':8x6yQva/O }mp{upQB_h㩱cw/G\Ǿ%@+9.ƛ?m>5>cY >&Z}ѕO~Gfp@׿$qHԣӰ_Oƍu:wWZnֹqZL~@9?}z<<1]s?ǃT׺@ٷ{R*9po[Y=u|?,@/\cOZ_,m?!zx>uj}}Ut gP?h.)Vut`STPd55e>г7ycJW<.G>*O˦<duq1%s]?A7V^"޽/-hhAn3 ^zZ';2?? Pܷ>bl_}m7W>q(7/QwiV7fq?pr|R(p)7sc$v|<4Gе#~ ܿAuj Z'm h)\B+湺l sހym@ /#.-A'?JW{}#u/tTϥc8'so x) 6ȵpţ7g-+(8kCl#MjXk`H#c!b9M Txlo[%p1HU ]gҀ~r4q|}AfzO_t 6zJ?SۿNYϟƹ| vrDB_}Žl|z*ng}P8KWz`qUuxu-VM^NMLԲ:U8П97JO.>ףX1E?TF}yu\^BmDyܙ3TtFj?P v[`sL[.ץ9o@qSqw ((9krf=9*"sϋ&]mΞS/3jk:oۇ);>ˡ>#zb򇜃Cy&rߋSf7L<;Uq\}=%Զ_ +|atsZsc/w_i_sτt?2v♤[|:DehnHhEVq40n5aMЩ1Iα U>3]׵ݮΛ 0tk6Ƴ_s[ZW]KB|1Sa=sn];`HD!5"U[)ݽѩZtS5A_|]} Z^N#>ǍFB_~=U~&ar{_ ǏWSy=rEV< |O 𡠝#>ѡKLM15>Bo^^\~o wOpIsBxo[kno>̜k\Qd9 [ L@3}Ys aOgl @Kڦ}jgׇMKL{??P rX4}6^IՒ-ǥɗ~{:(ϖjtߑ&Ϟ}[[`]y 6 gU6W|(\2`?`}sϟ3+e,?es>/l"}!?EY9<9c{j/ggoZ|#hfߧqƔW1ݳ_ձySmNq-4Rv ݗ`u{ۃ^G\/R٩J;eR{1]869?@>;Cj\Ч|_g2Oׯ3\ u~%lmB |{{Tk#xళ(*jX+u&}n,$nhA m-̛vy 3iT.W(`~T" 6Vç -56x~i/y-zů޹>=~D*s.^gJJ?(}|zܰ_5׹/U:~mT?zf/q|$MߦctۤFzUB` _H8b;`oL |/@⚂й wkCU`']/,#L`cŪ`DwAorm]P)HW wm]Z7=J}E\4uaOͩw7ܷ\O˚`_ ;Wb(o]ؽO\;vz?#r>Ћ 5P@bءsp[jۋ16:v@'c:|l2c}ߚY@z?<Ռ}bIh.M tߟqAώ syuNoͰ]d R3$X>Oˏ%<_T"0 .{p5x h": uir]61]}߿J7סU ǿhgT)}rߣW'5'6.f0Tߠi5]V* {5&z篁mB8ȦM8fXJ76qȩO;8N Xs*X_~^2Ԅk}ZXq0=շ >yS׺}UJӰo61}vͧ]̻Ekᙙ>7[A*ns>É Ҩܣ}|;xO~T]g_yVR/Ǘj`xMeoTEp,9˰g&&p*^&.z>k_Ss{;r lZBM̻ _}èx/?eo6MtM?]}; YZ][0wjguU1#Ufe'd8Egw>\߿[nw.+jl)X?5_N&Ϸڟ}upȏl11WpOh!lQz1#:\x6yBl5ٿ9kGڄnL)hft\d6}˜7}Ծij>""AOmL lT_.lTh{ D8p|yy,ѓӒ>{rl`)+P2{=gD8l"xPabx#8XXBpHc.^JM$~U<6qNژ:0sH`^\~/D0Ii9 &r~Sĸo`pޛ7ܧWs9zwֹ}ܟrQ_c|E_~%hqo݂mNH٥6}14U@~m_}`/ ]G}hb]MUoqZ](~]Zyul桱^?΁.OK7!9jt`CD"x*P><^so|w~8/?uKY`/ _:,*!~pkǿ`h}Ҹw?{ڿ ~PUEf2hu~7W}l;U{h'?/Yʛ,S|bߜJFu~~j>*w67htg}Pxr$ѫSpmؿ#\@qvW&f5~OxJ#:_m X߷L>W7=pOC UO|1[G-RݟxHs0sAf6K\z{W_]#_X_}cvQЫ<7)FƁ&|;XB-}װA:soFI}qpkKX:|OD4\91= >q^/ *~κt-_ҢO益;;s.k+Ozsг݂ß<g5^p$mnl? **Pj+>+Tm]zD 9O );j۽=-<Y9I(_֛QuCߔ=R7OEEYg=+r5Yɟš}C,ls\guO(n$wT[i hxb?ưNj_HVS*=:87>~@6ok{KzsB(?]}q 288ާb#ҍ]ҸTm_Izs=ŵLt=9eAONJCY.5{TXFk3Xk>l.Y8f~!9r{Ymbz~.+2_@re\}ey_mP~b/p;"K>Fwm'\;k<yP4_B#RWLNH?^'OMÓODz/p慮8,&lMjxBC`}|#@;^7>: ~Tt ?põ2 $@ŧ+Ug{=ͭT?MYKu߽ҵk>}U@/`pL`bK=6w:׾C%-h([;e&&ʓ~#T=:ܣKWkvCIĜ[}ypuM(5K]9̅:sU/&4V1G_bge~m'mKc1["?qAh{V 8?w6WmƧQBU&l \?4]ɜG5,[F-į5XgD?0>oX{_3?΂@?zYwe_HoE;w I%;~*HsGFV1bO`>:Y[G.7)^w$ݹͳ 30lncq}Ϗ}y28R|\f.ϒ޿ѽMtyToVU>ka=Pm pCfÞ7W6.9mmF' ukb o[_3kVYu|wEηK[a=̯ljT oٯ[om~>bX8{=Ydd~s,?-3ˌ3ֻ;$.9u4s߿T`?os}?E:D۳\ og뚧=f:>-ζt5UoJka꜄=l?r_O(>SmBo|5]ao@;sB:nфyKxsgD+WӅ*C߲1]CWYi5]b+JW"j}3h;]*_[ـKo1=XN}ߜ8*OMȥxѕuz\߱G./o/{wRk}%1JUkd .}ѱǯlIZy{PyvV'F':Kujr kб+gcsz8kUp]ʮES_c[j}/3gY?ΫWjJՙN +˛>hzȬc?{c?',w\O/fu_pBo(6irqX{guV_à s$_jv8b79tHCٸ/NVry 8?({ϰZ~0lvtsXϯ6\t7יX5YW=-N_ܣ?|fT-ȔgIsπܯ7e?'57?Ƕ{T}-WhϝvVSTÚ1 ? mO0؞a*u&Ỡy?x? uFL_KB@_q%ս_};]IJpgKW>ܥ1~޿'mܣ xbDսv%={مG1i kd4ջy+f ;22ŔBCꍪL \8oWoZZW/dWOϘO!_1#(̘?hkJ{,t 2[l^C '/lkCwVn9a>HrKshϬtkϐ4-iOs8nu3; gtŠL pW4R?slcu=07W6/hJ(d|u۬ksL9c-.}ZvXטArsהJI?={077 M4 h5܆6l%-ե-<4ߪШ3,|p; ր'm:}Oҩ5:aZc|fmh}z?{VOk{tU~A^LO֪t3A&t:B'Gw^G o߿Pڥگhi{t񀄙3kxxSq\E|usUqPxW$Kz)0GU㚵]Bu9{&ìg:FΗ{,.}w6<?45ӡ_Wcn/뿫?a)Mҗ{7+[R4̕]dL09~kb:Gg?t|yD݋}z=vAڢ٢3Oաgr#M I;I~}df&R\= ךUow_:۸n?@'&BUWM\@_=ا?8Fѱ/W&jvlu?{w4ۯ??KL;6@Y3_ϹlYd<ϸOk4w˵ce79N @j'| (>Cg'9?pJ<L9ΩAS?NvONr?!geGRuk(c#{SmҚw'Fz~ }F=_ }1hm m팶-RW3}&h=AP]<}F`G6>@ds * c{&~GO?cw~#U~xա+NtNn~+iw]@7)v-]vT5ƒ|`?t>/U\xDzp88o3AVDuykaM_WquxaI_f~\=~C}9olBN6}w!mt{}w\VxK~s# >~l.>J? ?ұ,kt ܈ZbGem4M^fS:\Ɇb:~ɜK%lmV?FFS_it Eց0~>`}*OZ91__'_OW +xTC[INaQ8W TX-(Ec4u260>_*_fWt\{n \`ս{r]!'?Wsor!OA65~ ?wjR_fokd?k01g/~dnf4Ӕe94vg6sRsJSdx${u({G)_GW;!5Zq4. &v|M-_RuG/<֨eGu)ߡ<:֯~C*%pίlwi]|U,.;?7+--Ͼm:sKQ$zw>})c ?ACw%wRY‰W0@ofs>;S~S~ 7zRWSA \φBJ\@'^vxI>y} q \?5sZya<8=zRq?4YxjqCjiÇX7mLg>G2en/rAv>|Ȟ _ P؟hg>\l0o'6)qʽSQ4?Θ1=jJO=kP51X;~)GFYsQنF{7Mb6'uIKX7%qomw(zҧIjP϶IkEI:اA]8`E/=krv1=Ń:G3ipk؟mjep-^՘N~^4+|[:4q} 砉yKǔkMD~gQA,<u ☾?e|A~򕶷yUFW@?x<'}Y?&z:ir֧Wt̛60FsKkcN&0Tʕ|&b`0$B8=FRy[39#SjvFjuy֧Ge~bN/X_d=%pj6nRx 2c 8qg4СV?X6'i|cG8a ͥi ˺G,Wn 1Vhm8~}Jo^};>Dv"Ct Uksfj<ƞ:}!?$CBZ' U]|M?auV=y:víA YVYBy2Xc@QOգ"k*c._#(f}21p*_?_/^qxHuq,?b#_~?/0am|?<?\Z~w]jI^{Zɳ&?`~>? ,#Zg)Y0a[+{u>S^NJ^'o%ڠ2_ԝ6c&F+ԭ mdog̽EGH^˼uA7.&:+^U(KJX5 ESY)-7Xeݏ6oaG;MH>!.6 3r5<=끿qo3<;7©h1Nߡq!@y(옆䧲Df<ʺJpU;]Jt_؁ԙ6m@8I9Ed=c p%g9l 6~7]k|GǙ%>`W4WoGXmYt.VK/n'=_m`ie{26MR-Gr{X }l{K(mX&۫M3w<P쇌 ~SΥu"BD0f5i!8B81?ڱ|EV'5d&"e=R F g?&z5׎f4mC?uYn = 4rY:pϜ[0v}}}S—^|3] _`xs bzu9_MV{lZfCU7ٿ67VkPlJe6S_ƄgYe٢ݽ0Wo~}Xf4?NScXFd?d??[2*>|Ϭ{}0U6:I_;mFz=c j]ƲOSW=oDX}&}26@[kd:.8-X;$k7C+ٌp T o q~ 4OYlap y/oy.Aac&7]*>8toȟG&5%G͖mD Ja) ]*m{?f\S~mUX"Zot>qB][,aUOxs2worZؿ;'e 8@l<)=~š(`n$h]X Ys>?u!1Y0$̓&-U eJmwxnN#<'O>nP.5 =[<7ry[_o|(=w''Ų6v_>Wo?}S\c~qw|}x1?ݗ/I3y!a|쿯T|M;׋T߻ 'Mk1ic[o|b?suue r5H7ةlV:I{B/kC\qߖS#_6HĨ1<D3u(ROJ~g=}6-8J|< v~GQPo$)/6G qdcb{%בZ5/?jkԉ/zhv*z#NެߎP} !HzbV֛Wcsyh/4ҘJslihL 6:ߔAI10y3ݛuҗJmѲF?|a ݭs:|&Y_p˗HϓC}" dq(8'Z&ygAO >X>v۝a:D jsM_1ߑ7:sū釒?ӣk׮p]kE$AyWCxa9o޻1ү`?Ns)zϯ$eYF2k#1yW8d_9pPG#IO?|W4+ Q1QzT}ڴ4!xh)MszqJIP܍)$Jw.A]?@}ݗEoKɫxКs8&ppCS$(C܍ۧZŔĊOaBuNQdSw:c\ oFNDI,HTk($2B١{Wh$Qu6K ծ{ZG1UNI|BU2eUj >\"~G=0WZKmԼRX_e4rNh'EAYR# }2}W3LVgg,&?# 1A>@{qz@Ap9 e8*'lvh`^^;\o=Dg\N)Էmq}GQ\'ڑ_Q'8iPaOZE0 ~x^BŁ_}s%_ !Ƿensrex%vxzm;&=bW\?6WQ|n}Q>/~4;/ i(m2p.?ˌ `o4)د}k'rcH|C?` AnH_婽l%scN?=9H&ܡ_Ce1eWQQ:8S.Sw>^|\ Aߞ_'$UOyopcC-?yP u}7)~El/ oDRQザ/vWZuµ ?)5_wSi-eW]u#pҦ;M_QdV,i(5٣G>M\ho\)ڃ}RLatㇺybHWIءYZQͬox6Ș'niϱI#3@㱂~s>|ik\ ߔ=Gcƥ&j${G?h哄*`P>0?dʭ*7`<iU?+썶Plgc 56Uqlz0sivOنkPhO@KAۤV81uPf+TyQ>+j?}3Q c7o.}>N)6T!{ߏG6s_f OPv@Eƪo շ?g%O@D5y[m]b,nl_ Pn'_J5.^ҫa~ 4uӾ7>tR6mam{~Pk+鑱'rM%bt/~$g U??B,){ʫ9>BW`+[wG G^s%*W_~\*zsn4BՠpB)C+iHdL8hk`3-|݁m2AC҃Z֯ A4wh-}xNصզo;S:iG/T@vFaEz; п,mqzvoxec' u3Aj!|nDce X>>s %tIӣꦤ=0pqFoaW~W y@ǹ_M>˨2YSݣ4gAǯ?HH\7Dc haMkT=ܟOwaG^; J~HPuKsuIٚhv-7He>v1] `C`WRùywec?hik?R9& O7kV{9պ$ٔt0'r=C|.?iȨWp =8 Ԇre3}{Xk/Fvf %Tj{Z'+5W/@r^ix׿@g0?  5_=}X([aߋD̵fAS{hO>sG'Qv`oQ?q0YDNG%fj`To26) Gm`cmyW` j0\8_жrIQ76x@T%48i̭M{h ǘ_Q\>mz 4U:t3L;ёD'o Z]أ_T7z#%軄j:͟/DχR:z=7'k)l5J]b=Gb4 deϱu;hFsڜt{}G b/kI.- sta5(W/bv9׶m&ޣ,HӦCVRSp?S~㙆A_yMEܾgm@hߑ {_VrM?,c}[91h/0\`oUA⿉_;c,^`wT(4/nf1is}r;mZ}ޣ͗hP}§`N<π.G{Ko#|b/I:v;ۂmF!I)ŗkU4F.- ?oQ FGtu:-ǿftXhj(j6~1C2ioE8N8̓zS+yFx^.IL)EYb=IQEXP}W2VҧZVYC8~xW]سvQ_;R$U-@Jm(>߇N0Ѻ3I\a=|Mm.rZƜ4/Oد/+?s_͏p{Mc"r{Ϋ }2%!߯~or G2 Pw+|n`O ۸kEA?`l>deR_8kb 0mߩ{I+fCajr#7QA>_]`^%#%_^9? rC6 'mK8m~zqP \;^`~~}OrkyZ=ZxGq=(w4>l,O|KKϚ}G?~V)A`t;]h(ϓ1}1_>J>"zMlT@ٷ}AX!sk>vO\o;z`ط_,wl I;Y_1o1eDd/86?87s?zf3A.ǜ\2=17w[&tB;}Tyf<^):lMX.9=קV 61'`z^]F^Ęwx(`<~EeZiI]/+n*&dZq9V]</eK/j-FP:lT6"/5]~W8>_@{B]YH2I m~hϔ2C| C~Ft{ )?@=>A/)<q7Yɮ۸ar.som\Z_ 7ѿӢC/7olf SGۃo|,f4PHEY sBVd ع>| )V)?_a]m̷=FNN E^6gZwMhE0aɣk{ʩaF5T ƫ㛕ʗաWZzj* 7u Σ\,N޾أg{1cL=# A#?{>=y߯442JtZq6ݟ"y> |=Z_xt|JwZ>G{{}zp/> hc=!;⢠Y(=+M:ьTt;=Bd1CfŘ_9.(Qs^HŨTZE|w8[>9>PPr^Sl_ER!=طGN-z6Rg&kw3jFb,|&GtOWpUh4 {pvYң.=#tlVPsheN*ǽ~ 1H^^x>נ*0b,<;&ō)؃UcrcNhym i{U4߄V6?)3;NG{RLD<_Wmߋ!,jqϲ- {o$94Xt\ױ~/7{P Io B;O8J:}{W]Z~;J!}ɔ~ۡϚtO>}?G;… U= 4֬jDZnFatRS]5wQO{L<7dL2C|v@UKgmnkOD~ ėc?1Hlq?qu`NKZ6Bs>} ׷`/?.Iph#\s@&앆mʗF8?`G/{+^oH\ %l֫1 ',&ִ uOa ;9^}0щNN>fݍT`/{ss|Oswo%e#-H `l'} 'ʝl߿2ETZ4I>-rrXP&.?.e G.F#?dK#8=:žvٲ١iw}v9лHy^P\{}bVqjΣj]pl zZn 4׹gniAMܼj/vc`O`J=}ܤZJt^~JC Օ.B:>3j4+tLJ OVnfk750}Ma.z{Lm=(1s* |=*36As:{*:@[ڂ>~0>BR'F=:2_o]z{O=ԇN諏W+şw)fLktŹ }{!k h0puǏQ eL#Zּ n0wkyj_)R&#z{NP?Qլ+N0 ev1 /"Ki?lu}' _OP:V`@'`N`|[l`3Ӻ4fo?X&~d׀9' =d}ic/:R|?pŋLLFށ3 б}EOR~' 濔 ⫃1ɛ(nS ʠ&/ogqca6#vHm X<=HxNi=ι2hi,>{}VGw }-&? K+ x7~vbvBWkԈ<=g{/G]3ң59y#7_Ov終ԧ31=Q| c~Ae?bSy̜ҵVӎ{rs)O?`"o)?I:#m>Y o@w~>ܧǟ3UewnPޙAOW3P.nU\/8Wp 5)f=[wo%|!TزN6im/4'B 5X9W:ne/J@k}gCP4Z.L,ZDrIyIמ➎9CcD}YO!}lrnN<Ǝ!~}A.u.D·tDyvoeIޟLߚ/\_mh׍bb:fdօ{_'4.tj^XJwDU:|O觟|rţSm9bzhv{ډ f tP x-Ma{ǵ3^g^h:,S{: R(e:gڰ`1/y`=4Ν<}͋-zpi*}kNtPz'-GOSsxsI({qX,g;K_pS L|e;~ͺߣ>$Z {fR1YKSc=I4 Y39NYs9mN#Yo7m!^z> S x1(+C޻a4zzD?z': 6-is{IOIz{ңϾeS" џ?h\t'w Ř|}I'kHrاoogyyn Rn>ݿJwoj1N=~]Wfi+iڧ'3Z;0[UTsq#_`ݖ7+ a`?f5ZJ=o*ܕ4O~y ZYgfgjF{v=>Ѿ9BROx_k ett!7ܝ}# q)&PufN=4B徯:3Zee~\?_/3z}3>Gw ?]QCni龗&??j=vf/{-w1J'2wBy32w=|3> g72_̬ft޾=_v __}Q眮>꒎t k=Nlwl†YC _\lѯ`_gt&^-.uo%mo7U?DPgzt.-t(ooGv ;!}XU=kc?wOxOo=ntޯ1ySCY3~xO?k#khݵ >'sbL?A@'Or^BܾM12Ygioi7 D?賷ߡ MX? Ŝ_[Տ =KcBc-֧QPèEgW$}B?Zϗ3Gw-gW }άOƟ~K4ʏwODt@O*_prGmy^\crcŸ4)18/hDEOZϥa_3WG_Oy_JC~Xot} '8T!}zi?.[,+zw4w ,;Z43|Xq{0z߹^Y4 ?tQOr/z?@߇!^Y[AYn,7O?z޲`~g' o?__f`AA:yd8kibq#u?ʇ̲b QJ_ _+עW@ϱ@g3k59c;u;Pg JU)LQ|2vYDcw.ibzZyݬ߳=kO_A?}^ӿ{o<>NB w癲';0bB3۩髇Pܼ: M]5՚@RKp e,g)QhR $ðv,_C%,y}A (v1 %{fP3uyKL$샫N8~9mگ}pڝ?X@.%ks.~9s97F]\h*fͧZG/?\VՈ~=e|gL݇lG?+8nmr.W=}s|=U> S~y/i[jE)*_[lWֹ6NmR_N۷! vE5pݚ_Y/ZŤ|gli;s?qVKg+dEuUWՊ0;>7>'2y/r"?/~2wKt ڗ/sv? %=' mSȇ?8jJ֖qꝫK9WǑ?gE6`9fr?,+}aY7,BU U]l;mb"1ksݔv0BGu`[+:ؓg5z~Z_ ZƃLs"ʻ˶ zkxھ*V~y^/'vוm91~jg|Gۼ_Ik޾zNym%x>w۾`?ݹù>q%:޺-KA^ftK^ۋ/Sq Z=ݻ$Up E_]q}Χx^c? o+1ܸc-s <؉@sδF "+ Omhm]1kj5wpIsdޥ'*d9ovJ`\֯_˜Ƕb6o7~0+D.44_>?Oi@%oVyuNQ\%wy\Mbס[9iTp.݇_Pu$ό_?=箓YxP@I׶QE+Y5%w_aT{7i )=Mӎm!𳇶f_ h0oy e!v5ٟH E3{wGk+K@c3kZ] 32ɓko9եvy^%hHpAzNkzoMg'Nx`ٍU իP33#Sp[׾'SC㾚!+r=ϵ1>>Уa:c:y#gδeLK͘rfͮbz9'ї'_c s 7˰M[`;:n؛@꺳O:fv lFG__8Pv%]L;D{tn{X&2'4g;ibo 3.~a|7zw){XY[0?poV\}|J#ȵ0y +YZYrޒYI'0?sY-3NR/?~m'ԜY7*bUtWz86[63 򛷞Yqqg5bmB}o?ι7]]Q609gbsU>={&hs7c8wL=zӧ=8C^7n=WO`B4:왌n}[|njMT|D4ݸ}=?Sg9R{>v{CUnk&r-A//CBM5|9nι89pNxVp'[b~Oky܂Ǻ[.H6ЫW1ݿ8qBd[]K_Y;z4?45{uLoHIRtbfttL. ^%0:~a*`lDrCYGvc>)_cu#-ύ_FN, ňʱ^Oσ#Sq"Ñ^cA{ L:} Q8t{Bc`˝~lBt3UIG_eя1^@/ :SwcO@9~~;ؙPM:4+&S )5=Vy"_gybfay =j~=+&7ܼsψ}36wpFJu.C4w:Bo>Tuy5 nHv2Bjp13;QEϹ99s \XEgB ??vo;_h*rub{AJT߀/UAwZ c 8w/fuK_o5ots?pدc}~mW_{ 㿎5g71(c1Nm !?hO _vorU`hs0L3|&kDjΎT1]:}s=_p}Лrrn}ks,>؟*~(忟F%Μ{uTؖx"ׯԡalڻs]&nX[X\&i` ܓ!ȃNͶOI?ȶ"Z OжO=sçhs- c>,սZ:tZi?7&9Q?C0fC^؅^9i /}/c20DDO`6ޞ=U!+ۑۓC̶0KL'(33pSgy&`CF*[/Kt>7 _id9R{g~L ["O9?]==8ߞ _hkZ{| ϦtStnώg*88y9h5 p~^CYS'5vGϫ\#:? fE\^?}2~<쫵 ʖXZסKj?kKFQj$g+Rӛ8{'5Qc90tϋRүrDuDC_?Z-ce0AkKE@pBܽrv=+j_3@"ܷ@mmY~p)L~=Q&5rp @?9k3`ڻjX6õlX_ZuN֭5ĉb-3k. sq nS:VET,e>rG>cv6Fs~Z~}"psP_cq$oiFiu]^Q樥2z7?1kx]?14Z9FJg/Dt:??XЃRE܊6zKLIol`~)Po.O{S U0N R6}km?TF#qTks`1|bכJ2CzJqFLa%Lګ;UGP}ة-߈(܏)1~LB~Iv9 i}}\:{zMZsʔTkG/.'5o_Աa*ǞwbABS4ӳ'@BgbCʼnrdBۘMwv3L_ 9[1rt|ah?Ǹ2/5۰mvW#φ?2yo܍[s_GLa=m/hG*vP3\o?'϶R9qwmj~01%K5e?\[snCps_u@%.9d '`ۇ7{^?f?2xoxrE@568@h=nskkPN>&@g jgv*-:v8rD|@벲}t @sgm0ױ >/{ʾs={,}t#g|Zw_5j=Wz(qJcOzu,V; 29ΟagTsS 3"4=3^DHۛT凎M>e&(yУ:&>ߡ#/ndLLS/b guxt nltSHzuFIg`NC!7aMξ^Sm|}<1[߿n$^Lw:Oxj? .=`mVaKKg0~F{{Y;76P9 \R;x{WaF \ޙzOץf|Ny}ZOu Q5Sf2 ACh^K{OY?45"&O5%4۶t}?'bh \g:#LR|W[Uq< (| |>>M?@8/qYO o*k MxM`0'M1|PMi34M'95u@C/+;9]TXˣ~dsCuxvE8^77FGM"z@!i#ڊYc3qyJi }q9 g?7v V,=:@<.yguF>w/`[I/ esy??9}3;g ]k3|⑱ xܣ*`ټbr HZ}R;EѻOVDzq>j@?!uTǠy8rL1:vzN?)ۓKnm}ٹL=`MLι6m?Q ?z ff廘+1=#ӛ =>ms^JyTEC݆}bgjߠ׾6u^q\D?]ze`L<:˚jLs`hKƵF"d >Tm}ZHԾ?oaԛ {)yaܿU^?wb>rkr8;zڿ!erfsl8*%O_m<olflZ paW"Ӑ:R%lZ/ _?9n]XkȱyuuO;/[z:9k>ur1ro1Ɩ9L@=mJ 3tZ9ؘp^;`ܻLx$}HY_s18uJ҉̔ )=fǵycx퀶_0?yМG4qQetjGx2{tVJjoG?QܺQyW7?@tĊYga^hGɽuf~̾zW-P5E`c|ffz; ք,5< s3:lqhگfrG,;˞}k@=ؿO 3O|9\9?9䫮Ožvqǧ- s| ?n>Lxh+VVZ˿cy[=MijCSz#o hz^oUhfOYʱVm^ﮟ{ZF[u|w16:5|M~(&XM @qާK77OciYu--Ĵ[`<Ч^Ң _O/ jQ.] ־D3^1S>(뒞]~i?ȆoV`?P4@YY@Gz#vu5]]ֺH`rs> W55}F!/)5Wg@ "T,/y_aӷ=~Og߾!q҇}{>.:CRrXy/CNM'#xSFo_`͝hә(O5tM= s94&:JBѸc`F.g1&:^~i^ >6j߆/3}@H 1_ms~~J'[m~.p?r.Dog1Σܥe/_ҧvGm| f~Y۰~ܛ)-~vmu;K?gtO5_lܻ&ճX󋒮>;Y]>)><>SI'a4O92_,⥚{[yzG >j+|; oOw*￈6~8U_)u046nbj?&q>Fu}6nr5u;2CAg}Vkb7O )rʵg\"ϔs#\ +Ѿ̢O.e~8>/. N٢vjU@ð{0&NM p0=XKQpaw 'M?kz &nYu۾Z1l#bS'y>}ЬPZ{^@+)k?q)z%7O z9QekK}o^1RjF~hk9%6Mkm?(z(>Ѣ5=i"cZsL~_,6ym#(5t}%aC`CsowaOEћ i\BkSal_Ci.y T60a{yӍM'оy]rc''lUaߝbO{Vhg}: =}yނ0CS ۘC ^>Wou5t~uh9^ɏs"´Bָ==wu,՟:jrW?8AnfuؿZ9|R}[?O(0}q_;__ pay/]:{Hٵ65ey0U^ g~|_8fEJ;=qNNЪ Ok?4>[9@Z9Y/x{0ljl,y)1k9T>@ O` aObG.9[\:y3 ('C|?:ƻ,~LgRp4"ϼOޮkgts iCp>IOͭ"qfCߚ1>; < ߅xYLio6.z{[Q&\p,_bh @s3D,_4U8bU<ז1 ѵcNS,'(ńB:<q}= }~\;b]g7~;){#,lg@Cwux9\s']g@U@kc,6?{|kgSjK|@Oky&h^qx|O%"/G\\.xGzC3\hbpI҉vDe7) 1z?@{oS</#,^٣N\uM_%gog]j;+Bq25a>P3J"uU^#XS9%lneW4\rG}ovzx8 pC@qQk#,FT7)efBj0臅{}~IO7c/d&2qE{x>ϴgbs9/ |ya&swC҈Zֱ*813`2+/f?L2󼄐jegOqsfuqS\3Y?O?jdO`/rm?{^?Nߟk+; {b?q<2_Hr9?SIǿ&Q1QeGj,s)ſG#72ua<}xLĆr9bUX?=`I|h&[@/20@L"z0uUp=WjWiFCs6Fu{[~ 2.mmi{ *g&N5MH}qP,5cǏ|/wF-`f,ϛ][ąk,w"˒[i"|"!xG5Xtp::9֪dJK\3%>{GB'DRE9B%!O߅TӨP.ViiOׅښj763P\FK뼹EmOMo)hX07ᆰpb4: 7BcÐpY,=H4mƇ ߠX$lY0W+z= ss^Х}+i /|㛝b+&#_: W LYgeL_VG׏iߗ?a\,K1\h࿝`\C&9_7}3?~mo3ėLUw)0`,0arW@?yphfjMݿYۼ_d21oc0y'<.'eq*)kFNU?=?2=ߙh=N hѝ&o)k'9u}f\T̮{O"q=Se(@4Cy@5ܜ+93ƚfg?D"]#F59eũBbB'YjE Hz]~v#<9^&B{«[%Tqb^ZV(2OފOCxG*!]w>#y1K puxI:FuPsoܿ , jt/N1#yɕYRG7^WIilkЙCzs}ƃr=SX{1xym@xŽzH%h 45$SgcvߙyĝݝOWw:413w@03~,T$s5/D8QG(&>Np_MY~~l|Zzr7>jL>~f-׼GƯ^ӂRw, >H9rah4,3CŃS;«l>, LMyappv``i4" my>>Lڶf|h[ٳϜY3r?X%"#U6L~rVAohw:4X_u{}_mq]{˸˫טL_/T_;y^s;ZL! k9kx~kz>Ik5xkLΗFۯ"7<yd=5wyx.=0`#G'pl͇h)S%6 c>`?s!ֵ>הܮ })'RO,r1O S08RP+8\egG@3=0t5p*LvkY;1G2ys)0r+oxXȶs=p~0 xޭb*qQ1Xا 6} \PKx >?*{8d7qgMxss~R綮/^ q?aH_un|ٺ&kyl A6%*=WoJQQF<<\aC~y%n{_< IL]6h\ N@}mgj#kŮP'gu 1ssifY>w?csc 6P_>I8 ==K0]!y>5Vo@H\< 3&L&xn|ߏ<&z=a0 `6+CVؤmmImAo^vTү M'}{fQN?ƸH+ʯ z [w6QKzcB.)p(H v-h;<LiTm؇s=Yһ?tc wLx1Ӊ>?%K#>x1s#eXfdRE2<az]cmq -wwI0BE—XBK+ivÂ~{Fۻ)2K1tGmh3Ü)] co9WCGu]?M7,>&[G IA?ڥb8yp=ymo޼#Ÿ9ۿ䧤m?yK9-cNkuC`랱ғK/:^7*,W^ # hUFVڞVyJ?s_bZ-^y *Xǻ]EqaGlJ &e۴4Pc^~?-ᅲqgx;r~^_~CmЩ?+\ԮKHJw1w|{ w ;+0~M Yx gp3fscw"j84c`&֫>}Os\]ٙQ4e]vvh՛e)y{goS*?0y;W"Pk {}*-8>8lh@?Kmdeߖԅi.OІ9{߱'p/ I|GGќX)[8ט!  :=vǟ&4~GrOw(wh'`$uB6 r}vgb?C><*P1x\6s>U?Rߤ18顥/5a`Twn-6ƃwp $$c$0{>_'wMh]cÕVwJLaV:$m |۰`]{nX79@mSS?*9-1}Eg?B@G..5AޮvI8Zz `!akwm1jp:8"|}9l͎.cc^lSK 0.׵.49n:`-8a5H9+f˽mlp,GJgSzӌ>Xb%?;ΦxwOS:i8y?}?GZb7pP+lIJvXw~ ltg.ogΏbs ᢣmy^[KOY|yc,cp 9Ͼ]pX\1ÂcJE{ܩ5KšcKx߁a_}?]%NY_jjjHo?nhؿ23q>F{?G4aLZ J] ޔ51xe=@c9y~ik M8R`ƻJA !xc玜ߎre)s,-`E T9840ޣ WF%ѳ9sfGo|qC]ْ^}oSc:F9'pE=`T9xo_3":|y 8g]w <æ? eۗ!9$_|$}؎C_8-.-Ki+dLcbڌş2p|d%+g4/JRiFoy5*2#f)?~O ڻh,K9ܳM+a)=G_|lS"ov^N>`Nj_~a3g7oe\:Mҽkk#zq<F(XXH7KO6P#un1iS9*<Ƽ3;)LvY]lGG0 _;[gӷOZ|kc.-B{Fm<4c3_+& #.*@_hVWcUJ`K A@-d up~i>=I !?^-}Ͼʩ|s풻&)3GlLu}|8&P8@e4\/1 Ms`LL*!2ƺ/\) G3:~ҋ7.HFTgp/9ݽěpl{Z>|WVho=9þIv;`H?t|ŽN.73k I<`vΤ }{s`!Mu1>{!ͭ/Ή18NNgRvB|>gP\sX b=!'>O 5[3ӻXl&njv(?A|+F\MF?4%jIo3z nd ıLmכ)bm~qii-bŘ7 l@w~3}ؿ7}Q&.-!Y9ݥڿ_4=YmoKm0@8Я:3kf77b ۂXC?M}R ?xϺ( 1 {B;rMyk讉 ׄTs8T6—ۮxnK5Dg#P } m΀eo >m z|tiRۡNgv 4 GKu^b$ނz1\cז3$hh߾'pO_aG$h>ZSH[fiJ킶ާbK(Ga[Z(Ǻ/3iOv8sk9&{9ssYgi51x f>ma\gи9c>TG2i5`N/-u 9y??}ʫWko1k{#b9ϣ5a+nDk8@_M}0qww_ar~خt.}؏y*ue4)=|*& l,xҧ9mgcٶ+ זN^hNɕ<94S!أl.9=79Եu18PDӱMۘ\b>r?q+GG[bn6G%F&sǜv 2:{@-^4??X ?},aN :z[гƸC/vf'uA ^1{mKR5lĚ2oPFQ?]+?={Cg ~ܻ5BPr9_?̿=A[%} m=Iwy>K)WœJ_n-.c(|iؿZjK.毩nP;_zjmsAbj |FU/vuZs*:fw49l1[T֛ U 2qe nc|/}lGQZU c.s$ɹ0( X^׼D;Pn't~Jo l ;ag~ 04=gMl q&(K(:c~dzОsYD_Otrj1bhwn >pq8yc-x1ĺj:P[VڿC\^M/?_bhOsB.XGe6]7q_SPg?u9f19s4sMMoZ];_? 4 |e@n@+`H `u(uKOj׸L\f{,1^)ϷdA.@܇5ܺ8 Aa Q 5X7[cON\.-IEKcsglFٽd2g?8oa˙u'4|2]pW0 N$Of>9^,\s9q]C 1|m\sY9Z_d}a`ӛxHOs3-lGρߵ{c5DZ(H$@86sC}n_2)}z7h;M84xRroK[&Č&6_-]C>aXԝ (hv+CDTTspOZɱH7M*+v8qWiMu?Q`ԂXOg}gB+q֭KIm=RkfZŊע+n[Zj  c;￴Cۿ%ĒyʛնkvR[=X75")Vn_6o59hS_8@%2[8@/ cuBOP+>&*8+M#9s\15{ug(=hI˹m$f8j=sfzD.cw?uGml&hH`?9g_<%7'}ь6wȰ [d.I?f4Že`9oI>57brM{K/Alr?vxǘk8w> 5yLp{k>@2iȵOj`2ܧp,Y:1%Hj6#sѐ1?6z?HNq{_|Is.Oi"07hEJ7)E\/0@tJ1ſ-hނ#7`\q A#t 9ȊbIr;+C\g$cǍ=]n0)sơ<K3ηPMUߑ?H5}_7:W57{;}Pl p0kU pV@?Ll>8@z :=ànlf$A~n؍1Xޓ9 csj}?Þ<oUŎ _/9:ChOh{6jpN51DkB~L僆X`:@iZ+rQ_y&|nh9P3KsWs;g/Wi*k,z/`{[kz$*r5NIpbLƒ=`\?~$5Ʒh7 Zž6YHl3R>x zv=@w:ԬO3>tl|zAsLݜNkm񞑚lx@tmu5C=|޾"rr=`Ⱥ^t纉7)}'ǑcheNm B?Rlc|3o`nƶ?vA/rx .9b_.Z,u{ 2G-o˺_`ۦ({*j[68;(Pn-߬7O [|%j PoL;+uc mu~YF}lM[f`PH|52/VͻooW}~k}kk4[^`<8@ q *<0|\5 za 3Bz$߰.  &5XC?RuN7 zS>np~r֫)1T%o9qN :\Ry}0|Ftu 5r+<` DZ9c21>-G8֠g`N1{c{Rx:sHr<ھr1fM`huFw]P!m͆yx{yOK@ڶ_Nۓ=8@Z ,}W8@PY׷R@9$@q  }qgeoϤy ߊx%9@J4ϛ (~`7}463:0i|k1vin dx|OᣩH&'o\Dž90/+#Bk^pjF;== b\H?>pǘ)?);ŀ2t OA$R08eӍ s}EgxtlxhF޻5fq6jvpncǀt y7#~jg/Gx͞ftE)X0S똶wwhʚA:p\zcIߖ% Zbَ6e(\4e&Z~i;Zr>pTBocS۴؟մ Vg /F[4Vߢj!9x[Jk@3pM @T{R?~kG49oAѽP?A~A`7?k>lC5u-|YR`c\Tc!U36/ϣ9b{ @ XOcni,`?Yao.. 6`wt۝>,eF-`gr?s%M{Oq+`Sp kblh9ݹAGu5e^Z5gxS`-笱wZ `\Q 1S:s|AXWTiМ"Zp(x|\p|p|Ȩưӄ6yͿ6*'utu:WFNq1aQv~z ?\c.'S/Eyv\k $ƐW 5OY}~d g 2 V9w\1msGG簾lZ뉵[W&#&cd%ۡ6\c˼?god/[T*( Zؿ~M}1T'u5Zl񶺿:/d?Q`{<  }+6%އ2y;qz-@ cJ1Z@Ȳz?OZ4Z@~V_z̡v8z@oA,e{ZWиX@J5hg8߂ v}ң(Ԗ:rp2΀SiNnthr ߟ=>ݷuĭ zƱ:#uX|2\&<6o 8$f~ rl%#J4ӝ"]݌.Np|Xvz~v?bQGpl$s, O+NJU8~۟uJ/*{0}~ٜ8Wd>1ޅ7/qC8~؟z/kuߟB:ڿF9U2?}G|DQ3ڰlܿpy~QnN࿻f@A `muk^@!G};:ߞ ؟Sy~ot7'kv ]@O5& r(  5D̆(J/=+onYD+~r Td?䥥 wz>&Q cyL=J`kԤ3`<8T xu=z'+{n}VIa熤S\Ņ{2C%X/{u^SY'a;yf\φ5>3#Q{آ5z/`jL`F'8}|ɏc5~:[_X yQ%@J?d1OxNWǂtЦyJw3:'envj'&1Zm0s>>weܟ[|Kܴp롿U/?jo:<$/ 6iYo˞ 5} j4s[9_Vy X~d~r5rxC9|=d[gk`\@0'.߂O~H8@h 8H%Si@a%'jT4VHi.^>u.G>#g xמM? sMxUOR1q%$ 8`/@Sq2. W}|+~ko8@:Q 8@_ x qC{ >uq(R ~^( }_]$,$5F#+y~bݽ`]LHwd ǁN4 nMiiN[=p1|X$=2y`K^䟑g% ;8,x#yE46k4hߣ3W; K d`}1}]=A~1Ⱥ} e=γ >¹zPp'Rgs.)]^pmϹbyh ᶎc728{?a2[bniq P<+Ę2^n.8~h2<cT5[ۼ1 W:L,\Km[8qmĬD[וK>ᏽ _a_q`*O`!?`nؿ0kuhh7_ếR0~ocVu:gߩU Z`(|@+bVkUp 95OT˅,p@kP5^khc9I< Bjs$!Z)륫5 .\}b3k=L85scJ4xZ!- 1)3S7A[3)nk<9~nGG{ϗ{hX _x4mCc*E2ڀ|Gb3w/qSvM`-`|eHzؾgg=>9.ǚFUۑc=ZYetvaOP|~ír|XHŸ́דv"`^ 1`O1Y?sxƟNO \Ixca`Sm~Ѽƞ_PlN3~r^ ' _|M/\@b(׭{_ZͶ.qW/KZmqkuE^6`g\_<&t3 UcD23bSզ}ufnvÞ<%i_gu u}gXO(ڙQC] ڀxPлN9/*@7Yšc>*GUk8A{r K+y~\6-6ʾ?Z_H -,7Glޣ i#? 2Wmpp-}#U#K6 Lsmxy =*f#Ƃ1iN=7= ~T=1(ɄpH!f\8ͦF/(؛ns?1޶mrOk]Br4{[pː?K)2L϶}e+>#g{̋XnkJܟAh95_co_u(yݰ毭״r@ho$kڿ_÷.Wi!_sKqFk 磵pm!hm OA VkR ^E/~jђ?i'@l*TzNjze=GPdzƂ-z$uV#c=Aߝ) \رHb{݀c܁~yW9L-43aoMacw csU Fw~W߿r"dZڨĥrFhW|R2yKA^oR8x`*n?_S*ڏjA5^8Yoq\ߙB_44Zl@4u~c@5 @mk g8 4z# \fiM4χH4]ހdax†R.Tꏍcݷwd7ak7ݞTvYD[x@8ӂh;úy w9R|ɔ& {c4Yr,.u$~ͨ){ljl7߂~~UYsRN֝YM8\slɾ8!kA^򢋧I >QD N }-Ό{c 0z]XkqCн"8/J_6@#_~cvgj= 1] :\#x6nN'49~}Ju#ӽaDANspq ^IchI.Vε#-mz| (IǶ?/u<x n]oa?~gJeIj%|uah3aktc! ހFnX~~X8Z2x=6`Tij%X.?4>/hz=x`4gpPk~1&xw>t|[oiyY>y#勨.?>A8W oLQxbs/5MLr`U;|?lѹN+V?+y}?>z}髦_xCZR灏'ZiW^9iVT_@mu9_3A?gcnx̥=6*,1_Tm@@@s]-r? rI*7_uPZPq9@70ح|)T &XX|@ Tai,"e~g8 Me8XP޷Gm.W ޻9=^dO!J1u~cAwGGncj}lWuR?/ cf:~=xi#\0uQ}??'׸??޿]~OILapa:/5lV!cLY5*oni;o5 @ecsv`?F6QFS`b Øac4UzqciNF~{ޓ8zda٘Ue@lX{}3Kߖޙz萟͵Q {6~WZ{fAܟYhd*V__%JXo44Wm:@}/@Z w7;km/s60kjF8 4 BokmW5vCm[q'P{ 0 5^$S k6&Aذh6v-z"ux,זc/r| ~>:Ćq^ x@fRà-ub)8@}֨DiM[Z0Uޯn(bcs[~xxk $3 w&l``#_6uxHtmtO Sb[Gkb|nQ>?g=͸^ۿ|loo&|0-q!"x}8%@#g>sg>u/\ TU*E-?_?15HǶͭ_14"̿凗G.1=qArqgNg8xa33=5n}ьU䱁ql;읻v88zmi,ƛ_;Hq?a~͹߼ ν7~_SyWG@&h#̋u yrjwq \0Mq1it7MC;sxkZ0޶8]~S ~mz{w/}ѹWz_?_. p 0#\y:w;yx~l7n{8oRt뱹->ǍkC9&ƪq<9 x}߽ny])0`^M̥\ch]tH8t`,nt?ſ.GwmڇڟAƄLMK {9X禵kB'+.2iwG N<=Z_kxS"Pb:f_àM⿬Ө(e!M_>WF} m8DZ_ֻMcV!=pk+c7X_^aCGd ʼn= ~v?h:q;\Zv:@LgXǿbCp'wGy}hpbO|N#?(.펼ޗqiE\(qg9fפ:@ rl+@oǼ/4X=u8d_5-`F'cc`ǢmEu)k"h#<`U:| ̥c/ηg`wۻ5e;lNAt@eMA`&:S H{ 8??F>#Z>lp!W\悾Y#y{<{zf~q\a7Mpci9ϟ'`π oM0dkXMpa6;=~5v| #a]aO;oz89s>: m{qC$^َp벮ɭڕ 0=\;U-&l].^";g!g3[w{x `=Gfpߧkx`p?!/a3g5An~wcn?|;5s ouZr.{whӾ}Iub^DAS =? T<4[:!BYiG'7C5[?Pj?MϳXs 5__'WLJsYpq?#4k|yM5rF^S@\r+m/}}1 W^_5pI:ZKcaz$`^p]:R ;'p0<}pBM9*[{sͻ`<*ؑu[O"qo1gpgƒ=Xg9^9#OG /ֿCg|q5ce^q;F3 kp_/{ġۓPe~~)O9~zOҙWK_3!m_+ؿgc/;#7>l<5_p /ZǷBtn=`9d R`5o9Cی 9~@~m7Z0~%E_$q~c:)J,`'D=tnwr ,b=pͧ"q*#ݧ?|fݞ4!cI˼C_7~B{O6̇<@p#g@`;\s-v=d/>O32`h$g{<”seWc?ź`~}tOm"P#c?Ņ_Ƈh#_t׋ks3:~͜/9)8'?ޱ.&ݟ0N-P(1o1c${u~CeG_HwӊE*ń?slk#aj5)_ G 9ކ`MP}8M@>4ޝװ8@B.+b 'Ʃ87`k ?<а̗T'_N"`? 8~0FB-1Fbr~v#X`wIa& ~Á+kp`;Q(\D9^Pu 4BgGyKl=܈0vpls"(c9p|[lx7[wxD߶0_} ϸo/,9WCgW}Gq8gW)x&="}x='h~'cBBv~~;g;9? `C?R!c(y2̿N؏sAmniLT~ lUԷ ~zcJqit4ck !틕1O ;]yisxhYOb q6瑞u_CNŵhr(q 1?o/|8Pc8l|Z0]n$]Zk cXMUs'o4wL:@=m4Fuےݓ?Qccpn <=O΋Eri#xt xPxťe8:<[&OlOx} no@ثG5v? ؿoKد"&F/`X9/OG,mew;70??7dØpS @V->}{u:ߪmv.q@~Pr9y/1MLГ ~$?Wõ]ZKwG 0e@i,rd(`H "py<Ú  rvԖ9&?n\*J !6Wɑv9ޫ7qMjÔfoqF]1$Rtຩ?]G@.r^k!ǖ󆰾]Y">s\ޗP//GY?k z)A 'cqg&ĚvQF~0 oc~3DC(=pauH1ܠ6OW)Otԑc_4hj'NH@4˶y/Wq)_"|s]׮>.,15 ڿ!Vd);ThD8,p\ (@ :>4V S8h`8߱pq%[b@~T@uJ?y40ɪu02z:gDB41UmpQt q 88`wtӺ,@#dІۜ03y9G4$ww PO5`.Ek̶)9s\8gu?X _!.#65{-}>sr9Ux1X |lȍ'V,z|1~}Xȋ11d|0?s wsG1ǯ%AB:VtԴ{{dGG.0lնth&Va5m~9Y.q..%X=첕ߙ?:O9?Y:1s/jkq +z_ ׵ߜ?[ڍrw~ H[o1nzcH31[;ڣb3y: =Z_wm$@9@ 0תy,@>);iZ{tnO@Fpgg&丕xl$D ' x{*=7,TǷuc_1S7u %2PN@Aۿm_;?9;~c@˵r@k>_ǢXG=uqlT~DA0mg}_nq,R, 3|{t&w1(8vN' +޿;Sh`]ROq=v.3#L0np $IR;^b js-$oͬgXyQ8\@K b+1oV~hѿKA?)~#2W9/oo޽]+`|.kF`j0f5+]د _y&*Ú\wbLv׋;~cꀫ߯?bJEfL2SH v@?>#p7?se7_|0OvdoGw:c(%d}1wR_1OVcʜ?gkwbOG_>q#o2_k|O*}kŽ {rk|*'ވF@<`cQh9V1hFca=qx>p soC\\ܥ;ú23p3p;/:M-蕈Ju{/y7R?],Cy80^@[^DjA}CX=$1ʆc[Wc.J* mMu%ua>Ϳ%^U0oqQƸC?6Zzޕs/͙̩j330OŜ5ubId?v[ca>iiϵ? ׶X_-'_ScW|?}eG/8 5ڔyOEjצ:8j+8&`kiPrPblȱ[=sV8{'RVZֆ.8KY8^6)=yҺy9#p.;辁DsEto0r\+Z .|>,5ꤪ :)IXc&0>-/Z $O;AC xN8in^os[p?W5~fMگu!sR?ϟb?vߡ؏3=Jtp\??97S"i5'WOp/]G5 UkOGO?G6ݑy^ ?S?1eO{ٜMYc~4bc?koKCkO/cwu5X~ɮ8uHQyڽ3hEoE=qVi>608κs,@9S?hEJ~f̷\GT@$ o~8 u8Ҟ83d-swۋ0-+}H1vW-dxh`zBcG8= =F 7Ri`h :}LEV~mX6RzR]?Њ>Lj&u(T~bQ7.ŤEN:7Ϥ9ڿ{_aO'ΕI\c9km=OϽ4l~0]{i?!ߏX?E'{C8@`݅kAhۏuųo?2Oq:z?:{Z? G[)X6k~}꘼E?kԺϝa_w,.ȕX_k 54Z 'Uk٘^kp:}Q<4y=R''Z?n5&H]P@n(@s $a6 @Tk&s; /;j\A&7>|:\B~ǀ){>kXkOߙ +\1=$ Es.u"Yl 60}#|8 N6'ڬ}oj,7pL:?#cegA~/Ap/FEۄ~_(;mOxD9A/R4rs)f^ =וlՈ8_טZe10zr(c+ܿ 20.󏯧?Kyyso_?a]go? J~P,u?:bϾ 37 q[2&b\05O\Sz&"j,@15j@5Mh6C]ƍ89`/0;Cg7hX[' aэ.-rɽ`7Kk}1y6FYz b=}HU9FSl[}9'`Dy'g,u ߿ߪ upoA_{r?sb/{Sr^9%om@?qӘBG7G9[]/WStfEߚz?3~<؟8@K'_etQ/YO9y k>5 ` @?84O5}එڀkun R1ؿuF+*}5 +_%!eG^P?i:>_V78V_s5Ig}gU @zs `<2` }ap? Ӓh +}J5X@s/PYn;5*)W!-)sad_FX?@JńZ,AugXFX7+ x{m6K-6(@vR=_E{9ڳB2!Lj-{>2@]_句PT@ `V >GZ~CO쌪_W*Mz3A4qq~a bT}[3i/'~19> ~9 ?8]z(9HG0xwy?pnbP.0_kNdA_acpb?a?)ؓ= g)oǕse]km1˶Jc61T߯羨wGO_,T\e/k`clVm0/07^)?Q~ccO 7Eo斱7j u'ljmj1{~qwUOW)5E kܟ+ng{k>k _8Ƣ@H`~5|I9@ȟ[":@Ą@> ZW~ :]Ř'!`E +cDpRhU]F8ź# ޏguڨw^꾏9gy@#uYic]W1gO uj^+% v>XcMB(Kbgח0'zrT_B }יsGɹ83`"b7ZXsz8{gĜF{״vF[Bo&[}AGj7Z/jPBt?~I\Il~,/-b[ݿ_wcq,/E_?wzgEs5- +>u `5 81c`0hM@gkLJ/'z/u9Dj0 -qFyT+O=j!hA0ΓopNhZ/  ZĂ\1is=H#ixO({`s^S7"1[w7pq=^-Oy㤊/r i _zͦxĞ]}`'}FS3'dkKiRs#BG^^E~MA`F/'$`wps69]a hbSkDwoH?/'XTFwMpj a+g{I}_&_8? ڿ[ 37/9w7hPhsoXK{ױ AX\IJ cL&/`':N_8"@?@#.- d9<.p`MamG/yO5+"\Rr5`~^H6 C-@yv[u0H{9SΦ^+ogwPN-;V9@+zG5}x?`T[PVGse>>{yse0m}iS t}?{KT{\\38~͛\7hE>U?@Wy _/UDtD9=l {+3Wwto6q1h6p=b&1lVi٨X}aUFZ)G}yP ڄ$6-KHkFxnCR>6v P]`{zcR("s)\Q~Fng-o:/8݈FO|gGqCw0ۆ|D%~F}~2so^[e`$??-H4SYsΗM/19}q0j3Oyi=-LPa'1uZ?p߶~l ȁeܟCs_7+Ƶguu^},.joGc+e_g}J<ߵԍI߻"k`)znx T7 hki]``X.7`Fbat<\g^]50lYP `C[f@bJ'Y :p`m۽;(uXGyM߹갡Zj'ٓܓ{TKzhM$Npr iPn n|bwpn-<="b7hx}C\OGz1-3 ιp[T_3J9c 5pV7Pn%0p\D($бE5td<8%_uˊ~ 77@p1>% k$?k!?\ ;"cYw:jDkVC>]wi4n|Qd=fʬA5<D˄PI\!?s6Oyo<=trl;ii2 QĶ+ o/^~n}Ƀ!_>1s~6Ts%+(qL#66b?ƿam;xFe?Vv?^Tb>;ov~TǸ1M]Y+:P){{_G/Ժ?qg|?W D,ϡ)`qY/fK~ _mõs3@NXp&lO``r-_2W1Wbsn gV&W>,< ܋i#ҀDzX!?@ g7 >j-o[MrƐ8;Bq]O}kY,5DL]jl޼w|Dp_Y^ch|]yhE8jڶҺ_'{a+7}塗J61|Y8NEOz?a9/O?<ؿx}~q/V}淿u GɁljbzz?!%߱ݯ~%̯o߉6'/bh]ToHo'S(IsoYW`1#>܊k/̅X  t~/HX?4@I6m߸n˭p< +vm~)C 9GΣGM9'%i侰o&, (80T~{lgZ4ң|xg+%{[p"SAH6?Pښck 5 O4dݟl/-y~;Wo?PKk/?*bb/lޯSPEY>r,?z,:lkL1~+_[q>^j޲'PW-@|!g㺳_B/4XR?e฿ (b.`>Xe3q% ED찱`,@59~62h.\ `R$zCOq SZYrI; 0nݬg[/c_;KMx/CvcՇ8>lBbg=YpYGܷp!=Kcj5/^Iy"w0#3$Β_yF:e '?F}b#E+9[ 'ߩ_LL`Vc6ۗd_KG9~/y!۟qI}S5/λXR:7X @\|^V+Sǭ8u>`غ0ƅgs;:-?sQ4rX+;?qOڬPs%[ p'TG'P^sP|r1}ͽr'+vKM[vpE}ӹ&yX/r0[!]HlgޠOFٍ%w9icWY1{cMDZzzKM[u@)j}OuAb[uG?Q_n~D?.G;Z?d.c?LWb5`iޛRus8|?GrEc #'#u~n_u%L6؟jո?s`;x۬͝A۝uֽνIږTWlwqB#n-Wп?N9NOxqB)/)-e{YkKu^K}}߯'w?~6u*iMZZ.{-m/ң=X_|>m;w'f9?]M1_&Nl׺K->rz-嶧QJ?ʵ,G)%-˵L:BXs>꿲-9#3n~QER)׿K)}c^kr˟o~,$G&;C |G)J_~}r)W9/+v/Xr,RK#oUշ~jVgmf?d}vzKV׾s{v뗗?Kmv>i~%cUiu\J;)VJ/.e[i?5_iR.6 M;qَ|t쭜H#_[:~>sOkiRn|sv+vQK6r{իBHC0moX~Sivk[ʹ^RnZvm_-ޟb@B1B:|KiK+$u񥖍2)e_V}v?G|Rr6 ?QO{`qhZ~,'rnn|T嚔\"kM6&b?g @2MJն^UƂ/wl#~?py5kدg@;/{O hW٘:`r2>?4>ݫ3_V@;4 ~5O(1}^6&6[/l!I~^hmomv{_O.a%K{C@hO|>RHP6B?1rickP>'#OVZXҨhE4RVD4@ p*$>\w?~rj;'l^Qi|?b4w9xOM?@n"gv?_}kG[1a濳nh?^xOy}Ԇs+p]'K'>-9KVq6% B`lW5N>f+A.@~.8@f?@u HhXl~Gq/}؅u4_i4=c`h7V*o~)-_tVM\Dh߹߿ Ը_J3sl,1,Y+{c__cP=k6pve\g? @ h]_}3?t/bU We1 s̟DP.gu#(ENE>&~ޗUgv#?l<0&K0_sNZy0/abgm쯏w: ݝ;\}:>$  @{'14w𿖚8@)jWO0> 3?q0h2h%׈HI4@6*cr4@rO%Pc/C8w> v1֤w?T4V~TP>i$ǖq,1sZu*y7 hy:G@ NxńGe CE#v>kcQ ][MTrPV\߬Azq+ ZC4hz|th$}!}k1Mf.8?)5g0>/ga UP?g~3Xټ89϶? ~岸rb'>{-hr3 w?+ޏ-xI: Na?o__\9/'>/y8!Ƴ2o{gRM Ür\PsHbt@ `/|,45p8D2&~_-6W7) A&3Y TyppsP_s8|GT 4߯]ڲ9}ޗR:޶}ޯRWTKv+K_kϾ--_7~\y}q@1.g_hj5a-PFa<ڀdsxN O4^9}mg?}ޫ pK` 8˧0?V.IcP{ZNjN|%7:b5GPb$܂#u, -[ ˱MP[eW(h>'ϊs_sj|j+o>*jvU__l?*Uۿ?ݯ͌uxCW@i0맱-`NCt׼Xdi {%(s޴gk|Ӽ?{Oӹjа&=iBa=Ng($88` \,@gcc|*DA7?@El{+I|iB?$9qCce8߹#?$Q y;A:WrUd),?kgl6љb+ ] ~_GHiYB#ܯEb f>u)K췸ro}?J{wUUK~GW*jؚzcp~-#kWO)8c9i5/4tbS7l~?w/}={6+u8}{hA q] 30o-NKbO`d6<,PW?@(~R?w5e`EM4V5H-Un?3sH(-tunwh>2m@}̡oycwlx  Cc=;,gg]޷5fx>~2[}Sb[ סd?-Wn_l]ߖ͟1cB;Bj+=$dswt͗ Ñg;M;oG'j~&0ޏZ?hwfg}q>>>=y|}up s?$gm<~NH<45=Q|msr _\<8iFq!ljc`F P_F;iG7j:`+c.0?@ni Xҩd^ K\9g]KXN rTڇ٨zpt?7z(xMWw2c^{dy o]Jm}?Ͽ{k_aׯgK}W^JX~~Ukõ5_5)1~=Ϣ?~_iV<~_}'Z5_/@3 Og}4L < 0;9V1 _IBX Yzj۝_w@q{ @?Y[4[efPXrkf ԏ31 oqw7x|8_`O3D[TrԢɷ}9;Wn41,__P, }ly}-Ϧ8/bэi3o~R?Y̿rCٙWۿs`>cDU Gۿ_77C`g ]ؾckg'sߙsF_?߶w?>O:^ueNA N\w)e[( pn/7 9@7-}QеI k}˧̾8 Ǽ@):u~_F6h}t#[ëB_nP9p /!h?csl{({{N1W=Cp\y6s2˒?2^%7`v_{<($?$ׯݿ_u:h+()_<?E#I̟ƹp&x.^==>sd'?g9h?Y|#nkl>yK~~Dq>|s׵7s!^S aČ-hn4A_H_ş@oy_l^2?s Գ?)upzπ N({V˥j逪6NPt-z9 ebKN`|@5'p>n1: %AZ!?c5̲hI do~-`>"<̿ #=Yi@e˅x}gO?Yٟ_riW.rQY>5N(?=zi|v/?Wli:|0@ݾSz][s\0 z`k@FcDG|t- = h>8 @ET ~z0/:z߀ΚB p C,@5&~x;d:n$Zy|_d T?0O`z@,}?>4$;E.E+Q 9u\jWbgT[>뀢mE]eK6?H_0Z|7D|?qWy\7 /Zf{MOݽ}`S<ԓ~A'egj]︟p[Or~u֪߸l{) 9?r5{4YS;ᅸ '_aQ8jf?عM8{զu:hU<ۍua@tr l91p0>sO`@:@zr״{Q _b68HAk~H]g|A)˱|[⽭Ϲz,Wlܗ,6~3W_l?G_Rj-}.v ~>^7_ﯶ0FkzAϟ}{ȩ>]&:՞G0S +i}v174@S@tg; @4t=.k/P@r0f-ɣ@m:றcDXܯ}k8@ -hԺt:T*Fx "mCl VV  H@u~h><+wikLmuЧr|ǟ=|\~e~}9w[1O>~jo> I^= \I?YΕ\}ݿ_n?rGh>t/ǜ=c?O'Q:N|-0>dggv?8@ܽ0yIĔ)?:c> ߇lc~j Qxxb j u*nsD-i5@=6 8.~)5øs+8htTW۽\@M~uX1t0: 5ÜC[~ano54Vo5_ίg _燿k6GGz\}6j?r>OwnNN=rw~qX $v?l+$}a?uaq,]ϙq_3o~f'>|f<5b)Oߨ{]lc8?@O1.%~u-Uڜ7NhhyH>@6|vbȸbѢ|F@:}cWx5 `_kH|v߀ܭaKv킺/@ig@r7m- d2^5!v@>_ Q[;Q/h_j/*qkks{.f?~EZ]Q=>?]|_nkr$/WnqAkBp\@?c}g~-PؿK;vy/6^q03VۼO`}8F5~ܙ>='~_C 3{?石z3vH@L=g' [g>iEp] cK|,a~XkbHRuT;ꀭ-/ ?@P}^Yj9a R$q$~,~ya V^7Wg>v$Y'@lAרVz_]GWo ^,wE`"N[뒓">}_a6V·Z.$;o^>tGmvhp }'?'67l[k`$3< Dzh3/v~\}RU~o[ͯ@SMxn,?om 1~=m _os}~yD0!0c6 0~7|1E{~OُȣG`xD_i$ [ j~k4c|SY2 %|$1/P r="u@X+ 4pƋ{dZ] Zo 0-_4MV+ǫ"si>ԙK: k/ Eͦ=c~lOpuM_$f7rjj++I/6dJ:_Y}nW'aqMG>+#'>u@\< v\:-Fi><_5~n=!6۩Zx5{ݫcRr9k5KθjiC8sX`A#'9q1Y}R5@i/5?Dlk Ǿ ރ'0 R7 6#S\ WR/3uR>\~yIvI?z?e~GOii |@0)⺁x_a-5-/`7D[6دg>D ɜGP 'TP-'0?C>@cslܲfǒ: P:7x3?>r'-'`Ƙ7Z >ZH&BmܲP{]y`M]J]Yj^fyKE6~elqNk_r}^\'Irdv^kS$p뱜kI\│ P_x/?/@#us힒=mx,^ \]p}c?ݯw?]$F??}?<O⿽l~\gqVt_5?9MLٟ}?M?6Hp9_Ghi4@ 9ܧrɴ.>mc׸ζe[Wm;5c2n3^%f=]y_ {;&7~1GcL8Z9_a: /kqs{?96j;{aC>46bQ~<smϟߑ',& xv`>-r!1?5|~@w2?4?|YN dn\.`2pŹĝcy:_X3 `G?/u8@-k$f |dID<_;/Lo"C-[L\,Irrw_':`zOAs DZ},]dfˢ T@5zȜ_F'}=柝5]c?fCoܔl88\|!׏Xo~w\y!$c[/x~/u.~|ac<K rGȃ?W[}Mr|w x 9r>@5D?`2'P 7r=ݹϯf+p_@+ t?e;?, h?|qP?ql_'9/g4\\c5@ ȽMu7t:|uX۞~JUl.O sY| f/<~yuNg{o ,{jdGrnoߟwqm먁}#%Zہrؿݟ/E*7?(eƮNLJ^tt;;C_j4}9_sq,|?=z//g8Y.h~@=<~ƙΕ?S@ݳ|cEP@ʸukE ЩƧOö'ܖqLIo[37<rmϵ:}eό`Jy^g lo{IObKmzh95{ |}+r_mZ߷=\'v}_c?w_D_cm[|_?9z߿r??)2J?A {^ ǐ?mq}Eqv[ќ5{ >|fC, úɼS^i ٟ߶mlj/E|OT49q,Lgn/@4@+I: y &ځei:='P1w=}'glj+#gj ?[ ٯ`ٶ nsn;_}RSyCi<g;__Ka"vc/>/~@_bc?aII'B՜{B/{p].:y_R?Î}'x 㠒mArr_v/&1Yάzv*AXsc@JrYcz|*4ճ*z~SݨouԷsv{gq>ʢ~s\:>^x7YKc-R%kwWx,_FQ9[)g;zף^vԷ8v{֎Ͻqyߩ00RVxGٯ?VHnx?S߫^\gb6{}HD:*g5c?Q:Q/os;ڎ(kXRkc5n趰_ժoKOe<|;f"ukڎGy~%o}{Ayo1qZw5ʾ;F+OecǍ"*$o7)~%j?.nW ˓ĥm}cw-߸/z@ k]ӚdWvggMhppM5Y /-JYk'3~vIX [/vj'v9ulE]>k Q8W}TZQeUtieF/a2~wn %l?ߦ ?r1?VaOV| prhcۘ[soʶע(0ש6ٯ^?} _{(:|6__~??)mMO|3w/ ?1I/  :84ls@{dX/@l/W_fj\Z?(۟5Lt p<1]}c >yEX:3C.z@mM/m8uK|۬f-m-8"EuWS_ +_ym2#|;3O?7:qlCJyt̏co|kJ򴉺p}w5G}oA5S%ͷ@a_5G]O`?˪v}}~2;$k A15x^`0bA g+jOqPbojj໖M/@}5@pH}cFl؏>N Rds}|5)$E0 _;xT D`?tTCYg@dR1?d!NXC]q=O?>uz}/tFм__ڧ'O߿a۟7_}5{{ O@kHJ7 7U$0gY=K}/@Mc.Q߈~w '}*kϾc@if\ OqFmF*?c8-R |CP.pjY? }4@qY]gj)|?/}9s@9a|{gW O_g::91O|z}_cU4X3~/AدwxL.d>hr @_Ԑr%??k|~;G hhx1CO+c\oK>Ǝr >/i DMu2@ /jq\sXwf@o>춵|^y~fiTfmk?7U[&xIy7΁?B w_W3yrr1 4k;\_k%ܳ; k#_e>d5OlMHouvݧTg- ?%s6 `k>`?rMr/nt1_4_XH?}}/~hcǓ8w~=P2 0>\>r/}-jL@x>@pZE uZgZ[O*  _y"Ʒp]#'?Oǜ1dȞ{S~snS@`̃4OWG}w۟?_O'v3Ƌ(ͱnj/~NodǕ5?4?_??$,O֣_ƨAS0#đ}i1v~,o%ik}.0f[؋l XaM <,S.~):?ikJ_S].ih8'95 h&/><#}=gt@xB|@?kbl{mQ_1B)u_d:^)-Pvudo}dV7}q5p8d.Ag k9]_g2߱Ybꃹ1 /3sp=Y3Z4OE&(K/T l/vWu6l}g+>_^}w󿿈KWϥ?4|>p(~끘kyeLؚ@0-pG85>8_F5@? jUqzqj[?`*D 5@ P-p`A5@vU _U@5NG^("xlsK~uyhO3/R7v:r/Ys(-晱~avJbOt?7իЪُ4TU}~H~{J6?)/}x_7_<>hcx1/w&Oyz=/ /W2vx[ u(W%?i24SE ?m+S.=*zxs Ф k tZ}{¾\J@?N_m?r9o`pA?xͧ OX}$ ?q_INz>-3_m >xW׳-I~ m|c^uǬKU\{dy\g]?yֿgv?i:"|篳H͹p@zѶhKY'e[Ƕso>1_?wZ  9,>HN5:@95GMcv5i}7W'9!'_4Az Jj/=T YLvB@H)q͢%Gr&?@gg]*+P|hG?40}`F=hNPeSף/`:c vjW-jޛ_ϥh1g?[7_tnzPq_5a;1?mlolݯGO~ xa?s]2cw5̣g]~i5 )(35qsg$zf%z|jm/<9Ug$$`s6?s`^ʓ~~'c>5ի_{v}~̧2(nαa.ˏL4@_zH5'̿>9]^?725 ?>0Ct4'}:j )h?%/>@R qw$s *h>J8`'_X??byr^J<_@x[^sXO/rc5v_n)Oz/ȼ}%K`vM JZhGʁv?<u/?C]ڰh.ԛoi_ 9ds80\Nf:_@ uy],% &|L~Cddl*4P rX^ٸ5]˅׶:iۯn@hNf:"CޘǷmE$V'y&n9z/mX _%i>p i-_#mލչ#oi(3d /ikr* @j Y~ur, TU ~tV31kmuu%}\ ܗK*ɓַ|.K{`l~[c霃ؿ5H.P{>WrMz?\w+W'?-۾ 5Z._=珷Y}^)`Q XXPc~'(eZHc\Oem]K+~F u/` 0ɧ /N%@ {L ౎5h/d _F]ufv}7_""mnyz\l/7.ٯ¸_xo_7J$b/}s#ue29f-~,Рm2-P>fc;۟ݿ#ɳ~~k<]w:;ma_'~F?@ &_m;耊k9y E?`L%]`h0\n lg Pl}@e ̗I ] ۪d֫ib1XY lhhm@[1=:?9M&J}a8(둺߸R~%_0ogATQ8أѿr9<$dߝ~Bk|`ȔL79ص{m~ͷ,3i](タhcT8j.@?h4>t^ɷ@|rF~M-P_6~#ߺTWS{<xO:`0}}6ch/澔zcuv= 5Bi_pY_4_o_t t*q9awZ.~ 5S&,CΟ5o| wFwF]9(gX_p{1_6tlN $\*KM~z}j/Px-T?8_@ iꀼ[-< -]/L~jgo ?`<<Lw"/at0rŷ6˽wWB ~7C>}/zn@ahV})?^.(c!qo~)k|"%. =ߗ쏾ftܕ>];y2J}@)Qz7 :kmJpa}5e㸼nsY (Qb#'$&ZhruZ0ZTwHpe : <3_4@%fv qk3`.>P'b~/=4ֿqcUYN|~}M4׼=v7WO%Ƕvy?:V|f}fDz+ P4z`(~, _h|^\ieq1험#ps1&ٟ`MWyGh+.v7<)?כ׶`ﰟ+XG_/M|E}_xQͱYm;"/?E߯;chOb_A7rO#᪋޸\ym~]QBm2_* z?1a?k[ ?@s%\ f.[nq~=/EO0J|},4ί|5y|Cx~_K%eS8ߢ,,逓˗`>Y0o?ǺB\b~1`?/4DX:>_df+y9ޏW_{6[Y9†縿NFgky٭gs;_VYii1C&TEǺW|[lϑWg{wRXqgZ)x  m}/֖߿po^̏vfW>s:hx)~?p_-o{5Ϗ@󥍠0l!>5@; 0[c>Cp_cw43i|GwZÓD-FżVB~ L1,`AMevO18Gn5_b>??\>K*'nK|{6{\x\ڽD,|ٺrv}/ow?I};lG>޿,_9ܧ-ߟ7o/g}x?p,;Mϭuib>߮q ۟;ؒALyx[ZcJ2UaL`bl;P\w474עpw>!?w/gW1z}H|?3eS$ov|l5s?s/b{_37So26~nJY6g|lc0֛@?uC 378_iX i,9[?q>kJQXAQlxNºx "3-Pzyd\o{rk'ˍ#Rl}e~~pR>_t\w>:+kY=]͝d(ؿxM|i}(_|0J }Joc]?޳=m^VwqD c< @ ?ʂZR䀹|S^h)_sz{s?:@^;OȌi[z5 p4T` })3eWzy?e˙Ϲ5|wKsWG?0߿X` ^N x=: O]R2-@/üF `s~_ _+/ UkU]{wPx %"{oM @dl4Åͺ~6m[l4vs} ߄h}}=-_/5mf;mkW~/9nm9jǂ-a_a9:mX_{_5/-?zj7ZbiG8ϒMj30y$/ݶ|hkf{D{kھj:7g=^vyt}\OLg|6<:ξCx}xwۆ+_׷z[_%}] }毫{'#|6M#9I9gOӲ1UnF+{ڿFq9[>`nKc)c.zSa۴yZد'WBD>8N<8?ywcZ:C ]l2*瘳XA}}zSꃷ[:BgYx>cMqF~<|ހC쏹w݁įR}568~N$x1|f#qO/G?y= ?ִ聎'׼_| MWiϴ/}>tOݎ3p?v\lSK2u,ǂأW@xTh?dO?ŧ̓KT ӏ4v.P몽K_16@k8y ༖ߑ1:`Tgw \B ߯ ̕;{`w?oΚMGW{֏+OS>W4g>} 7#oRf\Ւ86xIO 'i+h6\a?_~S?z\yng~|@fagwzn+?y)sEƳA1_k?2 ߿e1pH_Rj1 3$mP?Ygއ+߇cYd^M֒߾Oi@^16 {YP\@ '3tfr<`i.m{ ?'oΩ+Xk^>ˍ~~ᵑ|~'`VffZ'O_wO: kwg;o1ҩm ӰGGj_-Ot@'ڡ^w? ︯41k`2OD(KlS^uTb7WJNL'5!.ice%%6}o|_5Iz6A:*-16kځwqj@i>4fqxK8=?#905 \q?o+ayƾcrחk Z׸?+>gS_ >ߟZ<z`l[S;f8️tܡ?wXy\Y@stje?5: kh xL$jod- <[h׆~Ӿ?Nʋc:۬1X#n">Ojxomx|[b~d__#?4bmr[iW_r~Ø߉;F_~ 5v(}~x?]g_e5_g}Uq{l ]n#W_~ Fgk |dC,0N0&IǙև5hI5{`kyB oIG]/˒؆a%aI xo hENjC<s;?EH$?ܺ=4=}u{$*+Se{)Zic͎63m`<:~S`'hw`Z}!4cYX9^@P ZiIwEZSP{l5zߴ|Xh:,XtR P%,N:[^]?\yR܋S[P&:>b1kn_}/﫯uZӷŜ{ǟikُܿkݶ#%`7~w?5WuS7|egǫVʑ-3я_9ٮ59 %6DQ}`h=PPt+5O}ir2oDE  h)i&훫}~eLʳȏ} 6y%PkF"t|q04O}cgsb[}W?3p?]0^q?ϣW5iŕ*7?U T{' wkU5O{%pcoN d>qi_Dz~}oԸn['BӖP0yck@zx'< 8mdk4y})pZ)'piSMcsnsǝ+OqP_Sr@u$vx}N~`cUq& A@x*[c>?_s8Gu{1 uW_A]f[p<);'~a޾7?}A6#%LfK?vr{ G5+~q ԷWt:ViU `s8sioc矹8Ajij>`|8.[o?oMRj{0hG_>`SM؏-ѝqkk  jo:g4'=׏u}`Ⱦ9@̏Y]i4ıwOcZ﷧!oI?f`~cCEo'Pו+OY55GƟmW-2KD_4XkOX-'}e8  -᠕!~:~RFw-& `-6%FJo:=V!lƢe$:Y5Xl9}&v񋁡X'ko5}cZ~0?t@'}>QrS /W'E_u`g_~|~ט]{Z+-짶|CtvY/1X= ^~~ ˅|y<@5@ s<xt헛8zlgcL-&f~?+o%h:]^o R?=rUAZZ#x7wtg./Y'_[с|ܢCSTot-+j&/Qtwާe6?-!n/b~?O=.~~o]vd?a~k o7gy,mWvW_۽m}M5@.@T ) NӞ'c(iwcԏm}f8(~*KDl, jl@˷xْPc_㾭$ yq;^;oާ:iF˵&RkVT|nYNKٵ؟P9>`5@=Dػw:@ G:_4Pc*'go ŹOv|:]?-֏Q>g|~7{q 5pɤk__KU.lw1vKc~Ќy}罤)t9~ؿs.@f;-ޔuFk{Ph@ HtqW=hd-cd# N,~Sh?j&GcAmqA7Lm4|BF~V|[gl[W`* 9[#h}P׏slߏv+_:/rnF}Kb}`M Q D/ Ǹ62 0|#k7:u:qO':v5KOƟkC_^[/=>Vkq\W;FP `bXrS XNLI1?hn+4 z v%C?^d:sۍcL#x/Z_"Mz}AADl#Ѓ~Jb " u~ig=8}g(z5Sfgw9}nxN{~yy_5O>#/7+v=|ޛפ1-~#1sk?^#_ς:!_=c>_QG;?xM@`t4peȋ?M:@mq|5:K}Hsb0ׅE @ ;5jX- d-@oZ Z/PYme?7n|D/q=W̯DqإP_5vX@U羴~1c_/zԘoW߱7?mRccZݶ忥δ'cMo[s?7wq`jg>خc;$x$p$8OڇczOmVYcX?Yʣe|ytՠu9}qs<60[s(: < `(A t~NcTo~3֫~ZͿF_;޻og 2]@{xNkɵ}fij1kn6/g_ڛ9?y29k|RvOg_W;unSkk/xdfRo{۶$ fkz%pLhlem`rǤ~50NB|@R^@ Egv>~`m21#n/1/Ʒ/c2.>EGcs\D-~sh없׺?_# }oy:`D_ڿ(gi Yŷk=םz~e=<^c~|LVoU (=Z`OꀨZ[8M}%1jOGIh=̅zF`?][9වM1[M@GskHRm]0!`L};׃6:r>k=>MOvZCd}_{{=/V|W_ggUz7+4g5J=)<ոkG֯>5eKg %1X A HZ}83_m@#8n 2N&7Z;ګ9}tyKyzM`: 9&д(^ HA} qpi):ຎ͙W< /| c@ynRtEKէ7m̗?_./)/xG?? k۩yd-":_yg}|~?c?~κ> p:ȁ{KS Tcj`}eDO @Sp r_bϬs?Ǧ[z b t0kc.g 7 8l_z"7SuI>{g]@c[39s{ܯ}M*uio1lj H~`wp'_'Ep}N_X㷊=@%;:.Pc?j9Ɔ/"Q'dXo`OVwy\@3;}#hs `U*0/S <Ύs3?W c :0=0"C[8mo6o-p5{k}V cОe@:swS/޳?*'o'_OVwbxw_}k)woO,%0.Gz&{~7o'*cM+x{ )k\Vh =@!{w`} X kϖ Z~  .g΃y~XT|N%~ڊke>)?m8@S;v @ӬWwq}_W܏|c|Ϗwh\5d~i=pǚ.zi|qc?y\y?`ou0B-5pZosm-. (DV3ߏ:ocXQdox8jȞh'|yf0SdY'VKK>{~ku|56?q}{tH^=ӎۄ*̾!$ܿڞHy_X IZk? ~^F hksׇl_4Y؃^?h>G7_L60^d~dlt "x߷q ?Gg2=#y!K?V~~0[ܟ'~nA;mk3+p?eۍ-sY'#]fGV3k^OU==ކ:@ ]bs655F}N@7i[#s3Z|жj|wqs^3 u7֓b6-طO4~;A `xC^w 'i6 @_l?/1'_jcݵ&z"?IFx՝mAgtKӦ}6iiꚽH8 )3 E"%;i~UR۱]6(ռ-y'fW,'21#*gZنi>kTG#|=ʾHxU/~l_?GzerQVv\!orz2;Xjvώ,f]1u#Wb3Z7("NsKۢ:'faCz2z rE# vnɰqs/voeH}?G/#3c-wMk%ښRRf6(}+z~,βJ\L퐖\,3k:sO|(c%Qqj+c`إ3WQ O- þߚ8z62&88GthU $5__ wpPuU8ZBGpxQ2PZq_m0m$?9NRL+~nkp G;@~SX7Ĕ_Wuoй|Ep`c 2߳>MDgW 1qٷl^ C_8Vy<3뼏U,b`^xpY 8h ָ4hc & вUL E@z t5k~d}Żcg/oyPJ#_B8=/1/n]k]Ag7=c배{c+pe蓞z {._)VWwi=C!A|z￟ܿWkaWm,}~]X9cǜ:1Zhiet`_?p?fw`_k~}9;ho#d{m''e{z0  x0ce8.H[~E|+>43]ۭ~ /G/~¸ 1mh3a>c߸֥N}nkL`ʹc8?lr-%6c_&v(aUVWYbw@l yX}[ /jns}&٬9\TD`6_c ڝ 000ph704"hj:l l%2:]ca?z1Ozm/1`I)0ٻ `[77c,y2Q{qX] =1gOoyk~,g:__cݥT5Nk:݁g>=ڸgE.g2k#94<(yBup8oXn?Cϭ "Bڵ%?yg.=  lo27pgS5@ x q_d￿El/@jK3t6{C`_;FoZ&%Ldθ/ϛ6c~}0ߝ&O#6t$ޟ1ߖ 5-Y}racgo؁q>bd1Y]>g?;{@jܿz,3s41o^8]S-PL 11&bc\ qt~[t@˵=yfR D9~:>)(`䊖e=:}/Kf([m<f|߽q!~*8q듘*]~XCEDs| eUCp=~ԟ_V ]e}+ʽI׹ 7gZ#c 3 o쾾f_9 -tD㽲{3`I~/W p33O\Qo7ڥv~w-xkhi.{5e{ >֯t-Qp s:/z8@#:l<3_E?cwjO:LXz:$_7~G5Y`z19{{?:Y_iY+Kc~3ߌ8l?El(Z_cm?~Ιy_CD#:b늩ٿX_-mL؆*y?gzTI:bMa,iZ`J>6x@| Rs^XƗBͯ,c`-#iLıHEK>Z0Zsb>x"~|_@W>?&|~7cVK̿W]'<41W3z٘1g56vDG egڿ:8IJ&ioßϡL_\/exҿ! ~mWx„TgU?OK=kWk7sܔ*<Uȳ~2_y_g>?w}>~{|!DXѾrV*i>c |9=b?_ŏ F!~`^? D2=}}|Vl |A8 woŎ8d?P@0:\s &sO5?r_nQų'^cTU{zAn/A˪Z=xfe}x?qU1ac L[-α2d2~=ExY0?_2c=k쟟'8܏ڧV(s yC.yAc@.\L[XSZ2iǻj&?j?ߟS-08ZYA6ZL+9&h:' ?zaM-Kۼ_nb4{/Y+m]5KM{-bht̑c+-y+?ϜؿQˈ,΅?د|sٗ5r8|1BLakF;skq 6z3~L_Y_s_|X) mE{Ao>>?c}>o},+O?tnp$C~sV8k3ԯy?+.R_G7?CBOP#W;1qF_߷OL2j,CS6F[8Rl?#D{{],ž³z`geqb?O76Forc1ʰo6{;d2}fpYeJds| 3}]-u*qO./@6nCc,~>fϘ Wm49Bo%4@T ÞVC kQgH^poѴH"Ef:^ey< }Lr3;؜0;.^~8),YIY\J9}Odw+g +GywbHc~ռ$}:޿mI=\J j?|_'¨}gz`Og5c6s[w| >_Bs~kuKzBZemCDksk/~-LH0yl\ GI4o7g=+|Q\?yw/Eٿ<><}O H;qs;?Wg|ĴWAg\8fn0QHэ/bKX01S왿wx.*;S k\zwhCc`mz#G]*|~}u_g}s;^kx2j<^k Řl Hto6 ,DbFM](S]PzhAJ}`~ ?,~,Dr0^>~.1s>Z:1 |~NL}uy'eZy{k=6ovǦЏEˤ?2צx8d[~M0/2[^Tz{溟Mxg!o3hGtL` ž} f+؍(ƍd&W9-chy?uNN;Vn5a;Ӕ1q~|} .ُm1v0 0ܲLy\Yb%SV6~[~oG>-ޭ^=ڂ,6 3}~*2X紀y;k?o3;z}DG z׻>(Dn 4ll`6ΟX٘}{[G*BdvޯUyG͛8lږx {Q|}+c>>g;ѰPMYߗ|_}Ws?バo6[?|?wcy4<}o).@2{'=_$O/K-20mPSo:0z`SOo2F 7hWsu Hc,3Sm9ж\5ytDŽ;m,c=q=WyOx?=Ҥĭ#'ac;ϯ){}xF/ٶ֋fubm?j?чOksf]xm+cװy=ޕVjAu0cL?n 2y:@5@ŖiZ6ȸ#._5+_wՎ1ΔՅyUwq=/NRϘow?{KP߯m?m^}ر }Vg~m[pqZ/'KVpF'xݝ,Q#>4ް7 }Zܮ̐NDwNPHT?Rf|WwDz8?~la {IP~+}_B^?3ZHc,lx}އMIkt:Afy aAѶf8s#cs*#bճHMGLX- 1rq w?zbw1;[zP0m=rnm//ޯ}̿2_N[f;1e?E_bhC<ŹϗE\3?O{&|K>WGu]y+|_=漜~_ix(ZϑuAî)`濇g9F?4o[C~;1/Ӳ%G1M5HL<ֶysulbs^-;Frq48ү})} ,#_xTR^# ԯ1;:`ەehl-qt~609--0m@NW b'`p~_Q 2TCy/}_T}t?&2)M.~?Htq1l DL(g۬l5?bާ˴/na>s p3e)7G]p|\FnG=LXy{!kv5-t4nX^ll{(݀W>'p#>toݸ|E p4@|6/M|d|LW_Q 33 sF?nky?{@Lg/.VٺZkW+k֔U}a}n#80=Cf-ĩ*\d oA `"h+85}]j'e6 {mu<v^ʺ?̗^ :_ Yv]侽M4Le WU8v2m6^};B+۰̦Ѳ(񋶐,4gBUiD._>p>>41? T/4^c뿆{~d݀1 c(}%ytݿ^kf}um;PߊggNTZ>;Z6׌#p~,b˼=邏o~?Z\w= mhg-kƮyuch6~f: ۝|e;8u)x{ @[:!>}|Y\.Xh&;:uߪЗNҞwՃ~Cf<>tmº}x2k~veڵ}eyw"ٟhMlqK_s_^g1LLoq_ώ_=%;9q`?<f؏ sٮiLI**_?N㸙1f \Wu"ۓ ~4E]tk_G{~/S8_9c_W X6Pā MֹxZ{?;k|=Hlu{k?fKxU55h/cy=(Ks1nCĸ?|SLe`{k^:a\?x[fylR6s/"}qcf>gZ| ә;, {quALcX`ۄw}v3cٸ4.@l KxZU>Pg:N|B=֯GǹA3C4aqpLXLߣ>q^7{Վ{vYe\Ʀjo+k3k>_k,ʑ;, >pqwvڤ4{0>ެJ]As}%X|TacMIh%e^,zE=me3=wR]0֟ߵA}$op8^Tݸ!x8<UK<9S9f?mila2 gE>[<ڨe>> CxK;G]0ضu*?w%1MRYܿ[d| ,_j^W{w-7ڨkI"vԿb>ӳ@'6E#ztW85i:̧5; @=ߏ%~?a/-m~1-uK|X]zvq@*_9͉QoO9kX'"j  {O`>~? w;)4)fDr}/,k-zhvƒm0mg:-h+iM&lc-ڌ?GAJ>ʇx/8.5?ykxpp{L_֘e\rO?++u̷?!ۢ|+LxLsJ\ks`TT 0{ "=y6M}\WO|d~G Oىye4,5L+-6VyP_R= u"v hV/r?;Nx~n<|ħ@Kl h^SKc,Ҭ|fK3ޜC6Wy͸-TT+%ܺ_6p0}{Bl+ @THuc:<'m2 {^{_O4~Rm(=LhQ`} 1- Z){=cX{|0y98.olM87& q g7>}G]YcK1 Ҋ߫On@8COK=w#߽6qck\'G,=UC+ZxD.`-`Ltz띭ZDZZO7{|pGt\BYkfxptyM veg2A1/,3_lYuYnA/$nH[_z|̵w0b : s4Uv6눱w`MLH;m`vLO63"S{ zo饮 0㿟YzG p=`=c3MV2g?wo=?:w$/uxdmDo%}cy,sݪ/ð\ݾ':93]1$bd+wut~F[h }dǼq\?vzxx?o,Gf-`eXGЧ׭bMv[`BpC`6z^WG5@ؑ؏̷!u@ hdG_[x/X;@@yt8o'7_oy.-?I `_'kLWӆphp?2{->ҁ5x/#ړ@2x|^g>K9yit뮹t~}rq}o6xe?>7{[p?_zZJ=`yg2tk51}#Tuxog.7^:?[ Tպ\ǕOS{Ixw qOO-I?g|2u{ױC)C|^u!Z?2ʷҚh۬__o^9  +"} y p4}_?JDry|Wx^}-q\Lvr{1q@b`z۬ Z gGM{#9 s `a~؁˴l1@ -'F7zga[д1 ,|] P5o#|>Yl/_eSutOWO4 T%>GS p[<j,m|b~.jN0[x=o*5[\Y<.LVO̟?2Eq$={֏Z=#gy_Gg4𝚿K^H~i_w39)w׭,v̳6}wce藇yp c|;m+C>`kWxh_@%jk_O~s[U;,w5<8)<[ rjΏ:8G13X6úYCq ̎ ?^*J 18 `5?34S-!bd;.?{/V_f~"Dx>@Lhz5zm8F`ux] QΞ V~[Zʙ~X_ѩ دyMw}{~${}_kwc~-@H!obq.^ܟ={)J+ڟiZ@Vā~9#z=1}O5}ƳOܿH3 cHu@efރOtCN;E"EUgfl9;@!|H .Dµ+>'@\k eNh g~Y=DnOU8-wea$(h`}̧GzMY*M}A;9OfX.fM,[iL˧V3c>IkG}py`?(^ݾ}ž˧ kX[1`^0MYtsu?\:`|xfG;{~W;uX_EmY3ˢjJx' n˖܎ Wn${ivMցtVgu:aGuv;r} ϓ×|%Mufc=ˇ|>`_?O_S,o  JXZo}Iˮ_cBCWC>!1lo|U e׎2YÎGF`Q_qqX?#Uog؄؇؝a*`2Fx{x- O:T^6&eN|c}~@J O> k ߞ`yuw  qv8oY;sb?tfwѿ]"G%L">psuc\=ݩ.ƒku>[4{uJWr18Ӵ Fz 4?@?G-P.openimageio-1.7.17~dfsg0.orig/testsuite/texture-blurtube/ref/out.txt0000644000175000017500000002745613151711064024042 0ustar mfvmfvMemory use: 11.8 MB OpenImageIO Texture statistics (0x104270a70, cache = 0x104270c90) Queries/batches : texture : 262144 queries in 262144 batches texture 3d : 0 queries in 0 batches shadow : 0 queries in 0 batches environment : 0 queries in 0 batches Interpolations : closest : 0 bilinear : 2907992 bicubic : 282672 Average anisotropic probes : 6.09 Max anisotropy in the wild : 804 ustrings: requests: 1467, unique 50, 2 KB OpenImageIO ImageCache statistics (0x104270c90) ver 1.1.0 Images : 1 unique ImageInputs : 1 created, 1 current, 1 peak Total size of all images referenced : 5.3 MB Read from disk : 1.5 MB File I/O time : 0.0s File open time only : 0.0s Tiles: 97 created, 97 current, 97 peak total tile requests : 4114466 micro-cache misses : 135274 (3.28777%) main cache misses : 97 (0.00235754%) Peak cache memory : 1.5 MB Image file statistics: opens tiles MB read I/O time res File 1 1 97 1.5 0.0s 1024x1024x4.u8 ../../../../..//oiio-images/grid.tx Tot: 1 97 1.5 0.0s Memory use: 10.4 MB OpenImageIO Texture statistics (0x104270a90, cache = 0x104270cb0) Queries/batches : texture : 262144 queries in 262144 batches texture 3d : 0 queries in 0 batches shadow : 0 queries in 0 batches environment : 0 queries in 0 batches Interpolations : closest : 0 bilinear : 197380 bicubic : 892380 Average anisotropic probes : 2.08 Max anisotropy in the wild : 787 ustrings: requests: 1087, unique 50, 2 KB OpenImageIO ImageCache statistics (0x104270cb0) ver 1.1.0 Images : 1 unique ImageInputs : 1 created, 1 current, 1 peak Total size of all images referenced : 5.3 MB Read from disk : 112 KB File I/O time : 0.0s File open time only : 0.0s Tiles: 7 created, 7 current, 7 peak total tile requests : 3345100 micro-cache misses : 548 (0.0163822%) main cache misses : 7 (0.000209261%) Peak cache memory : 112 KB Image file statistics: opens tiles MB read I/O time res File 1 1 7 0.1 0.0s 1024x1024x4.u8 ../../../../..//oiio-images/grid.tx Tot: 1 7 0.1 0.0s Memory use: 10.4 MB OpenImageIO Texture statistics (0x104270a90, cache = 0x104270cb0) Queries/batches : texture : 262144 queries in 262144 batches texture 3d : 0 queries in 0 batches shadow : 0 queries in 0 batches environment : 0 queries in 0 batches Interpolations : closest : 0 bilinear : 52524 bicubic : 753908 Average anisotropic probes : 1.54 Max anisotropy in the wild : 762 ustrings: requests: 1077, unique 50, 2 KB OpenImageIO ImageCache statistics (0x104270cb0) ver 1.1.0 Images : 1 unique ImageInputs : 1 created, 1 current, 1 peak Total size of all images referenced : 5.3 MB Read from disk : 96 KB File I/O time : 0.0s File open time only : 0.0s Tiles: 6 created, 6 current, 6 peak total tile requests : 3996800 micro-cache misses : 422 (0.0105584%) main cache misses : 6 (0.00015012%) Peak cache memory : 96 KB Image file statistics: opens tiles MB read I/O time res File 1 1 6 0.1 0.0s 1024x1024x4.u8 ../../../../..//oiio-images/grid.tx Tot: 1 6 0.1 0.0s Memory use: 10.4 MB OpenImageIO Texture statistics (0x104270a90, cache = 0x104270cb0) Queries/batches : texture : 262144 queries in 262144 batches texture 3d : 0 queries in 0 batches shadow : 0 queries in 0 batches environment : 0 queries in 0 batches Interpolations : closest : 0 bilinear : 16220 bicubic : 666100 Average anisotropic probes : 1.3 Max anisotropy in the wild : 724 ustrings: requests: 1067, unique 50, 2 KB OpenImageIO ImageCache statistics (0x104270cb0) ver 1.1.0 Images : 1 unique ImageInputs : 1 created, 1 current, 1 peak Total size of all images referenced : 5.3 MB Read from disk : 80 KB File I/O time : 0.0s File open time only : 0.0s Tiles: 5 created, 5 current, 5 peak total tile requests : 5148204 micro-cache misses : 212 (0.00411794%) main cache misses : 5 (9.71212e-05%) Peak cache memory : 80 KB Image file statistics: opens tiles MB read I/O time res File 1 1 5 0.1 0.0s 1024x1024x4.u8 ../../../../..//oiio-images/grid.tx Tot: 1 5 0.1 0.0s Memory use: 10.4 MB OpenImageIO Texture statistics (0x104270a90, cache = 0x104270cb0) Queries/batches : texture : 262144 queries in 262144 batches texture 3d : 0 queries in 0 batches shadow : 0 queries in 0 batches environment : 0 queries in 0 batches Interpolations : closest : 0 bilinear : 4788 bicubic : 604708 Average anisotropic probes : 1.16 Max anisotropy in the wild : 658 ustrings: requests: 1057, unique 50, 2 KB OpenImageIO ImageCache statistics (0x104270cb0) ver 1.1.0 Images : 1 unique ImageInputs : 1 created, 1 current, 1 peak Total size of all images referenced : 5.3 MB Read from disk : 64 KB File I/O time : 0.0s File open time only : 0.0s Tiles: 4 created, 4 current, 4 peak total tile requests : 7250380 micro-cache misses : 104 (0.00143441%) main cache misses : 4 (5.51695e-05%) Peak cache memory : 64 KB Image file statistics: opens tiles MB read I/O time res File 1 1 4 0.1 0.0s 1024x1024x4.u8 ../../../../..//oiio-images/grid.tx Tot: 1 4 0.1 0.0s Memory use: 11.3 MB OpenImageIO Texture statistics (0x104270a80, cache = 0x104270ca0) Queries/batches : texture : 262144 queries in 262144 batches texture 3d : 0 queries in 0 batches shadow : 0 queries in 0 batches environment : 0 queries in 0 batches Interpolations : closest : 0 bilinear : 2290308 bicubic : 889476 Average anisotropic probes : 6.13 Max anisotropy in the wild : 804 ustrings: requests: 1495, unique 48, 2 KB OpenImageIO ImageCache statistics (0x104270ca0) ver 1.1.0 Images : 1 unique ImageInputs : 1 created, 1 current, 1 peak Total size of all images referenced : 1023 KB Read from disk : 1.0 MB File I/O time : 0.0s File open time only : 0.0s Tiles: 89 created, 89 current, 89 peak total tile requests : 4879704 micro-cache misses : 160932 (3.29799%) main cache misses : 89 (0.00182388%) Peak cache memory : 1.0 MB Image file statistics: opens tiles MB read I/O time res File 1 1 89 1.0 0.0s 512x 512x3.u8 ../../../../..//oiio-images/checker.tx Tot: 1 89 1.0 0.0s Memory use: 10.4 MB OpenImageIO Texture statistics (0x104270aa0, cache = 0x104270cc0) Queries/batches : texture : 262144 queries in 262144 batches texture 3d : 0 queries in 0 batches shadow : 0 queries in 0 batches environment : 0 queries in 0 batches Interpolations : closest : 0 bilinear : 197380 bicubic : 892380 Average anisotropic probes : 2.08 Max anisotropy in the wild : 787 ustrings: requests: 1121, unique 48, 2 KB OpenImageIO ImageCache statistics (0x104270cc0) ver 1.1.0 Images : 1 unique ImageInputs : 1 created, 1 current, 1 peak Total size of all images referenced : 1023 KB Read from disk : 84 KB File I/O time : 0.0s File open time only : 0.0s Tiles: 7 created, 7 current, 7 peak total tile requests : 3345100 micro-cache misses : 548 (0.0163822%) main cache misses : 7 (0.000209261%) Peak cache memory : 84 KB Image file statistics: opens tiles MB read I/O time res File 1 1 7 0.1 0.0s 512x 512x3.u8 ../../../../..//oiio-images/checker.tx Tot: 1 7 0.1 0.0s Memory use: 10.3 MB OpenImageIO Texture statistics (0x104270aa0, cache = 0x104270cc0) Queries/batches : texture : 262144 queries in 262144 batches texture 3d : 0 queries in 0 batches shadow : 0 queries in 0 batches environment : 0 queries in 0 batches Interpolations : closest : 0 bilinear : 52524 bicubic : 753908 Average anisotropic probes : 1.54 Max anisotropy in the wild : 762 ustrings: requests: 1110, unique 48, 2 KB OpenImageIO ImageCache statistics (0x104270cc0) ver 1.1.0 Images : 1 unique ImageInputs : 1 created, 1 current, 1 peak Total size of all images referenced : 1023 KB Read from disk : 72 KB File I/O time : 0.0s File open time only : 0.0s Tiles: 6 created, 6 current, 6 peak total tile requests : 3996800 micro-cache misses : 422 (0.0105584%) main cache misses : 6 (0.00015012%) Peak cache memory : 72 KB Image file statistics: opens tiles MB read I/O time res File 1 1 6 0.1 0.0s 512x 512x3.u8 ../../../../..//oiio-images/checker.tx Tot: 1 6 0.1 0.0s Memory use: 10.3 MB OpenImageIO Texture statistics (0x104270aa0, cache = 0x104270cc0) Queries/batches : texture : 262144 queries in 262144 batches texture 3d : 0 queries in 0 batches shadow : 0 queries in 0 batches environment : 0 queries in 0 batches Interpolations : closest : 0 bilinear : 16220 bicubic : 666100 Average anisotropic probes : 1.3 Max anisotropy in the wild : 724 ustrings: requests: 1099, unique 48, 2 KB OpenImageIO ImageCache statistics (0x104270cc0) ver 1.1.0 Images : 1 unique ImageInputs : 1 created, 1 current, 1 peak Total size of all images referenced : 1023 KB Read from disk : 60 KB File I/O time : 0.0s File open time only : 0.0s Tiles: 5 created, 5 current, 5 peak total tile requests : 5148204 micro-cache misses : 212 (0.00411794%) main cache misses : 5 (9.71212e-05%) Peak cache memory : 60 KB Image file statistics: opens tiles MB read I/O time res File 1 1 5 0.1 0.0s 512x 512x3.u8 ../../../../..//oiio-images/checker.tx Tot: 1 5 0.1 0.0s Memory use: 10.3 MB OpenImageIO Texture statistics (0x104270aa0, cache = 0x104270cc0) Queries/batches : texture : 262144 queries in 262144 batches texture 3d : 0 queries in 0 batches shadow : 0 queries in 0 batches environment : 0 queries in 0 batches Interpolations : closest : 0 bilinear : 4788 bicubic : 604708 Average anisotropic probes : 1.16 Max anisotropy in the wild : 658 ustrings: requests: 1088, unique 48, 2 KB OpenImageIO ImageCache statistics (0x104270cc0) ver 1.1.0 Images : 1 unique ImageInputs : 1 created, 1 current, 1 peak Total size of all images referenced : 1023 KB Read from disk : 48 KB File I/O time : 0.0s File open time only : 0.0s Tiles: 4 created, 4 current, 4 peak total tile requests : 7250380 micro-cache misses : 104 (0.00143441%) main cache misses : 4 (5.51695e-05%) Peak cache memory : 48 KB Image file statistics: opens tiles MB read I/O time res File 1 1 4 0.0 0.0s 512x 512x3.u8 ../../../../..//oiio-images/checker.tx Tot: 1 4 0.0 0.0s Comparing "grid-0.00.tif" and "ref/grid-0.00.tif" Comparing "grid-0.02.tif" and "ref/grid-0.02.tif" Comparing "grid-0.05.tif" and "ref/grid-0.05.tif" Comparing "grid-0.10.tif" and "ref/grid-0.10.tif" Comparing "grid-0.20.tif" and "ref/grid-0.20.tif" Comparing "checker-0.00.tif" and "ref/checker-0.00.tif" Comparing "checker-0.02.tif" and "ref/checker-0.02.tif" Comparing "checker-0.05.tif" and "ref/checker-0.05.tif" Comparing "checker-0.10.tif" and "ref/checker-0.10.tif" Comparing "checker-0.20.tif" and "ref/checker-0.20.tif" openimageio-1.7.17~dfsg0.orig/testsuite/texture-blurtube/ref/checker-0.02.tif0000644000175000017500000074531713151711064025162 0ustar mfvmfvII*> 2=RS~"'5/9BrMRBMfMRMB9:/("%M|>F>3QP2016:02:25 9:51:39 x˒,Gф!p{ZdYdJf!J#+a93=oݟO?ívg~w\^} _ë/~ /p/}_Wz/}߸}+ܽkw|;wַ}k_>yuKVɯ[?O>?~wֿhusktK_zU/Y%zWeԫnLWyu=)O[c <e裏~_˭k4zƎzHigUcq-%K/t/tB~}z>sY5wGb~:\lu65yz7y}{{\8[okKc8k5uM?|i=z .YoXI6oɫkiٞx=QߚY=Z^#!!#!I=8yl6E)O|km/ KؽF5q]:1'w̏~^^^u[ nKO]ڵ#&Iws5&^ZKU7y,' <~dM=^M9۲fR'f7JN?_lAc@(e'>7u=w>-clկ|]ߦv׮imоj7'ޥ\k&ysv0د>zVuzA= RF;גOϮB_Zߴ64ZҦduymmm=(}ĉ,+q"c^6v?'clc:q]?sbz4;2ـr<9޾mm]giuw3mW?'DZ崏#9--x?V_'>g ^ɶ2ٝe}+ǣGǁ6]c[C^suX[ѷ1Ov.ny5O_1?v_bƟ1'C;xέ 45e_iqi{^:r>:糴ua9+v e~ |7>F.NEҎ=O~8&})mxcr:m6)HYNmoY%K>so9f rSb #٧|n ک\&͑@|sf<ָ?{~}v85b8ѹ%cj1:h\3h\?GqĥfϷNw?Jbso}`-@='L`U"&jNO䯦Nǚuy++F9wm 3T(q?9ܕZ~?Mf2q2} cXfUpfSnx,š ovا)sC?%;_qEMg{ǴRkG3ĉSkcۏ\Sʽk_;3W%XȺ%㿯V9_L̷oVx:ڦM24o\XV߄Yo?1plZD3[6Ս"S.%s_\;|ӑZbWZ#OuĠğ6{0lxB~TglcM؟9}OK`}pas㓶yZߺ_jnʸ#+ߴ>,89eߝZ1vrԟ/uȺivOn?~3&yKuZLuKp>&1vk'l8cҺJ_ 9Gcd˚Ɖgq+3ovv׺ښ03VY&y4h|e< \֕ G5ܷߘB;_gƹ~랱y)Tϼ¾ +ЏxYS>jϸU]j+O/q7swyۦ!wf5Y[}"of aΙ91hsA_ZX6NGzxg rg\7iLuS{Fך.e>n/7/ie|L0cCڵwZʸ1`?5g1 ohY6a?~ V/;Z.yiQ_gX);1q^GoNKrۿbovz~IccGϜgXW=Xiwrp-$7̘9gtG{8={ ?e&ȝ7'4Óx$tvIaӺiuGC3}ɜ4GwWuGcpuj֕e>L]`Z9'񻱝/~Kl~}ŬxI?цy&B77u/9<ʧ^8}VtƼSW= vj0B2clg A[\j۟;Tsfw`l\s:fd.vty++cZ;;@`@Q.Vnc\촗>I{R Wϊgޑ=V57< Z_+g͚7oeN׾{j)4'g.ٸ#h2@C&gBs%~ks;>'~@ܓMg1GgVdޕsƚ3aTۓ]el?l3;2{Mx~w[+'n5Y1k̭e6v\TcO眳.p𳭰eϾ~M앐o}'>a+V5G7ut3qM=}ͅ-z q3Pig]6`,yeb?kebl@Ɲ- ',d[aQf#?;2P>iuڿ[a|!/).'>=o&$kbe(wHڋƘVw%WPC?0}U\y֯a1|ڷ{]ejWyPA~_[rkNoZR+}VJV^>cg:s+z'| yWƪyg֝K^ڹpy5rNչ=-3NyXXsa SLq}Gu O)#9cŞ$7woM j~~ 쑘>Gޓqy ?9gq֖3쵐螰)홯{ar1裏.wro:o~~S̵hZ ƈ퀹p濯k[-w?z/߱eS8~eY8ڳ{}gui1m&c.#Iw;8ݞ7sb{Z kgjg2_ָٙ bp rXyյ^\h k'>1J6o{9ETWu#O,z6ޗwaw7pzw;ǒTTH\dhܖJcK12k5{ߒy^Z?b>sS:+@='~^EۑYƘ7dZW\iߛ<)<~vs3#`N'a2:N\($<xV\ys.=;OsD#?y&_ӭXs'g.~۹\i6>sZCjG>@m Yg_\>{zOX?%ď8p?ku+lտ)}rO5A{f_# ynmo;sɡόo1wYGX>`؃q{^5rg0c5a~<{p@?Tr:\7cw}lK{jkCVb'Q'; 0gҥ1m؟\ O5O+8q #'^37ƾ {]۠%c8Y3{@=7Fv3,7^}|z qQ;7/ `l &΃Oc=/B3'U\mWkB܎)TZ?Gdd8}z=>G;o5+yh>g ^~bny}eexLHGn7SNf?|x~Lة;P{kw~Fz;Z;i~*n({9XkFܷ/>4b=:ʻ'gr^Kc{?T;.P>`=m&Ͼګ[3>L.#P/7?11s3o[=RvcVפה7ӥ`ζ_⭉s_awr&om#ls80X<#}bԺtlKrsb&;s;)Oz<3Xo8`h}x>-9N5iyn'4z4&淜}6;yS_/X7U7'9hO5;mm'T bsqo`S,|@Ïkq|#3勽td/Ķ>GN>_W{Mk3q8ΰ m}W{;Eӿc/g_sY2+jZJwiXc&@~5+UAw:k뚿ϕ|ga3ɚGSKm5k.XA6˴;ۄ)qJG5Sao\rs0$}NqL>.v;֣Zgrd'6~5 me;' ]#u-.w` }6^x:vNsrsytFu^ե/Y/ǿw9+!}rIzn3.9۳4uH0sgz#h9n>\r36yG8y <}  L~}<n~}Uk;Zs]sùX 'fzsJ:i~dYȼG{w4,ds]{㹽yϣ3[Ϭ엑6WLHL;{ۗɾOΊ6VkkSSəNvgw4?h.5ϣrΜ;mc01Yrp[u^Cq焟 [͟saosb=oSrY[kv1k< w+NK{S8vޟq?Őo}HK~0C^yͶFl?7gMd<5A Y;Mx1~ً3Vt97sst&?VzC6է_Oms oЕ\7=獳\z<,sGYF&~չٯ{4~qPNkO؟ZF9c=kVfeS'y|2O-_׳]Pérޟ=M|?H}`='8q\'Q-yLs#{<'89&7qz<uw_sMrl?)9r?_։$096P{kkȩ-1,78v{fs38לbc;^"9m]۫Jm9Z=;(|*ok|=S:O7ϯ`/Di}RgZ s9ff>OgM4jcsŽ]ُ9{<>61̭E[}{߽NyC>@)f&Sɏxp~8 ѲFxi(_Z?6zR>Oc|kEko{coqcc*W r73nVۺE^/*}s=aیWrS*~auv,-Ǘaﵺe2A)r>`2 uK {:ljyC;>gpvܵ֓oI)oI)kQ7m낹9׍CӺX'12w񛏸'l⿾ϵ1elcʵj\IKz?:3J%}K|I֎SJ}/$zgfL<& f՛ov1~ڰve2eù,^MyNMF@OgO]G>srh7@2yvh؟{k?Lyxěelɛ ??~'&_vn{o\ñ=Oؿ~:q،'fPÑ⸳d7i_-xԴԗk8Y3wmҾI;'VO+jgKz}yt̽O;Ȝ탟!9Vl3MyL˒>gAg]Ytgj,qy>Ɏ_yh\AwAΞip#nqKw;-+Cm,P>9w7ؾ},&sb땝=Ok}N^e-,$>3Ay @o{V{5Ygf^X}kuW>s`l4=S1kgYCl[cgu̳L:|pLLg>牱_~-&~lhd\k{0x]صvv Ov6}"O㸇i5vi !xݝlKE[p#0'00 C֍&鞞E|'Dϴ̪JSC]~*׿u?/y᷿/_7xw=y>ᄐ|;gr-oo~_ozӛc1??oƅ?/O~򓇟gqw3cg3~9@[zx3>~/G>}Cz%vw ?ïkӟ.4g[<~chκf3>ƕ1el<ܙwƐux3XG.:e Yя~ᓟç>Oӗ5wͯ{lw3sg2yZNS+s<g=.Yˇ ^櫖܆_÷p9|:;|>~u왖sսbY:Chا|^gÊ/n3=6dd3lG~cuO|/>_vZƟs^>q2'm1Gm2G]ӻf^:x%yEoƬzqd&y>e XG?k{kO3ӌ \:k&=\u}w6t0 앎WaNF/͙_{ܸ9sX+{B6@0@sc\S>.cZG~OnѸV.{ "b[0]_G-q3'm>b߾]nbeblas'G 5_[oB/>cEyg0ye?6lq ^qp[pg|\ wMcLcZ8yX8*v[߷O?ً;9 O+>?MXzg^ˈ։>|=|戸U3~qHq]V=F? gl|lH)}{v8cFƛ+kxo}>bc@;Gy˖ɖN[|l#p@%mzGy5?iۄ;6cA6Fyә|?5OYa'l૧ϓYȼEƛ G>>ߪ/Ј< ^ˍɘ]4e|Ѷ^pkƳZ&s>49cn Mg=vj&16Yb,6y_IL4Lt]0Ͷ;df2zf'\;WHCݘ(:&{ٶٽ쀕 ;-w>y_:4:}[~^ݒh_/7ZB_;oۮث9`ұooB ܟyd[q)Vb,sc&ZO3;2ΓcGƄVw|Xvs7>%wYdBχXkcwv<9g=7=KǴdF|~nz#+ `GD?O8Q/-L\c'Xq'#YD_;f3 72Ow5Zmȗ c-Xk;3gDzd б^|m[j?Gf 'HֳGygd;º9h2'OE>%z%$>e0L$<c:sG꼆Ǭg0ƤI0|.+'q,]ӹՑGV;<ɣk5뛕:9wf1Dˌᤋv?Ŕ ݸm9>Į!vƞG^wmm2l\첼?d 0_5o=wչޑv}3~:?D"񏠛%L];|xq8&7r^%\vZ&ܫ@| qu8OKߙk"<?{>{?k"Crzl<}b26)d'fF׽L<|˵tΥsȏ#~}:̮_[kv 9^"ѹhIb+b, ~0[x#my Ilg0.3?pnyEFTk4gYpFl^ӮrV~~s5ptһ18Oz c*eȗ&4 -Vx562O?tS bOm?W^Чϳwa l_uLtkC&VZ&>1۸Sku+ȱ-a,؛m[-O\Gl*v/.)ɸ\roӶ뺶hSot5.惶mS}pkzm8~?cr>/p{f;}a5>]k³FWGJ?NA1gZUEɖ:k8\7[^c>_\~g?{3_pQj8s&|3z:QzmډZd.pUrWBvOCeTkܽ^ Iw9zS}~F!W͜O'nU?.yQv~$sC7ɱl;`Nv.aoܟ{2wȴȱ33*l[ >cygtǭWu6kUIޏ㜛cLx(ث;?O wm+ _sex8W#8="8x$sB4n]ݘv }'_7V p{qZIwNk+9#k{ǔ{-;^+߶㴮]ݕZ:߈8-7>ٖYjԛFɝ-n g;osPk5 5/ ̢c>ą5ɜ _ܣmcct76FMc7Sz&,O~pKR+= NxbG7ÑI썌<՞t\Ժ579Nu='ug'j{Vɣu>k;lqrSνuog{s@+yӸ[^hL ~e>8 X1Ov8~eL^tMkt!y GbN/ l~m웶3/ܻfLg 4ՃYk=o48mm-k^xuS> _a+gd=13ON{=A,5|i_:]ٵgd5H->gcރ+cq&~9>qϹdO#>;:ts?٫?],!ٞ7>.xGW286՞ax\x]_%\{g^ߏqFW꙰%+=m3SҪVmO=[<'Ⱥfjxq/f=j\Lއ l\.jGbW2}nro01Ftv+Xmsb2XwO03q;n￉;/gh^OKoX|7 DZū'};_Z髦i&ݤL]Y5M0빜)g))F8{o _Jsԯa<Ύe|q|;هsM3jۍ{rq.dwWrȴOrV5/{\3 Gíp>*ǒ5&tH4Ϝzv/| e[359}Vhq1l%{oޚ6䞌_cNF_F+\vUlnIgΐZ{7]OyUߟm@dKM[)Vd踧ocwa_{;l|,x)q'u6a6 Ev<`[f65u҄XD䵼x6mvq)/=^|c~]g0}?3wʕ :B{w{Wc8'ۖ]k7/Lrҹ S. ާ`#6y#1~׿^aKwY0}*rv1k착zOz3>cѻ<ϝc0,S4b۞ίXgYLg3` Bk%_e {9t>#e׽r:q79W\$#~{_3S} "3~2ՠɺvsaߏc_a4#sPfۏ{w.=^Υǹ\ӵ]^ ҟ3h=AJn]J+7?Ρ<5?k$/EP X5}\H$wt_s&ﱬSM@{q#<5STr&pMuo<χ&G ΜMrcen|g|;ثm3n˄#)zYe?P>[t?:?~Bmx*e_qn4z/>5nq1h|ܿ͵<ז?8+M9YزS$N#i=D <蒌A}zJOu]>?vc]OÜrw66<?}*צڛ?iyt^{X13u:\~kq~ʵϧ|VbYGt?~?3q{#qWgy`Y #xxL [pwSA۫n&OYZrAo;x@ m٣]o>iK[^p#-kk36i7i^&-*߮{t&v ߑY Ycdx)B~?vwclt\m{My{l.vp+Y>}r~{oߍ47)Կ`kv;5=nڵ:܀|][bWgl'BxX7`V-7jxxعt]{G^5y=srlծqyoq?!Q;S}3{Dqz+еgѝ9b'.?1wtf_=s{KɧkT%Y_A/uw㺀7ЋZ0[vFE>}OK9;Er৸OGVɧB~}Ŝ~VOɧy?^K1N1Eb9ENp_6iNDS:MSo;>㐵_<$X_sɈA}|<:lg?o`/E5.;G1DqC}c}JLmr uΟ㬓>)}rvz\nˍo28S7LguN~؍>y:5ߕ'\{~SSsg?קxz79əw5^u+l|ӹ$}'͓b<;8{;k퀞|OcaHUb?tΥZN~v~zlѼZ7gL~s>Wt>]vZޗvo|ZCBK/~yixG9f-wƘBȊ3ȵ}{}CDrjfSk1U0tNu{LSScWg|uVg ;YW'^'w>5骎6p 0hb]d-O=:Wꨑ lGhxy~douu77V1 t~>dק3RV-}՞~>>K_ʞ_]G3蕟τO"5s>|-<~r-?rg3޼:_=WySY~%MؕS5l=e~YϷ XކOL/{r)߿򽡏3 [=gmd}ϭZUVu^g.hR"k޽sԭk>FgͲڬe:Ggc?@,|i~d,믿ҏ![vMwNߖ}y`\O+Ch5zy*mTxYa~M^t~aouj fw$xqEgJx>,<Ou?|ʙ"G"O$7*rOy` V5+?՘ Oǀj~N կ~{Y]X{W[?:W8ʶO`R=Q䥆]_El5^uݹs}FXTG7,u揘2d[1/r|2y\ r9K왬g{Lzl_;-Ĝݷe爖qi{}:Y(}yg# ~=wWS1 o{5M{~1UmhZa\ -3B.gk{!GS@28ˮJ7wƗ}B=yQ_-O|+kq|?yt NbΫ8td@ٶlh#>=o%N5̸Oݧ,sj[ 8a̱O`- Gy߻.b.Z1G>xkr&OJۮ>KqWz?}?zt^BSnT㵢og'ɛg7sI.NݸoмNMϲ7b::r6u`[sM9?||5r%G\/rxc#aa[E#+~pT#_< a @ƌ)dڗGWc;gmyV6dù[Oԣ{qS||;/u֐|av%hflr+-+,-.6gUȀ -GUƷ\+; Aul;7]px &6 ~_җL-vj`uV^Vy|p{?5(9έ:#2Vx^O<òk傎 0I~|rkߧ~]y_=s&tH^0,>"ul֭>%?r~cb]8Vv]U'2}MglP7 <*&z<]{-05Oel}Cwu}@u8?>?gx D7tD9 Okkc ^Su I FWGyWݻh:p\/R$|xm4ە[{yeՓ2P A)( {WKknr0L&ͮ}ޝ|oOO_O?N׿}iO{Ox|~%/yUz-oyvMo:59e/;/<=9xv?o;/9~Y~핛>q<-L7777 /~7gm0K\k`}|ws7y}xկ~_>`Wz>| Ow /7%Ѝ8~\+^ fnhO~6M7]kcam8k_ڍ^7n?7|/kf_gnuI&-4u`;9>~4]&|yL>\Ϻ|qMk6]o^)'#0COI_]6-Ώ–rM7C?y hfe@?Bд*?KB^)ͦK5傼3ryzXp Js3τ?אH9 cBˀ,#}MIO>-8aI~W_y{cyo+|)4ɞuJ`wxc_^m<&Swau)^5?8}[x >~iwޗR.%rsS?i3}Ý啿9g'>Jnt}i@w3]p[eO GOQ,0Wy>>r"rM8ԩy\B>0L~mI iz<_CΧWO?ecK>Y곕h zSaw0ݺޕɷ{Mb%Ik(l`'z*X`B|{ﱖ|'>6m},|'xy퇲[_*O~_hEח"~TזJk3*/L6`875>yNɇuV.^Oع!RCg[g)>i|0n>~i|x^L3Br46ز4y/Dy~@?_q꓉m5Hڲ^&)nPd)QI;q4K`"?xAڶ_/:>65rx?765mE׭Oeҍ7d,~HzM?q3֦WO;O=`D/ 1I`T4>1Ŭ&__*u>rX@ρ6Fw =_G50QktkkC1 |ؐ˼C2G>CݎO$+>+d挭SưvYr{2uFW0?t=<E+txoU~wc Nԏ.s~5&yB~!ǔ_3ܣܯ\K}b <0wh/Qw<>mmOֵqWڡ޳+)k$6u)G.26#6|=jA񳼬?G֐e 0k2ᝯi~^旎tZ 婾?0&x'h[ײmչQlsi/p i+ߘu7Ci\91ɇEM%x\7L9h_Y3-Gb}Zƚ0 Ck_ `OWV~>LK{UoYֱ @~!Ǭ &rh~O˧/Lm/RYc$f8ܤ]LxuͣEk+6Oۣ%2ig]vrc~vm9&}vOX ]@ c'1c*YC| ^Rg$N)o{/X{햽K,m˺/kM;ϔI_֦AƶpsiSY5MS^rc쭽Cl;3'ͮ2۵Pzm𮽽T7M1ou|pI=JPOӚ{^yUOu466?{M37t z䏬#Go`'^_]S (9D싘{51_D}?ĝ{hS7{&zv>uoso3&{N;V1;vTouS=ҤWnh_5m8q~S(n᤬8uh $Ig)ǔ֙cW^.!˾䫕/܀=mypnwy?n;{&E!wY8~i:7F(dPWþX&_$=9'8x8YZ^NZuӚ;Y]w(6q:}k}L&&AAKrC{جC^ +'lumЀ>Pk9':aD:k&uԷ5&ߧ}[`Sul{/?Ϛ|Gj}8 Y1C ZŶqćS[cMRVIa:־4UW{f,]νκf9ck~ktk5ϊQa\*y^x.3^b֝Xu6rs([r^Õ򤧤+̤ 739}2 b֡IBw3)Bvv|uS|swws9aeΪe%V)洺&;͋1b13c̅_՗X4OuwqM`L{8{nšU=%W¨my&3I_Y?xb2C pn^}|Z.>Ԛ#9#>)+)YpS}1Yߜ32fl Ys3SWh@[жzr m0xwe,\պ?cʙwy9Wg3m]=<Y!yNwu׳,07߽<>vp<+wM>*5n1D.aSOx+x_XkÓ05\ O[/3&z]]hLza O旸UfL?vfL4-{Sqa)v֋b4Wvg1tkv96؆yI__\@'fXy~\A![w]lA3޺ac?>?3ɳ3=ihUewq{l)@E sdcJ]:K{P•9 #{3=s6s'ri+'ɳg?k:7;(/Ti>xy6y}:A[ļ׮︿<1g 䂼@nt]5΍g̺'{^S̿e?[C|} ynk\lO|)ٺt~%9;X_ero縔2ǎYМkzbl\@ p[u?ϖ2nZ#'j7ש}bYgPog+4||`? Cxzkj2ީ woBv=S_<^OӚ|l)183joIζWѯ_+1e*<1:3޾7߱93&0N`L-HFI{r62$X \Қ%y5?__0YG Y3}:3f?[s~.뻧8@+r=7sFCoR1sqaswʹqs^a 7ps~Khk{~:#KNOZKtYX"gC/bAw|Vywc2ho%M}k%=HYoe/{?_rꟺ__|>kkkT{1ە]6PG֞Л5lY d.|Qee3, f֏@ O+_k(MGta̝p|9ͧS 0^ˬ8圇uɼ7Ї>i# ݳu~)awVG1{ٝYN?'>7?)WUϿ;CmM~~x6sYg!uuG :!&0eL$ylLk|{7io:&.2olz5f:-k}=:><3Edo_sx'F{*Wkj̯PK\g%}_jw9Kڳ2gUC Od-%5)ߚ6a'i͸ʿN}YFҔ~kֆ=D=`mﯴlΗOGf/qccjyT0Ait 7js =ZY7d*lHuΛ^S \D'ītLm\0b{V+m'ޚf8gO|>csfzlyG>&.e|9=\d$ }h} =IZ2]시-LJ/צ.;WL@mŪkү$l(KoV?Þ^&;H\Y|ɸOyq{\Ad69;ܣ>[Mp猃_O}S]yNJQcS_8-]6q9[ l|׶S*ۏ|# hy< sywS^"oϺY+Y79{k܍wbyYKȳm;ml/] d֔ B+ s r-+g&3=ئޫO]rOGf99[߸8` kTWg7S^_7Zɴ٦J~+ݙ^hd.cziϽ߳z*WuҰX |ʴű<,{=S\u5m<zog~Lg&uX XAk&y8T}3Gx|u1öw&iyw^.$Y"OƵ' t>Ż5oo u_#pH{B.%LVm?ל}C=Uvۑ_6 OYųČI?<A!Ve em9)?LJ{gZ}:'~CZJ4?O7t]@ʖ3xKWde ^O0,bWI:|7&Vgg~YWfoY7>RH J)/}i7kVޛj |8kޔ'?}WuCS.-Љ#?o{Qst9{^kEX_#er5X59'Ko:H8Y5)9צ C=5-it\c-y\1lmty @.AGȩXя2PX)EڙLFx%orr=-r.ـϠyy򓟼ݳҎO@:U/YϹSMtړzs6ƩɳY\>?l/~?O:&#{~\hdq 4yy>S[ ϯ =w%[O/|F>`3Fj}uSc=gԿȦ/~񋛟b^ntگI# `㻬5?_K>8QO<iĚ淩gsOx2a @qyG?ګүc]Oسh{})/>CpN >+)Ni_t;* iQ:rFq'yecS%-=?B!OW0v>5Fy#s<=ݟ6Ey^!zžc*S-9|N4"_=#^淌?O ܹֆZ/k*7̎S~Zk6=SZ1򼯩6WJՔѼ\HgYcq:j-y>箮iϳ6Y>y/ږS:w^ќTofyq)pSn>y =]󗶷o em5,׬c=w%i8o4}5Fqjg2f&tF1rka^T؏H{v)IM̩vu= o??O?{6*w[/&6Y}v\ ga{;'= 2Wg[ݘgX͂ػ:.i#|s~>#<^K9JS6ᆗvdO66|NγzbDS>Ț̫ `Ҭgdԧ}ڢI}L>wkadܳhm@Ɨ7Lo߇!B愀kٖ݇}'_y>v5-hwOvU3g'}Xu?79fGɯ\gr_s2MU<~;Jڶ䲐=7=?c#^_Ĝu<3xEZӛsɾL{W_lTk=WPW|fc͙Nvs3|'(,T8$gȯA\ZyC!yv3^U <ƘNډ50O5CyC҇y/4zm-k"{o{ܬW^s޸=ynn}\sW__ |ƉGYcӞ\Ck[ 緞;}}m)k{UOz}ѧ>s;}<;6,I]ȞRݟ'g?<=HJK-9Rk_\=t&XuI/ny}`:{hNq_Suwo)K31=w1{)Sg? ZqN9ɜ|o?mδ\0>Kk?y60WGo({bcu,.ė֌L6&x˳+;jo^3>Yg9/v;gsf3rxqN~n]c8Ezc/uKڸl*.e_9ӽ5t% 91E6`>R;kf1ל2&}aYsfx}ٯ|y5'ًpۆҫxzylQ$ͭewG[;e{Xo>Gbre'5C36}.3OYʽйSkWSyΝOs9s~eܷʚ8(\78!1e `4S݌5c8oxz?]xtAfKK?s\{_<LYis&<g>qçT滵rt |Ͼ*eZS99F`מyo> =Z״,ˮ%0 1 mHyk򝲤@[|^,ez]X?x9gq?4:Ǚiq n3X1> !"sC97GgIތdK}ʾ,d i _Mao64ڴ5֥w,(>K{U2n[dܝ>9J7Ѓ#S_XL@>#:wm2iƘi3%Otq,+}7rx!_/}JV)g͠5վUΘ^Wճ<93gpI_:KxmYF p-$!t>A!ݥp)ܥ(.wsyg';#{5t[w}ſ?_O_Nw}鮻:=?<8<==<=AZn~p|>^馛nvX^˼/ӷ7_g̕9~Ϲsd|#OOySNysNO|3OO|O}C5]~̇3?n'1ǜ\~Ox ' n،o|/~t/pclf /̓? g _3&HGtaB7Ϙ;9-'svI)caN d/#0fre{俲6u~M& |>592w>KuN*e\s`\T~ӟtYwʱsxFG,|~[o==YZxM~O/9Y|3G/x+C`?q9u&?;8} _8}+_YcJ_[>F~m=O]h O Cž?:--|^.7c{ 'Y2á5|OŸM.\v:`/ƄWnV; zy+hWu!>w=FߺnC=Q5弔ɾ3;s`A׸sI૸ |ww-[F=zb |L  7V$^&$[Yٟ25ϒ3.ß^/4?~3Dy0ά_9jxA6o ~Lx+pdiӥ2=/ʻ}{ U}(d4^-R`=N,ǘ/pj?!f |\'L?&_}yvK1VMml7HxClxB|ޓ邹kaLzCtwB_~45@#txQo?Bu\k{iÿKXW&'%-` s`ةm_}A^нwF=)6=7Fn&?sQ;#3w}mީ=uk0>4 |$|Ӗ-5e?1=ER` ΀?xzҊ:IH;/Pbyc ׏G_a=拰ņj\',+C5޿ #̔'%nwdN\<[b够)S/kУυNQ JGY\?g<1F&O5yyCxGr.]|Q$1/|^u΂sMyT0we>B6/N0sH>ѼcAx^|hG&44%-ېW0f^|.l̗ܾ-X&3&6 ̯O5`}Unb-9m)A+9`9S o@X'Mov ຖ09u;|/<c{93y% =Ϥԝ՟Ype % 3;2v,mZyqke?𔇱RVAJ[?q.<Ϟw9=S>8sM: \@^CIW~t 皯$E23@̅5_z5#t}z _zz5!lࢯdΔ:@M/}1#[-0a鿜b.'wkno\_Lvg = ?4`LƭX$<"օ >?gmE<_5|9˭oN9$] Ӯxabk>Y(ek65~epV8ko5/T?|v7g?OxsbЏ=>sCkhA%τNWX萩Kg~_gAn%/3ZCu;|+sm`b|sjZnO#2N%+'߲R$7GANCꓮO>q\ e}ߞNpxg[Gl؊ v?jz7GC _{1GJti'W. d̎=SKf0O ⻹.Pl@=p7uwO{E<)>_J-p]k /ɛ'3h-J?]wT):eqľ{eeGc0oҷ >%~MGrE:=s!3n/َ(WNWX9z>?\" qTf12e)|v˥e |ҝq;sX9&.t"@?_G>; B/t ӿrDXk>0Ǖw40g2vY/{_TȺsPg֯ 7t/p%&3_}Kc-|_z5Z G>{sS'߱ #&?s@Rf^Cqfhr9剴O @ve8a>muRk:5Bl$uÏЕ_W.~D7s:&}H2yc=<7Sx0g}nkuJ.Hh=8ʾ4uS^||A143~6qW[YÔT`|dKf-M{yk&kq#}O#%ݿe󋓝'ߵrf:gi2?sm%'V4tg45>{e4k{{!|~"7ßr`7oc?3*ЙMq^ Mzի^ 4-'K~:u3X;~0Y߅לkEyYnG?P"W_I}3uƭ-3y9w+O̳QWr?S2}Һ+.$?ʜC㒼dz!-lV\hoI+Oy.kqp?q5nv9 @?>/}[1ぎgCU<[t3ԩxe/~ KIXs/C|@<j_ܒ-g&?:8o(/sUcigxRF3u{X^eߘ7u cdA~3ֆX9i|9R;VOqӌd-0ֺo^9|ZqQ?7hc :8 &nkS)6\7sݍukc}mHxy.S  ;m7"(㢬=3r^33na]k?WǸvSst3sC7M8u)&6ɻoSh{~&tb^^j'sX=I?aBWK0pˑᎋ3|3  aN:`^̕}fmSR.ӟ0\2j_v1Q6ޕ=.2^ֵHO^/bc GcCQ{kӏg'wxN-oiy yI2o 1? \푐zOC>Cw~>Hた/xʻ8kʍ9f8m9W-Z-dk4]yY#\c̑u8p^Cc85hכ?>0ŨnlԼYKtg"Ay:mݶ![?9c'>D;96N #OlSS/ '18 _;~=m9N=Gc۹m ktnj-Cu^x=̋ڊs_}CE;Hf[ymn/=OtT|9 oƱA Fy }AgЛG`S<_gm/ǜd5~^́=x5Oڶ7N᫞qNs8}g.@7y?cOyߚߙc 's:ғ?W'\gn8u`@z-?oM';՞`z#XT=Jx PMg ձ{\O12ؑm)oqu{%5d,an$oPS g s~_`SΓy<1>i=~#on94< o?ΫG. \l#xla!B{x3χlaf^9iAXRwԬab֪x{n񼫼ژEgҴ׉Su c;.{nĸd=ka=}kt 񨤁\Xy AokkK?z$ϘsWs}&y=M=t.<=Eݯnm\0s~H߲X>wI g/= x}O LY$)sfgY w$Տa83?>:@ clmo`Y#|ܑsk|<~)asu\ìujZ0wY3gf 3=ZҴ)n#|4_>|XCԖvm X: #e\$kOfEptAYo'Ϛc'oYQOy'/6ՔN3O=& T0_Od2J{f~+\}G{}H;<t`ӥ>Ʉm東I{2d5HЛ5S H_@3c{,kQuږM]li?|nj\/h=>v//|G>7Õk37>g;h7yӑy끇Q乄]6yN|0cݣ4x.Ax:}a{ž&}L-*ã߹iػސy6TxS0\}d?Y{5\?9{x.ai<5ɏz3wC8Ro>xF5f3.y+bmΩ|=Nϩ H>loW{6\^][`MO@kNvu}g]ߥya\NtMWϣ7G <Ѓ> ,xPOG5D7]̈́k/cqpC''uKWg89Yξ#d] [uϳRG'ck8k?pM`O]i?1d<;UFٺ.BuzYqWFG[#s`m3ge-^:/ ~k 3aC&f > gα|m?k?08\s/3Kiflg"}{%ďuv>M8vmh`7uo%37PI='}mO~ ٳ <9i8EƯF&\\?e !elwg65ɄP2O{s]|ʞ?!ƒ qs-0k'eO mw{wͫumKX5XVy6pf+iw o~jK/rP2mκ~͞ҧ64ү1i9|]o;kkZ9}A1al#x> i1>x_GHb: x1 Y\͙3|;V%}Y?kMۦ/-} _ [uc%mR0da^1~Zoen۰q>Ԛdmw_>2gp 3nӁ!^VnFvf-෹t}0鼍RK:^鬖.n'e|3(q 5GbƬ x8g?X}搼v?3/k2'sxvʿHatIO݃]zׅYwyGp=dž~9sc S^yMsYσ^k} u\I9j }Ʊїme1/X:gugkЇ>t'?N%_7x[m~@/كsKK:^Y'q%w}gc/TGWw~q`;hZ멦3@ػ{칟< k.B^N1?үvt^=*Ea?7ue~pA$^K6{s::c9H_HF;x߸EoђQ̙k_52Q_GYds߰CӘ2+wcEށ<ǜA=y5#kpO1Hܛ%UoY+c󆺇_6&?wu ܁1)eߺpah?kyfv)x?S.g@'Ћqx{e-+isL[,| k.1J{;';GReNpO#W>/Lr&@::=e <3.F!<?< 8{^?-k\y'J^~Ѷ c4:{>a~49!n&'tY2ه\mU. +W3ޚz&|Վ!;k98&-sM3g7n]Yd.~/:z83o<_IX3x zns{t@- 3um噣ž_9pݳsĸ.< ϱ'{ "c L!ig>z$~^k>4)EJǮ/>#YWԋoNM_m+g}&r.yL`f^=S,FigͻuGa}<9P)vn[~oZSӿ7x>uk!yg\s[Sw4n@?fk_{@1g$27'׈o Լs^Ç=kZZs{W\Uf0o}pʖ,eR^~mS}y~ѳuo{.+ +wxTslIi_-kN=Zd1썴α2 9Z19ens<+/c{NN:\W)vsL֤lc7yeگvcf#33?&{sAu_)S}Z_+2uXWkA-ujWr4wMI98mU&3歮p<>Ę0chV!G9u߲>99ϱ~:+'d.z :ƚ՞3=3-S0̑sX=׿Ǿ `U}zYyͣą}hٿ\R.!r/sϕio;\.¾*ڬSO:}xg\ s>=qN0_H_3ElyQكԳGqڏ^x>ygs{ V+R+#pl 7pq=+X+sNc?Ȃ雯U?Jr1ߛ[&}+q!X+|? >~R_k.2FϾcl|,GG{}{bW{> n?`x,IX3g]2yB!Lq9{d,ۺ|l*,<'ZWKeξp1',ydgw z}2g"k~$0W O |OHf:gsc__'Vψy6o:#u`uce=-8#//{RgKȸOsdm4&OT'naOsncݾ̋g.~:-y=[?^L8%6Hx,i4:iԄ(_wE7v[3]=aO_<+xx53FgoY_r߳G'iey6oy5ַ/g_wFgzƎϰ758~ԙ1=k1M.P{e\G%6o A煇X XK$3Y-HtmkXa)$:ݖ_9{؃A9|u=xx'7=D>O{eX'ٻ]ʾ=]]c#_YgNO>: d2הSdMdM g2{_ |=Y`8 b)cML2!s0Ԟs.agkw8N֞w1| Z-I;XϚ Ɓw@^}7Wӿ7ǭU \se.%o}Ã[My'8{_)yH'ީ}<=Xlܕ=~A}FiMY;կNWmhΞp~o[+>?v'x jp=>Y<^|>O>[>䮟H>_t%ԏOCJXt!ߞ5¼4f!?t=@CGf)-N3>&`ڠks# G'fM|~N&07͜Km~-ٯ A@/kgWOrquoE[/|snsx粭KU"G, (Tk"/Z~PTT̢Y1QbfO5췟:UkS~??/}y[zݻwۿ_G]G_]=ȏ|G}iO{}}.nvycs||V\_/kv__];;{w~˻˻뻾~O{c{3[[߸.ov3pLw/8|k>.YuO~,[q0___O~闎 ?_ŗOO9ցhd k_5y/zy|r > ==oo^__.oe=p=8ŇO|wϏOO7O}ϓ+ '`?c??:~o<ʳ|\>C?dzx֙}{{~Su^.}؏}ٟu׾'0i?gz=< }):'Nym-|'S>)~ė3·zrA>;gp/x!yL4OOZ8yl/$_k::?1CC{Nx/Nx6}H/ҏ$&.ىg5q_^O;vWLz?AAww?pno{x/zMoz>7|L~y4pc]r`x:Ӿ<M?3?ii]=LgvWW~WCg <1}ρwl4!gGX. kl:>}SG ˏȏ8+SYmt߱Ot))d]wqsfó:p<)*OO;ǺtI9|G|ġ O=+~lS=7~h'x ~U}/>~ ү?]>^~EO{kO/xƳ3֞|i|DcKMycr@7yIw/kXCH.l?ڒkMΓuπ;ό%dz>|`mϡ=^IB[0_w&?pJ?g{gNl]vaw3W^i7;4oNn^+V;vc'\CXO㭻1k|?˯=ip \W>o:\krW@9 -J~gkd-|N {jh?>_128g?11lSr+^qy+_ysh;}H8*&KCߠ9N4kgDLrM`+A?y1C=t#^:pƶO|ez~(T=Mvֆc^;}WYpD/ҏ_d+9=wxM>oW۟ @kf4#dhonzys{>nܽV\#ˇ3>c_|W>&Zw2-9撙⣝s9' [^^0[=[7dLl4sit)Gro}{vzӿrtFuq⋾DiW:u~~'~'^>?С .=%'ScY{/;}+7ށ|Eߣ _Nt ՌeebϰFk 5p[^Z_lc̸'{x~8}gZd5>~ݬ7ًr _/̣G|?[wu^EN x;yJ\OrJ^[G#p%;9^rU3D?SwU|'(DrXm8Q,Cg>ك3يIj]t.JGk.kP`{7>k/̕^};^xN;]wQ>Sx0}|xjVCгՌo&ϕkJB0+\=<ی Ȱ}?>ѳk,ӫ\U//Atoz. '"w`,VWm|k_{֚p37kŘxt~="l V ~|_@#f=6/?t3В|36s57s/o՘/ ħփjz3-[\蝏Г>_.3Lg<_c ՞WNN !?JlG~ZSYg'>>E#z~'wL=#->3k#<Ӌ#zʓ鏮yrTV|Wg= Ch© Ն?>{7ֻY_/gKwYw{!9ȉ|}qp,N?|PŷN:.ANKn}^=OO|k):L~OqQuj3F?tϙÉoS]?8YOX/#*f'~X g+;E_}W`wx= :?='`఼癏;ְG12 7-8d3gc禌b_|sU7~~!/<7ÿY#ڔg|gN>o&?Μg|V?dO`$? 4k|ŎS^\=Э<}1c+;R~OzzKz<[sOWs|8lô`;z(&|.r3kILYpg_|^bkmO;Kig fst[gV_U `űjb~yirb:@mkbi: ψ[+?xNjhdNpuo}W{^fzztL=j ؏=h7s/W)[W;+7)j}9]π뀣:.7c﵎`i֮ C{^ϞzV0}y˙}kN/&k6x =)xX33fGk c:}+tG|0l}vkjt̾ks^N'd\w5crU`#oOӧ?﵎KN#ZDOWVk>qz %I7V/`'|Ung>}{ޠ xQO]Q{YCwXzwvN.Y]q5/ߓO-ߋ~cϸh9k#Af5Pm9uh2Ť|ڪK%T|i{_՗Sglf[5^CC/zf[OOgηiQOOy-ti?FZ .5{/8;L3Nږ3 v<fVKտ`T_lNw7'kޒKSO_36)KNuXkz_[3]<ˈSn<۽y]SSo-O,Tܝavv&BrzN8냳sාXSJ6 :ߵ^m/>/֯,gwŏpY/ 9~ߚg5<+9յ.A^U7g>ޠ?-P?5 9- k?N75pz]q>trwz95>uh;;_}g|N/nB3tn\;bX쏟)ӹX˹72=g\p7+99[w4gKtxo5sFWeWk4ϥȬg͛^7:5;d~3_o|WQk\G:~MC&hٓw??}ww,b6'=+^W8h蛟ŞLfl+}~?;u봯Y^_ W"g \><pOraO5:̙5Sc~ A#M}~@4ɪ'OQv9ٳxlŶ|;W;U|8Qv=bا5M|v\;q+?c)3'+^v5c!u>Ybr,sE5i5VY=5*Oכd]crw Z{@e~QOи<w|>%=?vt~|!gP~D߉%W7{m63|L2$g稊9Vͩm27ƶ;}:3泷lG=dՑק/׌}7 \'x;W`ɯ+g߳p T#}Ahbz)/}Ӝ-:9/'>ůͶ\{OC7rZMWk^ܟZs\_kzNbz>w׮赳kJlFٓ5ڛ=}zwzG{:ig;=?`#eO֚v?+gֵVeKW= <_·笮oyf_ ߞʓM֚8Xk鬝}ާ,k~SGoE=b%lֹkqs3?|δN} 96zn\m'^#ϟ!~O+ſ&89 |t6A_~9977c|=Bާo{7 w`fKZeћɢOcϜgX@o:tΑN\*{<K~w|3? WK5w)}qۙ +^ 1kY]3.3P*9SK&;Ϛs@7WO6O;9ts|us=;v'|~7za-g}iO\rR.M;WcukOWkgs:G+ |]cU^>ךk8Xw ~z44W3ߟ^g`<_3E^k>.Oh^u:}7?2Aw&z?5_IGtOEjn_3] _{lǝ8hQs>[l `JgN/O.чx'hmH9y17\ӕOTSêxH1ь[ylAqcq;St֋ErgO`4d79; d.g%>.ѵxP~ܾ}^h&`=37-@Ofws^zo}~Y+9`h\~: 9[p[_g{oH帊ý.N8˿;/v~P/3{clg=%ľ꥜ ,Fnf1٬OCgty:3io B3[_N;g]a==m38u&gYp}<+z+=?ZO̷K=-]~u9y3?}^7ZcU/뾢͞sVx[%/OY_PӮy5:>^Z|>qGMeKgw_Ic__zx֧3kj/{ܒf;w1͂'kkb;{ւ1!93V7>o/w\w=4'L7kjrh :Wvߝۏ7Kg )o͗ϺE:D.|3>Hg,\:|8>t伳3ֹ{ל_pg|*F> y!7҇.w|ϛ<{dW$u;?Ox,vėz}Lt/"O~,_W9Sz>;^,NbY˓m7\,2 ~/zC W:<_ ~?,v^kWG̗j#sBk ,~+|?W ~zיE`m/ ,y!7`"G{iVw7K\Y {9=n=3 ݈l9uy}Gy;ӝ~hУS6v=}$sp]π 9|!~ėsy=3ǬK^ !GrFޒzo??ZO~ŝL=C;ӗ^f9yW }̟5柱S wsQ1Zv-w^p۞Yy>5^[ͽk`sv yV3s{;pC%Zsml};˻} o?G97ߵFseY|rX/ƣGOnc6q6 8r8*/Z P ~&sXGv~0OӇDtKjQO%m ymm=}z)FX3m}Y9Gd3g_a]i:]չe_Y){^ ɛ|ֶx~ਖ਼8s/<÷ߣ'/:9zog|)5Ϻ;뻅Vc~n!=JK~mwu|3>yAz>cD?O}SSgvv{7ҙ=K|X0t&۹Yg_cYX 7]Xe0gwgحԮŬ3vwvwgޫ6=g<^'ɉϩwv?ъǛrb UOAbuϵگw7+N|=|Ն y*6+oݫo':֬Su=,kFﳾ'S".Zct=Dm}`8WON9]^4[1uXfPˢCК\6Y:V^w%V9es OAg uHsHG~u9!']p#UM6[ncyx u}stvsw|c=$&%ЇdٓU'39u5wI秃?_`'_gr/w5ww Wj x@LA'{s6+Oc/+Z Y3GghGů &M=|D+8+%\3]룟1H=595@Kݽ~/=v0 ]ǷݗoW*V^5Vv9H O;XOwI2:cuݚwkl=cs.\R{z :{FߔGvv6gYM^yf;s^m﷾|{AqV!*˻ry;ό_sr'knͨn^-UwHt5ը)~ŷ"r@E 'G䉽\ajkkw%pWq:oA/p1wWX ^jzn4hFu%;cye5斠=LWN_N_sWjY^9`?[ݟ=k>5i\\ͻn[{rkogK$Q7Gl%܊)Tk\,Fp37/yK=3H^>_&_}YH=9֤W]k#?&kWzKѣީGkq9tG:-oukַ|~9A_4Dcl\ͽ2jtA3e9@by~g=<\xrK+f.xد;k?%,j|˷ܛN_`m#~;u_~$uu֏XDnkԇn3o1ϖ?!p & 9˜lcfpT?IX .ft7ί6++w&5׎l𭛑}vE7k_#;efY.K }w3fud`C<^N?g9i4a#b𠚧c;4A/8 r mI~r6p{˗mfL=`KaosV_[6#z>P9\g|wÜ_Fh~63u7u}`'z[mguvWݙ̖s>lϟ/zыmށoܬ3f3g]PVm YSݩ;&N }o3x%xu2G漊2ZO)f773 SoAqo^8cV79߫N{dtY6z~k/Z5fx|x>Ѳ>sD}LgTX+8K7FȳlW[5o1(5٫q/oSr/?_Vg>3s= u~>ܩ}^Eg<[%>}|y'%Sr}ri ~{73Fhp@_p姩cљ^).?8cߊE1̎$70}L/-7PNqי@3] `gd3q>}5.}wg/Yݹ^v؝0k!ȷiOt^ɋ]wgy釈'ـ~hk^ȱڔ|72bTCد8sv8 t/Xg|Qͳ<<\GE6*S)۟'{ϳ_7K 9.c8};3]N3w'>0k6ݭ/[b}gsSw|!~肏3`lcqZ '֦[gKa)=grMw7r+鸩o+DOP͊^mzEqFw"2\M9Y#\~<mw9&3/9cn|+}gκƓ~N%35os<ɌAnsM+׳96՘.O>YRIyl8;g8 =zWuнRu>;[6 'WBotG|E}ݏW ?ui_ڿ=7{_,=9D̬5&xv&`x̗6iƣA?Sxzg"̐5Ul=टѠsSP;anJLαXiמlzqfӃ g~@swL`k.gqꋢ䵚Gݡjsɯbjcs+j~Hx}麵G^+w:Yl[뻾됇 MtݍF4o^k t}d;_yeU-U~whgu^{rv[ 4~M95Õ L}šSo^/ΉG%A]}`e?]g‡x "&٥ |`3:k/hIKfS<'<9}pWkN_> 6iegvh=ebn]yjfAxfֳB|w>}r_73_kO~fp_Kr6uhf둥FvF{XZw {u_GL'xK'o⪳9 cv!7o Y/FٌKEv}fs?jpxY-+ag;r[w*5WO.]S?xv.z& 7SwƷ39g;B?wnhx'^y\*7+y{6y$I%orNާ/Gzz %wrlzЋ#=ٝ kN/53^$ϟm>f yvvhf]5~fSٓd|=ӻ;}7rSqȧW֜@x{suby^~bėOk͵_q;Db7$񓚧snO ўg}9#k6ץy3wR`skˋȗAQ[%BnؓCǚs;/;Zѩr)Uuzf/.׬8l.`wz&;~Uߛ:3f ;:Շ{:MlfCoO׾RΜ^쌲g7/pͫ~ԝLӏ|?;8\za<_@ _6xlb57F@pڶ+guCo/:SY̑_ ㌟w)~ŷL;=+yH6'r;C#$z|##wg5랓u.tM4,KiP4}𦾱y>`/ cL8V[4gS}cnf̩~l~ ?>9|7tkuT;sIf. ۴u}}DMLm:iXLHb+g,#k>s}]~rkYije$_á~5y Qws˫I֕gijP\_G |98l \z>dgS7gb9lBc_sׇg7Ci}1gu~үV%>lX#{ŬWwǓ֙؆db3)_<8[jOkwz}  +СyN5G_tFoΈ4;~y[ G]F|핬[<;vIN_w#7+2D .tf7EugLїGL_[nkWNgߔO;4Eεuvt3Ǻf_fGƻ9p/Zyv+U}&(_rVTòw&&_w+ kdL2N5Ogg<@8#\r'*kޝ@'wǓ+_AX/:_⣇kjxdF#st g)y={U?ڤ_O~!Ⱥ;ټkIKѓ8Cb3=u6 Oޤޱӳwy-Ρuds߫חN4Dp>8Jrݩ;|)/ew ֋Gg#uw಻B6g=;_QϟXlL>dvr|%|o[\۔{)zo{6>5jƎs/zыdMgS9w~xޣs`gqfC9߯?򄝩LOgѻ= G23^Glnlww=3zt3gO ͹q ϔ]눳mTNvC>ggm}OދAꁪh͎xՙ]<6 " 萝&:g]9=tFo?>y|/ 't~lVÇB|{5 Iȅg{)_sP/x]rJ^:(#<7kGM>V==b箧%rgg{OxӫX.9~ 371|nֆ~ uΙݞwUyp/!*xy_5D!(J&24j"* H?VJB% *sѠ4ܞw޿;}?Oߵu?}_sg>?9|>я>>wۿ=___}?ItOs\ϧ~e/{ʧ|wnk??}h+ڢ'=hAçqknrkp]p+]iW ?n4K_mύ&t{.uKmcÝ|}zի^Lo^mK_i WW3?3OOwl>jWw+r||}//?S?uMcg|g꫾6~?}{v^Wo~~?7zrlc1Ň:?æЖ6}綷Ŵ_Wgmmۿ.;|^t-pэn>o6s|}\<|&mu;?f;dM׾7ᦳ__>yk_;7}]Wxyyxr]n+z莶.__7O{==j_?ӯсtm22>4^6~|hh'4i?o|OA.ȑ<ɕ|y=4zD'>~NݧX~Z}v! N؋LgW식~?|aaژɕx2#ϼ?>)O 3ad0;~ztnO8JO:6ox>{| xex41gI M 섟G][~3?~votߦ >']Wdַu7 [>~=XxG;NǴOѡ_6cd2c' w~Wj7icL 7[у7N?~} ^>S'm?993GIKh$wGO>G~xB#4>k~7oޫ?m N Ɇggݱu'xnٯ1j+YN98L=|?7z[w|D> m»S/釱Qxힶ~k{pNq/4NjPtГ;ɗ[/Evowа19\sEzKm|̾ky8@цEaP>e:ɗ7e_e-]\7^lsHsFg7~7?)t??ï^Wl<1Ox@/(>6@7о:d_qOt̯ޯ ӏt]7a