Sep 10, 2013

Combine Images into Facebook Style Collage in ASP.NET Web Forms

Facebook home page displays a collage of random profile photos which makes it something special. So I decided to implement the same thing in ASP.NET. In this tutorial, we will do following things:

1. To get random image names from database using Entity Framework Database First approach.

2. Calculate the total needed images for a given width and height. Assuming all profile images have same size.

3. Combine multiple images into collage using C# graphics

4. Add borders to differentiate images

5. Create linear horizontal opacity gradient for the collage and add it in a page similar to Facebook.

facebook style collage

Database Structure:

facebook style collage

In above structure, ImageType is extension of image, ImageGuid is unique identifier column and images are saved in the folder with Guid and extension.

Right click on project in solution explorer, Add "ADO.NET Entity Data Model", select database and table, Give Model Namespace and click on Finish. It creates entity data model in the project.

Creating Handler:

We are going to use handler to generate collage. So add a handler (say bgimage.ashx) in the project and make it following structure:


using System;
using System.Web;
using System.Drawing;
using System.Drawing.Imaging;
using System.Collections;
using System.Collections.Generic;
using System.Drawing.Drawing2D;
using System.Linq;


public class bgimage : IHttpHandler {

    private int imageWidth = 100, imageHeight = 100, maxWidth = 1000, maxHeight = 600,borderSize=1;

    
    public void ProcessRequest (HttpContext context) {
	   context.Response.ContentType = "image/jpg";    
	
	}
	public bool IsReusable {
        get {
            return false;
        }
    }



In the handler, imageWidth, imageHeight and default max width and height of collage are defined.

Getting Random Image Names:

To get random n image names from database:


 List<String> GetProfileImages(int max) {
         List<String> listImg;
        
        using (MyDatabaseEntities context = new MyDatabaseEntities()) {
            listImg = context.ProfileInfoes
                .OrderBy(x => Guid.NewGuid())
                .Take(max)
                .Select(x => new { x.ImageGuid, x.ImageType }).ToList()
                .Select(x=>x.ImageGuid + "."+ x.ImageType).ToList();        
        }
        return listImg;
    
    }

Calculating Rows and Columns:

We will pass maxHeight and maxWidth from query string so to set values from query string in ProcessRequest method:


  //Set maxwidth and maxheight
        if (!String.IsNullOrEmpty(context.Request.QueryString["w"]))
        {
            maxWidth = Convert.ToInt32(context.Request.QueryString["w"]);
        }
        if (!String.IsNullOrEmpty(context.Request.QueryString["h"]))
        {
            maxHeight = Convert.ToInt32(context.Request.QueryString["h"]);
        }

It might be possible the images doesn't fit for the given maxHeight and maxWidth in the collage, so for the adjusted images


	int rows =maxHeight / (imageHeight+2*borderSize); 
        int cols = maxWidth / (imageWidth+2*borderSize);    
        
        
        int total = rows * cols;
        maxWidth = cols * (imageWidth+2*borderSize);
        maxHeight = rows * (imageHeight+2*borderSize);

Combining Images:

To combine image and add borders:


 using (Bitmap img = new Bitmap(maxWidth,maxHeight))
        {           
            List<String> listImg = GetProfileImages(total);
            
             //In case no sufficient images, repeat images 
            total = listImg.Count;


            int row, col,x=0,y=0;
            using (Graphics g = Graphics.FromImage(img))
            {
                using (Pen pen = new Pen(Color.White, borderSize))
                {

                    for (row = 0; row < rows; row++)
                    {

                        for (col = 0; col < cols; col++)
                        {
                            x = col * (imageWidth + borderSize * 2);
                            y = row * (imageHeight + borderSize * 2);


                            using (Image imgtemp = Image.FromFile(HttpContext.Current.Server.MapPath("~/Images/profile/") + listImg[(row * cols + col) % total]))
                            {
                                g.DrawImage(imgtemp, x + borderSize, y + borderSize, imageWidth, imageHeight);
                            }

                            //pen.Alignment = System.Drawing.Drawing2D.PenAlignment.Inset;
                            g.DrawRectangle(pen, x, y, imageWidth + 2 * borderSize - 1, imageHeight + 2 * borderSize - 1);
                        }
                    }
                }


Suppose for a given dimension, 50 images are required, but we have only 10 images in database. In this case same images are used again and again using listImg[(row * cols + col) % total].

Adding Gradient:

To add linear horizontal opacity gradient for the collage left and right sides and return the image:


//Gradient effect
                if (cols > 3)
                {
                    // Create back box brush
                    Rectangle rect = new Rectangle(0, 0, imageWidth * 2, maxHeight);
                    //left side gradient
                    using (LinearGradientBrush lgBrush = new LinearGradientBrush(rect, Color.White, Color.Transparent, LinearGradientMode.Horizontal))
                    {
                        g.FillRectangle(lgBrush, rect);
                    }
                    //Right side gradient
                    rect = new Rectangle(maxWidth - imageWidth * 2, 0, imageWidth * 2, maxHeight);
                    using (LinearGradientBrush lgBrush = new LinearGradientBrush(rect, Color.Transparent, Color.White, LinearGradientMode.Horizontal))
                    {
                        g.FillRectangle(lgBrush, rect);
                    }
                }
            }
            img.Save(context.Response.OutputStream, ImageFormat.Jpeg);
        }

Displaying Collage:

In the web page, the following html, css and js are used

HTML:


<div id="collage">
            <div class="formContainer">
            <div class="formBox">
                <h1>Welcome to TechBrij.com</h1>
                <p>
                    An example to create Facebook home page like collage in ASP.NET.
                </p>

            </div>
                </div>
        </div>

CSS:


<style type="text/css">
        #collage
        {
           
            background-position: center top;
            background-repeat: no-repeat;          
            
        }
        .formContainer
        {
            padding-top:50px;
        }
        .formBox
        {
            background: linear-gradient(#FDFDFE, #EBEEF3) repeat scroll 0 0 #F6F7F8;
    border-color: #E9EAED;
    border-radius: 5px 5px 5px 5px;
    box-shadow: 0 12px 35px rgba(0, 0, 0, 0.8);
    margin: 20px auto;    
    width: 488px;
    text-align:center;
    padding:20px;
        }
    </style>     

JS:


 $(function () {
                var width = $(window).width(), 
                    height = $(window).height(),
                    imgWidth = parseInt(width * 1.2,10),
                    imgHeight = parseInt(height * 0.7,10);
                if (width > 600) {
                    var collage = $('#collage');
                    collage.css('background-image', 'url("bgimage.ashx?w=' + imgWidth + '&h=' + imgHeight + '")');
                    collage.css({ 'height': imgHeight });
                }
            })

First we capture the browser's width and height and then define the expected collage width and height. We are keeping collage width more than browser width because the left and right part of collage are gradient and not visible by default but displayed on zooming out.

So we have implemented Facebook style collage with random profile images using ASP.NET, Entity framework and C# graphics. If you like this post, don’t forget to share.