public void AddPalette(string name, ImmutablePalette p, bool allowModifiers) { if (palettes.ContainsKey(name)) { throw new InvalidOperationException("Palette {0} has already been defined".F(name)); } int index = palettes.Count; indices.Add(name, index); palettes.Add(name, p); if (palettes.Count > Height) { Height = Exts.NextPowerOf2(palettes.Count); Array.Resize(ref buffer, Height * Palette.Size * 4); } if (allowModifiers) { modifiablePalettes.Add(name, new MutablePalette(p)); } else { CopyPaletteToBuffer(index, p); } }
public void AddPalette(string name, ImmutablePalette p, bool allowModifiers) { if (palettes.ContainsKey(name)) { throw new InvalidOperationException($"Palette {name} has already been defined"); } // PERF: the first row in the palette textures is reserved as a placeholder for non-indexed sprites // that do not have a color-shift applied. This provides a quick shortcut to avoid querying the // color-shift texture for every pixel only to find that most are not shifted. var index = palettes.Count + 1; indices.Add(name, index); palettes.Add(name, p); if (index >= Height) { Height = Exts.NextPowerOf2(index + 1); Array.Resize(ref buffer, Height * Palette.Size * 4); Array.Resize(ref colorShiftBuffer, Height * 4); } if (allowModifiers) { mutablePalettes.Add(name, new MutablePalette(p)); } else { CopyPaletteToBuffer(index, p); } }
public void ReplacePalette(string name, IPalette p) { if (modifiablePalettes.ContainsKey(name)) CopyPaletteToBuffer(indices[name], modifiablePalettes[name] = new MutablePalette(p)); else if (palettes.ContainsKey(name)) CopyPaletteToBuffer(indices[name], palettes[name] = new ImmutablePalette(p)); else throw new InvalidOperationException("Palette `{0}` does not exist".F(name)); Texture.SetData(buffer); }
IHardwareCursor CreateCursor(ISpriteFrame f, ImmutablePalette palette, string name, CursorSequence sequence) { var hotspot = sequence.Hotspot - f.Offset.ToInt2() + new int2(f.Size) / 2; // Expand the frame if required to include the hotspot var frameWidth = f.Size.Width; var dataWidth = f.Size.Width; var dataX = 0; if (hotspot.X < 0) { dataX = -hotspot.X; dataWidth += dataX; hotspot = hotspot.WithX(0); } else if (hotspot.X >= frameWidth) { dataWidth = hotspot.X + 1; } var frameHeight = f.Size.Height; var dataHeight = f.Size.Height; var dataY = 0; if (hotspot.Y < 0) { dataY = -hotspot.Y; dataHeight += dataY; hotspot = hotspot.WithY(0); } else if (hotspot.Y >= frameHeight) { dataHeight = hotspot.Y + 1; } var data = new byte[4 * dataWidth * dataHeight]; for (var j = 0; j < frameHeight; j++) { for (var i = 0; i < frameWidth; i++) { var bytes = BitConverter.GetBytes(palette[f.Data[j * frameWidth + i]]); var start = 4 * ((j + dataY) * dataWidth + dataX + i); for (var k = 0; k < 4; k++) { data[start + k] = bytes[k]; } } } return(Game.Renderer.Window.CreateHardwareCursor(name, new Size(dataWidth, dataHeight), data, hotspot)); }
public void AddPalette(string name, ImmutablePalette pal, bool allowModifiers = false, bool allowOverwrite = false) { if (allowOverwrite && palette.Contains(name)) ReplacePalette(name, pal); else { var oldHeight = palette.Height; palette.AddPalette(name, pal, allowModifiers); if (oldHeight != palette.Height && PaletteInvalidated != null) PaletteInvalidated(); } }
public void LoadPlayerPalettes(WorldRenderer wr, string playerName, HSLColor color, bool replaceExisting) { var basePalette = wr.Palette(info.BasePalette).Palette; ImmutablePalette pal; int[] remap; if (info.PlayerIndex.TryGetValue(playerName, out remap)) pal = new ImmutablePalette(basePalette, new IndexedColorRemap(basePalette, info.RemapIndex, remap)); else pal = new ImmutablePalette(basePalette); wr.AddPalette(info.BaseName + playerName, pal, info.AllowModifiers, replaceExisting); }
public void AddPalette(string name, ImmutablePalette p, bool allowModifiers) { if (palettes.Count >= MaxPalettes) throw new InvalidOperationException("Limit of {0} palettes reached. Cannot add {1}.".F(MaxPalettes, name)); if (palettes.ContainsKey(name)) throw new InvalidOperationException("Palette {0} has already been defined".F(name)); int index = palettes.Count; indices.Add(name, index); palettes.Add(name, p); if (allowModifiers) modifiablePalettes.Add(name, new MutablePalette(p)); else CopyPaletteToBuffer(index, p); }
public void ReplacePalette(string name, IPalette p) { if (modifiablePalettes.ContainsKey(name)) { CopyPaletteToBuffer(indices[name], modifiablePalettes[name] = new MutablePalette(p)); } else if (palettes.ContainsKey(name)) { CopyPaletteToBuffer(indices[name], palettes[name] = new ImmutablePalette(p)); } else { throw new InvalidOperationException("Palette `{0}` does not exist".F(name)); } Texture.SetData(buffer); }
public void ReplacePalette(string name, IPalette p) { if (mutablePalettes.ContainsKey(name)) { CopyPaletteToBuffer(indices[name], mutablePalettes[name] = new MutablePalette(p)); } else if (palettes.ContainsKey(name)) { CopyPaletteToBuffer(indices[name], palettes[name] = new ImmutablePalette(p)); } else { throw new InvalidOperationException($"Palette `{name}` does not exist"); } CopyBufferToTexture(); }
public void Run(ModData modData, string[] args) { var remap = new Dictionary<int, int>(); /* the first 4 entries are fixed */ for (var i = 0; i < 4; i++) remap[i] = i; var srcMod = args[1].Split(':')[0]; Game.ModData = new ModData(srcMod); GlobalFileSystem.LoadFromManifest(Game.ModData.Manifest); var srcRules = Game.ModData.RulesetCache.Load(); var srcPaletteInfo = srcRules.Actors["player"].TraitInfo<PlayerColorPaletteInfo>(); var srcRemapIndex = srcPaletteInfo.RemapIndex; var destMod = args[2].Split(':')[0]; Game.ModData = new ModData(destMod); GlobalFileSystem.LoadFromManifest(Game.ModData.Manifest); var destRules = Game.ModData.RulesetCache.Load(); var destPaletteInfo = destRules.Actors["player"].TraitInfo<PlayerColorPaletteInfo>(); var destRemapIndex = destPaletteInfo.RemapIndex; var shadowIndex = new int[] { }; // the remap range is always 16 entries, but their location and order changes for (var i = 0; i < 16; i++) remap[PlayerColorRemap.GetRemapIndex(srcRemapIndex, i)] = PlayerColorRemap.GetRemapIndex(destRemapIndex, i); // map everything else to the best match based on channel-wise distance var srcPalette = new ImmutablePalette(args[1].Split(':')[1], shadowIndex); var destPalette = new ImmutablePalette(args[2].Split(':')[1], shadowIndex); for (var i = 0; i < Palette.Size; i++) if (!remap.ContainsKey(i)) remap[i] = Enumerable.Range(0, Palette.Size) .Where(a => !remap.ContainsValue(a)) .MinBy(a => ColorDistance(destPalette[a], srcPalette[i])); using (var s = File.OpenRead(args[3])) using (var destStream = File.Create(args[4])) { var srcImage = new ShpTDSprite(s); ShpTDSprite.Write(destStream, srcImage.Size, srcImage.Frames.Select(im => im.Data.Select(px => (byte)remap[px]).ToArray())); } }
IHardwareCursor CreateCursor(ISpriteFrame f, ImmutablePalette palette, string name, CursorSequence sequence) { var hotspot = sequence.Hotspot - f.Offset.ToInt2() + new int2(f.Size) / 2; // Expand the frame if required to include the hotspot var frameWidth = f.Size.Width; var dataWidth = f.Size.Width; var dataX = 0; if (hotspot.X < 0) { dataX = -hotspot.X; dataWidth += dataX; hotspot = hotspot.WithX(0); } else if (hotspot.X >= frameWidth) dataWidth = hotspot.X + 1; var frameHeight = f.Size.Height; var dataHeight = f.Size.Height; var dataY = 0; if (hotspot.Y < 0) { dataY = -hotspot.Y; dataHeight += dataY; hotspot = hotspot.WithY(0); } else if (hotspot.Y >= frameHeight) dataHeight = hotspot.Y + 1; var data = new byte[4 * dataWidth * dataHeight]; for (var j = 0; j < frameHeight; j++) { for (var i = 0; i < frameWidth; i++) { var bytes = BitConverter.GetBytes(palette[f.Data[j * frameWidth + i]]); var start = 4 * ((j + dataY) * dataWidth + dataX + i); for (var k = 0; k < 4; k++) data[start + k] = bytes[k]; } } return Game.Renderer.Device.CreateHardwareCursor(name, new Size(dataWidth, dataHeight), data, hotspot); }
public override void Init(ModData modData, Dictionary<string, string> info) { // Avoid standard loading mechanisms so we // can display the loadscreen as early as possible r = Game.Renderer; if (r == null) return; if (info.ContainsKey("Text")) messages = info["Text"].Split(','); if (info.ContainsKey("Palette")) { using (var stream = modData.DefaultFileSystem.Open(info["Palette"])) { palette = new ImmutablePalette(stream, new int[] { }); } hardwarePalette = new HardwarePalette(); hardwarePalette.AddPalette("loadscreen", palette, false); hardwarePalette.Initialize(); r.SetPalette(hardwarePalette); } if (info.ContainsKey("Image")) { using (var stream = modData.DefaultFileSystem.Open(info["Image"])) { CpsD2Loader loader = new CpsD2Loader(); if (!loader.TryParseSprite(stream, out frames)) return; } if (frames.Length == 0) return; sheetBuilder = new SheetBuilder(SheetType.Indexed, 512); logo = sheetBuilder.Add(frames[0]); logoPos = new float2((r.Resolution.Width - logo.Size.X) / 2, (r.Resolution.Height - logo.Size.Y) / 2); } }
public void AddPalette(string name, ImmutablePalette p, bool allowModifiers) { if (palettes.ContainsKey(name)) throw new InvalidOperationException("Palette {0} has already been defined".F(name)); int index = palettes.Count; indices.Add(name, index); palettes.Add(name, p); if (palettes.Count > Height) { Height = Exts.NextPowerOf2(palettes.Count); Array.Resize(ref buffer, Height * Palette.Size * 4); } if (allowModifiers) modifiablePalettes.Add(name, new MutablePalette(p)); else CopyPaletteToBuffer(index, p); }
void CreateNewTileset() { this.Show(); using (var formNew = new FormNew()) if (DialogResult.OK == formNew.ShowDialog()) { srcfile = formNew.ImageFile; this.size = formNew.TileSize; var bitmap = new Bitmap(srcfile); if (!formNew.PaletteFromImage) { var terrainPalette = new ImmutablePalette(formNew.PaletteFile, new int[0]); bitmap.Palette = terrainPalette.AsSystemPalette(); } InitializeSurface(bitmap); } }
IHardwareCursor CreateCursor(string name, ISpriteFrame frame, ImmutablePalette palette, int2 paddingTL, int2 paddingBR, int2 hotspot) { // Pad the cursor and convert to RBGA var newWidth = paddingTL.X + frame.Size.Width + paddingBR.X; var newHeight = paddingTL.Y + frame.Size.Height + paddingBR.Y; var rgbaData = new byte[4 * newWidth * newHeight]; for (var j = 0; j < frame.Size.Height; j++) { for (var i = 0; i < frame.Size.Width; i++) { var bytes = BitConverter.GetBytes(palette[frame.Data[j * frame.Size.Width + i]]); var o = 4 * ((j + paddingTL.Y) * newWidth + i + paddingTL.X); for (var k = 0; k < 4; k++) { rgbaData[o + k] = bytes[k]; } } } return(Game.Renderer.Window.CreateHardwareCursor(name, new Size(newWidth, newHeight), rgbaData, hotspot)); }
public void CreateNewTileset() { this.Show(); using (var formNew = new FormNew { }) if (DialogResult.OK == formNew.ShowDialog()) { PaletteFromImage = formNew.PaletteFromImage; PaletteFile = formNew.PaletteFile; ImageFile = formNew.ImageFile; TileSize = formNew.TileSize; srcfile = ImageFile; this.size = TileSize; surface1.TileSize = TileSize; Bitmap rbitmap; using (var fbitmap = new Bitmap(ImageFile)) rbitmap = fbitmap.Clone(new Rectangle(0, 0, fbitmap.Width, fbitmap.Height), fbitmap.PixelFormat); int[] shadowIndex = { }; if (!PaletteFromImage) { TerrainPalette = new ImmutablePalette(PaletteFile, shadowIndex); rbitmap.Palette = TerrainPalette.AsSystemPalette(); } surface1.Image = (Bitmap)rbitmap; surface1.TilesPerRow = surface1.Image.Size.Width / surface1.TileSize; surface1.Image.SetResolution(96, 96); // people keep being noobs about DPI, and GDI+ cares. surface1.TerrainTypes = new int[surface1.Image.Width / size, surface1.Image.Height / size]; /* all passable by default */ surface1.Templates = new List<Template>(); surface1.Size = surface1.Image.Size; surface1.Enabled = true; Load(); } }
public static byte[] FrameToBGRA(string name, ISpriteFrame frame, ImmutablePalette palette) { // Data is already in BGRA format if (frame.Type == SpriteFrameType.BGRA) { return(frame.Data); } // Cursors may be either native BGRA or Indexed. // Indexed sprites are converted to BGRA using the referenced palette. // All palettes must be explicitly referenced, even if they are embedded in the sprite. if (frame.Type == SpriteFrameType.Indexed && palette == null) { throw new InvalidOperationException("Cursor sequence `{0}` attempted to load an indexed sprite but does not define Palette".F(name)); } var width = frame.Size.Width; var height = frame.Size.Height; var data = new byte[4 * width * height]; for (var j = 0; j < height; j++) { for (var i = 0; i < width; i++) { var bytes = BitConverter.GetBytes(palette[frame.Data[j * width + i]]); var c = palette[frame.Data[j * width + i]]; var k = 4 * (j * width + i); // Convert RGBA to BGRA data[k] = bytes[2]; data[k + 1] = bytes[1]; data[k + 2] = bytes[0]; data[k + 3] = bytes[3]; } } return(data); }
public static byte[] ConvertIndexedToBgra(string name, ISpriteFrame frame, ImmutablePalette palette) { if (frame.Type != SpriteFrameType.Indexed8) { throw new ArgumentException("ConvertIndexedToBgra requires input frames to be indexed.", nameof(frame)); } // All palettes must be explicitly referenced, even if they are embedded in the sprite. if (palette == null) { throw new InvalidOperationException($"Cursor sequence `{name}` attempted to load an indexed sprite but does not define Palette"); } var width = frame.Size.Width; var height = frame.Size.Height; var data = new byte[4 * width * height]; unsafe { // Cast the data to an int array so we can copy the src data directly fixed(byte *bd = &data[0]) { var rgba = (uint *)bd; for (var j = 0; j < height; j++) { for (var i = 0; i < width; i++) { rgba[j * width + i] = palette[frame.Data[j * width + i]]; } } } } return(data); }
public void AddPalette(string name, ImmutablePalette p, bool allowModifiers) { if (palettes.Count >= MaxPalettes) { throw new InvalidOperationException("Limit of {0} palettes reached. Cannot add {1}.".F(MaxPalettes, name)); } if (palettes.ContainsKey(name)) { throw new InvalidOperationException("Palette {0} has already been defined".F(name)); } int index = palettes.Count; indices.Add(name, index); palettes.Add(name, p); if (allowModifiers) { modifiablePalettes.Add(name, new MutablePalette(p)); } else { CopyPaletteToBuffer(index, p); } }
public void LoadPlayerPalettes(WorldRenderer wr, string playerName, HSLColor color, bool replaceExisting) { var argb = (uint)Color.FromArgb(128, color.RGB).ToArgb(); var pal = new ImmutablePalette(Enumerable.Range(0, Palette.Size).Select(i => i == 0 ? 0 : argb)); wr.AddPalette(info.BaseName + playerName, pal, false, replaceExisting); }
// this code is insanely stupid, and mostly my fault -- chrisf void PrepareMapResources(ModData modData, Map map) { Program.Rules = map.Rules; tileset = Program.Rules.TileSets[map.Tileset]; tilesetRenderer = new TileSetRenderer(tileset, modData.Manifest.TileSize); var shadowIndex = new int[] { 3, 4 }; var palette = new ImmutablePalette(GlobalFileSystem.Open(tileset.Palette), shadowIndex); // required for desert terrain in RA var playerPalette = tileset.PlayerPalette ?? tileset.Palette; var shadowedPalette = new ImmutablePalette(GlobalFileSystem.Open(playerPalette), shadowIndex); surface1.Bind(map, tileset, tilesetRenderer, palette, shadowedPalette); // construct the palette of tiles var palettes = new[] { tilePalette, actorPalette, resourcePalette }; foreach (var p in palettes) { p.Visible = false; p.SuspendLayout(); } var templateOrder = tileset.EditorTemplateOrder ?? new string[] { }; foreach (var tc in tileset.Templates.GroupBy(t => t.Value.Category).OrderBy(t => templateOrder.IndexOf(t.Key))) { var category = tc.Key ?? "(Uncategorized)"; var categoryHeader = new Label { BackColor = SystemColors.Highlight, ForeColor = SystemColors.HighlightText, Text = category, AutoSize = false, Height = 24, TextAlign = ContentAlignment.MiddleLeft, Width = tilePalette.ClientSize.Width, }; // hook this manually, anchoring inside FlowLayoutPanel is flaky. tilePalette.Resize += (_, e) => categoryHeader.Width = tilePalette.ClientSize.Width; if (tilePalette.Controls.Count > 0) tilePalette.SetFlowBreak( tilePalette.Controls[tilePalette.Controls.Count - 1], true); tilePalette.Controls.Add(categoryHeader); foreach (var t in tc) { try { var bitmap = tilesetRenderer.RenderTemplate((ushort)t.Key, palette); var ibox = new PictureBox { Image = bitmap, Width = bitmap.Width / 2, Height = bitmap.Height / 2, SizeMode = PictureBoxSizeMode.StretchImage }; var brushTemplate = new BrushTemplate { Bitmap = bitmap, N = t.Key }; ibox.Click += (_, e) => surface1.SetTool(new BrushTool(brushTemplate)); var template = t.Value; tilePalette.Controls.Add(ibox); tt.SetToolTip(ibox, "{1}:{0} ({2}x{3})".F(template.Images[0], template.Id, template.Size.X, template.Size.Y)); } catch { } } } var actorTemplates = new List<ActorTemplate>(); foreach (var a in Program.Rules.Actors.Keys) { try { var info = Program.Rules.Actors[a]; if (!info.Traits.Contains<ILegacyEditorRenderInfo>()) continue; var etf = info.Traits.GetOrDefault<EditorTilesetFilterInfo>(); if (etf != null && etf.ExcludeTilesets != null && etf.ExcludeTilesets.Contains(tileset.Id)) continue; if (etf != null && etf.RequireTilesets != null && !etf.RequireTilesets.Contains(tileset.Id)) continue; var templatePalette = shadowedPalette; var rsi = info.Traits.GetOrDefault<ILegacyEditorRenderInfo>(); // exception for desert buildings if (rsi != null && rsi.EditorPalette != null && rsi.EditorPalette.Contains("terrain")) templatePalette = palette; var race = Program.Rules.Actors["world"].Traits.WithInterface<CountryInfo>().First().Race; var sequenceProvider = Program.Rules.Sequences[tileset.Id]; var template = RenderUtils.RenderActor(info, sequenceProvider, tileset, templatePalette, race); var ibox = new PictureBox { Image = template.Bitmap, Width = 32, Height = 32, SizeMode = PictureBoxSizeMode.Zoom, BorderStyle = BorderStyle.FixedSingle }; ibox.Click += (_, e) => surface1.SetTool(new ActorTool(template)); actorPalette.Controls.Add(ibox); tt.SetToolTip(ibox, "{0}".F(info.Name)); actorTemplates.Add(template); } catch { } } surface1.BindActorTemplates(actorTemplates); var resourceTemplates = new List<ResourceTemplate>(); foreach (var a in Program.Rules.Actors["world"].Traits.WithInterface<ResourceTypeInfo>()) { try { var template = RenderUtils.RenderResourceType(a, tileset, shadowedPalette); var ibox = new PictureBox { Image = template.Bitmap, Width = 32, Height = 32, SizeMode = PictureBoxSizeMode.Zoom, BorderStyle = BorderStyle.FixedSingle }; ibox.Click += (_, e) => surface1.SetTool(new ResourceTool(template)); resourcePalette.Controls.Add(ibox); tt.SetToolTip(ibox, "{0}:{1}cr".F(template.Info.Name, template.Info.ValuePerUnit)); resourceTemplates.Add(template); } catch { } } surface1.BindResourceTemplates(resourceTemplates); foreach (var p in palettes) { p.Visible = true; p.ResumeLayout(); } miniMapBox.Image = Minimap.RenderMapPreview(tileset, surface1.Map, true); propertiesToolStripMenuItem.Enabled = true; toolStripMenuItemProperties.Enabled = true; resizeToolStripMenuItem.Enabled = true; toolStripMenuItemResize.Enabled = true; saveToolStripMenuItem.Enabled = true; toolStripMenuItemSave.Enabled = true; saveAsToolStripMenuItem.Enabled = true; miniMapToPng.Enabled = true; PopulateActorOwnerChooser(); }
public void LoadPlayerPalettes(WorldRenderer wr, string playerName, HSLColor color, bool replaceExisting) { var remap = new AlphaPaletteRemap(info.Alpha, info.Premultiply); var pal = new ImmutablePalette(wr.Palette(info.BasePalette + playerName).Palette, remap); wr.AddPalette(info.BaseName + playerName, pal, info.AllowModifiers, replaceExisting); }
public void AddPalette(string name, ImmutablePalette pal, bool allowModifiers) { palette.AddPalette(name, pal, allowModifiers); }
public void LoadPlayerPalettes(WorldRenderer wr, string playerName, HSLColor color, bool replaceExisting) { var remap = new PlayerColorRemap(info.RemapIndex, color, info.Ramp); var pal = new ImmutablePalette(wr.Palette(info.BasePalette).Palette, remap); wr.AddPalette(info.BaseName + playerName, pal, info.AllowModifiers, replaceExisting); }
public void AddPalette(string name, ImmutablePalette pal) { palette.AddPalette(name, pal, false); }
void IUtilityCommand.Run(Utility utility, string[] args) { // HACK: The engine code assumes that Game.modData is set. var modData = Game.ModData = utility.ModData; var src = args[1]; var shadowIndex = new int[] { }; if (args.Contains("--noshadow")) { Array.Resize(ref shadowIndex, shadowIndex.Length + 3); shadowIndex[shadowIndex.Length - 1] = 1; shadowIndex[shadowIndex.Length - 2] = 3; shadowIndex[shadowIndex.Length - 3] = 4; } var palette = new ImmutablePalette(args[2], shadowIndex); var frames = SpriteLoader.GetFrames(File.OpenRead(src), modData.SpriteLoaders); var usePadding = !args.Contains("--nopadding"); var count = 0; var prefix = Path.GetFileNameWithoutExtension(src); foreach (var frame in frames) { var frameSize = usePadding && !frame.DisableExportPadding ? frame.FrameSize : frame.Size; var offset = usePadding && !frame.DisableExportPadding ? (frame.Offset - 0.5f * new float2(frame.Size - frame.FrameSize)).ToInt2() : int2.Zero; // shp(ts) may define empty frames if (frameSize.Width == 0 && frameSize.Height == 0) { count++; continue; } using (var bitmap = new Bitmap(frameSize.Width, frameSize.Height, PixelFormat.Format8bppIndexed)) { bitmap.Palette = palette.AsSystemPalette(); var data = bitmap.LockBits(new Rectangle(0, 0, frameSize.Width, frameSize.Height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed); // Clear the frame if (usePadding && !frame.DisableExportPadding) { var clearRow = new byte[data.Stride]; for (var i = 0; i < frameSize.Height; i++) Marshal.Copy(clearRow, 0, new IntPtr(data.Scan0.ToInt64() + i * data.Stride), data.Stride); } for (var i = 0; i < frame.Size.Height; i++) { var destIndex = new IntPtr(data.Scan0.ToInt64() + (i + offset.Y) * data.Stride + offset.X); Marshal.Copy(frame.Data, i * frame.Size.Width, destIndex, frame.Size.Width); } bitmap.UnlockBits(data); var filename = "{0}-{1:D4}.png".F(prefix, count++); bitmap.Save(filename); } } Console.WriteLine("Saved {0}-[0..{1}].png", prefix, count - 1); }
public static void ConvertSpriteToPng(string[] args) { var src = args[1]; var shadowIndex = new int[] { }; if (args.Contains("--noshadow")) { Array.Resize(ref shadowIndex, shadowIndex.Length + 3); shadowIndex[shadowIndex.Length - 1] = 1; shadowIndex[shadowIndex.Length - 2] = 3; shadowIndex[shadowIndex.Length - 3] = 4; } var palette = new ImmutablePalette(args[2], shadowIndex); ISpriteSource source; using (var stream = File.OpenRead(src)) source = SpriteSource.LoadSpriteSource(stream, src); // The r8 padding requires external information that we can't access here. var usePadding = !(args.Contains("--nopadding") || source is R8Reader); var count = 0; var prefix = Path.GetFileNameWithoutExtension(src); foreach (var frame in source.Frames) { var frameSize = usePadding ? frame.FrameSize : frame.Size; var offset = usePadding ? (frame.Offset - 0.5f * new float2(frame.Size - frame.FrameSize)).ToInt2() : int2.Zero; // shp(ts) may define empty frames if (frameSize.Width == 0 && frameSize.Height == 0) { count++; continue; } using (var bitmap = new Bitmap(frameSize.Width, frameSize.Height, PixelFormat.Format8bppIndexed)) { bitmap.Palette = palette.AsSystemPalette(); var data = bitmap.LockBits(new Rectangle(0, 0, frameSize.Width, frameSize.Height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed); // Clear the frame if (usePadding) { var clearRow = new byte[data.Stride]; for (var i = 0; i < frameSize.Height; i++) Marshal.Copy(clearRow, 0, new IntPtr(data.Scan0.ToInt64() + i * data.Stride), data.Stride); } for (var i = 0; i < frame.Size.Height; i++) { var destIndex = new IntPtr(data.Scan0.ToInt64() + (i + offset.Y) * data.Stride + offset.X); Marshal.Copy(frame.Data, i * frame.Size.Width, destIndex, frame.Size.Width); } bitmap.UnlockBits(data); var filename = "{0}-{1:D4}.png".F(prefix, count++); bitmap.Save(filename); } } Console.WriteLine("Saved {0}-[0..{1}].png", prefix, count - 1); }