static void fix_latl_edges (ImageBuf &buf) { ASSERT (envlatlmode && "only call fix_latl_edges for latlong maps"); 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] += 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); } }
void ImageBuf::copy_from (const ImageBuf &src) { if (this == &src) return; ASSERT (m_spec.width == src.m_spec.width && m_spec.height == src.m_spec.height && m_spec.depth == src.m_spec.depth && m_spec.nchannels == src.m_spec.nchannels); realloc (); if (m_spec.deep) m_deepdata = src.m_deepdata; else src.get_pixels (src.xbegin(), src.xend(), src.ybegin(), src.yend(), src.zbegin(), src.zend(), m_spec.format, m_localpixels); }
int main (int argc, char *argv[]) { // Arguments if(argc != 3) { std::cerr << "mkthumbnail usage : mkthumbnail <input> <output>" << std::endl; return EXIT_FAILURE; } std::string inputname=argv[1]; std::string outputname=argv[2]; std::string colortransfer_to = "sRGB"; std::string colortransfer_from = "sRGB"; // Colorspace selection if(extensionIs(inputname, "exr") ) { colortransfer_from="Linear"; } else if(extensionIs(inputname, "dpx")) { colortransfer_from="KodakLog"; } else { colortransfer_from="sRGB"; } bool ok = true; ImageBuf in; if (! read_input (inputname, in)) { std::cerr << "mkthumbnail: read error: " << in.geterror() << "\n"; return EXIT_FAILURE; } ColorTransfer *from_func = ColorTransfer::create (colortransfer_from + "_to_linear"); if (from_func == NULL) { std::cerr << "mkthumbnail: --colorspace needs a 'colorspace' of " << "Linear, Gamma, sRGB, AdobeRGB, Rec709 or KodakLog\n"; return EXIT_FAILURE; } ColorTransfer *to_func = ColorTransfer::create (std::string("linear_to_") + colortransfer_to); if (to_func == NULL) { std::cerr << "mkthumbnail: --transfer needs a 'colorspace' of " << "Linear, Gamma, sRGB, AdobeRGB, Rec709 or KodakLog\n"; return EXIT_FAILURE; } std::cout << "Converting [" << colortransfer_from << "] " << inputname << " to [" << colortransfer_to << "] " << outputname << "\n"; // ImageBuf linear; ImageBuf outtmp; ImageBufAlgo::colortransfer (linear, in, from_func); ImageBufAlgo::colortransfer (outtmp, linear, to_func); std::cout << "finished color transfer\n"; // Pixel aspect ratio const ImageIOParameter *aspect = in.spec().find_attribute ("pixelaspectratio", TypeDesc::FLOAT); float pixel_ratio = aspect ? *(float *)aspect->data() : 1.0f; float ratio = ((float)in.spec().height+(float)in.spec().y)/((float)in.spec().width+(float)in.spec().x); pixel_ratio = pixel_ratio != 0.f ? pixel_ratio : 1.f; const int resize_w = 480; // resize target width int resize_y = (int)((float)resize_w*ratio/pixel_ratio); int resize_x = resize_w; ImageSpec outspec = outtmp.spec(); outspec.x = 0; outspec.y = 0; outspec.full_x = 0; outspec.full_y = 0; outspec.width = resize_x; outspec.height = resize_y; outspec.full_width = resize_x; outspec.full_height = resize_y; ImageBuf out (outputname, outspec); float pixel[3] = { .1f, .1f, .1f }; ImageBufAlgo::fill (out, pixel); if(!ImageBufAlgo::resize (out, outtmp, out.xbegin(), out.xend(), out.ybegin(), out.yend())) { std::cerr << "mkthumbnail: unable to resize image, " << out.geterror() << std::endl; return EXIT_FAILURE; } if(out.spec().nchannels>3) { std::cout << "Changing number of channels to 3 "<< "\n"; ImageBuf newout("temp", out.spec()); setNbChannels (newout, out, 3); out = newout; } if(!out.save (outputname)) { std::cerr << "mkthumbnail: unable to save output image, " << out.geterror() << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; }
static bool write_mipmap (ImageBufAlgo::MakeTextureMode mode, ImageBuf &img, const ImageSpec &outspec_template, std::string outputfilename, ImageOutput *out, TypeDesc outputdatatype, bool mipmap, Filter2D *filter, const ImageSpec &configspec, std::ostream &outstream, double &stat_writetime, double &stat_miptime) { bool envlatlmode = (mode == ImageBufAlgo::MakeTxEnvLatl); 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; 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); 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"); if (verbose) { outstream << " Writing file: " << outputfilename << std::endl; outstream << " Filter \"" << filter->name() << "\" width = " << filter->width() << "\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<std::string> mipimages; std::string mipimages_unsplit = configspec.get_string_attribute ("maketx:mipimages"); if (mipimages_unsplit.length()) Strutil::split (mipimages_unsplit, mipimages, ";"); ImageBuf tmp; ImageBuf *big = &img, *small = &tmp; 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"; ImageBuf *t = new ImageBuf (mipimages[0], smallspec); ImageBufAlgo::setNumChannels(*t, *small, outspec.nchannels); std::swap (t, small); delete t; } 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 = big->spec().width; smallspec.height = big->spec().height; smallspec.depth = big->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; 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->alloc (smallspec); // Realocate with new size big->set_full (big->xbegin(), big->xend(), big->ybegin(), big->yend(), big->zbegin(), big->zend()); if (filter->name() == "box" && filter->width() == 1.0f) ImageBufAlgo::parallel_image (boost::bind(resize_block, small, big, _1, envlatlmode), OIIO::get_roi(small->spec())); else ImageBufAlgo::parallel_image (boost::bind(resize_block_HQ, small, big, _1, filter), OIIO::get_roi(small->spec())); } 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) { outstream << " " << formatres(smallspec) << std::endl; } std::swap (big, small); } } if (verbose) outstream << " Wrote file: " << outputfilename << std::endl; writetimer.reset (); writetimer.start (); if (! out->close ()) { outstream << "maketx ERROR writing \"" << outputfilename << "\" : " << out->geterror() << "\n"; return false; } stat_writetime += writetimer (); return true; }
bool ImageBuf::copy_pixels (const ImageBuf &src) { // compute overlap int xbegin = std::max (this->xbegin(), src.xbegin()); int xend = std::min (this->xend(), src.xend()); int ybegin = std::max (this->ybegin(), src.ybegin()); int yend = std::min (this->yend(), src.yend()); int zbegin = std::max (this->zbegin(), src.zbegin()); int zend = std::min (this->zend(), src.zend()); int nchannels = std::min (this->nchannels(), src.nchannels()); // If we aren't copying over all our pixels, zero out the pixels if (xbegin != this->xbegin() || xend != this->xend() || ybegin != this->ybegin() || yend != this->yend() || zbegin != this->zbegin() || zend != this->zend() || nchannels != this->nchannels()) ImageBufAlgo::zero (*this); // Call template copy_pixels_ based on src data type switch (src.spec().format.basetype) { case TypeDesc::FLOAT : copy_pixels_<float> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; case TypeDesc::UINT8 : copy_pixels_<unsigned char> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; case TypeDesc::INT8 : copy_pixels_<char> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; case TypeDesc::UINT16: copy_pixels_<unsigned short> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; case TypeDesc::INT16 : copy_pixels_<short> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; case TypeDesc::UINT : copy_pixels_<unsigned int> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; case TypeDesc::INT : copy_pixels_<int> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; case TypeDesc::HALF : copy_pixels_<half> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; case TypeDesc::DOUBLE: copy_pixels_<double> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; case TypeDesc::UINT64: copy_pixels_<unsigned long long> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; case TypeDesc::INT64 : copy_pixels_<long long> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; default: ASSERT (0); } return true; }