[Journal] Untitled Thought in Progress

Stub

I’m working on a concept, that I’ll hopefully have time to complete. But for today I’d like to throw out the idea of allowing Pixel scaling methods.

My thought is that these are in the spirit of the contest and would make playing the entries more fun. But if allowed would need to follow the rule:

The scaling method must be under end user control and should default to point sampling. Also, to be fair to everyone, the method source should be made available.

Below I have my implementations of Scale2X & Scale3X. These can be chained to create 4x & 6x. I also have Hq{2,3,4}x, but are not really ready for realtime usage. If TPTB decided that these methods are OK for usage, I’ll try to complete them along with an 2xSai version.

Scale2X:


public static final void convert(int[] dst, int[] src, int sw, int sh)
{
  int dw = sw+sw;
  int r0 = 0;
  int r1 = dw;

  int x, y;
  int pr = 0;
  int nr;
  int i = 0;
  
  // Input Sample Matrix
  //      c01    
  //  c10 c11 c12 ->
  //      c21    
  int     c01;
  int c10,c11,c12;
  int     c21;

  for (y=0; y<sh; y++) {
    if (y < sh-1) nr = sw; else nr = 0;
    
    c11 = src[i];
    c12 = src[i+1];

    for (x=0; x<sw; x++) {
      c10 = c11;
      c11 = c12;
      c01 = src[i + pr];
      c21 = src[i + nr];

      if (x < sw-1) c12 = src[i+1];

      if (c01 != c21 && c10 != c12) {
        dst[r0  ] = c10 == c01 ? c10 : c11;
        dst[r0+1] = c01 == c12 ? c12 : c11;
        dst[r1  ] = c10 == c21 ? c10 : c11;
        dst[r1+1] = c21 == c12 ? c12 : c11;
      } 
      else
        dst[r0] = dst[r0+1] = dst[r1] = dst[r1+1] = c11;

      r0 += 2;
      r1 += 2;
      i++;
    }
    
    pr  = -sw;
    r0  = r1;
    r1 += dw;
  }
}

Scale3X:


public static final void convert(int[] dst, int[] src, int sw, int sh)
{
  int i = 0;
  int x, y;
  int pr = 0;
  int nr;
  int dx = sw * 3;
  int r0 = 0;
  int r1 = dx;
  int r2 = r1 + dx;

  // Input Sample Matrix
  //   c00 c01 c02
  //   c10 c11 c12 ->
  //   c20 c21 c22

  int c00,c01,c02;
  int c10,c11,c12;
  int c20,c21,c22;

  for (y = 0; y < sh; y++) {
    if (y < sh - 1)
      nr = sw;
    else
      nr = 0;

    for (x = 0; x < sw; x++) {
      c01 = src[i + pr];
      c11 = src[i];
      c21 = src[i + nr];

      if (x > 0) {
        c00 = src[i + pr - 1];
        c10 = src[i - 1];
        c20 = src[i + nr - 1];
      }
      else {
        c00 = c01;
        c10 = c11;
        c20 = c21;
      }

      if (x < sw - 1) {
        c02 = src[i + pr + 1];
        c12 = src[i + 1];
        c22 = src[i + nr + 1];
      }
      else {
        c02 = c01;
        c12 = c11;
        c22 = c21;
      }

      i++;
      
      if ((c01 != c21) && (c10 != c12)) {
        boolean t0 = c01 == c12;
        boolean t1 = c10 == c21;
        boolean t2 = c11 != c02;
        boolean t3 = c11 != c20;
        
        if (c11 != c00) {
          if (c10 == c01) {
            dst[r0  ] = c10;                 
            dst[r0+1] = t0 || t2 ? c01 : c11;
            dst[r1  ] = t1 || t3 ? c10 : c11;
          }
          else {
            dst[r0  ] = c11;           
            dst[r0+1] = t0 ? c01 : c11;
            dst[r1  ] = t1 ? c10 : c11;
          }
        }
        else {
          if (c10 == c01) {
            dst[r0  ] = c10;           
            dst[r0+1] = t2 ? c01 : c11;
            dst[r1  ] = t3 ? c10 : c11;
          }
          else {
            dst[r0  ] = c11;
            dst[r0+1] = c11;
            dst[r1  ] = c11;
          }
        }

        if (c11 != c22) {
          if (c21 == c12) {
            dst[r2+2] = c12;
            dst[r1+2] = t0 || t2 ? c12 : c11;
            dst[r2+1] = t1 || t3 ? c21 : c11;
          }
          else {
            dst[r2+2] = c11;           
            dst[r1+2] = t0 ? c12 : c11;
            dst[r2+1] = t1 ? c21 : c11;
          }
        }
        else {
          if (c21 == c12) {
            dst[r2+2] = c12;           
            dst[r1+2] = t2 ? c12 : c11;
            dst[r2+1] = t3 ? c21 : c11;
          }
          else {
            dst[r2+2] = c11;
            dst[r1+2] = c11;
            dst[r2+1] = c11;
          }
        }

        dst[r0+2] = t0 ? c12 : c11;
        dst[r2  ] = t1 ? c10 : c11;
      }
      else {
        dst[r0  ] = 
        dst[r0+1] = 
        dst[r0+2] =
        dst[r1  ] = 
        dst[r1+2] = 
        dst[r2  ] = 
        dst[r2+1] = 
        dst[r2+2] = c11;
      }

      dst[r1+1] = c11;

      r0 += 3;
      r1 += 3;
      r2 += 3;
    }
    
    pr = -sw;
    r0 = r2;
    r1 = r0+dx;
    r2 = r1+dx;
  }
}

I think someone already unsuccessfully asked about upscaling. It makes things look nicer at x2 or x4, but at higher levels of magnification the benefit seemed questionable. Anyway it was ruled out (I think)

Incidentally, you can set rendering hints to get a choice of three upscaling algorithms in Java2D. E.g.


    public void paint1(Graphics g, int x, int y, int magnify) {
        consolidate();
        ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g.drawImage(this, x, y, magnify*getWidth(), magnify*getHeight(), null);
    }

I think if we allow methods of up-scaling other than nearest neighbour it will detract from the theme as it will no longer be clear that the game was generated at such a low resolution. Nearest neighbour up-scaling helps to emphasize the theme of the competition by exaggerating the pixelization. If enough participants disagree I’ll run a poll and we can vote but otherwise we’ll stick with nearest neighbour as the only allowed up-scaling method.

I personally don’t have a problem with smooth scaling, as long as it’s JUST smooth scaling and isn’t hiding a larger resolution. Not sure how we’ll verify that other than forcing people to show their rendering code or counting width and height of the pixels shown throughout play. So I understand you wanted to avoid this Shannon.

If a poll was setup I’ll vote for possible up-scaling, but I don’t really care either way.

Anything but nearest neighbor totally destroys the point of the compo…

@Alan_W: Traditional filter don’t work well. The source image from signal processing standpoint doesn’t match. Bi-linear, cubic, etc are fine for Gaussian images, but simply create a mess for pixel art which are a unnatural mix of very high and very low frequencies (text and raster images of vector art fall in the same category). Methods which do work well are based on either pattern matching or mathematical morphology.

@ShannonSmith & Markus_Persson: OK. I wanted to toss out the idea. Again, this thought was more for the fun-factor than anything else. (Thinking: fake retro game. Where it a real retro game, it would probably be run in an emulator, which generally support these kinds of techniques). Since obviously not everyone “jazzed” about the idea, I’ll drop it.

FYI. From tests against mock-up images, the output looks like a really cheesy flash game (given the limited resolution & colors)