TgaFileType.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726
  1. using System;
  2. using System.ComponentModel;
  3. using System.Drawing;
  4. using System.IO;
  5. using System.Runtime.InteropServices;
  6. namespace PaintDotNet.Data
  7. {
  8. public sealed class TgaFileType : InternalFileType
  9. {
  10. public enum PropertyNames
  11. {
  12. BitDepth = 0,
  13. RleCompress = 1
  14. }
  15. public enum TgaBitDepthUIChoices
  16. {
  17. AutoDetect = 0,
  18. Bpp32 = 1,
  19. Bpp24 = 2
  20. }
  21. private enum TgaType
  22. : byte
  23. {
  24. Null = 0,
  25. Map = 1,
  26. Rgb = 2,
  27. Mono = 3,
  28. RleMap = 9,
  29. RleRgb = 10,
  30. RleMono = 11,
  31. CompMap = 32,
  32. CompMap4 = 33
  33. }
  34. [StructLayout(LayoutKind.Sequential)]
  35. private struct TgaHeader
  36. {
  37. public byte idLength; // Image ID Field Length
  38. public byte cmapType; // Color Map Type
  39. public TgaType imageType; // Image Type
  40. public ushort cmapIndex; // First Entry Index
  41. public ushort cmapLength; // Color Map Length
  42. public byte cmapEntrySize; // Color Map Entry Size
  43. public ushort xOrigin; // X-origin of Image
  44. public ushort yOrigin; // Y-origin of Image
  45. public ushort imageWidth; // Image Width
  46. public ushort imageHeight; // Image Height
  47. public byte pixelDepth; // Pixel Depth
  48. public byte imageDesc; // Image Descriptor
  49. public void Write(Stream output)
  50. {
  51. output.WriteByte(this.idLength);
  52. output.WriteByte(this.cmapType);
  53. output.WriteByte((byte)this.imageType);
  54. Utility.WriteUInt16(output, this.cmapIndex);
  55. Utility.WriteUInt16(output, this.cmapLength);
  56. output.WriteByte(this.cmapEntrySize);
  57. Utility.WriteUInt16(output, this.xOrigin);
  58. Utility.WriteUInt16(output, this.yOrigin);
  59. Utility.WriteUInt16(output, this.imageWidth);
  60. Utility.WriteUInt16(output, this.imageHeight);
  61. output.WriteByte(this.pixelDepth);
  62. output.WriteByte(this.imageDesc);
  63. }
  64. public TgaHeader(Stream input)
  65. {
  66. int byteRead = input.ReadByte();
  67. if (byteRead == -1)
  68. {
  69. throw new EndOfStreamException();
  70. }
  71. else
  72. {
  73. this.idLength = (byte)byteRead;
  74. }
  75. byteRead = input.ReadByte();
  76. if (byteRead == -1)
  77. {
  78. throw new EndOfStreamException();
  79. }
  80. else
  81. {
  82. this.cmapType = (byte)byteRead;
  83. }
  84. byteRead = input.ReadByte();
  85. if (byteRead == -1)
  86. {
  87. throw new EndOfStreamException();
  88. }
  89. else
  90. {
  91. this.imageType = (TgaType)byteRead;
  92. }
  93. int shortRead = Utility.ReadUInt16(input);
  94. if (shortRead == -1)
  95. {
  96. throw new EndOfStreamException();
  97. }
  98. else
  99. {
  100. this.cmapIndex = (ushort)shortRead;
  101. }
  102. shortRead = Utility.ReadUInt16(input);
  103. if (shortRead == -1)
  104. {
  105. throw new EndOfStreamException();
  106. }
  107. else
  108. {
  109. this.cmapLength = (ushort)shortRead;
  110. }
  111. byteRead = input.ReadByte();
  112. if (byteRead == -1)
  113. {
  114. throw new EndOfStreamException();
  115. }
  116. else
  117. {
  118. this.cmapEntrySize = (byte)byteRead;
  119. }
  120. shortRead = Utility.ReadUInt16(input);
  121. if (shortRead == -1)
  122. {
  123. throw new EndOfStreamException();
  124. }
  125. else
  126. {
  127. this.xOrigin = (ushort)shortRead;
  128. }
  129. shortRead = Utility.ReadUInt16(input);
  130. if (shortRead == -1)
  131. {
  132. throw new EndOfStreamException();
  133. }
  134. else
  135. {
  136. this.yOrigin = (ushort)shortRead;
  137. }
  138. shortRead = Utility.ReadUInt16(input);
  139. if (shortRead == -1)
  140. {
  141. throw new EndOfStreamException();
  142. }
  143. else
  144. {
  145. this.imageWidth = (ushort)shortRead;
  146. }
  147. shortRead = Utility.ReadUInt16(input);
  148. if (shortRead == -1)
  149. {
  150. throw new EndOfStreamException();
  151. }
  152. else
  153. {
  154. this.imageHeight = (ushort)shortRead;
  155. }
  156. byteRead = input.ReadByte();
  157. if (byteRead == -1)
  158. {
  159. throw new EndOfStreamException();
  160. }
  161. else
  162. {
  163. this.pixelDepth = (byte)byteRead;
  164. }
  165. byteRead = input.ReadByte();
  166. if (byteRead == -1)
  167. {
  168. throw new EndOfStreamException();
  169. }
  170. else
  171. {
  172. this.imageDesc = (byte)byteRead;
  173. }
  174. }
  175. }
  176. public override Document OnLoad(System.IO.Stream input)
  177. {
  178. TgaHeader header = new TgaHeader(input);
  179. bool compressed;
  180. switch (header.imageType)
  181. {
  182. case TgaType.Map:
  183. case TgaType.Rgb:
  184. case TgaType.Mono:
  185. compressed = false;
  186. break;
  187. case TgaType.RleMap:
  188. case TgaType.RleRgb:
  189. case TgaType.RleMono:
  190. compressed = true;
  191. break;
  192. default:
  193. throw new FormatException("unknown TGA image type");
  194. }
  195. if (header.imageWidth == 0 ||
  196. header.imageHeight == 0 ||
  197. header.pixelDepth == 0 ||
  198. header.cmapLength > 256)
  199. {
  200. throw new FormatException("bad TGA header");
  201. }
  202. if (header.pixelDepth != 8 &&
  203. header.pixelDepth != 15 &&
  204. header.pixelDepth != 16 &&
  205. header.pixelDepth != 24 &&
  206. header.pixelDepth != 32)
  207. {
  208. throw new FormatException("bad TGA header: pixelDepth not one of {8, 15, 16, 24, 32}");
  209. }
  210. if (header.idLength > 0)
  211. {
  212. input.Position += header.idLength; // skip descriptor
  213. }
  214. //BitmapLayer layer = Layer.CreateBackgroundLayer(header.imageWidth, header.imageHeight);
  215. try
  216. {
  217. Surface surface = new Surface(new Size(header.imageWidth, header.imageHeight));//layer.Surface;
  218. surface.Clear((ColorBgra)0xffffffff);
  219. ColorBgra[] palette = null;
  220. if (header.cmapType != 0)
  221. {
  222. palette = LoadPalette(input, header.cmapLength);
  223. }
  224. if (header.imageType == TgaType.Mono ||
  225. header.imageType == TgaType.RleMono)
  226. {
  227. palette = CreateGrayPalette();
  228. }
  229. // Bits 4 & 5 of the image descriptor byte control the ordering of the pixels
  230. bool xReversed = ((header.imageDesc & 16) == 16);
  231. bool yReversed = ((header.imageDesc & 32) == 32);
  232. byte rleLeftOver = 255; // for images with illegal packet boundary
  233. for (int y = 0; y < header.imageHeight; ++y)
  234. {
  235. MemoryBlock dstRow;
  236. if (yReversed)
  237. {
  238. dstRow = surface.GetRow(y);
  239. }
  240. else
  241. {
  242. dstRow = surface.GetRow(header.imageHeight - y - 1);
  243. }
  244. if (compressed)
  245. {
  246. rleLeftOver = ExpandCompressedLine(dstRow, 0, ref header, input, header.imageWidth, y, rleLeftOver, palette);
  247. }
  248. else
  249. {
  250. ExpandUncompressedLine(dstRow, 0, ref header, input, header.imageWidth, y, 0, palette);
  251. }
  252. }
  253. if (xReversed)
  254. {
  255. MirrorX(surface);
  256. }
  257. Document document = new Document(surface.Width, surface.Height);
  258. //document.Layers.Add(layer);
  259. return document;
  260. }
  261. catch
  262. {
  263. /*if (layer != null)
  264. {
  265. layer.Dispose();
  266. layer = null;
  267. }*/
  268. throw;
  269. }
  270. }
  271. private void MirrorX(Surface surface)
  272. {
  273. for (int y = 0; y < surface.Height; ++y)
  274. {
  275. for (int x = 0; x < surface.Width / 2; ++x)
  276. {
  277. ColorBgra rightSide = surface[surface.Width - x - 1, y];
  278. surface[surface.Width - x - 1, y] = surface[x, y];
  279. surface[x, y] = rightSide;
  280. }
  281. }
  282. }
  283. private ColorBgra[] CreateGrayPalette()
  284. {
  285. ColorBgra[] palette = new ColorBgra[256];
  286. for (int i = 0; i < palette.Length; ++i)
  287. {
  288. palette[i] = ColorBgra.FromBgra((byte)i, (byte)i, (byte)i, 255);
  289. }
  290. return palette;
  291. }
  292. private ColorBgra[] LoadPalette(Stream input, int count)
  293. {
  294. ColorBgra[] palette = new ColorBgra[count];
  295. for (int i = 0; i < palette.Length; ++i)
  296. {
  297. int blue = input.ReadByte();
  298. if (blue == -1)
  299. {
  300. throw new EndOfStreamException();
  301. }
  302. int green = input.ReadByte();
  303. if (green == -1)
  304. {
  305. throw new EndOfStreamException();
  306. }
  307. int red = input.ReadByte();
  308. if (red == -1)
  309. {
  310. throw new EndOfStreamException();
  311. }
  312. palette[i] = ColorBgra.FromBgra((byte)blue, (byte)green, (byte)red, 255);
  313. }
  314. return palette;
  315. }
  316. private byte ExpandCompressedLine(MemoryBlock dst, int dstIndex, ref TgaHeader header, Stream input, int width, int y, byte rleLeftOver, ColorBgra[] palette)
  317. {
  318. byte rle;
  319. long filePos = 0;
  320. int x = 0;
  321. while (x < width)
  322. {
  323. if (rleLeftOver != 255)
  324. {
  325. rle = rleLeftOver;
  326. rleLeftOver = 255;
  327. }
  328. else
  329. {
  330. int byte1 = input.ReadByte();
  331. if (byte1 == -1)
  332. {
  333. throw new EndOfStreamException();
  334. }
  335. else
  336. {
  337. rle = (byte)byte1;
  338. }
  339. }
  340. if ((rle & 128) != 0)
  341. {
  342. // RLE Encoded packet
  343. rle -= 127; // calculate real repeat count
  344. if ((x + rle) > width)
  345. {
  346. rleLeftOver = (byte)(128 + (rle - (width - x) - 1));
  347. filePos = input.Position;
  348. rle = (byte)(width - x);
  349. }
  350. ColorBgra color = ReadColor(input, header.pixelDepth, palette);
  351. for (int ix = 0; ix < rle; ++ix)
  352. {
  353. int index = dstIndex + (ix * ColorBgra.SizeOf);
  354. dst[index] = color[0];
  355. dst[1 + index] = color[1];
  356. dst[2 + index] = color[2];
  357. dst[3 + index] = color[3];
  358. }
  359. if (rleLeftOver != 255)
  360. {
  361. input.Position = filePos;
  362. }
  363. }
  364. else
  365. {
  366. // Raw packet
  367. rle += 1; // calculate real repeat count
  368. if ((x + rle) > width)
  369. {
  370. rleLeftOver = (byte)(rle - (width - x) - 1);
  371. rle = (byte)(width - x);
  372. }
  373. ExpandUncompressedLine(dst, dstIndex, ref header, input, rle, y, x, palette);
  374. }
  375. dstIndex += rle * ColorBgra.SizeOf;
  376. x += rle;
  377. }
  378. return rleLeftOver;
  379. }
  380. private void ExpandUncompressedLine(MemoryBlock dst, int dstIndex, ref TgaHeader header, Stream input, int width, int y, int xoffset, ColorBgra[] palette)
  381. {
  382. for (int i = 0; i < width; ++i)
  383. {
  384. ColorBgra color = ReadColor(input, header.pixelDepth, palette);
  385. dst[dstIndex] = color[0];
  386. dst[1 + dstIndex] = color[1];
  387. dst[2 + dstIndex] = color[2];
  388. dst[3 + dstIndex] = color[3];
  389. dstIndex += 4;
  390. }
  391. }
  392. private ColorBgra ReadColor(Stream input, int pixelDepth, ColorBgra[] palette)
  393. {
  394. ColorBgra color;
  395. switch (pixelDepth)
  396. {
  397. case 32:
  398. {
  399. long colorInt = Utility.ReadUInt32(input);
  400. if (colorInt == -1)
  401. {
  402. throw new EndOfStreamException();
  403. }
  404. color = ColorBgra.FromUInt32((uint)colorInt);
  405. break;
  406. }
  407. case 24:
  408. {
  409. int colorInt = Utility.ReadUInt24(input);
  410. if (colorInt == -1)
  411. {
  412. throw new EndOfStreamException();
  413. }
  414. color = ColorBgra.FromUInt32((uint)colorInt);
  415. color.A = 255;
  416. break;
  417. }
  418. case 15:
  419. case 16:
  420. {
  421. int colorWord = Utility.ReadUInt16(input);
  422. if (colorWord == -1)
  423. {
  424. throw new EndOfStreamException();
  425. }
  426. color = ColorBgra.FromBgra(
  427. (byte)((colorWord >> 7) & 0xf8),
  428. (byte)((colorWord >> 2) & 0xf8),
  429. (byte)((colorWord & 0x1f) * 8),
  430. 255);
  431. break;
  432. }
  433. case 8:
  434. {
  435. int colorByte = input.ReadByte();
  436. if (colorByte == -1)
  437. {
  438. throw new EndOfStreamException();
  439. }
  440. if (colorByte >= palette.Length)
  441. {
  442. throw new FormatException("color index was outside the bounds of the palette");
  443. }
  444. color = palette[colorByte];
  445. break;
  446. }
  447. default:
  448. throw new FormatException("colorDepth was not one of {8, 15, 16, 24, 32}");
  449. }
  450. return color;
  451. }
  452. private void SaveTga(Surface input, Stream output, SavableBitDepths bitDepth, bool rleCompress, ProgressEventHandler progressCallback)
  453. {
  454. TgaHeader header = new TgaHeader();
  455. header.idLength = 0;
  456. header.cmapType = 0;
  457. header.imageType = rleCompress ? TgaType.RleRgb : TgaType.Rgb;
  458. header.cmapIndex = 0;
  459. header.cmapLength = 0;
  460. header.cmapEntrySize = 0; // if bpp=8, set this to 24
  461. header.xOrigin = 0;
  462. header.yOrigin = 0;
  463. header.imageWidth = (ushort)input.Width;
  464. header.imageHeight = (ushort)input.Height;
  465. header.imageDesc = 0;
  466. switch (bitDepth)
  467. {
  468. case SavableBitDepths.Rgba32:
  469. header.pixelDepth = 32;
  470. header.imageDesc |= 8;
  471. break;
  472. case SavableBitDepths.Rgb24:
  473. header.pixelDepth = 24;
  474. break;
  475. default:
  476. throw new InvalidEnumArgumentException("bitDepth", (int)bitDepth, typeof(SavableBitDepths));
  477. }
  478. header.Write(output);
  479. // write palette if doing 8-bit
  480. // ... todo?
  481. for (int y = input.Height - 1; y >= 0; --y)
  482. {
  483. // non-rle output
  484. if (rleCompress)
  485. {
  486. SaveTgaRowRle(output, input, ref header, y);
  487. }
  488. else
  489. {
  490. SaveTgaRowRaw(output, input, ref header, y);
  491. }
  492. if (progressCallback != null)
  493. {
  494. progressCallback(this, new ProgressEventArgs(100.0 * ((double)(input.Height - y) / (double)input.Height)));
  495. }
  496. }
  497. }
  498. private class TgaPacketStateMachine
  499. {
  500. private bool rlePacket = false;
  501. private ColorBgra[] packetColors = new ColorBgra[128];
  502. private int packetLength;
  503. private Stream output;
  504. private int bitDepth;
  505. public void Flush()
  506. {
  507. byte header = (byte)((rlePacket ? 128 : 0) + (byte)(packetLength - 1));
  508. output.WriteByte(header);
  509. int length = (rlePacket ? 1 : packetLength);
  510. for (int i = 0; i < length; ++i)
  511. {
  512. WriteColor(this.output, packetColors[i], this.bitDepth);
  513. }
  514. packetLength = 0;
  515. }
  516. public void Push(ColorBgra color)
  517. {
  518. if (packetLength == 0)
  519. {
  520. // Starting a fresh packet.
  521. rlePacket = false;
  522. packetColors[0] = color;
  523. packetLength = 1;
  524. }
  525. else if (packetLength == 1)
  526. {
  527. // 2nd byte of this packet... decide RLE or non-RLE.
  528. rlePacket = (color == packetColors[0]);
  529. packetColors[1] = color;
  530. packetLength = 2;
  531. }
  532. else if (packetLength == packetColors.Length)
  533. {
  534. // Packet is full. Start a new one.
  535. Flush();
  536. Push(color);
  537. }
  538. else if (packetLength >= 2 && rlePacket && color != packetColors[packetLength - 1])
  539. {
  540. // We were filling in an RLE packet, and we got a non-repeated color.
  541. // Emit the current packet and start a new one.
  542. Flush();
  543. Push(color);
  544. }
  545. else if (packetLength >= 2 && rlePacket && color == packetColors[packetLength - 1])
  546. {
  547. // We are filling in an RLE packet, and we got another repeated color.
  548. // Add the new color to the current packet.
  549. ++packetLength;
  550. packetColors[packetLength - 1] = color;
  551. }
  552. else if (packetLength >= 2 && !rlePacket && color != packetColors[packetLength - 1])
  553. {
  554. // We are filling in a raw packet, and we got another random color.
  555. // Add the new color to the current packet.
  556. ++packetLength;
  557. packetColors[packetLength - 1] = color;
  558. }
  559. else if (packetLength >= 2 && !rlePacket && color == packetColors[packetLength - 1])
  560. {
  561. // We were filling in a raw packet, but we got a repeated color.
  562. // Emit the current packet without its last color, and start a
  563. // new RLE packet that starts with a length of 2.
  564. --packetLength;
  565. Flush();
  566. Push(color);
  567. Push(color);
  568. }
  569. }
  570. public TgaPacketStateMachine(Stream output, int bitDepth)
  571. {
  572. this.output = output;
  573. this.bitDepth = bitDepth;
  574. }
  575. }
  576. private static void SaveTgaRowRle(Stream output, Surface input, ref TgaHeader header, int y)
  577. {
  578. TgaPacketStateMachine machine = new TgaPacketStateMachine(output, header.pixelDepth);
  579. for (int x = 0; x < input.Width; ++x)
  580. {
  581. machine.Push(input[x, y]);
  582. }
  583. machine.Flush();
  584. }
  585. private static void SaveTgaRowRaw(Stream output, Surface input, ref TgaHeader header, int y)
  586. {
  587. for (int x = 0; x < input.Width; ++x)
  588. {
  589. ColorBgra color = input[x, y];
  590. WriteColor(output, color, header.pixelDepth);
  591. }
  592. }
  593. private static void WriteColor(Stream output, ColorBgra color, int bitDepth)
  594. {
  595. switch (bitDepth)
  596. {
  597. case 24:
  598. {
  599. int red = ((color.R * color.A) + (255 * (255 - color.A))) / 255;
  600. int green = ((color.G * color.A) + (255 * (255 - color.A))) / 255;
  601. int blue = ((color.B * color.A) + (255 * (255 - color.A))) / 255;
  602. int colorInt = blue + (green << 8) + (red << 16);
  603. Utility.WriteUInt24(output, colorInt);
  604. break;
  605. }
  606. case 32:
  607. Utility.WriteUInt32(output, color.Bgra);
  608. break;
  609. }
  610. }
  611. public TgaFileType()
  612. : base("TGA",
  613. FileTypeFlags.SavesWithProgress |
  614. FileTypeFlags.SupportsLoading |
  615. FileTypeFlags.SupportsSaving,
  616. new string[] { ".tga" })
  617. {
  618. }
  619. }
  620. }