I've been trying to set the icon for my OpenTK window by using the ImageSharp library to load the image from my device and then converting the data to a byte array which I then set as the window icon using the WindowIcon method.
Although this did set the icon to something, it doesn't look anything like it should; it should be a comical picture of my cat. However, the result was three black horizontal lines on top of a grey and pink background.
...
If anyone could help me it would be greatly appreciated :)
(I'm using Visual Studio 2019 as my IDE with, of course, the language C#, and .NET Framework 5.0)
My code:
public static byte[] ImageToByteArray(string Icon)
{
var image = (Image<Rgba32>)SixLabors.ImageSharp.Image.Load(Configuration.Default, Icon);
image.Mutate(x => x.Flip(FlipMode.Vertical));
var pixels = new byte[4 * image.Width * image.Height];
image.CopyPixelDataTo(pixels);
return pixels;
}
public Game(int width = 1280, int height = 768, string title = "Window") :
base(
GameWindowSettings.Default,
new NativeWindowSettings()
{
Title = title,
Size = new Vector2i(width, height),
APIVersion = new Version(4, 6),
Icon = new WindowIcon(new OpenTK.Windowing.Common.Input.Image(100, 100, ImageToByteArray(#"C:\Users\xenon\Downloads\BobbilyIcon.png")))
})
{
this.CenterWindow();
}
Sadly, I can't directly include images since I am a new user, so I've attached links to a couple useful images concerning my problem below:
The picture of my cat which I am trying to set as the icon:
https://i.stack.imgur.com/uEMLk.jpg
The unexpected result:
https://i.stack.imgur.com/nvpdz.jpg
Okay, so apparently I'm a bit dumb. Turns out that when I am setting the icon, using the WindowIcon function, the height and width of the icon must match that of the image being used, which makes a lot of sense now that I think about it. I thought it was some incompatibility between the ImageSharp library used to load the image to memory and copy the data to the byte array and OpenTK.
Related
I'm trying to display an overlay during an auto login HTTP call.
I've found this code, which seems outdated somehow, but found nothing more recent.
Anyway, the Overlay is showing but not covering the whole screen as expected.
The calling code is this:
AppDelegate.FinishedLaunching
var avc = new AutoLoginViewController();
var navController = new UINavigationController(avc);
AutoLoginViewController.ViewDidLoad
var bounds = UIScreen.MainScreen.Bounds;
// show the loading overlay on the UI thread using the correct orientation sizing
loadPop = new LoadingOverlay(bounds, NSBundle.MainBundle.GetLocalizedString("connecting"));
View.Add(loadPop);
But the result is the following:
If I set a breakpoint in the LoadingOverlay constructor, I can see that the screen bounds (iPhone 6) are fine:
{{X=0,Y=0,Width=375,Height=667}}
public class LoadingOverlay : UIView
{
public LoadingOverlay(CGRect frame, string text) : base(frame)
{
// configurable bits
BackgroundColor = UIColor.Black;
Alpha = 0.75f;
AutoresizingMask = UIViewAutoresizing.FlexibleDimensions;
...
}
}
Clearly the UIView width is incorrect.
Because we're in 2020, maybe there is another way.
Any help appreciated.
EDIT: The app breaks on iPhone 8 iOS 13.3 simulator, So I can't say if this is tied to a particular screen size (1x in my case).
Cause :
It seems that you didn't set the LaunchImage , So whether on a simulator or real device , the value of bounds is a static value .
Solution:
The easiest way is set the size of overlay as bounds of View .
var bounds = View.Bounds;
Or you could set all size of LaunchImage of different screen .
anyone could tell me why this generate a black only animated gif?
the code also output each in memory generated gif to show that they are different
public static void Test()
{
Image<Rgba32> img = null;
Image<Rgba32> gif = null;
TextGraphicsOptions textGraphicsOptions = new TextGraphicsOptions(true);
SolidBrush<Rgba32> brushYellow = new SolidBrush<Rgba32>(Rgba32.Yellow);
FontCollection fonts = new FontCollection();
fonts.Install(fontLocation);
Font font = fonts.CreateFont("Liberation Mono", PngFontHeight, FontStyle.Regular);
gif = new Image<Rgba32>(400, 400);
for (int i = 0; i < 10;++i)
{
img = new Image<Rgba32>(400, 400);
img.Mutate(x => x.Fill(Rgba32.Black));
img.Mutate(x => x.DrawText(textGraphicsOptions, i.ToString(), font, brushYellow, new PointF(1,1)));
gif.Frames.AddFrame(img.Frames[0]);
using (FileStream fs = File.Create(Path.Join(Program.workingDirectory, string.Format("Test-{0}.gif", i))))
{
img.SaveAsGif(fs);
}
img.Dispose();
}
using (FileStream fs = File.Create(Path.Join(Program.workingDirectory, "Test.gif")))
{
gif.SaveAsGif(fs);
}
}
if I code it to load each individual physical file using this code it make the animated gif as expected.
I want to create the animated gif in memory only.
When you create an Image<>...
gif = new Image<Rgba32>(400, 400);
...gif.Frames[0] is a "transparent black" frame (each pixel's RGBA value is #00000000). The additional frames you create in your for loop and add with...
gif.Frames.AddFrame(img.Frames[0]);
...become gif.Frames[1] through gif.Frames[10], for a total of 11 frames.
The GIF encoder uses GifColorTableMode to decide if a color table is generated for each frame or the color table for the first frame is used for all frames. The combination of the default value, GifColorTableMode.Global, plus that first transparent frame results in an 11-frame .gif file with only one color, that same "transparent black". This is why your yellow text doesn't appear and every frame looks the same.
To solve this, at some point before you save the file you need to remove that initial transparent frame so it doesn't influence color table calculations and because it's not part of your animation, anyways...
gif.Frames.RemoveFrame(0);
You may also want to change to GifColorTableMode.Local so your .gif file contains color tables reflecting all of the colors rendered...
gif.MetaData.GetFormatMetaData(GifFormat.Instance).ColorTableMode = GifColorTableMode.Local;
...although your 10 frames each use almost the same set of colors so if file size is a greater concern than color representation you might leave that property alone. Generating your 400 × 400 animation with GifColorTableMode.Global produces a 9,835-byte file whereas GifColorTableMode.Local produces a 16,703-byte file; 70% bigger but I can't tell the difference between them.
Related issue on GitHub
By the way, since I found this along the way, if you wanted to change the duration of the animated frames you would do so using another GetFormatMetaData() method similar to the one shown above...
GifFrameMetaData frameMetaData = img.MetaData.GetFormatMetaData(GifFormat.Instance);
frameMetaData.FrameDelay = 100;// 1 second
I've searched a bit around the discussions\forums/StackOverflow/Official documentation, but i couldn't find much information about how to achieve what i'm trying. Most of the official documentation covers the command-line version of ImageMagick.
I'll describe what i'm trying to do:
I have a image loaded that i would like to paste into a larger one.
Ex: the image i loaded has 9920 width, 7085 height. I would like to place it in the middle of a larger one (10594 width, 7387 height). I do have all border calculation ready ([larger width - original width / 2] , same goes for height).
But i don't know how to do it using MagickImage. Here's the max i got:
private void drawInkzone(MagickImage loadedImage, List<string>inkzoneAreaInformation, string filePath)
{
unitConversion converter = new unitConversion();
List<double> inkZoneInfo = inkZoneListFill(inkzoneAreaInformation);
float DPI = getImageDPI(filePath);
double zoneAreaWidth_Pixels = converter.mmToPixel(inkZoneInfo.ElementAt(4), DPI);
double zoneAreaHeight_Pixels = converter.mmToPixel(inkZoneInfo.ElementAt(5), DPI);
using (MagickImage image = new MagickImage(MagickColor.FromRgb(255, 255, 255), Convert.ToInt32(zoneAreaWidth_Pixels), Convert.ToInt32(zoneAreaHeight_Pixels)))
{
//first: defining the larger image, with a white background (must be transparent, but for now its okay)
using (MagickImage original = loadedImage.Clone())
{
//Cloned the original image (already passed as parameter)
}
}
Here's the max i got. In order to achieve this, i used the following post:
How to process only one part of image by ImageMagick?
And i'm not using GDI+ because i'll be always working with larger TIFF files (big resolutions), and GDI+ tends to throw exceptions (Parameter not valid, out of memory) when it can't handle everything (i loaded three images with an resolution like that, and got out of memory).
Any help will be kindly appreciate, thanks.
Pablo.
You could either Composite the image on top of a new image with the required background or you could Clone and Extent if with the required background. In the answer from #Pablo Costa there is an example for Compositing the image so here is an example on how you could extent the image:
private void drawInkzone(MagickImage loadedImage, List<string> inkzoneAreaInformation, string filePath)
{
unitConversion converter = new unitConversion();
List<double> inkZoneInfo = inkZoneListFill(inkzoneAreaInformation);
float DPI = getImageDPI(filePath);
double zoneAreaWidth_Pixels = converter.mmToPixel(inkZoneInfo.ElementAt(4), DPI);
double zoneAreaHeight_Pixels = converter.mmToPixel(inkZoneInfo.ElementAt(5), DPI);
using (MagickImage image = loadedImage.Clone())
{
MagickColor background = MagickColors.Black;
int width = (int)zoneAreaWidth_Pixels;
int height = (int)zoneAreaHeight_Pixels;
image.Extent(width, height, Gravity.Center, background);
image.Write(#"C:\DI_PLOT\whatever.png");
}
}
I managed to accomplish what i needed.
Cool that i didn't had to calculate borders.
Here's the code:
private void drawInkzone(MagickImage loadedImage, List<string>inkzoneAreaInformation, string filePath)
{
unitConversion converter = new unitConversion();
List<double> inkZoneInfo = inkZoneListFill(inkzoneAreaInformation); //Larger image information
float DPI = getImageDPI(filePath);
double zoneAreaWidth_Pixels = converter.mmToPixel(inkZoneInfo.ElementAt(4), DPI); //Width and height for the larger image are in mm , converted them to pixel
double zoneAreaHeight_Pixels = converter.mmToPixel(inkZoneInfo.ElementAt(5), DPI);//Formula (is: mm * imageDPI) / 25.4
using (MagickImage image = new MagickImage(MagickColor.FromRgb(0, 0, 0), Convert.ToInt32(zoneAreaWidth_Pixels), Convert.ToInt32(zoneAreaHeight_Pixels)))
{
//first: defining the larger image, with a white background (must be transparent, but for now its okay)
using (MagickImage original = loadedImage.Clone())
{
//Cloned the original image (already passed as parameter)
image.Composite(loadedImage, Gravity.Center);
image.Write(#"C:\DI_PLOT\whatever.png");
}
}
Hope this helps someone :)
I am writing C# lib for very simple recognize image to use it in monodroid and also using zxing port to C#. But after I read image bytes from file I do such thing, same as in zxing barcode scanning.
binaryBitmap = new BinaryBitmap(new HybridBinarizer(new RGBLuminanceSource(rawRgb, width, height, format)));
But somehow it reverse image by vertical. I just saving binaryBitmap as bitmap to file by pixels.
Please help me understand why it's happen? What am I doing wrong?
#Michael am using Zxing.Net.Mobile port, from here https://github.com/Redth/ZXing.Net.Mobile. It's very weird for me it I am using PlanarYUVLuminanceSource - then I get such image http://i.imgur.com/OlwqC0I.png, but if I am using RGBLuminanceSource then I get full almost normal image, see example image. so now I have even 2 questions:
why planar take only part of image and have "layer on layer" effect? and
ok if I will use RGBLuminanceSource then, why it have some invertion of colors, I mean somewhere rectangles border is black and somewhere they are white. because it real image they all black?
UPDATE:
Here is how I get bytes from device and also as you see I set nv21 format, so it must be YUV, no? I wonder, what I am doing wrong that rgb source work(at list image is ok) and PLanarYUV not :((
BTW, original byte from preview frame have result and same file size.
Any suggestion?
public void OnPreviewFrame(byte[] bytes, Android.Hardware.Camera camera)
{
var img = new YuvImage(bytes, ImageFormatType.Nv21, cameraParameters.PreviewSize.Width, cameraParameters.PreviewSize.Height, null); string _fileName2 = "YUV_BYtes_"+ DateTime.Now.Ticks +".txt";
string pathToFile2 = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, _fileName2);
using (var fileStream = new FileStream(pathToFile2, FileMode.Append, FileAccess.Write, FileShare.None))
{
fileStream.Write(img.GetYuvData(), 0, img.GetYuvData().Length);
}
}
public void SurfaceChanged(ISurfaceHolder holder, global::Android.Graphics.Format format, int width, int height)
{
if (camera == null)
return;
var parameters = camera.GetParameters();
width = parameters.PreviewSize.Width;
height = parameters.PreviewSize.Height;
parameters.PreviewFormat = ImageFormatType.Nv21;
//parameters.PreviewFrameRate = 15;
//this.height = size.height;
//this.width = size.width;
//camera.setParameters( params );
//parameters.PreviewFormat = ImageFormatType.;
camera.SetParameters(parameters);
camera.SetDisplayOrientation(90);
camera.StartPreview();
cameraResolution = new Size(parameters.PreviewSize.Width, parameters.PreviewSize.Height);
AutoFocus();
}
I think I know what you have done. The data looks like RGB565 bitmap data (or something similar). You can't put such a byte array into the PlanarYUVLuminanceSource. You have to make sure that the byte array which you use with the planar source is really a array with only yuv data, not RGB565.
The rules are easy:
if you use the following code snippet
new RGBLuminanceSource(rawRgb, width, height, format)
make sure that the value of format matches the layout and data of the parameter rawRgb.
if you use somethin glike the following
new PlanarYUVLuminanceSource(yuvBytes, 640, 960, 0, 0, 640, 960, false);
make sure that yuvBytes only contains real yuv data.
I can only give a better answer if you post a more complete code sample.
I have a situation where I want to convert some XAML to an image, so I created a RichTextBox and then took the image of it. Now problem is that words in image is blurred, any idea how I might be able to fix it?
public System.Drawing.Bitmap ConvertXamltoImage(string XamlString, int Width, int Height)
{
RichTextBox AdContentRichTextBox = new RichTextBox() { Width = Width, Height = Height };
AdContentRichTextBox.BorderThickness = new Thickness(0);
XmlReader _XmlReader = XmlReader.Create(new StringReader(XamlString));
AdContentRichTextBox.Document = XamlString;
var size = new Size(Width, Height);
AdContentRichTextBox.Measure(size);
AdContentRichTextBox.Arrange(new Rect(size));
RenderTargetBitmap bmp = new RenderTargetBitmap(Width, Height, 300, 300, PixelFormats.Pbgra32);
bmp.Render(AdContentRichTextBox);
DrawingVisual _drawingVisual = new DrawingVisual();
using (DrawingContext _drwaingContext = _drawingVisual.RenderOpen())
{
VisualBrush _visualBrush = new VisualBrush(AdContentRichTextBox);
}
PngBitmapEncoder _png = new PngBitmapEncoder();
_png.Frames.Add(BitmapFrame.Create(bmp));
System.Drawing.Bitmap _tempBitmap = null;
using (Stream _fileStream = new MemoryStream())
{
_png.Save(_fileStream);
_tempBitmap = new System.Drawing.Bitmap(_fileStream);
_fileStream.Flush();
}
return _tempBitmap;
}
Hmmmm..there could be lots of things all interacting here:
1st
"Grayscale fall back - if ClearType is disabled or one is rendering text in certain situations where the ClearType algorithm cannot be run, WPF will use a grayscale rendering algorithm to antialias the rendered text."
Rendering Text to a RenderTargetBitmap seems to be one of those situations....(the renderer switches from a hardware to a software path).
2nd
In addition NET 4 switched the default scaling algorithm from high-quality (Fant) to low-quality (Bi-Linear).....now that shouldn't come into play here as it doesn't look like you are scaling the bitmap in any way...but you never know what's going on inside. It's possible to switch the scaler back to the higher quality one.
http://www.olsonsoft.com/blogs/stefanolson/post/Workaround-for-low-quality-bitmap-resizing-in-WPF-4.aspx
3rd
You may need to take into account the parent container of the RichTextBox...see last link below, mentions it can distort the font rendering.
Problems with rendering text as bitmaps using WPF
Some ideas on how to work around this are:
render the RichTextBox at a higher resolution e.g. 600dpi, and then scale down the bitmap (probably will make no difference)
capture the screen....difficult or not practical if your visual is offscreen/obscured, etc.
See related links:
http://windowsclient.net/wpf/white-papers/wpftextclarity.aspx
WPF RenderTargetBitmap downscaling text ClearType to GreyScale
WPF RenderTargetBitmap downscaling TextRenderMode to GreyScale
WPF text rendering inconsistencies