bool _openslide_jpeg_read_dimensions(const char *filename, int64_t offset, int32_t *w, int32_t *h, GError **err) { FILE *f = _openslide_fopen(filename, "rb", err); if (f == NULL) { return false; } if (offset && fseeko(f, offset, SEEK_SET) == -1) { _openslide_io_error(err, "Cannot seek to offset"); fclose(f); return false; } bool success = jpeg_get_dimensions(f, NULL, 0, w, h, err); fclose(f); return success; }
bool _openslide_jpeg_read(const char *filename, int64_t offset, uint32_t *dest, int32_t w, int32_t h, GError **err) { //g_debug("read JPEG: %s %" G_GINT64_FORMAT, filename, offset); FILE *f = _openslide_fopen(filename, "rb", err); if (f == NULL) { return false; } if (offset && fseeko(f, offset, SEEK_SET) == -1) { _openslide_io_error(err, "Cannot seek to offset"); fclose(f); return false; } bool success = jpeg_decode(f, NULL, 0, dest, false, w, h, err); fclose(f); return success; }
bool _openslide_gdkpixbuf_read(const char *format, const char *filename, int64_t offset, int64_t length, uint32_t *dest, int32_t w, int32_t h, GError **err) { GdkPixbufLoader *loader = NULL; uint8_t *buf = g_slice_alloc(BUFSIZE); bool success = false; struct load_state state = { .w = w, .h = h, }; // open and seek FILE *f = _openslide_fopen(filename, "rb", err); if (!f) { goto DONE; } if (fseeko(f, offset, SEEK_SET)) { _openslide_io_error(err, "Couldn't fseek %s", filename); goto DONE; } // create loader loader = gdk_pixbuf_loader_new_with_type(format, err); if (!loader) { goto DONE; } g_signal_connect(loader, "area-prepared", G_CALLBACK(area_prepared), &state); // read data while (length) { size_t count = fread(buf, 1, MIN(length, BUFSIZE), f); if (!count) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_FAILED, "Short read loading pixbuf from %s", filename); goto DONE; } if (!gdk_pixbuf_loader_write(loader, buf, count, err)) { g_prefix_error(err, "gdk-pixbuf error: "); goto DONE; } if (state.err) { goto DONE; } length -= count; } // finish load if (!gdk_pixbuf_loader_close(loader, err)) { g_prefix_error(err, "gdk-pixbuf error: "); goto DONE; } if (state.err) { goto DONE; } g_assert(state.pixbuf); // copy pixels uint8_t *pixels = gdk_pixbuf_get_pixels(state.pixbuf); int rowstride = gdk_pixbuf_get_rowstride(state.pixbuf); for (int32_t y = 0; y < h; y++) { for (int32_t x = 0; x < w; x++) { dest[y * w + x] = 0xFF000000 | // A pixels[y * rowstride + x * 3 + 0] << 16 | // R pixels[y * rowstride + x * 3 + 1] << 8 | // G pixels[y * rowstride + x * 3 + 2]; // B } } success = true; DONE: // clean up if (loader) { gdk_pixbuf_loader_close(loader, NULL); g_object_unref(loader); } if (f) { fclose(f); } g_slice_free1(BUFSIZE, buf); // now that the loader is closed, we know state.err won't be set // behind our back if (state.err) { // signal handler validation errors override GdkPixbuf errors g_clear_error(err); g_propagate_error(err, state.err); // signal handler errors should have been noticed before falling through g_assert(!success); } return success; }
static bool hamamatsu_ndpi_part2(openslide_t *osr, int num_jpegs, char *image_filenames, int num_jpeg_cols, GKeyFile *key_file, GError **err) { bool success = false; // initialize individual jpeg structs struct _openslide_jpeg_file **jpegs = g_new0(struct _openslide_jpeg_file *, num_jpegs); for (int i = 0; i < num_jpegs; i++) { jpegs[i] = g_slice_new0(struct _openslide_jpeg_file); } // init levels: base image + map int32_t level_count = 2; struct _openslide_jpeg_level **levels = g_new0(struct _openslide_jpeg_level *, level_count); for (int32_t i = 0; i < level_count; i++) { levels[i] = g_slice_new0(struct _openslide_jpeg_level); levels[i]->tiles = _openslide_jpeg_create_tiles_table(); } // process jpegs int32_t jpeg0_tw = 0; int32_t jpeg0_th = 0; int32_t jpeg0_ta = 0; int32_t jpeg0_td = 0; for (int i = 0; i < num_jpegs; i++) { struct _openslide_jpeg_file *jp = jpegs[i]; //jp->start_in_file = 0; jp->filename = g_strdup(image_filenames); FILE *f; if ((f = _openslide_fopen(jp->filename, "rb", err)) == NULL) { g_prefix_error(err, "Can't open JPEG %d: ", i); goto DONE; } // set start and end of jpeg file // from key_file jp->start_in_file = g_key_file_get_integer(key_file, GROUP_VMS, g_strdup_printf("hamamatsu.JpegStartOffset[%i]",i), err); int jpeg_size = g_key_file_get_integer(key_file, GROUP_VMS, g_strdup_printf("hamamatsu.JpegSize[%i]",i), err); if (fseeko(f, jp->start_in_file, SEEK_SET) != 0) { _openslide_io_error(err, "Cannot seek to offset"); goto DONE; } // comment? char *comment = NULL; char **comment_ptr = NULL; if (i == 0 && osr) { comment_ptr = &comment; } if (!verify_jpeg(f, &jp->w, &jp->h, &jp->tw, &jp->th, comment_ptr, err)) { g_prefix_error(err, "Can't verify JPEG %d: ", i); fclose(f); goto DONE; } if (comment) { g_hash_table_insert(osr->properties, g_strdup(OPENSLIDE_PROPERTY_NAME_COMMENT), comment); } //seek to end of jpeg offset fseeko(f, jpeg_size, SEEK_CUR); jp->end_in_file = ftello(f); if (jp->end_in_file == -1) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA, "Can't read file size for JPEG %d", i); fclose(f); goto DONE; } // file is done now fclose(f); int32_t num_tiles_across = jp->w / jp->tw; int32_t num_tiles_down = jp->h / jp->th; //my sample NDPI file dont have map file. //if I dont modified this code, I get error //The overwriting error message was: Tile size not consistent //so, I am remove "else if" code // because map file is last, ensure that all tw and th are the // same for 0 through num_jpegs-2 // g_debug("tile size: %d %d", tw, th); //if (i == 0) { jpeg0_tw = jp->tw; jpeg0_th = jp->th; jpeg0_ta = num_tiles_across; jpeg0_td = num_tiles_down; /*} else if (i != (num_jpegs - 1)) { // not map file (still within level 0) g_assert(jpeg0_tw != 0 && jpeg0_th != 0); if (jpeg0_tw != jp->tw || jpeg0_th != jp->th) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA, "Tile size not consistent"); goto DONE; } }*/ //NDPI file dont have optimisation file input, so remove this code /* // use the optimisation file, if present int32_t mcu_starts_count = (jp->w / jp->tw) * (jp->h / jp->th); // number of tiles int64_t *mcu_starts = NULL; if (optimisation_file) { mcu_starts = extract_one_optimisation(optimisation_file, num_tiles_down, num_tiles_across, mcu_starts_count); } if (mcu_starts) { jp->mcu_starts = mcu_starts; } else if (optimisation_file != NULL) { // the optimisation file is useless, ignore it optimisation_file = NULL; } */ // accumulate into some of the fields of the levels int32_t level; if (i != num_jpegs - 1) { // base (level 0) level = 0; } else { // map (level 1) level = 1; } struct _openslide_jpeg_level *l = levels[level]; int32_t file_x = 0; int32_t file_y = 0; if (level == 0) { file_x = i % num_jpeg_cols; file_y = i / num_jpeg_cols; } if (file_y == 0) { l->level_w += jp->w; l->tiles_across += num_tiles_across; } if (file_x == 0) { l->level_h += jp->h; l->tiles_down += num_tiles_down; } // set some values (don't accumulate) l->raw_tile_width = jp->tw; l->raw_tile_height = jp->th; l->tile_advance_x = jp->tw; // no overlaps or funny business l->tile_advance_y = jp->th; } // at this point, jpeg0_ta and jpeg0_td are set to values from 0,0 in level 0 for (int i = 0; i < num_jpegs; i++) { struct _openslide_jpeg_file *jp = jpegs[i]; int32_t level; int32_t file_x; int32_t file_y; if (i != num_jpegs - 1) { // base (level 0) level = 0; file_x = i % num_jpeg_cols; file_y = i / num_jpeg_cols; } else { // map (level 1) level = 1; file_x = 0; file_y = 0; } //g_debug("processing file %d %d %d", file_x, file_y, level); int32_t num_tiles_across = jp->w / jp->tw; struct _openslide_jpeg_level *l = levels[level]; int32_t tile_count = (jp->w / jp->tw) * (jp->h / jp->th); // number of tiles // add all the tiles for (int local_tileno = 0; local_tileno < tile_count; local_tileno++) { struct _openslide_jpeg_tile *t = g_slice_new0(struct _openslide_jpeg_tile); int32_t local_tile_x = local_tileno % num_tiles_across; int32_t local_tile_y = local_tileno / num_tiles_across; t->fileno = i; t->tileno = local_tileno; t->w = jp->tw; t->h = jp->th; // no dest or src offsets // compute key for hashtable (y * w + x) int64_t x = file_x * jpeg0_ta + local_tile_x; int64_t y = file_y * jpeg0_td + local_tile_y; int64_t *key = g_slice_new(int64_t); *key = (y * l->tiles_across) + x; //g_debug("inserting tile: fileno %d tileno %d, %gx%g, file: %d %d, local: %d %d, global: %" G_GINT64_FORMAT " %" G_GINT64_FORMAT ", l->tiles_across: %d, key: %" G_GINT64_FORMAT, t->fileno, t->tileno, t->w, t->h, file_x, file_y, local_tile_x, local_tile_y, x, y, l->tiles_across, *key); g_assert(!g_hash_table_lookup(l->tiles, key)); g_hash_table_insert(l->tiles, key, t); } } success = true; DONE: if (success) { _openslide_add_jpeg_ops(osr, num_jpegs, jpegs, level_count, levels); } else { // destroy for (int i = 0; i < num_jpegs; i++) { g_free(jpegs[i]->filename); g_free(jpegs[i]->mcu_starts); g_slice_free(struct _openslide_jpeg_file, jpegs[i]); } g_free(jpegs); for (int32_t i = 0; i < level_count; i++) { g_hash_table_unref(levels[i]->tiles); g_slice_free(struct _openslide_jpeg_level, levels[i]); } g_free(levels); } return success; }
bool _openslide_try_hamamatsu(openslide_t *osr, const char *filename, struct _openslide_hash *quickhash1, GError **err) { // initialize any variables destroyed/used in DONE bool success = false; char *dirname = g_path_get_dirname(filename); int num_images = 0; char **image_filenames = NULL; int num_cols = -1; int num_rows = -1; char **all_keys = NULL; int num_layers = -1; // first, see if it's a VMS/VMU file GKeyFile *key_file = g_key_file_new(); if (!_openslide_read_key_file(key_file, filename, G_KEY_FILE_NONE, NULL)) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_FORMAT_NOT_SUPPORTED, "Can't load key file"); goto DONE; } // select group or fail, then read dimensions const char *groupname; if (g_key_file_has_group(key_file, GROUP_VMS)) { groupname = GROUP_VMS; num_cols = g_key_file_get_integer(key_file, groupname, KEY_NUM_JPEG_COLS, NULL); num_rows = g_key_file_get_integer(key_file, groupname, KEY_NUM_JPEG_ROWS, NULL); } else if (g_key_file_has_group(key_file, GROUP_VMU)) { groupname = GROUP_VMU; num_cols = 1; // not specified in file for VMU num_rows = 1; } else { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_FORMAT_NOT_SUPPORTED, "Not VMS or VMU file"); goto DONE; } // validate cols/rows if (num_cols < 1) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_FORMAT_NOT_SUPPORTED, "File has no columns"); goto DONE; } if (num_rows < 1) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_FORMAT_NOT_SUPPORTED, "File has no rows"); goto DONE; } // init the image filenames // this format has cols*rows image files, plus the map num_images = (num_cols * num_rows) + 1; image_filenames = g_new0(char *, num_images); // hash in the key file if (!_openslide_hash_file(quickhash1, filename, err)) { goto DONE; } // make sure values are within known bounds num_layers = g_key_file_get_integer(key_file, groupname, KEY_NUM_LAYERS, NULL); if (num_layers < 1) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA, "Cannot handle Hamamatsu files with NoLayers < 1"); goto DONE; } // add properties if (osr) { add_properties(osr->properties, key_file, groupname); } // extract MapFile char *tmp; tmp = g_key_file_get_string(key_file, groupname, KEY_MAP_FILE, NULL); if (tmp) { char *map_filename = g_build_filename(dirname, tmp, NULL); g_free(tmp); image_filenames[num_images - 1] = map_filename; // hash in the map file if (!_openslide_hash_file(quickhash1, map_filename, err)) { goto DONE; } } else { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA, "Can't read map file"); goto DONE; } // now each ImageFile all_keys = g_key_file_get_keys(key_file, groupname, NULL, NULL); for (char **tmp = all_keys; *tmp != NULL; tmp++) { char *key = *tmp; char *value = g_key_file_get_string(key_file, groupname, key, NULL); // g_debug("%s", key); if (strncmp(KEY_IMAGE_FILE, key, strlen(KEY_IMAGE_FILE)) == 0) { // starts with ImageFile char *suffix = key + strlen(KEY_IMAGE_FILE); int layer; int col; int row; char **split = g_strsplit(suffix, ",", 0); switch (g_strv_length(split)) { case 0: // all zero layer = 0; col = 0; row = 0; break; case 1: // (z) // first item, skip '(' layer = g_ascii_strtoll(split[0] + 1, NULL, 10); col = 0; row = 0; break; case 2: // (x,y) layer = 0; // first item, skip '(' col = g_ascii_strtoll(split[0] + 1, NULL, 10); row = g_ascii_strtoll(split[1], NULL, 10); break; case 3: // (z,x,y) // first item, skip '(' layer = g_ascii_strtoll(split[0] + 1, NULL, 10); col = g_ascii_strtoll(split[1], NULL, 10); row = g_ascii_strtoll(split[2], NULL, 10); break; default: // we just don't know g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA, "Unknown number of image dimensions: %d", g_strv_length(split)); g_free(value); g_strfreev(split); g_strfreev(all_keys); goto DONE; } g_strfreev(split); //g_debug("layer: %d, col: %d, row: %d", layer, col, row); if (layer != 0) { // skip non-zero layers for now g_free(value); continue; } if (col >= num_cols || row >= num_rows || col < 0 || row < 0) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA, "Invalid row or column in Hamamatsu file (%d,%d)", col, row); g_free(value); g_strfreev(all_keys); goto DONE; } // compute index from x,y int i = row * num_cols + col; // init the file if (image_filenames[i]) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA, "Duplicate image for (%d,%d)", col, row); g_free(value); g_strfreev(all_keys); goto DONE; } image_filenames[i] = g_build_filename(dirname, value, NULL); } g_free(value); } g_strfreev(all_keys); // ensure all image filenames are filled for (int i = 0; i < num_images; i++) { if (!image_filenames[i]) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA, "Can't read image filename %d", i); goto DONE; } } // add macro image tmp = g_key_file_get_string(key_file, groupname, KEY_MACRO_IMAGE, NULL); if (tmp) { char *macro_filename = g_build_filename(dirname, tmp, NULL); bool result = _openslide_add_jpeg_associated_image(osr ? osr->associated_images : NULL, "macro", macro_filename, 0, err); g_free(macro_filename); g_free(tmp); if (!result) { g_prefix_error(err, "Could not read macro image: "); goto DONE; } } // finalize depending on what format if (groupname == GROUP_VMS) { // open OptimisationFile FILE *optimisation_file = NULL; char *tmp = g_key_file_get_string(key_file, GROUP_VMS, KEY_OPTIMISATION_FILE, NULL); if (tmp) { char *optimisation_filename = g_build_filename(dirname, tmp, NULL); g_free(tmp); optimisation_file = _openslide_fopen(optimisation_filename, "rb", NULL); if (optimisation_file == NULL) { // g_debug("Can't open optimisation file"); } g_free(optimisation_filename); } else { // g_debug("Optimisation file key not present"); } // do all the jpeg stuff success = hamamatsu_vms_part2(osr, num_images, image_filenames, num_cols, optimisation_file, err); // clean up if (optimisation_file) { fclose(optimisation_file); } } else if (groupname == GROUP_VMU) { // verify a few assumptions for VMU int bits_per_pixel = g_key_file_get_integer(key_file, GROUP_VMU, KEY_BITS_PER_PIXEL, NULL); char *pixel_order = g_key_file_get_string(key_file, GROUP_VMU, KEY_PIXEL_ORDER, NULL); if (bits_per_pixel != 36) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA, "%s must be 36", KEY_BITS_PER_PIXEL); } else if (!pixel_order || (strcmp(pixel_order, "RGB") != 0)) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA, "%s must be RGB", KEY_PIXEL_ORDER); } else { // assumptions verified success = hamamatsu_vmu_part2(osr, num_images, image_filenames, err); } g_free(pixel_order); } else { g_assert_not_reached(); } DONE: g_free(dirname); if (image_filenames) { for (int i = 0; i < num_images; i++) { g_free(image_filenames[i]); } g_free(image_filenames); } g_key_file_free(key_file); return success; }
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; }
bool _openslide_try_hamamatsu_ndpi(openslide_t *osr, const char *filename, struct _openslide_hash *quickhash1, GError **err) { bool success = false; char *image_filenames = NULL; char *dirname = g_path_get_dirname(filename); image_filenames = g_new0(char, 1); image_filenames = g_build_filename(dirname, filename, NULL); FILE *f = _openslide_fopen(image_filenames, "rb", NULL); if (!f) { g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_FORMAT_NOT_SUPPORTED, "Can't open file"); return false; } //dump data from NDPI file. from this function, we get all information about //NDPI file (number of image, size of image, offset, etc) GSList *dump = _openslide_tiffdump_create(f, err); if (!dump) { fclose(f); return false; } fclose(f); const char *groupname = GROUP_VMS; GKeyFile *key_file = g_key_file_new(); //NDPI file dont have key file. So, I am create the key using this method //but, the problem is I can not show the property using : //openslide_get_property_names(osr) //are you know why? g_key_file_set_string(key_file, groupname, "ImageFile", image_filenames); //I dont get information about number of layers from NDPI file. //So, I set this num_layers to 1 int num_layers = 1; g_key_file_set_integer(key_file, groupname, "NoLayers", num_layers); //create NDPI key from from dump variable //create key_file from this GSList. //We will get some of image from NDPI file with jpeg format //I save the count number of all jpeg image in "num_images" variable //some of the jpeg file dont have complete header (dont have restart marker offset) //"num_jpegs" is total number of jpeg with complete header int num_images; //the total number of jpegs images int num_jpegs; //the total number of jpegs with complete header //I am still confused how to create index name from extracted //NDPI file. So, I am create a file with symbols [%]. //please check this function ndpi_create_key_file(dump, key_file, groupname, &num_images, &num_jpegs); //I dont get information about num_cols and num_rows //So, I set this "num_cols" variable like this int num_cols; if(num_jpegs>1) num_cols = 2; else num_cols = 1; // add properties if (osr) { add_properties(osr->properties, key_file, groupname); } //I am not yet add the macros file //we can get the macros file from key_file success = hamamatsu_ndpi_part2(osr, num_jpegs, image_filenames, num_cols, key_file, err); _openslide_tiffdump_destroy(dump); g_key_file_free(key_file); return success; }