bool TextureSystemImpl::accum3d_sample_bilinear (const Imath::V3f &P, int miplevel, TextureFile &texturefile, PerThreadInfo *thread_info, TextureOpt &options, 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)); // 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 DASSERT (options.swrap_func != NULL && options.twrap_func != NULL && options.rwrap_func != NULL); 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) >= 6*sizeof(bool)); const unsigned long long none_valid = 0; const unsigned long long all_valid = 0x010101010101LL; DASSERT (__LITTLE_ENDIAN__ && "this trick won't work with big endian"); bool *svalid = valid_storage.bvalid; bool *tvalid = valid_storage.bvalid + 2; bool *rvalid = valid_storage.bvalid + 4; svalid[0] = options.swrap_func (stex[0], spec.x, spec.width); svalid[1] = options.swrap_func (stex[1], spec.x, spec.width); tvalid[0] = options.twrap_func (ttex[0], spec.y, spec.height); tvalid[1] = options.twrap_func (ttex[1], spec.y, spec.height); rvalid[0] = options.rwrap_func (rtex[0], spec.z, spec.depth); rvalid[1] = options.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(); size_t pixelsize = texturefile.pixelsize(); if (onetile && valid_storage.ivalid == all_valid) { // Shortcut if all the texels we need are on the same tile TileID id (texturefile, options.subimage, miplevel, 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 + options.firstchannel) * 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; TileID id (texturefile, options.subimage, miplevel, 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 + options.firstchannel) * channelsize; #if DEBUG 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 (channelsize == 1) { // special case for 8-bit tiles int c; for (c = 0; c < options.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 (c = 0; c < options.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 { // 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, options.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 < options.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 ); } } } return true; }
bool TextureSystemImpl::accum3d_sample_closest (const Imath::V3f &P, int miplevel, TextureFile &texturefile, PerThreadInfo *thread_info, TextureOpt &options, 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)); // 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 DASSERT (options.swrap_func != NULL && options.twrap_func != NULL && options.rwrap_func != NULL); bool svalid, tvalid, rvalid; // Valid texels? false means black border svalid = options.swrap_func (stex, spec.x, spec.width); tvalid = options.twrap_func (ttex, spec.y, spec.height); rvalid = options.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_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); 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; size_t channelsize = texturefile.channelsize(); int tilepel = (tile_r * spec.tile_height + tile_t) * spec.tile_width + tile_s; int offset = spec.nchannels * tilepel + options.firstchannel; DASSERT ((size_t)offset < spec.nchannels*spec.tile_pixels()); if (channelsize == 1) { // special case for 8-bit tiles const unsigned char *texel = tile->bytedata() + offset; for (int c = 0; c < options.actualchannels; ++c) accum[c] += weight * uchar2float(texel[c]); } else { // General case for float tiles const float *texel = tile->data() + offset; for (int c = 0; c < options.actualchannels; ++c) accum[c] += weight * texel[c]; } return true; }