The browser would render a webpage faster if it knows the images dimensions. But how can I assign the Width and Height in ASP.NET automatically?
I can do a server control extending Image that would query the image size using GDI+ and assign those values to its own Width and Height properties.
But there are several things that worries me and would like to hear opinions:
Cache: What is the best way to implement cache? I would like to cache the Height and Width values across all pages for each image.
Which method should I use to query the image size? The constructor or another method (OnInit, OnPreRender, etc, etc)?
Is it GDI+ the best option to query image size, or is there any other efficient way?
How can I query the size for remotes images?
Of course I have an idea of how to do all that I mentioned, but I wanted to hear some thoughts.
http://code.google.com/speed/page-speed/docs/rendering.html#SpecifyImageDimensions
EDIT: This is what I have so far:
[ToolboxData("<{0}:AutoSizeImage runat=server></{0}:AutoSizeImage>")]
public class AutoSizeImage : Image
{
protected override void OnPreRender(EventArgs e)
{
if (Width.Value + Height.Value == 0)
{
if (IsLocal(ImageUrl))
{
using (System.Drawing.Image img = System.Drawing.Image.FromFile(Context.Server.MapPath(ImageUrl)))
{
Width = img.Width;
Height = img.Height;
}
}
else
{
System.Net.WebRequest request = System.Net.WebRequest.Create(ImageUrl);
using (System.Net.WebResponse response = request.GetResponse())
using (System.IO.Stream responseStream = response.GetResponseStream())
using (System.Drawing.Image img = System.Drawing.Image.FromStream(responseStream))
{
Width = img.Width;
Height = img.Height;
}
}
}
base.OnPreRender(e);
}
private bool IsLocal(string p)
{
return !(p.StartsWith("http://") || p.StartsWith("https://") || p.StartsWith("ftp://"));
}
}
It works beautifully, but I need to implement cache now.
I want to cache the output of this control for each value of ImageUrl, that is, I want the OnPreRender method to be called once for each image. Is that possible?
If you are going to be querying the image size on the fly, you might end up making things slower. Of course it all depends on how your application works.
If I were to try to do something similar, I would get the image dimensions at the time when the image is uploaded. (or when it was created using a filesystem watcher). I would then store this information in a database and then cache against the database.
If the size of images will change foreach user that are using the app, then you will have to use Session or Cookie.
Otherwise, if the size of images will not change (always the same size, user independent), then maybe better you use Cache.
Which method should I use to query the image size? The constructor or another method (OnInit, OnPreRender, etc, etc)?
In PreRender, because at this time all your controls are already created. This is the best moment to set the properties of your controls.
In your scope, I think so. The GDI+ is optimized for it.
Remote images?
I am going to use Image Optimization Framework that generates sprites and writes the image width and height so no need to reinvent the wheel.
https://web.archive.org/web/20211020150102/https://www.4guysfromrolla.com/articles/101310-1.aspx
Related
I need to write my own pdf viewer (UserControl). I use pdfium.dll for that. The wrapper of it is PdfiumService. This service can render pages to BitmapSource.
PdfViewer displays pages in VirtualizingStackPanel in ScrollViewer.
Any ideas how can I do lazy render for pdf? The problem is if pdf is about 20mb (1000 pages), rendered pages take about 2gb RAM.
Can VirtualizingStackPanel help me? I didn't find any events for "BeginVirtualizing" or something else. Any easy ways to know what item is displaying now?
Maybe something like that:
Calculate how many pages can be displayed at once.
See ScrollViewer's offset.
Calculate the index of page is now displaying.
Render 5 pages next to current.
Are there any ready solutions, or some tips, or ideas for this?
well, i had a bit of that with images from books....the trouble is not really the gui where you put the bitmap, but how you get the images from the library...is it one by one, sequentially or randomly ?
In fact, if you use a VirtualizingStackPanel it will only manage the gui element to create or destroy, but if you have a full collection of bitmaps in memory you are dead.
One way is to create Page object whitout the bitmap, and create the image when needed + add a timer that will clear all "oldest images"
I do something like that in CBR; and i use a custom control to display pages
private BitmapImage _Image = null;
/// <summary>
/// the image
/// </summary>
public BitmapImage Image
{
get
{
if (_Image == null)
_Image = (DocumentFactory.Instance.GetService(Parent) as BookService).GetImageFromStream(Parent.FilePath, FilePath);
ImageLastAcces = DateTime.Now;
return _Image;
}
set { _Image = value; }
}
I would like to show 1 million locations on a map based on OpenStreetMap.
I work on C# VS2013 and GMAP.NET WPF. But, when I added markers for each location, the map cannot be shown up because the marker is a bitmap image.
And 1 million markers consume too much memory on my laptop (with 8 GB mem).
The code is:
public void add_marker(List<Tuple<double, double>> latLongList, ref GMapControl myMap)
{
System.Windows.Media.Imaging.BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri(#"C:\djx_2014_6_3\my_projects\test_gmap_dot_net\GMap_WPF\try1\try_gmap_wpf\try_gmap_wpf\images\map_marker.png", UriKind.Absolute);
bitmapImage.DecodePixelHeight = 5;
bitmapImage.DecodePixelWidth = 5;
bitmapImage.EndInit();
foreach(var v in latLongList)
{
GMap.NET.PointLatLng point = new GMap.NET.PointLatLng(v.Item1, v.Item2);
GMapMarker marker = new GMapMarker(point);
System.Windows.Controls.Image image = new System.Windows.Controls.Image();
image.Source = bitmapImage;
marker.Shape = image;
marker.ZIndex = 5;
myMap.Markers.Add(marker);
}
}
I do not want to use the image as markers but I cannot find out how to use default marker in openStreetMap.
Any help would be appreciated.
WPF wasn't designed to be used for things like this. First of all you're creating a Bitmap for each tag, which is a user control and comes with some pretty heavy overhead for GUI hit-testing and binding etc. Secondly, WPF renders with DirectX, which means at some point all that data has to be set up with vertex buffers and uploaded into the graphics card. If you use data binding or try to create separate UI elements then this is going to take a lot of initial set-up time and memory, as you have already discovered. And if you try to draw them yourself (e.g. by creating your own user control and overriding OnRender) then it can be even worse, since all that work is now being done every frame (apart from buffered stuff which still incurs the initial setup anyway so you're back to square one).
If I had to do this myself I would start by organizing the data set with an appropriate 2D structure such as a k-d tree, an R*-tree or even a basic quad-tree. This will allow you to quickly determine at any given moment which markers are in the view frustum.
Next, I would add the appropriate bindings to scrollbars etc so that I could monitor exactly where the current view frustum was, and then each frame I would update the list of visible tags based on that. So long as you don't have more than a few thousand objects comes into view at once you should be ok, otherwise you'll have to stagger the updates over multiple frames with a queue.
If that doesn't suit your needs then you really have only two options left: 1) generate the raw bitmap data yourself manually, or 2) use a more suitable technology.
We're using migradoc api to create an rtf document.. intermittently when we add an image it;s being resized and coming out absolutely tiny.
Code sample as follows:
MigraDoc.DocumentObjectModel.Shapes.Image image = section.AddImage(imagePath);
image.WrapFormat.Style = MigraDoc.DocumentObjectModel.Shapes.WrapStyle.Through;
If I set LockAspectRatio to true and set a width it does stop is from rendering very small but ideally would like to be able to set a MaxWidth.
Has anyone experiences similar issue?
You have a choice of either changing the image itself by storing a different DPI value in it.
The .Net command to do so with a Bitmap bmp is:
bmp.SetResolution(newHRes , newVRes);
This should not involve reencoding, but I'm not sure.
However you also can simply set the desired DPI value in the Migradoc Image by using its Image.Resolution Property, which
Gets or sets a user defined resolution for the image in dots per inch.
I am not familiar with it at all but I found this code that might help you :
image.RelativeVertical = RelativeVertical.Page;
image.RelativeHorizontal = RelativeHorizontal.Page;
is there a way, to make a picture transparent in CF2.0? I have to lay a small Image over a textbox, but it has to be transparent so the User can see the text anyway. Do you have an Idea?
Thank you very much
twickl
Edit:
Thanks for your answers, I will check those links out!
To complete my Post, here is what I´m trying to do:
I want to show a small image (the image does not exist yet and I have to make ist, so I´m totaly open for all formats) that is an X on the right end of a textbox. By clicking that X the Text inside the textbox will be erased...like on the iPhone. But I can not build my own control becuse in my Project are so many TextBoxes that are allready custom Controls with the windows TextBox on it, that it will be to much work and testing to switch all of them to custom controls. So I have the Idea to make a small Panel, Picturebox, whatever, that lays above the Textbox. But it has to be transparent. The OS is Windows CE 5.0 with CF 2.0 on it.
Depending on what kind of transparency you need, you might choose any of these options:
1.) If you have an image with a specific portion that should be entirely transparent, you can use ImageAttributes.SetColorKey() to set a single transparent color and then pass this to Graphics.DrawImage. Your image will need to have one color (e.g. Color.Cyan) that will be drawn completely transparent.
2.) If you'd like the entire image to be partially transparent, e.g. for a fade in/fade out effect, you can P/Invoke the AlphaBlend() function, as demonstrated here.
3.) If you have an image with transparency information built in, e.g. a transparent PNG image that needs to be rendered on a variety of background colors, these previous methods will not work and you need to use the COM based IImage interface.
The COM interop from .NETCF is documented on this page (search for "IImage interface" on that page).
Option 3 is the most flexible, but it also involves the most implementation effort. If you follow up with more information about the kind of image you want to draw transparently and your target platform, we might be able to help more.
I did it by deriving a class from PictureBox and handling OnPaint. The key is the ImageAttributes object passed to DrawImage. I'm assuming pixel 0,0 is the transparent color, but you could handle that differently.
public partial class TransparentPictureBox : PictureBox
{
private Color tColor;
public TransparentPictureBox()
{
InitializeComponent();
}
public new Image Image
{
get { return base.Image; }
set
{
if (value == base.Image)
return;
if (value != null)
{
Bitmap bmp = new Bitmap(value);
tColor = bmp.GetPixel(0, 0);
this.Width = value.Width;
this.Height = value.Height;
}
base.Image = value;
}
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.Clear(this.BackColor);
if (Image == null)
return;
ImageAttributes attr = new ImageAttributes();
// Set the transparency color.
attr.SetColorKey(tColor, tColor);
Rectangle dstRect = new Rectangle(0, 0, base.Image.Width, base.Image.Height);
e.Graphics.DrawImage(base.Image, dstRect, 0, 0, base.Image.Width, base.Image.Height, GraphicsUnit.Pixel, attr);
}
}
I'm trying to draw images on a C# form (in PictureBoxes, as well as using Graphics.DrawImage()), and am looking for a way to draw them smooth. The images must be a format that supports transparency, so PNG, GIF, SVG, and WMF. C# doesn't support SVG files out of the box, and I haven't found a good third-party library to use (I found SvgNet, but couldn't figure it out).
I need to draw a WMF file, which C# can do via the Image.FromFile() function, but it's not anti-aliased. I was wondering if there's any way to smooth this out?
The previous answers, while well intended were only partially correct.
What was correct? PictureBox doesn't expose InterpolationMode.
What was off base?
1) While you can easily set that property in the Paint event from the picture box, in its parent, or via an override in a derived class. . . Either way works and both are just as easy. However, unless SmoothingMode is set, the InterpolationMode will be ignored. You won't get any anti-aliasing without SmoothingMode set to SmoothingMode.AnitAlias.
2) Using a Panel when you've clearly expressed an interest in using the features of PictureBox is the wrong direction to go. You will lack any ability to load, save, or assign images directly to it without explicitly coding those properties. . . Why re-invent the wheel? By deriving off of PictureBox you get all of that for free.
The news gets even better as I've done the hard work for you and it took me less time than writing this message.
I've provided two version both of which derive from PictureBox. First is a simple example which always uses the best quality rendering possible. This is also the slowest rendering. Second is a class that allows anyone to set the various rendering parameters via properties off the derived class. Once set these are used in the OnPaint override.
public class HighQualitySmoothPictureBox : PictureBox
{
protected override void OnPaint(PaintEventArgs pe)
{
// This is the only line needed for anti-aliasing to be turned on.
pe.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
// the next two lines of code (not comments) are needed to get the highest
// possible quiality of anti-aliasing. Remove them if you want the image to render faster.
pe.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
pe.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
// this line is needed for .net to draw the contents.
base.OnPaint(pe);
}
}
...
public class ConfigurableQualityPictureBox : PictureBox
{
// Note: the use of the "?" indicates the value type is "nullable."
// If the property is unset, it doesn't have a value, and therefore isn't
// used when the OnPaint method executes.
System.Drawing.Drawing2D.SmoothingMode? smoothingMode;
System.Drawing.Drawing2D.CompositingQuality? compositingQuality;
System.Drawing.Drawing2D.InterpolationMode? interpolationMode;
public System.Drawing.Drawing2D.SmoothingMode? SmoothingMode
{
get { return smoothingMode; }
set { smoothingMode = value; }
}
public System.Drawing.Drawing2D.CompositingQuality? CompositingQuality
{
get { return compositingQuality; }
set { compositingQuality = value; }
}
public System.Drawing.Drawing2D.InterpolationMode? InterpolationMode
{
get { return interpolationMode; }
set { interpolationMode = value; }
}
protected override void OnPaint(PaintEventArgs pe)
{
if (smoothingMode.HasValue)
pe.Graphics.SmoothingMode = smoothingMode.Value;
if (compositingQuality.HasValue)
pe.Graphics.CompositingQuality = compositingQuality.Value;
if (interpolationMode.HasValue)
pe.Graphics.InterpolationMode = interpolationMode.Value;
// this line is needed for .net to draw the contents.
base.OnPaint(pe);
}
}
When drawing the image to a canvas, you can change the interpolation mode to something nicer then nearest neighbor to make resized images smooth:
Graphics g = ...
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(...);
You'll need to add System.Drawing.Drawing2D to get the InterpolationMode enum.
Using PictureBox will be a problem - it doesn't expose an InterpolationMode property, so you'll need to roll your own or download one.