ImagingIO.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  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. ImagingTypes, Imaging, SysUtils, Classes, 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. ReadBufferSize = 16 * 1024;
  51. WriteBufferSize = 16 * 1024;
  52. type
  53. TBufferedFile = class(TObject)
  54. protected
  55. FStream: TFileStream;
  56. FBuffer: PByteArray;
  57. FBufSize: LongInt;
  58. FBufPos: LongInt;
  59. FBufEnd: LongInt;
  60. function GetSize: LongInt;
  61. function GetPosition: LongInt; virtual; abstract;
  62. procedure SetPosition(Value: LongInt); virtual; abstract;
  63. procedure FlushBuffer; virtual; abstract;
  64. public
  65. constructor Create(const AFileName: string; Mode: Word; ABufferSize: LongInt);
  66. destructor Destroy; override;
  67. property Position: LongInt read GetPosition write SetPosition;
  68. property Size: LongInt read GetSize;
  69. end;
  70. { Simple file reader with buffering. Small reads are buffered
  71. (size < ABufferSize) and large ones are read whole at once.}
  72. TBufferedReadFile = class(TBufferedFile)
  73. protected
  74. function GetPosition: LongInt; override;
  75. procedure SetPosition(Value: LongInt); override;
  76. procedure FlushBuffer; override;
  77. public
  78. constructor Create(const AFileName: string; ABufferSize: LongInt);
  79. destructor Destroy; override;
  80. function Read(var DestBuffer; Count: LongInt): LongInt;
  81. end;
  82. { Simple file writer with buffering. Small writes are buffered
  83. (size < ABufferSize) and large ones are written whole at once.}
  84. TBufferedWriteFile = class(TBufferedFile)
  85. protected
  86. function GetPosition: LongInt; override;
  87. procedure SetPosition(Value: LongInt); override;
  88. procedure FlushBuffer; override;
  89. public
  90. constructor Create(const AFileName: string; ABufferSize: LongInt);
  91. destructor Destroy; override;
  92. function Write(var SrcBuffer; Count: LongInt): LongInt;
  93. end;
  94. { TBufferedFile }
  95. constructor TBufferedFile.Create(const AFileName: string; Mode: Word;
  96. ABufferSize: LongInt);
  97. begin
  98. FStream := TFileStream.Create(AFileName, Mode);
  99. FBufSize := ABufferSize;
  100. GetMem(FBuffer, FBufSize);
  101. end;
  102. destructor TBufferedFile.Destroy;
  103. begin
  104. FreeMem(FBuffer);
  105. FStream.Free;
  106. inherited Destroy;
  107. end;
  108. function TBufferedFile.GetSize: LongInt;
  109. begin
  110. Result := FStream.Size;
  111. end;
  112. { TBufferedReadFile }
  113. constructor TBufferedReadFile.Create(const AFileName: string;
  114. ABufferSize: LongInt);
  115. begin
  116. inherited Create(AFileName, fmOpenRead or fmShareDenyWrite, ABufferSize);
  117. end;
  118. destructor TBufferedReadFile.Destroy;
  119. begin
  120. // Set stream position to real position it would have without buffering
  121. SetPosition(Position);
  122. inherited Destroy;
  123. end;
  124. function TBufferedReadFile.Read(var DestBuffer; Count: LongInt): LongInt;
  125. var
  126. CopyNow: LongInt;
  127. Dest: PByte;
  128. begin
  129. if Count >= FBufSize then
  130. begin
  131. // Large data chunks are read directly from file, SetPosition is called
  132. // to invalidate current buffer
  133. SetPosition(Position);
  134. Result := FStream.Read(DestBuffer, Count);
  135. end
  136. else
  137. begin
  138. Dest := @DestBuffer;
  139. Result := 0;
  140. while Count > 0 do
  141. begin
  142. if FBufPos >= FBufEnd then
  143. begin
  144. // Current buffer position is >= buffer's current size so
  145. // new data is read to buffer
  146. FlushBuffer;
  147. if FBufEnd = 0 then
  148. // This happens if no new data was read to buffer - stream reached
  149. // end of file
  150. Exit;
  151. end;
  152. // Get exact number of bytes to copy
  153. CopyNow := FBufEnd - FBufPos;
  154. if CopyNow > Count then
  155. CopyNow := Count;
  156. // Copy data from buffer to dest and update counts
  157. Move(FBuffer[FBufPos], Dest^, CopyNow);
  158. Inc(FBufPos, CopyNow);
  159. Inc(Dest, CopyNow);
  160. Inc(Result, CopyNow);
  161. Dec(Count, CopyNow);
  162. end;
  163. end;
  164. end;
  165. function TBufferedReadFile.GetPosition: LongInt;
  166. begin
  167. Result := FStream.Position - (FBufEnd - FBufPos);
  168. end;
  169. procedure TBufferedReadFile.SetPosition(Value: LongInt);
  170. begin
  171. // Set stream position and invalidate buffer
  172. FStream.Position := Value;
  173. FBufPos := 0;
  174. FBufEnd := 0;
  175. end;
  176. procedure TBufferedReadFile.FlushBuffer;
  177. begin
  178. FBufEnd := FStream.Read(FBuffer^, FBufSize);
  179. FBufPos := 0;
  180. end;
  181. { TBufferedWriteFile }
  182. constructor TBufferedWriteFile.Create(const AFileName: string;
  183. ABufferSize: LongInt);
  184. begin
  185. inherited Create(AFileName, fmCreate or fmShareExclusive, ABufferSize);
  186. end;
  187. destructor TBufferedWriteFile.Destroy;
  188. begin
  189. // Buffer must be flushed before closing then file
  190. FlushBuffer;
  191. inherited Destroy;
  192. end;
  193. function TBufferedWriteFile.Write(var SrcBuffer; Count: LongInt): LongInt;
  194. var
  195. CopyNow: LongInt;
  196. Src: PByte;
  197. begin
  198. if Count >= FBufSize then
  199. begin
  200. // Large data chunks are written directly to file, current buffer is flushed first
  201. FlushBuffer;
  202. Result := FStream.Write(SrcBuffer, Count);
  203. end
  204. else
  205. begin
  206. Src := @SrcBuffer;
  207. Result := 0;
  208. while Count > 0 do
  209. begin
  210. // Get exact number of bytes to copy
  211. CopyNow := Count;
  212. if CopyNow > FBufSize - FBufPos then
  213. CopyNow := FBufSize - FBufPos;
  214. // Copy bytes from source to buffer
  215. Move(Src^, FBuffer[FBufPos], CopyNow);
  216. Inc(FBufPos, CopyNow);
  217. Inc(Src, CopyNow);
  218. Inc(Result, CopyNow);
  219. Dec(Count, CopyNow);
  220. // Flush buffer if it is full
  221. if FBufPos = FBufSize then
  222. FlushBuffer;
  223. end;
  224. end;
  225. end;
  226. function TBufferedWriteFile.GetPosition: LongInt;
  227. begin
  228. Result := FStream.Position + FBufPos;
  229. end;
  230. procedure TBufferedWriteFile.SetPosition(Value: LongInt);
  231. begin
  232. FlushBuffer;
  233. FStream.Position := Value;
  234. end;
  235. procedure TBufferedWriteFile.FlushBuffer;
  236. begin
  237. if FBufPos > 0 then
  238. begin
  239. FStream.WriteBuffer(FBuffer^, FBufPos);
  240. FBufPos := 0;
  241. end;
  242. end;
  243. { File IO functions }
  244. function FileOpenRead(FileName: PChar): TImagingHandle; cdecl;
  245. begin
  246. Result := TBufferedReadFile.Create(FileName, ReadBufferSize);
  247. end;
  248. function FileOpenWrite(FileName: PChar): TImagingHandle; cdecl;
  249. begin
  250. Result := TBufferedWriteFile.Create(FileName, ReadBufferSize);
  251. end;
  252. procedure FileClose(Handle: TImagingHandle); cdecl;
  253. begin
  254. TObject(Handle).Free;
  255. end;
  256. function FileEof(Handle: TImagingHandle): Boolean; cdecl;
  257. begin
  258. Result := TBufferedFile(Handle).Position = TBufferedFile(Handle).Size;
  259. end;
  260. function FileSeek(Handle: TImagingHandle; Offset: LongInt; Mode: TSeekMode):
  261. LongInt; cdecl;
  262. begin
  263. Result := TBufferedFile(Handle).Position;
  264. case Mode of
  265. smFromBeginning: Result := Offset;
  266. smFromCurrent: Result := TBufferedFile(Handle).Position + Offset;
  267. smFromEnd: Result := TBufferedFile(Handle).Size + Offset;
  268. end;
  269. TBufferedFile(Handle).Position := Result;
  270. end;
  271. function FileTell(Handle: TImagingHandle): LongInt; cdecl;
  272. begin
  273. Result := TBufferedFile(Handle).Position;
  274. end;
  275. function FileRead(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt):
  276. LongInt; cdecl;
  277. begin
  278. Result := TBufferedReadFile(Handle).Read(Buffer^, Count);
  279. end;
  280. function FileWrite(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt):
  281. LongInt; cdecl;
  282. begin
  283. Result := TBufferedWriteFile(Handle).Write(Buffer^, Count);
  284. end;
  285. { Stream IO functions }
  286. function StreamOpenRead(FileName: PChar): TImagingHandle; cdecl;
  287. begin
  288. Result := FileName;
  289. end;
  290. function StreamOpenWrite(FileName: PChar): TImagingHandle; cdecl;
  291. begin
  292. Result := FileName;
  293. end;
  294. procedure StreamClose(Handle: TImagingHandle); cdecl;
  295. begin
  296. end;
  297. function StreamEof(Handle: TImagingHandle): Boolean; cdecl;
  298. begin
  299. Result := TStream(Handle).Position = TStream(Handle).Size;
  300. end;
  301. function StreamSeek(Handle: TImagingHandle; Offset: LongInt; Mode: TSeekMode):
  302. LongInt; cdecl;
  303. begin
  304. Result := TStream(Handle).Seek(Offset, LongInt(Mode));
  305. end;
  306. function StreamTell(Handle: TImagingHandle): LongInt; cdecl;
  307. begin
  308. Result := TStream(Handle).Position;
  309. end;
  310. function StreamRead(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt):
  311. LongInt; cdecl;
  312. begin
  313. Result := TStream(Handle).Read(Buffer^, Count);
  314. end;
  315. function StreamWrite(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt):
  316. LongInt; cdecl;
  317. begin
  318. Result := TStream(Handle).Write(Buffer^, Count);
  319. end;
  320. { Memory IO functions }
  321. function MemoryOpenRead(FileName: PChar): TImagingHandle; cdecl;
  322. begin
  323. Result := FileName;
  324. end;
  325. function MemoryOpenWrite(FileName: PChar): TImagingHandle; cdecl;
  326. begin
  327. Result := FileName;
  328. end;
  329. procedure MemoryClose(Handle: TImagingHandle); cdecl;
  330. begin
  331. end;
  332. function MemoryEof(Handle: TImagingHandle): Boolean; cdecl;
  333. begin
  334. Result := PMemoryIORec(Handle).Position = PMemoryIORec(Handle).Size;
  335. end;
  336. function MemorySeek(Handle: TImagingHandle; Offset: LongInt; Mode: TSeekMode):
  337. LongInt; cdecl;
  338. begin
  339. Result := PMemoryIORec(Handle).Position;
  340. case Mode of
  341. smFromBeginning: Result := Offset;
  342. smFromCurrent: Result := PMemoryIORec(Handle).Position + Offset;
  343. smFromEnd: Result := PMemoryIORec(Handle).Size + Offset;
  344. end;
  345. Result := ClampInt(Result, 0, PMemoryIORec(Handle).Size);
  346. PMemoryIORec(Handle).Position := Result;
  347. end;
  348. function MemoryTell(Handle: TImagingHandle): LongInt; cdecl;
  349. begin
  350. Result := PMemoryIORec(Handle).Position;
  351. end;
  352. function MemoryRead(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt):
  353. LongInt; cdecl;
  354. var
  355. Rec: PMemoryIORec;
  356. begin
  357. Rec := PMemoryIORec(Handle);
  358. Result := Count;
  359. if Rec.Position + Count > Rec.Size then
  360. Result := Rec.Size - Rec.Position;
  361. Move(Rec.Data[Rec.Position], Buffer^, Result);
  362. Rec.Position := Rec.Position + Result;
  363. end;
  364. function MemoryWrite(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt):
  365. LongInt; cdecl;
  366. var
  367. Rec: PMemoryIORec;
  368. begin
  369. Rec := PMemoryIORec(Handle);
  370. Result := Count;
  371. if Rec.Position + Count > Rec.Size then
  372. Result := Rec.Size - Rec.Position;
  373. Move(Buffer^, Rec.Data[Rec.Position], Result);
  374. Rec.Position := Rec.Position + Result;
  375. end;
  376. { Helper IO functions }
  377. function GetInputSize(IOFunctions: TIOFunctions; Handle: TImagingHandle): LongInt;
  378. var
  379. OldPos: Int64;
  380. begin
  381. OldPos := IOFunctions.Tell(Handle);
  382. IOFunctions.Seek(Handle, 0, smFromEnd);
  383. Result := IOFunctions.Tell(Handle);
  384. IOFunctions.Seek(Handle, OldPos, smFromBeginning);
  385. end;
  386. function PrepareMemIO(Data: Pointer; Size: LongInt): TMemoryIORec;
  387. begin
  388. Result.Data := Data;
  389. Result.Position := 0;
  390. Result.Size := Size;
  391. end;
  392. initialization
  393. OriginalFileIO.OpenRead := FileOpenRead;
  394. OriginalFileIO.OpenWrite := FileOpenWrite;
  395. OriginalFileIO.Close := FileClose;
  396. OriginalFileIO.Eof := FileEof;
  397. OriginalFileIO.Seek := FileSeek;
  398. OriginalFileIO.Tell := FileTell;
  399. OriginalFileIO.Read := FileRead;
  400. OriginalFileIO.Write := FileWrite;
  401. StreamIO.OpenRead := StreamOpenRead;
  402. StreamIO.OpenWrite := StreamOpenWrite;
  403. StreamIO.Close := StreamClose;
  404. StreamIO.Eof := StreamEof;
  405. StreamIO.Seek := StreamSeek;
  406. StreamIO.Tell := StreamTell;
  407. StreamIO.Read := StreamRead;
  408. StreamIO.Write := StreamWrite;
  409. MemoryIO.OpenRead := MemoryOpenRead;
  410. MemoryIO.OpenWrite := MemoryOpenWrite;
  411. MemoryIO.Close := MemoryClose;
  412. MemoryIO.Eof := MemoryEof;
  413. MemoryIO.Seek := MemorySeek;
  414. MemoryIO.Tell := MemoryTell;
  415. MemoryIO.Read := MemoryRead;
  416. MemoryIO.Write := MemoryWrite;
  417. ResetFileIO;
  418. {
  419. File Notes:
  420. -- TODOS ----------------------------------------------------
  421. - nothing now
  422. -- 0.21 Changes/Bug Fixes -----------------------------------
  423. - Removed TMemoryIORec.Written, use Position to get proper memory
  424. position (Written didn't take Seeks into account).
  425. - Added TBufferedReadFile and TBufferedWriteFile classes for
  426. buffered file reading/writting. File IO functions now use these
  427. classes resulting in performance increase mainly in file formats
  428. that read/write many small chunks.
  429. - Added fmShareDenyWrite to FileOpenRead. You can now read
  430. files opened for reading by Imaging from other apps.
  431. - Added GetInputSize and PrepareMemIO helper functions.
  432. -- 0.19 Changes/Bug Fixes -----------------------------------
  433. - changed behaviour of MemorySeek to act as TStream
  434. based Seeks
  435. }
  436. end.