Added Base64Wrapper to support different Base64 Implementaions (like android). Also added some functionality for android compatibility
This commit is contained in:
parent
d7ccf6883c
commit
7cf965b1f5
9 changed files with 480 additions and 309 deletions
257
src/JPEG.java
257
src/JPEG.java
|
@ -1,307 +1,377 @@
|
|||
|
||||
import java.io.File;
|
||||
import java.util.Base64;
|
||||
|
||||
public class JPEG extends Const
|
||||
{
|
||||
|
||||
|
||||
/** MEMBERS */
|
||||
|
||||
|
||||
/**
|
||||
* location of the source file
|
||||
*/
|
||||
private String filename;
|
||||
|
||||
private String m_Filename;
|
||||
|
||||
/**
|
||||
* byte array containing the actual file
|
||||
*/
|
||||
private byte[] rawData;
|
||||
|
||||
private byte[] m_RawData;
|
||||
|
||||
/**
|
||||
* byte array containing EXIF block information without header
|
||||
*/
|
||||
private byte[] exif;
|
||||
|
||||
private byte[] m_Exif;
|
||||
|
||||
/**
|
||||
* byte array containing StandardXMP block information without header
|
||||
*/
|
||||
private byte[] xmpSta;
|
||||
|
||||
private byte[] m_StandardXMP;
|
||||
|
||||
/**
|
||||
* byte array containing ExtendedXMP block information without header
|
||||
*/
|
||||
private byte[] xmpExt;
|
||||
|
||||
private byte[] m_ExtendedXMP;
|
||||
|
||||
/**
|
||||
* byte array conatining the trailing headless data block conatining the blurred jpeg image
|
||||
* byte array containing the trailing headless data block containing the blurred jpeg image
|
||||
*/
|
||||
private byte[] tail;
|
||||
|
||||
private byte[] m_ImageTail;
|
||||
|
||||
private Base64Wrapper base64;
|
||||
|
||||
/** METHODS */
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param rawData
|
||||
*
|
||||
* @param rawData raw byte array
|
||||
*/
|
||||
public JPEG(byte[] rawData)
|
||||
public JPEG(byte[] rawData, Base64Wrapper wrapper)
|
||||
{
|
||||
this.init(rawData, null);
|
||||
this.base64 = wrapper;
|
||||
this.init(rawData, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor, that reads a byte array from file
|
||||
* @param filename
|
||||
*
|
||||
* @param filename raw byte array
|
||||
*/
|
||||
public JPEG(String filename)
|
||||
public JPEG(String filename, Base64Wrapper wrapper)
|
||||
{
|
||||
this.base64 = wrapper;
|
||||
this.init(IO.read(new File(filename)), filename);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disassemble the rawData byte array into sub arrays
|
||||
*/
|
||||
public void disassemble()
|
||||
{
|
||||
exif = this.getEXIFBlock();
|
||||
xmpSta = this.getStandardXMPBlockContent();
|
||||
xmpExt = this.getExtendedXMPContent();
|
||||
tail = this.getImageTail();
|
||||
{
|
||||
m_Exif = this.getEXIFBlock();
|
||||
m_StandardXMP = this.getStandardXMPBlockContent();
|
||||
m_ExtendedXMP = this.getExtendedXMPContent();
|
||||
m_ImageTail = this.getImageTail();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* extract the depthmap from the jpeg and store it in the same location as the jpeg itself but with the suffix _d.png
|
||||
* extract the depth map from the jpeg and store it in the same location as the jpeg itself but with the suffix _d.png
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean exportDepthMap()
|
||||
{
|
||||
String out = filename;
|
||||
if(out.endsWith(".jpg") || out.endsWith(".JPG")) out = out.substring(0, out.length()-4);
|
||||
String out = m_Filename;
|
||||
if (out.endsWith(".jpg") || out.endsWith(".JPG")) out = out.substring(0, out.length()-4);
|
||||
return this.exportDepthMap(out+"_d.png");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* extract the depthmap from the jpeg and store it under the location specified in "file"
|
||||
*
|
||||
* @param file String of the output location
|
||||
* @return success
|
||||
*/
|
||||
public boolean exportDepthMap(String file)
|
||||
{
|
||||
byte[] b64 = ArrayUtils.unsign(extractDepthMap());
|
||||
byte[] depth = Base64.getDecoder().decode(b64);
|
||||
if(depth != null)
|
||||
byte[] depth = base64.decode(b64);
|
||||
if (depth != null)
|
||||
{
|
||||
IO.write(depth, file);
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract and save the unblurred source image. The image will be saved in the same location as the jpeg itself, but with the suffix "_s.jpg"
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
public boolean exportSourceImage()
|
||||
{
|
||||
String out = filename;
|
||||
if(out.endsWith(".jpg") || out.endsWith(".JPG")) out = out.substring(0, out.length()-4);
|
||||
String out = m_Filename;
|
||||
if (out.endsWith(".jpg") || out.endsWith(".JPG")) out = out.substring(0, out.length()-4);
|
||||
return this.exportSourceImage(out+"_s.jpg");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract the unblurred source image and save it under the location specified in "file"
|
||||
*
|
||||
* @param file String of the location
|
||||
* @return success
|
||||
*/
|
||||
public boolean exportSourceImage(String file)
|
||||
{
|
||||
byte[] b64 = ArrayUtils.unsign(extractSourceImage());
|
||||
byte[] src = Base64.getDecoder().decode(b64);
|
||||
if(src != null)
|
||||
byte[] src = base64.decode(b64);
|
||||
if (src != null)
|
||||
{
|
||||
IO.write(src, file);
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and return the depthmap information
|
||||
*
|
||||
* @return depthmap as byte array
|
||||
*/
|
||||
public byte[] extractDepthMap()
|
||||
{
|
||||
return JPEGUtils.extractDepthMap(rawData);
|
||||
return JPEGUtils.extractDepthMap(m_RawData);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract and return the unblurred source image
|
||||
*
|
||||
* @return source image as byte array
|
||||
*/
|
||||
public byte[] extractSourceImage()
|
||||
{
|
||||
return JPEGUtils.extractSourceImage(rawData);
|
||||
return JPEGUtils.extractSourceImage(m_RawData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all indizes of APP1 markers in the jpegs byte array
|
||||
*
|
||||
* @return integer array of all positions of the APP1 marker in the rawData array
|
||||
*/
|
||||
public int[] getBoundaries()
|
||||
{
|
||||
return JPEGUtils.getBoundaries(rawData);
|
||||
return JPEGUtils.getBoundaries(m_RawData);
|
||||
}
|
||||
|
||||
/**
|
||||
* return the exif block of the jpeg as a byte array
|
||||
*
|
||||
* @return exifblock (member)
|
||||
*/
|
||||
public byte[] getExif()
|
||||
{
|
||||
return exif;
|
||||
return m_Exif;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exif block from the rawData array instead from the member
|
||||
*
|
||||
* @return the exif block from rawData
|
||||
*/
|
||||
public byte[] getEXIFBlock()
|
||||
{
|
||||
return JPEGUtils.getEXIFBlock(rawData);
|
||||
return JPEGUtils.getEXIFBlock(m_RawData);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract and return the content of all the ExtendedXMP blocks concatenated.
|
||||
*
|
||||
* @return content of the ExtendedXMP blocks
|
||||
*/
|
||||
public byte[] getExtendedXMPContent()
|
||||
{
|
||||
return JPEGUtils.getExtendedXMPBlockContent(rawData);
|
||||
return JPEGUtils.getExtendedXMPBlockContent(m_RawData);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* return the filename and path
|
||||
* @return
|
||||
*
|
||||
* @return member filename
|
||||
*/
|
||||
public String getFilename()
|
||||
{
|
||||
return filename;
|
||||
return m_Filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the headless block of data in the image (this contains the JPEG you see when opening the file)
|
||||
*
|
||||
* @return headless JPEG tail
|
||||
*/
|
||||
public byte[] getImageTail()
|
||||
{
|
||||
return JPEGUtils.getImageTail(rawData);
|
||||
return JPEGUtils.getImageTail(m_RawData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Metadata of the image (content of all APP1 marked blocks)
|
||||
*
|
||||
* @return metadata
|
||||
*/
|
||||
public byte[] getMetadata()
|
||||
{
|
||||
return JPEGUtils.getMetadata(rawData);
|
||||
return JPEGUtils.getMetadata(m_RawData);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the byte array that IO.read(image) returned
|
||||
*
|
||||
* @return byte array of the image file
|
||||
*/
|
||||
public byte[] getRawData()
|
||||
{
|
||||
return this.rawData;
|
||||
return this.m_RawData;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extracts and returns the content of the StandardXMP block
|
||||
*
|
||||
* @return content of the StandardXMP block
|
||||
*/
|
||||
public byte[] getStandardXMPBlockContent()
|
||||
{
|
||||
return JPEGUtils.getStandardXMPBlockContent(rawData);
|
||||
return JPEGUtils.getStandardXMPBlockContent(m_RawData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Image tail (member)
|
||||
*
|
||||
* @return member image tail
|
||||
*/
|
||||
public byte[] getTail()
|
||||
{
|
||||
return tail;
|
||||
return m_ImageTail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts and returns the content of all XMP blocks concatenated (StandardXMP + ExtendedXMPs)
|
||||
*
|
||||
* @return content of the XMP blocks
|
||||
*/
|
||||
public byte[] getXMPBlocksContent()
|
||||
{
|
||||
return JPEGUtils.getXMPBlocksContent(rawData);
|
||||
return JPEGUtils.getXMPBlocksContent(m_RawData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the member containing the ExtendedXMP information
|
||||
*
|
||||
* @return XmpExt (member)
|
||||
*/
|
||||
public byte[] getXmpExt()
|
||||
{
|
||||
return xmpExt;
|
||||
return m_ExtendedXMP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the member containing the StandardXMP information
|
||||
*
|
||||
* @return xmpSta (member)
|
||||
*/
|
||||
public byte[] getXmpSta()
|
||||
{
|
||||
return xmpSta;
|
||||
return m_StandardXMP;
|
||||
}
|
||||
|
||||
/**
|
||||
* initializes the JPEG object.
|
||||
* set the array containing the image as member rawData, set filename
|
||||
* disassemble the Image and set the other members with content.
|
||||
* @param raw byte array of the image
|
||||
*
|
||||
* @param raw byte array of the image
|
||||
* @param filename location
|
||||
*/
|
||||
private void init(byte[] raw, String filename)
|
||||
{
|
||||
this.rawData = raw;
|
||||
this.filename = filename;
|
||||
this.disassemble();
|
||||
if(ArrayUtils.arrayIsPartOfOtherArrayOnOffset(raw, Const.markJPG,0))
|
||||
{
|
||||
this.m_RawData = raw;
|
||||
this.m_Filename = filename;
|
||||
this.disassemble();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject a new depthmap into the jpeg ("replace" the old GDepth:Data value with the Base64 encoded png file)
|
||||
*
|
||||
* @param filename location of the new depthmap png
|
||||
* @return success
|
||||
*/
|
||||
public boolean injectDepthMap(String filename)
|
||||
{
|
||||
byte[] depth = Base64.getEncoder().encode(IO.read(new File(filename)));
|
||||
xmpExt = JPEGUtils.replace(xmpExt, keyGDepthData, depth);
|
||||
if(xmpExt != null) return true;
|
||||
else return false;
|
||||
byte[] depth = base64.encode(IO.read(new File(filename)));
|
||||
byte[] newExtendedXMP = JPEGUtils.replace(m_ExtendedXMP, keyGDepthData, depth);
|
||||
if (newExtendedXMP != null)
|
||||
{
|
||||
m_ExtendedXMP = newExtendedXMP;
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
public boolean injectDepthMap(byte[] data)
|
||||
{
|
||||
byte[] depth = base64.encode(data);
|
||||
byte[] newExtendedXMP = JPEGUtils.replace(m_ExtendedXMP, keyGDepthData, depth);
|
||||
if (newExtendedXMP != null)
|
||||
{
|
||||
m_ExtendedXMP = newExtendedXMP;
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject a new unblurred source image into the jpeg ("replace" the old GImage:Data value with the Base64 encoded jpg file)
|
||||
*
|
||||
* @param filename location of the new unblurred source image jpg
|
||||
* @return success
|
||||
*/
|
||||
public boolean injectSourceImage(String filename)
|
||||
{
|
||||
byte[] image = base64.encode(IO.read(new File(filename)));
|
||||
byte[] newExtendedXMP = JPEGUtils.replace(m_ExtendedXMP, keyGImageData, image);
|
||||
if (newExtendedXMP != null)
|
||||
{
|
||||
m_ExtendedXMP = newExtendedXMP;
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
public boolean injectSourceImage(byte[] data)
|
||||
{
|
||||
byte[] image = base64.encode(data);
|
||||
byte[] newExtendedXMP = JPEGUtils.replace(m_ExtendedXMP, keyGImageData, image);
|
||||
if (newExtendedXMP != null)
|
||||
{
|
||||
m_ExtendedXMP = newExtendedXMP;
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reassemble a functional jpeg byte array from block data.
|
||||
* Set rawData to the new array and also return it
|
||||
*
|
||||
* @return reassembled jpeg byte array
|
||||
*/
|
||||
public byte[] reassemble()
|
||||
{
|
||||
byte[] md5 = HexUtil.generateMD5(xmpExt);
|
||||
|
||||
byte[] md5 = HexUtil.generateMD5(m_ExtendedXMP);
|
||||
|
||||
byte[] out = markJPG;
|
||||
out = ArrayUtils.concatenate(out, JPEGUtils.decorateBlock(exif,EXIF));
|
||||
out = ArrayUtils.concatenate(out, JPEGUtils.decorateBlock(xmpSta, STANDARDXMP));
|
||||
out = ArrayUtils.concatenate(out, JPEGUtils.decorateBlock(xmpExt, EXTENDEDXMP));
|
||||
out = ArrayUtils.concatenate(out, tail);
|
||||
out = ArrayUtils.concatenate(out, JPEGUtils.decorateBlock(m_Exif, EXIF));
|
||||
out = ArrayUtils.concatenate(out, JPEGUtils.decorateBlock(m_StandardXMP, STANDARDXMP));
|
||||
out = ArrayUtils.concatenate(out, JPEGUtils.decorateBlock(m_ExtendedXMP, EXTENDEDXMP));
|
||||
out = ArrayUtils.concatenate(out, m_ImageTail);
|
||||
out = JPEGUtils.replace(out, keyHasExtendedXMP, md5);
|
||||
this.rawData = out;
|
||||
this.m_RawData = out;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -310,51 +380,56 @@ public class JPEG extends Const
|
|||
*/
|
||||
public void save()
|
||||
{
|
||||
this.save(filename);
|
||||
this.save(m_Filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the image to the file specified in "file"
|
||||
*
|
||||
* @param file
|
||||
*/
|
||||
public void save(String file)
|
||||
{
|
||||
IO.write(this.reassemble(), file);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the exif memberto a new byte array
|
||||
* Set the exif member to a new byte array
|
||||
*
|
||||
* @param exif new array
|
||||
*/
|
||||
public void setExif(byte[] exif)
|
||||
{
|
||||
this.exif=exif;
|
||||
this.m_Exif = exif;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the tail member array to a new byte array
|
||||
*
|
||||
* @param tail new array
|
||||
*/
|
||||
public void setTail(byte[] tail)
|
||||
{
|
||||
this.tail=tail;
|
||||
this.m_ImageTail = tail;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* set the xmpExt member array to a new byte array
|
||||
*
|
||||
* @param xmpExt new array
|
||||
*/
|
||||
public void setXmpExt(byte[] xmpExt)
|
||||
{
|
||||
this.xmpExt=xmpExt;
|
||||
this.m_ExtendedXMP = xmpExt;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the xmpSta member array to a new byte array
|
||||
*
|
||||
* @param xmpSta new array
|
||||
*/
|
||||
public void setXmpSta(byte[] xmpSta)
|
||||
{
|
||||
this.xmpSta=xmpSta;
|
||||
this.m_StandardXMP = xmpSta;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue