RleWriter.cs 6.4 KB

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