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 Operations</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. 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>