contrek 1.2.8 → 1.3.0

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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.rubocop.yml +11 -0
  4. data/CHANGELOG.md +7 -1
  5. data/Gemfile +2 -0
  6. data/Gemfile.lock +1 -1
  7. data/README.md +1 -1
  8. data/Rakefile +2 -0
  9. data/contrek.gemspec +2 -0
  10. data/ext/cpp_polygon_finder/PolygonFinder/CMakeLists.txt +2 -4
  11. data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +118 -19
  12. data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.h +1 -0
  13. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.cpp +5 -7
  14. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.h +4 -1
  15. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.cpp +14 -15
  16. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.h +2 -4
  17. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Polygon.h +2 -2
  18. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +13 -13
  19. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.h +79 -354
  20. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.cpp +6 -6
  21. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.h +3 -2
  22. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.cpp +8 -8
  23. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/EndPoint.h +4 -4
  24. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.h +3 -1
  25. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/InnerPolyline.cpp +6 -6
  26. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/InnerPolyline.h +3 -3
  27. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Merger.cpp +4 -3
  28. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.cpp +29 -9
  29. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.h +3 -1
  30. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.cpp +45 -30
  31. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp +18 -19
  32. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.h +6 -5
  33. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Position.cpp +2 -2
  34. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Position.h +1 -1
  35. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Queueable.h +4 -4
  36. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Sequence.cpp +7 -7
  37. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Sequence.h +2 -2
  38. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Shape.cpp +8 -2
  39. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Shape.h +4 -1
  40. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/ShapePool.cpp +19 -5
  41. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/ShapePool.h +6 -2
  42. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/StreamingMerger.cpp +117 -0
  43. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/StreamingMerger.h +41 -0
  44. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.cpp +9 -0
  45. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.h +1 -0
  46. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/VerticalMerger.cpp +4 -13
  47. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/VerticalMerger.h +1 -0
  48. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/LinearReducer.cpp +8 -8
  49. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/LinearReducer.h +2 -2
  50. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/Reducer.cpp +1 -1
  51. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/Reducer.h +2 -2
  52. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/UniqReducer.cpp +3 -3
  53. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/UniqReducer.h +1 -1
  54. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/VisvalingamReducer.cpp +3 -3
  55. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/VisvalingamReducer.h +10 -10
  56. data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +66 -16
  57. data/ext/cpp_polygon_finder/extconf.rb +2 -0
  58. data/lib/contrek/bitmaps/bitmap.rb +2 -0
  59. data/lib/contrek/bitmaps/chunky_bitmap.rb +13 -0
  60. data/lib/contrek/bitmaps/painting.rb +2 -0
  61. data/lib/contrek/bitmaps/png_bitmap.rb +2 -0
  62. data/lib/contrek/bitmaps/raw_bitmap.rb +2 -0
  63. data/lib/contrek/bitmaps/rgb_color.rb +2 -0
  64. data/lib/contrek/bitmaps/rgb_cpp_color.rb +2 -0
  65. data/lib/contrek/bitmaps/sample_generator.rb +2 -0
  66. data/lib/contrek/cpp/cpp_concurrent_finder.rb +2 -0
  67. data/lib/contrek/cpp/cpp_concurrent_horizontal_merger.rb +2 -0
  68. data/lib/contrek/cpp/cpp_concurrent_merger.rb +2 -0
  69. data/lib/contrek/cpp/cpp_concurrent_streaming_merger.rb +11 -0
  70. data/lib/contrek/cpp/cpp_concurrent_vertical_merger.rb +2 -0
  71. data/lib/contrek/cpp/cpp_result.rb +2 -0
  72. data/lib/contrek/cpp/cpp_tempfile.rb +28 -0
  73. data/lib/contrek/finder/bounds.rb +2 -0
  74. data/lib/contrek/finder/concurrent/clipped_polygon_finder.rb +2 -0
  75. data/lib/contrek/finder/concurrent/cluster.rb +3 -1
  76. data/lib/contrek/finder/concurrent/cursor.rb +2 -0
  77. data/lib/contrek/finder/concurrent/end_point.rb +2 -0
  78. data/lib/contrek/finder/concurrent/fake_cluster.rb +2 -0
  79. data/lib/contrek/finder/concurrent/finder.rb +6 -0
  80. data/lib/contrek/finder/concurrent/horizontal_merger.rb +2 -0
  81. data/lib/contrek/finder/concurrent/hub.rb +2 -0
  82. data/lib/contrek/finder/concurrent/inner_polyline.rb +2 -0
  83. data/lib/contrek/finder/concurrent/listable.rb +2 -0
  84. data/lib/contrek/finder/concurrent/merger.rb +4 -0
  85. data/lib/contrek/finder/concurrent/part.rb +13 -0
  86. data/lib/contrek/finder/concurrent/partitionable.rb +33 -17
  87. data/lib/contrek/finder/concurrent/polyline.rb +2 -0
  88. data/lib/contrek/finder/concurrent/poolable.rb +2 -0
  89. data/lib/contrek/finder/concurrent/position.rb +2 -0
  90. data/lib/contrek/finder/concurrent/queueable.rb +2 -0
  91. data/lib/contrek/finder/concurrent/sequence.rb +2 -0
  92. data/lib/contrek/finder/concurrent/shape.rb +2 -0
  93. data/lib/contrek/finder/concurrent/streaming_merger.rb +89 -0
  94. data/lib/contrek/finder/concurrent/tile.rb +2 -0
  95. data/lib/contrek/finder/concurrent/vertical_merger.rb +8 -2
  96. data/lib/contrek/finder/list.rb +2 -0
  97. data/lib/contrek/finder/list_entry.rb +2 -0
  98. data/lib/contrek/finder/listable.rb +2 -0
  99. data/lib/contrek/finder/lists.rb +2 -0
  100. data/lib/contrek/finder/node.rb +2 -0
  101. data/lib/contrek/finder/node_cluster.rb +3 -1
  102. data/lib/contrek/finder/polygon_finder.rb +4 -1
  103. data/lib/contrek/finder/result.rb +2 -0
  104. data/lib/contrek/map/mercator_projection.rb +2 -0
  105. data/lib/contrek/matchers/matcher.rb +2 -0
  106. data/lib/contrek/matchers/matcher_hsb.rb +2 -0
  107. data/lib/contrek/matchers/value_not_matcher.rb +2 -0
  108. data/lib/contrek/reducers/linear_reducer.rb +2 -0
  109. data/lib/contrek/reducers/reducer.rb +2 -0
  110. data/lib/contrek/reducers/uniq_reducer.rb +2 -0
  111. data/lib/contrek/reducers/visvalingam_reducer.rb +2 -0
  112. data/lib/contrek/shared/result.rb +2 -0
  113. data/lib/contrek/version.rb +3 -1
  114. data/lib/contrek.rb +5 -0
  115. metadata +11 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d6702682d1ddd30acbdc45a4344b1d761473afc336b7c07734f169739228f855
4
- data.tar.gz: 8fde7e85a08f0e8037b7711b303fefb0d4edcff1f7c9725a767ff09f815445a6
3
+ metadata.gz: 58d88411970256b3edc37da05dd9304aaa4593b320e7d73db5966b69d403c4cf
4
+ data.tar.gz: 6a05687f7ffba4b22eb7ebf02abf2a622931bc3250968fa785fb13720036118e
5
5
  SHA512:
6
- metadata.gz: f498831ee81c1ff533ce412b39efdfe9c9dc42ff40679749600a2e2cf833cd8193b1f3ca8bf00e8ff98ae3a3798f9670380274d3cf63e485bad4428200256af4
7
- data.tar.gz: 4fd128cf5cd14d929f2baaf9c611708cd9bd8ce01550e267f1d93252f5f23a9012b6f66bb59a4ab4390cb02c38b31a37f3fc6fd56d22478cf046359834a3bf0d
6
+ metadata.gz: 7150e06efce7580fdefd55725db0de52e2805b909e800679a0f99481c3e52333209d1b5ceb750c1fc63a28fa6aaaad7f7662d3baf3ebf377b29cfe4dacf6d19d
7
+ data.tar.gz: 988e85fd99234df2b3cd0e8bec0d8f68ba0f631111a3fda62e114e185dd1619589223266b5e3db71a2a5c478d2871f862209489df997a13a9da866b84718c609
data/.gitignore CHANGED
@@ -3,6 +3,7 @@
3
3
  .ruby-version
4
4
  # rspec failure tracking
5
5
  .rspec_status
6
+ /spec/files/performance_history.json
6
7
 
7
8
  /ext/cpp_polygon_finder/PolygonFinder/build/
8
9
  /ext/cpp_polygon_finder/PolygonFinder/contrek
@@ -12,4 +13,4 @@
12
13
  /ext/cpp_polygon_finder/cpp_polygon_finder.o
13
14
  /ext/cpp_polygon_finder/cpp_polygon_finder.so
14
15
  /ext/cpp_polygon_finder/mkmf.log
15
- /lib/cpp_polygon_finder.so
16
+ /lib/cpp_polygon_finder.so
data/.rubocop.yml ADDED
@@ -0,0 +1,11 @@
1
+ AllCops:
2
+ DisabledByDefault: true
3
+ TargetRubyVersion: 3.1
4
+ Exclude:
5
+ - 'bin/**/*'
6
+ - 'vendor/**/*'
7
+
8
+ # Attiva l'obbligo del magic comment
9
+ Style/FrozenStringLiteralComment:
10
+ Enabled: true
11
+ EnforcedStyle: always
data/CHANGELOG.md CHANGED
@@ -115,4 +115,10 @@ All notable changes to this project will be documented in this file.
115
115
 
116
116
  ## [1.2.7] - 2026-06-02
117
117
  ### Changed
118
- - **Refactored `bounds` option:** Starting from this release, precalculated bounds for each polygon can now be requested in concurrent mode as well, in addition to single-threaded mode.
118
+ - **Refactored `bounds` option:** Starting from this release, precalculated bounds for each polygon can now be requested in concurrent mode as well, in addition to single-threaded mode.
119
+
120
+ ## [1.2.8] - 2026-06-07
121
+ - **Optimize main pixel scanning loop:** Implemented 4-way loop unrolling to maximize L1 cache hits and eliminate redundant RAM lookups via direct register bit-casting.
122
+
123
+ ## [1.2.9] - 2026-06-13
124
+ - **Streaming merger:** The streaming merger class extends VerticalMerger and adds a useful feature: the progressive extraction of contours into a disk buffer (SVG file). In this way, all extracted polygons that are no longer within the junction zone of the next stripe are removed from the system and streamed directly to disk. This incredibly reduces memory consumption, allowing the processing of very large files on machines with low memory availability, at the expense of increased processing times. An example of this technique is available in both C++ and Ruby in the repository.
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  gemspec
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- contrek (1.2.7)
4
+ contrek (1.3.0)
5
5
  chunky_png (~> 1.4)
6
6
  concurrent-ruby (~> 1.3.5)
7
7
  rice (= 4.5.0)
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # Contrek
2
- Contrek is a Ruby gem powered by a <u>standalone C++17 core library</u> for fast contour tracing and edge detection in PNG images and raw memory streams. It employs a **topological approach** to extract polygonal contours, representing shapes as a connected graph of shared endpoints. This ensures perfect adjacency and structural integrity for shape analysis and raster-to-vector workflows, such as PNG to SVG conversion, managed via libspng (0.7.4) with multithreading support.
2
+ Contrek is a Ruby gem powered by a <u>[standalone C++17 core library](#-c-standalone-library-usage)</u> for fast contour tracing and edge detection in PNG images and raw memory streams. It employs a **topological approach** to extract polygonal contours, representing shapes as a connected graph of shared endpoints. This ensures perfect adjacency and structural integrity for shape analysis and raster-to-vector workflows, such as PNG to SVG conversion, managed via libspng (0.7.4) with multithreading support.
3
3
 
4
4
 
5
5
  ## About Contrek library
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rake"
2
4
 
3
5
  desc "compiles c++ extension"
data/contrek.gemspec CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  lib = File.expand_path("lib", __dir__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
5
  require "contrek/version"
@@ -11,10 +11,8 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
11
11
  list(FILTER CMAKE_CXX_FLAGS EXCLUDE REGEX "-DNDEBUG")
12
12
  list(FILTER CMAKE_C_FLAGS EXCLUDE REGEX "-DNDEBUG")
13
13
  else()
14
- #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pthread -march=native -DNDEBUG -Ofast -flto")
15
- #set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -march=native -fPIC -DNDEBUG")
16
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pthread -march=native -DNDEBUG -Ofast -flto -fopenmp-simd -ftree-vectorize")
17
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -pthread -march=native -DNDEBUG -Ofast -flto -fopenmp-simd -ftree-vectorize")
14
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pthread -march=native -fPIC -DNDEBUG -Ofast -flto -ftree-vectorize")
15
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -pthread -march=native -fPIC -DNDEBUG -Ofast -flto -ftree-vectorize")
18
16
  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread -flto=auto -Wl,--no-as-needed")
19
17
  find_library(TCMALLOC_LIB tcmalloc)
20
18
  if(TCMALLOC_LIB)
@@ -34,6 +34,7 @@
34
34
  #include "polygon/finder/concurrent/Finder.h"
35
35
  #include "polygon/finder/concurrent/HorizontalMerger.h"
36
36
  #include "polygon/finder/concurrent/VerticalMerger.h"
37
+ #include "polygon/finder/concurrent/StreamingMerger.h"
37
38
  #include "polygon/finder/concurrent/Sequence.h"
38
39
  #include "polygon/finder/concurrent/Position.h"
39
40
  #include "polygon/finder/Polygon.h"
@@ -59,16 +60,16 @@ void Tests::test_a()
59
60
  std::vector<int> array_compare;
60
61
 
61
62
  for (const auto& x : o->polygons)
62
- { for (const Point* p : x.outer) {
63
- array_compare.push_back(p->x);
64
- array_compare.push_back(p->y);
63
+ { for (const Point& p : x.outer) {
64
+ array_compare.push_back(p.x);
65
+ array_compare.push_back(p.y);
65
66
  }
66
67
  if (outer_array != array_compare) throw std::runtime_error("Wrong OUTER results!");
67
68
  array_compare.clear();
68
69
  for (const auto& z : x.inner)
69
- { for (const Point* y : z)
70
- { array_compare.push_back(y->x);
71
- array_compare.push_back(y->y);
70
+ { for (const Point& y : z)
71
+ { array_compare.push_back(y.x);
72
+ array_compare.push_back(y.y);
72
73
  }
73
74
  }
74
75
  if (inner_array != array_compare) throw std::runtime_error("Wrong INNER results!");
@@ -96,9 +97,9 @@ void Tests::test_b()
96
97
 
97
98
  void Tests::test_c()
98
99
  { Sequence sequence;
99
- Point* p1 = new Point({1, 1});
100
- Point* p2 = new Point({2, 2});
101
- Point* p3 = new Point({3, 3});
100
+ Point p1{1, 1};
101
+ Point p2{2, 2};
102
+ Point p3{3, 3};
102
103
 
103
104
  Hub* hub = new Hub(4);
104
105
 
@@ -113,7 +114,7 @@ void Tests::test_c()
113
114
  if (sequence.size != 3) throw std::runtime_error("Wrong sequence size");
114
115
 
115
116
  // iterator() initially gives head
116
- Point* head = sequence.head->payload;
117
+ const Point& head = sequence.head->payload;
117
118
  if (head != p1) throw std::runtime_error("Wrong head");
118
119
  if (sequence.iterator()->payload != p1) throw std::runtime_error("Wrong iterator to head");
119
120
  if (sequence.iterator() != pos1) throw std::runtime_error("Wrong iterator to head");
@@ -134,10 +135,6 @@ void Tests::test_c()
134
135
  sequence.rewind();
135
136
  if (sequence.iterator()->payload != p1) throw std::runtime_error("Wrong iterator to head");
136
137
 
137
- delete p1;
138
- delete p2;
139
- delete p3;
140
-
141
138
  delete hub;
142
139
 
143
140
  delete pos1;
@@ -347,10 +344,8 @@ void stream_png_image(const std::string& filepath, uint32_t stripe_height, bool
347
344
  ProcessResult *result = polygon_finder.process_info();
348
345
  if (result) {
349
346
  std::cout << "stripe " << stripe_count << ": found polygons " << result->groups << std::endl;
350
- ProcessResult* safe_result = result->clone();
351
- result_clones.push_back(safe_result);
352
- vmerger.add_tile(*safe_result);
353
- delete result;
347
+ result_clones.push_back(result);
348
+ vmerger.add_tile(*result);
354
349
  }
355
350
  stripe_count++;
356
351
  }
@@ -385,6 +380,110 @@ void stream_png_image(const std::string& filepath, uint32_t stripe_height, bool
385
380
  }
386
381
 
387
382
  void Tests::test_i() {
388
- stream_png_image("../images/graphs_1024x1024.png", 300);
383
+ stream_png_image("../images/graphs_1024x1024.png", 300, true);
384
+ std::cout << "Memory usage peak: " << get_peak_rss() << " MB" << std::endl;
385
+ }
386
+
387
+ void stream_progressive_png_image(const std::string& filepath, uint32_t stripe_height) {
388
+ std::vector<ProcessResult*> result_clones;
389
+ std::vector<std::string> varguments = {"--bounds"};
390
+ // opens image to stream
391
+ FILE* fp = fopen(filepath.c_str(), "rb");
392
+ if (!fp) {
393
+ std::cerr << "Unable open file: " << filepath << std::endl;
394
+ return;
395
+ }
396
+
397
+ // exams image
398
+ spng_ctx *ctx = spng_ctx_new(0);
399
+ spng_set_png_file(ctx, fp);
400
+ struct spng_ihdr ihdr;
401
+ if (spng_get_ihdr(ctx, &ihdr)) {
402
+ fclose(fp);
403
+ spng_ctx_free(ctx);
404
+ return;
405
+ }
406
+ uint32_t total_width = ihdr.width;
407
+ uint32_t total_height = ihdr.height;
408
+
409
+ // allocates stripe buffer
410
+ RawBitmap stripe_bitmap;
411
+ stripe_bitmap.define(total_width, stripe_height, 4, true);
412
+ RGBNotMatcher not_matcher(-1);
413
+ if (spng_decode_image(ctx, NULL, 0, SPNG_FMT_RGBA8, SPNG_DECODE_PROGRESSIVE)) {
414
+ fclose(fp);
415
+ spng_ctx_free(ctx);
416
+ return;
417
+ }
418
+
419
+ // allocates streaming svg buffer
420
+ std::string output_path = "streaming_buffer.svg";
421
+ std::ofstream shared_stream(output_path, std::ios::out | std::ios::binary);
422
+ if (!shared_stream) {
423
+ std::cerr << "Error: Unable creating output streaming file!" << std::endl;
424
+ }
425
+
426
+ StreamingMerger vmerger(0, &varguments, &shared_stream, total_width, total_height);
427
+ try {
428
+ size_t row_size = static_cast<size_t>(total_width) * 4;
429
+ int stripe_count = 0;
430
+ // main stripes loop
431
+ for (uint32_t current_y_offset = 0; current_y_offset < total_height; current_y_offset += stripe_height) {
432
+ int uncovered_height = total_height - current_y_offset;
433
+
434
+ // copy previous last line to the next new one (each contigue stripe must share one pixel scanline)
435
+ if (current_y_offset > 0) {
436
+ unsigned char* last_row_prev = const_cast<unsigned char*>(stripe_bitmap.get_row_ptr(stripe_height - 1));
437
+ unsigned char* first_row_curr = const_cast<unsigned char*>(stripe_bitmap.get_row_ptr(0));
438
+ std::memcpy(first_row_curr, last_row_prev, row_size);
439
+ }
440
+ // clears the part of the stripe wont be overwritten by png data
441
+ if (uncovered_height < stripe_height)
442
+ { stripe_bitmap.draw_filled_rectangle(0, 1, total_width, stripe_height - 1, 255, 255, 255);
443
+ }
444
+ // decoding data directly in the stripe buffer
445
+ uint32_t lines_to_read = std::min(stripe_height, total_height - current_y_offset);
446
+ for (uint32_t y = (current_y_offset == 0 ? 0 : 1); y < lines_to_read; y++) {
447
+ unsigned char* row_ptr = const_cast<unsigned char*>(stripe_bitmap.get_row_ptr(y));
448
+ int ret = spng_decode_row(ctx, row_ptr, row_size);
449
+ if (ret != 0 && ret != SPNG_EOI) break;
450
+ }
451
+ // stripe contour tracing
452
+ std::vector<std::string> finder_arguments = {
453
+ "--versus=a",
454
+ "--bounds",
455
+ "--strict_bounds", // <- this option is strictly needed when working with vertical merger
456
+ "--compress_uniq",
457
+ "--compress_linear"
458
+ };
459
+
460
+ PolygonFinder polygon_finder(&stripe_bitmap, &not_matcher, nullptr, &finder_arguments);
461
+ ProcessResult *result = polygon_finder.process_info();
462
+ if (result) {
463
+ std::cout << "stripe " << stripe_count << ": found polygons " << result->groups << std::endl;
464
+ result_clones.push_back(result);
465
+ vmerger.add_tile(*result, !(current_y_offset + stripe_height < total_height));
466
+ }
467
+ stripe_count++;
468
+ }
469
+
470
+ ProcessResult *merged_result = vmerger.process_info();
471
+ std::cout << "total found polygons " << merged_result->groups << std::endl;
472
+ delete merged_result;
473
+
474
+ // frees memory
475
+ for (auto c : result_clones) {
476
+ delete c;
477
+ }
478
+ } catch (const std::exception& e) {
479
+ std::cerr << "\n[ERROR] Processing exception: " << e.what() << std::endl;
480
+ if (shared_stream.is_open()) shared_stream.close();
481
+ }
482
+ spng_ctx_free(ctx);
483
+ fclose(fp);
484
+ }
485
+
486
+ void Tests::test_l() {
487
+ stream_progressive_png_image("../images/mixed_shapes_1024x1024.png", 300);
389
488
  std::cout << "Memory usage peak: " << get_peak_rss() << " MB" << std::endl;
390
489
  }
@@ -20,4 +20,5 @@ class Tests {
20
20
  virtual void test_g();
21
21
  virtual void test_h();
22
22
  virtual void test_i();
23
+ virtual void test_l();
23
24
  };
@@ -100,33 +100,31 @@ Node* Node::my_next_outer(Node *last, int versus) {
100
100
  return get_tangent_node_by_virtual_index(this->tangs_sequence[last_node_index]);
101
101
  }
102
102
 
103
- Point* Node::coords_entering_to(Node *enter_to, int mode, int tracking) {
103
+ Point Node::coords_entering_to(Node *enter_to, int mode, int tracking) {
104
104
  int enter_to_index;
105
105
  if (enter_to->y < this->y) enter_to_index = enter_to->abs_x_index + this->up_indexer;
106
106
  else enter_to_index = this->down_indexer - enter_to->abs_x_index;
107
107
 
108
108
  int tg_index = this->tangs_sequence[enter_to_index];
109
- Point* point;
110
109
  if (tg_index < 0) {
111
110
  Node& node_up = cluster->vert_nodes[y + T_UP][-(tg_index + 1)];
112
111
  if (mode == Node::A) {
113
112
  enter_to->track |= TURNER[tracking][OMAX - 1];
114
- point = &node_up.end_point;
113
+ return node_up.end_point;
115
114
  } else {
116
115
  enter_to->track |= TURNER[tracking][OMIN - 1];
117
- point = &node_up.start_point;
116
+ return node_up.start_point;
118
117
  }
119
118
  } else {
120
119
  Node& node_down = cluster->vert_nodes[y + T_DOWN][tg_index];
121
120
  if (mode == Node::A) {
122
121
  enter_to->track |= TURNER[tracking][OMIN - 1];
123
- point = &node_down.start_point;
122
+ return node_down.start_point;
124
123
  } else {
125
124
  enter_to->track |= TURNER[tracking][OMAX - 1];
126
- point = &node_down.end_point;
125
+ return node_down.end_point;
127
126
  }
128
127
  }
129
- return point;
130
128
  }
131
129
 
132
130
  Node* Node::get_tangent_node_by_virtual_index(int virtual_index) {
@@ -61,6 +61,9 @@ struct Point {
61
61
  bool operator==(const Point& other) const {
62
62
  return x == other.x && y == other.y;
63
63
  }
64
+ bool operator!=(const Point& other) const {
65
+ return !(*this == other);
66
+ }
64
67
  Point(int x_, int y_) : x(x_), y(y_) {}
65
68
  Point() : x(0), y(0) {}
66
69
  };
@@ -96,7 +99,7 @@ class Node : public Listable {
96
99
  NodeCluster* cluster;
97
100
  void add_intersection(Node& other_node, int other_node_index);
98
101
  SmallVec tangs_sequence;
99
- Point* coords_entering_to(Node *enter_to, int mode, int tracking);
102
+ Point coords_entering_to(Node *enter_to, int mode, int tracking);
100
103
  Node* my_next_outer(Node *last, int versus);
101
104
  Node* my_next_inner(Node *last, int versus);
102
105
  Node* get_tangent_node_by_virtual_index(int vitual_index);
@@ -18,7 +18,6 @@
18
18
  #include "NodeCluster.h"
19
19
  #include "Node.h"
20
20
  #include "RectBounds.h"
21
- #include "PointPool.h"
22
21
  #include "../reducers/UniqReducer.h"
23
22
  #include "../reducers/LinearReducer.h"
24
23
  #include "../reducers/VisvalingamReducer.h"
@@ -44,7 +43,7 @@ NodeCluster::~NodeCluster() {
44
43
  void NodeCluster::compress_coords(std::list<Polygon>& polygons, pf_Options options) {
45
44
  if (!(options.compress_linear || options.compress_uniq || options.compress_visvalingam)) return;
46
45
 
47
- auto compress_sequence = [&](std::vector<Point*>& points_vec) {
46
+ auto compress_sequence = [&](std::vector<Point>& points_vec) {
48
47
  if (points_vec.empty()) return;
49
48
 
50
49
  if (options.compress_uniq) {
@@ -122,9 +121,9 @@ void NodeCluster::plot(int versus) {
122
121
  next_node = root_node->get_tangent_node_by_virtual_index(root_node->tangs_sequence.front());
123
122
 
124
123
  if (next_node != nullptr)
125
- { Point* p = next_node->coords_entering_to(root_node, versus_inverter[versus], Node::OUTER);
124
+ { Point p = next_node->coords_entering_to(root_node, versus_inverter[versus], Node::OUTER);
125
+ poly.bounds.expand(p.x, p.y);
126
126
  poly.outer.push_back(p);
127
- poly.bounds.expand(p->x, p->y);
128
127
  }
129
128
  if ((this->nodes > 0) && (next_node != nullptr))
130
129
  { plot_node(poly.outer, next_node, root_node, versus, poly.bounds);
@@ -137,7 +136,7 @@ void NodeCluster::plot(int versus) {
137
136
  int index_inner = 0;
138
137
  while (inner_plot->size() > 0)
139
138
  { this->plot_sequence.clear();
140
- std::vector<Point*> inner_sequence;
139
+ std::vector<Point> inner_sequence;
141
140
  std::list<Node*>::iterator first_i;
142
141
  Node *first = nullptr;
143
142
 
@@ -243,7 +242,7 @@ std::pair<int, int> NodeCluster::test_in_hole_o(Node* node)
243
242
  return {-1, -1};
244
243
  }
245
244
 
246
- void NodeCluster::plot_inner_node(std::vector<Point*>& sequence_coords, Node *node, int versus, Node *stop_at, Node *start_node) {
245
+ void NodeCluster::plot_inner_node(std::vector<Point>& sequence_coords, Node *node, int versus, Node *stop_at, Node *start_node) {
247
246
  Node *current_node = node;
248
247
  bool strict_bounds = this->options->strict_bounds;
249
248
  while (current_node != nullptr) {
@@ -279,8 +278,8 @@ void NodeCluster::plot_inner_node(std::vector<Point*>& sequence_coords, Node *no
279
278
  }
280
279
  }
281
280
  } else if (strict_bounds) {
282
- sequence_coords.push_back(this->points_pool.acquire((first_is_max ? last_node->max_x : last_node->min_x), current_node->y));
283
- sequence_coords.push_back(this->points_pool.acquire((first_is_max ? next_node->min_x : next_node->max_x), current_node->y));
281
+ sequence_coords.push_back(Point{(first_is_max ? last_node->max_x : last_node->min_x), current_node->y});
282
+ sequence_coords.push_back(Point{(first_is_max ? next_node->min_x : next_node->max_x), current_node->y});
284
283
  }
285
284
 
286
285
  if (current_node->track_uncomplete()) {
@@ -293,7 +292,7 @@ void NodeCluster::plot_inner_node(std::vector<Point*>& sequence_coords, Node *no
293
292
  }
294
293
  }
295
294
 
296
- void NodeCluster::plot_node(std::vector<Point*>& sequence_coords, Node *node, Node *start_node, int versus, RectBounds& bounds) {
295
+ void NodeCluster::plot_node(std::vector<Point>& sequence_coords, Node *node, Node *start_node, int versus, RectBounds& bounds) {
297
296
  Node *current_node = node;
298
297
  bool strict_bounds = this->options->strict_bounds;
299
298
 
@@ -315,23 +314,23 @@ void NodeCluster::plot_node(std::vector<Point*>& sequence_coords, Node *node, No
315
314
  plot = (n == next_node);
316
315
  }
317
316
  if (plot) {
318
- Point* p = last_node->coords_entering_to(current_node, versus, Node::OUTER);
317
+ Point p = last_node->coords_entering_to(current_node, versus, Node::OUTER);
318
+ bounds.expand(p.x, p.y);
319
319
  sequence_coords.push_back(p);
320
- bounds.expand(p->x, p->y);
321
320
  if (current_node != start_node) {
322
321
  inner_plot->contains(current_node) ? inner_plot->remove(current_node) : inner_plot->push_back(current_node);
323
322
  if (last_node->y == next_node->y) {
324
- Point* p1 = next_node->coords_entering_to(current_node, versus_inverter[versus], Node::OUTER);
323
+ Point p1 = next_node->coords_entering_to(current_node, versus_inverter[versus], Node::OUTER);
324
+ bounds.expand(p1.x, p1.y);
325
325
  sequence_coords.push_back(p1);
326
- bounds.expand(p1->x, p1->y);
327
326
  inner_plot->contains(current_node) ? inner_plot->remove(current_node) : inner_plot->push_back(current_node);
328
327
  }
329
328
  }
330
329
  } else if (strict_bounds) {
331
330
  bool is_down = current_node->y > last_node->y;
332
331
  bool is_a = (versus == Node::A);
333
- sequence_coords.push_back(this->points_pool.acquire((is_down == is_a ? last_node->min_x : last_node->max_x), current_node->y));
334
- sequence_coords.push_back(this->points_pool.acquire((is_down == is_a ? next_node->max_x : next_node->min_x), current_node->y));
332
+ sequence_coords.push_back(Point{(is_down == is_a ? last_node->min_x : last_node->max_x), current_node->y});
333
+ sequence_coords.push_back(Point{(is_down == is_a ? next_node->max_x : next_node->min_x), current_node->y});
335
334
  }
336
335
 
337
336
  if (current_node == start_node) {
@@ -18,7 +18,6 @@
18
18
  #include "Lists.h"
19
19
  #include "RectBounds.h"
20
20
  #include "Polygon.h"
21
- #include "PointPool.h"
22
21
 
23
22
  class Node;
24
23
  struct Point;
@@ -26,8 +25,8 @@ struct pf_Options;
26
25
 
27
26
  class NodeCluster {
28
27
  private:
29
- void plot_node(std::vector<Point*>& sequence_coords, Node *node, Node *start_node, int versus, RectBounds& bounds);
30
- void plot_inner_node(std::vector<Point*>& sequence_coords, Node *node, int versus, Node *stop_at, Node *start_node);
28
+ void plot_node(std::vector<Point>& sequence_coords, Node *node, Node *start_node, int versus, RectBounds& bounds);
29
+ void plot_inner_node(std::vector<Point>& sequence_coords, Node *node, int versus, Node *stop_at, Node *start_node);
31
30
  std::vector<Node*> plot_sequence;
32
31
  List *inner_plot;
33
32
  List *inner_new;
@@ -35,7 +34,6 @@ class NodeCluster {
35
34
  int count = 0;
36
35
  int nodes;
37
36
  int width;
38
- PointPool points_pool;
39
37
 
40
38
  public:
41
39
  pf_Options *options;
@@ -14,8 +14,8 @@
14
14
  #include "RectBounds.h"
15
15
 
16
16
  struct Polygon {
17
- std::vector<Point*> outer;
18
- std::list<std::vector<Point*>> inner;
17
+ std::vector<Point> outer;
18
+ std::list<std::vector<Point>> inner;
19
19
  RectBounds bounds;
20
20
  Polygon() : bounds(RectBounds::empty()) {}
21
21
  };
@@ -46,7 +46,6 @@ PolygonFinder::PolygonFinder(Bitmap *bitmap,
46
46
  cpu_timer.start();
47
47
  scan();
48
48
  reports["scan"] = cpu_timer.stop();
49
- //std::cout << "scan " << reports["scan"] << std::endl;
50
49
  //=====================//
51
50
 
52
51
  //= BUILD_TANGS_SEQUENCE ===//
@@ -109,6 +108,7 @@ ProcessResult* PolygonFinder::process_info() {
109
108
  pr->width = this->source_bitmap->w();
110
109
  pr->height = this->source_bitmap->h();
111
110
  pr->has_bounds = this->node_cluster->options->bounds;
111
+ pr->versus = this->options.versus;
112
112
 
113
113
  if (this->node_cluster->options->named_sequences && typeid(*this->source_bitmap) == typeid(Bitmap))
114
114
  { std::string sequence;
@@ -134,25 +134,25 @@ void ProcessResult::draw_on_bitmap(RawBitmap& bitmap) const {
134
134
  // --- OUTER ---
135
135
  if (!poly.outer.empty()) {
136
136
  for (size_t i = 0; i < poly.outer.size() - 1; ++i) {
137
- Point* p1 = poly.outer[i];
138
- Point* p2 = poly.outer[i+1];
139
- bitmap.draw_line(p1->x, p1->y, p2->x, p2->y, 255, 0, 0, 255);
137
+ const Point& p1 = poly.outer[i];
138
+ const Point& p2 = poly.outer[i+1];
139
+ bitmap.draw_line(p1.x, p1.y, p2.x, p2.y, 255, 0, 0, 255);
140
140
  }
141
- Point* last = poly.outer.back();
142
- Point* first = poly.outer.front();
143
- bitmap.draw_line(last->x, last->y, first->x, first->y, 255, 0, 0, 255);
141
+ const Point& last = poly.outer.back();
142
+ const Point& first = poly.outer.front();
143
+ bitmap.draw_line(last.x, last.y, first.x, first.y, 255, 0, 0, 255);
144
144
  }
145
145
  // --- INNER ---
146
146
  for (const auto& sequence : poly.inner) {
147
147
  if (sequence.empty()) continue;
148
148
  for (size_t i = 0; i < sequence.size() - 1; ++i) {
149
- Point* p1 = sequence[i];
150
- Point* p2 = sequence[i+1];
151
- bitmap.draw_line(p1->x, p1->y, p2->x, p2->y, 0, 128, 0, 255);
149
+ const Point& p1 = sequence[i];
150
+ const Point& p2 = sequence[i+1];
151
+ bitmap.draw_line(p1.x, p1.y, p2.x, p2.y, 0, 128, 0, 255);
152
152
  }
153
- Point* last = sequence.back();
154
- Point* first = sequence.front();
155
- bitmap.draw_line(last->x, last->y, first->x, first->y, 0, 128, 0, 255);
153
+ const Point& last = sequence.back();
154
+ const Point& first = sequence.front();
155
+ bitmap.draw_line(last.x, last.y, first.x, first.y, 0, 128, 0, 255);
156
156
  }
157
157
  }
158
158
  }