docs: public final BufferedImage filter(final BufferedImage input, final BufferedImage output) {
          if (input == null) {
              throw new NullPointerException("Input == null");
          }
          if (input == output) {
              throw new IllegalArgumentException("Output image cannot be the same as the input image");
          }
  
          InterpolationFilter filter;
  
          // Special case for POINT, TRIANGLE and QUADRATIC filter, as standard
          // Java implementation is very fast (possibly H/W accelerated)
          switch (filterType) {
              case FILTER_POINT:
                  if (input.getType() != BufferedImage.TYPE_CUSTOM) {
                      return fastResample(input, output, width, height, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
                  }
                  // Else fall through
              case FILTER_TRIANGLE:
                  if (input.getType() != BufferedImage.TYPE_CUSTOM) {
                      return fastResample(input, output, width, height, AffineTransformOp.TYPE_BILINEAR);
                  }
                  // Else fall through
              case FILTER_QUADRATIC:
                  if (input.getType() != BufferedImage.TYPE_CUSTOM) {
                      return fastResample(input, output, width, height, AffineTransformOp.TYPE_BICUBIC);
                  }
                  // Else fall through
              default:
                  filter = createFilter(filterType);
                  // NOTE: Workaround for filter throwing exceptions when input or output is less than support...
                  if (Math.min(input.getWidth(), input.getHeight()) <= filter.support() || Math.min(width, height) <= filter.support()) {
                      return fastResample(input, output, width, height, AffineTransformOp.TYPE_BILINEAR);
                  }
                  // Fall through
          }
  
          // Try to use native ImageMagick code
          BufferedImage result = MagickAccelerator.filter(this, input, output);
          if (result != null) {
              return result;
          }
  
          // Otherwise, continue in pure Java mode
  
          // TODO: What if output != null and wrong size? Create new? Render on only a part? Document?
  
          // If filter type != POINT or BOX and input has IndexColorModel, convert
          // to true color, with alpha reflecting that of the original color model.
          BufferedImage temp;
          ColorModel cm;
          if (filterType != FILTER_POINT && filterType != FILTER_BOX && (cm = input.getColorModel()) instanceof IndexColorModel) {
              // TODO: OPTIMIZE: If color model has only b/w or gray, we could skip color info
              temp = ImageUtil.toBuffered(input, cm.hasAlpha() ? BufferedImage.TYPE_4BYTE_ABGR : BufferedImage.TYPE_3BYTE_BGR);
          }
          else {
              temp = input;
          }
  
          // Create or convert output to a suitable image
          // TODO: OPTIMIZE: Don't really need to convert all types to same as input
          result = output != null && temp.getType() != BufferedImage.TYPE_CUSTOM ? /*output*/ ImageUtil.toBuffered(output, temp.getType()) : createCompatibleDestImage(temp, null);
  
          resample(temp, result, filter);
  
          // If output != null and needed to be converted, draw it back
          if (output != null && output != result) {
              //output.setData(output.getRaster());
              ImageUtil.drawOnto(output, result);
              result = output;
          }
  
          return result;
      }