Resources as files + "use system font" bool

This commit is contained in:
Julien Richard 2022-01-30 21:34:20 +01:00
commit 870af8bd33
8 changed files with 153 additions and 91 deletions

View file

@ -1,17 +1,17 @@
using System; using System.Windows;
using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media;
using Greenshot.Editor.Drawing; using Greenshot.Editor.Drawing;
namespace Greenshot.Editor.Controls namespace Greenshot.Editor.Controls
{ {
internal class EmojiControl : Image internal class EmojiControl : Image
{ {
public static readonly DependencyProperty EmojiProperty = DependencyProperty.Register("Emoji", typeof(string), typeof(EmojiControl), new PropertyMetadata(default(string), PropertyChangedCallback)); public static readonly DependencyProperty EmojiProperty = DependencyProperty.Register("Emoji", typeof(string), typeof(EmojiControl), new PropertyMetadata(default(string), OnEmojiPropertyChanged));
private static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) private static void OnEmojiPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{ {
((EmojiControl)d).Source = EmojiRenderer.GetBitmapSource((string)e.NewValue, iconSize: 48); ((EmojiControl)d).Source = null;
} }
public string Emoji public string Emoji
@ -19,5 +19,28 @@ namespace Greenshot.Editor.Controls
get { return (string)GetValue(EmojiProperty); } get { return (string)GetValue(EmojiProperty); }
set { SetValue(EmojiProperty, value); } set { SetValue(EmojiProperty, value); }
} }
public static readonly DependencyProperty UseSystemFontProperty = DependencyProperty.Register("UseSystemFont", typeof(bool), typeof(EmojiControl), new PropertyMetadata(default(bool), OnUseSystemFontPropertyChanged));
private static void OnUseSystemFontPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((EmojiControl)d).Source = null;
}
protected override void OnRender(DrawingContext dc)
{
if (Source == null && !string.IsNullOrEmpty(Emoji))
{
Source = EmojiRenderer.GetBitmapSource(Emoji, iconSize: 48, useSystemFont: UseSystemFont);
}
base.OnRender(dc);
}
public bool UseSystemFont
{
get { return (bool)GetValue(UseSystemFontProperty); }
set { SetValue(UseSystemFontProperty, value); }
}
} }
} }

View file

@ -18,7 +18,6 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Greenshot.Editor.Drawing;
using SixLabors.Fonts.Unicode; using SixLabors.Fonts.Unicode;
namespace Greenshot.Editor.Controls namespace Greenshot.Editor.Controls
@ -242,8 +241,8 @@ namespace Greenshot.Editor.Controls
private static IEnumerable<string> EmojiDescriptionLines() private static IEnumerable<string> EmojiDescriptionLines()
{ {
using var stream = Assembly.GetCallingAssembly().GetManifestResourceStream("Greenshot.Editor.Resources.emoji-test.txt"); using var fileStream = new FileStream(Path.Combine(Path.GetDirectoryName(Assembly.GetCallingAssembly().Location), @"Resources\emoji-test.txt"), FileMode.Open, FileAccess.Read);
using var streamReader = new StreamReader(stream); using var streamReader = new StreamReader(fileStream);
return streamReader.ReadToEnd().Split('\r', '\n'); return streamReader.ReadToEnd().Split('\r', '\n');
} }
} }

View file

@ -17,7 +17,8 @@
BorderThickness="1" Height="Auto" Padding="0" Margin="1" BorderThickness="1" Height="Auto" Padding="0" Margin="1"
ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
MaxWidth="280"> <!-- 6 columns --> MaxWidth="280">
<!-- 6 columns -->
<ListView.ItemsPanel> <ListView.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<WrapPanel/> <WrapPanel/>
@ -35,7 +36,7 @@
<Button Background="Transparent" BorderBrush="Transparent" <Button Background="Transparent" BorderBrush="Transparent"
Margin="0" Click="OnEmojiPicked" Margin="0" Click="OnEmojiPicked"
Width="40" Height="40" ToolTip="{Binding Path=Name}"> Width="40" Height="40" ToolTip="{Binding Path=Name}">
<controls:EmojiControl Height="24" Emoji="{Binding Path=Text}"/> <controls:EmojiControl Height="24" UseSystemFont="{Binding UseSystemFont, ElementName=StackPanel_INTERNAL, Mode=OneWay}" Emoji="{Binding Path=Text}" />
</Button> </Button>
</DataTemplate> </DataTemplate>
</ListView.ItemTemplate> </ListView.ItemTemplate>
@ -47,7 +48,7 @@
x:Name="VariationButton" Background="Transparent" BorderBrush="Transparent" x:Name="VariationButton" Background="Transparent" BorderBrush="Transparent"
Click="OnEmojiPicked" Focusable="False" ToolTip="{Binding Path=Name}"> Click="OnEmojiPicked" Focusable="False" ToolTip="{Binding Path=Name}">
<Grid> <Grid>
<controls:EmojiControl Height="24" Margin="4" Emoji="{Binding Path=Text}" <controls:EmojiControl Height="24" Margin="4" UseSystemFont="{Binding UseSystemFont, ElementName=StackPanel_INTERNAL, Mode=OneWay}" Emoji="{Binding Path=Text}"
VerticalAlignment="Center" HorizontalAlignment="Center"/> VerticalAlignment="Center" HorizontalAlignment="Center"/>
<Polygon Visibility="{Binding HasVariations, Converter={StaticResource BoolToVis}}" <Polygon Visibility="{Binding HasVariations, Converter={StaticResource BoolToVis}}"
Width="6" Height="6" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="6" Height="6" VerticalAlignment="Bottom" HorizontalAlignment="Right"
@ -70,12 +71,13 @@
StaysOpen="False" StaysOpen="False"
KeyDown="OnPopupKeyDown" Loaded="OnPopupLoaded" KeyDown="OnPopupKeyDown" Loaded="OnPopupLoaded"
AllowsTransparency="True" Margin="0"> AllowsTransparency="True" Margin="0">
<Border>
<StackPanel Orientation="Vertical">
<TabControl ItemsSource="{Binding EmojiGroups, ElementName=StackPanel_INTERNAL}" Padding="0"> <TabControl ItemsSource="{Binding EmojiGroups, ElementName=StackPanel_INTERNAL}" Padding="0">
<TabControl.ItemTemplate> <TabControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<Grid> <Grid>
<controls:EmojiControl Height="24" Emoji="{Binding Icon}"> <controls:EmojiControl Height="24" UseSystemFont="{Binding UseSystemFont, ElementName=StackPanel_INTERNAL, Mode=OneWay}" Emoji="{Binding Icon}" >
<Image.ToolTip> <Image.ToolTip>
<TextBlock Text="{Binding Name}"/> <TextBlock Text="{Binding Name}"/>
</Image.ToolTip> </Image.ToolTip>
@ -132,5 +134,10 @@
</DataTemplate> </DataTemplate>
</TabControl.ContentTemplate> </TabControl.ContentTemplate>
</TabControl> </TabControl>
<CheckBox VerticalContentAlignment="Center" IsChecked="{Binding UseSystemFont, ElementName=StackPanel_INTERNAL}" >
Use system font
</CheckBox>
</StackPanel>
</Border>
</Popup> </Popup>
</StackPanel> </StackPanel>

View file

@ -42,6 +42,14 @@ namespace Greenshot.Editor.Controls
public IList<EmojiData.Group> EmojiGroups => EmojiData.AllGroups; public IList<EmojiData.Group> EmojiGroups => EmojiData.AllGroups;
public static readonly DependencyProperty UseSystemFontProperty = DependencyProperty.Register("UseSystemFont", typeof(bool), typeof(EmojiPicker), new PropertyMetadata(default(bool)));
public bool UseSystemFont
{
get { return (bool)GetValue(UseSystemFontProperty); }
set { SetValue(UseSystemFontProperty, value); }
}
// Backwards compatibility for when the backend was a TextBlock. // Backwards compatibility for when the backend was a TextBlock.
public double FontSize public double FontSize
{ {

View file

@ -24,12 +24,9 @@ using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Linq; using System.Linq;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Windows;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
using System.Windows.Forms; using System.Windows.Forms;
using System.Windows.Forms.Integration; using System.Windows.Forms.Integration;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Greenshot.Base.Core; using Greenshot.Base.Core;
using Greenshot.Base.Interfaces.Drawing; using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Controls; using Greenshot.Editor.Controls;
@ -37,7 +34,6 @@ using Greenshot.Editor.Drawing.Adorners;
using Greenshot.Editor.Helpers; using Greenshot.Editor.Helpers;
using Image = System.Drawing.Image; using Image = System.Drawing.Image;
using Matrix = System.Drawing.Drawing2D.Matrix; using Matrix = System.Drawing.Drawing2D.Matrix;
using Size = System.Windows.Size;
namespace Greenshot.Editor.Drawing namespace Greenshot.Editor.Drawing
{ {
@ -56,6 +52,7 @@ namespace Greenshot.Editor.Drawing
private string _emoji; private string _emoji;
private int _rotationAngle; private int _rotationAngle;
private bool _useSystemFont;
public string Emoji public string Emoji
{ {
@ -67,6 +64,16 @@ namespace Greenshot.Editor.Drawing
} }
} }
public bool UseSystemFont
{
get => _useSystemFont;
set
{
_useSystemFont = value;
ResetCachedBitmap();
}
}
public EmojiContainer(Surface parent) : this(parent, "🙂", size: 64) public EmojiContainer(Surface parent) : this(parent, "🙂", size: 64)
{ {
} }
@ -111,6 +118,7 @@ namespace Greenshot.Editor.Drawing
_emojiPicker.Picked += (_, args) => _emojiPicker.Picked += (_, args) =>
{ {
_currentContainer.Emoji = args.Emoji; _currentContainer.Emoji = args.Emoji;
_currentContainer.UseSystemFont = _emojiPicker.UseSystemFont;
_currentContainer.Invalidate(); _currentContainer.Invalidate();
}; };
@ -211,7 +219,7 @@ namespace Greenshot.Editor.Drawing
private Image ComputeBitmap(int iconSize) private Image ComputeBitmap(int iconSize)
{ {
var image = EmojiRenderer.GetBitmap(Emoji, iconSize); var image = EmojiRenderer.GetBitmap(Emoji, iconSize, useSystemFont: _useSystemFont);
if (_rotationAngle != 0) if (_rotationAngle != 0)
{ {
var newImage = image.Rotate(_rotationAngle); var newImage = image.Rotate(_rotationAngle);

View file

@ -1,6 +1,7 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.IO;
using System.Reflection; using System.Reflection;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
@ -19,28 +20,37 @@ namespace Greenshot.Editor.Drawing
{ {
internal static class EmojiRenderer internal static class EmojiRenderer
{ {
private static Lazy<FontFamily> _fontFamily = new Lazy<FontFamily>(() => static FontCollection _fontCollection = new FontCollection();
private static Lazy<FontFamily> _twemoji = new Lazy<FontFamily>(() =>
{ {
using var stream = Assembly.GetCallingAssembly().GetManifestResourceStream("Greenshot.Editor.Resources.TwemojiMozilla.ttf"); using var fileStream = new FileStream(Path.Combine(Path.GetDirectoryName(Assembly.GetCallingAssembly().Location), @"Resources\TwemojiMozilla.ttf"), FileMode.Open, FileAccess.Read);
var fontCollection = new FontCollection(); _fontCollection.Add(fileStream);
fontCollection.Add(stream); _fontCollection.TryGet("Twemoji Mozilla", out var fontFamily);
fontCollection.TryGet("Twemoji Mozilla", out var fontFamily);
return fontFamily; return fontFamily;
}); });
public static Image<Rgba32> GetImage(string emoji, int iconSize) private static Lazy<FontFamily> _segoeUI = new Lazy<FontFamily>(() =>
{
using var fileStream = new FileStream(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), @"Fonts\seguiemj.ttf"), FileMode.Open, FileAccess.Read);
_fontCollection.Add(fileStream);
_fontCollection.TryGet("Segoe UI Emoji", out var fontFamily);
return fontFamily;
});
public static Image<Rgba32> GetImage(string emoji, int iconSize, bool useSystemFont)
{ {
var image = new Image<Rgba32>(iconSize, iconSize); var image = new Image<Rgba32>(iconSize, iconSize);
RenderEmoji(emoji, iconSize, image); RenderEmoji(emoji, iconSize, image, useSystemFont);
return image; return image;
} }
private static void RenderEmoji(string emoji, int iconSize, SixLabors.ImageSharp.Image image) private static void RenderEmoji(string emoji, int iconSize, SixLabors.ImageSharp.Image image, bool useSystemFont)
{ {
var font = _fontFamily.Value.CreateFont(iconSize, FontStyle.Regular); var fontFamily = useSystemFont ? _segoeUI.Value : _twemoji.Value;
var font = fontFamily.CreateFont(useSystemFont ? iconSize * 0.95f : iconSize, FontStyle.Regular);
var verticalOffset = font.Size * 0.045f; var verticalOffset = useSystemFont ? -font.Size * 0.050f : font.Size * 0.045f;
var textOptions = new TextOptions(font) var textOptions = new TextOptions(font)
{ {
Origin = new SixLabors.ImageSharp.PointF(font.Size / 2.0f, font.Size / 2.0f - verticalOffset), Origin = new SixLabors.ImageSharp.PointF(font.Size / 2.0f, font.Size / 2.0f - verticalOffset),
@ -51,7 +61,7 @@ namespace Greenshot.Editor.Drawing
image.Mutate(x => x.DrawText(textOptions, emoji, SixLabors.ImageSharp.Color.Black)); image.Mutate(x => x.DrawText(textOptions, emoji, SixLabors.ImageSharp.Color.Black));
} }
public static Image GetBitmap(string emoji, int iconSize) public static Image GetBitmap(string emoji, int iconSize, bool useSystemFont)
{ {
int width = iconSize; int width = iconSize;
int height = iconSize; int height = iconSize;
@ -64,7 +74,7 @@ namespace Greenshot.Editor.Drawing
unsafe unsafe
{ {
var image = SixLabors.ImageSharp.Image.WrapMemory<Bgra32>((void*)bitmapData.Scan0, width: width, height: height); var image = SixLabors.ImageSharp.Image.WrapMemory<Bgra32>((void*)bitmapData.Scan0, width: width, height: height);
RenderEmoji(emoji, iconSize, image); RenderEmoji(emoji, iconSize, image, useSystemFont);
} }
} }
finally finally
@ -75,7 +85,7 @@ namespace Greenshot.Editor.Drawing
return bitmap; return bitmap;
} }
public static BitmapSource GetBitmapSource(string emoji, int iconSize) public static BitmapSource GetBitmapSource(string emoji, int iconSize, bool useSystemFont)
{ {
var pixelFormat = PixelFormats.Bgra32; var pixelFormat = PixelFormats.Bgra32;
int width = iconSize; int width = iconSize;
@ -85,7 +95,7 @@ namespace Greenshot.Editor.Drawing
var image = SixLabors.ImageSharp.Image.WrapMemory<Bgra32>(byteMemory: pixels, width: width, height: height); var image = SixLabors.ImageSharp.Image.WrapMemory<Bgra32>(byteMemory: pixels, width: width, height: height);
RenderEmoji(emoji, iconSize, image); RenderEmoji(emoji, iconSize, image, useSystemFont);
var source = BitmapSource.Create(pixelWidth: width, pixelHeight: height, dpiX: 96, dpiY: 96, pixelFormat: pixelFormat, palette: null, pixels: pixels, stride: stride); var source = BitmapSource.Create(pixelWidth: width, pixelHeight: height, dpiX: 96, dpiY: 96, pixelFormat: pixelFormat, palette: null, pixels: pixels, stride: stride);
source.Freeze(); source.Freeze();

View file

@ -450,7 +450,7 @@ namespace Greenshot.Editor.Forms {
// //
this.btnEmoji.CheckOnClick = true; this.btnEmoji.CheckOnClick = true;
this.btnEmoji.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; this.btnEmoji.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
this.btnEmoji.Image = EmojiRenderer.GetBitmap("🙂", 32); this.btnEmoji.Image = EmojiRenderer.GetBitmap("🙂", 32, useSystemFont: false);
this.btnEmoji.ImageTransparentColor = System.Drawing.Color.Magenta; this.btnEmoji.ImageTransparentColor = System.Drawing.Color.Magenta;
this.btnEmoji.Text = "Emoji (M)"; this.btnEmoji.Text = "Emoji (M)";
this.btnEmoji.Name = "btnEmoji"; this.btnEmoji.Name = "btnEmoji";

View file

@ -86,7 +86,14 @@
<EmbeddedResource Update="Forms\ImageEditorForm.resx"> <EmbeddedResource Update="Forms\ImageEditorForm.resx">
<DependentUpon>ImageEditorForm.cs</DependentUpon> <DependentUpon>ImageEditorForm.cs</DependentUpon>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Resources\emoji-test.txt" /> </ItemGroup>
<EmbeddedResource Include="Resources\TwemojiMozilla.ttf" />
<ItemGroup>
<None Update="Resources\emoji-test.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\TwemojiMozilla.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
</Project> </Project>