177 java.security.AccessController.doPrivileged(
178 new java.security.PrivilegedAction<Void>() {
179 public Void run() {
180 System.loadLibrary("javajpeg");
181 return null;
182 }
183 });
184 initWriterIDs(JPEGQTable.class,
185 JPEGHuffmanTable.class);
186 }
187
188 //////// Public API
189
190 public JPEGImageWriter(ImageWriterSpi originator) {
191 super(originator);
192 structPointer = initJPEGImageWriter();
193 disposerRecord = new JPEGWriterDisposerRecord(structPointer);
194 Disposer.addRecord(disposerReferent, disposerRecord);
195 }
196
197 public void setOutput(Object output) {
198 setThreadLock();
199 try {
200 cbLock.check();
201
202 super.setOutput(output); // validates output
203 resetInternalState();
204 ios = (ImageOutputStream) output; // so this will always work
205 // Set the native destination
206 setDest(structPointer);
207 } finally {
208 clearThreadLock();
209 }
210 }
211
212 public ImageWriteParam getDefaultWriteParam() {
213 return new JPEGImageWriteParam(null);
214 }
215
216 public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) {
217 setThreadLock();
218 try {
219 return new JPEGMetadata(param, this);
220 } finally {
221 clearThreadLock();
222 }
223 }
224
225 public IIOMetadata
226 getDefaultImageMetadata(ImageTypeSpecifier imageType,
227 ImageWriteParam param) {
228 setThreadLock();
229 try {
230 return new JPEGMetadata(imageType, param, this);
231 } finally {
232 clearThreadLock();
233 }
234 }
235
236 public IIOMetadata convertStreamMetadata(IIOMetadata inData,
237 ImageWriteParam param) {
238 // There isn't much we can do. If it's one of ours, then
239 // return it. Otherwise just return null. We use it only
240 // for tables, so we can't get a default and modify it,
241 // as this will usually not be what is intended.
242 if (inData instanceof JPEGMetadata) {
243 JPEGMetadata jpegData = (JPEGMetadata) inData;
244 if (jpegData.isStream) {
282 IIOMetadataFormatImpl.standardMetadataFormatName;
283 Node tree = inData.getAsTree(formatName);
284 if (tree != null) {
285 JPEGMetadata jpegData = new JPEGMetadata(imageType,
286 param,
287 this);
288 try {
289 jpegData.setFromTree(formatName, tree);
290 } catch (IIOInvalidTreeException e) {
291 // Other plug-in generates bogus standard tree
292 // XXX Maybe this should put out a warning?
293 return null;
294 }
295
296 return jpegData;
297 }
298 }
299 return null;
300 }
301
302 public int getNumThumbnailsSupported(ImageTypeSpecifier imageType,
303 ImageWriteParam param,
304 IIOMetadata streamMetadata,
305 IIOMetadata imageMetadata) {
306 // Check whether sufficient data is available.
307 if (imageType == null && imageMetadata == null) {
308 // The method has been invoked with insufficient data. Henceforth
309 // we return -1 as recommended by ImageWriter specification.
310 return -1;
311 }
312
313 // Check if the image type and metadata are JFIF compatible.
314 if (jfifOK(imageType, param, streamMetadata, imageMetadata)) {
315 return Integer.MAX_VALUE;
316 }
317 return 0;
318 }
319
320 static final Dimension [] preferredThumbSizes = {new Dimension(1, 1),
321 new Dimension(255, 255)};
322
323 public Dimension[] getPreferredThumbnailSizes(ImageTypeSpecifier imageType,
324 ImageWriteParam param,
325 IIOMetadata streamMetadata,
326 IIOMetadata imageMetadata) {
327 if (jfifOK(imageType, param, streamMetadata, imageMetadata)) {
328 return preferredThumbSizes.clone();
329 }
330 return null;
331 }
332
333 private boolean jfifOK(ImageTypeSpecifier imageType,
334 ImageWriteParam param,
335 IIOMetadata streamMetadata,
336 IIOMetadata imageMetadata) {
337 // If the image type and metadata are JFIF compatible, return true
338 if ((imageType != null) &&
339 (!JPEG.isJFIFcompliant(imageType, true))) {
340 return false;
341 }
342 if (imageMetadata != null) {
343 JPEGMetadata metadata = null;
344 if (imageMetadata instanceof JPEGMetadata) {
345 metadata = (JPEGMetadata) imageMetadata;
346 } else {
347 metadata = (JPEGMetadata)convertImageMetadata(imageMetadata,
348 imageType,
349 param);
350 }
351 // metadata must have a jfif node
352 if (metadata.findMarkerSegment
353 (JFIFMarkerSegment.class, true) == null){
354 return false;
355 }
356 }
357 return true;
358 }
359
360 public boolean canWriteRasters() {
361 return true;
362 }
363
364 public void write(IIOMetadata streamMetadata,
365 IIOImage image,
366 ImageWriteParam param) throws IOException {
367 setThreadLock();
368 try {
369 cbLock.check();
370
371 writeOnThread(streamMetadata, image, param);
372 } finally {
373 clearThreadLock();
374 }
375 }
376
377 private void writeOnThread(IIOMetadata streamMetadata,
378 IIOImage image,
379 ImageWriteParam param) throws IOException {
380
381 if (ios == null) {
382 throw new IllegalStateException("Output has not been set!");
383 }
1032 cbLock.lock();
1033 try {
1034 if (aborted) {
1035 processWriteAborted();
1036 } else {
1037 processImageComplete();
1038 }
1039
1040 ios.flush();
1041 } finally {
1042 cbLock.unlock();
1043 }
1044 currentImage++; // After a successful write
1045 }
1046
1047 @Override
1048 public boolean canWriteSequence() {
1049 return true;
1050 }
1051
1052 public void prepareWriteSequence(IIOMetadata streamMetadata)
1053 throws IOException {
1054 setThreadLock();
1055 try {
1056 cbLock.check();
1057
1058 prepareWriteSequenceOnThread(streamMetadata);
1059 } finally {
1060 clearThreadLock();
1061 }
1062 }
1063
1064 private void prepareWriteSequenceOnThread(IIOMetadata streamMetadata)
1065 throws IOException {
1066 if (ios == null) {
1067 throw new IllegalStateException("Output has not been set!");
1068 }
1069
1070 /*
1071 * from jpeg_metadata.html:
1113 streamDCHuffmanTables = JPEG.getDefaultHuffmanTables(true);
1114 }
1115 streamACHuffmanTables =
1116 collectHTablesFromMetadata(jmeta, false);
1117 if (streamACHuffmanTables == null) {
1118 streamACHuffmanTables = JPEG.getDefaultHuffmanTables(false);
1119 }
1120
1121 // Now write them out
1122 writeTables(structPointer,
1123 streamQTables,
1124 streamDCHuffmanTables,
1125 streamACHuffmanTables);
1126 } else {
1127 throw new IIOException("Stream metadata must be JPEG metadata");
1128 }
1129 }
1130 sequencePrepared = true;
1131 }
1132
1133 public void writeToSequence(IIOImage image, ImageWriteParam param)
1134 throws IOException {
1135 setThreadLock();
1136 try {
1137 cbLock.check();
1138
1139 if (sequencePrepared == false) {
1140 throw new IllegalStateException("sequencePrepared not called!");
1141 }
1142 // In the case of JPEG this does nothing different from write
1143 write(null, image, param);
1144 } finally {
1145 clearThreadLock();
1146 }
1147 }
1148
1149 public void endWriteSequence() throws IOException {
1150 setThreadLock();
1151 try {
1152 cbLock.check();
1153
1154 if (sequencePrepared == false) {
1155 throw new IllegalStateException("sequencePrepared not called!");
1156 }
1157 sequencePrepared = false;
1158 } finally {
1159 clearThreadLock();
1160 }
1161 }
1162
1163 public synchronized void abort() {
1164 setThreadLock();
1165 try {
1166 /**
1167 * NB: we do not check the call back lock here, we allow to abort
1168 * the reader any time.
1169 */
1170 super.abort();
1171 abortWrite(structPointer);
1172 } finally {
1173 clearThreadLock();
1174 }
1175 }
1176
1177 @Override
1178 protected synchronized void clearAbortRequest() {
1179 setThreadLock();
1180 try {
1181 cbLock.check();
1182 if (abortRequested()) {
1187 setDest(structPointer);
1188 }
1189 } finally {
1190 clearThreadLock();
1191 }
1192 }
1193
1194 private void resetInternalState() {
1195 // reset C structures
1196 resetWriter(structPointer);
1197
1198 // reset local Java structures
1199 srcRas = null;
1200 raster = null;
1201 convertTosRGB = false;
1202 currentImage = 0;
1203 numScans = 0;
1204 metadata = null;
1205 }
1206
1207 public void reset() {
1208 setThreadLock();
1209 try {
1210 cbLock.check();
1211
1212 super.reset();
1213 } finally {
1214 clearThreadLock();
1215 }
1216 }
1217
1218 public void dispose() {
1219 setThreadLock();
1220 try {
1221 cbLock.check();
1222
1223 if (structPointer != 0) {
1224 disposerRecord.dispose();
1225 structPointer = 0;
1226 }
1227 } finally {
1228 clearThreadLock();
1229 }
1230 }
1231
1232 ////////// End of public API
1233
1234 ///////// Package-access API
1235
1236 /**
1237 * Called by the native code or other classes to signal a warning.
|
177 java.security.AccessController.doPrivileged(
178 new java.security.PrivilegedAction<Void>() {
179 public Void run() {
180 System.loadLibrary("javajpeg");
181 return null;
182 }
183 });
184 initWriterIDs(JPEGQTable.class,
185 JPEGHuffmanTable.class);
186 }
187
188 //////// Public API
189
190 public JPEGImageWriter(ImageWriterSpi originator) {
191 super(originator);
192 structPointer = initJPEGImageWriter();
193 disposerRecord = new JPEGWriterDisposerRecord(structPointer);
194 Disposer.addRecord(disposerReferent, disposerRecord);
195 }
196
197 @Override
198 public void setOutput(Object output) {
199 setThreadLock();
200 try {
201 cbLock.check();
202
203 super.setOutput(output); // validates output
204 resetInternalState();
205 ios = (ImageOutputStream) output; // so this will always work
206 // Set the native destination
207 setDest(structPointer);
208 } finally {
209 clearThreadLock();
210 }
211 }
212
213 @Override
214 public ImageWriteParam getDefaultWriteParam() {
215 return new JPEGImageWriteParam(null);
216 }
217
218 @Override
219 public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) {
220 setThreadLock();
221 try {
222 return new JPEGMetadata(param, this);
223 } finally {
224 clearThreadLock();
225 }
226 }
227
228 @Override
229 public IIOMetadata
230 getDefaultImageMetadata(ImageTypeSpecifier imageType,
231 ImageWriteParam param) {
232 setThreadLock();
233 try {
234 return new JPEGMetadata(imageType, param, this);
235 } finally {
236 clearThreadLock();
237 }
238 }
239
240 public IIOMetadata convertStreamMetadata(IIOMetadata inData,
241 ImageWriteParam param) {
242 // There isn't much we can do. If it's one of ours, then
243 // return it. Otherwise just return null. We use it only
244 // for tables, so we can't get a default and modify it,
245 // as this will usually not be what is intended.
246 if (inData instanceof JPEGMetadata) {
247 JPEGMetadata jpegData = (JPEGMetadata) inData;
248 if (jpegData.isStream) {
286 IIOMetadataFormatImpl.standardMetadataFormatName;
287 Node tree = inData.getAsTree(formatName);
288 if (tree != null) {
289 JPEGMetadata jpegData = new JPEGMetadata(imageType,
290 param,
291 this);
292 try {
293 jpegData.setFromTree(formatName, tree);
294 } catch (IIOInvalidTreeException e) {
295 // Other plug-in generates bogus standard tree
296 // XXX Maybe this should put out a warning?
297 return null;
298 }
299
300 return jpegData;
301 }
302 }
303 return null;
304 }
305
306 @Override
307 public int getNumThumbnailsSupported(ImageTypeSpecifier imageType,
308 ImageWriteParam param,
309 IIOMetadata streamMetadata,
310 IIOMetadata imageMetadata) {
311 // Check whether sufficient data is available.
312 if (imageType == null && imageMetadata == null) {
313 // The method has been invoked with insufficient data. Henceforth
314 // we return -1 as recommended by ImageWriter specification.
315 return -1;
316 }
317
318 // Check if the image type and metadata are JFIF compatible.
319 if (jfifOK(imageType, param, streamMetadata, imageMetadata)) {
320 return Integer.MAX_VALUE;
321 }
322 return 0;
323 }
324
325 static final Dimension [] preferredThumbSizes = {new Dimension(1, 1),
326 new Dimension(255, 255)};
327 @Override
328 public Dimension[] getPreferredThumbnailSizes(ImageTypeSpecifier imageType,
329 ImageWriteParam param,
330 IIOMetadata streamMetadata,
331 IIOMetadata imageMetadata) {
332 if (jfifOK(imageType, param, streamMetadata, imageMetadata)) {
333 return preferredThumbSizes.clone();
334 }
335 return null;
336 }
337
338 private boolean jfifOK(ImageTypeSpecifier imageType,
339 ImageWriteParam param,
340 IIOMetadata streamMetadata,
341 IIOMetadata imageMetadata) {
342 // If the image type and metadata are JFIF compatible, return true
343 if ((imageType != null) &&
344 (!JPEG.isJFIFcompliant(imageType, true))) {
345 return false;
346 }
347 if (imageMetadata != null) {
348 JPEGMetadata metadata = null;
349 if (imageMetadata instanceof JPEGMetadata) {
350 metadata = (JPEGMetadata) imageMetadata;
351 } else {
352 metadata = (JPEGMetadata)convertImageMetadata(imageMetadata,
353 imageType,
354 param);
355 }
356 // metadata must have a jfif node
357 if (metadata.findMarkerSegment
358 (JFIFMarkerSegment.class, true) == null){
359 return false;
360 }
361 }
362 return true;
363 }
364
365 @Override
366 public boolean canWriteRasters() {
367 return true;
368 }
369
370 @Override
371 public void write(IIOMetadata streamMetadata,
372 IIOImage image,
373 ImageWriteParam param) throws IOException {
374 setThreadLock();
375 try {
376 cbLock.check();
377
378 writeOnThread(streamMetadata, image, param);
379 } finally {
380 clearThreadLock();
381 }
382 }
383
384 private void writeOnThread(IIOMetadata streamMetadata,
385 IIOImage image,
386 ImageWriteParam param) throws IOException {
387
388 if (ios == null) {
389 throw new IllegalStateException("Output has not been set!");
390 }
1039 cbLock.lock();
1040 try {
1041 if (aborted) {
1042 processWriteAborted();
1043 } else {
1044 processImageComplete();
1045 }
1046
1047 ios.flush();
1048 } finally {
1049 cbLock.unlock();
1050 }
1051 currentImage++; // After a successful write
1052 }
1053
1054 @Override
1055 public boolean canWriteSequence() {
1056 return true;
1057 }
1058
1059 @Override
1060 public void prepareWriteSequence(IIOMetadata streamMetadata)
1061 throws IOException {
1062 setThreadLock();
1063 try {
1064 cbLock.check();
1065
1066 prepareWriteSequenceOnThread(streamMetadata);
1067 } finally {
1068 clearThreadLock();
1069 }
1070 }
1071
1072 private void prepareWriteSequenceOnThread(IIOMetadata streamMetadata)
1073 throws IOException {
1074 if (ios == null) {
1075 throw new IllegalStateException("Output has not been set!");
1076 }
1077
1078 /*
1079 * from jpeg_metadata.html:
1121 streamDCHuffmanTables = JPEG.getDefaultHuffmanTables(true);
1122 }
1123 streamACHuffmanTables =
1124 collectHTablesFromMetadata(jmeta, false);
1125 if (streamACHuffmanTables == null) {
1126 streamACHuffmanTables = JPEG.getDefaultHuffmanTables(false);
1127 }
1128
1129 // Now write them out
1130 writeTables(structPointer,
1131 streamQTables,
1132 streamDCHuffmanTables,
1133 streamACHuffmanTables);
1134 } else {
1135 throw new IIOException("Stream metadata must be JPEG metadata");
1136 }
1137 }
1138 sequencePrepared = true;
1139 }
1140
1141 @Override
1142 public void writeToSequence(IIOImage image, ImageWriteParam param)
1143 throws IOException {
1144 setThreadLock();
1145 try {
1146 cbLock.check();
1147
1148 if (sequencePrepared == false) {
1149 throw new IllegalStateException("sequencePrepared not called!");
1150 }
1151 // In the case of JPEG this does nothing different from write
1152 write(null, image, param);
1153 } finally {
1154 clearThreadLock();
1155 }
1156 }
1157
1158 @Override
1159 public void endWriteSequence() throws IOException {
1160 setThreadLock();
1161 try {
1162 cbLock.check();
1163
1164 if (sequencePrepared == false) {
1165 throw new IllegalStateException("sequencePrepared not called!");
1166 }
1167 sequencePrepared = false;
1168 } finally {
1169 clearThreadLock();
1170 }
1171 }
1172
1173 @Override
1174 public synchronized void abort() {
1175 setThreadLock();
1176 try {
1177 /**
1178 * NB: we do not check the call back lock here, we allow to abort
1179 * the reader any time.
1180 */
1181 super.abort();
1182 abortWrite(structPointer);
1183 } finally {
1184 clearThreadLock();
1185 }
1186 }
1187
1188 @Override
1189 protected synchronized void clearAbortRequest() {
1190 setThreadLock();
1191 try {
1192 cbLock.check();
1193 if (abortRequested()) {
1198 setDest(structPointer);
1199 }
1200 } finally {
1201 clearThreadLock();
1202 }
1203 }
1204
1205 private void resetInternalState() {
1206 // reset C structures
1207 resetWriter(structPointer);
1208
1209 // reset local Java structures
1210 srcRas = null;
1211 raster = null;
1212 convertTosRGB = false;
1213 currentImage = 0;
1214 numScans = 0;
1215 metadata = null;
1216 }
1217
1218 @Override
1219 public void reset() {
1220 setThreadLock();
1221 try {
1222 cbLock.check();
1223
1224 super.reset();
1225 } finally {
1226 clearThreadLock();
1227 }
1228 }
1229
1230 @Override
1231 public void dispose() {
1232 setThreadLock();
1233 try {
1234 cbLock.check();
1235
1236 if (structPointer != 0) {
1237 disposerRecord.dispose();
1238 structPointer = 0;
1239 }
1240 } finally {
1241 clearThreadLock();
1242 }
1243 }
1244
1245 ////////// End of public API
1246
1247 ///////// Package-access API
1248
1249 /**
1250 * Called by the native code or other classes to signal a warning.
|