|
 |
aspx thread: FW: Dynamic Image Generation with ASP.NET...
Message #1 by Scott Guthrie <scottgu@m...> on Sun, 14 Jan 2001 19:37:12 -0800
|
|
Reposting from a post I did on the ASPNGBeta listserv
(http://www.asplists.com/asplists/aspngbeta.asp) in case some people aren't
on it.
Thanks,
Scott
------------------------
> -----Original Message-----
> From: Scott Guthrie
> Sent: Sunday, January 14, 2001 2:00 PM
> To: 'aspngbeta@l...'
> Subject: Dynamic Image Generation with ASP.NET...
>
> One of the neat features that you can now leverage with .NET is the
> ability to easily generate dynamic images from code -- which you can then
> either save to disk or directly stream back to a browser client with
> ASP.NET.
>
> The functionality to generate images with .NET is encapsulated within the
> System.Drawing namespace. It provides built-in support for generating
> images with a numer of file formats including: JPEG, GIF, PNG, TIFF, BMP,
> PhotoCD, FlashPIX, WMF, EMF and EXIF. Note that there are no license
> issues to worry about with any of these file formats -- our implementation
> of each format is license free (including for GIF images).
>
> The general mechanism through which you generate these graphics images is
> by constructing a BitMap object which provides an in-memory representation
> of your image. You can then call its "Save" method to either save it to
> disk -- or stream it out to any .NET output stream. Because ASP.NET
> exposes a .NET OutputStream via the Response.OutputStream property -- this
> means you can stream the image contents directly to the browser (without
> ever having to save it to disk).
>
> For example, to do this in VB you would write code like:
>
> ' Create In-Memory BitMap of JPEG
> Dim MyChartEngine as New ChartEngine
> Dim StockBitMap as BitMap = MyChartEngine.DrawChart(600, 400,
> myChartData)
>
> ' Render BitMap Stream Back To Browser
> StockBitMap.Save(Response.OutputStream, ImageFormat.JPEG)
>
> If you are using an ASPX page to do this, you will want to make sure you
> set the appropriate HTTP ContentType header as well (so that the browser
> client doesn't interpret the page's content as html -- but rather as an
> image). You can do this either via setting the Response.ContentType
> property through code, or via the new "ContentType" attribute that you can
> set on the top-level page directive:
>
> <%@ Page Language="VB" ContentType="image/jpeg" %>
>
> Note that the output caching features of ASP.NET work for both textual
> content -- as well as for binary output. As such, if you are dynamically
> generating an image from a page, you can easily leverage the output cache
> directive to avoid having to regenerate the image on each request (note:
> image generation can be expensive -- so this is highly recommended). For
> example, the below directive could be used to output cache the generated
> image for a 60 second interval:
>
> <%@ Page Language="VB" ContentType="image/jpeg" %>
> <%@ OutputCache Duration="60" %>
>
> For a complete sample of how to use image generation, I've included a
> simple stock chart generation sample below (note that the stock prices
> aren't real -- just wishful thinking on my part :-). The sample uses a
> custom "ChartEngine" class that helps encapsulate the logic required to
> build up a generic chart. You should be able to use this helper component
> to do any custom charting of your own -- it is definitely not limited to
> just stock data.
>
> Feel free to use any of the code however you want (and like with all my
> other samples -- feel free to post elsewhere as well as to use for
> articles, other samples, etc).
>
> Thanks,
>
> Scott
>
> -------------------------------------------------------------------------
> Instructions:
> -------------------------------------------------------------------------
>
> To run the sample, copy/paste/save the below files into an IIS Application
> VRoot. Then type the below statements into a command line:
>
> mkdir bin
> csc /t:library /out:bin\chartgen.dll ChartEngine.cs /r:System.Web.dll
> /r:System.Winforms.dll /r:System.Drawing.dll /r:System.dll
>
> Once the chartengine helper utility is compiled, hit the StockPicker.aspx
> page to run the sample (note that this in turn sets up a <img> tag to
> point to the ImageGenerator_VB.aspx page that does the actual image
> generation work).
>
> --------------------------------------------------------------------------
> StockPicker.aspx:
> --------------------------------------------------------------------------
>
> <script language="VB" runat=server>
>
> Sub ChartBtn_Click(Sender as Object, E as EventArgs)
>
> chart.ImageUrl = "ImageGenerator_Vb.aspx?"
> chart.Visible = true
>
> For i=0 to Stocks.Items.Count-1
> If (Stocks.Items(i).Selected = true) Then
> chart.ImageUrl = chart.ImageUrl & "symbols=" &
> Stocks.Items(i).Value & "&"
> End If
> Next
>
> End Sub
>
> </script>
>
> <html>
> <body>
> <form runat=server>
>
> <h1>Scott's Stock Picker</h1>
>
> <asp:checkboxlist id="Stocks" runat=server>
> <asp:listitem>MSFT</asp:listitem>
> <asp:listitem>SUN</asp:listitem>
> </asp:checkboxlist>
>
> <asp:button text="Chart Your Selected Stocks"
> OnClick="ChartBtn_Click" runat=server/>
> <hr>
> <asp:Image id="chart" ImageUrl="" Visible=false runat=server/>
>
> </form>
> </body>
> </html>
>
> --------------------------------------------------------------------------
> ImageGenerator_VB.aspx:
> --------------------------------------------------------------------------
>
> <%@ Page Language="VB" ContentType="image/jpeg" %>
> <%@ Import Namespace="System.Drawing" %>
> <%@ Import Namespace="System.Drawing.Drawing2D" %>
> <%@ Import Namespace="System.Drawing.Imaging" %>
> <%@ Import Namespace="ChartGenerator" %>
> <%@ OutputCache Duration="10" %>
>
> <script language="VB" runat=server>
>
> Function GetStockDetails(Symbol as String) as ChartLine
>
> Dim myChartLine as new ChartLine
>
> if (symbol = "msft") then
>
> Dim StockValues() as Single = { 60, 110, 120, 180, 185, 190,
> 240, 290 }
>
> myChartLine.Width = 5
> myChartLine.Color = Color.Blue
> myChartLine.LineStyle = DashStyle.Solid
> myChartLine.Title = "Microsoft Corp. (MSFT)"
> myChartLine.Symbol = "MSFT"
> myChartLine.Values = StockValues
> return myChartLine
>
> elseif (symbol = "sun") then
>
> Dim StockValues() as Single = { 180, 155, 125, 60, 25, 15, 10,
> 3 }
>
> myChartLine.Width = 5
> myChartLine.Color = Color.Red
> myChartLine.LineStyle = DashStyle.Dot
> myChartLine.Title = "Sun Corp. (Sun)"
> myChartLine.Symbol = "Sun"
> myChartLine.Values = StockValues
> return myChartLine
>
> end if
>
> return nothing
>
> End Function
>
> Sub Page_Load(Sender as Object, E as EventArgs)
>
> ' Generate Chart Data For Image....
> Dim XAxes() as String = { "9:00AM", "9:30AM", "10:00AM",
> "11:00AM", "12:00AM", "1:00PM", "1:30PM" }
>
> Dim MyChartData as New ChartData
> MyChartData.YTickSize = 20
> MyChartData.YMax = 250
> MyChartData.YMin = 0
> MyChartData.XAxisTitles = XAxes
>
> Dim Symbols() as String = Request.QueryString.GetValues("symbols")
>
> if (Not Symbols = Nothing) then
>
> for i=0 to Symbols.Length-1
>
> Dim stockValue as ChartLine
> GetStockDetails(symbols(i).ToLower)
>
> If (stockValue <> nothing) then
> myChartData.Lines.Add(stockValue)
> End if
>
> Next
>
> end if
>
> ' Create In-Memory BitMap of JPEG
> Dim MyChartEngine as New ChartEngine
> Dim StockBitMap as BitMap = MyChartEngine.DrawChart(600, 400,
> myChartData)
>
> ' Render BitMap Stream Back To Client
> StockBitMap.Save(Response.OutputStream, ImageFormat.JPEG)
>
> End Sub
>
> </script>
>
> --------------------------------------------------------------------------
> ChartEngine.cs:
> --------------------------------------------------------------------------
>
> using System.WinForms;
> using System.Collections;
> using System.Collections.Bases;
> using System.Drawing;
> using System.Drawing.Drawing2D;
> using System.Drawing.Imaging;
> using System.ComponentModel;
> using System;
> using System.IO;
>
> namespace ChartGenerator {
>
> //Core Line Data structure
> public struct LineData {
> public float[] LineValues ;
> public string LineTitle ;
> public string LineSymbol ;
> }
>
> //Line Data plus display style information
> public class ChartLine {
>
> private Color lineColor ;
> private LineData lineData ;
> private DashStyle lineStyle ;
> private int lineWidth ;
>
> //Constructors
> public ChartLine() :base() {}
>
> public ChartLine(LineData lineData) :base() {
> this.lineData = lineData;
> }
>
> //Properties
> public Color Color {
> get { return lineColor ; }
> set { lineColor = value ; }
> }
>
> public DashStyle LineStyle {
> get { return lineStyle ; }
> set { lineStyle = value ; }
> }
>
> public string Symbol {
> get { return lineData.LineSymbol ; }
> set { lineData.LineSymbol = value ; }
> }
>
> public string Title {
> get { return lineData.LineTitle ; }
> set { lineData.LineTitle = value ; }
> }
>
> public float[] Values {
> get { return lineData.LineValues ; }
> set { lineData.LineValues = value ; }
> }
>
> public int Width {
> get { return lineWidth ; }
> set { lineWidth = value ; }
> }
>
> //Methods
> public void SetLineData(LineData lineData) {
> this.lineData = lineData;
> }
> }
>
> //Chart Data structure
> public class ChartData {
>
> private float yTickSize;
> private float yMax;
> private float yMin;
> private string[] xAxisTitles ;
> private ChartLineList lines = new ChartLineList();
> private Color gridColor=Color.Blue;
> private bool showHGridLines=true;
> private bool showVGridLines=true;
>
> //Properties
> public float YTickSize {
> get { return yTickSize ; }
> set { yTickSize = value ; }
> }
>
> public float YMax {
> get { return yMax ; }
> set { yMax = value ; }
> }
>
> public float YMin {
> get { return yMin ; }
> set { yMin = value ; }
> }
>
> public string[] XAxisTitles {
> get { return xAxisTitles ; }
> set { xAxisTitles = value ; }
> }
>
> public ChartLineList Lines {
> get { return lines ; }
> set { lines = value ; }
> }
>
> public Color GridColor {
> get { return gridColor ; }
> set { gridColor = value ; }
> }
>
> public bool ShowHGridLines {
> get { return showHGridLines ; }
> set { showHGridLines = value ; }
> }
>
> public bool ShowVGridLines {
> get { return showVGridLines ; }
> set { showVGridLines = value ; }
> }
>
> //Collection of Chart Lines
> public class ChartLineList : TypedCollectionBase {
>
> public ChartLine this[int index] {
> get {
> return (ChartLine)(List[index]);
> }
> set {
> List[index] = value;
> }
> }
>
> public int Add(ChartLine value) {
> return List.Add(value);
> }
>
> public void Insert(int index, ChartLine value) {
> List.Insert(index, value);
> }
>
> public int IndexOf(ChartLine value) {
> return List.IndexOf(value);
> }
>
> public bool Contains(ChartLine value) {
> return List.Contains(value);
> }
>
> public void Remove(ChartLine value) {
> List.Remove(value);
> }
>
> public void CopyTo(ChartLine[] array, int index) {
> List.CopyTo(array, index);
> }
> }
> }
>
> //Charting Engine - draws a chart based on the given ChartData
> public class ChartEngine {
>
> private ChartData chartData ;
>
> private float left;
> private float right;
> private float top;
> private float bottom;
>
> private float tickCount;
> private float yCount;
> private float hspacing;
> private float vspacing;
>
> private Graphics g;
> private Rectangle r;
> private Color backColor;
> private Color foreColor;
> private Font baseFont;
> private Font legendFont;
> private RectangleF legendRect;
>
> public ChartEngine() {
> }
>
> public Bitmap DrawChart(int width, int height, ChartData
> chartData) {
>
> Bitmap newBitmap = new
> Bitmap(width,height,PixelFormat.Format32bppARGB);
> Graphics g = Graphics.FromImage(newBitmap);
>
> Rectangle r = new Rectangle(0, 0, width, height);
> Color myForeColor = Color.Black;
> Color myBackColor = Color.White;
> Font myFont = new Font("Arial", 10);
>
> this.DrawChart(g, r, myBackColor, myForeColor, myFont,
> chartData);
>
> return newBitmap;
> }
>
> public void DrawChart(Graphics g, Rectangle r, Color backColor,
> Color foreColor, Font baseFont, ChartData chartData) {
>
> this.chartData = chartData;
> this.g = g;
> this.r = r;
> this.backColor = backColor;
> this.foreColor = foreColor;
> this.baseFont = baseFont;
> this.legendFont = new Font(baseFont.FontFamily, (baseFont.Size
> * 2/3), baseFont.Style | FontStyle.Bold);
>
>
> g.SmoothingMode = SmoothingMode.AntiAlias;
>
> CalculateChartDimensions();
>
> DrawBackground();
> InternalDrawChart() ;
> }
>
> private void CalculateChartDimensions() {
>
> right = r.Width - 5;
> top = 5 * baseFont.Size ;
> bottom = r.Height - baseFont.Size * 2;
>
> tickCount = chartData.YMin ;
> yCount = (chartData.YMax-chartData.YMin) / chartData.YTickSize
> ;
> hspacing = (bottom-top) / yCount;
> vspacing = (right) / chartData.XAxisTitles.Length;
>
> //Left depends on width of text - for simplicities sake assume
> that largest yvalue is the biggest
> //Take into account the first X Axis title
> float maxYTextSize
> g.MeasureString(chartData.YMax.ToString(), baseFont).Width;
> float firstXTitle = g.MeasureString(chartData.XAxisTitles[0],
> baseFont).Width;
>
> left = (maxYTextSize > firstXTitle) ? maxYTextSize :
> firstXTitle ;
> left = r.X + left + 5 ;
>
> //Calculate size of legend box
>
> float maxLegendWidth = 0 ;
> float maxLegendHeight = 0 ;
>
> //Work out size of biggest legend
> foreach (ChartLine cl in chartData.Lines) {
> float currentWidth = g.MeasureString(cl.Title,
> legendFont).Width;
> float currentHeight = g.MeasureString(cl.Title,
> legendFont).Height;
> maxLegendWidth = (maxLegendWidth > currentWidth) ?
> maxLegendWidth : currentWidth ;
> maxLegendHeight = (maxLegendHeight > currentHeight) ?
> maxLegendHeight : currentHeight ;
> }
>
> legendRect = new RectangleF(r.X+2, r.Y+2, maxLegendWidth + 25
> + 5, ((maxLegendHeight+2)*chartData.Lines.Count) + 3) ;
> }
>
> private void DrawBackground() {
> LinearGradientBrush b = new LinearGradientBrush(r,
> Color.SteelBlue, backColor,LinearGradientMode.Horizontal);
> g.FillRectangle(b, r);
> b.Dispose();
> }
>
> private void InternalDrawChart() {
>
> DrawGrid() ;
>
> foreach (ChartLine cl in chartData.Lines) {
> DrawLine(cl);
> }
>
> DrawLegend() ;
>
> //Draw time on chart
> string timeString = "Generated:" +
> DateTime.Now.ToLongTimeString() ;
> SizeF textsize = g.MeasureString(timeString,baseFont);
> g.DrawString(timeString, baseFont, new SolidBrush(foreColor),
> r.Width - textsize.Width - 5, textsize.Height * 2 / 3) ;
> }
>
> private void DrawGrid() {
>
> Pen gridPen = new Pen(chartData.GridColor) ;
>
> //Vertical - include tick desc's
> if (chartData.ShowVGridLines) {
> for (int i = 0 ; (vspacing * i) < right; i++) {
> float x = left + (vspacing *i);
> string desc = chartData.XAxisTitles[i];
> g.DrawLine(gridPen, x,top,x,bottom
> +(baseFont.Size*1/3));
> SizeF textsize = g.MeasureString(desc,baseFont);
> g.DrawString(desc, baseFont, new
> SolidBrush(chartData.GridColor), x-(textsize.Width/2), bottom +
> (baseFont.Size*2/3)) ;
> }
> }
>
> //Horizontal
> if (chartData.ShowHGridLines) {
> for (float i = bottom ; i > top; i-=hspacing) {
> string desc = tickCount.ToString();
> tickCount+=chartData.YTickSize;
> g.DrawLine(gridPen, right, i, left-3, i);
> SizeF textsize = g.MeasureString(desc,baseFont);
> g.DrawString(desc, baseFont, new
> SolidBrush(chartData.GridColor), left-textsize.Width - 3, i -
> (textsize.Height/2)) ;
> }
> }
> }
>
> private void DrawLine(ChartLine chartLine) {
>
> Pen linePen = new Pen(chartLine.Color);
> linePen.StartCap = LineCap.Round;
> linePen.EndCap = LineCap.Round;
> linePen.Width = chartLine.Width ;
> linePen.DashStyle = chartLine.LineStyle;
>
> PointF[] Values = new PointF[chartLine.Values.Length];
> float scale = hspacing / chartData.YTickSize ;
>
> for (int i = 0 ; i < chartLine.Values.Length; i++) {
> float x = left + vspacing * i;
> Values[i] = new PointF(x,
> bottom-chartLine.Values[i]*scale);
> }
>
> g.DrawLines(linePen, Values);
> }
>
> private void DrawLegend() {
>
> //Draw Legend Box
> ControlPaint.DrawBorder(g, (Rectangle)legendRect,
> SystemColors.WindowFrame, ButtonBorderStyle.Solid);
> LinearGradientBrush b = new LinearGradientBrush(legendRect,
> backColor, Color.SteelBlue, LinearGradientMode.Horizontal);
> r.Inflate(-1, -1);
> g.FillRectangle(b, legendRect);
> b.Dispose();
>
> float startY = 5;
>
> foreach (ChartLine cl in chartData.Lines) {
> Pen p = new Pen(cl.Color) ;
> p.Width = p.Width*4;
> SizeF textsize = g.MeasureString(cl.Title, legendFont);
> float lineY = startY + textsize.Height / 2 ;
> g.DrawLine(p, r.X + 7, lineY, r.X + 25, lineY);
> g.DrawString(cl.Title, legendFont, new
> SolidBrush(foreColor), r.X + 30, startY);
> startY += (textsize.Height+2);
> }
> }
> }
> }
>
>
>
>
|
|
 |