Package: EditableImage
EditableImage
name | instruction | branch | complexity | line | method | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
EditableImage() |
|
|
|
|
|
||||||||||||||||||||
EditableImage(ImageModel) |
|
|
|
|
|
||||||||||||||||||||
as(As.Type) |
|
|
|
|
|
||||||||||||||||||||
as(Class) |
|
|
|
|
|
||||||||||||||||||||
asMany(As.Type) |
|
|
|
|
|
||||||||||||||||||||
asMany(Class) |
|
|
|
|
|
||||||||||||||||||||
cloneImage() |
|
|
|
|
|
||||||||||||||||||||
create(AbstractCreateOp) |
|
|
|
|
|
||||||||||||||||||||
create(ReadOp) |
|
|
|
|
|
||||||||||||||||||||
createResizedImage(int, int) |
|
|
|
|
|
||||||||||||||||||||
createResizedImage(int, int, Quality) |
|
|
|
|
|
||||||||||||||||||||
createSimilarImage() |
|
|
|
|
|
||||||||||||||||||||
dispose() |
|
|
|
|
|
||||||||||||||||||||
execute(Operation) |
|
|
|
|
|
||||||||||||||||||||
executeInPlace(Operation) |
|
|
|
|
|
||||||||||||||||||||
getAttribute(Key) |
|
|
|
|
|
||||||||||||||||||||
getAttributes() |
|
|
|
|
|
||||||||||||||||||||
getAvailableExtensions() |
|
|
|
|
|
||||||||||||||||||||
getBandCount() |
|
|
|
|
|
||||||||||||||||||||
getBitsPerBand() |
|
|
|
|
|
||||||||||||||||||||
getBitsPerPixel() |
|
|
|
|
|
||||||||||||||||||||
getColorModel() |
|
|
|
|
|
||||||||||||||||||||
getDataType() |
|
|
|
|
|
||||||||||||||||||||
getHeight() |
|
|
|
|
|
||||||||||||||||||||
getICCProfile() |
|
|
|
|
|
||||||||||||||||||||
getImageModel() |
|
|
|
|
|
||||||||||||||||||||
getInnerProperty(Class) |
|
|
|
|
|
||||||||||||||||||||
getLatestOperationDuration() |
|
|
|
|
|
||||||||||||||||||||
getLatestSerializationSize() |
|
|
|
|
|
||||||||||||||||||||
getMemorySize() |
|
|
|
|
|
||||||||||||||||||||
getMetadata(Class) |
|
|
|
|
|
||||||||||||||||||||
getMetadata(Class, int) |
|
|
|
|
|
||||||||||||||||||||
getMetadataCount(Class) |
|
|
|
|
|
||||||||||||||||||||
getNickName() |
|
|
|
|
|
||||||||||||||||||||
getWidth() |
|
|
|
|
|
||||||||||||||||||||
hasRaster() |
|
|
|
|
|
||||||||||||||||||||
internalExecute(Operation) |
|
|
|
|
|
||||||||||||||||||||
lambda$getMetadata$0(int, List) |
|
|
|
|
|
||||||||||||||||||||
maybeAs(As.Type) |
|
|
|
|
|
||||||||||||||||||||
maybeAs(Class) |
|
|
|
|
|
||||||||||||||||||||
removeAttribute(String) |
|
|
|
|
|
||||||||||||||||||||
setAttribute(Key, Object) |
|
|
|
|
|
||||||||||||||||||||
setAttributes(Map) |
|
|
|
|
|
||||||||||||||||||||
setNickName(String) |
|
|
|
|
|
||||||||||||||||||||
static {...} |
|
|
|
|
|
||||||||||||||||||||
toString() |
|
|
|
|
|
Coverage
1: /*
2: * *********************************************************************************************************************
3: *
4: * Mistral: open source imaging engine
5: * http://tidalwave.it/projects/mistral
6: *
7: * Copyright (C) 2003 - 2023 by Tidalwave s.a.s. (http://tidalwave.it)
8: *
9: * *********************************************************************************************************************
10: *
11: * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
12: * the License. You may obtain a copy of the License at
13: *
14: * http://www.apache.org/licenses/LICENSE-2.0
15: *
16: * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
17: * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
18: * specific language governing permissions and limitations under the License.
19: *
20: * *********************************************************************************************************************
21: *
22: * git clone https://bitbucket.org/tidalwave/mistral-src
23: * git clone https://github.com/tidalwave-it/mistral-src
24: *
25: * *********************************************************************************************************************
26: */
27: package it.tidalwave.image;
28:
29: import javax.annotation.Nonnegative;
30: import javax.annotation.Nonnull;
31: import java.time.Duration;
32: import java.time.Instant;
33: import java.util.Arrays;
34: import java.util.Collection;
35: import java.util.HashMap;
36: import java.util.List;
37: import java.util.Map;
38: import java.util.Optional;
39: import java.util.Set;
40: import java.util.TreeSet;
41: import java.io.IOException;
42: import java.io.Serializable;
43: import javax.imageio.ImageIO;
44: import javax.imageio.metadata.IIOMetadata;
45: import java.awt.color.ICC_ColorSpace;
46: import java.awt.color.ICC_Profile;
47: import java.awt.image.ColorModel;
48: import java.awt.image.DataBuffer;
49: import it.tidalwave.util.As;
50: import it.tidalwave.util.Key;
51: import it.tidalwave.util.TypeSafeMap;
52: import it.tidalwave.image.metadata.Directory;
53: import it.tidalwave.image.metadata.EXIF;
54: import it.tidalwave.image.metadata.IPTC;
55: import it.tidalwave.image.metadata.MakerNote;
56: import it.tidalwave.image.metadata.TIFF;
57: import it.tidalwave.image.metadata.XMP;
58: import it.tidalwave.image.op.AbstractCreateOp;
59: import it.tidalwave.image.op.AccessorOp;
60: import it.tidalwave.image.op.ImplementationFactoryRegistry;
61: import it.tidalwave.image.op.Operation;
62: import it.tidalwave.image.op.OperationImplementation;
63: import it.tidalwave.image.op.ReadOp;
64: import it.tidalwave.image.op.ScaleOp;
65: import lombok.Getter;
66: import lombok.RequiredArgsConstructor;
67: import lombok.ToString;
68: import lombok.experimental.Delegate;
69: import lombok.extern.slf4j.Slf4j;
70:
71: /***********************************************************************************************************************
72: *
73: * An opaque class which encapsulates all the image manipulation logics, and allows the implementation of these logics
74: * to be transparently changed (e.g. by using or not JAI, etc...)
75: *
76: * @author Fabrizio Giudici
77: *
78: **********************************************************************************************************************/
79: @ToString(of = {"imageModelHolder", "attributeMapByName"}) @Slf4j
80: public final class EditableImage implements As, Cloneable, Serializable // Externalizable
81: {
82: /*******************************************************************************************************************
83: *
84: *
85: ******************************************************************************************************************/
86: @RequiredArgsConstructor
87: public static enum DataType
88: {
89: BYTE(DataBuffer.TYPE_BYTE),
90: UNSIGNED_SHORT(DataBuffer.TYPE_USHORT),
91: SHORT(DataBuffer.TYPE_SHORT),
92: INT(DataBuffer.TYPE_INT),
93: FLOAT(DataBuffer.TYPE_FLOAT),
94: DOUBLE(DataBuffer.TYPE_DOUBLE),
95: UNDEFINED(DataBuffer.TYPE_UNDEFINED);
96:
97: @Getter
98: private final int value;
99:
100: /***************************************************************************************************************
101: *
102: * Returns the size in bits of this data type.
103: *
104: **************************************************************************************************************/
105: @Nonnegative
106: public int getSize()
107: {
108: return DataBuffer.getDataTypeSize(value);
109: }
110:
111: /***************************************************************************************************************
112: *
113: *
114: **************************************************************************************************************/
115: @Nonnull
116: public static DataType valueOf (final int value)
117: {
118: for (final var dataType : DataType.values())
119: {
120: if (dataType.value == value)
121: {
122: return dataType;
123: }
124: }
125:
126: return EditableImage.DataType.UNDEFINED;
127: }
128: }
129:
130: @RequiredArgsConstructor
131: public static class Accessor // FIXME: protected
132: {
133: @Nonnull
134: private final EditableImage image;
135:
136: public void setIIOMetadata (@Nonnull final IIOMetadata iioMetadata)
137: {
138: image.iioMetadata = iioMetadata;
139: }
140:
141: @Nonnull
142: public Map<Class<? extends Directory>, List<Directory>> getMetadataMapByClass ()
143: {
144: return image.metadataMapByClass;
145: }
146:
147: public void setLatestOperationDuration (@Nonnull final Duration latestOperationDuration)
148: {
149: image.latestOperationDuration = latestOperationDuration;
150: }
151: }
152:
153: private final AccessorOp accessor = new AccessorOp(this);
154:
155: private static final long serialVersionUID = -4524534539832240717L;
156: private static final String CLASS = EditableImage.class.getName();
157:
158: public static final Key<String> PROP_FORMAT = Key.of(CLASS + ".format", String.class);
159:
160: public static final Key<String> PROP_MIME_TYPE = Key.of(CLASS + ".mimeType", String.class);
161:
162: @Delegate
163: private final As asDelegate = As.forObject(this);
164:
165: /** The current image model. */
166: private ImageModelHolder imageModelHolder;
167:
168: /** The metadata as it comes from Image I/O. */
169: private transient IIOMetadata iioMetadata; // TODO make it serializable
170:
171: private final Map<Class<? extends Directory>, List<Directory>> metadataMapByClass = new HashMap<>();
172:
173: /** The attributes. */
174: @Nonnull
175: private TypeSafeMap attributeMapByName = TypeSafeMap.newInstance();
176:
177: private Duration latestOperationDuration;
178:
179: @Nonnegative
180: private int latestSerializationSize;
181:
182: /*******************************************************************************************************************
183: *
184: * For serialization only. Do not use.
185: *
186: ******************************************************************************************************************/
187: public EditableImage()
188: {
189: // By default put empty objects for which isAvailable() returns false
190: metadataMapByClass.put(TIFF.class, List.of(new TIFF()));
191: metadataMapByClass.put(EXIF.class, List.of(new EXIF()));
192: metadataMapByClass.put(IPTC.class, List.of(new IPTC()));
193: metadataMapByClass.put(XMP.class, List.of(new XMP()));
194: metadataMapByClass.put(MakerNote.class, List.of(new MakerNote()));
195: }
196:
197: /*******************************************************************************************************************
198: *
199: * For inner implementation only. Do not use.
200: *
201: ******************************************************************************************************************/
202: public EditableImage (final ImageModel imageModel) // FIXME: try to make it protected
203: {
204: // null imageModel is accepted for instances carrying only metadata
205: imageModelHolder = ImageModelHolder.wrap(imageModel);
206: }
207:
208: /*******************************************************************************************************************
209: *
210: *
211: ******************************************************************************************************************/
212: public void setNickName (final @Nonnull String nickName)
213: {
214:• if (imageModelHolder != null)
215: {
216: imageModelHolder.setNickName(nickName);
217: }
218: }
219:
220: /*******************************************************************************************************************
221: *
222: *
223: ******************************************************************************************************************/
224: @Nonnull
225: public Optional<String> getNickName()
226: {
227:• return (imageModelHolder != null) ? Optional.ofNullable(imageModelHolder.getNickName()) : Optional.empty();
228: }
229:
230: /*******************************************************************************************************************
231: *
232: * Creates a new EditableImage as specified by the parameter
233: *
234: * @param createOp the way the image should be created
235: * @return the image
236: *
237: ******************************************************************************************************************/
238: @Nonnull
239: public static EditableImage create (@Nonnull final AbstractCreateOp createOp)
240: {
241: final var editableImage = new EditableImage(null);
242: final var image = editableImage.internalExecute(createOp);
243: final var imageModel = ImplementationFactoryRegistry.getDefault().createImageModel(image);
244: editableImage.imageModelHolder = ImageModelHolder.wrap(imageModel);
245:
246: return editableImage;
247: }
248:
249: /*******************************************************************************************************************
250: *
251: * Reads a new EditableImage as specified by the parameter
252: *
253: * @param readOp the way the image should be read
254: * @return the image
255: *
256: ******************************************************************************************************************/
257: // FIXME: merge with create(AbstractCreateOp), introduce ReadJ2DOp
258: @Nonnull
259: public static EditableImage create (@Nonnull final ReadOp readOp)
260: throws IOException
261: {
262: return readOp.execute();
263: }
264:
265: /*******************************************************************************************************************
266: *
267: * Returns true if the image has a raster (EditableImages can be loaded with
268: * metadata only).
269: *
270: * @return true if the image has a raster
271: *
272: ******************************************************************************************************************/
273: public boolean hasRaster()
274: {
275:• return imageModelHolder.get() != null;
276: }
277:
278: /*******************************************************************************************************************
279: *
280: * DO NOT USE THIS. This method is only used by the module implementation.
281: *
282: ******************************************************************************************************************/
283: public ImageModel getImageModel()
284: {
285: return imageModelHolder.get();
286: }
287:
288: private static boolean availableExtensionsLogged;
289:
290: /*******************************************************************************************************************
291: *
292: * Returns all the file extensions of file formats that can be read into an
293: * EditableImage. The <code>ImageIO</code> registry is called to retrieve the
294: * requested information.
295: *
296: * @return an array of all file extensions
297: *
298: ******************************************************************************************************************/
299: @Nonnull
300: public static Collection<String> getAvailableExtensions()
301: {
302: final boolean logExtensions;
303:
304: synchronized (EditableImage.class)
305: {
306:• logExtensions = !availableExtensionsLogged;
307: availableExtensionsLogged = true;
308: }
309:
310:• if (logExtensions)
311: {
312: log.info("getAvailableExtensions()");
313: }
314:
315: final Set<String> suffixList = new TreeSet<>();
316:
317:• for (final var formatName : ImageIO.getReaderFormatNames())
318: {
319:• for (final var i = ImageIO.getImageReadersByFormatName(formatName); i.hasNext(); )
320: {
321: final var imageReader = i.next();
322: final var originatingProvider = imageReader.getOriginatingProvider();
323: final var suffixes = originatingProvider.getFileSuffixes();
324: final var suffixesAsList = Arrays.asList(suffixes);
325: suffixList.addAll(suffixesAsList);
326:
327:• if (logExtensions)
328: {
329: log.info(">>>> reader - format name: {} provider: {} supports {}",
330: formatName, originatingProvider.getPluginClassName(), suffixesAsList);
331: }
332: }
333: }
334:
335:• if (logExtensions)
336: {
337: log.info(">>>> returning {}", suffixList);
338: }
339:
340: return suffixList;
341: }
342:
343: /*******************************************************************************************************************
344: *
345: *
346: ******************************************************************************************************************/
347: public <T extends Directory> Optional<T> getMetadata (final @Nonnull Class<T> metadataClass)
348: {
349: return getMetadata(metadataClass, 0);
350: }
351:
352: /*******************************************************************************************************************
353: *
354: * Retrieve a metadata directory.
355: *
356: * @param metadataClass the type of the directory
357: * @param index the index (in case of multiple items)
358: * @return the metadata directory
359: *
360: ******************************************************************************************************************/
361: public <T extends Directory> Optional<T> getMetadata (final @Nonnull Class<T> metadataClass, final @Nonnegative int index)
362: {
363: final var list = (List<T>)metadataMapByClass.get(metadataClass);
364:• return Optional.ofNullable(list).flatMap(l -> l.isEmpty() ? Optional.empty() : Optional.of(l.get(index)));
365: }
366:
367: /*******************************************************************************************************************
368: *
369: *
370: ******************************************************************************************************************/
371: @Nonnegative
372: public int getMetadataCount (final @Nonnull Class<?> metadataClass)
373: {
374: return Optional.ofNullable(metadataMapByClass.get(metadataClass)).map(List::size).orElse(0);
375: }
376:
377: /*******************************************************************************************************************
378: *
379: * Returns the width of this image.
380: *
381: * @return the width
382: *
383: ******************************************************************************************************************/
384: @Nonnegative
385: public int getWidth()
386: {
387: return imageModelHolder.get().getWidth();
388: }
389:
390: /*******************************************************************************************************************
391: *
392: * Returns the height of this image.
393: *
394: * @return the height
395: *
396: ******************************************************************************************************************/
397: @Nonnegative
398: public int getHeight()
399: {
400: return imageModelHolder.get().getHeight();
401: }
402:
403: /*******************************************************************************************************************
404: *
405: * Returns the dataType used by this image.
406: *
407: * @return the data type
408: *
409: ******************************************************************************************************************/
410: @Nonnull
411: public DataType getDataType()
412: {
413: return imageModelHolder.get().getDataType();
414: }
415:
416: /*******************************************************************************************************************
417: *
418: * Returns the number of bands this EditableImage is composed of.
419: *
420: * @return the band count
421: *
422: ******************************************************************************************************************/
423: @Nonnegative
424: public int getBandCount()
425: {
426: return imageModelHolder.get().getBandCount();
427: }
428:
429: /*******************************************************************************************************************
430: *
431: * Returns the number of sample bits for each band this EditableImage is
432: * composed of.
433: *
434: * @return the number of bits
435: *
436: ******************************************************************************************************************/
437: @Nonnegative
438: public int getBitsPerBand()
439: {
440: return getDataType().getSize();
441: }
442:
443: /*******************************************************************************************************************
444: *
445: * Returns the number of sample bits for each pixel this EditableImage is
446: * composed of.
447: *
448: * @return the number of bits
449: *
450: ******************************************************************************************************************/
451: @Nonnegative
452: public int getBitsPerPixel()
453: {
454: return getBandCount() * getBitsPerBand();
455: }
456:
457: /*******************************************************************************************************************
458: *
459: * Executes an operation. The original image is lost and replaced by results.
460: *
461: * @param operation the operation to perform
462: * @return the operation (as a convenience in case it carries results)
463: *
464: ******************************************************************************************************************/
465: @Nonnull
466: public <T extends Operation> T executeInPlace (@Nonnull final T operation)
467: {
468: final var time = Instant.now();
469: final var image = internalExecute(operation);
470: imageModelHolder.get().setImage(image);
471: latestOperationDuration = Duration.between(time, Instant.now());
472:
473: return operation;
474: }
475:
476: /*******************************************************************************************************************
477: *
478: * Executes an operation. The original image is untouched as the results are placed in a brand-new instance of
479: * EditableImage.
480: *
481: * @param operation the operation to perform
482: * @return the result
483: *
484: ******************************************************************************************************************/
485: @Nonnull
486: public EditableImage execute (@Nonnull final Operation operation)
487: {
488: try
489: {
490: final var time = Instant.now();
491: final var image = internalExecute(operation);
492: final var modelClass = imageModelHolder.get().getClass();
493: final var constructor = modelClass.getConstructor(Object.class);
494: final var newModel = constructor.newInstance(image);
495: final var result = new EditableImage(newModel);
496: result.attributeMapByName = attributeMapByName; // immutable
497: result.latestOperationDuration = Duration.between(time, Instant.now());;
498:
499: return result;
500: }
501: catch (Exception e)
502: {
503: throw new RuntimeException(e);
504: }
505: }
506:
507: /*******************************************************************************************************************
508: *
509: * Returns the elapsed time of the latest operation performed. Note that for
510: * execute2() this value is available on the result. When an image is
511: * deserialized, this method returns the serialization time (this relies upon
512: * the fact that the clocks on all network nodes are synchronized).
513: *
514: * @return the latest operation elapsed time
515: *
516: ******************************************************************************************************************/
517: @Nonnull
518: public Duration getLatestOperationDuration()
519: {
520: return latestOperationDuration;
521: }
522:
523: /*******************************************************************************************************************
524: *
525: * Creates a similar image, that is a blank image with the same characteristics
526: * of this image (width, height, data type, color model).
527: * @deprecated will be merged with create(AbstractCreateOp)
528: *
529: * @return a new, similar image
530: *
531: ******************************************************************************************************************/
532: @Nonnull
533: public EditableImage createSimilarImage()
534: {
535: final var imageCopy = imageModelHolder.get().createCopy(false);
536: imageCopy.attributeMapByName = attributeMapByName; // immutable
537: return imageCopy;
538: }
539:
540: /*******************************************************************************************************************
541: *
542: * Clones this image.
543: *
544: ******************************************************************************************************************/
545: @Nonnull
546: public EditableImage cloneImage()
547: {
548: final var imageCopy = imageModelHolder.get().createCopy(true);
549: imageCopy.attributeMapByName = attributeMapByName; // immutable
550: return imageCopy;
551: }
552:
553: /*******************************************************************************************************************
554: *
555: * Creates a resized image. - FIXME should be removed
556: * @deprecated
557: *
558: ******************************************************************************************************************/
559: @Nonnull
560: public EditableImage createResizedImage (final @Nonnegative int width, final @Nonnegative int height)
561: {
562: return createResizedImage(width, height, Quality.FASTEST);
563: }
564:
565: /*******************************************************************************************************************
566: *
567: * Creates a resized image. - FIXME move to a factory method for ScaleOp.
568: * @deprecated
569: *
570: ******************************************************************************************************************/
571: @Nonnull
572: public EditableImage createResizedImage (final @Nonnegative int width,
573: final @Nonnegative int height,
574: final @Nonnull Quality quality)
575: {
576: final var hScale = (double)width / (double)getWidth();
577: final var vScale = (double)height / (double)getHeight();
578: final var scaleOp = new ScaleOp(hScale, vScale, quality);
579: executeInPlace(scaleOp);
580:
581: return this;
582: }
583:
584: /*******************************************************************************************************************
585: *
586: * Sets an attribute of this image. Attributes are user-specific name-value pairs.
587: *
588: * @param key the attribute name
589: * @param value the attribute value
590: *
591: ******************************************************************************************************************/
592: public <T> void setAttribute (final @Nonnull Key<T> key, final @Nonnull T value)
593: {
594: attributeMapByName = attributeMapByName.with(key, value);
595: }
596:
597: /*******************************************************************************************************************
598: *
599: * Returns an attribute of this image.
600: *
601: * @param key the attribute name
602: * @return the attribute value
603: *
604: ******************************************************************************************************************/
605: @Nonnull
606: public <T> Optional<T> getAttribute (final @Nonnull Key<T> key)
607: {
608: return attributeMapByName.getOptional(key);
609: }
610:
611: /*******************************************************************************************************************
612: *
613: *
614: ******************************************************************************************************************/
615: public void setAttributes (final @Nonnull Map<Key<?>, Object> attributes)
616: {
617: attributeMapByName = TypeSafeMap.ofCloned(attributes);
618: }
619:
620: /*******************************************************************************************************************
621: *
622: *
623: ******************************************************************************************************************/
624: @Nonnull
625: public TypeSafeMap getAttributes()
626: {
627: return attributeMapByName; // immutable
628: }
629:
630: /*******************************************************************************************************************
631: *
632: * Removes an attribute from this image.
633: *
634: * @param name the attribute name
635: * @return the attribute value
636: *
637: ******************************************************************************************************************/
638: @Nonnull
639: public <T> T removeAttribute (final @Nonnull String name)
640: {
641: throw new UnsupportedOperationException(); // FIXME: need to add TypeSafeMap.without()
642: }
643:
644: /*******************************************************************************************************************
645: *
646: * Removes all the resources bound to this image.
647: *
648: ******************************************************************************************************************/
649: public void dispose()
650: {
651: imageModelHolder.get().dispose();
652: imageModelHolder = null;
653: attributeMapByName = TypeSafeMap.newInstance();
654: }
655:
656: /*******************************************************************************************************************
657: *
658: * Returns an estimate of the memory allocated by this image.
659: *
660: * @return the memory allocated for this image
661: *
662: ******************************************************************************************************************/
663: @Nonnegative
664: public long getMemorySize()
665: {
666: final var imageModel = imageModelHolder.get();
667:• return (imageModel != null) ? imageModel.getMemorySize() : 0;
668: }
669:
670: /*******************************************************************************************************************
671: *
672: * Returns the ColorModel of this image.
673: *
674: * @return the color model
675: *
676: ******************************************************************************************************************/
677: @Nonnegative
678: public ColorModel getColorModel() // FIXME: to be removed
679: {
680: return imageModelHolder.get().getColorModel();
681: }
682:
683: /*******************************************************************************************************************
684: *
685: * Returns the ICC_Profile of this image (null will be returned if the ColorModel is not ICC-based). <i>Note that
686: * this is the profile of the image as it is optimized for the display, which is almost surely sRGB; and <b>it's
687: * probably different than the original image profile</b></i>.
688: *
689: * @return the color profile
690: *
691: ******************************************************************************************************************/
692: @Nonnegative
693: public ICC_Profile getICCProfile() // FIXME: to be removed
694: {
695: final var colorModel = getColorModel();
696:
697:• if (colorModel != null)
698: {
699: final var colorSpace = colorModel.getColorSpace();
700:
701:• if (colorSpace instanceof ICC_ColorSpace)
702: {
703: final var iccColorSpace = (ICC_ColorSpace)colorSpace;
704: return iccColorSpace.getProfile();
705: }
706: }
707:
708: return null;
709: }
710:
711: /*******************************************************************************************************************
712: *
713: *
714: ******************************************************************************************************************/
715:
716: /*
717: private final static int COMPRESSED = 1;
718:
719: public void writeExternal (ObjectOutput out)
720: throws IOException
721: {
722: ByteArrayOutputStream baos = new ByteArrayOutputStream();
723: // GZIPOutputStream gos = new GZIPOutputStream(baos);
724: DataOutputStream dos = new DataOutputStream(baos);
725: dos.writeUTF(imageModel.getClass().getName());
726: imageModel.writeExternal(dos);
727: dos.flush();
728: dos.close();
729: byte[] buffer = baos.toByteArray();
730:
731: out.writeLong(System.currentTimeMillis());
732: out.writeInt(0);
733: out.writeInt(buffer.length);
734: out.write(buffer);
735: out.flush();
736: }*/
737:
738: /*******************************************************************************************************************
739: *
740: *
741: ******************************************************************************************************************/
742:
743: /*
744: public void readExternal (ObjectInput in)
745: throws IOException, ClassNotFoundException
746: {
747: long serializationTimeStamp = in.readLong();
748: latestOperationTime = System.currentTimeMillis() - serializationTimeStamp;
749: int mode = in.readInt();
750: int bufferSize = in.readInt();
751: byte[] buffer = new byte[bufferSize];
752: in.readFully(buffer);
753: InputStream is = new ByteArrayInputStream(buffer);
754:
755: if (mode == COMPRESSED)
756: {
757: is = new GZIPInputStream(is);
758: }
759:
760: DataInputStream dis = new DataInputStream(is);
761: String className = dis.readUTF();
762: Class clazz = Class.forName(className);
763: try
764: {
765: imageModel = (ImageModel)clazz.newInstance();
766: }
767: catch (InstantiationException e)
768: {
769: throw new RuntimeException(e);
770: }
771: catch (IllegalAccessException e)
772: {
773: throw new RuntimeException(e);
774: }
775:
776: imageModel.readExternal(dis);
777: dis.close();
778: latestSerializationSize = bufferSize;
779: }*/
780:
781: /*******************************************************************************************************************
782: *
783: *
784: ******************************************************************************************************************/
785: @Nonnull
786: public long getLatestSerializationSize()
787: {
788: return latestSerializationSize;
789: }
790:
791: /*******************************************************************************************************************
792: *
793: * DO NOT USE. This is only for implementation and testing purposes.
794: *
795: ******************************************************************************************************************/
796: @Nonnull
797: public <T> T getInnerProperty (final @Nonnull Class<T> propertyClass)
798: {
799:• if (AccessorOp.class.equals(propertyClass))
800: {
801: return propertyClass.cast(accessor);
802: }
803:
804:• if (IIOMetadata.class.equals(propertyClass))
805: {
806: return propertyClass.cast(iioMetadata);
807: }
808:
809: return imageModelHolder.get().getInnerProperty(propertyClass);
810: }
811:
812: /*******************************************************************************************************************
813: *
814: * Executes an operation and return the raw result (the object to be wrapped by the ImageModel).
815: *
816: * @param operation the operation to perform
817: * @return the result (the object wrapped by the ImageModel)
818: *
819: ******************************************************************************************************************/
820: @Nonnull
821: private Object internalExecute (final @Nonnull Operation operation)
822: throws UnsupportedOperationException
823: {
824: final var implementationFactoryRegistry = ImplementationFactoryRegistry.getDefault();
825: final var imageModel = imageModelHolder.get();
826:• var image = (imageModel != null) ? imageModel.getImage() : null;
827: OperationImplementation implementation = null;
828:
829:• if ((image == null) && !(operation instanceof AbstractCreateOp))
830: {
831: throw new RuntimeException("null image with an Op different that AbstractCreateOp");
832: }
833:
834: try
835: {
836: implementation = implementationFactoryRegistry.findImplementation(operation, imageModelHolder.get(), false);
837: }
838: catch (UnsupportedOperationException e)
839: {
840: log.warn("No default implementation of {} for model: {}", operation, image);
841: implementation = implementationFactoryRegistry.findImplementation(operation, imageModelHolder.get(), true);
842: log.info("Found alternate implementation: {}", implementation);
843:
844:• if (!(operation instanceof AbstractCreateOp))
845: {
846:• if (implementation.getFactory().canConvertFrom(image.getClass()))
847: {
848: log.info(">>>> CONVERT FROM using {}", implementation.getFactory());
849: imageModelHolder = ImageModelHolder.wrap(implementation.getFactory().convertFrom(image));
850: image = imageModelHolder.get().getImage();
851: }
852:
853:• else if (imageModelHolder.get().getFactory().canConvertTo(implementation.getFactory().getModelClass()))
854: {
855: log.info(">>>> CONVERT TO {}", implementation.getFactory().getModelClass());
856: image = imageModelHolder.get().getFactory().convertTo(implementation.getFactory().getModelClass());
857: }
858:
859: else
860: {
861: throw new RuntimeException("Shouldn't get here");
862: }
863:
864: log.info(">>>> NEW IMAGE {} NEW IMAGE MODEL {}", image, imageModelHolder);
865: }
866: }
867:
868: try
869: {
870: return implementation.execute(this, image);
871: }
872: catch (RuntimeException e)
873: {
874: log.error("Operation failed, offending image: {}", image);
875: throw e;
876: }
877: }
878: }