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; std::vector<char> pels (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<char> 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) { bool native = (format == TypeDesc::UNKNOWN); stride_t pixel_bytes = native ? (stride_t) m_spec.pixel_bytes (native) : (stride_t) (format.size()*m_spec.nchannels); if (native && xstride == AutoStride) xstride = pixel_bytes; m_spec.auto_stride (xstride, ystride, zstride, format, m_spec.nchannels, 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 // Locally allocate a single tile to gracefully deal with image // dimensions smaller than a tile, or if one of the tiles runs // past the right or bottom edge. Then we copy from our tile to // the user data, only copying valid pixel ranges. stride_t tilexstride = pixel_bytes; stride_t tileystride = tilexstride * m_spec.tile_width; stride_t tilezstride = tileystride * m_spec.tile_height; imagesize_t tile_pixels = m_spec.tile_pixels(); std::vector<char> pels (tile_pixels * pixel_bytes); for (int z = 0; z < m_spec.depth; z += m_spec.tile_depth) for (int y = 0; y < m_spec.height; y += m_spec.tile_height) { for (int x = 0; x < m_spec.width && ok; x += m_spec.tile_width) { ok &= read_tile (x+m_spec.x, y+m_spec.y, z+m_spec.z, format, &pels[0]); // Now copy out the scanlines int ntz = std::min (z+m_spec.tile_depth, m_spec.depth) - z; int nty = std::min (y+m_spec.tile_height, m_spec.height) - y; int ntx = std::min (x+m_spec.tile_width, m_spec.width) - x; for (int tz = 0; tz < ntz; ++tz) { for (int ty = 0; ty < nty; ++ty) { // FIXME -- doesn't work for non-contiguous scanlines memcpy ((char *)data + x*xstride + (y+ty)*ystride + (z+tz)*zstride, &pels[ty*tileystride+tz*tilezstride], ntx*tilexstride); } } // return ok; // DEBUG -- just try very first tile } if (progress_callback) if (progress_callback (progress_callback_data, (float)y/m_spec.height)) return ok; } } else { // Scanline image for (int z = 0; z < m_spec.depth; ++z) for (int y = 0; y < m_spec.height && ok; ++y) { ok &= read_scanline (y+m_spec.y, z+m_spec.z, format, (char *)data + z*zstride + y*ystride, xstride); if (progress_callback && !(y & 0x0f)) 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 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 = (stride_t) m_spec.pixel_bytes (native); if (native && 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-1, 0, m_spec.height-1, 0, m_spec.depth-1, format, data, xstride, ystride, zstride); } bool ok = true; if (progress_callback) if (progress_callback (progress_callback_data, 0.0f)) return ok; if (m_spec.tile_width && supports ("tiles")) { // Tiled image // FIXME: what happens if the image dimensions are smaller than // the tile dimensions? Or if one of the tiles runs past the // right or bottom edge? Do we need to allocate a full tile and // copy into it before calling write_tile? That's probably the // safe thing to do. Or should that handling be pushed all the // way into write_tile itself? // Locally allocate a single tile to gracefully deal with image // dimensions smaller than a tile, or if one of the tiles runs // past the right or bottom edge. Then we copy from our tile to // the user data, only copying valid pixel ranges. size_t tilexstride = pixel_bytes; size_t tileystride = tilexstride * m_spec.tile_width; size_t tilezstride = tileystride * m_spec.tile_height; size_t tile_pixels = (size_t)m_spec.tile_width * (size_t)m_spec.tile_height * (size_t)std::max(1,m_spec.tile_depth); std::vector<char> pels (tile_pixels * pixel_bytes); for (int z = 0; z < m_spec.depth; z += m_spec.tile_depth) for (int y = 0; y < m_spec.height; y += m_spec.tile_height) { for (int x = 0; x < m_spec.width && ok; x += m_spec.tile_width) { // Now copy out the scanlines // FIXME -- can we do less work for the tiles that // don't overlap image boundaries? int ntz = std::min (z+m_spec.tile_depth, m_spec.depth) - z; int nty = std::min (y+m_spec.tile_height, m_spec.height) - y; int ntx = std::min (x+m_spec.tile_width, m_spec.width) - x; for (int tz = 0; tz < ntz; ++tz) { for (int ty = 0; ty < nty; ++ty) { // FIXME -- doesn't work for non-contiguous scanlines memcpy (&pels[ty*tileystride+tz*tilezstride], (char *)data + x*xstride + (y+ty)*ystride + (z+tz)*zstride, ntx*tilexstride); } } ok &= write_tile (x+m_spec.x, y+m_spec.y, z+m_spec.z, format, &pels[0]); } if (progress_callback) if (progress_callback (progress_callback_data, (float)y/m_spec.height)) return ok; } } else { // Scanline image for (int z = 0; z < m_spec.depth; ++z) for (int y = 0; y < m_spec.height && ok; ++y) { ok &= write_scanline (y+m_spec.y, z+m_spec.z, format, (const char *)data + z*zstride + y*ystride, xstride); if (progress_callback && !(y & 0x0f)) 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; }