From: Niki Roo Date: Sun, 3 Dec 2017 03:10:11 +0000 (+0100) Subject: Version 4.0.0: java.awt dependencies move X-Git-Tag: fanfix-3.0.1~29^2~227 X-Git-Url: https://git.nikiroo.be/?a=commitdiff_plain;h=805005449dacb1e7b825db63836bf100e472ddd0;p=fanfix.git Version 4.0.0: java.awt dependencies move --- diff --git a/VERSION b/VERSION index 9cec716..fcdb2e1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.1.6 +4.0.0 diff --git a/changelog.md b/changelog.md index 6552145..b54742c 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # nikiroo-utils +## Version 4.0.0 + +- Deplace all dependencies on java.awt into its own package (ui) + ## Version 3.1.6 - Fix Serialiser issue with custom objects and String in a custom object diff --git a/configure.sh b/configure.sh index 707c129..4105d93 100755 --- a/configure.sh +++ b/configure.sh @@ -4,14 +4,41 @@ PREFIX=/usr/local PROGS="java javac jar" +UI=be/nikiroo/utils/ui/test/TestUI +JUI="-C bin/ be/nikiroo/utils/ui" +ANDOIRD= +JANDROID= + valid=true while [ "$*" != "" ]; do - key=`echo "$1" | cut -c1-9` - val=`echo "$1" | cut -c10-` + key=`echo "$1" | cut -f1 -d=` + val=`echo "$1" | cut -f2 -d=` case "$key" in - --prefix=) + --help) # This help message + echo The following arguments can be used: + cat "$0" | grep '^\s*--' | grep '#' | while read ln; do + cmd=`echo "$ln" | cut -f1 -d')'` + msg=`echo "$ln" | cut -f2 -d'#'` + echo " $cmd$msg" + done + ;; + --prefix) #=PATH Change the prefix to the given path PREFIX="$val" ;; + --ui) #=no Disable UI (Swing/AWT) support + [ "$val" = no -o "$val" = false ] && UI= && JUI= + if [ "$val" = yes -o "$val" = true ]; then + UI=be/nikiroo/utils/ui/test/TestUI + JUI="-C bin/ be/nikiroo/utils/ui" + fi + ;; + --android) #=yes Enable Android UI support + [ "$val" = no -o "$val" = false ] && ANDROID= && JANDROID= + if [ "$val" = yes -o "$val" = true ]; then + ANDROID=be/nikiroo/utils/android/test/TestAndroid + JANDROID="-C bin/ be/nikiroo/utils/android" + fi + ;; *) echo "Unsupported parameter: '$1'" >&2 valid=false @@ -45,12 +72,12 @@ fi; echo "MAIN = be/nikiroo/utils/test/Test" > Makefile -echo "MORE = be/nikiroo/utils/MarkableFileInputStream be/nikiroo/utils/ui/UIUtils be/nikiroo/utils/ui/WrapLayout be/nikiroo/utils/ui/ProgressBar be/nikiroo/utils/Downloader be/nikiroo/utils/Cache" >> Makefile +echo "MORE = $UI $ANDROID" >> Makefile echo "TEST = be/nikiroo/utils/test/Test" >> Makefile echo "TEST_PARAMS = $cols $ok $ko" >> Makefile echo "NAME = nikiroo-utils" >> Makefile echo "PREFIX = $PREFIX" >> Makefile -echo "JAR_FLAGS += -C bin/ be -C bin/ org -C bin/ VERSION" >> Makefile +echo "JAR_FLAGS += -C bin/ be $JUI $JANDROID -C bin/ VERSION" >> Makefile echo "SJAR_FLAGS += -C src/ org -C src/ be" >> Makefile cat Makefile.base >> Makefile diff --git a/src/be/nikiroo/utils/Cache.java b/src/be/nikiroo/utils/Cache.java index 393f634..111fc76 100644 --- a/src/be/nikiroo/utils/Cache.java +++ b/src/be/nikiroo/utils/Cache.java @@ -70,6 +70,10 @@ public class Cache { * the new traces handler */ public void setTraceHandler(TraceHandler tracer) { + if (tracer == null) { + tracer = new TraceHandler(false, false, false); + } + this.tracer = tracer; } @@ -89,7 +93,7 @@ public class Cache { */ public boolean check(URL url, boolean allowTooOld, boolean stable) { File file = getCached(url); - if (file.exists()) { + if (file.exists() && file.isFile()) { if (allowTooOld || !isOld(file, stable)) { return true; } @@ -190,7 +194,8 @@ public class Cache { * @return the opened resource if found, NULL if not */ private InputStream load(File cached, boolean allowTooOld, boolean stable) { - if (cached.exists() && (allowTooOld || !isOld(cached, stable))) { + if (cached.exists() && cached.isFile() + && (allowTooOld || !isOld(cached, stable))) { try { return new MarkableFileInputStream(new FileInputStream(cached)); } catch (FileNotFoundException e) { diff --git a/src/be/nikiroo/utils/Downloader.java b/src/be/nikiroo/utils/Downloader.java index a8a591a..e01ec1d 100644 --- a/src/be/nikiroo/utils/Downloader.java +++ b/src/be/nikiroo/utils/Downloader.java @@ -63,6 +63,10 @@ public class Downloader { * the new traces handler */ public void setTraceHandler(TraceHandler tracer) { + if (tracer == null) { + tracer = new TraceHandler(false, false, false); + } + this.tracer = tracer; } diff --git a/src/be/nikiroo/utils/IOUtils.java b/src/be/nikiroo/utils/IOUtils.java index 973a61b..f857850 100644 --- a/src/be/nikiroo/utils/IOUtils.java +++ b/src/be/nikiroo/utils/IOUtils.java @@ -1,6 +1,7 @@ package be.nikiroo.utils; import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -246,4 +247,69 @@ public class IOUtils { return loader.getResourceAsStream(name); } + + /** + * Return a resetable {@link InputStream} from this stream, and reset it. + * + * @param in + * the input stream + * @return the resetable stream, which may be the same + * + * @throws IOException + * in case of I/O error + */ + public static InputStream forceResetableStream(InputStream in) + throws IOException { + MarkableFileInputStream tmpIn = null; + File tmp = null; + + boolean resetable = in.markSupported(); + if (resetable) { + try { + in.reset(); + } catch (IOException e) { + resetable = false; + } + } + + if (resetable) { + return in; + } + + tmp = File.createTempFile(".tmp-stream", ".tmp"); + try { + write(in, tmp); + tmpIn = new MarkableFileInputStream(new FileInputStream(tmp)); + return tmpIn; + } finally { + try { + if (tmpIn != null) { + tmpIn.close(); + } + } finally { + tmp.delete(); + } + } + } + + /** + * Convert the {@link InputStream} into a byte array. + * + * @param in + * the input stream + * + * @return the array + * + * @throws IOException + * in case of I/O error + */ + public static byte[] toByteArray(InputStream in) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + write(in, out); + + byte[] array = out.toByteArray(); + out.close(); + + return array; + } } diff --git a/src/be/nikiroo/utils/Image.java b/src/be/nikiroo/utils/Image.java new file mode 100644 index 0000000..58c0e10 --- /dev/null +++ b/src/be/nikiroo/utils/Image.java @@ -0,0 +1,78 @@ +package be.nikiroo.utils; + +import java.io.IOException; +import java.io.InputStream; + +/** + * This class represents an image data. + * + * @author niki + */ +public class Image { + private byte[] data; + + /** + * Do not use -- for serialisation purposes only. + */ + @SuppressWarnings("unused") + private Image() { + } + + /** + * Create a new {@link Image} with the given data. + * + * @param data + * the data + */ + public Image(byte[] data) { + this.data = data; + } + + /** + * Create a new {@link Image} from its Base64 representation. + * + * @param base64 + * the {@link Image} in Base64 format + * + * @throws IOException + * in case of I/O error + */ + public Image(String base64) throws IOException { + this(Base64.decode(base64)); + } + + /** + * Create a new {@link Image} from a stream. + * + * @param in + * the stream + * + * @throws IOException + * in case of I/O error + */ + public Image(InputStream in) throws IOException { + this.data = IOUtils.toByteArray(in); + } + + /** + * The actual image data. + *

+ * This is the actual data, not a copy, so any change made here will be + * reflected into the {@link Image} and vice-versa. + * + * @return the image data + */ + public byte[] getData() { + return data; + } + + /** + * Convert the given {@link Image} object into a Base64 representation of + * the same {@link Image} object. + * + * @return the Base64 representation + */ + public String toBase64() { + return new String(Base64.encodeBytes(getData())); + } +} diff --git a/src/be/nikiroo/utils/ImageUtils.java b/src/be/nikiroo/utils/ImageUtils.java index 496d2ea..37d7319 100644 --- a/src/be/nikiroo/utils/ImageUtils.java +++ b/src/be/nikiroo/utils/ImageUtils.java @@ -1,280 +1,45 @@ package be.nikiroo.utils; -import java.awt.Dimension; -import java.awt.Image; -import java.awt.geom.AffineTransform; -import java.awt.image.AffineTransformOp; -import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import javax.imageio.ImageIO; - -import be.nikiroo.utils.ImageText.Mode; +import be.nikiroo.utils.serial.SerialUtils; /** * This class offer some utilities based around images. * * @author niki */ -public class ImageUtils { - /** - * Convert the given {@link Image} object into a Base64 representation of - * the same {@link Image} object. - * - * @param image - * the {@link Image} object to convert - * - * @return the Base64 representation - * - * @throws IOException - * in case of IO error - */ - static public String toBase64(BufferedImage image) throws IOException { - return toBase64(image, null); - } +public abstract class ImageUtils { + private static ImageUtils instance = newObject(); /** - * Convert the given {@link Image} object into a Base64 representation of - * the same {@link Image}. object. - * - * @param image - * the {@link Image} object to convert - * @param format - * the image format to use to serialise it (default is PNG) - * - * @return the Base64 representation + * Get a (unique) instance of an {@link ImageUtils} compatible with your + * system. * - * @throws IOException - * in case of IO error + * @return an {@link ImageUtils} */ - static public String toBase64(BufferedImage image, String format) - throws IOException { - if (format == null) { - format = "png"; - } - - String imageString = null; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - - ImageIO.write(image, format, out); - byte[] imageBytes = out.toByteArray(); - - imageString = new String(Base64.encodeBytes(imageBytes)); - - out.close(); - - return imageString; + public static ImageUtils getInstance() { + return instance; } /** - * Convert the given image into a Base64 representation of the same - * {@link File}. - * - * @param in - * the image to convert + * Save the given resource as an image on disk using the given image format + * for content, or with "png" format if it fails. * - * @return the Base64 representation - * - * @throws IOException - * in case of IO error - */ - static public String toBase64(InputStream in) throws IOException { - String fileString = null; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - - byte[] buf = new byte[8192]; - - int c = 0; - while ((c = in.read(buf, 0, buf.length)) > 0) { - out.write(buf, 0, c); - } - out.flush(); - in.close(); - - fileString = new String(Base64.encodeBytes(out.toByteArray())); - out.close(); - - return fileString; - } - - /** - * Convert the given Base64 representation of an image into an {@link Image} - * object. - * - * @param b64data - * the {@link Image} in Base64 format - * - * @return the {@link Image} object - * - * @throws IOException - * in case of IO error - */ - static public BufferedImage fromBase64(String b64data) throws IOException { - ByteArrayInputStream in = new ByteArrayInputStream( - Base64.decode(b64data)); - return fromStream(in); - } - - /** - * A shorthand method to create an {@link ImageText} and return its output. - * - * @param image - * the source {@link Image} - * @param size - * the final text size to target - * @param mode - * the mode of conversion - * @param invert - * TRUE to invert colours rendering - * - * @return the text image - */ - static public String toAscii(Image image, Dimension size, Mode mode, - boolean invert) { - return new ImageText(image, size, mode, invert).toString(); - } - - /** - * Convert the given {@link InputStream} (which should allow calls to - * {@link InputStream#reset()} for better perfs) into an {@link Image} - * object, respecting the EXIF transformations if any. - * - * @param in - * the {@link InputStream} - * - * @return the {@link Image} object - * - * @throws IOException - * in case of IO error - */ - static public BufferedImage fromStream(InputStream in) throws IOException { - MarkableFileInputStream tmpIn = null; - File tmp = null; - - boolean resetable = in.markSupported(); - if (resetable) { - try { - in.reset(); - } catch (IOException e) { - resetable = false; - } - } - - if (resetable) { - return fromResetableStream(in); - } - - tmp = File.createTempFile(".tmp-image", ".tmp"); - try { - IOUtils.write(in, tmp); - tmpIn = new MarkableFileInputStream(new FileInputStream(tmp)); - return fromResetableStream(tmpIn); - } finally { - try { - if (tmpIn != null) { - tmpIn.close(); - } - } finally { - tmp.delete(); - } - } - } - - /** - * Convert the given resetable {@link InputStream} into an {@link Image} - * object, respecting the EXIF transformations if any. - * - * @param in - * the 'resetable' (this is mandatory) {@link InputStream} - * - * @return the {@link Image} object + * @param img + * the resource + * @param target + * the target file + * @param format + * the file format ("png", "jpeg", "bmp"...) * * @throws IOException - * in case of IO error + * in case of I/O error */ - static private BufferedImage fromResetableStream(InputStream in) - throws IOException { - - int orientation; - try { - orientation = getExifTransorm(in); - } catch (Exception e) { - // no EXIF transform, ok - orientation = -1; - } - - in.reset(); - BufferedImage image = ImageIO.read(in); - - if (image == null) { - throw new IOException("Failed to convert input to image"); - } - - // Note: this code has been found on Internet; - // thank you anonymous coder. - int width = image.getWidth(); - int height = image.getHeight(); - AffineTransform affineTransform = new AffineTransform(); - - switch (orientation) { - case 1: - affineTransform = null; - break; - case 2: // Flip X - affineTransform.scale(-1.0, 1.0); - affineTransform.translate(-width, 0); - break; - case 3: // PI rotation - affineTransform.translate(width, height); - affineTransform.rotate(Math.PI); - break; - case 4: // Flip Y - affineTransform.scale(1.0, -1.0); - affineTransform.translate(0, -height); - break; - case 5: // - PI/2 and Flip X - affineTransform.rotate(-Math.PI / 2); - affineTransform.scale(-1.0, 1.0); - break; - case 6: // -PI/2 and -width - affineTransform.translate(height, 0); - affineTransform.rotate(Math.PI / 2); - break; - case 7: // PI/2 and Flip - affineTransform.scale(-1.0, 1.0); - affineTransform.translate(-height, 0); - affineTransform.translate(0, width); - affineTransform.rotate(3 * Math.PI / 2); - break; - case 8: // PI / 2 - affineTransform.translate(0, width); - affineTransform.rotate(3 * Math.PI / 2); - break; - default: - affineTransform = null; - break; - } - - if (affineTransform != null) { - AffineTransformOp affineTransformOp = new AffineTransformOp( - affineTransform, AffineTransformOp.TYPE_BILINEAR); - - BufferedImage transformedImage = new BufferedImage(width, height, - image.getType()); - transformedImage = affineTransformOp - .filter(image, transformedImage); - - image = transformedImage; - } - // - - return image; - } + public abstract void saveAsImage(Image img, File target, String format) + throws IOException; /** * Return the EXIF transformation flag of this image if any. @@ -291,7 +56,7 @@ public class ImageUtils { * @throws IOException * in case of IO error */ - static private int getExifTransorm(InputStream in) throws IOException { + protected static int getExifTransorm(InputStream in) throws IOException { int[] exif_data = new int[100]; int set_flag = 0; int is_motorola = 0; @@ -424,4 +189,20 @@ public class ImageUtils { return set_flag; } + + /** + * Create a new {@link ImageUtils}. + * + * @return the {@link ImageUtils} + */ + private static ImageUtils newObject() { + for (String clazz : new String[] { "be.nikiroo.utils.ui.ImageUtilsAwt" }) { + try { + return (ImageUtils) SerialUtils.createObject(clazz); + } catch (Exception e) { + } + } + + return null; + } } diff --git a/src/be/nikiroo/utils/StringUtils.java b/src/be/nikiroo/utils/StringUtils.java index acd632a..a6117a0 100644 --- a/src/be/nikiroo/utils/StringUtils.java +++ b/src/be/nikiroo/utils/StringUtils.java @@ -300,6 +300,14 @@ public class StringUtils { HtmlEscapeLevel.LEVEL_1_ONLY_MARKUP_SIGNIFICANT); } + /** + * Zip the data and then encode it into Base64. + * + * @param data + * the data + * + * @return the Base64 zipped version + */ public static String zip64(String data) { try { return Base64.encodeBytes(data.getBytes(), Base64.GZIP); @@ -309,6 +317,17 @@ public class StringUtils { } } + /** + * Unconvert from Base64 then unzip the content. + * + * @param data + * the data in Base64 format + * + * @return the raw data + * + * @throws IOException + * in case of I/O error + */ public static String unzip64(String data) throws IOException { ByteArrayInputStream in = new ByteArrayInputStream(Base64.decode(data, Base64.GZIP)); diff --git a/src/be/nikiroo/utils/resources/Bundle.java b/src/be/nikiroo/utils/resources/Bundle.java index 83069f9..3c448ef 100644 --- a/src/be/nikiroo/utils/resources/Bundle.java +++ b/src/be/nikiroo/utils/resources/Bundle.java @@ -1,6 +1,5 @@ package be.nikiroo.utils.resources; -import java.awt.Color; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; @@ -268,76 +267,124 @@ public class Bundle> { } /** - * Return the value associated to the given id as a {@link Color}. + * Return the value associated to the given id as a colour if it is found + * and can be parsed. + *

+ * The returned value is an ARGB value. * * @param id * the id of the value to get * * @return the associated value */ - public Color getColor(E id) { - Color color = null; + public Integer getColor(E id) { + Integer rep = null; String bg = getString(id).trim(); + + int r = 0, g = 0, b = 0, a = -1; if (bg.startsWith("#") && (bg.length() == 7 || bg.length() == 9)) { try { - int r = Integer.parseInt(bg.substring(1, 3), 16); - int g = Integer.parseInt(bg.substring(3, 5), 16); - int b = Integer.parseInt(bg.substring(5, 7), 16); - int a = 255; + r = Integer.parseInt(bg.substring(1, 3), 16); + g = Integer.parseInt(bg.substring(3, 5), 16); + b = Integer.parseInt(bg.substring(5, 7), 16); if (bg.length() == 9) { a = Integer.parseInt(bg.substring(7, 9), 16); + } else { + a = 255; } - color = new Color(r, g, b, a); + } catch (NumberFormatException e) { - color = null; // no changes + // no changes } } // Try by name if still not found - if (color == null) { - try { - Field field = Color.class.getField(bg); - color = (Color) field.get(null); - } catch (Exception e) { + if (a == -1) { + if ("black".equalsIgnoreCase(bg)) { + a = 255; + r = 0; + g = 0; + b = 0; + } else if ("white".equalsIgnoreCase(bg)) { + a = 255; + r = 255; + g = 255; + b = 255; + } else if ("red".equalsIgnoreCase(bg)) { + a = 255; + r = 255; + g = 0; + b = 0; + } else if ("green".equalsIgnoreCase(bg)) { + a = 255; + r = 0; + g = 255; + b = 0; + } else if ("blue".equalsIgnoreCase(bg)) { + a = 255; + r = 0; + g = 0; + b = 255; + } else if ("grey".equalsIgnoreCase(bg) + || "gray".equalsIgnoreCase(bg)) { + a = 255; + r = 128; + g = 128; + b = 128; + } else if ("cyan".equalsIgnoreCase(bg)) { + a = 255; + r = 0; + g = 255; + b = 255; + } else if ("magenta".equalsIgnoreCase(bg)) { + a = 255; + r = 255; + g = 0; + b = 255; + } else if ("yellow".equalsIgnoreCase(bg)) { + a = 255; + r = 255; + g = 255; + b = 0; } } - // - return color; + if (a != -1) { + rep = ((a & 0xFF) << 24) // + | ((r & 0xFF) << 16) // + | ((g & 0xFF) << 8) // + | ((b & 0xFF) << 0); + } + + return rep; } /** - * Set the value associated to the given id as a {@link Color}. + * Set the value associated to the given id as a colour. + *

+ * The value is an BGRA value. * * @param id * the id of the value to set * @param color - * the new color - */ - public void setColor(E id, Color color) { - // Check for named colours first - try { - Field[] fields = Color.class.getFields(); - for (Field field : fields) { - if (field.equals(color)) { - setString(id, field.getName()); - return; - } - } - } catch (Exception e) { - } - // - - String r = Integer.toString(color.getRed(), 16); - String g = Integer.toString(color.getGreen(), 16); - String b = Integer.toString(color.getBlue(), 16); - String a = ""; - if (color.getAlpha() < 255) { - a = Integer.toString(color.getAlpha(), 16); + * the new colour + */ + public void setColor(E id, Integer color) { + int a = (color >> 24) & 0xFF; + int r = (color >> 16) & 0xFF; + int g = (color >> 8) & 0xFF; + int b = (color >> 0) & 0xFF; + + String rs = Integer.toString(r, 16); + String gs = Integer.toString(g, 16); + String bs = Integer.toString(b, 16); + String as = ""; + if (a < 255) { + as = Integer.toString(a, 16); } - setString(id, "#" + r + g + b + a); + setString(id, "#" + rs + gs + bs + as); } /** diff --git a/src/be/nikiroo/utils/serial/SerialUtils.java b/src/be/nikiroo/utils/serial/SerialUtils.java index a1105a9..3308a75 100644 --- a/src/be/nikiroo/utils/serial/SerialUtils.java +++ b/src/be/nikiroo/utils/serial/SerialUtils.java @@ -1,6 +1,5 @@ package be.nikiroo.utils.serial; -import java.awt.image.BufferedImage; import java.io.IOException; import java.io.NotSerializableException; import java.lang.reflect.Array; @@ -12,7 +11,7 @@ import java.util.HashMap; import java.util.Map; import java.util.UnknownFormatConversionException; -import be.nikiroo.utils.ImageUtils; +import be.nikiroo.utils.Image; /** * Small class to help with serialisation. @@ -133,25 +132,21 @@ public class SerialUtils { }); // Images (this is currently the only supported image type by default) - customTypes.put("java.awt.image.BufferedImage", new CustomSerializer() { + customTypes.put("be.nikiroo.utils.Image", new CustomSerializer() { @Override protected String toString(Object value) { - try { - return ImageUtils.toBase64((BufferedImage) value); - } catch (IOException e) { - throw new UnknownFormatConversionException(e.getMessage()); - } + return ((Image) value).toBase64(); } @Override protected String getType() { - return "java.awt.image.BufferedImage"; + return "be.nikiroo.utils.Image"; } @Override protected Object fromString(String content) { try { - return ImageUtils.fromBase64(content); + return new Image(content); } catch (IOException e) { throw new UnknownFormatConversionException(e.getMessage()); } diff --git a/src/be/nikiroo/utils/test/Test.java b/src/be/nikiroo/utils/test/Test.java index 4f20182..b88be7c 100644 --- a/src/be/nikiroo/utils/test/Test.java +++ b/src/be/nikiroo/utils/test/Test.java @@ -1,5 +1,8 @@ package be.nikiroo.utils.test; +import be.nikiroo.utils.Cache; +import be.nikiroo.utils.Downloader; + /** * Tests for nikiroo-utils. * @@ -23,6 +26,10 @@ public class Test extends TestLauncher { addSeries(new SerialTest(args)); addSeries(new SerialServerTest(args)); addSeries(new StringUtilsTest(args)); + + // TODO: test cache and downloader + Cache cache = null; + Downloader downloader = null; } /** diff --git a/src/be/nikiroo/utils/ImageText.java b/src/be/nikiroo/utils/ui/ImageTextAwt.java similarity index 95% rename from src/be/nikiroo/utils/ImageText.java rename to src/be/nikiroo/utils/ui/ImageTextAwt.java index 53d8def..ee4f58c 100644 --- a/src/be/nikiroo/utils/ImageText.java +++ b/src/be/nikiroo/utils/ui/ImageTextAwt.java @@ -1,4 +1,4 @@ -package be.nikiroo.utils; +package be.nikiroo.utils.ui; import java.awt.Color; import java.awt.Dimension; @@ -13,7 +13,7 @@ import java.awt.image.ImageObserver; * * @author niki */ -public class ImageText { +public class ImageTextAwt { private Image image; private Dimension size; private String text; @@ -22,7 +22,7 @@ public class ImageText { private boolean invert; /** - * Th rendering modes supported by this {@link ImageText} to convert + * The rendering modes supported by this {@link ImageTextAwt} to convert * {@link Image}s into text. * * @author niki @@ -60,7 +60,7 @@ public class ImageText { } /** - * Create a new {@link ImageText} with the given parameters. Defaults to + * Create a new {@link ImageTextAwt} with the given parameters. Defaults to * {@link Mode#DOUBLE_DITHERING} and no colour inversion. * * @param image @@ -68,12 +68,12 @@ public class ImageText { * @param size * the final text size to target */ - public ImageText(Image image, Dimension size) { + public ImageTextAwt(Image image, Dimension size) { this(image, size, Mode.DOUBLE_DITHERING, false); } /** - * Create a new {@link ImageText} with the given parameters. + * Create a new {@link ImageTextAwt} with the given parameters. * * @param image * the source {@link Image} @@ -84,7 +84,7 @@ public class ImageText { * @param invert * TRUE to invert colours rendering */ - public ImageText(Image image, Dimension size, Mode mode, boolean invert) { + public ImageTextAwt(Image image, Dimension size, Mode mode, boolean invert) { setImage(image); setSize(size); setMode(mode); @@ -104,7 +104,7 @@ public class ImageText { } /** - * Change the target size of this {@link ImageText}. + * Change the target size of this {@link ImageTextAwt}. * * @param size * the new size @@ -196,7 +196,7 @@ public class ImageText { @Override public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { - ImageText.this.ready = true; + ImageTextAwt.this.ready = true; return true; } })) { diff --git a/src/be/nikiroo/utils/ui/ImageUtilsAwt.java b/src/be/nikiroo/utils/ui/ImageUtilsAwt.java new file mode 100644 index 0000000..e1fcac7 --- /dev/null +++ b/src/be/nikiroo/utils/ui/ImageUtilsAwt.java @@ -0,0 +1,142 @@ +package be.nikiroo.utils.ui; + +import java.awt.geom.AffineTransform; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import javax.imageio.ImageIO; + +import be.nikiroo.utils.Image; +import be.nikiroo.utils.ImageUtils; + +/** + * This class offer some utilities based around images and uses java.awt. + * + * @author niki + */ +public class ImageUtilsAwt extends ImageUtils { + @Override + public void saveAsImage(Image img, File target, String format) + throws IOException { + try { + BufferedImage image = ImageUtilsAwt.fromImage(img); + + boolean ok = false; + try { + + ok = ImageIO.write(image, format, target); + } catch (IOException e) { + ok = false; + } + + // Some formats are not reliable + // Second change: PNG + if (!ok && !format.equals("png")) { + ok = ImageIO.write(image, "png", target); + } + + if (!ok) { + throw new IOException( + "Cannot find a writer for this image and format: " + + format); + } + } catch (IOException e) { + throw new IOException("Cannot write image to " + target, e); + } + } + + /** + * Convert the given {@link Image} into a {@link BufferedImage} object, + * respecting the EXIF transformations if any. + * + * @param img + * the {@link Image} + * + * @return the {@link Image} object + * + * @throws IOException + * in case of IO error + */ + static public BufferedImage fromImage(Image img) throws IOException { + InputStream in = new ByteArrayInputStream(img.getData()); + + int orientation; + try { + orientation = getExifTransorm(in); + } catch (Exception e) { + // no EXIF transform, ok + orientation = -1; + } + + in.reset(); + BufferedImage image = ImageIO.read(in); + + if (image == null) { + throw new IOException("Failed to convert input to image"); + } + + // Note: this code has been found on Internet; + // thank you anonymous coder. + int width = image.getWidth(); + int height = image.getHeight(); + AffineTransform affineTransform = new AffineTransform(); + + switch (orientation) { + case 1: + affineTransform = null; + break; + case 2: // Flip X + affineTransform.scale(-1.0, 1.0); + affineTransform.translate(-width, 0); + break; + case 3: // PI rotation + affineTransform.translate(width, height); + affineTransform.rotate(Math.PI); + break; + case 4: // Flip Y + affineTransform.scale(1.0, -1.0); + affineTransform.translate(0, -height); + break; + case 5: // - PI/2 and Flip X + affineTransform.rotate(-Math.PI / 2); + affineTransform.scale(-1.0, 1.0); + break; + case 6: // -PI/2 and -width + affineTransform.translate(height, 0); + affineTransform.rotate(Math.PI / 2); + break; + case 7: // PI/2 and Flip + affineTransform.scale(-1.0, 1.0); + affineTransform.translate(-height, 0); + affineTransform.translate(0, width); + affineTransform.rotate(3 * Math.PI / 2); + break; + case 8: // PI / 2 + affineTransform.translate(0, width); + affineTransform.rotate(3 * Math.PI / 2); + break; + default: + affineTransform = null; + break; + } + + if (affineTransform != null) { + AffineTransformOp affineTransformOp = new AffineTransformOp( + affineTransform, AffineTransformOp.TYPE_BILINEAR); + + BufferedImage transformedImage = new BufferedImage(width, height, + image.getType()); + transformedImage = affineTransformOp + .filter(image, transformedImage); + + image = transformedImage; + } + // + + return image; + } +} diff --git a/src/be/nikiroo/utils/test/ProgressBarManualTest.java b/src/be/nikiroo/utils/ui/test/ProgressBarManualTest.java similarity index 98% rename from src/be/nikiroo/utils/test/ProgressBarManualTest.java rename to src/be/nikiroo/utils/ui/test/ProgressBarManualTest.java index 1ff75c3..b416cbc 100644 --- a/src/be/nikiroo/utils/test/ProgressBarManualTest.java +++ b/src/be/nikiroo/utils/ui/test/ProgressBarManualTest.java @@ -1,4 +1,4 @@ -package be.nikiroo.utils.test; +package be.nikiroo.utils.ui.test; import java.awt.BorderLayout; import java.awt.event.ActionEvent; diff --git a/src/be/nikiroo/utils/ui/test/TestUI.java b/src/be/nikiroo/utils/ui/test/TestUI.java new file mode 100644 index 0000000..c260295 --- /dev/null +++ b/src/be/nikiroo/utils/ui/test/TestUI.java @@ -0,0 +1,8 @@ +package be.nikiroo.utils.ui.test; + +public class TestUI { + // TODO: make a GUI tester + public TestUI() { + ProgressBarManualTest a = new ProgressBarManualTest(); + } +}