Content of file EditableImageRenderer.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>EditableImageRenderer.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 Examples HistogramViewer</a> &gt; <a href="../index.html" class="el_bundle">image-renderer</a> &gt; <a href="index.source.html" class="el_package">it.tidalwave.image.render</a> &gt; <span class="el_source">EditableImageRenderer.java</span></div><h1>EditableImageRenderer.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.render;

import java.util.ArrayList;
import java.util.List;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Shape;
import java.awt.event.AdjustmentListener;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.border.Border;
import it.tidalwave.image.EditableImage;
import it.tidalwave.image.Quality;
import it.tidalwave.image.op.OptimizeOp;
import it.tidalwave.image.op.PaintOp;
import it.tidalwave.image.op.RotateOp;
import it.tidalwave.image.op.ScaleOp;
import it.tidalwave.image.render.event.EditableImageRendererEvent;
import it.tidalwave.image.render.event.EditableImageRendererListener;
import it.tidalwave.image.util.Platform;
import lombok.extern.slf4j.Slf4j;

/***********************************************************************************************************************
 *
 * This class is a pipe which adds to SimpleEditableImageRenderer scrolling
 * capabilities and a fit-to-size feature.
 *
 * @author Fabrizio Giudici
 *
 **********************************************************************************************************************/
<span class="nc" id="L61">@Slf4j</span>
public class EditableImageRenderer extends JComponent
  {
    /**
     * The maximum allowed value for scale.
     */
    public static final double MAX_SCALE = 40;

    /**
     * The maximum allowed value for scale.
     */
    public static final double MIN_SCALE = 0.01;

    /**
     * Over this image size the scaled image caching is always disabled.
     */
    private static final int MAX_SIZE_FOR_SCALED_CACHING = 8000;

    /**
     * The default background color for parts not covered by the image.
     */
<span class="nc" id="L82">    private static final Color DEFAULT_BACKGROUND = Color.DARK_GRAY;</span>

    /**
     * An empty margin.
     */
<span class="nc" id="L87">    private static final Insets NULL_MARGIN = new Insets(0, 0, 0, 0);</span>

    /**
     * The original image to be displayed.
     */
    protected EditableImage image;

    /**
     * A display-optimized copy of the image. If optimizedImageEnabled is false, it just references the original image.
     */
    private EditableImage optimizedImage;

    /**
     * A scaled-down version of the image to fit the actual rendering settings.
     */
    private EditableImage scaledImage;

    /**
     * True if a local optimized copy of the image should be used.
     */
    private boolean optimizedImageEnabled;

    /**
     * True if use a scaled local copy of the image for faster rendering.
     */
    private boolean scaledImageCachingEnabled;

    /**
     * The current scale.
     */
<span class="nc" id="L117">    protected double scale = 1;</span>

<span class="nc" id="L119">    private double minScale = MIN_SCALE;</span>

<span class="nc" id="L121">    private double maxScale = MAX_SCALE;</span>

    /**
     * The current rotation.
     */
<span class="nc" id="L126">    protected double angle = 0;</span>

    /**
     * The image coordinates of the pixel shown in the top left corner of the component.
     */
<span class="nc" id="L131">    private Point origin = new Point(0, 0);</span>

    /**
     * The current preview settings.
     */
    private PreviewSettings previewSettings;

    /**
     * The coordinates of the photo origin relative to the component location.
     */
    private int shownImageX;

    /**
     * The coordinates of the photo origin relative to the component location.
     */
    private int shownImageY;

    /**
     * The scaled photo dimension in pixels.
     */
    private int shownImageWidth;

    /**
     * The scaled photo dimension in pixels.
     */
    private int shownImageHeight;

    /**
     * The maximum margin that can be shown around the image.
     */
<span class="nc" id="L161">    private Insets margin = new Insets(0, 0, 0, 0);</span>

    /**
     * If not null, the image rendering will be clipped against this shape.
     */
    private Shape clippingShape;

    /**
     * The quality used for scale.
     */
<span class="nc" id="L171">    private Quality scaleQuality = Quality.INTERMEDIATE;</span>

    /**
     * The quality used for rotate.
     */
<span class="nc" id="L176">    private Quality rotateQuality = Quality.INTERMEDIATE;</span>

    /**
     * Overlays will be drawn over the image.
     */
<span class="nc" id="L181">    private final List&lt;Overlay&gt; overlayList = new ArrayList&lt;&gt;();</span>

    /**
     * If true, the image always fits the component size.
     */
    private boolean fitToDisplaySize;

    /**
     * The list of listeners.
     */
<span class="nc" id="L191">    private final List&lt;EditableImageRendererListener&gt; listenerList = new ArrayList&lt;&gt;();</span>

    /**
     * True if repaint is currently enabled.
     */
<span class="nc" id="L196">    private boolean repaintEnabled = true;</span>

    /**
     * The current EditingTool.
     */
    protected EditingTool editingTool;

    /**
     * The vertical scrollbar.
     */
<span class="nc" id="L206">    private final JScrollBar horizontalScrollBar = new JScrollBar(JScrollBar.HORIZONTAL);</span>

    /**
     * The horizontal scrollbar.
     */
<span class="nc" id="L211">    private final JScrollBar verticalScrollBar = new JScrollBar(JScrollBar.VERTICAL);</span>

<span class="nc" id="L213">    private final JPanel filler = new JPanel();</span>

    /**
     * True if scrollbars should be visible.
     */
<span class="nc" id="L218">    private boolean scrollBarsVisible = false;</span>

    /**
     * A border to be rendered around the image.
     */
    private Border imageBorder;

    /**
     * Width of the renderer before the latest resize, used to layout scrollbars.
     */
    private int previousWidth;

    /**
     * Height of the renderer before the latest resize, used to layout scrollbars.
     */
    private int previohsHeight;

    /**
     * The thickness of scrollbars.
     */
<span class="nc" id="L238">    private int scrollbarThickness = 16;</span>

    /*******************************************************************************************************************
     *
     * The scrollbar listener. 
     *
     ******************************************************************************************************************/
<span class="nc" id="L245">    private final AdjustmentListener scrollbarListener =</span>
<span class="nc" id="L246">            event -&gt; setOrigin(new Point(horizontalScrollBar.getValue(), verticalScrollBar.getValue()));</span>

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public EditableImageRenderer()
<span class="nc" id="L253">      {</span>
<span class="nc" id="L254">        setBackground(DEFAULT_BACKGROUND);</span>
<span class="nc" id="L255">        setLayout(null);</span>
<span class="nc" id="L256">        setOpaque(false);</span>

<span class="nc bnc" id="L258" title="All 2 branches missed.">        final var workaroundMST63 = !Platform.isMacOSX();</span>

<span class="nc bnc" id="L260" title="All 2 branches missed.">        if (workaroundMST63)</span>
          {
<span class="nc" id="L262">            log.warn(&quot;Enabled workaround for MST-63&quot;);</span>
          }

<span class="nc" id="L265">        setScaledImageCachingEnabled(workaroundMST63);</span>
<span class="nc" id="L266">        setOptimizedImageEnabled(workaroundMST63);</span>

<span class="nc" id="L268">        add(horizontalScrollBar);</span>
<span class="nc" id="L269">        add(verticalScrollBar);</span>
<span class="nc" id="L270">        add(filler);</span>
<span class="nc" id="L271">        horizontalScrollBar.addAdjustmentListener(scrollbarListener);</span>
<span class="nc" id="L272">        verticalScrollBar.addAdjustmentListener(scrollbarListener);</span>
<span class="nc" id="L273">        horizontalScrollBar.setVisible(scrollBarsVisible);</span>
<span class="nc" id="L274">        verticalScrollBar.setVisible(scrollBarsVisible);</span>
<span class="nc" id="L275">        filler.setVisible(scrollBarsVisible);</span>
<span class="nc" id="L276">      }</span>

    /*******************************************************************************************************************
     *
     * Sets the image to display. The image is internally cloned, so any further
     * operation performed on the same source (that could cause a model switch) won't
     * affect the rendering.
     *
     * @param  image  the image
     *
     ******************************************************************************************************************/
    public void setImage (final EditableImage image)
      {
<span class="nc" id="L289">        log.info(&quot;setImage(&quot; + image + &quot;)&quot;);</span>

<span class="nc bnc" id="L291" title="All 2 branches missed.">        if (image == null)</span>
          {
<span class="nc" id="L293">            this.image = null;</span>
<span class="nc" id="L294">            log.warn(&quot;setImage(null)&quot;);</span>
          }

        else
          {
<span class="nc" id="L299">            this.image = image; // image.cloneImage();</span>
          }

<span class="nc bnc" id="L302" title="All 2 branches missed.">        if (editingTool != null)</span>
          {
<span class="nc" id="L304">            editingTool.imageChanged();</span>
          }

<span class="nc" id="L307">        flushAllCaches();</span>
<span class="nc" id="L308">        updateScrollBars();</span>

<span class="nc bnc" id="L310" title="All 2 branches missed.">        if (fitToDisplaySize)</span>
          {
<span class="nc" id="L312">            fitToDisplaySize();</span>
          }
        else
          {
<span class="nc" id="L316">            repaint();</span>
          }
<span class="nc" id="L318">      }</span>

    /*******************************************************************************************************************
     *
     * Returns the displayed image.
     *
     * @return the image
     *
     ******************************************************************************************************************/
    public EditableImage getImage()
      {
<span class="nc" id="L329">        return image;</span>
      }

    /*******************************************************************************************************************
     *
     * Returns a possibly optimized version of the image. If useOptimizedImage is
     * false, this method returns the original image.
     *
     * @return the image
     *
     ******************************************************************************************************************/

    // useful for the loupe, to prevent it from recomputing the optimized version
    public EditableImage getOptimizedImage()
      {
<span class="nc" id="L344">        return optimizedImage;</span>
      }

    /*******************************************************************************************************************
     *
     * Turns on/off repaint. It's advisable to turn repainting off before a sequence
     * of operations, and turning it on again only at the end of the sequence.
     *
     * @param  repaintEnabled  the new setting
     *
     ******************************************************************************************************************/
    public void setRepaintEnabled (final boolean repaintEnabled)
      {
<span class="nc" id="L357">        log.info(&quot;setRepaintEnabled(&quot; + repaintEnabled + &quot;)&quot;);</span>
<span class="nc" id="L358">        this.repaintEnabled = repaintEnabled;</span>
<span class="nc" id="L359">      }</span>

    /*******************************************************************************************************************
     *
     * Returns the state of repaint.
     *
     * @return the repaint state
     *
     ******************************************************************************************************************/
    public boolean isRepaintEnabled()
      {
<span class="nc" id="L370">        return repaintEnabled;</span>
      }

    /*******************************************************************************************************************
     *
     * Sets the image point which is displayed in the top left corner (coordinates
     * are in actual image pixels).
     *
     * @param    origin  the origin
     *
     ******************************************************************************************************************/
    public void setOrigin (final Point origin)
      {
<span class="nc" id="L383">        log.info(&quot;setOrigin(&quot; + origin + &quot;)&quot;);</span>

<span class="nc bnc" id="L385" title="All 6 branches missed.">        if ((image != null) &amp;&amp; (image.getWidth() &gt; 0) &amp;&amp; (image.getHeight() &gt; 0))</span>
          {
<span class="nc" id="L387">            internalSetOrigin(origin);</span>
<span class="nc" id="L388">            updateScrollBars();</span>
<span class="nc" id="L389">            repaint();</span>
          }
<span class="nc" id="L391">      }</span>

    private void internalSetOrigin (final Point origin)
      {
<span class="nc" id="L395">        log.info(&quot;internalSetOrigin(&quot; + origin + &quot;)&quot;);</span>
        //
        // No margin with the scroll bars.
        //
//        final Insets margin = scrollBarsVisible ? NULL_MARGIN : this.margin;
        //
        // The size of the largest image displayable with no clipping at the current zoom.
        //
<span class="nc" id="L403">        final var maxWidth = (int)Math.round(getAvailableWidth() / scale);</span>
<span class="nc" id="L404">        final var maxHeight = (int)Math.round(getAvailableHeight() / scale);</span>

        //
        // The size of the image including the margin.
        //
<span class="nc" id="L409">        final var widthWithMargin = image.getWidth() + margin.left + margin.right;</span>
<span class="nc" id="L410">        final var heightWithMargin = image.getHeight() + margin.top + margin.bottom;</span>

        //
        // The bounds for the origin to keep the margin within its bounds.
        //
<span class="nc" id="L415">        final var xMin = -margin.left;</span>
<span class="nc" id="L416">        final var yMin = -margin.top;</span>
<span class="nc" id="L417">        final var xMax = (image.getWidth() + margin.right) - maxWidth;</span>
<span class="nc" id="L418">        final var yMax = (image.getHeight() + margin.bottom) - maxHeight;</span>
        //
        // If there's more room to display the image with its margin, center it.
        //
<span class="nc bnc" id="L422" title="All 2 branches missed.">        this.origin.x = (maxWidth &lt;= widthWithMargin)</span>
<span class="nc" id="L423">                        ? Math.min(Math.max(xMin, origin.x), xMax)</span>
<span class="nc" id="L424">                        : (-(maxWidth - image.getWidth()) / 2);</span>
<span class="nc bnc" id="L425" title="All 2 branches missed.">        this.origin.y = (maxHeight &lt;= heightWithMargin)</span>
<span class="nc" id="L426">                        ? Math.min(Math.max(yMin, origin.y), yMax)</span>
<span class="nc" id="L427">                        : (-(maxHeight - image.getHeight()) / 2);</span>
<span class="nc" id="L428">      }</span>

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public Point getOrigin()
      {
<span class="nc" id="L436">        return origin;</span>
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public double getScale()
      {
<span class="nc" id="L445">        return scale;</span>
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public void setAngle (final double angle)
      {
<span class="nc" id="L454">        log.info(&quot;setAngle(&quot; + angle + &quot;)&quot;);</span>

<span class="nc bnc" id="L456" title="All 2 branches missed.">        if (this.angle != angle)</span>
          {
<span class="nc" id="L458">            this.angle = angle;</span>
<span class="nc" id="L459">            flushScaledImageCache();</span>
<span class="nc" id="L460">            repaint();</span>
<span class="nc" id="L461">            fireAngleChangedEvent();</span>
          }
<span class="nc" id="L463">      }</span>

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public double getAngle()
      {
<span class="nc" id="L471">        return angle;</span>
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public EditingTool getEditingTool()
      {
<span class="nc" id="L480">        return editingTool;</span>
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public void setImageBorder (final Border imageBorder)
      {
<span class="nc" id="L489">        this.imageBorder = imageBorder;</span>
<span class="nc" id="L490">      }</span>

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public Border getImageBorder()
      {
<span class="nc" id="L498">        return imageBorder;</span>
      }

    /*******************************************************************************************************************
     *
     * Given a point in component coordinates, returns the coordinates of the
     * image pixel rendered at that point. If the point is outside of the image
     * rendering areas, returns null.
     *
     * @param  componentPoint  the point in relative coordinates
     * @return the image pixel coordinates (null if none)
     *
     ******************************************************************************************************************/
    public Point getPositionOverImage (final Point componentPoint)
      {
<span class="nc bnc" id="L513" title="All 2 branches missed.">        if (image == null)</span>
          {
<span class="nc" id="L515">            return null;</span>
          }

<span class="nc" id="L518">        final var imageWidth = image.getWidth();</span>
<span class="nc" id="L519">        final var imageHeight = image.getHeight();</span>

<span class="nc bnc" id="L521" title="All 4 branches missed.">        if ((imageWidth == 0) || (imageHeight == 0)) // can happen if metadata is not loaded yet</span>
          {
<span class="nc" id="L523">            return null;</span>
          }

<span class="nc bnc" id="L526" title="All 4 branches missed.">        if ((shownImageWidth == 0) || (shownImageHeight == 0))</span>
          {
<span class="nc" id="L528">            log.error(&quot;Image size: &quot; + shownImageHeight + &quot; x &quot; + shownImageHeight);</span>
<span class="nc" id="L529">            return null;</span>
          }

<span class="nc" id="L532">        final var x = ((componentPoint.x - shownImageX) * imageWidth) / shownImageWidth;</span>
<span class="nc" id="L533">        final var y = ((componentPoint.y - shownImageY) * imageHeight) / shownImageHeight;</span>

<span class="nc bnc" id="L535" title="All 8 branches missed.">        if ((x &gt;= 0) &amp;&amp; (y &gt;= 0) &amp;&amp; (x &lt; imageWidth) &amp;&amp; (y &lt; imageHeight))</span>
          {
<span class="nc" id="L537">            return new Point(x, y);</span>
          }

        else
          {
<span class="nc" id="L542">            return null;</span>
          }
      }

    /*******************************************************************************************************************
     *
     * Given a point in image coordinates, returns the coordinates of the
     * component point which renders that image point.
     *
     * @param  imagePoint  the point in image coordinates
     * @return the point coordinates
     *
     ******************************************************************************************************************/
    public Point convertImagePointToComponentPoint (final Point imagePoint)
      {
<span class="nc bnc" id="L557" title="All 2 branches missed.">        if (image == null)</span>
          {
<span class="nc" id="L559">            return null;</span>
          }

<span class="nc" id="L562">        final var imageWidth = image.getWidth();</span>
<span class="nc" id="L563">        final var imageHeight = image.getHeight();</span>

<span class="nc bnc" id="L565" title="All 4 branches missed.">        if ((imageWidth == 0) || (imageHeight == 0)) // can happen if metadata is not loaded yet</span>
          {
<span class="nc" id="L567">            return null;</span>
          }

<span class="nc bnc" id="L570" title="All 4 branches missed.">        if ((shownImageWidth == 0) || (shownImageHeight == 0))</span>
          {
<span class="nc" id="L572">            log.error(&quot;Image size: &quot; + shownImageHeight + &quot; x &quot; + shownImageHeight);</span>

<span class="nc" id="L574">            return null;</span>
          }

<span class="nc" id="L577">        final var x = ((imagePoint.x * shownImageWidth) / imageWidth) + shownImageX;</span>
<span class="nc" id="L578">        final var y = ((imagePoint.y * shownImageHeight) / imageHeight) + shownImageY;</span>

<span class="nc" id="L580">        return new Point(x, y);</span>

/*        if ((x &gt;= 0) &amp;&amp; (y &gt;= 0) &amp;&amp; (x &lt; imageWidth) &amp;&amp; (y &lt; imageHeight))
          {
            return new Point(x, y);
          }

        else
          {
            return null;
          }*/
      }

    /*******************************************************************************************************************
     *
     * Ensures that the given image pixel is shown at the given component
     * coordinates.
     *
     * @param   imagePoint     the coordinates of the image pixel
     * @param   componentPoint the relative coordinates where to show imagePoint
     *
     ******************************************************************************************************************/
    public void setPositionOverImage (final Point imagePoint, final Point componentPoint)
      {
<span class="nc" id="L604">        log.info(&quot;setPositionOverImage(&quot; + imagePoint + &quot;, &quot; + componentPoint + &quot;)&quot;);</span>
<span class="nc" id="L605">        final var newOrigin = computeOrigin(imagePoint, componentPoint, scale);</span>

<span class="nc bnc" id="L607" title="All 2 branches missed.">        if (newOrigin != null)</span>
          {
<span class="nc" id="L609">            setOrigin(newOrigin);</span>
          }
<span class="nc" id="L611">      }</span>

    /*******************************************************************************************************************
     *
     * Sets the maximum margin that can be shown around the image. The number of
     * pixels is in image scale.
     *
     * Please note that the margin is ignored when the scroll bars are visible.
     *
     * @param   margin  the new margin
     *
     ******************************************************************************************************************/
    public void setMargin (final Insets margin)
      {
<span class="nc" id="L625">        log.info(&quot;setMargin(&quot; + margin + &quot;)&quot;);</span>
<span class="nc" id="L626">        this.margin = (Insets)margin.clone();</span>
<span class="nc" id="L627">      }</span>

    /*******************************************************************************************************************
     *
     * Returns the maximum margin that can be shown around the image.
     *
     * @return the margin
     *
     ******************************************************************************************************************/
    public Insets getMargin()
      {
<span class="nc" id="L638">        return (Insets)margin.clone();</span>
      }

    /*******************************************************************************************************************
     *
     * Sets the scrollbars visible or not.
     *
     * @param  scrollBarsVisible  the new setting
     *
     ******************************************************************************************************************/
    public void setScrollBarsVisible (final boolean scrollBarsVisible)
      {
<span class="nc" id="L650">        log.info(&quot;setScrollBarsVisible(&quot; + scrollBarsVisible + &quot;)&quot;);</span>

<span class="nc bnc" id="L652" title="All 2 branches missed.">        if (this.scrollBarsVisible != scrollBarsVisible)</span>
          {
<span class="nc" id="L654">            this.scrollBarsVisible = scrollBarsVisible;</span>

<span class="nc bnc" id="L656" title="All 2 branches missed.">            if (scrollBarsVisible)</span>
              {
<span class="nc" id="L658">                previousWidth = previohsHeight = -1;</span>
<span class="nc" id="L659">                updateScrollBars();</span>
              }

<span class="nc" id="L662">            horizontalScrollBar.setVisible(scrollBarsVisible);</span>
<span class="nc" id="L663">            verticalScrollBar.setVisible(scrollBarsVisible);</span>
<span class="nc" id="L664">            filler.setVisible(scrollBarsVisible);</span>
<span class="nc" id="L665">            repaint();</span>
          }
<span class="nc" id="L667">      }</span>

    /*******************************************************************************************************************
     *
     * Return true if the scrollbars are visible.
     *
     * @return true if the scrollbars are visible
     *
     ******************************************************************************************************************/
    public boolean isScrollBarsVisible()
      {
<span class="nc" id="L678">        return scrollBarsVisible;</span>
      }

    /*******************************************************************************************************************
     *
     * Computes a new origin so that the given image point is shown at the given
     * relative coordinates.
     *
     * @param   imagePoint     the coordinates of the image pixel
     * @param   componentPoint the relative coordinates where to show imagePoint
     * @return the new origin
     *
     ******************************************************************************************************************/
    protected Point computeOrigin (final Point imagePoint, final Point componentPoint, final double scale)
      {
<span class="nc bnc" id="L693" title="All 2 branches missed.">        if (image == null)</span>
          {
<span class="nc" id="L695">            return null;</span>
          }

<span class="nc" id="L698">        final var imageWidth = image.getWidth();</span>
<span class="nc" id="L699">        final var imageHeight = image.getHeight();</span>

<span class="nc bnc" id="L701" title="All 4 branches missed.">        if ((imageWidth == 0) || (imageHeight == 0)) // can happen if metadata is not loaded yet</span>
          {
<span class="nc" id="L703">            return null;</span>
          }

<span class="nc" id="L706">        return new Point((int)Math.round(imagePoint.x - (componentPoint.x / scale)),</span>
<span class="nc" id="L707">                         (int)Math.round(imagePoint.y - (componentPoint.y / scale)));</span>
      }

    /*******************************************************************************************************************
     *
     * Sets the quality used for scale operations. This operation doesn't force a
     * &lt;code&gt;repaint()&lt;/code&gt;, so it must be explicitly invoked if you want to see
     * immediately the quality change.
     *
     * @param  quality  the quality
     *
     ******************************************************************************************************************/
    public void setScaleQuality (final Quality scaleQuality)
      {
<span class="nc" id="L721">        log.info(&quot;setScaleQuality(&quot; + scaleQuality + &quot;)&quot;);</span>

<span class="nc bnc" id="L723" title="All 2 branches missed.">        if (this.scaleQuality != scaleQuality)</span>
          {
<span class="nc" id="L725">            this.scaleQuality = scaleQuality;</span>
<span class="nc" id="L726">            flushScaledImageCache();</span>
          }
<span class="nc" id="L728">      }</span>

    /*******************************************************************************************************************
     *
     * Returns the quality used for scale operations.
     *
     * @return the quality
     *
     ******************************************************************************************************************/
    public Quality getScaleQuality()
      {
<span class="nc" id="L739">        return scaleQuality;</span>
      }

    /*******************************************************************************************************************
     *
     * Sets the quality used for rotate operations. This operation doesn't force a
     * &lt;code&gt;repaint()&lt;/code&gt;, so it must be explicitly invoked if you want to see
     * immediately the quality change.
     *
     * @param  quality  the quality
     *
     ******************************************************************************************************************/
    public void setRotateQuality (final Quality rotateQuality)
      {
<span class="nc" id="L753">        log.info(&quot;setRotateQuality(&quot; + rotateQuality + &quot;)&quot;);</span>

<span class="nc bnc" id="L755" title="All 2 branches missed.">        if (this.rotateQuality != rotateQuality)</span>
          {
<span class="nc" id="L757">            this.rotateQuality = rotateQuality;</span>
<span class="nc" id="L758">            flushScaledImageCache();</span>
          }
<span class="nc" id="L760">      }</span>

    /*******************************************************************************************************************
     *
     * Returns the quality used for scale operations.
     *
     * @return the quality
     *
     ******************************************************************************************************************/
    public Quality getRotateQuality()
      {
<span class="nc" id="L771">        return rotateQuality;</span>
      }

    /*******************************************************************************************************************
     *
     * Enables or disables the caching of a scaled image for faster speed.
     *
     * @param  cacheScaleImageEnabled  the switch for this property
     *
     ******************************************************************************************************************/
    public void setScaledImageCachingEnabled (final boolean scaledImageCachingEnabled)
      {
<span class="nc" id="L783">        log.info(&quot;setScaledImageCachingEnabled(&quot; + scaledImageCachingEnabled + &quot;)&quot;);</span>
<span class="nc" id="L784">        this.scaledImageCachingEnabled = scaledImageCachingEnabled;</span>

<span class="nc bnc" id="L786" title="All 2 branches missed.">        if (!scaledImageCachingEnabled)</span>
          {
<span class="nc" id="L788">            flushScaledImageCache();</span>
          }
<span class="nc" id="L790">      }</span>

    /*******************************************************************************************************************
     *
     * Returns the status of the caching of a scaled image for faster speed.
     *
     * @return the status of this feature
     *
     ******************************************************************************************************************/
    public boolean isScaledImageCachingEnabled()
      {
<span class="nc" id="L801">        return scaledImageCachingEnabled;</span>
      }

    /*******************************************************************************************************************
     *
     * Enables or disables the use of an optimized copy of the image.
     *
     * @param  optimizedImageEnabled  the switch for this property
     *
     ******************************************************************************************************************/
    public void setOptimizedImageEnabled (final boolean optimizedImageEnabled)
      {
<span class="nc" id="L813">        log.info(&quot;setOptimizedImageEnabled(&quot; + optimizedImageEnabled + &quot;)&quot;);</span>
<span class="nc" id="L814">        this.optimizedImageEnabled = optimizedImageEnabled;</span>
<span class="nc" id="L815">      }</span>

    /*******************************************************************************************************************
     *
     * Returns the status of the rgb image caching feature.
     *
     * @return the status of this feature
     *
     ******************************************************************************************************************/
    public boolean isOptimizedImageEnabled()
      {
<span class="nc" id="L826">        return optimizedImageEnabled;</span>
      }

    /*******************************************************************************************************************
     *
     * Sets a shape to clip rendering against.
     *
     * @param  clippingShape  the clipping shape
     *
     ******************************************************************************************************************/
    public void setClippingShape (final Shape clippingShape)
      {
<span class="nc" id="L838">        this.clippingShape = clippingShape;</span>
<span class="nc" id="L839">      }</span>

    /*******************************************************************************************************************
     *
     * Adds an overlay to be shown over the image.
     *
     * @param  overlay  the overlay
     *
     ******************************************************************************************************************/
    public void addOverlay (final Overlay overlay)
      {
<span class="nc" id="L850">        overlayList.add(overlay);</span>
<span class="nc" id="L851">      }</span>

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public void removeOverlay (final Overlay overlay)
      {
<span class="nc" id="L859">        overlayList.remove(overlay);</span>
<span class="nc" id="L860">      }</span>

    /*******************************************************************************************************************
     *
     * Sets the preview settings.
     *
     ******************************************************************************************************************/
    public void setPreviewSettings (final PreviewSettings previewSettings)
      {
<span class="nc" id="L869">        this.previewSettings = previewSettings;</span>
<span class="nc" id="L870">        repaint();</span>
<span class="nc" id="L871">      }</span>

    /*******************************************************************************************************************
     *
     * Gets the preview settings.
     *
     ******************************************************************************************************************/
    public PreviewSettings getPreviewSettings()
      {
<span class="nc" id="L880">        return previewSettings;</span>
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    @Override
    public void update (final Graphics g)
      {
<span class="nc" id="L890">        paint(g); // don't waste time on the background</span>
<span class="nc" id="L891">      }</span>

    /*******************************************************************************************************************
     *
     * Renders this component.
     *
     ******************************************************************************************************************/
    @Override
    public void paint (final Graphics g)
      {
<span class="nc" id="L901">        log.info(&quot;paint()&quot;);</span>

<span class="nc bnc" id="L903" title="All 2 branches missed.">        if (!repaintEnabled)</span>
          {
<span class="nc" id="L905">            return;</span>
          }

<span class="nc bnc" id="L908" title="All 2 branches missed.">        if (fitToDisplaySize)</span>
          {
<span class="nc" id="L910">            internalSetScale(getFitScale());</span>
<span class="nc" id="L911">            internalSetOrigin(computeCenterPoint());</span>
//            fitToDisplaySize();
          }

<span class="nc" id="L915">        layoutScrollBars();</span>

<span class="nc" id="L917">        final var myWidth = getWidth();</span>
<span class="nc" id="L918">        final var myHeight = getHeight();</span>
        //if (image == null) // FIXME: this can be optimized
this can be optimized
{ <span class="nc" id="L921"> g.setColor(getBackground());</span> <span class="nc" id="L922"> g.fillRect(0, 0, myWidth, myHeight);</span> } <span class="nc" id="L925"> Graphics2D g2 = null;</span> try { <span class="nc bnc" id="L929" title="All 2 branches missed."> if (image != null)</span> { <span class="nc" id="L931"> EditableImage imageToDraw = null;</span> <span class="nc" id="L932"> final var maxSize = scale * Math.max(image.getWidth(), image.getHeight());</span> <span class="nc" id="L933"> var needScaling = true;</span> <span class="nc" id="L934"> var rotationDeltaX = 0;</span> <span class="nc" id="L935"> var rotationDeltaY = 0;</span> // // If scaled image caching is enabled, create a scaled image and then // render it 1:1. Don't use a scaled image if it is too big! // <span class="nc bnc" id="L941" title="All 6 branches missed."> if (((scaledImageCachingEnabled &amp;&amp; (maxSize &lt; MAX_SIZE_FOR_SCALED_CACHING)) || (angle != 0)))</span> { <span class="nc bnc" id="L943" title="All 2 branches missed."> if (scaledImage == null)</span> { <span class="nc" id="L945"> log.debug(&quot;&gt;&gt;&gt;&gt; computing scaled image&quot;);</span> <span class="nc" id="L946"> scaledImage = optimizedImage.execute(new ScaleOp(scale, getScaleQuality()));</span> <span class="nc" id="L948"> final var prevWidth = scaledImage.getWidth();</span> <span class="nc" id="L949"> final var prevHeight = scaledImage.getHeight();</span> <span class="nc" id="L950"> scaledImage.executeInPlace(new RotateOp(angle, getRotateQuality()));</span> // // Rotating the image could have make it bigger (to avoid truncation). // Adjust the origin in order to compensate for it. // <span class="nc" id="L955"> rotationDeltaX = (prevWidth - scaledImage.getWidth()) / 2;</span> <span class="nc" id="L956"> rotationDeltaY = (prevHeight - scaledImage.getHeight()) / 2;</span> } <span class="nc" id="L959"> imageToDraw = scaledImage;</span> <span class="nc" id="L960"> shownImageWidth = scaledImage.getWidth();</span> <span class="nc" id="L961"> shownImageHeight = scaledImage.getHeight();</span> <span class="nc" id="L962"> needScaling = false;</span> } // // Otherwise scale the image on-the-fly. // else { <span class="nc" id="L970"> imageToDraw = optimizedImage;</span> <span class="nc" id="L971"> shownImageWidth = (int)Math.round(scale * optimizedImage.getWidth());</span> <span class="nc" id="L972"> shownImageHeight = (int)Math.round(scale * optimizedImage.getHeight());</span> } <span class="nc" id="L975"> shownImageX = -(int)Math.round(scale * origin.x) + rotationDeltaX;</span> <span class="nc" id="L976"> shownImageY = -(int)Math.round(scale * origin.y) + rotationDeltaY;</span> <span class="nc" id="L978"> g2 = (Graphics2D)g.create(); // make a copy since you're changing hints</span> <span class="nc bnc" id="L980" title="All 2 branches missed."> if (clippingShape != null)</span> { <span class="nc" id="L982"> g2.clip(clippingShape);</span> } // // Don't pass 'this' as an observer, it could trigger paint() twice (FIXME: check if it's true) // <span class="nc bnc" id="L988" title="All 2 branches missed."> final var paintOp = needScaling</span> <span class="nc" id="L989"> ? new PaintOp(g2,</span> shownImageX, shownImageY, shownImageWidth, shownImageHeight, scaleQuality, previewSettings, null) <span class="nc" id="L997"> : new PaintOp(g2, shownImageX, shownImageY, previewSettings, null);</span> <span class="nc" id="L999"> imageToDraw.executeInPlace(paintOp);</span> <span class="nc bnc" id="L1001" title="All 2 branches missed."> if (imageBorder != null)</span> { <span class="nc" id="L1003"> imageBorder.paintBorder(this, g2, shownImageX, shownImageY, shownImageWidth, shownImageHeight);</span> } } // if image != null <span class="nc bnc" id="L1007" title="All 2 branches missed."> if (g2 == null)</span> { <span class="nc" id="L1009"> g2 = (Graphics2D)g;</span> } <span class="nc bnc" id="L1012" title="All 2 branches missed."> for (final var overlay : overlayList)</span> { <span class="nc bnc" id="L1014" title="All 2 branches missed."> if (overlay.isVisible())</span> { <span class="nc" id="L1016"> final var g2Copy = (Graphics2D)g2.create();</span> try { <span class="nc" id="L1020"> overlay.paint(g2Copy, this);</span> } <span class="nc" id="L1022"> catch (Throwable t)</span> { <span class="nc" id="L1024"> log.warn(&quot;Exception in Overlay: &quot; + t);</span> <span class="nc" id="L1025"> log.warn(&quot;paint()&quot;, t);</span> <span class="nc" id="L1026"> }</span> <span class="nc" id="L1028"> g2Copy.dispose();</span> } <span class="nc" id="L1030"> }</span> } <span class="nc" id="L1033"> catch (Throwable t)</span> { <span class="nc" id="L1035"> log.warn(&quot;paint()&quot;, t);</span> } finally { <span class="nc bnc" id="L1040" title="All 2 branches missed."> if (g2 != null)</span> { <span class="nc" id="L1042"> g2.dispose();</span> } } <span class="nc" id="L1046"> paintComponents(g);</span> <span class="nc" id="L1047"> }</span> /******************************************************************************************************************* * * Flush all image caches. * ******************************************************************************************************************/ public void flushAllCaches() { <span class="nc" id="L1056"> log.info(&quot;flushAllCaches()&quot;);</span> <span class="nc" id="L1057"> log.info(&quot;&gt;&gt;&gt;&gt; all caches will be recomputed from: &quot; + image);</span> <span class="nc" id="L1058"> flushScaledImageCache();</span> <span class="nc bnc" id="L1060" title="All 2 branches missed."> if (image != null)</span> { <span class="nc bnc" id="L1062" title="All 2 branches missed."> optimizedImage = optimizedImageEnabled ? image.execute(new OptimizeOp()) : image;</span> } else { <span class="nc" id="L1067"> optimizedImage = null;</span> } <span class="nc" id="L1069"> }</span> /******************************************************************************************************************* * * Flush the cached scaled image. * ******************************************************************************************************************/ public void flushScaledImageCache() { <span class="nc" id="L1078"> log.info(&quot;flushScaledImageCache()&quot;);</span> <span class="nc" id="L1079"> scaledImage = null;</span> <span class="nc" id="L1080"> }</span> /******************************************************************************************************************* * * * ******************************************************************************************************************/ public void moveOrigin (final int deltaX, final int deltaY) { <span class="nc" id="L1089"> log.info(&quot;moveOrigin(&quot; + deltaX + &quot;,&quot; + deltaY + &quot;)&quot;);</span> <span class="nc" id="L1090"> final var position = getOrigin();</span> <span class="nc" id="L1091"> position.setLocation(position.getX() + deltaX, position.getY() + deltaY);</span> <span class="nc" id="L1092"> setOrigin(position);</span> <span class="nc" id="L1093"> }</span> /******************************************************************************************************************* * * Sets the explicit scale for displaying the current image. This disables the * fit-to-display-size feature. * * @param scale the new scale * ******************************************************************************************************************/ public void setScale (final double scale) { <span class="nc" id="L1105"> log.info(&quot;setScale(&quot; + scale + &quot;)&quot;);</span> <span class="nc" id="L1106"> setScale(scale, null);</span> <span class="nc" id="L1107"> }</span> /******************************************************************************************************************* * * Sets the explicit scale for displaying the current image. This disables the * fit-to-display-size feature. A pivot point is specified: the contents under * the pivot point don't move during the zoom. * * @param scale the new scale * @param pivotPoint the pivot point (if null, the center of the renderer * is used) * ******************************************************************************************************************/ public void setScale (double scale, final Point pivotPoint) { <span class="nc" id="L1122"> log.info(&quot;setScale(&quot; + scale + &quot;, &quot; + pivotPoint + &quot;)&quot;);</span> <span class="nc" id="L1123"> scale = Math.min(Math.max(scale, minScale), maxScale);</span> // if ((scale &lt; MIN_SCALE) || (scale &gt; MAX_SCALE)) // { // throw new IllegalArgumentException(&quot;scale: &quot; + scale); // } <span class="nc" id="L1130"> final var repaintEnabledSave = repaintEnabled;</span> <span class="nc" id="L1131"> repaintEnabled = false;</span> <span class="nc" id="L1132"> setFitToDisplaySize(false);</span> <span class="nc" id="L1133"> internalSetScale(scale);</span> <span class="nc bnc" id="L1135" title="All 2 branches missed."> if (pivotPoint != null)</span> { <span class="nc" id="L1137"> final var imagePivotPoint = getPositionOverImage(pivotPoint);</span> <span class="nc bnc" id="L1139" title="All 2 branches missed."> if (imagePivotPoint != null)</span> { <span class="nc" id="L1141"> final var newOrigin = computeOrigin(imagePivotPoint, pivotPoint, scale);</span> <span class="nc bnc" id="L1143" title="All 2 branches missed."> if (newOrigin != null)</span> { <span class="nc" id="L1145"> setOrigin(newOrigin);</span> } } } // internalSetScale(scale); <span class="nc" id="L1151"> repaintEnabled = repaintEnabledSave;</span> <span class="nc" id="L1152"> }</span> /******************************************************************************************************************* * * * ******************************************************************************************************************/ public void setMaxScale (final double maxScale) { <span class="nc" id="L1161"> this.maxScale = Math.min(MAX_SCALE, maxScale);</span> <span class="nc" id="L1162"> }</span> /******************************************************************************************************************* * * * ******************************************************************************************************************/ public double getMaxScale() { <span class="nc" id="L1171"> return maxScale;</span> } /******************************************************************************************************************* * * * ******************************************************************************************************************/ public void setMinScale (final double minScale) { <span class="nc" id="L1181"> this.minScale = Math.max(MIN_SCALE, minScale);</span> <span class="nc" id="L1182"> }</span> /******************************************************************************************************************* * * * ******************************************************************************************************************/ public double getMinScale() { <span class="nc" id="L1191"> return minScale;</span> } /******************************************************************************************************************* * * * ******************************************************************************************************************/ public double getFitScale() { <span class="nc" id="L1201"> final var hScale = (double)getAvailableWidth() / image.getWidth();</span> <span class="nc" id="L1202"> final var vScale = (double)getAvailableHeight() / image.getHeight();</span> <span class="nc" id="L1204"> return Math.min(hScale, vScale);</span> // if (this.scale &lt; 0) // { // log.info(&quot;SCALE &lt; 0: w:&quot; + w + &quot; h:&quot; + h + &quot; iw:&quot; + iw + &quot; ih:&quot; + ih); // } } /******************************************************************************************************************* * * Centers the image on the renderer, keeping the current scale. * ******************************************************************************************************************/ public void centerImage() { <span class="nc" id="L1219"> log.info(&quot;centerImage()&quot;);</span> <span class="nc" id="L1220"> setOrigin(computeCenterPoint());</span> <span class="nc" id="L1221"> }</span> /******************************************************************************************************************* * * * ******************************************************************************************************************/ public void fitToDisplaySize() { <span class="nc" id="L1230"> log.info(&quot;fitToDisplaySize()&quot;);</span> <span class="nc bnc" id="L1232" title="All 2 branches missed."> if (image != null)</span> { <span class="nc" id="L1234"> final var saveRepaintEnabled = repaintEnabled;</span> <span class="nc" id="L1235"> repaintEnabled = false;</span> <span class="nc" id="L1236"> internalSetScale(getFitScale());</span> <span class="nc" id="L1237"> centerImage();</span> <span class="nc" id="L1238"> repaintEnabled = saveRepaintEnabled;</span> <span class="nc" id="L1239"> repaint();</span> } <span class="nc" id="L1241"> }</span> /******************************************************************************************************************* * * Enables or disables the fit-to-display-size feature. * ******************************************************************************************************************/ public void setFitToDisplaySize (final boolean fitToDisplaySize) { <span class="nc" id="L1250"> log.info(&quot;setFitToDisplaySize(&quot; + fitToDisplaySize + &quot;)&quot;);</span> <span class="nc" id="L1251"> this.fitToDisplaySize = fitToDisplaySize;</span> <span class="nc bnc" id="L1253" title="All 2 branches missed."> if (fitToDisplaySize)</span> { <span class="nc" id="L1255"> fitToDisplaySize();</span> } <span class="nc" id="L1257"> }</span> /******************************************************************************************************************* * * * ******************************************************************************************************************/ public void addImageRendererListener (final EditableImageRendererListener listener) { <span class="nc" id="L1266"> listenerList.add(listener);</span> <span class="nc" id="L1267"> }</span> /******************************************************************************************************************* * * * ******************************************************************************************************************/ public void removeImageRendererListener (final EditableImageRendererListener listener) { <span class="nc" id="L1276"> listenerList.remove(listener);</span> <span class="nc" id="L1277"> }</span> /******************************************************************************************************************* * * * ******************************************************************************************************************/ private void internalSetScale (final double scale) { <span class="nc bnc" id="L1286" title="All 2 branches missed."> if (this.scale != scale)</span> { <span class="nc" id="L1288"> this.scale = scale;</span> <span class="nc" id="L1289"> flushScaledImageCache();</span> <span class="nc" id="L1290"> repaint();</span> } <span class="nc" id="L1293"> fireScaleChangedEvent();</span> <span class="nc" id="L1294"> }</span> /******************************************************************************************************************* * * * ******************************************************************************************************************/ private Point computeCenterPoint() { <span class="nc" id="L1303"> return new Point(-(int)Math.round(((getAvailableWidth() / scale) - image.getWidth()) / 2),</span> <span class="nc" id="L1304"> -(int)Math.round(((getAvailableHeight() / scale) - image.getHeight()) / 2));</span> } /******************************************************************************************************************* * * * ******************************************************************************************************************/ private void fireScaleChangedEvent() { <span class="nc" id="L1314"> final var event = new EditableImageRendererEvent(this);</span> <span class="nc bnc" id="L1316" title="All 2 branches missed."> for (final var listener : new ArrayList&lt;&gt;(listenerList))</span> { try { <span class="nc" id="L1320"> listener.scaleChanged(event);</span> } <span class="nc" id="L1322"> catch (Throwable t)</span> { <span class="nc" id="L1324"> log.warn(&quot;Exception in listener: &quot; + t);</span> <span class="nc" id="L1325"> log.warn(&quot;fireScaleChangedEvent()&quot;, t);</span> <span class="nc" id="L1326"> }</span> <span class="nc" id="L1327"> }</span> <span class="nc" id="L1328"> }</span> /******************************************************************************************************************* * * * ******************************************************************************************************************/ private void fireAngleChangedEvent() { <span class="nc" id="L1337"> final var event = new EditableImageRendererEvent(this);</span> <span class="nc bnc" id="L1339" title="All 2 branches missed."> for (final var listener : new ArrayList&lt;&gt;(listenerList))</span> { try { <span class="nc" id="L1343"> listener.angleChanged(event);</span> } <span class="nc" id="L1345"> catch (Throwable t)</span> { <span class="nc" id="L1347"> log.warn(&quot;Exception in listener: &quot; + t);</span> <span class="nc" id="L1348"> log.warn(&quot;fireAngleChangedEvent()&quot;, t);</span> <span class="nc" id="L1349"> }</span> <span class="nc" id="L1350"> }</span> <span class="nc" id="L1351"> }</span> /******************************************************************************************************************* * * * ******************************************************************************************************************/ protected void fireEditingToolActivated (final EditingTool editingTool) { <span class="nc" id="L1360"> final var event = new EditableImageRendererEvent(this, editingTool);</span> <span class="nc bnc" id="L1362" title="All 2 branches missed."> for (final var listener : new ArrayList&lt;&gt;(listenerList))</span> { try { <span class="nc" id="L1366"> listener.toolActivated(event);</span> } <span class="nc" id="L1368"> catch (Throwable t)</span> { <span class="nc" id="L1370"> log.warn(&quot;Exception in listener: &quot; + t);</span> <span class="nc" id="L1371"> log.warn(&quot;fireEditingToolActivated()&quot;, t);</span> <span class="nc" id="L1372"> }</span> <span class="nc" id="L1373"> }</span> <span class="nc" id="L1374"> }</span> /******************************************************************************************************************* * * * ******************************************************************************************************************/ protected void fireEditingToolDeactivated (final EditingTool editingTool) { <span class="nc" id="L1383"> final var event = new EditableImageRendererEvent(this, editingTool);</span> <span class="nc bnc" id="L1385" title="All 2 branches missed."> for (final var listener : new ArrayList&lt;&gt;(listenerList))</span> { try { <span class="nc" id="L1389"> listener.toolDeactivated(event);</span> } <span class="nc" id="L1391"> catch (Throwable t)</span> { <span class="nc" id="L1393"> log.warn(&quot;Exception in listener: &quot; + t);</span> <span class="nc" id="L1394"> log.warn(&quot;fireEditingToolDeactivated()&quot;, t);</span> <span class="nc" id="L1395"> }</span> <span class="nc" id="L1396"> }</span> <span class="nc" id="L1397"> }</span> /******************************************************************************************************************* * * Updates the scrollbar cursors positions. * ******************************************************************************************************************/ private void updateScrollBars() { <span class="nc bnc" id="L1406" title="All 2 branches missed."> if (scrollBarsVisible)</span> { <span class="nc" id="L1408"> horizontalScrollBar.setValues(this.origin.x,</span> <span class="nc" id="L1409"> (int)Math.round(getAvailableWidth() / scale),</span> 0, <span class="nc" id="L1411"> image.getWidth());</span> <span class="nc" id="L1412"> verticalScrollBar.setValues(this.origin.y,</span> <span class="nc" id="L1413"> (int)Math.round(getAvailableHeight() / scale),</span> 0, <span class="nc" id="L1415"> image.getHeight());</span> } <span class="nc" id="L1417"> }</span> /******************************************************************************************************************* * * Lays out the scrollbars in their correct position. * ******************************************************************************************************************/ private void layoutScrollBars() { <span class="nc bnc" id="L1426" title="All 6 branches missed."> if (scrollBarsVisible &amp;&amp; ((previousWidth != getWidth()) || (previohsHeight != getHeight())))</span> { <span class="nc" id="L1428"> horizontalScrollBar.setBounds(0,</span> <span class="nc" id="L1429"> getHeight() - scrollbarThickness,</span> <span class="nc" id="L1430"> getWidth() - scrollbarThickness,</span> scrollbarThickness); <span class="nc" id="L1432"> verticalScrollBar.setBounds(getWidth() - scrollbarThickness,</span> 0, scrollbarThickness, <span class="nc" id="L1435"> getHeight() - scrollbarThickness);</span> <span class="nc" id="L1436"> filler.setBounds(getWidth() - scrollbarThickness,</span> <span class="nc" id="L1437"> getHeight() - scrollbarThickness,</span> scrollbarThickness, scrollbarThickness); <span class="nc" id="L1440"> previousWidth = getWidth();</span> <span class="nc" id="L1441"> previohsHeight = getHeight();</span> } <span class="nc" id="L1443"> }</span> /******************************************************************************************************************* * * Returns the available width for rendering the image. * ******************************************************************************************************************/ private int getAvailableWidth() { <span class="nc bnc" id="L1452" title="All 2 branches missed."> return getWidth() - (scrollBarsVisible ? scrollbarThickness : 0);</span> } /******************************************************************************************************************* * * Returns the available height for rendering the image. * ******************************************************************************************************************/ private int getAvailableHeight() { <span class="nc bnc" id="L1462" title="All 2 branches missed."> return getHeight() - (scrollBarsVisible ? scrollbarThickness : 0);</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>