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.

856 lines
27 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. {
  24. This unit contains image format loader/saver for Windows Bitmap images.
  25. }
  26. unit ImagingBitmap;
  27. {$I ImagingOptions.inc}
  28. interface
  29. uses
  30. ImagingTypes, Imaging, ImagingUtility, ImagingFormats, ImagingIO;
  31. type
  32. { Class for loading and saving Windows Bitmap images.
  33. It can load/save 8bit indexed, 16, 24, 32 bit RGB or ARGB
  34. images with or without RLE compression. It can also load 1/4 bit
  35. indexed images and OS2 bitmaps.}
  36. TBitmapFileFormat = class(TImageFileFormat)
  37. protected
  38. FUseRLE: LongBool;
  39. procedure Define; override;
  40. function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray;
  41. OnlyFirstLevel: Boolean): Boolean; override;
  42. function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray;
  43. Index: LongInt): Boolean; override;
  44. procedure ConvertToSupported(var Image: TImageData;
  45. const Info: TImageFormatInfo); override;
  46. public
  47. function TestFormat(Handle: TImagingHandle): Boolean; override;
  48. published
  49. { Controls that RLE compression is used during saving. Accessible trough
  50. ImagingBitmapRLE option.}
  51. property UseRLE: LongBool read FUseRLE write FUseRLE;
  52. end;
  53. implementation
  54. const
  55. SBitmapFormatName = 'Windows Bitmap Image';
  56. SBitmapMasks = '*.bmp,*.dib';
  57. BitmapSupportedFormats: TImageFormats = [ifIndex8, ifA1R5G5B5, ifA4R4G4B4,
  58. ifR5G6B5, ifR8G8B8, ifA8R8G8B8, ifX1R5G5B5, ifX4R4G4B4, ifX8R8G8B8];
  59. BitmapDefaultRLE = True;
  60. const
  61. { Bitmap file identifier 'BM'.}
  62. BMMagic: Word = 19778;
  63. { Constants for the TBitmapInfoHeader.Compression field.}
  64. BI_RGB = 0;
  65. BI_RLE8 = 1;
  66. BI_RLE4 = 2;
  67. BI_BITFIELDS = 3;
  68. V3InfoHeaderSize = 40;
  69. V4InfoHeaderSize = 108;
  70. type
  71. { File Header for Windows/OS2 bitmap file.}
  72. TBitmapFileHeader = packed record
  73. ID: Word; // Is always 19778 : 'BM'
  74. Size: LongWord; // Filesize
  75. Reserved1: Word;
  76. Reserved2: Word;
  77. Offset: LongWord; // Offset from start pos to beginning of image bits
  78. end;
  79. { Info Header for Windows bitmap file version 4.}
  80. TBitmapInfoHeader = packed record
  81. Size: LongWord;
  82. Width: LongInt;
  83. Height: LongInt;
  84. Planes: Word;
  85. BitCount: Word;
  86. Compression: LongWord;
  87. SizeImage: LongWord;
  88. XPelsPerMeter: LongInt;
  89. YPelsPerMeter: LongInt;
  90. ClrUsed: LongInt;
  91. ClrImportant: LongInt;
  92. RedMask: LongWord;
  93. GreenMask: LongWord;
  94. BlueMask: LongWord;
  95. AlphaMask: LongWord;
  96. CSType: LongWord;
  97. EndPoints: array[0..8] of LongWord;
  98. GammaRed: LongWord;
  99. GammaGreen: LongWord;
  100. GammaBlue: LongWord;
  101. end;
  102. { Info Header for OS2 bitmaps.}
  103. TBitmapCoreHeader = packed record
  104. Size: LongWord;
  105. Width: Word;
  106. Height: Word;
  107. Planes: Word;
  108. BitCount: Word;
  109. end;
  110. { Used in RLE encoding and decoding.}
  111. TRLEOpcode = packed record
  112. Count: Byte;
  113. Command: Byte;
  114. end;
  115. PRLEOpcode = ^TRLEOpcode;
  116. { TBitmapFileFormat class implementation }
  117. procedure TBitmapFileFormat.Define;
  118. begin
  119. inherited;
  120. FName := SBitmapFormatName;
  121. FFeatures := [ffLoad, ffSave];
  122. FSupportedFormats := BitmapSupportedFormats;
  123. FUseRLE := BitmapDefaultRLE;
  124. AddMasks(SBitmapMasks);
  125. RegisterOption(ImagingBitmapRLE, @FUseRLE);
  126. end;
  127. function TBitmapFileFormat.LoadData(Handle: TImagingHandle;
  128. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  129. var
  130. BF: TBitmapFileHeader;
  131. BI: TBitmapInfoHeader;
  132. BC: TBitmapCoreHeader;
  133. IsOS2: Boolean;
  134. PalRGB: PPalette24;
  135. I, FPalSize, AlignedSize, StartPos, HeaderSize, AlignedWidthBytes, WidthBytes: LongInt;
  136. Info: TImageFormatInfo;
  137. Data: Pointer;
  138. procedure LoadRGB;
  139. var
  140. I: LongInt;
  141. LineBuffer: PByte;
  142. begin
  143. with Images[0], GetIO do
  144. begin
  145. // If BI.Height is < 0 then image data are stored non-flipped
  146. // but default in windows is flipped so if Height is positive we must
  147. // flip it
  148. if BI.BitCount < 8 then
  149. begin
  150. // For 1 and 4 bit images load aligned data, they will be converted to
  151. // 8 bit and unaligned later
  152. GetMem(Data, AlignedSize);
  153. if BI.Height < 0 then
  154. Read(Handle, Data, AlignedSize)
  155. else
  156. for I := Height - 1 downto 0 do
  157. Read(Handle, @PByteArray(Data)[I * AlignedWidthBytes], AlignedWidthBytes);
  158. end
  159. else
  160. begin
  161. // Images with pixels of size >= 1 Byte are read line by line and
  162. // copied to image bits without padding bytes
  163. GetMem(LineBuffer, AlignedWidthBytes);
  164. try
  165. if BI.Height < 0 then
  166. for I := 0 to Height - 1 do
  167. begin
  168. Read(Handle, LineBuffer, AlignedWidthBytes);
  169. Move(LineBuffer^, PByteArray(Bits)[I * WidthBytes], WidthBytes);
  170. end
  171. else
  172. for I := Height - 1 downto 0 do
  173. begin
  174. Read(Handle, LineBuffer, AlignedWidthBytes);
  175. Move(LineBuffer^, PByteArray(Bits)[I * WidthBytes], WidthBytes);
  176. end;
  177. finally
  178. FreeMemNil(LineBuffer);
  179. end;
  180. end;
  181. end;
  182. end;
  183. procedure LoadRLE4;
  184. var
  185. RLESrc: PByteArray;
  186. Row, Col, WriteRow, I: LongInt;
  187. SrcPos: LongWord;
  188. DeltaX, DeltaY, Low, High: Byte;
  189. Pixels: PByteArray;
  190. OpCode: TRLEOpcode;
  191. NegHeightBitmap: Boolean;
  192. begin
  193. GetMem(RLESrc, BI.SizeImage);
  194. GetIO.Read(Handle, RLESrc, BI.SizeImage);
  195. with Images[0] do
  196. try
  197. Low := 0;
  198. Pixels := Bits;
  199. SrcPos := 0;
  200. NegHeightBitmap := BI.Height < 0;
  201. Row := 0; // Current row in dest image
  202. Col := 0; // Current column in dest image
  203. // Row in dest image where actuall writting will be done
  204. WriteRow := Iff(NegHeightBitmap, Row, Height - 1 - Row);
  205. while (Row < Height) and (SrcPos < BI.SizeImage) do
  206. begin
  207. // Read RLE op-code
  208. OpCode := PRLEOpcode(@RLESrc[SrcPos])^;
  209. Inc(SrcPos, SizeOf(OpCode));
  210. if OpCode.Count = 0 then
  211. begin
  212. // A byte Count of zero means that this is a special
  213. // instruction.
  214. case OpCode.Command of
  215. 0:
  216. begin
  217. // Move to next row
  218. Inc(Row);
  219. WriteRow := Iff(NegHeightBitmap, Row, Height - 1 - Row);
  220. Col := 0;
  221. end ;
  222. 1: Break; // Image is finished
  223. 2:
  224. begin
  225. // Move to a new relative position
  226. DeltaX := RLESrc[SrcPos];
  227. DeltaY := RLESrc[SrcPos + 1];
  228. Inc(SrcPos, 2);
  229. Inc(Col, DeltaX);
  230. Inc(Row, DeltaY);
  231. end
  232. else
  233. // Do not read data after EOF
  234. if SrcPos + OpCode.Command > BI.SizeImage then
  235. OpCode.Command := BI.SizeImage - SrcPos;
  236. // Take padding bytes and nibbles into account
  237. if Col + OpCode.Command > Width then
  238. OpCode.Command := Width - Col;
  239. // Store absolute data. Command code is the
  240. // number of absolute bytes to store
  241. for I := 0 to OpCode.Command - 1 do
  242. begin
  243. if (I and 1) = 0 then
  244. begin
  245. High := RLESrc[SrcPos] shr 4;
  246. Low := RLESrc[SrcPos] and $F;
  247. Pixels[WriteRow * Width + Col] := High;
  248. Inc(SrcPos);
  249. end
  250. else
  251. Pixels[WriteRow * Width + Col] := Low;
  252. Inc(Col);
  253. end;
  254. // Odd number of bytes is followed by a pad byte
  255. if (OpCode.Command mod 4) in [1, 2] then
  256. Inc(SrcPos);
  257. end;
  258. end
  259. else
  260. begin
  261. // Take padding bytes and nibbles into account
  262. if Col + OpCode.Count > Width then
  263. OpCode.Count := Width - Col;
  264. // Store a run of the same color value
  265. for I := 0 to OpCode.Count - 1 do
  266. begin
  267. if (I and 1) = 0 then
  268. Pixels[WriteRow * Width + Col] := OpCode.Command shr 4
  269. else
  270. Pixels[WriteRow * Width + Col] := OpCode.Command and $F;
  271. Inc(Col);
  272. end;
  273. end;
  274. end;
  275. finally
  276. FreeMem(RLESrc);
  277. end;
  278. end;
  279. procedure LoadRLE8;
  280. var
  281. RLESrc: PByteArray;
  282. SrcCount, Row, Col, WriteRow: LongInt;
  283. SrcPos: LongWord;
  284. DeltaX, DeltaY: Byte;
  285. Pixels: PByteArray;
  286. OpCode: TRLEOpcode;
  287. NegHeightBitmap: Boolean;
  288. begin
  289. GetMem(RLESrc, BI.SizeImage);
  290. GetIO.Read(Handle, RLESrc, BI.SizeImage);
  291. with Images[0] do
  292. try
  293. Pixels := Bits;
  294. SrcPos := 0;
  295. NegHeightBitmap := BI.Height < 0;
  296. Row := 0; // Current row in dest image
  297. Col := 0; // Current column in dest image
  298. // Row in dest image where actuall writting will be done
  299. WriteRow := Iff(NegHeightBitmap, Row, Height - 1 - Row);
  300. while (Row < Height) and (SrcPos < BI.SizeImage) do
  301. begin
  302. // Read RLE op-code
  303. OpCode := PRLEOpcode(@RLESrc[SrcPos])^;
  304. Inc(SrcPos, SizeOf(OpCode));
  305. if OpCode.Count = 0 then
  306. begin
  307. // A byte Count of zero means that this is a special
  308. // instruction.
  309. case OpCode.Command of
  310. 0:
  311. begin
  312. // Move to next row
  313. Inc(Row);
  314. WriteRow := Iff(NegHeightBitmap, Row, Height - 1 - Row);
  315. Col := 0;
  316. end ;
  317. 1: Break; // Image is finished
  318. 2:
  319. begin
  320. // Move to a new relative position
  321. DeltaX := RLESrc[SrcPos];
  322. DeltaY := RLESrc[SrcPos + 1];
  323. Inc(SrcPos, 2);
  324. Inc(Col, DeltaX);
  325. Inc(Row, DeltaY);
  326. end
  327. else
  328. SrcCount := OpCode.Command;
  329. // Do not read data after EOF
  330. if SrcPos + OpCode.Command > BI.SizeImage then
  331. OpCode.Command := BI.SizeImage - SrcPos;
  332. // Take padding bytes into account
  333. if Col + OpCode.Command > Width then
  334. OpCode.Command := Width - Col;
  335. // Store absolute data. Command code is the
  336. // number of absolute bytes to store
  337. Move(RLESrc[SrcPos], Pixels[WriteRow * Width + Col], OpCode.Command);
  338. Inc(SrcPos, SrcCount);
  339. Inc(Col, OpCode.Command);
  340. // Odd number of bytes is followed by a pad byte
  341. if (SrcCount mod 2) = 1 then
  342. Inc(SrcPos);
  343. end;
  344. end
  345. else
  346. begin
  347. // Take padding bytes into account
  348. if Col + OpCode.Count > Width then
  349. OpCode.Count := Width - Col;
  350. // Store a run of the same color value. Count is number of bytes to store
  351. FillChar(Pixels [WriteRow * Width + Col], OpCode.Count, OpCode.Command);
  352. Inc(Col, OpCode.Count);
  353. end;
  354. end;
  355. finally
  356. FreeMem(RLESrc);
  357. end;
  358. end;
  359. begin
  360. Data := nil;
  361. SetLength(Images, 1);
  362. with GetIO, Images[0] do
  363. try
  364. FillChar(BI, SizeOf(BI), 0);
  365. StartPos := Tell(Handle);
  366. Read(Handle, @BF, SizeOf(BF));
  367. Read(Handle, @BI.Size, SizeOf(BI.Size));
  368. IsOS2 := BI.Size = SizeOf(TBitmapCoreHeader);
  369. // Bitmap Info reading
  370. if IsOS2 then
  371. begin
  372. // OS/2 type bitmap, reads info header without 4 already read bytes
  373. Read(Handle, @PByteArray(@BC)[SizeOf(BI.Size)],
  374. SizeOf(TBitmapCoreHeader) - SizeOf(BI.Size));
  375. with BI do
  376. begin
  377. ClrUsed := 0;
  378. Compression := BI_RGB;
  379. BitCount := BC.BitCount;
  380. Height := BC.Height;
  381. Width := BC.Width;
  382. end;
  383. end
  384. else
  385. begin
  386. // Windows type bitmap
  387. HeaderSize := Min(BI.Size - SizeOf(BI.Size), SizeOf(BI) - SizeOf(BI.Size)); // do not read more than size of BI!
  388. Read(Handle, @PByteArray(@BI)[SizeOf(BI.Size)], HeaderSize);
  389. // SizeImage can be 0 for BI_RGB images, but it is here because of:
  390. // I saved 8bit bitmap in Paint Shop Pro 8 as OS2 RLE compressed.
  391. // It wrote strange 64 Byte Info header with SizeImage set to 0
  392. // Some progs were able to open it, some were not.
  393. if BI.SizeImage = 0 then
  394. BI.SizeImage := BF.Size - BF.Offset;
  395. end;
  396. // Bit mask reading. Only read it if there is V3 header, V4 header has
  397. // masks laoded already (only masks for RGB in V3).
  398. if (BI.Compression = BI_BITFIELDS) and (BI.Size = V3InfoHeaderSize) then
  399. Read(Handle, @BI.RedMask, SizeOf(BI.RedMask) * 3);
  400. case BI.BitCount of
  401. 1, 4, 8: Format := ifIndex8;
  402. 16:
  403. if BI.RedMask = $0F00 then
  404. // Set XRGB4 or ARGB4 according to value of alpha mask
  405. Format := IffFormat(BI.AlphaMask = 0, ifX4R4G4B4, ifA4R4G4B4)
  406. else if BI.RedMask = $F800 then
  407. Format := ifR5G6B5
  408. else
  409. // R5G5B5 is default 16bit format (with Compression = BI_RGB or masks).
  410. // We set it to A1.. and later there is a check if there are any alpha values
  411. // and if not it is changed to X1R5G5B5
  412. Format := ifA1R5G5B5;
  413. 24: Format := ifR8G8B8;
  414. 32: Format := ifA8R8G8B8; // As with R5G5B5 there is alpha check later
  415. end;
  416. NewImage(BI.Width, Abs(BI.Height), Format, Images[0]);
  417. Info := GetFormatInfo(Format);
  418. WidthBytes := Width * Info.BytesPerPixel;
  419. AlignedWidthBytes := (((Width * BI.BitCount) + 31) shr 5) * 4;
  420. AlignedSize := Height * LongInt(AlignedWidthBytes);
  421. // Palette settings and reading
  422. if BI.BitCount <= 8 then
  423. begin
  424. // Seek to the begining of palette
  425. Seek(Handle, StartPos + SizeOf(TBitmapFileHeader) + LongInt(BI.Size),
  426. smFromBeginning);
  427. if IsOS2 then
  428. begin
  429. // OS/2 type
  430. FPalSize := 1 shl BI.BitCount;
  431. GetMem(PalRGB, FPalSize * SizeOf(TColor24Rec));
  432. try
  433. Read(Handle, PalRGB, FPalSize * SizeOf(TColor24Rec));
  434. for I := 0 to FPalSize - 1 do
  435. with PalRGB[I] do
  436. begin
  437. Palette[I].R := R;
  438. Palette[I].G := G;
  439. Palette[I].B := B;
  440. end;
  441. finally
  442. FreeMemNil(PalRGB);
  443. end;
  444. end
  445. else
  446. begin
  447. // Windows type
  448. FPalSize := BI.ClrUsed;
  449. if FPalSize = 0 then
  450. FPalSize := 1 shl BI.BitCount;
  451. Read(Handle, Palette, FPalSize * SizeOf(TColor32Rec));
  452. end;
  453. for I := 0 to Info.PaletteEntries - 1 do
  454. Palette[I].A := $FF;
  455. end;
  456. // Seek to the beginning of image bits
  457. Seek(Handle, StartPos + LongInt(BF.Offset), smFromBeginning);
  458. case BI.Compression of
  459. BI_RGB: LoadRGB;
  460. BI_RLE4: LoadRLE4;
  461. BI_RLE8: LoadRLE8;
  462. BI_BITFIELDS: LoadRGB;
  463. end;
  464. if BI.AlphaMask = 0 then
  465. begin
  466. // Alpha mask is not stored in file (V3) or not defined.
  467. // Check alpha channels of loaded images if they might contain them.
  468. if Format = ifA1R5G5B5 then
  469. begin
  470. // Check if there is alpha channel present in A1R5GB5 images, if it is not
  471. // change format to X1R5G5B5
  472. if not Has16BitImageAlpha(Width * Height, Bits) then
  473. Format := ifX1R5G5B5;
  474. end
  475. else if Format = ifA8R8G8B8 then
  476. begin
  477. // Check if there is alpha channel present in A8R8G8B8 images, if it is not
  478. // change format to X8R8G8B8
  479. if not Has32BitImageAlpha(Width * Height, Bits) then
  480. Format := ifX8R8G8B8;
  481. end;
  482. end;
  483. if BI.BitCount < 8 then
  484. begin
  485. // 1 and 4 bpp images are supported only for loading which is now
  486. // so we now convert them to 8bpp (and unalign scanlines).
  487. case BI.BitCount of
  488. 1: Convert1To8(Data, Bits, Width, Height, AlignedWidthBytes, False);
  489. 4:
  490. begin
  491. // RLE4 bitmaps are translated to 8bit during RLE decoding
  492. if BI.Compression <> BI_RLE4 then
  493. Convert4To8(Data, Bits, Width, Height, AlignedWidthBytes, False);
  494. end;
  495. end;
  496. // Enlarge palette
  497. ReallocMem(Palette, Info.PaletteEntries * SizeOf(TColor32Rec));
  498. end;
  499. Result := True;
  500. finally
  501. FreeMemNil(Data);
  502. end;
  503. end;
  504. function TBitmapFileFormat.SaveData(Handle: TImagingHandle;
  505. const Images: TDynImageDataArray; Index: LongInt): Boolean;
  506. var
  507. StartPos, EndPos, I, Pad, PadSize, WidthBytes: LongInt;
  508. BF: TBitmapFileHeader;
  509. BI: TBitmapInfoHeader;
  510. Info: TImageFormatInfo;
  511. ImageToSave: TImageData;
  512. MustBeFreed: Boolean;
  513. procedure SaveRLE8;
  514. const
  515. BufferSize = 8 * 1024;
  516. var
  517. X, Y, I, SrcPos: LongInt;
  518. DiffCount, SameCount: Byte;
  519. Pixels: PByteArray;
  520. Buffer: array[0..BufferSize - 1] of Byte;
  521. BufferPos: LongInt;
  522. procedure WriteByte(ByteToWrite: Byte);
  523. begin
  524. if BufferPos = BufferSize then
  525. begin
  526. // Flush buffer if necessary
  527. GetIO.Write(Handle, @Buffer, BufferPos);
  528. BufferPos := 0;
  529. end;
  530. Buffer[BufferPos] := ByteToWrite;
  531. Inc(BufferPos);
  532. end;
  533. begin
  534. BufferPos := 0;
  535. with GetIO, ImageToSave do
  536. begin
  537. for Y := Height - 1 downto 0 do
  538. begin
  539. X := 0;
  540. SrcPos := 0;
  541. Pixels := @PByteArray(Bits)[Y * Width];
  542. while X < Width do
  543. begin
  544. SameCount := 1;
  545. DiffCount := 0;
  546. // Determine run length
  547. while X + SameCount < Width do
  548. begin
  549. // If we reach max run length or byte with different value
  550. // we end this run
  551. if (SameCount = 255) or (Pixels[SrcPos + SameCount] <> Pixels[SrcPos]) then
  552. Break;
  553. Inc(SameCount);
  554. end;
  555. if SameCount = 1 then
  556. begin
  557. // If there are not some bytes with the same value we
  558. // compute how many different bytes are there
  559. while X + DiffCount < Width do
  560. begin
  561. // Stop diff byte counting if there two bytes with the same value
  562. // or DiffCount is too big
  563. if (DiffCount = 255) or (Pixels[SrcPos + DiffCount + 1] =
  564. Pixels[SrcPos + DiffCount]) then
  565. Break;
  566. Inc(DiffCount);
  567. end;
  568. end;
  569. // Now store absolute data (direct copy image->file) or
  570. // store RLE code only (number of repeats + byte to be repeated)
  571. if DiffCount > 2 then
  572. begin
  573. // Save 'Absolute Data' (0 + number of bytes) but only
  574. // if number is >2 because (0+1) and (0+2) are other special commands
  575. WriteByte(0);
  576. WriteByte(DiffCount);
  577. // Write absolute data to buffer
  578. for I := 0 to DiffCount - 1 do
  579. WriteByte(Pixels[SrcPos + I]);
  580. Inc(X, DiffCount);
  581. Inc(SrcPos, DiffCount);
  582. // Odd number of bytes must be padded
  583. if (DiffCount mod 2) = 1 then
  584. WriteByte(0);
  585. end
  586. else
  587. begin
  588. // Save number of repeats and byte that should be repeated
  589. WriteByte(SameCount);
  590. WriteByte(Pixels[SrcPos]);
  591. Inc(X, SameCount);
  592. Inc(SrcPos, SameCount);
  593. end;
  594. end;
  595. // Save 'End Of Line' command
  596. WriteByte(0);
  597. WriteByte(0);
  598. end;
  599. // Save 'End Of Bitmap' command
  600. WriteByte(0);
  601. WriteByte(1);
  602. // Flush buffer
  603. GetIO.Write(Handle, @Buffer, BufferPos);
  604. end;
  605. end;
  606. begin
  607. Result := False;
  608. if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then
  609. with GetIO, ImageToSave do
  610. try
  611. Info := GetFormatInfo(Format);
  612. StartPos := Tell(Handle);
  613. FillChar(BF, SizeOf(BF), 0);
  614. FillChar(BI, SizeOf(BI), 0);
  615. // Other fields will be filled later - we don't know all values now
  616. BF.ID := BMMagic;
  617. Write(Handle, @BF, SizeOf(BF));
  618. if Info.HasAlphaChannel and (Info.BytesPerPixel = 2){V4 temp hack} then
  619. // Save images with alpha in V4 format
  620. BI.Size := V4InfoHeaderSize
  621. else
  622. // Save images without alpha in V3 format - for better compatibility
  623. BI.Size := V3InfoHeaderSize;
  624. BI.Width := Width;
  625. BI.Height := Height;
  626. BI.Planes := 1;
  627. BI.BitCount := Info.BytesPerPixel * 8;
  628. BI.XPelsPerMeter := 2835; // 72 dpi
  629. BI.YPelsPerMeter := 2835; // 72 dpi
  630. // Set compression
  631. if (Info.BytesPerPixel = 1) and FUseRLE then
  632. BI.Compression := BI_RLE8
  633. else if (Info.HasAlphaChannel or
  634. ((BI.BitCount = 16) and (Format <> ifX1R5G5B5))) and (Info.BytesPerPixel = 2){V4 temp hack} then
  635. BI.Compression := BI_BITFIELDS
  636. else
  637. BI.Compression := BI_RGB;
  638. // Write header (first time)
  639. Write(Handle, @BI, BI.Size);
  640. // Write mask info
  641. if BI.Compression = BI_BITFIELDS then
  642. begin
  643. if BI.BitCount = 16 then
  644. with Info.PixelFormat^ do
  645. begin
  646. BI.RedMask := RBitMask;
  647. BI.GreenMask := GBitMask;
  648. BI.BlueMask := BBitMask;
  649. BI.AlphaMask := ABitMask;
  650. end
  651. else
  652. begin
  653. // Set masks for A8R8G8B8
  654. BI.RedMask := $00FF0000;
  655. BI.GreenMask := $0000FF00;
  656. BI.BlueMask := $000000FF;
  657. BI.AlphaMask := $FF000000;
  658. end;
  659. // If V3 header is used RGB masks must be written to file separately.
  660. // V4 header has embedded masks (V4 is default for formats with alpha).
  661. if BI.Size = V3InfoHeaderSize then
  662. Write(Handle, @BI.RedMask, SizeOf(BI.RedMask) * 3);
  663. end;
  664. // Write palette
  665. if Palette <> nil then
  666. Write(Handle, Palette, Info.PaletteEntries * SizeOf(TColor32Rec));
  667. BF.Offset := Tell(Handle) - StartPos;
  668. if BI.Compression <> BI_RLE8 then
  669. begin
  670. // Save uncompressed data, scanlines must be filled with pad bytes
  671. // to be multiples of 4, save as bottom-up (Windows native) bitmap
  672. Pad := 0;
  673. WidthBytes := Width * Info.BytesPerPixel;
  674. PadSize := ((Width * BI.BitCount + 31) div 32) * 4 - WidthBytes;
  675. for I := Height - 1 downto 0 do
  676. begin
  677. Write(Handle, @PByteArray(Bits)[I * WidthBytes], WidthBytes);
  678. if PadSize > 0 then
  679. Write(Handle, @Pad, PadSize);
  680. end;
  681. end
  682. else
  683. begin
  684. // Save data with RLE8 compression
  685. SaveRLE8;
  686. end;
  687. EndPos := Tell(Handle);
  688. Seek(Handle, StartPos, smFromBeginning);
  689. // Rewrite header with new values
  690. BF.Size := EndPos - StartPos;
  691. BI.SizeImage := BF.Size - BF.Offset;
  692. Write(Handle, @BF, SizeOf(BF));
  693. Write(Handle, @BI, BI.Size);
  694. Seek(Handle, EndPos, smFromBeginning);
  695. Result := True;
  696. finally
  697. if MustBeFreed then
  698. FreeImage(ImageToSave);
  699. end;
  700. end;
  701. procedure TBitmapFileFormat.ConvertToSupported(var Image: TImageData;
  702. const Info: TImageFormatInfo);
  703. var
  704. ConvFormat: TImageFormat;
  705. begin
  706. if Info.IsFloatingPoint then
  707. // Convert FP image to RGB/ARGB according to presence of alpha channel
  708. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifR8G8B8)
  709. else if Info.HasGrayChannel or Info.IsIndexed then
  710. // Convert all grayscale and indexed images to Index8 unless they have alpha
  711. // (preserve it)
  712. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifIndex8)
  713. else if Info.HasAlphaChannel then
  714. // Convert images with alpha channel to A8R8G8B8
  715. ConvFormat := ifA8R8G8B8
  716. else if Info.UsePixelFormat then
  717. // Convert 16bit RGB images (no alpha) to X1R5G5B5
  718. ConvFormat := ifX1R5G5B5
  719. else
  720. // Convert all other formats to R8G8B8
  721. ConvFormat := ifR8G8B8;
  722. ConvertImage(Image, ConvFormat);
  723. end;
  724. function TBitmapFileFormat.TestFormat(Handle: TImagingHandle): Boolean;
  725. var
  726. Hdr: TBitmapFileHeader;
  727. ReadCount: LongInt;
  728. begin
  729. Result := False;
  730. if Handle <> nil then
  731. with GetIO do
  732. begin
  733. ReadCount := Read(Handle, @Hdr, SizeOf(Hdr));
  734. Seek(Handle, -ReadCount, smFromCurrent);
  735. Result := (Hdr.ID = BMMagic) and (ReadCount = SizeOf(Hdr));
  736. end;
  737. end;
  738. initialization
  739. RegisterImageFileFormat(TBitmapFileFormat);
  740. {
  741. File Notes:
  742. -- TODOS ----------------------------------------------------
  743. - nothing now
  744. - Add option to choose to save V3 or V4 headers.
  745. -- 0.25.0 Changes/Bug Fixes ---------------------------------
  746. - Fixed problem with indexed BMP loading - some pal entries
  747. could end up with alpha=0.
  748. -- 0.23 Changes/Bug Fixes -----------------------------------
  749. - Now saves bitmaps as bottom-up for better compatibility
  750. (mainly Lazarus' TImage!).
  751. - Fixed crash when loading bitmaps with headers larger than V4.
  752. - Temp hacks to disable V4 headers for 32bit images (compatibility with
  753. other soft).
  754. -- 0.21 Changes/Bug Fixes -----------------------------------
  755. - Removed temporary data allocation for image with aligned scanlines.
  756. They are now directly written to output so memory requirements are
  757. much lower now.
  758. - Now uses and recognizes BITMAPINFOHEADERV4 when loading/saving.
  759. Mainly for formats with alpha channels.
  760. - Added ifR5G6B5 to supported formats, changed converting to supported
  761. formats little bit.
  762. - Rewritten SaveRLE8 nested procedure. Old code was long and
  763. mysterious - new is short and much more readable.
  764. - MakeCompatible method moved to base class, put ConvertToSupported here.
  765. GetSupportedFormats removed, it is now set in constructor.
  766. - Rewritten LoadRLE4 and LoadRLE8 nested procedures.
  767. Should be less buggy an more readable (load inspired by Colosseum Builders' code).
  768. - Made public properties for options registered to SetOption/GetOption
  769. functions.
  770. - Addded alpha check to 32b bitmap loading too (teh same as in 16b
  771. bitmap loading).
  772. - Moved Convert1To8 and Convert4To8 to ImagingFormats
  773. - Changed extensions to filename masks.
  774. - Changed SaveData, LoadData, and MakeCompatible methods according
  775. to changes in base class in Imaging unit.
  776. -- 0.19 Changes/Bug Fixes -----------------------------------
  777. - fixed wrong const that caused A4R4G4B4 BMPs to load as A1R5G5B5
  778. - fixed the bug that caused 8bit RLE compressed bitmaps to load as
  779. whole black
  780. -- 0.17 Changes/Bug Fixes -----------------------------------
  781. - 16 bit images are usually without alpha but some has alpha
  782. channel and there is no indication of it - so I have added
  783. a check: if all pixels of image are with alpha = 0 image is treated
  784. as X1R5G5B5 otherwise as A1R5G5B5
  785. -- 0.13 Changes/Bug Fixes -----------------------------------
  786. - when loading 1/4 bit images with dword aligned dimensions
  787. there was ugly memory rewritting bug causing image corruption
  788. }
  789. end.