org.jpedal.parser.image.mask.MaskDataDecoder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of OpenViewerFX Show documentation
Show all versions of OpenViewerFX Show documentation
Open Source (LGPL) JavaFX PDF Viewer
/*
* ===========================================
* Java Pdf Extraction Decoding Access Library
* ===========================================
*
* Project Info: http://www.idrsolutions.com
* Help section for developers at http://www.idrsolutions.com/support/
*
* (C) Copyright 1997-2017 IDRsolutions and Contributors.
*
* This file is part of JPedal/JPDF2HTML5
*
@LICENSE@
*
* ---------------
* MaskDataDecoder.java
* ---------------
*/
package org.jpedal.parser.image.mask;
import com.idrsolutions.pdf.color.shading.BitReader;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import org.jpedal.color.ColorSpaces;
import org.jpedal.color.GenericColorSpace;
import org.jpedal.color.JPEGDecoder;
import org.jpedal.external.ExternalHandlers;
import org.jpedal.io.ColorSpaceConvertor;
import org.jpedal.objects.raw.PdfDictionary;
import org.jpedal.objects.raw.PdfObject;
import org.jpedal.parser.image.ImageCommands;
import org.jpedal.parser.image.data.ImageData;
/**
* @author markee
*/
public class MaskDataDecoder {
/**
* apply the SMask to image data directly as a component on argb
*
* @param imageData
* @param decodeColorData
* @return
*/
static byte[] applyMask(final ImageData imageData, final GenericColorSpace decodeColorData, final PdfObject newMask, final PdfObject XObject, final byte[] maskDataSream) {
final int[] maskArray = XObject.getIntArray(PdfDictionary.Mask);
byte[] objectData = imageData.getObjectData();
/*
* Image data
*/
final int w = imageData.getWidth();
final int h = imageData.getHeight();
final int d = imageData.getDepth();
objectData = convertData(decodeColorData, objectData, w, h, imageData, d, 1, null);
XObject.setIntNumber(PdfDictionary.BitsPerComponent, 8);
if (maskArray != null) {
objectData = applyMaskArray(w, h, objectData, maskArray);
} else {
objectData = applyMaskStream(maskDataSream, imageData, newMask, XObject);
}
//include Decode if present
final float[] maskDecodeArray = newMask.getFloatArray(PdfDictionary.Decode);
if (maskDecodeArray != null) {
ImageCommands.applyDecodeArray(objectData, maskDecodeArray.length / 2, maskDecodeArray, ColorSpaces.DeviceRGB);
}
return objectData;
}
public static BufferedImage applyMaskArray(final ImageData imageData, final int[] maskArray) {
final int bitDepth = imageData.getDepth();
// int negate = 8-bitDepth;
final int nComp = maskArray.length / 2;
final int dim = imageData.getWidth() * imageData.getHeight();
final BufferedImage img = new BufferedImage(imageData.getWidth(), imageData.getHeight(), BufferedImage.TYPE_INT_ARGB);
final int[] output = ((DataBufferInt) img.getRaster().getDataBuffer()).getData();
final byte[] data = imageData.getObjectData();
int r, g, b, t;
boolean isMask;
switch (bitDepth) {
case 1:
case 2:
case 4:
final BitReader reader = new BitReader(data, true);
for (int i = 0; i < dim; i++) {
if (nComp == 1) {
t = reader.getPositive(bitDepth);
isMask = t >= maskArray[0] && t <= maskArray[1];
if (!isMask) {
t ^= 0xff;
output[i] = (255 << 24) | (t << 16) | (t << 8) | t;
}
} else { //assume number of components is 3 at the moment we can fix in future;
r = reader.getPositive(bitDepth);
g = reader.getPositive(bitDepth);
b = reader.getPositive(bitDepth);
isMask = r >= maskArray[0] && r <= maskArray[1]
&& g >= maskArray[2] && g <= maskArray[3]
&& b >= maskArray[4] && b <= maskArray[5];
if (!isMask) {
r ^= 0xff;
g ^= 0xff;
b ^= 0xff;
output[i] = (255 << 24) | (r << 16) | (g << 8) | b;
}
}
}
break;
case 8:
int p = 0;
if (nComp == 1) {
for (int i = 0; i < dim; i++) {
t = data[p++] & 0xff;
isMask = t >= maskArray[0] && t <= maskArray[1];
if (!isMask) {
output[i] = (255 << 24) | (t << 16) | (t << 8) | t;
}
}
} else if (nComp == 3) {
for (int i = 0; i < dim; i++) {
r = data[p++] & 0xff;
g = data[p++] & 0xff;
b = data[p++] & 0xff;
isMask = r >= maskArray[0] && r <= maskArray[1]
&& g >= maskArray[2] && g <= maskArray[3]
&& b >= maskArray[4] && b <= maskArray[5];
if (!isMask) {
output[i] = (255 << 24) | (r << 16) | (g << 8) | b;
}
}
} else if (nComp == 4) {
//does nothing in LGPL library
ExternalHandlers.ImageLib.processJPEG(dim, data, p, maskArray, output);
}
break;
}
return img;
}
static byte[] applySMask(byte[] maskData, final ImageData imageData, final GenericColorSpace decodeColorData, final PdfObject newSMask, final PdfObject XObject) {
byte[] objectData = imageData.getObjectData();
/*
* Image data
*/
final int w = imageData.getWidth();
final int h = imageData.getHeight();
final int d = imageData.getDepth();
/*
* Smask data (ASSUME single component at moment)
*/
final int maskW = newSMask.getInt(PdfDictionary.Width);
final int maskH = newSMask.getInt(PdfDictionary.Height);
final int maskD = newSMask.getInt(PdfDictionary.BitsPerComponent);
objectData = convertData(decodeColorData, objectData, w, h, imageData, d, maskD, maskData);
//needs to be 'normalised to 8 bit'
if (maskD != 8) {
maskData = ColorSpaceConvertor.normaliseTo8Bit(maskD, maskW, maskH, maskData);
}
//add mask as a element so we now have argb
if (w == maskW && h == maskH) {
//System.out.println("Same size");
objectData = buildUnscaledByteArray(w, h, objectData, maskData);
} else if (w < maskW) { //mask bigger than image
//System.out.println("Mask bigger");
objectData = upScaleImageToMask(w, h, maskW, maskH, objectData, maskData);
XObject.setIntNumber(PdfDictionary.Width, maskW);
XObject.setIntNumber(PdfDictionary.Height, maskH);
} else {
//System.out.println("Image bigger");
objectData = upScaleMaskToImage(w, h, maskW, maskH, objectData, maskData);
}
XObject.setIntNumber(PdfDictionary.BitsPerComponent, 8);
// BufferedImage img= ColorSpaceConvertor.createARGBImage( XObject.getInt(PdfDictionary.Width), XObject.getInt(PdfDictionary.Height), objectData);
//
// try{
// ImageIO.write(img, "PNG", new java.io.File("/Users/markee/Desktop/img.png"));
// }catch(Exception e){}
//
return objectData;
}
/**
* apply the Mask streamto image data directly as a component on argb
*
* @param imageData
* @return
*/
static byte[] applyMaskStream(byte[] maskData, final ImageData imageData, final PdfObject newMask, final PdfObject XObject) {
byte[] objectData = imageData.getObjectData();
/*
* Image data
*/
final int w = imageData.getWidth();
final int h = imageData.getHeight();
/*
* mask data (ASSUME single component at moment)
*/
final int maskW = newMask.getInt(PdfDictionary.Width);
final int maskH = newMask.getInt(PdfDictionary.Height);
final int maskD = newMask.getInt(PdfDictionary.BitsPerComponent);
//needs to be 'normalised to 8 bit'
if (maskD != 8) {
maskData = ColorSpaceConvertor.normaliseTo8Bit(maskD, maskW, maskH, maskData);
}
//add mask as a element so we now have argb
if (w == maskW && h == maskH) {
//System.out.println("Same size");
objectData = buildUnscaledByteArray(w, h, objectData, maskData);
} else if (w < maskW) { //mask bigger than image
//System.out.println("Mask bigger");
objectData = upScaleImageToMask(w, h, maskW, maskH, objectData, maskData);
XObject.setIntNumber(PdfDictionary.Width, maskW);
XObject.setIntNumber(PdfDictionary.Height, maskH);
} else {
//System.out.println("Image bigger");
objectData = upScaleMaskToImage(w, h, maskW, maskH, objectData, maskData);
}
XObject.setIntNumber(PdfDictionary.BitsPerComponent, 8);
// BufferedImage img= ColorSpaceConvertor.createARGBImage( XObject.getInt(PdfDictionary.Width), XObject.getInt(PdfDictionary.Height), objectData);
//
// try{
// ImageIO.write(img, "PNG", new java.io.File("/Users/markee/Desktop/img.png"));
// }catch(Exception e){}
//
return objectData;
}
static byte[] convertSmaskData(final GenericColorSpace decodeColorData, byte[] objectData, final int w, final int h, final ImageData imageData, final int d, final int maskD, final byte[] maskData, final PdfObject smask) {
byte[] index = decodeColorData.getIndexedMap();
if (index != null) {
index = decodeColorData.convertIndexToRGB(index);
objectData = ColorSpaceConvertor.convertIndexToRGBByte(index, w, h, imageData.getCompCount(), imageData.getDepth(), objectData, false, false);
} else if (decodeColorData.getID() == ColorSpaces.CalRGB) {
} else if (decodeColorData.getID() == ColorSpaces.DeviceRGB) {
if (d == 8) { //baseline_screens/adobe/PP_download.pdf is actually 4 bit
check4BitData(objectData);
}
final float[] decodeArr = smask.getFloatArray(PdfDictionary.Decode);
if (decodeArr != null && decodeArr[0] == 1 && decodeArr[1] == 0) { // data inverted refer to dec2011/example.pdf
for (int i = 0; i < maskData.length; i++) {
maskData[i] ^= 0xff;
}
}
} else if (decodeColorData.getID() == ColorSpaces.DeviceGray && imageData.isJPX()) {
if (maskData != null && maskD == 1) {
for (int ii = 0; ii < maskData.length; ii++) {
maskData[ii] = (byte) (maskData[ii] ^ 255);
}
}
} else if (!imageData.isDCT() && !imageData.isJPX()) {
//convert the data to rgb (last parameter is used in CalRGB so left in to make method same in all)
objectData = decodeColorData.dataToRGBByteArray(objectData, w, h);
//System.out.println(maskColorSpace.getParameterConstant(PdfDictionary.ColorSpace)+" "+newSMask.getObjectRefAsString());
}
return objectData;
}
static byte[] convertData(final GenericColorSpace decodeColorData, byte[] objectData, final int w, final int h, final ImageData imageData, final int d, final int maskD, final byte[] maskData) {
byte[] index = decodeColorData.getIndexedMap();
if (index != null) {
index = decodeColorData.convertIndexToRGB(index);
objectData = ColorSpaceConvertor.convertIndexToRGBByte(index, w, h, imageData.getCompCount(), imageData.getDepth(), objectData, false, false);
} else if (decodeColorData.getID() == ColorSpaces.CalRGB) {
} else if (decodeColorData.getID() == ColorSpaces.DeviceRGB) {
if (d == 8) { //baseline_screens/adobe/PP_download.pdf is actually 4 bit
check4BitData(objectData);
}
if (maskData != null && maskD == 1) {
for (int ii = 0; ii < maskData.length; ii++) {
maskData[ii] = (byte) (maskData[ii] ^ 255);
}
}
} else if (decodeColorData.getID() == ColorSpaces.DeviceGray && imageData.isJPX()) {
if (maskData != null && maskD == 1) {
for (int ii = 0; ii < maskData.length; ii++) {
maskData[ii] = (byte) (maskData[ii] ^ 255);
}
}
} else if (!imageData.isDCT() && !imageData.isJPX() && d != 1) { //we do not want to convert 1 bit
//convert the data to rgb (last parameter is used in CalRGB so left in to make method same in all)
objectData = decodeColorData.dataToRGBByteArray(objectData, w, h);
//System.out.println(maskColorSpace.getParameterConstant(PdfDictionary.ColorSpace)+" "+newSMask.getObjectRefAsString());
}
return objectData;
}
static void check4BitData(final byte[] objectData) {
final int size = objectData.length;
boolean is4Bit = true;
for (final byte b : objectData) {
if (b < 0 || b > 15) {
is4Bit = false;
break;
}
}
if (is4Bit) {
for (int ii = 0; ii < size; ii++) {
objectData[ii] = (byte) (objectData[ii] << 4);
}
}
}
private static byte[] upScaleMaskToImage(final int w, final int h, final int maskW, final int maskH, final byte[] objectData, final byte[] maskData) {
int rgbPtr = 0, aPtr;
int i = 0;
final float ratioW = maskW / (float) w;
final float ratioH = maskH / (float) h;
final byte[] combinedData = new byte[w * h * 4];
final int rawDataSize = objectData.length;
try {
for (int iY = 0; iY < h; iY++) {
for (int iX = 0; iX < w; iX++) {
//rgb
for (int comp = 0; comp < 3; comp++) {
if (rgbPtr < rawDataSize) {
combinedData[i + comp] = objectData[rgbPtr];
}
rgbPtr++;
}
aPtr = (((int) (iX * ratioW))) + (((int) (iY * ratioH)) * w);
combinedData[i + 3] = maskData[aPtr];
i += 4;
}
}
} catch (final Exception e) {
e.printStackTrace();
}
return combinedData;
}
private static byte[] upScaleImageToMask(final int w, final int h, final int maskW, final int maskH, final byte[] objectData, final byte[] maskData) {
int rgbPtr, aPtr = 0;
int i = 0;
final float ratioW = w / (float) maskW;
final float ratioH = h / (float) maskH;
final byte[] combinedData = new byte[maskW * maskH * 4];
final int rawDataSize = objectData.length;
final int maskSize = maskData.length;
try {
for (int mY = 0; mY < maskH; mY++) {
for (int mX = 0; mX < maskW; mX++) {
rgbPtr = (((int) (mX * ratioW)) * 3) + (((int) (mY * ratioH)) * w * 3);
// System.err.println(mX+"/"+maskW+" "+mY+"/"+maskH+" "+ratioW+" mask="+((int)(mX*ratioW))+" "+((int)(mY*ratioH)));
//rgb
for (int comp = 0; comp < 3; comp++) {
if (rgbPtr < rawDataSize) {
combinedData[i + comp] = objectData[rgbPtr];
}
rgbPtr++;
}
if (aPtr < maskSize) {
combinedData[i + 3] = maskData[aPtr];
aPtr++;
}
i += 4;
}
}
} catch (final Exception e) {
e.printStackTrace();
}
return combinedData;
}
public static byte[] getSMaskData(byte[] maskData, final ImageData smaskData, final PdfObject newSMask, final GenericColorSpace maskColorData) {
smaskData.getFilter(newSMask);
if (smaskData.isDCT()) {
maskData = JPEGDecoder.getBytesFromJPEG(maskData, maskColorData, newSMask);
newSMask.setMixedArray(PdfDictionary.Filter, null);
newSMask.setDecodedStream(maskData);
}
return maskData;
}
private static byte[] applyMaskArray(final int w, final int h, final byte[] objectData, final int[] maskArray) {
final int pixels = w * h * 4;
int rgbPtr = 0;
final byte[] combinedData = new byte[w * h * 4];
final int rawDataSize = objectData.length;
float diff = 0;
if (maskArray != null) {
diff = maskArray[1] - maskArray[0];
if (diff > 1f) {
diff /= 255f;
}
}
try {
for (int i = 0; i < pixels; i += 4) {
//rgb
for (int comp = 0; comp < 3; comp++) {
if (rgbPtr < rawDataSize) {
if (diff > 0) {
combinedData[i + comp] = (byte) (objectData[rgbPtr] * diff);
} else {
combinedData[i + comp] = objectData[rgbPtr];
}
}
rgbPtr++;
}
//opacity
combinedData[i + 3] = (byte) 255;
}
} catch (final Exception e) {
e.printStackTrace();
}
return combinedData;
}
private static byte[] buildUnscaledByteArray(final int w, final int h, final byte[] objectData, final byte[] maskData) {
final int pixels = w * h * 4;
int rgbPtr = 0, aPtr = 0;
final byte[] combinedData = new byte[w * h * 4];
final int rawDataSize = objectData.length;
final int maskSize = maskData.length;
try {
for (int i = 0; i < pixels; i += 4) {
//rgb
for (int comp = 0; comp < 3; comp++) {
if (rgbPtr < rawDataSize) {
combinedData[i + comp] = objectData[rgbPtr];
}
rgbPtr++;
}
if (aPtr < maskSize) {
//System.out.println(maskData[aPtr]);
combinedData[i + 3] = maskData[aPtr];
aPtr++;
}
}
} catch (final Exception e) {
e.printStackTrace();
}
return combinedData;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy