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
    @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
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>