News:

11 March 2016 - Forum Rules

Main Menu

Is this possible? (emulator graphics filter idea)

Started by PresidentLeever, November 17, 2013, 11:17:13 AM

Previous topic - Next topic

PresidentLeever

Sorry if this is too off topic or noobish. I was messing around with filters in Regen the other day, trying to make dither-heavy games look good and wasn't quite happy with any of them as you either get a blurry image or the blending of the dither patterns doesn't happen. So I made these rough sketches below, combining unfiltered and NTSC filtered graphics.

https://plus.google.com/photos/110507989581185950715/albums/5947251753240581329/5947251757700267282?pid=5947251757700267282&oid=110507989581185950715

From left to right (Regen and snes9x screns): no filter, ntsc tv filter by blarrg, rough combo mockup by me, snes screenshot comparison
(Unfortunately the quality isn't great in google photos but if you zoom in you should see the differences. Nevermind the brightness difference, I had to adjust brightness manually since the brighten feature didn't work with filtered screenshot dumps in Regen)

Is this kind of result possible with a general filter, or would it need to be more of a case by case thing with a new setting per game or even per background?

Mini-reviews, retro sound chip tribute, romhacks and general listage at my site: Mini-Revver.

Jorpho

Maybe you should just tell us what the difference is, because I for one can't really tell.

If you're talking about applying different filters to the sprites and backgrounds, there will almost certainly be no solution that can be neatly, automatically applied to every game.
This signature is an illusion and is a trap devisut by Satan. Go ahead dauntlessly! Make rapid progres!

Starscream

The screens are too small. But take a look at the stop sign in the second page (Aladdin shots) for example, there you can see the difference.


henke37

The missing information is what you did in the mockup.

PresidentLeever

#4
Oh, I thought most people were familiar with "ctrl" + "+" here. I'll post a better one in a bit.

Here:
http://postimg.org/image/4kip2ihqv/full/
http://postimg.org/image/3kv5f36ip/full/
http://postimg.org/image/xlwomrxw7/full/

Quote from: Jorpho on November 22, 2013, 09:12:50 AM
If you're talking about applying different filters to the sprites and backgrounds, there will almost certainly be no solution that can be neatly, automatically applied to every game.

I see, that's a shame. I figured it might work since you can turn on and off layers in one of the MD emulators.
Mini-reviews, retro sound chip tribute, romhacks and general listage at my site: Mini-Revver.

Jorpho

Sure, but not every game necessarily uses the same hardware layer to do the same thing.
This signature is an illusion and is a trap devisut by Satan. Go ahead dauntlessly! Make rapid progres!

henke37

Some games even use the same background layer for multiple things at once. Or at least different things at different times.

PresidentLeever

Please tread lightly on my dreams of visual perfection MD style.
Mini-reviews, retro sound chip tribute, romhacks and general listage at my site: Mini-Revver.

Bisqwit

I have actually had some success in the area of undithering algorithms in the past.

Here's the source code of a program I once wrote to undither a picture.
#include <gd.h>
#include <stdio.h>
#include <vector>
#include <assert.h>
#include <algorithm>
#include <cmath>

#define G(x) std::pow( (x)/255.0, 2.2)
#define Un(x) 255*std::pow( (x), 1/2.2)

static unsigned avg4pixels(unsigned a,unsigned b,unsigned c,unsigned d)
{
    int r1 = (a>>16)&0xFF, g1 = (a>>8)&0xFF, b1 = (a>>0)&0xFF;
    int r2 = (b>>16)&0xFF, g2 = (b>>8)&0xFF, b2 = (b>>0)&0xFF;
    int r3 = (c>>16)&0xFF, g3 = (c>>8)&0xFF, b3 = (c>>0)&0xFF;
    int r4 = (d>>16)&0xFF, g4 = (d>>8)&0xFF, b4 = (d>>0)&0xFF;
    int rr = Un((G(r1)+G(r2)+G(r3)+G(r4))/4);
    int gg = Un((G(g1)+G(g2)+G(g3)+G(g4))/4);
    int bb = Un((G(b1)+G(b2)+G(b3)+G(b4))/4);
    unsigned res = (rr<<16) + (gg<<8) + (bb);
    //printf("Averages %06X,%06X,%06X,%06X, becomes %06X\n", a,b,c,d,res);
    return res;
}

static bool IsPair(unsigned pix1,unsigned pix2)
{
    int r1 = (pix1 >> 16) & 0xFF, g1 = (pix1 >> 8) & 0xFF, b1 = (pix1) & 0xFF;
    int r2 = (pix2 >> 16) & 0xFF, g2 = (pix2 >> 8) & 0xFF, b2 = (pix2) & 0xFF;
    int rdiff = std::abs(r1-r2);
    int gdiff = std::abs(g1-g2);
    int bdiff = std::abs(b1-b2);
    return (rdiff < 20 && gdiff < 12 && bdiff < 50)
       || (pix1 == 0xFC0000 && pix2 == 0x0034DC)
       || (pix2 == 0xFC0000 && pix1 == 0x0034DC);
}

static const unsigned NotOptimized = 0x7B123456;
static unsigned CheckOptimize(
    unsigned a,unsigned b,
    unsigned c,unsigned d,
    int recursion = 0)
{
    if((IsPair(a,d) && (recursion <= 1 || IsPair(b,c)))
    || (IsPair(b,c) && (recursion <= 1 || IsPair(a,d)))
      )
    {
        if(a != b || a != c || a != d
        || b != c || b != d
        || c != d)
            return avg4pixels(a,b,c,d);
    }
    return NotOptimized;
}

int main(int argc, char** argv)
{
    FILE* fp = fopen(argv[1], "rb");
    gdImagePtr srcim = gdImageCreateFromPng(fp);
    fclose(fp);

    unsigned w = gdImageSX(srcim), h = gdImageSY(srcim);
    gdImagePtr im = gdImageCreateTrueColor(w, h);

    #define GetP(x,y) gdImageGetTrueColorPixel(srcim, x,y)
    #define PSet(x,y,c) gdImageSetPixel(im, x,y,c)

    std::vector<bool> optimized(w*h, false);
    for(unsigned x=0; x<w; ++x)
        for(unsigned y=0; y<h; ++y)
        {
            // Try each of these four patterns
            // * = current pixel
            // . = other pixel which is identical to this one
            // x = two other pixels
            //  Zx    zX   zx   zx
            //  xy    xy   xY   Xy
            if(optimized[y*w+x]) continue;

            for(int xo=-1; xo<=+1; xo+=2)
                for(int yo=-1; yo<=+1; yo+=2)
                    if(int(x)+xo >= 0 && int(x)+xo < int(w)
                    && int(y)+yo >= 0 && int(y)+yo < int(h))
                    {
                        unsigned color = CheckOptimize(
                            GetP(x,   y   ),
                            GetP(x+xo,y   ),
                            GetP(x,   y+yo),
                            GetP(x+xo,y+yo));
                        if(color != NotOptimized)
                        {
                            PSet(x,   y,   color); optimized[(y   )*w+(x   )] = true;
                            PSet(x+xo,y,   color); optimized[(y   )*w+(x+xo)] = true;
                            PSet(x,   y+yo,color); optimized[(y+yo)*w+(x   )] = true;
                            PSet(x+xo,y+yo,color); optimized[(y+yo)*w+(x+xo)] = true;
                            goto done2x2;
                        }
                    }
            PSet(x,y, GetP(x,y));
           done2x2:;
        }

    // Then do the same using 2x2 pixels
    bool changed=true;
    for(int recursion=1; recursion<=1 && changed; ++recursion)
    {
        changed=false;

        gdImageDestroy(srcim);
        srcim = im;
        im = gdImageCreateTrueColor(w,h);

        std::vector<bool> optimized2(w*h, false);
        for(unsigned x=0; x<w; ++x)
            for(unsigned y=0; y<h; ++y)
            {
                if(optimized2[y*w+x]) continue;
                if(x+1 < w && y+1 < h
                && IsPair(GetP(x,y), GetP(x,y+1))
                && IsPair(GetP(x,y), GetP(x+1,y))
                && IsPair(GetP(x,y), GetP(x+1,y+1)))
                {
                    for(int xo=+2; xo>=-2; xo-=4)
                        for(int yo=+2; yo>=-2; yo-=4)
                        if(int(x)+xo >= 0 && int(x)+xo+1 < int(w)
                        && int(y)+yo >= 0 && int(y)+yo+1 < int(h))
                        {
                            if(IsPair(GetP(x+xo,y), GetP(x+xo,  y+1))
                            && IsPair(GetP(x+xo,y), GetP(x+xo+1,y))
                            && IsPair(GetP(x+xo,y), GetP(x+xo+1,y+1))
                            //
                            && IsPair(GetP(x+xo,y+yo), GetP(x+xo,  y+yo+1))
                            && IsPair(GetP(x+xo,y+yo), GetP(x+xo+1,y+yo))
                            && IsPair(GetP(x+xo,y+yo), GetP(x+xo+1,y+yo+1))
                            //
                            && IsPair(GetP(x,y+yo), GetP(x,  y+yo+1))
                            && IsPair(GetP(x,y+yo), GetP(x+1,y+yo))
                            && IsPair(GetP(x,y+yo), GetP(x+1,y+yo+1)))
                            {
                                int x1 = xo<0 ? x+xo : x, x2 = xo<0 ? x+1 : (x+xo+1);
                                int y1 = yo<0 ? y+yo : y, y2 = yo<0 ? y+1 : (y+yo+1);
                                /*
                                printf("x=%d,y=%d,xo=%d,yo=%d,x1=%d,y1=%d,x2=%d,y2=%d, w=%d,h=%d\n",
                                    x,y, xo,yo, x1,y1, x2,y2, w,h);
                                */
                                assert(x1 >= 0);
                                assert(y1 >= 0);
                                assert(x1 < w);
                                assert(y1 < h);
                                unsigned c1 = avg4pixels(GetP(x,y),GetP(x+1,y),GetP(x,y+1),GetP(x+1,y+1));
                                unsigned c2 = avg4pixels(GetP(x+xo,y),GetP(x+xo+1,y),GetP(x+xo,y+1),GetP(x+xo+1,y+1));
                                unsigned c3 = avg4pixels(GetP(x,y+yo),GetP(x+1,y+yo),GetP(x,y+yo+1),GetP(x+1,y+yo+1));
                                unsigned c4 = avg4pixels(GetP(x+xo,y+yo),GetP(x+xo+1,y+yo),GetP(x+xo,y+yo+1),GetP(x+xo+1,y+yo+1));

                                unsigned color = CheckOptimize(c1,c2,c3,c4, recursion);
                                if(color != NotOptimized)
                                {
                                    for(int yp=y1; yp<=y2; ++yp)
                                        for(int xp=x1; xp<=x2; ++xp)
                                        {
                                            PSet(xp,yp, color);
                                            optimized2[yp*w+xp] = true;
                                        }
                                    changed = true;
                                    goto done4x4;
                                }
                            }
                        }
                }
                PSet(x,y, GetP(x,y));
               done4x4:;
            }
    }

    fp = fopen(argv[2], "wb");
    gdImagePng(im, fp);
    fclose(fp); gdImageDestroy(im); gdImageDestroy(srcim);
}


You can read more of the story here: http://bisqwit.iki.fi/story/howto/dither/jy/#FromThereToHere

PresidentLeever

Mini-reviews, retro sound chip tribute, romhacks and general listage at my site: Mini-Revver.

PresidentLeever

So apparently this kind of filter has been added to Retroarch. Hopefully it will be added to Regen or Fusion soon.




Mini-reviews, retro sound chip tribute, romhacks and general listage at my site: Mini-Revver.

MathUser2929

Wow, it's like a CRT without the damn scanlines. Still have a clear screen.

Reiska

Quote from: PresidentLeever on September 19, 2016, 08:43:06 AM
So apparently this kind of filter has been added to Retroarch. Hopefully it will be added to Regen or Fusion soon.





What's the name of the filter in Retroarch? :O

PresidentLeever

Mini-reviews, retro sound chip tribute, romhacks and general listage at my site: Mini-Revver.