/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Copyright 2013-2015 Redwarp */ package net.redwarp.tool.resizer.worker; import net.redwarp.tool.resizer.misc.Configuration; import net.redwarp.tool.resizer.misc.Localization; import net.redwarp.tool.resizer.table.Operation; import net.redwarp.tool.resizer.table.OperationStatus; import java.awt.*; import java.awt.RenderingHints.Key; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.imageio.ImageIO; import javax.swing.*; public class ImageScaler extends SwingWorker<Void, Operation> { private File inputFile; private Operation operation; private ScreenDensity inputDensity; private static ExecutorService executor = Executors .newFixedThreadPool(Runtime.getRuntime().availableProcessors()); private static final Object fileLock = new Object(); private static final Object folderLock = new Object(); public ImageScaler(final Operation operation, final ScreenDensity inputDensity) { this.operation = operation; this.inputFile = operation.getFile(); this.inputDensity = inputDensity; } @Override protected Void doInBackground() throws Exception { try { BufferedImage inputImage; synchronized (fileLock) { // inputImage = ImageIO.read(this.inputFile); Image tempImage = Toolkit.getDefaultToolkit().createImage(this.inputFile.getAbsolutePath()); ImageIcon tempIcon = new ImageIcon(tempImage); inputImage = new BufferedImage(tempIcon.getIconWidth(), tempIcon.getIconHeight(), BufferedImage.TYPE_INT_ARGB); inputImage.getGraphics().drawImage(tempImage, 0, 0, null); } if (inputImage == null) { this.operation.setStatus(OperationStatus.ERROR, Localization.get("error_wrong_png")); this.publish(this.operation); return null; } else { // if (inputImage.getType() != BufferedImage.TYPE_INT_ARGB) { // BufferedImage tempImage = new BufferedImage( // inputImage.getWidth(), inputImage.getHeight(), // BufferedImage.TYPE_INT_ARGB); // Graphics2D g = tempImage.createGraphics(); // g.drawImage(inputImage, 0, 0, tempImage.getWidth(), // tempImage.getHeight(), 0, 0, inputImage.getWidth(), // inputImage.getHeight(), null); // g.dispose(); // inputImage = tempImage; // } } this.operation.setStatus(OperationStatus.IN_PROGRESS); this.publish(this.operation); List<ScreenDensity> densityList = Configuration.getSettings() .getSupportedScreenDensity(); File parent = this.inputFile.getParentFile(); for (ScreenDensity density : densityList) { if (!density.isActive()) { continue; } File outputFolder; synchronized (folderLock) { outputFolder = new File(parent, "drawable-" + density.getName()); if (!outputFolder.exists()) { outputFolder.mkdir(); } } String name; Output output = null; int extensionPos = this.inputFile.getName().lastIndexOf('.'); if (extensionPos != -1) { String extension = this.inputFile.getName().substring(extensionPos, this.inputFile.getName().length()); if (".png".equalsIgnoreCase(extension)) { output = Output.PNG; } else if (".jpg".equalsIgnoreCase(extension) || ".jpeg".equalsIgnoreCase(extension)) { output = Output.JPG; } } if (output != null) { name = this.inputFile.getName().substring(0, extensionPos) + "." + output.getFormat(); } else { name = this.inputFile.getName(); output = Output.PNG; } File outputFile = new File(outputFolder, name); if (outputFile.exists()) { outputFile.delete(); } BufferedImage outputImage; if (this.inputFile.getName().endsWith(".9.png")) { BufferedImage trimedImage = this.trim9PBorder(inputImage); float ratio = density.getScale() / this.inputDensity.getScale(); trimedImage = this.rescaleImage(trimedImage, (int) (ratio * trimedImage.getWidth()), (int) (ratio * trimedImage.getHeight())); BufferedImage borderImage; int w = trimedImage.getWidth(); int h = trimedImage.getHeight(); try { borderImage = this.generateBordersImage(inputImage, w, h); } catch (Wrong9PatchException e) { this.operation.setStatus(OperationStatus.ERROR, Localization.get("error_wrong_9p")); this.publish(this.operation); return null; } int[] rgbArray = new int[w * h]; trimedImage.getRGB(0, 0, w, h, rgbArray, 0, w); borderImage.setRGB(1, 1, w, h, rgbArray, 0, w); rgbArray = null; outputImage = borderImage; } else { float ratio = density.getScale() / this.inputDensity.getScale(); outputImage = this.rescaleImage(inputImage, (int) (ratio * inputImage.getWidth()), (int) (ratio * inputImage.getHeight())); } try { synchronized (fileLock) { // Let's do the worst hack in the universe to speed things up ! if (Configuration.getSettings().shouldKeepSameDensityFile() && output == Output.JPG && outputImage.getWidth() == inputImage.getWidth() && outputImage.getHeight() == inputImage.getHeight()) { ImageWriter.copy(inputFile, outputFile); } else { ImageWriter.write(outputImage, output, outputFile); } } } catch (IOException e) { this.operation.setStatus(OperationStatus.ERROR); this.publish(this.operation); return null; } } // } this.operation.setStatus(OperationStatus.FINISH); this.publish(this.operation); } catch (Exception e) { this.operation.setStatus(OperationStatus.ERROR); this.publish(this.operation); } return null; } public void post() { executor.submit(this); } private BufferedImage rescaleImage(BufferedImage image, int targetWidth, int targetHeight) { if (targetWidth == 0) { targetWidth = 1; } if (targetHeight == 0) { targetHeight = 1; } if (targetWidth * 2 < image.getWidth() - 1) { BufferedImage tempImage = this.rescaleImage(image, image.getWidth() / 2, image.getHeight() / 2); return this.rescaleImage(tempImage, targetWidth, targetHeight); } else { BufferedImage outputImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_ARGB); Graphics2D graphics = outputImage.createGraphics(); Map<Key, Object> hints = new HashMap<RenderingHints.Key, Object>(); hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); hints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); hints.put(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); graphics.setRenderingHints(hints); graphics.drawImage(image, 0, 0, outputImage.getWidth(), outputImage.getHeight(), null); graphics.dispose(); return outputImage; } } private BufferedImage trim9PBorder(BufferedImage inputImage) { BufferedImage trimedImage = new BufferedImage( inputImage.getWidth() - 2, inputImage.getHeight() - 2, BufferedImage.TYPE_INT_ARGB); Graphics2D g = trimedImage.createGraphics(); g.drawImage(inputImage, 0, 0, trimedImage.getWidth(), trimedImage.getHeight(), 1, 1, inputImage.getWidth() - 1, inputImage.getHeight() - 1, null); g.dispose(); return trimedImage; } private void enforceBorderColors(BufferedImage inputImage) { Graphics2D g = inputImage.createGraphics(); g.setBackground(new Color(0, 0, 0, 0)); g.clearRect(1, 1, inputImage.getWidth() - 2, inputImage.getHeight() - 2); g.dispose(); int w = inputImage.getWidth(); int h = inputImage.getHeight(); int[] rgb = new int[w * h]; inputImage.getRGB(0, 0, w, h, rgb, 0, w); for (int i = 0; i < rgb.length; i++) { if ((0xff000000 & rgb[i]) != 0) { rgb[i] = 0xff000000; } } inputImage.setRGB(0, 0, w, h, rgb, 0, w); inputImage.setRGB(0, 0, 0x0); inputImage.setRGB(0, h - 1, 0x0); inputImage.setRGB(w - 1, h - 1, 0x0); inputImage.setRGB(w - 1, 0, 0x0); } private BufferedImage generateBordersImage(BufferedImage source, int trimedWidth, int trimedHeight) throws Wrong9PatchException { BufferedImage finalBorder = new BufferedImage(trimedWidth + 2, trimedHeight + 2, BufferedImage.TYPE_INT_ARGB); int cutW = source.getWidth() - 2; int cutH = source.getHeight() - 2; { // left border BufferedImage leftBorder = new BufferedImage(1, cutH, BufferedImage.TYPE_INT_ARGB); leftBorder.setRGB(0, 0, 1, cutH, source.getRGB(0, 1, 1, cutH, null, 0, 1), 0, 1); this.verifyBorderImage(leftBorder); leftBorder = this.resizeBorder(leftBorder, 1, trimedHeight); finalBorder.setRGB(0, 1, 1, trimedHeight, leftBorder.getRGB(0, 0, 1, trimedHeight, null, 0, 1), 0, 1); } { // right border BufferedImage rightBorder = new BufferedImage(1, cutH, BufferedImage.TYPE_INT_ARGB); rightBorder.setRGB(0, 0, 1, cutH, source.getRGB(cutW + 1, 1, 1, cutH, null, 0, 1), 0, 1); this.verifyBorderImage(rightBorder); rightBorder = this.resizeBorder(rightBorder, 1, trimedHeight); finalBorder .setRGB(trimedWidth + 1, 1, 1, trimedHeight, rightBorder .getRGB(0, 0, 1, trimedHeight, null, 0, 1), 0, 1); } { // top border BufferedImage topBorder = new BufferedImage(cutW, 1, BufferedImage.TYPE_INT_ARGB); topBorder.setRGB(0, 0, cutW, 1, source.getRGB(1, 0, cutW, 1, null, 0, cutW), 0, cutW); this.verifyBorderImage(topBorder); topBorder = this.resizeBorder(topBorder, trimedWidth, 1); finalBorder.setRGB(1, 0, trimedWidth, 1, topBorder.getRGB(0, 0, trimedWidth, 1, null, 0, trimedWidth), 0, trimedWidth); } { // bottom border BufferedImage bottomBorder = new BufferedImage(cutW, 1, BufferedImage.TYPE_INT_ARGB); bottomBorder .setRGB(0, 0, cutW, 1, source.getRGB(1, cutH + 1, cutW, 1, null, 0, cutW), 0, cutW); this.verifyBorderImage(bottomBorder); bottomBorder = this.resizeBorder(bottomBorder, trimedWidth, 1); finalBorder.setRGB(1, trimedHeight + 1, trimedWidth, 1, bottomBorder.getRGB(0, 0, trimedWidth, 1, null, 0, trimedWidth), 0, trimedWidth); } return finalBorder; } private BufferedImage resizeBorder(final BufferedImage border, int targetWidth, int targetHeight) { if (targetWidth > border.getWidth() || targetHeight > border.getHeight()) { BufferedImage endImage = this.rescaleImage(border, targetWidth, targetHeight); this.enforceBorderColors(endImage); return endImage; } int w = border.getWidth(); int h = border.getHeight(); int[] data = border.getRGB(0, 0, w, h, null, 0, w); int[] newData = new int[targetWidth * targetHeight]; float widthRatio = (float) Math.max(targetWidth - 1, 1) / (float) Math.max(w - 1, 1); float heightRatio = (float) Math.max(targetHeight - 1, 1) / (float) Math.max(h - 1, 1); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { if ((0xff000000 & data[y * w + x]) != 0) { int newX = Math.min(Math.round(x * widthRatio), targetWidth - 1); int newY = Math.min(Math.round(y * heightRatio), targetHeight - 1); newData[newY * targetWidth + newX] = data[y * w + x]; } } } BufferedImage img = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_ARGB); img.setRGB(0, 0, targetWidth, targetHeight, newData, 0, targetWidth); return img; } private void verifyBorderImage(BufferedImage border) throws Wrong9PatchException { int[] rgb = border.getRGB(0, 0, border.getWidth(), border.getHeight(), null, 0, border.getWidth()); for (int i = 0; i < rgb.length; i++) { if ((0xff000000 & rgb[i]) != 0) { if (rgb[i] != 0xff000000 && rgb[i] != 0xffff0000) { throw new Wrong9PatchException(); } } } } }