/** <summary> Returns a palette image loaded from the specified bitmap image. </summary> */ public static PaletteImage FromImage(Bitmap image, Palette palette) { PaletteImage paletteImage = new PaletteImage(image.Size); for (int x = 0; x < image.Width; x++) { for (int y = 0; y < image.Height; y++) { Color imageColor = image.GetPixel(x, y); int minDelta = GetColorDelta(imageColor, palette.Colors[0]); int minDeltaIndex = 0; for (int i = 10; i < palette.NumColors; i++) { int delta = GetColorDelta(imageColor, palette.Colors[i]); if (delta < minDelta) { minDelta = delta; minDeltaIndex = i; } } paletteImage.pixels[x, y] = (byte)minDeltaIndex; } } return(paletteImage); }
/** <summary> Inserts the specified palette image at the specified index. </summary> */ public void Insert(int index, PaletteImage paletteImage) { if (paletteImage != null) { this.paletteImages.Insert(index, paletteImage); this.palettes.Insert(index, null); this.imageDirectory.entries.Insert(index, paletteImage.entry); this.numImages++; } }
/** <summary> Adds the specified palette image. </summary> */ public void Add(PaletteImage paletteImage) { this.paletteImages.Add(paletteImage); this.palettes.Add(null); this.imageDirectory.entries.Add(paletteImage.entry); if (paletteImage != null) { this.numImages++; } }
/** <summary> Removes the specified palette image. </summary> */ public void Remove(PaletteImage paletteImage) { if (paletteImage != null) { int index = this.paletteImages.IndexOf(paletteImage); this.paletteImages.RemoveAt(index); this.palettes.RemoveAt(index); this.imageDirectory.entries.RemoveAt(index); this.numImages--; } }
//=========== DRAWING ============ #region Drawing /** <summary> Draws the terrain to the specified palette image. </summary> */ public void Draw(PaletteImage p, Point point, int darkness) { point.X -= 32 + ((Origin.X - Origin.Y) * 32); point.Y -= 15 + ((Origin.X + Origin.Y) * 16); for (int x1 = 0; x1 < Size.Width; x1++) { for (int y1 = 0; y1 < Size.Height; y1++) { if (Slope != -1 && ((Slope == 0 && x1 < Origin.X - 0) || (Slope == 2 && x1 > Origin.X + 2) || (Slope == 1 && y1 < Origin.Y - 1) || (Slope == 3 && y1 > Origin.Y + 1))) { LandTiles[0].DrawWithOffset(p, point.X + ((x1 - y1) * 32), point.Y + ((x1 + y1 - 1) * 16), darkness, false, RemapColors.None, RemapColors.None, RemapColors.None ); } else if (Slope == -1 || (Slope % 2 == 0 && (x1 < Origin.X - 0 || x1 > Origin.X + 2)) || (Slope % 2 == 1 && (y1 < Origin.Y - 1 || y1 > Origin.Y + 1))) { /*(Slope % 2 == 0 && (x1 < Origin.X - 0 || x1 > Origin.X + 2)) || * (Slope % 2 == 1 && (y1 < Origin.Y - 1 || y1 > Origin.Y + 1))) {*/ LandTiles[0].DrawWithOffset(p, point.X + ((x1 - y1) * 32), point.Y + ((x1 + y1) * 16), darkness, false, RemapColors.None, RemapColors.None, RemapColors.None ); } else if (Slope == 0 && x1 == Origin.X + 2) { LandTiles[1].DrawWithOffset(p, point.X + ((x1 - y1) * 32), point.Y + ((x1 + y1) * 16), darkness, false, RemapColors.None, RemapColors.None, RemapColors.None ); } else if (Slope == 1 && y1 == Origin.Y + 1) { LandTiles[2].DrawWithOffset(p, point.X + ((x1 - y1) * 32), point.Y + ((x1 + y1) * 16), darkness, false, RemapColors.None, RemapColors.None, RemapColors.None ); } else if (Slope == 2 && x1 == Origin.X - 0) { LandTiles[3].DrawWithOffset(p, point.X + ((x1 - y1) * 32), point.Y + ((x1 + y1) * 16), darkness, false, RemapColors.None, RemapColors.None, RemapColors.None ); } else if (Slope == 3 && y1 == Origin.Y - 1) { LandTiles[4].DrawWithOffset(p, point.X + ((x1 - y1) * 32), point.Y + ((x1 + y1) * 16), darkness, false, RemapColors.None, RemapColors.None, RemapColors.None ); } } } }
/** <summary> Draws the palette image into the specified palette image. </summary> */ public void Draw(PaletteImage p, Point point, int darkness, bool glass, RemapColors remap1 = RemapColors.None, RemapColors remap2 = RemapColors.None, RemapColors remap3 = RemapColors.None) { for (int x1 = 0; x1 < Width; x1++) { for (int y1 = 0; y1 < Height; y1++) { if (x1 + point.X >= 0 && y1 + point.Y >= 0 && x1 + point.X < p.Width && y1 + point.Y < p.Height) { p.pixels[x1 + point.X, y1 + point.Y] = GetColorIndexRemapped(pixels[x1, y1], p.pixels[x1 + point.X, y1 + point.Y], darkness, glass, remap1, remap2, remap3); } } } }
/** <summary> Draws the palette image into the specified palette image using the image entry's offset. </summary> */ public void DrawWithOffset(PaletteImage p, int x, int y, int darkness, bool glass, RemapColors remap1 = RemapColors.None, RemapColors remap2 = RemapColors.None, RemapColors remap3 = RemapColors.None) { x += this.entry.XOffset; y += this.entry.YOffset; for (int x1 = 0; x1 < Width; x1++) { for (int y1 = 0; y1 < Height; y1++) { if (x1 + x >= 0 && y1 + y >= 0 && x1 + x < p.Width && y1 + y < p.Height) { p.pixels[x1 + x, y1 + y] = GetColorIndexRemapped(pixels[x1, y1], p.pixels[x1 + x, y1 + y], darkness, glass, remap1, remap2, remap3); } } } }
/** <summary> Sets the specified palette image at the specified index. </summary> */ public void Set(int index, PaletteImage paletteImage) { if (paletteImage != null) { if (this.paletteImages[index] == null) { this.numImages++; if (this.palettes[index] != null) { this.palettes[index] = null; this.numPalettes--; } } this.paletteImages[index] = paletteImage; this.imageDirectory.entries[index] = paletteImage.entry; } }
/** <summary> Draws the palette into the specified palette image. </summary> */ public void Draw(PaletteImage p, Point point, Size colorSize) { for (int i = 0; i < colors.Length; i++) { int x2 = ((i + entry.XOffset) % 16) * colorSize.Width; int y2 = ((i + entry.XOffset) / 16) * colorSize.Height; for (int x1 = 0; x1 < colorSize.Width; x1++) { for (int y1 = 0; y1 < colorSize.Height; y1++) { if (point.X + x1 + x2 >= 0 && point.Y + y1 + y2 >= 0 && point.X + x1 + x2 < p.Width && point.Y + y1 + y2 < p.Height) { p.pixels[point.X + x1 + x2, point.Y + y1 + y2] = (byte)(i + entry.XOffset); } } } } }
/** <summary> Draws the palette into the specified palette image. </summary> */ public void Draw(PaletteImage p, int x, int y, int colorWidth, int colorHeight) { for (int i = 0; i < colors.Length; i++) { int x2 = ((i + entry.XOffset) % 16) * colorWidth; int y2 = ((i + entry.XOffset) / 16) * colorHeight; for (int x1 = 0; x1 < colorWidth; x1++) { for (int y1 = 0; y1 < colorHeight; y1++) { if (x + x1 + x2 >= 0 && y + y1 + y2 >= 0 && x + x1 + x2 < p.Width && y + y1 + y2 < p.Height) { p.pixels[x + x1 + x2, y + y1 + y2] = (byte)(i + entry.XOffset); } } } } }
/** <summary> Draws the terrain to the specified palette image. </summary> */ public void Draw(PaletteImage p, int x, int y, int darkness) { x -= 32 + ((Origin.X - Origin.Y) * 32); y -= 15 + ((Origin.X + Origin.Y) * 16); for (int x1 = 0; x1 < Size.Width; x1++) { for (int y1 = 0; y1 < Size.Height; y1++) { if (Slope == -1 || (Slope % 2 == 0 && x1 < Origin.X - 1 && x1 > Origin.X + 1) || (Slope % 2 == 1 && y1 < Origin.Y - 1 && y1 > Origin.Y + 1)) { LandTiles[0].DrawWithOffset(p, x + ((x1 - y1) * 32), y + ((x1 + y1) * 16), darkness, false, RemapColors.None, RemapColors.None, RemapColors.None ); } else if (Slope == 0 && x1 == Origin.X + 1) { LandTiles[1].DrawWithOffset(p, x + ((x1 - y1) * 32), y + ((x1 + y1) * 16), darkness, false, RemapColors.None, RemapColors.None, RemapColors.None ); } else if (Slope == 1 && y1 == Origin.Y + 1) { LandTiles[2].DrawWithOffset(p, x + ((x1 - y1) * 32), y + ((x1 + y1) * 16), darkness, false, RemapColors.None, RemapColors.None, RemapColors.None ); } else if (Slope == 2 && x1 == Origin.X - 1) { LandTiles[3].DrawWithOffset(p, x + ((x1 - y1) * 32), y + ((x1 + y1) * 16), darkness, false, RemapColors.None, RemapColors.None, RemapColors.None ); } else if (Slope == 3 && y1 == Origin.Y - 1) { LandTiles[4].DrawWithOffset(p, x + ((x1 - y1) * 32), y + ((x1 + y1) * 16), darkness, false, RemapColors.None, RemapColors.None, RemapColors.None ); } } } }
/** <summary> Draws the object as it is in the dialog. </summary> */ public virtual bool DrawDialog(PaletteImage p, Point position, Size dialogSize, DrawSettings drawSettings) { return(false); }
//=========== DRAWING ============ #region Drawing /** <summary> Draws the object as it is in game. </summary> */ public virtual bool Draw(PaletteImage p, Point position, DrawSettings drawSettings) { return(false); }
/** <summary> Writes the graphics data. </summary> */ public void Write(BinaryWriter writer) { long startPosition = writer.BaseStream.Position; for (int i = 0; i < imageDirectory.NumEntries; i++) { ImageEntry entry = imageDirectory.entries[i]; entry.StartAddress = (uint)(writer.BaseStream.Position - startPosition); if (entry.Flags.HasFlag(ImageFlags.DirectBitmap)) { if (!entry.Flags.HasFlag(ImageFlags.CompactedBitmap)) { if (entry.Flags.HasFlag(ImageFlags.LandTile)) { // Don't know what this flag does, but images can still be written normally. } PaletteImage paletteImage = this.paletteImages[i]; // Write each row for (int y = 0; y < entry.Height; y++) { // Write each pixel in the row for (int x = 0; x < entry.Width; x++) { writer.Write(paletteImage.Pixels[x, y]); } } } else { if (entry.Flags.HasFlag(ImageFlags.LandTile)) { // Don't know what this flag does, but images can still be written normally. } PaletteImage paletteImage = this.paletteImages[i]; List <ScanLine> scanLines = new List <ScanLine>(); ushort[] rowOffsets = new ushort[entry.Height]; ushort rowOffset = (ushort)(entry.Height * 2); // Write the scan lines in every row and figure out the scan line row offsets for (int y = 0; y < (int)entry.Height; y++) { rowOffsets[y] = rowOffset; ScanLine scanLine = new ScanLine(); scanLine.Row = (short)y; // Continue until the next row while ((scanLine.Count & 0x80) == 0x00) { // Reset the scan line count scanLine.Count = 0; // Find each scan line and then check if there's another one in the row bool finishedScanLine = false; bool lastScanLine = true; for (int x = 0; x + (int)scanLine.Offset < (int)entry.Width; x++) { if (!finishedScanLine) { if (scanLine.Count == 0) { // If the scan line hasn't started yet, increment the offset if (paletteImage.Pixels[x + scanLine.Offset, y] == 0x00) { scanLine.Offset++; x--; } else { scanLine.Count = 1; } } else if (paletteImage.Pixels[x + scanLine.Offset, y] == 0x00 || x == 0x7F) { // If the next pixel is transparent or the scan line is as big as possible, finish the line finishedScanLine = true; } else { // Increment the scan line byte count scanLine.Count++; } } else if (paletteImage.Pixels[x + scanLine.Offset, y] != 0x00) { // There is another scan line after this lastScanLine = false; break; } } // Set the end flag if the scan line is the last in the row if (lastScanLine) { scanLine.Count |= 0x80; } // If the row has all transparent pixels, set the offset to 0 if (scanLine.Count == 0) { scanLine.Offset = 0; scanLine.Count = 0; } rowOffset += (ushort)(2 + (scanLine.Count & 0x7F)); scanLines.Add(scanLine); // Increment the scan line count if (!lastScanLine) { scanLine.Offset += (byte)(scanLine.Count & 0x7F); } } } // Write the row offsets for (int j = 0; j < entry.Height; j++) { writer.Write(rowOffsets[j]); } // Write the scan lines for (int j = 0; j < scanLines.Count; j++) { writer.Write(scanLines[j].Count); writer.Write(scanLines[j].Offset); for (int k = 0; k < (int)(scanLines[j].Count & 0x7F); k++) { try { writer.Write(paletteImage.Pixels[k + scanLines[j].Offset, scanLines[j].Row]); } catch (Exception) { } } } } } else if (entry.Flags.HasFlag(ImageFlags.PaletteEntries)) { Palette palette = this.palettes[i]; // Write each color for (int j = 0; j < entry.Width; j++) { // Yes, the colors are in the order blue, green, red writer.Write(palette.Colors[j].B); writer.Write(palette.Colors[j].G); writer.Write(palette.Colors[j].R); } } } }
/** <summary> Reads the graphics data. </summary> */ public void Read(BinaryReader reader) { long startPosition = reader.BaseStream.Position; for (int i = 0; i < imageDirectory.NumEntries; i++) { ImageEntry entry = imageDirectory.entries[i]; if (entry.Flags.HasFlag(ImageFlags.DirectBitmap)) { if (!entry.Flags.HasFlag(ImageFlags.CompactedBitmap)) { if (entry.Flags.HasFlag(ImageFlags.LandTile)) { // Don't know what this flag does, but images can still be read normally. } PaletteImage paletteImage = new PaletteImage(entry); reader.BaseStream.Position = startPosition + entry.StartAddress; // Read each row for (int y = 0; y < entry.Height; y++) { for (int x = 0; x < entry.Width; x++) { byte b = reader.ReadByte(); paletteImage.Pixels[x, y] = b; } } this.paletteImages.Add(paletteImage); this.palettes.Add(null); this.numImages++; } else { if (entry.Flags.HasFlag(ImageFlags.LandTile)) { // Don't know what this flag does, but images can still be read normally. } PaletteImage paletteImage = new PaletteImage(entry); uint[] rowOffsets = new uint[entry.Height]; reader.BaseStream.Position = startPosition + entry.StartAddress; // Read the row offsets for (int j = 0; j < entry.Height; j++) { rowOffsets[j] = reader.ReadUInt16(); } // Read the scan lines in each row for (int j = 0; j < entry.Height; j++) { reader.BaseStream.Position = startPosition + entry.StartAddress + rowOffsets[j]; byte b1 = 0; byte b2 = 0; // A MSB of 1 means the last scan line in a row while ((b1 & 0x80) == 0) { // Read the number of bytes of data b1 = reader.ReadByte(); // Read the offset from the left edge of the image b2 = reader.ReadByte(); for (int k = 0; k < (int)(b1 & 0x7F); k++) { byte b3 = reader.ReadByte(); paletteImage.Pixels[(int)b2 + k, j] = b3; } } } this.paletteImages.Add(paletteImage); this.palettes.Add(null); this.numImages++; } } else if (entry.Flags.HasFlag(ImageFlags.PaletteEntries)) { Palette palette = new Palette(entry); reader.BaseStream.Position = startPosition + entry.StartAddress; // Read each color for (int j = 0; j < entry.Width; j++) { // Yes, the colors are in the order blue, green, red byte blue = reader.ReadByte(); byte green = reader.ReadByte(); byte red = reader.ReadByte(); palette.Colors[j] = Color.FromArgb(red, green, blue); } this.paletteImages.Add(null); this.palettes.Add(palette); this.numPalettes++; } } }
/** <summary> Reads the palette image from the graphics data. </summary> */ public PaletteImage ReadPaletteImage(BinaryReader reader, long startPosition, int index) { int i = index; ImageEntry entry = imageDirectory.entries[i]; if (entry.Flags.HasFlag(ImageFlags.DirectBitmap)) { if (!entry.Flags.HasFlag(ImageFlags.CompactedBitmap)) { if (entry.Flags.HasFlag(ImageFlags.LandTile)) { // Don't know what this flag does, but images can still be read normally. } PaletteImage paletteImage = new PaletteImage(entry); reader.BaseStream.Position = startPosition + entry.StartAddress; // Read each row for (int y = 0; y < entry.Height; y++) { for (int x = 0; x < entry.Width; x++) { byte b = reader.ReadByte(); paletteImage.Pixels[x, y] = b; } } return(paletteImage); } else { if (entry.Flags.HasFlag(ImageFlags.LandTile)) { // Don't know what this flag does, but images can still be read normally. } PaletteImage paletteImage = new PaletteImage(entry); uint[] rowOffsets = new uint[entry.Height]; reader.BaseStream.Position = startPosition + entry.StartAddress; // Read the row offsets for (int j = 0; j < entry.Height; j++) { rowOffsets[j] = reader.ReadUInt16(); } // Read the scan lines in each row for (int j = 0; j < entry.Height; j++) { reader.BaseStream.Position = startPosition + entry.StartAddress + rowOffsets[j]; byte b1 = 0; byte b2 = 0; // A MSB of 1 means the last scan line in a row while ((b1 & 0x80) == 0) { // Read the number of bytes of data b1 = reader.ReadByte(); // Read the offset from the left edge of the image b2 = reader.ReadByte(); for (int k = 0; k < (int)(b1 & 0x7F); k++) { byte b3 = reader.ReadByte(); paletteImage.Pixels[(int)b2 + k, j] = b3; } } } return(paletteImage); } } else if (entry.Flags.HasFlag(ImageFlags.PaletteEntries)) { } return(null); }