Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Magic Image Generator

0.00/5 (No votes)
10 May 2004 2  
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:

  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.

...
// 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).

...
// 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.)

...
// 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:

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.

// 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