简体   繁体   中英

Most Efficient Way To Watermark Image C# On The Fly?

I have an ecommerce store built in asp.net c# (Webforms) and a lot of the new product images are very hard to source, so I'd like to watermark them with our logo or domain name.

There are too many products to just download the images and add the watermark, and users with limited image editing experience will be uploading new ones (So they won't have a clue how to add the watermark).

So I guess this just leaves me with using a HttpHandler? Yes / No? If so can you provide some insight (Preferably code samples in C#) into the most efficient way of adding the watermark, considering some pages will have around 20 images (Jpegs) on (All of which need to be watermarked)

I would obtain the Graphics object to the jpeg, and then draw the watermark on top of that item, and save it again with the watermark:

using (Image image = Image.FromFile("myImage.jpg"))
using(Graphics g = Graphics.FromImage( image)){
  g.DrawImage( myWaterMarkImage, myPosition);
  image.Save(myFilename);
}

This looks like it could be helpful:

http://www.switchonthecode.com/tutorials/csharp-snippet-tutorial-how-to-draw-text-on-an-image

While it focus's on text, I am sure with a little modification you could add a graphic in also.

Once you have an implementation you could either call it once per view or when adding prior to saving the file.

Here is a sample HttpHandler

/// <summary>
/// Summary description for $codebehindclassname$
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class ImageHandler : IHttpHandler
{

    public void ProcessRequest(HttpContext context)
    {
        string imageName = string.Empty;
        string physicalPath = string.Empty;
        Image image = null;
        Image thumbnailImage = null;
        Bitmap bitmap = null;
        using (MemoryStream memoryStream = new MemoryStream())
        {
            string actionName = context.Request.QueryString["Image"];
            string opacity = context.Request.QueryString["Opacity"];
            int opacityPercent = int.Parse(opacity);
            Color waterMarkColor = Color.Gray;
            switch (actionName)
            {
                case "BlueHills":
                    string myCompany = "My Company Name";
                    Font font = new Font("Times New Roman", 8f);

                    context.Response.ContentType = "image/png";
                    bitmap = Resources.Resources.BlueHills;
                    Graphics g = Graphics.FromImage(bitmap);
                    Brush myBrush = new SolidBrush(Color.FromArgb(opacityPercent, waterMarkColor));
                    SizeF sz = g.MeasureString(myCompany, font);
                    int X = (int)(bitmap.Width - sz.Width) / 2;
                    int Y = (int)(sz.Height) / 2;
                    g.DrawString(myCompany, font, myBrush, new Point(X, Y));
                    bitmap.Save(memoryStream, ImageFormat.Png);
                    break;
                default:
                    string test = actionName;
                    break;
            }

            context.Response.BinaryWrite(memoryStream.GetBuffer());
            memoryStream.Close();
            if (image != null) { image.Dispose(); }
            if (thumbnailImage != null) { thumbnailImage.Dispose(); }
            if (bitmap != null) { bitmap.Dispose(); }
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

and can be called like such:

<asp:Image ID="Image1" runat="server" ImageUrl="~/ImageHandler.ashx?Image=BlueHills&Opacity=50" />

I suggest you to take a look to WPF classes to do this job (GDI+ are deprecated in a web context).

The way (I don't know if is THE BEST way, but I've already done this and works pretty fine) is something similar to:

// Load the original image
BitmapImage image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(physical_imagepath);
image.EndInit();

// Create a final render image
RenderTargetBitmap final = new RenderTargetBitmap(yourNeededWidth, yourNeededHeight, yourDpiDefault, yourDpiDefault, PixelFormats.Default);

DrawingVisual dv = new DrawingVisual();

using (DrawingContext dc = dv.RenderOpen())
{
    Rect rectImage = new Rect(0, 0, (double)image.PixelWidth, (double)image.PixelHeight);
    dc.DrawImage(image, rectImage);

    // Load the bitmap of the watermark
    BitmapImage watermark = new BitmapImage();
    watermark.BeginInit();
    watermark.CacheOption = BitmapCacheOption.OnLoad;
    watermark.UriSource = new Uri(physical_logopath);
    watermark.EndInit();

    // Defines the watermark box
    Rect rectWatermark = new Rect(0, 0, (double)watermark.PixelWidth, (double)watermark.PixelHeight);

    /* use rectWatermark.X and rectWatermark.Y to move your watermark box around on the final image */

    dc.DrawImage(watermark, rectWatermark);
}

final.Render(dv);

// And then serve the final Bitmap to the client

Of course all written as HttpHandler. The code above is not tested.

(little ads: I've published a CodeCanyon Item that do a similar job).

This isn't an answer so much as a few tips:

  1. Jpeg doesn't support transparency, the best you can probably do is add the watermark image and make it a very light grey color.
  2. Use the generic handler (.ashx), it's very lightweight and prevents you from having to add anything to your web.config file.
  3. If there are going to be upwards of 20 images per page, then I would recommend adding the watermark as you get the images. This is a one-time cost per image and will make loading pages with the images faster.

I can't vouch for the most efficient way of adding a watermark, but if you go with tip #3, it becomes less important as you will only be performing the operation once ever per image. I would probably just use the System.Drawing namespace to do this; just be sure to dispose of the resources you use (Image, Graphics, etc), though I'm sure there are libraries out there that would work much better.

The obvious optimization to any 'on the fly image wartermarking' is to cache the 'watermarked' image if you can afford the storage cost. So, the efficiency of the wartermarking operation itself do not matters much.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM