Jump to content
Paint.NET 5.1 is now available! ×

Recommended Posts

Posted (edited)

Hello all,

 

I need to make a FileExport plugin which export into a custom format, that is a binary file with 24 bits per pixel: 16 bits for RGB565 and 8bits for the alpha channel. No metadata, no header, just an array of bytes.

 

The big problem is, I have absolutely no idea how to do this... I saw some sample plugins but they all look simple (even if I don't understand most of the code...), using things like: PixelFormat.Bgr565 or PixelFormats.Bgra32. Obviously my custom pixel format isn't in the list.

 

 

For now, I just would like an example how to get pixels RGBA colors inside the OnSave(T?) callback. Then I can try myself to convert it to the format I need. And then I will probably come back here to ask other questions about how to actually write in the output file.

 

You guessed, I am totally lost...

 

Any help appreciated!

 

 

Edited by guix
Posted (edited)

Just flatten the input layers to the scratchsurface and write the pixels to the output stream.

            input.Flatten(scratchSurface);

            for (int row = 0; row < scratchSurface.Height; row++)
            {
                for (int column = 0; column < scratchSurface.Width; column++)
                {
                    ColorBgra bgra = scratchSurface[column, row];
                    // write pixel to output stream
                }
            }

Edited by midora
  • Upvote 1

midoras signature.gif

Posted (edited)

Hello midora, thank you for this. At least a start, let me try some things :)

 

Edit: I only did that for now as a quick test:

output.WriteByte(bgra.R);
output.WriteByte(bgra.G);
output.WriteByte(bgra.;
output.WriteByte(bgra.A);

And it worked... I can't believe how easy it was (but I couldn't have done it without your code) ;)

Edited by guix
Posted

Flatten just merges all layers to one surface.

 

If you need the data of all layers do:

            foreach (BitmapLayer layer in input.Layers)
            {
                for (int row = 0; row < layer.Height; row++)
                {
                    for (int column = 0; column < layer.Width; column++)
                    {
                        ColorBgra bgra = layer.Surface[column, row];
                    }
                }
            }

Maybe I should add some code to ExamplePropertyBasedFileType template.

midoras signature.gif

Posted

Thank you very much Midora. I was under the impression that plugins don't have access to any layer except the selected layer. The ability to use the information from other layers surprises me, and seems to open some possibilities to do things with plugins I didn't know could be done.

Posted (edited)

I can see not allowing writing the other layers -- that shouldn't be allowed -- but I don't see why reading the other layers isn't allowed. It may well be that there's no way of enforcing no-writing if reading is allowed. But there are lots of things one could do in a plugin that aren't allowed, such a writing to pixels outside the passed slice of the current layer. I've seen a number of kludgy implementations of plugin functions using the clipboard that could be done much more elegantly and conveniently using another layer.

 

Added: Actually, by using a C# property or indexer, reading could be allowed and writing prevented. Disregarding syntatic sugar, read access could be done through an old-fashioned function call.

Edited by MJW
Posted (edited)

Hello again,

 

Nice to know that we can save layers separately, even if I don't need it actually ;)

 

So I managed to make a plugin that can save and load the custom format, which is great news :) (actually, just RGBA8888 but I don't have problem converting from/to my custom format "RGBA5658")

 

Basically:

protected override Document OnLoad(Stream input)
{
    // The 4 first bytes of the file are the size of the image in pixels
    UInt16 sizeX = (UInt16)( (input.ReadByte() << 8) | input.ReadByte() );
    UInt16 sizeY = (UInt16)( (input.ReadByte() << 8) | input.ReadByte() );
    Bitmap bmp = new Bitmap(sizeX, sizeY, PixelFormat.Format32bppArgb);


    for (int column = 0; column < sizeY; column++)
    {
        for (int row = 0; row < sizeX; row++)
        {
            byte r = (byte)input.ReadByte();
            byte g = (byte)input.ReadByte();
            byte b = (byte)input.ReadByte();
            byte a = (byte)input.ReadByte();

            bmp.SetPixel(row, column, Color.FromArgb((int)((a << 24) | (r << 16) | (g << 8) | );
        }
    }
    return Document.FromImage(bmp);
}

protected override void OnSave(Document input, Stream output, SaveConfigToken token, Surface scratchSurface, ProgressEventHandler callback)
{
    //BinaryWriter binWriter = new BinaryWriter(output);

    input.Flatten(scratchSurface);

    UInt16 sizeX = (UInt16)input.Width;
    UInt16 sizeY = (UInt16)input.Height;

    output.WriteByte((byte)(sizeX >> 8));
    output.WriteByte((byte)(sizeX & 0xFF));
    output.WriteByte((byte)(sizeY >> 8));
    output.WriteByte((byte)(sizeY & 0xFF));

    for (int row = 0; row < scratchSurface.Height; row++)
    {
        for (int column = 0; column < scratchSurface.Width; column++)
        {
            ColorBgra bgra = scratchSurface[column, row];

            output.WriteByte(bgra.R);
            output.WriteByte(bgra.G);
            output.WriteByte(bgra.;
            output.WriteByte(bgra.A);

            //binWriter.Write( packRGBA8888(bgra.R, bgra.G, bgra.B, bgra.A) );
        }
    }

    //binWriter.Dispose();
} 

It works well enough for now, I'm just wondering if there is a better/faster way of doing the Load(). Any tips?

 

Also another questions:

 

What is the difference between OnSave and OnSaveT ?

Does scratchSurface.Height and scratchSurface.Width always equals input.Height and input.Width ?

 

Thanks.

Edited by guix
Posted

Because you are writing the size at the beginning of the file you are able to allocate memory and load all pixels in one block.

Use unsafe pointers to convert to the bitmap if speed is important and your algorithm has been proven.

Write a magic at the beginning of the file and check it on load.

midoras signature.gif

Posted (edited)

Sorry midora I don't understand what you said... But no problem, the code is still very fast, load/save speed isn't an issue at all.

 

 

Meanwhile I have made a lot of progress, I have added an optional RLE-like algorithm (2 versions of it), this was easy to make and works quite well, sometimes produce a smaller file than equivalent 24bits PNG. Actually I'm making tests to see what is the best compromise filesize/code speed (these are images that will be loaded on a small TFT display controlled by an Arduino, I'm making a GUI library for it).

 

 

But I would like to make an option "Autodetect" in the Save dialog, that will test some output modes (ex: with or without RLE v1, or v2...), and choose the one that makes a smaller file. Similar to the PNG Save dialog I guess.

 

How can I do that, is it at least possible with plugins?

 

 

Edit: auto detection done  ;)

Edited by guix
Posted

How can I change the extension of the file when saving? For ex if encoding method 1 is used, set extension to ".xg1", etc... Is it possible?

Posted

How can I change the extension of the file when saving? For ex if encoding method 1 is used, set extension to ".xg1", etc... Is it possible?

 

It is not possible for a plugin to do this.

PdnSig.png

Plugin Pack | PSFilterPdn | Content Aware Fill | G'MICPaint Shop Pro Filetype | RAW Filetype | WebP Filetype

The small increase in performance you get coding in C++ over C# is hardly enough to offset the headache of coding in the C++ language. ~BoltBait

 

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.

×
×
  • Create New...