using PaintDotNet.SystemLayer; using System; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Text; namespace PaintDotNet { public abstract class InternalFileType : FileType { /// /// The actual bit-depths we can save with. /// internal enum SavableBitDepths { Rgba32, // 2^24 colors, plus a full 8-bit alpha channel Rgb24, // 2^24 colors Rgb8, // 256 colors Rgba8 // 255 colors + 1 transparent } protected unsafe void SquishSurfaceTo24Bpp(Surface surface) { byte* dst = (byte*)surface.GetRowAddress(0); int byteWidth = surface.Width * 3; int stride24bpp = ((byteWidth + 3) / 4) * 4; // round up to multiple of 4 int delta = stride24bpp - byteWidth; for (int y = 0; y < surface.Height; ++y) { ColorBgra* src = surface.GetRowAddress(y); ColorBgra* srcEnd = src + surface.Width; while (src < srcEnd) { dst[0] = src->B; dst[1] = src->G; dst[2] = src->R; ++src; dst += 3; } dst += delta; } return; } private string PrintSet(Set set) { StringBuilder sb = new StringBuilder(); bool first = true; foreach (T item in set) { if (!first) { sb.Append(", "); } first = false; sb.Append(item.ToString()); } string sbTS = sb.ToString(); return sbTS; } internal SavableBitDepths ChooseBitDepth( Set allowedBitDepths, Set losslessBitDepths, bool allOpaque, bool all0Or255Alpha, int uniqueColorCount) { if (allowedBitDepths.Count == 0) { throw new ArgumentException("Count must be 1 or more", "allowedBitDepths"); } Tracing.Ping("allowedBitDepths = " + PrintSet(allowedBitDepths)); Tracing.Ping("losslessBitDepths = " + PrintSet(losslessBitDepths)); if (allowedBitDepths.Count == 1) { return allowedBitDepths.ToArray()[0]; } // allowedBitDepths.Count >= 2 Set bestBitDepths = Set.Intersect(allowedBitDepths, losslessBitDepths); if (bestBitDepths.Count == 1) { return bestBitDepths.ToArray()[0]; } Set candidates; if (bestBitDepths.Count == 0) { candidates = allowedBitDepths; } else { candidates = bestBitDepths; } // candidates.Count >= 2 // lossless choices if (candidates.Contains(SavableBitDepths.Rgba8) && all0Or255Alpha && uniqueColorCount <= 255) { return SavableBitDepths.Rgba8; } if (candidates.Contains(SavableBitDepths.Rgb8) && allOpaque && uniqueColorCount <= 256) { return SavableBitDepths.Rgb8; } if (candidates.Contains(SavableBitDepths.Rgb24) && allOpaque) { return SavableBitDepths.Rgb24; } if (candidates.Contains(SavableBitDepths.Rgba32)) { return SavableBitDepths.Rgba32; } // forced choices -- we wanted Rgba32 but it was not allowed if (candidates.IsEqualTo(Set.Create(SavableBitDepths.Rgb8, SavableBitDepths.Rgb24))) { return SavableBitDepths.Rgb24; } if (candidates.IsEqualTo(Set.Create(SavableBitDepths.Rgb8, SavableBitDepths.Rgba8))) { return SavableBitDepths.Rgba8; } if (candidates.IsEqualTo(Set.Create(SavableBitDepths.Rgba8, SavableBitDepths.Rgb24))) { return SavableBitDepths.Rgb24; } throw new ArgumentException("Could not accomodate input values -- internal error?"); } protected unsafe Bitmap CreateAliased24BppBitmap(Surface surface) { int stride = surface.Width * 3; int realStride = ((stride + 3) / 4) * 4; // round up to multiple of 4 return new Bitmap(surface.Width, surface.Height, realStride, PixelFormat.Format24bppRgb, new IntPtr(surface.Scan0.VoidStar)); } private unsafe void Analyze(Surface scratchSurface, out bool allOpaque, out bool all0or255Alpha, out int uniqueColorCount) { allOpaque = true; all0or255Alpha = true; Set uniqueColors = new Set(); for (int y = 0; y < scratchSurface.Height; ++y) { ColorBgra* srcPtr = scratchSurface.GetRowAddress(y); ColorBgra* endPtr = srcPtr + scratchSurface.Width; while (srcPtr < endPtr) { ColorBgra p = *srcPtr; if (p.A != 255) { allOpaque = false; } if (p.A > 0 && p.A < 255) { all0or255Alpha = false; } if (p.A == 255 && !uniqueColors.Contains(p) && uniqueColors.Count < 300) { uniqueColors.Add(*srcPtr); } ++srcPtr; } } uniqueColorCount = uniqueColors.Count; } internal InternalFileType(string name, FileTypeFlags flags, string[] extensions) : base(name, flags, extensions) { } } }