Click here to Skip to main content
Email Password   helpLost your password?

Introduction

The standard way to add a drop shadow to an image is to fire up GIMP or Photoshop, add a background layer, add a transparent layer, select a rectangular area the size of the image you want to give a drop shadow, blur it, add a new transparent layer, paste the image in the layer, move the layer with the blurred shadow a bit to the right and to the bottom and finally flatten the layers. If you know how to use GIMP or Photoshop, this is no big deal. If you never did this before, follow the steps in, say, 40 tutorials and you will have a good basic understanding of how to use these graphic programs. Take a look at Wintris, a T*tris Clone for Windows I've written. You can find Wintris on my site. The background of Wintris was painted in GIMP 1.x. Nice, innit? But if you don't have the time to learn how to use these programs, or you don't want to learn them just for adding a simple shadow to an image, maybe there's an easier way...

Shadow is a fascinating program which can add drop shadows to images. It's a command line utility written in C#. Because it is a command line program, you can add drop shadows to images in batch. Suppose you have a site with 4000 thumbnail images, you could add a drop shadow to all these images in seconds. Shadow is based on GPaint, my program to generate Google-like logos.

To demonstrate the capabilities of Shadow, let's put Josh The Egg to work. Josh was born in Microsoft Expression, a wonderful vector graphics program. Here's a picture of Josh:

 

Usage

If you invoke Shadow without any arguments, it will display the options:

 

If you provide a file name on the command line and no options, Shadow will add a drop shadow to the image and put the image on a white background:

C:\>shadow josh.jpg

 

If you don't want to modify the input file, you can specify an output file:

C:\>shadow josh.jpg josh-with-shadow.jpg

Options

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
Generalgiving image an inner shadow
YtseJam82
6:47 5 Mar '07  
There's any possibility to give to the input image an inner shadow?
Where should I change the code you wrote?

Regards.
GeneralRe: giving image an inner shadow
q123456789
22:47 5 Mar '07  
There's no option for inner shadows.

To do that in the code, you would have to create a layer, paint it black and cut out the piece inside that black region. Blur that layer. Cut out the same region of the input image and simply put the input image above the shadow layer. Move the shadow layer a bit to the right and to the bottom. I think it's better to start with the code of The Aqualizer or maybe learn GIMP Smile Also take a look at the GPaint article.


GeneralNatural World Shades
Saied Javadi
4:55 8 Jul '06  
Hi
Thanks your for your good control.
Can you find the edges of an image and create a shade for it,for example shade a flower or egg outside a Regtangle like the shades that we see for them in natural world?
Thanks again.
Generalgiving a shadowed image a transparent background?
rickengle
5:21 3 Feb '06  
Do one of the parameters for Shadow allow a transparent background to be set? If so what values should be used?

Thanks.
GeneralRe: giving a shadowed image a transparent background?
q123456789
4:26 4 Feb '06  
Hello,

take a look at the messages for my other cool program Smile :

http://www.codeproject.com/cs/media/aqualize.asp

Someone has posted the code in the messages to set the background transparent and to save the bitmap as a PNG.
GeneralRe: giving a shadowed image a transparent background?
rickengle
18:38 5 Feb '06  
Thanks I'm looking at that. Something else I tried to do with Shadow but couldn't get working was turning it into a .NET standalone class. I'd love to be able to create an instance of the Shadow class and then be able to call it with similar parameters to what you use via the command console and pass an Image to it and get one back with a shadow attached. Would that be hard to do? With all of the dependencies on your FastBitmap type I couldn't even pull out some of the classes and roll it into a new Shadow class.

Rick
GeneralRe: giving a shadowed image a transparent background?
q123456789
2:34 6 Feb '06  
I'm going to take a look at it.
GeneralRe: giving a shadowed image a transparent background?
rickengle
2:47 6 Feb '06  
Thats terrific, thanks!
GeneralRe: giving a shadowed image a transparent background?
q123456789
6:06 6 Feb '06  
You can download shadow.cs in a zip-file here:

http://home.tiscali.be/zoetrope/shadowizer/index.htm

It contains all classes you need. There is one class "Shadowizer" which contains the logic to add shadows. The main function demonstrates how to use it. You have to save them as PNG because it now saves bitmaps in 32-bit. You could put all classes in a separate DLL. Only FastBitmap and Shadowizer would have to be public. Some code differs from shadow in this article. The blur function now uses a sigmoid function instead of a gaussian function.


GeneralRe: giving a shadowed image a transparent background?
rickengle
6:44 6 Feb '06  
That is sensational! It works great!! Could you add a small tweak?

If I am using it as a class its more likely I am going to need the result as an Image and not necessarily saved as a file. With this line:
FastBitmap bmp = s.AddShadow(image);

This returns an image of type FastBitmap. Could it have an overload to return a System.Drawing.Image? Then I could use standard uses of that type including assigning it to a Picturebox.

Also any ideas how performance can be improved? If I use the class against a 343x475 sized jpg, it takes 19-20 seconds to add the shadow which seems fairly long. What takes up the bulk of the time? I remeber in your original article on Shadow that you planned it as a bulk shadowing tool which might take a while if the shadowing time is lengthy. If it makes any difference I am trying this using the .NET Framework 2.0 under Visual Studio 2005.

Also how hard is it to remove the dependency for using unsafe code? Your GetPixel and SetPixel functions seem to be the onlythings marked as unsafe and I was wondering why the native Image GetPixel and SetPixel functions couldn't be used, are they not flexible enough for your image processing?

Thanks so much for doing this, its a very powerful and useful class!! If I use this in some of my personal utilities, how should I credit you in the code? (If thats OK)

Rick
Rick
GeneralRe: giving a shadowed image a transparent background?
q123456789
0:13 7 Feb '06  
Hello,

1. download http://home.tiscali.be/zoetrope/shadowizer/index.htm again. It contains a bug fix: the background wasn't painted. Now you can set the BackColor to Color.Transparent or Color.Red or whatever. In the code you have, the backcolor will always be transparent.

2. FastBitmap contains a Bitmap property and a Bitmap is an Image. But I'm going to take a look at it. I pass and return FastBitmaps to avoid having to make a temporary copy of the image but maybe that's unnecessary. It won't be for today. I don't have time.

3. Performance: Standard GetPixel() and SetPixel() are SLOOOOWWWWW (same as Win32 calls: also very slow). So, I use pointers. The code you have is, I think, already faster than the code in the codeproject article because I think it uses GetPixelInt32(): no need to allocate a structure. The bulk time is .NET itself. Removing unsafe code would make it even slower. Probably not what you want!

4. Credits: something like this maybe:

Portions written by SFJOIBSUTUFZBFSU.
http://home.tiscali.be/zoetrope

(Or "Based on code written by" or something like that.)

In case the link becomes dead in the future, you can remove it. If you don't want to provide a link at all, well ok.

Now I have to go.

GeneralRe: giving a shadowed image a transparent background?
rickengle
17:53 9 Feb '06  
Hi I wanted to check in and see if you were able to look at passing back an Image type instead of a FastBitMap as the result of calling the Shadowizer class.

Out of curiosity, how long is it taking you to add a shadow to an image? I'm curious if it works fastre for you then me since I had been seeing the 20 second per image shadow processingg time.

Thanks a lot,
Rick
GeneralBlur improvement
Kosta Cherry
20:30 13 Oct '05  
I have improvement for Blur() function. It speeds thing for ~5 times.

Does anybody need it?



SY-
Kosta.


GeneralRe: Blur improvement
Anonymous
0:01 14 Oct '05  
Can you tell us what you did to improve the speed?
GeneralRe: Blur improvement
Kosta Cherry
5:40 14 Oct '05  
1. I added 4 arrays and 2 methods to class FastBitmap:

---------------------------
class FastBitmap : IDisposable, ICloneable {
internal Bitmap _bitmap;
internal byte[,] src_red;
internal byte[,] src_green;
internal byte[,] src_blue;
internal byte[,] src_alfa;

<...> unsafe public void ReadBytes() {
src_red = new byte[_bitmap.Width, _bitmap.Height];
src_green = new byte[_bitmap.Width, _bitmap.Height];
src_blue = new byte[_bitmap.Width, _bitmap.Height];
src_alfa = new byte[_bitmap.Width, _bitmap.Height];

Byte* b = (Byte*)_bitmapData.Scan0;

if (_bitmapData.PixelFormat == PixelFormat.Format32bppArgb) {
for (Int32 i=0; i<_bitmap.Height; i++) {
for (Int32 k=0; k<_bitmap.Width; k++) {
src_blue[k,i] = *b;
b++;
src_green[k,i] = *b;
b++;
src_red[k,i] = *b;
b++;
src_alfa[k,i] = *b;
b++;
}
}
}

if (_bitmapData.PixelFormat == PixelFormat.Format24bppRgb) {
for (Int32 i=0; i<_bitmap.Height; i++) {
for (Int32 k=0; k<_bitmap.Width; k++) {
src_blue[k,i] = *b;
b++;
src_green[k,i] = *b;
b++;
src_red[k,i] = *b;
b++;
src_alfa[k,i] = 0;
}
}
}

}

unsafe public void WriteBytes() {
Byte* b = (Byte*)_bitmapData.Scan0;

if (_bitmapData.PixelFormat == PixelFormat.Format32bppArgb) {
for (Int32 i=0; i<_bitmap.Height; i++) {
for (Int32 k=0; k<_bitmap.Width; k++) {
*b = src_blue[k,i];
b++;
*b = src_green[k,i];
b++;
*b = src_red[k,i];
b++;
*b = src_alfa[k,i];
b++;
}
}
}

if (_bitmapData.PixelFormat == PixelFormat.Format24bppRgb) {
for (Int32 i=0; i<_bitmap.Height; i++) {
for (Int32 k=0; k<_bitmap.Width; k++) {
*b = src_blue[k,i];
b++;
*b = src_green[k,i];
b++;
*b = src_red[k,i];
b++;
}
}
}

}

<...>
}
----------------------------

This was done because GetPixel and SetPixel methods do more operations to access data than just array access.
Here I saved 2 additions, 2 multiplications and two function calls for each call for pixel color. Given number of calls, it saves ~20% of time.

2. The biggest gain went from avoiding calls for _bitmap.Width and _bitmap.Height. For some reason, they are very slow.
3. Then, I moved calculation of alfa weight for each color outside the blur loop. This also gave substantial savings.
4. Then, i unified types (all Single - that saved ~10%).
Final version for Blur() function:
---------------------------------
public void Blur(Int32 horz, Int32 vert) {
Single weightsum;
Single[] weights;

FastBitmap t = (FastBitmap)_bitmap.Clone();

_bitmap.Lock();
t.Lock();

Int32 bmW = _bitmap.Width;
Int32 bmH = _bitmap.Height;
Int32 hr2 = horz * 2 + 1;
Int32 vr2 = vert * 2 + 1;
// horizontal blur

weights = new Single[hr2];
for (Int32 i = 0; i < hr2; i++) {
Single y = Gauss(-horz + i, 0, horz);
weights[i] = y;
}

// speed up code from KCherry on 10/13/2005:
Single[,] src_red = new Single[bmW, bmH];
Single[,] src_green = new Single[bmW, bmH];
Single[,] src_blue = new Single[bmW, bmH];

_bitmap.ReadBytes();
// now calculate intermediate multipliers:
for (Int32 row = 0; row < bmH; row++) {
for (Int32 col = 0; col < bmW; col++) {
src_red[col, row] = _bitmap.src_red[col, row] * _bitmap.src_alfa[col, row] / (Single)255.0;
src_green[col, row] = _bitmap.src_green[col, row] * _bitmap.src_alfa[col, row] / (Single)255.0;
src_blue[col, row] = _bitmap.src_blue[col, row] * _bitmap.src_alfa[col, row] / (Single)255.0;
}
}

t.src_red = new byte[bmW, bmH];
t.src_green = new byte[bmW, bmH];
t.src_blue = new byte[bmW, bmH];
t.src_alfa = new byte[bmW, bmH];

for (Int32 row = 0; row < bmH; row++) {
for (Int32 col = 0; col < bmW; col++) {
Single r = 0;
Single g = 0;
Single b = 0;
Single a = 0;
weightsum = 0;
for (Int32 i = 0; i < hr2; i++) {
Int32 x = col - horz + i;
if (x < 0) {
i += -x;
x = 0;
}
if (x > bmW - 1)
break;
/* Color c = _bitmap.GetPixel(x, row);
r += c.R * weights[i] / 255.0 * c.A;
g += c.G * weights[i] / 255.0 * c.A;
b += c.B * weights[i] / 255.0 * c.A;
a += c.A * weights[i];
*/
r += src_red[x, row] * weights[i];
g += src_green[x, row] * weights[i];
b += src_blue[x, row] * weights[i];
a += _bitmap.src_alfa[x, row] * weights[i];

weightsum += weights[i];
}
r /= weightsum;
g /= weightsum;
b /= weightsum;
a /= weightsum;
Byte br = (Byte)Math.Round(r);
Byte bg = (Byte)Math.Round(g);
Byte bb = (Byte)Math.Round(b);
Byte ba = (Byte)Math.Round(a);
if (br > 255) br = 255;
if (bg > 255) bg = 255;
if (bb > 255) bb = 255;
if (ba > 255) ba = 255;
//t.SetPixel(col, row, Color.FromArgb(ba, br, bg, bb));
t.src_alfa[col, row] = ba;
t.src_red[col, row] = br;
t.src_green[col, row] = bg;
t.src_blue[col, row] = bb;
}
}

// vertical blur
// now calculate intermediate multipliers:
for (Int32 row = 0; row < bmH; row++) {
for (Int32 col = 0; col < bmW; col++) {
src_red[col, row] = t.src_red[col, row] * t.src_alfa[col, row] / (Single)255.0;
src_green[col, row] = t.src_green[col, row] * t.src_alfa[col, row] / (Single)255.0;
src_blue[col, row] = t.src_blue[col, row] * t.src_alfa[col, row] / (Single)255.0;
}
}

weights = new Single[vr2];
for (Int32 i = 0; i < vr2; i++) {
Single y = Gauss(-vert + i, 0, vert);
weights[i] = y;
}

for (Int32 col = 0; col < bmW; col++) {
for (Int32 row = 0; row < bmH; row++) {
Single r = 0;
Single g = 0;
Single b = 0;
Single a = 0;
weightsum = 0;
for (Int32 i = 0; i < vr2; i++) {
Int32 y = row - vert + i;
if (y < 0) {
i += -y;
y = 0;
}
if (y > bmH - 1)
break;
/* Color c = t.GetPixel(col, y);
r += c.R * weights[i] / 255.0 * c.A;
g += c.G * weights[i] / 255.0 * c.A;
b += c.B * weights[i] / 255.0 * c.A;
a += c.A * weights[i];
*/
r += src_red[col, y] * weights[i];
g += src_green[col, y] * weights[i];
b += src_blue[col, y] * weights[i];
a += t.src_alfa[col, y] * weights[i];

weightsum += weights[i];
}
r /= weightsum;
g /= weightsum;
b /= weightsum;
a /= weightsum;
Byte br = (Byte)Math.Round(r);
Byte bg = (Byte)Math.Round(g);
Byte bb = (Byte)Math.Round(b);
Byte ba = (Byte)Math.Round(a);
if (br > 255) br = 255;
if (bg > 255) bg = 255;
if (bb > 255) bb = 255;
if (ba > 255) ba = 255;
_bitmap.src_alfa[col, row] = ba;
_bitmap.src_red[col, row] = br;
_bitmap.src_green[col, row] = bg;
_bitmap.src_blue[col, row] = bb;
//_bitmap.SetPixel(col, row, Color.FromArgb(ba, br, bg, bb));
}
}

_bitmap.WriteBytes();
t.Dispose(); // will unlock
_bitmap.Unlock();
}

---------------------------------

The further improvement will come if Signle would be changed to Int32. I'll finish that sometime soon Smile

But it is still slower than photoshop.
I'm sure they use different algorithm, but I don't have much time to think about it.
Anybody get better ideas?

SY-
Kosta.

GeneralRe: Blur improvement
Kosta Cherry
5:43 14 Oct '05  
Oh, sh.., it was so nice laid out, and posting removed all formatting. Grrr...
Sorry.

GeneralRe: Blur improvement
Kosta Cherry
5:43 14 Oct '05  
Oh, sh.., it was so nice laid out, and posting removed all formatting. Grrr...
Sorry.


SY-
Kosta.
GeneralA Note about Performance
Anonymous
0:03 8 Oct '04  
I was working on a new program based on the Shadow code when I noticed something: the Blur Filter is VERY slow. Applying a blur to an image should happen in no time, but it takes seconds on my computer.

Instead of doing a GetPixel(), I tried a GetPixelInt32(), a method which returns the color as a Int32 instead of a Color structure. Performance was better but still way too slow. I discovered the problem is the floating point stuff. But I can't remove it, I need those operations. Sigh.

Conclusion: I would write a new utility in good old (non-.NET) C++. But then I would have to write anti-aliasing code for lines and circles etc. or I would have to use a third-party library. Sigh again.

BTW, you can find an explanation of the Blur Filter on my site http://home.tiscali.be/zoetrope.
GeneralRe: A Note about Performance
cnurse
22:04 21 Oct '04  
Just a thought, a single shot in the dark. Rather than using floating point, can you use fixed point math? That's an optimisation we often used in Assembly language for games development. I will look at the code and see if I can offer anything on this.

Nursey
GeneralRe: A Note about Performance
Hardy
6:11 10 Mar '06  
Not only Blur, I found following slow functions (800 * 600 image and based on demo source code):
1) LayeredImage constructor
2) Layer bg = image.Layers.Add(); bg.Clear(bkcolor);
3) Layer shadow = image.Layers.Add(width + shadowwidth, height + shadowwidth);
4) shadow.Blur(shadowwidth, shadowwidth);

WWW: http://hardywang.1accesshost.com
ICQ: 3359839
yours Hardy
GeneralCSS alternative
PeterSteen
23:33 30 Sep '04  
First. Very nice utillity. Especially the batch part.

If however one only needs a dropshadow for at few items, the following CSS class will do this on the fly:

.Shadow
{
filter: progid:dximagetransform.microsoft.dropshadow(offx=2, offy=2, color= 'gray' , positive= 'true' );
}

Notice! Is a windows thing.

Peter Steen Nielsen
GeneralRe: CSS alternative
Anonymous
1:38 1 Oct '04  
This is also an IE only thing
GeneralNice tutorial
Anonymous
4:17 30 Sep '04  
Would be nice if you could walk through some of the code in article.
GeneralRe: Nice tutorial
Colin Angus Mackay
4:45 30 Sep '04  
Anonymous wrote: Would be nice if you could walk through some of the code in article
I agree. But is still looks like a great applet.


Do you want to know more?
Not getting the response you want from a question asked in an online forum: How to Ask Questions the Smart Way!


GeneralRe: Nice tutorial
Anonymous
5:31 30 Sep '04  
Sorry if that came over as not appreciating the work thats been done. It does look wicked Smile


Last Updated 30 Sep 2004 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010