static void Blit8To32Transparent(ReadOnlyImageBuffer <byte> fromBuffer, ImageBuffer <uint> toBuffer, uint[] palette, byte componentAlpha, byte transparentColor) { var from = fromBuffer.Buffer; var to = toBuffer.Buffer; int fromOffset = 0; int toOffset = 0; for (int j = 0; j < fromBuffer.Height; j++) { for (int i = 0; i < fromBuffer.Width; i++) { byte index = from[fromOffset]; if (index != transparentColor) { to[toOffset] = palette[index] & 0x00ffffff | ((uint)componentAlpha << 24); } fromOffset++; toOffset++; } fromOffset += fromBuffer.Stride - fromBuffer.Width; toOffset += toBuffer.Stride - toBuffer.Width; } }
public static void UnpackSpriteSheet( uint[] palette, int frameWidth, int frameHeight, ReadOnlyImageBuffer <uint> source, ImageBuffer <byte> dest, Action <int, int, int, int> frameFunc) { if (dest.Width < source.Width) { throw new ArgumentOutOfRangeException(nameof(dest), "Tried to unpack to a smaller destination"); } if (dest.Height < source.Height) { throw new ArgumentOutOfRangeException(nameof(dest), "Tried to unpack to a smaller destination"); } Blit32To8(source, dest, palette); int x = 0; int y = 0; do { frameFunc(x, y, frameWidth, frameHeight); x += frameWidth; if (x + frameWidth > source.Width) { y += frameHeight; x = 0; } } while (y + frameHeight <= source.Height); }
static IReadOnlyTexture <byte> Read(AssetId id, uint[] palette, IList <Image <Rgba32> > images) { int totalWidth = images.Max(x => x.Width); int totalHeight = images.Sum(x => x.Height); var pixels = new byte[totalWidth * totalHeight]; var frames = new List <Region>(); int currentY = 0; var quantizeCache = new Dictionary <uint, byte>(); for (int i = 0; i < images.Count; i++) { Image <Rgba32> image = images[i]; if (!image.TryGetSinglePixelSpan(out Span <Rgba32> rgbaSpan)) { throw new InvalidOperationException("Could not retrieve single span from Image"); } frames.Add(new Region(0, currentY, image.Width, image.Height, totalWidth, totalHeight, 0)); var uintSpan = MemoryMarshal.Cast <Rgba32, uint>(rgbaSpan); var from = new ReadOnlyImageBuffer <uint>(image.Width, image.Height, image.Width, uintSpan); var byteSpan = pixels.AsSpan(currentY * totalWidth, totalWidth * (image.Height - 1) + image.Width); var to = new ImageBuffer <byte>(image.Width, image.Height, totalWidth, byteSpan); BlitUtil.Blit32To8(from, to, palette, quantizeCache); currentY += image.Height; } return(new SimpleTexture <byte>(id, id.ToString(), totalWidth, totalHeight, pixels, frames)); }
public static void BlitMasked <T>(ReadOnlyImageBuffer <T> fromBuffer, ImageBuffer <T> toBuffer, Func <T, bool> opacityFunc) where T : unmanaged { var from = fromBuffer.Buffer; var to = toBuffer.Buffer; int fromOffset = 0; int toOffset = 0; for (int j = 0; j < fromBuffer.Height; j++) { for (int i = 0; i < fromBuffer.Width; i++) { var pixel = from[fromOffset]; if (opacityFunc(pixel)) { to[toOffset] = pixel; } fromOffset++; toOffset++; } fromOffset += fromBuffer.Stride - fromBuffer.Width; toOffset += toBuffer.Stride - toBuffer.Width; } }
public static void Blit32To8(ReadOnlyImageBuffer <uint> fromBuffer, ImageBuffer <byte> toBuffer, uint[] palette, Dictionary <uint, byte> quantizeCache = null) { quantizeCache ??= new Dictionary <uint, byte>(); var from = fromBuffer.Buffer; var to = toBuffer.Buffer; int fromOffset = 0; int toOffset = 0; for (int j = 0; j < fromBuffer.Height; j++) { for (int i = 0; i < fromBuffer.Width; i++) { uint pixel = from[fromOffset]; if (!quantizeCache.TryGetValue(pixel, out var index)) { index = Quantize(pixel, palette); quantizeCache[pixel] = index; } to[toOffset] = index; fromOffset++; toOffset++; } fromOffset += fromBuffer.Stride - fromBuffer.Width; toOffset += toBuffer.Stride - toBuffer.Width; } }
public ReadOnlyImageBuffer(ReadOnlyImageBuffer <T> existing, ReadOnlySpan <T> buffer) { Width = existing.Width; Height = existing.Height; Stride = existing.Stride; Buffer = buffer; }
public static void BlitDirect <T>(ReadOnlyImageBuffer <T> fromBuffer, ImageBuffer <T> toBuffer) where T : unmanaged { for (int j = 0; j < fromBuffer.Height; j++) { var fromSlice = fromBuffer.Buffer.Slice(j * fromBuffer.Stride, fromBuffer.Width); var toSlice = toBuffer.Buffer.Slice(j * toBuffer.Stride, toBuffer.Width); fromSlice.CopyTo(toSlice); } }
static byte[] Write(IImageEncoder encoder, uint[] palette, IReadOnlyTexture <byte> existing, int frameNum) { var frame = existing.Regions[frameNum]; var buffer = new ReadOnlyImageBuffer <byte>( frame.Width, frame.Height, existing.Width, existing.PixelData.Slice(frame.PixelOffset, frame.PixelLength)); Image <Rgba32> image = ImageSharpUtil.ToImageSharp(buffer, palette); var bytes = FormatUtil.BytesFromStream(stream => encoder.Encode(image, stream)); return(bytes); }
public static Image <Rgba32> ToImageSharp(ReadOnlyImageBuffer <uint> from) { Image <Rgba32> image = new Image <Rgba32>(from.Width, from.Height); if (!image.TryGetSinglePixelSpan(out var rgbaSpan)) { throw new InvalidOperationException("Could not retrieve single span from Image"); } Span <uint> toBuffer = MemoryMarshal.Cast <Rgba32, uint>(rgbaSpan); var to = new ImageBuffer <uint>(from.Width, from.Height, from.Width, toBuffer); BlitUtil.BlitDirect(from, to); return(image); }
public static void BlitTiled8To32(ReadOnlyImageBuffer <byte> from, ImageBuffer <uint> to, uint[] palette, byte componentAlpha, byte?transparentColor) { if (palette == null) { throw new ArgumentNullException(nameof(palette)); } int remainingWidth = to.Width; int remainingHeight = to.Height; Span <uint> dest = to.Buffer; int chunkHeight = Math.Min(from.Height, to.Height); do { Span <uint> rowStart = dest; chunkHeight = Math.Min(chunkHeight, remainingHeight); int chunkWidth = Math.Min(from.Width, to.Width); do { chunkWidth = Math.Min(chunkWidth, remainingWidth); var newFrom = new ReadOnlyImageBuffer <byte>(chunkWidth, chunkHeight, from.Stride, from.Buffer); var newTo = new ImageBuffer <uint>(chunkWidth, chunkHeight, to.Stride, dest); if (transparentColor.HasValue) { Blit8To32Transparent(newFrom, newTo, palette, componentAlpha, transparentColor.Value); } else { Blit8To32Opaque(newFrom, newTo, palette, componentAlpha); } dest = dest.Slice(chunkWidth); remainingWidth -= chunkWidth; } while (remainingWidth > 0); remainingHeight -= chunkHeight; remainingWidth = to.Width; if (remainingHeight > 0) { dest = rowStart.Slice(chunkHeight * to.Stride); } } while (remainingHeight > 0); }
/* * public static void Blit8(ReadOnlySpan<byte> from, Span<byte> to, int width, int height, int fromStride, int toStride) * { * int srcIndex = 0; * int destIndex = 0; * for (int i = 0; i < height; i++) * { * var row = from.Slice(srcIndex, width); * row.CopyTo(to.Slice(destIndex)); * srcIndex += fromStride; * destIndex += toStride; * } * } */ public static ISet <T> DistinctColors <T>(ReadOnlyImageBuffer <T> buffer) where T : unmanaged { int c = 0; var active = new HashSet <T>(); while (c < buffer.Buffer.Length) { int end = c + buffer.Width; while (c < end) { active.Add(buffer.Buffer[c]); c++; } c += buffer.Stride - buffer.Width; } return(active); }
public object Process(object asset, AssetInfo info) { if (asset == null) { throw new ArgumentNullException(nameof(asset)); } if (info == null) { throw new ArgumentNullException(nameof(info)); } var bitmap = (InterlacedBitmap)asset; var texture = new SimpleTexture <uint>(info.AssetId, bitmap.Width, bitmap.Height) .AddRegion(0, 0, bitmap.Width, bitmap.Height); var imageBuffer = new ReadOnlyImageBuffer <byte>(bitmap.Width, bitmap.Height, bitmap.Width, bitmap.ImageData); BlitUtil.BlitTiled8To32(imageBuffer, texture.GetMutableRegionBuffer(0), bitmap.Palette, 255, null); return(texture); }
public static void BlitTiled <T>(ReadOnlyImageBuffer <T> from, ImageBuffer <T> to, Func <T, bool> opacityFunc = null) where T : unmanaged { int remainingWidth = to.Width; int remainingHeight = to.Height; Span <T> dest = to.Buffer; int chunkHeight = Math.Min(from.Height, to.Height); do { Span <T> rowStart = dest; chunkHeight = Math.Min(chunkHeight, remainingHeight); int chunkWidth = Math.Min(from.Width, to.Width); do { chunkWidth = Math.Min(chunkWidth, remainingWidth); var newFrom = new ReadOnlyImageBuffer <T>(chunkWidth, chunkHeight, from.Stride, from.Buffer); var newTo = new ImageBuffer <T>(chunkWidth, chunkHeight, to.Stride, dest); if (opacityFunc == null) { BlitDirect(newFrom, newTo); } else { BlitMasked(newFrom, newTo, opacityFunc); } dest = dest.Slice(chunkWidth); remainingWidth -= chunkWidth; } while (remainingWidth > 0); remainingHeight -= chunkHeight; remainingWidth = to.Width; if (remainingHeight > 0) { dest = rowStart.Slice(chunkHeight * to.Stride); } } while (remainingHeight > 0); }
static IReadOnlyTexture <byte> Read(AssetId id, uint[] palette, Image <Rgba32> image, int subItemWidth, int subItemHeight) { var pixels = new byte[image.Width * image.Height]; var frames = new List <Region>(); if (!image.TryGetSinglePixelSpan(out Span <Rgba32> rgbaSpan)) { throw new InvalidOperationException("Could not retrieve single span from Image"); } var uintSpan = MemoryMarshal.Cast <Rgba32, uint>(rgbaSpan); var source = new ReadOnlyImageBuffer <uint>(image.Width, image.Height, image.Width, uintSpan); var dest = new ImageBuffer <byte>(image.Width, image.Height, image.Width, pixels); BlitUtil.UnpackSpriteSheet(palette, subItemWidth, subItemHeight, source, dest, (x, y, w, h) => frames.Add(new Region(x, y, w, h, image.Width, image.Height, 0))); while (IsFrameEmpty(frames.Last(), pixels, image.Width)) { frames.RemoveAt(frames.Count - 1); } return(new SimpleTexture <byte>(id, id.ToString(), image.Width, image.Height, pixels, frames)); }
public static void BlitTiled32(ReadOnlyImageBuffer <uint> from, ImageBuffer <uint> to) => BlitTiled(from, to, OpacityFunc32);
public static void BlitTiled8(ReadOnlyImageBuffer <byte> from, ImageBuffer <byte> to) => BlitTiled(from, to, OpacityFunc8);