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.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.rubocop.yml +11 -0
- data/CHANGELOG.md +7 -1
- data/Gemfile +2 -0
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/Rakefile +2 -0
- data/contrek.gemspec +2 -0
- data/ext/cpp_polygon_finder/PolygonFinder/CMakeLists.txt +2 -4
- data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +108 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.h +1 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +0 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.h +70 -297
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.h +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp +6 -6
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.h +3 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/StreamingMerger.cpp +114 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/StreamingMerger.h +41 -0
- data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +52 -2
- data/ext/cpp_polygon_finder/extconf.rb +2 -0
- data/lib/contrek/bitmaps/bitmap.rb +2 -0
- data/lib/contrek/bitmaps/chunky_bitmap.rb +2 -0
- data/lib/contrek/bitmaps/painting.rb +2 -0
- data/lib/contrek/bitmaps/png_bitmap.rb +2 -0
- data/lib/contrek/bitmaps/raw_bitmap.rb +2 -0
- data/lib/contrek/bitmaps/rgb_color.rb +2 -0
- data/lib/contrek/bitmaps/rgb_cpp_color.rb +2 -0
- data/lib/contrek/bitmaps/sample_generator.rb +2 -0
- data/lib/contrek/cpp/cpp_concurrent_finder.rb +2 -0
- data/lib/contrek/cpp/cpp_concurrent_horizontal_merger.rb +2 -0
- data/lib/contrek/cpp/cpp_concurrent_merger.rb +2 -0
- data/lib/contrek/cpp/cpp_concurrent_streaming_merger.rb +11 -0
- data/lib/contrek/cpp/cpp_concurrent_vertical_merger.rb +2 -0
- data/lib/contrek/cpp/cpp_result.rb +2 -0
- data/lib/contrek/cpp/cpp_tempfile.rb +28 -0
- data/lib/contrek/finder/bounds.rb +2 -0
- data/lib/contrek/finder/concurrent/clipped_polygon_finder.rb +2 -0
- data/lib/contrek/finder/concurrent/cluster.rb +2 -0
- data/lib/contrek/finder/concurrent/cursor.rb +2 -0
- data/lib/contrek/finder/concurrent/end_point.rb +2 -0
- data/lib/contrek/finder/concurrent/fake_cluster.rb +2 -0
- data/lib/contrek/finder/concurrent/finder.rb +2 -0
- data/lib/contrek/finder/concurrent/horizontal_merger.rb +2 -0
- data/lib/contrek/finder/concurrent/hub.rb +2 -0
- data/lib/contrek/finder/concurrent/inner_polyline.rb +2 -0
- data/lib/contrek/finder/concurrent/listable.rb +2 -0
- data/lib/contrek/finder/concurrent/merger.rb +3 -0
- data/lib/contrek/finder/concurrent/part.rb +2 -0
- data/lib/contrek/finder/concurrent/partitionable.rb +2 -0
- data/lib/contrek/finder/concurrent/polyline.rb +2 -0
- data/lib/contrek/finder/concurrent/poolable.rb +2 -0
- data/lib/contrek/finder/concurrent/position.rb +2 -0
- data/lib/contrek/finder/concurrent/queueable.rb +2 -0
- data/lib/contrek/finder/concurrent/sequence.rb +2 -0
- data/lib/contrek/finder/concurrent/shape.rb +2 -0
- data/lib/contrek/finder/concurrent/streaming_merger.rb +89 -0
- data/lib/contrek/finder/concurrent/tile.rb +2 -0
- data/lib/contrek/finder/concurrent/vertical_merger.rb +4 -2
- data/lib/contrek/finder/list.rb +2 -0
- data/lib/contrek/finder/list_entry.rb +2 -0
- data/lib/contrek/finder/listable.rb +2 -0
- data/lib/contrek/finder/lists.rb +2 -0
- data/lib/contrek/finder/node.rb +2 -0
- data/lib/contrek/finder/node_cluster.rb +3 -1
- data/lib/contrek/finder/polygon_finder.rb +2 -0
- data/lib/contrek/finder/result.rb +2 -0
- data/lib/contrek/map/mercator_projection.rb +2 -0
- data/lib/contrek/matchers/matcher.rb +2 -0
- data/lib/contrek/matchers/matcher_hsb.rb +2 -0
- data/lib/contrek/matchers/value_not_matcher.rb +2 -0
- data/lib/contrek/reducers/linear_reducer.rb +2 -0
- data/lib/contrek/reducers/reducer.rb +2 -0
- data/lib/contrek/reducers/uniq_reducer.rb +2 -0
- data/lib/contrek/reducers/visvalingam_reducer.rb +2 -0
- data/lib/contrek/shared/result.rb +2 -0
- data/lib/contrek/version.rb +3 -1
- data/lib/contrek.rb +5 -0
- metadata +11 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8f506a5908c11f7ac1de118b4772ad9e85cd00746e4ee57b5d9c1600faea371e
|
|
4
|
+
data.tar.gz: 81c2807712361d51398962c9496bf3c833b21eb42d34907069c923cd62654f62
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
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
data/Gemfile.lock
CHANGED
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
data/contrek.gemspec
CHANGED
|
@@ -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
|
-
|
|
15
|
-
|
|
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, ¬_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
|
}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
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
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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
|
-
|
|
427
|
-
|
|
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
|
-
|
|
445
|
-
|
|
446
|
-
|
|
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
|
-
|
|
511
|
-
|
|
512
|
-
|
|
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
|
-
|
|
529
|
-
|
|
530
|
-
|
|
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:
|