squib
squib copied to clipboard
Semi-transparent PNGs render incorrectly when embedded in a text method
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: 
Example output:

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
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
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.