/// <summary>
        /// Gets a Color32 array for engine with a border.
        /// </summary>
        /// <param name="srcBitmap">Source DFBitmap.</param>
        /// <param name="alphaIndex">Index to receive transparent alpha.</param>
        /// <param name="border">Number of pixels border to add around image.</param>
        /// <param name="sizeOut">Receives image dimensions with borders included.</param>
        /// <returns>Color32 array.</returns>
        public Color32[] GetColors32(DFBitmap srcBitmap, int alphaIndex, int border, out DFSize sizeOut)
        {
            // Must be an indexed format
            if (srcBitmap.Format != DFBitmap.Formats.Indexed)
            {
                sizeOut = new DFSize();
                return null;
            }

            // Calculate dimensions
            int srcWidth = srcBitmap.Width;
            int srcHeight = srcBitmap.Height;
            int dstWidth = srcWidth + border * 2;
            int dstHeight = srcHeight + border * 2;

            // Create target array
            Color32[] colors = new Color32[dstWidth * dstHeight];

            DFColor c;
            byte a;
            int index, srcRow, dstRow;
            for (int y = 0; y < srcHeight; y++)
            {
                // Get row position
                srcRow = y * srcWidth;
                dstRow = (dstHeight - 1 - border - y) * dstWidth;

                // Write data for this row
                for (int x = 0; x < srcWidth; x++)
                {
                    index = srcBitmap.Data[srcRow + x];
                    c = myPalette.Get(index);
                    if (alphaIndex == index) a = 0x00; else a = 0xff;

                    colors[dstRow + border + x] = new Color32(c.R, c.G, c.B, a);
                }
            }

            sizeOut = new DFSize(dstWidth, dstHeight);

            return colors;
        }
        /// <summary>
        /// Gets a Color32 array as emission map based for window textures.
        /// </summary>
        /// <param name="srcBitmap">Source bitmap.</param>
        /// <param name="emissionIndex">Index to receive emission colour.</param>
        /// <returns>Color32 array.</returns>
        public Color32[] GetWindowColors32(DFBitmap srcBitmap, int emissionIndex = 0xff)
        {
            // Must be an indexed format
            if (srcBitmap.Format != DFBitmap.Formats.Indexed)
                return null;

            // Create target array
            DFSize sz = new DFSize(srcBitmap.Width, srcBitmap.Height);
            Color32[] emissionColors = new Color32[sz.Width * sz.Height];

            // Generate emissive parts of texture based on index
            int index, srcRow, dstRow;
            for (int y = 0; y < sz.Height; y++)
            {
                // Get row position
                srcRow = y * sz.Width;
                dstRow = (sz.Height - 1 - y) * sz.Width;

                // Write data for this row
                for (int x = 0; x < sz.Width; x++)
                {
                    index = srcBitmap.Data[srcRow + x];
                    if (index == emissionIndex)
                        emissionColors[dstRow + x] = Color.white;
                }
            }

            return emissionColors;
        }
        /// <summary>
        /// Special handling for RCI files.
        /// </summary>
        /// <param name="reader">Source reader.</param>
        /// <param name="filename">Name of this file without path.</param>
        private void ReadRci(ref BinaryReader reader, string filename)
        {
            // Set dimensions based on filename
            DFSize sz;
            switch (filename)
            {
                case "FACES.CIF":
                case "CHLD00I0.RCI":
                    sz = new DFSize(64, 64);
                    break;
                case "TFAC00I0.RCI":
                    records = new Record[503];  // Extend array as this file has hundreds of images
                    sz = new DFSize(64, 64);
                    break;
                case "BUTTONS.RCI":
                    sz = new DFSize(32, 16);
                    break;
                case "MPOP.RCI":
                    sz = new DFSize(17, 17);
                    break;
                case "NOTE.RCI":
                    sz = new DFSize(44, 9);
                    break;
                case "SPOP.RCI":
                    sz = new DFSize(22, 22);
                    break;
                default:
                    return;
            }

            // Get image count
            int count = managedFile.Length / (sz.Width * sz.Height);

            // Read count image records
            for (int i = 0; i < count; i++)
            {
                // Create empty frame object
                records[i].Header.Position = reader.BaseStream.Position;
                records[i].Header.XOffset = 0;
                records[i].Header.YOffset = 0;
                records[i].Header.Width = (Int16)sz.Width;
                records[i].Header.Height = (Int16)sz.Height;
                records[i].Header.Compression = CompressionFormats.Uncompressed;
                records[i].Header.PixelDataLength = (UInt16)(sz.Width * sz.Height);
                records[i].Header.FrameCount = 1;
                records[i].Header.DataPosition = reader.BaseStream.Position;

                // Set record type
                records[i].FileType = RecordType.MultiImage;

                // Increment past image data for now
                reader.BaseStream.Position += records[i].Header.PixelDataLength;

                // Create empty frame object
                records[i].Frames = new DFBitmap[1];
            }

            // Store count
            totalRecords = count;
        }
        /// <summary>
        /// Gets a Color32 array for engine with a border.
        /// </summary>
        /// <param name="record">Record index.</param>
        /// <param name="frame">Frame index.</param>
        /// <param name="alphaIndex">Index to receive transparent alpha.</param>
        /// <param name="border">Number of pixels border to add around image.</param>
        /// <param name="sizeOut">Receives image dimensions with borders included.</param>
        /// <returns>Color32 array.</returns>
        public Color32[] GetColors32(int record, int frame, int alphaIndex, int border, out DFSize sizeOut)
        {
            // Get source bitmap
            DFBitmap srcBitmap = GetDFBitmap(record, frame);

            return GetColors32(srcBitmap, alphaIndex, border, out sizeOut);
        }
        private static void MixColor(ref Color32[] colors, ref DFSize size, Color32 src, int x, int y)
        {
            // Handle outside of bounds
            if (x < 0 || y < 0 || x > size.Width - 1 || y > size.Height - 1)
                return;

            // Get destination pixel colour and ensure it has empty alpha
            Color32 dst = ReadColor(ref colors, ref size, x, y);
            if (dst.a != 0)
                return;

            // Get count for averaging
            int count = 1;
            if (dst != Color.clear)
                count = 2;

            // Mix source colour with destination
            Vector3 avg = new Vector3(
                src.r + dst.r,
                src.g + dst.g,
                src.b + dst.b) / count;

            // Assign new colour to destination
            colors[y * size.Width + x] = new Color32((byte)avg.x, (byte)avg.y, (byte)avg.z, 0);
        }
        private static Color32 ReadColor(ref Color32[] colors, ref DFSize size, int x, int y)
        {
            // Handle outside of bounds
            if (x < 0 || y < 0 || x > size.Width - 1 || y > size.Height - 1)
                return Color.clear;

            return colors[y * size.Width + x];
        }
        /// <summary>
        /// Wraps texture into emtpty border area on opposite side.
        /// </summary>
        /// <param name="colors">Source image.</param>
        /// <param name="size">Image size.</param>
        /// <param name="border">Border width.</param>
        public static void WrapBorder(ref Color32[] colors, DFSize size, int border, bool leftRight = true, bool topBottom = true)
        {
            // Wrap left-right
            if (leftRight)
            {
                for (int y = border; y < size.Height - border; y++)
                {
                    int ypos = y * size.Width;
                    int il = ypos + border;
                    int ir = ypos + size.Width - border * 2;
                    for (int x = 0; x < border; x++)
                    {
                        colors[ypos + x] = colors[ir + x];
                        colors[ypos + size.Width - border + x] = colors[il + x];
                    }
                }
            }

            // Wrap top-bottom
            if (topBottom)
            {
                for (int y = 0; y < border; y++)
                {
                    int ypos1 = y * size.Width;
                    int ypos2 = (y + size.Height - border) * size.Width;

                    int it = (border + y) * size.Width;
                    int ib = (y + size.Height - border * 2) * size.Width;
                    for (int x = 0; x < size.Width; x++)
                    {
                        colors[ypos1 + x] = colors[ib + x];
                        colors[ypos2 + x] = colors[it + x];
                    }
                }
            }
        }
        /// <summary>
        /// Clamps texture into empty border area.
        /// </summary>
        /// <param name="colors">Source image.</param>
        /// <param name="size">Image size.</param>
        /// <param name="border">Border width.</param>
        public static void ClampBorder(
            ref Color32[] colors,
            DFSize size,
            int border,
            bool leftRight = true,
            bool topBottom = true,
            bool topLeft = true,
            bool topRight = true,
            bool bottomLeft = true,
            bool bottomRight = true)
        {
            if (leftRight)
            {
                // Clamp left-right
                for (int y = border; y < size.Height - border; y++)
                {
                    int ypos = y * size.Width;
                    Color32 leftColor = colors[ypos + border];
                    Color32 rightColor = colors[ypos + size.Width - border - 1];

                    int il = ypos;
                    int ir = ypos + size.Width - border;
                    for (int x = 0; x < border; x++)
                    {
                        colors[il + x] = leftColor;
                        colors[ir + x] = rightColor;
                    }
                }
            }

            if (topBottom)
            {
                // Clamp top-bottom
                for (int x = border; x < size.Width - border; x++)
                {
                    Color32 topColor = colors[((border) * size.Width) + x];
                    Color32 bottomColor = colors[(size.Height - border - 1) * size.Width + x];

                    for (int y = 0; y < border; y++)
                    {
                        int it = y * size.Width + x;
                        int ib = (size.Height - y - 1) * size.Width + x;

                        colors[it] = topColor;
                        colors[ib] = bottomColor;
                    }
                }
            }

            if (topLeft)
            {
                // Clamp top-left
                Color32 topLeftColor = colors[(border + 1) * size.Width + border];
                for (int y = 0; y < border; y++)
                {
                    for (int x = 0; x < border; x++)
                    {
                        colors[y * size.Width + x] = topLeftColor;
                    }
                }
            }

            if (topRight)
            {
                // Clamp top-right
                Color32 topRightColor = colors[(border + 1) * size.Width + size.Width - border - 1];
                for (int y = 0; y < border; y++)
                {
                    for (int x = size.Width - border; x < size.Width; x++)
                    {
                        colors[y * size.Width + x] = topRightColor;
                    }
                }
            }

            if (bottomLeft)
            {
                // Clamp bottom-left
                Color32 bottomLeftColor = colors[(size.Height - border - 1) * size.Width + border];
                for (int y = size.Height - border; y < size.Height; y++)
                {
                    for (int x = 0; x < border; x++)
                    {
                        colors[y * size.Width + x] = bottomLeftColor;
                    }
                }
            }

            if (bottomRight)
            {
                // Clamp bottom-right
                Color32 bottomRightColor = colors[(size.Height - border - 1) * size.Width + size.Width - border - 1];
                for (int y = size.Height - border; y < size.Height; y++)
                {
                    for (int x = size.Width - border; x < size.Width; x++)
                    {
                        colors[y * size.Width + x] = bottomRightColor;
                    }
                }
            }
        }
 /// <summary>
 /// Creates a blended border around transparent textures.
 /// Removes dark edges from billboards.
 /// </summary>
 /// <param name="colors">Source image.</param>
 /// <param name="size">Image size.</param>
 public static void DilateColors(ref Color32[] colors, DFSize size)
 {
     for (int y = 0; y < size.Height; y++)
     {
         for (int x = 0; x < size.Width; x++)
         {
             Color32 color = ReadColor(ref colors, ref size, x, y);
             if (color.a != 0)
             {
                 MixColor(ref colors, ref size, color, x - 1, y - 1);
                 MixColor(ref colors, ref size, color, x, y - 1);
                 MixColor(ref colors, ref size, color, x + 1, y - 1);
                 MixColor(ref colors, ref size, color, x - 1, y);
                 MixColor(ref colors, ref size, color, x + 1, y);
                 MixColor(ref colors, ref size, color, x - 1, y + 1);
                 MixColor(ref colors, ref size, color, x, y + 1);
                 MixColor(ref colors, ref size, color, x + 1, y + 1);
             }
         }
     }
 }
 // Copies a subset of Color32 array into XY position of another Color32 array
 public static void CopyColors(ref Color32[] src, ref Color32[] dst, DFSize srcSize, DFSize dstSize, DFPosition srcPos, DFPosition dstPos, DFSize copySize)
 {
     for (int y = 0; y < copySize.Height; y++)
     {
         for (int x = 0; x < copySize.Width; x++)
         {
             Color32 col = src[(srcPos.Y + y) * srcSize.Width + (srcPos.X + x)];
             dst[(dstPos.Y + y) * dstSize.Width + (dstPos.X + x)] = col;
         }
     }
 }
        /// <summary>
        /// Gets a Color32 array for engine.
        /// </summary>
        /// <param name="srcBitmap">Source DFBitmap.</param>
        /// <param name="alphaIndex">Index to receive transparent alpha.</param>
        /// <param name="border">Number of pixels border to add around image.</param>
        /// <param name="sizeOut">Receives image dimensions with borders included.</param>
        /// <returns>Color32 array.</returns>
        public Color32[] GetColor32(DFBitmap srcBitmap, int alphaIndex, int border, out DFSize sizeOut)
        {
            // Calculate dimensions
            int srcWidth = srcBitmap.Width;
            int srcHeight = srcBitmap.Height;
            int dstWidth = srcWidth + border * 2;
            int dstHeight = srcHeight + border * 2;

            Color32[] colors = new Color32[dstWidth * dstHeight];

            Color32 c = new Color32();
            int index, offset, srcRow, dstRow;
            byte[] paletteData = myPalette.PaletteBuffer;
            for (int y = 0; y < srcHeight; y++)
            {
                // Get row position
                srcRow = y * srcWidth;
                dstRow = (dstHeight - 1 - border - y) * dstWidth;

                // Write data for this row
                for (int x = 0; x < srcWidth; x++)
                {
                    index = srcBitmap.Data[srcRow + x];
                    offset = myPalette.HeaderLength + index * 3;
                    c.r = paletteData[offset];
                    c.g = paletteData[offset + 1];
                    c.b = paletteData[offset + 2];
                    c.a = (alphaIndex == index) ? (byte)0 : (byte)255;
                    colors[dstRow + border + x] = c;
                }
            }

            sizeOut = new DFSize(dstWidth, dstHeight);

            return colors;
        }