Click here to Skip to main content
15,886,689 members
Please Sign up or sign in to vote.
3.00/5 (2 votes)
I'm trying to do the following (in WPF):
  1. Load an image (a .png file with a transparent background big enough to contain a drop shadow).
  2. Apply a drop shadow effect to the image (not the text).
  3. Save the modified bitmap as a new file.

Here's what I've tried:
C#
// Source and target image files
string srcImageFile = @"D:\foo.png";
string targetImageFile = @"D:\foo_shadow.png";

// Load source image
Uri uri = new Uri (srcImageFile);
BitmapImage bi = new BitmapImage (uri);

// Add drop shadow
Image img = new Image();
img.Source = bi;
DropShadowEffect dse = new DropShadowEffect();
dse.Direction = 225;
dse.Color = Color.FromArgb (255, 182, 194, 203);
dse.ShadowDepth = 10;
dse.BlurRadius = 14;
img.Effect = dse;

// Get modified image
RenderTargetBitmap rtb = new RenderTargetBitmap ((int) img.Source.Width,
                                                 (int) img.Source.Height,
                                                 96d, 96d,
                                                 PixelFormats.Pbgra32);
rtb.Render (img);

// Save modified image
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add (BitmapFrame.Create (rtb));
using (Stream outputStream = File.OpenWrite (targetImageFile)) {
    encoder.Save (outputStream);
}

The generated target file contains a blankl (transparent) image. A nudge in the right direction would be appreciated. :)

Thanks,

/ravi
Posted
Updated 12-Sep-13 6:25am
v3
Comments
S Houghtelin 11-Sep-13 14:47pm    
Ravi,

I find that if I render the img to Content (Content = img;) I can see the image on the WPF form. But if I render the rtb (Content = rtb;) all I see is the path to the library source.

Other than that I have run out of time. Hope this helps...

Try this:
C#
string fileName = "test.png";
RenderTargetBitmap target = new RenderTargetBitmap(800, 600, 96, 96, PixelFormats.Default);
target.Render(this.MyGrid);
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(target));
// Save image to disk
FileStream fs = File.Open(fileName, FileMode.Create);
encoder.Save(fs);
fs.Close();

Taken from http://fr.slideshare.net/guest91fb2a/microsoft-wpf-silverlight-comparison-whitepaper-v1-1[^].

Search for "51. Silverlight / WPF Comparison Whitepaper." about 3/4 of the way down.
 
Share this answer
 
Comments
Ravi Bhavnani 11-Sep-13 13:11pm    
If you look at my code, you'll notice it's functionally identical. I'm not using a Grid because I don't need to enclose the Image in one. If I give the encoder a BitmapFrame created from "bi", the file contains the original (unmodified) image, so I know my scaffolding is correct.

/ravi
Bassam Abdul-Baki 11-Sep-13 13:18pm    
I noticed that. The only things I noticed different where the missing optional DSE parameter (i.e., Opacity) and you were using a Stream instead of a FileStream.

Also, is it possible that these have to be done in reverse order (i.e., Start with the transparent image and overlay (or print) source image.)?

P.S. - No idea what I'm talking about here. :D
Ravi Bhavnani 11-Sep-13 14:31pm    
The Opacity property defaults to 1.0. A FileStream is a Stream.

I don't understand what you mean by "Start with the transparent image and overlay (or print) source image.". My source image has a transparent background. I want to add a drop shadow effect to that and print the resulting image.

/ravi
DaveAuld 11-Sep-13 14:40pm    
Hey Ravi, I have given up for the time being! I was getting the same issue as you in my tests. AARRGHHHH!!!!
Ravi Bhavnani 11-Sep-13 15:10pm    
Thanks for trying. :)

/ravi
As I'm not 100% sure what your ultimate aim is with the code, this snippet (largely based on yours), renders out the image with the drop shadow (note that LayoutRoot is a grid I've got in the MainWindow so I can see the effects of what I'm doing):
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Media.Effects;
using System.IO;
using System.Diagnostics;

namespace RaviApp
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
        }

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            string srcImageFile = @"C:\Dev\Intel\foo.png";

            // Load source image
            Uri uri = new Uri(srcImageFile);
            BitmapImage bi = new BitmapImage();
            bi.BeginInit();
            bi.UriSource = uri;
            // Add drop shadow
            Image img = new Image();
            img.Loaded += new RoutedEventHandler(img_Loaded);
            LayoutRoot.Children.Add(img);
            img.Source = bi;
            bi.EndInit();
        }

        void img_Loaded(object sender, RoutedEventArgs e)
        {
            string targetImageFile = @"C:\Dev\Intel\foo_shadow.png";
            Image img = sender as Image;
            DropShadowEffect dse = new DropShadowEffect();
            dse.Direction = 225;
            dse.Color = Color.FromArgb(255, 182, 194, 203);
            dse.ShadowDepth = 20;
            dse.BlurRadius = 14;
            img.Effect = dse;
            // Get modified image
            RenderTargetBitmap rtb = new RenderTargetBitmap((int)img.Source.Width,
                                                             (int)img.Source.Height,
                                                             96d, 96d,
                                                             PixelFormats.Default);
            DrawingVisual visual = new DrawingVisual();
            using (DrawingContext ctx = visual.RenderOpen())
            {
                VisualBrush vb = new VisualBrush(img);
                ctx.DrawRectangle(vb, null, new Rect(new Point(), new Point(img.Source.Width, img.Source.Height)));
            }
            rtb.Render(visual);

            // Save modified image
            BitmapEncoder encoder = new PngBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(rtb));
            using (Stream outputStream = File.OpenWrite(targetImageFile))
            {
                encoder.Save(outputStream);
            }
        }
    }
}
 
Share this answer
 
Comments
Ravi Bhavnani 11-Sep-13 17:23pm    
Thanks, Pete! That works as described. I was hoping to write a GUI-less command line app that would add a dropshadow to a collection of images, but I think the key is to have a DrawingVisual that requires a physical screen. Thanks again!

/ravi
Bassam Abdul-Baki 11-Sep-13 17:36pm    
I'm curious, is this part of something you're building or only interested in the end result? ImageMagick can do all this for you (I think).
Ravi Bhavnani 11-Sep-13 21:56pm    
It's a tool I'm building. Didn't want have to interop with IM. Also tried AForge, but it expressly indicates it doesn't have a drop shadow filter.
As i told in the forum, i use this to add shadow to a text, maybe you can adapt it:
C#
public void ProcessRequest(HttpContext context)
        {
            Bitmap logo;

            if (pfcoll == null)
            {
                pfcoll = new System.Drawing.Text.PrivateFontCollection();
                pfcoll.AddFontFile(HttpContext.Current.Server.MapPath("Fontes\\BITSUMIS.TTF"));
            }

            Font f = new Font(pfcoll.Families[0], 96, FontStyle.Italic);

            logo = new Bitmap(210, 90);

            Graphics g = Graphics.FromImage(logo);
            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

// Shadow
            g.DrawString(HttpContext.Current.Request["id"], f, new SolidBrush(Color.FromArgb(0xB3, 0xB3, 0xA3)), new PointF(2, 2));
            var rec = new Rectangle(0, 0, 210, 90);
// Text
            using (var backBrush = new LinearGradientBrush(rec, Color.FromArgb(45, 62, 92), Color.FromArgb(0x1B, 0x25, 0x37), LinearGradientMode.Vertical))
            {
                g.DrawString(HttpContext.Current.Request["id"], f, backBrush, new PointF(0, 0));
            }

            var ms = new MemoryStream();
            logo.Save(ms, ImageFormat.Png);
            var msToArray = ms.ToArray();

            context.Response.ContentType = "image/png";
            context.Response.BinaryWrite(msToArray);
        }
 
Share this answer
 
v2
Comments
Ravi Bhavnani 11-Sep-13 15:05pm    
Adding a drop shadow to a text string is not the problem I'm having. I want to add a drop shadow to an image.

/ravi
The problem that you're facing is actually fairly simple to fix. The issue is that the image isn't loaded when you think it is. What you need to do is set the CacheOption like this:
C#
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.UriSource = uri;
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.EndInit();
 
Share this answer
 
Comments
Ravi Bhavnani 11-Sep-13 15:04pm    
That's not the problem. My original image *is* being loaded (confirmed because the generated file looks exactly as the source image). The problem is writing the modified (dropshadow'd) bitmap.

/ravi
Ravi Bhavnani 11-Sep-13 15:07pm    
Pete, I'm trying to port the dropshadow filter in this VB .NET article http://www.codeproject.com/Articles/313143/VB-NET-Image-Filters to C#. i.e. I'm trying to build a shadowmaker that will operate on a bunch of .png image files.

/ravi
Pete O'Hanlon 11-Sep-13 15:16pm    
If you can post a link to one of these PNG files, I'll take a look. This code works fine for me.
Ravi Bhavnani 11-Sep-13 15:42pm    
http://ravib.com/download/foo.png

Thanks, Pete!

/ravi

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900