Chapter 6: Picture-in-Picture

Contents

6.1 Image Drawing

6.1.1 DrawImage Method

AspJpeg 1.3+ enables you to place images on top of each other via the method DrawImage. To use this method, you must create two instances of the AspJpeg objects and populate both of them with images via calls to Open (or OpenBinary). When calling Canvas.DrawImage, the 2nd instance of AspJpeg is passed as an argument to this method, along with the X and Y offsets (in pixels):

Set Jpeg1 = Server.CreateObject("Persits.Jpeg")
Set Jpeg2 = Server.CreateObject("Persits.Jpeg")
Jpeg1.Open Path1
Jpeg2.Open Path2
...
Jpeg1.Canvas.DrawImage 10, 10, Jpeg2 ' optional arguments omitted

The following code sample creates a "front page" for an online album which contains the album title (aligned to the center) and up to 3 thumbnails of the images read from a folder.

To create a blank image, the New method is used which accepts the image dimensions and background color as arguments. To align a text string to the right, the method Canvas.GetTextExtent is used.

<%
' Directory with images
Path = Server.MapPath("../images")

' Album Title
Title = "My Favorite Photographs"
Set Jpeg = Server.CreateObject("Persits.Jpeg")

' Create a "blank" image
Jpeg.New 380, 150, &HFFFFFF

' Draw 1-pixel blue frame
Jpeg.Canvas.Pen.Color = &H000080 ' Blue
Jpeg.Canvas.Brush.Solid = False ' to avoid solid bar
Jpeg.Canvas.DrawBar 1, 1, Jpeg.Width, Jpeg.Height

' Set font options
Jpeg.Canvas.Font.Color = &H000000 ' black
Jpeg.Canvas.Font.Family = "Helvetica"
Jpeg.Canvas.Font.Bold = True
Jpeg.Canvas.Font.Size = 15
Jpeg.Canvas.Font.Quality = 4 ' antialiased
Jpeg.Canvas.Font.BkMode = "Opaque"
' Draw album title centered
TitleWidth = Jpeg.Canvas.GetTextExtent( Title )
Jpeg.Canvas.Print (Jpeg.Width - TitleWidth) / 2, 13, Title

' Read images from Images directory of the installation
Dim FileNames(3)
FileNames(0) = "apple.jpg"
FileNames(1) = "clock.jpg"
FileNames(2) = "photo.jpg"
Count = 0
While Count < 3
   ' Draw this image on front page
  Set Img = Server.CreateObject("Persits.Jpeg")
  Img.Open Path & "\" & FileNames(Count)

  ' Resize to inscribe in 100x100 square
  Img.PreserveAspectRatio = True
  If Img.OriginalWidth > 100 or Img.OriginalHeight > 100 Then
    If Img.OriginalWidth > Img.OriginalHeight Then
      Img.Width = 100
    Else
      Img.Height = 100
    End If
  End If
  X = 20 + 120 * Count
  Y = 40

   ' Draw frame for each thumbnail
  Jpeg.Canvas.DrawBar X - 1, Y - 1, X + 101, Y + 101

   ' center image inside frame vert or horiz as needed
  Jpeg.Canvas.DrawImage X + (100 - Img.Width)/2, Y + (100 - Img.Height)/2, Img

  Count = Count + 1
Wend

Jpeg.Save Server.MapPath("frontpage.jpg")
%>
// Source directory with images
String strPath = Server.MapPath("../images");

// Album Title
String strTitle = "My Favorite Photographs";

IASPJpeg objJpeg;
objJpeg = new ASPJpeg();

// Create a "blank" image for the front page, white background
objJpeg.New( 380, 150, 0xFFFFFF );

// Draw 1-pixel blue frame
objJpeg.Canvas.Pen.Color = 0x000080; // Blue
objJpeg.Canvas.Brush.Solid = 0; // or a solid bar would be drawn
objJpeg.Canvas.DrawBar( 1, 1, objJpeg.Width, objJpeg.Height );

// Set font options
objJpeg.Canvas.Font.Color = 0x000000; // black
objJpeg.Canvas.Font.Family = "Helvetica";
objJpeg.Canvas.Font.Bold = 1;
objJpeg.Canvas.Font.Size = 15;
objJpeg.Canvas.Font.Quality = 4; // antialiased
objJpeg.Canvas.Font.BkMode = "Opaque"; // for antialiasing

// Draw album title centered
int TitleWidth = objJpeg.Canvas.GetTextExtent( strTitle, Missing.Value );
objJpeg.Canvas.Print( (objJpeg.Width - TitleWidth) / 2, 13,
  strTitle, Missing.Value );

// Enumerate images in the source directory
String[] files = Directory.GetFiles( strPath, "*.*");
int nCount = 0;
foreach( String strFile in files )
{
  String strExt =Path.GetExtension( strFile ).ToUpper();
  if( strExt != ".JPG" && strExt != ".GIF" && strExt != ".TIF" )
    continue;

  // Draw this image on front page
  IASPJpeg objImg = new ASPJpeg();
  objImg.Open( strFile );

   // Resize to inscribe in 100x100 square
  objImg.PreserveAspectRatio = 1;
  if(objImg.OriginalWidth > 100 || objImg.OriginalHeight > 100)
  {
    if( objImg.OriginalWidth > objImg.OriginalHeight )
    {
      objImg.Width = 100;
    }
    else
    {
      objImg.Height = 100;
    }
  }

  int X = 20 + 120 * nCount;
  int Y = 40;

   // Draw frame for each thumbnail
  objJpeg.Canvas.DrawBar( X - 1, Y - 1, X + 101, Y + 101 );

   // Center image inside frame vert or horiz as needed
  objJpeg.Canvas.DrawImage( X + (100 - objImg.Width) / 2, Y + (100 - objImg.Height) / 2, (ASPJpeg)objImg, Missing.Value, Missing.Value, Missing.Value );

  nCount++;
  if( nCount >= 3 )
    break;
}

objJpeg.Save( Server.MapPath("frontpage.jpg") );

FramedImage.Src = "frontpage.jpg";

(Note: starting with Version 1.8, the code sample 06_frontpage.asp is re-written to not use Microsoft FileSystemObject, as this object may cause IIS to hang.)

Click the links below to run this code sample:

6.1.2 Opacity & Transparency

The DrawImage method also provides three optional arguments to specify opacity and transparency parameters.

By default, an image being embedded is fully opaque (non-transparent). Opacity can be changed via the fourth optional argument which must be a number in the range 0 to 1. The value of 0 means the image is to be displayed fully transparent (invisible), and 1 fully opaque.

The following code embeds an image at 50% opacity:

Photo.Canvas.DrawImage 50, 20, Logo, 0.5

Certain pixels of an image can be made transparent when this image is placed on top of another image. The color of pixels to be made transparent is specified via the fifth argument of the DrawImage method. The following code makes all red pixels of an image transparent when displayed on top of another image:

Photo.Canvas.DrawImage 50, 20, Logo, , &HFF0000 ' opacity arg omitted

Since JPEG uses lossy compression, pixel colors often get slightly distorted (although it may not be visible to the human eye). For example, one or all color components of a pixel may change from 255 (FF) to 254 during JPEG compression. To accommodate for this deficiency, the DrawImage method provides yet another argument, an allowable deviation from the color specified by argument 5. This number must be between 0 and 255 and affects all three color components of a pixel. For example, the following code turns transparent all pixels in the range &H16D636 to &H2AEA4A:

Photo.Canvas.DrawImage 50, 20, Logo, , &H20E040, 10

6.2 GIF Transparency

Starting with Version 1.6, AspJpeg is capable of extracting transparency color information from GIF images via the TransparencyColor property. This property enables you to replace the transparency color of an image with an arbitrary color (such as white) when resizing the image or rendering it on top of another image using the Canvas.DrawImage method.

If an image does not have a transparency color (such as, if the GIF image was not assigned a transparency color during creation, or if the image is not in GIF format at all), an attempt to use the TransparencyColor property will produce an error exception. To check whether the image has a transparency color, use the property TransparencyColorExists.

(NOTE: Not all transparency-enabled GIF images can be rendered correctly using the DrawImage method. Starting with Version 1.8, you should use the DrawPNG method instead.)

The following code snippet draws a GIF image (Img) on top of a larger image (Big) and preserves the transparency of the GIF image, if applicable.

Img.Open "c:\path\image.gif"

If Img.TransparencyColorExists Then
   Big.Canvas.DrawImage 10, 10, Img, 1, Img.TransparencyColor
Else
   Big.Canvas.DrawImage 10, 10, Img
End If

The following code snippet assigns white color to the transparency-colored pixels of a GIF image:

Jpeg.Open "c:\path\image.gif"

If Jpeg.TransparencyColorExists Then
   Jpeg.ReplaceColor Jpeg.TransparencyColor, &HFFFFFF
End If

Support for PNG transparency (alpha channel) is described in the following section.

6.3 PNG & WEBP Alpha Channel

Portable Network Graphics format, also known as PNG, allows each pixel to have an additional data component (alpha channel) which represents this pixel's degree of transparency. Thus, an RGB image becomes RGBa where "a" stands for alpha channel.

Unlike GIF, which designates a particular pixel either fully transparent or fully opaque, PNG allows each pixel to be assigned an arbitrary transparency value between 0 and 255. This enables alpha-enabled PNG images to blend well with any background (see images below).

Starting with Version 1.7, AspJpeg supports PNG alpha channel. This feature is especially useful for applications that need to "stamp" images with logos or text.

As of Version 2.9.0.2, AspJpeg also supports the WEBP image format, which permits alpha channels as well.

Original logo:

Logo drawn as a transparent GIF: poor blending.

Logo drawn as a PNG with alpha channel: smooth blending.

To draw a PNG or WEBP picture on a larger image while taking into account this picture's alpha channel, the method Canvas.DrawPNG should be used. This method's parameters are the X and Y offsets of the small picture relative to the larger image, and the path to the PNG or WEBP file:

jpeg.Open "c:\path\house123.jpg"
jpeg.Canvas.DrawPNG 10, 10, "c:\path\logo.png"
jpeg.Save "c:\path\out.jpg"

Starting with Version 1.8, the DrawPNG method recognizes GIF images as well. You should use this method to draw GIF images with transparency instead of DrawImage as the latter may render certain transparency-enabled GIFs incorrectly.

In addition to DrawPNG, there is also the DrawPNGBinary method which is identical to DrawPNG except that the last argument is a binary array of bytes containing the image to be drawn. This other method should be used if the PNG or GIF image is to be retrieved from the database as opposed to a disk file.

As of Version 2.7.0.3, the DrawPNG and DrawPNGBinary methods take into account both the alpha channel of the background image and the alpha channel of the image being drawn for more accurate blending.

Live Demo #6 demonstrates the use of the DrawPNG method online.

PNG & WEBP output is described in Chapter 10.

6.4 Perspective Projection

UPDATE: As of Version 2.8, AspJpeg offers a much more powerful and flexible method for projecting images onto arbitrary 3D surfaces including planes, spheres, cylinders, cones, etc. This functionality is described in Chapter 11 - 3D Surface Mapping.

As of Version 2.5, AspJpeg allows an image being drawn to be stretched into an arbitrary quadrilateral area defined by its four corners. This feature enables you to create a perspective effect when rendering the image on top of another picture such as the photograph of a billboard shown below:

To apply perspective projection to an image, the DrawImage method described above still needs to be used, but prior to calling this method you need to define a path consisting of exactly 4 points by calling the method MoveTo followed by 3 calls to LineTo. The 4 points of the path define the quadrilateral area on the main image within which the smaller image is rendered when DrawImage is called. The first point of the path corresponds to the upper-left corner of the smaller image.

Using any image editor such as Microsoft Paint or Photoshop, determine the coordinates of the 4 points within your main image (designated P1, P2, P3 and P4 on the picture below) in a clockwise order starting with the upper-left corner, as follows:

Use these coordinates in your script as follows:

<%
Set Jpeg = Server.CreateObject("Persits.Jpeg")

' Open image to draw on
Jpeg.Open Server.MapPath(".") & "/../images/billboard.jpg"

' Create 2nd instance of AspJpeg to hold image to be drawn
Set Img = Server.CreateObject("Persits.Jpeg")
Img.Open Server.MapPath(".") & "/../images/clock.jpg"

With Jpeg.Canvas
   ' Create path: must contain 4 points, start with upper-left corner
  .MoveTo 127, 465
  .LineTo 686, 68
  .LineTo 810, 504
  .LineTo 56, 731

   ' Draw image. 1nd and 2nd arguments (X and Y) are ignored.
  .DrawImage 0, 0, Img
End With

Jpeg.Save Server.MapPath("perspective.jpg")
%>
IASPJpeg objJpeg = new ASPJpeg();

// Compute path to source image
String strPath = Server.MapPath("../images/billboard.jpg");

// Open source image
objJpeg.Open( strPath );

// Create 2nd instance of AspJpeg to hold image to be drawn
IASPJpeg objImg = new ASPJpeg();
objImg.Open( Server.MapPath("../images/clock.jpg") );

ICanvas objCanvas = objJpeg.Canvas;

// Create path: must contain 4 points, start with upper-left corner
objCanvas.MoveTo( 127, 465 );
objCanvas.LineTo( 686, 68 );
objCanvas.LineTo( 810, 504 );
objCanvas.LineTo( 56, 731 );

// Draw image. 1nd and 2nd arguments (X and Y) are ignored.
objCanvas.DrawImage( 0, 0, (ASPJpeg)objImg, Missing.Value, Missing.Value, Missing.Value );

objJpeg.Save( Server.MapPath("perspective.jpg") );

Click the links below to run this code sample:

Note that when the DrawImage method is used in the "perspective projection mode", only the 3rd and 4th arguments (Picture and Opacity) are used while all others are ignored.