Islanti Thingvellir

 

Kuvan asennon korjaaminen

(11.5.2020)
Seuraavassa esitetään sovellus KuvikkuTesti2, joka esittää kuvan scrollien välissä ja huolehtii kuvan oikeasta asennosta. Orientation haetaan metatiedoista ja kuvasta tehdään tarvittaessa peilikuva ja/tai kierretään sopivaan suuntaan. Kuva esitetään scrollattavalla alueella ja tarvittaessa skaalataan mahtumaan kokonaan kuvaikkunaan.

Orientation-tiedon haku ja kuvan kääntämiset perustuvat github.comissa julkaistuun koodiin ja kirjastoihin, jotka on laatinut Drew Noakes. Alla keskeiset funktiot löytyvät luokasta Meka.java

Oheinen sovellus on tehty Eclipsessä (2020-03), ja kyseessä on SWT/JFace Java Project. Tiedosto Kuvaikku2 on tyypiltään SWT Dialog. Sovelluksen käyttöön tarvitaan lisäksi jar-paketit, jotka on ladattavissa github.com:in projektista "metadata-extractor", nimeltään tämän kirjoittamisen hetkellä:
- metadata-extractor-2.13.0.jar
- xmpcore-6.0.6.jar

Sovelluksen funktiossa A03 on sisäänkoodattuna kansiopolut Macille ja Windowsille. Muuta ne vastaamaan omaa ympäristöäsi.


// ************************************************************
// KuvikkuTesti2.java
// *************************************************************'

import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Label;

import java.util.HashMap;

import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;

//-------------------------------------------------
// 10.5.2020
// Kuvan valinta ikkunassa esitettäväksi
public class KuvikkuTesti2
{
    private Shell shell;
    private Text textPolku;
    private Kuvaikku2 kuvaikku;
    private HashMap<String, String> isty;   // kuvatyypit
   
    // --------------------------------------------------
    // 13.4.2020  A99
    // sovelluksen käynnistys
    public static void main(String[] args)
    {
        try
        {
            KuvikkuTesti2 window = new KuvikkuTesti2();
            window.open();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    // --------------------------------------------------
    // 13.4.2020     A98
    // Avataan sovelluksen pääikkuna
    public void open()
    {
        Display display = Display.getDefault();
        createContents();
        shell.open();
        shell.layout();
        aloitusHommia();
        while (!shell.isDisposed())
        {
            if (!display.readAndDispatch())
            {
                display.sleep();
            }
        }
    }

    // --------------------------------------------------
    // 10.5.2020    A01
    // Aloitustoimenpiteet
    private void aloitusHommia()
    {
        // tiedostotyypit
        isty = new HashMap<String, String>();

        isty.put("jpg#", "KUVA");
        isty.put("jpeg#", "KUVA");
        isty.put("gif#", "KUVA");
        isty.put("png#", "KUVA");
        isty.put("tif#", "KUVA");
        isty.put("tiff#", "KUVA");
    }
   
    // --------------------------------------------------
    // 13.4.2020   A03
    // Valitaan kuvatiedosto
    private void buttonValitse()
    {
        FileDialog fd = new FileDialog(shell, SWT.OPEN);
        fd.setText("Open");
        String[] filterExt = { "*.*" };
        String konez = Meka.mikaKone();
        String kanpolz;
        if (konez.equals("MAC"))
            kanpolz = "/Volumes/DATALEVY/KUVAT/Koekuvat";
        else
            kanpolz = "E:/KUVAT/Koekuvat";
        String[] filterNames = { "Kuvatiedostot" };
        fd.setFilterExtensions(filterExt);
        fd.setFilterNames(filterNames);
        fd.setFilterPath(kanpolz);
        String selected = fd.open();
        if (selected != null)
        {
            textPolku.setText(selected);
        }
    }
   
    // --------------------------------------------------
    // 11.5.2020  A02
    // Mennään katsomaan kuvaa
    private void buttonNayta()
    {
        String virhez = "";
        String polz = textPolku.getText();
        if (polz == null || polz.isEmpty())
            virhez = "A02-02: Kuvatiedoston polku on antamatta";
        if (virhez.isEmpty())
        {
            String paatez = Meka.tiedostoPaate(polz);
            String tuz = isty.getOrDefault(paatez.toLowerCase() + "#", "");
            if (tuz.isEmpty())
                virhez = "A02-01: tiedosto ei kelpaa";
        }
        if (virhez.isEmpty())
        {
            if (!(new File(polz)).exists())
                virhez = "A02-03: Kuvatiedoston polku ei kelpaa";
        }
        if (!virhez.isEmpty())
        {
            System.out.println(virhez);
            return;
        }
        kuvaikku = new Kuvaikku2(shell, SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM | SWT.RESIZE);
        System.out.println("A02-04: polz = " + polz);
        kuvaikku.polkuz = polz;
        kuvaikku.infoz = "Amsterdam 15.6.2013";
        kuvaikku.open();
    }
   
    // --------------------------------------------------
    // 13.4.2020    A97
    // Tee sovelluksen pääikkunalle sisältö ja buttoneille toiminnot
    protected void createContents()
    {
        shell = new Shell();
        shell.setSize(450, 300);
        shell.setText("SWT Application");
       
        Label lblNewLabel = new Label(shell, SWT.NONE);
        lblNewLabel.setBounds(25, 21, 200, 15);
        lblNewLabel.setText("Kuvan esittäminen ikkunassa");
       
        Button btnValitse = new Button(shell, SWT.NONE);
        btnValitse.addSelectionListener(new SelectionAdapter()
        {
            @Override
            public void widgetSelected(SelectionEvent e)
            {
                buttonValitse();
            }
        });
        btnValitse.setBounds(20, 56, 120, 25);
        btnValitse.setText("Valitse kuva");
       
        textPolku = new Text(shell, SWT.BORDER);
        textPolku.addSelectionListener(new SelectionAdapter()
        {
            @Override
            public void widgetSelected(SelectionEvent e)
            {
                buttonNayta();
            }
        });
        textPolku.setBounds(20, 87, 399, 21);
       
        Button btnNayta = new Button(shell, SWT.NONE);
        btnNayta.addSelectionListener(new SelectionAdapter()
        {
            @Override
            public void widgetSelected(SelectionEvent e)
            {
                buttonNayta();
            }
        });
        btnNayta.setBounds(20, 130, 120, 25);
        btnNayta.setText("Näytä kuva");
    }
}

// ************************************************************
// Meka.java
// *************************************************************'
import java.io.File;
import java.io.IOException;
import java.util.HashMap;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.widgets.Shell;

import com.drew.imaging.ImageMetadataReader;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.MetadataException;
import com.drew.metadata.exif.ExifIFD0Directory;
import com.drew.metadata.jpeg.JpegDirectory;

// --------------------------------------------------------
//  11.5.2020
public class Meka
{
    // -----------------------------------------------------
    // 10.5.2020   M16
    // Käännetään kuva oikeaan asentoon ennen sen näyttämistä
    // thanks: Drew Noakes, github.com
    public static ImageData kuvaOikaistu(String polkuz)
    {
        // Onko tiedosto olemassa
        boolean ok = true;
        if (polkuz == null || polkuz.isEmpty())
            ok = false
        else if (!(new File(polkuz)).exists())
            ok = false
        if (!ok)
        {
            System.out.println("M16-01: polku tyhjä tai ei tiedostoa");
            return null;
        }

        ImageData imgdata = null;
        int orientation = 1;
        String extz = tiedostoPaate(polkuz).toLowerCase();
        if (extz.equals("jpg") || extz.equals("tif") || extz.equals("jpeg") )
        {
            orientation = kuvaOrient(polkuz);
            if (orientation != 1)
            {
                imgdata = new ImageData(polkuz);
                if (orientation == 4)
                    imgdata = Meka.kuvaFlip(imgdata, true);
                else if (orientation == 2 || orientation == 5 || orientation == 7)
                    imgdata = Meka.kuvaFlip(imgdata, false);
                if (orientation == 8 || orientation == 5 )
                    imgdata = Meka.kuvaRotate(imgdata, SWT.LEFT);
                if (orientation == 7 || orientation == 6 )
                    imgdata = Meka.kuvaRotate(imgdata, SWT.RIGHT);
                if (orientation == 3 )
                    imgdata = Meka.kuvaRotate(imgdata, SWT.DOWN);
            }
        }
        if (imgdata == null)
            imgdata = new ImageData(polkuz);

        return imgdata;
    }
   
    // ----------------------------------------------------
    // 7.5.2020   M10
    // tehdään kuvasta peilikuvansa
    // vertical = true: peilikuva pystysuunnassa, vaakasuuntaisen viivan ympäri
    // thanks: Drew Noakes, github.com
    public static ImageData kuvaFlip(ImageData srcData, boolean vertical)
    {
        int bytesPerPixel = srcData.bytesPerLine / srcData.width;
        int destBytesPerLine = srcData.width * bytesPerPixel;
        byte[] newData = new byte[srcData.data.length];
        for (int srcY = 0; srcY < srcData.height; srcY++)
        {
            for (int srcX = 0; srcX < srcData.width; srcX++)
            {
                int destX = 0, destY = 0, destIndex = 0, srcIndex = 0;
                if (vertical)
                {
                    destX = srcX;
                    destY = srcData.height - srcY - 1;
                }
                else
                {
                    destX = srcData.width - srcX - 1;
                    destY = srcY;
                }
                destIndex = (destY * destBytesPerLine) + (destX * bytesPerPixel);
                srcIndex = (srcY * srcData.bytesPerLine) + (srcX * bytesPerPixel);
                System.arraycopy(srcData.data, srcIndex, newData, destIndex, bytesPerPixel);
            }
        }
        // destBytesPerLine is used as scanlinePad to ensure that no padding is
        // required
        return new ImageData(srcData.width, srcData.height, srcData.depth, srcData.palette,
        destBytesPerLine, newData);
    }
   
    // ------------------------------------------
    // 7.5.2020   M12
    // direction = SWT.LEFT  käännetään 90 astetta vastapäivään
    //   SWT.RIGHT  käännetään 90 astetta myötäpäivään
    //   SWT.DOWN  käännetään 180 astetta
    // thanks: Drew Noakes, github.com
    public static ImageData kuvaRotate(ImageData srcData, int direction)
    {
        int bytesPerPixel = srcData.bytesPerLine / srcData.width;
        int destBytesPerLine = (direction == SWT.DOWN) ? srcData.width * bytesPerPixel
          : srcData.height * bytesPerPixel;
        byte[] newData = new byte[(direction == SWT.DOWN) ? srcData.height * destBytesPerLine
        : srcData.width * destBytesPerLine];
        int width = 0, height = 0;
        for (int srcY = 0; srcY < srcData.height; srcY++)
        {
            for (int srcX = 0; srcX < srcData.width; srcX++)
            {
                int destX = 0, destY = 0, destIndex = 0, srcIndex = 0;
                switch (direction)
                {
                case SWT.LEFT: // left 90 degrees
                    destX = srcY;
                    destY = srcData.width - srcX - 1;
                    width = srcData.height;
                    height = srcData.width;
                    break;
                case SWT.RIGHT: // right 90 degrees
                    destX = srcData.height - srcY - 1;
                    destY = srcX;
                    width = srcData.height;
                    height = srcData.width;
                    break;
                case SWT.DOWN: // 180 degrees
                    destX = srcData.width - srcX - 1;
                    destY = srcData.height - srcY - 1;
                    width = srcData.width;
                    height = srcData.height;
                    break;
                }
                destIndex = (destY * destBytesPerLine) + (destX * bytesPerPixel);
                srcIndex = (srcY * srcData.bytesPerLine) + (srcX * bytesPerPixel);
                System.arraycopy(srcData.data, srcIndex, newData, destIndex, bytesPerPixel);
            }
        }
        // destBytesPerLine is used as scanlinePad to ensure that no padding is required
        return new ImageData(width, height, srcData.depth, srcData.palette, destBytesPerLine, newData);
    }

    //------------------------------------------------------------
    // 10.5.2020  M13
    // tarkista kuvan tiedoista pitääkä kuvaa kääntää
    // tuloksena tarvittava kiertäminen, esimerkiksi
    // BitmapImage bmi = ....
    // bmi.Rotation = Meka.TarkistaAsento(kokopolkuz);
    // orientation = 2 flip horizontally
    // orientation = 4 flip vertically
    // orientation = 5 flip horizontally ( -> 8) & rotate 270
    // orientation = 7 flip horizontally ( -> 6) & rotate 90
    // orientation = 1 ok, nop
    // orientation = 8 rotate 270      SWT.LEFT
    // orientation = 3 rotate 180   SWT.DOWN
    // orientation = 6 rotate 90    SWT.RIGHT
    // thanks: Drew Noakes, github.com
    public static int kuvaOrient(String kokopolkuz)
    {
        int orientation = 1;
        File kufile = new File(kokopolkuz);
        try
        {
            Metadata metadata = ImageMetadataReader.readMetadata(kufile);
            Directory directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);

            try
            {
                orientation = directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);
            }
            catch (MetadataException me)
            {
                System.out.println("M13-08: Could not get orientation");
            }
        }
        catch (Exception e)
        {
            System.out.println("M13-09: Metadataa ei voitu tutkia");
        }

        return orientation;
    }
   
    //-----------------------------------------------------
    // 13.4.2020   MA11
    // Anna tiedostopääte, ei kuitenkaan mukaan pistettä
    // output="", jos ei löydy pistettä tai patz alkaa pisteellä,
    // tai jos vastaan lopusta päin tulee ensin / tai \
    public static String tiedostoPaate(String patz)
    {
        String extz = "";

        int i = patz.lastIndexOf('.');  // -1 jos ei ole pistettä, 0 jos patz alkaa pisteellä
        int p = Math.max(patz.lastIndexOf('/'), patz.lastIndexOf('\\'));

        if (i > p && i != 0)
            extz = patz.substring(i+1);
        return extz;
    }
   
    // ----------------------------------------------------
    // 9.3.2020   M33
    // Missä koneessa olemme: MAC, WIN10, WIN8
   // String mikaz = Meka.mikaKone();  // MAC, WIN10, WIN8
    public static String mikaKone()
    {
        String osz = System.getProperty("os.name").toLowerCase();
        if (osz.indexOf("mac") != -1)
            return "MAC";
        if (osz.indexOf("windows 10") != -1)
            return "WIN10";
        return "WIN8";
    }

}

// ************************************************************
// Kuvaikku2.java
// *************************************************************'
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;

import java.io.File;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
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.custom.ScrolledComposite;

// -----------------------------------------------------------------------------
// 13.4.2020
// Kuvan esittäminen halutun kokoisena
public class Kuvaikku2 extends Dialog
{
    private Object result;
    private Shell shell;
    String polkuz;                      // INPUT  kuvan polku
    String infoz = "(paikka ja pvm)";   // INPUT
    ImageData imgData0;            // alkuperäinen käsiteltävä kuva
    Display display;
    Image image;            // esillä oleva kuva
    Label lblKuva;
    private Color vaalvihr;  // HoneyDew  240,255,240
    ScrolledComposite sc;
   
    // ------------------------------------------
    // 10.5.2020   SA99
    // Luodaan dialogi Kuvaikku2
    public Kuvaikku2(Shell parent, int style)
    {
        super(parent, style);
        setText("SWT Dialog");
    }

    // ------------------------------------------
    // 10.5.2020   SA98
    // Avataan dialogi
    // Kun jossain ikkuna suljetaan (tai dispose), niin tästä dialogi palauttaa tuloksen
    public Object open()
    {
        display = getParent().getDisplay();
        createContents();
        shell.open();
        shell.layout();
        aloitusHommia();
        while (!shell.isDisposed())
        {
            if (!display.readAndDispatch())
            {
                display.sleep();
            }
        }
        vaalvihr.dispose();
        image.dispose();
        return result;
    }

    // ------------------------------------------
    // 10.5.2020    SA01
    // Tutkitaan kuva, ja luetaan sen muuttujaan imgData0
    private void aloitusHommia()
    {
        ImageData imgdata = Meka.kuvaOikaistu(polkuz);
        imgData0 = imgdata;
        image = new Image(display, imgdata);           

        lblKuva.setImage(image);
        sc.setContent(lblKuva);
        sc.setExpandHorizontal(true);
        sc.setExpandVertical(true);
        sc.setMinSize(imgdata.width, imgdata.height);
    }

    // -------------------------------------------
    // 11.4.2020    SA02
    // Näytetään kuva sen kokoisena mikä sopii ikkunaan
    private void buttonSovi()
    {
        int leve = sc.getBounds().width;   // scrollialueen mitat
        int kork = sc.getBounds().height;
        image.dispose();    // poistetaan aiempi kuva
        image = skaalattuKuva(display, imgData0, leve, kork);
        lblKuva.setImage(image);
        String konez = Meka.mikaKone();
        if (konez == "MAC")
             sc.setMinSize(leve, kork);
        else
            sc.setMinSize(leve-10, kork-10);
    }

    // -------------------------------------------
    // 11.4.2020     SA03
    // Näytetään kuva alkuperäisen kokoisena
    private void buttonAlku()
    {
        image.dispose();    // poistetaan aiempi kuva
        image = new Image(display, imgData0);
        lblKuva.setImage(image);
        sc.setMinSize(imgData0.width, imgData0.height);
    }

    // ---------------------------------------------
    // 9.3.2020   SA04
    // Tehdään skaalattu kuva
    // Huom! Muista lopuksi poistaa käytetty kuva
    private Image skaalattuKuva(Display display, ImageData alku, int maxW, int maxH)
    {
        if (alku == null)
            return null;
        int uusiH = maxH;
        int uusiW = (alku.width * uusiH) / alku.height;
        if (uusiW > maxW)
        {
            uusiW = maxW;
            uusiH = (alku.height * uusiW) / alku.width;
        }
       
        Image uusIma = new Image(display, uusiW, uusiH);
        GC gc = new GC(uusIma);
        Image vanIma = new Image(display, alku);
        gc.drawImage(vanIma, 0, 0, alku.width, alku.height, 0, 0, uusiW, uusiH);
       
        vanIma.dispose();
        gc.dispose();
        return uusIma;
    }

    // ------------------------------------------
    // 11.4.2020    SA97
    // Tehdään dialogin sisältö
    private void createContents()
    {
        shell = new Shell(getParent(), getStyle());
        shell.setSize(700, 540);
        shell.setText(getText());
        FormLayout formLayout = new FormLayout();
        shell.setLayout(formLayout);
       
        Device device = Display.getCurrent();            // määritellään värejä
        vaalvihr = new Color(device, 240, 255, 240);

        Composite ylaComposite = new Composite(shell, SWT.NONE);   // tai SWT.BORDER
        FormData fd_ylaComposite = new FormData();
        fd_ylaComposite.top = new FormAttachment(0);
        fd_ylaComposite.left = new FormAttachment(0);
        fd_ylaComposite.bottom = new FormAttachment(0, 28);
        fd_ylaComposite.right = new FormAttachment(0, 700);
        ylaComposite.setLayoutData(fd_ylaComposite);

        Button btnPoistu = new Button(ylaComposite, SWT.NONE);
        btnPoistu.addSelectionListener(new SelectionAdapter()
        {
            @Override
            public void widgetSelected(SelectionEvent e)
            {
                result = "Klikattiin Poistu";
                shell.close();
            }
        });
        btnPoistu.setBounds(10, 2, 100, 25);
        btnPoistu.setText("Poistu");

        Button btnSovita = new Button(ylaComposite, SWT.NONE);
        btnSovita.addSelectionListener(new SelectionAdapter()
        {
            @Override
            public void widgetSelected(SelectionEvent e)
            {
                buttonSovi();
            }
        });
        btnSovita.setBounds(125, 2, 100, 25);
        btnSovita.setText("Sovita");
       
        Button btnAlkuper = new Button(ylaComposite, SWT.NONE);
        btnAlkuper.addSelectionListener(new SelectionAdapter()
        {
            @Override
            public void widgetSelected(SelectionEvent e)
            {
                buttonAlku();   
            }
        });
        btnAlkuper.setBounds(240, 2, 120, 25);
        btnAlkuper.setText("Alkuperäinen");
       
        Label labelTietoa = new Label(ylaComposite, SWT.NONE);
        labelTietoa.setText(infoz);
        labelTietoa.setBounds(370, 6, 290, 15);

        sc = new ScrolledComposite(shell, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
        FormData fd_sc = new FormData();
        fd_sc.top = new FormAttachment(ylaComposite, 0, SWT.BOTTOM);
        fd_sc.bottom = new FormAttachment(100, -2);
        fd_sc.right = new FormAttachment(100, -2);
        fd_sc.left = new FormAttachment(0);
        sc.setLayoutData(fd_sc);

        lblKuva = new Label(sc, SWT.NONE);
        sc.setContent(lblKuva);
        sc.setExpandHorizontal(true);
        sc.setExpandVertical(true);
    }
}


--------------------------------
(sivua muokattu  11.5.2020)