Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rubocop linter and fix styles #4

Merged
merged 4 commits into from
Dec 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ jobs:

- name: Test
run: bundle exec rspec

- name: Run Rubocop linter
run: bundle exec rubocop
37 changes: 37 additions & 0 deletions .rubocop.yml
Copy link
Contributor Author

@Lyams Lyams Dec 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In further refactoring, I propose enabling some of the currently disabled cops.

Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# .rubocop.yml

AllCops:
NewCops: enable

Naming/MethodParameterName:
Enabled: false

Metrics/ParameterLists:
Enabled: false

Metrics/PerceivedComplexity:
Enabled: false

Metrics/AbcSize:
Enabled: false

Style/OptionalBooleanParameter:
Enabled: false

Metrics/BlockLength:
Enabled: false

Metrics/ClassLength:
Enabled: false

Metrics/CyclomaticComplexity:
Enabled: false

Metrics/MethodLength:
Enabled: false

Naming/VariableNumber:
Enabled: false

Gemspec/DevelopmentDependencies:
EnforcedStyle: gemspec
12 changes: 5 additions & 7 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/butteff/is_dark_ruby_gem.git" }
# frozen_string_literal: true

gem "rmagick"
source 'https://rubygems.org'
git_source(:github) { |_repo| 'https://github.com/butteff/is_dark_ruby_gem.git' }

group :development, :test do
gem "rspec"
gem "minitest"
end
# Specify your gem's dependencies in scenic.gemspec
gemspec
10 changes: 6 additions & 4 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
require "rake/testtask"
# frozen_string_literal: true

require 'rake/testtask'

Rake::TestTask.new do |t|
t.libs << "test"
t.libs << 'test'
end

desc "Run tests"
task default: :test
desc 'Run tests'
task default: :test
36 changes: 23 additions & 13 deletions is_dark.gemspec
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
# frozen_string_literal: true

Gem::Specification.new do |s|
s.name = "is_dark"
s.version = "0.1.7"
s.summary = "Detects a dark background under an area or by a color code"
s.description = "Detects a dark color based on luminance W3 standarts ( https://www.w3.org/TR/WCAG20/#relativeluminancedef ). \n\n It has these options: \n * is a hex color dark \n * is an Imagick pixel dark \n * is an Imagick pixel from a blob dark \n * is an area in a blob over a dark background (uses Imagick for it too). \n\n An example practical aspect: it can be useful to understand will a black colored text be visible or not over an area."
s.authors = ["Sergei Illarionov", "Liamshin Ilia"]
s.email = "butteff.ru@gmail.com"
s.files = ["lib/is_dark.rb"]
s.homepage = "https://butteff.ru/en/"
s.post_install_message = "You can find docs about is_dark gem on https://butteff.ru/en/extensions or on https://github.com/butteff/is_dark_ruby_gem"
s.license = "MIT"
s.metadata = { "source_code_uri" => "https://github.com/butteff/is_dark_ruby_gem" }
s.name = 'is_dark'
s.version = '0.1.7'
s.summary = 'Detects a dark background under an area or by a color code'
s.description = 'Detects a dark color based on luminance W3 standarts ' \
"( https://www.w3.org/TR/WCAG20/#relativeluminancedef ). \n\n " \
"It has these options: \n * is a hex color dark \n * is an Imagick " \
"pixel dark \n * is an Imagick pixel from a blob dark \n * is an area " \
"in a blob over a dark background (uses Imagick for it too). \n\n " \
'An example practical aspect: it can be useful to understand will ' \
'a black colored text be visible or not over an area.'
s.authors = ['Sergei Illarionov', 'Liamshin Ilia']
s.email = 'butteff.ru@gmail.com'
s.files = ['lib/is_dark.rb']
s.homepage = 'https://butteff.ru/en/'
s.post_install_message = 'You can find docs about is_dark gem on https://butteff.ru/en/extensions or on https://github.com/butteff/is_dark_ruby_gem'
s.license = 'MIT'
s.metadata = { 'source_code_uri' => 'https://github.com/butteff/is_dark_ruby_gem',
'rubygems_mfa_required' => 'true' }
s.required_ruby_version = '>= 2.7.0'
s.add_dependency 'rmagick', '~> 5.2'
s.add_development_dependency 'rspec', "~> 3.13"
s.add_development_dependency 'minitest', "~> 5.16"
s.add_development_dependency 'minitest', '~> 5.16'
s.add_development_dependency 'rspec', '~> 3.13'
s.add_development_dependency 'rubocop', '~> 1.69'
end
34 changes: 24 additions & 10 deletions lib/is_dark.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
# frozen_string_literal: true

require 'rmagick'

# The `Is Dark` class is designed to determine whether a given color or pixel is dark based on luminance
# standards defined by the W3C. It utilizes the `rmagick` library to handle image processing tasks.
# The class includes methods to analyze colors from hexadecimal values, individual pixels,
# and areas within images blobs. It also supports debugging features to visualize the analysis process.
#
# Key features include:
# - **Color Analysis**: Determines if a color is dark based on its luminance.
# - **- **Pixel Analysis**: Analysis individual pixels from an image.
# - **Area Analysis**: Evaluates the darkness of a specified area within an image blob.
# - **Debugging**: Provides options to enable debugging information and visualize the analysis on a PDF file.
class IsDark
BLUE_LUMINANCE_COEFFICIENT = 0.0722
GREEN_LUMINANCE_COEFFICIENT = 0.7152
Expand All @@ -27,15 +38,15 @@ class IsDark
def self.color(hex)
@r, @g, @b = hex.match(/^#(..)(..)(..)$/).captures.map(&:hex)
@colorset = MAXIMUM_COLOR_DEPTH
is_dark
dark?
end

def self.magick_pixel(pix, x = nil, y = nil, set_not_detected_light = true)
@r = pix.red.to_f
@g = pix.green.to_f
@b = pix.blue.to_f
@colorset = MAX_COLOR_VALUE
is_dark(x, y, set_not_detected_light)
dark?(x, y, set_not_detected_light)
end

def self.magick_pixel_from_blob(x, y, blob, _set_not_detected_light = true)
Expand All @@ -45,14 +56,16 @@ def self.magick_pixel_from_blob(x, y, blob, _set_not_detected_light = true)
end

# (x, y) is the left corner of an element over a blob, height and width is the element's size
def self.magick_area_from_blob(x, y, blob, height, width, percent = 80, range = (0..10), set_not_detected_light = true)
def self.magick_area_from_blob(
x, y, blob, height, width, percent = 80, range = (0..10), set_not_detected_light = true
)
@set_not_detected_light = set_not_detected_light
image = Magick::Image.read(blob).first
dark = false
dots = []
range.each do |xx|
range.each do |yy|
dots << { 'x': (x + width * xx / 10).to_i, 'y': (y + height * yy / 10).to_i }
dots << { x: (x + (width * xx / 10)).to_i, y: (y + (height * yy / 10)).to_i }
end
end

Expand Down Expand Up @@ -106,11 +119,12 @@ def self.draw_debug_files(image, x, y, oldx, oldy)
end

# detects a dark color based on luminance W3 standarts ( https://www.w3.org/TR/WCAG20/#relativeluminancedef )
def self.is_dark(x = nil, y = nil, set_not_detected_light = true)
def self.dark?(x = nil, y = nil, set_not_detected_light = true)
dark = false
inverted = false
pixel = [@r.to_f, @g.to_f, @b.to_f]
return true if pixel == [0.00, 0.00, 0.00] #hardcoded exception
return true if pixel == [0.00, 0.00, 0.00] # hardcoded exception

# probably not detected pixel color by Imagick, will be considered as "white" if "set_not_detected_light = true"
if set_not_detected_light && pixel[0] == 0.0 && pixel[1] == 0.0 && pixel[2] == 0.0
pixel = [MAXIMUM_COLOR_DEPTH, MAXIMUM_COLOR_DEPTH, MAXIMUM_COLOR_DEPTH]
Expand All @@ -126,12 +140,12 @@ def self.is_dark(x = nil, y = nil, set_not_detected_light = true)
end
calculated << color
end
l = RED_LUMINANCE_COEFFICIENT * calculated[0] +
GREEN_LUMINANCE_COEFFICIENT * calculated[1] +
BLUE_LUMINANCE_COEFFICIENT * calculated[2]
l = (RED_LUMINANCE_COEFFICIENT * calculated[0]) +
(GREEN_LUMINANCE_COEFFICIENT * calculated[1]) +
(BLUE_LUMINANCE_COEFFICIENT * calculated[2])
dark = true if l <= LUMINANCE_THRESHOLD
if @with_debug
debug = { 'X': x, 'Y': y, 'R': @r, 'G': @g, 'B': @b, 'luminance value': l, 'is_dark': dark,
debug = { X: x, Y: y, R: @r, G: @g, B: @b, 'luminance value': l, dark?: dark,
'inverted to white': inverted }
p debug
end
Expand Down
119 changes: 64 additions & 55 deletions spec/lib/is_dark_spec.rb
Original file line number Diff line number Diff line change
@@ -1,125 +1,134 @@
require "rmagick"
# frozen_string_literal: true

require 'rmagick'
require 'spec_helper'
require_relative '../../lib/is_dark'

TEST_FILE_PATH = 'color_card.pdf'

describe IsDark do

describe ".color" do
context "dark colors tests" do
it "this color #000000 is dark, returns true" do
describe '.color' do
context 'dark colors tests' do
it 'this color #000000 is dark, returns true' do
expect(IsDark.color('#000000')).to eq(true)
end

it "this color #111111 is dark, returns true" do
it 'this color #111111 is dark, returns true' do
expect(IsDark.color('#111111')).to eq(true)
end

it "this color #102694 is dark, returns true" do
it 'this color #102694 is dark, returns true' do
expect(IsDark.color('#102694')).to eq(true)
end

it "this color #ff2e17 is dark, returns true" do
it 'this color #ff2e17 is dark, returns true' do
expect(IsDark.color('#800f03')).to eq(true)
end
end

context "not dark colors tests" do
it "this color is not dark, returns false" do
context 'not dark colors tests' do
it 'this color is not dark, returns false' do
expect(IsDark.color('#444444')).to eq(false)
end

it "this color is not dark, returns false" do
it 'this color is not dark, returns false' do
expect(IsDark.color('#888888')).to eq(false)
end

it "this color is not dark, returns false" do
it 'this color is not dark, returns false' do
expect(IsDark.color('#ffffff')).to eq(false)
end

it "this color is not dark, returns false" do
it 'this color is not dark, returns false' do
expect(IsDark.color('#fff6b2')).to eq(false)
end
end
end

describe ".magick_pixel_from_blob" do
context "test dark pixel" do
it "this pixel is dark, returns true" do
describe '.magick_pixel_from_blob' do
context 'test dark pixel' do
it 'this pixel is dark, returns true' do
x = 120
y = 120
expect(IsDark.magick_pixel_from_blob(x, y, TEST_FILE_PATH)).to eq(true)
end
end

context "test not dark pixel" do
it "this pixel is not dark, returns false" do
context 'test not dark pixel' do
it 'this pixel is not dark, returns false' do
x = 720
y = 120
expect(IsDark.magick_pixel_from_blob(x, y, TEST_FILE_PATH)).to eq(false)
end
end
end

describe ".magick_pixel" do
context "test dark pixel" do
it "this pixel is dark, returns true" do
describe '.magick_pixel' do
context 'test dark pixel' do
it 'this pixel is dark, returns true' do
image = Magick::Image.read(TEST_FILE_PATH).first
pix = image.pixel_color(80, 320)
expect(IsDark.magick_pixel(pix)).to eq(true)
end
end

context "test not dark pixel" do
it "this pixel is not dark, returns false" do
context 'test not dark pixel' do
it 'this pixel is not dark, returns false' do
image = Magick::Image.read(TEST_FILE_PATH).first
pix = image.pixel_color(720, 120)
expect(IsDark.magick_pixel(pix)).to eq(false)
end
end
end

describe ".magick_area_from_blob" do
context "test dark area" do
it "this area is dark, returns true" do
x = 120 #coordinate of a left corner of the area's rectangle X
y = 120 #coordinate of a left corner of the area's rectangle Y
cf_height = 64 #height of the area's rectangle
cf_width = 128 #height of the area's rectangle
percent = 70 #percent of detected dark pixels to invert
matrix = (1..10) #matrix of dots. Range of matrix to build dots 1..10 - means 10x10
with_not_detected = false #Sometimes Imagick can't detect a pixel or it has no color, so it detects it as (RGB: 0,0,0), the gem has an option to consider pixels like this as "white", but if you need to disable this option add true or false
expect(IsDark.magick_area_from_blob(x, y, TEST_FILE_PATH, cf_height, cf_width, percent, matrix, with_not_detected)).to eq(false)
describe '.magick_area_from_blob' do
context 'test dark area' do
it 'this area is dark, returns true' do
x = 120 # coordinate of a left corner of the area's rectangle X
y = 120 # coordinate of a left corner of the area's rectangle Y
cf_height = 64 # height of the area's rectangle
cf_width = 128 # height of the area's rectangle
percent = 70 # percent of detected dark pixels to invert
matrix = (1..10) # matrix of dots. Range of matrix to build dots 1..10 - means 10x10
# Sometimes Imagick can't detect a pixel or it has no color, so it detects it as (RGB: 0,0,0), the gem has an
# option to consider pixels like this as "white", but if you need to disable this option add true or false.
with_not_detected = false
expect(IsDark.magick_area_from_blob(x, y, TEST_FILE_PATH, cf_height, cf_width, percent, matrix,
with_not_detected)).to eq(false)
end
end

context "test bright area" do
it "this area is not dark, returns false" do
x = 720 #coordinate of a left corner of the area's rectangle X
y = 120 #coordinate of a left corner of the area's rectangle Y
cf_height = 64 #height of the area's rectangle
cf_width = 128 #height of the area's rectangle
percent = 70 #percent of detected dark pixels to invert
matrix = (1..10) #matrix of dots. Range of matrix to build dots 1..10 - means 10x10
with_not_detected = false #Sometimes Imagick can't detect a pixel or it has no color, so it detects it as (RGB: 0,0,0), the gem has an option to consider pixels like this as "white", but if you need to disable this option add true or false
expect(IsDark.magick_area_from_blob(x, y, TEST_FILE_PATH, cf_height, cf_width, percent, matrix, with_not_detected)).to eq(false)
context 'test bright area' do
it 'this area is not dark, returns false' do
x = 720 # coordinate of a left corner of the area's rectangle X
y = 120 # coordinate of a left corner of the area's rectangle Y
cf_height = 64 # height of the area's rectangle
cf_width = 128 # height of the area's rectangle
percent = 70 # percent of detected dark pixels to invert
matrix = (1..10) # matrix of dots. Range of matrix to build dots 1..10 - means 10x10
# Sometimes Imagick can't detect a pixel or it has no color, so it detects it as (RGB: 0,0,0), the gem has
# an option to consider pixels like this as "white", but if you need to disable this option add true or false.
with_not_detected = false
expect(IsDark.magick_area_from_blob(x, y, TEST_FILE_PATH, cf_height, cf_width, percent, matrix,
with_not_detected)).to eq(false)
end
end

context "test debug output" do
it "test area with logs and debug file" do
x = 120 #coordinate of a left corner of the area's rectangle X
y = 120 #coordinate of a left corner of the area's rectangle Y
cf_height = 64 #height of the area's rectangle
cf_width = 128 #height of the area's rectangle
percent = 70 #percent of detected dark pixels to invert
matrix = (1..10) #matrix of dots. Range of matrix to build dots 1..10 - means 10x10
with_not_detected = false #Sometimes Imagick can't detect a pixel or it has no color, so it detects it as (RGB: 0,0,0), the gem has an option to consider pixels like this as "white", but if you need to disable this option add true or false
context 'test debug output' do
it 'test area with logs and debug file' do
x = 120 # coordinate of a left corner of the area's rectangle X
y = 120 # coordinate of a left corner of the area's rectangle Y
cf_height = 64 # height of the area's rectangle
cf_width = 128 # height of the area's rectangle
percent = 70 # percent of detected dark pixels to invert
matrix = (1..10) # matrix of dots. Range of matrix to build dots 1..10 - means 10x10
# Sometimes Imagick can't detect a pixel or it has no color, so it detects it as (RGB: 0,0,0), the gem has
# an option to consider pixels like this as "white", but if you need to disable this option add true or false.
with_not_detected = false
IsDark.set_debug_data(true, './is_dark_debug_output.pdf')
expect(IsDark.magick_area_from_blob(x, y, TEST_FILE_PATH, cf_height, cf_width, percent, matrix, with_not_detected)).to eq(false)
expect(IsDark.magick_area_from_blob(x, y, TEST_FILE_PATH, cf_height, cf_width, percent, matrix,
with_not_detected)).to eq(false)
end
end
end

end
Loading