squib icon indicating copy to clipboard operation
squib copied to clipboard

Semi-transparent PNGs render incorrectly when embedded in a text method

Open tbsp opened this issue 6 years ago • 3 comments

When embedding semi-transparent images in a text method the semi-transparent portions are rendered much darker than expected. The png method behaves as expected.

require 'squib'

Squib::Deck.new cards: 1 do
  background color: 'white'

  # This image renders as expected
  png file: 'semi-transparent.png', x: 100, y: 100

  # The semi-transparent portions of this image render much darker than expected
  text(str: ':circle:', x: 200, y: 100) do |embed|
    embed.png key: ':circle:', file: 'semi-transparent.png'
  end

  save_png
end

Example PNG image: semi-transparent

Example output: output

tbsp avatar Jun 06 '19 15:06 tbsp

Ah. Figured out what it is. Turns out we paint the icon one time for each letter in the embed key. Normally the effect is not noticeable, but when there's alpha transparency they end up stacking up on each other so you get that effect.

The text embedding is most difficult code in Squib, so it might take me a few tries to get this fixed properly. In the meantime, you can work around this by making your embed string a single character. In the reworked example below I swapped out :circle: for @

require 'squib'

Squib::Deck.new cards: 1 do
  background color: 'white'

  # This image renders as expected
  png file: 'semi-transparent.png', x: 100, y: 100

  # The semi-transparent portions of this image render much darker than expected
  text(str: '@', x: 200, y: 100) do |embed|
    embed.png key: '@', file: 'semi-transparent.png'
  end

  save_png
end

andymeneely avatar Jun 07 '19 20:06 andymeneely

This is the code I'm using to remap my strings to single characters to workaround the issue without having to change all the strings in my source data.

def remapTokens(x)
 mapping = {':circle:' => '@', ':square:' => '#', ':triangle:' => '^'}
 mapping.each { |k,v| x.gsub!(k,v) }
 return x
end

tbsp avatar Jun 17 '19 17:06 tbsp

I think the multiple draws stem from setting a range of multiple characters to the Pango::AttrShape in embed_images! in text.rb here https://github.com/andymeneely/squib/blob/1b7dc6716cdfb4c404fae5ec8bef8d780d918b9f/lib/squib/graphics/text.rb#L89-L90

This seems to set this attribute for each character in the text, i.e. with the subsequent set_shape_renderer call it tells Cairo to render each character in the range separately with these attributes. It does not tell cairo to render the whole range as a single 'character'.

Maybe we could replace each embed key by a single character, associate that new characters position with the embed key (so we don't lose the information which key is which) and then do the image embedding for just that single character. In contrast to substituting each embed key with a different, unique single character, this would have the advantage that we can't get into a position where we use a character that by chance already exists in the user provided string.

Qgel avatar Oct 24 '19 09:10 Qgel