pyisyntax 0.1.3__tar.gz → 0.1.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/CHANGELOG.md +13 -1
  2. {pyisyntax-0.1.3/pyisyntax.egg-info → pyisyntax-0.1.4}/PKG-INFO +6 -6
  3. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax/lowlevel/libisyntax.py +4 -0
  4. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax/wrapper.py +4 -0
  5. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/examples/isyntax_to_tiff.c +4 -3
  6. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax.c +180 -87
  7. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax.h +11 -2
  8. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_streamer.c +77 -23
  9. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/libisyntax.c +4 -0
  10. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/libisyntax.h +2 -1
  11. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/third_party/ltalloc.cc +12 -3
  12. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/utils/mathutils.c +5 -3
  13. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/utils/mathutils.h +14 -7
  14. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/utils/stringutils.c +2 -2
  15. {pyisyntax-0.1.3 → pyisyntax-0.1.4/pyisyntax.egg-info}/PKG-INFO +6 -6
  16. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/pyproject.toml +9 -6
  17. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/tests/test_lowlevel.py +5 -0
  18. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/tests/test_wrapper.py +4 -0
  19. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/LICENSE +0 -0
  20. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/MANIFEST.in +0 -0
  21. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/README.md +0 -0
  22. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax/__init__.py +0 -0
  23. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax/lowlevel/__init__.py +0 -0
  24. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax/lowlevel/io_management.py +0 -0
  25. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/__init__.py +0 -0
  26. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/builder.py +0 -0
  27. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/pyisyntax.c +0 -0
  28. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/src/python_platform_utils.c +0 -0
  29. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/src/python_platform_utils.h +0 -0
  30. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/src/win32_utils.c +0 -0
  31. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/LICENSE.txt +0 -0
  32. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/README.md +0 -0
  33. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/examples/isyntax_example.c +0 -0
  34. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_dwt.c +0 -0
  35. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_reader.c +0 -0
  36. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_reader.h +0 -0
  37. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_streamer.h +0 -0
  38. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/platform/arena.h +0 -0
  39. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/platform/common.h +0 -0
  40. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/platform/intrinsics.h +0 -0
  41. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/platform/linux_platform.c +0 -0
  42. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/platform/linux_utils.c +0 -0
  43. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/platform/platform.c +0 -0
  44. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/platform/platform.h +0 -0
  45. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/platform/win32_utils.c +0 -0
  46. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/platform/win32_utils.h +0 -0
  47. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/platform/work_queue.c +0 -0
  48. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/platform/work_queue.h +0 -0
  49. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/third_party/ltalloc.h +0 -0
  50. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/third_party/stb_ds.h +0 -0
  51. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/third_party/stb_image.h +0 -0
  52. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/third_party/stb_image_write.h +0 -0
  53. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/third_party/stb_sprintf.h +0 -0
  54. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/third_party/yxml.c +0 -0
  55. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/third_party/yxml.h +0 -0
  56. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/utils/benaphore.c +0 -0
  57. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/utils/benaphore.h +0 -0
  58. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/utils/block_allocator.c +0 -0
  59. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/utils/block_allocator.h +0 -0
  60. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/utils/libtiff_api.h +0 -0
  61. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/utils/memrw.c +0 -0
  62. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/utils/memrw.h +0 -0
  63. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/utils/stringutils.h +0 -0
  64. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/utils/timerutils.c +0 -0
  65. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/isyntax_build/vendor/libisyntax/src/utils/timerutils.h +0 -0
  66. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/pyisyntax.egg-info/SOURCES.txt +0 -0
  67. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/pyisyntax.egg-info/dependency_links.txt +0 -0
  68. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/pyisyntax.egg-info/not-zip-safe +0 -0
  69. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/pyisyntax.egg-info/requires.txt +0 -0
  70. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/pyisyntax.egg-info/top_level.txt +0 -0
  71. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/setup.cfg +0 -0
  72. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/setup.py +0 -0
  73. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/tests/__init__.py +0 -0
  74. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/tests/conftest.py +0 -0
  75. {pyisyntax-0.1.3 → pyisyntax-0.1.4}/tests/data/DOWNLOAD.txt +0 -0
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.4] - 2024-08-31
11
+
12
+ ### Added
13
+
14
+ - Support for reading the slide barcode. The barcode string can be accessed via
15
+ the `barcode` property of an `ISyntax` instance.
16
+
17
+ ### Changed
18
+
19
+ - Bump the `libisyntax` submodule commit.
20
+
10
21
  ## [0.1.3] - 2024-03-28
11
22
 
12
23
  ### Added
@@ -50,7 +61,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
50
61
 
51
62
  - This is the initial release.
52
63
 
53
- [unreleased]: https://github.com/anibali/pyisyntax/compare/v0.1.3...HEAD
64
+ [unreleased]: https://github.com/anibali/pyisyntax/compare/v0.1.4...HEAD
65
+ [0.1.4]: https://github.com/anibali/pyisyntax/compare/v0.1.3...v0.1.4
54
66
  [0.1.3]: https://github.com/anibali/pyisyntax/compare/v0.1.2...v0.1.3
55
67
  [0.1.2]: https://github.com/anibali/pyisyntax/compare/v0.1.1...v0.1.2
56
68
  [0.1.1]: https://github.com/anibali/pyisyntax/compare/v0.1.0...v0.1.1
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyisyntax
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: Python bindings for libisyntax
5
5
  Author-email: Aiden Nibali <dismaldenizen@gmail.com>
6
6
  License: MIT
7
- Project-URL: homepage, https://github.com/anibali/pyisyntax
8
- Project-URL: source, https://github.com/anibali/pyisyntax
9
- Project-URL: download, https://pypi.org/project/pyisyntax/#files
10
- Project-URL: tracker, https://github.com/anibali/pyisyntax/issues
11
- Project-URL: release notes, https://github.com/anibali/pyisyntax/blob/master/CHANGELOG.md
7
+ Project-URL: Homepage, https://github.com/anibali/pyisyntax
8
+ Project-URL: Source, https://github.com/anibali/pyisyntax
9
+ Project-URL: Download, https://pypi.org/project/pyisyntax/#files
10
+ Project-URL: Tracker, https://github.com/anibali/pyisyntax/issues
11
+ Project-URL: Release notes, https://github.com/anibali/pyisyntax/blob/master/CHANGELOG.md
12
12
  Requires-Python: >=3.10
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
@@ -119,6 +119,10 @@ def get_macro_image(isyntax: ISyntaxPtr) -> ISyntaxImagePtr:
119
119
  return lib.libisyntax_get_macro_image(isyntax)
120
120
 
121
121
 
122
+ def get_barcode(isyntax: ISyntaxPtr) -> bytes:
123
+ return ffi.string(lib.libisyntax_get_barcode(isyntax))
124
+
125
+
122
126
  def image_get_level_count(wsi_image: ISyntaxImagePtr) -> int:
123
127
  return lib.libisyntax_image_get_level_count(wsi_image)
124
128
 
@@ -178,6 +178,10 @@ class ISyntax:
178
178
  except libisyntax.LibISyntaxFatalError:
179
179
  return None
180
180
 
181
+ @property
182
+ def barcode(self) -> str:
183
+ return libisyntax.get_barcode(self.ptr).decode("ascii")
184
+
181
185
  def read_icc_profile(self) -> memoryview | None:
182
186
  """Reads the ICC color profile for an image.
183
187
 
@@ -37,6 +37,7 @@
37
37
  #endif
38
38
 
39
39
  #include <stdint.h>
40
+ #include <inttypes.h>
40
41
  #include <assert.h>
41
42
  #include <stdlib.h>
42
43
  #include <string.h>
@@ -221,7 +222,7 @@ uint64_t parse_cache_size(const char *size_str) {
221
222
  uint64_t size;
222
223
  char unit;
223
224
 
224
- if (sscanf(size_str, "%lld%c", &size, &unit) == 2) {
225
+ if (sscanf(size_str, "%"PRIu64"%c", &size, &unit) == 2) {
225
226
  if (unit == 'M') {
226
227
  if (size > INT64_MAX / (1024)) {
227
228
  printf("Error: Cache size too large.\n");
@@ -238,7 +239,7 @@ uint64_t parse_cache_size(const char *size_str) {
238
239
  printf("Error: Invalid unit for cache size. Use 'M' for megabytes or 'G' for gigabytes.\n");
239
240
  return -1;
240
241
  }
241
- } else if (sscanf(size_str, "%lld", &size) != 1) {
242
+ } else if (sscanf(size_str, "%"PRIu64, &size) != 1) {
242
243
  printf("Error: Invalid cache size format.\n");
243
244
  return -1;
244
245
  }
@@ -426,7 +427,7 @@ int main(int argc, char **argv) {
426
427
 
427
428
  isyntax_cache_t *isyntax_cache = NULL;
428
429
  if (libisyntax_cache_create("isyntax-to-tiff cache", cache_size, &isyntax_cache) != LIBISYNTAX_OK) {
429
- fprintf(stderr, "Failed to create iSyntax cache with size %llu.\n", cache_size);
430
+ fprintf(stderr, "Failed to create iSyntax cache with size %"PRIu64".\n", cache_size);
430
431
  libisyntax_close(isyntax);
431
432
  return -1;
432
433
  }
@@ -66,7 +66,7 @@
66
66
  // Enable/disable debug routines for creating PNGs of IDWT steps
67
67
  #define ISYNTAX_WANT_DEBUG_OUTPUT_PNG 0
68
68
  #if ISYNTAX_WANT_DEBUG_OUTPUT_PNG
69
- #define STB_IMAGE_WRITE_IMPLEMENTATION
69
+ //#define STB_IMAGE_WRITE_IMPLEMENTATION
70
70
  // stb_image_write.h provides its own crc32() implementation, so we need to prevent a conflict if we already have it
71
71
  #define STBIW_CRC32 crc32
72
72
  #include <stb_image_write.h>
@@ -74,8 +74,6 @@
74
74
 
75
75
  #include <ctype.h>
76
76
 
77
- // TODO: Add ICC profiles support
78
-
79
77
  #define PER_LEVEL_PADDING 3
80
78
 
81
79
  #include "isyntax_dwt.c"
@@ -157,6 +155,21 @@ unsigned char * base64_decode(const unsigned char *src, size_t len,
157
155
  }
158
156
  // end of base64 decoder.
159
157
 
158
+ // Wrapper for base64_decode, taking into account possible extra (invalid) characters at the end
159
+ u8* isyntax_base64_decode(const char* src, size_t len, size_t *out_len) {
160
+ char last_char = src[len-1];
161
+ if (last_char == '/') {
162
+ len--; // The last character may cause the base64 decoding to fail if invalid
163
+ last_char = src[len-1];
164
+ }
165
+ while (last_char == '\n' || last_char == '\r' || last_char == ' ') {
166
+ --len;
167
+ last_char = src[len-1];
168
+ }
169
+ u8* decoded = base64_decode((u8*)src, len, out_len);
170
+ return decoded;
171
+ }
172
+
160
173
  // similar to atoi(), but also returning the string position so we can chain calls one after another.
161
174
  static const char* atoi_and_advance(const char* str, i32* dest) {
162
175
  i32 num = 0;
@@ -378,7 +391,14 @@ static void isyntax_parse_ufsimport_child_node(isyntax_t* isyntax, u32 group, u3
378
391
  // Value will likely be "5.0" for v1 iSyntax files, "100.5" for v2 iSyntax files
379
392
  isyntax->data_model_major_version = atoi(value);
380
393
  } break;
381
- case 0x1002: /*PIM_DP_UFS_BARCODE*/ {} break; // "<base64-encoded barcode value>"
394
+ case 0x1002: /*PIM_DP_UFS_BARCODE*/ {
395
+ size_t decoded_len = 0;
396
+ char* decoded = (char*) isyntax_base64_decode(value, value_len, &decoded_len);
397
+ if (decoded) {
398
+ memcpy(isyntax->barcode, decoded, MIN(decoded_len, sizeof(isyntax->barcode)-1));
399
+ free(decoded);
400
+ }
401
+ } break; // "<base64-encoded barcode value>"
382
402
  case 0x1003: /*PIM_DP_SCANNED_IMAGES*/ {} break;
383
403
  case 0x1010: /*PIM_DP_SCANNER_RACK_PRIORITY*/ {} break; // "<u16>"
384
404
  }
@@ -532,9 +552,9 @@ static bool isyntax_parse_scannedimage_child_node(isyntax_t* isyntax, u32 group,
532
552
  case 3: {
533
553
  image->level_count = range.numsteps;
534
554
  image->max_scale = range.numsteps - 1;
535
- int32_t level_padding = (PER_LEVEL_PADDING << range.numsteps) - PER_LEVEL_PADDING;
536
- image->width = image->width_including_padding - 2 * level_padding;
537
- image->height = image->height_including_padding - 2 * level_padding;
555
+ image->level0_padding = (PER_LEVEL_PADDING << range.numsteps) - PER_LEVEL_PADDING;
556
+ image->width = image->width_including_padding - 2 * image->level0_padding;
557
+ image->height = image->height_including_padding - 2 * image->level0_padding;
538
558
  } break;
539
559
  case 4: break; // always 4 wavelet coefficients ("LL" "LH" "HL" "HH"), no need to check
540
560
  }
@@ -588,23 +608,13 @@ static bool isyntax_parse_scannedimage_child_node(isyntax_t* isyntax, u32 group,
588
608
  case 0x2013: /*UFS_IMAGE_PIXEL_TRANSFORMATION_METHOD*/ {} break;
589
609
  case 0x2014: { /*UFS_IMAGE_BLOCK_HEADER_TABLE*/ // data model <100
590
610
  // NOTE: mutually exclusive with UFS_IMAGE_BLOCK_HEADERS (either one or the other must be present)
591
- size_t decoded_capacity = value_len;
592
- size_t decoded_len = 0;
593
- i32 last_char = value[value_len-1];
594
611
  /*
595
612
  FILE* test_out = file_stream_open_for_writing("test_b64.out");
596
613
  file_stream_write(value, value_len, test_out);
597
614
  file_stream_close(test_out);
598
615
  */
599
- if (last_char == '/') {
600
- value_len--; // The last character may cause the base64 decoding to fail if invalid
601
- last_char = value[value_len-1];
602
- }
603
- while (last_char == '\n' || last_char == '\r' || last_char == ' ') {
604
- --value_len;
605
- last_char = value[value_len-1];
606
- }
607
- u8* decoded = base64_decode((u8*)value, value_len, &decoded_len);
616
+ size_t decoded_len = 0;
617
+ u8* decoded = isyntax_base64_decode(value, value_len, &decoded_len);
608
618
  if (decoded) {
609
619
 
610
620
  u32 header_size = *(u32*) decoded + 0;
@@ -672,23 +682,13 @@ static bool isyntax_parse_scannedimage_child_node(isyntax_t* isyntax, u32 group,
672
682
  case 0x2016: /*UFS_IMAGE_CLUSTER_HEADER_TEMPLATES*/ {} break; // data model >= 100
673
683
  case 0x2017: /*UFS_IMAGE_DIMENSIONS_OVER_CLUSTER*/ {} break;
674
684
  case 0x201F: /*UFS_IMAGE_CLUSTER_HEADER_TABLE*/ { // data model >= 100
675
- size_t decoded_capacity = value_len;
676
- size_t decoded_len = 0;
677
- i32 last_char = value[value_len-1];
678
685
  /*
679
686
  FILE* test_out = file_stream_open_for_writing("test_b64.out");
680
687
  file_stream_write(value, value_len, test_out);
681
688
  file_stream_close(test_out);
682
689
  */
683
- if (last_char == '/') {
684
- value_len--; // The last character may cause the base64 decoding to fail if invalid
685
- last_char = value[value_len-1];
686
- }
687
- while (last_char == '\n' || last_char == '\r' || last_char == ' ') {
688
- --value_len;
689
- last_char = value[value_len-1];
690
- }
691
- u8* decoded = base64_decode((u8*)value, value_len, &decoded_len);
690
+ size_t decoded_len = 0;
691
+ u8* decoded = isyntax_base64_decode(value, value_len, &decoded_len);
692
692
  if (decoded) {
693
693
  u8* decoded_end = decoded + decoded_len;
694
694
  u32 header_size = *(u32*) decoded + 0;
@@ -701,7 +701,7 @@ static bool isyntax_parse_scannedimage_child_node(isyntax_t* isyntax, u32 group,
701
701
  goto decoding_cluster_header_table_failed;
702
702
  }
703
703
  // pass 1: check how many clusters there are
704
- i32 cluster_count = 0;
704
+ i32 cluster_count = 1;
705
705
  for (;;) {
706
706
  u8* next_sequence_element_pos = pos + sizeof(isyntax_dicom_tag_header_t) + ((isyntax_dicom_tag_header_t*)pos)->size;
707
707
  isyntax_dicom_tag_header_t* next_sequence_element = (isyntax_dicom_tag_header_t*)next_sequence_element_pos;
@@ -717,7 +717,9 @@ static bool isyntax_parse_scannedimage_child_node(isyntax_t* isyntax, u32 group,
717
717
  image->data_chunk_count = cluster_count;
718
718
  image->data_chunks = calloc(1, cluster_count * sizeof(isyntax_data_chunk_t));
719
719
  }
720
- ASSERT(image->number_of_blocks > 0); // TODO: handle error case
720
+ if (image->number_of_blocks <= 0) { // sanity check
721
+ goto decoding_cluster_header_table_failed;
722
+ }
721
723
  if (image->codeblocks == NULL) {
722
724
  // NOTE: this value seems to be much larger than the actual number of codeblocks present in the file
723
725
  image->codeblock_count = image->number_of_blocks; // from UFS_IMAGE_NUMBER_OF_BLOCKS attribute
@@ -868,8 +870,21 @@ static bool isyntax_parse_scannedimage_child_node(isyntax_t* isyntax, u32 group,
868
870
  template->dimension_count = parse_up_to_five_integers(value, template->dimension_order);
869
871
  } break; // data model >= 100
870
872
  case 0x2023: /*UFS_IMAGE_VALID_DATA_ENVELOPES*/ {} break; // data model >= 100
871
- case 0x2024: /*UFS_IMAGE_OPP_EXTREME_VERTICES*/ {} break; // data model >= 100
872
- case 0x2025: /*UFS_IMAGE_OPP_EXTREME_VERTEX*/ {} break; // data model >= 100
873
+ case 0x2024: /*UFS_IMAGE_OPP_EXTREME_VERTICES*/ { // data model >= 100
874
+ // increment envelope
875
+ } break;
876
+ case 0x2025: /*UFS_IMAGE_OPP_EXTREME_VERTEX*/ { // data model >= 100
877
+ if (isyntax->parser.data_object_flags & (ISYNTAX_OBJECT_UFSImageValidDataEnvelope)) {
878
+ isyntax_valid_data_envelope_t* envelope = isyntax->valid_data_envelopes + isyntax->parser.valid_data_envelope_index;
879
+ if (envelope->vertex_count < COUNT(envelope->vertices)) {
880
+ v2i vertex = {};
881
+ atoi_and_advance(atoi_and_advance(value, &vertex.x), &vertex.y); // parse two integers
882
+ envelope->vertices[envelope->vertex_count++] = vertex;
883
+ } else {
884
+ success = false; // out of bounds
885
+ }
886
+ }
887
+ } break; // data model >= 100
873
888
  case 0x2026: /*UFS_IMAGE_VALID_ENVELOPE_DIMENSIONS*/ {} break; // data model >= 100
874
889
  case 0x2027: /*UFS_IMAGE_DIMENSION_ORIGIN*/ {} break; // data model >= 100
875
890
  case 0x2029: /*UFS_IMAGE_PIXEL_TRANSFORM_METHOD*/ {} break; // data model >= 100
@@ -887,7 +902,7 @@ static bool validate_dicom_attr(const char* expected, const char* observed) {
887
902
  return ok;
888
903
  }
889
904
 
890
- void isyntax_xml_parser_init(isyntax_xml_parser_t* parser) {
905
+ static void isyntax_xml_parser_init(isyntax_xml_parser_t* parser) {
891
906
 
892
907
  parser->initialized = true;
893
908
 
@@ -942,6 +957,27 @@ static void push_to_buffer_maybe_grow(u8** restrict dest, size_t* restrict dest_
942
957
  *dest_len = new_len;
943
958
  }
944
959
 
960
+ static bool isyntax_parse_xml_header_cleanup(isyntax_t* isyntax, bool success) {
961
+ isyntax_xml_parser_t* parser = &isyntax->parser;
962
+ if (parser->x) {
963
+ free(parser->x);
964
+ parser->x = NULL;
965
+ }
966
+ if (parser->attrbuf) {
967
+ free(parser->attrbuf);
968
+ parser->attrbuf = NULL;
969
+ }
970
+ if (parser->contentbuf) {
971
+ free(parser->contentbuf);
972
+ parser->contentbuf = NULL;
973
+ }
974
+ return success;
975
+ }
976
+
977
+ static inline bool isyntax_parse_xml_header_failure(isyntax_t* isyntax) {
978
+ return isyntax_parse_xml_header_cleanup(isyntax, false);
979
+ }
980
+
945
981
  static bool isyntax_parse_xml_header(isyntax_t* isyntax, char* xml_header, i64 chunk_offset, i64 chunk_length, bool is_last_chunk) {
946
982
 
947
983
  yxml_t* x = NULL;
@@ -956,35 +992,19 @@ static bool isyntax_parse_xml_header(isyntax_t* isyntax, char* xml_header, i64 c
956
992
  }
957
993
  x = parser->x;
958
994
 
959
- if (0) { failed: cleanup:
960
- if (parser->x) {
961
- free(parser->x);
962
- parser->x = NULL;
963
- }
964
- if (parser->attrbuf) {
965
- free(parser->attrbuf);
966
- parser->attrbuf = NULL;
967
- }
968
- if (parser->contentbuf) {
969
- free(parser->contentbuf);
970
- parser->contentbuf = NULL;
971
- }
972
- return success;
973
- }
974
-
975
995
  // parse XML byte for byte
976
996
  char* doc = xml_header;
977
997
  for (i64 remaining_length = chunk_length; remaining_length > 0; --remaining_length, ++doc) {
978
998
  int c = *doc;
979
999
  if (c == '\0') {
980
1000
  // This should never trigger; iSyntax file is corrupt!
981
- goto failed;
1001
+ return isyntax_parse_xml_header_failure(isyntax);
982
1002
  }
983
1003
  yxml_ret_t r = yxml_parse(x, c);
984
1004
  if (r == YXML_OK) {
985
1005
  continue; // nothing worthy of note has happened -> continue
986
1006
  } else if (r < 0) {
987
- goto failed;
1007
+ return isyntax_parse_xml_header_failure(isyntax);
988
1008
  } else if (r > 0) {
989
1009
  // token
990
1010
  switch(r) {
@@ -1020,7 +1040,12 @@ static bool isyntax_parse_xml_header(isyntax_t* isyntax, char* xml_header, i64 c
1020
1040
  case 0: flags |= ISYNTAX_OBJECT_DPUfsImport; break;
1021
1041
  case PIM_DP_SCANNED_IMAGES: flags |= ISYNTAX_OBJECT_DPScannedImage; break;
1022
1042
  case UFS_IMAGE_GENERAL_HEADERS: flags |= ISYNTAX_OBJECT_UFSImageGeneralHeader; break;
1023
- case UFS_IMAGE_BLOCK_HEADER_TEMPLATES: flags |= ISYNTAX_OBJECT_UFSImageBlockHeaderTemplate; break;
1043
+ case UFS_IMAGE_BLOCK_HEADER_TEMPLATES: {
1044
+ flags |= ISYNTAX_OBJECT_UFSImageBlockHeaderTemplate;
1045
+ if (isyntax->parser.block_header_template_index >= COUNT(isyntax->block_header_templates)) { // bounds check
1046
+ return isyntax_parse_xml_header_failure(isyntax);
1047
+ }
1048
+ } break;
1024
1049
  case UFS_IMAGE_DIMENSIONS: flags |= ISYNTAX_OBJECT_UFSImageDimension; break;
1025
1050
  case UFS_IMAGE_DIMENSION_RANGES: flags |= ISYNTAX_OBJECT_UFSImageDimensionRange; break;
1026
1051
  case DP_COLOR_MANAGEMENT: flags |= ISYNTAX_OBJECT_DPColorManagement; break;
@@ -1029,8 +1054,20 @@ static bool isyntax_parse_xml_header(isyntax_t* isyntax, char* xml_header, i64 c
1029
1054
  case DP_WAVELET_QUANTIZER_SETTINGS_PER_LEVEL: flags |= ISYNTAX_OBJECT_DPWaveletQuantizerSeetingsPerLevel; break;
1030
1055
  case PIIM_PIXEL_DATA_REPRESENTATION_SEQUENCE: flags |= ISYNTAX_OBJECT_PixelDataRepresentation; break;
1031
1056
  case UFS_IMAGE_BLOCK_HEADERS: flags |= ISYNTAX_OBJECT_UFSImageBlockHeader; break;
1032
- case UFS_IMAGE_CLUSTER_HEADER_TEMPLATES: flags |= ISYNTAX_OBJECT_UFSImageClusterHeaderTemplate; break;
1033
- case UFS_IMAGE_VALID_DATA_ENVELOPES: flags |= ISYNTAX_OBJECT_UFSImageValidDataEnvelope; break;
1057
+ case UFS_IMAGE_CLUSTER_HEADER_TEMPLATES: {
1058
+ flags |= ISYNTAX_OBJECT_UFSImageClusterHeaderTemplate;
1059
+ if (isyntax->parser.cluster_header_template_index >= COUNT(isyntax->cluster_header_templates)) { // bounds check
1060
+ return isyntax_parse_xml_header_failure(isyntax);
1061
+ }
1062
+ } break;
1063
+ case UFS_IMAGE_VALID_DATA_ENVELOPES: {
1064
+ // 'soft' bounds check: parsing these is not strictly needed so we don't need to fail if we don't get them all.
1065
+ if (isyntax->parser.valid_data_envelope_index < COUNT(isyntax->valid_data_envelopes)) {
1066
+ flags |= ISYNTAX_OBJECT_UFSImageValidDataEnvelope;
1067
+ } else {
1068
+ console_print("Warning: cannot read all UFSImageValidDataEnvelopes (out of bounds)\n");
1069
+ }
1070
+ } break;
1034
1071
  case UFS_IMAGE_OPP_EXTREME_VERTICES: flags |= ISYNTAX_OBJECT_UFSImageOppExtremeVertex; break;
1035
1072
  }
1036
1073
  parser->data_object_flags = flags;
@@ -1160,7 +1197,9 @@ static bool isyntax_parse_xml_header(isyntax_t* isyntax, char* xml_header, i64 c
1160
1197
  case UFS_IMAGE_BLOCK_HEADER_TEMPLATES: {
1161
1198
  flags &= ~ISYNTAX_OBJECT_UFSImageBlockHeaderTemplate;
1162
1199
  ++parser->block_header_template_index;
1163
- ++isyntax->block_header_template_count; // TODO: refactor?
1200
+ if (isyntax->block_header_template_count < COUNT(isyntax->block_header_templates)) {
1201
+ ++isyntax->block_header_template_count;
1202
+ }
1164
1203
  parser->dimension_index = 0;
1165
1204
  } break;
1166
1205
  case UFS_IMAGE_DIMENSIONS: {
@@ -1181,14 +1220,14 @@ static bool isyntax_parse_xml_header(isyntax_t* isyntax, char* xml_header, i64 c
1181
1220
  // NOTE: Within a UFSImageClusterHeaderTemplate, the UFSImageBlockHeader objects contain coordinate offset values that apply to that cluster template
1182
1221
  // (Each UFSImageBlockHeader object corresponds to a codeblock in the cluster)
1183
1222
  ++parser->block_header_index_for_cluster;
1184
- if (parser->block_header_index_for_cluster >= MAX_CODEBLOCKS_PER_CLUSTER) {
1185
- fatal_error(); // TODO: unexpected error condition, fail more gracefully?
1223
+ if (parser->block_header_index_for_cluster >= MAX_CODEBLOCKS_PER_CLUSTER) { // bounds check
1224
+ return isyntax_parse_xml_header_failure(isyntax);
1186
1225
  }
1187
1226
  }
1188
1227
  } break;
1189
1228
  case UFS_IMAGE_CLUSTER_HEADER_TEMPLATES: {
1190
1229
  // Finalize cluster header template object: fix up relative codeblock coordinates within cluster
1191
- // using the information from UFS_IMAGE_DIMENSION_RANGES (=base value) // TODO: verify this
1230
+ // using the information from UFS_IMAGE_DIMENSION_RANGES (=base value)
1192
1231
  // combined with the UFS_IMAGE_BLOCK_COORDINATE values (=offsets) using the dimension ordering info provided by UFS_IMAGE_DIMENSIONS_IN_CLUSTER
1193
1232
  isyntax_cluster_header_template_t* template = isyntax->cluster_header_templates + parser->cluster_header_template_index;
1194
1233
  template->codeblock_in_cluster_count = parser->block_header_index_for_cluster;
@@ -1210,11 +1249,19 @@ static bool isyntax_parse_xml_header(isyntax_t* isyntax, char* xml_header, i64 c
1210
1249
  // Pop flags and reset indices
1211
1250
  flags &= ~ISYNTAX_OBJECT_UFSImageClusterHeaderTemplate;
1212
1251
  ++parser->cluster_header_template_index;
1213
- ++isyntax->cluster_header_template_count; // TODO: refactor?
1252
+ if (isyntax->cluster_header_template_count < COUNT(isyntax->cluster_header_templates)) {
1253
+ ++isyntax->cluster_header_template_count;
1254
+ }
1214
1255
  parser->dimension_index = 0;
1215
1256
  parser->block_header_index_for_cluster = 0;
1216
1257
  } break;
1217
- case UFS_IMAGE_VALID_DATA_ENVELOPES: flags &= ~ISYNTAX_OBJECT_UFSImageValidDataEnvelope; break;
1258
+ case UFS_IMAGE_VALID_DATA_ENVELOPES: {
1259
+ flags &= ~ISYNTAX_OBJECT_UFSImageValidDataEnvelope;
1260
+ ++parser->valid_data_envelope_index;
1261
+ if (isyntax->valid_data_envelope_count < COUNT(isyntax->valid_data_envelopes)) {
1262
+ ++isyntax->valid_data_envelope_count;
1263
+ }
1264
+ } break;
1218
1265
  case UFS_IMAGE_OPP_EXTREME_VERTICES: flags &= ~ISYNTAX_OBJECT_UFSImageOppExtremeVertex; break;
1219
1266
  }
1220
1267
  parser->data_object_flags = flags;
@@ -1232,8 +1279,8 @@ static bool isyntax_parse_xml_header(isyntax_t* isyntax, char* xml_header, i64 c
1232
1279
  parser->current_node_type = parser->node_stack[parser->node_stack_index].node_type;
1233
1280
  parser->current_node_has_children = parser->node_stack[parser->node_stack_index].has_children;
1234
1281
  } else {
1235
- //TODO: handle error condition
1236
1282
  console_print_error("iSyntax XML error: closing element without matching start\n");
1283
+ return isyntax_parse_xml_header_failure(isyntax);
1237
1284
  }
1238
1285
 
1239
1286
  } break;
@@ -1333,7 +1380,7 @@ static bool isyntax_parse_xml_header(isyntax_t* isyntax, char* xml_header, i64 c
1333
1380
  break; // processing instructions (uninteresting, skip)
1334
1381
  default: {
1335
1382
  console_print_error("yxml_parse(): unrecognized token (%d)\n", r);
1336
- goto failed;
1383
+ return isyntax_parse_xml_header_failure(isyntax);
1337
1384
  }
1338
1385
  }
1339
1386
  }
@@ -1341,7 +1388,7 @@ static bool isyntax_parse_xml_header(isyntax_t* isyntax, char* xml_header, i64 c
1341
1388
 
1342
1389
  success = true;
1343
1390
  if (is_last_chunk) {
1344
- goto cleanup;
1391
+ return isyntax_parse_xml_header_cleanup(isyntax, success);
1345
1392
  } else {
1346
1393
  return success; // no cleanup yet, we will still need resources until the last header chunk is reached.
1347
1394
  }
@@ -1466,7 +1513,7 @@ static rgba_t ycocg_to_rgb(i32 Y, i32 Co, i32 Cg) {
1466
1513
  i32 G = tmp + Cg;
1467
1514
  i32 B = tmp - Co/2;
1468
1515
  i32 R = B + Co;
1469
- return (rgba_t){ATMOST(255, R), ATMOST(255, G), ATMOST(255, B), 255};
1516
+ return (rgba_t){{{ATMOST(255, R), ATMOST(255, G), ATMOST(255, B), 255}}};
1470
1517
  }
1471
1518
 
1472
1519
  static rgba_t ycocg_to_bgr(i32 Y, i32 Co, i32 Cg) {
@@ -1474,7 +1521,7 @@ static rgba_t ycocg_to_bgr(i32 Y, i32 Co, i32 Cg) {
1474
1521
  i32 G = tmp + Cg;
1475
1522
  i32 B = tmp - Co/2;
1476
1523
  i32 R = B + Co;
1477
- return (rgba_t){ATMOST(255, B), ATMOST(255, G), ATMOST(255, R), 255};
1524
+ return (rgba_t){{{ATMOST(255, B), ATMOST(255, G), ATMOST(255, R), 255}}};
1478
1525
  }
1479
1526
 
1480
1527
  static void convert_ycocg_to_bgra_block(icoeff_t* Y, icoeff_t* Co, icoeff_t* Cg, i32 width, i32 height, i32 stride, u32* out_bgra) {
@@ -1757,6 +1804,18 @@ u32 isyntax_get_adjacent_tiles_mask_only_existing(isyntax_level_t* level, i32 ti
1757
1804
  return mask;
1758
1805
  }
1759
1806
 
1807
+ bool isyntax_is_parent_tile_missing(isyntax_image_t* wsi, i32 scale, i32 tile_x, i32 tile_y) {
1808
+ // Some tiles may have a missing parent tile (from scale + 1).
1809
+ // In this case the LL coefficients are missing, but dummy coefficients can be safely used instead.
1810
+ if (scale < wsi->max_scale) {
1811
+ isyntax_level_t* parent_level = wsi->levels + scale + 1;
1812
+ isyntax_tile_t* parent_tile = parent_level->tiles + (tile_y/2) * parent_level->width_in_tiles + (tile_x/2);
1813
+ return !parent_tile->exists;
1814
+ } else {
1815
+ return false;
1816
+ }
1817
+ }
1818
+
1760
1819
  u32 isyntax_idwt_tile_for_color_channel(isyntax_t* isyntax, isyntax_image_t* wsi, i32 scale, i32 tile_x, i32 tile_y, i32 color, icoeff_t* dest_buffer) {
1761
1820
  isyntax_level_t* level = wsi->levels + scale;
1762
1821
  ASSERT(tile_x >= 0 && tile_x < level->width_in_tiles);
@@ -1824,8 +1883,12 @@ u32 isyntax_idwt_tile_for_color_channel(isyntax_t* isyntax, isyntax_image_t* wsi
1824
1883
  isyntax_tile_t* source_tile = level->tiles + (tile_y-1) * level->width_in_tiles + (tile_x-1);
1825
1884
  if (source_tile->exists) {
1826
1885
  isyntax_tile_channel_t* color_channel = source_tile->color_channels + color;
1827
- if (!color_channel->coeff_ll) invalid_neighbors_ll |= ISYNTAX_ADJ_TILE_TOP_LEFT;
1828
- if (!color_channel->coeff_h) invalid_neighbors_h |= ISYNTAX_ADJ_TILE_TOP_LEFT;
1886
+ if (!color_channel->coeff_ll && !isyntax_is_parent_tile_missing(wsi, scale, tile_y-1, tile_x-1)) {
1887
+ invalid_neighbors_ll |= ISYNTAX_ADJ_TILE_TOP_LEFT;
1888
+ }
1889
+ if (!color_channel->coeff_h) {
1890
+ invalid_neighbors_h |= ISYNTAX_ADJ_TILE_TOP_LEFT;
1891
+ }
1829
1892
  get_offsetted_coeff_blocks(ll_hl_lh_hh, (top_margin_source_y * source_stride) + left_margin_source_x,
1830
1893
  color_channel, block_stride, h_dummy_coeff, ll_dummy_coeff);
1831
1894
  for (i32 i = 0; i < 4; ++i) {
@@ -1844,8 +1907,12 @@ u32 isyntax_idwt_tile_for_color_channel(isyntax_t* isyntax, isyntax_image_t* wsi
1844
1907
  isyntax_tile_t* source_tile = level->tiles + (tile_y-1) * level->width_in_tiles + tile_x;
1845
1908
  if (source_tile->exists) {
1846
1909
  isyntax_tile_channel_t* color_channel = source_tile->color_channels + color;
1847
- if (!color_channel->coeff_ll) invalid_neighbors_ll |= ISYNTAX_ADJ_TILE_TOP_CENTER;
1848
- if (!color_channel->coeff_h) invalid_neighbors_h |= ISYNTAX_ADJ_TILE_TOP_CENTER;
1910
+ if (!color_channel->coeff_ll && !isyntax_is_parent_tile_missing(wsi, scale, tile_y-1, tile_x)) {
1911
+ invalid_neighbors_ll |= ISYNTAX_ADJ_TILE_TOP_CENTER;
1912
+ }
1913
+ if (!color_channel->coeff_h) {
1914
+ invalid_neighbors_h |= ISYNTAX_ADJ_TILE_TOP_CENTER;
1915
+ }
1849
1916
  get_offsetted_coeff_blocks(ll_hl_lh_hh, (top_margin_source_y * source_stride),
1850
1917
  source_tile->color_channels + color, block_stride, h_dummy_coeff, ll_dummy_coeff);
1851
1918
  for (i32 i = 0; i < 4; ++i) {
@@ -1864,8 +1931,12 @@ u32 isyntax_idwt_tile_for_color_channel(isyntax_t* isyntax, isyntax_image_t* wsi
1864
1931
  isyntax_tile_t* source_tile = level->tiles + (tile_y-1) * level->width_in_tiles + (tile_x+1);
1865
1932
  if (source_tile->exists) {
1866
1933
  isyntax_tile_channel_t* color_channel = source_tile->color_channels + color;
1867
- if (!color_channel->coeff_ll) invalid_neighbors_ll |= ISYNTAX_ADJ_TILE_TOP_RIGHT;
1868
- if (!color_channel->coeff_h) invalid_neighbors_h |= ISYNTAX_ADJ_TILE_TOP_RIGHT;
1934
+ if (!color_channel->coeff_ll && !isyntax_is_parent_tile_missing(wsi, scale, tile_y-1, tile_x+1)) {
1935
+ invalid_neighbors_ll |= ISYNTAX_ADJ_TILE_TOP_RIGHT;
1936
+ }
1937
+ if (!color_channel->coeff_h) {
1938
+ invalid_neighbors_h |= ISYNTAX_ADJ_TILE_TOP_RIGHT;
1939
+ }
1869
1940
  get_offsetted_coeff_blocks(ll_hl_lh_hh, (top_margin_source_y * source_stride),
1870
1941
  source_tile->color_channels + color, block_stride, h_dummy_coeff, ll_dummy_coeff);
1871
1942
  for (i32 i = 0; i < 4; ++i) {
@@ -1884,8 +1955,12 @@ u32 isyntax_idwt_tile_for_color_channel(isyntax_t* isyntax, isyntax_image_t* wsi
1884
1955
  isyntax_tile_t* source_tile = level->tiles + (tile_y) * level->width_in_tiles + (tile_x-1);
1885
1956
  if (source_tile->exists) {
1886
1957
  isyntax_tile_channel_t* color_channel = source_tile->color_channels + color;
1887
- if (!color_channel->coeff_ll) invalid_neighbors_ll |= ISYNTAX_ADJ_TILE_CENTER_LEFT;
1888
- if (!color_channel->coeff_h) invalid_neighbors_h |= ISYNTAX_ADJ_TILE_CENTER_LEFT;
1958
+ if (!color_channel->coeff_ll && !isyntax_is_parent_tile_missing(wsi, scale, tile_y, tile_x-1)) {
1959
+ invalid_neighbors_ll |= ISYNTAX_ADJ_TILE_CENTER_LEFT;
1960
+ }
1961
+ if (!color_channel->coeff_h) {
1962
+ invalid_neighbors_h |= ISYNTAX_ADJ_TILE_CENTER_LEFT;
1963
+ }
1889
1964
  get_offsetted_coeff_blocks(ll_hl_lh_hh, left_margin_source_x,
1890
1965
  source_tile->color_channels + color, block_stride, h_dummy_coeff, ll_dummy_coeff);
1891
1966
  for (i32 i = 0; i < 4; ++i) {
@@ -1919,8 +1994,12 @@ u32 isyntax_idwt_tile_for_color_channel(isyntax_t* isyntax, isyntax_image_t* wsi
1919
1994
  isyntax_tile_t* source_tile = level->tiles + (tile_y) * level->width_in_tiles + (tile_x+1);
1920
1995
  if (source_tile->exists) {
1921
1996
  isyntax_tile_channel_t* color_channel = source_tile->color_channels + color;
1922
- if (!color_channel->coeff_ll) invalid_neighbors_ll |= ISYNTAX_ADJ_TILE_CENTER_RIGHT;
1923
- if (!color_channel->coeff_h) invalid_neighbors_h |= ISYNTAX_ADJ_TILE_CENTER_RIGHT;
1997
+ if (!color_channel->coeff_ll && !isyntax_is_parent_tile_missing(wsi, scale, tile_y, tile_x+1)) {
1998
+ invalid_neighbors_ll |= ISYNTAX_ADJ_TILE_CENTER_RIGHT;
1999
+ }
2000
+ if (!color_channel->coeff_h) {
2001
+ invalid_neighbors_h |= ISYNTAX_ADJ_TILE_CENTER_RIGHT;
2002
+ }
1924
2003
  get_offsetted_coeff_blocks(ll_hl_lh_hh, 0,
1925
2004
  source_tile->color_channels + color, block_stride, h_dummy_coeff, ll_dummy_coeff);
1926
2005
  for (i32 i = 0; i < 4; ++i) {
@@ -1939,8 +2018,12 @@ u32 isyntax_idwt_tile_for_color_channel(isyntax_t* isyntax, isyntax_image_t* wsi
1939
2018
  isyntax_tile_t* source_tile = level->tiles + (tile_y+1) * level->width_in_tiles + (tile_x-1);
1940
2019
  if (source_tile->exists) {
1941
2020
  isyntax_tile_channel_t* color_channel = source_tile->color_channels + color;
1942
- if (!color_channel->coeff_ll) invalid_neighbors_ll |= ISYNTAX_ADJ_TILE_BOTTOM_LEFT;
1943
- if (!color_channel->coeff_h) invalid_neighbors_h |= ISYNTAX_ADJ_TILE_BOTTOM_LEFT;
2021
+ if (!color_channel->coeff_ll && !isyntax_is_parent_tile_missing(wsi, scale, tile_y+1, tile_x-1)) {
2022
+ invalid_neighbors_ll |= ISYNTAX_ADJ_TILE_BOTTOM_LEFT;
2023
+ }
2024
+ if (!color_channel->coeff_h) {
2025
+ invalid_neighbors_h |= ISYNTAX_ADJ_TILE_BOTTOM_LEFT;
2026
+ }
1944
2027
  get_offsetted_coeff_blocks(ll_hl_lh_hh, left_margin_source_x,
1945
2028
  source_tile->color_channels + color, block_stride, h_dummy_coeff, ll_dummy_coeff);
1946
2029
  for (i32 i = 0; i < 4; ++i) {
@@ -1959,8 +2042,12 @@ u32 isyntax_idwt_tile_for_color_channel(isyntax_t* isyntax, isyntax_image_t* wsi
1959
2042
  isyntax_tile_t* source_tile = level->tiles + (tile_y+1) * level->width_in_tiles + tile_x;
1960
2043
  if (source_tile->exists) {
1961
2044
  isyntax_tile_channel_t* color_channel = source_tile->color_channels + color;
1962
- if (!color_channel->coeff_ll) invalid_neighbors_ll |= ISYNTAX_ADJ_TILE_BOTTOM_CENTER;
1963
- if (!color_channel->coeff_h) invalid_neighbors_h |= ISYNTAX_ADJ_TILE_BOTTOM_CENTER;
2045
+ if (!color_channel->coeff_ll && !isyntax_is_parent_tile_missing(wsi, scale, tile_y+1, tile_x)) {
2046
+ invalid_neighbors_ll |= ISYNTAX_ADJ_TILE_BOTTOM_CENTER;
2047
+ }
2048
+ if (!color_channel->coeff_h) {
2049
+ invalid_neighbors_h |= ISYNTAX_ADJ_TILE_BOTTOM_CENTER;
2050
+ }
1964
2051
  get_offsetted_coeff_blocks(ll_hl_lh_hh, 0,
1965
2052
  source_tile->color_channels + color, block_stride, h_dummy_coeff, ll_dummy_coeff);
1966
2053
  for (i32 i = 0; i < 4; ++i) {
@@ -1979,8 +2066,12 @@ u32 isyntax_idwt_tile_for_color_channel(isyntax_t* isyntax, isyntax_image_t* wsi
1979
2066
  isyntax_tile_t* source_tile = level->tiles + (tile_y+1) * level->width_in_tiles + (tile_x+1);
1980
2067
  if (source_tile->exists) {
1981
2068
  isyntax_tile_channel_t* color_channel = source_tile->color_channels + color;
1982
- if (!color_channel->coeff_ll) invalid_neighbors_ll |= ISYNTAX_ADJ_TILE_BOTTOM_RIGHT;
1983
- if (!color_channel->coeff_h) invalid_neighbors_h |= ISYNTAX_ADJ_TILE_BOTTOM_RIGHT;
2069
+ if (!color_channel->coeff_ll && !isyntax_is_parent_tile_missing(wsi, scale, tile_y+1, tile_x+1)) {
2070
+ invalid_neighbors_ll |= ISYNTAX_ADJ_TILE_BOTTOM_RIGHT;
2071
+ }
2072
+ if (!color_channel->coeff_h) {
2073
+ invalid_neighbors_h |= ISYNTAX_ADJ_TILE_BOTTOM_RIGHT;
2074
+ }
1984
2075
  get_offsetted_coeff_blocks(ll_hl_lh_hh, 0,
1985
2076
  source_tile->color_channels + color, block_stride, h_dummy_coeff, ll_dummy_coeff);
1986
2077
  for (i32 i = 0; i < 4; ++i) {
@@ -2181,9 +2272,9 @@ void isyntax_load_tile(isyntax_t* isyntax, isyntax_image_t* wsi, i32 scale, i32
2181
2272
  // float elapsed_rgb = get_seconds_elapsed(start, get_clock());
2182
2273
  // console_print_verbose("load: scale=%d x=%d y=%d idwt time =%g rgb transform time=%g malloc time=%g\n", scale, tile_x, tile_y, elapsed_idwt, elapsed_rgb, elapsed_malloc);
2183
2274
 
2184
- /*if (scale == wsi->max_scale && tile_x == 1 && tile_y == 1) {
2185
- stbi_write_png("debug_dwt_output.png", tile_width, tile_height, 4, bgra, tile_width * 4);
2186
- }*/
2275
+ // if (scale == wsi->max_scale-1 && tile_x == 5 && tile_y == 0) {
2276
+ // stbi_write_png("debug_dwt_output.png", tile_width, tile_height, 4, out_buffer_or_null, tile_width * 4);
2277
+ // }
2187
2278
 
2188
2279
  release_temp_memory(&temp_memory); // free Y, Co and Cg
2189
2280
  }
@@ -3295,6 +3386,8 @@ bool isyntax_open(isyntax_t* isyntax, const char* filename, bool init_allocators
3295
3386
 
3296
3387
  }
3297
3388
 
3389
+ // isyntax_dump_block_header(wsi_image, "test_block_header.csv");
3390
+
3298
3391
  parse_ticks_elapsed += (get_clock() - parse_begin);
3299
3392
  // console_print("iSyntax: the seektable is %u bytes, or %g%% of the total file size\n", seektable_size, (float)((float)seektable_size * 100.0f) / isyntax->filesize);
3300
3393
  // console_print(" I/O time: %g seconds\n", get_seconds_elapsed(0, io_ticks_elapsed));