internal override void FinalSave( Document input, Stream output, Surface scratchSurface, int ditherLevel, SavableBitDepths bitDepth, PropertyBasedSaveConfigToken token, ProgressEventHandler progressCallback) { bool enableAlpha; switch (bitDepth) { case SavableBitDepths.Rgb8: enableAlpha = false; break; case SavableBitDepths.Rgba8: enableAlpha = true; break; default: throw new InvalidEnumArgumentException("bitDepth", (int)bitDepth, typeof(SavableBitDepths)); } using (Bitmap quantized = Quantize(scratchSurface, ditherLevel, 256, enableAlpha, progressCallback)) { quantized.Save(output, ImageFormat.Gif); } }
private SavableBitDepths GetBitDepth(Surface scratchSurface, Rectangle bounds) { bool allOpaque; bool all0or255Alpha; int uniqueColorCount; Analyze(scratchSurface, bounds, out allOpaque, out all0or255Alpha, out uniqueColorCount); SavableBitDepths bitDepth = SavableBitDepths.Rgba32; if (allOpaque) { bitDepth = SavableBitDepths.Rgb24; if (uniqueColorCount <= 256) { bitDepth = SavableBitDepths.Rgb8; } } else if (all0or255Alpha && uniqueColorCount < 256) { bitDepth = SavableBitDepths.Rgba8; } // 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(); unsafe { for (int y = bounds.Top; y < bounds.Bottom; ++y) { ColorBgra *srcPtr = scratchSurface.GetPointAddressUnchecked(bounds.Left, y); ColorBgra *endPtr = srcPtr + bounds.Width; while (srcPtr < endPtr) { if (srcPtr->A < 128 && bitDepth == SavableBitDepths.Rgba8) { *srcPtr = ColorBgra.FromBgra(0, 0, 0, 0); } else { *srcPtr = blendOp.Apply(ColorBgra.White, *srcPtr); } srcPtr++; } } } } return(bitDepth); }
internal abstract void FinalSave( Document input, Stream output, Surface scratchSurface, int ditherLevel, SavableBitDepths bitDepth, PropertyBasedSaveConfigToken token, ProgressEventHandler progressCallback);
protected sealed override void OnSaveT(Document input, Stream output, PropertyBasedSaveConfigToken token, Surface scratchSurface, ProgressEventHandler progressCallback) { try { int num3; bool flag; bool flag2; int num4; int thresholdFromToken = this.GetThresholdFromToken(token); int ditherLevelFromToken = this.GetDitherLevelFromToken(token); HashSet <SavableBitDepths> allowedBitDepths = this.CreateAllowedBitDepthListFromToken(token); if (allowedBitDepths.Count == 0) { throw new ArgumentException("there must be at least 1 element returned from CreateAllowedBitDepthListFromToken()"); } SavableBitDepths[] items = new SavableBitDepths[] { SavableBitDepths.Rgb8, SavableBitDepths.Rgba8 }; if (allowedBitDepths.IsSubsetOf(HashSetUtil.Create <SavableBitDepths>(items))) { num3 = thresholdFromToken; } else { num3 = 1; } this.RenderFlattenedDocument(input, scratchSurface); this.Analyze(scratchSurface, out flag, out flag2, out num4); HashSet <SavableBitDepths> losslessBitDepths = new HashSet <SavableBitDepths> { SavableBitDepths.Rgba32 }; if (flag) { losslessBitDepths.Add(SavableBitDepths.Rgb24); if (num4 <= 0x100) { losslessBitDepths.Add(SavableBitDepths.Rgb8); } } else if (flag2 && (num4 < 0x100)) { losslessBitDepths.Add(SavableBitDepths.Rgba8); } double chooseBitDepthProgressLast = 0.0; ProgressEventHandler handler = delegate(object sender, ProgressEventArgs e) { chooseBitDepthProgressLast = e.Percent; progressCallback(sender, e); }; SavableBitDepths bitDepth = this.ChooseBitDepth(input, scratchSurface, ditherLevelFromToken, num3, token, handler, 0.0, 66.666666666666671, allowedBitDepths, losslessBitDepths, flag, flag2, num4); if (((bitDepth == SavableBitDepths.Rgba8) && (num3 == 0)) && (allowedBitDepths.Contains(SavableBitDepths.Rgba8) && allowedBitDepths.Contains(SavableBitDepths.Rgb8))) { bitDepth = SavableBitDepths.Rgb8; } this.FinalSave(input, output, scratchSurface, ditherLevelFromToken, num3, bitDepth, token, progressCallback, chooseBitDepthProgressLast, 100.0); } finally { } }
private long GetSavableBitDepthFileLength(SavableBitDepths bitDepth, Document input, Surface scratchSurface, int ditherLevel, int threshold, PropertyBasedSaveConfigToken token, ProgressEventHandler progressCallback, double progressStart, double progressEnd) { scratchSurface.Clear(ColorBgra.Zero); using (SegmentedMemoryStream stream = new SegmentedMemoryStream()) { this.FinalSave(input, stream, scratchSurface, ditherLevel, threshold, bitDepth, token, progressCallback, progressStart, progressEnd); stream.Flush(); return(stream.Length); } }
private void SaveTga(Surface input, Stream output, SavableBitDepths bitDepth, bool rleCompress, ProgressEventHandler progressCallback) { TgaHeader header = new TgaHeader(); header.idLength = 0; header.cmapType = 0; header.imageType = rleCompress ? TgaType.RleRgb : TgaType.Rgb; header.cmapIndex = 0; header.cmapLength = 0; header.cmapEntrySize = 0; // if bpp=8, set this to 24 header.xOrigin = 0; header.yOrigin = 0; header.imageWidth = (ushort)input.Width; header.imageHeight = (ushort)input.Height; header.imageDesc = 0; switch (bitDepth) { case SavableBitDepths.Rgba32: header.pixelDepth = 32; header.imageDesc |= 8; break; case SavableBitDepths.Rgb24: header.pixelDepth = 24; break; default: throw new InvalidEnumArgumentException("bitDepth", (int)bitDepth, typeof(SavableBitDepths)); } header.Write(output); // write palette if doing 8-bit // ... todo? for (int y = input.Height - 1; y >= 0; --y) { // non-rle output if (rleCompress) { SaveTgaRowRle(output, input, ref header, y); } else { SaveTgaRowRaw(output, input, ref header, y); } if (progressCallback != null) { progressCallback(this, new ProgressEventArgs(100.0 * ((double)(input.Height - y) / (double)input.Height))); } } }
internal override void FinalSave( Document input, Stream output, Surface scratchSurface, int ditherLevel, SavableBitDepths bitDepth, PropertyBasedSaveConfigToken token, ProgressEventHandler progressCallback) { bool rleCompress = token.GetProperty <BooleanProperty>(PropertyNames.RleCompress).Value; SaveTga(scratchSurface, output, bitDepth, rleCompress, progressCallback); }
internal override void FinalSave( Document input, Stream output, Surface scratchSurface, int ditherLevel, SavableBitDepths bitDepth, PropertyBasedSaveConfigToken token, ProgressEventHandler progressCallback) { // finally, do the save. if (bitDepth == SavableBitDepths.Rgb24) { // In order to save memory, we 'squish' the 32-bit bitmap down to 24-bit in-place // instead of allocating a new bitmap and copying it over. SquishSurfaceTo24Bpp(scratchSurface); ImageCodecInfo icf = GdiPlusFileType.GetImageCodecInfo(ImageFormat.Bmp); EncoderParameters parms = new EncoderParameters(1); EncoderParameter parm = new EncoderParameter(Encoder.ColorDepth, 24); parms.Param[0] = parm; using (Bitmap bitmap = CreateAliased24BppBitmap(scratchSurface)) { GdiPlusFileType.LoadProperties(bitmap, input); bitmap.Save(output, icf, parms); } } else if (bitDepth == SavableBitDepths.Rgb8) { using (Bitmap quantized = Quantize(scratchSurface, ditherLevel, 256, false, progressCallback)) { ImageCodecInfo icf = GdiPlusFileType.GetImageCodecInfo(ImageFormat.Bmp); EncoderParameters parms = new EncoderParameters(1); EncoderParameter parm = new EncoderParameter(Encoder.ColorDepth, 8); parms.Param[0] = parm; GdiPlusFileType.LoadProperties(quantized, input); quantized.Save(output, icf, parms); } } else { throw new InvalidEnumArgumentException("bitDepth", (int)bitDepth, typeof(SavableBitDepths)); } }
internal SavableBitDepths ChooseBitDepth(Document input, Surface scratchSurface, int ditherLevel, int threshold, PropertyBasedSaveConfigToken token, ProgressEventHandler progressCallback, double progressStart, double progressEnd, HashSet <SavableBitDepths> allowedBitDepths, HashSet <SavableBitDepths> losslessBitDepths, bool allOpaque, bool all0Or255Alpha, int uniqueColorCount) { if (allowedBitDepths.Count == 0) { throw new ArgumentException("Count must be 1 or more", "allowedBitDepths"); } try { HashSet <SavableBitDepths> set2; if (allowedBitDepths.Count == 1) { return(allowedBitDepths.First <SavableBitDepths>()); } HashSet <SavableBitDepths> source = HashSetUtil.Intersect <SavableBitDepths>(allowedBitDepths, losslessBitDepths); if (source.Count == 1) { return(source.First <SavableBitDepths>()); } if (source.Count == 0) { set2 = allowedBitDepths; } else { set2 = source; } long num = input.Width * input.Height; if (((all0Or255Alpha && (uniqueColorCount <= 0xff)) && ((num <= 0x10000L) && set2.Contains(SavableBitDepths.Rgb8))) && set2.Contains(SavableBitDepths.Rgb24)) { long num2 = 0L; long num3 = 0L; try { num2 = this.GetSavableBitDepthFileLength(SavableBitDepths.Rgb8, input, scratchSurface, ditherLevel, threshold, token, progressCallback, 0.0, 50.0); num3 = this.GetSavableBitDepthFileLength(SavableBitDepths.Rgb24, input, scratchSurface, ditherLevel, threshold, token, progressCallback, 50.0, 100.0); } catch (OutOfMemoryException) { return(SavableBitDepths.Rgb8); } if (num2 < num3) { return(SavableBitDepths.Rgb8); } return(SavableBitDepths.Rgb24); } if (((all0Or255Alpha && (uniqueColorCount <= 0xff)) && ((num < 0x10000L) && set2.Contains(SavableBitDepths.Rgba8))) && set2.Contains(SavableBitDepths.Rgba32)) { long num4 = 0L; long num5 = 0L; try { num4 = this.GetSavableBitDepthFileLength(SavableBitDepths.Rgba8, input, scratchSurface, ditherLevel, threshold, token, progressCallback, 0.0, 50.0); num5 = this.GetSavableBitDepthFileLength(SavableBitDepths.Rgba32, input, scratchSurface, ditherLevel, threshold, token, progressCallback, 50.0, 100.0); } catch (OutOfMemoryException) { return(SavableBitDepths.Rgba8); } if (num4 < num5) { return(SavableBitDepths.Rgba8); } return(SavableBitDepths.Rgba32); } if ((set2.Contains(SavableBitDepths.Rgb8) & allOpaque) && (uniqueColorCount <= 0x100)) { return(SavableBitDepths.Rgb8); } if ((set2.Contains(SavableBitDepths.Rgba8) & all0Or255Alpha) && (uniqueColorCount <= 0xff)) { return(SavableBitDepths.Rgba8); } if (!(set2.Contains(SavableBitDepths.Rgb24) & allOpaque)) { if (set2.Contains(SavableBitDepths.Rgba32)) { return(SavableBitDepths.Rgba32); } SavableBitDepths[] items = new SavableBitDepths[] { SavableBitDepths.Rgb8, SavableBitDepths.Rgb24 }; if (set2.SetEquals(HashSetUtil.Create <SavableBitDepths>(items))) { return(SavableBitDepths.Rgb24); } SavableBitDepths[] depthsArray2 = new SavableBitDepths[] { SavableBitDepths.Rgb8, SavableBitDepths.Rgba8 }; if (set2.SetEquals(HashSetUtil.Create <SavableBitDepths>(depthsArray2))) { return(SavableBitDepths.Rgba8); } SavableBitDepths[] depthsArray3 = new SavableBitDepths[] { SavableBitDepths.Rgba8, SavableBitDepths.Rgb24 }; if (!set2.SetEquals(HashSetUtil.Create <SavableBitDepths>(depthsArray3))) { throw new ArgumentException("Could not accomodate input values -- internal error?"); } } return(SavableBitDepths.Rgb24); } finally { } }
private unsafe void FinalSave(Document input, Stream output, Surface scratchSurface, int ditherLevel, int threshold, SavableBitDepths bitDepth, PropertyBasedSaveConfigToken token, ProgressEventHandler progressCallback, double progressStart, double progressEnd) { this.RenderFlattenedDocument(input, scratchSurface); if (((bitDepth == SavableBitDepths.Rgb8) || (bitDepth == SavableBitDepths.Rgba8)) || (bitDepth == SavableBitDepths.Rgb24)) { ColorBgra white = ColorBgra.White; Work.ParallelFor(WaitType.Blocking, 0, scratchSurface.Height, delegate(int y) { int width = scratchSurface.Width; ColorBgra *rowAddress = scratchSurface.GetRowAddress(y); for (int j = 0; j < width; j++) { ColorBgra rhs = rowAddress[0]; if ((bitDepth == SavableBitDepths.Rgba8) && (rhs.A < threshold)) { rowAddress->Bgra = 0; } else { rowAddress->Bgra = CompositionOps.Normal.ApplyStatic(white, rhs).Bgra; } rowAddress++; } }, WorkItemQueuePriority.Normal, null); } ProgressEventHandler handler = (sender, e) => progressCallback(sender, new ProgressEventArgs(progressStart + ((progressEnd - progressStart) * (e.Percent / 100.0)))); this.OnFinalSave(input, output, scratchSurface, ditherLevel, bitDepth, token, handler); }
internal override void FinalSave( Document input, Stream output, Surface scratchSurface, int ditherLevel, SavableBitDepths bitDepth, PropertyBasedSaveConfigToken token, ProgressEventHandler progressCallback) { bool rleCompress = token.GetProperty<BooleanProperty>(PropertyNames.RleCompress).Value; SaveTga(scratchSurface, output, bitDepth, rleCompress, progressCallback); }
protected override void OnSaveT(Document input, Stream output, PropertyBasedSaveConfigToken token, Surface scratchSurface, ProgressEventHandler callback) { int width = input.Width; int height = input.Height; using (RenderArgs args = new RenderArgs(scratchSurface)) { input.Render(args, true); } int tileHeight = token.GetProperty <Int32Property>(PropertyNames.TileHeight).Value; int tileWidth = token.GetProperty <Int32Property>(PropertyNames.TileWidth).Value; List <Rectangle> rects = new List <Rectangle>(); for (int y = 0; y < height; y += tileHeight) { for (int x = 0; x < width; x += tileWidth) { Rectangle bounds = new Rectangle(x, y, Math.Min(tileWidth, width - x), Math.Min(tileHeight, height - y)); rects.Add(bounds); } } double progressPercentage = 0.0; double progressDelta = (1.0 / rects.Count) * 100.0; using (ZipOutputStream zipStream = new ZipOutputStream(output, true)) { int count = rects.Count; ImageCodecInfo codec = GetImageCodecInfo(ImageFormat.Png); EncoderParameters encoderParams = new EncoderParameters(1); for (int i = 0; i < count; i++) { Rectangle bounds = rects[i]; SavableBitDepths bitDepth = GetBitDepth(scratchSurface, bounds); zipStream.PutNextEntry(string.Format("Image{0}.png", (i + 1))); if (bitDepth == SavableBitDepths.Rgba32) { encoderParams.Param[0] = new EncoderParameter(Encoder.ColorDepth, 32); using (Bitmap temp = scratchSurface.CreateAliasedBitmap(bounds, true)) { temp.Save(zipStream, codec, encoderParams); } } else if (bitDepth == SavableBitDepths.Rgb24) { encoderParams.Param[0] = new EncoderParameter(Encoder.ColorDepth, 24); using (Surface surface = new Surface(bounds.Width, bounds.Height)) { surface.CopySurface(scratchSurface, bounds); // Make a new surface so we do not clobber the stride of the scratchSurface. SquishSurfaceTo24Bpp(surface); using (Bitmap temp = CreateAliased24BppBitmap(surface)) { temp.Save(zipStream, codec, encoderParams); } } } else if (bitDepth == SavableBitDepths.Rgb8) { encoderParams.Param[0] = new EncoderParameter(Encoder.ColorDepth, 8); using (Surface surface = scratchSurface.CreateWindow(bounds)) { using (Bitmap temp = Quantize(surface, 7, 256, false, null)) { temp.Save(zipStream, codec, encoderParams); } } } else if (bitDepth == SavableBitDepths.Rgba8) { encoderParams.Param[0] = new EncoderParameter(Encoder.ColorDepth, 8); using (Surface surface = scratchSurface.CreateWindow(bounds)) { using (Bitmap temp = Quantize(surface, 7, 256, true, null)) { temp.Save(zipStream, codec, encoderParams); } } } progressPercentage += progressDelta; callback(this, new ProgressEventArgs(progressPercentage)); } } }
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); }