Cannot create Drop Shadow from Image created by Screen shot (capture) - c#

My code can make DropShadow for TextBlock but cannot create it for image if I create image from screen shot. Using normal image (Image source has been set already) problem do not occur.
I believe that problem must be in image format (somehow) I get from screen shot. Any way to convert SoftwareBitmap to other format or what to do?
(To test with TextBlock just replace Image with TextBlock in the first snippet)
Code to copy any element on the screen to image
public static async Task<Image> GetScreenShotFromElement(FrameworkElement TargetFrameworkElement)
{
Image RenderedImage = new Image();
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(TargetFrameworkElement);
RenderedImage.Source = renderTargetBitmap;
return RenderedImage;
}
Code to make drop shadow
public static void CreateDropShadowForImage(Image SOURCE,Grid SHADOWHERE)
{
Visual SOURCE_Visual = ElementCompositionPreview.GetElementVisual(SOURCE);
Compositor SOURCE_compositor = SOURCE_Visual.Compositor;
DropShadow DROP_SHADOW = SOURCE_compositor.CreateDropShadow();
DROP_SHADOW.Mask = SOURCE.GetAlphaMask();
DROP_SHADOW.Offset = new Vector3(10, 10, 0);
SpriteVisual SPRITE_VISUAL = SOURCE_compositor.CreateSpriteVisual();
SPRITE_VISUAL.Size = SOURCE.RenderSize.ToVector2();
SPRITE_VISUAL.Shadow = DROP_SHADOW;
ElementCompositionPreview.SetElementChildVisual(SHADOWHERE, SPRITE_VISUAL);
// Make sure size of shadow host and shadow visual always stay in sync
var bindSizeAnimation = SOURCE_compositor.CreateExpressionAnimation("hostVisual.Size");
bindSizeAnimation.SetReferenceParameter("hostVisual", SOURCE_Visual);
SPRITE_VISUAL.StartAnimation("Size", bindSizeAnimation);
}

Related

Xamarin.iOS Image on Image Watermark

How can I add a png image as a watermark to a larger image using Xamarin.iOS c# and save the output to the device?
I figured out the Xamarin.Android version from another question posted here.
Thanks in Advance!!
Using an image context, you can draw the original, then the watermark at the necessary location and obtain a new image from the context.
ImageContext example:
var originalImage = UIImage.FromBundle("buymore.jpg");
var watermarkImage = UIImage.FromFile("vs.png");
UIGraphics.BeginImageContextWithOptions(originalImage.Size, true, 1.0f);
originalImage.Draw(CGPoint.Empty);
watermarkImage.Draw(new CGRect(new CGPoint(200, 200), watermarkImage.Size));
var processedImage = UIGraphics.GetImageFromCurrentImageContext();
If your original and watermark images are the same size, you can use a CIFilter (CISourceOverCompositing) to "overlay" one image on top of another (assuming your watermark has a white or alpha background. This is my preferred method due to the speed.
CISourceOverCompositing example:
UIImage processedimage;
using (var filter = new CISourceOverCompositing())
{
filter.Image = new CIImage(UIImage.FromBundle("vs.png"));
filter.BackgroundImage = new CIImage(UIImage.FromBundle("buymore.jpg"));
processedimage = UIImage.FromImage(filter.OutputImage);
}

Win 2D producing different effect level when applied directly on a file and UIElement

I am applying Win2D gaussian blur on an Image File(Storage file) and directly on an UIElement - Image (Which is a XAML image) and I see that for the same value for the BlurAmount I am getting different output for the Storage file output and the XAML Image ..
Original Image
Output when 100% blur applied to XAML Image (Or any UIElement)
Output when 100% blur applied to a Storage file
Relevant Code :
For image File :
using (var stream = await originalFile.OpenAsync(FileAccessMode.Read))
{
var device = new CanvasDevice();
var bitmap = await CanvasBitmap.LoadAsync(device, stream);
var renderer = new CanvasRenderTarget(device, bitmap.SizeInPixels.Width, bitmap.SizeInPixels.Height, bitmap.Dpi);
using (var ds = renderer.CreateDrawingSession())
{
var blur = new GaussianBlurEffect();
blur.BlurAmount = eo.effectAmount1;
blur.BorderMode = EffectBorderMode.Hard;
blur.Source = bitmap;
ds.DrawImage(blur);
}
var saveFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync("ImageName.jpg", CreationCollisionOption.GenerateUniqueName);
using (var outStream = await saveFile.OpenAsync(FileAccessMode.ReadWrite))
{
await renderer.SaveAsync(outStream, CanvasBitmapFileFormat.Png,1.0f);
}
}
For UIElement(XAML Image) :
using (var stream = await sourceElement.RenderToRandomAccessStream())
{
var device = new CanvasDevice();
var bitmap = await CanvasBitmap.LoadAsync(device, stream);
var renderer = new CanvasRenderTarget(device,bitmap.SizeInPixels.Width,
bitmap.SizeInPixels.Height,
bitmap.Dpi);
using (var ds = renderer.CreateDrawingSession())
{
var blur = new GaussianBlurEffect();
blur.BlurAmount = blurAmount;
blur.Source = bitmap;
blur.BorderMode = EffectBorderMode.Hard;
ds.DrawImage(blur);
}
stream.Seek(0);
await renderer.SaveAsync(stream, CanvasBitmapFileFormat.Png);
}
Question : Is this the expected behaviour ? If yes, how can I make both the cases have the same output ? If no, what am I doing wrong ?
Can you try using bitmap.Size instead of bitmap.SizeInPixels?
var renderer = new CanvasRenderTarget(bitmap, bitmap.Size);
I'm guessing you're experiencing blur effect plus pixelation.
Edit 1
And if that doesn't work, put this assertion after you create your renderer. SizeInPixels needs to match:
Debug.Assert(
bitmap.SizeInPixels.Width == renderer.SizeInPixels.Width
&& bitmap.SizeInPixels.Height == renderer.SizeInPixels.Height
);
Edit 2
And if pixels are ok, try setting the StretchMode on the image element to None.
<Image Stretch="None" />
I'm just suspicious of pixelation somewhere in the visual tree.
Explanation
You need to accommodate for DPI scale. Your screen is probably set to 200%. So, if you have an Image control with dimensions 100x100, you actually need 200x200 pixel image to fill it. If you give it a 100x100 bitmap source, then it will stretch it to fit. This is why you get pixelation.
So, to solve your problem, either disable any stretching on the image OR resize it by the size of the source reduced by the DPI scale. That is, set the image width and height to 50x50 for a 100x100 pixel image at 200% DPI scale.
DPI scale can be obtained on the UI thread using:
var dpiScale = DisplayInformation.GetForCurrentView().LogicalDpi / 96f;

Create a screenshot of a WPF control in a background thread

we need to create WPF controls (say, a canvas with some Images on it) in a background thread and then take a screenshot of them. The controls must not be displayed.
I managed to create the control on a thread by making it an STA thread. Then I used the code from here http://blogs.msdn.com/b/swick/archive/2007/12/02/rendering-ink-and-image-to-a-bitmap-using-wpf.aspx to create the screenshot.
This doesn't work though: the control always has a size of 0 and therefore this crashes. Even if I specify a width and height manually it won't work, the saved image is always black.
Here's my code:
private void CreateScreenshotThread()
{
var image = CreateImage();
TakeScreenshot(image , #"e:\1.bmp");
}
I also tried to UpdateLayout() but without success. Do you have any idea how I can enforce a layout update and rendering of the control? I played around with PresentationSource but without success (don't fully understand the purpose of that class).
It is possible, the bit you are missing is possibly the measure and arrange of the control:
public MainWindow()
{
InitializeComponent();
var thread = new Thread(CreateScreenshot);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
private void CreateScreenshot()
{
Canvas c = new Canvas { Width = 100, Height = 100 };
c.Children.Add(new Rectangle { Height = 100, Width = 100, Fill = new SolidColorBrush(Colors.Red) });
var bitmap = new RenderTargetBitmap((int)c.Width, (int)c.Height, 120, 120, PixelFormats.Default);
c.Measure(new Size((int)c.Width, (int)c.Height));
c.Arrange(new Rect(new Size((int)c.ActualWidth, (int)c.ActualHeight)));
bitmap.Render(c);
var png = new PngBitmapEncoder();
png.Frames.Add(BitmapFrame.Create(bitmap));
using (Stream stm = File.Create("c:\\temp\\test.png"))
{
png.Save(stm);
}
}

How do I overlay an image in .NET

I have a .png image i wish to overlay on a base image.
My overlay image contains just a red slant line. I need to get the red line overlayed on the base image at the same co-ordinate as it is in overlay image.
The problem is I do not have the co-ordinates location.
I need to find it programmatically with C#. The overlay image will always be transparent or of white background. What code to find the line co-ordinates from overlay image?
You can create new image, render background image first and then render overlay image over it. Since overlay has alpha channel and line is placed where it should be (i mean there is opaque space on top and left side of line) you do not need coordinates. Illustration code:
Image imageBackground = Image.FromFile("bitmap1.png");
Image imageOverlay = Image.FromFile("bitmap2.png");
Image img = new Bitmap(imageBackground.Width, imageBackground.Height);
using (Graphics gr = Graphics.FromImage(img))
{
gr.DrawImage(imageBackground, new Point(0, 0));
gr.DrawImage(imageOverlay, new Point(0, 0));
}
img.Save("output.png", ImageFormat.Png);
If you are using WPF, why not use a path for your overlay instead of an image if it is a simple line? This would allow it to scale to any size, and has methods for manipulating its dimensions.
If you are using Winforms, there are some similar graphics drawing capabilities you might leverage. Getting the dimensions of the image should be easy, assuming you're using a PictureBox, the following properties should suffice:
myPictureBox.Top
myPictureBox.Bottom
myPictureBox.Left
myPictureBox.Right
Similarly, for a WPF Image:
myImage.Margin
I already needed to create a blank space around an image and I used the ImageFactory library to do that.
Here is the code. I guess you are capable to figure out how to adjust to your needs.
public static Image ResizedImage(Image image, int desiredWidth, int desiredHeight)
{
Image res = (Bitmap)image.Clone();
Image resizedImage;
ImageLayer imageLayer = new ImageLayer();
try
{
if (res != null)
{
//white background
res = new Bitmap(desiredWidth, desiredHeight, res.PixelFormat);
//image to handle
using (var imgf = new ImageFactory(true))
{
imgf
.Load(image)
.Resize(new ResizeLayer(new Size(desiredWidth, desiredHeight),
ResizeMode.Max,
AnchorPosition.Center,
false));
resizedImage = (Bitmap)imgf.Image.Clone();
}
//final image
if (resizedImage != null)
{
imageLayer.Image = resizedImage;
imageLayer.Size = new Size(resizedImage.Width, resizedImage.Height);
imageLayer.Opacity = 100;
using (var imgf = new ImageFactory(true))
{
imgf
.Load(res)
.BackgroundColor(Color.White)
.Overlay(imageLayer);
res = (Bitmap)imgf.Image.Clone();
}
}
}
}
catch (Exception ex)
{
ex.Message;
}
return res;
}

Replace Image with cropped BitmapSource

I have an Image on my wpf control
and I am trying to generate croped part of it - this is ok more or less.
I have used a codeproject solution to generate BitmapSource of croped image (http://www.codeproject.com/KB/WPF/CropAdorner.aspx) but when I am trying to
replace current image with generated BitmapSource like this
imgCurrent.Source = generatedBitmapSource;
I see very strange behaviour ((
I need an advice how to change current Image with new based on BitmapSource.
my XAML(there is nothing extraordinary - and by the right click I am trying to replace currentImage with croped):
<DockPanel Height="395" Width="926">
<!--Went with a DockPanel here so that the image would always be centered in its parent control.-->
<Image x:Name="imgCurrent" VerticalAlignment="Center" HorizontalAlignment="Center" MouseRightButtonDown="imgCurrent_MouseRightButtonDown"/>
</DockPanel>
right click:
private void imgCurrent_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
generatedBitmapSource = _clp.BpsCrop();
//this clears croping adonder
AdornerLayer aly = AdornerLayer.GetAdornerLayer(_felCur);
aly.Remove(_clp);
//
imageCurrent.Source = generatedBitmapSource;
}
croping method (from codeproject):
public BitmapSource BpsCrop()
{
Thickness margin = AdornerMargin();
Rect rcInterior = _prCropMask.RectInterior;
Point pxFromSize = UnitsToPx(rcInterior.Width, rcInterior.Height);
// It appears that CroppedBitmap indexes from the upper left of the margin whereas RenderTargetBitmap renders the
// control exclusive of the margin. Hence our need to take the margins into account here...
Point pxFromPos = UnitsToPx(rcInterior.Left + margin.Left, rcInterior.Top + margin.Top);
Point pxWhole = UnitsToPx(AdornedElement.RenderSize.Width + margin.Left, AdornedElement.RenderSize.Height + margin.Left);
pxFromSize.X = Math.Max(Math.Min(pxWhole.X - pxFromPos.X, pxFromSize.X), 0);
pxFromSize.Y = Math.Max(Math.Min(pxWhole.Y - pxFromPos.Y, pxFromSize.Y), 0);
if (pxFromSize.X == 0 || pxFromSize.Y == 0)
{
return null;
}
System.Windows.Int32Rect rcFrom = new System.Windows.Int32Rect(pxFromPos.X, pxFromPos.Y, pxFromSize.X, pxFromSize.Y);
RenderTargetBitmap rtb = new RenderTargetBitmap(pxWhole.X, pxWhole.Y, s_dpiX, s_dpiY, PixelFormats.Default);
rtb.Render(AdornedElement);
return new CroppedBitmap(rtb, rcFrom);
}
Are you sure you don't have issue with your "BitmapSource of croped image"? Can you for test purpose replace it with another valid Bitmap and try if it works. If it works with another one, but not "BitmapSource of croped image" then maybe you have issue with creating "BitmapSource of croped image".

Categories