Alone the control does everything you need, but it forces you to have your image pre-rendered (with Deep Zoom Composer or any other tool) . So what if we need to make this control work with our image library without creating all Seadragon images (without thinking about maintaining them after updates and moving them to your production nor even the extra size). It’s a small challenge.
- So after looking at the problem my approach is as follow:
Create an Http Handler to handle the requests from the Seadragon Control
- For IIS6 register the jpg extension to be handled by our handler
- Add the needed configurations to our Web.config
- Create our handler
- Create the needed Seadragon images on the fly. Providing the needed part only to the control.
- Finding the needed level and which part the control need
- Getting the high resolution image, making some zooming and slicing
- Returning the result to the control by the handler.
Simple, that’s it. We don’t need anything else.
Before going in the details, I only wanted to say that this is only the first phase. For future I will try to make it work with PDF documents with some special zooming tricks. But this is not our problem so let’s go back.
For the first task (1.1) registering the jpg extension in IIS, that’s a simple task
Launch the Internet Services Manager tool, right click on Default Web Site, select Properties, go to Home Directory tab and press Configuration button. This will popup Application Configuration dialog. Click Add button and fill the Executable field with the path to the aspnet_isapi.dll file and fill .jpg in the Extension field. Leave the other fields as is, close the Application Configuration and Default Web Site Properties dialog boxes by pressing the OK button. (if you have IIS7 this step is not needed).
Add the following in the
<httpHandlers>
<add verb="*" path="dzc_output_files/*.jpg" type="ImageHandler.HttpImageHandler,ImageHandler"/>
</httpHandlers>
For the first task (1.c), Create our handler
using System;
using System;
using System.Web;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text.RegularExpressions;
using Injazat.DeepZoom;
namespace ImageHandler
{
public class HttpImageHandler : IHttpHandler
{
public void ProcessRequest(System.Web.HttpContext context)
{
DynamicDeepZoomImageGenerator DDZIG = new DynamicDeepZoomImageGenerator();
if (context.Request.PhysicalPath.IndexOf(DDZIG.Identifier) < 0)
{
//this is not for the deep zoom images
context.Response.Clear();
context.Response.ContentType = getContentType(context.Request.PhysicalPath);
context.Response.WriteFile(context.Request.PhysicalPath);
context.Response.End();
}
else
{
context.Response.Clear();
context.Response.ContentType = getContentType(context.Request.PhysicalPath);
byte[] buffer = DDZIG.GeneratorImage(context.Request.PhysicalPath);
context.Response.OutputStream.Write(buffer, 0, buffer.Length);
context.Response.End();
}
}
public bool IsReusable
{
get
{
return false;
}
}
string getContentType(String path)
{
switch (Path.GetExtension(path))
{
case ".bmp": return "Image/bmp";
case ".gif": return "Image/gif";
case ".jpg": return "Image/jpeg";
case ".png": return "Image/png";
default: break;
}
return "";
}
}}
So we need now to create the needed Seadragon images on the fly
I created one class to handle that.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text.RegularExpressions;
namespace Injazat.DeepZoom
{
public class DynamicDeepZoomImageGenerator
{
int Size = 250;
public string Identifier = "dzc_output_files";
public DynamicDeepZoomImageGenerator()
{
}
public byte[] GeneratorImage(String PhysicalPath)
{
// 1. find the orignal size of the doc
String path = @"C:\inetpub\wwwroot\testthumb\SampleDoc.jpg";
Bitmap imgIn = new Bitmap(path);
Point OriginalSize = new Point(imgIn.Width, imgIn.Height);
// 1.5 find number of zoom level avaliable
int ZoomMax = 0;
int X = OriginalSize.Y;
do { X /= 2; ZoomMax++; } while (X > 0);
ZoomMax = (int)Math.Ceiling(Math.Log(Math.Max(imgIn.Width, imgIn.Height), 2));
// 2. get the zoom level and array location
int ZoomLevel = 0;
Point ArrayLocation = new Point(0, 0);
String Pattren = Identifier + @"\\(?<ZoomLevel>[0-9]{1,2})\\(?<XXX>[0-9]{1,2})_(?<YYY>[0-9]{1,2}).jpg";
// use 'x' modifier to ignore comments
Regex r = new Regex(Pattren);
// get the list of group numbers
int[] gnums = r.GetGroupNumbers();
// get first match
Match m = r.Match(PhysicalPath);
if (m.Success)
{
ZoomLevel = int.Parse(m.Groups["ZoomLevel"].Value);
ArrayLocation.X = int.Parse(m.Groups["XXX"].Value);
ArrayLocation.Y = int.Parse(m.Groups["YYY"].Value);
}
// 3. return the needed part depending on the req.
//find the scalling
int Scale;
if (ZoomMax > ZoomLevel)
{ Scale = (int)Math.Pow(2, (ZoomMax - ZoomLevel)); }
else { Scale = 1; }
// resize the image to the scale
Bitmap imgOut = ResizedImage(imgIn, Scale);
// get only the needed portion of the image
imgOut = GetRegionImage(imgOut, ArrayLocation, Size);
return Image2BateArray(imgOut);
}
byte[] Image2BateArray(Bitmap imgIn)
{
System.IO.MemoryStream outStream = new System.IO.MemoryStream();
imgIn.Save(outStream, ImageFormat.Jpeg);
return outStream.ToArray();
}
public Bitmap ResizedImage(Bitmap imgIn, int Scale)
{
if (Scale == 0) Scale = 1;
int NewWidth = (int)(1.0F * imgIn.Width / Scale);
int NewHeight = (int)(1.0F * imgIn.Height / Scale);
if (NewWidth == 0) NewWidth = 1;
if (NewHeight == 0) NewHeight = 1;
Size NewSize = new System.Drawing.Size(NewWidth, NewHeight);
Bitmap imgOut = new Bitmap(imgIn, NewSize);
return imgOut;
}
Bitmap GetRegionImage(Bitmap imgIn, Point ArrayLocation, int RegionSize)
{
Point TopLeft = new Point(ArrayLocation.X * RegionSize, ArrayLocation.Y * RegionSize);
Size NewSize = new Size(RegionSize, RegionSize);
if ((TopLeft.X + RegionSize) > imgIn.Width) NewSize.Width = imgIn.Width - TopLeft.X;
if ((TopLeft.Y + RegionSize) > imgIn.Height) NewSize.Height = imgIn.Height - TopLeft.Y;
Rectangle SourceRegion = new Rectangle(TopLeft, NewSize);
Bitmap imgOut = new Bitmap(RegionSize, RegionSize);
Graphics g = Graphics.FromImage(imgOut);
g.Clear(Color.White);
g.DrawImage(imgIn, new Rectangle(0, 0, NewSize.Width, NewSize.Height), SourceRegion, GraphicsUnit.Pixel);
g.Dispose();
return imgOut;
}
void DrawOnImage(ref Bitmap imgIn, String Text)
{
Graphics g = Graphics.FromImage(imgIn);
Font Fo = new Font("Arial", 12);
Rectangle Clip = new Rectangle(0, 0, imgIn.Width, imgIn.Height);
g.DrawString(Text, Fo, Brushes.Blue, Clip);
g.DrawEllipse(Pens.Red, Clip);
Fo.Dispose();
g.Dispose();
}
ImageFormat getImageFormat(String path)
{
switch (Path.GetExtension(path))
{
case ".bmp": return ImageFormat.Bmp;
case ".gif": return ImageFormat.Gif;
case ".jpg": return ImageFormat.Jpeg;
case ".png": return ImageFormat.Png;
default: break;
}
return ImageFormat.Jpeg;
}
string getContentType(String path)
{
switch (Path.GetExtension(path))
{
case ".bmp": return "Image/bmp";
case ".gif": return "Image/gif";
case ".jpg": return "Image/jpeg";
case ".png": return "Image/png";
default: break;
}
return "";
}
}
}
Create an xml file named “dzc_output.xml”, that will contain:
<?xml version="1.0" encoding="utf-8"?>
<Image TileSize="256" Overlap="0" Format="jpg" ServerFormat="Default" xmlns="http://schemas.microsoft.com/deepzoom/2009">
<Size Width="754" Height="948" /></Image>
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<cc1:Seadragon ID="Seadragon1" runat="server" SourceUrl="~/dzc_output.xml"
BorderColor="#660033" BorderStyle="Double" BorderWidth="4px"
Height="240px" AutoHideControls="False" MaxZoomPixelRatio="10" Width="200px">
</cc1:Seadragon>
That’s it.
You can see from the code that the image loaded is one image , in my next post I will add the needed code not only to change the image, but to extract on from a PDF and display it, and focus on some important locations too. (It depends on the time :) )
Related Links:
- AJAX Toolkit
- Seadragon control by visiting the Microsoft Live Labs website.
No comments:
Post a Comment