public unsafe bool Bake(PipelineBakeContext context)
        {
            Console.WriteLine("Art.Bake: " + context.ContentPath);

            float umin = 0;
            float vmin = 0;
            float umax = 1;
            float vmax = 1;

            var path = context.Depends["source"];

            var imageBuffer = ImageLoading.LoadImage(path);

            //TODO: we can only handle certain input formats here (no compressed formats)
            //but we can output any format (after we crop it)

            int width = imageBuffer.Width;
            int height = imageBuffer.Height;
            int physwidth = width;
            int physheight = height;
            int ox = 0, oy = 0;

            //NOTE: EVERYTHING BELOW IS EXPERIMENTAL. ITS A TOTAL MESS

            //TODO - apply art-specific operations (cropping, etc.)
            //TODO - make controllable
            //TODO - handle errors
            var conversionResult = imageBuffer.ConvertToAlphaProcessableFormat(false);

            bool doTrim = true;

            if (conversionResult.ConversionResult == ConversionResult.Error_InputFormatHasNoAlpha)
            {
                doTrim = false;
            }

            if (doTrim)
            {
                //accept the converted image
                imageBuffer = conversionResult.ResultImage;

                var alphaTrimResult = imageBuffer.AlphaTrim();
                imageBuffer = alphaTrimResult.ResultImage;

                ox         = alphaTrimResult.x;
                oy         = alphaTrimResult.y;
                physwidth  = alphaTrimResult.Width;
                physheight = alphaTrimResult.Height;
            }

            bool doPadPow2 = true;

            if (!imageBuffer.IsAlphaProcessableFormat())
            {
                doPadPow2 = false;
            }
            if (doPadPow2)
            {
                int widthRound  = PipelineMath.TextureUptoPow2(physwidth);
                int heightRound = PipelineMath.TextureUptoPow2(physheight);
                if (widthRound != physwidth || heightRound != physheight)
                {
                    imageBuffer = imageBuffer.ExpandDownRight(widthRound, heightRound);
                }
            }

            var fmtAttribute = context.Attributes.FirstOrDefault(a => a is TextureFormatAttribute);

            if (fmtAttribute != null)
            {
                var toFormat = ((TextureFormatAttribute)fmtAttribute).Format;

                ImageConversionContext imageContext = new ImageConversionContext();
                imageContext.From     = imageBuffer;
                imageContext.NewAlpha = 0xFF;
                imageContext.ToFormat = toFormat;
                ImageLoading.Convert(imageContext);
                imageBuffer = imageContext.Output;
            }

            umax = (ox + physwidth) / imageBuffer.Width;
            vmax = (oy + physheight) / imageBuffer.Height;

            //the texture goes first...
            var textureBakingContext = new PipelineConnector_TextureBaking()
            {
                Image  = imageBuffer,
                Writer = context.BakedWriter
            };

            context.PipelineConnector.BakeTexture(textureBakingContext);

            //..then art-specific stuff
            context.BakedWriter.Write(width);
            context.BakedWriter.Write(height);
            context.BakedWriter.Write(ox);
            context.BakedWriter.Write(oy);
            context.BakedWriter.Write(umin);
            context.BakedWriter.Write(vmin);
            context.BakedWriter.Write(umax);
            context.BakedWriter.Write(vmax);

            return(true);
        }
        public static void Convert(ImageConversionContext context)
        {
            if (context.From.Format == context.ToFormat)
            {
                return;
            }

            var ret = new ImageBuffer();

            ret.Format = context.ToFormat;

            int width  = ret.Width = context.From.Width;
            int height = ret.Height = context.From.Height;

            //reminder: we should intermediate-convert where reasonable to a standard format (i.e. RGBA8) so that we have fewer permutations to code
            //(needs to be done in a better way than this)
            //we can end up with permutations for the basic formats anyway. it will be more rare stuff that's obviously ridiculous to do without the intermediate stage
            if (context.From.Format == TextureFormat.BGRA8 && context.ToFormat == TextureFormat.RGB8)
            {
                var intermediateContext = new ImageConversionContext();
                intermediateContext.From     = context.From;
                intermediateContext.ToFormat = TextureFormat.RGBA8;
                Convert(intermediateContext);
                context.From = intermediateContext.Output;
            }

            var src = context.From.Data;

            if (context.From.Format == TextureFormat.R8 && context.ToFormat == TextureFormat.RGBA8)
            {
                context.Output = ret;

                byte[] dst     = ret.Data = new byte[width * height * 4];
                uint[] palette = context.From.Palette;

                for (int didx = 0, sidx = 0, y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++)
                    {
                        uint c = palette[src[sidx++]];
                        dst[didx++] = (byte)(c >> 0);
                        dst[didx++] = (byte)(c >> 8);
                        dst[didx++] = (byte)(c >> 16);
                        dst[didx++] = (byte)(c >> 24);
                    }
                }
            }

            if (context.From.Format == TextureFormat.BGRA8 && context.ToFormat == TextureFormat.RGBA8)
            {
                context.Output = ret;

                byte[] dst = ret.Data = new byte[width * height * 4];

                for (int didx = 0, sidx = 0, y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++)
                    {
                        byte b = (byte)(src[sidx++]);
                        byte g = (byte)(src[sidx++]);
                        byte r = (byte)(src[sidx++]);
                        byte a = (byte)(src[sidx++]);
                        dst[didx++] = r;
                        dst[didx++] = g;
                        dst[didx++] = b;
                        dst[didx++] = a;
                    }
                }
            }

            if (context.From.Format == TextureFormat.BGR8 && context.ToFormat == TextureFormat.RGB8)
            {
                context.Output = ret;

                byte[] dst = ret.Data = new byte[width * height * 3];

                for (int didx = 0, sidx = 0, y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++)
                    {
                        byte b = (byte)(src[sidx++]);
                        byte g = (byte)(src[sidx++]);
                        byte r = (byte)(src[sidx++]);
                        dst[didx++] = r;
                        dst[didx++] = g;
                        dst[didx++] = b;
                    }
                }
            }

            if (context.From.Format == TextureFormat.BGR8 && context.ToFormat == TextureFormat.RGBA8)
            {
                context.Output = ret;

                byte   a   = context.NewAlpha;
                byte[] dst = ret.Data = new byte[width * height * 4];

                for (int didx = 0, sidx = 0, y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++)
                    {
                        byte b = (byte)(src[sidx++]);
                        byte g = (byte)(src[sidx++]);
                        byte r = (byte)(src[sidx++]);
                        dst[didx++] = r;
                        dst[didx++] = g;
                        dst[didx++] = b;
                        dst[didx++] = a;
                    }
                }
            }

            if (context.From.Format == TextureFormat.RGBA8 && context.ToFormat == TextureFormat.RGB8)
            {
                context.Output = ret;

                byte[] dst = ret.Data = new byte[width * height * 3];

                for (int didx = 0, sidx = 0, y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++)
                    {
                        byte r = (byte)(src[sidx++]);
                        byte g = (byte)(src[sidx++]);
                        byte b = (byte)(src[sidx++]);
                        sidx++;
                        dst[didx++] = r;
                        dst[didx++] = g;
                        dst[didx++] = b;
                    }
                }
            }
        }         //Convert()