Statistics
| Branch: | Tag: | Revision:

root / Imaging / ImagingIO.pas @ 0:95bd93c28625

History | View | Annotate | Download (17.1 kB)

1
{
2
  $Id: ImagingIO.pas 100 2007-06-28 21:09:52Z galfar $
3
  Vampyre Imaging Library
4
  by Marek Mauder 
5
  http://imaginglib.sourceforge.net
6
7
  The contents of this file are used with permission, subject to the Mozilla
8
  Public License Version 1.1 (the "License"); you may not use this file except
9
  in compliance with the License. You may obtain a copy of the License at
10
  http://www.mozilla.org/MPL/MPL-1.1.html
11
12
  Software distributed under the License is distributed on an "AS IS" basis,
13
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
14
  the specific language governing rights and limitations under the License.
15
16
  Alternatively, the contents of this file may be used under the terms of the
17
  GNU Lesser General Public License (the  "LGPL License"), in which case the
18
  provisions of the LGPL License are applicable instead of those above.
19
  If you wish to allow use of your version of this file only under the terms
20
  of the LGPL License and not to allow others to use your version of this file
21
  under the MPL, indicate your decision by deleting  the provisions above and
22
  replace  them with the notice and other provisions required by the LGPL
23
  License.  If you do not delete the provisions above, a recipient may use
24
  your version of this file under either the MPL or the LGPL License.
25
26
  For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
27
}
28
29
{ This unit contains default IO functions for reading from/writting to
30
  files, streams and memory.}
31
unit ImagingIO;
32
33
{$I ImagingOptions.inc}
34
35
interface
36
37
uses
38
  SysUtils, Classes, ImagingTypes, Imaging, ImagingUtility;
39
40
type
41
  TMemoryIORec = record
42
    Data: ImagingUtility.PByteArray;
43
    Position: LongInt;
44
    Size: LongInt;
45
  end;
46
  PMemoryIORec = ^TMemoryIORec;
47
48
var
49
  OriginalFileIO: TIOFunctions;
50
  FileIO: TIOFunctions;
51
  StreamIO: TIOFunctions;
52
  MemoryIO: TIOFunctions;
53
54
{ Helper function that returns size of input (from current position to the end)
55
  represented by Handle (and opened and operated on by members of IOFunctions).}
56
function GetInputSize(IOFunctions: TIOFunctions; Handle: TImagingHandle): LongInt;
57
{ Helper function that initializes TMemoryIORec with given params.}
58
function PrepareMemIO(Data: Pointer; Size: LongInt): TMemoryIORec;
59
60
implementation
61
62
const
63
  DefaultBufferSize = 16 * 1024;
64
65
type
66
  { Based on TaaBufferedStream
67
    Copyright (c) Julian M Bucknall 1997, 1999 }
68
  TBufferedStream = class(TObject)
69
  private
70
    FBuffer: PByteArray;
71
    FBufSize: Integer;
72
    FBufStart: Integer;
73
    FBufPos: Integer;
74
    FBytesInBuf: Integer;
75
    FSize: Integer;
76
    FDirty: Boolean;
77
    FStream: TStream;
78
    function GetPosition: Integer;
79
    function GetSize: Integer;
80
    procedure ReadBuffer;
81
    procedure WriteBuffer;
82
    procedure SetPosition(const Value: Integer);
83
  public
84
    constructor Create(AStream: TStream);
85
    destructor Destroy; override;
86
    function Read(var Buffer; Count: Integer): Integer;
87
    function Write(const Buffer; Count: Integer): Integer;
88
    function Seek(Offset: Integer; Origin: Word): Integer;
89
    procedure Commit;
90
    property Stream: TStream read FStream;
91
    property Position: Integer read GetPosition write SetPosition;
92
    property Size: Integer read GetSize;
93
  end;
94
95
constructor TBufferedStream.Create(AStream: TStream);
96
begin
97
  inherited Create;
98
  FStream := AStream;
99
  FBufSize := DefaultBufferSize;
100
  GetMem(FBuffer, FBufSize);
101
  FBufPos := 0;
102
  FBytesInBuf := 0;
103
  FBufStart := 0;
104
  FDirty := False;
105
  FSize := AStream.Size;
106
end;
107
108
destructor TBufferedStream.Destroy;
109
begin
110
  if FBuffer <> nil then
111
  begin
112
    Commit;
113
    FreeMem(FBuffer);
114
  end;
115
  FStream.Position := Position; // Make sure source stream has right position
116
  inherited Destroy;
117
end;
118
119
function TBufferedStream.GetPosition: Integer;
120
begin
121
  Result := FBufStart + FBufPos;
122
end;
123
124
procedure TBufferedStream.SetPosition(const Value: Integer);
125
begin
126
  Seek(Value, soFromCurrent);
127
end;
128
129
function TBufferedStream.GetSize: Integer;
130
begin
131
  Result := FSize;
132
end;
133
134
procedure TBufferedStream.ReadBuffer;
135
var
136
  SeekResult: Integer;
137
begin
138
  SeekResult := FStream.Seek(FBufStart, 0);
139
  if SeekResult = -1 then
140
    raise Exception.Create('TBufferedStream.ReadBuffer: seek failed');
141
  FBytesInBuf := FStream.Read(FBuffer^, FBufSize);
142
  if FBytesInBuf <= 0 then
143
    raise Exception.Create('TBufferedStream.ReadBuffer: read failed');
144
end;
145
146
procedure TBufferedStream.WriteBuffer;
147
var
148
  SeekResult: Integer;
149
  BytesWritten: Integer;
150
begin
151
  SeekResult := FStream.Seek(FBufStart, 0);
152
  if SeekResult = -1 then
153
    raise Exception.Create('TBufferedStream.WriteBuffer: seek failed');
154
  BytesWritten := FStream.Write(FBuffer^, FBytesInBuf);
155
  if BytesWritten <> FBytesInBuf then
156
    raise Exception.Create('TBufferedStream.WriteBuffer: write failed');
157
end;
158
159
procedure TBufferedStream.Commit;
160
begin
161
  if FDirty then
162
  begin
163
    WriteBuffer;
164
    FDirty := False;
165
  end;
166
end;
167
168
function TBufferedStream.Read(var Buffer; Count: Integer): Integer;
169
var
170
  BufAsBytes  : TByteArray absolute Buffer;
171
  BufIdx, BytesToGo, BytesToRead: Integer;
172
begin
173
  // Calculate the actual number of bytes we can read - this depends on
174
  // the current position and size of the stream as well as the number
175
  // of bytes requested.
176
  BytesToGo := Count;
177
  if FSize < (FBufStart + FBufPos + Count) then
178
    BytesToGo := FSize - (FBufStart + FBufPos);
179
180
  if BytesToGo <= 0 then
181
  begin
182
    Result := 0;
183
    Exit;
184
  end;
185
  // Remember to return the result of our calculation
186
  Result := BytesToGo;
187
188
  BufIdx := 0;
189
  if FBytesInBuf = 0 then
190
    ReadBuffer;
191
  // Calculate the number of bytes we can read prior to the loop
192
  BytesToRead := FBytesInBuf - FBufPos;
193
  if BytesToRead > BytesToGo then
194
    BytesToRead := BytesToGo;
195
  // Copy from the stream buffer to the caller's buffer
196
  Move(FBuffer^[FBufPos], BufAsBytes[BufIdx], BytesToRead);
197
  // Calculate the number of bytes still to read}
198
  Dec(BytesToGo, BytesToRead);
199
200
  // while we have bytes to read, read them
201
  while BytesToGo > 0 do
202
  begin
203
    Inc(BufIdx, BytesToRead);
204
    // As we've exhausted this buffer-full, advance to the next, check
205
    //  to see whether we need to write the buffer out first
206
    if FDirty then
207
    begin
208
      WriteBuffer;
209
      FDirty := false;
210
    end;
211
    Inc(FBufStart, FBufSize);
212
    FBufPos := 0;
213
    ReadBuffer;
214
    // Calculate the number of bytes we can read in this cycle
215
    BytesToRead := FBytesInBuf;
216
    if BytesToRead > BytesToGo then
217
      BytesToRead := BytesToGo;
218
    // Ccopy from the stream buffer to the caller's buffer
219
    Move(FBuffer^, BufAsBytes[BufIdx], BytesToRead);
220
    // Calculate the number of bytes still to read
221
    Dec(BytesToGo, BytesToRead);
222
  end;
223
  // Remember our new position
224
  Inc(FBufPos, BytesToRead);
225
  if FBufPos = FBufSize then
226
  begin
227
    Inc(FBufStart, FBufSize);
228
    FBufPos := 0;
229
    FBytesInBuf := 0;
230
  end;
231
end;
232
233
function TBufferedStream.Seek(Offset: Integer; Origin: Word): Integer;
234
var
235
  NewBufStart, NewPos: Integer;
236
begin
237
  // Calculate the new position
238
  case Origin of
239
    soFromBeginning : NewPos := Offset;
240
    soFromCurrent   : NewPos := FBufStart + FBufPos + Offset;
241
    soFromEnd       : NewPos := FSize + Offset;
242
  else
243
    raise Exception.Create('TBufferedStream.Seek: invalid origin');
244
  end;
245
246
  if (NewPos < 0) or (NewPos > FSize) then
247
  begin
248
    //NewPos := ClampInt(NewPos, 0, FSize); don't do this - for writing
249
  end;
250
  // Calculate which page of the file we need to be at
251
  NewBufStart := NewPos and not Pred(FBufSize);
252
  // If the new page is different than the old, mark the buffer as being
253
  // ready to be replenished, and if need be write out any dirty data
254
  if NewBufStart <> FBufStart then
255
  begin
256
    if FDirty then
257
    begin
258
      WriteBuffer;
259
      FDirty := False;
260
    end;
261
    FBufStart := NewBufStart;
262
    FBytesInBuf := 0;
263
  end;
264
  // Save the new position
265
  FBufPos := NewPos - NewBufStart;
266
  Result := NewPos;
267
end;
268
269
function TBufferedStream.Write(const Buffer; Count: Integer): Integer;
270
var
271
  BufAsBytes: TByteArray absolute Buffer;
272
  BufIdx, BytesToGo, BytesToWrite: Integer;
273
begin
274
  // When we write to this stream we always assume that we can write the
275
  // requested number of bytes: if we can't (eg, the disk is full) we'll
276
  // get an exception somewhere eventually.
277
  BytesToGo := Count;
278
  // Remember to return the result of our calculation
279
  Result := BytesToGo;
280
281
  BufIdx := 0;
282
  if (FBytesInBuf = 0) and (FSize > FBufStart) then
283
    ReadBuffer;
284
  // Calculate the number of bytes we can write prior to the loop
285
  BytesToWrite := FBufSize - FBufPos;
286
  if BytesToWrite > BytesToGo then
287
    BytesToWrite := BytesToGo;
288
  // Copy from the caller's buffer to the stream buffer
289
  Move(BufAsBytes[BufIdx], FBuffer^[FBufPos], BytesToWrite);
290
  // Mark our stream buffer as requiring a save to the actual stream,
291
  // note that this will suffice for the rest of the routine as well: no
292
  // inner routine will turn off the dirty flag.
293
  FDirty := True;
294
  // Calculate the number of bytes still to write
295
  Dec(BytesToGo, BytesToWrite);
296
297
  // While we have bytes to write, write them
298
  while BytesToGo > 0 do
299
  begin
300
    Inc(BufIdx, BytesToWrite);
301
    // As we've filled this buffer, write it out to the actual stream
302
    // and advance to the next buffer, reading it if required
303
    FBytesInBuf := FBufSize;
304
    WriteBuffer;
305
    Inc(FBufStart, FBufSize);
306
    FBufPos := 0;
307
    FBytesInBuf := 0;
308
    if FSize > FBufStart then
309
      ReadBuffer;
310
    // Calculate the number of bytes we can write in this cycle
311
    BytesToWrite := FBufSize;
312
    if BytesToWrite > BytesToGo then
313
      BytesToWrite := BytesToGo;
314
    // Copy from the caller's buffer to our buffer
315
    Move(BufAsBytes[BufIdx], FBuffer^, BytesToWrite);
316
    // Calculate the number of bytes still to write
317
    Dec(BytesToGo, BytesToWrite);
318
  end;
319
  // Remember our new position
320
  Inc(FBufPos, BytesToWrite);
321
  // Make sure the count of valid bytes is correct
322
  if FBytesInBuf < FBufPos then
323
    FBytesInBuf := FBufPos;
324
  // Make sure the stream size is correct
325
  if FSize < (FBufStart + FBytesInBuf) then
326
    FSize := FBufStart + FBytesInBuf;
327
  // If we're at the end of the buffer, write it out and advance to the
328
  // start of the next page
329
  if FBufPos = FBufSize then
330
  begin
331
    WriteBuffer;
332
    FDirty := False;
333
    Inc(FBufStart, FBufSize);
334
    FBufPos := 0;
335
    FBytesInBuf := 0;
336
  end;
337
end;
338
339
{ File IO functions }
340
341
function FileOpenRead(FileName: PChar): TImagingHandle; cdecl;
342
begin
343
  Result := TBufferedStream.Create(TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite));
344
end;
345
346
function FileOpenWrite(FileName: PChar): TImagingHandle; cdecl;
347
begin
348
  Result := TBufferedStream.Create(TFileStream.Create(FileName, fmCreate or fmShareDenyWrite));
349
end;
350
351
procedure FileClose(Handle: TImagingHandle); cdecl;
352
var
353
  Stream: TStream;
354
begin
355
  Stream := TBufferedStream(Handle).Stream;
356
  TBufferedStream(Handle).Free;
357
  Stream.Free;
358
end;
359
360
function FileEof(Handle: TImagingHandle): Boolean; cdecl;
361
begin
362
  Result := TBufferedStream(Handle).Position = TBufferedStream(Handle).Size;
363
end;
364
365
function FileSeek(Handle: TImagingHandle; Offset: LongInt; Mode: TSeekMode):
366
  LongInt; cdecl;
367
begin
368
  Result := TBufferedStream(Handle).Seek(Offset, LongInt(Mode));
369
end;
370
371
function FileTell(Handle: TImagingHandle): LongInt; cdecl;
372
begin
373
  Result := TBufferedStream(Handle).Position;
374
end;
375
376
function FileRead(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt):
377
  LongInt; cdecl;
378
begin
379
  Result := TBufferedStream(Handle).Read(Buffer^, Count);
380
end;
381
382
function FileWrite(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt):
383
  LongInt; cdecl;
384
begin
385
  Result := TBufferedStream(Handle).Write(Buffer^, Count);
386
end;
387
388
{ Stream IO functions }
389
390
function StreamOpenRead(FileName: PChar): TImagingHandle; cdecl;
391
begin
392
  Result := FileName;
393
end;
394
395
function StreamOpenWrite(FileName: PChar): TImagingHandle; cdecl;
396
begin
397
  Result := FileName;
398
end;
399
400
procedure StreamClose(Handle: TImagingHandle); cdecl;
401
begin
402
end;
403
404
function StreamEof(Handle: TImagingHandle): Boolean; cdecl;
405
begin
406
  Result := TStream(Handle).Position = TStream(Handle).Size;
407
end;
408
409
function StreamSeek(Handle: TImagingHandle; Offset: LongInt; Mode: TSeekMode):
410
  LongInt; cdecl;
411
begin
412
  Result := TStream(Handle).Seek(Offset, LongInt(Mode));
413
end;
414
415
function StreamTell(Handle: TImagingHandle): LongInt; cdecl;
416
begin
417
  Result := TStream(Handle).Position;
418
end;
419
420
function StreamRead(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt):
421
  LongInt; cdecl;
422
begin
423
  Result := TStream(Handle).Read(Buffer^, Count);
424
end;
425
426
function StreamWrite(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt):
427
  LongInt; cdecl;
428
begin
429
  Result := TStream(Handle).Write(Buffer^, Count);
430
end;
431
432
{ Memory IO functions }
433
434
function MemoryOpenRead(FileName: PChar): TImagingHandle; cdecl;
435
begin
436
  Result := FileName;
437
end;
438
439
function MemoryOpenWrite(FileName: PChar): TImagingHandle; cdecl;
440
begin
441
  Result := FileName;
442
end;
443
444
procedure MemoryClose(Handle: TImagingHandle); cdecl;
445
begin
446
end;
447
448
function MemoryEof(Handle: TImagingHandle): Boolean; cdecl;
449
begin
450
  Result := PMemoryIORec(Handle).Position = PMemoryIORec(Handle).Size;
451
end;
452
453
function MemorySeek(Handle: TImagingHandle; Offset: LongInt; Mode: TSeekMode):
454
  LongInt; cdecl;
455
begin
456
  Result := PMemoryIORec(Handle).Position;
457
  case Mode of
458
    smFromBeginning: Result := Offset;
459
    smFromCurrent:   Result := PMemoryIORec(Handle).Position + Offset;
460
    smFromEnd:       Result := PMemoryIORec(Handle).Size + Offset;
461
  end;
462
  //Result := ClampInt(Result, 0, PMemoryIORec(Handle).Size); don't do this - some file formats use it
463
  PMemoryIORec(Handle).Position := Result;
464
end;
465
466
function MemoryTell(Handle: TImagingHandle): LongInt; cdecl;
467
begin
468
  Result := PMemoryIORec(Handle).Position;
469
end;
470
471
function MemoryRead(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt):
472
  LongInt; cdecl;
473
var
474
  Rec: PMemoryIORec;
475
begin
476
  Rec := PMemoryIORec(Handle);
477
  Result := Count;
478
  if Rec.Position + Count > Rec.Size then
479
    Result := Rec.Size - Rec.Position;
480
  Move(Rec.Data[Rec.Position], Buffer^, Result);
481
  Rec.Position := Rec.Position + Result;
482
end;
483
484
function MemoryWrite(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt):
485
  LongInt; cdecl;
486
var
487
  Rec: PMemoryIORec;
488
begin
489
  Rec := PMemoryIORec(Handle);
490
  Result := Count;
491
  if Rec.Position + Count > Rec.Size then
492
    Result := Rec.Size - Rec.Position;
493
  Move(Buffer^, Rec.Data[Rec.Position], Result);
494
  Rec.Position := Rec.Position + Result;
495
end;
496
497
{ Helper IO functions }
498
499
function GetInputSize(IOFunctions: TIOFunctions; Handle: TImagingHandle): LongInt;
500
var
501
  OldPos: Int64;
502
begin
503
  OldPos := IOFunctions.Tell(Handle);
504
  IOFunctions.Seek(Handle, 0, smFromEnd);
505
  Result := IOFunctions.Tell(Handle);
506
  IOFunctions.Seek(Handle, OldPos, smFromBeginning);
507
end;
508
509
function PrepareMemIO(Data: Pointer; Size: LongInt): TMemoryIORec;
510
begin
511
  Result.Data := Data;
512
  Result.Position := 0;
513
  Result.Size := Size;
514
end;
515
516
initialization
517
  OriginalFileIO.OpenRead := FileOpenRead;
518
  OriginalFileIO.OpenWrite := FileOpenWrite;
519
  OriginalFileIO.Close := FileClose;
520
  OriginalFileIO.Eof := FileEof;
521
  OriginalFileIO.Seek := FileSeek;
522
  OriginalFileIO.Tell := FileTell;
523
  OriginalFileIO.Read := FileRead;
524
  OriginalFileIO.Write := FileWrite;
525
526
  StreamIO.OpenRead := StreamOpenRead;
527
  StreamIO.OpenWrite := StreamOpenWrite;
528
  StreamIO.Close := StreamClose;
529
  StreamIO.Eof := StreamEof;
530
  StreamIO.Seek := StreamSeek;
531
  StreamIO.Tell := StreamTell;
532
  StreamIO.Read := StreamRead;
533
  StreamIO.Write := StreamWrite;
534
535
  MemoryIO.OpenRead := MemoryOpenRead;
536
  MemoryIO.OpenWrite := MemoryOpenWrite;
537
  MemoryIO.Close := MemoryClose;
538
  MemoryIO.Eof := MemoryEof;
539
  MemoryIO.Seek := MemorySeek;
540
  MemoryIO.Tell := MemoryTell;
541
  MemoryIO.Read := MemoryRead;
542
  MemoryIO.Write := MemoryWrite;
543
544
  ResetFileIO;
545
546
{
547
  File Notes:
548
549
  -- TODOS ----------------------------------------------------
550
    - nothing now
551
552
  -- 0.23 Changes/Bug Fixes -----------------------------------
553
    - Added merge between buffered read-only and write-only file
554
      stream adapters - TIFF saving needed both reading and writing.
555
    - Fixed bug causing wrong value of TBufferedWriteFile.Size
556
      (needed to add buffer pos to size).
557
558
  -- 0.21 Changes/Bug Fixes -----------------------------------
559
    - Removed TMemoryIORec.Written, use Position to get proper memory
560
      position (Written didn't take Seeks into account).
561
    - Added TBufferedReadFile and TBufferedWriteFile classes for
562
      buffered file reading/writting. File IO functions now use these
563
      classes resulting in performance increase mainly in file formats
564
      that read/write many small chunks. 
565
    - Added fmShareDenyWrite to FileOpenRead. You can now read
566
      files opened for reading by Imaging from other apps.
567
    - Added GetInputSize and PrepareMemIO helper functions.
568
569
  -- 0.19 Changes/Bug Fixes -----------------------------------
570
    - changed behaviour of MemorySeek to act as TStream
571
      based Seeks
572
}
573
end.
574