Package org.codemap.tasks

Source Code of org.codemap.tasks.ComputeBackgroundTask$FastBackgroundRenderer

package org.codemap.tasks;

import static org.codemap.CodemapCore.colorScheme;

import org.codemap.DigitalElevationModel;
import org.codemap.HillShading;
import org.codemap.Location;
import org.codemap.MapInstance;
import org.codemap.MapSetting;
import org.codemap.internal.DEMAlgorithm;
import org.codemap.util.MColor;
import org.codemap.util.MapScheme;
import org.codemap.util.StopWatch;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.widgets.Display;

import ch.akuhn.util.ProgressMonitor;
import ch.akuhn.values.Arguments;
import ch.akuhn.values.TaskValue;
import ch.akuhn.values.Value;

public class ComputeBackgroundTask extends TaskValue<Image> {
   
    public static final MapSetting<Integer> SHORELINE_HEIGHT = MapSetting.define("SHORELINE_HEIGHT", 2);
    public static final MapSetting<Integer> HILLLINE_HEIGHT = MapSetting.define("HILLLINE_HEIGHT", 10);   


    public ComputeBackgroundTask(
            Value<MapInstance> mapInstance,
            Value<DigitalElevationModel>  elevationModel,
            Value<HillShading> shading,
            Value<MapScheme<MColor>> colors) {
        super("Caching background image", mapInstance, elevationModel, shading, colors);
        this.doesNotRequireAllArguments();
    }

    @Override
    protected Image computeValue(ProgressMonitor monitor, Arguments arguments) {
        MapInstance mapInstance = arguments.nextOrNull();
        if (mapInstance == null) return null;

        DigitalElevationModel elevationModel = getCurrentElevationModel(arguments, mapInstance);
        HillShading hillShading = getCurrentHillShading(arguments, mapInstance);
        MapScheme<MColor> colors = getCurrentColorScheme(arguments);
        return computeValue(monitor, mapInstance, elevationModel, hillShading, colors);
    }

    private Image computeValue(ProgressMonitor monitor, MapInstance mapInstance, DigitalElevationModel elevationModel, HillShading hillShading, MapScheme<MColor> colors) {
        int mapSize = mapInstance.getWidth();
        Device device = Display.getCurrent();
        Image image = new Image(device, mapSize, mapSize);
        GC gc = new GC(image);
        this.paintWater(monitor, gc);       
        this.paintBackground(monitor, gc, mapInstance, elevationModel, hillShading, colors);
        gc.dispose();
        return image;
    }

    private MapScheme<MColor> getCurrentColorScheme(Arguments arguments) {
        MapScheme<MColor> colors = arguments.nextOrNull();
        if (colors == null) colors = MapScheme.with(MColor.HILLGREEN);
        return colors;
    }

    private DigitalElevationModel getCurrentElevationModel(Arguments arguments, MapInstance mapInstance) {
        DigitalElevationModel elevationModel = arguments.nextOrNull();
        if (elevationModel != null && mapInstance.getWidth() != elevationModel.getSize())
            elevationModel = null;
        return elevationModel;
    }

    private HillShading getCurrentHillShading(Arguments arguments, MapInstance mapInstance) {
        HillShading hillShading = arguments.nextOrNull();
        if (hillShading != null && mapInstance.getWidth() != hillShading.getSize())
            hillShading = null;
        return hillShading;
    }

    private void paintBackground(ProgressMonitor monitor, GC gc, MapInstance mapInstance, DigitalElevationModel elevationModel, HillShading hillShading, MapScheme<MColor> colors) {
        if (elevationModel == null) {
            StopWatch stopWatch = new StopWatch("Background (Draft)").start();
            paintDraft(monitor, gc, mapInstance);
            stopWatch.printStop();
        }
        else {
            StopWatch stopWatch = new StopWatch("Background").start();
            paintHills(monitor, gc, mapInstance, elevationModel, hillShading, colors);
            stopWatch.printStop();
        }
    }

    private void paintWater(ProgressMonitor monitor, GC gc) {
            if (monitor.isCanceled()) return;
           
            Color blue = colorScheme().getWaterColor().asSWTColor(gc.getDevice());
            gc.setBackground(blue);
            gc.fillRectangle(gc.getClipping());
            blue.dispose();
    }

    private void paintDraft(ProgressMonitor monitor, GC gc, MapInstance map) {
        if (monitor.isCanceled()) return;
        if (map == null) return;
       
        Color shore = colorScheme().getShoreColor().asSWTColor(gc.getDevice());
        gc.setForeground(shore);
        gc.setLineWidth(2);
        for (Location each: map.locations()) {
            if (monitor.isCanceled()) break;
            int r = (int) (each.getElevation() * 2 * map.getWidth() / DEMAlgorithm.MAGIC_VALUE);
            gc.drawOval(each.px - r, each.py - r, r * 2, r * 2);
        }
        shore.dispose();
    }

    private void paintHills(ProgressMonitor monitor, GC gc, MapInstance mapInstance, DigitalElevationModel elevationModel, HillShading hillShading, MapScheme<MColor> colors) {
        if (monitor.isCanceled()) return;
        if (hillShading == null) return;
               
        float[][] DEM = elevationModel.asFloatArray();
        double[][] shade = hillShading.asDoubleArray();
       
        // set black background so that the transparency works
        // getting it via SWT.COLOR_BLACK raises invalid Thread access
        // and it don't want to fire up a runnable here ...
        Color black = new Color(gc.getDevice(), 0, 0, 0);
        gc.setBackground(black);
        gc.fillRectangle(gc.getClipping());
        black.dispose();
       
        Image background = new FastBackgroundRenderer(DEM, shade, mapInstance, colors, gc.getDevice()).render();
       

        gc.drawImage(background, 0, 0);
    }

    @Override
    protected void maybeDisposeValue(Image previous, Image newValue) {
        if (previous != null && !previous.equals(newValue)) previous.dispose();
    }
   
    public static class FastBackgroundRenderer {

        private Device device;
        private int mapSize;
        private double[][] shade;
        private float[][] DEM;
        private MapInstance map;
        private MapScheme<MColor> colors;

        public FastBackgroundRenderer(float[][] DEM, double[][] shade, MapInstance mapInstance, MapScheme<MColor> colors, Device device) {
            this.DEM = DEM;
            this.shade = shade;
            this.map = mapInstance;
            this.mapSize = mapInstance.getWidth();           
            this.device = device;
            this.colors = colors;
        }
       
        /**
         * Performance optimized rendering,
         * uses byte[] for RGB color information and transparency.
         */
        public Image render() {
            // 1 byte per color, we fill 24bit per pixel
            byte[] imageBytes = new byte[mapSize*mapSize*3];
            byte[] alphaBytes = new byte[mapSize*mapSize];
           
            // colors needed later
            byte[] waterColor = colorScheme().getWaterColor().asByte();
            byte[] shoreColor = colorScheme().getShoreColor().asByte();
           
            int shorelineHeight = map.get(SHORELINE_HEIGHT);
            int hillineHeight = map.get(HILLLINE_HEIGHT);
           
            StopWatch nnStopWatch = new StopWatch("nearest neighbor (total)");
            for(int i=0; i < mapSize*mapSize; i++) {
                int y = i / mapSize;
                int x = i % mapSize;
               
                if (DEM[x][y] <= shorelineHeight) {
                    // water color
                    System.arraycopy(waterColor, 0, imageBytes, i*3, 3);
                    alphaBytes[i] = (byte) 255;
                    continue;
                } else if (DEM[x][y] <= hillineHeight) {
                    // shore colors
                    System.arraycopy(shoreColor, 0, imageBytes, i*3, 3);
                    alphaBytes[i] = (byte) 255;
                    continue;
                }
                // get color from location a.k.a. hill colors
                nnStopWatch.start();
                Location nearestNeighbor = map.nearestNeighbor(x, y);
                MColor mcolor;
                if (nearestNeighbor == null) {
                    mcolor = colorScheme().getHillColor();
                } else {
                    mcolor = colors.forLocation(nearestNeighbor.getPoint());                   
                }
                nnStopWatch.stop();
                // make rgb
                int baseIndex = i*3;
                imageBytes[baseIndex++] = (byte) mcolor.getRed(); // R
                imageBytes[baseIndex++] = (byte) mcolor.getGreen(); // G
                imageBytes[baseIndex++] = (byte) mcolor.getBlue(); // B
               
                // make alpha
                // 0 for fully transparent
                // 255 for fully opaque
                double f = shade[x][y];
                assert f <=1;
                // alpha values can get negative somehow so we fix that
                int alpha = (int) Math.max(0.0, 255*f);
                alphaBytes[i] = (byte) alpha;
            }
            nnStopWatch.print();
            // define a direct palette with masks for RGB
            PaletteData palette = new PaletteData(0xFF0000 , 0xFF00 , 0xFF);  
            ImageData imageData = new ImageData(mapSize, mapSize, 24, palette, mapSize*3, imageBytes);
            // enable alpha by setting alphabytes ... strange that i can't do that per pixel using 32bit values
            imageData.alphaData = alphaBytes;
            return new Image(device, imageData);           
        }
    }
}
TOP

Related Classes of org.codemap.tasks.ComputeBackgroundTask$FastBackgroundRenderer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.