Making smaller Bitmaps from a Bitmap object [duplicate] - c#

This question already has answers here:
How do I crop an image using C#?
(15 answers)
Closed 2 years ago.
I have a Bitmap object of a hand-written survey (see survey image below) which contains various checkboxes. I am using an algorithm to compare a Bitmap of a blank, unmarked checkbox against a Bitmap of the same checkbox (which may or may not be marked), in order to determine if the checkbox has been marked or not.
Would it be possible to break the main survey Bitmap into smaller Bitmap objects? For example, with question 14 below I would like to make smaller Bitmap objects out of the red squares given the startX, endX, startY and endY locations of each checkbox.

I have an open source program called Transparency Maker that I am working on as we speak that could help you do this, or at least give you some guides on how to get started.
My project reads in a Bitmap (.png or .jpg) and creates a Pixel Database. Actually it is a List of PixelInformation objects.
LoadPixelDatabase Method
My Windows Forms app has a PictureBox called Canvas for the background image.
You could easily pass in an image as a parameter instead.
using System.Drawing;
using System.Drawing.Imaging;
public void LoadPixelDatabase()
{
// if we do not have a BackgroundImage yet
if (this.Canvas.BackgroundImage == null)
{
// to do: Show message
// bail
return;
}
// Create a Bitmap from the Source image
Bitmap source = new Bitmap(this.Canvas.BackgroundImage);
// Code To Lockbits
BitmapData bitmapData = source.LockBits(new Rectangle(0, 0, source.Width,
source.Height), ImageLockMode.ReadWrite, source.PixelFormat);
IntPtr pointer = bitmapData.Scan0;
int size = Math.Abs(bitmapData.Stride) * source.Height;
byte[] pixels = new byte[size];
Marshal.Copy(pointer, pixels, 0, size);
// End Code To Lockbits
// Marshal.Copy(pixels,0,pointer, size);
source.UnlockBits(bitmapData);
// test only
int length = pixels.Length;
// Create a new instance of a 'PixelDatabase' object.
this.PixelDatabase = new PixelDatabase();
// locals
Color color = Color.FromArgb(0, 0, 0);
int red = 0;
int green = 0;
int blue = 0;
int alpha = 0;
// variables to hold height and width
int width = source.Width;
int height = source.Height;
int x = -1;
int y = 0;
// Iterating the pixel array, every 4th byte is a new pixel,faster than GetPixel
for (int a = 0; a < pixels.Length; a = a + 4)
{
// increment the value for x
x++;
// every new column
if (x >= width)
{
// reset x
x = 0;
// Increment the value for y
y++;
}
// get the values for r, g, and blue
blue = pixels[a];
green = pixels[a + 1];
red = pixels[a + 2];
alpha = pixels[a + 3];
// create a color
color = Color.FromArgb(alpha, red, green, blue);
// Add this point
PixelInformation pixelInformation = this.PixelDatabase.AddPixel(color, x, y);
}
// Create a DirectBitmap
this.DirectBitmap = new DirectBitmap(source.Width, source.Height);
// Now we must copy over the Pixels from the PixelDatabase to the DirectBitmap
if ((this.HasPixelDatabase) && (ListHelper.HasOneOrMoreItems(this.PixelDatabase.Pixels)))
{
// iterate the pixels
foreach (PixelInformation pixel in this.PixelDatabase.Pixels)
{
// Set the pixel at this spot
DirectBitmap.SetPixel(pixel.X, pixel.Y, pixel.Color);
}
}
}
The way my app could help you is, once the PixelDatabase is loaded, you can perform LinqQueries such as:
// Get the pixels in the X range
pixels = pixels.Where(x => x.X >= MinValue && x.X <= MaxValue).ToList();
// Get the Y range
pixels = pixels.Where(x => x.Y >= MinValue && x.Y <= MaxValue).ToList();
(I know the above could be written in 1 line, it is hard to post here).
Once you have your pixels, you can create a new image:
Image image = new Bitmap(width, height);
Create another DirectBitmap for the new image, and then copy the pixels from the query above into your new image and save.
// Save the bitmap
bitmap.Save(fileName);
PixelDatabase.cs
#region using statements
using DataJuggler.Core.UltimateHelper;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
#endregion
namespace TransparencyMaker.Objects
{
#region class PixelDatabase
/// <summary>
/// This class represents a collection of PixelInformation objects
/// </summary>
public class PixelDatabase
{
#region Private Variables
private List<PixelInformation> pixels;
#endregion
#region Constructor
/// <summary>
/// Create a new instance of a PixelDatabase object
/// </summary>
public PixelDatabase()
{
// Create a new collection of 'PixelInformation' objects.
this.Pixels = new List<PixelInformation>();
}
#endregion
#region Methods
#region AddPixel(Color color, int x, int y)
/// <summary>
/// method returns the Pixel
/// </summary>
public PixelInformation AddPixel(Color color, int x, int y)
{
// Create a pixe
PixelInformation pixel = new PixelInformation();
// Set the color
pixel.Color = color;
// Set the values for x and y
pixel.X = x;
pixel.Y = y;
/// The Index is set before the count increments when this item is added
pixel.Index = this.Pixels.Count;
// Add this pixel
this.Pixels.Add(pixel);
// return value
return pixel;
}
#endregion
#endregion
#region Properties
#region HasOneOrMorePixels
/// <summary>
/// This property returns true if this object has one or more 'Pixels'.
/// </summary>
public bool HasOneOrMorePixels
{
get
{
// initial value
bool hasOneOrMorePixels = ((this.HasPixels) && (this.Pixels.Count > 0));
// return value
return hasOneOrMorePixels;
}
}
#endregion
#region HasPixels
/// <summary>
/// This property returns true if this object has a 'Pixels'.
/// </summary>
public bool HasPixels
{
get
{
// initial value
bool hasPixels = (this.Pixels != null);
// return value
return hasPixels;
}
}
#endregion
#region Pixels
/// <summary>
/// This property gets or sets the value for 'Pixels'.
/// </summary>
public List<PixelInformation> Pixels
{
get { return pixels; }
set { pixels = value; }
}
#endregion
#endregion
}
#endregion
}
PixelInformation.cs
#region using statements
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
#endregion
namespace TransparencyMaker.Objects
{
#region class PixelInformation
/// <summary>
/// This class is used to contain information about a pixel, and
/// </summary>
public class PixelInformation
{
#region Private Variables
private int index;
private Color color;
private int x;
private int y;
#endregion
#region Constructor
/// <summary>
/// Create a new instance of a PixelInformationObject
/// </summary>
public PixelInformation()
{
// Perform initializations for this object
Init();
}
#endregion
#region Methods
#region Init()
/// <summary>
/// This method performs initializations for this object.
/// </summary>
public void Init()
{
}
#endregion
#region ToString()
/// <summary>
/// method returns the String
/// </summary>
public override string ToString()
{
// Create a new instance of a 'StringBuilder' object.
StringBuilder sb = new StringBuilder();
// Append the string
sb.Append("R:");
sb.Append(Red);
sb.Append("G:");
sb.Append(Green);
sb.Append("B:");
sb.Append(Blue);
sb.Append("T:");
sb.Append(Total);
// set the return value
string toString = sb.ToString();
// return value
return toString;
}
#endregion
#endregion
#region Properties
#region Alpha
/// <summary>
/// This property gets or sets the value for 'Alpha'.
/// </summary>
public int Alpha
{
get
{
// initial value
int alpha = Color.A;
// return value
return alpha;
}
}
#endregion
#region Blue
/// <summary>
/// This property gets or sets the value for 'Blue'.
/// </summary>
public int Blue
{
get
{
// initial value
int blue = Color.B;
// return value
return blue;
}
}
#endregion
#region BlueGreen
/// <summary>
/// This read only property returns the value for 'BlueGreen'.
/// </summary>
public int BlueGreen
{
get
{
// initial value
int blueGreen = Blue + Green;
// return value
return blueGreen;
}
}
#endregion
#region BlueRed
/// <summary>
/// This read only property returns the value for 'BlueRed'.
/// </summary>
public int BlueRed
{
get
{
// initial value
int blueRed = Blue + Red;
// return value
return blueRed;
}
}
#endregion
#region Color
/// <summary>
/// This property gets or sets the value for 'Color'.
/// </summary>
public Color Color
{
get { return color; }
set { color = value; }
}
#endregion
#region Green
/// <summary>
/// This property gets or sets the value for 'Green'.
/// </summary>
public int Green
{
get
{
// initial value
int green = Color.G;
// return value
return green;
}
}
#endregion
#region GreenRed
/// <summary>
/// This read only property returns the value for 'GreenRed'.
/// </summary>
public int GreenRed
{
get
{
// initial value
int greenRed = Green + Red;
// return value
return greenRed;
}
}
#endregion
#region Index
/// <summary>
/// This property gets or sets the value for 'Index'.
/// </summary>
public int Index
{
get { return index; }
set { index = value; }
}
#endregion
#region Red
/// <summary>
/// This property gets or sets the value for 'Red'.
/// </summary>
public int Red
{
get
{
// initial value
int red = this.Color.R;
// return value
return red;
}
}
#endregion
#region Total
/// <summary>
/// This read only property returns the value for 'Total'.
/// </summary>
public int Total
{
get
{
// initial value
int total = Red + Green + Blue;
// return value
return total;
}
}
#endregion
#region X
/// <summary>
/// This property gets or sets the value for 'X'.
/// </summary>
public int X
{
get { return x; }
set { x = value; }
}
#endregion
#region Y
/// <summary>
/// This property gets or sets the value for 'Y'.
/// </summary>
public int Y
{
get { return y; }
set { y = value; }
}
#endregion
#endregion
}
#endregion
}
DirectBitmap.cs
This class here is called DirectBitmap, I didn't write it but I wish I knew who the author was to give them credit as it sped up my app by quite a bit.
#region using statements
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using TransparencyMaker.Objects;
#endregion
namespace TransparencyMaker.Util
{
#region class DirectBitmap
/// <summary>
/// This class is used as a faster alternative to GetPixel and SetPixel
/// </summary>
public class DirectBitmap : IDisposable
{
#region Private Variables
private History history;
#endregion
#region Constructor
/// <summary>
/// Create a new instance of a 'DirectBitmap' object.
/// </summary>
public DirectBitmap(int width, int height)
{
Width = width;
Height = height;
Bits = new Int32[width * height];
BitsHandle = GCHandle.Alloc(Bits, GCHandleType.Pinned);
Bitmap = new Bitmap(width, height, width * 4, PixelFormat.Format32bppPArgb, BitsHandle.AddrOfPinnedObject());
}
#endregion
#region Methods
#region Dispose()
/// <summary>
/// method Dispose
/// </summary>
public void Dispose()
{
if (Disposed) return;
Disposed = true;
Bitmap.Dispose();
BitsHandle.Free();
}
#endregion
#region GetPixel(int x, int y)
/// <summary>
/// method Get Pixel
/// </summary>
public Color GetPixel(int x, int y)
{
int index = x + (y * Width);
int col = Bits[index];
Color result = Color.FromArgb(col);
return result;
}
#endregion
#region HandleHistory(int x, int y, Guid historyId, Color previousColor)
/// <summary>
/// This method Handle History
/// </summary>
public void HandleHistory(int x, int y, Guid historyId, Color previousColor)
{
// If the History object exists
if ((!this.HasHistory) || (History.Id != historyId) && (historyId != Guid.Empty))
{
// here a new History object is created and the pixels are added to it instead
this.History = new History(historyId);
}
// If the History object exists
if (this.HasHistory)
{
// Create a new instance of a 'PixelInformation' object.
PixelInformation pixel = new PixelInformation();
pixel.X = x;
pixel.Y = y;
pixel.Color = previousColor;
// Add this pixel to history
this.History.ChangedPixels.Add(pixel);
}
}
#endregion
#region SetPixel(int x, int y, Color color)
/// <summary>
/// method Set Pixel
/// </summary>
public void SetPixel(int x, int y, Color color)
{
int index = x + (y * Width);
int col = color.ToArgb();
Bits[index] = col;
}
#endregion
#region SetPixel(int x, int y, Color color, Guid historyId, Color prevoiusColor)
/// <summary>
/// This method Sets a Pixel and it includes a historyId so any changes are stored in history
/// </summary>
public void SetPixel(int x, int y, Color color, Guid historyId, Color prevoiusColor)
{
// history has to be set before the pixel is set
// Handle the history
HandleHistory(x, y, historyId, prevoiusColor);
int index = x + (y * Width);
int col = color.ToArgb();
Bits[index] = col;
}
#endregion
#region UndoChanges()
/// <summary>
/// This method Undo Changes
/// </summary>
public void UndoChanges()
{
// If the History object exists
if ((this.HasHistory) && (this.History.HasChangedPixels))
{
// get the changed pixels
List<PixelInformation> pixels = this.History.ChangedPixels;
// Iterate the collection of PixelInformation objects
foreach (PixelInformation pixel in pixels)
{
// for debugging only
int alpha = pixel.Color.A;
// Restore this pixel
SetPixel(pixel.X, pixel.Y, pixel.Color);
}
// Remove the history
this.History = null;
}
}
#endregion
#endregion
#region Properties
#region Bitmap
/// <summary>
/// method [Enter Method Description]
/// </summary>
public Bitmap Bitmap { get; private set; }
#endregion
#region Bits
/// <summary>
/// method [Enter Method Description]
/// </summary>
public Int32[] Bits { get; private set; }
#endregion
#region BitsHandle
/// <summary>
/// This is a ptr to the garbage collector
/// </summary>
protected GCHandle BitsHandle { get; private set; }
#endregion
#region Disposed
/// <summary>
/// method [Enter Method Description]
/// </summary>
public bool Disposed { get; private set; }
#endregion
#region HasHistory
/// <summary>
/// This property returns true if this object has a 'History'.
/// </summary>
public bool HasHistory
{
get
{
// initial value
bool hasHistory = (this.History != null);
// return value
return hasHistory;
}
}
#endregion
#region Height
/// <summary>
/// method [Enter Method Description]
/// </summary>
public int Height { get; private set; }
#endregion
#region History
/// <summary>
/// This property gets or sets the value for 'History'.
/// </summary>
public History History
{
get { return history; }
set { history = value; }
}
#endregion
#region Width
/// <summary>
/// method [Enter Method Description]
/// </summary>
public int Width { get; private set; }
#endregion
#endregion
}
#endregion
}
Here is the full Project, as it is hard to post a complete application, I tried to show the most relevant parts:
https://github.com/DataJuggler/TransparencyMaker
I have a section on my YouTube channel for TransparencyMaker videos if anyone would care to watch: https://youtu.be/7kfNKyr_oqg
Version 2 is released, but still being polished.

Related

Newbie in front of a LinkedList code C#

I have this code in front but I can't build a linked list with more than one element. I see that in the methode "einsetzenNach" the code block in the if statement will never executed. The reason is that the cursor will never get != 0
The code original was in Java.
I am thankful for any tips.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace EinfachVerketteteListe
{
class Program
{
static void Main(string[] args)
{
Liste myList = new Liste();
myList.einsetzenVor(1, "Nr01");
myList.einsetzenVor(2, "Nr02");
myList.einsetzenNach(2, "Nr02");
myList.einsetzenNach(3, "Nr03");
myList.inhalt(3);
myList.laenge();
myList.ToString();
}
}
Here is the Cell class
class Zelle
{
// Contents
public Object inhalt;
// Next cell
public Zelle next;
public Zelle(Object el)
{
inhalt = el;
}
public Zelle(Object el, Zelle z)
{
inhalt = el;
next = z;
}
public Zelle(Zelle z)
{
next = z;
}
}
Our custom list class
public class Liste
{
// Start element
private Zelle anfang;
// Current Element
private Zelle cursor;
/// <summary>
/// There are no more items in the list, start is null
/// </summary>
/// <returns>start == null</returns>
public Boolean IstLeer()
{
return anfang == null;
}
/// <summary>
/// Length of the list
/// </summary>
/// <returns>l = listlenght</returns>
public int laenge()
{
Zelle cur = anfang;
int l = 0;
while (cur != null)
{
l++;
cur = cur.next;
}
return l;
}
/// <summary>
/// Check of the declared position is valid
/// </summary>
/// <param name="p">Position</param>
/// <returns>true/false</returns>
public Boolean istGueltigePosition(int p)
{
return (p >= 1) && (p <= laenge() );
}
/// <summary>
///
/// </summary>
/// <param name="p">Set cursor on a specific position</param>
public void setzeCursor(int p)
{
cursor = null;
if (istGueltigePosition(p))
{
Zelle cur = anfang;
// cur.next is null. The reason is that there is only one element in the list
// How can I fix this block. However I assume the code will work.
// Maybe I handle something not in the correct order.
for (int i = 0; i < p; i++)
{ cur = cur.next; }
cursor = cur;
}
}
Maybe the Methode initCursor() is the answer to my understanding problem.
However I have no idea in which way I can use this methode to print out the first element.
/// <summary>
/// Initial Position Cursor
/// </summary>
public void initCursor()
{
cursor = anfang;
}
/// <summary>
/// Search for specific object and return it's index
/// </summary>
/// <param name="e">Zu findende Daten</param>
/// <returns>p = Index</returns>
public int suche(Object e)
{
cursor = null;
int p = 0, l = 0;
Zelle z = anfang;
while (z != null)
{
l++;
if ( z.inhalt == e )
{
p = l;
cursor = z;
break;
}
z = z.next;
}
return p;
}
/// <summary>
/// Insert cell after element p
/// </summary>
/// <param name="p">Position</param>
/// <param name="e">Daten</param>
public void einsetzenNach(int p, Object e)
{
setzeCursor(p);
This if statment will never get !=0
if (cursor != null)
{
Zelle z = new Zelle(e, cursor.next);
cursor.next = z;
}
}
/// <summary>
/// Insert cell after element p
/// </summary>
/// <param name="p">Position</param>
/// <param name="e">Daten</param>
public void einsetzenVor(int p, Object e)
{
if (p > 1) einsetzenNach(p-1,e);
else
{
// Insert at the beginning
Zelle z = new Zelle(e, anfang);
anfang = z;
}
}
public void loesche(int p)
{
if (istGueltigePosition(p))
{
if (p == 1) // Lösche 1. Object
anfang = anfang.next;
else
{
setzeCursor(p - 1);
cursor.next = cursor.next.next;
}
}
}
/// <summary>
/// Show the content
/// </summary>
/// <param name="p">position</param>
/// <returns></returns>
public Object inhalt(int p)
{
setzeCursor(p);
if (cursor == null) return null;
return cursor.inhalt;
}
/// <summary>
/// next cell/data
/// </summary>
/// <returns>Daten</returns>
public Object naechstes()
{
if (cursor == null) return null;
Object e = cursor.inhalt;
cursor = cursor.next;
return e;
}
}
}
Let's assume you added the first element successfully to the list and now you want to add the second element.
After checking for a valid position you set your current cell to start (Zelle cur = anfang;). After that you want to get to the position where you want to insert your object. But since it is your first element there are no following elements. So cur will always be null after the loop.
Since your list has not a zero-based index you should start your loop with the first index of your list. Try the following loop:
for (int i = 1; i < p; i++)
{
if (cur.next == null)
{
break;
}
cur = cur.next;
}
cursor = cur;

c# transparent spinning wheel (or gif) is just half transparent

Well, first try was with a readily made spinning wheel (a gif). After that, I found a code that dynamically generates a wheel, but no way in both cases to have full transparency.
Depending on what I set as parent (form or panel) and the position on my wheel on form, the spin is just half transparent.
I use C# express (VS 2008).
All I want is a nice wheel in center of form, activated when BG is doing something, but full transparent so I can resize form.
Thanks,
Update:
I worked on some code that is draw a spinning wheel just fine. Almost working except OnPantBackground() which needs to be empty to paint behind, but in this case, a black rectangle is drawn because of ControlStyles.OptimizedDoubleBuffer enabled.
Any suggestions? Thank you.
using System;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing.Drawing2D;
namespace WinFormsControls
{
/// <summary>
/// A label that can be transparent.
/// </summary>
public class TransparentLabel : Control
{
// Constants =========================================================
private const double NumberOfDegreesInCircle = 360;
private const double NumberOfDegreesInHalfCircle = NumberOfDegreesInCircle / 2;
private const int DefaultInnerCircleRadius = 8;
private const int DefaultOuterCircleRadius = 10;
private const int DefaultNumberOfSpoke = 10;
private const int DefaultSpokeThickness = 4;
private readonly Color DefaultColor = Color.DarkGray;
private const int MacOSXInnerCircleRadius = 5;
private const int MacOSXOuterCircleRadius = 11;
private const int MacOSXNumberOfSpoke = 12;
private const int MacOSXSpokeThickness = 2;
private const int FireFoxInnerCircleRadius = 6;
private const int FireFoxOuterCircleRadius = 7;
private const int FireFoxNumberOfSpoke = 9;
private const int FireFoxSpokeThickness = 4;
private const int IE7InnerCircleRadius = 8;
private const int IE7OuterCircleRadius = 9;
private const int IE7NumberOfSpoke = 24;
private const int IE7SpokeThickness = 4;
// Enumeration =======================================================
public enum StylePresets
{
MacOSX,
Firefox,
IE7,
Custom
}
// Attributes ========================================================
private Timer m_Timer;
private bool m_IsTimerActive;
private int m_NumberOfSpoke;
private int m_SpokeThickness;
private int m_ProgressValue;
private int m_OuterCircleRadius;
private int m_InnerCircleRadius;
private PointF m_CenterPoint;
private Color m_Color;
private Color[] m_Colors;
private double[] m_Angles;
private StylePresets m_StylePreset;
// Properties ========================================================
/// <summary>
/// Gets or sets the lightest color of the circle.
/// </summary>
/// <value>The lightest color of the circle.</value>
[TypeConverter("System.Drawing.ColorConverter"),
Category("LoadingCircle"),
Description("Sets the color of spoke.")]
public Color Color
{
get
{
return m_Color;
}
set
{
m_Color = value;
GenerateColorsPallet();
Invalidate();
}
}
/// <summary>
/// Gets or sets the outer circle radius.
/// </summary>
/// <value>The outer circle radius.</value>
[System.ComponentModel.Description("Gets or sets the radius of outer circle."),
System.ComponentModel.Category("LoadingCircle")]
public int OuterCircleRadius
{
get
{
if (m_OuterCircleRadius == 0)
m_OuterCircleRadius = DefaultOuterCircleRadius;
return m_OuterCircleRadius;
}
set
{
m_OuterCircleRadius = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the inner circle radius.
/// </summary>
/// <value>The inner circle radius.</value>
[System.ComponentModel.Description("Gets or sets the radius of inner circle."),
System.ComponentModel.Category("LoadingCircle")]
public int InnerCircleRadius
{
get
{
if (m_InnerCircleRadius == 0)
m_InnerCircleRadius = DefaultInnerCircleRadius;
return m_InnerCircleRadius;
}
set
{
m_InnerCircleRadius = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the number of spoke.
/// </summary>
/// <value>The number of spoke.</value>
[System.ComponentModel.Description("Gets or sets the number of spoke."),
System.ComponentModel.Category("LoadingCircle")]
public int NumberSpoke
{
get
{
if (m_NumberOfSpoke == 0)
m_NumberOfSpoke = DefaultNumberOfSpoke;
return m_NumberOfSpoke;
}
set
{
if (m_NumberOfSpoke != value && m_NumberOfSpoke > 0)
{
m_NumberOfSpoke = value;
GenerateColorsPallet();
GetSpokesAngles();
Invalidate();
}
}
}
/// <summary>
/// Gets or sets a value indicating whether this <see cref="T:LoadingCircle"/> is active.
/// </summary>
/// <value><c>true</c> if active; otherwise, <c>false</c>.</value>
[System.ComponentModel.Description("Gets or sets the number of spoke."),
System.ComponentModel.Category("LoadingCircle")]
public bool Active
{
get
{
return m_IsTimerActive;
}
set
{
m_IsTimerActive = value;
ActiveTimer();
}
}
/// <summary>
/// Gets or sets the spoke thickness.
/// </summary>
/// <value>The spoke thickness.</value>
[System.ComponentModel.Description("Gets or sets the thickness of a spoke."),
System.ComponentModel.Category("LoadingCircle")]
public int SpokeThickness
{
get
{
if (m_SpokeThickness <= 0)
m_SpokeThickness = DefaultSpokeThickness;
return m_SpokeThickness;
}
set
{
m_SpokeThickness = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the rotation speed.
/// </summary>
/// <value>The rotation speed.</value>
[System.ComponentModel.Description("Gets or sets the rotation speed. Higher the slower."),
System.ComponentModel.Category("LoadingCircle")]
public int RotationSpeed
{
get
{
return m_Timer.Interval;
}
set
{
if (value > 0)
m_Timer.Interval = value;
}
}
/// <summary>
/// Quickly sets the style to one of these presets, or a custom style if desired
/// </summary>
/// <value>The style preset.</value>
[Category("LoadingCircle"),
Description("Quickly sets the style to one of these presets, or a custom style if desired"),
DefaultValue(typeof(StylePresets), "Custom")]
public StylePresets StylePreset
{
get { return m_StylePreset; }
set
{
m_StylePreset = value;
switch (m_StylePreset)
{
case StylePresets.MacOSX:
SetCircleAppearance(MacOSXNumberOfSpoke,
MacOSXSpokeThickness, MacOSXInnerCircleRadius,
MacOSXOuterCircleRadius);
break;
case StylePresets.Firefox:
SetCircleAppearance(FireFoxNumberOfSpoke,
FireFoxSpokeThickness, FireFoxInnerCircleRadius,
FireFoxOuterCircleRadius);
break;
case StylePresets.IE7:
SetCircleAppearance(IE7NumberOfSpoke,
IE7SpokeThickness, IE7InnerCircleRadius,
IE7OuterCircleRadius);
break;
case StylePresets.Custom:
SetCircleAppearance(DefaultNumberOfSpoke,
DefaultSpokeThickness,
DefaultInnerCircleRadius,
DefaultOuterCircleRadius);
break;
}
}
}
/// <summary>
/// Creates a new <see cref="TransparentLabel"/> instance.
/// </summary>
public TransparentLabel()
{
TabStop = false;
InitializeComponent();
SetStyle(ControlStyles.UserPaint, true);
//SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
m_Color = DefaultColor;
GenerateColorsPallet();
GetSpokesAngles();
GetControlCenterPoint();
m_Timer = new Timer();
m_Timer.Tick += new EventHandler(aTimer_Tick);
ActiveTimer();
this.Resize += new EventHandler(LoadingCircle_Resize);
this.DoubleBuffered = true;
this.SetStyle(ControlStyles.UserPaint |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.ContainerControl |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.SupportsTransparentBackColor
, true);
}
// Events ============================================================
/// <summary>
/// Handles the Resize event of the LoadingCircle control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
void LoadingCircle_Resize(object sender, EventArgs e)
{
GetControlCenterPoint();
}
/// <summary>
/// Handles the Tick event of the aTimer control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
void aTimer_Tick(object sender, EventArgs e)
{
m_ProgressValue = ++m_ProgressValue % m_NumberOfSpoke;
Invalidate();
}
/// <summary>
/// Gets the creation parameters.
/// </summary>
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
// Overridden Methods ================================================
/// <summary>
/// Retrieves the size of a rectangular area into which a control can be fitted.
/// </summary>
/// <param name="proposedSize">The custom-sized area for a control.</param>
/// <returns>
/// An ordered pair of type <see cref="T:System.Drawing.Size"></see> representing the width and height of a rectangle.
/// </returns>
public override Size GetPreferredSize(Size proposedSize)
{
proposedSize.Width =
(m_OuterCircleRadius + m_SpokeThickness) * 2;
return proposedSize;
}
/// <summary>
/// Paints the background.
/// </summary>
/// <param name="e">E.</param>
///
protected override void OnPaintBackground(PaintEventArgs e)
{
// do nothing
}
/// <summary>
/// Paints the control.
/// </summary>
/// <param name="e">E.</param>
protected override void OnPaint(PaintEventArgs e)
{
//DrawText();
if (m_NumberOfSpoke > 0)
{
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
int intPosition = m_ProgressValue;
for (int intCounter = 0; intCounter < m_NumberOfSpoke; intCounter++)
{
intPosition = intPosition % m_NumberOfSpoke;
DrawLine(e.Graphics,
GetCoordinate(m_CenterPoint, m_InnerCircleRadius, m_Angles[intPosition]),
GetCoordinate(m_CenterPoint, m_OuterCircleRadius, m_Angles[intPosition]),
m_Colors[intCounter], m_SpokeThickness);
intPosition++;
}
}
base.OnPaint(e);
}
/*
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == 0x000F)
{
DrawText();
}
}
private void DrawText()
{
using (Graphics graphics = CreateGraphics())
using (SolidBrush brush = new SolidBrush(ForeColor))
{
SizeF size = graphics.MeasureString(Text, Font);
// first figure out the top
float top = 0;
switch (textAlign)
{
case ContentAlignment.MiddleLeft:
case ContentAlignment.MiddleCenter:
case ContentAlignment.MiddleRight:
top = (Height - size.Height) / 2;
break;
case ContentAlignment.BottomLeft:
case ContentAlignment.BottomCenter:
case ContentAlignment.BottomRight:
top = Height - size.Height;
break;
}
float left = -1;
switch (textAlign)
{
case ContentAlignment.TopLeft:
case ContentAlignment.MiddleLeft:
case ContentAlignment.BottomLeft:
if (RightToLeft == RightToLeft.Yes)
left = Width - size.Width;
else
left = -1;
break;
case ContentAlignment.TopCenter:
case ContentAlignment.MiddleCenter:
case ContentAlignment.BottomCenter:
left = (Width - size.Width) / 2;
break;
case ContentAlignment.TopRight:
case ContentAlignment.MiddleRight:
case ContentAlignment.BottomRight:
if (RightToLeft == RightToLeft.Yes)
left = -1;
else
left = Width - size.Width;
break;
}
graphics.DrawString(Text, Font, brush, left, top);
}
}
*/
/*
/// <summary>
/// Gets or sets the text associated with this control.
/// </summary>
/// <returns>
/// The text associated with this control.
/// </returns>
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
RecreateHandle();
}
}
/// <summary>
/// Gets or sets a value indicating whether control's elements are aligned to support locales using right-to-left fonts.
/// </summary>
/// <value></value>
/// <returns>
/// One of the <see cref="T:System.Windows.Forms.RightToLeft"/> values. The default is <see cref="F:System.Windows.Forms.RightToLeft.Inherit"/>.
/// </returns>
/// <exception cref="T:System.ComponentModel.InvalidEnumArgumentException">
/// The assigned value is not one of the <see cref="T:System.Windows.Forms.RightToLeft"/> values.
/// </exception>
public override RightToLeft RightToLeft
{
get
{
return base.RightToLeft;
}
set
{
base.RightToLeft = value;
RecreateHandle();
}
}
/// <summary>
/// Gets or sets the font of the text displayed by the control.
/// </summary>
/// <value></value>
/// <returns>
/// The <see cref="T:System.Drawing.Font"/> to apply to the text displayed by the control. The default is the value of the <see cref="P:System.Windows.Forms.Control.DefaultFont"/> property.
/// </returns>
public override Font Font
{
get
{
return base.Font;
}
set
{
base.Font = value;
RecreateHandle();
}
}
private ContentAlignment textAlign = ContentAlignment.TopLeft;
/// <summary>
/// Gets or sets the text alignment.
/// </summary>
public ContentAlignment TextAlign
{
get { return textAlign; }
set
{
textAlign = value;
RecreateHandle();
}
}
*/
private void InitializeComponent()
{
this.SuspendLayout();
this.ResumeLayout(false);
}
// Methods ===========================================================
/// <summary>
/// Darkens a specified color.
/// </summary>
/// <param name="_objColor">Color to darken.</param>
/// <param name="_intPercent">The percent of darken.</param>
/// <returns>The new color generated.</returns>
private Color Darken(Color _objColor, int _intPercent)
{
int intRed = _objColor.R;
int intGreen = _objColor.G;
int intBlue = _objColor.B;
return Color.FromArgb(_intPercent, Math.Min(intRed, byte.MaxValue), Math.Min(intGreen, byte.MaxValue), Math.Min(intBlue, byte.MaxValue));
}
/// <summary>
/// Generates the colors pallet.
/// </summary>
private void GenerateColorsPallet()
{
m_Colors = GenerateColorsPallet(m_Color, Active, m_NumberOfSpoke);
}
/// <summary>
/// Generates the colors pallet.
/// </summary>
/// <param name="_objColor">Color of the lightest spoke.</param>
/// <param name="_blnShadeColor">if set to <c>true</c> the color will be shaded on X spoke.</param>
/// <returns>An array of color used to draw the circle.</returns>
private Color[] GenerateColorsPallet(Color _objColor, bool _blnShadeColor, int _intNbSpoke)
{
Color[] objColors = new Color[NumberSpoke];
// Value is used to simulate a gradient feel... For each spoke, the
// color will be darken by value in intIncrement.
byte bytIncrement = (byte)(byte.MaxValue / NumberSpoke);
//Reset variable in case of multiple passes
byte PERCENTAGE_OF_DARKEN = 0;
for (int intCursor = 0; intCursor < NumberSpoke; intCursor++)
{
if (_blnShadeColor)
{
if (intCursor == 0 || intCursor < NumberSpoke - _intNbSpoke)
objColors[intCursor] = _objColor;
else
{
// Increment alpha channel color
PERCENTAGE_OF_DARKEN += bytIncrement;
// Ensure that we don't exceed the maximum alpha
// channel value (255)
if (PERCENTAGE_OF_DARKEN > byte.MaxValue)
PERCENTAGE_OF_DARKEN = byte.MaxValue;
// Determine the spoke forecolor
objColors[intCursor] = Darken(_objColor, PERCENTAGE_OF_DARKEN);
}
}
else
objColors[intCursor] = _objColor;
}
return objColors;
}
/// <summary>
/// Gets the control center point.
/// </summary>
private void GetControlCenterPoint()
{
m_CenterPoint = GetControlCenterPoint(this);
}
/// <summary>
/// Gets the control center point.
/// </summary>
/// <returns>PointF object</returns>
private PointF GetControlCenterPoint(Control _objControl)
{
return new PointF(_objControl.Width / 2, _objControl.Height / 2 - 1);
}
/// <summary>
/// Draws the line with GDI+.
/// </summary>
/// <param name="_objGraphics">The Graphics object.</param>
/// <param name="_objPointOne">The point one.</param>
/// <param name="_objPointTwo">The point two.</param>
/// <param name="_objColor">Color of the spoke.</param>
/// <param name="_intLineThickness">The thickness of spoke.</param>
private void DrawLine(Graphics _objGraphics, PointF _objPointOne, PointF _objPointTwo,
Color _objColor, int _intLineThickness)
{
using (Pen objPen = new Pen(new SolidBrush(_objColor), _intLineThickness))
{
objPen.StartCap = LineCap.Round;
objPen.EndCap = LineCap.Round;
_objGraphics.DrawLine(objPen, _objPointOne, _objPointTwo);
}
}
/// <summary>
/// Gets the coordinate.
/// </summary>
/// <param name="_objCircleCenter">The Circle center.</param>
/// <param name="_intRadius">The radius.</param>
/// <param name="_dblAngle">The angle.</param>
/// <returns></returns>
private PointF GetCoordinate(PointF _objCircleCenter, int _intRadius, double _dblAngle)
{
double dblAngle = Math.PI * _dblAngle / NumberOfDegreesInHalfCircle;
return new PointF(_objCircleCenter.X + _intRadius * (float)Math.Cos(dblAngle),
_objCircleCenter.Y + _intRadius * (float)Math.Sin(dblAngle));
}
/// <summary>
/// Gets the spokes angles.
/// </summary>
private void GetSpokesAngles()
{
m_Angles = GetSpokesAngles(NumberSpoke);
}
/// <summary>
/// Gets the spoke angles.
/// </summary>
/// <param name="_shtNumberSpoke">The number spoke.</param>
/// <returns>An array of angle.</returns>
private double[] GetSpokesAngles(int _intNumberSpoke)
{
double[] Angles = new double[_intNumberSpoke];
double dblAngle = (double)NumberOfDegreesInCircle / _intNumberSpoke;
for (int shtCounter = 0; shtCounter < _intNumberSpoke; shtCounter++)
Angles[shtCounter] = (shtCounter == 0 ? dblAngle : Angles[shtCounter - 1] + dblAngle);
return Angles;
}
/// <summary>
/// Actives the timer.
/// </summary>
private void ActiveTimer()
{
if (m_IsTimerActive)
{
m_Timer.Start();
}
else
{
m_Timer.Stop();
m_ProgressValue = 0;
}
GenerateColorsPallet();
Invalidate();
}
/// <summary>
/// Sets the circle appearance.
/// </summary>
/// <param name="numberSpoke">The number spoke.</param>
/// <param name="spokeThickness">The spoke thickness.</param>
/// <param name="innerCircleRadius">The inner circle radius.</param>
/// <param name="outerCircleRadius">The outer circle radius.</param>
public void SetCircleAppearance(int numberSpoke, int spokeThickness,
int innerCircleRadius, int outerCircleRadius)
{
NumberSpoke = numberSpoke;
SpokeThickness = spokeThickness;
InnerCircleRadius = innerCircleRadius;
OuterCircleRadius = outerCircleRadius;
Invalidate();
}
}
}
Sorry for answering my own post, but after testing a lot of projects, modify without success, I finally found one that is almost full transparent
https://www.codeproject.com/Articles/27185/WYSIWYG-Progress-Circle-for-NET-Framework-C
That guy made a very well job almost 10 years ago, at least inner part of spin is copied and re-drawn in OnPaint so quite nice transparent over RichTextBox control where all other projects failed :)
The only small problem is that the circle trail, don't copy the bmp behind so there will always be a comet effect, but I can live with that! Much pleasant effect than other solutions, including few on stackoverflow.
In case anyone will need!
Wow that seems incredibly complicated. I don't know much about wpf, but on iOS what we usually do is load an image that has the exact properties we need (here, transparent background), and just rotate the image to make the spinning effect.
You only need very little logic to make it appear/disappear and rotate. For a very basic spinner you get to create and use that class in a matter of seconds.
At least that's the route I'd go for, instead of writing 300 lines of code to manually handle the spinner :p

How to re-size image without effecting the quality of original image in C#? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Closed 9 years ago.
Improve this question
How to create a multiple image thumbnails of a image in C#.
Condition is:
1. Image quality should not effect.
2. Thumbnails size should be not same.
If you are using asp.net mvc then you could try the build-in WebImage class. More info can be found here: http://msdn.microsoft.com/en-us/library/system.web.helpers.webimage(v=vs.111).aspx
For more professional use, where you want to control everything, you could use: http://imageresizing.net/ this suite is very advanced with lots of features on image resizing.
There are also plenty of options available for writing your own code, an example can be found here: http://www.codeproject.com/Articles/191424/Resizing-an-Image-On-The-Fly-using-NET
My suggestion would be: "Don't try to invent the wheel again", there are a lot of good solutions out there.
Hope this can help you out.
using System.ComponentModel;
using System.Web.UI;
/// <summary>
/// An abstract ImageFilter class.
/// </summary>
public abstract class ImageFilter
{
/// <summary>
/// Processes the image with current filter.
/// </summary>
/// <param name="image">The image to process.</param>
/// <returns>Returns a Image after applying current filter.</returns>
public abstract System.Drawing.Image Process(System.Drawing.Image image);
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
public override string ToString()
{
return "Image Filter";
}
}
public class ResizeFilter : ImageFilter
{
private int x = -1;
private int y = -1;
private int height;
private int width;
private InterpolationMode interpolationMode = InterpolationMode.Bicubic;
private ResizeMode resizeMode = ResizeMode.Fit;
[DefaultValue(-1)]
[Category("Behavior")]
public int X
{
get
{
return this.x;
}
set
{
if (value < 0)
{
value = -1;
}
x = value;
}
}
[DefaultValue(-1)]
[Category("Behavior")]
public int Y
{
get
{
return this.y;
}
set
{
if (value < -1)
{
value = -1;
}
y = value;
}
}
/// <summary>
/// Gets or sets a value indicating whether disable transparency.
/// </summary>
/// <value>
/// <see langword="true"/> if disable transparency; otherwise, <see langword="false"/>.
/// </value>
/// <author>Anwar</author>
/// <datetime>3/4/2011 5:37 PM</datetime>
[DefaultValue(false), Category("Behavior")]
public bool DisableTransparency { get; set; }
/// <summary>
/// Gets or sets the maximum height of the resulting image.
/// </summary>
/// <value>The height of the resulting image.</value>
[DefaultValue(0)]
[Category("Behavior")]
public int Height
{
get
{
return this.height;
}
set
{
CheckValue(value);
this.height = value;
}
}
/// <summary>
/// Gets or sets the interpolation mode used for resizing images. The default is HighQualityBicubic.
/// </summary>
/// <value>The interpolation mode.</value>
[DefaultValue(InterpolationMode.HighQualityBicubic)]
[Category("Behavior")]
public InterpolationMode InterpolationMode
{
get { return this.interpolationMode; }
set { this.interpolationMode = value; }
}
/// <summary>
/// Gets or sets the resize mode. The default value is Fit.
/// </summary>
/// <value>The image resize mode.</value>
[DefaultValue(ResizeMode.Fit)]
[Category("Behavior")]
public ResizeMode ResizeMode
{
get { return this.resizeMode; }
set { this.resizeMode = value; }
}
/// <summary>
/// Gets or sets the maximum width of the resulting image.
/// </summary>
/// <value>The width of the image..</value>
[DefaultValue(0)]
[Category("Behavior")]
public int Width
{
get
{
return this.width;
}
set
{
CheckValue(value);
this.width = value;
}
}
/// <summary>
/// Processes the image with current filter.
/// </summary>
/// <param name="image">The image to process.</param>
/// <returns>Returns a Image after applying current filter.</returns>
public override Image Process(Image image)
{
int scaledHeight = (int)(image.Height * (this.Width / (float)image.Width));
int scaledWidth = (int)(image.Width * (this.Height / (float)image.Height));
switch (this.ResizeMode)
{
case ResizeMode.Fit:
return this.FitImage(image, scaledHeight, scaledWidth);
case ResizeMode.Crop:
return this.CropImage(image, scaledHeight, scaledWidth);
default:
Debug.Fail("Should not reach this");
break;
}
return null;
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
public override string ToString()
{
return "Resize Filter";
}
private static void CheckValue(int value)
{
if (value < 0)
{
throw new ArgumentOutOfRangeException("value");
}
}
private Image CropImage(Image img, int scaledHeight, int scaledWidth)
{
int resizeWidth;
int resizeHeight;
if (this.Width != 0 && this.Height != 0)
{
resizeWidth = this.Width;
resizeHeight = this.Height;
}
else if (this.Height == 0)
{
resizeWidth = this.Width;
resizeHeight = scaledHeight;
}
else if (this.Width == 0)
{
resizeWidth = scaledWidth;
resizeHeight = this.Height;
}
else
{
if (this.Width / (float)img.Width > this.Height / (float)img.Height)
{
resizeWidth = this.Width;
resizeHeight = scaledHeight;
}
else
{
resizeWidth = scaledWidth;
resizeHeight = this.Height;
}
}
Bitmap newImage = new Bitmap(this.Width, this.Height);
using (Graphics graphics = Graphics.FromImage(newImage))
{
this.SetupGraphics(graphics);
if (this.DisableTransparency)
{
graphics.FillRectangle(new SolidBrush(Color.White), 0, 0, resizeWidth, resizeHeight);
}
int srcX = this.X;
int srcY = this.Y;
if (srcX == -1)
{
srcX = (this.Width - resizeWidth) / 2;
}
if (srcY == -1)
{
srcY = (this.Height - resizeHeight) / 2;
}
graphics.DrawImage(img, new Rectangle(0, 0, resizeWidth, resizeHeight), srcX, srcY, resizeWidth, resizeHeight, GraphicsUnit.Pixel);
}
return newImage;
}
private Image FitImage(Image img, int scaledHeight, int scaledWidth)
{
int resizeWidth;
int resizeHeight;
if (this.Width != 0 && this.Height != 0)
{
resizeWidth = this.Width;
resizeHeight = this.Height;
}
else if (this.Height == 0)
{
resizeWidth = this.Width;
resizeHeight = scaledHeight;
}
else if (this.Width == 0)
{
resizeWidth = scaledWidth;
resizeHeight = this.Height;
}
else
{
if (this.Width / (float)img.Width < this.Height / (float)img.Height)
{
resizeWidth = this.Width;
resizeHeight = scaledHeight;
}
else
{
resizeWidth = scaledWidth;
resizeHeight = this.Height;
}
}
Bitmap newimage = new Bitmap(resizeWidth, resizeHeight);
using (Graphics gra = Graphics.FromImage(newimage))
{
if (DisableTransparency)
{
gra.FillRectangle(new SolidBrush(Color.White), 0, 0, resizeWidth, resizeHeight);
}
this.SetupGraphics(gra);
gra.DrawImage(img, 0, 0, resizeWidth, resizeHeight);
}
return newimage;
}
private void SetupGraphics(Graphics graphics)
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighSpeed;
graphics.InterpolationMode = this.InterpolationMode;
}
}
////Seprate this enum in your different class ResizeMode.cs
/// <summary>
/// Image Resize Modes.
/// </summary>
public enum ResizeMode
{
/// <summary>
/// Fit mode maintains the aspect ratio of the original image while ensuring that the dimensions of the result
/// do not exceed the maximum values for the resize transformation.
/// </summary>
Fit,
/// <summary>
/// Crop resizes the image and removes parts of it to ensure that the dimensions of the result are exactly
/// as specified by the transformation.
/// </summary>
Crop
}
///Your Controller Code
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult ImportImage()
{
var postedFile = Request.Files["FileUpload1"];
if (postedFile != null && postedFile.ContentLength > 0 )
{
MemoryStream ms=new MemoryStream();
postedFile.InputStream.CopyTo(ms);
Image image=new Bitmap(ms);
Guid newImageName=Guid.NewGuid();
string newNameToImage = newImageName.ToString() + Path.GetExtension(postedFile.FileName);
ResizeFilter resizeFilter=new ResizeFilter {Height = 0, Width = 75};
using (Image thumbnailImage = resizeFilter.Process(image))
{
var imagePath = Path.Combine(Server.MapPath("~/Content/Images"), newNameToImage);
thumbnailImage.Save(imagePath);
}
resizeFilter.Width = 350;
resizeFilter.Height = 0;
newImageName = Guid.NewGuid();
newNameToImage = newImageName.ToString() + Path.GetExtension(postedFile.FileName);
using (Image middleImage = resizeFilter.Process(image))
{
var imagePath = Path.Combine(Server.MapPath("~/Content/Images"), newNameToImage);
middleImage.Save(imagePath);
}
}
return RedirectToAction("Index");
}
}
///View Coding
#{
ViewBag.Title = "Index";
}
#using (Html.BeginForm("ImportImage", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<input type="file" name="FileUpload1" id="FileUpload1" />
<input type="submit" id="submit" />
}

WPF - VirtualPanel / ZoomableCanvas - Fastest way to find the intersection of objects in an ObservableCollection?

I am using a WPF controller called ZoomableCanvas (see http://blogs.msdn.com/b/kaelr/archive/2010/08/11/zoomableapplication2-a-million-items.aspx), which is a virtualized canvas (renders only what needs to be in the display area). I am trying to implement a method to, given the viewport rectangle, finds objects in the ObservableCollection whose top/left parameters as a point intersect the rectangle. I've got it working, as below:
//This is all in a class that inherits from ObserableCollection<BlockTile>
//Where blocktile is a struct-like class used to store brush and position info for the
//tiles that make up our canvas content.
public IEnumerable<int> Query(Rect rectangle)
{
rectangle.Intersect(Extent); //Extent is our total data set, in case the
//viewport pans past our data set.
foreach (BlockTile t in this)
{
if(rectangle.Contains(new Point(t.left, t.top)))
{
yield return (int)this.IndexOf(t);
}
}
}
The problem with this approach is that my BlockTile collection is around 70,000 items and iterating across the entire data set is killing performance. I'm trying to figure out if, given an IEnumerable of objects, there's a way to filter these items without iterating the entire resultset. My guess is no.
My gut says to try and insert the data in-order and then retrieving it becomes mapping the position in the canvas against the insert-order. However that's clumsy and inexact. The other option is to query the root data source and back into the calculation we used to get the position on the canvas in the first place. Also kind of clumsy, but at least we're removing the full iteration. Just curious if there's any tips or tricks to LINQ to replace the foreach but that doesn't actually do a foreach behind the scenes.
As #Kek alluded to, what you want is a hierarchical collision structure, like a QuadTree. I googled a bit and found what appears to be an ideal implementation (The QuadTree class is shamelessly lifted from GaryTexmo's Generic QuadTree, the example code I threw together)
Sample output from this script:
Filling tree with 10000 rectangles took 00:00:00.0066186
1000 queries of quadtree took 00:00:00.4597303 and avged 4334 hits per query
Code:
void Main()
{
var width = 10000;
var height = 10000;
var rectCount = 10000;
var queryCount = 1000;
var tree = new QuadTree<RectangleHolder>(0, 0, width, height);
var rnd = new Random();
var rects = Enumerable.Range(0, rectCount)
.Select(_ => new { px = new []{rnd.Next(width), rnd.Next(width)}, py = new []{rnd.Next(height), rnd.Next(height)}})
.Select(pts => new Rectangle(pts.px.Min(), pts.py.Min(), pts.px.Max() - pts.px.Min(), pts.py.Max() - pts.py.Min()))
.ToList();
var queryRanges = Enumerable.Range(0, queryCount)
.Select(_ => new { px = new []{rnd.Next(width), rnd.Next(width)}, py = new []{rnd.Next(height), rnd.Next(height)}})
.Select(pts => new Rectangle(pts.px.Min(), pts.py.Min(), pts.px.Max() - pts.px.Min(), pts.py.Max() - pts.py.Min()))
.ToList();
var sw = Stopwatch.StartNew();
foreach (var rect in rects)
{
tree.Insert(new RectangleHolder(rect));
}
Console.WriteLine("Filling tree with {0} rectangles took {1}", rectCount, sw.Elapsed);
sw.Restart();
var totHits = 0;
var res = new List<RectangleHolder>();
foreach (var queryRect in queryRanges)
{
res.Clear();
tree.GetObjects(queryRect, ref res);
totHits += res.Count;
}
Console.WriteLine("{0} queries of quadtree took {1} and avged {2} hits per query", queryCount, sw.Elapsed, (totHits / queryCount));
}
public class RectangleHolder : IHasRectangle
{
public RectangleHolder(Rectangle rect)
{
Rect = rect;
}
public Rectangle Rect {get; private set;}
}
public interface IHasRectangle
{
/// <summary>
/// The rectangle that defines the object's boundaries.
/// </summary>
Rectangle Rect { get; }
}
public class QuadTree<T> where T : IHasRectangle
{
#region Constants
// How many objects can exist in a QuadTree before it sub divides itself
private const int MAX_OBJECTS_PER_NODE = 2;
#endregion
#region Private Members
private List<T> m_objects = null; // The objects in this QuadTree
private Rectangle m_rect; // The area this QuadTree represents
private QuadTree<T> m_childTL = null; // Top Left Child
private QuadTree<T> m_childTR = null; // Top Right Child
private QuadTree<T> m_childBL = null; // Bottom Left Child
private QuadTree<T> m_childBR = null; // Bottom Right Child
#endregion
#region Public Properties
/// <summary>
/// The area this QuadTree represents.
/// </summary>
public Rectangle QuadRect { get { return m_rect; } }
/// <summary>
/// The top left child for this QuadTree
/// </summary>
public QuadTree<T> TopLeftChild { get { return m_childTL; } }
/// <summary>
/// The top right child for this QuadTree
/// </summary>
public QuadTree<T> TopRightChild { get { return m_childTR; } }
/// <summary>
/// The bottom left child for this QuadTree
/// </summary>
public QuadTree<T> BottomLeftChild { get { return m_childBL; } }
/// <summary>
/// The bottom right child for this QuadTree
/// </summary>
public QuadTree<T> BottomRightChild { get { return m_childBR; } }
/// <summary>
/// The objects contained in this QuadTree at it's level (ie, excludes children)
/// </summary>
public List<T> Objects { get { return m_objects; } }
/// <summary>
/// How many total objects are contained within this QuadTree (ie, includes children)
/// </summary>
public int Count { get { return this.ObjectCount(); } }
#endregion
#region Constructor
/// <summary>
/// Creates a QuadTree for the specified area.
/// </summary>
/// <param name="rect">The area this QuadTree object will encompass.</param>
public QuadTree(Rectangle rect)
{
m_rect = rect;
}
/// <summary>
/// Creates a QuadTree for the specified area.
/// </summary>
/// <param name="x">The top-left position of the area rectangle.</param>
/// <param name="y">The top-right position of the area reactangle.</param>
/// <param name="width">The width of the area rectangle.</param>
/// <param name="height">The height of the area rectangle.</param>
public QuadTree(int x, int y, int width, int height)
{
m_rect = new Rectangle(x, y, width, height);
}
#endregion
#region Private Members
/// <summary>
/// Add an item to the object list.
/// </summary>
/// <param name="item">The item to add.</param>
private void Add(T item)
{
if (m_objects == null)
m_objects = new List<T>();
m_objects.Add(item);
}
/// <summary>
/// Remove an item from the object list.
/// </summary>
/// <param name="item">The object to remove.</param>
private void Remove(T item)
{
if (m_objects != null && m_objects.Contains(item))
m_objects.Remove(item);
}
/// <summary>
/// Get the total for all objects in this QuadTree, including children.
/// </summary>
/// <returns>The number of objects contained within this QuadTree and its children.</returns>
private int ObjectCount()
{
int count = 0;
// Add the objects at this level
if (m_objects != null) count += m_objects.Count;
// Add the objects that are contained in the children
if (m_childTL != null)
{
count += m_childTL.ObjectCount();
count += m_childTR.ObjectCount();
count += m_childBL.ObjectCount();
count += m_childBR.ObjectCount();
}
return count;
}
/// <summary>
/// Subdivide this QuadTree and move it's children into the appropriate Quads where applicable.
/// </summary>
private void Subdivide()
{
// We've reached capacity, subdivide...
Point size = new Point(m_rect.Width / 2, m_rect.Height / 2);
Point mid = new Point(m_rect.X + size.X, m_rect.Y + size.Y);
m_childTL = new QuadTree<T>(new Rectangle(m_rect.Left, m_rect.Top, size.X, size.Y));
m_childTR = new QuadTree<T>(new Rectangle(mid.X, m_rect.Top, size.X, size.Y));
m_childBL = new QuadTree<T>(new Rectangle(m_rect.Left, mid.Y, size.X, size.Y));
m_childBR = new QuadTree<T>(new Rectangle(mid.X, mid.Y, size.X, size.Y));
// If they're completely contained by the quad, bump objects down
for (int i = 0; i < m_objects.Count; i++)
{
QuadTree<T> destTree = GetDestinationTree(m_objects[i]);
if (destTree != this)
{
// Insert to the appropriate tree, remove the object, and back up one in the loop
destTree.Insert(m_objects[i]);
Remove(m_objects[i]);
i--;
}
}
}
/// <summary>
/// Get the child Quad that would contain an object.
/// </summary>
/// <param name="item">The object to get a child for.</param>
/// <returns></returns>
private QuadTree<T> GetDestinationTree(T item)
{
// If a child can't contain an object, it will live in this Quad
QuadTree<T> destTree = this;
if (m_childTL.QuadRect.Contains(item.Rect))
{
destTree = m_childTL;
}
else if (m_childTR.QuadRect.Contains(item.Rect))
{
destTree = m_childTR;
}
else if (m_childBL.QuadRect.Contains(item.Rect))
{
destTree = m_childBL;
}
else if (m_childBR.QuadRect.Contains(item.Rect))
{
destTree = m_childBR;
}
return destTree;
}
#endregion
#region Public Methods
/// <summary>
/// Clears the QuadTree of all objects, including any objects living in its children.
/// </summary>
public void Clear()
{
// Clear out the children, if we have any
if (m_childTL != null)
{
m_childTL.Clear();
m_childTR.Clear();
m_childBL.Clear();
m_childBR.Clear();
}
// Clear any objects at this level
if (m_objects != null)
{
m_objects.Clear();
m_objects = null;
}
// Set the children to null
m_childTL = null;
m_childTR = null;
m_childBL = null;
m_childBR = null;
}
/// <summary>
/// Deletes an item from this QuadTree. If the object is removed causes this Quad to have no objects in its children, it's children will be removed as well.
/// </summary>
/// <param name="item">The item to remove.</param>
public void Delete(T item)
{
// If this level contains the object, remove it
bool objectRemoved = false;
if (m_objects != null && m_objects.Contains(item))
{
Remove(item);
objectRemoved = true;
}
// If we didn't find the object in this tree, try to delete from its children
if (m_childTL != null && !objectRemoved)
{
m_childTL.Delete(item);
m_childTR.Delete(item);
m_childBL.Delete(item);
m_childBR.Delete(item);
}
if (m_childTL != null)
{
// If all the children are empty, delete all the children
if (m_childTL.Count == 0 &&
m_childTR.Count == 0 &&
m_childBL.Count == 0 &&
m_childBR.Count == 0)
{
m_childTL = null;
m_childTR = null;
m_childBL = null;
m_childBR = null;
}
}
}
/// <summary>
/// Insert an item into this QuadTree object.
/// </summary>
/// <param name="item">The item to insert.</param>
public void Insert(T item)
{
// If this quad doesn't intersect the items rectangle, do nothing
if (!m_rect.Intersects(item.Rect))
return;
if (m_objects == null ||
(m_childTL == null && m_objects.Count + 1 <= MAX_OBJECTS_PER_NODE))
{
// If there's room to add the object, just add it
Add(item);
}
else
{
// No quads, create them and bump objects down where appropriate
if (m_childTL == null)
{
Subdivide();
}
// Find out which tree this object should go in and add it there
QuadTree<T> destTree = GetDestinationTree(item);
if (destTree == this)
{
Add(item);
}
else
{
destTree.Insert(item);
}
}
}
/// <summary>
/// Get the objects in this tree that intersect with the specified rectangle.
/// </summary>
/// <param name="rect">The rectangle to find objects in.</param>
/// <param name="results">A reference to a list that will be populated with the results.</param>
public void GetObjects(Rectangle rect, ref List<T> results)
{
// We can't do anything if the results list doesn't exist
if (results != null)
{
if (rect.Contains(m_rect))
{
// If the search area completely contains this quad, just get every object this quad and all it's children have
GetAllObjects(ref results);
}
else if (rect.Intersects(m_rect))
{
// Otherwise, if the quad isn't fully contained, only add objects that intersect with the search rectangle
if (m_objects != null)
{
for (int i = 0; i < m_objects.Count; i++)
{
if (rect.Intersects(m_objects[i].Rect))
{
results.Add(m_objects[i]);
}
}
}
// Get the objects for the search rectangle from the children
if (m_childTL != null)
{
m_childTL.GetObjects(rect, ref results);
m_childTR.GetObjects(rect, ref results);
m_childBL.GetObjects(rect, ref results);
m_childBR.GetObjects(rect, ref results);
}
}
}
}
/// <summary>
/// Get all objects in this Quad, and it's children.
/// </summary>
/// <param name="results">A reference to a list in which to store the objects.</param>
public void GetAllObjects(ref List<T> results)
{
// If this Quad has objects, add them
if (m_objects != null)
results.AddRange(m_objects);
// If we have children, get their objects too
if (m_childTL != null)
{
m_childTL.GetAllObjects(ref results);
m_childTR.GetAllObjects(ref results);
m_childBL.GetAllObjects(ref results);
m_childBR.GetAllObjects(ref results);
}
}
#endregion
}
public static class Ext
{
public static bool Intersects(this Rectangle lhs, Rectangle rhs)
{
var noIntersect =
(lhs.Right < rhs.Left) ||
(rhs.Right < lhs.Left) ||
(lhs.Bottom < rhs.Top) ||
(rhs.Bottom < lhs.Top);
return !noIntersect;
}
}

Get aspect ratio of a monitor

I want to get aspect ratio of a monitor as two digits : width and height. For example 4 and 3, 5 and 4, 16 and 9.
I wrote some code for that task. Maybe it is any easier way to do that ? For example, some library function =\
/// <summary>
/// Aspect ratio.
/// </summary>
public struct AspectRatio
{
int _height;
/// <summary>
/// Height.
/// </summary>
public int Height
{
get
{
return _height;
}
}
int _width;
/// <summary>
/// Width.
/// </summary>
public int Width
{
get
{
return _width;
}
}
/// <summary>
/// Ctor.
/// </summary>
/// <param name="height">Height of aspect ratio.</param>
/// <param name="width">Width of aspect ratio.</param>
public AspectRatio(int height, int width)
{
_height = height;
_width = width;
}
}
public sealed class Aux
{
/// <summary>
/// Get aspect ratio.
/// </summary>
/// <returns>Aspect ratio.</returns>
public static AspectRatio GetAspectRatio()
{
int deskHeight = Screen.PrimaryScreen.Bounds.Height;
int deskWidth = Screen.PrimaryScreen.Bounds.Width;
int gcd = GCD(deskWidth, deskHeight);
return new AspectRatio(deskHeight / gcd, deskWidth / gcd);
}
/// <summary>
/// Greatest Common Denominator (GCD). Euclidean algorithm.
/// </summary>
/// <param name="a">Width.</param>
/// <param name="b">Height.</param>
/// <returns>GCD.</returns>
static int GCD(int a, int b)
{
return b == 0 ? a : GCD(b, a % b);
}
}
Use Screen class to get the height/width.
Divide to get the GCD
Calculate the ratio.
See following code:
private void button1_Click(object sender, EventArgs e)
{
int nGCD = GetGreatestCommonDivisor(Screen.PrimaryScreen.Bounds.Height, Screen.PrimaryScreen.Bounds.Width);
string str = string.Format("{0}:{1}", Screen.PrimaryScreen.Bounds.Height / nGCD, Screen.PrimaryScreen.Bounds.Width / nGCD);
MessageBox.Show(str);
}
static int GetGreatestCommonDivisor(int a, int b)
{
return b == 0 ? a : GetGreatestCommonDivisor(b, a % b);
}
I don't think there's a library function to do it, but that code looks good. Very similar to the answer in this related post of doing the same thing in Javascript: Javascript Aspect Ratio

Categories