From f8f65f3de7b4c2b437f7603ec85fc86d192535eb Mon Sep 17 00:00:00 2001 From: Andy Meneely Date: Tue, 9 Dec 2014 22:59:49 -0500 Subject: [PATCH] Support png scaling, but warns on use. --- CHANGELOG.md | 2 ++ lib/squib/api/image.rb | 8 +++++--- lib/squib/graphics/image.rb | 8 +++++++- samples/load_images.rb | 6 ++++-- spec/api/api_image_spec.rb | 2 +- spec/data/samples/load_images.rb.txt | 8 ++++++++ spec/graphics/graphics_images_spec.rb | 26 +++++++++++++------------ spec/samples/samples_regression_spec.rb | 1 + spec/spec_helper.rb | 1 + 9 files changed, 43 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52ad53a..f7fee93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Squib CHANGELOG +* `png` now supports resizing, but warns you about it since it's non-ideal. Documented in yard, tested. + ## v0.0.6 * Added a `csv` command that works just like `xslx`. Uses Ruby's CSV inside, with some extra checking and warnings. * Custom layouts now support loading & merging multiple Yaml files! Updated README, docs, and sample to document it. diff --git a/lib/squib/api/image.rb b/lib/squib/api/image.rb index 52d1533..583d338 100644 --- a/lib/squib/api/image.rb +++ b/lib/squib/api/image.rb @@ -4,7 +4,6 @@ module Squib # Renders a png file at the given location. # # See {file:samples/image.rb samples/image.rb} and {file:samples/tgc-overlay.rb samples/tgc-overlay.rb} as examples. - # Note: scaling not currently supported for PNGs. # @example # png file: 'img.png', x: 50, y: 50 # @@ -12,6 +11,8 @@ module Squib # @option opts file [String] ((empty)) file(s) to read in. If it's a single file, then it's use for every card in range. If the parameter is an Array of files, then each file is looked up for each card. If any of them are nil or '', nothing is done. See {file:README.md#Specifying_Files Specifying Files}. Supports Arrays, see {file:README.md#Arrays_and_Singleton_Expansion Arrays and Singleon Expansion} # @option opts x [Integer] (0) the x-coordinate to place. Supports Arrays, see {file:README#Arrays_and_Singleton_Expansion Arrays and Singleon Expansion} # @option opts y [Integer] (0) the y-coordinate to place. Supports Arrays, see {file:README#Arrays_and_Singleton_Expansion Arrays and Singleon Expansion} + # @option opts width [Integer] (:native) the pixel width that the image should scale to. Scaling PNGs is not recommended for professional-looking cards. When set to `:native`, uses the DPI and units of the loaded SVG document. Supports Arrays, see {file:README.md#Arrays_and_Singleton_Expansion Arrays and Singleon Expansion} + # @option opts height [Integer] (:native) the pixel width that the image should scale to. Scaling PNGs is not recommended for professional-looking cards. When set to `:native`, uses the DPI and units of the loaded SVG document. Supports Arrays, see {file:README.md#Arrays_and_Singleton_Expansion Arrays and Singleon Expansion} # @option opts layout [String, Symbol] (nil) entry in the layout to use as defaults for this command. See {file:README.md#Custom_Layouts Custom Layouts}. Supports Arrays, see {file:README.md#Arrays_and_Singleton_Expansion Arrays and Singleon Expansion} # @option opts alpha [Decimal] (1.0) the alpha-transparency percentage used to blend this image. Supports Arrays, see {file:README.md#Arrays_and_Singleton_Expansion Arrays and Singleon Expansion} # @option opts blend [:none, :multiply, :screen, :overlay, :darken, :lighten, :color_dodge, :color_burn, :hard_light, :soft_light, :difference, :exclusion, :hsl_hue, :hsl_saturation, :hsl_color, :hsl_luminosity] (:none) the composite blend operator used when applying this image. See Blend Modes at http://cairographics.org/operators. Supports Arrays, see {file:README.md#Arrays_and_Singleton_Expansion Arrays and Singleon Expansion} @@ -19,11 +20,12 @@ module Squib # @return [nil] Returns nil # @api public def png(opts = {}) - opts = needs(opts, [:range, :files, :x, :y, :alpha, :layout, :blend, :angle]) + opts = needs(opts, [:range, :files, :x, :y, :width, :height, :alpha, :layout, :blend, :angle]) Dir.chdir(@img_dir) do @progress_bar.start('Loading PNG(s)', opts[:range].size) do |bar| opts[:range].each do |i| - @cards[i].png(opts[:file][i], opts[:x][i], opts[:y][i], + @cards[i].png(opts[:file][i], + opts[:x][i], opts[:y][i], opts[:width][i], opts[:height][i], opts[:alpha][i], opts[:blend][i], opts[:angle][i]) bar.increment end diff --git a/lib/squib/graphics/image.rb b/lib/squib/graphics/image.rb index 9f9a813..1c60553 100644 --- a/lib/squib/graphics/image.rb +++ b/lib/squib/graphics/image.rb @@ -14,12 +14,18 @@ module Squib # :nodoc: # @api private - def png(file, x, y, alpha, blend, angle) + def png(file, x, y, width, height, alpha, blend, angle) Squib.logger.debug {"Rendering: #{file} @#{x},#{y} #{width}x#{height}, alpha: #{alpha}, blend: #{blend}, angle: #{angle}"} return if file.nil? or file.eql? '' png = Squib.cache_load_image(file) use_cairo do |cc| cc.translate(x, y) + if width != :native || height != :native + width == :native && width = png.width.to_f + height == :native && height = png.height.to_f + Squib.logger.warn "PNG scaling results in antialiasing." + cc.scale(width.to_f / png.width.to_f, height.to_f / png.height.to_f) + end cc.rotate(angle) cc.translate(-1 * x, -1 * y) cc.set_source(png, x, y) diff --git a/samples/load_images.rb b/samples/load_images.rb index 630b065..c2a4add 100644 --- a/samples/load_images.rb +++ b/samples/load_images.rb @@ -4,11 +4,13 @@ Squib::Deck.new(width: 825, height: 1125, cards: 1) do background color: '#0b7c8e' rect x: 38, y: 38, width: 750, height: 1050, x_radius: 38, y_radius: 38 - png file: 'shiny-purse.png', x: 620, y: 75 + png file: 'shiny-purse.png', x: 620, y: 75 # no scaling is done by default svg file: 'spanner.svg', x: 620, y: 218 - # SVGs can be scaled too + # Can be scaled if width and height are set svg file: 'spanner.svg', x: 50, y: 50, width: 250, height: 250 + png file: 'shiny-purse.png', x: 305, y: 50, width: 250, height: 250 + #...but PNGs will warn if it's an upscale # We can also limit our rendering to a single object, if the SVG ID is set # Squib prepends a #-sign if one is not specified diff --git a/spec/api/api_image_spec.rb b/spec/api/api_image_spec.rb index 2a2ef4a..979a87d 100644 --- a/spec/api/api_image_spec.rb +++ b/spec/api/api_image_spec.rb @@ -7,7 +7,7 @@ describe Squib::Deck, 'images' do it 'calls Card#png, Dir, and progress bar' do card = instance_double(Squib::Card) progress = double(Squib::Progress) - expect(card).to receive(:png).with('foo', 0, 1, 0.5, :overlay, 0.75).once + expect(card).to receive(:png).with('foo', 0, 1, :native, :native, 0.5, :overlay, 0.75).once expect(Dir).to receive(:chdir).with('.').and_yield.once expect(progress).to receive(:start).and_yield(progress).once expect(progress).to receive(:increment).once diff --git a/spec/data/samples/load_images.rb.txt b/spec/data/samples/load_images.rb.txt index f52315e..a061456 100644 --- a/spec/data/samples/load_images.rb.txt +++ b/spec/data/samples/load_images.rb.txt @@ -36,6 +36,14 @@ cairo: translate([-50, -50]) cairo: set_source([MockDouble, 50, 50]) cairo: paint([1.0]) cairo: restore([]) +cairo: save([]) +cairo: translate([305, 50]) +cairo: scale([1.953125, 1.953125]) +cairo: rotate([0]) +cairo: translate([-305, -50]) +cairo: set_source([ImageSurface, 305, 50]) +cairo: paint([1.0]) +cairo: restore([]) cairo: scale([0.5859375, 0.5859375]) cairo: render_rsvg_handle([RSVG::Handle, "#backdrop"]) cairo: save([]) diff --git a/spec/graphics/graphics_images_spec.rb b/spec/graphics/graphics_images_spec.rb index 867e25f..5e04472 100644 --- a/spec/graphics/graphics_images_spec.rb +++ b/spec/graphics/graphics_images_spec.rb @@ -3,14 +3,15 @@ require 'squib' describe Squib::Card do - before(:each) do - @deck = double(Squib::Deck) - @context = double(Cairo::Context) - @svg = double(RSVG::Handle) - allow(Cairo::Context).to receive(:new).and_return(@context) - allow(Cairo::ImageSurface).to receive(:from_png).and_return(nil) - allow(Cairo::ImageSurface).to receive(:new).and_return(nil) - allow(RSVG::Handle).to receive(:new_from_file).and_return(@svg) + before(:example) do + @deck = double(Squib::Deck) + @context = double(Cairo::Context) + @svg = double(RSVG::Handle) + @png = double(Cairo::ImageSurface) + allow(Cairo::Context).to receive(:new).and_return(@context) + allow(Cairo::ImageSurface).to receive(:from_png).and_return(@png) + allow(Cairo::ImageSurface).to receive(:new).and_return(@png) + allow(RSVG::Handle).to receive(:new_from_file).and_return(@svg) end context '#png' do @@ -19,13 +20,13 @@ describe Squib::Card do expect(@context).to receive(:translate).with(-37, -38).once expect(@context).to receive(:rotate).with(0.0).once expect(@context).to receive(:translate).with(37, 38).once - expect(@context).to receive(:set_source).with(nil, 37, 38).once + expect(@context).to receive(:set_source).with(@png, 37, 38).once expect(@context).to receive(:paint).with(0.9).once expect(@context).to receive(:restore).once card = Squib::Card.new(@deck, 100, 150) # png(file, x, y, alpha, blend, angle) - card.png('foo.png', 37, 38, 0.9, :none, 0.0) + card.png('foo.png', 37, 38, :native, :native, 0.9, :none, 0.0) end it 'sets blend when needed' do @@ -33,8 +34,9 @@ describe Squib::Card do expect(@context).to receive(:operator=).with(:overlay).once card = Squib::Card.new(@deck, 100, 150) - card.png('foo.png', 37, 38, 0.9, :overlay, 0.0) + card.png('foo.png', 37, 38, :native, :native, 0.9, :overlay, 0.0) end + end context '#svg' do @@ -47,7 +49,7 @@ describe Squib::Card do expect(@context).to receive(:translate).with(37, 38).once expect(@context).to receive(:scale).with(1.0, 1.0).once expect(@context).to receive(:render_rsvg_handle).with(@svg, 'id').once - expect(@context).to receive(:set_source).with(nil, 37, 38).once + expect(@context).to receive(:set_source).with(@png, 37, 38).once expect(@context).to receive(:paint).with(0.9).once expect(@context).to receive(:restore).once diff --git a/spec/samples/samples_regression_spec.rb b/spec/samples/samples_regression_spec.rb index 94dd5eb..893e8f6 100644 --- a/spec/samples/samples_regression_spec.rb +++ b/spec/samples/samples_regression_spec.rb @@ -12,6 +12,7 @@ describe "Squib samples" do end it 'should execute with no errors' do + allow(Squib.logger).to receive(:warn) {} allow(ProgressBar).to receive(:create).and_return(Squib::DoNothing.new) Dir["#{samples_dir}/**/*.rb"].each do |sample| load sample diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 1ae67dc..c76046f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -54,6 +54,7 @@ def mock_cairo(strio) cxt = double(Cairo::Context) surface = double(Cairo::ImageSurface) pango = double(Pango::Layout) + allow(Squib.logger).to receive(:warn) {} allow(ProgressBar).to receive(:create).and_return(Squib::DoNothing.new) allow(Cairo::ImageSurface).to receive(:new).and_return(surface) allow(surface).to receive(:width).and_return(100)