Displaying an Image In the first example, we simply display an image on the panel. DisplayImage.java package com.zetcode; import import import import java.awt.Dimension; java.awt.Graphics; java.awt.Graphics2D; java.awt.Image; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JPanel; public class DisplayImage extends JPanel { Image castle; Dimension size; public DisplayImage() { size = new Dimension(); castle = new ImageIcon(this.getClass().getResource("redrock.png")).getImage(); size.width = castle.getWidth(null); size.height = castle.getHeight(null); setPreferredSize(size); } public void paint(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.drawImage(castle, 0, 0, null); } public static void main(String[] args) { JFrame frame = new JFrame("Red Rock"); frame.add(new DisplayImage()); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } } Grayscale image In computing, a grayscale digital image is an image in which the value of each pixel is a single sample, that is, it carries the full (and only) information about its intensity. Images of this sort are composed exclusively of shades of neutral gray, varying from black at the weakest intensity to white at the strongest. (wikipedia) In the next example, we will create a grayscale image with Java 2D tools. GrayImage.java package com.zetcode; import import import import import java.awt.Dimension; java.awt.Graphics; java.awt.Graphics2D; java.awt.Image; java.awt.image.BufferedImage; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JPanel; public class GrayImage extends JPanel { Image castle; BufferedImage bufferedImage; Dimension size; public GrayImage() { size = new Dimension(); castle = new ImageIcon("slanec.jpg").getImage(); size = new Dimension(); size.width = castle.getWidth(null); size.height = castle.getHeight(null); setPreferredSize(size); bufferedImage = new BufferedImage(size.width, size.height, BufferedImage.TYPE_BYTE_GRAY); Graphics g = bufferedImage.getGraphics(); g.drawImage(castle, 0, 0, null); g.dispose(); } public void paint(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.drawImage(bufferedImage, null, 0, 0); } public static void main(String[] args) { JFrame frame = new JFrame("Grayscale"); frame.add(new GrayImage()); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } } Flipped image Next we are going to flip an image. We are going to filter the image. There is a filter() method, which is transforming images. ImageFlip.java package com.zetcode; import import import import import import java.awt.Graphics; java.awt.Graphics2D; java.awt.Image; java.awt.geom.AffineTransform; java.awt.image.AffineTransformOp; java.awt.image.BufferedImage; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JPanel; public class ImageFlip extends JPanel { Image castle; BufferedImage bufferedImage; public ImageFlip() { castle = new ImageIcon("slanec.jpg").getImage(); bufferedImage = new BufferedImage(castle.getWidth(null), castle.getHeight(null), BufferedImage.TYPE_INT_RGB); } public void paint(Graphics g) { Graphics2D g2d = (Graphics2D) g; Graphics gb = bufferedImage.getGraphics(); gb.drawImage(castle, 0, 0, null); gb.dispose(); AffineTransform tx = AffineTransform.getScaleInstance(-1, 1); tx.translate(-castle.getWidth(null), 0); AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); bufferedImage = op.filter(bufferedImage, null); g2d.drawImage(castle, 10, 10, null); g2d.drawImage(bufferedImage, null, 290, 10); } public static void main(String[] args) { JFrame frame = new JFrame("Flip image"); frame.add(new ImageFlip()); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(570, 230); frame.setLocationRelativeTo(null); frame.setVisible(true); } } Blurred image The next code example will blur our image. Blur means an unfocused image. To blur an image, we use the convolution operation. It is a mathematical operations. It is also used in edge detection or noise elimination. Blur operations can be used in various graphical effects. For example creating speed illusion, showing an unfocused vision of a human being etc. The blur filter operation replaces each pixel in image with an average of the pixel and its neighbours. BlurredImage.java package com.zetcode; import import import import import import import java.awt.Dimension; java.awt.Graphics; java.awt.Graphics2D; java.awt.image.BufferedImage; java.awt.image.BufferedImageOp; java.awt.image.ConvolveOp; java.awt.image.Kernel; import java.io.IOException; import javax.imageio.ImageIO; import javax.swing.JFrame; import javax.swing.JPanel; public class BlurredImage extends JPanel { BufferedImage castle; BufferedImage filteredImage; Dimension size; public BlurredImage() { try { castle = ImageIO.read(this.getClass().getResource("redrock.png")); } catch (IOException e) { System.out.println("cannot read image"); } filteredImage = new BufferedImage(castle.getWidth(null), castle.getHeight(null), BufferedImage.TYPE_BYTE_GRAY); size = new Dimension(); size.width = castle.getWidth(null); size.height = castle.getHeight(null); setPreferredSize(size); Graphics g = filteredImage.getGraphics(); g.drawImage(castle, 455, 255, null); float[] blurKernel = { 1/9f, 1/9f, 1/9f, 1/9f, 1/9f, 1/9f, 1/9f, 1/9f, 1/9f }; BufferedImageOp blur = new ConvolveOp(new Kernel(3, 3, blurKernel)); castle = blur.filter(castle, new BufferedImage(castle.getWidth(), castle.getHeight(),castle.getType())); g.dispose(); } @Override public void paint(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.drawImage(castle, null, 3, 3); } public static void main(String[] args) { JFrame frame = new JFrame("Blurred Image"); frame.add(new BlurredImage()); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } } Reflection In the next example we show a reflected image. This beautiful effect makes an illusion as if the image was reflected in water. Reflection.java package com.zetcode; import import import import import java.awt.*; java.awt.image.*; java.io.*; javax.imageio.*; javax.swing.*; public class Reflection extends JComponent { private BufferedImage image; public Reflection() { try { image = ImageIO.read(new File("slanec.jpg")); } catch (Exception e) { e.printStackTrace(); } } public void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D)g; int width = getWidth(); int height = getHeight(); int imageWidth = image.getWidth(); int imageHeight = image.getHeight(); int gap = 20; float opacity = 0.4f; float fadeHeight = 0.3f; g2d.setPaint(new GradientPaint(0, 0, Color.black, 0, height, Color.darkGray)); g2d.fillRect(0, 0, width, height); g2d.translate((width - imageWidth) / 2, height / 2 - imageHeight); g2d.drawRenderedImage(image, null); g2d.translate(0, 2 * imageHeight + gap); g2d.scale(1, -1); BufferedImage reflection = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB); Graphics2D rg = reflection.createGraphics(); rg.drawRenderedImage(image, null); rg.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN)); rg.setPaint(new GradientPaint(0, imageHeight * fadeHeight, new Color(0.0f, 0.0f, 0.0f, 0.0f), 0, imageHeight, new Color(0.0f, 0.0f, 0.0f, opacity))); rg.fillRect(0, 0, imageWidth, imageHeight); rg.dispose(); g2d.drawRenderedImage(reflection, null); } public Dimension getPreferredSize() { return new Dimension(320, 390); } public static void main(String[] args) { JFrame frame = new JFrame("Reflection"); Reflection r = new Reflection(); frame.add(new Reflection()); frame.pack(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLocationRelativeTo(null); frame.setVisible(true); } } package com.example.vacphotos; 02. 03.import javax.swing.Icon; 04.import javax.swing.ImageIcon; 05.import photoalbumapp.Photo; 06. 07.public class VacPhotos implements Photo { 08. 09.ImageIcon icon1 = newImageIcon(getClass().getResource("/com/example/vacphotos/demo1.png")) ; 10.ImageIcon icon2 = newImageIcon(getClass().getResource("/com/example/vacphotos/demo2.png")) ; 11. 12.public VacPhotos() { 13.} 14. 15.@Override 16.public Icon[] getPhoto() { 17.Icon[] icons = new Icon[]{icon1, icon2}; 18.return icons; 19.} 20. 21.@Override 22.public String[] getDescription() { 23.String[] descs = new String[]{"pic 1", "pic2"}; 24.return descs; 25.} 26. 27.} 01.package photoalbumapp; 02. 03.import java.util.Collection; 04.import org.openide.util.Lookup; 05.import org.openide.util.Lookup.Result; 06.import org.openide.util.Lookup.Template; 07. 08.public class PhotoService { 09. 10.private static PhotoService service; 11.private Lookup photoLookup; 12.private Collection photos; 13.private Template photoTemplate; 14.private Result photoResults; 15. 16.private PhotoService() { 17.photoLookup = Lookup.getDefault(); 18.photoTemplate = new Template(Photo.class); 19.photoResults = photoLookup.lookup(photoTemplate); 20.photos = photoResults.allInstances(); 21.} 22. 23.public static synchronized PhotoService getInstance() { 24.if (service == null) { 25.service = new PhotoService(); 26.} 27.return service; 28.} 29. 30.public Collection getDefinitions() { 31.return photos; 32.} 33. 34.} Setting Up Your Own Photo Album To implement a photo album project, I have established a very simple directory structure. You may ultimately use something different, but if you want to use the included source with minimal changes, follow this structure: 1. All photo album folders will reside in some main "root" directory. 2. Every album folder must contain two sub folders, called "images" and "thumbnails" (more on this later). 3. Album folders can be nested. For example: Post a comment Email Article Print Article Share Articles C:photos: is my main root folder C:photosSomeAlbum: photo album folder C:photosSomeAlbumimages C:photosSomeAlbumthumbnails Helper Objects I will start with helper classes before moving on to the main algorithms. To filter out only specific file types to work with, I implemented classes using the standard java.io FileFilter interface, located in the filters package. The DirFilter filters out only directory file types and only those that I'm interested in. Here is the code implementing the accept method from the java.io FileFilter interface. public boolean accept(File file) { return file.isDirectory() && !excludeDir(file.getName()); } private boolean excludeDir(String dirname) { for (int i = 0; i < exclude.length; i++) { if (exclude[i].equals(dirname)) { return true; } } return false; } Similarly, the ImageFilesOnly filters out only image file types from the list of all files in a folder—in this case, only JPG files. static String[] accepted = {"jpg", "JPG"}; public boolean accept(File file) { if (file.isFile()) { String fn = file.getName(); return (file.isFile() && (fn.endsWith(accepted[0]) || fn.endsWith(accepted[1]))); } return false; } I apply the filters when I create the file objects representing a file system folders or files. For instance, File dir = new File("C:\photos"); File[] pics = dir.listFiles(new ImageFilesOnly()); will create a list of JPG files located in the "photos" folder. The ImageUtils from the util package contains APIs pertaining to the logical Image object, which represents one picture. I did not create a separate picture representation object in the beans package because all of the image properties required for the back-end engine are calculated in real time, and the image is represented simply by java.io.File. The APIs for the Image are: getImageCameraManufacturer(File img) getImageExifCameraModel(File img) getImageExifCreationDate(File img) getImageExifMetaData(File img, int meta) As you may have guessed, all of these APIs use the metadata extractor library developed by Drew Noakes (see reference), and the names are self explanatory. One API of particular interest is thegetImageExifCreationDate. It uses the FileTimes utility and has some logic in addition to the metadata extractor calls. Different manufacturers may call the field in the metadata of the file representing date the picture was taken differently, and in rare cases may not store that field at all. Therefore, I try different tags to locate the creation date of the picture. Otherwise, if image does not have this field, I get the time the file was created. Here is the source code for this method. See the complete source for the other methods: public static String getImageExifCreationDate(File img) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss"); try { Metadata metadata = JpegMetadataReader.readMetadata(img); Directory exifDirectory = metadata.getDirectory(ExifDirectory.class); String dt = exifDirectory.getString(ExifDirectory.TAG_DATETIME); String dt_org = exifDirectory.getString(ExifDirectory.TAG_DATETIME_ORIGINAL); String dt_dt = exifDirectory.getString(ExifDirectory.TAG_DATETIME_DIGITIZED); if (dt_org != null && !dt_org.startsWith("0000")) { return dt_org; } else if (dt != null && !dt.startsWith("0000")) { return dt; } else if (dt_dt != null && !dt_dt.startsWith("0000")) { return dt_dt; } else { // not correct call . java has no API to get the file // create date of a file // return sdf.format(new Date(img.lastModified())); return sdf.format(new Date(FileTimes.getFileCreated( img.getPath()))); } } catch (JpegProcessingException ex) { logger.error(ex, ex); return null; } } The AlbumUtil class in the same util package that pertains to the Album object, which is a higher obstructed object than the single image and is represented by the folder on the file system. AlbumUtil is much more complex than the ImageUtil and has a lot of logic that constitutes a big part of the main backend engine. For example, the getFolderDate method does a selective sampling of the file creation dates of the first, middle, and last picture in the folder using the getImageExifCreationDate method, which then returns the latest date. The getAlbumDate method uses the getFolderDate method to recursively calculate one master date for the whole Album, including subfolders and excluding folders designated by the EXCLUDE_DIR parameter. Here is the source for the getAlbumDate method: public static String getAlbumDate(File dir) { String[] dates = null; ArrayList aDates = new ArrayList(); File[] innerAlbums = dir.listFiles(new DirFilter(new String[] {TNAILS_DIR})); if (innerAlbums != null && innerAlbums.length > 0) { String currentDirDate = getFolderDate(dir); if (currentDirDate != null) { // if there are pictures in // the current dir aDates.add(currentDirDate); } for (int i = 0; i < innerAlbums.length; i++) { // recursive call to continue traversing the dir tree String date = getAlbumDate(innerAlbums[i]); if (date != null) { aDates.add(date); } } dates = (String[]) aDates.toArray(new String[aDates.size()]); Arrays.sort(dates, Collections.reverseOrder()); } else { // there are no child dir - return date for this dir return getFolderDate(dir); } return dates[0]; } Because I do not read the creation date for every file in every subfolder, but instead use selective sampling, very little I/O is generated and the calculation engine is extremely fast. The selective sampling works even if pictures are not in alphabetical order. For example, over 800 Mb of pictures can be analyzed in 953 MS on a standard hard drive (7200 RMP), with RAID optimized I/O; with 10000 RPM hard drives, this time can be decreased even further. Now, look at how the engine works. The GetResourceFiles object (called from the main method) is passed a location of the root folder. It then calls a getRootView method that recursively traverses the folders and pushes them on the Stack. For every folder that is pushed on the stack, a random thumbnail image is calculated every time the engine is called (see the pushAlbum method). This is the property that can be accessed on the front end to display a different picture for an album on every refresh. In addition, if the folder has subfolders, they are all analyzed, sorted by their calculated date, and also pushed on the Stack. The output indentation you see is created by the relative position of the object in the stack. Therefore, child folders are correctly indented from the parent folders (see the indent method) But, for the Web representation, the indent method would need to be changed to account for the HTML or other output. public void getRootView(File dir) { if (dir.exists() && dir.isDirectory()) { Album a = new Album(dir); pushAlbum(a); // one level down only Album[] innerAlbums = a.getInnerAlbums(); if (innerAlbums != null && innerAlbums.length > 0) { // sort in the order of newest first here // before expecting them SortedMap sortedInnerAlbums = AlbumUtils.sortAlbums(innerAlbums); Collection sAlbums = sortedInnerAlbums.values(); innerAlbums = (Album[]) sAlbums.toArray(new Album[sAlbums.size()]); sortedAlbums.putAll(sortedInnerAlbums); for (int i = 0; i < innerAlbums.length; i++) { // recursive call to continue traversing the dir tree getRootView(innerAlbums[i].getDir()); } } popAlbum(); } } private String indent(String tag) { StringBuffer res = new StringBuffer(); int indent = stack.size() - 1; for (int i = 0; i < indent; i++) { res.append("t"); } res.append(tag); return res.toString(); }