bool Channel::read_tile_or_closest_ancestor(TileIndex ti, TileIndex &ret_index, Tile &ret) const { Locker lock(*this); // Lock self and hold lock until exiting this method ChannelInfo info; bool success = read_info(info); if (!success) { if (verbosity) log_f("read_tile_or_closest_ancestor: can't read info"); return false; } TileIndex root = info.nonnegative_root_tile_index; if (ti.is_ancestor_of(root)) { ret_index = root; } else { if (ti != root && !root.is_ancestor_of(ti)) { // Tile isn't under root return false; } assert(tile_exists(root)); ret_index = root; while (ret_index != ti) { TileIndex child = ti.start_time() < ret_index.left_child().end_time() ? ret_index.left_child() : ret_index.right_child(); if (!tile_exists(child)) break; ret_index = child; } } // ret_index now holds closest ancestor to ti (or ti itself if it exists) assert(read_tile(ret_index, ret)); return true; }
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(); } }
void _openslide_read_tiles(cairo_t *cr, int32_t level, int64_t start_tile_x, int64_t start_tile_y, int64_t end_tile_x, int64_t end_tile_y, double offset_x, double offset_y, double advance_x, double advance_y, openslide_t *osr, struct _openslide_cache *cache, void *arg, void (*read_tile)(openslide_t *osr, cairo_t *cr, int32_t level, int64_t tile_x, int64_t tile_y, double translate_x, double translate_y, struct _openslide_cache *cache, void *arg)) { //g_debug("offset: %g %g, advance: %g %g", offset_x, offset_y, advance_x, advance_y); if (fabs(offset_x) >= advance_x) { _openslide_set_error(osr, "internal error: fabs(offset_x) >= advance_x"); return; } if (fabs(offset_y) >= advance_y) { _openslide_set_error(osr, "internal error: fabs(offset_y) >= advance_y"); return; } // cairo_set_source_rgb(cr, 0, 1, 0); // cairo_paint(cr); //g_debug("offset: %d %d", offset_x, offset_y); //g_debug("start: %" G_GINT64_FORMAT " %" G_GINT64_FORMAT, start_tile_x, start_tile_y); //g_debug("end: %" G_GINT64_FORMAT " %" G_GINT64_FORMAT, end_tile_x, end_tile_y); int64_t tile_y = end_tile_y - 1; while (tile_y >= start_tile_y) { double translate_y = ((tile_y - start_tile_y) * advance_y) - offset_y; int64_t tile_x = end_tile_x - 1; while (tile_x >= start_tile_x) { double translate_x = ((tile_x - start_tile_x) * advance_x) - offset_x; // g_debug("read_tiles %" G_GINT64_FORMAT " %" G_GINT64_FORMAT, tile_x, tile_y); read_tile(osr, cr, level, tile_x, tile_y, translate_x, translate_y, cache, arg); tile_x--; } tile_y--; } }
static void draw_hudline(const int x, const int y) { if ((vga_line-y)>=0 && (vga_line-y)<8) { for (int i=0;i<20;i++) { int chr=hud[i]; if (chr!=' ') { chr = chr>='A' ? chr+256-'A' : chr-'0'; for (int dx=0;dx<8;dx++) { uint8_t c=read_tile(0xF5, dx+chr*8, vga_line-y); if (c!=TRANSPARENT) draw_buffer[x+dx+i*8]=c; } } } } }
void Channel::read_bottommost_tiles_in_range(Range times, bool (*callback)(const Tile &t, Range times)) const { ChannelInfo info; bool success = read_info(info); if (!success) return; if (!info.times.intersects(times)) return; double time = times.min; TileIndex ti = TileIndex::null(); while (time < times.max) { if (ti.is_null()) { ti = find_lowest_child_overlapping_time(info.nonnegative_root_tile_index, times.min); } else { ti = find_lowest_successive_tile(info.nonnegative_root_tile_index, ti); } if (ti.is_null() || ti.start_time() >= times.max) break; Tile t; assert(read_tile(ti, t)); if (!(*callback)(t, times)) break; } }
void Channel::read_data(std::vector<DataSample<double> > &data, double begin, double end) const { double time = begin; data.clear(); Locker lock(*this); // Lock self and hold lock until exiting this method ChannelInfo info; bool success = read_info(info); if (!success) { // Channel doesn't yet exist; no data if (verbosity) log_f("read_data: can't read info"); return; } bool first_tile = true; while (time < end) { TileIndex ti = find_lowest_child_overlapping_time(info.nonnegative_root_tile_index, time); if (ti.is_null()) { // No tiles; no more data if (verbosity) log_f("read_data: can't read tile"); return; } Tile tile; assert(read_tile(ti, tile)); unsigned i = 0; if (first_tile) { // Skip any samples before requested time for (; i < tile.double_samples.size() && tile.double_samples[i].time < begin; i++); } for (; i < tile.double_samples.size() && tile.double_samples[i].time < end; i++) { data.push_back(tile.double_samples[i]); } time = ti.end_time(); } }
std::string Channel::dump_tile_summaries_internal(TileIndex ti, int level) const { Tile tile; if (!read_tile(ti, tile)) return ""; std::string ret = string_printf("%*s%d.%d: %zd samples\n", level, "", ti.level, ti.offset, tile.double_samples.size()); return ret + dump_tile_summaries_internal(ti.left_child(), level+1) + dump_tile_summaries_internal(ti.right_child(), level+1); }
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 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 ImageInput::read_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int firstchan, int nchans, TypeDesc format, 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; nchans = std::min (nchans, m_spec.nchannels-firstchan); // native_pixel_bytes is the size of a pixel in the FILE, including // the per-channel format. stride_t native_pixel_bytes = (stride_t) m_spec.pixel_bytes (firstchan, nchans, true); // perchanfile is true if the file has different per-channel formats bool perchanfile = m_spec.channelformats.size(); // native_data is true if the user asking for data in the native format bool native_data = (format == TypeDesc::UNKNOWN || (format == m_spec.format && !perchanfile)); if (format == TypeDesc::UNKNOWN && xstride == AutoStride) xstride = native_pixel_bytes; m_spec.auto_stride (xstride, ystride, zstride, format, nchans, xend-xbegin, yend-ybegin); // Do the strides indicate that the data area is contiguous? bool contiguous = (native_data && xstride == native_pixel_bytes) || (!native_data && xstride == (stride_t)m_spec.pixel_bytes(false)); contiguous &= (ystride == xstride*(xend-xbegin) && (zstride == ystride*(yend-ybegin) || (zend-zbegin) <= 1)); int nxtiles = (xend - xbegin + m_spec.tile_width - 1) / m_spec.tile_width; int nytiles = (yend - ybegin + m_spec.tile_height - 1) / m_spec.tile_height; int nztiles = (zend - zbegin + m_spec.tile_depth - 1) / m_spec.tile_depth; // If user's format and strides are set up to accept the native data // layout, and we're asking for a whole number of tiles (no partial // tiles at the edges), then read the tile directly into the user's // buffer. if (native_data && contiguous && (xend-xbegin) == nxtiles*m_spec.tile_width && (yend-ybegin) == nytiles*m_spec.tile_height && (zend-zbegin) == nztiles*m_spec.tile_depth) { if (firstchan == 0 && nchans == m_spec.nchannels) return read_native_tiles (xbegin, xend, ybegin, yend, zbegin, zend, data); // Simple case else return read_native_tiles (xbegin, xend, ybegin, yend, zbegin, zend, firstchan, nchans, data); } // No such luck. Just punt and read tiles individually. bool ok = true; stride_t pixelsize = native_data ? native_pixel_bytes : (format.size() * nchans); stride_t full_pixelsize = native_data ? m_spec.pixel_bytes(true) : (format.size() * m_spec.nchannels); size_t prefix_bytes = m_spec.pixel_bytes (0,firstchan,true); 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 read directly into the user buffer, // but partial tiles (such as at the image edge) or // partial channel subsets are read into a buffer and // then copied. if (xw == m_spec.tile_width && yh == m_spec.tile_height && zd == m_spec.tile_depth && firstchan == 0 && nchans == m_spec.nchannels) { ok &= read_tile (x, y, z, format, tilestart, xstride, ystride, zstride); } else { buf.resize (m_spec.tile_bytes()); ok &= read_tile (x, y, z, format, &buf[0], full_pixelsize, full_pixelsize*m_spec.tile_width, full_pixelsize*m_spec.tile_pixels()); if (ok) copy_image (nchans, xw, yh, zd, &buf[prefix_bytes], pixelsize, full_pixelsize, full_pixelsize*m_spec.tile_width, full_pixelsize*m_spec.tile_pixels(), tilestart, xstride, ystride, zstride); } tilestart += m_spec.tile_width * xstride; } } } if (! ok) error ("ImageInput::read_tiles : no support for format %s", m_spec.format.c_str()); return ok; }