This documentation is for the old version. Go to the latest Graphics Mill docs

Merging Layers

A typical task is to merge layers extracted from a PSD file into one image. Let's consider how to do it via Advanced PSD Add-on.

Merging Raster Layers

In order to combine raster layers into one image follow these steps:

  1. Create advanced PSD reader object;
  2. Open it on the necessary PSD file;
  3. Create two bitmap objects: one of them will store the result and another one will keep the intermediate image;
  4. Load the background raster layer into the result bitmap. The background is the bottommost layer in the PSD file (it has the index = 1);

    Note

    Remember that the very first frame in the advanced PSD reader (with index = 0) contains pre-rendered preview image. That's why you should skip it and load the second frame (with index = 1) as a background. But bear in mind that indices as still zero-based.

  5. Operate with the rest raster layers. For each layer do the following:
    1. Load the next layer using LoadFrame(Int32) method;
    2. Verify that the layer type is not unknown (i.e. whether the bitmap is available in the layer);
    3. Check out whether the layer is visible;
    4. Extract the bitmap from the current raster layer;
    5. Draw this bitmap on the result bitmap;
  6. Save the result bitmap into a file;
  7. Close advanced PSD reader object.

The following code snippet illustrates this approach:

Visual Basic
' Create advanced PSD reader object to read .psd files.
Dim psdReader As New Aurigma.GraphicsMill.Codecs.AdvancedPsdReader

' Create the resultBitmap object which contains merged bitmap, 
' and the currentBitmap object which contains current bitmap during iteration. 
' These object enable you to operate with layers.

Dim resultBitmap As New Aurigma.GraphicsMill.Bitmap
Dim currentBitmap As New Aurigma.GraphicsMill.Bitmap

' Open advanced PSD reader to be able to get layers.
psdReader.Open("C:\Temp\WorkingWithLayers.psd")

' Load the background layer which you will put other layers on. 
' Remember that the layer on zero position should be skiped 
' because it contains merged bitmap.

Dim frame As Aurigma.GraphicsMill.Codecs.AdvancedPsdFrame
frame = psdReader.LoadFrame(1)
frame.GetBitmap(resultBitmap)

'This code merges the rest layers with the background layer one by one.
Dim I As Integer
For I = 0 To psdReader.FrameCount - 1

    frame = psdReader.LoadFrame(I)

    ' Do not forget to verify the unknown layer type.  
    If (frame.Type <> Aurigma.GraphicsMill.Codecs.PsdFrameType.Unknown And frame.Visible) Then

        ' Extract the current image from the layer.
        frame.GetBitmap(currentBitmap)

        ' Draw current layer on the result bitmap. 
        ' Also check out if the layer is visible or not.
        ' If the layer is invisible we skip it.
        currentBitmap.Draw(resultBitmap, frame.Left, frame.Top, frame.Width, frame.Height, Aurigma.GraphicsMill.Transforms.CombineMode.Alpha, 1, Aurigma.GraphicsMill.Transforms.InterpolationMode.HighQuality)
    End If
Next

' Save the result bitmap into file. 
resultBitmap.Save("C:\Temp\resultBitmap.png")

' Clean up.
psdReader.Close()
C#
// Create advanced PSD reader object to read .psd files.
Aurigma.GraphicsMill.Codecs.AdvancedPsdReader psdReader = 
    new Aurigma.GraphicsMill.Codecs.AdvancedPsdReader();

// Create the resultBitmap object which contains merged bitmap, 
// and the currentBitmap object which contains current bitmap during iteration. 
// These object enable you to operate with layers.
Aurigma.GraphicsMill.Bitmap resultBitmap = new Aurigma.GraphicsMill.Bitmap();
Aurigma.GraphicsMill.Bitmap currentBitmap = new Aurigma.GraphicsMill.Bitmap();

// Open advanced PSD reader to be able to get layers.
psdReader.Open(@"C:\Temp\WorkingWithLayers.psd");

// Load the background layer which you will put other layers on. 
// Remember that the layer on zero position should be skiped 
// because it contains merged bitmap.
Aurigma.GraphicsMill.Codecs.AdvancedPsdFrame frame;
frame = (Aurigma.GraphicsMill.Codecs.AdvancedPsdFrame)psdReader.LoadFrame(1);
frame.GetBitmap(resultBitmap);

//This code merges the rest layers with the background layer one by one.
for (int i=2;i<psdReader.FrameCount;i++)
{ 
    frame = (Aurigma.GraphicsMill.Codecs.AdvancedPsdFrame)psdReader.LoadFrame(i);
    
    // Do not forget to verify the unknown layer type.  
    if (frame.Type != Aurigma.GraphicsMill.Codecs.PsdFrameType.Unknown && frame.Visible)
    {
        // Extract the current image from the layer.
        frame.GetBitmap(currentBitmap);

        // Draw current layer on the result bitmap. 
        // Also check out if the layer is visible or not.
        // If the layer is invisible we skip it.
        currentBitmap.Draw(resultBitmap, frame.Left, frame.Top, frame.Width, frame.Height, Aurigma.GraphicsMill.Transforms.CombineMode.Alpha, 1, Aurigma.GraphicsMill.Transforms.InterpolationMode.HighQuality);
    }
}

// Save the result bitmap into file. 
resultBitmap.Save(@"C:\Temp\resultBitmap.png");

// Clean up.
psdReader.Close();

Merging Text Layers

Precise text rendering is very complicated task. It is practically impossible to achieve 100% conformance with that you get from Adobe® Photoshop®. The key factor is that Photoshop operates with its own rasterizer which differs from those used in Windows (and Graphics Mill for .NET).

Moreover there are many text settings used in Adobe® Photoshop®, whereas not all of them are supported by the add-on. That is why you should use only those features that are available in Advanced PSD Add-on.

However not all tasks demand 100% coincidence. For example, assume that we building an application which takes a PSD template of a business card and inserts text into placeholders. The input template may be looking like this:

Business card template with empty placeholders.

After you run the code example below, you will get the following image:

Result image (placeholder [FullName] is replaced by the actual data).

The following function can be used to process PSD template and replace placeholders by the text:

Visual Basic
''' <summary>
''' This method loads a business card template from a file specified by the 
''' inputFilename argument and returns the result bitmap. It inserts 
''' the user's data in placeholders. Layers intepreted as placeholders and  
''' the data to be inserted is specified by the updateLayers argument.
''' </summary>
''' <param name="inputFilename">A path to PSD template of the business card</param>
''' <param name="updatedLayers">A collection of placeholder-value pairs used for processing.</param>
Public Function RasterizePsd(ByVal inputFilename As String, _
 ByVal updatedLayers As System.Collections.Hashtable) _
 As Aurigma.GraphicsMill.Bitmap

    ' Open the reader object on the template file.
    Dim reader As New Aurigma.GraphicsMill.Codecs.AdvancedPsdReader(inputFilename)

    ' Create the bitmap which will keep the result.
    Dim resultBitmap As New Aurigma.GraphicsMill.Bitmap

    ' Load the background layer into the result bitmap.
    ' Background layer always has index = 1.
    Dim frame As Aurigma.GraphicsMill.Codecs.AdvancedPsdFrame
    frame = reader.LoadFrame(1)
    frame.GetBitmap(resultBitmap)
    frame.Dispose()

    ' Create intermediate bitmap which will keep the current layer.
    Dim currentLayerBitmap As New Aurigma.GraphicsMill.Bitmap

    ' Process all frames (except of first two ones) of the template file and 
    ' draw them on the result bitmap. If it is placeholder which should 
    ' be replaced, draw the text instead of the bitmap.
    '
    ' We skip first two frames, because the very first one stores pre-rendered 
    ' PSD image (meaningless to us) and the second one stores the background
    ' layer which is already loaded into the result bitmap.
    For I As Integer = 2 To reader.FrameCount - 1
        frame = reader.LoadFrame(I)

        ' Skip layers which we cannot process.
        If frame.Type <> Aurigma.GraphicsMill.Codecs.PsdFrameType.Unknown Then

            ' Determine whether the current layer is a placeholder by 
            ' searching its name in the hash table of placeholder data.
            Dim isPlaceholder As Boolean = Not (updatedLayers(frame.Name) Is Nothing)

            ' If this is a placeholder, and the layer is a text, replace the text data...
            If isPlaceholder AndAlso frame.Type = Aurigma.GraphicsMill.Codecs.PsdFrameType.Text Then

                ' Cast the frame to text frame type to be able to get textual settings.
                Dim textFrame As Aurigma.GraphicsMill.Codecs.AdvancedPsdTextFrame = frame

                ' When drawing the text layer as a text string, keep in mind that
                ' Left and Top properties return position of the layer bounding box
                ' rather than coordinate of text output. While the DrawString method
                ' inserts small gap above the string, the Top property of the frame,
                ' does not include this gap (it returns the box which circumscribes 
                ' the text string).
                '
                ' There is no need to recalculate the X coordinate, because Graphics Mill 
                ' does not any horizontal gaps between the first character in the string.                         
                '
                ' To calculate the Y coordinate properly, we can use analyze the black box of
                ' characters of the text layer. It represents the bounding rectangle of the  
                ' character relatively the text output origin. This way we can get the 
                ' necessary offset. See GetBlackBox method of the Font object for more details. 
                Dim y As Single = textFrame.Top - textFrame.Font.GetBlackBox(textFrame.Text.Trim).Top

                ' Draw the necessary text instead the placeholder text.
                Dim graphics As Aurigma.GraphicsMill.Drawing.GdiGraphics = resultBitmap.GetGdiGraphics()

                graphics.DrawString(updatedLayers(frame.Name), textFrame.Font, textFrame.TextBrush, textFrame.Left, y)

                ' Release resources
                graphics.Dispose()

            ' ... otherwise just draw the bitmap.
            Else

                ' If this is a placeholder, draw passed bitmap.
                If isPlaceholder Then

                    ' Since the width of the bitmap from updatedLayers may differ from 
                    ' width of the layer, we need to resize it to avoid design corruption.
                    ' If you need to resize it differently, try to play with arguments.
                    DirectCast(updatedLayers(frame.Name), Aurigma.GraphicsMill.Bitmap).Draw(resultBitmap, _
                     frame.Left, frame.Top, _
                     frame.Width, 0, _
                     Aurigma.GraphicsMill.Transforms.CombineMode.Alpha, 1.0F, _
                     Aurigma.GraphicsMill.Transforms.InterpolationMode.LowQuality)

                ' If this is not a placeholder, draw the bitmap from PSD file.
                Else

                    frame.GetBitmap(currentLayerBitmap)

                    currentLayerBitmap.Draw(resultBitmap, _
                     frame.Left, frame.Top, _
                     0, 0, _
                     Aurigma.GraphicsMill.Transforms.CombineMode.Alpha, 1.0F, _
                     Aurigma.GraphicsMill.Transforms.InterpolationMode.LowQuality)

                End If

            End If
        End If

        frame.Dispose()
    Next

    ' Release resources
    currentLayerBitmap.Dispose()

    ' Do not forget to close the reader to release the template file.
    reader.Close()

    Return resultBitmap
End Function
C#
/// <summary>
/// This method loads a business card template from a file specified by the 
/// inputFilename argument and returns the result bitmap. It inserts 
/// the user's data in placeholders. Layers intepreted as placeholders and  
/// the data to be inserted is specified by the updateLayers argument.
/// </summary>
/// <param name="inputFilename">A path to PSD template of the business card</param>
/// <param name="updatedLayers">A collection of placeholder-value pairs used for processing.</param>
public static Aurigma.GraphicsMill.Bitmap RasterizePsd(string inputFilename, 
    System.Collections.Hashtable updatedLayers)        
{

    // Open the reader object on the template file.
    Aurigma.GraphicsMill.Codecs.AdvancedPsdReader reader = 
        new Aurigma.GraphicsMill.Codecs.AdvancedPsdReader(inputFilename);            

    // Create the bitmap which will keep the result.
    Aurigma.GraphicsMill.Bitmap resultBitmap = 
        new Aurigma.GraphicsMill.Bitmap();

    // Load the background layer into the result bitmap.
    // Background layer always has index = 1.
    Aurigma.GraphicsMill.Codecs.AdvancedPsdFrame frame;
    frame = (Aurigma.GraphicsMill.Codecs.AdvancedPsdFrame)reader.LoadFrame(1);
    frame.GetBitmap(resultBitmap);
    frame.Dispose();

    // Create intermediate bitmap which will keep the current layer.
    Aurigma.GraphicsMill.Bitmap currentLayerBitmap = 
        new Aurigma.GraphicsMill.Bitmap();

    // Process all frames (except of first two ones) of the template file and 
    // draw them on the result bitmap. If it is placeholder which should 
    // be replaced, draw the text instead of the bitmap.
    //
    // We skip first two frames, because the very first one stores pre-rendered 
    // PSD image (meaningless to us) and the second one stores the background
    // layer which is already loaded into the result bitmap.
    for (int i=2;i<reader.FrameCount;i++)
    {
        frame = (Aurigma.GraphicsMill.Codecs.AdvancedPsdFrame)reader.LoadFrame(i);

        // Skip layers which we cannot process.
        if (frame.Type != Aurigma.GraphicsMill.Codecs.PsdFrameType.Unknown)
        {
            // Determine whether the current layer is a placeholder by 
            // searching its name in the hash table of placeholder data.
            bool isPlaceholder = updatedLayers[frame.Name] != null;

            // If this is a placeholder, and the layer is a text, replace the text data...
            if (isPlaceholder && frame.Type == Aurigma.GraphicsMill.Codecs.PsdFrameType.Text)
            {
                Aurigma.GraphicsMill.Codecs.AdvancedPsdTextFrame textFrame;
                Aurigma.GraphicsMill.Drawing.GdiGraphics graphics;

                // Cast the frame to text frame type to be able to get textual settings.
                textFrame = (Aurigma.GraphicsMill.Codecs.AdvancedPsdTextFrame)frame;

                // When drawing the text layer as a text string, keep in mind that
                // Left and Top properties return position of the layer bounding box
                // rather than coordinate of text output. While the DrawString method
                // inserts small gap above the string, the Top property of the frame,
                // does not include this gap (it returns the box which circumscribes 
                // the text string).
                //
                // There is no need to recalculate the X coordinate, because Graphics Mill 
                // does not any horizontal gaps between the first character in the string.                         
                //
                // To calculate the Y coordinate properly, we can use analyze the black box of
                // characters of the text layer. It represents the bounding rectangle of the  
                // character relatively the text output origin. This way we can get the 
                // necessary offset. See GetBlackBox method of the Font object for more details. 
                float y = textFrame.Top - textFrame.Font.GetBlackBox(textFrame.Text.Trim()).Top; 

                // Draw the necessary text instead the placeholder text.
                graphics = resultBitmap.GetGdiGraphics();
                graphics.DrawString((string)(updatedLayers[frame.Name]), textFrame.Font, textFrame.TextBrush, textFrame.Left, y);

                // Release resources
                graphics.Dispose();
            
            }
            
            // ... otherwise just draw the bitmap.
            else
            {
                // If this is a placeholder, draw passed bitmap. 
                if (isPlaceholder)
                {
                    // Since the width of the bitmap from updatedLayers may differ from 
                    // width of the layer, we need to resize it to avoid design corruption.
                    // If you need to resize it differently, try to play with arguments.
                    ((Aurigma.GraphicsMill.Bitmap)(updatedLayers[frame.Name])).Draw(resultBitmap, 
                        frame.Left, frame.Top, 
                        frame.Width, 0, 
                        Aurigma.GraphicsMill.Transforms.CombineMode.Alpha, 1.0f, 
                        Aurigma.GraphicsMill.Transforms.InterpolationMode.LowQuality);
                }
                // If this is not a placeholder, draw the bitmap from PSD file.
                else
                {
                    frame.GetBitmap(currentLayerBitmap);
                    currentLayerBitmap.Draw(resultBitmap, 
                        frame.Left, frame.Top, 
                        0, 0,
                        Aurigma.GraphicsMill.Transforms.CombineMode.Alpha, 1.0f,
                        Aurigma.GraphicsMill.Transforms.InterpolationMode.LowQuality);
                }
            }
        }
    }

    // Release resources
    currentLayerBitmap.Dispose();

    // Do not forget to close the reader to release the template file.
    reader.Close();

    return resultBitmap;
}

The usage of this function is demonstrated below. For brevity this code example replaces only FullName placeholder.

Visual Basic
Public Sub RasterizeBusinessCard(ByVal inputFilename As String, ByVal outputFilename As String)
    Dim updatedLayers As New System.Collections.Hashtable

    ' Initialize the user full name and the name of appropriate layer 
    ' in the PSD template.
    updatedLayers.Add("FullName", "Andrew Simontsev")

    Dim businessCard As Aurigma.GraphicsMill.Bitmap = RasterizePsd(inputFilename, updatedLayers)

    ' Save the result to disk.
    businessCard.Save(outputFilename)
End Sub
C#
public static void RasterizeBusinessCard(string inputFilename, string outputFilename)
{
    System.Collections.Hashtable updatedLayers = new System.Collections.Hashtable();

    // Initialize the user full name and the name of appropriate layer 
    // in the PSD template.
    updatedLayers.Add("FullName", "Andrew Simontsev");

    Aurigma.GraphicsMill.Bitmap businessCard = RasterizePsd(inputFilename, updatedLayers);

    // Save the result to disk.
    businessCard.Save(outputFilename);
}

See Also

Manual