vbAccelerator - Contents of code file: EXIFListBox.csusing System;
using System.Collections;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.IO;
using System.Windows.Forms;
using vbAccelerator.Components.EXIF;
namespace vbAccelerator.Components.ListBox
{
/// <summary>
/// Provides progress information during
/// asynchronous loading of items
/// </summary>
public class ProgressEventArgs
{
private int item = 0;
private int count = 0;
private bool complete = false;
private string file = "";
private bool cancel = false;
/// <summary>
/// Gets the item being processed
/// </summary>
public int Item
{
get
{
return this.item;
}
}
/// <summary>
/// Gets the total count of items to be processed
/// </summary>
public int Count
{
get
{
return this.count;
}
}
/// <summary>
/// Gets the file being processed
/// </summary>
public string File
{
get
{
return this.file;
}
}
/// <summary>
/// Gets/sets whether the operation should be cancelled
/// </summary>
public bool Cancel
{
get
{
return this.cancel;
}
set
{
this.cancel = value;
}
}
/// <summary>
/// Gets/sets whether this operation has been completed
/// </summary>
public bool Complete
{
get
{
return this.complete;
}
}
public ProgressEventArgs(
int item,
int count,
string file,
bool complete
)
{
this.item = item;
this.count = count;
this.file = file;
this.complete = complete;
}
}
#region Event Delegates
public delegate void ProgressEventHandler(object sender, ProgressEventArgs
e);
#endregion
/// <summary>
/// Summary description for EXIFListBox.
/// </summary>
public class EXIFListBox : System.Windows.Forms.ListBox
{
#region Member Variables
private Size thumbNailSize = new Size(100,100);
private Stack fileStack = new Stack();
private bool cancel = false;
private int loadRun = 0;
private int progressItem = 0;
private int progressCount = 0;
#endregion
#region Events
public event ProgressEventHandler Progress;
#endregion
#region Properties
/// <summary>
/// Gets/sets the size of the thumbnail to create
/// </summary>
public Size ThumbNailSize
{
get
{
return this.thumbNailSize;
}
set
{
this.thumbNailSize = value;
setItemHeight();
}
}
/// <summary>
/// Determines whether an asynchronous item loading operation
/// is in progress
/// </summary>
public bool IsLoading
{
get
{
return (this.fileStack.Count > 0);
}
}
#endregion
#region Methods
/// <summary>
/// Cancels an asynchronous item load operation (if one is in
/// progress)
/// </summary>
public void Cancel()
{
this.loadRun++;
if (fileStack.Count > 0)
{
cancel = true;
}
}
/// <summary>
/// Adds all of the image files in the specified folder
/// asynchronously
/// </summary>
/// <param name="folder">Folder to add files from</param>
public void AddFolder(string folder)
{
cancel = false;
loadRun++;
progressItem = 0;
fileStack.Clear();
string[] files = Directory.GetFiles(folder, "*.*");
foreach (string file in files)
{
fileStack.Push(file);
}
progressCount = fileStack.Count;
processFile();
}
/// <summary>
/// Adds the specified image file asynchronously
/// </summary>
/// <param name="file">Image file to add</param>
public void AddFile(string file)
{
cancel = false;
loadRun++;
progressItem = 0;
fileStack.Clear();
fileStack.Push(file);
progressCount = fileStack.Count;
processFile();
}
#endregion
#region Asynchronous Loading
private void processFile()
{
if (cancel)
{
fileStack.Clear();
cancel = false;
}
else
{
if (fileStack.Count == 0)
{
// complete
cancel = false;
if (Progress != null)
{
ProgressEventArgs p = new ProgressEventArgs(
progressItem,
progressCount,
"",
true);
Progress(this, p);
}
}
else
{
string file = (string)fileStack.Pop();
progressItem++;
if (Progress != null)
{
ProgressEventArgs p = new ProgressEventArgs(
progressItem,
progressCount,
file,
false);
Progress(this, p);
if (p.Cancel)
{
fileStack.Clear();
cancel = false;
}
}
// This method runs on another thread:
ProcessFileHandler pfh = new
ProcessFileHandler(processFileBackground);
pfh.BeginInvoke(loadRun, file, this.thumbNailSize, null, null);
}
}
}
private delegate void ProcessFileHandler(int loadRun, string file, Size
thumbNailSize);
private void processFileBackground(int loadRun, string file, Size
thumbNailSize)
{
try
{
// Do the long operation of loading image, getting thumbnail,
// reading EXIF properties:
EXIFListBoxItem item = new EXIFListBoxItem(file, thumbNailSize);
// Now call back to the control. Must use BeginInvoke to do this
// in order to call back on the correct thread.
object[] pList = { loadRun, item };
this.BeginInvoke(new
ProcessFileCompleteHandler(processFileComplete), pList);
}
catch
{
// The image couldn't be loaded, so proceed to the next one
object[] pList = { loadRun, null };
this.BeginInvoke(new
ProcessFileCompleteHandler(processFileComplete), pList);
}
}
private delegate void ProcessFileCompleteHandler(int loadRun,
EXIFListBoxItem item);
private void processFileComplete(int loadRun, EXIFListBoxItem ef)
{
// add this item to the list box if valid:
if ((ef != null) && (loadRun == this.loadRun))
{
this.Items.Add(ef);
}
// do the next file
processFile();
}
#endregion
#region Drawing Code
protected override void OnDrawItem (
System.Windows.Forms.DrawItemEventArgs e )
{
if ((e.Index > -1) && (e.Index < this.Items.Count))
{
// Owner draw ListBoxes tend to flicker unless you
// draw to an off-screen bitmap
Bitmap bmOffscreen = new Bitmap(e.Bounds.Width, e.Bounds.Height);
Graphics gfx = Graphics.FromImage(bmOffscreen);
Rectangle bounds = new Rectangle(0, 0, e.Bounds.Width,
e.Bounds.Height);
// Fill the background of the item
Brush brBackground = SystemBrushes.Window;
gfx.FillRectangle(brBackground, bounds);
bool selected = ((e.State & DrawItemState.Selected) ==
DrawItemState.Selected);
if (selected)
{
Color brushColor = Color.FromArgb(128,
Color.FromKnownColor(KnownColor.Highlight));
brBackground = new SolidBrush(brushColor);
gfx.FillRectangle(brBackground, bounds);
brBackground.Dispose();
}
// Get the EXIF item we're working on
EXIFListBoxItem item = (EXIFListBoxItem )this.Items[e.Index];
SizeF size = e.Graphics.MeasureString("Xg", this.Font, 128);
// Draw the file name:
RectangleF textRect = new RectangleF(
bounds.X + 2,
bounds.Y + 2,
bounds.Width - 4,
size.Height);
Color titleColor = Color.FromKnownColor(KnownColor.Highlight);
if (!selected)
{
titleColor = Color.FromArgb(128,
Color.FromKnownColor(KnownColor.Highlight));
}
Brush titleBrush = new SolidBrush(titleColor);
gfx.FillRectangle(titleBrush, textRect);
if (selected)
{
gfx.DrawRectangle(SystemPens.ControlDarkDark, (int)textRect.X,
(int)textRect.Y, (int)textRect.Width, (int)textRect.Height);
}
else
{
gfx.DrawRectangle(SystemPens.Highlight, (int)textRect.X,
(int)textRect.Y, (int)textRect.Width, (int)textRect.Height);
}
titleBrush.Dispose();
Brush textBrush;
if (selected)
{
textBrush = SystemBrushes.HighlightText;
}
else
{
textBrush = SystemBrushes.WindowText;
}
StringFormat textFormat = new StringFormat();
textFormat.Alignment = StringAlignment.Near;
textFormat.Trimming = StringTrimming.EllipsisPath;
textFormat.FormatFlags = StringFormatFlags.LineLimit;
gfx.DrawString(
Path.GetFileName(item.FileName),
this.Font,
textBrush,
textRect,
textFormat);
textFormat.Dispose();
// Draw the thumbnail:
gfx.DrawImageUnscaled(
item.ThumbNail,
bounds.X + 2,
bounds.Y + 2 + (int)size.Height + 2);
// Draw the image size & colour depth:
textRect.Y = bounds.Y + 2 + (int)size.Height + 2 +
item.ThumbNail.Height + 2;
textRect.Height = (int)size.Height * 2;
gfx.DrawString(
String.Format("Size: {0} x {1}\r\nBit Depth {2}",
item.Size.Width, item.Size.Height,
bitDepthForDisplay(item.PixelFormat)),
this.Font,
textBrush,
textRect);
// Draw the EXIF Properties. Here we try for
// HorzRes, VertRes, DatePictureTaken, FrameCount,
// EquipmentMake, CameraModel,
// CreationSoftware, ColourRepresentation, FlashMode, FocalLength
// F-Number and ExposureTime:
textRect.X = bounds.X + item.ThumbNail.Width + 4;
textRect.Y = bounds.Y + 2 + (int)size.Height + 2;
textRect.Width = bounds.Width - 4 - textRect.X;
textRect.Height = bounds.Height - 4;
addProperty(gfx, "Horizontal Resolution",
item.HorizontalResolution, ref textRect, (int)size.Height);
addProperty(gfx, "Vertical Resolution", item.VerticalResolution,
ref textRect, (int)size.Height);
addProperty(gfx, "Date Picture Taken", item.DatePictureTaken, ref
textRect, (int)size.Height);
addProperty(gfx, "Equipment Make", item.Manufacturer, ref textRect,
(int)size.Height);
addProperty(gfx, "Model", item.Model, ref textRect,
(int)size.Height);
addProperty(gfx, "Creation Software", item.SoftwareVersion, ref
textRect, (int)size.Height);
addProperty(gfx, "Flash Mode", item.FlashMode, ref textRect,
(int)size.Height);
addProperty(gfx, "Focal Length", item.FocalLength, ref textRect,
(int)size.Height);
addProperty(gfx, "F-Number", item.Aperture, ref textRect,
(int)size.Height);
addProperty(gfx, "Exposure Time", item.ExposureTime, ref textRect,
(int)size.Height);
// Swap off screen graphics to display:
e.Graphics.DrawImageUnscaled(bmOffscreen, e.Bounds.X, e.Bounds.Y);
bmOffscreen.Dispose();
gfx.Dispose();
}
else
{
base.OnDrawItem(e);
}
}
private void addProperty(
Graphics gfx,
string propName,
string propValue,
ref RectangleF textRect,
int height
)
{
if (propValue.Length > 0)
{
gfx.DrawString(
String.Format("{0} : {1}", propName, propValue),
this.Font,
SystemBrushes.WindowText,
textRect);
textRect.Y += height;
}
}
private string bitDepthForDisplay(PixelFormat pixelFormat)
{
string ans = "";
switch (pixelFormat)
{
case PixelFormat.Format16bppArgb1555:
case PixelFormat.Format16bppGrayScale:
case PixelFormat.Format16bppRgb555:
case PixelFormat.Format16bppRgb565:
ans = "16 bit";
break;
case PixelFormat.Format1bppIndexed:
ans = "Black and White";
break;
case PixelFormat.Format24bppRgb:
ans = "24 bit";
break;
case PixelFormat.Format32bppArgb:
case PixelFormat.Format32bppPArgb:
case PixelFormat.Format32bppRgb:
ans = "32 bit";
break;
case PixelFormat.Format48bppRgb:
ans = "48 bit";
break;
case PixelFormat.Format4bppIndexed:
ans = "16 colour";
break;
case PixelFormat.Format64bppArgb:
case PixelFormat.Format64bppPArgb:
ans = "64 bit";
break;
case PixelFormat.Format8bppIndexed:
ans = "256 bit";
break;
}
return ans;
}
#endregion
#region General Implementation Details
protected override void OnFontChanged ( System.EventArgs e )
{
setItemHeight();
base.OnFontChanged(e);
}
private void setItemHeight()
{
Bitmap bm = new Bitmap(48,48);
Graphics gfx = Graphics.FromImage(bm);
SizeF size = gfx.MeasureString("Xg", this.Font, 128);
this.ItemHeight = (int)Math.Max(
9 * size.Height,
this.thumbNailSize.Height + size.Height * 3 + 6
);
gfx.Dispose();
bm.Dispose();
}
#endregion
#region Constructor, Dispose
public EXIFListBox() : base()
{
this.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
}
#endregion
}
public class EXIFListBoxItem : ReadOnlyCollectionBase
{
private string fileName = "";
private Bitmap thumbNail = null;
private System.Drawing.Imaging.PixelFormat pixelFormat =
PixelFormat.DontCare;
private System.Drawing.Size size;
public Bitmap ThumbNail
{
get
{
return this.thumbNail;
}
}
public string FileName
{
get
{
return this.fileName;
}
}
public string FlashMode
{
get
{
string ret = "";
EXIFPropertyItem flash = ItemForEXIFCode(KnownEXIFIDCodes.Flash);
if (flash != null)
{
try
{
switch (flash.Value[0])
{
case 0:
ret = "No Flash";
break;
case 1:
ret = "Flash";
break;
case 5:
ret = "Flash Fired, No Strobe Return";
break;
case 7:
ret = "Flash With Strobe Return";
break;
}
}
catch {}
}
return ret;
}
}
public string FocalLength
{
get
{
string ret = "";
EXIFPropertyItem focalLength =
ItemForEXIFCode(KnownEXIFIDCodes.FocalLength);
if (focalLength != null)
{
try
{
EXIFRational focalLengthFraction = focalLength.ParsedRational;
ret = string.Format("{0:N0} mm",
focalLengthFraction.Numerator * 1.0 /
focalLengthFraction.Denominator);
}
catch {}
}
return ret;
}
}
public string ExposureTime
{
get
{
string ret = "";
EXIFPropertyItem exposure =
ItemForEXIFCode(KnownEXIFIDCodes.ExposureTime);
if (exposure != null)
{
try
{
EXIFRational exposureTime = exposure.ParsedRational;
ret = string.Format("{0}/{1} s",
exposureTime.Numerator, exposureTime.Denominator);
}
catch {}
}
return ret;
}
}
public string Aperture
{
get
{
string ret = "";
EXIFPropertyItem aperture =
ItemForEXIFCode(KnownEXIFIDCodes.ApertureValue);
if (aperture != null)
{
try
{
EXIFRational apertureFraction = aperture.ParsedRational;
double apertureValue = Math.Pow(
Math.Sqrt(2.0),
(apertureFraction.Numerator * 1.0 /
apertureFraction.Denominator)
);
ret = string.Format("{0:F1}", apertureValue);
}
catch
{
}
}
return ret;
}
}
public string Model
{
get
{
return parseEXIFString(KnownEXIFIDCodes.Model);
}
}
public string Manufacturer
{
get
{
return parseEXIFString(KnownEXIFIDCodes.Manufacturer);
}
}
public string SoftwareVersion
{
get
{
return parseEXIFString(KnownEXIFIDCodes.Software);
}
}
public string HorizontalResolution
{
get
{
string ret = parseResolution(KnownEXIFIDCodes.XResolution);
return ret;
}
}
public string VerticalResolution
{
get
{
string ret = parseResolution(KnownEXIFIDCodes.YResolution);
return ret;
}
}
public string DatePictureTaken
{
get
{
return parseEXIFDateTime(KnownEXIFIDCodes.DateTimeOriginal);
}
}
private string parseEXIFDateTime(KnownEXIFIDCodes dateItem)
{
string ret = "";
EXIFPropertyItem item = ItemForEXIFCode(dateItem);
if (item != null)
{
try
{
DateTime dt = item.ParsedDate;
ret = String.Format("{0:d} {1:T}", dt, dt);
}
catch {}
}
return ret;
}
private string parseEXIFString(KnownEXIFIDCodes stringItem)
{
string ret = "";
EXIFPropertyItem item = ItemForEXIFCode(stringItem);
if (item != null)
{
try
{
ret = item.ParsedString;
}
catch {}
}
return ret;
}
private string parseResolution(KnownEXIFIDCodes resolutionAxis)
{
string ret = "";
EXIFPropertyItem resUnit =
ItemForEXIFCode(KnownEXIFIDCodes.ResolutionUnit);
EXIFPropertyItem res = ItemForEXIFCode(resolutionAxis);
if ((resUnit != null) && (res != null))
{
try
{
EXIFRational resFraction = res.ParsedRational;
double resValue = resFraction.Numerator * 1.0 /
resFraction.Denominator;
ret = String.Format("{0:N0}", resValue);
switch (res.Value[0])
{
case 2:
ret += " dpi";
break;
case 3:
ret += " dpcm";
break;
default:
ret += " dpi";
break;
}
}
catch {}
}
return ret;
}
public EXIFPropertyItem ItemForEXIFCode(KnownEXIFIDCodes code)
{
EXIFPropertyItem ret = null;
foreach (EXIFPropertyItem p in this.InnerList)
{
if (p.EXIFCode == code)
{
ret = p;
break;
}
}
return ret;
}
public EXIFPropertyItem this[int index]
{
get
{
return (EXIFPropertyItem)this.InnerList[index];
}
}
public System.Drawing.Imaging.PixelFormat PixelFormat
{
get
{
return this.pixelFormat;
}
}
public System.Drawing.Size Size
{
get
{
return this.size;
}
}
public EXIFListBoxItem(string file, Size thumbNailSize)
{
Image img = Image.FromFile(file);
this.pixelFormat = img.PixelFormat;
this.size = img.Size;
this.fileName = file;
Console.WriteLine("{0}", file);
int[] propIds = img.PropertyIdList;
foreach( int id in propIds )
{
try
{
PropertyItem prop = img.GetPropertyItem(id);
EXIFPropertyItem exifProp = new EXIFPropertyItem(prop);
this.InnerList.Add(exifProp);
}
catch
{
// can't read this property
}
}
// Create the thumbnail
this.thumbNail = new Bitmap(thumbNailSize.Width, thumbNailSize.Height);
Graphics gfx = Graphics.FromImage(this.thumbNail);
gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
// Determine scaling for new rectangle:
double xScale = (1.0 * img.Width) / thumbNail.Width;
double yScale = (1.0 * img.Height) / thumbNail.Height;
Rectangle destRect;
if (xScale > yScale)
{
if (img.Height / xScale < thumbNail.Height)
{
destRect = new Rectangle(0, 0, (int)(img.Width / xScale),
(int)(img.Height / xScale));
}
else
{
destRect = new Rectangle(0, 0, (int)(img.Width / yScale),
(int)(img.Height / yScale));
}
}
else
{
if (img.Width / yScale < thumbNail.Width)
{
destRect = new Rectangle(0, 0, (int)(img.Width / yScale),
(int)(img.Height / yScale));
}
else
{
destRect = new Rectangle(0, 0, (int)(img.Width / xScale),
(int)(img.Height / xScale));
}
}
gfx.DrawImage(img, destRect, 0, 0, img.Width, img.Height,
GraphicsUnit.Pixel);
gfx.Dispose();
img.Dispose();
}
}
}
|
|