class JPXFilter extends Filter
{
    private static final Logger LOG = LogManager.getLogger(JPXFilter.class);

    /**
     * {@inheritDoc}
     */
    @Override
    public DecodeResult decode(InputStream encoded, OutputStream decoded, COSDictionary
            parameters, int index, DecodeOptions options) throws IOException
    {
        DecodeResult result = new DecodeResult(new COSDictionary());
        result.getParameters().addAll(parameters);
        BufferedImage image = readJPX(encoded, options, result);

        Raster raster = image.getRaster();
        switch (raster.getDataBuffer().getDataType())
        {
            case DataBuffer.TYPE_BYTE:
                DataBufferByte byteBuffer = (DataBufferByte) raster.getDataBuffer();
                decoded.write(byteBuffer.getData());
                return result;

            case DataBuffer.TYPE_USHORT:
                DataBufferUShort wordBuffer = (DataBufferUShort) raster.getDataBuffer();
                for (short w : wordBuffer.getData())
                {
                    decoded.write(w >> 8);
                    decoded.write(w);
                }
                return result;

            case DataBuffer.TYPE_INT:
                // not yet used (as of October 2018) but works as fallback
                // if we decide to convert to BufferedImage.TYPE_INT_RGB
                int[] ar = new int[raster.getNumBands()];
                for (int y = 0; y < image.getHeight(); ++y)
                {
                    for (int x = 0; x < image.getWidth(); ++x)
                    {
                        raster.getPixel(x, y, ar);
                        for (int i = 0; i < ar.length; ++i)
                        {
                            decoded.write(ar[i]);
                        }
                    }
                }
                return result;

            default:
                throw new IOException("Data type " + raster.getDataBuffer().getDataType() + " not implemented");
        }
    }

    @Override
    public DecodeResult decode(InputStream encoded, OutputStream decoded,
                               COSDictionary parameters, int index) throws IOException
    {
        return decode(encoded, decoded, parameters, index, DecodeOptions.DEFAULT);
    }

    // try to read using JAI Image I/O
    private BufferedImage readJPX(InputStream input, DecodeOptions options, DecodeResult result) throws IOException
    {<FILL_FUNCTION_BODY>}

    /**
     * {@inheritDoc}
     */
    @Override
    protected void encode(InputStream input, OutputStream encoded, COSDictionary parameters)
            throws IOException
    {
        throw new UnsupportedOperationException("JPX encoding not implemented");
    }
}

        ImageReader reader = findImageReader("JPEG2000", "Java Advanced Imaging (JAI) Image I/O Tools are not installed");
        // PDFBOX-4121: ImageIO.createImageInputStream() is much slower
        try (ImageInputStream iis = new MemoryCacheImageInputStream(input))
        {
            reader.setInput(iis, true, true);
            ImageReadParam irp = reader.getDefaultReadParam();
            irp.setSourceRegion(options.getSourceRegion());
            irp.setSourceSubsampling(options.getSubsamplingX(), options.getSubsamplingY(),
                    options.getSubsamplingOffsetX(), options.getSubsamplingOffsetY());
            options.setFilterSubsampled(true);

            BufferedImage image;
            try
            {
                image = reader.read(0, irp);
            }
            catch (Exception e)
            {
                // wrap and rethrow any exceptions
                throw new IOException("Could not read JPEG 2000 (JPX) image", e);
            }

            COSDictionary parameters = result.getParameters();

            // "If the image stream uses the JPXDecode filter, this entry is optional
            // and shall be ignored if present"
            //
            // note that indexed color spaces make the BPC logic tricky, see PDFBOX-2204
            int bpc = image.getColorModel().getPixelSize() / image.getRaster().getNumBands();
            parameters.setInt(COSName.BITS_PER_COMPONENT, bpc);

            // "Decode shall be ignored, except in the case where the image is treated as a mask"
            if (!parameters.getBoolean(COSName.IMAGE_MASK, false))
            {
                parameters.setItem(COSName.DECODE, null);
            }

            // override dimensions, see PDFBOX-1735
            parameters.setInt(COSName.WIDTH, reader.getWidth(0));
            parameters.setInt(COSName.HEIGHT, reader.getHeight(0));

            // extract embedded color space
            if (!parameters.containsKey(COSName.COLORSPACE))
            {
                if (image.getSampleModel() instanceof MultiPixelPackedSampleModel &&
                    image.getColorModel().getPixelSize() == 1 &&
                    image.getRaster().getNumBands() == 1 && 
                    image.getColorModel() instanceof IndexColorModel)
                {
                    // PDFBOX-4326:
                    // force CS_GRAY colorspace because colorspace in IndexColorModel
                    // has 3 colors despite that there is only 1 color per pixel
                    // in raster
                    result.setColorSpace(new PDJPXColorSpace(ColorSpace.getInstance(ColorSpace.CS_GRAY)));
                }
                else if (image.getTransparency() == Transparency.TRANSLUCENT &&
                         parameters.getInt(COSName.SMASK_IN_DATA) > 0)
                {
                    LOG.warn("JPEG2000 SMaskInData is not supported, returning opaque image");
                    BufferedImage bim = new BufferedImage(
                            image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
                    Graphics2D g2d = (Graphics2D) bim.getGraphics();
                    g2d.drawImage(image, 0, 0, null);
                    g2d.dispose();
                    image = bim;
                    result.setColorSpace(new PDJPXColorSpace(image.getColorModel().getColorSpace()));
                }
                else
                {
                    result.setColorSpace(new PDJPXColorSpace(image.getColorModel().getColorSpace()));
                }
            }

            return image;
        }
        finally
        {
            reader.dispose();
        }
    
<methods>public abstract org.apache.pdfbox.filter.DecodeResult decode(java.io.InputStream, java.io.OutputStream, org.apache.pdfbox.cos.COSDictionary, int) throws java.io.IOException,public org.apache.pdfbox.filter.DecodeResult decode(java.io.InputStream, java.io.OutputStream, org.apache.pdfbox.cos.COSDictionary, int, org.apache.pdfbox.filter.DecodeOptions) throws java.io.IOException,public static org.apache.pdfbox.io.RandomAccessRead decode(java.io.InputStream, List<org.apache.pdfbox.filter.Filter>, org.apache.pdfbox.cos.COSDictionary, org.apache.pdfbox.filter.DecodeOptions, List<org.apache.pdfbox.filter.DecodeResult>) throws java.io.IOException,public final void encode(java.io.InputStream, java.io.OutputStream, org.apache.pdfbox.cos.COSDictionary, int) throws java.io.IOException,public static final javax.imageio.ImageReader findImageReader(java.lang.String, java.lang.String) throws org.apache.pdfbox.filter.MissingImageReaderException,public static int getCompressionLevel() <variables>private static final Logger LOG,public static final java.lang.String SYSPROP_DEFLATELEVEL