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)
{
}
}
}