Click here to Skip to main content
15,891,423 members
Articles / Programming Languages / C#
Article

Magic Image Generator

Rate me:
Please Sign up or sign in to vote.
4.63/5 (15 votes)
10 May 20043 min read 98.4K   1.2K   28   5
In this article, I describe how you can create an image that will change to another if it is selected in Internet Explorer.

Select the image and it will change

Introduction

Look at the picture above. It looks like an ordinary picture of bad quality. But if you select the image (with the mouse or press Ctrl-A), the image will change. There is no script or something else which changes the picture. Cool, isn't it? I have written a Windows application which can be used to generate such magic images. You just have to select the front and the back (the hidden one) image and the program will generate the magic image. You also can adjust contrast and opacity to improve the appearance.

Magic Image Generator

The small but powerful application consists of a web browser control that displays the result image, and the panel on the right where you can adjust the parameters. Open the 'New Image' dialog in the main menu to specify the output image and the two input images and hit the 'Generate' button. Additionally, you can adjust contrast and opacity. Hit the 'Refresh' button to re-generate the magic image.

How to generate 'Magic' images

For better understanding, I will give a short description of how to build such images with Adobe PhotoShop, before I will start to explain the code.

  1. Open the image that you want to hide.
  2. Duplicate the layer by right clicking and selecting 'Duplicate Layer'.
  3. Select the old layer and invert it (use Ctrl-I).
  4. Create a new image (Size: 2x2 pixels, Background: transparent) that looks like the following:

    Image 3

  5. Hit Ctrl-A to select the new image and go to Edit/Define Pattern, enter a name and hit OK. Now you can close the new image.
  6. Add a new layer to your hidden image and and use the paint bucket tool to fill the pattern you just defined.
  7. Now, hold Ctrl down and click on the layer you just filled. Now, click on the duplicate of the original layer and hit 'Delete' to delete the selected pixels.
  8. Remove the layer that you have filled with the pattern. You should now have two layers, the original with inverted colors, and the duplicate with the deleted pixels.
  9. Merge the two images (link the two layers and hit Ctrl-E).
  10. Go to Image/Adjustments/Brightness/Contrast and slide the contrast down to -30.
  11. Open the front image and hit Ctrl-A and Ctrl-C to copy it to the clipboard.
  12. Go back to the hidden image and hit Ctrl-V to paste the front image.
  13. Then change the opacity of the front image to 50%, save the image, and you are done.

You can test it by opening the picture in Microsoft Internet Explorer and hit Ctrl-A to select all.

About the code

All the work is done in the GenerateMagicImage function.

This function iterates through all pixels on the hidden image and inverts every pixel that matches the pattern above. To invert a pixel, we simply have to subtract the color value from 255. For further information on Image Processing, see Christian Graus's articles on Image Processing.

The following code operation matches steps 2-9 of How to generate 'Magic' images.

C#
...
// lock the bits
BitmapData bmData = 
  bHidden.LockBits( new Rectangle(0, 0, bHidden.Width, bHidden.Height), 
  ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int stride = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
unsafe
{
    byte * p = (byte *)(void *)Scan0;
    int nOffset = stride - bHidden.Width*3;

    for ( int y=0; y<bHidden.Height; ++y)
    {
        for ( int x=0; x<bHidden.Width; ++x )
        {
            // every pixel that matches the pattern will be inverted
            if ( (x+y) % 2 == 0 )
            {
                // invert color blue, green, red
                for ( int i=0; i<3; i++ )
                    p[i] = (byte)(255-p[i]);
            }
        
            ...
            
            p += 3;
        }
        p += nOffset;
    }
}
bHidden.UnlockBits(bmData);
...

Now, we have to change the contrast of the picture. (Step 10 of How to generate 'Magic' images).

C#
...
// initialize contrast values
if (nContrast < -100) return null;
if (nContrast >  100) return null;
double pixel = 0, contrast = (100.0+nContrast)/100.0;
contrast *= contrast;

// lock the bits
BitmapData bmData = 
  bHidden.LockBits( new Rectangle(0, 0, bHidden.Width, bHidden.Height), 
  ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int stride = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
unsafe
{
    byte * p = (byte *)(void *)Scan0;
    int nOffset = stride - bHidden.Width*3;

    for ( int y=0; y<bHidden.Height; ++y)
    {
        for ( int x=0; x<bHidden.Width; ++x )
        {
            ...
            // we will change the contrast for every pixel ( blue, green, red )
            for ( int i=0; i<3; i++ )
            {
                pixel = p[i]/255.0;
                pixel -= 0.5;
                pixel *= contrast;
                pixel += 0.5;
                pixel *= 255;
                if (pixel < 0) pixel = 0;
                if (pixel > 255) pixel = 255;
                p[i] = (byte) pixel;
            }
            p += 3;
        }
        p += nOffset;
    }
}
bHidden.UnlockBits(bmData);
...

Finally, we have to add the front image and change the opacity of the front image. (Step 11-13 of How to generate 'Magic' images.)

C#
...
// the following code draws the front image over
// the hidden image using the alpha blending effect
Graphics g = Graphics.FromImage( bHidden );
float[][] ptsArray ={ new float[] {1, 0, 0, 0, 0},
                      new float[] {0, 1, 0, 0, 0},
              new float[] {0, 0, 1, 0, 0},
              new float[] {0, 0, 0, opacity, 0}, 
              new float[] {0, 0, 0, 0, 1}}; 
ColorMatrix clrMatrix = new ColorMatrix( ptsArray );
ImageAttributes imgAttributes = new ImageAttributes();
imgAttributes.SetColorMatrix( clrMatrix, 
    ColorMatrixFlag.Default, ColorAdjustType.Bitmap );
g.DrawImage( bFront, new Rectangle( 0, 0, bFront.Width, bFront.Height ), 0, 0, 
             bFront.Width, bFront.Height, GraphicsUnit.Pixel, imgAttributes );

// return the result image
Bitmap bResult = new Bitmap( bHidden.Width, bHidden.Height );
bResult = (Bitmap)bHidden.Clone();
return bResult;
...

Here is the whole GenerateMagicImages function:

C#
public Bitmap GenerateMagicImage(Bitmap bHidden, 
     Bitmap bFront, sbyte nContrast, float opacity )
{
    // initialize contrast values
    if (nContrast < -100) return null;
    if (nContrast >  100) return null;
    double pixel = 0, contrast = (100.0+nContrast)/100.0;
    contrast *= contrast;
    
    // lock the bits
    BitmapData bmData = 
      bHidden.LockBits( new Rectangle(0, 0, bHidden.Width, bHidden.Height), 
      ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
    
    int stride = bmData.Stride;
    System.IntPtr Scan0 = bmData.Scan0;
    unsafe
    {
        byte * p = (byte *)(void *)Scan0;
        int nOffset = stride - bHidden.Width*3;

        for(int y=0; y<bHidden.Height; ++y)
        {
            for(int x=0; x<bHidden.Width; ++x )
            {
                // every pixel that matches the pattern will be inverted
                if ( (x+y) % 2 == 0 )
                {
                    // invert color blue, green, red
                    for ( int i=0; i<3; i++ )
                        p[i] = (byte)(255-p[i]);
                }

                // we will change the contrast
                // for every pixel ( blue, green, red )
                for ( int i=0; i<3; i++ )
                {
                    pixel = p[i]/255.0;
                    pixel -= 0.5;
                    pixel *= contrast;
                    pixel += 0.5;
                    pixel *= 255;
                    if (pixel < 0) pixel = 0;
                    if (pixel > 255) pixel = 255;
                    p[i] = (byte) pixel;
                }
                p += 3;
            }
            p += nOffset;
        }
    }
    bHidden.UnlockBits(bmData);

    // the following code draws the front image over the hidden
    // image using the alpha blending effect
    Graphics g = Graphics.FromImage( bHidden );
    float[][] ptsArray ={ new float[] {1, 0, 0, 0, 0},
                  new float[] {0, 1, 0, 0, 0},
                  new float[] {0, 0, 1, 0, 0},
                  new float[] {0, 0, 0, opacity, 0}, 
                  new float[] {0, 0, 0, 0, 1}}; 
    ColorMatrix clrMatrix = new ColorMatrix( ptsArray );
    ImageAttributes imgAttributes = new ImageAttributes();
    imgAttributes.SetColorMatrix( clrMatrix, 
        ColorMatrixFlag.Default, ColorAdjustType.Bitmap );
    g.DrawImage( bFront, 
        new Rectangle( 0, 0, bFront.Width, bFront.Height ), 0, 0, 
        bFront.Width, bFront.Height, 
        GraphicsUnit.Pixel, imgAttributes );

    // return the result image
    Bitmap bResult = new Bitmap( bHidden.Width, bHidden.Height );
    bResult = (Bitmap)bHidden.Clone();
    return bResult;
}

The selecting and unselecting of the image is done using the ExecWB function of the AxWebBrowser control.

C#
// execute the select all command for the web browser control
Object o = new Object();
SHDocVw.OLECMDID SelectAllCommand = SHDocVw.OLECMDID.OLECMDID_SELECTALL;
SHDocVw.OLECMDEXECOPT exeOpt = SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_DODEFAULT;
preview.ExecWB(SelectAllCommand, exeOpt, ref o, ref o);

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Austria Austria
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralAn UN*X flavour ;) Pin
Stanislaw Pusep25-Jan-07 4:48
Stanislaw Pusep25-Jan-07 4:48 
GeneralPretty nice! Pin
Lord Kixdemp28-Jun-05 20:12
Lord Kixdemp28-Jun-05 20:12 
Generalgood work! Pin
Elias Bachaalany11-May-04 21:50
Elias Bachaalany11-May-04 21:50 
Generalthe effect... Pin
Huisheng Chen11-May-04 15:42
Huisheng Chen11-May-04 15:42 
GeneralNice Idea, but only on IE Pin
Uwe Keim11-May-04 6:32
sitebuilderUwe Keim11-May-04 6:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.