void Channel::move_root_upwards(TileIndex new_root_index, TileIndex old_root_index) { Tile old_root_tile; Tile empty_tile; assert(read_tile(old_root_index, old_root_tile)); TileIndex ti = old_root_index; while (ti != new_root_index) { write_tile(ti.sibling(), empty_tile); write_tile(ti.parent(), old_root_tile); ti = ti.parent(); } }
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; std::vector<char> 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 { buf.resize (pixelsize * m_spec.tile_pixels()); OIIO_NAMESPACE::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 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); }
TileIndex Channel::split_tile_if_needed(TileIndex ti, Tile &tile) { TileIndex new_root_index = TileIndex::null(); if (tile.binary_length() <= m_max_tile_size) return new_root_index; Tile children[2]; TileIndex child_indexes[2]; if (verbosity) log_f("split_tile_if_needed: splitting tile %s", ti.to_string().c_str()); // If we're splitting an "all" tile, it means that until now the channel has only had one tile's worth of // data, and that a proper root tile location couldn't be selected. Select a new root tile now. if (ti.is_nonnegative_all()) { // TODO: this breaks if all samples are at one time ti = new_root_index = TileIndex::index_containing(Range(tile.first_sample_time(), tile.last_sample_time())); if (verbosity) log_f("split_tile_if_needed: Moving root tile to %s", ti.to_string().c_str()); } child_indexes[0]= ti.left_child(); child_indexes[1]= ti.right_child(); double split_time = ti.right_child().start_time(); split_samples(tile.double_samples, split_time, children[0], children[1]); split_samples(tile.string_samples, split_time, children[0], children[1]); for (int i = 0; i < 2; i++) { assert(!has_tile(child_indexes[i])); assert(split_tile_if_needed(child_indexes[i], children[i]) == TileIndex::null()); write_tile(child_indexes[i], children[i]); } create_parent_tile_from_children(ti, tile, children); return new_root_index; }
int traverse_zooms(int geomfd[4], off_t geom_size[4], char *metabase, unsigned *file_bbox, struct pool **file_keys, unsigned *midx, unsigned *midy, char **layernames, int maxzoom, int minzoom, sqlite3 *outdb, double droprate, int buffer, const char *fname, const char *tmpdir, double gamma, int nlayers, char *prevent) { int i; for (i = 0; i <= maxzoom; i++) { long long most = 0; FILE *sub[4]; int subfd[4]; int j; for (j = 0; j < 4; j++) { char geomname[strlen(tmpdir) + strlen("/geom2.XXXXXXXX") + 1]; sprintf(geomname, "%s/geom%d.XXXXXXXX", tmpdir, j); subfd[j] = mkstemp(geomname); //printf("%s\n", geomname); if (subfd[j] < 0) { perror(geomname); exit(EXIT_FAILURE); } sub[j] = fopen(geomname, "wb"); if (sub[j] == NULL) { perror(geomname); exit(EXIT_FAILURE); } unlink(geomname); } long long todo = 0; long long along = 0; for (j = 0; j < 4; j++) { todo += geom_size[j]; } for (j = 0; j < 4; j++) { if (geomfd[j] < 0) { // only one source file for zoom level 0 continue; } if (geom_size[j] == 0) { continue; } // printf("%lld of geom_size\n", (long long) geom_size[j]); char *geom = mmap(NULL, geom_size[j], PROT_READ, MAP_PRIVATE, geomfd[j], 0); if (geom == MAP_FAILED) { perror("mmap geom"); exit(EXIT_FAILURE); } char *geomstart = geom; char *end = geom + geom_size[j]; while (geom < end) { int z; unsigned x, y; deserialize_int(&geom, &z); deserialize_uint(&geom, &x); deserialize_uint(&geom, &y); // fprintf(stderr, "%d/%u/%u\n", z, x, y); long long len = write_tile(&geom, metabase, file_bbox, z, x, y, z == maxzoom ? full_detail : low_detail, maxzoom, file_keys, layernames, outdb, droprate, buffer, fname, sub, minzoom, maxzoom, todo, geomstart, along, gamma, nlayers, prevent); if (len < 0) { return i - 1; } if (z == maxzoom && len > most) { *midx = x; *midy = y; most = len; } } if (munmap(geomstart, geom_size[j]) != 0) { perror("munmap geom"); } along += geom_size[j]; } for (j = 0; j < 4; j++) { close(geomfd[j]); fclose(sub[j]); struct stat geomst; if (fstat(subfd[j], &geomst) != 0) { perror("stat geom\n"); exit(EXIT_FAILURE); } geomfd[j] = subfd[j]; geom_size[j] = geomst.st_size; } } fprintf(stderr, "\n"); return maxzoom; }
void Channel::create_tile(TileIndex ti) { Tile tile; write_tile(ti, tile); }
void Channel::add_data_internal(const std::vector<DataSample<T> > &data, DataRanges *channel_ranges) { if (!data.size()) return; // Sanity check if (data[0].time < 0) throw std::runtime_error("Unimplemented feature: adding data with negative time"); for (unsigned i = 0; i < data.size()-1; i++) { if (data[i].time > data[i+1].time) throw std::runtime_error("Attempt to add data that is not sorted by ascending time"); } // regenerate = empty set Locker lock(*this); // Lock self and hold lock until exiting this method std::set<TileIndex> to_regenerate; ChannelInfo info; bool success = read_info(info); if (!success) { // New channel info.magic = ChannelInfo::MAGIC; info.version = 0x00010000; info.times = Range(data[0].time, data.back().time); info.nonnegative_root_tile_index = TileIndex::nonnegative_all(); create_tile(TileIndex::nonnegative_all()); info.negative_root_tile_index = TileIndex::null(); } else { info.times.add(Range(data[0].time, data.back().time)); // If we're not the all-tile, see if we need to move the root upwards if (info.nonnegative_root_tile_index != TileIndex::nonnegative_all()) { TileIndex new_nonnegative_root_tile_index = TileIndex::index_containing(info.times); if (new_nonnegative_root_tile_index.level > info.nonnegative_root_tile_index.level) { // Root index changed. Confirm new root is parent or more distant ancestor of old root assert(new_nonnegative_root_tile_index.is_ancestor_of(info.nonnegative_root_tile_index)); // Trigger regeneration from old root's parent, up through new root to_regenerate.insert(info.nonnegative_root_tile_index.parent()); move_root_upwards(new_nonnegative_root_tile_index, info.nonnegative_root_tile_index); info.nonnegative_root_tile_index = new_nonnegative_root_tile_index; } } } unsigned i=0; while (i < data.size()) { TileIndex ti= find_lowest_child_overlapping_time(info.nonnegative_root_tile_index, data[i].time); assert(!ti.is_null()); Tile tile; assert(read_tile(ti, tile)); const DataSample<T> *begin = &data[i]; while (i < data.size() && ti.contains_time(data[i].time)) i++; const DataSample<T> *end = &data[i]; tile.insert_samples(begin, end); TileIndex new_root = split_tile_if_needed(ti, tile); if (new_root != TileIndex::null()) { assert(ti == TileIndex::nonnegative_all()); if (verbosity) log_f("Channel: %s changing root from %s to %s", descriptor().c_str(), ti.to_string().c_str(), new_root.to_string().c_str()); info.nonnegative_root_tile_index = new_root; delete_tile(ti); // Delete old root ti = new_root; } write_tile(ti, tile); if (ti == info.nonnegative_root_tile_index && channel_ranges) { *channel_ranges = tile.ranges; } if (ti != info.nonnegative_root_tile_index) to_regenerate.insert(ti.parent()); } // Regenerate from lowest level to highest while (!to_regenerate.empty()) { TileIndex ti = *to_regenerate.begin(); to_regenerate.erase(to_regenerate.begin()); Tile regenerated, children[2]; assert(read_tile(ti.left_child(), children[0])); assert(read_tile(ti.right_child(), children[1])); create_parent_tile_from_children(ti, regenerated, children); write_tile(ti, regenerated); if (ti == info.nonnegative_root_tile_index && channel_ranges) { *channel_ranges = regenerated.ranges; } if (ti != info.nonnegative_root_tile_index) to_regenerate.insert(ti.parent()); } write_info(info); }
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; }