I'd like to be able to create a simple PNG image, say of a red square using a c# web based service to generate the image, called from an <img src="myws.ashx?x=100> HTML element.
some example HTML:
<hmtl><body>
<img src="http://mysite.com/webservice/rectangle.ashx?size=100">
</body></html>
Is there is anyone who can cobble together a simple (working) C# class just to get me started? Once off and going I'm sure I can finish this off to actually do what I want it to do.
End game is to create simple Red/Amber/Green (RAG) embedded status markers for a data driven web page that shows performance metrics etc*
I'd like it to use PNG's as I anticipate using transparency in the future*
ASP.NET 2.0 C# solution please... (I don't have a production 3.5 box yet)
tia
SOLUTION
rectangle.html
<html>
<head></head>
<body>
<img src="rectangle.ashx" height="100" width="200">
</body>
</html>
rectangle.ashx
<%# WebHandler Language="C#" Class="ImageHandler" %>
rectangle.cs
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Web;
public class ImageHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
int width = 600; //int.Parse(context.Request.QueryString["width"]);
int height = 400; //int.Parse(context.Request.QueryString["height"]);
Bitmap bitmap = new Bitmap(width,height);
Graphics g = Graphics.FromImage( (Image) bitmap );
g.FillRectangle( Brushes.Red, 0f, 0f, bitmap.Width, bitmap.Height ); // fill the entire bitmap with a red rectangle
MemoryStream mem = new MemoryStream();
bitmap.Save(mem,ImageFormat.Png);
byte[] buffer = mem.ToArray();
context.Response.ContentType = "image/png";
context.Response.BinaryWrite(buffer);
context.Response.Flush();
}
public bool IsReusable {
get {return false;}
}
}
Web services, especially SOAP expect things like an XML envelope with the details of the call in. You'd be better off using a HttpHandler.
Something like this:
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Web;
public class ImageHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
int width = int.Parse(context.Request.QueryString["width"]);
int height = int.Parse(context.Request.QueryString["height"]);
using (Bitmap bitmap = new Bitmap(width,height)) {
...
using (MemoryStream mem = new MemoryStream()) {
bitmap.Save(mem,ImageFormat.Png);
mem.Seek(0,SeekOrigin.Begin);
context.Response.ContentType = "image/png";
mem.CopyTo(context.Response.OutputStream,4096);
context.Response.Flush();
}
}
}
}
This is very rough of course. You'd call it then:
<img src="myhandler.ashx?width=10&height=10"/>
A web service is not suitable for this. It returns a message in a specific format, typically SOAP, so it can't be an image.
Use a regular web form instead, where you remove all markup except the #page directive. Use the BinaryWrite method to write the image data to the response stream.
Example:
byte[] imageData;
using (Bitmap image = new Bitmap(10,10)) {
using (Graphics g = Graphics.FromImage(image)) {
g.Clear(Color.Red);
}
using (MemoryStream m = new MemoryStream()) {
image.Save(m, ImageFormat.Png);
imageData = m.ToArray();
}
}
Response.ContentType = "image/png";
Response.BinaryWrite(imageData);
I think #Lloyd's answer is a good start.
I've had problems with alpha transparencies and PNGs: Can you make an alpha transparent PNG with C#?
There is another way to accomplish serving a dynamic image.
namespace MyApp
{
[ServiceContract]
public interface IMyService
{
[WebGet(UriTemplate = "Image.png")]
[OperationContract]
Stream ShowImage();
}
}
For the implementation:
public Stream ShowImage()
{
Bitmap image = new Bitmap(#"C:\Image.png");
Image image2 = new Bitmap(125, 125);
using (Graphics graphics = Graphics.FromImage(image2))
{
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.DrawImage(image, 0, 0, 125, 125);
}
MemoryStream imageAsMemoryStream = new MemoryStream();
image2.Save(imageAsMemoryStream, ImageFormat.Png);
imageAsMemoryStream.Position = 0;
return imageAsMemoryStream;
}
Start the service as a regular WCF service and add the service in your app.config
(WebService = new WebServiceHost(typeof(MyService))).Open();
You can pass parameters to make it more dynamic.
It is NOT possible to output image from a WebService.
Check this: http://www.c-sharpcorner.com/UploadFile/gnsrinivas1511/Webservice05112009034709AM/Webservice.aspx
Also, depending on how you implement this, please be aware that you could be setting yourself up for a DOS attack. Generating images is not the most processor friendly thing. Please be sure to have some authentication and or caching mechanism in place to help alleviate this potential pain point.
Related
hey guys May I know how to use image processor class library Please, I logged into the provider website "https://imageprocessor.org/" but there is nothing wroten about using it thanks a lot for your time guys and thanks a lot for your help
In their website, there is a documentation about ImageFactory class which has a demo code for every method and showing the result.
You can find it here:ImageFactory
Here is a complete example taken from their documentation to load an image and do some operations to it and save it:
byte[] photoBytes = File.ReadAllBytes(file);
// Format is automatically detected though can be changed.
ISupportedImageFormat format = new JpegFormat { Quality = 70 };
Size size = new Size(150, 0)
using (MemoryStream inStream = new MemoryStream(photoBytes))
{
using (MemoryStream outStream = new MemoryStream())
{
// Initialize the ImageFactory using the overload to preserve EXIF metadata.
using (ImageFactory imageFactory = new ImageFactory(preserveExifData:true))
{
// Load, resize, set the format and quality and save an image.
imageFactory.Load(inStream)
.Resize(size)
.Format(format)
.Save(outStream);
}
// Do something with the stream.
}
}
Here is an example from their documentation:
Change backgroundColor:
public ImageFactory BackgroundColor(Color color)
Crop image:
public ImageFactory Crop(Rectangle rectangle)
Change the resoluation:
public ImageFactory Resolution(300, 300)
Add a watermark to image:
public ImageFactory Watermark (TextLayer textLayer)
And many many more. Just navigate to the url and you'll find all of them.
I have to "generate" a png file and send it to the Telegram bot via SendPhotoAsync of SeendDocumentAsync.
This is a piece of my C# code:
...
Bitmap speedometer = new Bitmap(#"C:\Immagini\bot\speedometer.png");
Bitmap pointer = new Bitmap(#"C:\Immagini\bot\pointer.png");
Bitmap finalImage = new Bitmap(speedometer);
using (Graphics graphics = Graphics.FromImage(finalImage))
{
Bitmap rotatedPointer = RotateImage(pointer, efficienza_int * (float)1.8);
rotatedPointer.MakeTransparent(Color.White);
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.DrawImage(rotatedPointer, 0, 0);
?????????????
}
Now, I want to send my finalImage without saving it on the disk with Save method.
How can I?
Thanks in advice!
Save it to MemoryStream, and send the MemoryStream in your call to the bot, like this:
using (MemoryStream ms = new MemoryStream())
using (Bitmap finalImage = new Bitmap(speedometer))
{
using (Graphics graphics = Graphics.FromImage(finalImage))
{
// ... stuff
}
finalImage.Save(ms, ImageFormat.Png);
// This is important: otherwise anything reading the stream
// will start at the point AFTER the written image.
ms.Position = 0;
Bot.SendPhotoAsync(/* send 'ms' here. Whatever the exact args are */);
}
It is possible that async sending requires the stream to remain open. Though, normally, when you have such an async send, you can specify a function that should be called after the sending has finished.
In that case, you should not put the MemoryStream in a using block, but instead store the stream object in a global variable in your class, and make sure that the function handling the end of the async send disposes it.
Also do note this question...
bot.sendphoto does not work asp.net
Apparently SendPhotoAsync is not enough to actually send it; the answer there specifies you need to call .GetAwaiter() and .GetResult(). I don't know the API, so you'll have to figure that out yourself.
From the Telegram Bot API documentation (link)
Sending files
There are three ways to send files (photos, stickers, audio, media, etc.):
...
Post the file using multipart/form-data in the usual way that files are uploaded via the browser. 10 MB max size for photos, 50 MB for other files.
Your question is not clear!
However, (if I understand your question right)
You are using TelgramBotClient from this repository: https://github.com/TelegramBots
when you invoke SendPhotoAsync from this client it takes FileToSend as a parameter which represent the photo you processed with rotation, transparency and smoothing.
when you pass this FileToSend you can set the photo either by loading it from temp file you created after processing or you can load it directory from MemoryStream like this:
using System.Drawing;
using System.Drawing.Drawing2D;
using Telegram.Bot;
using Telegram.Bot.Args;
using System.IO;
using System.Drawing.Imaging;
namespace LoadGraphicsFromMemory
{
public static class ImageExtensions
{
public static MemoryStream ToMemoryStream(this Bitmap image, ImageFormat format)
{
using (MemoryStream ms = new MemoryStream())
{
image.Save(ms, format);
return ms;
}
}
}
class Program
{
private static float efficienza_int;
private static readonly TelegramBotClient Bot = new TelegramBotClient("Your API key");
static void Main(string[] args)
{
Bot.OnMessage += BotOnMessageReceived;
}
private static void BotOnMessageReceived(object sender, MessageEventArgs e)
{
Bitmap speedometer = new Bitmap(#"C:\Immagini\bot\speedometer.png");
Bitmap pointer = new Bitmap(#"C:\Immagini\bot\pointer.png");
Bitmap finalImage = new Bitmap(speedometer);
using (Graphics graphics = Graphics.FromImage(finalImage))
{
Bitmap rotatedPointer = RotateImage(pointer, efficienza_int * (float)1.8);
rotatedPointer.MakeTransparent(Color.White);
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.DrawImage(rotatedPointer, 0, 0);
}
Bot.SendPhotoAsync(e.Message.Chat.Id, new Telegram.Bot.Types.FileToSend("My File", finalImage.ToMemoryStream(ImageFormat.Jpeg)));
}
private static Bitmap RotateImage(Bitmap pointer, object p)
{
return pointer;
}
}
}
I'm a newb when it comes to creating an ASP.NET custom user control that will render and return .png charts to an ASP.NET web application.
I've created a simple sandbox project that creates an ellipse on a bitmap, renders it to a MemoryStream, and now I want to stream the output connected via an HTTP handler so as to render an asp:image in my markup page.
My problem is that I don't know how to connect the MemoryStream created in my usercontrol to the GetImage method of the http handler. I know that the GetMethod of the HTTP Handler creating a memory stream within the method isn't correct, but I don't know how to access the memorystream of the codebehind.
My prototype test project code is:
namespace ChartControl
{
public partial class ChartCtl : System.Web.UI.UserControl
{
private int imageHeight = 150;
private int imageWidth = 400;
protected void Page_Load(object sender, EventArgs e)
{
renderChart();
}
protected MemoryStream renderChart()
{
Image imgChart = new Bitmap(imageWidth, imageHeight);
Graphics g = Graphics.FromImage(imgChart);
Rectangle r = new Rectangle(0, 0, imageWidth, imageHeight);
g.DrawEllipse(Pens.Orange, g.VisibleClipBounds);
MemoryStream ms = new MemoryStream();
imgChart.Save(ms, ImageFormat.Png);
return ms;
}
}
}
My HTTP Handler is:
namespace WIChart.UserControls
{
public class ImageHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.Clear();
if (!String.IsNullOrEmpty(context.Request.QueryString["id"]))
{
int id = Int32.Parse(context.Request.QueryString["id"]);
// Now we have the id, just pass it to GetImage to build the image
Image image = GetImage(id);
context.Response.ContentType = "image/png";
image.Save(context.Response.OutputStream, ImageFormat.Png);
}
else
{
context.Response.ContentType = "text/html";
context.Response.Write("<p>Valid id is required.</p>");
}
}
#region IHttpHandler Members
public bool IsReusable
{
get { return false; }
}
private Image GetImage(int id)
{
MemoryStream stream = new MemoryStream();
return Image.FromStream(stream);
}
#endregion
}
}
My .ascx page is:
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="ChartCtl.ascx.cs" Inherits="ChartControl.ChartCtl" %>
<%# Register Assembly="System.Web.DataVisualization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" Namespace="System.Web.UI.DataVisualization.Charting" TagPrefix="asp" %>
<div class="content-wrapper">
<div class="float-left">
<p class="site-title">
<asp:Image id="imgChart" ImageUrl="~/ImageHandler.ashx?id=1" runat="server" />
</p>
</div>
</div>
Thank you for any help that you can provide!
I couldn't understand what exactly you need here. But You can load image from user control using below code.
using System.Web;
using System.Web.UI;
using System.IO;
namespace WebApplication1
{
public class ImageHandler : Page, IHttpHandler
{
public new void ProcessRequest(HttpContext context)
{
context.Response.Clear();
ChartCtl chartCtl = (ChartCtl)LoadControl(ResolveClientUrl("ChartCtl.ascx"));
MemoryStream ms = new MemoryStream();
ms = chartCtl.renderChart(ms);
context.Response.Clear();
context.Response.ContentType = "image/jpeg";
context.Response.BinaryWrite(ReadFully(ms));
context.Response.End();
}
public static byte[] ReadFully(Stream input)
{
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
#region IHttpHandler Members
public new bool IsReusable
{
get { return false; }
}
#endregion
}
}
HTML-
<asp:Image ID="imgChart" ImageUrl="~/ImageHandler.ashx" runat="server" />
ChartCtrl -
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
namespace WebApplication1
{
public partial class ChartCtl : System.Web.UI.UserControl
{
private int imageHeight = 150;
private int imageWidth = 400;
public MemoryStream renderChart(MemoryStream ms)
{
Image imgChart = new Bitmap(imageWidth, imageHeight);
Graphics g = Graphics.FromImage(imgChart);
Rectangle r = new Rectangle(0, 0, imageWidth, imageHeight);
g.DrawEllipse(Pens.SteelBlue, g.VisibleClipBounds);
imgChart.Save(ms, ImageFormat.Jpeg); // save the image to the memorystream to be processed via the Image/HttpHandler
imgChart.Save(Context.Response.OutputStream, ImageFormat.Jpeg); // save to drive just to verify that image is being properly created.
return ms;
}
}
}
Web.Config [IIS 7] -
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<handlers>
<add name="ImageHandler" verb="*"
path="ImageHandler.ashx"
type="WebApplication1.ImageHandler, WebApplication1"
resourceType="Unspecified" />
</handlers>
</system.webServer>
Mem Stream to byte array conversion is from Creating a byte array from a stream
Check this link also http://www.codeproject.com/Articles/34084/Generic-Image-Handler-Using-IHttpHandler
P.S - I don't know why your code is being executed only with constructor. Without constructor I'm able to execute the code. When you are loading a web control from Handler, normal page events wouldn't get executed. We need call methods manually.
I think You need to host your website in IIS to get HTTpHandler called, I am not sure about this part.
You are coming at this a bit backwards with using your imagehandler as a page -- try to think of the page as static HTML where you are referencing an image and the answer is pretty straightforward. What you need to do is add an image tag referencing your graphic-generating handler -- ie <img src="~/MyHandler.ashx" />.
The challenge then becomes how to marshal data into the handler. There are loads of ways to handle this, the basic ones being query string variables if the data is simple or a query string with enough data so the handler can go back and load it's own data.
I'm taking a Stream convert it to Image, process that image, then return a FileStream.
Is this a performance problem? If not, whats the optimized way to convert and return back a stream?
public FileStream ResizeImage(int h, int w, Stream stream)
{
var img = Image.FromStream(stream);
/* ..Processing.. */
//converting back to stream? is this right?
img.Save(stream, ImageFormat.Png);
return stream;
}
The situation in which this is running: User uploads image on my site (controller gives me a Stream, i resize this, then send this stream to rackspace (Rackspace takes a FileStream).
You basically want something like this, don't you:
public void Resize(Stream input, Stream output, int width, int height)
{
using (var image = Image.FromStream(input))
using (var bmp = new Bitmap(width, height))
using (var gr = Graphics.FromImage(bmp))
{
gr.CompositingQuality = CompositingQuality.HighSpeed;
gr.SmoothingMode = SmoothingMode.HighSpeed;
gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
gr.DrawImage(image, new Rectangle(0, 0, width, height));
bmp.Save(output, ImageFormat.Png);
}
}
which will be used like this:
using (var input = File.OpenRead("input.jpg"))
using (var output = File.Create("output.png"))
{
Resize(input, output, 640, 480);
}
That looks as simple as it can be. You have to read the entire image contents to be able to process it and you have to write the result back.
FileStreams are the normal .NET way to handle files, so for normal purposes your approach is okay.
The only thing I don't understand is why you return the FileStream again - it is the same object as was passed by a parameter.
If you are doing a lot of images and only modify parts of the data, memory mapped files could improve performance. However it is a more advanced concept to use.
I have some textfields processed and other elements, but I want to get the bitmap so I can save it somewhere on disk. I need to do it directly from WatiN if this is possible.
How can I do this?
I had a similar problem some time ago.
Watin can't do this directly but it exposes the mshtml objects needed to get some results.
At the time my code was pretty much like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WatiN.Core;
using WatiN.Core.Native.InternetExplorer;
using mshtml;
using System.Windows.Forms;
namespace ConsoleApplication1
{
class Program
{
[STAThread]
static void Main(string[] args)
{
Browser browser = new IE("http://www.google.com");
IEElement banner = browser.Images[0].NativeElement as IEElement;
IHTMLElement bannerHtmlElem = banner.AsHtmlElement;
IEElement bodyNative = browser.Body.NativeElement as IEElement;
mshtml.IHTMLElement2 bodyHtmlElem = (mshtml.IHTMLElement2)bodyNative.AsHtmlElement;
mshtml.IHTMLControlRange controlRange = (mshtml.IHTMLControlRange)bodyHtmlElem.createControlRange();
controlRange.add((mshtml.IHTMLControlElement)bannerHtmlElem);
controlRange.execCommand("Copy", false, System.Reflection.Missing.Value);
controlRange.remove(0);
if (Clipboard.GetDataObject() != null)
{
IDataObject data = Clipboard.GetDataObject();
if (data.GetDataPresent(DataFormats.Bitmap))
{
System.Drawing.Image image = (System.Drawing.Image)data.GetData(DataFormats.Bitmap, true);
// do something here
}
}
}
}
}
This little hack, basically, tries to copy the image to the clipboard. However I had a couple of problems making it work properly and ended up snapshoting the region around the image and saving it to disk.
Although this may not be very helpful it may point you in some directions..
I don't think you can get the binary information directly from WatiN. However you have Image.Uri method give you the URI of the image. So then it is easy to download it wih http request.
using (Browser browser = new IE("http://www.sp4ce.net/computer/2011/01/06/how-to-use-WatiN-with-NUnit.en.html"))
{
Image image = browser.Images[0];
Console.Write(image.Uri);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(image.Uri);
WebResponse response = request.GetResponse();
using (Stream stream = response.GetResponseStream())
using (FileStream fs = File.OpenWrite(#"c:\foo.png"))
{
byte[] bytes = new byte[1024];
int count;
while((count = stream.Read(bytes, 0, bytes.Length))!=0)
{
fs.Write(bytes, 0, count);
}
}
}
Hope this helps
A while ago I needed to extract image data too so I came with this solution:
Create an hidden field and a canvas inside the page using
ie.RunScript("document.body.innerHTML += \"<input type='hidden' id='hidden64'/>\";");
ie.RunScript("document.body.innerHTML += \"<canvas id='canv' width='150px' height='40px' ></canvas>\";");
transform it to base64 using javascript and retrieving its value
ie.RunScript("var c = document.getElementById('canv');");
ie.RunScript("var ctx = c.getContext('2d');");
ie.RunScript("var img = document.getElementsByName('imgCaptcha')[0];");
ie.RunScript("ctx.drawImage(img, 10, 10);");
ie.RunScript("document.getElementById('hidden64').value=c.toDataURL();");
then retrieving the codified value
string data = ie.Element(Find.ById("hidden64")).GetAttributeValue("value");
var base64Data = Regex.Match(data, #"data:image/(?<type>.+?),(?<data>.+)").Groups["data"].Value;
var binData = Convert.FromBase64String(base64Data);
Bitmap im;
using (var stream = new MemoryStream(binData))
{
im = new Bitmap(stream);
}
Hope it helps :)
public Image GetImageFromElement(IHTMLElement Element)
{
int width = (int)Element.style.width;
int height = (int)Element.style.height;
IHTMLElementRender2 render = (IHTMLElementRender2)Element;
Bitmap screenCapture = new Bitmap(width, height);
Rectangle drawRectangle = new Rectangle(0, 0, width, height);
this.DrawToBitmap(screenCapture, drawRectangle);
Graphics graphics = Graphics.FromImage(screenCapture);
IntPtr graphicshdc = graphics.GetHdc();
render.DrawToDC(graphicshdc);
graphics.ReleaseHdc(graphicshdc);
graphics.Dispose();
return screenCapture as Image;
}
That's the Method i use for php generated images.
It's implimented in my own WebBrowserClass, which extends the webbrowser control.
(so "this" = WebBrowser)
But we have to import the IHTMLElementRender2 interface, to use the method.
[Guid("3050f669-98b5-11cf-bb82-00aa00bdce0b"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
ComVisible(true),
ComImport]
interface IHTMLElementRender2
{
void DrawToDC([In] IntPtr hDC);
void SetDocumentPrinter([In, MarshalAs(UnmanagedType.BStr)] string bstrPrinterName, [In] IntPtr hDC);
};
I found this method in web, about 1 year ago, so if you search for it you might find more information.
Iwan
I had such problem, and I could not solve it. PHP generated new images all the time so I used the CaptureWebPageToFile() method.