contrek 1.4.1 → 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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/Gemfile.lock +4 -1
  4. data/README.md +207 -36
  5. data/contrek.gemspec +1 -0
  6. data/ext/cpp_polygon_finder/PolygonFinder/src/ContrekApi.h +2 -2
  7. data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +53 -144
  8. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/RawBitmap.cpp +14 -2
  9. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/RawBitmap.h +1 -0
  10. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/AsciiSource.cpp +69 -0
  11. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/AsciiSource.h +34 -0
  12. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/PngSource.cpp +108 -0
  13. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/PngSource.h +40 -0
  14. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/RasterSource.h +22 -0
  15. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/RasterStreamer.h +92 -0
  16. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/FinderUtils.cpp +6 -8
  17. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +7 -2
  18. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.h +3 -1
  19. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp +6 -0
  20. data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +46 -0
  21. data/lib/contrek/bitmaps/bitmap.rb +2 -2
  22. data/lib/contrek/bitmaps/chunky_bitmap.rb +23 -8
  23. data/lib/contrek/bitmaps/png_bitmap.rb +4 -0
  24. data/lib/contrek/bitmaps/rgba_bitmap.rb +50 -0
  25. data/lib/contrek/bitmaps/streaming/raster_streamer.rb +66 -0
  26. data/lib/contrek/bitmaps/streaming/sources/ascii.rb +33 -0
  27. data/lib/contrek/bitmaps/streaming/sources/base.rb +27 -0
  28. data/lib/contrek/bitmaps/streaming/sources/png.rb +105 -0
  29. data/lib/contrek/finder/concurrent/finder.rb +3 -2
  30. data/lib/contrek/finder/polygon_finder.rb +6 -3
  31. data/lib/contrek/version.rb +1 -1
  32. data/lib/contrek.rb +5 -0
  33. metadata +26 -1
@@ -0,0 +1,69 @@
1
+ /*
2
+ * AsciiSource.cpp
3
+ *
4
+ * Copyright (c) 2025-2026 Emanuele Cesaroni
5
+ *
6
+ * Licensed under the GNU Affero General Public License v3 (AGPLv3).
7
+ * See the LICENSE file in this directory for the full license text.
8
+ */
9
+
10
+ #include "AsciiSource.h"
11
+ #include <cstring>
12
+ #include <stdexcept>
13
+
14
+ AsciiSource::AsciiSource(Bitmap& bitmap)
15
+ : bitmap_(bitmap),
16
+ current_row_(0) {
17
+ }
18
+
19
+ uint32_t AsciiSource::width() const {
20
+ return static_cast<uint32_t>(
21
+ bitmap_.w()
22
+ );
23
+ }
24
+
25
+ uint32_t AsciiSource::height() const {
26
+ return static_cast<uint32_t>(
27
+ bitmap_.h()
28
+ );
29
+ }
30
+
31
+ uint32_t AsciiSource::get_bytes_per_pixel() const {
32
+ return static_cast<uint32_t>(
33
+ bitmap_.get_bytes_per_pixel()
34
+ );
35
+ }
36
+
37
+ bool AsciiSource::read_next_row(
38
+ unsigned char* destination,
39
+ std::size_t row_size
40
+ ) {
41
+ if (current_row_ >= height()) {
42
+ return false;
43
+ }
44
+
45
+ const unsigned char* row =
46
+ bitmap_.get_row_ptr(current_row_);
47
+
48
+ const std::size_t expected_row_size =
49
+ static_cast<std::size_t>(width()) *
50
+ static_cast<std::size_t>(
51
+ get_bytes_per_pixel()
52
+ );
53
+
54
+ if (expected_row_size != row_size) {
55
+ throw std::runtime_error(
56
+ "Invalid row size"
57
+ );
58
+ }
59
+
60
+ std::memcpy(
61
+ destination,
62
+ row,
63
+ row_size
64
+ );
65
+
66
+ ++current_row_;
67
+
68
+ return true;
69
+ }
@@ -0,0 +1,34 @@
1
+ /*
2
+ * AsciiSource.h
3
+ *
4
+ * Copyright (c) 2025-2026 Emanuele Cesaroni
5
+ *
6
+ * Licensed under the GNU Affero General Public License v3 (AGPLv3).
7
+ * See the LICENSE file in this directory for the full license text.
8
+ */
9
+
10
+ #pragma once
11
+
12
+ #include <cstddef>
13
+ #include <cstdint>
14
+
15
+ #include "RasterSource.h"
16
+ #include "../Bitmap.h"
17
+
18
+ class AsciiSource : public RasterSource {
19
+ public:
20
+ explicit AsciiSource(Bitmap& bitmap);
21
+
22
+ uint32_t width() const override;
23
+ uint32_t height() const override;
24
+ uint32_t get_bytes_per_pixel() const override;
25
+
26
+ bool read_next_row(
27
+ unsigned char* destination,
28
+ std::size_t row_size
29
+ ) override;
30
+
31
+ private:
32
+ Bitmap& bitmap_;
33
+ uint32_t current_row_;
34
+ };
@@ -0,0 +1,108 @@
1
+ /*
2
+ * PngSource.cpp
3
+ *
4
+ * Copyright (c) 2025-2026 Emanuele Cesaroni
5
+ *
6
+ * Licensed under the GNU Affero General Public License v3 (AGPLv3).
7
+ * See the LICENSE file in this directory for the full license text.
8
+ */
9
+
10
+ #include "PngSource.h"
11
+
12
+ #include <stdexcept>
13
+ #include <string>
14
+
15
+ PngSource::PngSource(const std::string& filepath)
16
+ : fp_(nullptr),
17
+ ctx_(nullptr),
18
+ width_(0),
19
+ height_(0),
20
+ current_row_(0) {
21
+ fp_ = std::fopen(filepath.c_str(),"rb");
22
+ if (!fp_) {
23
+ throw std::runtime_error("Unable to open PNG file");
24
+ }
25
+
26
+ ctx_ = spng_ctx_new(0);
27
+ if (!ctx_) {
28
+ std::fclose(fp_);
29
+ fp_ = nullptr;
30
+ throw std::runtime_error("Unable to create spng context");
31
+ }
32
+
33
+ int ret = spng_set_png_file(ctx_,fp_);
34
+ if (ret != 0) {
35
+ spng_ctx_free(ctx_);
36
+ ctx_ = nullptr;
37
+ std::fclose(fp_);
38
+ fp_ = nullptr;
39
+ throw std::runtime_error(std::string("spng_set_png_file failed: ") + spng_strerror(ret));
40
+ }
41
+
42
+ spng_ihdr ihdr{};
43
+ ret = spng_get_ihdr(ctx_, &ihdr);
44
+
45
+ if (ret != 0) {
46
+ spng_ctx_free(ctx_);
47
+ ctx_ = nullptr;
48
+ std::fclose(fp_);
49
+ fp_ = nullptr;
50
+ throw std::runtime_error(std::string("spng_get_ihdr failed: ") + spng_strerror(ret));
51
+ }
52
+
53
+ width_ = static_cast<uint32_t>(ihdr.width);
54
+ height_ = static_cast<uint32_t>(ihdr.height);
55
+ ret = spng_decode_image(
56
+ ctx_,
57
+ nullptr,
58
+ 0,
59
+ SPNG_FMT_RGBA8,
60
+ SPNG_DECODE_PROGRESSIVE
61
+ );
62
+
63
+ if (ret != 0) {
64
+ spng_ctx_free(ctx_);
65
+ ctx_ = nullptr;
66
+ std::fclose(fp_);
67
+ fp_ = nullptr;
68
+ throw std::runtime_error(std::string("spng_decode_image failed: ") + spng_strerror(ret));
69
+ }
70
+ }
71
+
72
+ PngSource::~PngSource()
73
+ { if (ctx_) {
74
+ spng_ctx_free(ctx_);
75
+ }
76
+ if (fp_) {
77
+ std::fclose(fp_);
78
+ }
79
+ }
80
+
81
+ uint32_t PngSource::width() const {
82
+ return width_;
83
+ }
84
+
85
+ uint32_t PngSource::height() const {
86
+ return height_;
87
+ }
88
+
89
+ uint32_t PngSource::get_bytes_per_pixel() const {
90
+ return 4;
91
+ }
92
+
93
+ bool PngSource::read_next_row(unsigned char* destination, std::size_t row_size) {
94
+ if (current_row_ >= height_) {
95
+ return false;
96
+ }
97
+ const std::size_t expected_row_size = static_cast<std::size_t>(width_) * 4;
98
+ if (row_size != expected_row_size) {
99
+ throw std::runtime_error("Invalid row size");
100
+ }
101
+ const int ret = spng_decode_row(ctx_,destination,row_size);
102
+
103
+ if (ret != 0 && ret != SPNG_EOI) {
104
+ throw std::runtime_error(std::string("spng_decode_row failed: ") + spng_strerror(ret));
105
+ }
106
+ ++current_row_;
107
+ return true;
108
+ }
@@ -0,0 +1,40 @@
1
+ /*
2
+ * PngSource.h
3
+ *
4
+ * Copyright (c) 2025-2026 Emanuele Cesaroni
5
+ *
6
+ * Licensed under the GNU Affero General Public License v3 (AGPLv3).
7
+ * See the LICENSE file in this directory for the full license text.
8
+ */
9
+
10
+ #pragma once
11
+
12
+ #include <cstddef>
13
+ #include <cstdint>
14
+ #include <cstdio>
15
+ #include <string>
16
+
17
+ extern "C" {
18
+ #include "../spng.h"
19
+ }
20
+
21
+ #include "RasterSource.h"
22
+
23
+ class PngSource : public RasterSource {
24
+ public:
25
+ explicit PngSource(const std::string& filepath);
26
+ ~PngSource() override;
27
+
28
+ uint32_t width() const override;
29
+ uint32_t height() const override;
30
+ uint32_t get_bytes_per_pixel() const override;
31
+ bool read_next_row(unsigned char* destination, std::size_t row_size) override;
32
+
33
+ private:
34
+ FILE* fp_;
35
+ spng_ctx* ctx_;
36
+
37
+ uint32_t width_;
38
+ uint32_t height_;
39
+ uint32_t current_row_;
40
+ };
@@ -0,0 +1,22 @@
1
+ /*
2
+ * RasterSource.cpp
3
+ *
4
+ * Copyright (c) 2025-2026 Emanuele Cesaroni
5
+ *
6
+ * Licensed under the GNU Affero General Public License v3 (AGPLv3).
7
+ * See the LICENSE file in this directory for the full license text.
8
+ */
9
+
10
+ #pragma once
11
+
12
+ #include <cstddef>
13
+ #include <cstdint>
14
+
15
+ class RasterSource {
16
+ public:
17
+ virtual ~RasterSource() = default;
18
+ virtual uint32_t width() const = 0;
19
+ virtual uint32_t height() const = 0;
20
+ virtual uint32_t get_bytes_per_pixel() const = 0;
21
+ virtual bool read_next_row(unsigned char* destination, std::size_t row_size) = 0;
22
+ };
@@ -0,0 +1,92 @@
1
+ /*
2
+ * RasterStreamer.h
3
+ *
4
+ * Copyright (c) 2025-2026 Emanuele Cesaroni
5
+ *
6
+ * Licensed under the GNU Affero General Public License v3 (AGPLv3).
7
+ * See the LICENSE file in this directory for the full license text.
8
+ */
9
+
10
+ #pragma once
11
+
12
+ #include <algorithm>
13
+ #include <cstddef>
14
+ #include <cstdint>
15
+ #include <cstring>
16
+ #include <stdexcept>
17
+ #include <utility>
18
+ #include <vector>
19
+
20
+ #include "RasterSource.h"
21
+ #include "../RawBitmap.h"
22
+
23
+ class RasterStreamer {
24
+ public:
25
+ static constexpr uint32_t OVERLAP = 1;
26
+
27
+ RasterStreamer(RasterSource& source, uint32_t stripe_height)
28
+ : source_(source),
29
+ stripe_height_(stripe_height) {
30
+ if (stripe_height_ <= OVERLAP) {
31
+ throw std::invalid_argument("stripe_height must be greater than 1");
32
+ }
33
+ }
34
+ uint32_t stripe_height() const { return stripe_height_; }
35
+
36
+ template <typename Callback>
37
+ void each(Bitmap& buffer, Callback&& callback)
38
+ { const uint32_t width = source_.width();
39
+ const uint32_t height = source_.height();
40
+ const uint32_t bytes_per_pixel = source_.get_bytes_per_pixel();
41
+ const std::size_t row_size = static_cast<std::size_t>(width) * static_cast<std::size_t>(bytes_per_pixel);
42
+ uint32_t source_row = 0;
43
+ uint32_t previous_buffer_rows = 0;
44
+ bool first_stripe = true;
45
+
46
+ while (source_row < height) {
47
+ std::vector<unsigned char> overlap_row;
48
+ if (!first_stripe) {
49
+ overlap_row.resize(row_size);
50
+ const unsigned char* previous_last_row = buffer.get_row_ptr(previous_buffer_rows - 1);
51
+ std::memcpy(overlap_row.data(), previous_last_row, row_size);
52
+ }
53
+
54
+ const uint32_t overlap_rows = first_stripe ? 0 : OVERLAP;
55
+ const uint32_t available_rows = stripe_height_ - overlap_rows;
56
+ const uint32_t remaining_rows = height - source_row;
57
+ const uint32_t rows_to_read = std::min(available_rows, remaining_rows);
58
+ const uint32_t buffer_rows = overlap_rows + rows_to_read;
59
+
60
+ if (!first_stripe) {
61
+ unsigned char* first_row = const_cast<unsigned char*>(buffer.get_row_ptr(0));
62
+ std::memcpy(first_row, overlap_row.data(), row_size);
63
+ }
64
+ uint32_t rows_read = 0;
65
+ for (uint32_t i = 0; i < rows_to_read; ++i) {
66
+ const uint32_t buffer_y = overlap_rows + i;
67
+ unsigned char* destination = const_cast<unsigned char*>(buffer.get_row_ptr(buffer_y));
68
+ if (!source_.read_next_row(destination, row_size)) {
69
+ break;
70
+ }
71
+ ++source_row;
72
+ ++rows_read;
73
+ }
74
+ if (rows_read == 0) {
75
+ break;
76
+ }
77
+ const uint32_t actual_buffer_rows = overlap_rows + rows_read;
78
+ if (actual_buffer_rows != buffer_rows) {
79
+ throw std::runtime_error("Raster source ended before expected image height!");
80
+ }
81
+ const std::size_t buffer_size = static_cast<std::size_t>(buffer_rows) * row_size;
82
+
83
+ std::forward<Callback>(callback)(buffer, buffer_rows, buffer_size);
84
+ previous_buffer_rows = buffer_rows;
85
+ first_stripe = false;
86
+ }
87
+ }
88
+
89
+ private:
90
+ RasterSource& source_;
91
+ uint32_t stripe_height_;
92
+ };
@@ -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,7 +111,7 @@ 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_;
@@ -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;
@@ -159,6 +160,7 @@ class PolygonFinder {
159
160
  protected:
160
161
  int start_x;
161
162
  int end_x;
163
+ int processing_height;
162
164
  Options incoming_options_;
163
165
 
164
166
  private:
@@ -173,7 +175,7 @@ class PolygonFinder {
173
175
 
174
176
  template <typename M, typename F>
175
177
  void run_loop(M* specific_matcher, F&& fetch_color, int offset) {
176
- int img_h = this->source_bitmap->h();
178
+ int img_h = this->processing_height;
177
179
  int bpp = this->source_bitmap->get_bytes_per_pixel();
178
180
 
179
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();
@@ -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"
@@ -395,6 +401,7 @@ void Init_cpp_polygon_finder() {
395
401
  .define_method("value_at", &Bitmap::value_at)
396
402
  .define_method("w", &Bitmap::w)
397
403
  .define_method("h", &Bitmap::h)
404
+ .define_method("get_bytes_per_pixel", &Bitmap::get_bytes_per_pixel)
398
405
  .define_method("error", &Bitmap::error)
399
406
  .define_method("clear", &Bitmap::clear)
400
407
  .define_method("print", &Bitmap::print);
@@ -402,6 +409,7 @@ void Init_cpp_polygon_finder() {
402
409
  Data_Type<RawBitmap> rb_cRawBitmap =
403
410
  define_class<RawBitmap, Bitmap>("CPPRawBitMap")
404
411
  .define_constructor(Constructor<RawBitmap>())
412
+ .define_constructor(Constructor<RawBitmap, uint, uint>())
405
413
  .define_method("rgb_value_at", &RawBitmap::rgb_value_at)
406
414
  .define_method("w", &RawBitmap::w)
407
415
  .define_method("h", &RawBitmap::h)
@@ -430,6 +438,44 @@ void Init_cpp_polygon_finder() {
430
438
  .define_method("error", &FastPngBitmap::error)
431
439
  .define_method("print", &FastPngBitmap::print);
432
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
+
433
479
  Data_Type<Matcher> rb_cMatcher =
434
480
  define_class<Matcher>("CPPMatcher")
435
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