Repo for the search and displace ingest module that takes odf, docx and pdf and transforms it into .md to be used with search and displace operations
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

620 lines
18 KiB

3 years ago
  1. {
  2. Vampyre Imaging Library
  3. by Marek Mauder
  4. http://imaginglib.sourceforge.net
  5. The contents of this file are used with permission, subject to the Mozilla
  6. Public License Version 1.1 (the "License"); you may not use this file except
  7. in compliance with the License. You may obtain a copy of the License at
  8. http://www.mozilla.org/MPL/MPL-1.1.html
  9. Software distributed under the License is distributed on an "AS IS" basis,
  10. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  11. the specific language governing rights and limitations under the License.
  12. Alternatively, the contents of this file may be used under the terms of the
  13. GNU Lesser General Public License (the "LGPL License"), in which case the
  14. provisions of the LGPL License are applicable instead of those above.
  15. If you wish to allow use of your version of this file only under the terms
  16. of the LGPL License and not to allow others to use your version of this file
  17. under the MPL, indicate your decision by deleting the provisions above and
  18. replace them with the notice and other provisions required by the LGPL
  19. License. If you do not delete the provisions above, a recipient may use
  20. your version of this file under either the MPL or the LGPL License.
  21. For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
  22. }
  23. { This unit contains image format loader/saver for Targa images.}
  24. unit ImagingTarga;
  25. {$I ImagingOptions.inc}
  26. interface
  27. uses
  28. ImagingTypes, Imaging, ImagingFormats, ImagingUtility;
  29. type
  30. { Class for loading and saving Truevision Targa images.
  31. It can load/save 8bit indexed or grayscale, 16 bit RGB or grayscale,
  32. 24 bit RGB and 32 bit ARGB images with or without RLE compression.}
  33. TTargaFileFormat = class(TImageFileFormat)
  34. protected
  35. FUseRLE: LongBool;
  36. procedure Define; override;
  37. function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray;
  38. OnlyFirstLevel: Boolean): Boolean; override;
  39. function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray;
  40. Index: LongInt): Boolean; override;
  41. procedure ConvertToSupported(var Image: TImageData;
  42. const Info: TImageFormatInfo); override;
  43. public
  44. function TestFormat(Handle: TImagingHandle): Boolean; override;
  45. published
  46. { Controls that RLE compression is used during saving. Accessible trough
  47. ImagingTargaRLE option.}
  48. property UseRLE: LongBool read FUseRLE write FUseRLE;
  49. end;
  50. implementation
  51. const
  52. STargaFormatName = 'Truevision Targa Image';
  53. STargaMasks = '*.tga';
  54. TargaSupportedFormats: TImageFormats = [ifIndex8, ifGray8, ifA1R5G5B5,
  55. ifR8G8B8, ifA8R8G8B8];
  56. TargaDefaultRLE = False;
  57. const
  58. STargaSignature = 'TRUEVISION-XFILE';
  59. type
  60. { Targa file header.}
  61. TTargaHeader = packed record
  62. IDLength: Byte;
  63. ColorMapType: Byte;
  64. ImageType: Byte;
  65. ColorMapOff: Word;
  66. ColorMapLength: Word;
  67. ColorEntrySize: Byte;
  68. XOrg: SmallInt;
  69. YOrg: SmallInt;
  70. Width: SmallInt;
  71. Height: SmallInt;
  72. PixelSize: Byte;
  73. Desc: Byte;
  74. end;
  75. { Footer at the end of TGA file.}
  76. TTargaFooter = packed record
  77. ExtOff: LongWord; // Extension Area Offset
  78. DevDirOff: LongWord; // Developer Directory Offset
  79. Signature: TChar16; // TRUEVISION-XFILE
  80. Reserved: Byte; // ASCII period '.'
  81. NullChar: Byte; // 0
  82. end;
  83. { TTargaFileFormat class implementation }
  84. procedure TTargaFileFormat.Define;
  85. begin
  86. inherited;
  87. FName := STargaFormatName;
  88. FFeatures := [ffLoad, ffSave];
  89. FSupportedFormats := TargaSupportedFormats;
  90. FUseRLE := TargaDefaultRLE;
  91. AddMasks(STargaMasks);
  92. RegisterOption(ImagingTargaRLE, @FUseRLE);
  93. end;
  94. function TTargaFileFormat.LoadData(Handle: TImagingHandle;
  95. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  96. var
  97. Hdr: TTargaHeader;
  98. Foo: TTargaFooter;
  99. FooterFound, ExtFound: Boolean;
  100. I, PSize, PalSize: LongWord;
  101. Pal: Pointer;
  102. FmtInfo: TImageFormatInfo;
  103. WordValue: Word;
  104. procedure LoadRLE;
  105. var
  106. I, CPixel, Cnt: LongInt;
  107. Bpp, Rle: Byte;
  108. Buffer, Dest, Src: PByte;
  109. BufSize: LongInt;
  110. begin
  111. with GetIO, Images[0] do
  112. begin
  113. // Alocates buffer large enough to hold the worst case
  114. // RLE compressed data and reads then from input
  115. BufSize := Width * Height * FmtInfo.BytesPerPixel;
  116. BufSize := BufSize + BufSize div 2 + 1;
  117. GetMem(Buffer, BufSize);
  118. Src := Buffer;
  119. Dest := Bits;
  120. BufSize := Read(Handle, Buffer, BufSize);
  121. Cnt := Width * Height;
  122. Bpp := FmtInfo.BytesPerPixel;
  123. CPixel := 0;
  124. while CPixel < Cnt do
  125. begin
  126. Rle := Src^;
  127. Inc(Src);
  128. if Rle < 128 then
  129. begin
  130. // Process uncompressed pixel
  131. Rle := Rle + 1;
  132. CPixel := CPixel + Rle;
  133. for I := 0 to Rle - 1 do
  134. begin
  135. // Copy pixel from src to dest
  136. case Bpp of
  137. 1: Dest^ := Src^;
  138. 2: PWord(Dest)^ := PWord(Src)^;
  139. 3: PColor24Rec(Dest)^ := PColor24Rec(Src)^;
  140. 4: PLongWord(Dest)^ := PLongWord(Src)^;
  141. end;
  142. Inc(Src, Bpp);
  143. Inc(Dest, Bpp);
  144. end;
  145. end
  146. else
  147. begin
  148. // Process compressed pixels
  149. Rle := Rle - 127;
  150. CPixel := CPixel + Rle;
  151. // Copy one pixel from src to dest (many times there)
  152. for I := 0 to Rle - 1 do
  153. begin
  154. case Bpp of
  155. 1: Dest^ := Src^;
  156. 2: PWord(Dest)^ := PWord(Src)^;
  157. 3: PColor24Rec(Dest)^ := PColor24Rec(Src)^;
  158. 4: PLongWord(Dest)^ := PLongWord(Src)^;
  159. end;
  160. Inc(Dest, Bpp);
  161. end;
  162. Inc(Src, Bpp);
  163. end;
  164. end;
  165. // set position in source to real end of compressed data
  166. Seek(Handle, -(BufSize - LongInt(LongWord(Src) - LongWord(Buffer))),
  167. smFromCurrent);
  168. FreeMem(Buffer);
  169. end;
  170. end;
  171. begin
  172. SetLength(Images, 1);
  173. with GetIO, Images[0] do
  174. begin
  175. // Read targa header
  176. Read(Handle, @Hdr, SizeOf(Hdr));
  177. // Skip image ID info
  178. Seek(Handle, Hdr.IDLength, smFromCurrent);
  179. // Determine image format
  180. Format := ifUnknown;
  181. case Hdr.ImageType of
  182. 1, 9: Format := ifIndex8;
  183. 2, 10: case Hdr.PixelSize of
  184. 15: Format := ifX1R5G5B5;
  185. 16: Format := ifA1R5G5B5;
  186. 24: Format := ifR8G8B8;
  187. 32: Format := ifA8R8G8B8;
  188. end;
  189. 3, 11: Format := ifGray8;
  190. end;
  191. // Format was not assigned by previous testing (it should be in
  192. // well formed targas), so formats which reflects bit dept are selected
  193. if Format = ifUnknown then
  194. case Hdr.PixelSize of
  195. 8: Format := ifGray8;
  196. 15: Format := ifX1R5G5B5;
  197. 16: Format := ifA1R5G5B5;
  198. 24: Format := ifR8G8B8;
  199. 32: Format := ifA8R8G8B8;
  200. end;
  201. NewImage(Hdr.Width, Hdr.Height, Format, Images[0]);
  202. FmtInfo := GetFormatInfo(Format);
  203. if (Hdr.ColorMapType = 1) and (Hdr.ImageType in [1, 9]) then
  204. begin
  205. // Read palette
  206. PSize := Hdr.ColorMapLength * (Hdr.ColorEntrySize shr 3);
  207. GetMem(Pal, PSize);
  208. try
  209. Read(Handle, Pal, PSize);
  210. // Process palette
  211. PalSize := Iff(Hdr.ColorMapLength > FmtInfo.PaletteEntries,
  212. FmtInfo.PaletteEntries, Hdr.ColorMapLength);
  213. for I := 0 to PalSize - 1 do
  214. case Hdr.ColorEntrySize of
  215. 24:
  216. with Palette[I] do
  217. begin
  218. A := $FF;
  219. R := PPalette24(Pal)[I].R;
  220. G := PPalette24(Pal)[I].G;
  221. B := PPalette24(Pal)[I].B;
  222. end;
  223. // I've never seen tga with these palettes so they are untested
  224. 16:
  225. with Palette[I] do
  226. begin
  227. A := (PWordArray(Pal)[I] and $8000) shr 12;
  228. R := (PWordArray(Pal)[I] and $FC00) shr 7;
  229. G := (PWordArray(Pal)[I] and $03E0) shr 2;
  230. B := (PWordArray(Pal)[I] and $001F) shl 3;
  231. end;
  232. 32:
  233. with Palette[I] do
  234. begin
  235. A := PPalette32(Pal)[I].A;
  236. R := PPalette32(Pal)[I].R;
  237. G := PPalette32(Pal)[I].G;
  238. B := PPalette32(Pal)[I].B;
  239. end;
  240. end;
  241. finally
  242. FreeMemNil(Pal);
  243. end;
  244. end;
  245. case Hdr.ImageType of
  246. 0, 1, 2, 3:
  247. // Load uncompressed mode images
  248. Read(Handle, Bits, Size);
  249. 9, 10, 11:
  250. // Load RLE compressed mode images
  251. LoadRLE;
  252. end;
  253. // Check if there is alpha channel present in A1R5GB5 images, if it is not
  254. // change format to X1R5G5B5
  255. if Format = ifA1R5G5B5 then
  256. begin
  257. if not Has16BitImageAlpha(Width * Height, Bits) then
  258. Format := ifX1R5G5B5;
  259. end;
  260. // We must find true end of file and set input' position to it
  261. // paint programs appends extra info at the end of Targas
  262. // some of them multiple times (PSP Pro 8)
  263. repeat
  264. ExtFound := False;
  265. FooterFound := False;
  266. if Read(Handle, @WordValue, 2) = 2 then
  267. begin
  268. // 495 = size of Extension Area
  269. if WordValue = 495 then
  270. begin
  271. Seek(Handle, 493, smFromCurrent);
  272. ExtFound := True;
  273. end
  274. else
  275. Seek(Handle, -2, smFromCurrent);
  276. end;
  277. if Read(Handle, @Foo, SizeOf(Foo)) = SizeOf(Foo) then
  278. begin
  279. if Foo.Signature = STargaSignature then
  280. FooterFound := True
  281. else
  282. Seek(Handle, -SizeOf(Foo), smFromCurrent);
  283. end;
  284. until (not ExtFound) and (not FooterFound);
  285. // Some editors save targas flipped
  286. if Hdr.Desc < 31 then
  287. FlipImage(Images[0]);
  288. Result := True;
  289. end;
  290. end;
  291. function TTargaFileFormat.SaveData(Handle: TImagingHandle;
  292. const Images: TDynImageDataArray; Index: LongInt): Boolean;
  293. var
  294. I: LongInt;
  295. Hdr: TTargaHeader;
  296. FmtInfo: TImageFormatInfo;
  297. Pal: PPalette24;
  298. ImageToSave: TImageData;
  299. MustBeFreed: Boolean;
  300. procedure SaveRLE;
  301. var
  302. Dest: PByte;
  303. WidthBytes, Written, I, Total, DestSize: LongInt;
  304. function CountDiff(Data: PByte; Bpp, PixelCount: Longint): LongInt;
  305. var
  306. Pixel: LongWord;
  307. NextPixel: LongWord;
  308. N: LongInt;
  309. begin
  310. N := 0;
  311. Pixel := 0;
  312. NextPixel := 0;
  313. if PixelCount = 1 then
  314. begin
  315. Result := PixelCount;
  316. Exit;
  317. end;
  318. case Bpp of
  319. 1: Pixel := Data^;
  320. 2: Pixel := PWord(Data)^;
  321. 3: PColor24Rec(@Pixel)^ := PColor24Rec(Data)^;
  322. 4: Pixel := PLongWord(Data)^;
  323. end;
  324. while PixelCount > 1 do
  325. begin
  326. Inc(Data, Bpp);
  327. case Bpp of
  328. 1: NextPixel := Data^;
  329. 2: NextPixel := PWord(Data)^;
  330. 3: PColor24Rec(@NextPixel)^ := PColor24Rec(Data)^;
  331. 4: NextPixel := PLongWord(Data)^;
  332. end;
  333. if NextPixel = Pixel then
  334. Break;
  335. Pixel := NextPixel;
  336. N := N + 1;
  337. PixelCount := PixelCount - 1;
  338. end;
  339. if NextPixel = Pixel then
  340. Result := N
  341. else
  342. Result := N + 1;
  343. end;
  344. function CountSame(Data: PByte; Bpp, PixelCount: LongInt): LongInt;
  345. var
  346. Pixel: LongWord;
  347. NextPixel: LongWord;
  348. N: LongInt;
  349. begin
  350. N := 1;
  351. Pixel := 0;
  352. NextPixel := 0;
  353. case Bpp of
  354. 1: Pixel := Data^;
  355. 2: Pixel := PWord(Data)^;
  356. 3: PColor24Rec(@Pixel)^ := PColor24Rec(Data)^;
  357. 4: Pixel := PLongWord(Data)^;
  358. end;
  359. PixelCount := PixelCount - 1;
  360. while PixelCount > 0 do
  361. begin
  362. Inc(Data, Bpp);
  363. case Bpp of
  364. 1: NextPixel := Data^;
  365. 2: NextPixel := PWord(Data)^;
  366. 3: PColor24Rec(@NextPixel)^ := PColor24Rec(Data)^;
  367. 4: NextPixel := PLongWord(Data)^;
  368. end;
  369. if NextPixel <> Pixel then
  370. Break;
  371. N := N + 1;
  372. PixelCount := PixelCount - 1;
  373. end;
  374. Result := N;
  375. end;
  376. procedure RleCompressLine(Data: PByte; PixelCount, Bpp: LongInt; Dest:
  377. PByte; var Written: LongInt);
  378. const
  379. MaxRun = 128;
  380. var
  381. DiffCount: LongInt;
  382. SameCount: LongInt;
  383. RleBufSize: LongInt;
  384. begin
  385. RleBufSize := 0;
  386. while PixelCount > 0 do
  387. begin
  388. DiffCount := CountDiff(Data, Bpp, PixelCount);
  389. SameCount := CountSame(Data, Bpp, PixelCount);
  390. if (DiffCount > MaxRun) then
  391. DiffCount := MaxRun;
  392. if (SameCount > MaxRun) then
  393. SameCount := MaxRun;
  394. if (DiffCount > 0) then
  395. begin
  396. Dest^ := Byte(DiffCount - 1);
  397. Inc(Dest);
  398. PixelCount := PixelCount - DiffCount;
  399. RleBufSize := RleBufSize + (DiffCount * Bpp) + 1;
  400. Move(Data^, Dest^, DiffCount * Bpp);
  401. Inc(Data, DiffCount * Bpp);
  402. Inc(Dest, DiffCount * Bpp);
  403. end;
  404. if SameCount > 1 then
  405. begin
  406. Dest^ := Byte((SameCount - 1) or $80);
  407. Inc(Dest);
  408. PixelCount := PixelCount - SameCount;
  409. RleBufSize := RleBufSize + Bpp + 1;
  410. Inc(Data, (SameCount - 1) * Bpp);
  411. case Bpp of
  412. 1: Dest^ := Data^;
  413. 2: PWord(Dest)^ := PWord(Data)^;
  414. 3: PColor24Rec(Dest)^ := PColor24Rec(Data)^;
  415. 4: PLongWord(Dest)^ := PLongWord(Data)^;
  416. end;
  417. Inc(Data, Bpp);
  418. Inc(Dest, Bpp);
  419. end;
  420. end;
  421. Written := RleBufSize;
  422. end;
  423. begin
  424. with ImageToSave do
  425. begin
  426. // Allocate enough space to hold the worst case compression
  427. // result and then compress source's scanlines
  428. WidthBytes := Width * FmtInfo.BytesPerPixel;
  429. DestSize := WidthBytes * Height;
  430. DestSize := DestSize + DestSize div 2 + 1;
  431. GetMem(Dest, DestSize);
  432. Total := 0;
  433. try
  434. for I := 0 to Height - 1 do
  435. begin
  436. RleCompressLine(@PByteArray(Bits)[I * WidthBytes], Width,
  437. FmtInfo.BytesPerPixel, @PByteArray(Dest)[Total], Written);
  438. Total := Total + Written;
  439. end;
  440. GetIO.Write(Handle, Dest, Total);
  441. finally
  442. FreeMem(Dest);
  443. end;
  444. end;
  445. end;
  446. begin
  447. Result := False;
  448. if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then
  449. with GetIO, ImageToSave do
  450. try
  451. FmtInfo := GetFormatInfo(Format);
  452. // Fill targa header
  453. FillChar(Hdr, SizeOf(Hdr), 0);
  454. Hdr.IDLength := 0;
  455. Hdr.ColorMapType := Iff(FmtInfo.PaletteEntries > 0, 1, 0);
  456. Hdr.Width := Width;
  457. Hdr.Height := Height;
  458. Hdr.PixelSize := FmtInfo.BytesPerPixel * 8;
  459. Hdr.ColorMapLength := FmtInfo.PaletteEntries;
  460. Hdr.ColorEntrySize := Iff(FmtInfo.PaletteEntries > 0, 24, 0);
  461. Hdr.ColorMapOff := 0;
  462. // This indicates that targa is stored in top-left format
  463. // as our images -> no flipping is needed.
  464. Hdr.Desc := 32;
  465. // Set alpha channel size in descriptor (mostly ignored by other software though)
  466. if Format = ifA8R8G8B8 then
  467. Hdr.Desc := Hdr.Desc or 8
  468. else if Format = ifA1R5G5B5 then
  469. Hdr.Desc := Hdr.Desc or 1;
  470. // Choose image type
  471. if FmtInfo.IsIndexed then
  472. Hdr.ImageType := Iff(FUseRLE, 9, 1)
  473. else
  474. if FmtInfo.HasGrayChannel then
  475. Hdr.ImageType := Iff(FUseRLE, 11, 3)
  476. else
  477. Hdr.ImageType := Iff(FUseRLE, 10, 2);
  478. Write(Handle, @Hdr, SizeOf(Hdr));
  479. // Write palette
  480. if FmtInfo.PaletteEntries > 0 then
  481. begin
  482. GetMem(Pal, FmtInfo.PaletteEntries * SizeOf(TColor24Rec));
  483. try
  484. for I := 0 to FmtInfo.PaletteEntries - 1 do
  485. with Pal[I] do
  486. begin
  487. R := Palette[I].R;
  488. G := Palette[I].G;
  489. B := Palette[I].B;
  490. end;
  491. Write(Handle, Pal, FmtInfo.PaletteEntries * SizeOf(TColor24Rec));
  492. finally
  493. FreeMemNil(Pal);
  494. end;
  495. end;
  496. if FUseRLE then
  497. // Save rle compressed mode images
  498. SaveRLE
  499. else
  500. // Save uncompressed mode images
  501. Write(Handle, Bits, Size);
  502. Result := True;
  503. finally
  504. if MustBeFreed then
  505. FreeImage(ImageToSave);
  506. end;
  507. end;
  508. procedure TTargaFileFormat.ConvertToSupported(var Image: TImageData;
  509. const Info: TImageFormatInfo);
  510. var
  511. ConvFormat: TImageFormat;
  512. begin
  513. if Info.HasGrayChannel then
  514. // Convert all grayscale images to Gray8 (preserve alpha of AxGrayx formats)
  515. ConvFormat := IffFormat(not Info.HasAlphaChannel, ifGray8, ifA8R8G8B8)
  516. else if Info.IsIndexed then
  517. // Convert all indexed images to Index8
  518. ConvFormat := ifIndex8
  519. else if Info.HasAlphaChannel then
  520. // Convert images with alpha channel to A8R8G8B8
  521. ConvFormat := ifA8R8G8B8
  522. else if Info.UsePixelFormat then
  523. // Convert 16bit images (without alpha channel) to A1R5G5B5
  524. ConvFormat := ifA1R5G5B5
  525. else
  526. // Convert all other formats to R8G8B8
  527. ConvFormat := ifR8G8B8;
  528. ConvertImage(Image, ConvFormat);
  529. end;
  530. function TTargaFileFormat.TestFormat(Handle: TImagingHandle): Boolean;
  531. var
  532. Hdr: TTargaHeader;
  533. ReadCount: LongInt;
  534. begin
  535. Result := False;
  536. if Handle <> nil then
  537. begin
  538. ReadCount := GetIO.Read(Handle, @Hdr, SizeOf(Hdr));
  539. GetIO.Seek(Handle, -ReadCount, smFromCurrent);
  540. Result := (ReadCount >= SizeOf(Hdr)) and
  541. (Hdr.ImageType in [0, 1, 2, 3, 9, 10, 11]) and
  542. (Hdr.PixelSize in [1, 8, 15, 16, 24, 32]) and
  543. (Hdr.ColorEntrySize in [0, 16, 24, 32]);
  544. end;
  545. end;
  546. initialization
  547. RegisterImageFileFormat(TTargaFileFormat);
  548. {
  549. File Notes:
  550. -- TODOS ----------------------------------------------------
  551. - nothing now
  552. -- 0.21 Changes/Bug Fixes -----------------------------------
  553. - MakeCompatible method moved to base class, put ConvertToSupported here.
  554. GetSupportedFormats removed, it is now set in constructor.
  555. - Made public properties for options registered to SetOption/GetOption
  556. functions.
  557. - Changed extensions to filename masks.
  558. - Changed SaveData, LoadData, and MakeCompatible methods according
  559. to changes in base class in Imaging unit.
  560. -- 0.17 Changes/Bug Fixes -----------------------------------
  561. - 16 bit images are usually without alpha but some has alpha
  562. channel and there is no indication of it - so I have added
  563. a check: if all pixels of image are with alpha = 0 image is treated
  564. as X1R5G5B5 otherwise as A1R5G5B5
  565. - fixed problems with some nonstandard 15 bit images
  566. }
  567. end.