* @param fill                fill color to aply to image from current graphics context.
     * @param width               width of image.
     * @param height              heigth of image
     * @param colorSpaceCompCount colour space component count, 1, 3, 4 etc.
     * @param bitsPerComponent    number of bits that represent one component.
     * @param imageMask           boolean flag to use image mask or not.
     * @param decode              decode array, 1,0 or 0,1 can effect colour interpretation.
     * @param smaskImage          smaask image value, optional.
     * @param maskImage           buffered image image mask to apply to decoded image, optional.
     * @param maskMinRGB          max rgb values for the mask
     * @param maskMaxRGB          min rgb values for the mask.
     * @param maskMinIndex        max indexed colour values for the mask.
     * @param maskMaxIndex        min indexed colour values for the mask.
     * @return buffered image of decoded image stream, null if an error occured.
     */
    private BufferedImage getImage(
            PColorSpace colourSpace, Color fill,
            int width, int height,
            int colorSpaceCompCount,
            int bitsPerComponent,
            boolean imageMask,
            Vector decode,
            BufferedImage smaskImage,
            BufferedImage maskImage,
            int[] maskMinRGB, int[] maskMaxRGB,
            int maskMinIndex, int maskMaxIndex) {

        // return cached image if we have already.
        if (image != null) {
            // If Stream.dispose(true) was called since last call to Stream.getPageImage(),
            //   then might have to read from file
            checkMemory(width * height * Math.max(colorSpaceCompCount, 4));
            //TODO There's a race-ish condition here
            //  checkMemory() could have called Page.reduceMemory(), which
            //  could have called Stream.dispose(), which could have nulled
            //  out our "image" field
            BufferedImage img = null;
            synchronized (imageLock) {
                if (image != null) {
                    img = image.readImage();
                }
            }
            if (img != null) {
                return img;
            }
        }

        // No image cash yet, so we decode known image types.
        if (image == null) {
            BufferedImage decodedImage = null;

            // JPEG writes out image if successful
            if (shouldUseDCTDecode()) {
                if (Tagger.tagging)
                    Tagger.tagImage("DCTDecode");
                decodedImage = dctDecode(width, height, colourSpace, bitsPerComponent,
                        smaskImage, maskImage, maskMinRGB, maskMaxRGB, decode);
            }
            // JBIG2 writes out image if successful
            else if (shouldUseJBIG2Decode()) {
                if (Tagger.tagging)
                    Tagger.tagImage("JBIG2Decode");
                decodedImage = jbig2Decode(width, height, fill);
            }
            // JPEG2000 writes out image if successful
            else if (shouldUseJPXDecode()) {
                if (Tagger.tagging)
                    Tagger.tagImage("JPXDecode");
                decodedImage = jpxDecode(width, height, colourSpace, bitsPerComponent, fill,
                        smaskImage, maskImage, maskMinRGB, maskMaxRGB, decode);
            }
            // CCITTFax load via JAI, if this fails we have other non JAI
            // CCITTFax code to fall back on.
            else if (shouldUseCCITTFaxDecode()){
                 if (Tagger.tagging)
                    Tagger.tagImage("CCITTFaxDecode JAI");
                decodedImage =
                    CCITTFax.attemptDeriveBufferedImageFromBytes(this, library,
                            entries, fill);
            }

            // finally if we have something then we return it.
            if (decodedImage != null) {
                // write tmpImage to the cache
                return decodedImage;
            }
        }

        byte[] data;
        int dataLength;
        // CCITTfax data is raw byte decode.
        if (shouldUseCCITTFaxDecode()) {
            if (Tagger.tagging)
                Tagger.tagImage("CCITTFaxDecode");
            data = ccittFaxDecode(width, height);
            dataLength = data.length;
        }
        // no further processing needed we can just get the stream bytes using
        // normal filter decompression.
        else {
            Object[] dataAndSize = getDecodedStreamBytesAndSize(
                    width * height
                            * colourSpace.getNumComponents()
                            * bitsPerComponent / 8);
            data = (byte[]) dataAndSize[0];
            dataLength = (Integer) dataAndSize[1];
        }
        // finally push the bytes though the common image processor
        if (data != null) {
            try {
                BufferedImage decodedImage = makeImageWithRasterFromBytes(
                        colourSpace, fill,
                        width, height,
                        colorSpaceCompCount,
                        bitsPerComponent,
                        imageMask,
                        decode,
                        smaskImage,
                        maskImage,
                        maskMinRGB, maskMaxRGB,
                        maskMinIndex, maskMaxIndex,
                        data, dataLength);
                // if we have something then we can decode it.
                if (decodedImage != null) {
                    return decodedImage;
                }
            }
            catch (Exception e) {
                logger.log(Level.FINE, "Error building image raster.", e);
            }
        }

        // decodes the image stream and returns an image object. Legacy fallback
        // code, should never get here, put there are always corner cases. .
        BufferedImage decodedImage = parseImage(
                width,
                height,
                colourSpace,
                imageMask,
                fill,
                bitsPerComponent,
                decode,
                data,
                smaskImage,
                maskImage,
                maskMinRGB, maskMaxRGB);
        return decodedImage;
    }