ImagingIO.pas 17 KB

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