http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/30839c32/src/main/java/org/apache/pdfbox/jbig2/image/Bitmaps.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/pdfbox/jbig2/image/Bitmaps.java b/src/main/java/org/apache/pdfbox/jbig2/image/Bitmaps.java index af4a26a..895741f 100644 --- a/src/main/java/org/apache/pdfbox/jbig2/image/Bitmaps.java +++ b/src/main/java/org/apache/pdfbox/jbig2/image/Bitmaps.java @@ -32,538 +32,645 @@ import org.apache.pdfbox.jbig2.Bitmap; import org.apache.pdfbox.jbig2.JBIG2ReadParam; import org.apache.pdfbox.jbig2.util.CombinationOperator; -public class Bitmaps { +public class Bitmaps +{ /** * Returns the given bitmap as writable raster. - * + * * @param bitmap the given bitmap * @return the raster representation of the bitmap */ - public static WritableRaster asRaster(final Bitmap bitmap) { - return asRaster(bitmap, FilterType.Gaussian); - } + public static WritableRaster asRaster(final Bitmap bitmap) + { + return asRaster(bitmap, FilterType.Gaussian); + } /** * Returns the given bitmap as writable raster. - * + * * @param bitmap the given bitmap * @param filterType type of filter which is used when creating the writable raster * @return the raster representation of the bitmap */ - public static WritableRaster asRaster(final Bitmap bitmap, final FilterType filterType) { + public static WritableRaster asRaster(final Bitmap bitmap, final FilterType filterType) + { if (bitmap == null) - throw new IllegalArgumentException("bitmap must not be null"); - - final JBIG2ReadParam param = new JBIG2ReadParam(1, 1, 0, 0, new Rectangle(0, 0, bitmap.getWidth(), - bitmap.getHeight()), new Dimension(bitmap.getWidth(), bitmap.getHeight())); - + throw new IllegalArgumentException("bitmap must not be null"); + + final JBIG2ReadParam param = new JBIG2ReadParam(1, 1, 0, 0, + new Rectangle(0, 0, bitmap.getWidth(), bitmap.getHeight()), + new Dimension(bitmap.getWidth(), bitmap.getHeight())); + return asRaster(bitmap, param, filterType); } /** * Returns the given bitmap as writable raster. - * + * * @param bitmap the given bitmap * @param param ImageReadParam to be used when creating the writable raster * @param filterType type of filter which is used when creating the writable raster * @return the raster representation of the bitmap */ - public static WritableRaster asRaster(Bitmap bitmap, final ImageReadParam param, final FilterType filterType) { - if (bitmap == null) - throw new IllegalArgumentException("bitmap must not be null"); - - if (param == null) - throw new IllegalArgumentException("param must not be null"); - - final Dimension sourceRenderSize = param.getSourceRenderSize(); - - double scaleX; - double scaleY; - if (sourceRenderSize != null) { - scaleX = sourceRenderSize.getWidth() / bitmap.getWidth(); - scaleY = sourceRenderSize.getHeight() / bitmap.getHeight(); - } else { - scaleX = scaleY = 1; - } - - Rectangle sourceRegion = param.getSourceRegion(); - if (sourceRegion != null && !bitmap.getBounds().equals(sourceRegion)) { - // make sure we don't request an area outside of the source bitmap - sourceRegion = bitmap.getBounds().intersection(sourceRegion); + public static WritableRaster asRaster(Bitmap bitmap, final ImageReadParam param, + final FilterType filterType) + { + if (bitmap == null) + throw new IllegalArgumentException("bitmap must not be null"); - // get region of interest - bitmap = Bitmaps.extract(sourceRegion, bitmap); - } + if (param == null) + throw new IllegalArgumentException("param must not be null"); - /* - * Subsampling is the advance of columns/rows for each pixel in the according direction. The - * resulting image's quality will be bad because we loose information if we step over - * columns/rows. For example, a thin line (1 pixel high) may disappear completely. To avoid this - * we use resize filters if scaling will be performed anyway. The resize filters use scale - * factors, one for horizontal and vertical direction. We care about the given subsampling steps - * by adjusting the scale factors. If scaling is not performed, subsampling is performed in its - * original manner. - */ + final Dimension sourceRenderSize = param.getSourceRenderSize(); - final boolean requiresScaling = scaleX != 1 || scaleY != 1; - - final boolean requiresXSubsampling = param.getSourceXSubsampling() != 1; - final boolean requiresYSubsampling = param.getSourceYSubsampling() != 1; - - if (requiresXSubsampling && requiresYSubsampling) { - // Apply vertical and horizontal subsampling - if (requiresScaling) { - scaleX /= (double) param.getSourceXSubsampling(); - scaleY /= (double) param.getSourceYSubsampling(); - } else { - bitmap = subsample(bitmap, param); - } - } else { - if (requiresXSubsampling) { - // Apply horizontal subsampling only - if (requiresScaling) { - scaleX /= (double) param.getSourceXSubsampling(); - } else { - bitmap = Bitmaps.subsampleX(bitmap, param.getSourceXSubsampling(), param.getSubsamplingXOffset()); + double scaleX; + double scaleY; + if (sourceRenderSize != null) + { + scaleX = sourceRenderSize.getWidth() / bitmap.getWidth(); + scaleY = sourceRenderSize.getHeight() / bitmap.getHeight(); } - } - - if (requiresYSubsampling) { - // Apply vertical subsampling only - if (requiresScaling) { - scaleY /= (double) param.getSourceYSubsampling(); - } else { - bitmap = Bitmaps.subsampleY(bitmap, param.getSourceYSubsampling(), param.getSubsamplingYOffset()); + else + { + scaleX = scaleY = 1; } - } - } - return buildRaster(bitmap, filterType, scaleX, scaleY); - } - - private static WritableRaster buildRaster(final Bitmap bitmap, final FilterType filterType, final double scaleX, - final double scaleY) { - final Rectangle dstBounds = new Rectangle(0, 0, // - (int) Math.round(bitmap.getWidth() * scaleX), // - (int) Math.round(bitmap.getHeight() * scaleY)); - - final WritableRaster dst = WritableRaster.createInterleavedRaster(DataBuffer.TYPE_BYTE, dstBounds.width, - dstBounds.height, 1, new Point()); - - if (scaleX != 1 || scaleY != 1) { - // scaling required - final Resizer resizer = new Resizer(scaleX, scaleY); - final Filter filter = Filter.byType(filterType); - resizer.resize(bitmap, bitmap.getBounds() /* sourceRegion */, dst, dstBounds, filter, filter); - } else { - // scaling not required, paste bitmap into raster pixel per pixel - int byteIndex = 0; - for (int y = 0; y < bitmap.getHeight(); y++) { - for (int x = 0; x < bitmap.getWidth(); byteIndex++) { - final int pixels = (~bitmap.getByte(byteIndex)) & 0xFF; - final int relevantPixels = bitmap.getWidth() - x > 8 ? 8 : bitmap.getWidth() - x; - final int endIdx = 7 - relevantPixels; - for (int bytePosition = 7; bytePosition > endIdx; bytePosition--, x++) { - dst.setSample(x, y, 0, (pixels >> bytePosition) & 0x1); - } + Rectangle sourceRegion = param.getSourceRegion(); + if (sourceRegion != null && !bitmap.getBounds().equals(sourceRegion)) + { + // make sure we don't request an area outside of the source bitmap + sourceRegion = bitmap.getBounds().intersection(sourceRegion); + + // get region of interest + bitmap = Bitmaps.extract(sourceRegion, bitmap); } - } - } - return dst; - } - - /** - * Returns the given bitmap as buffered image. - * - * @param bitmap the given bitmap - * @return the image representation of the bitmap - */ - public static BufferedImage asBufferedImage(Bitmap bitmap) { - return asBufferedImage(bitmap, FilterType.Gaussian); - } - - /** - * Returns the given bitmap as buffered image. - * - * @param bitmap the given bitmap - * @param filterType type of filter which is used when creating the buffered image - * @return the image representation of the bitmap - */ - public static BufferedImage asBufferedImage(Bitmap bitmap, FilterType filterType) { - if (bitmap == null) - throw new IllegalArgumentException("bitmap must not be null"); - - final JBIG2ReadParam param = new JBIG2ReadParam(1, 1, 0, 0, new Rectangle(0, 0, bitmap.getWidth(), - bitmap.getHeight()), new Dimension(bitmap.getWidth(), bitmap.getHeight())); - - return asBufferedImage(bitmap, param, filterType); - } - - /** - * Returns the given bitmap as buffered image. - * - * @param bitmap the given bitmap - * @param param ImageReadParam to be used when creating the buffered image - * @param filterType type of filter which is used when creating the buffered image - * @return the image representation of the bitmap - */ - public static BufferedImage asBufferedImage(Bitmap bitmap, ImageReadParam param, FilterType filterType) { - if (bitmap == null) - throw new IllegalArgumentException("bitmap must not be null"); - - if (param == null) - throw new IllegalArgumentException("param must not be null"); - - final WritableRaster raster = asRaster(bitmap, param, filterType); - - final Dimension sourceRenderSize = param.getSourceRenderSize(); - - final double scaleX; - final double scaleY; - if (sourceRenderSize != null) { - scaleX = sourceRenderSize.getWidth() / bitmap.getWidth(); - scaleY = sourceRenderSize.getHeight() / bitmap.getHeight(); - } else { - scaleX = scaleY = 1d; - } + /* + * Subsampling is the advance of columns/rows for each pixel in the according direction. The resulting image's + * quality will be bad because we loose information if we step over columns/rows. For example, a thin line (1 + * pixel high) may disappear completely. To avoid this we use resize filters if scaling will be performed + * anyway. The resize filters use scale factors, one for horizontal and vertical direction. We care about the + * given subsampling steps by adjusting the scale factors. If scaling is not performed, subsampling is performed + * in its original manner. + */ + + final boolean requiresScaling = scaleX != 1 || scaleY != 1; + + final boolean requiresXSubsampling = param.getSourceXSubsampling() != 1; + final boolean requiresYSubsampling = param.getSourceYSubsampling() != 1; + + if (requiresXSubsampling && requiresYSubsampling) + { + // Apply vertical and horizontal subsampling + if (requiresScaling) + { + scaleX /= (double) param.getSourceXSubsampling(); + scaleY /= (double) param.getSourceYSubsampling(); + } + else + { + bitmap = subsample(bitmap, param); + } + } + else + { + if (requiresXSubsampling) + { + // Apply horizontal subsampling only + if (requiresScaling) + { + scaleX /= (double) param.getSourceXSubsampling(); + } + else + { + bitmap = Bitmaps.subsampleX(bitmap, param.getSourceXSubsampling(), + param.getSubsamplingXOffset()); + } + } + + if (requiresYSubsampling) + { + // Apply vertical subsampling only + if (requiresScaling) + { + scaleY /= (double) param.getSourceYSubsampling(); + } + else + { + bitmap = Bitmaps.subsampleY(bitmap, param.getSourceYSubsampling(), + param.getSubsamplingYOffset()); + } + } + } - ColorModel cm = null; - final boolean isScaled = scaleX != 1 || scaleY != 1; - if (isScaled) { - final int size = 256; - final int divisor = size - 1; - - final byte[] gray = new byte[size]; - for (int i = size - 1, s = 0; i >= 0; i--, s++) { - gray[i] = (byte) (255 - s * 255 / divisor); - } - cm = new IndexColorModel(8, size, gray, gray, gray); - } else { - - cm = new IndexColorModel(8, 2, // - new byte[]{ - 0x00, (byte) 0xff - }, new byte[]{ - 0x00, (byte) 0xff - }, new byte[]{ - 0x00, (byte) 0xff - }); + return buildRaster(bitmap, filterType, scaleX, scaleY); } - return new BufferedImage(cm, raster, false, null); - } - - /** - * Returns the specified rectangle area of the bitmap. - * - * @param roi - A {@link Rectangle} that specifies the requested image section. - * @param src the given bitmap - * @return A {@code Bitmap} that represents the requested image section. - */ - public static Bitmap extract(final Rectangle roi, final Bitmap src) { - final Bitmap dst = new Bitmap(roi.width, roi.height); - - final int upShift = roi.x & 0x07; - final int downShift = 8 - upShift; - int dstLineStartIdx = 0; - - final int padding = (8 - dst.getWidth() & 0x07); - int srcLineStartIdx = src.getByteIndex(roi.x, roi.y); - int srcLineEndIdx = src.getByteIndex(roi.x + roi.width - 1, roi.y); - final boolean usePadding = dst.getRowStride() == srcLineEndIdx + 1 - srcLineStartIdx; - - for (int y = roi.y; y < roi.getMaxY(); y++) { - int srcIdx = srcLineStartIdx; - int dstIdx = dstLineStartIdx; - - if (srcLineStartIdx == srcLineEndIdx) { - final byte pixels = (byte) (src.getByte(srcIdx) << upShift); - dst.setByte(dstIdx, unpad(padding, pixels)); - } else if (upShift == 0) { - for (int x = srcLineStartIdx; x <= srcLineEndIdx; x++) { - byte value = src.getByte(srcIdx++); - - if (x == srcLineEndIdx && usePadding) { - value = unpad(padding, value); - } - - dst.setByte(dstIdx++, value); + private static WritableRaster buildRaster(final Bitmap bitmap, final FilterType filterType, + final double scaleX, final double scaleY) + { + final Rectangle dstBounds = new Rectangle(0, 0, // + (int) Math.round(bitmap.getWidth() * scaleX), // + (int) Math.round(bitmap.getHeight() * scaleY)); + + final WritableRaster dst = WritableRaster.createInterleavedRaster(DataBuffer.TYPE_BYTE, + dstBounds.width, dstBounds.height, 1, new Point()); + + if (scaleX != 1 || scaleY != 1) + { + // scaling required + final Resizer resizer = new Resizer(scaleX, scaleY); + final Filter filter = Filter.byType(filterType); + resizer.resize(bitmap, bitmap.getBounds() /* sourceRegion */, dst, dstBounds, filter, + filter); + } + else + { + // scaling not required, paste bitmap into raster pixel per pixel + int byteIndex = 0; + for (int y = 0; y < bitmap.getHeight(); y++) + { + for (int x = 0; x < bitmap.getWidth(); byteIndex++) + { + final int pixels = (~bitmap.getByte(byteIndex)) & 0xFF; + final int relevantPixels = bitmap.getWidth() - x > 8 ? 8 + : bitmap.getWidth() - x; + final int endIdx = 7 - relevantPixels; + for (int bytePosition = 7; bytePosition > endIdx; bytePosition--, x++) + { + dst.setSample(x, y, 0, (pixels >> bytePosition) & 0x1); + } + } + } } - } else { - copyLine(src, dst, upShift, downShift, padding, srcLineStartIdx, srcLineEndIdx, usePadding, srcIdx, dstIdx); - } - srcLineStartIdx += src.getRowStride(); - srcLineEndIdx += src.getRowStride(); - dstLineStartIdx += dst.getRowStride(); + return dst; } - return dst; - } - - private static void copyLine(Bitmap src, Bitmap dst, int sourceUpShift, int sourceDownShift, int padding, - int firstSourceByteOfLine, int lastSourceByteOfLine, boolean usePadding, int sourceOffset, int targetOffset) { - for (int x = firstSourceByteOfLine; x < lastSourceByteOfLine; x++) { - - if (sourceOffset + 1 < src.getByteArray().length) { - final boolean isLastByte = x + 1 == lastSourceByteOfLine; - byte value = (byte) (src.getByte(sourceOffset++) << sourceUpShift | (src.getByte(sourceOffset) & 0xff) >>> sourceDownShift); - - if (isLastByte && !usePadding) { - value = unpad(padding, value); - } + /** + * Returns the given bitmap as buffered image. + * + * @param bitmap the given bitmap + * @return the image representation of the bitmap + */ + public static BufferedImage asBufferedImage(Bitmap bitmap) + { + return asBufferedImage(bitmap, FilterType.Gaussian); + } - dst.setByte(targetOffset++, value); + /** + * Returns the given bitmap as buffered image. + * + * @param bitmap the given bitmap + * @param filterType type of filter which is used when creating the buffered image + * @return the image representation of the bitmap + */ + public static BufferedImage asBufferedImage(Bitmap bitmap, FilterType filterType) + { + if (bitmap == null) + throw new IllegalArgumentException("bitmap must not be null"); - if (isLastByte && usePadding) { - value = unpad(padding, (byte) ((src.getByte(sourceOffset) & 0xff) << sourceUpShift)); - dst.setByte(targetOffset, value); - } + final JBIG2ReadParam param = new JBIG2ReadParam(1, 1, 0, 0, + new Rectangle(0, 0, bitmap.getWidth(), bitmap.getHeight()), + new Dimension(bitmap.getWidth(), bitmap.getHeight())); - } else { - final byte value = (byte) (src.getByte(sourceOffset++) << sourceUpShift & 0xff); - dst.setByte(targetOffset++, value); - } - } - } - - /** - * Removes unnecessary bits from a byte. - * - * @param padding - The amount of unnecessary bits. - * @param value - The byte that should be cleaned up. - * @return A cleaned byte. - */ - private static byte unpad(int padding, byte value) { - return (byte) (value >> padding << padding); - } - - public static Bitmap subsample(Bitmap src, ImageReadParam param) { - if (src == null) - throw new IllegalArgumentException("src must not be null"); - - if (param == null) - throw new IllegalArgumentException("param must not be null"); - - final int xSubsampling = param.getSourceXSubsampling(); - final int ySubsampling = param.getSourceYSubsampling(); - final int xSubsamplingOffset = param.getSubsamplingXOffset(); - final int ySubsamplingOffset = param.getSubsamplingYOffset(); - - final int dstWidth = (src.getWidth() - xSubsamplingOffset) / xSubsampling; - final int dstHeight = (src.getHeight() - ySubsamplingOffset) / ySubsampling; - - final Bitmap dst = new Bitmap(dstWidth, dstHeight); - - for (int yDst = 0, ySrc = ySubsamplingOffset; yDst < dst.getHeight(); yDst++, ySrc += ySubsampling) { - for (int xDst = 0, xSrc = xSubsamplingOffset; xDst < dst.getWidth(); xDst++, xSrc += xSubsampling) { - final byte pixel = src.getPixel(xSrc, ySrc); - if (pixel != 0) - dst.setPixel(xDst, yDst, pixel); - } + return asBufferedImage(bitmap, param, filterType); } - return dst; - } + /** + * Returns the given bitmap as buffered image. + * + * @param bitmap the given bitmap + * @param param ImageReadParam to be used when creating the buffered image + * @param filterType type of filter which is used when creating the buffered image + * @return the image representation of the bitmap + */ + public static BufferedImage asBufferedImage(Bitmap bitmap, ImageReadParam param, + FilterType filterType) + { + if (bitmap == null) + throw new IllegalArgumentException("bitmap must not be null"); - public static Bitmap subsampleX(Bitmap src, final int xSubsampling, final int xSubsamplingOffset) { - if (src == null) - throw new IllegalArgumentException("src must not be null"); + if (param == null) + throw new IllegalArgumentException("param must not be null"); - final int dstHeight = (src.getWidth() - xSubsamplingOffset) / xSubsampling; - final Bitmap dst = new Bitmap(src.getWidth(), dstHeight); + final WritableRaster raster = asRaster(bitmap, param, filterType); - for (int yDst = 0; yDst < dst.getHeight(); yDst++) { - for (int xDst = 0, xSrc = xSubsamplingOffset; xDst < dst.getWidth(); xDst++, xSrc += xSubsampling) { - final byte pixel = src.getPixel(xSrc, yDst); - if (pixel != 0) - dst.setPixel(xDst, yDst, pixel); - } - } + final Dimension sourceRenderSize = param.getSourceRenderSize(); - return dst; - } + final double scaleX; + final double scaleY; + if (sourceRenderSize != null) + { + scaleX = sourceRenderSize.getWidth() / bitmap.getWidth(); + scaleY = sourceRenderSize.getHeight() / bitmap.getHeight(); + } + else + { + scaleX = scaleY = 1d; + } - public static Bitmap subsampleY(Bitmap src, final int ySubsampling, final int ySubsamplingOffset) { - if (src == null) - throw new IllegalArgumentException("src must not be null"); + ColorModel cm = null; + final boolean isScaled = scaleX != 1 || scaleY != 1; + if (isScaled) + { + final int size = 256; + final int divisor = size - 1; + + final byte[] gray = new byte[size]; + for (int i = size - 1, s = 0; i >= 0; i--, s++) + { + gray[i] = (byte) (255 - s * 255 / divisor); + } + cm = new IndexColorModel(8, size, gray, gray, gray); + } + else + { - final int dstWidth = (src.getWidth() - ySubsamplingOffset) / ySubsampling; - final Bitmap dst = new Bitmap(dstWidth, src.getHeight()); + cm = new IndexColorModel(8, 2, // + new byte[] { 0x00, (byte) 0xff }, new byte[] { 0x00, (byte) 0xff }, + new byte[] { 0x00, (byte) 0xff }); + } - for (int yDst = 0, ySrc = ySubsamplingOffset; yDst < dst.getHeight(); yDst++, ySrc += ySubsampling) { - for (int xDst = 0; xDst < dst.getWidth(); xDst++) { - final byte pixel = src.getPixel(xDst, ySrc); - if (pixel != 0) - dst.setPixel(xDst, yDst, pixel); - } + return new BufferedImage(cm, raster, false, null); } - return dst; - } - - /** - * The method combines two given bytes with an logical operator. - * <p> - * The JBIG2 Standard specifies 5 possible combinations of bytes.<br> - * <p> - * <b>Hint:</b> Please take a look at ISO/IEC 14492:2001 (E) for detailed definition and - * description of the operators. - * - * @param value1 - The value that should be combined with value2. - * @param value2 - The value that should be combined with value1. - * @param op - The specified combination operator. - * - * @return The combination result. - */ - public static byte combineBytes(byte value1, byte value2, CombinationOperator op) { - - switch (op){ - case OR : - return (byte) (value2 | value1); - case AND : - return (byte) (value2 & value1); - case XOR : - return (byte) (value2 ^ value1); - case XNOR : - return (byte) ~(value1 ^ value2); - case REPLACE : - default : - // Old value is replaced by new value. - return value2; - } - } - - /** - * This method combines a given bitmap with the current instance. - * <p> - * Parts of the bitmap to blit that are outside of the target bitmap will be ignored. - * - * @param src - The bitmap that should be combined with the one of the current instance. - * @param dst - The destination bitmap. - * @param x - The x coordinate where the upper left corner of the bitmap to blit should be - * positioned. - * @param y - The y coordinate where the upper left corner of the bitmap to blit should be - * positioned. - * @param combinationOperator - The combination operator for combining two pixels. - */ - public static void blit(Bitmap src, Bitmap dst, int x, int y, CombinationOperator combinationOperator) { - - int startLine = 0; - int srcStartIdx = 0; - int srcEndIdx = (src.getRowStride() - 1); - - // Ignore those parts of the source bitmap which would be placed outside the target bitmap. - if (x < 0) { - srcStartIdx = -x; - x = 0; - } else if (x + src.getWidth() > dst.getWidth()) { - srcEndIdx -= (src.getWidth() + x - dst.getWidth()); - } + /** + * Returns the specified rectangle area of the bitmap. + * + * @param roi - A {@link Rectangle} that specifies the requested image section. + * @param src the given bitmap + * @return A {@code Bitmap} that represents the requested image section. + */ + public static Bitmap extract(final Rectangle roi, final Bitmap src) + { + final Bitmap dst = new Bitmap(roi.width, roi.height); + + final int upShift = roi.x & 0x07; + final int downShift = 8 - upShift; + int dstLineStartIdx = 0; + + final int padding = (8 - dst.getWidth() & 0x07); + int srcLineStartIdx = src.getByteIndex(roi.x, roi.y); + int srcLineEndIdx = src.getByteIndex(roi.x + roi.width - 1, roi.y); + final boolean usePadding = dst.getRowStride() == srcLineEndIdx + 1 - srcLineStartIdx; + + for (int y = roi.y; y < roi.getMaxY(); y++) + { + int srcIdx = srcLineStartIdx; + int dstIdx = dstLineStartIdx; + + if (srcLineStartIdx == srcLineEndIdx) + { + final byte pixels = (byte) (src.getByte(srcIdx) << upShift); + dst.setByte(dstIdx, unpad(padding, pixels)); + } + else if (upShift == 0) + { + for (int x = srcLineStartIdx; x <= srcLineEndIdx; x++) + { + byte value = src.getByte(srcIdx++); + + if (x == srcLineEndIdx && usePadding) + { + value = unpad(padding, value); + } + + dst.setByte(dstIdx++, value); + } + } + else + { + copyLine(src, dst, upShift, downShift, padding, srcLineStartIdx, srcLineEndIdx, + usePadding, srcIdx, dstIdx); + } + + srcLineStartIdx += src.getRowStride(); + srcLineEndIdx += src.getRowStride(); + dstLineStartIdx += dst.getRowStride(); + } - if (y < 0) { - startLine = -y; - y = 0; - srcStartIdx += src.getRowStride(); - srcEndIdx += src.getRowStride(); - } else if (y + src.getHeight() > dst.getHeight()) { - startLine = src.getHeight() + y - dst.getHeight(); + return dst; } - final int shiftVal1 = x & 0x07; - final int shiftVal2 = 8 - shiftVal1; - - final int padding = src.getWidth() & 0x07; - final int toShift = shiftVal2 - padding; - - final boolean useShift = (shiftVal2 & 0x07) != 0; - final boolean specialCase = src.getWidth() <= ((srcEndIdx - srcStartIdx) << 3) + shiftVal2; + private static void copyLine(Bitmap src, Bitmap dst, int sourceUpShift, int sourceDownShift, + int padding, int firstSourceByteOfLine, int lastSourceByteOfLine, boolean usePadding, + int sourceOffset, int targetOffset) + { + for (int x = firstSourceByteOfLine; x < lastSourceByteOfLine; x++) + { + + if (sourceOffset + 1 < src.getByteArray().length) + { + final boolean isLastByte = x + 1 == lastSourceByteOfLine; + byte value = (byte) (src.getByte(sourceOffset++) << sourceUpShift + | (src.getByte(sourceOffset) & 0xff) >>> sourceDownShift); + + if (isLastByte && !usePadding) + { + value = unpad(padding, value); + } + + dst.setByte(targetOffset++, value); + + if (isLastByte && usePadding) + { + value = unpad(padding, + (byte) ((src.getByte(sourceOffset) & 0xff) << sourceUpShift)); + dst.setByte(targetOffset, value); + } + + } + else + { + final byte value = (byte) (src.getByte(sourceOffset++) << sourceUpShift & 0xff); + dst.setByte(targetOffset++, value); + } + } + } - final int dstStartIdx = dst.getByteIndex(x, y); + /** + * Removes unnecessary bits from a byte. + * + * @param padding - The amount of unnecessary bits. + * @param value - The byte that should be cleaned up. + * @return A cleaned byte. + */ + private static byte unpad(int padding, byte value) + { + return (byte) (value >> padding << padding); + } - final int lastLine = Math.min(src.getHeight(), startLine + dst.getHeight()); + public static Bitmap subsample(Bitmap src, ImageReadParam param) + { + if (src == null) + throw new IllegalArgumentException("src must not be null"); + + if (param == null) + throw new IllegalArgumentException("param must not be null"); + + final int xSubsampling = param.getSourceXSubsampling(); + final int ySubsampling = param.getSourceYSubsampling(); + final int xSubsamplingOffset = param.getSubsamplingXOffset(); + final int ySubsamplingOffset = param.getSubsamplingYOffset(); + + final int dstWidth = (src.getWidth() - xSubsamplingOffset) / xSubsampling; + final int dstHeight = (src.getHeight() - ySubsamplingOffset) / ySubsampling; + + final Bitmap dst = new Bitmap(dstWidth, dstHeight); + + for (int yDst = 0, ySrc = ySubsamplingOffset; yDst < dst + .getHeight(); yDst++, ySrc += ySubsampling) + { + for (int xDst = 0, xSrc = xSubsamplingOffset; xDst < dst + .getWidth(); xDst++, xSrc += xSubsampling) + { + final byte pixel = src.getPixel(xSrc, ySrc); + if (pixel != 0) + dst.setPixel(xDst, yDst, pixel); + } + } - if (!useShift) { - blitUnshifted(src, dst, startLine, lastLine, dstStartIdx, srcStartIdx, srcEndIdx, combinationOperator); - } else if (specialCase) { - blitSpecialShifted(src, dst, startLine, lastLine, dstStartIdx, srcStartIdx, srcEndIdx, toShift, shiftVal1, - shiftVal2, combinationOperator); - } else { - blitShifted(src, dst, startLine, lastLine, dstStartIdx, srcStartIdx, srcEndIdx, toShift, shiftVal1, shiftVal2, - combinationOperator, padding); + return dst; } - } - private static void blitUnshifted(Bitmap src, Bitmap dst, int startLine, int lastLine, int dstStartIdx, - int srcStartIdx, int srcEndIdx, CombinationOperator op) { - - for (int dstLine = startLine; dstLine < lastLine; dstLine++, dstStartIdx += dst.getRowStride(), srcStartIdx += src.getRowStride(), srcEndIdx += src.getRowStride()) { - int dstIdx = dstStartIdx; + public static Bitmap subsampleX(Bitmap src, final int xSubsampling, + final int xSubsamplingOffset) + { + if (src == null) + throw new IllegalArgumentException("src must not be null"); + + final int dstHeight = (src.getWidth() - xSubsamplingOffset) / xSubsampling; + final Bitmap dst = new Bitmap(src.getWidth(), dstHeight); + + for (int yDst = 0; yDst < dst.getHeight(); yDst++) + { + for (int xDst = 0, xSrc = xSubsamplingOffset; xDst < dst + .getWidth(); xDst++, xSrc += xSubsampling) + { + final byte pixel = src.getPixel(xSrc, yDst); + if (pixel != 0) + dst.setPixel(xDst, yDst, pixel); + } + } - // Go through the bytes in a line of the Symbol - for (int srcIdx = srcStartIdx; srcIdx <= srcEndIdx; srcIdx++) { - byte oldByte = dst.getByte(dstIdx); - byte newByte = src.getByte(srcIdx); - dst.setByte(dstIdx++, Bitmaps.combineBytes(oldByte, newByte, op)); - } + return dst; } - } - private static void blitSpecialShifted(Bitmap src, Bitmap dst, int startLine, int lastLine, int dstStartIdx, - int srcStartIdx, int srcEndIdx, int toShift, int shiftVal1, int shiftVal2, CombinationOperator op) { + public static Bitmap subsampleY(Bitmap src, final int ySubsampling, + final int ySubsamplingOffset) + { + if (src == null) + throw new IllegalArgumentException("src must not be null"); + + final int dstWidth = (src.getWidth() - ySubsamplingOffset) / ySubsampling; + final Bitmap dst = new Bitmap(dstWidth, src.getHeight()); + + for (int yDst = 0, ySrc = ySubsamplingOffset; yDst < dst + .getHeight(); yDst++, ySrc += ySubsampling) + { + for (int xDst = 0; xDst < dst.getWidth(); xDst++) + { + final byte pixel = src.getPixel(xDst, ySrc); + if (pixel != 0) + dst.setPixel(xDst, yDst, pixel); + } + } - for (int dstLine = startLine; dstLine < lastLine; dstLine++, dstStartIdx += dst.getRowStride(), srcStartIdx += src.getRowStride(), srcEndIdx += src.getRowStride()) { - short register = 0; - int dstIdx = dstStartIdx; + return dst; + } - // Go through the bytes in a line of the Symbol - for (int srcIdx = srcStartIdx; srcIdx <= srcEndIdx; srcIdx++) { - byte oldByte = dst.getByte(dstIdx); - register = (short) ((register | src.getByte(srcIdx) & 0xff) << shiftVal2); - byte newByte = (byte) (register >> 8); + /** + * The method combines two given bytes with an logical operator. + * <p> + * The JBIG2 Standard specifies 5 possible combinations of bytes.<br> + * <p> + * <b>Hint:</b> Please take a look at ISO/IEC 14492:2001 (E) for detailed definition and description of the + * operators. + * + * @param value1 - The value that should be combined with value2. + * @param value2 - The value that should be combined with value1. + * @param op - The specified combination operator. + * + * @return The combination result. + */ + public static byte combineBytes(byte value1, byte value2, CombinationOperator op) + { + + switch (op) + { + case OR: + return (byte) (value2 | value1); + case AND: + return (byte) (value2 & value1); + case XOR: + return (byte) (value2 ^ value1); + case XNOR: + return (byte) ~(value1 ^ value2); + case REPLACE: + default: + // Old value is replaced by new value. + return value2; + } + } - if (srcIdx == srcEndIdx) { - newByte = unpad(toShift, newByte); + /** + * This method combines a given bitmap with the current instance. + * <p> + * Parts of the bitmap to blit that are outside of the target bitmap will be ignored. + * + * @param src - The bitmap that should be combined with the one of the current instance. + * @param dst - The destination bitmap. + * @param x - The x coordinate where the upper left corner of the bitmap to blit should be positioned. + * @param y - The y coordinate where the upper left corner of the bitmap to blit should be positioned. + * @param combinationOperator - The combination operator for combining two pixels. + */ + public static void blit(Bitmap src, Bitmap dst, int x, int y, + CombinationOperator combinationOperator) + { + + int startLine = 0; + int srcStartIdx = 0; + int srcEndIdx = (src.getRowStride() - 1); + + // Ignore those parts of the source bitmap which would be placed outside the target bitmap. + if (x < 0) + { + srcStartIdx = -x; + x = 0; + } + else if (x + src.getWidth() > dst.getWidth()) + { + srcEndIdx -= (src.getWidth() + x - dst.getWidth()); } - dst.setByte(dstIdx++, Bitmaps.combineBytes(oldByte, newByte, op)); - register <<= shiftVal1; - } - } - } + if (y < 0) + { + startLine = -y; + y = 0; + srcStartIdx += src.getRowStride(); + srcEndIdx += src.getRowStride(); + } + else if (y + src.getHeight() > dst.getHeight()) + { + startLine = src.getHeight() + y - dst.getHeight(); + } - private static void blitShifted(Bitmap src, Bitmap dst, int startLine, int lastLine, int dstStartIdx, - int srcStartIdx, int srcEndIdx, int toShift, int shiftVal1, int shiftVal2, CombinationOperator op, int padding) { + final int shiftVal1 = x & 0x07; + final int shiftVal2 = 8 - shiftVal1; - for (int dstLine = startLine; dstLine < lastLine; dstLine++, dstStartIdx += dst.getRowStride(), srcStartIdx += src.getRowStride(), srcEndIdx += src.getRowStride()) { - short register = 0; - int dstIdx = dstStartIdx; + final int padding = src.getWidth() & 0x07; + final int toShift = shiftVal2 - padding; - // Go through the bytes in a line of the symbol - for (int srcIdx = srcStartIdx; srcIdx <= srcEndIdx; srcIdx++) { - byte oldByte = dst.getByte(dstIdx); - register = (short) ((register | src.getByte(srcIdx) & 0xff) << shiftVal2); + final boolean useShift = (shiftVal2 & 0x07) != 0; + final boolean specialCase = src.getWidth() <= ((srcEndIdx - srcStartIdx) << 3) + shiftVal2; - byte newByte = (byte) (register >> 8); - dst.setByte(dstIdx++, Bitmaps.combineBytes(oldByte, newByte, op)); + final int dstStartIdx = dst.getByteIndex(x, y); - register <<= shiftVal1; + final int lastLine = Math.min(src.getHeight(), startLine + dst.getHeight()); - if (srcIdx == srcEndIdx) { - newByte = (byte) (register >> (8 - shiftVal2)); + if (!useShift) + { + blitUnshifted(src, dst, startLine, lastLine, dstStartIdx, srcStartIdx, srcEndIdx, + combinationOperator); + } + else if (specialCase) + { + blitSpecialShifted(src, dst, startLine, lastLine, dstStartIdx, srcStartIdx, srcEndIdx, + toShift, shiftVal1, shiftVal2, combinationOperator); + } + else + { + blitShifted(src, dst, startLine, lastLine, dstStartIdx, srcStartIdx, srcEndIdx, toShift, + shiftVal1, shiftVal2, combinationOperator, padding); + } + } - if (padding != 0) { - newByte = unpad(8 + toShift, newByte); - } + private static void blitUnshifted(Bitmap src, Bitmap dst, int startLine, int lastLine, + int dstStartIdx, int srcStartIdx, int srcEndIdx, CombinationOperator op) + { + + for (int dstLine = startLine; dstLine < lastLine; dstLine++, dstStartIdx += dst + .getRowStride(), srcStartIdx += src.getRowStride(), srcEndIdx += src.getRowStride()) + { + int dstIdx = dstStartIdx; + + // Go through the bytes in a line of the Symbol + for (int srcIdx = srcStartIdx; srcIdx <= srcEndIdx; srcIdx++) + { + byte oldByte = dst.getByte(dstIdx); + byte newByte = src.getByte(srcIdx); + dst.setByte(dstIdx++, Bitmaps.combineBytes(oldByte, newByte, op)); + } + } + } - oldByte = dst.getByte(dstIdx); - dst.setByte(dstIdx, Bitmaps.combineBytes(oldByte, newByte, op)); + private static void blitSpecialShifted(Bitmap src, Bitmap dst, int startLine, int lastLine, + int dstStartIdx, int srcStartIdx, int srcEndIdx, int toShift, int shiftVal1, + int shiftVal2, CombinationOperator op) + { + + for (int dstLine = startLine; dstLine < lastLine; dstLine++, dstStartIdx += dst + .getRowStride(), srcStartIdx += src.getRowStride(), srcEndIdx += src.getRowStride()) + { + short register = 0; + int dstIdx = dstStartIdx; + + // Go through the bytes in a line of the Symbol + for (int srcIdx = srcStartIdx; srcIdx <= srcEndIdx; srcIdx++) + { + byte oldByte = dst.getByte(dstIdx); + register = (short) ((register | src.getByte(srcIdx) & 0xff) << shiftVal2); + byte newByte = (byte) (register >> 8); + + if (srcIdx == srcEndIdx) + { + newByte = unpad(toShift, newByte); + } + + dst.setByte(dstIdx++, Bitmaps.combineBytes(oldByte, newByte, op)); + register <<= shiftVal1; + } } - } } - } + private static void blitShifted(Bitmap src, Bitmap dst, int startLine, int lastLine, + int dstStartIdx, int srcStartIdx, int srcEndIdx, int toShift, int shiftVal1, + int shiftVal2, CombinationOperator op, int padding) + { + + for (int dstLine = startLine; dstLine < lastLine; dstLine++, dstStartIdx += dst + .getRowStride(), srcStartIdx += src.getRowStride(), srcEndIdx += src.getRowStride()) + { + short register = 0; + int dstIdx = dstStartIdx; + + // Go through the bytes in a line of the symbol + for (int srcIdx = srcStartIdx; srcIdx <= srcEndIdx; srcIdx++) + { + byte oldByte = dst.getByte(dstIdx); + register = (short) ((register | src.getByte(srcIdx) & 0xff) << shiftVal2); + + byte newByte = (byte) (register >> 8); + dst.setByte(dstIdx++, Bitmaps.combineBytes(oldByte, newByte, op)); + + register <<= shiftVal1; + + if (srcIdx == srcEndIdx) + { + newByte = (byte) (register >> (8 - shiftVal2)); + + if (padding != 0) + { + newByte = unpad(8 + toShift, newByte); + } + + oldByte = dst.getByte(dstIdx); + dst.setByte(dstIdx, Bitmaps.combineBytes(oldByte, newByte, op)); + } + } + } + } }
http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/30839c32/src/main/java/org/apache/pdfbox/jbig2/image/Filter.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/pdfbox/jbig2/image/Filter.java b/src/main/java/org/apache/pdfbox/jbig2/image/Filter.java index 2b81957..bdcdad6 100644 --- a/src/main/java/org/apache/pdfbox/jbig2/image/Filter.java +++ b/src/main/java/org/apache/pdfbox/jbig2/image/Filter.java @@ -17,441 +17,512 @@ package org.apache.pdfbox.jbig2.image; - -abstract class Filter { - - /** - * Find a filter name by its type. - * - * @param type the filter type - * @return filter name - */ - public static String nameByType(final FilterType type) { - if (type == null) - throw new IllegalArgumentException("type must not be null"); - return type.name(); - } - - /** - * Find a filter type by its name. - * - * @param name the filter name - * @return filter type - */ - public static FilterType typeByName(final String name) { - if (name == null) - throw new IllegalArgumentException("name must not be null"); - return FilterType.valueOf(name); - } - - /** - * Find a filter by its type. - * - * @param type the filter type - * @return the Filter - */ - public static Filter byType(final FilterType type) { - switch (type){ - case Bessel : - return new Bessel(); - case Blackman : - return new Blackman(); - case Box : - return new Box(); - case Catrom : - return new Catrom(); - case Cubic : - return new Cubic(); - case Gaussian : - return new Gaussian(); - case Hamming : - return new Hamming(); - case Hanning : - return new Hanning(); - case Hermite : - return new Hermite(); - case Lanczos : - return new Lanczos(); - case Mitchell : - return new Mitchell(); - case Point : - return new Point(); - case Quadratic : - return new Quadratic(); - case Sinc : - return new Sinc(); - case Triangle : - return new Triangle(); - } - throw new IllegalArgumentException("No filter for given type."); - } - - public static final class Bessel extends Filter { - public Bessel() { - super(false, 3.2383, 1.0); - } - - private double J1(final double x) { - double p, q; - - int i; - - final double Pone[] = { - 0.581199354001606143928050809e+21, -0.6672106568924916298020941484e+20, 0.2316433580634002297931815435e+19, - -0.3588817569910106050743641413e+17, 0.2908795263834775409737601689e+15, -0.1322983480332126453125473247e+13, - 0.3413234182301700539091292655e+10, -0.4695753530642995859767162166e+7, 0.270112271089232341485679099e+4 - }, Qone[] = { - 0.11623987080032122878585294e+22, 0.1185770712190320999837113348e+20, 0.6092061398917521746105196863e+17, - 0.2081661221307607351240184229e+15, 0.5243710262167649715406728642e+12, 0.1013863514358673989967045588e+10, - 0.1501793594998585505921097578e+7, 0.1606931573481487801970916749e+4, 0.1e+1 - }; - - p = Pone[8]; - q = Qone[8]; - for (i = 7; i >= 0; i--) { - p = p * x * x + Pone[i]; - q = q * x * x + Qone[i]; - } - return p / q; - } - - private double P1(final double x) { - double p, q; - - int i; - - final double Pone[] = { - 0.352246649133679798341724373e+5, 0.62758845247161281269005675e+5, 0.313539631109159574238669888e+5, - 0.49854832060594338434500455e+4, 0.2111529182853962382105718e+3, 0.12571716929145341558495e+1 - }, Qone[] = { - 0.352246649133679798068390431e+5, 0.626943469593560511888833731e+5, 0.312404063819041039923015703e+5, - 0.4930396490181088979386097e+4, 0.2030775189134759322293574e+3, 0.1e+1 - }; - - p = Pone[5]; - q = Qone[5]; - for (i = 4; i >= 0; i--) { - p = p * (8.0 / x) * (8.0 / x) + Pone[i]; - q = q * (8.0 / x) * (8.0 / x) + Qone[i]; - } - return p / q; - } - - private double Q1(final double x) { - double p, q; - - int i; - - final double Pone[] = { - 0.3511751914303552822533318e+3, 0.7210391804904475039280863e+3, 0.4259873011654442389886993e+3, - 0.831898957673850827325226e+2, 0.45681716295512267064405e+1, 0.3532840052740123642735e-1 - }, Qone[] = { - 0.74917374171809127714519505e+4, 0.154141773392650970499848051e+5, 0.91522317015169922705904727e+4, - 0.18111867005523513506724158e+4, 0.1038187585462133728776636e+3, 0.1e+1 - }; - - p = Pone[5]; - q = Qone[5]; - for (i = 4; i >= 0; i--) { - p = p * (8.0 / x) * (8.0 / x) + Pone[i]; - q = q * (8.0 / x) * (8.0 / x) + Qone[i]; - } - return p / q; - } - - private double BesselOrderOne(double x) { - double p, q; - - if (x == 0.0) - return 0.0; - p = x; - if (x < 0.0) - x = -x; - if (x < 8.0) - return p * J1(x); - q = Math.sqrt(2.0 / (Math.PI * x)) - * (P1(x) * (1.0 / Math.sqrt(2.0) * (Math.sin(x) - Math.cos(x))) - 8.0 / x * Q1(x) - * (-1.0 / Math.sqrt(2.0) * (Math.sin(x) + Math.cos(x)))); - if (p < 0.0) - q = -q; - return q; - } - - @Override - public double f(final double x) { - if (x == 0.0) - return Math.PI / 4.0; - return BesselOrderOne(Math.PI * x) / (2.0 * x); - } - } - - public static final class Blackman extends Filter { - @Override - public double f(final double x) { - return 0.42 + 0.50 * Math.cos(Math.PI * x) + 0.08 * Math.cos(2.0 * Math.PI * x); - } - } - - public static class Box extends Filter { - public Box() { - super(true, .5, 1.0); - } - - public Box(final double supp) { - super(true, supp, 1.0); - } - - @Override - public double f(final double x) { - if (x >= -0.5 && x < 0.5) - return 1.0; - return 0.0; - } - } - - public static final class Point extends Box { - public Point() { - super(0); - } - - @Override - public double fWindowed(double x) { - // don't apply windowing as we have a radius of zero. - return super.f(x); - } - } - - public static final class Catrom extends Filter { - public Catrom() { - super(true, 2.0, 1.0); - } - - @Override - public double f(double x) { - if (x < 0) - x = -x; - if (x < 1.0) - return 0.5 * (2.0 + x * x * (-5.0 + x * 3.0)); - if (x < 2.0) - return 0.5 * (4.0 + x * (-8.0 + x * (5.0 - x))); - return 0.0; - } - } - - public static final class Cubic extends Filter { - public Cubic() { - super(false, 2.0, 1.0); - } - - @Override - public double f(double x) { - if (x < 0) - x = -x; - if (x < 1.0) - return 0.5 * x * x * x - x * x + 2.0 / 3.0; - if (x < 2.0) { - x = 2.0 - x; - return 1.0 / 6.0 * x * x * x; - } - return 0.0; - } - } - - public static final class Gaussian extends Filter { - public Gaussian() { - super(false, 1.25, 1.0); - } - - @Override - public double f(final double x) { - return Math.exp(-2.0 * x * x) * Math.sqrt(2.0 / Math.PI); - } - } - - public static final class Hamming extends Filter { - @Override - public double f(final double x) { - return 0.54 + 0.46 * Math.cos(Math.PI * x); - } - } - - public static final class Hanning extends Filter { - @Override - public double f(final double x) { - return 0.5 + 0.5 * Math.cos(Math.PI * x); - } - } - - public static final class Hermite extends Filter { - @Override - public double f(double x) { - if (x < 0) { - x = -x; - } - - if (x < 1.0) { - return (2.0 * x - 3.0) * x * x + 1.0; - } - return 0.0; - } - } - - public static final class Lanczos extends Filter { - public Lanczos() { - super(true, 3.0, 1.0); - } - - @Override - public double f(double x) { - if (x < 0) - x = -x; - if (x < 3.0) - return (float) (sinc(x) * sinc(x / 3.0)); - return 0.0; - } - - private double sinc(double value) { - if (value != 0.0f) { - value = value * Math.PI; - return Math.sin(value) / value; - } else { - return 1.0; - } - } - - } - - public static final class Mitchell extends Filter { - public Mitchell() { - super(false, 2.0, 1.0); - } - - @Override - public double f(double x) { - double b, c; - - b = 1.0 / 3.0; - c = 1.0 / 3.0; - if (x < 0) - x = -x; - if (x < 1.0) { - x = (12.0 - 9.0 * b - 6.0 * c) * (x * x * x) + (-18.0 + 12.0 * b + 6.0 * c) * x * x + (6.0 - 2.0 * b); - return x / 6.0; - } - if (x < 2.0) { - x = (-1.0 * b - 6.0 * c) * (x * x * x) + (6.0 * b + 30.0 * c) * x * x + (-12.0 * b - 48.0 * c) * x - + (8.0 * b + 24.0 * c); - return x / 6.0; - } - return 0.0; - } - } - - public static final class Quadratic extends Filter { - public Quadratic() { - super(false, 1.5, 1.0); - } - - @Override - public double f(double x) { - if (x < 0) - x = -x; - if (x < 0.5) - return 0.75 - x * x; - if (x < 1.5) { - x -= 1.5; - return 0.5 * x * x; - } - return 0.0; - } - } - - public static final class Sinc extends Filter { - public Sinc() { - super(true, 4.0, 1.0); - } - - @Override - public double f(double x) { - x *= Math.PI; - if (x != 0.0) - return Math.sin(x) / x; - return 1.0; - } - } - - public static final class Triangle extends Filter { - @Override - public double f(double x) { - if (x < 0.0) - x = -x; - if (x < 1.0) - return 1.0 - x; - return 0.0; +abstract class Filter +{ + + /** + * Find a filter name by its type. + * + * @param type the filter type + * @return filter name + */ + public static String nameByType(final FilterType type) + { + if (type == null) + throw new IllegalArgumentException("type must not be null"); + return type.name(); + } + + /** + * Find a filter type by its name. + * + * @param name the filter name + * @return filter type + */ + public static FilterType typeByName(final String name) + { + if (name == null) + throw new IllegalArgumentException("name must not be null"); + return FilterType.valueOf(name); + } + + /** + * Find a filter by its type. + * + * @param type the filter type + * @return the Filter + */ + public static Filter byType(final FilterType type) + { + switch (type) + { + case Bessel: + return new Bessel(); + case Blackman: + return new Blackman(); + case Box: + return new Box(); + case Catrom: + return new Catrom(); + case Cubic: + return new Cubic(); + case Gaussian: + return new Gaussian(); + case Hamming: + return new Hamming(); + case Hanning: + return new Hanning(); + case Hermite: + return new Hermite(); + case Lanczos: + return new Lanczos(); + case Mitchell: + return new Mitchell(); + case Point: + return new Point(); + case Quadratic: + return new Quadratic(); + case Sinc: + return new Sinc(); + case Triangle: + return new Triangle(); + } + throw new IllegalArgumentException("No filter for given type."); + } + + public static final class Bessel extends Filter + { + public Bessel() + { + super(false, 3.2383, 1.0); + } + + private double J1(final double x) + { + double p, q; + + int i; + + final double Pone[] = { 0.581199354001606143928050809e+21, + -0.6672106568924916298020941484e+20, 0.2316433580634002297931815435e+19, + -0.3588817569910106050743641413e+17, 0.2908795263834775409737601689e+15, + -0.1322983480332126453125473247e+13, 0.3413234182301700539091292655e+10, + -0.4695753530642995859767162166e+7, 0.270112271089232341485679099e+4 }, + Qone[] = { 0.11623987080032122878585294e+22, 0.1185770712190320999837113348e+20, + 0.6092061398917521746105196863e+17, 0.2081661221307607351240184229e+15, + 0.5243710262167649715406728642e+12, 0.1013863514358673989967045588e+10, + 0.1501793594998585505921097578e+7, 0.1606931573481487801970916749e+4, + 0.1e+1 }; + + p = Pone[8]; + q = Qone[8]; + for (i = 7; i >= 0; i--) + { + p = p * x * x + Pone[i]; + q = q * x * x + Qone[i]; + } + return p / q; + } + + private double P1(final double x) + { + double p, q; + + int i; + + final double Pone[] = { 0.352246649133679798341724373e+5, + 0.62758845247161281269005675e+5, 0.313539631109159574238669888e+5, + 0.49854832060594338434500455e+4, 0.2111529182853962382105718e+3, + 0.12571716929145341558495e+1 }, + Qone[] = { 0.352246649133679798068390431e+5, 0.626943469593560511888833731e+5, + 0.312404063819041039923015703e+5, 0.4930396490181088979386097e+4, + 0.2030775189134759322293574e+3, 0.1e+1 }; + + p = Pone[5]; + q = Qone[5]; + for (i = 4; i >= 0; i--) + { + p = p * (8.0 / x) * (8.0 / x) + Pone[i]; + q = q * (8.0 / x) * (8.0 / x) + Qone[i]; + } + return p / q; + } + + private double Q1(final double x) + { + double p, q; + + int i; + + final double Pone[] = { 0.3511751914303552822533318e+3, 0.7210391804904475039280863e+3, + 0.4259873011654442389886993e+3, 0.831898957673850827325226e+2, + 0.45681716295512267064405e+1, 0.3532840052740123642735e-1 }, + Qone[] = { 0.74917374171809127714519505e+4, 0.154141773392650970499848051e+5, + 0.91522317015169922705904727e+4, 0.18111867005523513506724158e+4, + 0.1038187585462133728776636e+3, 0.1e+1 }; + + p = Pone[5]; + q = Qone[5]; + for (i = 4; i >= 0; i--) + { + p = p * (8.0 / x) * (8.0 / x) + Pone[i]; + q = q * (8.0 / x) * (8.0 / x) + Qone[i]; + } + return p / q; + } + + private double BesselOrderOne(double x) + { + double p, q; + + if (x == 0.0) + return 0.0; + p = x; + if (x < 0.0) + x = -x; + if (x < 8.0) + return p * J1(x); + q = Math.sqrt(2.0 / (Math.PI * x)) + * (P1(x) * (1.0 / Math.sqrt(2.0) * (Math.sin(x) - Math.cos(x))) - 8.0 / x + * Q1(x) * (-1.0 / Math.sqrt(2.0) * (Math.sin(x) + Math.cos(x)))); + if (p < 0.0) + q = -q; + return q; + } + + @Override + public double f(final double x) + { + if (x == 0.0) + return Math.PI / 4.0; + return BesselOrderOne(Math.PI * x) / (2.0 * x); + } + } + + public static final class Blackman extends Filter + { + @Override + public double f(final double x) + { + return 0.42 + 0.50 * Math.cos(Math.PI * x) + 0.08 * Math.cos(2.0 * Math.PI * x); + } + } + + public static class Box extends Filter + { + public Box() + { + super(true, .5, 1.0); + } + + public Box(final double supp) + { + super(true, supp, 1.0); + } + + @Override + public double f(final double x) + { + if (x >= -0.5 && x < 0.5) + return 1.0; + return 0.0; + } + } + + public static final class Point extends Box + { + public Point() + { + super(0); + } + + @Override + public double fWindowed(double x) + { + // don't apply windowing as we have a radius of zero. + return super.f(x); + } + } + + public static final class Catrom extends Filter + { + public Catrom() + { + super(true, 2.0, 1.0); + } + + @Override + public double f(double x) + { + if (x < 0) + x = -x; + if (x < 1.0) + return 0.5 * (2.0 + x * x * (-5.0 + x * 3.0)); + if (x < 2.0) + return 0.5 * (4.0 + x * (-8.0 + x * (5.0 - x))); + return 0.0; + } + } + + public static final class Cubic extends Filter + { + public Cubic() + { + super(false, 2.0, 1.0); + } + + @Override + public double f(double x) + { + if (x < 0) + x = -x; + if (x < 1.0) + return 0.5 * x * x * x - x * x + 2.0 / 3.0; + if (x < 2.0) + { + x = 2.0 - x; + return 1.0 / 6.0 * x * x * x; + } + return 0.0; + } + } + + public static final class Gaussian extends Filter + { + public Gaussian() + { + super(false, 1.25, 1.0); + } + + @Override + public double f(final double x) + { + return Math.exp(-2.0 * x * x) * Math.sqrt(2.0 / Math.PI); + } + } + + public static final class Hamming extends Filter + { + @Override + public double f(final double x) + { + return 0.54 + 0.46 * Math.cos(Math.PI * x); + } + } + + public static final class Hanning extends Filter + { + @Override + public double f(final double x) + { + return 0.5 + 0.5 * Math.cos(Math.PI * x); + } + } + + public static final class Hermite extends Filter + { + @Override + public double f(double x) + { + if (x < 0) + { + x = -x; + } + + if (x < 1.0) + { + return (2.0 * x - 3.0) * x * x + 1.0; + } + return 0.0; + } + } + + public static final class Lanczos extends Filter + { + public Lanczos() + { + super(true, 3.0, 1.0); + } + + @Override + public double f(double x) + { + if (x < 0) + x = -x; + if (x < 3.0) + return (float) (sinc(x) * sinc(x / 3.0)); + return 0.0; + } + + private double sinc(double value) + { + if (value != 0.0f) + { + value = value * Math.PI; + return Math.sin(value) / value; + } + else + { + return 1.0; + } + } + + } + + public static final class Mitchell extends Filter + { + public Mitchell() + { + super(false, 2.0, 1.0); + } + + @Override + public double f(double x) + { + double b, c; + + b = 1.0 / 3.0; + c = 1.0 / 3.0; + if (x < 0) + x = -x; + if (x < 1.0) + { + x = (12.0 - 9.0 * b - 6.0 * c) * (x * x * x) + (-18.0 + 12.0 * b + 6.0 * c) * x * x + + (6.0 - 2.0 * b); + return x / 6.0; + } + if (x < 2.0) + { + x = (-1.0 * b - 6.0 * c) * (x * x * x) + (6.0 * b + 30.0 * c) * x * x + + (-12.0 * b - 48.0 * c) * x + (8.0 * b + 24.0 * c); + return x / 6.0; + } + return 0.0; + } + } + + public static final class Quadratic extends Filter + { + public Quadratic() + { + super(false, 1.5, 1.0); + } + + @Override + public double f(double x) + { + if (x < 0) + x = -x; + if (x < 0.5) + return 0.75 - x * x; + if (x < 1.5) + { + x -= 1.5; + return 0.5 * x * x; + } + return 0.0; + } + } + + public static final class Sinc extends Filter + { + public Sinc() + { + super(true, 4.0, 1.0); + } + + @Override + public double f(double x) + { + x *= Math.PI; + if (x != 0.0) + return Math.sin(x) / x; + return 1.0; + } + } + + public static final class Triangle extends Filter + { + @Override + public double f(double x) + { + if (x < 0.0) + x = -x; + if (x < 1.0) + return 1.0 - x; + return 0.0; + } + } + + /** + * is this filter cardinal? ie, does func(x) = (x==0) for integer x? + */ + final boolean cardinal; + + /** radius of nonzero portion */ + double support; + + /** blur factor (1=normal) */ + double blur; + + protected Filter() + { + this(true, 1.0, 1.0); + } + + protected Filter(final boolean cardinal, final double support, final double blur) + { + this.cardinal = cardinal; + this.support = support; + this.blur = blur; + } + + public double fWindowed(double x) + { + return x < -support || x > support ? 0 : f(x); + } + + public abstract double f(double x); + + /** + * Return the filter name. + * + * @return the filter's name + */ + public String getName() + { + return getClass().getSimpleName(); + } + + /** + * @return the support + */ + public double getSupport() + { + return support; + } + + /** + * @param support the support to set + */ + public void setSupport(final double support) + { + this.support = support; + } + + /** + * @return the blur + */ + public double getBlur() + { + return blur; + } + + /** + * @param blur the blur to set + */ + public void setBlur(final double blur) + { + this.blur = blur; } - } - - /** - * is this filter cardinal? ie, does func(x) = (x==0) for integer x? - */ - final boolean cardinal; - - /** radius of nonzero portion */ - double support; - - /** blur factor (1=normal) */ - double blur; - - protected Filter() { - this(true, 1.0, 1.0); - } - - protected Filter(final boolean cardinal, final double support, final double blur) { - this.cardinal = cardinal; - this.support = support; - this.blur = blur; - } - - public double fWindowed(double x) { - return x < -support || x > support ? 0 : f(x); - } - - public abstract double f(double x); - - /** - * Return the filter name. - * - * @return the filter's name - */ - public String getName() { - return getClass().getSimpleName(); - } - - /** - * @return the support - */ - public double getSupport() { - return support; - } - - /** - * @param support the support to set - */ - public void setSupport(final double support) { - this.support = support; - } - - /** - * @return the blur - */ - public double getBlur() { - return blur; - } - - /** - * @param blur the blur to set - */ - public void setBlur(final double blur) { - this.blur = blur; - } } http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/30839c32/src/main/java/org/apache/pdfbox/jbig2/image/FilterType.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/pdfbox/jbig2/image/FilterType.java b/src/main/java/org/apache/pdfbox/jbig2/image/FilterType.java index 1420726..6d3e0e2 100644 --- a/src/main/java/org/apache/pdfbox/jbig2/image/FilterType.java +++ b/src/main/java/org/apache/pdfbox/jbig2/image/FilterType.java @@ -17,34 +17,22 @@ package org.apache.pdfbox.jbig2.image; - /** * A FilterType enum for defining certain downscale filters to apply. */ -public enum FilterType { - Bessel, - Blackman, - Box, - Catrom, - Cubic, - Gaussian, - Hamming, - Hanning, - Hermite, - Lanczos, - Mitchell, - Point, - Quadratic, - Sinc, - Triangle; +public enum FilterType +{ + Bessel, Blackman, Box, Catrom, Cubic, Gaussian, Hamming, Hanning, Hermite, Lanczos, Mitchell, Point, Quadratic, Sinc, Triangle; - private static FilterType defaultFilter = Triangle; + private static FilterType defaultFilter = Triangle; - public static void setDefaultFilterType(FilterType defaultFilter) { - FilterType.defaultFilter = defaultFilter; - } + public static void setDefaultFilterType(FilterType defaultFilter) + { + FilterType.defaultFilter = defaultFilter; + } - public static FilterType getDefaultFilterType() { - return defaultFilter; - } -} \ No newline at end of file + public static FilterType getDefaultFilterType() + { + return defaultFilter; + } +} http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/30839c32/src/main/java/org/apache/pdfbox/jbig2/image/ParameterizedFilter.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/pdfbox/jbig2/image/ParameterizedFilter.java b/src/main/java/org/apache/pdfbox/jbig2/image/ParameterizedFilter.java index bc32755..e801296 100644 --- a/src/main/java/org/apache/pdfbox/jbig2/image/ParameterizedFilter.java +++ b/src/main/java/org/apache/pdfbox/jbig2/image/ParameterizedFilter.java @@ -19,51 +19,56 @@ package org.apache.pdfbox.jbig2.image; import org.apache.pdfbox.jbig2.util.Utils; -class ParameterizedFilter { - public ParameterizedFilter(final Filter f, final double scale) { - filter = f; - /* - * find scale of filter in a space (source space) when minifying, ascale=1/scale, but when - * magnifying, ascale=1 - */ - this.scale = f.blur * Math.max(1., 1. / scale); +class ParameterizedFilter +{ + public ParameterizedFilter(final Filter f, final double scale) + { + filter = f; + /* + * find scale of filter in a space (source space) when minifying, ascale=1/scale, but when magnifying, ascale=1 + */ + this.scale = f.blur * Math.max(1., 1. / scale); - /* - * find support radius of scaled filter if ax.supp and ay.supp are both <=.5 then we've got - * point sampling. Point sampling is essentially a special filter whose width is fixed at one - * source pixel. - */ - support = Math.max(.5, this.scale * f.support); - width = (int) Math.ceil(2. * support); - } + /* + * find support radius of scaled filter if ax.supp and ay.supp are both <=.5 then we've got point sampling. + * Point sampling is essentially a special filter whose width is fixed at one source pixel. + */ + support = Math.max(.5, this.scale * f.support); + width = (int) Math.ceil(2. * support); + } - public ParameterizedFilter(final Filter f, final double scale, final double support, final int width) { - filter = f; - this.scale = scale; - this.support = support; - this.width = width; - } + public ParameterizedFilter(final Filter f, final double scale, final double support, + final int width) + { + filter = f; + this.scale = scale; + this.support = support; + this.width = width; + } - final Filter filter; + final Filter filter; - /* filter scale (spacing between centers in a space) */ - final double scale; + /* filter scale (spacing between centers in a space) */ + final double scale; - /* scaled filter support radius */ - final double support; + /* scaled filter support radius */ + final double support; - /* filter width: max number of nonzero samples */ - final int width; + /* filter width: max number of nonzero samples */ + final int width; - public double eval(double center, int i) { - return filter.fWindowed((i + .5 - center) / scale); - } + public double eval(double center, int i) + { + return filter.fWindowed((i + .5 - center) / scale); + } - public int minIndex(double center) { - return Utils.floor(center - support); - } + public int minIndex(double center) + { + return Utils.floor(center - support); + } - public int maxIndex(double center) { - return Utils.ceil(center + support); - } -} \ No newline at end of file + public int maxIndex(double center) + { + return Utils.ceil(center + support); + } +}