contrek 1.2.8 → 1.2.9

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 (79) 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 +108 -1
  12. data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.h +1 -0
  13. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +0 -1
  14. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.h +70 -297
  15. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.h +1 -1
  16. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp +6 -6
  17. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.h +3 -2
  18. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/StreamingMerger.cpp +114 -0
  19. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/StreamingMerger.h +41 -0
  20. data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +52 -2
  21. data/ext/cpp_polygon_finder/extconf.rb +2 -0
  22. data/lib/contrek/bitmaps/bitmap.rb +2 -0
  23. data/lib/contrek/bitmaps/chunky_bitmap.rb +2 -0
  24. data/lib/contrek/bitmaps/painting.rb +2 -0
  25. data/lib/contrek/bitmaps/png_bitmap.rb +2 -0
  26. data/lib/contrek/bitmaps/raw_bitmap.rb +2 -0
  27. data/lib/contrek/bitmaps/rgb_color.rb +2 -0
  28. data/lib/contrek/bitmaps/rgb_cpp_color.rb +2 -0
  29. data/lib/contrek/bitmaps/sample_generator.rb +2 -0
  30. data/lib/contrek/cpp/cpp_concurrent_finder.rb +2 -0
  31. data/lib/contrek/cpp/cpp_concurrent_horizontal_merger.rb +2 -0
  32. data/lib/contrek/cpp/cpp_concurrent_merger.rb +2 -0
  33. data/lib/contrek/cpp/cpp_concurrent_streaming_merger.rb +11 -0
  34. data/lib/contrek/cpp/cpp_concurrent_vertical_merger.rb +2 -0
  35. data/lib/contrek/cpp/cpp_result.rb +2 -0
  36. data/lib/contrek/cpp/cpp_tempfile.rb +28 -0
  37. data/lib/contrek/finder/bounds.rb +2 -0
  38. data/lib/contrek/finder/concurrent/clipped_polygon_finder.rb +2 -0
  39. data/lib/contrek/finder/concurrent/cluster.rb +2 -0
  40. data/lib/contrek/finder/concurrent/cursor.rb +2 -0
  41. data/lib/contrek/finder/concurrent/end_point.rb +2 -0
  42. data/lib/contrek/finder/concurrent/fake_cluster.rb +2 -0
  43. data/lib/contrek/finder/concurrent/finder.rb +2 -0
  44. data/lib/contrek/finder/concurrent/horizontal_merger.rb +2 -0
  45. data/lib/contrek/finder/concurrent/hub.rb +2 -0
  46. data/lib/contrek/finder/concurrent/inner_polyline.rb +2 -0
  47. data/lib/contrek/finder/concurrent/listable.rb +2 -0
  48. data/lib/contrek/finder/concurrent/merger.rb +3 -0
  49. data/lib/contrek/finder/concurrent/part.rb +2 -0
  50. data/lib/contrek/finder/concurrent/partitionable.rb +2 -0
  51. data/lib/contrek/finder/concurrent/polyline.rb +2 -0
  52. data/lib/contrek/finder/concurrent/poolable.rb +2 -0
  53. data/lib/contrek/finder/concurrent/position.rb +2 -0
  54. data/lib/contrek/finder/concurrent/queueable.rb +2 -0
  55. data/lib/contrek/finder/concurrent/sequence.rb +2 -0
  56. data/lib/contrek/finder/concurrent/shape.rb +2 -0
  57. data/lib/contrek/finder/concurrent/streaming_merger.rb +89 -0
  58. data/lib/contrek/finder/concurrent/tile.rb +2 -0
  59. data/lib/contrek/finder/concurrent/vertical_merger.rb +4 -2
  60. data/lib/contrek/finder/list.rb +2 -0
  61. data/lib/contrek/finder/list_entry.rb +2 -0
  62. data/lib/contrek/finder/listable.rb +2 -0
  63. data/lib/contrek/finder/lists.rb +2 -0
  64. data/lib/contrek/finder/node.rb +2 -0
  65. data/lib/contrek/finder/node_cluster.rb +3 -1
  66. data/lib/contrek/finder/polygon_finder.rb +2 -0
  67. data/lib/contrek/finder/result.rb +2 -0
  68. data/lib/contrek/map/mercator_projection.rb +2 -0
  69. data/lib/contrek/matchers/matcher.rb +2 -0
  70. data/lib/contrek/matchers/matcher_hsb.rb +2 -0
  71. data/lib/contrek/matchers/value_not_matcher.rb +2 -0
  72. data/lib/contrek/reducers/linear_reducer.rb +2 -0
  73. data/lib/contrek/reducers/reducer.rb +2 -0
  74. data/lib/contrek/reducers/uniq_reducer.rb +2 -0
  75. data/lib/contrek/reducers/visvalingam_reducer.rb +2 -0
  76. data/lib/contrek/shared/result.rb +2 -0
  77. data/lib/contrek/version.rb +3 -1
  78. data/lib/contrek.rb +5 -0
  79. 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: 8f506a5908c11f7ac1de118b4772ad9e85cd00746e4ee57b5d9c1600faea371e
4
+ data.tar.gz: 81c2807712361d51398962c9496bf3c833b21eb42d34907069c923cd62654f62
5
5
  SHA512:
6
- metadata.gz: f498831ee81c1ff533ce412b39efdfe9c9dc42ff40679749600a2e2cf833cd8193b1f3ca8bf00e8ff98ae3a3798f9670380274d3cf63e485bad4428200256af4
7
- data.tar.gz: 4fd128cf5cd14d929f2baaf9c611708cd9bd8ce01550e267f1d93252f5f23a9012b6f66bb59a4ab4390cb02c38b31a37f3fc6fd56d22478cf046359834a3bf0d
6
+ metadata.gz: 76c41016ce95c124ed72beacd5c838bf5fe960483e3cf7b78848d53c3d997948f5de0c8ecacdb08d3c38b8faf7349ec79cef9546415773e5d5201019b04fe3b2
7
+ data.tar.gz: 1823196730b740221b2e75be9915706b5b96afd91349dd898eda70d1ccce69f569270e134663137523ea3860e5d49a419b59a083a6a11be027bf4e14a1a0e30b
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.2.9)
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"
@@ -385,6 +386,112 @@ void stream_png_image(const std::string& filepath, uint32_t stripe_height, bool
385
386
  }
386
387
 
387
388
  void Tests::test_i() {
388
- stream_png_image("../images/graphs_1024x1024.png", 300);
389
+ stream_png_image("../images/graphs_1024x1024.png", 300, true);
390
+ std::cout << "Memory usage peak: " << get_peak_rss() << " MB" << std::endl;
391
+ }
392
+
393
+ void stream_progressive_png_image(const std::string& filepath, uint32_t stripe_height) {
394
+ std::vector<ProcessResult*> result_clones;
395
+ std::vector<std::string> varguments = {"--bounds"};
396
+ // opens image to stream
397
+ FILE* fp = fopen(filepath.c_str(), "rb");
398
+ if (!fp) {
399
+ std::cerr << "Unable open file: " << filepath << std::endl;
400
+ return;
401
+ }
402
+
403
+ // exams image
404
+ spng_ctx *ctx = spng_ctx_new(0);
405
+ spng_set_png_file(ctx, fp);
406
+ struct spng_ihdr ihdr;
407
+ if (spng_get_ihdr(ctx, &ihdr)) {
408
+ fclose(fp);
409
+ spng_ctx_free(ctx);
410
+ return;
411
+ }
412
+ uint32_t total_width = ihdr.width;
413
+ uint32_t total_height = ihdr.height;
414
+
415
+ // allocates stripe buffer
416
+ RawBitmap stripe_bitmap;
417
+ stripe_bitmap.define(total_width, stripe_height, 4, true);
418
+ RGBNotMatcher not_matcher(-1);
419
+ if (spng_decode_image(ctx, NULL, 0, SPNG_FMT_RGBA8, SPNG_DECODE_PROGRESSIVE)) {
420
+ fclose(fp);
421
+ spng_ctx_free(ctx);
422
+ return;
423
+ }
424
+
425
+ // allocates streaming svg buffer
426
+ std::string output_path = "streaming_buffer.svg";
427
+ std::ofstream shared_stream(output_path, std::ios::out | std::ios::binary);
428
+ if (!shared_stream) {
429
+ std::cerr << "Error: Unable creating output streaming file!" << std::endl;
430
+ }
431
+
432
+ StreamingMerger vmerger(0, &varguments, &shared_stream, total_width, total_height);
433
+ try {
434
+ size_t row_size = static_cast<size_t>(total_width) * 4;
435
+ int stripe_count = 0;
436
+ // main stripes loop
437
+ for (uint32_t current_y_offset = 0; current_y_offset < total_height; current_y_offset += stripe_height) {
438
+ int uncovered_height = total_height - current_y_offset;
439
+
440
+ // copy previous last line to the next new one (each contigue stripe must share one pixel scanline)
441
+ if (current_y_offset > 0) {
442
+ unsigned char* last_row_prev = const_cast<unsigned char*>(stripe_bitmap.get_row_ptr(stripe_height - 1));
443
+ unsigned char* first_row_curr = const_cast<unsigned char*>(stripe_bitmap.get_row_ptr(0));
444
+ std::memcpy(first_row_curr, last_row_prev, row_size);
445
+ }
446
+ // clears the part of the stripe wont be overwritten by png data
447
+ if (uncovered_height < stripe_height)
448
+ { stripe_bitmap.draw_filled_rectangle(0, 1, total_width, stripe_height - 1, 255, 255, 255);
449
+ }
450
+ // decoding data directly in the stripe buffer
451
+ uint32_t lines_to_read = std::min(stripe_height, total_height - current_y_offset);
452
+ for (uint32_t y = (current_y_offset == 0 ? 0 : 1); y < lines_to_read; y++) {
453
+ unsigned char* row_ptr = const_cast<unsigned char*>(stripe_bitmap.get_row_ptr(y));
454
+ int ret = spng_decode_row(ctx, row_ptr, row_size);
455
+ if (ret != 0 && ret != SPNG_EOI) break;
456
+ }
457
+ // stripe contour tracing
458
+ std::vector<std::string> finder_arguments = {
459
+ "--versus=a",
460
+ "--bounds",
461
+ "--strict_bounds", // <- this option is strictly needed when working with vertical merger
462
+ "--compress_uniq",
463
+ "--compress_linear"
464
+ };
465
+
466
+ PolygonFinder polygon_finder(&stripe_bitmap, &not_matcher, nullptr, &finder_arguments);
467
+ ProcessResult *result = polygon_finder.process_info();
468
+ if (result) {
469
+ std::cout << "stripe " << stripe_count << ": found polygons " << result->groups << std::endl;
470
+ ProcessResult* safe_result = result->clone();
471
+ result_clones.push_back(safe_result);
472
+ vmerger.add_tile(*safe_result, !(current_y_offset + stripe_height < total_height));
473
+ delete result;
474
+ }
475
+ stripe_count++;
476
+ }
477
+
478
+ ProcessResult *merged_result = vmerger.process_info();
479
+ std::cout << "total found polygons " << merged_result->groups << std::endl;
480
+ delete merged_result;
481
+
482
+ // frees memory
483
+ for (auto c : result_clones) {
484
+ delete c;
485
+ }
486
+ } catch (const std::exception& e) {
487
+ std::cerr << "\n[ERROR] Processing exception: " << e.what() << std::endl;
488
+ if (shared_stream.is_open()) shared_stream.close();
489
+ }
490
+ spng_ctx_free(ctx);
491
+ fclose(fp);
492
+ }
493
+
494
+ void Tests::test_l() {
495
+ stream_progressive_png_image("../images/mixed_shapes_1024x1024.png", 300);
389
496
  std::cout << "Memory usage peak: " << get_peak_rss() << " MB" << std::endl;
390
497
  }
@@ -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
  };
@@ -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 ===//
@@ -207,330 +207,103 @@ class PolygonFinder {
207
207
  std::map<std::string, double> reports;
208
208
  void scan();
209
209
  CpuTimer cpu_timer;
210
- /*
210
+
211
211
  template <typename M, typename F>
212
212
  void run_loop(M* specific_matcher, F&& fetch_color, int offset) {
213
213
  int img_h = this->source_bitmap->h();
214
214
  int bpp = this->source_bitmap->get_bytes_per_pixel();
215
+
215
216
  for (int y = 0; y < img_h; y++) {
216
217
  const unsigned char* row_ptr = this->source_bitmap->get_row_ptr(y);
217
218
  const unsigned char* p = row_ptr + (this->start_x * bpp);
219
+
218
220
  int min_x = 0;
219
221
  bool matching = false;
220
222
  unsigned char last_red_value = 0;
221
- for (int x = this->start_x; x < this->end_x; x++) {
222
- unsigned int color = fetch_color(p);
223
- unsigned char current_val = p[0];
224
- p += bpp;
225
- if (specific_matcher->match(color)) {
226
- if (!matching) {
227
- min_x = x;
228
- last_red_value = current_val;
229
- matching = true;
230
- }
231
- if (x == this->end_x - 1) {
232
- this->node_cluster->add_node(min_x, x, y, last_red_value, offset);
233
- matching = false;
234
- }
235
- } else if (matching) {
236
- this->node_cluster->add_node(min_x, x - 1, y, last_red_value, offset);
237
- matching = false;
238
- }
239
- }
240
- }
241
- }*/
242
223
 
243
- template <typename M, typename F>
244
- void run_loop(M* specific_matcher, F&& fetch_color, int offset) {
245
- int img_h = this->source_bitmap->h();
246
- int bpp = this->source_bitmap->get_bytes_per_pixel();
224
+ int x = this->start_x;
247
225
 
248
- for (int y = 0; y < img_h; y++) {
249
- const unsigned char* row_ptr = this->source_bitmap->get_row_ptr(y);
250
- const unsigned char* p = row_ptr + (this->start_x * bpp);
251
-
252
- int min_x = 0;
253
- bool matching = false;
254
- unsigned char last_red_value = 0;
255
-
256
- int x = this->start_x;
257
-
258
- if (bpp == 4) {
259
- for (; x <= this->end_x - 4; x += 4) {
260
- // 1. Unico accesso alla RAM per pixel: leggiamo i 4 byte completi
261
- unsigned int c0 = fetch_color(p);
262
- unsigned int c1 = fetch_color(p + 4);
263
- unsigned int c2 = fetch_color(p + 8);
264
- unsigned int c3 = fetch_color(p + 12);
265
-
266
- // 2. Estrazione a costo zero dai registri della CPU (Operazione bitwise, NO RAM)
267
- // Se fetch_color ritorna il mascheramento standard, il primo byte si prende con il cast o lo shift
268
- unsigned char v0 = static_cast<unsigned char>(c0);
269
- unsigned char v1 = static_cast<unsigned char>(c1);
270
- unsigned char v2 = static_cast<unsigned char>(c2);
271
- unsigned char v3 = static_cast<unsigned char>(c3);
272
-
273
- p += 16;
274
-
275
- bool m0 = specific_matcher->match(c0);
276
- bool m1 = specific_matcher->match(c1);
277
- bool m2 = specific_matcher->match(c2);
278
- bool m3 = specific_matcher->match(c3);
279
-
280
- // [Resto della logica dei pixel 0, 1, 2, 3 invariata...]
281
- if (m0) {
282
- if (!matching) { min_x = x; last_red_value = v0; matching = true; }
283
- } else if (matching) {
284
- this->node_cluster->add_node(min_x, x - 1, y, last_red_value, offset);
285
- matching = false;
286
- }
287
-
288
- if (m1) {
289
- if (!matching) { min_x = x + 1; last_red_value = v1; matching = true; }
290
- } else if (matching) {
291
- this->node_cluster->add_node(min_x, x, y, last_red_value, offset);
292
- matching = false;
293
- }
294
-
295
- if (m2) {
296
- if (!matching) { min_x = x + 2; last_red_value = v2; matching = true; }
297
- } else if (matching) {
298
- this->node_cluster->add_node(min_x, x + 1, y, last_red_value, offset);
299
- matching = false;
300
- }
301
-
302
- if (m3) {
303
- if (!matching) { min_x = x + 3; last_red_value = v3; matching = true; }
304
- } else if (matching) {
305
- this->node_cluster->add_node(min_x, x + 2, y, last_red_value, offset);
306
- matching = false;
307
- }
308
- }
309
- }
226
+ if (bpp == 4) {
227
+ for (; x <= this->end_x - 4; x += 4) {
228
+ // read 4 pixels (16 bytes)
229
+ unsigned int c0 = fetch_color(p);
230
+ unsigned int c1 = fetch_color(p + 4);
231
+ unsigned int c2 = fetch_color(p + 8);
232
+ unsigned int c3 = fetch_color(p + 12);
310
233
 
311
- // --- CLEANUP SCALARE ---
312
- for (; x < this->end_x; x++) {
313
- unsigned int color = fetch_color(p);
314
- // Applichiamo la stessa ottimizzazione anche qui
315
- unsigned char current_val = static_cast<unsigned char>(color);
316
- p += bpp;
317
-
318
- if (specific_matcher->match(color)) {
319
- if (!matching) {
320
- min_x = x;
321
- last_red_value = current_val;
322
- matching = true;
323
- }
324
- } else if (matching) {
325
- this->node_cluster->add_node(min_x, x - 1, y, last_red_value, offset);
326
- matching = false;
327
- }
328
- }
234
+ // extracts value (used as debugging segment label)
235
+ unsigned char v0 = static_cast<unsigned char>(c0);
236
+ unsigned char v1 = static_cast<unsigned char>(c1);
237
+ unsigned char v2 = static_cast<unsigned char>(c2);
238
+ unsigned char v3 = static_cast<unsigned char>(c3);
329
239
 
330
- if (matching) {
331
- this->node_cluster->add_node(min_x, this->end_x - 1, y, last_red_value, offset);
332
- }
333
- }
334
- }
240
+ p += 16;
335
241
 
336
- /*template <typename M, typename F>
337
- void run_loop(M* specific_matcher, F&& fetch_color, int offset) {
338
- int img_h = this->source_bitmap->h();
339
- int bpp = this->source_bitmap->get_bytes_per_pixel();
242
+ bool m0 = specific_matcher->match(c0);
243
+ bool m1 = specific_matcher->match(c1);
244
+ bool m2 = specific_matcher->match(c2);
245
+ bool m3 = specific_matcher->match(c3);
340
246
 
341
- for (int y = 0; y < img_h; y++) {
342
- const unsigned char* row_ptr = this->source_bitmap->get_row_ptr(y);
343
- const unsigned char* p = row_ptr + (this->start_x * bpp);
344
-
345
- int min_x = 0;
346
- bool matching = false;
347
- unsigned char last_red_value = 0;
348
-
349
- int x = this->start_x;
350
-
351
- // --- IL TUO SWEET SPOT: UNROLLING A 4 VIE RIFINITO ---
352
- if (bpp == 4) {
353
- for (; x <= this->end_x - 4; x += 4) {
354
- // Leggiamo in sequenza stretta per ottimizzare i register e la cache L1
355
- unsigned int c0 = fetch_color(p);
356
- unsigned char v0 = p[0];
357
-
358
- unsigned int c1 = fetch_color(p + 4);
359
- unsigned char v1 = p[4];
360
-
361
- unsigned int c2 = fetch_color(p + 8);
362
- unsigned char v2 = p[8];
363
-
364
- unsigned int c3 = fetch_color(p + 12);
365
- unsigned char v3 = p[12];
366
-
367
- p += 16; // Spostato qui: avanza di 4 pixel RGBA precisi
368
-
369
- bool m0 = specific_matcher->match(c0);
370
- bool m1 = specific_matcher->match(c1);
371
- bool m2 = specific_matcher->match(c2);
372
- bool m3 = specific_matcher->match(c3);
373
-
374
- // Pixel 0
375
- if (m0) {
376
- if (!matching) { min_x = x; last_red_value = v0; matching = true; }
377
- } else if (matching) {
378
- this->node_cluster->add_node(min_x, x - 1, y, last_red_value, offset);
379
- matching = false;
380
- }
381
-
382
- // Pixel 1
383
- if (m1) {
384
- if (!matching) { min_x = x + 1; last_red_value = v1; matching = true; }
385
- } else if (matching) {
386
- this->node_cluster->add_node(min_x, x, y, last_red_value, offset);
387
- matching = false;
388
- }
389
-
390
- // Pixel 2
391
- if (m2) {
392
- if (!matching) { min_x = x + 2; last_red_value = v2; matching = true; }
393
- } else if (matching) {
394
- this->node_cluster->add_node(min_x, x + 1, y, last_red_value, offset);
395
- matching = false;
396
- }
397
-
398
- // Pixel 3
399
- if (m3) {
400
- if (!matching) { min_x = x + 3; last_red_value = v3; matching = true; }
401
- } else if (matching) {
402
- this->node_cluster->add_node(min_x, x + 2, y, last_red_value, offset);
403
- matching = false;
404
- }
247
+ if (m0) {
248
+ if (!matching) {
249
+ min_x = x; last_red_value = v0; matching = true;
405
250
  }
406
- }
251
+ } else if (matching) {
252
+ this->node_cluster->add_node(min_x, x - 1, y, last_red_value, offset);
253
+ matching = false;
254
+ }
407
255
 
408
- // --- CLEANUP SCALARE PER I RIMANENTI ---
409
- for (; x < this->end_x; x++) {
410
- unsigned int color = fetch_color(p);
411
- unsigned char current_val = p[0];
412
- p += bpp;
413
-
414
- if (specific_matcher->match(color)) {
415
- if (!matching) {
416
- min_x = x;
417
- last_red_value = current_val;
418
- matching = true;
419
- }
420
- } else if (matching) {
421
- this->node_cluster->add_node(min_x, x - 1, y, last_red_value, offset);
422
- matching = false;
256
+ if (m1) {
257
+ if (!matching) {
258
+ min_x = x + 1; last_red_value = v1; matching = true;
423
259
  }
424
- }
425
-
426
- // --- CHIUSURA DI SICUREZZA FINE RIGA ---
427
- if (matching) {
428
- this->node_cluster->add_node(min_x, this->end_x - 1, y, last_red_value, offset);
429
- }
430
- }
431
- }*/
432
-
433
-
434
-
435
- /*
436
-
437
- #include <cstdint>
438
-
439
- template <typename M, typename F>
440
- void run_loop(M* specific_matcher, F&& fetch_color, int offset) {
441
- int img_h = this->source_bitmap->h();
442
- int bpp = this->source_bitmap->get_bytes_per_pixel();
260
+ } else if (matching) {
261
+ this->node_cluster->add_node(min_x, x, y, last_red_value, offset);
262
+ matching = false;
263
+ }
443
264
 
444
- for (int y = 0; y < img_h; y++) {
445
- const unsigned char* row_ptr = this->source_bitmap->get_row_ptr(y);
446
- const unsigned char* p = row_ptr + (this->start_x * bpp);
447
-
448
- int min_x = 0;
449
- bool matching = false;
450
- unsigned char last_red_value = 0;
451
-
452
- int x = this->start_x;
453
-
454
- // --- LINEA 1: UNROLLING A 4 VIE (SIMD FRIENDLY, NO DEPENDENCY) ---
455
- if (bpp == 4) {
456
- for (; x <= this->end_x - 4; x += 4) {
457
- // Carichiamo 4 pixel in variabili separate (zero dipendenze tra loro)
458
- unsigned int c0 = fetch_color(p);
459
- unsigned int c1 = fetch_color(p + 4);
460
- unsigned int c2 = fetch_color(p + 8);
461
- unsigned int c3 = fetch_color(p + 12);
462
-
463
- unsigned char v0 = p[0];
464
- unsigned char v1 = p[4];
465
- unsigned char v2 = p[8];
466
- unsigned char v3 = p[12];
467
-
468
- p += 16; // Avanza di 4 pixel (4 * 4 byte)
469
-
470
- // Eseguiamo il match in parallelo. Il compilatore trasforma questo in istruzioni vettoriali
471
- bool m0 = specific_matcher->match(c0);
472
- bool m1 = specific_matcher->match(c1);
473
- bool m2 = specific_matcher->match(c2);
474
- bool m3 = specific_matcher->match(c3);
475
-
476
- // Pixel 0
477
- if (m0) {
478
- if (!matching) { min_x = x; last_red_value = v0; matching = true; }
479
- } else if (matching) {
480
- this->node_cluster->add_node(min_x, x - 1, y, last_red_value, offset);
481
- matching = false;
482
- }
483
-
484
- // Pixel 1
485
- if (m1) {
486
- if (!matching) { min_x = x + 1; last_red_value = v1; matching = true; }
487
- } else if (matching) {
488
- this->node_cluster->add_node(min_x, x, y, last_red_value, offset);
489
- matching = false;
490
- }
491
-
492
- // Pixel 2
493
- if (m2) {
494
- if (!matching) { min_x = x + 2; last_red_value = v2; matching = true; }
495
- } else if (matching) {
496
- this->node_cluster->add_node(min_x, x + 1, y, last_red_value, offset);
497
- matching = false;
498
- }
499
-
500
- // Pixel 3
501
- if (m3) {
502
- if (!matching) { min_x = x + 3; last_red_value = v3; matching = true; }
503
- } else if (matching) {
504
- this->node_cluster->add_node(min_x, x + 2, y, last_red_value, offset);
505
- matching = false;
506
- }
265
+ if (m2) {
266
+ if (!matching) {
267
+ min_x = x + 2; last_red_value = v2; matching = true;
507
268
  }
508
- }
269
+ } else if (matching) {
270
+ this->node_cluster->add_node(min_x, x + 1, y, last_red_value, offset);
271
+ matching = false;
272
+ }
509
273
 
510
- // --- LINEA 2: SCALAR CLEANUP ---
511
- for (; x < this->end_x; x++) {
512
- unsigned int color = fetch_color(p);
513
- unsigned char current_val = p[0];
514
- p += bpp;
515
-
516
- if (specific_matcher->match(color)) {
517
- if (!matching) {
518
- min_x = x;
519
- last_red_value = current_val;
520
- matching = true;
521
- }
522
- } else if (matching) {
523
- this->node_cluster->add_node(min_x, x - 1, y, last_red_value, offset);
524
- matching = false;
274
+ if (m3) {
275
+ if (!matching) {
276
+ min_x = x + 3; last_red_value = v3; matching = true;
525
277
  }
278
+ } else if (matching) {
279
+ this->node_cluster->add_node(min_x, x + 2, y, last_red_value, offset);
280
+ matching = false;
281
+ }
526
282
  }
283
+ }
527
284
 
528
- // --- CHIUSURA DI SICUREZZA ---
529
- if (matching) {
530
- this->node_cluster->add_node(min_x, this->end_x - 1, y, last_red_value, offset);
285
+ // remaining pixels (width not a multiple of 4)
286
+ for (; x < this->end_x; x++) {
287
+ unsigned int color = fetch_color(p);
288
+ unsigned char current_val = static_cast<unsigned char>(color);
289
+ p += bpp;
290
+ if (specific_matcher->match(color)) {
291
+ if (!matching) {
292
+ min_x = x;
293
+ last_red_value = current_val;
294
+ matching = true;
295
+ }
296
+ } else if (matching) {
297
+ this->node_cluster->add_node(min_x, x - 1, y, last_red_value, offset);
298
+ matching = false;
531
299
  }
300
+ }
301
+
302
+ if (matching) {
303
+ this->node_cluster->add_node(min_x, this->end_x - 1, y, last_red_value, offset);
304
+ }
532
305
  }
533
- }*/
306
+ }
534
307
 
535
308
  public:
536
309
  PolygonFinder(Bitmap *bitmap,
@@ -35,7 +35,6 @@ class Finder : public Poolable {
35
35
  Matcher *matcher;
36
36
  pf_Options options_;
37
37
  std::vector<std::string> input_options;
38
- Tile* whole_tile = nullptr;
39
38
  std::queue<ClippedPolygonFinder*> finders;
40
39
  std::mutex finders_mutex;
41
40
  std::map<std::string, double> reports;
@@ -45,6 +44,7 @@ class Finder : public Poolable {
45
44
  Queue<Tile*> tiles_;
46
45
  int maximum_width_;
47
46
  int height = 0;
47
+ Tile* whole_tile = nullptr;
48
48
  void process_tiles();
49
49
 
50
50
  public: