ImagingBitmap.pas 28 KB

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