I have a WPF .NET 3.5 app that draws transparent tile layers on top of one another using an ImageDrawing and DrawingGroup.
I'm trying to convert this for Windows 8 using Metro.
Microsoft's porting overview at http://msdn.microsoft.com/en-us/library/windows/apps/hh465136.aspx suggests changing System.Windows namespace references to Windows.UI.Xaml. This helped with most things, but I haven't found ImageDrawing and DrawingGroup and didn't find any mention of them in Windows.UI.Xaml.
Do they exist in Metro? If so, where, or if not, what can I use to accomplish an equivalent task?
Here's an example code I'm using:
Windows.UI.Xaml.Controls.Image img = mapGrid.Children.Cast<Windows.UI.Xaml.Controls.Image>().First(e => Grid.GetRow(e) == 2 && Grid.GetColumn(e) == 4);
if (img != null)
{
Uri back = new Uri("tiles/green.png", UriKind.RelativeOrAbsolute);
Windows.UI.Xaml.Media.ImageDrawing background = new Windows.UI.Xaml.Media.ImageDrawing(new Windows.UI.Xaml.Media.Imaging.BitmapImage(back), new Rect(0, 0, 32, 32));
Windows.UI.Xaml.Media.DrawingGroup myDrawingGroup = new Windows.UI.Xaml.Media.DrawingGroup();
myDrawingGroup.Children.Add(background);
// Add one or more foreground tiles here, removed for brevity.
}
Fully qualified namespaces for clarity (I hope).
Related
I have an Ionic / Cordova application hosted in the Windows UWP application, and which I am looking into swapping to host within a WPF application (latest .net, eg 6.0), and using WebView2.
Note, the Ionic/Cordova part is not really relevant to this question, this is purely related to WPF.
When running on a Tablet (eg Microsoft surface), I need to resize the app when the soft keyboard is shown, and hidden.
When in UWP, I could hook into the following events in my TypeScript file...
let w = <any>window;
const inputPane = w.Windows.UI.ViewManagement.InputPane.getForCurrentView();
if (!inputPane) {
this.logger.error('WindowsKeyboardService.hookupKeyboardHandlers: could not get inputPane');
return;
}
inputPane.addEventListener('showing', _ => this.onWindowsKeyboardUp);
inputPane.addEventListener('hiding', _ => this.onWindowsKeyboardClose);
So I won't have the WinJS any longer in the WPF, so I will do all the native in the WPF and then call into the JS myself using the appropriate API on the webview.
If I was in UWP, I could do something like the following:
System.Windows.UI.ViewManagement.InputPane.GetForCurrentView().Showing += (s, args) =>
{
GeneralTransform gt = loginButton.TransformToVisual(this);
Point buttonPoint = gt.TransformPoint(new Point(0, loginButton.RenderSize.Height - 1));
var trans = new TranslateTransform { Y = -(buttonPoint.Y - args.OccludedRect.Top) };
loginButton.RenderTransform = trans;
args.EnsuredFocusedElementInView = true;
};
But in WPF, I do not seem to have the `System.Windows.UI namespace:
Is there an equivalent way of doing this within a WPF application?
Update 1
I found this sample code
The whole solution will build in .net framework (4.7), but not in .net 6, as still missing the namespace Windows.UI. Perhaps this is renamed to something?
Update 2
I create a new WinUI project. Calling
var pane = Windows.UI.ViewManagement.InputPane.GetForCurrentView();
gives the same Element Not found error. I call this in a button click event, to give the main app/Window plenty of time to be fully initialized.
Note I am trying this out running from Visual Studio (i.e. Desktop Windows 10), and not on an actual tablet at this stage.
I this similar post where there is a comment
#LeftTwixWand ApplicationView.GetForCurrentView and CoreApplication.GetCurrentView() are only for UWP apps. For WinUI 3 desktop apps, use the Window class for some of the functionality. I'm not completely sure but some of them also now a GetForWindowId method.
It mentions using the Window class, but there is nothing on how to do what I am after here (monitoring the soft keyboard show/hide events).
Update 3
Following #Victor below, I added the code and it asks me to install
#Victor is this correct?
For WPF you just need to use net6.0-windows10.0.17763.0 target framework or newer. APIs will be available for you via existing Interop classes. Do not use System.Runtime.InteropServices.WindowsRuntime, it is .net framework approach.
IntPtr handle = new WindowInteropHelper(window).Handle;
InputPane inputPane = InputPaneInterop.GetForWindow(handle);
I have an image in my UWP c# project, that is a transparent png with white foreground. I now want to change the white color from this png image into another color (like blueish).
Example (note that the colored image does not have a transparent background. This is due bad image processing software I'm using and to demonstrate the change of the white color. The Background should be transparent in the end result).
I remember, that this was possible in unity, now I want to do this now in an uwp-app. I thought about using the Lumia ImagingSDK or maybe the Composition API, but do not know, hot to do it with either those.
A way you could do this is using the Composition effect system.
Prerequisites
Targeting at least build 10586 (the Composition API was experimental before this).
While not strictly required, having a basic understanding of the Visual Layer wouldn't hurt. I wrote a blog post that is an introduction to this topic here.
Adding the Win2D nuget package.
Additionally you can look to a gist I wrote here, which is a quick way to get up and running using the Composition API within a XAML app. It demos using an effect as well. Not only that, but it also covers loading an image using the Composition API (with a package I wrote).
Gettings Started
You'll want to do something very similar to the gist, but instead of defining an InvertEffect, you'll want to define both a CompositeEffect and a ColorSourceEffect. What this will do is take an image and use it as a "mask", and then replaces the white in the image with a color. You would define the effect like this:
IGraphicsEffect graphicsEffect = new CompositeEffect
{
Mode = Microsoft.Graphics.Canvas.CanvasComposite.DestinationIn,
Sources =
{
new ColorSourceEffect
{
Name = "colorSource",
Color = Color.FromArgb(255, 255, 255, 255)
},
new CompositionEffectSourceParameter("mask")
}
};
The next step is to create an effect factory:
var effectFactory = compositor.CreateEffectFactory(graphicsEffect, new string[] { "colorSource.Color" });
The second parameter, while not required, is probably what you want in this case. Setting this parameter allows you to change the property after the effect has been compiled, which allows you to set it manually and every new effect brush you create or to animate that property on an effect brush. We'll just be setting it manually. Use your new effect factory to create a new effect brush. Note that this factory can create many new effect brushes with the definition you used above:
var effectBrush = effectFactory.CreateBrush();
However, first you'll need to apply your image as the mask. You can load your image into a surface using a library I wrote called CompositionImageLoader. You can also download it on nuget. After creating a surface with your image, create a CompositionSurfaceBrush and apply it to an effect.
var imageLoader = ImageLoaderFactory.CreateImageLoader(compositor);
var surface = imageLoader.LoadImageFromUri(new Uri("ms-appx:///Assets/Images/HAvng.png"));
var brush = compositor.CreateSurfaceBrush(surface);
effectBrush.SetSourceParameter("mask", brush);
Note that you should probably keep your ImageLoader somewhere, as creating one over and over again will be expensive. All that's left to do is apply the effect brush to a visual and set the color:
visual.Brush = effectBrush;
effectBrush.Properties.InsertColor("colorSource.Color", Colors.Red);
And then you're done! Note that if you want to change the color after this, all you have to do is call the same InsertColor method as above with a new color.
Final Product
In my test code, the method looked like this:
var compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
var visual = compositor.CreateSpriteVisual();
visual.Size = new Vector2(83, 86);
visual.Offset = new Vector3(50, 50, 0);
_imageLoader = ImageLoaderFactory.CreateImageLoader(compositor);
var surface = _imageLoader.LoadImageFromUri(new Uri("ms-appx:///Assets/Images/HAvng.png"));
var brush = compositor.CreateSurfaceBrush(surface);
IGraphicsEffect graphicsEffect = new CompositeEffect
{
Mode = Microsoft.Graphics.Canvas.CanvasComposite.DestinationIn,
Sources =
{
new ColorSourceEffect
{
Name = "colorSource",
Color = Color.FromArgb(255, 255, 255, 255)
},
new CompositionEffectSourceParameter("mask")
}
};
_effectFactory = compositor.CreateEffectFactory(graphicsEffect, new string[] { "colorSource.Color" });
var effectBrush = _effectFactory.CreateBrush();
effectBrush.SetSourceParameter("mask", brush);
visual.Brush = effectBrush;
effectBrush.Properties.InsertColor("colorSource.Color", Colors.Red);
ElementCompositionPreview.SetElementChildVisual(this, visual);
Note that in this example the visual was attached to this, which was my MainPage. You can attach it to any XAML element. If you'd like to see an example of a custom control that you can define in your XAML markup that creates and then resizes your visual as you resize the control, you can find that here.
To see more Composition related stuff, come on over to our GitHub page! We'll be happy to help you out with any questions you might have about the API.
I am not an experienced programmer, just need to add a DICOM viewer to my VS2010 project. I can display the image in Windows Forms, however can't figure out how to change the window center and width. Here is my script:
DicomImage image = new DicomImage(_filename);
int maxV = image.NumberOfFrames;
sbSlice.Maximum = maxV - 1;
image.WindowCenter = 7.0;
double wc = image.WindowCenter;
double ww = image.WindowWidth;
Image result = image.RenderImage(0);
DisplayImage(result);
It did not work. I don't know if this is the right approach.
The DicomImage class was not created with the intention of it being used to implement an image viewer. It was created to render preview images in the DICOM Dump utility and to test the image compression/decompression codecs. Maybe it was a mistake to include it in the library at all?
It is difficult for me to find fault in the code as being buggy when it is being used for something far beyond its intended functionality.
That said, I have taken some time to modify the code so that the WindowCenter/WindowWidth properties apply to the rendered image. You can find these modifications in the Git repo.
var img = new DicomImage(fileName);
img.WindowCenter = 2048.0;
img.WindowWidth = 4096.0;
DisplayImage(img.RenderImage(0));
I looked at the code and it looked extremely buggy. https://github.com/rcd/fo-dicom/blob/master/DICOM/Imaging/DicomImage.cs
In the current buggy implementation setting the WindowCenter or WindowWidth properties has no effect unless Dataset.Get(DicomTag.PhotometricInterpretation) is either Monochrome1 or Monochrome2 during Load(). This is already ridiculous, but it still cannot be used because the _renderOptions variable is only set in a single place and is immediately used for the _pipeline creation (not giving you chance to change it using the WindowCenter property). Your only chance is the grayscale _renderOptions initialization: _renderOptions = GrayscaleRenderOptions.FromDataset(Dataset);.
The current solution: Your dataset should have
DicomTag.WindowCenter set appropriately
DicomTag.WindowWidth != 0.0
DicomTag.PhotometricInterpretation == Monochrome1 or Monochrome2
The following code accomplishes that:
DicomDataset dataset = DicomFile.Open(fileName).Dataset;
//dataset.Set(DicomTag.WindowWidth, 200.0); //the WindowWidth must be non-zero
dataset.Add(DicomTag.WindowCenter, "100.0");
//dataset.Add(DicomTag.PhotometricInterpretation, "MONOCHROME1"); //ValueRepresentations tag is broken
dataset.Add(new DicomCodeString(DicomTag.PhotometricInterpretation, "MONOCHROME1"));
DicomImage image = new DicomImage(dataset);
image.RenderImage();
The best solution: Wait while this buggy library is fixed.
I need to convert the following method from C# to MonoTouch compliant code:
private WritableBitMap CreateBitMapWPF(RGBPaletteRecord rgbPalette, double dpi)
{
WritableBitMap bitmapImage = null;
try
{
bitmapImage = new WritableBitMap(TileRecord.PixelWidth, TileRecord.PixelHight, dpi, dpi, PixelFormats.Rgb24, BitmapPalettes.Halftone256);
// int nStride = (bitmapImage.PixelWidth * bitmapImage.Format.BitsPerPixel + 7) / 8;
int nStride = (bitmapImage.PixelWidth * bitmapImage.Format.BitsPerPixel / 8);
System.Windows.Int32Rect rect = new System.Windows.Int32Rect(0, 0, TileRecord.PixelWidth, TileRecord.PixelHight);
byte[] data = GetBytes(rgbPalette);
bitmapImage.WritePixels(rect, data, nStride, 0);
}
catch (Exception ex)
{
}
//// temp write to file to test
//using (FileStream stream5 = new FileStream(#"C:\test.bmp", FileMode.Create))
//{
// BitmapEncoder encoder5 = new BmpBitmapEncoder();
// encoder5.Frames.Add(BitmapFrame.Create(bitmapImage));
// encoder5.Save(stream5);
// stream5.Close();
//}
//
return bitmapImage;
}
I'm not asking for someone to do the actual conversion, but I'm wondering what the best approach to take would be when converting this code? Should I focus on converting to use the MonoTouch libraries? Standard System libraries? Any advice would be much appreciated.
Thanks a lot,
Tysin
EDIT; Basically the purpose of this conversion is that I have these C# classes that perform a set of functions to do with bitmapped images, I'm using MonoTouch as I need to use these files in an iPhone application.
what the best approach to take would be when converting this code?
It depends on your goals and application(s). If you plan to share code across products / versions then you better have an API that abstract the platform details and separate code for each platform.
Should I focus on converting to use the MonoTouch libraries?
The WPF API is not supported on iOS (or MonoTouch). So you're closest bet is to look at the iOS API that MonoTouch provides (i.e. look at what's already available before looking at 3rd party libraries).
You'll find similar features by using iOS CoreGraphics (which in general is pretty similar to the older System.Drawing model). For bitmap image you should read the documentation of the CGImage type.
MonoTouch's CGImage documentation (C#)
Apple CGImage documentation
you should also look at this project :
https://github.com/praeclarum/CrossGraphics
made by Frank Krueger (gg!) it enable you to share the same basic graphics code between platform. It's an API that abstract the details implementation of each platform. For now it seems to be limited only to basic graphics (like fillrect and so forth....
After several days of tracking down bizarre GDI+ errors, I've stumbled across this little gem on MSDN:
Classes within the System.Drawing namespace are not supported for use within a Windows or ASP.NET service. Attempting to use these classes from within one of these application types may produce unexpected problems, such as diminished service performance and run-time exceptions.
I don't know whether "ASP.NET service" means "web application" in this context, but "diminished service performance" certainly seems to cover the random assortment of "A generic error occurred in GDI+" and "Out of memory" errors that my app is throwing - intermittent, non-reproducible errors reading and writing JPEG images that - in many cases - were actually created by System.Drawing.Imaging in the first place.
So - if GDI+ can't read and write JPEG files reliably in a Web app, what should I be using instead?
I want users to be able to upload images (JPEG required, other formats nice-to-have), resample them reliably, and display useful error messages if anything goes wrong. Any ideas? Are the System.Media namespaces from WPF worth considering?
EDIT: Yeah, I know GDI+ works "most of the time". That's not good enough, because when it fails, it does so in a way that's impossible to isolate or recover from gracefully. I am not interested in examples of GDI+ code that works for you: I am looking for alternative libraries to use for image processing.
There is an excellent blog post including C# code about using the ImageMagick graphics library through Interop over at TopTen Software Blog. This post deals specifically with running ASP.net on linux under mono; however, the C# code should be perfectly copy-paste-able, the only thing you'll need to change is the Interop attributes if you are running under windows referencing a window binary (DLL).
ImageMagick® is a software suite to create, edit, compose, or convert
bitmap images. It can read and write images in a variety of formats
(over 100) including DPX, EXR, GIF, JPEG, JPEG-2000, PDF, PhotoCD,
PNG, Postscript, SVG, and TIFF. Use ImageMagick to resize, flip,
mirror, rotate, distort, shear and transform images, adjust image
colors, apply various special effects, or draw text, lines, polygons,
ellipses and Bézier curves.
There is also an ImageMagick .Net development project on codeplex that wraps up everything for you. But it doesn't show active development since 2009, so it may be lagging behind the current ImageMagick library version. For a small trivial resizing routine, I'd probably stick with the interop. You just need to watch your implementation carefully for your own memory leak or unreleased resources (the library itself is well tested and vetted by the community).
The library is free and open source. The Apache 2 license appears to be compatible with both personal and commercial purposes. See ImageMagick License Page.
The library is totally cross platform and implements many powerful image handling and transformation routines that are not found in GDI+ (or not implemented under mono) and has a good reputation as an alternative for ASP.net image processing.
Update: Looks like there is an updated version of a .NET wrapper here: http://magick.codeplex.com/
Yes, use the WPF System.Windows.Media classes. Being fully managed they don't suffer the same problems as the GDI stuff.
Here's an excerpt from some MVC code I use to render gradients, to give you an idea how to get from a WPF Visual to a PNG:
using System;
using System.IO;
using System.Web.Mvc;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace MyMvcWebApp.Controllers
{
public class ImageGenController : Controller
{
// GET: ~/ImageGen/Gradient?color1=red&color2=pink
[OutputCache(CacheProfile = "Image")]
public ActionResult Gradient(Color color1, Color color2, int width = 1, int height = 30, double angle = 90)
{
var visual = new DrawingVisual();
using (DrawingContext dc = visual.RenderOpen())
{
Brush brush = new LinearGradientBrush(color1, color2, angle);
dc.DrawRectangle(brush, null, new Rect(0, 0, width, height));
}
return new FileStreamResult(renderPng(visual, width, height), "image/png");
}
static Stream renderPng(Visual visual, int width, int height)
{
var rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Default);
rtb.Render(visual);
var frame = BitmapFrame.Create(rtb);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(frame);
var stream = new MemoryStream();
encoder.Save(stream);
stream.Position = 0;
return stream;
}
}
}
You can find a very good article from a Microsoft Employee here: Resizing images from the server using WPF/WIC instead of GDI+ that proposes to use WPF instead of GDI+. It's more about thumbnailing but it's overall the same issues.
Anyway, at the end it states this:
I contacted the WPF team to have the final word on whether this is
supported. Unfortunately, it's not, and the documentation is being
updated accordingly. I apologize about any confusion this may have
caused. We're looking at ways to make that story more acceptable in
the future.
So WPF is also unsupported in web apps and still is I believe :-S
ImageSharp
ImageSharp is an open-source cross-platform 2D graphics library. It's written in C# on top of the new .NET Standard, with no dependency on any OS-specific API.
It's currently still in pre-release on MyGet (you'll have to add the package source in the VS options or a NuGet.config file), but we are already using it with some very positive results.
Most of the issues I have read about pertain to resources not being disposed properly.
I have used variants of this code time and time again with no issues from web applications:
public void GenerateThumbNail(HttpPostedFile fil, string sPhysicalPath,
string sOrgFileName,string sThumbNailFileName,
System.Drawing.Imaging.ImageFormat oFormat, int rez)
{
try
{
System.Drawing.Image oImg = System.Drawing.Image.FromStream(fil.InputStream);
decimal pixtosubstract = 0;
decimal percentage;
//default
Size ThumbNailSizeToUse = new Size();
if (ThumbNailSize.Width < oImg.Size.Width || ThumbNailSize.Height < oImg.Size.Height)
{
if (oImg.Size.Width > oImg.Size.Height)
{
percentage = (((decimal)oImg.Size.Width - (decimal)ThumbNailSize.Width) / (decimal)oImg.Size.Width);
pixtosubstract = percentage * oImg.Size.Height;
ThumbNailSizeToUse.Width = ThumbNailSize.Width;
ThumbNailSizeToUse.Height = oImg.Size.Height - (int)pixtosubstract;
}
else
{
percentage = (((decimal)oImg.Size.Height - (decimal)ThumbNailSize.Height) / (decimal)oImg.Size.Height);
pixtosubstract = percentage * (decimal)oImg.Size.Width;
ThumbNailSizeToUse.Height = ThumbNailSize.Height;
ThumbNailSizeToUse.Width = oImg.Size.Width - (int)pixtosubstract;
}
}
else
{
ThumbNailSizeToUse.Width = oImg.Size.Width;
ThumbNailSizeToUse.Height = oImg.Size.Height;
}
Bitmap bmp = new Bitmap(ThumbNailSizeToUse.Width, ThumbNailSizeToUse.Height);
bmp.SetResolution(rez, rez);
System.Drawing.Image oThumbNail = bmp;
bmp = null;
Graphics oGraphic = Graphics.FromImage(oThumbNail);
oGraphic.CompositingQuality = CompositingQuality.HighQuality;
oGraphic.SmoothingMode = SmoothingMode.HighQuality;
oGraphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
Rectangle oRectangle = new Rectangle(0, 0, ThumbNailSizeToUse.Width, ThumbNailSizeToUse.Height);
oGraphic.DrawImage(oImg, oRectangle);
oThumbNail.Save(sPhysicalPath + sThumbNailFileName, oFormat);
oImg.Dispose();
}
catch (Exception ex)
{
Response.Write(ex.Message);
}
}
You may have a look at http://gd-sharp.sourceforge.net/ which is a wrapper for the GD library. I haven't tested it but it seems promising.
I've had good behavior from the Cairo library (http://www.cairographics.org) in an ASP.Net webserver environment. I actually moved to cairo from WPF due to WPF's poor memory usage model for web-based stuff.
WPF actually tends to run your worker process out of memory. None of the WPF objects implement IDisposable, and many of them reference unmanaged memory that's only freed via a finalizer. Heavy use of WPF (especially if your server is significantly CPU-taxed) will eventually run you out of memory because your finalizer queue gets saturated. When I was profiling my app, for instance, the finalization queue had upwards of 50,000 objects on it, many of them holding references to unmanaged memory. Cairo has behaved much better for me, and its memory usage pattern has been much more predictable than WPF's.
If you're interested in using cairo, grab the libs from GTK+'s website. They have an x86 as well as an x64 set of binaries.
The only downside is that cairo can't read/write JPG natively; however, you could easily adapt WPF's stuff for reading/writing JPG and do the resampling/scaling/drawing/whatever else using Cairo.
Aspose.Drawing is a drop-in replacement for System.Drawing that is fully managed and can be safely used in web applications. (I'm one of the developers.)