Image.FromStream, another System.Drawing workaround

 

Since Buz is gone skiing, I'm alone to code the PixVillage next version... and I can tell you there will be a lot of good things in it.

But that's not what I wanted to talk about. You've surely already loaded a Image from a Stream... This is really simple :

Image image;

using (Stream stream = File.OpenRead(filename))

image = Image.FromStream(stream);

Then if your image is not at the beginning of the stream, you will naturally write :

Image image;

using (Stream stream = File.OpenRead(filename))

{

stream.Seek(offset,SeekOrigin.Begin);

image = Image.FromStream(stream);

}

And it will fail. It will fail because Image.FromStream can only read image from the beginning of the stream. A simple and direct workaround is to copy image portion of the stream in a MemoryStream :

Image image;

using (Stream stream = File.OpenRead(filename))

{

stream.Seek(offset,SeekOrigin.Begin);

using (MemoryStream memoryStream = new MemoryStream())

{

CopyLength(memoryStream, stream, imageStreamLength, 512);

image = Image.FromStream(stream);

}

}

 

public static void CopyLength(Stream to, Stream from, long length, int bufferSize)

{

byte[] buffer = new byte[bufferSize];

int len;

if (length < bufferSize)

bufferSize = (int)length;

while (length >= 0 && (len = from.Read(buffer,0,bufferSize)) != 0)

{

to.Write(buffer,0,len);

length -= len;

if (length < bufferSize)

bufferSize = (int)length;

}

}

This solution works well, but it adds an time and memory overhead, because the whole image stream is copied to memory.

A more elegant workaround is to lure the function that read the stream. You provide to the LoadStream function a Stream object called StreamView that reads data from the original stream but offsetting every seek operation with a given offset. This way, when the StreamView moves to position 0, it is ready to read from the offset position in the stream. Here is the complete code for this class :

public class StreamView : Stream {

 

private Stream baseStream;

private long offset;

private long length;

 

public StreamView(Stream baseStream,long offset,long length) {

this.baseStream = baseStream;

this.offset = offset;

this.length = length;

baseStream.Position = offset;

}

 

public override bool CanRead {

get { return baseStream.CanRead; }

}

 

public override bool CanWrite {

get { return baseStream.CanWrite; }

}

 

public override bool CanSeek {

get { return baseStream.CanSeek; }

}

 

public override IAsyncResult BeginRead(byte[] buffer,

int offset, int count, AsyncCallback callback,

object state) {

return baseStream.BeginRead (buffer, offset,

count, callback, state);

}

 

public override IAsyncResult BeginWrite(byte[] buffer,

int offset, int count, AsyncCallback callback,

object state) {

return baseStream.BeginWrite (buffer, offset,

count, callback, state);

}

 

public override void Close() {

baseStream.Close ();

}

 

public override int EndRead(IAsyncResult asyncResult) {

return baseStream.EndRead (asyncResult);

}

 

public override void EndWrite(IAsyncResult asyncResult) {

baseStream.EndWrite (asyncResult);

}

 

public override void Flush() {

baseStream.Flush();

}

 

public override long Length {

get { return length; }

}

 

public override long Position {

get { return baseStream.Position - offset; }

set { baseStream.Position = value + offset; }

}

 

public override int Read(byte[] buffer, int offset, int count) {

return baseStream.Read(buffer,offset,count);

}

 

public override int ReadByte() {

return baseStream.ReadByte ();

}

 

public override long Seek(long offset, SeekOrigin origin) {

switch(origin) {

case SeekOrigin.Begin:

return baseStream.Seek(offset + this.offset,origin) –

this.offset;

case SeekOrigin.Current:

return baseStream.Seek(offset,origin) - this.offset;

case SeekOrigin.End:

return baseStream.Seek(this.offset + length –

offset,SeekOrigin.Begin) - this.offset;

}

return 0;

}

 

public override void SetLength(long value) {

throw new NotSupportedException();

}

 

public override void Write(byte[] buffer, int offset, int count) {

baseStream.Write(buffer,offset,count);

}

 

public override void WriteByte(byte value) {

baseStream.WriteByte(value);

}

}

Now, reading from stream is really straight forward :

Image image;

using (Stream stream = File.OpenRead(filename))

{

stream.Seek(offset,SeekOrigin.Begin);

using (StreamView view =

new StreamView(stream, offset, imageStreamLength))

{

image = Image.FromStream(view);

}

}

There is no memory and time overhead anymore, you directly read the image from the stream.

Skup

posted on Tuesday, March 08, 2005 6:07 PM

Feedback

# re: Image.FromStream, another System.Drawing workaround 5/21/2005 1:39 AM Zakir

This is a nice code...
There is 1 ploblem if blocks the memory...did u checked that ? Pls tell me How u minimized that

# re: Image.FromStream, another System.Drawing workaround 5/23/2005 12:44 PM Skup

Of course, the 'Cannot access a closed stream' exception probleme discussed in http://www.pixvillage.com/blogs/devblog/archive/2005/03/09/154.aspx can still occure... and the only way to solve the problem is to copy the image bits by making a copy. And you should not forget that :
1) the Exif properties are lost in the copy (you should copy it yourself, refers to Buz's posts about Exif)
2) the link with the original image is lost, so you cannot perform lossel jpeg rotations once you've copied it.

Title
 
Name
 
Url
Comments   
Protected by Clearscreen.SharpHIPEnter the code you see: