Content of file Java2DUtils.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>Java2DUtils.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 Processor</a> &gt; <a href="../index.html" class="el_bundle">image-core</a> &gt; <a href="index.source.html" class="el_package">it.tidalwave.image.java2d</a> &gt; <span class="el_source">Java2DUtils.java</span></div><h1>Java2DUtils.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.java2d;

import java.util.Arrays;
import java.util.Map;
import java.util.Properties;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferUShort;
import java.awt.image.DirectColorModel;
import java.awt.image.Kernel;
import java.awt.image.PixelInterleavedSampleModel;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
import org.slf4j.Logger;
import it.tidalwave.image.ImageUtils;
import it.tidalwave.image.Kernel2;
import it.tidalwave.image.Quality;
import lombok.extern.slf4j.Slf4j;

/***********************************************************************************************************************
 *
 * @author Fabrizio Giudici
 *
 **********************************************************************************************************************/
<span class="pc bpc" id="L64" title="1 of 2 branches missed.">@Slf4j</span>
<span class="nc" id="L65">public class Java2DUtils</span>
  {
<span class="fc" id="L67">    public static final Float ZERO = (float)0;</span>
<span class="fc" id="L68">    private static final Map&lt;Quality, Object&gt; renderingHintsQualityMap =</span>
<span class="fc" id="L69">            Map.of(Quality.FASTEST,</span>
                   RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR,
                   Quality.INTERMEDIATE,
                   RenderingHints.VALUE_INTERPOLATION_BILINEAR,
                   Quality.BEST,
                   RenderingHints.VALUE_INTERPOLATION_BICUBIC);

<span class="fc" id="L76">    private static final Map&lt;Quality, Integer&gt; affineTransformQualityMap =</span>
<span class="fc" id="L77">            Map.of(Quality.FASTEST,</span>
<span class="fc" id="L78">                   AffineTransformOp.TYPE_NEAREST_NEIGHBOR,</span>
                   Quality.INTERMEDIATE,
<span class="fc" id="L80">                   AffineTransformOp.TYPE_BILINEAR,</span>
                   Quality.BEST,
<span class="fc" id="L82">                   AffineTransformOp.TYPE_BICUBIC);</span>

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public static Properties getProperties (final BufferedImage image)
      {
<span class="fc" id="L90">        final var properties = new Properties();</span>
<span class="fc" id="L91">        final var propertyNames = image.getPropertyNames();</span>

<span class="pc bpc" id="L93" title="1 of 2 branches missed.">        if (propertyNames != null)</span>
          {
<span class="nc bnc" id="L95" title="All 2 branches missed.">            for (final var propertyName : propertyNames)</span>
              {
<span class="nc" id="L97">                final var propertyValue = image.getProperty(propertyName);</span>
<span class="nc" id="L98">                properties.setProperty(propertyName, propertyValue.toString());</span>
              }
          }

<span class="fc" id="L102">        return properties;</span>
      }

    /*******************************************************************************************************************
     *
     * @param width
     * @param height
     * @return
     *
     ******************************************************************************************************************/
    public static BufferedImage createOptimizedImage (final int width, final int height)
      {
<span class="fc" id="L114">        final var ge = GraphicsEnvironment.getLocalGraphicsEnvironment();</span>
<span class="nc" id="L115">        final var gs = ge.getDefaultScreenDevice();</span>
<span class="nc" id="L116">        final var gc = gs.getDefaultConfiguration();</span>

<span class="nc" id="L118">        return gc.createCompatibleImage(width, height);</span>
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public static GraphicsConfiguration getGraphicsConfiguration()
      {
<span class="nc" id="L127">        final var ge = GraphicsEnvironment.getLocalGraphicsEnvironment();</span>
<span class="nc" id="L128">        final var gs = ge.getScreenDevices();</span>
<span class="nc" id="L129">        final var gd = gs[0]; // FIXME</span>

<span class="nc" id="L131">        return gd.getDefaultConfiguration();</span>
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public static BufferedImage createSimilarImage (final BufferedImage bufferedImage,
                                                    final int newWidth,
                                                    final int newHeight)
      {
<span class="nc" id="L142">        final var sampleModel = bufferedImage.getSampleModel().createCompatibleSampleModel(newWidth, newHeight);</span>
<span class="nc" id="L143">        final var newRaster = Raster.createWritableRaster(sampleModel, null);</span>
<span class="nc" id="L144">        final var colorModel = bufferedImage.getColorModel();</span>

<span class="nc" id="L146">        return new BufferedImage(colorModel, newRaster, false, Java2DUtils.getProperties(bufferedImage));</span>
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public static String getICCProfileName (final RenderedImage image)
      {
<span class="fc" id="L155">        final var iccProfile = ImageUtils.getICCProfile(image);</span>

<span class="pc bpc" id="L157" title="1 of 2 branches missed.">        if (iccProfile != null)</span>
          {
<span class="fc" id="L159">            return ImageUtils.getICCProfileName(iccProfile);</span>
          }

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

    /*******************************************************************************************************************
     *
     * It seems that SinglePixelPackedSampleModel is the only fast mode when a
     * color profile is converted. This is probably a bug (that has nothing to do
     * with bugs 4886071 and 4705399).
     * Note that grayscale images (TYPE_GRAY) are not converted.
     *
     ******************************************************************************************************************/
    public static BufferedImage convertToSinglePixelPackedSampleModel (BufferedImage image)
      {
<span class="fc" id="L178">        log.debug(&quot;convertToSinglePixelPackedSampleModel(image: &quot; + image + &quot;)&quot;);</span>
<span class="fc" id="L179">        Java2DUtils.logImage(log, &quot;&gt;&gt;&gt;&gt; source bufferedImage&quot;, image);</span>

<span class="fc" id="L181">        var time = System.currentTimeMillis();</span>

<span class="fc" id="L183">        final var sourceRaster = image.getRaster();</span>
<span class="fc" id="L184">        var colorModel = image.getColorModel();</span>
<span class="fc" id="L185">        var colorSpace = (ICC_ColorSpace)colorModel.getColorSpace();</span>
<span class="fc" id="L186">        final var ssmd = sourceRaster.getSampleModel();</span>

<span class="pc bpc" id="L188" title="1 of 2 branches missed.">        if (colorSpace.getType() == ColorSpace.TYPE_GRAY)</span>
          {
<span class="nc" id="L190">            log.debug(&quot;&gt;&gt;&gt;&gt; TYPE_GRAY, not converting&quot;);</span>
          }

<span class="pc bpc" id="L193" title="1 of 2 branches missed.">        else if (!(ssmd instanceof PixelInterleavedSampleModel))</span>
          {
<span class="nc" id="L195">            log.debug(&quot;&gt;&gt;&gt;&gt; sourceSampleModel is &quot; + ssmd.getClass() + &quot;, not converting&quot;);</span>
          }

        else
          {
<span class="fc" id="L200">            final var sourceSampleModel = (PixelInterleavedSampleModel)ssmd;</span>
<span class="fc" id="L201">            final var bitMasks = new int[]{0x00ff0000, 0x0000ff00, 0x000000ff};</span>

<span class="fc" id="L203">            final var sampleModel =</span>
<span class="fc" id="L204">                    new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, image.getWidth(),</span>
<span class="fc" id="L205">                                                     image.getHeight(), bitMasks);</span>

<span class="fc" id="L207">            final var destRaster = Raster.createWritableRaster(sampleModel, null);</span>
<span class="fc" id="L208">            final var destDataBuffer = (DataBufferInt)destRaster.getDataBuffer();</span>
<span class="fc" id="L209">            final var destBuffer = destDataBuffer.getData();</span>
<span class="fc" id="L210">            final var bandOffsets = sourceSampleModel.getBandOffsets();</span>

<span class="fc bfc" id="L212" title="All 2 branches covered.">            for (var i = 0; i &lt; bandOffsets.length; i++)</span>
              {
<span class="fc" id="L214">                bandOffsets[i] += ((-sourceRaster.getSampleModelTranslateX() * sourceSampleModel.getPixelStride()) -</span>
<span class="fc" id="L215">                                   (sourceRaster.getSampleModelTranslateY() * sourceSampleModel.getScanlineStride()));</span>
              }

<span class="fc" id="L218">            final var sourceDataBuffer = sourceRaster.getDataBuffer();</span>

<span class="pc bpc" id="L220" title="1 of 2 branches missed.">            if (sourceDataBuffer instanceof DataBufferUShort)</span>
              {
<span class="nc" id="L222">                convertUShortDataBuffer(image,</span>
                                        (DataBufferUShort)sourceDataBuffer,
                                        sourceSampleModel,
                                        bandOffsets,
                                        destBuffer);
              }

<span class="pc bpc" id="L229" title="1 of 2 branches missed.">            else if (sourceDataBuffer instanceof DataBufferByte)</span>
              {
<span class="fc" id="L231">                convertByteDataBuffer(image,</span>
                                      (DataBufferByte)sourceDataBuffer,
                                      sourceSampleModel,
                                      bandOffsets,
                                      destBuffer);
              }

            else
              {
<span class="nc" id="L240">                throw new IllegalArgumentException(&quot;Cannot deal with &quot; + sourceDataBuffer.getClass());</span>
              }

<span class="fc" id="L243">            final var sourceProfileName = ImageUtils.getICCProfileName(colorSpace.getProfile());</span>

<span class="pc bpc" id="L245" title="1 of 2 branches missed.">            if (sourceProfileName.equals(&quot;Nikon sRGB 4.0.0.3001&quot;))</span>
              {
<span class="nc" id="L247">                log.warn(&quot;&gt;&gt;&gt;&gt; Workaround #1094403: using sRGB instead of &quot; + sourceProfileName);</span>
<span class="nc" id="L248">                colorSpace = new ICC_ColorSpace(ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB));</span>
              }

<span class="fc" id="L251">            colorModel = new DirectColorModel(colorSpace,</span>
                                              24,
                                              bitMasks[0],
                                              bitMasks[1],
                                              bitMasks[2],
                                              0,
                                              false,
                                              DataBuffer.TYPE_INT);
<span class="fc" id="L259">            image = new BufferedImage(colorModel, destRaster, false, null);</span>
          }

<span class="fc" id="L262">        time = System.currentTimeMillis() - time;</span>
<span class="fc" id="L263">        Java2DUtils.logImage(log, &quot;&gt;&gt;&gt;&gt; convertToSinglePixelPackedSampleModel() returning&quot;, image);</span>
<span class="fc" id="L264">        log.debug(&quot;&gt;&gt;&gt;&gt; convertToSinglePixelPackedSampleModel() completed ok in &quot; + time + &quot; msec&quot;);</span>

<span class="fc" id="L266">        return image;</span>
      }

    /*******************************************************************************************************************
     *
     * Scales a &lt;code&gt;BufferedImage&lt;/code&gt; by filtering with an
     * &lt;code&gt;AffineTransform&lt;/code&gt;.
     *
     * @param   bufferedImage  the image to scale
     * @param   xScale         the horizontal scale
     * @param   yScale         the vertical scale
     * @param   quality        the quality
     *
     ******************************************************************************************************************/
    public static BufferedImage scaleWithAffineTransform (final BufferedImage bufferedImage,
                                                          final double xScale,
                                                          final double yScale,
                                                          final Quality quality)
            throws IllegalArgumentException
      {
<span class="nc" id="L286">        log.debug(&quot;scaleWithAffineTransform(&quot; + xScale + &quot;, &quot; + yScale + &quot;, &quot; + quality);</span>

<span class="nc" id="L288">        final var transform = AffineTransform.getScaleInstance(xScale, yScale);</span>
<span class="nc" id="L289">        final var interpolation = findAffineTransformInterpolation(quality);</span>
<span class="nc" id="L290">        log.debug(&quot;&gt;&gt;&gt;&gt; AffineTransformOp(&quot; + transform + &quot;, &quot; + interpolation + &quot;)&quot;);</span>

<span class="nc" id="L292">        final var op = new AffineTransformOp(transform, interpolation);</span>

<span class="nc" id="L294">        return op.filter(bufferedImage, null);</span>
      }

    /*******************************************************************************************************************
     *
     * Scales a &lt;code&gt;BufferedImage&lt;/code&gt; by redrawing it on a new bitmap.
     *
     * @param   bufferedImage  the image to scale
     * @param   xScale         the horizontal scale
     * @param   yScale         the vertical scale
     * @param   quality        the quality
     *
     ******************************************************************************************************************/
    public static BufferedImage scaleWithDrawImage (final BufferedImage bufferedImage,
                                                    final double xScale,
                                                    final double yScale,
                                                    final Quality quality)
            throws IllegalArgumentException
      {
<span class="nc" id="L313">        log.debug(&quot;scaleWithDrawImage(&quot; + xScale + &quot;, &quot; + yScale + &quot;, &quot; + quality);</span>

<span class="nc" id="L315">        final var newWidth = (int)Math.round(bufferedImage.getWidth() * xScale);</span>
<span class="nc" id="L316">        final var newHeight = (int)Math.round(bufferedImage.getHeight() * yScale);</span>
<span class="nc" id="L317">        final var result = createSimilarImage(bufferedImage, newWidth, newHeight);</span>

<span class="nc" id="L319">        final var g2d = (Graphics2D)result.getGraphics();</span>
<span class="nc" id="L320">        final var interpolation = findRenderingHintsInterpolation(quality);</span>
<span class="nc" id="L321">        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, interpolation);</span>

        try
          {
<span class="nc" id="L325">            g2d.drawImage(bufferedImage, 0, 0, newWidth, newHeight, null);</span>
          }
        finally
          {
<span class="nc" id="L329">            g2d.dispose();</span>
          }

<span class="nc" id="L332">        return result;</span>
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public static BufferedImage rotateWithDrawImage (final BufferedImage bufferedImage,
                                                     final double degrees,
                                                     final Quality quality)
      {
<span class="nc" id="L343">        final var radians = Math.toRadians(degrees);</span>
<span class="nc" id="L344">        final var cos = Math.cos(radians);</span>
<span class="nc" id="L345">        final var sin = Math.sin(radians);</span>
<span class="nc" id="L346">        final var width = bufferedImage.getWidth();</span>
<span class="nc" id="L347">        final var height = bufferedImage.getHeight();</span>
<span class="nc" id="L348">        final var newWidth = (int)Math.round(Math.abs(width * cos) + Math.abs(height * sin));</span>
<span class="nc" id="L349">        final var newHeight = (int)Math.round(Math.abs(width * sin) + Math.abs(height * cos));</span>
<span class="nc" id="L350">        final var result = createSimilarImage(bufferedImage, newWidth, newHeight);</span>
<span class="nc" id="L351">        final var g2d = (Graphics2D)result.getGraphics();</span>
<span class="nc" id="L352">        final var interpolation = findRenderingHintsInterpolation(quality);</span>
<span class="nc" id="L353">        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, interpolation);</span>

        try
          {
<span class="nc" id="L357">            g2d.transform(AffineTransform.getTranslateInstance((newWidth - width) / 2, (newHeight - height) / 2));</span>
<span class="nc" id="L358">            g2d.transform(AffineTransform.getRotateInstance(-radians, width / 2, height / 2));</span>
<span class="nc" id="L359">            g2d.drawImage(bufferedImage, 0, 0, null);</span>
          }

        finally
          {
<span class="nc" id="L364">            g2d.dispose();</span>
          }

<span class="nc" id="L367">        return result;</span>
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    protected static String toString (final int[] array, final int radix)
      {
<span class="fc" id="L376">        final var buffer = new StringBuilder();</span>

<span class="fc bfc" id="L378" title="All 2 branches covered.">        for (var i = 0; i &lt; array.length; i++)</span>
          {
<span class="fc" id="L380">            buffer.append(Integer.toString(array[i], radix));</span>

<span class="fc bfc" id="L382" title="All 2 branches covered.">            if (i &lt; (array.length - 1))</span>
              {
<span class="fc" id="L384">                buffer.append(&quot;,&quot;);</span>
              }
          }

<span class="fc" id="L388">        return buffer.toString();</span>
      }

    /**
     * @param image
     */

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public static void logImage (final Logger log, final String prefix, final RenderedImage image)
      {
<span class="pc bpc" id="L401" title="1 of 2 branches missed.">        if (Java2DUtils.log.isDebugEnabled())</span>
          {
<span class="fc bfc" id="L403" title="All 2 branches covered.">            if (image == null)</span>
              {
<span class="fc" id="L405">                Java2DUtils.log.debug(prefix + &quot;null image&quot;);</span>
              }

            else
              {
//            image.getData(); THIS IS SLOW SLOW SLOW!!
<span class="fc" id="L411">                final var colorModel = image.getColorModel();</span>
<span class="fc" id="L412">                Java2DUtils.log.debug(prefix + &quot;.size:           &quot; + image.getWidth() + &quot;, &quot; + image.getHeight());</span>
<span class="fc" id="L413">                Java2DUtils.log.debug(prefix + &quot;.tiles:          &quot; + image.getNumXTiles() + &quot; &quot; + image.getNumYTiles());</span>
<span class="fc" id="L414">                Java2DUtils.log.debug(prefix + &quot;.class:          &quot; + image.getClass().getName());</span>
<span class="fc" id="L415">                Java2DUtils.log.debug(prefix + &quot;.sampleModel:    &quot; + toString(image.getSampleModel()));</span>

<span class="pc bpc" id="L417" title="1 of 2 branches missed.">                if (colorModel != null)</span>
                  {
<span class="fc" id="L419">                    Java2DUtils.log.debug(</span>
<span class="fc" id="L420">                            prefix + &quot;.colorModel:     &quot; + colorModel.getClass().getName() + &quot; : &quot; + colorModel);</span>
<span class="fc" id="L421">                    Java2DUtils.log.debug(prefix + &quot;.colorSpace:     &quot; + toString(colorModel.getColorSpace()));</span>
                  }
              }

            //      log.debug(&quot;&gt;&gt;&gt;&gt; iccProfile is now: &quot; + getICCProfileName(bufferedImage));
          }
<span class="fc" id="L427">      }</span>

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    private static String toString (final SampleModel sampleModel)
      {
<span class="fc bfc" id="L435" title="All 2 branches covered.">        if (sampleModel instanceof SinglePixelPackedSampleModel)</span>
          {
<span class="fc" id="L437">            return toString((SinglePixelPackedSampleModel)sampleModel);</span>
          }

<span class="pc bpc" id="L440" title="1 of 2 branches missed.">        else if (sampleModel instanceof PixelInterleavedSampleModel)</span>
          {
<span class="fc" id="L442">            return toString((PixelInterleavedSampleModel)sampleModel);</span>
          }

        else
          {
<span class="nc" id="L447">            return sampleModel.toString();</span>
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    private static String toString (final ColorSpace colorSpace)
      {
<span class="pc bpc" id="L457" title="1 of 2 branches missed.">        if (colorSpace instanceof ICC_ColorSpace)</span>
          {
<span class="fc" id="L459">            return toString((ICC_ColorSpace)colorSpace);</span>
          }

        else
          {
<span class="nc" id="L464">            return colorSpace.toString();</span>
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    private static String toString (final ICC_ColorSpace colorSpace)
      {
<span class="fc" id="L474">        final var buffer = new StringBuilder();</span>
<span class="fc" id="L475">        buffer.append(colorSpace.getClass().getName());</span>
<span class="fc" id="L476">        buffer.append(&quot;[type: &quot;);</span>
<span class="fc" id="L477">        buffer.append(colorSpace.getType());</span>
<span class="fc" id="L478">        buffer.append(&quot;, profile name: &quot;);</span>
<span class="fc" id="L479">        buffer.append(ImageUtils.getICCProfileName(colorSpace.getProfile()));</span>
<span class="fc" id="L480">        buffer.append(&quot;]&quot;);</span>

<span class="fc" id="L482">        return buffer.toString();</span>
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    private static String toString (final SinglePixelPackedSampleModel sampleModel)
      {
<span class="fc" id="L491">        final var buffer = new StringBuilder();</span>
<span class="fc" id="L492">        buffer.append(sampleModel.getClass().getName());</span>
<span class="fc" id="L493">        buffer.append(&quot;[width: &quot;);</span>
<span class="fc" id="L494">        buffer.append(sampleModel.getWidth());</span>
<span class="fc" id="L495">        buffer.append(&quot;, height: &quot;);</span>
<span class="fc" id="L496">        buffer.append(sampleModel.getHeight());</span>
<span class="fc" id="L497">        buffer.append(&quot;, numBands: &quot;);</span>
<span class="fc" id="L498">        buffer.append(sampleModel.getNumBands());</span>
<span class="fc" id="L499">        buffer.append(&quot;, dataType: &quot;);</span>
<span class="fc" id="L500">        buffer.append(sampleModel.getDataType());</span>
<span class="fc" id="L501">        buffer.append(&quot;, scanlineStride: &quot;);</span>
<span class="fc" id="L502">        buffer.append(sampleModel.getScanlineStride());</span>
<span class="fc" id="L503">        buffer.append(&quot;, transferType: &quot;);</span>
<span class="fc" id="L504">        buffer.append(sampleModel.getTransferType());</span>
<span class="fc" id="L505">        buffer.append(&quot;, numDataElements: &quot;);</span>
<span class="fc" id="L506">        buffer.append(sampleModel.getNumDataElements());</span>
<span class="fc" id="L507">        buffer.append(&quot;, bitMasks: &quot;);</span>
<span class="fc" id="L508">        buffer.append(toString(sampleModel.getBitMasks(), 16));</span>
<span class="fc" id="L509">        buffer.append(&quot;, bitOffsets: &quot;);</span>
<span class="fc" id="L510">        buffer.append(toString(sampleModel.getBitOffsets(), 10));</span>
<span class="fc" id="L511">        buffer.append(&quot;]&quot;);</span>

<span class="fc" id="L513">        return buffer.toString();</span>
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    private static String toString (final PixelInterleavedSampleModel sampleModel)
      {
<span class="fc" id="L522">        final var buffer = new StringBuilder();</span>
<span class="fc" id="L523">        buffer.append(sampleModel.getClass().getName());</span>
<span class="fc" id="L524">        buffer.append(&quot;[width: &quot;);</span>
<span class="fc" id="L525">        buffer.append(sampleModel.getWidth());</span>
<span class="fc" id="L526">        buffer.append(&quot;, height: &quot;);</span>
<span class="fc" id="L527">        buffer.append(sampleModel.getHeight());</span>
<span class="fc" id="L528">        buffer.append(&quot;, numBands: &quot;);</span>
<span class="fc" id="L529">        buffer.append(sampleModel.getNumBands());</span>
<span class="fc" id="L530">        buffer.append(&quot;, dataType: &quot;);</span>
<span class="fc" id="L531">        buffer.append(sampleModel.getDataType());</span>
<span class="fc" id="L532">        buffer.append(&quot;, scanlineStride: &quot;);</span>
<span class="fc" id="L533">        buffer.append(sampleModel.getScanlineStride());</span>
<span class="fc" id="L534">        buffer.append(&quot;, transferType: &quot;);</span>
<span class="fc" id="L535">        buffer.append(sampleModel.getTransferType());</span>
<span class="fc" id="L536">        buffer.append(&quot;, numDataElements: &quot;);</span>
<span class="fc" id="L537">        buffer.append(sampleModel.getNumDataElements());</span>
<span class="fc" id="L538">        buffer.append(&quot;, bandOffsets: &quot;);</span>
<span class="fc" id="L539">        buffer.append(toString(sampleModel.getBandOffsets(), 10));</span>
<span class="fc" id="L540">        buffer.append(&quot;, bankIndices: &quot;);</span>
<span class="fc" id="L541">        buffer.append(toString(sampleModel.getBankIndices(), 10));</span>
<span class="fc" id="L542">        buffer.append(&quot;]&quot;);</span>

<span class="fc" id="L544">        return buffer.toString();</span>
      }

    /*******************************************************************************************************************
     *
     * @param image
     * @param sourceDataBuffer
     * @param sourceSampleModel
     * @param bandOffsets
     * @param destBuffer
     *
     ******************************************************************************************************************/
    private static void convertUShortDataBuffer (final BufferedImage image,
                                                 final DataBufferUShort sourceDataBuffer,
                                                 final PixelInterleavedSampleModel sourceSampleModel,
                                                 final int[] bandOffsets,
                                                 final int[] destBuffer)
      {
<span class="nc" id="L562">        var base = 0;</span>
<span class="nc" id="L563">        var i = 0;</span>
<span class="nc" id="L564">        final var sourceBuffer = sourceDataBuffer.getData();</span>

<span class="nc bnc" id="L566" title="All 2 branches missed.">        for (var y = 0; y &lt; image.getHeight(); y++)</span>
          {
<span class="nc" id="L568">            var j = base;</span>

<span class="nc bnc" id="L570" title="All 2 branches missed.">            for (var x = 0; x &lt; image.getWidth(); x++)</span>
              {
<span class="nc" id="L572">                final var r = (sourceBuffer[j + bandOffsets[0]] &amp; 0xffff) &gt;&gt; 8;</span>
<span class="nc" id="L573">                final var g = (sourceBuffer[j + bandOffsets[1]] &amp; 0xffff) &gt;&gt; 8;</span>
<span class="nc" id="L574">                final var b = (sourceBuffer[j + bandOffsets[2]] &amp; 0xffff) &gt;&gt; 8;</span>

<span class="nc" id="L576">                destBuffer[i++] = (r &lt;&lt; 16) | (g &lt;&lt; 8) | b;</span>
<span class="nc" id="L577">                j += 3;</span>
              }

<span class="nc" id="L580">            base += sourceSampleModel.getScanlineStride();</span>
          }
<span class="nc" id="L582">      }</span>

    /**
     * @param image
     * @param sourceDataBuffer
     * @param sourceSampleModel
     * @param bandOffsets
     * @param destBuffer
     */
    private static void convertByteDataBuffer (final BufferedImage image,
                                               final DataBufferByte sourceDataBuffer,
                                               final PixelInterleavedSampleModel sourceSampleModel,
                                               final int[] bandOffsets,
                                               final int[] destBuffer)
      {
<span class="fc" id="L597">        var base = 0;</span>
<span class="fc" id="L598">        var i = 0;</span>
<span class="fc" id="L599">        final var sourceBuffer = sourceDataBuffer.getData();</span>
<span class="fc" id="L600">        final var pixelStride = sourceSampleModel.getPixelStride();</span>

<span class="fc bfc" id="L602" title="All 2 branches covered.">        for (var y = 0; y &lt; image.getHeight(); y++)</span>
          {
<span class="fc" id="L604">            var j = base;</span>

<span class="fc bfc" id="L606" title="All 2 branches covered.">            for (var x = 0; x &lt; image.getWidth(); x++)</span>
              {
<span class="fc" id="L608">                final var r = (sourceBuffer[j + bandOffsets[0]] &amp; 0xff);</span>
<span class="fc" id="L609">                final var g = (sourceBuffer[j + bandOffsets[1]] &amp; 0xff);</span>
<span class="fc" id="L610">                final var b = (sourceBuffer[j + bandOffsets[2]] &amp; 0xff);</span>

<span class="fc" id="L612">                destBuffer[i++] = (r &lt;&lt; 16) | (g &lt;&lt; 8) | b;</span>
<span class="fc" id="L613">                j += pixelStride;</span>
              }

<span class="fc" id="L616">            base += sourceSampleModel.getScanlineStride();</span>
          }
<span class="fc" id="L618">      }</span>

    /*******************************************************************************************************************
     *
     * @param xScale
     * @param yScale
     * @param quality
     * @return
     *
     ******************************************************************************************************************/
    public static BufferedImage createOptimizedImage (final BufferedImage bufferedImage,
                                                      final double xScale,
                                                      final double yScale,
                                                      final Quality quality)
      {
<span class="fc" id="L633">        log.debug(&quot;createOptimizedImage(&quot; + xScale + &quot;, &quot; + yScale + &quot;, &quot; + quality + &quot;)&quot;);</span>

<span class="fc" id="L635">        final var iw = (int)Math.round(xScale * bufferedImage.getWidth());</span>
<span class="fc" id="L636">        final var ih = (int)Math.round(yScale * bufferedImage.getHeight());</span>
<span class="nc" id="L637">        final var image2 = createOptimizedImage(iw, ih);</span>
<span class="nc" id="L638">        final var g = (Graphics2D)image2.getGraphics();</span>

        try
          {
<span class="nc" id="L642">            final var interpolation = findRenderingHintsInterpolation(quality);</span>

            // Workaround for bugs #4886071 and #4705399
            // See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4886071
            // and http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4705399
            /*
             * FIXME: looks like we can avoid this now.
looks like we can avoid this now.
if (!bufferedImage.getColorModel().getColorSpace().isCS_sRGB()) { ICC_Profile iccProfile = ImageUtils.getICCProfile(bufferedImage); String iccProfileName = ImageUtils.getICCProfileName(iccProfile); if (ICC_PROFILES_WORKAROUND.contains(iccProfileName)) { logger.info(&quot;&gt;&gt;&gt;&gt; applying workaround for bugs #4886071 and #4705399 - profile &quot; + iccProfileName); BufferedImage tempImage = createOptimizedImage(bufferedImage.getWidth(), bufferedImage.getHeight()); long time = System.currentTimeMillis(); //BufferedImage image2 = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage * .TYPE_INT_RGB); Graphics g3 = tempImage.getGraphics(); g3.drawImage(bufferedImage, 0, 0, null); g3.dispose(); bufferedImage = tempImage; logger.info(&quot;&gt;&gt;&gt;&gt; workaround applied in &quot; + (System.currentTimeMillis() - time) + &quot; msec.&quot;); } } */ <span class="nc" id="L670"> log.debug(&quot;&gt;&gt;&gt;&gt; applying AffineTransform.getScaleInstance() with RenderingHint: &quot; + interpolation);</span> <span class="nc" id="L672"> final var renderingHintSave = g.getRenderingHint(RenderingHints.KEY_INTERPOLATION);</span> <span class="nc" id="L673"> g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, interpolation);</span> final var transform = <span class="nc bnc" id="L676" title="All 4 branches missed."> ((xScale == 1.0) &amp;&amp; (yScale == 1.0)) ? null : AffineTransform.getScaleInstance(xScale, yScale);</span> <span class="nc" id="L677"> g.drawRenderedImage(bufferedImage, transform);</span> <span class="nc bnc" id="L679" title="All 2 branches missed."> if (renderingHintSave != null)</span> { <span class="nc" id="L681"> g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, renderingHintSave);</span> } <span class="nc" id="L684"> log.debug(&quot;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; iccProfile is now: &quot; + Java2DUtils.getICCProfileName(image2));</span> } finally { <span class="nc" id="L689"> g.dispose();</span> } <span class="nc" id="L692"> return image2;</span> } /******************************************************************************************************************* * * ******************************************************************************************************************/ public static Object findRenderingHintsInterpolation (final Quality quality) throws IllegalArgumentException { <span class="nc bnc" id="L702" title="All 2 branches missed."> if (!renderingHintsQualityMap.containsKey(quality))</span> { <span class="nc" id="L704"> throw new IllegalArgumentException(quality.toString());</span> } <span class="nc" id="L707"> return renderingHintsQualityMap.get(quality);</span> } /******************************************************************************************************************* * * ******************************************************************************************************************/ public static int findAffineTransformInterpolation (final Quality quality) throws IllegalArgumentException { <span class="nc bnc" id="L717" title="All 2 branches missed."> if (!affineTransformQualityMap.containsKey(quality))</span> { <span class="nc" id="L719"> throw new IllegalArgumentException(quality.toString());</span> } <span class="nc" id="L722"> return affineTransformQualityMap.get(quality);</span> } /******************************************************************************************************************* * * @param n * @return * ******************************************************************************************************************/ public static Kernel getAveragingKernel (final int n) { <span class="nc" id="L733"> Kernel kernel = null;</span> <span class="nc bnc" id="L735" title="All 2 branches missed."> if ((n &amp; 1) == 1)</span> { <span class="nc" id="L737"> kernel = getOddAveragingKernel(n);</span> } else { <span class="nc" id="L742"> kernel = getEvenAveragingKernel(n);</span> } // log.debug(&quot;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; Kernel: &quot; + kernel); <span class="nc" id="L746"> return kernel;</span> } /******************************************************************************************************************* * * @param n * @return * ******************************************************************************************************************/ private static Kernel getEvenAveragingKernel (final int n) { <span class="nc" id="L757"> final var r = n + 1;</span> <span class="nc" id="L758"> final var totalCount = r * r;</span> <span class="nc" id="L759"> final var coreCount = (r - 2) * (r - 2);</span> <span class="nc" id="L760"> final var extCount = totalCount - coreCount;</span> <span class="nc" id="L761"> final var a = 1.0f / ((coreCount * 2) + extCount); // core count double</span> <span class="nc" id="L762"> final var coreValue = a * 2;</span> <span class="nc" id="L763"> final var result = new float[totalCount];</span> <span class="nc" id="L765"> var j = 0;</span> <span class="nc bnc" id="L767" title="All 2 branches missed."> for (var i = 0; i &lt; r; i++)</span> { <span class="nc" id="L769"> result[j++] = a;</span> } <span class="nc bnc" id="L772" title="All 2 branches missed."> for (var k = 0; k &lt; (r - 2); k++)</span> { <span class="nc" id="L774"> result[j++] = a;</span> <span class="nc bnc" id="L776" title="All 2 branches missed."> for (var h = 0; h &lt; (r - 2); h++)</span> { <span class="nc" id="L778"> result[j++] = coreValue;</span> } <span class="nc" id="L781"> result[j++] = a;</span> } <span class="nc bnc" id="L784" title="All 2 branches missed."> for (var i = 0; i &lt; r; i++)</span> { <span class="nc" id="L786"> result[j++] = a;</span> } <span class="nc bnc" id="L788" title="All 4 branches missed."> assert j == totalCount;</span> <span class="nc" id="L790"> return new Kernel2(r, r, result);</span> } /******************************************************************************************************************* * * @param n * @return * ******************************************************************************************************************/ private static Kernel getOddAveragingKernel (final int n) { <span class="nc" id="L801"> final var totalCount = n * n;</span> <span class="nc" id="L802"> final var v = 1.0f / (totalCount);</span> <span class="nc" id="L803"> final var result = new float[totalCount];</span> <span class="nc" id="L804"> Arrays.fill(result, v);</span> <span class="nc" id="L806"> return new Kernel2(n, n, result);</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>