From c62ec8be598fcf17ef621de0a687f6d18410965c Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Mon, 17 Apr 2023 20:15:55 +0200 Subject: [PATCH 01/38] Added first version for this isyntax_to_tiff --- CMakeLists.txt | 21 ++++ src/isyntax_to_tiff.c | 272 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 293 insertions(+) create mode 100644 src/isyntax_to_tiff.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 28d8e95..daa5f90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,11 @@ include_directories("${CMAKE_SOURCE_DIR}/src/platform") include_directories("${CMAKE_SOURCE_DIR}/src/utils") include_directories("${CMAKE_SOURCE_DIR}/src/isyntax") include_directories("${CMAKE_SOURCE_DIR}/src/third_party") +include_directories(${VIPS_INCLUDE_DIRS}) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(VIPS REQUIRED vips) + set(LIBISYNTAX_COMMON_SOURCE_FILES src/libisyntax.c @@ -29,6 +34,7 @@ set(LIBISYNTAX_COMMON_SOURCE_FILES src/third_party/ltalloc.cc ) + if (WIN32) set(LIBISYNTAX_COMMON_SOURCE_FILES ${LIBISYNTAX_COMMON_SOURCE_FILES} src/platform/win32_utils.c) else() @@ -43,10 +49,25 @@ add_executable(isyntax_example src/isyntax_example.c ${LIBISYNTAX_COMMON_SOURCE_FILES} ) +message(STATUS "VIPS_INCLUDE_DIRS: ${VIPS_INCLUDE_DIRS}") +message(STATUS "VIPS_LIBRARY_DIRS: ${VIPS_LIBRARY_DIRS}") +message(STATUS "VIPS_LIBRARIES: ${VIPS_LIBRARIES}") + +link_directories(${VIPS_LIBRARY_DIRS}) +add_executable(isyntax-to-tiff + src/isyntax_to_tiff.c + ${LIBISYNTAX_COMMON_SOURCE_FILES} + ) + +target_include_directories(isyntax-to-tiff PRIVATE ${VIPS_INCLUDE_DIRS}) +target_link_libraries(isyntax-to-tiff PRIVATE ${VIPS_LIBRARIES}) + if (WIN32) target_link_libraries(libisyntax winmm) target_link_libraries(isyntax_example winmm) + target_link_libraries(isyntax-to-tiff winmm) + else() endif() diff --git a/src/isyntax_to_tiff.c b/src/isyntax_to_tiff.c new file mode 100644 index 0000000..4d29544 --- /dev/null +++ b/src/isyntax_to_tiff.c @@ -0,0 +1,272 @@ +#include "libisyntax.h" + +#include +#include +#include +#include +#include +#include + +#define LOG_VAR(fmt, var) printf("%s: %s=" fmt "\n", __FUNCTION__, #var, var) + + +#include +#if defined(__ARM_NEON) +#include +#elif defined(__SSE2__) +#include +#endif + +void bgra_to_rgba(uint32_t* pixels, int tile_width, int tile_height) { + int num_pixels = tile_width * tile_height; + +#if defined(__ARM_NEON) + for (int i = 0; i < num_pixels; i += 4) { + uint32x4_t bgra = vld1q_u32(pixels + i); + uint32x4_t b_mask = vdupq_n_u32(0x000000FF); + uint32x4_t r_mask = vdupq_n_u32(0x00FF0000); + uint32x4_t b = vandq_u32(bgra, b_mask); + uint32x4_t r = vandq_u32(bgra, r_mask); + uint32x4_t br_swapped = vorrq_u32(vshlq_n_u32(b, 16), vshrq_n_u32(r, 16)); + uint32x4_t ga_alpha_mask = vdupq_n_u32(0xFF00FF00); + uint32x4_t ga_alpha = vandq_u32(bgra, ga_alpha_mask); + uint32x4_t rgba = vorrq_u32(ga_alpha, br_swapped); + vst1q_u32(pixels + i, rgba); + } +#elif defined(__SSE2__) + for (int i = 0; i < num_pixels; i += 4) { + __m128i bgra = _mm_loadu_si128((__m128i*)(pixels + i)); + __m128i b_mask = _mm_set1_epi32(0x000000FF); + __m128i r_mask = _mm_set1_epi32(0x00FF0000); + __m128i b = _mm_and_si128(bgra, b_mask); + __m128i r = _mm_and_si128(bgra, r_mask); + __m128i br_swapped = _mm_or_si128(_mm_slli_epi32(b, 16), _mm_srli_epi32(r, 16)); + __m128i ga_alpha_mask = _mm_set1_epi32(0xFF00FF00); + __m128i ga_alpha = _mm_and_si128(bgra, ga_alpha_mask); + __m128i rgba = _mm_or_si128(ga_alpha, br_swapped); + _mm_storeu_si128((__m128i*)(pixels + i), rgba); + } +#else + for (int i = 0; i < num_pixels; ++i) { + uint32_t val = pixels[i]; + pixels[i] = ((val & 0xff) << 16) | (val & 0x00ff00) | ((val & 0xff0000) >> 16) | (val & 0xff000000); + } +#endif +} + + + +typedef struct _VipsForeignLoadIsyntax { + VipsForeignLoad parent_object; + isyntax_t *isyntax; + isyntax_cache_t *isyntax_cache; + const isyntax_image_t *wsi_image; + const isyntax_level_t *level; + int32_t tile_width; + int32_t tile_height; + int32_t num_tiles_x; + int32_t num_tiles_y; +} VipsForeignLoadIsyntax; + +typedef VipsForeignLoadClass VipsForeignLoadIsyntaxClass; + +G_DEFINE_TYPE(VipsForeignLoadIsyntax, vips_foreign_load_isyntax, VIPS_TYPE_FOREIGN_LOAD); + + +static int +isyntax_generate(VipsRegion *out, void *seq, void *a, void *b, gboolean *stop) { + VipsForeignLoadIsyntax *isyntax = (VipsForeignLoadIsyntax *) a; + VipsRect *r = &out->valid; + + int32_t tile_width = isyntax->tile_width; + int32_t tile_height = isyntax->tile_height; + + int32_t level = 0; + uint32_t *pixels = NULL; + + // Calculate the tile range for the region + int32_t tile_start_x = r->left / tile_width; + int32_t tile_end_x = (r->left + r->width + tile_width - 1) / tile_width; + int32_t tile_start_y = r->top / tile_height; + int32_t tile_end_y = (r->top + r->height + tile_height - 1) / tile_height; + + for (int32_t tile_y = tile_start_y; tile_y < tile_end_y; ++tile_y) { + for (int32_t tile_x = tile_start_x; tile_x < tile_end_x; ++tile_x) { + assert(libisyntax_tile_read(isyntax->isyntax, isyntax->isyntax_cache, level, tile_x, tile_y, &pixels) == LIBISYNTAX_OK); + bgra_to_rgba(pixels, tile_width, tile_height); + + // Calculate the intersection of the region and the tile + VipsRect tile_rect = { + .left = tile_x * tile_width, + .top = tile_y * tile_height, + .width = tile_width, + .height = tile_height + }; + VipsRect intersection; + vips_rect_intersectrect(r, &tile_rect, &intersection); + + // Copy the intersection area from the tile to the output region + for (int y = intersection.top; y < VIPS_RECT_BOTTOM(&intersection); y++) { + uint32_t *p = (uint32_t *) VIPS_REGION_ADDR(out, intersection.left, y); + uint32_t *q = pixels + (y - tile_rect.top) * tile_width + (intersection.left - tile_rect.left); + + memcpy(p, q, intersection.width * sizeof(uint32_t)); + } + } + } + + // Free the pixels buffer if needed + if (pixels) { + free(pixels); + /* Free the pixels buffer */ + } + + return (0); +} + +/* Header function for VipsForeignLoadIsyntax */ +static int +vips_foreign_load_isyntax_header(VipsForeignLoad *load) { + VipsForeignLoadIsyntax *isyntax = (VipsForeignLoadIsyntax *)load; + + // Set the image properties using the appropriate libisyntax functions + int width = /* Call libisyntax function to get width */; + int height = /* Call libisyntax function to get height */; + int bands = /* Call libisyntax function to get bands */; + + vips_image_init_fields(load->out, + width, + height, + bands, + VIPS_FORMAT_UCHAR, // Adjust this according to your image format + VIPS_CODING_NONE, + VIPS_INTERPRETATION_sRGB, // Adjust this according to your image interpretation + 1.0, 1.0); + + return 0; +} + + +/* Class init function for VipsForeignLoadIsyntax */ +static void +vips_foreign_load_isyntax_class_init(VipsForeignLoadIsyntaxClass *klass) { + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + VipsObjectClass *object_class = VIPS_OBJECT_CLASS(klass); + VipsForeignLoadClass *foreign_load_class = (VipsForeignLoadClass *)klass; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "isyntaxload"; + object_class->description = _("Load an isyntax image"); + + /* Add your properties here using vips_object_class_install_property() */ + + foreign_load_class->header = vips_foreign_load_isyntax_header; + foreign_load_class->load = vips_foreign_load_isyntax_load; +} + +/* Register the VipsForeignLoadIsyntax class with libvips */ +static void +vips_foreign_load_isyntax_register(void) { + static GOnce once = G_ONCE_INIT; + + if (g_once_init_enter(&once)) { + GType type = g_type_register_static_simple( + VIPS_TYPE_FOREIGN_LOAD, + g_intern_static_string("VipsForeignLoadIsyntax"), + sizeof(VipsForeignLoadIsyntaxClass), + (GClassInitFunc)vips_foreign_load_isyntax_class_init, + sizeof(VipsForeignLoadIsyntax), + (GInstanceInitFunc)vips_foreign_load_isyntax_init, + 0); + + g_once_init_leave(&once, type); + } +} + +static void +vips_foreign_load_isyntax_init(VipsForeignLoadIsyntax *isyntax) { + /* Initialize other members */ + + isyntax->tile_width = libisyntax_get_tile_width(isyntax->isyntax); + isyntax->tile_height = libisyntax_get_tile_height(isyntax->isyntax); +} + + + +int main(int argc, char** argv) { + if (VIPS_INIT(argv[0])) { + vips_error_exit("Failed to initialize vips"); + } + + if (argc <= 1) { + printf("Usage: %s - convert an isyntax image to a tiff.", argv[0]); + return 0; + } + + char* filename = argv[1]; + char* output_file = argv[2]; + + libisyntax_init(); + + isyntax_t* isyntax; + if (libisyntax_open(filename, /*is_init_allocators=*/0, &isyntax) != LIBISYNTAX_OK) { + fprintf(stderr, "Failed to open %s\n", filename); + return -1; + } + printf("Successfully opened %s\n", filename); + + isyntax_cache_t *isyntax_cache = NULL; + assert(libisyntax_cache_create("tiff conversion cache", 200000, &isyntax_cache) == LIBISYNTAX_OK); + assert(libisyntax_cache_inject(isyntax_cache, isyntax) == LIBISYNTAX_OK); + + int wsi_image_idx = libisyntax_get_wsi_image_index(isyntax); + const isyntax_image_t* wsi_image = libisyntax_get_image(isyntax, wsi_image_idx); + + const isyntax_level_t *base_level = libisyntax_image_get_level(wsi_image, 0); + const int32_t tile_height = libisyntax_get_tile_height(isyntax); + const int32_t tile_width = libisyntax_get_tile_width(isyntax); + const int32_t num_tiles_height = libisyntax_level_get_height_in_tiles(base_level); + const int32_t num_tiles_width = libisyntax_level_get_width_in_tiles(base_level); + // TODO: The actual width is smaller! Get this from the library. + + uint32_t *pixels = NULL; + + int32_t total_tiles = num_tiles_width * num_tiles_height; + int32_t processed_tiles = 0; + int prev_progress = -1; // Initialize to an invalid value + + for (int32_t tile_y = 0; tile_y < num_tiles_height; ++tile_y) { + for (int32_t tile_x = 0; tile_x < num_tiles_width; ++tile_x) { + assert(libisyntax_tile_read(isyntax, isyntax_cache, 0, tile_x, tile_y, &pixels) == LIBISYNTAX_OK); + bgra_to_rgba(pixels, tile_height, tile_width); + // Do something with the tile. + + // Update the progress counter + processed_tiles++; + + // Calculate the current progress as a percentage + int current_progress = (int)((float)processed_tiles / total_tiles * 100.0); + + // Print the progress to the console only when it changes + if (current_progress != prev_progress) { + printf("Progress: %d%%\r", current_progress); + fflush(stdout); // Flush the output buffer to ensure it gets printed immediately + + // Update the previous progress value + prev_progress = current_progress; + } + } + } + + + + + libisyntax_tile_free_pixels(pixels); + libisyntax_cache_destroy(isyntax_cache); + libisyntax_close(isyntax); + vips_shutdown(); + + return 0; +} From 0441a1e6a4aabd14374f7d6eae66cb7319390cbd Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Tue, 18 Apr 2023 10:50:40 +0200 Subject: [PATCH 02/38] Added NEON versions --- src/isyntax_example.c | 54 +++++++++++-- src/isyntax_to_tiff.c | 168 +++++++++++++++++++++++++++------------- src/platform/platform.c | 2 +- 3 files changed, 165 insertions(+), 59 deletions(-) diff --git a/src/isyntax_example.c b/src/isyntax_example.c index 5bba620..3515090 100644 --- a/src/isyntax_example.c +++ b/src/isyntax_example.c @@ -10,10 +10,55 @@ #define LOG_VAR(fmt, var) printf("%s: %s=" fmt "\n", __FUNCTION__, #var, var) -uint32_t bgra_to_rgba(uint32_t val) { - return ((val & 0xff) << 16) | (val & 0x00ff00) | ((val & 0xff0000) >> 16) | (val & 0xff000000); +//uint32_t bgra_to_rgba(uint32_t val) { +// return ((val & 0xff) << 16) | (val & 0x00ff00) | ((val & 0xff0000) >> 16) | (val & 0xff000000); +//} + +#include +#if defined(__ARM_NEON) +#include +#elif defined(__SSE2__) +#include +#endif + +void bgra_to_rgba(uint32_t* pixels, int tile_width, int tile_height) { + int num_pixels = tile_width * tile_height; + +#if defined(__ARM_NEON) + for (int i = 0; i < num_pixels; i += 4) { + uint32x4_t bgra = vld1q_u32(pixels + i); + uint32x4_t b_mask = vdupq_n_u32(0x000000FF); + uint32x4_t r_mask = vdupq_n_u32(0x00FF0000); + uint32x4_t b = vandq_u32(bgra, b_mask); + uint32x4_t r = vandq_u32(bgra, r_mask); + uint32x4_t br_swapped = vorrq_u32(vshlq_n_u32(b, 16), vshrq_n_u32(r, 16)); + uint32x4_t ga_alpha_mask = vdupq_n_u32(0xFF00FF00); + uint32x4_t ga_alpha = vandq_u32(bgra, ga_alpha_mask); + uint32x4_t rgba = vorrq_u32(ga_alpha, br_swapped); + vst1q_u32(pixels + i, rgba); + } +#elif defined(__SSE2__) + for (int i = 0; i < num_pixels; i += 4) { + __m128i bgra = _mm_loadu_si128((__m128i*)(pixels + i)); + __m128i b_mask = _mm_set1_epi32(0x000000FF); + __m128i r_mask = _mm_set1_epi32(0x00FF0000); + __m128i b = _mm_and_si128(bgra, b_mask); + __m128i r = _mm_and_si128(bgra, r_mask); + __m128i br_swapped = _mm_or_si128(_mm_slli_epi32(b, 16), _mm_srli_epi32(r, 16)); + __m128i ga_alpha_mask = _mm_set1_epi32(0xFF00FF00); + __m128i ga_alpha = _mm_and_si128(bgra, ga_alpha_mask); + __m128i rgba = _mm_or_si128(ga_alpha, br_swapped); + _mm_storeu_si128((__m128i*)(pixels + i), rgba); + } +#else + for (int i = 0; i < num_pixels; ++i) { + uint32_t val = pixels[i]; + pixels[i] = ((val & 0xff) << 16) | (val & 0x00ff00) | ((val & 0xff0000) >> 16) | (val & 0xff000000); + } +#endif } + void print_isyntax_levels(isyntax_t* isyntax) { int wsi_image_idx = libisyntax_get_wsi_image_index(isyntax); LOG_VAR("%d", wsi_image_idx); @@ -74,9 +119,8 @@ int main(int argc, char** argv) { assert(libisyntax_tile_read(isyntax, isyntax_cache, level, tile_x, tile_y, &pixels) == LIBISYNTAX_OK); // convert data to the correct pixel format (bgra->rgba). - for (int i = 0; i < tile_height * tile_width; ++i) { - pixels[i] = bgra_to_rgba(pixels[i]); - } + bgra_to_rgba(pixels, tile_height, tile_width); + printf("Writing %s...\n", output_png); stbi_write_png(output_png, tile_width, tile_height, 4, pixels, tile_width * 4); printf("Done writing %s.\n", output_png); diff --git a/src/isyntax_to_tiff.c b/src/isyntax_to_tiff.c index 4d29544..6e79573 100644 --- a/src/isyntax_to_tiff.c +++ b/src/isyntax_to_tiff.c @@ -73,11 +73,59 @@ typedef VipsForeignLoadClass VipsForeignLoadIsyntaxClass; G_DEFINE_TYPE(VipsForeignLoadIsyntax, vips_foreign_load_isyntax, VIPS_TYPE_FOREIGN_LOAD); -static int -isyntax_generate(VipsRegion *out, void *seq, void *a, void *b, gboolean *stop) { - VipsForeignLoadIsyntax *isyntax = (VipsForeignLoadIsyntax *) a; - VipsRect *r = &out->valid; +//static int +//isyntax_generate(VipsRegion *out, void *seq, void *a, void *b, gboolean *stop) { +// VipsForeignLoadIsyntax *isyntax = (VipsForeignLoadIsyntax *) a; +// VipsRect *r = &out->valid; +// +// int32_t tile_width = isyntax->tile_width; +// int32_t tile_height = isyntax->tile_height; +// +// int32_t level = 0; +// uint32_t *pixels = NULL; +// +// // Calculate the tile range for the region +// int32_t tile_start_x = r->left / tile_width; +// int32_t tile_end_x = (r->left + r->width + tile_width - 1) / tile_width; +// int32_t tile_start_y = r->top / tile_height; +// int32_t tile_end_y = (r->top + r->height + tile_height - 1) / tile_height; +// +// for (int32_t tile_y = tile_start_y; tile_y < tile_end_y; ++tile_y) { +// for (int32_t tile_x = tile_start_x; tile_x < tile_end_x; ++tile_x) { +// assert(libisyntax_tile_read(isyntax->isyntax, isyntax->isyntax_cache, level, tile_x, tile_y, &pixels) == LIBISYNTAX_OK); +// bgra_to_rgba(pixels, tile_width, tile_height); +// +// // Calculate the intersection of the region and the tile +// VipsRect tile_rect = { +// .left = tile_x * tile_width, +// .top = tile_y * tile_height, +// .width = tile_width, +// .height = tile_height +// }; +// VipsRect intersection; +// vips_rect_intersectrect(r, &tile_rect, &intersection); +// +// // Copy the intersection area from the tile to the output region +// for (int y = intersection.top; y < VIPS_RECT_BOTTOM(&intersection); y++) { +// uint32_t *p = (uint32_t *) VIPS_REGION_ADDR(out, intersection.left, y); +// uint32_t *q = pixels + (y - tile_rect.top) * tile_width + (intersection.left - tile_rect.left); +// +// memcpy(p, q, intersection.width * sizeof(uint32_t)); +// } +// } +// } +// +// // Free the pixels buffer if needed +// if (pixels) { +// free(pixels); +// /* Free the pixels buffer */ +// } +// +// return (0); +//} +static void +read_region(VipsForeignLoadIsyntax *isyntax, VipsRegion *out, const VipsRect *r) { int32_t tile_width = isyntax->tile_width; int32_t tile_height = isyntax->tile_height; @@ -118,21 +166,35 @@ isyntax_generate(VipsRegion *out, void *seq, void *a, void *b, gboolean *stop) { // Free the pixels buffer if needed if (pixels) { free(pixels); - /* Free the pixels buffer */ } +} + +static int +isyntax_generate(VipsRegion *out, void *seq, void *a, void *b, gboolean *stop) { + VipsForeignLoadIsyntax *isyntax = (VipsForeignLoadIsyntax *) a; + VipsRect *r = &out->valid; - return (0); + read_region(isyntax, out, r); + + return 0; } + /* Header function for VipsForeignLoadIsyntax */ static int vips_foreign_load_isyntax_header(VipsForeignLoad *load) { - VipsForeignLoadIsyntax *isyntax = (VipsForeignLoadIsyntax *)load; + VipsForeignLoadIsyntax *isyntax_load = (VipsForeignLoadIsyntax *)load; + isyntax_t *isyntax = isyntax_load->isyntax; + isyntax_cache_t *isyntax_cache = isyntax_load->isyntax_cache; + const isyntax_image_t *wsi_image = isyntax_load->wsi_image; + const isyntax_level_t *level = isyntax_load->level; + // Set the image properties using the appropriate libisyntax functions - int width = /* Call libisyntax function to get width */; - int height = /* Call libisyntax function to get height */; - int bands = /* Call libisyntax function to get bands */; + + int width = libisyntax_get_(level); + int height = 100; // libisyntax_level_get_height(level); + int bands = 4; // Assuming RGBA vips_image_init_fields(load->out, width, @@ -146,6 +208,27 @@ vips_foreign_load_isyntax_header(VipsForeignLoad *load) { return 0; } +/* Load function for VipsForeignLoadIsyntax */ +static int +vips_foreign_load_isyntax_load(VipsForeignLoad *load) { + VipsForeignLoadIsyntax *isyntax = (VipsForeignLoadIsyntax *)load; + + VipsImage **t = (VipsImage **)vips_object_local_array(VIPS_OBJECT(load), 1); + t[0] = vips_image_new(); + + // Call isyntax_generate function + if (vips_image_generate(t[0], + NULL, isyntax_generate, NULL, + isyntax, NULL)) { + return -1; + } + + if (vips_image_write(t[0], load->real)) { + return -1; + } + + return 0; +} /* Class init function for VipsForeignLoadIsyntax */ static void @@ -158,7 +241,7 @@ vips_foreign_load_isyntax_class_init(VipsForeignLoadIsyntaxClass *klass) { gobject_class->get_property = vips_object_get_property; object_class->nickname = "isyntaxload"; - object_class->description = _("Load an isyntax image"); + object_class->description = "Load an isyntax image"; /* Add your properties here using vips_object_class_install_property() */ @@ -169,19 +252,25 @@ vips_foreign_load_isyntax_class_init(VipsForeignLoadIsyntaxClass *klass) { /* Register the VipsForeignLoadIsyntax class with libvips */ static void vips_foreign_load_isyntax_register(void) { - static GOnce once = G_ONCE_INIT; - - if (g_once_init_enter(&once)) { - GType type = g_type_register_static_simple( - VIPS_TYPE_FOREIGN_LOAD, - g_intern_static_string("VipsForeignLoadIsyntax"), - sizeof(VipsForeignLoadIsyntaxClass), - (GClassInitFunc)vips_foreign_load_isyntax_class_init, - sizeof(VipsForeignLoadIsyntax), - (GInstanceInitFunc)vips_foreign_load_isyntax_init, - 0); - - g_once_init_leave(&once, type); + static gboolean registered = FALSE; + static GStaticMutex mutex = G_STATIC_MUTEX_INIT; + + if (!registered) { + g_static_mutex_lock(&mutex); + + if (!registered) { + GType type = g_type_register_static_simple( + VIPS_TYPE_FOREIGN_LOAD, + g_intern_static_string("VipsForeignLoadIsyntax"), + sizeof(VipsForeignLoadIsyntaxClass), + (GClassInitFunc)vips_foreign_load_isyntax_class_init, + sizeof(VipsForeignLoadIsyntax), + (GInstanceInitFunc)vips_foreign_load_isyntax_init, + 0); + registered = TRUE; + } + + g_static_mutex_unlock(&mutex); } } @@ -196,6 +285,8 @@ vips_foreign_load_isyntax_init(VipsForeignLoadIsyntax *isyntax) { int main(int argc, char** argv) { + vips_foreign_load_isyntax_register(); + if (VIPS_INIT(argv[0])) { vips_error_exit("Failed to initialize vips"); } @@ -233,35 +324,6 @@ int main(int argc, char** argv) { uint32_t *pixels = NULL; - int32_t total_tiles = num_tiles_width * num_tiles_height; - int32_t processed_tiles = 0; - int prev_progress = -1; // Initialize to an invalid value - - for (int32_t tile_y = 0; tile_y < num_tiles_height; ++tile_y) { - for (int32_t tile_x = 0; tile_x < num_tiles_width; ++tile_x) { - assert(libisyntax_tile_read(isyntax, isyntax_cache, 0, tile_x, tile_y, &pixels) == LIBISYNTAX_OK); - bgra_to_rgba(pixels, tile_height, tile_width); - // Do something with the tile. - - // Update the progress counter - processed_tiles++; - - // Calculate the current progress as a percentage - int current_progress = (int)((float)processed_tiles / total_tiles * 100.0); - - // Print the progress to the console only when it changes - if (current_progress != prev_progress) { - printf("Progress: %d%%\r", current_progress); - fflush(stdout); // Flush the output buffer to ensure it gets printed immediately - - // Update the previous progress value - prev_progress = current_progress; - } - } - } - - - libisyntax_tile_free_pixels(pixels); libisyntax_cache_destroy(isyntax_cache); diff --git a/src/platform/platform.c b/src/platform/platform.c index 5b78dea..0007958 100644 --- a/src/platform/platform.c +++ b/src/platform/platform.c @@ -104,7 +104,7 @@ void get_system_info(bool verbose) { sysctlbyname("hw.logicalcpu", &logical_cpu_count, &logical_cpu_count_len, NULL, 0); os_page_size = (u32) getpagesize(); page_alignment_mask = ~((u64)(sysconf(_SC_PAGE_SIZE) - 1)); - is_macos = true; + bool is_macos = true; #elif LINUX logical_cpu_count = sysconf( _SC_NPROCESSORS_ONLN ); physical_cpu_count = logical_cpu_count; // TODO: how to read this on Linux? From 9bd41a7c4a6d1a1cdbfda174f1924cee05008c17 Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Tue, 18 Apr 2023 14:40:01 +0200 Subject: [PATCH 03/38] Add read_region function --- CMakeLists.txt | 5 ++ src/isyntax_example2.c | 118 +++++++++++++++++++++++++++++++++++++++++ src/libisyntax.c | 47 ++++++++++++++++ src/libisyntax.h | 2 + 4 files changed, 172 insertions(+) create mode 100644 src/isyntax_example2.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 28d8e95..e0dbb22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,11 @@ add_executable(isyntax_example ${LIBISYNTAX_COMMON_SOURCE_FILES} ) +add_executable(isyntax_example2 + src/isyntax_example2.c + ${LIBISYNTAX_COMMON_SOURCE_FILES} + ) + if (WIN32) target_link_libraries(libisyntax winmm) target_link_libraries(isyntax_example winmm) diff --git a/src/isyntax_example2.c b/src/isyntax_example2.c new file mode 100644 index 0000000..e128bdc --- /dev/null +++ b/src/isyntax_example2.c @@ -0,0 +1,118 @@ +#include "libisyntax.h" + +#include +#include + +#define STB_IMAGE_WRITE_IMPLEMENTATION + +#include "third_party/stb_image_write.h" // for png export + +#if defined(__ARM_NEON) + +#include + +#elif defined(__SSE2__) +#include +#endif + + +#define LOG_VAR(fmt, var) printf("%s: %s=" fmt "\n", __FUNCTION__, #var, var) + +void bgra_to_rgba(uint32_t *pixels, int tile_width, int tile_height) { + int num_pixels = tile_width * tile_height; + +#if defined(__ARM_NEON) + for (int i = 0; i < num_pixels; i += 4) { + uint32x4_t bgra = vld1q_u32(pixels + i); + uint32x4_t b_mask = vdupq_n_u32(0x000000FF); + uint32x4_t r_mask = vdupq_n_u32(0x00FF0000); + uint32x4_t b = vandq_u32(bgra, b_mask); + uint32x4_t r = vandq_u32(bgra, r_mask); + uint32x4_t br_swapped = vorrq_u32(vshlq_n_u32(b, 16), vshrq_n_u32(r, 16)); + uint32x4_t ga_alpha_mask = vdupq_n_u32(0xFF00FF00); + uint32x4_t ga_alpha = vandq_u32(bgra, ga_alpha_mask); + uint32x4_t rgba = vorrq_u32(ga_alpha, br_swapped); + vst1q_u32(pixels + i, rgba); + } +#elif defined(__SSE2__) + for (int i = 0; i < num_pixels; i += 4) { + __m128i bgra = _mm_loadu_si128((__m128i*)(pixels + i)); + __m128i b_mask = _mm_set1_epi32(0x000000FF); + __m128i r_mask = _mm_set1_epi32(0x00FF0000); + __m128i b = _mm_and_si128(bgra, b_mask); + __m128i r = _mm_and_si128(bgra, r_mask); + __m128i br_swapped = _mm_or_si128(_mm_slli_epi32(b, 16), _mm_srli_epi32(r, 16)); + __m128i ga_alpha_mask = _mm_set1_epi32(0xFF00FF00); + __m128i ga_alpha = _mm_and_si128(bgra, ga_alpha_mask); + __m128i rgba = _mm_or_si128(ga_alpha, br_swapped); + _mm_storeu_si128((__m128i*)(pixels + i), rgba); + } +#else + for (int i = 0; i < num_pixels; ++i) { + uint32_t val = pixels[i]; + pixels[i] = ((val & 0xff) << 16) | (val & 0x00ff00) | ((val & 0xff0000) >> 16) | (val & 0xff000000); + } +#endif +} + +int main(int argc, char **argv) { + + if (argc <= 7) { + printf("Usage: %s - write a tile to output.png", + argv[0], argv[0]); + return 0; + } + + char *filename = argv[1]; + + libisyntax_init(); + + isyntax_t *isyntax; + if (libisyntax_open(filename, /*is_init_allocators=*/0, &isyntax) != LIBISYNTAX_OK) { + printf("Failed to open %s\n", filename); + return -1; + } + printf("Successfully opened %s\n", filename); + + + int32_t level = atoi(argv[2]); + int32_t tile_x = atoi(argv[3]); + int32_t tile_y = atoi(argv[4]); + int32_t region_width = atoi(argv[5]); + int32_t region_height = atoi(argv[6]); + const char *output_png = argv[7]; + + LOG_VAR("%d", level); + LOG_VAR("%d", tile_x); + LOG_VAR("%d", tile_y); + LOG_VAR("%d", region_width); + LOG_VAR("%d", region_height); + LOG_VAR("%s", output_png); + + int32_t tile_width = libisyntax_get_tile_width(isyntax); + int32_t tile_height = libisyntax_get_tile_height(isyntax); + LOG_VAR("%d", tile_width); + LOG_VAR("%d", tile_height); + + isyntax_cache_t *isyntax_cache = NULL; + assert(libisyntax_cache_create("example cache", 2000, &isyntax_cache) == LIBISYNTAX_OK); + assert(libisyntax_cache_inject(isyntax_cache, isyntax) == LIBISYNTAX_OK); + + + uint32_t *pixels = NULL; + assert(libisyntax_read_region(isyntax, isyntax_cache, 8, 0, 0, region_width, region_height, &pixels) == + LIBISYNTAX_OK); + // convert data to the correct pixel format (bgra->rgba). + + bgra_to_rgba(pixels, region_width, region_height); + + + printf("Writing %s...\n", output_png); + stbi_write_png(output_png, region_width, region_height, 4, pixels, region_width * 4); + printf("Done writing %s.\n", output_png); + + libisyntax_tile_free_pixels(pixels); + libisyntax_cache_destroy(isyntax_cache); + libisyntax_close(isyntax); + return 0; +} diff --git a/src/libisyntax.c b/src/libisyntax.c index 599576a..d26f247 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -293,6 +293,53 @@ isyntax_error_t libisyntax_tile_read(isyntax_t* isyntax, isyntax_cache_t* isynta return LIBISYNTAX_OK; } +isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyntax_cache, int32_t level, + int64_t x, int64_t y, int64_t width, int64_t height, uint32_t** out_pixels) { + + // Calculate tile coordinates + int32_t tile_width = libisyntax_get_tile_width(isyntax); + int32_t tile_height = libisyntax_get_tile_height(isyntax); + + int32_t start_tile_x = x / tile_width; + int32_t end_tile_x = (x + width - 1) / tile_width; + int32_t start_tile_y = y / tile_height; + int32_t end_tile_y = (y + height - 1) / tile_height; + + // Allocate memory for region + *out_pixels = (uint32_t*)malloc(width * height * sizeof(uint32_t)); + + // Read tiles and copy the relevant portion of each tile to the region + for (int64_t tile_y = start_tile_y; tile_y <= end_tile_y; ++tile_y) { + for (int64_t tile_x = start_tile_x; tile_x <= end_tile_x; ++tile_x) { + + // Read tile + uint32_t* pixels = NULL; + assert(libisyntax_tile_read(isyntax, isyntax_cache, level, tile_x, tile_y, &pixels) == LIBISYNTAX_OK); + + // Calculate the portion of the tile to be copied + int64_t src_x = (tile_x == start_tile_x) ? x % tile_width : 0; + int64_t src_y = (tile_y == start_tile_y) ? y % tile_height : 0; + int64_t dest_x = (tile_x - start_tile_x) * tile_width - ((tile_x == start_tile_x) ? x % tile_width : 0); + int64_t dest_y = (tile_y - start_tile_y) * tile_height - ((tile_y == start_tile_y) ? y % tile_height : 0); + int64_t copy_width = (tile_x == end_tile_x) ? ((x + width) % tile_width) ?: tile_width : tile_width - src_x; + int64_t copy_height = (tile_y == end_tile_y) ? ((y + height) % tile_height) ?: tile_height : tile_height - src_y; + + // Copy the relevant portion of the tile to the region + for (int64_t i = 0; i < copy_height; ++i) { + memcpy((*out_pixels) + (dest_y * width) + dest_x + (i * width), + pixels + (src_y * tile_width) + src_x + (i * tile_width), + copy_width * sizeof(uint32_t)); + } + + // Free the tile data + free(pixels); + } + } + + return LIBISYNTAX_OK; +} + + void libisyntax_tile_free_pixels(uint32_t* pixels) { free(pixels); } diff --git a/src/libisyntax.h b/src/libisyntax.h index 4053641..87ee11d 100644 --- a/src/libisyntax.h +++ b/src/libisyntax.h @@ -63,6 +63,8 @@ void libisyntax_cache_destroy(isyntax_cache_t* isyntax_cache); // Note: must use libisyntax_tile_free_pixels() to free out_pixels. isyntax_error_t libisyntax_tile_read(isyntax_t* isyntax, isyntax_cache_t* isyntax_cache, int32_t level, int64_t tile_x, int64_t tile_y, uint32_t** out_pixels); +isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyntax_cache, int32_t level, + int64_t x, int64_t y, int64_t width, int64_t height, uint32_t** out_pixels); void libisyntax_tile_free_pixels(uint32_t* pixels); From b63971b94457e88f6b1a5f38a83588a319f1d298 Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Tue, 18 Apr 2023 16:36:17 +0200 Subject: [PATCH 04/38] Compute bounds correctly... --- src/libisyntax.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/libisyntax.c b/src/libisyntax.c index d26f247..8ce8bc5 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -319,18 +319,20 @@ isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyn // Calculate the portion of the tile to be copied int64_t src_x = (tile_x == start_tile_x) ? x % tile_width : 0; int64_t src_y = (tile_y == start_tile_y) ? y % tile_height : 0; - int64_t dest_x = (tile_x - start_tile_x) * tile_width - ((tile_x == start_tile_x) ? x % tile_width : 0); - int64_t dest_y = (tile_y - start_tile_y) * tile_height - ((tile_y == start_tile_y) ? y % tile_height : 0); - int64_t copy_width = (tile_x == end_tile_x) ? ((x + width) % tile_width) ?: tile_width : tile_width - src_x; - int64_t copy_height = (tile_y == end_tile_y) ? ((y + height) % tile_height) ?: tile_height : tile_height - src_y; + int64_t dest_x = (tile_x == start_tile_x) ? 0 : (tile_x - start_tile_x) * tile_width - (x % tile_width); + int64_t dest_y = (tile_y == start_tile_y) ? 0 : (tile_y - start_tile_y) * tile_height - (y % tile_height); + int64_t copy_width = (tile_x == end_tile_x) ? (x + width) % tile_width : tile_width - src_x; + int64_t copy_height = (tile_y == end_tile_y) ? (y + height) % tile_height : tile_height - src_y; // Copy the relevant portion of the tile to the region for (int64_t i = 0; i < copy_height; ++i) { - memcpy((*out_pixels) + (dest_y * width) + dest_x + (i * width), - pixels + (src_y * tile_width) + src_x + (i * tile_width), + memcpy((*out_pixels) + (dest_y + i) * width + dest_x, + pixels + (src_y + i) * tile_width + src_x, copy_width * sizeof(uint32_t)); } + + // Free the tile data free(pixels); } From a721431464617d012ee0ba3b0801ce1165a03814 Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Tue, 18 Apr 2023 16:47:08 +0200 Subject: [PATCH 05/38] Take offset into account --- src/libisyntax.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libisyntax.c b/src/libisyntax.c index 8ce8bc5..8e5fd44 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -296,6 +296,12 @@ isyntax_error_t libisyntax_tile_read(isyntax_t* isyntax, isyntax_cache_t* isynta isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyntax_cache, int32_t level, int64_t x, int64_t y, int64_t width, int64_t height, uint32_t** out_pixels) { + // Compute the offset + // TODO: Is this always in [0]? + v2f offset = isyntax->images[0].levels[level].origin_offset; + x += (int64_t) (offset.x * isyntax->mpp_x); + y += (int64_t) (offset.y * isyntax->mpp_y); + // Calculate tile coordinates int32_t tile_width = libisyntax_get_tile_width(isyntax); int32_t tile_height = libisyntax_get_tile_height(isyntax); From 4ce5d12ea65eb7a69a169faa9f474af105c3d1ce Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Tue, 18 Apr 2023 16:53:35 +0200 Subject: [PATCH 06/38] - Make SIMD function also process sizes not divisible by 4. - Cleanup read_region function --- src/isyntax_example2.c | 20 ++++++++++---------- src/libisyntax.c | 12 ++++++------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/isyntax_example2.c b/src/isyntax_example2.c index e128bdc..3f91791 100644 --- a/src/isyntax_example2.c +++ b/src/isyntax_example2.c @@ -20,9 +20,10 @@ void bgra_to_rgba(uint32_t *pixels, int tile_width, int tile_height) { int num_pixels = tile_width * tile_height; + int num_pixels_aligned = (num_pixels / 4) * 4; #if defined(__ARM_NEON) - for (int i = 0; i < num_pixels; i += 4) { + for (int i = 0; i < num_pixels_aligned; i += 4) { uint32x4_t bgra = vld1q_u32(pixels + i); uint32x4_t b_mask = vdupq_n_u32(0x000000FF); uint32x4_t r_mask = vdupq_n_u32(0x00FF0000); @@ -35,7 +36,7 @@ void bgra_to_rgba(uint32_t *pixels, int tile_width, int tile_height) { vst1q_u32(pixels + i, rgba); } #elif defined(__SSE2__) - for (int i = 0; i < num_pixels; i += 4) { + for (int i = 0; i < num_pixels_aligned; i += 4) { __m128i bgra = _mm_loadu_si128((__m128i*)(pixels + i)); __m128i b_mask = _mm_set1_epi32(0x000000FF); __m128i r_mask = _mm_set1_epi32(0x00FF0000); @@ -48,7 +49,7 @@ void bgra_to_rgba(uint32_t *pixels, int tile_width, int tile_height) { _mm_storeu_si128((__m128i*)(pixels + i), rgba); } #else - for (int i = 0; i < num_pixels; ++i) { + for (int i = num_pixels_aligned; i < num_pixels; ++i) { uint32_t val = pixels[i]; pixels[i] = ((val & 0xff) << 16) | (val & 0x00ff00) | ((val & 0xff0000) >> 16) | (val & 0xff000000); } @@ -58,7 +59,7 @@ void bgra_to_rgba(uint32_t *pixels, int tile_width, int tile_height) { int main(int argc, char **argv) { if (argc <= 7) { - printf("Usage: %s - write a tile to output.png", + printf("Usage: %s - write a tile to output.png", argv[0], argv[0]); return 0; } @@ -76,15 +77,15 @@ int main(int argc, char **argv) { int32_t level = atoi(argv[2]); - int32_t tile_x = atoi(argv[3]); - int32_t tile_y = atoi(argv[4]); + int32_t x_coord = atoi(argv[3]); + int32_t y_coord = atoi(argv[4]); int32_t region_width = atoi(argv[5]); int32_t region_height = atoi(argv[6]); const char *output_png = argv[7]; LOG_VAR("%d", level); - LOG_VAR("%d", tile_x); - LOG_VAR("%d", tile_y); + LOG_VAR("%d", x_coord); + LOG_VAR("%d", y_coord); LOG_VAR("%d", region_width); LOG_VAR("%d", region_height); LOG_VAR("%s", output_png); @@ -98,9 +99,8 @@ int main(int argc, char **argv) { assert(libisyntax_cache_create("example cache", 2000, &isyntax_cache) == LIBISYNTAX_OK); assert(libisyntax_cache_inject(isyntax_cache, isyntax) == LIBISYNTAX_OK); - uint32_t *pixels = NULL; - assert(libisyntax_read_region(isyntax, isyntax_cache, 8, 0, 0, region_width, region_height, &pixels) == + assert(libisyntax_read_region(isyntax, isyntax_cache, level, x_coord, y_coord, region_width, region_height, &pixels) == LIBISYNTAX_OK); // convert data to the correct pixel format (bgra->rgba). diff --git a/src/libisyntax.c b/src/libisyntax.c index 8e5fd44..6c1af97 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -303,13 +303,13 @@ isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyn y += (int64_t) (offset.y * isyntax->mpp_y); // Calculate tile coordinates - int32_t tile_width = libisyntax_get_tile_width(isyntax); - int32_t tile_height = libisyntax_get_tile_height(isyntax); + int32_t tile_width = isyntax->tile_width; + int32_t tile_height = isyntax->tile_height; - int32_t start_tile_x = x / tile_width; - int32_t end_tile_x = (x + width - 1) / tile_width; - int32_t start_tile_y = y / tile_height; - int32_t end_tile_y = (y + height - 1) / tile_height; + int64_t start_tile_x = x / tile_width; + int64_t end_tile_x = (x + width - 1) / tile_width; + int64_t start_tile_y = y / tile_height; + int64_t end_tile_y = (y + height - 1) / tile_height; // Allocate memory for region *out_pixels = (uint32_t*)malloc(width * height * sizeof(uint32_t)); From ef34a0d3a12a5822b6662e6ad637deb1a36be46e Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Tue, 18 Apr 2023 17:09:40 +0200 Subject: [PATCH 07/38] Refactor BGRA to RGBA function. --- src/isyntax/isyntax.c | 40 +++++++++++++++++++++++++++++++++ src/isyntax_example.c | 13 +++-------- src/isyntax_example2.c | 51 +----------------------------------------- src/libisyntax.h | 4 +++- 4 files changed, 47 insertions(+), 61 deletions(-) diff --git a/src/isyntax/isyntax.c b/src/isyntax/isyntax.c index 62ef56e..1d8b02e 100644 --- a/src/isyntax/isyntax.c +++ b/src/isyntax/isyntax.c @@ -3200,3 +3200,43 @@ void isyntax_destroy(isyntax_t* isyntax) { } file_handle_close(isyntax->file_handle); } + + + +void bgra_to_rgba(uint32_t *pixels, int tile_width, int tile_height) { + int num_pixels = tile_width * tile_height; + int num_pixels_aligned = (num_pixels / 4) * 4; + +#if defined(__ARM_NEON) + for (int i = 0; i < num_pixels_aligned; i += 4) { + uint32x4_t bgra = vld1q_u32(pixels + i); + uint32x4_t b_mask = vdupq_n_u32(0x000000FF); + uint32x4_t r_mask = vdupq_n_u32(0x00FF0000); + uint32x4_t b = vandq_u32(bgra, b_mask); + uint32x4_t r = vandq_u32(bgra, r_mask); + uint32x4_t br_swapped = vorrq_u32(vshlq_n_u32(b, 16), vshrq_n_u32(r, 16)); + uint32x4_t ga_alpha_mask = vdupq_n_u32(0xFF00FF00); + uint32x4_t ga_alpha = vandq_u32(bgra, ga_alpha_mask); + uint32x4_t rgba = vorrq_u32(ga_alpha, br_swapped); + vst1q_u32(pixels + i, rgba); + } +#elif defined(__SSE2__) + for (int i = 0; i < num_pixels_aligned; i += 4) { + __m128i bgra = _mm_loadu_si128((__m128i*)(pixels + i)); + __m128i b_mask = _mm_set1_epi32(0x000000FF); + __m128i r_mask = _mm_set1_epi32(0x00FF0000); + __m128i b = _mm_and_si128(bgra, b_mask); + __m128i r = _mm_and_si128(bgra, r_mask); + __m128i br_swapped = _mm_or_si128(_mm_slli_epi32(b, 16), _mm_srli_epi32(r, 16)); + __m128i ga_alpha_mask = _mm_set1_epi32(0xFF00FF00); + __m128i ga_alpha = _mm_and_si128(bgra, ga_alpha_mask); + __m128i rgba = _mm_or_si128(ga_alpha, br_swapped); + _mm_storeu_si128((__m128i*)(pixels + i), rgba); + } +#else + for (int i = num_pixels_aligned; i < num_pixels; ++i) { + uint32_t val = pixels[i]; + pixels[i] = ((val & 0xff) << 16) | (val & 0x00ff00) | ((val & 0xff0000) >> 16) | (val & 0xff000000); + } +#endif +} \ No newline at end of file diff --git a/src/isyntax_example.c b/src/isyntax_example.c index 5bba620..97f859a 100644 --- a/src/isyntax_example.c +++ b/src/isyntax_example.c @@ -6,14 +6,8 @@ #define STB_IMAGE_WRITE_IMPLEMENTATION #include "third_party/stb_image_write.h" // for png export - - #define LOG_VAR(fmt, var) printf("%s: %s=" fmt "\n", __FUNCTION__, #var, var) -uint32_t bgra_to_rgba(uint32_t val) { - return ((val & 0xff) << 16) | (val & 0x00ff00) | ((val & 0xff0000) >> 16) | (val & 0xff000000); -} - void print_isyntax_levels(isyntax_t* isyntax) { int wsi_image_idx = libisyntax_get_wsi_image_index(isyntax); LOG_VAR("%d", wsi_image_idx); @@ -74,9 +68,8 @@ int main(int argc, char** argv) { assert(libisyntax_tile_read(isyntax, isyntax_cache, level, tile_x, tile_y, &pixels) == LIBISYNTAX_OK); // convert data to the correct pixel format (bgra->rgba). - for (int i = 0; i < tile_height * tile_width; ++i) { - pixels[i] = bgra_to_rgba(pixels[i]); - } + bgra_to_rgba(pixels); + printf("Writing %s...\n", output_png); stbi_write_png(output_png, tile_width, tile_height, 4, pixels, tile_width * 4); printf("Done writing %s.\n", output_png); @@ -87,4 +80,4 @@ int main(int argc, char** argv) { libisyntax_close(isyntax); return 0; -} +} \ No newline at end of file diff --git a/src/isyntax_example2.c b/src/isyntax_example2.c index 3f91791..aa56f7a 100644 --- a/src/isyntax_example2.c +++ b/src/isyntax_example2.c @@ -1,5 +1,4 @@ #include "libisyntax.h" - #include #include @@ -7,54 +6,8 @@ #include "third_party/stb_image_write.h" // for png export -#if defined(__ARM_NEON) - -#include - -#elif defined(__SSE2__) -#include -#endif - - #define LOG_VAR(fmt, var) printf("%s: %s=" fmt "\n", __FUNCTION__, #var, var) -void bgra_to_rgba(uint32_t *pixels, int tile_width, int tile_height) { - int num_pixels = tile_width * tile_height; - int num_pixels_aligned = (num_pixels / 4) * 4; - -#if defined(__ARM_NEON) - for (int i = 0; i < num_pixels_aligned; i += 4) { - uint32x4_t bgra = vld1q_u32(pixels + i); - uint32x4_t b_mask = vdupq_n_u32(0x000000FF); - uint32x4_t r_mask = vdupq_n_u32(0x00FF0000); - uint32x4_t b = vandq_u32(bgra, b_mask); - uint32x4_t r = vandq_u32(bgra, r_mask); - uint32x4_t br_swapped = vorrq_u32(vshlq_n_u32(b, 16), vshrq_n_u32(r, 16)); - uint32x4_t ga_alpha_mask = vdupq_n_u32(0xFF00FF00); - uint32x4_t ga_alpha = vandq_u32(bgra, ga_alpha_mask); - uint32x4_t rgba = vorrq_u32(ga_alpha, br_swapped); - vst1q_u32(pixels + i, rgba); - } -#elif defined(__SSE2__) - for (int i = 0; i < num_pixels_aligned; i += 4) { - __m128i bgra = _mm_loadu_si128((__m128i*)(pixels + i)); - __m128i b_mask = _mm_set1_epi32(0x000000FF); - __m128i r_mask = _mm_set1_epi32(0x00FF0000); - __m128i b = _mm_and_si128(bgra, b_mask); - __m128i r = _mm_and_si128(bgra, r_mask); - __m128i br_swapped = _mm_or_si128(_mm_slli_epi32(b, 16), _mm_srli_epi32(r, 16)); - __m128i ga_alpha_mask = _mm_set1_epi32(0xFF00FF00); - __m128i ga_alpha = _mm_and_si128(bgra, ga_alpha_mask); - __m128i rgba = _mm_or_si128(ga_alpha, br_swapped); - _mm_storeu_si128((__m128i*)(pixels + i), rgba); - } -#else - for (int i = num_pixels_aligned; i < num_pixels; ++i) { - uint32_t val = pixels[i]; - pixels[i] = ((val & 0xff) << 16) | (val & 0x00ff00) | ((val & 0xff0000) >> 16) | (val & 0xff000000); - } -#endif -} int main(int argc, char **argv) { @@ -75,7 +28,6 @@ int main(int argc, char **argv) { } printf("Successfully opened %s\n", filename); - int32_t level = atoi(argv[2]); int32_t x_coord = atoi(argv[3]); int32_t y_coord = atoi(argv[4]); @@ -102,11 +54,10 @@ int main(int argc, char **argv) { uint32_t *pixels = NULL; assert(libisyntax_read_region(isyntax, isyntax_cache, level, x_coord, y_coord, region_width, region_height, &pixels) == LIBISYNTAX_OK); - // convert data to the correct pixel format (bgra->rgba). + // convert data to the correct pixel format (bgra->rgba). bgra_to_rgba(pixels, region_width, region_height); - printf("Writing %s...\n", output_png); stbi_write_png(output_png, region_width, region_height, 4, pixels, region_width * 4); printf("Done writing %s.\n", output_png); diff --git a/src/libisyntax.h b/src/libisyntax.h index 87ee11d..a74c157 100644 --- a/src/libisyntax.h +++ b/src/libisyntax.h @@ -57,9 +57,11 @@ isyntax_error_t libisyntax_cache_create(const char* debug_name_or_null, int32_t isyntax_error_t libisyntax_cache_inject(isyntax_cache_t* isyntax_cache, isyntax_t* isyntax); void libisyntax_cache_destroy(isyntax_cache_t* isyntax_cache); +//== Helpers === +void bgra_to_rgba(uint32_t *pixels, int tile_width, int tile_height); //== Tile API == -// Note: pixels are in BGRA layout. +// Note: pixels are in BGRA layout. Use bgra_to_rgba to convert. // Note: must use libisyntax_tile_free_pixels() to free out_pixels. isyntax_error_t libisyntax_tile_read(isyntax_t* isyntax, isyntax_cache_t* isyntax_cache, int32_t level, int64_t tile_x, int64_t tile_y, uint32_t** out_pixels); From 3988f237066422690688f0346a56b04d996620e5 Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Wed, 19 Apr 2023 12:28:22 +0200 Subject: [PATCH 08/38] Skip empty tiles --- src/libisyntax.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/libisyntax.c b/src/libisyntax.c index 6c1af97..2a6dca2 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -302,7 +302,6 @@ isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyn x += (int64_t) (offset.x * isyntax->mpp_x); y += (int64_t) (offset.y * isyntax->mpp_y); - // Calculate tile coordinates int32_t tile_width = isyntax->tile_width; int32_t tile_height = isyntax->tile_height; @@ -311,6 +310,9 @@ isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyn int64_t start_tile_y = y / tile_height; int64_t end_tile_y = (y + height - 1) / tile_height; + // Get the level + isyntax_level_t* current_level = &isyntax->images[0].levels[level]; + // Allocate memory for region *out_pixels = (uint32_t*)malloc(width * height * sizeof(uint32_t)); @@ -318,27 +320,38 @@ isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyn for (int64_t tile_y = start_tile_y; tile_y <= end_tile_y; ++tile_y) { for (int64_t tile_x = start_tile_x; tile_x <= end_tile_x; ++tile_x) { - // Read tile - uint32_t* pixels = NULL; - assert(libisyntax_tile_read(isyntax, isyntax_cache, level, tile_x, tile_y, &pixels) == LIBISYNTAX_OK); + // Check if tile exists + int64_t tile_index = tile_y * current_level->width_in_tiles + tile_x; + bool tile_exists = (isyntax->images[0].levels[level].tiles + tile_index)->exists; // Calculate the portion of the tile to be copied int64_t src_x = (tile_x == start_tile_x) ? x % tile_width : 0; int64_t src_y = (tile_y == start_tile_y) ? y % tile_height : 0; int64_t dest_x = (tile_x == start_tile_x) ? 0 : (tile_x - start_tile_x) * tile_width - (x % tile_width); - int64_t dest_y = (tile_y == start_tile_y) ? 0 : (tile_y - start_tile_y) * tile_height - (y % tile_height); + int64_t dest_y = (tile_y == start_tile_y) ? 0 : (tile_y - start_tile_y) * tile_height - + (y % tile_height); int64_t copy_width = (tile_x == end_tile_x) ? (x + width) % tile_width : tile_width - src_x; int64_t copy_height = (tile_y == end_tile_y) ? (y + height) % tile_height : tile_height - src_y; - // Copy the relevant portion of the tile to the region - for (int64_t i = 0; i < copy_height; ++i) { - memcpy((*out_pixels) + (dest_y + i) * width + dest_x, - pixels + (src_y + i) * tile_width + src_x, - copy_width * sizeof(uint32_t)); + uint32_t *pixels = NULL; + if (tile_exists) { + // Read tile + assert(libisyntax_tile_read(isyntax, isyntax_cache, level, tile_x, tile_y, &pixels) == LIBISYNTAX_OK); + // Copy the relevant portion of the tile to the region + for (int64_t i = 0; i < copy_height; ++i) { + memcpy((*out_pixels) + (dest_y + i) * width + dest_x, + pixels + (src_y + i) * tile_width + src_x, + copy_width * sizeof(uint32_t)); + } + } else { + // Fill up with transparent pixels (R, G, B, A=0) + for (int64_t i = 0; i < copy_height; ++i) { + for (int64_t j = 0; j < copy_width; ++j) { + (*out_pixels)[(dest_y + i) * width + dest_x + j] = 0x00000000; + } + } } - - // Free the tile data free(pixels); } From 751cb94a86d141397fc6a8818a02f5f6539b9aae Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Wed, 19 Apr 2023 13:18:00 +0200 Subject: [PATCH 09/38] Improve offset computation --- src/libisyntax.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libisyntax.c b/src/libisyntax.c index 2a6dca2..47f09c9 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -296,11 +296,13 @@ isyntax_error_t libisyntax_tile_read(isyntax_t* isyntax, isyntax_cache_t* isynta isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyntax_cache, int32_t level, int64_t x, int64_t y, int64_t width, int64_t height, uint32_t** out_pixels) { - // Compute the offset - // TODO: Is this always in [0]? - v2f offset = isyntax->images[0].levels[level].origin_offset; - x += (int64_t) (offset.x * isyntax->mpp_x); - y += (int64_t) (offset.y * isyntax->mpp_y); + // TODO: Borrow this value from elsewhere + int PER_LEVEL_PADDING = 3; + int32_t num_levels = isyntax->images[0].level_count; + // TODO: This is probably a property of the isyntax->images[0].levels + int32_t offset = ((PER_LEVEL_PADDING << num_levels) - PER_LEVEL_PADDING) >> level; + x += offset; + y += offset; int32_t tile_width = isyntax->tile_width; int32_t tile_height = isyntax->tile_height; From 3767301d8eb8e096cfc1a3ef70e8aabbbeaa1868 Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Wed, 19 Apr 2023 13:32:26 +0200 Subject: [PATCH 10/38] Compute offset differently (and fix rgb conversion in example) --- src/isyntax/isyntax.c | 4 ++-- src/isyntax/isyntax.h | 2 +- src/isyntax_example.c | 2 +- src/libisyntax.c | 15 ++++++--------- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/isyntax/isyntax.c b/src/isyntax/isyntax.c index 1d8b02e..8cedc8b 100644 --- a/src/isyntax/isyntax.c +++ b/src/isyntax/isyntax.c @@ -2975,9 +2975,9 @@ bool isyntax_open(isyntax_t* isyntax, const char* filename, bool init_allocators // (this is also reflected in the x and y coordinates of the codeblocks in the iSyntax header). for (i32 scale = 1; scale < wsi_image->level_count; ++scale) { isyntax_level_t* level = wsi_image->levels + scale; + level->origin_offset_in_pixels = ((PER_LEVEL_PADDING << wsi_image->level_count) - PER_LEVEL_PADDING) >> scale; float offset_in_pixels = (float) (get_first_valid_coef_pixel(scale - 1)); - level->origin_offset_in_pixels = offset_in_pixels; - float offset_in_um_x = offset_in_pixels * wsi_image->levels[0].um_per_pixel_x; + float offset_in_um_x = offset_in_pixels * wsi_image->levels[0].um_per_pixel_x; float offset_in_um_y = offset_in_pixels * wsi_image->levels[0].um_per_pixel_y; level->origin_offset = (v2f){offset_in_um_x, offset_in_um_y}; } diff --git a/src/isyntax/isyntax.h b/src/isyntax/isyntax.h index 7f9d359..19765b1 100644 --- a/src/isyntax/isyntax.h +++ b/src/isyntax/isyntax.h @@ -281,7 +281,7 @@ typedef struct isyntax_level_t { float x_tile_side_in_um; float y_tile_side_in_um; u64 tile_count; - float origin_offset_in_pixels; + i32 origin_offset_in_pixels; v2f origin_offset; isyntax_tile_t* tiles; bool is_fully_loaded; diff --git a/src/isyntax_example.c b/src/isyntax_example.c index 97f859a..bc9f290 100644 --- a/src/isyntax_example.c +++ b/src/isyntax_example.c @@ -68,7 +68,7 @@ int main(int argc, char** argv) { assert(libisyntax_tile_read(isyntax, isyntax_cache, level, tile_x, tile_y, &pixels) == LIBISYNTAX_OK); // convert data to the correct pixel format (bgra->rgba). - bgra_to_rgba(pixels); + bgra_to_rgba(pixels, tile_width, tile_height); printf("Writing %s...\n", output_png); stbi_write_png(output_png, tile_width, tile_height, 4, pixels, tile_width * 4); diff --git a/src/libisyntax.c b/src/libisyntax.c index 47f09c9..fed2f97 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -296,13 +296,12 @@ isyntax_error_t libisyntax_tile_read(isyntax_t* isyntax, isyntax_cache_t* isynta isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyntax_cache, int32_t level, int64_t x, int64_t y, int64_t width, int64_t height, uint32_t** out_pixels) { - // TODO: Borrow this value from elsewhere - int PER_LEVEL_PADDING = 3; - int32_t num_levels = isyntax->images[0].level_count; - // TODO: This is probably a property of the isyntax->images[0].levels - int32_t offset = ((PER_LEVEL_PADDING << num_levels) - PER_LEVEL_PADDING) >> level; - x += offset; - y += offset; + // Get the level + isyntax_level_t* current_level = &isyntax->images[0].levels[level]; + + // Setup the origin offset + x += current_level->origin_offset_in_pixels; + y += current_level->origin_offset_in_pixels; int32_t tile_width = isyntax->tile_width; int32_t tile_height = isyntax->tile_height; @@ -312,8 +311,6 @@ isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyn int64_t start_tile_y = y / tile_height; int64_t end_tile_y = (y + height - 1) / tile_height; - // Get the level - isyntax_level_t* current_level = &isyntax->images[0].levels[level]; // Allocate memory for region *out_pixels = (uint32_t*)malloc(width * height * sizeof(uint32_t)); From 5b34dde7a89c4c6e474d5e253ffff798c93bcacc Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Wed, 19 Apr 2023 13:51:41 +0200 Subject: [PATCH 11/38] Reformat code slightly --- src/libisyntax.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/libisyntax.c b/src/libisyntax.c index fed2f97..f45deba 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -311,18 +311,12 @@ isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyn int64_t start_tile_y = y / tile_height; int64_t end_tile_y = (y + height - 1) / tile_height; - // Allocate memory for region *out_pixels = (uint32_t*)malloc(width * height * sizeof(uint32_t)); // Read tiles and copy the relevant portion of each tile to the region for (int64_t tile_y = start_tile_y; tile_y <= end_tile_y; ++tile_y) { for (int64_t tile_x = start_tile_x; tile_x <= end_tile_x; ++tile_x) { - - // Check if tile exists - int64_t tile_index = tile_y * current_level->width_in_tiles + tile_x; - bool tile_exists = (isyntax->images[0].levels[level].tiles + tile_index)->exists; - // Calculate the portion of the tile to be copied int64_t src_x = (tile_x == start_tile_x) ? x % tile_width : 0; int64_t src_y = (tile_y == start_tile_y) ? y % tile_height : 0; @@ -333,6 +327,11 @@ isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyn int64_t copy_height = (tile_y == end_tile_y) ? (y + height) % tile_height : tile_height - src_y; uint32_t *pixels = NULL; + + // Check if tile exists, if not, don't use the function to read the tile and immediately return an empty + // tile. + int64_t tile_index = tile_y * current_level->width_in_tiles + tile_x; + bool tile_exists = (isyntax->images[0].levels[level].tiles + tile_index)->exists; if (tile_exists) { // Read tile assert(libisyntax_tile_read(isyntax, isyntax_cache, level, tile_x, tile_y, &pixels) == LIBISYNTAX_OK); From aa4c4f688413cb8e76c1859dc2e2bc588748c4ce Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Wed, 19 Apr 2023 15:13:26 +0200 Subject: [PATCH 12/38] Check some bounds: - Added width and height to properties of level - Check if within bounds (temporary) --- src/isyntax/isyntax.c | 2 ++ src/isyntax/isyntax.h | 2 ++ src/libisyntax.c | 18 +++++++++++++++++- src/libisyntax.h | 2 ++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/isyntax/isyntax.c b/src/isyntax/isyntax.c index 8cedc8b..1e44aeb 100644 --- a/src/isyntax/isyntax.c +++ b/src/isyntax/isyntax.c @@ -2976,6 +2976,8 @@ bool isyntax_open(isyntax_t* isyntax, const char* filename, bool init_allocators for (i32 scale = 1; scale < wsi_image->level_count; ++scale) { isyntax_level_t* level = wsi_image->levels + scale; level->origin_offset_in_pixels = ((PER_LEVEL_PADDING << wsi_image->level_count) - PER_LEVEL_PADDING) >> scale; + level->width = wsi_image->width >> scale; + level->height = wsi_image->height >> scale; float offset_in_pixels = (float) (get_first_valid_coef_pixel(scale - 1)); float offset_in_um_x = offset_in_pixels * wsi_image->levels[0].um_per_pixel_x; float offset_in_um_y = offset_in_pixels * wsi_image->levels[0].um_per_pixel_y; diff --git a/src/isyntax/isyntax.h b/src/isyntax/isyntax.h index 19765b1..52ac1d5 100644 --- a/src/isyntax/isyntax.h +++ b/src/isyntax/isyntax.h @@ -275,6 +275,8 @@ typedef struct isyntax_level_t { i32 scale; i32 width_in_tiles; i32 height_in_tiles; + u64 width; + u64 height; float downsample_factor; float um_per_pixel_x; float um_per_pixel_y; diff --git a/src/libisyntax.c b/src/libisyntax.c index f45deba..c5146e3 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -218,6 +218,15 @@ int32_t libisyntax_level_get_scale(const isyntax_level_t* level) { return level->scale; } +// TODO(jt): Signature equivalent with openslide would be openslide_get_level_dimensions(isyntax_image_t *isyntax, int32_t level, int64_t *w, int64_t *h) { +int32_t libisyntax_level_get_width(const isyntax_level_t* level) { + return level->width; +} + +int32_t libisyntax_level_get_height(const isyntax_level_t* level) { + return level->height; +} + int32_t libisyntax_level_get_width_in_tiles(const isyntax_level_t* level) { return level->width_in_tiles; } @@ -296,13 +305,20 @@ isyntax_error_t libisyntax_tile_read(isyntax_t* isyntax, isyntax_cache_t* isynta isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyntax_cache, int32_t level, int64_t x, int64_t y, int64_t width, int64_t height, uint32_t** out_pixels) { + // Get the level + assert(level < &isyntax->images[0].level_count); isyntax_level_t* current_level = &isyntax->images[0].levels[level]; - + // Setup the origin offset x += current_level->origin_offset_in_pixels; y += current_level->origin_offset_in_pixels; + // Check bounds + // TODO: Figure out if the bounds are starting from x = 0 or from the offset + assert(x + width <= current_level->width); + assert(y + height <= current_level->height); + int32_t tile_width = isyntax->tile_width; int32_t tile_height = isyntax->tile_height; diff --git a/src/libisyntax.h b/src/libisyntax.h index a74c157..44df522 100644 --- a/src/libisyntax.h +++ b/src/libisyntax.h @@ -46,6 +46,8 @@ const isyntax_level_t* libisyntax_image_get_level(const isyntax_image_t* image, int32_t libisyntax_level_get_scale(const isyntax_level_t* level); int32_t libisyntax_level_get_width_in_tiles(const isyntax_level_t* level); int32_t libisyntax_level_get_height_in_tiles(const isyntax_level_t* level); +int32_t libisyntax_level_get_width(const isyntax_level_t* level); +int32_t libisyntax_level_get_height(const isyntax_level_t* level); //== Cache API == isyntax_error_t libisyntax_cache_create(const char* debug_name_or_null, int32_t cache_size, From 4e5e890837528220d2c8dd4d7a450df0f17d8706 Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Thu, 20 Apr 2023 12:13:09 +0200 Subject: [PATCH 13/38] Additions: - Tiles should be white when missing - Expose mpp_x and mpp_y --- src/libisyntax.c | 24 ++++++++++++++++-------- src/libisyntax.h | 2 ++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/libisyntax.c b/src/libisyntax.c index c5146e3..3f4e91d 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -218,6 +218,14 @@ int32_t libisyntax_level_get_scale(const isyntax_level_t* level) { return level->scale; } +float libisyntax_level_get_mpp_x(const isyntax_level_t* level) { + return level->um_per_pixel_x; +} + +float libisyntax_level_get_mpp_y(const isyntax_level_t* level) { + return level->um_per_pixel_y; +} + // TODO(jt): Signature equivalent with openslide would be openslide_get_level_dimensions(isyntax_image_t *isyntax, int32_t level, int64_t *w, int64_t *h) { int32_t libisyntax_level_get_width(const isyntax_level_t* level) { return level->width; @@ -305,19 +313,19 @@ isyntax_error_t libisyntax_tile_read(isyntax_t* isyntax, isyntax_cache_t* isynta isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyntax_cache, int32_t level, int64_t x, int64_t y, int64_t width, int64_t height, uint32_t** out_pixels) { - // Get the level assert(level < &isyntax->images[0].level_count); isyntax_level_t* current_level = &isyntax->images[0].levels[level]; - + // Setup the origin offset - x += current_level->origin_offset_in_pixels; - y += current_level->origin_offset_in_pixels; + int32_t offset = current_level->origin_offset_in_pixels; + x += offset; + y += offset; // Check bounds // TODO: Figure out if the bounds are starting from x = 0 or from the offset - assert(x + width <= current_level->width); - assert(y + height <= current_level->height); + assert(x + width - offset <= current_level->width); + assert(y + height - offset <= current_level->height); int32_t tile_width = isyntax->tile_width; int32_t tile_height = isyntax->tile_height; @@ -358,10 +366,10 @@ isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyn copy_width * sizeof(uint32_t)); } } else { - // Fill up with transparent pixels (R, G, B, A=0) + // Fill up with transparent white pixels (R=255, G=255, B=255, A=0) for (int64_t i = 0; i < copy_height; ++i) { for (int64_t j = 0; j < copy_width; ++j) { - (*out_pixels)[(dest_y + i) * width + dest_x + j] = 0x00000000; + (*out_pixels)[(dest_y + i) * width + dest_x + j] = 0x00FFFFFF; } } } diff --git a/src/libisyntax.h b/src/libisyntax.h index 44df522..4277c82 100644 --- a/src/libisyntax.h +++ b/src/libisyntax.h @@ -48,6 +48,8 @@ int32_t libisyntax_level_get_width_in_tiles(const isyntax_level_t int32_t libisyntax_level_get_height_in_tiles(const isyntax_level_t* level); int32_t libisyntax_level_get_width(const isyntax_level_t* level); int32_t libisyntax_level_get_height(const isyntax_level_t* level); +float libisyntax_level_get_mpp_x(const isyntax_level_t* level); +float libisyntax_level_get_mpp_y(const isyntax_level_t* level); //== Cache API == isyntax_error_t libisyntax_cache_create(const char* debug_name_or_null, int32_t cache_size, From bd4e6c3ec9173ac6288b55fd7f1c1dd9b50c499e Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Thu, 20 Apr 2023 12:26:31 +0200 Subject: [PATCH 14/38] Initial version of isyntax -> tiff --- CMakeLists.txt | 26 ++++++- src/isyntax_to_tiff.c | 167 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+), 3 deletions(-) create mode 100644 src/isyntax_to_tiff.c diff --git a/CMakeLists.txt b/CMakeLists.txt index e0dbb22..8f5cf72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,14 @@ include_directories("${CMAKE_SOURCE_DIR}/src/utils") include_directories("${CMAKE_SOURCE_DIR}/src/isyntax") include_directories("${CMAKE_SOURCE_DIR}/src/third_party") +# Find LibTIFF library +find_package(TIFF REQUIRED) +if (NOT TIFF_FOUND) + message(WARNING "LibTIFF not found") + message(WARNING "Will not compile `isyntax-to-tiff` utility") +endif() +include_directories(${TIFF_INCLUDE_DIR}) + set(LIBISYNTAX_COMMON_SOURCE_FILES src/libisyntax.c src/isyntax/isyntax.c @@ -45,9 +53,21 @@ add_executable(isyntax_example ) add_executable(isyntax_example2 - src/isyntax_example2.c - ${LIBISYNTAX_COMMON_SOURCE_FILES} - ) + src/isyntax_example2.c + ${LIBISYNTAX_COMMON_SOURCE_FILES} +) + +if (TIFF_FOUND) + message(STATUS "Will compile `isyntax-to-tiff` utility") + add_executable(isyntax-to-tiff + src/isyntax_to_tiff.c + ${LIBISYNTAX_COMMON_SOURCE_FILES} ${TIFF_LIBRARIES}) + +endif() + +target_include_directories(isyntax-to-tiff PRIVATE ${TIFF_INCLUDE_DIRS}) +target_link_libraries(isyntax-to-tiff PRIVATE ${TIFF_LIBRARIES}) + if (WIN32) target_link_libraries(libisyntax winmm) diff --git a/src/isyntax_to_tiff.c b/src/isyntax_to_tiff.c new file mode 100644 index 0000000..cbccfd7 --- /dev/null +++ b/src/isyntax_to_tiff.c @@ -0,0 +1,167 @@ +#include "libisyntax.h" +#include "tiffio.h" +#include +#include +#include +#include + +#define LOG_VAR(fmt, var) printf("%s: %s=" fmt "\n", __FUNCTION__, #var, var) + +void update_progress(int32_t total_progress, int32_t page_progress, int32_t page_number) { + printf("\rProgress: %3d%% | Page %d progress: %3d%%", total_progress, page_number, page_progress); + fflush(stdout); +} +void write_page_to_tiff(TIFF *output_tiff, isyntax_t *isyntax, isyntax_cache_t *isyntax_cache, isyntax_level_t* level, int32_t tile_width, int32_t tile_height, int32_t total_tiles_written) { + int32_t width = libisyntax_level_get_width(level); + int32_t height = libisyntax_level_get_height(level); + + // Set the TIFF properties for the current level. + TIFFSetField(output_tiff, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField(output_tiff, TIFFTAG_IMAGELENGTH, height); + TIFFSetField(output_tiff, TIFFTAG_BITSPERSAMPLE, 8); + TIFFSetField(output_tiff, TIFFTAG_SAMPLESPERPIXEL, 4); + TIFFSetField(output_tiff, TIFFTAG_COMPRESSION, COMPRESSION_LZW); + TIFFSetField(output_tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + TIFFSetField(output_tiff, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(output_tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(output_tiff, TIFFTAG_TILEWIDTH, tile_width); + TIFFSetField(output_tiff, TIFFTAG_TILELENGTH, tile_height); + + // Set the resolution + double res_x = 10000.0 / libisyntax_level_get_mpp_x(level); + TIFFSetField(output_tiff, TIFFTAG_XRESOLUTION, res_x); + double res_y = 10000.0 / libisyntax_level_get_mpp_y(level); + TIFFSetField(output_tiff, TIFFTAG_YRESOLUTION, res_y); + TIFFSetField(output_tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_CENTIMETER); + TIFFSetField(output_tiff, TIFFTAG_EXTRASAMPLES, 1, (uint16_t[]){EXTRASAMPLE_UNASSALPHA}); + + + if (level == 0) { + TIFFSetField(output_tiff, TIFFTAG_SUBFILETYPE, 0); + } else { + TIFFSetField(output_tiff, TIFFTAG_SUBFILETYPE, FILETYPE_REDUCEDIMAGE); + } + + int32_t total_tiles = (height / tile_height) * (width / tile_width); + int32_t tile_progress = 0; + + int32_t scale = libisyntax_level_get_scale(level); + + int32_t tiles_in_page = height * width / tile_height / tile_width; + // Write the current level tile by tile. + for (int32_t y_coord = 0; y_coord < height; y_coord += tile_height) { + for (int32_t x_coord = 0; x_coord < width; x_coord += tile_width) { + // At the borders the tile size can be smaller + int32_t region_width = (x_coord + tile_width > width) ? width - x_coord : tile_width; + int32_t region_height = (y_coord + tile_height > height) ? height - y_coord : tile_height; + + uint32_t *pixels = NULL; + assert(libisyntax_read_region(isyntax, isyntax_cache, scale, x_coord, y_coord, region_width, region_height, &pixels) == LIBISYNTAX_OK); + + // Convert data to the correct pixel format (bgra->rgba). + bgra_to_rgba(pixels, region_width, region_height); + + uint32_t *tile_pixels = pixels; + + if (region_width != tile_width || region_height != tile_height) { + tile_pixels = calloc(tile_width * tile_height, sizeof(uint32_t)); + for (int32_t row = 0; row < region_height; ++row) { + memcpy(tile_pixels + row * tile_width, pixels + row * region_width, region_width * sizeof(uint32_t)); + } + } + + // Write the tile to the output TIFF. + TIFFWriteTile(output_tiff, tile_pixels, x_coord, y_coord, 0, 0); + + ++tile_progress; + int32_t tile_percent = (tile_progress * 100) / tiles_in_page; + update_progress(total_tiles_written, tile_percent, scale); + + libisyntax_tile_free_pixels(pixels); + } + } + + // Write the directory for the current level. + TIFFWriteDirectory(output_tiff); +} + + + +int main(int argc, char **argv) { + if (argc <= 2) { + printf("Usage: %s - convert iSyntax file to a pyramidal BigTIFF\n", argv[0]); + return 0; + } + + char *filename = argv[1]; + char *output_tiffname = argv[2]; + + libisyntax_init(); + + isyntax_t *isyntax; + if (libisyntax_open(filename, /*is_init_allocators=*/0, &isyntax) != LIBISYNTAX_OK) { + printf("Failed to open %s\n", filename); + return -1; + } + printf("Successfully opened %s\n", filename); + + int32_t tile_width = libisyntax_get_tile_width(isyntax); + int32_t tile_height = libisyntax_get_tile_height(isyntax); + LOG_VAR("%d", tile_width); + LOG_VAR("%d", tile_height); + + isyntax_cache_t *isyntax_cache = NULL; + assert(libisyntax_cache_create("example cache", 2000, &isyntax_cache) == LIBISYNTAX_OK); + assert(libisyntax_cache_inject(isyntax_cache, isyntax) == LIBISYNTAX_OK); + + // Initialize the output TIFF file. + TIFF *output_tiff; + output_tiff = TIFFOpen(output_tiffname, "w8"); + if (!output_tiff) { + printf("Failed to create %s\n", output_tiffname); + return -1; + } + // TODO: Tile size != 256, 256 + tile_width = 1024; + tile_height = 1024; + + // Write all levels to the output TIFF. + int start_at_page = 3; + + const isyntax_image_t *image = libisyntax_get_image(isyntax, 0); + int32_t num_levels = libisyntax_image_get_level_count(image); + int32_t total_tiles = 0; + + // Let's find the total number of tiles so we can have a progress counter + for (int32_t level = start_at_page; level < num_levels; ++level) { + isyntax_level_t* current_level = libisyntax_image_get_level(image, level); + int32_t width = libisyntax_level_get_width(current_level); + int32_t height = libisyntax_level_get_height(current_level); + int32_t tiles_in_page = ((height - 1) / tile_height) * ((width - 1) / tile_width) + 2; + total_tiles += tiles_in_page; + } + + int32_t total_tiles_written = 0; + for (int32_t level = start_at_page; level < num_levels; ++level) { + isyntax_level_t* current_level = libisyntax_image_get_level(image, level); + int32_t width = libisyntax_level_get_width(current_level); + int32_t height = libisyntax_level_get_height(current_level); + int32_t tiles_in_page = (height / tile_height) * (width / tile_width); + + write_page_to_tiff(output_tiff, isyntax, isyntax_cache, current_level, tile_width, tile_height, total_tiles_written); + + total_tiles_written += tiles_in_page; + int32_t total_progress = (total_tiles_written * 100) / total_tiles; + int32_t page_progress = (total_tiles_written * 100) / tiles_in_page; + update_progress(total_progress, 0, level); // Reset tile progress to 0 + } + + // Close the output TIFF file. + TIFFClose(output_tiff); + + // Clean up. + libisyntax_cache_destroy(isyntax_cache); + libisyntax_close(isyntax); + + return 0; +} From 4cbb72a4e82be8e95994728adb1d08c85b0baa27 Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Thu, 20 Apr 2023 14:12:53 +0200 Subject: [PATCH 15/38] Update tool --- src/isyntax/isyntax.c | 10 +++- src/isyntax_to_tiff.c | 135 +++++++++++++++++++++++++++++------------- 2 files changed, 103 insertions(+), 42 deletions(-) diff --git a/src/isyntax/isyntax.c b/src/isyntax/isyntax.c index 1e44aeb..78d2e73 100644 --- a/src/isyntax/isyntax.c +++ b/src/isyntax/isyntax.c @@ -2973,8 +2973,14 @@ bool isyntax_open(isyntax_t* isyntax, const char* filename, bool init_allocators // (I guess this is related to the way the wavelet transform works.) // Put another way: the highest (zoomed out levels) are shifted the to the bottom right // (this is also reflected in the x and y coordinates of the codeblocks in the iSyntax header). - for (i32 scale = 1; scale < wsi_image->level_count; ++scale) { - isyntax_level_t* level = wsi_image->levels + scale; + // TODO(jt): Not sure why the below counter starts at 1. Is there no offset in the first page?? + // Anyway, we need the width here. + isyntax_level_t* level = wsi_image->levels + 0; + level->width = wsi_image->width; + level->height = wsi_image->height; + + for (i32 scale = 1; scale < wsi_image->level_count; ++scale) { + level += scale; level->origin_offset_in_pixels = ((PER_LEVEL_PADDING << wsi_image->level_count) - PER_LEVEL_PADDING) >> scale; level->width = wsi_image->width >> scale; level->height = wsi_image->height >> scale; diff --git a/src/isyntax_to_tiff.c b/src/isyntax_to_tiff.c index cbccfd7..2ea0be1 100644 --- a/src/isyntax_to_tiff.c +++ b/src/isyntax_to_tiff.c @@ -4,23 +4,32 @@ #include #include #include +#include + #define LOG_VAR(fmt, var) printf("%s: %s=" fmt "\n", __FUNCTION__, #var, var) -void update_progress(int32_t total_progress, int32_t page_progress, int32_t page_number) { - printf("\rProgress: %3d%% | Page %d progress: %3d%%", total_progress, page_number, page_progress); +void update_progress(int32_t total_progress, int32_t page_progress, int32_t page_number, double eta) { + printf("\rProgress: %3d%% | Page %d progress: %3d%% | ETA: %.0fs", total_progress, page_number, page_progress, eta); fflush(stdout); } -void write_page_to_tiff(TIFF *output_tiff, isyntax_t *isyntax, isyntax_cache_t *isyntax_cache, isyntax_level_t* level, int32_t tile_width, int32_t tile_height, int32_t total_tiles_written) { + + +void write_page_to_tiff(TIFF *output_tiff, isyntax_t *isyntax, isyntax_cache_t *isyntax_cache, isyntax_level_t *level, + int32_t tile_width, int32_t tile_height, int32_t *total_tiles_written, int32_t total_tiles) { int32_t width = libisyntax_level_get_width(level); int32_t height = libisyntax_level_get_height(level); + int32_t scale = libisyntax_level_get_scale(level); // Set the TIFF properties for the current level. TIFFSetField(output_tiff, TIFFTAG_IMAGEWIDTH, width); TIFFSetField(output_tiff, TIFFTAG_IMAGELENGTH, height); TIFFSetField(output_tiff, TIFFTAG_BITSPERSAMPLE, 8); TIFFSetField(output_tiff, TIFFTAG_SAMPLESPERPIXEL, 4); - TIFFSetField(output_tiff, TIFFTAG_COMPRESSION, COMPRESSION_LZW); + TIFFSetField(output_tiff, TIFFTAG_COMPRESSION, COMPRESSION_JPEG); + // TODO: ImageDescription + TIFFSetField(output_tiff, TIFFTAG_JPEGQUALITY, 80); + TIFFSetField(output_tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); TIFFSetField(output_tiff, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); TIFFSetField(output_tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); @@ -33,8 +42,7 @@ void write_page_to_tiff(TIFF *output_tiff, isyntax_t *isyntax, isyntax_cache_t * double res_y = 10000.0 / libisyntax_level_get_mpp_y(level); TIFFSetField(output_tiff, TIFFTAG_YRESOLUTION, res_y); TIFFSetField(output_tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_CENTIMETER); - TIFFSetField(output_tiff, TIFFTAG_EXTRASAMPLES, 1, (uint16_t[]){EXTRASAMPLE_UNASSALPHA}); - + TIFFSetField(output_tiff, TIFFTAG_EXTRASAMPLES, 1, (uint16_t[]) {EXTRASAMPLE_UNASSALPHA}); if (level == 0) { TIFFSetField(output_tiff, TIFFTAG_SUBFILETYPE, 0); @@ -42,13 +50,10 @@ void write_page_to_tiff(TIFF *output_tiff, isyntax_t *isyntax, isyntax_cache_t * TIFFSetField(output_tiff, TIFFTAG_SUBFILETYPE, FILETYPE_REDUCEDIMAGE); } - int32_t total_tiles = (height / tile_height) * (width / tile_width); int32_t tile_progress = 0; + int32_t tiles_in_page = ((height - 1) / tile_height) * ((width - 1) / tile_width) + 2; + clock_t start_time = clock(); - int32_t scale = libisyntax_level_get_scale(level); - - int32_t tiles_in_page = height * width / tile_height / tile_width; - // Write the current level tile by tile. for (int32_t y_coord = 0; y_coord < height; y_coord += tile_height) { for (int32_t x_coord = 0; x_coord < width; x_coord += tile_width) { // At the borders the tile size can be smaller @@ -56,7 +61,8 @@ void write_page_to_tiff(TIFF *output_tiff, isyntax_t *isyntax, isyntax_cache_t * int32_t region_height = (y_coord + tile_height > height) ? height - y_coord : tile_height; uint32_t *pixels = NULL; - assert(libisyntax_read_region(isyntax, isyntax_cache, scale, x_coord, y_coord, region_width, region_height, &pixels) == LIBISYNTAX_OK); + assert(libisyntax_read_region(isyntax, isyntax_cache, scale, x_coord, y_coord, region_width, region_height, + &pixels) == LIBISYNTAX_OK); // Convert data to the correct pixel format (bgra->rgba). bgra_to_rgba(pixels, region_width, region_height); @@ -66,7 +72,8 @@ void write_page_to_tiff(TIFF *output_tiff, isyntax_t *isyntax, isyntax_cache_t * if (region_width != tile_width || region_height != tile_height) { tile_pixels = calloc(tile_width * tile_height, sizeof(uint32_t)); for (int32_t row = 0; row < region_height; ++row) { - memcpy(tile_pixels + row * tile_width, pixels + row * region_width, region_width * sizeof(uint32_t)); + memcpy(tile_pixels + row * tile_width, pixels + row * region_width, + region_width * sizeof(uint32_t)); } } @@ -75,7 +82,16 @@ void write_page_to_tiff(TIFF *output_tiff, isyntax_t *isyntax, isyntax_cache_t * ++tile_progress; int32_t tile_percent = (tile_progress * 100) / tiles_in_page; - update_progress(total_tiles_written, tile_percent, scale); + int32_t total_progress = ((*total_tiles_written + tile_progress) * 100) / total_tiles; + + // Calculate ETA + clock_t current_time = clock(); + double elapsed_time = (double) (current_time - start_time) / CLOCKS_PER_SEC; + double avg_time_per_tile = elapsed_time / tile_progress; + double eta = avg_time_per_tile * (tiles_in_page - tile_progress); + + + update_progress(total_progress, tile_percent, scale, eta); libisyntax_tile_free_pixels(pixels); } @@ -85,17 +101,67 @@ void write_page_to_tiff(TIFF *output_tiff, isyntax_t *isyntax, isyntax_cache_t * TIFFWriteDirectory(output_tiff); } +int parse_cache_size(const char *size_str) { + int size; + char unit; + + if (sscanf(size_str, "%d%c", &size, &unit) == 2) { + if (unit == 'M') { + size *= 1024 * 1024; + } else if (unit == 'G') { + size *= 1024 * 1024 * 1024; + } else { + printf("Error: Invalid unit for cache size. Use 'M' for megabytes or 'G' for gigabytes.\n"); + return -1; + } + } else if (sscanf(size_str, "%d", &size) != 1) { + printf("Error: Invalid cache size format.\n"); + return -1; + } + + return size; +} int main(int argc, char **argv) { - if (argc <= 2) { - printf("Usage: %s - convert iSyntax file to a pyramidal BigTIFF\n", argv[0]); - return 0; - } - char *filename = argv[1]; char *output_tiffname = argv[2]; + int64_t cache_size = 2000; + int32_t tile_size = 1024; + + for (int i = 3; i < argc; ++i) { + if (strcmp(argv[i], "--tile-size") == 0) { + if (i + 1 < argc) { + tile_size = atoi(argv[i + 1]); + if (tile_size <= 0) { + printf("Error: Invalid tile size. Please provide a positive integer value for the tile size.\n"); + return -1; + } + i++; // Skip the next argument (tile size value) + } else { + printf("Error: Missing value for --tile-size option.\n"); + return -1; + } + } else if (strcmp(argv[i], "--cache-size") == 0) { + if (i + 1 < argc) { + cache_size = parse_cache_size(argv[i + 1]); + if (cache_size < 0) { + return -1; + } + i++; // Skip the next argument (cache size value) + } else { + printf("Error: Missing value for --cache-size option.\n"); + return -1; + } + } else { + printf("Error: Unknown option %s\n", argv[i]); + return -1; + } + } + int32_t tile_width = tile_size; + int32_t tile_height = tile_size; + libisyntax_init(); isyntax_t *isyntax; @@ -105,13 +171,13 @@ int main(int argc, char **argv) { } printf("Successfully opened %s\n", filename); - int32_t tile_width = libisyntax_get_tile_width(isyntax); - int32_t tile_height = libisyntax_get_tile_height(isyntax); - LOG_VAR("%d", tile_width); - LOG_VAR("%d", tile_height); + int32_t internal_tile_width = libisyntax_get_tile_width(isyntax); + int32_t internal_tile_height = libisyntax_get_tile_height(isyntax); + LOG_VAR("%d", internal_tile_width); + LOG_VAR("%d", internal_tile_height); isyntax_cache_t *isyntax_cache = NULL; - assert(libisyntax_cache_create("example cache", 2000, &isyntax_cache) == LIBISYNTAX_OK); + assert(libisyntax_cache_create("isyntax-to-tiff cache", cache_size, &isyntax_cache) == LIBISYNTAX_OK); assert(libisyntax_cache_inject(isyntax_cache, isyntax) == LIBISYNTAX_OK); // Initialize the output TIFF file. @@ -121,12 +187,9 @@ int main(int argc, char **argv) { printf("Failed to create %s\n", output_tiffname); return -1; } - // TODO: Tile size != 256, 256 - tile_width = 1024; - tile_height = 1024; // Write all levels to the output TIFF. - int start_at_page = 3; + int start_at_page = 0; const isyntax_image_t *image = libisyntax_get_image(isyntax, 0); int32_t num_levels = libisyntax_image_get_level_count(image); @@ -134,7 +197,7 @@ int main(int argc, char **argv) { // Let's find the total number of tiles so we can have a progress counter for (int32_t level = start_at_page; level < num_levels; ++level) { - isyntax_level_t* current_level = libisyntax_image_get_level(image, level); + isyntax_level_t *current_level = libisyntax_image_get_level(image, level); int32_t width = libisyntax_level_get_width(current_level); int32_t height = libisyntax_level_get_height(current_level); int32_t tiles_in_page = ((height - 1) / tile_height) * ((width - 1) / tile_width) + 2; @@ -143,17 +206,9 @@ int main(int argc, char **argv) { int32_t total_tiles_written = 0; for (int32_t level = start_at_page; level < num_levels; ++level) { - isyntax_level_t* current_level = libisyntax_image_get_level(image, level); - int32_t width = libisyntax_level_get_width(current_level); - int32_t height = libisyntax_level_get_height(current_level); - int32_t tiles_in_page = (height / tile_height) * (width / tile_width); - - write_page_to_tiff(output_tiff, isyntax, isyntax_cache, current_level, tile_width, tile_height, total_tiles_written); - - total_tiles_written += tiles_in_page; - int32_t total_progress = (total_tiles_written * 100) / total_tiles; - int32_t page_progress = (total_tiles_written * 100) / tiles_in_page; - update_progress(total_progress, 0, level); // Reset tile progress to 0 + isyntax_level_t *current_level = libisyntax_image_get_level(image, level); + write_page_to_tiff(output_tiff, isyntax, isyntax_cache, current_level, tile_width, tile_height, + &total_tiles_written, total_tiles); } // Close the output TIFF file. From 4e7e1aa49867de3a46da6ba86e98ea9e5473f9f6 Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Thu, 20 Apr 2023 14:19:22 +0200 Subject: [PATCH 16/38] Global eta makes more sense --- src/isyntax_to_tiff.c | 15 +++++++++------ src/libisyntax.c | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/isyntax_to_tiff.c b/src/isyntax_to_tiff.c index 2ea0be1..66c5f2c 100644 --- a/src/isyntax_to_tiff.c +++ b/src/isyntax_to_tiff.c @@ -16,7 +16,8 @@ void update_progress(int32_t total_progress, int32_t page_progress, int32_t page void write_page_to_tiff(TIFF *output_tiff, isyntax_t *isyntax, isyntax_cache_t *isyntax_cache, isyntax_level_t *level, - int32_t tile_width, int32_t tile_height, int32_t *total_tiles_written, int32_t total_tiles) { + int32_t tile_width, int32_t tile_height, int32_t *total_tiles_written, int32_t total_tiles, + clock_t global_start_time) { int32_t width = libisyntax_level_get_width(level); int32_t height = libisyntax_level_get_height(level); int32_t scale = libisyntax_level_get_scale(level); @@ -85,10 +86,11 @@ void write_page_to_tiff(TIFF *output_tiff, isyntax_t *isyntax, isyntax_cache_t * int32_t total_progress = ((*total_tiles_written + tile_progress) * 100) / total_tiles; // Calculate ETA - clock_t current_time = clock(); - double elapsed_time = (double) (current_time - start_time) / CLOCKS_PER_SEC; - double avg_time_per_tile = elapsed_time / tile_progress; - double eta = avg_time_per_tile * (tiles_in_page - tile_progress); + clock_t current_global_time = clock(); + double elapsed_global_time = (double)(current_global_time - global_start_time) / CLOCKS_PER_SEC; + double avg_time_per_tile = elapsed_global_time / (*total_tiles_written + tile_progress); + double eta = avg_time_per_tile * (total_tiles - (*total_tiles_written + tile_progress)); + update_progress(total_progress, tile_percent, scale, eta); @@ -205,10 +207,11 @@ int main(int argc, char **argv) { } int32_t total_tiles_written = 0; + clock_t global_start_time = clock(); for (int32_t level = start_at_page; level < num_levels; ++level) { isyntax_level_t *current_level = libisyntax_image_get_level(image, level); write_page_to_tiff(output_tiff, isyntax, isyntax_cache, current_level, tile_width, tile_height, - &total_tiles_written, total_tiles); + &total_tiles_written, total_tiles, global_start_time); } // Close the output TIFF file. diff --git a/src/libisyntax.c b/src/libisyntax.c index 3f4e91d..4a7408a 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -369,7 +369,7 @@ isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyn // Fill up with transparent white pixels (R=255, G=255, B=255, A=0) for (int64_t i = 0; i < copy_height; ++i) { for (int64_t j = 0; j < copy_width; ++j) { - (*out_pixels)[(dest_y + i) * width + dest_x + j] = 0x00FFFFFF; + (*out_pixels)[(dest_y + i) * width + dest_x + j] = 0x00FFFFFFu; } } } From c450e5c5dcb65ee10a455cf3ca9bfc4b9a3d6038 Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Thu, 20 Apr 2023 17:59:25 +0200 Subject: [PATCH 17/38] Cleanup tiff utility --- src/isyntax_to_tiff.c | 54 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/src/isyntax_to_tiff.c b/src/isyntax_to_tiff.c index 66c5f2c..db4d90d 100644 --- a/src/isyntax_to_tiff.c +++ b/src/isyntax_to_tiff.c @@ -17,7 +17,7 @@ void update_progress(int32_t total_progress, int32_t page_progress, int32_t page void write_page_to_tiff(TIFF *output_tiff, isyntax_t *isyntax, isyntax_cache_t *isyntax_cache, isyntax_level_t *level, int32_t tile_width, int32_t tile_height, int32_t *total_tiles_written, int32_t total_tiles, - clock_t global_start_time) { + clock_t global_start_time, uint16_t compression_type, uint16_t quality) { int32_t width = libisyntax_level_get_width(level); int32_t height = libisyntax_level_get_height(level); int32_t scale = libisyntax_level_get_scale(level); @@ -27,9 +27,13 @@ void write_page_to_tiff(TIFF *output_tiff, isyntax_t *isyntax, isyntax_cache_t * TIFFSetField(output_tiff, TIFFTAG_IMAGELENGTH, height); TIFFSetField(output_tiff, TIFFTAG_BITSPERSAMPLE, 8); TIFFSetField(output_tiff, TIFFTAG_SAMPLESPERPIXEL, 4); - TIFFSetField(output_tiff, TIFFTAG_COMPRESSION, COMPRESSION_JPEG); - // TODO: ImageDescription - TIFFSetField(output_tiff, TIFFTAG_JPEGQUALITY, 80); + + if (compression_type == COMPRESSION_JPEG) { + TIFFSetField(output_tiff, TIFFTAG_COMPRESSION, COMPRESSION_JPEG); + TIFFSetField(output_tiff, TIFFTAG_JPEGQUALITY, quality); + } else if (compression_type == COMPRESSION_LZW) { + TIFFSetField(output_tiff, TIFFTAG_COMPRESSION, COMPRESSION_LZW); + } TIFFSetField(output_tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); TIFFSetField(output_tiff, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); @@ -53,7 +57,6 @@ void write_page_to_tiff(TIFF *output_tiff, isyntax_t *isyntax, isyntax_cache_t * int32_t tile_progress = 0; int32_t tiles_in_page = ((height - 1) / tile_height) * ((width - 1) / tile_width) + 2; - clock_t start_time = clock(); for (int32_t y_coord = 0; y_coord < height; y_coord += tile_height) { for (int32_t x_coord = 0; x_coord < width; x_coord += tile_width) { @@ -90,9 +93,6 @@ void write_page_to_tiff(TIFF *output_tiff, isyntax_t *isyntax, isyntax_cache_t * double elapsed_global_time = (double)(current_global_time - global_start_time) / CLOCKS_PER_SEC; double avg_time_per_tile = elapsed_global_time / (*total_tiles_written + tile_progress); double eta = avg_time_per_tile * (total_tiles - (*total_tiles_written + tile_progress)); - - - update_progress(total_progress, tile_percent, scale, eta); libisyntax_tile_free_pixels(pixels); @@ -132,6 +132,9 @@ int main(int argc, char **argv) { int64_t cache_size = 2000; int32_t tile_size = 1024; + int compression_type = COMPRESSION_JPEG; + int quality = 80; + for (int i = 3; i < argc; ++i) { if (strcmp(argv[i], "--tile-size") == 0) { if (i + 1 < argc) { @@ -145,6 +148,38 @@ int main(int argc, char **argv) { printf("Error: Missing value for --tile-size option.\n"); return -1; } + } else if (strcmp(argv[i], "--compression") == 0) { + if (i + 1 < argc) { + if (strcmp(argv[i + 1], "JPEG") == 0) { + compression_type = COMPRESSION_JPEG; + } else if (strcmp(argv[i + 1], "LZW") == 0) { + compression_type = COMPRESSION_LZW; + } else if (strcmp(argv[i + 1], "NONE") == 0) { + compression_type = COMPRESSION_NONE; + } else { + printf("Error: Invalid compression type. Supported types are JPEG, LZW, and NONE.\n"); + return -1; + } + i++; // Skip the next argument (compression type value) + } else { + printf("Error: Missing value for --compression option.\n"); + return -1; + } + } else if (strcmp(argv[i], "--quality") == 0) { + if (i + 1 < argc) { + quality = atoi(argv[i + 1]); + if (quality < 0 || quality > 100) { + printf("Error: Invalid quality value. Please provide an integer value between 0 and 100 for the quality.\n"); + return -1; + } + if (compression_type != COMPRESSION_JPEG) { + printf("Warning: The --quality flag is ignored with the current compression type. Quality is only applicable to JPEG compressions.\n"); + } + i++; // Skip the next argument (quality value) + } else { + printf("Error: Missing value for --quality option.\n"); + return -1; + } } else if (strcmp(argv[i], "--cache-size") == 0) { if (i + 1 < argc) { cache_size = parse_cache_size(argv[i + 1]); @@ -156,6 +191,7 @@ int main(int argc, char **argv) { printf("Error: Missing value for --cache-size option.\n"); return -1; } + } else { printf("Error: Unknown option %s\n", argv[i]); return -1; @@ -211,7 +247,7 @@ int main(int argc, char **argv) { for (int32_t level = start_at_page; level < num_levels; ++level) { isyntax_level_t *current_level = libisyntax_image_get_level(image, level); write_page_to_tiff(output_tiff, isyntax, isyntax_cache, current_level, tile_width, tile_height, - &total_tiles_written, total_tiles, global_start_time); + &total_tiles_written, total_tiles, global_start_time, compression_type, quality); } // Close the output TIFF file. From 77745a6a34f86e1a479ba0216a0564b2232e4cbc Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Thu, 20 Apr 2023 18:02:02 +0200 Subject: [PATCH 18/38] Fix offset for level 0 --- src/isyntax/isyntax.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/isyntax/isyntax.c b/src/isyntax/isyntax.c index 78d2e73..32e1e49 100644 --- a/src/isyntax/isyntax.c +++ b/src/isyntax/isyntax.c @@ -2973,14 +2973,8 @@ bool isyntax_open(isyntax_t* isyntax, const char* filename, bool init_allocators // (I guess this is related to the way the wavelet transform works.) // Put another way: the highest (zoomed out levels) are shifted the to the bottom right // (this is also reflected in the x and y coordinates of the codeblocks in the iSyntax header). - // TODO(jt): Not sure why the below counter starts at 1. Is there no offset in the first page?? - // Anyway, we need the width here. - isyntax_level_t* level = wsi_image->levels + 0; - level->width = wsi_image->width; - level->height = wsi_image->height; - - for (i32 scale = 1; scale < wsi_image->level_count; ++scale) { - level += scale; + for (i32 scale = 0; scale < wsi_image->level_count; ++scale) { + isyntax_level_t* level = wsi_image->levels + scale; level->origin_offset_in_pixels = ((PER_LEVEL_PADDING << wsi_image->level_count) - PER_LEVEL_PADDING) >> scale; level->width = wsi_image->width >> scale; level->height = wsi_image->height >> scale; From 061053db0ab5f58e114989346ccef3f3cb07a132 Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Thu, 20 Apr 2023 18:19:23 +0200 Subject: [PATCH 19/38] Add usage string --- src/isyntax_to_tiff.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/isyntax_to_tiff.c b/src/isyntax_to_tiff.c index db4d90d..02e01b1 100644 --- a/src/isyntax_to_tiff.c +++ b/src/isyntax_to_tiff.c @@ -90,7 +90,7 @@ void write_page_to_tiff(TIFF *output_tiff, isyntax_t *isyntax, isyntax_cache_t * // Calculate ETA clock_t current_global_time = clock(); - double elapsed_global_time = (double)(current_global_time - global_start_time) / CLOCKS_PER_SEC; + double elapsed_global_time = (double) (current_global_time - global_start_time) / CLOCKS_PER_SEC; double avg_time_per_tile = elapsed_global_time / (*total_tiles_written + tile_progress); double eta = avg_time_per_tile * (total_tiles - (*total_tiles_written + tile_progress)); update_progress(total_progress, tile_percent, scale, eta); @@ -126,6 +126,32 @@ int parse_cache_size(const char *size_str) { int main(int argc, char **argv) { + const char *usage_string = + "Usage: isyntax-to-tiff [OPTIONS] INPUT OUTPUT\n\n" + "Converts Philips iSyntax files to multi-resolution TIFF files.\n\n" + "Positional arguments:\n" + " INPUT Path to the input iSyntax file.\n" + " OUTPUT Path to the output TIFF file.\n\n" + "Options:\n" + " --tile-size SIZE Specifies the tile size for the output TIFF (default: 1024).\n" + " Must be a positive integer.\n\n" + " --compression TYPE Specifies the compression type for the output TIFF.\n" + " Supported types: JPEG, LZW, NONE (default: JPEG).\n\n" + " --quality VALUE Specifies the quality for JPEG compression (0-100).\n" + " Only applicable when using JPEG compression (default: 80).\n\n" + " --cache-size SIZE Specifies the cache size for the iSyntax library.\n" + " Accepts a number followed by 'M' (for megabytes) or 'G' (for gigabytes),\n" + " or just a number for bytes (default: 2000).\n\n" + "Example:\n\n" + " isyntax-to-tiff --tile-size 512 --compression JPEG --quality 90 --cache-size 1G input.isyntax output.tiff\n\n" + "This command will convert the input.isyntax file into an output.tiff file with a tile size of 512, JPEG compression at 90 quality, and a cache size of 1 gigabyte.\n"; + + if (argc < 3) { + printf("Error: Missing input and/or output file arguments.\n\n"); + printf("%s", usage_string); + return -1; + } + char *filename = argv[1]; char *output_tiffname = argv[2]; From 82d6b57cf8adf00f41fd9de9f0f3705f2b386d7f Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Thu, 20 Apr 2023 18:52:54 +0200 Subject: [PATCH 20/38] Fix cache size (is it kilobytes?) --- src/isyntax_to_tiff.c | 51 ++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/src/isyntax_to_tiff.c b/src/isyntax_to_tiff.c index 02e01b1..c354940 100644 --- a/src/isyntax_to_tiff.c +++ b/src/isyntax_to_tiff.c @@ -103,20 +103,28 @@ void write_page_to_tiff(TIFF *output_tiff, isyntax_t *isyntax, isyntax_cache_t * TIFFWriteDirectory(output_tiff); } -int parse_cache_size(const char *size_str) { - int size; +uint64_t parse_cache_size(const char *size_str) { + uint64_t size; char unit; - if (sscanf(size_str, "%d%c", &size, &unit) == 2) { + if (sscanf(size_str, "%lld%c", &size, &unit) == 2) { if (unit == 'M') { - size *= 1024 * 1024; + if (size > INT64_MAX / (1024)) { + printf("Error: Cache size too large.\n"); + return -1; + } + size *= 1024; } else if (unit == 'G') { - size *= 1024 * 1024 * 1024; + if (size > INT64_MAX / (1024 * 1024)) { + printf("Error: Cache size too large.\n"); + return -1; + } + size *= 1024 * 1024; } else { printf("Error: Invalid unit for cache size. Use 'M' for megabytes or 'G' for gigabytes.\n"); return -1; } - } else if (sscanf(size_str, "%d", &size) != 1) { + } else if (sscanf(size_str, "%lld", &size) != 1) { printf("Error: Invalid cache size format.\n"); return -1; } @@ -141,7 +149,7 @@ int main(int argc, char **argv) { " Only applicable when using JPEG compression (default: 80).\n\n" " --cache-size SIZE Specifies the cache size for the iSyntax library.\n" " Accepts a number followed by 'M' (for megabytes) or 'G' (for gigabytes),\n" - " or just a number for bytes (default: 2000).\n\n" + " or just a number for kilobytes (default: 2000).\n\n" "Example:\n\n" " isyntax-to-tiff --tile-size 512 --compression JPEG --quality 90 --cache-size 1G input.isyntax output.tiff\n\n" "This command will convert the input.isyntax file into an output.tiff file with a tile size of 512, JPEG compression at 90 quality, and a cache size of 1 gigabyte.\n"; @@ -151,11 +159,11 @@ int main(int argc, char **argv) { printf("%s", usage_string); return -1; } - + char *filename = argv[1]; char *output_tiffname = argv[2]; - int64_t cache_size = 2000; + uint64_t cache_size = 2000; int32_t tile_size = 1024; int compression_type = COMPRESSION_JPEG; @@ -209,7 +217,8 @@ int main(int argc, char **argv) { } else if (strcmp(argv[i], "--cache-size") == 0) { if (i + 1 < argc) { cache_size = parse_cache_size(argv[i + 1]); - if (cache_size < 0) { + if (cache_size >= INT64_MAX || cache_size < 0) { + printf("Error: Cache size not suitable for the system.\n"); return -1; } i++; // Skip the next argument (cache size value) @@ -230,25 +239,37 @@ int main(int argc, char **argv) { isyntax_t *isyntax; if (libisyntax_open(filename, /*is_init_allocators=*/0, &isyntax) != LIBISYNTAX_OK) { - printf("Failed to open %s\n", filename); + fprintf(stderr, "Failed to open %s\n", filename); return -1; } - printf("Successfully opened %s\n", filename); int32_t internal_tile_width = libisyntax_get_tile_width(isyntax); int32_t internal_tile_height = libisyntax_get_tile_height(isyntax); LOG_VAR("%d", internal_tile_width); LOG_VAR("%d", internal_tile_height); + LOG_VAR("%llu", cache_size); + LOG_VAR("%d", compression_type); + LOG_VAR("%d", quality); + LOG_VAR("%d", tile_size); isyntax_cache_t *isyntax_cache = NULL; - assert(libisyntax_cache_create("isyntax-to-tiff cache", cache_size, &isyntax_cache) == LIBISYNTAX_OK); - assert(libisyntax_cache_inject(isyntax_cache, isyntax) == LIBISYNTAX_OK); + if (libisyntax_cache_create("isyntax-to-tiff cache", cache_size, &isyntax_cache) != LIBISYNTAX_OK) { + fprintf(stderr, "Failed to create iSyntax cache with size %" PRId64 ".\n", cache_size); + libisyntax_close(isyntax); + return -1; + } + if (libisyntax_cache_inject(isyntax_cache, isyntax) != LIBISYNTAX_OK) { + fprintf(stderr, "Failed to inject iSyntax cache into iSyntax instance.\n"); + libisyntax_cache_destroy(isyntax_cache); + libisyntax_close(isyntax); + return -1; + } // Initialize the output TIFF file. TIFF *output_tiff; output_tiff = TIFFOpen(output_tiffname, "w8"); if (!output_tiff) { - printf("Failed to create %s\n", output_tiffname); + fprintf(stderr, "Failed to create %s\n", output_tiffname); return -1; } From b350af32500e054cbf8359d566c35eab5269c86b Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Thu, 20 Apr 2023 20:10:36 +0200 Subject: [PATCH 21/38] Fix formatting --- src/isyntax_to_tiff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/isyntax_to_tiff.c b/src/isyntax_to_tiff.c index c354940..f9a1f77 100644 --- a/src/isyntax_to_tiff.c +++ b/src/isyntax_to_tiff.c @@ -254,7 +254,7 @@ int main(int argc, char **argv) { isyntax_cache_t *isyntax_cache = NULL; if (libisyntax_cache_create("isyntax-to-tiff cache", cache_size, &isyntax_cache) != LIBISYNTAX_OK) { - fprintf(stderr, "Failed to create iSyntax cache with size %" PRId64 ".\n", cache_size); + fprintf(stderr, "Failed to create iSyntax cache with size %llu.\n", cache_size); libisyntax_close(isyntax); return -1; } From 05a7f579a39948faee77e8143ef5a11a4e2e7d6a Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Thu, 20 Apr 2023 20:29:30 +0200 Subject: [PATCH 22/38] Updated CMakeLists.txt --- CMakeLists.txt | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f3f08d..ff0e92d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.23) +cmake_minimum_required(VERSION 3.22) project(libisyntax) set(CMAKE_C_STANDARD 17) @@ -20,11 +20,6 @@ include_directories("${CMAKE_SOURCE_DIR}/src/platform") include_directories("${CMAKE_SOURCE_DIR}/src/utils") include_directories("${CMAKE_SOURCE_DIR}/src/isyntax") include_directories("${CMAKE_SOURCE_DIR}/src/third_party") -include_directories(${VIPS_INCLUDE_DIRS}) - -find_package(PkgConfig REQUIRED) -pkg_check_modules(VIPS REQUIRED vips) - # Find LibTIFF library find_package(TIFF REQUIRED) @@ -33,6 +28,17 @@ if (NOT TIFF_FOUND) message(WARNING "Will not compile `isyntax-to-tiff` utility") endif() include_directories(${TIFF_INCLUDE_DIR}) +find_package(Threads REQUIRED) +if (UNIX AND NOT APPLE) # needed for sem_wait, etc. + find_library(RT_LIBRARY rt) + set(EXTRA_LIBS ${RT_LIBRARY}) +elseif (APPLE) + # No extra libraries needed for macOS +elseif (WIN32) + # No extra libraries needed for Windows +endif () + + set(LIBISYNTAX_COMMON_SOURCE_FILES src/libisyntax.c @@ -62,18 +68,7 @@ add_executable(isyntax_example src/isyntax_example.c ${LIBISYNTAX_COMMON_SOURCE_FILES} ) -message(STATUS "VIPS_INCLUDE_DIRS: ${VIPS_INCLUDE_DIRS}") -message(STATUS "VIPS_LIBRARY_DIRS: ${VIPS_LIBRARY_DIRS}") -message(STATUS "VIPS_LIBRARIES: ${VIPS_LIBRARIES}") -link_directories(${VIPS_LIBRARY_DIRS}) -add_executable(isyntax-to-tiff - src/isyntax_to_tiff.c - ${LIBISYNTAX_COMMON_SOURCE_FILES} - ) - -target_include_directories(isyntax-to-tiff PRIVATE ${VIPS_INCLUDE_DIRS}) -target_link_libraries(isyntax-to-tiff PRIVATE ${VIPS_LIBRARIES}) add_executable(isyntax_example2 @@ -90,14 +85,19 @@ if (TIFF_FOUND) endif() target_include_directories(isyntax-to-tiff PRIVATE ${TIFF_INCLUDE_DIRS}) -target_link_libraries(isyntax-to-tiff PRIVATE ${TIFF_LIBRARIES}) - if (WIN32) - target_link_libraries(libisyntax winmm) - target_link_libraries(isyntax_example winmm) - target_link_libraries(isyntax-to-tiff winmm) + target_link_libraries(libisyntax winmm Threads::Threads) + target_link_libraries(isyntax_example winmm Threads::Threads) + target_link_libraries(isyntax_example2 winmm Threads::Threads) + target_link_libraries(isyntax-to-tiff winmm Threads::Threads) -else() +else() + target_link_libraries(libisyntax Threads::Threads ${EXTRA_LIBS}) + target_link_libraries(isyntax_example Threads::Threads ${EXTRA_LIBS}) + target_link_libraries(isyntax_example2 Threads::Threads ${EXTRA_LIBS}) + if (TIFF_FOUND) + target_link_libraries(isyntax-to-tiff PRIVATE ${TIFF_LIBRARIES} Threads::Threads ${EXTRA_LIBS}) + endif() endif() From 64d70dd97a0d23c6c15fd5a31bd076390fbe80b7 Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Tue, 18 Apr 2023 14:40:01 +0200 Subject: [PATCH 23/38] Add read_region function --- CMakeLists.txt | 5 ++ src/isyntax_example2.c | 118 +++++++++++++++++++++++++++++++++++++++++ src/libisyntax.c | 47 ++++++++++++++++ src/libisyntax.h | 2 + 4 files changed, 172 insertions(+) create mode 100644 src/isyntax_example2.c diff --git a/CMakeLists.txt b/CMakeLists.txt index d8222c2..1804bcc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,6 +68,11 @@ target_include_directories(isyntax-to-tiff PRIVATE ${VIPS_INCLUDE_DIRS}) target_link_libraries(isyntax-to-tiff PRIVATE ${VIPS_LIBRARIES}) +add_executable(isyntax_example2 + src/isyntax_example2.c + ${LIBISYNTAX_COMMON_SOURCE_FILES} + ) + if (WIN32) target_link_libraries(libisyntax winmm) target_link_libraries(isyntax_example winmm) diff --git a/src/isyntax_example2.c b/src/isyntax_example2.c new file mode 100644 index 0000000..e128bdc --- /dev/null +++ b/src/isyntax_example2.c @@ -0,0 +1,118 @@ +#include "libisyntax.h" + +#include +#include + +#define STB_IMAGE_WRITE_IMPLEMENTATION + +#include "third_party/stb_image_write.h" // for png export + +#if defined(__ARM_NEON) + +#include + +#elif defined(__SSE2__) +#include +#endif + + +#define LOG_VAR(fmt, var) printf("%s: %s=" fmt "\n", __FUNCTION__, #var, var) + +void bgra_to_rgba(uint32_t *pixels, int tile_width, int tile_height) { + int num_pixels = tile_width * tile_height; + +#if defined(__ARM_NEON) + for (int i = 0; i < num_pixels; i += 4) { + uint32x4_t bgra = vld1q_u32(pixels + i); + uint32x4_t b_mask = vdupq_n_u32(0x000000FF); + uint32x4_t r_mask = vdupq_n_u32(0x00FF0000); + uint32x4_t b = vandq_u32(bgra, b_mask); + uint32x4_t r = vandq_u32(bgra, r_mask); + uint32x4_t br_swapped = vorrq_u32(vshlq_n_u32(b, 16), vshrq_n_u32(r, 16)); + uint32x4_t ga_alpha_mask = vdupq_n_u32(0xFF00FF00); + uint32x4_t ga_alpha = vandq_u32(bgra, ga_alpha_mask); + uint32x4_t rgba = vorrq_u32(ga_alpha, br_swapped); + vst1q_u32(pixels + i, rgba); + } +#elif defined(__SSE2__) + for (int i = 0; i < num_pixels; i += 4) { + __m128i bgra = _mm_loadu_si128((__m128i*)(pixels + i)); + __m128i b_mask = _mm_set1_epi32(0x000000FF); + __m128i r_mask = _mm_set1_epi32(0x00FF0000); + __m128i b = _mm_and_si128(bgra, b_mask); + __m128i r = _mm_and_si128(bgra, r_mask); + __m128i br_swapped = _mm_or_si128(_mm_slli_epi32(b, 16), _mm_srli_epi32(r, 16)); + __m128i ga_alpha_mask = _mm_set1_epi32(0xFF00FF00); + __m128i ga_alpha = _mm_and_si128(bgra, ga_alpha_mask); + __m128i rgba = _mm_or_si128(ga_alpha, br_swapped); + _mm_storeu_si128((__m128i*)(pixels + i), rgba); + } +#else + for (int i = 0; i < num_pixels; ++i) { + uint32_t val = pixels[i]; + pixels[i] = ((val & 0xff) << 16) | (val & 0x00ff00) | ((val & 0xff0000) >> 16) | (val & 0xff000000); + } +#endif +} + +int main(int argc, char **argv) { + + if (argc <= 7) { + printf("Usage: %s - write a tile to output.png", + argv[0], argv[0]); + return 0; + } + + char *filename = argv[1]; + + libisyntax_init(); + + isyntax_t *isyntax; + if (libisyntax_open(filename, /*is_init_allocators=*/0, &isyntax) != LIBISYNTAX_OK) { + printf("Failed to open %s\n", filename); + return -1; + } + printf("Successfully opened %s\n", filename); + + + int32_t level = atoi(argv[2]); + int32_t tile_x = atoi(argv[3]); + int32_t tile_y = atoi(argv[4]); + int32_t region_width = atoi(argv[5]); + int32_t region_height = atoi(argv[6]); + const char *output_png = argv[7]; + + LOG_VAR("%d", level); + LOG_VAR("%d", tile_x); + LOG_VAR("%d", tile_y); + LOG_VAR("%d", region_width); + LOG_VAR("%d", region_height); + LOG_VAR("%s", output_png); + + int32_t tile_width = libisyntax_get_tile_width(isyntax); + int32_t tile_height = libisyntax_get_tile_height(isyntax); + LOG_VAR("%d", tile_width); + LOG_VAR("%d", tile_height); + + isyntax_cache_t *isyntax_cache = NULL; + assert(libisyntax_cache_create("example cache", 2000, &isyntax_cache) == LIBISYNTAX_OK); + assert(libisyntax_cache_inject(isyntax_cache, isyntax) == LIBISYNTAX_OK); + + + uint32_t *pixels = NULL; + assert(libisyntax_read_region(isyntax, isyntax_cache, 8, 0, 0, region_width, region_height, &pixels) == + LIBISYNTAX_OK); + // convert data to the correct pixel format (bgra->rgba). + + bgra_to_rgba(pixels, region_width, region_height); + + + printf("Writing %s...\n", output_png); + stbi_write_png(output_png, region_width, region_height, 4, pixels, region_width * 4); + printf("Done writing %s.\n", output_png); + + libisyntax_tile_free_pixels(pixels); + libisyntax_cache_destroy(isyntax_cache); + libisyntax_close(isyntax); + return 0; +} diff --git a/src/libisyntax.c b/src/libisyntax.c index 599576a..d26f247 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -293,6 +293,53 @@ isyntax_error_t libisyntax_tile_read(isyntax_t* isyntax, isyntax_cache_t* isynta return LIBISYNTAX_OK; } +isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyntax_cache, int32_t level, + int64_t x, int64_t y, int64_t width, int64_t height, uint32_t** out_pixels) { + + // Calculate tile coordinates + int32_t tile_width = libisyntax_get_tile_width(isyntax); + int32_t tile_height = libisyntax_get_tile_height(isyntax); + + int32_t start_tile_x = x / tile_width; + int32_t end_tile_x = (x + width - 1) / tile_width; + int32_t start_tile_y = y / tile_height; + int32_t end_tile_y = (y + height - 1) / tile_height; + + // Allocate memory for region + *out_pixels = (uint32_t*)malloc(width * height * sizeof(uint32_t)); + + // Read tiles and copy the relevant portion of each tile to the region + for (int64_t tile_y = start_tile_y; tile_y <= end_tile_y; ++tile_y) { + for (int64_t tile_x = start_tile_x; tile_x <= end_tile_x; ++tile_x) { + + // Read tile + uint32_t* pixels = NULL; + assert(libisyntax_tile_read(isyntax, isyntax_cache, level, tile_x, tile_y, &pixels) == LIBISYNTAX_OK); + + // Calculate the portion of the tile to be copied + int64_t src_x = (tile_x == start_tile_x) ? x % tile_width : 0; + int64_t src_y = (tile_y == start_tile_y) ? y % tile_height : 0; + int64_t dest_x = (tile_x - start_tile_x) * tile_width - ((tile_x == start_tile_x) ? x % tile_width : 0); + int64_t dest_y = (tile_y - start_tile_y) * tile_height - ((tile_y == start_tile_y) ? y % tile_height : 0); + int64_t copy_width = (tile_x == end_tile_x) ? ((x + width) % tile_width) ?: tile_width : tile_width - src_x; + int64_t copy_height = (tile_y == end_tile_y) ? ((y + height) % tile_height) ?: tile_height : tile_height - src_y; + + // Copy the relevant portion of the tile to the region + for (int64_t i = 0; i < copy_height; ++i) { + memcpy((*out_pixels) + (dest_y * width) + dest_x + (i * width), + pixels + (src_y * tile_width) + src_x + (i * tile_width), + copy_width * sizeof(uint32_t)); + } + + // Free the tile data + free(pixels); + } + } + + return LIBISYNTAX_OK; +} + + void libisyntax_tile_free_pixels(uint32_t* pixels) { free(pixels); } diff --git a/src/libisyntax.h b/src/libisyntax.h index 4053641..87ee11d 100644 --- a/src/libisyntax.h +++ b/src/libisyntax.h @@ -63,6 +63,8 @@ void libisyntax_cache_destroy(isyntax_cache_t* isyntax_cache); // Note: must use libisyntax_tile_free_pixels() to free out_pixels. isyntax_error_t libisyntax_tile_read(isyntax_t* isyntax, isyntax_cache_t* isyntax_cache, int32_t level, int64_t tile_x, int64_t tile_y, uint32_t** out_pixels); +isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyntax_cache, int32_t level, + int64_t x, int64_t y, int64_t width, int64_t height, uint32_t** out_pixels); void libisyntax_tile_free_pixels(uint32_t* pixels); From ad9f4d9ee307d3bff7eaf7caaa1d8a8c7d695f16 Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Tue, 18 Apr 2023 16:36:17 +0200 Subject: [PATCH 24/38] Compute bounds correctly... --- src/libisyntax.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/libisyntax.c b/src/libisyntax.c index d26f247..8ce8bc5 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -319,18 +319,20 @@ isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyn // Calculate the portion of the tile to be copied int64_t src_x = (tile_x == start_tile_x) ? x % tile_width : 0; int64_t src_y = (tile_y == start_tile_y) ? y % tile_height : 0; - int64_t dest_x = (tile_x - start_tile_x) * tile_width - ((tile_x == start_tile_x) ? x % tile_width : 0); - int64_t dest_y = (tile_y - start_tile_y) * tile_height - ((tile_y == start_tile_y) ? y % tile_height : 0); - int64_t copy_width = (tile_x == end_tile_x) ? ((x + width) % tile_width) ?: tile_width : tile_width - src_x; - int64_t copy_height = (tile_y == end_tile_y) ? ((y + height) % tile_height) ?: tile_height : tile_height - src_y; + int64_t dest_x = (tile_x == start_tile_x) ? 0 : (tile_x - start_tile_x) * tile_width - (x % tile_width); + int64_t dest_y = (tile_y == start_tile_y) ? 0 : (tile_y - start_tile_y) * tile_height - (y % tile_height); + int64_t copy_width = (tile_x == end_tile_x) ? (x + width) % tile_width : tile_width - src_x; + int64_t copy_height = (tile_y == end_tile_y) ? (y + height) % tile_height : tile_height - src_y; // Copy the relevant portion of the tile to the region for (int64_t i = 0; i < copy_height; ++i) { - memcpy((*out_pixels) + (dest_y * width) + dest_x + (i * width), - pixels + (src_y * tile_width) + src_x + (i * tile_width), + memcpy((*out_pixels) + (dest_y + i) * width + dest_x, + pixels + (src_y + i) * tile_width + src_x, copy_width * sizeof(uint32_t)); } + + // Free the tile data free(pixels); } From 69d37e94ead3452d3938ff1315198060f540c05a Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Tue, 18 Apr 2023 16:47:08 +0200 Subject: [PATCH 25/38] Take offset into account --- src/libisyntax.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libisyntax.c b/src/libisyntax.c index 8ce8bc5..8e5fd44 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -296,6 +296,12 @@ isyntax_error_t libisyntax_tile_read(isyntax_t* isyntax, isyntax_cache_t* isynta isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyntax_cache, int32_t level, int64_t x, int64_t y, int64_t width, int64_t height, uint32_t** out_pixels) { + // Compute the offset + // TODO: Is this always in [0]? + v2f offset = isyntax->images[0].levels[level].origin_offset; + x += (int64_t) (offset.x * isyntax->mpp_x); + y += (int64_t) (offset.y * isyntax->mpp_y); + // Calculate tile coordinates int32_t tile_width = libisyntax_get_tile_width(isyntax); int32_t tile_height = libisyntax_get_tile_height(isyntax); From 1fba046f405f1ee0a3ca45c3f1eb0626cf67ae1f Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Tue, 18 Apr 2023 16:53:35 +0200 Subject: [PATCH 26/38] - Make SIMD function also process sizes not divisible by 4. - Cleanup read_region function --- src/isyntax_example2.c | 20 ++++++++++---------- src/libisyntax.c | 12 ++++++------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/isyntax_example2.c b/src/isyntax_example2.c index e128bdc..3f91791 100644 --- a/src/isyntax_example2.c +++ b/src/isyntax_example2.c @@ -20,9 +20,10 @@ void bgra_to_rgba(uint32_t *pixels, int tile_width, int tile_height) { int num_pixels = tile_width * tile_height; + int num_pixels_aligned = (num_pixels / 4) * 4; #if defined(__ARM_NEON) - for (int i = 0; i < num_pixels; i += 4) { + for (int i = 0; i < num_pixels_aligned; i += 4) { uint32x4_t bgra = vld1q_u32(pixels + i); uint32x4_t b_mask = vdupq_n_u32(0x000000FF); uint32x4_t r_mask = vdupq_n_u32(0x00FF0000); @@ -35,7 +36,7 @@ void bgra_to_rgba(uint32_t *pixels, int tile_width, int tile_height) { vst1q_u32(pixels + i, rgba); } #elif defined(__SSE2__) - for (int i = 0; i < num_pixels; i += 4) { + for (int i = 0; i < num_pixels_aligned; i += 4) { __m128i bgra = _mm_loadu_si128((__m128i*)(pixels + i)); __m128i b_mask = _mm_set1_epi32(0x000000FF); __m128i r_mask = _mm_set1_epi32(0x00FF0000); @@ -48,7 +49,7 @@ void bgra_to_rgba(uint32_t *pixels, int tile_width, int tile_height) { _mm_storeu_si128((__m128i*)(pixels + i), rgba); } #else - for (int i = 0; i < num_pixels; ++i) { + for (int i = num_pixels_aligned; i < num_pixels; ++i) { uint32_t val = pixels[i]; pixels[i] = ((val & 0xff) << 16) | (val & 0x00ff00) | ((val & 0xff0000) >> 16) | (val & 0xff000000); } @@ -58,7 +59,7 @@ void bgra_to_rgba(uint32_t *pixels, int tile_width, int tile_height) { int main(int argc, char **argv) { if (argc <= 7) { - printf("Usage: %s - write a tile to output.png", + printf("Usage: %s - write a tile to output.png", argv[0], argv[0]); return 0; } @@ -76,15 +77,15 @@ int main(int argc, char **argv) { int32_t level = atoi(argv[2]); - int32_t tile_x = atoi(argv[3]); - int32_t tile_y = atoi(argv[4]); + int32_t x_coord = atoi(argv[3]); + int32_t y_coord = atoi(argv[4]); int32_t region_width = atoi(argv[5]); int32_t region_height = atoi(argv[6]); const char *output_png = argv[7]; LOG_VAR("%d", level); - LOG_VAR("%d", tile_x); - LOG_VAR("%d", tile_y); + LOG_VAR("%d", x_coord); + LOG_VAR("%d", y_coord); LOG_VAR("%d", region_width); LOG_VAR("%d", region_height); LOG_VAR("%s", output_png); @@ -98,9 +99,8 @@ int main(int argc, char **argv) { assert(libisyntax_cache_create("example cache", 2000, &isyntax_cache) == LIBISYNTAX_OK); assert(libisyntax_cache_inject(isyntax_cache, isyntax) == LIBISYNTAX_OK); - uint32_t *pixels = NULL; - assert(libisyntax_read_region(isyntax, isyntax_cache, 8, 0, 0, region_width, region_height, &pixels) == + assert(libisyntax_read_region(isyntax, isyntax_cache, level, x_coord, y_coord, region_width, region_height, &pixels) == LIBISYNTAX_OK); // convert data to the correct pixel format (bgra->rgba). diff --git a/src/libisyntax.c b/src/libisyntax.c index 8e5fd44..6c1af97 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -303,13 +303,13 @@ isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyn y += (int64_t) (offset.y * isyntax->mpp_y); // Calculate tile coordinates - int32_t tile_width = libisyntax_get_tile_width(isyntax); - int32_t tile_height = libisyntax_get_tile_height(isyntax); + int32_t tile_width = isyntax->tile_width; + int32_t tile_height = isyntax->tile_height; - int32_t start_tile_x = x / tile_width; - int32_t end_tile_x = (x + width - 1) / tile_width; - int32_t start_tile_y = y / tile_height; - int32_t end_tile_y = (y + height - 1) / tile_height; + int64_t start_tile_x = x / tile_width; + int64_t end_tile_x = (x + width - 1) / tile_width; + int64_t start_tile_y = y / tile_height; + int64_t end_tile_y = (y + height - 1) / tile_height; // Allocate memory for region *out_pixels = (uint32_t*)malloc(width * height * sizeof(uint32_t)); From e006cea98492f8d1c88eb8b3d4643b390178abcc Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Tue, 18 Apr 2023 17:09:40 +0200 Subject: [PATCH 27/38] Refactor BGRA to RGBA function. --- src/isyntax/isyntax.c | 38 ++++++++++++++++++++++++++++++- src/isyntax_example.c | 9 +------- src/isyntax_example2.c | 51 +----------------------------------------- src/libisyntax.h | 4 +++- 4 files changed, 42 insertions(+), 60 deletions(-) diff --git a/src/isyntax/isyntax.c b/src/isyntax/isyntax.c index 509c8fc..a3c0b19 100644 --- a/src/isyntax/isyntax.c +++ b/src/isyntax/isyntax.c @@ -3297,4 +3297,40 @@ void isyntax_destroy(isyntax_t* isyntax) { file_handle_close(isyntax->file_handle); } - +void bgra_to_rgba(uint32_t *pixels, int tile_width, int tile_height) { + int num_pixels = tile_width * tile_height; + int num_pixels_aligned = (num_pixels / 4) * 4; + +#if defined(__ARM_NEON) + for (int i = 0; i < num_pixels_aligned; i += 4) { + uint32x4_t bgra = vld1q_u32(pixels + i); + uint32x4_t b_mask = vdupq_n_u32(0x000000FF); + uint32x4_t r_mask = vdupq_n_u32(0x00FF0000); + uint32x4_t b = vandq_u32(bgra, b_mask); + uint32x4_t r = vandq_u32(bgra, r_mask); + uint32x4_t br_swapped = vorrq_u32(vshlq_n_u32(b, 16), vshrq_n_u32(r, 16)); + uint32x4_t ga_alpha_mask = vdupq_n_u32(0xFF00FF00); + uint32x4_t ga_alpha = vandq_u32(bgra, ga_alpha_mask); + uint32x4_t rgba = vorrq_u32(ga_alpha, br_swapped); + vst1q_u32(pixels + i, rgba); + } +#elif defined(__SSE2__) + for (int i = 0; i < num_pixels_aligned; i += 4) { + __m128i bgra = _mm_loadu_si128((__m128i*)(pixels + i)); + __m128i b_mask = _mm_set1_epi32(0x000000FF); + __m128i r_mask = _mm_set1_epi32(0x00FF0000); + __m128i b = _mm_and_si128(bgra, b_mask); + __m128i r = _mm_and_si128(bgra, r_mask); + __m128i br_swapped = _mm_or_si128(_mm_slli_epi32(b, 16), _mm_srli_epi32(r, 16)); + __m128i ga_alpha_mask = _mm_set1_epi32(0xFF00FF00); + __m128i ga_alpha = _mm_and_si128(bgra, ga_alpha_mask); + __m128i rgba = _mm_or_si128(ga_alpha, br_swapped); + _mm_storeu_si128((__m128i*)(pixels + i), rgba); + } +#else + for (int i = num_pixels_aligned; i < num_pixels; ++i) { + uint32_t val = pixels[i]; + pixels[i] = ((val & 0xff) << 16) | (val & 0x00ff00) | ((val & 0xff0000) >> 16) | (val & 0xff000000); + } +#endif +} diff --git a/src/isyntax_example.c b/src/isyntax_example.c index 3515090..af158ff 100644 --- a/src/isyntax_example.c +++ b/src/isyntax_example.c @@ -6,14 +6,8 @@ #define STB_IMAGE_WRITE_IMPLEMENTATION #include "third_party/stb_image_write.h" // for png export - - #define LOG_VAR(fmt, var) printf("%s: %s=" fmt "\n", __FUNCTION__, #var, var) -//uint32_t bgra_to_rgba(uint32_t val) { -// return ((val & 0xff) << 16) | (val & 0x00ff00) | ((val & 0xff0000) >> 16) | (val & 0xff000000); -//} - #include #if defined(__ARM_NEON) #include @@ -58,7 +52,6 @@ void bgra_to_rgba(uint32_t* pixels, int tile_width, int tile_height) { #endif } - void print_isyntax_levels(isyntax_t* isyntax) { int wsi_image_idx = libisyntax_get_wsi_image_index(isyntax); LOG_VAR("%d", wsi_image_idx); @@ -131,4 +124,4 @@ int main(int argc, char** argv) { libisyntax_close(isyntax); return 0; -} +} \ No newline at end of file diff --git a/src/isyntax_example2.c b/src/isyntax_example2.c index 3f91791..aa56f7a 100644 --- a/src/isyntax_example2.c +++ b/src/isyntax_example2.c @@ -1,5 +1,4 @@ #include "libisyntax.h" - #include #include @@ -7,54 +6,8 @@ #include "third_party/stb_image_write.h" // for png export -#if defined(__ARM_NEON) - -#include - -#elif defined(__SSE2__) -#include -#endif - - #define LOG_VAR(fmt, var) printf("%s: %s=" fmt "\n", __FUNCTION__, #var, var) -void bgra_to_rgba(uint32_t *pixels, int tile_width, int tile_height) { - int num_pixels = tile_width * tile_height; - int num_pixels_aligned = (num_pixels / 4) * 4; - -#if defined(__ARM_NEON) - for (int i = 0; i < num_pixels_aligned; i += 4) { - uint32x4_t bgra = vld1q_u32(pixels + i); - uint32x4_t b_mask = vdupq_n_u32(0x000000FF); - uint32x4_t r_mask = vdupq_n_u32(0x00FF0000); - uint32x4_t b = vandq_u32(bgra, b_mask); - uint32x4_t r = vandq_u32(bgra, r_mask); - uint32x4_t br_swapped = vorrq_u32(vshlq_n_u32(b, 16), vshrq_n_u32(r, 16)); - uint32x4_t ga_alpha_mask = vdupq_n_u32(0xFF00FF00); - uint32x4_t ga_alpha = vandq_u32(bgra, ga_alpha_mask); - uint32x4_t rgba = vorrq_u32(ga_alpha, br_swapped); - vst1q_u32(pixels + i, rgba); - } -#elif defined(__SSE2__) - for (int i = 0; i < num_pixels_aligned; i += 4) { - __m128i bgra = _mm_loadu_si128((__m128i*)(pixels + i)); - __m128i b_mask = _mm_set1_epi32(0x000000FF); - __m128i r_mask = _mm_set1_epi32(0x00FF0000); - __m128i b = _mm_and_si128(bgra, b_mask); - __m128i r = _mm_and_si128(bgra, r_mask); - __m128i br_swapped = _mm_or_si128(_mm_slli_epi32(b, 16), _mm_srli_epi32(r, 16)); - __m128i ga_alpha_mask = _mm_set1_epi32(0xFF00FF00); - __m128i ga_alpha = _mm_and_si128(bgra, ga_alpha_mask); - __m128i rgba = _mm_or_si128(ga_alpha, br_swapped); - _mm_storeu_si128((__m128i*)(pixels + i), rgba); - } -#else - for (int i = num_pixels_aligned; i < num_pixels; ++i) { - uint32_t val = pixels[i]; - pixels[i] = ((val & 0xff) << 16) | (val & 0x00ff00) | ((val & 0xff0000) >> 16) | (val & 0xff000000); - } -#endif -} int main(int argc, char **argv) { @@ -75,7 +28,6 @@ int main(int argc, char **argv) { } printf("Successfully opened %s\n", filename); - int32_t level = atoi(argv[2]); int32_t x_coord = atoi(argv[3]); int32_t y_coord = atoi(argv[4]); @@ -102,11 +54,10 @@ int main(int argc, char **argv) { uint32_t *pixels = NULL; assert(libisyntax_read_region(isyntax, isyntax_cache, level, x_coord, y_coord, region_width, region_height, &pixels) == LIBISYNTAX_OK); - // convert data to the correct pixel format (bgra->rgba). + // convert data to the correct pixel format (bgra->rgba). bgra_to_rgba(pixels, region_width, region_height); - printf("Writing %s...\n", output_png); stbi_write_png(output_png, region_width, region_height, 4, pixels, region_width * 4); printf("Done writing %s.\n", output_png); diff --git a/src/libisyntax.h b/src/libisyntax.h index 87ee11d..a74c157 100644 --- a/src/libisyntax.h +++ b/src/libisyntax.h @@ -57,9 +57,11 @@ isyntax_error_t libisyntax_cache_create(const char* debug_name_or_null, int32_t isyntax_error_t libisyntax_cache_inject(isyntax_cache_t* isyntax_cache, isyntax_t* isyntax); void libisyntax_cache_destroy(isyntax_cache_t* isyntax_cache); +//== Helpers === +void bgra_to_rgba(uint32_t *pixels, int tile_width, int tile_height); //== Tile API == -// Note: pixels are in BGRA layout. +// Note: pixels are in BGRA layout. Use bgra_to_rgba to convert. // Note: must use libisyntax_tile_free_pixels() to free out_pixels. isyntax_error_t libisyntax_tile_read(isyntax_t* isyntax, isyntax_cache_t* isyntax_cache, int32_t level, int64_t tile_x, int64_t tile_y, uint32_t** out_pixels); From 9edcfcc0115481c60069f4f786018676c6ef4c95 Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Wed, 19 Apr 2023 12:28:22 +0200 Subject: [PATCH 28/38] Skip empty tiles --- src/libisyntax.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/libisyntax.c b/src/libisyntax.c index 6c1af97..2a6dca2 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -302,7 +302,6 @@ isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyn x += (int64_t) (offset.x * isyntax->mpp_x); y += (int64_t) (offset.y * isyntax->mpp_y); - // Calculate tile coordinates int32_t tile_width = isyntax->tile_width; int32_t tile_height = isyntax->tile_height; @@ -311,6 +310,9 @@ isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyn int64_t start_tile_y = y / tile_height; int64_t end_tile_y = (y + height - 1) / tile_height; + // Get the level + isyntax_level_t* current_level = &isyntax->images[0].levels[level]; + // Allocate memory for region *out_pixels = (uint32_t*)malloc(width * height * sizeof(uint32_t)); @@ -318,27 +320,38 @@ isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyn for (int64_t tile_y = start_tile_y; tile_y <= end_tile_y; ++tile_y) { for (int64_t tile_x = start_tile_x; tile_x <= end_tile_x; ++tile_x) { - // Read tile - uint32_t* pixels = NULL; - assert(libisyntax_tile_read(isyntax, isyntax_cache, level, tile_x, tile_y, &pixels) == LIBISYNTAX_OK); + // Check if tile exists + int64_t tile_index = tile_y * current_level->width_in_tiles + tile_x; + bool tile_exists = (isyntax->images[0].levels[level].tiles + tile_index)->exists; // Calculate the portion of the tile to be copied int64_t src_x = (tile_x == start_tile_x) ? x % tile_width : 0; int64_t src_y = (tile_y == start_tile_y) ? y % tile_height : 0; int64_t dest_x = (tile_x == start_tile_x) ? 0 : (tile_x - start_tile_x) * tile_width - (x % tile_width); - int64_t dest_y = (tile_y == start_tile_y) ? 0 : (tile_y - start_tile_y) * tile_height - (y % tile_height); + int64_t dest_y = (tile_y == start_tile_y) ? 0 : (tile_y - start_tile_y) * tile_height - + (y % tile_height); int64_t copy_width = (tile_x == end_tile_x) ? (x + width) % tile_width : tile_width - src_x; int64_t copy_height = (tile_y == end_tile_y) ? (y + height) % tile_height : tile_height - src_y; - // Copy the relevant portion of the tile to the region - for (int64_t i = 0; i < copy_height; ++i) { - memcpy((*out_pixels) + (dest_y + i) * width + dest_x, - pixels + (src_y + i) * tile_width + src_x, - copy_width * sizeof(uint32_t)); + uint32_t *pixels = NULL; + if (tile_exists) { + // Read tile + assert(libisyntax_tile_read(isyntax, isyntax_cache, level, tile_x, tile_y, &pixels) == LIBISYNTAX_OK); + // Copy the relevant portion of the tile to the region + for (int64_t i = 0; i < copy_height; ++i) { + memcpy((*out_pixels) + (dest_y + i) * width + dest_x, + pixels + (src_y + i) * tile_width + src_x, + copy_width * sizeof(uint32_t)); + } + } else { + // Fill up with transparent pixels (R, G, B, A=0) + for (int64_t i = 0; i < copy_height; ++i) { + for (int64_t j = 0; j < copy_width; ++j) { + (*out_pixels)[(dest_y + i) * width + dest_x + j] = 0x00000000; + } + } } - - // Free the tile data free(pixels); } From 345a913d4b2401f9a5e565096ea431740cde8403 Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Wed, 19 Apr 2023 13:18:00 +0200 Subject: [PATCH 29/38] Improve offset computation --- src/libisyntax.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libisyntax.c b/src/libisyntax.c index 2a6dca2..47f09c9 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -296,11 +296,13 @@ isyntax_error_t libisyntax_tile_read(isyntax_t* isyntax, isyntax_cache_t* isynta isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyntax_cache, int32_t level, int64_t x, int64_t y, int64_t width, int64_t height, uint32_t** out_pixels) { - // Compute the offset - // TODO: Is this always in [0]? - v2f offset = isyntax->images[0].levels[level].origin_offset; - x += (int64_t) (offset.x * isyntax->mpp_x); - y += (int64_t) (offset.y * isyntax->mpp_y); + // TODO: Borrow this value from elsewhere + int PER_LEVEL_PADDING = 3; + int32_t num_levels = isyntax->images[0].level_count; + // TODO: This is probably a property of the isyntax->images[0].levels + int32_t offset = ((PER_LEVEL_PADDING << num_levels) - PER_LEVEL_PADDING) >> level; + x += offset; + y += offset; int32_t tile_width = isyntax->tile_width; int32_t tile_height = isyntax->tile_height; From 5612d5ae2c87a0d550510f57b26692f85e5453ac Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Wed, 19 Apr 2023 13:32:26 +0200 Subject: [PATCH 30/38] Compute offset differently (and fix rgb conversion in example) --- src/isyntax/isyntax.c | 4 ++-- src/isyntax/isyntax.h | 2 +- src/isyntax_example.c | 2 +- src/libisyntax.c | 15 ++++++--------- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/isyntax/isyntax.c b/src/isyntax/isyntax.c index a3c0b19..f04a500 100644 --- a/src/isyntax/isyntax.c +++ b/src/isyntax/isyntax.c @@ -3071,9 +3071,9 @@ bool isyntax_open(isyntax_t* isyntax, const char* filename, bool init_allocators // (this is also reflected in the x and y coordinates of the codeblocks in the iSyntax header). for (i32 scale = 1; scale < wsi_image->level_count; ++scale) { isyntax_level_t* level = wsi_image->levels + scale; + level->origin_offset_in_pixels = ((PER_LEVEL_PADDING << wsi_image->level_count) - PER_LEVEL_PADDING) >> scale; float offset_in_pixels = (float) (get_first_valid_coef_pixel(scale - 1)); - level->origin_offset_in_pixels = offset_in_pixels; - float offset_in_um_x = offset_in_pixels * wsi_image->levels[0].um_per_pixel_x; + float offset_in_um_x = offset_in_pixels * wsi_image->levels[0].um_per_pixel_x; float offset_in_um_y = offset_in_pixels * wsi_image->levels[0].um_per_pixel_y; level->origin_offset = (v2f){offset_in_um_x, offset_in_um_y}; } diff --git a/src/isyntax/isyntax.h b/src/isyntax/isyntax.h index 7f9d359..19765b1 100644 --- a/src/isyntax/isyntax.h +++ b/src/isyntax/isyntax.h @@ -281,7 +281,7 @@ typedef struct isyntax_level_t { float x_tile_side_in_um; float y_tile_side_in_um; u64 tile_count; - float origin_offset_in_pixels; + i32 origin_offset_in_pixels; v2f origin_offset; isyntax_tile_t* tiles; bool is_fully_loaded; diff --git a/src/isyntax_example.c b/src/isyntax_example.c index af158ff..123c24b 100644 --- a/src/isyntax_example.c +++ b/src/isyntax_example.c @@ -112,7 +112,7 @@ int main(int argc, char** argv) { assert(libisyntax_tile_read(isyntax, isyntax_cache, level, tile_x, tile_y, &pixels) == LIBISYNTAX_OK); // convert data to the correct pixel format (bgra->rgba). - bgra_to_rgba(pixels, tile_height, tile_width); + bgra_to_rgba(pixels, tile_width, tile_height); printf("Writing %s...\n", output_png); stbi_write_png(output_png, tile_width, tile_height, 4, pixels, tile_width * 4); diff --git a/src/libisyntax.c b/src/libisyntax.c index 47f09c9..fed2f97 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -296,13 +296,12 @@ isyntax_error_t libisyntax_tile_read(isyntax_t* isyntax, isyntax_cache_t* isynta isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyntax_cache, int32_t level, int64_t x, int64_t y, int64_t width, int64_t height, uint32_t** out_pixels) { - // TODO: Borrow this value from elsewhere - int PER_LEVEL_PADDING = 3; - int32_t num_levels = isyntax->images[0].level_count; - // TODO: This is probably a property of the isyntax->images[0].levels - int32_t offset = ((PER_LEVEL_PADDING << num_levels) - PER_LEVEL_PADDING) >> level; - x += offset; - y += offset; + // Get the level + isyntax_level_t* current_level = &isyntax->images[0].levels[level]; + + // Setup the origin offset + x += current_level->origin_offset_in_pixels; + y += current_level->origin_offset_in_pixels; int32_t tile_width = isyntax->tile_width; int32_t tile_height = isyntax->tile_height; @@ -312,8 +311,6 @@ isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyn int64_t start_tile_y = y / tile_height; int64_t end_tile_y = (y + height - 1) / tile_height; - // Get the level - isyntax_level_t* current_level = &isyntax->images[0].levels[level]; // Allocate memory for region *out_pixels = (uint32_t*)malloc(width * height * sizeof(uint32_t)); From e1d9666d2869f8b42569b3d19e4414374592a75d Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Wed, 19 Apr 2023 13:51:41 +0200 Subject: [PATCH 31/38] Reformat code slightly --- src/libisyntax.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/libisyntax.c b/src/libisyntax.c index fed2f97..f45deba 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -311,18 +311,12 @@ isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyn int64_t start_tile_y = y / tile_height; int64_t end_tile_y = (y + height - 1) / tile_height; - // Allocate memory for region *out_pixels = (uint32_t*)malloc(width * height * sizeof(uint32_t)); // Read tiles and copy the relevant portion of each tile to the region for (int64_t tile_y = start_tile_y; tile_y <= end_tile_y; ++tile_y) { for (int64_t tile_x = start_tile_x; tile_x <= end_tile_x; ++tile_x) { - - // Check if tile exists - int64_t tile_index = tile_y * current_level->width_in_tiles + tile_x; - bool tile_exists = (isyntax->images[0].levels[level].tiles + tile_index)->exists; - // Calculate the portion of the tile to be copied int64_t src_x = (tile_x == start_tile_x) ? x % tile_width : 0; int64_t src_y = (tile_y == start_tile_y) ? y % tile_height : 0; @@ -333,6 +327,11 @@ isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyn int64_t copy_height = (tile_y == end_tile_y) ? (y + height) % tile_height : tile_height - src_y; uint32_t *pixels = NULL; + + // Check if tile exists, if not, don't use the function to read the tile and immediately return an empty + // tile. + int64_t tile_index = tile_y * current_level->width_in_tiles + tile_x; + bool tile_exists = (isyntax->images[0].levels[level].tiles + tile_index)->exists; if (tile_exists) { // Read tile assert(libisyntax_tile_read(isyntax, isyntax_cache, level, tile_x, tile_y, &pixels) == LIBISYNTAX_OK); From 3319ae034a2e74f598ba732464ef4eb966ef7101 Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Wed, 19 Apr 2023 15:13:26 +0200 Subject: [PATCH 32/38] Check some bounds: - Added width and height to properties of level - Check if within bounds (temporary) --- src/isyntax/isyntax.c | 2 ++ src/isyntax/isyntax.h | 2 ++ src/libisyntax.c | 18 +++++++++++++++++- src/libisyntax.h | 2 ++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/isyntax/isyntax.c b/src/isyntax/isyntax.c index f04a500..eeca092 100644 --- a/src/isyntax/isyntax.c +++ b/src/isyntax/isyntax.c @@ -3072,6 +3072,8 @@ bool isyntax_open(isyntax_t* isyntax, const char* filename, bool init_allocators for (i32 scale = 1; scale < wsi_image->level_count; ++scale) { isyntax_level_t* level = wsi_image->levels + scale; level->origin_offset_in_pixels = ((PER_LEVEL_PADDING << wsi_image->level_count) - PER_LEVEL_PADDING) >> scale; + level->width = wsi_image->width >> scale; + level->height = wsi_image->height >> scale; float offset_in_pixels = (float) (get_first_valid_coef_pixel(scale - 1)); float offset_in_um_x = offset_in_pixels * wsi_image->levels[0].um_per_pixel_x; float offset_in_um_y = offset_in_pixels * wsi_image->levels[0].um_per_pixel_y; diff --git a/src/isyntax/isyntax.h b/src/isyntax/isyntax.h index 19765b1..52ac1d5 100644 --- a/src/isyntax/isyntax.h +++ b/src/isyntax/isyntax.h @@ -275,6 +275,8 @@ typedef struct isyntax_level_t { i32 scale; i32 width_in_tiles; i32 height_in_tiles; + u64 width; + u64 height; float downsample_factor; float um_per_pixel_x; float um_per_pixel_y; diff --git a/src/libisyntax.c b/src/libisyntax.c index f45deba..c5146e3 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -218,6 +218,15 @@ int32_t libisyntax_level_get_scale(const isyntax_level_t* level) { return level->scale; } +// TODO(jt): Signature equivalent with openslide would be openslide_get_level_dimensions(isyntax_image_t *isyntax, int32_t level, int64_t *w, int64_t *h) { +int32_t libisyntax_level_get_width(const isyntax_level_t* level) { + return level->width; +} + +int32_t libisyntax_level_get_height(const isyntax_level_t* level) { + return level->height; +} + int32_t libisyntax_level_get_width_in_tiles(const isyntax_level_t* level) { return level->width_in_tiles; } @@ -296,13 +305,20 @@ isyntax_error_t libisyntax_tile_read(isyntax_t* isyntax, isyntax_cache_t* isynta isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyntax_cache, int32_t level, int64_t x, int64_t y, int64_t width, int64_t height, uint32_t** out_pixels) { + // Get the level + assert(level < &isyntax->images[0].level_count); isyntax_level_t* current_level = &isyntax->images[0].levels[level]; - + // Setup the origin offset x += current_level->origin_offset_in_pixels; y += current_level->origin_offset_in_pixels; + // Check bounds + // TODO: Figure out if the bounds are starting from x = 0 or from the offset + assert(x + width <= current_level->width); + assert(y + height <= current_level->height); + int32_t tile_width = isyntax->tile_width; int32_t tile_height = isyntax->tile_height; diff --git a/src/libisyntax.h b/src/libisyntax.h index a74c157..44df522 100644 --- a/src/libisyntax.h +++ b/src/libisyntax.h @@ -46,6 +46,8 @@ const isyntax_level_t* libisyntax_image_get_level(const isyntax_image_t* image, int32_t libisyntax_level_get_scale(const isyntax_level_t* level); int32_t libisyntax_level_get_width_in_tiles(const isyntax_level_t* level); int32_t libisyntax_level_get_height_in_tiles(const isyntax_level_t* level); +int32_t libisyntax_level_get_width(const isyntax_level_t* level); +int32_t libisyntax_level_get_height(const isyntax_level_t* level); //== Cache API == isyntax_error_t libisyntax_cache_create(const char* debug_name_or_null, int32_t cache_size, From 4c239e653c1b6f60ca75185952c727a84ad4b0c7 Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Thu, 20 Apr 2023 12:13:09 +0200 Subject: [PATCH 33/38] Additions: - Tiles should be white when missing - Expose mpp_x and mpp_y --- src/libisyntax.c | 24 ++++++++++++++++-------- src/libisyntax.h | 2 ++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/libisyntax.c b/src/libisyntax.c index c5146e3..3f4e91d 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -218,6 +218,14 @@ int32_t libisyntax_level_get_scale(const isyntax_level_t* level) { return level->scale; } +float libisyntax_level_get_mpp_x(const isyntax_level_t* level) { + return level->um_per_pixel_x; +} + +float libisyntax_level_get_mpp_y(const isyntax_level_t* level) { + return level->um_per_pixel_y; +} + // TODO(jt): Signature equivalent with openslide would be openslide_get_level_dimensions(isyntax_image_t *isyntax, int32_t level, int64_t *w, int64_t *h) { int32_t libisyntax_level_get_width(const isyntax_level_t* level) { return level->width; @@ -305,19 +313,19 @@ isyntax_error_t libisyntax_tile_read(isyntax_t* isyntax, isyntax_cache_t* isynta isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyntax_cache, int32_t level, int64_t x, int64_t y, int64_t width, int64_t height, uint32_t** out_pixels) { - // Get the level assert(level < &isyntax->images[0].level_count); isyntax_level_t* current_level = &isyntax->images[0].levels[level]; - + // Setup the origin offset - x += current_level->origin_offset_in_pixels; - y += current_level->origin_offset_in_pixels; + int32_t offset = current_level->origin_offset_in_pixels; + x += offset; + y += offset; // Check bounds // TODO: Figure out if the bounds are starting from x = 0 or from the offset - assert(x + width <= current_level->width); - assert(y + height <= current_level->height); + assert(x + width - offset <= current_level->width); + assert(y + height - offset <= current_level->height); int32_t tile_width = isyntax->tile_width; int32_t tile_height = isyntax->tile_height; @@ -358,10 +366,10 @@ isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyn copy_width * sizeof(uint32_t)); } } else { - // Fill up with transparent pixels (R, G, B, A=0) + // Fill up with transparent white pixels (R=255, G=255, B=255, A=0) for (int64_t i = 0; i < copy_height; ++i) { for (int64_t j = 0; j < copy_width; ++j) { - (*out_pixels)[(dest_y + i) * width + dest_x + j] = 0x00000000; + (*out_pixels)[(dest_y + i) * width + dest_x + j] = 0x00FFFFFF; } } } diff --git a/src/libisyntax.h b/src/libisyntax.h index 44df522..4277c82 100644 --- a/src/libisyntax.h +++ b/src/libisyntax.h @@ -48,6 +48,8 @@ int32_t libisyntax_level_get_width_in_tiles(const isyntax_level_t int32_t libisyntax_level_get_height_in_tiles(const isyntax_level_t* level); int32_t libisyntax_level_get_width(const isyntax_level_t* level); int32_t libisyntax_level_get_height(const isyntax_level_t* level); +float libisyntax_level_get_mpp_x(const isyntax_level_t* level); +float libisyntax_level_get_mpp_y(const isyntax_level_t* level); //== Cache API == isyntax_error_t libisyntax_cache_create(const char* debug_name_or_null, int32_t cache_size, From 1ed0185b69900b7b906e9a0182710415037d5075 Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Thu, 20 Apr 2023 12:26:31 +0200 Subject: [PATCH 34/38] Initial version of isyntax -> tiff --- CMakeLists.txt | 26 ++- src/isyntax_to_tiff.c | 532 ++++++++++++++++++++---------------------- 2 files changed, 276 insertions(+), 282 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1804bcc..0527450 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,14 @@ find_package(PkgConfig REQUIRED) pkg_check_modules(VIPS REQUIRED vips) +# Find LibTIFF library +find_package(TIFF REQUIRED) +if (NOT TIFF_FOUND) + message(WARNING "LibTIFF not found") + message(WARNING "Will not compile `isyntax-to-tiff` utility") +endif() +include_directories(${TIFF_INCLUDE_DIR}) + set(LIBISYNTAX_COMMON_SOURCE_FILES src/libisyntax.c src/isyntax/isyntax.c @@ -69,9 +77,21 @@ target_link_libraries(isyntax-to-tiff PRIVATE ${VIPS_LIBRARIES}) add_executable(isyntax_example2 - src/isyntax_example2.c - ${LIBISYNTAX_COMMON_SOURCE_FILES} - ) + src/isyntax_example2.c + ${LIBISYNTAX_COMMON_SOURCE_FILES} +) + +if (TIFF_FOUND) + message(STATUS "Will compile `isyntax-to-tiff` utility") + add_executable(isyntax-to-tiff + src/isyntax_to_tiff.c + ${LIBISYNTAX_COMMON_SOURCE_FILES} ${TIFF_LIBRARIES}) + +endif() + +target_include_directories(isyntax-to-tiff PRIVATE ${TIFF_INCLUDE_DIRS}) +target_link_libraries(isyntax-to-tiff PRIVATE ${TIFF_LIBRARIES}) + if (WIN32) target_link_libraries(libisyntax winmm) diff --git a/src/isyntax_to_tiff.c b/src/isyntax_to_tiff.c index 6e79573..cbd4cca 100644 --- a/src/isyntax_to_tiff.c +++ b/src/isyntax_to_tiff.c @@ -1,334 +1,308 @@ #include "libisyntax.h" - +#include "tiffio.h" #include #include +#include #include -#include -#include -#include +#include -#define LOG_VAR(fmt, var) printf("%s: %s=" fmt "\n", __FUNCTION__, #var, var) +#define LOG_VAR(fmt, var) printf("%s: %s=" fmt "\n", __FUNCTION__, #var, var) -#include -#if defined(__ARM_NEON) -#include -#elif defined(__SSE2__) -#include -#endif - -void bgra_to_rgba(uint32_t* pixels, int tile_width, int tile_height) { - int num_pixels = tile_width * tile_height; - -#if defined(__ARM_NEON) - for (int i = 0; i < num_pixels; i += 4) { - uint32x4_t bgra = vld1q_u32(pixels + i); - uint32x4_t b_mask = vdupq_n_u32(0x000000FF); - uint32x4_t r_mask = vdupq_n_u32(0x00FF0000); - uint32x4_t b = vandq_u32(bgra, b_mask); - uint32x4_t r = vandq_u32(bgra, r_mask); - uint32x4_t br_swapped = vorrq_u32(vshlq_n_u32(b, 16), vshrq_n_u32(r, 16)); - uint32x4_t ga_alpha_mask = vdupq_n_u32(0xFF00FF00); - uint32x4_t ga_alpha = vandq_u32(bgra, ga_alpha_mask); - uint32x4_t rgba = vorrq_u32(ga_alpha, br_swapped); - vst1q_u32(pixels + i, rgba); - } -#elif defined(__SSE2__) - for (int i = 0; i < num_pixels; i += 4) { - __m128i bgra = _mm_loadu_si128((__m128i*)(pixels + i)); - __m128i b_mask = _mm_set1_epi32(0x000000FF); - __m128i r_mask = _mm_set1_epi32(0x00FF0000); - __m128i b = _mm_and_si128(bgra, b_mask); - __m128i r = _mm_and_si128(bgra, r_mask); - __m128i br_swapped = _mm_or_si128(_mm_slli_epi32(b, 16), _mm_srli_epi32(r, 16)); - __m128i ga_alpha_mask = _mm_set1_epi32(0xFF00FF00); - __m128i ga_alpha = _mm_and_si128(bgra, ga_alpha_mask); - __m128i rgba = _mm_or_si128(ga_alpha, br_swapped); - _mm_storeu_si128((__m128i*)(pixels + i), rgba); - } -#else - for (int i = 0; i < num_pixels; ++i) { - uint32_t val = pixels[i]; - pixels[i] = ((val & 0xff) << 16) | (val & 0x00ff00) | ((val & 0xff0000) >> 16) | (val & 0xff000000); - } -#endif +void update_progress(int32_t total_progress, int32_t page_progress, int32_t page_number, double eta) { + printf("\rProgress: %3d%% | Page %d progress: %3d%% | ETA: %.0fs", total_progress, page_number, page_progress, eta); + fflush(stdout); } - -typedef struct _VipsForeignLoadIsyntax { - VipsForeignLoad parent_object; - isyntax_t *isyntax; - isyntax_cache_t *isyntax_cache; - const isyntax_image_t *wsi_image; - const isyntax_level_t *level; - int32_t tile_width; - int32_t tile_height; - int32_t num_tiles_x; - int32_t num_tiles_y; -} VipsForeignLoadIsyntax; - -typedef VipsForeignLoadClass VipsForeignLoadIsyntaxClass; - -G_DEFINE_TYPE(VipsForeignLoadIsyntax, vips_foreign_load_isyntax, VIPS_TYPE_FOREIGN_LOAD); - - -//static int -//isyntax_generate(VipsRegion *out, void *seq, void *a, void *b, gboolean *stop) { -// VipsForeignLoadIsyntax *isyntax = (VipsForeignLoadIsyntax *) a; -// VipsRect *r = &out->valid; -// -// int32_t tile_width = isyntax->tile_width; -// int32_t tile_height = isyntax->tile_height; -// -// int32_t level = 0; -// uint32_t *pixels = NULL; -// -// // Calculate the tile range for the region -// int32_t tile_start_x = r->left / tile_width; -// int32_t tile_end_x = (r->left + r->width + tile_width - 1) / tile_width; -// int32_t tile_start_y = r->top / tile_height; -// int32_t tile_end_y = (r->top + r->height + tile_height - 1) / tile_height; -// -// for (int32_t tile_y = tile_start_y; tile_y < tile_end_y; ++tile_y) { -// for (int32_t tile_x = tile_start_x; tile_x < tile_end_x; ++tile_x) { -// assert(libisyntax_tile_read(isyntax->isyntax, isyntax->isyntax_cache, level, tile_x, tile_y, &pixels) == LIBISYNTAX_OK); -// bgra_to_rgba(pixels, tile_width, tile_height); -// -// // Calculate the intersection of the region and the tile -// VipsRect tile_rect = { -// .left = tile_x * tile_width, -// .top = tile_y * tile_height, -// .width = tile_width, -// .height = tile_height -// }; -// VipsRect intersection; -// vips_rect_intersectrect(r, &tile_rect, &intersection); -// -// // Copy the intersection area from the tile to the output region -// for (int y = intersection.top; y < VIPS_RECT_BOTTOM(&intersection); y++) { -// uint32_t *p = (uint32_t *) VIPS_REGION_ADDR(out, intersection.left, y); -// uint32_t *q = pixels + (y - tile_rect.top) * tile_width + (intersection.left - tile_rect.left); -// -// memcpy(p, q, intersection.width * sizeof(uint32_t)); -// } -// } -// } -// -// // Free the pixels buffer if needed -// if (pixels) { -// free(pixels); -// /* Free the pixels buffer */ -// } -// -// return (0); -//} - -static void -read_region(VipsForeignLoadIsyntax *isyntax, VipsRegion *out, const VipsRect *r) { - int32_t tile_width = isyntax->tile_width; - int32_t tile_height = isyntax->tile_height; - - int32_t level = 0; - uint32_t *pixels = NULL; - - // Calculate the tile range for the region - int32_t tile_start_x = r->left / tile_width; - int32_t tile_end_x = (r->left + r->width + tile_width - 1) / tile_width; - int32_t tile_start_y = r->top / tile_height; - int32_t tile_end_y = (r->top + r->height + tile_height - 1) / tile_height; - - for (int32_t tile_y = tile_start_y; tile_y < tile_end_y; ++tile_y) { - for (int32_t tile_x = tile_start_x; tile_x < tile_end_x; ++tile_x) { - assert(libisyntax_tile_read(isyntax->isyntax, isyntax->isyntax_cache, level, tile_x, tile_y, &pixels) == LIBISYNTAX_OK); - bgra_to_rgba(pixels, tile_width, tile_height); - - // Calculate the intersection of the region and the tile - VipsRect tile_rect = { - .left = tile_x * tile_width, - .top = tile_y * tile_height, - .width = tile_width, - .height = tile_height - }; - VipsRect intersection; - vips_rect_intersectrect(r, &tile_rect, &intersection); - - // Copy the intersection area from the tile to the output region - for (int y = intersection.top; y < VIPS_RECT_BOTTOM(&intersection); y++) { - uint32_t *p = (uint32_t *) VIPS_REGION_ADDR(out, intersection.left, y); - uint32_t *q = pixels + (y - tile_rect.top) * tile_width + (intersection.left - tile_rect.left); - - memcpy(p, q, intersection.width * sizeof(uint32_t)); - } - } +void write_page_to_tiff(TIFF *output_tiff, isyntax_t *isyntax, isyntax_cache_t *isyntax_cache, isyntax_level_t *level, + int32_t tile_width, int32_t tile_height, int32_t *total_tiles_written, int32_t total_tiles, + clock_t global_start_time, uint16_t compression_type, uint16_t quality) { + int32_t width = libisyntax_level_get_width(level); + int32_t height = libisyntax_level_get_height(level); + int32_t scale = libisyntax_level_get_scale(level); + + // Set the TIFF properties for the current level. + TIFFSetField(output_tiff, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField(output_tiff, TIFFTAG_IMAGELENGTH, height); + TIFFSetField(output_tiff, TIFFTAG_BITSPERSAMPLE, 8); + TIFFSetField(output_tiff, TIFFTAG_SAMPLESPERPIXEL, 4); + + if (compression_type == COMPRESSION_JPEG) { + TIFFSetField(output_tiff, TIFFTAG_COMPRESSION, COMPRESSION_JPEG); + TIFFSetField(output_tiff, TIFFTAG_JPEGQUALITY, quality); + } else if (compression_type == COMPRESSION_LZW) { + TIFFSetField(output_tiff, TIFFTAG_COMPRESSION, COMPRESSION_LZW); } - // Free the pixels buffer if needed - if (pixels) { - free(pixels); + TIFFSetField(output_tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + TIFFSetField(output_tiff, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(output_tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(output_tiff, TIFFTAG_TILEWIDTH, tile_width); + TIFFSetField(output_tiff, TIFFTAG_TILELENGTH, tile_height); + + // Set the resolution + double res_x = 10000.0 / libisyntax_level_get_mpp_x(level); + TIFFSetField(output_tiff, TIFFTAG_XRESOLUTION, res_x); + double res_y = 10000.0 / libisyntax_level_get_mpp_y(level); + TIFFSetField(output_tiff, TIFFTAG_YRESOLUTION, res_y); + TIFFSetField(output_tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_CENTIMETER); + TIFFSetField(output_tiff, TIFFTAG_EXTRASAMPLES, 1, (uint16_t[]) {EXTRASAMPLE_UNASSALPHA}); + + if (level == 0) { + TIFFSetField(output_tiff, TIFFTAG_SUBFILETYPE, 0); + } else { + TIFFSetField(output_tiff, TIFFTAG_SUBFILETYPE, FILETYPE_REDUCEDIMAGE); } -} - -static int -isyntax_generate(VipsRegion *out, void *seq, void *a, void *b, gboolean *stop) { - VipsForeignLoadIsyntax *isyntax = (VipsForeignLoadIsyntax *) a; - VipsRect *r = &out->valid; - - read_region(isyntax, out, r); - - return 0; -} + int32_t tile_progress = 0; + int32_t tiles_in_page = ((height - 1) / tile_height) * ((width - 1) / tile_width) + 2; -/* Header function for VipsForeignLoadIsyntax */ -static int -vips_foreign_load_isyntax_header(VipsForeignLoad *load) { - VipsForeignLoadIsyntax *isyntax_load = (VipsForeignLoadIsyntax *)load; - isyntax_t *isyntax = isyntax_load->isyntax; - isyntax_cache_t *isyntax_cache = isyntax_load->isyntax_cache; - const isyntax_image_t *wsi_image = isyntax_load->wsi_image; - const isyntax_level_t *level = isyntax_load->level; + for (int32_t y_coord = 0; y_coord < height; y_coord += tile_height) { + for (int32_t x_coord = 0; x_coord < width; x_coord += tile_width) { + // At the borders the tile size can be smaller + int32_t region_width = (x_coord + tile_width > width) ? width - x_coord : tile_width; + int32_t region_height = (y_coord + tile_height > height) ? height - y_coord : tile_height; + uint32_t *pixels = NULL; + assert(libisyntax_read_region(isyntax, isyntax_cache, scale, x_coord, y_coord, region_width, region_height, + &pixels) == LIBISYNTAX_OK); - // Set the image properties using the appropriate libisyntax functions + // Convert data to the correct pixel format (bgra->rgba). + bgra_to_rgba(pixels, region_width, region_height); - int width = libisyntax_get_(level); - int height = 100; // libisyntax_level_get_height(level); - int bands = 4; // Assuming RGBA + uint32_t *tile_pixels = pixels; - vips_image_init_fields(load->out, - width, - height, - bands, - VIPS_FORMAT_UCHAR, // Adjust this according to your image format - VIPS_CODING_NONE, - VIPS_INTERPRETATION_sRGB, // Adjust this according to your image interpretation - 1.0, 1.0); - - return 0; -} + if (region_width != tile_width || region_height != tile_height) { + tile_pixels = calloc(tile_width * tile_height, sizeof(uint32_t)); + for (int32_t row = 0; row < region_height; ++row) { + memcpy(tile_pixels + row * tile_width, pixels + row * region_width, + region_width * sizeof(uint32_t)); + } + } -/* Load function for VipsForeignLoadIsyntax */ -static int -vips_foreign_load_isyntax_load(VipsForeignLoad *load) { - VipsForeignLoadIsyntax *isyntax = (VipsForeignLoadIsyntax *)load; + // Write the tile to the output TIFF. + TIFFWriteTile(output_tiff, tile_pixels, x_coord, y_coord, 0, 0); - VipsImage **t = (VipsImage **)vips_object_local_array(VIPS_OBJECT(load), 1); - t[0] = vips_image_new(); + ++tile_progress; + int32_t tile_percent = (tile_progress * 100) / tiles_in_page; + int32_t total_progress = ((*total_tiles_written + tile_progress) * 100) / total_tiles; - // Call isyntax_generate function - if (vips_image_generate(t[0], - NULL, isyntax_generate, NULL, - isyntax, NULL)) { - return -1; - } + // Calculate ETA + clock_t current_global_time = clock(); + double elapsed_global_time = (double) (current_global_time - global_start_time) / CLOCKS_PER_SEC; + double avg_time_per_tile = elapsed_global_time / (*total_tiles_written + tile_progress); + double eta = avg_time_per_tile * (total_tiles - (*total_tiles_written + tile_progress)); + update_progress(total_progress, tile_percent, scale, eta); - if (vips_image_write(t[0], load->real)) { - return -1; + libisyntax_tile_free_pixels(pixels); + } } - return 0; + // Write the directory for the current level. + TIFFWriteDirectory(output_tiff); } -/* Class init function for VipsForeignLoadIsyntax */ -static void -vips_foreign_load_isyntax_class_init(VipsForeignLoadIsyntaxClass *klass) { - GObjectClass *gobject_class = G_OBJECT_CLASS(klass); - VipsObjectClass *object_class = VIPS_OBJECT_CLASS(klass); - VipsForeignLoadClass *foreign_load_class = (VipsForeignLoadClass *)klass; - - gobject_class->set_property = vips_object_set_property; - gobject_class->get_property = vips_object_get_property; - - object_class->nickname = "isyntaxload"; - object_class->description = "Load an isyntax image"; - - /* Add your properties here using vips_object_class_install_property() */ - - foreign_load_class->header = vips_foreign_load_isyntax_header; - foreign_load_class->load = vips_foreign_load_isyntax_load; -} +uint64_t parse_cache_size(const char *size_str) { + uint64_t size; + char unit; -/* Register the VipsForeignLoadIsyntax class with libvips */ -static void -vips_foreign_load_isyntax_register(void) { - static gboolean registered = FALSE; - static GStaticMutex mutex = G_STATIC_MUTEX_INIT; - - if (!registered) { - g_static_mutex_lock(&mutex); - - if (!registered) { - GType type = g_type_register_static_simple( - VIPS_TYPE_FOREIGN_LOAD, - g_intern_static_string("VipsForeignLoadIsyntax"), - sizeof(VipsForeignLoadIsyntaxClass), - (GClassInitFunc)vips_foreign_load_isyntax_class_init, - sizeof(VipsForeignLoadIsyntax), - (GInstanceInitFunc)vips_foreign_load_isyntax_init, - 0); - registered = TRUE; + if (sscanf(size_str, "%lld%c", &size, &unit) == 2) { + if (unit == 'M') { + if (size > INT64_MAX / (1024)) { + printf("Error: Cache size too large.\n"); + return -1; + } + size *= 1024; + } else if (unit == 'G') { + if (size > INT64_MAX / (1024 * 1024)) { + printf("Error: Cache size too large.\n"); + return -1; + } + size *= 1024 * 1024; + } else { + printf("Error: Invalid unit for cache size. Use 'M' for megabytes or 'G' for gigabytes.\n"); + return -1; } - - g_static_mutex_unlock(&mutex); + } else if (sscanf(size_str, "%lld", &size) != 1) { + printf("Error: Invalid cache size format.\n"); + return -1; } -} -static void -vips_foreign_load_isyntax_init(VipsForeignLoadIsyntax *isyntax) { - /* Initialize other members */ - - isyntax->tile_width = libisyntax_get_tile_width(isyntax->isyntax); - isyntax->tile_height = libisyntax_get_tile_height(isyntax->isyntax); + return size; } - -int main(int argc, char** argv) { - vips_foreign_load_isyntax_register(); - - if (VIPS_INIT(argv[0])) { - vips_error_exit("Failed to initialize vips"); +int main(int argc, char **argv) { + const char *usage_string = + "Usage: isyntax-to-tiff [OPTIONS] INPUT OUTPUT\n\n" + "Converts Philips iSyntax files to multi-resolution TIFF files.\n\n" + "Positional arguments:\n" + " INPUT Path to the input iSyntax file.\n" + " OUTPUT Path to the output TIFF file.\n\n" + "Options:\n" + " --tile-size SIZE Specifies the tile size for the output TIFF (default: 1024).\n" + " Must be a positive integer.\n\n" + " --compression TYPE Specifies the compression type for the output TIFF.\n" + " Supported types: JPEG, LZW, NONE (default: JPEG).\n\n" + " --quality VALUE Specifies the quality for JPEG compression (0-100).\n" + " Only applicable when using JPEG compression (default: 80).\n\n" + " --cache-size SIZE Specifies the cache size for the iSyntax library.\n" + " Accepts a number followed by 'M' (for megabytes) or 'G' (for gigabytes),\n" + " or just a number for kilobytes (default: 2000).\n\n" + "Example:\n\n" + " isyntax-to-tiff --tile-size 512 --compression JPEG --quality 90 --cache-size 1G input.isyntax output.tiff\n\n" + "This command will convert the input.isyntax file into an output.tiff file with a tile size of 512, JPEG compression at 90 quality, and a cache size of 1 gigabyte.\n"; + + if (argc < 3) { + printf("Error: Missing input and/or output file arguments.\n\n"); + printf("%s", usage_string); + return -1; } - if (argc <= 1) { - printf("Usage: %s - convert an isyntax image to a tiff.", argv[0]); - return 0; - } + char *filename = argv[1]; + char *output_tiffname = argv[2]; + + uint64_t cache_size = 2000; + int32_t tile_size = 1024; + + int compression_type = COMPRESSION_JPEG; + int quality = 80; + + for (int i = 3; i < argc; ++i) { + if (strcmp(argv[i], "--tile-size") == 0) { + if (i + 1 < argc) { + tile_size = atoi(argv[i + 1]); + if (tile_size <= 0) { + printf("Error: Invalid tile size. Please provide a positive integer value for the tile size.\n"); + return -1; + } + i++; // Skip the next argument (tile size value) + } else { + printf("Error: Missing value for --tile-size option.\n"); + return -1; + } + } else if (strcmp(argv[i], "--compression") == 0) { + if (i + 1 < argc) { + if (strcmp(argv[i + 1], "JPEG") == 0) { + compression_type = COMPRESSION_JPEG; + } else if (strcmp(argv[i + 1], "LZW") == 0) { + compression_type = COMPRESSION_LZW; + } else if (strcmp(argv[i + 1], "NONE") == 0) { + compression_type = COMPRESSION_NONE; + } else { + printf("Error: Invalid compression type. Supported types are JPEG, LZW, and NONE.\n"); + return -1; + } + i++; // Skip the next argument (compression type value) + } else { + printf("Error: Missing value for --compression option.\n"); + return -1; + } + } else if (strcmp(argv[i], "--quality") == 0) { + if (i + 1 < argc) { + quality = atoi(argv[i + 1]); + if (quality < 0 || quality > 100) { + printf("Error: Invalid quality value. Please provide an integer value between 0 and 100 for the quality.\n"); + return -1; + } + if (compression_type != COMPRESSION_JPEG) { + printf("Warning: The --quality flag is ignored with the current compression type. Quality is only applicable to JPEG compressions.\n"); + } + i++; // Skip the next argument (quality value) + } else { + printf("Error: Missing value for --quality option.\n"); + return -1; + } + } else if (strcmp(argv[i], "--cache-size") == 0) { + if (i + 1 < argc) { + cache_size = parse_cache_size(argv[i + 1]); + if (cache_size >= INT64_MAX || cache_size < 0) { + printf("Error: Cache size not suitable for the system.\n"); + return -1; + } + i++; // Skip the next argument (cache size value) + } else { + printf("Error: Missing value for --cache-size option.\n"); + return -1; + } - char* filename = argv[1]; - char* output_file = argv[2]; + } else { + printf("Error: Unknown option %s\n", argv[i]); + return -1; + } + } + int32_t tile_width = tile_size; + int32_t tile_height = tile_size; libisyntax_init(); - isyntax_t* isyntax; + isyntax_t *isyntax; if (libisyntax_open(filename, /*is_init_allocators=*/0, &isyntax) != LIBISYNTAX_OK) { fprintf(stderr, "Failed to open %s\n", filename); return -1; } - printf("Successfully opened %s\n", filename); + + int32_t internal_tile_width = libisyntax_get_tile_width(isyntax); + int32_t internal_tile_height = libisyntax_get_tile_height(isyntax); + LOG_VAR("%d", internal_tile_width); + LOG_VAR("%d", internal_tile_height); + LOG_VAR("%llu", cache_size); + LOG_VAR("%d", compression_type); + LOG_VAR("%d", quality); + LOG_VAR("%d", tile_size); isyntax_cache_t *isyntax_cache = NULL; - assert(libisyntax_cache_create("tiff conversion cache", 200000, &isyntax_cache) == LIBISYNTAX_OK); - assert(libisyntax_cache_inject(isyntax_cache, isyntax) == LIBISYNTAX_OK); + if (libisyntax_cache_create("isyntax-to-tiff cache", cache_size, &isyntax_cache) != LIBISYNTAX_OK) { + fprintf(stderr, "Failed to create iSyntax cache with size %llu.\n", cache_size); + libisyntax_close(isyntax); + return -1; + } + if (libisyntax_cache_inject(isyntax_cache, isyntax) != LIBISYNTAX_OK) { + fprintf(stderr, "Failed to inject iSyntax cache into iSyntax instance.\n"); + libisyntax_cache_destroy(isyntax_cache); + libisyntax_close(isyntax); + return -1; + } - int wsi_image_idx = libisyntax_get_wsi_image_index(isyntax); - const isyntax_image_t* wsi_image = libisyntax_get_image(isyntax, wsi_image_idx); + // Initialize the output TIFF file. + TIFF *output_tiff; + output_tiff = TIFFOpen(output_tiffname, "w8"); + if (!output_tiff) { + fprintf(stderr, "Failed to create %s\n", output_tiffname); + return -1; + } - const isyntax_level_t *base_level = libisyntax_image_get_level(wsi_image, 0); - const int32_t tile_height = libisyntax_get_tile_height(isyntax); - const int32_t tile_width = libisyntax_get_tile_width(isyntax); - const int32_t num_tiles_height = libisyntax_level_get_height_in_tiles(base_level); - const int32_t num_tiles_width = libisyntax_level_get_width_in_tiles(base_level); - // TODO: The actual width is smaller! Get this from the library. + // Write all levels to the output TIFF. + int start_at_page = 0; - uint32_t *pixels = NULL; + const isyntax_image_t *image = libisyntax_get_image(isyntax, 0); + int32_t num_levels = libisyntax_image_get_level_count(image); + int32_t total_tiles = 0; + // Let's find the total number of tiles so we can have a progress counter + for (int32_t level = start_at_page; level < num_levels; ++level) { + isyntax_level_t *current_level = libisyntax_image_get_level(image, level); + int32_t width = libisyntax_level_get_width(current_level); + int32_t height = libisyntax_level_get_height(current_level); + int32_t tiles_in_page = ((height - 1) / tile_height) * ((width - 1) / tile_width) + 2; + total_tiles += tiles_in_page; + } - libisyntax_tile_free_pixels(pixels); + int32_t total_tiles_written = 0; + clock_t global_start_time = clock(); + for (int32_t level = start_at_page; level < num_levels; ++level) { + isyntax_level_t *current_level = libisyntax_image_get_level(image, level); + write_page_to_tiff(output_tiff, isyntax, isyntax_cache, current_level, tile_width, tile_height, + &total_tiles_written, total_tiles, global_start_time, compression_type, quality); + } + + // Close the output TIFF file. + TIFFClose(output_tiff); + + // Clean up. libisyntax_cache_destroy(isyntax_cache); libisyntax_close(isyntax); - vips_shutdown(); return 0; -} +} \ No newline at end of file From 93abd487788ed04dbcadd7c2874ebab305394255 Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Thu, 20 Apr 2023 21:53:01 +0200 Subject: [PATCH 35/38] Update tool # Conflicts: # src/isyntax_to_tiff.c --- src/isyntax/isyntax.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/isyntax/isyntax.c b/src/isyntax/isyntax.c index eeca092..f63c041 100644 --- a/src/isyntax/isyntax.c +++ b/src/isyntax/isyntax.c @@ -3069,8 +3069,14 @@ bool isyntax_open(isyntax_t* isyntax, const char* filename, bool init_allocators // (I guess this is related to the way the wavelet transform works.) // Put another way: the highest (zoomed out levels) are shifted the to the bottom right // (this is also reflected in the x and y coordinates of the codeblocks in the iSyntax header). - for (i32 scale = 1; scale < wsi_image->level_count; ++scale) { - isyntax_level_t* level = wsi_image->levels + scale; + // TODO(jt): Not sure why the below counter starts at 1. Is there no offset in the first page?? + // Anyway, we need the width here. + isyntax_level_t* level = wsi_image->levels + 0; + level->width = wsi_image->width; + level->height = wsi_image->height; + + for (i32 scale = 1; scale < wsi_image->level_count; ++scale) { + level += scale; level->origin_offset_in_pixels = ((PER_LEVEL_PADDING << wsi_image->level_count) - PER_LEVEL_PADDING) >> scale; level->width = wsi_image->width >> scale; level->height = wsi_image->height >> scale; From 141d567518cb4544f6fdbe781b659fa1e74ac034 Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Thu, 20 Apr 2023 14:19:22 +0200 Subject: [PATCH 36/38] Global eta makes more sense --- src/isyntax_to_tiff.c | 7 ++++++- src/libisyntax.c | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/isyntax_to_tiff.c b/src/isyntax_to_tiff.c index cbd4cca..73c2f3f 100644 --- a/src/isyntax_to_tiff.c +++ b/src/isyntax_to_tiff.c @@ -90,9 +90,10 @@ void write_page_to_tiff(TIFF *output_tiff, isyntax_t *isyntax, isyntax_cache_t * // Calculate ETA clock_t current_global_time = clock(); - double elapsed_global_time = (double) (current_global_time - global_start_time) / CLOCKS_PER_SEC; + double elapsed_global_time = (double)(current_global_time - global_start_time) / CLOCKS_PER_SEC; double avg_time_per_tile = elapsed_global_time / (*total_tiles_written + tile_progress); double eta = avg_time_per_tile * (total_tiles - (*total_tiles_written + tile_progress)); + update_progress(total_progress, tile_percent, scale, eta); libisyntax_tile_free_pixels(pixels); @@ -294,7 +295,11 @@ int main(int argc, char **argv) { for (int32_t level = start_at_page; level < num_levels; ++level) { isyntax_level_t *current_level = libisyntax_image_get_level(image, level); write_page_to_tiff(output_tiff, isyntax, isyntax_cache, current_level, tile_width, tile_height, +<<<<<<< HEAD &total_tiles_written, total_tiles, global_start_time, compression_type, quality); +======= + &total_tiles_written, total_tiles, global_start_time); +>>>>>>> 4e7e1aa (Global eta makes more sense) } // Close the output TIFF file. diff --git a/src/libisyntax.c b/src/libisyntax.c index 3f4e91d..4a7408a 100644 --- a/src/libisyntax.c +++ b/src/libisyntax.c @@ -369,7 +369,7 @@ isyntax_error_t libisyntax_read_region(isyntax_t* isyntax, isyntax_cache_t* isyn // Fill up with transparent white pixels (R=255, G=255, B=255, A=0) for (int64_t i = 0; i < copy_height; ++i) { for (int64_t j = 0; j < copy_width; ++j) { - (*out_pixels)[(dest_y + i) * width + dest_x + j] = 0x00FFFFFF; + (*out_pixels)[(dest_y + i) * width + dest_x + j] = 0x00FFFFFFu; } } } From 7ddb61a427c9dcb3ec9fd21f5523d73bc88a419d Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Thu, 20 Apr 2023 18:02:02 +0200 Subject: [PATCH 37/38] Fix offset for level 0 --- src/isyntax/isyntax.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/isyntax/isyntax.c b/src/isyntax/isyntax.c index f63c041..55ecb82 100644 --- a/src/isyntax/isyntax.c +++ b/src/isyntax/isyntax.c @@ -3069,14 +3069,8 @@ bool isyntax_open(isyntax_t* isyntax, const char* filename, bool init_allocators // (I guess this is related to the way the wavelet transform works.) // Put another way: the highest (zoomed out levels) are shifted the to the bottom right // (this is also reflected in the x and y coordinates of the codeblocks in the iSyntax header). - // TODO(jt): Not sure why the below counter starts at 1. Is there no offset in the first page?? - // Anyway, we need the width here. - isyntax_level_t* level = wsi_image->levels + 0; - level->width = wsi_image->width; - level->height = wsi_image->height; - - for (i32 scale = 1; scale < wsi_image->level_count; ++scale) { - level += scale; + for (i32 scale = 0; scale < wsi_image->level_count; ++scale) { + isyntax_level_t* level = wsi_image->levels + scale; level->origin_offset_in_pixels = ((PER_LEVEL_PADDING << wsi_image->level_count) - PER_LEVEL_PADDING) >> scale; level->width = wsi_image->width >> scale; level->height = wsi_image->height >> scale; From fd214a622b9a9ecd91bbdd26f2e7c1e6b138ded2 Mon Sep 17 00:00:00 2001 From: Jonas Teuwen Date: Thu, 20 Apr 2023 20:29:30 +0200 Subject: [PATCH 38/38] Updated CMakeLists.txt --- CMakeLists.txt | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0527450..106dd50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,11 +20,6 @@ include_directories("${CMAKE_SOURCE_DIR}/src/platform") include_directories("${CMAKE_SOURCE_DIR}/src/utils") include_directories("${CMAKE_SOURCE_DIR}/src/isyntax") include_directories("${CMAKE_SOURCE_DIR}/src/third_party") -include_directories(${VIPS_INCLUDE_DIRS}) - -find_package(PkgConfig REQUIRED) -pkg_check_modules(VIPS REQUIRED vips) - # Find LibTIFF library find_package(TIFF REQUIRED) @@ -33,6 +28,17 @@ if (NOT TIFF_FOUND) message(WARNING "Will not compile `isyntax-to-tiff` utility") endif() include_directories(${TIFF_INCLUDE_DIR}) +find_package(Threads REQUIRED) +if (UNIX AND NOT APPLE) # needed for sem_wait, etc. + find_library(RT_LIBRARY rt) + set(EXTRA_LIBS ${RT_LIBRARY}) +elseif (APPLE) + # No extra libraries needed for macOS +elseif (WIN32) + # No extra libraries needed for Windows +endif () + + set(LIBISYNTAX_COMMON_SOURCE_FILES src/libisyntax.c @@ -62,18 +68,7 @@ add_executable(isyntax_example src/isyntax_example.c ${LIBISYNTAX_COMMON_SOURCE_FILES} ) -message(STATUS "VIPS_INCLUDE_DIRS: ${VIPS_INCLUDE_DIRS}") -message(STATUS "VIPS_LIBRARY_DIRS: ${VIPS_LIBRARY_DIRS}") -message(STATUS "VIPS_LIBRARIES: ${VIPS_LIBRARIES}") -link_directories(${VIPS_LIBRARY_DIRS}) -add_executable(isyntax-to-tiff - src/isyntax_to_tiff.c - ${LIBISYNTAX_COMMON_SOURCE_FILES} - ) - -target_include_directories(isyntax-to-tiff PRIVATE ${VIPS_INCLUDE_DIRS}) -target_link_libraries(isyntax-to-tiff PRIVATE ${VIPS_LIBRARIES}) add_executable(isyntax_example2 @@ -90,14 +85,19 @@ if (TIFF_FOUND) endif() target_include_directories(isyntax-to-tiff PRIVATE ${TIFF_INCLUDE_DIRS}) -target_link_libraries(isyntax-to-tiff PRIVATE ${TIFF_LIBRARIES}) - if (WIN32) - target_link_libraries(libisyntax winmm) - target_link_libraries(isyntax_example winmm) - target_link_libraries(isyntax-to-tiff winmm) + target_link_libraries(libisyntax winmm Threads::Threads) + target_link_libraries(isyntax_example winmm Threads::Threads) + target_link_libraries(isyntax_example2 winmm Threads::Threads) + target_link_libraries(isyntax-to-tiff winmm Threads::Threads) -else() +else() + target_link_libraries(libisyntax Threads::Threads ${EXTRA_LIBS}) + target_link_libraries(isyntax_example Threads::Threads ${EXTRA_LIBS}) + target_link_libraries(isyntax_example2 Threads::Threads ${EXTRA_LIBS}) + if (TIFF_FOUND) + target_link_libraries(isyntax-to-tiff PRIVATE ${TIFF_LIBRARIES} Threads::Threads ${EXTRA_LIBS}) + endif() endif()