InternalFileType.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. using PaintDotNet.SystemLayer;
  2. using System;
  3. using System.Drawing;
  4. using System.Drawing.Imaging;
  5. using System.IO;
  6. using System.Text;
  7. namespace PaintDotNet
  8. {
  9. public abstract class InternalFileType : FileType
  10. {
  11. /// <summary>
  12. /// The actual bit-depths we can save with.
  13. /// </summary>
  14. internal enum SavableBitDepths
  15. {
  16. Rgba32, // 2^24 colors, plus a full 8-bit alpha channel
  17. Rgb24, // 2^24 colors
  18. Rgb8, // 256 colors
  19. Rgba8 // 255 colors + 1 transparent
  20. }
  21. protected unsafe void SquishSurfaceTo24Bpp(Surface surface)
  22. {
  23. byte* dst = (byte*)surface.GetRowAddress(0);
  24. int byteWidth = surface.Width * 3;
  25. int stride24bpp = ((byteWidth + 3) / 4) * 4; // round up to multiple of 4
  26. int delta = stride24bpp - byteWidth;
  27. for (int y = 0; y < surface.Height; ++y)
  28. {
  29. ColorBgra* src = surface.GetRowAddress(y);
  30. ColorBgra* srcEnd = src + surface.Width;
  31. while (src < srcEnd)
  32. {
  33. dst[0] = src->B;
  34. dst[1] = src->G;
  35. dst[2] = src->R;
  36. ++src;
  37. dst += 3;
  38. }
  39. dst += delta;
  40. }
  41. return;
  42. }
  43. private string PrintSet<T>(Set<T> set)
  44. {
  45. StringBuilder sb = new StringBuilder();
  46. bool first = true;
  47. foreach (T item in set)
  48. {
  49. if (!first)
  50. {
  51. sb.Append(", ");
  52. }
  53. first = false;
  54. sb.Append(item.ToString());
  55. }
  56. string sbTS = sb.ToString();
  57. return sbTS;
  58. }
  59. internal SavableBitDepths ChooseBitDepth(
  60. Set<SavableBitDepths> allowedBitDepths,
  61. Set<SavableBitDepths> losslessBitDepths,
  62. bool allOpaque,
  63. bool all0Or255Alpha,
  64. int uniqueColorCount)
  65. {
  66. if (allowedBitDepths.Count == 0)
  67. {
  68. throw new ArgumentException("Count must be 1 or more", "allowedBitDepths");
  69. }
  70. Tracing.Ping("allowedBitDepths = " + PrintSet(allowedBitDepths));
  71. Tracing.Ping("losslessBitDepths = " + PrintSet(losslessBitDepths));
  72. if (allowedBitDepths.Count == 1)
  73. {
  74. return allowedBitDepths.ToArray()[0];
  75. }
  76. // allowedBitDepths.Count >= 2
  77. Set<SavableBitDepths> bestBitDepths = Set<SavableBitDepths>.Intersect(allowedBitDepths, losslessBitDepths);
  78. if (bestBitDepths.Count == 1)
  79. {
  80. return bestBitDepths.ToArray()[0];
  81. }
  82. Set<SavableBitDepths> candidates;
  83. if (bestBitDepths.Count == 0)
  84. {
  85. candidates = allowedBitDepths;
  86. }
  87. else
  88. {
  89. candidates = bestBitDepths;
  90. }
  91. // candidates.Count >= 2
  92. // lossless choices
  93. if (candidates.Contains(SavableBitDepths.Rgba8) && all0Or255Alpha && uniqueColorCount <= 255)
  94. {
  95. return SavableBitDepths.Rgba8;
  96. }
  97. if (candidates.Contains(SavableBitDepths.Rgb8) && allOpaque && uniqueColorCount <= 256)
  98. {
  99. return SavableBitDepths.Rgb8;
  100. }
  101. if (candidates.Contains(SavableBitDepths.Rgb24) && allOpaque)
  102. {
  103. return SavableBitDepths.Rgb24;
  104. }
  105. if (candidates.Contains(SavableBitDepths.Rgba32))
  106. {
  107. return SavableBitDepths.Rgba32;
  108. }
  109. // forced choices -- we wanted Rgba32 but it was not allowed
  110. if (candidates.IsEqualTo(Set.Create(SavableBitDepths.Rgb8, SavableBitDepths.Rgb24)))
  111. {
  112. return SavableBitDepths.Rgb24;
  113. }
  114. if (candidates.IsEqualTo(Set.Create(SavableBitDepths.Rgb8, SavableBitDepths.Rgba8)))
  115. {
  116. return SavableBitDepths.Rgba8;
  117. }
  118. if (candidates.IsEqualTo(Set.Create(SavableBitDepths.Rgba8, SavableBitDepths.Rgb24)))
  119. {
  120. return SavableBitDepths.Rgb24;
  121. }
  122. throw new ArgumentException("Could not accomodate input values -- internal error?");
  123. }
  124. protected unsafe Bitmap CreateAliased24BppBitmap(Surface surface)
  125. {
  126. int stride = surface.Width * 3;
  127. int realStride = ((stride + 3) / 4) * 4; // round up to multiple of 4
  128. return new Bitmap(surface.Width, surface.Height, realStride, PixelFormat.Format24bppRgb, new IntPtr(surface.Scan0.VoidStar));
  129. }
  130. private unsafe void Analyze(Surface scratchSurface, out bool allOpaque, out bool all0or255Alpha, out int uniqueColorCount)
  131. {
  132. allOpaque = true;
  133. all0or255Alpha = true;
  134. Set<ColorBgra> uniqueColors = new Set<ColorBgra>();
  135. for (int y = 0; y < scratchSurface.Height; ++y)
  136. {
  137. ColorBgra* srcPtr = scratchSurface.GetRowAddress(y);
  138. ColorBgra* endPtr = srcPtr + scratchSurface.Width;
  139. while (srcPtr < endPtr)
  140. {
  141. ColorBgra p = *srcPtr;
  142. if (p.A != 255)
  143. {
  144. allOpaque = false;
  145. }
  146. if (p.A > 0 && p.A < 255)
  147. {
  148. all0or255Alpha = false;
  149. }
  150. if (p.A == 255 && !uniqueColors.Contains(p) && uniqueColors.Count < 300)
  151. {
  152. uniqueColors.Add(*srcPtr);
  153. }
  154. ++srcPtr;
  155. }
  156. }
  157. uniqueColorCount = uniqueColors.Count;
  158. }
  159. internal InternalFileType(string name, FileTypeFlags flags, string[] extensions)
  160. : base(name, flags, extensions)
  161. {
  162. }
  163. }
  164. }