Jump to content

Pixelation filter how to


ShadowDragon
 Share

Recommended Posts

Hey guys, I LOVE your program!

I work at a school for video game programming and I recommend your tool to all the students when they are working on their games.

I'm having difficulty finding information on how to do a pixelation filter (which you guys have working in your program under Effects->Distort->Pixelate).

I'm working on my own game and would really LOVE to get that effect going in real time using a post process pixel shader.

Can you tell me the algorithm you are using or point me to somewhere I can find that information?

Thanks in advance!

Link to comment
Share on other sites

Moved to General Coding

 

The Doctor: There was a goblin, or a trickster, or a warrior... A nameless, terrible thing, soaked in the blood of a billion galaxies. The most feared being in all the cosmos. And nothing could stop it, or hold it, or reason with it. One day it would just drop out of the sky and tear down your world.
Amy: But how did it end up in there?
The Doctor: You know fairy tales. A good wizard tricked it.
River Song: I hate good wizards in fairy tales; they always turn out to be him.

Link to comment
Share on other sites

src\Effects\PixelateEffect.cs

/////////////////////////////////////////////////////////////////////////////////
// Paint.NET                                                                   //
// Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors.     //
// Portions Copyright (C) Microsoft Corporation. All Rights Reserved.          //
// See src/Resources/Files/License.txt for full licensing and attribution      //
// details.                                                                    //
// .                                                                           //
/////////////////////////////////////////////////////////////////////////////////

using PaintDotNet.IndirectUI;
using PaintDotNet.PropertySystem;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace PaintDotNet.Effects
{
   [EffectTypeHint(EffectTypeHint.Fast)]
   public sealed class PixelateEffect 
       : PropertyBasedEffect
   {
       public static string StaticName
       {
           get
           {
               return PdnResources.GetString("PixelateEffect.Name");
           }
       }

       public PixelateEffect() 
           : base(StaticName,
                  PdnResources.GetImageResource("Icons.PixelateEffect.png").Reference,
                  SubmenuNames.Distort,
                  EffectFlags.Configurable)
       {
       }

       public enum PropertyNames
       {
           CellSize
       }

       protected override PropertyCollection OnCreatePropertyCollection()
       {
           List props = new List();

           props.Add(new Int32Property(PropertyNames.CellSize, 2, 1, 100));

           return new PropertyCollection(props);
       }

       protected override ControlInfo OnCreateConfigUI(PropertyCollection props)
       {
           ControlInfo configUI = CreateDefaultConfigUI(props);

           configUI.SetPropertyControlValue(PropertyNames.CellSize, ControlInfoPropertyNames.DisplayName, PdnResources.GetString("PixelateEffect.ConfigDialog.SliderLabel"));
           // TODO: units label?
           //aecg.SliderUnitsName = PdnResources.GetString("PixelateEffect.ConfigDialog.SliderUnitsName");

           return configUI;
       }

       private ColorBgra ComputeCellColor(int x, int y, RenderArgs src, int cellSize)
       {
           Rectangle cell = GetCellBox(x, y, cellSize);
           cell.Intersect(src.Bounds);

           int left = cell.Left;
           int right = cell.Right - 1;
           int bottom = cell.Bottom - 1;
           int top = cell.Top;

           ColorBgra colorTopLeft = src.Surface[left, top];
           ColorBgra colorTopRight = src.Surface[right, top];
           ColorBgra colorBottomLeft = src.Surface[left, bottom];
           ColorBgra colorBottomRight = src.Surface[right, bottom];

           ColorBgra c = ColorBgra.BlendColors4W16IP(colorTopLeft, 16384, colorTopRight, 16384, colorBottomLeft, 16384, colorBottomRight, 16384);

           return c;
       }

       private Rectangle GetCellBox(int x, int y, int cellSize)
       {
           int widthBoxNum = x % cellSize;
           int heightBoxNum = y % cellSize;
           Point leftUpper = new Point(x - widthBoxNum, y - heightBoxNum);
           Rectangle returnMe = new Rectangle(leftUpper, new Size(cellSize, cellSize));
           return returnMe;
       }

       private int cellSize;
       protected override void OnSetRenderInfo(PropertyBasedEffectConfigToken newToken, RenderArgs dstArgs, RenderArgs srcArgs)
       {
           this.cellSize = newToken.GetProperty(PropertyNames.CellSize).Value;
           base.OnSetRenderInfo(newToken, dstArgs, srcArgs);
       }

       protected unsafe override void OnRender(Rectangle[] rois, int startIndex, int length)
       {
           for (int i = startIndex; i < startIndex + length; ++i)
           {
               Rectangle rect = rois[i];

               for (int y = rect.Top; y < rect.Bottom; ++y)
               {
                   int yEnd = y + 1;

                   for (int x = rect.Left; x < rect.Right; ++x)
                   {
                       Rectangle cellRect = GetCellBox(x, y, this.cellSize);
                       cellRect.Intersect(DstArgs.Bounds);
                       ColorBgra color = ComputeCellColor(x, y, SrcArgs, this.cellSize);

                       int xEnd = Math.Min(rect.Right, cellRect.Right);
                       yEnd = Math.Min(rect.Bottom, cellRect.Bottom);

                       for (int y2 = y; y2 < yEnd; ++y2)
                       {
                           ColorBgra *ptr = DstArgs.Surface.GetPointAddressUnchecked(x, y2);

                           for (int x2 = x; x2 < xEnd; ++x2)
                           {
                               ptr->Bgra = color.Bgra;
                               ++ptr;
                           }
                       }

                       x = xEnd - 1;
                   }

                   y = yEnd - 1;
               }
           }
       }
   }
}

Link to comment
Share on other sites

The easiest way is probably to create a new resized Bitmap object and draw rectangles on a new Bitmap object for each pixel (If you just lower the resolution and increase it it will blur it).

Edit:

Not from the pdn code - but try editing this for your purposes:

int factor = 10;

           Bitmap bpic = new Bitmap(Properties.Resources.sbavi08,
               Properties.Resources.sbavi08.Width/factor,Properties.Resources.sbavi08.Height/factor);

           Bitmap b = new Bitmap(Properties.Resources.sbavi08.Width,
               Properties.Resources.sbavi08.Height);
           Graphics g = Graphics.FromImage(;

           for (int county = 0; county < bpic.Height; county++)
           {
               for (int countx = 0; countx < bpic.Width; countx++)
               {
                   g.FillRectangle(new SolidBrush(bpic.GetPixel(countx, county)),
                       countx*factor, county*factor, factor, factor);
               }
           }

           pictureBox1.Image = b;

KaHuc.png
Link to comment
Share on other sites

I must be missing something but isn't this almost trivial.

Each pixelation cell has a number of pixels in it, eg 2x2. For each cell find the average of the colours of the pixels in it and set the colour of all the pixels to this colour.

Link to comment
Share on other sites

Each pixelation cell has a number of pixels in it, eg 2x2. For each cell find the average of the colours of the pixels in it and set the colour of all the pixels to this colour.

That's the way GIMP does it, yes. But Paint.NET only takes the average of the four corner pixels of the cell.

xZYt6wl.png

ambigram signature by Kemaru

[i write plugins and stuff]

If you like a post, upvote it!

Link to comment
Share on other sites

Each pixelation cell has a number of pixels in it, eg 2x2. For each cell find the average of the colours of the pixels in it and set the colour of all the pixels to this colour.

That's the way GIMP does it, yes. But Paint.NET only takes the average of the four corner pixels of the cell.

The advantage of the code I published it that its faster because it uses lower-level functions which do the job much more quickly.

KaHuc.png
Link to comment
Share on other sites

Yeeeahhh.... no.

If you're doing it all at once (single-threaded render, not using Paint.NET's plugin architecture) yours will be about the same render time as Paint.NET's.

If you're sticking that in a Paint.NET effect, well, lord have mercy.

And really, does your post really have anything to do with what you quoted? Nope...

xZYt6wl.png

ambigram signature by Kemaru

[i write plugins and stuff]

If you like a post, upvote it!

Link to comment
Share on other sites

Yeeeahhh.... no.

If you're doing it all at once (single-threaded render, not using Paint.NET's plugin architecture) yours will be about the same render time as Paint.NET's.

If you're sticking that in a Paint.NET effect, well, lord have mercy.

And really, does your post really have anything to do with what you quoted? Nope...

At the risk of looking stupid again, if you use the inbuilt .NET functions then it probably averages the whole box - leaving better quality. IMHO it also makes the code cleaner.

KaHuc.png
Link to comment
Share on other sites

Say you have a 10x10 cell. Getting the values of 4 pixels from that -- unless you are some insanely brilliant God-Of-Optimization (and the people behind the .NET Framework are smart, but not that smart) -- will always be faster than getting the values of 100. Not only is there the drag of memory access in a non-linear order (unless your image is exactly 10 pixels wide and you're using pointers), but you need to allocate more of your own memory space to accommodate the larger sample size, which will also slow you down. Not a lot, but it's still there.

And as for being "better quality," well it's a bloody Pixelation filter. Using a different algorithm might result in different-looking results, but better results? You're making a bunch of big single-colored squares, for cryin' out loud! Someone would have to be all kinds of stupid to make a Pixelizer that looked "bad."

xZYt6wl.png

ambigram signature by Kemaru

[i write plugins and stuff]

If you like a post, upvote it!

Link to comment
Share on other sites

how about drawing to a somewhat smaller render target first and then drawing (screen-aligned quad) that with pointsampling? that's a pixelation algorithm for free (well, free.. you would have to draw to a render target first..)

simple, easy, no coding involved :)

If you'd do it as fullscreen effect it would also require a render target - you have to sample neighboring pixels after all. It would also be harder since for each pixel you first have to find out what block they are in anyway (simply taking the average of 3 texels '1 pixel away' and the current one will only blur)

edit: we are talking about hardware shaders here right? (not C# code)

I would write plugins, if I knew what kind of plugins were needed.. :(

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.

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.

 Share

×
×
  • Create New...