static void test_getimagespec_gettexels (ustring filename) { ImageSpec spec; if (! texsys->get_imagespec (filename, 0, spec)) { std::cerr << "Could not get spec for " << filename << "\n"; std::string e = texsys->geterror (); if (! e.empty()) std::cerr << "ERROR: " << e << "\n"; return; } int w = spec.width/2, h = spec.height/2; ImageSpec postagespec (w, h, spec.nchannels, TypeDesc::FLOAT); ImageBuf buf ("postage.exr", postagespec); TextureOptions opt; opt.nchannels = spec.nchannels; if (missing[0] >= 0) opt.missingcolor.init ((float *)&missing, 0); std::vector<float> tmp (w*h*spec.nchannels); bool ok = texsys->get_texels (filename, opt, 0, w/2, w/2+w, h/2, h/2+h, 0, 1, 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 offset = (y*w + x) * spec.nchannels; buf.setpixel (x, y, &tmp[offset]); } buf.save (); }
// 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); }
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); } }
// 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); }
// Test ImageBuf::zero and ImageBuf::fill void ImageBuf_zero_fill () { 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 ("A", spec); // Set a pixel to an odd value, make sure it takes const float arbitrary1[CHANNELS] = { 0.2, 0.3, 0.4, 0.5 }; 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.6, 0.7, 0.3, 0.9 }; 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.42, 0.43, 0.44, 0.45 }; { const int xbegin = 3, xend = 5, ybegin = 0, yend = 4; ImageBufAlgo::fill (A, arbitrary3, 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]); } } } } }
static void test_texture3d (ustring filename) { std::cerr << "Testing 3d texture " << filename << ", output = " << output_filename << "\n"; const int nchannels = 4; ImageSpec outspec (output_xres, output_yres, nchannels, TypeDesc::HALF); ImageBuf image (output_filename, outspec); ImageBufAlgo::zero (image); Imath::M33f scale; scale.scale (Imath::V2f (0.5, 0.5)); Imath::M33f rot; rot.rotate (radians(30.0f)); Imath::M33f trans; trans.translate (Imath::V2f (0.35f, 0.15f)); Imath::M33f xform = scale * rot * trans; xform.invert(); TextureOptions opt; opt.sblur = blur; opt.tblur = blur; opt.rblur = blur; opt.swidth = width; opt.twidth = width; opt.rwidth = width; opt.nchannels = nchannels; float localfill = (fill >= 0 ? fill : 0.0f); opt.fill = localfill; if (missing[0] >= 0) opt.missingcolor.init ((float *)&missing, 0); opt.swrap = opt.twrap = opt.rwrap = TextureOptions::WrapPeriodic; int shadepoints = blocksize*blocksize; Imath::V3f *P = ALLOCA (Imath::V3f, shadepoints); Runflag *runflags = ALLOCA (Runflag, shadepoints); Imath::V3f *dPdx = ALLOCA (Imath::V3f, shadepoints); Imath::V3f *dPdy = ALLOCA (Imath::V3f, shadepoints); Imath::V3f *dPdz = ALLOCA (Imath::V3f, shadepoints); float *result = ALLOCA (float, shadepoints*nchannels); for (int iter = 0; iter < iters; ++iter) { // Iterate over blocks // Trick: switch to second texture, if given, for second iteration if (iter && filenames.size() > 1) filename = ustring (filenames[1]); for (int by = 0; by < output_yres; by+=blocksize) { for (int bx = 0; bx < output_xres; bx+=blocksize) { // Process pixels within a block. First save the texture warp // (s,t) and derivatives into SIMD vectors. int idx = 0; for (int y = by; y < by+blocksize; ++y) { for (int x = bx; x < bx+blocksize; ++x) { if (x < output_xres && y < output_yres) { if (nowarp) { P[idx][0] = (float)x/output_xres * sscale; P[idx][1] = (float)y/output_yres * tscale; P[idx][2] = 0.5f * sscale; P[idx] += offset; dPdx[idx][0] = 1.0f/output_xres * sscale; dPdx[idx][1] = 0; dPdx[idx][2] = 0; dPdy[idx][0] = 0; dPdy[idx][1] = 1.0f/output_yres * tscale; dPdy[idx][2] = 0; dPdz[idx].setValue (0,0,0); } else { Imath::V3f coord = warp ((float)x/output_xres, (float)y/output_yres, 0.5, xform); coord.x *= sscale; coord.y *= tscale; coord += offset; Imath::V3f coordx = warp ((float)(x+1)/output_xres, (float)y/output_yres, 0.5, xform); coordx.x *= sscale; coordx.y *= tscale; coordx += offset; Imath::V3f coordy = warp ((float)x/output_xres, (float)(y+1)/output_yres, 0.5, xform); coordy.x *= sscale; coordy.y *= tscale; coordy += offset; P[idx] = coord; dPdx[idx] = coordx - coord; dPdy[idx] = coordy - coord; dPdz[idx].setValue (0,0,0); } runflags[idx] = RunFlagOn; } else { runflags[idx] = RunFlagOff; } ++idx; } } // Call the texture system to do the filtering. bool ok = texsys->texture3d (filename, opt, runflags, 0, shadepoints, Varying(P), Varying(dPdx), Varying(dPdy), Varying(dPdz), result); if (! ok) { std::string e = texsys->geterror (); if (! e.empty()) std::cerr << "ERROR: " << e << "\n"; } for (int i = 0; i < shadepoints*nchannels; ++i) result[i] *= scalefactor; // Save filtered pixels back to the image. idx = 0; for (int y = by; y < by+blocksize; ++y) { for (int x = bx; x < bx+blocksize; ++x) { if (runflags[idx]) { image.setpixel (x, y, result + idx*nchannels); } ++idx; } } } } } if (! image.save ()) std::cerr << "Error writing " << output_filename << " : " << image.geterror() << "\n"; }
static void test_plain_texture () { std::cerr << "Testing 2d texture " << filenames[0] << ", output = " << output_filename << "\n"; const int nchannels = 4; ImageSpec outspec (output_xres, output_yres, nchannels, TypeDesc::HALF); ImageBuf image (output_filename, outspec); ImageBufAlgo::zero (image); Imath::M33f scale; scale.scale (Imath::V2f (0.5, 0.5)); Imath::M33f rot; rot.rotate (radians(30.0f)); Imath::M33f trans; trans.translate (Imath::V2f (0.35f, 0.15f)); Imath::M33f xform = scale * rot * trans; xform.invert(); TextureOptions opt; opt.sblur = blur; opt.tblur = blur; opt.swidth = width; opt.twidth = width; opt.nchannels = nchannels; float localfill = (fill >= 0.0f) ? fill : 1.0f; opt.fill = localfill; if (missing[0] >= 0) opt.missingcolor.init ((float *)&missing, 0); // opt.interpmode = TextureOptions::InterpSmartBicubic; // opt.mipmode = TextureOptions::MipModeAniso; opt.swrap = opt.twrap = TextureOptions::WrapPeriodic; // opt.twrap = TextureOptions::WrapBlack; #if 1 TextureOpt opt1; opt1.sblur = blur; opt1.tblur = blur; opt1.swidth = width; opt1.twidth = width; opt1.nchannels = nchannels; opt1.fill = localfill; if (missing[0] >= 0) opt1.missingcolor = (float *)&missing; opt1.swrap = opt1.twrap = TextureOpt::WrapPeriodic; #endif int shadepoints = blocksize*blocksize; float *s = ALLOCA (float, shadepoints); float *t = ALLOCA (float, shadepoints); Runflag *runflags = ALLOCA (Runflag, shadepoints); float *dsdx = ALLOCA (float, shadepoints); float *dtdx = ALLOCA (float, shadepoints); float *dsdy = ALLOCA (float, shadepoints); float *dtdy = ALLOCA (float, shadepoints); float *result = ALLOCA (float, shadepoints*nchannels); ustring filename = ustring (filenames[0]); TextureSystem::Perthread *perthread_info = texsys->get_perthread_info (); TextureSystem::TextureHandle *texture_handle = texsys->get_texture_handle (filename); 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 = ustring (filenames[texid]); std::cerr << "iter " << iter << " file " << filename << "\n"; } // Iterate over blocks for (int by = 0, b = 0; by < output_yres; by+=blocksize) { for (int bx = 0; bx < output_xres; bx+=blocksize, ++b) { // Trick: switch to other textures on later iterations, if any if (iters == 1 && filenames.size() > 1) { // Use a different filename from block to block filename = ustring (filenames[b % (int)filenames.size()]); } // Process pixels within a block. First save the texture warp // (s,t) and derivatives into SIMD vectors. int idx = 0; for (int y = by; y < by+blocksize; ++y) { for (int x = bx; x < bx+blocksize; ++x) { if (x < output_xres && y < output_yres) { if (nowarp) { s[idx] = (float)x/output_xres * sscale + offset[0]; t[idx] = (float)y/output_yres * tscale + offset[1]; dsdx[idx] = 1.0f/output_xres * sscale; dtdx[idx] = 0; dsdy[idx] = 0; dtdy[idx] = 1.0f/output_yres * tscale; } else { Imath::V3f coord = warp ((float)x/output_xres, (float)y/output_yres, xform); coord.x *= sscale; coord.y *= tscale; coord += offset; Imath::V3f coordx = warp ((float)(x+1)/output_xres, (float)y/output_yres, xform); coordx.x *= sscale; coordx.y *= tscale; coordx += offset; Imath::V3f coordy = warp ((float)x/output_xres, (float)(y+1)/output_yres, xform); coordy.x *= sscale; coordy.y *= tscale; coordy += offset; s[idx] = coord[0]; t[idx] = coord[1]; dsdx[idx] = coordx[0] - coord[0]; dtdx[idx] = coordx[1] - coord[1]; dsdy[idx] = coordy[0] - coord[0]; dtdy[idx] = coordy[1] - coord[1]; } runflags[idx] = RunFlagOn; } else { runflags[idx] = RunFlagOff; } ++idx; } } // Call the texture system to do the filtering. bool ok; if (blocksize == 1) { if (use_handle) ok = texsys->texture (texture_handle, perthread_info, opt1, s[0], t[0], dsdx[0], dtdx[0], dsdy[0], dtdy[0], result); else ok = texsys->texture (filename, opt1, s[0], t[0], dsdx[0], dtdx[0], dsdy[0], dtdy[0], result); } else { ok = texsys->texture (filename, opt, runflags, 0, shadepoints, Varying(s), Varying(t), Varying(dsdx), Varying(dtdx), Varying(dsdy), Varying(dtdy), result); } if (! ok) { std::string e = texsys->geterror (); if (! e.empty()) std::cerr << "ERROR: " << e << "\n"; } for (int i = 0; i < shadepoints*nchannels; ++i) result[i] *= scalefactor; // Save filtered pixels back to the image. idx = 0; for (int y = by; y < by+blocksize; ++y) { for (int x = bx; x < bx+blocksize; ++x) { if (runflags[idx]) { image.setpixel (x, y, result + idx*nchannels); } ++idx; } } } } } if (! image.save ()) std::cerr << "Error writing " << output_filename << " : " << image.geterror() << "\n"; }
bool setNbChannels(ImageBuf &dst, const ImageBuf &src, int numChannels) { // Not intended to create 0-channel images. if (numChannels <= 0) 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) return false; if (numChannels == src.spec().nchannels) { dst = src; return true; } // Update the ImageSpec // (should this be moved to a helper function in the imagespec.h? ImageSpec dst_spec = src.spec(); dst_spec.nchannels = numChannels; if (numChannels < src.spec().nchannels) { // Reduce the number of formats, and names, if needed if (static_cast<int>(dst_spec.channelformats.size()) == src.spec().nchannels) dst_spec.channelformats.resize(numChannels); if (static_cast<int>(dst_spec.channelnames.size()) == src.spec().nchannels) dst_spec.channelnames.resize(numChannels); if (dst_spec.alpha_channel < numChannels-1) { dst_spec.alpha_channel = -1; } if (dst_spec.z_channel < numChannels-1) { dst_spec.z_channel = -1; } } else { // Increase the number of formats, and names, if needed if (static_cast<int>(dst_spec.channelformats.size()) == src.spec().nchannels) { for (int c = dst_spec.channelnames.size(); c < numChannels; ++c) { dst_spec.channelformats.push_back(dst_spec.format); } } if (static_cast<int>(dst_spec.channelnames.size()) == src.spec().nchannels) { for (int c = dst_spec.channelnames.size(); c < numChannels; ++c) { dst_spec.channelnames.push_back (Strutil::format("channel%d", c)); } } } // Update the image (realloc with the new spec) dst.alloc (dst_spec); std::vector<float> pixel(numChannels, 0.0f); // Walk though the data window. I.e., the crop window in a small image // or the overscanned area in a large image. for (int k = dst_spec.z; k < dst_spec.z+dst_spec.depth; k++) { for (int j = dst_spec.y; j < dst_spec.y+dst_spec.height; j++) { for (int i = dst_spec.x; i < dst_spec.x+dst_spec.width; i++) { src.getpixel (i, j, k, &pixel[0], numChannels); dst.setpixel (i, j, k, &pixel[0], numChannels); } } } return true; }
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 }
static void test_plain_texture () { std::cerr << "Testing 2d texture " << filenames[0] << ", output = " << output_filename << "\n"; const int nchannels = 4; ImageSpec outspec (output_xres, output_yres, nchannels, TypeDesc::HALF); 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 (); } ImageBuf image (output_filename, outspec); ImageBufAlgo::zero (image); Imath::M33f scale; scale.scale (Imath::V2f (0.5, 0.5)); Imath::M33f rot; rot.rotate (radians(30.0f)); Imath::M33f trans; trans.translate (Imath::V2f (0.35f, 0.15f)); Imath::M33f xform = scale * rot * trans; xform.invert(); TextureOptions opt; opt.sblur = blur; opt.tblur = blur; opt.swidth = width; opt.twidth = width; opt.nchannels = nchannels; float localfill = (fill >= 0.0f) ? fill : 1.0f; opt.fill = localfill; if (missing[0] >= 0) opt.missingcolor.init ((float *)&missing, 0); // opt.interpmode = TextureOptions::InterpSmartBicubic; // opt.mipmode = TextureOptions::MipModeAniso; TextureOptions::parse_wrapmodes (wrapmodes.c_str(), opt.swrap, opt.twrap); TextureOpt opt1; opt1.sblur = blur; opt1.tblur = blur; opt1.swidth = width; opt1.twidth = width; opt1.nchannels = nchannels; opt1.fill = localfill; if (missing[0] >= 0) opt1.missingcolor = (float *)&missing; TextureOpt::parse_wrapmodes (wrapmodes.c_str(), opt1.swrap, opt1.twrap); int shadepoints = blocksize*blocksize; float *s = ALLOCA (float, shadepoints); float *t = ALLOCA (float, shadepoints); Runflag *runflags = ALLOCA (Runflag, shadepoints); float *dsdx = ALLOCA (float, shadepoints); float *dtdx = ALLOCA (float, shadepoints); float *dsdy = ALLOCA (float, shadepoints); float *dtdy = ALLOCA (float, shadepoints); float *result = ALLOCA (float, shadepoints*nchannels); ustring filename = ustring (filenames[0]); TextureSystem::Perthread *perthread_info = texsys->get_perthread_info (); TextureSystem::TextureHandle *texture_handle = texsys->get_texture_handle (filename); 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 = ustring (filenames[texid]); std::cerr << "iter " << iter << " file " << filename << "\n"; } // Iterate over blocks for (int by = 0, b = 0; by < output_yres; by+=blocksize) { for (int bx = 0; bx < output_xres; bx+=blocksize, ++b) { // Trick: switch to other textures on later iterations, if any if (iters == 1 && filenames.size() > 1) { // Use a different filename from block to block filename = ustring (filenames[b % (int)filenames.size()]); } // Process pixels within a block. First save the texture warp // (s,t) and derivatives into SIMD vectors. int idx = 0; for (int y = by; y < by+blocksize; ++y) { for (int x = bx; x < bx+blocksize; ++x) { if (x < output_xres && y < output_yres) { if (nowarp) { s[idx] = (float)x/output_xres * sscale + offset[0]; t[idx] = (float)y/output_yres * tscale + offset[1]; dsdx[idx] = 1.0f/output_xres * sscale; dtdx[idx] = 0; dsdy[idx] = 0; dtdy[idx] = 1.0f/output_yres * tscale; } else if (tube) { float xt = float(x)/output_xres - 0.5f; float dxt_dx = 1.0f/output_xres; float yt = float(y)/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 float denom = 1.0f / (xt*xt + yt*yt); float dtheta_dx = yt*dxt_dx * denom; float dtheta_dy = -xt*dyt_dy * denom; s[idx] = 4.0f * theta / (2.0f * M_PI); dsdx[idx] = 4.0f * dtheta_dx / (2.0f * M_PI); dsdy[idx] = 4.0f * dtheta_dy / (2.0f * M_PI); float h = hypot(xt,yt); float dh_dx = xt*dxt_dx / h; float dh_dy = yt*dyt_dy / h; h *= M_SQRT2; dh_dx *= M_SQRT2; dh_dy *= M_SQRT2; float hinv = 1.0f / h; t[idx] = hinv; dtdx[idx] = hinv * (-hinv * dh_dx); dtdy[idx] = hinv * (-hinv * dh_dy); } else { Imath::V3f coord = warp ((float)x/output_xres, (float)y/output_yres, xform); coord.x *= sscale; coord.y *= tscale; coord += offset; Imath::V3f coordx = warp ((float)(x+1)/output_xres, (float)y/output_yres, xform); coordx.x *= sscale; coordx.y *= tscale; coordx += offset; Imath::V3f coordy = warp ((float)x/output_xres, (float)(y+1)/output_yres, xform); coordy.x *= sscale; coordy.y *= tscale; coordy += offset; s[idx] = coord[0]; t[idx] = coord[1]; dsdx[idx] = coordx[0] - coord[0]; dtdx[idx] = coordx[1] - coord[1]; dsdy[idx] = coordy[0] - coord[0]; dtdy[idx] = coordy[1] - coord[1]; } runflags[idx] = RunFlagOn; } else { runflags[idx] = RunFlagOff; } ++idx; } } // Call the texture system to do the filtering. bool ok; if (blocksize == 1) { if (use_handle) ok = texsys->texture (texture_handle, perthread_info, opt1, s[0], t[0], dsdx[0], dtdx[0], dsdy[0], dtdy[0], result); else ok = texsys->texture (filename, opt1, s[0], t[0], dsdx[0], dtdx[0], dsdy[0], dtdy[0], result); } else { ok = texsys->texture (filename, opt, runflags, 0, shadepoints, Varying(s), Varying(t), Varying(dsdx), Varying(dtdx), Varying(dsdy), Varying(dtdy), result); } if (! ok) { std::string e = texsys->geterror (); if (! e.empty()) std::cerr << "ERROR: " << e << "\n"; } for (int i = 0; i < shadepoints*nchannels; ++i) result[i] *= scalefactor; // Save filtered pixels back to the image. idx = 0; for (int y = by; y < by+blocksize; ++y) { for (int x = bx; x < bx+blocksize; ++x) { if (runflags[idx]) { image.setpixel (x, y, result + idx*nchannels); } ++idx; } } } } if (resetstats) { std::cout << texsys->getstats(2) << "\n"; texsys->reset_stats (); } } if (! image.save ()) std::cerr << "Error writing " << output_filename << " : " << image.geterror() << "\n"; }
bool ImageBufAlgo::colorconvert (ImageBuf &dst, const ImageBuf &src, const ColorProcessor* processor, bool unpremult) { // If the processor is NULL, return false (error) if (!processor) return false; ImageSpec dstspec = dst.spec(); std::vector<float> scanline(dstspec.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 dstspec.alpha_channel index // (but first validate that the index is set properly for normal formats) int channelsToCopy = std::min (4, dstspec.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 // // FIXME: Use the ImageBuf::ConstIterator<T,T> s (src); s.isValid() // idiom for traversal instead, to allow for more efficient tile access // iteration order float * dstPtr = NULL; const float fltmin = std::numeric_limits<float>::min(); for (int k = dstspec.z; k < dstspec.z+dstspec.depth; k++) { for (int j = dstspec.y; j < dstspec.y+dstspec.height; j++) { // Load the scanline dstPtr = &scanline[0]; for (int i = dstspec.x; i < dstspec.x+dstspec.width ; i++) { src.getpixel (i, j, dstPtr, channelsToCopy); dstPtr += 4; } // Optionally unpremult if ((channelsToCopy>=4) && unpremult) { float alpha = 0.0; for (int i=0; i<dstspec.width; ++i) { 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 // This is always an rgba float image, due to the conversion above. for(int i=0; i<dstspec.width; ++i) { scanline[4*i+0] = (*processor->t2)((*processor->t1)(scanline[4*i+0])); scanline[4*i+1] = (*processor->t2)((*processor->t1)(scanline[4*i+1])); scanline[4*i+2] = (*processor->t2)((*processor->t1)(scanline[4*i+2])); } // Optionally premult if ((channelsToCopy>=4) && unpremult) { float alpha = 0.0; for (int i=0; i<dstspec.width; ++i) { 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]; for (int i = dstspec.x; i < dstspec.x+dstspec.width ; i++) { dst.setpixel (i, j, dstPtr, channelsToCopy); dstPtr += 4; } } } return true; }