// Path: mapsforge-core/src/main/java/org/mapsforge/core/util/Parameters.java
// public final class Parameters {
// 
//     public enum ParentTilesRendering {QUALITY, SPEED, OFF}
// 
//     public enum SymbolScaling {ALL, POI}
// 
//     /**
//      * If true will use anti-aliasing in rendering.
//      */
//     public static boolean ANTI_ALIASING = true;
// 
//     /**
//      * If true the <code>FrameBufferHA3</code> will be used instead of default <code>FrameBufferHA2}</code>.
//      */
//     public static boolean FRAME_BUFFER_HA3 = true;
// 
//     /**
//      * Process layer scroll events.
//      */
//     public static boolean LAYER_SCROLL_EVENT = false;
// 
//     /**
//      * Maximum buffer size for map files.
//      */
//     public static int MAXIMUM_BUFFER_SIZE = 10000000;
// 
//     /**
//      * The default number of threads is one greater than the number of processors, as one thread is
//      * likely to be blocked on I/O reading map data. Technically this value can change to a better
//      * implementation, maybe one that also takes the available memory into account would be good.
//      * For stability reasons (see #591), we set default number of threads to 1.
//      */
//     public static int NUMBER_OF_THREADS = 1;//Runtime.getRuntime().availableProcessors() + 1;
// 
//     /**
//      * Parent tiles rendering mode.
//      */
//     public static ParentTilesRendering PARENT_TILES_RENDERING = ParentTilesRendering.QUALITY;
// 
//     /**
//      * If square frame buffer is enabled, the frame buffer allocated for drawing will be
//      * large enough for drawing in either orientation, so no change is needed when the device
//      * orientation changes. To avoid overly large frame buffers, the aspect ratio for this policy
//      * determines when this will be used.
//      */
//     public static boolean SQUARE_FRAME_BUFFER = true;
// 
//     /**
//      * Symbol scaling mode.
//      */
//     public static SymbolScaling SYMBOL_SCALING = SymbolScaling.POI;
// 
//     /**
//      * Validate coordinates.
//      */
//     public static boolean VALIDATE_COORDINATES = true;
// 
//     private Parameters() {
//         throw new IllegalStateException();
//     }
// }


import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Rectangle2D;
import java.awt.image.*;
import java.util.AbstractMap;
import java.util.Map;
import org.mapsforge.core.graphics.Canvas;
import org.mapsforge.core.graphics.Color;
import org.mapsforge.core.graphics.Paint;
import org.mapsforge.core.graphics.*;
import org.mapsforge.core.model.Dimension;
import org.mapsforge.core.model.Rectangle;
import org.mapsforge.core.util.Parameters;
import java.awt.*;
import java.awt.color.ColorSpace;
/*
 * Copyright 2010, 2011, 2012, 2013 mapsforge.org
 * Copyright 2014-2021 devemux86
 * Copyright 2017 usrusr
 * Copyright 2019 cpt1gl0
 * Copyright 2019 Adrian Batzill
 * Copyright 2019 Matthew Egeler
 * Copyright 2019 mg4gh
 *
 * This program is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.mapsforge.map.awt.graphics;



class AwtCanvas implements Canvas {
    private static final String UNKNOWN_STYLE = "unknown style: ";

    private BufferedImage bufferedImage;
    private Graphics2D graphics2D;
    private BufferedImageOp grayscaleOp, invertOp, invertOp4;

    private static final java.awt.Color NEUTRAL_HILLS = new java.awt.Color(127, 127, 127);
    private static Map.Entry<Float, Composite> sizeOneShadingCompositeCache = null;
    private final AffineTransform transform = new AffineTransform();

    private static Composite getHillshadingComposite(float magnitude) {
        Map.Entry<Float, Composite> existing = sizeOneShadingCompositeCache;
        if (existing != null && existing.getKey() == magnitude) {
            // JMM says: "A thread-safe immutable object is seen as immutable by all threads, even
            // if a data race is used to pass references to the immutable object between threads"
            // worst case we construct more than strictly needed
            return existing.getValue();
        }

        Composite selected = selectHillShadingComposite(magnitude);

        if (sizeOneShadingCompositeCache == null) {
            // only cache the first magnitude value, in the rare instance that more than one
            // magnitude value would be used in a process lifecycle it would be better to create
            // new Composite instances than create new instances _and_ new cache entries
            sizeOneShadingCompositeCache = new AbstractMap.SimpleImmutableEntry<>(magnitude, selected);
        }

        return selected;
    }

    /**
     * Composite selection, select between {@link AlphaComposite} (fast, squashes saturation at high magnitude)
     * and {@link AwtLuminanceShadingComposite} (per-pixel rgb->hsv->rgb conversion to keep saturation at high magnitude,
     * might be a bit slow and/or inconsistent with the android implementation)
     */
    private static Composite selectHillShadingComposite(float magnitude) {
        //return new AwtLuminanceShadingComposite(magnitude);
        return AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, magnitude);
    }

    AwtCanvas() {
        createFilters();
    }

    AwtCanvas(Graphics2D graphics2D) {
        this.graphics2D = graphics2D;
// Path: mapsforge-core/src/main/java/org/mapsforge/core/util/Parameters.java
// public final class Parameters {
// 
//     public enum ParentTilesRendering {QUALITY, SPEED, OFF}
// 
//     public enum SymbolScaling {ALL, POI}
// 
//     /**
//      * If true will use anti-aliasing in rendering.
//      */
//     public static boolean ANTI_ALIASING = true;
// 
//     /**
//      * If true the <code>FrameBufferHA3</code> will be used instead of default <code>FrameBufferHA2}</code>.
//      */
//     public static boolean FRAME_BUFFER_HA3 = true;
// 
//     /**
//      * Process layer scroll events.
//      */
//     public static boolean LAYER_SCROLL_EVENT = false;
// 
//     /**
//      * Maximum buffer size for map files.
//      */
//     public static int MAXIMUM_BUFFER_SIZE = 10000000;
// 
//     /**
//      * The default number of threads is one greater than the number of processors, as one thread is
//      * likely to be blocked on I/O reading map data. Technically this value can change to a better
//      * implementation, maybe one that also takes the available memory into account would be good.
//      * For stability reasons (see #591), we set default number of threads to 1.
//      */
//     public static int NUMBER_OF_THREADS = 1;//Runtime.getRuntime().availableProcessors() + 1;
// 
//     /**
//      * Parent tiles rendering mode.
//      */
//     public static ParentTilesRendering PARENT_TILES_RENDERING = ParentTilesRendering.QUALITY;
// 
//     /**
//      * If square frame buffer is enabled, the frame buffer allocated for drawing will be
//      * large enough for drawing in either orientation, so no change is needed when the device
//      * orientation changes. To avoid overly large frame buffers, the aspect ratio for this policy
//      * determines when this will be used.
//      */
//     public static boolean SQUARE_FRAME_BUFFER = true;
// 
//     /**
//      * Symbol scaling mode.
//      */
//     public static SymbolScaling SYMBOL_SCALING = SymbolScaling.POI;
// 
//     /**
//      * Validate coordinates.
//      */
//     public static boolean VALIDATE_COORDINATES = true;
// 
//     private Parameters() {
//         throw new IllegalStateException();
//     }
// }


// Path: mapsforge-map-awt/src/main/java/org/mapsforge/map/awt/graphics/AwtCanvas.java
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Rectangle2D;
import java.awt.image.*;
import java.util.AbstractMap;
import java.util.Map;
import org.mapsforge.core.graphics.Canvas;
import org.mapsforge.core.graphics.Color;
import org.mapsforge.core.graphics.Paint;
import org.mapsforge.core.graphics.*;
import org.mapsforge.core.model.Dimension;
import org.mapsforge.core.model.Rectangle;
import org.mapsforge.core.util.Parameters;
import java.awt.*;
import java.awt.color.ColorSpace;

/*
 * Copyright 2010, 2011, 2012, 2013 mapsforge.org
 * Copyright 2014-2021 devemux86
 * Copyright 2017 usrusr
 * Copyright 2019 cpt1gl0
 * Copyright 2019 Adrian Batzill
 * Copyright 2019 Matthew Egeler
 * Copyright 2019 mg4gh
 *
 * This program is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.mapsforge.map.awt.graphics;



class AwtCanvas implements Canvas {
    private static final String UNKNOWN_STYLE = "unknown style: ";

    private BufferedImage bufferedImage;
    private Graphics2D graphics2D;
    private BufferedImageOp grayscaleOp, invertOp, invertOp4;

    private static final java.awt.Color NEUTRAL_HILLS = new java.awt.Color(127, 127, 127);
    private static Map.Entry<Float, Composite> sizeOneShadingCompositeCache = null;
    private final AffineTransform transform = new AffineTransform();

    private static Composite getHillshadingComposite(float magnitude) {
        Map.Entry<Float, Composite> existing = sizeOneShadingCompositeCache;
        if (existing != null && existing.getKey() == magnitude) {
            // JMM says: "A thread-safe immutable object is seen as immutable by all threads, even
            // if a data race is used to pass references to the immutable object between threads"
            // worst case we construct more than strictly needed
            return existing.getValue();
        }

        Composite selected = selectHillShadingComposite(magnitude);

        if (sizeOneShadingCompositeCache == null) {
            // only cache the first magnitude value, in the rare instance that more than one
            // magnitude value would be used in a process lifecycle it would be better to create
            // new Composite instances than create new instances _and_ new cache entries
            sizeOneShadingCompositeCache = new AbstractMap.SimpleImmutableEntry<>(magnitude, selected);
        }

        return selected;
    }

    /**
     * Composite selection, select between {@link AlphaComposite} (fast, squashes saturation at high magnitude)
     * and {@link AwtLuminanceShadingComposite} (per-pixel rgb->hsv->rgb conversion to keep saturation at high magnitude,
     * might be a bit slow and/or inconsistent with the android implementation)
     */
    private static Composite selectHillShadingComposite(float magnitude) {
        //return new AwtLuminanceShadingComposite(magnitude);
        return AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, magnitude);
    }

    AwtCanvas() {
        createFilters();
    }

    AwtCanvas(Graphics2D graphics2D) {
        this.graphics2D = graphics2D;
        setAntiAlias(Parameters.ANTI_ALIASING);