contrek 1.4.0 → 1.4.2
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/CHANGELOG.md +7 -1
- data/Gemfile.lock +4 -1
- data/PERFORMANCE.md +40 -4
- data/README.md +211 -59
- data/contrek.gemspec +1 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/ContrekApi.h +37 -11
- data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +53 -144
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/RawBitmap.cpp +14 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/RawBitmap.h +1 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/AsciiSource.cpp +69 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/AsciiSource.h +34 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/PngSource.cpp +108 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/PngSource.h +40 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/RasterSource.h +22 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/RasterStreamer.h +92 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/FinderUtils.cpp +6 -8
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +8 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.h +4 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp +7 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Poolable.h +1 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.cpp +3 -3
- data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +47 -0
- data/lib/contrek/bitmaps/bitmap.rb +2 -2
- data/lib/contrek/bitmaps/chunky_bitmap.rb +23 -8
- data/lib/contrek/bitmaps/png_bitmap.rb +4 -0
- data/lib/contrek/bitmaps/rgba_bitmap.rb +50 -0
- data/lib/contrek/bitmaps/streaming/raster_streamer.rb +66 -0
- data/lib/contrek/bitmaps/streaming/sources/ascii.rb +33 -0
- data/lib/contrek/bitmaps/streaming/sources/base.rb +27 -0
- data/lib/contrek/bitmaps/streaming/sources/png.rb +105 -0
- data/lib/contrek/finder/concurrent/finder.rb +5 -3
- data/lib/contrek/finder/polygon_finder.rb +8 -4
- data/lib/contrek/version.rb +1 -1
- data/lib/contrek.rb +5 -0
- metadata +26 -1
|
@@ -17,18 +17,17 @@ void FinderUtils::sanitize_options(pf_Options& options, const Options& incoming_
|
|
|
17
17
|
const Identifier versus = incoming_options.get<Identifier>("versus", options.versus == Node::A ? "a" : "o");
|
|
18
18
|
options.versus = versus == "a" ? Node::A : Node::O;
|
|
19
19
|
options.number_of_tiles = std::max(1, static_cast<int>(incoming_options.get<std::int64_t>(
|
|
20
|
-
"number_of_tiles",
|
|
21
|
-
options.number_of_tiles)));
|
|
20
|
+
"number_of_tiles", options.number_of_tiles)));
|
|
22
21
|
options.connectivity_offset = incoming_options.get<std::int64_t>(
|
|
23
|
-
"connectivity",
|
|
24
|
-
options.connectivity_offset == 1 ? 8 : 4) == 8;
|
|
22
|
+
"connectivity", options.connectivity_offset == 1 ? 8 : 4) == 8;
|
|
25
23
|
options.treemap = incoming_options.get<bool>("treemap", options.treemap);
|
|
26
24
|
options.named_sequences = incoming_options.get<bool>(
|
|
27
|
-
"named_sequences",
|
|
28
|
-
options.named_sequences);
|
|
25
|
+
"named_sequences", options.named_sequences);
|
|
29
26
|
options.bounds = incoming_options.get<bool>("bounds", options.bounds);
|
|
30
27
|
options.unsafe_mode = incoming_options.get<bool>("unsafe_mode", options.unsafe_mode);
|
|
31
28
|
options.deterministic = incoming_options.get<bool>("deterministic", options.deterministic);
|
|
29
|
+
options.processing_height = static_cast<int>(incoming_options.get<std::int64_t>(
|
|
30
|
+
"processing_height", options.processing_height));
|
|
32
31
|
|
|
33
32
|
if (const Options* compress = incoming_options.get_options("compress")) {
|
|
34
33
|
options.compress_uniq = compress->get<bool>("uniq", options.compress_uniq);
|
|
@@ -39,8 +38,7 @@ void FinderUtils::sanitize_options(pf_Options& options, const Options& incoming_
|
|
|
39
38
|
if (compress->contains("visvalingam_tolerance")) {
|
|
40
39
|
options.compress_visvalingam = true;
|
|
41
40
|
options.compress_visvalingam_tolerance = static_cast<float>(compress->get<double>(
|
|
42
|
-
"visvalingam_tolerance",
|
|
43
|
-
options.compress_visvalingam_tolerance));
|
|
41
|
+
"visvalingam_tolerance", options.compress_visvalingam_tolerance));
|
|
44
42
|
}
|
|
45
43
|
}
|
|
46
44
|
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
#include <list>
|
|
12
12
|
#include <map>
|
|
13
13
|
#include <ctime>
|
|
14
|
+
#include <cmath>
|
|
14
15
|
#include <typeinfo>
|
|
15
16
|
#include <string>
|
|
16
17
|
#include <vector>
|
|
@@ -40,7 +41,11 @@ PolygonFinder::PolygonFinder(Bitmap *bitmap,
|
|
|
40
41
|
{ this->rgb_matcher = dynamic_cast<RGBMatcher*>(matcher);
|
|
41
42
|
FinderUtils::sanitize_options(this->options, options);
|
|
42
43
|
|
|
43
|
-
this->
|
|
44
|
+
this->processing_height = std::abs(this->options.processing_height == 0 ? source_bitmap->h() : this->options.processing_height);
|
|
45
|
+
if (this->processing_height > source_bitmap->h()) {
|
|
46
|
+
throw std::invalid_argument("Option processing_height must be less or equal than image height!");
|
|
47
|
+
}
|
|
48
|
+
this->node_cluster = new NodeCluster(this->processing_height, source_bitmap->w(), &this->options);
|
|
44
49
|
|
|
45
50
|
//= SCAN ==============//
|
|
46
51
|
cpu_timer.start();
|
|
@@ -106,10 +111,11 @@ ProcessResult* PolygonFinder::process_info() {
|
|
|
106
111
|
pr->benchmarks = std::move(this->reports);
|
|
107
112
|
pr->treemap = this->node_cluster->treemap;
|
|
108
113
|
pr->width = this->source_bitmap->w();
|
|
109
|
-
pr->height = this->
|
|
114
|
+
pr->height = this->processing_height;
|
|
110
115
|
pr->has_bounds = this->node_cluster->options->bounds;
|
|
111
116
|
pr->versus = this->options.versus;
|
|
112
117
|
pr->options = this->incoming_options_;
|
|
118
|
+
pr->number_of_threads = 0;
|
|
113
119
|
|
|
114
120
|
if (this->node_cluster->options->named_sequences && typeid(*this->source_bitmap) == typeid(Bitmap))
|
|
115
121
|
{ std::string sequence;
|
|
@@ -54,6 +54,7 @@ struct pf_Options {
|
|
|
54
54
|
bool unsafe_mode = false;
|
|
55
55
|
bool bounds = false;
|
|
56
56
|
bool deterministic = false;
|
|
57
|
+
int processing_height = 0;
|
|
57
58
|
int connectivity_offset = 0;
|
|
58
59
|
float compress_visvalingam_tolerance = 10.0;
|
|
59
60
|
int number_of_tiles = 1;
|
|
@@ -69,6 +70,7 @@ struct ProcessResult {
|
|
|
69
70
|
int groups;
|
|
70
71
|
int width, height;
|
|
71
72
|
int versus;
|
|
73
|
+
int number_of_threads;
|
|
72
74
|
bool has_bounds = false;
|
|
73
75
|
std::map<std::string, double> benchmarks;
|
|
74
76
|
std::list<Polygon> polygons;
|
|
@@ -158,6 +160,7 @@ class PolygonFinder {
|
|
|
158
160
|
protected:
|
|
159
161
|
int start_x;
|
|
160
162
|
int end_x;
|
|
163
|
+
int processing_height;
|
|
161
164
|
Options incoming_options_;
|
|
162
165
|
|
|
163
166
|
private:
|
|
@@ -172,7 +175,7 @@ class PolygonFinder {
|
|
|
172
175
|
|
|
173
176
|
template <typename M, typename F>
|
|
174
177
|
void run_loop(M* specific_matcher, F&& fetch_color, int offset) {
|
|
175
|
-
int img_h = this->
|
|
178
|
+
int img_h = this->processing_height;
|
|
176
179
|
int bpp = this->source_bitmap->get_bytes_per_pixel();
|
|
177
180
|
|
|
178
181
|
for (int y = 0; y < img_h; y++) {
|
|
@@ -32,6 +32,9 @@ Finder::Finder(int number_of_threads, Bitmap *bitmap, Matcher *matcher, const Op
|
|
|
32
32
|
{ cpu_timer.start();
|
|
33
33
|
FinderUtils::sanitize_options(this->options_, options);
|
|
34
34
|
|
|
35
|
+
if (this->options_.processing_height != 0) {
|
|
36
|
+
this->height = std::abs(this->options_.processing_height);
|
|
37
|
+
}
|
|
35
38
|
double cw = static_cast<double>(this->maximum_width_) / this->options_.number_of_tiles;
|
|
36
39
|
if (cw < 1.0) {
|
|
37
40
|
throw std::runtime_error("One pixel tile width minimum!");
|
|
@@ -54,6 +57,9 @@ Finder::Finder(int number_of_threads, Bitmap *bitmap, Matcher *matcher, const Op
|
|
|
54
57
|
if (this->options_.treemap) {
|
|
55
58
|
base_arguments["treemap"] = true;
|
|
56
59
|
}
|
|
60
|
+
if (this->options_.processing_height != 0) {
|
|
61
|
+
base_arguments["processing_height"] = this->options_.processing_height;
|
|
62
|
+
}
|
|
57
63
|
|
|
58
64
|
CpuTimer t;
|
|
59
65
|
t.start();
|
|
@@ -166,6 +172,7 @@ ProcessResult* Finder::process_info() {
|
|
|
166
172
|
pr->has_bounds = this->options_.bounds;
|
|
167
173
|
pr->versus = this->options_.versus;
|
|
168
174
|
pr->options = this->input_options;
|
|
175
|
+
pr->number_of_threads = this->number_of_threads();
|
|
169
176
|
FakeCluster fake_cluster(pr->polygons, this->options_);
|
|
170
177
|
cpu_timer.start();
|
|
171
178
|
fake_cluster.compress_coords(pr->polygons, this->options_);
|
|
@@ -24,10 +24,10 @@ Tile::Tile(Finder *finder, int start_x, int end_x, std::string name, int index,
|
|
|
24
24
|
: finder(finder),
|
|
25
25
|
start_x_(start_x),
|
|
26
26
|
end_x_(end_x),
|
|
27
|
-
name_(name),
|
|
28
27
|
index_(index),
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
order_(order),
|
|
29
|
+
name_(name),
|
|
30
|
+
benchmarks(b) {
|
|
31
31
|
this->shapes_pool = new ShapePool();
|
|
32
32
|
this->shapes_pool->set_owner(this);
|
|
33
33
|
this->shapes_pools.push_back(this->shapes_pool);
|
|
@@ -37,6 +37,12 @@
|
|
|
37
37
|
#include "PolygonFinder/src/polygon/bitmaps/FastPngBitmap.cpp"
|
|
38
38
|
#include "PolygonFinder/src/polygon/bitmaps/RemoteFastPngBitmap.h"
|
|
39
39
|
#include "PolygonFinder/src/polygon/bitmaps/RemoteFastPngBitmap.cpp"
|
|
40
|
+
#include "PolygonFinder/src/polygon/bitmaps/streaming/RasterSource.h"
|
|
41
|
+
#include "PolygonFinder/src/polygon/bitmaps/streaming/AsciiSource.h"
|
|
42
|
+
#include "PolygonFinder/src/polygon/bitmaps/streaming/AsciiSource.cpp"
|
|
43
|
+
#include "PolygonFinder/src/polygon/bitmaps/streaming/PngSource.h"
|
|
44
|
+
#include "PolygonFinder/src/polygon/bitmaps/streaming/PngSource.cpp"
|
|
45
|
+
#include "PolygonFinder/src/polygon/bitmaps/streaming/RasterStreamer.h"
|
|
40
46
|
#include "PolygonFinder/src/polygon/matchers/Matcher.h"
|
|
41
47
|
#include "PolygonFinder/src/polygon/matchers/Matcher.cpp"
|
|
42
48
|
#include "PolygonFinder/src/polygon/matchers/ValueNotMatcher.h"
|
|
@@ -232,6 +238,7 @@ class To_Ruby<ProcessResult*>
|
|
|
232
238
|
return_me[Symbol("height")] = pr->height;
|
|
233
239
|
return_me[Symbol("versus")] = Symbol(pr->versus == Node::O ? "o" : "a");
|
|
234
240
|
return_me[Symbol("options")] = options_to_ruby(pr->options);
|
|
241
|
+
return_me[Symbol("number_of_threads")] = pr->number_of_threads;
|
|
235
242
|
|
|
236
243
|
Rice::Array out;
|
|
237
244
|
for (Polygon& x : pr->polygons)
|
|
@@ -394,6 +401,7 @@ void Init_cpp_polygon_finder() {
|
|
|
394
401
|
.define_method("value_at", &Bitmap::value_at)
|
|
395
402
|
.define_method("w", &Bitmap::w)
|
|
396
403
|
.define_method("h", &Bitmap::h)
|
|
404
|
+
.define_method("get_bytes_per_pixel", &Bitmap::get_bytes_per_pixel)
|
|
397
405
|
.define_method("error", &Bitmap::error)
|
|
398
406
|
.define_method("clear", &Bitmap::clear)
|
|
399
407
|
.define_method("print", &Bitmap::print);
|
|
@@ -401,6 +409,7 @@ void Init_cpp_polygon_finder() {
|
|
|
401
409
|
Data_Type<RawBitmap> rb_cRawBitmap =
|
|
402
410
|
define_class<RawBitmap, Bitmap>("CPPRawBitMap")
|
|
403
411
|
.define_constructor(Constructor<RawBitmap>())
|
|
412
|
+
.define_constructor(Constructor<RawBitmap, uint, uint>())
|
|
404
413
|
.define_method("rgb_value_at", &RawBitmap::rgb_value_at)
|
|
405
414
|
.define_method("w", &RawBitmap::w)
|
|
406
415
|
.define_method("h", &RawBitmap::h)
|
|
@@ -429,6 +438,44 @@ void Init_cpp_polygon_finder() {
|
|
|
429
438
|
.define_method("error", &FastPngBitmap::error)
|
|
430
439
|
.define_method("print", &FastPngBitmap::print);
|
|
431
440
|
|
|
441
|
+
Data_Type<RasterSource> rb_cRasterSource =
|
|
442
|
+
define_class<RasterSource>("CPPRasterSource");
|
|
443
|
+
|
|
444
|
+
Data_Type<AsciiSource> rb_cAsciiSource =
|
|
445
|
+
define_class<AsciiSource, RasterSource>("CPPAsciiSource")
|
|
446
|
+
.define_constructor(Constructor<AsciiSource, Bitmap&>(),Arg("bitmap"))
|
|
447
|
+
.define_method("width",&AsciiSource::width)
|
|
448
|
+
.define_method("height",&AsciiSource::height)
|
|
449
|
+
.define_method("get_bytes_per_pixel",&AsciiSource::get_bytes_per_pixel);
|
|
450
|
+
|
|
451
|
+
Data_Type<PngSource> rb_cPngSource =
|
|
452
|
+
define_class<PngSource, RasterSource>("CPPPngSource")
|
|
453
|
+
.define_constructor(Constructor<PngSource, std::string>(),Arg("filepath"))
|
|
454
|
+
.define_method("width",&PngSource::width)
|
|
455
|
+
.define_method("height",&PngSource::height)
|
|
456
|
+
.define_method("get_bytes_per_pixel",&PngSource::get_bytes_per_pixel);
|
|
457
|
+
|
|
458
|
+
Data_Type<RasterStreamer> rb_cRasterStreamer =
|
|
459
|
+
define_class<RasterStreamer>("CPPRasterStreamer")
|
|
460
|
+
.define_constructor(Constructor<RasterStreamer, RasterSource&, uint32_t>(),Arg("source"),Arg("stripe_height"))
|
|
461
|
+
.define_method("stripe_height",&RasterStreamer::stripe_height)
|
|
462
|
+
.define_method("each",[](RasterStreamer& self, Bitmap& buffer) {
|
|
463
|
+
VALUE block = rb_block_proc();
|
|
464
|
+
self.each(
|
|
465
|
+
buffer,
|
|
466
|
+
[block](Bitmap& bitmap,uint32_t buffer_rows,std::size_t buffer_size) {
|
|
467
|
+
Rice::Data_Object<Bitmap> rb_bitmap(&bitmap);
|
|
468
|
+
Rice::Object(block).call(
|
|
469
|
+
"call",
|
|
470
|
+
rb_bitmap,
|
|
471
|
+
buffer_rows,
|
|
472
|
+
buffer_size
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
);
|
|
478
|
+
|
|
432
479
|
Data_Type<Matcher> rb_cMatcher =
|
|
433
480
|
define_class<Matcher>("CPPMatcher")
|
|
434
481
|
.define_constructor(Constructor<Matcher, char>())
|
|
@@ -5,7 +5,7 @@ module Contrek
|
|
|
5
5
|
class Bitmap
|
|
6
6
|
include Painting
|
|
7
7
|
|
|
8
|
-
def scan(start_x: 0, end_x: w)
|
|
8
|
+
def scan(start_x: 0, end_x: w, end_y: h)
|
|
9
9
|
x = start_x
|
|
10
10
|
y = 0
|
|
11
11
|
loop do
|
|
@@ -14,7 +14,7 @@ module Contrek
|
|
|
14
14
|
if x == end_x
|
|
15
15
|
x = start_x
|
|
16
16
|
y += 1
|
|
17
|
-
break if y ==
|
|
17
|
+
break if y == end_y
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
end
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "ffi"
|
|
4
|
+
|
|
3
5
|
module Contrek
|
|
4
6
|
module Bitmaps
|
|
5
7
|
class ChunkyBitmap < Bitmap
|
|
6
8
|
def initialize(data, mod)
|
|
7
|
-
@raw = data.dup
|
|
8
9
|
@module = mod
|
|
10
|
+
@height = data.bytesize / @module
|
|
11
|
+
@raw = FFI::MemoryPointer.new(:uint8, data.bytesize)
|
|
12
|
+
@raw.put_bytes(0, data)
|
|
9
13
|
end
|
|
10
14
|
|
|
11
15
|
def clear(val = "0")
|
|
12
|
-
@raw
|
|
16
|
+
@raw.put_bytes(0, val * @module * h)
|
|
13
17
|
end
|
|
14
18
|
|
|
15
19
|
def w
|
|
@@ -17,22 +21,30 @@ module Contrek
|
|
|
17
21
|
end
|
|
18
22
|
|
|
19
23
|
def h
|
|
20
|
-
@
|
|
24
|
+
@height
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def get_bytes_per_pixel
|
|
28
|
+
1
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def get_row_ptr(y)
|
|
32
|
+
@raw + (@module * y)
|
|
21
33
|
end
|
|
22
34
|
|
|
23
35
|
def value_at(x, y)
|
|
24
|
-
@raw
|
|
36
|
+
@raw.get_uint8(y * @module + x).chr
|
|
25
37
|
end
|
|
26
38
|
|
|
27
39
|
def value_set(x, y, value)
|
|
28
40
|
return if y >= h
|
|
29
41
|
return if x >= w
|
|
30
42
|
|
|
31
|
-
@raw
|
|
43
|
+
@raw.put_uint8(y * @module + x, value.ord)
|
|
32
44
|
end
|
|
33
45
|
|
|
34
46
|
def dup!
|
|
35
|
-
ChunkyBitmap.new(@raw, @module)
|
|
47
|
+
ChunkyBitmap.new(@raw.read_bytes(@module * h), @module)
|
|
36
48
|
end
|
|
37
49
|
|
|
38
50
|
def draw_rect(x:, y:, width:, height:, color: "o", filled: true)
|
|
@@ -80,7 +92,7 @@ module Contrek
|
|
|
80
92
|
puts label if label
|
|
81
93
|
puts " " + (0...@module).map { |i| (i % 10).to_s }.join
|
|
82
94
|
n = 0
|
|
83
|
-
@raw.scan(/.{1,#{@module}}/).each do |line|
|
|
95
|
+
@raw.read_bytes(@module * h).scan(/.{1,#{@module}}/).each do |line|
|
|
84
96
|
colored_line = line.chars.map { |c| colorize_char(c) }.join
|
|
85
97
|
puts "#{n} #{colored_line}"
|
|
86
98
|
n += 1
|
|
@@ -96,8 +108,11 @@ module Contrek
|
|
|
96
108
|
transposed += value_at(x, y)
|
|
97
109
|
end
|
|
98
110
|
end
|
|
99
|
-
|
|
111
|
+
old_width = w
|
|
100
112
|
@module = h
|
|
113
|
+
@height = old_width
|
|
114
|
+
@raw = FFI::MemoryPointer.new(:uint8, transposed.bytesize)
|
|
115
|
+
@raw.put_bytes(0, transposed)
|
|
101
116
|
end
|
|
102
117
|
|
|
103
118
|
private
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ffi"
|
|
4
|
+
|
|
5
|
+
module Contrek
|
|
6
|
+
module Bitmaps
|
|
7
|
+
class RgbaBitmap < Bitmap
|
|
8
|
+
BYTES_PER_PIXEL = 4
|
|
9
|
+
|
|
10
|
+
def initialize(width, height)
|
|
11
|
+
@width = width
|
|
12
|
+
@height = height
|
|
13
|
+
@raw = FFI::MemoryPointer.new(:uint8, @width * @height * BYTES_PER_PIXEL)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def w
|
|
17
|
+
@width
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def h
|
|
21
|
+
@height
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def get_bytes_per_pixel
|
|
25
|
+
BYTES_PER_PIXEL
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def value_at(x, y)
|
|
29
|
+
rgb_value_at(x, y)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def get_row_ptr(y)
|
|
33
|
+
@raw + (y * @width * BYTES_PER_PIXEL)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def rgb_value_at(x, y)
|
|
37
|
+
index = (y * @width + x) * BYTES_PER_PIXEL
|
|
38
|
+
r = @raw.get_uint8(index)
|
|
39
|
+
g = @raw.get_uint8(index + 1)
|
|
40
|
+
b = @raw.get_uint8(index + 2)
|
|
41
|
+
a = @raw.get_uint8(index + 3)
|
|
42
|
+
r | (g << 8) | (b << 16) | (a << 24)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def read_bytes(size)
|
|
46
|
+
@raw.read_bytes(size)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ffi"
|
|
4
|
+
|
|
5
|
+
module Contrek
|
|
6
|
+
module Bitmaps
|
|
7
|
+
module Streaming
|
|
8
|
+
class RasterStreamer
|
|
9
|
+
OVERLAP = 1
|
|
10
|
+
|
|
11
|
+
attr_reader :stripe_height
|
|
12
|
+
|
|
13
|
+
def initialize(source, stripe_height:)
|
|
14
|
+
@source = source
|
|
15
|
+
@stripe_height = stripe_height
|
|
16
|
+
|
|
17
|
+
raise ArgumentError, "stripe_height must be greater than 1" if @stripe_height <= OVERLAP
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def each(buffer)
|
|
21
|
+
return enum_for(:each, buffer) unless block_given?
|
|
22
|
+
|
|
23
|
+
width = @source.width
|
|
24
|
+
height = @source.height
|
|
25
|
+
bytes_per_pixel = @source.get_bytes_per_pixel
|
|
26
|
+
|
|
27
|
+
row_size = width * bytes_per_pixel
|
|
28
|
+
|
|
29
|
+
source_row = 0
|
|
30
|
+
buffer_rows = 0
|
|
31
|
+
first_stripe = true
|
|
32
|
+
|
|
33
|
+
while source_row < height
|
|
34
|
+
unless first_stripe
|
|
35
|
+
previous_last_row = buffer.get_row_ptr(buffer_rows - 1)
|
|
36
|
+
current_first_row = buffer.get_row_ptr(0)
|
|
37
|
+
current_first_row.put_bytes(0, previous_last_row.read_bytes(row_size))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
overlap_rows = first_stripe ? 0 : OVERLAP
|
|
41
|
+
available_rows = @stripe_height - overlap_rows
|
|
42
|
+
remaining_rows = height - source_row
|
|
43
|
+
rows_to_read = [available_rows, remaining_rows].min
|
|
44
|
+
|
|
45
|
+
rows_read = 0
|
|
46
|
+
rows_to_read.times do |i|
|
|
47
|
+
buffer_y = overlap_rows + i
|
|
48
|
+
row_ptr = buffer.get_row_ptr(buffer_y)
|
|
49
|
+
break unless @source.read_next_row(row_ptr, row_size)
|
|
50
|
+
|
|
51
|
+
source_row += 1
|
|
52
|
+
rows_read += 1
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
buffer_rows = overlap_rows + rows_read
|
|
56
|
+
break if buffer_rows == 0
|
|
57
|
+
|
|
58
|
+
yield buffer, buffer_rows, buffer_rows * row_size
|
|
59
|
+
|
|
60
|
+
first_stripe = false
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "forwardable"
|
|
4
|
+
|
|
5
|
+
module Contrek
|
|
6
|
+
module Bitmaps
|
|
7
|
+
module Streaming
|
|
8
|
+
module Sources
|
|
9
|
+
class Ascii < Base
|
|
10
|
+
extend Forwardable
|
|
11
|
+
|
|
12
|
+
def_delegator :@bitmap, :w, :width
|
|
13
|
+
def_delegator :@bitmap, :h, :height
|
|
14
|
+
def_delegators :@bitmap, :get_bytes_per_pixel
|
|
15
|
+
|
|
16
|
+
def initialize(bitmap)
|
|
17
|
+
@current_row = 0
|
|
18
|
+
@bitmap = bitmap
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def read_next_row(destination, row_size)
|
|
22
|
+
return false if @current_row >= height
|
|
23
|
+
|
|
24
|
+
row = @bitmap.get_row_ptr(@current_row)
|
|
25
|
+
destination.put_bytes(0, row.read_bytes(row_size))
|
|
26
|
+
@current_row += 1
|
|
27
|
+
true
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Contrek
|
|
4
|
+
module Bitmaps
|
|
5
|
+
module Streaming
|
|
6
|
+
module Sources
|
|
7
|
+
class Base
|
|
8
|
+
def width
|
|
9
|
+
raise NotImplementedError
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def height
|
|
13
|
+
raise NotImplementedError
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def get_bytes_per_pixel
|
|
17
|
+
raise NotImplementedError
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def read_next_row(destination, row_size)
|
|
21
|
+
raise NotImplementedError
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ffi"
|
|
4
|
+
require "cpp_polygon_finder"
|
|
5
|
+
|
|
6
|
+
module Contrek
|
|
7
|
+
module Bitmaps
|
|
8
|
+
module Streaming
|
|
9
|
+
module Sources
|
|
10
|
+
class Png
|
|
11
|
+
attr_reader :width, :height
|
|
12
|
+
|
|
13
|
+
module LibSpng
|
|
14
|
+
extend FFI::Library
|
|
15
|
+
|
|
16
|
+
ffi_lib FFI::Library::CURRENT_PROCESS
|
|
17
|
+
|
|
18
|
+
SPNG_FMT_RGBA8 = 1
|
|
19
|
+
SPNG_DECODE_PROGRESSIVE = 256
|
|
20
|
+
SPNG_EOI = 75
|
|
21
|
+
|
|
22
|
+
class Ihdr < FFI::Struct
|
|
23
|
+
layout(
|
|
24
|
+
:width, :uint32,
|
|
25
|
+
:height, :uint32,
|
|
26
|
+
:bit_depth, :uint8,
|
|
27
|
+
:color_type, :uint8,
|
|
28
|
+
:compression_method, :uint8,
|
|
29
|
+
:filter_method, :uint8,
|
|
30
|
+
:interlace_method, :uint8
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
attach_function :spng_ctx_new, [:int], :pointer
|
|
35
|
+
attach_function :spng_ctx_free, [:pointer], :void
|
|
36
|
+
attach_function :spng_set_png_file, [:pointer, :pointer], :int
|
|
37
|
+
attach_function :spng_get_ihdr, [:pointer, :pointer], :int
|
|
38
|
+
attach_function :spng_decode_image, [:pointer, :pointer, :size_t, :int, :int], :int
|
|
39
|
+
attach_function :spng_decode_row, [:pointer, :pointer, :size_t], :int
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
module LibC
|
|
43
|
+
extend FFI::Library
|
|
44
|
+
|
|
45
|
+
ffi_lib FFI::Library::LIBC
|
|
46
|
+
|
|
47
|
+
attach_function :fopen, [:string, :string], :pointer
|
|
48
|
+
attach_function :fclose, [:pointer], :int
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def initialize(filepath)
|
|
52
|
+
@fp = LibC.fopen(filepath, "rb")
|
|
53
|
+
raise "Unable to open PNG: #{filepath}" if @fp.null?
|
|
54
|
+
|
|
55
|
+
@ctx = LibSpng.spng_ctx_new(0)
|
|
56
|
+
raise "Unable to create spng context" if @ctx.null?
|
|
57
|
+
|
|
58
|
+
ret = LibSpng.spng_set_png_file(@ctx, @fp)
|
|
59
|
+
raise "spng_set_png_file failed: #{ret}" unless ret == 0
|
|
60
|
+
|
|
61
|
+
ihdr = LibSpng::Ihdr.new
|
|
62
|
+
|
|
63
|
+
ret = LibSpng.spng_get_ihdr(@ctx, ihdr)
|
|
64
|
+
raise "spng_get_ihdr failed: #{ret}" unless ret == 0
|
|
65
|
+
|
|
66
|
+
@width = ihdr[:width]
|
|
67
|
+
@height = ihdr[:height]
|
|
68
|
+
|
|
69
|
+
ret = LibSpng.spng_decode_image(
|
|
70
|
+
@ctx,
|
|
71
|
+
nil,
|
|
72
|
+
0,
|
|
73
|
+
LibSpng::SPNG_FMT_RGBA8,
|
|
74
|
+
LibSpng::SPNG_DECODE_PROGRESSIVE
|
|
75
|
+
)
|
|
76
|
+
raise "Unable to initialize progressive decoding: #{ret}" unless ret == 0
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def get_bytes_per_pixel
|
|
80
|
+
4
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def read_next_row(destination, row_size)
|
|
84
|
+
ret = LibSpng.spng_decode_row(@ctx, destination, row_size)
|
|
85
|
+
unless ret == 0 || ret == LibSpng::SPNG_EOI
|
|
86
|
+
raise "spng_decode_row failed: #{ret}"
|
|
87
|
+
end
|
|
88
|
+
true
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def close
|
|
92
|
+
unless @ctx.nil? || @ctx.null?
|
|
93
|
+
LibSpng.spng_ctx_free(@ctx)
|
|
94
|
+
@ctx = nil
|
|
95
|
+
end
|
|
96
|
+
unless @fp.nil? || @fp.null?
|
|
97
|
+
LibC.fclose(@fp)
|
|
98
|
+
@fp = nil
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -30,7 +30,7 @@ module Contrek
|
|
|
30
30
|
@options = options
|
|
31
31
|
@clusters = []
|
|
32
32
|
@maximum_width = bitmap.w
|
|
33
|
-
@height = bitmap.h
|
|
33
|
+
@height = options[:processing_height] || bitmap.h
|
|
34
34
|
@number_of_tiles = options[:number_of_tiles] || (raise "number_of_tiles params is needed!")
|
|
35
35
|
@number_of_tiles = 1 if @number_of_tiles <= 0
|
|
36
36
|
|
|
@@ -51,6 +51,7 @@ module Contrek
|
|
|
51
51
|
bitmap: bitmap,
|
|
52
52
|
matcher: matcher,
|
|
53
53
|
options: {
|
|
54
|
+
processing_height: @options[:processing_height],
|
|
54
55
|
versus: current_versus,
|
|
55
56
|
bounds: true,
|
|
56
57
|
treemap: @options[:treemap],
|
|
@@ -71,7 +72,7 @@ module Contrek
|
|
|
71
72
|
|
|
72
73
|
x = tile_end_x - 1
|
|
73
74
|
end
|
|
74
|
-
process_tiles!(bitmap, height:
|
|
75
|
+
process_tiles!(bitmap, height: @height, deterministic: @options[:deterministic])
|
|
75
76
|
end.real
|
|
76
77
|
end
|
|
77
78
|
|
|
@@ -95,7 +96,8 @@ module Contrek
|
|
|
95
96
|
width: @maximum_width,
|
|
96
97
|
height: @height,
|
|
97
98
|
versus: options[:versus],
|
|
98
|
-
options: options
|
|
99
|
+
options: options,
|
|
100
|
+
number_of_threads: @number_of_threads
|
|
99
101
|
}
|
|
100
102
|
metadata[:treemap] = @whole_tile.compute_treemap if options[:treemap]
|
|
101
103
|
Contrek::Finder::Result.new(raw_polygons, metadata)
|