Skip to content

Commit

Permalink
More work on gradients
Browse files Browse the repository at this point in the history
  • Loading branch information
andymeneely committed Jan 22, 2015
1 parent 62a9959 commit e0a180c
Show file tree
Hide file tree
Showing 10 changed files with 194 additions and 12 deletions.
3 changes: 2 additions & 1 deletion lib/squib/card.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'cairo'
require 'squib/input_helpers'
require 'squib/graphics/cairo_context_wrapper'

module Squib
# Back end graphics. Private.
Expand All @@ -19,7 +20,7 @@ class Card
def initialize(deck, width, height)
@deck=deck; @width=width; @height=height
@cairo_surface = Cairo::ImageSurface.new(width,height)
@cairo_context = Cairo::Context.new(@cairo_surface)
@cairo_context = Squib::Graphics::CairoContextWrapper.new(Cairo::Context.new(@cairo_surface))
end

# A save/restore wrapper for using Cairo
Expand Down
2 changes: 1 addition & 1 deletion lib/squib/graphics/background.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class Card
# @api private
def background(color)
use_cairo do |cc|
cc.set_source_color(color)
cc.set_source_squibcolor(color)
cc.paint
end
end
Expand Down
42 changes: 42 additions & 0 deletions lib/squib/graphics/cairo_context_wrapper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
require 'forwardable'
require 'squib/graphics/gradient_regex'

module Squib
module Graphics
class CairoContextWrapper
extend Forwardable
attr_accessor :cairo_cxt

def initialize(cairo_cxt)
@cairo_cxt = cairo_cxt
end

def_delegators :cairo_cxt, :save, :set_source_color, :paint, :restore,
:translate, :rotate, :move_to, :update_pango_layout, :width, :height,
:show_pango_layout, :rounded_rectangle, :set_line_width, :stroke, :fill,
:set_source, :scale, :render_rsvg_handle, :circle, :triangle, :line_to,
:operator=, :show_page, :clip, :transform, :mask, :create_pango_layout

def set_source_squibcolor(arg)
if match = arg.match(LINEAR_GRADIENT)
x1, y1, x2, y2 = match.captures
linear = Cairo::LinearPattern.new(x1.to_f, y1.to_f, x2.to_f, y2.to_f)
arg.scan(STOPS).each do |color, offset|
linear.add_color_stop(offset.to_f, color)
end
@cairo_cxt.set_source(linear)
elsif match = arg.match(RADIAL_GRADIENT)
x1, y1, r1, x2, y2, r2 = match.captures
linear = Cairo::RadialPattern.new(x1.to_f, y1.to_f, r1.to_f,
x2.to_f, y2.to_f, r2.to_f)
arg.scan(STOPS).each do |color, offset|
linear.add_color_stop(offset.to_f, color)
end
@cairo_cxt.set_source(linear)
else
@cairo_cxt.set_source_color(arg)
end
end
end
end
end
46 changes: 46 additions & 0 deletions lib/squib/graphics/gradient_regex.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module Squib
module Graphics
STOPS = / # used to capture the stops
\s* # leading whitespace is ok
(\#?[\w]+) # color
@ # no spaces here
(\d+\.?\d*) # offset number
/x

LINEAR_GRADIENT = /
\( \s* # coordinate 1
(\d+\.?\d*) \s* # x1 number
,\s* # whitespace after comma is ok
(\d+\.?\d*) \s* # y1 number
\)
\s* # space between coordinates is ok
\( \s* # coordinate 2
(\d+\.?\d*) \s* # x2 number
,\s* # whitespace after comma is ok
(\d+\.?\d*) \s* # y2 number
\)
(#{STOPS})+ # stops
\s* # trailing whitespace is ok
/x

RADIAL_GRADIENT = /
\( \s* # coordinate 1
(\d+\.?\d*) \s* # x1 number
,\s* # whitespace after comma is ok
(\d+\.?\d*) \s* # y1 number
,\s* # whitespace after comma is ok
(\d+\.?\d*) \s* # r1 number
\)
\s* # space between coordinates is ok
\( \s* # coordinate 2
(\d+\.?\d*) \s* # x2 number
,\s* # whitespace after comma is ok
(\d+\.?\d*) \s* # y2 number
,\s* # whitespace after comma is ok
(\d+\.?\d*) \s* # r2 number
\)
(#{STOPS})+ # stops
\s* # trailing whitespace is ok
/x
end
end
14 changes: 7 additions & 7 deletions lib/squib/graphics/shapes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ def rect(x, y, width, height, x_radius, y_radius, fill_color, stroke_color, stro
height = @height if height == :native
use_cairo do |cc|
cc.rounded_rectangle(x, y, width, height, x_radius, y_radius)
cc.set_source_color(stroke_color)
cc.set_source_squibcolor(stroke_color)
cc.set_line_width(stroke_width)
cc.stroke
cc.rounded_rectangle(x, y, width, height, x_radius, y_radius)
cc.set_source_color(fill_color)
cc.set_source_squibcolor(fill_color)
cc.fill
end
end
Expand All @@ -22,11 +22,11 @@ def rect(x, y, width, height, x_radius, y_radius, fill_color, stroke_color, stro
def circle(x, y, radius, fill_color, stroke_color, stroke_width)
use_cairo do |cc|
cc.circle(x, y, radius)
cc.set_source_color(stroke_color)
cc.set_source_squibcolor(stroke_color)
cc.set_line_width(stroke_width)
cc.stroke
cc.circle(x, y, radius)
cc.set_source_color(fill_color)
cc.set_source_squibcolor(fill_color)
cc.fill
end
end
Expand All @@ -36,11 +36,11 @@ def circle(x, y, radius, fill_color, stroke_color, stroke_width)
def triangle(x1, y1, x2, y2, x3, y3, fill_color, stroke_color, stroke_width)
use_cairo do |cc|
cc.triangle(x1, y1, x2, y2, x3, y3)
cc.set_source_color(stroke_color)
cc.set_source_squibcolor(stroke_color)
cc.set_line_width(stroke_width)
cc.stroke
cc.triangle(x1, y1, x2, y2, x3, y3)
cc.set_source_color(fill_color)
cc.set_source_squibcolor(fill_color)
cc.fill
end
end
Expand All @@ -51,7 +51,7 @@ def line(x1, y1, x2, y2, stroke_color, stroke_width)
use_cairo do |cc|
cc.move_to(x1, y1)
cc.line_to(x2, y2)
cc.set_source_color(stroke_color)
cc.set_source_squibcolor(stroke_color)
cc.set_line_width(stroke_width)
cc.stroke
end
Expand Down
2 changes: 1 addition & 1 deletion lib/squib/graphics/text.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def text(str, font, font_size, color,
Squib.logger.debug {"Placing '#{str}'' with font '#{font}' @ #{x}, #{y}, color: #{color}, angle: #{angle} etc."}
extents = nil
use_cairo do |cc|
cc.set_source_color(color)
cc.set_source_squibcolor(color)
cc.translate(x,y)
cc.rotate(angle)
cc.translate(-1*x,-1*y)
Expand Down
3 changes: 2 additions & 1 deletion lib/squib/input_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ def colorify(opts, nillable=false, key=:color)
if @custom_colors.key? color.to_s
color = @custom_colors[color.to_s]
end
opts[key][i] = Cairo::Color.parse(color)
# opts[key][i] = Cairo::Color.parse(color)
color
end
end
Squib.logger.debug {"After colorify: #{opts}"}
Expand Down
5 changes: 4 additions & 1 deletion samples/colors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
text color: '#f00', str: '3-hex', x: 50, y: y+=50
text color: '#f00', str: '3-hex (alpha)', x: 50, y: y+=50
text color: '#ff0000', str: '6-hex', x: 50, y: y+=50
text color: '#ff000099', str: '8-hex(alpha) *', x: 50, y: y+=50
text color: '#ff000099', str: '8-hex(alpha)', x: 50, y: y+=50
text color: '#ffff00000000', str: '12-hex', x: 50, y: y+=50
text color: '#ffff000000009999', str: '12-hex (alpha)', x: 50, y: y+=50
text color: :burnt_orange, str: 'Symbols of constants too', x: 50, y: y+=50
text color: '(0,0)(400,0) blue@0.0 red@1.0', str: 'Linear gradients!', x: 50, y: y+=50
text color: '(200,500,10)(200,500,100) blue@0.0 red@1.0', str: 'Radial gradients!', x: 50, y: y+=50
# see gradients.rb sample for more on gradients

save_png prefix: 'colors_'
end
14 changes: 14 additions & 0 deletions samples/gradients.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require 'squib'

Squib::Deck.new do
# Just about anywhere Squib takes in a color it can also take in a gradient too
# Note that we need to provide xy coordinates.
# These are relative to the xy provided the command.
background color: '(0,0)(0,1125) #ccc@0.0 #111@1.0'

# Radial gradients look like this
circle x: 415, y: 415, radius: 100, stroke_color: '#0000',
fill_color: '(425,400,2)(425,400,120) #ccc@0.0 #111@1.0'

save_png prefix: 'gradient_'
end
75 changes: 75 additions & 0 deletions spec/graphics/cairo_context_wrapper_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
require 'spec_helper'
require 'squib/graphics/cairo_context_wrapper'

describe Squib::Graphics::CairoContextWrapper do

let(:cairo) { double(Cairo::Context) }
subject { Squib::Graphics::CairoContextWrapper.new(cairo) }

it 'passes on colors as normal' do
expect(cairo).to receive(:set_source_color).with('blue')
subject.set_source_squibcolor('blue')
end

it 'passes on color symbols as normal' do
expect(cairo).to receive(:set_source_color).with(:blue)
subject.set_source_squibcolor(:blue)
end

it 'passes on color hashes' do
expect(cairo).to receive(:set_source_color)
.with('#aabbccdd')
subject.set_source_squibcolor('#aabbccdd')
end

context 'regex variations for linear gradients' do
before(:each) do
dbl = double(Cairo::LinearPattern)
expect(Cairo::LinearPattern).to receive(:new).with(1,2,3,4).and_return(dbl)
expect(dbl).to receive(:add_color_stop).with(0.0, 'blue')
expect(dbl).to receive(:add_color_stop).with(1.0, 'red')
expect(cairo).to receive(:set_source).with(dbl)
end

it('handles no decimals' ) { subject.set_source_squibcolor('(1,2) (3,4) blue@0 red@1') }
it('handles decimals' ) { subject.set_source_squibcolor('(1.0,2.0) (3.0,4.0) blue@0 red@1') }
it('handles no whitespace') { subject.set_source_squibcolor('(1,2)(3,4)blue@0red@1') }
it('handles whitespace' ) { subject.set_source_squibcolor(' ( 1 , 2 ) ( 3 , 4 ) blue@0 red@1 ') }
end

context 'regex variations for radial gradients' do
before(:each) do
dbl = double(Cairo::RadialPattern)
expect(Cairo::RadialPattern).to receive(:new).with(1,2,5,3,4,6).and_return(dbl)
expect(dbl).to receive(:add_color_stop).with(0.0, 'blue')
expect(dbl).to receive(:add_color_stop).with(1.0, 'red')
expect(cairo).to receive(:set_source).with(dbl)
end

it('handles no decimals' ) { subject.set_source_squibcolor('(1,2,5) (3,4,6) blue@0 red@1') }
it('handles decimals' ) { subject.set_source_squibcolor('(1.0,2.0,5.0) (3.0,4.0,6.0) blue@0 red@1') }
it('handles no whitespace') { subject.set_source_squibcolor('(1,2,5)(3,4,6)blue@0red@1') }
it('handles whitespace' ) { subject.set_source_squibcolor(' ( 1 , 2 , 5 ) ( 3 , 4 , 6 ) blue@0 red@1 ') }
end

context 'regex handles hash notation' do
it 'on radial patterns' do
dbl = double(Cairo::RadialPattern)
expect(Cairo::RadialPattern).to receive(:new).with(1,2,5,3,4,6).and_return(dbl)
expect(dbl).to receive(:add_color_stop).with(0.0, '#def')
expect(dbl).to receive(:add_color_stop).with(1.0, '#112233')
expect(cairo).to receive(:set_source).with(dbl)
subject.set_source_squibcolor('(1,2,5) (3,4,6) #def@0 #112233@1')
end

it 'on linear patterns' do
dbl = double(Cairo::LinearPattern)
expect(Cairo::LinearPattern).to receive(:new).with(1,2,3,4).and_return(dbl)
expect(dbl).to receive(:add_color_stop).with(0.0, '#def')
expect(dbl).to receive(:add_color_stop).with(1.0, '#112233')
expect(cairo).to receive(:set_source).with(dbl)
subject.set_source_squibcolor('(1,2) (3,4) #def@0 #112233@1')
end
end

end

0 comments on commit e0a180c

Please sign in to comment.