@JsGauthier For sure. The idea is to use marching squares, which I already coded but a simpler version. I have a function to render a cell in a specific position, here’s the simpler version:
void renderPiece(int x, int y, int state) {
  if (state < 11 && state != 7 && state != 9) {
    fill(0);
  } else {
    fill(255);
  }
  rect(x, y, S, S);
  if (state < 11 && state != 7 && state != 9) {
    fill(255);
  } else {
    fill(0);
    state = 15-state;
  }
  
  switch (state) {
    case 1:
      triangle(x, y+HS, x+HS, y+S, x, y+S);
      break;
    case 2:
      triangle(x+S, y+S, x+S, y+HS, x+HS, y+S);
      break;
    case 3:
      rect(x, y+HS, S, HS);
      break;
    case 4:
      triangle(x+HS, y, x+S, y, x+S, y+HS);
      break;
    case 5:
      triangle(x+HS, y, x+S, y, x+S, y+HS);
      triangle(x, y+HS, x+HS, y+S, x, y+S);
      break;
    case 6:
      rect(x+HS, y, HS, S);
      break;
    case 8:
      triangle(x, y, x+HS, y, x, y+HS);
      break;
   }
}
state is made up of four bits, 2³ being top left point, 2² top right, 2¹ bottom right, 2⁰ being bottom left.
The code works as follows; half of the marching squares cases are just inverted other cases. Specifically, states 0111, 1001, and everything above state 1011. Therefore, what I do is I draw the background of the square (inverted or not), then set the fill to the right color and switch the inverted states to the original, then treat all of the 8 remaining cases.
Here is the already slightly modified but incomplete image version:
  void renderPiece(int x, int y, int state) {
    if (state < 11 && state != 7 && state != 9) {
      copy(filtered, x, y, S, S, x, y, S, S);
    } else {
      copy(unfiltered, x, y, S, S, x, y, S, S);
      state = 15-state;
    }
    
    switch (state) {
      case 1:
        triangle(x, y+HS, x+HS, y+S, x, y+S);
        break;
      case 2:
        triangle(x+S, y+S, x+S, y+HS, x+HS, y+S);
        break;
      case 3:
        rect(x, y+HS, S, HS);
        break;
      case 4:
        triangle(x+HS, y, x+S, y, x+S, y+HS);
        break;
      case 5:
        triangle(x+HS, y, x+S, y, x+S, y+HS);
        triangle(x, y+HS, x+HS, y+S, x, y+S);
        break;
      case 6:
        rect(x+HS, y, HS, S);
        break;
      case 8:
        triangle(x, y, x+HS, y, x, y+HS);
        break;
     }
  }
this is the code that needs to be changed, but here’s the method where it’s called from as context:
void marchingSquares() {
  int pX = 0;
  int pY = 0;
  for (int x = 0; x < W; x++) {
    pY = 0;
    for (int y = 0; y < H; y++) {
      if (changed[x+1][y+1]) {
        renderPiece(pX, pY, ((map[x][y] ? 1 : 0) << 3) | ((map[x+1][y] ? 1 : 0) << 2) | ((map[x+1][y+1] ? 1 : 0) << 1) | (map[x][y+1] ? 1 : 0));
      }
      pY += S;
    }
    pX += S;
  }
}
@scudly That would work, after a bit of testing! How fast is it, though?