gauguin 0.0.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 (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +9 -0
  5. data/Gemfile +4 -0
  6. data/Guardfile +6 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +119 -0
  9. data/Rakefile +13 -0
  10. data/gauguin.gemspec +30 -0
  11. data/lib/gauguin/color.rb +76 -0
  12. data/lib/gauguin/color_space/lab_vector.rb +6 -0
  13. data/lib/gauguin/color_space/rgb_vector.rb +36 -0
  14. data/lib/gauguin/color_space/xyz_vector.rb +33 -0
  15. data/lib/gauguin/color_space.rb +4 -0
  16. data/lib/gauguin/colors_clusterer.rb +63 -0
  17. data/lib/gauguin/colors_limiter.rb +14 -0
  18. data/lib/gauguin/colors_retriever.rb +33 -0
  19. data/lib/gauguin/image.rb +55 -0
  20. data/lib/gauguin/image_recolorer.rb +29 -0
  21. data/lib/gauguin/image_repository.rb +7 -0
  22. data/lib/gauguin/noise_reducer.rb +26 -0
  23. data/lib/gauguin/painting.rb +29 -0
  24. data/lib/gauguin/palette_serializer.rb +22 -0
  25. data/lib/gauguin/version.rb +3 -0
  26. data/lib/gauguin.rb +43 -0
  27. data/spec/integration/painting_spec.rb +79 -0
  28. data/spec/integration/samples_spec.rb +43 -0
  29. data/spec/lib/gauguin/color_space/rgb_vector_spec.rb +15 -0
  30. data/spec/lib/gauguin/color_space/xyz_vector_spec.rb +15 -0
  31. data/spec/lib/gauguin/color_spec.rb +125 -0
  32. data/spec/lib/gauguin/colors_clusterer_spec.rb +158 -0
  33. data/spec/lib/gauguin/colors_limiter_spec.rb +27 -0
  34. data/spec/lib/gauguin/colors_retriever_spec.rb +85 -0
  35. data/spec/lib/gauguin/image_recolorer_spec.rb +94 -0
  36. data/spec/lib/gauguin/image_repository_spec.rb +15 -0
  37. data/spec/lib/gauguin/image_spec.rb +90 -0
  38. data/spec/lib/gauguin/noise_reducer_spec.rb +51 -0
  39. data/spec/lib/gauguin/painting_spec.rb +55 -0
  40. data/spec/lib/gauguin/palette_serializer_spec.rb +24 -0
  41. data/spec/spec_helper.rb +60 -0
  42. data/spec/support/pictures/10_colors.png +0 -0
  43. data/spec/support/pictures/12_colors.png +0 -0
  44. data/spec/support/pictures/gauguin.png +0 -0
  45. data/spec/support/pictures/gray_and_black.png +0 -0
  46. data/spec/support/pictures/not_unique_colors.png +0 -0
  47. data/spec/support/pictures/samples/sample1.png +0 -0
  48. data/spec/support/pictures/samples/sample10.png +0 -0
  49. data/spec/support/pictures/samples/sample11.png +0 -0
  50. data/spec/support/pictures/samples/sample2.png +0 -0
  51. data/spec/support/pictures/samples/sample3.png +0 -0
  52. data/spec/support/pictures/samples/sample4.png +0 -0
  53. data/spec/support/pictures/samples/sample5.png +0 -0
  54. data/spec/support/pictures/samples/sample6.png +0 -0
  55. data/spec/support/pictures/samples/sample7.png +0 -0
  56. data/spec/support/pictures/samples/sample8.png +0 -0
  57. data/spec/support/pictures/samples/sample9.png +0 -0
  58. data/spec/support/pictures/too_many_colors.png +0 -0
  59. data/spec/support/pictures/transparent_background.png +0 -0
  60. data/spec/support/pictures/unique_colors.png +0 -0
  61. metadata +251 -0
@@ -0,0 +1,3 @@
1
+ module Gauguin
2
+ VERSION = "0.0.2"
3
+ end
data/lib/gauguin.rb ADDED
@@ -0,0 +1,43 @@
1
+ require "gauguin/version"
2
+ require "gauguin/color"
3
+ require "gauguin/color_space"
4
+ require "gauguin/colors_retriever"
5
+ require "gauguin/colors_limiter"
6
+ require "gauguin/colors_clusterer"
7
+ require "gauguin/noise_reducer"
8
+ require "gauguin/image_recolorer"
9
+ require "gauguin/painting"
10
+ require "gauguin/image"
11
+ require "gauguin/image_repository"
12
+ require "gauguin/palette_serializer"
13
+
14
+ module Gauguin
15
+ class << self
16
+ attr_accessor :configuration
17
+ end
18
+
19
+ def self.configure
20
+ self.configuration ||= Configuration.new
21
+ yield(configuration) if block_given?
22
+ end
23
+
24
+ class Configuration
25
+ DEFAULT_MAX_COLORS_COUNT = 10
26
+ DEFAULT_COLORS_LIMIT = 10000
27
+ DEFAULT_MIN_PERCENTAGE_SUM = 0.981
28
+ DEFAULT_COLOR_SIMILARITY_THRESHOLD = 25
29
+
30
+ attr_accessor :max_colors_count, :colors_limit,
31
+ :min_percentage_sum, :color_similarity_threshold
32
+
33
+ def initialize
34
+ @max_colors_count = DEFAULT_MAX_COLORS_COUNT
35
+ @colors_limit = DEFAULT_COLORS_LIMIT
36
+ @min_percentage_sum = DEFAULT_MIN_PERCENTAGE_SUM
37
+ @color_similarity_threshold = DEFAULT_COLOR_SIMILARITY_THRESHOLD
38
+ end
39
+ end
40
+ end
41
+
42
+ Gauguin.configure
43
+
@@ -0,0 +1,79 @@
1
+ require 'spec_helper'
2
+
3
+ module Gauguin
4
+ describe Painting do
5
+ let(:path) do
6
+ File.join("spec", "support", "pictures", file_name)
7
+ end
8
+
9
+ let(:gray) { Color.new(204, 204, 204) }
10
+ let(:black) { Color.new(0, 0, 0) }
11
+ let(:white) { Color.new(255, 255, 255) }
12
+
13
+ let(:painting) { Painting.new(path) }
14
+
15
+ describe "#palette" do
16
+ shared_examples_for "retrieves unique colors" do
17
+ it { expect(subject.count).to eq 5 }
18
+ it do
19
+ expect(subject.keys).to include(white)
20
+ end
21
+ end
22
+
23
+ subject { painting.palette }
24
+
25
+ context "unique colors in the picture" do
26
+ let(:file_name) { "unique_colors.png" }
27
+
28
+ it_behaves_like "retrieves unique colors"
29
+ end
30
+
31
+ context "not unique colors in the picture" do
32
+ let(:file_name) { "not_unique_colors.png" }
33
+
34
+ it_behaves_like "retrieves unique colors"
35
+ end
36
+
37
+ context "image has two colors but with different gradients
38
+ so actually 1942 unique colors" do
39
+ let(:file_name) { "gray_and_black.png" }
40
+ let(:values) { subject.values.flatten }
41
+
42
+ it { expect(subject.count).to eq 2 }
43
+ it { expect(values.include?(black)).to be true }
44
+ it { expect(values.include?(gray)).to be true }
45
+ end
46
+
47
+ context "transparent background" do
48
+ let(:file_name) { "transparent_background.png" }
49
+
50
+ it { expect(subject.count).to eq 1 }
51
+ it do
52
+ expect(subject.keys).to eq [Color.new(2, 0, 0)]
53
+ end
54
+ end
55
+
56
+ context "image with 10 colors" do
57
+ let(:file_name) { "10_colors.png" }
58
+
59
+ it { expect(subject.count).to eq 10 }
60
+ end
61
+
62
+ context "image with over than max_colors_count colors" do
63
+ let(:file_name) { "12_colors.png" }
64
+
65
+ it { expect(subject.count).to eq 10 }
66
+
67
+ context "image with over than colors_limit colors" do
68
+ configure(:colors_limit, 9)
69
+ configure(:max_colors_count, 12)
70
+
71
+ it "returns colors_limit colors" do
72
+ expect(subject.count).to eq 9
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ module Gauguin
4
+ describe "samples" do
5
+ def self.picture_path(file_name)
6
+ File.join("spec", "support", "pictures", file_name)
7
+ end
8
+
9
+ let(:painting) { Painting.new(picture_path(file_name)) }
10
+
11
+ def self.paths
12
+ (1..11).map { |i| picture_path(File.join("samples", "sample#{i}.png")) }
13
+ end
14
+
15
+ def self.expected_results
16
+ [
17
+ ["rgb(219, 12, 38)", "rgb(255, 255, 255)"],
18
+ ["rgb(168, 36, 40)", "rgb(255, 255, 255)"],
19
+ ["rgb(0, 0, 0)", "rgb(204, 204, 204)"],
20
+ ["rgb(154, 79, 54)", "rgb(187, 196, 201)", "rgb(236, 112, 48)", "rgb(28, 28, 64)", "rgb(92, 54, 59)"],
21
+ ["rgb(254, 254, 254)", "rgb(255, 195, 13)", "rgb(60, 4, 67)"],
22
+ ["rgb(2, 0, 0)"],
23
+ ["rgb(148, 158, 149)", "rgb(198, 64, 63)"],
24
+ ["rgb(109, 207, 246)", "rgb(237, 28, 36)", "rgb(255, 255, 255)"],
25
+ ["rgb(255, 255, 255)", "rgb(87, 196, 15)"],
26
+ ["rgb(240, 110, 170)", "rgb(255, 255, 255)"],
27
+ ["rgb(0, 165, 19)", "rgb(0, 71, 241)", "rgb(230, 27, 49)", "rgb(249, 166, 0)", "rgb(255, 255, 255)"]
28
+ ]
29
+ end
30
+
31
+ def self.samples
32
+ Hash[paths.zip(expected_results)]
33
+ end
34
+
35
+ samples.each do |sample_path, expected_result|
36
+ it "returns expected result for #{sample_path}" do
37
+ painting = Painting.new(sample_path)
38
+ expect(painting.palette.keys.map(&:to_s).sort).to eq(expected_result.sort)
39
+ end
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ module Gauguin::ColorSpace
4
+ describe RgbVector do
5
+ describe "#to_xyz" do
6
+ let(:red) { RgbVector[255, 0, 0] }
7
+
8
+ it "converts to lab space" do
9
+ expect(red.to_xyz).to eq(
10
+ XyzVector[41.24, 21.26, 1.9300000000000002])
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ module Gauguin::ColorSpace
4
+ describe XyzVector do
5
+ describe "#to_lab" do
6
+ let(:red) { XyzVector[41.24, 21.26, 1.9300000000000002] }
7
+
8
+ it "converts to lab space" do
9
+ expect(red.to_lab).to eq(
10
+ LabVector[53.23288178584245, 80.10930952982204, 67.22006831026425])
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+
3
+ module Gauguin
4
+ describe Color do
5
+ let(:black) { Color.new(0, 0, 0) }
6
+ let(:red) { Color.new(255, 0, 0) }
7
+
8
+ describe "initialize" do
9
+ let(:red) { 1 }
10
+ let(:green) { 2 }
11
+ let(:blue) { 3 }
12
+ let(:percentage) { 0.5 }
13
+
14
+ subject { Color.new(red, green, blue, percentage) }
15
+
16
+ it { expect(subject.red).to eq red }
17
+ it { expect(subject.green).to eq green }
18
+ it { expect(subject.blue).to eq blue }
19
+ it { expect(subject.percentage).to eq percentage }
20
+ end
21
+
22
+ describe "#==" do
23
+ it "returns true for colors with the same key values" do
24
+ expect(black == Color.new(0, 0, 0)).to be true
25
+ end
26
+
27
+ it "returns false if any key value is different" do
28
+ expect(black == Color.new(0, 0, 1)).to be false
29
+ end
30
+
31
+ it "returns false for objects with different classes" do
32
+ expect(black == "black").to be false
33
+ end
34
+ end
35
+
36
+ describe "#similar?" do
37
+ context "similar colors" do
38
+ it { expect(black.similar?(Color.new(0, 0, 1))).to be true }
39
+ end
40
+
41
+ context "different colors" do
42
+ it { expect(black.similar?(red)).to be false }
43
+ end
44
+ end
45
+
46
+ describe '#distance' do
47
+ it 'returns circa 178.36 between black & red' do
48
+ expect(black.distance(red)).to be_within(0.01).of(117.34)
49
+ end
50
+ end
51
+
52
+ describe "#to_lab" do
53
+ let(:red) { 1 }
54
+ let(:green) { 2 }
55
+ let(:blue) { 3 }
56
+
57
+ subject { Color.new(red, green, blue).to_lab }
58
+
59
+ it "returns lab vector" do
60
+ rgb_vector = double
61
+ xyz_vector = double
62
+ expect(ColorSpace::RgbVector).to receive(:[]).with(red, green, blue).and_return(rgb_vector)
63
+ expect(rgb_vector).to receive(:to_xyz).and_return(xyz_vector)
64
+ expect(xyz_vector).to receive(:to_lab)
65
+
66
+ subject
67
+ end
68
+ end
69
+
70
+ describe "#to_s" do
71
+ subject { black.to_s }
72
+
73
+ it { expect(subject).to eq("rgb(0, 0, 0)") }
74
+ end
75
+
76
+ let(:color) { Color.new(1, 2, 3, 0.4, true) }
77
+
78
+ describe "#to_rgb" do
79
+ subject { color.to_rgb }
80
+
81
+ it { expect(subject).to eq([1, 2, 3]) }
82
+ end
83
+
84
+ describe "#to_key" do
85
+ subject { color.to_key }
86
+
87
+ it { expect(subject).to eq([1, 2, 3, true]) }
88
+ end
89
+
90
+ describe "#to_a" do
91
+ subject { color.to_a }
92
+
93
+ it { expect(subject).to eq([1, 2, 3, 0.4, true]) }
94
+ end
95
+
96
+ describe ".from_a" do
97
+ subject { Color.from_a([1, 2, 3, 0.4, true]) }
98
+
99
+ it { expect(subject.red).to eq(1) }
100
+ it { expect(subject.green).to eq(2) }
101
+ it { expect(subject.blue).to eq(3) }
102
+ it { expect(subject.percentage).to eq(0.4) }
103
+ it { expect(subject.transparent).to be true }
104
+ end
105
+
106
+ describe "#transparent?" do
107
+ subject { color.transparent? }
108
+
109
+ it { expect(subject).to be true }
110
+ end
111
+
112
+ describe "#hash" do
113
+ it "can be used as keys in the hash" do
114
+ hash = { Color.new(255, 255, 255) => 777 }
115
+ expect(hash[Color.new(255, 255, 255)]).to eq(777)
116
+ end
117
+ end
118
+
119
+ describe "#inspect" do
120
+ subject { color.inspect }
121
+
122
+ it { expect(subject).to eq("rgb(1, 2, 3)[0.4][transparent]")}
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,158 @@
1
+ require 'spec_helper'
2
+
3
+ module Gauguin
4
+ describe ColorsClusterer do
5
+ let(:black) { Color.new(0, 0, 0, 0.597) }
6
+ let(:white) { Color.new(255, 255, 255, 0.4) }
7
+
8
+ let(:clusterer) { ColorsClusterer.new }
9
+
10
+ describe "call" do
11
+ subject { clusterer.call(colors) }
12
+
13
+ context "colors is empty" do
14
+ let(:colors) { [] }
15
+
16
+ it { expect(subject).to eq({}) }
17
+ end
18
+
19
+ context "colors includes similar colors" do
20
+ let(:pseudo_black) { Color.new(4, 0, 0, 0.001) }
21
+ let(:other_pseudo_black) { Color.new(5, 0, 0, 0.001) }
22
+ let(:another_pseudo_black) { Color.new(6, 0, 0, 0.001) }
23
+
24
+ let(:colors) do
25
+ [black, white, pseudo_black, other_pseudo_black,
26
+ another_pseudo_black]
27
+ end
28
+
29
+ it "make separate groups for them" do
30
+ expect(subject).to eq({
31
+ white => [white],
32
+ black => [black, pseudo_black, other_pseudo_black,
33
+ another_pseudo_black]
34
+ })
35
+ end
36
+
37
+ context do
38
+ let(:white) { Color.new(255, 255, 255, 0.3) }
39
+ let(:transparent_white) do
40
+ Color.new(255, 255, 255, 0.1, Image::Pixel::MAX_TRANSPARENCY)
41
+ end
42
+
43
+ it "make separate groups for fully transparent colors" do
44
+ colors << transparent_white
45
+
46
+ expect(subject).to eq({
47
+ white => [white],
48
+ transparent_white => [transparent_white],
49
+ black => [black, pseudo_black, other_pseudo_black,
50
+ another_pseudo_black]
51
+ })
52
+ end
53
+ end
54
+
55
+ it "updates percentage of leader of each group" do
56
+ subject
57
+ expect(white.percentage).to eq(0.4)
58
+ expect(black.percentage).to eq(0.6)
59
+ end
60
+
61
+ context "there is color with bigger percentage
62
+ than pivot in the group" do
63
+ before do
64
+ black.percentage = 0.001
65
+ other_pseudo_black.percentage = 0.597
66
+ end
67
+
68
+ it "chooses it as pivot" do
69
+ expect(subject).to eq({
70
+ white => [white],
71
+ other_pseudo_black => [black, pseudo_black,
72
+ other_pseudo_black,
73
+ another_pseudo_black]
74
+ })
75
+ end
76
+
77
+ context "pivots are similar" do
78
+ before do
79
+ other_pseudo_black.red = 30
80
+ another_pseudo_black.red = 60
81
+ end
82
+
83
+ it "merge their groups" do
84
+ expect(subject).to eq({
85
+ white => [white],
86
+ other_pseudo_black => [black, pseudo_black,
87
+ other_pseudo_black,
88
+ another_pseudo_black]
89
+ })
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ context "colors includes different colors" do
96
+ let(:colors) do
97
+ [black, white]
98
+ end
99
+
100
+ before do
101
+ expect(white).to receive(:similar?).
102
+ with(black).and_return(false)
103
+ end
104
+
105
+ it "make separate groups for them" do
106
+ expect(subject).to eq({
107
+ black => [black],
108
+ white => [white]
109
+ })
110
+ end
111
+ end
112
+ end
113
+
114
+ describe "#clusters" do
115
+ let(:red) { Color.new(255, 0, 0, 0.1) }
116
+ let(:colors) { [black, red, white] }
117
+
118
+ subject { clusterer.clusters(colors) }
119
+
120
+ configure(:max_colors_count, 2)
121
+
122
+ before do
123
+ expect(clusterer).to receive(:call).and_return({
124
+ black => [black],
125
+ red => [red],
126
+ white => [white]
127
+ })
128
+ end
129
+
130
+ it "returns max_colors_count most common colors" do
131
+ expect(subject).to eq({
132
+ white => [white],
133
+ black => [black]
134
+ })
135
+ end
136
+ end
137
+
138
+ describe "#reversed_clusters" do
139
+ let(:gray) { Color.new(0, 0, 10, 0.4) }
140
+ let(:clusters) do
141
+ {
142
+ white => [white],
143
+ black => [black, gray]
144
+ }
145
+ end
146
+
147
+ subject { clusterer.reversed_clusters(clusters) }
148
+
149
+ it "returns reversed clusters" do
150
+ expect(subject).to eq({
151
+ white => white,
152
+ black => black,
153
+ gray => black
154
+ })
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ module Gauguin
4
+ describe ColorsLimiter do
5
+ describe "#limit" do
6
+ let(:limiter) { ColorsLimiter.new }
7
+ let(:colors) { [black, red, white] }
8
+ let(:white) { Color.new(255, 255, 255, 0.01) }
9
+ let(:red) { Color.new(255, 0, 0, 0.02) }
10
+ let(:black) { Color.new(0, 0, 0, 0.97) }
11
+
12
+ subject { limiter.call(colors) }
13
+
14
+ it "returns all colors" do
15
+ expect(subject).to eq([black, red, white])
16
+ end
17
+
18
+ context "colors count is greater than colors_limit" do
19
+ configure(:colors_limit, 2)
20
+
21
+ it "reduces colors to colors_limit" do
22
+ expect(subject).to eq([black, red])
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+
3
+ module Gauguin
4
+ describe ColorsRetriever do
5
+ let(:retriever) { ColorsRetriever.new(image) }
6
+ let(:image) do
7
+ fake = FakeImage.new
8
+
9
+ fake.magic_black_pixel = magic_black_pixel
10
+ fake.magic_white_pixel = magic_white_pixel
11
+ fake.magic_red_pixel = magic_red_pixel
12
+ fake.magic_red_little_transparent_pixel = magic_red_little_transparent_pixel
13
+
14
+ fake.pixels_repository = {
15
+ magic_white_pixel => FakeImage::Pixel.new(magic_white_pixel),
16
+ magic_red_pixel => FakeImage::Pixel.new(magic_red_pixel),
17
+ magic_black_pixel => FakeImage::Pixel.new(magic_black_pixel),
18
+ magic_red_little_transparent_pixel => FakeImage::Pixel.new(
19
+ magic_red_little_transparent_pixel)
20
+ }
21
+
22
+ fake.color_histogram = {
23
+ magic_white_pixel => 20,
24
+ magic_black_pixel => 30,
25
+ magic_red_pixel => 10
26
+ }
27
+
28
+ fake.rows = 10
29
+ fake.columns = 10
30
+
31
+ fake
32
+ end
33
+
34
+ def magic_pixel(rgb, opacity)
35
+ double(rgb: rgb, opacity: opacity)
36
+ end
37
+
38
+ let(:magic_black_pixel) { magic_pixel([0, 0, 0], 0) }
39
+ let(:magic_white_pixel) { magic_pixel([255, 255, 255], 0) }
40
+ let(:magic_red_pixel) { magic_pixel([255, 0, 0], 0) }
41
+ let(:magic_red_little_transparent_pixel) { magic_pixel([255, 0, 0], 50) }
42
+
43
+ describe "#colors" do
44
+ subject { retriever.colors.sort_by(&:percentage) }
45
+
46
+ it "returns array with colors with percentages" do
47
+ expect(subject).to eq([
48
+ Color.new(255, 0, 0, 0.1),
49
+ Color.new(255, 255, 255, 0.2),
50
+ Color.new(0, 0, 0, 0.3)
51
+ ])
52
+ end
53
+
54
+ context "histogram contains different magic pixels
55
+ for the same color with different opacity" do
56
+ before do
57
+ image.color_histogram[magic_red_little_transparent_pixel] = 40
58
+ end
59
+
60
+ it "sums percentage" do
61
+ expect(subject).to eq([
62
+ Color.new(255, 255, 255, 0.2),
63
+ Color.new(0, 0, 0, 0.3),
64
+ Color.new(255, 0, 0, 0.5)
65
+ ])
66
+ end
67
+
68
+ context "fully transparent colors" do
69
+ let(:magic_red_little_transparent_pixel) do
70
+ magic_pixel([255, 0, 0], Image::Pixel::MAX_TRANSPARENCY)
71
+ end
72
+
73
+ it "should be treated separately" do
74
+ expect(subject).to eq([
75
+ Color.new(255, 0, 0, 0.1),
76
+ Color.new(255, 255, 255, 0.2),
77
+ Color.new(0, 0, 0, 0.3),
78
+ Color.new(255, 0, 0, 0.4, true)
79
+ ])
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+
3
+ module Gauguin
4
+ describe ImageRecolorer do
5
+ describe "#recolor" do
6
+ let(:image) do
7
+ fake = FakeImage.new
8
+
9
+ fake.pixels = pixels
10
+ fake.rows = pixels.count
11
+ fake.columns = pixels.first.count
12
+ fake.colors_to_pixels = {
13
+ white.to_s => white_pixel,
14
+ red.to_s => red_pixel,
15
+ black.to_s => black_pixel
16
+ }
17
+
18
+ fake
19
+ end
20
+ let(:pixels) do
21
+ [
22
+ [black_pixel, white_pixel, white_pixel],
23
+ [black_pixel, black_pixel, white_pixel],
24
+ [black_pixel, black_pixel, black_pixel]
25
+ ]
26
+ end
27
+ let(:image_recolorer) { ImageRecolorer.new(image) }
28
+ let(:white_pixel) do
29
+ double('white', to_rgb: [255, 255, 255], transparent?: false)
30
+ end
31
+ let(:black_pixel) do
32
+ double('black', to_rgb: [0, 0, 0], transparent?: false)
33
+ end
34
+ let(:red_pixel) do
35
+ double('red', to_rgb: [255, 0, 0], transparent?: false)
36
+ end
37
+ let(:white) { Color.new(255, 255, 255) }
38
+ let(:black) { Color.new(0, 0, 0) }
39
+ let(:red) { Color.new(255, 0, 0) }
40
+ let(:new_colors) do
41
+ {
42
+ black => white,
43
+ white => black
44
+ }
45
+ end
46
+
47
+ subject { image_recolorer.recolor(new_colors) }
48
+
49
+ before do
50
+ allow(Image).to receive(:blank).and_return(image)
51
+ end
52
+
53
+ it "recolors image based on new_colors" do
54
+ expect(subject.pixels).to eq([
55
+ [white_pixel, black_pixel, black_pixel],
56
+ [white_pixel, white_pixel, black_pixel],
57
+ [white_pixel, white_pixel, white_pixel]
58
+ ])
59
+ end
60
+
61
+ context "transparent pixel" do
62
+ let(:black_pixel) do
63
+ double('black', to_rgb: [0, 0, 0], transparent?: true)
64
+ end
65
+
66
+ it "stays the same" do
67
+ expect(subject.pixels).to eq([
68
+ [black_pixel, black_pixel, black_pixel],
69
+ [black_pixel, black_pixel, black_pixel],
70
+ [black_pixel, black_pixel, black_pixel]
71
+ ])
72
+ end
73
+ end
74
+
75
+ context "color not present in new_colors" do
76
+ let(:pixels) do
77
+ [
78
+ [red_pixel, white_pixel, white_pixel],
79
+ [red_pixel, red_pixel, white_pixel],
80
+ [red_pixel, red_pixel, red_pixel]
81
+ ]
82
+ end
83
+
84
+ it "stays the same" do
85
+ expect(subject.pixels).to eq([
86
+ [red_pixel, black_pixel, black_pixel],
87
+ [red_pixel, red_pixel, black_pixel],
88
+ [red_pixel, red_pixel, red_pixel]
89
+ ])
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end