bitmap-plus-plus 1.0.0
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 +7 -0
- data/CHANGELOG.md +40 -0
- data/LICENSE +25 -0
- data/README.md +75 -0
- data/examples/colormaps.rb +1857 -0
- data/examples/draw_primitives.rb +53 -0
- data/examples/julia.rb +68 -0
- data/examples/mandelbrot.rb +68 -0
- data/examples/random_colors.rb +37 -0
- data/examples/transformations.rb +84 -0
- data/ext/BitmapPlusPlus-rb.cpp +420 -0
- data/ext/BitmapPlusPlus-rb.hpp +11 -0
- data/ext/BitmapPlusPlus.hpp +659 -0
- data/ext/CMakeLists.txt +170 -0
- data/ext/CMakePresets.json +209 -0
- data/lib/bitmap-plus-plus/version.rb +3 -0
- data/lib/bitmap-plus-plus.rb +3 -0
- data/sig/Bmp/Bitmap.rbs +39 -0
- data/sig/Bmp/BitmapHeader.rbs +22 -0
- data/sig/Bmp/Exception.rbs +5 -0
- data/sig/Bmp/Pixel.rbs +16 -0
- data/sig/Rice/Arg.rbs +6 -0
- data/sig/Rice/Buffer/342/211/272Rice/352/236/211/352/236/211detail/352/236/211/352/236/211ParameterAbstract/342/210/227/342/211/273.rbs +15 -0
- data/sig/Rice/Buffer/342/211/272char/342/211/273.rbs +16 -0
- data/sig/Rice/ModuleRegistry.rbs +5 -0
- data/sig/Rice/Native.rbs +9 -0
- data/sig/Rice/NativeKind.rbs +20 -0
- data/sig/Rice/NativeRegistry.rbs +5 -0
- data/sig/Rice/Parameter.rbs +7 -0
- data/sig/Rice/Pointer/342/211/272Rice/352/236/211/352/236/211detail/352/236/211/352/236/211ParameterAbstract/342/210/227/342/211/273.rbs +5 -0
- data/sig/Rice/Pointer/342/211/272char/342/211/273.rbs +5 -0
- data/sig/Rice/Reference/342/211/272char/342/211/273.rbs +6 -0
- data/sig/Rice/Reference/342/211/272int/342/211/273.rbs +6 -0
- data/sig/Rice/Reference/342/211/272unsigned/302/240Int64/342/211/273.rbs +6 -0
- data/sig/Rice/Registries.rbs +8 -0
- data/sig/Rice/TypeRegistry.rbs +5 -0
- data/sig/Std/Exception.rbs +6 -0
- data/sig/Std/Filesystem/Path.rbs +6 -0
- data/sig/Std/RuntimeError.rbs +6 -0
- data/sig/Std/Vector/342/211/272Rice/352/236/211/352/236/211detail/352/236/211/352/236/211ParameterAbstract/302/240const/342/210/227/342/211/273.rbs +33 -0
- metadata +116 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example: Drawing Primitives
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates the basic drawing capabilities of BitmapPlusPlus:
|
|
7
|
+
# - Lines
|
|
8
|
+
# - Rectangles (filled and outline)
|
|
9
|
+
# - Triangles (filled and outline)
|
|
10
|
+
# - Circles (filled and outline)
|
|
11
|
+
|
|
12
|
+
require "bitmap-plus-plus"
|
|
13
|
+
|
|
14
|
+
def draw_primitives
|
|
15
|
+
# Create a custom background color from hex value
|
|
16
|
+
background = Bmp::Pixel.new(0x25292e)
|
|
17
|
+
|
|
18
|
+
# Create a 512x240 bitmap
|
|
19
|
+
image = Bmp::Bitmap.new(512, 240)
|
|
20
|
+
image.clear(background)
|
|
21
|
+
|
|
22
|
+
# Draw a yellow line from position (250, 50) to position (500, 50)
|
|
23
|
+
image.draw_line(250, 50, 500, 50, Bmp::Yellow)
|
|
24
|
+
|
|
25
|
+
# Draw a red rectangle outline at position (10, 10) with size 100x100
|
|
26
|
+
image.draw_rect(10, 10, 100, 100, Bmp::Red)
|
|
27
|
+
|
|
28
|
+
# Draw a white filled rectangle at position (120, 10) with size 100x100
|
|
29
|
+
image.fill_rect(120, 10, 100, 100, Bmp::White)
|
|
30
|
+
|
|
31
|
+
# Draw a cyan triangle outline
|
|
32
|
+
image.draw_triangle(60, 120, 10, 220, 120, 220, Bmp::Cyan)
|
|
33
|
+
|
|
34
|
+
# Draw a magenta filled triangle
|
|
35
|
+
image.fill_triangle(180, 120, 130, 220, 245, 220, Bmp::Magenta)
|
|
36
|
+
|
|
37
|
+
# Draw a gray circle outline at center (300, 170) with radius 50
|
|
38
|
+
image.draw_circle(300, 170, 50, Bmp::Gray)
|
|
39
|
+
|
|
40
|
+
# Draw a lime filled circle at center (420, 170) with radius 50
|
|
41
|
+
image.fill_circle(420, 170, 50, Bmp::Lime)
|
|
42
|
+
|
|
43
|
+
# Save the bitmap
|
|
44
|
+
output_path = File.join(__dir__, "output", "primitives.bmp")
|
|
45
|
+
Dir.mkdir(File.dirname(output_path)) unless Dir.exist?(File.dirname(output_path))
|
|
46
|
+
std_path = Std::Filesystem::Path.new(output_path)
|
|
47
|
+
image.save(std_path)
|
|
48
|
+
|
|
49
|
+
puts "Saved: #{output_path}"
|
|
50
|
+
puts "Image size: #{image.width}x#{image.height}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
draw_primitives if __FILE__ == $PROGRAM_NAME
|
data/examples/julia.rb
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example: Julia Set Fractal
|
|
5
|
+
#
|
|
6
|
+
# This example generates a Julia set fractal image using BitmapPlusPlus.
|
|
7
|
+
# The Julia set is similar to the Mandelbrot set but uses a fixed complex
|
|
8
|
+
# constant instead of varying it per pixel.
|
|
9
|
+
|
|
10
|
+
require "bitmap-plus-plus"
|
|
11
|
+
require_relative "colormaps"
|
|
12
|
+
|
|
13
|
+
MAX_ITERATIONS = 300
|
|
14
|
+
|
|
15
|
+
# Julia set constant - try different values for different patterns!
|
|
16
|
+
# Some interesting values:
|
|
17
|
+
# -0.70, 0.27015 (classic)
|
|
18
|
+
# -0.8, 0.156 (dendrite)
|
|
19
|
+
# -0.4, 0.6 (rabbit)
|
|
20
|
+
# 0.285, 0.01 (siegel disk)
|
|
21
|
+
CR = -0.70000
|
|
22
|
+
CI = 0.27015
|
|
23
|
+
|
|
24
|
+
def julia
|
|
25
|
+
width = 640
|
|
26
|
+
height = 480
|
|
27
|
+
image = Bmp::Bitmap.new(width, height)
|
|
28
|
+
|
|
29
|
+
puts "Generating Julia set (#{width}x#{height})..."
|
|
30
|
+
puts "Constant: #{CR} + #{CI}i"
|
|
31
|
+
|
|
32
|
+
height.times do |y|
|
|
33
|
+
puts "Row: #{y}/#{height}" if (y % 50).zero?
|
|
34
|
+
|
|
35
|
+
width.times do |x|
|
|
36
|
+
# Map pixel coordinates to complex plane
|
|
37
|
+
nextr = 1.5 * (2.0 * x / width - 1.0)
|
|
38
|
+
nexti = (2.0 * y / height - 1.0)
|
|
39
|
+
|
|
40
|
+
MAX_ITERATIONS.times do |i|
|
|
41
|
+
prevr = nextr
|
|
42
|
+
previ = nexti
|
|
43
|
+
|
|
44
|
+
# z = z^2 + c (where c is fixed)
|
|
45
|
+
nextr = prevr * prevr - previ * previ + CR
|
|
46
|
+
nexti = 2 * prevr * previ + CI
|
|
47
|
+
|
|
48
|
+
# Check if escaped
|
|
49
|
+
if (nextr * nextr + nexti * nexti) > 4
|
|
50
|
+
index = ((1000.0 * i) / MAX_ITERATIONS).floor
|
|
51
|
+
pixel = HSV_COLORMAP[index.clamp(0, HSV_COLORMAP.length - 1)]
|
|
52
|
+
image.set(x, y, pixel)
|
|
53
|
+
break
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Save the bitmap
|
|
60
|
+
output_path = File.join(__dir__, "output", "julia.bmp")
|
|
61
|
+
Dir.mkdir(File.dirname(output_path)) unless Dir.exist?(File.dirname(output_path))
|
|
62
|
+
std_path = Std::Filesystem::Path.new(output_path)
|
|
63
|
+
image.save(std_path)
|
|
64
|
+
|
|
65
|
+
puts "Saved: #{output_path}"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
julia if __FILE__ == $PROGRAM_NAME
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example: Mandelbrot Set Fractal
|
|
5
|
+
#
|
|
6
|
+
# This example generates a Mandelbrot set fractal image using BitmapPlusPlus.
|
|
7
|
+
# It demonstrates:
|
|
8
|
+
# - Creating bitmaps with specific dimensions
|
|
9
|
+
# - Setting individual pixels with calculated colors
|
|
10
|
+
# - Using color maps for smooth coloring
|
|
11
|
+
|
|
12
|
+
require "bitmap-plus-plus"
|
|
13
|
+
require_relative "colormaps"
|
|
14
|
+
|
|
15
|
+
MAX_ITERATIONS = 500
|
|
16
|
+
|
|
17
|
+
def mandelbrot
|
|
18
|
+
width = 640
|
|
19
|
+
height = 480
|
|
20
|
+
image = Bmp::Bitmap.new(width, height)
|
|
21
|
+
|
|
22
|
+
puts "Generating Mandelbrot set (#{width}x#{height})..."
|
|
23
|
+
|
|
24
|
+
height.times do |y|
|
|
25
|
+
puts "Row: #{y}/#{height}" if (y % 50).zero?
|
|
26
|
+
|
|
27
|
+
width.times do |x|
|
|
28
|
+
# Map pixel coordinates to complex plane
|
|
29
|
+
cr = 1.5 * (2.0 * x / width - 1.0) - 0.5
|
|
30
|
+
ci = (2.0 * y / height - 1.0)
|
|
31
|
+
|
|
32
|
+
nextr = nexti = 0.0
|
|
33
|
+
|
|
34
|
+
MAX_ITERATIONS.times do |i|
|
|
35
|
+
prevr = nextr
|
|
36
|
+
previ = nexti
|
|
37
|
+
|
|
38
|
+
# z = z^2 + c
|
|
39
|
+
nextr = prevr * prevr - previ * previ + cr
|
|
40
|
+
nexti = 2 * prevr * previ + ci
|
|
41
|
+
|
|
42
|
+
# Check if escaped (|z| > 2)
|
|
43
|
+
if (nextr * nextr + nexti * nexti) > 4
|
|
44
|
+
z = Math.sqrt(nextr * nextr + nexti * nexti)
|
|
45
|
+
|
|
46
|
+
# Smooth coloring using continuous iteration count
|
|
47
|
+
# https://en.wikipedia.org/wiki/Mandelbrot_set#Continuous_.28smooth.29_coloring
|
|
48
|
+
smooth_i = i + 1 - Math.log2(Math.log2(z))
|
|
49
|
+
index = (1000.0 * Math.log2(1.75 + smooth_i) / Math.log2(MAX_ITERATIONS)).to_i
|
|
50
|
+
|
|
51
|
+
pixel = JET_COLORMAP[index.clamp(0, JET_COLORMAP.length - 1)]
|
|
52
|
+
image.set(x, y, pixel)
|
|
53
|
+
break
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Save the bitmap
|
|
60
|
+
output_path = File.join(__dir__, "output", "mandelbrot.bmp")
|
|
61
|
+
Dir.mkdir(File.dirname(output_path)) unless Dir.exist?(File.dirname(output_path))
|
|
62
|
+
std_path = Std::Filesystem::Path.new(output_path)
|
|
63
|
+
image.save(std_path)
|
|
64
|
+
|
|
65
|
+
puts "Saved: #{output_path}"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
mandelbrot if __FILE__ == $PROGRAM_NAME
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example: Random Colors
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates:
|
|
7
|
+
# - Using the each iterator to modify pixels
|
|
8
|
+
# - Creating pixels with random RGB values
|
|
9
|
+
# - The assign method for copying pixel values
|
|
10
|
+
|
|
11
|
+
require "bitmap-plus-plus"
|
|
12
|
+
|
|
13
|
+
def random_pixel
|
|
14
|
+
Bmp::Pixel.new(rand(256), rand(256), rand(256))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def random_colors
|
|
18
|
+
puts "Generating random color image..."
|
|
19
|
+
|
|
20
|
+
image = Bmp::Bitmap.new(512, 512)
|
|
21
|
+
|
|
22
|
+
# Use the each iterator to set each pixel to a random color
|
|
23
|
+
image.each do |pixel|
|
|
24
|
+
pixel.assign(random_pixel)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Save the bitmap
|
|
28
|
+
output_path = File.join(__dir__, "output", "random_colors.bmp")
|
|
29
|
+
Dir.mkdir(File.dirname(output_path)) unless Dir.exist?(File.dirname(output_path))
|
|
30
|
+
std_path = Std::Filesystem::Path.new(output_path)
|
|
31
|
+
image.save(std_path)
|
|
32
|
+
|
|
33
|
+
puts "Saved: #{output_path}"
|
|
34
|
+
puts "Total pixels: #{image.size}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
random_colors if __FILE__ == $PROGRAM_NAME
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example: Image Transformations
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates the transformation methods:
|
|
7
|
+
# - flip_v (vertical flip)
|
|
8
|
+
# - flip_h (horizontal flip)
|
|
9
|
+
# - rotate_90_left
|
|
10
|
+
# - rotate_90_right
|
|
11
|
+
#
|
|
12
|
+
# All transformations return a new Bitmap (they are immutable operations).
|
|
13
|
+
|
|
14
|
+
require "bitmap-plus-plus"
|
|
15
|
+
|
|
16
|
+
def create_test_image
|
|
17
|
+
# Create an asymmetric image to clearly show transformations
|
|
18
|
+
image = Bmp::Bitmap.new(200, 150)
|
|
19
|
+
image.clear(Bmp::White)
|
|
20
|
+
|
|
21
|
+
# Draw an "F" shape to make orientation obvious
|
|
22
|
+
image.fill_rect(20, 20, 20, 110, Bmp::Blue) # Vertical bar
|
|
23
|
+
image.fill_rect(40, 20, 80, 20, Bmp::Blue) # Top horizontal
|
|
24
|
+
image.fill_rect(40, 60, 50, 20, Bmp::Blue) # Middle horizontal
|
|
25
|
+
|
|
26
|
+
# Add a red circle in top-right corner
|
|
27
|
+
image.fill_circle(160, 40, 25, Bmp::Red)
|
|
28
|
+
|
|
29
|
+
image
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def transformations
|
|
33
|
+
puts "Generating transformation examples..."
|
|
34
|
+
|
|
35
|
+
output_dir = File.join(__dir__, "output")
|
|
36
|
+
Dir.mkdir(output_dir) unless Dir.exist?(output_dir)
|
|
37
|
+
|
|
38
|
+
# Create original image
|
|
39
|
+
original = create_test_image
|
|
40
|
+
path = File.join(output_dir, "transform_original.bmp")
|
|
41
|
+
std_path = Std::Filesystem::Path.new(path)
|
|
42
|
+
original.save(std_path)
|
|
43
|
+
|
|
44
|
+
puts "Saved: transform_original.bmp (#{original.width}x#{original.height})"
|
|
45
|
+
|
|
46
|
+
# Vertical flip
|
|
47
|
+
flipped_v = original.flip_v
|
|
48
|
+
path = File.join(output_dir, "transform_flip_v.bmp")
|
|
49
|
+
std_path = Std::Filesystem::Path.new(path)
|
|
50
|
+
flipped_v.save(std_path)
|
|
51
|
+
puts "Saved: transform_flip_v.bmp"
|
|
52
|
+
|
|
53
|
+
# Horizontal flip
|
|
54
|
+
flipped_h = original.flip_h
|
|
55
|
+
path = File.join(output_dir, "transform_flip_h.bmp")
|
|
56
|
+
std_path = Std::Filesystem::Path.new(path)
|
|
57
|
+
flipped_h.save(std_path)
|
|
58
|
+
puts "Saved: transform_flip_h.bmp"
|
|
59
|
+
|
|
60
|
+
# Rotate 90 degrees left (counter-clockwise)
|
|
61
|
+
rotated_left = original.rotate_90_left
|
|
62
|
+
path = File.join(output_dir, "transform_rotate_left.bmp")
|
|
63
|
+
std_path = Std::Filesystem::Path.new(path)
|
|
64
|
+
rotated_left.save(std_path)
|
|
65
|
+
puts "Saved: transform_rotate_left.bmp (#{rotated_left.width}x#{rotated_left.height})"
|
|
66
|
+
|
|
67
|
+
# Rotate 90 degrees right (clockwise)
|
|
68
|
+
rotated_right = original.rotate_90_right
|
|
69
|
+
path = File.join(output_dir, "transform_rotate_right.bmp")
|
|
70
|
+
std_path = Std::Filesystem::Path.new(path)
|
|
71
|
+
rotated_right.save(std_path)
|
|
72
|
+
puts "Saved: transform_rotate_right.bmp (#{rotated_right.width}x#{rotated_right.height})"
|
|
73
|
+
|
|
74
|
+
# Chain transformations: flip both ways = 180 degree rotation
|
|
75
|
+
rotated_180 = original.flip_v.flip_h
|
|
76
|
+
path = File.join(output_dir, "transform_rotate_180.bmp")
|
|
77
|
+
std_path = Std::Filesystem::Path.new(path)
|
|
78
|
+
rotated_180.save(std_path)
|
|
79
|
+
puts "Saved: transform_rotate_180.bmp (flip_v + flip_h)"
|
|
80
|
+
|
|
81
|
+
puts "\nAll transformations complete!"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
transformations if __FILE__ == $PROGRAM_NAME
|