|
-
Jun 1st, 2005, 06:40 PM
#1
Extract thumbnail image from a file without reading the whole file
You could use Image.GetThumbnailImage() to get a thumbnail of an already loaded image, but if you have a lot of images and you do that, it'd be fairly slow because you would have to load all the images.
Alternatively most images taken with cameras have a thumbnail of the image stored in the image metadata, and that can be accessed without loading the image as a whole.
If you have .NET 1.1 installed then you can do this:
.NET 1.1 version only
Code:
// Please do not remove :)
// Written by Kourosh Derakshan
//
using System.IO;
using System.Drawing.Imaging;
private const int THUMBNAIL_DATA = 0x501B;
/// <summary>
/// Gets the thumbnail from the image metadata. Returns null of no thumbnail
/// is stored in the image metadata
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
Image GetThumbnail (string path)
{
FileStream fs = File.OpenRead (path);
// Last parameter tells GDI+ not the load the actual image data
Image img = Image.FromStream (fs, false, false);
// GDI+ throws an error if we try to read a property when the image
// doesn't have that property. Check to make sure the thumbnail property
// item exists.
bool propertyFound = false;
for (int i=0; i<img.PropertyIdList.Length; i++)
if (img.PropertyIdList[i] == THUMBNAIL_DATA)
{
propertyFound = true;
break;
}
if (!propertyFound)
return null;
PropertyItem p = img.GetPropertyItem (THUMBNAIL_DATA);
fs.Close();
img.Dispose();
// The image data is in the form of a byte array. Write all
// the bytes to a stream and create a new image from that stream
byte[] imageBytes = p.Value;
MemoryStream stream = new MemoryStream (imageBytes.Length);
stream.Write (imageBytes, 0, imageBytes.Length);
return Image.FromStream(stream);
}
If you want support for .NET 1.0 also, you can try this code below (or download the source file, right click and save target ExifThumbReader.cs):
Code:
// Please do not remove :)
// Written by Kourosh Derakshan
//
using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
/// <summary>
/// Allows reading of embedded thumbnail image from the EXIF information in an image.
/// </summary>
public abstract class ExifThumbReader
{
// GDI plus functions
[DllImport("gdiplus.dll", CharSet=CharSet.Unicode, ExactSpelling=true)]
internal static extern int GdipGetPropertyItem(IntPtr image, int propid, int size, IntPtr buffer);
[DllImport("gdiplus.dll", CharSet=CharSet.Unicode, ExactSpelling=true)]
internal static extern int GdipGetPropertyItemSize(IntPtr image, int propid, out int size);
[DllImport("gdiplus.dll", CharSet=CharSet.Unicode, ExactSpelling=true)]
internal static extern int GdipLoadImageFromFile(string filename, out IntPtr image);
[DllImport("gdiplus.dll", EntryPoint="GdipDisposeImage", CharSet=CharSet.Unicode, ExactSpelling=true)]
private static extern int GdipDisposeImage(IntPtr image);
// EXIT tag value for thumbnail data. Value specified by EXIF standard
private const int THUMBNAIL_DATA = 0x501B;
/// <summary>
/// Reads the thumbnail in the given image. If no thumbnail is found, returns null
/// </summary>
public static Image ReadThumb (string imagePath)
{
const int GDI_ERR_PROP_NOT_FOUND = 19; // Property not found error
const int GDI_ERR_OUT_OF_MEMORY = 3;
IntPtr hImage = IntPtr.Zero;
IntPtr buffer = IntPtr.Zero; // Holds the thumbnail data
int ret;
ret = GdipLoadImageFromFile (imagePath, out hImage);
try
{
if (ret!=0)
throw createException (ret);
int propSize;
ret = GdipGetPropertyItemSize (hImage, THUMBNAIL_DATA, out propSize);
// Image has no thumbnail data in it. Return null
if (ret==GDI_ERR_PROP_NOT_FOUND)
return null;
if (ret!=0)
throw createException (ret);
// Allocate a buffer in memory
buffer = Marshal.AllocHGlobal(propSize);
if (buffer == IntPtr.Zero)
throw createException (GDI_ERR_OUT_OF_MEMORY);
ret = GdipGetPropertyItem (hImage, THUMBNAIL_DATA, propSize, buffer);
if (ret!=0)
throw createException (ret);
// buffer has the thumbnail data. Now we have to convert it to
// an Image
return convertFromMemory (buffer);
}
finally
{
// Free the buffer
if (buffer != IntPtr.Zero)
Marshal.FreeHGlobal (buffer);
GdipDisposeImage (hImage);
}
}
/// <summary>
/// Generates an exception depending on the GDI+ error codes (I removed some error
/// codes)
/// </summary>
private static Exception createException (int gdipErrorCode)
{
switch (gdipErrorCode)
{
case 1:
return new ExternalException("Gdiplus Generic Error", -2147467259);
case 2:
return new ArgumentException("Gdiplus Invalid Parameter");
case 3:
return new OutOfMemoryException("Gdiplus Out Of Memory");
case 4:
return new InvalidOperationException("Gdiplus Object Busy");
case 5:
return new OutOfMemoryException("Gdiplus Insufficient Buffer");
case 7:
return new ExternalException("Gdiplus Generic Error", -2147467259);
case 8:
return new InvalidOperationException("Gdiplus Wrong State");
case 9:
return new ExternalException("Gdiplus Aborted", -2147467260);
case 10:
return new FileNotFoundException("Gdiplus File Not Found");
case 11:
return new OverflowException("Gdiplus Over flow");
case 12:
return new ExternalException("Gdiplus Access Denied", -2147024891);
case 13:
return new ArgumentException("Gdiplus Unknown Image Format");
case 18:
return new ExternalException("Gdiplus Not Initialized", -2147467259);
case 20:
return new ArgumentException("Gdiplus Property Not Supported Error");
}
return new ExternalException("Gdiplus Unknown Error", -2147418113);
}
/// <summary>
/// Converts the IntPtr buffer to a property item and then converts its
/// value to a Drawing.Image item
/// </summary>
private static Image convertFromMemory (IntPtr thumbData)
{
propertyItemInternal prop =
(propertyItemInternal)Marshal.PtrToStructure
(thumbData, typeof (propertyItemInternal));
// The image data is in the form of a byte array. Write all
// the bytes to a stream and create a new image from that stream
byte[] imageBytes = prop.Value;
MemoryStream stream = new MemoryStream (imageBytes.Length);
stream.Write (imageBytes, 0, imageBytes.Length);
return Image.FromStream(stream);
}
/// <summary>
/// Used in Marshal.PtrToStructure().
/// We need this dummy class because Imaging.PropertyItem is not a "blittable"
/// class and Marshal.PtrToStructure only accepted blittable classes.
/// (It's not blitable because it uses a byte[] array and that's not a blittable
/// type. See MSDN for a definition of Blittable.)
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private class propertyItemInternal
{
public int id=0;
public int len=0;
public short type=0;
public IntPtr value=IntPtr.Zero;
public byte[] Value
{
get
{
byte[] bytes = new byte[(uint)len];
Marshal.Copy(value, bytes, 0, len);
return bytes;
}
}
}
}
Last edited by MrPolite; Jun 5th, 2005 at 04:36 PM.
rate my posts if they help ya!
Extract thumbnail without reading the whole image file: (C# - VB)
Apply texture to bitmaps: (C# - VB)
Extended console library: (VB)
Save JPEG with a certain quality (image compression): (C# - VB )
VB.NET to C# conversion tips!!
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|