package nl.aegeedelft.watermarker import org.intellij.lang.annotations.Language import java.awt.* import java.awt.image.BufferedImage import java.awt.image.RenderedImage import java.io.BufferedInputStream import java.io.ByteArrayInputStream import java.io.File import java.util.concurrent.atomic.AtomicInteger import javax.imageio.IIOImage import javax.imageio.ImageIO import javax.imageio.ImageWriteParam import javax.imageio.stream.MemoryCacheImageOutputStream import javax.swing.* import javax.swing.filechooser.FileFilter import kotlin.concurrent.fixedRateTimer import kotlin.concurrent.thread @Language("RegExp") val filenameRegex = ".*\\.(png|jpe?g|bmp)".toRegex() val jf = JFrame("WaterMarker") val counter = AtomicInteger() val runbtn = JButton("Run!") val pbar = JProgressBar() val dlg = JDialog(jf, "Watermarking Images!", true) fun main(args: Array) { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()) var watermark: File? = null var outdir: File? = null var basedir: File? = null val basepicker = JFileChooser() val bds = JLabel("Pick an input folder") val outpicker = JFileChooser() val ods = JLabel("Pick an output folder") val wmpicker = JFileChooser() val wms = JLabel("Pick a Watermark file") val box = Box.createVerticalBox() basepicker.currentDirectory = File(".") basepicker.fileSelectionMode = JFileChooser.FILES_AND_DIRECTORIES basepicker.isAcceptAllFileFilterUsed = false basepicker.fileFilter = object : FileFilter() { override fun accept(f: File): Boolean = f.isDirectory override fun getDescription(): String = "Folders" } outpicker.currentDirectory = File(".") outpicker.fileSelectionMode = JFileChooser.FILES_AND_DIRECTORIES outpicker.isAcceptAllFileFilterUsed = false outpicker.fileFilter = basepicker.fileFilter wmpicker.currentDirectory = File(".") wmpicker.fileSelectionMode = JFileChooser.FILES_ONLY wmpicker.fileFilter = object : FileFilter() { override fun accept(f: File): Boolean = filenameRegex.matches(f.name) or f.isDirectory override fun getDescription(): String = "Image Files" } wmpicker.isAcceptAllFileFilterUsed = false box.add( JButton("Select Input").apply { addActionListener { val c = basepicker.showOpenDialog(jf) if (c == JFileChooser.APPROVE_OPTION) { basedir = basepicker.selectedFile bds.text = basepicker.selectedFile.path } } }.forceToCenter() ) box.add(bds.forceToCenter()) box.add( JButton("Select Output").apply { addActionListener { val c = outpicker.showOpenDialog(jf) if (c == JFileChooser.APPROVE_OPTION) { outdir = outpicker.selectedFile ods.text = outpicker.selectedFile.path } } }.forceToCenter() ) box.add(ods.forceToCenter()) box.add( JButton("Select Watermark").apply { addActionListener { val c = wmpicker.showOpenDialog(jf) if (c == JFileChooser.APPROVE_OPTION) { watermark = wmpicker.selectedFile wms.text = wmpicker.selectedFile.path } } }.forceToCenter() ) box.add(wms.forceToCenter()) runbtn.apply { addActionListener { if (watermark != null && outdir != null && basedir != null) { this.isEnabled = false thread { watermarkImages(basedir!!, outdir!!, watermark!!) } } else { JOptionPane.showMessageDialog( jf, "You have left one of the files empty!", "Error!", JOptionPane.ERROR_MESSAGE ) } } } box.add(runbtn.forceToCenter()) jf.add(box, BorderLayout.CENTER) val menuBar = JMenuBar() val helpMenu = JMenu("Help") val fileMenu = JMenu("File") menuBar.add(fileMenu) menuBar.add(helpMenu) val infoItem = JMenuItem("About") helpMenu.add(infoItem) infoItem.addActionListener { JOptionPane.showMessageDialog( jf, "WaterMarker\n" + "An app written by Julius de Jeu for AEGEE-Delft. \n" + "\n" + "Copyright (c) Julius de Jeu 2019", "About", JOptionPane.PLAIN_MESSAGE ) } fileMenu.add(JMenuItem("Exit").apply { addActionListener { System.exit(0) } accelerator = KeyStroke.getKeyStroke("ALT F4") }) jf.jMenuBar = menuBar jf.setLocationRelativeTo(null) jf.defaultCloseOperation = JFrame.EXIT_ON_CLOSE jf.preferredSize = Dimension(300, 300) jf.size = jf.preferredSize jf.isVisible = true dlg.add(BorderLayout.CENTER, pbar) dlg.defaultCloseOperation = JDialog.DO_NOTHING_ON_CLOSE dlg.setSize(300, 75) dlg.setLocationRelativeTo(jf) pbar.foreground = Color.GREEN.darker() } fun watermarkImages(basedir: File, outdir: File, watermarkpath: File) { val wmbase = ImageIO.read(watermarkpath) val scale = 3.6 //JOptionPane.showMessageDialog(jf, "", "Inserting watermark!", JOptionPane.INFORMATION_MESSAGE, ImageIcon(watermark)) //FrmPopUpInfo("Inserting Watermark", watermark) val files = basedir.listFiles().filter { filenameRegex.matches(it.name) }.chunked(5) val propersize = files.sumBy { it.size } counter.set(0) SwingUtilities.invokeLater { pbar.value = 0 pbar.maximum = propersize dlg.isVisible = true } fixedRateTimer("theWait", period = 500, initialDelay = 0) { if (counter.get() == propersize) { SwingUtilities.invokeLater { dlg.isVisible = false runbtn.isEnabled = true this.cancel() } } } for (fl in files) { thread { for (f in fl) { if (filenameRegex.matches(f.name.toString()) && !"jpe?g".toRegex().matches(f.extension)) { val image = ImageIO.read(f) val combined = addWM(resize(wmbase, wmbase.width / scale, wmbase.height / scale), image) as RenderedImage ImageIO.write(combined, "JPG", File(outdir, f.nameWithoutExtension + ".jpg")) } else { val `is` = BufferedInputStream(ByteArrayInputStream(f.readBytes())) val it = ImageIO.getImageReadersByMIMEType("image/jpeg") val reader = it.next() val iis = ImageIO.createImageInputStream(`is`) reader.setInput(iis, false, false) val image = reader.read(0) val imageMetadata = reader.getImageMetadata(0) val combined = addWM(resize(wmbase, wmbase.width / scale, wmbase.height / scale), image) as RenderedImage val iter = ImageIO.getImageWritersByMIMEType("image/jpeg") val writer = iter.next() val iwp = writer.defaultWriteParam iwp.compressionMode = ImageWriteParam.MODE_EXPLICIT iwp.compressionQuality = 0.9f val imgOut = MemoryCacheImageOutputStream(File(outdir, f.nameWithoutExtension + ".jpg").outputStream()) writer.output = imgOut val ending = IIOImage(combined, null, imageMetadata) writer.write(null, ending, iwp) writer.dispose() } SwingUtilities.invokeLater { pbar.value = counter.incrementAndGet() } } } } } fun Component.forceToCenter(): JPanel = JPanel().apply { this.add(this@forceToCenter) } fun addWM(watermark: BufferedImage, image: BufferedImage): Image { val combined = BufferedImage(image.width, image.height, BufferedImage.TYPE_INT_RGB) val g = combined.graphics g.drawImage(image, 0, 0, null) val x = image.width - watermark.width - 5 val y = image.height - watermark.height - 5 g.drawImage(watermark, x, y, null) return combined } fun resize(img: BufferedImage, newW: Double, newH: Double): BufferedImage { val tmp = img.getScaledInstance(newW.toInt(), newH.toInt(), Image.SCALE_SMOOTH) val dimg = BufferedImage(newW.toInt(), newH.toInt(), BufferedImage.TYPE_INT_ARGB) val g2d = dimg.createGraphics() g2d.drawImage(tmp, 0, 0, null) g2d.dispose() return dimg }