<commit_before>package org.helioviewer.jhv.view.jp2view.kakadu;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;

import javax.annotation.Nullable;

import kdu_jni.Jp2_input_box;
import kdu_jni.Jp2_palette;
import kdu_jni.Jp2_threadsafe_family_src;
import kdu_jni.Jpx_codestream_source;
import kdu_jni.Jpx_input_box;
import kdu_jni.Jpx_meta_manager;
import kdu_jni.Jpx_metanode;
import kdu_jni.Jpx_source;
import kdu_jni.KduException;
import kdu_jni.Kdu_cache;
import kdu_jni.Kdu_channel_mapping;
import kdu_jni.Kdu_codestream;
import kdu_jni.Kdu_coords;
import kdu_jni.Kdu_dims;
import kdu_jni.Kdu_global;
import kdu_jni.Kdu_ilayer_ref;
import kdu_jni.Kdu_quality_limiter;
import kdu_jni.Kdu_region_compositor;

import org.helioviewer.jhv.math.MathUtils;
import org.helioviewer.jhv.metadata.MetaData;
import org.helioviewer.jhv.metadata.XMLMetaDataContainer;
import org.helioviewer.jhv.view.jp2view.image.ResolutionSet;

public class KakaduSource {

    private final Jp2_threadsafe_family_src familySrc = new Jp2_threadsafe_family_src(); // reference has to be maintained
    private final Jpx_source jpxSrc;
    private Kdu_region_compositor compositor;

    public KakaduSource(Kdu_cache cache, URI uri) throws KduException, IOException {
        if (cache == null) { // local
            File file = new File(uri);
            familySrc.Open(file.getCanonicalPath(), true);
        } else {
            familySrc.Open(cache);
        }

        jpxSrc = new Jpx_source();
        jpxSrc.Open(familySrc, false);
    }

    public Kdu_region_compositor getCompositor() throws KduException {
        if (compositor == null)
            compositor = createCompositor(jpxSrc);
        return compositor;
    }

    public int getNumberLayers() throws KduException {
        int[] temp = new int[1];
        jpxSrc.Count_compositing_layers(temp);
        return temp[0];
    }

    public ResolutionSet getResolutionSet(int frame) throws KduException {
        Jpx_codestream_source xstream = jpxSrc.Access_codestream(frame);
        Jpx_input_box inputBox = xstream.Open_stream();
        Kdu_codestream stream = new Kdu_codestream();
        stream.Create(inputBox);
        if (!stream.Exists()) {
            throw new KduException(">> stream does not exist " + frame);
        }

        // Since it gets tricky here I am just grabbing a bunch of values
        // and taking the max of them. It is acceptable to think that an
        // image is color when its not monochromatic, but not the other way
        // around... so this is just playing it safe.
        Kdu_channel_mapping cmap = new Kdu_channel_mapping();
        cmap.Configure(stream);

        int maxComponents = MathUtils.max(cmap.Get_num_channels(), cmap.Get_num_colour_channels(), stream.Get_num_components(true), stream.Get_num_components(false));
        // numComponents = maxComponents == 1 ? 1 : 3;
        // With new file formats we may have 2 components
        cmap.Clear();
        cmap.Native_destroy();

        int maxDWT = stream.Get_min_dwt_levels();
        ResolutionSet res = new ResolutionSet(maxDWT + 1, maxComponents);

        Kdu_dims dims = new Kdu_dims();
        stream.Get_dims(0, dims);
        Kdu_coords siz = dims.Access_size();
        int width0 = siz.Get_x(), height0 = siz.Get_y();
        res.addResolutionLevel(0, width0, height0, 1, 1);

        for (int i = 1; i <= maxDWT; i++) {
            stream.Apply_input_restrictions(0, 0, i, 0, null, Kdu_global.KDU_WANT_CODESTREAM_COMPONENTS);
            stream.Get_dims(0, dims);
            siz = dims.Access_size();
            int width = siz.Get_x(), height = siz.Get_y();
            res.addResolutionLevel(i, width, height, width0 / (double) width, height0 / (double) height);
        }

        stream.Destroy();
        inputBox.Close();
        inputBox.Native_destroy();

        return res;
    }

    @Nullable
    public int[] getLUT() throws KduException {
        Jpx_codestream_source xstream = jpxSrc.Access_codestream(0);
        if (!xstream.Exists()) {
            throw new KduException(">> stream does not exist");
        }
        Jp2_palette palette = xstream.Access_palette();

        int numLUTs = palette.Get_num_luts();
        if (numLUTs == 0)
            return null;

        int[] lut = new int[palette.Get_num_entries()];
        float[] red = new float[lut.length];
        float[] green = new float[lut.length];
        float[] blue = new float[lut.length];

        palette.Get_lut(0, red, Kdu_global.JP2_CHANNEL_FORMAT_DEFAULT);
        palette.Get_lut(1, green, Kdu_global.JP2_CHANNEL_FORMAT_DEFAULT);
        palette.Get_lut(2, blue, Kdu_global.JP2_CHANNEL_FORMAT_DEFAULT);

        for (int i = 0; i < lut.length; i++) {
            lut[i] = 0xFF000000 | ((int) ((red[i] + 0.5f) * 0xFF) << 16) | ((int) ((green[i] + 0.5f) * 0xFF) << 8) | ((int) ((blue[i] + 0.5f) * 0xFF));
        }
        return lut;
    }

    /*
     * Deactivates the internal color lookup table for the given composition layer
     *
     * It is not allowed to call this function for a layer which is not loaded yet.
     *
     * @param numLayer
     *            composition layer to deactivate internal color lookup for
     */
    /*
        in preservation - not needed
        void deactivateColorLookupTable(int numLayer) throws KduException {
            for (int i = 0; i < numLUTs; i++) {
                jpxSrc.Access_layer(numLayer).Access_channels().Set_colour_mapping(i, 0, -1, numLayer);
            }
        }
    */

    @Override
    protected void finalize() throws Throwable {
        try {
            destroyCompositor(compositor);
        } catch (KduException e) {
            e.printStackTrace();
        } finally {
            super.finalize();
        }
    }

    private static Kdu_region_compositor createCompositor(Jpx_source jpx) throws KduException {
        Kdu_region_compositor compositor = new Kdu_region_compositor();
        // System.out.println(">>>> compositor create " + compositor + " " + Thread.currentThread().getName());
        compositor.Create(jpx, KakaduConstants.CODESTREAM_CACHE_THRESHOLD);
        compositor.Set_surface_initialization_mode(false);
        compositor.Set_quality_limiting(new Kdu_quality_limiter(1f/256), -1, -1);
        return compositor;
    }

    private static void destroyCompositor(Kdu_region_compositor compositor) throws KduException {
        // System.out.println(">>>> compositor destroy " + compositor + " " + Thread.currentThread().getName());
        compositor.Halt_processing();
        compositor.Remove_ilayer(new Kdu_ilayer_ref(), true);
        compositor.Set_thread_env(null, null);
        compositor.Native_destroy();
        compositor = null;
    }

    private static final long[] xmlFilter = { Kdu_global.jp2_xml_4cc };

    public void extractMetaData(MetaData[] metaDataList) throws Exception {
        Jpx_meta_manager metaManager = jpxSrc.Access_meta_manager();
        Jpx_metanode node = new Jpx_metanode();
        int i = 0;

        Jp2_input_box xmlBox = new Jp2_input_box();
        while ((node = metaManager.Peek_and_clear_touched_nodes(1, xmlFilter, node)).Exists()) {
            if (i == metaDataList.length)
                return;
            if (node.Open_existing(xmlBox)) {
                metaDataList[i] = new XMLMetaDataContainer(xmlBox2String(xmlBox)).getHVMetaData(i);
                xmlBox.Close();
            }
            i++;
        }
    }

    public String extractXMLString(int frame) throws KduException {
        Jpx_meta_manager metaManager = jpxSrc.Access_meta_manager();
        Jpx_metanode node = new Jpx_metanode();
        int i = 0;
<commit_msg>Clean<commit_after>package org.helioviewer.jhv.view.jp2view.kakadu;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;

import javax.annotation.Nullable;

import kdu_jni.Jp2_input_box;
import kdu_jni.Jp2_palette;
import kdu_jni.Jp2_threadsafe_family_src;
import kdu_jni.Jpx_codestream_source;
import kdu_jni.Jpx_input_box;
import kdu_jni.Jpx_meta_manager;
import kdu_jni.Jpx_metanode;
import kdu_jni.Jpx_source;
import kdu_jni.KduException;
import kdu_jni.Kdu_cache;
import kdu_jni.Kdu_channel_mapping;
import kdu_jni.Kdu_codestream;
import kdu_jni.Kdu_coords;
import kdu_jni.Kdu_dims;
import kdu_jni.Kdu_global;
import kdu_jni.Kdu_ilayer_ref;
import kdu_jni.Kdu_quality_limiter;
import kdu_jni.Kdu_region_compositor;

import org.helioviewer.jhv.math.MathUtils;
import org.helioviewer.jhv.metadata.MetaData;
import org.helioviewer.jhv.metadata.XMLMetaDataContainer;
import org.helioviewer.jhv.view.jp2view.image.ResolutionSet;

public class KakaduSource {

    private final Jp2_threadsafe_family_src familySrc = new Jp2_threadsafe_family_src(); // reference has to be maintained
    private final Jpx_source jpxSrc;
    private Kdu_region_compositor compositor;

    public KakaduSource(Kdu_cache cache, URI uri) throws KduException, IOException {
        if (cache == null) { // local
            File file = new File(uri);
            familySrc.Open(file.getCanonicalPath(), true);
        } else {
            familySrc.Open(cache);
        }

        jpxSrc = new Jpx_source();
        jpxSrc.Open(familySrc, false);
    }

    public Kdu_region_compositor getCompositor() throws KduException {
        if (compositor == null)
            compositor = createCompositor(jpxSrc);
        return compositor;
    }

    public int getNumberLayers() throws KduException {
        int[] temp = new int[1];
        jpxSrc.Count_compositing_layers(temp);
        return temp[0];
    }

    public ResolutionSet getResolutionSet(int frame) throws KduException {
        Jpx_codestream_source xstream = jpxSrc.Access_codestream(frame);
        Jpx_input_box inputBox = xstream.Open_stream();
        Kdu_codestream stream = new Kdu_codestream();
        stream.Create(inputBox);
        if (!stream.Exists()) {
            throw new KduException(">> stream does not exist " + frame);
        }

        // Since it gets tricky here I am just grabbing a bunch of values
        // and taking the max of them. It is acceptable to think that an
        // image is color when its not monochromatic, but not the other way
        // around... so this is just playing it safe.
        Kdu_channel_mapping cmap = new Kdu_channel_mapping();
        cmap.Configure(stream);

        int maxComponents = MathUtils.max(cmap.Get_num_channels(), cmap.Get_num_colour_channels(), stream.Get_num_components(true), stream.Get_num_components(false));
        // numComponents = maxComponents == 1 ? 1 : 3;
        // With new file formats we may have 2 components
        cmap.Clear();
        cmap.Native_destroy();

        int maxDWT = stream.Get_min_dwt_levels();
        ResolutionSet res = new ResolutionSet(maxDWT + 1, maxComponents);

        Kdu_dims dims = new Kdu_dims();
        stream.Get_dims(0, dims);
        Kdu_coords siz = dims.Access_size();
        int width0 = siz.Get_x(), height0 = siz.Get_y();
        res.addResolutionLevel(0, width0, height0, 1, 1);

        for (int i = 1; i <= maxDWT; i++) {
            stream.Apply_input_restrictions(0, 0, i, 0, null, Kdu_global.KDU_WANT_CODESTREAM_COMPONENTS);
            stream.Get_dims(0, dims);
            siz = dims.Access_size();
            int width = siz.Get_x(), height = siz.Get_y();
            res.addResolutionLevel(i, width, height, width0 / (double) width, height0 / (double) height);
        }

        stream.Destroy();
        inputBox.Close();
        inputBox.Native_destroy();

        return res;
    }

    @Nullable
    public int[] getLUT() throws KduException {
        Jpx_codestream_source xstream = jpxSrc.Access_codestream(0);
        if (!xstream.Exists()) {
            throw new KduException(">> stream does not exist");
        }
        Jp2_palette palette = xstream.Access_palette();

        int numLUTs = palette.Get_num_luts();
        if (numLUTs == 0)
            return null;

        int[] lut = new int[palette.Get_num_entries()];
        float[] red = new float[lut.length];
        float[] green = new float[lut.length];
        float[] blue = new float[lut.length];

        palette.Get_lut(0, red, Kdu_global.JP2_CHANNEL_FORMAT_DEFAULT);
        palette.Get_lut(1, green, Kdu_global.JP2_CHANNEL_FORMAT_DEFAULT);
        palette.Get_lut(2, blue, Kdu_global.JP2_CHANNEL_FORMAT_DEFAULT);

        for (int i = 0; i < lut.length; i++) {
            lut[i] = 0xFF000000 | ((int) ((red[i] + 0.5f) * 0xFF) << 16) | ((int) ((green[i] + 0.5f) * 0xFF) << 8) | ((int) ((blue[i] + 0.5f) * 0xFF));
        }
        return lut;
    }

    /*
     * Deactivates the internal color lookup table for the given composition layer
     *
     * It is not allowed to call this function for a layer which is not loaded yet.
     *
     * @param numLayer
     *            composition layer to deactivate internal color lookup for
     */
    /*
        in preservation - not needed
        void deactivateColorLookupTable(int numLayer) throws KduException {
            for (int i = 0; i < numLUTs; i++) {
                jpxSrc.Access_layer(numLayer).Access_channels().Set_colour_mapping(i, 0, -1, numLayer);
            }
        }
    */

    @Override
    protected void finalize() throws Throwable {
        try {
            destroyCompositor(compositor);
            compositor = null;
        } catch (KduException e) {
            e.printStackTrace();
        } finally {
            super.finalize();
        }
    }

    private static Kdu_region_compositor createCompositor(Jpx_source jpx) throws KduException {
        Kdu_region_compositor krc = new Kdu_region_compositor();
        // System.out.println(">>>> compositor create " + compositor + " " + Thread.currentThread().getName());
        krc.Create(jpx, KakaduConstants.CODESTREAM_CACHE_THRESHOLD);
        krc.Set_surface_initialization_mode(false);
        krc.Set_quality_limiting(new Kdu_quality_limiter(1f/256), -1, -1);
        return krc;
    }

    private static void destroyCompositor(Kdu_region_compositor krc) throws KduException {
        // System.out.println(">>>> compositor destroy " + compositor + " " + Thread.currentThread().getName());
        krc.Halt_processing();
        krc.Remove_ilayer(new Kdu_ilayer_ref(), true);
        krc.Set_thread_env(null, null);
        krc.Native_destroy();
    }

    private static final long[] xmlFilter = { Kdu_global.jp2_xml_4cc };

    public void extractMetaData(MetaData[] metaDataList) throws Exception {
        Jpx_meta_manager metaManager = jpxSrc.Access_meta_manager();
        Jpx_metanode node = new Jpx_metanode();
        int i = 0;

        Jp2_input_box xmlBox = new Jp2_input_box();
        while ((node = metaManager.Peek_and_clear_touched_nodes(1, xmlFilter, node)).Exists()) {
            if (i == metaDataList.length)
                return;
            if (node.Open_existing(xmlBox)) {
                metaDataList[i] = new XMLMetaDataContainer(xmlBox2String(xmlBox)).getHVMetaData(i);
                xmlBox.Close();
            }
            i++;
        }
    }

    public String extractXMLString(int frame) throws KduException {
        Jpx_meta_manager metaManager = jpxSrc.Access_meta_manager();
        Jpx_metanode node = new Jpx_metanode();
        int i = 0;

{'fraction_non_alphanumeric': 0.10240415854450942, 'fraction_numerical': 0.009746588693957114, 'mean_word_length': 2.743857942106543, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 16}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1216897', 'n_tokens_mistral': 5099, 'n_tokens_neox': 4955, 'n_words': 1088}