Exemplo n.º 1
0
        public static void ScaleTemplates(TemplateDoc tmpd, Size sizTile)
        {
            // Scale templates

            Template[] atmpl          = tmpd.GetTemplates();
            Template   tmplBackground = tmpd.GetBackgroundTemplate();

            tmpd.RemoveTemplates(atmpl);
            foreach (Template tmpl in atmpl)
            {
                ScaleTemplate(tmpl, tmpd.TileSize, sizTile);
            }
            tmpd.TileSize = sizTile;
            tmpd.AddTemplates(atmpl);
            tmpd.SetBackgroundTemplate(tmplBackground);
        }
Exemplo n.º 2
0
        public static TemplateDoc CloneTemplateDoc(TemplateDoc tmpdSrc)
        {
            // This should probably be on ICloneable::Clone() on TemplateDoc

            TemplateDoc tmpdDst = (TemplateDoc)DocManager.NewDocument(typeof(TemplateDoc), new Object[] { tmpdSrc.TileSize });

            DocManager.SetActiveDocument(typeof(TemplateDoc), tmpdSrc);

            Template[] atmplSrc          = tmpdSrc.GetTemplates();
            Template   tmplSrcBackground = tmpdSrc.GetBackgroundTemplate();
            Template   tmplDstBackground = null;
            ArrayList  alsTmplDst        = new ArrayList();

            foreach (Template tmplSrc in atmplSrc)
            {
                Template tmplDst = new Template(tmpdDst, tmplSrc.Name);
                tmplDst.OccupancyMap = tmplSrc.OccupancyMap;
                tmplDst.TerrainMap   = tmplSrc.TerrainMap;
                tmplDst.Bitmap       = (Bitmap)tmplSrc.Bitmap.Clone();
                alsTmplDst.Add(tmplDst);
                if (tmplSrc == tmplSrcBackground)
                {
                    tmplDstBackground = tmplDst;
                }
            }
            if (tmplDstBackground != null)
            {
                tmpdDst.SetBackgroundTemplate(tmplDstBackground);
            }
            tmpdDst.AddTemplates((Template[])alsTmplDst.ToArray(typeof(Template)));
            Palette palSrc = tmpdSrc.GetPalette();

            if (palSrc != null)
            {
                tmpdDst.SetPalette(palSrc, false);
            }

            return(tmpdDst);
        }
Exemplo n.º 3
0
        public static void ScaleTemplates(TemplateDoc tmpd, Size sizTile)
        {
            // Scale templates

            Template[] atmpl = tmpd.GetTemplates();
            Template tmplBackground = tmpd.GetBackgroundTemplate();
            tmpd.RemoveTemplates(atmpl);
            foreach (Template tmpl in atmpl)
                ScaleTemplate(tmpl, tmpd.TileSize, sizTile);
            tmpd.TileSize = sizTile;
            tmpd.AddTemplates(atmpl);
            tmpd.SetBackgroundTemplate(tmplBackground);
        }
Exemplo n.º 4
0
        public static void QuantizeTemplates(TemplateDoc tmpd, Palette palFixed, int cPalEntries, int cPalEntriesFixed, int cPalEntriesBackground)
        {
            // Load the fixed palette. The result will be 24 bit templates normalized to a palette of 256 colors,
            // the "fixed" palette plus a quantized palette.

            if (palFixed == null) {
                palFixed = Palette.OpenDialog(null);
                if (palFixed == null) {
                    MessageBox.Show(DocManager.GetFrameParent(), "Must have the fixed color palette to continue!");
                    return;
                }

                switch (palFixed.Length) {
                case 16:
                    cPalEntries = 16;
                    cPalEntriesFixed = 16;
                    cPalEntriesBackground = 0;
                    break;

                case 256:
                    cPalEntries = 256;
                    cPalEntriesFixed = 128;
                    cPalEntriesBackground = 32;
                    break;
                }
            }

            // Quantize loop. Designed to make optimal use of the lower 128 fixed colors

            // Quantize background separately from foreground

            Template tmplBackground = tmpd.GetBackgroundTemplate();
            if (tmplBackground != null && cPalEntriesBackground != 0) {
                // Create a despeckled hue map of the background. We'll use this to
                // subtract background from foreground so we can quantize foreground separately

                Bitmap bmHueBackground = MakeHueMap(tmplBackground.Bitmap);
                DespeckleGrayscaleBitmap(bmHueBackground, 9, 50);

                // Calc mean and standard deviation for filtering purposes

                double nMean = CalcGrayscaleMean(bmHueBackground);
                double nStdDev = CalcGrayscaleStandardDeviation(bmHueBackground, nMean);

                // Add extract & quantize the background pixels

                ArrayList alsColorsBackground = new ArrayList();
                AddTemplateColors(alsColorsBackground, tmplBackground.Bitmap);
                palFixed = QuantizeColors(alsColorsBackground, palFixed, cPalEntriesFixed + cPalEntriesBackground, cPalEntriesFixed);
                cPalEntriesFixed += cPalEntriesBackground;

                // Now extract foreground pixels by first subtracting background pixels

                ArrayList alsColorsForeground = new ArrayList();
                Template[] atmpl = tmpd.GetTemplates();
                foreach (Template tmpl in atmpl) {
                    if (tmpl == tmplBackground)
                        continue;
                    Bitmap bmT = MakeHueMap(tmpl.Bitmap);
                    DespeckleGrayscaleBitmap(bmT, 9, 50);
                    SubtractGrayscaleDistribution(bmT, nMean, nStdDev);
                    for (int y = 0; y < bmT.Height; y++) {
                        for (int x = 0; x < bmT.Width; x++) {
                            Color clr = bmT.GetPixel(x, y);
                            if (clr != Color.FromArgb(255, 0, 255))
                                bmT.SetPixel(x, y, tmpl.Bitmap.GetPixel(x, y));
                        }
                    }
                    AddTemplateColors(alsColorsForeground, bmT);
                }

                // Now quantize foreground pixels
                // Set the palette and color match

                Palette palNew = QuantizeColors(alsColorsForeground, palFixed, cPalEntries, cPalEntriesFixed);
                tmpd.SetPalette(palNew, true);
            } else {
                // No background template; just quantize everything together

                Template[] atmpl = tmpd.GetTemplates();
                ArrayList alsColors = new ArrayList();
                foreach (Template tmpl in atmpl)
                    AddTemplateColors(alsColors, tmpl.Bitmap);

                // Now quantize foreground pixels
                // Set the palette and color match

                Palette palNew = QuantizeColors(alsColors, palFixed, cPalEntries, cPalEntriesFixed);
                tmpd.SetPalette(palNew, true);
            }
        }
Exemplo n.º 5
0
        public static TemplateDoc CloneTemplateDoc(TemplateDoc tmpdSrc)
        {
            // This should probably be on ICloneable::Clone() on TemplateDoc

            TemplateDoc tmpdDst = (TemplateDoc)DocManager.NewDocument(typeof(TemplateDoc), new Object[] { tmpdSrc.TileSize });
            DocManager.SetActiveDocument(typeof(TemplateDoc), tmpdSrc);

            Template[] atmplSrc = tmpdSrc.GetTemplates();
            Template tmplSrcBackground = tmpdSrc.GetBackgroundTemplate();
            Template tmplDstBackground = null;
            ArrayList alsTmplDst = new ArrayList();
            foreach (Template tmplSrc in atmplSrc) {
                Template tmplDst = new Template(tmpdDst, tmplSrc.Name);
                tmplDst.OccupancyMap = tmplSrc.OccupancyMap;
                tmplDst.TerrainMap = tmplSrc.TerrainMap;
                tmplDst.Bitmap = (Bitmap)tmplSrc.Bitmap.Clone();
                alsTmplDst.Add(tmplDst);
                if (tmplSrc == tmplSrcBackground)
                    tmplDstBackground = tmplDst;
            }
            if (tmplDstBackground != null)
                tmpdDst.SetBackgroundTemplate(tmplDstBackground);
            tmpdDst.AddTemplates((Template[])alsTmplDst.ToArray(typeof(Template)));
            Palette palSrc = tmpdSrc.GetPalette();
            if (palSrc != null)
                tmpdDst.SetPalette(palSrc, false);

            return tmpdDst;
        }
Exemplo n.º 6
0
        public void DrawTileMap(Bitmap bm, ArrayList alsmiSelected, Size sizTile, TemplateDoc tmpd, LayerFlags lyrf)
        {
            // Draw background

            using (Graphics g = Graphics.FromImage(bm)) {
                Template tmplBackground = null;
                if (tmpd != null)
                    tmplBackground = tmpd.GetBackgroundTemplate();
                if (tmplBackground == null) {
                    g.FillRectangle(new SolidBrush(Color.Black), 0, 0, bm.Width, bm.Height);
                    for (int x = sizTile.Width; x < bm.Width; x += sizTile.Width) {
                        for (int y = sizTile.Height; y < bm.Height; y += sizTile.Height) {
                            bm.SetPixel(x, y, Color.BlanchedAlmond);
                        }
                    }
                } else {
                    for (int x = 0; x < bm.Width; x += tmplBackground.Bitmap.Width) {
                        for (int y = 0; y < bm.Height; y += tmplBackground.Bitmap.Height)
                            g.DrawImage(tmplBackground.Bitmap, x, y);
                    }
                }

                // Draw templates

                if ((lyrf & LayerFlags.Templates) != 0) {
                    foreach (IMapItem mi in m_alsmi) {
                        int x = (int)mi.tx * sizTile.Width;
                        int y = (int)mi.ty * sizTile.Height;
                        mi.Draw(g, x, y, sizTile, tmpd, LayerType.TileMap, alsmiSelected != null ? alsmiSelected.Contains(mi) : false);
                    }
                }
            }
        }
Exemplo n.º 7
0
        public static void QuantizeTemplates(TemplateDoc tmpd, Palette palFixed, int cPalEntries, int cPalEntriesFixed, int cPalEntriesBackground)
        {
            // Load the fixed palette. The result will be 24 bit templates normalized to a palette of 256 colors,
            // the "fixed" palette plus a quantized palette.

            if (palFixed == null)
            {
                palFixed = Palette.OpenDialog(null);
                if (palFixed == null)
                {
                    MessageBox.Show(DocManager.GetFrameParent(), "Must have the fixed color palette to continue!");
                    return;
                }

                switch (palFixed.Length)
                {
                case 16:
                    cPalEntries           = 16;
                    cPalEntriesFixed      = 16;
                    cPalEntriesBackground = 0;
                    break;

                case 256:
                    cPalEntries           = 256;
                    cPalEntriesFixed      = 128;
                    cPalEntriesBackground = 32;
                    break;
                }
            }

            // Quantize loop. Designed to make optimal use of the lower 128 fixed colors

            // Quantize background separately from foreground

            Template tmplBackground = tmpd.GetBackgroundTemplate();

            if (tmplBackground != null && cPalEntriesBackground != 0)
            {
                // Create a despeckled hue map of the background. We'll use this to
                // subtract background from foreground so we can quantize foreground separately

                Bitmap bmHueBackground = MakeHueMap(tmplBackground.Bitmap);
                DespeckleGrayscaleBitmap(bmHueBackground, 9, 50);

                // Calc mean and standard deviation for filtering purposes

                double nMean   = CalcGrayscaleMean(bmHueBackground);
                double nStdDev = CalcGrayscaleStandardDeviation(bmHueBackground, nMean);

                // Add extract & quantize the background pixels

                ArrayList alsColorsBackground = new ArrayList();
                AddTemplateColors(alsColorsBackground, tmplBackground.Bitmap);
                palFixed          = QuantizeColors(alsColorsBackground, palFixed, cPalEntriesFixed + cPalEntriesBackground, cPalEntriesFixed);
                cPalEntriesFixed += cPalEntriesBackground;

                // Now extract foreground pixels by first subtracting background pixels

                ArrayList  alsColorsForeground = new ArrayList();
                Template[] atmpl = tmpd.GetTemplates();
                foreach (Template tmpl in atmpl)
                {
                    if (tmpl == tmplBackground)
                    {
                        continue;
                    }
                    Bitmap bmT = MakeHueMap(tmpl.Bitmap);
                    DespeckleGrayscaleBitmap(bmT, 9, 50);
                    SubtractGrayscaleDistribution(bmT, nMean, nStdDev);
                    for (int y = 0; y < bmT.Height; y++)
                    {
                        for (int x = 0; x < bmT.Width; x++)
                        {
                            Color clr = bmT.GetPixel(x, y);
                            if (clr != Color.FromArgb(255, 0, 255))
                            {
                                bmT.SetPixel(x, y, tmpl.Bitmap.GetPixel(x, y));
                            }
                        }
                    }
                    AddTemplateColors(alsColorsForeground, bmT);
                }

                // Now quantize foreground pixels
                // Set the palette and color match

                Palette palNew = QuantizeColors(alsColorsForeground, palFixed, cPalEntries, cPalEntriesFixed);
                tmpd.SetPalette(palNew, true);
            }
            else
            {
                // No background template; just quantize everything together

                Template[] atmpl     = tmpd.GetTemplates();
                ArrayList  alsColors = new ArrayList();
                foreach (Template tmpl in atmpl)
                {
                    AddTemplateColors(alsColors, tmpl.Bitmap);
                }

                // Now quantize foreground pixels
                // Set the palette and color match

                Palette palNew = QuantizeColors(alsColors, palFixed, cPalEntries, cPalEntriesFixed);
                tmpd.SetPalette(palNew, true);
            }
        }
Exemplo n.º 8
0
        static void ImportTileMap(TileMap tmap, TileSet tset, TemplateDoc tmpd, LevelDoc lvld)
        {
            // The TileMap is a list of indexes into a tile set. A Tileset is a list of tiles compiled
            // from Templates. Reverse the tilemap into Templates, and set into lvld.

            bool[,] afCellTaken = new bool[64, 64];
            ArrayList alsTemplPos = new ArrayList();

            Template[] atmpl = tmpd.GetTemplates();
            for (int ty = 0; ty < tmap.Height; ty++)
            {
                for (int tx = 0; tx < tmap.Width; tx++)
                {
                    // Cell mapped already?
                    if (afCellTaken[ty, tx])
                    {
                        continue;
                    }

                    // Cell not mapped. Create TemplatePos.
                    int      iTile = tmap.GetTileIndex(tx, ty);
                    TileData tdata = tset.GetTileData(iTile);
                    Template tmpl  = atmpl[tdata.iTemplate];

                    // Don't bother with background tiles
                    if (tmpl == tmpd.GetBackgroundTemplate())
                    {
                        continue;
                    }

                    int txOrigin = tx - tdata.txTemplate;
                    int tyOrigin = ty - tdata.tyTemplate;
                    bool[,] afMapped = new bool[tmpl.Cty, tmpl.Ctx];

                    for (int tyTmpl = 0; tyTmpl < tmpl.Cty; tyTmpl++)
                    {
                        for (int txTmpl = 0; txTmpl < tmpl.Ctx; txTmpl++)
                        {
                            int txT = txOrigin + txTmpl;
                            int tyT = tyOrigin + tyTmpl;
                            if (txT < 0 || txT >= 64 || tyT < 0 || tyT >= 64)
                            {
                                continue;
                            }
                            if (afCellTaken[tyT, txT])
                            {
                                continue;
                            }
                            int iTileT = tmap.GetTileIndex(txT, tyT);
                            if (iTileT != -1)
                            {
                                TileData tdataT = tset.GetTileData(iTileT);
                                if (tdataT.iTemplate != tdata.iTemplate)
                                {
                                    continue;
                                }
                                if (tdataT.txTemplate != txTmpl || tdataT.tyTemplate != tyTmpl)
                                {
                                    continue;
                                }
                            }
                            afMapped[tyTmpl, txTmpl] = true;
                            afCellTaken[tyT, txT]    = true;
                        }
                    }
                    alsTemplPos.Add(new TemplatePos(tmpl, txOrigin, tyOrigin, afMapped));
                }
            }

            // Figure out the bounds.

            Rectangle rcBounds = new Rectangle((64 - tmap.Width) / 2, (64 - tmap.Height) / 2,
                                               tmap.Width, tmap.Height);

            lvld.Bounds = rcBounds;

            // The list of TemplatePos's has been created. Add to LevelDoc.

            ArrayList alsTiles = new ArrayList();

            foreach (TemplatePos tpos in alsTemplPos)
            {
                Tile tile = new Tile(tpos.tmpl.Name,
                                     tpos.txOrigin + rcBounds.Left,
                                     tpos.tyOrigin + rcBounds.Top,
                                     tpos.afMapped, tpos.tmpl.OccupancyMap);
                alsTiles.Add(tile);
            }
            lvld.AddMapItems((IMapItem[])alsTiles.ToArray(typeof(IMapItem)));
        }
Exemplo n.º 9
0
        void WriteMiniTiles(BinaryWriter bwtr, Palette pal, TemplateDoc tmpd, int cx, bool fNext, double nAreaBackgroundThreshold, double nLuminanceMultBackground, double nSaturationMultBackground, double nLuminanceMultForeground, double nSaturationMultForeground)
        {
            // struct MiniTileSetHeader { // mtshdr
            //  ushort offNext;
            //    ushort cTiles;
            //    ushort cxTile;
            //    ushort cyTile;
            // };

            ushort offNext = 0;

            if (fNext)
            {
                offNext = (ushort)(8 + m_alsTileData.Count * cx * cx);
            }
            bwtr.Write(Misc.SwapUShort(offNext));
            bwtr.Write(Misc.SwapUShort((ushort)m_alsTileData.Count));
            bwtr.Write(Misc.SwapUShort((ushort)cx));
            bwtr.Write(Misc.SwapUShort((ushort)cx));

            // If a background template exists, use it to distinguish foreground from background objects for better minimaps

            Size      sizTile        = tmpd.TileSize;
            ArrayList alsColors      = new ArrayList();
            Template  tmplBackground = tmpd.GetBackgroundTemplate();

            if (tmplBackground != null && nAreaBackgroundThreshold >= 0.0)
            {
                // Get despeckled hue map of background, calc mean and
                // std dev for filtering purposes

                Bitmap bmHueBackground = TemplateTools.MakeHueMap(tmplBackground.Bitmap);
                TemplateTools.DespeckleGrayscaleBitmap(bmHueBackground, 9, 50);
                double nMean   = TemplateTools.CalcGrayscaleMean(bmHueBackground);
                double nStdDev = TemplateTools.CalcGrayscaleStandardDeviation(bmHueBackground, nMean);

                // Go through each tile, first make a mask that'll delineate foreground from background

                Bitmap bmTile = new Bitmap(sizTile.Width, sizTile.Height);
                foreach (TileData td in m_alsTileData)
                {
                    // Need to turn data back into a bitmap - doh!

                    for (int y = 0; y < sizTile.Height; y++)
                    {
                        for (int x = 0; x < sizTile.Width; x++)
                        {
                            Color clr = (Color)td.aclr[y * sizTile.Width + x];
                            bmTile.SetPixel(x, y, clr);
                        }
                    }

                    // Create mask which'll replace background with transparent color (255, 0, 255)

                    Bitmap bmMask = TemplateTools.MakeHueMap(bmTile);
                    TemplateTools.DespeckleGrayscaleBitmap(bmMask, 9, 50);
                    TemplateTools.SubtractGrayscaleDistribution(bmMask, nMean, nStdDev);

                    // Now scale tile down to desired size, using mask as input

                    Bitmap bmScaled = TemplateTools.ScaleTemplateBitmap(bmTile, bmMask, cx, cx, nAreaBackgroundThreshold, nLuminanceMultBackground, nSaturationMultBackground, nLuminanceMultForeground, nSaturationMultForeground);

                    // Grab the data

                    for (int y = 0; y < cx; y++)
                    {
                        for (int x = 0; x < cx; x++)
                        {
                            alsColors.Add(bmScaled.GetPixel(x, y));
                        }
                    }
                    bmScaled.Dispose();
                    bmMask.Dispose();
                }
            }
            else
            {
                // No background template; just scale

                Bitmap bmTile = new Bitmap(sizTile.Width, sizTile.Height);
                foreach (TileData td in m_alsTileData)
                {
                    // Need to turn data back into a bitmap - doh!

                    for (int y = 0; y < sizTile.Height; y++)
                    {
                        for (int x = 0; x < sizTile.Width; x++)
                        {
                            Color clr = (Color)td.aclr[y * sizTile.Width + x];
                            bmTile.SetPixel(x, y, clr);
                        }
                    }

                    // Now scale tile down to desired size, using mask as input

                    Bitmap bmScaled = TemplateTools.ScaleTemplateBitmap(bmTile, null, cx, cx, 1.0, 1.0, 1.0, 1.0, 1.0);

                    // Grab the data

                    for (int y = 0; y < cx; y++)
                    {
                        for (int x = 0; x < cx; x++)
                        {
                            alsColors.Add(bmScaled.GetPixel(x, y));
                        }
                    }
                    bmScaled.Dispose();
                }
            }

            // Palette match and write results

            foreach (Color clr in alsColors)
            {
                bwtr.Write((byte)pal.FindClosestEntry(clr));
            }
        }
Exemplo n.º 10
0
        static void ImportTileMap(TileMap tmap, TileSet tset, TemplateDoc tmpd, LevelDoc lvld)
        {
            // The TileMap is a list of indexes into a tile set. A Tileset is a list of tiles compiled
            // from Templates. Reverse the tilemap into Templates, and set into lvld.

            bool[,] afCellTaken = new bool[64, 64];
            ArrayList alsTemplPos = new ArrayList();
            Template[] atmpl = tmpd.GetTemplates();
            for (int ty = 0; ty < tmap.Height; ty++) {
                for (int tx = 0; tx < tmap.Width; tx++) {
                    // Cell mapped already?
                    if (afCellTaken[ty, tx]) {
                        continue;
                    }

                    // Cell not mapped. Create TemplatePos.
                    int iTile = tmap.GetTileIndex(tx, ty);
                    TileData tdata = tset.GetTileData(iTile);
                    Template tmpl = atmpl[tdata.iTemplate];

                    // Don't bother with background tiles
                    if (tmpl == tmpd.GetBackgroundTemplate()) {
                        continue;
                    }

                    int txOrigin = tx - tdata.txTemplate;
                    int tyOrigin = ty - tdata.tyTemplate;
                    bool[,] afMapped = new bool[tmpl.Cty, tmpl.Ctx];

                    for (int tyTmpl = 0; tyTmpl < tmpl.Cty; tyTmpl++) {
                        for (int txTmpl = 0; txTmpl < tmpl.Ctx; txTmpl++) {
                            int txT = txOrigin + txTmpl;
                            int tyT = tyOrigin + tyTmpl;
                            if (txT < 0 || txT >= 64 || tyT < 0 || tyT >= 64) {
                                continue;
                            }
                            if (afCellTaken[tyT, txT]) {
                                continue;
                            }
                            int iTileT = tmap.GetTileIndex(txT, tyT);
                            if (iTileT != -1) {
                                TileData tdataT = tset.GetTileData(iTileT);
                                if (tdataT.iTemplate != tdata.iTemplate) {
                                    continue;
                                }
                                if (tdataT.txTemplate != txTmpl || tdataT.tyTemplate != tyTmpl) {
                                    continue;
                                }
                            }
                            afMapped[tyTmpl, txTmpl] = true;
                            afCellTaken[tyT, txT] = true;
                        }
                    }
                    alsTemplPos.Add(new TemplatePos(tmpl, txOrigin, tyOrigin, afMapped));
                }
            }

            // Figure out the bounds.

            Rectangle rcBounds = new Rectangle((64 - tmap.Width) / 2, (64 - tmap.Height) / 2,
                    tmap.Width, tmap.Height);
            lvld.Bounds = rcBounds;

            // The list of TemplatePos's has been created. Add to LevelDoc.

            ArrayList alsTiles = new ArrayList();
            foreach (TemplatePos tpos in alsTemplPos) {
                Tile tile = new Tile(tpos.tmpl.Name,
                        tpos.txOrigin + rcBounds.Left,
                        tpos.tyOrigin + rcBounds.Top,
                        tpos.afMapped, tpos.tmpl.OccupancyMap);
                alsTiles.Add(tile);
            }
            lvld.AddMapItems((IMapItem[])alsTiles.ToArray(typeof(IMapItem)));
        }
Exemplo n.º 11
0
        void WriteMiniTiles(BinaryWriter bwtr, Palette pal, TemplateDoc tmpd, int cx, bool fNext, double nAreaBackgroundThreshold, double nLuminanceMultBackground, double nSaturationMultBackground, double nLuminanceMultForeground, double nSaturationMultForeground)
        {
            // struct MiniTileSetHeader { // mtshdr
            //  ushort offNext;
            //    ushort cTiles;
            //    ushort cxTile;
            //    ushort cyTile;
            // };

            ushort offNext = 0;
            if (fNext)
                offNext = (ushort)(8 + m_alsTileData.Count * cx * cx);
            bwtr.Write(Misc.SwapUShort(offNext));
            bwtr.Write(Misc.SwapUShort((ushort)m_alsTileData.Count));
            bwtr.Write(Misc.SwapUShort((ushort)cx));
            bwtr.Write(Misc.SwapUShort((ushort)cx));

            // If a background template exists, use it to distinguish foreground from background objects for better minimaps

            Size sizTile = tmpd.TileSize;
            ArrayList alsColors = new ArrayList();
            Template tmplBackground = tmpd.GetBackgroundTemplate();
            if (tmplBackground != null && nAreaBackgroundThreshold >= 0.0) {
                // Get despeckled hue map of background, calc mean and
                // std dev for filtering purposes

                Bitmap bmHueBackground = TemplateTools.MakeHueMap(tmplBackground.Bitmap);
                TemplateTools.DespeckleGrayscaleBitmap(bmHueBackground, 9, 50);
                double nMean = TemplateTools.CalcGrayscaleMean(bmHueBackground);
                double nStdDev = TemplateTools.CalcGrayscaleStandardDeviation(bmHueBackground, nMean);

                // Go through each tile, first make a mask that'll delineate foreground from background

                Bitmap bmTile = new Bitmap(sizTile.Width, sizTile.Height);
                foreach (TileData td in m_alsTileData) {
                    // Need to turn data back into a bitmap - doh!

                    for (int y = 0; y < sizTile.Height; y++) {
                        for (int x = 0; x < sizTile.Width; x++) {
                            Color clr = (Color)td.aclr[y * sizTile.Width + x];
                            bmTile.SetPixel(x, y, clr);
                        }
                    }

                    // Create mask which'll replace background with transparent color (255, 0, 255)

                    Bitmap bmMask = TemplateTools.MakeHueMap(bmTile);
                    TemplateTools.DespeckleGrayscaleBitmap(bmMask, 9, 50);
                    TemplateTools.SubtractGrayscaleDistribution(bmMask, nMean, nStdDev);

                    // Now scale tile down to desired size, using mask as input

                    Bitmap bmScaled = TemplateTools.ScaleTemplateBitmap(bmTile, bmMask, cx, cx, nAreaBackgroundThreshold, nLuminanceMultBackground, nSaturationMultBackground, nLuminanceMultForeground, nSaturationMultForeground);

                    // Grab the data

                    for (int y = 0; y < cx; y++) {
                        for (int x = 0; x < cx; x++) {
                            alsColors.Add(bmScaled.GetPixel(x, y));
                        }
                    }
                    bmScaled.Dispose();
                    bmMask.Dispose();
                }
            } else {
                // No background template; just scale

                Bitmap bmTile = new Bitmap(sizTile.Width, sizTile.Height);
                foreach (TileData td in m_alsTileData) {
                    // Need to turn data back into a bitmap - doh!

                    for (int y = 0; y < sizTile.Height; y++) {
                        for (int x = 0; x < sizTile.Width; x++) {
                            Color clr = (Color)td.aclr[y * sizTile.Width + x];
                            bmTile.SetPixel(x, y, clr);
                        }
                    }

                    // Now scale tile down to desired size, using mask as input

                    Bitmap bmScaled = TemplateTools.ScaleTemplateBitmap(bmTile, null, cx, cx, 1.0, 1.0, 1.0, 1.0, 1.0);

                    // Grab the data

                    for (int y = 0; y < cx; y++) {
                        for (int x = 0; x < cx; x++) {
                            alsColors.Add(bmScaled.GetPixel(x, y));
                        }
                    }
                    bmScaled.Dispose();
                }
            }

            // Palette match and write results

            foreach (Color clr in alsColors)
                bwtr.Write((byte)pal.FindClosestEntry(clr));
        }