static bool process_hier_data_pages_from_indexfile(FILE *f, off_t seek_location, int datafile_count, char **datafile_names, const char *dirname, int zoom_levels, struct _openslide_jpeg_layer **layers, int tiles_across, int tiles_down, int image_divisions, int32_t *tile_positions, GList **jpegs_list, struct _openslide_hash *quickhash1) { int32_t jpeg_number = 0; bool success = false; // used for storing which positions actually have data GHashTable *active_positions = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, NULL); for (int zoom_level = 0; zoom_level < zoom_levels; zoom_level++) { struct _openslide_jpeg_layer *l = layers[zoom_level]; int32_t ptr; // g_debug("reading zoom_level %d", zoom_level); if (fseeko(f, seek_location, SEEK_SET) == -1) { g_warning("Cannot seek to zoom level pointer %d", zoom_level + 1); goto DONE; } ptr = read_le_int32_from_file(f); if (ptr == -1) { g_warning("Can't read zoom level pointer"); goto DONE; } if (fseeko(f, ptr, SEEK_SET) == -1) { g_warning("Cannot seek to start of data pages"); goto DONE; } // read initial 0 if (read_le_int32_from_file(f) != 0) { g_warning("Expected 0 value at beginning of data page"); goto DONE; } // read pointer ptr = read_le_int32_from_file(f); if (ptr == -1) { g_warning("Can't read initial data page pointer"); goto DONE; } // seek to offset if (fseeko(f, ptr, SEEK_SET) == -1) { g_warning("Can't seek to initial data page"); goto DONE; } int32_t next_ptr; do { // read length int32_t page_len = read_le_int32_from_file(f); if (page_len == -1) { g_warning("Can't read page length"); goto DONE; } // g_debug("page_len: %d", page_len); // read "next" pointer next_ptr = read_le_int32_from_file(f); if (next_ptr == -1) { g_warning("Cannot read \"next\" pointer"); goto DONE; } // read all the data into the list for (int i = 0; i < page_len; i++) { int32_t tile_index = read_le_int32_from_file(f); int32_t offset = read_le_int32_from_file(f); int32_t length = read_le_int32_from_file(f); int32_t fileno = read_le_int32_from_file(f); if (tile_index < 0) { g_warning("tile_index < 0"); goto DONE; } if (offset < 0) { g_warning("offset < 0"); goto DONE; } if (length < 0) { g_warning("length < 0"); goto DONE; } if (fileno < 0) { g_warning("fileno < 0"); goto DONE; } // we have only encountered images with exactly power-of-two scale // factors, and there appears to be no clear way to specify otherwise, // so require it int32_t x = tile_index % tiles_across; int32_t y = tile_index / tiles_across; if (y >= tiles_down) { g_warning("y (%d) outside of bounds for zoom level (%d)", y, zoom_level); goto DONE; } if (x % (1 << zoom_level)) { g_warning("x (%d) not correct multiple for zoom level (%d)", x, zoom_level); goto DONE; } if (y % (1 << zoom_level)) { g_warning("y (%d) not correct multiple for zoom level (%d)", y, zoom_level); goto DONE; } // save filename if (fileno >= datafile_count) { g_warning("Invalid fileno"); goto DONE; } char *filename = g_build_filename(dirname, datafile_names[fileno], NULL); // hash in the lowest-res on-disk tiles if (zoom_level == zoom_levels - 1) { if (!_openslide_hash_file_part(quickhash1, filename, offset, length)) { g_free(filename); g_warning("Can't hash tiles"); goto DONE; } } // populate the file structure struct _openslide_jpeg_file *jpeg = g_slice_new0(struct _openslide_jpeg_file); jpeg->filename = filename; jpeg->start_in_file = offset; jpeg->end_in_file = jpeg->start_in_file + length; jpeg->tw = l->raw_tile_width; jpeg->th = l->raw_tile_height; jpeg->w = l->raw_tile_width; jpeg->h = l->raw_tile_height; *jpegs_list = g_list_prepend(*jpegs_list, jpeg); // see comments elsewhere in this file const int tile_concat = 1 << zoom_level; const int tile_count_divisor = MIN(tile_concat, image_divisions); const int subtiles_per_jpeg_tile = MAX(1, tile_concat / image_divisions); const double subtile_w = (double) jpeg->w / subtiles_per_jpeg_tile; const double subtile_h = (double) jpeg->h / subtiles_per_jpeg_tile; const int tile0_w = layers[0]->raw_tile_width; const int tile0_h = layers[0]->raw_tile_height; // subtile_count: how many subtiles in a JPEG tile (one dimension)? this is constant // for the first few levels, depending on image_divisions const int subtile_count = MAX(1, tile_concat / image_divisions); /* g_debug("tile_concat: %d, subtile_count: %d", tile_concat, subtile_count); g_debug("found %d %d from file", x, y); */ // start processing 1 JPEG tile into subtile_count^2 subtiles for (int yi = 0; yi < subtile_count; yi++) { int yy = y + (yi * image_divisions); if (yy >= tiles_down) { break; } for (int xi = 0; xi < subtile_count; xi++) { int xx = x + (xi * image_divisions); if (xx >= tiles_across) { break; } // xx and yy are the tile coordinates in level0 space // look up the tile position, stored in 24.8 fixed point int xp = xx / image_divisions; int yp = yy / image_divisions; int tp = yp * (tiles_across / image_divisions) + xp; //g_debug("xx %d, yy %d, xp %d, yp %d, tp %d, spp %d, sc %d, tile0: %d %d subtile: %g %g", xx, yy, xp, yp, tp, subtiles_per_position, subtile_count, tile0_w, tile0_h, subtile_w, subtile_h); if (zoom_level == 0) { // if the zoom level is 0, then mark this position as active int *key = g_new(int, 1); *key = tp; g_hash_table_insert(active_positions, key, NULL); } else { // make sure we have an active position for this tile if (!g_hash_table_lookup_extended(active_positions, &tp, NULL, NULL)) { continue; } } // position in layer 0 const double pos0_x = ((double) tile_positions[tp * 2]) / 256.0 + tile0_w * (xx - xp * image_divisions); const double pos0_y = ((double) tile_positions[(tp * 2) + 1]) / 256.0 + tile0_h * (yy - yp * image_divisions); // position in this layer const double pos_x = pos0_x / tile_concat; const double pos_y = pos0_y / tile_concat; //g_debug("pos0: %g %g, pos: %g %g", pos0_x, pos0_y, pos_x, pos_y); insert_subtile(l->tiles, jpeg_number, pos_x, pos_y, subtile_w * xi, subtile_h * yi, subtile_w, subtile_h, l->tile_advance_x, l->tile_advance_y, x / tile_count_divisor + xi, y / tile_count_divisor + yi, tiles_across / tile_count_divisor, zoom_level); } } jpeg_number++; } } while (next_ptr != 0);
static bool hamamatsu_vmu_part2(openslide_t *osr, int num_files, char **image_filenames, GError **err) { bool success = false; // initialize individual ngr structs struct _openslide_ngr **files = g_new0(struct _openslide_ngr *, num_files); for (int i = 0; i < num_files; i++) { files[i] = g_slice_new0(struct _openslide_ngr); } // open files for (int i = 0; i < num_files; i++) { struct _openslide_ngr *ngr = files[i]; ngr->filename = g_strdup(image_filenames[i]); FILE *f; if ((f = _openslide_fopen(ngr->filename, "rb", err)) == NULL) { goto DONE; } // validate magic if ((fgetc(f) != 'G') || (fgetc(f) != 'N')) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA, "Bad magic on NGR file"); fclose(f); goto DONE; } // read w, h, column width, headersize fseeko(f, 4, SEEK_SET); ngr->w = read_le_int32_from_file(f); ngr->h = read_le_int32_from_file(f); ngr->column_width = read_le_int32_from_file(f); fseeko(f, 24, SEEK_SET); ngr->start_in_file = read_le_int32_from_file(f); // validate if ((ngr->w <= 0) || (ngr->h <= 0) || (ngr->column_width <= 0) || (ngr->start_in_file <= 0)) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA, "Error processing header"); fclose(f); goto DONE; } // ensure no remainder on columns if ((ngr->w % ngr->column_width) != 0) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA, "Width not multiple of column width"); fclose(f); goto DONE; } fclose(f); } success = true; DONE: if (success) { _openslide_add_ngr_ops(osr, num_files, files); } else { // destroy for (int i = 0; i < num_files; i++) { g_slice_free(struct _openslide_ngr, files[i]); g_free(files[i]->filename); } g_free(files); } return success; }
static bool read_nonhier_record(FILE *f, int64_t nonhier_root_position, int recordno, int *fileno, int64_t *size, int64_t *position) { if (recordno == -1) return false; if (fseeko(f, nonhier_root_position, SEEK_SET) == -1) { g_warning("Cannot seek to nonhier root"); return false; } int32_t ptr = read_le_int32_from_file(f); if (ptr == -1) { g_warning("Can't read initial nonhier pointer"); return false; } // jump to start of interesting data // g_debug("seek %d", ptr); if (fseeko(f, ptr, SEEK_SET) == -1) { g_warning("Cannot seek to start of nonhier data"); return false; } // seek to record pointer if (fseeko(f, recordno * 4, SEEK_CUR) == -1) { g_warning("Cannot seek to nonhier record pointer %d", recordno); return false; } // read pointer ptr = read_le_int32_from_file(f); if (ptr == -1) { g_warning("Can't read nonhier record %d", recordno); return false; } // seek if (fseeko(f, ptr, SEEK_SET) == -1) { g_warning("Cannot seek to nonhier record %d", recordno); return false; } // read initial 0 if (read_le_int32_from_file(f) != 0) { g_warning("Expected 0 value at beginning of data page"); return false; } // read pointer ptr = read_le_int32_from_file(f); if (ptr == -1) { g_warning("Can't read initial data page pointer"); return false; } // seek to offset if (fseeko(f, ptr, SEEK_SET) == -1) { g_warning("Can't seek to initial data page"); return false; } // read pagesize == 1 if (read_le_int32_from_file(f) != 1) { g_warning("Expected 1 value"); return false; } // read 3 zeroes if (read_le_int32_from_file(f) != 0) { g_warning("Expected first 0 value"); return false; } if (read_le_int32_from_file(f) != 0) { g_warning("Expected second 0 value"); return false; } if (read_le_int32_from_file(f) != 0) { g_warning("Expected third 0 value"); return false; } // finally read offset, size, fileno *position = read_le_int32_from_file(f); if (*position == -1) { g_warning("Can't read position"); return false; } *size = read_le_int32_from_file(f); if (*size == -1) { g_warning("Can't read size"); return false; } *fileno = read_le_int32_from_file(f); if (*fileno == -1) { g_warning("Can't read fileno"); return false; } return true; }