MadDugan

Spritesheet Export Plugin

Recommended Posts

7/24/2014 - v1.2.2 Minor Code change and recompile to support Paint.NET 4

 

I was in need of a consistent way to create simple, evenly spaced spritesheets from multiple layers in Paint.NET and I figured I would share the plugin.
 

Download
 

DOWNLOAD: https://ssplugin.codeplex.com/releases/view/125730
 
This is a FileType plugin that will generate a PNG formatted file.
 
v1.2.1 -  Add honoring layer visibility.
v1.2.2  - Minor Code change and recompile to support Paint.NET 4

Installation Instructions

  • Verify Paint.NET is not running
  • Copy spritesheet.dll into the /Paint.NET/FileTypes sub-directory
  • Start Paint.NET and verify "SpriteSheet (*.ss.png)" is now an option for "Save as"

If there is a lot of interest, I will consider adding features, but as is, it suits my requirements.
 
Please cite if you use this tool for your application development and I will return the favor by linking to your application.
 
An example of multilayer sprite
post-102196-0-49478800-1364090376_thumb.
 
The resulting file created by exporter
post-102196-0-15765100-1364090385_thumb.

Edited by toe_head2001
Added plugin as an attachment
  • Like 1
  • Upvote 1

Share this post


Link to post
Share on other sites

Hi MadDugan - welcome to the forum :)

 

Your first plugin is very nicely done.  Works just as anticipated without hassle or fuss.  You've made a nice job of it.

 

You might want to mention in your post that it is a filetype plugin and the DLL should go in the /Filetypes/ folder.

Share this post


Link to post
Share on other sites

Hi MadDugan - welcome to the forum :)

 

Your first plugin is very nicely done.  Works just as anticipated without hassle or fuss.  You've made a nice job of it.

 

You might want to mention in your post that it is a filetype plugin and the DLL should go in the /Filetypes/ folder.

 

Thanks for the feedback.  I have made the changes as suggested.

Share this post


Link to post
Share on other sites

v1.2.1

Updated plugin to honor a layer's visibility flag

 

Scenerio where this would be used: Multiple sprite animations in one file for proper centering and resizing then only making visible layers you want to export.

Share this post


Link to post
Share on other sites

v1.2.1

Updated plugin to honor a layer's visibility flag

 

Scenerio where this would be used: Multiple sprite animations in one file for proper centering and resizing then only making visible layers you want to export.

I haven't used either of those, but I highly recommend this one.

Share this post


Link to post
Share on other sites

DennisW mentioned in the ImPDF thread that the SpriteSheet Export plugin is not working. I verfied this and he is right.

 

Maybe a moderator could mark the first post in the thread with 'Not Paint.NET 4 compatible'.

System.TypeLoadException: Could not load type 'PaintDotNet.GdiPlusFileType' from assembly 'PaintDotNet.Data, Version=4.2.5309.33059, Culture=neutral, PublicKeyToken=null'.

   at SpriteSheet.MyFileType.OnSave(Document input, Stream output, SaveConfigToken token, Surface scratchSurface, ProgressEventHandler callback)

   at PaintDotNet.FileType.Save(Document input, Stream output, SaveConfigToken token, Surface scratchSurface, ProgressEventHandler callback, Boolean rememberToken) in d:\src\pdn\paintdotnet\src\Data\FileType.cs:line 284

   at PaintDotNet.Controls.DocumentWorkspace.<>c__DisplayClass30.<DoSave>b__28() in d:\src\pdn\paintdotnet\src\PaintDotNet\Controls\DocumentWorkspace.cs:line 2953

   at PaintDotNet.Functional.Func.Try(Action f) in d:\src\pdn\paintdotnet\src\Base\Functional\Func.cs:line 171

Share this post


Link to post
Share on other sites

This might help.

 

/////////////////////////////////////////////////////////////////////////////////
// paint.net                                                                   //
// Copyright (C) dotPDN LLC, Rick Brewster, and contributors.                  //
// All Rights Reserved.                                                        //
/////////////////////////////////////////////////////////////////////////////////
 
using PaintDotNet.SystemLayer;
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
 
namespace PaintDotNet.Data
{
    /// <summary>
    /// Implements FileType for generic GDI+ codecs.
    /// </summary>
    /// <remarks>
    /// GDI+ file types do not support custom headers.
    /// </remarks>
    internal class GdiPlusFileType
        : FileType
    {
        private ImageFormat imageFormat; 
        public ImageFormat ImageFormat
        {
            get
            {
                return this.imageFormat;
            }
        }
 
        protected override void OnSave(Document input, Stream output, SaveConfigToken token, Surface scratchSurface, ProgressEventHandler callback)
        {
            GdiPlusFileType.Save(input, output, scratchSurface, this.ImageFormat, callback);
        }
 
        public static void Save(Document input, Stream output, Surface scratchSurface, ImageFormat format, ProgressEventHandler callback)
        {
            // flatten the document
            scratchSurface.Clear(ColorBgra.FromBgra(0, 0, 0, 0));
 
            using (RenderArgs ra = new RenderArgs(scratchSurface))
            {
                input.Render(ra, true);
            }
 
            using (Bitmap bitmap = scratchSurface.CreateAliasedBitmap())
            {
                LoadProperties(bitmap, input);
                bitmap.Save(output, format);
            }
        }
 
        public static void LoadProperties(Image dstImage, Document srcDoc)
        {
            LoadProperties(dstImage, srcDoc, _ => true);
        }
 
        public static void LoadProperties(Image dstImage, Document srcDoc, Func<PropertyItem, bool> selectorFn)
        {
            Bitmap asBitmap = dstImage as Bitmap;
 
            if (asBitmap != null)
            {
                // Sometimes GDI+ does not honor the resolution tags that we
                // put in manually via the EXIF properties.
                float dpiX;
                float dpiY;
 
                switch (srcDoc.DpuUnit)
                {
                    case MeasurementUnit.Centimeter:
                        dpiX = (float)Document.DotsPerCmToDotsPerInch(srcDoc.DpuX);
                        dpiY = (float)Document.DotsPerCmToDotsPerInch(srcDoc.DpuY);
                        break;
 
                    case MeasurementUnit.Inch:
                        dpiX = (float)srcDoc.DpuX;
                        dpiY = (float)srcDoc.DpuY;
                        break;
 
                    default:
                    case MeasurementUnit.Pixel:
                        dpiX = 1.0f;
                        dpiY = 1.0f;
                        break;
                }
 
                try
                {
                    asBitmap.SetResolution(dpiX, dpiY);
                }
 
                catch (Exception)
                {
                    // Ignore error
                }
            }
 
            Metadata metaData = srcDoc.Metadata;
 
            foreach (string key in metaData.GetKeys(Metadata.ExifSectionName))
            {
                string blob = metaData.GetValue(Metadata.ExifSectionName, key);
                PropertyItem pi = PdnGraphics.DeserializePropertyItem(blob);
 
                bool include = selectorFn(pi);
 
                if (include)
                {
                    try
                    {
                        dstImage.SetPropertyItem(pi);
                    }
 
                    catch (ArgumentException)
                    {
                        // Ignore error: the image does not support property items
                    }
                }
            }
        }
 
        protected override Document OnLoad(Stream input)
        {
            using (Image image = Image.FromStream(input, false, true))
            {
                Document document = Document.FromGdipImage(image, false);
                return document;
            }
        }
        
        public static ImageCodecInfo GetImageCodecInfo(ImageFormat format)
        {
            ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
 
            foreach (ImageCodecInfo icf in encoders)
            {
                if (icf.FormatID == format.Guid)
                {
                    return icf;
                }
            }
 
            return null;
        }
 
        public GdiPlusFileType(string name, ImageFormat imageFormat, bool supportsLayers, string[] extensions)
            : this(name, imageFormat, supportsLayers, extensions, false)
        {
        }
 
        public GdiPlusFileType(string name, ImageFormat imageFormat, bool supportsLayers, string[] extensions, bool savesWithProgress)
            : base(name, 
                   (supportsLayers ? FileTypeFlags.SupportsLayers : 0) | 
                       FileTypeFlags.SupportsLoading | 
                       FileTypeFlags.SupportsSaving | 
                       (savesWithProgress ? FileTypeFlags.SavesWithProgress : 0),
                   extensions)
        {
            this.imageFormat = imageFormat;
        }
    }
}

 

Share this post


Link to post
Share on other sites

This might help.

 

Hidden Content:
/////////////////////////////////////////////////////////////////////////////////
// paint.net                                                                   //
// Copyright (C) dotPDN LLC, Rick Brewster, and contributors.                  //
// All Rights Reserved.                                                        //
/////////////////////////////////////////////////////////////////////////////////
 
using PaintDotNet.SystemLayer;
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
 
namespace PaintDotNet.Data
{
    /// <summary>
    /// Implements FileType for generic GDI+ codecs.
    /// </summary>
    /// <remarks>
    /// GDI+ file types do not support custom headers.
    /// </remarks>
    internal class GdiPlusFileType
        : FileType
    {
        private ImageFormat imageFormat; 
        public ImageFormat ImageFormat
        {
            get
            {
                return this.imageFormat;
            }
        }
 
        protected override void OnSave(Document input, Stream output, SaveConfigToken token, Surface scratchSurface, ProgressEventHandler callback)
        {
            GdiPlusFileType.Save(input, output, scratchSurface, this.ImageFormat, callback);
        }
 
        public static void Save(Document input, Stream output, Surface scratchSurface, ImageFormat format, ProgressEventHandler callback)
        {
            // flatten the document
            scratchSurface.Clear(ColorBgra.FromBgra(0, 0, 0, 0));
 
            using (RenderArgs ra = new RenderArgs(scratchSurface))
            {
                input.Render(ra, true);
            }
 
            using (Bitmap bitmap = scratchSurface.CreateAliasedBitmap())
            {
                LoadProperties(bitmap, input);
                bitmap.Save(output, format);
            }
        }
 
        public static void LoadProperties(Image dstImage, Document srcDoc)
        {
            LoadProperties(dstImage, srcDoc, _ => true);
        }
 
        public static void LoadProperties(Image dstImage, Document srcDoc, Func<PropertyItem, bool> selectorFn)
        {
            Bitmap asBitmap = dstImage as Bitmap;
 
            if (asBitmap != null)
            {
                // Sometimes GDI+ does not honor the resolution tags that we
                // put in manually via the EXIF properties.
                float dpiX;
                float dpiY;
 
                switch (srcDoc.DpuUnit)
                {
                    case MeasurementUnit.Centimeter:
                        dpiX = (float)Document.DotsPerCmToDotsPerInch(srcDoc.DpuX);
                        dpiY = (float)Document.DotsPerCmToDotsPerInch(srcDoc.DpuY);
                        break;
 
                    case MeasurementUnit.Inch:
                        dpiX = (float)srcDoc.DpuX;
                        dpiY = (float)srcDoc.DpuY;
                        break;
 
                    default:
                    case MeasurementUnit.Pixel:
                        dpiX = 1.0f;
                        dpiY = 1.0f;
                        break;
                }
 
                try
                {
                    asBitmap.SetResolution(dpiX, dpiY);
                }
 
                catch (Exception)
                {
                    // Ignore error
                }
            }
 
            Metadata metaData = srcDoc.Metadata;
 
            foreach (string key in metaData.GetKeys(Metadata.ExifSectionName))
            {
                string blob = metaData.GetValue(Metadata.ExifSectionName, key);
                PropertyItem pi = PdnGraphics.DeserializePropertyItem(blob);
 
                bool include = selectorFn(pi);
 
                if (include)
                {
                    try
                    {
                        dstImage.SetPropertyItem(pi);
                    }
 
                    catch (ArgumentException)
                    {
                        // Ignore error: the image does not support property items
                    }
                }
            }
        }
 
        protected override Document OnLoad(Stream input)
        {
            using (Image image = Image.FromStream(input, false, true))
            {
                Document document = Document.FromGdipImage(image, false);
                return document;
            }
        }
        
        public static ImageCodecInfo GetImageCodecInfo(ImageFormat format)
        {
            ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
 
            foreach (ImageCodecInfo icf in encoders)
            {
                if (icf.FormatID == format.Guid)
                {
                    return icf;
                }
            }
 
            return null;
        }
 
        public GdiPlusFileType(string name, ImageFormat imageFormat, bool supportsLayers, string[] extensions)
            : this(name, imageFormat, supportsLayers, extensions, false)
        {
        }
 
        public GdiPlusFileType(string name, ImageFormat imageFormat, bool supportsLayers, string[] extensions, bool savesWithProgress)
            : base(name, 
                   (supportsLayers ? FileTypeFlags.SupportsLayers : 0) | 
                       FileTypeFlags.SupportsLoading | 
                       FileTypeFlags.SupportsSaving | 
                       (savesWithProgress ? FileTypeFlags.SavesWithProgress : 0),
                   extensions)
        {
            this.imageFormat = imageFormat;
        }
    }
}

Exactly what I needed. Thanks!

Share this post


Link to post
Share on other sites

I tried to use the new version 1.22 and I get this error

 

System.TypeLoadException: Could not load type 'PaintDotNet.GdiPlusFileType' from assembly 'PaintDotNet.Data, Version=4.3.5316.40022, Culture=neutral, PublicKeyToken=null'.

   at SpriteSheet.MyFileType.OnSave(Document input, Stream output, SaveConfigToken token, Surface scratchSurface, ProgressEventHandler callback)

   at PaintDotNet.FileType.Save(Document input, Stream output, SaveConfigToken token, Surface scratchSurface, ProgressEventHandler callback, Boolean rememberToken) in d:\src\pdn\paintdotnet\src\Data\FileType.cs:line 284

   at PaintDotNet.Controls.DocumentWorkspace.<>c__DisplayClass30.<DoSave>b__28() in d:\src\pdn\paintdotnet\src\PaintDotNet\Controls\DocumentWorkspace.cs:line 2954

   at PaintDotNet.Functional.Func.Try(Action f) in d:\src\pdn\paintdotnet\src\Base\Functional\Func.cs:line 174

 

My apologizes I checked things over and I found that the dll was not installed in the correct place. I corrected the install and low and behold it worked. Please forgive my error Great work nice to have it back again.
 
Dennis 
Edited by DennisW

Share this post


Link to post
Share on other sites

 

I tried to use the new version 1.22 and I get this error

System.TypeLoadException: Could not load type 'PaintDotNet.GdiPlusFileType' from assembly 'PaintDotNet.Data, Version=4.3.5316.40022, Culture=neutral, PublicKeyToken=null'.
   at SpriteSheet.MyFileType.OnSave(Document input, Stream output, SaveConfigToken token, Surface scratchSurface, ProgressEventHandler callback)
   at PaintDotNet.FileType.Save(Document input, Stream output, SaveConfigToken token, Surface scratchSurface, ProgressEventHandler callback, Boolean rememberToken) in d:\src\pdn\paintdotnet\src\Data\FileType.cs:line 284
   at PaintDotNet.Controls.DocumentWorkspace.<>c__DisplayClass30.<DoSave>b__28() in d:\src\pdn\paintdotnet\src\PaintDotNet\Controls\DocumentWorkspace.cs:line 2954
   at PaintDotNet.Functional.Func.Try(Action f) in d:\src\pdn\paintdotnet\src\Base\Functional\Func.cs:line 174
My apologizes I checked things over and I found that the dll was not installed in the correct place. I corrected the install and low and behold it worked. Please forgive my error Great work nice to have it back again.
 
Dennis 

 

 

I got notified before your edit and immediately thought maybe it was still finding 1.2.1 somewhere.  Glad to see you resolved it.

 

-M

Share this post


Link to post
Share on other sites

Forgive my ignorance but how do I download this now? (new computer) I could only find the 'archive' file thru your link and that didn't have any .dll files. 

Edited by ZcheK

Share this post


Link to post
Share on other sites

Hi @ZcheK

Welcome to the forum.

Although it says archive - when you click on it it will prompt you to save a FIle with the extension .ZIP

The ZIP file contains the DLL file which you need to extract once downloaded to your hard drive.

Hope this helps :)

 

Share this post


Link to post
Share on other sites

@JLeno

I just downloaded it to have a look and I see what you mean. Here is my copy.

 

 

 

If @Ego Eram Reputo would be so kind to post it to the first entry,  it should resolve any further headaches.

Hope this helps

 

Edited by toe_head2001
removed attachment; thanks
  • Like 1

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.