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)