From 81a892ad111f139a37cf414ec75eed544d787008 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 4 Dec 2022 13:07:50 +0000 Subject: [PATCH 1/6] Add image scenario --- UICatalog/Scenarios/Images.cs | 123 ++++++++++++++++++++++++++++++++++ UICatalog/UICatalog.csproj | 1 + 2 files changed, 124 insertions(+) create mode 100644 UICatalog/Scenarios/Images.cs diff --git a/UICatalog/Scenarios/Images.cs b/UICatalog/Scenarios/Images.cs new file mode 100644 index 000000000..d0c671b1b --- /dev/null +++ b/UICatalog/Scenarios/Images.cs @@ -0,0 +1,123 @@ +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using Terminal.Gui; +using Attribute = Terminal.Gui.Attribute; + +namespace UICatalog.Scenarios { + [ScenarioMetadata (Name: "Images", Description: "Demonstration of how to render an image with/without true color support.")] + [ScenarioCategory ("Colors")] + public class Images : Scenario + { + public override void Setup () + { + base.Setup (); + + var x = 0; + var y = 0; + + var canTrueColor = Application.Driver.SupportsTrueColorOutput; + + var lblDriverName = new Label ($"Current driver is {Application.Driver.GetType ().Name}") { + X = x, + Y = y++ + }; + Win.Add (lblDriverName); + y++; + + var cbSupportsTrueColor = new CheckBox ("Driver supports true color ") { + X = x, + Y = y++, + Checked = canTrueColor, + CanFocus = false + }; + Win.Add (cbSupportsTrueColor); + + var cbUseTrueColor = new CheckBox ("Use true color") { + X = x, + Y = y++, + Checked = Application.Driver.UseTrueColor, + Enabled = canTrueColor, + }; + cbUseTrueColor.Toggled += (_) => Application.Driver.UseTrueColor = cbUseTrueColor.Checked; + Win.Add (cbUseTrueColor); + + var btnOpenImage = new Button ("Open Image") { + X = x, + Y = y++ + }; + Win.Add (btnOpenImage); + + var imageView = new ImageView () { + X = x, + Y = y++, + Width = Dim.Fill(), + Height = Dim.Fill(), + }; + Win.Add (imageView); + + btnOpenImage.Clicked += () => { + var ofd = new OpenDialog ("Open Image", "Image"); + Application.Run (ofd); + + var path = ofd.FilePath.ToString (); + + if (string.IsNullOrWhiteSpace (path)) { + return; + } + + if(!File.Exists(path)) { + return; + } + + imageView.SetImage(Image.Load (File.ReadAllBytes (path))); + }; + } + + class ImageView : View { + + private Image fullResImage; + private Image matchSize; + + ConcurrentDictionary cache = new ConcurrentDictionary(); + + internal void SetImage (Image image) + { + fullResImage = image; + this.SetNeedsDisplay (); + } + + public override void Redraw (Rect bounds) + { + base.Redraw (bounds); + + if (fullResImage == null) { + return; + } + + // if we have not got a cached resized image of this size + if(matchSize == null || bounds.Width != matchSize.Width || bounds.Height != matchSize.Height) { + + // generate one + matchSize = fullResImage.Clone (x => x.Resize (bounds.Width, bounds.Height)); + } + + for (int y = 0; y < bounds.Height; y++) { + for (int x = 0; x < bounds.Width; x++) { + var rgb = matchSize [x, y]; + + var attr = cache.GetOrAdd (rgb, (rgb) => new Attribute (new TrueColor (), new TrueColor (rgb.R, rgb.G, rgb.B))); + + Driver.SetAttribute(attr); + AddRune (x, y, ' '); + } + } + } + } + } +} \ No newline at end of file diff --git a/UICatalog/UICatalog.csproj b/UICatalog/UICatalog.csproj index 6320c1e99..84f0cdb07 100644 --- a/UICatalog/UICatalog.csproj +++ b/UICatalog/UICatalog.csproj @@ -19,6 +19,7 @@ TRACE;DEBUG_IDISPOSABLE + From c28d97f203334932c18a88a0de984f7587755907 Mon Sep 17 00:00:00 2001 From: tznind Date: Fri, 17 Feb 2023 21:19:41 +0000 Subject: [PATCH 2/6] Add gif --- .../Scenarios/Spinning_globe_dark_small.gif | Bin 0 -> 22869 bytes .../Scenarios/spinning-globe-attribution.txt | 3 +++ 2 files changed, 3 insertions(+) create mode 100644 UICatalog/Scenarios/Spinning_globe_dark_small.gif create mode 100644 UICatalog/Scenarios/spinning-globe-attribution.txt diff --git a/UICatalog/Scenarios/Spinning_globe_dark_small.gif b/UICatalog/Scenarios/Spinning_globe_dark_small.gif new file mode 100644 index 0000000000000000000000000000000000000000..2618d2cced8f2ee73eaa86bf601cf167da166686 GIT binary patch literal 22869 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 Date: Fri, 17 Feb 2023 22:56:02 +0000 Subject: [PATCH 3/6] spinning world --- UICatalog/Scenarios/Animation.cs | 192 +++++++++++++++++++++++++++++++ UICatalog/Scenarios/Images.cs | 123 -------------------- UICatalog/UICatalog.csproj | 3 + 3 files changed, 195 insertions(+), 123 deletions(-) create mode 100644 UICatalog/Scenarios/Animation.cs delete mode 100644 UICatalog/Scenarios/Images.cs diff --git a/UICatalog/Scenarios/Animation.cs b/UICatalog/Scenarios/Animation.cs new file mode 100644 index 000000000..231b0f399 --- /dev/null +++ b/UICatalog/Scenarios/Animation.cs @@ -0,0 +1,192 @@ +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using Terminal.Gui; +using Attribute = Terminal.Gui.Attribute; + +namespace UICatalog.Scenarios { + [ScenarioMetadata (Name: "Animation", Description: "Demonstration of how to render animated images with threading.")] + [ScenarioCategory ("Colors")] + public class Animation : Scenario + { + private bool isDisposed; + + public override void Setup () + { + base.Setup (); + + var x = 0; + var y = 0; + + var imageView = new ImageView () { + X = x, + Y = y++, + Width = Dim.Fill(), + Height = Dim.Fill(), + }; + Win.Add (imageView); + + var dir = new DirectoryInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); + + var f = new FileInfo( + Path.Combine(dir.FullName,"Scenarios","Spinning_globe_dark_small.gif")); + if(!f.Exists) + { + MessageBox.ErrorQuery("Could not find gif","Could not find "+ f.FullName,"Ok"); + return; + } + + imageView.SetImage(Image.Load (File.ReadAllBytes (f.FullName))); + + Task.Run(()=>{ + while(!isDisposed) + { + Application.MainLoop.Invoke(()=> + { + imageView.NextFrame(); + imageView.SetNeedsDisplay(); + }); + + Task.Delay(100).Wait(); + } + }); + } + + protected override void Dispose(bool disposing) + { + isDisposed = true; + base.Dispose(); + } + + // This is a C# port of https://github.com/andraaspar/bitmap-to-braille by Andraaspar + + /// + /// Renders an image as unicode Braille. + /// + public class BitmapToBraille + { + + public const int CHAR_WIDTH = 2; + public const int CHAR_HEIGHT = 4; + + const string CHARS = " ⠁⠂⠃⠄⠅⠆⠇⡀⡁⡂⡃⡄⡅⡆⡇⠈⠉⠊⠋⠌⠍⠎⠏⡈⡉⡊⡋⡌⡍⡎⡏⠐⠑⠒⠓⠔⠕⠖⠗⡐⡑⡒⡓⡔⡕⡖⡗⠘⠙⠚⠛⠜⠝⠞⠟⡘⡙⡚⡛⡜⡝⡞⡟⠠⠡⠢⠣⠤⠥⠦⠧⡠⡡⡢⡣⡤⡥⡦⡧⠨⠩⠪⠫⠬⠭⠮⠯⡨⡩⡪⡫⡬⡭⡮⡯⠰⠱⠲⠳⠴⠵⠶⠷⡰⡱⡲⡳⡴⡵⡶⡷⠸⠹⠺⠻⠼⠽⠾⠿⡸⡹⡺⡻⡼⡽⡾⡿⢀⢁⢂⢃⢄⢅⢆⢇⣀⣁⣂⣃⣄⣅⣆⣇⢈⢉⢊⢋⢌⢍⢎⢏⣈⣉⣊⣋⣌⣍⣎⣏⢐⢑⢒⢓⢔⢕⢖⢗⣐⣑⣒⣓⣔⣕⣖⣗⢘⢙⢚⢛⢜⢝⢞⢟⣘⣙⣚⣛⣜⣝⣞⣟⢠⢡⢢⢣⢤⢥⢦⢧⣠⣡⣢⣣⣤⣥⣦⣧⢨⢩⢪⢫⢬⢭⢮⢯⣨⣩⣪⣫⣬⣭⣮⣯⢰⢱⢲⢳⢴⢵⢶⢷⣰⣱⣲⣳⣴⣵⣶⣷⢸⢹⢺⢻⢼⢽⢾⢿⣸⣹⣺⣻⣼⣽⣾⣿"; + + public int WidthPixels {get; } + public int HeightPixels { get; } + + public Func PixelIsLit {get;} + + public BitmapToBraille (int widthPixels, int heightPixels, Func pixelIsLit) + { + WidthPixels = widthPixels; + HeightPixels = heightPixels; + PixelIsLit = pixelIsLit; + } + + public string GenerateImage() { + int imageHeightChars = (int) Math.Ceiling((double)HeightPixels / CHAR_HEIGHT); + int imageWidthChars = (int) Math.Ceiling((double)WidthPixels / CHAR_WIDTH); + + var result = new StringBuilder(); + + for (int y = 0; y < imageHeightChars; y++) { + + for (int x = 0; x < imageWidthChars; x++) { + int baseX = x * CHAR_WIDTH; + int baseY = y * CHAR_HEIGHT; + + int charIndex = 0; + int value = 1; + + for (int charX = 0; charX < CHAR_WIDTH; charX++) { + for (int charY = 0; charY < CHAR_HEIGHT; charY++) { + int bitmapX = baseX + charX; + int bitmapY = baseY + charY; + bool pixelExists = bitmapX < WidthPixels && bitmapY < HeightPixels; + + if (pixelExists && PixelIsLit(bitmapX, bitmapY)) { + charIndex += value; + } + value *= 2; + } + } + + result.Append(CHARS[charIndex]); + } + result.Append('\n'); + } + return result.ToString().TrimEnd(); + } + } + + class ImageView : View { + private int frameCount; + private int currentFrame = 0; + + private Image[] fullResImages; + private Image[] matchSizes; + + internal void SetImage (Image image) + { + frameCount = image.Frames.Count; + + fullResImages = new Image[frameCount]; + + for(int i=0;iIsLit(img,x,y)); + + var pix = braille.GenerateImage(); + return pix.Split('\n'); + } + + private bool IsLit (Image img, int x, int y) + { + var rgb = img[x,y]; + return rgb.R + rgb.G + rgb.B > 50; + } + } + } +} \ No newline at end of file diff --git a/UICatalog/Scenarios/Images.cs b/UICatalog/Scenarios/Images.cs deleted file mode 100644 index d0c671b1b..000000000 --- a/UICatalog/Scenarios/Images.cs +++ /dev/null @@ -1,123 +0,0 @@ -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using Terminal.Gui; -using Attribute = Terminal.Gui.Attribute; - -namespace UICatalog.Scenarios { - [ScenarioMetadata (Name: "Images", Description: "Demonstration of how to render an image with/without true color support.")] - [ScenarioCategory ("Colors")] - public class Images : Scenario - { - public override void Setup () - { - base.Setup (); - - var x = 0; - var y = 0; - - var canTrueColor = Application.Driver.SupportsTrueColorOutput; - - var lblDriverName = new Label ($"Current driver is {Application.Driver.GetType ().Name}") { - X = x, - Y = y++ - }; - Win.Add (lblDriverName); - y++; - - var cbSupportsTrueColor = new CheckBox ("Driver supports true color ") { - X = x, - Y = y++, - Checked = canTrueColor, - CanFocus = false - }; - Win.Add (cbSupportsTrueColor); - - var cbUseTrueColor = new CheckBox ("Use true color") { - X = x, - Y = y++, - Checked = Application.Driver.UseTrueColor, - Enabled = canTrueColor, - }; - cbUseTrueColor.Toggled += (_) => Application.Driver.UseTrueColor = cbUseTrueColor.Checked; - Win.Add (cbUseTrueColor); - - var btnOpenImage = new Button ("Open Image") { - X = x, - Y = y++ - }; - Win.Add (btnOpenImage); - - var imageView = new ImageView () { - X = x, - Y = y++, - Width = Dim.Fill(), - Height = Dim.Fill(), - }; - Win.Add (imageView); - - btnOpenImage.Clicked += () => { - var ofd = new OpenDialog ("Open Image", "Image"); - Application.Run (ofd); - - var path = ofd.FilePath.ToString (); - - if (string.IsNullOrWhiteSpace (path)) { - return; - } - - if(!File.Exists(path)) { - return; - } - - imageView.SetImage(Image.Load (File.ReadAllBytes (path))); - }; - } - - class ImageView : View { - - private Image fullResImage; - private Image matchSize; - - ConcurrentDictionary cache = new ConcurrentDictionary(); - - internal void SetImage (Image image) - { - fullResImage = image; - this.SetNeedsDisplay (); - } - - public override void Redraw (Rect bounds) - { - base.Redraw (bounds); - - if (fullResImage == null) { - return; - } - - // if we have not got a cached resized image of this size - if(matchSize == null || bounds.Width != matchSize.Width || bounds.Height != matchSize.Height) { - - // generate one - matchSize = fullResImage.Clone (x => x.Resize (bounds.Width, bounds.Height)); - } - - for (int y = 0; y < bounds.Height; y++) { - for (int x = 0; x < bounds.Width; x++) { - var rgb = matchSize [x, y]; - - var attr = cache.GetOrAdd (rgb, (rgb) => new Attribute (new TrueColor (), new TrueColor (rgb.R, rgb.G, rgb.B))); - - Driver.SetAttribute(attr); - AddRune (x, y, ' '); - } - } - } - } - } -} \ No newline at end of file diff --git a/UICatalog/UICatalog.csproj b/UICatalog/UICatalog.csproj index 84f0cdb07..4cc046017 100644 --- a/UICatalog/UICatalog.csproj +++ b/UICatalog/UICatalog.csproj @@ -18,6 +18,9 @@ TRACE;DEBUG_IDISPOSABLE + + + From 2b0d14f41f96d5e0c14d2ef80141049b2d024d67 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 17 Feb 2023 23:12:48 +0000 Subject: [PATCH 4/6] Resizing --- UICatalog/Scenarios/Animation.cs | 36 +++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/UICatalog/Scenarios/Animation.cs b/UICatalog/Scenarios/Animation.cs index 231b0f399..ececdb508 100644 --- a/UICatalog/Scenarios/Animation.cs +++ b/UICatalog/Scenarios/Animation.cs @@ -134,11 +134,15 @@ namespace UICatalog.Scenarios { private Image[] fullResImages; private Image[] matchSizes; + Rect oldSize = Rect.Empty; + + internal void SetImage (Image image) { frameCount = image.Frames.Count; fullResImages = new Image[frameCount]; + matchSizes = new Image[frameCount]; for(int i=0;i[frameCount]; + oldSize = bounds; + } + + var imgScaled = matchSizes[currentFrame]; + + if(imgScaled == null) + { + var imgFull = fullResImages[currentFrame]; - var lines = GetBraille(); + // keep aspect ratio + var newSize = Math.Min(bounds.Width,bounds.Height); + + // generate one + matchSizes[currentFrame] = imgFull.Clone ( + x => x.Resize ( + newSize * BitmapToBraille.CHAR_HEIGHT, + newSize * BitmapToBraille.CHAR_HEIGHT)); + + } + + var lines = GetBraille(matchSizes[currentFrame]); for(int y = 0; y < lines.Length;y++) { @@ -169,10 +197,8 @@ namespace UICatalog.Scenarios { } } - private string[] GetBraille () + private string[] GetBraille (Image img) { - var img = fullResImages[currentFrame]; - var braille = new BitmapToBraille( img.Width, img.Height, From 8371a74bf94921939d5e394e0324219f0c8118f3 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 17 Feb 2023 23:18:20 +0000 Subject: [PATCH 5/6] CC attribution --- UICatalog/Scenarios/Animation.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/UICatalog/Scenarios/Animation.cs b/UICatalog/Scenarios/Animation.cs index ececdb508..783990e04 100644 --- a/UICatalog/Scenarios/Animation.cs +++ b/UICatalog/Scenarios/Animation.cs @@ -23,17 +23,24 @@ namespace UICatalog.Scenarios { { base.Setup (); - var x = 0; - var y = 0; var imageView = new ImageView () { - X = x, - Y = y++, Width = Dim.Fill(), - Height = Dim.Fill(), + Height = Dim.Fill()-2, }; + Win.Add (imageView); + var lbl = new Label("Image by Wikiscient"){ + Y = Pos.AnchorEnd(2) + }; + Win.Add(lbl); + + var lbl2 = new Label("https://commons.wikimedia.org/wiki/File:Spinning_globe.gif"){ + Y = Pos.AnchorEnd(1) + }; + Win.Add(lbl2); + var dir = new DirectoryInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); var f = new FileInfo( From 0e84090c31451fa4af82db7427c96d9a6ebb46a0 Mon Sep 17 00:00:00 2001 From: Thomas Date: Sat, 18 Feb 2023 07:56:45 +0000 Subject: [PATCH 6/6] Add caching and comments --- UICatalog/Scenarios/Animation.cs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/UICatalog/Scenarios/Animation.cs b/UICatalog/Scenarios/Animation.cs index 783990e04..6f25a293f 100644 --- a/UICatalog/Scenarios/Animation.cs +++ b/UICatalog/Scenarios/Animation.cs @@ -56,6 +56,7 @@ namespace UICatalog.Scenarios { Task.Run(()=>{ while(!isDisposed) { + // When updating from a Thread/Task always use Invoke Application.MainLoop.Invoke(()=> { imageView.NextFrame(); @@ -140,6 +141,7 @@ namespace UICatalog.Scenarios { private Image[] fullResImages; private Image[] matchSizes; + private string[] brailleCache; Rect oldSize = Rect.Empty; @@ -150,6 +152,7 @@ namespace UICatalog.Scenarios { fullResImages = new Image[frameCount]; matchSizes = new Image[frameCount]; + brailleCache = new string[frameCount]; for(int i=0;i[frameCount]; + brailleCache = new string[frameCount]; oldSize = bounds; } var imgScaled = matchSizes[currentFrame]; + var braille = brailleCache[currentFrame]; if(imgScaled == null) { @@ -185,14 +190,19 @@ namespace UICatalog.Scenarios { var newSize = Math.Min(bounds.Width,bounds.Height); // generate one - matchSizes[currentFrame] = imgFull.Clone ( + matchSizes[currentFrame] = imgScaled = imgFull.Clone ( x => x.Resize ( newSize * BitmapToBraille.CHAR_HEIGHT, newSize * BitmapToBraille.CHAR_HEIGHT)); - } - var lines = GetBraille(matchSizes[currentFrame]); + if(braille == null) + { + brailleCache[currentFrame] = braille = GetBraille(matchSizes[currentFrame]); + } + + + var lines = braille.Split('\n'); for(int y = 0; y < lines.Length;y++) { @@ -204,15 +214,14 @@ namespace UICatalog.Scenarios { } } - private string[] GetBraille (Image img) + private string GetBraille (Image img) { var braille = new BitmapToBraille( img.Width, img.Height, (x,y)=>IsLit(img,x,y)); - var pix = braille.GenerateImage(); - return pix.Split('\n'); + return braille.GenerateImage(); } private bool IsLit (Image img, int x, int y)