No Description
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.

RleWriter.cs 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Photoshop PSD FileType Plugin for Paint.NET
  4. // http://psdplugin.codeplex.com/
  5. //
  6. // This software is provided under the MIT License:
  7. // Copyright (c) 2006-2007 Frank Blumenberg
  8. // Copyright (c) 2010-2015 Tao Yue
  9. //
  10. // Portions of this file are provided under the BSD 3-clause License:
  11. // Copyright (c) 2006, Jonas Beckeman
  12. //
  13. // See LICENSE.txt for complete licensing and attribution information.
  14. //
  15. /////////////////////////////////////////////////////////////////////////////////
  16. using System;
  17. using System.Diagnostics;
  18. using System.IO;
  19. using UnityEngine.Assertions;
  20. namespace PhotoshopFile
  21. {
  22. internal class RleWriter
  23. {
  24. private int maxPacketLength = 128;
  25. // Current task
  26. private object rleLock;
  27. private Stream stream;
  28. private byte[] data;
  29. private int offset;
  30. // Current packet
  31. private bool isRepeatPacket;
  32. private int idxPacketStart;
  33. private int packetLength;
  34. private byte runValue;
  35. private int runLength;
  36. public RleWriter(Stream stream)
  37. {
  38. rleLock = new object();
  39. this.stream = stream;
  40. }
  41. /// <summary>
  42. /// Encodes byte data using PackBits RLE compression.
  43. /// </summary>
  44. /// <param name="data">Raw data to be encoded.</param>
  45. /// <param name="offset">Offset at which to begin transferring data.</param>
  46. /// <param name="count">Number of bytes of data to transfer.</param>
  47. /// <returns>Number of compressed bytes written to the stream.</returns>
  48. /// <remarks>
  49. /// There are multiple ways to encode two-byte runs:
  50. /// 1. Apple PackBits only encodes three-byte runs as repeats.
  51. /// 2. Adobe Photoshop encodes two-byte runs as repeats, unless preceded
  52. /// by literals.
  53. /// 3. TIFF PackBits recommends that two-byte runs be encoded as repeats,
  54. /// unless preceded *and* followed by literals.
  55. ///
  56. /// This class adopts the Photoshop behavior, as it has slightly better
  57. /// compression efficiency than Apple PackBits, and is easier to implement
  58. /// than TIFF PackBits.
  59. /// </remarks>
  60. public int Write(byte[] data, int offset, int count)
  61. {
  62. if (!Util.CheckBufferBounds(data, offset, count))
  63. throw new ArgumentOutOfRangeException();
  64. // We cannot encode a count of 0, because the PackBits flag-counter byte
  65. // uses 0 to indicate a length of 1.
  66. if (count == 0)
  67. {
  68. throw new ArgumentOutOfRangeException("count");
  69. }
  70. lock (rleLock)
  71. {
  72. var startPosition = stream.Position;
  73. this.data = data;
  74. this.offset = offset;
  75. //fixed (byte* ptrData = &data[0])
  76. {
  77. //byte* ptr = ptrData + offset;
  78. //byte* ptrEnd = ptr + count;
  79. //var bytesEncoded = EncodeToStream(ptr, ptrEnd);
  80. //Debug.Assert(bytesEncoded == count, "Encoded byte count should match the argument.");
  81. var bytesEncoded = EncodeToStream(data, offset, offset + count);
  82. Assert.AreEqual(bytesEncoded, count, "Encoded byte count should match the argument.");
  83. }
  84. return (int)(stream.Position - startPosition);
  85. }
  86. }
  87. private void ClearPacket()
  88. {
  89. this.isRepeatPacket = false;
  90. this.packetLength = 0;
  91. }
  92. private void WriteRepeatPacket(int length)
  93. {
  94. var header = unchecked((byte)(1 - length));
  95. stream.WriteByte(header);
  96. stream.WriteByte(runValue);
  97. }
  98. private void WriteLiteralPacket(int length)
  99. {
  100. var header = unchecked((byte)(length - 1));
  101. stream.WriteByte(header);
  102. stream.Write(data, idxPacketStart, length);
  103. }
  104. private void WritePacket()
  105. {
  106. if (isRepeatPacket)
  107. WriteRepeatPacket(packetLength);
  108. else
  109. WriteLiteralPacket(packetLength);
  110. }
  111. private void StartPacket(int count,
  112. bool isRepeatPacket, int runLength, byte value)
  113. {
  114. this.isRepeatPacket = isRepeatPacket;
  115. this.packetLength = runLength;
  116. this.runLength = runLength;
  117. this.runValue = value;
  118. this.idxPacketStart = offset + count;
  119. }
  120. private void ExtendPacketAndRun(byte value)
  121. {
  122. packetLength++;
  123. runLength++;
  124. }
  125. private void ExtendPacketStartNewRun(byte value)
  126. {
  127. packetLength++;
  128. runLength = 1;
  129. runValue = value;
  130. }
  131. private int EncodeToStream(byte[] ptr, int start, int end /*byte* ptr, byte* ptrEnd*/)
  132. {
  133. // Begin the first packet.
  134. StartPacket(0, false, 1, ptr[start]);
  135. int numBytesEncoded = 1;
  136. start++;
  137. // Loop invariant: Packet is never empty.
  138. while (start < end)
  139. {
  140. var value = ptr[start];
  141. if (packetLength == 1)
  142. {
  143. isRepeatPacket = (value == runValue);
  144. if (isRepeatPacket)
  145. ExtendPacketAndRun(value);
  146. else
  147. ExtendPacketStartNewRun(value);
  148. }
  149. else if (packetLength == maxPacketLength)
  150. {
  151. // Packet is full, so write it out and start a new one.
  152. WritePacket();
  153. StartPacket(numBytesEncoded, false, 1, value);
  154. }
  155. else if (isRepeatPacket)
  156. {
  157. // Decide whether to continue the repeat packet.
  158. if (value == runValue)
  159. ExtendPacketAndRun(value);
  160. else
  161. {
  162. // Different color, so terminate the run and start a new packet.
  163. WriteRepeatPacket(packetLength);
  164. StartPacket(numBytesEncoded, false, 1, value);
  165. }
  166. }
  167. else
  168. {
  169. // Decide whether to continue the literal packet.
  170. if (value == runValue)
  171. {
  172. ExtendPacketAndRun(value);
  173. // A 3-byte run terminates the literal and starts a new repeat
  174. // packet. That's because the 3-byte run can be encoded as a
  175. // 2-byte repeat. So even if the run ends at 3, we've already
  176. // paid for the next flag-counter byte.
  177. if (runLength == 3)
  178. {
  179. // The 3-byte run can come in the middle of a literal packet,
  180. // but not at the beginning. The first 2 bytes of the run
  181. // should've triggered a repeat packet.
  182. Debug.Assert(packetLength > 3);
  183. // -2 because numBytesEncoded has not yet been incremented
  184. WriteLiteralPacket(packetLength - 3);
  185. StartPacket(numBytesEncoded - 2, true, 3, value);
  186. }
  187. }
  188. else
  189. {
  190. ExtendPacketStartNewRun(value);
  191. }
  192. }
  193. start++;
  194. numBytesEncoded++;
  195. }
  196. // Loop terminates with a non-empty packet waiting to be written out.
  197. WritePacket();
  198. ClearPacket();
  199. return numBytesEncoded;
  200. }
  201. }
  202. }