1 // Licensed to the .NET Foundation under one or more agreements.
2 // See the LICENSE file in the project root for more information.
3 //
4 // System.Drawing.GdiPlusStreamHelper.cs
5 //   - Originally in System.Drawing.gdipFunctions.cs
6 //
7 // Authors:
8 //    Alexandre Pigolkine (pigolkine@gmx.de)
9 //    Jordi Mas i Hernandez (jordi@ximian.com)
10 //    Sanjay Gupta (gsanjay@novell.com)
11 //    Ravindra (rkumar@novell.com)
12 //    Peter Dennis Bartok (pbartok@novell.com)
13 //    Sebastien Pouliot <sebastien@ximian.com>
14 //
15 // Copyright (C) 2004 - 2007 Novell, Inc (http://www.novell.com)
16 //
17 // Permission is hereby granted, free of charge, to any person obtaining
18 // a copy of this software and associated documentation files (the
19 // "Software"), to deal in the Software without restriction, including
20 // without limitation the rights to use, copy, modify, merge, publish,
21 // distribute, sublicense, and/or sell copies of the Software, and to
22 // permit persons to whom the Software is furnished to do so, subject to
23 // the following conditions:
24 //
25 // The above copyright notice and this permission notice shall be
26 // included in all copies or substantial portions of the Software.
27 //
28 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 //
36 
37 using System.IO;
38 using System.Runtime.InteropServices;
39 
40 namespace System.Drawing
41 {
42     internal sealed partial class GdiPlusStreamHelper
43     {
44         public Stream stream;
45 
46         private StreamGetHeaderDelegate sghd = null;
47         private StreamGetBytesDelegate sgbd = null;
48         private StreamSeekDelegate skd = null;
49         private StreamPutBytesDelegate spbd = null;
50         private StreamCloseDelegate scd = null;
51         private StreamSizeDelegate ssd = null;
52         private byte[] start_buf;
53         private int start_buf_pos;
54         private int start_buf_len;
55         private byte[] managedBuf;
56         private const int default_bufsize = 4096;
57 
GdiPlusStreamHelper(Stream s, bool seekToOrigin)58         public GdiPlusStreamHelper(Stream s, bool seekToOrigin)
59         {
60             managedBuf = new byte[default_bufsize];
61 
62             stream = s;
63             if (stream != null && stream.CanSeek && seekToOrigin)
64             {
65                 stream.Seek(0, SeekOrigin.Begin);
66             }
67         }
68 
StreamGetHeaderImpl(IntPtr buf, int bufsz)69         public int StreamGetHeaderImpl(IntPtr buf, int bufsz)
70         {
71             int bytesRead;
72 
73             start_buf = new byte[bufsz];
74 
75             try
76             {
77                 bytesRead = stream.Read(start_buf, 0, bufsz);
78             }
79             catch (IOException)
80             {
81                 return -1;
82             }
83 
84             if (bytesRead > 0 && buf != IntPtr.Zero)
85             {
86                 Marshal.Copy(start_buf, 0, (IntPtr)(buf.ToInt64()), bytesRead);
87             }
88 
89             start_buf_pos = 0;
90             start_buf_len = bytesRead;
91 
92             return bytesRead;
93         }
94 
95         public StreamGetHeaderDelegate GetHeaderDelegate
96         {
97             get
98             {
99                 if (stream != null && stream.CanRead)
100                 {
101                     if (sghd == null)
102                     {
103                         sghd = new StreamGetHeaderDelegate(StreamGetHeaderImpl);
104                     }
105                     return sghd;
106                 }
107                 return null;
108             }
109         }
110 
StreamGetBytesImpl(IntPtr buf, int bufsz, bool peek)111         public int StreamGetBytesImpl(IntPtr buf, int bufsz, bool peek)
112         {
113             if (buf == IntPtr.Zero && peek)
114             {
115                 return -1;
116             }
117 
118             if (bufsz > managedBuf.Length)
119                 managedBuf = new byte[bufsz];
120             int bytesRead = 0;
121             long streamPosition = 0;
122 
123             if (bufsz > 0)
124             {
125                 if (stream.CanSeek)
126                 {
127                     streamPosition = stream.Position;
128                 }
129                 if (start_buf_len > 0)
130                 {
131                     if (start_buf_len > bufsz)
132                     {
133                         Array.Copy(start_buf, start_buf_pos, managedBuf, 0, bufsz);
134                         start_buf_pos += bufsz;
135                         start_buf_len -= bufsz;
136                         bytesRead = bufsz;
137                         bufsz = 0;
138                     }
139                     else
140                     {
141                         // this is easy
142                         Array.Copy(start_buf, start_buf_pos, managedBuf, 0, start_buf_len);
143                         bufsz -= start_buf_len;
144                         bytesRead = start_buf_len;
145                         start_buf_len = 0;
146                     }
147                 }
148 
149                 if (bufsz > 0)
150                 {
151                     try
152                     {
153                         bytesRead += stream.Read(managedBuf, bytesRead, bufsz);
154                     }
155                     catch (IOException)
156                     {
157                         return -1;
158                     }
159                 }
160 
161                 if (bytesRead > 0 && buf != IntPtr.Zero)
162                 {
163                     Marshal.Copy(managedBuf, 0, (IntPtr)(buf.ToInt64()), bytesRead);
164                 }
165 
166                 if (!stream.CanSeek && (bufsz == 10) && peek)
167                 {
168                     // Special 'hack' to support peeking of the type for gdi+ on non-seekable streams
169                 }
170 
171                 if (peek)
172                 {
173                     if (stream.CanSeek)
174                     {
175                         // If we are peeking bytes, then go back to original position before peeking
176                         stream.Seek(streamPosition, SeekOrigin.Begin);
177                     }
178                     else
179                     {
180                         throw new NotSupportedException();
181                     }
182                 }
183             }
184 
185             return bytesRead;
186         }
187 
188         public StreamGetBytesDelegate GetBytesDelegate
189         {
190             get
191             {
192                 if (stream != null && stream.CanRead)
193                 {
194                     if (sgbd == null)
195                     {
196                         sgbd = new StreamGetBytesDelegate(StreamGetBytesImpl);
197                     }
198                     return sgbd;
199                 }
200                 return null;
201             }
202         }
203 
StreamSeekImpl(int offset, int whence)204         public long StreamSeekImpl(int offset, int whence)
205         {
206             // Make sure we have a valid 'whence'.
207             if ((whence < 0) || (whence > 2))
208                 return -1;
209 
210             // Invalidate the start_buf if we're actually going to call a Seek method.
211             start_buf_pos += start_buf_len;
212             start_buf_len = 0;
213 
214             SeekOrigin origin;
215 
216             // Translate 'whence' into a SeekOrigin enum member.
217             switch (whence)
218             {
219                 case 0:
220                     origin = SeekOrigin.Begin;
221                     break;
222                 case 1:
223                     origin = SeekOrigin.Current;
224                     break;
225                 case 2:
226                     origin = SeekOrigin.End;
227                     break;
228 
229                 // The following line is redundant but necessary to avoid a
230                 // "Use of unassigned local variable" error without actually
231                 // initializing 'origin' to a dummy value.
232                 default:
233                     return -1;
234             }
235 
236             // Do the actual seek operation and return its result.
237             return stream.Seek((long)offset, origin);
238         }
239 
240         public StreamSeekDelegate SeekDelegate
241         {
242             get
243             {
244                 if (stream != null && stream.CanSeek)
245                 {
246                     if (skd == null)
247                     {
248                         skd = new StreamSeekDelegate(StreamSeekImpl);
249                     }
250                     return skd;
251                 }
252                 return null;
253             }
254         }
255 
StreamPutBytesImpl(IntPtr buf, int bufsz)256         public int StreamPutBytesImpl(IntPtr buf, int bufsz)
257         {
258             if (bufsz > managedBuf.Length)
259                 managedBuf = new byte[bufsz];
260             Marshal.Copy(buf, managedBuf, 0, bufsz);
261             stream.Write(managedBuf, 0, bufsz);
262             return bufsz;
263         }
264 
265         public StreamPutBytesDelegate PutBytesDelegate
266         {
267             get
268             {
269                 if (stream != null && stream.CanWrite)
270                 {
271                     if (spbd == null)
272                     {
273                         spbd = new StreamPutBytesDelegate(StreamPutBytesImpl);
274                     }
275                     return spbd;
276                 }
277                 return null;
278             }
279         }
280 
StreamCloseImpl()281         public void StreamCloseImpl()
282         {
283             stream.Dispose();
284         }
285 
286         public StreamCloseDelegate CloseDelegate
287         {
288             get
289             {
290                 if (stream != null)
291                 {
292                     if (scd == null)
293                     {
294                         scd = new StreamCloseDelegate(StreamCloseImpl);
295                     }
296                     return scd;
297                 }
298                 return null;
299             }
300         }
301 
StreamSizeImpl()302         public long StreamSizeImpl()
303         {
304             try
305             {
306                 return stream.Length;
307             }
308             catch
309             {
310                 return -1;
311             }
312         }
313 
314         public StreamSizeDelegate SizeDelegate
315         {
316             get
317             {
318                 if (stream != null)
319                 {
320                     if (ssd == null)
321                     {
322                         ssd = new StreamSizeDelegate(StreamSizeImpl);
323                     }
324                     return ssd;
325                 }
326                 return null;
327             }
328         }
329     }
330 }
331