0

I've a set of images that I'm programmatically drawing a simple watermark on them using System.Windows and System.Windows.Media.Imaging (yes, not with GDI+) by following a tutorial in here.

Most of the images are not more than 500Kb, but after applying a simple watermark, which is a text with a transparent background, the image size is drastically increasing.

For example, a 440Kb image is becoming 8.33MB after applying the watermark with the below method, and that is shocking me.

private static BitmapFrame ApplyWatermark(BitmapFrame image, string waterMarkText) {
    const int x = 5;
    var y = image.Height - 20;
    var targetVisual = new DrawingVisual();
    var targetContext = targetVisual.RenderOpen();
    var brush = (SolidColorBrush)(new BrushConverter().ConvertFrom("#FFFFFF"));
    brush.Opacity = 0.5;
    targetContext.DrawImage(image, new Rect(0, 0, image.Width, image.Height));
    targetContext.DrawRectangle(brush, new Pen(), new Rect(0, y, image.Width, 20));
    targetContext.DrawText(new FormattedText(waterMarkText, CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
                             new Typeface("Batang"), 13, Brushes.Black), new Point(x, y));
    targetContext.Close();
    var target = new RenderTargetBitmap((int)image.Width, (int)image.Height, 96, 96, PixelFormats.Default);
    target.Render(targetVisual);
    var targetFrame = BitmapFrame.Create(target);
    return targetFrame;
}

I've noticed that the image quality is improved compared than the original image. The image is more smoother and colors are more lighter. But, you know I don't really want this. I want the image to be as it is, but include the watermark. No quality increases, and of course no drastic changes in image size.

Is there any settings that I'm missing in here to tell my program to keep the quality as same as source image? How can I prevent the significant change of the image size after the changes in my ApplyWatermark method?

Edit

1. This is how I convert BitmapFrame to Stream. Then I use that Stream to save the image to AmazonS3

private Stream EncodeBitmap(BitmapFrame image) {
    BitmapEncoder enc = new BmpBitmapEncoder();
    enc.Frames.Add(BitmapFrame.Create(image));
    var memoryStream = new MemoryStream();
    enc.Save(memoryStream);
    return memoryStream;
}

2. This is how I get the BitmapFrame from Stream

private static BitmapFrame ReadBitmapFrame(Stream stream) {
    var photoDecoder = BitmapDecoder.Create(
        stream,
        BitmapCreateOptions.PreservePixelFormat,
        BitmapCacheOption.None);
    return photoDecoder.Frames[0];
}

3. This is how I read the file from local directory

public Stream FindFileInLocalImageDir() {
    try {
        var path = @"D:\Some\Path\Image.png";
        return !File.Exists(path) ? null : File.Open(path, FileMode.Open, FileAccess.Read,  FileShare.Read);
        } catch (Exception) {
            return null;
    }
}
user3332579
  • 3,111
  • 3
  • 17
  • 21
  • the image format is .png and the image will be saved as .png as well. The image will be written to AmazonS3 after process. There is a different method that converts `BitmapFrame` to `Stream` then I save the stream to Amazon using the AmazonS3 API for .net. – user3332579 Feb 22 '14 at 22:47
  • Can that `BitmapFrame` to `Stream` converter matter here? should I also post that code? – user3332579 Feb 22 '14 at 22:48
  • Only use BmpBitmapEncoder() if you *like* big files. Use Png or Jpeg, they compress the image data. – Hans Passant Feb 22 '14 at 23:48
  • I've used `PngBitmapEncoder` as my output image format is `png`. however, I've recently noticed that the image size is being changed after the watermark apply. for example `960x1280` image is becoming `1280x1706` when applied watermark, even though I tell the set the target frame width and height as the same as the source image dimensions. may be this is the reason of why the image size is getting larger? how can I keep the original size? what I'm missing? please help. – user3332579 Feb 23 '14 at 19:39

2 Answers2

1

The problem is that when you edit the image, the compression is gone. A 730x1108 JPG with 433kB disc size with 32bit (you mentioned transparency, so ARGB) will need at least 730 * 1108 * 4 = 3,09MB on disc. Of course you can compress it afterwards again (for disc, network stream of what else).

This is the reason why image software always needs much memory even when working with compressed data.

Conclusion: You will need the free memory to work with the image. Not possible to have it otherwise completly at hand.

ZoolWay
  • 5,411
  • 6
  • 42
  • 76
  • Thanks, but I'm not sure if I understood you correctly. What do you mean by `free memory to work with the image`? I'm not having `Out of memory exception`. I don't have any exceptions, but the image size is really huge. So, do I need to compress the image after process and before saving? – user3332579 Feb 22 '14 at 23:15
  • I am just saying to modify an image it must be uncompressed. The huge size is the "real" size without compression. You can recompress after your changes (jpg, png, etc.) – ZoolWay Feb 24 '14 at 07:58
  • what he is saying is that most image formats JPEG,PNG, GIF all employ image compression to reduce file size, this is what the encoder and decoder do, so you are opening a compressed image which decompresses it to full size and then saving it again with out applying a compression to it. you need to pick an encoder such as JPEG or PNG that using file compression – MikeT Aug 10 '16 at 10:12
0

The reason I asked my question in the comments earlier, is because I noticed there were several different encoders available. A bitmap usually has a significantly larger file size, due to the amount of information it's storing about your image.

I haven't tested this myself, but have you tried a different encoder?

var pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(ApplyWatermark(null, null));

MemoryStream stm = File.Create(image);
pngEncoder.Save(stm);
return stm;
Grant Winney
  • 65,241
  • 13
  • 115
  • 165
  • I've tried `PngBitmapEncoder` and got `The codec cannot use the type of stream provided` error. – user3332579 Feb 23 '14 at 15:18
  • the error above is not a problem anymore, solved. but it seems that I'm doing some mistake somewhere so the image size is being changed after applying the watermark. for example `960x1280` image becomes `1280x1706` after the watermark. do you have any idea where is my mistake? – user3332579 Feb 23 '14 at 19:36
  • the source image's DPI is `72` in both `x` and `y`. but as you see I'm setting the dpi to `96` when building output image. the is also a 33% increase. that is more likely the reason? When I set the output image's DPI to 72 (the same as source) then the image is being drawn on the original dimension however, weirdly, the image has some free space (33%) from the right and bottom sides. [example is here](http://screencast.com/t/BE5QzBUd) – user3332579 Feb 23 '14 at 20:15
  • but when I set the DPI to 96, then image is full to borders, but the dimensions are higher than I expect, as well as the size. – user3332579 Feb 23 '14 at 20:22