/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
///////// Private variables
private boolean debug = false;
/**
* The following variable contains a pointer to the IJG library
* structure for this reader. It is assigned in the constructor
* and then is passed in to every native call. It is set to 0
* by dispose to avoid disposing twice.
*/
/** The output stream we write to */
/** The Raster we will write from */
/** An intermediate Raster holding compressor-friendly data */
/**
* Set to true if we are writing an image with an
* indexed ColorModel
*/
private boolean indexed = false;
private boolean isAlphaPremultiplied = false;
/**
* If there are thumbnails to be written, this is the list.
*/
/**
* If metadata should include an icc profile, store it here.
*/
/** Used when calling listeners */
// Parameters for writing metadata
private boolean writeDefaultJFIF = false;
private boolean writeAdobe = false;
private boolean sequencePrepared = false;
/** The referent to be registered with the Disposer. */
/** The DisposerRecord that handles the actual disposal of this writer. */
///////// End of Private variables
///////// Protected variables
///////// End of Protected variables
///////// static initializer
static {
initWriterIDs(JPEGQTable.class,
JPEGHuffmanTable.class);
}
//////// Public API
super(originator);
}
try {
// Set the native destination
} finally {
}
}
return new JPEGImageWriteParam(null);
}
try {
return new JPEGMetadata(param, this);
} finally {
}
}
public IIOMetadata
try {
} finally {
}
}
// There isn't much we can do. If it's one of ours, then
// return it. Otherwise just return null. We use it only
// for tables, so we can't get a default and modify it,
// as this will usually not be what is intended.
if (inData instanceof JPEGMetadata) {
return inData;
}
}
return null;
}
public IIOMetadata
try {
} finally {
}
}
private IIOMetadata
// If it's one of ours, just return it
if (inData instanceof JPEGMetadata) {
return inData;
} else {
// Can't convert stream metadata to image metadata
// XXX Maybe this should put out a warning?
return null;
}
}
// If it's not one of ours, create a default and set it from
// the standard tree from the input, if it exists.
if (inData.isStandardMetadataFormatSupported()) {
this);
try {
} catch (IIOInvalidTreeException e) {
// Other plug-in generates bogus standard tree
// XXX Maybe this should put out a warning?
return null;
}
return jpegData;
}
}
return null;
}
}
return 0;
}
}
return null;
}
// If the image type and metadata are JFIF compatible, return true
return false;
}
if (imageMetadata != null) {
if (imageMetadata instanceof JPEGMetadata) {
} else {
param);
}
// metadata must have a jfif node
(JFIFMarkerSegment.class, true) == null){
return false;
}
}
return true;
}
public boolean canWriteRasters() {
return true;
}
try {
} finally {
}
}
throw new IllegalStateException("Output has not been set!");
}
throw new IllegalArgumentException("image is null!");
}
// if streamMetadata is not null, issue a warning
if (streamMetadata != null) {
}
// Obtain the raster and image, if there is one
if (rasterOnly) {
} else {
if (rimage instanceof BufferedImage) {
// Use the Raster directly.
{
// Get the unique tile.
rimage.getMinTileY());
// Ensure the Raster has dimensions of the image,
// as the tile dimensions might differ.
{
null);
}
} else {
// Image is tiled so get a contiguous raster by copying.
}
}
// Now determine if we are using a band subset
// By default, we are using all source bands
indexed = false;
isAlphaPremultiplied = false;
if (!rasterOnly) {
if (cm instanceof IndexColorModel) {
indexed = true;
}
if (cm.isAlphaPremultiplied()) {
isAlphaPremultiplied = true;
}
}
}
int numBandsUsed = numSrcBands;
// Consult the param to determine if we're writing a subset
if (indexed) {
} else {
if (numBandsUsed > numSrcBands) {
throw new IIOException
("ImageWriteParam specifies too many source bands");
}
}
}
}
if (!indexed) {
// If this is a subset, we must adjust bandSizes
if (usingBandSubset) {
int [] temp = new int [numBandsUsed];
for (int i = 0; i < numBandsUsed; i++) {
}
}
} else {
bandSizes = new int [numSrcBands];
for (int i = 0; i < numSrcBands; i++) {
}
}
// 4450894 part 1: The IJG libraries are compiled so they only
// handle <= 8-bit samples. We now check the band sizes and throw
// an exception for images, such as USHORT_GRAY, with > 8 bits
// per sample.
if (bandSizes[i] > 8) {
throw new IIOException("Sample size must be <= 8");
}
// 4450894 part 2: We expand IndexColorModel images to full 24-
// or 32-bit in grabPixels() for each scanline. For indexed
// images such as BYTE_BINARY, we need to ensure that we update
// bandSizes to account for the scaling from 1-bit band sizes
// to 8-bit.
if (indexed) {
bandSizes[i] = 8;
}
}
if (debug) {
}
}
// Destination type, if there is one
// Ignore dest type if we are writing a complete image
}
}
// Examine the param
int periodX = 1;
int periodY = 1;
int gridX = 0;
int gridY = 0;
boolean optimizeHuffman = false;
if (sourceRegion != null) {
}
}
}
switch(param.getCompressionMode()) {
case ImageWriteParam.MODE_DISABLED:
throw new IIOException("JPEG compression cannot be disabled");
case ImageWriteParam.MODE_EXPLICIT:
(quality, true);
(quality, true);
break;
case ImageWriteParam.MODE_DEFAULT:
break;
// We'll handle the metadata case later
}
if (param instanceof JPEGImageWriteParam) {
}
}
// Now examine the metadata
if (mdata instanceof JPEGMetadata) {
if (debug) {
("We have metadata, and it's JPEG metadata");
}
} else {
if (!rasterOnly) {
}
type,
param);
} else {
}
}
}
// First set a default state
ignoreJFIF = false; // If it's there, use it
ignoreAdobe = false; // If it's there, use it
writeDefaultJFIF = false;
writeAdobe = false;
// By default we'll do no conversion:
(JFIFMarkerSegment.class, true);
(AdobeMarkerSegment.class, true);
(SOFMarkerSegment.class, true);
}
convertTosRGB = false; // PhotoYCC does this
throw new IIOException
("Number of source bands != number of destination bands");
}
// Check the metadata against the destination type
// Do we want to write an ICC profile?
}
}
} else { // no metadata, but there is a dest type
// If we can add a JFIF or an Adobe marker segment, do so
writeDefaultJFIF = true;
// Do we want to write an ICC profile?
}
} else {
writeAdobe = true;
}
}
// re-create the metadata
}
} else { // no destination type
if (fullImage) { // no dest, no metadata, full image
// Use default metadata matching the image and param
param, this);
(JFIFMarkerSegment.class, true) != null) {
}
}
}
// else no dest, no metadata, not an image,
// so no special headers, no color conversion
} else { // no dest type, but there is metadata
if (fullImage) { // no dest, metadata, image
// Check that the metadata and the image match
new ImageTypeSpecifier(rimage);
case ColorSpace.TYPE_GRAY:
if (!alpha) {
} else {
ignoreJFIF = true;
}
// out colorspace remains unknown
}
}
break;
case ColorSpace.TYPE_RGB:
if (!alpha) {
|| ((cs instanceof ICC_ColorSpace)
}
case JPEG.ADOBE_UNKNOWN:
break;
break;
default:
break;
}
} else {
// consult the ids
// if they don't resolve it,
// consult the sampling factors
} else {
boolean subsampled =
if (subsampled) {
} else {
}
}
}
} else { // RGBA
ignoreJFIF = true;
}
!= JPEG.ADOBE_UNKNOWN) {
}
} else {
// consult the ids
// if they don't resolve it,
// consult the sampling factors
} else {
boolean subsampled =
outCsType = subsampled ?
}
}
}
break;
case ColorSpace.TYPE_3CLR:
if (!alpha) {
convertTosRGB = true;
new ColorConvertOp(cs,
null);
}
} else {
}
} else { // PhotoYCCA
ignoreJFIF = true;
!= JPEG.ADOBE_UNKNOWN) {
}
}
}
}
}
}
} // else no dest, metadata, not an image. Defaults ok
}
}
boolean metadataProgressive = false;
(SOFMarkerSegment.class, true);
}
metadataProgressive = true;
} else {
numScans = 0;
}
}
(JFIFMarkerSegment.class, true);
}
}
forceJFIF = false;
// determine if thumbnails can be written
// If we are going to add a default JFIF marker segment,
// then thumbnails can be written
if (!writeDefaultJFIF) {
// If there is no metadata, then we can't write thumbnails
thumbnails = null;
if (numThumbs != 0) {
}
} else {
// There is metadata
// If we are writing a raster or subbands,
// then the user must specify JFIF on the metadata
if (fullImage == false) {
if (numThumbs != 0) {
}
}
} else { // It is a full image, and there is metadata
// Can it have JFIF?
if (numThumbs != 0) {
forceJFIF = true;
}
} else { // Nope, not JFIF-compatible
thumbnails = null;
if (numThumbs != 0) {
}
}
}
}
}
}
// Set up a boolean to indicate whether we need to call back to
// write metadata
boolean haveMetadata =
// Now that we have dealt with metadata, finalize our tables set up
// Are we going to write tables? By default, yes.
boolean writeDQT = true;
boolean writeDHT = true;
// But if the metadata has no tables, no.
int restartInterval = 0;
(DQTMarkerSegment.class, true);
(DHTMarkerSegment.class, true);
(DRIMarkerSegment.class, true);
}
writeDQT = false;
}
writeDHT = false; // Ignored if optimizeHuffman is true
}
}
// Whether we write tables or not, we need to figure out which ones
// to use
} else if (streamQTables != null) {
} else {
}
}
// If we are optimizing, we don't want any tables.
if (optimizeHuffman == false) {
// If they were for progressive scans, we can't use them.
} else if (streamDCHuffmanTables != null) {
} else {
}
}
// By default, ids are 1 - N, no subsampling
int [] componentIds = new int[numBandsUsed];
int [] HsamplingFactors = new int[numBandsUsed];
int [] VsamplingFactors = new int[numBandsUsed];
int [] QtableSelectors = new int[numBandsUsed];
for (int i = 0; i < numBandsUsed; i++) {
HsamplingFactors[i] = 1;
VsamplingFactors[i] = 1;
QtableSelectors[i] = 0;
}
// Now override them with the contents of sof, if there is one,
for (int i = 0; i < numBandsUsed; i++) {
if (forceJFIF == false) { // else use JFIF-compatible default
}
}
}
sourceXOffset += gridX;
sourceWidth -= gridX;
sourceYOffset += gridY;
sourceHeight -= gridY;
// Create an appropriate 1-line databuffer for writing
// Create a raster from that
sourceWidth, 1,
null);
// Call the writer, who will call back for every scanline
boolean aborted = false;
if (debug) {
}
// Note that getData disables acceleration on buffer, but it is
// just a 1-line intermediate data transfer buffer that does not
// affect the acceleration of the source image.
try {
if (aborted) {
} else {
}
} finally {
}
currentImage++; // After a successful write
}
throws IOException {
try {
} finally {
}
}
throws IOException {
throw new IllegalStateException("Output has not been set!");
}
/*
* from jpeg_metadata.html:
* If no stream metadata is supplied to
* <code>ImageWriter.prepareWriteSequence</code>, then no
* tables-only image is written. If stream metadata containing
* no tables is supplied to
* <code>ImageWriter.prepareWriteSequence</code>, then a tables-only
* image containing default visually lossless tables is written.
*/
if (streamMetadata != null) {
if (streamMetadata instanceof JPEGMetadata) {
// write a complete tables-only image at the beginning of
// the stream.
throw new IllegalArgumentException
("Invalid stream metadata object.");
}
// Check that we are
// at the beginning of the stream, or can go there, and haven't
// written out the metadata already.
if (currentImage != 0) {
throw new IIOException
("JPEG Stream metadata must precede all images");
}
if (sequencePrepared == true) {
throw new IIOException("Stream metadata already written!");
}
// Set the tables
// If the metadata has no tables, use default tables.
if (debug) {
+ "streamQTables.length is "
+ streamQTables.length);
}
if (streamQTables == null) {
}
collectHTablesFromMetadata(jmeta, true);
if (streamDCHuffmanTables == null) {
}
collectHTablesFromMetadata(jmeta, false);
if (streamACHuffmanTables == null) {
}
// Now write them out
} else {
throw new IIOException("Stream metadata must be JPEG metadata");
}
}
sequencePrepared = true;
}
throws IOException {
try {
if (sequencePrepared == false) {
throw new IllegalStateException("sequencePrepared not called!");
}
// In the case of JPEG this does nothing different from write
} finally {
}
}
try {
if (sequencePrepared == false) {
throw new IllegalStateException("sequencePrepared not called!");
}
sequencePrepared = false;
} finally {
}
}
public synchronized void abort() {
try {
/**
* NB: we do not check the call back lock here, we allow to abort
* the reader any time.
*/
super.abort();
} finally {
}
}
private void resetInternalState() {
// reset C structures
// reset local Java structures
convertTosRGB = false;
currentImage = 0;
numScans = 0;
}
public void reset() {
try {
super.reset();
} finally {
}
}
public void dispose() {
try {
if (structPointer != 0) {
structPointer = 0;
}
} finally {
}
}
////////// End of public API
///////// Package-access API
/**
* Called by the native code or other classes to signal a warning.
* The code is used to lookup a localized message to be used when
* sending warnings to listeners.
*/
try {
throw new InternalError("Invalid warning index");
}
"com.sun.imageio.plugins.jpeg.JPEGImageWriterResources",
} finally {
}
}
/**
* The library has it's own error facility that emits warning messages.
* This routine is called by the native code when it has already
* formatted a string for output.
* XXX For truly complete localization of all warning messages,
* the sun_jpeg_output_message routine in the native code should
* send only the codes and parameters to a method here in Java,
* which will then format and send the warnings, using localized
* strings. This method will have to deal with all the parameters
* and formats (%u with possibly large numbers, %02d, %02x, etc.)
* that actually occur in the JPEG library. For now, this prevents
* library warnings from being printed to stderr.
*/
try {
} finally {
}
}
try {
} finally {
}
}
// Provide access to protected superclass method
try {
} finally {
}
}
// Provide access to protected superclass method
void thumbnailComplete() {
try {
} finally {
}
}
///////// End of Package-access API
///////// Private methods
///////// Metadata handling
throws IIOException {
// Does the metadata frame header, if any, match numBandsUsed?
throw new IIOException
("Metadata components != number of destination bands");
}
}
}
boolean input) {
ignoreJFIF = true; // type overrides metadata
}
}
}
boolean input) {
ignoreAdobe = true;
} else {
}
}
}
}
/**
* Collect all the scan info from the given metadata, and
* organize it into the scan info array required by the
* IJG libray. It is much simpler to parse out this
* data in Java and then just copy the data in C.
*/
int SCAN_SIZE = 9;
int MAX_COMPS_PER_SCAN = 4;
if (seg instanceof SOSMarkerSegment) {
}
}
numScans = 0;
int index = 0;
for (int i = 0; i < numScans; i++) {
for (int j = 0; j < MAX_COMPS_PER_SCAN; j++) {
break; // out of for over sof comps
}
}
} else {
}
}
}
}
return retval;
}
/**
* Finds all DQT marker segments and returns all the q
* tables as a single array of JPEGQTables.
*/
(JPEGMetadata metadata) {
if (seg instanceof DQTMarkerSegment) {
}
}
retval[i] =
}
}
return retval;
}
/**
* Finds all DHT marker segments and returns all the q
* tables as a single array of JPEGQTables. The metadata
* must not be for a progressive image, or an exception
* will be thrown when two Huffman tables with the same
* table id are encountered.
*/
if (seg instanceof DHTMarkerSegment) {
}
}
}
}
throw new IIOException("Metadata has duplicate Htables!");
}
}
}
}
}
return retval;
}
/////////// End of metadata handling
////////////// ColorSpace conversion
}
}
case ColorSpace.TYPE_GRAY:
break;
case ColorSpace.TYPE_RGB:
if (alpha) {
} else {
}
break;
case ColorSpace.TYPE_YCbCr:
if (alpha) {
} else {
}
break;
case ColorSpace.TYPE_3CLR:
if (alpha) {
} else {
}
}
case ColorSpace.TYPE_CMYK:
break;
}
}
return retval;
}
case ColorSpace.TYPE_GRAY:
break;
case ColorSpace.TYPE_RGB:
if (alpha) {
} else {
}
break;
case ColorSpace.TYPE_YCbCr:
if (alpha) {
} else {
}
break;
case ColorSpace.TYPE_3CLR:
if (alpha) {
} else {
}
}
case ColorSpace.TYPE_CMYK:
break;
}
return retval;
}
}
}
case ColorSpace.TYPE_GRAY:
break;
case ColorSpace.TYPE_RGB:
if (alpha) {
} else {
}
break;
case ColorSpace.TYPE_YCbCr:
if (alpha) {
} else {
}
break;
case ColorSpace.TYPE_3CLR:
if (alpha) {
} else {
}
}
case ColorSpace.TYPE_CMYK:
break;
}
}
return retval;
}
return true;
}
return false;
}
////////////// End of ColorSpace conversion
////////////// Native methods and callbacks
/** Sets up static native structures. */
/** Sets up per-writer native structure and returns a pointer to it. */
private native long initJPEGImageWriter();
/** Sets up native structures for output stream */
/**
* Returns <code>true</code> if the write was aborted.
*/
byte [] data,
int numBands,
int [] bandSizes,
int srcWidth,
int destWidth, int destHeight,
JPEGQTable [] qtables,
boolean writeDQT,
boolean writeDHT,
boolean optimizeHuffman,
boolean progressive,
int numScans,
int [] scans,
int [] componentIds,
int [] HsamplingFactors,
int [] VsamplingFactors,
int [] QtableSelectors,
boolean haveMetadata,
int restartInterval);
/**
* Writes the metadata out when called by the native code,
* which will have already written the header to the stream
* and established the library state. This is simpler than
* breaking the write call in two.
*/
if (writeDefaultJFIF) {
this);
}
if (writeAdobe) {
}
} else {
this);
}
}
/**
* Write out a tables-only image to the stream.
*/
JPEGQTable [] qtables,
/**
* Put the scanline y of the source ROI view Raster into the
* 1-line Raster for writing. This handles ROI and band
* rearrangements, and expands indexed images. Subsampling is
* done in the native code.
* This is called by the native code.
*/
private void grabPixels(int y) {
if (indexed) {
sourceWidth, 1,
0, 0,
new int [] {0});
// If the image has BITMASK transparency, we need to make sure
// it gets converted to 32-bit ARGB, because the JPEG encoder
// relies upon the full 8-bit alpha channel.
boolean forceARGB =
} else {
sourceWidth, 1,
0, 0,
srcBands);
}
if (convertTosRGB) {
if (debug) {
}
// The first time through, converted is null, so
// a new raster is allocated. It is then reused
// on subsequent lines.
}
if (isAlphaPremultiplied) {
data);
data);
0, 0,
srcBands);
}
if ((y > 7) && (y%8 == 0)) { // Every 8 scanlines
try {
} finally {
}
}
}
/** Aborts the current write in the native code */
/** Resets native structures */
/** Releases native structures */
private long pData;
}
public synchronized void dispose() {
if (pData != 0) {
pData = 0;
}
}
}
/**
* This method is called from native code in order to write encoder
* output to the destination.
*
* We block any attempt to change the writer state during this
* method, in order to prevent a corruption of the native encoder
* state.
*/
throws IOException
{
try {
} finally {
}
}
private synchronized void setThreadLock() {
if (theThread != currThread) {
// it looks like that this reader instance is used
// by multiple threads.
throw new IllegalStateException("Attempt to use instance of " +
this + " locked on thread " +
theThread + " from thread " +
} else {
theLockCount ++;
}
} else {
theLockCount = 1;
}
}
private synchronized void clearThreadLock() {
throw new IllegalStateException("Attempt to clear thread lock form wrong thread. " +
"Locked thread: " + theThread +
"; current thread: " + currThread);
}
theLockCount --;
if (theLockCount == 0) {
}
}
private static class CallBackLock {
CallBackLock() {
}
void check() {
throw new IllegalStateException("Access to the writer is not allowed");
}
}
private void lock() {
}
private void unlock() {
}
private static enum State {
}
}
}