protected BufferedImage filterPixelsNN(
      BufferedImage dst, int width, int height, int[] inPixels, Rectangle transformedSpace) {
    int srcWidth = width;
    int srcHeight = height;
    int outWidth = transformedSpace.width;
    int outHeight = transformedSpace.height;
    int outX, outY, srcX, srcY;
    int[] outPixels = new int[outWidth];

    outX = transformedSpace.x;
    outY = transformedSpace.y;
    int[] rgb = new int[4];
    float[] out = new float[2];

    for (int y = 0; y < outHeight; y++) {
      for (int x = 0; x < outWidth; x++) {
        transformInverse(outX + x, outY + y, out);
        srcX = (int) out[0];
        srcY = (int) out[1];
        // int casting rounds towards zero, so we check out[0] < 0, not srcX < 0
        if (out[0] < 0 || srcX >= srcWidth || out[1] < 0 || srcY >= srcHeight) {
          int p;
          switch (edgeAction) {
            case ZERO:
            default:
              p = 0;
              break;
            case WRAP:
              p =
                  inPixels[
                      (ImageMath.mod(srcY, srcHeight) * srcWidth) + ImageMath.mod(srcX, srcWidth)];
              break;
            case CLAMP:
              p =
                  inPixels[
                      (ImageMath.clamp(srcY, 0, srcHeight - 1) * srcWidth)
                          + ImageMath.clamp(srcX, 0, srcWidth - 1)];
              break;
            case RGB_CLAMP:
              p =
                  inPixels[
                          (ImageMath.clamp(srcY, 0, srcHeight - 1) * srcWidth)
                              + ImageMath.clamp(srcX, 0, srcWidth - 1)]
                      & 0x00ffffff;
          }
          outPixels[x] = p;
        } else {
          int i = srcWidth * srcY + srcX;
          rgb[0] = inPixels[i];
          outPixels[x] = inPixels[i];
        }
      }
      setRGB(dst, 0, y, transformedSpace.width, 1, outPixels);
    }
    return dst;
  }
 private final int getPixel(int[] pixels, int x, int y, int width, int height) {
   if (x < 0 || x >= width || y < 0 || y >= height) {
     switch (edgeAction) {
       case ZERO:
       default:
         return 0;
       case WRAP:
         return pixels[(ImageMath.mod(y, height) * width) + ImageMath.mod(x, width)];
       case CLAMP:
         return pixels[
             (ImageMath.clamp(y, 0, height - 1) * width) + ImageMath.clamp(x, 0, width - 1)];
       case RGB_CLAMP:
         return pixels[
                 (ImageMath.clamp(y, 0, height - 1) * width) + ImageMath.clamp(x, 0, width - 1)]
             & 0x00ffffff;
     }
   }
   return pixels[y * width + x];
 }
  @Override
  protected void transformInverse(int x, int y, float[] out) {
    float dx = x - icentreX;
    float dy = y - icentreY;
    float theta = (float) Math.atan2(-dy, -dx) + angle;
    float r = (float) Math.sqrt(dx * dx + dy * dy);

    theta = ImageMath.mod(theta, 2 * (float) Math.PI);

    out[0] = iWidth * theta / (spreadAngle + 0.00001f);
    out[1] = iHeight * (1 - (r - radius) / (height + 0.00001f));
  }