Version 2.0.0 (small API change)
authorNiki Roo <niki@nikiroo.be>
Thu, 6 Jul 2017 20:03:12 +0000 (22:03 +0200)
committerNiki Roo <niki@nikiroo.be>
Thu, 6 Jul 2017 20:03:12 +0000 (22:03 +0200)
VERSION
changelog [deleted file]
changelog.md [new file with mode: 0644]
configure.sh
export.sh
src/be/nikiroo/utils/IOUtils.java
src/be/nikiroo/utils/ImageText.java [new file with mode: 0644]
src/be/nikiroo/utils/ImageUtils.java [new file with mode: 0644]
src/be/nikiroo/utils/StringUtils.java
src/be/nikiroo/utils/resources/Bundle.java
src/be/nikiroo/utils/serial/SerialUtils.java

diff --git a/VERSION b/VERSION
index 266146b87cbc8e6d59fe83c426a8500f9549adfb..227cea215648b1af34a87c9acf5b707fe02d2072 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.6.3
+2.0.0
diff --git a/changelog b/changelog
deleted file mode 100644 (file)
index 2069eac..0000000
--- a/changelog
+++ /dev/null
@@ -1,199 +0,0 @@
-Version 1.6.3
--------------
-
-Version.java
-       Fix toString issues + test + update scripts
-
-Version 1.6.2
--------------
-
-Version.java
-       Now supports "tag" on the versions (i.e., 0.0.4-niki1)
-       -> tag is "niki", tagVersion is 1
-
-Version 1.6.1
--------------
-
-Serialisation utilities
-       Now supports enums and BufferedImages
-
-Version 1.6.0
--------------
-
-Serialisation utilities
-       Server class to send/receive objects via network easily
-       Serialiser now supports Arrays + fixes
-
-Version 1.5.1
--------------
-
-Serialisation utilities
-       SerialUtils is now public and can be used to dynamically create an
-       Object
-       The Importer is now easier to use
-
-Version 1.5.0
--------------
-
-Bundles: change in Bundles and meta data
-       The meta data is more complete now, but it breaks compatibility with
-       both Bundles and @Meta
-       A description can now be added to a bundle item in the graphical
-       editor as a tooltip
-
-Serialisation utilities
-       A new set of utilities to quickly serialise objects
-
-Version 1.4.3
--------------
-
-Bugfix: unhtml
-       Also replace non-breakable spaces by normal spaces
-
-Version 1.4.2
--------------
-
-Bugfix: Deltree
-       Deltree was not OK for files...
-
-Version 1.4.1
--------------
-
-Progress
-       Better handling of min==max case
-       New methods .done() and .add(int step)
-
-Version 1.4.0
--------------
-
-R/W Bundles
-       Bundle is now Read/Write
-
-Bundle Configuration
-       New UI controls to configure the Bundles graphically
-
-Version 1.3.6
--------------
-
-Fix for Java 1.6 compat
-       Java 1.6 cannot compile it due to variables with ambigous names (which
-       Java 1.8 can identify)
-
-Version 1.3.5
--------------
-
-Improve ProgressBar UI
-       It now shows all the progression bars of the different steps of
-       progression at the same time
-
-Version 1.3.4
--------------
-
-Improve TestCase error reporting
-       We know display the full stack trace even for AssertionErrors
-
-Extends Version
-       ...with new methods: isOlderThan(Version) and isNewerThan(Version)
-
-Version 1.3.3
--------------
-
-New Version class
-       Which can parse versions from the running program
-
-Version 1.2.3
--------------
-
-Add openResource and getVersion in IOUtils
-       The file VERSION is supposed to exist
-
-Give more informartion on AssertErrors
-       The TestCase were not always helpful in case of AssertExceptions; they
-       now print the stacktrace (they only used to do it for non-assert
-       exceptions)
-
-Fix configure.sh
-       The VERSION file was not added, the Main method was not the correct
-       one (so it was not producing working runnable JAR, yet it stated so)
-
-Version 1.2.2
--------------
-
-Fix bug in Bundle regarding \t handling
-       ...tests should be written (later)
-
-Version 1.2.1
--------------
-
-New drawEllipse3D method
-       ...in UIUtils
-
-Version 1.1.1
--------------
-
-Add UI component for Progress
-       Still a WIP, it only show the current progress bar, still not the
-       children bars (it's planned)
-
-Version 1.1.0
--------------
-
-Add progress reporting, move to ui package
-       A new progress reporting system (and tests) in the new ui package
-       (some other classes have been moved into ui, too: WrapLayout and
-       UIUtils)
-
-Version 1.0.0
--------------
-
-Add WrapLayout and UIUtils
-       A FlowLayout that automatically wrap to the next line (from existing
-       code found on internet) and a method to set a fake-native look & feel
-
-Version 0.9.7
--------------
-
-Improve toImage and allow non-resetable InputStreams
-       ...though they are then automatically saved onto disk then re-opened,
-       then the file is deleted at the end of the process -- bad perfs
-       Worse, it does it even if no EXIF metadata are present (because it
-       cannot know that before reading the Stream, and cannot save a
-       partially, non-resetable Stream to disk)
-
-Reoarganize some methods from String to IO
-
-Version 0.9.6
--------------
-
-New test system
-       Now some unit tests have been added, as well as the support classes
-
-Version 0.9.5
--------------
-
-Resource bundle bug
-       UTF-8 strings were sometimes wrangled
-       It is fixed by using a Bundle#Control, whih sadly is only available in
-       Java 1.6+
-
-Version 0.9.4
--------------
-
-Compatibility bug
-       Again... because of some useless imports made there for a wrong jDoc
-       comment
-
-Version 0.9.3
--------------
-
-Compatibility bug
-       The library did not work with JDK versions prior to 1.8 because
-       of a dependency on Base64
-       A new (public domain) class was used instead, which is compatible with
-       Java 1.5 this time
-
-Version 0.9.2
--------------
-
-Initial version
-       ...on GIT
diff --git a/changelog.md b/changelog.md
new file mode 100644 (file)
index 0000000..68ef60c
--- /dev/null
@@ -0,0 +1,166 @@
+# nikiroo-utils
+
+## Version 2.0.0
+
+- API change
+  - IOUtils is now split between itself and ImageUtils -- some changes required in dependant projects
+  - Some slight renaming in StringUtils/IOUtils/ImageUtils
+
+- New class ImageText
+  - To create ASCII art
+
+## Version 1.6.3
+
+- Version.java
+  - Fix toString issues + test + update scripts
+
+## Version 1.6.2
+
+- Version.java
+  - Now supports "tag" on the versions (i.e., 0.0.4-niki1 -> tag is "niki", tagVersion is 1)
+
+## Version 1.6.1
+
+- Serialisation utilities
+  - Now supports enums and BufferedImages
+
+## Version 1.6.0
+
+- Serialisation utilities
+  - Server class to send/receive objects via network easily
+  - Serialiser now supports Arrays + fixes
+
+## Version 1.5.1
+
+- Serialisation utilities
+  - SerialUtils is now public and can be used to dynamically create an Object
+  - The Importer is now easier to use
+
+## Version 1.5.0
+
+- Bundles: change in Bundles and meta data
+  - The meta data is more complete now, but it breaks compatibility with both Bundles and @Meta
+  - A description can now be added to a bundle item in the graphical editor as a tooltip
+
+- Serialisation utilities
+  - A new set of utilities to quickly serialise objects
+
+## Version 1.4.3
+
+- Bugfix: unhtml
+  - Also replace non-breakable spaces by normal spaces
+
+## Version 1.4.2
+
+- Bugfix: Deltree
+  - Deltree was not OK for files...
+
+## Version 1.4.1
+
+- Progress
+  - Better handling of min==max case
+  - New methods .done() and .add(int step)
+
+## Version 1.4.0
+
+- R/W Bundles
+  - Bundle is now Read/Write
+
+- Bundle Configuration
+  - New UI controls to configure the Bundles graphically
+
+## Version 1.3.6
+
+- Fix for Java 1.6 compat
+  - Java 1.6 cannot compile it due to variables with ambigous names (which
+  - Java 1.8 can identify)
+
+## Version 1.3.5
+
+- Improve ProgressBar UI
+  - It now shows all the progression bars of the different steps of progression at the same time
+
+## Version 1.3.4
+
+- Improve TestCase error reporting
+  - We know display the full stack trace even for AssertionErrors
+
+- Extends Version
+  - ...with new methods: isOlderThan(Version) and isNewerThan(Version)
+
+## Version 1.3.3
+
+- New Version class
+  - Which can parse versions from the running program
+
+## Version 1.2.3
+
+- Add openResource and getVersion in IOUtils
+  - The file VERSION is supposed to exist
+
+- Give more informartion on AssertErrors
+  - The TestCase were not always helpful in case of AssertExceptions; they now print the stacktrace (they only used to do it for non-assert exceptions)
+
+- Fix configure.sh
+  - The VERSION file was not added, the Main method was not the correct one (so it was not producing working runnable JAR, yet it stated so)
+
+## Version 1.2.2
+
+- Fix bug in Bundle regarding \t handling
+  - ...tests should be written (later)
+
+## Version 1.2.1
+
+- New drawEllipse3D method
+  - ...in UIUtils
+
+## Version 1.1.1
+
+- Add UI component for Progress
+  - Still a WIP, it only show the current progress bar, still not the children bars (it's planned)
+
+## Version 1.1.0
+
+- Add progress reporting, move to ui package
+  - A new progress reporting system (and tests) in the new ui package (some other classes have been moved into ui, too: WrapLayout and UIUtils)
+
+## Version 1.0.0
+
+- Add WrapLayout and UIUtils
+  - A FlowLayout that automatically wrap to the next line (from existing code found on internet) and a method to set a fake-native look & feel
+
+## Version 0.9.7
+
+- Improve toImage and allow non-resetable InputStreams
+  - ...though they are then automatically saved onto disk then re-opened, then the file is deleted at the end of the process -- bad perfs
+  - Worse, it does it even if no EXIF metadata are present (because it cannot know that before reading the Stream, and cannot save a partially, non-resetable Stream to disk)
+
+- Reoarganize some methods from String to IO
+
+## Version 0.9.6
+
+- New test system
+  - Now some unit tests have been added, as well as the support classes
+
+## Version 0.9.5
+
+- Resource bundle bug
+  - UTF-8 strings were sometimes wrangled
+  - It is fixed by using a Bundle#Control, whih sadly is only available in Java 1.6+
+
+## Version 0.9.4
+
+- Compatibility bug
+  - Again... because of some useless imports made there for a wrong jDoc comment
+
+## Version 0.9.3
+
+- Compatibility bug
+  - The library did not work with JDK versions prior to 1.8 because of a dependency on Base64
+  - A new (public domain) class was used instead, which is compatible with Java 1.5 this time
+
+## Version 0.9.2
+
+- Initial version
+  - ...on GIT
+
index b88e39aa1341416ca8bed35d8251e40516f13b79..a27a91ecd519bd4153d33bf92728dd6297373f26 100755 (executable)
@@ -50,8 +50,8 @@ 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/ VERSION" >> Makefile
-echo "SJAR_FLAGS += -C src/ be" >> Makefile
+echo "JAR_FLAGS += -C bin/ be -C bin/ org -C bin/ VERSION" >> Makefile
+echo "SJAR_FLAGS += -C src/ org -C src/ be" >> Makefile
 
 cat Makefile.base >> Makefile
 
index b0fdddf982d89e22cfd429cfd9c832462d274611..0cff17bfe4d73ca520a6b54cfef2867ab25c3d63 100755 (executable)
--- a/export.sh
+++ b/export.sh
@@ -3,6 +3,7 @@
 # Export script
 # 
 # Version:
+# - 1.1.0: allow multiple targets
 # - 1.0.0: add a version comment
 
 cd "`dirname "$0"`"
@@ -10,16 +11,19 @@ cd "`dirname "$0"`"
 if [ "$1" = "" ]; then
        echo "You need to specify where to export it" >&2
        exit 1
-elif [ ! -d "$1/libs" ]; then
-       echo "The target export directory is not compatible" >&2
-       exit 2
 fi
 
 LIBNAME="`cat configure.sh | grep '^echo "NAME = ' | cut -d'"' -f2 | cut -d= -f2`"
 LIBNAME="`echo $LIBNAME`"
 
 make mrpropre
-./configure.sh && make \
-       && cp "$LIBNAME"-`cat VERSION`-sources.jar "$1"/libs/ \
-       && cp "$LIBNAME".jar "$1"/libs/
+./configure.sh && make
+if [ $? = 0 ]; then
+       while [ "$1" != "" ]; do
+               mkdir -p "$1"/libs/
+               cp "$LIBNAME"-`cat VERSION`-sources.jar "$1"/libs/
+               cp "$LIBNAME".jar "$1"/libs/
+               shift
+       done
+fi
 
index b0566432aeb7fad7aae1938e7162a9810ed8ae2a..83dcd502b242fb26d88b611b1d7ff4e7d1a30c17 100644 (file)
@@ -1,9 +1,5 @@
 package be.nikiroo.utils;
 
-import java.awt.Image;
-import java.awt.geom.AffineTransform;
-import java.awt.image.AffineTransformOp;
-import java.awt.image.BufferedImage;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
@@ -16,8 +12,6 @@ import java.io.OutputStream;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
 
-import javax.imageio.ImageIO;
-
 /**
  * This class offer some utilities based around Streams.
  * 
@@ -214,116 +208,6 @@ public class IOUtils {
                }
        }
 
-       /**
-        * 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 'resetable' {@link InputStream}
-        * 
-        * @return the {@link Image} object
-        * 
-        * @throws IOException
-        *             in case of IO error
-        */
-       public static BufferedImage toImage(InputStream in) throws IOException {
-               MarkableFileInputStream tmpIn = null;
-               File tmp = null;
-               try {
-                       in.reset();
-               } catch (IOException e) {
-                       tmp = File.createTempFile("fanfic-tmp-image", ".tmp");
-                       tmp.deleteOnExit();
-                       IOUtils.write(in, tmp);
-                       tmpIn = new MarkableFileInputStream(new FileInputStream(tmp));
-               }
-
-               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) {
-                       if (tmp != null) {
-                               tmp.delete();
-                               tmpIn.close();
-                       }
-                       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;
-               }
-               //
-
-               if (tmp != null) {
-                       tmp.delete();
-                       tmpIn.close();
-               }
-
-               return image;
-       }
-
        /**
         * Open the given /-separated resource (from the binary root).
         * 
@@ -340,153 +224,4 @@ public class IOUtils {
 
                return loader.getResourceAsStream(name);
        }
-
-       /**
-        * Return the EXIF transformation flag of this image if any.
-        * 
-        * <p>
-        * Note: this code has been found on internet; thank you anonymous coder.
-        * </p>
-        * 
-        * @param in
-        *            the data {@link InputStream}
-        * 
-        * @return the transformation flag if any
-        * 
-        * @throws IOException
-        *             in case of IO error
-        */
-       private static int getExifTransorm(InputStream in) throws IOException {
-               int[] exif_data = new int[100];
-               int set_flag = 0;
-               int is_motorola = 0;
-
-               /* Read File head, check for JPEG SOI + Exif APP1 */
-               for (int i = 0; i < 4; i++)
-                       exif_data[i] = in.read();
-
-               if (exif_data[0] != 0xFF || exif_data[1] != 0xD8
-                               || exif_data[2] != 0xFF || exif_data[3] != 0xE1)
-                       return -2;
-
-               /* Get the marker parameter length count */
-               int length = (in.read() << 8 | in.read());
-
-               /* Length includes itself, so must be at least 2 */
-               /* Following Exif data length must be at least 6 */
-               if (length < 8)
-                       return -1;
-               length -= 8;
-               /* Read Exif head, check for "Exif" */
-               for (int i = 0; i < 6; i++)
-                       exif_data[i] = in.read();
-
-               if (exif_data[0] != 0x45 || exif_data[1] != 0x78
-                               || exif_data[2] != 0x69 || exif_data[3] != 0x66
-                               || exif_data[4] != 0 || exif_data[5] != 0)
-                       return -1;
-
-               /* Read Exif body */
-               length = length > exif_data.length ? exif_data.length : length;
-               for (int i = 0; i < length; i++)
-                       exif_data[i] = in.read();
-
-               if (length < 12)
-                       return -1; /* Length of an IFD entry */
-
-               /* Discover byte order */
-               if (exif_data[0] == 0x49 && exif_data[1] == 0x49)
-                       is_motorola = 0;
-               else if (exif_data[0] == 0x4D && exif_data[1] == 0x4D)
-                       is_motorola = 1;
-               else
-                       return -1;
-
-               /* Check Tag Mark */
-               if (is_motorola == 1) {
-                       if (exif_data[2] != 0)
-                               return -1;
-                       if (exif_data[3] != 0x2A)
-                               return -1;
-               } else {
-                       if (exif_data[3] != 0)
-                               return -1;
-                       if (exif_data[2] != 0x2A)
-                               return -1;
-               }
-
-               /* Get first IFD offset (offset to IFD0) */
-               int offset;
-               if (is_motorola == 1) {
-                       if (exif_data[4] != 0)
-                               return -1;
-                       if (exif_data[5] != 0)
-                               return -1;
-                       offset = exif_data[6];
-                       offset <<= 8;
-                       offset += exif_data[7];
-               } else {
-                       if (exif_data[7] != 0)
-                               return -1;
-                       if (exif_data[6] != 0)
-                               return -1;
-                       offset = exif_data[5];
-                       offset <<= 8;
-                       offset += exif_data[4];
-               }
-               if (offset > length - 2)
-                       return -1; /* check end of data segment */
-
-               /* Get the number of directory entries contained in this IFD */
-               int number_of_tags;
-               if (is_motorola == 1) {
-                       number_of_tags = exif_data[offset];
-                       number_of_tags <<= 8;
-                       number_of_tags += exif_data[offset + 1];
-               } else {
-                       number_of_tags = exif_data[offset + 1];
-                       number_of_tags <<= 8;
-                       number_of_tags += exif_data[offset];
-               }
-               if (number_of_tags == 0)
-                       return -1;
-               offset += 2;
-
-               /* Search for Orientation Tag in IFD0 */
-               for (;;) {
-                       if (offset > length - 12)
-                               return -1; /* check end of data segment */
-                       /* Get Tag number */
-                       int tagnum;
-                       if (is_motorola == 1) {
-                               tagnum = exif_data[offset];
-                               tagnum <<= 8;
-                               tagnum += exif_data[offset + 1];
-                       } else {
-                               tagnum = exif_data[offset + 1];
-                               tagnum <<= 8;
-                               tagnum += exif_data[offset];
-                       }
-                       if (tagnum == 0x0112)
-                               break; /* found Orientation Tag */
-                       if (--number_of_tags == 0)
-                               return -1;
-                       offset += 12;
-               }
-
-               /* Get the Orientation value */
-               if (is_motorola == 1) {
-                       if (exif_data[offset + 8] != 0)
-                               return -1;
-                       set_flag = exif_data[offset + 9];
-               } else {
-                       if (exif_data[offset + 9] != 0)
-                               return -1;
-                       set_flag = exif_data[offset + 8];
-               }
-               if (set_flag > 8)
-                       return -1;
-
-               return set_flag;
-       }
 }
diff --git a/src/be/nikiroo/utils/ImageText.java b/src/be/nikiroo/utils/ImageText.java
new file mode 100644 (file)
index 0000000..00bf3e4
--- /dev/null
@@ -0,0 +1,475 @@
+package be.nikiroo.utils;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import java.awt.image.ImageObserver;
+
+/**
+ * This class converts an {@link Image} into a textual representation that can
+ * be displayed to the user in a TUI.
+ * 
+ * @author niki
+ */
+public class ImageText {
+       private Image image;
+       private Dimension size;
+       private String text;
+       private boolean ready;
+       private Mode mode;
+       private boolean invert;
+
+       /**
+        * Th rendering modes supported by this {@link ImageText} to convert
+        * {@link Image}s into text.
+        * 
+        * @author niki
+        * 
+        */
+       public enum Mode {
+               /**
+                * Use 5 different "colours" which are actually Unicode
+                * {@link Character}s representing
+                * <ul>
+                * <li>space (blank)</li>
+                * <li>low shade (░)</li>
+                * <li>medium shade (▒)</li>
+                * <li>high shade (▓)</li>
+                * <li>full block (█)</li>
+                * </ul>
+                */
+               DITHERING,
+               /**
+                * Use "block" Unicode {@link Character}s up to quarter blocks, thus in
+                * effect doubling the resolution both in vertical and horizontal space.
+                * Note that since 2 {@link Character}s next to each other are square,
+                * we will use 4 blocks per 2 blocks for w/h resolution.
+                */
+               DOUBLE_RESOLUTION,
+               /**
+                * Use {@link Character}s from both {@link Mode#DOUBLE_RESOLUTION} and
+                * {@link Mode#DITHERING}.
+                */
+               DOUBLE_DITHERING,
+               /**
+                * Only use ASCII {@link Character}s.
+                */
+               ASCII,
+       }
+
+       /**
+        * Create a new {@link ImageText} with the given parameters. Defaults to
+        * {@link Mode#DOUBLE_DITHERING} and no colour inversion.
+        * 
+        * @param image
+        *            the source {@link Image}
+        * @param size
+        *            the final text size to target
+        */
+       public ImageText(Image image, Dimension size) {
+               this(image, size, Mode.DOUBLE_DITHERING, false);
+       }
+
+       /**
+        * Create a new {@link ImageText} with the given parameters.
+        * 
+        * @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
+        */
+       public ImageText(Image image, Dimension size, Mode mode, boolean invert) {
+               setImage(image);
+               setSize(size);
+               setMode(mode);
+               setColorInvert(invert);
+       }
+
+       /**
+        * Change the source {@link Image}.
+        * 
+        * @param image
+        *            the new {@link Image}
+        */
+       public void setImage(Image image) {
+               this.text = null;
+               this.ready = false;
+               this.image = image;
+       }
+
+       /**
+        * Change the target size of this {@link ImageText}.
+        * 
+        * @param size
+        *            the new size
+        */
+       public void setSize(Dimension size) {
+               this.text = null;
+               this.ready = false;
+               this.size = size;
+       }
+
+       /**
+        * Change the image-to-text mode.
+        * 
+        * @param mode
+        *            the new {@link Mode}
+        */
+       public void setMode(Mode mode) {
+               this.mode = mode;
+               this.text = null;
+               this.ready = false;
+       }
+
+       /**
+        * Set the colour-invert mode.
+        * 
+        * @param invert
+        *            TRUE to inverse the colours
+        */
+       public void setColorInvert(boolean invert) {
+               this.invert = invert;
+               this.text = null;
+               this.ready = false;
+       }
+
+       /**
+        * Check if the colours are inverted.
+        * 
+        * @return TRUE if the colours are inverted
+        */
+       public boolean isColorInvert() {
+               return invert;
+       }
+
+       /**
+        * Return the textual representation of the included {@link Image}.
+        * 
+        * @return the {@link String} representation
+        */
+       public String getText() {
+               if (text == null) {
+                       if (image == null || size == null || size.width == 0
+                                       || size.height == 0)
+                               return "";
+
+                       int mult = 1;
+                       if (mode == Mode.DOUBLE_RESOLUTION || mode == Mode.DOUBLE_DITHERING)
+                               mult = 2;
+
+                       int w = size.width * mult;
+                       int h = size.height * mult;
+
+                       BufferedImage buff = new BufferedImage(w, h,
+                                       BufferedImage.TYPE_INT_ARGB);
+
+                       Graphics gfx = buff.getGraphics();
+
+                       Dimension srcSize = getSize(image);
+                       srcSize = new Dimension(srcSize.width * 2, srcSize.height);
+                       int x = 0;
+                       int y = 0;
+
+                       if (srcSize.width < srcSize.height) {
+                               double ratio = (double) size.width / (double) size.height;
+                               ratio *= (double) srcSize.height / (double) srcSize.width;
+
+                               h = (int) Math.round(ratio * h);
+                               y = (buff.getHeight() - h) / 2;
+                       } else {
+                               double ratio = (double) size.height / (double) size.width;
+                               ratio *= (double) srcSize.width / (double) srcSize.height;
+
+                               w = (int) Math.round(ratio * w);
+                               x = (buff.getWidth() - w) / 2;
+                       }
+
+                       if (gfx.drawImage(image, x, y, w, h, new ImageObserver() {
+                               public boolean imageUpdate(Image img, int infoflags, int x,
+                                               int y, int width, int height) {
+                                       ImageText.this.ready = true;
+                                       return true;
+                               }
+                       })) {
+                               ready = true;
+                       }
+
+                       while (!ready) {
+                               try {
+                                       Thread.sleep(100);
+                               } catch (InterruptedException e) {
+                               }
+                       }
+
+                       gfx.dispose();
+
+                       StringBuilder builder = new StringBuilder();
+
+                       for (int row = 0; row < buff.getHeight(); row += mult) {
+                               if (row > 0)
+                                       builder.append('\n');
+
+                               for (int col = 0; col < buff.getWidth(); col += mult) {
+                                       if (mult == 1) {
+                                               char car = ' ';
+                                               float brightness = getBrightness(buff.getRGB(col, row));
+                                               if (mode == Mode.DITHERING)
+                                                       car = getDitheringChar(brightness, " ░▒▓█");
+                                               if (mode == Mode.ASCII)
+                                                       car = getDitheringChar(brightness, " .-+=o8#");
+
+                                               builder.append(car);
+                                       } else if (mult == 2) {
+                                               builder.append(getBlockChar( //
+                                                               buff.getRGB(col, row),//
+                                                               buff.getRGB(col + 1, row),//
+                                                               buff.getRGB(col, row + 1),//
+                                                               buff.getRGB(col + 1, row + 1),//
+                                                               mode == Mode.DOUBLE_DITHERING//
+                                               ));
+                                       }
+                               }
+                       }
+
+                       text = builder.toString();
+               }
+
+               return text;
+       }
+
+       @Override
+       public String toString() {
+               return getText();
+       }
+
+       /**
+        * Return the size of the given {@link Image}.
+        * 
+        * @param img
+        *            the image to measure
+        * 
+        * @return the size
+        */
+       static private Dimension getSize(Image img) {
+               Dimension size = null;
+               while (size == null) {
+                       int w = img.getWidth(null);
+                       int h = img.getHeight(null);
+                       if (w > -1 && h > -1) {
+                               size = new Dimension(w, h);
+                       } else {
+                               try {
+                                       Thread.sleep(100);
+                               } catch (InterruptedException e) {
+                               }
+                       }
+               }
+
+               return size;
+       }
+
+       /**
+        * Return the {@link Character} corresponding to the given brightness level
+        * from the evenly-separated given {@link Character}s.
+        * 
+        * @param brightness
+        *            the brightness level
+        * @param cars
+        *            the {@link Character}s to choose from, from less bright to
+        *            most bright; <b>MUST</b> contain at least one
+        *            {@link Character}
+        * 
+        * @return the {@link Character} to use
+        */
+       private char getDitheringChar(float brightness, String cars) {
+               int index = Math.round(brightness * (cars.length() - 1));
+               return cars.charAt(index);
+       }
+
+       /**
+        * Return the {@link Character} corresponding to the 4 given colours in
+        * {@link Mode#DOUBLE_RESOLUTION} or {@link Mode#DOUBLE_DITHERING} mode.
+        * 
+        * @param upperleft
+        *            the upper left colour
+        * @param upperright
+        *            the upper right colour
+        * @param lowerleft
+        *            the lower left colour
+        * @param lowerright
+        *            the lower right colour
+        * @param dithering
+        *            TRUE to use {@link Mode#DOUBLE_DITHERING}, FALSE for
+        *            {@link Mode#DOUBLE_RESOLUTION}
+        * 
+        * @return the {@link Character} to use
+        */
+       private char getBlockChar(int upperleft, int upperright, int lowerleft,
+                       int lowerright, boolean dithering) {
+               int choice = 0;
+               if (getBrightness(upperleft) > 0.5f)
+                       choice += 1;
+               if (getBrightness(upperright) > 0.5f)
+                       choice += 2;
+               if (getBrightness(lowerleft) > 0.5f)
+                       choice += 4;
+               if (getBrightness(lowerright) > 0.5f)
+                       choice += 8;
+
+               switch (choice) {
+               case 0:
+                       return ' ';
+               case 1:
+                       return '▘';
+               case 2:
+                       return '▝';
+               case 3:
+                       return '▀';
+               case 4:
+                       return '▖';
+               case 5:
+                       return '▌';
+               case 6:
+                       return '▞';
+               case 7:
+                       return '▛';
+               case 8:
+                       return '▗';
+               case 9:
+                       return '▚';
+               case 10:
+                       return '▐';
+               case 11:
+                       return '▜';
+               case 12:
+                       return '▄';
+               case 13:
+                       return '▙';
+               case 14:
+                       return '▟';
+               case 15:
+                       if (dithering) {
+                               float avg = 0;
+                               avg += getBrightness(upperleft);
+                               avg += getBrightness(upperright);
+                               avg += getBrightness(lowerleft);
+                               avg += getBrightness(lowerright);
+                               avg /= 4;
+
+                               return getDitheringChar(avg, " ░▒▓█");
+                       } else {
+                               return '█';
+                       }
+               }
+
+               return ' ';
+       }
+
+       /**
+        * Temporary array used so not to create a lot of new ones.
+        */
+       private float[] tmp = new float[4];
+
+       /**
+        * Return the brightness value to use from the given ARGB colour.
+        * 
+        * @param argb
+        *            the argb colour
+        * 
+        * @return the brightness to sue for computations
+        */
+       private float getBrightness(int argb) {
+               if (invert)
+                       return 1 - rgb2hsb(argb, tmp)[2];
+               return rgb2hsb(argb, tmp)[2];
+       }
+
+       /**
+        * Convert the given ARGB colour in HSL/HSB, either into the supplied array
+        * or into a new one if array is NULL.
+        * 
+        * <p>
+        * ARGB pixels are given in 0xAARRGGBB format, while the returned array will
+        * contain Hue, Saturation, Lightness/Brightness, Alpha, in this order. H,
+        * S, L and A are all ranging from 0 to 1 (indeed, H is in 1/360th).
+        * </p>
+        * pixel
+        * 
+        * @param argb
+        *            the ARGB colour pixel to convert
+        * @param array
+        *            the array to convert into or NULL to create a new one
+        * 
+        * @return the array containing the HSL/HSB converted colour
+        */
+       static float[] rgb2hsb(int argb, float[] array) {
+               int a, r, g, b;
+               a = ((argb & 0xff000000) >> 24);
+               r = ((argb & 0x00ff0000) >> 16);
+               g = ((argb & 0x0000ff00) >> 8);
+               b = ((argb & 0x000000ff));
+
+               if (array == null)
+                       array = new float[4];
+               Color.RGBtoHSB(r, g, b, array);
+
+               array[3] = a;
+
+               return array;
+
+               // // other implementation:
+               //
+               // float a, r, g, b;
+               // a = ((argb & 0xff000000) >> 24) / 255.0f;
+               // r = ((argb & 0x00ff0000) >> 16) / 255.0f;
+               // g = ((argb & 0x0000ff00) >> 8) / 255.0f;
+               // b = ((argb & 0x000000ff)) / 255.0f;
+               //
+               // float rgbMin, rgbMax;
+               // rgbMin = Math.min(r, Math.min(g, b));
+               // rgbMax = Math.max(r, Math.max(g, b));
+               //
+               // float l;
+               // l = (rgbMin + rgbMax) / 2;
+               //
+               // float s;
+               // if (rgbMin == rgbMax) {
+               // s = 0;
+               // } else {
+               // if (l <= 0.5) {
+               // s = (rgbMax - rgbMin) / (rgbMax + rgbMin);
+               // } else {
+               // s = (rgbMax - rgbMin) / (2.0f - rgbMax - rgbMin);
+               // }
+               // }
+               //
+               // float h;
+               // if (r > g && r > b) {
+               // h = (g - b) / (rgbMax - rgbMin);
+               // } else if (g > b) {
+               // h = 2.0f + (b - r) / (rgbMax - rgbMin);
+               // } else {
+               // h = 4.0f + (r - g) / (rgbMax - rgbMin);
+               // }
+               // h /= 6; // from 0 to 1
+               //
+               // return new float[] { h, s, l, a };
+               //
+               // // // natural mode:
+               // //
+               // // int aa = (int) Math.round(100 * a);
+               // // int hh = (int) (360 * h);
+               // // if (hh < 0)
+               // // hh += 360;
+               // // int ss = (int) Math.round(100 * s);
+               // // int ll = (int) Math.round(100 * l);
+               // //
+               // // return new int[] { hh, ss, ll, aa };
+       }
+}
diff --git a/src/be/nikiroo/utils/ImageUtils.java b/src/be/nikiroo/utils/ImageUtils.java
new file mode 100644 (file)
index 0000000..afeb5d3
--- /dev/null
@@ -0,0 +1,400 @@
+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;
+
+/**
+ * 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);
+       }
+
+       /**
+        * 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
+        * 
+        * @throws IOException
+        *             in case of IO error
+        */
+       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;
+       }
+
+       /**
+        * Convert the given image into a Base64 representation of the same
+        * {@link File}.
+        * 
+        * @param in
+        *            the image to convert
+        * 
+        * @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);
+       }
+
+       /**
+        * 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 'resetable' {@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;
+               try {
+                       in.reset();
+               } catch (IOException e) {
+                       tmp = File.createTempFile(".tmp-image", ".tmp");
+                       tmp.deleteOnExit();
+                       IOUtils.write(in, tmp);
+                       tmpIn = new MarkableFileInputStream(new FileInputStream(tmp));
+               }
+
+               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) {
+                       if (tmp != null) {
+                               tmp.delete();
+                               tmpIn.close();
+                       }
+                       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;
+               }
+               //
+
+               if (tmp != null) {
+                       tmp.delete();
+                       tmpIn.close();
+               }
+
+               return image;
+       }
+
+       /**
+        * 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();
+       }
+
+       /**
+        * Return the EXIF transformation flag of this image if any.
+        * 
+        * <p>
+        * Note: this code has been found on internet; thank you anonymous coder.
+        * </p>
+        * 
+        * @param in
+        *            the data {@link InputStream}
+        * 
+        * @return the transformation flag if any
+        * 
+        * @throws IOException
+        *             in case of IO error
+        */
+       static private int getExifTransorm(InputStream in) throws IOException {
+               int[] exif_data = new int[100];
+               int set_flag = 0;
+               int is_motorola = 0;
+
+               /* Read File head, check for JPEG SOI + Exif APP1 */
+               for (int i = 0; i < 4; i++)
+                       exif_data[i] = in.read();
+
+               if (exif_data[0] != 0xFF || exif_data[1] != 0xD8
+                               || exif_data[2] != 0xFF || exif_data[3] != 0xE1)
+                       return -2;
+
+               /* Get the marker parameter length count */
+               int length = (in.read() << 8 | in.read());
+
+               /* Length includes itself, so must be at least 2 */
+               /* Following Exif data length must be at least 6 */
+               if (length < 8)
+                       return -1;
+               length -= 8;
+               /* Read Exif head, check for "Exif" */
+               for (int i = 0; i < 6; i++)
+                       exif_data[i] = in.read();
+
+               if (exif_data[0] != 0x45 || exif_data[1] != 0x78
+                               || exif_data[2] != 0x69 || exif_data[3] != 0x66
+                               || exif_data[4] != 0 || exif_data[5] != 0)
+                       return -1;
+
+               /* Read Exif body */
+               length = length > exif_data.length ? exif_data.length : length;
+               for (int i = 0; i < length; i++)
+                       exif_data[i] = in.read();
+
+               if (length < 12)
+                       return -1; /* Length of an IFD entry */
+
+               /* Discover byte order */
+               if (exif_data[0] == 0x49 && exif_data[1] == 0x49)
+                       is_motorola = 0;
+               else if (exif_data[0] == 0x4D && exif_data[1] == 0x4D)
+                       is_motorola = 1;
+               else
+                       return -1;
+
+               /* Check Tag Mark */
+               if (is_motorola == 1) {
+                       if (exif_data[2] != 0)
+                               return -1;
+                       if (exif_data[3] != 0x2A)
+                               return -1;
+               } else {
+                       if (exif_data[3] != 0)
+                               return -1;
+                       if (exif_data[2] != 0x2A)
+                               return -1;
+               }
+
+               /* Get first IFD offset (offset to IFD0) */
+               int offset;
+               if (is_motorola == 1) {
+                       if (exif_data[4] != 0)
+                               return -1;
+                       if (exif_data[5] != 0)
+                               return -1;
+                       offset = exif_data[6];
+                       offset <<= 8;
+                       offset += exif_data[7];
+               } else {
+                       if (exif_data[7] != 0)
+                               return -1;
+                       if (exif_data[6] != 0)
+                               return -1;
+                       offset = exif_data[5];
+                       offset <<= 8;
+                       offset += exif_data[4];
+               }
+               if (offset > length - 2)
+                       return -1; /* check end of data segment */
+
+               /* Get the number of directory entries contained in this IFD */
+               int number_of_tags;
+               if (is_motorola == 1) {
+                       number_of_tags = exif_data[offset];
+                       number_of_tags <<= 8;
+                       number_of_tags += exif_data[offset + 1];
+               } else {
+                       number_of_tags = exif_data[offset + 1];
+                       number_of_tags <<= 8;
+                       number_of_tags += exif_data[offset];
+               }
+               if (number_of_tags == 0)
+                       return -1;
+               offset += 2;
+
+               /* Search for Orientation Tag in IFD0 */
+               for (;;) {
+                       if (offset > length - 12)
+                               return -1; /* check end of data segment */
+                       /* Get Tag number */
+                       int tagnum;
+                       if (is_motorola == 1) {
+                               tagnum = exif_data[offset];
+                               tagnum <<= 8;
+                               tagnum += exif_data[offset + 1];
+                       } else {
+                               tagnum = exif_data[offset + 1];
+                               tagnum <<= 8;
+                               tagnum += exif_data[offset];
+                       }
+                       if (tagnum == 0x0112)
+                               break; /* found Orientation Tag */
+                       if (--number_of_tags == 0)
+                               return -1;
+                       offset += 12;
+               }
+
+               /* Get the Orientation value */
+               if (is_motorola == 1) {
+                       if (exif_data[offset + 8] != 0)
+                               return -1;
+                       set_flag = exif_data[offset + 9];
+               } else {
+                       if (exif_data[offset + 9] != 0)
+                               return -1;
+                       set_flag = exif_data[offset + 8];
+               }
+               if (set_flag > 8)
+                       return -1;
+
+               return set_flag;
+       }
+}
index af69845b1a6c416f55e162b506c49b7ad2d938cf..a50ec28f19481363c9c09af476507b4cc5901826 100644 (file)
@@ -1,12 +1,7 @@
 package be.nikiroo.utils;
 
-import java.awt.Image;
-import java.awt.image.BufferedImage;
 import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.text.Normalizer;
@@ -17,8 +12,6 @@ import java.util.Date;
 import java.util.Scanner;
 import java.util.regex.Pattern;
 
-import javax.imageio.ImageIO;
-
 import org.unbescape.html.HtmlEscape;
 import org.unbescape.html.HtmlEscapeLevel;
 import org.unbescape.html.HtmlEscapeType;
@@ -201,104 +194,6 @@ public class StringUtils {
                }
        }
 
-       /**
-        * 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 fromImage(BufferedImage image) throws IOException {
-               return fromImage(image, null);
-       }
-
-       /**
-        * 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
-        * 
-        * @throws IOException
-        *             in case of IO error
-        */
-       static public String fromImage(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;
-       }
-
-       /**
-        * Convert the given image into a Base64 representation of the same
-        * {@link File}.
-        * 
-        * @param in
-        *            the image to convert
-        * 
-        * @return the Base64 representation
-        * 
-        * @throws IOException
-        *             in case of IO error
-        */
-       static public String fromStream(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 toImage(String b64data) throws IOException {
-               ByteArrayInputStream in = new ByteArrayInputStream(
-                               Base64.decode(b64data));
-               return IOUtils.toImage(in);
-       }
-
        /**
         * Return a hash of the given {@link String}.
         * 
@@ -307,7 +202,7 @@ public class StringUtils {
         * 
         * @return the hash
         */
-       static public String getHash(String input) {
+       static public String getMd5Hash(String input) {
                try {
                        MessageDigest md = MessageDigest.getInstance("MD5");
                        md.update(input.getBytes());
index bad7f3e6c46679c31622b8e34bb8b6b0c0bb955a..1dbb251f942b4d3a95d0720e29a8ddcdc48a444e 100644 (file)
@@ -657,7 +657,7 @@ public class Bundle<E extends Enum<E>> {
         * @param bundle
         *            the bundle to copy
         */
-       private void resetMap(ResourceBundle bundle) {
+       protected void resetMap(ResourceBundle bundle) {
                this.map.clear();
 
                if (bundle != null) {
@@ -671,7 +671,7 @@ public class Bundle<E extends Enum<E>> {
                        }
                }
        }
-
+       
        /**
         * Take a snapshot of the changes in memory in this {@link Bundle} made by
         * the "set" methods ( {@link Bundle#setString(Enum, String)}...) at the
index c28faeddebd86cd783e4924460aa01cb749f4b83..be18769ee06967cd5c5a78439fcfe2afccb27d50 100644 (file)
@@ -11,7 +11,7 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.UnknownFormatConversionException;
 
-import be.nikiroo.utils.StringUtils;
+import be.nikiroo.utils.ImageUtils;
 
 /**
  * Small class to help with serialisation.
@@ -88,7 +88,7 @@ public class SerialUtils {
                        @Override
                        protected String toString(Object value) {
                                try {
-                                       return StringUtils.fromImage((BufferedImage) value);
+                                       return ImageUtils.toBase64((BufferedImage) value);
                                } catch (IOException e) {
                                        throw new UnknownFormatConversionException(e.getMessage());
                                }
@@ -102,7 +102,7 @@ public class SerialUtils {
                        @Override
                        protected Object fromString(String content) {
                                try {
-                                       return StringUtils.toImage(content);
+                                       return ImageUtils.fromBase64(content);
                                } catch (IOException e) {
                                        throw new UnknownFormatConversionException(e.getMessage());
                                }