GLS.FileTGA.pas 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit GLS.FileTGA;
  5. (*
  6. Simple TGA formats supports.
  7. Currently supports only 24 and 32 bits RGB formats
  8. (uncompressed and RLE compressed).
  9. Based on David McDuffee's document from www.wotsit.org
  10. *)
  11. interface
  12. {$I GLScene.inc}
  13. uses
  14. System.Classes,
  15. System.SysUtils,
  16. Vcl.Graphics,
  17. GLS.Graphics;
  18. type
  19. (* TGA image load/save capable class.
  20. TGA formats supported : 24 and 32 bits uncompressed or RLE compressed,
  21. saves only to uncompressed TGA. *)
  22. TTGAImage = class(TBitmap)
  23. public
  24. constructor Create; override;
  25. destructor Destroy; override;
  26. procedure LoadFromStream(stream: TStream); override;
  27. procedure SaveToStream(stream: TStream); override;
  28. end;
  29. ETGAException = class(Exception)
  30. end;
  31. // ------------------------------------------------------------------
  32. implementation
  33. // ------------------------------------------------------------------
  34. type
  35. TTGAHeader = packed record
  36. IDLength: Byte;
  37. ColorMapType: Byte;
  38. ImageType: Byte;
  39. ColorMapOrigin: Word;
  40. ColorMapLength: Word;
  41. ColorMapEntrySize: Byte;
  42. XOrigin: Word;
  43. YOrigin: Word;
  44. Width: Word;
  45. Height: Word;
  46. PixelSize: Byte;
  47. ImageDescriptor: Byte;
  48. end;
  49. procedure ReadAndUnPackRLETGA24(stream: TStream; destBuf: PAnsiChar;
  50. totalSize: Integer);
  51. type
  52. TRGB24 = packed record
  53. r, g, b: Byte;
  54. end;
  55. PRGB24 = ^TRGB24;
  56. var
  57. n: Integer;
  58. color: TRGB24;
  59. bufEnd: PAnsiChar;
  60. b: Byte;
  61. begin
  62. bufEnd := @destBuf[totalSize];
  63. while destBuf < bufEnd do
  64. begin
  65. stream.Read(b, 1);
  66. if b >= 128 then
  67. begin
  68. // repetition packet
  69. stream.Read(color, 3);
  70. b := (b and 127) + 1;
  71. while b > 0 do
  72. begin
  73. PRGB24(destBuf)^ := color;
  74. Inc(destBuf, 3);
  75. Dec(b);
  76. end;
  77. end
  78. else
  79. begin
  80. n := ((b and 127) + 1) * 3;
  81. stream.Read(destBuf^, n);
  82. Inc(destBuf, n);
  83. end;
  84. end;
  85. end;
  86. procedure ReadAndUnPackRLETGA32(stream: TStream; destBuf: PAnsiChar;
  87. totalSize: Integer);
  88. type
  89. TRGB32 = packed record
  90. r, g, b, a: Byte;
  91. end;
  92. PRGB32 = ^TRGB32;
  93. var
  94. n: Integer;
  95. color: TRGB32;
  96. bufEnd: PAnsiChar;
  97. b: Byte;
  98. begin
  99. bufEnd := @destBuf[totalSize];
  100. while destBuf < bufEnd do
  101. begin
  102. stream.Read(b, 1);
  103. if b >= 128 then
  104. begin
  105. // repetition packet
  106. stream.Read(color, 4);
  107. b := (b and 127) + 1;
  108. while b > 0 do
  109. begin
  110. PRGB32(destBuf)^ := color;
  111. Inc(destBuf, 4);
  112. Dec(b);
  113. end;
  114. end
  115. else
  116. begin
  117. n := ((b and 127) + 1) * 4;
  118. stream.Read(destBuf^, n);
  119. Inc(destBuf, n);
  120. end;
  121. end;
  122. end;
  123. // ------------------
  124. // ------------------ TTGAImage ------------------
  125. // ------------------
  126. constructor TTGAImage.Create;
  127. begin
  128. inherited Create;
  129. end;
  130. destructor TTGAImage.Destroy;
  131. begin
  132. inherited Destroy;
  133. end;
  134. procedure TTGAImage.LoadFromStream(stream: TStream);
  135. var
  136. header: TTGAHeader;
  137. y, rowSize, bufSize: Integer;
  138. verticalFlip: Boolean;
  139. unpackBuf: PAnsiChar;
  140. function GetLineAddress(ALine: Integer): PByte;
  141. begin
  142. Result := PByte(ScanLine[ALine]);
  143. end;
  144. begin
  145. stream.Read(header, Sizeof(TTGAHeader));
  146. if header.ColorMapType <> 0 then
  147. raise ETGAException.Create('ColorMapped TGA unsupported');
  148. case header.PixelSize of
  149. 24:
  150. PixelFormat := pf24bit;
  151. 32:
  152. PixelFormat := pf32bit;
  153. else
  154. raise ETGAException.Create('Unsupported TGA ImageType');
  155. end;
  156. Width := header.Width;
  157. Height := header.Height;
  158. rowSize := (Width * header.PixelSize) div 8;
  159. verticalFlip := ((header.ImageDescriptor and $20) = 0);
  160. if header.IDLength > 0 then
  161. stream.Seek(header.IDLength, soFromCurrent);
  162. try
  163. case header.ImageType of
  164. 0:
  165. begin // empty image, support is useless but easy ;)
  166. Width := 0;
  167. Height := 0;
  168. Abort;
  169. end;
  170. 2:
  171. begin // uncompressed RGB/RGBA
  172. if verticalFlip then
  173. begin
  174. for y := 0 to Height - 1 do
  175. stream.Read(GetLineAddress(Height - y - 1)^, rowSize);
  176. end
  177. else
  178. begin
  179. for y := 0 to Height - 1 do
  180. stream.Read(GetLineAddress(y)^, rowSize);
  181. end;
  182. end;
  183. 10:
  184. begin // RLE encoded RGB/RGBA
  185. bufSize := Height * rowSize;
  186. unpackBuf := GetMemory(bufSize);
  187. try
  188. // read & unpack everything
  189. if header.PixelSize = 24 then
  190. ReadAndUnPackRLETGA24(stream, unpackBuf, bufSize)
  191. else
  192. ReadAndUnPackRLETGA32(stream, unpackBuf, bufSize);
  193. // fillup bitmap
  194. if verticalFlip then
  195. begin
  196. for y := 0 to Height - 1 do
  197. begin
  198. Move(unpackBuf[y * rowSize], GetLineAddress(Height - y - 1)
  199. ^, rowSize);
  200. end;
  201. end
  202. else
  203. begin
  204. for y := 0 to Height - 1 do
  205. Move(unpackBuf[y * rowSize], GetLineAddress(y)^, rowSize);
  206. end;
  207. finally
  208. FreeMemory(unpackBuf);
  209. end;
  210. end;
  211. else
  212. raise ETGAException.Create('Unsupported TGA ImageType ' +
  213. IntToStr(header.ImageType));
  214. end;
  215. finally
  216. //
  217. end;
  218. end;
  219. procedure TTGAImage.SaveToStream(stream: TStream);
  220. var
  221. y, rowSize: Integer;
  222. header: TTGAHeader;
  223. begin
  224. // prepare the header, essentially made up from zeroes
  225. FillChar(header, Sizeof(TTGAHeader), 0);
  226. header.ImageType := 2;
  227. header.Width := Width;
  228. header.Height := Height;
  229. case PixelFormat of
  230. pf24bit:
  231. header.PixelSize := 24;
  232. pf32bit:
  233. header.PixelSize := 32;
  234. else
  235. raise ETGAException.Create('Unsupported Bitmap format');
  236. end;
  237. stream.Write(header, Sizeof(TTGAHeader));
  238. rowSize := (Width * header.PixelSize) div 8;
  239. for y := 0 to Height - 1 do
  240. stream.Write(ScanLine[Height - y - 1]^, rowSize);
  241. end;
  242. // ------------------------------------------------------------------
  243. initialization
  244. // ------------------------------------------------------------------
  245. TPicture.RegisterFileFormat('tga', 'Targa', TTGAImage);
  246. finalization
  247. TPicture.UnregisterGraphicClass(TTGAImage);
  248. end.