Jump to content

FileTypePlugin OnCreateSavePropertyCollection

Recommended Posts

I run a game developers group and we are studying the Unity plug in.  The Unity plug in allows you to create terrains from heightmaps.  The heightmap format extension is .RAW but from what I understand it's just an 8bit or 16bit sequence of bytes with no header whatsoever.


So quite literally, for a 513x513 heightmap, your file size if 8 bit is simply 263169.


I ended up writing this code in OnSaveT():


var layer = input.Layers[0] as PaintDotNet.BitmapLayer;

for (int i = 0; i < layer.Surface.Height; i++)
  var row = layer.Surface.GetRow(i);
  var bytes = row.ToByteArray()
    .Where((x, n) => (n + 3) % 4 == 0)

  output.Write(bytes, 0, bytes.Count());
It's terrible and probably will blow up in many situations.  It is just pulling the red channel and writing that byte.  I did import it into Unity and it looks like a success.

My question is I'm looking for some best practices since I imagine users could blow up Paint.Net running through this code.  Second, what do I do with OnCreateSavePropertyCollection() since I feel like I have no save properties to ask for?


I just went with:

return new PropertyCollection(Enumerable.Empty<Property>());
Edited by cagomez
Link to comment
Share on other sites

Objectively: sure, the code is terrible and probably wouldn't scale well to large file sizes.


Subjectively: so what? :) Does it work? (I believe you said it does!) How much effort do you want to put into it? Is your goal to write high performance C#/.NET code, or is your goal to do stuff with game development and Unity?


If you've only got 513 pixels per row, what you're doing isn't really going to be a big deal, especially on modern PCs (2010 or newer). That's only about 2KB per temporary allocation. Paint.NET is pretty aggressive about invoking the garbage collector at the right times, and .NET's garbage collector is pretty good anyway, so I wouldn't worry too much about "blowing up Paint.NET". If you want to learn more ins and outs of writing really performant code in C#/.NET, then by all means, let's start a party on it. But if your goal is to get stuff booted up in Unity, and your plugin already works, then that should be fine for now. If it's too slow then you'll know pretty soon, but it's not like you're running on a Pentium II from 1997, or on a mobile device.


Plus, you can probably get a better return on your time investment by parallelizing the code.

using System.Threading.Tasks;
var layer = input.Layers[0] as PaintDotNet.BitmapLayer;
Task<byte[]>[] tasks = new Task<byte[]>[layer.Surface.Height];
for (int i = 0; i < layer.Surface.Height; ++i)
    tasks[i] = Task.Factory.StartNew(() => layer.Surface.GetRow(i).ToByteArray().Where((x, n) => (n + 3) % 4 == 0).ToArray());
for (int i = 0; i < layer.Surface.Height; ++i)
    byte[] bytes = tasks[i].Result;
    output.Write(bytes, 0, bytes.Length);

(Disclaimer: That code might work ...)


If you don't need any UI for save configuration, just derive from FIleType instead of FileType<T> or PropertyBasedFileType.

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html


Link to comment
Share on other sites

I'm less concerned about performance and more concerned about throwing exceptions that people will submit to YOU.  After all, once I got the initial implementation by simply having VS stub out required interface members, the next thing I did was set breakpoints just to find out what Paint.Net calls.


(Just a few weeks ago I had started that incident over my laptop dual graphics drivers because I mean how could it possibly be my laptop, but sure enough upon updating to latest drivers it's humming along).


And of course my methods threw NotImplementedException.


So I want to avoid bringing down Paint.Net.  Otherwise, you are right... the image is PRESUMED to be black and white, so running the Black and White tool is helpful there.  In fact, man if there was some way I could enforce that before actually saving it would be great, but it's okay if it's not.  And really you see the length of the code.  That's ALL I wrote and it does seem to be creating heightmaps the way Unity expects (notwithstanding that MY heightmaps are worthless but I got tired of tutorials starting with "Open Photoshop").


For me, the best practices are about preventing crashes that look like Paint.Net issues.  I want the plugin to be a good citizen.  For this small task, I agree with you... functionality beats all else.  But people on other forums may want this.

Is there some documentation out there or something I can contribute to?  I got a gist of this only because I read your how to debug post and looked at the code in the screenshots.  That made me guess what I needed to reference and after some trial and error I got this far.  This is likely only because of 14+ years of .NET experience (from whatever night at the hotel at PDC 2000 when they announced .NET).  I don't know that bar is low enough for other developers.

If I am just being obtuse and missing where the developer docs reside, then I wholeheartedly apologize.

Link to comment
Share on other sites

You want to force black and white or gray scale? There's a unary pixelop (desaturateOp) you can use to massage each pixel before you process it. See BoltBaits excellent website here: http://boltbait.com/pdn/CodeLab/help/tutorial3.asp

What sort of height maps do you want? I create height maps for land textures using Perkin Noise (aka Value Noise). Let me know if you want some code.

Link to comment
Share on other sites

To Rick's point, I originally just wanted to see if I could extend Paint.Net to produce the .RAW files the way Unity expects them.  And it works!  So that Is awesome.  And yeah I can easily go on my way.


But I like to contribute whenever I can, and this was getting kind of fun.  So what I figure is perhaps I can use some more features of the plugin model.  For example, since a heightmap has to be grayscale (I believe), why not grayscale it for the user if they haven't done so yet?  I could do that on the save, of course.  Is it possible to, via a plugin, perform that operation on the user's current document?  Just curious.


I am going to see if I can get loading from these same files working.  I'm not sure how useful that REALLY is, but I'd like to see if I can do it.


I was also thinking I could at least consider implementing some other file type... maybe one from a bygone era like PCX purely for the purposes of contributing to getting started documentation.  I was able to get things rolling in Visual Studio, and once Rick pointed out there were other types to derive from, I did feel more comfortable spelunking the Object Browser to see what the other classes were and what I might implement.


My weakness here is I actually know very little about graphics and my days of dealing with file formats are behind me (I used to learn a great deal about them to program small games, but nowadays that concern is taken from you in nearly any engine or framework).  Still, I am a Windows platform developer by day and I've been using Paint.Net for a very, very long time.  I feel like I came across your project a very long time ago and it's been in my toolchest for so long that I would be proud to contribute in any small way.

Link to comment
Share on other sites

Link to comment
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.

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.

  • Create New...