2 Vampyre Imaging Library
4 http://imaginglib.sourceforge.net
6 The contents of this file are used with permission, subject to the Mozilla
7 Public License Version 1.1 (the "License"); you may not use this file except
8 in compliance with the License. You may obtain a copy of the License at
9 http://www.mozilla.org/MPL/MPL-1.1.html
11 Software distributed under the License is distributed on an "AS IS" basis,
12 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
13 the specific language governing rights and limitations under the License.
15 Alternatively, the contents of this file may be used under the terms of the
16 GNU Lesser General Public License (the "LGPL License"), in which case the
17 provisions of the LGPL License are applicable instead of those above.
18 If you wish to allow use of your version of this file only under the terms
19 of the LGPL License and not to allow others to use your version of this file
20 under the MPL, indicate your decision by deleting the provisions above and
21 replace them with the notice and other provisions required by the LGPL
22 License. If you do not delete the provisions above, a recipient may use
23 your version of this file under either the MPL or the LGPL License.
25 For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
28 { This unit contains default IO functions for reading from/writting to
29 files, streams and memory.}
32 {$I ImagingOptions.inc}
37 SysUtils
, Classes
, ImagingTypes
, Imaging
, ImagingUtility
;
41 Data
: ImagingUtility
.PByteArray
;
45 PMemoryIORec
= ^TMemoryIORec
;
48 OriginalFileIO
: TIOFunctions
;
50 StreamIO
: TIOFunctions
;
51 MemoryIO
: TIOFunctions
;
53 { Helper function that returns size of input (from current position to the end)
54 represented by Handle (and opened and operated on by members of IOFunctions).}
55 function GetInputSize(IOFunctions
: TIOFunctions
; Handle
: TImagingHandle
): LongInt;
56 { Helper function that initializes TMemoryIORec with given params.}
57 function PrepareMemIO(Data
: Pointer; Size
: LongInt): TMemoryIORec
;
58 { Reads one text line from input (CR+LF, CR, or LF as line delimiter).}
59 function ReadLine(IOFunctions
: TIOFunctions
; Handle
: TImagingHandle
;
60 out Line
: AnsiString; FailOnControlChars
: Boolean = False): Boolean;
61 { Writes one text line to input with optional line delimiter.}
62 procedure WriteLine(IOFunctions
: TIOFunctions
; Handle
: TImagingHandle
;
63 const Line
: AnsiString; const LineEnding
: AnsiString = sLineBreak
);
68 DefaultBufferSize
= 16 * 1024;
71 { Based on TaaBufferedStream
72 Copyright (c) Julian M Bucknall 1997, 1999 }
73 TBufferedStream
= class
83 function GetPosition
: Integer;
84 function GetSize
: Integer;
86 procedure WriteBuffer
;
87 procedure SetPosition(const Value
: Integer);
89 constructor Create(AStream
: TStream
);
90 destructor Destroy
; override;
91 function Read(var Buffer
; Count
: Integer): Integer;
92 function Write(const Buffer
; Count
: Integer): Integer;
93 function Seek(Offset
: Integer; Origin
: Word): Integer;
95 property Stream
: TStream read FStream
;
96 property Position
: Integer read GetPosition write SetPosition
;
97 property Size
: Integer read GetSize
;
100 constructor TBufferedStream
.Create(AStream
: TStream
);
104 FBufSize
:= DefaultBufferSize
;
105 GetMem(FBuffer
, FBufSize
);
110 FSize
:= AStream
.Size
;
113 destructor TBufferedStream
.Destroy
;
115 if FBuffer
<> nil then
120 FStream
.Position
:= Position
; // Make sure source stream has right position
124 function TBufferedStream
.GetPosition
: Integer;
126 Result
:= FBufStart
+ FBufPos
;
129 procedure TBufferedStream
.SetPosition(const Value
: Integer);
131 Seek(Value
, soFromCurrent
);
134 function TBufferedStream
.GetSize
: Integer;
139 procedure TBufferedStream
.ReadBuffer
;
143 SeekResult
:= FStream
.Seek(FBufStart
, 0);
144 if SeekResult
= -1 then
145 raise Exception
.Create('TBufferedStream.ReadBuffer: seek failed');
146 FBytesInBuf
:= FStream
.Read(FBuffer
^, FBufSize
);
147 if FBytesInBuf
<= 0 then
148 raise Exception
.Create('TBufferedStream.ReadBuffer: read failed');
151 procedure TBufferedStream
.WriteBuffer
;
154 BytesWritten
: Integer;
156 SeekResult
:= FStream
.Seek(FBufStart
, 0);
157 if SeekResult
= -1 then
158 raise Exception
.Create('TBufferedStream.WriteBuffer: seek failed');
159 BytesWritten
:= FStream
.Write(FBuffer
^, FBytesInBuf
);
160 if BytesWritten
<> FBytesInBuf
then
161 raise Exception
.Create('TBufferedStream.WriteBuffer: write failed');
164 procedure TBufferedStream
.Commit
;
173 function TBufferedStream
.Read(var Buffer
; Count
: Integer): Integer;
175 BufAsBytes
: TByteArray
absolute Buffer
;
176 BufIdx
, BytesToGo
, BytesToRead
: Integer;
178 // Calculate the actual number of bytes we can read - this depends on
179 // the current position and size of the stream as well as the number
180 // of bytes requested.
182 if FSize
< (FBufStart
+ FBufPos
+ Count
) then
183 BytesToGo
:= FSize
- (FBufStart
+ FBufPos
);
185 if BytesToGo
<= 0 then
190 // Remember to return the result of our calculation
194 if FBytesInBuf
= 0 then
196 // Calculate the number of bytes we can read prior to the loop
197 BytesToRead
:= FBytesInBuf
- FBufPos
;
198 if BytesToRead
> BytesToGo
then
199 BytesToRead
:= BytesToGo
;
200 // Copy from the stream buffer to the caller's buffer
201 Move(FBuffer
^[FBufPos
], BufAsBytes
[BufIdx
], BytesToRead
);
202 // Calculate the number of bytes still to read}
203 Dec(BytesToGo
, BytesToRead
);
205 // while we have bytes to read, read them
206 while BytesToGo
> 0 do
208 Inc(BufIdx
, BytesToRead
);
209 // As we've exhausted this buffer-full, advance to the next, check
210 // to see whether we need to write the buffer out first
216 Inc(FBufStart
, FBufSize
);
219 // Calculate the number of bytes we can read in this cycle
220 BytesToRead
:= FBytesInBuf
;
221 if BytesToRead
> BytesToGo
then
222 BytesToRead
:= BytesToGo
;
223 // Ccopy from the stream buffer to the caller's buffer
224 Move(FBuffer
^, BufAsBytes
[BufIdx
], BytesToRead
);
225 // Calculate the number of bytes still to read
226 Dec(BytesToGo
, BytesToRead
);
228 // Remember our new position
229 Inc(FBufPos
, BytesToRead
);
230 if FBufPos
= FBufSize
then
232 Inc(FBufStart
, FBufSize
);
238 function TBufferedStream
.Seek(Offset
: Integer; Origin
: Word): Integer;
240 NewBufStart
, NewPos
: Integer;
242 // Calculate the new position
244 soFromBeginning
: NewPos
:= Offset
;
245 soFromCurrent
: NewPos
:= FBufStart
+ FBufPos
+ Offset
;
246 soFromEnd
: NewPos
:= FSize
+ Offset
;
248 raise Exception
.Create('TBufferedStream.Seek: invalid origin');
251 if (NewPos
< 0) or (NewPos
> FSize
) then
253 //NewPos := ClampInt(NewPos, 0, FSize); don't do this - for writing
255 // Calculate which page of the file we need to be at
256 NewBufStart
:= NewPos
and not Pred(FBufSize
);
257 // If the new page is different than the old, mark the buffer as being
258 // ready to be replenished, and if need be write out any dirty data
259 if NewBufStart
<> FBufStart
then
266 FBufStart
:= NewBufStart
;
269 // Save the new position
270 FBufPos
:= NewPos
- NewBufStart
;
274 function TBufferedStream
.Write(const Buffer
; Count
: Integer): Integer;
276 BufAsBytes
: TByteArray
absolute Buffer
;
277 BufIdx
, BytesToGo
, BytesToWrite
: Integer;
279 // When we write to this stream we always assume that we can write the
280 // requested number of bytes: if we can't (eg, the disk is full) we'll
281 // get an exception somewhere eventually.
283 // Remember to return the result of our calculation
287 if (FBytesInBuf
= 0) and (FSize
> FBufStart
) then
289 // Calculate the number of bytes we can write prior to the loop
290 BytesToWrite
:= FBufSize
- FBufPos
;
291 if BytesToWrite
> BytesToGo
then
292 BytesToWrite
:= BytesToGo
;
293 // Copy from the caller's buffer to the stream buffer
294 Move(BufAsBytes
[BufIdx
], FBuffer
^[FBufPos
], BytesToWrite
);
295 // Mark our stream buffer as requiring a save to the actual stream,
296 // note that this will suffice for the rest of the routine as well: no
297 // inner routine will turn off the dirty flag.
299 // Calculate the number of bytes still to write
300 Dec(BytesToGo
, BytesToWrite
);
302 // While we have bytes to write, write them
303 while BytesToGo
> 0 do
305 Inc(BufIdx
, BytesToWrite
);
306 // As we've filled this buffer, write it out to the actual stream
307 // and advance to the next buffer, reading it if required
308 FBytesInBuf
:= FBufSize
;
310 Inc(FBufStart
, FBufSize
);
313 if FSize
> FBufStart
then
315 // Calculate the number of bytes we can write in this cycle
316 BytesToWrite
:= FBufSize
;
317 if BytesToWrite
> BytesToGo
then
318 BytesToWrite
:= BytesToGo
;
319 // Copy from the caller's buffer to our buffer
320 Move(BufAsBytes
[BufIdx
], FBuffer
^, BytesToWrite
);
321 // Calculate the number of bytes still to write
322 Dec(BytesToGo
, BytesToWrite
);
324 // Remember our new position
325 Inc(FBufPos
, BytesToWrite
);
326 // Make sure the count of valid bytes is correct
327 if FBytesInBuf
< FBufPos
then
328 FBytesInBuf
:= FBufPos
;
329 // Make sure the stream size is correct
330 if FSize
< (FBufStart
+ FBytesInBuf
) then
331 FSize
:= FBufStart
+ FBytesInBuf
;
332 // If we're at the end of the buffer, write it out and advance to the
333 // start of the next page
334 if FBufPos
= FBufSize
then
338 Inc(FBufStart
, FBufSize
);
344 { File IO functions }
346 function FileOpen(FileName
: PChar; Mode
: TOpenMode
): TImagingHandle
; cdecl;
353 omReadOnly
: Stream
:= TFileStream
.Create(FileName
, fmOpenRead
or fmShareDenyWrite
);
354 omCreate
: Stream
:= TFileStream
.Create(FileName
, fmCreate
);
357 if FileExists(FileName
) then
358 Stream
:= TFileStream
.Create(FileName
, fmOpenReadWrite
or fmShareExclusive
)
360 Stream
:= TFileStream
.Create(FileName
, fmCreate
);
364 Assert(Stream
<> nil);
365 Result
:= TBufferedStream
.Create(Stream
);
368 procedure FileClose(Handle
: TImagingHandle
); cdecl;
372 Stream
:= TBufferedStream(Handle
).Stream
;
373 TBufferedStream(Handle
).Free
;
377 function FileEof(Handle
: TImagingHandle
): Boolean; cdecl;
379 Result
:= TBufferedStream(Handle
).Position
= TBufferedStream(Handle
).Size
;
382 function FileSeek(Handle
: TImagingHandle
; Offset
: LongInt; Mode
: TSeekMode
):
385 Result
:= TBufferedStream(Handle
).Seek(Offset
, LongInt(Mode
));
388 function FileTell(Handle
: TImagingHandle
): LongInt; cdecl;
390 Result
:= TBufferedStream(Handle
).Position
;
393 function FileRead(Handle
: TImagingHandle
; Buffer
: Pointer; Count
: LongInt):
396 Result
:= TBufferedStream(Handle
).Read(Buffer
^, Count
);
399 function FileWrite(Handle
: TImagingHandle
; Buffer
: Pointer; Count
: LongInt):
402 Result
:= TBufferedStream(Handle
).Write(Buffer
^, Count
);
405 { Stream IO functions }
407 function StreamOpen(FileName
: PChar; Mode
: TOpenMode
): TImagingHandle
; cdecl;
412 procedure StreamClose(Handle
: TImagingHandle
); cdecl;
416 function StreamEof(Handle
: TImagingHandle
): Boolean; cdecl;
418 Result
:= TStream(Handle
).Position
= TStream(Handle
).Size
;
421 function StreamSeek(Handle
: TImagingHandle
; Offset
: LongInt; Mode
: TSeekMode
):
424 Result
:= TStream(Handle
).Seek(Offset
, LongInt(Mode
));
427 function StreamTell(Handle
: TImagingHandle
): LongInt; cdecl;
429 Result
:= TStream(Handle
).Position
;
432 function StreamRead(Handle
: TImagingHandle
; Buffer
: Pointer; Count
: LongInt):
435 Result
:= TStream(Handle
).Read(Buffer
^, Count
);
438 function StreamWrite(Handle
: TImagingHandle
; Buffer
: Pointer; Count
: LongInt):
441 Result
:= TStream(Handle
).Write(Buffer
^, Count
);
444 { Memory IO functions }
446 function MemoryOpen(FileName
: PChar; Mode
: TOpenMode
): TImagingHandle
; cdecl;
451 procedure MemoryClose(Handle
: TImagingHandle
); cdecl;
455 function MemoryEof(Handle
: TImagingHandle
): Boolean; cdecl;
457 Result
:= PMemoryIORec(Handle
).Position
= PMemoryIORec(Handle
).Size
;
460 function MemorySeek(Handle
: TImagingHandle
; Offset
: LongInt; Mode
: TSeekMode
):
463 Result
:= PMemoryIORec(Handle
).Position
;
465 smFromBeginning
: Result
:= Offset
;
466 smFromCurrent
: Result
:= PMemoryIORec(Handle
).Position
+ Offset
;
467 smFromEnd
: Result
:= PMemoryIORec(Handle
).Size
+ Offset
;
469 //Result := ClampInt(Result, 0, PMemoryIORec(Handle).Size); don't do this - some file formats use it
470 PMemoryIORec(Handle
).Position
:= Result
;
473 function MemoryTell(Handle
: TImagingHandle
): LongInt; cdecl;
475 Result
:= PMemoryIORec(Handle
).Position
;
478 function MemoryRead(Handle
: TImagingHandle
; Buffer
: Pointer; Count
: LongInt):
483 Rec
:= PMemoryIORec(Handle
);
485 if Rec
.Position
+ Count
> Rec
.Size
then
486 Result
:= Rec
.Size
- Rec
.Position
;
487 Move(Rec
.Data
[Rec
.Position
], Buffer
^, Result
);
488 Rec
.Position
:= Rec
.Position
+ Result
;
491 function MemoryWrite(Handle
: TImagingHandle
; Buffer
: Pointer; Count
: LongInt):
496 Rec
:= PMemoryIORec(Handle
);
498 if Rec
.Position
+ Count
> Rec
.Size
then
499 Result
:= Rec
.Size
- Rec
.Position
;
500 Move(Buffer
^, Rec
.Data
[Rec
.Position
], Result
);
501 Rec
.Position
:= Rec
.Position
+ Result
;
504 { Helper IO functions }
506 function GetInputSize(IOFunctions
: TIOFunctions
; Handle
: TImagingHandle
): LongInt;
510 OldPos
:= IOFunctions
.Tell(Handle
);
511 IOFunctions
.Seek(Handle
, 0, smFromEnd
);
512 Result
:= IOFunctions
.Tell(Handle
);
513 IOFunctions
.Seek(Handle
, OldPos
, smFromBeginning
);
516 function PrepareMemIO(Data
: Pointer; Size
: LongInt): TMemoryIORec
;
519 Result
.Position
:= 0;
523 function ReadLine(IOFunctions
: TIOFunctions
; Handle
: TImagingHandle
;
524 out Line
: AnsiString; FailOnControlChars
: Boolean): Boolean;
528 EolPos
, Pos
: Integer;
531 Endings
: set of AnsiChar;
537 Endings
:= [#10, #13];
540 while not IOFunctions
.Eof(Handle
) do
542 IOFunctions
.Read(Handle
, @C
, SizeOf(C
));
544 if FailOnControlChars
and (Byte(C
) < $20) then
549 if not (C
in Endings
) then
553 IOFunctions
.Seek(Handle
, EolPos
, smFromBeginning
);
558 SetLength(Line
, Length(Line
) + 1);
559 Line
[Length(Line
)] := C
;
562 else if not EolReached
then
565 EolPos
:= IOFunctions
.Tell(Handle
);
569 if Pos
>= MaxLine
then
576 IOFunctions
.Seek(Handle
, -Pos
, smFromCurrent
);
579 procedure WriteLine(IOFunctions
: TIOFunctions
; Handle
: TImagingHandle
;
580 const Line
: AnsiString; const LineEnding
: AnsiString);
584 ToWrite
:= Line
+ LineEnding
;
585 IOFunctions
.Write(Handle
, @ToWrite
[1], Length(ToWrite
));
589 OriginalFileIO
.Open
:= FileOpen
;
590 OriginalFileIO
.Close
:= FileClose
;
591 OriginalFileIO
.Eof
:= FileEof
;
592 OriginalFileIO
.Seek
:= FileSeek
;
593 OriginalFileIO
.Tell
:= FileTell
;
594 OriginalFileIO
.Read
:= FileRead
;
595 OriginalFileIO
.Write
:= FileWrite
;
597 StreamIO
.Open
:= StreamOpen
;
598 StreamIO
.Close
:= StreamClose
;
599 StreamIO
.Eof
:= StreamEof
;
600 StreamIO
.Seek
:= StreamSeek
;
601 StreamIO
.Tell
:= StreamTell
;
602 StreamIO
.Read
:= StreamRead
;
603 StreamIO
.Write
:= StreamWrite
;
605 MemoryIO
.Open
:= MemoryOpen
;
606 MemoryIO
.Close
:= MemoryClose
;
607 MemoryIO
.Eof
:= MemoryEof
;
608 MemoryIO
.Seek
:= MemorySeek
;
609 MemoryIO
.Tell
:= MemoryTell
;
610 MemoryIO
.Read
:= MemoryRead
;
611 MemoryIO
.Write
:= MemoryWrite
;
618 -- TODOS ----------------------------------------------------
621 -- 0.77.1 ---------------------------------------------------
622 - Updated IO Open functions according to changes in ImagingTypes.
623 - Added ReadLine and WriteLine functions.
625 -- 0.23 Changes/Bug Fixes -----------------------------------
626 - Added merge between buffered read-only and write-only file
627 stream adapters - TIFF saving needed both reading and writing.
628 - Fixed bug causing wrong value of TBufferedWriteFile.Size
629 (needed to add buffer pos to size).
631 -- 0.21 Changes/Bug Fixes -----------------------------------
632 - Removed TMemoryIORec.Written, use Position to get proper memory
633 position (Written didn't take Seeks into account).
634 - Added TBufferedReadFile and TBufferedWriteFile classes for
635 buffered file reading/writting. File IO functions now use these
636 classes resulting in performance increase mainly in file formats
637 that read/write many small chunks.
638 - Added fmShareDenyWrite to FileOpenRead. You can now read
639 files opened for reading by Imaging from other apps.
640 - Added GetInputSize and PrepareMemIO helper functions.
642 -- 0.19 Changes/Bug Fixes -----------------------------------
643 - changed behaviour of MemorySeek to act as TStream