RleReader.cs 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  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-2019 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 RleReader
  22. {
  23. private Stream stream;
  24. public RleReader(Stream stream)
  25. {
  26. this.stream = stream;
  27. }
  28. /// <summary>
  29. /// Decodes a PackBits RLE stream.
  30. /// </summary>
  31. /// <param name="buffer">Output buffer for decoded data.</param>
  32. /// <param name="offset">Offset at which to begin writing.</param>
  33. /// <param name="count">Number of bytes to decode from the stream.</param>
  34. unsafe public int Read(byte[] buffer, int offset, int count)
  35. {
  36. if (!Util.CheckBufferBounds(buffer, offset, count))
  37. {
  38. throw new ArgumentOutOfRangeException();
  39. }
  40. if (count == 0)
  41. {
  42. return 0;
  43. }
  44. // Pin the entire buffer now, so that we don't keep pinning and unpinning
  45. // for each RLE packet.
  46. fixed (byte* ptrBuffer = &buffer[0])
  47. {
  48. int bytesLeft = count;
  49. int bufferIdx = offset;
  50. while (bytesLeft > 0)
  51. {
  52. // ReadByte returns an unsigned byte, but we want a signed byte.
  53. var flagCounter = unchecked((sbyte)stream.ReadByte());
  54. // Raw packet
  55. if (flagCounter > 0)
  56. {
  57. var readLength = flagCounter + 1;
  58. if (bytesLeft < readLength)
  59. throw new RleException("Raw packet overruns the decode window.");
  60. stream.Read(buffer, bufferIdx, readLength);
  61. bufferIdx += readLength;
  62. bytesLeft -= readLength;
  63. }
  64. // RLE packet
  65. else if (flagCounter > -128)
  66. {
  67. var runLength = 1 - flagCounter;
  68. var byteValue = (byte)stream.ReadByte();
  69. if (runLength > bytesLeft)
  70. throw new RleException("RLE packet overruns the decode window.");
  71. byte* ptr = ptrBuffer + bufferIdx;
  72. byte* ptrEnd = ptr + runLength;
  73. while (ptr < ptrEnd)
  74. {
  75. *ptr = byteValue;
  76. ptr++;
  77. }
  78. bufferIdx += runLength;
  79. bytesLeft -= runLength;
  80. }
  81. else
  82. {
  83. // The canonical PackBits algorithm will never emit 0x80 (-128), but
  84. // some programs do. Simply skip over the byte.
  85. }
  86. }
  87. Debug.Assert(bytesLeft == 0);
  88. return count - bytesLeft;
  89. }
  90. }
  91. }
  92. }