internal SavableBitDepths ChooseBitDepth( Set <SavableBitDepths> allowedBitDepths, Set <SavableBitDepths> 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 <SavableBitDepths> bestBitDepths = Set <SavableBitDepths> .Intersect(allowedBitDepths, losslessBitDepths); if (bestBitDepths.Count == 1) { return(bestBitDepths.ToArray()[0]); } Set <SavableBitDepths> 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 override sealed void OnSaveT( Document input, Stream output, PropertyBasedSaveConfigToken token, Surface scratchSurface, ProgressEventHandler progressCallback) { // flatten the document -- render w/ transparent background scratchSurface.Clear(ColorBgra.Transparent); using (RenderArgs ra = new RenderArgs(scratchSurface)) { input.Render(ra, false); } // load properties from token int thresholdFromToken = GetThresholdFromToken(token); int ditherLevel = GetDitherLevelFromToken(token); Set <SavableBitDepths> allowedBitDepths = CreateAllowedBitDepthListFromToken(token); if (allowedBitDepths.Count == 0) { throw new ArgumentException("there must be at least 1 element returned from CreateAllowedBitDepthListFromToken()"); } // allowedBitDepths.Count >= 1 // set to 1 unless allowedBitDepths contains only Rgb8 and Rgba8 int threshold; if (allowedBitDepths.IsSubsetOf(Set.Create(SavableBitDepths.Rgb8, SavableBitDepths.Rgba8))) { threshold = thresholdFromToken; } else { threshold = 1; } // Analyze image, try to detect what bit-depth or whatever to use, based on allowedBitDepths bool allOpaque; bool all0or255Alpha; int uniqueColorCount; Analyze(scratchSurface, out allOpaque, out all0or255Alpha, out uniqueColorCount); Set <SavableBitDepths> losslessBitDepths = new Set <SavableBitDepths>(); losslessBitDepths.Add(SavableBitDepths.Rgba32); if (allOpaque) { losslessBitDepths.Add(SavableBitDepths.Rgb24); if (uniqueColorCount <= 256) { losslessBitDepths.Add(SavableBitDepths.Rgb8); } } else if (all0or255Alpha && uniqueColorCount < 256) { losslessBitDepths.Add(SavableBitDepths.Rgba8); } SavableBitDepths bitDepth = ChooseBitDepth(allowedBitDepths, losslessBitDepths, allOpaque, all0or255Alpha, uniqueColorCount); if (bitDepth == SavableBitDepths.Rgba8 && threshold == 0 && allowedBitDepths.Contains(SavableBitDepths.Rgba8) && allowedBitDepths.Contains(SavableBitDepths.Rgb8)) { // threshold of 0 should effectively force full 256 color palette, instead of 255+1 transparent bitDepth = SavableBitDepths.Rgb8; } // if bit depth is 24 or 8, then we have to do away with the alpha channel // for 8-bit, we must have pixels that have either 0 or 255 alpha if (bitDepth == SavableBitDepths.Rgb8 || bitDepth == SavableBitDepths.Rgba8 || bitDepth == SavableBitDepths.Rgb24) { UserBlendOps.NormalBlendOp blendOp = new UserBlendOps.NormalBlendOp(); for (int y = 0; y < scratchSurface.Height; ++y) { for (int x = 0; x < scratchSurface.Width; ++x) { ColorBgra p = scratchSurface[x, y]; if (p.A < threshold && bitDepth == SavableBitDepths.Rgba8) { p = ColorBgra.FromBgra(0, 0, 0, 0); } else { p = blendOp.Apply(ColorBgra.White, p); } scratchSurface[x, y] = p; } } } Tracing.Ping("Chose " + bitDepth + ", ditherLevel=" + ditherLevel + ", threshold=" + threshold); // finally, do the save. FinalSave(input, output, scratchSurface, ditherLevel, bitDepth, token, progressCallback); }