parent
d92aab4bf3
commit
b25f0d535b
|
|
@ -1,9 +1,9 @@
|
|||
# Squib CHANGELOG
|
||||
Squib follows [semantic versioning](http://semver.org).
|
||||
|
||||
## v0.16.1 / 2021-07-22
|
||||
|
||||
## v0.17.0 / 2021-07-23
|
||||
Features:
|
||||
* Drop shadows! The `save_png` method now supports a bunch of `shadow_` arguments that will add a drop shadow just before rendering. This is intended for using in rulebooks or marketing. Try it out by adding `shadow_radius: 8` to your save_png (#306, #264)
|
||||
* Added debug methods for checking font access. `Squib.system_fonts` and `Squib.print_system_fonts` (#334)
|
||||
|
||||
Bugs:
|
||||
|
|
|
|||
|
|
@ -55,9 +55,9 @@ author = u'Andy Meneely'
|
|||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'v0.16'
|
||||
version = u'v0.17'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'v0.16.1'
|
||||
release = u'v0.17.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
|
|
|||
|
|
@ -41,7 +41,59 @@ trim_radius
|
|||
|
||||
the rounded rectangle radius around the card to trim before saving.
|
||||
|
||||
shadow_radius
|
||||
default: ``nil``
|
||||
|
||||
adds a drop shadow behind the card just before rendering, when non-nil. Does nothing when set to nil.
|
||||
|
||||
`Drop Shadows`. A larger radius extends the blur's reach. A radius of ``0`` will enable the feature but not do any blurring (which can still look good!).
|
||||
The final image will have a new width of: ``w + shadow_offset_x + (3 * shadow_radius) - (2 * shadow_trim) - (2 * trim)``, and similar for height.
|
||||
The image will be painted at ``shadow_radius, shadow_radius`` in the new image.
|
||||
This drop shadow happens before rendering and does not alter the original card graphic.
|
||||
At this stage, the feature will just draw a rectangle and blur from there. So if your card has transparency (other than from ``trim_radius``), from, say, a circular chit, then this won't work. We're working on a better implementation.
|
||||
See [blur algorithm](https://github.com/rcairo/rcairo/blob/master/lib/cairo/context/blur.rb) for details on blur implementation. Recommended range: 3-10 pixels. Supports :doc:`/units`.
|
||||
|
||||
shadow_offset_x
|
||||
default: ``3``
|
||||
|
||||
the horizontal distance that the drop shadow will be shifted beneath the final image.
|
||||
Ignored when ``shadow_radius`` is ``nil``. See ``shadow_radius`` above for drop shadow details.
|
||||
Supports :doc:`/units`.
|
||||
|
||||
shadow_offset_y
|
||||
default: ``3``
|
||||
|
||||
Ignored when `shadow_radius` is ``nil``. See ``shadow_radius`` above for drop shadow details.
|
||||
Supports :doc:`/units`.
|
||||
|
||||
shadow_trim
|
||||
default: ``0``
|
||||
|
||||
the space around the edge of the output image trimmed when a drop shadow is drawn.
|
||||
A negative number here would enlarge the margin on the right and bottom only.
|
||||
Ignored when `shadow_radius` is ``nil``. See ``shadow_radius`` above for drop shadow details.
|
||||
Supports :doc:`/units`.
|
||||
|
||||
shadow_color
|
||||
default: ``:black``
|
||||
|
||||
the color or gradient of the drop shadow. See :doc:`/colors`.
|
||||
|
||||
`Note about gradients:` while gradients are technically supported here, Squib will do the blurring for you. But, using a gradient here does give you enormous control over the softness of the shadow. See example below of doing a custom gradient for customizing your look.
|
||||
|
||||
|
||||
.. include:: /args/range.rst
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
This sample `lives here <https://github.com/andymeneely/squib/tree/master/samples/shadows>`_.
|
||||
|
||||
.. literalinclude:: ../../samples/shadows/_shadow.rb
|
||||
:language: ruby
|
||||
:linenos:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<img src="../saves/with_shadow_00_expected.png" class="figure">
|
||||
<img src="../saves/with_shadow_test_00_expected.png" class="figure">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
require_relative 'color_validator'
|
||||
|
||||
module Squib::Args
|
||||
module_function def extract_drop_shadow(opts, deck)
|
||||
DropShadow.new(deck.custom_colors).extract! opts, deck
|
||||
end
|
||||
|
||||
class DropShadow
|
||||
include ArgLoader
|
||||
include ColorValidator
|
||||
|
||||
def initialize(custom_colors)
|
||||
@custom_colors = custom_colors
|
||||
end
|
||||
|
||||
def self.parameters
|
||||
{
|
||||
shadow_color: :black,
|
||||
shadow_offset_x: 3,
|
||||
shadow_offset_y: 3,
|
||||
shadow_radius: nil,
|
||||
shadow_trim: 0,
|
||||
}
|
||||
end
|
||||
|
||||
def self.expanding_parameters
|
||||
self.parameters.keys # all of them
|
||||
end
|
||||
|
||||
def self.params_with_units
|
||||
[:shadow_offset_x, :shadow_offset_y, :shadow_radius, :shadow_trim]
|
||||
end
|
||||
|
||||
def validate_shadow_color(arg, _i)
|
||||
colorify(arg, @custom_colors)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
require_relative '../errors_warnings/warn_unexpected_params'
|
||||
|
||||
require_relative '../args/card_range'
|
||||
require_relative '../args/save_batch'
|
||||
require_relative '../args/drop_shadow'
|
||||
|
||||
module Squib
|
||||
class Deck
|
||||
|
|
@ -24,6 +26,7 @@ module Squib
|
|||
range
|
||||
dir prefix suffix count_format
|
||||
rotate trim trim_radius
|
||||
shadow_offset_x shadow_offset_y shadow_radius shadow_color shadow_trim
|
||||
)
|
||||
end
|
||||
|
||||
|
|
@ -31,9 +34,10 @@ module Squib
|
|||
warn_if_unexpected opts
|
||||
range = Args.extract_range opts, deck
|
||||
batch = Args.extract_save_batch opts, deck
|
||||
shadow = Args.extract_drop_shadow opts, deck
|
||||
@bar.start("Saving PNGs to #{batch.summary}", deck.size) do |bar|
|
||||
range.map do |i|
|
||||
deck.cards[i].save_png(batch[i])
|
||||
deck.cards[i].save_png(batch[i], shadow[i])
|
||||
bar.increment
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -20,12 +20,14 @@ module Squib
|
|||
|
||||
def_delegators :cairo_cxt, :save, :set_source_color, :paint, :restore,
|
||||
:translate, :rotate, :move_to, :update_pango_layout, :width, :height,
|
||||
:show_pango_layout, :rectangle, :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,
|
||||
:antialias=, :curve_to, :matrix, :matrix=, :identity_matrix, :pango_layout_path,
|
||||
:stroke_preserve, :target, :new_path, :new_sub_path, :reset_clip, :fill_preserve, :close_path,
|
||||
:set_line_join, :set_line_cap, :set_dash, :arc, :arc_negative
|
||||
:show_pango_layout, :rectangle, :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, :antialias=, :curve_to, :matrix, :matrix=,
|
||||
:identity_matrix, :pango_layout_path, :stroke_preserve, :target,
|
||||
:new_path, :new_sub_path, :reset_clip, :fill_preserve, :close_path,
|
||||
:set_line_join, :set_line_cap, :set_dash, :arc, :arc_negative,
|
||||
:pseudo_blur
|
||||
|
||||
# :nodoc:
|
||||
# @api private
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
require_relative 'cairo_context_wrapper'
|
||||
|
||||
module Squib
|
||||
class Card
|
||||
|
||||
# :nodoc:
|
||||
# @api private
|
||||
def save_png(batch)
|
||||
surface = if preprocess_save?(batch)
|
||||
def save_png(batch, shadow)
|
||||
surface = if preprocess_save?(batch, shadow)
|
||||
w, h = compute_dimensions(batch.rotate, batch.trim)
|
||||
preprocessed_save(w, h, batch)
|
||||
preprocessed_save(w, h, batch, shadow)
|
||||
else
|
||||
@cairo_surface
|
||||
end
|
||||
|
|
@ -15,8 +17,8 @@ module Squib
|
|||
|
||||
# :nodoc:
|
||||
# @api private
|
||||
def preprocess_save?(batch)
|
||||
batch.rotate != false || batch.trim > 0
|
||||
def preprocess_save?(batch, shadow)
|
||||
batch.rotate != false || batch.trim > 0 || !(shadow.shadow_radius.nil?)
|
||||
end
|
||||
|
||||
def compute_dimensions(rotate, trim)
|
||||
|
|
@ -27,25 +29,62 @@ module Squib
|
|||
end
|
||||
end
|
||||
|
||||
def preprocessed_save(width, height, batch)
|
||||
new_cc = Cairo::Context.new(Cairo::ImageSurface.new(width, height))
|
||||
def preprocessed_save(w, h, batch, shadow)
|
||||
new_cc = Cairo::Context.new(Cairo::ImageSurface.new(w, h))
|
||||
trim_radius = batch.trim_radius
|
||||
if batch.rotate != false
|
||||
new_cc.translate(width * 0.5, height * 0.5)
|
||||
new_cc.rotate(batch.angle)
|
||||
new_cc.translate(height * -0.5, width * -0.5)
|
||||
new_cc.rounded_rectangle(0, 0, height, width, trim_radius, trim_radius)
|
||||
new_cc.translate w * 0.5, h * 0.5
|
||||
new_cc.rotate batch.angle
|
||||
new_cc.translate h * -0.5, w * -0.5
|
||||
new_cc.rounded_rectangle(0, 0, h, w, trim_radius, trim_radius)
|
||||
else
|
||||
new_cc.rounded_rectangle(0, 0, width, height, trim_radius, trim_radius)
|
||||
new_cc.rounded_rectangle(0, 0, w, h, trim_radius, trim_radius)
|
||||
end
|
||||
new_cc.clip
|
||||
new_cc.set_source(@cairo_surface, -batch.trim, -batch.trim)
|
||||
new_cc.paint
|
||||
new_cc.reset_clip
|
||||
new_cc = drop_shadow(new_cc, shadow, batch) unless shadow.shadow_radius.nil?
|
||||
return new_cc.target
|
||||
end
|
||||
|
||||
# pseudo-blur behave weirdly with a radius of 0 - wrapping
|
||||
def blur(cc, r, &block)
|
||||
if r == 0
|
||||
yield(block)
|
||||
else
|
||||
cc.pseudo_blur(r, &block)
|
||||
end
|
||||
end
|
||||
|
||||
def drop_shadow(cc, s, batch)
|
||||
off_x = s.shadow_offset_x
|
||||
off_y = s.shadow_offset_y
|
||||
s_trim = s.shadow_trim
|
||||
s_rad = s.shadow_radius
|
||||
new_w = cc.target.width + off_x + 3 * s_rad - (2 * s_trim)
|
||||
new_h = cc.target.height + off_y + 3 * s_rad - (2 * s_trim)
|
||||
new_cc = Squib::Graphics::CairoContextWrapper.new(
|
||||
Cairo::Context.new(Cairo::ImageSurface.new(new_w, new_h)))
|
||||
blur(new_cc, s_rad) do
|
||||
# fill in with shadow color
|
||||
new_cc.set_source_squibcolor s.shadow_color
|
||||
new_cc.rectangle 0, 0, new_cc.target.width, new_cc.target.height
|
||||
new_cc.fill
|
||||
# then, paint but blend with :dest_in to get a shadow-shaped drawing
|
||||
new_cc.set_source cc.target, s_rad + off_x, s_rad + off_y
|
||||
new_cc.operator = :dest_in # see https://www.cairographics.org/operators/
|
||||
new_cc.paint
|
||||
end
|
||||
new_cc.set_source cc.target, s_rad, s_rad
|
||||
new_cc.operator = :over
|
||||
new_cc.paint
|
||||
return new_cc
|
||||
end
|
||||
|
||||
def write_png(surface, i, b)
|
||||
surface.write_to_png("#{b.dir}/#{b.prefix}#{b.count_format % i}#{b.suffix}.png")
|
||||
filename = "#{b.dir}/#{b.prefix}#{b.count_format % i}#{b.suffix}.png"
|
||||
surface.write_to_png filename
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
require 'squib'
|
||||
|
||||
orig = Cairo::ImageSurface.new(100,100)
|
||||
orig_cc = Cairo::Context.new(orig)
|
||||
orig_cc.circle(50,50, 30)
|
||||
orig_cc.set_source_color(:red)
|
||||
orig_cc.fill
|
||||
|
||||
orig.write_to_png '_output/blend_orig.png'
|
||||
|
||||
new_img = Cairo::ImageSurface.new(120, 120)
|
||||
new_cc = Cairo::Context.new(new_img)
|
||||
new_cc.set_source_color(:black)
|
||||
new_cc.rectangle(0,0,120,120)
|
||||
new_cc.fill
|
||||
new_cc.set_source orig
|
||||
new_cc.operator = :dest_in
|
||||
new_cc.paint
|
||||
|
||||
new_img.write_to_png '_output/blend_new.png'
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
require_relative '../../lib/squib'
|
||||
# require 'squib'
|
||||
# The save_png method supports drop shadows on the final save
|
||||
# This is useful for when you want to generate images for your rulebook
|
||||
|
||||
Squib::Deck.new(width: 100, height: 150) do
|
||||
background color: '#abc'
|
||||
svg file: '../spanner.svg',
|
||||
x: 'middle - 25', y: 'middle - 25',
|
||||
width: 50, height: 50
|
||||
|
||||
# Shadows off by default, i.e. shadow_radius is nil
|
||||
# So our final dimensions are 100 - 2*15 and 150-2*15
|
||||
save_png prefix: 'no_shadow_', trim: 15, trim_radius: 15
|
||||
|
||||
# Here's a nice-looking drop shadow
|
||||
# Defaults are designed to be generally good, so I recommend just
|
||||
# trying out a shadow_radius of 3 to 10 and see how it looks first
|
||||
save_png prefix: 'with_shadow_', trim: 15, trim_radius: 15,
|
||||
shadow_radius: 8,
|
||||
shadow_offset_x: 3, shadow_offset_y: 3, # about r / 2.5 looks good
|
||||
shadow_trim: 2.5, # about r/ 3 looks good
|
||||
shadow_color: '#101010aa' #tip: start the shadow color kinda transparent
|
||||
|
||||
# Don't want a blur? Use a radius of 0
|
||||
save_png prefix: 'no_blur_', trim: 15, trim_radius: 15,
|
||||
shadow_radius: 0
|
||||
|
||||
# Ok this next stop is crazytown, but it does give you ultimate control
|
||||
# Remember that all Squib colors can also be gradients.
|
||||
# They can be clunky but they do work here.
|
||||
# - x,y's are centered in the card itself
|
||||
# - stops go from fully empty to fully black
|
||||
# - we need to still turn on radius to get the effect
|
||||
# - but, this makes the upper-left corner not have a glowing effect and
|
||||
# have a harder edge, which (to my eye at least) feels more realistic
|
||||
# since the card would obscure the upper-left shadow at that angle
|
||||
# - this also allows you have a larger, softer blur without making it look
|
||||
# like it's glowing
|
||||
#
|
||||
# Oh just because it's easier to write we can use a ruby heredoc
|
||||
save_png prefix: 'gradient_blur_', trim: 15, trim_radius: 15,
|
||||
shadow_radius: 10,
|
||||
shadow_color: <<~EOS
|
||||
(25,25)
|
||||
(175,175)
|
||||
#0000@0.0
|
||||
#000f@1.0
|
||||
EOS
|
||||
|
||||
# This one looks weird I know but it's for regression testing
|
||||
save_png prefix: 'with_shadow_test_',
|
||||
trim: 15, trim_radius: 15, rotate: :clockwise,
|
||||
shadow_offset_x: 5, shadow_offset_y: 25, shadow_radius: 10,
|
||||
shadow_trim: 10,
|
||||
shadow_color: '#123'
|
||||
end
|
||||
|
||||
Squib::Deck.new(width:50, height: 50) do
|
||||
|
||||
# What if we had a transparent card but just some shapes?
|
||||
# Like chits or something
|
||||
|
||||
# background defaults to fully transparent here
|
||||
|
||||
# star x: 50, y: 50, inner_radius: 15, outer_radius: 35,
|
||||
# fill_color: :red, stroke_color: :red
|
||||
|
||||
png file: 'doodle.png'
|
||||
|
||||
# save_png prefix: 'no_bg_shadow_', shadow_radius: 8, shadow_offset_x: 3, shadow_offset_y: 3
|
||||
save_png prefix: 'transparent_bg_shadow_',
|
||||
shadow_radius: 2,
|
||||
shadow_offset_x: 2, shadow_offset_y: 2,
|
||||
shadow_color: :black
|
||||
|
||||
end
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
Loading…
Reference in New Issue