Kernel/convolution matrix processing

A project log for Supercon2017 Enhanced imagefx

I added a few fx, enhanced the UI (including a HW hack), and allowed the fx'd images to be saved.

Wikipedia can explain Kenel's for image processing better than I can:

Convolution is the process of adding each element of the image to its local neighbors, weighted by the kernel. This is related to a form of mathematical convolution.

The existing code seems to do this in the dither function but it's not really reusable:

```for(y=0 ; y != ypixels; y++) {
for (x = 0; x != xpixels; x++) {
inptr = cambuffer + bufstart + x + y * xpixels;
op = *inptr;
if (op > 0x80) np = 0xff; else np = 0;
er = op - np;
*inptr = np;
inptr++; //right
z = (int) *inptr + er * 7/16;
if (z<0) z = 0; else if (z > 255) z = 255;
*inptr = z;
/* snip */
} // x
} // y```

Here's an example of the Boxcar (smoothing) Kernel. Unlike the example in Wikipedia, these are not 1's - but I was trying to get some variation, as we'll see later.

```const Kernel_t BoxcarKernel = {
{7, 7, 7},
{7, 7, 7},
{7, 7, 7},
};```

One thing I learned the hard way was you can't modify the matrix in-place; in other words the result of the calculation has to go into a new array. But where to put it? The answer was in the dither function but I failed to realize that at the time. I tied allocating the entire frame buffer again, but there's not sufficient RAM for that. The trick is that the frame buffer is allocated for RGB with 1 byte per color per pixel, while the effects only work on Greyscale - 1 byte total per pixel.

I don't know about you but since I'm only an occasional C coder, so things like pointers, addresses and the like really hurt my brain.

So I planned to pass a matrix (only a 3x3 array - in the interest of performance), along with the from and to buffers, and a some other values to help in the calculations.  Here's the function signature:

`void convolution(mono_buffer_t sourceBuffer, mono_buffer_t targetBuffer, Kernel_t Kernel, signed int scaler, signed int offset)`

Then it's just a matter of doing some prep, and calling the convolution code:

`convolution(cambuffer + bufstart, cambuffer + bufstart + cambuffmono_offset, Kernel, 5, 0);`

And then displaying it - just be tweaking the existing display code to work with my "new" buffer definitions:

```monopalette (0,255);
dispimage(0, 12, xpixels, ypixels, (img_mono | img_revscan), cambuffer + bufstart + cambuffmono_offset);

```