Parse the image stream into a buffered image. Note that this is + // * guaranteed to be called after all the other setXXX methods have been + // * called.
+ // * + // *NOTE: the color convolving is extremely slow on large images. + // * It would be good to see if it could be moved out into the rendering + // * phases, where we might be able to scale the image down first.
data.length) { + // byte[] tempLargerData = new byte[tempExpectedSize]; + // System.arraycopy (data, 0, tempLargerData, 0, data.length); + // db = new DataBufferByte (tempLargerData, tempExpectedSize); + // raster = + // Raster.createWritableRaster (sm, db, new Point (0, 0)); + // } else { + // throw e; + // } + // } + // + // /* + // * Workaround for a bug on the Mac -- a class cast exception in + // * drawImage() due to the wrong data buffer type (?) + // */ + // BufferedImage bi = null; + // if (cm instanceof IndexColorModel) { + // IndexColorModel icm = (IndexColorModel) cm; + // + // // choose the image type based on the size + // int type = BufferedImage.TYPE_BYTE_BINARY; + // if (getBitsPerComponent() == 8) { + // type = BufferedImage.TYPE_BYTE_INDEXED; + // } + // + // // create the image with an explicit indexed color model. + // bi = new BufferedImage(getWidth(), getHeight(), type, icm); + // + // // set the data explicitly as well + // bi.setData(raster); + // } else { + // bi = new BufferedImage(cm, raster, true, null); + // } + // + // // hack to avoid *very* slow conversion + // ColorSpace cs = cm.getColorSpace(); + // ColorSpace rgbCS = ColorSpace.getInstance(ColorSpace.CS_sRGB); + // + // // add in the alpha data supplied by the SMask, if any + // PDFImage sMaskImage = getSMask(); + // if (sMaskImage != null) { + // BufferedImage si = sMaskImage.getImage(); + // + // BufferedImage outImage = new BufferedImage(getWidth(), + // getHeight(), BufferedImage.TYPE_INT_ARGB); + // + // int[] srcArray = new int[width]; + // int[] maskArray = new int[width]; + // + // for (int i = 0; i < height; i++) { + // bi.getRGB(0, i, width, 1, srcArray, 0, width); + // si.getRGB(0, i, width, 1, maskArray, 0, width); + // + // for (int j = 0; j < width; j++) { + // int ac = 0xff000000; + // + // maskArray[j] = ((maskArray[j] & 0xff) << 24) | (srcArray[j] & ~ac); + // } + // + // outImage.setRGB(0, i, width, 1, maskArray, 0, width); + // } + // + // bi = outImage; + // } + // + // return (bi); + // } + + /** + * Get the image's width + */ + public int getWidth() { + return width; + } + + /** + * Set the image's width + */ + protected void setWidth(int width) { + this.width = width; + } + + /** + * Get the image's height + */ + public int getHeight() { + return height; + } + + /** + * Set the image's height + */ + protected void setHeight(int height) { + this.height = height; + } + + /** + * set the color key mask. It is an array of start/end entries + * to indicate ranges of color indicies that should be masked out. + */ + private void setColorKeyMask(PDFObject maskArrayObject) throws IOException { + PDFObject[] maskObjects = maskArrayObject.getArray(); + colorKeyMask = null; + int[] masks = new int[maskObjects.length]; + for (int i = 0; i < masks.length; i++) { + masks[i] = maskObjects[i].getIntValue(); + } + colorKeyMask = masks; + } + + /** + * Get the colorspace associated with this image, or null if there + * isn't one + */ + protected PDFColorSpace getColorSpace() { + return colorSpace; + } + + /** + * Set the colorspace associated with this image + */ + protected void setColorSpace(PDFColorSpace colorSpace) { + this.colorSpace = colorSpace; + } + + /** + * Get the number of bits per component sample + */ + protected int getBitsPerComponent() { + return bpc; + } + + /** + * Set the number of bits per component sample + */ + protected void setBitsPerComponent(int bpc) { + this.bpc = bpc; + } + + /** + * Return whether or not this is an image mask + */ + public boolean isImageMask() { + return imageMask; + } + + /** + * Set whether or not this is an image mask + */ + public void setImageMask(boolean imageMask) { + this.imageMask = imageMask; + } + + /** + * Return the soft mask associated with this image + */ + public PDFImage getSMask() { + return sMask; + } + + /** + * Set the soft mask image + */ + protected void setSMask(PDFImage sMask) { + this.sMask = sMask; + } + + /** + * Get the decode array + */ + protected float[] getDecode() { + return decode; + } + + /** + * Set the decode array + */ + protected void setDecode(float[] decode) { + this.decode = decode; + } + + // /** + // * get a Java ColorModel consistent with the current color space, + // * number of bits per component and decode array + // * + // * @param bpc the number of bits per component + // */ + // private ColorModel getColorModel() { + // PDFColorSpace cs = getColorSpace(); + // + // if (cs instanceof IndexedColor) { + // IndexedColor ics = (IndexedColor) cs; + // + // byte[] components = ics.getColorComponents(); + // int num = ics.getCount(); + // + // // process the decode array + // if (decode != null) { + // byte[] normComps = new byte[components.length]; + // + // // move the components array around + // for (int i = 0; i < num; i++) { + // byte[] orig = new byte[1]; + // orig[0] = (byte) i; + // + // float[] res = normalize(orig, null, 0); + // int idx = (int) res[0]; + // + // normComps[i * 3] = components[idx * 3]; + // normComps[(i * 3) + 1] = components[(idx * 3) + 1]; + // normComps[(i * 3) + 2] = components[(idx * 3) + 2]; + // } + // + // components = normComps; + // } + // + // // make sure the size of the components array is 2 ^ numBits + // // since if it's not, Java will complain + // int correctCount = 1 << getBitsPerComponent(); + // if (correctCount < num) { + // byte[] fewerComps = new byte[correctCount * 3]; + // + // System.arraycopy(components, 0, fewerComps, 0, correctCount * 3); + // + // components = fewerComps; + // num = correctCount; + // } + // if (colorKeyMask == null || colorKeyMask.length == 0) { + // return new IndexColorModel(getBitsPerComponent(), num, components, + // 0, false); + // } else { + // byte[] aComps = new byte[num * 4]; + // int idx = 0; + // for (int i = 0; i < num; i++) { + // aComps[idx++] = components[(i * 3)]; + // aComps[idx++] = components[(i * 3) + 1]; + // aComps[idx++] = components[(i * 3) + 2]; + // aComps[idx++] = (byte) 0xFF; + // } + // for (int i = 0; i < colorKeyMask.length; i += 2) { + // for (int j = colorKeyMask[i]; j <= colorKeyMask[i + 1]; j++) { + // aComps[(j * 4) + 3] = 0; // make transparent + // } + // } + // return new IndexColorModel(getBitsPerComponent(), num, aComps, + // 0, true); + // } + // } else { + // int[] bits = new int[cs.getNumComponents()]; + // for (int i = 0; i < bits.length; i++) { + // bits[i] = getBitsPerComponent(); + // } + // + // return new DecodeComponentColorModel(cs.getColorSpace(), bits); + // } + // } + + /** + * Normalize an array of values to match the decode array + */ + private float[] normalize(byte[] pixels, float[] normComponents, int normOffset) { + if (normComponents == null) { + normComponents = new float[normOffset + pixels.length]; } - /** - * Get the image that this PDFImage generates. - * - * @return a buffered image containing the decoded image data - */ - public Bitmap getImage() { - try { - Bitmap bi = (Bitmap) imageObj.getCache(); + float[] decodeArray = getDecode(); - if (bi == null) { - if (!sShowImages) - throw new UnsupportedOperationException("do not show images"); - byte[] imgBytes = imageObj.getStream(); - bi = parseData(imgBytes); - // TODO [FHe]: is the cache useful on Android? - imageObj.setCache(bi); - } -// if(bi != null) -// ImageIO.write(bi, "png", new File("/tmp/test/" + System.identityHashCode(this) + ".png")); - return bi; - } catch (IOException ioe) { - System.out.println("Error reading image"); - ioe.printStackTrace(); - return null; - } catch (OutOfMemoryError e) { - // fix for too large images - Log.e(TAG, "image too large (OutOfMemoryError)"); - int size = 15; - int max = size-1; - int half = size/2-1; - Bitmap bi = Bitmap.createBitmap(size, size, Config.RGB_565); - Canvas c = new Canvas(bi); - c.drawColor(Color.RED); - Paint p = new Paint(); - p.setColor(Color.WHITE); - c.drawLine(0, 0, max, max, p); - c.drawLine(0, max, max, 0, p); - c.drawLine(half, 0, half, max, p); - c.drawLine(0, half, max, half, p); - return bi; - } + for (int i = 0; i < pixels.length; i++) { + int val = pixels[i] & 0xff; + int pow = ((int) Math.pow(2, getBitsPerComponent())) - 1; + float ymin = decodeArray[i * 2]; + float ymax = decodeArray[(i * 2) + 1]; + + normComponents[normOffset + i] = FunctionType0.interpolate(val, 0, pow, ymin, ymax); } - private Bitmap parseData(byte[] imgBytes) { - Bitmap bi; - long startTime = System.currentTimeMillis(); - // parse the stream data into an actual image - Log.i(TAG, "Creating Image width="+getWidth() + ", Height="+getHeight()+", bpc="+getBitsPerComponent()+",cs="+colorSpace); - if (colorSpace == null) { - throw new UnsupportedOperationException("image without colorspace"); - } else if (colorSpace.getType() == PDFColorSpace.COLORSPACE_RGB) { - int maxH = getHeight(); - int maxW = getWidth(); - if (imgBytes.length == 2*maxW*maxH) { - // decoded JPEG as RGB565 - bi = Bitmap.createBitmap(maxW, maxH, Config.RGB_565); - bi.copyPixelsFromBuffer(ByteBuffer.wrap(imgBytes)); - } - else { - // create RGB image - bi = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888); - int[] line = new int[maxW]; - int n=0; - for (int h = 0; h- * DCT is the format used by JPEG images, so this class simply - * loads the DCT-format bytes as an image, then reads the bytes out - * of the image to create the array. Unfortunately, their most - * likely use is to get turned BACK into an image, so this isn't - * terribly efficient... but is is general... don't hit, please. - *
- * The DCT-encoded stream may have 1, 3 or 4 samples per pixel, depending - * on the colorspace of the image. In decoding, we look for the colorspace - * in the stream object's dictionary to decide how to decode this image. - * If no colorspace is present, we guess 3 samples per pixel. - * - * @param dict the stream dictionary - * @param buf the DCT-encoded buffer - * @param params the parameters to the decoder (ignored) - * @return the decoded buffer - */ - protected static ByteBuffer decode(PDFObject dict, ByteBuffer buf, - PDFObject params) throws PDFParseException - { - // System.out.println("DCTDecode image info: "+params); - buf.rewind(); - - // copy the data into a byte array required by createimage - byte[] ary = new byte[buf.remaining()]; - buf.get(ary); + /** + * decode an array of bytes in DCT format. + *
+ * DCT is the format used by JPEG images, so this class simply + * loads the DCT-format bytes as an image, then reads the bytes out + * of the image to create the array. Unfortunately, their most + * likely use is to get turned BACK into an image, so this isn't + * terribly efficient... but is is general... don't hit, please. + *
+ * The DCT-encoded stream may have 1, 3 or 4 samples per pixel, depending
+ * on the colorspace of the image. In decoding, we look for the colorspace
+ * in the stream object's dictionary to decide how to decode this image.
+ * If no colorspace is present, we guess 3 samples per pixel.
+ *
+ * @param dict the stream dictionary
+ * @param buf the DCT-encoded buffer
+ * @param params the parameters to the decoder (ignored)
+ * @return the decoded buffer
+ */
+ protected static ByteBuffer decode(PDFObject dict, ByteBuffer buf, PDFObject params) throws PDFParseException {
+ // System.out.println("DCTDecode image info: "+params);
+ buf.rewind();
- Bitmap img = BitmapFactory.decodeByteArray(ary, 0, ary.length);
+ // copy the data into a byte array required by createimage
+ byte[] ary = new byte[buf.remaining()];
+ buf.get(ary);
+ Bitmap img = BitmapFactory.decodeByteArray(ary, 0, ary.length);
- if (img == null)
- throw new PDFParseException("could not decode image of compressed size "+ary.length);
- Config conf = img.getConfig();
- Log.e("ANDPDF.dctdecode", "decoded image type"+conf);
- int size = 4*img.getWidth()*img.getHeight();
- if (conf == Config.RGB_565)
- size = 2*img.getWidth()*img.getHeight();
- // TODO [FHe]: else ... what do we get for gray? Config.ALPHA_8?
-
- java.nio.ByteBuffer byteBuf = java.nio.ByteBuffer.allocate(size);
- img.copyPixelsToBuffer(byteBuf);
-
- ByteBuffer result = ByteBuffer.fromNIO(byteBuf);
- result.rewind();
- return result;
+ if (img == null) throw new PDFParseException("could not decode image of compressed size " + ary.length);
+ Config conf = img.getConfig();
+ Log.e("ANDPDF.dctdecode", "decoded image type" + conf);
+ int size = 4 * img.getWidth() * img.getHeight();
+ if (conf == Config.RGB_565) {
+ size = 2 * img.getWidth() * img.getHeight();
}
+ // TODO [FHe]: else ... what do we get for gray? Config.ALPHA_8?
+
+ java.nio.ByteBuffer byteBuf = java.nio.ByteBuffer.allocate(size);
+ img.copyPixelsToBuffer(byteBuf);
+ ByteBuffer result = ByteBuffer.fromNIO(byteBuf);
+ result.rewind();
+
+ return result;
+ }
}
diff --git a/build.gradle b/build.gradle
index 6a5c233..739ceb5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,7 +4,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:1.3.0'
+ classpath 'com.android.tools.build:gradle:2.0.0'
}
}
diff --git a/build/generated/mockable-android-21.jar b/build/generated/mockable-android-21.jar
new file mode 100644
index 0000000..9933d22
Binary files /dev/null and b/build/generated/mockable-android-21.jar differ
diff --git a/build/generated/mockable-android-23.jar b/build/generated/mockable-android-23.jar
new file mode 100644
index 0000000..61cf979
Binary files /dev/null and b/build/generated/mockable-android-23.jar differ
diff --git a/build/intermediates/dex-cache/cache.xml b/build/intermediates/dex-cache/cache.xml
index e070661..fba8db8 100644
--- a/build/intermediates/dex-cache/cache.xml
+++ b/build/intermediates/dex-cache/cache.xml
@@ -1,12 +1,2 @@
-