# DecodeOptions.java

    /*
     * Licensed to the Apache Software Foundation (ASF) under one or more
     * contributor license agreements.  See the NOTICE file distributed with
     * this work for additional information regarding copyright ownership.
     * The ASF licenses this file to You under the Apache License, Version 2.0
     * (the "License"); you may not use this file except in compliance with
     * the License.  You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    package org.apache.pdfbox.filter;

    import java.awt.Rectangle;

    /**
     * Options that may be passed to a Filter to request special handling when decoding the stream.
     * Filters may not honor some or all of the specified options, and so callers should check the
     * honored flag if further processing relies on the options being used.
     */
    public class DecodeOptions
    {
        /**
         * Default decode options. The honored flag for this instance is always true, as it represents
         * the default behavior.
         */
        public static final DecodeOptions DEFAULT = new FinalDecodeOptions(true);

        private Rectangle sourceRegion = null;
        private int subsamplingX = 1;
        private int subsamplingY = 1;
        private int subsamplingOffsetX = 0;
        private int subsamplingOffsetY = 0;
        private boolean filterSubsampled = false;

        /**
         * Constructs an empty DecodeOptions instance
         */
        public DecodeOptions()
        {
            // this constructor is intentionally left empty
        }

        /**
         * Constructs an instance specifying the region of the image that should be decoded. The actual
         * region will be clipped to the dimensions of the image.
         *
         * @param sourceRegion Region of the source image that should be decoded
         */
        public DecodeOptions(Rectangle sourceRegion)
        {
            this.sourceRegion = sourceRegion;
        }

        /**
         * Constructs an instance specifying the region of the image that should be decoded. The actual
         * region will be clipped to the dimensions of the image.
         *
         * @param x x-coordinate of the top-left corner of the region to be decoded
         * @param y y-coordinate of the top-left corner of the region to be decoded
         * @param width Width of the region to be decoded
         * @param height Height of the region to be decoded
         */
        public DecodeOptions(int x, int y, int width, int height)
        {
            this(new Rectangle(x, y, width, height));
        }

        /**
         * Constructs an instance specifying the image should be decoded using subsampling. The
         * subsampling will be the same for the X and Y axes.
         *
         * @param subsampling The number of rows and columns to advance in the source for each pixel in
         * the decoded image.
         */
        public DecodeOptions(int subsampling)
        {
            subsamplingX = subsampling;
            subsamplingY = subsampling;
        }

        /**
         * When decoding an image, the part of the image that should be decoded, or null if the entire
         * image is needed.
         *
         * @return The source region to decode, or null if the entire image should be decoded
         */
        public Rectangle getSourceRegion()
        {
            return sourceRegion;
        }

        /**
         * Sets the region of the source image that should be decoded. The region will be clipped to the
         * dimensions of the source image. Setting this value to null will result in the entire image
         * being decoded.
         *
         * @param sourceRegion The source region to decode, or null if the entire image should be
         * decoded.
         */
        public void setSourceRegion(Rectangle sourceRegion)
        {
            this.sourceRegion = sourceRegion;
        }

        /**
         * When decoding an image, the number of columns to advance in the source for every pixel
         * decoded.
         *
         * @return The x-axis subsampling value
         */
        public int getSubsamplingX()
        {
            return subsamplingX;
        }

        /**
         * Sets the number of columns to advance in the source for every pixel decoded
         *
         * @param ssX The x-axis subsampling value
         */
        public void setSubsamplingX(int ssX)
        {
            this.subsamplingX = ssX;
        }

        /**
         * When decoding an image, the number of rows to advance in the source for every pixel decoded.
         *
         * @return The y-axis subsampling value
         */
        public int getSubsamplingY()
        {
            return subsamplingY;
        }

        /**
         * Sets the number of rows to advance in the source for every pixel decoded
         *
         * @param ssY The y-axis subsampling value
         */
        public void setSubsamplingY(int ssY)
        {
            this.subsamplingY = ssY;
        }

        /**
         * When decoding an image, the horizontal offset for subsampling
         *
         * @return The x-axis subsampling offset
         */
        public int getSubsamplingOffsetX()
        {
            return subsamplingOffsetX;
        }

        /**
         * Sets the horizontal subsampling offset for decoding images
         *
         * @param ssOffsetX The x-axis subsampling offset
         */
        public void setSubsamplingOffsetX(int ssOffsetX)
        {
            this.subsamplingOffsetX = ssOffsetX;
        }

        /**
         * When decoding an image, the vertical offset for subsampling
         *
         * @return The y-axis subsampling offset
         */
        public int getSubsamplingOffsetY()
        {
            return subsamplingOffsetY;
        }

        /**
         * Sets the vertical subsampling offset for decoding images
         *
         * @param ssOffsetY The y-axis subsampling offset
         */
        public void setSubsamplingOffsetY(int ssOffsetY)
        {
            this.subsamplingOffsetY = ssOffsetY;
        }

        /**
         * Flag used by the filter to specify if it performed subsampling.
         *
         * Some filters may be unable or unwilling to apply subsampling, and so the caller must check
         * this flag <b>after</b> decoding.
         *
         * @return True if the filter applied the options specified by this instance, false otherwise.
         */
        public boolean isFilterSubsampled()
        {
            return filterSubsampled;
        }

        /**
         * Used internally by filters to signal they have applied subsampling as requested by this
         * options instance.
         *
         * @param filterSubsampled Value specifying if the filter could meet the requested options.
         * Usually a filter will only call this with the value <code>true</code>, as the default value
         * for the flag is <code>false</code>.
         */
        void setFilterSubsampled(boolean filterSubsampled)
        {
            this.filterSubsampled = filterSubsampled;
        }

        /**
         * Helper class for reusable instances which may not be modified.
         */
        private static class FinalDecodeOptions extends DecodeOptions
        {
            FinalDecodeOptions(boolean filterSubsampled)
            {
                super.setFilterSubsampled(filterSubsampled);
            }

            @Override
            public void setSourceRegion(Rectangle sourceRegion)
            {
                throw new UnsupportedOperationException("This instance may not be modified.");
            }

            @Override
            public void setSubsamplingX(int ssX)
            {
                throw new UnsupportedOperationException("This instance may not be modified.");
            }

            @Override
            public void setSubsamplingY(int ssY)
            {
                throw new UnsupportedOperationException("This instance may not be modified.");
            }

            @Override
            public void setSubsamplingOffsetX(int ssOffsetX)
            {
                throw new UnsupportedOperationException("This instance may not be modified.");
            }

            @Override
            public void setSubsamplingOffsetY(int ssOffsetY)
            {
                throw new UnsupportedOperationException("This instance may not be modified.");
            }

            @Override
            void setFilterSubsampled(boolean filterSubsampled)
            {
                // Silently ignore the request.
            }
        }
    }