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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -1
  3. data/Gemfile.lock +4 -1
  4. data/PERFORMANCE.md +40 -4
  5. data/README.md +211 -59
  6. data/contrek.gemspec +1 -0
  7. data/ext/cpp_polygon_finder/PolygonFinder/src/ContrekApi.h +37 -11
  8. data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +53 -144
  9. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/RawBitmap.cpp +14 -2
  10. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/RawBitmap.h +1 -0
  11. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/AsciiSource.cpp +69 -0
  12. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/AsciiSource.h +34 -0
  13. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/PngSource.cpp +108 -0
  14. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/PngSource.h +40 -0
  15. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/RasterSource.h +22 -0
  16. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/RasterStreamer.h +92 -0
  17. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/FinderUtils.cpp +6 -8
  18. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +8 -2
  19. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.h +4 -1
  20. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp +7 -0
  21. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Poolable.h +1 -0
  22. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.cpp +3 -3
  23. data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +47 -0
  24. data/lib/contrek/bitmaps/bitmap.rb +2 -2
  25. data/lib/contrek/bitmaps/chunky_bitmap.rb +23 -8
  26. data/lib/contrek/bitmaps/png_bitmap.rb +4 -0
  27. data/lib/contrek/bitmaps/rgba_bitmap.rb +50 -0
  28. data/lib/contrek/bitmaps/streaming/raster_streamer.rb +66 -0
  29. data/lib/contrek/bitmaps/streaming/sources/ascii.rb +33 -0
  30. data/lib/contrek/bitmaps/streaming/sources/base.rb +27 -0
  31. data/lib/contrek/bitmaps/streaming/sources/png.rb +105 -0
  32. data/lib/contrek/finder/concurrent/finder.rb +5 -3
  33. data/lib/contrek/finder/polygon_finder.rb +8 -4
  34. data/lib/contrek/version.rb +1 -1
  35. data/lib/contrek.rb +5 -0
  36. 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->node_cluster = new NodeCluster(source_bitmap->h(), source_bitmap->w(), &this->options);
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->source_bitmap->h();
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->source_bitmap->h();
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_);
@@ -38,6 +38,7 @@ class Poolable {
38
38
  }
39
39
  }
40
40
  void wait();
41
+ int number_of_threads() const { return number_of_threads_; }
41
42
 
42
43
  private:
43
44
  std::vector<std::thread> workers;
@@ -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
- benchmarks(b),
30
- order_(order) {
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 == h
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 = val * @module * h
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
- @raw.size / @module
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[y * @module + x]
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[y * @module + x] = value
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
- @raw = transposed
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
@@ -20,6 +20,10 @@ module Contrek
20
20
  @image.dimension.height
21
21
  end
22
22
 
23
+ def get_bytes_per_pixel
24
+ 4
25
+ end
26
+
23
27
  def draw_line(start_x, start_y, end_x, end_y, value)
24
28
  @image.line_xiaolin_wu(start_x, start_y, end_x, end_y, value)
25
29
  end
@@ -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: bitmap.h, deterministic: @options[:deterministic])
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)