Content of file EditableImage.java.html

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>EditableImage.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Mistral Core</a> &gt; <a href="index.source.html" class="el_package">it.tidalwave.image</a> &gt; <span class="el_source">EditableImage.java</span></div><h1>EditableImage.java</h1><pre class="source lang-java linenums">/*
 * *********************************************************************************************************************
 *
 * Mistral: open source imaging engine
 * http://tidalwave.it/projects/mistral
 *
 * Copyright (C) 2003 - 2023 by Tidalwave s.a.s. (http://tidalwave.it)
 *
 * *********************************************************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the &quot;License&quot;); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations under the License.
 *
 * *********************************************************************************************************************
 *
 * git clone https://bitbucket.org/tidalwave/mistral-src
 * git clone https://github.com/tidalwave-it/mistral-src
 *
 * *********************************************************************************************************************
 */
package it.tidalwave.image;

import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.io.IOException;
import java.io.Serializable;
import javax.imageio.ImageIO;
import javax.imageio.metadata.IIOMetadata;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import it.tidalwave.util.Key;
import it.tidalwave.util.TypeSafeMap;
import it.tidalwave.image.metadata.Directory;
import it.tidalwave.image.metadata.EXIF;
import it.tidalwave.image.metadata.IPTC;
import it.tidalwave.image.metadata.MakerNote;
import it.tidalwave.image.metadata.TIFF;
import it.tidalwave.image.metadata.XMP;
import it.tidalwave.image.op.AbstractCreateOp;
import it.tidalwave.image.op.AccessorOp;
import it.tidalwave.image.op.ImplementationFactoryRegistry;
import it.tidalwave.image.op.Operation;
import it.tidalwave.image.op.OperationImplementation;
import it.tidalwave.image.op.ReadOp;
import it.tidalwave.image.op.ScaleOp;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

/***********************************************************************************************************************
 *
 * An opaque class which encapsulates all the image manipulation logics, and allows the implementation of these logics 
 * to be transparently changed (e.g. by using or not JAI, etc...)
 *
 * @author Fabrizio Giudici
 *
 **********************************************************************************************************************/
<span class="fc" id="L77">@ToString(of = {&quot;imageModelHolder&quot;, &quot;attributeMapByName&quot;}) @Slf4j</span>
public final class EditableImage implements Cloneable, Serializable // Externalizable
  {
    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
<span class="fc" id="L84">    @RequiredArgsConstructor</span>
    public static enum DataType
      {
<span class="fc" id="L87">        BYTE(DataBuffer.TYPE_BYTE),</span>
<span class="fc" id="L88">        UNSIGNED_SHORT(DataBuffer.TYPE_USHORT),</span>
<span class="fc" id="L89">        SHORT(DataBuffer.TYPE_SHORT),</span>
<span class="fc" id="L90">        INT(DataBuffer.TYPE_INT),</span>
<span class="fc" id="L91">        FLOAT(DataBuffer.TYPE_FLOAT),</span>
<span class="fc" id="L92">        DOUBLE(DataBuffer.TYPE_DOUBLE),</span>
<span class="fc" id="L93">        UNDEFINED(DataBuffer.TYPE_UNDEFINED);</span>

<span class="nc" id="L95">        @Getter</span>
        private final int value;

        /***************************************************************************************************************
         *
         * Returns the size in bits of this data type.
         *
         **************************************************************************************************************/
        @Nonnegative
        public int getSize()
          {
<span class="fc" id="L106">            return DataBuffer.getDataTypeSize(value);</span>
          }

        /***************************************************************************************************************
         *
         *
         **************************************************************************************************************/
        @Nonnull
        public static DataType valueOf (final int value)
          {
<span class="pc bpc" id="L116" title="1 of 2 branches missed.">            for (final var dataType : DataType.values())</span>
              {
<span class="fc bfc" id="L118" title="All 2 branches covered.">                if (dataType.value == value)</span>
                  {
<span class="fc" id="L120">                    return dataType;</span>
                  }
              }

<span class="nc" id="L124">            return EditableImage.DataType.UNDEFINED;</span>
          }
      }

<span class="pc bpc" id="L128" title="1 of 2 branches missed.">    @RequiredArgsConstructor</span>
    public static class Accessor // FIXME: protected
      {
        @Nonnull
        private final EditableImage image;

        public void setIIOMetadata (@Nonnull final IIOMetadata iioMetadata)
          {
<span class="fc" id="L136">            image.iioMetadata = iioMetadata;</span>
<span class="fc" id="L137">          }</span>

        @Nonnull
        public Map&lt;Class&lt;? extends Directory&gt;, List&lt;Directory&gt;&gt; getMetadataMapByClass ()
          {
<span class="fc" id="L142">            return image.metadataMapByClass;</span>
          }

        public void setLatestOperationDuration (@Nonnull final Duration latestOperationDuration)
          {
<span class="fc" id="L147">            image.latestOperationDuration = latestOperationDuration;</span>
<span class="fc" id="L148">          }</span>
      }

<span class="pc" id="L151">    private final AccessorOp accessor = new AccessorOp(this);</span>

    private static final long serialVersionUID = -4524534539832240717L;
<span class="fc" id="L154">    private static final String CLASS = EditableImage.class.getName();</span>

<span class="fc" id="L156">    public static final Key&lt;String&gt; PROP_FORMAT = Key.of(CLASS + &quot;.format&quot;, String.class);</span>

<span class="fc" id="L158">    public static final Key&lt;String&gt; PROP_MIME_TYPE = Key.of(CLASS + &quot;.mimeType&quot;, String.class);</span>

    /** The current image model. */
    private ImageModelHolder imageModelHolder;

    /** The metadata as it comes from Image I/O. */
    private transient IIOMetadata iioMetadata; // TODO make it serializable

<span class="pc" id="L166">    private final Map&lt;Class&lt;? extends Directory&gt;, List&lt;Directory&gt;&gt; metadataMapByClass = new HashMap&lt;&gt;();</span>

    /** The attributes. */
<span class="pc" id="L169">    @Nonnull</span>
<span class="pc" id="L170">    private TypeSafeMap attributeMapByName = TypeSafeMap.newInstance();</span>

    private Duration latestOperationDuration;

    @Nonnegative
    private int latestSerializationSize;

    /*******************************************************************************************************************
     *
     * For serialization only. Do not use.
     *
     ******************************************************************************************************************/
    public EditableImage()
<span class="nc" id="L183">      {</span>
        // By default put empty objects for which isAvailable() returns false
<span class="nc" id="L185">        metadataMapByClass.put(TIFF.class, List.of(new TIFF()));</span>
<span class="nc" id="L186">        metadataMapByClass.put(EXIF.class, List.of(new EXIF()));</span>
<span class="nc" id="L187">        metadataMapByClass.put(IPTC.class, List.of(new IPTC()));</span>
<span class="nc" id="L188">        metadataMapByClass.put(XMP.class, List.of(new XMP()));</span>
<span class="nc" id="L189">        metadataMapByClass.put(MakerNote.class, List.of(new MakerNote()));</span>
<span class="nc" id="L190">      }</span>

    /*******************************************************************************************************************
     *
     * For inner implementation only. Do not use.
     *
     ******************************************************************************************************************/
    public EditableImage (final ImageModel imageModel) // FIXME: try to make it protected
<span class="fc" id="L198">      {</span>
        // null imageModel is accepted for instances carrying only metadata
<span class="fc" id="L200">        imageModelHolder = ImageModelHolder.wrap(imageModel);</span>
<span class="fc" id="L201">      }</span>

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public void setNickName (final @Nonnull String nickName)
      {
<span class="nc bnc" id="L209" title="All 2 branches missed.">        if (imageModelHolder != null)</span>
          {
<span class="nc" id="L211">            imageModelHolder.setNickName(nickName);</span>
          }
<span class="nc" id="L213">      }</span>

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    @Nonnull
    public Optional&lt;String&gt; getNickName()
      {
<span class="nc bnc" id="L222" title="All 2 branches missed.">        return (imageModelHolder != null) ? Optional.ofNullable(imageModelHolder.getNickName()) : Optional.empty();</span>
      }

    /*******************************************************************************************************************
     *
     * Creates a new EditableImage as specified by the parameter
     *
     * @param   createOp  the way the image should be created
     * @return the image
     *
     ******************************************************************************************************************/
    @Nonnull
    public static EditableImage create (@Nonnull final AbstractCreateOp createOp)
      {
<span class="fc" id="L236">        final var editableImage = new EditableImage(null);</span>
<span class="fc" id="L237">        final var image = editableImage.internalExecute(createOp);</span>
<span class="fc" id="L238">        final var imageModel = ImplementationFactoryRegistry.getDefault().createImageModel(image);</span>
<span class="fc" id="L239">        editableImage.imageModelHolder = ImageModelHolder.wrap(imageModel);</span>

<span class="fc" id="L241">        return editableImage;</span>
      }

    /*******************************************************************************************************************
     *
     * Reads a new EditableImage as specified by the parameter
     *
     * @param   readOp    the way the image should be read
     * @return the image
     *
     ******************************************************************************************************************/
    // FIXME: merge with create(AbstractCreateOp), introduce ReadJ2DOp
merge with create(AbstractCreateOp), introduce ReadJ2DOp
@Nonnull public static EditableImage create (@Nonnull final ReadOp readOp) throws IOException { <span class="fc" id="L257"> return readOp.execute();</span> } /******************************************************************************************************************* * * Returns true if the image has a raster (EditableImages can be loaded with * metadata only). * * @return true if the image has a raster * ******************************************************************************************************************/ public boolean hasRaster() { <span class="nc bnc" id="L270" title="All 2 branches missed."> return imageModelHolder.get() != null;</span> } /******************************************************************************************************************* * * DO NOT USE THIS. This method is only used by the module implementation. * ******************************************************************************************************************/ public ImageModel getImageModel() { <span class="nc" id="L280"> return imageModelHolder.get();</span> } private static boolean availableExtensionsLogged; /******************************************************************************************************************* * * Returns all the file extensions of file formats that can be read into an * EditableImage. The &lt;code&gt;ImageIO&lt;/code&gt; registry is called to retrieve the * requested information. * * @return an array of all file extensions * ******************************************************************************************************************/ @Nonnull public static Collection&lt;String&gt; getAvailableExtensions() { final boolean logExtensions; <span class="nc" id="L299"> synchronized (EditableImage.class)</span> { <span class="nc bnc" id="L301" title="All 2 branches missed."> logExtensions = !availableExtensionsLogged;</span> <span class="nc" id="L302"> availableExtensionsLogged = true;</span> <span class="nc" id="L303"> }</span> <span class="nc bnc" id="L305" title="All 2 branches missed."> if (logExtensions)</span> { <span class="nc" id="L307"> log.info(&quot;getAvailableExtensions()&quot;);</span> } <span class="nc" id="L310"> final Set&lt;String&gt; suffixList = new TreeSet&lt;&gt;();</span> <span class="nc bnc" id="L312" title="All 2 branches missed."> for (final var formatName : ImageIO.getReaderFormatNames())</span> { <span class="nc bnc" id="L314" title="All 2 branches missed."> for (final var i = ImageIO.getImageReadersByFormatName(formatName); i.hasNext(); )</span> { <span class="nc" id="L316"> final var imageReader = i.next();</span> <span class="nc" id="L317"> final var originatingProvider = imageReader.getOriginatingProvider();</span> <span class="nc" id="L318"> final var suffixes = originatingProvider.getFileSuffixes();</span> <span class="nc" id="L319"> final var suffixesAsList = Arrays.asList(suffixes);</span> <span class="nc" id="L320"> suffixList.addAll(suffixesAsList);</span> <span class="nc bnc" id="L322" title="All 2 branches missed."> if (logExtensions)</span> { <span class="nc" id="L324"> log.info(&quot;&gt;&gt;&gt;&gt; reader - format name: {} provider: {} supports {}&quot;,</span> <span class="nc" id="L325"> formatName, originatingProvider.getPluginClassName(), suffixesAsList);</span> } <span class="nc" id="L327"> }</span> } <span class="nc bnc" id="L330" title="All 2 branches missed."> if (logExtensions)</span> { <span class="nc" id="L332"> log.info(&quot;&gt;&gt;&gt;&gt; returning {}&quot;, suffixList);</span> } <span class="nc" id="L335"> return suffixList;</span> } /******************************************************************************************************************* * * ******************************************************************************************************************/ public &lt;T extends Directory&gt; Optional&lt;T&gt; getMetadata (final @Nonnull Class&lt;T&gt; metadataClass) { <span class="fc" id="L344"> return getMetadata(metadataClass, 0);</span> } /******************************************************************************************************************* * * Retrieve a metadata directory. * * @param metadataClass the type of the directory * @param index the index (in case of multiple items) * @return the metadata directory * ******************************************************************************************************************/ public &lt;T extends Directory&gt; Optional&lt;T&gt; getMetadata (final @Nonnull Class&lt;T&gt; metadataClass, final @Nonnegative int index) { <span class="fc" id="L358"> final var list = (List&lt;T&gt;)metadataMapByClass.get(metadataClass);</span> <span class="pc bpc" id="L359" title="1 of 2 branches missed."> return Optional.ofNullable(list).flatMap(l -&gt; l.isEmpty() ? Optional.empty() : Optional.of(l.get(index)));</span> } /******************************************************************************************************************* * * ******************************************************************************************************************/ @Nonnegative public int getMetadataCount (final @Nonnull Class&lt;?&gt; metadataClass) { <span class="fc" id="L369"> return Optional.ofNullable(metadataMapByClass.get(metadataClass)).map(List::size).orElse(0);</span> } /******************************************************************************************************************* * * Returns the width of this image. * * @return the width * ******************************************************************************************************************/ @Nonnegative public int getWidth() { <span class="fc" id="L382"> return imageModelHolder.get().getWidth();</span> } /******************************************************************************************************************* * * Returns the height of this image. * * @return the height * ******************************************************************************************************************/ @Nonnegative public int getHeight() { <span class="fc" id="L395"> return imageModelHolder.get().getHeight();</span> } /******************************************************************************************************************* * * Returns the dataType used by this image. * * @return the data type * ******************************************************************************************************************/ @Nonnull public DataType getDataType() { <span class="fc" id="L408"> return imageModelHolder.get().getDataType();</span> } /******************************************************************************************************************* * * Returns the number of bands this EditableImage is composed of. * * @return the band count * ******************************************************************************************************************/ @Nonnegative public int getBandCount() { <span class="fc" id="L421"> return imageModelHolder.get().getBandCount();</span> } /******************************************************************************************************************* * * Returns the number of sample bits for each band this EditableImage is * composed of. * * @return the number of bits * ******************************************************************************************************************/ @Nonnegative public int getBitsPerBand() { <span class="fc" id="L435"> return getDataType().getSize();</span> } /******************************************************************************************************************* * * Returns the number of sample bits for each pixel this EditableImage is * composed of. * * @return the number of bits * ******************************************************************************************************************/ @Nonnegative public int getBitsPerPixel() { <span class="fc" id="L449"> return getBandCount() * getBitsPerBand();</span> } /******************************************************************************************************************* * * Executes an operation. The original image is lost and replaced by results. * * @param operation the operation to perform * @return the operation (as a convenience in case it carries results) * ******************************************************************************************************************/ @Nonnull public &lt;T extends Operation&gt; T executeInPlace (@Nonnull final T operation) { <span class="fc" id="L463"> final var time = Instant.now();</span> <span class="fc" id="L464"> final var image = internalExecute(operation);</span> <span class="fc" id="L465"> imageModelHolder.get().setImage(image);</span> <span class="fc" id="L466"> latestOperationDuration = Duration.between(time, Instant.now());</span> <span class="fc" id="L468"> return operation;</span> } /******************************************************************************************************************* * * Executes an operation. The original image is untouched as the results are placed in a brand-new instance of * EditableImage. * * @param operation the operation to perform * @return the result * ******************************************************************************************************************/ @Nonnull public EditableImage execute (@Nonnull final Operation operation) { try { <span class="fc" id="L485"> final var time = Instant.now();</span> <span class="fc" id="L486"> final var image = internalExecute(operation);</span> <span class="fc" id="L487"> final var modelClass = imageModelHolder.get().getClass();</span> <span class="fc" id="L488"> final var constructor = modelClass.getConstructor(Object.class);</span> <span class="fc" id="L489"> final var newModel = constructor.newInstance(image);</span> <span class="fc" id="L490"> final var result = new EditableImage(newModel);</span> <span class="fc" id="L491"> result.attributeMapByName = attributeMapByName; // immutable</span> <span class="fc" id="L492"> result.latestOperationDuration = Duration.between(time, Instant.now());;</span> <span class="fc" id="L494"> return result;</span> } <span class="fc" id="L496"> catch (Exception e)</span> { <span class="fc" id="L498"> throw new RuntimeException(e);</span> } } /******************************************************************************************************************* * * Returns the elapsed time of the latest operation performed. Note that for * execute2() this value is available on the result. When an image is * deserialized, this method returns the serialization time (this relies upon * the fact that the clocks on all network nodes are synchronized). * * @return the latest operation elapsed time * ******************************************************************************************************************/ @Nonnull public Duration getLatestOperationDuration() { <span class="nc" id="L515"> return latestOperationDuration;</span> } /******************************************************************************************************************* * * Creates a similar image, that is a blank image with the same characteristics * of this image (width, height, data type, color model). * @deprecated will be merged with create(AbstractCreateOp) * * @return a new, similar image * ******************************************************************************************************************/ @Nonnull public EditableImage createSimilarImage() { <span class="nc" id="L530"> final var imageCopy = imageModelHolder.get().createCopy(false);</span> <span class="nc" id="L531"> imageCopy.attributeMapByName = attributeMapByName; // immutable</span> <span class="nc" id="L532"> return imageCopy;</span> } /******************************************************************************************************************* * * Clones this image. * ******************************************************************************************************************/ @Nonnull public EditableImage cloneImage() { <span class="nc" id="L543"> final var imageCopy = imageModelHolder.get().createCopy(true);</span> <span class="nc" id="L544"> imageCopy.attributeMapByName = attributeMapByName; // immutable</span> <span class="nc" id="L545"> return imageCopy;</span> } /******************************************************************************************************************* * * Creates a resized image. - FIXME should be removed * @deprecated * ******************************************************************************************************************/ @Nonnull public EditableImage createResizedImage (final @Nonnegative int width, final @Nonnegative int height) { <span class="nc" id="L557"> return createResizedImage(width, height, Quality.FASTEST);</span> } /******************************************************************************************************************* * * Creates a resized image. - FIXME move to a factory method for ScaleOp. * @deprecated * ******************************************************************************************************************/ @Nonnull public EditableImage createResizedImage (final @Nonnegative int width, final @Nonnegative int height, final @Nonnull Quality quality) { <span class="nc" id="L571"> final var hScale = (double)width / (double)getWidth();</span> <span class="nc" id="L572"> final var vScale = (double)height / (double)getHeight();</span> <span class="nc" id="L573"> final var scaleOp = new ScaleOp(hScale, vScale, quality);</span> <span class="nc" id="L574"> executeInPlace(scaleOp);</span> <span class="nc" id="L576"> return this;</span> } /******************************************************************************************************************* * * Sets an attribute of this image. Attributes are user-specific name-value pairs. * * @param key the attribute name * @param value the attribute value * ******************************************************************************************************************/ public &lt;T&gt; void setAttribute (final @Nonnull Key&lt;T&gt; key, final @Nonnull T value) { <span class="fc" id="L589"> attributeMapByName = attributeMapByName.with(key, value);</span> <span class="fc" id="L590"> }</span> /******************************************************************************************************************* * * Returns an attribute of this image. * * @param key the attribute name * @return the attribute value * ******************************************************************************************************************/ @Nonnull public &lt;T&gt; Optional&lt;T&gt; getAttribute (final @Nonnull Key&lt;T&gt; key) { <span class="fc" id="L603"> return attributeMapByName.getOptional(key);</span> } /******************************************************************************************************************* * * ******************************************************************************************************************/ public void setAttributes (final @Nonnull Map&lt;Key&lt;?&gt;, Object&gt; attributes) { <span class="nc" id="L612"> attributeMapByName = TypeSafeMap.ofCloned(attributes);</span> <span class="nc" id="L613"> }</span> /******************************************************************************************************************* * * ******************************************************************************************************************/ @Nonnull public TypeSafeMap getAttributes() { <span class="nc" id="L622"> return attributeMapByName; // immutable</span> } /******************************************************************************************************************* * * Removes an attribute from this image. * * @param name the attribute name * @return the attribute value * ******************************************************************************************************************/ @Nonnull public &lt;T&gt; T removeAttribute (final @Nonnull String name) { <span class="nc" id="L636"> throw new UnsupportedOperationException(); // FIXME: need to add TypeSafeMap.without()</span> } /******************************************************************************************************************* * * Removes all the resources bound to this image. * ******************************************************************************************************************/ public void dispose() { <span class="nc" id="L646"> imageModelHolder.get().dispose();</span> <span class="nc" id="L647"> imageModelHolder = null;</span> <span class="nc" id="L648"> attributeMapByName = TypeSafeMap.newInstance();</span> <span class="nc" id="L649"> }</span> /******************************************************************************************************************* * * Returns an estimate of the memory allocated by this image. * * @return the memory allocated for this image * ******************************************************************************************************************/ @Nonnegative public long getMemorySize() { <span class="nc" id="L661"> final var imageModel = imageModelHolder.get();</span> <span class="nc bnc" id="L662" title="All 2 branches missed."> return (imageModel != null) ? imageModel.getMemorySize() : 0;</span> } /******************************************************************************************************************* * * Returns the ColorModel of this image. * * @return the color model * ******************************************************************************************************************/ @Nonnegative public ColorModel getColorModel() // FIXME: to be removed { <span class="nc" id="L675"> return imageModelHolder.get().getColorModel();</span> } /******************************************************************************************************************* * * Returns the ICC_Profile of this image (null will be returned if the ColorModel is not ICC-based). &lt;i&gt;Note that * this is the profile of the image as it is optimized for the display, which is almost surely sRGB; and &lt;b&gt;it's * probably different than the original image profile&lt;/b&gt;&lt;/i&gt;. * * @return the color profile * ******************************************************************************************************************/ @Nonnegative public ICC_Profile getICCProfile() // FIXME: to be removed { <span class="nc" id="L690"> final var colorModel = getColorModel();</span> <span class="nc bnc" id="L692" title="All 2 branches missed."> if (colorModel != null)</span> { <span class="nc" id="L694"> final var colorSpace = colorModel.getColorSpace();</span> <span class="nc bnc" id="L696" title="All 2 branches missed."> if (colorSpace instanceof ICC_ColorSpace)</span> { <span class="nc" id="L698"> final var iccColorSpace = (ICC_ColorSpace)colorSpace;</span> <span class="nc" id="L699"> return iccColorSpace.getProfile();</span> } } <span class="nc" id="L703"> return null;</span> } /******************************************************************************************************************* * * ******************************************************************************************************************/ /* private final static int COMPRESSED = 1; public void writeExternal (ObjectOutput out) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); // GZIPOutputStream gos = new GZIPOutputStream(baos); DataOutputStream dos = new DataOutputStream(baos); dos.writeUTF(imageModel.getClass().getName()); imageModel.writeExternal(dos); dos.flush(); dos.close(); byte[] buffer = baos.toByteArray(); out.writeLong(System.currentTimeMillis()); out.writeInt(0); out.writeInt(buffer.length); out.write(buffer); out.flush(); }*/ /******************************************************************************************************************* * * ******************************************************************************************************************/ /* public void readExternal (ObjectInput in) throws IOException, ClassNotFoundException { long serializationTimeStamp = in.readLong(); latestOperationTime = System.currentTimeMillis() - serializationTimeStamp; int mode = in.readInt(); int bufferSize = in.readInt(); byte[] buffer = new byte[bufferSize]; in.readFully(buffer); InputStream is = new ByteArrayInputStream(buffer); if (mode == COMPRESSED) { is = new GZIPInputStream(is); } DataInputStream dis = new DataInputStream(is); String className = dis.readUTF(); Class clazz = Class.forName(className); try { imageModel = (ImageModel)clazz.newInstance(); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } imageModel.readExternal(dis); dis.close(); latestSerializationSize = bufferSize; }*/ /******************************************************************************************************************* * * ******************************************************************************************************************/ @Nonnull public long getLatestSerializationSize() { <span class="nc" id="L783"> return latestSerializationSize;</span> } /******************************************************************************************************************* * * DO NOT USE. This is only for implementation and testing purposes. * ******************************************************************************************************************/ @Nonnull public &lt;T&gt; T getInnerProperty (final @Nonnull Class&lt;T&gt; propertyClass) { <span class="fc bfc" id="L794" title="All 2 branches covered."> if (AccessorOp.class.equals(propertyClass))</span> { <span class="fc" id="L796"> return propertyClass.cast(accessor);</span> } <span class="pc bpc" id="L799" title="1 of 2 branches missed."> if (IIOMetadata.class.equals(propertyClass))</span> { <span class="nc" id="L801"> return propertyClass.cast(iioMetadata);</span> } <span class="fc" id="L804"> return imageModelHolder.get().getInnerProperty(propertyClass);</span> } /******************************************************************************************************************* * * Executes an operation and return the raw result (the object to be wrapped by the ImageModel). * * @param operation the operation to perform * @return the result (the object wrapped by the ImageModel) * ******************************************************************************************************************/ @Nonnull private Object internalExecute (final @Nonnull Operation operation) throws UnsupportedOperationException { <span class="fc" id="L819"> final var implementationFactoryRegistry = ImplementationFactoryRegistry.getDefault();</span> <span class="fc" id="L820"> final var imageModel = imageModelHolder.get();</span> <span class="fc bfc" id="L821" title="All 2 branches covered."> var image = (imageModel != null) ? imageModel.getImage() : null;</span> <span class="fc" id="L822"> OperationImplementation implementation = null;</span> <span class="pc bpc" id="L824" title="1 of 4 branches missed."> if ((image == null) &amp;&amp; !(operation instanceof AbstractCreateOp))</span> { <span class="nc" id="L826"> throw new RuntimeException(&quot;null image with an Op different that AbstractCreateOp&quot;);</span> } try { <span class="fc" id="L831"> implementation = implementationFactoryRegistry.findImplementation(operation, imageModelHolder.get(), false);</span> } <span class="fc" id="L833"> catch (UnsupportedOperationException e)</span> { <span class="fc" id="L835"> log.warn(&quot;No default implementation of {} for model: {}&quot;, operation, image);</span> <span class="nc" id="L836"> implementation = implementationFactoryRegistry.findImplementation(operation, imageModelHolder.get(), true);</span> <span class="nc" id="L837"> log.info(&quot;Found alternate implementation: {}&quot;, implementation);</span> <span class="nc bnc" id="L839" title="All 2 branches missed."> if (!(operation instanceof AbstractCreateOp))</span> { <span class="nc bnc" id="L841" title="All 2 branches missed."> if (implementation.getFactory().canConvertFrom(image.getClass()))</span> { <span class="nc" id="L843"> log.info(&quot;&gt;&gt;&gt;&gt; CONVERT FROM using {}&quot;, implementation.getFactory());</span> <span class="nc" id="L844"> imageModelHolder = ImageModelHolder.wrap(implementation.getFactory().convertFrom(image));</span> <span class="nc" id="L845"> image = imageModelHolder.get().getImage();</span> } <span class="nc bnc" id="L848" title="All 2 branches missed."> else if (imageModelHolder.get().getFactory().canConvertTo(implementation.getFactory().getModelClass()))</span> { <span class="nc" id="L850"> log.info(&quot;&gt;&gt;&gt;&gt; CONVERT TO {}&quot;, implementation.getFactory().getModelClass());</span> <span class="nc" id="L851"> image = imageModelHolder.get().getFactory().convertTo(implementation.getFactory().getModelClass());</span> } else { <span class="nc" id="L856"> throw new RuntimeException(&quot;Shouldn't get here&quot;);</span> } <span class="nc" id="L859"> log.info(&quot;&gt;&gt;&gt;&gt; NEW IMAGE {} NEW IMAGE MODEL {}&quot;, image, imageModelHolder);</span> } <span class="fc" id="L861"> }</span> try { <span class="fc" id="L865"> return implementation.execute(this, image);</span> } <span class="fc" id="L867"> catch (RuntimeException e)</span> { <span class="fc" id="L869"> log.error(&quot;Operation failed, offending image: {}&quot;, image);</span> <span class="fc" id="L870"> throw e;</span> } } } </pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.7.202105040129</span></div></body></html>