Skip to content

Commit

Permalink
Add circle option to contrl the circle draw.
Browse files Browse the repository at this point in the history
  • Loading branch information
huacnlee committed Feb 12, 2025
1 parent e6f1c77 commit ed56851
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 62 deletions.
2 changes: 1 addition & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ task default: :spec

def create_captcha(length = 5, difficulty = 5)
require "rucaptcha"
RuCaptchaCore.create(length, difficulty, false, false, "png")
RuCaptchaCore.create(length, difficulty, false, false, false, "png")
end

task :preview do
Expand Down
130 changes: 72 additions & 58 deletions ext/rucaptcha/src/captcha.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,63 +74,6 @@ fn get_next(min: f32, max: u32) -> f32 {
min + rand_num(max as usize - min as usize) as f32
}

fn cyclic_write_character(captcha: &str, image: &mut ImageBuffer<Rgba<u8>, Vec<u8>>, lines: bool) {
let c = (image.width() - 20) / captcha.len() as u32;
let y = image.height() / 3 - 15;

let h = image.height() as f32;

let scale = match captcha.len() {
1..=3 => SCALE_LG,
4..=5 => SCALE_MD,
_ => SCALE_SM,
} as f32;

let colors = get_colors(captcha.len());
let line_colors = get_colors(captcha.len());

let xscale = scale - rand_num((scale * 0.2) as usize) as f32;
let yscale = h - rand_num((h * 0.2) as usize) as f32;

// Draw line, ellipse first as background
(0..captcha.len()).for_each(|i| {
let line_color = line_colors[i];

if lines {
draw_interference_line(1, image, line_color);
}
draw_interference_ellipse(1, image, line_color);
});

let font = match rand_num(2) {
0 => &FONT_0,
1 => &FONT_1,
_ => &FONT_1,
};

// Draw text
for (i, ch) in captcha.chars().enumerate() {
let color = colors[i];

for j in 0..(rand_num(3) + 1) as i32 {
// Draw text again with offset
let offset = j * (rand_num(2) as i32);
imageproc::drawing::draw_text_mut(
image,
color,
10 + offset + (i as u32 * c) as i32,
y as i32,
Scale {
x: xscale + offset as f32,
y: yscale as f32,
},
font,
&ch.to_string(),
);
}
}
}

fn draw_interference_line(num: usize, image: &mut ImageBuffer<Rgba<u8>, Vec<u8>>, color: Rgba<u8>) {
for _ in 0..num {
let width = image.width();
Expand Down Expand Up @@ -185,6 +128,7 @@ pub struct CaptchaBuilder {
complexity: usize,
line: bool,
noise: bool,
circle: bool,
format: image::ImageFormat,
}

Expand All @@ -197,6 +141,7 @@ impl Default for CaptchaBuilder {
complexity: 5,
line: true,
noise: false,
circle: true,
format: image::ImageFormat::Png,
}
}
Expand All @@ -222,6 +167,11 @@ impl CaptchaBuilder {
self
}

pub fn circle(mut self, circle: bool) -> Self {
self.circle = circle;
self
}

pub fn format(mut self, format: &str) -> Self {
self.format = match format {
"png" => image::ImageFormat::Png,
Expand All @@ -238,6 +188,70 @@ impl CaptchaBuilder {
self
}

fn cyclic_write_character(
&self,
captcha: &str,
image: &mut ImageBuffer<Rgba<u8>, Vec<u8>>,
lines: bool,
) {
let c = (image.width() - 20) / captcha.len() as u32;
let y = image.height() / 3 - 15;

let h = image.height() as f32;

let scale = match captcha.len() {
1..=3 => SCALE_LG,
4..=5 => SCALE_MD,
_ => SCALE_SM,
} as f32;

let colors = get_colors(captcha.len());
let line_colors = get_colors(captcha.len());

let xscale = scale - rand_num((scale * 0.2) as usize) as f32;
let yscale = h - rand_num((h * 0.2) as usize) as f32;

// Draw line, ellipse first as background
if self.circle {
(0..captcha.len()).for_each(|i| {
let line_color = line_colors[i];

if lines {
draw_interference_line(1, image, line_color);
}
draw_interference_ellipse(1, image, line_color);
});
}

let font = match rand_num(2) {
0 => &FONT_0,
1 => &FONT_1,
_ => &FONT_1,
};

// Draw text
for (i, ch) in captcha.chars().enumerate() {
let color = colors[i];

for j in 0..(rand_num(3) + 1) as i32 {
// Draw text again with offset
let offset = j * (rand_num(2) as i32);
imageproc::drawing::draw_text_mut(
image,
color,
10 + offset + (i as u32 * c) as i32,
y as i32,
Scale {
x: xscale + offset as f32,
y: yscale as f32,
},
font,
&ch.to_string(),
);
}
}
}

pub fn build(self) -> Captcha {
// Generate an array of captcha characters
let text = rand_captcha(self.length);
Expand All @@ -248,7 +262,7 @@ impl CaptchaBuilder {
});

// Loop to write the verification code string into the background image
cyclic_write_character(&text, &mut buf, self.line);
self.cyclic_write_character(&text, &mut buf, self.line);

if self.noise {
gaussian_noise_mut(
Expand Down
4 changes: 3 additions & 1 deletion ext/rucaptcha/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub fn create(
difficulty: usize,
line: bool,
noise: bool,
circle: bool,
format: String,
) -> (String, Vec<u8>) {
let c = captcha::CaptchaBuilder::new();
Expand All @@ -15,6 +16,7 @@ pub fn create(
.length(len)
.line(line)
.noise(noise)
.circle(circle)
.format(&format)
.build();

Expand All @@ -24,7 +26,7 @@ pub fn create(
#[magnus::init]
fn init() -> Result<(), Error> {
let class = define_class("RuCaptchaCore", magnus::class::object())?;
class.define_singleton_method("create", function!(create, 5))?;
class.define_singleton_method("create", function!(create, 6))?;

Ok(())
}
5 changes: 3 additions & 2 deletions lib/rucaptcha.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ def config
@config.expires_in = 2.minutes
@config.skip_cache_store_check = false
@config.line = true
@config.noise = true
@config.noise = false
@config.circle = true
@config.format = "png"
@config.mount_path = "/rucaptcha"
@config.case_sensitive = false
Expand All @@ -52,7 +53,7 @@ def generate

raise RuCaptcha::Errors::Configuration, "length config error, value must in 3..7" unless length.in?(3..7)

result = RuCaptchaCore.create(length, config.difficulty || 5, config.line, config.noise, config.format)
result = RuCaptchaCore.create(length, config.difficulty || 5, config.line, config.noise, config.circle, config.format)
unless config.case_sensitive
result[0].downcase!
end
Expand Down
2 changes: 2 additions & 0 deletions lib/rucaptcha/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ class Configuration
attr_accessor :line
# Enable or disable noise on captcha image, default: false
attr_accessor :noise
# Enable or disable circle background on captcha image, default: true
attr_accessor :circle
# Image format allow: ['jpeg', 'png', 'webp'], default: 'png'
attr_accessor :format
# skip_cache_store_check, default: false
Expand Down

0 comments on commit ed56851

Please sign in to comment.