<?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> > <a href="../index.html" class="el_bundle">image-renderer</a> > <a href="index.source.html" class="el_package">it.tidalwave.image.render</a> > <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 "License"); 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 "AS IS" 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<Overlay> overlayList = new ArrayList<>();</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<EditableImageRendererListener> listenerList = new ArrayList<>();</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 -> 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("Enabled workaround for MST-63");</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("setImage(" + image + ")");</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("setImage(null)");</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("setRepaintEnabled(" + repaintEnabled + ")");</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("setOrigin(" + origin + ")");</span>
<span class="nc bnc" id="L385" title="All 6 branches missed."> if ((image != null) && (image.getWidth() > 0) && (image.getHeight() > 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("internalSetOrigin(" + origin + ")");</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 <= 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 <= 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("setAngle(" + angle + ")");</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("Image size: " + shownImageHeight + " x " + 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 >= 0) && (y >= 0) && (x < imageWidth) && (y < 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("Image size: " + shownImageHeight + " x " + 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 >= 0) && (y >= 0) && (x < imageWidth) && (y < 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("setPositionOverImage(" + imagePoint + ", " + componentPoint + ")");</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("setMargin(" + margin + ")");</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("setScrollBarsVisible(" + scrollBarsVisible + ")");</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
* <code>repaint()</code>, 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("setScaleQuality(" + scaleQuality + ")");</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
* <code>repaint()</code>, 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("setRotateQuality(" + rotateQuality + ")");</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("setScaledImageCachingEnabled(" + scaledImageCachingEnabled + ")");</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("setOptimizedImageEnabled(" + optimizedImageEnabled + ")");</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("paint()");</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 && (maxSize < 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(">>>> computing scaled image");</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("Exception in Overlay: " + t);</span>
<span class="nc" id="L1025"> log.warn("paint()", 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("paint()", 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("flushAllCaches()");</span>
<span class="nc" id="L1057"> log.info(">>>> all caches will be recomputed from: " + 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("flushScaledImageCache()");</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("moveOrigin(" + deltaX + "," + deltaY + ")");</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("setScale(" + scale + ")");</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("setScale(" + scale + ", " + pivotPoint + ")");</span>
<span class="nc" id="L1123"> scale = Math.min(Math.max(scale, minScale), maxScale);</span>
// if ((scale < MIN_SCALE) || (scale > MAX_SCALE))
// {
// throw new IllegalArgumentException("scale: " + 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 < 0)
// {
// log.info("SCALE < 0: w:" + w + " h:" + h + " iw:" + iw + " ih:" + ih);
// }
}
/*******************************************************************************************************************
*
* Centers the image on the renderer, keeping the current scale.
*
******************************************************************************************************************/
public void centerImage()
{
<span class="nc" id="L1219"> log.info("centerImage()");</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("fitToDisplaySize()");</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("setFitToDisplaySize(" + fitToDisplaySize + ")");</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<>(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("Exception in listener: " + t);</span>
<span class="nc" id="L1325"> log.warn("fireScaleChangedEvent()", 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<>(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("Exception in listener: " + t);</span>
<span class="nc" id="L1348"> log.warn("fireAngleChangedEvent()", 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<>(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("Exception in listener: " + t);</span>
<span class="nc" id="L1371"> log.warn("fireEditingToolActivated()", 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<>(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("Exception in listener: " + t);</span>
<span class="nc" id="L1394"> log.warn("fireEditingToolDeactivated()", 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 && ((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>